@adcops/autocore-react 3.3.8 → 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
|
@@ -1,512 +1,512 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (C) 2026 Automated Design Corp. All Rights Reserved.
|
|
3
|
-
*
|
|
4
|
-
* CoreStream Protocol Types for TypeScript
|
|
5
|
-
*
|
|
6
|
-
* This module defines the JSON-wrapped CoreStream protocol used for
|
|
7
|
-
* WebSocket communication between web clients and autocore-server.
|
|
8
|
-
*
|
|
9
|
-
* The protocol supports four message types:
|
|
10
|
-
* - SDO: Service Data Object - read/write individual values
|
|
11
|
-
* - Cyclic: Subscription-based push updates
|
|
12
|
-
* - System: Module lifecycle (announce, health, start/stop)
|
|
13
|
-
* - RPC: Remote procedure calls for arbitrary functions
|
|
14
|
-
*
|
|
15
|
-
* Addressing supports two modes:
|
|
16
|
-
* - FQDN: Human-readable names like "vfd.frequency"
|
|
17
|
-
* - Index:SubIndex: CANopen-style addresses like {index: 0x2000, subindex: 1}
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
// ============================================================================
|
|
21
|
-
// Core Types
|
|
22
|
-
// ============================================================================
|
|
23
|
-
|
|
24
|
-
/** Message type discriminator */
|
|
25
|
-
export type MessageType = "sdo" | "cyclic" | "system" | "rpc";
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Address can be either a symbol name (FQDN) or CANopen-style index:subindex.
|
|
29
|
-
* Modules accept both formats and resolve internally.
|
|
30
|
-
*/
|
|
31
|
-
export type SymbolAddress =
|
|
32
|
-
| { symbol: string } // FQDN: "vfd.frequency"
|
|
33
|
-
| { index: number; subindex: number }; // OD: {index: 0x2000, subindex: 1}
|
|
34
|
-
|
|
35
|
-
/** Data types supported by the protocol */
|
|
36
|
-
export type DataType =
|
|
37
|
-
| "bool"
|
|
38
|
-
| "i8" | "i16" | "i32" | "i64"
|
|
39
|
-
| "u8" | "u16" | "u32" | "u64"
|
|
40
|
-
| "f32" | "f64"
|
|
41
|
-
| "string"
|
|
42
|
-
| "bytes" // Base64-encoded binary
|
|
43
|
-
| "json"; // Arbitrary JSON value
|
|
44
|
-
|
|
45
|
-
/** Access mode for symbols */
|
|
46
|
-
export type AccessMode = "ro" | "wo" | "rw";
|
|
47
|
-
|
|
48
|
-
// ============================================================================
|
|
49
|
-
// Base Message Structure
|
|
50
|
-
// ============================================================================
|
|
51
|
-
|
|
52
|
-
/** Base structure for all CoreStream messages */
|
|
53
|
-
export interface CoreStreamMessage<T extends MessageType = MessageType> {
|
|
54
|
-
/** Message type discriminator */
|
|
55
|
-
type: T;
|
|
56
|
-
/** Command within the message type */
|
|
57
|
-
command: string;
|
|
58
|
-
/** Request ID for request/response matching (optional for push messages) */
|
|
59
|
-
request_id?: number;
|
|
60
|
-
/** Message payload (type-specific) */
|
|
61
|
-
payload: unknown;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// ============================================================================
|
|
65
|
-
// SDO Messages (Service Data Object - Read/Write)
|
|
66
|
-
// ============================================================================
|
|
67
|
-
|
|
68
|
-
export type SdoCommand =
|
|
69
|
-
| "upload_request" // Read request (client → server)
|
|
70
|
-
| "upload_response" // Read response (server → client)
|
|
71
|
-
| "download_request" // Write request (client → server)
|
|
72
|
-
| "download_response" // Write response (server → client)
|
|
73
|
-
| "abort"; // Error/abort
|
|
74
|
-
|
|
75
|
-
/** SDO Upload Request - Read a value */
|
|
76
|
-
export interface SdoUploadRequest extends CoreStreamMessage<"sdo"> {
|
|
77
|
-
command: "upload_request";
|
|
78
|
-
request_id: number;
|
|
79
|
-
payload: SymbolAddress;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/** SDO Upload Response - Read result */
|
|
83
|
-
export interface SdoUploadResponse extends CoreStreamMessage<"sdo"> {
|
|
84
|
-
command: "upload_response";
|
|
85
|
-
request_id: number;
|
|
86
|
-
payload: {
|
|
87
|
-
/** The address that was read (echoed back) */
|
|
88
|
-
address: SymbolAddress;
|
|
89
|
-
/** The value read */
|
|
90
|
-
value: unknown;
|
|
91
|
-
/** Data type of the value */
|
|
92
|
-
datatype: DataType;
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** SDO Download Request - Write a value */
|
|
97
|
-
export interface SdoDownloadRequest extends CoreStreamMessage<"sdo"> {
|
|
98
|
-
command: "download_request";
|
|
99
|
-
request_id: number;
|
|
100
|
-
payload: SymbolAddress & {
|
|
101
|
-
/** The value to write */
|
|
102
|
-
value: unknown;
|
|
103
|
-
/** Optional: data type hint */
|
|
104
|
-
datatype?: DataType;
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/** SDO Download Response - Write acknowledgment */
|
|
109
|
-
export interface SdoDownloadResponse extends CoreStreamMessage<"sdo"> {
|
|
110
|
-
command: "download_response";
|
|
111
|
-
request_id: number;
|
|
112
|
-
payload: {
|
|
113
|
-
/** The address that was written (echoed back) */
|
|
114
|
-
address: SymbolAddress;
|
|
115
|
-
/** Whether the write succeeded */
|
|
116
|
-
success: boolean;
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** SDO Abort - Error response */
|
|
121
|
-
export interface SdoAbort extends CoreStreamMessage<"sdo"> {
|
|
122
|
-
command: "abort";
|
|
123
|
-
request_id: number;
|
|
124
|
-
payload: {
|
|
125
|
-
/** The address that caused the error */
|
|
126
|
-
address: SymbolAddress;
|
|
127
|
-
/** CANopen-style abort code */
|
|
128
|
-
abort_code: number;
|
|
129
|
-
/** Human-readable error message */
|
|
130
|
-
message: string;
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/** Standard SDO abort codes (CANopen-compatible) */
|
|
135
|
-
export const SdoAbortCode = {
|
|
136
|
-
TOGGLE_BIT_NOT_ALTERED: 0x05030000,
|
|
137
|
-
SDO_PROTOCOL_TIMEOUT: 0x05040000,
|
|
138
|
-
INVALID_COMMAND: 0x05040001,
|
|
139
|
-
OUT_OF_MEMORY: 0x05040005,
|
|
140
|
-
UNSUPPORTED_ACCESS: 0x06010000,
|
|
141
|
-
WRITE_ONLY: 0x06010001,
|
|
142
|
-
READ_ONLY: 0x06010002,
|
|
143
|
-
OBJECT_NOT_EXIST: 0x06020000,
|
|
144
|
-
SUBINDEX_NOT_EXIST: 0x06090011,
|
|
145
|
-
GENERAL_ERROR: 0x08000000,
|
|
146
|
-
DATA_TRANSFER_ERROR: 0x08000020,
|
|
147
|
-
DATA_LENGTH_MISMATCH: 0x08000022,
|
|
148
|
-
} as const;
|
|
149
|
-
|
|
150
|
-
export type SdoMessage =
|
|
151
|
-
| SdoUploadRequest
|
|
152
|
-
| SdoUploadResponse
|
|
153
|
-
| SdoDownloadRequest
|
|
154
|
-
| SdoDownloadResponse
|
|
155
|
-
| SdoAbort;
|
|
156
|
-
|
|
157
|
-
// ============================================================================
|
|
158
|
-
// Cyclic Messages (Subscriptions and Push Updates)
|
|
159
|
-
// ============================================================================
|
|
160
|
-
|
|
161
|
-
export type CyclicCommand =
|
|
162
|
-
| "subscribe" // Subscribe to updates
|
|
163
|
-
| "subscribe_ack" // Subscribe acknowledgment
|
|
164
|
-
| "unsubscribe" // Unsubscribe
|
|
165
|
-
| "unsubscribe_ack" // Unsubscribe acknowledgment
|
|
166
|
-
| "data_update" // Single value update (push)
|
|
167
|
-
| "data_update_batch"; // Multiple values update (push)
|
|
168
|
-
|
|
169
|
-
/** Subscription options for cyclic updates */
|
|
170
|
-
export interface SubscriptionOptions {
|
|
171
|
-
/** Minimum interval between updates in milliseconds */
|
|
172
|
-
sampling_interval_ms?: number;
|
|
173
|
-
/** Only send updates when value changes */
|
|
174
|
-
on_change_only?: boolean;
|
|
175
|
-
/** Deadband as percentage of full scale (0-100) */
|
|
176
|
-
deadband_percent?: number;
|
|
177
|
-
/** Deadband as absolute value */
|
|
178
|
-
deadband_absolute?: number;
|
|
179
|
-
/** Queue size for buffering (default: 1 = latest only) */
|
|
180
|
-
queue_size?: number;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/** Subscribe to cyclic updates for a symbol */
|
|
184
|
-
export interface CyclicSubscribe extends CoreStreamMessage<"cyclic"> {
|
|
185
|
-
command: "subscribe";
|
|
186
|
-
request_id: number;
|
|
187
|
-
payload: SymbolAddress & {
|
|
188
|
-
/** Optional subscription options */
|
|
189
|
-
options?: SubscriptionOptions;
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/** Subscribe acknowledgment */
|
|
194
|
-
export interface CyclicSubscribeAck extends CoreStreamMessage<"cyclic"> {
|
|
195
|
-
command: "subscribe_ack";
|
|
196
|
-
request_id: number;
|
|
197
|
-
payload: {
|
|
198
|
-
/** The address subscribed to */
|
|
199
|
-
address: SymbolAddress;
|
|
200
|
-
/** Whether subscription succeeded */
|
|
201
|
-
success: boolean;
|
|
202
|
-
/** Error message if failed */
|
|
203
|
-
error?: string;
|
|
204
|
-
/** Effective options after server clamping */
|
|
205
|
-
effective_options?: SubscriptionOptions;
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/** Unsubscribe from cyclic updates */
|
|
210
|
-
export interface CyclicUnsubscribe extends CoreStreamMessage<"cyclic"> {
|
|
211
|
-
command: "unsubscribe";
|
|
212
|
-
request_id: number;
|
|
213
|
-
payload: SymbolAddress;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/** Unsubscribe acknowledgment */
|
|
217
|
-
export interface CyclicUnsubscribeAck extends CoreStreamMessage<"cyclic"> {
|
|
218
|
-
command: "unsubscribe_ack";
|
|
219
|
-
request_id: number;
|
|
220
|
-
payload: {
|
|
221
|
-
address: SymbolAddress;
|
|
222
|
-
success: boolean;
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/** Single value update (pushed from server) */
|
|
227
|
-
export interface CyclicDataUpdate extends CoreStreamMessage<"cyclic"> {
|
|
228
|
-
command: "data_update";
|
|
229
|
-
payload: {
|
|
230
|
-
/** The symbol that changed */
|
|
231
|
-
symbol: string;
|
|
232
|
-
/** The new value */
|
|
233
|
-
value: unknown;
|
|
234
|
-
/** Server timestamp (nanoseconds since epoch, optional) */
|
|
235
|
-
timestamp_ns?: number;
|
|
236
|
-
/** Data type */
|
|
237
|
-
datatype?: DataType;
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/** Batch value update (multiple values in one message) */
|
|
242
|
-
export interface CyclicDataUpdateBatch extends CoreStreamMessage<"cyclic"> {
|
|
243
|
-
command: "data_update_batch";
|
|
244
|
-
payload: {
|
|
245
|
-
/** Array of updates */
|
|
246
|
-
updates: Array<{
|
|
247
|
-
symbol: string;
|
|
248
|
-
value: unknown;
|
|
249
|
-
timestamp_ns?: number;
|
|
250
|
-
datatype?: DataType;
|
|
251
|
-
}>;
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
export type CyclicMessage =
|
|
256
|
-
| CyclicSubscribe
|
|
257
|
-
| CyclicSubscribeAck
|
|
258
|
-
| CyclicUnsubscribe
|
|
259
|
-
| CyclicUnsubscribeAck
|
|
260
|
-
| CyclicDataUpdate
|
|
261
|
-
| CyclicDataUpdateBatch;
|
|
262
|
-
|
|
263
|
-
// ============================================================================
|
|
264
|
-
// System Messages (Module Lifecycle)
|
|
265
|
-
// ============================================================================
|
|
266
|
-
|
|
267
|
-
export type SystemCommand =
|
|
268
|
-
| "announce" // Module registration
|
|
269
|
-
| "announce_ack" // Registration acknowledgment
|
|
270
|
-
| "discovery" // Request all modules
|
|
271
|
-
| "discovery_response" // Module list
|
|
272
|
-
| "leaving" // Module disconnecting
|
|
273
|
-
| "start" // Start cyclic operation
|
|
274
|
-
| "stop" // Stop cyclic operation
|
|
275
|
-
| "health_request" // Health check
|
|
276
|
-
| "health_response"; // Health status
|
|
277
|
-
|
|
278
|
-
/** Symbol descriptor in module announcement */
|
|
279
|
-
export interface SymbolDescriptor {
|
|
280
|
-
/** Human-readable symbol name (FQDN within module) */
|
|
281
|
-
name: string;
|
|
282
|
-
/** CANopen-style index */
|
|
283
|
-
index: number;
|
|
284
|
-
/** CANopen-style subindex */
|
|
285
|
-
subindex: number;
|
|
286
|
-
/** Data type */
|
|
287
|
-
datatype: DataType;
|
|
288
|
-
/** Access mode */
|
|
289
|
-
access: AccessMode;
|
|
290
|
-
/** Optional description */
|
|
291
|
-
description?: string;
|
|
292
|
-
/** Optional unit (e.g., "mm", "Hz") */
|
|
293
|
-
unit?: string;
|
|
294
|
-
/** Optional min value (for validation) */
|
|
295
|
-
min?: number;
|
|
296
|
-
/** Optional max value (for validation) */
|
|
297
|
-
max?: number;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/** Module capabilities */
|
|
301
|
-
export interface ModuleCapabilities {
|
|
302
|
-
/** Supports SDO read */
|
|
303
|
-
sdo_read: boolean;
|
|
304
|
-
/** Supports SDO write */
|
|
305
|
-
sdo_write: boolean;
|
|
306
|
-
/** Supports cyclic subscriptions */
|
|
307
|
-
cyclic: boolean;
|
|
308
|
-
/** Supports batch operations */
|
|
309
|
-
batch: boolean;
|
|
310
|
-
/** Maximum symbols per batch */
|
|
311
|
-
max_batch_size?: number;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/** Module announces itself and its symbol catalog */
|
|
315
|
-
export interface SystemAnnounce extends CoreStreamMessage<"system"> {
|
|
316
|
-
command: "announce";
|
|
317
|
-
payload: {
|
|
318
|
-
/** Module name (used as FQDN prefix) */
|
|
319
|
-
module_name: string;
|
|
320
|
-
/** Module version */
|
|
321
|
-
version?: string;
|
|
322
|
-
/** Module capabilities */
|
|
323
|
-
capabilities: ModuleCapabilities;
|
|
324
|
-
/** Symbol catalog */
|
|
325
|
-
symbols: SymbolDescriptor[];
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/** Announcement acknowledgment */
|
|
330
|
-
export interface SystemAnnounceAck extends CoreStreamMessage<"system"> {
|
|
331
|
-
command: "announce_ack";
|
|
332
|
-
payload: {
|
|
333
|
-
/** Whether announcement was accepted */
|
|
334
|
-
success: boolean;
|
|
335
|
-
/** Assigned module ID (if applicable) */
|
|
336
|
-
module_id?: number;
|
|
337
|
-
/** Error message if failed */
|
|
338
|
-
error?: string;
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
/** Request list of all connected modules */
|
|
343
|
-
export interface SystemDiscovery extends CoreStreamMessage<"system"> {
|
|
344
|
-
command: "discovery";
|
|
345
|
-
request_id: number;
|
|
346
|
-
payload: Record<string, never>; // Empty object
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/** Discovery response with module list */
|
|
350
|
-
export interface SystemDiscoveryResponse extends CoreStreamMessage<"system"> {
|
|
351
|
-
command: "discovery_response";
|
|
352
|
-
request_id: number;
|
|
353
|
-
payload: {
|
|
354
|
-
modules: Array<{
|
|
355
|
-
module_name: string;
|
|
356
|
-
version?: string;
|
|
357
|
-
capabilities: ModuleCapabilities;
|
|
358
|
-
symbol_count: number;
|
|
359
|
-
state: "running" | "stopped" | "error";
|
|
360
|
-
}>;
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/** Module leaving notification */
|
|
365
|
-
export interface SystemLeaving extends CoreStreamMessage<"system"> {
|
|
366
|
-
command: "leaving";
|
|
367
|
-
payload: {
|
|
368
|
-
module_name: string;
|
|
369
|
-
reason?: string;
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/** Health check request */
|
|
374
|
-
export interface SystemHealthRequest extends CoreStreamMessage<"system"> {
|
|
375
|
-
command: "health_request";
|
|
376
|
-
request_id: number;
|
|
377
|
-
payload: {
|
|
378
|
-
/** Optional: specific module to check */
|
|
379
|
-
module_name?: string;
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
/** Health check response */
|
|
384
|
-
export interface SystemHealthResponse extends CoreStreamMessage<"system"> {
|
|
385
|
-
command: "health_response";
|
|
386
|
-
request_id: number;
|
|
387
|
-
payload: {
|
|
388
|
-
healthy: boolean;
|
|
389
|
-
modules: Array<{
|
|
390
|
-
module_name: string;
|
|
391
|
-
state: "running" | "stopped" | "error";
|
|
392
|
-
uptime_secs?: number;
|
|
393
|
-
error?: string;
|
|
394
|
-
}>;
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
export type SystemMessage =
|
|
399
|
-
| SystemAnnounce
|
|
400
|
-
| SystemAnnounceAck
|
|
401
|
-
| SystemDiscovery
|
|
402
|
-
| SystemDiscoveryResponse
|
|
403
|
-
| SystemLeaving
|
|
404
|
-
| SystemHealthRequest
|
|
405
|
-
| SystemHealthResponse;
|
|
406
|
-
|
|
407
|
-
// ============================================================================
|
|
408
|
-
// RPC Messages (Remote Procedure Calls)
|
|
409
|
-
// ============================================================================
|
|
410
|
-
|
|
411
|
-
export type RpcCommand =
|
|
412
|
-
| "invoke" // Call a function
|
|
413
|
-
| "response"; // Function result
|
|
414
|
-
|
|
415
|
-
/** RPC invoke request - call a function on a module */
|
|
416
|
-
export interface RpcInvoke extends CoreStreamMessage<"rpc"> {
|
|
417
|
-
command: "invoke";
|
|
418
|
-
request_id: number;
|
|
419
|
-
payload: {
|
|
420
|
-
/** Target module name */
|
|
421
|
-
module: string;
|
|
422
|
-
/** Method/function name */
|
|
423
|
-
method: string;
|
|
424
|
-
/** Arguments (JSON object) */
|
|
425
|
-
args?: Record<string, unknown>;
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
/** RPC response - function result */
|
|
430
|
-
export interface RpcResponse extends CoreStreamMessage<"rpc"> {
|
|
431
|
-
command: "response";
|
|
432
|
-
request_id: number;
|
|
433
|
-
payload: {
|
|
434
|
-
/** Whether the call succeeded */
|
|
435
|
-
success: boolean;
|
|
436
|
-
/** Return value (if success) */
|
|
437
|
-
data?: unknown;
|
|
438
|
-
/** Error message (if failed) */
|
|
439
|
-
error?: string;
|
|
440
|
-
/** Error code (if failed) */
|
|
441
|
-
error_code?: number;
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
export type RpcMessage = RpcInvoke | RpcResponse;
|
|
446
|
-
|
|
447
|
-
// ============================================================================
|
|
448
|
-
// Union Type for All Messages
|
|
449
|
-
// ============================================================================
|
|
450
|
-
|
|
451
|
-
export type AnyCoreSteamMessage =
|
|
452
|
-
| SdoMessage
|
|
453
|
-
| CyclicMessage
|
|
454
|
-
| SystemMessage
|
|
455
|
-
| RpcMessage;
|
|
456
|
-
|
|
457
|
-
// ============================================================================
|
|
458
|
-
// Helper Functions
|
|
459
|
-
// ============================================================================
|
|
460
|
-
|
|
461
|
-
/** Type guard for SDO messages */
|
|
462
|
-
export function isSdoMessage(msg: CoreStreamMessage): msg is SdoMessage {
|
|
463
|
-
return msg.type === "sdo";
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
/** Type guard for Cyclic messages */
|
|
467
|
-
export function isCyclicMessage(msg: CoreStreamMessage): msg is CyclicMessage {
|
|
468
|
-
return msg.type === "cyclic";
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
/** Type guard for System messages */
|
|
472
|
-
export function isSystemMessage(msg: CoreStreamMessage): msg is SystemMessage {
|
|
473
|
-
return msg.type === "system";
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
/** Type guard for RPC messages */
|
|
477
|
-
export function isRpcMessage(msg: CoreStreamMessage): msg is RpcMessage {
|
|
478
|
-
return msg.type === "rpc";
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/** Check if an address uses symbol format */
|
|
482
|
-
export function isSymbolAddress(addr: SymbolAddress): addr is { symbol: string } {
|
|
483
|
-
return "symbol" in addr;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
/** Check if an address uses index:subindex format */
|
|
487
|
-
export function isIndexAddress(addr: SymbolAddress): addr is { index: number; subindex: number } {
|
|
488
|
-
return "index" in addr && "subindex" in addr;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
/** Convert address to string for display/logging */
|
|
492
|
-
export function addressToString(addr: SymbolAddress): string {
|
|
493
|
-
if (isSymbolAddress(addr)) {
|
|
494
|
-
return addr.symbol;
|
|
495
|
-
} else {
|
|
496
|
-
return `0x${addr.index.toString(16).padStart(4, '0')}:${addr.subindex.toString(16).padStart(2, '0')}`;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
/** Parse an address string (either "vfd.frequency" or "0x2000:01") */
|
|
501
|
-
export function parseAddress(str: string): SymbolAddress {
|
|
502
|
-
// Check for hex format: 0xNNNN:NN or NNNN:NN
|
|
503
|
-
const hexMatch = str.match(/^(?:0x)?([0-9a-fA-F]{1,4}):([0-9a-fA-F]{1,2})$/);
|
|
504
|
-
if (hexMatch) {
|
|
505
|
-
return {
|
|
506
|
-
index: parseInt(hexMatch[1], 16),
|
|
507
|
-
subindex: parseInt(hexMatch[2], 16),
|
|
508
|
-
};
|
|
509
|
-
}
|
|
510
|
-
// Otherwise treat as symbol name
|
|
511
|
-
return { symbol: str };
|
|
512
|
-
}
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2026 Automated Design Corp. All Rights Reserved.
|
|
3
|
+
*
|
|
4
|
+
* CoreStream Protocol Types for TypeScript
|
|
5
|
+
*
|
|
6
|
+
* This module defines the JSON-wrapped CoreStream protocol used for
|
|
7
|
+
* WebSocket communication between web clients and autocore-server.
|
|
8
|
+
*
|
|
9
|
+
* The protocol supports four message types:
|
|
10
|
+
* - SDO: Service Data Object - read/write individual values
|
|
11
|
+
* - Cyclic: Subscription-based push updates
|
|
12
|
+
* - System: Module lifecycle (announce, health, start/stop)
|
|
13
|
+
* - RPC: Remote procedure calls for arbitrary functions
|
|
14
|
+
*
|
|
15
|
+
* Addressing supports two modes:
|
|
16
|
+
* - FQDN: Human-readable names like "vfd.frequency"
|
|
17
|
+
* - Index:SubIndex: CANopen-style addresses like {index: 0x2000, subindex: 1}
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Core Types
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
/** Message type discriminator */
|
|
25
|
+
export type MessageType = "sdo" | "cyclic" | "system" | "rpc";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Address can be either a symbol name (FQDN) or CANopen-style index:subindex.
|
|
29
|
+
* Modules accept both formats and resolve internally.
|
|
30
|
+
*/
|
|
31
|
+
export type SymbolAddress =
|
|
32
|
+
| { symbol: string } // FQDN: "vfd.frequency"
|
|
33
|
+
| { index: number; subindex: number }; // OD: {index: 0x2000, subindex: 1}
|
|
34
|
+
|
|
35
|
+
/** Data types supported by the protocol */
|
|
36
|
+
export type DataType =
|
|
37
|
+
| "bool"
|
|
38
|
+
| "i8" | "i16" | "i32" | "i64"
|
|
39
|
+
| "u8" | "u16" | "u32" | "u64"
|
|
40
|
+
| "f32" | "f64"
|
|
41
|
+
| "string"
|
|
42
|
+
| "bytes" // Base64-encoded binary
|
|
43
|
+
| "json"; // Arbitrary JSON value
|
|
44
|
+
|
|
45
|
+
/** Access mode for symbols */
|
|
46
|
+
export type AccessMode = "ro" | "wo" | "rw";
|
|
47
|
+
|
|
48
|
+
// ============================================================================
|
|
49
|
+
// Base Message Structure
|
|
50
|
+
// ============================================================================
|
|
51
|
+
|
|
52
|
+
/** Base structure for all CoreStream messages */
|
|
53
|
+
export interface CoreStreamMessage<T extends MessageType = MessageType> {
|
|
54
|
+
/** Message type discriminator */
|
|
55
|
+
type: T;
|
|
56
|
+
/** Command within the message type */
|
|
57
|
+
command: string;
|
|
58
|
+
/** Request ID for request/response matching (optional for push messages) */
|
|
59
|
+
request_id?: number;
|
|
60
|
+
/** Message payload (type-specific) */
|
|
61
|
+
payload: unknown;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// SDO Messages (Service Data Object - Read/Write)
|
|
66
|
+
// ============================================================================
|
|
67
|
+
|
|
68
|
+
export type SdoCommand =
|
|
69
|
+
| "upload_request" // Read request (client → server)
|
|
70
|
+
| "upload_response" // Read response (server → client)
|
|
71
|
+
| "download_request" // Write request (client → server)
|
|
72
|
+
| "download_response" // Write response (server → client)
|
|
73
|
+
| "abort"; // Error/abort
|
|
74
|
+
|
|
75
|
+
/** SDO Upload Request - Read a value */
|
|
76
|
+
export interface SdoUploadRequest extends CoreStreamMessage<"sdo"> {
|
|
77
|
+
command: "upload_request";
|
|
78
|
+
request_id: number;
|
|
79
|
+
payload: SymbolAddress;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** SDO Upload Response - Read result */
|
|
83
|
+
export interface SdoUploadResponse extends CoreStreamMessage<"sdo"> {
|
|
84
|
+
command: "upload_response";
|
|
85
|
+
request_id: number;
|
|
86
|
+
payload: {
|
|
87
|
+
/** The address that was read (echoed back) */
|
|
88
|
+
address: SymbolAddress;
|
|
89
|
+
/** The value read */
|
|
90
|
+
value: unknown;
|
|
91
|
+
/** Data type of the value */
|
|
92
|
+
datatype: DataType;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** SDO Download Request - Write a value */
|
|
97
|
+
export interface SdoDownloadRequest extends CoreStreamMessage<"sdo"> {
|
|
98
|
+
command: "download_request";
|
|
99
|
+
request_id: number;
|
|
100
|
+
payload: SymbolAddress & {
|
|
101
|
+
/** The value to write */
|
|
102
|
+
value: unknown;
|
|
103
|
+
/** Optional: data type hint */
|
|
104
|
+
datatype?: DataType;
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** SDO Download Response - Write acknowledgment */
|
|
109
|
+
export interface SdoDownloadResponse extends CoreStreamMessage<"sdo"> {
|
|
110
|
+
command: "download_response";
|
|
111
|
+
request_id: number;
|
|
112
|
+
payload: {
|
|
113
|
+
/** The address that was written (echoed back) */
|
|
114
|
+
address: SymbolAddress;
|
|
115
|
+
/** Whether the write succeeded */
|
|
116
|
+
success: boolean;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** SDO Abort - Error response */
|
|
121
|
+
export interface SdoAbort extends CoreStreamMessage<"sdo"> {
|
|
122
|
+
command: "abort";
|
|
123
|
+
request_id: number;
|
|
124
|
+
payload: {
|
|
125
|
+
/** The address that caused the error */
|
|
126
|
+
address: SymbolAddress;
|
|
127
|
+
/** CANopen-style abort code */
|
|
128
|
+
abort_code: number;
|
|
129
|
+
/** Human-readable error message */
|
|
130
|
+
message: string;
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Standard SDO abort codes (CANopen-compatible) */
|
|
135
|
+
export const SdoAbortCode = {
|
|
136
|
+
TOGGLE_BIT_NOT_ALTERED: 0x05030000,
|
|
137
|
+
SDO_PROTOCOL_TIMEOUT: 0x05040000,
|
|
138
|
+
INVALID_COMMAND: 0x05040001,
|
|
139
|
+
OUT_OF_MEMORY: 0x05040005,
|
|
140
|
+
UNSUPPORTED_ACCESS: 0x06010000,
|
|
141
|
+
WRITE_ONLY: 0x06010001,
|
|
142
|
+
READ_ONLY: 0x06010002,
|
|
143
|
+
OBJECT_NOT_EXIST: 0x06020000,
|
|
144
|
+
SUBINDEX_NOT_EXIST: 0x06090011,
|
|
145
|
+
GENERAL_ERROR: 0x08000000,
|
|
146
|
+
DATA_TRANSFER_ERROR: 0x08000020,
|
|
147
|
+
DATA_LENGTH_MISMATCH: 0x08000022,
|
|
148
|
+
} as const;
|
|
149
|
+
|
|
150
|
+
export type SdoMessage =
|
|
151
|
+
| SdoUploadRequest
|
|
152
|
+
| SdoUploadResponse
|
|
153
|
+
| SdoDownloadRequest
|
|
154
|
+
| SdoDownloadResponse
|
|
155
|
+
| SdoAbort;
|
|
156
|
+
|
|
157
|
+
// ============================================================================
|
|
158
|
+
// Cyclic Messages (Subscriptions and Push Updates)
|
|
159
|
+
// ============================================================================
|
|
160
|
+
|
|
161
|
+
export type CyclicCommand =
|
|
162
|
+
| "subscribe" // Subscribe to updates
|
|
163
|
+
| "subscribe_ack" // Subscribe acknowledgment
|
|
164
|
+
| "unsubscribe" // Unsubscribe
|
|
165
|
+
| "unsubscribe_ack" // Unsubscribe acknowledgment
|
|
166
|
+
| "data_update" // Single value update (push)
|
|
167
|
+
| "data_update_batch"; // Multiple values update (push)
|
|
168
|
+
|
|
169
|
+
/** Subscription options for cyclic updates */
|
|
170
|
+
export interface SubscriptionOptions {
|
|
171
|
+
/** Minimum interval between updates in milliseconds */
|
|
172
|
+
sampling_interval_ms?: number;
|
|
173
|
+
/** Only send updates when value changes */
|
|
174
|
+
on_change_only?: boolean;
|
|
175
|
+
/** Deadband as percentage of full scale (0-100) */
|
|
176
|
+
deadband_percent?: number;
|
|
177
|
+
/** Deadband as absolute value */
|
|
178
|
+
deadband_absolute?: number;
|
|
179
|
+
/** Queue size for buffering (default: 1 = latest only) */
|
|
180
|
+
queue_size?: number;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Subscribe to cyclic updates for a symbol */
|
|
184
|
+
export interface CyclicSubscribe extends CoreStreamMessage<"cyclic"> {
|
|
185
|
+
command: "subscribe";
|
|
186
|
+
request_id: number;
|
|
187
|
+
payload: SymbolAddress & {
|
|
188
|
+
/** Optional subscription options */
|
|
189
|
+
options?: SubscriptionOptions;
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Subscribe acknowledgment */
|
|
194
|
+
export interface CyclicSubscribeAck extends CoreStreamMessage<"cyclic"> {
|
|
195
|
+
command: "subscribe_ack";
|
|
196
|
+
request_id: number;
|
|
197
|
+
payload: {
|
|
198
|
+
/** The address subscribed to */
|
|
199
|
+
address: SymbolAddress;
|
|
200
|
+
/** Whether subscription succeeded */
|
|
201
|
+
success: boolean;
|
|
202
|
+
/** Error message if failed */
|
|
203
|
+
error?: string;
|
|
204
|
+
/** Effective options after server clamping */
|
|
205
|
+
effective_options?: SubscriptionOptions;
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Unsubscribe from cyclic updates */
|
|
210
|
+
export interface CyclicUnsubscribe extends CoreStreamMessage<"cyclic"> {
|
|
211
|
+
command: "unsubscribe";
|
|
212
|
+
request_id: number;
|
|
213
|
+
payload: SymbolAddress;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** Unsubscribe acknowledgment */
|
|
217
|
+
export interface CyclicUnsubscribeAck extends CoreStreamMessage<"cyclic"> {
|
|
218
|
+
command: "unsubscribe_ack";
|
|
219
|
+
request_id: number;
|
|
220
|
+
payload: {
|
|
221
|
+
address: SymbolAddress;
|
|
222
|
+
success: boolean;
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Single value update (pushed from server) */
|
|
227
|
+
export interface CyclicDataUpdate extends CoreStreamMessage<"cyclic"> {
|
|
228
|
+
command: "data_update";
|
|
229
|
+
payload: {
|
|
230
|
+
/** The symbol that changed */
|
|
231
|
+
symbol: string;
|
|
232
|
+
/** The new value */
|
|
233
|
+
value: unknown;
|
|
234
|
+
/** Server timestamp (nanoseconds since epoch, optional) */
|
|
235
|
+
timestamp_ns?: number;
|
|
236
|
+
/** Data type */
|
|
237
|
+
datatype?: DataType;
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** Batch value update (multiple values in one message) */
|
|
242
|
+
export interface CyclicDataUpdateBatch extends CoreStreamMessage<"cyclic"> {
|
|
243
|
+
command: "data_update_batch";
|
|
244
|
+
payload: {
|
|
245
|
+
/** Array of updates */
|
|
246
|
+
updates: Array<{
|
|
247
|
+
symbol: string;
|
|
248
|
+
value: unknown;
|
|
249
|
+
timestamp_ns?: number;
|
|
250
|
+
datatype?: DataType;
|
|
251
|
+
}>;
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export type CyclicMessage =
|
|
256
|
+
| CyclicSubscribe
|
|
257
|
+
| CyclicSubscribeAck
|
|
258
|
+
| CyclicUnsubscribe
|
|
259
|
+
| CyclicUnsubscribeAck
|
|
260
|
+
| CyclicDataUpdate
|
|
261
|
+
| CyclicDataUpdateBatch;
|
|
262
|
+
|
|
263
|
+
// ============================================================================
|
|
264
|
+
// System Messages (Module Lifecycle)
|
|
265
|
+
// ============================================================================
|
|
266
|
+
|
|
267
|
+
export type SystemCommand =
|
|
268
|
+
| "announce" // Module registration
|
|
269
|
+
| "announce_ack" // Registration acknowledgment
|
|
270
|
+
| "discovery" // Request all modules
|
|
271
|
+
| "discovery_response" // Module list
|
|
272
|
+
| "leaving" // Module disconnecting
|
|
273
|
+
| "start" // Start cyclic operation
|
|
274
|
+
| "stop" // Stop cyclic operation
|
|
275
|
+
| "health_request" // Health check
|
|
276
|
+
| "health_response"; // Health status
|
|
277
|
+
|
|
278
|
+
/** Symbol descriptor in module announcement */
|
|
279
|
+
export interface SymbolDescriptor {
|
|
280
|
+
/** Human-readable symbol name (FQDN within module) */
|
|
281
|
+
name: string;
|
|
282
|
+
/** CANopen-style index */
|
|
283
|
+
index: number;
|
|
284
|
+
/** CANopen-style subindex */
|
|
285
|
+
subindex: number;
|
|
286
|
+
/** Data type */
|
|
287
|
+
datatype: DataType;
|
|
288
|
+
/** Access mode */
|
|
289
|
+
access: AccessMode;
|
|
290
|
+
/** Optional description */
|
|
291
|
+
description?: string;
|
|
292
|
+
/** Optional unit (e.g., "mm", "Hz") */
|
|
293
|
+
unit?: string;
|
|
294
|
+
/** Optional min value (for validation) */
|
|
295
|
+
min?: number;
|
|
296
|
+
/** Optional max value (for validation) */
|
|
297
|
+
max?: number;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** Module capabilities */
|
|
301
|
+
export interface ModuleCapabilities {
|
|
302
|
+
/** Supports SDO read */
|
|
303
|
+
sdo_read: boolean;
|
|
304
|
+
/** Supports SDO write */
|
|
305
|
+
sdo_write: boolean;
|
|
306
|
+
/** Supports cyclic subscriptions */
|
|
307
|
+
cyclic: boolean;
|
|
308
|
+
/** Supports batch operations */
|
|
309
|
+
batch: boolean;
|
|
310
|
+
/** Maximum symbols per batch */
|
|
311
|
+
max_batch_size?: number;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** Module announces itself and its symbol catalog */
|
|
315
|
+
export interface SystemAnnounce extends CoreStreamMessage<"system"> {
|
|
316
|
+
command: "announce";
|
|
317
|
+
payload: {
|
|
318
|
+
/** Module name (used as FQDN prefix) */
|
|
319
|
+
module_name: string;
|
|
320
|
+
/** Module version */
|
|
321
|
+
version?: string;
|
|
322
|
+
/** Module capabilities */
|
|
323
|
+
capabilities: ModuleCapabilities;
|
|
324
|
+
/** Symbol catalog */
|
|
325
|
+
symbols: SymbolDescriptor[];
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** Announcement acknowledgment */
|
|
330
|
+
export interface SystemAnnounceAck extends CoreStreamMessage<"system"> {
|
|
331
|
+
command: "announce_ack";
|
|
332
|
+
payload: {
|
|
333
|
+
/** Whether announcement was accepted */
|
|
334
|
+
success: boolean;
|
|
335
|
+
/** Assigned module ID (if applicable) */
|
|
336
|
+
module_id?: number;
|
|
337
|
+
/** Error message if failed */
|
|
338
|
+
error?: string;
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** Request list of all connected modules */
|
|
343
|
+
export interface SystemDiscovery extends CoreStreamMessage<"system"> {
|
|
344
|
+
command: "discovery";
|
|
345
|
+
request_id: number;
|
|
346
|
+
payload: Record<string, never>; // Empty object
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** Discovery response with module list */
|
|
350
|
+
export interface SystemDiscoveryResponse extends CoreStreamMessage<"system"> {
|
|
351
|
+
command: "discovery_response";
|
|
352
|
+
request_id: number;
|
|
353
|
+
payload: {
|
|
354
|
+
modules: Array<{
|
|
355
|
+
module_name: string;
|
|
356
|
+
version?: string;
|
|
357
|
+
capabilities: ModuleCapabilities;
|
|
358
|
+
symbol_count: number;
|
|
359
|
+
state: "running" | "stopped" | "error";
|
|
360
|
+
}>;
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/** Module leaving notification */
|
|
365
|
+
export interface SystemLeaving extends CoreStreamMessage<"system"> {
|
|
366
|
+
command: "leaving";
|
|
367
|
+
payload: {
|
|
368
|
+
module_name: string;
|
|
369
|
+
reason?: string;
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/** Health check request */
|
|
374
|
+
export interface SystemHealthRequest extends CoreStreamMessage<"system"> {
|
|
375
|
+
command: "health_request";
|
|
376
|
+
request_id: number;
|
|
377
|
+
payload: {
|
|
378
|
+
/** Optional: specific module to check */
|
|
379
|
+
module_name?: string;
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** Health check response */
|
|
384
|
+
export interface SystemHealthResponse extends CoreStreamMessage<"system"> {
|
|
385
|
+
command: "health_response";
|
|
386
|
+
request_id: number;
|
|
387
|
+
payload: {
|
|
388
|
+
healthy: boolean;
|
|
389
|
+
modules: Array<{
|
|
390
|
+
module_name: string;
|
|
391
|
+
state: "running" | "stopped" | "error";
|
|
392
|
+
uptime_secs?: number;
|
|
393
|
+
error?: string;
|
|
394
|
+
}>;
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export type SystemMessage =
|
|
399
|
+
| SystemAnnounce
|
|
400
|
+
| SystemAnnounceAck
|
|
401
|
+
| SystemDiscovery
|
|
402
|
+
| SystemDiscoveryResponse
|
|
403
|
+
| SystemLeaving
|
|
404
|
+
| SystemHealthRequest
|
|
405
|
+
| SystemHealthResponse;
|
|
406
|
+
|
|
407
|
+
// ============================================================================
|
|
408
|
+
// RPC Messages (Remote Procedure Calls)
|
|
409
|
+
// ============================================================================
|
|
410
|
+
|
|
411
|
+
export type RpcCommand =
|
|
412
|
+
| "invoke" // Call a function
|
|
413
|
+
| "response"; // Function result
|
|
414
|
+
|
|
415
|
+
/** RPC invoke request - call a function on a module */
|
|
416
|
+
export interface RpcInvoke extends CoreStreamMessage<"rpc"> {
|
|
417
|
+
command: "invoke";
|
|
418
|
+
request_id: number;
|
|
419
|
+
payload: {
|
|
420
|
+
/** Target module name */
|
|
421
|
+
module: string;
|
|
422
|
+
/** Method/function name */
|
|
423
|
+
method: string;
|
|
424
|
+
/** Arguments (JSON object) */
|
|
425
|
+
args?: Record<string, unknown>;
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/** RPC response - function result */
|
|
430
|
+
export interface RpcResponse extends CoreStreamMessage<"rpc"> {
|
|
431
|
+
command: "response";
|
|
432
|
+
request_id: number;
|
|
433
|
+
payload: {
|
|
434
|
+
/** Whether the call succeeded */
|
|
435
|
+
success: boolean;
|
|
436
|
+
/** Return value (if success) */
|
|
437
|
+
data?: unknown;
|
|
438
|
+
/** Error message (if failed) */
|
|
439
|
+
error?: string;
|
|
440
|
+
/** Error code (if failed) */
|
|
441
|
+
error_code?: number;
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export type RpcMessage = RpcInvoke | RpcResponse;
|
|
446
|
+
|
|
447
|
+
// ============================================================================
|
|
448
|
+
// Union Type for All Messages
|
|
449
|
+
// ============================================================================
|
|
450
|
+
|
|
451
|
+
export type AnyCoreSteamMessage =
|
|
452
|
+
| SdoMessage
|
|
453
|
+
| CyclicMessage
|
|
454
|
+
| SystemMessage
|
|
455
|
+
| RpcMessage;
|
|
456
|
+
|
|
457
|
+
// ============================================================================
|
|
458
|
+
// Helper Functions
|
|
459
|
+
// ============================================================================
|
|
460
|
+
|
|
461
|
+
/** Type guard for SDO messages */
|
|
462
|
+
export function isSdoMessage(msg: CoreStreamMessage): msg is SdoMessage {
|
|
463
|
+
return msg.type === "sdo";
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/** Type guard for Cyclic messages */
|
|
467
|
+
export function isCyclicMessage(msg: CoreStreamMessage): msg is CyclicMessage {
|
|
468
|
+
return msg.type === "cyclic";
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/** Type guard for System messages */
|
|
472
|
+
export function isSystemMessage(msg: CoreStreamMessage): msg is SystemMessage {
|
|
473
|
+
return msg.type === "system";
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/** Type guard for RPC messages */
|
|
477
|
+
export function isRpcMessage(msg: CoreStreamMessage): msg is RpcMessage {
|
|
478
|
+
return msg.type === "rpc";
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/** Check if an address uses symbol format */
|
|
482
|
+
export function isSymbolAddress(addr: SymbolAddress): addr is { symbol: string } {
|
|
483
|
+
return "symbol" in addr;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/** Check if an address uses index:subindex format */
|
|
487
|
+
export function isIndexAddress(addr: SymbolAddress): addr is { index: number; subindex: number } {
|
|
488
|
+
return "index" in addr && "subindex" in addr;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/** Convert address to string for display/logging */
|
|
492
|
+
export function addressToString(addr: SymbolAddress): string {
|
|
493
|
+
if (isSymbolAddress(addr)) {
|
|
494
|
+
return addr.symbol;
|
|
495
|
+
} else {
|
|
496
|
+
return `0x${addr.index.toString(16).padStart(4, '0')}:${addr.subindex.toString(16).padStart(2, '0')}`;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/** Parse an address string (either "vfd.frequency" or "0x2000:01") */
|
|
501
|
+
export function parseAddress(str: string): SymbolAddress {
|
|
502
|
+
// Check for hex format: 0xNNNN:NN or NNNN:NN
|
|
503
|
+
const hexMatch = str.match(/^(?:0x)?([0-9a-fA-F]{1,4}):([0-9a-fA-F]{1,2})$/);
|
|
504
|
+
if (hexMatch) {
|
|
505
|
+
return {
|
|
506
|
+
index: parseInt(hexMatch[1], 16),
|
|
507
|
+
subindex: parseInt(hexMatch[2], 16),
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
// Otherwise treat as symbol name
|
|
511
|
+
return { symbol: str };
|
|
512
|
+
}
|