@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
@@ -2,45 +2,84 @@
2
2
  * Copyright (C) 2024 Automated Design Corp. All Rights Reserved.
3
3
  * Created Date: 2024-01-17 11:45:10
4
4
  * -----
5
- * Last Modified: 2025-08-30 06:54:17
5
+ * Last Modified: 2026-01-29 09:32:29
6
6
  * Modified By: ADC
7
7
  * -----
8
8
  *
9
9
  */
10
10
 
11
+ /**
12
+ * @fileoverview EventEmitterContext - Global Event Bus for AutoCore React Applications
13
+ *
14
+ * The EventEmitterContext provides a comprehensive event-driven communication system for React applications,
15
+ * enabling seamless interaction between components and backend services. It serves as the foundational layer
16
+ * for AutoCore's real-time data flow and component communication architecture.
17
+ *
18
+ * ## Core Features
19
+ *
20
+ * - **Global Event Bus**: Publish and subscribe to events across all components.
21
+ * - **Backend Integration**: Direct communication with AutoCore server via Hub abstraction.
22
+ * - **Type-Safe Subscriptions**: Strongly-typed event handling with automatic cleanup.
23
+ * - **Performance Optimized**: Uses stable context values and direct callbacks to avoid global re-renders.
24
+ * - **Connection Management**: Automatic reconnection and state synchronization.
25
+ *
26
+ * ## Architecture
27
+ *
28
+ * The system consists of three main components:
29
+ * 1. **EventEmitterProvider**: React context provider that manages global state and event routing.
30
+ * 2. **Hub**: Abstraction layer for backend communication (WebSocket, HTTP, etc.).
31
+ * 3. **Subscription Manager**: Handles event routing and lifecycle management.
32
+ *
33
+ * ## Performance Notes
34
+ *
35
+ * To ensure high performance with high-frequency data (e.g., 50Hz sensor updates):
36
+ * 1. **No State Updates on Dispatch**: The `dispatch` function does NOT update React state. It calls listeners directly.
37
+ * This prevents the entire component tree from re-rendering on every message.
38
+ * 2. **Stable Context Value**: The `contextValue` object is memoized and rarely changes.
39
+ * 3. **Direct Subscriptions**: Components subscribe via `useEffect` and receive updates via callbacks, not props.
40
+ *
41
+ * @module core/EventEmitterContext
42
+ * @version 3.0.42
43
+ * @author Automated Design Corp.
44
+ */
45
+
11
46
  import React, {
12
47
  createContext,
13
- ReactNode,
14
48
  useState,
15
49
  useMemo,
16
50
  useCallback,
17
51
  useRef,
18
52
  useEffect,
19
53
  } from "react";
54
+
55
+ import type {ReactNode} from "react";
20
56
  import { createHub, Hub } from "../hub";
21
- import { CommandMessageResult } from "../hub/CommandMessage";
57
+ import type { CommandMessage } from "../hub";
58
+ import { MessageType } from "../hub/CommandMessage";
22
59
 
23
60
  export { Hub };
24
61
 
25
62
  /**
26
- * Represents an event subsription.
63
+ * Represents an active event subscription.
27
64
  */
28
65
  export interface Subscription {
29
- /** ID of the subscription used for unsubscription. */
66
+ /** Unique ID of the subscription used for unsubscription. */
30
67
  id: number;
31
- /** Callback function. */
68
+ /** Callback function to execute when the event fires. */
32
69
  callback: React.Dispatch<any>;
33
70
  }
34
71
 
35
72
  /**
36
- * Represents the payload data associated with an event.
73
+ * Represents the internal state of the EventEmitter.
74
+ * Mostly used for debugging and dev tools inspection.
37
75
  */
