@robdobsn/raftjs 1.6.5 → 1.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/react-native/RaftChannel.d.ts +2 -0
  2. package/dist/react-native/RaftChannelBLE.native.d.ts +2 -0
  3. package/dist/react-native/RaftChannelBLE.native.js +10 -0
  4. package/dist/react-native/RaftChannelBLE.native.js.map +1 -1
  5. package/dist/react-native/RaftChannelBLE.web.d.ts +2 -0
  6. package/dist/react-native/RaftChannelBLE.web.js +10 -0
  7. package/dist/react-native/RaftChannelBLE.web.js.map +1 -1
  8. package/dist/react-native/RaftChannelSimulated.d.ts +32 -0
  9. package/dist/react-native/RaftChannelSimulated.js +418 -0
  10. package/dist/react-native/RaftChannelSimulated.js.map +1 -0
  11. package/dist/react-native/RaftChannelWebSerial.d.ts +2 -0
  12. package/dist/react-native/RaftChannelWebSerial.js +10 -0
  13. package/dist/react-native/RaftChannelWebSerial.js.map +1 -1
  14. package/dist/react-native/RaftChannelWebSocket.d.ts +2 -0
  15. package/dist/react-native/RaftChannelWebSocket.js +10 -0
  16. package/dist/react-native/RaftChannelWebSocket.js.map +1 -1
  17. package/dist/react-native/RaftConnector.js +5 -0
  18. package/dist/react-native/RaftConnector.js.map +1 -1
  19. package/dist/react-native/RaftDeviceInfo.d.ts +1 -1
  20. package/dist/react-native/RaftDeviceManager.js +1 -1
  21. package/dist/react-native/RaftDeviceManager.js.map +1 -1
  22. package/dist/react-native/RaftMsgHandler.d.ts +5 -0
  23. package/dist/react-native/RaftMsgHandler.js +32 -0
  24. package/dist/react-native/RaftMsgHandler.js.map +1 -1
  25. package/dist/react-native/RaftStruct.js +1 -1
  26. package/dist/react-native/RaftStruct.js.map +1 -1
  27. package/dist/react-native/main.d.ts +2 -0
  28. package/dist/react-native/main.js +4 -1
  29. package/dist/react-native/main.js.map +1 -1
  30. package/dist/web/RaftChannel.d.ts +2 -0
  31. package/dist/web/RaftChannelBLE.web.d.ts +2 -0
  32. package/dist/web/RaftChannelBLE.web.js +10 -0
  33. package/dist/web/RaftChannelBLE.web.js.map +1 -1
  34. package/dist/web/RaftChannelSimulated.d.ts +32 -0
  35. package/dist/web/RaftChannelSimulated.js +418 -0
  36. package/dist/web/RaftChannelSimulated.js.map +1 -0
  37. package/dist/web/RaftChannelWebSerial.d.ts +2 -0
  38. package/dist/web/RaftChannelWebSerial.js +10 -0
  39. package/dist/web/RaftChannelWebSerial.js.map +1 -1
  40. package/dist/web/RaftChannelWebSocket.d.ts +2 -0
  41. package/dist/web/RaftChannelWebSocket.js +10 -0
  42. package/dist/web/RaftChannelWebSocket.js.map +1 -1
  43. package/dist/web/RaftConnector.js +5 -0
  44. package/dist/web/RaftConnector.js.map +1 -1
  45. package/dist/web/RaftDeviceInfo.d.ts +1 -1
  46. package/dist/web/RaftDeviceManager.js +1 -1
  47. package/dist/web/RaftDeviceManager.js.map +1 -1
  48. package/dist/web/RaftMsgHandler.d.ts +5 -0
  49. package/dist/web/RaftMsgHandler.js +32 -0
  50. package/dist/web/RaftMsgHandler.js.map +1 -1
  51. package/dist/web/RaftStruct.js +1 -1
  52. package/dist/web/RaftStruct.js.map +1 -1
  53. package/dist/web/main.d.ts +2 -0
  54. package/dist/web/main.js +4 -1
  55. package/dist/web/main.js.map +1 -1
  56. package/package.json +1 -1
  57. package/src/RaftChannel.ts +2 -0
  58. package/src/RaftChannelBLE.native.ts +12 -0
  59. package/src/RaftChannelBLE.web.ts +13 -0
  60. package/src/RaftChannelSimulated.ts +482 -0
  61. package/src/RaftChannelWebSerial.ts +12 -0
  62. package/src/RaftChannelWebSocket.ts +13 -0
  63. package/src/RaftConnector.ts +4 -0
  64. package/src/RaftDeviceInfo.ts +1 -1
  65. package/src/RaftDeviceManager.ts +1 -1
  66. package/src/RaftMsgHandler.ts +36 -0
  67. package/src/RaftStruct.ts +1 -1
  68. package/src/main.ts +2 -0
@@ -0,0 +1,482 @@
1
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2
+ //
3
+ // RaftChannelSimulated.ts
4
+ // Part of RaftJS
5
+ //
6
+ // Rob Dobson 2020-2025
7
+ // (C) 2020-2025 All rights reserved
8
+ //
9
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
10
+
11
+ import RaftChannel from "./RaftChannel";
12
+ import RaftMsgHandler from "./RaftMsgHandler";
13
+ import RaftLog from "./RaftLog";
14
+ import { RaftConnEvent, RaftConnEventFn } from "./RaftConnEvents";
15
+ import { RaftCommsMsgTypeCode } from './RaftProtocolDefs';
16
+ import { ConnectorOptions } from "./RaftSystemType";
17
+ import { DeviceTypeInfoRecs, DeviceTypeInfo } from "./RaftDeviceInfo";
18
+
19
+ interface SimulatedDeviceInfo {
20
+ name: string;
21
+ publishRatePerSecond: number;
22
+ }
23
+
24
+ export default class RaftChannelSimulated implements RaftChannel {
25
+
26
+ // Message handler
27
+ private _raftMsgHandler: RaftMsgHandler | null = null;
28
+
29
+ // Is connected
30
+ private _isConnected = false;
31
+
32
+ // Simulated device name and rate
33
+ private _simulatedDeviceInfo: Array<SimulatedDeviceInfo> | null = null;
34
+
35
+ // Simulated device information timer
36
+ private _simulatedDeviceInfoTimers: Array<NodeJS.Timeout> | null = null;
37
+ private _simulatedDeviceInfoTimeMs: Array<number> = [];
38
+
39
+ // Conn event fn
40
+ private _onConnEvent: RaftConnEventFn | null = null;
41
+
42
+ // File Handler parameters
43
+ private _requestedBatchAckSize = 10;
44
+ private _requestedFileBlockSize = 500;
45
+
46
+ fhBatchAckSize(): number { return this._requestedBatchAckSize; }
47
+ fhFileBlockSize(): number { return this._requestedFileBlockSize; }
48
+
49
+ // isConnected
50
+ isConnected(): boolean {
51
+ return this._isConnected;
52
+ }
53
+
54
+ // Set message handler
55
+ setMsgHandler(raftMsgHandler: RaftMsgHandler): void {
56
+ this._raftMsgHandler = raftMsgHandler;
57
+ this._raftMsgHandler.setRawMsgMode(true);
58
+ }
59
+
60
+ // WebSocket interfaces require subscription to published messages
61
+ requiresSubscription(): boolean {
62
+ return true;
63
+ }
64
+
65
+ // RICREST command before disconnect
66
+ ricRestCmdBeforeDisconnect(): string | null {
67
+ return null;
68
+ }
69
+
70
+ // Set onConnEvent handler
71
+ setOnConnEvent(connEventFn: RaftConnEventFn): void {
72
+ this._onConnEvent = connEventFn;
73
+ }
74
+
75
+ // Get connected locator
76
+ getConnectedLocator(): string | object {
77
+ return "simulated";
78
+ }
79
+
80
+ // Connect to a device
81
+ async connect(locator: string | object, connectorOptions: ConnectorOptions): Promise<boolean> {
82
+
83
+ // Debug
84
+ RaftLog.debug(`RaftChannelSimulated.connect connected ${locator.toString()} options ${JSON.stringify(connectorOptions)}`);
85
+
86
+ // Extract SimulatedDeviceInfo from JSON locator
87
+ if (typeof locator === 'string') {
88
+ try {
89
+ const parsedLocator = JSON.parse(locator);
90
+ if (parsedLocator && Array.isArray(parsedLocator)) {
91
+ this._simulatedDeviceInfo = parsedLocator;
92
+ }
93
+ } catch (e) {
94
+ RaftLog.error(`RaftChannelSimulated.connect - error parsing locator ${locator}`);
95
+ return false;
96
+ }
97
+ }
98
+
99
+ // Handle simulated devices
100
+ if (this._simulatedDeviceInfo) {
101
+ // Create timers for simulated devices
102
+ this._simulatedDeviceInfoTimers = [];
103
+ for (let i = 0; i < this._simulatedDeviceInfo.length; i++) {
104
+ const deviceInfo = this._simulatedDeviceInfo[i];
105
+ const deviceName = deviceInfo.name ? deviceInfo.name : `SimulatedDevice${i}`;
106
+ this._simulatedDeviceInfoTimeMs.push(0);
107
+ const deviceRate = deviceInfo.publishRatePerSecond ? deviceInfo.publishRatePerSecond : 1;
108
+ let deviceIntervalMs = 1000;
109
+ if ((deviceRate > 0.01) && (deviceRate < 1000)) {
110
+ deviceIntervalMs = Math.floor(1000 / deviceRate);
111
+ }
112
+ const deviceTypeInfo = this._deviceTypeInfo[deviceName];
113
+ if (deviceTypeInfo) {
114
+ const timer = setInterval(() => {
115
+ const msg = this._createSimulatedDeviceInfoMsg(
116
+ deviceIntervalMs,
117
+ deviceName,
118
+ deviceTypeInfo,
119
+ this._simulatedDeviceInfoTimeMs[i]
120
+ );
121
+ this._raftMsgHandler?.handleNewRxMsgRaw(msg, RaftCommsMsgTypeCode.MSG_TYPE_PUBLISH, 0, this._simulatedDeviceInfoTimeMs[i]);
122
+ this._simulatedDeviceInfoTimeMs[i] += deviceIntervalMs;
123
+ }, deviceIntervalMs);
124
+ this._simulatedDeviceInfoTimers.push(timer);
125
+ } else {
126
+ RaftLog.error(`RaftChannelSimulated.connect - device type info not found for ${deviceName}`);
127
+ }
128
+ }
129
+ } else {
130
+ RaftLog.error(`RaftChannelSimulated.connect - no simulated devices found`);
131
+ }
132
+
133
+ // Connected
134
+ this._isConnected = true;
135
+ return true;
136
+ }
137
+
138
+ // Disconnect
139
+ async disconnect(): Promise<void> {
140
+
141
+ // Not connected
142
+ this._isConnected = false;
143
+
144
+ // Clear timers
145
+ if (this._simulatedDeviceInfoTimers) {
146
+ for (const timer of this._simulatedDeviceInfoTimers) {
147
+ clearInterval(timer);
148
+ }
149
+ this._simulatedDeviceInfoTimers = null;
150
+ }
151
+
152
+ // Notify connection event
153
+ if (this._onConnEvent) {
154
+ this._onConnEvent(RaftConnEvent.CONN_DISCONNECTED);
155
+ }
156
+
157
+ // Debug
158
+ RaftLog.debug(`RaftChannelSimulated.disconnect closed`);
159
+ }
160
+
161
+ pauseConnection(pause: boolean): void {
162
+ RaftLog.debug(`pauseConnection ${pause} - no effect for this channel type`);
163
+ return;
164
+ }
165
+
166
+ // Send a message
167
+ async sendTxMsg(
168
+ msg: Uint8Array,
169
+ sendWithResponse: boolean
170
+ ): Promise<boolean> {
171
+
172
+ // Check connected
173
+ if (!this._isConnected)
174
+ return false;
175
+
176
+ // Debug
177
+ RaftLog.debug(`RaftChannelSimulated.sendTxMsg ${msg.toString()} sendWithResp ${sendWithResponse.toString()}`);
178
+ return true;
179
+ }
180
+
181
+ async sendTxMsgNoAwait(
182
+ msg: Uint8Array,
183
+ sendWithResponse: boolean
184
+ ): Promise<boolean> {
185
+
186
+ // Check connected
187
+ if (!this._isConnected)
188
+ return false;
189
+
190
+ // Debug
191
+ RaftLog.debug(`RaftChannelSimulated.sendTxMsgNoAwait ${msg.toString()} sendWithResp ${sendWithResponse.toString()}`);
192
+ return true;
193
+ }
194
+
195
+ // Method used for testing and simulation should never be called
196
+ sendTxMsgRaw(msg: string): boolean {
197
+ RaftLog.debug(`sendTxMsgRaw - not implemented ${msg}`);
198
+ return false;
199
+ }
200
+
201
+ // Method used for testing and simulation should never be called
202
+ sendTxMsgRawAndWaitForReply<T>(msgPayload: Uint8Array): T {
203
+ RaftLog.debug(`sendTxMsgRawAndWaitForReply ${msgPayload}`);
204
+
205
+ // Decode the message from Uint8Array to string
206
+ const textDecoder = new TextDecoder('utf-8');
207
+ const decodedString = textDecoder.decode(msgPayload.slice(1)).replace("\0", "").trim()
208
+
209
+ RaftLog.debug(`sendTxMsgRawAndWaitForReply ${decodedString}`);
210
+
211
+ // Check for version request
212
+ if (decodedString === "v") {
213
+ // R"({"req":"%s","rslt":"ok","SystemName":"%s","SystemVersion":"%s","Friendly":"%s","SerialNo":"%s","MAC":"%s",%s})",
214
+ const response = {
215
+ req: "v",
216
+ rslt: "ok",
217
+ SystemName: "Simulated",
218
+ SystemVersion: "1.0.0",
219
+ Friendly: "Simulated",
220
+ SerialNo: "123456",
221
+ MAC: "00:00:00:00:00:00"
222
+ }
223
+ return response as T;
224
+ }
225
+
226
+ else if (decodedString.startsWith("sub")) {
227
+ const response = {
228
+ req: decodedString,
229
+ rslt: "ok"
230
+ }
231
+ return response as T;
232
+ }
233
+
234
+ // Check if this is a device type info request
235
+ else if (decodedString.startsWith("devman/typeinfo?")) {
236
+ // Extract the type parameter from the request
237
+ const match = decodedString.match(/type=([^&]+)/);
238
+ if (match && match[1]) {
239
+ const deviceType = match[1];
240
+
241
+ // Look up the device type in the _deviceTypeInfo
242
+ if (deviceType in this._deviceTypeInfo) {
243
+ // Prepare response with the device type info
244
+ const response = {
245
+ req: decodedString,
246
+ rslt: "ok",
247
+ devinfo: this._deviceTypeInfo[deviceType]
248
+ };
249
+
250
+ RaftLog.debug(`Device type info for ${deviceType} found, returning response`);
251
+ return response as T;
252
+ } else {
253
+ // Device type not found
254
+ const response = {
255
+ req: decodedString,
256
+ rslt: "err",
257
+ msg: `Device type ${deviceType} not found`
258
+ };
259
+
260
+ RaftLog.warn(`Device type info for ${deviceType} not found`);
261
+ return response as T;
262
+ }
263
+ }
264
+ }
265
+
266
+ // Unknown message
267
+ const response = {
268
+ req: decodedString,
269
+ rslt: "err",
270
+ msg: `Unknown request`
271
+ };
272
+ return response as T;
273
+ }
274
+
275
+ // Create simulated device info message
276
+ private _createSimulatedDeviceInfoMsg(
277
+ deviceIntervalMs: number,
278
+ deviceName: string,
279
+ deviceTypeInfo: DeviceTypeInfo,
280
+ deviceTimeMs: number
281
+ ): Uint8Array {
282
+ // Make sure we have response metadata
283
+ if (!deviceTypeInfo?.resp?.a) {
284
+ return new Uint8Array(0);
285
+ }
286
+
287
+ const attributes = deviceTypeInfo.resp.a;
288
+ const dataBlockSizeBytes = deviceTypeInfo.resp.b;
289
+
290
+ // Create a buffer for the data
291
+ const dataBuffer = new ArrayBuffer(dataBlockSizeBytes + 2);
292
+ const dataView = new DataView(dataBuffer);
293
+ let bytePos = 0;
294
+
295
+ // Add 16 bit big endian deviceTimeMs mod 65536 to the buffer
296
+ dataView.setUint16(bytePos, deviceTimeMs % 65536, false);
297
+ bytePos += 2;
298
+
299
+ // Calculate sine wave with phase offsets for each attribute
300
+ const numAttributes = attributes.length;
301
+ // Adjust frequency based on the device interval with N samples per cycle
302
+ const numSamplesPerCycle = 10;
303
+ let frequencyHz = 0.1; // Default frequency in Hz
304
+ if (deviceIntervalMs > 0) {
305
+ frequencyHz = (1000 / deviceIntervalMs) / numSamplesPerCycle;
306
+ }
307
+
308
+ // Amplitude of the sine wave (0 to 1)
309
+ const amplitude = 0.8;
310
+
311
+ // Iterate through attributes and set values
312
+ for (let i = 0; i < numAttributes; i++) {
313
+ const attr = attributes[i];
314
+ // Calculate phase offset for this attribute
315
+ const phaseOffset = (2 * Math.PI * i) / numAttributes;
316
+
317
+ // Generate sine wave value
318
+ const timeRadians = deviceTimeMs * frequencyHz * (2 * Math.PI) / 1000;
319
+ const sinValue = Math.sin(timeRadians + phaseOffset);
320
+
321
+ // Scale the value to fit within the attribute's range
322
+ let scaledValue: number;
323
+ if (attr.r && attr.r.length >= 2) {
324
+ const minValue = attr.r[0];
325
+ const maxValue = attr.r[1];
326
+ const midPoint = (maxValue + minValue) / 2;
327
+ const range = (maxValue - minValue) / 2;
328
+ scaledValue = midPoint + sinValue * range * amplitude;
329
+ } else {
330
+ // Default range if not specified
331
+ scaledValue = sinValue * 1000 * amplitude;
332
+ }
333
+
334
+ // Convert to raw integer value if needed
335
+ let rawValue = scaledValue;
336
+ if (attr.d) {
337
+ // Multiply by the divisor to get the raw value (reverse of what happens when decoding)
338
+ rawValue = scaledValue * attr.d;
339
+ }
340
+
341
+ // Write the value to the buffer based on its type
342
+ if (attr.t === "b") {
343
+ dataView.setUint8(bytePos, Math.round(rawValue));
344
+ bytePos += 1;
345
+ } else if (attr.t === "B") {
346
+ dataView.setUint8(bytePos, Math.round(rawValue));
347
+ bytePos += 1;
348
+ } else if (attr.t === "c") {
349
+ dataView.setInt8(bytePos, Math.round(rawValue));
350
+ bytePos += 1;
351
+ } else if (attr.t === "C") {
352
+ dataView.setUint8(bytePos, Math.round(rawValue));
353
+ bytePos += 1;
354
+ } else if (attr.t === "<h") {
355
+ dataView.setInt16(bytePos, Math.round(rawValue), true); // Little endian
356
+ bytePos += 2;
357
+ } else if (attr.t === ">h") {
358
+ dataView.setInt16(bytePos, Math.round(rawValue), false); // Big endian
359
+ bytePos += 2;
360
+ } else if (attr.t === "<H") {
361
+ dataView.setUint16(bytePos, Math.round(rawValue), true); // Little endian
362
+ bytePos += 2;
363
+ } else if (attr.t === ">H") {
364
+ dataView.setUint16(bytePos, Math.round(rawValue), false); // Big endian
365
+ bytePos += 2;
366
+ } else if (attr.t === "f" || attr.t === "<f") {
367
+ dataView.setFloat32(bytePos, rawValue, true); // Little endian
368
+ bytePos += 4;
369
+ } else if (attr.t === ">f") {
370
+ dataView.setFloat32(bytePos, rawValue, false); // Big endian
371
+ bytePos += 4;
372
+ } else {
373
+ RaftLog.warn(`RaftChannelSimulated._createSimulatedDeviceInfoMsg - unsupported attribute type ${attr.t}`);
374
+ }
375
+
376
+ }
377
+
378
+ // Convert the buffer to a byte array
379
+ const dataBytes = new Uint8Array(dataBuffer);
380
+
381
+ // Create the JSON message structure
382
+ const message = {
383
+ "BUS1": {
384
+ [deviceName]: {
385
+ "_t": deviceTypeInfo.type,
386
+ "_o": 1, // Device is online
387
+ "pub": this._bytesToHexStr(dataBytes)
388
+ }
389
+ }
390
+ };
391
+
392
+ // Convert the JSON to a string and then to Uint8Array with prepended timestamp
393
+ const jsonString = JSON.stringify(message);
394
+ const encodedMsg = new TextEncoder().encode(jsonString);
395
+ const msgWithPrefix = new ArrayBuffer(2 + encodedMsg.byteLength);
396
+ const msgWithPrefixView = new DataView(msgWithPrefix);
397
+ const msgPrefixBytes = new Uint8Array(msgWithPrefix);
398
+ msgWithPrefixView.setUint16(0, 0, false);
399
+ msgPrefixBytes.set(encodedMsg, 2);
400
+ return msgPrefixBytes;
401
+
402
+ }
403
+
404
+ // Helper function to convert bytes to hex string
405
+ private _bytesToHexStr(bytes: Uint8Array): string {
406
+ return Array.from(bytes)
407
+ .map(byte => byte.toString(16).padStart(2, '0'))
408
+ .join('');
409
+ }
410
+
411
+ // Simulated device type information - this is a copy of part of DeviceTypeInfo in RaftCore
412
+ private _deviceTypeInfo: DeviceTypeInfoRecs =
413
+ {
414
+ "LSM6DS": {
415
+ "name": "LSM6DS",
416
+ "desc": "6-Axis IMU",
417
+ "manu": "ST",
418
+ "type": "LSM6DS",
419
+ "clas": ["ACC","GYRO"],
420
+ "resp": {
421
+ "b": 12,
422
+ "a": [
423
+ {
424
+ "n": "gx",
425
+ "t": "<h",
426
+ "u": "dps",
427
+ "r": [-2000, 2000],
428
+ "d": 16.384,
429
+ "f": ".2f",
430
+ "o": "float"
431
+ },
432
+ {
433
+ "n": "gy",
434
+ "t": "<h",
435
+ "u": "dps",
436
+ "r": [-2000, 2000],
437
+ "d": 16.384,
438
+ "f": ".2f",
439
+ "o": "float"
440
+ },
441
+ {
442
+ "n": "gz",
443
+ "t": "<h",
444
+ "u": "dps",
445
+ "r": [-2000, 2000],
446
+ "d": 16.384,
447
+ "f": ".2f",
448
+ "o": "float"
449
+ },
450
+ {
451
+ "n": "ax",
452
+ "t": "<h",
453
+ "u": "g",
454
+ "r": [-4.0, 4.0],
455
+ "d": 8192,
456
+ "f": ".2f",
457
+ "o": "float"
458
+ },
459
+ {
460
+ "n": "ay",
461
+ "t": "<h",
462
+ "u": "g",
463
+ "r": [-4.0,4.0],
464
+ "d": 8192,
465
+ "f": ".2f",
466
+ "o": "float"
467
+ },
468
+ {
469
+ "n": "az",
470
+ "t": "<h",
471
+ "u": "g",
472
+ "r": [-4.0,4.0],
473
+ "d": 8192,
474
+ "f": ".2f",
475
+ "o": "float"
476
+ }
477
+ ]
478
+ }
479
+ }
480
+ };
481
+
482
+ }
@@ -405,4 +405,16 @@ export default class RaftChannelWebSerial implements RaftChannel {
405
405
  this._isConnected = false;
406
406
  RaftLog.debug("Finished read loop");
407
407
  }
