@adcops/autocore-react 3.1.1 → 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 (252) hide show
  1. package/additional-docs/react_performance_notes.md +94 -0
  2. package/dist/components/AutoCoreDevPanel.d.ts.map +1 -1
  3. package/dist/components/AutoCoreDevPanel.js +1 -1
  4. package/dist/components/FileList.d.ts.map +1 -1
  5. package/dist/components/FileList.js +1 -1
  6. package/dist/components/FileSelect.d.ts.map +1 -1
  7. package/dist/components/FileSelect.js +1 -1
  8. package/dist/core/AutoCoreTagContext.d.ts +59 -185
  9. package/dist/core/AutoCoreTagContext.d.ts.map +1 -1
  10. package/dist/core/AutoCoreTagContext.js +1 -1
  11. package/dist/core/AutoCoreTagTypes.d.ts +127 -6
  12. package/dist/core/AutoCoreTagTypes.d.ts.map +1 -1
  13. package/dist/core/CoreStreamTypes.d.ts +345 -0
  14. package/dist/core/CoreStreamTypes.d.ts.map +1 -0
  15. package/dist/core/CoreStreamTypes.js +1 -0
  16. package/dist/core/EventEmitterContext.d.ts +91 -473
  17. package/dist/core/EventEmitterContext.d.ts.map +1 -1
  18. package/dist/core/EventEmitterContext.js +1 -1
  19. package/dist/hooks/adsHooks.d.ts.map +1 -1
  20. package/dist/hooks/adsHooks.js +1 -1
  21. package/dist/hooks/commandHooks.d.ts +3 -3
  22. package/dist/hooks/commandHooks.d.ts.map +1 -1
  23. package/dist/hooks/commandHooks.js +1 -1
  24. package/dist/hooks/useAutoCoreTag.js +1 -1
  25. package/dist/hub/CommandMessage.d.ts +18 -9
  26. package/dist/hub/CommandMessage.d.ts.map +1 -1
  27. package/dist/hub/CommandMessage.js +1 -1
  28. package/dist/hub/DebugPanel.d.ts +31 -0
  29. package/dist/hub/DebugPanel.d.ts.map +1 -0
  30. package/dist/hub/DebugPanel.js +1 -0
  31. package/dist/hub/HubBase.d.ts +83 -129
  32. package/dist/hub/HubBase.d.ts.map +1 -1
  33. package/dist/hub/HubBase.js +1 -1
  34. package/dist/hub/HubSimulate.d.ts +41 -8
  35. package/dist/hub/HubSimulate.d.ts.map +1 -1
  36. package/dist/hub/HubSimulate.js +1 -1
  37. package/dist/hub/HubTauri.d.ts +24 -60
  38. package/dist/hub/HubTauri.d.ts.map +1 -1
  39. package/dist/hub/HubTauri.js +1 -1
  40. package/dist/hub/HubWebSocket.d.ts +33 -17
  41. package/dist/hub/HubWebSocket.d.ts.map +1 -1
  42. package/dist/hub/HubWebSocket.js +1 -1
  43. package/dist/hub/debug.d.ts +23 -0
  44. package/dist/hub/debug.d.ts.map +1 -0
  45. package/dist/hub/debug.js +1 -0
  46. package/dist/hub/index.d.ts +19 -4
  47. package/dist/hub/index.d.ts.map +1 -1
  48. package/dist/hub/index.js +1 -1
  49. package/package.json +4 -4
  50. package/src/components/AutoCoreDevPanel.tsx +14 -11
  51. package/src/components/FileList.tsx +5 -4
  52. package/src/components/FileSelect.tsx +2 -1
  53. package/src/core/ActionMode.ts +1 -1
  54. package/src/core/AutoCoreTagContext.tsx +247 -330
  55. package/src/core/AutoCoreTagTypes.ts +236 -104
  56. package/src/core/CoreStreamTypes.ts +512 -0
  57. package/src/core/EventEmitterContext.tsx +182 -520
  58. package/src/core/IndicatorButtonState.ts +1 -1
  59. package/src/core/hoc.tsx +1 -1
  60. package/src/hooks/adsHooks.tsx +21 -22
  61. package/src/hooks/commandHooks.tsx +23 -19
  62. package/src/hooks/index.ts +1 -1
  63. package/src/hooks/useAutoCoreTag.ts +2 -2
  64. package/src/hooks/useScaledValue.tsx +1 -1
  65. package/src/hub/CommandMessage.ts +71 -19
  66. package/src/hub/DebugPanel.ts +280 -0
  67. package/src/hub/HubBase.ts +147 -223
  68. package/src/hub/HubSimulate.ts +93 -24
  69. package/src/hub/HubTauri.ts +87 -96
  70. package/src/hub/HubWebSocket.ts +118 -140
  71. package/src/hub/debug.ts +211 -0
  72. package/src/hub/index.ts +49 -39
  73. package/docs/.nojekyll +0 -1
  74. package/docs/assets/hierarchy.js +0 -1
  75. package/docs/assets/highlight.css +0 -134
  76. package/docs/assets/icons.js +0 -18
  77. package/docs/assets/icons.svg +0 -1
  78. package/docs/assets/main.js +0 -60
  79. package/docs/assets/navigation.js +0 -1
  80. package/docs/assets/search.js +0 -1
  81. package/docs/assets/style.css +0 -1633
  82. package/docs/classes/components_CodeEditor.CodeEditor.html +0 -135
  83. package/docs/classes/components_Indicator.Indicator.html +0 -122
  84. package/docs/classes/components_IndicatorRect.IndicatorRect.html +0 -121
  85. package/docs/classes/components_JogPanel.JogPanel.html +0 -136
  86. package/docs/classes/components_Lamp.Lamp.html +0 -122
  87. package/docs/classes/components_OskDialog.OskDialog.html +0 -125
  88. package/docs/classes/components_TextInput.TextInput.html +0 -125
  89. package/docs/classes/components_ValueDisplay.ValueDisplay.html +0 -148
  90. package/docs/classes/components_ValueIndicator.ValueIndicator.html +0 -126
  91. package/docs/classes/core_ValueSimulator.ValueSimulator.html +0 -51
  92. package/docs/classes/hub_HubBase.HubBase.html +0 -106
  93. package/docs/classes/hub_HubSimulate.HubSimulate.html +0 -75
  94. package/docs/classes/hub_HubTauri.HubTauri.html +0 -93
  95. package/docs/classes/hub_HubWebSocket.HubWebSocket.html +0 -112
  96. package/docs/documents/core_AutoCoreTagContext.AutoCoreTagContext.html +0 -148
  97. package/docs/enums/components_JogPanel.JogDistanceAction.html +0 -5
  98. package/docs/enums/components_JogPanel.JogPanelAction.html +0 -18
  99. package/docs/enums/components_JogPanel.JogSpeedAction.html +0 -5
  100. package/docs/enums/core_ActionMode.ActionMode.html +0 -6
  101. package/docs/enums/core_IndicatorColor.IndicatorColor.html +0 -23
  102. package/docs/functions/assets.BlocklyLogo.html +0 -1
  103. package/docs/functions/assets.Distance.html +0 -1
  104. package/docs/functions/assets.JogLong.html +0 -1
  105. package/docs/functions/assets.JogMedium.html +0 -1
  106. package/docs/functions/assets.JogShort.html +0 -1
  107. package/docs/functions/assets.PythonLogo.html +0 -1
  108. package/docs/functions/assets.Rotation3D.html +0 -1
  109. package/docs/functions/assets.RotationCcw.html +0 -1
  110. package/docs/functions/assets.RotationCcwA.html +0 -1
  111. package/docs/functions/assets.RotationCcwB.html +0 -1
  112. package/docs/functions/assets.RotationCcwC.html +0 -1
  113. package/docs/functions/assets.RotationCw.html +0 -1
  114. package/docs/functions/assets.RotationCwA.html +0 -1
  115. package/docs/functions/assets.RotationCwB.html +0 -1
  116. package/docs/functions/assets.RotationCwC.html +0 -1
  117. package/docs/functions/assets.Run.html +0 -1
  118. package/docs/functions/assets.Speed.html +0 -1
  119. package/docs/functions/assets.SpeedFast.html +0 -1
  120. package/docs/functions/assets.SpeedMedium.html +0 -1
  121. package/docs/functions/assets.SpeedNone.html +0 -1
  122. package/docs/functions/assets.SpeedSlow.html +0 -1
  123. package/docs/functions/assets.Walk.html +0 -1
  124. package/docs/functions/components_BlocklyEditor.createCustomToolbox.html +0 -6
  125. package/docs/functions/core_UniqueId.UniqueId.html +0 -9
  126. package/docs/functions/core_hoc.hocAddSubscription.html +0 -6
  127. package/docs/functions/hooks_adsHooks.useAdsRegisterSymbols.html +0 -16
  128. package/docs/functions/hooks_adsHooks.useAdsTapValue.html +0 -8
  129. package/docs/functions/hooks_adsHooks.useAdsWriteScaledValue.html +0 -18
  130. package/docs/functions/hooks_adsHooks.useAdsWriteValue.html +0 -9
  131. package/docs/functions/hooks_commandHooks.useRegisterSymbols.html +0 -16
  132. package/docs/functions/hooks_commandHooks.useTapValue.html +0 -10
  133. package/docs/functions/hooks_commandHooks.useWriteScaledValue.html +0 -18
  134. package/docs/functions/hooks_commandHooks.useWriteValue.html +0 -11
  135. package/docs/functions/hooks_useAutoCoreTag.ts.makeAutoCoreTagHooks.html +0 -12
  136. package/docs/functions/hooks_useScaledValue.useScaledValue.html +0 -18
  137. package/docs/functions/hub.createHub.html +0 -3
  138. package/docs/hierarchy.html +0 -1
  139. package/docs/index.html +0 -148
  140. package/docs/interfaces/components_IndicatorButton.IndicatorButtonProps.html +0 -654
  141. package/docs/interfaces/components_IndicatorRect.IndicatorRectProps.html +0 -37
  142. package/docs/interfaces/components_JogPanel.JogPanelButtonDefinition.html +0 -5
  143. package/docs/interfaces/components_ToggleGroup.ToggleGroupProps.html +0 -644
  144. package/docs/interfaces/core_AutoCoreTagTypes.BaseContextValue.html +0 -12
  145. package/docs/interfaces/core_AutoCoreTagTypes.ScaleConfig.html +0 -13
  146. package/docs/interfaces/core_EventEmitterContext.Action.html +0 -8
  147. package/docs/interfaces/core_EventEmitterContext.EventEmitterContextType.html +0 -33
  148. package/docs/interfaces/core_EventEmitterContext.State.html +0 -8
  149. package/docs/interfaces/core_EventEmitterContext.Subscription.html +0 -6
  150. package/docs/interfaces/core_IndicatorButtonState.IndicatorButtonState.html +0 -10
  151. package/docs/interfaces/core_PositionContext.IPositionContext.html +0 -17
  152. package/docs/interfaces/hub_CommandMessage.CommandMessage.html +0 -6
  153. package/docs/interfaces/hub_CommandMessage.CommandMessageResult.html +0 -4
  154. package/docs/modules/assets.html +0 -1
  155. package/docs/modules/assets_BlocklyLogo.html +0 -1
  156. package/docs/modules/assets_Distance.html +0 -1
  157. package/docs/modules/assets_JogLong.html +0 -1
  158. package/docs/modules/assets_JogMedium.html +0 -1
  159. package/docs/modules/assets_JogShort.html +0 -1
  160. package/docs/modules/assets_PythonLogo.html +0 -1
  161. package/docs/modules/assets_Rotation3D.html +0 -1
  162. package/docs/modules/assets_RotationCcw.html +0 -1
  163. package/docs/modules/assets_RotationCcwA.html +0 -1
  164. package/docs/modules/assets_RotationCcwB.html +0 -1
  165. package/docs/modules/assets_RotationCcwC.html +0 -1
  166. package/docs/modules/assets_RotationCw.html +0 -1
  167. package/docs/modules/assets_RotationCwA.html +0 -1
  168. package/docs/modules/assets_RotationCwB.html +0 -1
  169. package/docs/modules/assets_RotationCwC.html +0 -1
  170. package/docs/modules/assets_Run.html +0 -1
  171. package/docs/modules/assets_Speed.html +0 -1
  172. package/docs/modules/assets_SpeedFast.html +0 -1
  173. package/docs/modules/assets_SpeedMedium.html +0 -1
  174. package/docs/modules/assets_SpeedNone.html +0 -1
  175. package/docs/modules/assets_SpeedSlow.html +0 -1
  176. package/docs/modules/assets_Walk.html +0 -1
  177. package/docs/modules/components_AutoCoreDevPanel.html +0 -20
  178. package/docs/modules/components_BlocklyEditor.html +0 -1
  179. package/docs/modules/components_CodeEditor.html +0 -1
  180. package/docs/modules/components_FileList.html +0 -1
  181. package/docs/modules/components_FileSelect.html +0 -1
  182. package/docs/modules/components_FitText.html +0 -1
  183. package/docs/modules/components_Indicator.html +0 -1
  184. package/docs/modules/components_IndicatorButton.html +0 -1
  185. package/docs/modules/components_IndicatorRect.html +0 -1
  186. package/docs/modules/components_JogPanel.html +0 -1
  187. package/docs/modules/components_Lamp.html +0 -1
  188. package/docs/modules/components_Osk.html +0 -1
  189. package/docs/modules/components_OskDialog.html +0 -1
  190. package/docs/modules/components_ProgressBarWithValue.html +0 -1
  191. package/docs/modules/components_TextInput.html +0 -1
  192. package/docs/modules/components_ToggleGroup.html +0 -1
  193. package/docs/modules/components_ValueDisplay.html +0 -1
  194. package/docs/modules/components_ValueIndicator.html +0 -1
  195. package/docs/modules/components_ValueInput.html +0 -1
  196. package/docs/modules/core_ActionMode.html +0 -1
  197. package/docs/modules/core_AutoCoreTagContext.html +0 -11
  198. package/docs/modules/core_AutoCoreTagTypes.html +0 -1
  199. package/docs/modules/core_EventEmitterContext.html +0 -53
  200. package/docs/modules/core_IndicatorButtonState.html +0 -1
  201. package/docs/modules/core_IndicatorColor.html +0 -1
  202. package/docs/modules/core_MaskPatterns.html +0 -1
  203. package/docs/modules/core_NumerableTypes.html +0 -1
  204. package/docs/modules/core_PositionContext.html +0 -1
  205. package/docs/modules/core_UniqueId.html +0 -1
  206. package/docs/modules/core_ValueSimulator.html +0 -1
  207. package/docs/modules/core_hoc.html +0 -1
  208. package/docs/modules/hooks.html +0 -1
  209. package/docs/modules/hooks_adsHooks.html +0 -1
  210. package/docs/modules/hooks_commandHooks.html +0 -1
  211. package/docs/modules/hooks_useAutoCoreTag.ts.html +0 -52
  212. package/docs/modules/hooks_useScaledValue.html +0 -1
  213. package/docs/modules/hub.html +0 -1
  214. package/docs/modules/hub_CommandMessage.html +0 -1
  215. package/docs/modules/hub_HubBase.html +0 -1
  216. package/docs/modules/hub_HubSimulate.html +0 -1
  217. package/docs/modules/hub_HubTauri.html +0 -1
  218. package/docs/modules/hub_HubWebSocket.html +0 -1
  219. package/docs/modules.html +0 -23
  220. package/docs/types/components_IndicatorButton.IndicatorButtonOptionsType.html +0 -1
  221. package/docs/types/core_AutoCoreTagTypes.ExtractByTag.html +0 -2
  222. package/docs/types/core_AutoCoreTagTypes.PrimitiveKind.html +0 -1
  223. package/docs/types/core_AutoCoreTagTypes.TagConfig.html +0 -16
  224. package/docs/types/core_AutoCoreTagTypes.TagValueMap.html +0 -1
  225. package/docs/types/core_AutoCoreTagTypes.TagValueOf.html +0 -1
  226. package/docs/types/core_EventEmitterContext.EmitterDispatchFunction.html +0 -3
  227. package/docs/types/core_EventEmitterContext.EmitterSubscribeFunction.html +0 -3
  228. package/docs/types/core_EventEmitterContext.EmitterUnsubscribeFunction.html +0 -3
  229. package/docs/types/core_NumerableTypes.NumerableFormatOptions.html +0 -4
  230. package/docs/types/core_hoc.HocAddSubscriptionProps.html +0 -6
  231. package/docs/variables/components_AutoCoreDevPanel.AutoCoreDevPanel.html +0 -43
  232. package/docs/variables/components_BlocklyEditor.BlocklyEditor.html +0 -13
  233. package/docs/variables/components_BlocklyEditor.StandardToolbox.html +0 -1
  234. package/docs/variables/components_FileList.FileList.html +0 -23
  235. package/docs/variables/components_FileSelect.FileSelect.html +0 -1
  236. package/docs/variables/components_FitText.FitText.html +0 -4
  237. package/docs/variables/components_IndicatorButton.IndicatorButton.html +0 -1
  238. package/docs/variables/components_JogPanel.DefaultLinearJogButtons.html +0 -2
  239. package/docs/variables/components_JogPanel.DefaultRotationJogButtons.html +0 -2
  240. package/docs/variables/components_Osk.Osk.html +0 -1
  241. package/docs/variables/components_ProgressBarWithValue.ProgressBarWithValue.html +0 -1
  242. package/docs/variables/components_ToggleGroup.ToggleGroup.html +0 -1
  243. package/docs/variables/components_ValueInput.ValueInput.html +0 -4
  244. package/docs/variables/core_AutoCoreTagContext.AutoCoreTagContext.html +0 -1
  245. package/docs/variables/core_AutoCoreTagContext.AutoCoreTagProvider.html +0 -7
  246. package/docs/variables/core_EventEmitterContext.EventEmitterContext.html +0 -64
  247. package/docs/variables/core_EventEmitterContext.EventEmitterProvider.html +0 -10
  248. package/docs/variables/core_MaskPatterns.PrimeReactMaskPatterns.html +0 -14
  249. package/docs/variables/core_MaskPatterns.RegExMaskPatterns.html +0 -15
  250. package/docs/variables/core_PositionContext.DimensionsContext.html +0 -6
  251. package/docs/variables/hooks_useScaledValue.kMillimeters2Inches.html +0 -2
  252. package/docs/variables/hooks_useScaledValue.kNewtons2Pounds.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: 2025-09-05 14:53:18
