@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,139 @@
1
+ import React, { useEffect, useState, memo, useRef } from "react";
2
+ import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, ArcElement, Tooltip, Legend } from 'chart.js';
3
+ import { Line } from "react-chartjs-2";
4
+ import ConnManager from "./ConnManager";
5
+ import { DeviceState } from "../../../src/RaftDeviceStates";
6
+
7
+ const connManager = ConnManager.getInstance();
8
+
9
+ ChartJS.register(
10
+ CategoryScale,
11
+ LinearScale,
12
+ PointElement,
13
+ LineElement,
14
+ ArcElement,
15
+ Tooltip,
16
+ Legend
17
+ );
18
+
19
+ export interface DeviceLineChartProps {
20
+ deviceKey: string;
21
+ lastUpdated: number;
22
+ }
23
+
24
+ interface ChartJSData {
25
+ labels: string[];
26
+ datasets: {
27
+ label: string;
28
+ data: number[];
29
+ fill: boolean;
30
+ borderColor: string;
31
+ backgroundColor: string;
32
+ yAxisID: string;
33
+ }[];
34
+ }
35
+
36
+ const DeviceLineChart: React.FC<DeviceLineChartProps> = memo(({ deviceKey, lastUpdated }) => {
37
+
38
+ const deviceManager = connManager.getConnector().getSystemType()?.deviceMgrIF;
39
+ const deviceState: DeviceState | undefined = deviceManager?.getDeviceState(deviceKey);
40
+ // const { deviceAttributes, deviceTimeline } = deviceState;
41
+ const [chartData, setChartData] = useState<ChartJSData>({
42
+ labels: [],
43
+ datasets: []
44
+ });
45
+
46
+ const options = {
47
+ responsive: true,
48
+ maintainAspectRatio: false,
49
+ animation: {
50
+ duration: 1, // default is 1000ms
51
+ },
52
+ scales: {}
53
+ };
54
+
55
+ const colourMapRef = useRef<{ [key: string]: string }>({
56
+ prox: "hsl(60, 70%, 60%)",
57
+ als: "hsl(0, 70%, 60%)",
58
+ white: "hsl(120, 70%, 60%)",
59
+ x: "hsl(240, 70%, 60%)",
60
+ y: "hsl(300, 70%, 60%)",
61
+ z: "hsl(0, 70%, 60%)",
62
+ dist: "hsl(60, 70%, 60%)",
63
+ temperature: "hsl(360, 70%, 60%)",
64
+ humidity: "hsl(200, 70%, 60%)",
65
+ Red: "hsl(0, 70%, 60%)",
66
+ Green: "hsl(120, 70%, 60%)",
67
+ Blue: "hsl(240, 70%, 60%)",
68
+ IR: "hsl(300, 70%, 60%)",
69
+ });
70
+
71
+ useEffect(() => {
72
+ if (!deviceState)
73
+ return;
74
+ const maxChartDataPoints = 50;
75
+ const labels = deviceState.deviceTimeline.timestampsUs.slice(maxChartDataPoints).map(time => {
76
+ const seconds = time / 1e6; // Convert microseconds to seconds
77
+ const secondsStr = seconds.toFixed(3); // Format decimal places
78
+ return secondsStr;
79
+ });
80
+
81
+ const uniqueAxes = new Map<string, { range: [number, number], units: string }>();
82
+ const datasets = Object.entries(deviceState.deviceAttributes)
83
+ .filter(([attributeName, attributeDetails]) => attributeDetails.visibleSeries !== false)
84
+ .map(([attributeName, attributeDetails]) => {
85
+ const data = attributeDetails.values.slice(maxChartDataPoints);
86
+ let colour = colourMapRef.current[attributeName];
87
+ if (!colour) {
88
+ colour = `hsl(${Math.random() * 360}, 70%, 60%)`;
89
+ colourMapRef.current[attributeName] = colour;
90
+ }
91
+ let rangeEnds: [number,number] = [Math.min(...attributeDetails.range), Math.max(...attributeDetails.range)];
92
+ const axisKey = `${rangeEnds[0]}-${rangeEnds[1]}-${attributeDetails.units}`;
93
+ if (!uniqueAxes.has(axisKey)) {
94
+ uniqueAxes.set(axisKey, { range: rangeEnds, units: attributeDetails.units });
95
+ }
96
+ return {
97
+ label: attributeName,
98
+ data: data,
99
+ fill: false,
100
+ borderColor: colour,
101
+ backgroundColor: colour,
102
+ yAxisID: axisKey
103
+ };
104
+ });
105
+
106
+ const scales: { [key: string]: any } = {};
107
+ uniqueAxes.forEach((axis, key) => {
108
+ scales[key] = {
109
+ type: 'linear',
110
+ display: true,
111
+ position: 'left',
112
+ scaleLabel: {
113
+ display: true,
114
+ labelString: axis.units
115
+ },
116
+ ticks: {
117
+ min: axis.range[0],
118
+ max: axis.range[1]
119
+ }
120
+ };
121
+ }
122
+ );
123
+ options.scales = scales;
124
+ setChartData({ labels, datasets });
125
+ }
126
+ , [lastUpdated]);
127
+
128
+ if (!deviceState || Object.keys(deviceState.deviceAttributes).length === 0) {
129
+ return <></>;
130
+ }
131
+
132
+ return (
133
+ <div className="device-line-chart">
134
+ <Line data={chartData} options={options} />
135
+ </div>
136
+ );
137
+ });
138
+
139
+ export default DeviceLineChart;
@@ -0,0 +1,135 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import './styles.css';
3
+ import { DeviceState } from '../../../src/RaftDeviceStates';
4
+ import DeviceAttrsForm from './DeviceAttrsForm';
5
+ import DeviceActionsForm from './DeviceActionsForm';
6
+ import DeviceLineChart from './DeviceLineChart';
7
+ import ConnManager from './ConnManager';
8
+
9
+ const connManager = ConnManager.getInstance();
10
+
11
+ export interface DevicePanelProps {
12
+ deviceKey: string;
13
+ lastUpdated: number;
14
+ }
15
+
16
+ const DevicePanel = ({ deviceKey, lastUpdated }: DevicePanelProps) => {
17
+ const deviceManager = connManager.getConnector().getSystemType()?.deviceMgrIF;
18
+ const deviceState: DeviceState | undefined = deviceManager?.getDeviceState(deviceKey);
19
+
20
+ // Gray out the device panel if the device is offline
21
+ const offlineClass = deviceState?.isOnline ? '' : 'offline';
22
+
23
+ const [timedChartUpdate, setTimedChartUpdate] = useState<number>(0);
24
+ const [menuOpen, setMenuOpen] = useState<boolean>(false);
25
+ const menuRef = useRef<HTMLDivElement>(null);
26
+
27
+ useEffect(() => {
28
+ const startTime = Date.now();
29
+ const updateChart = () => {
30
+ setTimedChartUpdate(Date.now());
31
+ };
32
+ const updateTimer = setInterval(updateChart, 500);
33
+ return () => clearInterval(updateTimer);
34
+ }, []);
35
+
36
+ useEffect(() => {
37
+ const handleClickOutside = (event: MouseEvent) => {
38
+ if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
39
+ setMenuOpen(false);
40
+ }
41
+ };
42
+ document.addEventListener("mousedown", handleClickOutside);
43
+ return () => {
44
+ document.removeEventListener("mousedown", handleClickOutside);
45
+ };
46
+ }, []);
47
+
48
+ const handleCopyToClipboard = () => {
49
+ if (!deviceState) {
50
+ return;
51
+ }
52
+ const headers = ["Time (s)"];
53
+ const rows: string[][] = [];
54
+
55
+ const timestampsUs = deviceState.deviceTimeline.timestampsUs;
56
+ const attributes = deviceState.deviceAttributes;
57
+
58
+ // Collect headers and initialize rows with timestamps
59
+ Object.keys(attributes).forEach(attrName => {
60
+ headers.push(attrName);
61
+ });
62
+
63
+ timestampsUs.forEach((timestampUs, index) => {
64
+ const row: string[] = [(timestampUs / 1000000.0).toString()];
65
+ Object.keys(attributes).forEach(attrName => {
66
+ const values = attributes[attrName].values;
67
+ row.push(values[index]?.toString() || "");
68
+ });
69
+ rows.push(row);
70
+ });
71
+
72
+ // Create a tab-separated string
73
+ const csvContent = [headers.join("\t"), ...rows.map(row => row.join("\t"))].join("\n");
74
+
75
+ // Try using navigator.clipboard.writeText, with a fallback to document.execCommand
76
+ if (navigator.clipboard) {
77
+ navigator.clipboard.writeText(csvContent).then(() => {
78
+ console.log("Device values copied to clipboard");
79
+ }).catch(err => {
80
+ console.error('Failed to copy: ', err);
81
+ fallbackCopyTextToClipboard(csvContent);
82
+ });
83
+ } else {
84
+ fallbackCopyTextToClipboard(csvContent);
85
+ }
86
+ setMenuOpen(false);
87
+ };
88
+
89
+ const fallbackCopyTextToClipboard = (text: string) => {
90
+ const textArea = document.createElement("textarea");
91
+ textArea.value = text;
92
+
93
+ // Avoid scrolling to bottom
94
+ textArea.style.top = "0";
95
+ textArea.style.left = "0";
96
+ textArea.style.position = "fixed";
97
+
98
+ document.body.appendChild(textArea);
99
+ textArea.focus();
100
+ textArea.select();
101
+
102
+ try {
103
+ document.execCommand("copy");
104
+ // alert("Device values copied to clipboard!");
105
+ } catch (err) {
106
+ console.error('Fallback: Oops, unable to copy', err);
107
+ alert("Failed to copy device values to clipboard");
108
+ }
109
+
110
+ document.body.removeChild(textArea);
111
+ };
112
+
113
+ return (
114
+ <div className={`device-panel ${offlineClass}`}>
115
+ <div className="device-block-heading">
116
+ <div className="device-block-heading-text">Device {deviceState?.deviceTypeInfo?.name} Address {deviceKey}{!deviceState?.isOnline ? " (Offline)" : ""}</div>
117
+ <div className="menu-icon always-enabled" onClick={() => setMenuOpen(!menuOpen)}>☰</div>
118
+ {menuOpen && (
119
+ <div className="dropdown-menu" ref={menuRef}>
120
+ <div className="menu-item always-enabled" onClick={handleCopyToClipboard}>Copy Values to Clipboard</div>
121
+ </div>
122
+ )}
123
+ </div>
124
+ <div className={`device-block-data`}>
125
+ <div className="device-attrs-and-actions">
126
+ <DeviceAttrsForm deviceKey={deviceKey} lastUpdated={lastUpdated} />
127
+ <DeviceActionsForm deviceKey={deviceKey} />
128
+ </div>
129
+ <DeviceLineChart deviceKey={deviceKey} lastUpdated={timedChartUpdate} />
130
+ </div>
131
+ </div>
132
+ );
133
+ };
134
+
135
+ export default DevicePanel;
@@ -0,0 +1,57 @@
1
+ // Component which uses the DeviceList component to display the list of devices
2
+
3
+ import React, { useEffect, useState } from 'react';
4
+ // import { DeviceAttributeState, DevicesState, DeviceState } from "../../../src/main";
5
+ // import { DeviceManager } from './DeviceManager';
6
+ // import DeviceScreen from './DeviceScreen';
7
+ import './styles.css';
8
+ import ConnManager from "./ConnManager";
9
+ import { DeviceAttributeState, DevicesState, DeviceState } from '../../../src/RaftDeviceStates';
10
+ import DevicePanel from './DevicePanel';
11
+
12
+ const connManager = ConnManager.getInstance();
13
+
14
+ export class DevicesPanelProps {
15
+ constructor(
16
+ ) { }
17
+ }
18
+
19
+ export default function DevicesPanel(props: DevicesPanelProps) {
20
+ const [lastUpdated, setLastUpdated] = useState<number>(0);
21
+
22
+ useEffect(() => {
23
+ const deviceManager = connManager.getConnector().getSystemType()?.deviceMgrIF;
24
+ if (!deviceManager) {
25
+ return;
26
+ }
27
+
28
+ const onNewDevice = (deviceKey: string, newDeviceState: DeviceState) => {
29
+ setLastUpdated(Date.now());
30
+ };
31
+ deviceManager.onNewDevice(onNewDevice);
32
+
33
+ const onNewAttribute = (deviceKey: string, attribute: DeviceAttributeState) => {
34
+ setLastUpdated(Date.now());
35
+ }
36
+ deviceManager.onNewDeviceAttribute(onNewAttribute);
37
+
38
+ const onNewAttributeData = (deviceKey: string, attribute: DeviceAttributeState) => {
39
+ setLastUpdated(Date.now());
40
+ }
41
+ deviceManager.onNewAttributeData(onNewAttributeData);
42
+
43
+ }, [lastUpdated]);
44
+
45
+ const deviceManager = connManager.getConnector().getSystemType()?.deviceMgrIF;
46
+ let devicesState: DevicesState = {};
47
+ if (deviceManager)
48
+ devicesState = deviceManager.getDevicesState();
49
+
50
+ return (
51
+ <div className="devices-container">
52
+ {Object.entries(devicesState).filter(([key, _]) => key !== 'getDeviceKey').map(([deviceKey, data]) => (
53
+ <DevicePanel key={deviceKey} deviceKey={deviceKey} lastUpdated={lastUpdated} />
54
+ ))}
55
+ </div>
56
+ );
57
+ }
@@ -0,0 +1,110 @@
1
+ import React, { useState } from 'react';
2
+ import DispOneLED from './DispOneLed';
3
+ import ConnManager from './ConnManager';
4
+ import { DeviceTypeAction } from '../../../src/RaftDeviceInfo';
5
+ import { CirclePicker } from 'react-color';
6
+
7
+ const connManager = ConnManager.getInstance();
8
+
9
+ interface LEDGridProps {
10
+ rows: number;
11
+ cols: number;
12
+ deviceKey: string;
13
+ deviceAction: DeviceTypeAction;
14
+ }
15
+
16
+ const customColors = [
17
+ '#000000', // black
18
+ '#FFFFFF', // white
19
+ '#FF0000', // red
20
+ '#00FF00', // green
21
+ '#0000FF', // blue
22
+ '#FFFF00', // yellow
23
+ '#FF00FF', // pink
24
+ '#00FFFF', // cyan
25
+ '#FFA500', // orange
26
+ '#800080', // purple
27
+ '#808080', // gray
28
+ '#A52A2A', // brown
29
+ '#008000', // dark green
30
+ '#800000', // maroon
31
+ '#008080', // teal
32
+ '#000080', // navy
33
+ '#FFD700', // gold
34
+ '#FF4500', // orange red
35
+ '#FF6347', // tomato
36
+ // Add more custom colors as needed
37
+ ];
38
+
39
+ const DispLEDGrid: React.FC<LEDGridProps> = ({ rows, cols, deviceKey, deviceAction }) => {
40
+ // Initialize the grid with all LEDs turned off (black)
41
+ const [colors, setColors] = useState<string[][]>(
42
+ Array.from({ length: rows }, () => Array.from({ length: cols }, () => '#000000'))
43
+ );
44
+ const [activeLED, setActiveLED] = useState<{row: number, col: number} | null>(null);
45
+
46
+ const handleLEDClick = (row: number, col: number) => {
47
+ setActiveLED({ row, col });
48
+ };
49
+
50
+ const hexToRgb = (hex: string): number[] => {
51
+ const r = parseInt(hex.slice(1, 3), 16);
52
+ const g = parseInt(hex.slice(3, 5), 16);
53
+ const b = parseInt(hex.slice(5, 7), 16);
54
+ return [r, g, b];
55
+ };
56
+
57
+ const handleChangeComplete = (color: any) => {
58
+ if (activeLED) {
59
+ const { row, col } = activeLED;
60
+ const newColors = colors.map((rowColors, rowIndex) =>
61
+ rowColors.map((colColor, colIndex) =>
62
+ rowIndex === row && colIndex === col ? color.hex : colColor
63
+ )
64
+ );
65
+ setColors(newColors);
66
+ setActiveLED(null); // Optionally close the picker automatically
67
+
68
+ // Convert into a list of RGB values
69
+ let colourList = [];
70
+ for (let i = 0; i < rows; i++) {
71
+ for (let j = 0; j < cols; j++) {
72
+ colourList.push(hexToRgb(newColors[i][j]));
73
+ }
74
+ }
75
+
76
+ // Send result
77
+ const deviceManager = connManager.getConnector().getSystemType()?.deviceMgrIF;
78
+ if (deviceManager) {
79
+ deviceManager.sendCompoundAction(deviceKey, deviceAction, colourList);
80
+ }
81
+ }
82
+ };
83
+
84
+ return (
85
+ <div>
86
+ <div style={{
87
+ display: 'grid',
88
+ gridTemplateColumns: `repeat(${cols}, 24px)`,
89
+ gridTemplateRows: `${rows === 1 ? '1fr' : `repeat(${rows}, 24px)`}`,
90
+ justifyContent: 'center',
91
+ width: `${cols * 24}px`
92
+ }}>
93
+ {colors.map((row, rowIndex) =>
94
+ row.map((color, colIndex) => (
95
+ <DispOneLED key={`${rowIndex}-${colIndex}`} color={color} onClick={() => handleLEDClick(rowIndex, colIndex)} />
96
+ ))
97
+ )}
98
+ </div>
99
+ {activeLED && (
100
+ <CirclePicker
101
+ color={colors[activeLED.row][activeLED.col]}
102
+ colors={customColors}
103
+ onChangeComplete={handleChangeComplete}
104
+ />
105
+ )}
106
+ </div>
107
+ );
108
+ };
109
+
110
+ export default DispLEDGrid;
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+
3
+ interface LEDProps {
4
+ color: string;
5
+ onClick: () => void;
6
+ }
7
+
8
+ const DispOneLED: React.FC<LEDProps> = ({ color, onClick }) => {
9
+ return (
10
+ <div style={{
11
+ width: '20px',
12
+ height: '20px',
13
+ backgroundColor: color,
14
+ margin: '2px',
15
+ cursor: 'pointer'
16
+ }} onClick={onClick} />
17
+ );
18
+ };
19
+
20
+ export default DispOneLED;
@@ -0,0 +1,106 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import './styles.css';
3
+ import ConnManager from './ConnManager';
4
+ import { RaftConnEvent, RaftUpdateEvent, RaftPublishEvent } from "../../../src/main";
5
+ import StatusPanel from './StatusPanel';
6
+ import DevicesPanel from './DevicesPanel';
7
+ import CommandPanel from './CommandPanel';
8
+
9
+ const connManager = ConnManager.getInstance();
10
+
11
+ export default function Main() {
12
+ const [connectionStatus, setConnectionStatus] = useState<RaftConnEvent>(RaftConnEvent.CONN_DISCONNECTED);
13
+
14
+ useEffect(() => {
15
+ // Define the listener for raft events
16
+ const listener = (eventType: string,
17
+ eventEnum: RaftConnEvent | RaftUpdateEvent | RaftPublishEvent,
18
+ eventName: string,
19
+ data?: object | string | null) => {
20
+ console.log(`Connection event: ${eventName}`);
21
+ if (eventType === "conn") {
22
+ if ((eventEnum === RaftConnEvent.CONN_CONNECTED) || (eventEnum === RaftConnEvent.CONN_DISCONNECTED)) {
23
+ setConnectionStatus(eventEnum);
24
+ }
25
+ }
26
+ };
27
+
28
+ // Set the listener function
29
+ connManager.setConnectionEventListener(listener);
30
+
31
+ // Clean up the listener when the component unmounts
32
+ return () => {
33
+ connManager.setConnectionEventListener(() => { });
34
+ };
35
+ }, []);
36
+
37
+ return (
38
+ <div className="content-outer">
39
+ <div className="header">
40
+ <h1>RaftJS Dashboard</h1>
41
+ </div>
42
+ <div className="content-body">
43
+ {/* Div optionally shown if connected */}
44
+ {connectionStatus === RaftConnEvent.CONN_CONNECTED ?
45
+ <>
46
+ <div className="connected-panel">
47
+ <div className="info-boxes">
48
+ <div className="info-box">
49
+ <div className="conn-indication">
50
+ <h3>Connected</h3>
51
+ </div>
52
+ <div>
53
+ <button className="action-button" onClick={() => connManager.disconnect()}>Disconnect</button>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ <StatusPanel />
58
+ <CommandPanel />
59
+ </div>
60
+ <DevicesPanel />
61
+ </>
62
+ :
63
+ <>
64
+ <div className="info-boxes">
65
+ <div className="info-box">
66
+ <h3>WebSocket</h3>
67
+ <input className="ip-addr-input" id="ip-addr" type="text" placeholder="IP Address" />
68
+ <button className="action-button" onClick={() => {
69
+ // Get IP address
70
+ const ipAddrElem = document.getElementById("ip-addr") as HTMLInputElement;
71
+ if (ipAddrElem) {
72
+ const ipAddr = ipAddrElem.value;
73
+ connManager.connect("WebSocket", ipAddr, "");
74
+ } else {
75
+ console.error("No IP address entered");
76
+ }
77
+ }
78
+ }>
79
+ Connect
80
+ </button>
81
+ </div>
82
+ <div className="info-box">
83
+ <h3>WebBLE</h3>
84
+ <button className="action-button" onClick={() => {
85
+ connManager.connect("WebBLE", "", "");
86
+ }
87
+ }>
88
+ Connect
89
+ </button>
90
+ </div>
91
+ <div className="info-box">
92
+ <h3>WebSerial</h3>
93
+ <button className="action-button" onClick={() => {
94
+ connManager.connect("WebSerial", "", "");
95
+ }
96
+ }>
97
+ Connect
98
+ </button>
99
+ </div>
100
+ </div>
101
+ </>
102
+ }
103
+ </div>
104
+ </div>
105
+ );
106
+ }
@@ -0,0 +1,71 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import './styles.css';
3
+ import { RaftSystemInfo } from "../../../src/main";
4
+ import ConnManager from "./ConnManager";
5
+
6
+ const connManager = ConnManager.getInstance();
7
+
8
+ export default function StatusPanel() {
9
+ const [systemInfo, setSystemInfo] = useState<RaftSystemInfo>(new RaftSystemInfo());
10
+
11
+ // Use useEffect to fetch system info when the component mounts
12
+ useEffect(() => {
13
+ if (connManager.isConnected()) {
14
+ connManager.getConnector().getRaftSystemUtils().getSystemInfo().then((sysInfo: RaftSystemInfo) => {
15
+ console.log(`System Info: ${JSON.stringify(sysInfo)}`);
16
+ setSystemInfo(sysInfo);
17
+ });
18
+ }
19
+ }, []); // Empty dependency == on component mount
20
+
21
+ return (
22
+ <div className="info-boxes">
23
+
24
+ <div className="info-box">
25
+ <h3>SysInfo</h3>
26
+ {
27
+ (systemInfo !== undefined) && (systemInfo.validMs) && (systemInfo.validMs > 0) ?
28
+ <div className="info">
29
+ <div className="info-line">
30
+ <div className="info-label">System Name:</div>
31
+ <div className="info-value">{systemInfo.SystemName}</div>
32
+ </div>
33
+
34
+ <div className="info-line">
35
+ <div className="info-label">System Version:</div>
36
+ <div className="info-value">{systemInfo.SystemVersion}</div>
37
+ </div>
38
+
39
+ <div className="info-line">
40
+ <div className="info-label">HwRev:</div>
41
+ <div className="info-value">{systemInfo.HwRev}</div>
42
+ </div>
43
+
44
+ <div className="info-line">
45
+ <div className="info-label">MAC:</div>
46
+ <div className="info-value">{systemInfo.MAC}</div>
47
+ </div>
48
+
49
+ <div className="info-line">
50
+ <div className="info-label">SerialNo:</div>
51
+ <div className="info-value">{systemInfo.SerialNo}</div>
52
+ </div>
53
+
54
+ <div className="info-line">
55
+ <div className="info-label">Friendly:</div>
56
+ <div className="info-value">{systemInfo.Friendly}</div>
57
+ </div>
58
+ </div>
59
+ :
60
+ <div className="info">
61
+ <div className="info-line">
62
+ <div className="info-label">System Info:</div>
63
+ <div className="info-value">Not available</div>
64
+ </div>
65
+ </div>
66
+ }
67
+ </div>
68
+ </div>
69
+ );
70
+
71
+ }