@robotical/raftjs 2.0.10 → 2.1.0

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.
@@ -7,7 +7,7 @@
7
7
  //
8
8
  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
9
9
 
10
- import { DeviceAttributeState, DevicesState, DeviceState, getDeviceKey } from "./RaftDeviceStates";
10
+ import { DeviceAttributeState, DeviceAttributesState, DevicesState, DeviceState, getDeviceKey } from "./RaftDeviceStates";
11
11
  import { DeviceMsgJson } from "./RaftDeviceMsg";
12
12
  import { RaftOKFail } from './RaftTypes';
13
13
  import { DeviceTypeInfo, DeviceTypeAction, DeviceTypeInfoRecs, RaftDevTypeInfoResponse } from "./RaftDeviceInfo";
@@ -17,6 +17,18 @@ import RaftDeviceMgrIF from "./RaftDeviceMgrIF";
17
17
  import { structPack } from "./RaftStruct";
18
18
  // import RaftUtils from "./RaftUtils";
19
19
 
20
+ export interface DeviceDecodedData {
21
+ deviceKey: string;
22
+ busName: string;
23
+ deviceAddress: string;
24
+ deviceType: string;
25
+ attrGroupName?: string;
26
+ attrValues: Record<string, number[]>;
27
+ timestampsUs: number[];
28
+ markers?: Record<string, unknown>;
29
+ fromOfflineBuffer?: boolean;
30
+ }
31
+
20
32
  export class DeviceManager implements RaftDeviceMgrIF{
21
33
 
22
34
  // Max data points to store
@@ -45,6 +57,7 @@ export class DeviceManager implements RaftDeviceMgrIF{
45
57
  private _newDeviceCallbacks: Array<(deviceKey: string, state: DeviceState) => void> = [];
46
58
  private _newDeviceAttributeCallbacks: Array<(deviceKey: string, attrState: DeviceAttributeState) => void> = [];
47
59
  private _newAttributeDataCallbacks: Array<(deviceKey: string, attrState: DeviceAttributeState) => void> = [];
60
+ private _decodedDataCallbacks: Array<(decoded: DeviceDecodedData) => void> = [];
48
61
 
49
62
  // Debug message index (to help debug with async messages)
50
63
  private _debugMsgIndex = 0;
@@ -146,6 +159,16 @@ export class DeviceManager implements RaftDeviceMgrIF{
146
159
  this._newAttributeDataCallbacks = this._newAttributeDataCallbacks.filter((cb) => cb !== callback);
147
160
  }
148
161
 
162
+ public addDecodedDataCallback(callback: (decoded: DeviceDecodedData) => void): void {
163
+ if (!this._decodedDataCallbacks.includes(callback)) {
164
+ this._decodedDataCallbacks.push(callback);
165
+ }
166
+ }
167
+
168
+ public removeDecodedDataCallback(callback: (decoded: DeviceDecodedData) => void): void {
169
+ this._decodedDataCallbacks = this._decodedDataCallbacks.filter((cb) => cb !== callback);
170
+ }
171
+
149
172
  ////////////////////////////////////////////////////////////////////////////
150
173
  // Set the friendly name for the device
151
174
  ////////////////////////////////////////////////////////////////////////////
@@ -293,6 +316,8 @@ export class DeviceManager implements RaftDeviceMgrIF{
293
316
  // Iterate over attribute groups
294
317
  const attrGroupDataLen = sectionLen - sectionHeaderLen;
295
318
  const attrGroupStartPos = attrGroupPos;
319
+ const attrLengthsBefore = this.snapshotAttrLengths(deviceState.deviceAttributes, pollRespMetadata);
320
+ const timelineLenBefore = deviceState.deviceTimeline.timestampsUs.length;
296
321
  while (attrGroupPos < attrGroupStartPos + attrGroupDataLen) {
297
322
 
298
323
  // Add bounds checking
@@ -328,6 +353,10 @@ export class DeviceManager implements RaftDeviceMgrIF{
328
353
 
329
354
  // console.log(`DevMan.handleClientMsgBinary group done debugIdx ${debugMsgIndex} attrGroupPos ${attrGroupPos} sectionLen ${sectionLen} msgPos ${msgPos} rxMsgLen ${rxMsg.length} remainingLen ${remainingLen}`);
330
355
  }
356
+
357
+ // Inform decoded-data callbacks
358
+ this.emitDecodedData(deviceKey, busNum.toString(), devAddr.toString(), deviceState,
359
+ pollRespMetadata, attrLengthsBefore, timelineLenBefore);
331
360
  } else {
332
361
  console.warn(`DevMan.handleClientMsgBinary debugIdx ${debugMsgIndex} deviceState incomplete for device ${deviceKey}, skipping attribute processing`);
333
362
  }
@@ -441,6 +470,8 @@ export class DeviceManager implements RaftDeviceMgrIF{
441
470
  return;
442
471
  }
443
472
 
473
+ const markers = this.extractMarkers(attrGroups);
474
+
444
475
  // Iterate attribute groups
445
476
  Object.entries(attrGroups).forEach(([attrGroupName, msgHexStr]) => {
446
477
 
@@ -463,6 +494,9 @@ export class DeviceManager implements RaftDeviceMgrIF{
463
494
  // Iterate over attributes in the group
464
495
  const pollRespMetadata = deviceState.deviceTypeInfo!.resp!;
465
496
 
497
+ const attrLengthsBefore = this.snapshotAttrLengths(deviceState.deviceAttributes, pollRespMetadata);
498
+ const timelineLenBefore = deviceState.deviceTimeline.timestampsUs.length;
499
+
466
500
  // Loop
467
501
  while (msgBufIdx < msgBytes.length) {
468
502
 
@@ -475,6 +509,9 @@ export class DeviceManager implements RaftDeviceMgrIF{
475
509
  msgBufIdx = newMsgBufIdx;
476
510
  deviceState.stateChanged = true;
477
511
  }
512
+
513
+ this.emitDecodedData(deviceKey, busName, devAddr, deviceState, pollRespMetadata,
514
+ attrLengthsBefore, timelineLenBefore, attrGroupName, markers);
478
515
  });
479
516
  });
480
517
  });
@@ -712,4 +749,91 @@ export class DeviceManager implements RaftDeviceMgrIF{
712
749
  }
713
750
  return bytes;
714
751
  }
