diff --git a/Cs_HMI/SubProject/EnigProtocol b/Cs_HMI/SubProject/EnigProtocol index 070aa84..e8cae2c 160000 --- a/Cs_HMI/SubProject/EnigProtocol +++ b/Cs_HMI/SubProject/EnigProtocol @@ -1 +1 @@ -Subproject commit 070aa848c96ec7097ae70b5a12f19f328fb6ae03 +Subproject commit e8cae2c4ee72b509f47a48bd7bc7d416d45e6a78 diff --git a/Cs_HMI/TestProject/Test_ACS/AppSettings.cs b/Cs_HMI/TestProject/Test_ACS/AppSettings.cs new file mode 100644 index 0000000..49ef15b --- /dev/null +++ b/Cs_HMI/TestProject/Test_ACS/AppSettings.cs @@ -0,0 +1,67 @@ +using System; +using System.IO; +using System.Windows.Forms; +using Newtonsoft.Json; + +namespace Test_ACS +{ + /// + /// 애플리케이션 설정을 관리하는 클래스 + /// + public class AppSettings + { + public string LastPort { get; set; } = "COM1"; + public string LastBaudRate { get; set; } = "9600"; + public string LastRFID { get; set; } = "0001"; + public string LastAlias { get; set; } = "CHARGER1"; + public int LastAGV { get; set; } = 11; + + private static string GetConfigFilePath() + { + // 실행 파일과 같은 폴더에 같은 이름으로 .config 파일 생성 + string exePath = Application.ExecutablePath; + string exeName = Path.GetFileNameWithoutExtension(exePath); + string configPath = Path.Combine(Path.GetDirectoryName(exePath), exeName + ".config"); + return configPath; + } + + /// + /// 설정 파일을 로드합니다 + /// + public static AppSettings Load() + { + try + { + string configPath = GetConfigFilePath(); + if (File.Exists(configPath)) + { + string json = File.ReadAllText(configPath); + return JsonConvert.DeserializeObject(json); + } + } + catch (Exception) + { + // 로드 실패 시 기본값 반환 + } + + return new AppSettings(); + } + + /// + /// 설정을 파일에 저장합니다 + /// + public void Save() + { + try + { + string configPath = GetConfigFilePath(); + string json = JsonConvert.SerializeObject(this, Formatting.Indented); + File.WriteAllText(configPath, json); + } + catch (Exception) + { + // 저장 실패 시 무시 + } + } + } +} diff --git a/Cs_HMI/TestProject/Test_ACS/CHANGELOG.md b/Cs_HMI/TestProject/Test_ACS/CHANGELOG.md new file mode 100644 index 0000000..4387e2b --- /dev/null +++ b/Cs_HMI/TestProject/Test_ACS/CHANGELOG.md @@ -0,0 +1,162 @@ +# Test_ACS 변경 이력 + +## v1.4.0 (2025-01-12) + +### 설정 파일 저장 방식 변경 +- **실행 폴더에 설정 파일 저장** + - 이전: 사용자 프로파일 폴더 (`%LOCALAPPDATA%`) + - 변경: 실행 파일과 같은 폴더에 `Test_ACS.config` 저장 + - 형식: JSON 형식으로 저장 (가독성 향상) + +### 코드 개선 +- `AppSettings` 클래스 신규 생성 + - JSON 직렬화/역직렬화 지원 + - 실행 파일 경로 자동 감지 + - 설정 파일 자동 생성 및 로드 +- Newtonsoft.Json 패키지 참조 추가 + +### 사용성 개선 +- 설정 파일을 직접 편집 가능 (JSON 형식) +- 프로그램 이동 시에도 설정 파일 함께 이동 +- 여러 버전 동시 사용 가능 (각 폴더별 독립 설정) + +--- + +## v1.3.0 (2025-01-12) + +### 신규 기능 +- **AGV 상태 실시간 표시** + - AGV 상태 그룹박스 추가 + - 8가지 AGV 상태 정보 표시: + - 모드 (수동/자동) + - 실행상태 (정지/실행/에러) + - 방향 (직진/좌회전/우회전/마크정지) + - 도달완료 (OFF/ON) + - 충전 상태 (OFF/ON) + - 카트 감지 (없음/있음/알 수 없음) + - 리프트 위치 (하강/상승/알 수 없음) + - 현재 RFID 태그 번호 (6자리) + - 상태별 색상 표시로 직관적 모니터링 + - AGV Status 메시지(cmd=3) 자동 수신 및 업데이트 + +### UI 개선 +- AGV 상태 그룹박스 추가 (2열 x 4행 레이아웃) +- 메인 폼 크기 확장 (539 → 588 픽셀) +- 상태별 색상 구분: + - 수동(파란색) / 자동(녹색) + - 정지(회색) / 실행(녹색) / 에러(빨간색) + - 충전중(주황색) + +### 프로토콜 업데이트 +- ENIGProtocol ReadMe.MD 수정 + - AGV Status 프로토콜 정의 명확화 (총 13바이트) + - LastTag 필드 크기 수정 (4바이트 → 6바이트) + - CurrentPath 필드 제거 (실제 구현과 일치) + +### 코드 개선 +- `UpdateAGVStatus()` 메서드 구현 +- Status 메시지 수신 처리 로직 추가 +- 스레드 안전성 보장 (InvokeRequired 체크) +- 데이터 유효성 검사 추가 + +--- + +## v1.2.0 (2025-01-12) + +### 신규 기능 +- **설정 자동 저장/불러오기 기능** + - 마지막 사용 COM 포트 자동 저장 + - 마지막 사용 보레이트 자동 저장 + - 마지막 입력 RFID 번호 자동 저장 + - 마지막 입력 별칭 자동 저장 + - 마지막 선택 AGV (11/12) 자동 저장 + - 프로그램 종료 시 자동 저장 + - 프로그램 시작 시 자동 불러오기 + +### 구현 상세 +- **Settings.settings 파일 확장** + - LastPort (string) + - LastBaudRate (string) + - LastRFID (string) + - LastAlias (string) + - LastAGV (int) + +- **자동 저장 이벤트** + - COM 포트 선택 변경 시 + - 보레이트 텍스트 변경 시 + - RFID 번호 텍스트 변경 시 + - 별칭 텍스트 변경 시 + - AGV 선택 변경 시 + - 프로그램 종료 시 (OnFormClosing) + +- **자동 불러오기** + - 프로그램 시작 시 LoadSettings() 호출 + - 포트 목록 로드 후 마지막 설정 적용 + +### 사용자 경험 개선 +- 프로그램 재실행 시 이전 설정 자동 복원 +- 설정 저장/불러오기 상태 로그 표시 +- 에러 발생 시 로그에 에러 메시지 표시 + +--- + +## v1.1.0 (2025-01-12) + +### 신규 기능 +- **GotoAlias 명령어 추가** + - 별칭(Alias)을 사용한 AGV 이동 명령 지원 + - RFID 번호 대신 의미 있는 이름으로 목적지 지정 가능 + - 예: "CHARGER1", "BUFFER2", "STATION_A" 등 + +### UI 개선 +- **별칭 입력 필드 추가** + - 위치: RFID 입력 필드 아래 + - 기본값: "CHARGER1" + +- **GotoAlias 버튼 추가** + - 위치: Goto 버튼 우측 + - 버튼명: "별칭 이동 (GotoAlias)" + +- **레이아웃 최적화** + - RFID 번호 입력: 라인 1 + - 별칭 입력: 라인 2 + - SetCurrent / GotoAlias 버튼: 라인 3 + - Goto / GotoAlias 버튼: 라인 4 + - Stop / Reset 버튼: 라인 5 + - MarkStop 버튼 및 체크박스: 라인 6 + - 수동 제어 / 리프트 제어: 하단 그룹 + +### 프로토콜 변경 +- **AGVCommandHE Enum 확장** + - GotoAlias = 107 추가 + - 데이터 형식: TargetID(2 hex) + Alias(ASCII string) + +### 코드 개선 +- `btnGotoAlias_Click` 이벤트 핸들러 구현 +- 별칭 유효성 검사 (빈 문자열 체크) +- ASCII → HEX 변환 로직 추가 + +### 문서 업데이트 +- README.md에 GotoAlias 사용법 추가 +- 프로토콜 명령어 표에 GotoAlias 추가 +- 버전 이력 업데이트 + +--- + +## v1.0.0 (2025-01-11) + +### 초기 릴리즈 +- ACS 시뮬레이터 기본 구현 +- ENIGProtocol 프로젝트 참조 +- RS232 통신 지원 +- 7가지 기본 ACS 명령어: + - SetCurrent (현재 위치 설정) + - Goto (RFID 이동) + - Stop (정지) + - Reset (에러 리셋) + - Manual (수동 제어) + - MarkStop (마크센서 정지) + - LiftControl (리프트 제어) +- AGV 선택 기능 (AGV1/AGV2) +- 로그 시스템 (TX/RX/Info 탭) +- COM 포트 관리 (연결/해제/새로고침) diff --git a/Cs_HMI/TestProject/Test_ACS/MainForm.Designer.cs b/Cs_HMI/TestProject/Test_ACS/MainForm.Designer.cs new file mode 100644 index 0000000..5e4ac2d --- /dev/null +++ b/Cs_HMI/TestProject/Test_ACS/MainForm.Designer.cs @@ -0,0 +1,828 @@ +namespace Test_ACS +{ + partial class MainForm + { + private System.ComponentModel.IContainer components = null; + + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + private void InitializeComponent() + { + this.grpConnection = new System.Windows.Forms.GroupBox(); + this.btnRefresh = new System.Windows.Forms.Button(); + this.btnConnect = new System.Windows.Forms.Button(); + this.txtBaudRate = new System.Windows.Forms.TextBox(); + this.cmbPort = new System.Windows.Forms.ComboBox(); + this.lblBaudRate = new System.Windows.Forms.Label(); + this.lblPort = new System.Windows.Forms.Label(); + this.grpAGV = new System.Windows.Forms.GroupBox(); + this.rbAGV2 = new System.Windows.Forms.RadioButton(); + this.rbAGV1 = new System.Windows.Forms.RadioButton(); + this.grpCommands = new System.Windows.Forms.GroupBox(); + this.grpLift = new System.Windows.Forms.GroupBox(); + this.btnLiftStop = new System.Windows.Forms.Button(); + this.btnLiftDown = new System.Windows.Forms.Button(); + this.btnLiftUp = new System.Windows.Forms.Button(); + this.grpManual = new System.Windows.Forms.GroupBox(); + this.btnManual = new System.Windows.Forms.Button(); + this.numRuntime = new System.Windows.Forms.NumericUpDown(); + this.lblRuntime = new System.Windows.Forms.Label(); + this.cmbSpeed = new System.Windows.Forms.ComboBox(); + this.lblSpeed = new System.Windows.Forms.Label(); + this.cmbDirection = new System.Windows.Forms.ComboBox(); + this.lblDirection = new System.Windows.Forms.Label(); + this.chkMarkStop = new System.Windows.Forms.CheckBox(); + this.btnMarkStop = new System.Windows.Forms.Button(); + this.btnReset = new System.Windows.Forms.Button(); + this.btnStop = new System.Windows.Forms.Button(); + this.btnGotoAlias = new System.Windows.Forms.Button(); + this.btnGoto = new System.Windows.Forms.Button(); + this.btnSetCurrent = new System.Windows.Forms.Button(); + this.txtAlias = new System.Windows.Forms.TextBox(); + this.lblAlias = new System.Windows.Forms.Label(); + this.txtRFID = new System.Windows.Forms.TextBox(); + this.lblRFID = new System.Windows.Forms.Label(); + this.grpLogs = new System.Windows.Forms.GroupBox(); + this.tabLogs = new System.Windows.Forms.TabControl(); + this.tabRX = new System.Windows.Forms.TabPage(); + this.txtRxLog = new System.Windows.Forms.TextBox(); + this.tabTX = new System.Windows.Forms.TabPage(); + this.txtTxLog = new System.Windows.Forms.TextBox(); + this.tabInfo = new System.Windows.Forms.TabPage(); + this.txtInfoLog = new System.Windows.Forms.TextBox(); + this.grpAGVStatus = new System.Windows.Forms.GroupBox(); + this.lblLastTagValue = new System.Windows.Forms.Label(); + this.lblLastTag = new System.Windows.Forms.Label(); + this.lblLiftStValue = new System.Windows.Forms.Label(); + this.lblLiftSt = new System.Windows.Forms.Label(); + this.lblCartStValue = new System.Windows.Forms.Label(); + this.lblCartSt = new System.Windows.Forms.Label(); + this.lblChargeStValue = new System.Windows.Forms.Label(); + this.lblChargeSt = new System.Windows.Forms.Label(); + this.lblInpositionValue = new System.Windows.Forms.Label(); + this.lblInposition = new System.Windows.Forms.Label(); + this.lblDirectionValue = new System.Windows.Forms.Label(); + this.lblRunStValue = new System.Windows.Forms.Label(); + this.lblRunSt = new System.Windows.Forms.Label(); + this.lblModeValue = new System.Windows.Forms.Label(); + this.lblMode = new System.Windows.Forms.Label(); + this.grpConnection.SuspendLayout(); + this.grpAGV.SuspendLayout(); + this.grpCommands.SuspendLayout(); + this.grpLift.SuspendLayout(); + this.grpManual.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numRuntime)).BeginInit(); + this.grpLogs.SuspendLayout(); + this.tabLogs.SuspendLayout(); + this.tabRX.SuspendLayout(); + this.tabTX.SuspendLayout(); + this.tabInfo.SuspendLayout(); + this.grpAGVStatus.SuspendLayout(); + this.SuspendLayout(); + // + // grpConnection + // + this.grpConnection.Controls.Add(this.btnRefresh); + this.grpConnection.Controls.Add(this.btnConnect); + this.grpConnection.Controls.Add(this.txtBaudRate); + this.grpConnection.Controls.Add(this.cmbPort); + this.grpConnection.Controls.Add(this.lblBaudRate); + this.grpConnection.Controls.Add(this.lblPort); + this.grpConnection.Location = new System.Drawing.Point(12, 12); + this.grpConnection.Name = "grpConnection"; + this.grpConnection.Size = new System.Drawing.Size(260, 120); + this.grpConnection.TabIndex = 0; + this.grpConnection.TabStop = false; + this.grpConnection.Text = "연결 설정"; + // + // btnRefresh + // + this.btnRefresh.Location = new System.Drawing.Point(179, 22); + this.btnRefresh.Name = "btnRefresh"; + this.btnRefresh.Size = new System.Drawing.Size(70, 23); + this.btnRefresh.TabIndex = 5; + this.btnRefresh.Text = "새로고침"; + this.btnRefresh.UseVisualStyleBackColor = true; + this.btnRefresh.Click += new System.EventHandler(this.btnRefresh_Click); + // + // btnConnect + // + this.btnConnect.Location = new System.Drawing.Point(15, 81); + this.btnConnect.Name = "btnConnect"; + this.btnConnect.Size = new System.Drawing.Size(234, 30); + this.btnConnect.TabIndex = 4; + this.btnConnect.Text = "연결"; + this.btnConnect.UseVisualStyleBackColor = true; + this.btnConnect.Click += new System.EventHandler(this.btnConnect_Click); + // + // txtBaudRate + // + this.txtBaudRate.Location = new System.Drawing.Point(85, 53); + this.txtBaudRate.Name = "txtBaudRate"; + this.txtBaudRate.Size = new System.Drawing.Size(164, 21); + this.txtBaudRate.TabIndex = 3; + this.txtBaudRate.Text = "9600"; + this.txtBaudRate.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; + this.txtBaudRate.TextChanged += new System.EventHandler(this.txtBaudRate_TextChanged); + // + // cmbPort + // + this.cmbPort.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbPort.FormattingEnabled = true; + this.cmbPort.Location = new System.Drawing.Point(85, 23); + this.cmbPort.Name = "cmbPort"; + this.cmbPort.Size = new System.Drawing.Size(88, 20); + this.cmbPort.TabIndex = 2; + this.cmbPort.SelectedIndexChanged += new System.EventHandler(this.cmbPort_SelectedIndexChanged); + // + // lblBaudRate + // + this.lblBaudRate.AutoSize = true; + this.lblBaudRate.Location = new System.Drawing.Point(15, 56); + this.lblBaudRate.Name = "lblBaudRate"; + this.lblBaudRate.Size = new System.Drawing.Size(57, 12); + this.lblBaudRate.TabIndex = 1; + this.lblBaudRate.Text = "보레이트:"; + // + // lblPort + // + this.lblPort.AutoSize = true; + this.lblPort.Location = new System.Drawing.Point(15, 26); + this.lblPort.Name = "lblPort"; + this.lblPort.Size = new System.Drawing.Size(66, 12); + this.lblPort.TabIndex = 0; + this.lblPort.Text = "COM 포트:"; + // + // grpAGV + // + this.grpAGV.Controls.Add(this.rbAGV2); + this.grpAGV.Controls.Add(this.rbAGV1); + this.grpAGV.Location = new System.Drawing.Point(278, 12); + this.grpAGV.Name = "grpAGV"; + this.grpAGV.Size = new System.Drawing.Size(200, 120); + this.grpAGV.TabIndex = 1; + this.grpAGV.TabStop = false; + this.grpAGV.Text = "AGV 선택"; + // + // rbAGV2 + // + this.rbAGV2.AutoSize = true; + this.rbAGV2.Location = new System.Drawing.Point(20, 60); + this.rbAGV2.Name = "rbAGV2"; + this.rbAGV2.Size = new System.Drawing.Size(95, 16); + this.rbAGV2.TabIndex = 1; + this.rbAGV2.Text = "AGV2 (ID:12)"; + this.rbAGV2.UseVisualStyleBackColor = true; + this.rbAGV2.CheckedChanged += new System.EventHandler(this.rbAGV2_CheckedChanged); + // + // rbAGV1 + // + this.rbAGV1.AutoSize = true; + this.rbAGV1.Checked = true; + this.rbAGV1.Location = new System.Drawing.Point(20, 30); + this.rbAGV1.Name = "rbAGV1"; + this.rbAGV1.Size = new System.Drawing.Size(95, 16); + this.rbAGV1.TabIndex = 0; + this.rbAGV1.TabStop = true; + this.rbAGV1.Text = "AGV1 (ID:11)"; + this.rbAGV1.UseVisualStyleBackColor = true; + this.rbAGV1.CheckedChanged += new System.EventHandler(this.rbAGV1_CheckedChanged); + // + // grpCommands + // + this.grpCommands.Controls.Add(this.grpLift); + this.grpCommands.Controls.Add(this.grpManual); + this.grpCommands.Controls.Add(this.chkMarkStop); + this.grpCommands.Controls.Add(this.btnMarkStop); + this.grpCommands.Controls.Add(this.btnReset); + this.grpCommands.Controls.Add(this.btnStop); + this.grpCommands.Controls.Add(this.btnGotoAlias); + this.grpCommands.Controls.Add(this.btnGoto); + this.grpCommands.Controls.Add(this.btnSetCurrent); + this.grpCommands.Controls.Add(this.txtAlias); + this.grpCommands.Controls.Add(this.lblAlias); + this.grpCommands.Controls.Add(this.txtRFID); + this.grpCommands.Controls.Add(this.lblRFID); + this.grpCommands.Location = new System.Drawing.Point(12, 138); + this.grpCommands.Name = "grpCommands"; + this.grpCommands.Size = new System.Drawing.Size(466, 312); + this.grpCommands.TabIndex = 2; + this.grpCommands.TabStop = false; + this.grpCommands.Text = "ACS 명령"; + // + // grpLift + // + this.grpLift.Controls.Add(this.btnLiftStop); + this.grpLift.Controls.Add(this.btnLiftDown); + this.grpLift.Controls.Add(this.btnLiftUp); + this.grpLift.Location = new System.Drawing.Point(240, 202); + this.grpLift.Name = "grpLift"; + this.grpLift.Size = new System.Drawing.Size(210, 100); + this.grpLift.TabIndex = 9; + this.grpLift.TabStop = false; + this.grpLift.Text = "리프트 제어"; + // + // btnLiftStop + // + this.btnLiftStop.Location = new System.Drawing.Point(135, 20); + this.btnLiftStop.Name = "btnLiftStop"; + this.btnLiftStop.Size = new System.Drawing.Size(60, 70); + this.btnLiftStop.TabIndex = 2; + this.btnLiftStop.Text = "정지"; + this.btnLiftStop.UseVisualStyleBackColor = true; + this.btnLiftStop.Click += new System.EventHandler(this.btnLiftStop_Click); + // + // btnLiftDown + // + this.btnLiftDown.Location = new System.Drawing.Point(70, 20); + this.btnLiftDown.Name = "btnLiftDown"; + this.btnLiftDown.Size = new System.Drawing.Size(60, 70); + this.btnLiftDown.TabIndex = 1; + this.btnLiftDown.Text = "하강"; + this.btnLiftDown.UseVisualStyleBackColor = true; + this.btnLiftDown.Click += new System.EventHandler(this.btnLiftDown_Click); + // + // btnLiftUp + // + this.btnLiftUp.Location = new System.Drawing.Point(5, 20); + this.btnLiftUp.Name = "btnLiftUp"; + this.btnLiftUp.Size = new System.Drawing.Size(60, 70); + this.btnLiftUp.TabIndex = 0; + this.btnLiftUp.Text = "상승"; + this.btnLiftUp.UseVisualStyleBackColor = true; + this.btnLiftUp.Click += new System.EventHandler(this.btnLiftUp_Click); + // + // grpManual + // + this.grpManual.Controls.Add(this.btnManual); + this.grpManual.Controls.Add(this.numRuntime); + this.grpManual.Controls.Add(this.lblRuntime); + this.grpManual.Controls.Add(this.cmbSpeed); + this.grpManual.Controls.Add(this.lblSpeed); + this.grpManual.Controls.Add(this.cmbDirection); + this.grpManual.Location = new System.Drawing.Point(15, 202); + this.grpManual.Name = "grpManual"; + this.grpManual.Size = new System.Drawing.Size(215, 100); + this.grpManual.TabIndex = 8; + this.grpManual.TabStop = false; + this.grpManual.Text = "수동 제어"; + // + // btnManual + // + this.btnManual.Location = new System.Drawing.Point(136, 16); + this.btnManual.Name = "btnManual"; + this.btnManual.Size = new System.Drawing.Size(73, 74); + this.btnManual.TabIndex = 6; + this.btnManual.Text = "수동\r\n이동\r\n실행"; + this.btnManual.UseVisualStyleBackColor = true; + this.btnManual.Click += new System.EventHandler(this.btnManual_Click); + // + // numRuntime + // + this.numRuntime.Location = new System.Drawing.Point(60, 66); + this.numRuntime.Maximum = new decimal(new int[] { + 255, + 0, + 0, + 0}); + this.numRuntime.Name = "numRuntime"; + this.numRuntime.Size = new System.Drawing.Size(70, 21); + this.numRuntime.TabIndex = 5; + this.numRuntime.Value = new decimal(new int[] { + 5, + 0, + 0, + 0}); + // + // lblRuntime + // + this.lblRuntime.AutoSize = true; + this.lblRuntime.Location = new System.Drawing.Point(3, 70); + this.lblRuntime.Name = "lblRuntime"; + this.lblRuntime.Size = new System.Drawing.Size(51, 12); + this.lblRuntime.TabIndex = 4; + this.lblRuntime.Text = "시간(초)"; + // + // cmbSpeed + // + this.cmbSpeed.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbSpeed.FormattingEnabled = true; + this.cmbSpeed.Items.AddRange(new object[] { + "느림", + "보통", + "빠름"}); + this.cmbSpeed.Location = new System.Drawing.Point(60, 41); + this.cmbSpeed.Name = "cmbSpeed"; + this.cmbSpeed.Size = new System.Drawing.Size(70, 20); + this.cmbSpeed.TabIndex = 3; + // + // lblSpeed + // + this.lblSpeed.AutoSize = true; + this.lblSpeed.Location = new System.Drawing.Point(14, 45); + this.lblSpeed.Name = "lblSpeed"; + this.lblSpeed.Size = new System.Drawing.Size(29, 12); + this.lblSpeed.TabIndex = 2; + this.lblSpeed.Text = "속도"; + // + // cmbDirection + // + this.cmbDirection.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbDirection.FormattingEnabled = true; + this.cmbDirection.Items.AddRange(new object[] { + "후진", + "전진", + "좌", + "우"}); + this.cmbDirection.Location = new System.Drawing.Point(60, 16); + this.cmbDirection.Name = "cmbDirection"; + this.cmbDirection.Size = new System.Drawing.Size(70, 20); + this.cmbDirection.TabIndex = 1; + // + // lblDirection + // + this.lblDirection.AutoSize = true; + this.lblDirection.Location = new System.Drawing.Point(10, 48); + this.lblDirection.Name = "lblDirection"; + this.lblDirection.Size = new System.Drawing.Size(33, 12); + this.lblDirection.TabIndex = 4; + this.lblDirection.Text = "방향:"; + // + // chkMarkStop + // + this.chkMarkStop.AutoSize = true; + this.chkMarkStop.Location = new System.Drawing.Point(240, 174); + this.chkMarkStop.Name = "chkMarkStop"; + this.chkMarkStop.Size = new System.Drawing.Size(76, 16); + this.chkMarkStop.TabIndex = 7; + this.chkMarkStop.Text = "정지 설정"; + this.chkMarkStop.UseVisualStyleBackColor = true; + // + // btnMarkStop + // + this.btnMarkStop.Location = new System.Drawing.Point(15, 167); + this.btnMarkStop.Name = "btnMarkStop"; + this.btnMarkStop.Size = new System.Drawing.Size(215, 30); + this.btnMarkStop.TabIndex = 6; + this.btnMarkStop.Text = "마크센서 정지"; + this.btnMarkStop.UseVisualStyleBackColor = true; + this.btnMarkStop.Click += new System.EventHandler(this.btnMarkStop_Click); + // + // btnReset + // + this.btnReset.Location = new System.Drawing.Point(240, 132); + this.btnReset.Name = "btnReset"; + this.btnReset.Size = new System.Drawing.Size(210, 30); + this.btnReset.TabIndex = 5; + this.btnReset.Text = "에러 리셋"; + this.btnReset.UseVisualStyleBackColor = true; + this.btnReset.Click += new System.EventHandler(this.btnReset_Click); + // + // btnStop + // + this.btnStop.Location = new System.Drawing.Point(15, 132); + this.btnStop.Name = "btnStop"; + this.btnStop.Size = new System.Drawing.Size(215, 30); + this.btnStop.TabIndex = 4; + this.btnStop.Text = "정지"; + this.btnStop.UseVisualStyleBackColor = true; + this.btnStop.Click += new System.EventHandler(this.btnStop_Click); + // + // btnGotoAlias + // + this.btnGotoAlias.Location = new System.Drawing.Point(286, 94); + this.btnGotoAlias.Name = "btnGotoAlias"; + this.btnGotoAlias.Size = new System.Drawing.Size(174, 30); + this.btnGotoAlias.TabIndex = 11; + this.btnGotoAlias.Text = "별칭 이동 (GotoAlias)"; + this.btnGotoAlias.UseVisualStyleBackColor = true; + this.btnGotoAlias.Click += new System.EventHandler(this.btnGotoAlias_Click); + // + // btnGoto + // + this.btnGoto.Location = new System.Drawing.Point(286, 23); + this.btnGoto.Name = "btnGoto"; + this.btnGoto.Size = new System.Drawing.Size(174, 30); + this.btnGoto.TabIndex = 3; + this.btnGoto.Text = "RFID 이동 (Goto)"; + this.btnGoto.UseVisualStyleBackColor = true; + this.btnGoto.Click += new System.EventHandler(this.btnGoto_Click); + // + // btnSetCurrent + // + this.btnSetCurrent.Location = new System.Drawing.Point(85, 57); + this.btnSetCurrent.Name = "btnSetCurrent"; + this.btnSetCurrent.Size = new System.Drawing.Size(195, 30); + this.btnSetCurrent.TabIndex = 2; + this.btnSetCurrent.Text = "현재 위치 설정 (SetCurrent)"; + this.btnSetCurrent.UseVisualStyleBackColor = true; + this.btnSetCurrent.Click += new System.EventHandler(this.btnSetCurrent_Click); + // + // txtAlias + // + this.txtAlias.Font = new System.Drawing.Font("Consolas", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.txtAlias.Location = new System.Drawing.Point(85, 96); + this.txtAlias.Name = "txtAlias"; + this.txtAlias.Size = new System.Drawing.Size(195, 26); + this.txtAlias.TabIndex = 10; + this.txtAlias.Text = "CHARGER1"; + this.txtAlias.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; + this.txtAlias.TextChanged += new System.EventHandler(this.txtAlias_TextChanged); + // + // lblAlias + // + this.lblAlias.AutoSize = true; + this.lblAlias.Location = new System.Drawing.Point(15, 103); + this.lblAlias.Name = "lblAlias"; + this.lblAlias.Size = new System.Drawing.Size(71, 12); + this.lblAlias.TabIndex = 9; + this.lblAlias.Text = "별칭(Alias):"; + // + // txtRFID + // + this.txtRFID.Font = new System.Drawing.Font("Consolas", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.txtRFID.Location = new System.Drawing.Point(85, 25); + this.txtRFID.Name = "txtRFID"; + this.txtRFID.Size = new System.Drawing.Size(195, 26); + this.txtRFID.TabIndex = 1; + this.txtRFID.Text = "0001"; + this.txtRFID.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; + this.txtRFID.TextChanged += new System.EventHandler(this.txtRFID_TextChanged); + // + // lblRFID + // + this.lblRFID.AutoSize = true; + this.lblRFID.Location = new System.Drawing.Point(15, 32); + this.lblRFID.Name = "lblRFID"; + this.lblRFID.Size = new System.Drawing.Size(63, 12); + this.lblRFID.TabIndex = 0; + this.lblRFID.Text = "RFID 번호:"; + // + // grpLogs + // + this.grpLogs.Controls.Add(this.tabLogs); + this.grpLogs.Location = new System.Drawing.Point(484, 12); + this.grpLogs.Name = "grpLogs"; + this.grpLogs.Size = new System.Drawing.Size(520, 564); + this.grpLogs.TabIndex = 3; + this.grpLogs.TabStop = false; + this.grpLogs.Text = "로그"; + // + // tabLogs + // + this.tabLogs.Controls.Add(this.tabRX); + this.tabLogs.Controls.Add(this.tabTX); + this.tabLogs.Controls.Add(this.tabInfo); + this.tabLogs.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabLogs.Location = new System.Drawing.Point(3, 17); + this.tabLogs.Name = "tabLogs"; + this.tabLogs.SelectedIndex = 0; + this.tabLogs.Size = new System.Drawing.Size(514, 544); + this.tabLogs.TabIndex = 0; + // + // tabRX + // + this.tabRX.Controls.Add(this.txtRxLog); + this.tabRX.Location = new System.Drawing.Point(4, 22); + this.tabRX.Name = "tabRX"; + this.tabRX.Padding = new System.Windows.Forms.Padding(3); + this.tabRX.Size = new System.Drawing.Size(506, 518); + this.tabRX.TabIndex = 1; + this.tabRX.Text = "수신 (RX)"; + this.tabRX.UseVisualStyleBackColor = true; + // + // txtRxLog + // + this.txtRxLog.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtRxLog.Font = new System.Drawing.Font("Consolas", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.txtRxLog.Location = new System.Drawing.Point(3, 3); + this.txtRxLog.Multiline = true; + this.txtRxLog.Name = "txtRxLog"; + this.txtRxLog.ReadOnly = true; + this.txtRxLog.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.txtRxLog.Size = new System.Drawing.Size(500, 512); + this.txtRxLog.TabIndex = 0; + // + // tabTX + // + this.tabTX.Controls.Add(this.txtTxLog); + this.tabTX.Location = new System.Drawing.Point(4, 22); + this.tabTX.Name = "tabTX"; + this.tabTX.Padding = new System.Windows.Forms.Padding(3); + this.tabTX.Size = new System.Drawing.Size(506, 469); + this.tabTX.TabIndex = 0; + this.tabTX.Text = "송신 (TX)"; + this.tabTX.UseVisualStyleBackColor = true; + // + // txtTxLog + // + this.txtTxLog.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtTxLog.Font = new System.Drawing.Font("Consolas", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.txtTxLog.Location = new System.Drawing.Point(3, 3); + this.txtTxLog.Multiline = true; + this.txtTxLog.Name = "txtTxLog"; + this.txtTxLog.ReadOnly = true; + this.txtTxLog.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.txtTxLog.Size = new System.Drawing.Size(500, 463); + this.txtTxLog.TabIndex = 0; + // + // tabInfo + // + this.tabInfo.Controls.Add(this.txtInfoLog); + this.tabInfo.Location = new System.Drawing.Point(4, 22); + this.tabInfo.Name = "tabInfo"; + this.tabInfo.Size = new System.Drawing.Size(506, 469); + this.tabInfo.TabIndex = 2; + this.tabInfo.Text = "정보"; + this.tabInfo.UseVisualStyleBackColor = true; + // + // txtInfoLog + // + this.txtInfoLog.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtInfoLog.Font = new System.Drawing.Font("Consolas", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.txtInfoLog.Location = new System.Drawing.Point(0, 0); + this.txtInfoLog.Multiline = true; + this.txtInfoLog.Name = "txtInfoLog"; + this.txtInfoLog.ReadOnly = true; + this.txtInfoLog.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.txtInfoLog.Size = new System.Drawing.Size(506, 469); + this.txtInfoLog.TabIndex = 0; + // + // grpAGVStatus + // + this.grpAGVStatus.Controls.Add(this.lblLastTagValue); + this.grpAGVStatus.Controls.Add(this.lblLastTag); + this.grpAGVStatus.Controls.Add(this.lblLiftStValue); + this.grpAGVStatus.Controls.Add(this.lblLiftSt); + this.grpAGVStatus.Controls.Add(this.lblCartStValue); + this.grpAGVStatus.Controls.Add(this.lblCartSt); + this.grpAGVStatus.Controls.Add(this.lblChargeStValue); + this.grpAGVStatus.Controls.Add(this.lblChargeSt); + this.grpAGVStatus.Controls.Add(this.lblInpositionValue); + this.grpAGVStatus.Controls.Add(this.lblInposition); + this.grpAGVStatus.Controls.Add(this.lblDirectionValue); + this.grpAGVStatus.Controls.Add(this.lblDirection); + this.grpAGVStatus.Controls.Add(this.lblRunStValue); + this.grpAGVStatus.Controls.Add(this.lblRunSt); + this.grpAGVStatus.Controls.Add(this.lblModeValue); + this.grpAGVStatus.Controls.Add(this.lblMode); + this.grpAGVStatus.Location = new System.Drawing.Point(12, 456); + this.grpAGVStatus.Name = "grpAGVStatus"; + this.grpAGVStatus.Size = new System.Drawing.Size(466, 120); + this.grpAGVStatus.TabIndex = 4; + this.grpAGVStatus.TabStop = false; + this.grpAGVStatus.Text = "AGV 상태"; + // + // lblLastTagValue + // + this.lblLastTagValue.AutoSize = true; + this.lblLastTagValue.Font = new System.Drawing.Font("Consolas", 9F, System.Drawing.FontStyle.Bold); + this.lblLastTagValue.Location = new System.Drawing.Point(310, 98); + this.lblLastTagValue.Name = "lblLastTagValue"; + this.lblLastTagValue.Size = new System.Drawing.Size(14, 14); + this.lblLastTagValue.TabIndex = 15; + this.lblLastTagValue.Text = "-"; + // + // lblLastTag + // + this.lblLastTag.AutoSize = true; + this.lblLastTag.Location = new System.Drawing.Point(240, 98); + this.lblLastTag.Name = "lblLastTag"; + this.lblLastTag.Size = new System.Drawing.Size(57, 12); + this.lblLastTag.TabIndex = 14; + this.lblLastTag.Text = "현재태그:"; + // + // lblLiftStValue + // + this.lblLiftStValue.AutoSize = true; + this.lblLiftStValue.Font = new System.Drawing.Font("굴림", 9F, System.Drawing.FontStyle.Bold); + this.lblLiftStValue.Location = new System.Drawing.Point(80, 98); + this.lblLiftStValue.Name = "lblLiftStValue"; + this.lblLiftStValue.Size = new System.Drawing.Size(12, 12); + this.lblLiftStValue.TabIndex = 13; + this.lblLiftStValue.Text = "-"; + // + // lblLiftSt + // + this.lblLiftSt.AutoSize = true; + this.lblLiftSt.Location = new System.Drawing.Point(10, 98); + this.lblLiftSt.Name = "lblLiftSt"; + this.lblLiftSt.Size = new System.Drawing.Size(45, 12); + this.lblLiftSt.TabIndex = 12; + this.lblLiftSt.Text = "리프트:"; + // + // lblCartStValue + // + this.lblCartStValue.AutoSize = true; + this.lblCartStValue.Font = new System.Drawing.Font("굴림", 9F, System.Drawing.FontStyle.Bold); + this.lblCartStValue.Location = new System.Drawing.Point(310, 73); + this.lblCartStValue.Name = "lblCartStValue"; + this.lblCartStValue.Size = new System.Drawing.Size(12, 12); + this.lblCartStValue.TabIndex = 11; + this.lblCartStValue.Text = "-"; + // + // lblCartSt + // + this.lblCartSt.AutoSize = true; + this.lblCartSt.Location = new System.Drawing.Point(240, 73); + this.lblCartSt.Name = "lblCartSt"; + this.lblCartSt.Size = new System.Drawing.Size(33, 12); + this.lblCartSt.TabIndex = 10; + this.lblCartSt.Text = "카트:"; + // + // lblChargeStValue + // + this.lblChargeStValue.AutoSize = true; + this.lblChargeStValue.Font = new System.Drawing.Font("굴림", 9F, System.Drawing.FontStyle.Bold); + this.lblChargeStValue.Location = new System.Drawing.Point(80, 73); + this.lblChargeStValue.Name = "lblChargeStValue"; + this.lblChargeStValue.Size = new System.Drawing.Size(12, 12); + this.lblChargeStValue.TabIndex = 9; + this.lblChargeStValue.Text = "-"; + // + // lblChargeSt + // + this.lblChargeSt.AutoSize = true; + this.lblChargeSt.Location = new System.Drawing.Point(10, 73); + this.lblChargeSt.Name = "lblChargeSt"; + this.lblChargeSt.Size = new System.Drawing.Size(33, 12); + this.lblChargeSt.TabIndex = 8; + this.lblChargeSt.Text = "충전:"; + // + // lblInpositionValue + // + this.lblInpositionValue.AutoSize = true; + this.lblInpositionValue.Font = new System.Drawing.Font("굴림", 9F, System.Drawing.FontStyle.Bold); + this.lblInpositionValue.Location = new System.Drawing.Point(310, 48); + this.lblInpositionValue.Name = "lblInpositionValue"; + this.lblInpositionValue.Size = new System.Drawing.Size(12, 12); + this.lblInpositionValue.TabIndex = 7; + this.lblInpositionValue.Text = "-"; + // + // lblInposition + // + this.lblInposition.AutoSize = true; + this.lblInposition.Location = new System.Drawing.Point(240, 48); + this.lblInposition.Name = "lblInposition"; + this.lblInposition.Size = new System.Drawing.Size(57, 12); + this.lblInposition.TabIndex = 6; + this.lblInposition.Text = "도달완료:"; + // + // lblDirectionValue + // + this.lblDirectionValue.AutoSize = true; + this.lblDirectionValue.Font = new System.Drawing.Font("굴림", 9F, System.Drawing.FontStyle.Bold); + this.lblDirectionValue.Location = new System.Drawing.Point(80, 48); + this.lblDirectionValue.Name = "lblDirectionValue"; + this.lblDirectionValue.Size = new System.Drawing.Size(12, 12); + this.lblDirectionValue.TabIndex = 5; + this.lblDirectionValue.Text = "-"; + // + // lblRunStValue + // + this.lblRunStValue.AutoSize = true; + this.lblRunStValue.Font = new System.Drawing.Font("굴림", 9F, System.Drawing.FontStyle.Bold); + this.lblRunStValue.Location = new System.Drawing.Point(310, 23); + this.lblRunStValue.Name = "lblRunStValue"; + this.lblRunStValue.Size = new System.Drawing.Size(12, 12); + this.lblRunStValue.TabIndex = 3; + this.lblRunStValue.Text = "-"; + // + // lblRunSt + // + this.lblRunSt.AutoSize = true; + this.lblRunSt.Location = new System.Drawing.Point(240, 23); + this.lblRunSt.Name = "lblRunSt"; + this.lblRunSt.Size = new System.Drawing.Size(57, 12); + this.lblRunSt.TabIndex = 2; + this.lblRunSt.Text = "실행상태:"; + // + // lblModeValue + // + this.lblModeValue.AutoSize = true; + this.lblModeValue.Font = new System.Drawing.Font("굴림", 9F, System.Drawing.FontStyle.Bold); + this.lblModeValue.Location = new System.Drawing.Point(80, 23); + this.lblModeValue.Name = "lblModeValue"; + this.lblModeValue.Size = new System.Drawing.Size(12, 12); + this.lblModeValue.TabIndex = 1; + this.lblModeValue.Text = "-"; + // + // lblMode + // + this.lblMode.AutoSize = true; + this.lblMode.Location = new System.Drawing.Point(10, 23); + this.lblMode.Name = "lblMode"; + this.lblMode.Size = new System.Drawing.Size(33, 12); + this.lblMode.TabIndex = 0; + this.lblMode.Text = "모드:"; + // + // MainForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1016, 588); + this.Controls.Add(this.grpAGVStatus); + this.Controls.Add(this.grpLogs); + this.Controls.Add(this.grpCommands); + this.Controls.Add(this.grpAGV); + this.Controls.Add(this.grpConnection); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.Name = "MainForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "ACS 시뮬레이터 - ENIG AGV 테스트"; + this.grpConnection.ResumeLayout(false); + this.grpConnection.PerformLayout(); + this.grpAGV.ResumeLayout(false); + this.grpAGV.PerformLayout(); + this.grpCommands.ResumeLayout(false); + this.grpCommands.PerformLayout(); + this.grpLift.ResumeLayout(false); + this.grpManual.ResumeLayout(false); + this.grpManual.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numRuntime)).EndInit(); + this.grpLogs.ResumeLayout(false); + this.tabLogs.ResumeLayout(false); + this.tabRX.ResumeLayout(false); + this.tabRX.PerformLayout(); + this.tabTX.ResumeLayout(false); + this.tabTX.PerformLayout(); + this.tabInfo.ResumeLayout(false); + this.tabInfo.PerformLayout(); + this.grpAGVStatus.ResumeLayout(false); + this.grpAGVStatus.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.GroupBox grpConnection; + private System.Windows.Forms.Button btnConnect; + private System.Windows.Forms.TextBox txtBaudRate; + private System.Windows.Forms.ComboBox cmbPort; + private System.Windows.Forms.Label lblBaudRate; + private System.Windows.Forms.Label lblPort; + private System.Windows.Forms.GroupBox grpAGV; + private System.Windows.Forms.RadioButton rbAGV2; + private System.Windows.Forms.RadioButton rbAGV1; + private System.Windows.Forms.GroupBox grpCommands; + private System.Windows.Forms.Button btnSetCurrent; + private System.Windows.Forms.TextBox txtRFID; + private System.Windows.Forms.Label lblRFID; + private System.Windows.Forms.Button btnGoto; + private System.Windows.Forms.Button btnStop; + private System.Windows.Forms.Button btnReset; + private System.Windows.Forms.Button btnMarkStop; + private System.Windows.Forms.CheckBox chkMarkStop; + private System.Windows.Forms.GroupBox grpManual; + private System.Windows.Forms.Button btnManual; + private System.Windows.Forms.NumericUpDown numRuntime; + private System.Windows.Forms.Label lblRuntime; + private System.Windows.Forms.ComboBox cmbSpeed; + private System.Windows.Forms.Label lblSpeed; + private System.Windows.Forms.ComboBox cmbDirection; + private System.Windows.Forms.GroupBox grpLift; + private System.Windows.Forms.Button btnLiftStop; + private System.Windows.Forms.Button btnLiftDown; + private System.Windows.Forms.Button btnLiftUp; + private System.Windows.Forms.GroupBox grpLogs; + private System.Windows.Forms.TabControl tabLogs; + private System.Windows.Forms.TabPage tabTX; + private System.Windows.Forms.TextBox txtTxLog; + private System.Windows.Forms.TabPage tabRX; + private System.Windows.Forms.TextBox txtRxLog; + private System.Windows.Forms.TabPage tabInfo; + private System.Windows.Forms.TextBox txtInfoLog; + private System.Windows.Forms.Button btnRefresh; + private System.Windows.Forms.Button btnGotoAlias; + private System.Windows.Forms.TextBox txtAlias; + private System.Windows.Forms.Label lblAlias; + private System.Windows.Forms.GroupBox grpAGVStatus; + private System.Windows.Forms.Label lblLastTagValue; + private System.Windows.Forms.Label lblLastTag; + private System.Windows.Forms.Label lblLiftStValue; + private System.Windows.Forms.Label lblLiftSt; + private System.Windows.Forms.Label lblCartStValue; + private System.Windows.Forms.Label lblCartSt; + private System.Windows.Forms.Label lblChargeStValue; + private System.Windows.Forms.Label lblChargeSt; + private System.Windows.Forms.Label lblInpositionValue; + private System.Windows.Forms.Label lblInposition; + private System.Windows.Forms.Label lblDirectionValue; + private System.Windows.Forms.Label lblDirection; + private System.Windows.Forms.Label lblRunStValue; + private System.Windows.Forms.Label lblRunSt; + private System.Windows.Forms.Label lblModeValue; + private System.Windows.Forms.Label lblMode; + } +} diff --git a/Cs_HMI/TestProject/Test_ACS/MainForm.cs b/Cs_HMI/TestProject/Test_ACS/MainForm.cs new file mode 100644 index 0000000..fcf365a --- /dev/null +++ b/Cs_HMI/TestProject/Test_ACS/MainForm.cs @@ -0,0 +1,515 @@ +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(); + InitializeComboBoxes(); + } + + private void InitializeComboBoxes() + { + // Direction combobox default + if (cmbDirection.Items.Count > 0) cmbDirection.SelectedIndex = 1; // 전진 + + // Speed combobox default + if (cmbSpeed.Items.Count > 0) cmbSpeed.SelectedIndex = 1; // 보통 + } + + 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) + if (e.ReceivedPacket.Command == (byte)ENIGProtocol.AGVCommandEH.Status) + { + UpdateAGVStatus(e.ReceivedPacket.Data); + } + } + + 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); + } + 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 rfid = txtRFID.Text.PadLeft(4, '0'); + var targetID = selectedAGV.ToString("X2"); + var dataStr = targetID + rfid; + SendCommand(AGVCommandHE.SetCurrent, dataStr); + } + + private void btnGoto_Click(object sender, EventArgs e) + { + // Goto: data = TargetID(2 hex) + RFID(4 hex) + var rfid = txtRFID.Text.PadLeft(4, '0'); + var targetID = selectedAGV.ToString("X2"); + var dataStr = targetID + rfid; + 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); + } + + 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) + { + // Manual: data = TargetID(2 hex) + Direction(1 byte) + Speed(1 byte) + Runtime(1 byte) + var targetID = selectedAGV.ToString("X2"); + var direction = (byte)cmbDirection.SelectedIndex; + var speed = (byte)cmbSpeed.SelectedIndex; + var runtime = (byte)numRuntime.Value; + + var dataBytes = new byte[] { direction, speed, runtime }; + 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"))); + AddLog($"TX: {hexString}\nCMD: {command} ({(byte)command:X2}), DATA: {dataHexString}", LogType.TX); + } + catch (Exception ex) + { + AddLog($"전송 실패: {ex.Message}", LogType.Error); + } + } + + private void UpdateAGVStatus(byte[] data) + { + if (data.Length < 13) + { + 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; + } + + // Direction[1]: 0=straight, 1=left, 2=right, 3=markstop + switch (data[2]) + { + case 0: + lblDirectionValue.Text = "직진"; + break; + case 1: + lblDirectionValue.Text = "좌회전"; + break; + case 2: + lblDirectionValue.Text = "우회전"; + break; + case 3: + lblDirectionValue.Text = "마크정지"; + break; + default: + lblDirectionValue.Text = "알 수 없음"; + break; + } + + // Inposition[1]: 0=off, 1=on + lblInpositionValue.Text = data[3] == 0 ? "OFF" : "ON"; + lblInpositionValue.ForeColor = data[3] == 0 ? Color.Gray : Color.Green; + + // ChargeSt[1]: 0=off, 1=on + lblChargeStValue.Text = data[4] == 0 ? "OFF" : "ON"; + lblChargeStValue.ForeColor = data[4] == 0 ? Color.Gray : Color.Orange; + + // CartSt[1]: 0=off, 1=on, 2=unknown + switch (data[5]) + { + case 0: + lblCartStValue.Text = "없음"; + lblCartStValue.ForeColor = Color.Gray; + break; + case 1: + lblCartStValue.Text = "있음"; + lblCartStValue.ForeColor = Color.Green; + break; + case 2: + lblCartStValue.Text = "알 수 없음"; + lblCartStValue.ForeColor = Color.Orange; + break; + default: + lblCartStValue.Text = "오류"; + lblCartStValue.ForeColor = Color.Red; + break; + } + + // LiftSt[1]: 0=down, 1=up, 2=unknown + switch (data[6]) + { + case 0: + lblLiftStValue.Text = "하강"; + lblLiftStValue.ForeColor = Color.Blue; + break; + case 1: + lblLiftStValue.Text = "상승"; + lblLiftStValue.ForeColor = Color.Green; + break; + case 2: + lblLiftStValue.Text = "알 수 없음"; + lblLiftStValue.ForeColor = Color.Orange; + break; + default: + lblLiftStValue.Text = "오류"; + lblLiftStValue.ForeColor = Color.Red; + break; + } + + // LastTag[6]: "000000" + string lastTag = Encoding.ASCII.GetString(data, 7, 6); + 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) + { + // 설정 저장 + SaveSettings(); + + if (serialPort != null && serialPort.IsOpen) + { + serialPort.Close(); + } + base.OnFormClosing(e); + } + } +} diff --git a/Cs_HMI/TestProject/Test_ACS/MainForm.resx b/Cs_HMI/TestProject/Test_ACS/MainForm.resx new file mode 100644 index 0000000..d58980a --- /dev/null +++ b/Cs_HMI/TestProject/Test_ACS/MainForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Cs_HMI/TestProject/Test_ACS/Program.cs b/Cs_HMI/TestProject/Test_ACS/Program.cs new file mode 100644 index 0000000..7029c4d --- /dev/null +++ b/Cs_HMI/TestProject/Test_ACS/Program.cs @@ -0,0 +1,16 @@ +using System; +using System.Windows.Forms; + +namespace Test_ACS +{ + static class Program + { + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainForm()); + } + } +} diff --git a/Cs_HMI/TestProject/Test_ACS/Properties/AssemblyInfo.cs b/Cs_HMI/TestProject/Test_ACS/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..6812718 --- /dev/null +++ b/Cs_HMI/TestProject/Test_ACS/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Test_ACS")] +[assembly: AssemblyDescription("ACS Simulator for ENIG AGV Testing")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("ENIG")] +[assembly: AssemblyProduct("Test_ACS")] +[assembly: AssemblyCopyright("Copyright © 2025")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d")] + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Cs_HMI/TestProject/Test_ACS/Properties/Resources.Designer.cs b/Cs_HMI/TestProject/Test_ACS/Properties/Resources.Designer.cs new file mode 100644 index 0000000..271cd0d --- /dev/null +++ b/Cs_HMI/TestProject/Test_ACS/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// 이 코드는 도구를 사용하여 생성되었습니다. +// 런타임 버전:4.0.30319.42000 +// +// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면 +// 이러한 변경 내용이 손실됩니다. +// +//------------------------------------------------------------------------------ + +namespace Test_ACS.Properties { + using System; + + + /// + /// 지역화된 문자열 등을 찾기 위한 강력한 형식의 리소스 클래스입니다. + /// + // 이 클래스는 ResGen 또는 Visual Studio와 같은 도구를 통해 StronglyTypedResourceBuilder + // 클래스에서 자동으로 생성되었습니다. + // 멤버를 추가하거나 제거하려면 .ResX 파일을 편집한 다음 /str 옵션을 사용하여 ResGen을 + // 다시 실행하거나 VS 프로젝트를 다시 빌드하십시오. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// 이 클래스에서 사용하는 캐시된 ResourceManager 인스턴스를 반환합니다. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Test_ACS.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 이 강력한 형식의 리소스 클래스를 사용하여 모든 리소스 조회에 대한 현재 스레드의 CurrentUICulture + /// 속성을 재정의합니다. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/Cs_HMI/TestProject/Test_ACS/Properties/Resources.resx b/Cs_HMI/TestProject/Test_ACS/Properties/Resources.resx new file mode 100644 index 0000000..4fdb1b6 --- /dev/null +++ b/Cs_HMI/TestProject/Test_ACS/Properties/Resources.resx @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Cs_HMI/TestProject/Test_ACS/Properties/Settings.Designer.cs b/Cs_HMI/TestProject/Test_ACS/Properties/Settings.Designer.cs new file mode 100644 index 0000000..e3262da --- /dev/null +++ b/Cs_HMI/TestProject/Test_ACS/Properties/Settings.Designer.cs @@ -0,0 +1,86 @@ +//------------------------------------------------------------------------------ +// +// 이 코드는 도구를 사용하여 생성되었습니다. +// 런타임 버전:4.0.30319.42000 +// +// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면 +// 이러한 변경 내용이 손실됩니다. +// +//------------------------------------------------------------------------------ + +namespace Test_ACS.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("COM1")] + public string LastPort { + get { + return ((string)(this["LastPort"])); + } + set { + this["LastPort"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("9600")] + public string LastBaudRate { + get { + return ((string)(this["LastBaudRate"])); + } + set { + this["LastBaudRate"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0001")] + public string LastRFID { + get { + return ((string)(this["LastRFID"])); + } + set { + this["LastRFID"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("CHARGER1")] + public string LastAlias { + get { + return ((string)(this["LastAlias"])); + } + set { + this["LastAlias"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("11")] + public int LastAGV { + get { + return ((int)(this["LastAGV"])); + } + set { + this["LastAGV"] = value; + } + } + } +} diff --git a/Cs_HMI/TestProject/Test_ACS/Properties/Settings.settings b/Cs_HMI/TestProject/Test_ACS/Properties/Settings.settings new file mode 100644 index 0000000..def4ca8 --- /dev/null +++ b/Cs_HMI/TestProject/Test_ACS/Properties/Settings.settings @@ -0,0 +1,23 @@ + + + + + + + + COM1 + + + 9600 + + + 0001 + + + CHARGER1 + + + 11 + + + diff --git a/Cs_HMI/TestProject/Test_ACS/README.md b/Cs_HMI/TestProject/Test_ACS/README.md new file mode 100644 index 0000000..dfcb23b --- /dev/null +++ b/Cs_HMI/TestProject/Test_ACS/README.md @@ -0,0 +1,242 @@ +# Test_ACS - ACS 시뮬레이터 + +ENIG AGV 프로젝트를 위한 ACS(중앙제어시스템) 시뮬레이터입니다. + +## 프로젝트 개요 + +ACS는 AGV에게 명령을 내리는 중앙 제어 시스템입니다. 이 프로그램은 실제 ACS 대신 테스트 목적으로 AGV에 명령을 보낼 수 있는 시뮬레이터입니다. + +### 통신 방식 +- **프로토콜**: ENIG Protocol (ENIGProtocol 프로젝트 사용) +- **통신**: RS232 (Xbee 모듈) +- **ACS Xbee ID**: 0 +- **AGV Xbee ID**: + - AGV1: 11 + - AGV2: 12 + +## 프로젝트 구조 + +``` +Test_ACS/ +├── Test_ACS.csproj # 프로젝트 파일 +├── Program.cs # 진입점 +├── MainForm.cs # 메인 폼 로직 +├── MainForm.Designer.cs # 메인 폼 디자이너 +├── MainForm.resx # 메인 폼 리소스 +├── Properties/ # 프로젝트 속성 +│ ├── AssemblyInfo.cs +│ ├── Resources.Designer.cs +│ ├── Resources.resx +│ ├── Settings.Designer.cs +│ └── Settings.settings +└── app.config # 애플리케이션 설정 +``` + +## 빌드 방법 + +1. Visual Studio 2022 또는 MSBuild 사용 +2. .NET Framework 4.8 필요 +3. ENIGProtocol 프로젝트 참조 필요 + +```bash +# Visual Studio에서 빌드 +- 솔루션 탐색기에서 Test_ACS 프로젝트 선택 +- 빌드 > 솔루션 빌드 + +# 또는 MSBuild 사용 +msbuild Test_ACS.csproj /p:Configuration=Debug +``` + +## 사용 방법 + +### 1. 연결 설정 + +1. **COM 포트 선택**: Xbee 모듈이 연결된 COM 포트 선택 +2. **보레이트 입력**: 기본값 9600 (Xbee 설정에 맞춰 조정) +3. **연결 버튼 클릭**: 포트 연결 + +**자동 저장**: 포트와 보레이트는 변경 시 자동으로 저장되며, 다음 실행 시 자동으로 불러옵니다. + +### 2. AGV 선택 + +- **AGV1 (ID:11)**: 첫 번째 AGV 선택 +- **AGV2 (ID:12)**: 두 번째 AGV 선택 + +**자동 저장**: 선택한 AGV는 자동으로 저장되며, 다음 실행 시 자동으로 선택됩니다. + +### 3. 명령 전송 + +#### SetCurrent (현재 위치 설정) +- **RFID 번호 입력**: 예) 0001 +- **SetCurrent 버튼 클릭**: AGV의 현재 위치를 지정한 RFID로 설정 +- **자동 저장**: 입력한 RFID 번호는 자동으로 저장됩니다. + +#### Goto (RFID 이동 명령) +- **RFID 번호 입력**: 목표 위치 RFID (예: 0001) +- **Goto 버튼 클릭**: AGV를 지정한 RFID 위치로 이동 +- **자동 저장**: 입력한 RFID 번호는 자동으로 저장됩니다. + +#### GotoAlias (별칭 이동 명령) +- **별칭(Alias) 입력**: 목표 위치 별칭 (예: CHARGER1, BUFFER2, STATION_A) +- **GotoAlias 버튼 클릭**: AGV를 지정한 별칭 위치로 이동 +- **참고**: 별칭은 AGV에 미리 등록된 이름으로, RFID 대신 의미 있는 이름 사용 +- **자동 저장**: 입력한 별칭은 자동으로 저장됩니다. + +#### Stop (정지) +- **Stop 버튼 클릭**: AGV 즉시 정지 + +#### Reset (에러 리셋) +- **Reset 버튼 클릭**: AGV 에러 상태 초기화 + +#### Manual (수동 제어) +- **방향 선택**: 후진, 전진, 좌회전, 우회전 +- **속도 선택**: 느림, 보통, 빠름 +- **시간(초) 입력**: 이동 시간 +- **수동 이동 실행 버튼 클릭** + +#### MarkStop (마크센서 정지) +- **정지 설정 체크**: 마크센서 정지 활성화/비활성화 +- **마크센서 정지 버튼 클릭** + +#### LiftControl (리프트 제어) +- **리프트 상승**: 리프트 올리기 +- **리프트 하강**: 리프트 내리기 +- **리프트 정지**: 리프트 동작 중지 + +## AGV 상태 확인 + +프로그램 하단의 "AGV 상태" 그룹박스에서 실시간 AGV 상태를 확인할 수 있습니다: + +- **모드**: 수동/자동 +- **실행상태**: 정지/실행/에러 +- **방향**: 직진/좌회전/우회전/마크정지 +- **도달완료**: 목적지 도달 여부 (OFF/ON) +- **충전**: 충전 상태 (OFF/ON) +- **카트**: 카트 감지 상태 (없음/있음/알 수 없음) +- **리프트**: 리프트 위치 (하강/상승/알 수 없음) +- **현재태그**: 마지막으로 인식한 RFID 태그 번호 + +상태는 AGV가 주기적으로 전송하는 Status 메시지(cmd=3)를 수신하여 자동으로 업데이트됩니다. + +## 로그 확인 + +프로그램 우측에 3개의 탭으로 로그를 확인할 수 있습니다: + +- **송신 (TX)**: ACS에서 AGV로 보낸 명령 +- **수신 (RX)**: AGV에서 ACS로 받은 응답 +- **정보**: 연결 상태, 설정 저장/불러오기, 에러 메시지 + +## 설정 저장 위치 + +모든 설정은 실행 파일과 같은 폴더에 저장됩니다: +- **위치**: `Test_ACS.exe`와 같은 폴더의 `Test_ACS.config` 파일 +- **형식**: JSON 형식으로 저장 +- **저장 항목**: + - 마지막 사용 COM 포트 + - 마지막 사용 보레이트 + - 마지막 입력 RFID 번호 + - 마지막 입력 별칭 + - 마지막 선택 AGV (11 또는 12) +- **자동 저장**: 설정 변경 시 자동 저장 +- **자동 불러오기**: 프로그램 시작 시 자동 불러오기 + +예시 설정 파일 (`Test_ACS.config`): +```json +{ + "LastPort": "COM3", + "LastBaudRate": "9600", + "LastRFID": "0001", + "LastAlias": "CHARGER1", + "LastAGV": 11 +} +``` + +## 프로토콜 상세 + +### 패킷 구조 +``` +STX (0x02) + Length + ID + Command + Data + CRC16 (2 bytes) + ETX (0x03) +``` + +### ACS 명령어 (AGVCommandHE) + +| 명령 | 코드 | 데이터 형식 | 설명 | +|------|------|-------------|------| +| Goto | 100 | TargetID(2) + RFID(4) | RFID로 목표 위치로 이동 | +| Stop | 101 | TargetID(2) | 정지 | +| Reset | 102 | TargetID(2) | 에러 리셋 | +| SetCurrent | 103 | TargetID(2) + RFID(4) | 현재 위치 설정 | +| Manual | 104 | TargetID(2) + Direction(1) + Speed(1) + Runtime(1) | 수동 이동 | +| MarkStop | 105 | TargetID(2) + MarkStop(1) | 마크센서 정지 | +| LiftControl | 106 | TargetID(2) + LiftCommand(1) | 리프트 제어 | +| **GotoAlias** | **107** | **TargetID(2) + Alias(ASCII)** | **별칭으로 목표 위치로 이동** | + +### 데이터 상세 + +#### Direction (Manual 명령) +- 0: 후진 (Back) +- 1: 전진 (Forward) +- 2: 좌회전 (Left) +- 3: 우회전 (Right) + +#### Speed (Manual 명령) +- 0: 느림 (Slow) +- 1: 보통 (Normal) +- 2: 빠름 (Fast) + +#### LiftCommand (LiftControl 명령) +- 0: 정지 (Stop) +- 1: 상승 (Up) +- 2: 하강 (Down) + +## 참고 파일 + +- **프로토콜 정의**: `SubProject\EnigProtocol\enigprotocol\Commands.cs` +- **프로토콜 구현**: `SubProject\EnigProtocol\enigprotocol\EEProtocol.cs` +- **AGV 수신 처리**: `Project\StateMachine\_Xbee.cs` + +## 개발자 노트 + +### 프로젝트 참조 +- ENIGProtocol 프로젝트를 참조하여 프로토콜 기능 사용 +- ProjectReference GUID: {499d8912-4b96-41e5-a70d-cfe797883d65} + +### 주요 클래스 +- `EEProtocol`: 패킷 생성 및 파싱 +- `SerialPort`: RS232 통신 관리 +- `MainForm`: UI 및 명령 전송 로직 + +### 디버깅 팁 +1. 로그 탭에서 송수신 패킷의 HEX 값 확인 +2. CRC 에러 발생 시 프로토콜 버전 확인 +3. 응답이 없을 경우 Xbee 모듈 설정 확인 (PAN ID: 46A5, Channel: 17) + +## 라이센스 + +ENIG AGV 프로젝트의 일부로, 내부 테스트 용도로 사용됩니다. + +## 버전 이력 + +- v1.4.0 (2025-01-12): 설정 파일 저장 방식 변경 + - 실행 폴더에 JSON 형식으로 설정 저장 (Test_ACS.config) + - 설정 파일 직접 편집 가능 + - 프로그램 이동 시 설정 파일도 함께 이동 + +- v1.3.0 (2025-01-12): AGV 상태 실시간 표시 기능 추가 + - AGV 상태 그룹박스 추가 (모드, 실행상태, 방향, 도달완료, 충전, 카트, 리프트, 현재태그) + - Status 메시지(cmd=3) 자동 수신 및 UI 업데이트 + - 상태별 색상 표시로 직관적 모니터링 + - 프로토콜 문서 업데이트 (LastTag 6바이트, CurrentPath 제거) + +- v1.2.0 (2025-01-12): 설정 자동 저장/불러오기 + - COM 포트, 보레이트, RFID, 별칭, AGV 선택 자동 저장 + - 프로그램 시작 시 마지막 설정 자동 복원 + +- v1.1.0 (2025-01-12): GotoAlias 명령어 추가 + - 별칭(Alias) 기반 이동 명령 지원 + - UI 레이아웃 최적화 + +- v1.0.0 (2025-01-11): 초기 버전 생성 + - ACS 시뮬레이터 기본 기능 + - ENIGProtocol 연동 + - 7가지 ACS 명령어 지원 (Goto, Stop, Reset, SetCurrent, Manual, MarkStop, LiftControl) diff --git a/Cs_HMI/TestProject/Test_ACS/Test_ACS.csproj b/Cs_HMI/TestProject/Test_ACS/Test_ACS.csproj new file mode 100644 index 0000000..5323343 --- /dev/null +++ b/Cs_HMI/TestProject/Test_ACS/Test_ACS.csproj @@ -0,0 +1,92 @@ + + + + + Debug + AnyCPU + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D} + WinExe + Test_ACS + Test_ACS + v4.8 + 512 + true + + + + AnyCPU + true + full + false + ..\..\..\..\..\..\Amkor\AGV4\ + DEBUG;TRACE + prompt + 4 + false + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + ..\..\SubProject\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + Form + + + MainForm.cs + + + + + True + True + Resources.resx + + + True + True + Settings.settings + + + MainForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + {499d8912-4b96-41e5-a70d-cfe797883d65} + ENIGProtocol + + + + \ No newline at end of file diff --git a/Cs_HMI/TestProject/Test_ACS/app.config b/Cs_HMI/TestProject/Test_ACS/app.config new file mode 100644 index 0000000..1634906 --- /dev/null +++ b/Cs_HMI/TestProject/Test_ACS/app.config @@ -0,0 +1,30 @@ + + + + +
+ + + + + + + + + COM1 + + + 9600 + + + 0001 + + + CHARGER1 + + + 11 + + + + diff --git a/Cs_HMI/TestProject/Test_ACS/packages.config b/Cs_HMI/TestProject/Test_ACS/packages.config new file mode 100644 index 0000000..efd7b64 --- /dev/null +++ b/Cs_HMI/TestProject/Test_ACS/packages.config @@ -0,0 +1,4 @@ + + + +