Working tree changes 2024-07-31 01:00
All checks were successful
Test project build / Build (push) Successful in 1m17s
All checks were successful
Test project build / Build (push) Successful in 1m17s
This commit is contained in:
parent
319a33084e
commit
2e7cfe46b0
@ -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,
|
||||||
|
@ -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}
|
||||||
|
@ -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>
|
||||||
|
@ -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}
|
||||||
|
@ -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"
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user