export const ADDR = { CTRL: 0xdb96, OVEN: 0xaa55, }; export const FUNC = { // used by oven O_CONNECT: 0x01, O_TEMPS: 0x02, O_READ: 0x03, O_ACK_WRITE: 0x06, // used by CTRL C_PWM_HEAT: 0x08, C_PWM_COOL: 0x0d, C_STARTSTOP: 0x10, C_REQ_READ: 0x11, C_WRITE: 0x20, C_ACK_CONNECT: 0xf1, C_ACK_READ: 0xf2, }; export const ARG = { // for STARSTOP, REQUEST_READ STARTSTOP_START: 0x11, STARTSTOP_STOP: 0x22, REQUEST_READ: 0x11, // for CONNECT / ACK_CONNECT CONNECT_CONNECTING: 0x0100, CONNECT_CONNECTED: 0x0200, // for *_PWM PWM_ON: 0x44, PWM_OFF: 0x88, }; const lookup = (tbl, val) => Object.keys(tbl).find(key => tbl[key] === val); export class Message { constructor(buffer) { this.buf = buffer; this.view = new DataView(this.buf); this.data = new DataView(this.buf, 4, this.buf.byteLength - 6); } get16(i) { return this.data.getUint16(i, false); } set16(i, v) { this.data.setUint16(i, v, false); } get address() { return this.view.getUint16(0, false); } set address(val) { this.view.setUint16(0, val, false); } get func() { return this.view.getUint8(2); } set func(val) { this.view.setUint8(2, val)} get data_len() { return this.view.getUint8(3); } set data_len(val) { this.view.setUint8(3, val); } get length() { return this.buf.byteLength; } get checksum() { return this.view.getUint16(this.buf.byteLength-2, false); } set checksum(val) { this.view.setUint16(this.buf.byteLength-2, val, false); } calculateSum() { let sum = 0; for (let i = 0; i < this.length - 2; i++) { sum += this.view.getUint8(i); } return sum & 0xffff; } getDataArray() { return new Uint8Array(this.buf, 4, this.buf.byteLength - 6); } toString() { const address = lookup(ADDR, this.address) ?? "????"; const func = lookup(FUNC, this.func) ?? "?_??"; const data = this.getDataArray(); return `to ${address}: ${func.substr(2)} [${data}]`; } static from8(addr, func, data) { const len = 2 + 2 + data.length + 2; const msg = new Message(new ArrayBuffer(len)); msg.address = addr; msg.func = func; msg.data_len = data.length; for (let i = 0; i < data.length; i++) { msg.data.setUint8(i, data[i]); } msg.checksum = msg.calculateSum(); return msg; } static from16(addr, func, data) { const len = 2 + 2 + 2*data.length + 2; const msg = new Message(new ArrayBuffer(len)); msg.address = addr; msg.func = func; msg.data_len = 2*data.length; for (let i = 0; i < data.length; i++) { msg.data.setUint16(2*i, data[i], false); } msg.checksum = msg.calculateSum(); return msg; } static empty(addr, func, data_len) { const len = 2 + 2 + 2*data_len + 2; const msg = new Message(new ArrayBuffer(len)); msg.address = addr; msg.func = func; msg.data_len = data_len; return msg; } static tryParse(buffer) { if (buffer.length < 4 + 2) return; const data_len = buffer[3]; if (buffer.length < 4 + data_len + 2) return; const msg = new Message(buffer.slice(0, 4 + data_len + 2).buffer); if (msg.calculateSum() !== msg.checksum) { throw new Error(`checksum mismatch: expected ${hex(msg.checksum)} but calculated ${hex(msg.calculateSum())}`); } return msg; }; }