408
+
409
+ // Method used for testing and simulation should never be called
410
+ sendTxMsgRaw(): boolean {
411
+ RaftLog.debug(`sendTxMsgRaw - not implemented`);
412
+ return false;
413
+ }
414
+
415
+ // Method used for testing and simulation should never be called
416
+ sendTxMsgRawAndWaitForReply<T>(): T {
417
+ RaftLog.debug(`sendTxMsgRawAndWaitForReply - not implemented`);
418
+ return null as T;
419
+ }
408
420
  }
@@ -242,4 +242,17 @@ export default class RaftChannelWebSocket implements RaftChannel {
242
242
  }
243
243
  });
244
244
  }
245
+
246
+ // Method used for testing and simulation should never be called
247
+ sendTxMsgRaw(): boolean {
248
+ RaftLog.debug(`sendTxMsgRaw - not implemented`);
249
+ return false;
250
+ }
251
+
252
+ // Method used for testing and simulation should never be called
253
+ sendTxMsgRawAndWaitForReply<T>(): T {
254
+ RaftLog.debug(`sendTxMsgRawAndWaitForReply - not implemented`);
255
+ return null as T;
256
+ }
257
+
245
258
  }
@@ -12,6 +12,7 @@ import RaftChannel from "./RaftChannel";
12
12
  import RaftMsgHandler, { RaftMsgResultCode } from "./RaftMsgHandler";