38
76
  export interface State {
39
77
  /**
40
- * The optional data payload of the event.
78
+ * The optional data payload of the last event (DEBUGGING ONLY).
79
+ * Note: This is no longer updated in production for performance reasons.
41
80
  */
42
81
  eventData?: any;
43
- /** Callback subscription. A list of callback subscriptions matched to a topic. */
82
+ /** Active subscriptions grouped by topic. */
44
83
  subscriptions: Record<string, Subscription[]>;
45
84
 
46
85
  /** Tracks the next subscription ID that will be assigned. */
@@ -48,261 +87,152 @@ export interface State {
48
87
  }
49
88
 
50
89
  /**
51
- * An action event published by the EventEmitterContext. Contains
52
- * the topic identifying the event and the optional payload, which is
53
- * a value of any type.
90
+ * An action event published by the EventEmitterContext.
54
91
  */
55
92
  export interface Action {
56
- /**
57
- * The topic or identifier of the event.
58
- */
93
+ /** The topic or identifier of the event. */
59
94
  topic: string;
60
-
61
- /**
62
- * The optional data payload associated with the event.
63
- */
95
+ /** The optional data payload associated with the event. */
64
96
  payload?: any;
65
97
  }
66
98
 
67
- /**
68
- * Type declaration for the EventEmitter dispatch function, which
69
- * publishes an Action globally throughout the EventEmitterContext.
70
- */
71
99
  export type EmitterDispatchFunction = (action: Action) => void;
72
100
 
73
101
  /**
74
- * Type declaration for the EventEmitter dispatch function, which
75
- * receives an Action on a specific topic broadcast through the EventEmitterContext.
76
- */
77
- export type EmitterSubscribeFunction = (action: Action) => void;
78
-
79
- /**
80
- * Type declaration for the EventEmitter unsubscribe function, which
81
- * receives an Action on a specific topic broadcast through the EventEmitterContext.
82
- */
83
- export type EmitterUnsubscribeFunction = (action: Action) => void;
84
-
85
- /**
86
- * Defines the context for an event emitter used throughout a front-end application to manage and dispatch events.
87
- * This interface includes methods for managing the application's state, handling global actions, communicating with the back end,
88
- * subscribing to and unsubscribing from events, and accessing a global hub for event publishing and subscription management.
89
- * It serves as a central point for event-driven interactions within the application, facilitating communication between
90
- * components and the back end, as well as among components themselves.
102
+ * Defines the context for the global event emitter.
103
+ * Provides methods for dispatching events, subscribing to topics, and communicating with the backend.
91
104
  */
92
105
  export interface EventEmitterContextType {
93
106
  /**
94
- * The current state of the event emitter, containing the latest event data.
107
+ * The current state of the event emitter (mostly for debugging).
95
108
  */
96
109
  state: State;
97
110
 
98
111
  /**
99
- * A function to dispatch actions globally throughout the front-end,
100
- * triggering state updates and events.
101
- *
102
- * @param action The action to dispatch, containing topic and optional payload.
112
+ * Dispatch an action globally throughout the front-end.
113
+ * Triggers callbacks for all subscribers of the topic.
114
+ *
115
+ * @param action The action to dispatch.
103
116
  */
104
- dispatch: (action: Action) => void;
117
+ dispatch: EmitterDispatchFunction;
105
118
 
106
119
  /**
107
- * Invoke/send a message to the back end.
108
- * This does NOT get published to the front end.
120
+ * Send a message to the backend (Low-level).
121
+ *
122
+ * @param topic The FQDN of the resource (e.g., "ads.plc1.gio.bControlPowerOk")
123
+ * @param messageType The action to perform (Read, Write, Subscribe, etc.)
124
+ * @param payload Optional data payload
125
+ * @returns Promise resolving to the CommandMessage response
109
126
  */
110
127
  invoke(
111
- domain: string,
112
- fname: string,
128
+ topic: string,
129
+ messageType: MessageType,
113
130
  payload?: object
114
- ): Promise<CommandMessageResult>;
131
+ ): Promise<CommandMessage>;
115
132
 
116
133
  /**
117
- * Subscribe to events identified by the topic.
118
- * @param topic The subscription topic.
134
+ * Read a value from the backend.
135
+ *
136
+ * @param topic The FQDN of the resource to read
137
+ * @param payload Optional additional parameters
138
+ * @returns Promise resolving to the CommandMessage response with the value in `data`
139
+ */
140
+ read(topic: string, payload?: object): Promise<CommandMessage>;
141
+
142
+ /**
143
+ * Write a value to the backend.
144
+ *
145
+ * @param topic The FQDN of the resource to write
146
+ * @param value The value to write
147
+ * @returns Promise resolving to the CommandMessage response
148
+ */
149
+ write(topic: string, value: any): Promise<CommandMessage>;
150
+
151
+ /**
152
+ * Subscribe to value changes on the backend.
153
+ *
154
+ * Tells the server to start broadcasting updates for a specific topic.
155
+ * Also registers a mapping between the local `tagName` and the remote `topic` (FQDN)
156
+ * in the Hub, so that incoming broadcasts are routed correctly.
157
+ *
158
+ * @param tagName The local tag name to map the topic to
159
+ * @param topic The FQDN of the resource to watch
160
+ * @param payload Optional subscription parameters (e.g., update rate)
161
+ * @returns Promise resolving to the CommandMessage response
162
+ */
163
+ serverSubscribe(tagName: string, topic: string, payload?: object): Promise<CommandMessage>;
164
+
165
+ /**
166
+ * Unsubscribe from value changes on the backend.
167
+ *
168
+ * @param tagName The local tag name to stop watching
169
+ * @param payload Optional parameters
170
+ * @returns Promise resolving to the CommandMessage response
171
+ */
172
+ serverUnsubscribe(tagName: string, payload?: object): Promise<CommandMessage>;
173
+
174
+ /**
175
+ * Subscribe to **local** frontend events.
176
+ *
177
+ * This registers a callback to listen for events dispatched on the local event bus.
178
+ * This includes both locally dispatched events AND broadcast messages received
179
+ * from the server (which the Hub dispatches to the local bus).
180
+ *
181
+ * @param topic The subscription topic (or tagName).
119
182
  * @param callback The callback to signal.
120
183
  * @returns number Subscription ID used to unsubscribe later.
121
184
  */
122
185
  subscribe: (topic: string, callback: React.Dispatch<any>) => number;
123
186
 
124
187
  /**
125
- * Unsubscribe to events.
126
- * @param subscriptionId The id of the subscription returned by the subscribe method.
127
- * @returns
188
+ * Unsubscribe from **local** frontend events.
189
+ *
190
+ * @param subscriptionId The id returned by the subscribe method.
128
191
  */
129
192
  unsubscribe: (subscriptionId: number) => void;
130
193
 
131
194
  /**
132
- * Global hub for publishing and receiving events throughout the interface, and for exchanging
133
- * data with the backend.
195
+ * Global hub instance for backend communication.
134
196
  */
135
197
  hub: Hub | null;
136
198
 
137
199
  /**
138
200
  * Retrieves the current subscriptions. Used for debugging purposes.
139
- * @param topic Optional. The topic to retrieve subscriptions for. If omitted, returns all subscriptions.
140
- * @returns An object containing the current subscriptions, optionally filtered by topic.
141
201
  */
142
202
  getSubscriptions: (
143
203
  topic?: string
144
204
  ) => Record<string, Subscription[]> | Subscription[];
145
205
 
146
206
  /**
147
- * Returns true if the Hub in use is connected to its source.
148
- * @returns boolean
207
+ * Returns true if the Hub is connected to the backend.
149
208
  */
150
209
  isConnected: () => boolean;
151
210
  }
