using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using GSG.NET.Concurrent; using GSG.NET.Logging; using GSG.NET.Utils; using OHV.Common.Events; using OHV.Common.Model; using OHV.SqliteDAL; using Prism.Events; namespace VehicleControlSystem.Managers { public class Scheduler : IDisposable { static Logger logger = Logger.GetLogger(); private List commands; public List CommandsList { get { return commands; } set { commands = value; } } Thread thread = null; bool isThreadAlive = false; IEventAggregator eventAggregator; AutoManager autoManager; SqliteManager sql = null; public Scheduler(IEventAggregator ea, AutoManager autoManager, SqliteManager sqlite) { this.CommandsList = new List(); eventAggregator = ea;// CommonServiceLocator.ServiceLocator.Current.GetInstance(); eventAggregator.GetEvent().Subscribe(OnReceivedMessageEvent); this.autoManager = autoManager; this.sql = sqlite; } private void OnReceivedMessageEvent(VCSMessageEventArgs obj) { this.AddCommand(obj.Command); //var m = new GUIMessageEventArgs() { MessageText = obj.MessageText, MessageKey = obj.MessageKey }; //m.Command = ObjectCopyUtils.DeepClone(obj.Command); //GUIMessagePublish(m); } void GUIMessagePublish(GUIMessageEventArgs args) { eventAggregator.GetEvent().Publish(args); } public void Init() { this.isThreadAlive = true; this.thread = ThreadUtils.Invoke(this.ExcuteCommand); } public bool AddCommand(Command cmd) { if (this.CommandsList == null) return false; try { this.CommandsList.Add(cmd); } catch (Exception ex) { logger.E(ex); return false; } return true; } bool RemoveCommand(Command cmd) { if (!this.CommandsList.Remove(cmd)) { logger.D($"Scheduler - [Command] : RemoveCommand fail ID-{cmd.CommandID} / Result-{cmd.Result} "); return false; } logger.D($"Scheduler - [Command] : RemoveCommand OK ID-{cmd.CommandID} / Result-{cmd.Result} "); return true; } void ExcuteCommand() { while (isThreadAlive) { Thread.Sleep(500); try { var state = this.autoManager.AutoModeStateProperty; switch (state) { case OHV.Common.Shareds.eAutoModeState.ErrorStop: break; case OHV.Common.Shareds.eAutoModeState.StartRun: break; case OHV.Common.Shareds.eAutoModeState.Run: { Command cmd = sql.CommandDAL.GetCmd(); if (cmd == null) continue; DoWork(cmd); } break; case OHV.Common.Shareds.eAutoModeState.Stop: break; default: break; } } catch (Exception ex) { logger.E(ex); } } } private void DoWork(Command cmd) { switch (cmd.State) { case OHV.Common.Shareds.eCommandState.Queued: if (sql.SubCmdDAL.Count <= 0) { switch (cmd.Type) { case OHV.Common.Shareds.eCommandType.Move: case OHV.Common.Shareds.eCommandType.MoveNLoad: case OHV.Common.Shareds.eCommandType.MoveNUnload: CreateSubCommand(cmd, SubCmd.eType.Move); break; case OHV.Common.Shareds.eCommandType.Charging: break; default: break; } } sql.CommandDAL.UpdateState(cmd.CommandID, OHV.Common.Shareds.eCommandState.Assign); break; case OHV.Common.Shareds.eCommandState.Assign: //Assign 되면 Vehicle 의 상태가 Busy 면 Processing 으로 전환. { var subCmd = sql.SubCmdDAL.GetSubCmd(); if (subCmd == null) break; if (!subCmd.CmdID.Equals(cmd.CommandID)) break; } sql.CommandDAL.UpdateState(cmd.CommandID, OHV.Common.Shareds.eCommandState.Processing); break; case OHV.Common.Shareds.eCommandState.Processing: { var subCmd = sql.SubCmdDAL.GetSubCmd(); if (subCmd == null) // Move 명령을 수행 완료 했다고 판단. { if ( cmd.IsSecondCommanded) { sql.CommandDAL.UpdateState(cmd.CommandID, OHV.Common.Shareds.eCommandState.Complete); break; } if (cmd.Type == OHV.Common.Shareds.eCommandType.MoveNLoad ) { if (this.CreateSubCommand(cmd, SubCmd.eType.Load)) cmd.IsSecondCommanded = true; break; } else if(cmd.Type == OHV.Common.Shareds.eCommandType.MoveNUnload) { if (this.CreateSubCommand(cmd, SubCmd.eType.Unload)) cmd.IsSecondCommanded = true; break; } else if ( cmd.Type == OHV.Common.Shareds.eCommandType.Charging) { if ( this.CreateSubCommand(cmd, SubCmd.eType.Chaging)) cmd.IsSecondCommanded = true; break; } else //Move Command { sql.CommandDAL.UpdateState(cmd.CommandID, OHV.Common.Shareds.eCommandState.Complete); break; } } if (subCmd.CmdID.Equals(cmd.CommandID)) //진행중 판단 break; else //다른 아이디의 일을 하고 있을 경우 가 생길까?? { } } sql.CommandDAL.UpdateState(cmd.CommandID, OHV.Common.Shareds.eCommandState.Complete); break; case OHV.Common.Shareds.eCommandState.Complete: sql.CommandDAL.Delete(cmd); break; default: break; } } private bool CreateSubCommand(Command cmd, SubCmd.eType type) { //명령을 만들기 전에 Vehicle 의 상태를 확인하여 만들자. switch (type) { case SubCmd.eType.Move: break; case SubCmd.eType.Load: break; case SubCmd.eType.Unload: break; case SubCmd.eType.Chaging: break; default: break; } var sub = new SubCmd { CmdType = SubCmd.eCmdType.Auto, Type = type, TargetID = cmd.TargetID, CmdID = cmd.CommandID, }; sql.SubCmdDAL.Add(sub); logger.I($"{sub.ID} Sub Command Create - Type={sub.Type}, TargetID={sub.TargetID}, CommandID={sub.CmdID}"); return true; } public bool CreateTransferCommand() { Command cmd = new Command { }; sql.CommandDAL.Add(cmd); logger.I($"{cmd.CommandID} Command Create - Type={cmd.Type}, TargetID={cmd.TargetID}"); return true; } #region IDisposable Support private bool disposedValue = false; // 중복 호출을 검색하려면 protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { // TODO: 관리되는 상태(관리되는 개체)를 삭제합니다. this.isThreadAlive = false; if ( !this.thread.Join(3000) ) ThreadUtils.Kill(this.thread); } // TODO: 관리되지 않는 리소스(관리되지 않는 개체)를 해제하고 아래의 종료자를 재정의합니다. // TODO: 큰 필드를 null로 설정합니다. disposedValue = true; } } // TODO: 위의 Dispose(bool disposing)에 관리되지 않는 리소스를 해제하는 코드가 포함되어 있는 경우에만 종료자를 재정의합니다. // ~Scheduler() // { // // 이 코드를 변경하지 마세요. 위의 Dispose(bool disposing)에 정리 코드를 입력하세요. // Dispose(false); // } // 삭제 가능한 패턴을 올바르게 구현하기 위해 추가된 코드입니다. public void Dispose() { // 이 코드를 변경하지 마세요. 위의 Dispose(bool disposing)에 정리 코드를 입력하세요. Dispose(true); // TODO: 위의 종료자가 재정의된 경우 다음 코드 줄의 주석 처리를 제거합니다. // GC.SuppressFinalize(this); } #endregion } }