Upgraded Math Operation Node
This commit is contained in:
parent
84cd4b6a54
commit
b6500b84da
@ -1,179 +1,172 @@
|
||||
////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/Data/WebUI/src/nodes/General Purpose/Node_Math_Operations.jsx
|
||||
|
||||
/**
|
||||
* ============================================
|
||||
* Borealis - Math Operation Node (Multi-Input A/B)
|
||||
* ============================================
|
||||
*
|
||||
* COMPONENT ROLE:
|
||||
* Performs live math operations on *two grouped input sets* (A and B).
|
||||
*
|
||||
* FUNCTIONALITY:
|
||||
* - Inputs connected to Handle A are summed
|
||||
* - Inputs connected to Handle B are summed
|
||||
* - Math operation is applied as: A <operator> B
|
||||
* - Result pushed via BorealisValueBus[id]
|
||||
*
|
||||
* SUPPORTED OPERATORS:
|
||||
* - Add, Subtract, Multiply, Divide, Average
|
||||
*/
|
||||
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Handle, Position, useReactFlow, useStore } from "reactflow";
|
||||
import { IconButton } from "@mui/material";
|
||||
import SettingsIcon from "@mui/icons-material/Settings";
|
||||
|
||||
// Init shared memory bus if not already set
|
||||
if (!window.BorealisValueBus) window.BorealisValueBus = {};
|
||||
if (!window.BorealisUpdateRate) window.BorealisUpdateRate = 100;
|
||||
|
||||
const MathNode = ({ id, data }) => {
|
||||
const { setNodes } = useReactFlow();
|
||||
const edges = useStore(state => state.edges);
|
||||
const { setNodes } = useReactFlow();
|
||||
const edges = useStore(state => state.edges);
|
||||
const [renderResult, setRenderResult] = useState(data?.value || "0");
|
||||
const resultRef = useRef(renderResult);
|
||||
|
||||
const [operator, setOperator] = useState(data?.operator || "Add");
|
||||
const [result, setResult] = useState("0");
|
||||
const resultRef = useRef(0);
|
||||
useEffect(() => {
|
||||
let intervalId = null;
|
||||
let currentRate = window.BorealisUpdateRate;
|
||||
|
||||
useEffect(() => {
|
||||
let intervalId = null;
|
||||
let currentRate = window.BorealisUpdateRate;
|
||||
const runLogic = () => {
|
||||
const operator = data?.operator || "Add";
|
||||
const inputsA = edges.filter(e => e.target === id && e.targetHandle === "a");
|
||||
const inputsB = edges.filter(e => e.target === id && e.targetHandle === "b");
|
||||
|
||||
const runLogic = () => {
|
||||
const inputsA = edges.filter(e => e.target === id && e.targetHandle === "a");
|
||||
const inputsB = edges.filter(e => e.target === id && e.targetHandle === "b");
|
||||
const sum = (list) =>
|
||||
list
|
||||
.map(e => parseFloat(window.BorealisValueBus[e.source]) || 0)
|
||||
.reduce((a, b) => a + b, 0);
|
||||
|
||||
const sum = (list) =>
|
||||
list.map(e => parseFloat(window.BorealisValueBus[e.source]) || 0).reduce((a, b) => a + b, 0);
|
||||
const valA = sum(inputsA);
|
||||
const valB = sum(inputsB);
|
||||
|
||||
const valA = sum(inputsA);
|
||||
const valB = sum(inputsB);
|
||||
let value = 0;
|
||||
switch (operator) {
|
||||
case "Add":
|
||||
value = valA + valB;
|
||||
break;
|
||||
case "Subtract":
|
||||
value = valA - valB;
|
||||
break;
|
||||
case "Multiply":
|
||||
value = valA * valB;
|
||||
break;
|
||||
case "Divide":
|
||||
value = valB !== 0 ? valA / valB : 0;
|
||||
break;
|
||||
case "Average":
|
||||
const totalInputs = inputsA.length + inputsB.length;
|
||||
const totalSum = valA + valB;
|
||||
value = totalInputs > 0 ? totalSum / totalInputs : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
let value = 0;
|
||||
switch (operator) {
|
||||
case "Add":
|
||||
value = valA + valB;
|
||||
break;
|
||||
case "Subtract":
|
||||
value = valA - valB;
|
||||
break;
|
||||
case "Multiply":
|
||||
value = valA * valB;
|
||||
break;
|
||||
case "Divide":
|
||||
value = valB !== 0 ? valA / valB : 0;
|
||||
break;
|
||||
case "Average":
|
||||
const totalInputs = inputsA.length + inputsB.length;
|
||||
const totalSum = valA + valB;
|
||||
value = totalInputs > 0 ? totalSum / totalInputs : 0;
|
||||
break;
|
||||
}
|
||||
resultRef.current = value;
|
||||
setRenderResult(value.toString());
|
||||
window.BorealisValueBus[id] = value.toString();
|
||||
|
||||
resultRef.current = value;
|
||||
setResult(value.toString());
|
||||
window.BorealisValueBus[id] = value.toString();
|
||||
setNodes(nds =>
|
||||
nds.map(n =>
|
||||
n.id === id
|
||||
? { ...n, data: { ...n.data, value: value.toString() } }
|
||||
: n
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
setNodes(nds =>
|
||||
nds.map(n =>
|
||||
n.id === id ? { ...n, data: { ...n.data, operator, value: value.toString() } } : n
|
||||
)
|
||||
);
|
||||
};
|
||||
intervalId = setInterval(runLogic, currentRate);
|
||||
|
||||
// Watch for update rate changes
|
||||
const monitor = setInterval(() => {
|
||||
const newRate = window.BorealisUpdateRate;
|
||||
if (newRate !== currentRate) {
|
||||
clearInterval(intervalId);
|
||||
currentRate = newRate;
|
||||
intervalId = setInterval(runLogic, currentRate);
|
||||
}
|
||||
}, 250);
|
||||
|
||||
const monitor = setInterval(() => {
|
||||
const newRate = window.BorealisUpdateRate;
|
||||
if (newRate !== currentRate) {
|
||||
clearInterval(intervalId);
|
||||
currentRate = newRate;
|
||||
intervalId = setInterval(runLogic, currentRate);
|
||||
}
|
||||
}, 250);
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
clearInterval(monitor);
|
||||
};
|
||||
}, [id, edges, setNodes, data?.operator]);
|
||||
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
clearInterval(monitor);
|
||||
};
|
||||
}, [id, operator, edges, setNodes]);
|
||||
return (
|
||||
<div className="borealis-node">
|
||||
<div style={{ position: "absolute", left: -16, top: 12, fontSize: "8px", color: "#ccc" }}>A</div>
|
||||
<div style={{ position: "absolute", left: -16, top: 50, fontSize: "8px", color: "#ccc" }}>B</div>
|
||||
<Handle type="target" position={Position.Left} id="a" style={{ top: 12 }} className="borealis-handle" />
|
||||
<Handle type="target" position={Position.Left} id="b" style={{ top: 50 }} className="borealis-handle" />
|
||||
|
||||
return (
|
||||
<div className="borealis-node">
|
||||
<div style={{ position: "absolute", left: -16, top: 12, fontSize: "8px", color: "#ccc" }}>A</div>
|
||||
<div style={{ position: "absolute", left: -16, top: 50, fontSize: "8px", color: "#ccc" }}>B</div>
|
||||
<Handle type="target" position={Position.Left} id="a" style={{ top: 12 }} className="borealis-handle" />
|
||||
<Handle type="target" position={Position.Left} id="b" style={{ top: 50 }} className="borealis-handle" />
|
||||
<div className="borealis-node-header" style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||
<span>{data?.label || "Math Operation"}</span>
|
||||
</div>
|
||||
|
||||
<div className="borealis-node-header">
|
||||
{data?.label || "Math Operation"}
|
||||
</div>
|
||||
<div className="borealis-node-content" style={{ fontSize: "9px", color: "#ccc", marginTop: 4 }}>
|
||||
Result: {renderResult}
|
||||
</div>
|
||||
|
||||
<div className="borealis-node-content">
|
||||
<div style={{ marginBottom: "8px", fontSize: "9px", color: "#ccc" }}>
|
||||
Aggregates A and B inputs then performs operation.
|
||||
</div>
|
||||
|
||||
<label style={{ fontSize: "9px", display: "block", marginBottom: "4px" }}>
|
||||
Operator:
|
||||
</label>
|
||||
<select
|
||||
value={operator}
|
||||
onChange={(e) => setOperator(e.target.value)}
|
||||
style={dropdownStyle}
|
||||
>
|
||||
<option value="Add">Add</option>
|
||||
<option value="Subtract">Subtract</option>
|
||||
<option value="Multiply">Multiply</option>
|
||||
<option value="Divide">Divide</option>
|
||||
<option value="Average">Average</option>
|
||||
</select>
|
||||
|
||||
<label style={{ fontSize: "9px", display: "block", marginBottom: "4px" }}>
|
||||
Result:
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={result}
|
||||
disabled
|
||||
style={resultBoxStyle}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Handle type="source" position={Position.Right} className="borealis-handle" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const dropdownStyle = {
|
||||
fontSize: "9px",
|
||||
padding: "4px",
|
||||
background: "#1e1e1e",
|
||||
color: "#ccc",
|
||||
border: "1px solid #444",
|
||||
borderRadius: "2px",
|
||||
width: "100%",
|
||||
marginBottom: "8px"
|
||||
};
|
||||
|
||||
const resultBoxStyle = {
|
||||
fontSize: "9px",
|
||||
padding: "4px",
|
||||
background: "#2a2a2a",
|
||||
color: "#ccc",
|
||||
border: "1px solid #444",
|
||||
borderRadius: "2px",
|
||||
width: "100%"
|
||||
<Handle type="source" position={Position.Right} className="borealis-handle" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default {
|
||||
type: "MathNode",
|
||||
label: "Math Operation",
|
||||
description: `
|
||||
Perform Math on Aggregated Inputs
|
||||
type: "MathNode",
|
||||
label: "Math Operation",
|
||||
description: `
|
||||
Live math node for computing on two grouped inputs.
|
||||
|
||||
- A and B groups are independently summed
|
||||
- Performs: Add, Subtract, Multiply, Divide, or Average
|
||||
- Result = A <op> B
|
||||
- Emits result via BorealisValueBus every update tick
|
||||
`.trim(),
|
||||
content: "Perform Math Operations",
|
||||
component: MathNode
|
||||
- Sums all A and B handle inputs separately
|
||||
- Performs selected math operation: Add, Subtract, Multiply, Divide, Average
|
||||
- Emits result as string via BorealisValueBus
|
||||
- Updates at the global update rate
|
||||
|
||||
**Common Uses:**
|
||||
Live dashboard math, sensor fusion, calculation chains, dynamic thresholds
|
||||
`.trim(),
|
||||
content: "Perform Math Operations",
|
||||
component: MathNode,
|
||||
config: [
|
||||
{
|
||||
key: "operator",
|
||||
label: "Operator",
|
||||
type: "select",
|
||||
options: [
|
||||
"Add",
|
||||
"Subtract",
|
||||
"Multiply",
|
||||
"Divide",
|
||||
"Average"
|
||||
]
|
||||
}
|
||||
],
|
||||
usage_documentation: `
|
||||
### Math Operation Node
|
||||
|
||||
Performs live math between two groups of inputs (**A** and **B**).
|
||||
|
||||
#### Usage
|
||||
|
||||
- Connect any number of nodes to the **A** and **B** input handles.
|
||||
- The node **sums all values** from A and from B before applying the operator.
|
||||
- Select the math operator in the sidebar config:
|
||||
- **Add**: A + B
|
||||
- **Subtract**: A - B
|
||||
- **Multiply**: A * B
|
||||
- **Divide**: A / B (0 if B=0)
|
||||
- **Average**: (A + B) / total number of inputs
|
||||
|
||||
#### Output
|
||||
|
||||
- The computed result is pushed as a string to downstream nodes every update tick.
|
||||
|
||||
#### Input Handles
|
||||
|
||||
- **A** (Top Left)
|
||||
- **B** (Middle Left)
|
||||
|
||||
#### Example
|
||||
|
||||
If three nodes outputting 5, 10, 15 are connected to A,
|
||||
and one node outputs 2 is connected to B,
|
||||
and operator is Multiply:
|
||||
|
||||
- **A** = 5 + 10 + 15 = 30
|
||||
- **B** = 2
|
||||
- **Result** = 30 * 2 = 60
|
||||
|
||||
`.trim()
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user