@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,409 @@
1
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2
+ //
3
+ // RaftChannelWebSerial
4
+ // Part of RaftJS
5
+ //
6
+ // Rob Dobson & Chris Greening 2020-2024
7
+ // (C) 2020-2024 All rights reserved
8
+ //
9
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
10
+
11
+ import RaftChannel from "./RaftChannel";
12
+ import RaftMsgHandler from "./RaftMsgHandler";
13
+ import RaftLog from "./RaftLog";
14
+ import { RaftConnEvent, RaftConnEventFn } from "./RaftConnEvents";
15
+ import { ConnectorOptions } from "./RaftSystemType";
16
+ import { TextDecoder } from 'text-encoding';
17
+
18
+ type TWebParityType = 'none' | 'even' | 'odd';
19
+ type TWebFlowControlType = 'none' | 'hardware';
20
+
21
+ interface TWebSerialOptions {
22
+ baudRate: number;
23
+ dataBits?: number | undefined;
24
+ stopBits?: number | undefined;
25
+ parity?: TWebParityType | undefined;
26
+ bufferSize?: number | undefined;
27
+ flowControl?: TWebFlowControlType | undefined;
28
+ }
29
+
30
+ declare class TWebSerialPort extends EventTarget {
31
+ readonly readable: ReadableStream<Uint8Array> | null;
32
+ readonly writable: WritableStream<Uint8Array> | null;
33
+
34
+ open(options: TWebSerialOptions): Promise<void>;
35
+ close(): Promise<void>;
36
+ forget(): Promise<void>;
37
+
38
+ addEventListener(
39
+ type: 'connect' | 'disconnect',
40
+ listener: (this: this, ev: Event) => any,
41
+ useCapture?: boolean): void;
42
+ addEventListener(
43
+ type: string,
44
+ listener: EventListenerOrEventListenerObject | null,
45
+ options?: boolean | AddEventListenerOptions): void;
46
+ removeEventListener(
47
+ type: 'connect' | 'disconnect',
48
+ callback: (this: this, ev: Event) => any,
49
+ useCapture?: boolean): void;
50
+ removeEventListener(
51
+ type: string,
52
+ callback: EventListenerOrEventListenerObject | null,
53
+ options?: EventListenerOptions | boolean): void;
54
+ }
55
+
56
+ interface TWebSerialPortFilter {
57
+ usbVendorId?: number | undefined;
58
+ usbProductId?: number | undefined;
59
+ }
60
+
61
+ interface TWebSerialPortRequestOptions {
62
+ filters?: TWebSerialPortFilter[] | undefined;
63
+ }
64
+
65
+ declare class TWebSerial extends EventTarget {
66
+ requestPort(options?: TWebSerialPortRequestOptions): Promise<TWebSerialPort>;
67
+ }
68
+
69
+ interface NavigatorWithSerial {
70
+ readonly serial: TWebSerial;
71
+ }
72
+
73
+ export default class RaftChannelWebSerial implements RaftChannel {
74
+
75
+ // Message handler
76
+ private _raftMsgHandler: RaftMsgHandler | null = null;
77
+
78
+ private _port: TWebSerialPort | null = null;
79
+ private _reader?: ReadableStreamDefaultReader<Uint8Array>;
80
+
81
+ // Last message tx time
82
+ // private _msgTxTimeLast = Date.now();
83
+ // private _msgTxMinTimeBetweenMs = 15;
84
+
85
+ // Is connected
86
+ private _isConnected = false;
87
+ private _connPaused = false;
88
+
89
+ private _serialBuffer: number[] = [];
90
+
91
+ private _escapeSeqCode = 0;
92
+ private _OVERASCII_ESCAPE_1 = 0x85;
93
+ private _OVERASCII_ESCAPE_2 = 0x8E;
94
+ private _OVERASCII_ESCAPE_3 = 0x8F;
95
+ private _OVERASCII_MOD_CODE = 0x20;
96
+
97
+ // Conn event fn
98
+ private _onConnEvent: RaftConnEventFn | null = null;
99
+
100
+ // File Handler parameters
101
+ private _requestedBatchAckSize = 1;
102
+ private _requestedFileBlockSize = 1200;
103
+
104
+ fhBatchAckSize(): number { return this._requestedBatchAckSize; }
105
+ fhFileBlockSize(): number { return this._requestedFileBlockSize; }
106
+
107
+ // isConnected
108
+ isConnected(): boolean {
109
+ return this._isConnected;
110
+ }
111
+
112
+ // Set message handler
113
+ setMsgHandler(raftMsgHandler: RaftMsgHandler): void {
114
+ this._raftMsgHandler = raftMsgHandler;
115
+ }
116
+
117
+ // Serial interface will require subscription, but don't start it by default
118
+ requiresSubscription(): boolean {
119
+ return false;
120
+ }
121
+
122
+ // RICREST command before disconnect
123
+ ricRestCmdBeforeDisconnect(): string | null {
124
+ return null;
125
+ }
126
+
127
+ // Set onConnEvent handler
128
+ setOnConnEvent(connEventFn: RaftConnEventFn): void {
129
+ this._onConnEvent = connEventFn;
130
+ }
131
+
132
+ // Get connected locator
133
+ getConnectedLocator(): string | object {
134
+ return this._port || "";
135
+ }
136
+
137
+ // Connect to a device
138
+ async connect(locator: string | object, _connectorOptions: ConnectorOptions): Promise<boolean> {
139
+ console.log("_connectorOptions", _connectorOptions);
140
+ // Debug
141
+ RaftLog.debug("RaftChannelWebSerial.connect " + locator.toString());
142
+
143
+ // Check already connected
144
+ if (await this.isConnected()) {
145
+ return true;
146
+ }
147
+
148
+ try {
149
+ if (('serial' in navigator) && (!this._port || locator != "reusePort")) {
150
+ const port = await (navigator as NavigatorWithSerial).serial.requestPort();
151
+ this._port = port;
152
+ }
153
+ // Connect
154
+ if (!this._port)
155
+ return false;
156
+
157
+ try {
158
+ RaftLog.info("opening port");
159
+ await this._port.open({ baudRate: 115200 });
160
+ } catch (err: any) {
161
+ if (err.name == "InvalidStateError") {
162
+ RaftLog.debug(`Opening port failed - already open ${err}`);
163
+ } else {
164
+ RaftLog.error(`Opening port failed: ${err}`);
165
+ throw err;
166
+ }
167
+ }
168
+
169
+ this._isConnected = true;
170
+
171
+ // start read loop
172
+ this._readLoop();
173
+
174
+ this._port.addEventListener('disconnect', (event) => {
175
+ RaftLog.debug("WebSerial disconnect " + JSON.stringify(event));
176
+ if (this._onConnEvent)
177
+ this._onConnEvent(RaftConnEvent.CONN_DISCONNECTED);
178
+ });
179
+ // TODO: handle errors
180
+ } catch (err) {
181
+ RaftLog.error("RaftChannelWebSerial.connect fail. Error: " + JSON.stringify(err));
182
+ return false;
183
+ }
184
+
185
+ return true;
186
+ }
187
+
188
+ // Disconnect
189
+ async disconnect(): Promise<void> {
190
+
191
+ // Not connected
192
+ this._isConnected = false;
193
+
194
+ RaftLog.debug(`RaftChannelWebSerial.disconnect attempting to close webserial`);
195
+
196
+ while (this._reader) {
197
+ await new Promise((resolve) => setTimeout(resolve, 100));
198
+ }
199
+
200
+ // Disconnect webserial
201
+ try {
202
+ if (this._port)
203
+ await this._port.close();
204
+ } catch (err) {
205
+ console.debug(`Error closing port ${err}`);
206
+ }
207
+ if (this._onConnEvent)
208
+ this._onConnEvent(RaftConnEvent.CONN_DISCONNECTED);
209
+
210
+ RaftLog.debug("WebSerial port closed");
211
+ return;
212
+ }
213
+
214
+ pauseConnection(pause: boolean): void {
215
+ this._connPaused = pause;
216
+ }
217
+
218
+ private _overasciiDecodeByte(ch: number) {
219
+ // Check if in escape sequence
220
+ if (this._escapeSeqCode != 0) {
221
+ const prevEscCode = this._escapeSeqCode;
222
+ this._escapeSeqCode = 0;
223
+ if (prevEscCode == 1) {
224
+ return (ch ^ this._OVERASCII_MOD_CODE) & 0x7f;
225
+ } else if (prevEscCode == 2) {
226
+ return ch ^ this._OVERASCII_MOD_CODE;
227
+ }
228
+ return ch;
229
+ } else if (ch == this._OVERASCII_ESCAPE_1) {
230
+ this._escapeSeqCode = 1;
231
+ return -1;
232
+ } else if (ch == this._OVERASCII_ESCAPE_2) {
233
+ this._escapeSeqCode = 2;
234
+ return -1;
235
+ } else if (ch == this._OVERASCII_ESCAPE_3) {
236
+ this._escapeSeqCode = 3;
237
+ return -1;
238
+ } else {
239
+ return ch & 0x7f;
240
+ }
241
+ }
242
+
243
+ private _overasciiEncode(inData: Uint8Array) {
244
+ /*
245
+ Encode frame
246
+ Values 0x00-0x0F map to ESCAPE_CODE_1, VALUE_XOR_20H_AND_MSB_SET
247
+ Values 0x10-0x7f map to VALUE_WITH_MSB_SET
248
+ Values 0x80-0x8F map to ESCAPE_CODE_2, VALUE_XOR_20H
249
+ Values 0x90-0xff map to ESCAPE_CODE_3, VALUE
250
+ Value ESCAPE_CODE_1 maps to ESCAPE_CODE_1 + VALUE
251
+ Args:
252
+ inData: data to encode (bytes)
253
+ Returns:
254
+ encoded frame (bytes)
255
+ */
256
+ // Iterate over frame
257
+ const encodedFrame: number[] = [];
258
+ for (let i = 0; i < inData.length; i++) {
259
+ if (inData[i] <= 0x0f) {
260
+ encodedFrame.push(this._OVERASCII_ESCAPE_1);
261
+ encodedFrame.push((inData[i] ^ this._OVERASCII_MOD_CODE) | 0x80);
262
+ } else if ((inData[i] >= 0x10) && (inData[i] <= 0x7f)) {
263
+ encodedFrame.push(inData[i] | 0x80);
264
+ } else if ((inData[i] >= 0x80) && (inData[i] <= 0x8f)) {
265
+ encodedFrame.push(this._OVERASCII_ESCAPE_2);
266
+ encodedFrame.push(inData[i] ^ this._OVERASCII_MOD_CODE);
267
+ } else {
268
+ encodedFrame.push(this._OVERASCII_ESCAPE_3);
269
+ encodedFrame.push(inData[i]);
270
+ }
271
+ }
272
+ return new Uint8Array(encodedFrame);
273
+ }
274
+
275
+
276
+ // Handle notifications
277
+ _onMsgRx(msg: Uint8Array | null): void {
278
+
279
+ if (msg === null) return;
280
+ // Debug
281
+ //const decoder = new TextDecoder();
282
+ //RaftLog.debug(`RaftChannelWebSerial._onMsgRx ${decoder.decode(msg)}`);
283
+
284
+ const overasciiBuffer: number[] = [];
285
+ for (let i = 0; i < msg.length; i++) {
286
+ if (msg[i] > 127) {
287
+ const ch = this._overasciiDecodeByte(msg[i]);
288
+ if (ch != -1) {
289
+ overasciiBuffer.push(ch);
290
+ }
291
+ } else {
292
+ this._serialBuffer.push(msg[i]);
293
+ }
294
+ }
295
+ if (this._raftMsgHandler)
296
+ this._raftMsgHandler.handleNewRxMsg(new Uint8Array(overasciiBuffer));
297
+
298
+ // any output over the non overascii channel will be delimited by a new line character
299
+ // scan for this, and log any lines as they occur before removing them from the buffer
300
+ if (this._serialBuffer.includes(0x0a)) {
301
+ const decoder = new TextDecoder();
302
+ RaftLog.debug(decoder.decode(new Uint8Array(this._serialBuffer.slice(0, this._serialBuffer.indexOf(0x0a)))));
303
+ this._serialBuffer.splice(0, this._serialBuffer.indexOf(0x0a) + 1);
304
+ }
305
+ }
306
+
307
+ // Send a message
308
+ async sendTxMsg(
309
+ msg: Uint8Array,
310
+ sendWithResponse: boolean
311
+ ): Promise<boolean> {
312
+
313
+ // Check connected
314
+ if (!this._isConnected || !this._port)
315
+ return false;
316
+
317
+ // Debug
318
+ const decoder = new TextDecoder();
319
+ RaftLog.verbose(`RaftChannelWebSerial.sendTxMsg ${msg.toString()} ${decoder.decode(msg)} sendWithResp ${sendWithResponse.toString()}`);
320
+
321
+ try {
322
+ if (this._port.writable != null) {
323
+ const writer = this._port.writable.getWriter();
324
+ await writer.write(this._overasciiEncode(msg));
325
+ writer.releaseLock();
326
+ }
327
+ } catch (err) {
328
+ RaftLog.warn("sendMsg error: " + JSON.stringify(err));
329
+ return false;
330
+ }
331
+ return true;
332
+ }
333
+
334
+ async sendTxMsgNoAwait(
335
+ msg: Uint8Array,
336
+ sendWithResponse: boolean
337
+ ): Promise<boolean> {
338
+
339
+ // Check connected
340
+ if (!this._isConnected || !this._port)
341
+ return false;
342
+
343
+ // Debug
344
+ RaftLog.verbose(`RaftChannelWebSerial.sendTxMsgNoAwait ${msg.toString()} sendWithResp ${sendWithResponse.toString()}`);
345
+
346
+ try {
347
+ if (this._port.writable != null) {
348
+ const writer = this._port.writable.getWriter();
349
+ writer.write(msg).then(() => { writer.releaseLock(); });
350
+ }
351
+ } catch (err) {
352
+ RaftLog.error("sendMsg error: " + JSON.stringify(err));
353
+ }
354
+
355
+ return true;
356
+ }
357
+
358
+ async _readLoop() {
359
+ RaftLog.debug("Starting read loop");
360
+
361
+ if (!this._port)
362
+ return;
363
+
364
+ let retries = 10;
365
+ try {
366
+ if (!this._port.readable) {
367
+ RaftLog.error("RaftChannelWebSerial _readLoop port is not readble");
368
+ return;
369
+ }
370
+ this._reader = this._port.readable.getReader();
371
+ while (this._port.readable && this._isConnected) {
372
+ if (this._connPaused) {
373
+ if (this._reader) {
374
+ this._reader.releaseLock();
375
+ this._reader = undefined;
376
+ }
377
+ await new Promise(resolve => setTimeout(resolve, 100));
378
+ continue;
379
+ }
380
+ try {
381
+ if (!this._reader) this._reader = this._port.readable.getReader();
382
+ const { value, done } = await this._reader.read();
383
+ if (done) {
384
+ this._reader.releaseLock();
385
+ break;
386
+ }
387
+ if (!value || value.length === 0) {
388
+ continue;
389
+ }
390
+
391
+ this._onMsgRx(new Uint8Array(value));
392
+ } catch (err) {
393
+ RaftLog.error("read loop issue: " + JSON.stringify(err));
394
+ retries -= 1;
395
+ if (!retries) break;
396
+ await new Promise(resolve => setTimeout(resolve, 100));
397
+ this._reader = this._port.readable.getReader();
398
+ }
399
+ }
400
+ if (this._reader) this._reader.releaseLock();
401
+ this._reader = undefined;
402
+ } catch (err) {
403
+ RaftLog.error("Read loop got disconnected. err: " + JSON.stringify(err));
404
+ }
405
+ // Disconnected!
406
+ this._isConnected = false;
407
+ RaftLog.debug("Finished read loop");
408
+ }
409
+ }
@@ -0,0 +1,245 @@
1
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2
+ //
3
+ // RaftChannelWebSockets
4
+ // Part of RaftJS
5
+ //
6
+ // Rob Dobson & Chris Greening 2020-2024
7
+ // (C) 2020-2024 All rights reserved
8
+ //
9
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
10
+
11
+ import RaftChannel from "./RaftChannel";
12
+ import WebSocket from "isomorphic-ws";
13
+ import RaftMsgHandler from "./RaftMsgHandler";
14
+ import RaftLog from "./RaftLog";
15
+ import RaftUtils from "./RaftUtils";
16
+ import { RaftConnEvent, RaftConnEventFn } from "./RaftConnEvents";
17
+ import { ConnectorOptions } from "./RaftSystemType";
18
+
19
+ export default class RaftChannelWebSocket implements RaftChannel {
20
+
21
+ // Message handler
22
+ private _raftMsgHandler: RaftMsgHandler | null = null;
23
+
24
+ // Websocket we are connected to
25
+ private _webSocket: WebSocket | null = null;
26
+
27
+ // Last message tx time
28
+ // private _msgTxTimeLast = Date.now();
29
+ // private _msgTxMinTimeBetweenMs = 15;
30
+
31
+ // Is connected
32
+ private _isConnected = false;
33
+
34
+ // Conn event fn
35
+ private _onConnEvent: RaftConnEventFn | null = null;
36
+
37
+ // File Handler parameters
38
+ private _requestedBatchAckSize = 10;
39
+ private _requestedFileBlockSize = 500;
40
+
41
+ fhBatchAckSize(): number { return this._requestedBatchAckSize; }
42
+ fhFileBlockSize(): number { return this._requestedFileBlockSize; }
43
+
44
+
45
+ // isConnected
46
+ isConnected(): boolean {
47
+ return this._isConnected;
48
+ }
49
+
50
+ // Set message handler
51
+ setMsgHandler(raftMsgHandler: RaftMsgHandler): void {
52
+ this._raftMsgHandler = raftMsgHandler;
53
+ }
54
+
55
+ // WebSocket interfaces require subscription to published messages
56
+ requiresSubscription(): boolean {
57
+ return true;
58
+ }
59
+
60
+ // RICREST command before disconnect
61
+ ricRestCmdBeforeDisconnect(): string | null {
62
+ return null;
63
+ }
64
+
65
+ // Set onConnEvent handler
66
+ setOnConnEvent(connEventFn: RaftConnEventFn): void {
67
+ this._onConnEvent = connEventFn;
68
+ }
69
+
70
+ // Get connected locator
71
+ getConnectedLocator(): string | object {
72
+ return this._webSocket;
73
+ }
74
+
75
+ // Connect to a device
76
+ async connect(locator: string | object, connectorOptions: ConnectorOptions): Promise<boolean> {
77
+
78
+ // Debug
79
+ RaftLog.debug("RaftChannelWebSocket.connect " + locator.toString());
80
+
81
+ // Get ws suffix
82
+ const wsSuffix = connectorOptions ? (connectorOptions.wsSuffix ? connectorOptions.wsSuffix : "ws") : "ws";
83
+
84
+ // Connect
85
+ const connOk = await this._wsConnect("ws://" + locator + "/" + wsSuffix);
86
+ return connOk;
87
+ }
88
+
89
+ // Disconnect
90
+ async disconnect(): Promise<void> {
91
+
92
+ // Not connected
93
+ this._isConnected = false;
94
+
95
+ // Disconnect websocket
96
+ this._webSocket?.close(1000);
97
+
98
+ // Debug
99
+ RaftLog.debug(`RaftChannelWebSocket.disconnect attempting to close websocket`);
100
+ }
101
+
102
+ pauseConnection(pause: boolean): void { RaftLog.verbose(`pauseConnection ${pause} - no effect for this channel type`); return; }
103
+
104
+ // Handle notifications
105
+ _onMsgRx(msg: Uint8Array | null): void {
106
+
107
+ // Debug
108
+ if (msg !== null) {
109
+ RaftLog.verbose(`RaftChannelWebSocket._onMsgRx ${RaftUtils.bufferToHex(msg)}`);
110
+ }
111
+
112
+ // Handle message
113
+ if (msg !== null && this._raftMsgHandler) {
114
+ this._raftMsgHandler.handleNewRxMsg(msg);
115
+ }
116
+
117
+ }
118
+
119
+ // Send a message
120
+ async sendTxMsg(
121
+ msg: Uint8Array,
122
+ sendWithResponse: boolean
123
+ ): Promise<boolean> {
124
+
125
+ // Check connected
126
+ if (!this._isConnected)
127
+ return false;
128
+
129
+ // Debug
130
+ RaftLog.verbose(`RaftChannelWebSocket.sendTxMsg ${msg.toString()} sendWithResp ${sendWithResponse.toString()}`);
131
+
132
+ // Send over websocket
133
+ try {
134
+ await this._webSocket?.send(msg);
135
+ } catch (error: unknown) {
136
+ RaftLog.warn(`RaftChannelWebSocket.sendTxMsg - send failed ${error}`);
137
+ return false;
138
+ }
139
+ return true;
140
+ }
141
+
142
+ async sendTxMsgNoAwait(
143
+ msg: Uint8Array,
144
+ sendWithResponse: boolean
145
+ ): Promise<boolean> {
146
+
147
+ // Check connected
148
+ if (!this._isConnected)
149
+ return false;
150
+
151
+ // Debug
152
+ RaftLog.verbose(`RaftChannelWebSocket.sendTxMsgNoAwait ${msg.toString()} sendWithResp ${sendWithResponse.toString()}`);
153
+
154
+ // Send over websocket
155
+ this._webSocket?.send(msg);
156
+
157
+ return true;
158
+ }
159
+
160
+ async _wsConnect(locator: string | object): Promise<boolean> {
161
+
162
+ // Check already connected
163
+ if (await this.isConnected()) {
164
+ return true;
165
+ }
166
+
167
+ // Form websocket address
168
+ const wsURL = locator.toString();
169
+
170
+ // Connect to websocket
171
+ // try {
172
+ // this._webSocket = await this._webSocketOpen(wsURL);
173
+ // } catch (error: any) {
174
+ // RaftLog.debug(`Unable to create WebSocket ${error.toString()}`);
175
+ // return false;
176
+ // }
177
+ this._webSocket = null;
178
+ return new Promise((resolve: (value: boolean | PromiseLike<boolean>) => void,
179
+ reject: (reason?: unknown) => void) => {
180
+ this._webSocketOpen(wsURL).then((ws) => {
181
+ this._webSocket = ws;
182
+ RaftLog.debug(`_wsConnect - opened connection`);
183
+
184
+ // Handle messages
185
+ this._webSocket.onmessage = (evt: WebSocket.MessageEvent) => {
186
+ // RaftLog.debug("WebSocket rx");
187
+ if (evt.data instanceof ArrayBuffer) {
188
+ const msg = new Uint8Array(evt.data);
189
+ this._onMsgRx(msg);
190
+ }
191
+ }
192
+
193
+ // Handle close event
194
+ this._webSocket.onclose = (evt: WebSocket.CloseEvent) => {
195
+ RaftLog.info(`_wsConnect - closed code ${evt.code} wasClean ${evt.wasClean} reason ${evt.reason}`);
196
+ this._webSocket = null;
197
+ this._isConnected = false;
198
+
199
+ // Event handler
200
+ if (this._onConnEvent) {
201
+ this._onConnEvent(RaftConnEvent.CONN_DISCONNECTED);
202
+ }
203
+ }
204
+
205
+ // Resolve the promise - success
206
+ resolve(true);
207
+ }).catch((err: unknown) => {
208
+ if (err instanceof Error) {
209
+ RaftLog.verbose(`WS open failed ${err.toString()}`)
210
+ }
211
+ // Resolve - failed
212
+ reject(false);
213
+ })
214
+ });
215
+ }
216
+
217
+ private async _webSocketOpen(url: string): Promise<WebSocket> {
218
+ return new Promise((resolve, reject) => {
219
+
220
+ // Debug
221
+ // RaftLog.debug('Attempting WebSocket connection');
222
+
223
+ // Open the socket
224
+ try {
225
+ const webSocket = new WebSocket(url);
226
+
227
+ // Open socket
228
+ webSocket.binaryType = "arraybuffer";
229
+ webSocket.onopen = (_evt: WebSocket.Event) => {
230
+ RaftLog.debug(`RaftChannelWebSocket._webSocketOpen - onopen ${_evt.toString()}`);
231
+ // // We're connected
232
+ this._isConnected = true;
233
+ resolve(webSocket);
234
+ };
235
+ webSocket.onerror = function (evt: WebSocket.ErrorEvent) {
236
+ RaftLog.warn(`RaftChannelWebSocket._webSocketOpen - onerror: ${evt.message}`);
237
+ reject(evt);
238
+ }
239
+ } catch (error: unknown) {
240
+ RaftLog.warn(`RaftChannelWebSocket._webSocketOpen - open failed ${error}`);
241
+ reject(error);
242
+ }
243
+ });
244
+ }
245
+ }