Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 41 additions & 15 deletions src/Commands/CommandSet.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as Conf from '../Configs/index.js';
import * as Commands from './Commands.js';
import { TranspileDocumentError, type TranspiledDocumentState } from "./TranspileCommand.js";
import { MessageParsingError, RawMessageTransformer, StringMessageTransformer, type IMessageHandlerResult, type MessageTransformer } from './Messages.js';
import { MessageParsingError, RawMessageTransformer, StringMessageTransformer, type CommandSetMessageHandlerDelegate, type IMessageHandlerResult, type MessageTransformer } from './Messages.js';
import type { PrinterConfig } from './PrinterConfig.js';

/** How a command should be wrapped into a form, if at all */
Expand All @@ -15,9 +15,10 @@ export enum CommandFormInclusionMode {
/** Describes a command set for a printer. */
export interface CommandSet<TMsgType extends Conf.MessageArrayLike> {

/** Parse a message object received from the printer. */
parseMessage<TReceived extends Conf.MessageArrayLike>(
/** Handle the dispatch of a message received from the printer. */
handleMessage<TReceived extends Conf.MessageArrayLike>(
msg: TReceived,
config: PrinterConfig,
sentCommand?: Commands.IPrinterCommand
): IMessageHandlerResult<TReceived>;

Expand All @@ -35,6 +36,11 @@ export interface CommandSet<TMsgType extends Conf.MessageArrayLike> {
isCommandNonFormCommand(cmd: Commands.IPrinterCommand): boolean;
/** Combine separate commands into one. */
combineCommands(...commands: TMsgType[]): TMsgType;
/** Dispatch a message to the appropriate handler for it. */
callMessageHandler(
message: TMsgType,
sentCommand?: Commands.IPrinterCommand
): IMessageHandlerResult<TMsgType>

/** Expand a printer config to a language-specific config. */
getConfig(config: PrinterConfig): PrinterConfig;
Expand Down Expand Up @@ -91,24 +97,27 @@ export abstract class PrinterCommandSet<TMsgType extends Conf.MessageArrayLike>

protected messageTransformer: MessageTransformer<TMsgType>;

protected messageHandlerDelegate: CommandSetMessageHandlerDelegate<TMsgType>;

protected commandMap = new Map<Commands.CommandAnyType, IPrinterCommandMapping<TMsgType>>;

protected constructor(
transformer : MessageTransformer<TMsgType>,
implementedLanguage: Conf.PrinterCommandLanguage,
basicCommands : Record<Commands.CommandType, IPrinterCommandMapping<TMsgType>>,
extendedCommands : IPrinterCommandMapping<TMsgType>[] = []
transformer : MessageTransformer<TMsgType>,
messageHandlerDelegate: CommandSetMessageHandlerDelegate<TMsgType>,
implementedLanguage : Conf.PrinterCommandLanguage,
basicCommands : Record<Commands.CommandType, IPrinterCommandMapping<TMsgType>>,
extendedCommands : IPrinterCommandMapping<TMsgType>[] = []
) {
this.cmdLanguage = implementedLanguage;
this.messageTransformer = transformer;
this.messageHandlerDelegate = messageHandlerDelegate;
for (const cmdType of Commands.basicCommandTypes) {
this.commandMap.set(cmdType, basicCommands[cmdType]);
}
// Support overriding behaviors
extendedCommands.forEach(c => this.commandMap.set(c.commandType, c));
}

abstract parseMessage<TReceived extends Conf.MessageArrayLike>(msg: TReceived, sentCommand?: Commands.IPrinterCommand): IMessageHandlerResult<TReceived>;
abstract get documentStartPrefix(): TMsgType;
abstract get documentEndSuffix(): TMsgType;

Expand All @@ -124,6 +133,19 @@ export abstract class PrinterCommandSet<TMsgType extends Conf.MessageArrayLike>
return handler(cmd, docMetadata, this);
}

public handleMessage<TReceived extends Conf.MessageArrayLike>(
msg: TReceived,
config: PrinterConfig,
sentCommand?: Commands.IPrinterCommand,
): IMessageHandlerResult<TReceived> {
return this.messageHandlerDelegate(
this,
msg,
config,
sentCommand
);
}

protected getMappedCmd(cmd: Commands.IPrinterCommand) {
return this.commandMap.get(Commands.getCommandAnyType(cmd));
}
Expand All @@ -147,7 +169,7 @@ export abstract class PrinterCommandSet<TMsgType extends Conf.MessageArrayLike>
public callMessageHandler(
message: TMsgType,
sentCommand?: Commands.IPrinterCommand
) {
): IMessageHandlerResult<TMsgType> {
if (sentCommand === undefined) {
throw new MessageParsingError(
`Received a command reply message without 'sentCommand' being provided, can't handle this message.`,
Expand Down Expand Up @@ -195,12 +217,14 @@ export abstract class RawCommandSet extends PrinterCommandSet<Uint8Array> {
}

protected constructor(
implementedLanguage: Conf.PrinterCommandLanguage,
basicCommands : Record<Commands.CommandType, IPrinterCommandMapping<Uint8Array>>,
extendedCommands : IPrinterCommandMapping<Uint8Array>[] = []
implementedLanguage : Conf.PrinterCommandLanguage,
messageHandlerDelegate: CommandSetMessageHandlerDelegate<Uint8Array>,
basicCommands : Record<Commands.CommandType, IPrinterCommandMapping<Uint8Array>>,
extendedCommands : IPrinterCommandMapping<Uint8Array>[] = []
) {
super(
new RawMessageTransformer(),
messageHandlerDelegate,
implementedLanguage,
basicCommands,
extendedCommands
Expand All @@ -216,12 +240,14 @@ export abstract class StringCommandSet extends PrinterCommandSet<string> {
}

protected constructor(
implementedLanguage: Conf.PrinterCommandLanguage,
basicCommands : Record<Commands.CommandType, IPrinterCommandMapping<string>>,
extendedCommands : IPrinterCommandMapping<string>[] = []
implementedLanguage : Conf.PrinterCommandLanguage,
messageHandlerDelegate: CommandSetMessageHandlerDelegate<string>,
basicCommands : Record<Commands.CommandType, IPrinterCommandMapping<string>>,
extendedCommands : IPrinterCommandMapping<string>[] = []
) {
super(
new StringMessageTransformer(),
messageHandlerDelegate,
implementedLanguage,
basicCommands,
extendedCommands
Expand Down
24 changes: 15 additions & 9 deletions src/Commands/Messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as Conf from '../Configs/index.js';
import type { IDeviceInformation } from "web-device-mux";
import type { IPrinterCommand } from "./Commands.js";
import type { CommandSet } from './CommandSet.js';
import type { PrinterConfig } from './PrinterConfig.js';

export type PrinterMessage
= ISettingUpdateMessage
Expand Down Expand Up @@ -96,17 +97,13 @@ export type AwaitedCommand = {
reject?: (reason?: unknown) => void,
}

export type UpdateClass<Type> = {
-readonly [Property in keyof Type]?: Type[Property];
};

/** A printer settings message, describing printer configuration status. */
export interface ISettingUpdateMessage {
messageType: 'SettingUpdateMessage';

printerHardware?: UpdateClass<Conf.IPrinterHardware>;
printerMedia ?: UpdateClass<Conf.IPrinterMedia>;
printerSettings?: UpdateClass<Conf.IPrinterSettings>;
printerHardware?: Conf.UpdateFor<Conf.IPrinterHardware>;
printerMedia ?: Conf.UpdateFor<Conf.IPrinterMedia>;
printerSettings?: Conf.UpdateFor<Conf.IPrinterSettings>;
}

/** A status message sent by the printer. */
Expand Down Expand Up @@ -213,6 +210,14 @@ export interface IMessageHandlerResult<TInput> {
remainder: TInput
}

export type CommandSetMessageHandlerDelegate<TMsgType extends Conf.MessageArrayLike> =
<TReceived extends Conf.MessageArrayLike>(
cmdSet: CommandSet<TMsgType>,
message: TReceived,
config: PrinterConfig,
sentCommand?: IPrinterCommand
) => IMessageHandlerResult<TReceived>;

/** An error indicating a problem parsing a received message. */
export class MessageParsingError extends Util.WebZlpError {
public readonly receivedMessage: Conf.MessageArrayLike;
Expand All @@ -237,6 +242,7 @@ export function deviceInfoToOptionsUpdate(deviceInfo: IDeviceInformation): ISett
export async function parseRaw<TInput extends Conf.MessageArrayLike>(
input: TInput,
commandSet: CommandSet<Conf.MessageArrayLike>,
config: PrinterConfig,
awaitedCommands: AwaitedCommand[]
): Promise<{ remainderMsg: TInput; remainderCommands: AwaitedCommand[], messages: PrinterMessage[]; }> {
let remainderMsg = input;
Expand All @@ -249,7 +255,7 @@ export async function parseRaw<TInput extends Conf.MessageArrayLike>(
do {
if (remainderCommands.length === 0) {
// No candidate commands, treat as raw!
const parseResult = commandSet.parseMessage(remainderMsg);
const parseResult = commandSet.handleMessage(remainderMsg, config);
remainderMsg = parseResult.remainder;
incomplete = parseResult.messageIncomplete;
parseResult.messages.forEach(m => messages.push(m));
Expand All @@ -261,7 +267,7 @@ export async function parseRaw<TInput extends Conf.MessageArrayLike>(
return true;
}

const parseResult = commandSet.parseMessage(remainderMsg, c.cmd);
const parseResult = commandSet.handleMessage(remainderMsg, config, c.cmd);
if (parseResult.messageMatchedExpectedCommand) {
// The command found its response! Mark it accordingly.
if (parseResult.messageIncomplete) {
Expand Down
38 changes: 5 additions & 33 deletions src/Configs/ConfigurationTypes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import * as Util from '../Util/index.js';

/** Utility type to create an 'update' object, making all properties optional and not readonly. */
export type UpdateFor<Type> = {
-readonly [Property in keyof Type]?: Type[Property];
};

/** The darkness of the printer setting, higher being printing darker. */
export type DarknessPercent = Util.Percent;

Expand Down Expand Up @@ -245,18 +250,6 @@ export interface IPrinterHardware {
readonly speedTable: SpeedTable;
}

export interface IPrinterHardwareUpdate {
dpi?: number;
firmware?: string;
manufacturer?: string;
model?: string;
maxMediaWidthDots?: number;
maxMediaLengthDots?: number;
maxMediaDarkness?: number,
serialNumber?: string;
speedTable?: SpeedTable;
}

/** Settings for printer behavior */
export interface IPrinterSettings {
/** What percentage of backfeed happens after cutting/taking label, vs before printing. */
Expand All @@ -266,11 +259,6 @@ export interface IPrinterSettings {
readonly feedButtonMode: FeedButtonMode;
}

export interface IPrinterSettingsUpdate {
backfeedAfterTaken?: BackfeedAfterTaken;
feedButtonMode?: FeedButtonMode;
}

/** Printer options related to the media media being printed */
export interface IPrinterMedia {
/** How dark to print. 0 is blank, 99 is max darkness */
Expand Down Expand Up @@ -326,19 +314,3 @@ export interface IPrinterMedia {
/** Whether the media prints right-side-up or upside-down. */
printOrientation: PrintOrientation;
}

export interface IPrinterMediaUpdate {
darknessPercent?: DarknessPercent;
mediaGapDetectMode?: MediaMediaGapDetectionMode;
mediaGapDots?: number;
mediaLineOffsetDots?: number;
mediaLengthDots?: number;
mediaWidthDots?: number;
mediaPrintOriginOffsetDots?: Coordinate;
mediaDimensionRoundingStep?: number;
thermalPrintMode?: ThermalPrintMode;
mediaPrintMode?: MediaPrintMode;
printOrientation?: PrintOrientation;

speed?: PrintSpeedSettings;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect, describe, it } from 'vitest';
import * as Util from '../../Util/index.js';
import * as Cmds from '../../Commands/index.js';
import { EplPrinterCommandSet } from './index.js';
import { addImageCommand } from './BasicCommands.js';

// Class pulled from jest-mock-canvas which I can't seem to actually import.
class ImageData {
Expand Down Expand Up @@ -83,34 +83,32 @@ function getImageDataInput(width: number, height: number, fill: number, alpha?:
return arr;
}


describe('EPL Image Conversion', () => {
describe('addImageCommand', () => {
it('Should convert blank images to valid command', () => {
const cmdSet = new EplPrinterCommandSet();
const imageData = new ImageData(getImageDataInput(8, 1, 0), 8, 1);
const bitmap = Util.BitmapGRF.fromCanvasImageData(imageData, { trimWhitespace: false });
const cmd = new Cmds.AddImageCommand(bitmap, {});
const doc = Cmds.getNewTranspileState(new Cmds.PrinterConfig());
const resultCmd = cmdSet['addImageCommand'](cmd, doc);
const resultCmd = addImageCommand(cmd, doc);

const expectedCmd = 'GW0,0,1,1,ÿ\r\n';

expect(resultCmd).toEqual(expectedCmd);
});

it('Should apply offsets in command', () => {
const cmdSet = new EplPrinterCommandSet();
const imageData = new ImageData(getImageDataInput(8, 1, 0), 8, 1);
const bitmap = Util.BitmapGRF.fromCanvasImageData(imageData, { trimWhitespace: false });
const cmd = new Cmds.AddImageCommand(bitmap, {});
const appliedOffset = 10;
const doc = Cmds.getNewTranspileState(new Cmds.PrinterConfig());
doc.horizontalOffset = appliedOffset;
doc.verticalOffset = appliedOffset * 2;
const resultCmd = cmdSet['addImageCommand'](cmd, doc);
const resultCmd = addImageCommand(cmd, doc);

const expectedCmd = `GW${appliedOffset},${appliedOffset * 2},1,1,ÿ\r\n`;

expect(resultCmd).toEqual(expectedCmd);
});

});
Loading
Loading