@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
@@ -13,7 +13,6 @@ import RaftMsgHandler from "./RaftMsgHandler";
13
13
  import RaftLog from "./RaftLog";
14
14
  import { RaftConnEvent, RaftConnEventFn } from "./RaftConnEvents";
15
15
  import { ConnectorOptions } from "./RaftSystemType";
16
- import { TextDecoder } from 'text-encoding';
17
16
 
18
17
  type TWebParityType = 'none' | 'even' | 'odd';
19
18
  type TWebFlowControlType = 'none' | 'hardware';
@@ -161,7 +160,7 @@ export default class RaftChannelWebSerial implements RaftChannel {
161
160
  if (err.name == "InvalidStateError") {
162
161
  RaftLog.debug(`Opening port failed - already open ${err}`);
163
162
  } else {
164
- RaftLog.error(`Opening port failed: ${err}`);
163
+ RaftLog.warn(`Opening port failed: ${err}`);
165
164
  throw err;
166
165
  }
167
166
  }
@@ -178,7 +177,7 @@ export default class RaftChannelWebSerial implements RaftChannel {
178
177
  });
179
178
  // TODO: handle errors
180
179
  } catch (err) {
181
- RaftLog.error("RaftChannelWebSerial.connect fail. Error: " + JSON.stringify(err));
180
+ RaftLog.warn("RaftChannelWebSerial.connect fail. Error: " + JSON.stringify(err));
182
181
  return false;
183
182
  }
184
183
 
@@ -349,7 +348,7 @@ export default class RaftChannelWebSerial implements RaftChannel {
349
348
  writer.write(msg).then(() => { writer.releaseLock(); });
350
349
  }
351
350
  } catch (err) {
352
- RaftLog.error("sendMsg error: " + JSON.stringify(err));
351
+ RaftLog.warn("sendMsg error: " + JSON.stringify(err));
353
352
  }
354
353
 
355
354
  return true;
