@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.
- package/.editorconfig +14 -0
- package/.gitattributes +11 -0
- package/.nvmrc +1 -0
- package/LICENSE +22 -0
- package/README.md +11 -0
- package/TODO.md +1 -0
- package/dist/RaftAttributeHandler.d.ts +12 -0
- package/dist/RaftAttributeHandler.js +241 -0
- package/dist/RaftAttributeHandler.js.map +1 -0
- package/dist/RaftChannel.d.ts +18 -0
- package/dist/RaftChannel.js +12 -0
- package/dist/RaftChannel.js.map +1 -0
- package/dist/RaftChannelWebBLE.d.ts +38 -0
- package/dist/RaftChannelWebBLE.js +274 -0
- package/dist/RaftChannelWebBLE.js.map +1 -0
- package/dist/RaftChannelWebSerial.d.ts +37 -0
- package/dist/RaftChannelWebSerial.js +319 -0
- package/dist/RaftChannelWebSerial.js.map +1 -0
- package/dist/RaftChannelWebSocket.d.ts +28 -0
- package/dist/RaftChannelWebSocket.js +197 -0
- package/dist/RaftChannelWebSocket.js.map +1 -0
- package/dist/RaftCommsStats.d.ts +39 -0
- package/dist/RaftCommsStats.js +128 -0
- package/dist/RaftCommsStats.js.map +1 -0
- package/dist/RaftConnEvents.d.ts +31 -0
- package/dist/RaftConnEvents.js +44 -0
- package/dist/RaftConnEvents.js.map +1 -0
- package/dist/RaftConnector.d.ts +242 -0
- package/dist/RaftConnector.js +613 -0
- package/dist/RaftConnector.js.map +1 -0
- package/dist/RaftCustomAttrHandler.d.ts +4 -0
- package/dist/RaftCustomAttrHandler.js +50 -0
- package/dist/RaftCustomAttrHandler.js.map +1 -0
- package/dist/RaftDeviceInfo.d.ts +64 -0
- package/dist/RaftDeviceInfo.js +36 -0
- package/dist/RaftDeviceInfo.js.map +1 -0
- package/dist/RaftDeviceManager.d.ts +37 -0
- package/dist/RaftDeviceManager.js +450 -0
- package/dist/RaftDeviceManager.js.map +1 -0
- package/dist/RaftDeviceMsg.d.ts +9 -0
- package/dist/RaftDeviceMsg.js +11 -0
- package/dist/RaftDeviceMsg.js.map +1 -0
- package/dist/RaftDeviceStates.d.ts +33 -0
- package/dist/RaftDeviceStates.js +60 -0
- package/dist/RaftDeviceStates.js.map +1 -0
- package/dist/RaftFileHandler.d.ts +52 -0
- package/dist/RaftFileHandler.js +502 -0
- package/dist/RaftFileHandler.js.map +1 -0
- package/dist/RaftLog.d.ts +22 -0
- package/dist/RaftLog.js +63 -0
- package/dist/RaftLog.js.map +1 -0
- package/dist/RaftMiniHDLC.d.ts +18 -0
- package/dist/RaftMiniHDLC.js +383 -0
- package/dist/RaftMiniHDLC.js.map +1 -0
- package/dist/RaftMsgHandler.d.ts +57 -0
- package/dist/RaftMsgHandler.js +480 -0
- package/dist/RaftMsgHandler.js.map +1 -0
- package/dist/RaftMsgTrackInfo.d.ts +17 -0
- package/dist/RaftMsgTrackInfo.js +42 -0
- package/dist/RaftMsgTrackInfo.js.map +1 -0
- package/dist/RaftProtocolDefs.d.ts +30 -0
- package/dist/RaftProtocolDefs.js +48 -0
- package/dist/RaftProtocolDefs.js.map +1 -0
- package/dist/RaftStreamHandler.d.ts +38 -0
- package/dist/RaftStreamHandler.js +257 -0
- package/dist/RaftStreamHandler.js.map +1 -0
- package/dist/RaftSystemType.d.ts +21 -0
- package/dist/RaftSystemType.js +3 -0
- package/dist/RaftSystemType.js.map +1 -0
- package/dist/RaftSystemUtils.d.ts +136 -0
- package/dist/RaftSystemUtils.js +410 -0
- package/dist/RaftSystemUtils.js.map +1 -0
- package/dist/RaftTypes.d.ts +184 -0
- package/dist/RaftTypes.js +157 -0
- package/dist/RaftTypes.js.map +1 -0
- package/dist/RaftUpdateEvents.d.ts +33 -0
- package/dist/RaftUpdateEvents.js +46 -0
- package/dist/RaftUpdateEvents.js.map +1 -0
- package/dist/RaftUpdateManager.d.ts +61 -0
- package/dist/RaftUpdateManager.js +618 -0
- package/dist/RaftUpdateManager.js.map +1 -0
- package/dist/RaftUtils.d.ts +125 -0
- package/dist/RaftUtils.js +454 -0
- package/dist/RaftUtils.js.map +1 -0
- package/dist/RaftWifiTypes.d.ts +23 -0
- package/dist/RaftWifiTypes.js +43 -0
- package/dist/RaftWifiTypes.js.map +1 -0
- package/dist/TestDataGen.d.ts +7 -0
- package/dist/TestDataGen.js +133 -0
- package/dist/TestDataGen.js.map +1 -0
- package/dist/main.d.ts +18 -0
- package/dist/main.js +42 -0
- package/dist/main.js.map +1 -0
- package/eslint.config.mjs +33 -0
- package/examples/dashboard/package.json +39 -0
- package/examples/dashboard/src/ConnManager.ts +86 -0
- package/examples/dashboard/src/Main.tsx +100 -0
- package/examples/dashboard/src/StatusScreen.tsx +72 -0
- package/examples/dashboard/src/SystemTypeCog/CogStateInfo.ts +144 -0
- package/examples/dashboard/src/SystemTypeCog/SystemTypeCog.ts +77 -0
- package/examples/dashboard/src/SystemTypeMarty/RICAddOn.ts +70 -0
- package/examples/dashboard/src/SystemTypeMarty/RICAddOnBase.ts +33 -0
- package/examples/dashboard/src/SystemTypeMarty/RICAddOnManager.ts +342 -0
- package/examples/dashboard/src/SystemTypeMarty/RICCommsStats.ts +170 -0
- package/examples/dashboard/src/SystemTypeMarty/RICHWElem.ts +123 -0
- package/examples/dashboard/src/SystemTypeMarty/RICLEDPatternChecker.ts +207 -0
- package/examples/dashboard/src/SystemTypeMarty/RICROSSerial.ts +464 -0
- package/examples/dashboard/src/SystemTypeMarty/RICServoFaultDetector.ts +146 -0
- package/examples/dashboard/src/SystemTypeMarty/RICStateInfo.ts +32 -0
- package/examples/dashboard/src/SystemTypeMarty/RICSystemUtils.ts +371 -0
- package/examples/dashboard/src/SystemTypeMarty/RICTypes.ts +20 -0
- package/examples/dashboard/src/SystemTypeMarty/SystemTypeMarty.ts +113 -0
- package/examples/dashboard/src/index.html +15 -0
- package/examples/dashboard/src/index.tsx +15 -0
- package/examples/dashboard/src/styles.css +122 -0
- package/examples/dashboard/tsconfig.json +18 -0
- package/jest.config.js +11 -0
- package/package.json +50 -0
- package/src/RaftAttributeHandler.ts +289 -0
- package/src/RaftChannel.ts +30 -0
- package/src/RaftChannelWebBLE.ts +342 -0
- package/src/RaftChannelWebSerial.ts +408 -0
- package/src/RaftChannelWebSocket.ts +245 -0
- package/src/RaftCommsStats.ts +142 -0
- package/src/RaftConnEvents.ts +46 -0
- package/src/RaftConnector.ts +745 -0
- package/src/RaftCustomAttrHandler.ts +54 -0
- package/src/RaftDeviceInfo.ts +104 -0
- package/src/RaftDeviceManager.ts +542 -0
- package/src/RaftDeviceMsg.ts +20 -0
- package/src/RaftDeviceStates.ts +89 -0
- package/src/RaftFileHandler.ts +668 -0
- package/src/RaftLog.ts +70 -0
- package/src/RaftMiniHDLC.ts +396 -0
- package/src/RaftMsgHandler.ts +778 -0
- package/src/RaftMsgTrackInfo.ts +51 -0
- package/src/RaftProtocolDefs.ts +46 -0
- package/src/RaftStreamHandler.ts +328 -0
- package/src/RaftSystemType.ts +25 -0
- package/src/RaftSystemUtils.ts +487 -0
- package/src/RaftTypes.ts +250 -0
- package/src/RaftUpdateEvents.ts +48 -0
- package/src/RaftUpdateManager.ts +778 -0
- package/src/RaftUtils.ts +484 -0
- package/src/RaftWifiTypes.ts +36 -0
- package/src/TestDataGen.ts +157 -0
- package/src/main.ts +28 -0
- package/testdata/TestDeviceTypeRecs.json +492 -0
- 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
|
+
|