diff --git a/demo/index.html b/demo/index.html index 9fe2a4c..b8861d5 100644 --- a/demo/index.html +++ b/demo/index.html @@ -91,6 +91,9 @@ // For this demo we're going to make use of the USB printer manager // so it can take care of concerns like the USB connect and disconnect events. + // For this demo we're going to make use of the USB printer manager + // so it can take care of concerns like the USB connect and disconnect events. + // It's a handy-dandy feature included from the web-device-mux library! // We need to tell it what type of device it's managing, and how to filter // USB devices that are receipt printers. @@ -220,9 +223,7 @@
Fix the issue, then dismiss this alert to check the status again.
`, // And when the alert is dismissed, check the status again! - () => printer.sendDocument({ - commands: [new WebReceipt.GetStatus()] - }) + () => printer.sendDocument(WebReceipt.ReadyToPrintDocuments.getStatus), ); }); }); @@ -339,30 +340,21 @@Fix the issue, then dismiss this alert to check the status again.
`, // And when the alert is dismissed, check the status again! - () => printer.sendDocument({ - commands: [new WebReceipt.GetStatus()] - }) + () => printer.sendDocument(WebReceipt.ReadyToPrintDocuments.getStatus), ); }); }); @@ -268,30 +266,21 @@ class BasicDocumentPrinterApp { e.preventDefault(); const printerIdx = (e.currentTarget as HTMLAnchorElement).dataset.printerIdx as unknown as number; const printer = this.printers[printerIdx]; - const doc = { - commands: [new WebReceipt.TestPrint('rolling')] - }; - await printer.sendDocument(doc); + await printer.sendDocument(WebReceipt.ReadyToPrintDocuments.printTest); }); document.getElementById(`printconfig_${idx}`)! .addEventListener('click', async (e) => { e.preventDefault(); const printerIdx = (e.currentTarget as HTMLAnchorElement).dataset.printerIdx as unknown as number; const printer = this.printers[printerIdx]; - const doc = { - commands: [new WebReceipt.TestPrint('printerStatus')] - }; - await printer.sendDocument(doc); + await printer.sendDocument(WebReceipt.ReadyToPrintDocuments.printConfig); }); document.getElementById(`drawerkick_${idx}`)! .addEventListener('click', async (e) => { e.preventDefault(); const printerIdx = (e.currentTarget as HTMLAnchorElement).dataset.printerIdx as unknown as number; const printer = this.printers[printerIdx]; - const doc = { - commands: [new WebReceipt.PulseCommand()] - }; - await printer.sendDocument(doc); + await printer.sendDocument(WebReceipt.ReadyToPrintDocuments.drawerKick); }); } diff --git a/package.json b/package.json index e08e2a7..4d12c2c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "web-receiptline-printer", - "version": "0.3.0-rc1", + "version": "0.3.0-rc2", "description": "A small library for printing ReceiptLine documents to various receipt printers from a browser.", "type": "module", "repository": { diff --git a/src/Commands/BasicCommands.ts b/src/Commands/BasicCommands.ts index 6056e3f..b698063 100644 --- a/src/Commands/BasicCommands.ts +++ b/src/Commands/BasicCommands.ts @@ -41,6 +41,12 @@ export class QueryConfiguration extends BasicCommand { constructor() { super(['waitsForResponse']); } } +export class PrintConfiguration extends BasicCommand { + name = 'Print the printer configuration' + type: CommandTypeBasic = 'PrintConfiguration'; + constructor() { super(['feedsPaper']); } +} + export class GetStatus extends BasicCommand { name = 'Get the printer status' type: CommandTypeBasic = 'GetStatus'; diff --git a/src/Documents/DocumentTranspiler.test.ts b/src/Documents/DocumentTranspiler.test.ts new file mode 100644 index 0000000..efd289a --- /dev/null +++ b/src/Documents/DocumentTranspiler.test.ts @@ -0,0 +1,309 @@ +import { expect, describe, it } from 'vitest'; +import {ReadyToPrintDocuments} from '../ReadyToPrintDocuments.js'; +import { getNewTranspileState, PrinterConfig, type TranspiledDocumentState } from '../Commands/index.js'; +import { transpileDocument } from './DocumentTranspiler.js'; +import { EscPos } from '../Languages/index.js'; + +function getFakeState(): TranspiledDocumentState { + return getNewTranspileState(new PrinterConfig()); +} + +describe('DocumentTranspiler', () => { + describe('escpos', () => { + const escpos = new EscPos.EscPos(); + + describe('ReadyToPrint Docs', () => { + it('configDocument', () => { + const state = getFakeState(); + expect(transpileDocument(ReadyToPrintDocuments.getConfig, escpos, state)) + .toMatchInlineSnapshot(` + CompiledDocument { + "effects": Set { + "waitsForResponse", + }, + "language": 1, + "transactions": [ + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterId { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer ID", + "subcommand": "TypeID", + "type": "CustomCommand", + "typeExtended": Symbol(CmdTransmitPrinterId), + }, + ], + "commands": Uint8Array [ + 29, + 73, + 2, + ], + }, + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterId { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer ID", + "subcommand": "InfoBMakerName", + "type": "CustomCommand", + "typeExtended": Symbol(CmdTransmitPrinterId), + }, + ], + "commands": Uint8Array [ + 29, + 73, + 66, + ], + }, + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterId { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer ID", + "subcommand": "InfoBModelName", + "type": "CustomCommand", + "typeExtended": Symbol(CmdTransmitPrinterId), + }, + ], + "commands": Uint8Array [ + 29, + 73, + 67, + ], + }, + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterId { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer ID", + "subcommand": "InfoBSerialNo", + "type": "CustomCommand", + "typeExtended": Symbol(CmdTransmitPrinterId), + }, + ], + "commands": Uint8Array [ + 29, + 73, + 68, + ], + }, + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterId { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer ID", + "subcommand": "InfoBFirmwareVersion", + "type": "CustomCommand", + "typeExtended": Symbol(CmdTransmitPrinterId), + }, + ], + "commands": Uint8Array [ + 29, + 73, + 65, + ], + }, + ], + } + `); + }); + + it('printerStatus', () => { + const state = getFakeState(); + expect(transpileDocument(ReadyToPrintDocuments.getStatus, escpos, state)) + .toMatchInlineSnapshot(` + CompiledDocument { + "effects": Set { + "waitsForResponse", + }, + "language": 1, + "transactions": [ + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterStatus { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer Status", + "subcommand": "PaperSensorStatus", + "type": "CustomCommand", + "typeExtended": Symbol(TransmitPrinterStatus), + }, + ], + "commands": Uint8Array [ + 29, + 114, + 1, + ], + }, + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterStatus { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer Status", + "subcommand": "DrawerKickStatus", + "type": "CustomCommand", + "typeExtended": Symbol(TransmitPrinterStatus), + }, + ], + "commands": Uint8Array [ + 29, + 114, + 2, + ], + }, + ], + } + `); + }); + + it('printerStatus', () => { + const state = getFakeState(); + expect(transpileDocument(ReadyToPrintDocuments.feedMedia, escpos, state)) + .toMatchInlineSnapshot(` + CompiledDocument { + "effects": Set { + "feedsPaper", + "waitsForResponse", + }, + "language": 1, + "transactions": [ + Transaction { + "awaitedCommands": [], + "commands": Uint8Array [ + 10, + 10, + 10, + 10, + ], + }, + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterStatus { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer Status", + "subcommand": "PaperSensorStatus", + "type": "CustomCommand", + "typeExtended": Symbol(TransmitPrinterStatus), + }, + ], + "commands": Uint8Array [ + 29, + 114, + 1, + ], + }, + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterStatus { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer Status", + "subcommand": "DrawerKickStatus", + "type": "CustomCommand", + "typeExtended": Symbol(TransmitPrinterStatus), + }, + ], + "commands": Uint8Array [ + 29, + 114, + 2, + ], + }, + ], + } + `); + }); + + it('printerStatus', () => { + const state = getFakeState(); + expect(transpileDocument(ReadyToPrintDocuments.printConfig, escpos, state)) + .toMatchInlineSnapshot(` + CompiledDocument { + "effects": Set { + "feedsPaper", + "waitsForResponse", + }, + "language": 1, + "transactions": [ + Transaction { + "awaitedCommands": [], + "commands": Uint8Array [ + 29, + 40, + 65, + 2, + 0, + 1, + 2, + ], + }, + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterStatus { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer Status", + "subcommand": "PaperSensorStatus", + "type": "CustomCommand", + "typeExtended": Symbol(TransmitPrinterStatus), + }, + ], + "commands": Uint8Array [ + 29, + 114, + 1, + ], + }, + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterStatus { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer Status", + "subcommand": "DrawerKickStatus", + "type": "CustomCommand", + "typeExtended": Symbol(TransmitPrinterStatus), + }, + ], + "commands": Uint8Array [ + 29, + 114, + 2, + ], + }, + ], + } + `); + }); + }); + }); +}); diff --git a/src/Documents/DocumentTranspiler.ts b/src/Documents/DocumentTranspiler.ts index fca162f..4bae184 100644 --- a/src/Documents/DocumentTranspiler.ts +++ b/src/Documents/DocumentTranspiler.ts @@ -23,10 +23,6 @@ export function transpileDocument( const forms = splitTransactionsAndForms(doc.commands, commandSet, commandReorderBehavior); - // Wait why do we throw away the form data here? - // ZPL has some advanced form processing concepts that aren't implemented in - // this library yet, and that code was annoying to figure out. It hangs out - // here until the advanced form processing can be implemented later. // TODO: Handle separate forms instead of mooshing them together. const { transactions, effects } = combineForms(forms); @@ -140,10 +136,8 @@ function splitTransactionsAndForms( } // Record the command in the transpile buffer. - if (command.type !== "NoOp") { - currentTrans.commands.push(command); - command.effectFlags.forEach(f => currentForm.effects.add(f)); - } + currentTrans.commands.push(command); + command.effectFlags.forEach(f => currentForm.effects.add(f)); if (command.effectFlags.has("waitsForResponse")) { // This command expects the printer to provide feedback. @@ -221,6 +215,19 @@ function splitTransactionsAndForms( } } + const lastTransaction = forms.at(-1)?.transactions.at(-1); + if (lastTransaction === undefined || lastTransaction.waitCommands.length === 0) { + // Best practice is to always have the printer return some information when + // it's finished printing. If the last transaction doesn't have anything to + // wait for add our own. + const awaitForms = splitTransactionsAndForms( + [new Cmds.GetStatus()], + commandSet, + reorderBehavior, + ); + awaitForms.forEach(f => forms.push(f)); + } + return forms; } diff --git a/src/Languages/EscPos/CmdSetAutoStatusBack.ts b/src/Languages/EscPos/CmdSetAutoStatusBack.ts index d365054..ccc2d60 100644 --- a/src/Languages/EscPos/CmdSetAutoStatusBack.ts +++ b/src/Languages/EscPos/CmdSetAutoStatusBack.ts @@ -32,7 +32,8 @@ export class CmdSetAutoStatusBack implements Cmds.IPrinterExtendedCommand { export const mappingCmdSetAutoStatusBack: Cmds.IPrinterCommandMapping