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

This commit is contained in:
Bot 2024-07-31 01:00:02 +03:00 committed by multimote
parent 319a33084e
commit 2e7cfe46b0
6 changed files with 103 additions and 47 deletions

View File

@ -54,6 +54,22 @@ export class ImageEditorUtils {
} }
} }
static addImageWithFilePicker(fabricCanvas: fabric.Canvas) {
const input: HTMLInputElement = document.createElement("input");
input.type = "file";
input.onchange = (e: Event) => {
let target = e.target as HTMLInputElement;
if (target.files !== null) {
let file: File = target.files[0];
this.addImageFile(fabricCanvas, file);
}
};
input.click();
}
public static addText(canvas: fabric.Canvas, text?: string): void { public static addText(canvas: fabric.Canvas, text?: string): void {
const obj = new fabric.IText(text ?? "Text", { const obj = new fabric.IText(text ?? "Text", {
...this.OBJECT_DEFAULTS, ...this.OBJECT_DEFAULTS,

View File

@ -51,23 +51,24 @@
const onUpdateLabelProps = () => { const onUpdateLabelProps = () => {
labelProps = labelProps; // trigger update labelProps = labelProps; // trigger update
fabricCanvas.setDimensions(labelProps.size); fabricCanvas.setDimensions(labelProps.size);
localStorage.setItem("last_label_props", JSON.stringify(labelProps));
}; };
const onSaveClicked = () => { const onSaveClicked = () => {
const data = fabricCanvas.toJSON(); const data = fabricCanvas.toJSON();
localStorage.setItem("canvas_data", JSON.stringify(data)); localStorage.setItem("saved_canvas_data", JSON.stringify(data));
localStorage.setItem("canvas_props", JSON.stringify(labelProps)); localStorage.setItem("saved_canvas_props", JSON.stringify(labelProps));
}; };
const onLoadClicked = () => { const onLoadClicked = () => {
const props = localStorage.getItem("canvas_props"); const props = localStorage.getItem("saved_canvas_props");
if (props) { if (props) {
const parsedProps = JSON.parse(props); const parsedProps = JSON.parse(props);
labelProps = parsedProps; labelProps = parsedProps;
onUpdateLabelProps(); onUpdateLabelProps();
} }
const data = localStorage.getItem("canvas_data"); const data = localStorage.getItem("saved_canvas_data");
fabricCanvas.loadFromJSON( fabricCanvas.loadFromJSON(
data, data,
() => { () => {
@ -89,6 +90,8 @@
ImageEditorUtils.addCircle(fabricCanvas); ImageEditorUtils.addCircle(fabricCanvas);
} else if (name === "rectangle") { } else if (name === "rectangle") {
ImageEditorUtils.addRect(fabricCanvas); ImageEditorUtils.addRect(fabricCanvas);
} else if (name === "image") {
ImageEditorUtils.addImageWithFilePicker(fabricCanvas);
} }
}; };
@ -116,6 +119,19 @@
}; };
onMount(() => { onMount(() => {
const savedLabelPropsStr: string | null = localStorage.getItem("last_label_props");
if (savedLabelPropsStr != null) {
try {
const obj = JSON.parse(savedLabelPropsStr);
if ("size" in obj && "width" in obj.size && "height" in obj.size && ["top", "left"].includes(obj.startPos)) {
labelProps = obj as LabelProps;
}
} catch (e) {
console.error(e);
}
}
fabricCanvas = new fabric.Canvas(htmlCanvas, { fabricCanvas = new fabric.Canvas(htmlCanvas, {
width: labelProps.size.width, width: labelProps.size.width,
height: labelProps.size.height, height: labelProps.size.height,
@ -124,7 +140,6 @@
ImageEditorUtils.addText(fabricCanvas); ImageEditorUtils.addText(fabricCanvas);
fabricCanvas.on("object:moving", (e: fabric.IEvent<MouseEvent>) => { fabricCanvas.on("object:moving", (e: fabric.IEvent<MouseEvent>) => {
const grid = 5; const grid = 5;
if (e.target && e.target.left !== undefined && e.target.top !== undefined) { if (e.target && e.target.left !== undefined && e.target.top !== undefined) {
@ -198,7 +213,7 @@
<div class="col d-flex justify-content-center"> <div class="col d-flex justify-content-center">
<div class="toolbar d-flex flex-wrap gap-1 justify-content-center align-items-center"> <div class="toolbar d-flex flex-wrap gap-1 justify-content-center align-items-center">
{#if selectedCount > 0} {#if selectedCount > 0}
<button class="btn btn-sm btn-danger me-1" on:click={deleteSelected}><FaIcon icon="trash" /></button> <button class="btn btn-sm btn-danger me-1" on:click={deleteSelected}><FaIcon icon="trash" /></button>
{/if} {/if}
{#if selectedObject && selectedCount === 1} {#if selectedObject && selectedCount === 1}

View File

@ -26,6 +26,10 @@
<button class="btn me-1" on:click={() => onSubmit("circle")}> <button class="btn me-1" on:click={() => onSubmit("circle")}>
<FaIcon icon="circle-dot" /> Circle <FaIcon icon="circle-dot" /> Circle
</button> </button>
<button class="btn me-1" on:click={() => onSubmit("image")}>
<FaIcon icon="image" /> Image
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -4,12 +4,7 @@
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 { import { type EncodedImage, ImageEncoder, PacketGenerator, ProtocolVersion } from "@mmote/niimbluelib";
type EncodedImage,
ImageEncoder,
PacketGenerator,
ProtocolVersion,
} from "@mmote/niimbluelib";
import type { LabelProps } from "../types"; import type { LabelProps } from "../types";
import FaIcon from "./FaIcon.svelte"; import FaIcon from "./FaIcon.svelte";
@ -19,59 +14,64 @@
let modalElement: HTMLElement; let modalElement: HTMLElement;
let previewCanvas: HTMLCanvasElement; let previewCanvas: HTMLCanvasElement;
let printState: "idle" | "sending" | "printing" = "idle";
let modal: Modal; let modal: Modal;
let sendProgress: number = 0;
let printProgress: number = 0; // todo: more progress data 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 postProcessType: "threshold" | "dither"; let postProcessType: "threshold" | "dither";
let thresholdValue: number = 140; let thresholdValue: number = 140;
let imgData: ImageData; let imgData: ImageData;
let imgContext: CanvasRenderingContext2D; let imgContext: CanvasRenderingContext2D;
let printTaskVersion: ProtocolVersion = ProtocolVersion.V3; let printTaskVersion: ProtocolVersion = ProtocolVersion.V3;
let statusTimer: NodeJS.Timeout | undefined = undefined; let statusTimer: NodeJS.Timeout | undefined = undefined;
let printError: boolean = false; let error: string = "";
const disconnected = derived(connectionState, ($connectionState) => $connectionState !== "connected"); const disconnected = derived(connectionState, ($connectionState) => $connectionState !== "connected");
const cancelPrint = async () => { const endPrint = async () => {
clearInterval(statusTimer); clearInterval(statusTimer);
if (!$disconnected && printed) { if (!$disconnected && printState !== "idle") {
await $printerClient.sendPacket(PacketGenerator.printEnd()); await $printerClient.abstraction.printEnd();
} }
printed = false; printState = "idle";
printProgress = 0; printProgress = 0;
}; };
const onPrint = async () => { const onPrint = async () => {
printError = false;
const encoded: EncodedImage = ImageEncoder.encodeCanvas(previewCanvas, labelProps.startPos); const encoded: EncodedImage = ImageEncoder.encodeCanvas(previewCanvas, labelProps.startPos);
const packets = PacketGenerator.generatePrintSequence(printTaskVersion, encoded, { quantity, density });
for (let i = 0; i < packets.length; i++) { printState = "sending";
sendProgress = Math.round(((i + 1) / packets.length) * 100); error = "";
await $printerClient.sendPacketWaitResponse(packets[i], 10_000);
try {
await $printerClient.abstraction.print(printTaskVersion, encoded, { quantity, density });
} catch (e) {
error = `${e}`;
console.error(e);
return;
} }
printState = "printing";
statusTimer = setInterval(async () => { statusTimer = setInterval(async () => {
try { try {
const status = await $printerClient.abstraction.getPrintStatus(); const status = await $printerClient.abstraction.getPrintStatus();
printProgress = status.pagePrintProgress; printProgress = status.pagePrintProgress;
if (status.page === quantity && status.pagePrintProgress === 100 && status.pageFeedProgress === 100) { if (status.page === quantity && status.pagePrintProgress === 100 && status.pageFeedProgress === 100) {
await cancelPrint(); await endPrint();
} }
} catch (e) { } catch (e) {
error = `${e}`;
console.error(e); console.error(e);
await cancelPrint(); await endPrint();
printError = true;
} }
}, 100); }, 100);
printed = true; printState = "idle";
}; };
const updatePreview = () => { const updatePreview = () => {
@ -102,13 +102,13 @@
modal = new Modal(modalElement); modal = new Modal(modalElement);
modal.show(); modal.show();
modalElement.addEventListener("hidden.bs.modal", async () => { modalElement.addEventListener("hidden.bs.modal", async () => {
cancelPrint(); endPrint();
onClosed(); onClosed();
}); });
const taskVer = $printerClient?.getCapabilities().printTaskVersion; const taskVer = $printerClient?.getCapabilities().printTaskVersion;
if (taskVer !== undefined && taskVer !== ProtocolVersion.UNKNOWN) { if (taskVer !== undefined && taskVer !== ProtocolVersion.UNKNOWN) {
console.log(`Detected print task version: ${ProtocolVersion[taskVer]}`) console.log(`Detected print task version: ${ProtocolVersion[taskVer]}`);
printTaskVersion = taskVer; printTaskVersion = taskVer;
} }
}); });
@ -139,13 +139,8 @@
<div class="modal-body text-center"> <div class="modal-body text-center">
<canvas class="print-start-{labelProps.startPos}" bind:this={previewCanvas}></canvas> <canvas class="print-start-{labelProps.startPos}" bind:this={previewCanvas}></canvas>
{#if sendProgress != 0 && sendProgress != 100} {#if printState === "sending"}
<div> <div>Sending...</div>
Sending...
<div class="progress" role="progressbar">
<div class="progress-bar" style="width: {sendProgress}%">{sendProgress}%</div>
</div>
</div>
{/if} {/if}
{#if printProgress != 0 && printProgress != 100} {#if printProgress != 0 && printProgress != 100}
<div> <div>
@ -157,8 +152,8 @@
{/if} {/if}
</div> </div>
{#if printError} {#if error}
<div class="alert alert-danger" role="alert">Print error</div> <div class="alert alert-danger" role="alert">{error}</div>
{/if} {/if}
<div class="modal-footer"> <div class="modal-footer">
@ -204,13 +199,13 @@
<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 printState !== "idle"}
<button type="button" class="btn btn-primary" disabled={$disconnected} on:click={cancelPrint}> <button type="button" class="btn btn-primary" disabled={$disconnected} on:click={endPrint}>
Cancel print Cancel print
</button> </button>
{/if} {/if}
<button type="button" class="btn btn-primary" disabled={$disconnected || printed} on:click={onPrint}> <button type="button" class="btn btn-primary" disabled={$disconnected || printState !== "idle"} on:click={onPrint}>
{#if $disconnected} {#if $disconnected}
Printer is not connected Printer is not connected
{:else} {:else}

View File

@ -15,4 +15,4 @@ export type LabelPreset = {
startPosition: HeadPosition; startPosition: HeadPosition;
}; };
export type OjectType = "text" | "rectangle" | "line" | "circle" export type OjectType = "text" | "rectangle" | "line" | "circle" | "image"

View File

@ -7,12 +7,17 @@ import {
NiimbotPacket, NiimbotPacket,
PacketGenerator, PacketGenerator,
PrinterInfoType, PrinterInfoType,
PrintOptions,
ResponseCommandId, ResponseCommandId,
SoundSettingsItemType, SoundSettingsItemType,
SoundSettingsType, SoundSettingsType,
} from "."; EncodedImage,
import { NiimbotAbstractClient, Utils, Validators } from ".."; NiimbotAbstractClient,
import { PrinterModel } from "../printers"; Utils,
Validators,
PrinterModel,
ProtocolVersion,
} from "..";
import { SequentialDataReader } from "./data_reader"; import { SequentialDataReader } from "./data_reader";
export class PrintError extends Error { export class PrintError extends Error {
@ -64,8 +69,9 @@ export interface PrinterStatusData {
/** Not sure for name. */ /** Not sure for name. */
export class Abstraction { export class Abstraction {
private readonly DEFAULT_TIMEOUT: number = 1_000;
private client: NiimbotAbstractClient; private client: NiimbotAbstractClient;
private timeout: number = 1000; private timeout: number = this.DEFAULT_TIMEOUT;
constructor(client: NiimbotAbstractClient) { constructor(client: NiimbotAbstractClient) {
this.client = client; this.client = client;
@ -79,6 +85,10 @@ export class Abstraction {
this.timeout = value; this.timeout = value;
} }
public setDefaultTimeout() {
this.timeout = this.DEFAULT_TIMEOUT;
}
/** Send packet and wait for response */ /** Send packet and wait for response */
private async send(packet: NiimbotPacket): Promise<NiimbotPacket> { private async send(packet: NiimbotPacket): Promise<NiimbotPacket> {
return this.client.sendPacketWaitResponse(packet, this.timeout); return this.client.sendPacketWaitResponse(packet, this.timeout);
@ -277,4 +287,20 @@ export class Abstraction {
public async setSoundEnabled(soundType: SoundSettingsItemType, value: boolean): Promise<void> { public async setSoundEnabled(soundType: SoundSettingsItemType, value: boolean): Promise<void> {
await this.send(PacketGenerator.setSoundSettings(soundType, value)); await this.send(PacketGenerator.setSoundSettings(soundType, value));
} }
public async print(protoVersion: ProtocolVersion, image: EncodedImage, options?: PrintOptions, timeout?:number): Promise<void> {
this.setTimeout(timeout ?? 10_000);
const packets: NiimbotPacket[] = PacketGenerator.generatePrintSequence(protoVersion, image, options);
try {
for (const element of packets) {
await this.send(element);
}
} finally {
this.setDefaultTimeout();
}
}
public async printEnd(): Promise<void> {
await this.send(PacketGenerator.printEnd());
}
} }