@robotical/raftjs 1.4.7 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react-native/RaftAttributeHandler.d.ts +2 -0
- package/dist/react-native/RaftAttributeHandler.js +136 -10
- package/dist/react-native/RaftAttributeHandler.js.map +1 -1
- package/dist/react-native/RaftChannel.d.ts +2 -0
- package/dist/react-native/RaftChannelBLE.native.d.ts +2 -0
- package/dist/react-native/RaftChannelBLE.native.js +14 -7
- package/dist/react-native/RaftChannelBLE.native.js.map +1 -1
- package/dist/react-native/RaftChannelBLE.web.d.ts +2 -0
- package/dist/react-native/RaftChannelBLE.web.js +24 -15
- package/dist/react-native/RaftChannelBLE.web.js.map +1 -1
- package/dist/react-native/RaftChannelSimulated.d.ts +32 -0
- package/dist/react-native/RaftChannelSimulated.js +418 -0
- package/dist/react-native/RaftChannelSimulated.js.map +1 -0
- package/dist/react-native/RaftChannelWebSerial.d.ts +2 -0
- package/dist/react-native/RaftChannelWebSerial.js +18 -9
- package/dist/react-native/RaftChannelWebSerial.js.map +1 -1
- package/dist/react-native/RaftChannelWebSocket.d.ts +2 -0
- package/dist/react-native/RaftChannelWebSocket.js +10 -0
- package/dist/react-native/RaftChannelWebSocket.js.map +1 -1
- package/dist/react-native/RaftConnector.js +13 -6
- package/dist/react-native/RaftConnector.js.map +1 -1
- package/dist/react-native/RaftCustomAttrHandler.js +15 -0
- package/dist/react-native/RaftCustomAttrHandler.js.map +1 -1
- package/dist/react-native/RaftDeviceInfo.d.ts +8 -1
- package/dist/react-native/RaftDeviceInfo.js +17 -3
- package/dist/react-native/RaftDeviceInfo.js.map +1 -1
- package/dist/react-native/RaftDeviceManager.d.ts +3 -0
- package/dist/react-native/RaftDeviceManager.js +123 -43
- package/dist/react-native/RaftDeviceManager.js.map +1 -1
- package/dist/react-native/RaftFileHandler.js +8 -8
- package/dist/react-native/RaftFileHandler.js.map +1 -1
- package/dist/react-native/RaftLog.js +1 -1
- package/dist/react-native/RaftLog.js.map +1 -1
- package/dist/react-native/RaftMsgHandler.d.ts +5 -0
- package/dist/react-native/RaftMsgHandler.js +32 -0
- package/dist/react-native/RaftMsgHandler.js.map +1 -1
- package/dist/react-native/RaftStreamHandler.js +6 -5
- package/dist/react-native/RaftStreamHandler.js.map +1 -1
- package/dist/react-native/RaftStruct.js +197 -147
- package/dist/react-native/RaftStruct.js.map +1 -1
- package/dist/react-native/RaftSysTypeManager.d.ts +2 -0
- package/dist/react-native/RaftSysTypeManager.js +25 -0
- package/dist/react-native/RaftSysTypeManager.js.map +1 -1
- package/dist/react-native/RaftSystemType.d.ts +9 -7
- package/dist/react-native/RaftSystemUtils.js +3 -1
- package/dist/react-native/RaftSystemUtils.js.map +1 -1
- package/dist/react-native/RaftUpdateManager.js +2 -2
- package/dist/react-native/RaftUpdateManager.js.map +1 -1
- package/dist/react-native/RaftUtils.d.ts +2 -0
- package/dist/react-native/RaftUtils.js +22 -2
- package/dist/react-native/RaftUtils.js.map +1 -1
- package/dist/react-native/main.d.ts +2 -0
- package/dist/react-native/main.js +4 -1
- package/dist/react-native/main.js.map +1 -1
- package/dist/web/RaftAttributeHandler.d.ts +2 -0
- package/dist/web/RaftAttributeHandler.js +136 -10
- package/dist/web/RaftAttributeHandler.js.map +1 -1
- package/dist/web/RaftChannel.d.ts +2 -0
- package/dist/web/RaftChannelBLE.web.d.ts +2 -0
- package/dist/web/RaftChannelBLE.web.js +24 -15
- package/dist/web/RaftChannelBLE.web.js.map +1 -1
- package/dist/web/RaftChannelSimulated.d.ts +32 -0
- package/dist/web/RaftChannelSimulated.js +418 -0
- package/dist/web/RaftChannelSimulated.js.map +1 -0
- package/dist/web/RaftChannelWebSerial.d.ts +2 -0
- package/dist/web/RaftChannelWebSerial.js +18 -9
- package/dist/web/RaftChannelWebSerial.js.map +1 -1
- package/dist/web/RaftChannelWebSocket.d.ts +2 -0
- package/dist/web/RaftChannelWebSocket.js +10 -0
- package/dist/web/RaftChannelWebSocket.js.map +1 -1
- package/dist/web/RaftConnector.js +13 -6
- package/dist/web/RaftConnector.js.map +1 -1
- package/dist/web/RaftCustomAttrHandler.js +15 -0
- package/dist/web/RaftCustomAttrHandler.js.map +1 -1
- package/dist/web/RaftDeviceInfo.d.ts +8 -1
- package/dist/web/RaftDeviceInfo.js +17 -3
- package/dist/web/RaftDeviceInfo.js.map +1 -1
- package/dist/web/RaftDeviceManager.d.ts +3 -0
- package/dist/web/RaftDeviceManager.js +123 -43
- package/dist/web/RaftDeviceManager.js.map +1 -1
- package/dist/web/RaftFileHandler.js +8 -8
- package/dist/web/RaftFileHandler.js.map +1 -1
- package/dist/web/RaftLog.js +1 -1
- package/dist/web/RaftLog.js.map +1 -1
- package/dist/web/RaftMsgHandler.d.ts +5 -0
- package/dist/web/RaftMsgHandler.js +32 -0
- package/dist/web/RaftMsgHandler.js.map +1 -1
- package/dist/web/RaftStreamHandler.js +6 -5
- package/dist/web/RaftStreamHandler.js.map +1 -1
- package/dist/web/RaftStruct.js +197 -147
- package/dist/web/RaftStruct.js.map +1 -1
- package/dist/web/RaftSysTypeManager.d.ts +2 -0
- package/dist/web/RaftSysTypeManager.js +25 -0
- package/dist/web/RaftSysTypeManager.js.map +1 -1
- package/dist/web/RaftSystemType.d.ts +9 -7
- package/dist/web/RaftSystemUtils.js +3 -1
- package/dist/web/RaftSystemUtils.js.map +1 -1
- package/dist/web/RaftUpdateManager.js +2 -2
- package/dist/web/RaftUpdateManager.js.map +1 -1
- package/dist/web/RaftUtils.d.ts +2 -0
- package/dist/web/RaftUtils.js +22 -2
- package/dist/web/RaftUtils.js.map +1 -1
- package/dist/web/main.d.ts +2 -0
- package/dist/web/main.js +4 -1
- package/dist/web/main.js.map +1 -1
- package/examples/dashboard/package.json +1 -1
- package/examples/dashboard/src/CommandPanel.tsx +3 -3
- package/examples/dashboard/src/ConnManager.ts +83 -6
- package/examples/dashboard/src/DeviceActionsForm.tsx +2 -2
- package/examples/dashboard/src/DevicePanel.tsx +2 -2
- package/examples/dashboard/src/Main.tsx +14 -4
- package/examples/dashboard/src/SystemTypeMarty/RICSystemUtils.ts +4 -4
- package/examples/dashboard/src/styles.css +8 -0
- package/examples/dashboard/tsconfig.json +1 -1
- package/package.json +10 -11
- package/src/RaftAttributeHandler.ts +163 -11
- package/src/RaftChannel.ts +2 -0
- package/src/RaftChannelBLE.native.ts +17 -8
- package/src/RaftChannelBLE.web.ts +28 -16
- package/src/RaftChannelSimulated.ts +482 -0
- package/src/RaftChannelWebSerial.ts +18 -7
- package/src/RaftChannelWebSocket.ts +13 -0
- package/src/RaftConnector.ts +13 -7
- package/src/RaftCustomAttrHandler.ts +17 -0
- package/src/RaftDeviceInfo.ts +27 -5
- package/src/RaftDeviceManager.ts +155 -47
- package/src/RaftFileHandler.ts +8 -8
- package/src/RaftLog.ts +1 -1
- package/src/RaftMsgHandler.ts +36 -0
- package/src/RaftStreamHandler.ts +48 -47
- package/src/RaftStruct.ts +220 -147
- package/src/RaftSysTypeManager.ts +27 -0
- package/src/RaftSystemType.ts +9 -7
- package/src/RaftSystemUtils.ts +3 -1
- package/src/RaftUpdateManager.ts +2 -2
- package/src/RaftUtils.ts +25 -5
- package/src/main.ts +2 -0
package/src/RaftDeviceInfo.ts
CHANGED
|
@@ -17,10 +17,26 @@ const attrTypeBits: { [key: string]: number } = {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
export function getAttrTypeBits(attrType: string): number {
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
let repeat = 1;
|
|
21
|
+
let baseAttrType = attrType;
|
|
22
|
+
|
|
23
|
+
const repeatStartIdx = attrType.indexOf("[");
|
|
24
|
+
if (repeatStartIdx >= 0) {
|
|
25
|
+
const repeatEndIdx = attrType.indexOf("]", repeatStartIdx + 1);
|
|
26
|
+
if (repeatEndIdx > repeatStartIdx) {
|
|
27
|
+
const repeatStr = attrType.slice(repeatStartIdx + 1, repeatEndIdx);
|
|
28
|
+
const parsedRepeat = parseInt(repeatStr, 10);
|
|
29
|
+
if (Number.isFinite(parsedRepeat) && parsedRepeat > 0) {
|
|
30
|
+
repeat = parsedRepeat;
|
|
31
|
+
}
|
|
32
|
+
baseAttrType = attrType.slice(0, repeatStartIdx);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (baseAttrType in attrTypeBits) {
|
|
37
|
+
return attrTypeBits[baseAttrType] * repeat;
|
|
22
38
|
}
|
|
23
|
-
return 8;
|
|
39
|
+
return 8 * repeat;
|
|
24
40
|
}
|
|
25
41
|
|
|
26
42
|
export function isAttrTypeSigned(attrType: string): boolean {
|
|
@@ -33,10 +49,14 @@ export function decodeAttrUnitsEncoding(unitsEncoding: string): string {
|
|
|
33
49
|
return unitsEncoding.replace(/°/g, "°");
|
|
34
50
|
}
|
|
35
51
|
|
|
52
|
+
export interface LUTRow {
|
|
53
|
+
r: string;
|
|
54
|
+
v: number;
|
|
55
|
+
}
|
|
36
56
|
export interface DeviceTypeAttribute {
|
|
37
57
|
n: string; // Name
|
|
38
58
|
t: string; // Type in python struct module format (e.g. 'H' uint16, 'h' int16, 'f' float etc.)
|
|
39
|
-
at?: number;
|
|
59
|
+
at?: number | number[]; // Start pos in buffer (after timestamp) if present (otherwise use relative position) or array of byte positions for non-contiguous data
|
|
40
60
|
u?: string; // Units (e.g. mm)
|
|
41
61
|
r?: number[]; // Range (either min, max or min, max, step or discrete values)
|
|
42
62
|
x?: number; // XOR bit mask to invert bits in the attribute value
|
|
@@ -51,6 +71,8 @@ export interface DeviceTypeAttribute {
|
|
|
51
71
|
v?: boolean | number; // Visibility of the attribute in all locations (mainly used to hide attributes that are not useful to the user)
|
|
52
72
|
vs?: boolean | number; // Display attribute value in time-series graphs
|
|
53
73
|
vf?: boolean | number; // Display attribute value in the device info panel
|
|
74
|
+
vft?: string; // Attribute validity based on the value of another named attribute
|
|
75
|
+
lut?: Array<LUTRow>; // Lookup table for the attribute value - each row is a lookup table for a range of values e.g. [{"r":"0x20-0x30","v":0},{"r":"1,2,3","v":42},{r:"","v":1}]
|
|
54
76
|
}
|
|
55
77
|
|
|
56
78
|
export interface CustomFunctionDefinition {
|
|
@@ -86,6 +108,7 @@ export interface DeviceTypeInfo {
|
|
|
86
108
|
manu: string;
|
|
87
109
|
type: string;
|
|
88
110
|
resp?: DeviceTypePollRespMetadata;
|
|
111
|
+
clas?: Array<string>;
|
|
89
112
|
actions?: DeviceTypeAction[];
|
|
90
113
|
}
|
|
91
114
|
|
|
@@ -98,4 +121,3 @@ export type RaftDevTypeInfoResponse = {
|
|
|
98
121
|
rslt: string;
|
|
99
122
|
devinfo: DeviceTypeInfo;
|
|
100
123
|
};
|
|
101
|
-
|
package/src/RaftDeviceManager.ts
CHANGED
|
@@ -15,12 +15,10 @@ import AttributeHandler from "./RaftAttributeHandler";
|
|
|
15
15
|
import RaftSystemUtils from "./RaftSystemUtils";
|
|
16
16
|
import RaftDeviceMgrIF from "./RaftDeviceMgrIF";
|
|
17
17
|
import { structPack } from "./RaftStruct";
|
|
18
|
+
// import RaftUtils from "./RaftUtils";
|
|
18
19
|
|
|
19
20
|
export class DeviceManager implements RaftDeviceMgrIF{
|
|
20
21
|
|
|
21
|
-
// Singleton
|
|
22
|
-
// private static _instance: DeviceManager;
|
|
23
|
-
|
|
24
22
|
// Max data points to store
|
|
25
23
|
private _maxDatapointsToStore = 1000;
|
|
26
24
|
|
|
@@ -48,6 +46,9 @@ export class DeviceManager implements RaftDeviceMgrIF{
|
|
|
48
46
|
private _newDeviceAttributeCallbacks: Array<(deviceKey: string, attrState: DeviceAttributeState) => void> = [];
|
|
49
47
|
private _newAttributeDataCallbacks: Array<(deviceKey: string, attrState: DeviceAttributeState) => void> = [];
|
|
50
48
|
|
|
49
|
+
// Debug message index (to help debug with async messages)
|
|
50
|
+
private _debugMsgIndex = 0;
|
|
51
|
+
|
|
51
52
|
public getDevicesState(): DevicesState {
|
|
52
53
|
return this._devicesState;
|
|
53
54
|
}
|
|
@@ -62,6 +63,12 @@ export class DeviceManager implements RaftDeviceMgrIF{
|
|
|
62
63
|
// Cached device type previous attempt times
|
|
63
64
|
private _cachedDeviceTypePreviousAttemptTimes: { [deviceType: string]: number } = {};
|
|
64
65
|
|
|
66
|
+
// Pending device type requests - queue-based to maintain order
|
|
67
|
+
private _pendingDeviceTypeRequests: { [deviceType: string]: {
|
|
68
|
+
promise: Promise<DeviceTypeInfo | undefined>;
|
|
69
|
+
waitingQueue: Array<{resolve: (value: DeviceTypeInfo | undefined) => void, reject: (reason?: any) => void}>;
|
|
70
|
+
} } = {};
|
|
71
|
+
|
|
65
72
|
// Constructor
|
|
66
73
|
constructor() {
|
|
67
74
|
}
|
|
@@ -156,44 +163,71 @@ export class DeviceManager implements RaftDeviceMgrIF{
|
|
|
156
163
|
// console.log(`DeviceManager client1 msg ${RaftUtils.bufferToHex(rxMsg)}`);
|
|
157
164
|
|
|
158
165
|
// Example messages
|
|
159
|
-
// 0080
|
|
160
|
-
// 0080
|
|
166
|
+
// 0080 0015 81 0000006a 0004 53b7 feff00000100081857079314 0011 80 00000000 0011 53b2 075106e400d60054 0010 80 00000000 0012 5231 000d0000010e01
|
|
167
|
+
// 0080 0011 80 00000000 0002 4ae1 0787052606240007 000e 80 00000000 0003 0006 030001af01
|
|
168
|
+
// 0080 0011 80 00000000 0002 e46e 061e05a206830433 0010 80000000000003e4760006030001c701
|
|
169
|
+
// 0080 0010 81 00000015 0004 e4a2 0650fe00305002 0011 80000000000002e4a8061f059f06850438 001080000000000003e4aa0006030001c701
|
|
170
|
+
|
|
171
|
+
// 0080 0011 80 00000000 0002 31e4 05ea05a506660137 000e 80 00000000 0003 0007 030001d901
|
|
172
|
+
|
|
173
|
+
// 0080 0011 80 00000000 0002 4d63 0792053e06500061 000e 80 00000000 0003 0005 030001de01
|
|
161
174
|
|
|
162
175
|
// First two bytes of each message are the message type (0080)
|
|
163
176
|
// There are then a series of sections each of which is the data for a device
|
|
164
|
-
// First two bytes of each section is the section length (big endian) not including the length bytes
|
|
177
|
+
// First two bytes of each section is the section length (big endian) not including the section length bytes themselves
|
|
165
178
|
// Next byte is the connection mode (0 for direct connect, 1+ for bus number) and the MSB of this byte is 1 if the device is online
|
|
166
179
|
// Next is the device address (4 bytes big endian)
|
|
167
180
|
// Next is the device type index (2 bytes big endian)
|
|
168
181
|
// Finally the device data which can be one or more groups of attributes defined by the schema
|
|
169
182
|
|
|
183
|
+
// Debug
|
|
184
|
+
// const debugMsgTime = Date.now();
|
|
185
|
+
const debugMsgIndex = this._debugMsgIndex++;
|
|
186
|
+
|
|
187
|
+
// Message layout
|
|
188
|
+
const msgTypeLen = 2; // Length of the message type (first two bytes)
|
|
189
|
+
const sectionLengthLen = 2; // Length of the inclusive section length (first two bytes of each section)
|
|
190
|
+
const sectionConnectionModeLen = 1; // Length of the connection mode (next byte after section length)
|
|
191
|
+
const sectionDeviceAddrLen = 4; // Length of the device address (next 4 bytes after connection mode)
|
|
192
|
+
const sectionDeviceTypeIdxLen = 2; // Length of the device type index (next 2 bytes after device address)
|
|
193
|
+
const sectionHeaderLen = sectionConnectionModeLen + sectionDeviceAddrLen + sectionDeviceTypeIdxLen;
|
|
194
|
+
|
|
195
|
+
// console.log(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} rxMsg.length ${rxMsg.length} rxMsg ${RaftUtils.bufferToHex(rxMsg)}`);
|
|
196
|
+
|
|
197
|
+
// Start after the message type
|
|
198
|
+
let msgPos = msgTypeLen;
|
|
199
|
+
|
|
170
200
|
// Iterate through sections
|
|
171
|
-
let msgPos = 2;
|
|
172
201
|
while (msgPos < rxMsg.length) {
|
|
173
202
|
|
|
174
203
|
// Check length
|
|
175
|
-
|
|
176
|
-
|
|
204
|
+
const remainingLen = rxMsg.length - msgPos;
|
|
205
|
+
if (remainingLen < sectionLengthLen + sectionHeaderLen) {
|
|
206
|
+
console.warn(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} invalid length ${rxMsg.length} < ${sectionLengthLen + sectionHeaderLen + msgPos}`);
|
|
177
207
|
return;
|
|
178
208
|
}
|
|
179
209
|
|
|
180
210
|
// Get the length of the section
|
|
181
211
|
const sectionLen = (rxMsg[msgPos] << 8) + rxMsg[msgPos + 1];
|
|
182
|
-
if (sectionLen >
|
|
183
|
-
console.warn(`
|
|
212
|
+
if (sectionLen > remainingLen + sectionLengthLen) {
|
|
213
|
+
console.warn(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} invalid msgPos ${msgPos} msgLen ${sectionLen} remainingLenAfterLenBytes ${remainingLen-sectionLengthLen}`);
|
|
184
214
|
return;
|
|
185
215
|
}
|
|
186
216
|
|
|
187
217
|
// Extract message elements
|
|
188
|
-
|
|
189
|
-
const
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
218
|
+
let sectionPos = msgPos + sectionLengthLen;
|
|
219
|
+
const busNum = rxMsg[sectionPos] & 0x7f;
|
|
220
|
+
const isOnline = (rxMsg[sectionPos] & 0x80) !== 0;
|
|
221
|
+
sectionPos += sectionConnectionModeLen;
|
|
222
|
+
// Get the device address and type index
|
|
223
|
+
const devAddr = (rxMsg[sectionPos] << 24) + (rxMsg[sectionPos + 1] << 16) + (rxMsg[sectionPos + 2] << 8) + rxMsg[sectionPos + 3];
|
|
224
|
+
sectionPos += sectionDeviceAddrLen;
|
|
225
|
+
const devTypeIdx = (rxMsg[sectionPos] << 8) + rxMsg[sectionPos + 1];
|
|
226
|
+
let attrGroupPos = sectionPos + sectionDeviceTypeIdxLen;
|
|
193
227
|
|
|
194
228
|
// Debug
|
|
195
|
-
// console.log(`
|
|
196
|
-
// console.log(`
|
|
229
|
+
// console.log(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} overallLen ${rxMsg.length} sectionPos ${msgPos} sectionLen ${sectionLen} ${attrGroupPos} ${RaftUtils.bufferToHex(rxMsg.slice(msgPos, msgPos + sectionLen))}`);
|
|
230
|
+
// console.log(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} connMode ${busNum} isOnline ${isOnline} devAddr ${devAddr} devTypeIdx ${devTypeIdx} attrGroupDataLen ${sectionLen - sectionHeaderLen}`);
|
|
197
231
|
|
|
198
232
|
// Device key
|
|
199
233
|
const deviceKey = getDeviceKey(busNum.toString(), devAddr.toString(), devTypeIdx.toString());
|
|
@@ -203,9 +237,20 @@ export class DeviceManager implements RaftDeviceMgrIF{
|
|
|
203
237
|
|
|
204
238
|
// Check if a device state already exists
|
|
205
239
|
if (!(deviceKey in this._devicesState) || (this._devicesState[deviceKey].deviceTypeInfo === undefined)) {
|
|
240
|
+
|
|
206
241
|
// Get the device type info
|
|
207
|
-
const deviceTypeInfo = await this.getDeviceTypeInfo(busNum.toString(),
|
|
208
|
-
|
|
242
|
+
const deviceTypeInfo = await this.getDeviceTypeInfo(busNum.toString(), devTypeIdx.toString());
|
|
243
|
+
|
|
244
|
+
// Debug
|
|
245
|
+
// console.log(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} attrGroupPos ${attrGroupPos} busNum ${busNum} devAddr ${devAddr} devTypeIdx ${devTypeIdx} deviceTypeInfo ${JSON.stringify(deviceTypeInfo)}`);
|
|
246
|
+
|
|
247
|
+
// Handle case where device type info is not available
|
|
248
|
+
if (deviceTypeInfo === undefined) {
|
|
249
|
+
console.warn(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} deviceType ${devTypeIdx} info not available, skipping attribute processing for this section`);
|
|
250
|
+
// Skip to next section without processing attributes
|
|
251
|
+
msgPos += sectionLengthLen + sectionLen;
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
209
254
|
|
|
210
255
|
// Check if device record exists
|
|
211
256
|
if (deviceKey in this._devicesState) {
|
|
@@ -239,30 +284,59 @@ export class DeviceManager implements RaftDeviceMgrIF{
|
|
|
239
284
|
const deviceState = this._devicesState[deviceKey];
|
|
240
285
|
deviceState.isOnline = isOnline;
|
|
241
286
|
|
|
242
|
-
// Check if device type info is available
|
|
287
|
+
// Check if device type info is available and complete
|
|
243
288
|
if (deviceState.deviceTypeInfo && deviceState.deviceTypeInfo.resp) {
|
|
244
289
|
|
|
245
290
|
// Iterate over attributes in the group
|
|
246
291
|
const pollRespMetadata = deviceState.deviceTypeInfo!.resp!;
|
|
247
292
|
|
|
248
293
|
// Iterate over attribute groups
|
|
249
|
-
|
|
250
|
-
|
|
294
|
+
const attrGroupDataLen = sectionLen - sectionHeaderLen;
|
|
295
|
+
const attrGroupStartPos = attrGroupPos;
|
|
296
|
+
while (attrGroupPos < attrGroupStartPos + attrGroupDataLen) {
|
|
297
|
+
|
|
298
|
+
// Add bounds checking
|
|
299
|
+
if (attrGroupPos >= rxMsg.length) {
|
|
300
|
+
console.warn(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} attrGroupPos ${attrGroupPos} exceeds message length ${rxMsg.length}`);
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
|
|
251
304
|
const newMsgBufIdx = this._attributeHandler.processMsgAttrGroup(rxMsg, attrGroupPos,
|
|
252
305
|
deviceState.deviceTimeline, pollRespMetadata,
|
|
253
306
|
deviceState.deviceAttributes,
|
|
254
307
|
this._maxDatapointsToStore);
|
|
308
|
+
|
|
309
|
+
// console.log(`DevMan.handleClientMsgBinary decoded debugIdx ${debugMsgIndex} devType ${deviceState.deviceTypeInfo.name} attrGroupDataLen ${attrGroupDataLen} attrGroupPos ${attrGroupPos} sectionLen ${sectionLen} msgPos ${msgPos} rxMsgLen ${rxMsg.length} remainingLen ${remainingLen} pollRespMetadata ${JSON.stringify(pollRespMetadata)}`);
|
|
310
|
+
|
|
255
311
|
if (newMsgBufIdx < 0)
|
|
312
|
+
{
|
|
313
|
+
console.warn(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} processMsgAttrGroup failed newMsgBufIdx ${newMsgBufIdx}`);
|
|
256
314
|
break;
|
|
257
|
-
attrGroupPos = newMsgBufIdx;
|
|
258
|
-
if (deviceState.deviceTimeline.timestampsUs.length !== curTimelineLen) {
|
|
259
|
-
deviceState.stateChanged = true;
|
|
260
315
|
}
|
|
316
|
+
|
|
317
|
+
// Prevent infinite loops
|
|
318
|
+
if (newMsgBufIdx <= attrGroupPos) {
|
|
319
|
+
console.warn(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} processMsgAttrGroup didn't advance position from ${attrGroupPos} to ${newMsgBufIdx}`);
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
attrGroupPos = newMsgBufIdx;
|
|
324
|
+
deviceState.stateChanged = true;
|
|
325
|
+
|
|
326
|
+
// console.log(`debugMsgTime ${debugMsgTime} newPt debugMsgIdx ${debugMsgIndex} rxMsgLen ${rxMsg.length} devType ${deviceState.deviceTypeInfo!.name} timestampsUs ${deviceState.deviceTimeline.timestampsUs[deviceState.deviceTimeline.timestampsUs.length - 1]} curTimelineLen ${deviceState.deviceTimeline.timestampsUs.length}`);
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
// console.log(`DevMan.handleClientMsgBinary group done debugIdx ${debugMsgIndex} attrGroupPos ${attrGroupPos} sectionLen ${sectionLen} msgPos ${msgPos} rxMsgLen ${rxMsg.length} remainingLen ${remainingLen}`);
|
|
261
330
|
}
|
|
331
|
+
} else {
|
|
332
|
+
console.warn(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} deviceState incomplete for device ${deviceKey}, skipping attribute processing`);
|
|
262
333
|
}
|
|
263
334
|
|
|
335
|
+
// Debug
|
|
336
|
+
// console.log(`DevMan.handleClientMsgBinary section done debugIdx ${debugMsgIndex} attrGroupPos ${attrGroupPos} sectionLen ${sectionLen} msgPos ${msgPos} newMsgPos ${msgPos + sectionLengthLen + sectionLen} rxMsgLen ${rxMsg.length} remainingLen ${remainingLen}`);
|
|
337
|
+
|
|
264
338
|
// Move to next message
|
|
265
|
-
msgPos +=
|
|
339
|
+
msgPos += sectionLengthLen + sectionLen;
|
|
266
340
|
}
|
|
267
341
|
|
|
268
342
|
// Check for devices that have not been updated for a while
|
|
@@ -276,7 +350,7 @@ export class DeviceManager implements RaftDeviceMgrIF{
|
|
|
276
350
|
}
|
|
277
351
|
|
|
278
352
|
// Process the callback
|
|
279
|
-
this.processStateCallback();
|
|
353
|
+
this.processStateCallback();
|
|
280
354
|
}
|
|
281
355
|
|
|
282
356
|
////////////////////////////////////////////////////////////////////////////
|
|
@@ -324,7 +398,7 @@ export class DeviceManager implements RaftDeviceMgrIF{
|
|
|
324
398
|
if (!(deviceKey in this._devicesState) || (this._devicesState[deviceKey].deviceTypeInfo === undefined)) {
|
|
325
399
|
|
|
326
400
|
// Get the device type info
|
|
327
|
-
const deviceTypeInfo = await this.getDeviceTypeInfo(busName,
|
|
401
|
+
const deviceTypeInfo = await this.getDeviceTypeInfo(busName, deviceTypeName);
|
|
328
402
|
|
|
329
403
|
// Check if device record exists
|
|
330
404
|
if (deviceKey in this._devicesState) {
|
|
@@ -392,7 +466,6 @@ export class DeviceManager implements RaftDeviceMgrIF{
|
|
|
392
466
|
// Loop
|
|
393
467
|
while (msgBufIdx < msgBytes.length) {
|
|
394
468
|
|
|
395
|
-
const curTimelineLen = deviceState.deviceTimeline.timestampsUs.length;
|
|
396
469
|
const newMsgBufIdx = this._attributeHandler.processMsgAttrGroup(msgBytes, msgBufIdx,
|
|
397
470
|
deviceState.deviceTimeline, pollRespMetadata,
|
|
398
471
|
deviceState.deviceAttributes,
|
|
@@ -400,9 +473,7 @@ export class DeviceManager implements RaftDeviceMgrIF{
|
|
|
400
473
|
if (newMsgBufIdx < 0)
|
|
401
474
|
break;
|
|
402
475
|
msgBufIdx = newMsgBufIdx;
|
|
403
|
-
|
|
404
|
-
deviceState.stateChanged = true;
|
|
405
|
-
}
|
|
476
|
+
deviceState.stateChanged = true;
|
|
406
477
|
}
|
|
407
478
|
});
|
|
408
479
|
});
|
|
@@ -455,39 +526,76 @@ export class DeviceManager implements RaftDeviceMgrIF{
|
|
|
455
526
|
// Get device type info
|
|
456
527
|
////////////////////////////////////////////////////////////////////////////
|
|
457
528
|
|
|
458
|
-
private async getDeviceTypeInfo(busName: string,
|
|
459
|
-
|
|
460
|
-
// Check if already in the cache
|
|
529
|
+
private async getDeviceTypeInfo(busName: string, deviceType: string): Promise<DeviceTypeInfo | undefined> {
|
|
530
|
+
// Check if already in cache
|
|
461
531
|
if (deviceType in this._cachedDeviceTypeRecs) {
|
|
462
532
|
return this._cachedDeviceTypeRecs[deviceType];
|
|
463
533
|
}
|
|
464
534
|
|
|
465
|
-
// Check if
|
|
535
|
+
// Check if there's already a pending request for this device type
|
|
536
|
+
if (deviceType in this._pendingDeviceTypeRequests) {
|
|
537
|
+
// console.log(`DevMan.getDeviceTypeInfo joining existing request queue for deviceType ${deviceType}`);
|
|
538
|
+
|
|
539
|
+
// Add this request to the waiting queue
|
|
540
|
+
return new Promise<DeviceTypeInfo | undefined>((resolve, reject) => {
|
|
541
|
+
this._pendingDeviceTypeRequests[deviceType].waitingQueue.push({ resolve, reject });
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Check rate limiting for new requests
|
|
466
546
|
if (deviceType in this._cachedDeviceTypePreviousAttemptTimes) {
|
|
467
|
-
|
|
468
|
-
if (
|
|
547
|
+
const timeSinceLastAttempt = Date.now() - this._cachedDeviceTypePreviousAttemptTimes[deviceType];
|
|
548
|
+
if (timeSinceLastAttempt < this._minTimeBetweenDeviceTypeInfoRetrievalMs) {
|
|
549
|
+
console.log(`DevMan.getDeviceTypeInfo rate limited for deviceType ${deviceType}`);
|
|
469
550
|
return undefined;
|
|
470
551
|
}
|
|
471
552
|
}
|
|
472
|
-
this._cachedDeviceTypePreviousAttemptTimes[deviceType] = Date.now();
|
|
473
553
|
|
|
474
|
-
//
|
|
554
|
+
// Create and cache the promise with an empty waiting queue
|
|
555
|
+
const requestPromise = this.executeDeviceTypeInfoRequest(busName, deviceType);
|
|
556
|
+
this._pendingDeviceTypeRequests[deviceType] = {
|
|
557
|
+
promise: requestPromise,
|
|
558
|
+
waitingQueue: []
|
|
559
|
+
};
|
|
560
|
+
|
|
475
561
|
try {
|
|
476
|
-
|
|
477
|
-
|
|
562
|
+
const result = await requestPromise;
|
|
563
|
+
|
|
564
|
+
// Resolve all waiting requests with the same result
|
|
565
|
+
const waitingQueue = this._pendingDeviceTypeRequests[deviceType].waitingQueue;
|
|
566
|
+
waitingQueue.forEach(({ resolve }) => resolve(result));
|
|
567
|
+
|
|
568
|
+
return result;
|
|
569
|
+
} catch (error) {
|
|
570
|
+
// Reject all waiting requests with the same error
|
|
571
|
+
const waitingQueue = this._pendingDeviceTypeRequests[deviceType].waitingQueue;
|
|
572
|
+
waitingQueue.forEach(({ reject }) => reject(error));
|
|
573
|
+
|
|
574
|
+
console.warn(`DevMan.getDeviceTypeInfo failed for ${deviceType}: ${error}`);
|
|
575
|
+
return undefined;
|
|
576
|
+
} finally {
|
|
577
|
+
// Clean up the pending request
|
|
578
|
+
delete this._pendingDeviceTypeRequests[deviceType];
|
|
579
|
+
}
|
|
580
|
+
}
|
|
478
581
|
|
|
479
|
-
|
|
582
|
+
private async executeDeviceTypeInfoRequest(busName: string, deviceType: string): Promise<DeviceTypeInfo | undefined> {
|
|
583
|
+
this._cachedDeviceTypePreviousAttemptTimes[deviceType] = Date.now();
|
|
584
|
+
|
|
585
|
+
try {
|
|
586
|
+
const cmd = "devman/typeinfo?bus=" + busName + "&type=" + deviceType;
|
|
480
587
|
const msgHandler = this._systemUtils?.getMsgHandler();
|
|
588
|
+
|
|
481
589
|
if (msgHandler) {
|
|
482
590
|
const msgRslt = await msgHandler.sendRICRESTURL<RaftDevTypeInfoResponse>(cmd);
|
|
483
|
-
if (msgRslt.rslt === "ok") {
|
|
591
|
+
if (msgRslt && msgRslt.rslt === "ok") {
|
|
484
592
|
this._cachedDeviceTypeRecs[deviceType] = msgRslt.devinfo;
|
|
485
|
-
return msgRslt.devinfo
|
|
593
|
+
return msgRslt.devinfo;
|
|
486
594
|
}
|
|
487
595
|
}
|
|
488
596
|
return undefined;
|
|
489
597
|
} catch (error) {
|
|
490
|
-
console.
|
|
598
|
+
console.warn(`DeviceManager getDeviceTypeInfo error ${error}`);
|
|
491
599
|
return undefined;
|
|
492
600
|
}
|
|
493
601
|
}
|
package/src/RaftFileHandler.ts
CHANGED
|
@@ -162,11 +162,11 @@ export default class RaftFileHandler {
|
|
|
162
162
|
RICRESTElemCode.RICREST_ELEM_CODE_COMMAND_FRAME,
|
|
163
163
|
);
|
|
164
164
|
} catch (err) {
|
|
165
|
-
RaftLog.
|
|
165
|
+
RaftLog.warn(`sendFileStartMsg error ${err}`);
|
|
166
166
|
return false;
|
|
167
167
|
}
|
|
168
168
|
if (fileStartResp.rslt !== 'ok') {
|
|
169
|
-
RaftLog.
|
|
169
|
+
RaftLog.warn(`sendFileStartMsg error ${fileStartResp.rslt}`);
|
|
170
170
|
return false;
|
|
171
171
|
}
|
|
172
172
|
|
|
@@ -209,7 +209,7 @@ export default class RaftFileHandler {
|
|
|
209
209
|
await this.awaitOutstandingMsgPromises(true);
|
|
210
210
|
} catch (err) {
|
|
211
211
|
// Ignore
|
|
212
|
-
RaftLog.
|
|
212
|
+
RaftLog.warn(`sendFileEndMsg awaitOutstandingMsgPromises error ${err}`);
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
// Send
|
|
@@ -220,7 +220,7 @@ export default class RaftFileHandler {
|
|
|
220
220
|
RICRESTElemCode.RICREST_ELEM_CODE_COMMAND_FRAME,
|
|
221
221
|
);
|
|
222
222
|
} catch (err) {
|
|
223
|
-
RaftLog.
|
|
223
|
+
RaftLog.warn(`sendFileEndMsg error ${err}`);
|
|
224
224
|
return false;
|
|
225
225
|
}
|
|
226
226
|
return fileEndResp.rslt === 'ok';
|
|
@@ -240,7 +240,7 @@ export default class RaftFileHandler {
|
|
|
240
240
|
RICRESTElemCode.RICREST_ELEM_CODE_COMMAND_FRAME,
|
|
241
241
|
);
|
|
242
242
|
} catch (err) {
|
|
243
|
-
RaftLog.
|
|
243
|
+
RaftLog.warn(`sendFileCancelMsg error ${err}`);
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
|
|
@@ -438,7 +438,7 @@ export default class RaftFileHandler {
|
|
|
438
438
|
const bridgedDeviceSerialPort = "Serial" + fileSource.slice(bridgeSerialPrefix.length);
|
|
439
439
|
const cmdResp = await this._msgHandler.createCommsBridge(bridgedDeviceSerialPort, "fileSource");
|
|
440
440
|
if (cmdResp.rslt != "ok") {
|
|
441
|
-
RaftLog.
|
|
441
|
+
RaftLog.warn(`fileReceive - failed to setup bridge ${cmdResp.rslt}`);
|
|
442
442
|
return new RaftFileDownloadResult();
|
|
443
443
|
}
|
|
444
444
|
bridgeID = cmdResp.bridgeID;
|
|
@@ -490,7 +490,7 @@ export default class RaftFileHandler {
|
|
|
490
490
|
bridgeID,
|
|
491
491
|
);
|
|
492
492
|
} catch (err) {
|
|
493
|
-
RaftLog.
|
|
493
|
+
RaftLog.warn(`_receiveFileStartMsg error ${err}`);
|
|
494
494
|
return false;
|
|
495
495
|
}
|
|
496
496
|
RaftLog.info(`_receiveFileStartMsg rslt ${JSON.stringify(cmdResp)}`);
|
|
@@ -530,7 +530,7 @@ export default class RaftFileHandler {
|
|
|
530
530
|
// Check CRC
|
|
531
531
|
const crc16 = RaftMiniHDLC.crc16(this._fileRxBuffer);
|
|
532
532
|
if (crc16 !== this._fileRxCrc16) {
|
|
533
|
-
RaftLog.
|
|
533
|
+
RaftLog.warn(`_receiveFileContents - CRC error ${crc16} ${this._fileRxCrc16}`);
|
|
534
534
|
reject(new Error('fileReceive CRC error'));
|
|
535
535
|
return;
|
|
536
536
|
} else {
|
package/src/RaftLog.ts
CHANGED
package/src/RaftMsgHandler.ts
CHANGED
|
@@ -60,6 +60,8 @@ export interface RaftMessageSender {
|
|
|
60
60
|
msg: Uint8Array,
|
|
61
61
|
sendWithResponse: boolean,
|
|
62
62
|
): Promise<boolean>;
|
|
63
|
+
sendTxMsgRaw(msg: string): boolean;
|
|
64
|
+
sendTxMsgRawAndWaitForReply<T>(msgPayload: Uint8Array): T;
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
export default class RaftMsgHandler {
|
|
@@ -87,6 +89,9 @@ export default class RaftMsgHandler {
|
|
|
87
89
|
// RaftMiniHDLC - handles part of RICSerial protocol
|
|
88
90
|
private _miniHDLC: RaftMiniHDLC;
|
|
89
91
|
|
|
92
|
+
// Raw message mode
|
|
93
|
+
private _rawMsgMode = false;
|
|
94
|
+
|
|
90
95
|
// Constructor
|
|
91
96
|
constructor(commsStats: RaftCommsStats) {
|
|
92
97
|
this._commsStats = commsStats;
|
|
@@ -120,6 +125,22 @@ export default class RaftMsgHandler {
|
|
|
120
125
|
// RaftLog.verbose(`handleNewRxMsg len ${rxMsg.length} ${RaftUtils.bufferToHex(rxMsg)}`)
|
|
121
126
|
}
|
|
122
127
|
|
|
128
|
+
// This is currently only for testing and simulated channels
|
|
129
|
+
handleNewRxMsgRaw(rxMsg: Uint8Array, rxMsgType: RaftCommsMsgTypeCode, rxMsgNum: number, rxMsgTimeMs: number): void {
|
|
130
|
+
// Check message types
|
|
131
|
+
if (rxMsgType == RaftCommsMsgTypeCode.MSG_TYPE_RESPONSE) {
|
|
132
|
+
// Convert raw Uint8Array to string assuming UTF-8
|
|
133
|
+
const restStr = new TextDecoder('utf-8').decode(rxMsg);
|
|
134
|
+
this._handleResponseMessages(restStr, rxMsgNum);
|
|
135
|
+
} else if (rxMsgType == RaftCommsMsgTypeCode.MSG_TYPE_REPORT) {
|
|
136
|
+
// Convert raw Uint8Array to string assuming UTF-8
|
|
137
|
+
const restStr = new TextDecoder('utf-8').decode(rxMsg);
|
|
138
|
+
this._handleReportMessages(restStr);
|
|
139
|
+
} else {
|
|
140
|
+
this._msgResultHandler?.onRxOtherMsgType(rxMsg, rxMsgTimeMs);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
123
144
|
reportMsgCallbacksSet(callbackName: string, callback: (report: RaftReportMsg) => Promise<void>): void {
|
|
124
145
|
this._reportMsgCallbacks.set(callbackName, callback);
|
|
125
146
|
}
|
|
@@ -128,6 +149,11 @@ export default class RaftMsgHandler {
|
|
|
128
149
|
this._reportMsgCallbacks.delete(callbackName);
|
|
129
150
|
}
|
|
130
151
|
|
|
152
|
+
setRawMsgMode(isRawMode: boolean): void {
|
|
153
|
+
this._rawMsgMode = isRawMode;
|
|
154
|
+
RaftLog.debug(`setRawMsgMode ${isRawMode}`);
|
|
155
|
+
}
|
|
156
|
+
|
|
131
157
|
_onHDLCFrameDecode(rxMsg: Uint8Array, frameTimeMs: number): void {
|
|
132
158
|
// Add to stats
|
|
133
159
|
this._commsStats.msgRx();
|
|
@@ -336,6 +362,11 @@ export default class RaftMsgHandler {
|
|
|
336
362
|
return false;
|
|
337
363
|
}
|
|
338
364
|
|
|
365
|
+
// Check for raw message mode (used for testing and simulated channels)
|
|
366
|
+
if (this._rawMsgMode) {
|
|
367
|
+
return this._msgSender.sendTxMsgRaw(cmdStr);
|
|
368
|
+
}
|
|
369
|
+
|
|
339
370
|
// Put cmdStr into buffer
|
|
340
371
|
const cmdBytes = new Uint8Array(cmdStr.length + 1);
|
|
341
372
|
RaftUtils.addStringToBuffer(cmdBytes, cmdStr, 0);
|
|
@@ -406,6 +437,11 @@ export default class RaftMsgHandler {
|
|
|
406
437
|
throw new Error('sendMsgAndWaitForReply failed no sender');
|
|
407
438
|
}
|
|
408
439
|
|
|
440
|
+
// Check for raw message mode (used for testing and simulated channels)
|
|
441
|
+
if (this._rawMsgMode) {
|
|
442
|
+
return this._msgSender.sendTxMsgRawAndWaitForReply(msgPayload);
|
|
443
|
+
}
|
|
444
|
+
|
|
409
445
|
// Frame the message
|
|
410
446
|
let framedMsg = this.frameCommsMsg(msgPayload, msgDirection, msgProtocol, true);
|
|
411
447
|
|