@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.
- package/dist/react-native/RaftAttributeHandler.d.ts +2 -0
- package/dist/react-native/RaftAttributeHandler.js +136 -10
- package/dist/react-native/RaftAttributeHandler.js.map +1 -1
- package/dist/react-native/RaftChannel.d.ts +2 -0
- package/dist/react-native/RaftChannelBLE.native.d.ts +2 -0
- package/dist/react-native/RaftChannelBLE.native.js +14 -7
- package/dist/react-native/RaftChannelBLE.native.js.map +1 -1
- package/dist/react-native/RaftChannelBLE.web.d.ts +2 -0
- package/dist/react-native/RaftChannelBLE.web.js +24 -15
- package/dist/react-native/RaftChannelBLE.web.js.map +1 -1
- package/dist/react-native/RaftChannelSimulated.d.ts +32 -0
- package/dist/react-native/RaftChannelSimulated.js +418 -0
- package/dist/react-native/RaftChannelSimulated.js.map +1 -0
- package/dist/react-native/RaftChannelWebSerial.d.ts +2 -0
- package/dist/react-native/RaftChannelWebSerial.js +18 -9
- package/dist/react-native/RaftChannelWebSerial.js.map +1 -1
- package/dist/react-native/RaftChannelWebSocket.d.ts +2 -0
- package/dist/react-native/RaftChannelWebSocket.js +10 -0
- package/dist/react-native/RaftChannelWebSocket.js.map +1 -1
- package/dist/react-native/RaftConnector.js +13 -6
- package/dist/react-native/RaftConnector.js.map +1 -1
- package/dist/react-native/RaftCustomAttrHandler.js +15 -0
- package/dist/react-native/RaftCustomAttrHandler.js.map +1 -1
- package/dist/react-native/RaftDeviceInfo.d.ts +8 -1
- package/dist/react-native/RaftDeviceManager.d.ts +3 -0
- package/dist/react-native/RaftDeviceManager.js +123 -43
- package/dist/react-native/RaftDeviceManager.js.map +1 -1
- package/dist/react-native/RaftFileHandler.js +8 -8
- package/dist/react-native/RaftFileHandler.js.map +1 -1
- package/dist/react-native/RaftLog.js +1 -1
- package/dist/react-native/RaftLog.js.map +1 -1
- package/dist/react-native/RaftMsgHandler.d.ts +5 -0
- package/dist/react-native/RaftMsgHandler.js +32 -0
- package/dist/react-native/RaftMsgHandler.js.map +1 -1
- package/dist/react-native/RaftStreamHandler.js +6 -5
- package/dist/react-native/RaftStreamHandler.js.map +1 -1
- package/dist/react-native/RaftStruct.js +1 -1
- package/dist/react-native/RaftStruct.js.map +1 -1
- package/dist/react-native/RaftSysTypeManager.d.ts +2 -0
- package/dist/react-native/RaftSysTypeManager.js +25 -0
- package/dist/react-native/RaftSysTypeManager.js.map +1 -1
- package/dist/react-native/RaftSystemType.d.ts +9 -7
- package/dist/react-native/RaftSystemUtils.js +3 -1
- package/dist/react-native/RaftSystemUtils.js.map +1 -1
- package/dist/react-native/RaftUpdateManager.js +2 -2
- package/dist/react-native/RaftUpdateManager.js.map +1 -1
- package/dist/react-native/RaftUtils.d.ts +2 -0
- package/dist/react-native/RaftUtils.js +22 -2
- package/dist/react-native/RaftUtils.js.map +1 -1
- package/dist/react-native/main.d.ts +2 -0
- package/dist/react-native/main.js +4 -1
- package/dist/react-native/main.js.map +1 -1
- package/dist/web/RaftAttributeHandler.d.ts +2 -0
- package/dist/web/RaftAttributeHandler.js +136 -10
- package/dist/web/RaftAttributeHandler.js.map +1 -1
- package/dist/web/RaftChannel.d.ts +2 -0
- package/dist/web/RaftChannelBLE.web.d.ts +2 -0
- package/dist/web/RaftChannelBLE.web.js +24 -15
- package/dist/web/RaftChannelBLE.web.js.map +1 -1
- package/dist/web/RaftChannelSimulated.d.ts +32 -0
- package/dist/web/RaftChannelSimulated.js +418 -0
- package/dist/web/RaftChannelSimulated.js.map +1 -0
- package/dist/web/RaftChannelWebSerial.d.ts +2 -0
- package/dist/web/RaftChannelWebSerial.js +18 -9
- package/dist/web/RaftChannelWebSerial.js.map +1 -1
- package/dist/web/RaftChannelWebSocket.d.ts +2 -0
- package/dist/web/RaftChannelWebSocket.js +10 -0
- package/dist/web/RaftChannelWebSocket.js.map +1 -1
- package/dist/web/RaftConnector.js +13 -6
- package/dist/web/RaftConnector.js.map +1 -1
- package/dist/web/RaftCustomAttrHandler.js +15 -0
- package/dist/web/RaftCustomAttrHandler.js.map +1 -1
- package/dist/web/RaftDeviceInfo.d.ts +8 -1
- package/dist/web/RaftDeviceManager.d.ts +3 -0
- package/dist/web/RaftDeviceManager.js +123 -43
- package/dist/web/RaftDeviceManager.js.map +1 -1
- package/dist/web/RaftFileHandler.js +8 -8
- package/dist/web/RaftFileHandler.js.map +1 -1
- package/dist/web/RaftLog.js +1 -1
- package/dist/web/RaftLog.js.map +1 -1
- package/dist/web/RaftMsgHandler.d.ts +5 -0
- package/dist/web/RaftMsgHandler.js +32 -0
- package/dist/web/RaftMsgHandler.js.map +1 -1
- package/dist/web/RaftStreamHandler.js +6 -5
- package/dist/web/RaftStreamHandler.js.map +1 -1
- package/dist/web/RaftStruct.js +1 -1
- package/dist/web/RaftStruct.js.map +1 -1
- package/dist/web/RaftSysTypeManager.d.ts +2 -0
- package/dist/web/RaftSysTypeManager.js +25 -0
- package/dist/web/RaftSysTypeManager.js.map +1 -1
- package/dist/web/RaftSystemType.d.ts +9 -7
- package/dist/web/RaftSystemUtils.js +3 -1
- package/dist/web/RaftSystemUtils.js.map +1 -1
- package/dist/web/RaftUpdateManager.js +2 -2
- package/dist/web/RaftUpdateManager.js.map +1 -1
- package/dist/web/RaftUtils.d.ts +2 -0
- package/dist/web/RaftUtils.js +22 -2
- package/dist/web/RaftUtils.js.map +1 -1
- package/dist/web/main.d.ts +2 -0
- package/dist/web/main.js +4 -1
- package/dist/web/main.js.map +1 -1
- package/examples/dashboard/package.json +1 -1
- package/examples/dashboard/src/CommandPanel.tsx +3 -3
- package/examples/dashboard/src/ConnManager.ts +83 -6
- package/examples/dashboard/src/DeviceActionsForm.tsx +2 -2
- package/examples/dashboard/src/DevicePanel.tsx +2 -2
- package/examples/dashboard/src/Main.tsx +14 -4
- package/examples/dashboard/src/SystemTypeMarty/RICSystemUtils.ts +4 -4
- package/examples/dashboard/src/styles.css +8 -0
- package/examples/dashboard/tsconfig.json +1 -1
- package/package.json +10 -11
- package/src/RaftAttributeHandler.ts +163 -11
- package/src/RaftChannel.ts +2 -0
- package/src/RaftChannelBLE.native.ts +17 -8
- package/src/RaftChannelBLE.web.ts +28 -16
- package/src/RaftChannelSimulated.ts +482 -0
- package/src/RaftChannelWebSerial.ts +18 -7
- package/src/RaftChannelWebSocket.ts +13 -0
- package/src/RaftConnector.ts +13 -7
- package/src/RaftCustomAttrHandler.ts +17 -0
- package/src/RaftDeviceInfo.ts +8 -2
- package/src/RaftDeviceManager.ts +155 -47
- package/src/RaftFileHandler.ts +8 -8
- package/src/RaftLog.ts +1 -1
- package/src/RaftMsgHandler.ts +36 -0
- package/src/RaftStreamHandler.ts +48 -47
- package/src/RaftStruct.ts +1 -1
- package/src/RaftSysTypeManager.ts +27 -0
- package/src/RaftSystemType.ts +9 -7
- package/src/RaftSystemUtils.ts +3 -1
- package/src/RaftUpdateManager.ts +2 -2
- package/src/RaftUtils.ts +25 -5
- 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
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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(
|
|
312
|
+
await this._characteristicTx.writeValueWithoutResponse(bs);
|
|
315
313
|
} else if (this._characteristicTx.writeValue) {
|
|
316
|
-
await this._characteristicTx.writeValue(
|
|
314
|
+
await this._characteristicTx.writeValue(bs);
|
|
317
315
|
} else if (this._characteristicTx.writeValueWithResponse) {
|
|
318
|
-
await this._characteristicTx.writeValueWithResponse(
|
|
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(
|
|
351
|
+
this._characteristicTx.writeValueWithoutResponse(bs);
|
|
353
352
|
} else if (this._characteristicTx.writeValue) {
|
|
354
|
-
this._characteristicTx.writeValue(
|
|
353
|
+
this._characteristicTx.writeValue(bs);
|
|
355
354
|
} else if (this._characteristicTx.writeValueWithResponse) {
|
|
356
|
-
this._characteristicTx.writeValueWithResponse(
|
|
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": "°/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": "°/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": "°/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
|
+
}
|