@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.
Files changed (224) hide show
  1. package/.editorconfig +14 -0
  2. package/.gitattributes +11 -0
  3. package/.nvmrc +1 -0
  4. package/TODO.md +1 -0
  5. package/dist/react-native/RaftAttributeHandler.d.ts +14 -0
  6. package/dist/react-native/RaftAttributeHandler.js +375 -0
  7. package/dist/react-native/RaftAttributeHandler.js.map +1 -0
  8. package/dist/react-native/RaftChannel.d.ts +20 -0
  9. package/dist/react-native/RaftChannel.js +12 -0
  10. package/dist/react-native/RaftChannel.js.map +1 -0
  11. package/dist/react-native/RaftChannelBLE.native.d.ts +95 -0
  12. package/dist/react-native/RaftChannelBLE.native.js +483 -0
  13. package/dist/react-native/RaftChannelBLE.native.js.map +1 -0
  14. package/dist/react-native/RaftChannelBLE.web.d.ts +40 -0
  15. package/dist/react-native/RaftChannelBLE.web.js +302 -0
  16. package/dist/react-native/RaftChannelBLE.web.js.map +1 -0
  17. package/dist/react-native/RaftChannelBLEFactory.d.ts +10 -0
  18. package/dist/react-native/RaftChannelBLEFactory.js +17 -0
  19. package/dist/react-native/RaftChannelBLEFactory.js.map +1 -0
  20. package/dist/react-native/RaftChannelBLEScanner.native.d.ts +18 -0
  21. package/dist/react-native/RaftChannelBLEScanner.native.js +138 -0
  22. package/dist/react-native/RaftChannelBLEScanner.native.js.map +1 -0
  23. package/dist/react-native/RaftChannelSimulated.d.ts +42 -0
  24. package/dist/react-native/RaftChannelSimulated.js +1000 -0
  25. package/dist/react-native/RaftChannelSimulated.js.map +1 -0
  26. package/dist/react-native/RaftChannelWebSerial.d.ts +39 -0
  27. package/dist/react-native/RaftChannelWebSerial.js +329 -0
  28. package/dist/react-native/RaftChannelWebSerial.js.map +1 -0
  29. package/dist/react-native/RaftChannelWebSocket.d.ts +30 -0
  30. package/dist/react-native/RaftChannelWebSocket.js +222 -0
  31. package/dist/react-native/RaftChannelWebSocket.js.map +1 -0
  32. package/dist/react-native/RaftCommsStats.d.ts +39 -0
  33. package/dist/react-native/RaftCommsStats.js +128 -0
  34. package/dist/react-native/RaftCommsStats.js.map +1 -0
  35. package/dist/react-native/RaftConnEvents.d.ts +39 -0
  36. package/dist/react-native/RaftConnEvents.js +54 -0
  37. package/dist/react-native/RaftConnEvents.js.map +1 -0
  38. package/dist/react-native/RaftConnector.d.ts +248 -0
  39. package/dist/react-native/RaftConnector.js +658 -0
  40. package/dist/react-native/RaftConnector.js.map +1 -0
  41. package/dist/react-native/RaftCustomAttrHandler.d.ts +6 -0
  42. package/dist/react-native/RaftCustomAttrHandler.js +93 -0
  43. package/dist/react-native/RaftCustomAttrHandler.js.map +1 -0
  44. package/dist/react-native/RaftDeviceInfo.d.ts +71 -0
  45. package/dist/react-native/RaftDeviceInfo.js +50 -0
  46. package/dist/react-native/RaftDeviceInfo.js.map +1 -0
  47. package/dist/react-native/RaftDeviceManager.d.ts +61 -0
  48. package/dist/react-native/RaftDeviceManager.js +665 -0
  49. package/dist/react-native/RaftDeviceManager.js.map +1 -0
  50. package/dist/react-native/RaftDeviceMgrIF.d.ts +15 -0
  51. package/dist/react-native/RaftDeviceMgrIF.js +11 -0
  52. package/dist/react-native/RaftDeviceMgrIF.js.map +1 -0
  53. package/dist/react-native/RaftDeviceMsg.d.ts +9 -0
  54. package/dist/react-native/RaftDeviceMsg.js +11 -0
  55. package/dist/react-native/RaftDeviceMsg.js.map +1 -0
  56. package/dist/react-native/RaftDeviceStates.d.ts +37 -0
  57. package/dist/react-native/RaftDeviceStates.js +60 -0
  58. package/dist/react-native/RaftDeviceStates.js.map +1 -0
  59. package/dist/react-native/RaftFileHandler.d.ts +52 -0
  60. package/dist/react-native/RaftFileHandler.js +502 -0
  61. package/dist/react-native/RaftFileHandler.js.map +1 -0
  62. package/dist/react-native/RaftLog.d.ts +22 -0
  63. package/dist/react-native/RaftLog.js +63 -0
  64. package/dist/react-native/RaftLog.js.map +1 -0
  65. package/dist/react-native/RaftMiniHDLC.d.ts +18 -0
  66. package/dist/react-native/RaftMiniHDLC.js +383 -0
  67. package/dist/react-native/RaftMiniHDLC.js.map +1 -0
  68. package/dist/react-native/RaftMsgHandler.d.ts +62 -0
  69. package/dist/react-native/RaftMsgHandler.js +511 -0
  70. package/dist/react-native/RaftMsgHandler.js.map +1 -0
  71. package/dist/react-native/RaftMsgTrackInfo.d.ts +17 -0
  72. package/dist/react-native/RaftMsgTrackInfo.js +42 -0
  73. package/dist/react-native/RaftMsgTrackInfo.js.map +1 -0
  74. package/dist/react-native/RaftProtocolDefs.d.ts +30 -0
  75. package/dist/react-native/RaftProtocolDefs.js +48 -0
  76. package/dist/react-native/RaftProtocolDefs.js.map +1 -0
  77. package/dist/react-native/RaftStreamHandler.d.ts +38 -0
  78. package/dist/react-native/RaftStreamHandler.js +258 -0
  79. package/dist/react-native/RaftStreamHandler.js.map +1 -0
  80. package/dist/react-native/RaftStruct.d.ts +3 -0
  81. package/dist/react-native/RaftStruct.js +258 -0
  82. package/dist/react-native/RaftStruct.js.map +1 -0
  83. package/dist/react-native/RaftSysTypeManager.d.ts +16 -0
  84. package/dist/react-native/RaftSysTypeManager.js +78 -0
  85. package/dist/react-native/RaftSysTypeManager.js.map +1 -0
  86. package/dist/react-native/RaftSystemType.d.ts +30 -0
  87. package/dist/react-native/RaftSystemType.js +3 -0
  88. package/dist/react-native/RaftSystemType.js.map +1 -0
  89. package/dist/react-native/RaftSystemUtils.d.ts +136 -0
  90. package/dist/react-native/RaftSystemUtils.js +412 -0
  91. package/dist/react-native/RaftSystemUtils.js.map +1 -0
  92. package/dist/react-native/RaftTypes.d.ts +195 -0
  93. package/dist/react-native/RaftTypes.js +153 -0
  94. package/dist/react-native/RaftTypes.js.map +1 -0
  95. package/dist/react-native/RaftUpdateEvents.d.ts +33 -0
  96. package/dist/react-native/RaftUpdateEvents.js +46 -0
  97. package/dist/react-native/RaftUpdateEvents.js.map +1 -0
  98. package/dist/react-native/RaftUpdateManager.d.ts +61 -0
  99. package/dist/react-native/RaftUpdateManager.js +621 -0
  100. package/dist/react-native/RaftUpdateManager.js.map +1 -0
  101. package/dist/react-native/RaftUtils.d.ts +128 -0
  102. package/dist/react-native/RaftUtils.js +487 -0
  103. package/dist/react-native/RaftUtils.js.map +1 -0
  104. package/dist/react-native/RaftWifiTypes.d.ts +23 -0
  105. package/dist/react-native/RaftWifiTypes.js +43 -0
  106. package/dist/react-native/RaftWifiTypes.js.map +1 -0
  107. package/dist/react-native/main.d.ts +26 -0
  108. package/dist/react-native/main.js +51 -0
  109. package/dist/react-native/main.js.map +1 -0
  110. package/dist/web/RaftAttributeHandler.js +9 -6
  111. package/dist/web/RaftAttributeHandler.js.map +1 -1
  112. package/dist/web/RaftChannelBLE.web.js +8 -6
  113. package/dist/web/RaftChannelBLE.web.js.map +1 -1
  114. package/dist/web/RaftChannelSimulated.d.ts +10 -0
  115. package/dist/web/RaftChannelSimulated.js +662 -80
  116. package/dist/web/RaftChannelSimulated.js.map +1 -1
  117. package/dist/web/RaftChannelWebSerial.js +2 -2
  118. package/dist/web/RaftChannelWebSerial.js.map +1 -1
  119. package/dist/web/RaftChannelWebSocket.js +16 -1
  120. package/dist/web/RaftChannelWebSocket.js.map +1 -1
  121. package/dist/web/RaftConnector.d.ts +2 -0
  122. package/dist/web/RaftConnector.js +38 -15
  123. package/dist/web/RaftConnector.js.map +1 -1
  124. package/dist/web/RaftCustomAttrHandler.d.ts +2 -0
  125. package/dist/web/RaftCustomAttrHandler.js +54 -26
  126. package/dist/web/RaftCustomAttrHandler.js.map +1 -1
  127. package/dist/web/RaftDeviceInfo.d.ts +3 -1
  128. package/dist/web/RaftDeviceInfo.js +17 -3
  129. package/dist/web/RaftDeviceInfo.js.map +1 -1
  130. package/dist/web/RaftDeviceManager.d.ts +22 -1
  131. package/dist/web/RaftDeviceManager.js +210 -44
  132. package/dist/web/RaftDeviceManager.js.map +1 -1
  133. package/dist/web/RaftDeviceStates.d.ts +1 -1
  134. package/dist/web/RaftDeviceStates.js +2 -2
  135. package/dist/web/RaftDeviceStates.js.map +1 -1
  136. package/dist/web/RaftMsgHandler.js.map +1 -1
  137. package/dist/web/RaftStreamHandler.js +2 -1
  138. package/dist/web/RaftStreamHandler.js.map +1 -1
  139. package/dist/web/RaftStruct.js +197 -147
  140. package/dist/web/RaftStruct.js.map +1 -1
  141. package/dist/web/RaftUpdateManager.js +1 -1
  142. package/dist/web/RaftUpdateManager.js.map +1 -1
  143. package/dist/web/RaftUtils.d.ts +2 -0
  144. package/dist/web/RaftUtils.js +20 -0
  145. package/dist/web/RaftUtils.js.map +1 -1
  146. package/dist/web/main.d.ts +1 -0
  147. package/dist/web/main.js.map +1 -1
  148. package/eslint.config.mjs +33 -0
  149. package/examples/dashboard/package.json +36 -0
  150. package/examples/dashboard/src/CommandPanel.tsx +147 -0
  151. package/examples/dashboard/src/ConnManager.ts +166 -0
  152. package/examples/dashboard/src/DeviceActionsForm.tsx +133 -0
  153. package/examples/dashboard/src/DeviceAttrsForm.tsx +49 -0
  154. package/examples/dashboard/src/DeviceLineChart.tsx +163 -0
  155. package/examples/dashboard/src/DevicePanel.tsx +171 -0
  156. package/examples/dashboard/src/DevicesPanel.tsx +58 -0
  157. package/examples/dashboard/src/DispLedGrid.tsx +110 -0
  158. package/examples/dashboard/src/DispOneLed.tsx +20 -0
  159. package/examples/dashboard/src/LatencyTest.ts +130 -0
  160. package/examples/dashboard/src/LatencyTestPanel.tsx +92 -0
  161. package/examples/dashboard/src/Main.tsx +234 -0
  162. package/examples/dashboard/src/SettingsManager.ts +67 -0
  163. package/examples/dashboard/src/SettingsScreen.tsx +174 -0
  164. package/examples/dashboard/src/StatusPanel.tsx +71 -0
  165. package/examples/dashboard/src/SystemTypeCog/CogStateInfo.ts +162 -0
  166. package/examples/dashboard/src/SystemTypeCog/SystemTypeCog.ts +91 -0
  167. package/examples/dashboard/src/SystemTypeGeneric/StateInfoGeneric.ts +30 -0
  168. package/examples/dashboard/src/SystemTypeGeneric/SystemTypeGeneric.ts +91 -0
  169. package/examples/dashboard/src/SystemTypeMarty/RICAddOn.ts +70 -0
  170. package/examples/dashboard/src/SystemTypeMarty/RICAddOnBase.ts +33 -0
  171. package/examples/dashboard/src/SystemTypeMarty/RICAddOnManager.ts +342 -0
  172. package/examples/dashboard/src/SystemTypeMarty/RICCommsStats.ts +170 -0
  173. package/examples/dashboard/src/SystemTypeMarty/RICHWElem.ts +123 -0
  174. package/examples/dashboard/src/SystemTypeMarty/RICLEDPatternChecker.ts +207 -0
  175. package/examples/dashboard/src/SystemTypeMarty/RICROSSerial.ts +464 -0
  176. package/examples/dashboard/src/SystemTypeMarty/RICServoFaultDetector.ts +146 -0
  177. package/examples/dashboard/src/SystemTypeMarty/RICStateInfo.ts +97 -0
  178. package/examples/dashboard/src/SystemTypeMarty/RICSystemUtils.ts +371 -0
  179. package/examples/dashboard/src/SystemTypeMarty/RICTypes.ts +20 -0
  180. package/examples/dashboard/src/SystemTypeMarty/SystemTypeMarty.ts +119 -0
  181. package/examples/dashboard/src/index.html +15 -0
  182. package/examples/dashboard/src/index.tsx +13 -0
  183. package/examples/dashboard/src/styles.css +408 -0
  184. package/examples/dashboard/tsconfig.json +18 -0
  185. package/jest.config.js +11 -0
  186. package/package.json +4 -7
  187. package/src/RaftAttributeHandler.ts +450 -0
  188. package/src/RaftChannel.ts +32 -0
  189. package/src/RaftChannelBLE.native.ts +617 -0
  190. package/src/RaftChannelBLE.web.ts +374 -0
  191. package/src/RaftChannelBLEFactory.ts +13 -0
  192. package/src/RaftChannelBLEScanner.native.ts +184 -0
  193. package/src/RaftChannelSimulated.ts +1176 -0
  194. package/src/RaftChannelWebSerial.ts +420 -0
  195. package/src/RaftChannelWebSocket.ts +272 -0
  196. package/src/RaftCommsStats.ts +142 -0
  197. package/src/RaftConnEvents.ts +58 -0
  198. package/src/RaftConnector.ts +785 -0
  199. package/src/RaftCustomAttrHandler.ts +117 -0
  200. package/src/RaftDeviceInfo.ts +125 -0
  201. package/src/RaftDeviceManager.ts +844 -0
  202. package/src/RaftDeviceMgrIF.ts +33 -0
  203. package/src/RaftDeviceMsg.ts +20 -0
  204. package/src/RaftDeviceStates.ts +92 -0
  205. package/src/RaftFileHandler.ts +668 -0
  206. package/src/RaftLog.ts +70 -0
  207. package/src/RaftMiniHDLC.ts +396 -0
  208. package/src/RaftMsgHandler.ts +812 -0
  209. package/src/RaftMsgTrackInfo.ts +51 -0
  210. package/src/RaftProtocolDefs.ts +46 -0
  211. package/src/RaftStreamHandler.ts +329 -0
  212. package/src/RaftStruct.ts +282 -0
  213. package/src/RaftSysTypeManager.ts +87 -0
  214. package/src/RaftSystemType.ts +34 -0
  215. package/src/RaftSystemUtils.ts +489 -0
  216. package/src/RaftTypes.ts +279 -0
  217. package/src/RaftUpdateEvents.ts +48 -0
  218. package/src/RaftUpdateManager.ts +781 -0
  219. package/src/RaftUtils.ts +514 -0
  220. package/src/RaftWifiTypes.ts +36 -0
  221. package/src/main.ts +39 -0
  222. package/testdata/TestDeviceTypeRecs.json +492 -0
  223. package/tsconfig.json +30 -0
  224. 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
+ }