152
211
 
153
- let globalSubscriptionId = 1;
212
+ // Default placeholder CommandMessage for context initialization
213
+ const placeholderResponse: CommandMessage = {
214
+ data: {},
215
+ success: false,
216
+ error_message: "Context not initialized",
217
+ transaction_id: 0,
218
+ timecode: 0,
219
+ topic: "",
220
+ message_type: MessageType.NoOp
221
+ };
154
222
 
155
- /**
156
- * A global context for managing event emission and subscription.
157
- *
158
- * Creates a React context for the EventEmitter system, providing a structured way to manage events and data flow
159
- * in a React application. It serves as a global event bus that components can subscribe to or emit events, allowing for
160
- * a loosely coupled architecture. Additionally, it provides a mechanism for invoking backend functions and managing
161
- * subscriptions, making it easier to integrate React components with backend services.
162
- *
163
- * The context includes several key functionalities:
164
- * - `state`: Maintains the current state of subscriptions and their identifiers.
165
- * - `dispatch`: Allows components to emit events with specific topics and payloads, which can be listened to by other components.
166
- * - `subscribe`: Enables components to listen to specific topics and react to those events by providing a callback function.
167
- * - `unsubscribe`: Provides a way for components to stop listening to events, helping prevent memory leaks and unnecessary updates.
168
- * - `invoke`: Facilitates calling backend functions with specific arguments and handling their responses asynchronously.
169
- * - `getSubscriptions`: Offers insight into current active subscriptions, useful for debugging purposes.
170
- *
171
- * This context is essential for applications that require a high degree of inter-component communication or need to
172
- * interact with a backend efficiently.
173
- *
174
- * For more information, see [Additional Documentation](../additional-docs/GlobalEventEmitter.md).
175
- *
176
- * ## Usage
177
- *
178
- * The entire application should be wrapped in the EventEmitterProvider.
179
- *
180
- * App.tsx
181
- * ```
182
- * import { EventEmitterProvider } from "@adcops/autocore-react/core/EventEmitterContext.js";
183
- * function App() {
184
- *
185
- * return(
186
- * <EventEmitterProvider>
187
- * <PrimeReactProvider>
188
- * <main>
189
- * <section>
190
- * <ContentView />
191
- * </section>
192
- * </main>
193
- * </PrimeReactProvider>
194
- * </EventEmitterProvider>
195
- * );
196
- *
197
- * }
198
- *
199
- * ```
200
- *
201
- * ### Catching and receiving events
202
- * The EventEmitterContext creates an appropriate instance of the hub, which is derived from HubBase.
203
- * That hub can be used to publish and subscribe to
204
- * topics globally in the front-end, regardless of being connected to any backend.
205
- * Usage within a component is simple.
206
- *
207
- * ```
208
- * const {dispatch, subscribe, unsubscribe} = useContext(EventEmitterContext);
209
- * const [controlPower, setControlPower] = useState(false);
210
- * useEffect(() => {
211
- * const unsubscribeControlPower = subscribe('value-simulator-bBit1', (value) => {
212
- * setControlPower(value);
213
- * });
214
- *
215
- *
216
- * return () => {
217
- * unsubscribe(unsubscribeControlPower);
218
- * }
219
- * }, [] );
220
- *
221
- * const onPbPressed = () => {
222
- * let count = 1;
223
- * dispatch({
224
- * topic: "my-awesome-topic",
225
- * payload: count
226
- * });
227
- * }
228
- *
229
- * ```
230
- * The hub should also be used for invoking events in the backend.
231
- * This example will call the function "update_count" in the backend, passing
232
- * the expected argument "count". Details of the interaction between the Hub and
233
- * the backend will be handled by the appropriate HubBase sub-class, and should
234
- * be transparent to the front end.
235
- *
236
- * ```
237
- * const {invoke} = useContext(EventEmitterContext);
238
- * const incrementCount = () => {
239
- * count += 1;
240
- * invoke('update_count', {"count": count});
241
- * };
242
- *
243
- * Subscribing to a topic is simple. The type of value received is specific
244
- * to the topic.
245
- *
246
- * Example: Listen to an event 'xarm-position':
247
- * ```
248
- * const {subscribe, unsubscribe} = useContext(EventEmitterContext);
249
- * useEffect(() => {
250
- * const unsubscripeMp = subscribe('xarm-position', (value) => {
251
- * // The publisher sent a JSON object of 3D position values.
252
- * setX(value.x);
253
- * setY(value.y);
254
- * setZ(value.z);
255
- * setA(value.roll);
256
- * setB(value.yaw);
257
- * setC(value.pitch);
258
- * });
259
- *
260
- * return () => {
261
- * unsubscribe(unsubscripeMp);
262
- * }
263
- *
264
- * }, [] );
265
- *
266
- * ```
267
- *
268
- * For applications that need to access the instance of the hub, get the current instance
269
- * from the EventEmitterContext:
270
- *
271
- * ```
272
- * const {hub} = useContext(EventEmitterContext);
273
- * * ```
274
- *
275
- *
276
- */
277
223
  export const EventEmitterContext = createContext<EventEmitterContextType>({
278
224
  state: { subscriptions: {}, nextSubscriptionId: 1 },
279
225
  dispatch: () => {},
280
- subscribe: () => {
281
- return 0;
282
- }, // Placeholder for subscription logic
283
- invoke: async (domain: string, fname: string, payload?: object) => {
284
- domain;
285
- fname;
286
- payload;
287
- let ret: CommandMessageResult = {
288
- data: {},
289
- success: false,
290
- error_message: "",
291
- };
292
- // Placeholder for invoke logic
293
- // Implement the logic to send a message to the backend and return a promise
294
- return Promise.resolve(ret); // Example placeholder, replace with actual implementation
295
- },
296
- unsubscribe: (subscriptionId: number) => {
297
- subscriptionId;
298
- }, // Placeholder for unsubscription logic
226
+ subscribe: () => 0,
227
+ unsubscribe: () => {},
228
+ invoke: async () => placeholderResponse,
229
+ read: async () => placeholderResponse,
230
+ write: async () => placeholderResponse,
231
+ serverSubscribe: async (_tagName: string, _topic: string, _payload?: object) => placeholderResponse,
232
+ serverUnsubscribe: async (_tagName: string, _payload?: object) => placeholderResponse,
299
233
  hub: null,
300
- getSubscriptions: () => {
301
- return [];
302
- },
303
- isConnected: () => {
304
- return false;
305
- },
234
+ getSubscriptions: () => [],
235
+ isConnected: () => false,
306
236
  });