13
13
  import RaftChannelWebSocket from "./RaftChannelWebSocket";
14
14
  import RaftChannelWebSerial from "./RaftChannelWebSerial";
15
+ import RaftChannelSimulated from "./RaftChannelSimulated";
15
16
  import RaftCommsStats from "./RaftCommsStats";
16
17
  import { RaftEventFn, RaftOKFail, RaftFileSendType, RaftFileDownloadResult, RaftProgressCBType, RaftBridgeSetupResp, RaftFileDownloadFn } from "./RaftTypes";
17
18
  import RaftSystemUtils from "./RaftSystemUtils";
@@ -219,6 +220,9 @@ export default class RaftConnector {
219
220
  } else if (method === 'WebSerial') {
220
221
  this._raftChannel = new RaftChannelWebSerial();
221
222
  this._channelConnMethod = 'WebSerial';
223
+ } else if (method === 'Simulated') {
224
+ this._raftChannel = new RaftChannelSimulated();
225
+ this._channelConnMethod = 'Simulated';
222
226
  } else {
223
227
  RaftLog.error('Unknown method: ' + method);
224
228
  return false;
@@ -86,7 +86,7 @@ export interface DeviceTypeInfo {
86
86
  manu: string;
87
87
  type: string;
88
88
  resp?: DeviceTypePollRespMetadata;
89
- clas?: [string];
89
+ clas?: Array<string>;
90
90
  actions?: DeviceTypeAction[];
91
91
  }
92
92
 
@@ -480,7 +480,7 @@ export class DeviceManager implements RaftDeviceMgrIF{
480
480
  const msgHandler = this._systemUtils?.getMsgHandler();
481
481
  if (msgHandler) {
482
482
  const msgRslt = await msgHandler.sendRICRESTURL<RaftDevTypeInfoResponse>(cmd);
483
- if (msgRslt.rslt === "ok") {
483
+ if (msgRslt && msgRslt.rslt && msgRslt.rslt === "ok") {
484
484
  this._cachedDeviceTypeRecs[deviceType] = msgRslt.devinfo;
485
485
  return msgRslt.devinfo
486
486
  }
@@ -60,6 +60,8 @@ export interface RaftMessageSender {
60
60
  msg: Uint8Array,
61
61
  sendWithResponse: boolean,
62
62
  ): Promise<boolean>;
63
+ sendTxMsgRaw(msg: string): boolean;
64
+ sendTxMsgRawAndWaitForReply<T>(msgPayload: Uint8Array): T;
63
65
  }
64
66
 
65
67
  export default class RaftMsgHandler {
@@ -87,6 +89,9 @@ export default class RaftMsgHandler {
87
89
  // RaftMiniHDLC - handles part of RICSerial protocol
88
90
  private _miniHDLC: RaftMiniHDLC;
89
91
 
92
+ // Raw message mode
93
+ private _rawMsgMode = false;
94
+
90
95
  // Constructor
91
96
  constructor(commsStats: RaftCommsStats) {
92
97
  this._commsStats = commsStats;
@@ -120,6 +125,22 @@ export default class RaftMsgHandler {
120
125
  // RaftLog.verbose(`handleNewRxMsg len ${rxMsg.length} ${RaftUtils.bufferToHex(rxMsg)}`)
121
126
  }
122
127
 
128
+ // This is currently only for testing and simulated channels
129
+ handleNewRxMsgRaw(rxMsg: Uint8Array, rxMsgType: RaftCommsMsgTypeCode, rxMsgNum: number, rxMsgTimeMs: number): void {
130
+ // Check message types
131
+ if (rxMsgType == RaftCommsMsgTypeCode.MSG_TYPE_RESPONSE) {
132
+ // Convert raw Uint8Array to string assuming UTF-8
133
+ const restStr = new TextDecoder('utf-8').decode(rxMsg);
134
+ this._handleResponseMessages(restStr, rxMsgNum);
135
+ } else if (rxMsgType == RaftCommsMsgTypeCode.MSG_TYPE_REPORT) {
136
+ // Convert raw Uint8Array to string assuming UTF-8
137
+ const restStr = new TextDecoder('utf-8').decode(rxMsg);
138
+ this._handleReportMessages(restStr);
139
+ } else {
140
+ this._msgResultHandler?.onRxOtherMsgType(rxMsg, rxMsgTimeMs);
141
+ }
142
+ }
143
+
123
144
  reportMsgCallbacksSet(callbackName: string, callback: (report: RaftReportMsg) => void): void {
124
145
  this._reportMsgCallbacks.set(callbackName, callback);
125
146
  }
@@ -128,6 +149,11 @@ export default class RaftMsgHandler {
128
149
  this._reportMsgCallbacks.delete(callbackName);
129
150
  }
130
151
 
152
+ setRawMsgMode(isRawMode: boolean): void {
153
+ this._rawMsgMode = isRawMode;
154
+ RaftLog.debug(`setRawMsgMode ${isRawMode}`);
155
+ }
156
+
131
157
  _onHDLCFrameDecode(rxMsg: Uint8Array, frameTimeMs: number): void {
132
158
  // Add to stats
133
159
  this._commsStats.msgRx();
@@ -336,6 +362,11 @@ export default class RaftMsgHandler {
336
362
  return false;
337
363
  }
338
364
 
365
+ // Check for raw message mode (used for testing and simulated channels)
366
+ if (this._rawMsgMode) {
367
+ return this._msgSender.sendTxMsgRaw(cmdStr);
368
+ }
369
+
339
370
  // Put cmdStr into buffer
340
371
  const cmdBytes = new Uint8Array(cmdStr.length + 1);
341
372
  RaftUtils.addStringToBuffer(cmdBytes, cmdStr, 0);
@@ -406,6 +437,11 @@ export default class RaftMsgHandler {
406
437
  throw new Error('sendMsgAndWaitForReply failed no sender');
407
438
  }
408
439
 
440
+ // Check for raw message mode (used for testing and simulated channels)
441
+ if (this._rawMsgMode) {
442
+ return this._msgSender.sendTxMsgRawAndWaitForReply(msgPayload);
443
+ }
444
+
409
445
  // Frame the message
410
446
  let framedMsg = this.frameCommsMsg(msgPayload, msgDirection, msgProtocol, true);
411
447
 
package/src/RaftStruct.ts CHANGED
@@ -134,7 +134,7 @@ export function structPack(format: string, values: number[]): Uint8Array {
134
134
  let offset = 0;
135
135
  let littleEndian = false;
136
136
 
137
- let valIdx = 0;
137
+ const valIdx = 0;
138
138
  for (let i = 0; i < format.length; i++) {
139
139
  const char = format[i];
140
140
  const value = values[valIdx];
package/src/main.ts CHANGED
@@ -16,6 +16,7 @@ export { default as RaftCommsStats } from './RaftCommsStats';
16
16
  export { default as RaftConnector } from './RaftConnector';
17
17
  export { default as RaftChannel } from './RaftChannel';
18
18
  export { default as RaftChannelWebSocket } from './RaftChannelWebSocket';
19
+ export { default as RaftChannelSimulated } from './RaftChannelSimulated';
19
20
  export { default as RaftFileHandler } from './RaftFileHandler';
20
21
  export { default as RaftLog } from './RaftLog';
21
22
  export { default as RaftMiniHDLC } from './RaftMiniHDLC';
@@ -34,3 +35,4 @@ export * from './RaftConnEvents';
34
35
  export * from './RaftUpdateEvents';
35
36
  export * from "./RaftProtocolDefs";
36
37
  export * from "./RaftDeviceStates";
38
+ export * from "./RaftDeviceInfo";