using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.IO.Ports; using System.IO; using AR; namespace Project { public partial class Form1 : Form { public Form1() { InitializeComponent(); PUB.init(); // 현재 설치되어있는 시리얼포트목록을 조회해서 cmbPort... 컨트롤에 미리 입력한다. RefreshPortList(); // 기본 BaudRate 설정 tbBaudAGV.Text = "115200"; tbBaudBMS.Text = "9600"; } /// /// 시리얼 포트 목록을 새로고침하여 콤보박스에 추가 /// private void RefreshPortList() { try { string[] ports = SerialPort.GetPortNames(); cmbPortAGV.Items.Clear(); cmbPortBMS.Items.Clear(); foreach (string port in ports) { cmbPortAGV.Items.Add(port); cmbPortBMS.Items.Add(port); } addLog($"포트 목록 새로고침 완료: {ports.Length}개 포트 발견"); } catch (Exception ex) { addLog($"포트 목록 조회 오류: {ex.Message}"); } } /// /// 설정파일에서 데이터를 읽어서 컨트롤에 설정한다 /// void LoadSetting() { try { // AGV 설정 if (!string.IsNullOrEmpty(PUB.setting.Port_AGV)) { this.cmbPortAGV.Text = PUB.setting.Port_AGV; } tbBaudAGV.Text = PUB.setting.Baud_AGV.ToString(); // BMS 설정 if (!string.IsNullOrEmpty(PUB.setting.Port_BAT)) { cmbPortBMS.Text = PUB.setting.Port_BAT; } tbBaudBMS.Text = PUB.setting.Baud_BAT.ToString(); addLog("설정 파일 로드 완료"); } catch (Exception ex) { addLog($"설정 로드 오류: {ex.Message}"); } } /// /// 현재 설정된 값을 확인하고 셋팅파일에 기록한다 /// void SaveSetting() { try { // 현재 UI 값을 설정 객체에 반영 PUB.setting.Port_AGV = cmbPortAGV.SelectedItem?.ToString() ?? ""; PUB.setting.Baud_AGV = int.TryParse(tbBaudAGV.Text, out int agvBaud) ? agvBaud : 115200; PUB.setting.Port_BAT = cmbPortBMS.SelectedItem?.ToString() ?? ""; PUB.setting.Baud_BAT = int.TryParse(tbBaudBMS.Text, out int bmsBaud) ? bmsBaud : 9600; // 파일에 저장 PUB.setting.Save(); addLog("설정 파일 저장 완료"); } catch (Exception ex) { addLog($"설정 저장 오류: {ex.Message}"); } } private void Form1_Load(object sender, EventArgs e) { LoadSetting(); } private void btLoadSetting_Click(object sender, EventArgs e) { LoadSetting(); } private void btSaveSetting_Click(object sender, EventArgs e) { SaveSetting(); } private void btTestAGV_Click(object sender, EventArgs e) { // AGV 용 프로토콜을 전송해서 올바르게 피드백이 오는지 확인한다 // AGV Protocol: STX(0x02) + Data + ETX(0x03) if (cmbPortAGV.SelectedItem == null) { addLog("[AGV] 포트를 선택해주세요"); return; } if (!int.TryParse(tbBaudAGV.Text, out int baudRate)) { addLog("[AGV] BaudRate가 올바르지 않습니다"); return; } try { addLog($"[AGV] 테스트 시작: {cmbPortAGV.SelectedItem} @ {baudRate}bps"); using (arDev.Narumi port = new arDev.Narumi()) { port.PortName = cmbPortAGV.SelectedItem.ToString(); port.BaudRate = baudRate; port.Message += (s1, e1) =>{ addLog($"[AGV] 응답 수신: {e1.Message}"); }; port.Open(); addLog($"[AGV] 포트 열기 성공"); DateTime sendtime = DateTime.Now; port.AGVMoveStop("test");// addLog($"[AGV] 테스트 명령 전송:"); // 응답 대기 System.Threading.Thread.Sleep(1200); if (port.lastRecvTime > sendtime) { // STX, ETX 확인 //if (response.Length >= 2 && response[0] == 0x02 && response[response.Length - 1] == 0x03) //{ addLog($"[AGV] ✓ 통신 성공 - 올바른 프로토콜 응답"); //} //else //{ // addLog($"[AGV] ⚠ 응답은 받았으나 프로토콜 형식이 다름"); //} } else { addLog($"[AGV] ⚠ 응답 없음 - 포트는 열렸으나 장치 응답 없음"); } port.Close(); addLog($"[AGV] 포트 닫기 완료"); } } catch (Exception ex) { addLog($"[AGV] ✗ 오류 발생: {ex.Message}"); } } private void btTestBMS_Click(object sender, EventArgs e) { // BMS 용 프로토콜을 전송해서 올바르게 피드백이 오는지 확인한다 // BMS Protocol: STX(0xDD) + Data(32 bytes) + ETX(0x77) if (cmbPortBMS.SelectedItem == null) { addLog("[BMS] 포트를 선택해주세요"); return; } if (!int.TryParse(tbBaudBMS.Text, out int baudRate)) { addLog("[BMS] BaudRate가 올바르지 않습니다"); return; } try { addLog($"[BMS] 테스트 시작: {cmbPortBMS.SelectedItem} @ {baudRate}bps"); using (SerialPort port = new SerialPort()) { port.PortName = cmbPortBMS.SelectedItem.ToString(); port.BaudRate = baudRate; port.DataBits = 8; port.Parity = Parity.None; port.StopBits = StopBits.One; port.ReadTimeout = 3000; port.WriteTimeout = 3000; port.Open(); addLog($"[BMS] 포트 열기 성공"); // 간단한 테스트 명령 전송 var testCmd = new List(); testCmd.Add(0xDD); testCmd.Add(0xA5); testCmd.Add(0x03); testCmd.Add(0x00); testCmd.Add(0xFF); testCmd.Add(0xFD); testCmd.Add(0x77); testCmd.Add(0x0D); port.Write(testCmd.ToArray(), 0, testCmd.Count);//.Length); addLog($"[BMS] 테스트 명령 전송: {BitConverter.ToString(testCmd.ToArray())}"); // 응답 대기 System.Threading.Thread.Sleep(1200); if (port.BytesToRead > 0) { byte[] response = new byte[port.BytesToRead]; port.Read(response, 0, response.Length); addLog($"[BMS] 응답 수신 ({response.Length} bytes): {BitConverter.ToString(response)}"); // STX, ETX 확인 if (response.Length >= 2 && response[0] == 0xDD && response[response.Length - 1] == 0x77) { addLog($"[BMS] ✓ 통신 성공 - 올바른 프로토콜 응답"); if (response.Length == 34 || response.Length == 23) { addLog($"[BMS] ✓ 데이터 길이 확인 ({response.Length} bytes)"); } } else { addLog($"[BMS] ⚠ 응답은 받았으나 프로토콜 형식이 다름"); } } else { addLog($"[BMS] ⚠ 응답 없음 - 포트는 열렸으나 장치 응답 없음"); } port.Close(); addLog($"[BMS] 포트 닫기 완료"); } } catch (Exception ex) { addLog($"[BMS] ✗ 오류 발생: {ex.Message}"); } } void addLog(string msg) { // rtLog 컨트롤에 메세지를 추가하고, 추가된 메세지에 맞게 커서가 자동이동되게 한다 if (rtLog.InvokeRequired) { rtLog.Invoke(new Action(() => addLog(msg))); } else { string timestamp = DateTime.Now.ToString("HH:mm:ss.fff"); rtLog.AppendText($"[{timestamp}] {msg}\r\n"); rtLog.SelectionStart = rtLog.Text.Length; rtLog.ScrollToCaret(); } } private async void btFindPort_Click(object sender, EventArgs e) { // 버튼 비활성화 (중복 실행 방지) btFindPort.Enabled = false; try { await Task.Run(() => FindPortsAsync()); } finally { // 버튼 재활성화 btFindPort.Enabled = true; } } private void FindPortsAsync() { // AGV와 BMS의 응답 포트를 병렬로 동시에 찾는다 addLog("===== 포트 자동 검색 시작 (병렬 모드) ====="); string[] ports = SerialPort.GetPortNames(); if (ports.Length == 0) { addLog("사용 가능한 포트가 없습니다"); return; } addLog($"총 {ports.Length}개 포트 검색 중..."); // 병렬 검색을 위한 변수 object lockObj = new object(); string foundAGVPort = null; string foundBMSPort = null; int agvBaud = 115200; int bmsBaud = 9600; bool bothFound = false; // 현재 설정된 포트 (우선순위 표시용) string currentAGVPort = null; string currentBMSPort = null; this.Invoke(new Action(() => { currentAGVPort = cmbPortAGV.SelectedItem?.ToString(); currentBMSPort = cmbPortBMS.SelectedItem?.ToString(); })); // 현재 설정 확인 로그 addLog($"현재 AGV 설정: {(string.IsNullOrEmpty(currentAGVPort) ? "(선택 안됨)" : currentAGVPort)}"); addLog($"현재 BMS 설정: {(string.IsNullOrEmpty(currentBMSPort) ? "(선택 안됨)" : currentBMSPort)}"); // === 1단계: 현재 설정된 포트 우선 테스트 === addLog("--- 1단계: 현재 설정 포트 우선 테스트 ---"); // AGV 현재 포트 우선 테스트 if (!string.IsNullOrEmpty(currentAGVPort) && ports.Contains(currentAGVPort)) { addLog($"[우선] AGV 현재 포트 테스트: {currentAGVPort}"); for (int retry = 1; retry <= 3; retry++) { if (TestAGVPort(currentAGVPort, agvBaud)) { foundAGVPort = currentAGVPort; addLog($"✓ 현재 설정 포트에서 AGV 발견: {currentAGVPort}"); break; } if (retry < 3) System.Threading.Thread.Sleep(500); } } // BMS 현재 포트 우선 테스트 if (!string.IsNullOrEmpty(currentBMSPort) && ports.Contains(currentBMSPort)) { addLog($"[우선] BMS 현재 포트 테스트: {currentBMSPort}"); for (int retry = 1; retry <= 3; retry++) { if (TestBMSPort(currentBMSPort, bmsBaud)) { foundBMSPort = currentBMSPort; addLog($"✓ 현재 설정 포트에서 BMS 발견: {currentBMSPort}"); break; } if (retry < 3) System.Threading.Thread.Sleep(500); } } // 둘 다 찾았으면 즉시 종료 if (foundAGVPort != null && foundBMSPort != null) { addLog("현재 설정 포트에서 모두 발견! 검색 종료"); ApplyFoundPorts(foundAGVPort, foundBMSPort, agvBaud, bmsBaud); addLog("===== 포트 자동 검색 완료 ====="); return; } // === 2단계: 나머지 포트 병렬 검색 === if (foundAGVPort == null || foundBMSPort == null) { addLog("--- 2단계: 나머지 포트 병렬 검색 ---"); } // 모든 포트를 병렬로 테스트 (단, 이미 테스트한 포트 제외) List tasks = new List(); foreach (string portName in ports) { string port = portName; // 클로저 캡처용 // 이미 우선 테스트에서 체크한 포트는 제외 if (port == currentAGVPort || port == currentBMSPort) continue; Task task = Task.Run(() => { // 이미 둘 다 찾았으면 즉시 종료 if (bothFound) return; addLog($"[{port}] 검사 시작..."); // AGV 테스트 (3회 재시도) for (int retry = 1; retry <= 3; retry++) { // 첫 번째 시도에서는 조기 종료하지 않고 전체 대기 시간 보장 if (retry > 1 && bothFound) return; try { if (foundAGVPort == null && TestAGVPort(port, agvBaud)) { lock (lockObj) { if (foundAGVPort == null) { foundAGVPort = port; addLog($"[{port}] ✓ AGV 발견! (재시도: {retry}/3)"); // 둘 다 찾았는지 확인 if (foundBMSPort != null) { bothFound = true; addLog("AGV와 BMS 모두 발견! 검색 종료"); } } } break; } } catch { } if (retry < 3 && foundAGVPort == null && !bothFound) { System.Threading.Thread.Sleep(500); } } // BMS 테스트 (3회 재시도) for (int retry = 1; retry <= 3; retry++) { // 첫 번째 시도에서는 조기 종료하지 않고 전체 대기 시간 보장 if (retry > 1 && bothFound) return; try { if (foundBMSPort == null && TestBMSPort(port, bmsBaud)) { lock (lockObj) { if (foundBMSPort == null) { foundBMSPort = port; addLog($"[{port}] ✓ BMS 발견! (재시도: {retry}/3)"); // 둘 다 찾았는지 확인 if (foundAGVPort != null) { bothFound = true; addLog("AGV와 BMS 모두 발견! 검색 종료"); } } } break; } } catch { } if (retry < 3 && foundBMSPort == null && !bothFound) { System.Threading.Thread.Sleep(500); } } // 둘 다 실패한 경우에만 로그 if (!bothFound && foundAGVPort != port && foundBMSPort != port) { addLog($"[{port}] ✗ 응답 없음"); } }); tasks.Add(task); } // 나머지 포트가 있을 경우만 대기 if (tasks.Count > 0) { addLog("나머지 포트 테스트 진행 중..."); // 주기적으로 체크하면서 둘 다 찾았으면 즉시 종료 while (!Task.WaitAll(tasks.ToArray(), 100)) { if (bothFound) { addLog("검색 조기 종료 - 모든 포트 발견"); break; } } } // 결과 적용 ApplyFoundPorts(foundAGVPort, foundBMSPort, agvBaud, bmsBaud); addLog("===== 포트 자동 검색 완료 ====="); } /// /// 발견된 포트를 UI에 적용 /// private void ApplyFoundPorts(string agvPort, string bmsPort, int agvBaud, int bmsBaud) { if (agvPort != null) { this.Invoke(new Action(() => { int idx = cmbPortAGV.Items.IndexOf(agvPort); if (idx >= 0) { cmbPortAGV.SelectedIndex = idx; tbBaudAGV.Text = agvBaud.ToString(); } })); addLog($"✓ AGV 포트: {agvPort} (Baud: {agvBaud})"); } else { addLog("⚠ AGV 포트를 찾지 못했습니다"); } if (bmsPort != null) { this.Invoke(new Action(() => { int idx = cmbPortBMS.Items.IndexOf(bmsPort); if (idx >= 0) { cmbPortBMS.SelectedIndex = idx; tbBaudBMS.Text = bmsBaud.ToString(); } })); addLog($"✓ BMS 포트: {bmsPort} (Baud: {bmsBaud})"); } else { addLog("⚠ BMS 포트를 찾지 못했습니다"); } } /// /// AGV 포트인지 테스트 (btTestAGV_Click 코드 사용) /// private bool TestAGVPort(string portName, int baudRate) { try { using (arDev.Narumi port = new arDev.Narumi()) { port.PortName = portName; port.BaudRate = baudRate; addLog($" >> AGV 테스트 시작: {portName} @ {baudRate}bps"); port.Open(); addLog($" >> AGV 포트 열기 성공: {portName}"); DateTime sendtime = DateTime.Now; port.AGVMoveStop("test"); addLog($" >> AGV 명령 전송: {portName} (시간: {sendtime:HH:mm:ss.fff})"); // 응답 대기 (더 길게, 주기적으로 체크) int maxWait = 2000; // 최대 2초 대기 int waitStep = 100; int totalWait = 0; while (totalWait < maxWait) { System.Threading.Thread.Sleep(waitStep); totalWait += waitStep; // lastRecvTime 확인 if (port.lastRecvTime > sendtime) { addLog($" >> ✓✓✓ AGV 응답 수신: {portName} (응답시간: {port.lastRecvTime:HH:mm:ss.fff}, 대기: {totalWait}ms)"); addLog($" >> 응답 데이터: {port.LastRecvString}"); port.Close(); return true; } // LastRecvString도 확인 (lastRecvTime이 업데이트 안 될 수도 있음) if (!string.IsNullOrEmpty(port.LastRecvString)) { addLog($" >> ✓✓✓ AGV 응답 감지 (문자열): {portName} (대기: {totalWait}ms)"); addLog($" >> 응답 데이터: {port.LastRecvString}"); port.Close(); return true; } } addLog($" >> AGV 응답 없음: {portName} (최종 대기: {totalWait}ms, 마지막수신: {port.lastRecvTime:HH:mm:ss.fff})"); port.Close(); } } catch (Exception ex) { addLog($" >> AGV 테스트 오류: {portName} - {ex.Message}"); } return false; } /// /// BMS 포트인지 테스트 (btTestBMS_Click 코드 사용) /// private bool TestBMSPort(string portName, int baudRate) { try { using (SerialPort port = new SerialPort()) { port.PortName = portName; port.BaudRate = baudRate; port.DataBits = 8; port.Parity = Parity.None; port.StopBits = StopBits.One; port.ReadTimeout = 500; port.WriteTimeout = 500; addLog($" >> BMS 테스트 시작: {portName} @ {baudRate}bps"); port.Open(); addLog($" >> BMS 포트 열기 성공: {portName}"); // BMS 테스트 명령 전송 (테스트 버튼과 동일한 명령) var testCmd = new List(); testCmd.Add(0xDD); testCmd.Add(0xA5); testCmd.Add(0x03); testCmd.Add(0x00); testCmd.Add(0xFF); testCmd.Add(0xFD); testCmd.Add(0x77); testCmd.Add(0x0D); port.Write(testCmd.ToArray(), 0, testCmd.Count); addLog($" >> BMS 명령 전송: {portName} - {BitConverter.ToString(testCmd.ToArray())}"); // 응답 대기 System.Threading.Thread.Sleep(300); if (port.BytesToRead > 0) { byte[] response = new byte[port.BytesToRead]; port.Read(response, 0, response.Length); addLog($" >> BMS 응답 수신: {portName} ({response.Length} bytes) - {BitConverter.ToString(response)}"); // BMS 프로토콜 확인 (STX=0xDD, ETX=0x77) if (response.Length >= 2 && response[0] == 0xDD && response[response.Length - 1] == 0x77) { addLog($" >> ✓✓✓ BMS 프로토콜 확인: {portName}"); port.Close(); return true; } else { addLog($" >> BMS 프로토콜 불일치: {portName}"); } } else { addLog($" >> BMS 응답 없음: {portName}"); } port.Close(); } } catch (Exception ex) { addLog($" >> BMS 테스트 오류: {portName} - {ex.Message}"); } return false; } private void toolStripButton1_Click(object sender, EventArgs e) { LoadSetting(); } private void toolStripButton2_Click(object sender, EventArgs e) { SaveSetting(); } } }