SprtDiscoveryService caught SocketException on the UDP 9100 bind and the broadcast send and returned an empty list either way. That defeated the CompositeDiscoveryService fix that rethrows only when every child discovery service fails: on a real network fault the composite would see one child throw (ENPC) and one child silently succeed empty (SPRT), conclude discovery worked and found nothing, and let PrinterDiscoveryBackgroundTask expire and deregister live Epson printers after the retention window - discarding their queued jobs. Move the policy decision to the composite, which already has it right: the child now reports bind/broadcast failure honestly by rethrowing, and CompositeDiscoveryService absorbs a single child's failure while still rethrowing when every child fails. The per-datagram receive-loop catch is unchanged - a single bad receive still shouldn't abort the pass. Rewrote SprtDiscoveryServiceTests' bind-failure test to assert the throw, and added an end-to-end CompositeDiscoveryServiceTests case with a real SprtDiscoveryService failing to bind alongside a stub that succeeds, proving the composite still degrades gracefully rather than losing the pass. Updated the POS895 design doc to match the new contract. Claude-Session: https://claude.ai/code/session_01E6N3P4nSZLtp4BC2LXVref
Inspectron.Epson - Epson TM-m30III Printer SDK
A comprehensive C# SDK for the Epson TM-m30III thermal receipt printer, providing easy-to-use APIs for printing, status checking, and diagnostics.
Features
- Async/Await Pattern - Modern asynchronous API throughout
- Connection Management - Reliable TCP/IP connection handling
- Status Monitoring - Comprehensive printer status queries
- Error Detection - Detailed error reporting and diagnostics
- Printing Operations - Simple text printing with ESC/POS commands
- Logging Support - Optional ILogger integration for diagnostics
- Type-Safe - Strong typing with C# records for status data
Installation
Add the project reference to your solution:
dotnet add reference path/to/Inspectron.Epson/Inspectron.Epson.csproj
Or include the compiled DLL in your project.
Dependencies
- .NET 8.0 or later
- Microsoft.Extensions.Logging.Abstractions 8.0.0
Quick Start
Basic Printing
using Inspectron.Epson;
// Create printer instance
await using var printer = new EpsonPrinter();
// Connect to printer
await printer.ConnectAsync("192.168.1.100");
// Print text with paper cut
await printer.PrintTextAndCutAsync("Hello World!\nThis is a test receipt.");
With Logging
using Inspectron.Epson;
using Microsoft.Extensions.Logging;
// Create logger
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Information);
});
var logger = loggerFactory.CreateLogger<EpsonPrinter>();
// Create printer with logging
await using var printer = new EpsonPrinter(logger);
await printer.ConnectAsync("192.168.1.100");
await printer.PrintTextAndCutAsync("Hello World!");
Usage Examples
Checking Printer Status
await using var printer = new EpsonPrinter();
await printer.ConnectAsync("192.168.1.100");
// Get overall status with health assessment
var status = await printer.GetOverallStatusAsync();
Console.WriteLine($"Status: {status.StatusText}");
Console.WriteLine($"Ready: {status.IsReady}");
Console.WriteLine($"Model: {status.PrinterModel}");
if (!status.IsReady)
{
Console.WriteLine("Recommendations:");
foreach (var recommendation in status.Recommendations)
{
Console.WriteLine($" - {recommendation}");
}
}
Individual Status Queries
await using var printer = new EpsonPrinter();
await printer.ConnectAsync("192.168.1.100");
// Get specific status information
var printerStatus = await printer.GetPrinterStatusAsync();
Console.WriteLine($"Online: {printerStatus.IsOnline}");
Console.WriteLine($"Cover Closed: {printerStatus.IsCoverClosed}");
var paperStatus = await printer.GetPaperSensorStatusAsync();
Console.WriteLine($"Paper Present: {paperStatus.PaperPresent}");
Console.WriteLine($"Paper Near End: {paperStatus.PaperNearEnd}");
var errorStatus = await printer.GetErrorStatusAsync();
Console.WriteLine($"Has Error: {errorStatus.HasError}");
Console.WriteLine($"Recoverable Error: {errorStatus.RecoverableError}");
Running Diagnostics
using Inspectron.Epson;
var diagnostics = new EpsonDiagnostics();
var report = await diagnostics.RunFullDiagnosticsAsync("192.168.1.100");
Console.WriteLine(report);
Sample diagnostic output:
======================================================================
EPSON TM-m30III PRINTER DIAGNOSTICS
======================================================================
Printer IP: 192.168.1.100:9100
Timestamp: 2024-01-15 14:30:45
======================================================================
⏳ Connecting to printer...
✅ Connection established
⏳ Retrieving printer information...
⏳ Querying printer status...
======================================================================
OVERALL STATUS
======================================================================
🟢 READY - Printer is operational
🖨️ Printer Model TM-m30III
======================================================================
GENERAL PRINTER STATUS
======================================================================
📊 Raw Status Byte 0x12
📊 Binary 00010010
✅ Printer Online YES
✅ Cover Closed YES
❌ Paper Feed Button Pressed NO
❌ Drawer Open Signal NO
Advanced Printing
await using var printer = new EpsonPrinter();
await printer.ConnectAsync("192.168.1.100");
// Print without cutting
await printer.PrintTextAsync("Line 1\n");
await printer.PrintTextAsync("Line 2\n");
await printer.PrintTextAsync("Line 3\n");
// Feed and cut
await printer.SendRawCommandAsync(EpsonCommands.FeedLines(5));
await printer.SendRawCommandAsync(EpsonCommands.CutPaperFull);
Sending Raw ESC/POS Commands
await using var printer = new EpsonPrinter();
await printer.ConnectAsync("192.168.1.100");
// Use predefined commands
await printer.SendRawCommandAsync(EpsonCommands.Initialize);
await printer.PrintTextAsync("Initialized printer\n");
// Or create custom commands
byte[] customCommand = { 0x1B, 0x40 }; // ESC @ (Initialize)
await printer.SendRawCommandAsync(customCommand);
API Reference
EpsonPrinter Class
Main class for printer operations.
Constructor
public EpsonPrinter(ILogger<EpsonPrinter>? logger = null)
Creates a new printer instance with optional logging support.
Connection Methods
Task ConnectAsync(string ip, int port = 9100, int timeoutSeconds = 5)
Connect to the printer at the specified IP address and port.
void Disconnect()
Disconnect from the printer.
bool IsConnected { get; }
Returns true if currently connected to the printer.
Status Query Methods
Task<PrinterStatus> GetPrinterStatusAsync()
Get general printer status (online, cover, drawer, etc.).
Task<OfflineStatus> GetOfflineStatusAsync()
Get offline status (reasons why printer is offline).
Task<ErrorStatus> GetErrorStatusAsync()
Get error status (recoverable, unrecoverable, cutter errors).
Task<PaperSensorStatus> GetPaperSensorStatusAsync()
Get paper sensor status (paper present, near end).
Task<string?> GetPrinterIdAsync()
Get printer model/ID string.
Task<OverallStatus> GetOverallStatusAsync()
Get comprehensive status with health assessment and recommendations.
Printing Methods
Task PrintTextAsync(string text)
Print text to the printer.
Task PrintTextAndCutAsync(string text, int feedLines = 5)
Print text, feed paper, and cut.
Task SendRawCommandAsync(byte[] command)
Send raw ESC/POS command bytes.
EpsonDiagnostics Class
Comprehensive diagnostics tool.
public EpsonDiagnostics(ILogger<EpsonDiagnostics>? logger = null)
Task<string> RunFullDiagnosticsAsync(string ip, int port = 9100, int timeoutSeconds = 5)
Run full diagnostic check and return formatted report.
Status Data Models
All status classes are immutable records with these common properties:
byte RawByte- Raw status byte from printerstring Binary- Binary representation (8 bits)string Hex- Hexadecimal representation
PrinterStatus
bool DrawerOpen- Drawer kick-out connector pin 3 is HIGHbool IsOnline- Printer is onlinebool IsCoverClosed- Cover is closedbool PaperFeedButtonPressed- Paper feed button is pressed
OfflineStatus
bool CoverOpen- Cover is open (offline reason)bool PaperFeedButton- Paper feed button active (offline reason)bool PaperEnd- Paper end detected (offline reason)bool ErrorOccurred- Error occurred (offline reason)
ErrorStatus
bool RecoverableError- Recoverable error occurredbool AutoCutterError- Auto-cutter error occurredbool UnrecoverableError- Unrecoverable error occurredbool AutoRecoveryError- Auto-recovery error occurredbool HasError- True if any error is present
PaperSensorStatus
bool PaperNearEnd- Paper roll is near endbool PaperPresent- Paper is present
OverallStatus
string? PrinterModel- Printer model/IDPrinterStatus? PrinterStatus- General printer statusOfflineStatus? OfflineStatus- Offline statusErrorStatus? ErrorStatus- Error statusPaperSensorStatus? PaperStatus- Paper sensor statusstring StatusText- Overall status descriptionbool IsReady- True if printer is readyList<string> Recommendations- List of recommendationsstring StatusIcon- Status emoji (🟢/🟡/🟠/🔴/❓)
EpsonCommands Class
Static class containing ESC/POS command constants:
byte[] Initialize- Initialize printer (ESC @)byte[] GetPrinterStatus- DLE EOT 1byte[] GetOfflineStatus- DLE EOT 2byte[] GetErrorStatus- DLE EOT 3byte[] GetPaperSensorStatus- DLE EOT 4byte[] GetPrinterId- GS I 1byte[] CutPaperFull- Full paper cut (GS V 0)byte[] CutPaperPartial- Partial paper cut (GS V 1)byte[] FeedLines(byte lines)- Feed n lines (ESC d n)byte[] LineFeed- Line feed (LF)
Exception Types
EpsonPrinterException
Base exception for all Epson printer errors.
EpsonConnectionException
Thrown when connection fails or is lost.
Properties:
string? PrinterIp- Printer IP addressint? PrinterPort- Printer port
EpsonCommandException
Thrown when a command fails.
Properties:
byte[]? Command- The command that failed
Troubleshooting
Connection Issues
Problem: Cannot connect to printer
Solutions:
- Verify printer IP address is correct
- Ensure printer is powered on
- Check network connectivity (ping the printer)
- Verify printer is on the same network
- Check firewall settings
- Ensure port 9100 is accessible
Timeout Errors
Problem: Operations timeout
Solutions:
- Increase timeout parameter in ConnectAsync
- Check printer is not busy with another job
- Verify printer is not in an error state
- Check network latency
Printer Offline
Problem: Printer shows as offline
Solutions:
- Check cover is closed
- Ensure paper is loaded
- Clear any error conditions
- Check offline status for specific reasons
Paper Issues
Problem: Paper not detected or near end
Solutions:
- Load paper roll correctly
- Ensure paper is feeding through sensor
- Replace paper if near end
- Check paper roll is compatible
Auto-Cutter Errors
Problem: Cutter error reported
Solutions:
- Check for paper jams in cutter
- Remove any obstructions
- Open and close cover to reset
- May require service if persistent
Technical Details
ESC/POS Protocol
This SDK uses ESC/POS commands via TCP/IP on port 9100. The DLE EOT (0x10 0x04) real-time status commands are used for status queries.
Status Byte Format
All status responses follow the format: 0xx1xx10b where:
- Bit 7: Always 0
- Bits 6,5,3,2: Status-specific flags
- Bit 4: Always 1
- Bits 1,0: Fixed pattern (10b)
Connection
- Protocol: TCP/IP
- Default Port: 9100
- Character Encoding: UTF-8 for text, ASCII for responses
- Timeout: Configurable (default 5 seconds)
License
Copyright Inspectron. All rights reserved.
Support
For issues, questions, or contributions, please contact the development team.