752
+
753
+ ////////////////////////////////////////////////////////////////////////////
754
+ // Helpers for decoded data callbacks
755
+ ////////////////////////////////////////////////////////////////////////////
756
+
757
+ private snapshotAttrLengths(deviceAttrs: DeviceAttributesState, pollRespMetadata: DeviceTypeInfo["resp"]): Record<string, number> {
758
+ const lengths: Record<string, number> = {};
759
+ if (!pollRespMetadata) {
760
+ return lengths;
761
+ }
762
+ pollRespMetadata.a.forEach((attr) => {
763
+ lengths[attr.n] = deviceAttrs[attr.n]?.values.length || 0;
764
+ });
765
+ return lengths;
766
+ }
767
+
768
+ private emitDecodedData(
769
+ deviceKey: string,
770
+ busName: string,
771
+ devAddr: string,
772
+ deviceState: DeviceState,
773
+ pollRespMetadata: DeviceTypeInfo["resp"],
774
+ attrLengthsBefore: Record<string, number>,
775
+ timelineLenBefore: number,
776
+ attrGroupName = "",
777
+ markers?: Record<string, unknown>,
778
+ ): void {
779
+
780
+ if (!pollRespMetadata) {
781
+ return;
782
+ }
783
+
784
+ const attrValues: Record<string, number[]> = {};
785
+ let hasValues = false;
786
+
787
+ pollRespMetadata.a.forEach((attr) => {
788
+ const attrState = deviceState.deviceAttributes[attr.n];
789
+ if (!attrState) {
790
+ return;
791
+ }
792
+ const prevLen = attrLengthsBefore[attr.n] || 0;
793
+ if (attrState.values.length > prevLen) {
794
+ attrValues[attr.n] = attrState.values.slice(prevLen);
795
+ hasValues = hasValues || attrValues[attr.n].length > 0;
796
+ }
797
+ });
798
+
799
+ if (!hasValues) {
800
+ return;
801
+ }
802
+
803
+ const timestampsUs = deviceState.deviceTimeline.timestampsUs.slice(timelineLenBefore);
804
+
805
+ const decoded: DeviceDecodedData = {
806
+ deviceKey,
807
+ busName,
808
+ deviceAddress: devAddr,
809
+ deviceType: deviceState.deviceType,
810
+ attrGroupName: attrGroupName || undefined,
811
+ attrValues,
812
+ timestampsUs,
813
+ };
814
+
815
+ if (markers && Object.keys(markers).length > 0) {
816
+ decoded.markers = markers;
817
+ decoded.fromOfflineBuffer = this.isTruthy(markers["_buf"]);
818
+ }
819
+
820
+ this._decodedDataCallbacks.forEach((cb) => cb(decoded));
821
+ }
822
+
823
+ private extractMarkers(attrGroups: any): Record<string, unknown> {
824
+ const markers: Record<string, unknown> = {};
825
+ if (!attrGroups || typeof attrGroups !== "object") {
826
+ return markers;
827
+ }
828
+ Object.entries(attrGroups).forEach(([key, value]) => {
829
+ if (key.startsWith("_") && key !== "_t" && key !== "_o") {
830
+ markers[key] = value;
831
+ }
832
+ });
833
+ return markers;
834
+ }
835
+
836
+ private isTruthy(val: unknown): boolean {
837
+ return val === true || val === 1 || val === "1";
838
+ }
715
839
  }
package/src/main.ts CHANGED
@@ -27,6 +27,7 @@ export { default as RaftUtils } from './RaftUtils';
27
27
  export { default as RaftSysTypeManager } from './RaftSysTypeManager';
28
28
  export { default as RaftDeviceMgrIF } from './RaftDeviceMgrIF';
29
29
  export { DeviceManager as RaftDeviceManager } from './RaftDeviceManager';
30
+ export type { DeviceDecodedData } from './RaftDeviceManager';
30
31
 
31
32
  export * from './RaftTypes';
32
33
  export * from './RaftSystemType';