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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -188,13 +188,15 @@ export class PacketGenerator {
// https://github.com/ayufan/niimprint-web/blob/main/cmds.js#L215 // https://github.com/ayufan/niimprint-web/blob/main/cmds.js#L215
public static printEmptySpace(pos: number, repeats: number): NiimbotPacket { 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 { public static printBitmapRow(pos: number, repeats: number, data: Uint8Array): NiimbotPacket {
const blackPixelCount: number = Utils.countSetBits(data); const blackPixelCount: number = Utils.countSetBits(data);
return new NiimbotPacket(RequestCommandId.PrintBitmapRow, [ const packet = new NiimbotPacket(RequestCommandId.PrintBitmapRow, [
...Utils.u16ToBytes(pos), ...Utils.u16ToBytes(pos),
// Black pixel count. Not sure what role it plays in printing. // Black pixel count. Not sure what role it plays in printing.
// There is two formats of this part // There is two formats of this part
@ -205,6 +207,8 @@ export class PacketGenerator {
repeats, repeats,
...data, ...data,
]); ]);
packet.setNoResponse();
return packet;
} }
/** Printer powers off if black pixel count > 6 */ /** Printer powers off if black pixel count > 6 */
@ -216,13 +220,16 @@ export class PacketGenerator {
throw new Error(`Black pixel count > 6 (${blackPixelCount})`); throw new Error(`Black pixel count > 6 (${blackPixelCount})`);
} }
return new NiimbotPacket(RequestCommandId.PrintBitmapRowIndexed, [ const packet = new NiimbotPacket(RequestCommandId.PrintBitmapRowIndexed, [
...Utils.u16ToBytes(pos), ...Utils.u16ToBytes(pos),
0, 0,
...Utils.u16ToBytes(blackPixelCount), ...Utils.u16ToBytes(blackPixelCount),
repeats, repeats,
...indexes, ...indexes,
]); ]);
packet.setNoResponse();
return packet;
} }
public static printClear(): NiimbotPacket { 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" resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz#f066abfcd1cbe66267cdbbf0de010d8a41b41597"
integrity sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow== 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: typescript-event-target@^1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/typescript-event-target/-/typescript-event-target-1.1.1.tgz#20a6d491b77d2e37dc432c5394ab74c0d7065539" resolved "https://registry.yarnpkg.com/typescript-event-target/-/typescript-event-target-1.1.1.tgz#20a6d491b77d2e37dc432c5394ab74c0d7065539"