removed md templates

cleanup
This commit is contained in:
EugeneTes
2026-02-03 11:20:01 +01:00
parent dca0f259ac
commit 46b648d753
195 changed files with 128 additions and 82694 deletions

225
CLAUDE.md
View File

@@ -4,19 +4,15 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project Overview
This is a C# .NET 8.0 solution for Epson TM-m30III thermal receipt printer integration. The solution consists of a core SDK library (Inspectron.Epson) and multiple applications for production printing, configuration, testing, and discovery.
This is a C# .NET 8.0 solution for Epson thermal receipt printer integration. The solution consists of a core SDK library (Inspectron.Epson) and a production print service application.
## Solution Structure
### Core Projects
### Projects
- **Inspectron.Epson** - Core SDK library providing printer communication, status monitoring, and ESC/POS commands
- **EpsonPrintService** - Production console application with SignalR-based remote job source and systemd service support
- **ConfigurationPannel** - ASP.NET Razor Pages web application for printer configuration with embedded print server
- **Discovery** - Printer network discovery utilities (SLP, ENPC, mDNS)
- **EpsonTest** - Test project for SDK functionality
- **TestClient** - SDK usage examples and testing
- **SendPrintJob** - Direct print job submission utility
- **Inspectron.Epson** - Core SDK library providing printer communication, status monitoring, ESC/POS commands, print queue management, and receipt conversion
- **EpsonPrintService** - Production console application with SignalR-based remote job source, printer discovery, and systemd service support
- **EpsonTest** - Test/example project for SDK functionality and receipt conversion
## Common Build Commands
@@ -27,14 +23,12 @@ dotnet build Inspectron.Epson.slnx
# Build specific project
dotnet build Inspectron.Epson/Inspectron.Epson.csproj
dotnet build EpsonPrintService/EpsonPrintService.csproj
dotnet build ConfigurationPannel/ConfigurationPannel.csproj
# Run projects
# Run print service
dotnet run --project EpsonPrintService/EpsonPrintService.csproj
dotnet run --project ConfigurationPannel/ConfigurationPannel.csproj
# Run tests
dotnet test EpsonTest/EpsonTest.csproj
# Run tests/examples
dotnet run --project EpsonTest/EpsonTest.csproj
# Clean build artifacts
dotnet clean
@@ -45,95 +39,127 @@ dotnet clean
### Three-Layer Architecture
1. **SDK Layer (Inspectron.Epson)**
- `EpsonPrinter`: Main facade for printer operations (TCP/IP, async/await)
- `IEpsonPrinter` / `EpsonPrinter`: Main interface and TCP/IP implementation for printer operations (async/await)
- `EpsonCommands`: Static ESC/POS command definitions
- `EpsonImageConverter`: Image processing with Floyd-Steinberg dithering
- `EpsonPrinterDiscovery`: Network printer discovery (SLP, ENPC)
- `IDiscoveryService` / `DiscoveryService`: Network printer discovery (SLP, ENPC)
- `PreconfiguredDiscoveryService`: Preconfigured printer list (no network scan)
- `BonjourDiscovery`: mDNS-based printer discovery
- `HtmlPrinter`: HTML output implementation for testing without hardware
- Status classes: PrinterStatus, OfflineStatus, ErrorStatus, PaperSensorStatus, OverallStatus
- Exception hierarchy: EpsonPrinterException EpsonConnectionException, EpsonCommandException
- Exception hierarchy: EpsonPrinterException -> EpsonConnectionException, EpsonCommandException
2. **Print Server Layer (PrintServer/ namespace in Inspectron.Epson)**
- `PrintServer`: Manager for printer queues, coordinates multiple printers by IP
2. **Queue Layer (Inspectron.Epson.Queue namespace)**
- `PrintServer`: Manager for per-printer queues with read/write lock coordination between printing and discovery
- `PrinterQueue`: Per-printer queue with retry logic (ConcurrentQueue + priority ConcurrentStack)
- `IPrintService`: Print workflow interface (PrintAsync returns `PrintResult`)
- `PrintJob`: Job model with IP, Document, QueuedAt, RetryCount
- `PrintResult`: Success/Failure result with `PrintErrorType`
- `IJobStatusReporter`: Interface for reporting job status updates
3. **PrintServer Layer (Inspectron.Epson.PrintServer namespace)**
- `PrintLoop`: Orchestrator connecting job sources to print server
- `IPrintJobSource`: Strategy pattern for job sources (SignalRPrintJobSource, SingleJobSource, NoOpPrintJobSource)
- `IPrintService`: Print workflow orchestration (EpsonPrintService implementation)
- `IPrinter`: Printer model abstraction (TM-T30III, TM-U220II)
- `PrinterFactory`: Factory pattern for creating printer instances by model ID
- **JobSources/**: `IPrintJobSource` strategy pattern (SignalRPrintJobSource, SingleJobSource)
- **PrintServices/**: `IPrintService` implementation (`EpsonPrintService`), `IAssignedPrinterRepository`, `IPrinterConfigurationSource`
- **ConfigurationSources/**: `EpsonPrintServiceConfiguration`, `FixedConfigurationSource`
- **Printers/**: `IPrinterFactory` (creates `IEpsonPrinter`), `IWrapperPrinterFactory` / `PrinterWrapperFactory` (creates `IPrinter` adapter by model ID)
- **Printers/Utils/**: `PrintCommand` model, `IReceiptConverter` / `IReceiptConverterFactory` with receipt type implementations
- **WorkAreaSources/**: `IWorkAreasSource`, `JamesWorkAreaSource`
- **DiscoveredPrintersReceiver/**: `IDiscoveredPrintersReceiver`, `JamesDiscoveredPrintersReceiver`
3. **Application Layer**
- EpsonPrintService: Production deployment with Ninject DI, SignalR remote jobs, systemd service
- ConfigurationPannel: ASP.NET Core with Microsoft.Extensions.DI, cookie authentication, hosted service pattern
4. **Application Layer (EpsonPrintService)**
- Production deployment with Ninject DI, SignalR remote jobs, printer discovery, heartbeat, systemd service
### Dependency Injection Patterns
### Receipt Conversion System
The codebase uses TWO different DI containers:
Receipt content is converted from JSON to `PrintCommand` lists via `IReceiptConverterFactory`:
- **KitchenReceipt** - Kitchen order tickets
- **FinalReceipt** - Final customer receipts
- **InvoiceReceipt** - Invoice receipts
- **BarReceipt** - Bar order tickets
- **OrderItems** - Order item lists
`PrintCommand` properties: Text, IsBig, IsTall, IsBold, IsRed, SetLineSpacing, IsCut
### Printer Model Adapters
`IPrinter` implementations wrap `IEpsonPrinter` with model-specific behavior:
- `TM-T30III` / `TM-T30IIITranslated` - Thermal receipt printer (384px image width)
- `TM-U220II` / `TM-U220IITranslated` - Dot matrix receipt printer (narrower image width)
Created via `IWrapperPrinterFactory.CreatePrinterFromId(modelIdByte, epsonPrinter)`.
### Dependency Injection (Ninject)
**Ninject (EpsonPrintService console app):**
```csharp
StandardKernel kernel = new();
kernel.Bind<IPrinterFactory>().To<EpsonPrinterFactory>();
kernel.Bind<IWrapperPrinterFactory>().To<PrinterWrapperFactory>().InSingletonScope();
kernel.Bind<IPrintJobSource, SignalRPrintJobSource>().To<SignalRPrintJobSource>().InSingletonScope();
kernel.Bind<IPrintService>().To<EpsonPrintService>();
kernel.Bind<IAssignedPrinterRepository>().ToConstant(config);
kernel.Bind<IPrinterFactory>().To<PrinterFactory>().InSingletonScope();
kernel.Bind<IPrinterConfigurationSource>().To<FixedConfigurationSource>();
kernel.Bind<IReceiptConverterFactory>().To<ReceiptConverterFactory>().InSingletonScope();
kernel.Bind<IJobStatusReporter>().To<NullJobStatusReporter>();
kernel.Bind<IDiscoveryService>().ToConstant(discovery);
kernel.Bind<IDiscoveredPrintersReceiver>().To<JamesDiscoveredPrintersReceiver>().InSingletonScope();
kernel.Bind<PrintServer>().ToSelf().InSingletonScope();
```
**Microsoft.Extensions.DependencyInjection (ConfigurationPannel ASP.NET):**
```csharp
builder.Services.AddSingleton<UserService>();
builder.Services.AddSingleton<ConfigurationManager>();
builder.Services.AddHostedService<PrintServerHostedService>();
```
When adding new services, match the DI pattern of the project you're working in.
### Key Interfaces and Abstractions
- `IEpsonPrinter`: Low-level printer operations (connect, status, ESC/POS commands, image printing)
- `IPrinter`: Printer model adapter (InitAsync, PrintImageAsync, SetFontSizeAsync, PrintAsync, Cut)
- `IPrintJobSource`: Pluggable job sources (async via Channel<PrintJob>)
- `IPrintService`: Print workflow execution (PrintAsync returns bool for success/failure)
- `IAssignedPrinterRepository`: Maps work area IDs to printer IPs (GetAssignedPrinter)
- `IPrinterConfigurationSource`: Provides printer configuration (address, font size, logo)
- `IPrinter`: Printer model-specific operations (InitAsync, PrintImageAsync, SetFontSizeAsync, PrintTextAsync, Cut)
- `IPrinterFactory`: Creates IPrinter from model ID byte (0x01 = TM-T30III, 0x13 = TM-U220II)
- `IPrintService`: Print workflow execution (PrintAsync returns `PrintResult`)
- `IPrinterFactory`: Creates `IEpsonPrinter` instances (EpsonPrinterFactory, HtmlPrinterFactory)
- `IWrapperPrinterFactory`: Creates `IPrinter` adapter from model ID byte
- `IPrinterConfigurationSource`: Provides printer configuration
- `IReceiptConverter` / `IReceiptConverterFactory`: Converts JSON receipt content to `PrintCommand` list
- `IDiscoveryService`: Printer network discovery
- `IDiscoveredPrintersReceiver`: Receives and processes discovered printers
- `IJobStatusReporter`: Reports job status updates
### Print Job Flow
```
PrintLoop.StartAsync()
IPrintJobSource.GetNextJobAsync() [blocks until job available]
→ IAssignedPrinterRepository.GetAssignedPrinter(job.AreaId) [returns printer IP]
PrintServer.SubmitJob(printerIp, job)
→ PrinterQueue.Enqueue(job) [per-printer queue]
→ ProcessQueueAsync() [background loop]
IPrintService.PrintAsync(printerIp, job)
→ Get configuration, connect to printer, detect model
→ IPrinterFactory.CreatePrinterFromId()
Initialize printer, print logo (if configured), set font size
→ Print text content, cut paper
→ Return true/false for success
→ On failure: retry up to 3 times via priority queue
-> IPrintJobSource.GetNextJobAsync() [blocks until job available]
-> PrintServer.SubmitJob(job.IP, job)
-> PrinterQueue.Enqueue(job) [per-printer queue]
-> ProcessQueueAsync() [background loop]
-> IPrintService.PrintAsync(printerIp, job)
-> IPrinterFactory.CreatePrinter() [creates IEpsonPrinter]
-> Connect, get printer ID, check status
-> IWrapperPrinterFactory.CreatePrinterFromId() [creates IPrinter adapter]
-> Initialize printer, download & cache logo (if URL provided)
-> IReceiptConverterFactory.Create(receiptType, printerId)
-> Convert JSON content to PrintCommand list
-> printerAdapter.PrintAsync(commands), cut paper
-> Return PrintResult.Ok() or PrintResult.Fail(errorType)
-> On failure: retry up to 3 times via priority queue
```
### Configuration System
**EpsonPrintServiceConfiguration** (config.json):
```json
{
"GroupId": "67e534f8e86e816689323023",
"RestaurantId": "63e37295bd6f26dbd36164c0",
"PrinterConfigurations": {
"192.168.1.100": {
"Address": "192.168.1.100:9100",
"FontSize": 2,
"LogoFilename": "logo.png",
"AreaId": "kitchen"
}
}
}
Configuration is loaded from a base64-encoded `config.txt` file containing `apiUrl;restaurantId;apiKey`:
```
# config.txt contains a single base64-encoded line
# Decoded format: apiUrl;restaurantId;apiKey
# Example decoded: https://api.gastrojames.ch;63e37295bd6f26dbd36164c0;yourApiKeyHere
```
This class implements BOTH `IPrinterConfigurationSource` and `IAssignedPrinterRepository` for dual roles.
`ConfigurationPaths` resolves the config path:
- **Windows**: local `config.txt`
- **Linux**: `/root/epsonprintservice/config.txt`
Printers are registered via `PreconfiguredDiscoveryService` with hardcoded IPs in `Program.cs`, or dynamically via `PrinterDiscoveryBackgroundTask`.
### PrintServer Read/Write Lock
`PrintServer` uses a readers-writer lock pattern to coordinate printing with discovery:
- **Print operations** acquire a read lock (`EnterPrintLock` / `ExitPrintLock`) - multiple prints run concurrently
- **Discovery operations** acquire a write lock (`EnterDiscoveryLockAsync` / `ExitDiscoveryLock`) - blocks until all prints complete, prevents new prints during discovery
### Printer Communication
@@ -149,9 +175,17 @@ This class implements BOTH `IPrinterConfigurationSource` and `IAssignedPrinterRe
1. Get printer ID byte via `EpsonPrinter.GetPrinterIdAsync()` (GS I 1 command)
2. Create new class implementing `IPrinter` in `Inspectron.Epson/PrintServer/Printers/`
3. Implement required methods: InitAsync, PrintImageAsync, SetFontSizeAsync, PrintTextAsync, Cut
4. Add case to `PrinterFactory.CreatePrinterFromId()` with the printer ID byte
5. Test with actual hardware - different models have different image widths and positioning
3. Implement required methods: InitAsync, PrintImageAsync, SetFontSizeAsync, PrintAsync, Cut
4. Add case to `PrinterWrapperFactory.CreatePrinterFromId()` with the printer ID byte
5. Add receipt converter support in `ReceiptConverterFactory` if needed
6. Test with actual hardware - different models have different image widths and positioning
## Adding New Receipt Types
1. Create a new folder under `Inspectron.Epson/PrintServer/Printers/Utils/`
2. Add Models.cs with the receipt data model
3. Create a converter implementing `IReceiptConverter` (returns `List<PrintCommand>`)
4. Register the new receipt type in `ReceiptConverterFactory`
## Testing Printer Connectivity
@@ -172,7 +206,7 @@ Console.WriteLine(report);
### Systemd Service (Linux)
EpsonPrintService includes systemd service file (`epson.service`):
EpsonPrintService includes systemd service files (`epson.service`, `print_server.service`):
```bash
# Copy files to deployment directory
cp -r EpsonPrintService/bin/Debug/net8.0/* /home/pi/epson_service/
@@ -187,13 +221,7 @@ sudo systemctl start epson.service
sudo systemctl status epson.service
```
### ASP.NET Hosted Service
ConfigurationPannel runs print server as `IHostedService`:
```bash
dotnet run --project ConfigurationPannel/ConfigurationPannel.csproj
```
Access web UI at http://localhost:5000 (default login credentials in users.json).
Deployment scripts included: `start.sh`, `postinst`, `prem`, `uninstall.sh`.
## Important Implementation Notes
@@ -215,12 +243,20 @@ When printing images via `EpsonPrinter.LoadImageAsync()`:
5. Pack pixels to byte-aligned format
6. Generate column format for bit-image mode (ESC * 33)
### Logo Caching
`EpsonPrintService` downloads logos from URLs provided in print jobs:
- Logos are cached locally in a `logos/` directory under the app base directory
- Filename is base64-encoded to avoid path issues
- Subsequent prints skip the download if the cached file exists
### SignalR Job Source
`SignalRPrintJobSource` connects to `https://api.gastrojames.ch/hubs/internal`:
- Authentication: Bearer token from configuration
- Group: Joins via GroupId from configuration
- Message: "PrintJob" with { WorkingAreaId, Content }
`SignalRPrintJobSource` connects to `{ApiUrl}/hubs/printer-servers`:
- Uses `InfiniteRetryPolicy` with exponential backoff (capped at 30s) for automatic reconnection
- Retries initial connection in a loop on failure
- Joins group by RestaurantId from configuration
- Message: "PrintJob" with { PrinterIp, LogoUrl, ReceiptType, Content }
- Uses Channel<PrintJob> for async producer-consumer pattern
### Font Sizing
@@ -236,7 +272,7 @@ Font magnification is 1-8x for both width and height:
- Verify IP address with `ping <printer-ip>`
- Check printer is on same network/VLAN
- Ensure port 9100 is not blocked by firewall
- Try printer discovery: run Discovery project
- Check `PreconfiguredDiscoveryService` entries in `Program.cs`
### "Cover open" or "Paper end" errors
- Check `OverallStatus.Recommendations` for specific issues
@@ -244,15 +280,10 @@ Font magnification is 1-8x for both width and height:
### Image not printing or garbled
- Verify image width matches printer specification (384px for TM-T30III)
- Check image file path is accessible
- Check image file path or URL is accessible
- Ensure printer model supports raster graphics (TM-T30III does, older models may not)
### SignalR connection failures
- Check GroupId and RestaurantId in config.json
- Verify network connectivity to api.gastrojames.ch
- Check bearer token expiration
### ConfigurationPannel login issues
- Default credentials in users.json (BCrypt hashed)
- Cookie expiration: 8 hours with sliding expiration
- Authentication uses CookieAuthenticationDefaults scheme
- Check RestaurantId and ApiKey in config.txt (base64-encoded)
- Verify network connectivity to the API URL
- SignalR auto-reconnects with infinite retry; check logs for reconnection attempts