307
237
 
308
238
  /**
@@ -312,56 +242,53 @@ export const EventEmitterContext = createContext<EventEmitterContextType>({
312
242
  * with the event emitter.
313
243
  *
314
244
  * @param children The child components to be wrapped in the context.
315
- *
316
- * ## Usage
317
- *
318
- * The entire application should be wrapped in the EventEmitterProvider.
319
- *
320
- * App.tsx
321
- * ```
322
- * import { EventEmitterProvider } from "@adcops/autocore-react/core/EventEmitterContext.js";
323
- * function App() {
324
- *
325
- * return(
326
- * <EventEmitterProvider>
327
- * <PrimeReactProvider>
328
- * <main>
329
- * <section>
330
- * <ContentView />
331
- * </section>
332
- * </main>
333
- * </PrimeReactProvider>
334
- * </EventEmitterProvider>
335
- * );
336
- *
337
- * }
338
- *
339
- * ```
340
245
  */
341
246
  export const EventEmitterProvider: React.FC<{ children: ReactNode }> = ({
342
247
  children,
343
248
  }) => {
249
+ console.log("[EventEmitterProvider] Rendering...");
250
+
344
251
  const [state, setState] = useState<State>({
345
252
  subscriptions: {},
346
253
  nextSubscriptionId: 1,
347
254
  });
348
255
 
349
- // Memoize the hub instance so it's only created once
350
- const hub = useMemo(() => createHub(), []);
256
+ // PERFORMANCE: Memoize the hub instance so it's only created once.
257
+ // This ensures the WebSocket connection persists across re-renders.
258
+ const hub = useMemo(() => {
259
+ console.log("[EventEmitterProvider] Creating Hub instance");
260
+ return createHub();
261
+ }, []);
262
+
263
+ // Use ref for subscription ID management (avoids global state issues)
264
+ const nextIdRef = useRef(1);
351
265
 
352
- // <-- New: the source of truth for subscriptions lives in a ref
266
+ // Subscription storage - this is the single source of truth for local listeners
353
267
  const subsRef = useRef<Record<string, Subscription[]>>({});
354
268
 
355
- // Keep the ref in sync for debugging / external reads
269
+ // Sync state with ref for debugging/inspection purposes only (on mount)
356
270
  useEffect(() => {
357
- subsRef.current = state.subscriptions;
358
- }, [state.subscriptions]);
271
+ setState(prev => ({
272
+ ...prev,
273
+ subscriptions: { ...subsRef.current },
274
+ nextSubscriptionId: nextIdRef.current
275
+ }));
276
+ }, []);
359
277
 
