@robotical/raftjs 1.3.3

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 (260) hide show
  1. package/.editorconfig +14 -0
  2. package/.gitattributes +11 -0
  3. package/.nvmrc +1 -0
  4. package/LICENSE +22 -0
  5. package/README.md +13 -0
  6. package/TODO.md +1 -0
  7. package/dist/react-native/RaftAttributeHandler.d.ts +12 -0
  8. package/dist/react-native/RaftAttributeHandler.js +241 -0
  9. package/dist/react-native/RaftAttributeHandler.js.map +1 -0
  10. package/dist/react-native/RaftChannel.d.ts +18 -0
  11. package/dist/react-native/RaftChannel.js +12 -0
  12. package/dist/react-native/RaftChannel.js.map +1 -0
  13. package/dist/react-native/RaftChannelBLE.native.d.ts +93 -0
  14. package/dist/react-native/RaftChannelBLE.native.js +455 -0
  15. package/dist/react-native/RaftChannelBLE.native.js.map +1 -0
  16. package/dist/react-native/RaftChannelBLE.web.d.ts +39 -0
  17. package/dist/react-native/RaftChannelBLE.web.js +294 -0
  18. package/dist/react-native/RaftChannelBLE.web.js.map +1 -0
  19. package/dist/react-native/RaftChannelBLEFactory.d.ts +10 -0
  20. package/dist/react-native/RaftChannelBLEFactory.js +17 -0
  21. package/dist/react-native/RaftChannelBLEFactory.js.map +1 -0
  22. package/dist/react-native/RaftChannelBLEScanner.native.d.ts +18 -0
  23. package/dist/react-native/RaftChannelBLEScanner.native.js +146 -0
  24. package/dist/react-native/RaftChannelBLEScanner.native.js.map +1 -0
  25. package/dist/react-native/RaftChannelWebSerial.d.ts +37 -0
  26. package/dist/react-native/RaftChannelWebSerial.js +320 -0
  27. package/dist/react-native/RaftChannelWebSerial.js.map +1 -0
  28. package/dist/react-native/RaftChannelWebSocket.d.ts +28 -0
  29. package/dist/react-native/RaftChannelWebSocket.js +197 -0
  30. package/dist/react-native/RaftChannelWebSocket.js.map +1 -0
  31. package/dist/react-native/RaftCommsStats.d.ts +39 -0
  32. package/dist/react-native/RaftCommsStats.js +128 -0
  33. package/dist/react-native/RaftCommsStats.js.map +1 -0
  34. package/dist/react-native/RaftConnEvents.d.ts +39 -0
  35. package/dist/react-native/RaftConnEvents.js +54 -0
  36. package/dist/react-native/RaftConnEvents.js.map +1 -0
  37. package/dist/react-native/RaftConnector.d.ts +245 -0
  38. package/dist/react-native/RaftConnector.js +614 -0
  39. package/dist/react-native/RaftConnector.js.map +1 -0
  40. package/dist/react-native/RaftCustomAttrHandler.d.ts +4 -0
  41. package/dist/react-native/RaftCustomAttrHandler.js +50 -0
  42. package/dist/react-native/RaftCustomAttrHandler.js.map +1 -0
  43. package/dist/react-native/RaftDeviceInfo.d.ts +59 -0
  44. package/dist/react-native/RaftDeviceInfo.js +36 -0
  45. package/dist/react-native/RaftDeviceInfo.js.map +1 -0
  46. package/dist/react-native/RaftDeviceManager.d.ts +35 -0
  47. package/dist/react-native/RaftDeviceManager.js +353 -0
  48. package/dist/react-native/RaftDeviceManager.js.map +1 -0
  49. package/dist/react-native/RaftDeviceMgrIF.d.ts +12 -0
  50. package/dist/react-native/RaftDeviceMgrIF.js +11 -0
  51. package/dist/react-native/RaftDeviceMgrIF.js.map +1 -0
  52. package/dist/react-native/RaftDeviceMsg.d.ts +9 -0
  53. package/dist/react-native/RaftDeviceMsg.js +11 -0
  54. package/dist/react-native/RaftDeviceMsg.js.map +1 -0
  55. package/dist/react-native/RaftDeviceStates.d.ts +33 -0
  56. package/dist/react-native/RaftDeviceStates.js +60 -0
  57. package/dist/react-native/RaftDeviceStates.js.map +1 -0
  58. package/dist/react-native/RaftFileHandler.d.ts +52 -0
  59. package/dist/react-native/RaftFileHandler.js +502 -0
  60. package/dist/react-native/RaftFileHandler.js.map +1 -0
  61. package/dist/react-native/RaftLog.d.ts +22 -0
  62. package/dist/react-native/RaftLog.js +63 -0
  63. package/dist/react-native/RaftLog.js.map +1 -0
  64. package/dist/react-native/RaftMiniHDLC.d.ts +18 -0
  65. package/dist/react-native/RaftMiniHDLC.js +383 -0
  66. package/dist/react-native/RaftMiniHDLC.js.map +1 -0
  67. package/dist/react-native/RaftMsgHandler.d.ts +57 -0
  68. package/dist/react-native/RaftMsgHandler.js +480 -0
  69. package/dist/react-native/RaftMsgHandler.js.map +1 -0
  70. package/dist/react-native/RaftMsgTrackInfo.d.ts +17 -0
  71. package/dist/react-native/RaftMsgTrackInfo.js +42 -0
  72. package/dist/react-native/RaftMsgTrackInfo.js.map +1 -0
  73. package/dist/react-native/RaftProtocolDefs.d.ts +30 -0
  74. package/dist/react-native/RaftProtocolDefs.js +48 -0
  75. package/dist/react-native/RaftProtocolDefs.js.map +1 -0
  76. package/dist/react-native/RaftStreamHandler.d.ts +38 -0
  77. package/dist/react-native/RaftStreamHandler.js +257 -0
  78. package/dist/react-native/RaftStreamHandler.js.map +1 -0
  79. package/dist/react-native/RaftSystemType.d.ts +25 -0
  80. package/dist/react-native/RaftSystemType.js +3 -0
  81. package/dist/react-native/RaftSystemType.js.map +1 -0
  82. package/dist/react-native/RaftSystemUtils.d.ts +136 -0
  83. package/dist/react-native/RaftSystemUtils.js +410 -0
  84. package/dist/react-native/RaftSystemUtils.js.map +1 -0
  85. package/dist/react-native/RaftTypes.d.ts +195 -0
  86. package/dist/react-native/RaftTypes.js +190 -0
  87. package/dist/react-native/RaftTypes.js.map +1 -0
  88. package/dist/react-native/RaftUpdateEvents.d.ts +33 -0
  89. package/dist/react-native/RaftUpdateEvents.js +46 -0
  90. package/dist/react-native/RaftUpdateEvents.js.map +1 -0
  91. package/dist/react-native/RaftUpdateManager.d.ts +61 -0
  92. package/dist/react-native/RaftUpdateManager.js +618 -0
  93. package/dist/react-native/RaftUpdateManager.js.map +1 -0
  94. package/dist/react-native/RaftUtils.d.ts +125 -0
  95. package/dist/react-native/RaftUtils.js +454 -0
  96. package/dist/react-native/RaftUtils.js.map +1 -0
  97. package/dist/react-native/RaftWifiTypes.d.ts +23 -0
  98. package/dist/react-native/RaftWifiTypes.js +43 -0
  99. package/dist/react-native/RaftWifiTypes.js.map +1 -0
  100. package/dist/react-native/main.d.ts +22 -0
  101. package/dist/react-native/main.js +48 -0
  102. package/dist/react-native/main.js.map +1 -0
  103. package/dist/web/RaftAttributeHandler.d.ts +12 -0
  104. package/dist/web/RaftAttributeHandler.js +241 -0
  105. package/dist/web/RaftAttributeHandler.js.map +1 -0
  106. package/dist/web/RaftChannel.d.ts +18 -0
  107. package/dist/web/RaftChannel.js +12 -0
  108. package/dist/web/RaftChannel.js.map +1 -0
  109. package/dist/web/RaftChannelBLE.web.d.ts +39 -0
  110. package/dist/web/RaftChannelBLE.web.js +294 -0
  111. package/dist/web/RaftChannelBLE.web.js.map +1 -0
  112. package/dist/web/RaftChannelBLEFactory.d.ts +10 -0
  113. package/dist/web/RaftChannelBLEFactory.js +17 -0
  114. package/dist/web/RaftChannelBLEFactory.js.map +1 -0
  115. package/dist/web/RaftChannelWebSerial.d.ts +37 -0
  116. package/dist/web/RaftChannelWebSerial.js +320 -0
  117. package/dist/web/RaftChannelWebSerial.js.map +1 -0
  118. package/dist/web/RaftChannelWebSocket.d.ts +28 -0
  119. package/dist/web/RaftChannelWebSocket.js +197 -0
  120. package/dist/web/RaftChannelWebSocket.js.map +1 -0
  121. package/dist/web/RaftCommsStats.d.ts +39 -0
  122. package/dist/web/RaftCommsStats.js +128 -0
  123. package/dist/web/RaftCommsStats.js.map +1 -0
  124. package/dist/web/RaftConnEvents.d.ts +39 -0
  125. package/dist/web/RaftConnEvents.js +54 -0
  126. package/dist/web/RaftConnEvents.js.map +1 -0
  127. package/dist/web/RaftConnector.d.ts +245 -0
  128. package/dist/web/RaftConnector.js +614 -0
  129. package/dist/web/RaftConnector.js.map +1 -0
  130. package/dist/web/RaftCustomAttrHandler.d.ts +4 -0
  131. package/dist/web/RaftCustomAttrHandler.js +50 -0
  132. package/dist/web/RaftCustomAttrHandler.js.map +1 -0
  133. package/dist/web/RaftDeviceInfo.d.ts +59 -0
  134. package/dist/web/RaftDeviceInfo.js +36 -0
  135. package/dist/web/RaftDeviceInfo.js.map +1 -0
  136. package/dist/web/RaftDeviceManager.d.ts +35 -0
  137. package/dist/web/RaftDeviceManager.js +353 -0
  138. package/dist/web/RaftDeviceManager.js.map +1 -0
  139. package/dist/web/RaftDeviceMgrIF.d.ts +12 -0
  140. package/dist/web/RaftDeviceMgrIF.js +11 -0
  141. package/dist/web/RaftDeviceMgrIF.js.map +1 -0
  142. package/dist/web/RaftDeviceMsg.d.ts +9 -0
  143. package/dist/web/RaftDeviceMsg.js +11 -0
  144. package/dist/web/RaftDeviceMsg.js.map +1 -0
  145. package/dist/web/RaftDeviceStates.d.ts +33 -0
  146. package/dist/web/RaftDeviceStates.js +60 -0
  147. package/dist/web/RaftDeviceStates.js.map +1 -0
  148. package/dist/web/RaftFileHandler.d.ts +52 -0
  149. package/dist/web/RaftFileHandler.js +502 -0
  150. package/dist/web/RaftFileHandler.js.map +1 -0
  151. package/dist/web/RaftLog.d.ts +22 -0
  152. package/dist/web/RaftLog.js +63 -0
  153. package/dist/web/RaftLog.js.map +1 -0
  154. package/dist/web/RaftMiniHDLC.d.ts +18 -0
  155. package/dist/web/RaftMiniHDLC.js +383 -0
  156. package/dist/web/RaftMiniHDLC.js.map +1 -0
  157. package/dist/web/RaftMsgHandler.d.ts +57 -0
  158. package/dist/web/RaftMsgHandler.js +480 -0
  159. package/dist/web/RaftMsgHandler.js.map +1 -0
  160. package/dist/web/RaftMsgTrackInfo.d.ts +17 -0
  161. package/dist/web/RaftMsgTrackInfo.js +42 -0
  162. package/dist/web/RaftMsgTrackInfo.js.map +1 -0
  163. package/dist/web/RaftProtocolDefs.d.ts +30 -0
  164. package/dist/web/RaftProtocolDefs.js +48 -0
  165. package/dist/web/RaftProtocolDefs.js.map +1 -0
  166. package/dist/web/RaftStreamHandler.d.ts +38 -0
  167. package/dist/web/RaftStreamHandler.js +257 -0
  168. package/dist/web/RaftStreamHandler.js.map +1 -0
  169. package/dist/web/RaftSystemType.d.ts +25 -0
  170. package/dist/web/RaftSystemType.js +3 -0
  171. package/dist/web/RaftSystemType.js.map +1 -0
  172. package/dist/web/RaftSystemUtils.d.ts +136 -0
  173. package/dist/web/RaftSystemUtils.js +410 -0
  174. package/dist/web/RaftSystemUtils.js.map +1 -0
  175. package/dist/web/RaftTypes.d.ts +195 -0
  176. package/dist/web/RaftTypes.js +190 -0
  177. package/dist/web/RaftTypes.js.map +1 -0
  178. package/dist/web/RaftUpdateEvents.d.ts +33 -0
  179. package/dist/web/RaftUpdateEvents.js +46 -0
  180. package/dist/web/RaftUpdateEvents.js.map +1 -0
  181. package/dist/web/RaftUpdateManager.d.ts +61 -0
  182. package/dist/web/RaftUpdateManager.js +618 -0
  183. package/dist/web/RaftUpdateManager.js.map +1 -0
  184. package/dist/web/RaftUtils.d.ts +125 -0
  185. package/dist/web/RaftUtils.js +454 -0
  186. package/dist/web/RaftUtils.js.map +1 -0
  187. package/dist/web/RaftWifiTypes.d.ts +23 -0
  188. package/dist/web/RaftWifiTypes.js +43 -0
  189. package/dist/web/RaftWifiTypes.js.map +1 -0
  190. package/dist/web/main.d.ts +22 -0
  191. package/dist/web/main.js +48 -0
  192. package/dist/web/main.js.map +1 -0
  193. package/eslint.config.mjs +33 -0
  194. package/examples/dashboard/package.json +43 -0
  195. package/examples/dashboard/src/CommandPanel.tsx +147 -0
  196. package/examples/dashboard/src/ConnManager.ts +85 -0
  197. package/examples/dashboard/src/DeviceActionsForm.tsx +133 -0
  198. package/examples/dashboard/src/DeviceAttrsForm.tsx +49 -0
  199. package/examples/dashboard/src/DeviceLineChart.tsx +139 -0
  200. package/examples/dashboard/src/DevicePanel.tsx +135 -0
  201. package/examples/dashboard/src/DevicesPanel.tsx +57 -0
  202. package/examples/dashboard/src/DispLedGrid.tsx +110 -0
  203. package/examples/dashboard/src/DispOneLed.tsx +20 -0
  204. package/examples/dashboard/src/Main.tsx +106 -0
  205. package/examples/dashboard/src/StatusPanel.tsx +71 -0
  206. package/examples/dashboard/src/SystemTypeCog/CogStateInfo.ts +157 -0
  207. package/examples/dashboard/src/SystemTypeCog/SystemTypeCog.ts +84 -0
  208. package/examples/dashboard/src/SystemTypeMarty/RICAddOn.ts +70 -0
  209. package/examples/dashboard/src/SystemTypeMarty/RICAddOnBase.ts +33 -0
  210. package/examples/dashboard/src/SystemTypeMarty/RICAddOnManager.ts +342 -0
  211. package/examples/dashboard/src/SystemTypeMarty/RICCommsStats.ts +170 -0
  212. package/examples/dashboard/src/SystemTypeMarty/RICHWElem.ts +123 -0
  213. package/examples/dashboard/src/SystemTypeMarty/RICLEDPatternChecker.ts +207 -0
  214. package/examples/dashboard/src/SystemTypeMarty/RICROSSerial.ts +464 -0
  215. package/examples/dashboard/src/SystemTypeMarty/RICServoFaultDetector.ts +146 -0
  216. package/examples/dashboard/src/SystemTypeMarty/RICStateInfo.ts +78 -0
  217. package/examples/dashboard/src/SystemTypeMarty/RICSystemUtils.ts +371 -0
  218. package/examples/dashboard/src/SystemTypeMarty/RICTypes.ts +20 -0
  219. package/examples/dashboard/src/SystemTypeMarty/SystemTypeMarty.ts +116 -0
  220. package/examples/dashboard/src/index.html +15 -0
  221. package/examples/dashboard/src/index.tsx +15 -0
  222. package/examples/dashboard/src/styles.css +386 -0
  223. package/examples/dashboard/tsconfig.json +18 -0
  224. package/jest.config.js +11 -0
  225. package/package.json +59 -0
  226. package/src/RaftAttributeHandler.ts +289 -0
  227. package/src/RaftChannel.ts +30 -0
  228. package/src/RaftChannelBLE.native.ts +583 -0
  229. package/src/RaftChannelBLE.web.ts +362 -0
  230. package/src/RaftChannelBLEFactory.ts +13 -0
  231. package/src/RaftChannelBLEScanner.native.ts +190 -0
  232. package/src/RaftChannelWebSerial.ts +409 -0
  233. package/src/RaftChannelWebSocket.ts +245 -0
  234. package/src/RaftCommsStats.ts +142 -0
  235. package/src/RaftConnEvents.ts +58 -0
  236. package/src/RaftConnector.ts +737 -0
  237. package/src/RaftCustomAttrHandler.ts +54 -0
  238. package/src/RaftDeviceInfo.ts +98 -0
  239. package/src/RaftDeviceManager.ts +436 -0
  240. package/src/RaftDeviceMgrIF.ts +28 -0
  241. package/src/RaftDeviceMsg.ts +20 -0
  242. package/src/RaftDeviceStates.ts +88 -0
  243. package/src/RaftFileHandler.ts +668 -0
  244. package/src/RaftLog.ts +70 -0
  245. package/src/RaftMiniHDLC.ts +396 -0
  246. package/src/RaftMsgHandler.ts +778 -0
  247. package/src/RaftMsgTrackInfo.ts +51 -0
  248. package/src/RaftProtocolDefs.ts +46 -0
  249. package/src/RaftStreamHandler.ts +328 -0
  250. package/src/RaftSystemType.ts +29 -0
  251. package/src/RaftSystemUtils.ts +487 -0
  252. package/src/RaftTypes.ts +281 -0
  253. package/src/RaftUpdateEvents.ts +48 -0
  254. package/src/RaftUpdateManager.ts +778 -0
  255. package/src/RaftUtils.ts +484 -0
  256. package/src/RaftWifiTypes.ts +36 -0
  257. package/src/main.ts +36 -0
  258. package/testdata/TestDeviceTypeRecs.json +492 -0
  259. package/tsconfig.json +30 -0
  260. package/tsconfig.react-native.json +29 -0
