@robdobsn/raftjs 1.7.8 → 1.10.7
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/TODO.md +1 -0
- package/dist/react-native/RaftAttributeHandler.d.ts +14 -0
- package/dist/react-native/RaftAttributeHandler.js +375 -0
- package/dist/react-native/RaftAttributeHandler.js.map +1 -0
- package/dist/react-native/RaftChannel.d.ts +20 -0
- package/dist/react-native/RaftChannel.js +12 -0
- package/dist/react-native/RaftChannel.js.map +1 -0
- package/dist/react-native/RaftChannelBLE.native.d.ts +95 -0
- package/dist/react-native/RaftChannelBLE.native.js +483 -0
- package/dist/react-native/RaftChannelBLE.native.js.map +1 -0
- package/dist/react-native/RaftChannelBLE.web.d.ts +40 -0
- package/dist/react-native/RaftChannelBLE.web.js +302 -0
- package/dist/react-native/RaftChannelBLE.web.js.map +1 -0
- package/dist/react-native/RaftChannelBLEFactory.d.ts +10 -0
- package/dist/react-native/RaftChannelBLEFactory.js +17 -0
- package/dist/react-native/RaftChannelBLEFactory.js.map +1 -0
- package/dist/react-native/RaftChannelBLEScanner.native.d.ts +18 -0
- package/dist/react-native/RaftChannelBLEScanner.native.js +138 -0
- package/dist/react-native/RaftChannelBLEScanner.native.js.map +1 -0
- package/dist/react-native/RaftChannelSimulated.d.ts +42 -0
- package/dist/react-native/RaftChannelSimulated.js +1000 -0
- package/dist/react-native/RaftChannelSimulated.js.map +1 -0
- package/dist/react-native/RaftChannelWebSerial.d.ts +39 -0
- package/dist/react-native/RaftChannelWebSerial.js +329 -0
- package/dist/react-native/RaftChannelWebSerial.js.map +1 -0
- package/dist/react-native/RaftChannelWebSocket.d.ts +30 -0
- package/dist/react-native/RaftChannelWebSocket.js +222 -0
- package/dist/react-native/RaftChannelWebSocket.js.map +1 -0
- package/dist/react-native/RaftCommsStats.d.ts +39 -0
- package/dist/react-native/RaftCommsStats.js +128 -0
- package/dist/react-native/RaftCommsStats.js.map +1 -0
- package/dist/react-native/RaftConnEvents.d.ts +39 -0
- package/dist/react-native/RaftConnEvents.js +54 -0
- package/dist/react-native/RaftConnEvents.js.map +1 -0
- package/dist/react-native/RaftConnector.d.ts +248 -0
- package/dist/react-native/RaftConnector.js +658 -0
- package/dist/react-native/RaftConnector.js.map +1 -0
- package/dist/react-native/RaftCustomAttrHandler.d.ts +6 -0
- package/dist/react-native/RaftCustomAttrHandler.js +93 -0
- package/dist/react-native/RaftCustomAttrHandler.js.map +1 -0
- package/dist/react-native/RaftDeviceInfo.d.ts +71 -0
- package/dist/react-native/RaftDeviceInfo.js +50 -0
- package/dist/react-native/RaftDeviceInfo.js.map +1 -0
- package/dist/react-native/RaftDeviceManager.d.ts +61 -0
- package/dist/react-native/RaftDeviceManager.js +665 -0
- package/dist/react-native/RaftDeviceManager.js.map +1 -0
- package/dist/react-native/RaftDeviceMgrIF.d.ts +15 -0
- package/dist/react-native/RaftDeviceMgrIF.js +11 -0
- package/dist/react-native/RaftDeviceMgrIF.js.map +1 -0
- package/dist/react-native/RaftDeviceMsg.d.ts +9 -0
- package/dist/react-native/RaftDeviceMsg.js +11 -0
- package/dist/react-native/RaftDeviceMsg.js.map +1 -0
- package/dist/react-native/RaftDeviceStates.d.ts +37 -0
- package/dist/react-native/RaftDeviceStates.js +60 -0
- package/dist/react-native/RaftDeviceStates.js.map +1 -0
- package/dist/react-native/RaftFileHandler.d.ts +52 -0
- package/dist/react-native/RaftFileHandler.js +502 -0
- package/dist/react-native/RaftFileHandler.js.map +1 -0
- package/dist/react-native/RaftLog.d.ts +22 -0
- package/dist/react-native/RaftLog.js +63 -0
- package/dist/react-native/RaftLog.js.map +1 -0
- package/dist/react-native/RaftMiniHDLC.d.ts +18 -0
- package/dist/react-native/RaftMiniHDLC.js +383 -0
- package/dist/react-native/RaftMiniHDLC.js.map +1 -0
- package/dist/react-native/RaftMsgHandler.d.ts +62 -0
- package/dist/react-native/RaftMsgHandler.js +511 -0
- package/dist/react-native/RaftMsgHandler.js.map +1 -0
- package/dist/react-native/RaftMsgTrackInfo.d.ts +17 -0
- package/dist/react-native/RaftMsgTrackInfo.js +42 -0
- package/dist/react-native/RaftMsgTrackInfo.js.map +1 -0
- package/dist/react-native/RaftProtocolDefs.d.ts +30 -0
- package/dist/react-native/RaftProtocolDefs.js +48 -0
- package/dist/react-native/RaftProtocolDefs.js.map +1 -0
- package/dist/react-native/RaftStreamHandler.d.ts +38 -0
- package/dist/react-native/RaftStreamHandler.js +258 -0
- package/dist/react-native/RaftStreamHandler.js.map +1 -0
- package/dist/react-native/RaftStruct.d.ts +3 -0
- package/dist/react-native/RaftStruct.js +258 -0
- package/dist/react-native/RaftStruct.js.map +1 -0
- package/dist/react-native/RaftSysTypeManager.d.ts +16 -0
- package/dist/react-native/RaftSysTypeManager.js +78 -0
- package/dist/react-native/RaftSysTypeManager.js.map +1 -0
- package/dist/react-native/RaftSystemType.d.ts +30 -0
- package/dist/react-native/RaftSystemType.js +3 -0
- package/dist/react-native/RaftSystemType.js.map +1 -0
- package/dist/react-native/RaftSystemUtils.d.ts +136 -0
- package/dist/react-native/RaftSystemUtils.js +412 -0
- package/dist/react-native/RaftSystemUtils.js.map +1 -0
- package/dist/react-native/RaftTypes.d.ts +195 -0
- package/dist/react-native/RaftTypes.js +153 -0
- package/dist/react-native/RaftTypes.js.map +1 -0
- package/dist/react-native/RaftUpdateEvents.d.ts +33 -0
- package/dist/react-native/RaftUpdateEvents.js +46 -0
- package/dist/react-native/RaftUpdateEvents.js.map +1 -0
- package/dist/react-native/RaftUpdateManager.d.ts +61 -0
- package/dist/react-native/RaftUpdateManager.js +621 -0
- package/dist/react-native/RaftUpdateManager.js.map +1 -0
- package/dist/react-native/RaftUtils.d.ts +128 -0
- package/dist/react-native/RaftUtils.js +487 -0
- package/dist/react-native/RaftUtils.js.map +1 -0
- package/dist/react-native/RaftWifiTypes.d.ts +23 -0
- package/dist/react-native/RaftWifiTypes.js +43 -0
- package/dist/react-native/RaftWifiTypes.js.map +1 -0
- package/dist/react-native/main.d.ts +26 -0
- package/dist/react-native/main.js +51 -0
- package/dist/react-native/main.js.map +1 -0
- package/dist/web/RaftAttributeHandler.js +9 -6
- package/dist/web/RaftAttributeHandler.js.map +1 -1
- package/dist/web/RaftChannelBLE.web.js +8 -6
- package/dist/web/RaftChannelBLE.web.js.map +1 -1
- package/dist/web/RaftChannelSimulated.d.ts +10 -0
- package/dist/web/RaftChannelSimulated.js +662 -80
- package/dist/web/RaftChannelSimulated.js.map +1 -1
- package/dist/web/RaftChannelWebSerial.js +2 -2
- package/dist/web/RaftChannelWebSerial.js.map +1 -1
- package/dist/web/RaftChannelWebSocket.js +16 -1
- package/dist/web/RaftChannelWebSocket.js.map +1 -1
- package/dist/web/RaftConnector.d.ts +2 -0
- package/dist/web/RaftConnector.js +38 -15
- package/dist/web/RaftConnector.js.map +1 -1
- package/dist/web/RaftCustomAttrHandler.d.ts +2 -0
- package/dist/web/RaftCustomAttrHandler.js +54 -26
- package/dist/web/RaftCustomAttrHandler.js.map +1 -1
- package/dist/web/RaftDeviceInfo.d.ts +3 -1
- package/dist/web/RaftDeviceInfo.js +17 -3
- package/dist/web/RaftDeviceInfo.js.map +1 -1
- package/dist/web/RaftDeviceManager.d.ts +22 -1
- package/dist/web/RaftDeviceManager.js +210 -44
- package/dist/web/RaftDeviceManager.js.map +1 -1
- package/dist/web/RaftDeviceStates.d.ts +1 -1
- package/dist/web/RaftDeviceStates.js +2 -2
- package/dist/web/RaftDeviceStates.js.map +1 -1
- package/dist/web/RaftMsgHandler.js.map +1 -1
- package/dist/web/RaftStreamHandler.js +2 -1
- 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/RaftUpdateManager.js +1 -1
- package/dist/web/RaftUpdateManager.js.map +1 -1
- package/dist/web/RaftUtils.d.ts +2 -0
- package/dist/web/RaftUtils.js +20 -0
- package/dist/web/RaftUtils.js.map +1 -1
- package/dist/web/main.d.ts +1 -0
- package/dist/web/main.js.map +1 -1
- package/eslint.config.mjs +33 -0
- package/examples/dashboard/package.json +36 -0
- package/examples/dashboard/src/CommandPanel.tsx +147 -0
- package/examples/dashboard/src/ConnManager.ts +166 -0
- package/examples/dashboard/src/DeviceActionsForm.tsx +133 -0
- package/examples/dashboard/src/DeviceAttrsForm.tsx +49 -0
- package/examples/dashboard/src/DeviceLineChart.tsx +163 -0
- package/examples/dashboard/src/DevicePanel.tsx +171 -0
- package/examples/dashboard/src/DevicesPanel.tsx +58 -0
- package/examples/dashboard/src/DispLedGrid.tsx +110 -0
- package/examples/dashboard/src/DispOneLed.tsx +20 -0
- package/examples/dashboard/src/LatencyTest.ts +130 -0
- package/examples/dashboard/src/LatencyTestPanel.tsx +92 -0
- package/examples/dashboard/src/Main.tsx +234 -0
- package/examples/dashboard/src/SettingsManager.ts +67 -0
- package/examples/dashboard/src/SettingsScreen.tsx +174 -0
- package/examples/dashboard/src/StatusPanel.tsx +71 -0
- package/examples/dashboard/src/SystemTypeCog/CogStateInfo.ts +162 -0
- package/examples/dashboard/src/SystemTypeCog/SystemTypeCog.ts +91 -0
- package/examples/dashboard/src/SystemTypeGeneric/StateInfoGeneric.ts +30 -0
- package/examples/dashboard/src/SystemTypeGeneric/SystemTypeGeneric.ts +91 -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 +97 -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 +119 -0
- package/examples/dashboard/src/index.html +15 -0
- package/examples/dashboard/src/index.tsx +13 -0
- package/examples/dashboard/src/styles.css +408 -0
- package/examples/dashboard/tsconfig.json +18 -0
- package/jest.config.js +11 -0
- package/package.json +4 -7
- package/src/RaftAttributeHandler.ts +450 -0
- package/src/RaftChannel.ts +32 -0
- package/src/RaftChannelBLE.native.ts +617 -0
- package/src/RaftChannelBLE.web.ts +374 -0
- package/src/RaftChannelBLEFactory.ts +13 -0
- package/src/RaftChannelBLEScanner.native.ts +184 -0
- package/src/RaftChannelSimulated.ts +1176 -0
- package/src/RaftChannelWebSerial.ts +420 -0
- package/src/RaftChannelWebSocket.ts +272 -0
- package/src/RaftCommsStats.ts +142 -0
- package/src/RaftConnEvents.ts +58 -0
- package/src/RaftConnector.ts +785 -0
- package/src/RaftCustomAttrHandler.ts +117 -0
- package/src/RaftDeviceInfo.ts +125 -0
- package/src/RaftDeviceManager.ts +844 -0
- package/src/RaftDeviceMgrIF.ts +33 -0
- package/src/RaftDeviceMsg.ts +20 -0
- package/src/RaftDeviceStates.ts +92 -0
- package/src/RaftFileHandler.ts +668 -0
- package/src/RaftLog.ts +70 -0
- package/src/RaftMiniHDLC.ts +396 -0
- package/src/RaftMsgHandler.ts +812 -0
- package/src/RaftMsgTrackInfo.ts +51 -0
- package/src/RaftProtocolDefs.ts +46 -0
- package/src/RaftStreamHandler.ts +329 -0
- package/src/RaftStruct.ts +282 -0
- package/src/RaftSysTypeManager.ts +87 -0
- package/src/RaftSystemType.ts +34 -0
- package/src/RaftSystemUtils.ts +489 -0
- package/src/RaftTypes.ts +279 -0
- package/src/RaftUpdateEvents.ts +48 -0
- package/src/RaftUpdateManager.ts +781 -0
- package/src/RaftUtils.ts +514 -0
- package/src/RaftWifiTypes.ts +36 -0
- package/src/main.ts +39 -0
- package/testdata/TestDeviceTypeRecs.json +492 -0
- package/tsconfig.json +30 -0
- package/tsconfig.react-native.json +29 -0
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
//
|
|
3
|
+
// RaftAttributeHandler
|
|
4
|
+
// Attribute handler for Raft devices
|
|
5
|
+
//
|
|
6
|
+
// Rob Dobson (C) 2024
|
|
7
|
+
//
|
|
8
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
9
|
+
|
|
10
|
+
import CustomAttrHandler from "./RaftCustomAttrHandler";
|
|
11
|
+
import { DeviceTypeAttribute, DeviceTypePollRespMetadata, decodeAttrUnitsEncoding, isAttrTypeSigned } from "./RaftDeviceInfo";
|
|
12
|
+
import { DeviceAttributesState, DeviceTimeline } from "./RaftDeviceStates";
|
|
13
|
+
import { structSizeOf, structUnpack } from "./RaftStruct";
|
|
14
|
+
|
|
15
|
+
export default class AttributeHandler {
|
|
16
|
+
|
|
17
|
+
// Custom attribute handler
|
|
18
|
+
private _customAttrHandler = new CustomAttrHandler();
|
|
19
|
+
|
|
20
|
+
// Message timestamp size
|
|
21
|
+
private POLL_RESULT_TIMESTAMP_SIZE = 2;
|
|
22
|
+
private POLL_RESULT_WRAP_VALUE = this.POLL_RESULT_TIMESTAMP_SIZE === 2 ? 65536 : 4294967296;
|
|
23
|
+
private POLL_RESULT_RESOLUTION_US = 1000;
|
|
24
|
+
|
|
25
|
+
public processMsgAttrGroup(msgBuffer: Uint8Array, msgBufIdx: number, deviceTimeline: DeviceTimeline, pollRespMetadata: DeviceTypePollRespMetadata,
|
|
26
|
+
devAttrsState: DeviceAttributesState, maxDataPoints: number): number {
|
|
27
|
+
|
|
28
|
+
// console.log(`processMsgAttrGroup msg ${msgHexStr} timestamp ${timestamp} origTimestamp ${origTimestamp} msgBufIdx ${msgBufIdx}`)
|
|
29
|
+
|
|
30
|
+
// Extract msg timestamp
|
|
31
|
+
const { newBufIdx, timestampUs } = this.extractTimestampAndAdvanceIdx(msgBuffer, msgBufIdx, deviceTimeline);
|
|
32
|
+
if (newBufIdx < 0)
|
|
33
|
+
return -1;
|
|
34
|
+
msgBufIdx = newBufIdx;
|
|
35
|
+
|
|
36
|
+
// Start of message data
|
|
37
|
+
const msgDataStartIdx = msgBufIdx;
|
|
38
|
+
|
|
39
|
+
// New attribute values (in order as they appear in the attributes JSON)
|
|
40
|
+
let newAttrValues: number[][] = [];
|
|
41
|
+
if ("c" in pollRespMetadata) {
|
|
42
|
+
|
|
43
|
+
// Extract attribute values using custom handler
|
|
44
|
+
newAttrValues = this._customAttrHandler.handleAttr(pollRespMetadata, msgBuffer, msgBufIdx);
|
|
45
|
+
|
|
46
|
+
} else {
|
|
47
|
+
|
|
48
|
+
// console.log(`RaftAttrHdlr.processMsgAttrGroup ${JSON.stringify(pollRespMetadata)} msgBufIdx ${msgBufIdx} timestampUs ${timestampUs}`);
|
|
49
|
+
|
|
50
|
+
// Iterate over attributes
|
|
51
|
+
for (let attrIdx = 0; attrIdx < pollRespMetadata.a.length; attrIdx++) {
|
|
52
|
+
|
|
53
|
+
// Get the attribute definition
|
|
54
|
+
const attrDef: DeviceTypeAttribute = pollRespMetadata.a[attrIdx];
|
|
55
|
+
if (!("t" in attrDef)) {
|
|
56
|
+
console.warn(`DeviceManager msg unknown msgBuffer ${msgBuffer} tsUs ${timestampUs} attrDef ${JSON.stringify(attrDef)}`);
|
|
57
|
+
newAttrValues.push([]);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// console.log(`RaftAttrHdlr.processMsgAttrGroup attr ${attrDef.n} msgBufIdx ${msgBufIdx} timestampUs ${timestampUs} attrDef ${JSON.stringify(attrDef)}`);
|
|
62
|
+
|
|
63
|
+
// Process the attribute
|
|
64
|
+
const { values, newMsgBufIdx } = this.processMsgAttribute(attrDef, msgBuffer, msgBufIdx, msgDataStartIdx);
|
|
65
|
+
if (newMsgBufIdx < 0) {
|
|
66
|
+
newAttrValues.push([]);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
msgBufIdx = newMsgBufIdx;
|
|
70
|
+
newAttrValues.push(values);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Number of bytes in group
|
|
75
|
+
let pollRespSizeBytes = msgBufIdx - msgDataStartIdx;
|
|
76
|
+
if (pollRespSizeBytes < pollRespMetadata.b) {
|
|
77
|
+
pollRespSizeBytes = pollRespMetadata.b;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Check if any attributes were added (in addition to timestamp)
|
|
81
|
+
if (newAttrValues.length === 0) {
|
|
82
|
+
console.warn(`DeviceManager msg attrGroup ${JSON.stringify(pollRespMetadata)} newAttrValues ${newAttrValues} is empty`);
|
|
83
|
+
return msgDataStartIdx+pollRespSizeBytes;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// All attributes must have the same number of new values
|
|
87
|
+
const numNewDataPoints = newAttrValues[0].length;
|
|
88
|
+
for (let i = 1; i < newAttrValues.length; i++) {
|
|
89
|
+
if (newAttrValues[i].length !== numNewDataPoints) {
|
|
90
|
+
console.warn(`DeviceManager msg attrGroup ${pollRespMetadata} attrName ${pollRespMetadata.a[i].n} newAttrValues ${newAttrValues} do not have the same length`);
|
|
91
|
+
return msgDataStartIdx+pollRespSizeBytes;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// All attributes in the schema should have values
|
|
96
|
+
if (newAttrValues.length !== pollRespMetadata.a.length) {
|
|
97
|
+
console.warn(`DeviceManager msg attrGroup ${pollRespMetadata} newAttrValues ${newAttrValues} length does not match attrGroup.a length`);
|
|
98
|
+
return msgDataStartIdx+pollRespSizeBytes;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Add the new attribute values to the device state
|
|
102
|
+
for (let attrIdx = 0; attrIdx < pollRespMetadata.a.length; attrIdx++) {
|
|
103
|
+
// Check if attribute already exists in the device state
|
|
104
|
+
const attrDef: DeviceTypeAttribute = pollRespMetadata.a[attrIdx];
|
|
105
|
+
if (!(attrDef.n in devAttrsState)) {
|
|
106
|
+
devAttrsState[attrDef.n] = {
|
|
107
|
+
name: attrDef.n,
|
|
108
|
+
newAttribute: true,
|
|
109
|
+
newData: false,
|
|
110
|
+
numNewValues: 0,
|
|
111
|
+
values: [],
|
|
112
|
+
units: decodeAttrUnitsEncoding(attrDef.u || ""),
|
|
113
|
+
range: attrDef.r || [0, 0],
|
|
114
|
+
format: ("f" in attrDef && typeof attrDef.f == "string") ? attrDef.f : "",
|
|
115
|
+
visibleSeries: "v" in attrDef ? attrDef.v === 0 || attrDef.v === false : ("vs" in attrDef ? (attrDef.vs === 0 || attrDef.vs === false ? false : !!attrDef.vs) : true),
|
|
116
|
+
visibleForm: "v" in attrDef ? attrDef.v === 0 || attrDef.v === false : ("vf" in attrDef ? (attrDef.vf === 0 || attrDef.vf === false ? false : !!attrDef.vf) : true),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Check if any data points need to be discarded
|
|
121
|
+
const discardCount = Math.max(0, devAttrsState[attrDef.n].values.length + newAttrValues[attrIdx].length - maxDataPoints);
|
|
122
|
+
if (discardCount > 0) {
|
|
123
|
+
devAttrsState[attrDef.n].values.splice(0, discardCount);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Add the new values
|
|
127
|
+
devAttrsState[attrDef.n].values.push(...newAttrValues[attrIdx]);
|
|
128
|
+
devAttrsState[attrDef.n].newData = newAttrValues[attrIdx].length > 0;
|
|
129
|
+
devAttrsState[attrDef.n].numNewValues = newAttrValues[attrIdx].length;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Handle the timestamps with increments if specified
|
|
133
|
+
const timeIncUs: number = pollRespMetadata.us ? pollRespMetadata.us : 1000;
|
|
134
|
+
const timestampsUs = Array(numNewDataPoints).fill(0);
|
|
135
|
+
for (let i = 0; i < numNewDataPoints; i++) {
|
|
136
|
+
timestampsUs[i] = timestampUs + i * timeIncUs;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Check if timeline points need to be discarded
|
|
140
|
+
const discardCount = Math.max(0, deviceTimeline.timestampsUs.length + timestampsUs.length - maxDataPoints);
|
|
141
|
+
if (discardCount > 0) {
|
|
142
|
+
deviceTimeline.timestampsUs.splice(0, discardCount);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Add the new timestamps
|
|
146
|
+
deviceTimeline.timestampsUs.push(...timestampsUs);
|
|
147
|
+
|
|
148
|
+
// Validate attributes based on the vft field
|
|
149
|
+
this.validateAttributes(pollRespMetadata, devAttrsState, numNewDataPoints);
|
|
150
|
+
|
|
151
|
+
// Return the next message buffer index
|
|
152
|
+
return msgDataStartIdx+pollRespSizeBytes;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private validateAttributes(pollRespMetadata: DeviceTypePollRespMetadata, devAttrsState: DeviceAttributesState, numNewDataPoints: number): void {
|
|
156
|
+
// Iterate through all attributes to find those with a vft field
|
|
157
|
+
for (let attrIdx = 0; attrIdx < pollRespMetadata.a.length; attrIdx++) {
|
|
158
|
+
const attrDef: DeviceTypeAttribute = pollRespMetadata.a[attrIdx];
|
|
159
|
+
|
|
160
|
+
// Check if this attribute has a vft field
|
|
161
|
+
if (!("vft" in attrDef) || !attrDef.vft) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Get the name of the validating attribute
|
|
166
|
+
const validatingAttrName = attrDef.vft;
|
|
167
|
+
|
|
168
|
+
// Check if the validating attribute exists in the state
|
|
169
|
+
if (!(validatingAttrName in devAttrsState)) {
|
|
170
|
+
console.debug(`Cannot validate attribute ${attrDef.n} as validating attribute ${validatingAttrName} doesn't exist`);
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Get the current attribute state
|
|
175
|
+
const currentAttr = devAttrsState[attrDef.n];
|
|
176
|
+
const validatingAttr = devAttrsState[validatingAttrName];
|
|
177
|
+
|
|
178
|
+
// Check if both attributes have values
|
|
179
|
+
if (!currentAttr.values.length || !validatingAttr.values.length) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Get the most recent values from both attributes
|
|
184
|
+
const numValues = currentAttr.values.length;
|
|
185
|
+
const startIdx = numValues - numNewDataPoints;
|
|
186
|
+
|
|
187
|
+
// Process each of the new values
|
|
188
|
+
for (let i = 0; i < numNewDataPoints; i++) {
|
|
189
|
+
const valueIdx = startIdx + i;
|
|
190
|
+
if (valueIdx >= 0 && valueIdx < numValues) {
|
|
191
|
+
// Check if the validating attribute's value is 0/false at the same index
|
|
192
|
+
const validatingValueIdx = validatingAttr.values.length - numNewDataPoints + i;
|
|
193
|
+
if (validatingValueIdx >= 0 && validatingValueIdx < validatingAttr.values.length) {
|
|
194
|
+
// If the validating attribute's value is 0 or false, mark the current value as invalid
|
|
195
|
+
if (!validatingAttr.values[validatingValueIdx]) {
|
|
196
|
+
currentAttr.values[valueIdx] = NaN; // Using NaN to represent invalid values
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private processMsgAttribute(attrDef: DeviceTypeAttribute, msgBuffer: Uint8Array, msgBufIdx: number, msgDataStartIdx: number): { values: number[], newMsgBufIdx: number} {
|
|
205
|
+
|
|
206
|
+
// Current field message string index
|
|
207
|
+
let curFieldBufIdx = msgBufIdx;
|
|
208
|
+
let attrUsesAbsPos = false;
|
|
209
|
+
|
|
210
|
+
// Check for "at" field which means absolute position in the buffer
|
|
211
|
+
if (attrDef.at !== undefined) {
|
|
212
|
+
// Handle both single value and array of byte positions
|
|
213
|
+
if (Array.isArray(attrDef.at)) {
|
|
214
|
+
// Create a new buffer for non-contiguous data extraction
|
|
215
|
+
const elemSize = structSizeOf(attrDef.t);
|
|
216
|
+
const bytesForType = new Uint8Array(elemSize);
|
|
217
|
+
|
|
218
|
+
// Zero out the buffer
|
|
219
|
+
bytesForType.fill(0);
|
|
220
|
+
|
|
221
|
+
// Copy bytes from the specified positions
|
|
222
|
+
for (let i = 0; i < attrDef.at.length && i < elemSize; i++) {
|
|
223
|
+
const sourceIdx = msgDataStartIdx + attrDef.at[i];
|
|
224
|
+
if (sourceIdx < msgBuffer.length) {
|
|
225
|
+
bytesForType[i] = msgBuffer[sourceIdx];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Use this buffer for attribute extraction
|
|
230
|
+
msgBuffer = bytesForType;
|
|
231
|
+
curFieldBufIdx = 0;
|
|
232
|
+
} else {
|
|
233
|
+
// Standard absolute position in the buffer
|
|
234
|
+
curFieldBufIdx = msgDataStartIdx + attrDef.at;
|
|
235
|
+
}
|
|
236
|
+
attrUsesAbsPos = true;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Check if outside bounds of message
|
|
240
|
+
if (curFieldBufIdx >= msgBuffer.length) {
|
|
241
|
+
// console.warn(`DeviceManager msg outside bounds msgBuffer ${msgBuffer} attrName ${attrDef.n}`);
|
|
242
|
+
return { values: [], newMsgBufIdx: -1 };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Attribute type
|
|
246
|
+
const attrTypesOnly = attrDef.t;
|
|
247
|
+
|
|
248
|
+
// Slice into buffer
|
|
249
|
+
const attrBuf = msgBuffer.slice(curFieldBufIdx);
|
|
250
|
+
|
|
251
|
+
// Check if a mask is used and the value is signed
|
|
252
|
+
const maskOnSignedValue = "m" in attrDef && isAttrTypeSigned(attrTypesOnly);
|
|
253
|
+
|
|
254
|
+
// Extract the value using python-struct
|
|
255
|
+
const unpackValues = structUnpack(maskOnSignedValue ? attrTypesOnly.toUpperCase() : attrTypesOnly, attrBuf);
|
|
256
|
+
let attrValues = unpackValues as number[];
|
|
257
|
+
|
|
258
|
+
// Get number of bytes consumed
|
|
259
|
+
const numBytesConsumed = structSizeOf(attrTypesOnly);
|
|
260
|
+
|
|
261
|
+
// // Check if sign extendable mask specified on signed value
|
|
262
|
+
// if (mmSpecifiedOnSignedValue) {
|
|
263
|
+
// const signBitMask = 1 << (signExtendableMaskSignPos - 1);
|
|
264
|
+
// const valueOnlyMask = signBitMask - 1;
|
|
265
|
+
// if (value & signBitMask) {
|
|
266
|
+
// value = (value & valueOnlyMask) - signBitMask;
|
|
267
|
+
// } else {
|
|
268
|
+
// value = value & valueOnlyMask;
|
|
269
|
+
// }
|
|
270
|
+
// }
|
|
271
|
+
|
|
272
|
+
// Check for XOR mask
|
|
273
|
+
if ("x" in attrDef) {
|
|
274
|
+
const mask = typeof attrDef.x === "string" ? parseInt(attrDef.x, 16) : attrDef.x as number;
|
|
275
|
+
attrValues = attrValues.map((value) => (value >>> 0) ^ mask);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Check for AND mask
|
|
279
|
+
if ("m" in attrDef) {
|
|
280
|
+
const mask = typeof attrDef.m === "string" ? parseInt(attrDef.m, 16) : attrDef.m as number;
|
|
281
|
+
attrValues = attrValues.map((value) => (maskOnSignedValue ? this.signExtend(value, mask) : (value >>> 0) & mask));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Check for a sign-bit
|
|
285
|
+
if ("sb" in attrDef) {
|
|
286
|
+
const signBitPos = attrDef.sb as number;
|
|
287
|
+
const signBitMask = 1 << signBitPos;
|
|
288
|
+
if ("ss" in attrDef) {
|
|
289
|
+
const signBitSubtract = attrDef.ss as number;
|
|
290
|
+
attrValues = attrValues.map((value) => (value & signBitMask) ? signBitSubtract - value : value);
|
|
291
|
+
} else {
|
|
292
|
+
attrValues = attrValues.map((value) => (value & signBitMask) ? value - (signBitMask << 1) : value);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Check for bit shift required
|
|
297
|
+
if ("s" in attrDef && attrDef.s) {
|
|
298
|
+
const bitshift = attrDef.s as number;
|
|
299
|
+
if (bitshift > 0) {
|
|
300
|
+
attrValues = attrValues.map((value) => (value >>> 0) >>> bitshift);
|
|
301
|
+
} else if (bitshift < 0) {
|
|
302
|
+
attrValues = attrValues.map((value) => (value >>> 0) << -bitshift);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Check for divisor
|
|
307
|
+
if ("d" in attrDef && attrDef.d) {
|
|
308
|
+
const divisor = attrDef.d as number;
|
|
309
|
+
attrValues = attrValues.map((value) => (value) / divisor);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Check for value to add
|
|
313
|
+
if ("a" in attrDef && attrDef.a !== undefined) {
|
|
314
|
+
const addValue = attrDef.a as number;
|
|
315
|
+
attrValues = attrValues.map((value) => (value) + addValue);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Apply lookup table if defined
|
|
319
|
+
if ("lut" in attrDef && attrDef.lut !== undefined) {
|
|
320
|
+
attrValues = attrValues.map((value): number => {
|
|
321
|
+
// Skip NaN values
|
|
322
|
+
if (isNaN(value)) {
|
|
323
|
+
return value;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Search through the lookup table rows for a match
|
|
327
|
+
let defaultValue: number | null = null;
|
|
328
|
+
|
|
329
|
+
for (const row of attrDef.lut || []) {
|
|
330
|
+
// Empty string means default for unmatched values
|
|
331
|
+
if (row.r === "") {
|
|
332
|
+
defaultValue = row.v;
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Parse the range string
|
|
337
|
+
if (this.isValueInRangeString(value, row.r)) {
|
|
338
|
+
return row.v;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// If no match found but we have a default, use it
|
|
343
|
+
if (defaultValue !== null) {
|
|
344
|
+
return defaultValue;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Otherwise keep the original value
|
|
348
|
+
return value;
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// const msgBufIdxIn = msgBufIdx;
|
|
353
|
+
|
|
354
|
+
// Move buffer position if using relative positioning
|
|
355
|
+
msgBufIdx += attrUsesAbsPos ? 0 : numBytesConsumed;
|
|
356
|
+
|
|
357
|
+
// console.log(`RaftAttrHdlr.processMsgAttr attr ${attrDef.n} msgBufIdx ${msgBufIdxIn} msgBufIdx ${msgBufIdx} attrUsesAbsPos ${attrUsesAbsPos} numBytesConsumed ${numBytesConsumed} attrValues ${attrValues}`);
|
|
358
|
+
|
|
359
|
+
// if (attrDef.n === "amb0") {
|
|
360
|
+
// console.log(`${new Date().toISOString()} ${attrDef.n} ${attrValues}`);
|
|
361
|
+
// }
|
|
362
|
+
|
|
363
|
+
// Return the value
|
|
364
|
+
return { values: attrValues, newMsgBufIdx: msgBufIdx };
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
private signExtend(value: number, mask: number): number {
|
|
368
|
+
const signBitMask = (mask + 1) >> 1;
|
|
369
|
+
const signBit = value & signBitMask;
|
|
370
|
+
|
|
371
|
+
if (signBit !== 0) { // If sign bit is set
|
|
372
|
+
const highBitsMask = ~mask & ~((mask + 1) >> 1);
|
|
373
|
+
value |= highBitsMask; // Apply the sign extension
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return value;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
private extractTimestampAndAdvanceIdx(msgBuffer: Uint8Array, msgBufIdx: number, timestampWrapHandler: DeviceTimeline):
|
|
380
|
+
{ newBufIdx: number, timestampUs: number } {
|
|
381
|
+
|
|
382
|
+
// Check there are enough bytes for the timestamp
|
|
383
|
+
if (msgBufIdx + this.POLL_RESULT_TIMESTAMP_SIZE > msgBuffer.length) {
|
|
384
|
+
return { newBufIdx: -1, timestampUs: 0 };
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Use struct to extract the timestamp
|
|
388
|
+
const tsBuffer = msgBuffer.slice(msgBufIdx, msgBufIdx + this.POLL_RESULT_TIMESTAMP_SIZE);
|
|
389
|
+
let timestampUs: number;
|
|
390
|
+
if (this.POLL_RESULT_TIMESTAMP_SIZE === 2) {
|
|
391
|
+
timestampUs = structUnpack(">H", tsBuffer)[0] as number * this.POLL_RESULT_RESOLUTION_US;
|
|
392
|
+
} else {
|
|
393
|
+
timestampUs = structUnpack(">I", tsBuffer)[0] as number * this.POLL_RESULT_RESOLUTION_US;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Check if time is before lastReportTimeMs by more than 100ms - in which case a wrap around occurred to add on the max value
|
|
397
|
+
if (timestampUs + 100000 < timestampWrapHandler.lastReportTimestampUs ) {
|
|
398
|
+
timestampWrapHandler.reportTimestampOffsetUs += this.POLL_RESULT_WRAP_VALUE * this.POLL_RESULT_RESOLUTION_US;
|
|
399
|
+
}
|
|
400
|
+
timestampWrapHandler.lastReportTimestampUs = timestampUs;
|
|
401
|
+
|
|
402
|
+
// Offset timestamp
|
|
403
|
+
timestampUs += timestampWrapHandler.reportTimestampOffsetUs;
|
|
404
|
+
|
|
405
|
+
// Advance the index
|
|
406
|
+
msgBufIdx += this.POLL_RESULT_TIMESTAMP_SIZE;
|
|
407
|
+
|
|
408
|
+
// Return the timestamp
|
|
409
|
+
return { newBufIdx: msgBufIdx, timestampUs: timestampUs };
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Helper method to check if a value is in a range string like "42,43,44-45,47"
|
|
413
|
+
private isValueInRangeString(value: number, rangeStr: string): boolean {
|
|
414
|
+
// Round to integer for comparison
|
|
415
|
+
const roundedValue = Math.round(value);
|
|
416
|
+
|
|
417
|
+
// Split the range string by commas
|
|
418
|
+
const parts = rangeStr.split(',');
|
|
419
|
+
|
|
420
|
+
for (const part of parts) {
|
|
421
|
+
// Check if it's a range (contains a hyphen)
|
|
422
|
+
if (part.includes('-')) {
|
|
423
|
+
const [startStr, endStr] = part.split('-');
|
|
424
|
+
|
|
425
|
+
// Handle hex values
|
|
426
|
+
const start = startStr.toLowerCase().startsWith('0x') ?
|
|
427
|
+
parseInt(startStr, 16) : parseInt(startStr, 10);
|
|
428
|
+
const end = endStr.toLowerCase().startsWith('0x') ?
|
|
429
|
+
parseInt(endStr, 16) : parseInt(endStr, 10);
|
|
430
|
+
|
|
431
|
+
if (!isNaN(start) && !isNaN(end) && roundedValue >= start && roundedValue <= end) {
|
|
432
|
+
return true;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
// Check if it's a single value
|
|
436
|
+
else {
|
|
437
|
+
// Handle hex values
|
|
438
|
+
const partValue = part.toLowerCase().startsWith('0x') ?
|
|
439
|
+
parseInt(part, 16) : parseInt(part, 10);
|
|
440
|
+
|
|
441
|
+
if (!isNaN(partValue) && roundedValue === partValue) {
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
//
|
|
3
|
+
// RaftChannel
|
|
4
|
+
// Part of RaftJS
|
|
5
|
+
//
|
|
6
|
+
// Rob Dobson & Chris Greening 2020-2024
|
|
7
|
+
// (C) 2020-2024 All rights reserved
|
|
8
|
+
//
|
|
9
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
10
|
+
|
|
11
|
+
import { RaftConnEventFn } from "./RaftConnEvents";
|
|
12
|
+
import RaftMsgHandler from "./RaftMsgHandler";
|
|
13
|
+
import { ConnectorOptions } from "./RaftSystemType";
|
|
14
|
+
|
|
15
|
+
export default interface RaftChannel
|
|
16
|
+
{
|
|
17
|
+
isConnected(): boolean;
|
|
18
|
+
connect(locator: string | object, connectorOptions: ConnectorOptions): Promise<boolean>;
|
|
19
|
+
disconnect(): Promise<void>;
|
|
20
|
+
getConnectedLocator(): string | object;
|
|
21
|
+
setOnConnEvent(connEventFn: RaftConnEventFn): void;
|
|
22
|
+
setMsgHandler(raftMsgHandler: RaftMsgHandler): void;
|
|
23
|
+
sendTxMsg(msg: Uint8Array, sendWithResponse: boolean): Promise<boolean>;
|
|
24
|
+
sendTxMsgNoAwait(msg: Uint8Array, sendWithResponse: boolean): Promise<boolean>;
|
|
25
|
+
sendTxMsgRaw(msg: string): boolean;
|
|
26
|
+
sendTxMsgRawAndWaitForReply<T>(msgPayload: Uint8Array): T;
|
|
27
|
+
requiresSubscription(): boolean;
|
|
28
|
+
ricRestCmdBeforeDisconnect(): string | null;
|
|
29
|
+
fhBatchAckSize(): number;
|
|
30
|
+
fhFileBlockSize(): number;
|
|
31
|
+
pauseConnection(pause: boolean): void;
|
|
32
|
+
}
|