using System; using System.Collections.Generic; using System.Linq; using Aitex.Core.RT.DataCenter; using Aitex.Core.RT.Device; using Aitex.Core.RT.Device.Unit; using Aitex.Core.RT.Event; using Aitex.Core.RT.Fsm; using Aitex.Core.RT.Log; using Aitex.Core.RT.OperationCenter; using Aitex.Core.RT.SCCore; using Aitex.Core.Util; using Aitex.Sorter.Common; using MECF.Framework.Common.Equipment; namespace Aitex.Sorter.RT.EFEMs.Servers { public partial class EfemEntity : Entity, IEntity, IEfemServerSocketCallback { public enum ServerState { Init, Listening, Connected, } public enum MSG { Online, Offline, NewSessionConnected, Listening, Reset, Error, Disconnect, ReceiveData, SendData, } private EfemServerSocket _socket = null; private string _sessionId; private List _activeTaskList = new List(); private TaskFactory _factory = new TaskFactory(); public string Name { get; set; } public bool IsCommunicationOk { get; set; } public bool IsOnlineMode { get; private set; } private bool IsSystemHold { get; set; } DeviceTimer _sendReadyTimer = new DeviceTimer(); public EfemEntity() { Name = "Efem"; fsm = new StateMachine(Name, (int)ServerState.Init, 50); } protected override bool Init() { IsOnlineMode = true; _socket = new EfemServerSocket(this, Singleton.Instance.EfemDevice.EfemPort); BuildTransitionTable(); Subscribe(); Singleton.Instance.EfemDevice.EnableTowerLocalMode(false); return true; } private void Subscribe() { DATA.Subscribe("Efem.IsCommunicationReady", () => fsm.State == (int)ServerState.Connected); DATA.Subscribe("Efem.IsOnline", () => IsOnlineMode); DATA.Subscribe("System.IsHold", () => IsSystemHold); DATA.Subscribe("System.IsOnlineMode", () => IsOnlineMode); DATA.Subscribe("System.ServerStatus", () => ((ServerState)fsm.State).ToString()); OP.Subscribe(OperationName.Online, (cmd, param) => { if (IsOnlineMode) return true; if (Singleton.Instance.EfemDevice.IsRunning) { EV.PostWarningLog("Server", "System is busy, can not switch to online"); return true; } return CheckToPostMsg(MSG.Online); }); OP.Subscribe(OperationName.Offline, (cmd, param) => { if (!IsOnlineMode) return true; return CheckToPostMsg(MSG.Offline); }); } private void BuildTransitionTable() { //online offline AnyStateTransition(MSG.Offline, FsmSetOffline, FSM_STATE.SAME); AnyStateTransition(MSG.Online, FsmSetOnline, FSM_STATE.SAME); //init EnterExitTransition(ServerState.Init, FsmEnterInit, FSM_MSG.NONE, null); Transition(ServerState.Init, MSG.Listening, null, ServerState.Listening); Transition(ServerState.Init, MSG.Reset, FsmEnterInit, ServerState.Init); //listening Transition(ServerState.Listening, MSG.Error, null, ServerState.Init); Transition(ServerState.Listening, MSG.NewSessionConnected, FsmNewSessionConnected, ServerState.Connected); //Connected Transition(ServerState.Connected, MSG.Disconnect, FsmDisconnect, ServerState.Listening); Transition(ServerState.Connected, MSG.Error, null, ServerState.Init); Transition(ServerState.Connected, MSG.NewSessionConnected, FsmNewSessionConnected, ServerState.Connected); Transition(ServerState.Connected, MSG.ReceiveData, FsmReceiveData, ServerState.Connected); Transition(ServerState.Connected, MSG.SendData, FsmSendData, ServerState.Connected); Transition(ServerState.Connected, FSM_MSG.TIMER, FsmMonitor, ServerState.Connected); } ~EfemEntity() { _socket?.Stop(); } public bool SetSystemHold() { if (IsSystemHold) { return false; } IsSystemHold = true; return true; } public bool SetSystemUnHold() { if (!IsSystemHold) { return true; } IsSystemHold = false; return true; } public bool CheckToPostMsg(MSG msg) { if (!fsm.FindTransition(fsm.State, (int)msg)) { EV.PostWarningLog("System", string.Format("{0} is in {1} state,can not do {2}", Name, (ServerState)fsm.State, (MSG)msg)); return false; } PostMsg(msg); return true; } public bool CheckToPostMsg(MSG msg, object param1) { if (!fsm.FindTransition(fsm.State, (int)msg)) { EV.PostWarningLog("System", string.Format("{0} is in {1} state,can not do {2}", Name, (ServerState)fsm.State, (MSG)msg)); return false; } PostMsg(msg, param1); return true; } private bool FsmNewSessionConnected(object[] param) { _sessionId = (string) param[0]; _activeTaskList.Clear(); _eventData.Clear(); IsCommunicationOk = false; return true; } private bool FsmDisconnect(object[] param) { var sessionId = (string)param[0]; if (sessionId != _sessionId) return false; return true; } private bool FsmEnterInit(object[] param) { string ip = SC.GetStringValue("System.EfemServerLocalIp"); int port = SC.GetValue("System.EfemPortNumber"); _sendReadyTimer.Start(0); if (string.IsNullOrEmpty(ip)) ip = "127.0.0.1"; if (_socket.Start(ip, port)) { PostMsg(MSG.Listening); return true; } return false; } private bool FsmSendData(object[] param) { _socket.Send((string)param[0]); return true; } private bool FsmReceiveData(object[] param) { try { string rawMessage = (string)param[0]; string[] words = rawMessage.Split(new char[5] { ':', '/', '>', '|', ';' }, StringSplitOptions.RemoveEmptyEntries); if (!Enum.TryParse(words[0], out EfemCommandType type)) { LOG.Write($"{rawMessage} is not a valid EFEM message format"); SendMessage("NAK:MSG"); return true; } if (!Enum.TryParse(words[1], out EfemCommand cmd)) { LOG.Write($"{rawMessage} is not a valid EFEM message format"); SendMessage("NAK:MSG"); return true; } string commandData = rawMessage.Substring(rawMessage.IndexOf(':')); if (type == EfemCommandType.ACK) { //special for handshake if (!IsCommunicationOk && (rawMessage == "ACK:READY/COMM")) { IsCommunicationOk = true; return true; } foreach (ITaskT active in _activeTaskList) { if (active.CommandData == commandData && active.CommandType == type && active.CommandName == cmd) { active.Ack(type, cmd, words.Skip(2).Take(words.Length).ToArray()); } } return true; } if (_factory.UnSupport(type, cmd)) { LOG.Write($"{rawMessage} is not a valid EFEM message format"); SendMessage("NAK:MSG"); return true; } ITaskT task = _factory.Create(type, cmd); task.CommandData = commandData; foreach (ITaskT active in _activeTaskList) { if (active.CommandData == commandData && active.CommandType==type && active.CommandName==cmd) { EV.PostWarningLog("EFEM", $"ignored {rawMessage}, already active"); return true; } } if (task.Execute(out string resp, rawMessage, words.Skip(2).Take(words.Length).ToArray())) { _activeTaskList.Add(task); } SendMessage(resp); } catch (Exception ex) { LOG.Write(ex); } return true; } private bool FsmSetOnline(object[] param) { IsOnlineMode = true; Singleton.Instance.EfemDevice.EnableTowerLocalMode(false); return true; } private bool FsmSetOffline(object[] param) { IsOnlineMode = false; Singleton.Instance.EfemDevice.EnableTowerLocalMode(true); return true; } public void OnConnected(string sessionId) { CheckToPostMsg(MSG.NewSessionConnected, sessionId); } public void OnDisconnected(string sessionId) { CheckToPostMsg(MSG.Disconnect, sessionId); } public void OnReceived(string msg) { CheckToPostMsg(MSG.ReceiveData, msg); } public void SendMessage(string msg) { if (IsCommunicationOk || msg == "INF:READY/COMM") { CheckToPostMsg(MSG.SendData, msg); } } private bool FsmMonitor(object[] objs) { if (!IsCommunicationOk) { if (_sendReadyTimer.GetElapseTime() > Singleton.Instance.EfemDevice.SendReadyInterval) { _sendReadyTimer.Start(0); SendMessage("INF:READY/COMM"); } return true; } MonitorRunningTask(); MonitorEvent(); return true; } private void MonitorRunningTask() { List tobeRemoved = new List(); foreach (ITaskT task in _activeTaskList) { if (!task.HasInfoMessage) { tobeRemoved.Add(task); continue; } string msg = string.Empty; bool? ret = task.Monitor(out msg); if (ret.HasValue) { SendMessage(msg); tobeRemoved.Add(task); break; } } if (tobeRemoved.Any()) { foreach (var task in tobeRemoved) { _activeTaskList.Remove(task); } } } protected override void Term() { if (_socket != null) _socket.Stop(); } public bool Check(int msg, out string reason, params object[] args) { if (!fsm.FindTransition(fsm.State, msg)) { reason = String.Format("{0} is in {1} state,can not do {2}", Name, (ServerState)fsm.State, (MSG)msg); return false; } reason = ""; return true; } public void Reset() { if (fsm.State == (int)ServerState.Init) { string ip = SC.GetStringValue("System.EfemServerLocalIp"); int port = SC.GetValue("System.EfemPortNumber"); _sendReadyTimer.Start(0); if (_socket.Start(ip, port)) { PostMsg(MSG.Listening); } } } } }