@@ -0,0 +1,54 @@
1
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2
+ //
3
+ // RaftCustomAttrHandler
4
+ // Custom attribute handler for Raft devices
5
+ //
6
+ // Rob Dobson (C) 2024
7
+ //
8
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
9
+
10
+ import { DeviceTypePollRespMetadata } from "./RaftDeviceInfo";
11
+
12
+ export default class CustomAttrHandler {
13
+
14
+ public handleAttr(pollRespMetadata: DeviceTypePollRespMetadata, msgBuffer: Buffer, msgBufIdx: number): number[][] {
15
+
16
+ // Number of bytes in the each message
17
+ const numMsgBytes = pollRespMetadata.b;
18
+
19
+ // Create a vector for each attribute in the metadata
20
+ let attrValueVecs: [][] = [];
21
+
22
+ // Reference to each vector by attribute name
23
+ let attrValues: { [key: string]: number[] } = {};
24
+
25
+ // Add attributes to the vector
26
+ for (let attrIdx = 0; attrIdx < pollRespMetadata.a.length; attrIdx++) {
27
+ attrValueVecs.push([]);
28
+ attrValues[pollRespMetadata.a[attrIdx].n] = attrValueVecs[attrIdx];
29
+ }
30
+
31
+ // Custom code for each device type
32
+ if (pollRespMetadata.c!.n === "max30101_fifo") {
33
+ // Hex dump msgBuffer
34
+ // console.log(`CustomAttrHandler handleAttr ${pollRespMetadata.c!.n} msgBuffer: ${msgBuffer.toString('hex')}`);
35
+ let buf = msgBuffer.slice(msgBufIdx);
36
+ if (buf.length < numMsgBytes) {
37
+ return [];
38
+ }
39
+
40
+ // Generated code ...
41
+ let N=(buf[0]+32-buf[2])%32;
42
+ let k=3;
43
+ let i=0;
44
+ while (i<N) {
45
+ attrValues['Red'].push(0); attrValues['Red'][attrValues['Red'].length-1] =(buf[k]<<16)|(buf[k+1]<<8)|buf[k+2];
46
+ attrValues['IR'].push(0); attrValues['IR'][attrValues['IR'].length-1] =(buf[k+3]<<16)|(buf[k+4]<<8)|buf[k+5];
47
+ k+=6;
48
+ i++;
49
+ ;
50
+ }
51
+ }
52
+ return attrValueVecs;
53
+ }
54
+ }
@@ -0,0 +1,98 @@
1
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2
+ //
3
+ // RaftDeviceInfo
4
+ // Device information for Raft devices
5
+ //
6
+ // Rob Dobson (C) 2024
7
+ //
8
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
9
+
10
+ const attrTypeBits: { [key: string]: number } = {
11
+ "c": 8, "b": 8, "B": 8, "?": 8,
12
+ "h": 16, "H": 16, ">h": 16, "<h": 16, ">H": 16, "<H": 16,
13
+ "i": 32, "I": 32, ">i": 32, "<i": 32, ">I": 32, "<I": 32, "l": 32, "L": 32, ">l": 32, "<l": 32, ">L": 32, "<L": 32,
14
+ "q": 64, "Q": 64, ">q": 64, "<q": 64, ">Q": 64, "<Q": 64,
15
+ "f": 32, ">f": 32, "<f": 32,
16
+ "d": 64, ">d": 64, "<d": 64,
17
+ };
18
+
19
+ export function getAttrTypeBits(attrType: string): number {
20
+ if (attrType in attrTypeBits) {
21
+ return attrTypeBits[attrType];
22
+ }
23
+ return 8;
24
+ }
25
+
26
+ export function isAttrTypeSigned(attrType: string): boolean {
27
+ const attrStr = attrType.charAt(0) === ">" || attrType.charAt(0) === "<" ? attrType.slice(1).charAt(0) : attrType.charAt(0);
28
+ return attrStr === "b" || attrStr === "h" || attrStr === "i" || attrStr === "l" || attrStr === "q";
29
+ }
30
+
31
+ export function decodeAttrUnitsEncoding(unitsEncoding: string): string {
32
+ // Replace instances of HTML encoded chars like &deg; with the actual char
33
+ return unitsEncoding.replace(/&deg;/g, "°");
34
+ }
35
+
36
+ export interface DeviceTypeAttribute {
37
+ n: string; // Name
38
+ t: string; // Type in python struct module format (e.g. 'H' uint16, 'h' int16, 'f' float etc.)
39
+ at?: number; // Start pos in buffer (after timestamp) if present (otherwise use relative position)
40
+ u?: string; // Units (e.g. mm)
41
+ r?: number[]; // Range (either min, max or min, max, step or discrete values)
42
+ x?: number; // XOR bit mask to invert bits in the attribute value
43
+ m?: number | string; // AND bit mask to extract the attribute value from the message
44
+ s?: number; // Shift value to shift the attribute value to the right (or left if negative)
45
+ sb?: number; // Sign-bit position (0-based)
46
+ ss?: number; // Sign-bit subtraction value
47
+ d?: number; // Divisor to convert the raw attribute value (after operations above) to the actual value
48
+ a?: number; // Value to add after division
49
+ f?: string; // Format string similar to C printf format string (e.g. %d, %x, %f, %04d, %08x, %08.2f etc.), %b = boolean (0 iff 0, else 1)
50
+ o?: string; // Type of output value (e.g. 'bool', 'uint8', 'float')
51
+ v?: boolean | number; // Visibility of the attribute in all locations (mainly used to hide attributes that are not useful to the user)
52
+ vs?: boolean | number; // Display attribute value in time-series graphs
53
+ vf?: boolean | number; // Display attribute value in the device info panel
54
+ }
55
+
56
+ export interface CustomFunctionDefinition {
57
+ n: string; // Function name
58
+ c: string; // Function pseudo-code
59
+ }
60
+
61
+ export interface DeviceTypePollRespMetadata {
62
+ b: number; // Size of polled response data block in bytes (excluding timestamp)
63
+ a: DeviceTypeAttribute[]; // Attributes in the polled response
64
+ c?: CustomFunctionDefinition; // Custom function definition
65
+ us?: number; // Time between consecutive samples in microseconds
66
+ }
67
+
68
+ export interface DeviceTypeAction {
69
+ n: string; // Action name
70
+ t?: string; // Action type using python struct module format (e.g. 'H' for unsigned short, 'h' for signed short, 'f' for float etc.)
71
+ w: string; // Prefix to write to cmd API
72
+ r?: number[]; // Range of valid values for the action
73
+ f?: string; // Custom formatting options (e.g. LEDPIX for LED pixel grid)
74
+ NX?: number; // Number of X in the LED pixel grid
75
+ NY?: number; // Number of Y in the LED pixel grid
76
+ concat?: boolean; // Concatenate the all values into a single command
77
+ d?: number; // Default value
78
+ }
79
+
80
+ export interface DeviceTypeInfo {
81
+ name: string;
82
+ desc: string;
83
+ manu: string;
84
+ type: string;
85
+ resp?: DeviceTypePollRespMetadata;
86
+ actions?: DeviceTypeAction[];
87
+ }
88
+
89
+ export interface DeviceTypeInfoRecs {
90
+ [devType: string]: DeviceTypeInfo;
91
+ }
92
+
93
+ export type RaftDevTypeInfoResponse = {
94
+ req: string;
95
+ rslt: string;
96
+ devinfo: DeviceTypeInfo;
97
+ };
98
+
@@ -0,0 +1,436 @@
1
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2
+ //
3
+ // RaftDeviceManager
4
+ // Device manager for Raft devices
5
+ //
6
+ // Rob Dobson (C) 2024
7
+ //
8
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
9
+
10
+ import { DeviceAttributeState, DevicesState, DeviceState, getDeviceKey } from "./RaftDeviceStates";
11
+ import { DeviceMsgJson } from "./RaftDeviceMsg";
12
+ import { RaftOKFail } from './RaftTypes';
13
+ import { DeviceTypeInfo, DeviceTypeAction, DeviceTypeInfoRecs, RaftDevTypeInfoResponse } from "./RaftDeviceInfo";
14
+ import struct, { DataType } from 'python-struct';
15
+ import AttributeHandler from "./RaftAttributeHandler";
16
+ import RaftSystemUtils from "./RaftSystemUtils";
17
+ import RaftDeviceMgrIF from "./RaftDeviceMgrIF";
18
+ import { Buffer } from 'buffer';
19
+
20
+ export class DeviceManager implements RaftDeviceMgrIF{
21
+
22
+ // Singleton
23
+ // private static _instance: DeviceManager;
24
+
25
+ // Max data points to store
26
+ private _maxDatapointsToStore = 1000;
27
+
28
+ // Min time between attempts to retrieve device type info
29
+ private _minTimeBetweenDeviceTypeInfoRetrievalMs = 60000;
30
+
31
+ // Attribute handler
32
+ private _attributeHandler = new AttributeHandler();
33
+
34
+ // Devices state
35
+ private _devicesState = new DevicesState();
36
+
37
+ // Last time each device was updated - used to detect devices that are no longer present
38
+ private _deviceLastUpdateTime: { [deviceKey: string]: number } = {};
39
+
40
+ // Flag indicating that removed devices should be removed from the state
41
+ private _removeDevicesFlag = true;
42
+ private _removeDevicesTimeMs = 60000;
43
+
44
+ // System utils
45
+ private _systemUtils: RaftSystemUtils | null = null;
46
+
47
+ // Device callbacks
48
+ private _callbackNewDevice: ((deviceKey: string, state: DeviceState) => void) | null = null;
49
+ private _callbackNewDeviceAttribute: ((deviceKey: string, attrState: DeviceAttributeState) => void) | null = null;
50
+ private _callbackNewAttributeData: ((deviceKey: string, attrState: DeviceAttributeState) => void) | null = null;
51
+
52
+ public getDevicesState(): DevicesState {
53
+ return this._devicesState;
54
+ }
55
+
56
+ public getDeviceState(deviceKey: string): DeviceState {
57
+ return this._devicesState[deviceKey];
58
+ }
59
+
60
+ // Cached device type data
61
+ private _cachedDeviceTypeRecs: DeviceTypeInfoRecs = {};
62
+
63
+ // Cached device type previous attempt times
64
+ private _cachedDeviceTypePreviousAttemptTimes: { [deviceType: string]: number } = {};
65
+
66
+ // Constructor
67
+ constructor() {
68
+ }
69
+
70
+ ////////////////////////////////////////////////////////////////////////////
71
+ // Send REST commands
72
+ ////////////////////////////////////////////////////////////////////////////
73
+
74
+ async sendCommand(cmd: string): Promise<boolean> {
75
+ try {
76
+ // Get the msg handler
77
+ const msgHandler = this._systemUtils?.getMsgHandler();
78
+ if (msgHandler) {
79
+ const msgRslt = await msgHandler.sendRICRESTURL<RaftOKFail>(cmd);
80
+ return msgRslt.isOk();
81
+ }
82
+ return false;
83
+ } catch (error) {
84
+ console.warn(`DeviceManager sendCommand error ${error}`);
85
+ return false;
86
+ }
87
+ }
88
+
89
+ ////////////////////////////////////////////////////////////////////////////
90
+ // Setup
91
+ ////////////////////////////////////////////////////////////////////////////
92
+
93
+ public async setup(systemUtils: RaftSystemUtils): Promise<boolean> {
94
+
95
+ // Save the system utils
96
+ this._systemUtils = systemUtils;
97
+ return true;
98
+ }
99
+
100
+ ////////////////////////////////////////////////////////////////////////////
101
+ // Callbacks
102
+ ////////////////////////////////////////////////////////////////////////////
103
+
104
+ // Register state change callbacks
105
+ public onNewDevice(callback: (deviceKey: string, state: DeviceState) => void): void {
106
+ // Save the callback
107
+ this._callbackNewDevice = callback;
108
+ }
109
+ public onNewDeviceAttribute(callback: (deviceKey: string, attrState: DeviceAttributeState) => void): void {
110
+ // Save the callback
111
+ this._callbackNewDeviceAttribute = callback;
112
+ }
113
+ public onNewAttributeData(callback: (deviceKey: string, attrState: DeviceAttributeState) => void): void {
114
+ // Save the callback
115
+ this._callbackNewAttributeData = callback;
116
+ }
117
+
118
+ ////////////////////////////////////////////////////////////////////////////
119
+ // Set the friendly name for the device
120
+ ////////////////////////////////////////////////////////////////////////////
121
+
122
+ public async setFriendlyName(friendlyName: string): Promise<void> {
123
+ // Set using utils
124
+ await this._systemUtils?.setRaftName(friendlyName);
125
+ }
126
+
127
+ ////////////////////////////////////////////////////////////////////////////
128
+ // Handle device message JSON
129
+ ////////////////////////////////////////////////////////////////////////////
130
+
131
+ public handleClientMsgJson(jsonMsg: string) {
132
+
133
+ let data = JSON.parse(jsonMsg) as DeviceMsgJson;
134
+ // console.log(`DeviceManager websocket message ${JSON.stringify(data)}`);
135
+
136
+ // Iterate over the buses
137
+ Object.entries(data).forEach(([busName, devices]) => {
138
+
139
+ // Check for bus status info
140
+ if (devices && typeof devices === "object" && "_s" in devices) {
141
+ // console.log(`DeviceManager bus status ${JSON.stringify(devices._s)}`);
142
+ return;
143
+ }
144
+
145
+ // Iterate over the devices
146
+ Object.entries(devices).forEach(async ([devAddr, attrGroups]) => {
147
+
148
+ // Check for non-device info (starts with _)
149
+ if (devAddr.startsWith("_")) {
150
+ return;
151
+ }
152
+
153
+ // Device key
154
+ const deviceKey = getDeviceKey(busName, devAddr);
155
+
156
+ // Update the last update time
157
+ this._deviceLastUpdateTime[deviceKey] = Date.now();
158
+
159
+ // Check if a device state already exists
160
+ if (!(deviceKey in this._devicesState) || (this._devicesState[deviceKey].deviceTypeInfo === undefined)) {
161
+
162
+ let deviceTypeName = "";
163
+ if (attrGroups && typeof attrGroups === 'object' && "_t" in attrGroups && typeof attrGroups._t === "string") {
164
+ deviceTypeName = attrGroups._t || "";
165
+ } else {
166
+ console.warn(`DeviceManager missing device type attrGroups ${JSON.stringify(attrGroups)}`);
167
+ return;
168
+ }
169
+
170
+ // Get the device type info
171
+ const deviceTypeInfo = await this.getDeviceTypeInfo(busName, devAddr, deviceTypeName);
172
+
173
+ // Check if device record exists
174
+ if (deviceKey in this._devicesState) {
175
+ if (deviceTypeInfo !== undefined) {
176
+ this._devicesState[deviceKey].deviceTypeInfo = deviceTypeInfo;
177
+ }
178
+ } else {
179
+ // Create device record - device type info may be undefined
180
+ this._devicesState[deviceKey] = {
181
+ deviceTypeInfo: deviceTypeInfo,
182
+ deviceTimeline: {
183
+ timestampsUs: [],
184
+ lastReportTimestampUs: 0,
185
+ reportTimestampOffsetUs: 0
186
+ },
187
+ deviceAttributes: {},
188
+ deviceIsNew: true,
189
+ stateChanged: false,
190
+ isOnline: true
191
+ };
192
+ }
193
+ }
194
+
195
+ // Get device state
196
+ const deviceState = this._devicesState[deviceKey];
197
+
198
+ // Check for online/offline state information
199
+ if (attrGroups && typeof attrGroups === "object" && "_o" in attrGroups) {
200
+ deviceState.isOnline = ((attrGroups._o === true) || (attrGroups._o === "1") || (attrGroups._o === 1));
201
+ }
202
+
203
+ // Check if device type info is available
204
+ if (!deviceState.deviceTypeInfo) {
205
+ return;
206
+ }
207
+
208
+ // Iterate attribute groups
209
+ Object.entries(attrGroups).forEach(([attrGroupName, msgHexStr]) => {
210
+
211
+ // Check valid
212
+ if (attrGroupName.startsWith("_") || (typeof msgHexStr != 'string')) {
213
+ return;
214
+ }
215
+
216
+ // Check the device type info
217
+ if (!deviceState.deviceTypeInfo!.resp) {
218
+ return;
219
+ }
220
+
221
+ // Convert the hex string to an arraybuffer by converting each pair of hex chars to a byte
222
+ const msgBytes = this.hexToBytes(msgHexStr);
223
+
224
+ // Convert to a Buffer
225
+ const msgBuffer = Buffer.from(msgBytes);
226
+
227
+ // Work through the message which may contain multiple data instances
228
+ let msgBufIdx = 0;
229
+
230
+ // Iterate over attributes in the group
231
+ const pollRespMetadata = deviceState.deviceTypeInfo!.resp!;
232
+
233
+ // Loop
234
+ while (msgBufIdx < msgBytes.length) {
235
+
236
+ const curTimelineLen = deviceState.deviceTimeline.timestampsUs.length;
237
+ const newMsgBufIdx = this._attributeHandler.processMsgAttrGroup(msgBuffer, msgBufIdx,
238
+ deviceState.deviceTimeline, pollRespMetadata,
239
+ deviceState.deviceAttributes,
240
+ this._maxDatapointsToStore);
241
+ if (newMsgBufIdx < 0)
242
+ break;
243
+ msgBufIdx = newMsgBufIdx;
244
+ if (deviceState.deviceTimeline.timestampsUs.length !== curTimelineLen) {
245
+ deviceState.stateChanged = true;
246
+ }
247
+ }
248
+ });
249
+ });
250
+ });
251
+
252
+ // Check for devices that have not been updated for a while
253
+ if (this._removeDevicesFlag) {
254
+ const nowTime = Date.now();
255
+ Object.entries(this._deviceLastUpdateTime).forEach(([deviceKey, lastUpdateTime]) => {
256
+ if ((nowTime - lastUpdateTime) > this._removeDevicesTimeMs) {
257
+ delete this._devicesState[deviceKey];
258
+ }
259
+ });
260
+ }
261
+
262
+ // Process the callback
263
+ this.processStateCallback();
264
+ }
265
+
266
+ ////////////////////////////////////////////////////////////////////////////
267
+ // Process state change callback
268
+ ////////////////////////////////////////////////////////////////////////////
269
+
270
+ private processStateCallback() {
271
+
272
+ // Iterate over the devices
273
+ Object.entries(this._devicesState).forEach(([deviceKey, deviceState]) => {
274
+
275
+ // Check if device record is new
276
+ if (deviceState.deviceIsNew) {
277
+ if (this._callbackNewDevice) {
278
+ this._callbackNewDevice(
279
+ deviceKey,
280
+ deviceState
281
+ );
282
+ }
283
+ deviceState.deviceIsNew = false;
284
+ }
285
+
286
+ // Iterate over the attributes
287
+ Object.entries(deviceState.deviceAttributes).forEach(([_attrKey, attrState]) => {
288
+ if (attrState.newAttribute) {
289
+ if (this._callbackNewDeviceAttribute) {
290
+ this._callbackNewDeviceAttribute(
291
+ deviceKey,
292
+ attrState
293
+ );
294
+ }
295
+ attrState.newAttribute = false;
296
+ }
297
+ if (attrState.newData) {
298
+ if (this._callbackNewAttributeData) {
299
+ this._callbackNewAttributeData(
300
+ deviceKey,
301
+ attrState
302
+ );
303
+ }
304
+ attrState.newData = false;
305
+ }
306
+ });
307
+ });
308
+ }
309
+
310
+ ////////////////////////////////////////////////////////////////////////////
311
+ // Get device type info
312
+ ////////////////////////////////////////////////////////////////////////////
313
+
314
+ private async getDeviceTypeInfo(busName: string, _devAddr: string, deviceType: string): Promise<DeviceTypeInfo | undefined> {
315
+
316
+ // Check if already in the cache
317
+ if (deviceType in this._cachedDeviceTypeRecs) {
318
+ return this._cachedDeviceTypeRecs[deviceType];
319
+ }
320
+
321
+ // Check if we have tried to get device info previously (and failed presumably since it isn't in the cache)
322
+ if (deviceType in this._cachedDeviceTypePreviousAttemptTimes) {
323
+ // Check if we should retry
324
+ if ((Date.now() - this._cachedDeviceTypePreviousAttemptTimes[deviceType]) < this._minTimeBetweenDeviceTypeInfoRetrievalMs) {
325
+ return undefined;
326
+ }
327
+ }
328
+ this._cachedDeviceTypePreviousAttemptTimes[deviceType] = Date.now();
329
+
330
+ // Get the device type info from the server
331
+ try {
332
+ // Form the request
333
+ const cmd = "devman/typeinfo?bus=" + busName + "&type=" + deviceType;
334
+
335
+ // Get the msg handler
336
+ const msgHandler = this._systemUtils?.getMsgHandler();
337
+ if (msgHandler) {
338
+ const msgRslt = await msgHandler.sendRICRESTURL<RaftDevTypeInfoResponse>(cmd);
339
+ if (msgRslt.rslt === "ok") {
340
+ this._cachedDeviceTypeRecs[deviceType] = msgRslt.devinfo;
341
+ return msgRslt.devinfo
342
+ }
343
+ }
344
+ return undefined;
345
+ } catch (error) {
346
+ console.error(`DeviceManager getDeviceTypeInfo error ${error}`);
347
+ return undefined;
348
+ }
349
+ }
350
+
351
+ ////////////////////////////////////////////////////////////////////////////
352
+ // Send action to device
353
+ ////////////////////////////////////////////////////////////////////////////
354
+
355
+ public async sendAction(deviceKey: string, action: DeviceTypeAction, data: DataType[]): Promise<boolean> {
356
+ // console.log(`DeviceManager sendAction ${deviceKey} action name ${action.n} value ${value} prefix ${action.w}`);
357
+
358
+ // Form the write bytes
359
+ let writeBytes = action.t ? struct.pack(action.t, data) : Buffer.from([]);
360
+
361
+ // Convert to hex string
362
+ let writeHexStr = Buffer.from(writeBytes).toString('hex');
363
+
364
+ // Add prefix
365
+ writeHexStr = action.w + writeHexStr;
366
+
367
+ // Separate the bus and address in the deviceKey (_ char)
368
+ const devBus = deviceKey.split("_")[0]
369
+ const devAddr = deviceKey.split("_")[1]
370
+
371
+ // Send the action to the server
372
+ const cmd = "devman/cmdraw?bus=" + devBus + "&addr=" + devAddr + "&hexWr=" + writeHexStr;
373
+
374
+ console.log(`DeviceManager deviceKey ${deviceKey} action name ${action.n} value ${data} prefix ${action.w} sendAction ${cmd}`);
375
+
376
+ // Send the command
377
+ try {
378
+
379
+ // Get the msg handler
380
+ const msgHandler = this._systemUtils?.getMsgHandler();
381
+ if (msgHandler) {
382
+ const msgRslt = await msgHandler.sendRICRESTURL<RaftOKFail>(cmd);
383
+ return msgRslt.isOk();
384
+ }
385
+ return false;
386
+ } catch (error) {
387
+ console.warn(`DeviceManager sendAction error ${error}`);
388
+ return false;
389
+ }
390
+ }
391
+
392
+ ////////////////////////////////////////////////////////////////////////////
393
+ // Send a compound action to the device
394
+ ////////////////////////////////////////////////////////////////////////////
395
+
396
+ public async sendCompoundAction(deviceKey: string, action: DeviceTypeAction, data: DataType[][]): Promise<boolean> {
397
+ // console.log(`DeviceManager sendAction ${deviceKey} action name ${action.n} value ${value} prefix ${action.w}`);
398
+
399
+ // Check if all data to be sent at once
400
+ if (action.concat) {
401
+ // Form a single list by flattening data
402
+ let dataToWrite: DataType[] = [];
403
+ for (let dataIdx = 0; dataIdx < data.length; dataIdx++) {
404
+ dataToWrite = dataToWrite.concat(data[dataIdx]);
405
+ }
406
+
407
+ // Use sendAction to send this
408
+ return await this.sendAction(deviceKey, action, dataToWrite);
409
+ } else {
410
+ // Iterate over the data
411
+ let allOk = true;
412
+ for (let dataIdx = 0; dataIdx < data.length; dataIdx++) {
413
+
414
+ // Create the data to write by prepending the index to the data for this index
415
+ let dataToWrite = [dataIdx as DataType].concat(data[dataIdx]);
416
+
417
+ // Use sendAction to send this
418
+ allOk = allOk && await this.sendAction(deviceKey, action, dataToWrite);
419
+ }
420
+ }
421
+ return false;
422
+ }
423
+
424
+ ////////////////////////////////////////////////////////////////////////////
425
+ // Convert hex to bytes
426
+ ////////////////////////////////////////////////////////////////////////////
427
+
428
+ private hexToBytes(hex: string): Uint8Array {
429
+ const bytes = new Uint8Array(hex.length / 2);
430
+ for (let i = 0; i < bytes.length; i++) {
431
+ bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
432
+ }
433
+ return bytes;
434
+ }
435
+
436
+ }
@@ -0,0 +1,28 @@
1
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2
+ //
3
+ // RaftDeviceMgrIF.ts
4
+ // Interface to devices state
5
+ //
6
+ // Rob Dobson (C) 2024
7
+ //
8
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
9
+
10
+ import { DataType } from "python-struct";
11
+ import { DeviceTypeAction } from "./RaftDeviceInfo";
12
+ import { DeviceAttributeState, DevicesState, DeviceState } from "./RaftDeviceStates";
13
+
14
+ export default interface RaftDeviceMgrIF {
15
+
16
+ // Get state of devices
17
+ getDevicesState(): DevicesState;
18
+ getDeviceState(deviceKey: string): DeviceState;
19
+
20
+ // Callbacks
21
+ onNewDevice(callback: (deviceKey: string, state: DeviceState) => void): void;
22
+ onNewDeviceAttribute(callback: (deviceKey: string, attrState: DeviceAttributeState) => void): void;
23
+ onNewAttributeData(callback: (deviceKey: string, attrState: DeviceAttributeState) => void): void;
24
+
25
+ // Send action to device
26
+ sendAction(deviceKey: string, action: DeviceTypeAction, data: DataType[]): void;
27
+ sendCompoundAction(deviceKey: string, action: DeviceTypeAction, data: DataType[][]): void;
28
+ }
@@ -0,0 +1,20 @@
1
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2
+ //
3
+ // RaftDeviceMsg
4
+ // Device message for Raft devices
5
+ //
6
+ // Rob Dobson (C) 2024
7
+ //
8
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
9
+
10
+ export interface DeviceMsgJsonElem {
11
+ [attrGroupName: string]: string | number | boolean; // Attribute group name and value
12
+ }
13
+
14
+ export interface DeviceMsgJsonBus {
15
+ [devAddr: string]: DeviceMsgJsonElem;
16
+ }
17
+
18
+ export interface DeviceMsgJson {
19
+ [busName: string]: DeviceMsgJsonBus;
20
+ }