import ReconnectingWebSocket from 'reconnecting-websocket'; import * as msgpack from 'msgpack-lite'; const OpCodes = { // from swim tracker device to frontend ERROR: 1, INITIAL_INFO: 2, SESSION_STARTED: 3, SESSION_STOPPED: 4, SESSION_NEW_DATA: 5, ANSWER_USER_LIST: 6, ANSWER_SESSION_LIST: 7, WIFI_STATE_RESPONSE: 8, WIFI_SCAN_RESPONSE: 9, // from frontend to device START_SESSION: 128, STOP_SESSION: 129, TARE: 130, QUERY_USER_LIST: 131, QUERY_SESSION_LIST: 132, WIFI_STATE_SET: 133, WIFI_STATE_GET: 134, WIFI_TRIGGER_SCAN: 135, }; export default class SwimTrackerWebsocketConnection { constructor(swimTrackerHost, onData, onStarted, onStopped, onConnect, onDisconnect) { this.swimTrackerHost = swimTrackerHost; this.onData = onData; this.onStarted = onStarted; this.onStopped = onStopped; this.onConnect = onConnect; this.onDisconnect = onDisconnect; const wsOptions = { maxReconnectionDelay: 4000 }; this.ws = new ReconnectingWebSocket(`ws://${swimTrackerHost}:81`, [], wsOptions); this.ws.onmessage = this._onMessage; this.ws.onopen = this.onConnect; this.ws.onclose = this.onDisconnect; this.ws.onerror = this._onError; this.ws.binaryType = 'arraybuffer'; this.msgpackCodec = msgpack.createCodec(); this.msgpackCodec.addExtUnpacker(205, function (byteArr) { const buffer = byteArr.buffer.slice(byteArr.byteOffset, byteArr.byteLength + byteArr.byteOffset); const result = new Int16Array(buffer); return result; }); this._wifiScanPromise = null; } sendStartCommand() { const data = new Uint8Array(1); data[0] = OpCodes.START_SESSION; this.ws.send(data); } sendStopCommand() { const data = new Uint8Array(1); data[0] = OpCodes.STOP_SESSION; this.ws.send(data); } sendTareCommand() { const data = new Uint8Array(1); data[0] = OpCodes.TARE; this.ws.send(data); } scanWifiNetworks() { // trigger scan const data = new Uint8Array(1); data[0] = OpCodes.WIFI_TRIGGER_SCAN; this.ws.send(data); if (this._wifiScanPromise !== null) { return Promise.reject("Scan in progress"); } else { let conn = this; return new Promise((resolve, reject) => { conn._wifiScanPromise = { resolve: resolve, reject: reject }; }); } } _onMessage = (e) => { const dv = new DataView(e.data); const opCode = dv.getInt8(0); if (opCode === OpCodes.INITIAL_INFO) { const headerSize = 6; const running = Boolean(dv.getInt8(1)); const sessionId = dv.getUint32(2); if (running && e.data.byteLength > headerSize) { const data = new Uint16Array(e.data.slice(headerSize)); this.onStarted(sessionId); this.onData(data); } else this.onStopped(); } else if (opCode === OpCodes.SESSION_STARTED) { const sessionId = dv.getUint32(1); this.onStarted(sessionId); } else if (opCode === OpCodes.SESSION_STOPPED) { this.onStopped(); } else if (opCode === OpCodes.SESSION_NEW_DATA) { const data = new Uint16Array(e.data.slice(1)); this.onData(data); } else if (opCode === OpCodes.WIFI_SCAN_RESPONSE) { console.log("got data", e.data); const scanResult = msgpack.decode(new Uint8Array(e.data).slice(1), { codec: this.msgpackCodec }); if (this._wifiScanPromise !== null) { this._wifiScanPromise.resolve(scanResult); this._wifiScanPromise = null; } else { console.log("Got unexpected WiFi scan result", scanResult); } } } _onError = (ev) => { console.log("Websocket error", ev); } };