@adcops/autocore-react 3.3.9 → 3.3.10
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.
- package/LICENSE +58 -58
- package/additional-docs/AutoCoreTagContext.md +441 -441
- package/additional-docs/ButtonApiSpecs.md +48 -48
- package/additional-docs/GlobalEventEmitter.md +243 -243
- package/additional-docs/general_recommendations.md +22 -22
- package/additional-docs/react_performance_notes.md +94 -94
- package/dist/assets/svg/blockly_logo.svg +82 -82
- package/dist/assets/svg/distance.svg +40 -40
- package/dist/assets/svg/python_logo.svg +246 -246
- package/dist/assets/svg/rotation_ccw.svg +50 -50
- package/dist/assets/svg/rotation_ccw_a.svg +57 -57
- package/dist/assets/svg/rotation_ccw_b.svg +57 -57
- package/dist/assets/svg/rotation_ccw_c.svg +57 -57
- package/dist/assets/svg/rotation_cw.svg +49 -49
- package/dist/assets/svg/rotation_cw_a.svg +30 -30
- package/dist/assets/svg/rotation_cw_b.svg +30 -30
- package/dist/assets/svg/rotation_cw_c.svg +30 -30
- package/dist/assets/svg/speed.svg +39 -39
- package/dist/components/BlocklyEditor.css +93 -93
- package/dist/components/JogPanel.css +41 -41
- package/dist/components/ProgressBarWithValue.css +27 -27
- package/dist/components/ValueIndicator.css +31 -31
- package/dist/components/osk.css +123 -123
- package/dist/core/AutoCoreTagContext.d.ts.map +1 -1
- package/dist/core/AutoCoreTagContext.js +1 -1
- package/dist/hub/HubBase.d.ts +3 -3
- package/dist/hub/HubBase.d.ts.map +1 -1
- package/dist/hub/HubBase.js +1 -1
- package/package.json +104 -104
- package/readme.md +343 -343
- package/src/assets/BlocklyLogo.tsx +27 -27
- package/src/assets/Distance.tsx +18 -18
- package/src/assets/JogLong.tsx +13 -13
- package/src/assets/JogMedium.tsx +13 -13
- package/src/assets/JogShort.tsx +13 -13
- package/src/assets/PythonLogo.tsx +83 -83
- package/src/assets/Rotation3D.tsx +13 -13
- package/src/assets/RotationCcw.tsx +33 -33
- package/src/assets/RotationCcwA.tsx +45 -45
- package/src/assets/RotationCcwB.tsx +45 -45
- package/src/assets/RotationCcwC.tsx +45 -45
- package/src/assets/RotationCw.tsx +31 -31
- package/src/assets/RotationCwA.tsx +42 -42
- package/src/assets/RotationCwB.tsx +42 -42
- package/src/assets/RotationCwC.tsx +42 -42
- package/src/assets/Run.tsx +13 -13
- package/src/assets/Speed.tsx +18 -18
- package/src/assets/SpeedFast.tsx +13 -13
- package/src/assets/SpeedMedium.tsx +13 -13
- package/src/assets/SpeedNone.tsx +13 -13
- package/src/assets/SpeedSlow.tsx +13 -13
- package/src/assets/Walk.tsx +13 -13
- package/src/assets/index.ts +22 -22
- package/src/assets/svg/blockly_logo.svg +82 -82
- package/src/assets/svg/distance.svg +40 -40
- package/src/assets/svg/python_logo.svg +246 -246
- package/src/assets/svg/rotation_ccw.svg +50 -50
- package/src/assets/svg/rotation_ccw_a.svg +57 -57
- package/src/assets/svg/rotation_ccw_b.svg +57 -57
- package/src/assets/svg/rotation_ccw_c.svg +57 -57
- package/src/assets/svg/rotation_cw.svg +49 -49
- package/src/assets/svg/rotation_cw_a.svg +30 -30
- package/src/assets/svg/rotation_cw_b.svg +30 -30
- package/src/assets/svg/rotation_cw_c.svg +30 -30
- package/src/assets/svg/speed.svg +39 -39
- package/src/components/AutoCoreDevPanel.tsx +414 -414
- package/src/components/BlocklyEditor.css +93 -93
- package/src/components/BlocklyEditor.tsx +609 -609
- package/src/components/CodeEditor.tsx +155 -155
- package/src/components/FileList.tsx +390 -390
- package/src/components/FileSelect.tsx +128 -128
- package/src/components/FitText.tsx +35 -35
- package/src/components/Indicator.tsx +188 -188
- package/src/components/IndicatorButton.tsx +214 -214
- package/src/components/IndicatorRect.tsx +172 -172
- package/src/components/JogPanel.css +41 -41
- package/src/components/JogPanel.tsx +461 -461
- package/src/components/Lamp.tsx +243 -243
- package/src/components/Osk.tsx +192 -192
- package/src/components/OskDialog.tsx +164 -164
- package/src/components/ProgressBarWithValue.css +27 -27
- package/src/components/ProgressBarWithValue.tsx +48 -48
- package/src/components/TextInput.tsx +195 -195
- package/src/components/ToggleGroup.tsx +322 -322
- package/src/components/ValueDisplay.tsx +236 -236
- package/src/components/ValueIndicator.css +31 -31
- package/src/components/ValueIndicator.tsx +135 -135
- package/src/components/ValueInput.tsx +368 -368
- package/src/components/osk.css +123 -123
- package/src/core/ActionMode.ts +19 -19
- package/src/core/AutoCoreTagContext.tsx +625 -614
- package/src/core/AutoCoreTagTypes.ts +334 -334
- package/src/core/CoreStreamTypes.ts +512 -512
- package/src/core/EventEmitterContext.tsx +434 -434
- package/src/core/IndicatorButtonState.ts +34 -34
- package/src/core/IndicatorColor.ts +35 -35
- package/src/core/MaskPatterns.ts +87 -87
- package/src/core/NumerableTypes.ts +80 -80
- package/src/core/PositionContext.ts +59 -59
- package/src/core/UniqueId.ts +41 -41
- package/src/core/ValueSimulator.ts +166 -166
- package/src/core/hoc.tsx +65 -65
- package/src/hooks/adsHooks.tsx +287 -287
- package/src/hooks/commandHooks.tsx +300 -300
- package/src/hooks/index.ts +12 -12
- package/src/hooks/useAutoCoreTag.ts +103 -103
- package/src/hooks/useScaledValue.tsx +99 -99
- package/src/hub/CommandMessage.ts +89 -89
- package/src/hub/DebugPanel.ts +307 -307
- package/src/hub/HubBase.ts +249 -236
- package/src/hub/HubSimulate.ts +124 -124
- package/src/hub/HubTauri.ts +140 -140
- package/src/hub/HubWebSocket.ts +250 -250
- package/src/hub/debug.ts +211 -211
- package/src/hub/index.ts +81 -81
- package/src/themes/adc-dark/_extensions.scss +166 -166
- package/src/themes/adc-dark/_variables.scss +913 -913
- package/src/themes/adc-dark/blue/_fonts.scss +23 -23
- package/src/themes/adc-dark/blue/adc_theme.scss +31 -31
- package/src/themes/adc-dark/blue/theme.scss +14 -14
- package/src/themes/theme-base/_colors.scss +17 -17
- package/src/themes/theme-base/_common.scss +74 -74
- package/src/themes/theme-base/_components.scss +111 -111
- package/src/themes/theme-base/_mixins.scss +243 -243
- package/src/themes/theme-base/components/button/_button.scss +644 -644
- package/src/themes/theme-base/components/button/_speeddial.scss +91 -91
- package/src/themes/theme-base/components/button/_splitbutton.scss +358 -358
- package/src/themes/theme-base/components/data/_carousel.scss +39 -39
- package/src/themes/theme-base/components/data/_datascroller.scss +47 -47
- package/src/themes/theme-base/components/data/_datatable.scss +388 -388
- package/src/themes/theme-base/components/data/_dataview.scss +47 -47
- package/src/themes/theme-base/components/data/_filter.scss +137 -137
- package/src/themes/theme-base/components/data/_orderlist.scss +86 -86
- package/src/themes/theme-base/components/data/_organizationchart.scss +50 -50
- package/src/themes/theme-base/components/data/_paginator.scss +91 -91
- package/src/themes/theme-base/components/data/_picklist.scss +73 -73
- package/src/themes/theme-base/components/data/_timeline.scss +38 -38
- package/src/themes/theme-base/components/data/_tree.scss +184 -184
- package/src/themes/theme-base/components/data/_treetable.scss +431 -431
- package/src/themes/theme-base/components/file/_fileupload.scss +41 -41
- package/src/themes/theme-base/components/input/_autocomplete.scss +94 -94
- package/src/themes/theme-base/components/input/_calendar.scss +251 -251
- package/src/themes/theme-base/components/input/_cascadeselect.scss +107 -107
- package/src/themes/theme-base/components/input/_checkbox.scss +181 -181
- package/src/themes/theme-base/components/input/_chips.scss +102 -102
- package/src/themes/theme-base/components/input/_colorpicker.scss +17 -17
- package/src/themes/theme-base/components/input/_dropdown.scss +252 -252
- package/src/themes/theme-base/components/input/_editor.scss +122 -122
- package/src/themes/theme-base/components/input/_iconfield.scss +9 -9
- package/src/themes/theme-base/components/input/_inputgroup.scss +74 -74
- package/src/themes/theme-base/components/input/_inputicon.scss +14 -14
- package/src/themes/theme-base/components/input/_inputnumber.scss +4 -4
- package/src/themes/theme-base/components/input/_inputotp.scss +10 -10
- package/src/themes/theme-base/components/input/_inputswitch.scss +99 -99
- package/src/themes/theme-base/components/input/_inputtext.scss +101 -101
- package/src/themes/theme-base/components/input/_listbox.scss +138 -138
- package/src/themes/theme-base/components/input/_mention.scss +30 -30
- package/src/themes/theme-base/components/input/_multiselect.scss +278 -278
- package/src/themes/theme-base/components/input/_password.scss +32 -32
- package/src/themes/theme-base/components/input/_radiobutton.scss +169 -169
- package/src/themes/theme-base/components/input/_rating.scss +80 -80
- package/src/themes/theme-base/components/input/_selectbutton.scss +49 -49
- package/src/themes/theme-base/components/input/_slider.scss +49 -49
- package/src/themes/theme-base/components/input/_togglebutton.scss +99 -99
- package/src/themes/theme-base/components/input/_treeselect.scss +151 -151
- package/src/themes/theme-base/components/input/_tristatecheckbox.scss +46 -46
- package/src/themes/theme-base/components/menu/_breadcrumb.scss +42 -42
- package/src/themes/theme-base/components/menu/_contextmenu.scss +39 -39
- package/src/themes/theme-base/components/menu/_dock.scss +109 -109
- package/src/themes/theme-base/components/menu/_megamenu.scss +141 -141
- package/src/themes/theme-base/components/menu/_menu.scss +33 -33
- package/src/themes/theme-base/components/menu/_menubar.scss +216 -216
- package/src/themes/theme-base/components/menu/_panelmenu.scss +153 -153
- package/src/themes/theme-base/components/menu/_slidemenu.scss +60 -60
- package/src/themes/theme-base/components/menu/_steps.scss +57 -57
- package/src/themes/theme-base/components/menu/_tabmenu.scss +50 -50
- package/src/themes/theme-base/components/menu/_tieredmenu.scss +43 -43
- package/src/themes/theme-base/components/messages/_inlinemessage.scss +69 -69
- package/src/themes/theme-base/components/messages/_message.scss +107 -107
- package/src/themes/theme-base/components/messages/_toast.scss +100 -100
- package/src/themes/theme-base/components/misc/_avatar.scss +33 -33
- package/src/themes/theme-base/components/misc/_badge.scss +76 -76
- package/src/themes/theme-base/components/misc/_chip.scss +38 -38
- package/src/themes/theme-base/components/misc/_inplace.scss +17 -17
- package/src/themes/theme-base/components/misc/_metergroup.scss +80 -80
- package/src/themes/theme-base/components/misc/_progressbar.scss +17 -17
- package/src/themes/theme-base/components/misc/_scrolltop.scss +24 -24
- package/src/themes/theme-base/components/misc/_skeleton.scss +7 -7
- package/src/themes/theme-base/components/misc/_tag.scss +39 -39
- package/src/themes/theme-base/components/misc/_terminal.scss +12 -12
- package/src/themes/theme-base/components/multimedia/_galleria.scss +153 -153
- package/src/themes/theme-base/components/multimedia/_image.scss +53 -53
- package/src/themes/theme-base/components/overlay/_confirmpopup.scss +72 -72
- package/src/themes/theme-base/components/overlay/_dialog.scss +78 -78
- package/src/themes/theme-base/components/overlay/_overlaypanel.scss +64 -64
- package/src/themes/theme-base/components/overlay/_sidebar.scss +23 -23
- package/src/themes/theme-base/components/overlay/_tooltip.scss +33 -33
- package/src/themes/theme-base/components/panel/_accordion.scss +118 -118
- package/src/themes/theme-base/components/panel/_card.scss +30 -30
- package/src/themes/theme-base/components/panel/_divider.scss +30 -30
- package/src/themes/theme-base/components/panel/_fieldset.scss +47 -47
- package/src/themes/theme-base/components/panel/_panel.scss +47 -47
- package/src/themes/theme-base/components/panel/_scrollpanel.scss +10 -10
- package/src/themes/theme-base/components/panel/_splitter.scss +23 -23
- package/src/themes/theme-base/components/panel/_stepper.scss +136 -136
- package/src/themes/theme-base/components/panel/_tabview.scss +147 -147
- package/src/themes/theme-base/components/panel/_toolbar.scss +11 -11
- package/terser.config.cjs +25 -25
- package/todo.md +18 -18
- package/tools/build-themes.cjs +65 -65
- package/tools/copy-distribution-files.cjs +77 -77
- package/tools/minify.cjs +55 -55
- package/tsconfig.json +48 -48
- package/typedoc.json +12 -12
- package/.claude/settings.local.json +0 -7
package/src/hub/HubBase.ts
CHANGED
|
@@ -1,237 +1,250 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (C) 2023 Automated Design Corp. All Rights Reserved.
|
|
3
|
-
* Created Date: 2023-12-15 14:21:33
|
|
4
|
-
* Author: Thomas C. Bitsky Jr.
|
|
5
|
-
* -----
|
|
6
|
-
* Last Modified: 2026-01-29 09:34:10
|
|
7
|
-
* Modified By: ADC
|
|
8
|
-
* -----
|
|
9
|
-
*
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import type { EventEmitterContextType } from '../core/EventEmitterContext';
|
|
13
|
-
import type { CommandMessage } from "./CommandMessage";
|
|
14
|
-
import { MessageType } from "./CommandMessage";
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @class HubBase
|
|
19
|
-
* @abstract
|
|
20
|
-
*
|
|
21
|
-
* Abstract base class that defines the interface for communicating with the AutoCore backend.
|
|
22
|
-
* Implementations (like `HubWebSocket` or `HubTauri`) handle the specific transport mechanics.
|
|
23
|
-
*
|
|
24
|
-
* ## Core Responsibilities
|
|
25
|
-
*
|
|
26
|
-
* 1. **Transport Abstraction**: Provides a unified API (`read`, `write`, `subscribe`) regardless of
|
|
27
|
-
* underlying transport (WS, IPC, etc.).
|
|
28
|
-
* 2. **Topic Mapping**: Maintains a bidirectional mapping between local `tagName` (UI friendly)
|
|
29
|
-
* and backend `FQDN` (Fully Qualified Domain Name).
|
|
30
|
-
* - `tagName`: "motorsEnable"
|
|
31
|
-
* - `FQDN`: "ADS.Main.bMotorsEnable"
|
|
32
|
-
* 3. **Event Dispatching**: Routes incoming messages from the backend to the `EventEmitterContext`.
|
|
33
|
-
*
|
|
34
|
-
* ## Usage
|
|
35
|
-
*
|
|
36
|
-
* The `Hub` is typically accessed via the `EventEmitterContext`:
|
|
37
|
-
*
|
|
38
|
-
* ```typescript
|
|
39
|
-
* const { hub } = useContext(EventEmitterContext);
|
|
40
|
-
* await hub.write("ADS.Main.nSpeed", 500);
|
|
41
|
-
* ```
|
|
42
|
-
*/
|
|
43
|
-
export abstract class HubBase {
|
|
44
|
-
|
|
45
|
-
/** Maps local tag names to backend FQDNs. */
|
|
46
|
-
protected localTopicToBackendTopicMap: Map<string, string> = new Map<string, string>();
|
|
47
|
-
|
|
48
|
-
/** Maps backend FQDNs to local tag names. */
|
|
49
|
-
protected backendTopicToLocalMap: Map<string, string> = new Map<string, string>();
|
|
50
|
-
|
|
51
|
-
/** Reference to the global event emitter for dispatching incoming messages. */
|
|
52
|
-
protected context: EventEmitterContextType | null = null;
|
|
53
|
-
|
|
54
|
-
/** Connection state tracker. */
|
|
55
|
-
protected isConnected_: boolean = false;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Constructor
|
|
60
|
-
*/
|
|
61
|
-
constructor() {
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Links this Hub instance with the React application's Event Context.
|
|
67
|
-
*
|
|
68
|
-
* This connection allows the Hub to "push" messages into the React app by calling `context.dispatch()`.
|
|
69
|
-
* This is called automatically by `EventEmitterProvider` on initialization.
|
|
70
|
-
*
|
|
71
|
-
* @param context The React context instance.
|
|
72
|
-
*/
|
|
73
|
-
setContext(context: EventEmitterContextType | null) {
|
|
74
|
-
this.context = context;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// =========================================================================
|
|
78
|
-
// Abstract Methods - Must be implemented by subclasses
|
|
79
|
-
// =========================================================================
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Sends a raw command message to the backend.
|
|
83
|
-
*
|
|
84
|
-
* @param topic The resource identifier (FQDN).
|
|
85
|
-
* @param messageType The operation to perform (Read, Write, etc.).
|
|
86
|
-
* @param payload The data payload.
|
|
87
|
-
* @returns A Promise that resolves with the server's response.
|
|
88
|
-
*/
|
|
89
|
-
abstract invoke(topic: string, messageType: MessageType, payload?: object): Promise<CommandMessage>;
|
|
90
|
-
|
|
91
|
-
// =========================================================================
|
|
92
|
-
// Convenience Helper Methods
|
|
93
|
-
// =========================================================================
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Request the current value of a resource.
|
|
97
|
-
*
|
|
98
|
-
* @param topic FQDN of the resource.
|
|
99
|
-
*/
|
|
100
|
-
read(topic: string, payload?: object): Promise<CommandMessage> {
|
|
101
|
-
return this.invoke(topic, MessageType.Read, payload);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Write a new value to a resource.
|
|
106
|
-
*
|
|
107
|
-
* @param topic FQDN of the resource.
|
|
108
|
-
* @param value The value to write.
|
|
109
|
-
*/
|
|
110
|
-
write(topic: string, value: any): Promise<CommandMessage> {
|
|
111
|
-
return this.invoke(topic, MessageType.Write, { value });
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Subscribe to updates for a specific resource.
|
|
116
|
-
*
|
|
117
|
-
* Registers a mapping so that when the server sends updates for `topic` (FQDN),
|
|
118
|
-
* the Hub will translate it to `tagName` before dispatching to the UI.
|
|
119
|
-
*
|
|
120
|
-
* @param tagName The local alias for this resource (e.g., "position").
|
|
121
|
-
* @param topic The backend FQDN (e.g., "ADS.Main.fPos").
|
|
122
|
-
* @param payload Optional subscription parameters (rate, deadband).
|
|
123
|
-
*/
|
|
124
|
-
subscribe(tagName : string, topic: string, payload?: object): Promise<CommandMessage> {
|
|
125
|
-
this.mapTopic(tagName, topic);
|
|
126
|
-
return this.invoke(topic, MessageType.Subscribe, payload);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Unsubscribe from updates.
|
|
131
|
-
*
|
|
132
|
-
* Uses the `tagName` to look up the FQDN and send the unsubscribe command.
|
|
133
|
-
* Also removes the local mapping.
|
|
134
|
-
*
|
|
135
|
-
* @param tagName The local alias to unsubscribe.
|
|
136
|
-
*/
|
|
137
|
-
unsubscribe( /* topic: string,*/ tagName : string, payload?: object): Promise<CommandMessage> {
|
|
138
|
-
const fqdn = this.toBackendTopic(tagName);
|
|
139
|
-
this.unmapTopic(tagName);
|
|
140
|
-
return this.invoke(fqdn, MessageType.Unsubscribe, payload);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Send a generic request (custom command).
|
|
145
|
-
*/
|
|
146
|
-
request(topic: string, payload?: object): Promise<CommandMessage> {
|
|
147
|
-
return this.invoke(topic, MessageType.Request, payload);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// =========================================================================
|
|
151
|
-
// Connection State
|
|
152
|
-
// =========================================================================
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Update internal connection state.
|
|
156
|
-
*/
|
|
157
|
-
protected setIsConnected = (s: boolean) => {
|
|
158
|
-
this.isConnected_ = s;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Check if currently connected to backend.
|
|
163
|
-
*/
|
|
164
|
-
isConnected = (): boolean => {
|
|
165
|
-
return this.isConnected_;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// =========================================================================
|
|
169
|
-
// Frontend Event Bus (Local Publish/Subscribe)
|
|
170
|
-
// =========================================================================
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Dispatches an event to the React application.
|
|
174
|
-
*
|
|
175
|
-
* 1. Translates the `topic` (potentially an FQDN) to a local `tagName` if a mapping exists.
|
|
176
|
-
* 2. Calls `context.dispatch()` to notify local subscribers.
|
|
177
|
-
*
|
|
178
|
-
* This is the bridge between the backend transport and the frontend UI components.
|
|
179
|
-
*
|
|
180
|
-
* @param topic The topic from the message (usually FQDN).
|
|
181
|
-
* @param data The data payload.
|
|
182
|
-
*/
|
|
183
|
-
publish(topic: string, data: any | undefined): void {
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
// =========================================================================
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2023 Automated Design Corp. All Rights Reserved.
|
|
3
|
+
* Created Date: 2023-12-15 14:21:33
|
|
4
|
+
* Author: Thomas C. Bitsky Jr.
|
|
5
|
+
* -----
|
|
6
|
+
* Last Modified: 2026-01-29 09:34:10
|
|
7
|
+
* Modified By: ADC
|
|
8
|
+
* -----
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { EventEmitterContextType } from '../core/EventEmitterContext';
|
|
13
|
+
import type { CommandMessage } from "./CommandMessage";
|
|
14
|
+
import { MessageType } from "./CommandMessage";
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @class HubBase
|
|
19
|
+
* @abstract
|
|
20
|
+
*
|
|
21
|
+
* Abstract base class that defines the interface for communicating with the AutoCore backend.
|
|
22
|
+
* Implementations (like `HubWebSocket` or `HubTauri`) handle the specific transport mechanics.
|
|
23
|
+
*
|
|
24
|
+
* ## Core Responsibilities
|
|
25
|
+
*
|
|
26
|
+
* 1. **Transport Abstraction**: Provides a unified API (`read`, `write`, `subscribe`) regardless of
|
|
27
|
+
* underlying transport (WS, IPC, etc.).
|
|
28
|
+
* 2. **Topic Mapping**: Maintains a bidirectional mapping between local `tagName` (UI friendly)
|
|
29
|
+
* and backend `FQDN` (Fully Qualified Domain Name).
|
|
30
|
+
* - `tagName`: "motorsEnable"
|
|
31
|
+
* - `FQDN`: "ADS.Main.bMotorsEnable"
|
|
32
|
+
* 3. **Event Dispatching**: Routes incoming messages from the backend to the `EventEmitterContext`.
|
|
33
|
+
*
|
|
34
|
+
* ## Usage
|
|
35
|
+
*
|
|
36
|
+
* The `Hub` is typically accessed via the `EventEmitterContext`:
|
|
37
|
+
*
|
|
38
|
+
* ```typescript
|
|
39
|
+
* const { hub } = useContext(EventEmitterContext);
|
|
40
|
+
* await hub.write("ADS.Main.nSpeed", 500);
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export abstract class HubBase {
|
|
44
|
+
|
|
45
|
+
/** Maps local tag names to backend FQDNs. */
|
|
46
|
+
protected localTopicToBackendTopicMap: Map<string, string> = new Map<string, string>();
|
|
47
|
+
|
|
48
|
+
/** Maps backend FQDNs to local tag names. */
|
|
49
|
+
protected backendTopicToLocalMap: Map<string, string[]> = new Map<string, string[]>();
|
|
50
|
+
|
|
51
|
+
/** Reference to the global event emitter for dispatching incoming messages. */
|
|
52
|
+
protected context: EventEmitterContextType | null = null;
|
|
53
|
+
|
|
54
|
+
/** Connection state tracker. */
|
|
55
|
+
protected isConnected_: boolean = false;
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Constructor
|
|
60
|
+
*/
|
|
61
|
+
constructor() {
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Links this Hub instance with the React application's Event Context.
|
|
67
|
+
*
|
|
68
|
+
* This connection allows the Hub to "push" messages into the React app by calling `context.dispatch()`.
|
|
69
|
+
* This is called automatically by `EventEmitterProvider` on initialization.
|
|
70
|
+
*
|
|
71
|
+
* @param context The React context instance.
|
|
72
|
+
*/
|
|
73
|
+
setContext(context: EventEmitterContextType | null) {
|
|
74
|
+
this.context = context;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// =========================================================================
|
|
78
|
+
// Abstract Methods - Must be implemented by subclasses
|
|
79
|
+
// =========================================================================
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Sends a raw command message to the backend.
|
|
83
|
+
*
|
|
84
|
+
* @param topic The resource identifier (FQDN).
|
|
85
|
+
* @param messageType The operation to perform (Read, Write, etc.).
|
|
86
|
+
* @param payload The data payload.
|
|
87
|
+
* @returns A Promise that resolves with the server's response.
|
|
88
|
+
*/
|
|
89
|
+
abstract invoke(topic: string, messageType: MessageType, payload?: object): Promise<CommandMessage>;
|
|
90
|
+
|
|
91
|
+
// =========================================================================
|
|
92
|
+
// Convenience Helper Methods
|
|
93
|
+
// =========================================================================
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Request the current value of a resource.
|
|
97
|
+
*
|
|
98
|
+
* @param topic FQDN of the resource.
|
|
99
|
+
*/
|
|
100
|
+
read(topic: string, payload?: object): Promise<CommandMessage> {
|
|
101
|
+
return this.invoke(topic, MessageType.Read, payload);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Write a new value to a resource.
|
|
106
|
+
*
|
|
107
|
+
* @param topic FQDN of the resource.
|
|
108
|
+
* @param value The value to write.
|
|
109
|
+
*/
|
|
110
|
+
write(topic: string, value: any): Promise<CommandMessage> {
|
|
111
|
+
return this.invoke(topic, MessageType.Write, { value });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Subscribe to updates for a specific resource.
|
|
116
|
+
*
|
|
117
|
+
* Registers a mapping so that when the server sends updates for `topic` (FQDN),
|
|
118
|
+
* the Hub will translate it to `tagName` before dispatching to the UI.
|
|
119
|
+
*
|
|
120
|
+
* @param tagName The local alias for this resource (e.g., "position").
|
|
121
|
+
* @param topic The backend FQDN (e.g., "ADS.Main.fPos").
|
|
122
|
+
* @param payload Optional subscription parameters (rate, deadband).
|
|
123
|
+
*/
|
|
124
|
+
subscribe(tagName : string, topic: string, payload?: object): Promise<CommandMessage> {
|
|
125
|
+
this.mapTopic(tagName, topic);
|
|
126
|
+
return this.invoke(topic, MessageType.Subscribe, payload);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Unsubscribe from updates.
|
|
131
|
+
*
|
|
132
|
+
* Uses the `tagName` to look up the FQDN and send the unsubscribe command.
|
|
133
|
+
* Also removes the local mapping.
|
|
134
|
+
*
|
|
135
|
+
* @param tagName The local alias to unsubscribe.
|
|
136
|
+
*/
|
|
137
|
+
unsubscribe( /* topic: string,*/ tagName : string, payload?: object): Promise<CommandMessage> {
|
|
138
|
+
const fqdn = this.toBackendTopic(tagName);
|
|
139
|
+
this.unmapTopic(tagName);
|
|
140
|
+
return this.invoke(fqdn, MessageType.Unsubscribe, payload);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Send a generic request (custom command).
|
|
145
|
+
*/
|
|
146
|
+
request(topic: string, payload?: object): Promise<CommandMessage> {
|
|
147
|
+
return this.invoke(topic, MessageType.Request, payload);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// =========================================================================
|
|
151
|
+
// Connection State
|
|
152
|
+
// =========================================================================
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Update internal connection state.
|
|
156
|
+
*/
|
|
157
|
+
protected setIsConnected = (s: boolean) => {
|
|
158
|
+
this.isConnected_ = s;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Check if currently connected to backend.
|
|
163
|
+
*/
|
|
164
|
+
isConnected = (): boolean => {
|
|
165
|
+
return this.isConnected_;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// =========================================================================
|
|
169
|
+
// Frontend Event Bus (Local Publish/Subscribe)
|
|
170
|
+
// =========================================================================
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Dispatches an event to the React application.
|
|
174
|
+
*
|
|
175
|
+
* 1. Translates the `topic` (potentially an FQDN) to a local `tagName` if a mapping exists.
|
|
176
|
+
* 2. Calls `context.dispatch()` to notify local subscribers.
|
|
177
|
+
*
|
|
178
|
+
* This is the bridge between the backend transport and the frontend UI components.
|
|
179
|
+
*
|
|
180
|
+
* @param topic The topic from the message (usually FQDN).
|
|
181
|
+
* @param data The data payload.
|
|
182
|
+
*/
|
|
183
|
+
publish(topic: string, data: any | undefined): void {
|
|
184
|
+
const convertedTopics = this.toLocalTopics(topic);
|
|
185
|
+
|
|
186
|
+
for (const convertedTopic of convertedTopics) {
|
|
187
|
+
console.log(`HUB Dispatch topic ${convertedTopic} payload ${JSON.stringify(data)}`);
|
|
188
|
+
this.context?.dispatch({
|
|
189
|
+
topic: convertedTopic,
|
|
190
|
+
payload: data
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// =========================================================================
|
|
196
|
+
// Topic Mapping (for aliasing backend topics to local names)
|
|
197
|
+
// =========================================================================
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Registers a mapping between a local name and a backend FQDN.
|
|
201
|
+
*/
|
|
202
|
+
mapTopic(localTopic: string, backendTopic: string): void {
|
|
203
|
+
this.localTopicToBackendTopicMap.set(localTopic, backendTopic);
|
|
204
|
+
|
|
205
|
+
let localTopics = this.backendTopicToLocalMap.get(backendTopic) || [];
|
|
206
|
+
if (!localTopics.includes(localTopic)) {
|
|
207
|
+
localTopics.push(localTopic);
|
|
208
|
+
this.backendTopicToLocalMap.set(backendTopic, localTopics);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Removes a mapping.
|
|
214
|
+
*/
|
|
215
|
+
unmapTopic(localTopic: string): void {
|
|
216
|
+
if (this.localTopicToBackendTopicMap.has(localTopic)) {
|
|
217
|
+
const be = this.localTopicToBackendTopicMap.get(localTopic);
|
|
218
|
+
this.localTopicToBackendTopicMap.delete(localTopic);
|
|
219
|
+
|
|
220
|
+
if (be !== null && be !== undefined && this.backendTopicToLocalMap.has(be)) {
|
|
221
|
+
let localTopics = this.backendTopicToLocalMap.get(be) || [];
|
|
222
|
+
localTopics = localTopics.filter(t => t !== localTopic);
|
|
223
|
+
if (localTopics.length > 0) {
|
|
224
|
+
this.backendTopicToLocalMap.set(be, localTopics);
|
|
225
|
+
} else {
|
|
226
|
+
this.backendTopicToLocalMap.delete(be);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Translates Local Name -> Backend FQDN.
|
|
234
|
+
*/
|
|
235
|
+
toBackendTopic(localTopic: string): string {
|
|
236
|
+
const mapped = this.localTopicToBackendTopicMap.get(localTopic);
|
|
237
|
+
return mapped ?? localTopic;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Translates Backend FQDN -> Array of Local Names.
|
|
242
|
+
*/
|
|
243
|
+
toLocalTopics(backendTopic: string): string[] {
|
|
244
|
+
const mapped = this.backendTopicToLocalMap.get(backendTopic);
|
|
245
|
+
return mapped && mapped.length > 0 ? mapped : [backendTopic];
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
|
|
237
250
|
export default HubBase;
|