using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; using COMM; using System.CodeDom; using AR; using Project.StateMachine; using System.Security.Cryptography.X509Certificates; using AGVNavigationCore.Models; using System.IO; namespace Project { public partial class fMain : Form { double freeSpaceRate = 0.0; //뷰어용화면 ViewForm.fAuto form_auto = null; ViewForm.fManual form_manu = null; ViewForm.fIO form_zlift = null; ViewForm.fFlag form_flag = null; ViewForm.fAgv form_agv = null; ViewForm.fBms form_bms = null; Dialog.fLog form_log = null; bool remoteClose = false; bool forceClose = false; readonly usbdetect.DriveDetector usbdet; public fMain() { InitializeComponent(); VAR.Init(128); PUB.initCore(); UTIL.MessgeBoxLegacyMode = false; this.KeyDown += (s1, e1) => { if (e1.KeyCode == Keys.Escape) this.Close(); else if (e1.KeyCode == Keys.F1 && e1.Control && e1.Shift) { } else if (e1.KeyCode == Keys.F5) btAutoRun.PerformClick(); else if (e1.KeyCode == Keys.F9) btAutoRun.PerformClick(); else if (e1.KeyCode == Keys.F11 && System.Diagnostics.Debugger.IsAttached) { // F11: 모든 스레드 상태 덤프 DumpAllThreadsState(); } if (DateTime.Now > PUB.LastInputTime) PUB.LastInputTime = DateTime.Now; }; usbdet = new usbdetect.DriveDetector(this); usbdet.DeviceArrived += Usbdet_DeviceArrived; usbdet.DeviceRemoved += Usbdet_DeviceRemoved; this.panTopMenu.MouseMove += LbTitle_MouseMove; this.panTopMenu.MouseUp += LbTitle_MouseUp; this.panTopMenu.MouseDown += LbTitle_MouseDown; this.panTopMenu.DoubleClick += LbTitle_DoubleClick; this.ctlPos1.ItemClick += CtlPos1_ItemClick; this.MouseMove += (s1, e1) => { if (DateTime.Now > PUB.LastInputTime) PUB.LastInputTime = DateTime.Now; }; this.FormClosing += __Closing; if (PUB.setting.FullScreen) this.WindowState = FormWindowState.Maximized; } protected override void WndProc(ref Message m) { base.WndProc(ref m); if (usbdet != null) { usbdet.WndProc(ref m); } } private void Usbdet_DeviceRemoved(object sender, usbdetect.DriveDetectorEventArgs e) { //throw new NotImplementedException(); Console.WriteLine(e.Drive); } private void Usbdet_DeviceArrived(object sender, usbdetect.DriveDetectorEventArgs e) { //throw new NotImplementedException(); using (var fUpdate = new Dialog.fUpdateForm(e.Drive)) if (fUpdate.ShowDialog() == DialogResult.Yes) { //종료한다 remoteClose = true; this.Close(); } } /// /// 상단 위치 표시기의 대상 위치값을 표시 합니다 /// /// /// private void Display_Position_TargetPosSet(object sender, EventArgs e) { //대상위치가 설정되었다x this.ctlPos1.SetTargetPosition(PUB.Result.TargetPos); this.ctlPos1.Invalidate(); } private void __Closing(object sender, FormClosingEventArgs e) { // 장치 관리 태스크는 _STEP_CLOSING_START에서 종료됨 PUB.popup.needClose = true; if (remoteClose == true) { PUB.log.Add("패치로 인해 프로그램을 종료 합니다"); PUB.sm.SetNewStep(eSMStep.IDLE); //_Close_Start(); _STEP_CLOSING_START(PUB.sm.Step); PUB.sm.Stop(); var patchfile = new System.IO.FileInfo(System.IO.Path.Combine(UTIL.CurrentPath, "SWPatch.exe")); if (patchfile.Exists == false) { UTIL.MsgE("패치파일(" + patchfile.Name + ")이 없습니다.\n프로그램을 다시 실행해주세요", true); } else { UTIL.RunProcess(patchfile.FullName); } } else if (forceClose == true) { PUB.log.Add("강제 종료"); PUB.sm.SetNewStep(eSMStep.IDLE); //_Close_Start(); _STEP_CLOSING_START(PUB.sm.Step); PUB.sm.Stop(); } else { if (PUB.sm.Step == eSMStep.RUN) { UTIL.MsgE("동작 중에는 종료 할 수 없습니다."); e.Cancel = true; return; } if (PUB.sm.Step < eSMStep.CLOSING) { var rlt = UTIL.MsgQ("종료하시겠습니까"); if (rlt == System.Windows.Forms.DialogResult.Yes) { //비젼의 측정상태가 변경된 경우 PUB.sm.SetNewStep(eSMStep.CLOSING, true); } e.Cancel = true; return; //_Close_Start(); } } } private Dictionary FlagMap = new Dictionary(); private void __Load(object sender, EventArgs e) { this.Text = Application.ProductName + " ver " + Application.ProductVersion; //this.lbTitle.Text = this.Text; PUB.init(); //public initialize if (PUB.setting.FullScreen) this.WindowState = FormWindowState.Maximized; // PUB.log.RaiseMsg += log_RaiseMsg; this.Show(); PUB.Result.TargetPosSet += Display_Position_TargetPosSet; this.ctlPos1.ClearData(); this.ctlPos1.Invalidate(); //lbDM1.Text = ""; //sbBatteryLv.Text = ""; btHome.Text = "홈"; btChargeA.Text = "자동충전"; VAR.STR[eVarString.SWVersion] = Application.ProductVersion; Application.DoEvents(); //setting dio events this.IOState.ItemClick += gridView2_ItemClick; //PUB.flag.ValueChanged += Flag_ValueChagned; VAR.BOOL.PropertyChanged += BOOL_PropertyChanged; /////모터용 pLC //PUB.PLC = new arDev.FakePLC(); //PUB.PLC.ValueChanged += PLC_DioChanged; //PUB.PLC.FlagChanged += PLC_FlagChanged; //PUB.PLC.Message += PLC_Message; //지그비통신 PUB.XBE = new Device.Xbee(); PUB.XBE.MessageReceived += XBE_MessageReceived; PUB.XBE.ProtocReceived += XBE_ProtocReceived; //HWState.setTitle(1, 3, Pub.setting.Port_Xbee); //HWState.setTitle(1, 0, Pub.setting.Address_RFID); //AGV PUB.AGV = new arDev.Narumi(); PUB.AGV.Message += AGV_Message; PUB.AGV.DataReceive += AGV_DataReceive; //배터리관리시스템 PUB.BMS = new arDev.BMS(); PUB.BMS.BMSDataReceive += Bms_BMSDataReceive; PUB.BMS.BMSCellDataReceive += BMS_BMSCellDataReceive; PUB.BMS.Message += Bms_Message; PUB.BMS.ChargeDetect += BMS_ChargeDetect; PUB.BMS.ScanInterval = PUB.setting.interval_bms;// //디버그메세지 출력용 소켓 PUB.sock_debug = new Device.Socket(); PUB.sock_debug.GetMessage += socket_GetMessage; PUB.sock_debug.RecvMessage += socket_RecvMessage; PUB.sock_debug.SendMessage += socket_SendMessage; //clear Resultclear(); //충전이 시작되지 않도록 기본값을 넣는다 VAR.TIME[eVarTime.ChargeTry] = DateTime.Now; VAR.TIME[eVarTime.ChargeSearch] = DateTime.Now; PUB.sm.SetMsgOptOff(); //모든 메세지출력을 해제한다. (이벤트는 동작함) PUB.log.Add("State Machine Start"); try { PUB.sm = new StateMachine.StateMachine(); PUB.log.Add("StateMachine", $"객체 생성 완료 - Type: {PUB.sm.GetType().FullName}"); // StateMachine 객체의 속성 확인 var smType = PUB.sm.GetType(); var properties = smType.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); PUB.log.Add("StateMachine", $"Public Properties: {properties.Length}개"); var methods = smType.GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly); PUB.log.Add("StateMachine", $"Public Methods: {methods.Length}개"); } catch (Exception ex) { PUB.log.AddE($"StateMachine 객체 생성 실패: {ex.Message}"); PUB.log.AddE($"StackTrace: {ex.StackTrace}"); throw; } PUB.sm.StepChanged += sm_StepChanged; PUB.log.Add("StateMachine", "StepChanged 이벤트 등록 완료"); PUB.sm.Message += sm_Message; PUB.log.Add("StateMachine", "Message 이벤트 등록 완료"); PUB.sm.Running += sm_Running; PUB.log.Add("StateMachine", "Running 이벤트 등록 완료"); PUB.sm.SPS += sm_SPS; PUB.log.Add("StateMachine", "SPS 이벤트 등록 완료"); PUB.sm.Start(); PUB.log.Add("StateMachine", $"Start() 호출 완료 - IsThreadRun:{PUB.sm.IsThreadRun}"); // 스레드 시작 대기 (최대 3초) for (int i = 0; i < 30; i++) { System.Threading.Thread.Sleep(100); if (PUB.sm.IsThreadRun) { PUB.log.Add("StateMachine", $"스레드 시작 확인됨 ({i * 100}ms 소요)"); break; } } if (!PUB.sm.IsThreadRun) { PUB.log.AddE( "경고: 3초 대기 후에도 스레드가 시작되지 않음!"); System.Windows.Forms.MessageBox.Show( "상태머신 스레드가 시작되지 않았습니다.\n" + "로그 파일을 확인하세요.", "오류", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); } tmDisplay.Tick += tmDisplay_Tick; tmDisplay.Start(); //start Display PUB.log.Add("Display", "Display Timer 시작 완료"); this.btDebug.Visible = System.Diagnostics.Debugger.IsAttached || PUB.setting.UseDebugMode; PUB.log.Add("Program Start"); //수량표시 PUB.counter.PropertyChanged += (s1, e1) => Update_Count(); Update_Count(); PUB.log.Add("프로그램 실행 기록 추가"); PUB.CheckNRegister3(Application.ProductName, "chi", Application.ProductVersion); if (PUB.setting.StartLog) MenuLog.PerformClick(); PUB.AddEEDB("프로그램 시작"); } #region "Mouse Form Move" private Boolean fMove = false; private Point MDownPos; private void LbTitle_DoubleClick(object sender, EventArgs e) { if (this.WindowState == FormWindowState.Maximized) this.WindowState = FormWindowState.Normal; else this.WindowState = FormWindowState.Maximized; } private void LbTitle_MouseMove(object sender, MouseEventArgs e) { if (fMove) { Point offset = new Point(e.X - MDownPos.X, e.Y - MDownPos.Y); this.Left += offset.X; this.Top += offset.Y; //offset = new Point(0, 0); } } private void LbTitle_MouseUp(object sender, MouseEventArgs e) { fMove = false; } private void LbTitle_MouseDown(object sender, MouseEventArgs e) { MDownPos = new Point(e.X, e.Y); fMove = true; } #endregion void socket_SendMessage(object sender, Device.Socket.SocketMessageEventArgs e) { //핑로그가 꺼져있는 상황일때 핑로그는 로그를 기록하지 않는다. 181205 if (!PUB.setting.Log_Ping && e.Message.Type == Device.Socket.eType.REPLY && e.Message.SType == Device.Socket.eSType.STATUS) { return; } PUB.log.Add("WS << " + e.rawData); } void socket_RecvMessage(object sender, Device.Socket.SocketMessageEventArgs e) { if (e.Message.isError) PUB.log.AddE("WS >> " + e.Message.ErrorMessage + ",RAW=" + e.rawData); else { switch (e.Message.Type) { case Device.Socket.eType.REQUEST: if (e.Message.SType == Device.Socket.eSType.STATUS) { var packet = PUB.sock_debug.makeReplyStatus(Device.Socket.eStatus.IDLE); var rlt = PUB.sock_debug.Send(PUB.setting.Asset, Device.Socket.eType.REPLY, Device.Socket.eSType.STATUS, packet); if (!rlt) { PUB.log.AddE("Status Reply Error"); } else { if (PUB.setting.Log_Ping) PUB.log.AddI("Server Status Reply OK"); } } else { PUB.log.Add("WS >> " + e.rawData); } break; } } } void socket_GetMessage(object sender, Device.Socket.MesasgeEventArgs e) { if (e.isError) PUB.log.AddE("WS:" + e.Message); else PUB.log.Add("WS:" + e.Message); } delegate void SelectModelHandler(string modelName); void SelectModelM(string modelName) { if (this.InvokeRequired) { this.Invoke(new SelectModelHandler(SelectModelM), new object[] { modelName }); return; } } void func_sw_start(bool Prompt = false) { if(PUB.sm.Step == eSMStep.SYNC) { UTIL.MsgE("초기화 중에는 사용할 수 없습니다\n초기화가 완료 된 후 시도하세요"); return; } if (VAR.BOOL[eVarBool.FLAG_AUTORUN] == false) //자동상태가 아니라면 { PUB.AGV.AGVErrorReset(); if (Prompt) { if (UTIL.MsgQ("AGV상태를 자동으로 전환 할까요?") != DialogResult.Yes) return; } //충전상태확인 if (PUB.CheckManualChargeMode() == false) return; PUB.popup.needClose = true; PUB.sm.bPause = false; PUB.sm.SetNewStep(eSMStep.RUN); PUB.Speak(Lang.자동전환); } else { PUB.AGV.AGVCharge(PUB.setting.ChargerID, false); //230425 수동전환하면 충전 해제한다 PUB.sm.SetNewStep(eSMStep.IDLE); PUB.Speak(Lang.작업종료); } } void Resultclear() { PUB.Result.Clear(); PUB.log.AddI("Clear Resultclear"); } void CheckFreeSpace() { try { if (PUB.path.FullName.StartsWith("\\")) { SSInfo.setTitle(0, 1, "UNC Path Detected"); SSInfo.setValue(0, 1, 2); } else { double freeSpaceRate_ = PUB.GetFreeSpace(); this.freeSpaceRate = freeSpaceRate_; SSInfo.setTitle(0, 1, "FREE(" + PUB.path.FullName.Substring(0, 1) + ") : " + freeSpaceRate.ToString("N2") + "%"); if (freeSpaceRate < PUB.setting.AutoDeleteThreshold) { VAR.BOOL[eVarBool.MINSPACE] = true; SSInfo.setValue(0, 1, 2); //lbFreeSpace.ForeColor = Color.Tomato; } else { VAR.BOOL[eVarBool.MINSPACE] = false; SSInfo.setValue(0, 1, 3);// lbFreeSpace.ForeColor = Color.White; } } //Pub.log.Add("남은디스크공간확인:" + freeSpaceRate.ToString("N2") + "%"); 190129 } catch (Exception ex) { SSInfo.setTitle(0, 1, "FREE SPACE : -ERROR-"); PUB.log.AddE("check free space : " + ex.Message); } } private void CtlPos1_ItemClick(object sender, CtlPos.ItemClickEventArgs e) { if (VAR.BOOL[eVarBool.FLAG_CHARGEONM] == true) { UTIL.MsgE("수동 충전 상태이므로 진행 할 수 없습니다"); return; } if (VAR.BOOL[eVarBool.EMERGENCY] == true) { var dlgE = UTIL.MsgQ("비상정지 상태입니다.\n오류를 먼저 소거하고 실행 할까요?"); if (dlgE == DialogResult.Yes) PUB.AGV.AGVErrorReset(); else return; } //위치표시 컨트롤에서 아이템을 클릭했다 var dlg = new Dialog.fJobSelect(); PUB.log.Add("사용자 임의 위치 클릭 : " + e.Item.Title); switch (e.Item.Position) { case ePosition.QA: //아이템을 가지고 있었으니 하차를 해야한다 if (VAR.BOOL[eVarBool.ITEMON]) { dlg.setMessage("작업 실행\n" + "(QA) 위치로 하차를 진행 할까요?\n" + "대상위치 : QA\n" + "현재위치 : " + PUB.Result.CurrentPos.ToString()); if (dlg.ShowDialog() == DialogResult.Yes) { //아이템이 있으면 하차이고 없으면 상차이다 PUB.sm.ClearRunStep(); PUB.Result.TargetPos = ePosition.QA; PUB.sm.SetNewRunStep(ERunStep.GODOWN); PUB.sm.SetNewStep(eSMStep.RUN); } } else { dlg.setMessage("작업 실행\n" + "(QA) 위치로 이동을 실행 할까요?\n" + "대상위치 : QA\n" + "현재위치 : " + PUB.Result.CurrentPos.ToString()); if (dlg.ShowDialog() == DialogResult.Yes) { //아이템이 있으면 하차이고 없으면 상차이다 PUB.sm.ClearRunStep(); PUB.Result.TargetPos = ePosition.QA; PUB.sm.SetNewRunStep(ERunStep.GOHOME); PUB.sm.SetNewStep(eSMStep.RUN); } } break; case ePosition.QC: //아이템을 가지고 있었으니 하차를 해야한다 if (VAR.BOOL[eVarBool.ITEMON]) { dlg.setMessage("작업 실행\n" + "홈(QC) 위치로 하차를 진행 할까요?\n" + "대상위치 : QC\n" + "현재위치 : " + PUB.Result.CurrentPos.ToString()); if (dlg.ShowDialog() == DialogResult.Yes) { //아이템이 있으면 하차이고 없으면 상차이다 PUB.sm.ClearRunStep(); PUB.Result.TargetPos = ePosition.QC; PUB.sm.SetNewRunStep(ERunStep.GODOWN); PUB.sm.SetNewStep(eSMStep.RUN); } } else { dlg.setMessage("작업 실행\n" + "홈(QC) 위치로 이동을 실행 할까요?\n" + "대상위치 : QC\n" + "현재위치 : " + PUB.Result.CurrentPos.ToString()); if (dlg.ShowDialog() == DialogResult.Yes) { //아이템이 있으면 하차이고 없으면 상차이다 PUB.sm.ClearRunStep(); PUB.Result.TargetPos = ePosition.QC; PUB.sm.SetNewRunStep(ERunStep.GOHOME); PUB.sm.SetNewStep(eSMStep.RUN); } } break; case ePosition.F1: //FVI영역은 모두 상차이동 dlg.setMessage("작업 실행\n" + "(상차) 작업을 실행 할까요?\n" + "대상위치 : FVI-1\n" + "현재위치 : " + PUB.Result.CurrentPos.ToString()); if (dlg.ShowDialog() == DialogResult.Yes) { PUB.Result.TargetPos = ePosition.F1; PUB.sm.SetNewRunStep(ERunStep.GOUP); PUB.sm.SetNewStep(eSMStep.RUN); } break; case ePosition.F2: dlg.setMessage("작업 실행\n" + "(상차) 작업을 실행 할까요?\n" + "대상위치 : FVI-2\n" + "현재위치 : " + PUB.Result.CurrentPos.ToString()); if (dlg.ShowDialog() == DialogResult.Yes) { PUB.Result.TargetPos = ePosition.F2; PUB.sm.SetNewRunStep(ERunStep.GOUP); PUB.sm.SetNewStep(eSMStep.RUN); } break; case ePosition.F3: dlg.setMessage("작업 실행\n" + "(상차) 작업을 실행 할까요?\n" + "대상위치 : FVI-3\n" + "현재위치 : " + PUB.Result.CurrentPos.ToString()); if (dlg.ShowDialog() == DialogResult.Yes) { PUB.Result.TargetPos = ePosition.F3; PUB.sm.SetNewRunStep(ERunStep.GOUP); PUB.sm.SetNewStep(eSMStep.RUN); //VAR.BOOL[eVarBool.FLAG_AUTORUN] = true; } break; case ePosition.F4: dlg.setMessage("작업 실행\n" + "(상차) 작업을 실행 할까요?\n" + "대상위치 : FVI-4\n" + "현재위치 : " + PUB.Result.CurrentPos.ToString()); if (dlg.ShowDialog() == DialogResult.Yes) { PUB.Result.TargetPos = ePosition.F4; PUB.sm.SetNewRunStep(ERunStep.GOUP); PUB.sm.SetNewStep(eSMStep.RUN); } break; } if (dlg != null) dlg.Dispose(); } private void demoRunToolStripMenuItem_Click(object sender, EventArgs e) { PUB.Result.JobEndTime = DateTime.Now; } private void setZValidToolStripMenuItem_Click(object sender, EventArgs e) { PUB.log.Add("user set z-home set on"); } private void errorToolStripMenuItem_Click(object sender, EventArgs e) { PUB.popup.setMessage("error\berror message"); } private void informationToolStripMenuItem_Click(object sender, EventArgs e) { PUB.popup.setMessage("info\ninformation message", MessageWindow.eWindowType.information); } private void attentionToolStripMenuItem_Click(object sender, EventArgs e) { PUB.popup.setMessage("attention\nattention message", MessageWindow.eWindowType.attention); } private void btShowManual_Click(object sender, EventArgs e) { string file = System.IO.Path.Combine(UTIL.CurrentPath, "manual.pdf"); if (System.IO.File.Exists(file) == false) { UTIL.MsgE("사용설명서 파일이 존재하지 않습니다\n" + "문의 : T8567 (장비기술 1파트)\n" + "파일명 : " + file); return; } UTIL.RunExplorer(file); } private void btClose_Click(object sender, EventArgs e) { this.Close(); } private void arLabel5_Click(object sender, EventArgs e) { //SetScreen(form_setup); var popmsg = PUB.popup.Visible; PUB.popup.Visible = false; VAR.BOOL[eVarBool.FLAG_SETUP] = true; using (var f = new fSetup()) if (f.ShowDialog() == DialogResult.OK) { //AGV정보 싱크 PUB.sm.ResetRunStepSeq(); PUB.sm.SetNewStep(eSMStep.SYNC); } VAR.BOOL[eVarBool.FLAG_SETUP] = false;// VAR.BOOL[eVarBool.FLAG_SETUP] = false;//VAR.BOOL[eVarBool.FLAG_SETUP] = false; if (popmsg) PUB.popup.Visible = true; } private void btMReset_Click(object sender, EventArgs e) { //if (Pub.sm.Step != eSMStep.IDLE) //{ var dlg = UTIL.MsgQ( "프로그램 상태를 초기화 하시겠습니까?\n" + "진행 중인 사항은 모두 취소 됩니다"); if (dlg != System.Windows.Forms.DialogResult.Yes) return; //} //else Pub.log.AddAT("대기상태일때에는 초기화 할 수 없습니다"); PUB.log.Add("User Click : initialize", false); PUB.sm.ClearRunStep(); PUB.sm.SetNewStep(eSMStep.RESET); PUB.sm.bPause = false; if (PUB.mplayer != null && PUB.mplayer.SoundLocation.isEmpty() == false) { PUB.mplayer.Stop(); PUB.bPlayMusic = false; } } private void btCapture_Click(object sender, EventArgs e) { UTIL.ScreenCapture(Screen.PrimaryScreen.WorkingArea.Width, Screen.PrimaryScreen.WorkingArea.Height, new Point(0, 0)); } private void btOpenDir_Click(object sender, EventArgs e) { UTIL.RunExplorer(UTIL.CurrentPath); } private void ManualMan_Click(object sender, EventArgs e) { SetScreen(form_manu); MenuMAN.ForeColor = Color.Gold; } private void MenuLift_Click(object sender, EventArgs e) { SetScreen(form_zlift); MenuLift.ForeColor = Color.Gold; } private void btDebug_Click(object sender, EventArgs e) { arCtl.arLabel ctl = sender as arCtl.arLabel; this.cmDebug.Show(ctl, new Point((int)(ctl.Width / 1.5), (int)(ctl.Height / 1.5))); } private void btAutorun_Click(object sender, EventArgs e) { var ctl = sender as arCtl.arLabel; if (ctl.Enabled == false) return; PUB.log.Add("User Click : Start", false); func_sw_start(true); } private void btMenuAuto_Click(object sender, EventArgs e) { SetScreen(form_auto); MenuAuto.ForeColor = Color.Gold; } private void btMenuFlag_Click(object sender, EventArgs e) { SetScreen(form_flag); MenuFlag.ForeColor = Color.Gold; } private void btLog_Click(object sender, EventArgs e) { if (form_log == null || form_log.IsDisposed || form_log.Disposing) form_log = new Dialog.fLog(); form_log.Show(); form_log.Activate(); if (form_log.WindowState == FormWindowState.Minimized) form_log.WindowState = FormWindowState.Normal; } private void btCharge_Click(object sender, EventArgs e) { var bCharge = (PUB.sm.RunStep == ERunStep.GOCHARGE || PUB.sm.RunStep == ERunStep.CHARGECHECK || VAR.BOOL[eVarBool.FLAG_CHARGEONA] == true); if (VAR.BOOL[eVarBool.FLAG_CHARGEONM]) { UTIL.MsgE("수동 충전 상태이므로 진행 할 수 없습니다"); return; } //충전을 시작해라ㅏ if (bCharge == true) { var dlg = UTIL.MsgQ("충전을 중지할까요?"); if (dlg == DialogResult.Yes) { PUB.sm.ClearRunStep(); PUB.sm.SetNewRunStep(ERunStep.GOHOME); PUB.sm.SetNewStep(eSMStep.RUN); PUB.log.Add("사용자 충전 해제"); } } else { var dlg = UTIL.MsgQ("충전을 시작할까요?"); if (dlg == DialogResult.Yes) { PUB.sm.ClearRunStep(); PUB.sm.SetNewRunStep(ERunStep.GOCHARGE); PUB.sm.SetNewStep(eSMStep.RUN); PUB.log.Add("충전(사용자)작업 시작"); } } } private void pictureBox1_Click(object sender, EventArgs e) { using (var f = new Dialog.fSystem()) { f.ShowDialog(); if (f.shutdown) { forceClose = true; this.Close(); } } } private void SSInfo_Click(object sender, EventArgs e) { PUB.Speak("고경석 수석님 밥은 드셨습니까?", true); UpdateProgressStatus(20, 100, "progre"); } private void brHome_Click(object sender, EventArgs e) { var bCharge = PUB.sm.Step == eSMStep.RUN && (PUB.sm.RunStep == ERunStep.GOHOME); if (PUB.CheckManualChargeMode() == false) return; if (bCharge == true) { var dlg = UTIL.MsgQ("홈(QC) 이동을 취소 할까요?"); if (dlg == DialogResult.Yes) { PUB.sm.ClearRunStep(); PUB.sm.SetNewStep(eSMStep.IDLE); PUB.AGV.AGVMoveStop("user home cancle", arDev.Narumi.eStopOpt.Stop); //PUB.AGV.AddCommand(arDev.Narumi.eAgvCmd.MoveStop);//.Move(Device.PLC.Rundirection.Stop, "사용자 홈 이동 취소"); PUB.log.Add("사용자 홈 이동 취소"); } } else { var dlg = UTIL.MsgQ("홈(QC) 이동을 실행 할까요?"); if (dlg == DialogResult.Yes) { PUB.sm.ClearRunStep(); PUB.sm.SetNewRunStep(ERunStep.GOHOME); PUB.sm.SetNewStep(eSMStep.RUN); PUB.log.Add("사용자 홈 이동 실행"); } } } private void btTopMenu_Volume_Click_1(object sender, EventArgs e) { using (var f = new Dialog.fVolume()) f.ShowDialog(); } private void lbCNtUp_Click(object sender, EventArgs e) { //down,up둘다 연결해야함 using (var f = new Dialog.fCounter()) f.ShowDialog(); } private void btMenuAGV_Click_1(object sender, EventArgs e) { SetScreen(form_agv); MenuAGV.ForeColor = Color.Gold; } private void btMenuBMS_Click_1(object sender, EventArgs e) { SetScreen(form_bms); MenuBMS.ForeColor = Color.Gold; } private void lbBat_Click(object sender, EventArgs e) { var bat = (int)PUB.BMS.Current_Level; var msg = $"{bat}퍼센트"; PUB.Speak(msg, false, false); PUB.counter.CountQA += 1; } private void IOState_ItemClick_1(object sender, arFrame.Control.GridView.ItemClickEventArgs e) { var title = IOState.Titles[e.idx]; switch (title.ToLower()) { case "emg": PUB.AGV.AGVErrorReset(); break; case "itm": var itemon = VAR.BOOL[eVarBool.ITEMON]; VAR.BOOL[eVarBool.ITEMON] = !itemon; break; } } private void btChargeM_Click(object sender, EventArgs e) { if (VAR.BOOL[eVarBool.FLAG_CHARGEONM]) { var dlg = UTIL.MsgQ("수동 충전을 해제 할까요?"); if (dlg != DialogResult.Yes) return; VAR.BOOL[eVarBool.FLAG_CHARGEONM] = false; PUB.log.Add("수동 충전 실행"); } else { if (PUB.AGV.system1.Battery_charging) { UTIL.MsgE("현재 자동 충전 중이라 진행 할 수 없습니다"); return; } var dlg = UTIL.MsgQ("수동 충전을 진행 할까요?"); if (dlg != DialogResult.Yes) return; VAR.BOOL[eVarBool.FLAG_CHARGEONM] = true; PUB.log.Add("수동 충전 실행"); } } private void saveToolStripMenuItem_Click(object sender, EventArgs e) { //mapsave using (var sd = new SaveFileDialog()) { sd.Filter = "AGV Map Files (*.agvmap)|*.agvmap|All Files (*.*)|*.*"; sd.DefaultExt = "agvmap"; sd.FileName = PUB._mapCanvas.MapFileName; if (sd.ShowDialog() == DialogResult.OK) { SaveMapToFile(sd.FileName); } } } private void loadToolStripMenuItem_Click(object sender, EventArgs e) { //load var od = new OpenFileDialog { Filter = "AGV Map Files (*.agvmap)|*.agvmap|All Files (*.*)|*.*", DefaultExt = "agvmap", FileName = PUB._mapCanvas.MapFileName, }; if (od.ShowDialog() == DialogResult.OK) { try { LoadMapFromFile(od.FileName); } catch (Exception ex) { MessageBox.Show($"맵 로드 중 오류가 발생했습니다: {ex.Message}", "오류", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } private void LoadMapFromFile(string filePath) { var result = MapLoader.LoadMapFromFile(filePath); if (result.Success) { var _mapCanvas = PUB._mapCanvas; PUB._mapNodes = result.Nodes; // 맵 캔버스에 데이터 설정 _mapCanvas.Nodes = result.Nodes; // RfidMappings 제거됨 - MapNode에 통합 // 🔥 맵 설정 적용 (배경색, 그리드 표시) if (result.Settings != null) { _mapCanvas.BackColor = System.Drawing.Color.FromArgb(result.Settings.BackgroundColorArgb); _mapCanvas.ShowGrid = result.Settings.ShowGrid; } // 설정에 마지막 맵 파일 경로 저장 PUB.setting.LastMapFile = filePath; PUB.setting.Save(); // 맵 로드 후 자동으로 맵에 맞춤 _mapCanvas.FitToNodes(); } else { MessageBox.Show($"맵 파일 로딩 실패: {result.ErrorMessage}", "오류", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void SaveMapToFile(string filePath) { // 🔥 백업 파일 생성 (기존 파일이 있을 경우) if (File.Exists(filePath)) { try { // 날짜시간 포함 백업 파일명 생성 var directory = Path.GetDirectoryName(filePath); var fileNameWithoutExt = Path.GetFileNameWithoutExtension(filePath); var extension = Path.GetExtension(filePath); var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); var backupFileName = $"{fileNameWithoutExt}_{timestamp}{extension}.bak"; var backupFilePath = Path.Combine(directory, backupFileName); // 기존 파일을 백업 파일로 복사 File.Copy(filePath, backupFilePath, true); } catch (Exception ex) { // 백업 파일 생성 실패 시 경고만 표시하고 계속 진행 MessageBox.Show($"백업 파일 생성 실패: {ex.Message}\n원본 파일은 계속 저장됩니다.", "백업 경고", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } var _mapCanvas = PUB._mapCanvas; var _mapNodes = PUB._mapNodes; // 🔥 현재 캔버스 설정을 맵 파일에 저장 var settings = new MapLoader.MapSettings { BackgroundColorArgb = _mapCanvas.BackColor.ToArgb(), ShowGrid = _mapCanvas.ShowGrid }; if (MapLoader.SaveMapToFile(filePath, _mapNodes, settings)) { // 설정에 마지막 맵 파일 경로 저장 PUB.setting.LastMapFile = filePath; PUB.setting.Save(); } else { MessageBox.Show("맵 파일 저장 실패", "오류", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void DumpAllThreadsState() { try { var sb = new System.Text.StringBuilder(); sb.AppendLine("===== 스레드 상태 덤프 ====="); sb.AppendLine($"시간: {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}"); //sb.AppendLine($"마지막 sm_Running 호출: {(DateTime.Now - lastSmRunningTime).TotalSeconds:F1}초 전"); //sb.AppendLine($"sm_Running 호출 횟수: {sm_Running_CallCount}"); sb.AppendLine($"IsThreadRun: {PUB.sm.IsThreadRun}"); sb.AppendLine($"Current Step: {PUB.sm.Step}"); sb.AppendLine($"Current RunStep: {PUB.sm.RunStep}"); sb.AppendLine(); var process = System.Diagnostics.Process.GetCurrentProcess(); sb.AppendLine($"총 스레드 수: {process.Threads.Count}"); sb.AppendLine(); foreach (System.Diagnostics.ProcessThread thread in process.Threads) { sb.AppendLine($"Thread {thread.Id}:"); sb.AppendLine($" State: {thread.ThreadState}"); sb.AppendLine($" Priority: {thread.PriorityLevel}"); if (thread.ThreadState == System.Diagnostics.ThreadState.Wait) { sb.AppendLine($" WaitReason: {thread.WaitReason}"); } sb.AppendLine(); } var dump = sb.ToString(); PUB.log.Add("THREAD_DUMP", dump); Console.WriteLine(dump); MessageBox.Show( $"스레드 덤프 완료\n로그 파일에 저장되었습니다.\n\n" + //$"마지막 sm_Running: {(DateTime.Now - lastSmRunningTime).TotalSeconds:F1}초 전\n" + $"IsThreadRun: {PUB.sm.IsThreadRun}", "스레드 덤프", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { PUB.log.AddE($"DumpAllThreadsState 오류: {ex.Message}"); } } private void stateMachineDebugToolStripMenuItem_Click(object sender, EventArgs e) { try { var debugForm = new Dialog.fStateMachineDebug(); debugForm.Show(); } catch (Exception ex) { MessageBox.Show($"디버그 창을 열 수 없습니다:\n{ex.Message}", "오류", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void editorToolStripMenuItem_Click(object sender, EventArgs e) { try { // MapEditor 실행 파일 경로 확인 string mapEditorPath = "AGVMapEditor.exe"; // 경로가 설정되지 않았거나 파일이 없는 경우 사용자에게 선택을 요청 if (string.IsNullOrEmpty(mapEditorPath) || !File.Exists(mapEditorPath)) { using (var openDialog = new OpenFileDialog()) { openDialog.Filter = "실행 파일 (*.exe)|*.exe|모든 파일 (*.*)|*.*"; openDialog.Title = "AGV MapEditor 실행 파일 선택"; openDialog.InitialDirectory = Application.StartupPath; if (openDialog.ShowDialog() == DialogResult.OK) { mapEditorPath = openDialog.FileName; } else { return; // 사용자가 취소함 } } } // MapEditor 실행 var startInfo = new System.Diagnostics.ProcessStartInfo { FileName = mapEditorPath, UseShellExecute = true }; // 현재 로드된 맵 파일이 있으면 파라미터로 전달 var _currentMapFilePath = PUB._mapCanvas.MapFileName; if (!string.IsNullOrEmpty(_currentMapFilePath) && File.Exists(_currentMapFilePath)) { startInfo.Arguments = $"\"{_currentMapFilePath}\""; } System.Diagnostics.Process.Start(startInfo); } catch (Exception ex) { MessageBox.Show($"MapEditor를 실행할 수 없습니다:\n{ex.Message}", "오류", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } }