using System; using System.Threading.Tasks; using leak_test_project.Infrastructure; using leak_test_project.Models; using leak_test_project.Utils; namespace leak_test_project.Services { /// /// 신규 4253 보드를 사용하여 제품 ID를 읽는 센서 서비스. /// ZmdiSensorService와 동일한 구조로 구현되어 교체가 용이함. /// public class Board4253SensorService : IIdSensorService { private readonly Board4253Service _service; private readonly int _sensorIndex; public event EventHandler ProgressMessage; public event EventHandler ErrorMessage; public event EventHandler ConnectionChanged; public Board4253SensorService(Board4253Service service, int sensorIndex) { _service = service; _sensorIndex = sensorIndex; // 좌우 오류 간섭을 막기 위해 공용 에러 이벤트 구독 해제 _service.ConnectionChanged += (s, isConnected) => ConnectionChanged?.Invoke(this, isConnected); } public bool Connect() => _service.Connect(); public void Disconnect() => _service.Disconnect(); public SensorIdData ReadSensor() { try { ProgressMessage?.Invoke(this, "4253 보드에서 ID 읽기 시도 중..."); int channel = _sensorIndex + 1; // 1. 보드 상태 확인 (Fail인 경우 중단) int extendedTimeout = 15000; string statusCmd = $"x00c_00{channel}101:owt28006727ea97c7801"; var statusTask = Task.Run(() => _service.CheckStatusAsync(channel)); if (!statusTask.Wait(extendedTimeout)) { string buf = _service.GetLastBuffer(); string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; ErrorMessage?.Invoke(this, $"통신 실패 (4253 보드 CH{channel} 상태 타임아웃)\r\n[송신값]: {statusCmd}\r\n[수신값]: {displayBuf}"); return null; } if (!statusTask.Result) { string buf = _service.GetLastBuffer(); string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; ErrorMessage?.Invoke(this, $"통신 실패 (4253 보드 CH{channel} 상태 이상 또는 Fail)\r\n[송신값]: {statusCmd}\r\n[수신값]: {displayBuf}"); return null; } // 2. ID 읽기 (끝자리가 F일 경우 최대 3회 시도) string idCmd = $"x00c_00{channel}101:ow2800326003e"; string rawId = null; int maxAttempts = 3; for (int attempt = 1; attempt <= maxAttempts; attempt++) { var idTask = Task.Run(() => _service.ReadIdAsync(channel)); if (!idTask.Wait(extendedTimeout)) { if (attempt == maxAttempts) { string buf = _service.GetLastBuffer(); string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; ErrorMessage?.Invoke(this, $"통신 실패 (4253 보드 CH{channel} ID 대기 타임아웃)\r\n[송신값]: {idCmd}\r\n[수신값]: {displayBuf}"); return null; } continue; } rawId = idTask.Result; if (string.IsNullOrEmpty(rawId)) { if (attempt == maxAttempts) { string buf = _service.GetLastBuffer(); string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; ErrorMessage?.Invoke(this, $"통신 실패 (4253 보드 CH{channel} ID 응답 없거나 파싱 오류)\r\n[송신값]: {idCmd}\r\n[수신값]: {displayBuf}"); return null; } continue; } // 정상적으로 파싱된 경우, 끝자리가 'F'인지 확인 if (rawId.EndsWith("F", StringComparison.OrdinalIgnoreCase)) { if (attempt < maxAttempts) { ProgressMessage?.Invoke(this, $"ID 끝자리가 'F'입니다 ({rawId}). 재시도 중... ({attempt}/{maxAttempts})"); Task.Delay(500).Wait(); // 재시도 전 약간의 딜레이 continue; } else { ProgressMessage?.Invoke(this, $"최대 재시도(2회 추가) 초과. 끝자리가 F인 ID({rawId})를 사용합니다."); } } // 제대로 된 값을 얻었거나 최대 횟수에 도달하면 루프 탈출 break; } // 3. SensorIdData 객체 구성 (16자리 ID를 각 필드에 적절히 분배) // 신규 보드는 16자리 전체가 ID이므로, 파싱 로직 없이 통째로 넣거나 // 특정 규칙이 있다면 여기서 분할함. var data = new SensorIdData { LowID = rawId, ID = rawId, // 16자리 전체를 ID로 사용 Serial = "", // 시리얼 번호는 현재 존재하지 않으므로 강제로 파싱하지 않음 Item = "N/A", PrevResult = "F" // '불량제품 투입' 필터를 통과하기 위한 강제 초기화 }; ProgressMessage?.Invoke(this, "ID 읽기 성공"); return data; } catch (Exception ex) { ErrorMessage?.Invoke(this, $"4253 보드 읽기 중 예외 발생: {ex.Message}"); return null; } } public void Dispose() { _service.Dispose(); } } }