@robotical/raftjs 1.4.7 → 2.0.4

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 (137) 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/RaftDeviceInfo.js +17 -3
  26. package/dist/react-native/RaftDeviceInfo.js.map +1 -1
  27. package/dist/react-native/RaftDeviceManager.d.ts +3 -0
  28. package/dist/react-native/RaftDeviceManager.js +123 -43
  29. package/dist/react-native/RaftDeviceManager.js.map +1 -1
  30. package/dist/react-native/RaftFileHandler.js +8 -8
  31. package/dist/react-native/RaftFileHandler.js.map +1 -1
  32. package/dist/react-native/RaftLog.js +1 -1
  33. package/dist/react-native/RaftLog.js.map +1 -1
  34. package/dist/react-native/RaftMsgHandler.d.ts +5 -0
  35. package/dist/react-native/RaftMsgHandler.js +32 -0
  36. package/dist/react-native/RaftMsgHandler.js.map +1 -1
  37. package/dist/react-native/RaftStreamHandler.js +6 -5
  38. package/dist/react-native/RaftStreamHandler.js.map +1 -1
  39. package/dist/react-native/RaftStruct.js +197 -147
  40. package/dist/react-native/RaftStruct.js.map +1 -1
  41. package/dist/react-native/RaftSysTypeManager.d.ts +2 -0
  42. package/dist/react-native/RaftSysTypeManager.js +25 -0
  43. package/dist/react-native/RaftSysTypeManager.js.map +1 -1
  44. package/dist/react-native/RaftSystemType.d.ts +9 -7
  45. package/dist/react-native/RaftSystemUtils.js +3 -1
  46. package/dist/react-native/RaftSystemUtils.js.map +1 -1
  47. package/dist/react-native/RaftUpdateManager.js +2 -2
  48. package/dist/react-native/RaftUpdateManager.js.map +1 -1
  49. package/dist/react-native/RaftUtils.d.ts +2 -0
  50. package/dist/react-native/RaftUtils.js +22 -2
  51. package/dist/react-native/RaftUtils.js.map +1 -1
  52. package/dist/react-native/main.d.ts +2 -0
  53. package/dist/react-native/main.js +4 -1
  54. package/dist/react-native/main.js.map +1 -1
  55. package/dist/web/RaftAttributeHandler.d.ts +2 -0
  56. package/dist/web/RaftAttributeHandler.js +136 -10
  57. package/dist/web/RaftAttributeHandler.js.map +1 -1
  58. package/dist/web/RaftChannel.d.ts +2 -0
  59. package/dist/web/RaftChannelBLE.web.d.ts +2 -0
  60. package/dist/web/RaftChannelBLE.web.js +24 -15
  61. package/dist/web/RaftChannelBLE.web.js.map +1 -1
  62. package/dist/web/RaftChannelSimulated.d.ts +32 -0
  63. package/dist/web/RaftChannelSimulated.js +418 -0
  64. package/dist/web/RaftChannelSimulated.js.map +1 -0
  65. package/dist/web/RaftChannelWebSerial.d.ts +2 -0
  66. package/dist/web/RaftChannelWebSerial.js +18 -9
  67. package/dist/web/RaftChannelWebSerial.js.map +1 -1
  68. package/dist/web/RaftChannelWebSocket.d.ts +2 -0
  69. package/dist/web/RaftChannelWebSocket.js +10 -0
  70. package/dist/web/RaftChannelWebSocket.js.map +1 -1
  71. package/dist/web/RaftConnector.js +13 -6
  72. package/dist/web/RaftConnector.js.map +1 -1
  73. package/dist/web/RaftCustomAttrHandler.js +15 -0
  74. package/dist/web/RaftCustomAttrHandler.js.map +1 -1
  75. package/dist/web/RaftDeviceInfo.d.ts +8 -1
  76. package/dist/web/RaftDeviceInfo.js +17 -3
  77. package/dist/web/RaftDeviceInfo.js.map +1 -1
  78. package/dist/web/RaftDeviceManager.d.ts +3 -0
  79. package/dist/web/RaftDeviceManager.js +123 -43
  80. package/dist/web/RaftDeviceManager.js.map +1 -1
  81. package/dist/web/RaftFileHandler.js +8 -8
  82. package/dist/web/RaftFileHandler.js.map +1 -1
  83. package/dist/web/RaftLog.js +1 -1
  84. package/dist/web/RaftLog.js.map +1 -1
  85. package/dist/web/RaftMsgHandler.d.ts +5 -0
  86. package/dist/web/RaftMsgHandler.js +32 -0
  87. package/dist/web/RaftMsgHandler.js.map +1 -1
  88. package/dist/web/RaftStreamHandler.js +6 -5
  89. package/dist/web/RaftStreamHandler.js.map +1 -1
  90. package/dist/web/RaftStruct.js +197 -147
  91. package/dist/web/RaftStruct.js.map +1 -1
  92. package/dist/web/RaftSysTypeManager.d.ts +2 -0
  93. package/dist/web/RaftSysTypeManager.js +25 -0
  94. package/dist/web/RaftSysTypeManager.js.map +1 -1
  95. package/dist/web/RaftSystemType.d.ts +9 -7
  96. package/dist/web/RaftSystemUtils.js +3 -1
  97. package/dist/web/RaftSystemUtils.js.map +1 -1
  98. package/dist/web/RaftUpdateManager.js +2 -2
  99. package/dist/web/RaftUpdateManager.js.map +1 -1
  100. package/dist/web/RaftUtils.d.ts +2 -0
  101. package/dist/web/RaftUtils.js +22 -2
  102. package/dist/web/RaftUtils.js.map +1 -1
  103. package/dist/web/main.d.ts +2 -0
  104. package/dist/web/main.js +4 -1
  105. package/dist/web/main.js.map +1 -1
  106. package/examples/dashboard/package.json +1 -1
  107. package/examples/dashboard/src/CommandPanel.tsx +3 -3
  108. package/examples/dashboard/src/ConnManager.ts +83 -6
  109. package/examples/dashboard/src/DeviceActionsForm.tsx +2 -2
  110. package/examples/dashboard/src/DevicePanel.tsx +2 -2
  111. package/examples/dashboard/src/Main.tsx +14 -4
  112. package/examples/dashboard/src/SystemTypeMarty/RICSystemUtils.ts +4 -4
  113. package/examples/dashboard/src/styles.css +8 -0
  114. package/examples/dashboard/tsconfig.json +1 -1
  115. package/package.json +10 -11
  116. package/src/RaftAttributeHandler.ts +163 -11
  117. package/src/RaftChannel.ts +2 -0
  118. package/src/RaftChannelBLE.native.ts +17 -8
  119. package/src/RaftChannelBLE.web.ts +28 -16
  120. package/src/RaftChannelSimulated.ts +482 -0
  121. package/src/RaftChannelWebSerial.ts +18 -7
  122. package/src/RaftChannelWebSocket.ts +13 -0
  123. package/src/RaftConnector.ts +13 -7
  124. package/src/RaftCustomAttrHandler.ts +17 -0
  125. package/src/RaftDeviceInfo.ts +27 -5
  126. package/src/RaftDeviceManager.ts +155 -47
  127. package/src/RaftFileHandler.ts +8 -8
  128. package/src/RaftLog.ts +1 -1
  129. package/src/RaftMsgHandler.ts +36 -0
  130. package/src/RaftStreamHandler.ts +48 -47
  131. package/src/RaftStruct.ts +220 -147
  132. package/src/RaftSysTypeManager.ts +27 -0
  133. package/src/RaftSystemType.ts +9 -7
  134. package/src/RaftSystemUtils.ts +3 -1
  135. package/src/RaftUpdateManager.ts +2 -2
  136. package/src/RaftUtils.ts +25 -5
  137. package/src/main.ts +2 -0
