using System; using System.IO.Ports; namespace leak_test_project.Infrastructure { /// /// 하드웨어 통신을 위한 추상화 인터페이스. /// 시리얼(RS232), 이더넷(TCP/IP) 등 통신 방식에 상관없이 /// 상위 서비스에서 동일한 방식으로 하드웨어에 접근할 수 있도록 정의함. /// public interface ICommunication { /// 통신 채널 식별 이름 (예: COM1, TCP_192.168.0.1) string Name { get; } /// 현재 통신 채널이 열려 있는지 여부 bool IsOpen { get; } /// 통신 채널을 연결함 bool Open(); /// 통신 채널 연결을 해제함 void Close(); /// 데이터를 하드웨어로 전송함 bool Write(string data); /// 수신 버퍼를 비워 잔류 데이터를 제거함 void ClearBuffer(); /// 하드웨어로부터 데이터를 수신했을 때 발생하는 이벤트 event System.EventHandler DataReceived; /// 연결 상태가 변경되었을 때 발생하는 이벤트 (connected, disconnected) event System.EventHandler ConnectionStatusChanged; } /// /// RS232 시리얼 통신을 담당하는 구현체. /// 연결 안정성 강화를 위한 예외 처리 및 상태 감지 로직 포함. /// public class SerialProvider : ICommunication, IDisposable { private SerialPort _serialPort; private bool _disposed = false; public string Name { get; private set; } public bool IsOpen => _serialPort != null && !_disposed && _serialPort.IsOpen; public event EventHandler DataReceived; public event EventHandler ConnectionStatusChanged; public SerialProvider(string portName, int baudRate) { Name = portName; _serialPort = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One); _serialPort.Handshake = Handshake.None; _serialPort.DtrEnable = true; // 터미널 프로그램(포트몬)과 동일하게 제어 신호 활성화 _serialPort.RtsEnable = true; _serialPort.ReadTimeout = 500; _serialPort.WriteTimeout = 500; _serialPort.DataReceived += (s, e) => { try { string data = _serialPort.ReadExisting(); if (!string.IsNullOrEmpty(data)) DataReceived?.Invoke(this, data); } catch (Exception ex) { Console.WriteLine($"[Serial Error] Read error on {Name}: {ex.Message}"); } }; // 기기가 끊겼을 때 감지 (일부 환경에서는 PinChanged 등으로 감지 가능) _serialPort.ErrorReceived += (s, e) => { ConnectionStatusChanged?.Invoke(this, false); }; } public bool Open() { if (_disposed) return false; try { if (IsOpen) return true; _serialPort.Open(); ConnectionStatusChanged?.Invoke(this, true); return true; } catch (Exception ex) { Console.WriteLine($"[Serial Error] Failed to open {Name}: {ex.Message}"); ConnectionStatusChanged?.Invoke(this, false); return false; } } public void Close() { try { if (IsOpen) { _serialPort.Close(); ConnectionStatusChanged?.Invoke(this, false); } } catch (Exception ex) { Console.WriteLine($"[Serial Error] Error closing {Name}: {ex.Message}"); } } public bool Write(string data) { if (!IsOpen) return false; try { _serialPort.Write(data); return true; } catch (Exception ex) { Console.WriteLine($"[Serial Error] Write error on {Name}: {ex.Message}"); return false; } } public void ClearBuffer() { if (IsOpen) { try { _serialPort.DiscardInBuffer(); _serialPort.DiscardOutBuffer(); _serialPort.ReadExisting(); } catch { } } } public void Dispose() { if (!_disposed) { _disposed = true; Close(); _serialPort?.Dispose(); _serialPort = null; } } } }