@robdobsn/raftjs 1.1.1

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 (149) hide show
  1. package/.editorconfig +14 -0
  2. package/.gitattributes +11 -0
  3. package/.nvmrc +1 -0
  4. package/LICENSE +22 -0
  5. package/README.md +11 -0
  6. package/TODO.md +1 -0
  7. package/dist/RaftAttributeHandler.d.ts +12 -0
  8. package/dist/RaftAttributeHandler.js +241 -0
  9. package/dist/RaftAttributeHandler.js.map +1 -0
  10. package/dist/RaftChannel.d.ts +18 -0
  11. package/dist/RaftChannel.js +12 -0
  12. package/dist/RaftChannel.js.map +1 -0
  13. package/dist/RaftChannelWebBLE.d.ts +38 -0
  14. package/dist/RaftChannelWebBLE.js +274 -0
  15. package/dist/RaftChannelWebBLE.js.map +1 -0
  16. package/dist/RaftChannelWebSerial.d.ts +37 -0
  17. package/dist/RaftChannelWebSerial.js +319 -0
  18. package/dist/RaftChannelWebSerial.js.map +1 -0
  19. package/dist/RaftChannelWebSocket.d.ts +28 -0
  20. package/dist/RaftChannelWebSocket.js +197 -0
  21. package/dist/RaftChannelWebSocket.js.map +1 -0
  22. package/dist/RaftCommsStats.d.ts +39 -0
  23. package/dist/RaftCommsStats.js +128 -0
  24. package/dist/RaftCommsStats.js.map +1 -0
  25. package/dist/RaftConnEvents.d.ts +31 -0
  26. package/dist/RaftConnEvents.js +44 -0
  27. package/dist/RaftConnEvents.js.map +1 -0
  28. package/dist/RaftConnector.d.ts +242 -0
  29. package/dist/RaftConnector.js +613 -0
  30. package/dist/RaftConnector.js.map +1 -0
  31. package/dist/RaftCustomAttrHandler.d.ts +4 -0
  32. package/dist/RaftCustomAttrHandler.js +50 -0
  33. package/dist/RaftCustomAttrHandler.js.map +1 -0
  34. package/dist/RaftDeviceInfo.d.ts +64 -0
  35. package/dist/RaftDeviceInfo.js +36 -0
  36. package/dist/RaftDeviceInfo.js.map +1 -0
  37. package/dist/RaftDeviceManager.d.ts +37 -0
  38. package/dist/RaftDeviceManager.js +450 -0
  39. package/dist/RaftDeviceManager.js.map +1 -0
  40. package/dist/RaftDeviceMsg.d.ts +9 -0
  41. package/dist/RaftDeviceMsg.js +11 -0
  42. package/dist/RaftDeviceMsg.js.map +1 -0
  43. package/dist/RaftDeviceStates.d.ts +33 -0
  44. package/dist/RaftDeviceStates.js +60 -0
  45. package/dist/RaftDeviceStates.js.map +1 -0
  46. package/dist/RaftFileHandler.d.ts +52 -0
  47. package/dist/RaftFileHandler.js +502 -0
  48. package/dist/RaftFileHandler.js.map +1 -0
  49. package/dist/RaftLog.d.ts +22 -0
  50. package/dist/RaftLog.js +63 -0
  51. package/dist/RaftLog.js.map +1 -0
  52. package/dist/RaftMiniHDLC.d.ts +18 -0
  53. package/dist/RaftMiniHDLC.js +383 -0
  54. package/dist/RaftMiniHDLC.js.map +1 -0
  55. package/dist/RaftMsgHandler.d.ts +57 -0
  56. package/dist/RaftMsgHandler.js +480 -0
  57. package/dist/RaftMsgHandler.js.map +1 -0
  58. package/dist/RaftMsgTrackInfo.d.ts +17 -0
  59. package/dist/RaftMsgTrackInfo.js +42 -0
  60. package/dist/RaftMsgTrackInfo.js.map +1 -0
  61. package/dist/RaftProtocolDefs.d.ts +30 -0
  62. package/dist/RaftProtocolDefs.js +48 -0
  63. package/dist/RaftProtocolDefs.js.map +1 -0
  64. package/dist/RaftStreamHandler.d.ts +38 -0
  65. package/dist/RaftStreamHandler.js +257 -0
  66. package/dist/RaftStreamHandler.js.map +1 -0
  67. package/dist/RaftSystemType.d.ts +21 -0
  68. package/dist/RaftSystemType.js +3 -0
  69. package/dist/RaftSystemType.js.map +1 -0
  70. package/dist/RaftSystemUtils.d.ts +136 -0
  71. package/dist/RaftSystemUtils.js +410 -0
  72. package/dist/RaftSystemUtils.js.map +1 -0
  73. package/dist/RaftTypes.d.ts +184 -0
  74. package/dist/RaftTypes.js +157 -0
  75. package/dist/RaftTypes.js.map +1 -0
  76. package/dist/RaftUpdateEvents.d.ts +33 -0
  77. package/dist/RaftUpdateEvents.js +46 -0
  78. package/dist/RaftUpdateEvents.js.map +1 -0
  79. package/dist/RaftUpdateManager.d.ts +61 -0
  80. package/dist/RaftUpdateManager.js +618 -0
  81. package/dist/RaftUpdateManager.js.map +1 -0
  82. package/dist/RaftUtils.d.ts +125 -0
  83. package/dist/RaftUtils.js +454 -0
  84. package/dist/RaftUtils.js.map +1 -0
  85. package/dist/RaftWifiTypes.d.ts +23 -0
  86. package/dist/RaftWifiTypes.js +43 -0
  87. package/dist/RaftWifiTypes.js.map +1 -0
  88. package/dist/TestDataGen.d.ts +7 -0
  89. package/dist/TestDataGen.js +133 -0
  90. package/dist/TestDataGen.js.map +1 -0
  91. package/dist/main.d.ts +18 -0
  92. package/dist/main.js +42 -0
  93. package/dist/main.js.map +1 -0
  94. package/eslint.config.mjs +33 -0
  95. package/examples/dashboard/package.json +39 -0
  96. package/examples/dashboard/src/ConnManager.ts +86 -0
  97. package/examples/dashboard/src/Main.tsx +100 -0
  98. package/examples/dashboard/src/StatusScreen.tsx +72 -0
  99. package/examples/dashboard/src/SystemTypeCog/CogStateInfo.ts +144 -0
  100. package/examples/dashboard/src/SystemTypeCog/SystemTypeCog.ts +77 -0
  101. package/examples/dashboard/src/SystemTypeMarty/RICAddOn.ts +70 -0
  102. package/examples/dashboard/src/SystemTypeMarty/RICAddOnBase.ts +33 -0
  103. package/examples/dashboard/src/SystemTypeMarty/RICAddOnManager.ts +342 -0
  104. package/examples/dashboard/src/SystemTypeMarty/RICCommsStats.ts +170 -0
  105. package/examples/dashboard/src/SystemTypeMarty/RICHWElem.ts +123 -0
  106. package/examples/dashboard/src/SystemTypeMarty/RICLEDPatternChecker.ts +207 -0
  107. package/examples/dashboard/src/SystemTypeMarty/RICROSSerial.ts +464 -0
  108. package/examples/dashboard/src/SystemTypeMarty/RICServoFaultDetector.ts +146 -0
  109. package/examples/dashboard/src/SystemTypeMarty/RICStateInfo.ts +32 -0
  110. package/examples/dashboard/src/SystemTypeMarty/RICSystemUtils.ts +371 -0
  111. package/examples/dashboard/src/SystemTypeMarty/RICTypes.ts +20 -0
  112. package/examples/dashboard/src/SystemTypeMarty/SystemTypeMarty.ts +113 -0
  113. package/examples/dashboard/src/index.html +15 -0
  114. package/examples/dashboard/src/index.tsx +15 -0
  115. package/examples/dashboard/src/styles.css +122 -0
  116. package/examples/dashboard/tsconfig.json +18 -0
  117. package/jest.config.js +11 -0
  118. package/package.json +50 -0
  119. package/src/RaftAttributeHandler.ts +289 -0
  120. package/src/RaftChannel.ts +30 -0
  121. package/src/RaftChannelWebBLE.ts +342 -0
  122. package/src/RaftChannelWebSerial.ts +408 -0
  123. package/src/RaftChannelWebSocket.ts +245 -0
  124. package/src/RaftCommsStats.ts +142 -0
  125. package/src/RaftConnEvents.ts +46 -0
  126. package/src/RaftConnector.ts +745 -0
  127. package/src/RaftCustomAttrHandler.ts +54 -0
  128. package/src/RaftDeviceInfo.ts +104 -0
  129. package/src/RaftDeviceManager.ts +542 -0
  130. package/src/RaftDeviceMsg.ts +20 -0
  131. package/src/RaftDeviceStates.ts +89 -0
  132. package/src/RaftFileHandler.ts +668 -0
  133. package/src/RaftLog.ts +70 -0
  134. package/src/RaftMiniHDLC.ts +396 -0
  135. package/src/RaftMsgHandler.ts +778 -0
  136. package/src/RaftMsgTrackInfo.ts +51 -0
  137. package/src/RaftProtocolDefs.ts +46 -0
  138. package/src/RaftStreamHandler.ts +328 -0
  139. package/src/RaftSystemType.ts +25 -0
  140. package/src/RaftSystemUtils.ts +487 -0
  141. package/src/RaftTypes.ts +250 -0
  142. package/src/RaftUpdateEvents.ts +48 -0
  143. package/src/RaftUpdateManager.ts +778 -0
  144. package/src/RaftUtils.ts +484 -0
  145. package/src/RaftWifiTypes.ts +36 -0
  146. package/src/TestDataGen.ts +157 -0
  147. package/src/main.ts +28 -0
  148. package/testdata/TestDeviceTypeRecs.json +492 -0
  149. package/tsconfig.json +27 -0
