print server with new concept
This commit is contained in:
@@ -42,7 +42,7 @@ public class PrintServerHostedService : IHostedService
|
||||
_kernel.Bind<IPrintService>().To<Inspectron.Epson.PrintServer.PrintServices.EpsonPrintService>();
|
||||
_kernel.Bind<Microsoft.Extensions.Logging.ILogger>().ToConstant(_logger);
|
||||
_kernel.Bind<IAssignedPrinterRepository>().ToConstant(config);
|
||||
_kernel.Bind<IPrinterFactory>().To<PrinterFactory>().InSingletonScope();
|
||||
_kernel.Bind<IWrapperPrinterFactory>().To<PrinterWrapperFactory>().InSingletonScope();
|
||||
_kernel.Bind<IPrinterConfigurationSource>().ToConstant(config);
|
||||
_kernel.Bind<PrintServer>().ToSelf().InSingletonScope();
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Inspectron.Epson;
|
||||
using Inspectron.Epson.PrintServer.ConfigurationSources;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace EpsonPrintService;
|
||||
@@ -20,15 +21,13 @@ public class PrinterDiscoveryBackgroundTask
|
||||
public PrinterDiscoveryBackgroundTask(
|
||||
IDiscoveryService discoveryService,
|
||||
IDiscoveredPrintersReceiver receiver,
|
||||
ILogger logger,
|
||||
TimeSpan? interval = null,
|
||||
TimeSpan? discoveryTimeout = null)
|
||||
ILogger logger)
|
||||
{
|
||||
_discoveryService = discoveryService;
|
||||
_receiver = receiver;
|
||||
_logger = logger;
|
||||
_interval = interval ?? TimeSpan.FromSeconds(30);
|
||||
_discoveryTimeout = discoveryTimeout ?? TimeSpan.FromSeconds(3);
|
||||
_interval = TimeSpan.FromSeconds(30);
|
||||
_discoveryTimeout = TimeSpan.FromSeconds(3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -68,6 +67,11 @@ public class PrinterDiscoveryBackgroundTask
|
||||
try
|
||||
{
|
||||
var printers = await _discoveryService.DiscoverPrintersAsync(_discoveryTimeout);
|
||||
printers.Add(new DiscoveredPrinter()
|
||||
{
|
||||
IPAddress = "127.0.0.1",
|
||||
ModelName = "TM-T30III",
|
||||
});
|
||||
var currentIps = printers.Select(p => p.IPAddress).ToHashSet();
|
||||
|
||||
if (HasConfigurationChanged(currentIps))
|
||||
|
||||
@@ -23,6 +23,9 @@ kernel.Bind<IDiscoveryService>().To<DiscoveryService>().InSingletonScope();
|
||||
|
||||
|
||||
kernel.Bind<EpsonPrintServiceConfiguration>().ToConstant(config);
|
||||
//kernel.Bind<IPrinterFactory>().ToMethod(sp=>new HtmlPrinterFactory("TM-U220II.html", paperWidth: 258,type:0x0d, logger:sp.Kernel.Get<ILogger>()));
|
||||
//kernel.Bind<IPrinterFactory>().ToMethod(sp=>new HtmlPrinterFactory("TM-T30III.html",logger:sp.Kernel.Get<ILogger>()));
|
||||
kernel.Bind<IPrinterFactory>().To<EpsonPrinterFactory>();
|
||||
|
||||
kernel.Bind<IPrintJobSource, SignalRPrintJobSource>().To<SignalRPrintJobSource>().InSingletonScope();
|
||||
kernel.Bind<IPrintService>().To<Inspectron.Epson.PrintServer.PrintServices.EpsonPrintService>();
|
||||
@@ -35,10 +38,10 @@ kernel.Bind<ILogger>().ToMethod(ctx =>
|
||||
return loggerFactory.CreateLogger("EpsonPrintService");
|
||||
});
|
||||
kernel.Bind<IAssignedPrinterRepository>().ToConstant(config);
|
||||
kernel.Bind<IPrinterFactory>().To<PrinterFactory>().InSingletonScope();
|
||||
kernel.Bind<IWrapperPrinterFactory>().To<PrinterWrapperFactory>().InSingletonScope();
|
||||
kernel.Bind<IPrinterConfigurationSource>().To<FixedConfigurationSource>();
|
||||
kernel.Bind<PrintServer>().ToSelf().InSingletonScope();
|
||||
kernel.Bind<IDiscoveredPrintersReceiver>().To<ConsoleDiscoveredPrintersReceiver>().InSingletonScope();
|
||||
kernel.Bind<IDiscoveredPrintersReceiver>().To<JamesDiscoveredPrintersReceiver>().InSingletonScope();
|
||||
kernel.Bind<PrinterDiscoveryBackgroundTask>().ToSelf().InSingletonScope();
|
||||
|
||||
var printLoop = kernel.Get<PrintLoop>();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"GroupId": "67e534f8e86e816689323023",
|
||||
"RestaurantId": "63e37295bd6f26dbd36164c0",
|
||||
"GroupId": "64a3d9f0876bc5d1704bce43",
|
||||
"RestaurantId": "64a3d9f0876bc5d1704bce43",
|
||||
"ApiKey": "LBOACMlvFEgDbZqt24uW0LjiFv0LKU5DT94g0JDoBnI=",
|
||||
"PrinterConfigurations": {}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ using SixLabors.ImageSharp.PixelFormats;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Xml.Linq;
|
||||
using Inspectron.Epson.PrintServer.Printers.Utils.OrderItems;
|
||||
using FinalReceipt = Inspectron.Epson.PrintServer.Printers.Utils.FinalRecipe.FinalReceipt;
|
||||
|
||||
|
||||
@@ -58,159 +59,178 @@ using FinalReceipt = Inspectron.Epson.PrintServer.Printers.Utils.FinalRecipe.Fin
|
||||
//Console.ReadLine();
|
||||
|
||||
|
||||
var receipt = new FinalReceipt
|
||||
{
|
||||
CompanyName = "Klingler Gastro AG",
|
||||
Address1 = "Münzplatz 3",
|
||||
Address2 = "CH-8001 Zürich",
|
||||
Phone = "043 321 22 22",
|
||||
ReceiptNumber = "1-81-202",
|
||||
DateTime = new DateTime(2025, 12, 16, 14, 58, 0),
|
||||
Guests = 2,
|
||||
Total = 160.00m,
|
||||
Currency = "CHF",
|
||||
TotalInAlternateCurrency = 172.80m,
|
||||
AlternateCurrency = "EUR",
|
||||
PaymentMethod = "MASTER",
|
||||
PaymentAmount = 160.00m,
|
||||
WaiterName = "Yves",
|
||||
Terminal = "Hauptkasse ZH",
|
||||
TableNumber = "12",
|
||||
VatNumber = "CHE-449.635.880 MWST",
|
||||
ThankYouMessage = "Das Team bedankt sich herzlich für Ihren",
|
||||
GoodbyeMessageLine1 = "Besuch.",
|
||||
GoodbyeMessageLine2 = "Auf Wiedersehen."
|
||||
};
|
||||
//var receipt = new FinalReceipt
|
||||
//{
|
||||
// CompanyName = "Klingler Gastro AG",
|
||||
// Address1 = "Münzplatz 3",
|
||||
// Address2 = "CH-8001 Zürich",
|
||||
// Phone = "043 321 22 22",
|
||||
// ReceiptNumber = "1-81-202",
|
||||
// DateTime = new DateTime(2025, 12, 16, 14, 58, 0),
|
||||
// Guests = 2,
|
||||
// Total = 160.00m,
|
||||
// Currency = "CHF",
|
||||
// TotalInAlternateCurrency = 172.80m,
|
||||
// AlternateCurrency = "EUR",
|
||||
// PaymentMethod = "MASTER",
|
||||
// PaymentAmount = 160.00m,
|
||||
// WaiterName = "Yves",
|
||||
// Terminal = "Hauptkasse ZH",
|
||||
// TableNumber = "12",
|
||||
// VatNumber = "CHE-449.635.880 MWST",
|
||||
// ThankYouMessage = "Das Team bedankt sich herzlich für Ihren",
|
||||
// GoodbyeMessageLine1 = "Besuch.",
|
||||
// GoodbyeMessageLine2 = "Auf Wiedersehen.",
|
||||
// TerminalReceipt = new PaymentTerminalReceipt
|
||||
// {
|
||||
// ReceiptType = "*** Kundenbeleg ***",
|
||||
// BookingType = "Buchung",
|
||||
// PaymentSystem = "TWINT",
|
||||
// TransactionNumber = "XXXXXXXXXXXXXXX1494",
|
||||
// TransactionDateTime = new DateTime(2025, 11, 11, 12, 34, 49),
|
||||
// TerminalId = "31108834",
|
||||
// AID = "A0000015749E",
|
||||
// TransactionSeqCount = "6127",
|
||||
// TransactionRefNo = "99036644599",
|
||||
// AuthCode = "0d3396",
|
||||
// AcquirerId = "2",
|
||||
// EftAmount = 67.00m,
|
||||
// TipAmount = 6.70m,
|
||||
// TotalEftAmount = 73.70m,
|
||||
// Currency = "CHF"
|
||||
// }
|
||||
//};
|
||||
|
||||
// Add items
|
||||
receipt.Items.Add(new ReceiptItem
|
||||
{
|
||||
Quantity = 1,
|
||||
Description = "San Pellegrino 50cl",
|
||||
UnitPrice = 6.50m,
|
||||
TotalPrice = 6.50m,
|
||||
TaxCategory = "A"
|
||||
});
|
||||
//// Add items
|
||||
//receipt.Items.Add(new ReceiptItem
|
||||
//{
|
||||
// Quantity = 1,
|
||||
// Description = "San Pellegrino 50cl",
|
||||
// UnitPrice = 6.50m,
|
||||
// TotalPrice = 6.50m,
|
||||
// TaxCategory = "A"
|
||||
//});
|
||||
|
||||
receipt.Items.Add(new ReceiptItem
|
||||
{
|
||||
Quantity = 1,
|
||||
Description = "Panna 50cl",
|
||||
UnitPrice = 6.50m,
|
||||
TotalPrice = 6.50m,
|
||||
TaxCategory = "A"
|
||||
});
|
||||
//receipt.Items.Add(new ReceiptItem
|
||||
//{
|
||||
// Quantity = 1,
|
||||
// Description = "Panna 50cl",
|
||||
// UnitPrice = 6.50m,
|
||||
// TotalPrice = 6.50m,
|
||||
// TaxCategory = "A"
|
||||
//});
|
||||
|
||||
receipt.Items.Add(new ReceiptItem
|
||||
{
|
||||
Quantity = 1,
|
||||
Description = "Granini Tomatensaft",
|
||||
UnitPrice = 5.50m,
|
||||
TotalPrice = 5.50m,
|
||||
TaxCategory = "A"
|
||||
});
|
||||
//receipt.Items.Add(new ReceiptItem
|
||||
//{
|
||||
// Quantity = 1,
|
||||
// Description = "Granini Tomatensaft",
|
||||
// UnitPrice = 5.50m,
|
||||
// TotalPrice = 5.50m,
|
||||
// TaxCategory = "A"
|
||||
//});
|
||||
|
||||
receipt.Items.Add(new ReceiptItem
|
||||
{
|
||||
Quantity = 2,
|
||||
Description = "Business Lunch Menu Seco",
|
||||
UnitPrice = 44.00m,
|
||||
TotalPrice = 88.00m,
|
||||
TaxCategory = "A"
|
||||
});
|
||||
//receipt.Items.Add(new ReceiptItem
|
||||
//{
|
||||
// Quantity = 2,
|
||||
// Description = "Business Lunch Menu Seco",
|
||||
// UnitPrice = 44.00m,
|
||||
// TotalPrice = 88.00m,
|
||||
// TaxCategory = "A"
|
||||
//});
|
||||
|
||||
receipt.Items.Add(new ReceiptItem
|
||||
{
|
||||
Quantity = 2,
|
||||
Description = "Brunello di Montalcino 1",
|
||||
UnitPrice = 16.00m,
|
||||
TotalPrice = 32.00m,
|
||||
TaxCategory = "A"
|
||||
});
|
||||
//receipt.Items.Add(new ReceiptItem
|
||||
//{
|
||||
// Quantity = 2,
|
||||
// Description = "Brunello di Montalcino 1",
|
||||
// UnitPrice = 16.00m,
|
||||
// TotalPrice = 32.00m,
|
||||
// TaxCategory = "A"
|
||||
//});
|
||||
|
||||
receipt.Items.Add(new ReceiptItem
|
||||
{
|
||||
Quantity = 2,
|
||||
Description = "Espresso",
|
||||
UnitPrice = 5.50m,
|
||||
TotalPrice = 11.00m,
|
||||
TaxCategory = "A"
|
||||
});
|
||||
//receipt.Items.Add(new ReceiptItem
|
||||
//{
|
||||
// Quantity = 2,
|
||||
// Description = "Espresso",
|
||||
// UnitPrice = 5.50m,
|
||||
// TotalPrice = 11.00m,
|
||||
// TaxCategory = "A"
|
||||
//});
|
||||
|
||||
receipt.Items.Add(new ReceiptItem
|
||||
{
|
||||
Quantity = 1,
|
||||
Description = "Tip",
|
||||
UnitPrice = 10.50m,
|
||||
TotalPrice = 10.50m,
|
||||
TaxCategory = "B"
|
||||
});
|
||||
//receipt.Items.Add(new ReceiptItem
|
||||
//{
|
||||
// Quantity = 1,
|
||||
// Description = "Tip",
|
||||
// UnitPrice = 10.50m,
|
||||
// TotalPrice = 10.50m,
|
||||
// TaxCategory = "B"
|
||||
//});
|
||||
|
||||
// Add tax breakdown
|
||||
receipt.TaxBreakdown.Add(new TaxInfo
|
||||
{
|
||||
Category = "A",
|
||||
Rate = 8.1m,
|
||||
Gross = 149.50m,
|
||||
Net = 138.30m,
|
||||
TaxAmount = 11.20m,
|
||||
Currency = "CHF"
|
||||
});
|
||||
//// Add tax breakdown
|
||||
//receipt.TaxBreakdown.Add(new TaxInfo
|
||||
//{
|
||||
// Category = "A",
|
||||
// Rate = 8.1m,
|
||||
// Gross = 149.50m,
|
||||
// Net = 138.30m,
|
||||
// TaxAmount = 11.20m,
|
||||
// Currency = "CHF"
|
||||
//});
|
||||
|
||||
receipt.TaxBreakdown.Add(new TaxInfo
|
||||
{
|
||||
Category = "B",
|
||||
Rate = 0m,
|
||||
Gross = 10.50m,
|
||||
Net = 10.50m,
|
||||
TaxAmount = 0.00m,
|
||||
Currency = "CHF"
|
||||
});
|
||||
//receipt.TaxBreakdown.Add(new TaxInfo
|
||||
//{
|
||||
// Category = "B",
|
||||
// Rate = 0m,
|
||||
// Gross = 10.50m,
|
||||
// Net = 10.50m,
|
||||
// TaxAmount = 0.00m,
|
||||
// Currency = "CHF"
|
||||
//});
|
||||
|
||||
|
||||
// Convert to print commands
|
||||
var converter = new ReceiptConverter(lineWidth: 48, bigFontLineWidth: 24);
|
||||
var printCommands = converter.ConvertToPrintCommands(receipt);
|
||||
//var serializedReceipt = JsonSerializer.Serialize(receipt, new JsonSerializerOptions { WriteIndented = true });
|
||||
//var deserializedReceipt = JsonSerializer.Deserialize<FinalReceipt>(serializedReceipt);
|
||||
//// Convert to print commands
|
||||
//var converter = new ReceiptConverter(lineWidth: 48, bigFontLineWidth: 24);
|
||||
//var printCommands = converter.ConvertToPrintCommands(deserializedReceipt);
|
||||
|
||||
|
||||
|
||||
|
||||
// Print the commands
|
||||
Console.WriteLine("=== PRINT COMMANDS ===\n");
|
||||
var printer = new HtmlPrinter(@".\test.html", paperWidth: 384);
|
||||
await printer.ConnectAsync("");
|
||||
await printer.SetAbsolutePrintPosition(97);
|
||||
await printer.LoadImageAsync("ristorante-klinglers.ch-logo_white_bg.png", 390);
|
||||
await printer.SetAbsolutePrintPosition(0);
|
||||
await printer.PrintLoadedImage();
|
||||
await Task.Delay(200);
|
||||
await printer.FeedLinesAsync(1);
|
||||
await printer.SetCustomLineSpacing(22);
|
||||
foreach (var command in printCommands)
|
||||
{
|
||||
string attributes = "";
|
||||
if (command.IsBig) attributes += "[BIG] ";
|
||||
if (command.IsBold) attributes += "[BOLD] ";
|
||||
|
||||
Console.WriteLine($"{attributes}{command.Text}");
|
||||
|
||||
if (command.IsBig)
|
||||
{
|
||||
await printer.SetFontSizeAsync(2, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
await printer.SetFontSizeAsync(1, 1);
|
||||
}
|
||||
//// Print the commands
|
||||
//Console.WriteLine("=== PRINT COMMANDS ===\n");
|
||||
//var printer = new HtmlPrinter(@".\test.html", paperWidth: 384);
|
||||
//await printer.ConnectAsync("");
|
||||
//await printer.SetAbsolutePrintPosition(42);
|
||||
//await printer.LoadImageAsync("ristorante-klinglers.ch-logo_white_bg.png", 300);
|
||||
//await printer.PrintLoadedImage();
|
||||
//await printer.SetAbsolutePrintPosition(0);
|
||||
//await Task.Delay(200);
|
||||
////await printer.FeedLinesAsync(1);
|
||||
//await printer.SetCustomLineSpacing(22);
|
||||
//foreach (var command in printCommands)
|
||||
//{
|
||||
// string attributes = "";
|
||||
// if (command.IsBig) attributes += "[BIG] ";
|
||||
// if (command.IsBold) attributes += "[BOLD] ";
|
||||
|
||||
await printer.SetEmphasized(command.IsBold);
|
||||
//await Task.Delay(200);
|
||||
// Console.WriteLine($"{attributes}{command.Text}");
|
||||
|
||||
await printer.PrintTextAsync(command.Text + "\n");
|
||||
//await Task.Delay(200);
|
||||
// if (command.IsBig)
|
||||
// {
|
||||
// await printer.SetFontSizeAsync(2, 1);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// await printer.SetFontSizeAsync(1, 1);
|
||||
// }
|
||||
// //await Task.Delay(200);
|
||||
|
||||
}
|
||||
// await printer.SetEmphasized(command.IsBold);
|
||||
// //await Task.Delay(200);
|
||||
|
||||
// await printer.PrintTextAsync(command.Text + "\n");
|
||||
// //await Task.Delay(200);
|
||||
|
||||
//}
|
||||
|
||||
|
||||
//var receipt = new KitchenReceipt
|
||||
@@ -246,10 +266,11 @@ foreach (var command in printCommands)
|
||||
|
||||
|
||||
//// Add additional info
|
||||
//receipt.AdditionalInfo.Add("1 x Amuse Bouche");
|
||||
|
||||
//var serializedReceipt = JsonSerializer.Serialize(receipt, new JsonSerializerOptions { WriteIndented = true });
|
||||
//var deserializedReceipt = JsonSerializer.Deserialize<KitchenReceipt>(serializedReceipt);
|
||||
//KitchenReceiptConverter converter = new KitchenReceiptConverter(bigLineWidth: 20, lineWidth: 33);
|
||||
//var printCommands = converter.ConvertToPrintCommands(receipt);
|
||||
//var printCommands = converter.ConvertToPrintCommands(deserializedReceipt);
|
||||
|
||||
//Console.WriteLine("=== PRINT COMMANDS ===\n");
|
||||
//var printer = new HtmlPrinter(@".\test.html", paperWidth: 258);
|
||||
@@ -291,8 +312,83 @@ foreach (var command in printCommands)
|
||||
//await printer.PrintTextAsync(
|
||||
// "1x San Pellegrino 50cl 6.50 6.50 A\n1x Panna 50cl 6.50 6.50 A");
|
||||
|
||||
// Order Items Receipt Test
|
||||
//var orderItemsReceipt = new OrderItemsReceipt
|
||||
//{
|
||||
// OrderNumber = "1",
|
||||
// QRInfo = "Margherita Pizza Classic longer text",
|
||||
// DateTime = new DateTime(2026, 1, 14, 14, 35, 47),
|
||||
// ClientNotes = "Some main note",
|
||||
// Items = new List<OrderItem>
|
||||
// {
|
||||
// new OrderItem
|
||||
// {
|
||||
// Number = 1,
|
||||
// Name = "pappara",
|
||||
// Quantity = 1,
|
||||
// SubItems = new List<string> { "pizza", "cola" },
|
||||
// Comment = "Cola zero please"
|
||||
// },
|
||||
// new OrderItem
|
||||
// {
|
||||
// Number = 2,
|
||||
// Name = "Classic Beef Burger",
|
||||
// Quantity = 1,
|
||||
// Modifications = new ItemModifications
|
||||
// {
|
||||
// Removed = new List<string> { "Onion" },
|
||||
// Added = new List<string> { "butter" }
|
||||
// },
|
||||
// Comment = "Medium rare please"
|
||||
// },
|
||||
// new OrderItem
|
||||
// {
|
||||
// Number = 3,
|
||||
// Name = "Margherita Pizza Classic",
|
||||
// Quantity = 2,
|
||||
// Size = "L"
|
||||
// }
|
||||
// }
|
||||
//};
|
||||
|
||||
//var serializedOrderItems = JsonSerializer.Serialize(orderItemsReceipt, new JsonSerializerOptions { WriteIndented = true });
|
||||
//Console.WriteLine(serializedOrderItems);
|
||||
//var deserializedOrderItems = JsonSerializer.Deserialize<OrderItemsReceipt>(serializedOrderItems);
|
||||
|
||||
//var orderItemsConverter = new Inspectron.Epson.PrintServer.Printers.Utils.OrderItems.ReceiptConverter(lineWidth: 50, bigFontLineWidth: 21);
|
||||
//var printCommands = orderItemsConverter.ConvertToPrintCommands(deserializedOrderItems);
|
||||
|
||||
//Console.WriteLine("=== PRINT COMMANDS ===\n");
|
||||
//var printer = new HtmlPrinter(@".\order_items.html", paperWidth: 384);
|
||||
//await printer.ConnectAsync("");
|
||||
//await Task.Delay(200);
|
||||
////await printer.FeedLinesAsync(1);
|
||||
//await printer.SetCustomLineSpacing(22);
|
||||
//foreach (var command in printCommands)
|
||||
//{
|
||||
// string attributes = "";
|
||||
// if (command.IsBig) attributes += "[BIG] ";
|
||||
// if (command.IsBold) attributes += "[BOLD] ";
|
||||
|
||||
// Console.WriteLine($"{attributes}{command.Text}");
|
||||
|
||||
// if (command.IsBig)
|
||||
// {
|
||||
// await printer.SetFontSizeAsync(2, 1);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// await printer.SetFontSizeAsync(1, 1);
|
||||
// }
|
||||
// //await Task.Delay(200);
|
||||
|
||||
// await printer.SetEmphasized(command.IsBold);
|
||||
// //await Task.Delay(200);
|
||||
|
||||
// await printer.PrintTextAsync(command.Text + "\n");
|
||||
// //await Task.Delay(200);
|
||||
|
||||
//}
|
||||
|
||||
|
||||
|
||||
@@ -417,7 +513,10 @@ foreach (var command in printCommands)
|
||||
//await Task.Delay(2000);
|
||||
//await printer.PrintBuffer();
|
||||
|
||||
//await printer.SendRawCommandAsync([0x1b,0x21,0x10+0x21]);
|
||||
//var printer = new EpsonPrinter();
|
||||
//await printer.ConnectAsync("127.0.0.1", 8888);
|
||||
//await printer.InitAsync();
|
||||
|
||||
//await printer.PrintTextAsync("Hello!");
|
||||
//await printer.FeedLinesAsync(5);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using SixLabors.ImageSharp.Formats.Png;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using System.Text;
|
||||
using Inspectron.Epson.PrintServer.PrintServices;
|
||||
|
||||
namespace Inspectron.Epson;
|
||||
|
||||
@@ -14,6 +15,7 @@ namespace Inspectron.Epson;
|
||||
public class HtmlPrinter : IEpsonPrinter
|
||||
{
|
||||
private readonly ILogger? _logger;
|
||||
private readonly byte _id;
|
||||
private readonly string _outputPath;
|
||||
private readonly int _paperWidth;
|
||||
private readonly string _imageDirectory;
|
||||
@@ -60,11 +62,12 @@ public class HtmlPrinter : IEpsonPrinter
|
||||
/// <param name="outputPath">Path to the output HTML file</param>
|
||||
/// <param name="paperWidth">Paper width in pixels (default 384 for 80mm thermal printer)</param>
|
||||
/// <param name="logger">Optional logger for diagnostic output</param>
|
||||
public HtmlPrinter(string outputPath, int paperWidth = 384, ILogger? logger = null)
|
||||
public HtmlPrinter(string outputPath, int paperWidth = 384, ILogger? logger = null, byte id=0x01)
|
||||
{
|
||||
_outputPath = outputPath;
|
||||
_paperWidth = paperWidth;
|
||||
_logger = logger;
|
||||
_id = id;
|
||||
|
||||
// Create image directory next to HTML file
|
||||
var directory = Path.GetDirectoryName(outputPath) ?? ".";
|
||||
@@ -174,8 +177,7 @@ public class HtmlPrinter : IEpsonPrinter
|
||||
/// <inheritdoc />
|
||||
public Task<byte?> GetPrinterIdAsync()
|
||||
{
|
||||
_logger?.LogDebug("HtmlPrinter: Returning mock printer ID (0x01 = TM-T30III)");
|
||||
return Task.FromResult<byte?>(0x01);
|
||||
return Task.FromResult<byte?>(_id);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -320,9 +322,6 @@ public class HtmlPrinter : IEpsonPrinter
|
||||
{
|
||||
EnsureConnected();
|
||||
_logger?.LogInformation("HtmlPrinter: Cutting paper");
|
||||
|
||||
_content.AppendLine("<div class=\"cut\"><span class=\"scissors\">✂</span></div>");
|
||||
|
||||
WriteHtmlFile();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -493,7 +492,7 @@ public class HtmlPrinter : IEpsonPrinter
|
||||
|
||||
if (_isBiggerFontTM220)
|
||||
{
|
||||
effectiveHeightMultiplier *= 2;
|
||||
effectiveWidthMultiplier *= 2;
|
||||
}
|
||||
|
||||
int fontSize = baseHeight * effectiveHeightMultiplier;
|
||||
|
||||
@@ -6,6 +6,7 @@ public class EpsonPrintServiceConfiguration: IPrinterConfigurationSource, IAssig
|
||||
{
|
||||
public string GroupId { get; set; }
|
||||
public string RestaurantId { get; set; }
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
public Dictionary<string, PrinterConfiguration> PrinterConfigurations { get; set; }
|
||||
public PrinterConfiguration GetConfiguration(string printerAddress)
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
using Inspectron.Epson;
|
||||
using Inspectron.Epson.PrintServer.ConfigurationSources;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace EpsonPrintService;
|
||||
|
||||
public class JamesDiscoveredPrintersReceiver: IDiscoveredPrintersReceiver
|
||||
{
|
||||
private readonly EpsonPrintServiceConfiguration _configuration;
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public JamesDiscoveredPrintersReceiver(EpsonPrintServiceConfiguration configuration, HttpClient httpClient)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public async Task OnPrintersDiscoveredAsync(IReadOnlyList<DiscoveredPrinter> printers)
|
||||
{
|
||||
var apiKey = _configuration.ApiKey;
|
||||
|
||||
var request = new DiscoveredPrintersRequest
|
||||
{
|
||||
Printers = printers.Select(p => new DiscoveredPrinterDto
|
||||
{
|
||||
PrinterId = p.IPAddress,
|
||||
Name = p.IPAddress,
|
||||
Address = p.IPAddress,
|
||||
Model = p.ModelName,
|
||||
IsMain = false
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
using var requestMessage = new HttpRequestMessage(HttpMethod.Post, "https://api.dev.gastrojames.ch/api/printerServer/discovered-printers");
|
||||
requestMessage.Headers.Add("X-Printer-Server-Key", apiKey);
|
||||
requestMessage.Content = JsonContent.Create(request);
|
||||
|
||||
var response = await _httpClient.SendAsync(requestMessage);
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
private class DiscoveredPrintersRequest
|
||||
{
|
||||
public List<DiscoveredPrinterDto> Printers { get; set; } = new();
|
||||
}
|
||||
|
||||
private class DiscoveredPrinterDto
|
||||
{
|
||||
public string PrinterId { get; set; } = "";
|
||||
public string Name { get; set; } = "";
|
||||
public string Address { get; set; } = "";
|
||||
public string Model { get; set; } = "";
|
||||
public bool IsMain { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ public class SignalRPrintJobSource: IPrintJobSource
|
||||
_logger = logger;
|
||||
|
||||
_connection = new HubConnectionBuilder()
|
||||
.WithUrl("https://api.stage.gastrojames.ch/hubs/internal?access_token=https://api.stage.gastrojames.ch/hubs/internal")
|
||||
.WithUrl("https://api.dev.gastrojames.ch/hubs/printer-servers")
|
||||
.WithAutomaticReconnect(new InfiniteRetryPolicy())
|
||||
.Build();
|
||||
|
||||
@@ -92,8 +92,8 @@ public class SignalRPrintJobSource: IPrintJobSource
|
||||
_logger.LogInformation("Received print job for working area: {PrintJob}", JsonSerializer.Serialize(args));
|
||||
_printJobChannel.Writer.TryWrite(new PrintJob
|
||||
{
|
||||
AreaId = args.WorkingAreaId,
|
||||
Document = args.Content,
|
||||
IP = args.PrinterIp,
|
||||
Document = args,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -104,8 +104,15 @@ public class SignalRPrintJobSource: IPrintJobSource
|
||||
|
||||
public class PrintJobFromSignalR
|
||||
{
|
||||
[JsonPropertyName("workingAreaId")]
|
||||
public string WorkingAreaId { get; set; }
|
||||
[JsonPropertyName("printerIp")]
|
||||
public string PrinterIp { get; set; }
|
||||
|
||||
[JsonPropertyName("logoUrl")]
|
||||
public string? LogoUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("receiptType")]
|
||||
public int ReceiptType { get; set; }
|
||||
|
||||
[JsonPropertyName("content")]
|
||||
public string Content { get; set; }
|
||||
}
|
||||
|
||||
@@ -34,14 +34,8 @@ public class PrintLoop
|
||||
while (!_cancellationSource!.Token.IsCancellationRequested)
|
||||
{
|
||||
var job = await _jobSource.GetNextJobAsync(_cancellationToken);
|
||||
var assigned = _assignedPrinterRepository.GetAssignedPrinter(job.AreaId);
|
||||
if (assigned == null)
|
||||
{
|
||||
_logger.LogWarning("Received print job for unassigned area {AreaId}, skipping", job.AreaId);
|
||||
continue;
|
||||
}
|
||||
|
||||
_printServer.SubmitJob(assigned, job);
|
||||
_printServer.SubmitJob(job.IP, job);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Reflection;
|
||||
using Inspectron.Epson.PrintServer.Printers;
|
||||
|
||||
namespace Inspectron.Epson.PrintServer.PrintServices;
|
||||
|
||||
@@ -7,12 +8,14 @@ public class EpsonPrintService: IPrintService
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IPrinterFactory _printerFactory;
|
||||
private readonly IWrapperPrinterFactory _wrapperPrinterFactory;
|
||||
private readonly IPrinterConfigurationSource _printerConfigurationSource;
|
||||
|
||||
public EpsonPrintService(ILogger logger, IPrinterFactory printerFactory, IPrinterConfigurationSource printerConfigurationSource)
|
||||
public EpsonPrintService(ILogger logger, IPrinterFactory printerFactory, IWrapperPrinterFactory wrapperPrinterFactory, IPrinterConfigurationSource printerConfigurationSource)
|
||||
{
|
||||
_logger = logger;
|
||||
_printerFactory = printerFactory;
|
||||
_wrapperPrinterFactory = wrapperPrinterFactory;
|
||||
_printerConfigurationSource = printerConfigurationSource;
|
||||
}
|
||||
|
||||
@@ -21,7 +24,7 @@ public class EpsonPrintService: IPrintService
|
||||
try
|
||||
{
|
||||
var configuration = _printerConfigurationSource.GetConfiguration(printerIp);
|
||||
await using var epsonPrinter = new EpsonPrinter(_logger);
|
||||
await using var epsonPrinter = _printerFactory.CreatePrinter();
|
||||
string address;
|
||||
int port = 9100;
|
||||
var printerAddress = configuration.Address;
|
||||
@@ -45,19 +48,46 @@ public class EpsonPrintService: IPrintService
|
||||
{
|
||||
throw new InvalidOperationException("Printer is offline or out of paper.");
|
||||
}
|
||||
|
||||
var printerAdapter = _printerFactory.CreatePrinterFromId(printerId.Value, epsonPrinter);
|
||||
var printerAdapter = _wrapperPrinterFactory.CreatePrinterFromId(printerId.Value, epsonPrinter);
|
||||
|
||||
await printerAdapter.InitAsync();
|
||||
|
||||
if (configuration.LogoFilename != null)
|
||||
if (!string.IsNullOrEmpty(job.Document.LogoUrl))
|
||||
{
|
||||
await printerAdapter.PrintImageAsync(configuration.LogoFilename);
|
||||
// Download logo image
|
||||
var logoFileName = Path.GetFileName(new Uri(job.Document.LogoUrl).LocalPath);
|
||||
var logoCachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logos");
|
||||
|
||||
// Ensure logos cache directory exists
|
||||
if (!Directory.Exists(logoCachePath))
|
||||
{
|
||||
Directory.CreateDirectory(logoCachePath);
|
||||
}
|
||||
|
||||
var cachedLogoPath = Path.Combine(logoCachePath, logoFileName);
|
||||
|
||||
// Download only if not already cached
|
||||
if (!File.Exists(cachedLogoPath))
|
||||
{
|
||||
using var httpClient = new HttpClient();
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(10);
|
||||
|
||||
var logoData = await httpClient.GetByteArrayAsync(job.Document.LogoUrl);
|
||||
await File.WriteAllBytesAsync(cachedLogoPath, logoData);
|
||||
|
||||
_logger.LogInformation("Downloaded logo {LogoFileName} from {LogoUrl}", logoFileName, job.Document.LogoUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogDebug("Using cached logo {LogoFileName}", logoFileName);
|
||||
}
|
||||
|
||||
await printerAdapter.PrintImageAsync(cachedLogoPath);
|
||||
}
|
||||
|
||||
await printerAdapter.SetFontSizeAsync(configuration.FontSize);
|
||||
|
||||
await printerAdapter.PrintTextAsync(job.Document);
|
||||
await printerAdapter.PrintTextAsync(job.Document.Content);
|
||||
await Task.Delay(200);
|
||||
status = await epsonPrinter.GetPrinterStatusAsync();
|
||||
if (!status.IsOnline)
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Inspectron.Epson.PrintServer.PrintServices;
|
||||
|
||||
public interface IPrinterFactory
|
||||
{
|
||||
IPrinter CreatePrinterFromId(byte printerId, EpsonPrinter epsonPrinter);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Inspectron.Epson.PrintServer.PrintServices;
|
||||
|
||||
public interface IWrapperPrinterFactory
|
||||
{
|
||||
IPrinter CreatePrinterFromId(byte printerId, IEpsonPrinter epsonPrinter);
|
||||
}
|
||||
18
Inspectron.Epson/PrintServer/Printers/EpsonPrinterFactory.cs
Normal file
18
Inspectron.Epson/PrintServer/Printers/EpsonPrinterFactory.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Inspectron.Epson.PrintServer.Printers;
|
||||
|
||||
public class EpsonPrinterFactory:IPrinterFactory
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public EpsonPrinterFactory(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public IEpsonPrinter CreatePrinter()
|
||||
{
|
||||
return new EpsonPrinter(_logger);
|
||||
}
|
||||
}
|
||||
12
Inspectron.Epson/PrintServer/Printers/HtmlPrinterFactory.cs
Normal file
12
Inspectron.Epson/PrintServer/Printers/HtmlPrinterFactory.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Inspectron.Epson.PrintServer.Printers;
|
||||
|
||||
public class HtmlPrinterFactory(string filename,int paperWidth=384, byte type=0x1, ILogger logger=null) : IPrinterFactory
|
||||
{
|
||||
|
||||
public IEpsonPrinter CreatePrinter()
|
||||
{
|
||||
return new HtmlPrinter(filename,paperWidth: paperWidth, id:type);
|
||||
}
|
||||
}
|
||||
6
Inspectron.Epson/PrintServer/Printers/IPrinterFactory.cs
Normal file
6
Inspectron.Epson/PrintServer/Printers/IPrinterFactory.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Inspectron.Epson.PrintServer.Printers;
|
||||
|
||||
public interface IPrinterFactory
|
||||
{
|
||||
IEpsonPrinter CreatePrinter();
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
namespace Inspectron.Epson.PrintServer.Printers;
|
||||
|
||||
public class PrinterFactory:IPrinterFactory
|
||||
public class PrinterWrapperFactory:IWrapperPrinterFactory
|
||||
{
|
||||
public IPrinter CreatePrinterFromId(byte printerId, EpsonPrinter epsonPrinter)
|
||||
public IPrinter CreatePrinterFromId(byte printerId, IEpsonPrinter epsonPrinter)
|
||||
{
|
||||
switch (printerId)
|
||||
{
|
||||
@@ -8,9 +8,9 @@ namespace Inspectron.Epson.PrintServer.Printers;
|
||||
|
||||
public class TM_T30IIITranslated:IPrinter
|
||||
{
|
||||
private readonly EpsonPrinter _printer;
|
||||
private readonly IEpsonPrinter _printer;
|
||||
|
||||
public TM_T30IIITranslated(EpsonPrinter printer)
|
||||
public TM_T30IIITranslated(IEpsonPrinter printer)
|
||||
{
|
||||
_printer = printer;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
using Inspectron.Epson.PrintServer.Printers.Utils;
|
||||
using Inspectron.Epson.PrintServer.Printers.Utils.KitchenReceipt;
|
||||
using Inspectron.Epson.PrintServer.PrintServices;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Inspectron.Epson.PrintServer.Printers;
|
||||
|
||||
public class TM_U220IITranslated:IPrinter
|
||||
{
|
||||
private readonly EpsonPrinter _printer;
|
||||
private readonly IEpsonPrinter _printer;
|
||||
|
||||
public TM_U220IITranslated(EpsonPrinter printer)
|
||||
public TM_U220IITranslated(IEpsonPrinter printer)
|
||||
{
|
||||
_printer = printer;
|
||||
}
|
||||
@@ -34,11 +36,10 @@ public class TM_U220IITranslated:IPrinter
|
||||
|
||||
public async Task PrintTextAsync(string text)
|
||||
{
|
||||
ReceiptTranslator translator = new ReceiptTranslator();
|
||||
var ast = translator.ParseReceipt(text);
|
||||
KitchenReceiptPrinter printer = new KitchenReceiptPrinter(lineWidth:33,21);
|
||||
var commands = printer.ConvertToCommands((TranslatedKitchenReceipt)ast);
|
||||
foreach (KitchenPrintCommand command in commands)
|
||||
var deserializedReceipt = JsonSerializer.Deserialize<KitchenReceipt>(text);
|
||||
KitchenReceiptConverter translator = new KitchenReceiptConverter(bigLineWidth: 20, lineWidth: 33);
|
||||
var commands = translator.ConvertToPrintCommands(deserializedReceipt);
|
||||
foreach (var command in commands)
|
||||
{
|
||||
await _printer.SetBiggerFontTM220(command.IsBig);
|
||||
await Task.Delay(200);
|
||||
@@ -49,12 +50,7 @@ public class TM_U220IITranslated:IPrinter
|
||||
await _printer.PrintTextAsync(command.Text + "\n");
|
||||
await Task.Delay(200);
|
||||
}
|
||||
//await _printer.PrintTextAsync(text);
|
||||
//var lines = text.Split('\n').Length;
|
||||
//for (int i = 0; i < lines; i++)
|
||||
//{
|
||||
// await Task.Delay(200);
|
||||
//}
|
||||
|
||||
await _printer.FeedLinesAsync(10);
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ public class FinalReceipt
|
||||
public string ThankYouMessage { get; set; }
|
||||
public string GoodbyeMessageLine1 { get; set; }
|
||||
public string GoodbyeMessageLine2 { get; set; }
|
||||
public PaymentTerminalReceipt TerminalReceipt { get; set; }
|
||||
}
|
||||
|
||||
public class ReceiptItem
|
||||
@@ -44,3 +45,22 @@ public class TaxInfo
|
||||
public decimal TaxAmount { get; set; }
|
||||
public string Currency { get; set; }
|
||||
}
|
||||
|
||||
public class PaymentTerminalReceipt
|
||||
{
|
||||
public string ReceiptType { get; set; } // e.g., "*** Kundenbeleg ***"
|
||||
public string BookingType { get; set; } // e.g., "Buchung"
|
||||
public string PaymentSystem { get; set; } // e.g., "TWINT"
|
||||
public string TransactionNumber { get; set; }
|
||||
public DateTime TransactionDateTime { get; set; }
|
||||
public string TerminalId { get; set; }
|
||||
public string AID { get; set; }
|
||||
public string TransactionSeqCount { get; set; }
|
||||
public string TransactionRefNo { get; set; }
|
||||
public string AuthCode { get; set; }
|
||||
public string AcquirerId { get; set; }
|
||||
public decimal EftAmount { get; set; }
|
||||
public decimal TipAmount { get; set; }
|
||||
public decimal TotalEftAmount { get; set; }
|
||||
public string Currency { get; set; }
|
||||
}
|
||||
@@ -83,9 +83,6 @@ public class ReceiptConverter
|
||||
commands.Add(new PrintCommand(""));
|
||||
|
||||
// Footer info
|
||||
//commands.Add(new PrintCommand(Center($"Bedient von: {finalReceipt.WaiterName}", false)));
|
||||
//commands.Add(new PrintCommand(Center($"Terminal: {finalReceipt.Terminal}", false)));
|
||||
//commands.Add(new PrintCommand(Center($"Tisch: {finalReceipt.TableNumber}", false)));
|
||||
commands.Add(new PrintCommand($"Bedient von:".PadLeft(_lineWidth / 2) + $" {finalReceipt.WaiterName}"));
|
||||
commands.Add(new PrintCommand($"Terminal:".PadLeft(_lineWidth / 2) + $" {finalReceipt.Terminal}"));
|
||||
commands.Add(new PrintCommand($"Tisch:".PadLeft(_lineWidth / 2)+$" {finalReceipt.TableNumber}"));
|
||||
@@ -94,6 +91,33 @@ public class ReceiptConverter
|
||||
|
||||
// VAT number
|
||||
commands.Add(new PrintCommand(Center(finalReceipt.VatNumber, false)));
|
||||
commands.Add(new PrintCommand(""));
|
||||
|
||||
// Payment Terminal Receipt
|
||||
if (finalReceipt.TerminalReceipt != null)
|
||||
{
|
||||
var terminal = finalReceipt.TerminalReceipt;
|
||||
|
||||
commands.Add(new PrintCommand(Center(terminal.ReceiptType, false)));
|
||||
commands.Add(new PrintCommand(Center(terminal.BookingType, false)));
|
||||
commands.Add(new PrintCommand(Center(terminal.PaymentSystem, false)));
|
||||
commands.Add(new PrintCommand(terminal.TransactionNumber));
|
||||
commands.Add(new PrintCommand($"{terminal.TransactionDateTime:dd.MM.yyyy}".PadRight(_lineWidth/2) + $"{terminal.TransactionDateTime:HH:mm:ss}".PadLeft(_lineWidth/2)));
|
||||
|
||||
commands.Add(new PrintCommand($"Trm-Id:".PadRight(_lineWidth/2) + $"{terminal.TerminalId}".PadLeft(_lineWidth/2)));
|
||||
commands.Add(new PrintCommand($"AID:".PadRight(_lineWidth/2) + $"{terminal.AID}".PadLeft(_lineWidth/2)));
|
||||
commands.Add(new PrintCommand($"Trx. Seq-Cnt:".PadRight(_lineWidth/2) + $"{terminal.TransactionSeqCount}".PadLeft(_lineWidth/2)));
|
||||
commands.Add(new PrintCommand($"Trx. Ref-No:".PadRight(_lineWidth/2) + $"{terminal.TransactionRefNo}".PadLeft(_lineWidth/2)));
|
||||
commands.Add(new PrintCommand($"Auth. Code:".PadRight(_lineWidth/2) + $"{terminal.AuthCode}".PadLeft(_lineWidth/2)));
|
||||
commands.Add(new PrintCommand($"Acq-Id:".PadRight(_lineWidth/2) + $"{terminal.AcquirerId}".PadLeft(_lineWidth/2)));
|
||||
|
||||
commands.Add(new PrintCommand($"EFT {terminal.Currency}:".PadRight(_lineWidth/2) + $"{terminal.EftAmount:F2}".PadLeft(_lineWidth/2)));
|
||||
commands.Add(new PrintCommand($"Trinkgeld {terminal.Currency}:".PadRight(_lineWidth/2) + $"{terminal.TipAmount:F2}".PadLeft(_lineWidth/2)));
|
||||
commands.Add(new PrintCommand($"Total-EFT {terminal.Currency}:".PadRight(_lineWidth/2) + $"{terminal.TotalEftAmount:F2}".PadLeft(_lineWidth/2)));
|
||||
|
||||
commands.Add(new PrintCommand(new string('-', _lineWidth)));
|
||||
commands.Add(new PrintCommand(""));
|
||||
}
|
||||
|
||||
// Thank you message
|
||||
commands.Add(new PrintCommand(Center(finalReceipt.ThankYouMessage, false)));
|
||||
|
||||
@@ -40,15 +40,11 @@
|
||||
|
||||
public List<Dish> Dishes { get; set; } = new List<Dish>();
|
||||
|
||||
/// <summary>
|
||||
/// Additional items or notes (e.g., "Amuse Bouche")
|
||||
/// </summary>
|
||||
public List<string> AdditionalInfo { get; set; }
|
||||
|
||||
public KitchenReceipt()
|
||||
{
|
||||
Gangs = new List<Gang>();
|
||||
AdditionalInfo = new List<string>();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace Inspectron.Epson.PrintServer.Printers.Utils.OrderItems;
|
||||
|
||||
public class OrderItemsReceipt
|
||||
{
|
||||
public string OrderNumber { get; set; }
|
||||
public string QRInfo { get; set; }
|
||||
public DateTime DateTime { get; set; }
|
||||
public string ClientNotes { get; set; }
|
||||
public List<OrderItem> Items { get; set; } = new List<OrderItem>();
|
||||
}
|
||||
|
||||
public class OrderItem
|
||||
{
|
||||
public int Number { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public string Size { get; set; }
|
||||
public List<string> SubItems { get; set; } = new List<string>();
|
||||
public ItemModifications Modifications { get; set; }
|
||||
public string Comment { get; set; }
|
||||
}
|
||||
|
||||
public class ItemModifications
|
||||
{
|
||||
public List<string> Removed { get; set; } = new List<string>();
|
||||
public List<string> Added { get; set; } = new List<string>();
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
namespace Inspectron.Epson.PrintServer.Printers.Utils.OrderItems;
|
||||
|
||||
public class ReceiptConverter
|
||||
{
|
||||
private readonly int _lineWidth;
|
||||
private readonly int _bigFontLineWidth;
|
||||
|
||||
public ReceiptConverter(int lineWidth = 42, int bigFontLineWidth = 21)
|
||||
{
|
||||
_lineWidth = lineWidth;
|
||||
_bigFontLineWidth = bigFontLineWidth;
|
||||
}
|
||||
|
||||
public List<PrintCommand> ConvertToPrintCommands(OrderItemsReceipt receipt)
|
||||
{
|
||||
var commands = new List<PrintCommand>();
|
||||
|
||||
// Header box
|
||||
AddHeaderBox(commands, receipt);
|
||||
|
||||
// Client Notes
|
||||
if (!string.IsNullOrEmpty(receipt.ClientNotes))
|
||||
{
|
||||
commands.Add(new PrintCommand($"|Client Notes: {receipt.ClientNotes}"));
|
||||
}
|
||||
|
||||
// Separator line
|
||||
commands.Add(new PrintCommand(new string('-', _lineWidth)));
|
||||
|
||||
// Items
|
||||
foreach (var item in receipt.Items)
|
||||
{
|
||||
commands.Add(new PrintCommand(""));
|
||||
AddOrderItem(commands, item);
|
||||
}
|
||||
|
||||
return commands;
|
||||
}
|
||||
|
||||
private void AddHeaderBox(List<PrintCommand> commands, OrderItemsReceipt receipt)
|
||||
{
|
||||
// Column widths (fixed proportions)
|
||||
int col1Width = 8; // Order column
|
||||
int col3Width = 12; // Date/Time column
|
||||
int col2Width = _lineWidth - col1Width - col3Width - 4; // QR column (4 for borders |)
|
||||
|
||||
// Prepare column content with labels on first line, values on subsequent lines
|
||||
var col1Lines = new List<string> { "Order:" };
|
||||
col1Lines.AddRange(WrapText($"#{receipt.OrderNumber}", col1Width));
|
||||
|
||||
var col2Lines = new List<string> { "QR:" };
|
||||
col2Lines.AddRange(WrapText(receipt.QRInfo, col2Width));
|
||||
|
||||
var col3Lines = new List<string>
|
||||
{
|
||||
$"{receipt.DateTime:dd.MM.yyyy}",
|
||||
$"{receipt.DateTime:HH:mm:ss}"
|
||||
};
|
||||
|
||||
// Determine max rows needed
|
||||
int maxRows = Math.Max(Math.Max(col1Lines.Count, col2Lines.Count), col3Lines.Count);
|
||||
|
||||
// Pad columns to have equal rows
|
||||
while (col1Lines.Count < maxRows) col1Lines.Add("");
|
||||
while (col2Lines.Count < maxRows) col2Lines.Add("");
|
||||
while (col3Lines.Count < maxRows) col3Lines.Add("");
|
||||
|
||||
// Top border
|
||||
commands.Add(new PrintCommand("+" + new string('-', col1Width) + "+" + new string('-', col2Width) + "+" + new string('-', col3Width) + "+"));
|
||||
|
||||
// Content rows
|
||||
for (int i = 0; i < maxRows; i++)
|
||||
{
|
||||
string row = "|" +
|
||||
CenterInWidth(col1Lines[i], col1Width) + "|" +
|
||||
CenterInWidth(col2Lines[i], col2Width) + "|" +
|
||||
CenterInWidth(col3Lines[i], col3Width) + "|";
|
||||
|
||||
commands.Add(new PrintCommand(row, isBold: i == 0));
|
||||
}
|
||||
|
||||
// Bottom border
|
||||
commands.Add(new PrintCommand("+" + new string('-', col1Width) + "+" + new string('-', col2Width) + "+" + new string('-', col3Width) + "+"));
|
||||
}
|
||||
|
||||
private List<string> WrapText(string text, int maxWidth)
|
||||
{
|
||||
var lines = new List<string>();
|
||||
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
lines.Add("");
|
||||
return lines;
|
||||
}
|
||||
|
||||
// Trim to fit within column
|
||||
while (text.Length > 0)
|
||||
{
|
||||
if (text.Length <= maxWidth)
|
||||
{
|
||||
lines.Add(text);
|
||||
break;
|
||||
}
|
||||
|
||||
// Find a good break point (prefer space)
|
||||
int breakPoint = text.LastIndexOf(' ', Math.Min(maxWidth, text.Length - 1));
|
||||
if (breakPoint <= 0 || breakPoint > maxWidth)
|
||||
{
|
||||
breakPoint = maxWidth;
|
||||
}
|
||||
|
||||
lines.Add(text.Substring(0, breakPoint).TrimEnd());
|
||||
text = text.Substring(breakPoint).TrimStart();
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
private void AddOrderItem(List<PrintCommand> commands, OrderItem item)
|
||||
{
|
||||
// Main item line: "1 Item Name x1" or "3 Item Name Size: L x2"
|
||||
string numberPart = $"{item.Number}";
|
||||
string namePart = item.Name;
|
||||
string quantityPart = $"x{item.Quantity}";
|
||||
|
||||
string sizePart = "";
|
||||
if (!string.IsNullOrEmpty(item.Size))
|
||||
{
|
||||
sizePart = $"Size: {item.Size} ";
|
||||
}
|
||||
|
||||
string rightPart = sizePart + quantityPart;
|
||||
int spacesNeeded = _lineWidth - numberPart.Length - 1 - namePart.Length - rightPart.Length;
|
||||
if (spacesNeeded < 1) spacesNeeded = 1;
|
||||
|
||||
string mainLine = numberPart + " " + namePart + new string(' ', spacesNeeded) + rightPart;
|
||||
commands.Add(new PrintCommand(mainLine));
|
||||
|
||||
// Sub-items
|
||||
foreach (var subItem in item.SubItems)
|
||||
{
|
||||
commands.Add(new PrintCommand($" - {subItem}"));
|
||||
}
|
||||
|
||||
// Modifications
|
||||
if (item.Modifications != null && (item.Modifications.Removed.Any() || item.Modifications.Added.Any()))
|
||||
{
|
||||
commands.Add(new PrintCommand(" Change of"));
|
||||
commands.Add(new PrintCommand(" Ingredients:"));
|
||||
|
||||
foreach (var removed in item.Modifications.Removed)
|
||||
{
|
||||
commands.Add(new PrintCommand($" - {removed}"));
|
||||
}
|
||||
|
||||
foreach (var added in item.Modifications.Added)
|
||||
{
|
||||
commands.Add(new PrintCommand($" + {added}"));
|
||||
}
|
||||
}
|
||||
|
||||
// Comment
|
||||
if (!string.IsNullOrEmpty(item.Comment))
|
||||
{
|
||||
commands.Add(new PrintCommand($" Comment: {item.Comment}"));
|
||||
}
|
||||
}
|
||||
|
||||
private string CenterInWidth(string text, int width)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return new string(' ', width);
|
||||
|
||||
if (text.Length >= width)
|
||||
return text.Substring(0, width);
|
||||
|
||||
int totalPadding = width - text.Length;
|
||||
int leftPadding = totalPadding / 2;
|
||||
int rightPadding = totalPadding - leftPadding;
|
||||
|
||||
return new string(' ', leftPadding) + text + new string(' ', rightPadding);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
public class PrintJob
|
||||
using static Inspectron.Epson.PrintServer.JobSources.SignalRPrintJobSource;
|
||||
|
||||
public class PrintJob
|
||||
{
|
||||
public string AreaId { get; set; }
|
||||
public string Document { get; set; }
|
||||
// todo: change to ip address
|
||||
public string IP { get; set; }
|
||||
public PrintJobFromSignalR Document { get; set; }
|
||||
public DateTime QueuedAt { get; set; }
|
||||
public int RetryCount { get; set; }
|
||||
|
||||
public PrintJob()
|
||||
{
|
||||
AreaId = Guid.NewGuid().ToString();
|
||||
QueuedAt = DateTime.UtcNow;
|
||||
RetryCount = 0;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public class PrinterQueue
|
||||
{
|
||||
_queue.Enqueue(job);
|
||||
_signal.Release(); // Signal that there's work to do
|
||||
_logger.LogInformation("Job {JobId} queued for printer {PrinterId}. Queue length: {QueueLength}", job.AreaId, PrinterIp, QueueLength);
|
||||
_logger.LogInformation("Job queued for printer {PrinterId}. Queue length: {QueueLength}", PrinterIp, QueueLength);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
@@ -50,7 +50,7 @@ public class PrinterQueue
|
||||
{
|
||||
_priorityQueue.Push(job);
|
||||
_signal.Release();
|
||||
_logger.LogInformation("Job {JobId} priority queued for printer {PrinterId}. Queue length: {QueueLength}", job.AreaId, PrinterIp, QueueLength);
|
||||
_logger.LogInformation("Job priority queued for printer {PrinterId}. Queue length: {QueueLength}", PrinterIp, QueueLength);
|
||||
}
|
||||
private async Task ProcessQueueAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -68,7 +68,7 @@ public class PrinterQueue
|
||||
|
||||
if (job!=null)
|
||||
{
|
||||
_logger.LogInformation("Processing job {JobId} on printer {PrinterId}", job.AreaId, PrinterIp);
|
||||
_logger.LogInformation("Processing job on printer {PrinterId}", PrinterIp);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -79,18 +79,18 @@ public class PrinterQueue
|
||||
{
|
||||
// Re-queue with retry logic
|
||||
job.RetryCount++;
|
||||
_logger.LogWarning("Job {JobId} failed, retrying ({RetryCount}/3)", job.AreaId, job.RetryCount);
|
||||
_logger.LogWarning("Job failed, retrying ({RetryCount}/3)", job.RetryCount);
|
||||
await Task.Delay(5000, cancellationToken); // Wait before retry
|
||||
EnqueuePriority(job);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("Job {JobId} completed successfully on printer {PrinterId}", job.AreaId, PrinterIp);
|
||||
_logger.LogInformation("Job completed successfully on printer {PrinterId}", PrinterIp);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error processing job {JobId}", job.AreaId);
|
||||
_logger.LogError(ex, "Error processing job");
|
||||
// Handle exception (retry, log, etc.)
|
||||
}
|
||||
}
|
||||
|
||||
BIN
order_items.png
Normal file
BIN
order_items.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
BIN
receipt_tail.png
Normal file
BIN
receipt_tail.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
Reference in New Issue
Block a user