diff --git a/example_graphs/events.json b/example_graphs/events.json new file mode 100644 index 00000000..47c11737 --- /dev/null +++ b/example_graphs/events.json @@ -0,0 +1,140 @@ +{ + "nodes": [ + { + "id": "0", + "type": "constant", + "position": { + "x": 443.74999237060547, + "y": 132.24999809265137 + }, + "data": { + "label": "source", + "value": "" + }, + "measured": { + "width": 205, + "height": 53 + }, + "selected": true + }, + { + "id": "1", + "type": "integrator", + "position": { + "x": 688.7499923706055, + "y": 251.24999809265137 + }, + "data": { + "label": "integrator", + "initial_value": "", + "reset_times": "" + }, + "measured": { + "width": 200, + "height": 48 + }, + "selected": false, + "dragging": false + }, + { + "id": "2", + "type": "scope", + "position": { + "x": 1022.2499923706055, + "y": 252.24999809265137 + }, + "data": { + "label": "scope 2", + "labels": "", + "sampling_rate": "", + "t_wait": "" + }, + "measured": { + "width": 120, + "height": 140 + } + } + ], + "edges": [ + { + "id": "e0-1", + "source": "0", + "target": "1", + "sourceHandle": null, + "targetHandle": null, + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + }, + { + "id": "e0-2", + "source": "0", + "target": "2", + "sourceHandle": null, + "targetHandle": null, + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + }, + { + "id": "e1-2", + "source": "1", + "target": "2", + "sourceHandle": null, + "targetHandle": null, + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + } + ], + "nodeCounter": 3, + "solverParams": { + "dt": "0.01", + "dt_min": "1e-6", + "dt_max": "1.0", + "Solver": "SSPRK22", + "tolerance_fpi": "1e-6", + "iterations_max": "100", + "log": "true", + "simulation_duration": "50.0", + "extra_params": "{}" + }, + "globalVariables": [], + "events": [ + { + "name": "my_event", + "type": "ZeroCrossingDown", + "func_evt": "def func_evt(t):\n *_, x = integrator_1()\n return 10 - x", + "func_act": "def func_act(t):\n source_0.off()", + "tolerance": "1e-8", + "id": 1754342253698 + } + ] +} \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index b2bb8671..0bbba41d 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -18,6 +18,7 @@ import Sidebar from './Sidebar'; import NodeSidebar from './NodeSidebar'; import { DnDProvider, useDnD } from './DnDContext.jsx'; import ContextMenu from './ContextMenu.jsx'; +import EventsTab from './EventsTab.jsx'; import { isValidPythonIdentifier } from './utils.js'; import { makeEdge } from './CustomEdge'; import { nodeTypes } from './nodeConfig.js'; @@ -78,6 +79,7 @@ const DnDFlow = () => { // Global variables state const [globalVariables, setGlobalVariables] = useState([]); + const [events, setEvents] = useState([]); const [defaultValues, setDefaultValues] = useState({}); const [isEditingLabel, setIsEditingLabel] = useState(false); const [tempLabel, setTempLabel] = useState(''); @@ -219,7 +221,8 @@ const DnDFlow = () => { edges, nodeCounter, solverParams, - globalVariables + globalVariables, + events }; // Check if File System Access API is supported @@ -299,7 +302,7 @@ const DnDFlow = () => { } // Load the graph data - const { nodes: loadedNodes, edges: loadedEdges, nodeCounter: loadedNodeCounter, solverParams: loadedSolverParams, globalVariables: loadedGlobalVariables } = graphData; + const { nodes: loadedNodes, edges: loadedEdges, nodeCounter: loadedNodeCounter, solverParams: loadedSolverParams, globalVariables: loadedGlobalVariables, events: loadedEvents } = graphData; setNodes(loadedNodes || []); setEdges(loadedEdges || []); setSelectedNode(null); @@ -316,6 +319,7 @@ const DnDFlow = () => { extra_params: '{}' }); setGlobalVariables(loadedGlobalVariables ?? []); + setEvents(loadedEvents ?? []); alert('Graph loaded successfully!'); } catch (error) { @@ -350,7 +354,7 @@ const DnDFlow = () => { return; } - const { nodes: loadedNodes, edges: loadedEdges, nodeCounter: loadedNodeCounter, solverParams: loadedSolverParams, globalVariables: loadedGlobalVariables } = graphData; + const { nodes: loadedNodes, edges: loadedEdges, nodeCounter: loadedNodeCounter, solverParams: loadedSolverParams, globalVariables: loadedGlobalVariables, events: loadedEvents } = graphData; setNodes(loadedNodes || []); setEdges(loadedEdges || []); setSelectedNode(null); @@ -367,6 +371,7 @@ const DnDFlow = () => { extra_params: '{}' }); setGlobalVariables(loadedGlobalVariables ?? []); + setEvents(loadedEvents ?? []); alert('Graph loaded successfully!'); } catch (error) { @@ -463,7 +468,8 @@ const DnDFlow = () => { edges, nodeCounter, solverParams, - globalVariables + globalVariables, + events }; const response = await fetch(getApiEndpoint('/convert-to-python'), { @@ -533,7 +539,8 @@ const DnDFlow = () => { nodes, edges, solverParams, - globalVariables + globalVariables, + events }; const response = await fetch(getApiEndpoint('/run-pathsim'), { @@ -923,6 +930,20 @@ const DnDFlow = () => { > Graph Editor +