using System; using System.Collections.Generic; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Media.Animation; using AR; using arCtl; using COMM; using Project.StateMachine; namespace Project { /// /// 장치 연결 및 상태 전송을 담당하는 별도 태스크 /// public partial class fMain { // 장치 관리 태스크 관련 private Task deviceManagementTask; private CancellationTokenSource deviceManagementCts; private volatile bool isDeviceManagementRunning = false; /// /// 장치 관리 태스크 시작 (IDLE 상태 진입 시 호출) /// public void StartDeviceManagementTask() { if (isDeviceManagementRunning) { PUB.log.Add("DeviceManagement", "이미 실행 중입니다."); return; } isDeviceManagementRunning = true; deviceManagementCts = new CancellationTokenSource(); deviceManagementTask = Task.Factory.StartNew( () => DeviceManagementWorker(deviceManagementCts.Token), deviceManagementCts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default ); PUB.log.Add("DeviceManagement", "장치 관리 태스크 시작"); } /// /// 장치 관리 태스크 종료 /// File : /device/_DeviceManagement.cs /// public void StopDeviceManagementTask() { if (!isDeviceManagementRunning) return; isDeviceManagementRunning = false; try { deviceManagementCts?.Cancel(); if (deviceManagementTask != null) { if (!deviceManagementTask.Wait(3000)) // 3초 대기 { PUB.log.AddE("DeviceManagement:태스크 종료 대기 시간 초과"); } } } catch (Exception ex) { PUB.log.AddE($"DeviceManagement 종료 중 오류: {ex.Message}"); } finally { deviceManagementCts?.Dispose(); deviceManagementCts = null; deviceManagementTask = null; PUB.log.Add("DeviceManagement", "장치 관리 태스크 종료"); } } /// /// 장치 관리 워커 (별도 태스크에서 실행) /// - 장치 연결 관리 (AGV, XBee, BMS) /// - 자동 상태 전송 (XBee, BMS) /// private void DeviceManagementWorker(CancellationToken cancellationToken) { PUB.log.Add("DeviceManagementWorker", "시작"); DateTime lastXbeStatusSendTime = DateTime.Now; DateTime lastBmsQueryTime = DateTime.Now; while (!cancellationToken.IsCancellationRequested && isDeviceManagementRunning) { try { // 상태머신이 IDLE 이상이고 CLOSING 미만일 때만 동작 if (PUB.sm.Step >= eSMStep.IDLE && PUB.sm.Step < eSMStep.CLOSING) { // ========== 1. 장치 연결 관리 ========== ManageDeviceConnections(); // ========== 2. XBee 상태 전송 ========== if (PUB.XBE != null && PUB.XBE.IsOpen) { var tsXbe = DateTime.Now - lastXbeStatusSendTime; if (tsXbe.TotalSeconds >= PUB.setting.interval_xbe) { lastXbeStatusSendTime = DateTime.Now; ThreadPool.QueueUserWorkItem(_ => { try { PUB.XBE.SendStatus(); } catch (Exception ex) { PUB.log.AddE($"XBee SendStatus 오류: {ex.Message}"); } }); } } // ========== 3. BMS 쿼리 및 배터리 경고 ========== if (PUB.BMS != null && PUB.BMS.IsOpen) { var tsBms = DateTime.Now - lastBmsQueryTime; if (tsBms.TotalSeconds >= PUB.setting.interval_bms) { lastBmsQueryTime = DateTime.Now; ThreadPool.QueueUserWorkItem(_ => { try { PUB.BMS.SendQuery(); } catch (Exception ex) { PUB.log.AddE($"BMS SendQuery 오류: {ex.Message}"); } }); } // 배터리 경고음 try { Update_BatteryWarnSpeak(); } catch (Exception ex) { PUB.log.AddE($"BatteryWarnSpeak 오류: {ex.Message}"); } } } } catch (Exception ex) { PUB.log.AddE($"DeviceManagementWorker 오류: {ex.Message}"); } // 1초 대기 (또는 취소 요청 시 즉시 종료) try { Task.Delay(1000, cancellationToken).Wait(); } catch (OperationCanceledException) { break; } } PUB.log.Add("DeviceManagementWorker", "종료"); } /// /// 장치 연결 상태 관리 /// private void ManageDeviceConnections() { try { // AGV 연결 ConnectSerialPort(PUB.AGV, PUB.setting.Port_AGV, PUB.setting.Baud_AGV, eVarTime.LastConn_AGV, eVarTime.LastConnTry_AGV, eVarTime.LastRecv_AGV); // XBee 연결 ConnectSerialPort(PUB.XBE, PUB.setting.Port_XBE, PUB.setting.Baud_XBE, eVarTime.LastConn_XBE, eVarTime.LastConnTry_XBE, eVarTime.LastRecv_XBE); // BMS 연결 if (PUB.BMS.IsOpen == false) { var ts = VAR.TIME.RUN(eVarTime.LastConn_BAT); if (ts.TotalSeconds > 3) { PUB.log.Add($"BMS 연결 시도: {PUB.setting.Port_BAT}"); PUB.BMS.PortName = PUB.setting.Port_BAT; if (PUB.BMS.Open()) PUB.log.AddI($"BMS 연결 완료({PUB.setting.Port_BAT})"); VAR.TIME.Update(eVarTime.LastConn_BAT); VAR.TIME.Update(eVarTime.LastConnTry_BAT); } } else if (PUB.BMS.IsValid == false) { var ts = VAR.TIME.RUN(eVarTime.LastConnTry_BAT); if (ts.TotalSeconds > (PUB.setting.interval_bms * 2.5)) { PUB.log.Add("BMS 자동 연결 해제 (응답 없음)"); PUB.BMS.Close(); VAR.TIME.Set(eVarTime.LastConn_BAT, DateTime.Now.AddSeconds(5)); } } } catch (Exception ex) { PUB.log.AddE($"ManageDeviceConnections 오류: {ex.Message}"); } } /// /// 시리얼 포트 연결 (arDev.arRS232) /// void ConnectSerialPort(arDev.arRS232 dev, string port, int baud, eVarTime conn, eVarTime conntry, eVarTime recvtime) { if (dev.IsOpen == false && port.isEmpty() == false) { var tsPLC = VAR.TIME.RUN(conntry); if (tsPLC.TotalSeconds > 5) { VAR.TIME.Update(conntry); try { VAR.TIME.Update(recvtime); dev.PortName = port; dev.BaudRate = baud; if (dev.Open()) { PUB.log.Add(port, $"[AGV:{port}:{baud}] 연결 완료"); } else { var errmessage = dev.errorMessage; PUB.log.AddE($"[AGV:{port}:{baud}] {errmessage}"); } VAR.TIME.Update(conn); VAR.TIME.Update(conntry); } catch (Exception ex) { PUB.log.AddE(ex.Message); } } } else if (dev.PortName.Equals(port) == false) { PUB.log.Add(port, $"포트 변경({dev.PortName}->{port})으로 연결 종료"); dev.Close(); VAR.TIME.Update(conntry); } } /// /// 시리얼 포트 연결 (Device.Xbee) /// void ConnectSerialPort(Device.Xbee dev, string port, int baud, eVarTime conn, eVarTime conntry, eVarTime recvtime) { if (dev.IsOpen == false && port.isEmpty() == false) { var tsPLC = VAR.TIME.RUN(conntry); if (tsPLC.TotalSeconds > 5) { VAR.TIME.Update(conntry); try { VAR.TIME.Update(recvtime); dev.PortName = port; dev.BaudRate = baud; if (dev.Open()) { PUB.log.Add(port, $"[XBEE:{port}:{baud}] 연결 완료"); } else { var errmessage = dev.errorMessage; PUB.log.AddE($"[XBEE:{port}:{baud}] {errmessage}"); } VAR.TIME.Update(conn); VAR.TIME.Update(conntry); } catch (Exception ex) { PUB.log.AddE(ex.Message); } } } else if (dev.PortName.Equals(port) == false) { PUB.log.Add(port, $"포트 변경({dev.PortName}->{port})으로 연결 종료"); dev.Close(); VAR.TIME[(int)conntry] = DateTime.Now; } } } }