Last active
January 24, 2021 18:50
-
-
Save MikeiLL/0ec8b8d39ed6d2dfa4bf4b557dc1f1cd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ----------------------- | |
// TypeScript [2.6.2] | |
// ----------------------- | |
import MSeriesReceiverParser from './mSeriesReceiverParser' | |
const RECEIVER_VENDOR_ID = 0x0483 | |
const RECEIVER_PRODUCT_ID = 0x5740 | |
const DEVICE_NAME = 'USB Multi-Bike Reciever' | |
if ("serial" in navigator) { | |
console.log("The Serial API is supported.") | |
} else { | |
console.error('Web serial doesn\'t seem to be enabled in your browser. Try enabling it by visiting:') | |
console.error('chrome://flags/#enable-experimental-web-platform-features'); | |
console.error('opera://flags/#enable-experimental-web-platform-features'); | |
console.error('edge://flags/#enable-experimental-web-platform-features'); | |
} | |
export class Receiver { | |
callback: ((broadcast: Broadcast) => void) | |
targetPort: SerialPort | null | |
constructor (callback: ((broadcast: Broadcast) => void)) { | |
console.log("Receiver callback") | |
this.callback = callback | |
this.targetPort = null | |
navigator.serial.addEventListener('connect', event => { this.attached(event) }) | |
navigator.serial.addEventListener('disconnect', event => { this.detached(event) }) | |
} | |
async requestPort () { | |
console.log("requestPort") | |
console.log(this.targetPort) | |
if (!this.targetPort) { | |
try { | |
this.targetPort = await navigator.serial.requestPort({ | |
filters: [{ | |
usbVendorId: RECEIVER_VENDOR_ID | |
usbProductId: RECEIVER_PRODUCT_ID | |
}] | |
}) | |
this.connect(this.targetPort) | |
} catch (error) { | |
console.log(error) | |
serial_state.deviceMessage = DEVICE_NAME + ': Permission Denied' | |
serial_state.deviceStatus = false | |
} | |
} | |
} | |
close () { | |
console.log("close") | |
if (this.targetPort) { | |
console.log(this.targetPort) | |
this.targetPort.stopTransfer() | |
this.targetPort = null | |
} | |
} | |
private attached (event: SerialUSB.ConnectionEvent) { | |
console.log("attached") | |
console.log(event) // Serial Connection Event | |
if (this.matchesTarget(event.port)) { | |
serial_state.deviceMessage = DEVICE_NAME + ': Connected' | |
serial_state.deviceStatus = true | |
this.connect(event.port) | |
} | |
} | |
private detached (event: SerialUSB.ConnectionEvent) { | |
console.log("detached") | |
if (this.matchesTarget(event.target) && this.targetPort && this.targetPort.isSameDevice(event.target)) { | |
serial_state.deviceMessage = DEVICE_NAME + ': Disconnected' | |
serial_state.deviceStatus = false | |
this.close() | |
} | |
} | |
private connect (port: SerialUSB.Port) { | |
console.log("connect") | |
console.log(port) // USB Port | |
this.targetPort = new SerialPortWrapper(port, this.callback) | |
serial_state.deviceMessage = DEVICE_NAME + ': Connected' | |
serial_state.deviceStatus = true | |
} | |
private matchesTarget (port: SerialUSB.Port) { | |
console.log("matchesTarget") | |
let info = port.getInfo() | |
return info.usbVendorId === RECEIVER_VENDOR_ID && | |
info.usbProductId === RECEIVER_PRODUCT_ID | |
} | |
} | |
export default class SerialPortWrapper { | |
continueTransfer: boolean | |
callback: ((broadcast: Broadcast) => void) | |
portConnection: SerialUSB.Port | |
readerDone: boolean | |
constructor (port: SerialUSB.Port, callback: ((broadcast: Broadcast) => void)) { | |
console.log("Port Wrapper Class") | |
console.log(port) | |
this.reader = null | |
this.continueTransfer = false | |
this.callback = callback | |
this.portConnection = port | |
this.initialize() | |
} | |
/***************************************** | |
* Exposed Controls | |
*****************************************/ | |
isSameDevice (port: SerialUSB.Port) { | |
let info = port.getInfo() | |
return info.serialNumber === this.portConnection.serialNumber | |
} | |
async disconnect () { | |
try { | |
this.stopTransfer() | |
this.portConnection.close() | |
serial_state.deviceMessage = 'Device closed or disconnected. Check connection, reload page or click button to reconnect.' | |
serial_state.deviceStatus = false | |
serial_state.dataStatus = false | |
} catch (error) { | |
serial_state.deviceMessage = 'Error closing device\n' + error | |
serial_state.deviceStatus = false | |
serial_state.dataStatus = false | |
} | |
render_serial_activity() | |
} | |
/***************************************** | |
* Control Flow | |
*****************************************/ | |
async initialize () { | |
try { | |
await this.portConnection.open({ baudRate: 115200 }) | |
this.startTransfer() | |
} catch (error) { | |
console.error('Initialization error\n', error) | |
console.log(this.portConnection) | |
} | |
} | |
/***************************************** | |
* Transfer | |
*****************************************/ | |
async startTransfer () { | |
this.continueTransfer = true | |
while (this.portConnection.readable && this.continueTransfer) { | |
try { | |
this.reader = this.portConnection.readable.getReader() | |
try { | |
const { value, readerDone } = await this.reader.read() | |
} catch (error) { | |
console.error('Reader read failed\n', error) | |
serial_state.deviceMessage = DEVICE_NAME + ': Reader read failed. Disconnected?' | |
serial_state.deviceStatus = false | |
render_serial_activity() | |
} | |
if (readerDone) { | |
// Allow the serial port to be closed later. | |
this.reader.releaseLock(); | |
break; | |
} | |
if (value && value.byteLength > 0) { | |
this.parse(value) | |
} | |
} catch (error) { | |
// TODO: Handle non-fatal read error. | |
console.log(error) | |
} finally { | |
this.reader.releaseLock() | |
} | |
} | |
await this.portConnection.close() | |
} | |
stopTransfer () { | |
this.continueTransfer = false | |
} | |
/***************************************** | |
* Parse | |
*****************************************/ | |
parse (data: DataView) { | |
let rawStream = new Uint8Array(data.buffer) | |
let segments = String.fromCharCode.apply(null, rawStream).split(' ') | |
let firstSegmentValue = parseInt(segments[0], 10) | |
if (firstSegmentValue >= 201) { | |
console.log("No active Elliptical data") | |
render_serial_activity() | |
console.log(segments) | |
} | |
if (firstSegmentValue <= 200) { | |
let swValue = parseFloat(segments[6]) | |
if (swValue >= 6.0 && swValue < 7.0) { | |
try { | |
let broadcast = MSeriesReceiverParser(segments) | |
if (broadcast.ordinalId == instructor_unit){ | |
this.callback(broadcast) | |
} | |
} catch (error) { | |
console.error('M Series parse error\n', error) | |
} | |
} else if (swValue >= 8.0 && swValue < 9.0) { | |
console.error('Incorrect device connected') | |
} | |
} | |
} | |
} | |
/* | |
* Top level callback function through which Receiver does stuff | |
*/ | |
export default function logEllipticalStats(broadcast){ | |
console.log(broadcast) | |
} | |
const receiver = new Receiver( logEllipticalStats ) | |
document.addEventListener('DOMContentLoaded', async () => { | |
// const receiver = new Receiver( myCallback ) | |
const connectButton = document.querySelector('#connect') as HTMLInputElement | |
const disconnectButton = document.querySelector('#disconnect') as HTMLInputElement | |
if (connectButton) { | |
connectButton.addEventListener('click', async () => { | |
try { | |
await receiver.requestPort() | |
} catch (e) { | |
console.error(e.message) | |
} | |
}) | |
} | |
if (disconnectButton) { | |
connectButton.addEventListener('click', async () => { | |
try { | |
await receiver.close() | |
} catch (e) { | |
console.error(e.message) | |
} | |
}) | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment