Du hast auf den Branch log-test-branch 2026-04-12 20:57:35 +00:00 gepusht
Neuer Pull-Request
Du hast auf den Branch anon-test2 2026-04-12 20:50:35 +00:00 gepusht
Neuer Pull-Request
Dateien
Soft-LTS/Monitors/HoneywellMonitor.cs
2026-04-12 17:39:45 +02:00

185 Zeilen
5.4 KiB
C#

using System.IO;
using System.Net.Sockets;
using System.Text;
using PrinterMonitor.Configuration;
using PrinterMonitor.Interfaces;
using PrinterMonitor.Models;
namespace PrinterMonitor.Monitors;
/// <summary>
/// IPrinterMonitor-Implementierung für Honeywell PM45 Drucker.
///
/// Protokoll (ASCII über TCP, Standard-Port 9201):
/// Beim Verbindungsaufbau sendet der Drucker automatisch Statuszeilen:
/// LTS:[0|1];HEAD:[0|1];MEDIA:[0|1] + LF (0x0A)
///
/// Werte-Mapping:
/// LTS: 0 = Sensor frei, 1 = Etikett steht an
/// HEAD: 0 = Klappe geschlossen, 1 = Druckkopf offen
/// MEDIA: 0 = Etiketten vorhanden, 1 = Papier leer
///
/// Der Monitor verbindet sich als TCP-Client und liest eingehende Zeilen
/// im Hintergrund. PollStateAsync() gibt den zuletzt empfangenen Zustand zurück.
/// </summary>
public class HoneywellMonitor : IPrinterMonitor
{
private readonly PrinterConfig _config;
private TcpClient? _tcpClient;
private StreamReader? _reader;
private CancellationTokenSource? _readCts;
private Task? _readTask;
// Letzter empfangener Zustand – wird vom Lese-Thread aktualisiert
private volatile SimplePrinterState _lastState = new();
// Wird auf true gesetzt sobald der Lese-Thread ein EOF / Disconnect erkennt
private volatile bool _isDisconnected = true;
public string PrinterName => _config.Name;
public bool IsConnected => _tcpClient?.Connected == true && !_isDisconnected;
public HoneywellMonitor(PrinterConfig config)
{
_config = config;
}
public async Task ConnectAsync(CancellationToken ct = default)
{
_isDisconnected = false;
var client = new TcpClient();
try
{
await client.ConnectAsync(_config.Host, _config.Port, ct);
_reader = new StreamReader(
client.GetStream(), Encoding.ASCII, false, 256, leaveOpen: true);
_tcpClient = client;
_readCts = new CancellationTokenSource();
_readTask = ReadLoopAsync(_readCts.Token);
}
catch
{
client.Dispose();
_isDisconnected = true;
throw;
}
}
public async Task DisconnectAsync()
{
_readCts?.Cancel();
if (_readTask != null)
{
try { await _readTask; } catch { }
_readTask = null;
}
_readCts?.Dispose();
_readCts = null;
_reader?.Dispose();
_reader = null;
_tcpClient?.Dispose();
_tcpClient = null;
}
public Task<SimplePrinterState> PollStateAsync(CancellationToken ct = default)
{
if (_isDisconnected || _tcpClient == null || !_tcpClient.Connected)
throw new IOException("Verbindung zum Honeywell-Drucker getrennt");
return Task.FromResult(_lastState);
}
/// <summary>
/// Liest Statuszeilen vom Drucker im Hintergrund.
/// Format: LTS:0;HEAD:0;MEDIA:0
/// </summary>
private async Task ReadLoopAsync(CancellationToken ct)
{
try
{
while (!ct.IsCancellationRequested && _reader != null)
{
var line = await _reader.ReadLineAsync(ct);
if (line == null)
{
// Drucker hat die Verbindung geschlossen (EOF)
_isDisconnected = true;
break;
}
var parsed = ParseStatusLine(line);
if (parsed != null)
_lastState = parsed;
else
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {PrinterName}: Ungültige Statuszeile: {line}");
}
}
catch (OperationCanceledException) { }
catch (IOException)
{
// Netzwerkfehler → als Disconnect markieren
_isDisconnected = true;
}
catch (ObjectDisposedException) { }
}
/// <summary>
/// Parst eine Honeywell-Statuszeile: LTS:0;HEAD:0;MEDIA:0
/// Gibt null zurück wenn das Format ungültig ist.
/// </summary>
private static SimplePrinterState? ParseStatusLine(string line)
{
// Beispiel: "LTS:0;HEAD:0;MEDIA:0"
line = line.Trim();
if (string.IsNullOrEmpty(line)) return null;
bool lts = false, head = false, media = false;
bool foundLts = false, foundHead = false, foundMedia = false;
var parts = line.Split(';');
foreach (var part in parts)
{
var kv = part.Split(':');
if (kv.Length != 2) continue;
var key = kv[0].Trim().ToUpperInvariant();
var val = kv[1].Trim();
switch (key)
{
case "LTS":
lts = val == "1";
foundLts = true;
break;
case "HEAD":
head = val == "1";
foundHead = true;
break;
case "MEDIA":
media = val == "1";
foundMedia = true;
break;
}
}
if (!foundLts || !foundHead || !foundMedia)
return null;
return new SimplePrinterState
{
LtsSensor = lts,
Druckerklappe = head,
KeineEtiketten = media
};
}
public async ValueTask DisposeAsync()
{
await DisconnectAsync();
}
}