@adcops/autocore-react 3.0.40 → 3.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. package/additional-docs/AutoCoreTagContext.md +441 -0
  2. package/additional-docs/react_performance_notes.md +94 -0
  3. package/dist/assets/BlocklyLogo.d.ts +1 -0
  4. package/dist/assets/BlocklyLogo.d.ts.map +1 -0
  5. package/dist/assets/Distance.d.ts +1 -0
  6. package/dist/assets/Distance.d.ts.map +1 -0
  7. package/dist/assets/JogLong.d.ts +1 -0
  8. package/dist/assets/JogLong.d.ts.map +1 -0
  9. package/dist/assets/JogMedium.d.ts +1 -0
  10. package/dist/assets/JogMedium.d.ts.map +1 -0
  11. package/dist/assets/JogShort.d.ts +1 -0
  12. package/dist/assets/JogShort.d.ts.map +1 -0
  13. package/dist/assets/PythonLogo.d.ts +1 -0
  14. package/dist/assets/PythonLogo.d.ts.map +1 -0
  15. package/dist/assets/Rotation3D.d.ts +1 -0
  16. package/dist/assets/Rotation3D.d.ts.map +1 -0
  17. package/dist/assets/RotationCcw.d.ts +1 -0
  18. package/dist/assets/RotationCcw.d.ts.map +1 -0
  19. package/dist/assets/RotationCcwA.d.ts +1 -0
  20. package/dist/assets/RotationCcwA.d.ts.map +1 -0
  21. package/dist/assets/RotationCcwB.d.ts +1 -0
  22. package/dist/assets/RotationCcwB.d.ts.map +1 -0
  23. package/dist/assets/RotationCcwC.d.ts +1 -0
  24. package/dist/assets/RotationCcwC.d.ts.map +1 -0
  25. package/dist/assets/RotationCw.d.ts +1 -0
  26. package/dist/assets/RotationCw.d.ts.map +1 -0
  27. package/dist/assets/RotationCwA.d.ts +1 -0
  28. package/dist/assets/RotationCwA.d.ts.map +1 -0
  29. package/dist/assets/RotationCwB.d.ts +1 -0
  30. package/dist/assets/RotationCwB.d.ts.map +1 -0
  31. package/dist/assets/RotationCwC.d.ts +1 -0
  32. package/dist/assets/RotationCwC.d.ts.map +1 -0
  33. package/dist/assets/Run.d.ts +1 -0
  34. package/dist/assets/Run.d.ts.map +1 -0
  35. package/dist/assets/Speed.d.ts +1 -0
  36. package/dist/assets/Speed.d.ts.map +1 -0
  37. package/dist/assets/SpeedFast.d.ts +1 -0
  38. package/dist/assets/SpeedFast.d.ts.map +1 -0
  39. package/dist/assets/SpeedMedium.d.ts +1 -0
  40. package/dist/assets/SpeedMedium.d.ts.map +1 -0
  41. package/dist/assets/SpeedNone.d.ts +1 -0
  42. package/dist/assets/SpeedNone.d.ts.map +1 -0
  43. package/dist/assets/SpeedSlow.d.ts +1 -0
  44. package/dist/assets/SpeedSlow.d.ts.map +1 -0
  45. package/dist/assets/Walk.d.ts +1 -0
  46. package/dist/assets/Walk.d.ts.map +1 -0
  47. package/dist/assets/index.d.ts +1 -0
  48. package/dist/assets/index.d.ts.map +1 -0
  49. package/dist/components/AutoCoreDevPanel.d.ts +144 -0
  50. package/dist/components/AutoCoreDevPanel.d.ts.map +1 -0
  51. package/dist/components/AutoCoreDevPanel.js +1 -0
  52. package/dist/components/BlocklyEditor.d.ts +1 -0
  53. package/dist/components/BlocklyEditor.d.ts.map +1 -0
  54. package/dist/components/BlocklyEditor.js +1 -1
  55. package/dist/components/CodeEditor.d.ts +2 -1
  56. package/dist/components/CodeEditor.d.ts.map +1 -0
  57. package/dist/components/CodeEditor.js +1 -1
  58. package/dist/components/FileList.d.ts +1 -0
  59. package/dist/components/FileList.d.ts.map +1 -0
  60. package/dist/components/FileList.js +1 -1
  61. package/dist/components/FileSelect.d.ts +1 -0
  62. package/dist/components/FileSelect.d.ts.map +1 -0
  63. package/dist/components/FileSelect.js +1 -1
  64. package/dist/components/FitText.d.ts +1 -0
  65. package/dist/components/FitText.d.ts.map +1 -0
  66. package/dist/components/FitText.js +1 -1
  67. package/dist/components/Indicator.d.ts +2 -1
  68. package/dist/components/Indicator.d.ts.map +1 -0
  69. package/dist/components/Indicator.js +1 -1
  70. package/dist/components/IndicatorButton.d.ts +2 -1
  71. package/dist/components/IndicatorButton.d.ts.map +1 -0
  72. package/dist/components/IndicatorButton.js +1 -1
  73. package/dist/components/IndicatorRect.d.ts +2 -1
  74. package/dist/components/IndicatorRect.d.ts.map +1 -0
  75. package/dist/components/JogPanel.d.ts +1 -0
  76. package/dist/components/JogPanel.d.ts.map +1 -0
  77. package/dist/components/Lamp.d.ts +2 -1
  78. package/dist/components/Lamp.d.ts.map +1 -0
  79. package/dist/components/Lamp.js +1 -1
  80. package/dist/components/Osk.d.ts +1 -0
  81. package/dist/components/Osk.d.ts.map +1 -0
  82. package/dist/components/Osk.js +1 -1
  83. package/dist/components/OskDialog.d.ts +1 -0
  84. package/dist/components/OskDialog.d.ts.map +1 -0
  85. package/dist/components/ProgressBarWithValue.d.ts +1 -0
  86. package/dist/components/ProgressBarWithValue.d.ts.map +1 -0
  87. package/dist/components/ProgressBarWithValue.js +1 -1
  88. package/dist/components/TextInput.d.ts +62 -103
  89. package/dist/components/TextInput.d.ts.map +1 -0
  90. package/dist/components/TextInput.js +1 -1
  91. package/dist/components/ToggleGroup.d.ts +2 -1
  92. package/dist/components/ToggleGroup.d.ts.map +1 -0
  93. package/dist/components/ToggleGroup.js +1 -1
  94. package/dist/components/ValueDisplay.d.ts +3 -2
  95. package/dist/components/ValueDisplay.d.ts.map +1 -0
  96. package/dist/components/ValueDisplay.js +1 -1
  97. package/dist/components/ValueIndicator.d.ts +2 -1
  98. package/dist/components/ValueIndicator.d.ts.map +1 -0
  99. package/dist/components/ValueIndicator.js +1 -1
  100. package/dist/components/ValueInput.d.ts +2 -1
  101. package/dist/components/ValueInput.d.ts.map +1 -0
  102. package/dist/components/ValueInput.js +1 -1
  103. package/dist/core/ActionMode.d.ts +1 -0
  104. package/dist/core/ActionMode.d.ts.map +1 -0
  105. package/dist/core/AutoCoreTagContext.d.ts +98 -0
  106. package/dist/core/AutoCoreTagContext.d.ts.map +1 -0
  107. package/dist/core/AutoCoreTagContext.js +1 -0
  108. package/dist/core/AutoCoreTagTypes.d.ts +283 -0
  109. package/dist/core/AutoCoreTagTypes.d.ts.map +1 -0
  110. package/dist/core/AutoCoreTagTypes.js +1 -0
  111. package/dist/core/CoreStreamTypes.d.ts +345 -0
  112. package/dist/core/CoreStreamTypes.d.ts.map +1 -0
  113. package/dist/core/CoreStreamTypes.js +1 -0
  114. package/dist/core/EventEmitterContext.d.ts +113 -202
  115. package/dist/core/EventEmitterContext.d.ts.map +1 -0
  116. package/dist/core/EventEmitterContext.js +1 -1
  117. package/dist/core/IndicatorButtonState.d.ts +1 -0
  118. package/dist/core/IndicatorButtonState.d.ts.map +1 -0
  119. package/dist/core/IndicatorColor.d.ts +1 -0
  120. package/dist/core/IndicatorColor.d.ts.map +1 -0
  121. package/dist/core/MaskPatterns.d.ts +1 -0
  122. package/dist/core/MaskPatterns.d.ts.map +1 -0
  123. package/dist/core/NumerableTypes.d.ts +1 -0
  124. package/dist/core/NumerableTypes.d.ts.map +1 -0
  125. package/dist/core/NumerableTypes.js +1 -1
  126. package/dist/core/PositionContext.d.ts +1 -1
  127. package/dist/core/PositionContext.d.ts.map +1 -0
  128. package/dist/core/UniqueId.d.ts +1 -0
  129. package/dist/core/UniqueId.d.ts.map +1 -0
  130. package/dist/core/ValueSimulator.d.ts +2 -1
  131. package/dist/core/ValueSimulator.d.ts.map +1 -0
  132. package/dist/core/ValueSimulator.js +1 -1
  133. package/dist/core/hoc.d.ts +1 -0
  134. package/dist/core/hoc.d.ts.map +1 -0
  135. package/dist/core/hoc.js +1 -1
  136. package/dist/hooks/adsHooks.d.ts +1 -0
  137. package/dist/hooks/adsHooks.d.ts.map +1 -0
  138. package/dist/hooks/adsHooks.js +1 -1
  139. package/dist/hooks/commandHooks.d.ts +4 -3
  140. package/dist/hooks/commandHooks.d.ts.map +1 -0
  141. package/dist/hooks/commandHooks.js +1 -1
  142. package/dist/hooks/index.d.ts +1 -0
  143. package/dist/hooks/index.d.ts.map +1 -0
  144. package/dist/hooks/useAutoCoreTag.d.ts +26 -0
  145. package/dist/hooks/useAutoCoreTag.d.ts.map +1 -0
  146. package/dist/hooks/useAutoCoreTag.js +1 -0
  147. package/dist/hooks/useScaledValue.d.ts +1 -0
  148. package/dist/hooks/useScaledValue.d.ts.map +1 -0
  149. package/dist/hooks/useScaledValue.js +1 -1
  150. package/dist/hub/CommandMessage.d.ts +19 -9
  151. package/dist/hub/CommandMessage.d.ts.map +1 -0
  152. package/dist/hub/CommandMessage.js +1 -1
  153. package/dist/hub/DebugPanel.d.ts +31 -0
  154. package/dist/hub/DebugPanel.d.ts.map +1 -0
  155. package/dist/hub/DebugPanel.js +1 -0
  156. package/dist/hub/HubBase.d.ts +85 -130
  157. package/dist/hub/HubBase.d.ts.map +1 -0
  158. package/dist/hub/HubBase.js +1 -1
  159. package/dist/hub/HubSimulate.d.ts +42 -8
  160. package/dist/hub/HubSimulate.d.ts.map +1 -0
  161. package/dist/hub/HubSimulate.js +1 -1
  162. package/dist/hub/HubTauri.d.ts +25 -60
  163. package/dist/hub/HubTauri.d.ts.map +1 -0
  164. package/dist/hub/HubTauri.js +1 -1
  165. package/dist/hub/HubWebSocket.d.ts +34 -17
  166. package/dist/hub/HubWebSocket.d.ts.map +1 -0
  167. package/dist/hub/HubWebSocket.js +1 -1
  168. package/dist/hub/debug.d.ts +23 -0
  169. package/dist/hub/debug.d.ts.map +1 -0
  170. package/dist/hub/debug.js +1 -0
  171. package/dist/hub/index.d.ts +20 -4
  172. package/dist/hub/index.d.ts.map +1 -0
  173. package/dist/hub/index.js +1 -1
  174. package/package.json +32 -27
  175. package/readme.md +193 -22
  176. package/src/components/AutoCoreDevPanel.tsx +414 -0
  177. package/src/components/CodeEditor.tsx +2 -2
  178. package/src/components/FileList.tsx +7 -6
  179. package/src/components/FileSelect.tsx +2 -1
  180. package/src/components/Indicator.tsx +2 -2
  181. package/src/components/IndicatorButton.tsx +2 -2
  182. package/src/components/IndicatorRect.tsx +2 -2
  183. package/src/components/Lamp.tsx +3 -3
  184. package/src/components/TextInput.tsx +159 -240
  185. package/src/components/ToggleGroup.tsx +3 -3
  186. package/src/components/ValueDisplay.tsx +4 -4
  187. package/src/components/ValueIndicator.tsx +2 -2
  188. package/src/components/ValueInput.tsx +2 -2
  189. package/src/core/ActionMode.ts +1 -1
  190. package/src/core/AutoCoreTagContext.tsx +615 -0
  191. package/src/core/AutoCoreTagTypes.ts +334 -0
  192. package/src/core/CoreStreamTypes.ts +512 -0
  193. package/src/core/EventEmitterContext.tsx +257 -281
  194. package/src/core/IndicatorButtonState.ts +1 -1
  195. package/src/core/ValueSimulator.ts +2 -2
  196. package/src/core/hoc.tsx +1 -1
  197. package/src/hooks/adsHooks.tsx +21 -22
  198. package/src/hooks/commandHooks.tsx +23 -19
  199. package/src/hooks/index.ts +1 -1
  200. package/src/hooks/useAutoCoreTag.ts +103 -0
  201. package/src/hooks/useScaledValue.tsx +1 -1
  202. package/src/hub/CommandMessage.ts +71 -19
  203. package/src/hub/DebugPanel.ts +280 -0
  204. package/src/hub/HubBase.ts +147 -223
  205. package/src/hub/HubSimulate.ts +93 -24
  206. package/src/hub/HubTauri.ts +87 -96
  207. package/src/hub/HubWebSocket.ts +133 -158
  208. package/src/hub/debug.ts +211 -0
  209. package/src/hub/index.ts +49 -39
  210. package/tsconfig.json +43 -28
  211. package/docs/classes/components_BlocklyEditor.BlocklyEditor.html +0 -124
  212. package/docs/classes/components_CodeEditor.CodeEditor.html +0 -128
  213. package/docs/classes/components_JogPanel.JogPanel.html +0 -138
  214. package/docs/classes/components_Lamp.Lamp.html +0 -105
  215. package/docs/classes/components_TextInput.TextInput.html +0 -115
  216. package/docs/classes/components_ValueIndicator.ValueIndicator.html +0 -119
  217. package/docs/classes/components_ValueInput.ValueInput.html +0 -113
  218. package/docs/classes/hub_HubWebSocket.HubWebSocket.html +0 -106
  219. package/docs/enums/components_JogPanel.JogDistanceAction.html +0 -5
  220. package/docs/enums/components_JogPanel.JogPanelAction.html +0 -18
  221. package/docs/enums/components_JogPanel.JogSpeedAction.html +0 -5
  222. package/docs/enums/core_ActionMode.ActionMode.html +0 -6
  223. package/docs/enums/core_IndicatorColor.IndicatorColor.html +0 -23
  224. package/docs/functions/assets_BlocklyLogo.default.html +0 -1
  225. package/docs/functions/assets_Distance.default.html +0 -1
  226. package/docs/functions/assets_JogLong.default.html +0 -1
  227. package/docs/functions/assets_JogMedium.default.html +0 -1
  228. package/docs/functions/assets_JogShort.default.html +0 -1
  229. package/docs/functions/assets_PythonLogo.default.html +0 -1
  230. package/docs/functions/assets_Rotation3D.default.html +0 -1
  231. package/docs/functions/assets_RotationCcw.default.html +0 -1
  232. package/docs/functions/assets_RotationCcwA.default.html +0 -1
  233. package/docs/functions/assets_RotationCcwB.default.html +0 -1
  234. package/docs/functions/assets_RotationCcwC.default.html +0 -1
  235. package/docs/functions/assets_RotationCw.default.html +0 -1
  236. package/docs/functions/assets_RotationCwA.default.html +0 -1
  237. package/docs/functions/assets_RotationCwB.default.html +0 -1
  238. package/docs/functions/assets_RotationCwC.default.html +0 -1
  239. package/docs/functions/assets_Run.default.html +0 -1
  240. package/docs/functions/assets_Speed.default.html +0 -1
  241. package/docs/functions/assets_SpeedFast.default.html +0 -1
  242. package/docs/functions/assets_SpeedMedium.default.html +0 -1
  243. package/docs/functions/assets_SpeedNone.default.html +0 -1
  244. package/docs/functions/assets_SpeedSlow.default.html +0 -1
  245. package/docs/functions/assets_Walk.default.html +0 -1
  246. package/docs/functions/components_BlocklyEditor.createCustomToolbox.html +0 -6
  247. package/docs/functions/components_FileList.FileList.html +0 -21
  248. package/docs/functions/components_FitText.FitText.html +0 -8
  249. package/docs/functions/components_ToggleGroup.ToggleGroup.html +0 -5
  250. package/docs/interfaces/components_JogPanel.JogPanelButtonDefinition.html +0 -5
  251. package/docs/interfaces/components_ToggleGroup.ToggleGroupProps.html +0 -618
  252. package/docs/interfaces/core_IndicatorButtonState.IndicatorButtonState.html +0 -10
  253. package/docs/interfaces/hub_CommandMessage.CommandMessage.html +0 -6
  254. package/docs/interfaces/hub_CommandMessage.CommandMessageResult.html +0 -4
  255. package/docs/modules/assets.html +0 -23
  256. package/docs/modules/assets_BlocklyLogo.html +0 -2
  257. package/docs/modules/assets_Distance.html +0 -2
  258. package/docs/modules/assets_JogLong.html +0 -2
  259. package/docs/modules/assets_JogMedium.html +0 -2
  260. package/docs/modules/assets_JogShort.html +0 -2
  261. package/docs/modules/assets_PythonLogo.html +0 -2
  262. package/docs/modules/assets_Rotation3D.html +0 -2
  263. package/docs/modules/assets_RotationCcw.html +0 -2
  264. package/docs/modules/assets_RotationCcwA.html +0 -2
  265. package/docs/modules/assets_RotationCcwB.html +0 -2
  266. package/docs/modules/assets_RotationCcwC.html +0 -2
  267. package/docs/modules/assets_RotationCw.html +0 -2
  268. package/docs/modules/assets_RotationCwA.html +0 -2
  269. package/docs/modules/assets_RotationCwB.html +0 -2
  270. package/docs/modules/assets_RotationCwC.html +0 -2
  271. package/docs/modules/assets_Run.html +0 -2
  272. package/docs/modules/assets_Speed.html +0 -2
  273. package/docs/modules/assets_SpeedFast.html +0 -2
  274. package/docs/modules/assets_SpeedMedium.html +0 -2
  275. package/docs/modules/assets_SpeedNone.html +0 -2
  276. package/docs/modules/assets_SpeedSlow.html +0 -2
  277. package/docs/modules/assets_Walk.html +0 -2
  278. package/docs/modules/components_BlocklyEditor.html +0 -5
  279. package/docs/modules/components_CodeEditor.html +0 -3
  280. package/docs/modules/components_FileList.html +0 -3
  281. package/docs/modules/components_FitText.html +0 -3
  282. package/docs/modules/components_JogPanel.html +0 -9
  283. package/docs/modules/components_Lamp.html +0 -4
  284. package/docs/modules/components_TextInput.html +0 -2
  285. package/docs/modules/components_ToggleGroup.html +0 -6
  286. package/docs/modules/components_ValueIndicator.html +0 -4
  287. package/docs/modules/components_ValueInput.html +0 -2
  288. package/docs/modules/core_ActionMode.html +0 -2
  289. package/docs/modules/core_IndicatorButtonState.html +0 -2
  290. package/docs/modules/core_IndicatorColor.html +0 -2
  291. package/docs/modules/hub_CommandMessage.html +0 -3
  292. package/docs/modules/hub_HubWebSocket.html +0 -2
  293. package/docs/types/components_IndicatorButton.IndicatorButtonOptionsType.html +0 -1
  294. package/docs/variables/components_BlocklyEditor.StandardToolbox.html +0 -1
  295. package/docs/variables/components_JogPanel.DefaultLinearJogButtons.html +0 -2
  296. package/docs/variables/components_JogPanel.DefaultRotationJogButtons.html +0 -2
