Working tree changes 2024-07-21 00:00

This commit is contained in:
Bot 2024-07-21 00:00:01 +03:00 committed by multimote
parent 093ad46b68
commit 4e5250fc06
9 changed files with 105 additions and 35 deletions

View File

@ -34,7 +34,7 @@
for (let i = 0; i < packets.length; i++) {
sendProgress = Math.round(((i + 1) / packets.length) * 100);
// console.log(Utils.bufToHex(packets[i].toBytes()))
await $printerClient.sendPacket(packets[i]);
await $printerClient.sendPacketWaitResponse(packets[i]);
// await Utils.sleep(100);
// console.log(Utils.bufToHex(packets[i].toBytes()))

View File

@ -194,6 +194,7 @@
"@mmote/niimbluelib@file:../niimbluelib":
version "0.0.1"
dependencies:
async-mutex "^0.5.0"
typescript-event-target "^1.1.1"
"@popperjs/core@^2.11.8", "@popperjs/core@^2.9.2":
@ -408,6 +409,13 @@ aria-query@^5.3.0:
dependencies:
dequal "^2.0.3"
async-mutex@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.5.0.tgz#353c69a0b9e75250971a64ac203b0ebfddd75482"
integrity sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==
dependencies:
tslib "^2.4.0"
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@ -1281,7 +1289,7 @@ tr46@~0.0.3:
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
tslib@^2.6.3:
tslib@^2.4.0, tslib@^2.6.3:
version "2.6.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0"
integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==

View File

@ -23,6 +23,7 @@
"typescript": "^5.4.5"
},
"dependencies": {
"async-mutex": "^0.5.0",
"typescript-event-target": "^1.1.1"
}
}

View File

