import { ConnectionInfo, NiimbotAbstractClient } from "."; import { ConnectResult, NiimbotPacket } from "../packets"; import { Utils } from "../utils"; import { ConnectEvent, DisconnectEvent, RawPacketSentEvent } from "./events"; /** WIP. Uses serial communication, Works worse than NiimbotBluetoothClient at the moment, events are not firing */ export class NiimbotSerialClient extends NiimbotAbstractClient { private port?: SerialPort = undefined; private writer?: WritableStreamDefaultWriter<Uint8Array> = undefined; public async connect(): Promise<ConnectionInfo> { this.disconnect(); const _port: SerialPort = await navigator.serial.requestPort(); _port.addEventListener("disconnect", () => { this.port = undefined; this.dispatchTypedEvent("disconnect", new DisconnectEvent()); }); await _port.open({ baudRate: 115200 }); if (_port.writable === null) { throw new Error("Port is not writable"); } this.port = _port; this.writer = _port.writable.getWriter(); const info = _port.getInfo(); try { await this.initialNegotiate(); await this.fetchPrinterInfo(); } catch (e) { console.error(e); } const result: ConnectionInfo = { deviceName: `Serial (VID:${info.usbVendorId?.toString(16)} PID:${info.usbProductId?.toString(16)})`, result: this.info.connectResult ?? ConnectResult.FirmwareErrors }; this.dispatchTypedEvent("connect", new ConnectEvent(result)); return result; } public async disconnect() { if (this.writer !== undefined) { this.writer.releaseLock(); } if (this.port !== undefined) { this.port.close(); this.dispatchTypedEvent("disconnect", new DisconnectEvent()); } this.port = undefined; this.writer = undefined; } public isConnected(): boolean { throw this.port !== undefined && this.writer !== undefined; } public async sendPacketWaitResponse(packet: NiimbotPacket, timeoutMs: number = 1000): Promise<NiimbotPacket> { if (this.port === undefined) { throw new Error("Port is closed"); } this.sendPacket(packet); if (this.port.readable === null) { throw new Error("Port is not readable"); } let data = new Uint8Array(); let p: NiimbotPacket | undefined = undefined; const reader = this.port.readable.getReader(); // todo: rewrite, no timeout! try { while (true) { const { value, done } = await reader.read(); if (done) { console.log("done"); break; } const newArr = new Uint8Array(data.length + value.length); newArr.set(data); newArr.set(value, data.length); data = newArr; try { const dv = new DataView(data.buffer, data.byteOffset, data.byteLength); p = NiimbotPacket.fromBytes(dv); break; } catch (e) { console.log("skipping"); } // Do something with |value|... } } catch (error) { console.error(error); } finally { reader.releaseLock(); } console.log("end"); if (p === undefined) { throw new Error("err"); } // const reader: ReadableStreamDefaultReader<Uint8Array> = this.port.readable.getReader(); // const timer: NodeJS.Timeout = setTimeout(() => { // reader.releaseLock(); // }, timeoutMs); // const result: ReadableStreamReadResult<Uint8Array> = await reader.read(); // clearTimeout(timer); // reader.releaseLock(); // const arr: Uint8Array = result.value!; // console.log(Utils.bufToHex(arr)); // const dv = new DataView(arr.buffer, arr.byteOffset, arr.byteLength); // const inPacket = NiimbotPacket.fromBytes(dv); // this.port.readable.pipeTo(new WritableStream({ // write(chunk) { // console.log("Chunk received", chunk); // }, // close() { // console.log("All data successfully read!"); // }, // abort(e) { // console.error("Something went wrong!", e); // } // })); return p; } public async sendRaw(data: Uint8Array, force?: boolean) { if (this.writer === undefined) { throw new Error("Port is not writable"); } // this.writer.releaseLock(); this.dispatchTypedEvent("rawpacketsent", new RawPacketSentEvent(data)); await Utils.sleep(2); // fixme maybe } }