@@ -3,87 +3,49 @@
3
3
  * Created Date: 2023-12-17 09:50:23
4
4
  * Author: Thomas C. Bitsky Jr.
5
5
  * -----
6
- * Last Modified: 2024-04-24 11:56:32
6
+ * Last Modified: 2026-01-29 09:34:21
7
7
  * Modified By: ADC
8
8
  * -----
9
- *
9
+ *
10
10
  */
11
11
 
12
- import { InvokeArgs } from '@tauri-apps/api/tauri';
13
12
  import { HubBase } from './HubBase';
14
- import {event, invoke} from '@tauri-apps/api';
15
- import {CommandMessageResult} from "./CommandMessage";
13
+ import { invoke as tauriInvoke } from '@tauri-apps/api/core';
14
+ import { listen } from '@tauri-apps/api/event';
15
+ import type { CommandMessage } from "./CommandMessage";
16
+ import { MessageType } from "./CommandMessage";
17
+
18
+ type InvokeArgs = Record<string, unknown>;
16
19
 
17
20
  /**
18
21
  * Hub for integrating with the Tauri backend.
19
- *
20
- * The tauri backend is expected to broadcast messages on the event
22
+ *
23
+ * The Tauri backend is expected to broadcast messages on the event
21
24
  * name: 'autocore://broadcast_event'
22
- *
23
- * This hub will capture those messages and dispatch them globally to any
25
+ *
26
+ * This hub will capture those messages and dispatch them globally to any
24
27
  * listeners in the web app.
25
- *
26
- *
27
- * Example: Listen to an event 'xarm-position':
28
- * ```
29
- * const {subscribe, unsubscribe} = useContext(EventEmitterContext);
30
- * useEffect(() => {
31
- * const unsubscripeMp = subscribe('xarm-position', (value) => {
32
- * // The rust backend sent a JSON object of 3D position values.
33
- * setX(value.x);
34
- * setY(value.y);
35
- * setZ(value.z);
36
- * setA(value.roll);
37
- * setB(value.yaw);
38
- * setC(value.pitch);
39
- * });
40
- *
41
- * return () => {
42
- * unsubscribe(unsubscripeMp);
43
- * }
44
- *
45
- * }, [] );
46
28
  *
29
+ * ## Usage Examples
30
+ *
31
+ * ```typescript
32
+ * const { hub } = useContext(EventEmitterContext);
33
+ *
34
+ * // Read a value
35
+ * const result = await hub.read("ads.plc1.gio.bControlPowerOk");
36
+ *
37
+ * // Write a value
38
+ * await hub.write("ads.plc1.gio.nSetpoint", 100);
39
+ *
40
+ * // Subscribe to changes
41
+ * await hub.subscribe("modbus.holding_registers.5");
47
42
  * ```
48
- *
49
- * The hub should also be used for invoking events in the Tauri backend.
50
- * This example will call the function "update_count" in the rust backend, passing
51
- * the expected argument "count".
52
- * ```
53
- * const {invoke} = useContext(EventEmitterContext);
54
- * const incrementCount = () => {
55
- * count += 1;
56
- * invoke('update_count', {"count": count});
57
- * };
58
- * ```
59
- *
60
- * Of course, like any class derived from HubBase, the hub can be used to publish and subscribe to
61
- * topics in the front-end, without need of interacting with the Tauri backed.
62
- *
63
- * ```
64
- * const {dispatch, subscribe, unsubscribe} = useContext(EventEmitterContext);
65
- * const [controlPower, setControlPower] = useState(false);
66
- * useEffect(() => {
67
- * const unsubscribeControlPower = subscribe('value-simulator-bBit1', (value) => {
68
- * setControlPower(value);
69
- * });
70
43
  *
44
+ * The hub can also publish/subscribe to frontend-only events:
71
45
  *
72
- * return () => {
73
- * unsubscribe(unsubscribeControlPower);
74
- * }
75
- * }, [] );
76
- *
77
- * const onPbPressed = () => {
78
- * let count = 1;
79
- * dispatch({
80
- * topic: "my-awesome-topic",
81
- * payload: count
82
- * });
83
- * }
84
- *
46
+ * ```typescript
47
+ * hub.publish("ui/panel-opened", { panelId: "settings" });
85
48
  * ```
86
- *
87
49
  */
