using COMM; using System.Net.NetworkInformation; using System.Net; using System.Management; using System.Data; using AR; using System.Media; using System.Runtime.InteropServices; using System.Windows.Forms; using System; #if SPEECH using Microsoft.Speech.Synthesis; #endif using System.Threading.Tasks; using System.Data.SqlClient; using System.Linq; using AGVNavigationCore.Models; using AGVNavigationCore.Controls; using System.Collections.Generic; using System.Drawing; namespace Project { public static class PUB { //public static Device.CFlag flag; public static bool bShutdown = false; public static bool Automodeonreboot = false; public static bool AutRebootAlreay = false; public static bool DriveSpeed = false; public static AGVNavigationCore.Controls.UnifiedAGVCanvas _mapCanvas; public static List _mapNodes; /// /// 다음 작업 명령 (PickOn/PickOff) /// public static ENIGProtocol.AGVCommandHE NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop; /// /// 가상 AGV (시뮬레이션용) /// public static VirtualAGV _virtualAGV; #region "Hardware" /// /// 읽기/쓰기용이며 구동모터 와 비상정지가 연결됨(USB-ATMEGA 2560) /// //public static arDev.FakePLC PLC; ///// ///// 읽기전용이며 Z축 모터와 외부 버튼이 연결됨(USB-ATMEGA 2560) ///// //public static Device.PLC2 plcS; /// /// XBEE 통신(USB-TTL) /// public static Device.Xbee XBE; /// /// 배터리 잔량 확인(COM2 : RS232C - TTL) /// // public static arDevice.BMS bms; /// /// RFID READER (COM1 : RS232C) /// public static arDev.Narumi AGV; public static arDev.BMS BMS; #endregion /// /// 디버그모니터용 소켓(데이터를 전송만 한다) /// public static Device.Socket sock_debug; #if SPEECH private static SpeechSynthesizer voice; #endif public static SoundPlayer mplayer; [DllImport("winmm.dll")] private static extern int waveOutSetVolume(IntPtr hwo, uint dwVolume); public static void SetVolume(int volume) { // volume은 0~100 사이의 값 uint vol = (uint)((volume / 100.0) * 0xFFFF); waveOutSetVolume(IntPtr.Zero, vol); setting.musicvol = volume; } public static bool CheckPassword() { var f = new Dialog.fPassword(); if (f.ShowDialog() != System.Windows.Forms.DialogResult.OK) return false; var pass = f.tbInput.Text.Trim(); var passok = DateTime.Now.ToString("ddMM"); if (pass.Equals(passok)) return true; else return false; } /// /// 스피커를 통한 음성을 출력합니다 /// 출력되는 음성은 기본로그에도 자동 포함됩니다 /// /// /// /// 로그에도 출력 합니다 public static void Speak(string m, Boolean force = false, bool addlog = true,string logcate="") { if (force == false && PUB.setting.Enable_Speak == false) { Console.WriteLine("speech disabled"); return; } #if SPEECH if (force) voice.SpeakAsyncCancelAll(); if (voice.State == SynthesizerState.Ready) voice.SpeakAsync(m); if (addlog) { if (logcate.isEmpty()) logcate = "SPEAK"; PUB.log.Add(logcate, m); } #else PUB.log.Add($"스피치컴파일상수OFF"); #endif } /// /// 콜을 받을 수 있는 상황인가? /// /// public static bool IsCanCALL() { //콜가능여부도 전송한다 if (VAR.BOOL[eVarBool.FLAG_AUTORUN] && VAR.BOOL[eVarBool.FLAG_CHARGEONM] == false && PUB.BMS.Current_Level > PUB.setting.ChargeEmergencyLevel && PUB.sm.RunStep != StateMachine.ERunStep.BUFFER_OUT && VAR.BOOL[eVarBool.WAIT_COVER_DOWN] == false && VAR.BOOL[eVarBool.WAIT_COVER_UP] == false) { return true; } else return false; } public static CounterSetting counter; /// /// popup message window /// public static MessageWindow popup; /// /// database manager /// public static Manager.DatabaseManager dbm; /// /// 설정정보 /// public static CSetting setting; /// /// 시스템로그 /// public static AR.Log log, logagv, logplc, logbms, logxbee; public static Boolean bPlayMusic = false; /// /// 사용자 인풋 감지 시간 /// public static DateTime LastInputTime = DateTime.Now; public static CResult Result; public static string PatchVersion { get; set; } /// /// 상태머신 /// public static StateMachine.StateMachine sm; //상태머신분리 190529 public static System.IO.DirectoryInfo path; public static void initCore() { //setting setting = new CSetting(); setting.Load(); //counter setting counter = new CounterSetting(); counter.Load(); //log log = new AR.Log(); logagv = new AR.Log(); logplc = new AR.Log(); logbms = new AR.Log(); logxbee = new AR.Log(); logagv.FileNameFormat = "{yyyyMMdd}_agv"; logplc.FileNameFormat = "{yyyyMMdd}_plc"; logbms.FileNameFormat = "{yyyyMMdd}_bms"; logxbee.FileNameFormat = "{yyyyMMdd}_cal"; //popupmessage popup = new MessageWindow(); Result = new CResult(); Result.Clear(); } public static void init() { Result = new CResult(); //state machine sm = new StateMachine.StateMachine(); path = new System.IO.DirectoryInfo(UTIL.CurrentPath); mplayer = new SoundPlayer(); if (PUB.setting.musicfile.isEmpty() == false) if (System.IO.File.Exists(PUB.setting.musicfile)) { PUB.mplayer.SoundLocation = PUB.setting.musicfile; SetVolume(PUB.setting.musicvol); } #if SPEECH voice = new SpeechSynthesizer(); try { // 한국어 음성을 찾아서 설정 var koreanVoice = voice.GetInstalledVoices() .Where(v => v.VoiceInfo.Culture.Name.StartsWith("ko")) .FirstOrDefault(); if (koreanVoice != null) { voice.SelectVoice(koreanVoice.VoiceInfo.Name); } else { // 한국어 음성이 없으면 기본 음성 사용 var defaultVoice = voice.GetInstalledVoices().FirstOrDefault(); if (defaultVoice != null) { voice.SelectVoice(defaultVoice.VoiceInfo.Name); } } } catch { // 음성 설정 실패 시 기본값 사용 } voice.SetOutputToDefaultAudioDevice(); #endif var file_version = System.IO.Path.Combine(UTIL.CurrentPath, "version.txt"); if (System.IO.File.Exists(file_version)) { PUB.PatchVersion = System.IO.File.ReadAllText(file_version, System.Text.Encoding.UTF8); } else PUB.PatchVersion = string.Empty; } public static Boolean CheckManualChargeMode(bool Prompt = true) { if (VAR.BOOL[eVarBool.FLAG_CHARGEONM] == true) { string msg = "수동 충전 상태이므로 진행 할 수 없습니다"; PUB.Speak(msg); if (Prompt) UTIL.MsgE(msg); return false; } else return true; } public static double GetFreeSpace(string driveletter) { try { var di = new System.IO.DriveInfo(driveletter); var freespace = di.TotalFreeSpace; var totalspace = di.TotalSize; var freeSpaceRate = (freespace * 1.0 / totalspace) * 100.0; return freeSpaceRate; } catch { return 100.0; } } public static double GetFreeSpace() { double retval = 100.0; string savePath1 = path.FullName; if (savePath1 != "" && System.IO.Directory.Exists(savePath1)) { //이폴더를 사용 if (savePath1.StartsWith("\\") == false) { //남은잔량을 체크한다. retval = GetFreeSpace(savePath1.Substring(0, 1)); //if (freespace1 >= Pub.setting.AutoDeleteThreshold) return savePath1; } } return retval; } /// /// 장치에 오류가 있는지? /// /// public static Boolean HasHWError() { //if (PUB.PLC.IsValid == false) return true; if (PUB.AGV.IsOpen == false) return true; if (PUB.XBE.IsOpen == false) return true; return false; } //public static void AddStatusSQL(StateMachine.eSMStep status, string remark, DateTime wdate) //{ // VAR.TIME[eVarTime.StatusReporttime] = DateTime.Now; // try // { // var state = 0; // if (status == StateMachine.eSMStep.RUN) state = 1; // else if (status == StateMachine.eSMStep.ERROR || status == StateMachine.eSMStep.EMERGENCY) state = 2; // if (string.IsNullOrEmpty(remark)) remark = status.ToString(); // var mcid = PUB.setting.MCID; // var path = System.IO.Path.Combine(Util.CurrentPath, "Status"); // var file = System.IO.Path.Combine(path, $"{DateTime.Now.ToString("HHmmssfff")}_{status}.sql"); // var sql = "insert into MCMonitor_Rawdata(Model,status,remark,ip,mac,time) values('{0}','{1}','{2}','{3}','{4}','{5}')"; // sql = string.Format(sql, mcid, state, remark, IP, MAC, wdate.ToString("yyyy-MM-dd HH:mm:ss")); // System.IO.File.WriteAllText(file, sql, System.Text.Encoding.Default); // //만들어진지 3분이 지난 파일은 삭제한다. // //var di = new System.IO.DirectoryInfo(path); // //var fi = di.GetFiles("*.sql", System.IO.SearchOption.TopDirectoryOnly).Where(t => t.LastWriteTime < DateTime.Now.AddMinutes(-3)).FirstOrDefault(); // //if (fi != null) fi.Delete(); // } // catch (Exception ex) // { // PUB.log.AddE(ex.Message); // } //} public static void AddEEDB(string remark) { var step = PUB.sm.Step.ToString(); var rtep = PUB.sm.RunStep.ToString(); var mcid = PUB.setting.MCID; var path = System.IO.Path.Combine(UTIL.CurrentPath, "Status"); var file = System.IO.Path.Combine(path, $"{DateTime.Now.ToString("HHmmssfff")}_{step}_{rtep}.sql"); var wdate = DateTime.Now; try { var sql = "insert into AGV_History(mcid,step,runstep,remark,ip,wdate) values('{0}','{1}','{2}','{3}','{4}','{5}')"; sql = string.Format(sql, mcid, step, rtep, remark, IP, wdate.ToString("yyyy-MM-dd HH:mm:ss")); System.IO.File.WriteAllText(file, sql, System.Text.Encoding.Default); //만들어진지 3분이 지난 파일은 삭제한다. //var di = new System.IO.DirectoryInfo(path); //var fi = di.GetFiles("*.sql", System.IO.SearchOption.TopDirectoryOnly).Where(t => t.LastWriteTime < DateTime.Now.AddMinutes(-3)).FirstOrDefault(); //if (fi != null) fi.Delete(); } catch (Exception ex) { PUB.log.AddE(ex.Message); } } public static string GetResultCodeMessage(eResult err) { return err.ToString().ToUpper(); } public static string GetErrorMessage(eECode err, params object[] values) { switch (err) { case eECode.NOTALLOWUP: return "상차 허용 위치가 아닙니다"; case eECode.AGVCONN: return Lang.AGV연결실패; case eECode.PLCCONN: return Lang.PLC통신실패; case eECode.MESSAGE_ERROR: if (values.Length > 0) return values[0].ToString(); return "Message Error"; default: return err.ToString(); } } public static string IP { get; set; } public static string MAC { get; set; } /// /// 프로그램 사용기록 추가 /// /// /// /// public static void CheckNRegister3(string prgmName, string develop, string prgmVersion) { if (prgmName.Length > 50) prgmName = prgmName.Substring(0, 50); //길이제한 var task = Task.Factory.StartNew(() => { try { string ip = ""; string mac = ""; // string prgmName = Application.ProductName; var nif = NetworkInterface.GetAllNetworkInterfaces(); var host = Dns.GetHostEntry(Dns.GetHostName()); string fullname = System.Net.Dns.GetHostEntry("").HostName; foreach (IPAddress r in host.AddressList) { string str = r.ToString(); if (str != "" && str.Substring(0, 3) == "10.") { ip = str; break; } } string rtn = string.Empty; ObjectQuery oq = new System.Management.ObjectQuery("SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled='TRUE'"); ManagementObjectSearcher query1 = new ManagementObjectSearcher(oq); foreach (ManagementObject mo in query1.Get()) { string[] address = (string[])mo["IPAddress"]; if (address[0] == ip && mo["MACAddress"] != null) { mac = mo["MACAddress"].ToString(); break; } } if (ip == "" || mac == "") return; var conn = new SqlConnection(AGV4.Properties.Settings.Default.CS);// "Data Source=10.131.15.18;Initial Catalog=EE;Persist Security Info=True;User ID=eeuser;Password=Amkor123!"); conn.Open(); string ProcName = "AddPrgmUser3"; var cmd = new SqlCommand(ProcName, conn) { CommandType = CommandType.StoredProcedure }; SqlParameter param = cmd.Parameters.Add("@mac", SqlDbType.NVarChar, 50); param.Value = mac; param = cmd.Parameters.Add("@ip", SqlDbType.NVarChar, 50); param.Value = ip; param = cmd.Parameters.Add("@pgrm", SqlDbType.NVarChar, 50); param.Value = prgmName; param = cmd.Parameters.Add("@develop", SqlDbType.NVarChar, 50); param.Value = develop; param = cmd.Parameters.Add("@pgver", SqlDbType.NVarChar, 50); param.Value = prgmVersion; param = cmd.Parameters.Add("@prgmLogin", SqlDbType.VarChar, 20); param.Value = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); param = cmd.Parameters.Add("@account", SqlDbType.NVarChar, 50); param.Value = System.Environment.UserName; param = cmd.Parameters.Add("@hostname", SqlDbType.NVarChar, 100); param.Value = fullname; cmd.ExecuteNonQuery(); conn.Close(); IP = ip; MAC = mac; } catch (Exception ex) { PUB.log.AddE(ex.Message); } }); } public static string SelectSerialPort() { var f = new System.Windows.Forms.Form(); f.WindowState = System.Windows.Forms.FormWindowState.Normal; f.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; f.Size = new System.Drawing.Size(800, 400); f.MaximizeBox = false; f.MinimizeBox = false; f.Text = "Select Port"; f.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; var lst = new System.Windows.Forms.ListBox(); lst.Font = new System.Drawing.Font("Consolas", 15, System.Drawing.FontStyle.Bold); lst.Dock = System.Windows.Forms.DockStyle.Fill; lst.DoubleClick += (s1, e1) => { if (lst.SelectedItem != null) f.DialogResult = System.Windows.Forms.DialogResult.OK; }; using (var searcher = new ManagementObjectSearcher("SELECT * FROM WIN32_SerialPort")) { var portnames = System.IO.Ports.SerialPort.GetPortNames().OrderBy(t => t); var ports = searcher.Get().Cast().ToList(); foreach (var port in portnames) { var desc = ""; var portInfo = ports.Where(t => t["DeviceId"].ToString() == port).FirstOrDefault(); if (portInfo != null) desc = portInfo["Caption"].ToString(); lst.Items.Add(string.Format("{0} - {1}", port, desc)); } } f.Controls.Add(lst); if (f.ShowDialog() == System.Windows.Forms.DialogResult.OK) { var name = lst.SelectedItem.ToString().Split('-'); return name[0].Trim(); } else return string.Empty; } public static void SystemReboot(UInt16 timeout, bool setautomode = false) { System.Diagnostics.ProcessStartInfo si = new System.Diagnostics.ProcessStartInfo(); si.FileName = @"c:\windows\system32\shutdown.exe"; si.Arguments = "-r -t " + timeout.ToString(); si.UseShellExecute = false; System.Diagnostics.Process prc = new System.Diagnostics.Process(); prc.StartInfo = si; PUB.AGV.AGVMoveStop("user reboot"); if (setautomode) { PUB.Automodeonreboot = true; PUB.setting.SetAutoModeOn = true; PUB.setting.AUtoRebootLastTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); PUB.setting.Save(); } prc.Start(); } public static void SystemShutdown(UInt16 timeout) { //System.Text.StringBuilder sb = new StringBuilder(); //sb.AppendLine("@echo off"); //sb.AppendLine("echo System OFF After " + timeout.ToString() + "Seconds"); //sb.AppendLine("shutdown -s -t " + timeout.ToString()); //string bfile = AppDomain.CurrentDomain.BaseDirectory + "Shutdown.bat"; //System.IO.File.WriteAllText(bfile, string.Format(sb.ToString(), AppDomain.CurrentDomain.BaseDirectory), System.Text.Encoding.Default); System.Diagnostics.ProcessStartInfo si = new System.Diagnostics.ProcessStartInfo(); si.FileName = @"c:\windows\system32\shutdown.exe"; si.Arguments = "-s -t " + timeout.ToString(); si.UseShellExecute = false; System.Diagnostics.Process prc = new System.Diagnostics.Process(); prc.StartInfo = si; prc.Start(); } public static void ChangeUIPopup(System.Windows.Forms.NumericUpDown valueCtl) { var value = valueCtl.Value.ToString(); var f = UTIL.InputBox("input", value);// new Dialog.fInput(value); if (f.Item1) { var val = decimal.Parse(f.Item2); if (val < valueCtl.Minimum) { UTIL.MsgE(string.Format("최소 입력값은 {0} 입니다.", valueCtl.Minimum)); val = valueCtl.Minimum; } if (val > valueCtl.Maximum) { UTIL.MsgE(string.Format("최대 입력값은 {0} 입니다.", valueCtl.Maximum)); val = valueCtl.Maximum; } valueCtl.Value = val; } } #region VirtualAGV 실제 데이터 동기화 public static MapNode FindByNodeID(string nodeidx) { if (_mapNodes == null || _mapNodes.Any() == false) return null; if (nodeidx.isEmpty()) return null; return _mapNodes.Where(t => t.Id.Equals(nodeidx)).FirstOrDefault(); } public static MapNode FindByRFID(string rfidValue) { if (_mapNodes == null || _mapNodes.Any() == false) return null; if (rfidValue.isEmpty()) return null; return _mapNodes.Where(t => t.RfidId.Equals(rfidValue)).FirstOrDefault(); } public static List FindByNodeAlias(string alias) { if (_mapNodes == null || _mapNodes.Any() == false) return null; if (alias.isEmpty()) return null; var lst = _mapNodes.Where(t => t.AliasName.Equals(alias)); if (lst.Any() == false) return null; return lst.ToList(); } public static List FindByNodeType(AGVNavigationCore.Models.MapNode type) { if (_mapNodes == null || _mapNodes.Any() == false) return null; var lst = _mapNodes.Where(t => t.Type.Equals(type)); if (lst.Any() == false) return null; return lst.ToList(); } /// /// RFID 읽기 시 해당 노드 위치로 AGV 업데이트 /// /// 읽은 RFID ID /// 모터 방향 (Forward/Backward) /// 업데이트 성공 여부 public static bool UpdateAGVFromRFID(string rfidId, AgvDirection motorDirection = AgvDirection.Forward) { if (_virtualAGV == null || _mapNodes == null) return false; // RFID에 해당하는 노드 찾기 var node = _mapNodes.FirstOrDefault(n => n.RfidId == rfidId); if (node != null) { _virtualAGV.SetPosition(node, motorDirection); RefreshAGVCanvas(); log.Add($"[AGV] RFID {rfidId} 감지 → 노드 {node.Id} 위치 업데이트 (방향: {motorDirection})"); return true; } log.Add($"[AGV] RFID {rfidId}에 해당하는 노드를 찾을 수 없음"); return false; } /// /// 노드ID로 AGV 위치 업데이트 /// /// 노드 ID /// 모터 방향 (Forward/Backward) /// 업데이트 성공 여부 public static bool UpdateAGVToNode(string nodeId, AgvDirection motorDirection = AgvDirection.Forward) { if (_virtualAGV == null || _mapNodes == null) return false; var node = _mapNodes.FirstOrDefault(n => n.Id == nodeId); if (node != null) { _virtualAGV.SetPosition(node, motorDirection); RefreshAGVCanvas(); log.Add($"[AGV] 노드 {nodeId} 위치로 이동 (방향: {motorDirection})"); return true; } return false; } /// /// 모든 로그를 플러시 합니다. /// public static void LogFlush() { PUB.log.Flush(); PUB.logagv.Flush(); PUB.logplc.Flush(); PUB.logbms.Flush(); PUB.logxbee.Flush(); } /// /// AGV 방향 업데이트 /// /// 새로운 방향 public static void UpdateAGVDirection(AgvDirection direction) { if (_virtualAGV == null) return; _virtualAGV.CurrentDirection = direction; RefreshAGVCanvas(); } /// /// AGV 상태 업데이트 /// /// 새로운 상태 public static void UpdateAGVState(AGVState state) { if (_virtualAGV == null) return; _virtualAGV.CurrentState = state; RefreshAGVCanvas(); } /// /// AGV 배터리 레벨 업데이트 /// /// 배터리 레벨 (0.0 ~ 100.0) public static void UpdateAGVBattery(float batteryLevel) { if (_virtualAGV == null) return; _virtualAGV.SetBatteryLevel(batteryLevel); RefreshAGVCanvas(); } /// /// 맵 캔버스 강제 갱신 (AGV 위치 표시 업데이트) /// public static void RefreshAGVCanvas() { if (_mapCanvas != null && _mapCanvas.IsHandleCreated) { _mapCanvas.Invalidate(); } } #endregion } }