278
+ /**
279
+ * Dispatches an event to all listeners.
280
+ *
281
+ * PERFORMANCE CRITICAL: This function iterates through listeners and calls them directly.
282
+ * It intentionally does NOT update React state (setState) to avoid triggering a
283
+ * full app re-render on every high-frequency event.
284
+ */
360
285
  const dispatch = useCallback((action: Action) => {
361
286
  const { topic, payload } = action;
362
287
 
363
- // Read once, outside setState, so it can't be double-invoked by StrictMode
288
+ // Read from ref (single source of truth) and create a defensive copy
364
289
  const listeners = subsRef.current[topic]?.slice() ?? [];
290
+
291
+ // Execute callbacks synchronously to avoid timing issues
365
292
  for (const sub of listeners) {
366
293
  try {
367
294
  sub.callback(payload);
@@ -370,24 +297,32 @@ export const EventEmitterProvider: React.FC<{ children: ReactNode }> = ({
370
297
  }
371
298
  }
372
299
 
373
- // Optional: keep last payload in state (pure)
374
- setState((prev) => ({ ...prev, eventData: payload }));
300
+ // NOTE: State update removed for performance.
301
+ // Uncommenting this will cause the entire Provider to re-render on every event!
302
+ // setState((prev) => ({ ...prev, eventData: payload }));
375
303
  }, []);
376
304
 
377
305
  const subscribe = useCallback(
378
306
  (topic: string, callback: React.Dispatch<any>): number => {
379
- const id = ++globalSubscriptionId;
380
-
381
- // Mutate ref
382
- const prev = subsRef.current[topic] ?? [];
383
- const next = [...prev, { id, callback }];
384
- subsRef.current[topic] = next;
385
-
386
- // Reflect in state (for debugging/inspection)
387
- setState((prevState) => ({
307
+ const id = nextIdRef.current++;
308
+
309
+ // Create new subscription entry
310
+ const newSub: Subscription = { id, callback };
311
+
312
+ // Atomically update the ref with a complete new structure
313
+ const currentSubs = subsRef.current[topic] ?? [];
314
+ const newSubs = [...currentSubs, newSub];
315
+
316
+ subsRef.current = {
317
+ ...subsRef.current,
318
+ [topic]: newSubs
319
+ };
320
+
321
+ // Update state for debugging/inspection (less frequent than dispatch, so acceptable)
322
+ setState(prevState => ({
388
323
  ...prevState,
389
- subscriptions: { ...prevState.subscriptions, [topic]: next },
390
- nextSubscriptionId: globalSubscriptionId + 1,
324
+ subscriptions: { ...subsRef.current },
325
+ nextSubscriptionId: nextIdRef.current,
391
326
  }));
392
327
 
393
328
  return id;
@@ -396,57 +331,98 @@ export const EventEmitterProvider: React.FC<{ children: ReactNode }> = ({
396
331
  );
397
332
 
398
333
  const unsubscribe = useCallback((subscriptionId: number) => {
399
- const map = subsRef.current;
400
- for (const t of Object.keys(map)) {
401
- const next = map[t].filter((s) => s.id !== subscriptionId);
402
- if (next.length) map[t] = next;
403
- else delete map[t];
334
+ // Create a completely new structure to avoid mutation issues
335
+ const newSubscriptions: Record<string, Subscription[]> = {};
336
+
337
+ for (const [topic, subs] of Object.entries(subsRef.current)) {
338
+ const filteredSubs = subs.filter(s => s.id !== subscriptionId);
339
+ if (filteredSubs.length > 0) {
340
+ newSubscriptions[topic] = filteredSubs;
341
+ }
404
342
  }
343
+
344
+ // Atomically replace the entire structure
345
+ subsRef.current = newSubscriptions;
346
+
347
+ // Update state for debugging/inspection
348
+ setState(prev => ({
349
+ ...prev,
350
+ subscriptions: { ...newSubscriptions }
351
+ }));
352
+ }, []);
405
353
 
406
- setState((prev) => ({ ...prev, subscriptions: { ...map } }));
354
+ // Clean up on unmount
355
+ useEffect(() => {
356
+ return () => {
357
+ subsRef.current = {};
358
+ nextIdRef.current = 1;
359
+ };
407
360
  }, []);
408
361
 
409
- // (Nice-to-have) Handle HMR so listeners dont leak across hot updates
362
+ // Handle HMR so listeners don't leak across hot updates
410
363
  if (import.meta && (import.meta as any).hot) {
411
364
  (import.meta as any).hot.dispose(() => {
412
365
  subsRef.current = {};
366
+ nextIdRef.current = 1;
413
367
  });
414
368
  }
415
369
 
416
- // const getSubscriptions = useCallback(
417
- // (topic?: string): Record<string, Subscription[]> | Subscription[] => {
418
- // if (topic) {
419
- // // Return subscriptions for the provided topic, or an empty array if the topic doesn't exist
420
- // return state.subscriptions[topic] || [];
421
- // } else {
422
- // // Return all subscriptions
423
- // return state.subscriptions;
424
- // }
425
- // },
426
- // []
427
- // );
428
-
429
- // Provide the memoized hub instance in the context value
370
+ // Memoize hub methods separately so they don't change when state changes.
371
+ // This prevents downstream useEffects from re-running due to function reference changes.
372
+ const invoke = useCallback(
373
+ (topic: string, messageType: MessageType, payload?: object) => hub.invoke(topic, messageType, payload),
374
+ [hub]
375
+ );
376
+ const read = useCallback(
377
+ (topic: string, payload?: object) => hub.read(topic, payload),
378
+ [hub]
379
+ );
380
+ const write = useCallback(
381
+ (topic: string, value: any) => hub.write(topic, value),
382
+ [hub]
383
+ );
384
+ const serverSubscribe = useCallback(
385
+ (tagName: string, topic: string, payload?: object) => hub.subscribe(tagName, topic, payload),
386
+ [hub]
387
+ );
388
+ const serverUnsubscribe = useCallback(
389
+ (tagName: string, payload?: object) => hub.unsubscribe(tagName, payload),
390
+ [hub]
391
+ );
392
+ const isConnected = useCallback(() => hub.isConnected(), [hub]);
393
+
394
+ // Provide the memoized context value
395
+ // PERFORMANCE: This object is memoized and its dependencies (dispatch, subscribe, etc.)
396
+ // are also memoized/stable. This means 'contextValue' reference rarely changes,
397
+ // preventing consumers (like App) from re-rendering unless necessary.
430
398
  const contextValue = useMemo(
431
399
  () => ({
432
400
  state,
433
401
  dispatch,
434
402
  subscribe,
435
403
  unsubscribe,
436
- invoke: hub.invoke,
404
+ invoke,
405
+ read,
406
+ write,
407
+ serverSubscribe,
408
+ serverUnsubscribe,
437
409
  hub,
438
410
  getSubscriptions: (topic?: string) =>
439
411
  topic ? subsRef.current[topic] ?? [] : subsRef.current,
440
- isConnected: hub.isConnected,
412
+ isConnected,
441
413
  }),
442
- [state, hub, dispatch, subscribe, unsubscribe]
414
+ [state, hub, dispatch, subscribe, unsubscribe, invoke, read, write, serverSubscribe, serverUnsubscribe, isConnected]
443
415
  );
444
416
 
445
- hub.setContext(contextValue);
417
+ // Set the context on the hub instance so it can dispatch back to us.
418
+ // Must be in useEffect to avoid side-effects during render phase.
419
+ useEffect(() => {
420
+ hub.setContext(contextValue);
421
+ }, [hub, contextValue]);
446
422
 
447
423
  return (
448
424
  <EventEmitterContext.Provider value={contextValue}>
449
425
  {children}
450
426
  </EventEmitterContext.Provider>
451
427
  );
452
- };
428
+ };