@robdobsn/raftjs 1.10.7 → 1.11.6
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.js +11 -9
- package/dist/react-native/RaftAttributeHandler.js.map +1 -1
- package/dist/react-native/RaftChannelSimulated.js +4 -3
- package/dist/react-native/RaftChannelSimulated.js.map +1 -1
- package/dist/react-native/RaftConnector.d.ts +10 -1
- package/dist/react-native/RaftConnector.js +23 -10
- package/dist/react-native/RaftConnector.js.map +1 -1
- package/dist/react-native/RaftDeviceManager.d.ts +14 -2
- package/dist/react-native/RaftDeviceManager.js +224 -77
- package/dist/react-native/RaftDeviceManager.js.map +1 -1
- package/dist/react-native/RaftDeviceMgrIF.d.ts +5 -1
- package/dist/react-native/RaftDeviceStates.d.ts +21 -3
- package/dist/react-native/RaftDeviceStates.js +31 -6
- package/dist/react-native/RaftDeviceStates.js.map +1 -1
- package/dist/react-native/RaftPublish.d.ts +2 -0
- package/dist/react-native/RaftPublish.js +81 -0
- package/dist/react-native/RaftPublish.js.map +1 -0
- package/dist/react-native/RaftStreamHandler.d.ts +11 -0
- package/dist/react-native/RaftStreamHandler.js +66 -0
- package/dist/react-native/RaftStreamHandler.js.map +1 -1
- package/dist/react-native/RaftStruct.d.ts +2 -2
- package/dist/react-native/RaftStruct.js +97 -26
- package/dist/react-native/RaftStruct.js.map +1 -1
- package/dist/react-native/RaftSystemUtils.d.ts +17 -1
- package/dist/react-native/RaftSystemUtils.js +51 -0
- package/dist/react-native/RaftSystemUtils.js.map +1 -1
- package/dist/react-native/RaftTypes.d.ts +21 -0
- package/dist/react-native/RaftTypes.js.map +1 -1
- package/dist/react-native/main.d.ts +1 -0
- package/dist/react-native/main.js +1 -0
- package/dist/react-native/main.js.map +1 -1
- package/dist/web/RaftAttributeHandler.js +11 -9
- package/dist/web/RaftAttributeHandler.js.map +1 -1
- package/dist/web/RaftChannelSimulated.js +4 -3
- package/dist/web/RaftChannelSimulated.js.map +1 -1
- package/dist/web/RaftConnector.d.ts +10 -1
- package/dist/web/RaftConnector.js +23 -10
- package/dist/web/RaftConnector.js.map +1 -1
- package/dist/web/RaftDeviceManager.d.ts +14 -2
- package/dist/web/RaftDeviceManager.js +224 -77
- package/dist/web/RaftDeviceManager.js.map +1 -1
- package/dist/web/RaftDeviceMgrIF.d.ts +5 -1
- package/dist/web/RaftDeviceStates.d.ts +21 -3
- package/dist/web/RaftDeviceStates.js +31 -6
- package/dist/web/RaftDeviceStates.js.map +1 -1
- package/dist/web/RaftPublish.d.ts +2 -0
- package/dist/web/RaftPublish.js +81 -0
- package/dist/web/RaftPublish.js.map +1 -0
- package/dist/web/RaftStreamHandler.d.ts +11 -0
- package/dist/web/RaftStreamHandler.js +66 -0
- package/dist/web/RaftStreamHandler.js.map +1 -1
- package/dist/web/RaftStruct.d.ts +2 -2
- package/dist/web/RaftStruct.js +97 -26
- package/dist/web/RaftStruct.js.map +1 -1
- package/dist/web/RaftSystemUtils.d.ts +17 -1
- package/dist/web/RaftSystemUtils.js +51 -0
- package/dist/web/RaftSystemUtils.js.map +1 -1
- package/dist/web/RaftTypes.d.ts +21 -0
- package/dist/web/RaftTypes.js.map +1 -1
- package/dist/web/main.d.ts +1 -0
- package/dist/web/main.js +1 -0
- package/dist/web/main.js.map +1 -1
- package/examples/dashboard/package.json +1 -1
- package/examples/dashboard/src/DeviceActionsForm.tsx +2 -2
- package/examples/dashboard/src/DeviceLineChart.tsx +16 -3
- package/examples/dashboard/src/DevicePanel.tsx +79 -3
- package/examples/dashboard/src/DeviceStatsPanel.tsx +76 -0
- package/examples/dashboard/src/DevicesPanel.tsx +11 -0
- package/examples/dashboard/src/SettingsScreen.tsx +9 -4
- package/examples/dashboard/src/SystemTypeCog/CogStateInfo.ts +10 -2
- package/examples/dashboard/src/SystemTypeCog/SystemTypeCog.ts +37 -3
- package/examples/dashboard/src/SystemTypeGeneric/StateInfoGeneric.ts +10 -2
- package/examples/dashboard/src/SystemTypeGeneric/SystemTypeGeneric.ts +38 -4
- package/examples/dashboard/src/SystemTypeMarty/RICStateInfo.ts +10 -2
- package/examples/dashboard/src/styles.css +162 -0
- package/package.json +52 -49
- package/src/RaftAttributeHandler.ts +25 -22
- package/src/RaftChannelSimulated.ts +4 -3
- package/src/RaftConnector.ts +34 -13
- package/src/RaftDeviceManager.ts +253 -83
- package/src/RaftDeviceMgrIF.ts +5 -1
- package/src/RaftDeviceStates.ts +42 -8
- package/src/RaftPublish.ts +92 -0
- package/src/RaftStreamHandler.ts +84 -1
- package/src/RaftStruct.test.ts +229 -0
- package/src/RaftStruct.ts +101 -37
- package/src/RaftSystemUtils.ts +59 -0
- package/src/RaftTypes.ts +27 -0
- package/src/main.ts +1 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RaftSubscribeForUpdatesCBType, RaftSystemType } from "../../../../src/RaftSystemType";
|
|
2
|
-
import { RaftEventFn, RaftLog,
|
|
2
|
+
import { inspectPublishFrame, RaftEventFn, RaftLog, RaftPublishEvent, RaftPublishEventNames, RaftSubscriptionUpdateResponse, RaftSystemUtils } from "../../../../src/main";
|
|
3
3
|
import { CogStateInfo } from "./CogStateInfo";
|
|
4
4
|
import { DeviceManager } from "../../../../src/RaftDeviceManager";
|
|
5
5
|
|
|
@@ -53,10 +53,16 @@ export default class SystemTypeCog implements RaftSystemType {
|
|
|
53
53
|
']}';
|
|
54
54
|
|
|
55
55
|
const msgHandler = systemUtils.getMsgHandler();
|
|
56
|
-
const ricResp = await msgHandler.sendRICRESTCmdFrame<
|
|
56
|
+
const ricResp = await msgHandler.sendRICRESTCmdFrame<RaftSubscriptionUpdateResponse>(
|
|
57
57
|
enable ? subscribeEnable : subscribeDisable
|
|
58
58
|
);
|
|
59
59
|
|
|
60
|
+
// Cache topic index->name map from response, then refresh from pubtopics endpoint when enabling
|
|
61
|
+
systemUtils.updatePublishTopicMapFromSubscriptionResponse(ricResp);
|
|
62
|
+
if (enable) {
|
|
63
|
+
await systemUtils.refreshPublishTopicMap();
|
|
64
|
+
}
|
|
65
|
+
|
|
60
66
|
// Debug
|
|
61
67
|
RaftLog.debug(`subscribe enable/disable returned ${JSON.stringify(ricResp)}`);
|
|
62
68
|
} catch (error: unknown) {
|
|
@@ -72,13 +78,41 @@ export default class SystemTypeCog implements RaftSystemType {
|
|
|
72
78
|
|
|
73
79
|
// RICLog.debug(`rxOtherMsgType payload ${RaftUtils.bufferToHex(payload)}`);
|
|
74
80
|
RaftLog.verbose(`rxOtherMsgType payloadLen ${payload.length}`);
|
|
75
|
-
|
|
81
|
+
|
|
82
|
+
const frameMeta = inspectPublishFrame(payload, (idx) => this._systemUtils?.getPublishTopicName(idx));
|
|
83
|
+
let handledByDeviceManager = false;
|
|
84
|
+
|
|
85
|
+
if (frameMeta.frameType === "binary") {
|
|
86
|
+
if (frameMeta.binaryHasEnvelope) {
|
|
87
|
+
if (frameMeta.topicName === "devbin") {
|
|
88
|
+
this._stateInfo.handleBinaryPayload(payload);
|
|
89
|
+
handledByDeviceManager = true;
|
|
90
|
+
}
|
|
91
|
+
} else if (SUBSCRIBE_BINARY_MSGS) {
|
|
92
|
+
this._stateInfo.handleBinaryPayload(payload);
|
|
93
|
+
handledByDeviceManager = true;
|
|
94
|
+
}
|
|
95
|
+
} else if (frameMeta.frameType === "json") {
|
|
96
|
+
if (frameMeta.topicName === "devjson" || frameMeta.topicName === undefined) {
|
|
97
|
+
if (frameMeta.jsonString !== undefined) {
|
|
98
|
+
this._stateInfo.handleJsonPayload(frameMeta.jsonString);
|
|
99
|
+
handledByDeviceManager = true;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const topicIDs = frameMeta.topicIndex !== undefined ? [frameMeta.topicIndex.toString()] : [];
|
|
76
105
|
|
|
77
106
|
// Call event handler if registered
|
|
78
107
|
if (this._onEvent) {
|
|
79
108
|
this._onEvent("pub", RaftPublishEvent.PUBLISH_EVENT_DATA, RaftPublishEventNames[RaftPublishEvent.PUBLISH_EVENT_DATA],
|
|
80
109
|
{
|
|
81
110
|
topicIDs: topicIDs,
|
|
111
|
+
topicName: frameMeta.topicName,
|
|
112
|
+
topicIndex: frameMeta.topicIndex,
|
|
113
|
+
topicVersion: frameMeta.version,
|
|
114
|
+
frameType: frameMeta.frameType,
|
|
115
|
+
handledByDeviceManager,
|
|
82
116
|
payload: payload,
|
|
83
117
|
frameTimeMs: frameTimeMs,
|
|
84
118
|
isBinary: SUBSCRIBE_BINARY_MSGS
|
|
@@ -7,6 +7,14 @@ export class StateInfoGeneric {
|
|
|
7
7
|
public constructor(private _deviceManager: DeviceManager) {
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
async handleBinaryPayload(rxMsg: Uint8Array): Promise<void> {
|
|
11
|
+
await this._deviceManager.handleClientMsgBinary(rxMsg);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async handleJsonPayload(jsonString: string): Promise<void> {
|
|
15
|
+
await this._deviceManager.handleClientMsgJson(jsonString);
|
|
16
|
+
}
|
|
17
|
+
|
|
10
18
|
updateFromMsg(rxMsg: Uint8Array, frameTimeMs: number, isBinary: boolean): Array<string> {
|
|
11
19
|
|
|
12
20
|
// Debug
|
|
@@ -15,14 +23,14 @@ export class StateInfoGeneric {
|
|
|
15
23
|
// Handle binary or JSON
|
|
16
24
|
if (isBinary) {
|
|
17
25
|
// Handle using device manager
|
|
18
|
-
this.
|
|
26
|
+
this.handleBinaryPayload(rxMsg);
|
|
19
27
|
} else {
|
|
20
28
|
// Convert Uint8Array to string
|
|
21
29
|
const decoder = new TextDecoder('utf-8');
|
|
22
30
|
const jsonString = decoder.decode(rxMsg.slice(2));
|
|
23
31
|
|
|
24
32
|
// Handle using device manager
|
|
25
|
-
this.
|
|
33
|
+
this.handleJsonPayload(jsonString);
|
|
26
34
|
}
|
|
27
35
|
return [];
|
|
28
36
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { RaftSubscribeForUpdatesCBType, RaftSystemType } from "../../../../src/RaftSystemType";
|
|
2
|
-
import { RaftEventFn, RaftLog,
|
|
2
|
+
import { inspectPublishFrame, RaftEventFn, RaftLog, RaftPublishEvent, RaftPublishEventNames, RaftSubscriptionUpdateResponse, RaftSystemUtils } from "../../../../src/main";
|
|
3
3
|
import { StateInfoGeneric } from "./StateInfoGeneric";
|
|
4
4
|
import { DeviceManager } from "../../../../src/RaftDeviceManager";
|
|
5
5
|
|
|
6
|
-
const SUBSCRIBE_BINARY_MSGS =
|
|
6
|
+
const SUBSCRIBE_BINARY_MSGS = true;
|
|
7
7
|
|
|
8
8
|
export default class SystemTypeGeneric implements RaftSystemType {
|
|
9
9
|
nameForDialogs = "Generic System";
|
|
@@ -53,10 +53,16 @@ export default class SystemTypeGeneric implements RaftSystemType {
|
|
|
53
53
|
']}';
|
|
54
54
|
|
|
55
55
|
const msgHandler = systemUtils.getMsgHandler();
|
|
56
|
-
const ricResp = await msgHandler.sendRICRESTCmdFrame<
|
|
56
|
+
const ricResp = await msgHandler.sendRICRESTCmdFrame<RaftSubscriptionUpdateResponse>(
|
|
57
57
|
enable ? subscribeEnable : subscribeDisable
|
|
58
58
|
);
|
|
59
59
|
|
|
60
|
+
// Cache topic index->name map from response, then refresh from pubtopics endpoint when enabling
|
|
61
|
+
systemUtils.updatePublishTopicMapFromSubscriptionResponse(ricResp);
|
|
62
|
+
if (enable) {
|
|
63
|
+
await systemUtils.refreshPublishTopicMap();
|
|
64
|
+
}
|
|
65
|
+
|
|
60
66
|
// Debug
|
|
61
67
|
RaftLog.debug(`subscribe enable/disable returned ${JSON.stringify(ricResp)}`);
|
|
62
68
|
} catch (error: unknown) {
|
|
@@ -72,13 +78,41 @@ export default class SystemTypeGeneric implements RaftSystemType {
|
|
|
72
78
|
|
|
73
79
|
// RICLog.debug(`rxOtherMsgType payload ${RaftUtils.bufferToHex(payload)}`);
|
|
74
80
|
RaftLog.verbose(`rxOtherMsgType payloadLen ${payload.length}`);
|
|
75
|
-
|
|
81
|
+
|
|
82
|
+
const frameMeta = inspectPublishFrame(payload, (idx) => this._systemUtils?.getPublishTopicName(idx));
|
|
83
|
+
let handledByDeviceManager = false;
|
|
84
|
+
|
|
85
|
+
if (frameMeta.frameType === "binary") {
|
|
86
|
+
if (frameMeta.binaryHasEnvelope) {
|
|
87
|
+
if (frameMeta.topicName === "devbin") {
|
|
88
|
+
this._stateInfo.handleBinaryPayload(payload);
|
|
89
|
+
handledByDeviceManager = true;
|
|
90
|
+
}
|
|
91
|
+
} else if (SUBSCRIBE_BINARY_MSGS) {
|
|
92
|
+
this._stateInfo.handleBinaryPayload(payload);
|
|
93
|
+
handledByDeviceManager = true;
|
|
94
|
+
}
|
|
95
|
+
} else if (frameMeta.frameType === "json") {
|
|
96
|
+
if (frameMeta.topicName === "devjson" || frameMeta.topicName === undefined) {
|
|
97
|
+
if (frameMeta.jsonString !== undefined) {
|
|
98
|
+
this._stateInfo.handleJsonPayload(frameMeta.jsonString);
|
|
99
|
+
handledByDeviceManager = true;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const topicIDs = frameMeta.topicIndex !== undefined ? [frameMeta.topicIndex.toString()] : [];
|
|
76
105
|
|
|
77
106
|
// Call event handler if registered
|
|
78
107
|
if (this._onEvent) {
|
|
79
108
|
this._onEvent("pub", RaftPublishEvent.PUBLISH_EVENT_DATA, RaftPublishEventNames[RaftPublishEvent.PUBLISH_EVENT_DATA],
|
|
80
109
|
{
|
|
81
110
|
topicIDs: topicIDs,
|
|
111
|
+
topicName: frameMeta.topicName,
|
|
112
|
+
topicIndex: frameMeta.topicIndex,
|
|
113
|
+
topicVersion: frameMeta.version,
|
|
114
|
+
frameType: frameMeta.frameType,
|
|
115
|
+
handledByDeviceManager,
|
|
82
116
|
payload: payload,
|
|
83
117
|
frameTimeMs: frameTimeMs,
|
|
84
118
|
isBinary: SUBSCRIBE_BINARY_MSGS
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import RaftDeviceMgrIF from "../../../../src/RaftDeviceMgrIF";
|
|
2
|
-
import { DeviceAttributeState, DevicesState, DeviceState } from
|
|
2
|
+
import { DeviceAttributeState, DevicesState, DeviceState, DeviceOnlineState } from '../../../../src/RaftDeviceStates';
|
|
3
3
|
import { RICSERIAL_PAYLOAD_POS } from "../../../../src/RaftProtocolDefs";
|
|
4
4
|
import RICAddOnManager from "./RICAddOnManager";
|
|
5
5
|
import RICCommsStats from "./RICCommsStats";
|
|
@@ -50,7 +50,7 @@ export class RICStateInfo implements RaftDeviceMgrIF {
|
|
|
50
50
|
deviceAttributes: {},
|
|
51
51
|
deviceIsNew: false,
|
|
52
52
|
stateChanged: false,
|
|
53
|
-
|
|
53
|
+
onlineState: DeviceOnlineState.Offline,
|
|
54
54
|
deviceAddress: "",
|
|
55
55
|
deviceType: "",
|
|
56
56
|
busName: ""
|
|
@@ -85,6 +85,14 @@ export class RICStateInfo implements RaftDeviceMgrIF {
|
|
|
85
85
|
// TODO - implement if RICStateInfo is to be used as a DeviceMgr
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
addDeviceRemovedCallback(callback: (deviceKey: string, state: DeviceState) => void): void {
|
|
89
|
+
// TODO - implement if RICStateInfo is to be used as a DeviceMgr
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
removeDeviceRemovedCallback(callback: (deviceKey: string, state: DeviceState) => void): void {
|
|
93
|
+
// TODO - implement if RICStateInfo is to be used as a DeviceMgr
|
|
94
|
+
}
|
|
95
|
+
|
|
88
96
|
sendAction(deviceKey: string, action: any, data: any): void {
|
|
89
97
|
// TODO - implement if RICStateInfo is to be used as a DeviceMgr
|
|
90
98
|
}
|
|
@@ -381,6 +381,168 @@ h1 {
|
|
|
381
381
|
background-color: #555;
|
|
382
382
|
}
|
|
383
383
|
|
|
384
|
+
.menu-item-toggle {
|
|
385
|
+
padding: 6px 10px;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.menu-toggle {
|
|
389
|
+
display: flex;
|
|
390
|
+
align-items: center;
|
|
391
|
+
gap: 8px;
|
|
392
|
+
cursor: pointer;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.menu-toggle input {
|
|
396
|
+
margin: 0;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.device-stats-panel {
|
|
400
|
+
flex: 0 0 220px;
|
|
401
|
+
min-width: 220px;
|
|
402
|
+
padding: 10px;
|
|
403
|
+
border: 1px solid #666;
|
|
404
|
+
border-radius: 4px;
|
|
405
|
+
background: #444;
|
|
406
|
+
display: flex;
|
|
407
|
+
flex-direction: column;
|
|
408
|
+
gap: 10px;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.device-stats-header {
|
|
412
|
+
display: flex;
|
|
413
|
+
justify-content: space-between;
|
|
414
|
+
align-items: center;
|
|
415
|
+
font-weight: bold;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.device-stats-reset {
|
|
419
|
+
background: #555;
|
|
420
|
+
color: #fff;
|
|
421
|
+
border: none;
|
|
422
|
+
border-radius: 4px;
|
|
423
|
+
padding: 4px 8px;
|
|
424
|
+
cursor: pointer;
|
|
425
|
+
font-size: 0.8em;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.device-stats-reset:hover {
|
|
429
|
+
background: #666;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.device-stats-grid {
|
|
433
|
+
display: grid;
|
|
434
|
+
grid-template-columns: 1fr;
|
|
435
|
+
gap: 8px;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.device-stats-item {
|
|
439
|
+
display: flex;
|
|
440
|
+
justify-content: space-between;
|
|
441
|
+
gap: 10px;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.device-stats-label {
|
|
445
|
+
color: #bbb;
|
|
446
|
+
font-size: 0.85em;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.device-stats-value {
|
|
450
|
+
font-weight: 600;
|
|
451
|
+
font-size: 0.9em;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.poll-rate-dialog-overlay {
|
|
455
|
+
position: fixed;
|
|
456
|
+
inset: 0;
|
|
457
|
+
background: rgba(0, 0, 0, 0.5);
|
|
458
|
+
z-index: 100;
|
|
459
|
+
display: flex;
|
|
460
|
+
align-items: center;
|
|
461
|
+
justify-content: center;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.poll-rate-dialog {
|
|
465
|
+
background: #333;
|
|
466
|
+
border: 1px solid #666;
|
|
467
|
+
border-radius: 6px;
|
|
468
|
+
padding: 18px 22px;
|
|
469
|
+
min-width: 240px;
|
|
470
|
+
display: flex;
|
|
471
|
+
flex-direction: column;
|
|
472
|
+
gap: 10px;
|
|
473
|
+
color: #fff;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.poll-rate-dialog-title {
|
|
477
|
+
font-size: 1em;
|
|
478
|
+
font-weight: bold;
|
|
479
|
+
margin-bottom: 4px;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.poll-rate-dialog-row {
|
|
483
|
+
display: flex;
|
|
484
|
+
align-items: center;
|
|
485
|
+
gap: 8px;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.poll-rate-input {
|
|
489
|
+
flex: 1;
|
|
490
|
+
background: #222;
|
|
491
|
+
border: 1px solid #666;
|
|
492
|
+
border-radius: 4px;
|
|
493
|
+
color: #fff;
|
|
494
|
+
padding: 6px 8px;
|
|
495
|
+
font-size: 1em;
|
|
496
|
+
width: 100%;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.poll-rate-unit {
|
|
500
|
+
color: #aaa;
|
|
501
|
+
font-size: 0.9em;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.poll-rate-preview {
|
|
505
|
+
font-size: 0.8em;
|
|
506
|
+
color: #aaa;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.poll-rate-status {
|
|
510
|
+
font-size: 0.85em;
|
|
511
|
+
color: #f90;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.poll-rate-dialog-buttons {
|
|
515
|
+
display: flex;
|
|
516
|
+
gap: 8px;
|
|
517
|
+
justify-content: flex-end;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
.poll-rate-btn {
|
|
521
|
+
padding: 6px 14px;
|
|
522
|
+
border: none;
|
|
523
|
+
border-radius: 4px;
|
|
524
|
+
cursor: pointer;
|
|
525
|
+
font-size: 0.9em;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
.poll-rate-btn-set {
|
|
529
|
+
background: #4a7;
|
|
530
|
+
color: #fff;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.poll-rate-btn-set:hover {
|
|
534
|
+
background: #5b8;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.poll-rate-btn-cancel {
|
|
538
|
+
background: #555;
|
|
539
|
+
color: #fff;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.poll-rate-btn-cancel:hover {
|
|
543
|
+
background: #666;
|
|
544
|
+
}
|
|
545
|
+
|
|
384
546
|
/* Adjust height for portrait orientation */
|
|
385
547
|
@media (orientation: portrait) {
|
|
386
548
|
.device-line-chart {
|
package/package.json
CHANGED
|
@@ -1,51 +1,54 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
2
|
+
"name": "@robdobsn/raftjs",
|
|
3
|
+
"version": "1.11.6",
|
|
4
|
+
"description": "Javascript/TS library for Raft library",
|
|
5
|
+
"main": "dist/web/main.js",
|
|
6
|
+
"types": "dist/web/main.d.ts",
|
|
7
|
+
"react-native": "dist/react-native/main.js",
|
|
8
|
+
"author": "Rob Dobson <rob@dobson.com>",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/robdobsn/raftjs.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/robdobsn/raftjs/issues"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"keywords": [
|
|
18
|
+
"Raft"
|
|
19
|
+
],
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"registry": "https://registry.npmjs.org/",
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc -p tsconfig.json",
|
|
26
|
+
"build:phone": "tsc -p tsconfig.react-native.json",
|
|
27
|
+
"build-all": "npm run clean && npm run build && npm run build:phone",
|
|
28
|
+
"lint": "eslint ./src",
|
|
29
|
+
"clean": "rimraf dist build package",
|
|
30
|
+
"docs": "typedoc --entryPoints src/main.ts",
|
|
31
|
+
"watch-all": "tsc -p tsconfig.json --watch & tsc -p tsconfig.react-native.json --watch"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/jest": "^30.0.0",
|
|
35
|
+
"@types/node": "^22.13.11",
|
|
36
|
+
"@types/text-encoding": "^0.0.40",
|
|
37
|
+
"@types/web-bluetooth": "^0.0.21",
|
|
38
|
+
"@typescript-eslint/eslint-plugin": "^8.27.0",
|
|
39
|
+
"eslint": "^9.23.0",
|
|
40
|
+
"jest": "^30.3.0",
|
|
41
|
+
"react-native-ble-plx": "^3.5.0",
|
|
42
|
+
"rimraf": "^6.0.1",
|
|
43
|
+
"ts-jest": "^29.4.6",
|
|
44
|
+
"typescript": "^5.8.2"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"isomorphic-ws": "^5.0.0",
|
|
48
|
+
"tslib": "^2.8.1"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"react-native": "*",
|
|
52
|
+
"react-native-ble-plx": "*"
|
|
53
|
+
}
|
|
51
54
|
}
|
|
@@ -37,7 +37,7 @@ export default class AttributeHandler {
|
|
|
37
37
|
const msgDataStartIdx = msgBufIdx;
|
|
38
38
|
|
|
39
39
|
// New attribute values (in order as they appear in the attributes JSON)
|
|
40
|
-
let newAttrValues: number[][] = [];
|
|
40
|
+
let newAttrValues: (number | string)[][] = [];
|
|
41
41
|
if ("c" in pollRespMetadata) {
|
|
42
42
|
|
|
43
43
|
// Extract attribute values using custom handler
|
|
@@ -201,7 +201,7 @@ export default class AttributeHandler {
|
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
private processMsgAttribute(attrDef: DeviceTypeAttribute, msgBuffer: Uint8Array, msgBufIdx: number, msgDataStartIdx: number): { values: number[], newMsgBufIdx: number} {
|
|
204
|
+
private processMsgAttribute(attrDef: DeviceTypeAttribute, msgBuffer: Uint8Array, msgBufIdx: number, msgDataStartIdx: number): { values: (number | string)[], newMsgBufIdx: number} {
|
|
205
205
|
|
|
206
206
|
// Current field message string index
|
|
207
207
|
let curFieldBufIdx = msgBufIdx;
|
|
@@ -253,11 +253,14 @@ export default class AttributeHandler {
|
|
|
253
253
|
|
|
254
254
|
// Extract the value using python-struct
|
|
255
255
|
const unpackValues = structUnpack(maskOnSignedValue ? attrTypesOnly.toUpperCase() : attrTypesOnly, attrBuf);
|
|
256
|
-
let attrValues = unpackValues as number[];
|
|
256
|
+
let attrValues = unpackValues as (number | string)[];
|
|
257
257
|
|
|
258
258
|
// Get number of bytes consumed
|
|
259
259
|
const numBytesConsumed = structSizeOf(attrTypesOnly);
|
|
260
260
|
|
|
261
|
+
// Check if any values are strings (from 's' format) — skip numeric transforms for those
|
|
262
|
+
const hasStringValues = attrValues.some(v => typeof v === 'string');
|
|
263
|
+
|
|
261
264
|
// // Check if sign extendable mask specified on signed value
|
|
262
265
|
// if (mmSpecifiedOnSignedValue) {
|
|
263
266
|
// const signBitMask = 1 << (signExtendableMaskSignPos - 1);
|
|
@@ -270,57 +273,57 @@ export default class AttributeHandler {
|
|
|
270
273
|
// }
|
|
271
274
|
|
|
272
275
|
// Check for XOR mask
|
|
273
|
-
if ("x" in attrDef) {
|
|
276
|
+
if (!hasStringValues && "x" in attrDef) {
|
|
274
277
|
const mask = typeof attrDef.x === "string" ? parseInt(attrDef.x, 16) : attrDef.x as number;
|
|
275
|
-
attrValues = attrValues.map((value) => (value >>> 0) ^ mask);
|
|
278
|
+
attrValues = attrValues.map((value) => ((value as number) >>> 0) ^ mask);
|
|
276
279
|
}
|
|
277
280
|
|
|
278
281
|
// Check for AND mask
|
|
279
|
-
if ("m" in attrDef) {
|
|
282
|
+
if (!hasStringValues && "m" in attrDef) {
|
|
280
283
|
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));
|
|
284
|
+
attrValues = attrValues.map((value) => (maskOnSignedValue ? this.signExtend(value as number, mask) : ((value as number) >>> 0) & mask));
|
|
282
285
|
}
|
|
283
286
|
|
|
284
287
|
// Check for a sign-bit
|
|
285
|
-
if ("sb" in attrDef) {
|
|
288
|
+
if (!hasStringValues && "sb" in attrDef) {
|
|
286
289
|
const signBitPos = attrDef.sb as number;
|
|
287
290
|
const signBitMask = 1 << signBitPos;
|
|
288
291
|
if ("ss" in attrDef) {
|
|
289
292
|
const signBitSubtract = attrDef.ss as number;
|
|
290
|
-
attrValues = attrValues.map((value) => (value & signBitMask) ? signBitSubtract - value : value);
|
|
293
|
+
attrValues = attrValues.map((value) => ((value as number) & signBitMask) ? signBitSubtract - (value as number) : value);
|
|
291
294
|
} else {
|
|
292
|
-
attrValues = attrValues.map((value) => (value & signBitMask) ? value - (signBitMask << 1) : value);
|
|
295
|
+
attrValues = attrValues.map((value) => ((value as number) & signBitMask) ? (value as number) - (signBitMask << 1) : value);
|
|
293
296
|
}
|
|
294
297
|
}
|
|
295
298
|
|
|
296
299
|
// Check for bit shift required
|
|
297
|
-
if ("s" in attrDef && attrDef.s) {
|
|
300
|
+
if (!hasStringValues && "s" in attrDef && attrDef.s) {
|
|
298
301
|
const bitshift = attrDef.s as number;
|
|
299
302
|
if (bitshift > 0) {
|
|
300
|
-
attrValues = attrValues.map((value) => (value >>> 0) >>> bitshift);
|
|
303
|
+
attrValues = attrValues.map((value) => ((value as number) >>> 0) >>> bitshift);
|
|
301
304
|
} else if (bitshift < 0) {
|
|
302
|
-
attrValues = attrValues.map((value) => (value >>> 0) << -bitshift);
|
|
305
|
+
attrValues = attrValues.map((value) => ((value as number) >>> 0) << -bitshift);
|
|
303
306
|
}
|
|
304
307
|
}
|
|
305
308
|
|
|
306
309
|
// Check for divisor
|
|
307
|
-
if ("d" in attrDef && attrDef.d) {
|
|
310
|
+
if (!hasStringValues && "d" in attrDef && attrDef.d) {
|
|
308
311
|
const divisor = attrDef.d as number;
|
|
309
|
-
attrValues = attrValues.map((value) => (value) / divisor);
|
|
312
|
+
attrValues = attrValues.map((value) => (value as number) / divisor);
|
|
310
313
|
}
|
|
311
314
|
|
|
312
315
|
// Check for value to add
|
|
313
|
-
if ("a" in attrDef && attrDef.a !== undefined) {
|
|
316
|
+
if (!hasStringValues && "a" in attrDef && attrDef.a !== undefined) {
|
|
314
317
|
const addValue = attrDef.a as number;
|
|
315
|
-
attrValues = attrValues.map((value) => (value) + addValue);
|
|
318
|
+
attrValues = attrValues.map((value) => (value as number) + addValue);
|
|
316
319
|
}
|
|
317
320
|
|
|
318
321
|
// Apply lookup table if defined
|
|
319
|
-
if ("lut" in attrDef && attrDef.lut !== undefined) {
|
|
322
|
+
if (!hasStringValues && "lut" in attrDef && attrDef.lut !== undefined) {
|
|
320
323
|
attrValues = attrValues.map((value): number => {
|
|
321
324
|
// Skip NaN values
|
|
322
|
-
if (isNaN(value)) {
|
|
323
|
-
return value;
|
|
325
|
+
if (isNaN(value as number)) {
|
|
326
|
+
return value as number;
|
|
324
327
|
}
|
|
325
328
|
|
|
326
329
|
// Search through the lookup table rows for a match
|
|
@@ -334,7 +337,7 @@ export default class AttributeHandler {
|
|
|
334
337
|
}
|
|
335
338
|
|
|
336
339
|
// Parse the range string
|
|
337
|
-
if (this.isValueInRangeString(value, row.r)) {
|
|
340
|
+
if (this.isValueInRangeString(value as number, row.r)) {
|
|
338
341
|
return row.v;
|
|
339
342
|
}
|
|
340
343
|
}
|
|
@@ -345,7 +348,7 @@ export default class AttributeHandler {
|
|
|
345
348
|
}
|
|
346
349
|
|
|
347
350
|
// Otherwise keep the original value
|
|
348
|
-
return value;
|
|
351
|
+
return value as number;
|
|
349
352
|
});
|
|
350
353
|
}
|
|
351
354
|
|
|
@@ -357,9 +357,9 @@ export default class RaftChannelSimulated implements RaftChannel {
|
|
|
357
357
|
// Convert the buffer to a byte array
|
|
358
358
|
const dataBytes = new Uint8Array(dataBuffer);
|
|
359
359
|
|
|
360
|
-
// Create the JSON message structure
|
|
360
|
+
// Create the JSON message structure (bus key is numeric string to match firmware convention)
|
|
361
361
|
const message = {
|
|
362
|
-
"
|
|
362
|
+
"1": {
|
|
363
363
|
[deviceName]: {
|
|
364
364
|
"_t": deviceTypeInfo.type,
|
|
365
365
|
"_o": 1, // Device is online
|
|
@@ -464,7 +464,8 @@ export default class RaftChannelSimulated implements RaftChannel {
|
|
|
464
464
|
const hotspotRow = (Math.sin(hotspotPhase) + 1) * (rows - 1) / 2;
|
|
465
465
|
const hotspotCol = (Math.cos(hotspotPhase) + 1) * (cols - 1) / 2;
|
|
466
466
|
const hotspotAmplitude = 6;
|
|
467
|
-
const
|
|
467
|
+
const rawSigma = Math.max(rows, cols) / 3;
|
|
468
|
+
const sigma = Number.isFinite(rawSigma) ? rawSigma : 1;
|
|
468
469
|
|
|
469
470
|
for (let idx = 0; idx < repeatCount; idx++) {
|
|
470
471
|
const row = Math.floor(idx / cols);
|