Working tree changes 2024-07-26 00:00
All checks were successful
Test project build / Build (push) Successful in 1m3s

This commit is contained in:
Bot 2024-07-26 00:00:02 +03:00 committed by multimote
parent a33dc79fa1
commit 16324d5f5f
8 changed files with 120 additions and 88 deletions

View File

@ -33,5 +33,6 @@
"tslib": "^2.6.3",
"typescript": "^5.2.2",
"vite": "^5.3.1"
}
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}

View File

@ -61,14 +61,9 @@ 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 {
const data = PacketParser.parse(packet);
if (data !== undefined) {
this.dispatchTypedEvent("packetparsed", new PacketParsedEvent(data));
}
}*/
if (!(packet.command in ResponseCommandId)) {
console.warn(`Unknown response command: 0x${Utils.numberToHex(packet.command)}`);
}
});
await channel.startNotifications();
@ -76,11 +71,8 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
this.gattServer = gattServer;
this.channel = channel;
let connectResult = ConnectResult.Connected;
try {
connectResult = await this.abstraction.connectResult();
console.log({connectResult: ConnectResult[connectResult]});
await this.initialNegotiate();
await this.fetchPrinterConfig();
} catch (e) {
console.error(e);
@ -88,7 +80,7 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
const result: ConnectionInfo = {
deviceName: device.name,
result: connectResult
result: this.printerConfig.connectResult ?? ConnectResult.FirmwareErrors
};
this.dispatchTypedEvent("connect", new ConnectEvent(result));
@ -116,7 +108,7 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
return this.mutex.runExclusive(async () => {
await this.sendPacket(packet, true);
if (packet.isOneWay()) {
if (packet.oneWay) {
return new NiimbotPacket(-1, []); // or undefined is better?
}
@ -127,8 +119,8 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
const listener = (evt: PacketReceivedEvent) => {
if (
packet.getValidResponseIds().length === 0 ||
packet.getValidResponseIds().includes(evt.packet.getCommand())
packet.validResponseIds.length === 0 ||
packet.validResponseIds.includes(evt.packet.command)
) {
clearTimeout(timeout);
this.removeEventListener("packetreceived", listener);
@ -139,7 +131,7 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
timeout = setTimeout(() => {
this.removeEventListener("packetreceived", listener);
throw new Error(
`Timeout waiting response (waited for ${Utils.bufToHex(packet.getValidResponseIds(), ", ")})`
`Timeout waiting response (waited for ${Utils.bufToHex(packet.validResponseIds, ", ")})`
);
}, timeoutMs ?? 1000);

View File

@ -5,10 +5,12 @@ import { Abstraction } from "../packets/abstraction";
export type ConnectionInfo = {
deviceName?: string;
result: ConnectResult
result: ConnectResult;
};
export interface PrinterConfig {
connectResult?: ConnectResult;
protocolVersion?: number;
model?: PrinterModelId;
serial?: string;
mac?: string;
@ -52,8 +54,21 @@ export abstract class NiimbotAbstractClient extends TypedEventTarget<ClientEvent
await this.sendRaw(packet.toBytes(), force);
}
/** Send "connect" packet and fetch the protocol version */
protected async initialNegotiate(): Promise<void> {
const cfg = this.printerConfig;
cfg.connectResult = await this.abstraction.connectResult();
cfg.protocolVersion = 0;
if (cfg.connectResult === ConnectResult.ConnectedNew) {
cfg.protocolVersion = 1;
} else if (cfg.connectResult === ConnectResult.ConnectedV3) {
const statusData = await this.abstraction.getPrinterStatusData();
cfg.protocolVersion = statusData.protocolVersion;
}
}
public async fetchPrinterConfig(): Promise<PrinterConfig> {
this.printerConfig = {};
// console.log(await this.abstraction.getPrinterStatusData());
this.printerConfig.model = await this.abstraction.getPrinterModel();
this.printerConfig.serial = await this.abstraction.getPrinterSerialNumber();

View File

@ -1,5 +1,5 @@
import { ConnectionInfo, NiimbotAbstractClient } from ".";
import { NiimbotPacket } from "../packets";
import { ConnectResult, NiimbotPacket } from "../packets";
import { Utils } from "../utils";
import { ConnectEvent, DisconnectEvent, RawPacketSentEvent } from "./events";
@ -28,8 +28,16 @@ export class NiimbotSerialClient extends NiimbotAbstractClient {
this.writer = _port.writable.getWriter();
const info = _port.getInfo();
try {
await this.initialNegotiate();
await this.fetchPrinterConfig();
} catch (e) {
console.error(e);
}
const result: ConnectionInfo = {
deviceName: `Serial (VID:${info.usbVendorId?.toString(16)} PID:${info.usbProductId?.toString(16)})`,
result: this.printerConfig.connectResult ?? ConnectResult.FirmwareErrors
};
this.dispatchTypedEvent("connect", new ConnectEvent(result));

View File

@ -87,25 +87,25 @@ export class Abstraction {
public async getPrintStatus(): Promise<PrintStatus> {
const packet = await this.send(PacketGenerator.printStatus());
if (packet.getCommand() === ResponseCommandId.In_PrintError) {
Validators.u8ArrayLengthEquals(packet.getData(), 1);
if (packet.command === ResponseCommandId.In_PrintError) {
Validators.u8ArrayLengthEquals(packet.data, 1);
throw new PrintError(
`Print error (${ResponseCommandId[packet.getCommand()]} packet received)`,
packet.getData()[0]
`Print error (${ResponseCommandId[packet.command]} packet received)`,
packet.data[0]
);
}
Validators.u8ArrayLengthAtLeast(packet.getData(), 4); // can be 8, 10, but ignore it for now
Validators.u8ArrayLengthAtLeast(packet.data, 4); // can be 8, 10, but ignore it for now
const r = new SequentialDataReader(packet.getData());
const r = new SequentialDataReader(packet.data);
const page = r.readI16();
const pagePrintProgress = r.readI8();
const pageFeedProgress = r.readI8();
if (packet.getData().length === 10) {
if (packet.dataLength === 10) {
r.skip(2);
const error = r.readI8();
throw new PrintError(`Print error (${ResponseCommandId[packet.getCommand()]} packet flag)`, error);
throw new PrintError(`Print error (${ResponseCommandId[packet.command]} packet flag)`, error);
}
return { page, pagePrintProgress, pageFeedProgress };
@ -113,8 +113,8 @@ export class Abstraction {
public async connectResult(): Promise<ConnectResult> {
const packet = await this.send(PacketGenerator.connect());
Validators.u8ArrayLengthEquals(packet.getData(), 1);
return packet.getData()[0] as ConnectResult;
Validators.u8ArrayLengthAtLeast(packet.data, 1);
return packet.data[0] as ConnectResult;
}
public async getPrinterStatusData(): Promise<PrinterStatusData> {
@ -122,10 +122,10 @@ export class Abstraction {
const packet = await this.send(PacketGenerator.getPrinterStatusData());
let supportColor = 0;
if (packet.getData().length > 12) {
supportColor = packet.getData()[10];
if (packet.dataLength > 12) {
supportColor = packet.data[10];
let n = packet.getData()[11] * 100 + packet.getData()[12];
let n = packet.data[11] * 100 + packet.data[12];
if (n >= 204 && n < 300) {
protocolVersion = 3;
}
@ -142,9 +142,9 @@ export class Abstraction {
public async getPrinterModel(): Promise<PrinterModelId> {
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.PrinterModelId));
Validators.u8ArrayLengthEquals(packet.getData(), 2);
Validators.u8ArrayLengthEquals(packet.data, 2);
const id = Utils.bytesToI16(packet.getData());
const id = Utils.bytesToI16(packet.data);
if (id in PrinterModelId) {
return id as PrinterModelId;
@ -166,11 +166,11 @@ export class Abstraction {
consumablesType: LabelType.Invalid,
};
if (packet.getData().length === 1) {
if (packet.dataLength === 1) {
return info;
}
const r = new SequentialDataReader(packet.getData());
const r = new SequentialDataReader(packet.data);
info.tagPresent = true;
info.uuid = Utils.bufToHex(r.readBytes(8), "");
info.barCode = r.readVString();
@ -194,8 +194,8 @@ export class Abstraction {
};
// originally expected packet length is bound to model id, but we make it more robust and simple
const len = packet.getData().length;
const r = new SequentialDataReader(packet.getData());
const len = packet.dataLength;
const r = new SequentialDataReader(packet.data);
if (len === 10) {
// d110
@ -235,38 +235,38 @@ export class Abstraction {
public async getBatteryChargeLevel(): Promise<BatteryChargeLevel> {
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.BatteryChargeLevel));
Validators.u8ArrayLengthEquals(packet.getData(), 1);
return packet.getData()[0] as BatteryChargeLevel;
Validators.u8ArrayLengthEquals(packet.data, 1);
return packet.data[0] as BatteryChargeLevel;
}
public async getAutoShutDownTime(): Promise<AutoShutdownTime> {
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.AutoShutdownTime));
Validators.u8ArrayLengthEquals(packet.getData(), 1);
return packet.getData()[0] as AutoShutdownTime;
Validators.u8ArrayLengthEquals(packet.data, 1);
return packet.data[0] as AutoShutdownTime;
}
public async getLabelType(): Promise<LabelType> {
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.LabelType));
Validators.u8ArrayLengthEquals(packet.getData(), 1);
return packet.getData()[0] as LabelType;
Validators.u8ArrayLengthEquals(packet.data, 1);
return packet.data[0] as LabelType;
}
public async getPrinterSerialNumber(): Promise<string> {
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.SerialNumber));
Validators.u8ArrayLengthAtLeast(packet.getData(), 1);
return Utils.u8ArrayToString(packet.getData());
Validators.u8ArrayLengthAtLeast(packet.data, 1);
return Utils.u8ArrayToString(packet.data);
}
public async getPrinterBluetoothMacAddress(): Promise<string> {
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.BluetoothAddress));
Validators.u8ArrayLengthAtLeast(packet.getData(), 1);
return Utils.bufToHex(packet.getData().reverse(), ":");
Validators.u8ArrayLengthAtLeast(packet.data, 1);
return Utils.bufToHex(packet.data.reverse(), ":");
}
public async isSoundEnabled(soundType: SoundSettingsItemType): Promise<boolean> {
const packet = await this.send(PacketGenerator.getSoundSettings(soundType));
Validators.u8ArrayLengthEquals(packet.getData(), 3);
const value = !!packet.getData()[2];
Validators.u8ArrayLengthEquals(packet.data, 3);
const value = !!packet.data[2];
return value;
}

View File

@ -13,6 +13,7 @@ export enum RequestCommandId {
PrintEmptyRow = 0x84, // -124
PrintEnd = 0xf3,
PrinterInfo = 0x40, // See PrinterInfoType
PrinterConfig = 0xaf,
PrinterStatusData = 0xa5,
PrintQuantity = 0x15,
PrintStart = 0x01,
@ -27,7 +28,7 @@ export enum RequestCommandId {
SetPageSize = 0x13, // 2, 4 or 6 bytes
SoundSettings = 0x58,
Unknown1 = 0x0b, // some info request (niimbot app), 01 long 02 short
WriteRFID = 0x70,
WriteRFID = 0x70, // same as GetVolumeLevel???
}
export enum ResponseCommandId {
@ -42,6 +43,7 @@ export enum ResponseCommandId {
In_PrintClear = 0x30,
In_PrintEmptyRows = 0xd3,
In_PrintEnd = 0xf4,
In_PrinterConfig = 0xbf,
In_PrinterInfoAutoShutDownTime = 0x47,
In_PrinterInfoBluetoothAddress = 0x4d,
In_PrinterInfoDensity = 0x41,
@ -65,7 +67,6 @@ export enum ResponseCommandId {
In_SetPageSize = 0x14,
In_SoundSettings = 0x68,
In_Unknown1 = 0xe4,
In_Unknown2 = 0xc2,
}
export enum PrinterInfoType {

View File

@ -5,45 +5,60 @@ export class NiimbotPacket {
public static readonly HEAD = new Uint8Array([0x55, 0x55]);
public static readonly TAIL = new Uint8Array([0xaa, 0xaa]);
private commandId: RequestCommandId;
private data: Uint8Array;
private validResponseIds: ResponseCommandId[];
private _command: RequestCommandId;
private _data: Uint8Array;
private _validResponseIds: ResponseCommandId[];
/** There can be no response after this request. */
private oneWay: boolean;
private _oneWay: 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.oneWay = false;
constructor(command: number, data: Uint8Array | number[], validResponseIds: ResponseCommandId[] = []) {
this._command = command;
this._data = data instanceof Uint8Array ? data : new Uint8Array(data);
this._validResponseIds = validResponseIds;
this._oneWay = false;
}
public setNoResponse(): void {
this.oneWay = true;
/** Data length (header, command, dataLen, checksum, tail are excluded). */
public get dataLength(): number {
return this._data.length;
}
public get length(): number {
return (
NiimbotPacket.HEAD.length + // head
1 + // cmd
1 + // dataLength
this.dataLength +
1 + // checksum
NiimbotPacket.TAIL.length
);
}
public isOneWay(): boolean {
return this.oneWay;
public set oneWay(value: boolean) {
this._oneWay = value;
}
public getValidResponseIds(): ResponseCommandId[] {
return this.validResponseIds;
public get oneWay(): boolean {
return this._oneWay;
}
public getCommand(): number {
return this.commandId;
public get validResponseIds(): ResponseCommandId[] {
return this._validResponseIds;
}
public getData(): Uint8Array {
return this.data;
public get command(): number {
return this._command;
}
public checksum(): number {
public get data(): Uint8Array {
return this._data;
}
public get checksum(): number {
let checksum = 0;
checksum ^= this.commandId;
checksum ^= this.data.length;
this.data.forEach((i: number) => (checksum ^= i));
checksum ^= this._command;
checksum ^= this._data.length;
this._data.forEach((i: number) => (checksum ^= i));
return checksum;
}
@ -53,7 +68,7 @@ export class NiimbotPacket {
NiimbotPacket.HEAD.length + // head
1 + // cmd
1 + // dataLength
this.data.length +
this._data.length +
1 + // checksum
NiimbotPacket.TAIL.length
);
@ -65,21 +80,21 @@ export class NiimbotPacket {
arr.set(NiimbotPacket.HEAD, pos);
pos += NiimbotPacket.HEAD.length;
arr[pos] = this.commandId;
arr[pos] = this._command;
pos += 1;
arr[pos] = this.data.length;
arr[pos] = this._data.length;
pos += 1;
arr.set(this.data, pos);
pos += this.data.length;
arr.set(this._data, pos);
pos += this._data.length;
arr[pos] = this.checksum();
arr[pos] = this.checksum;
pos += 1;
arr.set(NiimbotPacket.TAIL, pos);
if(this.commandId === RequestCommandId.Connect) {
if (this._command === RequestCommandId.Connect) {
// const newArr = new Uint8Array(arr.length + 1);
// newArr[0] = 3;
// newArr.set(arr, 1);
@ -119,7 +134,7 @@ export class NiimbotPacket {
const checksum: number = buf.getUint8(4 + dataLen);
const packet = new NiimbotPacket(cmd, data);
if (packet.checksum() !== checksum) {
if (packet.checksum !== checksum) {
throw new Error("Invalid packet checksum");
}

View File

@ -229,7 +229,7 @@ export class PacketGenerator {
// https://github.com/ayufan/niimprint-web/blob/main/cmds.js#L215
public static printEmptySpace(pos: number, repeats: number): NiimbotPacket {
const packet = new NiimbotPacket(RequestCommandId.PrintEmptyRow, [...Utils.u16ToBytes(pos), repeats]);
packet.setNoResponse();
packet.oneWay = true;
return packet;
}
@ -247,7 +247,7 @@ export class PacketGenerator {
repeats,
...data,
]);
packet.setNoResponse();
packet.oneWay = true;
return packet;
}
@ -268,7 +268,7 @@ export class PacketGenerator {
...indexes,
]);
packet.setNoResponse();
packet.oneWay = true;
return packet;
}