diff --git a/EpsonPrintService/PrintServerBootstrapper.cs b/EpsonPrintService/PrintServerBootstrapper.cs index b6d35fb..043cd7d 100644 --- a/EpsonPrintService/PrintServerBootstrapper.cs +++ b/EpsonPrintService/PrintServerBootstrapper.cs @@ -31,10 +31,32 @@ public class PrintServerBootstrapper { _kernel = new StandardKernel(); - _kernel.Bind().To().InSingletonScope(); _kernel.Bind().ToConstant(new HttpClient { Timeout = TimeSpan.FromSeconds(30) }).InSingletonScope(); _kernel.Bind().ToConstant(_config); - _kernel.Bind().To(); + + if (_config.EmulationMode) + { + var emulationConfigs = new Dictionary + { + ["127.0.0.2"] = ("emulation/u220.html", 0x13, 258), + ["127.0.0.3"] = ("emulation/t30.html", 0x01, 384), + }; + + var discovery = new PreconfiguredDiscoveryService(); + discovery.AddPrinter("127.0.0.2", "TM-U220II"); + discovery.AddPrinter("127.0.0.3", "TM-T30III"); + _kernel.Bind().ToConstant(discovery).InSingletonScope(); + + _kernel.Bind().ToMethod(ctx => + new EmulationPrinterFactory(emulationConfigs, ctx.Kernel.Get())); + + Console.WriteLine("Emulation mode enabled: 127.0.0.2 (TM-U220II), 127.0.0.3 (TM-T30III)"); + } + else + { + _kernel.Bind().To().InSingletonScope(); + _kernel.Bind().To(); + } _kernel.Bind().To().InSingletonScope(); _kernel.Bind().To(); _kernel.Bind().ToMethod(ctx => diff --git a/EpsonPrintService/Program.cs b/EpsonPrintService/Program.cs index 13e1e6e..d1d1a10 100644 --- a/EpsonPrintService/Program.cs +++ b/EpsonPrintService/Program.cs @@ -3,6 +3,7 @@ using Inspectron.Epson.PrintServer.ConfigurationSources; // Load configuration (may be absent on first boot) var configResult = ConfigurationLoader.TryLoadFromFile(); +configResult.Config.EmulationMode = true; EpsonPrintServiceConfiguration? activeConfig = null; if (configResult.Success) diff --git a/Inspectron.Epson/EmulationPrinterProxy.cs b/Inspectron.Epson/EmulationPrinterProxy.cs new file mode 100644 index 0000000..c26fc4c --- /dev/null +++ b/Inspectron.Epson/EmulationPrinterProxy.cs @@ -0,0 +1,79 @@ +using Microsoft.Extensions.Logging; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; + +namespace Inspectron.Epson; + +/// +/// IEpsonPrinter proxy that defers HtmlPrinter creation until ConnectAsync is called, +/// selecting the correct configuration (output file, printer ID, paper width) based on the IP address. +/// +public class EmulationPrinterProxy : IEpsonPrinter +{ + private readonly Dictionary _printerConfigs; + private readonly ILogger? _logger; + private HtmlPrinter? _inner; + + public EmulationPrinterProxy( + Dictionary printerConfigs, + ILogger? logger = null) + { + _printerConfigs = printerConfigs; + _logger = logger; + } + + public bool IsConnected => _inner?.IsConnected ?? false; + + public Task ConnectAsync(string ip, int port = 9100, int timeoutSeconds = 10) + { + if (!_printerConfigs.TryGetValue(ip, out var config)) + throw new EpsonConnectionException($"No emulation configuration for IP {ip}"); + + _logger?.LogInformation("EmulationPrinterProxy: Creating HtmlPrinter for {IP} (id=0x{Id:X2}, width={Width}px, output={Path})", + ip, config.PrinterId, config.PaperWidth, config.OutputPath); + + _inner = new HtmlPrinter(config.OutputPath, config.PaperWidth, _logger, config.PrinterId); + return _inner.ConnectAsync(ip, port, timeoutSeconds); + } + + private HtmlPrinter Inner => _inner ?? throw new EpsonConnectionException("Not connected. Call ConnectAsync first."); + + public void Disconnect() => Inner.Disconnect(); + public Task GetPrinterStatusAsync() => Inner.GetPrinterStatusAsync(); + public Task GetOfflineStatusAsync() => Inner.GetOfflineStatusAsync(); + public Task GetErrorStatusAsync() => Inner.GetErrorStatusAsync(); + public Task GetPaperSensorStatusAsync() => Inner.GetPaperSensorStatusAsync(); + public Task GetTM220StatusAsync() => Inner.GetTM220StatusAsync(); + public Task GetPrinterIdAsync() => Inner.GetPrinterIdAsync(); + public Task GetOverallStatusAsync() => Inner.GetOverallStatusAsync(); + public Task InitAsync() => Inner.InitAsync(); + public Task SetQuadrupleMode(bool enable) => Inner.SetQuadrupleMode(enable); + public Task SetRedColor(bool enable) => Inner.SetRedColor(enable); + public Task SetEmphasized(bool enable) => Inner.SetEmphasized(enable); + public Task SelectFont(EpsonCommands.PrinterFont font) => Inner.SelectFont(font); + public Task SetBiggerFontTM220(bool biggerWidth, bool biggerHeight = false, bool secondaryFont = true) => Inner.SetBiggerFontTM220(biggerWidth, biggerHeight, secondaryFont); + public Task PrintTextAsync(string text) => Inner.PrintTextAsync(text); + public Task SetFontSizeAsync(int width, int height) => Inner.SetFontSizeAsync(width, height); + public Task PrintTextAndCutAsync(string text, int feedLines = 5) => Inner.PrintTextAndCutAsync(text, feedLines); + public Task PrintBuffer() => Inner.PrintBuffer(); + public Task FeedLinesAsync(int lines) => Inner.FeedLinesAsync(lines); + public Task CutAsync(bool fullCut = true) => Inner.CutAsync(fullCut); + public Task LoadImageAsync(string imagePath, int maxWidth = 384) => Inner.LoadImageAsync(imagePath, maxWidth); + public Task LoadImageAsync(Stream imageStream, int maxWidth = 384) => Inner.LoadImageAsync(imageStream, maxWidth); + public Task LoadImageAsync(Image image, int maxWidth = 384) => Inner.LoadImageAsync(image, maxWidth); + public Task PrintLoadedImage() => Inner.PrintLoadedImage(); + public Task PrintImageBitModeAsync(string imagePath, EpsonCommands.BitImageMode mode = EpsonCommands.BitImageMode.SingleDensity8Dot, int maxWidth = 10) => Inner.PrintImageBitModeAsync(imagePath, mode, maxWidth); + public Task PrintImageBitModeAsync(Stream imageStream, EpsonCommands.BitImageMode mode = EpsonCommands.BitImageMode.DoubleDensity24Dot, int maxWidth = 384) => Inner.PrintImageBitModeAsync(imageStream, mode, maxWidth); + public Task PrintImageBitModeAsync(Image image, EpsonCommands.BitImageMode mode, int maxWidth) => Inner.PrintImageBitModeAsync(image, mode, maxWidth); + public Task SendRawCommandAsync(byte[] command) => Inner.SendRawCommandAsync(command); + public Task SetAbsolutePrintPosition(int x) => Inner.SetAbsolutePrintPosition(x); + public Task SetDefaultLineSpacing() => Inner.SetDefaultLineSpacing(); + public Task SetCustomLineSpacing(int spacing = 24) => Inner.SetCustomLineSpacing(spacing); + + public ValueTask DisposeAsync() + { + if (_inner != null) + return _inner.DisposeAsync(); + return ValueTask.CompletedTask; + } +} diff --git a/Inspectron.Epson/PrintServer/ConfigurationSources/EpsonPrintServiceConfiguration.cs b/Inspectron.Epson/PrintServer/ConfigurationSources/EpsonPrintServiceConfiguration.cs index 21841bc..0454d32 100644 --- a/Inspectron.Epson/PrintServer/ConfigurationSources/EpsonPrintServiceConfiguration.cs +++ b/Inspectron.Epson/PrintServer/ConfigurationSources/EpsonPrintServiceConfiguration.cs @@ -7,6 +7,5 @@ public class EpsonPrintServiceConfiguration public string RestaurantId { get; set; } public string ApiKey { get; set; } public string? ApiUrl { get; set; } - - + public bool EmulationMode { get; set; } } \ No newline at end of file diff --git a/Inspectron.Epson/PrintServer/Printers/EmulationPrinterFactory.cs b/Inspectron.Epson/PrintServer/Printers/EmulationPrinterFactory.cs new file mode 100644 index 0000000..c3c4cc2 --- /dev/null +++ b/Inspectron.Epson/PrintServer/Printers/EmulationPrinterFactory.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.Logging; + +namespace Inspectron.Epson.PrintServer.Printers; + +public class EmulationPrinterFactory : IPrinterFactory +{ + private readonly Dictionary _printerConfigs; + private readonly ILogger _logger; + + public EmulationPrinterFactory( + Dictionary printerConfigs, + ILogger logger) + { + _printerConfigs = printerConfigs; + _logger = logger; + } + + public IEpsonPrinter CreatePrinter() + { + return new EmulationPrinterProxy(_printerConfigs, _logger); + } +}