jmri-client 4.2.0-beta.2 → 5.1.0
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/README.md +3 -1
- package/dist/browser/jmri-client.js +88 -28
- package/dist/cjs/index.js +2442 -31
- package/dist/esm/index.js +2393 -17
- package/dist/types/client.d.ts +9 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/managers/roster-manager.d.ts +9 -1
- package/dist/types/mocks/mock-data.d.ts +30 -6
- package/dist/types/mocks/mock-response-manager.d.ts +7 -2
- package/dist/types/types/jmri-messages.d.ts +22 -0
- package/docs/API.md +8 -0
- package/docs/BROWSER.md +4 -4
- package/docs/MIGRATION.md +30 -1
- package/docs/MOCK_MODE.md +15 -9
- package/package.json +17 -18
- package/dist/cjs/client.js +0 -366
- package/dist/cjs/core/connection-state-manager.js +0 -84
- package/dist/cjs/core/heartbeat-manager.js +0 -79
- package/dist/cjs/core/index.js +0 -25
- package/dist/cjs/core/message-queue.js +0 -59
- package/dist/cjs/core/reconnection-manager.js +0 -97
- package/dist/cjs/core/websocket-adapter.js +0 -135
- package/dist/cjs/core/websocket-client.js +0 -388
- package/dist/cjs/managers/index.js +0 -25
- package/dist/cjs/managers/light-manager.js +0 -111
- package/dist/cjs/managers/power-manager.js +0 -90
- package/dist/cjs/managers/roster-manager.js +0 -118
- package/dist/cjs/managers/system-connections-manager.js +0 -28
- package/dist/cjs/managers/throttle-manager.js +0 -233
- package/dist/cjs/managers/turnout-manager.js +0 -111
- package/dist/cjs/mocks/index.js +0 -12
- package/dist/cjs/mocks/mock-data.js +0 -237
- package/dist/cjs/mocks/mock-response-manager.js +0 -290
- package/dist/cjs/types/client-options.js +0 -66
- package/dist/cjs/types/events.js +0 -16
- package/dist/cjs/types/index.js +0 -23
- package/dist/cjs/types/jmri-messages.js +0 -95
- package/dist/cjs/types/throttle.js +0 -19
- package/dist/cjs/utils/exponential-backoff.js +0 -40
- package/dist/cjs/utils/index.js +0 -21
- package/dist/cjs/utils/message-id.js +0 -40
- package/dist/esm/client.js +0 -362
- package/dist/esm/core/connection-state-manager.js +0 -80
- package/dist/esm/core/heartbeat-manager.js +0 -75
- package/dist/esm/core/index.js +0 -9
- package/dist/esm/core/message-queue.js +0 -55
- package/dist/esm/core/reconnection-manager.js +0 -93
- package/dist/esm/core/websocket-adapter.js +0 -98
- package/dist/esm/core/websocket-client.js +0 -384
- package/dist/esm/managers/index.js +0 -9
- package/dist/esm/managers/light-manager.js +0 -107
- package/dist/esm/managers/power-manager.js +0 -86
- package/dist/esm/managers/roster-manager.js +0 -114
- package/dist/esm/managers/system-connections-manager.js +0 -24
- package/dist/esm/managers/throttle-manager.js +0 -229
- package/dist/esm/managers/turnout-manager.js +0 -107
- package/dist/esm/mocks/index.js +0 -6
- package/dist/esm/mocks/mock-data.js +0 -234
- package/dist/esm/mocks/mock-response-manager.js +0 -286
- package/dist/esm/types/client-options.js +0 -62
- package/dist/esm/types/events.js +0 -13
- package/dist/esm/types/index.js +0 -7
- package/dist/esm/types/jmri-messages.js +0 -89
- package/dist/esm/types/throttle.js +0 -15
- package/dist/esm/utils/exponential-backoff.js +0 -36
- package/dist/esm/utils/index.js +0 -5
- package/dist/esm/utils/message-id.js +0 -36
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mock Response Manager
|
|
3
|
-
* Generates mock JMRI responses for testing and demo purposes
|
|
4
|
-
*/
|
|
5
|
-
import { PowerState, TurnoutState, LightState } from '../types/jmri-messages.js';
|
|
6
|
-
import { mockData } from './mock-data.js';
|
|
7
|
-
/**
|
|
8
|
-
* Manages mock responses for JMRI protocol
|
|
9
|
-
*/
|
|
10
|
-
export class MockResponseManager {
|
|
11
|
-
constructor(options = {}) {
|
|
12
|
-
this.throttles = new Map();
|
|
13
|
-
this.lights = new Map([
|
|
14
|
-
['IL1', LightState.OFF],
|
|
15
|
-
['IL2', LightState.OFF],
|
|
16
|
-
['IL3', LightState.ON]
|
|
17
|
-
]);
|
|
18
|
-
this.turnouts = new Map([
|
|
19
|
-
['LT1', TurnoutState.CLOSED],
|
|
20
|
-
['LT2', TurnoutState.CLOSED],
|
|
21
|
-
['LT3', TurnoutState.THROWN]
|
|
22
|
-
]);
|
|
23
|
-
this.responseDelay = options.responseDelay ?? 50;
|
|
24
|
-
this.powerState = options.initialPowerState ?? PowerState.OFF;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Get a mock response for a given message
|
|
28
|
-
*/
|
|
29
|
-
async getMockResponse(message) {
|
|
30
|
-
// Simulate network delay
|
|
31
|
-
if (this.responseDelay > 0) {
|
|
32
|
-
await this.delay(this.responseDelay);
|
|
33
|
-
}
|
|
34
|
-
// Route to appropriate handler based on message type
|
|
35
|
-
switch (message.type) {
|
|
36
|
-
case 'hello':
|
|
37
|
-
return this.getHelloResponse();
|
|
38
|
-
case 'power':
|
|
39
|
-
return this.getPowerResponse(message);
|
|
40
|
-
case 'roster':
|
|
41
|
-
return this.getRosterResponse(message);
|
|
42
|
-
case 'throttle':
|
|
43
|
-
return this.getThrottleResponse(message);
|
|
44
|
-
case 'light':
|
|
45
|
-
return this.getLightResponse(message);
|
|
46
|
-
case 'turnout':
|
|
47
|
-
return this.getTurnoutResponse(message);
|
|
48
|
-
case 'ping':
|
|
49
|
-
return this.getPingResponse();
|
|
50
|
-
case 'goodbye':
|
|
51
|
-
return this.getGoodbyeResponse();
|
|
52
|
-
default:
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Get hello response (connection establishment)
|
|
58
|
-
*/
|
|
59
|
-
getHelloResponse() {
|
|
60
|
-
return JSON.parse(JSON.stringify(mockData.hello));
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Get power response
|
|
64
|
-
*/
|
|
65
|
-
getPowerResponse(message) {
|
|
66
|
-
// Handle power state change
|
|
67
|
-
if (message.data?.state !== undefined) {
|
|
68
|
-
this.powerState = message.data.state;
|
|
69
|
-
return {
|
|
70
|
-
type: 'power',
|
|
71
|
-
data: { state: this.powerState }
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
// Return current power state
|
|
75
|
-
return {
|
|
76
|
-
type: 'power',
|
|
77
|
-
data: { state: this.powerState }
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Get roster response
|
|
82
|
-
*/
|
|
83
|
-
getRosterResponse(message) {
|
|
84
|
-
if (message.type === 'roster' && message.method === 'list') {
|
|
85
|
-
return {
|
|
86
|
-
type: 'roster',
|
|
87
|
-
data: JSON.parse(JSON.stringify(mockData.roster.list))
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
return {
|
|
91
|
-
type: 'roster',
|
|
92
|
-
data: []
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Get throttle response
|
|
97
|
-
*/
|
|
98
|
-
getThrottleResponse(message) {
|
|
99
|
-
const data = message.data || {};
|
|
100
|
-
// Acquire throttle
|
|
101
|
-
if (data.address !== undefined && !data.throttle) {
|
|
102
|
-
const throttleId = data.name || `MOCK-${data.address}`;
|
|
103
|
-
const throttleState = {
|
|
104
|
-
throttle: throttleId,
|
|
105
|
-
address: data.address,
|
|
106
|
-
speed: 0,
|
|
107
|
-
forward: true,
|
|
108
|
-
F0: false,
|
|
109
|
-
F1: false,
|
|
110
|
-
F2: false,
|
|
111
|
-
F3: false,
|
|
112
|
-
F4: false
|
|
113
|
-
};
|
|
114
|
-
this.throttles.set(throttleId, throttleState);
|
|
115
|
-
return {
|
|
116
|
-
type: 'throttle',
|
|
117
|
-
data: { ...throttleState }
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
// Release throttle
|
|
121
|
-
if (data.release !== undefined && data.throttle) {
|
|
122
|
-
this.throttles.delete(data.throttle);
|
|
123
|
-
return {
|
|
124
|
-
type: 'throttle',
|
|
125
|
-
data: {}
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
// Throttle control (speed, direction, functions)
|
|
129
|
-
if (data.throttle) {
|
|
130
|
-
const throttleState = this.throttles.get(data.throttle);
|
|
131
|
-
if (!throttleState) {
|
|
132
|
-
// Throttle not found - this shouldn't normally happen in mock mode
|
|
133
|
-
// but we'll create it on the fly
|
|
134
|
-
const newState = {
|
|
135
|
-
throttle: data.throttle,
|
|
136
|
-
address: 0,
|
|
137
|
-
speed: 0,
|
|
138
|
-
forward: true
|
|
139
|
-
};
|
|
140
|
-
this.throttles.set(data.throttle, newState);
|
|
141
|
-
return {
|
|
142
|
-
type: 'throttle',
|
|
143
|
-
data: { ...newState }
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
// Update throttle state
|
|
147
|
-
if (data.speed !== undefined) {
|
|
148
|
-
throttleState.speed = data.speed;
|
|
149
|
-
}
|
|
150
|
-
if (data.forward !== undefined) {
|
|
151
|
-
throttleState.forward = data.forward;
|
|
152
|
-
}
|
|
153
|
-
// Update function keys
|
|
154
|
-
for (let i = 0; i <= 28; i++) {
|
|
155
|
-
const key = `F${i}`;
|
|
156
|
-
if (data[key] !== undefined) {
|
|
157
|
-
throttleState[key] = data[key];
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
// Return updated state (no response for throttle control commands)
|
|
161
|
-
return {
|
|
162
|
-
type: 'throttle',
|
|
163
|
-
data: {}
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
return {
|
|
167
|
-
type: 'throttle',
|
|
168
|
-
data: {}
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Get light response
|
|
173
|
-
*/
|
|
174
|
-
getLightResponse(message) {
|
|
175
|
-
// List all lights
|
|
176
|
-
if (message.method === 'list') {
|
|
177
|
-
return {
|
|
178
|
-
type: 'light',
|
|
179
|
-
data: JSON.parse(JSON.stringify(mockData.light.list))
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
const name = message.data?.name;
|
|
183
|
-
if (!name) {
|
|
184
|
-
return { type: 'light', data: { name: '', state: LightState.UNKNOWN } };
|
|
185
|
-
}
|
|
186
|
-
// Set light state
|
|
187
|
-
if (message.method === 'post' && message.data?.state !== undefined) {
|
|
188
|
-
this.lights.set(name, message.data.state);
|
|
189
|
-
}
|
|
190
|
-
// Get or confirm current state
|
|
191
|
-
const state = this.lights.get(name) ?? LightState.UNKNOWN;
|
|
192
|
-
return { type: 'light', data: { name, state } };
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* Get turnout response
|
|
196
|
-
*/
|
|
197
|
-
getTurnoutResponse(message) {
|
|
198
|
-
// List all turnouts
|
|
199
|
-
if (message.method === 'list') {
|
|
200
|
-
return {
|
|
201
|
-
type: 'turnout',
|
|
202
|
-
data: JSON.parse(JSON.stringify(mockData.turnout.list))
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
const name = message.data?.name;
|
|
206
|
-
if (!name) {
|
|
207
|
-
return { type: 'turnout', data: { name: '', state: TurnoutState.UNKNOWN } };
|
|
208
|
-
}
|
|
209
|
-
// Set turnout state
|
|
210
|
-
if (message.method === 'post' && message.data?.state !== undefined) {
|
|
211
|
-
this.turnouts.set(name, message.data.state);
|
|
212
|
-
}
|
|
213
|
-
// Get or confirm current state
|
|
214
|
-
const state = this.turnouts.get(name) ?? TurnoutState.UNKNOWN;
|
|
215
|
-
return { type: 'turnout', data: { name, state } };
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* Get ping response (pong)
|
|
219
|
-
*/
|
|
220
|
-
getPingResponse() {
|
|
221
|
-
return JSON.parse(JSON.stringify(mockData.pong));
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Get goodbye response
|
|
225
|
-
*/
|
|
226
|
-
getGoodbyeResponse() {
|
|
227
|
-
return JSON.parse(JSON.stringify(mockData.goodbye));
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Get current power state
|
|
231
|
-
*/
|
|
232
|
-
getPowerState() {
|
|
233
|
-
return this.powerState;
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Set power state (for testing)
|
|
237
|
-
*/
|
|
238
|
-
setPowerState(state) {
|
|
239
|
-
this.powerState = state;
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Get all throttles (for testing)
|
|
243
|
-
*/
|
|
244
|
-
getThrottles() {
|
|
245
|
-
return this.throttles;
|
|
246
|
-
}
|
|
247
|
-
/**
|
|
248
|
-
* Get all light states (for testing)
|
|
249
|
-
*/
|
|
250
|
-
getLights() {
|
|
251
|
-
return this.lights;
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Get all turnout states (for testing)
|
|
255
|
-
*/
|
|
256
|
-
getTurnouts() {
|
|
257
|
-
return this.turnouts;
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Reset all state (for testing)
|
|
261
|
-
*/
|
|
262
|
-
reset() {
|
|
263
|
-
this.powerState = PowerState.OFF;
|
|
264
|
-
this.throttles.clear();
|
|
265
|
-
this.lights = new Map([
|
|
266
|
-
['IL1', LightState.OFF],
|
|
267
|
-
['IL2', LightState.OFF],
|
|
268
|
-
['IL3', LightState.ON]
|
|
269
|
-
]);
|
|
270
|
-
this.turnouts = new Map([
|
|
271
|
-
['LT1', TurnoutState.CLOSED],
|
|
272
|
-
['LT2', TurnoutState.CLOSED],
|
|
273
|
-
['LT3', TurnoutState.THROWN]
|
|
274
|
-
]);
|
|
275
|
-
}
|
|
276
|
-
/**
|
|
277
|
-
* Delay helper
|
|
278
|
-
*/
|
|
279
|
-
delay(ms) {
|
|
280
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
/**
|
|
284
|
-
* Singleton instance for shared use across tests
|
|
285
|
-
*/
|
|
286
|
-
export const mockResponseManager = new MockResponseManager({ responseDelay: 0 });
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Client configuration options
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Default client options
|
|
6
|
-
*/
|
|
7
|
-
export const DEFAULT_CLIENT_OPTIONS = {
|
|
8
|
-
host: 'localhost',
|
|
9
|
-
port: 12080,
|
|
10
|
-
protocol: 'ws',
|
|
11
|
-
autoConnect: true,
|
|
12
|
-
reconnection: {
|
|
13
|
-
enabled: true,
|
|
14
|
-
maxAttempts: 0, // infinite
|
|
15
|
-
initialDelay: 1000,
|
|
16
|
-
maxDelay: 30000,
|
|
17
|
-
multiplier: 1.5,
|
|
18
|
-
jitter: true
|
|
19
|
-
},
|
|
20
|
-
heartbeat: {
|
|
21
|
-
enabled: true,
|
|
22
|
-
interval: 30000,
|
|
23
|
-
timeout: 5000
|
|
24
|
-
},
|
|
25
|
-
messageQueueSize: 100,
|
|
26
|
-
requestTimeout: 10000,
|
|
27
|
-
mock: {
|
|
28
|
-
enabled: false,
|
|
29
|
-
responseDelay: 50
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
/**
|
|
33
|
-
* Merge user options with defaults
|
|
34
|
-
*/
|
|
35
|
-
export function mergeOptions(userOptions) {
|
|
36
|
-
const options = { ...DEFAULT_CLIENT_OPTIONS };
|
|
37
|
-
if (!userOptions) {
|
|
38
|
-
return options;
|
|
39
|
-
}
|
|
40
|
-
if (userOptions.host !== undefined)
|
|
41
|
-
options.host = userOptions.host;
|
|
42
|
-
if (userOptions.port !== undefined)
|
|
43
|
-
options.port = userOptions.port;
|
|
44
|
-
if (userOptions.protocol !== undefined)
|
|
45
|
-
options.protocol = userOptions.protocol;
|
|
46
|
-
if (userOptions.autoConnect !== undefined)
|
|
47
|
-
options.autoConnect = userOptions.autoConnect;
|
|
48
|
-
if (userOptions.messageQueueSize !== undefined)
|
|
49
|
-
options.messageQueueSize = userOptions.messageQueueSize;
|
|
50
|
-
if (userOptions.requestTimeout !== undefined)
|
|
51
|
-
options.requestTimeout = userOptions.requestTimeout;
|
|
52
|
-
if (userOptions.reconnection) {
|
|
53
|
-
options.reconnection = { ...DEFAULT_CLIENT_OPTIONS.reconnection, ...userOptions.reconnection };
|
|
54
|
-
}
|
|
55
|
-
if (userOptions.heartbeat) {
|
|
56
|
-
options.heartbeat = { ...DEFAULT_CLIENT_OPTIONS.heartbeat, ...userOptions.heartbeat };
|
|
57
|
-
}
|
|
58
|
-
if (userOptions.mock) {
|
|
59
|
-
options.mock = { ...DEFAULT_CLIENT_OPTIONS.mock, ...userOptions.mock };
|
|
60
|
-
}
|
|
61
|
-
return options;
|
|
62
|
-
}
|
package/dist/esm/types/events.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Event types for JmriClient EventEmitter
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Connection states
|
|
6
|
-
*/
|
|
7
|
-
export var ConnectionState;
|
|
8
|
-
(function (ConnectionState) {
|
|
9
|
-
ConnectionState["DISCONNECTED"] = "disconnected";
|
|
10
|
-
ConnectionState["CONNECTING"] = "connecting";
|
|
11
|
-
ConnectionState["CONNECTED"] = "connected";
|
|
12
|
-
ConnectionState["RECONNECTING"] = "reconnecting";
|
|
13
|
-
})(ConnectionState || (ConnectionState = {}));
|
package/dist/esm/types/index.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JMRI WebSocket Protocol Message Types
|
|
3
|
-
* Based on JMRI JSON protocol specification
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* Power state values (from JMRI JSON protocol constants)
|
|
7
|
-
* UNKNOWN = 0 (state cannot be determined)
|
|
8
|
-
* ON = 2 (power is on)
|
|
9
|
-
* OFF = 4 (power is off)
|
|
10
|
-
*/
|
|
11
|
-
export var PowerState;
|
|
12
|
-
(function (PowerState) {
|
|
13
|
-
PowerState[PowerState["UNKNOWN"] = 0] = "UNKNOWN";
|
|
14
|
-
PowerState[PowerState["ON"] = 2] = "ON";
|
|
15
|
-
PowerState[PowerState["OFF"] = 4] = "OFF";
|
|
16
|
-
})(PowerState || (PowerState = {}));
|
|
17
|
-
/**
|
|
18
|
-
* Convert PowerState enum to human-readable string
|
|
19
|
-
* @param state - The power state
|
|
20
|
-
* @returns 'ON', 'OFF', or 'UNKNOWN'
|
|
21
|
-
*/
|
|
22
|
-
export function powerStateToString(state) {
|
|
23
|
-
switch (state) {
|
|
24
|
-
case PowerState.ON:
|
|
25
|
-
return 'ON';
|
|
26
|
-
case PowerState.OFF:
|
|
27
|
-
return 'OFF';
|
|
28
|
-
case PowerState.UNKNOWN:
|
|
29
|
-
return 'UNKNOWN';
|
|
30
|
-
default:
|
|
31
|
-
return 'UNKNOWN';
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Turnout state values (from JMRI JSON protocol constants)
|
|
36
|
-
* UNKNOWN = 0 (state cannot be determined)
|
|
37
|
-
* CLOSED = 2 (straight through / normal position)
|
|
38
|
-
* THROWN = 4 (diverging route position)
|
|
39
|
-
* INCONSISTENT = 8 (contradictory feedback state)
|
|
40
|
-
*/
|
|
41
|
-
export var TurnoutState;
|
|
42
|
-
(function (TurnoutState) {
|
|
43
|
-
TurnoutState[TurnoutState["UNKNOWN"] = 0] = "UNKNOWN";
|
|
44
|
-
TurnoutState[TurnoutState["CLOSED"] = 2] = "CLOSED";
|
|
45
|
-
TurnoutState[TurnoutState["THROWN"] = 4] = "THROWN";
|
|
46
|
-
TurnoutState[TurnoutState["INCONSISTENT"] = 8] = "INCONSISTENT";
|
|
47
|
-
})(TurnoutState || (TurnoutState = {}));
|
|
48
|
-
/**
|
|
49
|
-
* Convert TurnoutState enum to human-readable string
|
|
50
|
-
*/
|
|
51
|
-
export function turnoutStateToString(state) {
|
|
52
|
-
switch (state) {
|
|
53
|
-
case TurnoutState.CLOSED:
|
|
54
|
-
return 'CLOSED';
|
|
55
|
-
case TurnoutState.THROWN:
|
|
56
|
-
return 'THROWN';
|
|
57
|
-
case TurnoutState.INCONSISTENT:
|
|
58
|
-
return 'INCONSISTENT';
|
|
59
|
-
case TurnoutState.UNKNOWN:
|
|
60
|
-
default:
|
|
61
|
-
return 'UNKNOWN';
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Light state values (from JMRI JSON protocol constants)
|
|
66
|
-
* UNKNOWN = 0 (state cannot be determined)
|
|
67
|
-
* ON = 2 (light is on)
|
|
68
|
-
* OFF = 4 (light is off)
|
|
69
|
-
*/
|
|
70
|
-
export var LightState;
|
|
71
|
-
(function (LightState) {
|
|
72
|
-
LightState[LightState["UNKNOWN"] = 0] = "UNKNOWN";
|
|
73
|
-
LightState[LightState["ON"] = 2] = "ON";
|
|
74
|
-
LightState[LightState["OFF"] = 4] = "OFF";
|
|
75
|
-
})(LightState || (LightState = {}));
|
|
76
|
-
/**
|
|
77
|
-
* Convert LightState enum to human-readable string
|
|
78
|
-
*/
|
|
79
|
-
export function lightStateToString(state) {
|
|
80
|
-
switch (state) {
|
|
81
|
-
case LightState.ON:
|
|
82
|
-
return 'ON';
|
|
83
|
-
case LightState.OFF:
|
|
84
|
-
return 'OFF';
|
|
85
|
-
case LightState.UNKNOWN:
|
|
86
|
-
default:
|
|
87
|
-
return 'UNKNOWN';
|
|
88
|
-
}
|
|
89
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Throttle-specific types
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Validates that a value is a valid throttle function key
|
|
6
|
-
*/
|
|
7
|
-
export function isThrottleFunctionKey(key) {
|
|
8
|
-
return /^F([0-9]|1[0-9]|2[0-8])$/.test(key);
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Validates that speed is in valid range (0.0 to 1.0)
|
|
12
|
-
*/
|
|
13
|
-
export function isValidSpeed(speed) {
|
|
14
|
-
return typeof speed === 'number' && speed >= 0 && speed <= 1;
|
|
15
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Exponential backoff calculator with jitter
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Calculate next reconnection delay using exponential backoff
|
|
6
|
-
*
|
|
7
|
-
* @param attempt - Current attempt number (1-indexed)
|
|
8
|
-
* @param options - Reconnection options
|
|
9
|
-
* @returns Delay in milliseconds
|
|
10
|
-
*/
|
|
11
|
-
export function calculateBackoffDelay(attempt, options) {
|
|
12
|
-
// Base delay calculation: initialDelay * (multiplier ^ (attempt - 1))
|
|
13
|
-
const exponentialDelay = options.initialDelay * Math.pow(options.multiplier, attempt - 1);
|
|
14
|
-
// Cap at maxDelay
|
|
15
|
-
const cappedDelay = Math.min(exponentialDelay, options.maxDelay);
|
|
16
|
-
// Add jitter if enabled (±25%)
|
|
17
|
-
if (options.jitter) {
|
|
18
|
-
const jitterAmount = cappedDelay * 0.25;
|
|
19
|
-
const jitter = (Math.random() * 2 - 1) * jitterAmount; // Random between -25% and +25%
|
|
20
|
-
return Math.max(0, Math.round(cappedDelay + jitter));
|
|
21
|
-
}
|
|
22
|
-
return Math.round(cappedDelay);
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Check if should attempt reconnection
|
|
26
|
-
*
|
|
27
|
-
* @param attempt - Current attempt number (1-indexed)
|
|
28
|
-
* @param maxAttempts - Maximum attempts (0 = infinite)
|
|
29
|
-
* @returns True if should attempt reconnection
|
|
30
|
-
*/
|
|
31
|
-
export function shouldReconnect(attempt, maxAttempts) {
|
|
32
|
-
if (maxAttempts === 0) {
|
|
33
|
-
return true; // Infinite attempts
|
|
34
|
-
}
|
|
35
|
-
return attempt <= maxAttempts;
|
|
36
|
-
}
|
package/dist/esm/utils/index.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Message ID generator for request/response correlation
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Sequential message ID generator
|
|
6
|
-
* Provides unique IDs for correlating requests and responses
|
|
7
|
-
*/
|
|
8
|
-
export class MessageIdGenerator {
|
|
9
|
-
constructor() {
|
|
10
|
-
this.currentId = 0;
|
|
11
|
-
this.maxId = Number.MAX_SAFE_INTEGER;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Generate next sequential ID
|
|
15
|
-
* Wraps around at MAX_SAFE_INTEGER
|
|
16
|
-
*/
|
|
17
|
-
next() {
|
|
18
|
-
this.currentId++;
|
|
19
|
-
if (this.currentId >= this.maxId) {
|
|
20
|
-
this.currentId = 1;
|
|
21
|
-
}
|
|
22
|
-
return this.currentId;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Reset ID counter to 0
|
|
26
|
-
*/
|
|
27
|
-
reset() {
|
|
28
|
-
this.currentId = 0;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Get current ID without incrementing
|
|
32
|
-
*/
|
|
33
|
-
current() {
|
|
34
|
-
return this.currentId;
|
|
35
|
-
}
|
|
36
|
-
}
|