Files
backuppc 6e54633c08 fix
2025-11-10 14:43:47 +09:00

716 lines
26 KiB
C#

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";
}
/// <summary>
/// 시리얼 포트 목록을 새로고침하여 콤보박스에 추가
/// </summary>
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}");
}
}
/// <summary>
/// 설정파일에서 데이터를 읽어서 컨트롤에 설정한다
/// </summary>
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}");
}
}
/// <summary>
/// 현재 설정된 값을 확인하고 셋팅파일에 기록한다
/// </summary>
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<byte>();
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<Task> tasks = new List<Task>();
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("===== 포트 자동 검색 완료 =====");
}
/// <summary>
/// 발견된 포트를 UI에 적용
/// </summary>
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 포트를 찾지 못했습니다");
}
}
/// <summary>
/// AGV 포트인지 테스트 (btTestAGV_Click 코드 사용)
/// </summary>
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;
}
/// <summary>
/// BMS 포트인지 테스트 (btTestBMS_Click 코드 사용)
/// </summary>
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<byte>();
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();
}
}
}