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
|
////////// 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 React, { useEffect, useRef, useState } from "react";
|
||||||
import { Handle, Position, useReactFlow, useStore } from "reactflow";
|
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.BorealisValueBus) window.BorealisValueBus = {};
|
||||||
if (!window.BorealisUpdateRate) window.BorealisUpdateRate = 100;
|
if (!window.BorealisUpdateRate) window.BorealisUpdateRate = 100;
|
||||||
|
|
||||||
const MathNode = ({ id, data }) => {
|
const MathNode = ({ id, data }) => {
|
||||||
const { setNodes } = useReactFlow();
|
const { setNodes } = useReactFlow();
|
||||||
const edges = useStore(state => state.edges);
|
const edges = useStore(state => state.edges);
|
||||||
|
const [renderResult, setRenderResult] = useState(data?.value || "0");
|
||||||
|
const resultRef = useRef(renderResult);
|
||||||
|
|
||||||
const [operator, setOperator] = useState(data?.operator || "Add");
|
useEffect(() => {
|
||||||
const [result, setResult] = useState("0");
|
let intervalId = null;
|
||||||
const resultRef = useRef(0);
|
let currentRate = window.BorealisUpdateRate;
|
||||||
|
|
||||||
useEffect(() => {
|
const runLogic = () => {
|
||||||
let intervalId = null;
|
const operator = data?.operator || "Add";
|
||||||
let currentRate = window.BorealisUpdateRate;
|
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 sum = (list) =>
|
||||||
const inputsA = edges.filter(e => e.target === id && e.targetHandle === "a");
|
list
|
||||||
const inputsB = edges.filter(e => e.target === id && e.targetHandle === "b");
|
.map(e => parseFloat(window.BorealisValueBus[e.source]) || 0)
|
||||||
|
.reduce((a, b) => a + b, 0);
|
||||||
|
|
||||||
const sum = (list) =>
|
const valA = sum(inputsA);
|
||||||
list.map(e => parseFloat(window.BorealisValueBus[e.source]) || 0).reduce((a, b) => a + b, 0);
|
const valB = sum(inputsB);
|
||||||
|
|
||||||
const valA = sum(inputsA);
|
let value = 0;
|
||||||
const valB = sum(inputsB);
|
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;
|
resultRef.current = value;
|
||||||
switch (operator) {
|
setRenderResult(value.toString());
|
||||||
case "Add":
|
window.BorealisValueBus[id] = value.toString();
|
||||||
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;
|
setNodes(nds =>
|
||||||
setResult(value.toString());
|
nds.map(n =>
|
||||||
window.BorealisValueBus[id] = value.toString();
|
n.id === id
|
||||||
|
? { ...n, data: { ...n.data, value: value.toString() } }
|
||||||
|
: n
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
setNodes(nds =>
|
intervalId = setInterval(runLogic, currentRate);
|
||||||
nds.map(n =>
|
|
||||||
n.id === id ? { ...n, data: { ...n.data, operator, value: value.toString() } } : n
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// Watch for update rate changes
|
||||||
|
const monitor = setInterval(() => {
|
||||||
|
const newRate = window.BorealisUpdateRate;
|
||||||
|
if (newRate !== currentRate) {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
currentRate = newRate;
|
||||||
intervalId = setInterval(runLogic, currentRate);
|
intervalId = setInterval(runLogic, currentRate);
|
||||||
|
}
|
||||||
|
}, 250);
|
||||||
|
|
||||||
const monitor = setInterval(() => {
|
return () => {
|
||||||
const newRate = window.BorealisUpdateRate;
|
clearInterval(intervalId);
|
||||||
if (newRate !== currentRate) {
|
clearInterval(monitor);
|
||||||
clearInterval(intervalId);
|
};
|
||||||
currentRate = newRate;
|
}, [id, edges, setNodes, data?.operator]);
|
||||||
intervalId = setInterval(runLogic, currentRate);
|
|
||||||
}
|
|
||||||
}, 250);
|
|
||||||
|
|
||||||
return () => {
|
return (
|
||||||
clearInterval(intervalId);
|
<div className="borealis-node">
|
||||||
clearInterval(monitor);
|
<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>
|
||||||
}, [id, operator, edges, setNodes]);
|
<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-header" style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||||
<div className="borealis-node">
|
<span>{data?.label || "Math Operation"}</span>
|
||||||
<div style={{ position: "absolute", left: -16, top: 12, fontSize: "8px", color: "#ccc" }}>A</div>
|
</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">
|
<div className="borealis-node-content" style={{ fontSize: "9px", color: "#ccc", marginTop: 4 }}>
|
||||||
{data?.label || "Math Operation"}
|
Result: {renderResult}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="borealis-node-content">
|
<Handle type="source" position={Position.Right} className="borealis-handle" />
|
||||||
<div style={{ marginBottom: "8px", fontSize: "9px", color: "#ccc" }}>
|
</div>
|
||||||
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%"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
type: "MathNode",
|
type: "MathNode",
|
||||||
label: "Math Operation",
|
label: "Math Operation",
|
||||||
description: `
|
description: `
|
||||||
Perform Math on Aggregated Inputs
|
Live math node for computing on two grouped inputs.
|
||||||
|
|
||||||
- A and B groups are independently summed
|
- Sums all A and B handle inputs separately
|
||||||
- Performs: Add, Subtract, Multiply, Divide, or Average
|
- Performs selected math operation: Add, Subtract, Multiply, Divide, Average
|
||||||
- Result = A <op> B
|
- Emits result as string via BorealisValueBus
|
||||||
- Emits result via BorealisValueBus every update tick
|
- Updates at the global update rate
|
||||||
`.trim(),
|
|
||||||
content: "Perform Math Operations",
|
**Common Uses:**
|
||||||
component: MathNode
|
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