@@ -0,0 +1,464 @@
1
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2
+ //
3
+ // RICJS
4
+ // Communications Library
5
+ //
6
+ // Rob Dobson & Chris Greening 2020-2022
7
+ // (C) 2020-2022
8
+ //
9
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
10
+
11
+ import RaftLog from "../../../../src/RaftLog";
12
+ import RaftUtils from "../../../../src/RaftUtils";
13
+ import RICAddOnManager from "./RICAddOnManager";
14
+ import RICCommsStats from "./RICCommsStats";
15
+ import { RICStateInfo } from "./RICStateInfo";
16
+
17
+ export class ROSSerialSmartServos {
18
+ smartServos: {
19
+ id: number;
20
+ pos: number;
21
+ current: number;
22
+ status: number;
23
+ }[] = [];
24
+ }
25
+
26
+ export class ROSSerialIMU {
27
+ accel: {
28
+ x: number;
29
+ y: number;
30
+ z: number;
31
+ } = { x: 0, y: 0, z: 0 };
32
+ }
33
+
34
+ export class ROSSerialPowerStatus {
35
+ powerStatus: {
36
+ battRemainCapacityPercent: number;
37
+ battTempDegC: number;
38
+ battRemainCapacityMAH: number;
39
+ battFullCapacityMAH: number;
40
+ battCurrentMA: number;
41
+ power5VOnTimeSecs: number;
42
+ power5VIsOn: boolean;
43
+ powerUSBIsConnected: boolean;
44
+ battInfoValid: boolean;
45
+ powerUSBIsValid: boolean;
46
+ powerFlags: number;
47
+ } = {
48
+ battRemainCapacityPercent: 0,
49
+ battTempDegC: 0,
50
+ battRemainCapacityMAH: 0,
51
+ battFullCapacityMAH: 0,
52
+ battCurrentMA: 0,
53
+ power5VOnTimeSecs: 0,
54
+ power5VIsOn: false,
55
+ powerUSBIsConnected: false,
56
+ battInfoValid: false,
57
+ powerUSBIsValid: false,
58
+ powerFlags: 0,
59
+ };
60
+ }
61
+
62
+ export class ROSSerialAddOnStatus {
63
+ id = 0;
64
+ deviceTypeID = 0;
65
+ whoAmI = "";
66
+ name = "";
67
+ status = 0;
68
+ vals: { [key: string]: number | boolean | string} = {};
69
+ }
70
+
71
+ export class ROSSerialAddOnStatusList {
72
+ addons: Array<ROSSerialAddOnStatus> = new Array<ROSSerialAddOnStatus>();
73
+ }
74
+
75
+ export class ROSSerialRGBT {
76
+ r = 0;
77
+ g = 0;
78
+ b = 0;
79
+ t = 0;
80
+ constructor(r: number, g: number, b: number, t: number) {
81
+ this.r = r;
82
+ this.g = g;
83
+ this.b = b;
84
+ this.t = t;
85
+ }
86
+ toString() {
87
+ return `R:${this.r} G:${this.g} B:${this.b} T:${this.t}`;
88
+ }
89
+ }
90
+
91
+ export class ROSSerialRobotStatus {
92
+ robotStatus: {
93
+ flags: number;
94
+ isMoving: boolean;
95
+ isPaused: boolean;
96
+ isFwUpdating: boolean;
97
+ workQCount: number;
98
+ heapFree: number;
99
+ heapMin: number;
100
+ pixRGBT: ROSSerialRGBT[];
101
+ loopMsAvg: number;
102
+ loopMsMax: number;
103
+ wifiRSSI: number;
104
+ bleRSSI: number;
105
+ } = {
106
+ flags: 0,
107
+ isMoving: false,
108
+ isPaused: false,
109
+ isFwUpdating: false,
110
+ workQCount: 0,
111
+ heapFree: 0,
112
+ heapMin: 0,
113
+ pixRGBT: [],
114
+ loopMsAvg: 0,
115
+ loopMsMax: 0,
116
+ wifiRSSI: 0,
117
+ bleRSSI: 0,
118
+ };
119
+ }
120
+
121
+ export class ROSCameraData {
122
+ cameraData: {
123
+ unixTimeMs: number;
124
+ sinceStartMs: number;
125
+ imageData: Uint8Array;
126
+ imageWidth: number,
127
+ imageHeight: number,
128
+ imageFormat: number,
129
+ imageQuality: number,
130
+ frameTimeMs: number,
131
+ imageCount: number
132
+ } = {
133
+ unixTimeMs: 0,
134
+ sinceStartMs: 0,
135
+ imageData: new Uint8Array(0),
136
+ imageWidth: 0,
137
+ imageHeight: 0,
138
+ imageFormat: 0,
139
+ imageQuality: 0,
140
+ frameTimeMs: 0,
141
+ imageCount: 0
142
+ };
143
+ }
144
+
145
+ export type ROSSerialMsg =
146
+ | ROSSerialSmartServos
147
+ | ROSSerialIMU
148
+ | ROSSerialPowerStatus
149
+ | ROSSerialAddOnStatusList
150
+ | ROSSerialRobotStatus;
151
+
152
+ // ROSSerial ROSTopics
153
+ export const ROSTOPIC_V2_SMART_SERVOS = 120;
154
+ export const ROSTOPIC_V2_ACCEL = 121;
155
+ export const ROSTOPIC_V2_POWER_STATUS = 122;
156
+ export const ROSTOPIC_V2_ADDONS = 123;
157
+ export const ROSTOPIC_V2_ROBOT_STATUS = 124;
158
+ export const ROSTOPIC_V2_CAMERA = 200;
159
+
160
+ export class RICROSSerial {
161
+
162
+ static decode(
163
+ rosSerialMsg: Uint8Array,
164
+ startPos: number,
165
+ commsStats: RICCommsStats,
166
+ addOnManager: RICAddOnManager,
167
+ ricStateInfo: RICStateInfo,
168
+ frameTimeMs: number
169
+ ): Array<number> {
170
+
171
+ // ROSSerial message format
172
+ const RS_MSG_MIN_LENGTH = 8;
173
+ const RS_MSG_LEN_LOW_POS = 2;
174
+ const RS_MSG_LEN_HIGH_POS = 3;
175
+ const RS_MSG_TOPIC_ID_LOW_POS = 5;
176
+ const RS_MSG_TOPIC_ID_HIGH_POS = 6;
177
+ const RS_MSG_PAYLOAD_POS = 7;
178
+
179
+ // Max payload length
180
+ const MAX_VALID_PAYLOAD_LEN = 200000;
181
+
182
+ // Update stats
183
+ commsStats.updateROSSerialRxRate(rosSerialMsg.length, frameTimeMs);
184
+
185
+ // Payload may contain multiple ROSSerial messages
186
+ let msgPos = startPos;
187
+ const topicIDs = new Array<number>();
188
+ for (;;) {
189
+ const remainingMsgLen = rosSerialMsg.length - msgPos;
190
+
191
+ // RaftLog.debug('ROSSerial Decode ' + remainingMsgLen);
192
+
193
+ if (remainingMsgLen < RS_MSG_MIN_LENGTH) break;
194
+
195
+ // Extract header
196
+ const payloadLength =
197
+ rosSerialMsg[msgPos + RS_MSG_LEN_LOW_POS] +
198
+ rosSerialMsg[msgPos + RS_MSG_LEN_HIGH_POS] * 256;
199
+ const topicID =
200
+ rosSerialMsg[msgPos + RS_MSG_TOPIC_ID_LOW_POS] +
201
+ rosSerialMsg[msgPos + RS_MSG_TOPIC_ID_HIGH_POS] * 256;
202
+
203
+ // RaftLog.debug('ROSSerial ' + payloadLength + ' topic ' + topicID);
204
+
205
+ // Check max length
206
+ if (payloadLength < 0 || payloadLength > MAX_VALID_PAYLOAD_LEN) break;
207
+
208
+ // Check min length
209
+ if (rosSerialMsg.length < payloadLength + RS_MSG_MIN_LENGTH) break;
210
+
211
+ // Extract payload
212
+ const payload = rosSerialMsg.slice(
213
+ msgPos + RS_MSG_PAYLOAD_POS,
214
+ msgPos + RS_MSG_PAYLOAD_POS + payloadLength
215
+ );
216
+ // RaftLog.debug('ROSSerial ' + RaftUtils.bufferToHex(payload));
217
+
218
+ // we need to register the static addons here in case
219
+ // marty only has static addons (and so the rostopic_v2_addons case
220
+ // never runs)
221
+ let allAdons: ROSSerialAddOnStatusList = {addons: []};
222
+ const staticAddons = addOnManager.getProcessedStaticAddons();
223
+ for (const staticAddon of staticAddons) {
224
+ allAdons.addons.push(staticAddon);
225
+ }
226
+ if (commsStats._msgAddOnPub === 0) {
227
+ // we set the static addons only if we don't have any other addons
228
+ // the _msgAddOnPub is incremented in the rostopic_v2_addons case
229
+ // (when we get addons from marty)
230
+ // otherwise, the static addons will be set along with the regular addons (below)
231
+ ricStateInfo.addOnInfo = allAdons;
232
+ ricStateInfo.addOnInfoValidMs = Date.now();
233
+ }
234
+
235
+ // Record topic ID
236
+ topicIDs.push(topicID);
237
+
238
+ // Update state info
239
+ switch (topicID) {
240
+ case ROSTOPIC_V2_SMART_SERVOS:
241
+ // Smart Servos
242
+ ricStateInfo.smartServos = this.extractSmartServos(payload);
243
+ ricStateInfo.smartServosValidMs = Date.now();
244
+ commsStats.recordSmartServos();
245
+ break;
246
+ case ROSTOPIC_V2_ACCEL:
247
+ // Accelerometer
248
+ ricStateInfo.imuData = this.extractAccel(payload);
249
+ ricStateInfo.imuDataValidMs = Date.now();
250
+ commsStats.recordIMU();
251
+ break;
252
+ case ROSTOPIC_V2_POWER_STATUS:
253
+ // Power Status
254
+ ricStateInfo.power = this.extractPowerStatus(payload);
255
+ ricStateInfo.powerValidMs = Date.now();
256
+ commsStats.recordPowerStatus();
257
+ break;
258
+ case ROSTOPIC_V2_ADDONS:
259
+ // Addons
260
+ ricStateInfo.addOnInfo = this.extractAddOnStatus(payload, addOnManager);
261
+ ricStateInfo.addOnInfoValidMs = Date.now();
262
+ for (const staticAddon of staticAddons) {
263
+ allAdons.addons.push(staticAddon);
264
+ }
265
+ commsStats.recordAddOnPub();
266
+ break;
267
+ case ROSTOPIC_V2_ROBOT_STATUS:
268
+ // Robot Status
269
+ ricStateInfo.robotStatus = this.extractRobotStatus(payload);
270
+ ricStateInfo.robotStatusValidMs = Date.now();
271
+ commsStats.recordRobotStatus();
272
+ break;
273
+ case ROSTOPIC_V2_CAMERA:
274
+ // Camera
275
+ // RaftLog.debug('Camera ' + payloadLength);
276
+ ricStateInfo.cameraData = this.extractCameraData(payload, frameTimeMs);
277
+ ricStateInfo.cameraDataValidMs = Date.now();
278
+ commsStats.recordOtherTopic();
279
+ break;
280
+ default:
281
+ // Unknown topic
282
+ commsStats.recordOtherTopic();
283
+ break;
284
+ }
285
+
286
+ // Move msgPos on
287
+ msgPos += RS_MSG_PAYLOAD_POS + payloadLength + 1;
288
+
289
+ // RaftLog.debug('MsgPos ' + msgPos);
290
+ }
291
+
292
+ return topicIDs;
293
+ }
294
+
295
+ static extractSmartServos(buf: Uint8Array): ROSSerialSmartServos {
296
+ // Each group of attributes for a servo is a fixed size
297
+ const ROS_SMART_SERVOS_ATTR_GROUP_BYTES = 6;
298
+ const numGroups = Math.floor(
299
+ buf.length / ROS_SMART_SERVOS_ATTR_GROUP_BYTES
300
+ );
301
+ const msg: ROSSerialSmartServos = { smartServos: [] };
302
+ let bufPos = 0;
303
+ for (let i = 0; i < numGroups; i++) {
304
+ const servoId = buf[bufPos];
305
+ const servoPos = RaftUtils.getBEInt16FromBuf(buf, bufPos + 1);
306
+ const servoCurrent = RaftUtils.getBEUint16FromBuf(buf, bufPos + 3);
307
+ const servoStatus = buf[bufPos + 5];
308
+ bufPos += ROS_SMART_SERVOS_ATTR_GROUP_BYTES;
309
+ msg.smartServos.push({
310
+ id: servoId,
311
+ pos: servoPos,
312
+ current: servoCurrent,
313
+ status: servoStatus,
314
+ });
315
+ }
316
+ return msg;
317
+ }
318
+
319
+ static extractAccel(buf: Uint8Array): ROSSerialIMU {
320
+ // Three accelerometer floats
321
+ const x = RaftUtils.getBEFloatFromBuf(buf);
322
+ const y = RaftUtils.getBEFloatFromBuf(buf.slice(4));
323
+ const z = RaftUtils.getBEFloatFromBuf(buf.slice(8));
324
+ return { accel: { x: x / 1024, y: y / 1024, z: z / 1024 } };
325
+ }
326
+
327
+ static extractPowerStatus(buf: Uint8Array): ROSSerialPowerStatus {
328
+ // Power indicator values
329
+ // RaftLog.debug(`PowerStatus ${RaftUtils.bufferToHex(buf)}`);
330
+ const remCapPC = RaftUtils.getBEUint8FromBuf(buf, 0);
331
+ const tempDegC = RaftUtils.getBEUint8FromBuf(buf, 1);
332
+ const remCapMAH = RaftUtils.getBEUint16FromBuf(buf, 2);
333
+ const fullCapMAH = RaftUtils.getBEUint16FromBuf(buf, 4);
334
+ const currentMA = RaftUtils.getBEInt16FromBuf(buf, 6);
335
+ const power5VOnTimeSecs = RaftUtils.getBEUint16FromBuf(buf, 8);
336
+ const powerFlags = RaftUtils.getBEUint16FromBuf(buf, 10);
337
+ const isOnUSBPower = (powerFlags & 0x0001) != 0;
338
+ const is5VOn = (powerFlags & 0x0002) != 0;
339
+ const isBattInfoValid = (powerFlags & 0x0004) == 0;
340
+ const isUSBPowerInfoValid = (powerFlags & 0x0008) == 0;
341
+ return {
342
+ powerStatus: {
343
+ battRemainCapacityPercent: remCapPC,
344
+ battTempDegC: tempDegC,
345
+ battRemainCapacityMAH: remCapMAH,
346
+ battFullCapacityMAH: fullCapMAH,
347
+ battCurrentMA: currentMA,
348
+ power5VOnTimeSecs: power5VOnTimeSecs,
349
+ power5VIsOn: is5VOn,
350
+ powerUSBIsConnected: isOnUSBPower && isUSBPowerInfoValid,
351
+ battInfoValid: isBattInfoValid,
352
+ powerUSBIsValid: isUSBPowerInfoValid,
353
+ powerFlags: powerFlags,
354
+ },
355
+ };
356
+ }
357
+
358
+ static extractAddOnStatus(
359
+ buf: Uint8Array,
360
+ addOnManager: RICAddOnManager
361
+ ): ROSSerialAddOnStatusList {
362
+ // RaftLog.debug(`AddOnRawData ${RaftUtils.bufferToHex(buf)}`);
363
+ // Each group of attributes for a add-on is a fixed size
364
+ const ROS_ADDON_ATTR_GROUP_BYTES = 12;
365
+ const numGroups = Math.floor(buf.length / ROS_ADDON_ATTR_GROUP_BYTES);
366
+ const msg: ROSSerialAddOnStatusList = { addons: [] };
367
+ let bufPos = 0;
368
+ for (let i = 0; i < numGroups; i++) {
369
+ const addOnId = buf[bufPos];
370
+ const status = buf[bufPos + 1];
371
+ const addOnData = buf.slice(bufPos + 2, bufPos + 12);
372
+ bufPos += ROS_ADDON_ATTR_GROUP_BYTES;
373
+ const addOnRec = addOnManager.processPublishedData(
374
+ addOnId,
375
+ status,
376
+ addOnData
377
+ );
378
+ if (addOnRec !== null) {
379
+ msg.addons.push(addOnRec);
380
+ }
381
+ }
382
+ return msg;
383
+ }
384
+
385
+ static extractRGBT(buf: Uint8Array, offset: number): ROSSerialRGBT {
386
+ return new ROSSerialRGBT(
387
+ buf[offset],
388
+ buf[offset + 1],
389
+ buf[offset + 2],
390
+ buf[offset + 3]
391
+ );
392
+ }
393
+
394
+ static extractRobotStatus(buf: Uint8Array): ROSSerialRobotStatus {
395
+ const flags = RaftUtils.getBEUint8FromBuf(buf, 0);
396
+ const workQCount = RaftUtils.getBEUint8FromBuf(buf, 1);
397
+ let heapFree = 0;
398
+ let heapMin = 0;
399
+ let pixRGBT1 = new ROSSerialRGBT(0, 0, 0, 0);
400
+ let pixRGBT2 = new ROSSerialRGBT(0, 0, 0, 0);
401
+ let pixRGBT3 = new ROSSerialRGBT(0, 0, 0, 0);
402
+ let loopMsAvg = 0;
403
+ let loopMsMax = 0;
404
+ // RaftLog.debug(`RobotStatus ${buf.length} ${RaftUtils.bufferToHex(buf)} ${flags} ${workQCount} ${heapFree} ${heapMin} ${pixRGBT1.toString()} ${pixRGBT2.toString()} ${pixRGBT3.toString()} ${loopMsAvg} ${loopMsMax}`);
405
+ let wifiRSSI = 0;
406
+ let bleRSSI = 0;
407
+
408
+ if (buf.length >= 24) {
409
+ heapFree = RaftUtils.getBEUint32FromBuf(buf, 2);
410
+ heapMin = RaftUtils.getBEUint32FromBuf(buf, 6);
411
+ pixRGBT1 = RICROSSerial.extractRGBT(buf, 10);
412
+ pixRGBT2 = RICROSSerial.extractRGBT(buf, 14);
413
+ pixRGBT3 = RICROSSerial.extractRGBT(buf, 18);
414
+ loopMsAvg = RaftUtils.getBEUint8FromBuf(buf, 22);
415
+ loopMsMax = RaftUtils.getBEUint8FromBuf(buf, 23);
416
+ // RaftLog.debug(`RobotStatus ${buf.length} ${RaftUtils.bufferToHex(buf)} ${flags} ${workQCount} ${heapFree} ${heapMin} ${pixRGBT1.toString()} ${pixRGBT2.toString()} ${pixRGBT3.toString()} ${loopMsAvg} ${loopMsMax}`);
417
+ if (buf.length >= 26) {
418
+ wifiRSSI = RaftUtils.getBEInt8FromBuf(buf, 24);
419
+ bleRSSI = RaftUtils.getBEInt8FromBuf(buf, 25);
420
+ }
421
+ }
422
+ return {
423
+ robotStatus: {
424
+ flags: flags,
425
+ isMoving: (flags & 0x01) != 0,
426
+ isPaused: (flags & 0x02) != 0,
427
+ isFwUpdating: (flags & 0x04) != 0,
428
+ workQCount: workQCount,
429
+ heapFree: heapFree,
430
+ heapMin: heapMin,
431
+ pixRGBT: [pixRGBT1, pixRGBT2, pixRGBT3],
432
+ loopMsAvg: loopMsAvg,
433
+ loopMsMax: loopMsMax,
434
+ wifiRSSI: wifiRSSI,
435
+ bleRSSI: bleRSSI,
436
+ },
437
+ };
438
+ }
439
+
440
+ static extractCameraData(buf: Uint8Array, frameTimeMs: number): ROSCameraData {
441
+ RaftLog.verbose(`CameraData ${buf.length} ${RaftUtils.bufferToHex(buf.slice(0,30))}`);
442
+ const unixTime = RaftUtils.getBEUint64FromBuf(buf, 0);
443
+ const sinceStartMs = RaftUtils.getBEUint32FromBuf(buf, 8);
444
+ const imageWidth = RaftUtils.getBEUint16FromBuf(buf, 12);
445
+ const imageHeight = RaftUtils.getBEUint16FromBuf(buf, 14);
446
+ const imageFormat = RaftUtils.getBEUint8FromBuf(buf, 16);
447
+ const imageQuality = RaftUtils.getBEUint8FromBuf(buf, 17);
448
+ const imageCount = RaftUtils.getBEUint32FromBuf(buf, 18);
449
+ const imageData = buf.slice(22);
450
+ return { cameraData:
451
+ {
452
+ unixTimeMs: unixTime,
453
+ sinceStartMs: sinceStartMs,
454
+ imageWidth: imageWidth,
455
+ imageHeight: imageHeight,
456
+ imageFormat: imageFormat,
457
+ imageQuality: imageQuality,
458
+ imageData: imageData,
459
+ frameTimeMs: frameTimeMs,
460
+ imageCount: imageCount
461
+ }
462
+ };
463
+ }
464
+ }
@@ -0,0 +1,146 @@
1
+ /**
2
+ * The RICServoFaultDetection class is responsible for detecting faults in the RIC servos.
3
+ * These are the faults that can currently be detected:
4
+ 1) Wiring fault - Intermittent connection to servo potentiometer
5
+ 2) Wiring fault - no connection to servo potentiometer
6
+ 3) Wiring fault - faulty connection to servo drive
7
+ 4) Servo horn has shifted out of position
8
+
9
+ * To detect these faults:
10
+ 1) get the list of all servos
11
+ 1.1) We filter out the servos that don't have the fault bit enabled in their status byte
12
+ 2) sending an atomic read command to the servos
13
+ 2.1) the servos will respond with a report msg
14
+ 2.2) the fault flags are on the 11th byte of the report msg
15
+ 2.3) The 'fault' byte has 8 bits, each of which corresponds to a specific type of fault (there are only 4 types of faults so far, so the remaining 4 bits are unused.)
16
+ Bit 0: Intermittent connection to potentiometer
17
+ Bit 1: No connection to potentiometer
18
+ Bit 2: Faulty connection to motor drive
19
+ Bit 3: Servo horn position error
20
+ 3) in a second phase, we receive the report msg from the app, and check the fault flags
21
+ */
22
+
23
+ import { RICServoFaultFlags } from "./RICTypes";
24
+ import { RICStateInfo } from "./RICStateInfo";
25
+ import RaftMsgHandler from "../../../../src/RaftMsgHandler";
26
+ import RaftLog from "../../../../src/RaftLog";
27
+ import { RICHWElemList_Min } from "./RICHWElem";
28
+ import { RaftReportMsg } from "../../../../src/RaftTypes";
29
+
30
+ export default class RICServoFaultDetector {
31
+ private _raftMsgHandler: RaftMsgHandler;
32
+ private static expirationDate: Date = new Date();
33
+ private static _servoList: string[] = [];
34
+ private ricStateReference: RICStateInfo;
35
+
36
+ constructor(raftMsgHandler: RaftMsgHandler, ricStateReference: RICStateInfo) {
37
+ this._raftMsgHandler = raftMsgHandler;
38
+ this.ricStateReference = ricStateReference;
39
+ }
40
+
41
+ private async getAllServos(): Promise<void> {
42
+ RICServoFaultDetector._servoList = [];
43
+ const response = await this._raftMsgHandler.sendRICRESTURL<RICHWElemList_Min>("hwstatus/minstat?filterByType=SmartServo");
44
+ if (!response || !response.hw) {
45
+ RaftLog.warn("RICServoFaultDetector: Error getting servo list");
46
+ return;
47
+ }
48
+ const servosWithIdAndName = response.hw.map((smartServo) => ({ id: smartServo.I, name: smartServo.n }));
49
+ // filter only the servos that they have enabled the fault bit in their status byte
50
+ const servosWithFaultBitEnabled = servosWithIdAndName.filter((smartServo) => {
51
+ const foundSmartServoStatus = this.ricStateReference.smartServos.smartServos.find((smartServoStat) => smartServoStat.id === +smartServo.id);
52
+ if (!foundSmartServoStatus) {
53
+ return false;
54
+ }
55
+ return RICServoFaultDetector.isFaultBitEnabled(foundSmartServoStatus.status);
56
+ });
57
+ const filteredServoArrayWithNames = servosWithFaultBitEnabled.map((smartServo) => smartServo.name);
58
+ try {
59
+ RICServoFaultDetector._servoList = filteredServoArrayWithNames;
60
+ } catch (e) {
61
+ console.log("Error getting servo list");
62
+ }
63
+ }
64
+
65
+ async atomicReadOperation(expirationTime = 5000): Promise<void> {
66
+ await this.getAllServos();
67
+ // setting an expiration date within which the interpreter should receive the report msg
68
+ RICServoFaultDetector.expirationDate = new Date(Date.now() + expirationTime);
69
+ for (let i = 0; i < RICServoFaultDetector._servoList.length; i++) {
70
+ const servoName = RICServoFaultDetector._servoList[i];
71
+ try {
72
+ await this._raftMsgHandler.sendRICRESTURL<void>(`elem/${servoName}/json?cmd=raw&numToRd=11&msgKey=100${i}`);
73
+ } catch (e) {
74
+ RaftLog.warn(`RICServoFaultDetector: Error sending atomic read command to servo ${servoName}`);
75
+ }
76
+ }
77
+ }
78
+
79
+ static interpretReportMsg(reportMsg: RaftReportMsg): RICServoFaultFlags | undefined {
80
+ // make sure the report msg is not expired
81
+ if (new Date() > RICServoFaultDetector.expirationDate) {
82
+ RaftLog.warn(`RICServoFaultDetector: Received report msg after expiration date: ${reportMsg}`);
83
+ return;
84
+ }
85
+ // make sure this is a raw report msg
86
+ if (reportMsg.msgType !== "raw") {
87
+ RaftLog.warn(`RICServoFaultDetector: Received non-raw report msg: ${reportMsg}`);
88
+ return;
89
+ }
90
+ // making sure the report msg is from a servo
91
+ if (reportMsg.elemName && !RICServoFaultDetector._servoList.includes(reportMsg.elemName)) {
92
+ RaftLog.warn(`RICServoFaultDetector: Received report msg from non-servo: ${reportMsg}`);
93
+ return;
94
+ }
95
+ const hexRd = reportMsg.hexRd;
96
+ if (!hexRd) {
97
+ RaftLog.warn(`RICServoFaultDetector: Received report msg with no hexRd: ${reportMsg}`);
98
+ return;
99
+ }
100
+ const faultByteHex = hexRd.slice(20, 22); // the fault byte is the 11th byte of the report msg
101
+ if (!faultByteHex || hexRd.length !== 22) { // since we are reading 11 bytes, the hexRd should be 22 characters long
102
+ RaftLog.warn(`RICServoFaultDetector: Received report msg with invalid hexRd: ${reportMsg}`);
103
+ return;
104
+ }
105
+ try {
106
+ return RICServoFaultDetector.decodeFault(faultByteHex);
107
+ } catch (e) {
108
+ RaftLog.warn(`RICServoFaultDetector: Received report msg with invalid fault byte: ${reportMsg}`);
109
+ return;
110
+ }
111
+ }
112
+
113
+ private static decodeFault(faultByteHex: string): RICServoFaultFlags {
114
+ const byte = parseInt(faultByteHex, 16);
115
+ return {
116
+ intermittentConnection: !!(byte & 0b0001),
117
+ noConnection: !!(byte & 0b0010),
118
+ faultyConnection: !!(byte & 0b0100),
119
+ servoHornPositionError: !!(byte & 0b1000)
120
+ };
121
+ }
122
+
123
+ public static isFaultBitEnabled(input: number | string): boolean {
124
+ let num: number;
125
+
126
+ // If input is a hexadecimal string, convert it to a decimal number
127
+ if (typeof input === "string") {
128
+ if (!input.startsWith("0x")) {
129
+ throw new Error("Input string must start with '0x' for hexadecimal representation");
130
+ }
131
+ num = parseInt(input, 16);
132
+ } else {
133
+ num = input;
134
+ }
135
+
136
+ // Check if number is an 8-bit number
137
+ if (num < 0 || num > 255 || !Number.isInteger(num)) {
138
+ throw new Error("Input is not an 8-bit number");
139
+ }
140
+
141
+ // Check if the 6th bit (from the right) is enabled
142
+ return Boolean(num & 64);
143
+ }
144
+
145
+
146
+ }
@@ -0,0 +1,32 @@
1
+ import { RICSERIAL_PAYLOAD_POS } from "../../../../src/RaftProtocolDefs";
2
+ import RICAddOnManager from "./RICAddOnManager";
3
+ import RICCommsStats from "./RICCommsStats";
4
+ import { RICROSSerial, ROSCameraData, ROSSerialAddOnStatusList, ROSSerialIMU, ROSSerialPowerStatus, ROSSerialRobotStatus, ROSSerialSmartServos } from "./RICROSSerial";
5
+
6
+ export class RICStateInfo {
7
+ smartServos: ROSSerialSmartServos = new ROSSerialSmartServos();
8
+ smartServosValidMs = 0;
9
+ imuData: ROSSerialIMU = new ROSSerialIMU();
10
+ imuDataValidMs = 0;
11
+ power: ROSSerialPowerStatus = new ROSSerialPowerStatus();
12
+ powerValidMs = 0;
13
+ addOnInfo: ROSSerialAddOnStatusList = new ROSSerialAddOnStatusList();
14
+ addOnInfoValidMs = 0;
15
+ robotStatus: ROSSerialRobotStatus = new ROSSerialRobotStatus();
16
+ robotStatusValidMs = 0;
17
+ cameraData: ROSCameraData = new ROSCameraData();
18
+ cameraDataValidMs = 0;
19
+
20
+ updateFromROSSerialMsg(rxMsg: Uint8Array, commsStats: RICCommsStats,
21
+ addOnManager: RICAddOnManager, frameTimeMs: number): Array<number> {
22
+ return RICROSSerial.decode(
23
+ rxMsg,
24
+ RICSERIAL_PAYLOAD_POS,
25
+ commsStats,
26
+ addOnManager,
27
+ this,
28
+ frameTimeMs
29
+ );
30
+ }
31
+ }
32
+