Implement PrinterCheckLine

This commit is contained in:
MultiMote 2025-01-24 18:50:06 +03:00
parent a453dfe497
commit 1ab81bc0bd
10 changed files with 157 additions and 75 deletions

@ -57,10 +57,10 @@ PrintStart [1(u8)]
Page:
```
PrintClear [1(u8)]
PageStart [1(u8)]
SetPageSize [rows(u16), cols(u16)]
PrintEmptyRow | PrintBitmapRow | PrintBitmapRowIndexed
PrinterCheckLine [line(u16), 1(u8)] - after every 200 lines (data example: 00c701, 018f01, 025701, 031f01, ...)
PageEnd [1(u8)]
```

@ -90,7 +90,7 @@ export abstract class NiimbotAbstractClient extends EventEmitter<ClientEventMap>
await this.sendPacket(packet, true);
if (packet.oneWay) {
return new NiimbotPacket(ResponseCommandId.Invalid, []); // or undefined is better?
return new NiimbotPacket(ResponseCommandId.In_Invalid, []); // or undefined is better?
}
return this.waitForPacket(packet.validResponseIds, true, timeoutMs);

@ -2,7 +2,7 @@ import { Utils } from ".";
/** @category Image encoder */
export type ImageRow = {
dataType: "void" | "pixels";
dataType: "void" | "pixels" | "check";
rowNumber: number;
repeat: number;
blackPixelsCount: number;
@ -83,14 +83,31 @@ export class ImageEncoder {
} else {
rowsData.push(newPart);
}
const sendRowCheck = row % 200 === 199;
if (sendRowCheck) {
rowsData.push({
dataType: "check",
rowNumber: row,
repeat: 0,
rowData: undefined,
blackPixelsCount: 0,
});
}
}
}
return { cols, rows, rowsData };
}
/** printDirection = "left" rotates image to 90 degrees clockwise */
public static isPixelNonWhite(iData: ImageData, x: number, y: number, printDirection: PrintDirection = "left"): boolean {
public static isPixelNonWhite(
iData: ImageData,
x: number,
y: number,
printDirection: PrintDirection = "left"
): boolean {
let idx = y * iData.width + x;
if (printDirection === "left") {

@ -55,7 +55,7 @@ export enum RequestCommandId {
* @category Packets
**/
export enum ResponseCommandId {
Invalid = -1,
In_Invalid = -1,
In_NotSupported = 0x00,
In_Connect = 0xc2,
In_CalibrateHeight = 0x69,

@ -10,9 +10,20 @@ import {
commandsMap,
NiimbotCrc32Packet,
} from ".";
import { EncodedImage, ImageEncoder, ImageRow } from "../image_encoder";
import { EncodedImage, ImageEncoder } from "../image_encoder";
import { Utils } from "../utils";
export interface ImagePacketsGenerateOptions {
/** Mode for "black pixel count" section of bitmap packet. */
countsMode?: "auto" | "split" | "total";
/** Disable PrintBitmapRowIndexed packet. */
noIndexPacket?: boolean;
/** Send PrinterCheckLine every 200 line. */
enableCheckLine?: boolean;
/** Printer head resolution. Used for "black pixel count" section calculation. */
printheadPixels?: number;
}
/**
* A helper class that generates various types of packets.
* @category Packets
@ -178,8 +189,14 @@ export class PacketGenerator {
return this.mapped(TX.PrintEmptyRow, [...Utils.u16ToBytes(pos), repeats]);
}
public static printBitmapRow(pos: number, repeats: number, data: Uint8Array, printheadPixels?: number): NiimbotPacket {
const counts = Utils.countPixelsForBitmapPacket(data, printheadPixels ?? 0);
public static printBitmapRow(
pos: number,
repeats: number,
data: Uint8Array,
printheadPixels: number,
countsMode: "auto" | "split" | "total" = "auto"
): NiimbotPacket {
const counts = Utils.countPixelsForBitmapPacket(data, printheadPixels, countsMode);
return this.mapped(TX.PrintBitmapRow, [...Utils.u16ToBytes(pos), ...counts.parts, repeats, ...data]);
}
@ -189,9 +206,10 @@ export class PacketGenerator {
pos: number,
repeats: number,
data: Uint8Array,
printheadPixels?: number
printheadPixels: number,
countsMode: "auto" | "split" | "total" = "auto"
): NiimbotPacket {
const counts = Utils.countPixelsForBitmapPacket(data, printheadPixels ?? 0);
const counts = Utils.countPixelsForBitmapPacket(data, printheadPixels ?? 0, countsMode);
const indexes: Uint8Array = ImageEncoder.indexPixels(data);
if (counts.total > 6) {
@ -209,18 +227,50 @@ export class PacketGenerator {
return this.mapped(TX.WriteRFID, data);
}
public static writeImageData(image: EncodedImage, printheadPixels?: number): NiimbotPacket[] {
return image.rowsData.map((p: ImageRow) => {
if (p.dataType === "pixels") {
if (p.blackPixelsCount > 6) {
return this.printBitmapRow(p.rowNumber, p.repeat, p.rowData!, printheadPixels);
public static checkLine(line: number): NiimbotPacket {
return this.mapped(TX.PrinterCheckLine, [...Utils.u16ToBytes(line), 0x01]);
}
public static writeImageData(image: EncodedImage, options?: ImagePacketsGenerateOptions): NiimbotPacket[] {
let out: NiimbotPacket[] = [];
for (const d of image.rowsData) {
if (d.dataType === "pixels") {
if (d.blackPixelsCount <= 6 && !options?.noIndexPacket) {
out.push(
this.printBitmapRowIndexed(
d.rowNumber,
d.repeat,
d.rowData!,
options?.printheadPixels ?? 0,
options?.countsMode ?? "auto"
)
);
} else {
return this.printBitmapRowIndexed(p.rowNumber, p.repeat, p.rowData!, printheadPixels);
out.push(
this.printBitmapRow(
d.rowNumber,
d.repeat,
d.rowData!,
options?.printheadPixels ?? 0,
options?.countsMode ?? "auto"
)
);
}
} else {
return this.printEmptySpace(p.rowNumber, p.repeat);
continue;
}
});
if (d.dataType === "check" && options?.enableCheckLine) {
out.push(this.checkLine(d.rowNumber));
continue;
}
if (d.dataType === "void") {
out.push(this.printEmptySpace(d.rowNumber, d.repeat));
}
}
return out;
}
public static printTestPage(): NiimbotPacket {
@ -232,11 +282,11 @@ export class PacketGenerator {
}
public static startFirmwareUpgrade(version: string): NiimbotPacket {
if(!/^\d+\.\d+$/.test(version)) {
if (!/^\d+\.\d+$/.test(version)) {
throw new Error("Invalid version format (x.x expected)");
}
const [a, b] = version.split(".").map(p => parseInt(p));
const [a, b] = version.split(".").map((p) => parseInt(p));
return this.mapped(TX.StartFirmwareUpgrade, [a, b]);
}

@ -17,20 +17,22 @@ export class B1PrintTask extends AbstractPrintTask {
override printPage(image: EncodedImage, quantity?: number): Promise<void> {
this.checkAddPage(quantity ?? 1);
return this.abstraction.sendAll([
PacketGenerator.pageStart(),
PacketGenerator.setPageSizeV3(image.rows, image.cols, quantity ?? 1),
...PacketGenerator.writeImageData(image, this.printheadPixels()),
PacketGenerator.pageEnd(),
], this.printOptions.pageTimeoutMs);
return this.abstraction.sendAll(
[
PacketGenerator.pageStart(),
PacketGenerator.setPageSizeV3(image.rows, image.cols, quantity ?? 1),
...PacketGenerator.writeImageData(image, { printheadPixels: this.printheadPixels() }),
PacketGenerator.pageEnd(),
],
this.printOptions.pageTimeoutMs
);
}
override waitForFinished(): Promise<void> {
this.abstraction.setPacketTimeout(this.printOptions.statusTimeoutMs);
return this.abstraction.waitUntilPrintFinishedByStatusPoll(
this.printOptions.totalPages,
this.printOptions.statusPollIntervalMs
).finally(() => this.abstraction.setDefaultPacketTimeout());
return this.abstraction
.waitUntilPrintFinishedByStatusPoll(this.printOptions.totalPages, this.printOptions.statusPollIntervalMs)
.finally(() => this.abstraction.setDefaultPacketTimeout());
}
}

@ -18,22 +18,28 @@ export class B21V1PrintTask extends AbstractPrintTask {
this.checkAddPage(quantity ?? 1);
for (let i = 0; i < (quantity ?? 1); i++) {
await this.abstraction.sendAll([
PacketGenerator.printClear(),
PacketGenerator.pageStart(),
PacketGenerator.setPageSizeV2(image.rows, image.cols),
...PacketGenerator.writeImageData(image, this.printheadPixels()),
PacketGenerator.pageEnd(),
], this.printOptions.pageTimeoutMs);
await this.abstraction.sendAll(
[
// PacketGenerator.printClear(),
PacketGenerator.pageStart(),
PacketGenerator.setPageSizeV2(image.rows, image.cols),
...PacketGenerator.writeImageData(image, {
countsMode: "total",
enableCheckLine: true,
printheadPixels: this.printheadPixels(),
}),
PacketGenerator.pageEnd(),
],
this.printOptions.pageTimeoutMs
);
}
}
override waitForFinished(): Promise<void> {
this.abstraction.setPacketTimeout(this.printOptions.statusTimeoutMs);
return this.abstraction.waitUntilPrintFinishedByPrintEndPoll(
this.printOptions.totalPages,
this.printOptions.statusPollIntervalMs
).finally(() => this.abstraction.setDefaultPacketTimeout());
return this.abstraction
.waitUntilPrintFinishedByPrintEndPoll(this.printOptions.totalPages, this.printOptions.statusPollIntervalMs)
.finally(() => this.abstraction.setDefaultPacketTimeout());
}
}

@ -17,22 +17,24 @@ export class D110PrintTask extends AbstractPrintTask {
override printPage(image: EncodedImage, quantity?: number): Promise<void> {
this.checkAddPage(quantity ?? 1);
return this.abstraction.sendAll([
PacketGenerator.printClear(),
PacketGenerator.pageStart(),
PacketGenerator.setPageSizeV2(image.rows, image.cols),
PacketGenerator.setPrintQuantity(quantity ?? 1),
...PacketGenerator.writeImageData(image, this.printheadPixels()),
PacketGenerator.pageEnd(),
], this.printOptions.pageTimeoutMs);
return this.abstraction.sendAll(
[
PacketGenerator.printClear(),
PacketGenerator.pageStart(),
PacketGenerator.setPageSizeV2(image.rows, image.cols),
PacketGenerator.setPrintQuantity(quantity ?? 1),
...PacketGenerator.writeImageData(image, { printheadPixels: this.printheadPixels() }),
PacketGenerator.pageEnd(),
],
this.printOptions.pageTimeoutMs
);
}
override waitForFinished(): Promise<void> {
this.abstraction.setPacketTimeout(this.printOptions.statusTimeoutMs);
return this.abstraction.waitUntilPrintFinishedByStatusPoll(
this.printOptions.totalPages,
this.printOptions.statusPollIntervalMs
).finally(() => this.abstraction.setDefaultPacketTimeout());
return this.abstraction
.waitUntilPrintFinishedByStatusPoll(this.printOptions.totalPages, this.printOptions.statusPollIntervalMs)
.finally(() => this.abstraction.setDefaultPacketTimeout());
}
}

@ -17,14 +17,17 @@ export class OldD11PrintTask extends AbstractPrintTask {
override printPage(image: EncodedImage, quantity: number): Promise<void> {
this.checkAddPage(quantity ?? 1);
return this.abstraction.sendAll([
PacketGenerator.printClear(),
PacketGenerator.pageStart(),
PacketGenerator.setPageSizeV1(image.rows),
PacketGenerator.setPrintQuantity(quantity ?? 1),
...PacketGenerator.writeImageData(image, this.printheadPixels()),
PacketGenerator.pageEnd(),
], this.printOptions.pageTimeoutMs);
return this.abstraction.sendAll(
[
PacketGenerator.printClear(),
PacketGenerator.pageStart(),
PacketGenerator.setPageSizeV1(image.rows),
PacketGenerator.setPrintQuantity(quantity ?? 1),
...PacketGenerator.writeImageData(image, { printheadPixels: this.printheadPixels() }),
PacketGenerator.pageEnd(),
],
this.printOptions.pageTimeoutMs
);
}
override waitForFinished(): Promise<void> {

@ -1,5 +1,5 @@
import { EncodedImage } from "../image_encoder";
import { PacketGenerator } from "../packets";
import { PacketGenerator } from "../packets";
import { AbstractPrintTask } from "./AbstractPrintTask";
/**
@ -10,27 +10,29 @@ export class V5PrintTask extends AbstractPrintTask {
return this.abstraction.sendAll([
PacketGenerator.setDensity(this.printOptions.density),
PacketGenerator.setLabelType(this.printOptions.labelType),
PacketGenerator.printStartV5(this.printOptions.totalPages, 0, 0)
PacketGenerator.printStartV5(this.printOptions.totalPages, 0, 0),
]);
}
override printPage(image: EncodedImage, quantity?: number): Promise<void> {
this.checkAddPage(quantity ?? 1);
return this.abstraction.sendAll([
PacketGenerator.pageStart(),
PacketGenerator.setPageSizeV4(image.rows, image.cols, quantity ?? 1, 0, false),
...PacketGenerator.writeImageData(image, this.printheadPixels()),
PacketGenerator.pageEnd(),
], this.printOptions.pageTimeoutMs);
return this.abstraction.sendAll(
[
PacketGenerator.pageStart(),
PacketGenerator.setPageSizeV4(image.rows, image.cols, quantity ?? 1, 0, false),
...PacketGenerator.writeImageData(image, { printheadPixels: this.printheadPixels() }),
PacketGenerator.pageEnd(),
],
this.printOptions.pageTimeoutMs
);
}
override waitForFinished(): Promise<void> {
this.abstraction.setPacketTimeout(this.printOptions.statusTimeoutMs);
return this.abstraction.waitUntilPrintFinishedByStatusPoll(
this.printOptions.totalPages ?? 1,
this.printOptions.statusPollIntervalMs
).finally(() => this.abstraction.setDefaultPacketTimeout());
return this.abstraction
.waitUntilPrintFinishedByStatusPoll(this.printOptions.totalPages ?? 1, this.printOptions.statusPollIntervalMs)
.finally(() => this.abstraction.setDefaultPacketTimeout());
}
}