@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
|
@@ -35,12 +35,14 @@ export default function Main() {
|
|
|
35
35
|
localStorage.getItem('lastIpAddress') || ''
|
|
36
36
|
);
|
|
37
37
|
|
|
38
|
+
const [serialNo, setSerialNo] = useState<string>('');
|
|
39
|
+
|
|
38
40
|
const handleConnect = () => {
|
|
39
41
|
if (ipAddress.trim() === '') {
|
|
40
|
-
console.
|
|
42
|
+
console.warn('No IP address entered');
|
|
41
43
|
return;
|
|
42
44
|
}
|
|
43
|
-
connManager.connect('WebSocket', ipAddress, []);
|
|
45
|
+
connManager.connect('WebSocket', ipAddress, [], null);
|
|
44
46
|
localStorage.setItem('lastIpAddress', ipAddress);
|
|
45
47
|
};
|
|
46
48
|
|
|
@@ -193,10 +195,18 @@ export default function Main() {
|
|
|
193
195
|
</div>
|
|
194
196
|
<div className="info-box">
|
|
195
197
|
<h3>WebBLE</h3>
|
|
198
|
+
<input
|
|
199
|
+
className="serial-no-input"
|
|
200
|
+
id="serial-no"
|
|
201
|
+
type="text"
|
|
202
|
+
placeholder="Serial No (ignored if empty)"
|
|
203
|
+
value={serialNo}
|
|
204
|
+
onChange={(e) => setSerialNo(e.target.value)}
|
|
205
|
+
/>
|
|
196
206
|
<button
|
|
197
207
|
className="action-button"
|
|
198
208
|
onClick={() => {
|
|
199
|
-
connManager.connect('WebBLE', '', sysTypeManager.getAllServiceUUIDs());
|
|
209
|
+
connManager.connect('WebBLE', '', sysTypeManager.getAllServiceUUIDs(), serialNo);
|
|
200
210
|
}}
|
|
201
211
|
>
|
|
202
212
|
Connect
|
|
@@ -207,7 +217,7 @@ export default function Main() {
|
|
|
207
217
|
<button
|
|
208
218
|
className="action-button"
|
|
209
219
|
onClick={() => {
|
|
210
|
-
connManager.connect('WebSerial', '', []);
|
|
220
|
+
connManager.connect('WebSerial', '', [], null);
|
|
211
221
|
}}
|
|
212
222
|
>
|
|
213
223
|
Connect
|
|
@@ -98,7 +98,7 @@ export default class RICSystemUtils {
|
|
|
98
98
|
const retrieveResult = await this.retrieveInfo();
|
|
99
99
|
return retrieveResult;
|
|
100
100
|
} catch (err) {
|
|
101
|
-
RaftLog.
|
|
101
|
+
RaftLog.warn(`retrieveMartySystemInfo: error ${err}`);
|
|
102
102
|
}
|
|
103
103
|
return false;
|
|
104
104
|
}
|
|
@@ -260,7 +260,7 @@ export default class RICSystemUtils {
|
|
|
260
260
|
// Debug
|
|
261
261
|
RaftLog.debug(
|
|
262
262
|
`getHWElemList: found ${hwElemList.hw.length} addons/buspixels`
|
|
263
|
-
|
|
263
|
+
);
|
|
264
264
|
} else if (addToNonAddOnsList) {
|
|
265
265
|
this._hwElemsExcludingAddOns.push(...hwElemList.hw);
|
|
266
266
|
// Debug
|
|
@@ -279,9 +279,9 @@ export default class RICSystemUtils {
|
|
|
279
279
|
try {
|
|
280
280
|
const reports: Array<RaftReportMsg> = [];
|
|
281
281
|
// add callback to subscribe to report messages
|
|
282
|
-
this._msgHandler.reportMsgCallbacksSet("getHWElemCB", function (
|
|
282
|
+
this._msgHandler.reportMsgCallbacksSet("getHWElemCB", async function (
|
|
283
283
|
report: RaftReportMsg
|
|
284
|
-
) {
|
|
284
|
+
): Promise<void> {
|
|
285
285
|
reports.push(report);
|
|
286
286
|
RaftLog.debug(`getHWElemCB Report callback ${JSON.stringify(report)}`);
|
|
287
287
|
});
|
|
@@ -121,6 +121,14 @@ h1 {
|
|
|
121
121
|
width: 100%;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
.serial-no-input {
|
|
125
|
+
border: 1px solid #ccc;
|
|
126
|
+
border-radius: 4px;
|
|
127
|
+
margin-bottom: 10px;
|
|
128
|
+
padding: 10px;
|
|
129
|
+
width: 100%;
|
|
130
|
+
}
|
|
131
|
+
|
|
124
132
|
.command-input {
|
|
125
133
|
padding: 10px;
|
|
126
134
|
margin-bottom: 10px;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robotical/raftjs",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": "Javascript/TS library for Raft library",
|
|
5
5
|
"main": "dist/web/main.js",
|
|
6
6
|
"types": "dist/web/main.d.ts",
|
|
@@ -31,19 +31,18 @@
|
|
|
31
31
|
"watch-all": "tsc -p tsconfig.json --watch & tsc -p tsconfig.react-native.json --watch"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@types/node": "^
|
|
35
|
-
"@types/web-bluetooth": "^0.0.
|
|
36
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
37
|
-
"eslint": "^9.
|
|
38
|
-
"react-native-ble-plx": "^3.
|
|
39
|
-
"typescript": "^5.
|
|
40
|
-
"
|
|
41
|
-
"
|
|
34
|
+
"@types/node": "^22.13.11",
|
|
35
|
+
"@types/web-bluetooth": "^0.0.21",
|
|
36
|
+
"@typescript-eslint/eslint-plugin": "^8.27.0",
|
|
37
|
+
"eslint": "^9.23.0",
|
|
38
|
+
"react-native-ble-plx": "^3.5.0",
|
|
39
|
+
"typescript": "^5.8.2",
|
|
40
|
+
"rimraf": "^6.0.1",
|
|
41
|
+
"@types/text-encoding": "^0.0.40"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"text-encoding": "^0.7.0",
|
|
45
44
|
"isomorphic-ws": "^5.0.0",
|
|
46
|
-
"tslib": "^2.
|
|
45
|
+
"tslib": "^2.8.1"
|
|
47
46
|
},
|
|
48
47
|
"peerDependencies": {
|
|
49
48
|
"react-native-ble-plx": "*",
|
|
@@ -45,6 +45,8 @@ export default class AttributeHandler {
|
|
|
45
45
|
|
|
46
46
|
} else {
|
|
47
47
|
|
|
48
|
+
// console.log(`RaftAttrHdlr.processMsgAttrGroup ${JSON.stringify(pollRespMetadata)} msgBufIdx ${msgBufIdx} timestampUs ${timestampUs}`);
|
|
49
|
+
|
|
48
50
|
// Iterate over attributes
|
|
49
51
|
for (let attrIdx = 0; attrIdx < pollRespMetadata.a.length; attrIdx++) {
|
|
50
52
|
|
|
@@ -56,6 +58,8 @@ export default class AttributeHandler {
|
|
|
56
58
|
continue;
|
|
57
59
|
}
|
|
58
60
|
|
|
61
|
+
// console.log(`RaftAttrHdlr.processMsgAttrGroup attr ${attrDef.n} msgBufIdx ${msgBufIdx} timestampUs ${timestampUs} attrDef ${JSON.stringify(attrDef)}`);
|
|
62
|
+
|
|
59
63
|
// Process the attribute
|
|
60
64
|
const { values, newMsgBufIdx } = this.processMsgAttribute(attrDef, msgBuffer, msgBufIdx, msgDataStartIdx);
|
|
61
65
|
if (newMsgBufIdx < 0) {
|
|
@@ -65,7 +69,6 @@ export default class AttributeHandler {
|
|
|
65
69
|
msgBufIdx = newMsgBufIdx;
|
|
66
70
|
newAttrValues.push(values);
|
|
67
71
|
}
|
|
68
|
-
|
|
69
72
|
}
|
|
70
73
|
|
|
71
74
|
// Number of bytes in group
|
|
@@ -142,19 +145,94 @@ export default class AttributeHandler {
|
|
|
142
145
|
// Add the new timestamps
|
|
143
146
|
deviceTimeline.timestampsUs.push(...timestampsUs);
|
|
144
147
|
|
|
148
|
+
// Validate attributes based on the vft field
|
|
149
|
+
this.validateAttributes(pollRespMetadata, devAttrsState, numNewDataPoints);
|
|
150
|
+
|
|
145
151
|
// Return the next message buffer index
|
|
146
152
|
return msgDataStartIdx+pollRespSizeBytes;
|
|
147
153
|
}
|
|
148
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
|
+
|
|
149
204
|
private processMsgAttribute(attrDef: DeviceTypeAttribute, msgBuffer: Uint8Array, msgBufIdx: number, msgDataStartIdx: number): { values: number[], newMsgBufIdx: number} {
|
|
150
205
|
|
|
151
206
|
// Current field message string index
|
|
152
207
|
let curFieldBufIdx = msgBufIdx;
|
|
153
208
|
let attrUsesAbsPos = false;
|
|
154
209
|
|
|
155
|
-
// Check for "at"
|
|
210
|
+
// Check for "at" field which means absolute position in the buffer
|
|
156
211
|
if (attrDef.at !== undefined) {
|
|
157
|
-
|
|
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
|
+
}
|
|
158
236
|
attrUsesAbsPos = true;
|
|
159
237
|
}
|
|
160
238
|
|
|
@@ -194,13 +272,13 @@ export default class AttributeHandler {
|
|
|
194
272
|
// Check for XOR mask
|
|
195
273
|
if ("x" in attrDef) {
|
|
196
274
|
const mask = typeof attrDef.x === "string" ? parseInt(attrDef.x, 16) : attrDef.x as number;
|
|
197
|
-
attrValues = attrValues.map((value) => value ^ mask);
|
|
275
|
+
attrValues = attrValues.map((value) => (value >>> 0) ^ mask);
|
|
198
276
|
}
|
|
199
277
|
|
|
200
278
|
// Check for AND mask
|
|
201
279
|
if ("m" in attrDef) {
|
|
202
280
|
const mask = typeof attrDef.m === "string" ? parseInt(attrDef.m, 16) : attrDef.m as number;
|
|
203
|
-
attrValues = attrValues.map((value) => (maskOnSignedValue ? this.signExtend(value, mask) : value & mask));
|
|
281
|
+
attrValues = attrValues.map((value) => (maskOnSignedValue ? this.signExtend(value, mask) : (value >>> 0) & mask));
|
|
204
282
|
}
|
|
205
283
|
|
|
206
284
|
// Check for a sign-bit
|
|
@@ -219,9 +297,9 @@ export default class AttributeHandler {
|
|
|
219
297
|
if ("s" in attrDef && attrDef.s) {
|
|
220
298
|
const bitshift = attrDef.s as number;
|
|
221
299
|
if (bitshift > 0) {
|
|
222
|
-
attrValues = attrValues.map((value) => (value)
|
|
300
|
+
attrValues = attrValues.map((value) => (value >>> 0) >>> bitshift);
|
|
223
301
|
} else if (bitshift < 0) {
|
|
224
|
-
attrValues = attrValues.map((value) => (value) << -bitshift);
|
|
302
|
+
attrValues = attrValues.map((value) => (value >>> 0) << -bitshift);
|
|
225
303
|
}
|
|
226
304
|
}
|
|
227
305
|
|
|
@@ -237,10 +315,47 @@ export default class AttributeHandler {
|
|
|
237
315
|
attrValues = attrValues.map((value) => (value) + addValue);
|
|
238
316
|
}
|
|
239
317
|
|
|
240
|
-
//
|
|
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
|
+
|
|
241
354
|
// Move buffer position if using relative positioning
|
|
242
355
|
msgBufIdx += attrUsesAbsPos ? 0 : numBytesConsumed;
|
|
243
356
|
|
|
357
|
+
// console.log(`RaftAttrHdlr.processMsgAttr attr ${attrDef.n} msgBufIdx ${msgBufIdxIn} msgBufIdx ${msgBufIdx} attrUsesAbsPos ${attrUsesAbsPos} numBytesConsumed ${numBytesConsumed} attrValues ${attrValues}`);
|
|
358
|
+
|
|
244
359
|
// if (attrDef.n === "amb0") {
|
|
245
360
|
// console.log(`${new Date().toISOString()} ${attrDef.n} ${attrValues}`);
|
|
246
361
|
// }
|
|
@@ -264,7 +379,7 @@ export default class AttributeHandler {
|
|
|
264
379
|
private extractTimestampAndAdvanceIdx(msgBuffer: Uint8Array, msgBufIdx: number, timestampWrapHandler: DeviceTimeline):
|
|
265
380
|
{ newBufIdx: number, timestampUs: number } {
|
|
266
381
|
|
|
267
|
-
// Check there are enough
|
|
382
|
+
// Check there are enough bytes for the timestamp
|
|
268
383
|
if (msgBufIdx + this.POLL_RESULT_TIMESTAMP_SIZE > msgBuffer.length) {
|
|
269
384
|
return { newBufIdx: -1, timestampUs: 0 };
|
|
270
385
|
}
|
|
@@ -278,8 +393,8 @@ export default class AttributeHandler {
|
|
|
278
393
|
timestampUs = structUnpack(">I", tsBuffer)[0] as number * this.POLL_RESULT_RESOLUTION_US;
|
|
279
394
|
}
|
|
280
395
|
|
|
281
|
-
// Check if time is before lastReportTimeMs - in which case a wrap around occurred to add on the max value
|
|
282
|
-
if (timestampUs < timestampWrapHandler.lastReportTimestampUs) {
|
|
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 ) {
|
|
283
398
|
timestampWrapHandler.reportTimestampOffsetUs += this.POLL_RESULT_WRAP_VALUE * this.POLL_RESULT_RESOLUTION_US;
|
|
284
399
|
}
|
|
285
400
|
timestampWrapHandler.lastReportTimestampUs = timestampUs;
|
|
@@ -294,5 +409,42 @@ export default class AttributeHandler {
|
|
|
294
409
|
return { newBufIdx: msgBufIdx, timestampUs: timestampUs };
|
|
295
410
|
}
|
|
296
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
|
+
}
|
|
297
449
|
|
|
298
450
|
}
|
package/src/RaftChannel.ts
CHANGED
|
@@ -22,6 +22,8 @@ export default interface RaftChannel
|
|
|
22
22
|
setMsgHandler(raftMsgHandler: RaftMsgHandler): void;
|
|
23
23
|
sendTxMsg(msg: Uint8Array, sendWithResponse: boolean): Promise<boolean>;
|
|
24
24
|
sendTxMsgNoAwait(msg: Uint8Array, sendWithResponse: boolean): Promise<boolean>;
|
|
25
|
+
sendTxMsgRaw(msg: string): boolean;
|
|
26
|
+
sendTxMsgRawAndWaitForReply<T>(msgPayload: Uint8Array): T;
|
|
25
27
|
requiresSubscription(): boolean;
|
|
26
28
|
ricRestCmdBeforeDisconnect(): string | null;
|
|
27
29
|
fhBatchAckSize(): number;
|
|
@@ -125,7 +125,7 @@ export default class RaftChannelPhoneBLE implements RaftChannel {
|
|
|
125
125
|
|
|
126
126
|
async discoveryStart(uuids: string[], tries = 10): Promise<boolean> {
|
|
127
127
|
if (tries <= 0) {
|
|
128
|
-
RaftLog.debug(`BLEChannel discoveryStart failed
|
|
128
|
+
RaftLog.debug(`BLEChannel discoveryStart failed`);
|
|
129
129
|
return false;
|
|
130
130
|
}
|
|
131
131
|
// Disconnect any existing connection
|
|
@@ -363,7 +363,7 @@ export default class RaftChannelPhoneBLE implements RaftChannel {
|
|
|
363
363
|
try {
|
|
364
364
|
if (this._bleDevice) {
|
|
365
365
|
if (!this._connectedDeviceServiceUUID) {
|
|
366
|
-
RaftLog.
|
|
366
|
+
RaftLog.warn('BLEChannel _configDeviceConnection - no connected device service UUID');
|
|
367
367
|
return false;
|
|
368
368
|
}
|
|
369
369
|
this._bleSubscrOnRx = this._bleDevice.monitorCharacteristicForService(
|
|
@@ -535,7 +535,7 @@ export default class RaftChannelPhoneBLE implements RaftChannel {
|
|
|
535
535
|
|
|
536
536
|
try {
|
|
537
537
|
if (!this._connectedDeviceServiceUUID) {
|
|
538
|
-
RaftLog.
|
|
538
|
+
RaftLog.warn('BLEChannel sendTxMsg - no connected device service UUID');
|
|
539
539
|
return false;
|
|
540
540
|
}
|
|
541
541
|
await this._bleDevice!.writeCharacteristicWithoutResponseForService(
|
|
@@ -578,7 +578,7 @@ export default class RaftChannelPhoneBLE implements RaftChannel {
|
|
|
578
578
|
|
|
579
579
|
try {
|
|
580
580
|
if (!this._connectedDeviceServiceUUID) {
|
|
581
|
-
RaftLog.
|
|
581
|
+
RaftLog.warn('BLEChannel sendTxMsgNoAwait - no connected device service UUID');
|
|
582
582
|
return false;
|
|
583
583
|
}
|
|
584
584
|
this._bleDevice!.writeCharacteristicWithoutResponseForService(
|
|
@@ -600,9 +600,18 @@ export default class RaftChannelPhoneBLE implements RaftChannel {
|
|
|
600
600
|
|
|
601
601
|
// RICREST command before disconnect
|
|
602
602
|
ricRestCmdBeforeDisconnect(): string | null {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
603
|
+
return "bledisconnect";
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// Method used for testing and simulation should never be called
|
|
607
|
+
sendTxMsgRaw(): boolean {
|
|
608
|
+
RaftLog.debug(`sendTxMsgRaw - not implemented`);
|
|
609
|
+
return false;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Method used for testing and simulation should never be called
|
|
613
|
+
sendTxMsgRawAndWaitForReply<T>(): T {
|
|
614
|
+
RaftLog.debug(`sendTxMsgRawAndWaitForReply - not implemented`);
|
|
615
|
+
return null as T;
|
|
607
616
|
}
|
|
608
617
|
}
|
|
@@ -21,7 +21,7 @@ export default class RaftChannelBLE implements RaftChannel {
|
|
|
21
21
|
_cmdUUID = 'aa76677e-9cfd-4626-a510-0d305be57c8e';
|
|
22
22
|
_respUUID = 'aa76677e-9cfd-4626-a510-0d305be57c8f';
|
|
23
23
|
_serviceUUIDs = ['aa76677e-9cfd-4626-a510-0d305be57c8d', 'da903f65-d5c2-4f4d-a065-d1aade7af874'];
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
// Device and characteristics
|
|
26
26
|
private _bleDevice: BluetoothDevice | null = null;
|
|
27
27
|
private _characteristicTx: BluetoothRemoteGATTCharacteristic | null = null;
|
|
@@ -67,16 +67,13 @@ export default class RaftChannelBLE implements RaftChannel {
|
|
|
67
67
|
|
|
68
68
|
// RICREST command before disconnect
|
|
69
69
|
ricRestCmdBeforeDisconnect(): string | null {
|
|
70
|
-
|
|
71
|
-
// suggested fix: allow callaback command to be sent after disconnect on the fw side
|
|
72
|
-
// return "blerestart";
|
|
73
|
-
return null;
|
|
70
|
+
return "bledisconnect";
|
|
74
71
|
}
|
|
75
72
|
|
|
76
73
|
// isEnabled
|
|
77
74
|
isEnabled() {
|
|
78
75
|
if (navigator.bluetooth) {
|
|
79
|
-
RaftLog.
|
|
76
|
+
RaftLog.warn("Web Bluetooth is supported in your browser.");
|
|
80
77
|
return true;
|
|
81
78
|
} else {
|
|
82
79
|
window.alert(
|
|
@@ -101,7 +98,7 @@ export default class RaftChannelBLE implements RaftChannel {
|
|
|
101
98
|
onDisconnected(event: Event): void {
|
|
102
99
|
const device = event.target as BluetoothDevice;
|
|
103
100
|
RaftLog.debug(`RaftChannelBLE.onDisconnected ${device.name}`);
|
|
104
|
-
if (this._bleDevice) {
|
|
101
|
+
if (this._bleDevice && this._eventListenerFn) {
|
|
105
102
|
this._bleDevice.removeEventListener(
|
|
106
103
|
"gattserverdisconnected",
|
|
107
104
|
this._eventListenerFn
|
|
@@ -157,7 +154,7 @@ export default class RaftChannelBLE implements RaftChannel {
|
|
|
157
154
|
}
|
|
158
155
|
|
|
159
156
|
if (!service) {
|
|
160
|
-
RaftLog.
|
|
157
|
+
RaftLog.warn(
|
|
161
158
|
`RaftChannelBLE.connect - cannot get primary service - giving up`
|
|
162
159
|
);
|
|
163
160
|
return false;
|
|
@@ -211,13 +208,13 @@ export default class RaftChannelBLE implements RaftChannel {
|
|
|
211
208
|
this._isConnected = true;
|
|
212
209
|
return true;
|
|
213
210
|
} catch (error) {
|
|
214
|
-
RaftLog.
|
|
211
|
+
RaftLog.warn(
|
|
215
212
|
`RaftChannelBLE.connect - cannot find characteristic: ${error}`
|
|
216
213
|
);
|
|
217
214
|
}
|
|
218
215
|
} catch (error) {
|
|
219
216
|
if (connRetry === this._maxConnRetries - 1) {
|
|
220
|
-
RaftLog.
|
|
217
|
+
RaftLog.warn(
|
|
221
218
|
`RaftChannelBLE.connect - cannot get primary service ${error} - attempt #${connRetry + 1} - giving up`
|
|
222
219
|
);
|
|
223
220
|
} else {
|
|
@@ -310,12 +307,13 @@ export default class RaftChannelBLE implements RaftChannel {
|
|
|
310
307
|
// Write to the characteristic
|
|
311
308
|
try {
|
|
312
309
|
if (this._characteristicTx) {
|
|
310
|
+
const bs = RaftUtils.toBufferSource(msg);
|
|
313
311
|
if (this._characteristicTx.writeValueWithoutResponse) {
|
|
314
|
-
await this._characteristicTx.writeValueWithoutResponse(
|
|
312
|
+
await this._characteristicTx.writeValueWithoutResponse(bs);
|
|
315
313
|
} else if (this._characteristicTx.writeValue) {
|
|
316
|
-
await this._characteristicTx.writeValue(
|
|
314
|
+
await this._characteristicTx.writeValue(bs);
|
|
317
315
|
} else if (this._characteristicTx.writeValueWithResponse) {
|
|
318
|
-
await this._characteristicTx.writeValueWithResponse(
|
|
316
|
+
await this._characteristicTx.writeValueWithResponse(bs);
|
|
319
317
|
}
|
|
320
318
|
}
|
|
321
319
|
break;
|
|
@@ -348,15 +346,29 @@ export default class RaftChannelBLE implements RaftChannel {
|
|
|
348
346
|
|
|
349
347
|
// Write to the characteristic
|
|
350
348
|
if (this._characteristicTx) {
|
|
349
|
+
const bs = RaftUtils.toBufferSource(msg);
|
|
351
350
|
if (this._characteristicTx.writeValueWithoutResponse) {
|
|
352
|
-
this._characteristicTx.writeValueWithoutResponse(
|
|
351
|
+
this._characteristicTx.writeValueWithoutResponse(bs);
|
|
353
352
|
} else if (this._characteristicTx.writeValue) {
|
|
354
|
-
this._characteristicTx.writeValue(
|
|
353
|
+
this._characteristicTx.writeValue(bs);
|
|
355
354
|
} else if (this._characteristicTx.writeValueWithResponse) {
|
|
356
|
-
this._characteristicTx.writeValueWithResponse(
|
|
355
|
+
this._characteristicTx.writeValueWithResponse(bs);
|
|
357
356
|
}
|
|
358
357
|
return true;
|
|
359
358
|
}
|
|
360
359
|
return false;
|
|
361
360
|
}
|
|
361
|
+
|
|
362
|
+
// Method used for testing and simulation should never be called
|
|
363
|
+
sendTxMsgRaw(): boolean {
|
|
364
|
+
RaftLog.debug(`sendTxMsgRaw - not implemented`);
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Method used for testing and simulation should never be called
|
|
369
|
+
sendTxMsgRawAndWaitForReply<T>(): T {
|
|
370
|
+
RaftLog.debug(`sendTxMsgRawAndWaitForReply - not implemented`);
|
|
371
|
+
return null as T;
|
|
372
|
+
}
|
|
373
|
+
|
|
362
374
|
}
|