@@ -35,12 +35,14 @@ export default function Main() {
35
35
  localStorage.getItem('lastIpAddress') || ''
36
36
  );
37
37
 
38
+ const [serialNo, setSerialNo] = useState<string>('');
39
+
38
40
  const handleConnect = () => {
39
41
  if (ipAddress.trim() === '') {
40
- console.error('No IP address entered');
42
+ console.warn('No IP address entered');
41
43
  return;
42
44
  }
43
- connManager.connect('WebSocket', ipAddress, []);
45
+ connManager.connect('WebSocket', ipAddress, [], null);
44
46
  localStorage.setItem('lastIpAddress', ipAddress);
45
47
  };
46
48
 
@@ -193,10 +195,18 @@ export default function Main() {
193
195
  </div>
194
196
  <div className="info-box">
195
197
  <h3>WebBLE</h3>
198
+ <input
199
+ className="serial-no-input"
200
+ id="serial-no"
201
+ type="text"
202
+ placeholder="Serial No (ignored if empty)"
203
+ value={serialNo}
204
+ onChange={(e) => setSerialNo(e.target.value)}
205
+ />
196
206
  <button
197
207
  className="action-button"
198
208
  onClick={() => {
199
- connManager.connect('WebBLE', '', sysTypeManager.getAllServiceUUIDs());
209
+ connManager.connect('WebBLE', '', sysTypeManager.getAllServiceUUIDs(), serialNo);
200
210
  }}
201
211
  >
202
212
  Connect
@@ -207,7 +217,7 @@ export default function Main() {
207
217
  <button
208
218
  className="action-button"
209
219
  onClick={() => {
210
- connManager.connect('WebSerial', '', []);
220
+ connManager.connect('WebSerial', '', [], null);
211
221
  }}
212
222
  >
213
223
  Connect
@@ -98,7 +98,7 @@ export default class RICSystemUtils {
98
98
  const retrieveResult = await this.retrieveInfo();
99
99
  return retrieveResult;
100
100
  } catch (err) {
101
- RaftLog.error(`retrieveMartySystemInfo: error ${err}`);
101
+ RaftLog.warn(`retrieveMartySystemInfo: error ${err}`);
102
102
  }
103
103
  return false;
104
104
  }
@@ -260,7 +260,7 @@ export default class RICSystemUtils {
260
260
  // Debug
261
261
  RaftLog.debug(
262
262
  `getHWElemList: found ${hwElemList.hw.length} addons/buspixels`
263
-
263
+ );
264
264
  } else if (addToNonAddOnsList) {
265
265
  this._hwElemsExcludingAddOns.push(...hwElemList.hw);
266
266
  // Debug
@@ -279,9 +279,9 @@ export default class RICSystemUtils {
279
279
  try {
280
280
  const reports: Array<RaftReportMsg> = [];
281
281
  // add callback to subscribe to report messages
282
- this._msgHandler.reportMsgCallbacksSet("getHWElemCB", function (
282
+ this._msgHandler.reportMsgCallbacksSet("getHWElemCB", async function (
283
283
  report: RaftReportMsg
284
- ) {
284
+ ): Promise<void> {
285
285
  reports.push(report);
286
286
  RaftLog.debug(`getHWElemCB Report callback ${JSON.stringify(report)}`);
287
287
  });
@@ -121,6 +121,14 @@ h1 {
121
121
  width: 100%;
122
122
  }
123
123
 
124
+ .serial-no-input {
125
+ border: 1px solid #ccc;
126
+ border-radius: 4px;
127
+ margin-bottom: 10px;
128
+ padding: 10px;
129
+ width: 100%;
130
+ }
131
+
124
132
  .command-input {
125
133
  padding: 10px;
126
134
  margin-bottom: 10px;
@@ -4,7 +4,7 @@
4
4
  "esModuleInterop": true,
5
5
  "resolveJsonModule": true,
6
6
  "module": "esnext",
7
- "target": "es5",
7
+ "target": "es2017",
8
8
  "strict": true,
9
9
  "moduleResolution": "node",
10
10
  "lib": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robotical/raftjs",
3
- "version": "1.4.7",
3
+ "version": "2.0.4",
4
4
  "description": "Javascript/TS library for Raft library",
5
5
  "main": "dist/web/main.js",
6
6
  "types": "dist/web/main.d.ts",
@@ -31,19 +31,18 @@
31
31
  "watch-all": "tsc -p tsconfig.json --watch & tsc -p tsconfig.react-native.json --watch"
32
32
  },
33
33
  "devDependencies": {
34
- "@types/node": "^20.14.0",
35
- "@types/web-bluetooth": "^0.0.20",
36
- "@typescript-eslint/eslint-plugin": "^8.6.0",
37
- "eslint": "^9.4.0",
38
- "react-native-ble-plx": "^3.2.0",
39
- "typescript": "^5.4.5",
40
- "@types/text-encoding": "^0.0.39",
41
- "rimraf": "^6.0.1"
34
+ "@types/node": "^22.13.11",
35
+ "@types/web-bluetooth": "^0.0.21",
36
+ "@typescript-eslint/eslint-plugin": "^8.27.0",
37
+ "eslint": "^9.23.0",
38
+ "react-native-ble-plx": "^3.5.0",
39
+ "typescript": "^5.8.2",
40
+ "rimraf": "^6.0.1",
41
+ "@types/text-encoding": "^0.0.40"
42
42
  },
43
43
  "dependencies": {
44
- "text-encoding": "^0.7.0",
45
44
  "isomorphic-ws": "^5.0.0",
46
- "tslib": "^2.6.2"
45
+ "tslib": "^2.8.1"
47
46
  },
48
47
  "peerDependencies": {
49
48
  "react-native-ble-plx": "*",
@@ -45,6 +45,8 @@ export default class AttributeHandler {
45
45
 
46
46
  } else {
47
47
 
48
+ // console.log(`RaftAttrHdlr.processMsgAttrGroup ${JSON.stringify(pollRespMetadata)} msgBufIdx ${msgBufIdx} timestampUs ${timestampUs}`);
49
+
48
50
  // Iterate over attributes
49
51
  for (let attrIdx = 0; attrIdx < pollRespMetadata.a.length; attrIdx++) {
50
52
 
@@ -56,6 +58,8 @@ export default class AttributeHandler {
56
58
  continue;
57
59
  }
58
60
 
61
+ // console.log(`RaftAttrHdlr.processMsgAttrGroup attr ${attrDef.n} msgBufIdx ${msgBufIdx} timestampUs ${timestampUs} attrDef ${JSON.stringify(attrDef)}`);
62
+
59
63
  // Process the attribute
60
64
  const { values, newMsgBufIdx } = this.processMsgAttribute(attrDef, msgBuffer, msgBufIdx, msgDataStartIdx);
61
65
  if (newMsgBufIdx < 0) {
@@ -65,7 +69,6 @@ export default class AttributeHandler {
65
69
  msgBufIdx = newMsgBufIdx;
66
70
  newAttrValues.push(values);
67
71
  }
68
-
69
72
  }
70
73
 
71
74
  // Number of bytes in group
@@ -142,19 +145,94 @@ export default class AttributeHandler {
142
145
  // Add the new timestamps
143
146
  deviceTimeline.timestampsUs.push(...timestampsUs);
144
147
 
148
+ // Validate attributes based on the vft field
149
+ this.validateAttributes(pollRespMetadata, devAttrsState, numNewDataPoints);
150
+
145
151
  // Return the next message buffer index
146
152
  return msgDataStartIdx+pollRespSizeBytes;
147
153
  }
148
154
 
155
+ private validateAttributes(pollRespMetadata: DeviceTypePollRespMetadata, devAttrsState: DeviceAttributesState, numNewDataPoints: number): void {
156
+ // Iterate through all attributes to find those with a vft field
157
+ for (let attrIdx = 0; attrIdx < pollRespMetadata.a.length; attrIdx++) {
158
+ const attrDef: DeviceTypeAttribute = pollRespMetadata.a[attrIdx];
159
+
160
+ // Check if this attribute has a vft field
161
+ if (!("vft" in attrDef) || !attrDef.vft) {
162
+ continue;
163
+ }
164
+
165
+ // Get the name of the validating attribute
166
+ const validatingAttrName = attrDef.vft;
167
+
168
+ // Check if the validating attribute exists in the state
169
+ if (!(validatingAttrName in devAttrsState)) {
170
+ console.debug(`Cannot validate attribute ${attrDef.n} as validating attribute ${validatingAttrName} doesn't exist`);
171
+ continue;
172
+ }
173
+
174
+ // Get the current attribute state
175
+ const currentAttr = devAttrsState[attrDef.n];
176
+ const validatingAttr = devAttrsState[validatingAttrName];
177
+
178
+ // Check if both attributes have values
179
+ if (!currentAttr.values.length || !validatingAttr.values.length) {
180
+ continue;
181
+ }
182
+
183
+ // Get the most recent values from both attributes
184
+ const numValues = currentAttr.values.length;
185
+ const startIdx = numValues - numNewDataPoints;
186
+
187
+ // Process each of the new values
188
+ for (let i = 0; i < numNewDataPoints; i++) {
189
+ const valueIdx = startIdx + i;
190
+ if (valueIdx >= 0 && valueIdx < numValues) {
191
+ // Check if the validating attribute's value is 0/false at the same index
192
+ const validatingValueIdx = validatingAttr.values.length - numNewDataPoints + i;
193
+ if (validatingValueIdx >= 0 && validatingValueIdx < validatingAttr.values.length) {
194
+ // If the validating attribute's value is 0 or false, mark the current value as invalid
195
+ if (!validatingAttr.values[validatingValueIdx]) {
196
+ currentAttr.values[valueIdx] = NaN; // Using NaN to represent invalid values
197
+ }
198
+ }
199
+ }
200
+ }
201
+ }
202
+ }
203
+
149
204
  private processMsgAttribute(attrDef: DeviceTypeAttribute, msgBuffer: Uint8Array, msgBufIdx: number, msgDataStartIdx: number): { values: number[], newMsgBufIdx: number} {
150
205
 
151
206
  // Current field message string index
152
207
  let curFieldBufIdx = msgBufIdx;
153
208
  let attrUsesAbsPos = false;
154
209
 
155
- // Check for "at": N which means start reading from byte N of the message (after the timestamp bytes)
210
+ // Check for "at" field which means absolute position in the buffer
156
211
  if (attrDef.at !== undefined) {
157
- curFieldBufIdx = msgDataStartIdx + attrDef.at;
212
+ // Handle both single value and array of byte positions
213
+ if (Array.isArray(attrDef.at)) {
214
+ // Create a new buffer for non-contiguous data extraction
215
+ const elemSize = structSizeOf(attrDef.t);
216
+ const bytesForType = new Uint8Array(elemSize);
217
+
218
+ // Zero out the buffer
219
+ bytesForType.fill(0);
220
+
221
+ // Copy bytes from the specified positions
222
+ for (let i = 0; i < attrDef.at.length && i < elemSize; i++) {
223
+ const sourceIdx = msgDataStartIdx + attrDef.at[i];
224
+ if (sourceIdx < msgBuffer.length) {
225
+ bytesForType[i] = msgBuffer[sourceIdx];
226
+ }
227
+ }
228
+
229
+ // Use this buffer for attribute extraction
230
+ msgBuffer = bytesForType;
231
+ curFieldBufIdx = 0;
232
+ } else {
233
+ // Standard absolute position in the buffer
234
+ curFieldBufIdx = msgDataStartIdx + attrDef.at;
235
+ }
158
236
  attrUsesAbsPos = true;
159
237
  }
160
238
 
@@ -194,13 +272,13 @@ export default class AttributeHandler {
194
272
  // Check for XOR mask
195
273
  if ("x" in attrDef) {
196
274
  const mask = typeof attrDef.x === "string" ? parseInt(attrDef.x, 16) : attrDef.x as number;
197
- attrValues = attrValues.map((value) => value ^ mask);
275
+ attrValues = attrValues.map((value) => (value >>> 0) ^ mask);
198
276
  }
199
277
 
200
278
  // Check for AND mask
201
279
  if ("m" in attrDef) {
202
280
  const mask = typeof attrDef.m === "string" ? parseInt(attrDef.m, 16) : attrDef.m as number;
203
- attrValues = attrValues.map((value) => (maskOnSignedValue ? this.signExtend(value, mask) : value & mask));
281
+ attrValues = attrValues.map((value) => (maskOnSignedValue ? this.signExtend(value, mask) : (value >>> 0) & mask));
204
282
  }
205
283
 
206
284
  // Check for a sign-bit
@@ -219,9 +297,9 @@ export default class AttributeHandler {
219
297
  if ("s" in attrDef && attrDef.s) {
220
298
  const bitshift = attrDef.s as number;
221
299
  if (bitshift > 0) {
222
- attrValues = attrValues.map((value) => (value) >> bitshift);
300
+ attrValues = attrValues.map((value) => (value >>> 0) >>> bitshift);
223
301
  } else if (bitshift < 0) {
224
- attrValues = attrValues.map((value) => (value) << -bitshift);
302
+ attrValues = attrValues.map((value) => (value >>> 0) << -bitshift);
225
303
  }
226
304
  }
227
305
 
@@ -237,10 +315,47 @@ export default class AttributeHandler {
237
315
  attrValues = attrValues.map((value) => (value) + addValue);
238
316
  }
239
317
 
240
- // console.log(`DeviceManager msg attrGroup ${attrGroup} devkey ${deviceKey} valueHexChars ${valueHexChars} msgHexStr ${msgHexStr} ts ${timestamp} attrName ${attrDef.n} type ${attrDef.t} value ${value} signExtendableMaskSignPos ${signExtendableMaskSignPos} attrTypeDefForStruct ${attrTypeDefForStruct} attrDef ${attrDef}`);
318
+ // Apply lookup table if defined
319
+ if ("lut" in attrDef && attrDef.lut !== undefined) {
320
+ attrValues = attrValues.map((value): number => {
321
+ // Skip NaN values
322
+ if (isNaN(value)) {
323
+ return value;
324
+ }
325
+
326
+ // Search through the lookup table rows for a match
327
+ let defaultValue: number | null = null;
328
+
329
+ for (const row of attrDef.lut || []) {
330
+ // Empty string means default for unmatched values
331
+ if (row.r === "") {
332
+ defaultValue = row.v;
333
+ continue;
334
+ }
335
+
336
+ // Parse the range string
337
+ if (this.isValueInRangeString(value, row.r)) {
338
+ return row.v;
339
+ }
340
+ }
341
+
342
+ // If no match found but we have a default, use it
343
+ if (defaultValue !== null) {
344
+ return defaultValue;
345
+ }
346
+
347
+ // Otherwise keep the original value
348
+ return value;
349
+ });
350
+ }
351
+
352
+ // const msgBufIdxIn = msgBufIdx;
353
+
241
354
  // Move buffer position if using relative positioning
242
355
  msgBufIdx += attrUsesAbsPos ? 0 : numBytesConsumed;
243
356
 
357
+ // console.log(`RaftAttrHdlr.processMsgAttr attr ${attrDef.n} msgBufIdx ${msgBufIdxIn} msgBufIdx ${msgBufIdx} attrUsesAbsPos ${attrUsesAbsPos} numBytesConsumed ${numBytesConsumed} attrValues ${attrValues}`);
358
+
244
359
  // if (attrDef.n === "amb0") {
245
360
  // console.log(`${new Date().toISOString()} ${attrDef.n} ${attrValues}`);
246
361
  // }
@@ -264,7 +379,7 @@ export default class AttributeHandler {
264
379
  private extractTimestampAndAdvanceIdx(msgBuffer: Uint8Array, msgBufIdx: number, timestampWrapHandler: DeviceTimeline):
265
380
  { newBufIdx: number, timestampUs: number } {
266
381
 
267
- // Check there are enough characters for the timestamp
382
+ // Check there are enough bytes for the timestamp
268
383
  if (msgBufIdx + this.POLL_RESULT_TIMESTAMP_SIZE > msgBuffer.length) {
269
384
  return { newBufIdx: -1, timestampUs: 0 };
270
385
  }
@@ -278,8 +393,8 @@ export default class AttributeHandler {
278
393
  timestampUs = structUnpack(">I", tsBuffer)[0] as number * this.POLL_RESULT_RESOLUTION_US;
279
394
  }
280
395
 
281
- // Check if time is before lastReportTimeMs - in which case a wrap around occurred to add on the max value
282
- if (timestampUs < timestampWrapHandler.lastReportTimestampUs) {
396
+ // Check if time is before lastReportTimeMs by more than 100ms - in which case a wrap around occurred to add on the max value
397
+ if (timestampUs + 100000 < timestampWrapHandler.lastReportTimestampUs ) {
283
398
  timestampWrapHandler.reportTimestampOffsetUs += this.POLL_RESULT_WRAP_VALUE * this.POLL_RESULT_RESOLUTION_US;
284
399
  }
285
400
  timestampWrapHandler.lastReportTimestampUs = timestampUs;
@@ -294,5 +409,42 @@ export default class AttributeHandler {
294
409
  return { newBufIdx: msgBufIdx, timestampUs: timestampUs };
295
410
  }
296
411
 
412
+ // Helper method to check if a value is in a range string like "42,43,44-45,47"
413
+ private isValueInRangeString(value: number, rangeStr: string): boolean {
414
+ // Round to integer for comparison
415
+ const roundedValue = Math.round(value);
416
+
417
+ // Split the range string by commas
418
+ const parts = rangeStr.split(',');
419
+
420
+ for (const part of parts) {
421
+ // Check if it's a range (contains a hyphen)
422
+ if (part.includes('-')) {
423
+ const [startStr, endStr] = part.split('-');
424
+
425
+ // Handle hex values
426
+ const start = startStr.toLowerCase().startsWith('0x') ?
427
+ parseInt(startStr, 16) : parseInt(startStr, 10);
428
+ const end = endStr.toLowerCase().startsWith('0x') ?
429
+ parseInt(endStr, 16) : parseInt(endStr, 10);
430
+
431
+ if (!isNaN(start) && !isNaN(end) && roundedValue >= start && roundedValue <= end) {
432
+ return true;
433
+ }
434
+ }
435
+ // Check if it's a single value
436
+ else {
437
+ // Handle hex values
438
+ const partValue = part.toLowerCase().startsWith('0x') ?
439
+ parseInt(part, 16) : parseInt(part, 10);
440
+
441
+ if (!isNaN(partValue) && roundedValue === partValue) {
442
+ return true;
443
+ }
444
+ }
445
+ }
446
+
447
+ return false;
448
+ }
297
449
 
298
450
  }
@@ -22,6 +22,8 @@ export default interface RaftChannel
22
22
  setMsgHandler(raftMsgHandler: RaftMsgHandler): void;
23
23
  sendTxMsg(msg: Uint8Array, sendWithResponse: boolean): Promise<boolean>;
24
24
  sendTxMsgNoAwait(msg: Uint8Array, sendWithResponse: boolean): Promise<boolean>;
25
+ sendTxMsgRaw(msg: string): boolean;
26
+ sendTxMsgRawAndWaitForReply<T>(msgPayload: Uint8Array): T;
25
27
  requiresSubscription(): boolean;
26
28
  ricRestCmdBeforeDisconnect(): string | null;
27
29
  fhBatchAckSize(): number;
@@ -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
  }