using System; using System.IO.Ports; using System.Threading; using System.Threading.Tasks; namespace ERVSimulator.Protocol { // 공용 시리얼 채널 - byte 단위 수신 콜백 + 송신 helper public class SerialChannel : IDisposable { private SerialPort? _port; private CancellationTokenSource? _cts; private bool _disposed; public string ChannelName { get; } public event Action? ByteReceived; public event Action? Log; public event Action? ConnectionChanged; public bool IsConnected => _port?.IsOpen == true; public SerialChannel(string channelName) { ChannelName = channelName; } public static string[] GetAvailablePorts() => SerialPort.GetPortNames(); public bool Connect(string portName, int baudRate) { try { Disconnect(); _port = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One) { ReadTimeout = 100, WriteTimeout = 200, Handshake = Handshake.None, DtrEnable = false, RtsEnable = false, }; _port.Open(); _cts = new CancellationTokenSource(); _ = Task.Run(() => ReadLoop(_cts.Token)); Log?.Invoke($"[{ChannelName}] Connected {portName} @ {baudRate}"); ConnectionChanged?.Invoke(true); return true; } catch (Exception ex) { Log?.Invoke($"[{ChannelName}] Connect FAIL: {ex.Message}"); return false; } } public void Disconnect() { try { _cts?.Cancel(); } catch { } try { _port?.Close(); } catch { } _port?.Dispose(); _port = null; ConnectionChanged?.Invoke(false); } void ReadLoop(CancellationToken ct) { var buf = new byte[64]; while (!ct.IsCancellationRequested && _port != null && _port.IsOpen) { try { int n = _port.Read(buf, 0, buf.Length); for (int i = 0; i < n; i++) ByteReceived?.Invoke(buf[i]); } catch (TimeoutException) { /* expected */ } catch (Exception ex) { Log?.Invoke($"[{ChannelName}] ReadLoop error: {ex.Message}"); break; } } } public bool Send(byte[] data, int length) { if (_port == null || !_port.IsOpen) return false; try { _port.Write(data, 0, length); return true; } catch (Exception ex) { Log?.Invoke($"[{ChannelName}] Send FAIL: {ex.Message}"); return false; } } public void Dispose() { if (_disposed) return; _disposed = true; Disconnect(); } } public static class HexFormat { public static string Bytes(byte[] data, int length) { var sb = new System.Text.StringBuilder(length * 3); for (int i = 0; i < length; i++) { if (i > 0) sb.Append(' '); sb.Append(data[i].ToString("X2")); } return sb.ToString(); } } }