Working tree changes 2024-07-22 00:00
This commit is contained in:
parent
4e5250fc06
commit
9344bb5997
@ -29,7 +29,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- svelte-ignore missing-declaration -->
|
<!-- svelte-ignore missing-declaration -->
|
||||||
<div class="version text-secondary">v{__APP_VERSION__} built at {__BUILD_DATE__}</div>
|
<div class="version text-end text-secondary">
|
||||||
|
<div>
|
||||||
|
v{__APP_VERSION__} built at {__BUILD_DATE__}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a class="text-secondary" href="https://gitee.mmote.ru/MultiMote/niimblue-nightly">code (temporary)</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.niim {
|
.niim {
|
||||||
@ -38,6 +45,7 @@
|
|||||||
.blue {
|
.blue {
|
||||||
color: #0b7eff;
|
color: #0b7eff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.version {
|
.version {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
@ -4,7 +4,17 @@
|
|||||||
import Modal from "bootstrap/js/dist/modal";
|
import Modal from "bootstrap/js/dist/modal";
|
||||||
import { connectionState, printerClient } from "../stores";
|
import { connectionState, printerClient } from "../stores";
|
||||||
import { copyImageData, threshold, atkinson } from "../post_process";
|
import { copyImageData, threshold, atkinson } from "../post_process";
|
||||||
import { type EncodedImage, ImageEncoder, Utils, LabelType, PacketGenerator, PrintSequenceVariant } from "@mmote/niimbluelib";
|
import {
|
||||||
|
type EncodedImage,
|
||||||
|
ImageEncoder,
|
||||||
|
Utils,
|
||||||
|
LabelType,
|
||||||
|
PacketGenerator,
|
||||||
|
PrintSequenceVariant,
|
||||||
|
type PrintStatusDecoded,
|
||||||
|
ResponseCommandId,
|
||||||
|
PrintError,
|
||||||
|
} from "@mmote/niimbluelib";
|
||||||
import type { LabelProps } from "../types";
|
import type { LabelProps } from "../types";
|
||||||
import FaIcon from "./FaIcon.svelte";
|
import FaIcon from "./FaIcon.svelte";
|
||||||
|
|
||||||
@ -17,6 +27,7 @@
|
|||||||
let previewCanvas: HTMLCanvasElement;
|
let previewCanvas: HTMLCanvasElement;
|
||||||
let modal: Modal;
|
let modal: Modal;
|
||||||
let sendProgress: number = 0;
|
let sendProgress: number = 0;
|
||||||
|
let printProgress: number = 0; // todo: more progress data
|
||||||
let density: number = 3;
|
let density: number = 3;
|
||||||
let quantity: number = 1;
|
let quantity: number = 1;
|
||||||
let printed: boolean = false;
|
let printed: boolean = false;
|
||||||
@ -25,28 +36,48 @@
|
|||||||
let imgData: ImageData;
|
let imgData: ImageData;
|
||||||
let imgContext: CanvasRenderingContext2D;
|
let imgContext: CanvasRenderingContext2D;
|
||||||
let protoVariant: PrintSequenceVariant = PrintSequenceVariant.V1;
|
let protoVariant: PrintSequenceVariant = PrintSequenceVariant.V1;
|
||||||
|
let statusTimer: NodeJS.Timeout | undefined = undefined;
|
||||||
|
let printError: boolean = false;
|
||||||
|
|
||||||
const disconnected = derived(connectionState, ($connectionState) => $connectionState !== "connected");
|
const disconnected = derived(connectionState, ($connectionState) => $connectionState !== "connected");
|
||||||
|
|
||||||
const onPrint = async () => {
|
const cancelPrint = async () => {
|
||||||
const encoded: EncodedImage = ImageEncoder.encodeCanvas(previewCanvas, labelProps.startPos);
|
clearInterval(statusTimer);
|
||||||
const packets = PacketGenerator.generatePrintSequence(protoVariant, encoded, { quantity, density });
|
|
||||||
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.sendPacketWaitResponse(packets[i]);
|
|
||||||
|
|
||||||
// await Utils.sleep(100);
|
|
||||||
// console.log(Utils.bufToHex(packets[i].toBytes()))
|
|
||||||
}
|
|
||||||
printed = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onEndPrint = async () => {
|
|
||||||
if (!$disconnected && printed) {
|
if (!$disconnected && printed) {
|
||||||
await $printerClient.sendPacket(PacketGenerator.printEnd());
|
await $printerClient.sendPacket(PacketGenerator.printEnd());
|
||||||
printed = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printed = false;
|
||||||
|
printProgress = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPrint = async () => {
|
||||||
|
printError = false;
|
||||||
|
const encoded: EncodedImage = ImageEncoder.encodeCanvas(previewCanvas, labelProps.startPos);
|
||||||
|
const packets = PacketGenerator.generatePrintSequence(protoVariant, encoded, { quantity, density });
|
||||||
|
|
||||||
|
for (let i = 0; i < packets.length; i++) {
|
||||||
|
sendProgress = Math.round(((i + 1) / packets.length) * 100);
|
||||||
|
await $printerClient.sendPacketWaitResponse(packets[i], 10_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
statusTimer = setInterval(async () => {
|
||||||
|
try {
|
||||||
|
const status = await $printerClient.abstraction.getPrintStatus();
|
||||||
|
printProgress = status.pagePrintProgress;
|
||||||
|
|
||||||
|
if (status.page === quantity && status.pagePrintProgress === 100 && status.pageFeedProgress === 100) {
|
||||||
|
await cancelPrint();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
await cancelPrint();
|
||||||
|
printError = true;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
printed = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const updatePreview = () => {
|
const updatePreview = () => {
|
||||||
@ -78,7 +109,7 @@
|
|||||||
modal = new Modal(modalElement);
|
modal = new Modal(modalElement);
|
||||||
modal.show();
|
modal.show();
|
||||||
modalElement.addEventListener("hidden.bs.modal", async () => {
|
modalElement.addEventListener("hidden.bs.modal", async () => {
|
||||||
onEndPrint();
|
cancelPrint();
|
||||||
onClosed();
|
onClosed();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -117,8 +148,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if printProgress != 0 && printProgress != 100}
|
||||||
|
<div>
|
||||||
|
Printing...
|
||||||
|
<div class="progress" role="progressbar">
|
||||||
|
<div class="progress-bar" style="width: {printProgress}%">{printProgress}%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#if printError}
|
||||||
|
<div class="alert alert-danger" role="alert">Print error</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<div class="input-group input-group-sm">
|
<div class="input-group input-group-sm">
|
||||||
<span class="input-group-text">Post-process</span>
|
<span class="input-group-text">Post-process</span>
|
||||||
@ -144,24 +187,24 @@
|
|||||||
<input class="form-control" type="number" min="1" max="6" bind:value={density} />
|
<input class="form-control" type="number" min="1" max="6" bind:value={density} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <div class="input-group flex-nowrap input-group-sm">
|
<div class="input-group flex-nowrap input-group-sm">
|
||||||
<span class="input-group-text">Quantity</span>
|
<span class="input-group-text">Quantity</span>
|
||||||
<input class="form-control" type="number" min="1" bind:value={quantity} />
|
<input class="form-control" type="number" min="1" bind:value={quantity} />
|
||||||
</div> -->
|
</div>
|
||||||
|
|
||||||
<div class="input-group input-group-sm">
|
<div class="input-group input-group-sm">
|
||||||
<span class="input-group-text">Protocol variant</span>
|
<span class="input-group-text">Protocol variant</span>
|
||||||
<select class="form-select" bind:value={protoVariant}>
|
<select class="form-select" bind:value={protoVariant}>
|
||||||
<option value="{PrintSequenceVariant.V1}">V1 - D110</option>
|
<option value={PrintSequenceVariant.V1}>V1 - D110</option>
|
||||||
<option value="{PrintSequenceVariant.V2}">V2 - B1</option>
|
<option value={PrintSequenceVariant.V2}>V2 - B1</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
|
||||||
{#if printed}
|
{#if printed}
|
||||||
<button type="button" class="btn btn-primary" disabled={$disconnected} on:click={onEndPrint}>
|
<button type="button" class="btn btn-primary" disabled={$disconnected} on:click={cancelPrint}>
|
||||||
End print
|
Cancel print
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {
|
import { PrinterModelId } from "@mmote/niimbluelib";
|
||||||
PacketGenerator,
|
import { printerClient, connectedPrinterName, connectionState, initClient, heartbeatData } from "../stores";
|
||||||
PrinterInfoType,
|
|
||||||
HeartbeatType,
|
|
||||||
PrinterId,
|
|
||||||
type PrinterInfoPrinterCodeDecoded,
|
|
||||||
} from "@mmote/niimbluelib";
|
|
||||||
import { printerClient, connectedPrinterName, connectionState, initClient } from "../stores";
|
|
||||||
import type { ConnectionType } from "../types";
|
import type { ConnectionType } from "../types";
|
||||||
import FaIcon from "./FaIcon.svelte";
|
import FaIcon from "./FaIcon.svelte";
|
||||||
|
import { icon } from "@fortawesome/fontawesome-svg-core";
|
||||||
|
|
||||||
let connectionType: ConnectionType = "bluetooth";
|
let connectionType: ConnectionType = "bluetooth";
|
||||||
let timer: NodeJS.Timeout | undefined = undefined;
|
|
||||||
const onConnectClicked = async () => {
|
const onConnectClicked = async () => {
|
||||||
initClient(connectionType);
|
initClient(connectionType);
|
||||||
connectionState.set("connecting");
|
connectionState.set("connecting");
|
||||||
@ -27,31 +22,25 @@
|
|||||||
$printerClient.disconnect();
|
$printerClient.disconnect();
|
||||||
};
|
};
|
||||||
const getRfidInfo = async () => {
|
const getRfidInfo = async () => {
|
||||||
const data = await $printerClient.sendPacketWaitResponseDecoded(PacketGenerator.rfidInfo());
|
const data = await $printerClient.abstraction.rfidInfo();
|
||||||
alert(JSON.stringify(data, null, 2));
|
alert(JSON.stringify(data, null, 2));
|
||||||
|
|
||||||
// const data = Uint8Array.of(1,1,1,1);
|
|
||||||
// await $printerClient.sendPacketWaitResponse(PacketGenerator.writeRfid(data), 1000);
|
|
||||||
// await $printerClient.sendPacketWaitResponse(PacketGenerator.rfidInfo(), 1000);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const test = async () => {
|
const startHeartbeat = async () => {
|
||||||
timer = setInterval(async () => {
|
// timer = setInterval(async () => {
|
||||||
const data = await $printerClient.sendPacketWaitResponseDecoded(
|
// const data = await $printerClient.abstraction.heartbeat();
|
||||||
PacketGenerator.heartbeat(HeartbeatType.Unknown1)
|
// console.log(data);
|
||||||
);
|
// }, 1000);
|
||||||
}, 1000);
|
$printerClient.startHeartbeat();
|
||||||
};
|
};
|
||||||
const test2 = async () => {
|
const stopHeartbeat = async () => {
|
||||||
clearInterval(timer);
|
// clearInterval(timer);
|
||||||
|
$printerClient.stopHeartbeat();
|
||||||
};
|
};
|
||||||
|
|
||||||
const test3 = async () => {
|
const test3 = async () => {
|
||||||
const info = (await $printerClient.sendPacketWaitResponseDecoded(
|
const id = await $printerClient.abstraction.getPrinterModel();
|
||||||
PacketGenerator.getPrinterInfo(PrinterInfoType.PrinterCode)
|
alert(`Printer model: ${PrinterModelId[id]}`);
|
||||||
)) as PrinterInfoPrinterCodeDecoded;
|
|
||||||
const id: string | undefined = PrinterId[info.code];
|
|
||||||
alert(`Printer model: ${id ?? "Unknown"}`);
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -61,9 +50,12 @@
|
|||||||
><FaIcon icon="gear" />
|
><FaIcon icon="gear" />
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown-menu p-1">
|
<div class="dropdown-menu p-1">
|
||||||
|
<div>
|
||||||
|
<FaIcon icon="battery-empty"/> {($heartbeatData?.powerLevel ?? 0) * 25}%
|
||||||
|
</div>
|
||||||
<button class="btn btn-sm btn-primary" on:click={getRfidInfo}>RfidInfo</button>
|
<button class="btn btn-sm btn-primary" on:click={getRfidInfo}>RfidInfo</button>
|
||||||
<button class="btn btn-sm btn-primary" on:click={test}>start heartbeat (test)</button>
|
<button class="btn btn-sm btn-primary" on:click={startHeartbeat}>start heartbeat (test)</button>
|
||||||
<button class="btn btn-sm btn-primary" on:click={test2}>stop 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={test3}>Guess printer model</button>
|
||||||
</div>
|
</div>
|
||||||
<span class="input-group-text">{$connectedPrinterName}</span>
|
<span class="input-group-text">{$connectedPrinterName}</span>
|
||||||
|
@ -2,18 +2,21 @@ import { writable } from "svelte/store";
|
|||||||
import type { ConnectionState, ConnectionType } from "./types";
|
import type { ConnectionState, ConnectionType } from "./types";
|
||||||
import {
|
import {
|
||||||
ConnectEvent,
|
ConnectEvent,
|
||||||
|
HeartbeatEvent,
|
||||||
NiimbotBluetoothClient,
|
NiimbotBluetoothClient,
|
||||||
NiimbotSerialClient,
|
NiimbotSerialClient,
|
||||||
PacketParsedEvent,
|
// PacketParsedEvent,
|
||||||
RawPacketReceivedEvent,
|
RawPacketReceivedEvent,
|
||||||
RawPacketSentEvent,
|
RawPacketSentEvent,
|
||||||
Utils,
|
Utils,
|
||||||
|
type HeartbeatData,
|
||||||
type NiimbotAbstractClient,
|
type NiimbotAbstractClient,
|
||||||
} from "@mmote/niimbluelib";
|
} from "@mmote/niimbluelib";
|
||||||
|
|
||||||
export const connectionState = writable<ConnectionState>("disconnected");
|
export const connectionState = writable<ConnectionState>("disconnected");
|
||||||
export const connectedPrinterName = writable<string>("");
|
export const connectedPrinterName = writable<string>("");
|
||||||
export const printerClient = writable<NiimbotAbstractClient>();
|
export const printerClient = writable<NiimbotAbstractClient>();
|
||||||
|
export const heartbeatData = writable<HeartbeatData>();
|
||||||
|
|
||||||
export const initClient = (connectionType: ConnectionType) => {
|
export const initClient = (connectionType: ConnectionType) => {
|
||||||
printerClient.update((prevClient: NiimbotAbstractClient) => {
|
printerClient.update((prevClient: NiimbotAbstractClient) => {
|
||||||
@ -43,9 +46,9 @@ export const initClient = (connectionType: ConnectionType) => {
|
|||||||
console.log(`<< ${Utils.bufToHex(e.data)}`);
|
console.log(`<< ${Utils.bufToHex(e.data)}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
newClient.addEventListener("packetparsed", (e: PacketParsedEvent) => {
|
// newClient.addEventListener("packetparsed", (e: PacketParsedEvent) => {
|
||||||
console.log(e.packet);
|
// console.log(e.packet);
|
||||||
});
|
// });
|
||||||
|
|
||||||
newClient.addEventListener("connect", (e: ConnectEvent) => {
|
newClient.addEventListener("connect", (e: ConnectEvent) => {
|
||||||
console.log("onConnect");
|
console.log("onConnect");
|
||||||
@ -59,6 +62,10 @@ export const initClient = (connectionType: ConnectionType) => {
|
|||||||
connectionState.set("disconnected");
|
connectionState.set("disconnected");
|
||||||
connectedPrinterName.set("");
|
connectedPrinterName.set("");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
newClient.addEventListener("heartbeat", (e: HeartbeatEvent) => {
|
||||||
|
heartbeatData.set(e.data);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return newClient;
|
return newClient;
|
||||||
|
@ -5,7 +5,6 @@ import { Utils } from "../utils";
|
|||||||
import {
|
import {
|
||||||
ConnectEvent,
|
ConnectEvent,
|
||||||
DisconnectEvent,
|
DisconnectEvent,
|
||||||
PacketParsedEvent,
|
|
||||||
PacketReceivedEvent,
|
PacketReceivedEvent,
|
||||||
RawPacketReceivedEvent,
|
RawPacketReceivedEvent,
|
||||||
RawPacketSentEvent,
|
RawPacketSentEvent,
|
||||||
@ -41,6 +40,8 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
|
|||||||
const disconnectListener = async () => {
|
const disconnectListener = async () => {
|
||||||
this.gattServer = undefined;
|
this.gattServer = undefined;
|
||||||
this.channel = undefined;
|
this.channel = undefined;
|
||||||
|
this.config = {};
|
||||||
|
this.stopHeartbeat();
|
||||||
this.dispatchTypedEvent("disconnect", new DisconnectEvent());
|
this.dispatchTypedEvent("disconnect", new DisconnectEvent());
|
||||||
device.removeEventListener("gattserverdisconnected", disconnectListener);
|
device.removeEventListener("gattserverdisconnected", disconnectListener);
|
||||||
};
|
};
|
||||||
@ -62,12 +63,12 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
|
|||||||
|
|
||||||
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 {
|
||||||
const data = PacketParser.parse(packet);
|
const data = PacketParser.parse(packet);
|
||||||
if (data !== undefined) {
|
if (data !== undefined) {
|
||||||
this.dispatchTypedEvent("packetparsed", new PacketParsedEvent(data));
|
this.dispatchTypedEvent("packetparsed", new PacketParsedEvent(data));
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
});
|
});
|
||||||
|
|
||||||
await channel.startNotifications();
|
await channel.startNotifications();
|
||||||
@ -79,15 +80,28 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
|
|||||||
deviceName: device.name,
|
deviceName: device.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await this.fetchPrinterConfig();
|
||||||
|
console.log(data);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
this.dispatchTypedEvent("connect", new ConnectEvent(result));
|
this.dispatchTypedEvent("connect", new ConnectEvent(result));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isConnected(): boolean {
|
||||||
|
return this.gattServer !== undefined && this.channel !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
public async disconnect() {
|
public async disconnect() {
|
||||||
|
this.stopHeartbeat();
|
||||||
this.gattServer?.disconnect();
|
this.gattServer?.disconnect();
|
||||||
this.gattServer = undefined;
|
this.gattServer = undefined;
|
||||||
this.channel = undefined;
|
this.channel = undefined;
|
||||||
|
this.config = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -120,7 +134,9 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
|
|||||||
|
|
||||||
timeout = setTimeout(() => {
|
timeout = setTimeout(() => {
|
||||||
this.removeEventListener("packetreceived", listener);
|
this.removeEventListener("packetreceived", listener);
|
||||||
throw new Error("Timeout waiting response");
|
throw new Error(
|
||||||
|
`Timeout waiting response (waited for ${Utils.bufToHex(packet.getValidResponseIds(), ", ")})`
|
||||||
|
);
|
||||||
}, timeoutMs ?? 1000);
|
}, timeoutMs ?? 1000);
|
||||||
|
|
||||||
this.addEventListener("packetreceived", listener);
|
this.addEventListener("packetreceived", listener);
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import { NiimbotPacket, ParsedPacket } from "../packets";
|
import { ConnectionInfo } from ".";
|
||||||
|
import { HeartbeatData, NiimbotPacket, ParsedPacket } from "../packets";
|
||||||
|
|
||||||
export type ConnectionInfo = {
|
|
||||||
deviceName?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -27,13 +25,13 @@ export class DisconnectEvent extends Event {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export class PacketParsedEvent extends Event {
|
// export class PacketParsedEvent extends Event {
|
||||||
packet: ParsedPacket;
|
// packet: ParsedPacket;
|
||||||
constructor(packet: ParsedPacket) {
|
// constructor(packet: ParsedPacket) {
|
||||||
super("packetparsed");
|
// super("packetparsed");
|
||||||
this.packet = packet;
|
// this.packet = packet;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -68,11 +66,23 @@ export class RawPacketReceivedEvent extends Event {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export class HeartbeatEvent extends Event {
|
||||||
|
data: HeartbeatData;
|
||||||
|
constructor(data: HeartbeatData) {
|
||||||
|
super("heartbeat");
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface ClientEventMap {
|
export interface ClientEventMap {
|
||||||
connect: ConnectEvent;
|
connect: ConnectEvent;
|
||||||
disconnect: DisconnectEvent;
|
disconnect: DisconnectEvent;
|
||||||
rawpacketsent: RawPacketSentEvent;
|
rawpacketsent: RawPacketSentEvent;
|
||||||
rawpacketreceived: RawPacketReceivedEvent;
|
rawpacketreceived: RawPacketReceivedEvent;
|
||||||
packetreceived: PacketReceivedEvent;
|
packetreceived: PacketReceivedEvent;
|
||||||
packetparsed: PacketParsedEvent;
|
heartbeat: HeartbeatEvent;
|
||||||
|
// packetparsed: PacketParsedEvent;
|
||||||
}
|
}
|
@ -1,15 +1,34 @@
|
|||||||
import { ParsedPacket, NiimbotPacket, PacketParser } from "../packets";
|
import { NiimbotPacket, PrinterModelId } from "../packets";
|
||||||
import { TypedEventTarget } from 'typescript-event-target';
|
import { TypedEventTarget } from "typescript-event-target";
|
||||||
import { ClientEventMap, ConnectionInfo } from "./events";
|
import { ClientEventMap, HeartbeatEvent } from "./events";
|
||||||
|
import { Abstraction } from "../packets/parser/abstraction";
|
||||||
|
|
||||||
|
export type ConnectionInfo = {
|
||||||
|
deviceName?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface PrinterConfig {
|
||||||
|
model?: PrinterModelId;
|
||||||
|
}
|
||||||
|
|
||||||
export abstract class NiimbotAbstractClient extends TypedEventTarget<ClientEventMap> {
|
export abstract class NiimbotAbstractClient extends TypedEventTarget<ClientEventMap> {
|
||||||
|
public readonly abstraction: Abstraction;
|
||||||
|
protected config: PrinterConfig = {};
|
||||||
|
private heartbeatTimer?: NodeJS.Timeout;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.abstraction = new Abstraction(this);
|
||||||
|
}
|
||||||
|
|
||||||
/** Connect to printer port */
|
/** Connect to printer port */
|
||||||
public abstract connect(): Promise<ConnectionInfo>;
|
public abstract connect(): Promise<ConnectionInfo>;
|
||||||
|
|
||||||
/** Disconnect from printer port */
|
/** Disconnect from printer port */
|
||||||
public abstract disconnect(): Promise<void>;
|
public abstract disconnect(): Promise<void>;
|
||||||
|
|
||||||
|
public abstract isConnected(): boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send packet and wait for response.
|
* Send packet and wait for response.
|
||||||
* 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.
|
||||||
@ -28,15 +47,47 @@ export abstract class NiimbotAbstractClient extends TypedEventTarget<ClientEvent
|
|||||||
await this.sendRaw(packet.toBytes(), force);
|
await this.sendRaw(packet.toBytes(), force);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendPacketWaitResponseDecoded(packet: NiimbotPacket, timeoutMs?: number): Promise<ParsedPacket> {
|
// todo: delete
|
||||||
const response: NiimbotPacket = await this.sendPacketWaitResponse(packet, timeoutMs);
|
// public async sendPacketWaitResponseParsed(packet: NiimbotPacket, timeoutMs?: number): Promise<ParsedPacket> {
|
||||||
|
// const response: NiimbotPacket = await this.sendPacketWaitResponse(packet, timeoutMs);
|
||||||
|
|
||||||
// todo: prevent double parsing without duplicating sendPacketWaitResponse
|
// // todo: prevent double parsing without duplicating sendPacketWaitResponse
|
||||||
const data = PacketParser.parse(response);
|
// const data = PacketParser.parse(response);
|
||||||
if (!data) {
|
// if (!data) {
|
||||||
throw new Error("Decoder for this packet is not implemented or packet is invalid");
|
// throw new Error("Decoder for this packet is not implemented or packet is invalid");
|
||||||
}
|
// }
|
||||||
return data;
|
// return data;
|
||||||
|
// }
|
||||||
|
|
||||||
|
public async fetchPrinterConfig(): Promise<PrinterConfig> {
|
||||||
|
const props: PrinterConfig = {};
|
||||||
|
props.model = await this.abstraction.getPrinterModel();
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getConfig(): PrinterConfig {
|
||||||
|
return this.config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the heartbeat timer, "heartbeat" is emitted after packet received.
|
||||||
|
*
|
||||||
|
* @param interval Heartbeat interval, default is 1000ms
|
||||||
|
*/
|
||||||
|
public startHeartbeat(intervalMs: number = 1000): void {
|
||||||
|
this.heartbeatTimer = setInterval(async () => {
|
||||||
|
const data = await this.abstraction.heartbeat();
|
||||||
|
this.dispatchTypedEvent("heartbeat", new HeartbeatEvent(data));
|
||||||
|
}, intervalMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public stopHeartbeat(): void {
|
||||||
|
clearInterval(this.heartbeatTimer);
|
||||||
|
this.heartbeatTimer = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isHeartbeatStarted(): boolean {
|
||||||
|
return this.heartbeatTimer === undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,10 @@ export class NiimbotSerialClient extends NiimbotAbstractClient {
|
|||||||
this.writer = undefined;
|
this.writer = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isConnected(): boolean {
|
||||||
|
throw this.port !== undefined && this.writer !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
public async sendPacketWaitResponse(packet: NiimbotPacket, timeoutMs: number = 1000): Promise<NiimbotPacket> {
|
public async sendPacketWaitResponse(packet: NiimbotPacket, timeoutMs: number = 1000): Promise<NiimbotPacket> {
|
||||||
if (this.port === undefined) {
|
if (this.port === undefined) {
|
||||||
throw new Error("Port is closed");
|
throw new Error("Port is closed");
|
||||||
|
@ -147,6 +147,13 @@ export class PacketGenerator {
|
|||||||
return new NiimbotPacket(RequestCommandId.PrintQuantity, [h, l]);
|
return new NiimbotPacket(RequestCommandId.PrintQuantity, [h, l]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static printStatus(): NiimbotPacket {
|
||||||
|
return new NiimbotPacket(RequestCommandId.PrintStatus, [1], [
|
||||||
|
ResponseCommandId.In_PrintStatus,
|
||||||
|
ResponseCommandId.In_PrintError
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* B1 behavior: after {@link pageEnd} paper stops at printhead position, on {@link printEnd} paper moved further.
|
* B1 behavior: after {@link pageEnd} paper stops at printhead position, on {@link printEnd} paper moved further.
|
||||||
*
|
*
|
||||||
|
227
niimbluelib/src/packets/parser/abstraction.ts
Normal file
227
niimbluelib/src/packets/parser/abstraction.ts
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
import {
|
||||||
|
HeartbeatType as HeartbeatType,
|
||||||
|
LabelType,
|
||||||
|
NiimbotPacket,
|
||||||
|
PacketGenerator,
|
||||||
|
PrinterInfoType,
|
||||||
|
PrinterModelId,
|
||||||
|
ResponseCommandId,
|
||||||
|
SoundSettingsItemType,
|
||||||
|
SoundSettingsType,
|
||||||
|
} from "..";
|
||||||
|
import { NiimbotAbstractClient, Utils, Validators } from "../..";
|
||||||
|
import { SequentialDataReader } from "../parser/data_reader";
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// experiments
|
||||||
|
// export abstract class ParsedPacket2 {
|
||||||
|
// public readonly commandId: ResponseCommandId;
|
||||||
|
|
||||||
|
// constructor(commandId: ResponseCommandId) {
|
||||||
|
// this.commandId = commandId;
|
||||||
|
// }
|
||||||
|
// public abstract parse(packet: NiimbotPacket): ParsedPacket2;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export class PrintErrorDecoded2 extends ParsedPacket2 {
|
||||||
|
// public errorType: number;
|
||||||
|
|
||||||
|
// constructor() {
|
||||||
|
// super(ResponseCommandId.In_PrintError);
|
||||||
|
// this.errorType = 0;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public parse(packet: NiimbotPacket): PrintErrorDecoded2 {
|
||||||
|
// Validators.u8ArrayLengthEqual(packet.getData(), 1);
|
||||||
|
// const p = new PrintErrorDecoded2();
|
||||||
|
// p.errorType = packet.getData()[0];
|
||||||
|
// return p;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
|
export class PrintError extends Error {
|
||||||
|
public readonly reasonId: number;
|
||||||
|
|
||||||
|
constructor(message: string, reasonId: number) {
|
||||||
|
super(message);
|
||||||
|
this.reasonId = reasonId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PrintStatus {
|
||||||
|
/** 0 – n */
|
||||||
|
page: number;
|
||||||
|
/** 0 – 100 */
|
||||||
|
pagePrintProgress: number;
|
||||||
|
/** 0 – 100 */
|
||||||
|
pageFeedProgress: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RfidInfo {
|
||||||
|
tagPresent: boolean;
|
||||||
|
uuid: string;
|
||||||
|
barCode: string;
|
||||||
|
serialNumber: string;
|
||||||
|
allPaper: number;
|
||||||
|
usedPaper: number;
|
||||||
|
consumablesType: LabelType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** closingState inverted on some printers */
|
||||||
|
export interface HeartbeatData {
|
||||||
|
paperState: number;
|
||||||
|
rfidReadState: number;
|
||||||
|
closingState: number;
|
||||||
|
powerLevel: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Experimental class for retrieving data. Not sure for name. */
|
||||||
|
export class Abstraction {
|
||||||
|
private client: NiimbotAbstractClient;
|
||||||
|
private timeout: number = 1000;
|
||||||
|
|
||||||
|
constructor(client: NiimbotAbstractClient) {
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTimeout(): number {
|
||||||
|
return this.timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setTimeout(value: number) {
|
||||||
|
this.timeout = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async send(packet: NiimbotPacket): Promise<NiimbotPacket> {
|
||||||
|
return this.client.sendPacketWaitResponse(packet, this.timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getPrintStatus(): Promise<PrintStatus> {
|
||||||
|
const packet = await this.send(PacketGenerator.printStatus());
|
||||||
|
|
||||||
|
if (packet.getCommand() === ResponseCommandId.In_PrintError) {
|
||||||
|
Validators.u8ArrayLengthEquals(packet.getData(), 1);
|
||||||
|
throw new PrintError(
|
||||||
|
`Print error (${ResponseCommandId[packet.getCommand()]} packet received)`,
|
||||||
|
packet.getData()[0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Validators.u8ArrayLengthAtLeast(packet.getData(), 4); // can be 8, 10, but ignore it for now
|
||||||
|
|
||||||
|
const r = new SequentialDataReader(packet.getData());
|
||||||
|
const page = r.readI16();
|
||||||
|
const pagePrintProgress = r.readI8();
|
||||||
|
const pageFeedProgress = r.readI8();
|
||||||
|
|
||||||
|
if (packet.getData().length === 10) {
|
||||||
|
r.skip(2);
|
||||||
|
const error = r.readI8();
|
||||||
|
throw new PrintError(`Print error (${ResponseCommandId[packet.getCommand()]} packet flag)`, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { page, pagePrintProgress, pageFeedProgress };
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getPrinterModel(): Promise<PrinterModelId> {
|
||||||
|
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.PrinterModelId));
|
||||||
|
Validators.u8ArrayLengthEquals(packet.getData(), 2);
|
||||||
|
|
||||||
|
const id = Utils.bytesToI16(packet.getData());
|
||||||
|
|
||||||
|
if (id in PrinterModelId) {
|
||||||
|
return id as PrinterModelId;
|
||||||
|
}
|
||||||
|
return PrinterModelId.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Read paper nfc tag info */
|
||||||
|
public async rfidInfo(): Promise<RfidInfo> {
|
||||||
|
const packet = await this.send(PacketGenerator.rfidInfo());
|
||||||
|
|
||||||
|
const info: RfidInfo = {
|
||||||
|
tagPresent: false,
|
||||||
|
uuid: "",
|
||||||
|
barCode: "",
|
||||||
|
serialNumber: "",
|
||||||
|
allPaper: -1,
|
||||||
|
usedPaper: -1,
|
||||||
|
consumablesType: LabelType.Invalid,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (packet.getData().length === 1) {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
const r = new SequentialDataReader(packet.getData());
|
||||||
|
info.tagPresent = true;
|
||||||
|
info.uuid = Utils.bufToHex(r.readBytes(8), "");
|
||||||
|
info.barCode = r.readVString();
|
||||||
|
info.serialNumber = r.readVString();
|
||||||
|
info.allPaper = r.readI16();
|
||||||
|
info.usedPaper = r.readI16();
|
||||||
|
info.consumablesType = r.readI8() as LabelType;
|
||||||
|
r.end();
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async heartbeat(): Promise<HeartbeatData> {
|
||||||
|
const packet = await this.send(PacketGenerator.heartbeat(HeartbeatType.Unknown1));
|
||||||
|
|
||||||
|
const info: HeartbeatData = {
|
||||||
|
paperState: -1,
|
||||||
|
rfidReadState: -1,
|
||||||
|
closingState: -1,
|
||||||
|
powerLevel: -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// originally expected packet length is calculated from model id, but we make it simple
|
||||||
|
const len = packet.getData().length;
|
||||||
|
const r = new SequentialDataReader(packet.getData());
|
||||||
|
|
||||||
|
if (len === 17 - 7) {
|
||||||
|
// d110
|
||||||
|
// n3 = n8 + 9;
|
||||||
|
// DataCheck.parseClosingState(callback, byArray[n3], n2);
|
||||||
|
// DataCheck.parsePowerLevel(callback, byArray[n8 + 10]);
|
||||||
|
// DataCheck.parseRfidReadState(callback, byArray[n3], n2); ??????
|
||||||
|
r.skip(8);
|
||||||
|
info.closingState = r.readI8();
|
||||||
|
info.powerLevel = r.readI8();
|
||||||
|
} else if (len === 27 - 7) {
|
||||||
|
// 19 parsePaperState
|
||||||
|
// 20 parseRfidReadState
|
||||||
|
r.skip(18);
|
||||||
|
info.paperState = r.readI8();
|
||||||
|
info.rfidReadState = r.readI8();
|
||||||
|
} else if (len === 26 - 7) {
|
||||||
|
// 16 parseClosingState
|
||||||
|
// 17 parsePowerLevel
|
||||||
|
// 18 parsePaperState
|
||||||
|
// 19 parseRfidReadState
|
||||||
|
r.skip(15);
|
||||||
|
info.closingState = r.readI8();
|
||||||
|
info.powerLevel = r.readI8();
|
||||||
|
info.paperState = r.readI8();
|
||||||
|
info.rfidReadState = r.readI8();
|
||||||
|
} else if (len === 20 - 7) {
|
||||||
|
// b1
|
||||||
|
r.skip(9);
|
||||||
|
info.closingState = r.readI8();
|
||||||
|
info.powerLevel = r.readI8();
|
||||||
|
info.paperState = r.readI8();
|
||||||
|
info.rfidReadState = r.readI8();
|
||||||
|
// 10 parseClosingState
|
||||||
|
// 11 parsePowerLevel
|
||||||
|
// 12 parsePaperState
|
||||||
|
// 13 parseRfidReadState
|
||||||
|
} else {
|
||||||
|
throw new Error("Invalid heartbeat length");
|
||||||
|
}
|
||||||
|
r.end();
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
@ -1,2 +1,3 @@
|
|||||||
export * from "./data_reader"
|
export * from "./data_reader"
|
||||||
export * from "./parsed_packets"
|
export * from "./parsed_packets"
|
||||||
|
export * from "./abstraction"
|
@ -1,11 +1,22 @@
|
|||||||
|
// todo: migrate to Abstraction class
|
||||||
|
|
||||||
import { LabelType, NiimbotPacket, ResponseCommandId, SoundSettingsItemType, SoundSettingsType } from "..";
|
import { LabelType, NiimbotPacket, ResponseCommandId, SoundSettingsItemType, SoundSettingsType } from "..";
|
||||||
import { Utils, Validators } from "../..";
|
import { Utils, Validators } from "../..";
|
||||||
import { SequentialDataReader } from "../parser/data_reader";
|
import { SequentialDataReader } from "../parser/data_reader";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export interface ParsedPacket {
|
export interface ParsedPacket {
|
||||||
commandId: ResponseCommandId;
|
commandId: ResponseCommandId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PrintErrorDecoded extends ParsedPacket {
|
||||||
|
/**
|
||||||
|
* 8 - no paper?
|
||||||
|
*/
|
||||||
|
errorType: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SettingsInDecoded extends ParsedPacket {
|
export interface SettingsInDecoded extends ParsedPacket {
|
||||||
category: SoundSettingsType;
|
category: SoundSettingsType;
|
||||||
item: SoundSettingsItemType;
|
item: SoundSettingsItemType;
|
||||||
@ -31,9 +42,20 @@ export interface RfidInfoDecoded extends ParsedPacket {
|
|||||||
consumablesType: LabelType;
|
consumablesType: LabelType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PrinterInfoPrinterCodeDecoded extends ParsedPacket {
|
export interface PrinterInfoPrinterModelIdDecoded extends ParsedPacket {
|
||||||
/** See {@link PrinterId} */
|
/** See {@link PrinterId} */
|
||||||
code: number;
|
model: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PrintStatusDecoded extends ParsedPacket {
|
||||||
|
/** 0 – n */
|
||||||
|
page: number;
|
||||||
|
/** 0 – 100 */
|
||||||
|
pagePrintProgress: number;
|
||||||
|
/** 0 – 100 */
|
||||||
|
pageFeedProgress: number;
|
||||||
|
|
||||||
|
error?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/** closingState inverted on some printers */
|
/** closingState inverted on some printers */
|
||||||
@ -45,15 +67,15 @@ export interface HeartbeatDecoded extends ParsedPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class PacketParser {
|
export class PacketParser {
|
||||||
private static decodePrinterInfoSerialPacket(packet: NiimbotPacket): PrinterInfoSerialDecoded {
|
private static decodePrinterInfoSerial(packet: NiimbotPacket): PrinterInfoSerialDecoded {
|
||||||
return {
|
return {
|
||||||
commandId: packet.getCommand(),
|
commandId: packet.getCommand(),
|
||||||
serialNumber: Utils.u8ArrayToString(packet.getData()),
|
serialNumber: Utils.u8ArrayToString(packet.getData()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static decodeSettingsPacket(packet: NiimbotPacket): SettingsInDecoded {
|
private static decodeSettings(packet: NiimbotPacket): SettingsInDecoded {
|
||||||
Validators.u8ArrayLengthEqual(packet.getData(), 3);
|
Validators.u8ArrayLengthEquals(packet.getData(), 3);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
commandId: packet.getCommand(),
|
commandId: packet.getCommand(),
|
||||||
@ -63,8 +85,8 @@ export class PacketParser {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static decodePrinterInfoLabelTypePacket(packet: NiimbotPacket): PrinterInfoLabelTypeDecoded {
|
private static decodePrinterInfoLabelType(packet: NiimbotPacket): PrinterInfoLabelTypeDecoded {
|
||||||
Validators.u8ArrayLengthEqual(packet.getData(), 1);
|
Validators.u8ArrayLengthEquals(packet.getData(), 1);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
commandId: packet.getCommand(),
|
commandId: packet.getCommand(),
|
||||||
@ -72,12 +94,12 @@ export class PacketParser {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static decodePrinterInfoPrinterCodePacket(packet: NiimbotPacket): PrinterInfoPrinterCodeDecoded {
|
private static decodePrinterInfoPrinterModelId(packet: NiimbotPacket): PrinterInfoPrinterModelIdDecoded {
|
||||||
Validators.u8ArrayLengthEqual(packet.getData(), 2);
|
Validators.u8ArrayLengthEquals(packet.getData(), 2);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
commandId: packet.getCommand(),
|
commandId: packet.getCommand(),
|
||||||
code: Utils.bytesToI16(packet.getData()),
|
model: Utils.bytesToI16(packet.getData()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,22 +191,136 @@ export class PacketParser {
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
d110, 3 pages
|
||||||
|
5555 b3 04 00000000 b7aaaa
|
||||||
|
5555 b3 04 00000000 b7aaaa
|
||||||
|
5555 b3 04 00000000 b7aaaa
|
||||||
|
5555 b3 04 00001000 a7aaaa
|
||||||
|
5555 b3 04 00002100 96aaaa
|
||||||
|
5555 b3 04 00002100 96aaaa
|
||||||
|
5555 b3 04 00004200 f5aaaa
|
||||||
|
5555 b3 04 00005300 e4aaaa
|
||||||
|
5555 b3 04 00006400 d3aaaa
|
||||||
|
5555 b3 04 00006419 caaaaa
|
||||||
|
5555 b3 04 00006432 e1aaaa
|
||||||
|
5555 b3 04 0000644d 9eaaaa
|
||||||
|
|
||||||
|
5555 b3 04 00010000 b6aaaa
|
||||||
|
5555 b3 04 00010000 b6aaaa
|
||||||
|
5555 b3 04 00010000 b6aaaa
|
||||||
|
5555 b3 04 00012100 97aaaa
|
||||||
|
5555 b3 04 00012100 97aaaa
|
||||||
|
5555 b3 04 00012100 97aaaa
|
||||||
|
5555 b3 04 00014200 f4aaaa
|
||||||
|
5555 b3 04 00015300 e5aaaa
|
||||||
|
5555 b3 04 00016404 d6aaaa
|
||||||
|
5555 b3 04 00016420 f2aaaa
|
||||||
|
5555 b3 04 0001643f edaaaa
|
||||||
|
5555 b3 04 00016460 b2aaaa
|
||||||
|
5555 b3 04 00020000 b5aaaa
|
||||||
|
5555 b3 04 00020000 b5aaaa
|
||||||
|
5555 b3 04 00020000 b5aaaa
|
||||||
|
5555 b3 04 00022100 94aaaa
|
||||||
|
5555 b3 04 00022100 94aaaa
|
||||||
|
5555 b3 04 00023200 87aaaa
|
||||||
|
5555 b3 04 00025300 e6aaaa
|
||||||
|
5555 b3 04 00025300 e6aaaa
|
||||||
|
5555 b3 04 00026422 f3aaaa
|
||||||
|
5555 b3 04 00026446 97aaaa
|
||||||
|
|
||||||
|
5555 b3 04 00036464 b4aaaa
|
||||||
|
|
||||||
|
|
||||||
|
b3, 3 pages
|
||||||
|
|
||||||
|
5555 b3 0a 0000 0000031f00000000 a5aaaa
|
||||||
|
5555 b3 0a 0000 0000031a00000000 a0aaaa
|
||||||
|
5555 b3 0a 0000 0000031900010000 a2aaaa
|
||||||
|
5555 b3 0a 0000 0000031900010000 a2aaaa
|
||||||
|
5555 b3 0a 0000 0000031900010000 a2aaaa
|
||||||
|
5555 b3 0a 0000 0c00031900010000 aeaaaa
|
||||||
|
5555 b3 0a 0000 1d00031900010000 bfaaaa
|
||||||
|
5555 b3 0a 0000 2e00031900010000 8caaaa
|
||||||
|
5555 b3 0a 0000 4200031900010000 e0aaaa
|
||||||
|
5555 b3 0a 0000 5600031900010000 f4aaaa
|
||||||
|
5555 b3 0a 0000 642c031900010000 eaaaaa
|
||||||
|
|
||||||
|
5555 b3 0a 0001 0a00031900010000 a9aaaa
|
||||||
|
5555 b3 0a 0001 1e00031900010000 bdaaaa
|
||||||
|
5555 b3 0a 0001 2f00031900010000 8caaaa
|
||||||
|
5555 b3 0a 0001 4600031900010000 e5aaaa
|
||||||
|
5555 b3 0a 0001 5500031900010000 f6aaaa
|
||||||
|
5555 b3 0a 0001 6400031900010000 c7aaaa
|
||||||
|
5555 b3 0a 0001 6403031900010000 c4aaaa
|
||||||
|
5555 b3 0a 0001 640c031900010000 cbaaaa
|
||||||
|
5555 b3 0a 0001 6435031900010000 f2aaaa
|
||||||
|
5555 b3 0a 0001 6463031900010000 a4aaaa
|
||||||
|
5555 b3 0a 0002 1200031900010000 b2aaaa
|
||||||
|
5555 b3 0a 0002 2600031b00010000 84aaaa
|
||||||
|
5555 b3 0a 0002 3800031b00010000 9aaaaa
|
||||||
|
5555 b3 0a 0002 4b00031d00010000 efaaaa
|
||||||
|
5555 b3 0a 0002 5d00031e00010000 faaaaa
|
||||||
|
5555 b3 0a 0002 6401031e00010000 c2aaaa
|
||||||
|
5555 b3 0a 0002 6405031e00010000 c6aaaa
|
||||||
|
5555 b3 0a 0002 642f031e00010000 ecaaaa
|
||||||
|
5555 b3 0a 0002 6451031e00010000 92aaaa
|
||||||
|
|
||||||
|
5555 b3 0a 0003 6464031e00010000 a6aaaa
|
||||||
|
*/
|
||||||
|
|
||||||
|
private static decodePrintStatus(packet: NiimbotPacket): PrintStatusDecoded {
|
||||||
|
Validators.u8ArrayLengthAtLeast(packet.getData(), 4); // can be 8, 10, but ignore it for now
|
||||||
|
|
||||||
|
const result: PrintStatusDecoded = {
|
||||||
|
commandId: packet.getCommand(),
|
||||||
|
page: -1,
|
||||||
|
pagePrintProgress: -1,
|
||||||
|
pageFeedProgress: -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
const r = new SequentialDataReader(packet.getData());
|
||||||
|
result.page = r.readI16();
|
||||||
|
result.pagePrintProgress = r.readI8();
|
||||||
|
result.pageFeedProgress = r.readI8();
|
||||||
|
|
||||||
|
if(packet.getData().length === 10) {
|
||||||
|
r.skip(2)
|
||||||
|
result.error = r.readI8();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static decodePrintError(packet: NiimbotPacket): PrintErrorDecoded {
|
||||||
|
Validators.u8ArrayLengthEquals(packet.getData(), 1);
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandId: packet.getCommand(),
|
||||||
|
errorType: packet.getData()[0]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static parse(packet: NiimbotPacket): ParsedPacket | undefined {
|
public static parse(packet: NiimbotPacket): ParsedPacket | undefined {
|
||||||
const command: ResponseCommandId = packet.getCommand();
|
const command: ResponseCommandId = packet.getCommand();
|
||||||
|
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case ResponseCommandId.In_SoundSettings:
|
case ResponseCommandId.In_SoundSettings:
|
||||||
return this.decodeSettingsPacket(packet);
|
return this.decodeSettings(packet);
|
||||||
case ResponseCommandId.In_PrinterInfoSerialNumber:
|
case ResponseCommandId.In_PrinterInfoSerialNumber:
|
||||||
return this.decodePrinterInfoSerialPacket(packet);
|
return this.decodePrinterInfoSerial(packet);
|
||||||
case ResponseCommandId.In_PrinterInfoLabelType:
|
case ResponseCommandId.In_PrinterInfoLabelType:
|
||||||
return this.decodePrinterInfoLabelTypePacket(packet);
|
return this.decodePrinterInfoLabelType(packet);
|
||||||
case ResponseCommandId.In_RfidInfo:
|
case ResponseCommandId.In_RfidInfo:
|
||||||
return this.decodeRfidInfo(packet);
|
return this.decodeRfidInfo(packet);
|
||||||
case ResponseCommandId.In_PrinterInfoPrinterCode:
|
case ResponseCommandId.In_PrinterInfoPrinterCode:
|
||||||
return this.decodePrinterInfoPrinterCodePacket(packet);
|
return this.decodePrinterInfoPrinterModelId(packet);
|
||||||
case ResponseCommandId.In_Heartbeat1:
|
case ResponseCommandId.In_Heartbeat1:
|
||||||
return this.decodeHeartbeat(packet);
|
return this.decodeHeartbeat(packet);
|
||||||
|
case ResponseCommandId.In_PrintError:
|
||||||
|
return this.decodePrintError(packet);
|
||||||
|
case ResponseCommandId.In_PrintStatus:
|
||||||
|
return this.decodePrintStatus(packet);
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,8 @@ export enum ResponseCommandId {
|
|||||||
In_PrinterInfoSoftWareVersion = 0x49,
|
In_PrinterInfoSoftWareVersion = 0x49,
|
||||||
In_PrinterInfoUnknown1 = 0x4f,
|
In_PrinterInfoUnknown1 = 0x4f,
|
||||||
IN_PrinterStatusData = 0xb5,
|
IN_PrinterStatusData = 0xb5,
|
||||||
In_PrintParamsError = 0xdb, // For example, sent on SetPageSize when page print is not started
|
In_PrintStatus = 0xb3,
|
||||||
|
In_PrintError = 0xdb, // For example, sent on SetPageSize when page print is not started
|
||||||
In_PrintQuantity = 0x16,
|
In_PrintQuantity = 0x16,
|
||||||
In_PrintStart = 0x02,
|
In_PrintStart = 0x02,
|
||||||
In_RfidInfo = 0x1b,
|
In_RfidInfo = 0x1b,
|
||||||
@ -72,7 +73,7 @@ export enum PrinterInfoType {
|
|||||||
Language = 6,
|
Language = 6,
|
||||||
AutoShutDownTime = 7,
|
AutoShutDownTime = 7,
|
||||||
/** See {@link PrinterId} */
|
/** See {@link PrinterId} */
|
||||||
PrinterCode = 8,
|
PrinterModelId = 8,
|
||||||
SoftWareVersion = 9,
|
SoftWareVersion = 9,
|
||||||
Electricity = 10,
|
Electricity = 10,
|
||||||
SerialNumber = 11,
|
SerialNumber = 11,
|
||||||
@ -127,7 +128,7 @@ export enum PowerLevel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Generated from android app (assets/flutter_assets/assets/config/printerList.json) */
|
/** Generated from android app (assets/flutter_assets/assets/config/printerList.json) */
|
||||||
export enum PrinterId {
|
export enum PrinterModelId {
|
||||||
UNKNOWN = 0,
|
UNKNOWN = 0,
|
||||||
T6 = 51715,
|
T6 = 51715,
|
||||||
TP2M_H = 4609,
|
TP2M_H = 4609,
|
||||||
|
@ -4,7 +4,7 @@ export class Utils {
|
|||||||
return hex.length === 1 ? `0${hex}` : hex;
|
return hex.length === 1 ? `0${hex}` : hex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bufToHex(buf: DataView | Uint8Array, separator: string = " "): string {
|
public static bufToHex(buf: DataView | Uint8Array | number[], separator: string = " "): string {
|
||||||
const arr: number[] = buf instanceof DataView ? this.dataViewToNumberArray(buf) : Array.from(buf);
|
const arr: number[] = buf instanceof DataView ? this.dataViewToNumberArray(buf) : Array.from(buf);
|
||||||
return arr.map(Utils.numberToHex).join(separator);
|
return arr.map(Utils.numberToHex).join(separator);
|
||||||
}
|
}
|
||||||
@ -53,7 +53,7 @@ export class Utils {
|
|||||||
|
|
||||||
/** Big endian */
|
/** Big endian */
|
||||||
public static bytesToI16(arr: Uint8Array): number {
|
public static bytesToI16(arr: Uint8Array): number {
|
||||||
Validators.u8ArrayLengthEqual(arr, 2);
|
Validators.u8ArrayLengthEquals(arr, 2);
|
||||||
return arr[0] * 256 + arr[1];
|
return arr[0] * 256 + arr[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,9 +80,14 @@ export class Validators {
|
|||||||
throw new Error(message ?? "Arrays must be equal");
|
throw new Error(message ?? "Arrays must be equal");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static u8ArrayLengthEqual(a: Uint8Array, len: number, message?: string): void {
|
public static u8ArrayLengthEquals(a: Uint8Array, len: number, message?: string): void {
|
||||||
if (a.length !== len) {
|
if (a.length !== len) {
|
||||||
throw new Error(message ?? `Array length must be ${len}`);
|
throw new Error(message ?? `Array length must be ${len}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public static u8ArrayLengthAtLeast(a: Uint8Array, len: number, message?: string): void {
|
||||||
|
if (a.length < len) {
|
||||||
|
throw new Error(message ?? `Array length must be at least ${len}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user