Skip to content

Commit 9448454

Browse files
authored
H-5549: Add support for sending Petri net simulation state back to Hazel (#86)
* support for sending simulation state back to hazel * remove unused import
1 parent 584a987 commit 9448454

File tree

5 files changed

+122
-48
lines changed

5 files changed

+122
-48
lines changed

pocs/petrinaut-hazel/package.json

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,13 @@
1515
"@fortawesome/free-solid-svg-icons": "6.7.2",
1616
"@hashintel/block-design-system": "0.0.5",
1717
"@hashintel/design-system": "0.0.9-canary.2",
18-
"@hashintel/petrinaut": "0.0.5",
18+
"@hashintel/petrinaut": "0.0.5-state-reporting-variant.2",
1919
"@mui/material": "5.18.0",
2020
"@mui/system": "5.18.0",
21-
"elkjs": "0.10.0",
2221
"immer": "10.1.3",
23-
"reactflow": "11.11.4",
2422
"react": "18.3.1",
2523
"react-dom": "18.3.1",
26-
"react-use": "17.6.0",
27-
"uuid": "11.1.0"
24+
"react-use": "17.6.0"
2825
},
2926
"devDependencies": {
3027
"@types/react": "18.3.12",

pocs/petrinaut-hazel/src/main/app.tsx

Lines changed: 81 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
import { useState } from "react";
1+
import { useCallback, useState } from "react";
22
import {
33
Petrinaut,
44
defaultTokenTypes,
55
type PetriNetDefinitionObject,
66
} from "@hashintel/petrinaut";
7-
import { useHazelIntegration } from "./app/use-hazel-integration";
7+
import {
8+
useHazelIntegration,
9+
type HazelValue,
10+
} from "./app/use-hazel-integration";
811
import { produce } from "immer";
12+
import type { MinimalNetMetadata, SimulationState } from "@hashintel/petrinaut";
913

1014
const createDefaultNetDefinition = (): PetriNetDefinitionObject => {
1115
return {
@@ -15,6 +19,8 @@ const createDefaultNetDefinition = (): PetriNetDefinitionObject => {
1519
};
1620
};
1721

22+
const existingNets: MinimalNetMetadata[] = [];
23+
1824
/**
1925
* An incomplete type guard to check if a value is a valid Petri net definition.
2026
* Does not check the content of arrays.
@@ -41,6 +47,22 @@ const isValidNetDefinition = (
4147
return true;
4248
};
4349

50+
/**
51+
* Hazel errors if sent an empty array at the root of an object value returned, e.g. { simulationState: [] }.
52+
*/
53+
const stripEmptyTuple = (
54+
simulationState: SimulationState,
55+
): HazelValue["simulationState"] => {
56+
if (
57+
simulationState.length === 0 ||
58+
Object.keys(simulationState[0]).length === 0
59+
) {
60+
return undefined;
61+
}
62+
63+
return simulationState;
64+
};
65+
4466
/**
4567
* Wraps Petrinaut with the event handlers necessary for a Hazel Livelit.
4668
*/
@@ -50,6 +72,7 @@ export const App = () => {
5072

5173
const [netDefinition, setNetDefinition] =
5274
useState<PetriNetDefinitionObject | null>(null);
75+
const [simulationState, setSimulationState] = useState<SimulationState>([]);
5376

5477
const { setSyntax } = useHazelIntegration({
5578
id,
@@ -60,57 +83,89 @@ export const App = () => {
6083
try {
6184
const parsedValue = JSON.parse(value);
6285

63-
if (isValidNetDefinition(parsedValue)) {
64-
setNetDefinition(parsedValue);
86+
if (isValidNetDefinition(parsedValue.netDefinition)) {
87+
setNetDefinition(parsedValue.netDefinition);
88+
setSimulationState(parsedValue.simulationState ?? []);
6589
} else {
66-
console.error("Invalid net definition", parsedValue);
90+
console.error("Invalid net definition", parsedValue.netDefinition);
6791
const defaultNetDefinition = createDefaultNetDefinition();
6892
setNetDefinition(defaultNetDefinition);
69-
setSyntax(JSON.stringify(defaultNetDefinition));
93+
94+
setSyntax({
95+
netDefinition: defaultNetDefinition,
96+
simulationState: stripEmptyTuple(simulationState),
97+
});
7098
}
7199
} catch (error) {
72100
console.error("Error parsing net definition as JSON", error);
73101
}
74102
},
75103
});
76104

105+
const reportSimulationState = useCallback(
106+
(simulationState: SimulationState) => {
107+
console.log("Simulation state reported");
108+
setSimulationState(simulationState);
109+
110+
setSyntax({
111+
netDefinition: netDefinition as PetriNetDefinitionObject,
112+
simulationState: stripEmptyTuple(simulationState),
113+
});
114+
},
115+
[netDefinition, setSyntax],
116+
);
117+
118+
const mutatePetriNetDefinition = useCallback(
119+
(definitionMutationFn: (definition: PetriNetDefinitionObject) => void) => {
120+
setNetDefinition((existingDefinition) => {
121+
const newDefinition = produce(existingDefinition, definitionMutationFn);
122+
setSyntax({
123+
netDefinition: newDefinition as PetriNetDefinitionObject,
124+
simulationState: stripEmptyTuple(simulationState),
125+
});
126+
return newDefinition;
127+
});
128+
},
129+
[setSyntax, simulationState],
130+
);
131+
77132
if (!netDefinition) {
133+
if (typeof window !== "undefined" && window.self === window.top) {
134+
return (
135+
<p style={{ padding: 15 }}>
136+
This application is designed to be run in an iFrame.
137+
</p>
138+
);
139+
}
78140
return null;
79141
}
80142

81143
return (
82144
<Petrinaut
83145
key={id}
84-
hideNetManagementControls
146+
hideNetManagementControls="includeLoadExampleOnly"
85147
petriNetId={id}
86148
petriNetDefinition={netDefinition}
87-
existingNets={[]}
88-
mutatePetriNetDefinition={(definitionMutationFn) => {
89-
setNetDefinition((existingDefinition) => {
90-
const newDefinition = produce(
91-
existingDefinition,
92-
definitionMutationFn,
93-
);
94-
95-
setSyntax(JSON.stringify(newDefinition));
96-
97-
return newDefinition;
98-
});
99-
}}
149+
existingNets={existingNets}
150+
mutatePetriNetDefinition={mutatePetriNetDefinition}
100151
parentNet={null}
101-
createNewNet={() => {
102-
throw new Error(
103-
"Petrinaut should not be attemping to create new nets when wrapped by Patchwork",
104-
);
152+
createNewNet={({ petriNetDefinition }) => {
153+
setNetDefinition(petriNetDefinition);
154+
setSimulationState([]);
155+
setSyntax({
156+
netDefinition: petriNetDefinition,
157+
simulationState: undefined,
158+
});
105159
}}
106160
loadPetriNet={() => {
107161
throw new Error(
108-
"Petrinaut should not be attemping to load other nets when wrapped by Patchwork",
162+
"Petrinaut should not be attemping to load other nets when used as a Hazel livelit",
109163
);
110164
}}
165+
reportSimulationState={reportSimulationState}
111166
setTitle={() => {
112167
throw new Error(
113-
"Petrinaut should not be attemping to set the net title when wrapped by Patchwork",
168+
"Petrinaut should not be attemping to set the net title when used as a Hazel livelit",
114169
);
115170
}}
116171
title={""}

pocs/petrinaut-hazel/src/main/app/use-hazel-integration.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { PetriNetDefinitionObject } from "@hashintel/petrinaut";
2+
import type { SimulationState } from "@hashintel/petrinaut/dist/petrinaut/types";
13
import { useCallback, useEffect, useState } from "react";
24

35
export type MessageToHazel =
@@ -35,6 +37,11 @@ type HazelIntegrationConfig = {
3537
onInit: (value: string) => void;
3638
};
3739

40+
export type HazelValue = {
41+
netDefinition: PetriNetDefinitionObject;
42+
simulationState: SimulationState | undefined;
43+
};
44+
3845
const sendToHazel = (message: MessageToHazel, targetOrigin: string) => {
3946
if (window.parent && window.parent !== window) {
4047
console.log("Sending message to Hazel", message);
@@ -43,7 +50,7 @@ const sendToHazel = (message: MessageToHazel, targetOrigin: string) => {
4350
};
4451

4552
/**
46-
* Core Hazel integration for SolidJS - handles protocol, messaging, and setup
53+
* Core Hazel integration - handles protocol, messaging, and setup
4754
*/
4855
export const useHazelIntegration = (config: HazelIntegrationConfig) => {
4956
const { id, codec, onInit } = config;
@@ -53,8 +60,11 @@ export const useHazelIntegration = (config: HazelIntegrationConfig) => {
5360
new URLSearchParams(window.location.search).get("parentOrigin") || "*";
5461

5562
const setSyntax = useCallback(
56-
(value: string) => {
57-
sendToHazel({ type: "setSyntax", id, codec, value }, targetOrigin);
63+
(value: HazelValue) => {
64+
sendToHazel(
65+
{ type: "setSyntax", id, codec, value: JSON.stringify(value) },
66+
targetOrigin,
67+
);
5868
},
5969
[id, codec, targetOrigin],
6070
);
@@ -89,7 +99,7 @@ export const useHazelIntegration = (config: HazelIntegrationConfig) => {
8999
return () => {
90100
window.removeEventListener("message", handleMessage);
91101
};
92-
});
102+
}, [hasInit, id, onInit, targetOrigin]);
93103

94104
return {
95105
setSyntax,

pocs/petrinaut-hazel/vite.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,8 @@ export default defineConfig({
88
},
99

1010
plugins: [react()],
11+
12+
server: {
13+
port: 5179,
14+
},
1115
});

pocs/petrinaut-hazel/yarn.lock

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -508,20 +508,18 @@
508508
echarts "5.6.0"
509509
react-loading-skeleton "3.5.0"
510510

511-
"@hashintel/petrinaut@0.0.5":
512-
version "0.0.5"
513-
resolved "https://registry.yarnpkg.com/@hashintel/petrinaut/-/petrinaut-0.0.5.tgz#28fb78da72e07fe9cd7985e04ebfad969e6957cb"
514-
integrity sha512-MlnT1EHx2S1O3o9TKT3Qg8Xsc9op8EBHvMpbX02sYAJjY5MJyoJy1k+RzROFwdT8tQhQQu1ea/A+/ehOGlWZeQ==
511+
"@hashintel/petrinaut@0.0.5-state-reporting-variant.2":
512+
version "0.0.5-state-reporting-variant.2"
513+
resolved "https://registry.yarnpkg.com/@hashintel/petrinaut/-/petrinaut-0.0.5-state-reporting-variant.2.tgz#54cdc1ce36f27a72e132ee1384adb992d0b15045"
514+
integrity sha512-Mmp95R78X0aGn16i7qehzpxvONidVKqWdBxNNjpN2J0D6Dq/rQs/LLZK+IOAncO9ILpn9M0XkmuGLIo2X31Z6w==
515515
dependencies:
516-
"@emotion/react" "11.14.0"
517516
"@fortawesome/free-solid-svg-icons" "6.7.2"
518517
"@hashintel/block-design-system" "0.0.5"
519518
"@hashintel/design-system" "0.0.9-canary.2"
520-
"@mui/material" "5.18.0"
521-
"@mui/system" "5.18.0"
522-
elkjs "0.10.0"
519+
elkjs "0.11.0"
523520
reactflow "11.11.4"
524-
uuid "11.1.0"
521+
uuid "13.0.0"
522+
web-worker "1.4.1"
525523

526524
"@humanwhocodes/config-array@^0.11.10":
527525
version "0.11.14"
@@ -1621,10 +1619,10 @@ electron-to-chromium@^1.5.204:
16211619
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.209.tgz#403e7a84933b7206bb2e737d897042b2a6ef8d3e"
16221620
integrity sha512-Xoz0uMrim9ZETCQt8UgM5FxQF9+imA7PBpokoGcZloA1uw2LeHzTlip5cb5KOAsXZLjh/moN2vReN3ZjJmjI9A==
16231621

1624-
elkjs@0.10.0:
1625-
version "0.10.0"
1626-
resolved "https://registry.yarnpkg.com/elkjs/-/elkjs-0.10.0.tgz#abe2aa6cb25e7439b708fab873b2448d26ed33a1"
1627-
integrity sha512-v/3r+3Bl2NMrWmVoRTMBtHtWvRISTix/s9EfnsfEWApNrsmNjqgqJOispCGg46BPwIFdkag3N/HYSxJczvCm6w==
1622+
elkjs@0.11.0:
1623+
version "0.11.0"
1624+
resolved "https://registry.yarnpkg.com/elkjs/-/elkjs-0.11.0.tgz#ca57b209ff66531c0f77ff32b9667aaeaad45d1b"
1625+
integrity sha512-u4J8h9mwEDaYMqo0RYJpqNMFDoMK7f+pu4GjcV+N8jIC7TRdORgzkfSjTJemhqONFfH6fBI3wpysgWbhgVWIXw==
16281626

16291627
error-ex@^1.3.1:
16301628
version "1.3.2"
@@ -2943,6 +2941,11 @@ uuid@11.1.0:
29432941
resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.1.0.tgz#9549028be1753bb934fc96e2bca09bb4105ae912"
29442942
integrity sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==
29452943

2944+
uuid@13.0.0:
2945+
version "13.0.0"
2946+
resolved "https://registry.yarnpkg.com/uuid/-/uuid-13.0.0.tgz#263dc341b19b4d755eb8fe36b78d95a6b65707e8"
2947+
integrity sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==
2948+
29462949
uuid@^8.3.2:
29472950
version "8.3.2"
29482951
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
@@ -2959,6 +2962,11 @@ vite@5:
29592962
optionalDependencies:
29602963
fsevents "~2.3.3"
29612964

2965+
web-worker@1.4.1:
2966+
version "1.4.1"
2967+
resolved "https://registry.yarnpkg.com/web-worker/-/web-worker-1.4.1.tgz#894e71a8f6fce059dc175864fcdf9e633eb7cc6b"
2968+
integrity sha512-bEYkHEaeUTjiWscVoW7UBLmAV1S9v0AELr9+3B94Ps1G6E5N/jmSth1e5RZoWbLZWqkI/eyb7KT3sto0ugRpLg==
2969+
29622970
which@^2.0.1:
29632971
version "2.0.2"
29642972
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"

0 commit comments

Comments
 (0)