@robotical/raftjs 1.4.7 → 2.0.3

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 (133) hide show
  1. package/dist/react-native/RaftAttributeHandler.d.ts +2 -0
  2. package/dist/react-native/RaftAttributeHandler.js +136 -10
  3. package/dist/react-native/RaftAttributeHandler.js.map +1 -1
  4. package/dist/react-native/RaftChannel.d.ts +2 -0
  5. package/dist/react-native/RaftChannelBLE.native.d.ts +2 -0
  6. package/dist/react-native/RaftChannelBLE.native.js +14 -7
  7. package/dist/react-native/RaftChannelBLE.native.js.map +1 -1
  8. package/dist/react-native/RaftChannelBLE.web.d.ts +2 -0
  9. package/dist/react-native/RaftChannelBLE.web.js +24 -15
  10. package/dist/react-native/RaftChannelBLE.web.js.map +1 -1
  11. package/dist/react-native/RaftChannelSimulated.d.ts +32 -0
  12. package/dist/react-native/RaftChannelSimulated.js +418 -0
  13. package/dist/react-native/RaftChannelSimulated.js.map +1 -0
  14. package/dist/react-native/RaftChannelWebSerial.d.ts +2 -0
  15. package/dist/react-native/RaftChannelWebSerial.js +18 -9
  16. package/dist/react-native/RaftChannelWebSerial.js.map +1 -1
  17. package/dist/react-native/RaftChannelWebSocket.d.ts +2 -0
  18. package/dist/react-native/RaftChannelWebSocket.js +10 -0
  19. package/dist/react-native/RaftChannelWebSocket.js.map +1 -1
  20. package/dist/react-native/RaftConnector.js +13 -6
  21. package/dist/react-native/RaftConnector.js.map +1 -1
  22. package/dist/react-native/RaftCustomAttrHandler.js +15 -0
  23. package/dist/react-native/RaftCustomAttrHandler.js.map +1 -1
  24. package/dist/react-native/RaftDeviceInfo.d.ts +8 -1
  25. package/dist/react-native/RaftDeviceManager.d.ts +3 -0
  26. package/dist/react-native/RaftDeviceManager.js +123 -43
  27. package/dist/react-native/RaftDeviceManager.js.map +1 -1
  28. package/dist/react-native/RaftFileHandler.js +8 -8
  29. package/dist/react-native/RaftFileHandler.js.map +1 -1
  30. package/dist/react-native/RaftLog.js +1 -1
  31. package/dist/react-native/RaftLog.js.map +1 -1
  32. package/dist/react-native/RaftMsgHandler.d.ts +5 -0
  33. package/dist/react-native/RaftMsgHandler.js +32 -0
  34. package/dist/react-native/RaftMsgHandler.js.map +1 -1
  35. package/dist/react-native/RaftStreamHandler.js +6 -5
  36. package/dist/react-native/RaftStreamHandler.js.map +1 -1
  37. package/dist/react-native/RaftStruct.js +1 -1
  38. package/dist/react-native/RaftStruct.js.map +1 -1
  39. package/dist/react-native/RaftSysTypeManager.d.ts +2 -0
  40. package/dist/react-native/RaftSysTypeManager.js +25 -0
  41. package/dist/react-native/RaftSysTypeManager.js.map +1 -1
  42. package/dist/react-native/RaftSystemType.d.ts +9 -7
  43. package/dist/react-native/RaftSystemUtils.js +3 -1
  44. package/dist/react-native/RaftSystemUtils.js.map +1 -1
  45. package/dist/react-native/RaftUpdateManager.js +2 -2
  46. package/dist/react-native/RaftUpdateManager.js.map +1 -1
  47. package/dist/react-native/RaftUtils.d.ts +2 -0
  48. package/dist/react-native/RaftUtils.js +22 -2
  49. package/dist/react-native/RaftUtils.js.map +1 -1
  50. package/dist/react-native/main.d.ts +2 -0
  51. package/dist/react-native/main.js +4 -1
  52. package/dist/react-native/main.js.map +1 -1
  53. package/dist/web/RaftAttributeHandler.d.ts +2 -0
  54. package/dist/web/RaftAttributeHandler.js +136 -10
  55. package/dist/web/RaftAttributeHandler.js.map +1 -1
  56. package/dist/web/RaftChannel.d.ts +2 -0
  57. package/dist/web/RaftChannelBLE.web.d.ts +2 -0
  58. package/dist/web/RaftChannelBLE.web.js +24 -15
  59. package/dist/web/RaftChannelBLE.web.js.map +1 -1
  60. package/dist/web/RaftChannelSimulated.d.ts +32 -0
  61. package/dist/web/RaftChannelSimulated.js +418 -0
  62. package/dist/web/RaftChannelSimulated.js.map +1 -0
  63. package/dist/web/RaftChannelWebSerial.d.ts +2 -0
  64. package/dist/web/RaftChannelWebSerial.js +18 -9
  65. package/dist/web/RaftChannelWebSerial.js.map +1 -1
  66. package/dist/web/RaftChannelWebSocket.d.ts +2 -0
  67. package/dist/web/RaftChannelWebSocket.js +10 -0
  68. package/dist/web/RaftChannelWebSocket.js.map +1 -1
  69. package/dist/web/RaftConnector.js +13 -6
  70. package/dist/web/RaftConnector.js.map +1 -1
  71. package/dist/web/RaftCustomAttrHandler.js +15 -0
  72. package/dist/web/RaftCustomAttrHandler.js.map +1 -1
  73. package/dist/web/RaftDeviceInfo.d.ts +8 -1
  74. package/dist/web/RaftDeviceManager.d.ts +3 -0
  75. package/dist/web/RaftDeviceManager.js +123 -43
  76. package/dist/web/RaftDeviceManager.js.map +1 -1
  77. package/dist/web/RaftFileHandler.js +8 -8
  78. package/dist/web/RaftFileHandler.js.map +1 -1
  79. package/dist/web/RaftLog.js +1 -1
  80. package/dist/web/RaftLog.js.map +1 -1
  81. package/dist/web/RaftMsgHandler.d.ts +5 -0
  82. package/dist/web/RaftMsgHandler.js +32 -0
  83. package/dist/web/RaftMsgHandler.js.map +1 -1
  84. package/dist/web/RaftStreamHandler.js +6 -5
  85. package/dist/web/RaftStreamHandler.js.map +1 -1
  86. package/dist/web/RaftStruct.js +1 -1
  87. package/dist/web/RaftStruct.js.map +1 -1
  88. package/dist/web/RaftSysTypeManager.d.ts +2 -0
  89. package/dist/web/RaftSysTypeManager.js +25 -0
  90. package/dist/web/RaftSysTypeManager.js.map +1 -1
  91. package/dist/web/RaftSystemType.d.ts +9 -7
  92. package/dist/web/RaftSystemUtils.js +3 -1
  93. package/dist/web/RaftSystemUtils.js.map +1 -1
  94. package/dist/web/RaftUpdateManager.js +2 -2
  95. package/dist/web/RaftUpdateManager.js.map +1 -1
  96. package/dist/web/RaftUtils.d.ts +2 -0
  97. package/dist/web/RaftUtils.js +22 -2
  98. package/dist/web/RaftUtils.js.map +1 -1
  99. package/dist/web/main.d.ts +2 -0
  100. package/dist/web/main.js +4 -1
  101. package/dist/web/main.js.map +1 -1
  102. package/examples/dashboard/package.json +1 -1
  103. package/examples/dashboard/src/CommandPanel.tsx +3 -3
  104. package/examples/dashboard/src/ConnManager.ts +83 -6
  105. package/examples/dashboard/src/DeviceActionsForm.tsx +2 -2
  106. package/examples/dashboard/src/DevicePanel.tsx +2 -2
  107. package/examples/dashboard/src/Main.tsx +14 -4
  108. package/examples/dashboard/src/SystemTypeMarty/RICSystemUtils.ts +4 -4
  109. package/examples/dashboard/src/styles.css +8 -0
  110. package/examples/dashboard/tsconfig.json +1 -1
  111. package/package.json +10 -11
  112. package/src/RaftAttributeHandler.ts +163 -11
  113. package/src/RaftChannel.ts +2 -0
  114. package/src/RaftChannelBLE.native.ts +17 -8
  115. package/src/RaftChannelBLE.web.ts +28 -16
  116. package/src/RaftChannelSimulated.ts +482 -0
  117. package/src/RaftChannelWebSerial.ts +18 -7
  118. package/src/RaftChannelWebSocket.ts +13 -0
  119. package/src/RaftConnector.ts +13 -7
  120. package/src/RaftCustomAttrHandler.ts +17 -0
  121. package/src/RaftDeviceInfo.ts +8 -2
  122. package/src/RaftDeviceManager.ts +155 -47
  123. package/src/RaftFileHandler.ts +8 -8
  124. package/src/RaftLog.ts +1 -1
  125. package/src/RaftMsgHandler.ts +36 -0
  126. package/src/RaftStreamHandler.ts +48 -47
  127. package/src/RaftStruct.ts +1 -1
  128. package/src/RaftSysTypeManager.ts +27 -0
  129. package/src/RaftSystemType.ts +9 -7
  130. package/src/RaftSystemUtils.ts +3 -1
  131. package/src/RaftUpdateManager.ts +2 -2
  132. package/src/RaftUtils.ts +25 -5
  133. package/src/main.ts +2 -0
