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

This commit is contained in:
Bot 2024-07-28 00:00:01 +03:00 committed by multimote
parent 16324d5f5f
commit 266a392401
16 changed files with 334 additions and 272 deletions

View File

@ -0,0 +1,28 @@
<script lang="ts">
import { fabric } from "fabric";
import FaIcon from "./FaIcon.svelte";
export let selectedObject: fabric.Object;
export let valueUpdated: () => void;
const putToCenterV = () => {
selectedObject.canvas?.centerObjectH(selectedObject);
selectedObject.centerV();
valueUpdated();
};
const putToCenterH = () => {
selectedObject.centerH();
valueUpdated();
};
</script>
<button class="btn btn-sm btn-secondary" on:click={putToCenterV} title="Center vertically">
<FaIcon icon="up-down" />
</button>
<button class="btn btn-sm btn-secondary" on:click={putToCenterH} title="Center horizontally">
<FaIcon icon="left-right" />
</button>
<style>
</style>

View File

@ -9,6 +9,7 @@
import FaIcon from "./FaIcon.svelte";
import PrintPreview from "./PrintPreview.svelte";
import TextParamsPanel from "./TextParamsControls.svelte";
import GenericObjectParamsControls from "./GenericObjectParamsControls.svelte";
let htmlCanvas: HTMLCanvasElement;
let fabricCanvas: fabric.Canvas;
@ -94,9 +95,10 @@
if (name === "text") {
const obj = new fabric.IText("Text", {
...fabricObjectDefaults,
fontFamily: "Arial",
fontFamily: "Arial"
});
fabricCanvas.add(obj);
obj.center();
} else if (name === "line") {
const obj = new fabric.Line([10, 10, 10 + defaultSize, 10], {
...fabricObjectDefaults,
@ -111,8 +113,8 @@
mt: false,
mb: false,
});
fabricCanvas.add(obj);
obj.centerV();
} else if (name === "circle") {
const obj = new fabric.Circle({
...fabricObjectDefaults,
@ -122,6 +124,7 @@
strokeWidth: 3,
});
fabricCanvas.add(obj);
obj.centerV();
} else if (name === "rectangle") {
const obj = new fabric.Rect({
...fabricObjectDefaults,
@ -132,6 +135,7 @@
strokeWidth: 3,
});
fabricCanvas.add(obj);
obj.centerV();
}
};
@ -288,8 +292,9 @@
<button class="btn btn-sm btn-danger me-1" on:click={deleteSelected}><FaIcon icon="trash" /></button>
{/if}
{#if selectedCount === 1}
{#if selectedObject && selectedCount === 1}
<button class="btn btn-sm btn-secondary me-1" on:click={cloneSelected}><FaIcon icon="clone" /></button>
<GenericObjectParamsControls {selectedObject} valueUpdated={() => fabricCanvas.requestRenderAll()} />
{/if}
{#if selectedObject instanceof fabric.IText}

View File

@ -7,19 +7,14 @@
import {
type EncodedImage,
ImageEncoder,
Utils,
LabelType,
PacketGenerator,
ProtocolVariant,
ResponseCommandId,
PrintError,
ProtocolVersion,
} from "@mmote/niimbluelib";
import type { LabelProps } from "../types";
import FaIcon from "./FaIcon.svelte";
export let onClosed: () => void;
export let labelProps: LabelProps;
export let imageCallback: () => string;
let modalElement: HTMLElement;
@ -34,7 +29,7 @@
let thresholdValue: number = 140;
let imgData: ImageData;
let imgContext: CanvasRenderingContext2D;
let protoVariant: ProtocolVariant = ProtocolVariant.V3;
let printTaskVersion: ProtocolVersion = ProtocolVersion.V3;
let statusTimer: NodeJS.Timeout | undefined = undefined;
let printError: boolean = false;
@ -54,7 +49,7 @@
const onPrint = async () => {
printError = false;
const encoded: EncodedImage = ImageEncoder.encodeCanvas(previewCanvas, labelProps.startPos);
const packets = PacketGenerator.generatePrintSequence(protoVariant, encoded, { quantity, density });
const packets = PacketGenerator.generatePrintSequence(printTaskVersion, encoded, { quantity, density });
for (let i = 0; i < packets.length; i++) {
sendProgress = Math.round(((i + 1) / packets.length) * 100);
@ -92,15 +87,14 @@
};
onMount(() => {
// create image from fabric canvas to work with
const img = new Image();
img.onload = () => {
previewCanvas.width = img.width;
previewCanvas.height = img.height;
imgContext = previewCanvas.getContext("2d")!;
imgContext.drawImage(img, 0, 0, img.width, img.height);
imgData = imgContext.getImageData(0, 0, img.width, img.height);
updatePreview();
};
img.src = imageCallback();
@ -111,6 +105,12 @@
cancelPrint();
onClosed();
});
const taskVer = $printerClient?.getCapabilities().printTaskVersion;
if (taskVer !== undefined && taskVer !== ProtocolVersion.UNKNOWN) {
console.log(`Detected print task version: ${ProtocolVersion[taskVer]}`)
printTaskVersion = taskVer;
}
});
onDestroy(() => {
@ -192,10 +192,13 @@
</div>
<div class="input-group input-group-sm">
<span class="input-group-text">Protocol variant</span>
<select class="form-select" bind:value={protoVariant}>
<option value={ProtocolVariant.V3}>V3 - D110</option>
<option value={ProtocolVariant.V4}>V4 - B1</option>
<span class="input-group-text">Print task version</span>
<select class="form-select" bind:value={printTaskVersion}>
<option value={ProtocolVersion.V1} disabled>V1 - NOT IMPLEMENTED</option>
<option value={ProtocolVersion.V2} disabled>V2 - NOT IMPLEMENTED</option>
<option value={ProtocolVersion.V3}>V3 - D110</option>
<option value={ProtocolVersion.V4}>V4 - B1</option>
<option value={ProtocolVersion.V5} disabled>V5 - NOT IMPLEMENTED</option>
</select>
</div>

View File

@ -1,5 +1,5 @@
<script lang="ts">
import { PrinterModelId, type RfidInfo } from "@mmote/niimbluelib";
import { PrinterModel, type RfidInfo } from "@mmote/niimbluelib";
import { printerClient, connectedPrinterName, connectionState, initClient, heartbeatData, printerConfig } from "../stores";
import type { ConnectionType } from "../types";
import FaIcon from "./FaIcon.svelte";
@ -36,11 +36,6 @@
// clearInterval(timer);
$printerClient.stopHeartbeat();
};
const test3 = async () => {
const id = await $printerClient.abstraction.getPrinterModel();
alert(`Printer model: ${PrinterModelId[id]}`);
};
</script>
<div class="input-group flex-nowrap justify-content-end">
@ -49,24 +44,33 @@
><FaIcon icon="gear" />
</button>
<div class="dropdown-menu p-1">
<div>
Model: {$printerConfig.model === undefined ? "?" : (PrinterModel[$printerConfig.model] ?? "Unknown")}
</div>
<div>
Printer info:
<pre>{JSON.stringify($printerConfig, null, 1)}</pre>
</div>
<div>
Rfid info:
<pre>{JSON.stringify(rfidInfo || {}, null, 1)}</pre>
</div>
<div>
Heartbeat data:
<pre>{JSON.stringify($heartbeatData, null, 1)}</pre>
</div>
<div>
Rfid info:
<pre>{JSON.stringify(rfidInfo || {}, null, 1)}</pre>
Tests
</div>
<button class="btn btn-sm btn-primary" on:click={getRfidInfo}>RfidInfo</button>
<button class="btn btn-sm btn-primary" on:click={startHeartbeat}>start heartbeat (test)</button>
<button class="btn btn-sm btn-primary" on:click={stopHeartbeat}>stop heartbeat (test)</button>
<button class="btn btn-sm btn-primary" on:click={test3}>Guess printer model</button>
<button class="btn btn-sm btn-primary" on:click={getRfidInfo}>Rfid</button>
<button class="btn btn-sm btn-primary" on:click={startHeartbeat}>Heartbeat on</button>
<button class="btn btn-sm btn-primary" on:click={stopHeartbeat}>Heartbeat off</button>
</div>
<span class="input-group-text">{$connectedPrinterName}</span>
{:else}

View File

@ -54,14 +54,17 @@
{#if selectedText}
<!-- <div class="d-flex flex-wrap gap-1"> -->
<button
title="Align text: Left"
class="btn btn-sm {selectedText.textAlign === 'left' ? 'btn-secondary' : ''}"
on:click={() => setAlign("left")}><FaIcon icon="align-left" /></button
>
<button
title="Align text: Center"
class="btn btn-sm {selectedText.textAlign === 'center' ? 'btn-secondary' : ''}"
on:click={() => setAlign("center")}><FaIcon icon="align-center" /></button
>
<button
title="Align text: Right"
class="btn btn-sm {selectedText.textAlign === 'right' ? 'btn-secondary' : ''}"
on:click={() => setAlign("right")}><FaIcon icon="align-right" /></button
>

View File

@ -2,25 +2,12 @@ export const copyImageData = (iData: ImageData): ImageData => {
return new ImageData(new Uint8ClampedArray(iData.data), iData.width, iData.height);
};
export const convertImgDataToBlackAndWhite = (iData: ImageData, threshold = 0xff): ImageData => {
for (let x = 0; x < iData.width; x++) {
for (let i = 0; i < iData.data.byteLength / 4; i++) {
const pos = i * 4;
const b =
iData.data[pos] >= threshold || iData.data[pos + 1] >= threshold || iData.data[pos + 2] >= threshold ? 0xff : 0;
iData.data[pos] = b;
iData.data[pos + 1] = b;
iData.data[pos + 2] = b;
iData.data[pos + 3] = 0xff;
}
}
return iData;
};
// Original code is taken from https://github.com/NielsLeenheer/CanvasDither
/**
* Change the image to blank and white using a simple threshold
*
*
* @param {object} image The imageData of a Canvas 2d context
* @param {number} threshold Threshold value (0-255)
* @return {object} The resulting imageData
@ -29,7 +16,6 @@ export const convertImgDataToBlackAndWhite = (iData: ImageData, threshold = 0xff
export const threshold = (image: ImageData, threshold: number): ImageData => {
for (let i = 0; i < image.data.length; i += 4) {
const luminance = image.data[i] * 0.299 + image.data[i + 1] * 0.587 + image.data[i + 2] * 0.114;
const value = luminance < threshold ? 0 : 255;
image.data.fill(value, i, i + 3);
}
@ -37,68 +23,11 @@ export const threshold = (image: ImageData, threshold: number): ImageData => {
return image;
};
/**
* Change the image to blank and white using the Bayer algorithm
*
* @param {object} image The imageData of a Canvas 2d context
* @param {number} threshold Threshold value (0-255)
* @return {object} The resulting imageData
*
*/
export const bayer = (image: ImageData, threshold: number): ImageData => {
const thresholdMap = [
[15, 135, 45, 165],
[195, 75, 225, 105],
[60, 180, 30, 150],
[240, 120, 210, 90],
];
for (let i = 0; i < image.data.length; i += 4) {
const luminance = image.data[i] * 0.299 + image.data[i + 1] * 0.587 + image.data[i + 2] * 0.114;
const x = (i / 4) % image.width;
const y = Math.floor(i / 4 / image.width);
const map = Math.floor((luminance + thresholdMap[x % 4][y % 4]) / 2);
const value = map < threshold ? 0 : 255;
image.data.fill(value, i, i + 3);
}
return image;
};
/**
* Change the image to blank and white using the Floyd-Steinberg algorithm
*
* @param {object} image The imageData of a Canvas 2d context
* @return {object} The resulting imageData
*
*/
export const floydsteinberg = (image: ImageData, threshold: number): ImageData => {
const width = image.width;
const luminance = new Uint8ClampedArray(image.width * image.height);
for (let l = 0, i = 0; i < image.data.length; l++, i += 4) {
luminance[l] = image.data[i] * 0.299 + image.data[i + 1] * 0.587 + image.data[i + 2] * 0.114;
}
for (let l = 0, i = 0; i < image.data.length; l++, i += 4) {
const value = luminance[l] < threshold ? 0 : 255;
const error = Math.floor((luminance[l] - value) / 16);
image.data.fill(value, i, i + 3);
luminance[l + 1] += error * 7;
luminance[l + width - 1] += error * 3;
luminance[l + width] += error * 5;
luminance[l + width + 1] += error * 1;
}
return image;
};
/**
* Change the image to blank and white using the Atkinson algorithm
*
* @param {object} image The imageData of a Canvas 2d context
* @param {number} threshold Threshold value (0-255)
* @return {object} The resulting imageData
*
*/
@ -125,35 +54,3 @@ export const atkinson = (image: ImageData, threshold: number): ImageData => {
return image;
};
// https://observablehq.com/@tmcw/dithering
export const atkinson2 = (image: ImageData, threshold: number):ImageData => {
let GRAYS = 256;
let THRESHOLD = [];
for (let i = 0; i < GRAYS; i++) {
THRESHOLD.push(i < (GRAYS >> 1) ? 0 : GRAYS - 1);
}
let clone = new ImageData(new Uint8ClampedArray(image.data), image.width, image.height);
function px(x:number, y:number) {
return (x * 4) + (y * image.width * 4);
}
for (let y = 0; y < image.height; y++) {
for (let x = 0; x < image.width; x++) {
let oldPixel = clone.data[px(x, y)];
let grayNew = THRESHOLD[oldPixel];
let grayErr = (oldPixel - grayNew) >> 3;
let newPixel = oldPixel > 125 ? 255 : 0;
clone.data[px(x, y)] = clone.data[px(x, y) + 1] = clone.data[px(x, y) + 2] = newPixel;
clone.data[px(x, y)] =
clone.data[px(x, y) + 1] =
clone.data[px(x, y) + 2] = grayNew;
[[1, 0], [2, 0], [-1, 1], [0, 1], [1, 1], [0, 2]].forEach(([dx, dy]) => {
clone.data[px(x + dx, y + dy)] += grayErr;
});
}
}
return clone;
}

View File

@ -52,7 +52,7 @@ export const initClient = (connectionType: ConnectionType) => {
console.log("onConnect");
connectionState.set("connected");
connectedPrinterName.set(e.info.deviceName ?? "unknown");
printerConfig.set(newClient.getPrinterConfig());
printerConfig.set(newClient.getPrinterInfo());
});
newClient.addEventListener("disconnect", () => {

View File

@ -40,7 +40,7 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
const disconnectListener = async () => {
this.gattServer = undefined;
this.channel = undefined;
this.printerConfig = {};
this.info = {};
this.stopHeartbeat();
this.dispatchTypedEvent("disconnect", new DisconnectEvent());
device.removeEventListener("gattserverdisconnected", disconnectListener);
@ -73,14 +73,14 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
try {
await this.initialNegotiate();
await this.fetchPrinterConfig();
await this.fetchPrinterInfo();
} catch (e) {
console.error(e);
}
const result: ConnectionInfo = {
deviceName: device.name,
result: this.printerConfig.connectResult ?? ConnectResult.FirmwareErrors
result: this.info.connectResult ?? ConnectResult.FirmwareErrors
};
this.dispatchTypedEvent("connect", new ConnectEvent(result));
@ -97,7 +97,7 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
this.gattServer?.disconnect();
this.gattServer = undefined;
this.channel = undefined;
this.printerConfig = {};
this.info = {};
}
/**

View File

@ -1,7 +1,8 @@
import { AutoShutdownTime, BatteryChargeLevel, ConnectResult, NiimbotPacket, PrinterModelId } from "../packets";
import { AutoShutdownTime, BatteryChargeLevel, ConnectResult, NiimbotPacket } from "../packets";
import { TypedEventTarget } from "typescript-event-target";
import { ClientEventMap, HeartbeatEvent } from "./events";
import { Abstraction } from "../packets/abstraction";
import { getPrinterCapabilities, PrinterCapabilities, PrinterModel } from "../printers";
export type ConnectionInfo = {
deviceName?: string;
@ -11,7 +12,7 @@ export type ConnectionInfo = {
export interface PrinterConfig {
connectResult?: ConnectResult;
protocolVersion?: number;
model?: PrinterModelId;
model?: PrinterModel;
serial?: string;
mac?: string;
charge?: BatteryChargeLevel;
@ -20,7 +21,7 @@ export interface PrinterConfig {
export abstract class NiimbotAbstractClient extends TypedEventTarget<ClientEventMap> {
public readonly abstraction: Abstraction;
protected printerConfig: PrinterConfig = {};
protected info: PrinterConfig = {};
private heartbeatTimer?: NodeJS.Timeout;
constructor() {
@ -56,7 +57,7 @@ export abstract class NiimbotAbstractClient extends TypedEventTarget<ClientEvent
/** Send "connect" packet and fetch the protocol version */
protected async initialNegotiate(): Promise<void> {
const cfg = this.printerConfig;
const cfg = this.info;
cfg.connectResult = await this.abstraction.connectResult();
cfg.protocolVersion = 0;
@ -68,18 +69,18 @@ export abstract class NiimbotAbstractClient extends TypedEventTarget<ClientEvent
}
}
public async fetchPrinterConfig(): Promise<PrinterConfig> {
public async fetchPrinterInfo(): Promise<PrinterConfig> {
// console.log(await this.abstraction.getPrinterStatusData());
this.printerConfig.model = await this.abstraction.getPrinterModel();
this.printerConfig.serial = await this.abstraction.getPrinterSerialNumber();
this.printerConfig.mac = await this.abstraction.getPrinterBluetoothMacAddress();
this.printerConfig.charge = await this.abstraction.getBatteryChargeLevel();
this.printerConfig.autoShutdownTime = await this.abstraction.getAutoShutDownTime();
return this.printerConfig;
this.info.model = await this.abstraction.getPrinterModel();
this.info.serial = await this.abstraction.getPrinterSerialNumber();
this.info.mac = await this.abstraction.getPrinterBluetoothMacAddress();
this.info.charge = await this.abstraction.getBatteryChargeLevel();
this.info.autoShutdownTime = await this.abstraction.getAutoShutDownTime();
return this.info;
}
public getPrinterConfig(): PrinterConfig {
return this.printerConfig;
public getPrinterInfo(): PrinterConfig {
return this.info;
}
/**
@ -102,6 +103,11 @@ export abstract class NiimbotAbstractClient extends TypedEventTarget<ClientEvent
public isHeartbeatStarted(): boolean {
return this.heartbeatTimer === undefined;
}
/** Get printer capabilities based on the printer model */
public getCapabilities(): PrinterCapabilities {
return getPrinterCapabilities(this.info.model ?? PrinterModel.UNKNOWN);
}
}
export * from "./events";

View File

@ -30,14 +30,14 @@ export class NiimbotSerialClient extends NiimbotAbstractClient {
try {
await this.initialNegotiate();
await this.fetchPrinterConfig();
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.printerConfig.connectResult ?? ConnectResult.FirmwareErrors
result: this.info.connectResult ?? ConnectResult.FirmwareErrors
};
this.dispatchTypedEvent("connect", new ConnectEvent(result));

View File

@ -95,7 +95,7 @@ export class ImageEncoder {
}
/**
* @param data Encoded pixels (every byte is 8 pixels)
* @param data Pixels encoded by {@link encodeCanvas} (byte is 8 pixels)
* @returns Array of indexes where every index stored in two bytes (big endian)
*/
public static indexPixels(data: Uint8Array): Uint8Array {

View File

@ -2,3 +2,4 @@ export * from "./client";
export * from "./packets";
export * from "./image_encoder";
export * from "./utils";
export * from "./printers";

View File

@ -7,12 +7,12 @@ import {
NiimbotPacket,
PacketGenerator,
PrinterInfoType,
PrinterModelId,
ResponseCommandId,
SoundSettingsItemType,
SoundSettingsType,
} from ".";
import { NiimbotAbstractClient, Utils, Validators } from "..";
import { PrinterModel } from "../printers";
import { SequentialDataReader } from "./data_reader";
export class PrintError extends Error {
@ -89,10 +89,7 @@ export class Abstraction {
if (packet.command === ResponseCommandId.In_PrintError) {
Validators.u8ArrayLengthEquals(packet.data, 1);
throw new PrintError(
`Print error (${ResponseCommandId[packet.command]} packet received)`,
packet.data[0]
);
throw new PrintError(`Print error (${ResponseCommandId[packet.command]} packet received)`, packet.data[0]);
}
Validators.u8ArrayLengthAtLeast(packet.data, 4); // can be 8, 10, but ignore it for now
@ -105,7 +102,10 @@ export class Abstraction {
if (packet.dataLength === 10) {
r.skip(2);
const error = r.readI8();
throw new PrintError(`Print error (${ResponseCommandId[packet.command]} packet flag)`, error);
if (error !== 0) {
throw new PrintError(`Print error (${ResponseCommandId[packet.command]} packet flag)`, error);
}
}
return { page, pagePrintProgress, pageFeedProgress };
@ -122,34 +122,34 @@ export class Abstraction {
const packet = await this.send(PacketGenerator.getPrinterStatusData());
let supportColor = 0;
if (packet.dataLength > 12) {
supportColor = packet.data[10];
if (packet.dataLength > 12) {
supportColor = packet.data[10];
let n = packet.data[11] * 100 + packet.data[12];
if (n >= 204 && n < 300) {
protocolVersion = 3;
}
if (n >= 301) {
protocolVersion = 4;
}
let n = packet.data[11] * 100 + packet.data[12];
if (n >= 204 && n < 300) {
protocolVersion = 3;
}
if (n >= 301) {
protocolVersion = 4;
}
}
return {
supportColor,
protocolVersion
}
protocolVersion,
};
}
public async getPrinterModel(): Promise<PrinterModelId> {
public async getPrinterModel(): Promise<PrinterModel> {
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.PrinterModelId));
Validators.u8ArrayLengthEquals(packet.data, 2);
const id = Utils.bytesToI16(packet.data);
if (id in PrinterModelId) {
return id as PrinterModelId;
if (id in PrinterModel) {
return id as PrinterModel;
}
return PrinterModelId.UNKNOWN;
return PrinterModel.UNKNOWN;
}
/** Read paper nfc tag info */
@ -224,7 +224,7 @@ export class Abstraction {
}
r.end();
const model = this.client.getPrinterConfig().model ?? PrinterModelId.UNKNOWN;
const model = this.client.getPrinterInfo().model ?? PrinterModel.UNKNOWN;
if (![512, 514, 513, 2304, 1792, 3584, 5120, 2560, 3840, 4352, 272].includes(model)) {
info.lidClosed = !info.lidClosed;
@ -245,6 +245,10 @@ export class Abstraction {
return packet.data[0] as AutoShutdownTime;
}
public async setAutoShutDownTime(time: AutoShutdownTime): Promise<void> {
await this.send(PacketGenerator.setAutoShutDownTime(time));
}
public async getLabelType(): Promise<LabelType> {
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.LabelType));
Validators.u8ArrayLengthEquals(packet.data, 1);

View File

@ -27,7 +27,7 @@ export enum RequestCommandId {
SetLabelType = 0x23 /* D11 - 1,5, for D110 able to set 1,2,3,5; see LabelType */,
SetPageSize = 0x13, // 2, 4 or 6 bytes
SoundSettings = 0x58,
Unknown1 = 0x0b, // some info request (niimbot app), 01 long 02 short
AntiFake = 0x0b, // some info request (niimbot app), 01 long 02 short
WriteRFID = 0x70, // same as GetVolumeLevel???
}
@ -53,7 +53,7 @@ export enum ResponseCommandId {
In_PrinterInfoPrinterCode = 0x48,
In_PrinterInfoSerialNumber = 0x4b,
In_PrinterInfoSoftWareVersion = 0x49,
In_PrinterInfoUnknown1 = 0x4f,
In_PrinterInfoArea = 0x4f,
IN_PrinterStatusData = 0xb5,
In_PrintStatus = 0xb3,
In_PrintError = 0xdb, // For example, sent on SetPageSize when page print is not started
@ -66,7 +66,7 @@ export enum ResponseCommandId {
In_SetLabelType = 0x33,
In_SetPageSize = 0x14,
In_SoundSettings = 0x68,
In_Unknown1 = 0xe4,
In_OageEnd = 0xe4,
}
export enum PrinterInfoType {
@ -83,7 +83,7 @@ export enum PrinterInfoType {
HardWareVersion = 12,
BluetoothAddress = 13,
PrintMode = 14,
Unknown1 = 15,
Area = 15,
}
export enum SoundSettingsType {
@ -138,67 +138,6 @@ export enum ConnectResult {
FirmwareErrors = 90,
}
/** Generated from android app (assets/flutter_assets/assets/config/printerList.json) */
export enum PrinterModelId {
UNKNOWN = 0,
T6 = 51715,
TP2M_H = 4609,
B31 = 5632,
B1 = 4096,
M2_H = 4608,
B21_PRO = 785,
P1 = 1024,
T2S = 53250,
B50W = 51714,
T7 = 51717,
B50 = 51713,
Z401 = 2051,
B32R = 2050,
A63 = 2054,
T8S = 2053,
B32 = 2049,
B18S = 3585,
B18 = 3584,
MP3K_W = 4867,
MP3K = 4866,
K3_W = 4865,
K3 = 4864,
B3S_P = 272,
S6 = 261,
B3S = 256,
B3S_V2 = 260,
B3S_V3 = 262,
B3 = 52993,
A203 = 2818,
A20 = 2817,
B203 = 2816,
S1 = 51458,
JC_M90 = 51461,
S3 = 51460,
B11 = 51457,
B21_H = 784,
B21S_C2B = 776,
B21S = 777,
B21_L2B = 769,
B21_C2B = 771,
B21_C2B_V2 = 775,
B21 = 768,
B16 = 1792,
BETTY = 2561,
D101 = 2560,
D110_M = 2320,
HI_D110 = 2305,
D110 = 2304,
D11_H = 528,
D11S = 514,
FUST = 513,
D11 = 512,
ET10 = 5376,
P18 = 1026,
P1S = 1025,
T8 = 51718,
}
export * from "./packet";
export * from "./packet_generator";
export * from "./abstraction";

View File

@ -10,6 +10,7 @@ import {
SoundSettingsType,
} from ".";
import { EncodedImage, ImageEncoder, ImageRow as ImagePart } from "../image_encoder";
import { ProtocolVersion } from "../printers";
import { Utils } from "../utils";
export type PrintOptions = {
@ -18,19 +19,6 @@ export type PrintOptions = {
quantity?: number;
};
export enum ProtocolVariant {
/** Used in D11 */
V1 = 1,
/** Used in B21, D110new */
V2,
/** Used in B16 */
V3,
/** Used in B1 */
V4,
/** Not used */
V5,
}
export class PacketGenerator {
public static generic(
requestId: RequestCommandId,
@ -73,7 +61,7 @@ export class PacketGenerator {
ResponseCommandId.In_PrinterInfoHardWareVersion,
ResponseCommandId.In_PrinterInfoBluetoothAddress,
// ResponseCommandId.In_PrinterInfoPrintMode,
ResponseCommandId.In_PrinterInfoUnknown1,
ResponseCommandId.In_PrinterInfoArea,
]
);
}
@ -334,12 +322,12 @@ export class PacketGenerator {
/*
B1 print process example (square in square, 160x240)
SetDensity 5555 21 0102 22aaaa
SetLabelType 5555 23 0101 23aaaa
PrintStart 5555 01 0700010000000000 07aaaa
PageStart 5555 03 0101 03aaaa
SetPageSize 5555 13 0600a000f00001 44aaaa
PrintEmptyRows 5555 84 0300001d 9aaaaa
SetDensity 5555 21 01 02 22aaaa
SetLabelType 5555 23 01 01 23aaaa
PrintStart 5555 01 07 00010000000000 07aaaa
PageStart 5555 03 01 01 03aaaa
SetPageSize 5555 13 06 00a000f00001 44aaaa
PrintEmptyRows 5555 84 03 00001d 9aaaaa
PrintBitmapRows 5555 85 24 001d 3e3000 04 000000000000003ffffffffffffffffffffffffffffffff0000000000000 86aaaa
PrintBitmapRows 5555 85 24 0021 b83000 21 000000000000003c000000000000000000000000000000f0000000000000 e5aaaa
PrintBitmapRows 5555 85 24 0042 9e3000 04 000000000000003c000000000007fffffe000000000000f0000000000000 7caaaa
@ -347,10 +335,10 @@ export class PacketGenerator {
PrintBitmapRows 5555 85 24 005a 9e3000 04 000000000000003c000000000007fffffe000000000000f0000000000000 64aaaa
PrintBitmapRows 5555 85 24 005e b83000 23 000000000000003c000000000000000000000000000000f0000000000000 98aaaa
PrintBitmapRows 5555 85 24 0081 3e3000 04 000000000000003ffffffffffffffffffffffffffffffff0000000000000 1aaaaa
PrintEmptyRows 5555 84 0300851b19aaaa
PageEnd 5555 e3 0101 e3aaaa
PrintStatus 5555 a3 0101 a3aaaa (alot)
PrintEnd 5555 f3 0101 f3aaaa
PrintEmptyRows 5555 84 03 00851b19aaaa
PageEnd 5555 e3 01 01 e3aaaa
PrintStatus 5555 a3 01 01 a3aaaa (alot)
PrintEnd 5555 f3 01 01 f3aaaa
You should send PrintEnd manually after this sequence (after print finished)
@ -368,17 +356,17 @@ export class PacketGenerator {
}
public static generatePrintSequence(
variant: ProtocolVariant,
protoVersion: ProtocolVersion,
image: EncodedImage,
options?: PrintOptions
): NiimbotPacket[] {
switch (variant) {
case ProtocolVariant.V3:
switch (protoVersion) {
case ProtocolVersion.V3:
return PacketGenerator.generatePrintSequenceV3(image, options);
case ProtocolVariant.V4:
case ProtocolVersion.V4:
return PacketGenerator.generatePrintSequenceV4(image, options);
default:
throw new Error("Not implemented");
throw new Error(`PrintTaskVersion ${protoVersion} Not implemented`);
}
}
}

184
niimbluelib/src/printers.ts Normal file
View File

@ -0,0 +1,184 @@
export enum PrinterModel {
UNKNOWN = 0,
A20 = 2817,
A203 = 2818,
A63 = 2054,
A8 = 1280,
B11 = 51457,
B16 = 1792,
B18 = 3584,
B18S = 3585,
B20 = 4608,
B201__B1 = 4096,
B203 = 2816,
B21__B21_OLD = 768,
B21_C2B = 771,
B21_C2B_ZX__B21_C2B_V2 = 775,
B21_C2W = 772,
B21_C3W = 774,
B21_H = 784,
B21_L2B = 769,
B21_L2W = 770,
B21_PRO = 785,
B21S = 777,
B21S_C2B = 776,
B3 = 52993,
B31 = 5632,
B32 = 2049,
B32_R = 2050,
B3S = 256,
B3S_GD__B3S_V3 = 262,
B3S_P = 272,
B3S_ZX__B3S_V2 = 260,
B50 = 51713,
B50W = 51714,
BETTY = 2561,
C1 = 5120,
D101 = 2560,
D11 = 512,
D11_H = 528,
D110 = 2304,
D110_M = 2320,
D11S = 514,
D61 = 1536,
ET10 = 5376,
FUST = 513,
H1 = 4352,
H10 = 3840,
HI_D110 = 2305,
JC_M90 = 51461,
K1__K3 = 4864,
K1S__K3_W = 4865,
MP3K = 4866,
MP3K_W = 4867,
P1 = 1024,
P18 = 1026,
P1S = 1025,
S1 = 51458,
S3 = 51460,
S6 = 257,
S6_1 = 258,
S6_2 = 261,
T2S = 53250,
T6 = 51715,
T7 = 51717,
T8 = 51718,
T8S = 2053,
TP2M_H = 4609,
TSC = 255,
Z401 = 2051,
Z401_R = 2052,
}
export enum ProtocolVersion {
UNKNOWN = 0,
/** Used in D11 */
V1,
/** Used in B21, D110new */
V2,
/** Used in B16 */
V3,
/** Used in B1 */
V4,
/** Not used */
V5,
}
export interface PrinterCapabilities {
printTaskVersion: ProtocolVersion;
}
import M = PrinterModel;
export const getPrinterCapabilities = (id: PrinterModel): PrinterCapabilities => {
let printTaskVersion = ProtocolVersion.UNKNOWN;
switch (id) {
case M.D11:
case M.D11_H:
case M.D11S:
printTaskVersion = ProtocolVersion.V1;
break;
case M.D110:
case M.D110_M:
printTaskVersion = ProtocolVersion.V3;
break;
case M.B201__B1:
printTaskVersion = ProtocolVersion.V4;
break;
// todo: find other models info
case M.UNKNOWN:
case M.A20:
case M.A203:
case M.A63:
case M.A8:
case M.B11:
case M.B16:
case M.B18:
case M.B18S:
case M.B20:
case M.B203:
case M.B21__B21_OLD:
case M.B21_C2B:
case M.B21_C2B_ZX__B21_C2B_V2:
case M.B21_C2W:
case M.B21_C3W:
case M.B21_H:
case M.B21_L2B:
case M.B21_L2W:
case M.B21_PRO:
case M.B21S:
case M.B21S_C2B:
case M.B3:
case M.B31:
case M.B32:
case M.B32_R:
case M.B3S:
case M.B3S_GD__B3S_V3:
case M.B3S_P:
case M.B3S_ZX__B3S_V2:
case M.B50:
case M.B50W:
case M.BETTY:
case M.C1:
case M.D101:
case M.D61:
case M.ET10:
case M.FUST:
case M.H1:
case M.H10:
case M.HI_D110:
case M.JC_M90:
case M.K1__K3:
case M.K1S__K3_W:
case M.MP3K:
case M.MP3K_W:
case M.P1:
case M.P18:
case M.P1S:
case M.S1:
case M.S3:
case M.S6:
case M.S6_1:
case M.S6_2:
case M.T2S:
case M.T6:
case M.T7:
case M.T8:
case M.T8S:
case M.TP2M_H:
case M.TSC:
case M.Z401:
case M.Z401_R:
break;
default:
((_id: never) => {
throw new Error(`Printer model ${_id} was unhandled!`);
})(id);
}
return { printTaskVersion };
};