EpsonTest predates the adapter layer: PrintCommandsToEpsonPrinterAsync takes an EpsonPrinter and drives it directly, so PrinterWrapperFactory, the model adapters and the 0x20 dispatch were never exercised. Against a POS895 that path feeds with ESC d, which the firmware ignores, so every cut slices through the last printed line, and it never sends a logo at all - the only logo branch is the HTML one, which uses GS ( L, the command that prints nothing on this hardware. PrintCommandsViaAdapterAsync instead does what EpsonPrintService does for a real job: query GS I 1, let PrinterWrapperFactory choose the adapter from the reported byte, then print the logo and commands through it and cut. It logs the reported id and the chosen adapter, so a wrong or missing model byte is visible rather than silent. Menu gains a third target and the non-interactive form accepts "pos895", so `dotnet run -- final pos895` prints a Final receipt with its logo through the production path. That covers the one part of the POS895 work nothing had executed end to end: the ESC * bit-image logo. Note EpsonTest must be run from its own directory; the logo paths are relative and `dotnet run --project` from the repo root cannot resolve them. That predates this change. Claude-Session: https://claude.ai/code/session_01E6N3P4nSZLtp4BC2LXVref
364 lines
13 KiB
C#
364 lines
13 KiB
C#
using EpsonTest;
|
|
using Inspectron.Epson.PrintServer.Printers.Utils.BarReceipt;
|
|
using Inspectron.Epson.PrintServer.Printers.Utils.FinalReceipt;
|
|
using Inspectron.Epson.PrintServer.Printers.Utils.InvoiceReceipt;
|
|
using Inspectron.Epson.PrintServer.Printers.Utils.KitchenReceipt;
|
|
using Inspectron.Epson.PrintServer.Printers.Utils.MoneyReturnReceipt;
|
|
using Inspectron.Epson.PrintServer.Printers.Utils.OrderItems;
|
|
using System.Text.Json;
|
|
|
|
// Non-interactive mode: `dotnet run -- <receiptType> [html|epson]`
|
|
// receiptType: kitchen | final | invoice | bar | orderitems | moneyreturn (or 0-5)
|
|
if (args.Length > 0)
|
|
{
|
|
int rt = ParseReceiptTypeArg(args[0]);
|
|
if (rt < 0)
|
|
{
|
|
Console.WriteLine($"Unknown receipt type: {args[0]}");
|
|
return;
|
|
}
|
|
|
|
int pt = ParsePrinterTargetArg(args.Length > 1 ? args[1] : "html");
|
|
Console.WriteLine($"Running {GetReceiptTypeName(rt)} on {GetPrinterTargetName(pt)} printer...\n");
|
|
await RunTest(rt, pt);
|
|
Console.WriteLine("\nDone.");
|
|
return;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
Console.Clear();
|
|
Console.WriteLine("=== Epson Print Test Runner ===\n");
|
|
|
|
var receiptType = ShowMenu("Select receipt type:", new[]
|
|
{
|
|
"KitchenReceipt",
|
|
"FinalReceipt",
|
|
"InvoiceReceipt",
|
|
"BarReceipt",
|
|
"OrderItems",
|
|
"MoneyReturnReceipt",
|
|
"Exit"
|
|
});
|
|
|
|
if (receiptType == 6) break;
|
|
|
|
var printerTarget = ShowMenu("Select printer target:", new[]
|
|
{
|
|
"HTML Printer (local file)",
|
|
"Epson Printer (real hardware)",
|
|
"POS895 (real hardware, via IPrinter adapter)"
|
|
});
|
|
|
|
Console.Clear();
|
|
Console.WriteLine($"Running {GetReceiptTypeName(receiptType)} on {GetPrinterTargetName(printerTarget)} printer...\n");
|
|
|
|
try
|
|
{
|
|
await RunTest(receiptType, printerTarget);
|
|
Console.WriteLine("\nDone! Press any key to return to menu...");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"\nError: {ex.Message}");
|
|
Console.WriteLine("Press any key to return to menu...");
|
|
}
|
|
|
|
Console.ReadKey(true);
|
|
}
|
|
|
|
static int ShowMenu(string prompt, string[] options)
|
|
{
|
|
int selected = 0;
|
|
|
|
while (true)
|
|
{
|
|
Console.Clear();
|
|
Console.WriteLine("=== Epson Print Test Runner ===\n");
|
|
Console.WriteLine(prompt);
|
|
Console.WriteLine();
|
|
|
|
for (int i = 0; i < options.Length; i++)
|
|
{
|
|
if (i == selected)
|
|
{
|
|
Console.BackgroundColor = ConsoleColor.White;
|
|
Console.ForegroundColor = ConsoleColor.Black;
|
|
Console.WriteLine($" > {options[i]}");
|
|
Console.ResetColor();
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine($" {options[i]}");
|
|
}
|
|
}
|
|
|
|
Console.WriteLine("\n[Up/Down] Navigate [Enter] Select");
|
|
|
|
var key = Console.ReadKey(true).Key;
|
|
switch (key)
|
|
{
|
|
case ConsoleKey.UpArrow:
|
|
selected = (selected - 1 + options.Length) % options.Length;
|
|
break;
|
|
case ConsoleKey.DownArrow:
|
|
selected = (selected + 1) % options.Length;
|
|
break;
|
|
case ConsoleKey.Enter:
|
|
return selected;
|
|
}
|
|
}
|
|
}
|
|
|
|
static string GetPrinterTargetName(int index) => index switch
|
|
{
|
|
0 => "HTML",
|
|
1 => "Epson",
|
|
2 => "POS895",
|
|
_ => "Unknown"
|
|
};
|
|
|
|
static int ParsePrinterTargetArg(string arg) => arg.ToLowerInvariant() switch
|
|
{
|
|
"1" or "epson" => 1,
|
|
"2" or "pos895" or "pos" => 2,
|
|
_ => 0
|
|
};
|
|
|
|
static string GetReceiptTypeName(int index) => index switch
|
|
{
|
|
0 => "KitchenReceipt",
|
|
1 => "FinalReceipt",
|
|
2 => "InvoiceReceipt",
|
|
3 => "BarReceipt",
|
|
4 => "OrderItems",
|
|
5 => "MoneyReturnReceipt",
|
|
_ => "Unknown"
|
|
};
|
|
|
|
static int ParseReceiptTypeArg(string arg) => arg.ToLowerInvariant() switch
|
|
{
|
|
"0" or "kitchen" or "kitchenreceipt" => 0,
|
|
"1" or "final" or "finalreceipt" => 1,
|
|
"2" or "invoice" or "invoicereceipt" => 2,
|
|
"3" or "bar" or "barreceipt" => 3,
|
|
"4" or "orderitems" => 4,
|
|
"5" or "moneyreturn" or "moneyreturnreceipt" or "refund" or "creditnote" => 5,
|
|
_ => -1
|
|
};
|
|
|
|
static async Task RunTest(int receiptType, int printerTarget)
|
|
{
|
|
switch (receiptType)
|
|
{
|
|
case 0: await RunKitchenReceiptTest(printerTarget); break;
|
|
case 1: await RunFinalReceiptTest(printerTarget); break;
|
|
case 2: await RunInvoiceReceiptTest(printerTarget); break;
|
|
case 3: await RunBarReceiptTest(printerTarget); break;
|
|
case 4: await RunOrderItemsTest(printerTarget); break;
|
|
case 5: await RunMoneyReturnReceiptTest(printerTarget); break;
|
|
}
|
|
}
|
|
|
|
static async Task RunKitchenReceiptTest(int printerTarget)
|
|
{
|
|
var receipt = TestHelpers.BuildSampleKitchenReceipt();
|
|
var serializedReceipt = JsonSerializer.Serialize(receipt, new JsonSerializerOptions { WriteIndented = true });
|
|
var deserializedReceipt = JsonSerializer.Deserialize<KitchenReceipt>(serializedReceipt);
|
|
|
|
var converter = new KitchenReceiptConverterKlinglers(bigLineWidth: 20, lineWidth: 33);
|
|
var printCommands = converter.ConvertToPrintCommands(deserializedReceipt);
|
|
|
|
TestHelpers.PrintCommandsToConsole(printCommands);
|
|
|
|
if (printerTarget == 0)
|
|
{
|
|
var printer = await TestHelpers.CreateHtmlPrinterAsync(
|
|
path: @".\kitchen_test.html",
|
|
paperWidth: TestHelpers.KitchenPaperWidth);
|
|
await TestHelpers.PrintKitchenCommandsToHtmlPrinterAsync(printer, printCommands);
|
|
Console.WriteLine("Output written to: kitchen_test.html");
|
|
}
|
|
else if (printerTarget == 1)
|
|
{
|
|
var printer = await TestHelpers.CreateEpsonPrinterAsync(paperWidth: TestHelpers.KitchenPaperWidth);
|
|
await TestHelpers.PrintCommandsToEpsonPrinterAsync(printer, printCommands, useDelay: true);
|
|
}
|
|
else
|
|
{
|
|
// Routes through PrinterWrapperFactory and the model adapter, the way
|
|
// EpsonPrintService does for a real job. The Epson branch above drives
|
|
// EpsonPrinter directly and feeds with ESC d, which the POS895 ignores.
|
|
var printer = await TestHelpers.CreateEpsonPrinterAsync();
|
|
await TestHelpers.PrintCommandsViaAdapterAsync(printer, printCommands);
|
|
}
|
|
}
|
|
|
|
static async Task RunFinalReceiptTest(int printerTarget)
|
|
{
|
|
var receipt = TestHelpers.BuildSampleFinalReceipt();
|
|
var serializedReceipt = JsonSerializer.Serialize(receipt, new JsonSerializerOptions { WriteIndented = true });
|
|
|
|
var converter = new FinalReceiptConverter(lineWidth: 48, bigFontLineWidth: 24);
|
|
var printCommands = converter.Convert(serializedReceipt);
|
|
|
|
TestHelpers.PrintCommandsToConsole(printCommands);
|
|
|
|
if (printerTarget == 0)
|
|
{
|
|
var printer = await TestHelpers.CreateHtmlPrinterAsync();
|
|
await TestHelpers.PrintCommandsToHtmlPrinterAsync(
|
|
printer,
|
|
printCommands,
|
|
logoPath: "ristorante-klinglers.ch-logo_white_bg.png");
|
|
Console.WriteLine("Output written to: test.html");
|
|
}
|
|
else if (printerTarget == 1)
|
|
{
|
|
var printer = await TestHelpers.CreateEpsonPrinterAsync();
|
|
await TestHelpers.PrintCommandsToEpsonPrinterAsync(printer, printCommands);
|
|
}
|
|
else
|
|
{
|
|
// Routes through PrinterWrapperFactory and the model adapter, the way
|
|
// EpsonPrintService does for a real job. The Epson branch above drives
|
|
// EpsonPrinter directly and feeds with ESC d, which the POS895 ignores.
|
|
var printer = await TestHelpers.CreateEpsonPrinterAsync();
|
|
await TestHelpers.PrintCommandsViaAdapterAsync(printer, printCommands,
|
|
logoPath: "ristorante-klinglers.ch-logo_white_bg.png");
|
|
}
|
|
}
|
|
|
|
static async Task RunInvoiceReceiptTest(int printerTarget)
|
|
{
|
|
var receipt = TestHelpers.BuildSampleInvoiceReceipt();
|
|
var serializedReceipt = JsonSerializer.Serialize(receipt, new JsonSerializerOptions { WriteIndented = true });
|
|
|
|
var converter = new InvoiceReceiptConverter(lineWidth: 48, bigFontLineWidth: 24);
|
|
var printCommands = converter.Convert(serializedReceipt);
|
|
|
|
TestHelpers.PrintCommandsToConsole(printCommands);
|
|
|
|
if (printerTarget == 0)
|
|
{
|
|
var printer = await TestHelpers.CreateHtmlPrinterAsync(path: @".\invoice_test.html");
|
|
await TestHelpers.PrintCommandsToHtmlPrinterAsync(
|
|
printer,
|
|
printCommands,
|
|
logoPath: "ristorante-klinglers.ch-logo_white_bg.png");
|
|
Console.WriteLine("Output written to: invoice_test.html");
|
|
}
|
|
else if (printerTarget == 1)
|
|
{
|
|
var printer = await TestHelpers.CreateEpsonPrinterAsync();
|
|
await TestHelpers.PrintCommandsToEpsonPrinterAsync(printer, printCommands);
|
|
}
|
|
else
|
|
{
|
|
// Routes through PrinterWrapperFactory and the model adapter, the way
|
|
// EpsonPrintService does for a real job. The Epson branch above drives
|
|
// EpsonPrinter directly and feeds with ESC d, which the POS895 ignores.
|
|
var printer = await TestHelpers.CreateEpsonPrinterAsync();
|
|
await TestHelpers.PrintCommandsViaAdapterAsync(printer, printCommands,
|
|
logoPath: "ristorante-klinglers.ch-logo_white_bg.png");
|
|
}
|
|
}
|
|
|
|
static async Task RunBarReceiptTest(int printerTarget)
|
|
{
|
|
var receipt = TestHelpers.BuildSampleBarReceipt();
|
|
|
|
var converter = new BarReceiptConverter(lineWidth: 42, bigLineWidth: 22);
|
|
var printCommands = converter.ConvertToPrintCommands(receipt);
|
|
|
|
TestHelpers.PrintCommandsToConsole(printCommands);
|
|
|
|
if (printerTarget == 0)
|
|
{
|
|
var printer = await TestHelpers.CreateHtmlPrinterAsync(
|
|
path: @".\bar_test.html",
|
|
paperWidth: TestHelpers.KitchenPaperWidth);
|
|
await TestHelpers.PrintKitchenCommandsToHtmlPrinterAsync(printer, printCommands);
|
|
Console.WriteLine("Output written to: bar_test.html");
|
|
}
|
|
else if (printerTarget == 1)
|
|
{
|
|
var printer = await TestHelpers.CreateEpsonPrinterAsync(paperWidth: TestHelpers.KitchenPaperWidth);
|
|
await TestHelpers.PrintCommandsToEpsonPrinterAsync(printer, printCommands, useDelay: true);
|
|
}
|
|
else
|
|
{
|
|
// Routes through PrinterWrapperFactory and the model adapter, the way
|
|
// EpsonPrintService does for a real job. The Epson branch above drives
|
|
// EpsonPrinter directly and feeds with ESC d, which the POS895 ignores.
|
|
var printer = await TestHelpers.CreateEpsonPrinterAsync();
|
|
await TestHelpers.PrintCommandsViaAdapterAsync(printer, printCommands);
|
|
}
|
|
}
|
|
|
|
static async Task RunOrderItemsTest(int printerTarget)
|
|
{
|
|
var receipt = TestHelpers.BuildSampleOrderItemsReceipt();
|
|
var serializedReceipt = JsonSerializer.Serialize(receipt, new JsonSerializerOptions { WriteIndented = true });
|
|
|
|
var converter = new OrderItemsReceiptConverter(lineWidth: 50, bigFontLineWidth: 21);
|
|
var printCommands = converter.Convert(serializedReceipt);
|
|
|
|
TestHelpers.PrintCommandsToConsole(printCommands);
|
|
|
|
if (printerTarget == 0)
|
|
{
|
|
var printer = await TestHelpers.CreateHtmlPrinterAsync(path: @".\order_items.html");
|
|
await TestHelpers.PrintCommandsToHtmlPrinterAsync(printer, printCommands);
|
|
Console.WriteLine("Output written to: order_items.html");
|
|
}
|
|
else if (printerTarget == 1)
|
|
{
|
|
var printer = await TestHelpers.CreateEpsonPrinterAsync();
|
|
await TestHelpers.PrintCommandsToEpsonPrinterAsync(printer, printCommands);
|
|
}
|
|
else
|
|
{
|
|
// Routes through PrinterWrapperFactory and the model adapter, the way
|
|
// EpsonPrintService does for a real job. The Epson branch above drives
|
|
// EpsonPrinter directly and feeds with ESC d, which the POS895 ignores.
|
|
var printer = await TestHelpers.CreateEpsonPrinterAsync();
|
|
await TestHelpers.PrintCommandsViaAdapterAsync(printer, printCommands);
|
|
}
|
|
}
|
|
|
|
static async Task RunMoneyReturnReceiptTest(int printerTarget)
|
|
{
|
|
var receipt = TestHelpers.BuildSampleMoneyReturnReceipt();
|
|
var serializedReceipt = JsonSerializer.Serialize(receipt, new JsonSerializerOptions { WriteIndented = true });
|
|
|
|
var converter = new MoneyReturnReceiptConverter(lineWidth: 48, bigFontLineWidth: 24);
|
|
var printCommands = converter.Convert(serializedReceipt);
|
|
|
|
TestHelpers.PrintCommandsToConsole(printCommands);
|
|
|
|
if (printerTarget == 0)
|
|
{
|
|
var printer = await TestHelpers.CreateHtmlPrinterAsync(path: @".\money_return_test.html");
|
|
await TestHelpers.PrintCommandsToHtmlPrinterAsync(
|
|
printer,
|
|
printCommands,
|
|
logoPath: "ristorante-klinglers.ch-logo_white_bg.png");
|
|
Console.WriteLine("Output written to: money_return_test.html");
|
|
}
|
|
else if (printerTarget == 1)
|
|
{
|
|
var printer = await TestHelpers.CreateEpsonPrinterAsync();
|
|
await TestHelpers.PrintCommandsToEpsonPrinterAsync(printer, printCommands);
|
|
}
|
|
else
|
|
{
|
|
// Routes through PrinterWrapperFactory and the model adapter, the way
|
|
// EpsonPrintService does for a real job. The Epson branch above drives
|
|
// EpsonPrinter directly and feeds with ESC d, which the POS895 ignores.
|
|
var printer = await TestHelpers.CreateEpsonPrinterAsync();
|
|
await TestHelpers.PrintCommandsViaAdapterAsync(printer, printCommands,
|
|
logoPath: "ristorante-klinglers.ch-logo_white_bg.png");
|
|
}
|
|
}
|