88
50
  export class HubTauri extends HubBase {
89
51
 
@@ -93,57 +55,86 @@ export class HubTauri extends HubBase {
93
55
  constructor() {
94
56
  super();
95
57
 
58
+ // Mark as connected since Tauri IPC is always available
59
+ this.setIsConnected(true);
96
60
 
97
61
  /**
98
62
  * Receive broadcasts from the Tauri backend. Data is received as a payload object,
99
63
  * {topic: string, payload: any | null}
100
64
  * From here, we extract into a form HubBase can use, then pass on to the
101
65
  * dispatcher.
102
- *
66
+ *
103
67
  * Events without a topic are ignored.
104
68
  */
105
- event.listen('autocore://broadcast_event', (ev: any) => {
106
-
69
+ listen('autocore://broadcast_event', (ev: any) => {
107
70
  let objPayload = JSON.parse(ev.payload);
108
71
 
109
- if (objPayload.topic !== undefined && objPayload.topic !== null ) {
110
-
111
- if ( objPayload.payload !== undefined && objPayload.payload !== null ) {
72
+ if (objPayload.topic !== undefined && objPayload.topic !== null) {
73
+ if (objPayload.payload !== undefined && objPayload.payload !== null) {
112
74
  this.publish(objPayload.topic, objPayload.payload);
113
75
  }
114
76
  else {
115
77
  this.publish(objPayload.topic, undefined);
116
78
  }
117
-
118
79
  }
119
-
120
80
  });
121
-
122
81
  }
123
82
 
124
83
  /**
125
- * Invoke a method in the rust backend.
126
- *
127
- * @param fname method name
128
- * @param payload Optional data payload
129
- * @returns The return value of the backend method.
84
+ * Send a message to the Tauri backend.
85
+ *
86
+ * The topic and messageType are passed to a generic Tauri command handler
87
+ * that routes the request appropriately.
88
+ *
89
+ * @param topic The FQDN of the resource (e.g., "ads.plc1.gio.bControlPowerOk")
90
+ * @param messageType The action to perform (Read, Write, Subscribe, etc.)
91
+ * @param payload Optional data payload (e.g., value to write)
92
+ * @returns Promise<CommandMessage> The response from the backend
130
93
  */
131
- invoke(domain : string, fname: string, payload?: object | undefined): Promise<CommandMessageResult> {
132
-
133
- console.log(JSON.stringify(event));
94
+ invoke(topic: string, messageType: MessageType, payload?: object): Promise<CommandMessage> {
95
+ console.log(`HubTauri.invoke: topic=${topic}, type=${MessageType[messageType]}`);
134
96
 
135
- if (payload !== undefined && payload !== null) {
136
- console.log(`Payload: ${JSON.stringify(payload)}`);
97
+ // Build the command arguments
98
+ const args: InvokeArgs = {
99
+ topic: topic,
100
+ message_type: messageType,
101
+ ...(payload ?? {})
102
+ };
137
103
 
138
- // todo! This should probably always been the same function, and then that function
139
- // in the backend routes the command.
140
- let topic = `${domain}_${fname}`;
141
- return invoke(topic, payload as InvokeArgs);
142
- }
143
- else {
144
- return invoke(fname);
145
- }
146
-
104
+ // Call the generic autocore handler in Tauri backend
105
+ return new Promise((resolve, reject) => {
106
+ tauriInvoke('autocore_invoke', args)
107
+ .then((result: any) => {
108
+ // If the result is already a CommandMessage, use it directly
109
+ if (result && typeof result === 'object' && 'topic' in result) {
110
+ resolve(result as CommandMessage);
111
+ } else {
112
+ // Wrap raw result in CommandMessage
113
+ const response: CommandMessage = {
114
+ transaction_id: 0,
115
+ timecode: Date.now(),
116
+ topic: topic,
117
+ message_type: MessageType.Response,
118
+ data: result,
119
+ success: true,
120
+ error_message: ''
121
+ };
122
+ resolve(response);
123
+ }
124
+ })
125
+ .catch((error: any) => {
126
+ // Wrap error in CommandMessage
127
+ const response: CommandMessage = {
128
+ transaction_id: 0,
129
+ timecode: Date.now(),
130
+ topic: topic,
131
+ message_type: MessageType.Response,
132
+ data: null,
133
+ success: false,
134
+ error_message: error?.toString() || 'Unknown error'
135
+ };
136
+ reject(response);
137
+ });
138
+ });
147
139
  }
148
-
149
- }
140
+ }
@@ -2,36 +2,40 @@
2
2
  * Copyright (C) 2024 Automated Design Corp.. All Rights Reserved.
3
3
  * Created Date: 2024-04-17 09:13:07
4
4
  * -----
5
- * Last Modified: 2025-03-31 08:35:12
5
+ * Last Modified: 2026-01-29 09:34:31
6
+ * Modified By: ADC
6
7
  * -----
7
8
  *
8
9
  */
9
10
 
10
- /** @file
11
+ /**
12
+ * @module hub/HubWebSocket
11
13
  *
12
- * ## broadcast messages
13
- * The result field of CommandMessages used for broadcast must have
14
- * success set to true, and must contain the field "topic." The topic field, along
15
- * with the domain field of the CommandMessage will be combined for the topic used
16
- * by the websocket client.
14
+ * Implementation of `HubBase` using standard WebSockets.
17
15
  *
18
- * broadcast topic = `${CommandMessage.domain}/${CommandMessage.result["topic"]}
16
+ * ## Functionality
19
17
  *
20
- * So, to subscribe to a broadcast topic for GM.fReal that is served by the ADS client, the
21
- * topic will be: "ADS/GM.fReal"
18
+ * - **Transport**: Connects to `ws://host:port/ws/` (or `wss://`).
19
+ * - **Request/Response**: Correlates requests to responses using `transaction_id`.
20
+ * - **Broadcasts**: Handles unsolicited messages (subscriptions) and routes them via `HubBase.publish`.
21
+ * - **Binary Data**: Handles `FILE_DOWNLOAD` events by decoding Base64 payloads into Blobs.
22
+ *
23
+ * ## Protocol
24
+ *
25
+ * Messages are JSON-serialized `CommandMessage` objects.
26
+ * - **Requests**: Client sends `message_type` (Read/Write/etc) + `transaction_id`.
27
+ * - **Responses**: Server replies with same `transaction_id` + `success`/`data`.
28
+ * - **Broadcasts**: Server sends `message_type: Broadcast` + `topic` (FQDN) + `data`.
22
29
  */
23
30
 
24
31
  import { HubBase } from './HubBase';
25
- import {CommandMessage, CommandMessageResult} from "./CommandMessage";
26
-
27
- // Function to parse the JSON string received from the server into a CommandMessage object
28
- // function parseCommandMessage(jsonString: string): CommandMessage {
29
- // const parsed: CommandMessage = JSON.parse(jsonString);
30
- // // Assuming the JSON structure directly matches the TypeScript interfaces
31
- // return parsed;
32
- // }
33
-
32
+ import { MessageType, type CommandMessage } from "./CommandMessage";
33
+ import { getDebugPanel } from './DebugPanel';
34
34
 
35
+ /**
36
+ * Internal tracking record for pending requests.
37
+ * Allows the `invoke` promise to be resolved/rejected when the response arrives.
38
+ */
35
39
  interface RequestRecord {
36
40
  resolve: (value?: any) => void;
37
41
  reject: (reason?: any) => void;
@@ -40,237 +44,208 @@ interface RequestRecord {
40
44
 
41
45
  /**
42
46
  * Converts a Base64 encoded string into a Blob object.
47
+ * Used for file downloads.
43
48
  *
44
- * This function is useful for handling binary data encoded in Base64 format,
45
- * especially when the data needs to be reconstructed into a binary format
46
- * such as when downloading files that were transferred as Base64 strings over
47
- * a network. The function slices the Base64 string, decodes it to binary,
48
- * and constructs a Blob from the resulting byte arrays.
49
- *
50
- * @param {string} b64Data The base64 encoded string you want to convert to a Blob.
51
- * @param {string} [contentType='application/octet-stream'] The MIME type of the Blob.
52
- * This parameter is optional and defaults to 'application/octet-stream'.
53
- * @param {number} [sliceSize=512] The size of each chunk used to slice the Base64 string.
54
- * Smaller slice sizes can improve the function's handling of large Base64 strings.
55
- * This parameter is optional and defaults to 512.
56
- *
57
- * @returns {Blob} A Blob object representing the decoded binary data.
49
+ * @param {string} b64Data The base64 encoded string.
50
+ * @param {string} [contentType='application/octet-stream'] The MIME type.
51
+ * @param {number} [sliceSize=512] Chunk size for processing.
52
+ * @returns {Blob} The decoded binary blob.
58
53
  */
59
54
  function b64toBlob(
60
- b64Data: string,
61
- contentType: string = 'application/octet-stream',
55
+ b64Data: string,
56
+ contentType: string = "application/octet-stream",
62
57
  sliceSize: number = 512
63
58
  ): Blob {
64
-
65
59
  const byteCharacters = atob(b64Data);
66
- const byteArrays: Uint8Array[] = [];
60
+ const buffers: ArrayBuffer[] = [];
67
61
 
68
62
  for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
69
63
  const slice = byteCharacters.slice(offset, offset + sliceSize);
70
64
 
71
- const byteNumbers: number[] = new Array(slice.length);
72
- for (let i = 0; i < slice.length; i++) {
73
- byteNumbers[i] = slice.charCodeAt(i);
74
- }
65
+ const byteNumbers = new Array<number>(slice.length);
66
+ for (let i = 0; i < slice.length; i++) byteNumbers[i] = slice.charCodeAt(i);
75
67
 
76
- const byteArray = new Uint8Array(byteNumbers);
77
- byteArrays.push(byteArray);
68
+ const uint8 = new Uint8Array(byteNumbers);
69
+ buffers.push(uint8.buffer as ArrayBuffer);
78
70
  }
79
71
 
80
- const blob = new Blob(byteArrays, { type: contentType });
81
- return blob;
72
+ return new Blob(buffers, { type: contentType });
82
73
  }
83
74
 
84
75
 
76
+
85
77
  /**
86
- * Creates a temporary hyperlink in the document and programmatically triggers a download of the Blob provided.
78
+ * Triggers a browser download for a Blob object.
87
79
  *
88
- * This function is useful for triggering downloads of binary data such as files, which are represented
89
- * by Blob objects in the browser. The function creates a URL from the Blob, sets it as the href of a
90
- * dynamically created anchor tag (`<a>`), sets the suggested filename, and then simulates a click on
91
- * this link to start the download. After the download is triggered, the link is removed and the URL
92
- * is revoked to free up memory.
93
- *
94
- * @param {Blob} blob The Blob object containing the data to be downloaded.
95
- * @param {string} filename The filename to be used for the downloaded file.
80
+ * @param {Blob} blob The binary data.
81
+ * @param {string} filename The suggested filename.
96
82
  */
97
83
  function downloadBlob(blob: Blob, filename: string): void {
98
- // Create a URL for the blob object
99
84
  const url = window.URL.createObjectURL(blob);
100
-
101
- // Create a temporary anchor tag (`<a>`) element
102
85
  const a = document.createElement('a');
103
- a.href = url; // Set the href to the blob URL
104
- a.download = filename; // Suggest a filename for the downloaded file
105
-
106
- // Append the anchor tag to the body of the document
86
+ a.href = url;
87
+ a.download = filename;
107
88
  document.body.appendChild(a);
108
-
109
- // Programmatically trigger a click on the anchor tag
110
89
  a.click();
111
-
112
- // Remove the anchor tag from the document
113
90
  document.body.removeChild(a);
114
-
115
- // Revoke the blob URL to free up resources
116
91
  window.URL.revokeObjectURL(url);
117
92
  }
118
93
 
119
94
 
120
-
121
-
95
+ /**
96
+ * WebSocket implementation of the Hub communication layer.
97
+ */
122
98
  export class HubWebSocket extends HubBase {
123
99
  private socket: WebSocket;
124
100
  private requestId = 0;
101
+
102
+ /**
103
+ * Map of pending Transaction ID -> Promise Resolvers.
104
+ * Used to match asynchronous WebSocket responses to their original requests.
105
+ */
125
106
  private pendingRequests = new Map<number, RequestRecord>();
126
107
 
127
108
  /**
128
- * Constructor. Creates and attempts to make the Websocket connection.
109
+ * Initializes the WebSocket connection immediately.
129
110
  */
130
111
  constructor() {
131
112
  super();
132
-
133
- const host = window.location.hostname; // Get the hostname from the address bar
134
-
135
- // Default to using autocore-server.
136
- let port = undefined; // window.location.port; // Get the port from the address bar, if any
113
+ console.log("[HubWebSocket] Constructor called");
114
+ getDebugPanel().hubCreated();
137
115
 
138
- const proto = window.location.protocol === 'https:' ? 'wss://' : 'ws://'; // Determine the protocol
139
- const wsUrl = `${proto}${host}${port ? ':' + port : ''}/ws/`; // Construct WebSocket URL
140
-
141
- this.socket = new WebSocket(wsUrl);
116
+ const host = window.location.hostname;
117
+ const port = window.location.port;
142
118
 
119
+ const proto = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
120
+ const wsUrl = `${proto}${host}${port ? ':' + port : ''}/ws/`;
143
121
 
122
+ this.socket = new WebSocket(wsUrl);
144
123
  let self = this;
145
124
 
146
- //
147
- // Websocket connection established.
148
- //
149
- this.socket.onopen = function() {
125
+ // --- Connection Handlers ---
126
+
127
+ this.socket.onopen = function () {
150
128
  console.log("WebSocket connection established.");
129
+ getDebugPanel().wsOpened(wsUrl);
130
+ // Notify app that we are online
151
131
  self.publish("HUB/connected", true);
152
132
  self.setIsConnected(true);
153
133
  };
154
-
155
-
156
- // Message recevied via the websocket connection.
157
- //
158
- this.socket.onmessage = (event) => {
159
- const data: CommandMessage = JSON.parse(event.data);
160
134
 
161
- // console.log(`MSG: ${JSON.stringify(data)}`);
135
+ // --- Message Handler ---
162
136
 
163
- if (data.request_id && this.pendingRequests.has(data.request_id)) {
164
-
165
- const { resolve, reject } = this.pendingRequests.get(data.request_id)!;
166
-
167
- if (data.fname === "FILE_DOWNLOAD"
168
- && data.args["file_name"] !== undefined && data.args["file_name"] !== null
169
- && data.args["file_name"].length > 0
137
+ this.socket.onmessage = (event) => {
138
+ const data: CommandMessage = JSON.parse(event.data);
139
+ getDebugPanel().messageReceived(
140
+ MessageType[data.message_type] || String(data.message_type),
141
+ data.topic,
142
+ data.transaction_id
143
+ );
144
+
145
+ console.log(`HUB MSG RX: ${JSON.stringify(data)}`);
146
+
147
+ // Check if this message is a response to a pending request (has ID)
148
+ if (data.transaction_id && this.pendingRequests.has(data.transaction_id)) {
149
+ const { resolve, reject } = this.pendingRequests.get(data.transaction_id)!;
150
+
151
+ // SPECIAL CASE: File Download Response
152
+ if (data.topic === "FILE_DOWNLOAD"
153
+ && data.data["file_name"] !== undefined && data.data["file_name"] !== null
154
+ && data.data["file_name"].length > 0
170
155
  ) {
171
-
172
- // The file name was supplied in the original request
173
- let tokens = data.args["file_name"].split("/");
156
+ let tokens = data.data["file_name"].split("/");
157
+ const filename = tokens[tokens.length - 1];
174
158
 
175
- const filename = tokens[tokens.length - 1];
176
- if (data.result && data.result.success) {
177
- const blob = b64toBlob(data.result.data);
159
+ if (data.success) {
160
+ // Decode and trigger download
161
+ const blob = b64toBlob(data.data);
178
162
  downloadBlob(blob, filename);
179
-
180
- // Don't send the whole file through; that's crazy.
181
- data.result.data = filename;
182
-
183
- // Signal the function that initiated the download that all is well.
184
- resolve(data.result);
185
163
 
164
+ // Clean up response data to avoid passing giant string to app
165
+ data.data = filename;
166
+ resolve(data);
186
167
  } else {
187
- // Signal the function that initiated the download that it failed.
188
- reject(new Error(data.result?.error_message));
168
+ reject(new Error(data.error_message));
189
169
  }
190
-
191
170
  }
171
+ // STANDARD CASE: Normal Command Response
192
172
  else {
193
-
194
- if (!data.result?.success) {
195
- // Return the result to the function that initiated the request.
196
- reject(new Error(data.result?.error_message));
173
+ if (!data.success) {
174
+ reject(new Error(data.error_message));
197
175
  } else {
198
- // Send the error to the function that intiated the request.
199
- resolve(data.result);
200
- }
176
+ // Success: resolve with the full message
177
+ resolve(data);
178
+ }
201
179
  }
202
- this.pendingRequests.delete(data.request_id);
180
+ // Cleanup request record
181
+ this.pendingRequests.delete(data.transaction_id);
203
182
  } else {
183
+ // No transaction ID means it's an unsolicited broadcast (subscription update)
184
+ console.log(`HUB BROADCAST: ${JSON.stringify(data)}`);
204
185
  this.handleUnsolicitedMessage(data);
205
186
  }
206
187
  };
207
188
 
208
- //
209
- // Error occurred in the Websocket connection.
210
- //
211
189
  this.socket.onerror = (error: Event) => {
212
190
  console.error('WebSocket error:', error);
191
+ getDebugPanel().wsError(String(error));
213
192
  };
214
193
 
215
- //
216
- // The Websocket connection has closed.
217
- //
218
- this.socket.onclose = () => {
194
+ this.socket.onclose = (event: CloseEvent) => {
219
195
  self.setIsConnected(false);
220
- console.log('WebSocket connection closed.');
196
+ console.log('WebSocket connection closed.');
197
+ getDebugPanel().wsClosed(event.code, event.reason || 'No reason');
221
198
  };
222
199
  }
223
200
 
224
201
 
225
202
  /**
226
- * Invoke a command in the remote webserver.
227
- * @param domain The domain of the Servelet supplying the functionality.
228
- * @param fname The name of the command to execute.
229
- * @param payload The arguments of the command.
230
- * @returns Promise<CommandMessageResult>
203
+ * Sends a command to the server via WebSocket.
204
+ *
205
+ * Wraps the operation in a Promise that resolves when the server replies
206
+ * with a matching `transaction_id`.
231
207
  */
232
- invoke = (domain : string, fname: string, payload?: object): Promise<CommandMessageResult> => {
208
+ invoke = (topic: string, messageType: MessageType, payload?: object): Promise<CommandMessage> => {
233
209
 
234
210
  return new Promise((resolve, reject) => {
235
- const id = ++this.requestId; // Increment and use the request ID
211
+ const id = ++this.requestId; // Generate unique ID
236
212
  this.pendingRequests.set(id, { resolve, reject });
237
213
 
238
- let cm : CommandMessage = {
239
- request_id: id,
240
- domain: domain,
241
- fname: fname,
242
- args: payload,
243
- result: undefined
214
+ const data = payload ? payload : {};
215
+
216
+ const cm: CommandMessage = {
217
+ transaction_id: id,
218
+ topic: topic,
219
+ data: data,
220
+ timecode: Date.now(),
221
+ message_type: messageType,
222
+ success: false,
223
+ error_message: ''
244
224
  };
245
225
 
226
+ getDebugPanel().messageSent(MessageType[messageType], topic, id);
246
227
  this.socket.send(JSON.stringify(cm));
247
228
  });
248
229
  }
249
230
 
231
+ /**
232
+ * Processes messages that are not responses to a specific request.
233
+ * Usually these are subscription updates (Broadcasts).
234
+ */
250
235
  handleUnsolicitedMessage = (msg: CommandMessage) => {
251
- // Handle messages that do not correspond to a pending request
252
-
253
- if (msg.fname === "BROADCAST"
254
- && msg.domain.length >= 1
255
- && msg.result !== undefined && msg.result !== null
256
- && msg.result.data !== undefined && msg.result.data !== null
257
- ) {
258
-
259
- let topic = `${msg.domain}/${msg.result.data["topic"]}`;
260
-
261
- //console.log(`HUB PUBLISHING: ${topic} : ${JSON.stringify(msg.result.data)}`);
262
- this.publish(topic, msg.result.data);
236
+ if (msg.message_type == MessageType.Broadcast && msg.topic.length > 1) {
237
+ console.log(`HUB PUBLISHING: ${msg.topic} : ${JSON.stringify(msg.data)}`);
238
+
239
+ // Pass to HubBase to map the FQDN topic to a local tagName and dispatch
240
+ this.publish(msg.topic, msg.data);
263
241
  }
264
- // else {
265
- // console.error("INVALID BROADCAST RECEIVED!");
266
- // }
267
242
  }
268
243
 
269
- disconnect= (): void => {
244
+ disconnect = (): void => {
270
245
  this.socket.close();
271
246
  }
272
247
 
273
248
  protected emit = (eventName: string, payload?: object): void => {
274
249
  this.socket.send(JSON.stringify({ eventName, payload }));
275
250
  }
276
- }
251
+ }