@@ -125,7 +125,7 @@ export default class RaftChannelPhoneBLE implements RaftChannel {
125
125
 
126
126
  async discoveryStart(uuids: string[], tries = 10): Promise<boolean> {
127
127
  if (tries <= 0) {
128
- RaftLog.debug(`BLEChannel discoveryStart failed after 5 tries`);
128
+ RaftLog.debug(`BLEChannel discoveryStart failed`);
129
129
  return false;
130
130
  }
131
131
  // Disconnect any existing connection
@@ -363,7 +363,7 @@ export default class RaftChannelPhoneBLE implements RaftChannel {
363
363
  try {
364
364
  if (this._bleDevice) {
365
365
  if (!this._connectedDeviceServiceUUID) {
366
- RaftLog.error('BLEChannel _configDeviceConnection - no connected device service UUID');
366
+ RaftLog.warn('BLEChannel _configDeviceConnection - no connected device service UUID');
367
367
  return false;
368
368
  }
369
369
  this._bleSubscrOnRx = this._bleDevice.monitorCharacteristicForService(
@@ -535,7 +535,7 @@ export default class RaftChannelPhoneBLE implements RaftChannel {
535
535
 
536
536
  try {
537
537
  if (!this._connectedDeviceServiceUUID) {
538
- RaftLog.error('BLEChannel sendTxMsg - no connected device service UUID');
538
+ RaftLog.warn('BLEChannel sendTxMsg - no connected device service UUID');
539
539
  return false;
540
540
  }
541
541
  await this._bleDevice!.writeCharacteristicWithoutResponseForService(
@@ -578,7 +578,7 @@ export default class RaftChannelPhoneBLE implements RaftChannel {
578
578
 
579
579
  try {
580
580
  if (!this._connectedDeviceServiceUUID) {
581
- RaftLog.error('BLEChannel sendTxMsgNoAwait - no connected device service UUID');
581
+ RaftLog.warn('BLEChannel sendTxMsgNoAwait - no connected device service UUID');
582
582
  return false;
583
583
  }
584
584
  this._bleDevice!.writeCharacteristicWithoutResponseForService(
@@ -600,9 +600,18 @@ export default class RaftChannelPhoneBLE implements RaftChannel {
600
600
 
601
601
  // RICREST command before disconnect
602
602
  ricRestCmdBeforeDisconnect(): string | null {
603
- // NT: Sending blerestart *before* disconnecting results in timeout issues as the device is no longer connected when we try to actually disconnect
604
- // suggested fix: allow callaback command to be sent after disconnect on the fw side
605
- // return "blerestart";
606
- return null;
603
+ return "bledisconnect";
604
+ }
605
+
606
+ // Method used for testing and simulation should never be called
607
+ sendTxMsgRaw(): boolean {
608
+ RaftLog.debug(`sendTxMsgRaw - not implemented`);
609
+ return false;
610
+ }
611
+
612
+ // Method used for testing and simulation should never be called
613
+ sendTxMsgRawAndWaitForReply<T>(): T {
614
+ RaftLog.debug(`sendTxMsgRawAndWaitForReply - not implemented`);
615
+ return null as T;
607
616
  }
608
617
  }
@@ -21,7 +21,7 @@ export default class RaftChannelBLE implements RaftChannel {
21
21
  _cmdUUID = 'aa76677e-9cfd-4626-a510-0d305be57c8e';
22
22
  _respUUID = 'aa76677e-9cfd-4626-a510-0d305be57c8f';
23
23
  _serviceUUIDs = ['aa76677e-9cfd-4626-a510-0d305be57c8d', 'da903f65-d5c2-4f4d-a065-d1aade7af874'];
24
-
24
+
25
25
  // Device and characteristics
26
26
  private _bleDevice: BluetoothDevice | null = null;
27
27
  private _characteristicTx: BluetoothRemoteGATTCharacteristic | null = null;
@@ -67,16 +67,13 @@ export default class RaftChannelBLE implements RaftChannel {
67
67
 
68
68
  // RICREST command before disconnect
69
69
  ricRestCmdBeforeDisconnect(): string | null {
70
- // NT: Sending blerestart *before* disconnecting results in timeout issues as the device is no longer connected when we try to actually disconnect
71
- // suggested fix: allow callaback command to be sent after disconnect on the fw side
72
- // return "blerestart";
73
- return null;
70
+ return "bledisconnect";
74
71
  }
75
72
 
76
73
  // isEnabled
77
74
  isEnabled() {
78
75
  if (navigator.bluetooth) {
79
- RaftLog.error("Web Bluetooth is supported in your browser.");
76
+ RaftLog.warn("Web Bluetooth is supported in your browser.");
80
77
  return true;
81
78
  } else {
82
79
  window.alert(
@@ -101,7 +98,7 @@ export default class RaftChannelBLE implements RaftChannel {
101
98
  onDisconnected(event: Event): void {
102
99
  const device = event.target as BluetoothDevice;
103
100
  RaftLog.debug(`RaftChannelBLE.onDisconnected ${device.name}`);
104
- if (this._bleDevice) {
101
+ if (this._bleDevice && this._eventListenerFn) {
105
102
  this._bleDevice.removeEventListener(
106
103
  "gattserverdisconnected",
107
104
  this._eventListenerFn
@@ -157,7 +154,7 @@ export default class RaftChannelBLE implements RaftChannel {
157
154
  }
158
155
 
159
156
  if (!service) {
160
- RaftLog.error(
157
+ RaftLog.warn(
161
158
  `RaftChannelBLE.connect - cannot get primary service - giving up`
162
159
  );
163
160
  return false;
@@ -211,13 +208,13 @@ export default class RaftChannelBLE implements RaftChannel {
211
208
  this._isConnected = true;
212
209
  return true;
213
210
  } catch (error) {
214
- RaftLog.error(
211
+ RaftLog.warn(
215
212
  `RaftChannelBLE.connect - cannot find characteristic: ${error}`
216
213
  );
217
214
  }
218
215
  } catch (error) {
219
216
  if (connRetry === this._maxConnRetries - 1) {
220
- RaftLog.error(
217
+ RaftLog.warn(
221
218
  `RaftChannelBLE.connect - cannot get primary service ${error} - attempt #${connRetry + 1} - giving up`
222
219
  );
223
220
  } else {
@@ -310,12 +307,13 @@ export default class RaftChannelBLE implements RaftChannel {
310
307
  // Write to the characteristic
311
308
  try {
312
309
  if (this._characteristicTx) {
310
+ const bs = RaftUtils.toBufferSource(msg);
313
311
  if (this._characteristicTx.writeValueWithoutResponse) {
314
- await this._characteristicTx.writeValueWithoutResponse(msg);
312
+ await this._characteristicTx.writeValueWithoutResponse(bs);
315
313
  } else if (this._characteristicTx.writeValue) {
316
- await this._characteristicTx.writeValue(msg);
314
+ await this._characteristicTx.writeValue(bs);
317
315
  } else if (this._characteristicTx.writeValueWithResponse) {
318
- await this._characteristicTx.writeValueWithResponse(msg);
316
+ await this._characteristicTx.writeValueWithResponse(bs);
319
317
  }
320
318
  }
321
319
  break;
@@ -348,15 +346,29 @@ export default class RaftChannelBLE implements RaftChannel {
348
346
 
349
347
  // Write to the characteristic
350
348
  if (this._characteristicTx) {
349
+ const bs = RaftUtils.toBufferSource(msg);
351
350
  if (this._characteristicTx.writeValueWithoutResponse) {
352
- this._characteristicTx.writeValueWithoutResponse(msg);
351
+ this._characteristicTx.writeValueWithoutResponse(bs);
353
352
  } else if (this._characteristicTx.writeValue) {
354
- this._characteristicTx.writeValue(msg);
353
+ this._characteristicTx.writeValue(bs);
355
354
  } else if (this._characteristicTx.writeValueWithResponse) {
356
- this._characteristicTx.writeValueWithResponse(msg);
355
+ this._characteristicTx.writeValueWithResponse(bs);
357
356
  }
358
357
  return true;
359
358
  }
360
359
  return false;
361
360
  }
361
+
362
+ // Method used for testing and simulation should never be called
363
+ sendTxMsgRaw(): boolean {
364
+ RaftLog.debug(`sendTxMsgRaw - not implemented`);
365
+ return false;
366
+ }
367
+
368
+ // Method used for testing and simulation should never be called
369
+ sendTxMsgRawAndWaitForReply<T>(): T {
370
+ RaftLog.debug(`sendTxMsgRawAndWaitForReply - not implemented`);
371
+ return null as T;
372
+ }
373
+
362
374
  }
@@ -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.warn(`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 = 911;
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.warn(`RaftChannelSimulated.connect - device type info not found for ${deviceName}`);
127
+ }
128
+ }
129
+ } else {
130
+ RaftLog.warn(`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": "&deg;/s",
427
+ "r": [-2000, 2000],
428
+ "d": 16.384,
429
+ "f": ".2f",
430
+ "o": "float"
431
+ },
432
+ {
433
+ "n": "gy",
434
+ "t": "<h",
435
+ "u": "&deg;/s",
436
+ "r": [-2000, 2000],
437
+ "d": 16.384,
438
+ "f": ".2f",
439
+ "o": "float"
440
+ },
441
+ {
442
+ "n": "gz",
443
+ "t": "<h",
444
+ "u": "&deg;/s",
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
+ }