@@ -364,7 +363,7 @@ export default class RaftChannelWebSerial implements RaftChannel {
364
363
  let retries = 10;
365
364
  try {
366
365
  if (!this._port.readable) {
367
- RaftLog.error("RaftChannelWebSerial _readLoop port is not readble");
366
+ RaftLog.warn("RaftChannelWebSerial _readLoop port is not readble");
368
367
  return;
369
368
  }
370
369
  this._reader = this._port.readable.getReader();
@@ -390,7 +389,7 @@ export default class RaftChannelWebSerial implements RaftChannel {
390
389
 
391
390
  this._onMsgRx(new Uint8Array(value));
392
391
  } catch (err) {
393
- RaftLog.error("read loop issue: " + JSON.stringify(err));
392
+ RaftLog.warn("read loop issue: " + JSON.stringify(err));
394
393
  retries -= 1;
395
394
  if (!retries) break;
396
395
  await new Promise(resolve => setTimeout(resolve, 100));
@@ -400,10 +399,22 @@ export default class RaftChannelWebSerial implements RaftChannel {
400
399
  if (this._reader) this._reader.releaseLock();
401
400
  this._reader = undefined;
402
401
  } catch (err) {
403
- RaftLog.error("Read loop got disconnected. err: " + JSON.stringify(err));
402
+ RaftLog.warn("Read loop got disconnected. err: " + JSON.stringify(err));
404
403
  }
405
404
  // Disconnected!
406
405
  this._isConnected = false;
407
406
  RaftLog.debug("Finished read loop");
408
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
+ }
409
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, RaftReportMsg } from "./RaftTypes";
17
18
  import RaftSystemUtils from "./RaftSystemUtils";
@@ -224,8 +225,11 @@ export default class RaftConnector {
224
225
  } else if (method === 'WebSerial') {
225
226
  this._raftChannel = new RaftChannelWebSerial();
226
227
  this._channelConnMethod = 'WebSerial';
228
+ } else if (method === 'Simulated') {
229
+ this._raftChannel = new RaftChannelSimulated();
230
+ this._channelConnMethod = 'Simulated';
227
231
  } else {
228
- RaftLog.error('Unknown method: ' + method);
232
+ RaftLog.warn('Unknown method: ' + method);
229
233
  return false;
230
234
  }
231
235
 
@@ -256,7 +260,7 @@ export default class RaftConnector {
256
260
  */
257
261
  async connect(locator: string | object): Promise<boolean> {
258
262
  if (!this._raftChannel) {
259
- RaftLog.error('Raft channel is not initialized.');
263
+ RaftLog.warn('Raft channel is not initialized.');
260
264
  return false;
261
265
  }
262
266
 
@@ -273,7 +277,7 @@ export default class RaftConnector {
273
277
  // Connect
274
278
  connOk = await this._connectToChannel();
275
279
  } catch (err) {
276
- RaftLog.error('RaftConnector.connect - error: ' + err);
280
+ RaftLog.warn('RaftConnector.connect - error: ' + err);
277
281
  }
278
282
 
279
283
  if (connOk) {
@@ -283,7 +287,7 @@ export default class RaftConnector {
283
287
  this._systemType = await this._getSystemTypeCB(this._raftSystemUtils);
284
288
 
285
289
  // Set defaults
286
- if (this._systemType) {
290
+ if (this._systemType && this._systemType.defaultWiFiHostname) {
287
291
  this._raftSystemUtils.setDefaultWiFiHostname(this._systemType.defaultWiFiHostname);
288
292
  }
289
293
  }
@@ -325,10 +329,12 @@ export default class RaftConnector {
325
329
  if (this._raftChannel) {
326
330
  // Check if there is a RICREST command to send before disconnecting
327
331
  const ricRestCommand = this._raftChannel.ricRestCmdBeforeDisconnect();
328
- console.log(`sending RICREST command before disconnect: ${ricRestCommand}`);
329
332
  if (ricRestCommand) {
333
+ console.log(`sending RICREST command before disconnect: ${ricRestCommand}`);
330
334
  await this.sendRICRESTMsg(ricRestCommand, {});
331
335
  }
336
+ // Pause a little before disconnecting
337
+ await new Promise(resolve => setTimeout(resolve, 1000));
332
338
  // await this.sendRICRESTMsg("bledisc", {});
333
339
  await this._raftChannel.disconnect();
334
340
  this._raftChannel = null;
@@ -659,14 +665,14 @@ export default class RaftConnector {
659
665
  // Connect
660
666
  try {
661
667
  if (this._raftChannel) {
662
- const connected = await this._raftChannel.connect(this._channelConnLocator, this._systemType ? this._systemType.connectorOptions : {});
668
+ const connected = await this._raftChannel.connect(this._channelConnLocator, this._systemType ? this._systemType.connectorOptions : { });
663
669
  if (connected) {
664
670
  this._retryIfLostIsConnected = true;
665
671
  return true;
666
672
  }
667
673
  }
668
674
  } catch (error) {
669
- RaftLog.error(`RaftConnector.connect() error: ${error}`);
675
+ RaftLog.warn(`RaftConnector.connect() error: ${error}`);
670
676
  }
671
677
  return false;
672
678
  }
@@ -13,6 +13,9 @@ export default class CustomAttrHandler {
13
13
 
14
14
  public handleAttr(pollRespMetadata: DeviceTypePollRespMetadata, msgBuffer: Uint8Array, msgBufIdx: number): number[][] {
15
15
 
16
+ // Implement the pseudo-code:
17
+ // int N=(buf[0]+32-buf[2])%32;int k=3;int i=0;while(i<N){out.Red=(buf[k]<<16)|(buf[k+1]<<8)|buf[k+2];out.IR=(buf[k+3]<<16)|(buf[k+4]<<8)|buf[k+5];k+=6;i++;next;}
18
+
16
19
  // Number of bytes in the each message
17
20
  const numMsgBytes = pollRespMetadata.b;
18
21
 
@@ -48,6 +51,20 @@ export default class CustomAttrHandler {
48
51
  i++;
49
52
  ;
50
53
  }
54
+ } else if (pollRespMetadata.c!.n === "gravity_o2_calc") {
55
+ // Get the buffer
56
+ const buf = msgBuffer.slice(msgBufIdx);
57
+ if (buf.length < numMsgBytes) {
58
+ return [];
59
+ }
60
+
61
+ // Implement the pseudo-code:
62
+ // float key = 20.9/120.0; float val = key * (buf[0] + (buf[1]/10.0) + (buf[2]/100.0)); out.oxygen = val;
63
+ const key = 20.9 / 120.0;
64
+ const val = key * (buf[0] + (buf[1] / 10.0) + (buf[2] / 100.0));
65
+
66
+ // Add the value to the oxygen attribute
67
+ attrValues['oxygen'].push(val);
51
68
  }
52
69
  return attrValueVecs;
53
70
  }
@@ -33,10 +33,14 @@ export function decodeAttrUnitsEncoding(unitsEncoding: string): string {
33
33
  return unitsEncoding.replace(/&deg;/g, "°");
34
34
  }
35
35
 
36
+ export interface LUTRow {
37
+ r: string;
38
+ v: number;
39
+ }
36
40
  export interface DeviceTypeAttribute {
37
41
  n: string; // Name
38
42
  t: string; // Type in python struct module format (e.g. 'H' uint16, 'h' int16, 'f' float etc.)
39
- at?: number; // Start pos in buffer (after timestamp) if present (otherwise use relative position)
43
+ at?: number | number[]; // Start pos in buffer (after timestamp) if present (otherwise use relative position) or array of byte positions for non-contiguous data
40
44
  u?: string; // Units (e.g. mm)
41
45
  r?: number[]; // Range (either min, max or min, max, step or discrete values)
42
46
  x?: number; // XOR bit mask to invert bits in the attribute value
@@ -51,6 +55,8 @@ export interface DeviceTypeAttribute {
51
55
  v?: boolean | number; // Visibility of the attribute in all locations (mainly used to hide attributes that are not useful to the user)
52
56
  vs?: boolean | number; // Display attribute value in time-series graphs
53
57
  vf?: boolean | number; // Display attribute value in the device info panel
58
+ vft?: string; // Attribute validity based on the value of another named attribute
59
+ lut?: Array<LUTRow>; // Lookup table for the attribute value - each row is a lookup table for a range of values e.g. [{"r":"0x20-0x30","v":0},{"r":"1,2,3","v":42},{r:"","v":1}]
54
60
  }
55
61
 
56
62
  export interface CustomFunctionDefinition {
@@ -86,6 +92,7 @@ export interface DeviceTypeInfo {
86
92
  manu: string;
87
93
  type: string;
88
94
  resp?: DeviceTypePollRespMetadata;
95
+ clas?: Array<string>;
89
96
  actions?: DeviceTypeAction[];
90
97
  }
91
98
 
@@ -98,4 +105,3 @@ export type RaftDevTypeInfoResponse = {
98
105
  rslt: string;
99
106
  devinfo: DeviceTypeInfo;
100
107
  };
101
-
@@ -15,12 +15,10 @@ import AttributeHandler from "./RaftAttributeHandler";
15
15
  import RaftSystemUtils from "./RaftSystemUtils";
16
16
  import RaftDeviceMgrIF from "./RaftDeviceMgrIF";
17
17
  import { structPack } from "./RaftStruct";
18
+ // import RaftUtils from "./RaftUtils";
18
19
 
19
20
  export class DeviceManager implements RaftDeviceMgrIF{
20
21
 
21
- // Singleton
22
- // private static _instance: DeviceManager;
23
-
24
22
  // Max data points to store
25
23
  private _maxDatapointsToStore = 1000;
26
24
 
@@ -48,6 +46,9 @@ export class DeviceManager implements RaftDeviceMgrIF{
48
46
  private _newDeviceAttributeCallbacks: Array<(deviceKey: string, attrState: DeviceAttributeState) => void> = [];
49
47
  private _newAttributeDataCallbacks: Array<(deviceKey: string, attrState: DeviceAttributeState) => void> = [];
50
48
 
49
+ // Debug message index (to help debug with async messages)
50
+ private _debugMsgIndex = 0;
51
+
51
52
  public getDevicesState(): DevicesState {
52
53
  return this._devicesState;
53
54
  }
@@ -62,6 +63,12 @@ export class DeviceManager implements RaftDeviceMgrIF{
62
63
  // Cached device type previous attempt times
63
64
  private _cachedDeviceTypePreviousAttemptTimes: { [deviceType: string]: number } = {};
64
65
 
66
+ // Pending device type requests - queue-based to maintain order
67
+ private _pendingDeviceTypeRequests: { [deviceType: string]: {
68
+ promise: Promise<DeviceTypeInfo | undefined>;
69
+ waitingQueue: Array<{resolve: (value: DeviceTypeInfo | undefined) => void, reject: (reason?: any) => void}>;
70
+ } } = {};
71
+
65
72
  // Constructor
66
73
  constructor() {
67
74
  }
@@ -156,44 +163,71 @@ export class DeviceManager implements RaftDeviceMgrIF{
156
163
  // console.log(`DeviceManager client1 msg ${RaftUtils.bufferToHex(rxMsg)}`);
157
164
 
158
165
  // Example messages
159
- // 0080 0015 81 0000006a 0004 53b7 feff00000100081857079314 0011 80 00000000 0011 53b2 075106e400d60054 0010 80 00000000 0012 5231 000d0000010e01
160
- // 0080 0011 80 00000000 0011 53f1 075806e900d70052 0010 80 00000000 0012 5231 000d0000010e01
166
+ // 0080 0015 81 0000006a 0004 53b7 feff00000100081857079314 0011 80 00000000 0011 53b2 075106e400d60054 0010 80 00000000 0012 5231 000d0000010e01
167
+ // 0080 0011 80 00000000 0002 4ae1 0787052606240007 000e 80 00000000 0003 0006 030001af01
168
+ // 0080 0011 80 00000000 0002 e46e 061e05a206830433 0010 80000000000003e4760006030001c701
169
+ // 0080 0010 81 00000015 0004 e4a2 0650fe00305002 0011 80000000000002e4a8061f059f06850438 001080000000000003e4aa0006030001c701
170
+
171
+ // 0080 0011 80 00000000 0002 31e4 05ea05a506660137 000e 80 00000000 0003 0007 030001d901
172
+
173
+ // 0080 0011 80 00000000 0002 4d63 0792053e06500061 000e 80 00000000 0003 0005 030001de01
161
174
 
162
175
  // First two bytes of each message are the message type (0080)
163
176
  // There are then a series of sections each of which is the data for a device
164
- // First two bytes of each section is the section length (big endian) not including the length bytes
177
+ // First two bytes of each section is the section length (big endian) not including the section length bytes themselves
165
178
  // Next byte is the connection mode (0 for direct connect, 1+ for bus number) and the MSB of this byte is 1 if the device is online
166
179
  // Next is the device address (4 bytes big endian)
167
180
  // Next is the device type index (2 bytes big endian)
168
181
  // Finally the device data which can be one or more groups of attributes defined by the schema
169
182
 
183
+ // Debug
184
+ // const debugMsgTime = Date.now();
185
+ const debugMsgIndex = this._debugMsgIndex++;
186
+
187
+ // Message layout
188
+ const msgTypeLen = 2; // Length of the message type (first two bytes)
189
+ const sectionLengthLen = 2; // Length of the inclusive section length (first two bytes of each section)
190
+ const sectionConnectionModeLen = 1; // Length of the connection mode (next byte after section length)
191
+ const sectionDeviceAddrLen = 4; // Length of the device address (next 4 bytes after connection mode)
192
+ const sectionDeviceTypeIdxLen = 2; // Length of the device type index (next 2 bytes after device address)
193
+ const sectionHeaderLen = sectionConnectionModeLen + sectionDeviceAddrLen + sectionDeviceTypeIdxLen;
194
+
195
+ // console.log(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} rxMsg.length ${rxMsg.length} rxMsg ${RaftUtils.bufferToHex(rxMsg)}`);
196
+
197
+ // Start after the message type
198
+ let msgPos = msgTypeLen;
199
+
170
200
  // Iterate through sections
171
- let msgPos = 2;
172
201
  while (msgPos < rxMsg.length) {
173
202
 
174
203
  // Check length
175
- if (rxMsg.length < msgPos + 11) {
176
- console.warn(`DeviceManager handleClientMsgBinary invalid length ${rxMsg.length}`);
204
+ const remainingLen = rxMsg.length - msgPos;
205
+ if (remainingLen < sectionLengthLen + sectionHeaderLen) {
206
+ console.warn(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} invalid length ${rxMsg.length} < ${sectionLengthLen + sectionHeaderLen + msgPos}`);
177
207
  return;
178
208
  }
179
209
 
180
210
  // Get the length of the section
181
211
  const sectionLen = (rxMsg[msgPos] << 8) + rxMsg[msgPos + 1];
182
- if (sectionLen > rxMsg.length) {
183
- console.warn(`DeviceManager handleClientMsgBinary invalid msgPos ${msgPos} msgLen ${sectionLen} rxMsgLen ${rxMsg.length}`);
212
+ if (sectionLen > remainingLen + sectionLengthLen) {
213
+ console.warn(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} invalid msgPos ${msgPos} msgLen ${sectionLen} remainingLenAfterLenBytes ${remainingLen-sectionLengthLen}`);
184
214
  return;
185
215
  }
186
216
 
187
217
  // Extract message elements
188
- const busNum = rxMsg[msgPos + 2] & 0x7f;
189
- const isOnline = (rxMsg[msgPos + 2] & 0x80) !== 0;
190
- const devAddr = (rxMsg[msgPos + 3] << 24) + (rxMsg[msgPos + 4] << 16) + (rxMsg[msgPos + 5] << 8) + rxMsg[msgPos + 6];
191
- const devTypeIdx = (rxMsg[msgPos + 7] << 8) + rxMsg[msgPos + 8];
192
- let attrGroupPos = msgPos + 9;
218
+ let sectionPos = msgPos + sectionLengthLen;
219
+ const busNum = rxMsg[sectionPos] & 0x7f;
220
+ const isOnline = (rxMsg[sectionPos] & 0x80) !== 0;
221
+ sectionPos += sectionConnectionModeLen;
222
+ // Get the device address and type index
223
+ const devAddr = (rxMsg[sectionPos] << 24) + (rxMsg[sectionPos + 1] << 16) + (rxMsg[sectionPos + 2] << 8) + rxMsg[sectionPos + 3];
224
+ sectionPos += sectionDeviceAddrLen;
225
+ const devTypeIdx = (rxMsg[sectionPos] << 8) + rxMsg[sectionPos + 1];
226
+ let attrGroupPos = sectionPos + sectionDeviceTypeIdxLen;
193
227
 
194
228
  // Debug
195
- // console.log(`DeviceManager overallLen ${rxMsg.length} sectionPos ${msgPos} sectionLen ${sectionLen} ${RaftUtils.bufferToHex(rxMsg.slice(msgPos, msgPos + sectionLen))}`);
196
- // console.log(`DeviceManager connMode ${busNum} isOnline ${isOnline} devAddr ${devAddr} devTypeIdx ${devTypeIdx} attrGroupDataLen ${sectionLen - 9}`);
229
+ // console.log(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} overallLen ${rxMsg.length} sectionPos ${msgPos} sectionLen ${sectionLen} ${attrGroupPos} ${RaftUtils.bufferToHex(rxMsg.slice(msgPos, msgPos + sectionLen))}`);
230
+ // console.log(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} connMode ${busNum} isOnline ${isOnline} devAddr ${devAddr} devTypeIdx ${devTypeIdx} attrGroupDataLen ${sectionLen - sectionHeaderLen}`);
197
231
 
198
232
  // Device key
199
233
  const deviceKey = getDeviceKey(busNum.toString(), devAddr.toString(), devTypeIdx.toString());
@@ -203,9 +237,20 @@ export class DeviceManager implements RaftDeviceMgrIF{
203
237
 
204
238
  // Check if a device state already exists
205
239
  if (!(deviceKey in this._devicesState) || (this._devicesState[deviceKey].deviceTypeInfo === undefined)) {
240
+
206
241
  // Get the device type info
207
- const deviceTypeInfo = await this.getDeviceTypeInfo(busNum.toString(), devAddr.toString(), devTypeIdx.toString());
208
- // console.log(`DeviceManager deviceTypeInfo ${JSON.stringify(deviceTypeInfo)}`);
242
+ const deviceTypeInfo = await this.getDeviceTypeInfo(busNum.toString(), devTypeIdx.toString());
243
+
244
+ // Debug
245
+ // console.log(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} attrGroupPos ${attrGroupPos} busNum ${busNum} devAddr ${devAddr} devTypeIdx ${devTypeIdx} deviceTypeInfo ${JSON.stringify(deviceTypeInfo)}`);
246
+
247
+ // Handle case where device type info is not available
248
+ if (deviceTypeInfo === undefined) {
249
+ console.warn(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} deviceType ${devTypeIdx} info not available, skipping attribute processing for this section`);
250
+ // Skip to next section without processing attributes
251
+ msgPos += sectionLengthLen + sectionLen;
252
+ continue;
253
+ }
209
254
 
210
255
  // Check if device record exists
211
256
  if (deviceKey in this._devicesState) {
@@ -239,30 +284,59 @@ export class DeviceManager implements RaftDeviceMgrIF{
239
284
  const deviceState = this._devicesState[deviceKey];
240
285
  deviceState.isOnline = isOnline;
241
286
 
242
- // Check if device type info is available
287
+ // Check if device type info is available and complete
243
288
  if (deviceState.deviceTypeInfo && deviceState.deviceTypeInfo.resp) {
244
289
 
245
290
  // Iterate over attributes in the group
246
291
  const pollRespMetadata = deviceState.deviceTypeInfo!.resp!;
247
292
 
248
293
  // Iterate over attribute groups
249
- while (attrGroupPos < msgPos + sectionLen + 2) {
250
- const curTimelineLen = deviceState.deviceTimeline.timestampsUs.length;
294
+ const attrGroupDataLen = sectionLen - sectionHeaderLen;
295
+ const attrGroupStartPos = attrGroupPos;
296
+ while (attrGroupPos < attrGroupStartPos + attrGroupDataLen) {
297
+
298
+ // Add bounds checking
299
+ if (attrGroupPos >= rxMsg.length) {
300
+ console.warn(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} attrGroupPos ${attrGroupPos} exceeds message length ${rxMsg.length}`);
301
+ break;
302
+ }
303
+
251
304
  const newMsgBufIdx = this._attributeHandler.processMsgAttrGroup(rxMsg, attrGroupPos,
252
305
  deviceState.deviceTimeline, pollRespMetadata,
253
306
  deviceState.deviceAttributes,
254
307
  this._maxDatapointsToStore);
308
+
309
+ // console.log(`DevMan.handleClientMsgBinary decoded debugIdx ${debugMsgIndex} devType ${deviceState.deviceTypeInfo.name} attrGroupDataLen ${attrGroupDataLen} attrGroupPos ${attrGroupPos} sectionLen ${sectionLen} msgPos ${msgPos} rxMsgLen ${rxMsg.length} remainingLen ${remainingLen} pollRespMetadata ${JSON.stringify(pollRespMetadata)}`);
310
+
255
311
  if (newMsgBufIdx < 0)
312
+ {
313
+ console.warn(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} processMsgAttrGroup failed newMsgBufIdx ${newMsgBufIdx}`);
256
314
  break;
257
- attrGroupPos = newMsgBufIdx;
258
- if (deviceState.deviceTimeline.timestampsUs.length !== curTimelineLen) {
259
- deviceState.stateChanged = true;
260
315
  }
316
+
317
+ // Prevent infinite loops
318
+ if (newMsgBufIdx <= attrGroupPos) {
319
+ console.warn(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} processMsgAttrGroup didn't advance position from ${attrGroupPos} to ${newMsgBufIdx}`);
320
+ break;
321
+ }
322
+
323
+ attrGroupPos = newMsgBufIdx;
324
+ deviceState.stateChanged = true;
325
+
326
+ // console.log(`debugMsgTime ${debugMsgTime} newPt debugMsgIdx ${debugMsgIndex} rxMsgLen ${rxMsg.length} devType ${deviceState.deviceTypeInfo!.name} timestampsUs ${deviceState.deviceTimeline.timestampsUs[deviceState.deviceTimeline.timestampsUs.length - 1]} curTimelineLen ${deviceState.deviceTimeline.timestampsUs.length}`);
327
+
328
+
329
+ // console.log(`DevMan.handleClientMsgBinary group done debugIdx ${debugMsgIndex} attrGroupPos ${attrGroupPos} sectionLen ${sectionLen} msgPos ${msgPos} rxMsgLen ${rxMsg.length} remainingLen ${remainingLen}`);
261
330
  }
331
+ } else {
332
+ console.warn(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} deviceState incomplete for device ${deviceKey}, skipping attribute processing`);
262
333
  }
263
334
 
335
+ // Debug
336
+ // console.log(`DevMan.handleClientMsgBinary section done debugIdx ${debugMsgIndex} attrGroupPos ${attrGroupPos} sectionLen ${sectionLen} msgPos ${msgPos} newMsgPos ${msgPos + sectionLengthLen + sectionLen} rxMsgLen ${rxMsg.length} remainingLen ${remainingLen}`);
337
+
264
338
  // Move to next message
265
- msgPos += sectionLen + 2;
339
+ msgPos += sectionLengthLen + sectionLen;
266
340
  }
267
341
 
268
342
  // Check for devices that have not been updated for a while
@@ -276,7 +350,7 @@ export class DeviceManager implements RaftDeviceMgrIF{
276
350
  }
277
351
 
278
352
  // Process the callback
279
- this.processStateCallback();
353
+ this.processStateCallback();
280
354
  }
281
355
 
282
356
  ////////////////////////////////////////////////////////////////////////////
@@ -324,7 +398,7 @@ export class DeviceManager implements RaftDeviceMgrIF{
324
398
  if (!(deviceKey in this._devicesState) || (this._devicesState[deviceKey].deviceTypeInfo === undefined)) {
325
399
 
326
400
  // Get the device type info
327
- const deviceTypeInfo = await this.getDeviceTypeInfo(busName, devAddr, deviceTypeName);
401
+ const deviceTypeInfo = await this.getDeviceTypeInfo(busName, deviceTypeName);
328
402
 
329
403
  // Check if device record exists
330
404
  if (deviceKey in this._devicesState) {
@@ -392,7 +466,6 @@ export class DeviceManager implements RaftDeviceMgrIF{
392
466
  // Loop
393
467
  while (msgBufIdx < msgBytes.length) {
394
468
 
395
- const curTimelineLen = deviceState.deviceTimeline.timestampsUs.length;
396
469
  const newMsgBufIdx = this._attributeHandler.processMsgAttrGroup(msgBytes, msgBufIdx,
397
470
  deviceState.deviceTimeline, pollRespMetadata,
398
471
  deviceState.deviceAttributes,
@@ -400,9 +473,7 @@ export class DeviceManager implements RaftDeviceMgrIF{
400
473
  if (newMsgBufIdx < 0)
401
474
  break;
402
475
  msgBufIdx = newMsgBufIdx;
403
- if (deviceState.deviceTimeline.timestampsUs.length !== curTimelineLen) {
404
- deviceState.stateChanged = true;
405
- }
476
+ deviceState.stateChanged = true;
406
477
  }
407
478
  });
408
479
  });
@@ -455,39 +526,76 @@ export class DeviceManager implements RaftDeviceMgrIF{
455
526
  // Get device type info
456
527
  ////////////////////////////////////////////////////////////////////////////
457
528
 
458
- private async getDeviceTypeInfo(busName: string, _devAddr: string, deviceType: string): Promise<DeviceTypeInfo | undefined> {
459
-
460
- // Check if already in the cache
529
+ private async getDeviceTypeInfo(busName: string, deviceType: string): Promise<DeviceTypeInfo | undefined> {
530
+ // Check if already in cache
461
531
  if (deviceType in this._cachedDeviceTypeRecs) {
462
532
  return this._cachedDeviceTypeRecs[deviceType];
463
533
  }
464
534
 
465
- // Check if we have tried to get device info previously (and failed presumably since it isn't in the cache)
535
+ // Check if there's already a pending request for this device type
536
+ if (deviceType in this._pendingDeviceTypeRequests) {
537
+ // console.log(`DevMan.getDeviceTypeInfo joining existing request queue for deviceType ${deviceType}`);
538
+
539
+ // Add this request to the waiting queue
540
+ return new Promise<DeviceTypeInfo | undefined>((resolve, reject) => {
541
+ this._pendingDeviceTypeRequests[deviceType].waitingQueue.push({ resolve, reject });
542
+ });
543
+ }
544
+
545
+ // Check rate limiting for new requests
466
546
  if (deviceType in this._cachedDeviceTypePreviousAttemptTimes) {
467
- // Check if we should retry
468
- if ((Date.now() - this._cachedDeviceTypePreviousAttemptTimes[deviceType]) < this._minTimeBetweenDeviceTypeInfoRetrievalMs) {
547
+ const timeSinceLastAttempt = Date.now() - this._cachedDeviceTypePreviousAttemptTimes[deviceType];
548
+ if (timeSinceLastAttempt < this._minTimeBetweenDeviceTypeInfoRetrievalMs) {
549
+ console.log(`DevMan.getDeviceTypeInfo rate limited for deviceType ${deviceType}`);
469
550
  return undefined;
470
551
  }
471
552
  }
472
- this._cachedDeviceTypePreviousAttemptTimes[deviceType] = Date.now();
473
553
 
474
- // Get the device type info from the server
554
+ // Create and cache the promise with an empty waiting queue
555
+ const requestPromise = this.executeDeviceTypeInfoRequest(busName, deviceType);
556
+ this._pendingDeviceTypeRequests[deviceType] = {
557
+ promise: requestPromise,
558
+ waitingQueue: []
559
+ };
560
+
475
561
  try {
476
- // Form the request
477
- const cmd = "devman/typeinfo?bus=" + busName + "&type=" + deviceType;
562
+ const result = await requestPromise;
563
+
564
+ // Resolve all waiting requests with the same result
565
+ const waitingQueue = this._pendingDeviceTypeRequests[deviceType].waitingQueue;
566
+ waitingQueue.forEach(({ resolve }) => resolve(result));
567
+
568
+ return result;
569
+ } catch (error) {
570
+ // Reject all waiting requests with the same error
571
+ const waitingQueue = this._pendingDeviceTypeRequests[deviceType].waitingQueue;
572
+ waitingQueue.forEach(({ reject }) => reject(error));
573
+
574
+ console.warn(`DevMan.getDeviceTypeInfo failed for ${deviceType}: ${error}`);
575
+ return undefined;
576
+ } finally {
577
+ // Clean up the pending request
578
+ delete this._pendingDeviceTypeRequests[deviceType];
579
+ }
580
+ }
478
581
 
479
- // Get the msg handler
582
+ private async executeDeviceTypeInfoRequest(busName: string, deviceType: string): Promise<DeviceTypeInfo | undefined> {
583
+ this._cachedDeviceTypePreviousAttemptTimes[deviceType] = Date.now();
584
+
585
+ try {
586
+ const cmd = "devman/typeinfo?bus=" + busName + "&type=" + deviceType;
480
587
  const msgHandler = this._systemUtils?.getMsgHandler();
588
+
481
589
  if (msgHandler) {
482
590
  const msgRslt = await msgHandler.sendRICRESTURL<RaftDevTypeInfoResponse>(cmd);
483
- if (msgRslt.rslt === "ok") {
591
+ if (msgRslt && msgRslt.rslt === "ok") {
484
592
  this._cachedDeviceTypeRecs[deviceType] = msgRslt.devinfo;
485
- return msgRslt.devinfo
593
+ return msgRslt.devinfo;
486
594
  }
487
595
  }
488
596
  return undefined;
489
597
  } catch (error) {
490
- console.error(`DeviceManager getDeviceTypeInfo error ${error}`);
598
+ console.warn(`DeviceManager getDeviceTypeInfo error ${error}`);
491
599
  return undefined;
492
600
  }
493
601
  }
@@ -162,11 +162,11 @@ export default class RaftFileHandler {
162
162
  RICRESTElemCode.RICREST_ELEM_CODE_COMMAND_FRAME,
163
163
  );
164
164
  } catch (err) {
165
- RaftLog.error(`sendFileStartMsg error ${err}`);
165
+ RaftLog.warn(`sendFileStartMsg error ${err}`);
166
166
  return false;
167
167
  }
168
168
  if (fileStartResp.rslt !== 'ok') {
169
- RaftLog.error(`sendFileStartMsg error ${fileStartResp.rslt}`);
169
+ RaftLog.warn(`sendFileStartMsg error ${fileStartResp.rslt}`);
170
170
  return false;
171
171
  }
172
172
 
@@ -209,7 +209,7 @@ export default class RaftFileHandler {
209
209
  await this.awaitOutstandingMsgPromises(true);
210
210
  } catch (err) {
211
211
  // Ignore
212
- RaftLog.error(`sendFileEndMsg awaitOutstandingMsgPromises error ${err}`);
212
+ RaftLog.warn(`sendFileEndMsg awaitOutstandingMsgPromises error ${err}`);
213
213
  }
214
214
 
215
215
  // Send
@@ -220,7 +220,7 @@ export default class RaftFileHandler {
220
220
  RICRESTElemCode.RICREST_ELEM_CODE_COMMAND_FRAME,
221
221
  );
222
222
  } catch (err) {
223
- RaftLog.error(`sendFileEndMsg error ${err}`);
223
+ RaftLog.warn(`sendFileEndMsg error ${err}`);
224
224
  return false;
225
225
  }
226
226
  return fileEndResp.rslt === 'ok';
@@ -240,7 +240,7 @@ export default class RaftFileHandler {
240
240
  RICRESTElemCode.RICREST_ELEM_CODE_COMMAND_FRAME,
241
241
  );
242
242
  } catch (err) {
243
- RaftLog.error(`sendFileCancelMsg error ${err}`);
243
+ RaftLog.warn(`sendFileCancelMsg error ${err}`);
244
244
  }
245
245
  }
246
246
 
@@ -438,7 +438,7 @@ export default class RaftFileHandler {
438
438
  const bridgedDeviceSerialPort = "Serial" + fileSource.slice(bridgeSerialPrefix.length);
439
439
  const cmdResp = await this._msgHandler.createCommsBridge(bridgedDeviceSerialPort, "fileSource");
440
440
  if (cmdResp.rslt != "ok") {
441
- RaftLog.error(`fileReceive - failed to setup bridge ${cmdResp.rslt}`);
441
+ RaftLog.warn(`fileReceive - failed to setup bridge ${cmdResp.rslt}`);
442
442
  return new RaftFileDownloadResult();
443
443
  }
444
444
  bridgeID = cmdResp.bridgeID;
@@ -490,7 +490,7 @@ export default class RaftFileHandler {
490
490
  bridgeID,
491
491
  );
492
492
  } catch (err) {
493
- RaftLog.error(`_receiveFileStartMsg error ${err}`);
493
+ RaftLog.warn(`_receiveFileStartMsg error ${err}`);
494
494
  return false;
495
495
  }
496
496
  RaftLog.info(`_receiveFileStartMsg rslt ${JSON.stringify(cmdResp)}`);
@@ -530,7 +530,7 @@ export default class RaftFileHandler {
530
530
  // Check CRC
531
531
  const crc16 = RaftMiniHDLC.crc16(this._fileRxBuffer);
532
532
  if (crc16 !== this._fileRxCrc16) {
533
- RaftLog.error(`_receiveFileContents - CRC error ${crc16} ${this._fileRxCrc16}`);
533
+ RaftLog.warn(`_receiveFileContents - CRC error ${crc16} ${this._fileRxCrc16}`);
534
534
  reject(new Error('fileReceive CRC error'));
535
535
  return;
536
536
  } else {
package/src/RaftLog.ts CHANGED
@@ -44,7 +44,7 @@ export default class RaftLog {
44
44
 
45
45
  static error(msg: string) {
46
46
  if (!this.doLogging(RaftLogLevel.ERROR, msg))
47
- console.error(RaftLog.format(msg));
47
+ console.warn(RaftLog.format(msg));
48
48
  }
49
49
 
50
50
  static verbose(msg: string) {