@ -1,7 +1,15 @@
import { Mutex } from "async-mutex";
import { ConnectionInfo, NiimbotAbstractClient } from ".";
import { NiimbotPacket, PacketParser, ResponseCommandId } from "../packets";
import { Utils } from "../utils";
import { ConnectEvent, DisconnectEvent, PacketParsedEvent, PacketReceivedEvent, RawPacketReceivedEvent, RawPacketSentEvent } from "./events";
import {
ConnectEvent,
DisconnectEvent,
PacketParsedEvent,
PacketReceivedEvent,
RawPacketReceivedEvent,
RawPacketSentEvent,
} from "./events";
class BleConfiguration {
public static readonly SERVICE: string = "e7810a71-73ae-499d-8c15-faa9aef0c3f2";
@ -16,6 +24,7 @@ class BleConfiguration {
export class NiimbotBluetoothClient extends NiimbotAbstractClient {
private gattServer?: BluetoothRemoteGATTServer = undefined;
private channel?: BluetoothRemoteGATTCharacteristic = undefined;
private mutex: Mutex = new Mutex();
public async connect(): Promise<ConnectionInfo> {
this.disconnect();
@ -51,7 +60,6 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
this.dispatchTypedEvent("rawpacketreceived", new RawPacketReceivedEvent(target.value!));
this.dispatchTypedEvent("packetreceived", new PacketReceivedEvent(packet));
if (!(packet.getCommand() in ResponseCommandId)) {
console.warn(`Unknown response command: 0x${Utils.numberToHex(packet.getCommand())}`);
} else {
@ -87,33 +95,52 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
* If packet.responsePacketCommandId is defined, it will wait for packet with this command id.
*/
public async sendPacketWaitResponse(packet: NiimbotPacket, timeoutMs?: number): Promise<NiimbotPacket> {
await this.sendPacket(packet);
// what if response received at this point?
return new Promise((resolve) => {
let timeout: NodeJS.Timeout | undefined = undefined;
return this.mutex.runExclusive(async () => {
await this.sendPacket(packet, true);
const listener = (evt: PacketReceivedEvent) => {
if (packet.getValidResponseIds().length === 0 || packet.getValidResponseIds().includes(evt.packet.getCommand())) {
clearTimeout(timeout);
this.removeEventListener("packetreceived", listener)
resolve(evt.packet);
}
};
if (packet.isNoResponse()) {
return new NiimbotPacket(-1, []); // or undefined is better?
}
timeout = setTimeout(() => {
this.removeEventListener("packetreceived", listener)
throw new Error("Timeout waiting response");
}, timeoutMs ?? 1000);
// what if response received at this point?
this.addEventListener("packetreceived", listener)
return new Promise((resolve) => {
let timeout: NodeJS.Timeout | undefined = undefined;
const listener = (evt: PacketReceivedEvent) => {
if (
packet.getValidResponseIds().length === 0 ||
packet.getValidResponseIds().includes(evt.packet.getCommand())
) {
clearTimeout(timeout);
this.removeEventListener("packetreceived", listener);
resolve(evt.packet);
}
};
timeout = setTimeout(() => {
this.removeEventListener("packetreceived", listener);
throw new Error("Timeout waiting response");
}, timeoutMs ?? 1000);
this.addEventListener("packetreceived", listener);
});
});
}
public async sendRaw(data: Uint8Array) {
if (this.channel === undefined) {
throw new Error("Channel is closed");
public async sendRaw(data: Uint8Array, force?: boolean) {
const send = async () => {
if (this.channel === undefined) {
throw new Error("Channel is closed");
}
await this.channel.writeValueWithoutResponse(data);
this.dispatchTypedEvent("rawpacketsent", new RawPacketSentEvent(data));
};
if (force) {
return send();
} else {
await this.mutex.runExclusive(send);
}
await this.channel.writeValueWithoutResponse(data);
this.dispatchTypedEvent("rawpacketsent", new RawPacketSentEvent(data));
}
}

View File

@ -16,11 +16,16 @@ export abstract class NiimbotAbstractClient extends TypedEventTarget<ClientEvent
*/
public abstract sendPacketWaitResponse(packet: NiimbotPacket, timeoutMs?: number): Promise<NiimbotPacket>;
/** Send raw data tp printer port */
public abstract sendRaw(data: Uint8Array): Promise<void>;
/**
* Send raw bytes to the printer port.
*
* @param data Bytes to send.
* @param force Ignore mutex lock. You should avoid using it.
*/
public abstract sendRaw(data: Uint8Array, force?: boolean): Promise<void>;
public async sendPacket(packet: NiimbotPacket) {
await this.sendRaw(packet.toBytes());
public async sendPacket(packet: NiimbotPacket, force?: boolean) {
await this.sendRaw(packet.toBytes(), force);
}
public async sendPacketWaitResponseDecoded(packet: NiimbotPacket, timeoutMs?: number): Promise<ParsedPacket> {

View File

@ -130,13 +130,11 @@ export class NiimbotSerialClient extends NiimbotAbstractClient {
return p;
}
public async sendRaw(data: Uint8Array) {
public async sendRaw(data: Uint8Array, force?: boolean) {
if (this.writer === undefined) {
throw new Error("Port is not writable");
}
await this.writer.write(data);
// this.writer.releaseLock();
this.dispatchTypedEvent("rawpacketsent", new RawPacketSentEvent(data));

View File

@ -9,10 +9,22 @@ export class NiimbotPacket {
private data: Uint8Array;
private validResponseIds: ResponseCommandId[];
/** There can be no response after this request. */
private noResponse: boolean;
constructor(commandId: number, data: Uint8Array | number[], validResponseIds: ResponseCommandId[] = []) {
this.commandId = commandId;
this.data = data instanceof Uint8Array ? data : new Uint8Array(data);
this.validResponseIds = validResponseIds;
this.noResponse = false;
}
public setNoResponse(): void {
this.noResponse = true;
}
public isNoResponse(): boolean {
return this.noResponse;
}
public getValidResponseIds(): ResponseCommandId[] {

View File

@ -188,13 +188,15 @@ export class PacketGenerator {
// https://github.com/ayufan/niimprint-web/blob/main/cmds.js#L215
public static printEmptySpace(pos: number, repeats: number): NiimbotPacket {
return new NiimbotPacket(RequestCommandId.PrintEmptyRow, [...Utils.u16ToBytes(pos), repeats]);
const packet = new NiimbotPacket(RequestCommandId.PrintEmptyRow, [...Utils.u16ToBytes(pos), repeats]);
packet.setNoResponse();
return packet;
}
public static printBitmapRow(pos: number, repeats: number, data: Uint8Array): NiimbotPacket {
const blackPixelCount: number = Utils.countSetBits(data);
return new NiimbotPacket(RequestCommandId.PrintBitmapRow, [
const packet = new NiimbotPacket(RequestCommandId.PrintBitmapRow, [
...Utils.u16ToBytes(pos),
// Black pixel count. Not sure what role it plays in printing.
// There is two formats of this part
@ -205,6 +207,8 @@ export class PacketGenerator {
repeats,
...data,
]);
packet.setNoResponse();
return packet;
}
/** Printer powers off if black pixel count > 6 */
@ -216,13 +220,16 @@ export class PacketGenerator {
throw new Error(`Black pixel count > 6 (${blackPixelCount})`);
}
return new NiimbotPacket(RequestCommandId.PrintBitmapRowIndexed, [
const packet = new NiimbotPacket(RequestCommandId.PrintBitmapRowIndexed, [
...Utils.u16ToBytes(pos),
0,
...Utils.u16ToBytes(blackPixelCount),
repeats,
...indexes,
]);
packet.setNoResponse();
return packet;
}
public static printClear(): NiimbotPacket {

View File

@ -19,6 +19,18 @@
resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz#f066abfcd1cbe66267cdbbf0de010d8a41b41597"
integrity sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==
async-mutex@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.5.0.tgz#353c69a0b9e75250971a64ac203b0ebfddd75482"
integrity sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==
dependencies:
tslib "^2.4.0"
tslib@^2.4.0:
version "2.6.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0"
integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==
typescript-event-target@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/typescript-event-target/-/typescript-event-target-1.1.1.tgz#20a6d491b77d2e37dc432c5394ab74c0d7065539"