@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,144 @@
|
|
|
1
|
+
import { time } from "console";
|
|
2
|
+
import RaftLog from "../../../../src/RaftLog";
|
|
3
|
+
|
|
4
|
+
export interface IMUStateInfo {
|
|
5
|
+
gx: number;
|
|
6
|
+
gy: number;
|
|
7
|
+
gz: number;
|
|
8
|
+
ax: number;
|
|
9
|
+
ay: number;
|
|
10
|
+
az: number;
|
|
11
|
+
tsMs: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface DeviceLastTsState {
|
|
15
|
+
lastMs: number;
|
|
16
|
+
offsetMs: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface DeviceMsgJsonElem {
|
|
20
|
+
_t: string; // Device type
|
|
21
|
+
_o: string; // Device name
|
|
22
|
+
x: string; // Hex encoded message values
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface DeviceMsgJsonBus {
|
|
26
|
+
[devAddr: string]: DeviceMsgJsonElem;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface DeviceMsgJson {
|
|
30
|
+
[busName: string]: DeviceMsgJsonBus;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class CogStateInfo {
|
|
34
|
+
|
|
35
|
+
LSM6DS: IMUStateInfo = {gx: 0, gy: 0, gz: 0, ax: 0, ay: 0, az: 0, tsMs: 0};
|
|
36
|
+
|
|
37
|
+
// Last timestamp for each device
|
|
38
|
+
private _deviceLastTs: {[devName: string]: DeviceLastTsState} = {};
|
|
39
|
+
|
|
40
|
+
updateFromMsg(rxMsg: Uint8Array, frameTimeMs: number): Array<string> {
|
|
41
|
+
|
|
42
|
+
// RaftLog.info(`CogStateInfo: updateFromMsg: rxMsg: ${rxMsg} frameTimeMs: ${frameTimeMs}`);
|
|
43
|
+
|
|
44
|
+
// Convert Uint8Array to string
|
|
45
|
+
const decoder = new TextDecoder('utf-8');
|
|
46
|
+
const jsonString = decoder.decode(rxMsg.slice(2));
|
|
47
|
+
|
|
48
|
+
// Debug
|
|
49
|
+
// RaftLog.info(`CogStateInfo: updateFromMsg: jsonString: ${jsonString}`);
|
|
50
|
+
|
|
51
|
+
// Parse JSON string to TypeScript object
|
|
52
|
+
const deviceMsg: DeviceMsgJson = JSON.parse(jsonString);
|
|
53
|
+
|
|
54
|
+
// Debug
|
|
55
|
+
// for (const busName in deviceMsg) {
|
|
56
|
+
// for (const devAddr in deviceMsg[busName]) {
|
|
57
|
+
// for (const attrGroupName in deviceMsg[busName][devAddr]) {
|
|
58
|
+
// RaftLog.info(`CogStateInfo: updateFromMsg: busName ${busName} devAddr ${devAddr} attrGroupName: ${attrGroupName}`);
|
|
59
|
+
// RaftLog.info(`CogStateInfo: updateFromMsg: attrValue: ${deviceMsg[busName][devAddr][attrGroupName]}`);
|
|
60
|
+
// }
|
|
61
|
+
// }
|
|
62
|
+
// }
|
|
63
|
+
|
|
64
|
+
// Iterate over values and extract
|
|
65
|
+
for (const busName in deviceMsg) {
|
|
66
|
+
for (const devAddr in deviceMsg[busName]) {
|
|
67
|
+
|
|
68
|
+
// Validate
|
|
69
|
+
if ((deviceMsg[busName][devAddr].x === undefined) || (deviceMsg[busName][devAddr]._t === undefined)) {
|
|
70
|
+
RaftLog.warn(`CogStateInfo: updateFromMsg: Invalid message`);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Extract message and device type
|
|
75
|
+
const msgHex = deviceMsg[busName][devAddr].x;
|
|
76
|
+
const devType = deviceMsg[busName][devAddr]._t;
|
|
77
|
+
|
|
78
|
+
// Check if message is empty
|
|
79
|
+
if (msgHex === '') {
|
|
80
|
+
RaftLog.warn(`CogStateInfo: updateFromMsg: Empty message`);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Convert the hex string to an arraybuffer by converting each pair of hex chars to a byte
|
|
85
|
+
const msgBytes = this.hexToBytes(msgHex);
|
|
86
|
+
|
|
87
|
+
// Convert to a Buffer
|
|
88
|
+
const msgBuffer = Buffer.from(msgBytes);
|
|
89
|
+
|
|
90
|
+
// Extract timestamp
|
|
91
|
+
let timestamp = msgBuffer.readUInt16BE(0);
|
|
92
|
+
if (this._deviceLastTs[devType] === undefined) {
|
|
93
|
+
this._deviceLastTs[devType] = {lastMs: 0, offsetMs: 0};
|
|
94
|
+
}
|
|
95
|
+
const devTsState = this._deviceLastTs[devType];
|
|
96
|
+
if (timestamp < devTsState.lastMs) {
|
|
97
|
+
devTsState.offsetMs += 0x10000;
|
|
98
|
+
}
|
|
99
|
+
devTsState.lastMs = timestamp;
|
|
100
|
+
timestamp += devTsState.offsetMs;
|
|
101
|
+
let msgBufferOffset = 2;
|
|
102
|
+
// RaftLog.info(`CogStateInfo: updateFromMsg: timestamp: ${timestamp}`);
|
|
103
|
+
|
|
104
|
+
// Handle device types
|
|
105
|
+
switch (devType) {
|
|
106
|
+
case 'LSM6DS':
|
|
107
|
+
// RaftLog.info(`CogStateInfo: updateFromMsg: IMU`);
|
|
108
|
+
|
|
109
|
+
// LSD6DS IMU data is coded as 16-bit signed integers little-endian
|
|
110
|
+
// gx, gy, gz, ax, ay, az
|
|
111
|
+
this.LSM6DS = {
|
|
112
|
+
gx: msgBuffer.readInt16LE(msgBufferOffset) / 16.384,
|
|
113
|
+
gy: msgBuffer.readInt16LE(msgBufferOffset + 2) / 16.384,
|
|
114
|
+
gz: msgBuffer.readInt16LE(msgBufferOffset + 4) / 16.384,
|
|
115
|
+
ax: msgBuffer.readInt16LE(msgBufferOffset + 6) / 8192,
|
|
116
|
+
ay: msgBuffer.readInt16LE(msgBufferOffset + 8) / 8192,
|
|
117
|
+
az: msgBuffer.readInt16LE(msgBufferOffset + 10) / 8192,
|
|
118
|
+
tsMs: timestamp
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Debug
|
|
122
|
+
// RaftLog.info(`CogStateInfo: updateFromMsg: LSM6DS: gx: ${this.LSM6DS.gx.toFixed(2)} gy: ${this.LSM6DS.gy.toFixed(2)} gz: ${this.LSM6DS.gz.toFixed(2)} ax: ${this.LSM6DS.ax.toFixed(2)} ay: ${this.LSM6DS.ay.toFixed(2)} az: ${this.LSM6DS.az.toFixed(2)} tsMs: ${this.LSM6DS.tsMs}`);
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
// const imu = deviceMsg[busName][devAddr] as DeviceMsgJsonElem;
|
|
127
|
+
// this.imu = {x: imu.x as number, y: imu.y as number, z: imu.z as number, lastMs: frameTimeMs};
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return [];
|
|
134
|
+
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private hexToBytes(hex: string): Uint8Array {
|
|
138
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
139
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
140
|
+
bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
|
|
141
|
+
}
|
|
142
|
+
return bytes;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { RaftSubscribeForUpdatesCBType, RaftSystemType } from "../../../../src/RaftSystemType";
|
|
2
|
+
import { RaftEventFn, RaftLog, RaftOKFail, RaftPublishEvent, RaftPublishEventNames, RaftSystemUtils } from "../../../../src/main";
|
|
3
|
+
import { CogStateInfo } from "./CogStateInfo";
|
|
4
|
+
|
|
5
|
+
export default class SystemTypeCog implements RaftSystemType {
|
|
6
|
+
nameForDialogs = "Robotical Cog";
|
|
7
|
+
defaultWiFiHostname = "Cog";
|
|
8
|
+
firmwareDestName = "ricfw";
|
|
9
|
+
normalFileDestName = "fs";
|
|
10
|
+
connectorOptions = {wsSuffix: "wsjson"};
|
|
11
|
+
|
|
12
|
+
// Latest data from servos, IMU, etc
|
|
13
|
+
private _stateInfo: CogStateInfo = new CogStateInfo();
|
|
14
|
+
getStateInfo(): CogStateInfo {
|
|
15
|
+
return this._stateInfo;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Event handler
|
|
19
|
+
private _onEvent: RaftEventFn | null = null;
|
|
20
|
+
|
|
21
|
+
// Raft system utils
|
|
22
|
+
private _systemUtils: RaftSystemUtils | null = null;
|
|
23
|
+
|
|
24
|
+
// Setup
|
|
25
|
+
setup(systemUtils: RaftSystemUtils, onEvent: RaftEventFn | null): void {
|
|
26
|
+
this._systemUtils = systemUtils;
|
|
27
|
+
this._onEvent = onEvent;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Subscribe for updates
|
|
31
|
+
subscribeForUpdates: RaftSubscribeForUpdatesCBType | null = async (systemUtils: RaftSystemUtils, enable: boolean) => {
|
|
32
|
+
// Subscription rate
|
|
33
|
+
const subscribeRateHz = 0.1;
|
|
34
|
+
try {
|
|
35
|
+
const subscribeDisable = '{"cmdName":"subscription","action":"update",' +
|
|
36
|
+
'"pubRecs":[' +
|
|
37
|
+
`{"name":"devjson","rateHz":0,}` +
|
|
38
|
+
']}';
|
|
39
|
+
const subscribeEnable = '{"cmdName":"subscription","action":"update",' +
|
|
40
|
+
'"pubRecs":[' +
|
|
41
|
+
`{"name":"devjson","trigger":"timeorchange","rateHz":${subscribeRateHz.toString()}}` +
|
|
42
|
+
']}';
|
|
43
|
+
|
|
44
|
+
const msgHandler = systemUtils.getMsgHandler();
|
|
45
|
+
const ricResp = await msgHandler.sendRICRESTCmdFrame<RaftOKFail>(
|
|
46
|
+
enable ? subscribeEnable : subscribeDisable
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// Debug
|
|
50
|
+
RaftLog.debug(`subscribe enable/disable returned ${JSON.stringify(ricResp)}`);
|
|
51
|
+
} catch (error: unknown) {
|
|
52
|
+
RaftLog.warn(`getRICCalibInfo Failed subscribe for updates ${error}`);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Invalidate state
|
|
57
|
+
stateIsInvalid(): void {};
|
|
58
|
+
|
|
59
|
+
// Other message type
|
|
60
|
+
rxOtherMsgType(payload: Uint8Array, frameTimeMs: number) {
|
|
61
|
+
|
|
62
|
+
// RICLog.debug(`rxOtherMsgType payload ${RICUtils.bufferToHex(payload)}`);
|
|
63
|
+
RaftLog.verbose(`rxOtherMsgType payloadLen ${payload.length}`);
|
|
64
|
+
const topicIDs = this._stateInfo.updateFromMsg(payload, frameTimeMs);
|
|
65
|
+
|
|
66
|
+
// Call event handler if registered
|
|
67
|
+
if (this._onEvent) {
|
|
68
|
+
this._onEvent("pub", RaftPublishEvent.PUBLISH_EVENT_DATA, RaftPublishEventNames[RaftPublishEvent.PUBLISH_EVENT_DATA],
|
|
69
|
+
{
|
|
70
|
+
topicIDs: topicIDs,
|
|
71
|
+
payload: payload,
|
|
72
|
+
frameTimeMs: frameTimeMs
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { RICHWElem } from "./RICHWElem";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* RICAddOn
|
|
5
|
+
*
|
|
6
|
+
* @description
|
|
7
|
+
* Information about an add-on
|
|
8
|
+
*
|
|
9
|
+
* @field name: string - Name of add-on
|
|
10
|
+
* @field SN: string - Serial number
|
|
11
|
+
* @field poll: string - polling type ("status")
|
|
12
|
+
* @field pollRd: string - hex data most recently read
|
|
13
|
+
* @field pollHz: number - rate of polling
|
|
14
|
+
*/
|
|
15
|
+
export type RICAddOn = {
|
|
16
|
+
name: string;
|
|
17
|
+
SN: string;
|
|
18
|
+
poll: string;
|
|
19
|
+
pollRd: number;
|
|
20
|
+
pollHz: number;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export class RICConfiguredAddOns {
|
|
24
|
+
req = '';
|
|
25
|
+
rslt = 'ok';
|
|
26
|
+
addons: Array<RICAddOn> = [];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* AddOnElemAndConfig
|
|
31
|
+
*
|
|
32
|
+
* @description
|
|
33
|
+
* Carrier of information about an add-on combining
|
|
34
|
+
* the add-on element and the add-on configuration
|
|
35
|
+
*
|
|
36
|
+
* @field addOnConfig: RICAddOn - Add-on configuration
|
|
37
|
+
* @field hwElemRec: RICHWElem - Add-on element
|
|
38
|
+
* @field elemIdx: number - Index of the add-on element
|
|
39
|
+
*/
|
|
40
|
+
export class AddOnElemAndConfig {
|
|
41
|
+
constructor(
|
|
42
|
+
addOnConfig: RICAddOn | null,
|
|
43
|
+
hwElemRec: RICHWElem | null,
|
|
44
|
+
elemIdx: number,
|
|
45
|
+
) {
|
|
46
|
+
this.isConfigured = addOnConfig !== null;
|
|
47
|
+
this.isConnected = hwElemRec !== null;
|
|
48
|
+
if (addOnConfig != null) {
|
|
49
|
+
this.SN = addOnConfig.SN;
|
|
50
|
+
this.name = addOnConfig.name;
|
|
51
|
+
} else if (hwElemRec != null) {
|
|
52
|
+
this.SN = hwElemRec.SN;
|
|
53
|
+
this.name = hwElemRec.name;
|
|
54
|
+
}
|
|
55
|
+
this.addOnConfig = addOnConfig;
|
|
56
|
+
this.hwElemRec = hwElemRec;
|
|
57
|
+
this.id = elemIdx.toString();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Fields from config (stored in RIC NVS using addon REST API)
|
|
61
|
+
addOnConfig: RICAddOn | null = null;
|
|
62
|
+
// Fields from HWElem (from hwstatus command)
|
|
63
|
+
hwElemRec: RICHWElem | null = null;
|
|
64
|
+
// Fields allocated when combining records
|
|
65
|
+
name = '';
|
|
66
|
+
SN = '';
|
|
67
|
+
id = '0';
|
|
68
|
+
isConnected = false;
|
|
69
|
+
isConfigured = false;
|
|
70
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
//
|
|
3
|
+
// RICJS
|
|
4
|
+
// Communications Library
|
|
5
|
+
//
|
|
6
|
+
// Rob Dobson & Chris Greening 2020-2022
|
|
7
|
+
// (C) 2020-2022
|
|
8
|
+
//
|
|
9
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
10
|
+
|
|
11
|
+
import { RaftReportMsg } from '../../../../src/RaftTypes';
|
|
12
|
+
import { ROSSerialAddOnStatus } from './RICROSSerial';
|
|
13
|
+
|
|
14
|
+
export default abstract class RICAddOnBase {
|
|
15
|
+
_name = '';
|
|
16
|
+
_typeName = '';
|
|
17
|
+
_whoAmI = "";
|
|
18
|
+
_whoAmITypeCode = "";
|
|
19
|
+
_isStatic = false;
|
|
20
|
+
_initCmd: string | null = null;
|
|
21
|
+
constructor(name: string, typeName: string, whoAmI: string, whoAmITypeCode: string) {
|
|
22
|
+
this._name = name;
|
|
23
|
+
this._typeName = typeName;
|
|
24
|
+
this._whoAmI = whoAmI;
|
|
25
|
+
this._whoAmITypeCode = whoAmITypeCode;
|
|
26
|
+
}
|
|
27
|
+
abstract processInit(_dataReceived: RaftReportMsg): void;
|
|
28
|
+
abstract processPublishedData(
|
|
29
|
+
addOnID: number,
|
|
30
|
+
statusByte: number,
|
|
31
|
+
rawData: Uint8Array,
|
|
32
|
+
): ROSSerialAddOnStatus;
|
|
33
|
+
}
|
|
@@ -0,0 +1,342 @@
|
|
|
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 RaftMsgHandler from "../../../../src/RaftMsgHandler";
|
|
13
|
+
import { Dictionary, RaftOKFail, RaftReportMsg } from "../../../../src/RaftTypes";
|
|
14
|
+
import RICAddOnBase from "./RICAddOnBase";
|
|
15
|
+
import { RICHWElem } from "./RICHWElem";
|
|
16
|
+
import { ROSSerialAddOnStatus } from "./RICROSSerial";
|
|
17
|
+
|
|
18
|
+
export type RICAddOnCreator = (
|
|
19
|
+
name: string,
|
|
20
|
+
addOnFamily: string,
|
|
21
|
+
whoAmI: string,
|
|
22
|
+
whoAmITypeCode: string
|
|
23
|
+
) => RICAddOnBase;
|
|
24
|
+
|
|
25
|
+
class AddOnFactoryElem {
|
|
26
|
+
typeName: string;
|
|
27
|
+
addOnFamily: string;
|
|
28
|
+
whoAmI: string;
|
|
29
|
+
factoryFn: RICAddOnCreator;
|
|
30
|
+
constructor(
|
|
31
|
+
typeName: string,
|
|
32
|
+
addOnFamily: string,
|
|
33
|
+
whoAmI: string,
|
|
34
|
+
factoryFn: RICAddOnCreator
|
|
35
|
+
) {
|
|
36
|
+
this.addOnFamily = addOnFamily;
|
|
37
|
+
this.typeName = typeName;
|
|
38
|
+
this.whoAmI = whoAmI;
|
|
39
|
+
this.factoryFn = factoryFn;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface RICAddOnRegistry {
|
|
44
|
+
registerHWElemType(
|
|
45
|
+
typeName: string,
|
|
46
|
+
addOnFamily: string,
|
|
47
|
+
whoAmI: string,
|
|
48
|
+
factoryFn: RICAddOnCreator
|
|
49
|
+
): void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* RICAddOnManager
|
|
54
|
+
*
|
|
55
|
+
* @description
|
|
56
|
+
* Handles the creation and management of RIC Add-Ons
|
|
57
|
+
*
|
|
58
|
+
*/
|
|
59
|
+
export default class RICAddOnManager implements RICAddOnRegistry {
|
|
60
|
+
|
|
61
|
+
// Message handler, etc
|
|
62
|
+
private _msgHandler: RaftMsgHandler | null = null;
|
|
63
|
+
|
|
64
|
+
// Add-on factory map
|
|
65
|
+
private _addOnFactoryMap: Dictionary<AddOnFactoryElem> = {};
|
|
66
|
+
|
|
67
|
+
// Configured add-ons
|
|
68
|
+
private _configuredAddOns: Dictionary<RICAddOnBase> = {};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Setup the RIC Add-On Manager
|
|
72
|
+
*/
|
|
73
|
+
setup(msgHandler: RaftMsgHandler): void {
|
|
74
|
+
this._msgHandler = msgHandler;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
*
|
|
79
|
+
* @param typeName Register HWElem type
|
|
80
|
+
* @param addOnFamily
|
|
81
|
+
* @param whoAmI
|
|
82
|
+
* @param factoryFn
|
|
83
|
+
*/
|
|
84
|
+
registerHWElemType(
|
|
85
|
+
typeName: string,
|
|
86
|
+
addOnFamily: string,
|
|
87
|
+
whoAmI: string,
|
|
88
|
+
factoryFn: RICAddOnCreator
|
|
89
|
+
): void {
|
|
90
|
+
RaftLog.debug(`registerHWElemType ${whoAmI} ${typeName}`);
|
|
91
|
+
const lookupStr = addOnFamily + "_" + whoAmI;
|
|
92
|
+
this._addOnFactoryMap[lookupStr] = new AddOnFactoryElem(
|
|
93
|
+
typeName,
|
|
94
|
+
addOnFamily,
|
|
95
|
+
whoAmI,
|
|
96
|
+
factoryFn
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Mark: Set AddOn config -----------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
*
|
|
104
|
+
* setAddOnConfig - set a specified add-on's configuration
|
|
105
|
+
* @param serialNo used to identify the add-on
|
|
106
|
+
* @param newName name to refer to add-on by
|
|
107
|
+
* @returns Promise<RaftOKFail>
|
|
108
|
+
*
|
|
109
|
+
*/
|
|
110
|
+
async setAddOnConfig(serialNo: string, newName: string): Promise<RaftOKFail> {
|
|
111
|
+
try {
|
|
112
|
+
if (this._msgHandler) {
|
|
113
|
+
const msgRslt = await this._msgHandler.sendRICRESTURL<RaftOKFail>(
|
|
114
|
+
`addon/set?SN=${serialNo}&name=${newName}`,
|
|
115
|
+
);
|
|
116
|
+
return msgRslt;
|
|
117
|
+
}
|
|
118
|
+
} catch (error) {
|
|
119
|
+
}
|
|
120
|
+
return new RaftOKFail();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* deleteAddOn - remove an addon from the addonlist on RIC
|
|
125
|
+
* @param serialNo used to identify the add-on
|
|
126
|
+
* @returns Promise<RaftOKFail>
|
|
127
|
+
*/
|
|
128
|
+
async deleteAddOn(serialNo: string): Promise<RaftOKFail> {
|
|
129
|
+
try {
|
|
130
|
+
if (this._msgHandler) {
|
|
131
|
+
const msgRslt = await this._msgHandler.sendRICRESTURL<RaftOKFail>(
|
|
132
|
+
`addon/del?SN=${serialNo}`,
|
|
133
|
+
);
|
|
134
|
+
return msgRslt;
|
|
135
|
+
}
|
|
136
|
+
} catch (error) {
|
|
137
|
+
}
|
|
138
|
+
return new RaftOKFail();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Mark: Identify AddOn -----------------------------------------------------------
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
*
|
|
145
|
+
* identifyAddOn - send the 'identify' command to a specified add-on
|
|
146
|
+
* @param name used to identify the add-on
|
|
147
|
+
* @returns Promise<RaftOKFail>
|
|
148
|
+
*
|
|
149
|
+
*/
|
|
150
|
+
async identifyAddOn(name: string): Promise<RaftOKFail> {
|
|
151
|
+
try {
|
|
152
|
+
if (this._msgHandler) {
|
|
153
|
+
const msgRslt = await this._msgHandler.sendRICRESTURL<RaftOKFail>(
|
|
154
|
+
`elem/${name}/json?cmd=raw&hexWr=F8`,
|
|
155
|
+
);
|
|
156
|
+
return msgRslt;
|
|
157
|
+
}
|
|
158
|
+
} catch (error) {
|
|
159
|
+
}
|
|
160
|
+
return new RaftOKFail();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @function getStaticAddonIds
|
|
165
|
+
* Get the ids of the add-ons that are static
|
|
166
|
+
* (their data do not get published from ricjs, eg buspixel ledeyes)
|
|
167
|
+
* @returns {Array<number>} the ids of the static add-ons
|
|
168
|
+
*/
|
|
169
|
+
getStaticAddonIds(): Array<number> {
|
|
170
|
+
// at this point we will create the buspixel addon for the batch 4 ledeye,
|
|
171
|
+
// as their data is not published the same way the RSAddOn do
|
|
172
|
+
// to do so, however, we need to know if the batch 4 ledeye is connected
|
|
173
|
+
const staticAddonIds = [];
|
|
174
|
+
for (const addOnId in this._configuredAddOns) {
|
|
175
|
+
const addon = this._configuredAddOns[addOnId];
|
|
176
|
+
if (addon._isStatic) {
|
|
177
|
+
staticAddonIds.push(+addOnId);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return staticAddonIds;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @function getStaticAddons
|
|
185
|
+
* Get the add-ons that are static
|
|
186
|
+
* (their data do not get published from ricjs, eg buspixel ledeyes)
|
|
187
|
+
* @returns {Array<RICAddOnBase>} the static add-ons unprocessed
|
|
188
|
+
*/
|
|
189
|
+
getStaticAddons(): Array<RICAddOnBase> {
|
|
190
|
+
const staticAddons = [];
|
|
191
|
+
for (const addOnId in this._configuredAddOns) {
|
|
192
|
+
const addon = this._configuredAddOns[addOnId];
|
|
193
|
+
if (addon._isStatic) {
|
|
194
|
+
staticAddons.push(addon);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return staticAddons;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @function getProcessedStaticAddons
|
|
202
|
+
* Get the add-ons that are static
|
|
203
|
+
* (their data do not get published from ricjs, eg buspixel ledeyes)
|
|
204
|
+
* @returns {Array<ROSSerialAddOnStatus>} the static add-ons processed
|
|
205
|
+
*/
|
|
206
|
+
getProcessedStaticAddons(): Array<ROSSerialAddOnStatus> {
|
|
207
|
+
const ids = this.getStaticAddonIds();
|
|
208
|
+
const staticAddons: ROSSerialAddOnStatus[] = [];
|
|
209
|
+
ids.forEach((id) => {
|
|
210
|
+
const processedAddon = this.processPublishedData(
|
|
211
|
+
id,
|
|
212
|
+
0,
|
|
213
|
+
new Uint8Array(0)
|
|
214
|
+
);
|
|
215
|
+
if (processedAddon) {
|
|
216
|
+
staticAddons.push(processedAddon);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
return staticAddons;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* @function setHWElems
|
|
224
|
+
* Set the hardware elements from a list of RICHWElem
|
|
225
|
+
* @param hwElems
|
|
226
|
+
*
|
|
227
|
+
*/
|
|
228
|
+
setHWElems(hwElems: Array<RICHWElem>): void {
|
|
229
|
+
this._configuredAddOns = this.configureAddOns(hwElems);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
clear(): void {
|
|
233
|
+
this._configuredAddOns = {};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
configureAddOns(hwElems: Array<RICHWElem>): Dictionary<RICAddOnBase> {
|
|
237
|
+
const addOnMap: Dictionary<RICAddOnBase> = {};
|
|
238
|
+
// Iterate HWElems to find addons
|
|
239
|
+
for (const hwElem of hwElems) {
|
|
240
|
+
RaftLog.debug(`configureAddOns whoAmITypeCode ${hwElem.whoAmI}`);
|
|
241
|
+
|
|
242
|
+
// Lookup the add-on
|
|
243
|
+
const lookupStr = hwElem.type + "_" + hwElem.whoAmI;
|
|
244
|
+
if (lookupStr in this._addOnFactoryMap) {
|
|
245
|
+
const addOnFactoryElem = this._addOnFactoryMap[lookupStr];
|
|
246
|
+
const whoAmILen = hwElem.whoAmITypeCode.length;
|
|
247
|
+
hwElem.whoAmITypeCode = hwElem.whoAmITypeCode.slice(
|
|
248
|
+
whoAmILen - 2,
|
|
249
|
+
whoAmILen
|
|
250
|
+
);
|
|
251
|
+
const addOn = addOnFactoryElem.factoryFn(
|
|
252
|
+
hwElem.name,
|
|
253
|
+
hwElem.type,
|
|
254
|
+
hwElem.whoAmI,
|
|
255
|
+
hwElem.whoAmITypeCode
|
|
256
|
+
);
|
|
257
|
+
if (addOn !== null) {
|
|
258
|
+
addOnMap[hwElem.IDNo.toString()] = addOn;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return addOnMap;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
getHWElemTypeStr(
|
|
266
|
+
whoAmITypeCode: string | undefined,
|
|
267
|
+
whoAmI: string | undefined
|
|
268
|
+
) {
|
|
269
|
+
RaftLog.debug(`getting type code for ${whoAmITypeCode}`);
|
|
270
|
+
if (whoAmITypeCode === undefined) {
|
|
271
|
+
return `Undefined whoamiTypeCode`;
|
|
272
|
+
}
|
|
273
|
+
if (whoAmITypeCode in this._addOnFactoryMap) {
|
|
274
|
+
return this._addOnFactoryMap[whoAmITypeCode].typeName;
|
|
275
|
+
}
|
|
276
|
+
return `Unknown (${whoAmI} - ${whoAmITypeCode})`;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
processPublishedData(
|
|
280
|
+
addOnID: number,
|
|
281
|
+
statusByte: number,
|
|
282
|
+
rawData: Uint8Array
|
|
283
|
+
): ROSSerialAddOnStatus | null {
|
|
284
|
+
// Lookup in map
|
|
285
|
+
const addOnIdStr = addOnID.toString();
|
|
286
|
+
if (addOnIdStr in this._configuredAddOns) {
|
|
287
|
+
const addOnHandler = this._configuredAddOns[addOnIdStr];
|
|
288
|
+
const data = addOnHandler.processPublishedData(
|
|
289
|
+
addOnID,
|
|
290
|
+
statusByte,
|
|
291
|
+
rawData
|
|
292
|
+
);
|
|
293
|
+
return data;
|
|
294
|
+
}
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
getIDNoFromName(name: string): string | null {
|
|
298
|
+
for (const key in this._configuredAddOns) {
|
|
299
|
+
if (key in this._configuredAddOns) {
|
|
300
|
+
if (this._configuredAddOns[key]._name == name) return key;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
getInitCmds(): Array<string> {
|
|
307
|
+
const cmds: Array<string> = [];
|
|
308
|
+
for (const key in this._configuredAddOns) {
|
|
309
|
+
if (key in this._configuredAddOns) {
|
|
310
|
+
const initCmd = this._configuredAddOns[key]._initCmd;
|
|
311
|
+
if (initCmd) {
|
|
312
|
+
cmds.push(initCmd);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return cmds;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
processReportMsg(reportMsgs: Array<RaftReportMsg>, timeInitStart: number) {
|
|
320
|
+
for (const reportID in reportMsgs) {
|
|
321
|
+
const report = reportMsgs[reportID];
|
|
322
|
+
//RaftLog.debug(`Report message: ${JSON.stringify(report)}`);
|
|
323
|
+
if (report.timeReceived && report.timeReceived < timeInitStart) {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if (report.elemName) {
|
|
327
|
+
let hwElemIDNoStr = "";
|
|
328
|
+
if (report.IDNo) {
|
|
329
|
+
hwElemIDNoStr = report.IDNo.toString();
|
|
330
|
+
} else if (report.elemName) {
|
|
331
|
+
const maybeIdno = this.getIDNoFromName(report.elemName);
|
|
332
|
+
if (maybeIdno) {
|
|
333
|
+
hwElemIDNoStr = maybeIdno;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (hwElemIDNoStr.length > 0) {
|
|
337
|
+
this._configuredAddOns[hwElemIDNoStr].processInit(report);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|