From d46c6ecc3c845fd49c65531237e70f2f240ca856 Mon Sep 17 00:00:00 2001 From: Nicole Rappe Date: Tue, 1 Apr 2025 20:53:16 -0600 Subject: [PATCH] Overhauled Data_Node to be functional Implemented dynamically-configurable global timer Revised Data_Node to be foundational for future nodes. --- Data/WebUI/public/index.html | 22 --- Data/WebUI/src/App.jsx | 43 +++- .../src/nodes/General Purpose/Data_Node.jsx | 187 +++++++++++++++++- 3 files changed, 219 insertions(+), 33 deletions(-) diff --git a/Data/WebUI/public/index.html b/Data/WebUI/public/index.html index da117a9..124e801 100644 --- a/Data/WebUI/public/index.html +++ b/Data/WebUI/public/index.html @@ -10,34 +10,12 @@ content="Workflow Automation Tool" /> - - Borealis
- diff --git a/Data/WebUI/src/App.jsx b/Data/WebUI/src/App.jsx index 19a262c..e655fb9 100644 --- a/Data/WebUI/src/App.jsx +++ b/Data/WebUI/src/App.jsx @@ -54,6 +54,11 @@ import ReactFlow, { import "reactflow/dist/style.css"; import "./Borealis.css"; +// Global Node Update Timer Variable +if (!window.BorealisUpdateRate) { + window.BorealisUpdateRate = 100; // Default Update Rate: 100ms +} + const nodeContext = require.context("./nodes", true, /\.jsx$/); const nodeTypes = {}; const categorizedNodes = {}; @@ -413,8 +418,42 @@ export default function App() { - - Nodes: 0 | Update Rate: 500ms + + Nodes: 0 + + Update Rate (ms): + + diff --git a/Data/WebUI/src/nodes/General Purpose/Data_Node.jsx b/Data/WebUI/src/nodes/General Purpose/Data_Node.jsx index 6ba4104..4ae4cc9 100644 --- a/Data/WebUI/src/nodes/General Purpose/Data_Node.jsx +++ b/Data/WebUI/src/nodes/General Purpose/Data_Node.jsx @@ -1,21 +1,190 @@ -import React from "react"; -import { Handle, Position } from "reactflow"; +/** + * ============================================ + * Borealis - Standard Live Data Node Template + * ============================================ + * + * COMPONENT ROLE: + * This component defines a "data conduit" node that can accept input, + * process/override it with local logic, and forward the output on a timed basis. + * + * It serves as the core behavior model for other nodes that rely on live propagation. + * Clone and extend this file to create nodes with specialized logic. + * + * CORE CONCEPTS: + * - Uses a centralized shared memory (window.BorealisValueBus) for value sharing + * - Synchronizes with upstream nodes based on ReactFlow edges + * - Emits to downstream nodes by updating its own BorealisValueBus[id] value + * - Controlled by a global polling timer (window.BorealisUpdateRate) + * + * LIFECYCLE SUMMARY: + * - onMount: initializes logic loop and sync monitor + * - onUpdate: watches edges and global rate, reconfigures as needed + * - onUnmount: cleans up all timers + * + * DATA FLOW OVERVIEW: + * - INPUT: if a left-edge (target) is connected, disables manual editing + * - OUTPUT: propagates renderValue to downstream nodes via right-edge (source) + * + * STRUCTURE: + * - Node UI includes: + * * Label (from data.label) + * * Body description (from data.content) + * * Input textbox (disabled if input is connected) + * + * HOW TO EXTEND: + * - For transformations, insert logic into runNodeLogic() + * - To validate or restrict input types, modify handleManualInput() + * - For side-effects or external API calls, add hooks inside runNodeLogic() + */ + +import React, { useEffect, useRef, useState } from "react"; +import { Handle, Position, useReactFlow, useStore } from "reactflow"; + +// Global Shared Bus for Node Data Propagation +if (!window.BorealisValueBus) { + window.BorealisValueBus = {}; +} + +// Global Update Rate (ms) for All Data Nodes +if (!window.BorealisUpdateRate) { + window.BorealisUpdateRate = 100; +} + +const DataNode = ({ id, data }) => { + const { setNodes } = useReactFlow(); + const edges = useStore(state => state.edges); + + const [renderValue, setRenderValue] = useState(data?.value || ""); + const valueRef = useRef(renderValue); + + // Manual input handler (disabled if connected to input) + const handleManualInput = (e) => { + const newValue = e.target.value; + + // TODO: Add input validation/sanitization here if needed + valueRef.current = newValue; + setRenderValue(newValue); + + window.BorealisValueBus[id] = newValue; + + setNodes(nds => + nds.map(n => + n.id === id + ? { ...n, data: { ...n.data, value: newValue } } + : n + ) + ); + }; + + useEffect(() => { + let currentRate = window.BorealisUpdateRate || 100; + let intervalId = null; + + const runNodeLogic = () => { + const inputEdge = edges.find(e => e?.target === id); + const hasInput = Boolean(inputEdge); + + if (hasInput && inputEdge.source) { + const upstreamValue = window.BorealisValueBus[inputEdge.source] ?? ""; + + // TODO: Insert custom transform logic here (e.g., parseInt, apply formula) + + if (upstreamValue !== valueRef.current) { + valueRef.current = upstreamValue; + setRenderValue(upstreamValue); + window.BorealisValueBus[id] = upstreamValue; + + setNodes(nds => + nds.map(n => + n.id === id + ? { ...n, data: { ...n.data, value: upstreamValue } } + : n + ) + ); + } + } else { + // OUTPUT BROADCAST: emits to downstream via shared memory + window.BorealisValueBus[id] = valueRef.current; + } + }; + + const startInterval = () => { + intervalId = setInterval(runNodeLogic, currentRate); + }; + + startInterval(); + + // Monitor for global update rate changes + const monitor = setInterval(() => { + const newRate = window.BorealisUpdateRate || 100; + if (newRate !== currentRate) { + currentRate = newRate; + clearInterval(intervalId); + startInterval(); + } + }, 250); + + return () => { + clearInterval(intervalId); + clearInterval(monitor); + }; + }, [id, setNodes, edges]); + + const inputEdge = edges.find(e => e?.target === id); + const hasInput = Boolean(inputEdge); + const upstreamId = inputEdge?.source || ""; + const upstreamValue = window.BorealisValueBus[upstreamId] || ""; -const dataNode = ({ data }) => { return (
+ +
+ {data?.label || "Data Node"} +
+ +
+ {/* Description visible in node body */} +
+ {data?.content || "Foundational node for live value propagation."} +
+ + + +
+ -
{data.label || "Data Node"}
-
{data.content || "Placeholder Data Content"}
); }; export default { - type: "dataNode", + type: "DataNode", // REQUIRED: unique identifier for the node type label: "Data Node", - description: "This node acts as a baseline foundational example for all nodes to follow.", - defaultContent: "Placeholder Node", - component: dataNode + description: ` +Foundational Data Node + +- Accepts input from another node +- If no input is connected, allows user-defined value +- Pushes value to downstream nodes every X ms +- Uses BorealisValueBus to communicate with other nodes +`.trim(), + content: "Store Strings, Ints, and Floats", + component: DataNode };