using System; using System.Collections.Generic; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Windows.Media.Animation; using AR; using arCtl; using COMM; using Project.StateMachine; namespace Project { /// /// SPS (Scan Per Second) 이벤트 핸들러 /// - 장치 연결 및 상태 전송 기능은 _DeviceManagement.cs로 분리됨 /// public partial class fMain { DateTime chargesynctime = DateTime.Now; DateTime agvsendstarttime = DateTime.Now; DateTime lastXbeStatusSendTime = DateTime.Now; DateTime lastBmsQueryTime = DateTime.Now; object connectobj = new object(); void sm_SPS(object sender, EventArgs e) { if (PUB.sm == null || PUB.sm.Step < eSMStep.IDLE || PUB.sm.Step >= eSMStep.CLOSING || PUB.bShutdown == true) return; // SPS는 이제 간단한 작업만 수행 // 장치 연결 및 상태 전송은 별도 태스크(_DeviceManagement.cs)에서 처리 // 장치 연결이 별도로 존재할때 1회 수신 후 통신이 전체 먹통되는 증상이 있어 우선 복귀 251215 try { // ========== 1. 장치 연결 관리 ========== // AGV 연결 lock (connectobj) { ConnectSerialPort(PUB.AGV, PUB.setting.Port_AGV, PUB.setting.Baud_AGV, eVarTime.LastConn_AGV, eVarTime.LastConnTry_AGV, eVarTime.LastRecv_AGV); } // XBee 연결 lock (connectobj) { ConnectSerialPort(PUB.XBE, PUB.setting.Port_XBE, PUB.setting.Baud_XBE, eVarTime.LastConn_XBE, eVarTime.LastConnTry_XBE, eVarTime.LastRecv_XBE); } // BMS 연결 lock (connectobj) { 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)) { this.BeginInvoke(new Action(() => { PUB.log.Add("BMS 자동 연결 해제 (응답 없음)"); PUB.BMS.Close(); })); VAR.TIME.Set(eVarTime.LastConn_BAT, DateTime.Now.AddSeconds(5)); } } } // ========== 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($"sm_SPS Exception: {ex.Message}"); } } /// /// 시리얼 포트 연결 (arDev.arRS232) /// bool ConnectSerialPort(arDev.ISerialComm dev, string port, int baud, eVarTime conn, eVarTime conntry, eVarTime recvtime) { if (port.isEmpty()) return false; 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; PUB.log.Add($"Connect to {port}:{baud}"); if (dev.Open()) { VAR.TIME[recvtime] = DateTime.Now; //값을 수신한것처럼한다 PUB.log.Add(port, $"[{port}:{baud}] 연결 완료"); } else { //존재하지 않는 포트라면 sync를 벗어난다 var ports = System.IO.Ports.SerialPort.GetPortNames().Select(t => t.ToLower()).ToList(); if (ports.Contains(PUB.setting.Port_AGV.ToLower()) == false) { return false; } 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) { this.BeginInvoke(new Action(() => { PUB.log.Add(port, $"포트 변경({dev.PortName}->{port})으로 연결 종료"); VAR.TIME.Set(conntry, DateTime.Now); dev.Close(); })); VAR.TIME.Update(conntry); } else if (dev.IsOpen) { //연결은 되었으나 통신이 지난지 10초가 지났다면 자동종료한다 var tsRecv = VAR.TIME.RUN(recvtime); var tsConn = VAR.TIME.RUN(conntry); if (tsRecv.TotalSeconds > 10 && tsConn.TotalSeconds > 5) { this.BeginInvoke(new Action(() => { PUB.log.Add($"{port} 자동 연결 해제 (응답 없음)"); dev.Close(); })); VAR.TIME.Set(conntry, DateTime.Now); } } return true; } } }