using System; using System.Drawing; using System.IO.Ports; using System.Linq; using System.Text; using System.Windows.Forms; using ENIG; using ENIGProtocol; namespace Test_ACS { public partial class MainForm : Form { private SerialPort serialPort; private EEProtocol protocol; private byte selectedAGV = 11; // 기본값: AGV1 (11) private AppSettings settings; public MainForm() { InitializeComponent(); InitializeProtocol(); LoadPortList(); } private void InitializeProtocol() { protocol = new EEProtocol(); protocol.OnDataReceived += Protocol_OnDataReceived; protocol.OnMessage += Protocol_OnMessage; serialPort = new SerialPort(); serialPort.ReadTimeout = 2000; serialPort.WriteTimeout = 1000; serialPort.DataReceived += SerialPort_DataReceived; } private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { var buffer = new byte[serialPort.BytesToRead]; serialPort.Read(buffer, 0, buffer.Length); protocol.ProcessReceivedData(buffer); } private void Protocol_OnDataReceived(object sender, EEProtocol.DataEventArgs e) { var hexstrRaw = string.Join(" ", e.ReceivedPacket.RawData.Select(b => b.ToString("X2"))); var hexstr = string.Join(" ", e.ReceivedPacket.Data.Select(b => b.ToString("X2"))); var cmd = e.ReceivedPacket.Command.ToString("X2"); var id = e.ReceivedPacket.ID.ToString("X2"); AddLog($"RX: {hexstrRaw}\nID:{id}, CMD:{cmd}, DATA:{hexstr}", LogType.RX); // AGV 상태 수신 처리 (cmd = 3) var device = e.ReceivedPacket.ID; var command = (ENIGProtocol.AGVCommandEH)e.ReceivedPacket.Command; switch (command) { case AGVCommandEH.Status: UpdateAGVStatus(e.ReceivedPacket.Data); break; case AGVCommandEH.Error: var errorcode = (AGVErrorCode)e.ReceivedPacket.Data[0]; var errorMessage = System.Text.Encoding.UTF8.GetString(e.ReceivedPacket.Data, 1, e.ReceivedPacket.Data.Length - 1); AddLog($"Error Received : {errorcode} ID:{e.ReceivedPacket.ID} MSG:{errorMessage}", LogType.Info); break; default: AddLog($"unknown command:{command}", LogType.Error); break; } } private void Protocol_OnMessage(object sender, EEProtocol.MessageEventArgs e) { AddLog(e.Message, e.IsError ? LogType.Error : LogType.Info); } private void LoadPortList() { cmbPort.Items.Clear(); foreach (var port in SerialPort.GetPortNames()) { cmbPort.Items.Add(port); } // 마지막 설정 불러오기 LoadSettings(); } private void LoadSettings() { try { // 설정 파일 로드 settings = AppSettings.Load(); // 포트 설정 if (!string.IsNullOrEmpty(settings.LastPort) && cmbPort.Items.Contains(settings.LastPort)) { cmbPort.SelectedItem = settings.LastPort; } else if (cmbPort.Items.Count > 0) { cmbPort.SelectedIndex = 0; } // 보레이트 설정 txtBaudRate.Text = settings.LastBaudRate; // RFID 번호 설정 txtRFID.Text = settings.LastRFID; // 별칭 설정 txtAlias.Text = settings.LastAlias; // AGV 선택 설정 if (settings.LastAGV == 11) { rbAGV1.Checked = true; } else if (settings.LastAGV == 12) { rbAGV2.Checked = true; } AddLog("설정을 불러왔습니다.", LogType.Info); } catch (Exception ex) { settings = new AppSettings(); AddLog($"설정 불러오기 실패: {ex.Message}", LogType.Error); } } private void SaveSettings() { try { if (settings == null) settings = new AppSettings(); settings.LastPort = cmbPort.Text; settings.LastBaudRate = txtBaudRate.Text; settings.LastRFID = txtRFID.Text; settings.LastAlias = txtAlias.Text; settings.LastAGV = selectedAGV; settings.Save(); } catch (Exception ex) { AddLog($"설정 저장 실패: {ex.Message}", LogType.Error); } } private void btnConnect_Click(object sender, EventArgs e) { if (serialPort.IsOpen) { serialPort.Close(); btnConnect.Text = "연결"; AddLog("포트 닫힘", LogType.Info); } else { try { serialPort.PortName = cmbPort.Text; serialPort.BaudRate = int.Parse(txtBaudRate.Text); serialPort.Open(); btnConnect.Text = "해제"; AddLog($"{serialPort.PortName}:{serialPort.BaudRate} 연결됨", LogType.Info); SaveSettings(); } catch (Exception ex) { AddLog($"연결 실패: {ex.Message}", LogType.Error); } } } private void btnRefresh_Click(object sender, EventArgs e) { LoadPortList(); } private void cmbPort_SelectedIndexChanged(object sender, EventArgs e) { //SaveSettings(); } private void txtBaudRate_TextChanged(object sender, EventArgs e) { //SaveSettings(); } private void txtRFID_TextChanged(object sender, EventArgs e) { //SaveSettings(); } private void txtAlias_TextChanged(object sender, EventArgs e) { //SaveSettings(); } private void rbAGV1_CheckedChanged(object sender, EventArgs e) { if (rbAGV1.Checked) { selectedAGV = 11; //SaveSettings(); } } private void rbAGV2_CheckedChanged(object sender, EventArgs e) { if (rbAGV2.Checked) { selectedAGV = 12; //SaveSettings(); } } private void btnSetCurrent_Click(object sender, EventArgs e) { // SetCurrent: data = TargetID(2 hex) + RFID(4 hex) var targetID = selectedAGV.ToString("X2"); var rfidBytes = Encoding.ASCII.GetBytes(txtRFID.Text.PadLeft(4, '0')); var rfidHex = string.Join("", rfidBytes.Select(b => b.ToString("X2"))); var dataStr = targetID + rfidHex; SendCommand(AGVCommandHE.SetCurrent, dataStr); } private void btnGoto_Click(object sender, EventArgs e) { // Goto: data = TargetID(2 hex) + RFID(4 hex) var targetID = selectedAGV.ToString("X2"); var rfidBytes = Encoding.ASCII.GetBytes(txtRFID.Text.PadLeft(4, '0')); var rfidHex = string.Join("", rfidBytes.Select(b => b.ToString("X2"))); var dataStr = targetID + rfidHex; SendCommand(AGVCommandHE.Goto, dataStr); } private void btnGotoAlias_Click(object sender, EventArgs e) { // GotoAlias: data = TargetID(2 hex) + Alias(ASCII string) var alias = txtAlias.Text.Trim(); if (string.IsNullOrEmpty(alias)) { AddLog("별칭을 입력하세요.", LogType.Error); return; } var targetID = selectedAGV.ToString("X2"); var aliasBytes = Encoding.ASCII.GetBytes(alias); var aliasHex = string.Join("", aliasBytes.Select(b => b.ToString("X2"))); var dataStr = targetID + aliasHex; SendCommand(AGVCommandHE.GotoAlias, dataStr); SaveSettings(); } private void btnStop_Click(object sender, EventArgs e) { // Stop: data = TargetID(2 hex) var targetID = selectedAGV.ToString("X2"); SendCommand(AGVCommandHE.Stop, targetID); } private void btnReset_Click(object sender, EventArgs e) { // Reset: data = TargetID(2 hex) var targetID = selectedAGV.ToString("X2"); SendCommand(AGVCommandHE.Reset, targetID); } private void btnManual_Click(object sender, EventArgs e) { var but = sender as Button; if (but.Tag == null) return; var tagstr = but.Tag.ToString(); var targetID = selectedAGV.ToString("X2"); var direction = byte.Parse(tagstr);// //back var speed = (byte)0;// cmbSpeed.SelectedIndex; if (radSpdM.Checked) speed = 1; else if (radSpdH.Checked) speed = 2; var dataBytes = new byte[] { direction, speed }; var dataStr = targetID + string.Join("", dataBytes.Select(b => b.ToString("X2"))); SendCommand(AGVCommandHE.Manual, dataStr); } private void btnMarkStop_Click(object sender, EventArgs e) { // MarkStop: data = TargetID(2 hex) + MarkStop(1 byte) var targetID = selectedAGV.ToString("X2"); var markStop = chkMarkStop.Checked ? "01" : "00"; SendCommand(AGVCommandHE.MarkStop, targetID + markStop); } private void btnLiftUp_Click(object sender, EventArgs e) { SendLiftCommand(1); // Up } private void btnLiftDown_Click(object sender, EventArgs e) { SendLiftCommand(2); // Down } private void btnLiftStop_Click(object sender, EventArgs e) { SendLiftCommand(0); // Stop } private void SendLiftCommand(byte liftCmd) { // LiftControl: data = TargetID(2 hex) + LiftCommand(1 byte) var targetID = selectedAGV.ToString("X2"); var dataStr = targetID + liftCmd.ToString("X2"); SendCommand(AGVCommandHE.LiftControl, dataStr); } private void SendCommand(AGVCommandHE command, string dataHexString) { if (!serialPort.IsOpen) { AddLog("포트가 연결되지 않았습니다.", LogType.Error); return; } try { // Hex 문자열을 byte 배열로 변환 byte[] dataBytes = new byte[dataHexString.Length / 2]; for (int i = 0; i < dataBytes.Length; i++) { dataBytes[i] = Convert.ToByte(dataHexString.Substring(i * 2, 2), 16); } // 패킷 생성 (ACS ID = 0) byte[] packet = protocol.CreatePacket(0, (byte)command, dataBytes); // 전송 serialPort.Write(packet, 0, packet.Length); var hexString = string.Join(" ", packet.Select(b => b.ToString("X2"))); //정보를 조금더 추출한다. var Sender = packet[2]; if (Sender == 0x00) { var Receiver = packet[4]; var strdata = System.Text.Encoding.Default.GetString(dataBytes, 1, dataBytes.Length - 1);// (dataHexString.Substring(2), 16); AddLog($"{hexString}|{command}({(byte)command:X2}) From:{Sender} To:{Receiver} => {dataHexString} STR:{strdata}", LogType.TX); } else { AddLog($"{hexString}|{command}({(byte)command:X2}) From:{Sender} => {dataHexString}", LogType.TX); } } catch (Exception ex) { AddLog($"전송 실패: {ex.Message}", LogType.Error); } } private void UpdateAGVStatus(byte[] data) { if (data.Length < 12) { AddLog($"AGV 상태 데이터 길이 오류: {data.Length} bytes", LogType.Error); return; } if (InvokeRequired) { BeginInvoke(new Action(() => UpdateAGVStatus(data))); return; } try { // Mode[1]: 0=manual, 1=auto lblModeValue.Text = data[0] == 0 ? "수동" : "자동"; lblModeValue.ForeColor = data[0] == 0 ? Color.Blue : Color.Green; // RunSt[1]: 0=stop, 1=run, 2=error switch (data[1]) { case 0: lblRunStValue.Text = "정지"; lblRunStValue.ForeColor = Color.Gray; break; case 1: lblRunStValue.Text = "실행"; lblRunStValue.ForeColor = Color.Green; break; case 2: lblRunStValue.Text = "에러"; lblRunStValue.ForeColor = Color.Red; break; default: lblRunStValue.Text = "알 수 없음"; lblRunStValue.ForeColor = Color.Black; break; } // Mot Direction[1]: 0=forward, 1:backward switch (data[2]) { case 0: lblDirectionValue.Text = "전진"; break; case 1: lblDirectionValue.Text = "후진"; break; default: lblDirectionValue.Text = "??"; break; } // Direction[1]: 0=straight, 1=left, 2=right, 3=markstop switch (data[3]) { case 0: lblDirectionValue.Text += "/직진"; break; case 1: lblDirectionValue.Text += "/좌회전"; break; case 2: lblDirectionValue.Text += "/우회전"; break; default: lblDirectionValue.Text += "/??"; break; } // Inposition[1]: 0=off, 1=on lblInpositionValue.Text = data[4] == 0 ? "OFF" : "ON"; lblInpositionValue.ForeColor = data[4] == 0 ? Color.Gray : Color.Green; // ChargeSt[1]: 0=off, 1=on lblChargeStValue.Text = data[5] == 0 ? "OFF" : "ON"; lblChargeStValue.ForeColor = data[5] == 0 ? Color.Gray : Color.Orange; // CartSt[1]: 0=off, 1=on, 2=unknown switch (data[6]) { case 0: lblCartStValue.Text = "없음"; lblCartStValue.ForeColor = Color.Gray; break; case 1: lblCartStValue.Text = "있음"; lblCartStValue.ForeColor = Color.Green; break; default: lblCartStValue.Text = "??"; lblCartStValue.ForeColor = Color.Red; break; } // LiftSt[1]: 0=down, 1=up, 2=unknown switch (data[7]) { case 0: lblLiftStValue.Text = "하강"; lblLiftStValue.ForeColor = Color.Blue; break; case 1: lblLiftStValue.Text = "상승"; lblLiftStValue.ForeColor = Color.Green; break; default: lblLiftStValue.Text = "??"; lblLiftStValue.ForeColor = Color.Red; break; } string lastTag = Encoding.ASCII.GetString(data, 8, 4); lblLastTagValue.Text = lastTag; lblLastTagValue.ForeColor = Color.Black; } catch (Exception ex) { AddLog($"AGV 상태 업데이트 실패: {ex.Message}", LogType.Error); } } private enum LogType { TX, RX, Info, Error } private void AddLog(string message, LogType type) { if (InvokeRequired) { BeginInvoke(new Action(() => AddLog(message, type))); return; } var timestamp = DateTime.Now.ToString("HH:mm:ss"); var logMessage = $"[{timestamp}] {message}\r\n"; switch (type) { case LogType.TX: txtTxLog.AppendText(logMessage); txtTxLog.ScrollToCaret(); break; case LogType.RX: txtRxLog.AppendText(logMessage); txtRxLog.ScrollToCaret(); break; case LogType.Info: txtInfoLog.AppendText(logMessage); txtInfoLog.ScrollToCaret(); break; case LogType.Error: txtInfoLog.ForeColor = Color.Red; txtInfoLog.AppendText(logMessage); txtInfoLog.ForeColor = Color.Black; txtInfoLog.ScrollToCaret(); break; } } protected override void OnFormClosing(FormClosingEventArgs e) { if (serialPort != null && serialPort.IsOpen) { serialPort.Close(); } base.OnFormClosing(e); } private void btAMove_Click(object sender, EventArgs e) { var targetID = selectedAGV.ToString("X2"); byte Motdirection = 0;// (byte)cmbMotDirection.SelectedIndex; if (radForw.Checked) Motdirection = 1; byte Magdirection = 0; if (radLeft.Checked) Magdirection = 1; else if (radRight.Checked) Magdirection = 2; byte speed = 0;// (byte)cmbAutoSpeed.SelectedIndex; if (radSpdM.Checked) speed = 1; else if (radSpdH.Checked) speed = 2; var dataBytes = new byte[] { Motdirection, Magdirection, speed }; var dataStr = targetID + string.Join("", dataBytes.Select(b => b.ToString("X2"))); SendCommand(AGVCommandHE.AutoMove, dataStr); } private void button2_Click(object sender, EventArgs e) { // Stop: data = TargetID(2 hex) var targetID = selectedAGV.ToString("X2"); SendCommand(AGVCommandHE.Stop, targetID); } private void groupBox2_Enter(object sender, EventArgs e) { } } }