Add project files.
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
using Inspectron.Epson.PrintServer.PrintServices;
|
||||
|
||||
namespace Inspectron.Epson.PrintServer.ConfigurationSources;
|
||||
|
||||
public class EpsonPrintServiceConfiguration: IPrinterConfigurationSource, IAssignedPrinterRepository
|
||||
{
|
||||
public string GroupId { get; set; }
|
||||
public string RestaurantId { get; set; }
|
||||
|
||||
public Dictionary<string, PrinterConfiguration> PrinterConfigurations { get; set; }
|
||||
public PrinterConfiguration GetConfiguration(string printerAddress)
|
||||
{
|
||||
return PrinterConfigurations[printerAddress];
|
||||
}
|
||||
|
||||
public string? GetAssignedPrinter(string workAreaId)
|
||||
{
|
||||
return PrinterConfigurations.Values.Where(x => x.AreaId == workAreaId).Select(x=>x.Address).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Inspectron.Epson.PrintServer.PrintServices;
|
||||
|
||||
namespace Inspectron.Epson.PrintServer.ConfigurationSources;
|
||||
|
||||
public class FixedConfigurationSource: IPrinterConfigurationSource
|
||||
{
|
||||
public PrinterConfiguration GetConfiguration(string printerAddress)
|
||||
{
|
||||
return new PrinterConfiguration()
|
||||
{
|
||||
Address = "127.0.0.1:8888",
|
||||
FontSize = 1,
|
||||
LogoFilename = "test.png"
|
||||
};
|
||||
}
|
||||
}
|
||||
6
Inspectron.Epson/PrintServer/IPrintJobSource.cs
Normal file
6
Inspectron.Epson/PrintServer/IPrintJobSource.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Inspectron.Epson.PrintServer;
|
||||
|
||||
public interface IPrintJobSource
|
||||
{
|
||||
Task<PrintJob> GetNextJobAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
122
Inspectron.Epson/PrintServer/JobSources/SignalRPrintJobSource.cs
Normal file
122
Inspectron.Epson/PrintServer/JobSources/SignalRPrintJobSource.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Channels;
|
||||
using Inspectron.Epson.PrintServer.ConfigurationSources;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Inspectron.Epson.PrintServer.JobSources;
|
||||
|
||||
public class SignalRPrintJobSource: IPrintJobSource
|
||||
{
|
||||
private readonly EpsonPrintServiceConfiguration _groupConfiguration;
|
||||
private readonly ILogger _logger;
|
||||
private readonly HubConnection _connection;
|
||||
private readonly Channel<PrintJob> _printJobChannel = Channel.CreateUnbounded<PrintJob>();
|
||||
|
||||
public SignalRPrintJobSource(EpsonPrintServiceConfiguration groupConfiguration, ILogger logger)
|
||||
{
|
||||
_groupConfiguration = groupConfiguration;
|
||||
_logger = logger;
|
||||
|
||||
_connection = new HubConnectionBuilder()
|
||||
.WithUrl("https://api.stage.gastrojames.ch/hubs/internal?access_token=https://api.stage.gastrojames.ch/hubs/internal")
|
||||
.WithAutomaticReconnect(new InfiniteRetryPolicy())
|
||||
.Build();
|
||||
|
||||
// Register handlers BEFORE starting connection
|
||||
_connection.On<PrintJobFromSignalR>("PrintJob", OnPrintJobReceived);
|
||||
|
||||
_connection.Reconnecting += OnReconnecting;
|
||||
_connection.Reconnected += OnReconnected;
|
||||
_connection.Closed += OnClosed;
|
||||
|
||||
_ = InitializeConnectionAsync();
|
||||
}
|
||||
|
||||
private async Task InitializeConnectionAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await _connection.StartAsync();
|
||||
await _connection.InvokeAsync("JoinGroup", _groupConfiguration.GroupId);
|
||||
_logger.LogInformation("SignalR connection started and joined group {GroupId}.", _groupConfiguration.GroupId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to initialize SignalR connection.");
|
||||
}
|
||||
}
|
||||
|
||||
private Task OnReconnecting(Exception? exception)
|
||||
{
|
||||
_logger.LogWarning(exception, "SignalR connection lost. Reconnecting...");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task OnReconnected(string? connectionId)
|
||||
{
|
||||
_logger.LogInformation("SignalR reconnected with connection ID: {ConnectionId}. Rejoining group...", connectionId);
|
||||
try
|
||||
{
|
||||
await _connection.InvokeAsync("JoinGroup", _groupConfiguration.GroupId);
|
||||
_logger.LogInformation("Rejoined SignalR group {GroupId} after reconnection.", _groupConfiguration.GroupId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to rejoin group after reconnection.");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnClosed(Exception? exception)
|
||||
{
|
||||
_logger.LogError(exception, "SignalR connection closed. Attempting manual restart...");
|
||||
|
||||
// Manual reconnection loop if automatic reconnection exhausted (shouldn't happen with infinite retry)
|
||||
await Task.Delay(5000); // Wait before retry
|
||||
try
|
||||
{
|
||||
await _connection.StartAsync();
|
||||
await _connection.InvokeAsync("JoinGroup", _groupConfiguration.GroupId);
|
||||
_logger.LogInformation("Manually reconnected to SignalR.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Manual reconnection failed. Will retry on next connection closed event.");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPrintJobReceived(PrintJobFromSignalR args)
|
||||
{
|
||||
|
||||
_logger.LogInformation("Received print job for working area: {PrintJob}", JsonSerializer.Serialize(args));
|
||||
_printJobChannel.Writer.TryWrite(new PrintJob
|
||||
{
|
||||
AreaId = args.WorkingAreaId,
|
||||
Document = args.Content,
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<PrintJob> GetNextJobAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return await _printJobChannel.Reader.ReadAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public class PrintJobFromSignalR
|
||||
{
|
||||
[JsonPropertyName("workingAreaId")]
|
||||
public string WorkingAreaId { get; set; }
|
||||
[JsonPropertyName("content")]
|
||||
public string Content { get; set; }
|
||||
}
|
||||
|
||||
private class InfiniteRetryPolicy : IRetryPolicy
|
||||
{
|
||||
public TimeSpan? NextRetryDelay(RetryContext retryContext)
|
||||
{
|
||||
// Exponential backoff with a cap at 30 seconds
|
||||
var delay = Math.Min(Math.Pow(2, retryContext.PreviousRetryCount), 30);
|
||||
return TimeSpan.FromSeconds(delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Inspectron.Epson/PrintServer/JobSources/SingleJobSource.cs
Normal file
18
Inspectron.Epson/PrintServer/JobSources/SingleJobSource.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace Inspectron.Epson.PrintServer.JobSources;
|
||||
|
||||
public class SingleJobSource: IPrintJobSource
|
||||
{
|
||||
public SingleJobSource(PrintJob job)
|
||||
{
|
||||
_channel.Writer.WriteAsync(job);
|
||||
}
|
||||
|
||||
private Channel<PrintJob> _channel = Channel.CreateUnbounded<PrintJob>();
|
||||
public async Task<PrintJob> GetNextJobAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return await _channel.Reader.ReadAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
53
Inspectron.Epson/PrintServer/PrintLoop.cs
Normal file
53
Inspectron.Epson/PrintServer/PrintLoop.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using Inspectron.Epson.PrintServer.PrintServices;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Inspectron.Epson.PrintServer;
|
||||
|
||||
public class PrintLoop
|
||||
{
|
||||
private readonly global::PrintServer _printServer;
|
||||
private readonly IPrintService _printService;
|
||||
private readonly IPrintJobSource _jobSource;
|
||||
private readonly IAssignedPrinterRepository _assignedPrinterRepository;
|
||||
private readonly ILogger _logger;
|
||||
private CancellationTokenSource? _cancellationSource;
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
public PrintLoop(global::PrintServer printServer,IPrintService printService, IPrintJobSource jobSource, IAssignedPrinterRepository assignedPrinterRepository, ILogger logger)
|
||||
{
|
||||
_printServer = printServer;
|
||||
_printService = printService;
|
||||
_jobSource = jobSource;
|
||||
_assignedPrinterRepository = assignedPrinterRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Task StartAsync()
|
||||
{
|
||||
_cancellationSource = new CancellationTokenSource();
|
||||
_cancellationToken = _cancellationSource.Token;
|
||||
_= Task.Run(Loop);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
public async Task Loop()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public Task StopAsync()
|
||||
{
|
||||
_cancellationSource!.Cancel();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Inspectron.Epson.PrintServer.PrintServices;
|
||||
|
||||
public class EpsonPrintService: IPrintService
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IPrinterFactory _printerFactory;
|
||||
private readonly IPrinterConfigurationSource _printerConfigurationSource;
|
||||
|
||||
public EpsonPrintService(ILogger logger, IPrinterFactory printerFactory, IPrinterConfigurationSource printerConfigurationSource)
|
||||
{
|
||||
_logger = logger;
|
||||
_printerFactory = printerFactory;
|
||||
_printerConfigurationSource = printerConfigurationSource;
|
||||
}
|
||||
|
||||
public async Task<bool> PrintAsync(string printerIp, PrintJob job)
|
||||
{
|
||||
try
|
||||
{
|
||||
var configuration = _printerConfigurationSource.GetConfiguration(printerIp);
|
||||
await using var epsonPrinter = new EpsonPrinter(_logger);
|
||||
string address;
|
||||
int port = 9100;
|
||||
var printerAddress = configuration.Address;
|
||||
if (printerAddress.Contains(":"))
|
||||
{
|
||||
var parts = printerAddress.Split(':');
|
||||
address = parts[0];
|
||||
port = int.Parse(parts[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
address = printerAddress;
|
||||
}
|
||||
|
||||
await epsonPrinter.ConnectAsync(address, port);
|
||||
|
||||
var printerId = await epsonPrinter.GetPrinterIdAsync();
|
||||
|
||||
var status = await epsonPrinter.GetPrinterStatusAsync();
|
||||
if (!status.IsOnline)
|
||||
{
|
||||
throw new InvalidOperationException("Printer is offline or out of paper.");
|
||||
}
|
||||
|
||||
var printerAdapter = _printerFactory.CreatePrinterFromId(printerId.Value, epsonPrinter);
|
||||
|
||||
await printerAdapter.InitAsync();
|
||||
|
||||
if (configuration.LogoFilename != null)
|
||||
{
|
||||
await printerAdapter.PrintImageAsync(configuration.LogoFilename);
|
||||
}
|
||||
|
||||
await printerAdapter.SetFontSizeAsync(configuration.FontSize);
|
||||
|
||||
await printerAdapter.PrintTextAsync(job.Document);
|
||||
await Task.Delay(200);
|
||||
status = await epsonPrinter.GetPrinterStatusAsync();
|
||||
if (!status.IsOnline)
|
||||
{
|
||||
throw new InvalidOperationException("Printer is offline or out of paper.");
|
||||
}
|
||||
|
||||
await printerAdapter.Cut();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogWarning( e, "Failed to print job for printer {PrinterUid}", printerIp);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Inspectron.Epson.PrintServer.PrintServices;
|
||||
|
||||
public interface IAssignedPrinterRepository
|
||||
{
|
||||
string? GetAssignedPrinter(string workAreaId);
|
||||
}
|
||||
10
Inspectron.Epson/PrintServer/PrintServices/IPrinter.cs
Normal file
10
Inspectron.Epson/PrintServer/PrintServices/IPrinter.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Inspectron.Epson.PrintServer.PrintServices;
|
||||
|
||||
public interface IPrinter
|
||||
{
|
||||
public Task InitAsync();
|
||||
public Task PrintImageAsync(string path);
|
||||
public Task SetFontSizeAsync(int fontSize);
|
||||
public Task PrintTextAsync(string text);
|
||||
public Task Cut();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Inspectron.Epson.PrintServer.PrintServices;
|
||||
|
||||
public interface IPrinterConfigurationSource
|
||||
{
|
||||
PrinterConfiguration GetConfiguration(string printerAddress);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Inspectron.Epson.PrintServer.PrintServices;
|
||||
|
||||
public interface IPrinterFactory
|
||||
{
|
||||
IPrinter CreatePrinterFromId(byte printerId, EpsonPrinter epsonPrinter);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Inspectron.Epson.PrintServer.PrintServices;
|
||||
|
||||
public class PrinterConfiguration
|
||||
{
|
||||
public string? LogoFilename { get; set; }
|
||||
public int FontSize { get; set; }
|
||||
public string Address { get; set; }
|
||||
|
||||
public string AreaId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using Inspectron.Epson.PrintServer.PrintServices;
|
||||
|
||||
namespace Inspectron.Epson.PrintServer.PrinterAssinment;
|
||||
|
||||
public class TestAssignedPrinterRepository:IAssignedPrinterRepository
|
||||
{
|
||||
public string? GetAssignedPrinter(string workAreaId)
|
||||
{
|
||||
|
||||
return "127.0.0.1";
|
||||
}
|
||||
}
|
||||
21
Inspectron.Epson/PrintServer/Printers/PrinterFactory.cs
Normal file
21
Inspectron.Epson/PrintServer/Printers/PrinterFactory.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Inspectron.Epson.PrintServer.PrintServices;
|
||||
|
||||
namespace Inspectron.Epson.PrintServer.Printers;
|
||||
|
||||
public class PrinterFactory:IPrinterFactory
|
||||
{
|
||||
public IPrinter CreatePrinterFromId(byte printerId, EpsonPrinter epsonPrinter)
|
||||
{
|
||||
switch (printerId)
|
||||
{
|
||||
case 0x13:
|
||||
return new TM_U220IITranslated(epsonPrinter);
|
||||
case 0x0D:
|
||||
return new TM_U220IITranslated(epsonPrinter);
|
||||
case 0x01:
|
||||
return new TM_T30IIITranslated(epsonPrinter);
|
||||
default:
|
||||
throw new NotSupportedException($"Printer with ID {printerId:X2} is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
47
Inspectron.Epson/PrintServer/Printers/TM-T30III.cs
Normal file
47
Inspectron.Epson/PrintServer/Printers/TM-T30III.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Inspectron.Epson.PrintServer.PrintServices;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Inspectron.Epson.PrintServer.Printers;
|
||||
|
||||
public class TM_T30III:IPrinter
|
||||
{
|
||||
private readonly EpsonPrinter _printer;
|
||||
|
||||
public TM_T30III(EpsonPrinter printer)
|
||||
{
|
||||
_printer = printer;
|
||||
}
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
await _printer.InitAsync();
|
||||
}
|
||||
|
||||
public async Task PrintImageAsync(string path)
|
||||
{
|
||||
await _printer.SetAbsolutePrintPosition(100);
|
||||
await _printer.LoadImageAsync(Path.Combine("logos", path), 384);
|
||||
await Task.Delay(100);
|
||||
//await _printer.PrintLoadedImage();
|
||||
await _printer.FeedLinesAsync(1);
|
||||
await _printer.SetAbsolutePrintPosition(0);
|
||||
}
|
||||
|
||||
public async Task SetFontSizeAsync(int fontSize)
|
||||
{
|
||||
await _printer.SetFontSizeAsync(fontSize, fontSize);
|
||||
}
|
||||
|
||||
public async Task PrintTextAsync(string text)
|
||||
{
|
||||
await _printer.PrintTextAsync(text);
|
||||
await _printer.FeedLinesAsync(5);
|
||||
var status = await _printer.GetPrinterStatusAsync();
|
||||
}
|
||||
|
||||
public async Task Cut()
|
||||
{
|
||||
await _printer.CutAsync();
|
||||
}
|
||||
}
|
||||
118
Inspectron.Epson/PrintServer/Printers/TM-T30IIITranslated.cs
Normal file
118
Inspectron.Epson/PrintServer/Printers/TM-T30IIITranslated.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System.Text.Json;
|
||||
using Inspectron.Epson.PrintServer.Printers.Utils;
|
||||
using Inspectron.Epson.PrintServer.Printers.Utils.FinalRecipe;
|
||||
using Inspectron.Epson.PrintServer.PrintServices;
|
||||
|
||||
namespace Inspectron.Epson.PrintServer.Printers;
|
||||
|
||||
public class TM_T30IIITranslated:IPrinter
|
||||
{
|
||||
private readonly EpsonPrinter _printer;
|
||||
|
||||
public TM_T30IIITranslated(EpsonPrinter printer)
|
||||
{
|
||||
_printer = printer;
|
||||
}
|
||||
|
||||
public Task InitAsync()
|
||||
{
|
||||
return _printer.InitAsync();
|
||||
}
|
||||
|
||||
public async Task PrintImageAsync(string path)
|
||||
{
|
||||
await _printer.SetAbsolutePrintPosition(100);
|
||||
await _printer.LoadImageAsync(Path.Combine("logos", path), 384);
|
||||
await Task.Delay(100);
|
||||
await _printer.FeedLinesAsync(1);
|
||||
await _printer.SetAbsolutePrintPosition(0);
|
||||
}
|
||||
|
||||
public async Task SetFontSizeAsync(int fontSize)
|
||||
{
|
||||
//await _printer.SetFontSizeAsync(fontSize, fontSize);
|
||||
}
|
||||
|
||||
public async Task PrintTextAsync(string text)
|
||||
{
|
||||
Receipt? receipt;
|
||||
try
|
||||
{
|
||||
receipt = JsonSerializer.Deserialize<Receipt>(text);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fallback to kitchen receipt
|
||||
await PrintKitchen(text);
|
||||
return;
|
||||
}
|
||||
var converter = new ReceiptConverter(lineWidth: 48, bigFontLineWidth: 24);
|
||||
var printCommands = converter.ConvertToPrintCommands(receipt);
|
||||
await _printer.FeedLinesAsync(1);
|
||||
await _printer.SetCustomLineSpacing(22);
|
||||
|
||||
foreach (var command in printCommands)
|
||||
{
|
||||
string attributes = "";
|
||||
if (command.IsBig) attributes += "[BIG]\n";
|
||||
if (command.IsBold) attributes += "[BOLD]\n";
|
||||
|
||||
Console.WriteLine($"{attributes}{command.Text}");
|
||||
|
||||
if (command.IsBig)
|
||||
{
|
||||
await _printer.SetFontSizeAsync(2, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _printer.SetFontSizeAsync(1, 1);
|
||||
}
|
||||
|
||||
await _printer.SetEmphasized(command.IsBold);
|
||||
|
||||
await _printer.PrintTextAsync(command.Text + "\n");
|
||||
|
||||
}
|
||||
|
||||
await _printer.SetDefaultLineSpacing();
|
||||
await _printer.FeedLinesAsync(10);
|
||||
await Task.Delay(200);
|
||||
}
|
||||
|
||||
private async Task PrintKitchen(string text)
|
||||
{
|
||||
ReceiptTranslator translator = new ReceiptTranslator();
|
||||
var ast = translator.ParseReceipt(text);
|
||||
KitchenReceiptPrinter printer = new KitchenReceiptPrinter(lineWidth: 33, 21);
|
||||
var commands = printer.ConvertToCommands((KitchenReceipt)ast);
|
||||
foreach (KitchenPrintCommand command in commands)
|
||||
{
|
||||
if (command.IsBig)
|
||||
{
|
||||
await _printer.SetFontSizeAsync(2,2);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _printer.SetFontSizeAsync(1, 1);
|
||||
}
|
||||
|
||||
|
||||
await Task.Delay(200);
|
||||
await _printer.SetRedColor(command.IsRed);
|
||||
await Task.Delay(200);
|
||||
await _printer.SetEmphasized(command.IsBold);
|
||||
await Task.Delay(200);
|
||||
await _printer.PrintTextAsync(command.Text + "\n");
|
||||
await Task.Delay(200);
|
||||
}
|
||||
|
||||
await _printer.FeedLinesAsync(10);
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
|
||||
public async Task Cut()
|
||||
{
|
||||
await _printer.CutAsync();
|
||||
await Task.Delay(200);
|
||||
}
|
||||
}
|
||||
51
Inspectron.Epson/PrintServer/Printers/TM-U220II.cs
Normal file
51
Inspectron.Epson/PrintServer/Printers/TM-U220II.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Inspectron.Epson.PrintServer.PrintServices;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Inspectron.Epson.PrintServer.Printers;
|
||||
|
||||
public class TM_U220II:IPrinter
|
||||
{
|
||||
private readonly EpsonPrinter _printer;
|
||||
|
||||
public TM_U220II(EpsonPrinter printer)
|
||||
{
|
||||
_printer = printer;
|
||||
}
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
await _printer.InitAsync();
|
||||
await Task.Delay(200);
|
||||
}
|
||||
|
||||
public Task PrintImageAsync(string path)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task SetFontSizeAsync(int fontSize)
|
||||
{
|
||||
await _printer.SetBiggerFontTM220(fontSize == 2);
|
||||
await Task.Delay(200);
|
||||
await _printer.SelectFont(EpsonCommands.PrinterFont.A);
|
||||
await Task.Delay(200);
|
||||
}
|
||||
|
||||
public async Task PrintTextAsync(string text)
|
||||
{
|
||||
await _printer.PrintTextAsync(text);
|
||||
var lines = text.Split('\n').Length;
|
||||
for (int i = 0; i < lines; i++)
|
||||
{
|
||||
await Task.Delay(200);
|
||||
}
|
||||
await _printer.FeedLinesAsync(5);
|
||||
await Task.Delay(200);
|
||||
}
|
||||
|
||||
public async Task Cut()
|
||||
{
|
||||
await _printer.CutAsync();
|
||||
await Task.Delay(200);
|
||||
}
|
||||
}
|
||||
67
Inspectron.Epson/PrintServer/Printers/TM-U220IITranslated.cs
Normal file
67
Inspectron.Epson/PrintServer/Printers/TM-U220IITranslated.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using Inspectron.Epson.PrintServer.Printers.Utils;
|
||||
using Inspectron.Epson.PrintServer.PrintServices;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Inspectron.Epson.PrintServer.Printers;
|
||||
|
||||
public class TM_U220IITranslated:IPrinter
|
||||
{
|
||||
private readonly EpsonPrinter _printer;
|
||||
|
||||
public TM_U220IITranslated(EpsonPrinter printer)
|
||||
{
|
||||
_printer = printer;
|
||||
}
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
await _printer.InitAsync();
|
||||
await Task.Delay(200);
|
||||
}
|
||||
|
||||
public Task PrintImageAsync(string path)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task SetFontSizeAsync(int fontSize)
|
||||
{
|
||||
//await _printer.SetBiggerFontTM220(fontSize == 2);
|
||||
//await Task.Delay(200);
|
||||
//await _printer.SelectFont(EpsonCommands.PrinterFont.A);
|
||||
//await Task.Delay(200);
|
||||
}
|
||||
|
||||
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((KitchenReceipt)ast);
|
||||
foreach (KitchenPrintCommand command in commands)
|
||||
{
|
||||
await _printer.SetBiggerFontTM220(command.IsBig);
|
||||
await Task.Delay(200);
|
||||
await _printer.SetRedColor(command.IsRed);
|
||||
await Task.Delay(200);
|
||||
await _printer.SetEmphasized(command.IsBold);
|
||||
await Task.Delay(200);
|
||||
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);
|
||||
}
|
||||
|
||||
public async Task Cut()
|
||||
{
|
||||
await _printer.CutAsync();
|
||||
await Task.Delay(200);
|
||||
}
|
||||
}
|
||||
78
Inspectron.Epson/PrintServer/Printers/Utils/ASTNode.cs
Normal file
78
Inspectron.Epson/PrintServer/Printers/Utils/ASTNode.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Inspectron.Epson.PrintServer.Printers.Utils;
|
||||
|
||||
public record ASTNode
|
||||
{
|
||||
public string Accept(IAccepter compiler)
|
||||
{
|
||||
return ((string)compiler.GetType()
|
||||
.GetMethod("Visit", BindingFlags.Public | BindingFlags.Instance, new[] { this.GetType() })!
|
||||
.Invoke(compiler, new[] { this })!)!;
|
||||
}
|
||||
|
||||
public static string PrintNode(ASTNode node)
|
||||
{
|
||||
// print type name and string representation of all its fields
|
||||
var type = node.GetType();
|
||||
var fields = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(type.Name);
|
||||
sb.Append("(");
|
||||
int i = 0;
|
||||
foreach (var field in fields)
|
||||
{
|
||||
if (i++ > 0) sb.Append(",");
|
||||
var value = field.GetValue(node);
|
||||
if (value is ASTNode valueNode)
|
||||
{
|
||||
sb.Append(PrintNode(valueNode));
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(value);
|
||||
}
|
||||
|
||||
// if field is list - print all its elements
|
||||
if (field.PropertyType.IsGenericType && field.PropertyType.GetGenericTypeDefinition() == typeof(List<>))
|
||||
{
|
||||
var list = (IList)field.GetValue(node);
|
||||
sb.Append("[");
|
||||
int j = 0;
|
||||
sb.AppendLine();
|
||||
foreach (var item in list)
|
||||
{
|
||||
if (j++ > 0)
|
||||
{
|
||||
sb.Append(",");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
if (item is ASTNode itemNode)
|
||||
{
|
||||
sb.Append(PrintNode(itemNode));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(item);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sb.Append("]");
|
||||
}
|
||||
}
|
||||
|
||||
sb.Append(")");
|
||||
return sb.ToString();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public interface IAccepter
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
namespace Inspectron.Epson.PrintServer.Printers.Utils.FinalRecipe;
|
||||
|
||||
public class Receipt
|
||||
{
|
||||
public string CompanyName { get; set; }
|
||||
public string Address1 { get; set; }
|
||||
public string Address2 { get; set; }
|
||||
public string Phone { get; set; }
|
||||
public string ReceiptNumber { get; set; }
|
||||
public DateTime DateTime { get; set; }
|
||||
public int Guests { get; set; }
|
||||
public List<ReceiptItem> Items { get; set; } = new List<ReceiptItem>();
|
||||
public decimal Total { get; set; }
|
||||
public string Currency { get; set; }
|
||||
public decimal? TotalInAlternateCurrency { get; set; }
|
||||
public string AlternateCurrency { get; set; }
|
||||
public string PaymentMethod { get; set; }
|
||||
public decimal PaymentAmount { get; set; }
|
||||
public List<TaxInfo> TaxBreakdown { get; set; } = new List<TaxInfo>();
|
||||
public string ServerName { get; set; }
|
||||
public string Terminal { get; set; }
|
||||
public string TableNumber { get; set; }
|
||||
public string VatNumber { get; set; }
|
||||
public string ThankYouMessage { get; set; }
|
||||
public string GoodbyeMessageLine1 { get; set; }
|
||||
public string GoodbyeMessageLine2 { get; set; }
|
||||
}
|
||||
|
||||
public class ReceiptItem
|
||||
{
|
||||
public int Quantity { get; set; }
|
||||
public string Description { get; set; }
|
||||
public decimal UnitPrice { get; set; }
|
||||
public decimal TotalPrice { get; set; }
|
||||
public string TaxCategory { get; set; }
|
||||
}
|
||||
|
||||
public class TaxInfo
|
||||
{
|
||||
public string Category { get; set; }
|
||||
public decimal Rate { get; set; }
|
||||
public decimal Gross { get; set; }
|
||||
public decimal Net { get; set; }
|
||||
public decimal TaxAmount { get; set; }
|
||||
public string Currency { get; set; }
|
||||
}
|
||||
|
||||
public class PrintCommand
|
||||
{
|
||||
public string Text { get; set; }
|
||||
public bool IsBig { get; set; }
|
||||
public bool IsBold { get; set; }
|
||||
|
||||
public PrintCommand(string text, bool isBig = false, bool isBold = false)
|
||||
{
|
||||
Text = text;
|
||||
IsBig = isBig;
|
||||
IsBold = isBold;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
namespace Inspectron.Epson.PrintServer.Printers.Utils.FinalRecipe;
|
||||
|
||||
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(Receipt receipt)
|
||||
{
|
||||
var commands = new List<PrintCommand>();
|
||||
|
||||
// Header - Company info
|
||||
commands.Add(new PrintCommand(Center(receipt.CompanyName, false)));
|
||||
commands.Add(new PrintCommand(Center(receipt.Address1, false)));
|
||||
commands.Add(new PrintCommand(Center(receipt.Address2, false)));
|
||||
commands.Add(new PrintCommand(Center(receipt.Phone, false)));
|
||||
commands.Add(new PrintCommand(""));
|
||||
commands.Add(new PrintCommand(""));
|
||||
|
||||
// Receipt info
|
||||
string receiptLine = $"Rechnung Nr. {receipt.ReceiptNumber}".PadRight(_lineWidth/2)+$"{receipt.DateTime:HH:mm dd.MM.yyyy}".PadLeft(_lineWidth/2);
|
||||
commands.Add(new PrintCommand(receiptLine,isBold:true));
|
||||
commands.Add(new PrintCommand($"Guests: {receipt.Guests}"));
|
||||
commands.Add(new PrintCommand(""));
|
||||
|
||||
// Items
|
||||
foreach (var item in receipt.Items)
|
||||
{
|
||||
string quantityDesc = $"{item.Quantity}x {item.Description}";
|
||||
string prices = $"{item.UnitPrice:F2}"+$"{item.TotalPrice:F2}".PadLeft(7)+$" {item.TaxCategory}";
|
||||
|
||||
// Calculate spacing to align prices to the right
|
||||
int spacesNeeded = _lineWidth - quantityDesc.Length - prices.Length;
|
||||
if (spacesNeeded < 1) spacesNeeded = 1;
|
||||
|
||||
string itemLine = quantityDesc + new string(' ', spacesNeeded) + prices;
|
||||
commands.Add(new PrintCommand(itemLine));
|
||||
}
|
||||
|
||||
commands.Add(new PrintCommand("")); // Reini
|
||||
commands.Add(new PrintCommand("---------".PadLeft(_lineWidth)));
|
||||
commands.Add(new PrintCommand("")); //Reini
|
||||
|
||||
// Total
|
||||
string totalLine = $"Summe: {receipt.Total:F2} {receipt.Currency}";
|
||||
commands.Add(new PrintCommand(Center(totalLine, true), true, true));
|
||||
commands.Add(new PrintCommand(""));
|
||||
|
||||
// Alternate currency
|
||||
if (receipt.TotalInAlternateCurrency.HasValue)
|
||||
{
|
||||
string altCurrencyLine = $"{receipt.TotalInAlternateCurrency:F2} {receipt.AlternateCurrency}";
|
||||
commands.Add(new PrintCommand(altCurrencyLine.PadLeft(_lineWidth)));
|
||||
commands.Add(new PrintCommand(""));
|
||||
}
|
||||
|
||||
// Payment method
|
||||
string paymentLine = $"{receipt.PaymentMethod}";
|
||||
string paymentAmount = $"{receipt.PaymentAmount:F2} {receipt.Currency}";
|
||||
int paymentSpaces = _lineWidth - paymentLine.Length - paymentAmount.Length;
|
||||
if (paymentSpaces < 1) paymentSpaces = 1;
|
||||
commands.Add(new PrintCommand(paymentLine + new string(' ', paymentSpaces) + paymentAmount,isBold:true));
|
||||
commands.Add(new PrintCommand(""));
|
||||
|
||||
// Tax breakdown
|
||||
foreach (var tax in receipt.TaxBreakdown)
|
||||
{
|
||||
string taxLine = "MwSt %".PadRight(_lineWidth/4)+" Brutto".PadRight(_lineWidth / 4) + " Netto".PadRight(_lineWidth / 4) + "MwSt".PadLeft(_lineWidth / 4);
|
||||
if (receipt.TaxBreakdown.IndexOf(tax) == 0)
|
||||
{
|
||||
commands.Add(new PrintCommand(taxLine));
|
||||
}
|
||||
|
||||
string taxDetail = ($"{tax.Category}:"+ $"{tax.Rate}%".PadLeft(5)).PadRight(_lineWidth / 4) + $"{tax.Gross:F2} {tax.Currency}".PadLeft(_lineWidth/4)+$"{tax.Net:F2} {tax.Currency}".PadLeft(_lineWidth/4)+$"{tax.TaxAmount:F2} {tax.Currency}".PadLeft(_lineWidth / 4);
|
||||
commands.Add(new PrintCommand(taxDetail));
|
||||
}
|
||||
commands.Add(new PrintCommand(""));
|
||||
|
||||
// Footer info
|
||||
//commands.Add(new PrintCommand(Center($"Bedient von: {receipt.ServerName}", false)));
|
||||
//commands.Add(new PrintCommand(Center($"Terminal: {receipt.Terminal}", false)));
|
||||
//commands.Add(new PrintCommand(Center($"Tisch: {receipt.TableNumber}", false)));
|
||||
commands.Add(new PrintCommand($"Bedient von:".PadLeft(_lineWidth / 2) + $" {receipt.ServerName}"));
|
||||
commands.Add(new PrintCommand($"Terminal:".PadLeft(_lineWidth / 2) + $" {receipt.Terminal}"));
|
||||
commands.Add(new PrintCommand($"Tisch:".PadLeft(_lineWidth / 2)+$" {receipt.TableNumber}"));
|
||||
commands.Add(new PrintCommand(""));
|
||||
commands.Add(new PrintCommand(""));
|
||||
|
||||
// VAT number
|
||||
commands.Add(new PrintCommand(Center(receipt.VatNumber, false)));
|
||||
|
||||
// Thank you message
|
||||
commands.Add(new PrintCommand(Center(receipt.ThankYouMessage, false)));
|
||||
commands.Add(new PrintCommand(Center(receipt.GoodbyeMessageLine1, false)));
|
||||
commands.Add(new PrintCommand(Center(receipt.GoodbyeMessageLine2, false)));
|
||||
|
||||
return commands;
|
||||
}
|
||||
|
||||
private string Center(string text, bool isBigFont)
|
||||
{
|
||||
int effectiveLineWidth = isBigFont ? _bigFontLineWidth : _lineWidth;
|
||||
|
||||
if (string.IsNullOrEmpty(text) || text.Length >= effectiveLineWidth)
|
||||
return text;
|
||||
|
||||
int totalPadding = effectiveLineWidth - text.Length;
|
||||
int leftPadding = totalPadding / 2;
|
||||
|
||||
return new string(' ', leftPadding) + text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Inspectron.Epson.PrintServer.Printers.Utils;
|
||||
|
||||
public class KitchenPrintCommand
|
||||
{
|
||||
public bool IsBig { get; set; }
|
||||
public bool IsBold { get; set; }
|
||||
public bool IsRed { get; set; }
|
||||
public string Text { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
namespace Inspectron.Epson.PrintServer.Printers.Utils;
|
||||
|
||||
public class KitchenReceiptPrinter
|
||||
{
|
||||
private readonly int _lineWidth;
|
||||
private readonly int _bigFontLineWidth;
|
||||
|
||||
public KitchenReceiptPrinter(int lineWidth = 42, int bigFontLineWidth = 21)
|
||||
{
|
||||
_lineWidth = lineWidth;
|
||||
_bigFontLineWidth = bigFontLineWidth;
|
||||
}
|
||||
|
||||
public List<KitchenPrintCommand> ConvertToCommands(KitchenReceipt receipt)
|
||||
{
|
||||
var commands = new List<KitchenPrintCommand>();
|
||||
|
||||
// Header: "Warme Küche" - Big, Bold, Red, Centered
|
||||
commands.Add(new KitchenPrintCommand
|
||||
{
|
||||
Text = Center(receipt.Location, isBigFont: true),
|
||||
IsBig = true,
|
||||
IsBold = true,
|
||||
IsRed = true
|
||||
});
|
||||
|
||||
commands.Add(Separator());
|
||||
|
||||
// Date and Owner info - Normal, Left-aligned
|
||||
commands.Add(new KitchenPrintCommand { Text = Center(receipt.Date,false) });
|
||||
var ownerLines = receipt.Owner.Split('\n');
|
||||
foreach (var line in ownerLines)
|
||||
{
|
||||
commands.Add(new KitchenPrintCommand { Text = Center(line, false) });
|
||||
}
|
||||
|
||||
// Table number - Big, Bold, Centered
|
||||
commands.Add(new KitchenPrintCommand
|
||||
{
|
||||
Text = Center($"Tisch: {receipt.Tisch}", isBigFont: true),
|
||||
IsBig = true,
|
||||
IsBold = true
|
||||
});
|
||||
|
||||
commands.Add(Separator());
|
||||
|
||||
// Items - Left-aligned
|
||||
foreach (var item in receipt.items)
|
||||
{
|
||||
if (item is KitchenProduct kp)
|
||||
{
|
||||
commands.Add(new KitchenPrintCommand
|
||||
{
|
||||
Text = $"{kp.Amount}x {item.Name}"
|
||||
});
|
||||
}
|
||||
if(item is KitchenGang kg)
|
||||
{
|
||||
commands.Add(new KitchenPrintCommand
|
||||
{
|
||||
IsRed = true,
|
||||
IsBig = true,
|
||||
IsBold = true,
|
||||
Text = Center(item.Name,true)
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
commands.Add(Separator());
|
||||
|
||||
return commands;
|
||||
}
|
||||
|
||||
private KitchenPrintCommand Separator()
|
||||
{
|
||||
return new KitchenPrintCommand
|
||||
{
|
||||
Text = new string('-', _lineWidth)
|
||||
};
|
||||
}
|
||||
|
||||
private string Center(string text, bool isBigFont)
|
||||
{
|
||||
int effectiveLineWidth = isBigFont ? _bigFontLineWidth : _lineWidth;
|
||||
|
||||
if (string.IsNullOrEmpty(text) || text.Length >= effectiveLineWidth)
|
||||
return text;
|
||||
|
||||
int totalPadding = effectiveLineWidth - text.Length;
|
||||
int leftPadding = totalPadding / 2;
|
||||
|
||||
return new string(' ', leftPadding) + text;
|
||||
}
|
||||
}
|
||||
206
Inspectron.Epson/PrintServer/Printers/Utils/ReceiptTranslator.cs
Normal file
206
Inspectron.Epson/PrintServer/Printers/Utils/ReceiptTranslator.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
namespace Inspectron.Epson.PrintServer.Printers.Utils;
|
||||
|
||||
|
||||
public record KitchenReceipt(string Location, string Date, string Owner, string Tisch, List<KitchenItem> items) :ASTNode;
|
||||
public record KitchenItem(string Name) : ASTNode;
|
||||
public record KitchenProduct(int Amount, string Name) : KitchenItem(Name);
|
||||
public record KitchenGang(string Name) : KitchenItem(Name);
|
||||
|
||||
public record FinalReceipt(string Location, string Phone, string URL, string Date, List<FinalReceiptItem> items, string total, string MWST, string Comment, string Thanks) : ASTNode;
|
||||
public record FinalReceiptItem(string Name, int Amount, string Price, string Total) : ASTNode;
|
||||
|
||||
|
||||
public class ReceiptTranslator
|
||||
{
|
||||
public ASTNode ParseReceipt(string receiptText)
|
||||
{
|
||||
// Determine receipt type based on content
|
||||
bool isFinalReceipt = receiptText.Contains("http") ||
|
||||
receiptText.Contains("+41") ||
|
||||
receiptText.Contains("Summe CHF") ||
|
||||
receiptText.Contains("MWST") ||
|
||||
receiptText.Contains("Thank you");
|
||||
|
||||
bool isKitchenReceipt = receiptText.Contains("TISCH:");
|
||||
|
||||
if (isKitchenReceipt && !isFinalReceipt)
|
||||
{
|
||||
return ParseKitchenReceipt(receiptText);
|
||||
}
|
||||
else if (isFinalReceipt)
|
||||
{
|
||||
return ParseFinalReceipt(receiptText);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default to kitchen receipt if unclear
|
||||
return ParseKitchenReceipt(receiptText);
|
||||
}
|
||||
}
|
||||
|
||||
public static FinalReceipt ParseFinalReceipt(string receiptText)
|
||||
{
|
||||
var lines = receiptText.Split('\n', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(l => l.Trim())
|
||||
.ToList();
|
||||
|
||||
// Extract header information
|
||||
string location = lines[0];
|
||||
string phone = lines[1];
|
||||
string url = lines[2];
|
||||
|
||||
// Find and extract date
|
||||
var dateLine = lines.FirstOrDefault(l => l.StartsWith("Datum:"));
|
||||
string date = dateLine?.Replace("Datum:", "").Trim() ?? "";
|
||||
|
||||
// Find total line
|
||||
var totalLine = lines.FirstOrDefault(l => l.Contains("Summe CHF :"));
|
||||
string total = totalLine?.Split(':').Last().Trim() ?? "";
|
||||
|
||||
// Find MWST line (comes after "TOTAL MWST" header)
|
||||
var mwstLineIndex = lines.FindIndex(l => l.StartsWith("TOTAL") && l.Contains("MWST"));
|
||||
string mwst = mwstLineIndex >= 0 && mwstLineIndex + 1 < lines.Count
|
||||
? lines[mwstLineIndex + 1].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault() ?? ""
|
||||
: "";
|
||||
|
||||
// Extract comment (line after MWST values)
|
||||
string comment = mwstLineIndex >= 0 && mwstLineIndex + 2 < lines.Count
|
||||
? lines[mwstLineIndex + 2]
|
||||
: "";
|
||||
|
||||
// Extract thank you message (last non-separator line)
|
||||
string thanks = lines.LastOrDefault(l => !l.Contains("--")) ?? "";
|
||||
|
||||
// Parse items
|
||||
var items = new List<FinalReceiptItem>();
|
||||
var startIndex = lines.FindIndex(l => l.StartsWith("Datum:")) + 2; // Skip date and separator
|
||||
var endIndex = lines.FindIndex(l => l.Contains("Summe CHF"));
|
||||
|
||||
for (int i = startIndex; i < endIndex; i++)
|
||||
{
|
||||
var line = lines[i];
|
||||
|
||||
// Skip separator lines and empty lines
|
||||
if (line.Contains("---") || string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
|
||||
// Check if line contains item data (has * separator)
|
||||
if (line.Contains("*"))
|
||||
{
|
||||
// Parse format: "Name Amount * Price Total"
|
||||
var parts = line.Split('*');
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
var leftPart = parts[0].Trim();
|
||||
var rightPart = parts[1].Trim();
|
||||
|
||||
// Extract name and amount from left part
|
||||
var leftTokens = leftPart.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var amount = int.Parse(leftTokens.Last());
|
||||
var name = string.Join(" ", leftTokens.Take(leftTokens.Length - 1));
|
||||
|
||||
// Extract price and total from right part
|
||||
var rightTokens = rightPart.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var price = rightTokens.Length > 0 ? rightTokens[0] : "";
|
||||
var itemTotal = rightTokens.Length > 1 ? rightTokens[1] : "";
|
||||
|
||||
items.Add(new FinalReceiptItem(name, amount, price, itemTotal));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new FinalReceipt(location, phone, url, date, items, total, mwst, comment, thanks);
|
||||
}
|
||||
|
||||
public static KitchenReceipt ParseKitchenReceipt(string receiptText)
|
||||
{
|
||||
var lines = receiptText.Split('\n', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(l => l.Trim())
|
||||
.ToList();
|
||||
|
||||
int currentIndex = 0;
|
||||
|
||||
// Skip optional "*** KOPIE ***" header
|
||||
if (lines[currentIndex].Contains("***"))
|
||||
{
|
||||
currentIndex++;
|
||||
}
|
||||
|
||||
// Extract location (can be any phrase)
|
||||
string location = lines[currentIndex];
|
||||
currentIndex++;
|
||||
|
||||
// Skip separator line
|
||||
while (currentIndex < lines.Count && lines[currentIndex].Contains("---"))
|
||||
{
|
||||
currentIndex++;
|
||||
}
|
||||
|
||||
// Extract date line
|
||||
string date = lines[currentIndex];
|
||||
currentIndex++;
|
||||
|
||||
// Extract owner
|
||||
string owner = "";
|
||||
do
|
||||
{
|
||||
if (owner != "")
|
||||
{
|
||||
owner += "/n";
|
||||
}
|
||||
owner += lines[currentIndex];
|
||||
currentIndex++;
|
||||
} while (!lines[currentIndex].ToLower().Contains("tisch:"));
|
||||
|
||||
|
||||
// Extract table (extract text after "TISCH:")
|
||||
string tisch = lines[currentIndex].Replace("TISCH:", "").Trim();
|
||||
currentIndex++;
|
||||
|
||||
// Skip empty lines and separators until we reach items
|
||||
while (currentIndex < lines.Count &&
|
||||
(string.IsNullOrWhiteSpace(lines[currentIndex]) || lines[currentIndex].Contains("-")))
|
||||
{
|
||||
currentIndex++;
|
||||
}
|
||||
|
||||
// Parse items
|
||||
var items = new List<KitchenItem>();
|
||||
while (currentIndex < lines.Count)
|
||||
{
|
||||
var line = lines[currentIndex];
|
||||
|
||||
// Stop at separator or end
|
||||
if (line.Contains("---") || string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (line.ToLower().Contains(". gang"))
|
||||
{
|
||||
items.Add(new KitchenGang(line.Trim()));
|
||||
currentIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse format: "1x Espresso" or " 1x Espresso"
|
||||
var trimmedLine = line.Trim();
|
||||
if (trimmedLine.Contains("x"))
|
||||
{
|
||||
var parts = trimmedLine.Split('x', 2);
|
||||
if (parts.Length == 2 && int.TryParse(parts[0].Trim(), out int amount))
|
||||
{
|
||||
var itemName = parts[1].Trim();
|
||||
items.Add(new KitchenProduct(amount, itemName));
|
||||
}
|
||||
}
|
||||
|
||||
currentIndex++;
|
||||
}
|
||||
|
||||
return new KitchenReceipt(location, date, owner, tisch, items);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Inspectron.Epson.PrintServer.WorkAreaSources;
|
||||
|
||||
public interface IWorkAreasSource
|
||||
{
|
||||
public Task<List<WorkArea>> GetWorkAreasAsync();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Net.Http.Json;
|
||||
using Inspectron.Epson.PrintServer.ConfigurationSources;
|
||||
|
||||
namespace Inspectron.Epson.PrintServer.WorkAreaSources;
|
||||
|
||||
public class JamesWorkAreaSource: IWorkAreasSource
|
||||
{
|
||||
private readonly EpsonPrintServiceConfiguration _configuration;
|
||||
|
||||
public JamesWorkAreaSource(EpsonPrintServiceConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public async Task<List<WorkArea>> GetWorkAreasAsync()
|
||||
{
|
||||
var url = $@"https://api.stage.gastrojames.ch/api/Restaurant/{_configuration.RestaurantId}/work-areas";
|
||||
|
||||
HttpClient client = new();
|
||||
var response = await client.GetFromJsonAsync<List<WorkArea>>(url);
|
||||
response.Add(new WorkArea()
|
||||
{
|
||||
Id = "print-receipt",
|
||||
Name = "Receipt"
|
||||
});
|
||||
return response!;
|
||||
}
|
||||
}
|
||||
7
Inspectron.Epson/PrintServer/WorkAreaSources/WorkArea.cs
Normal file
7
Inspectron.Epson/PrintServer/WorkAreaSources/WorkArea.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Inspectron.Epson.PrintServer.WorkAreaSources;
|
||||
|
||||
public class WorkArea
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
}
|
||||
Reference in New Issue
Block a user