6
+ * Last Modified: 2026-01-29 09:34:21
7
7
  * Modified By: ADC
8
8
  * -----
9
- *
9
+ *
10
10
  */
11
11
 
12
- import type { InvokeArgs } from '@tauri-apps/api/tauri';
13
12
  import { HubBase } from './HubBase';
14
- import {event, invoke} from '@tauri-apps/api';
15
- import type {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-09-05 15:18:44
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 type { 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,21 +44,12 @@ 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
55
  b64Data: string,
@@ -71,7 +66,7 @@ function b64toBlob(
71
66
  for (let i = 0; i < slice.length; i++) byteNumbers[i] = slice.charCodeAt(i);
72
67
 
73
68
  const uint8 = new Uint8Array(byteNumbers);
74
- buffers.push(uint8.buffer as ArrayBuffer); // <-- pass ArrayBuffer to Blob
69
+ buffers.push(uint8.buffer as ArrayBuffer);
75
70
  }
76
71
 
77
72
  return new Blob(buffers, { type: contentType });
@@ -80,187 +75,170 @@ function b64toBlob(
80
75
 
81
76
 
82
77
  /**
83
- * 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.
84
79
  *
85
- * This function is useful for triggering downloads of binary data such as files, which are represented
86
- * by Blob objects in the browser. The function creates a URL from the Blob, sets it as the href of a
87
- * dynamically created anchor tag (`<a>`), sets the suggested filename, and then simulates a click on
88
- * this link to start the download. After the download is triggered, the link is removed and the URL
89
- * is revoked to free up memory.
90
- *
91
- * @param {Blob} blob The Blob object containing the data to be downloaded.
92
- * @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.
93
82
  */
94
83
  function downloadBlob(blob: Blob, filename: string): void {
95
- // Create a URL for the blob object
96
84
  const url = window.URL.createObjectURL(blob);
97
-
98
- // Create a temporary anchor tag (`<a>`) element
99
85
  const a = document.createElement('a');
100
- a.href = url; // Set the href to the blob URL
101
- a.download = filename; // Suggest a filename for the downloaded file
102
-
103
- // Append the anchor tag to the body of the document
86
+ a.href = url;
87
+ a.download = filename;
104
88
  document.body.appendChild(a);
105
-
106
- // Programmatically trigger a click on the anchor tag
107
89
  a.click();
108
-
109
- // Remove the anchor tag from the document
110
90
  document.body.removeChild(a);
111
-
112
- // Revoke the blob URL to free up resources
113
91
  window.URL.revokeObjectURL(url);
114
92
  }
115
93
 
116
94
 
117
-
118
-
95
+ /**
96
+ * WebSocket implementation of the Hub communication layer.
97
+ */
119
98
  export class HubWebSocket extends HubBase {
120
99
  private socket: WebSocket;
121
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
+ */
122
106
  private pendingRequests = new Map<number, RequestRecord>();
123
107
 
124
108
  /**
125
- * Constructor. Creates and attempts to make the Websocket connection.
109
+ * Initializes the WebSocket connection immediately.
126
110
  */
127
111
  constructor() {
128
112
  super();
113
+ console.log("[HubWebSocket] Constructor called");
114
+ getDebugPanel().hubCreated();
129
115
 
130
- const host = window.location.hostname; // Get the hostname from the address bar
131
-
132
- // Default to using autocore-server.
133
- let port = undefined; // window.location.port; // Get the port from the address bar, if any
116
+ const host = window.location.hostname;
117
+ const port = window.location.port;
134
118
 
135
- const proto = window.location.protocol === 'https:' ? 'wss://' : 'ws://'; // Determine the protocol
136
- const wsUrl = `${proto}${host}${port ? ':' + port : ''}/ws/`; // Construct WebSocket URL
119
+ const proto = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
120
+ const wsUrl = `${proto}${host}${port ? ':' + port : ''}/ws/`;
137
121
 
138
122
  this.socket = new WebSocket(wsUrl);
139
-
140
-
141
123
  let self = this;
142
124
 
143
- //
144
- // Websocket connection established.
145
- //
125
+ // --- Connection Handlers ---
126
+
146
127
  this.socket.onopen = function () {
147
128
  console.log("WebSocket connection established.");
129
+ getDebugPanel().wsOpened(wsUrl);
130
+ // Notify app that we are online
148
131
  self.publish("HUB/connected", true);
149
132
  self.setIsConnected(true);
150
133
  };
151
134
 
135
+ // --- Message Handler ---
152
136
 
153
- // Message recevied via the websocket connection.
154
- //
155
137
  this.socket.onmessage = (event) => {
156
138
  const data: CommandMessage = JSON.parse(event.data);
157
-
158
- // console.log(`MSG: ${JSON.stringify(data)}`);
159
-
160
- if (data.request_id && this.pendingRequests.has(data.request_id)) {
161
-
162
- const { resolve, reject } = this.pendingRequests.get(data.request_id)!;
163
-
164
- if (data.fname === "FILE_DOWNLOAD"
165
- && data.args["file_name"] !== undefined && data.args["file_name"] !== null
166
- && data.args["file_name"].length > 0
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
167
155
  ) {
168
-
169
- // The file name was supplied in the original request
170
- let tokens = data.args["file_name"].split("/");
171
-
156
+ let tokens = data.data["file_name"].split("/");
172
157
  const filename = tokens[tokens.length - 1];
173
- if (data.result && data.result.success) {
174
- const blob = b64toBlob(data.result.data);
158
+
159
+ if (data.success) {
160
+ // Decode and trigger download
161
+ const blob = b64toBlob(data.data);
175
162
  downloadBlob(blob, filename);
176
163
 
177
- // Don't send the whole file through; that's crazy.
178
- data.result.data = filename;
179
-
180
- // Signal the function that initiated the download that all is well.
181
- resolve(data.result);
182
-
164
+ // Clean up response data to avoid passing giant string to app
165
+ data.data = filename;
166
+ resolve(data);
183
167
  } else {
184
- // Signal the function that initiated the download that it failed.
185
- reject(new Error(data.result?.error_message));
168
+ reject(new Error(data.error_message));
186
169
  }
187
-
188
170
  }
171
+ // STANDARD CASE: Normal Command Response
189
172
  else {
190
-
191
- if (!data.result?.success) {
192
- // Return the result to the function that initiated the request.
193
- reject(new Error(data.result?.error_message));
173
+ if (!data.success) {
174
+ reject(new Error(data.error_message));
194
175
  } else {
195
- // Send the error to the function that intiated the request.
196
- resolve(data.result);
176
+ // Success: resolve with the full message
177
+ resolve(data);
197
178
  }
198
179
  }
199
- this.pendingRequests.delete(data.request_id);
180
+ // Cleanup request record
181
+ this.pendingRequests.delete(data.transaction_id);
200
182
  } else {
183
+ // No transaction ID means it's an unsolicited broadcast (subscription update)
184
+ console.log(`HUB BROADCAST: ${JSON.stringify(data)}`);
201
185
  this.handleUnsolicitedMessage(data);
202
186
  }
203
187
  };
204
188
 
205
- //
206
- // Error occurred in the Websocket connection.
207
- //
208
189
  this.socket.onerror = (error: Event) => {
209
190
  console.error('WebSocket error:', error);
191
+ getDebugPanel().wsError(String(error));
210
192
  };
211
193
 
212
- //
213
- // The Websocket connection has closed.
214
- //
215
- this.socket.onclose = () => {
194
+ this.socket.onclose = (event: CloseEvent) => {
216
195
  self.setIsConnected(false);
217
196
  console.log('WebSocket connection closed.');
197
+ getDebugPanel().wsClosed(event.code, event.reason || 'No reason');
218
198
  };
219
199
  }
220
200
 
221
201
 
222
202
  /**
223
- * Invoke a command in the remote webserver.
224
- * @param domain The domain of the Servelet supplying the functionality.
225
- * @param fname The name of the command to execute.
226
- * @param payload The arguments of the command.
227
- * @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`.
228
207
  */
229
- invoke = (domain: string, fname: string, payload?: object): Promise<CommandMessageResult> => {
208
+ invoke = (topic: string, messageType: MessageType, payload?: object): Promise<CommandMessage> => {
230
209
 
231
210
  return new Promise((resolve, reject) => {
232
- const id = ++this.requestId; // Increment and use the request ID
211
+ const id = ++this.requestId; // Generate unique ID
233
212
  this.pendingRequests.set(id, { resolve, reject });
234
213
 
235
- let cm: CommandMessage = {
236
- request_id: id,
237
- domain: domain,
238
- fname: fname,
239
- args: payload,
240
- 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: ''
241
224
  };
242
225
 
226
+ getDebugPanel().messageSent(MessageType[messageType], topic, id);
243
227
  this.socket.send(JSON.stringify(cm));
244
228
  });
245
229
  }
246
230
 
231
+ /**
232
+ * Processes messages that are not responses to a specific request.
233
+ * Usually these are subscription updates (Broadcasts).
234
+ */
247
235
  handleUnsolicitedMessage = (msg: CommandMessage) => {
248
- // Handle messages that do not correspond to a pending request
249
-
250
- if (msg.fname === "BROADCAST"
251
- && msg.domain.length >= 1
252
- && msg.result !== undefined && msg.result !== null
253
- && msg.result.data !== undefined && msg.result.data !== null
254
- ) {
255
-
256
- let topic = `${msg.domain}/${msg.result.data["topic"]}`;
257
-
258
- //console.log(`HUB PUBLISHING: ${topic} : ${JSON.stringify(msg.result.data)}`);
259
- 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);
260
241
  }
261
- // else {
262
- // console.error("INVALID BROADCAST RECEIVED!");
263
- // }
264
242
  }
265
243
 
266
244
  disconnect = (): void => {
@@ -270,4 +248,4 @@ export class HubWebSocket extends HubBase {
270
248
  protected emit = (eventName: string, payload?: object): void => {
271
249
  this.socket.send(JSON.stringify({ eventName, payload }));
272
250
  }
273
- }
251
+ }