using Aitex.Common.Util; using Aitex.Core.RT.Event; using Aitex.Core.RT.Log; using Aitex.Core.RT.RecipeCenter; using Aitex.Core.RT.SCCore; using Aitex.Core.Util; using FabConnect.SecsGemInterface.Common; using FabConnect.SecsGemInterface.Common.ToolModel; using FabConnect.SecsGemInterface.GemModel; using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using System.Xml; using MECF.Framework.Common.CommonData; using VirgoRT.HostWrapper; namespace VirgoRT.FAs { public class FaHost : IFaHost { public string FaCommunicationState { get { return _gem.CommunicationState.ToString(); } } public string FaControlState { get { return _gem.ControlState.ToString(); } } public string FaControlSubState { get { return _gem.ControlRemoteSwitch.ToString(); } } public bool IsConnected { get { return _gem.CommunicationState == CommunicationState.EnabledCommunicating; } } public int SpoolingState { get { return (int)_gem.SpoolingState; } } public string SpoolingActual { get { return _gem.GetAttribute(GEMVariables.SpoolCountActual, AttributeType.SV) ; } } public string SpoolingTotal { get { return _gem.GetAttribute(GEMVariables.SpoolCountTotal, AttributeType.SV); } } public string SpoolingFullTime { get { return _gem.GetAttribute(GEMVariables.SpoolFullTime, AttributeType.SV); } } public string SpoolingStartTime { get { return _gem.GetAttribute(GEMVariables.SpoolStartTime, AttributeType.SV); } } public bool IsSpoolingEnable { get { return Convert.ToBoolean(_gem.GetAttribute(GEMVariables.SpoolEnabled, AttributeType.EC)); } } private readonly GEMController _gem = new GEMController(); public IHostCallback _equipment; private HashSet _systemBuildInEc = new HashSet(); private HashSet _systemBuildInVariables = new HashSet(); private PeriodicJob _faMonitorThread; private FixSizeQueue _lstEvent = new FixSizeQueue(300); FALogFileCleaner _logCleaner = new FALogFileCleaner(); private ControlState _state = ControlState.Unknown; private const string EventTerminalMessage = "TerminalMessage"; public void Initialize(IHostCallback equipment, string modelFile) { _equipment = equipment; _gem.CommunicationStateChanged += OnCommunicationStateChanged; _gem.RemoteCommandS2F49In += _gem_RemoteCommandS2F49In; ; _gem.PrimaryMessageIn += _gem_PrimaryMessageIn; _gem.ControlStateChanged += _gem_ControlStateChanged; _gem.ShowTrialMessageBox = false; _gem.AutoPPDataReply = false; _gem.Initialize(modelFile, PathManager.GetLogDir()); _gem.EquipmentModel.GemConnection.HSMS.localIPAddress = SC.GetStringValue("System.FA.LocalIpAddress"); _gem.EquipmentModel.GemConnection.HSMS.localPortNumber = SC.GetValue("System.FA.LocalPortNumber"); _gem.EquipmentModel.GemConnection.HSMS.T3Timeout = SC.GetValue("System.FA.T3Timeout"); _gem.EquipmentModel.GemConnection.HSMS.T5Timeout = SC.GetValue("System.FA.T5Timeout"); _gem.EquipmentModel.GemConnection.HSMS.T6Timeout = SC.GetValue("System.FA.T6Timeout"); _gem.EquipmentModel.GemConnection.HSMS.T7Timeout = SC.GetValue("System.FA.T7Timeout"); _gem.EquipmentModel.GemConnection.HSMS.T8Timeout = SC.GetValue("System.FA.T8Timeout"); _gem.ReInitialize(); //get system build in ec _systemBuildInEc = new HashSet(); _systemBuildInEc.Add("EstablishCommunicationsTimeout"); _systemBuildInEc.Add("MaxSpoolTransmit"); _systemBuildInEc.Add("OverWriteSpool"); _systemBuildInEc.Add("MaxSpoolCapacity"); _systemBuildInEc.Add("SpoolEnabled"); _systemBuildInEc.Add("TimeFormat"); //initial system build in variable _systemBuildInVariables.Add("AlarmsEnabled"); _systemBuildInVariables.Add("AlarmsSet"); _systemBuildInVariables.Add("Clock"); _systemBuildInVariables.Add("ControlState"); _systemBuildInVariables.Add("EventsEnabled"); _systemBuildInVariables.Add("PPExecName"); _systemBuildInVariables.Add("PreviousProcessState"); _systemBuildInVariables.Add("ProcessState"); _systemBuildInVariables.Add("SpoolCountActual"); _systemBuildInVariables.Add("SpoolCountTotal"); _systemBuildInVariables.Add("SpoolFullTime"); _systemBuildInVariables.Add("SpoolStartTime"); _systemBuildInVariables.Add("SpoolState"); _systemBuildInVariables.Add("SpoolSubstate"); _logCleaner.Run(); _faMonitorThread = new PeriodicJob(200, MonitorFaTask, "Monitor FA Thread", true); EV.Subscribe(new EventItem("Host", EventTerminalMessage, "{0}", EventLevel.Warning, EventType.EventUI_Notify)); } private void _gem_ControlStateChanged(object sender, SECsEventArgs e) { if (_state != _gem.ControlState) { _state = _gem.ControlState; if (_state == ControlState.OnlineRemote) { //EV.PostInfoLog("FA", Aitex.RT.Properties.Resources.HostControlModeChangeTo_stateNotifySystemChangeToAutoMode); //Singleton.Instance.PostMsg((int)PMEntity.MSG.SetAutoMode); } } } public void Invoke(string method, object[] args) { switch (method) { case "FAEnable": _gem.SetEnable(); break; case "FADisable": _gem.SetDisable(); break; case "FAOnline": _gem.SetOnline(); break; case "FAOffline": _gem.SetOffline(); break; case "FALocal": _gem.SetLocal(); break; case "FARemote": _gem.SetRemote(); break; case "FAEnableSpooling": SetEnableSpooling(); break; case "FADisableSpooling": SetDisableSpooling(); break; } } private bool _gem_RemoteCommandS2F41In(SECsTransaction trans) { try { bool ret = true; string reason = string.Empty; SECsMessage reply = _gem.Services.CustomMessage.CreateMessage(2, 42, false); reply.DataItem.AddList(); if (ret) { reply.DataItem[0].Add("HCACK", 0, SECsFormat.Binary); } _gem.SendReply(reply, trans.Id); } catch (Exception ex) { LOG.Write(string.Format("Handle_S2F41 Exception: {0}", ex.Message)); SECsMessage reply = _gem.Services.CustomMessage.CreateMessage(2, 42, false); reply.DataItem.AddList(); reply.DataItem[0].Add("HCACK", 3/*At least one parameter is invalid*/, SECsFormat.Binary); reply.DataItem[0].AddList(); reply.DataItem[0][1].AddList(); reply.DataItem[0][1].Add("CPVALUE", ex.Message, SECsFormat.Ascii); _gem.SendReply(reply, trans.Id); } return true; } private bool _gem_RemoteCommandS2F49In(SECsTransaction trans) { try { bool ret = true; string reason = string.Empty; SECsMessage reply = _gem.Services.CustomMessage.CreateMessage(2, 50, false); string remoteCommandName = trans.Primary.DataItem[0][2].ToString(); switch (remoteCommandName.ToUpper()) { case "MAPCASSETTE": { if (trans.Primary.DataItem[0][3][0][0].ToString() != "PortID") throw new Exception($"MAPCASSETTE Command Format Not Correct, CPNAME1 should be PortID"); string portID = trans.Primary.DataItem[0][3][0][1].ToString(); ret = _equipment.MapCassette(portID, out reason); } break; case "PP-SELECT": { if (trans.Primary.DataItem[0][3][0][0].ToString() != "PortID") throw new Exception($"PP-SELECT Command Format Not Correct, CPNAME1 should be PortID"); if (trans.Primary.DataItem[0][3][1][0].ToString() != "JobID") throw new Exception($"PP-SELECT Command Format Not Correct, CPNAME2 should be JobID"); if (trans.Primary.DataItem[0][3][2][0].ToString() != "LotID") throw new Exception($"PP-SELECT Command Format Not Correct, CPNAME3 should be LotID"); if (trans.Primary.DataItem[0][3][3][0].ToString() != "SequenceID") throw new Exception($"PP-SELECT Command Format Not Correct, CPNAME4 should be SequenceID"); string[] slotSequence = new string[25]; for (int i = 0; i < 25; i++) { slotSequence[i] = trans.Primary.DataItem[0][3][3][1][i].ToString(); if (slotSequence[i].Contains("*null")) slotSequence[i] = string.Empty; } string portID = trans.Primary.DataItem[0][3][0][1].ToString(); string jobID = trans.Primary.DataItem[0][3][1][1].ToString(); string lotID = trans.Primary.DataItem[0][3][2][1].ToString(); ret = _equipment.PPSelect(portID, jobID, lotID, slotSequence, out reason); } break; case "STARTJOB": { if (trans.Primary.DataItem[0][3][0][0].ToString() != "JobID") throw new Exception($"STARTJOB Command Format Not Correct, CPNAME1 should be JobID"); string jobID = trans.Primary.DataItem[0][3][0][1].ToString(); ret = _equipment.StartJob(jobID, out reason); } break; case "ABORTJOB": { if (trans.Primary.DataItem[0][3][0][0].ToString() != "JobID") throw new Exception($"ABORTJOB Command Format Not Correct, CPNAME1 should be JobID"); string jobID = trans.Primary.DataItem[0][3][0][1].ToString(); ret = _equipment.AbortJob(jobID, out reason); } break; case "PAUSEJOB": { if (trans.Primary.DataItem[0][3][0][0].ToString() != "JobID") throw new Exception($"PAUSEJOB Command Format Not Correct, CPNAME1 should be JobID"); string jobID = trans.Primary.DataItem[0][3][0][1].ToString(); ret = _equipment.PauseJob(jobID, out reason); } break; case "RESUMEJOB": { if (trans.Primary.DataItem[0][3][0][0].ToString() != "JobID") throw new Exception($"RESUMEJOB Command Format Not Correct, CPNAME1 should be JobID"); string jobID = trans.Primary.DataItem[0][3][0][1].ToString(); ret = _equipment.ResumeJob(jobID, out reason); } break; case "STOPJOB": { if (trans.Primary.DataItem[0][3][0][0].ToString() != "JobID") throw new Exception($"STOPJOB Command Format Not Correct, CPNAME1 should be JobID"); string jobID = trans.Primary.DataItem[0][3][0][1].ToString(); ret = _equipment.StopJob(jobID, out reason); } break; case "LOAD": { if (trans.Primary.DataItem[0][3][0][0].ToString() != "PortID") throw new Exception($"LOAD Command Format Not Correct, CPNAME1 should be PortID"); string portID = trans.Primary.DataItem[0][3][0][1].ToString(); ret = _equipment.Load(portID, out reason); } break; case "UNLOAD": { if (trans.Primary.DataItem[0][3][0][0].ToString() != "PortID") throw new Exception($"UNLOAD Command Format Not Correct, CPNAME1 should be PortID"); string portID = trans.Primary.DataItem[0][3][0][1].ToString(); ret = _equipment.Unload(portID, out reason); } break; case "LOCK": { if (trans.Primary.DataItem[0][3][0][0].ToString() != "PortID") throw new Exception($"LOCK Command Format Not Correct, CPNAME1 should be PortID"); string portID = trans.Primary.DataItem[0][3][0][1].ToString(); ret = _equipment.Lock(portID, out reason); } break; case "UNLOCK": { if (trans.Primary.DataItem[0][3][0][0].ToString() != "PortID") throw new Exception($"UNLOCK Command Format Not Correct, CPNAME1 should be PortID"); string portID = trans.Primary.DataItem[0][3][0][1].ToString(); ret = _equipment.Unlock(portID, out reason); } break; case "READID": { if (trans.Primary.DataItem[0][3][0][0].ToString() != "PortID") throw new Exception($"READID Command Format Not Correct, CPNAME1 should be PortID"); string portID = trans.Primary.DataItem[0][3][0][1].ToString(); ret = _equipment.ReadID(portID, out reason); } break; case "WRITEID": { if (trans.Primary.DataItem[0][3][0][0].ToString() != "PortID") throw new Exception($"WRITEID Command Format Not Correct, CPNAME1 should be PortID"); if (trans.Primary.DataItem[0][3][1][0].ToString() != "CarrierID") throw new Exception($"WRITEID Command Format Not Correct, CPNAME2 should be CarrierID"); string portID = trans.Primary.DataItem[0][3][0][1].ToString(); string carrierID = trans.Primary.DataItem[0][3][1][1].ToString(); ret = _equipment.WriteID(portID, carrierID, out reason); } break; case "READTAG": { if (trans.Primary.DataItem[0][3][0][0].ToString() != "PortID") throw new Exception($"READTAG Command Format Not Correct, CPNAME1 should be PortID"); string portID = trans.Primary.DataItem[0][3][0][1].ToString(); ret = _equipment.ReadTag(portID, out reason); } break; case "WRITETAG": { if (trans.Primary.DataItem[0][3][0][0].ToString() != "PortID") throw new Exception($"WRITEID Command Format Not Correct, CPNAME1 should be PortID"); if (trans.Primary.DataItem[0][3][1][0].ToString() != "TagData") throw new Exception($"WRITEID Command Format Not Correct, CPNAME2 should be TagData"); string portID = trans.Primary.DataItem[0][3][0][1].ToString(); string tagData = trans.Primary.DataItem[0][3][1][1].ToString(); ret = _equipment.WriteTag(portID, tagData, out reason); } break; } reply.DataItem.AddList(); if (ret) { reply.DataItem[0].Add("HCACK", 0, SECsFormat.Binary); } else { reply.DataItem[0].Add("HCACK", 3, SECsFormat.Binary); reply.DataItem[0].Add("CPVALUE", reason, SECsFormat.Ascii); } _gem.SendReply(reply, trans.Id); } catch (Exception ex) { SECsMessage reply = _gem.Services.CustomMessage.CreateMessage(2, 50, false); reply.DataItem.AddList(); reply.DataItem[0].Add("HCACK", 3/*At least one parameter is invalid*/, SECsFormat.Binary); reply.DataItem[0].AddList(); reply.DataItem[0][1].AddList(); reply.DataItem[0][1].Add("CPVALUE", ex.Message, SECsFormat.Ascii); _gem.SendReply(reply, trans.Id); } return true; } private void _gem_PrimaryMessageIn(object sender, SECsPrimaryInEventArgs e) { string SF = "S" + e.Inputs.Stream.ToString() + "F" + e.Inputs.Function.ToString(); switch (SF) { case "S7F1": Handle_S7F1(e); break; case "S7F5": Handle_S7F5(e); break; case "S7F3"://down load recipe Handle_S7F3(e); //Host must send S7F1/F2 and equipment application must either grant the host requests. break; case "S7F17": Handle_S7F17(e); break; case "S7F19": Handle_S7F19(e); break; case "S7F25": Handle_S7F25(e); break; case "S10F3"://Single terminal message Handle_S10F3(e); break; case "S10F5"://Multiple terminal message Handle_S10F5(e); break; } switch (e.EventId) { case PrimaryEventType.ProcessProgramLoadInquire: //S7F1 break; } } /// /// Host requests the equipment to delete the Process Program /// L,n (Number of process programs to be deleted) /// 1. /// . /// . /// n. /// Exception: If n=0, then delete all. /// /// private void Handle_S7F17(SECsPrimaryInEventArgs e) { try { string reason = string.Empty; if (e.Inputs.DataItem[0].Count == 0) { LOG.Write("Handle_S7F17, delete all recipes"); DeleteAllRecipes(out reason); } else // Delete the list of selected Process Program { List deleteRecipes = new List(); for (int i = 0; i < e.Inputs.DataItem[0].Count; i++) { string ppid = e.Inputs.DataItem[0][i].ToString(); deleteRecipes.Add(ppid); LOG.Write("Handle_S7F17, delete recipe:" + ppid); } DeleteRecipe(deleteRecipes, out reason); } //give reply to host SECsMessage messageToReply = _gem.Services.ProcessProgram.DeleteProcessProgramAcknowledge(0); _gem.SendReply(messageToReply, e.TransactionID); } catch (Exception ex) { LOG.Write("Handle_S7F17 Exception: " + ex.Message); //give reply to host SECsMessage messageToReply = _gem.Services.ProcessProgram.DeleteProcessProgramAcknowledge(4/*error*/); _gem.SendReply(messageToReply, e.TransactionID); } } protected void DeleteAllRecipes(out string reason) { reason = string.Empty; var recipes = RecipeFileManager.Instance.GetSequenceNameList(); foreach (var name in recipes) { if (!RecipeFileManager.Instance.DeleteSequence(name)) { EV.PostWarningLog("System", $"Can not delete {name}, delete all failed."); return; } } EV.PostInfoLog("System", "All recipe deleted"); } protected void DeleteRecipe(List recipeFiles, out string reason) { reason = string.Empty; foreach (var name in recipeFiles) { if (!RecipeFileManager.Instance.DeleteSequence(name)) { EV.PostWarningLog("System", $"Can not delete {name}."); return; } else { EV.PostWarningLog("System", $"Delete recipe {name}."); } } } private void Handle_S7F3(SECsPrimaryInEventArgs e) { try { LOG.Write("Handle_S7F4, Request down load recipe"); string ppid = e.Inputs.DataItem[0][0].Value.ToString(); string reason = string.Empty; // Get the format of PPBODY SECsFormat bodyType = e.Inputs.DataItem[0][1].Format; bool recipeSuccess = false; if (bodyType == SECsFormat.Binary) { recipeSuccess = DownLoadRecipeInByte(ppid, (byte[])e.Inputs.DataItem[0][1].Value, out reason); } else { recipeSuccess = DownLoadRecipe(ppid, e.Inputs.DataItem[0][1].ToString(), out reason); } if (recipeSuccess) { SECsMessage acknowledge = _gem.Services.ProcessProgram.ProcessProgramAcknowledge(0); _gem.SendReply(acknowledge, e.TransactionID); } else { //1:download recipe body fail:Permission Not Granted //2:download recipe body fail:Length Error //3:download recipe body fail:Matrix Overflow //4:download recipe body fail:PPID Not Found //5:download recipe body fail:Mode Unsupported SECsMessage acknowledge = _gem.Services.ProcessProgram.ProcessProgramAcknowledge(5); _gem.SendReply(acknowledge, e.TransactionID); } } catch (Exception ex) { LOG.Write("Handle_S7F4 exception:" + ex.Message); SECsMessage acknowledge = _gem.Services.ProcessProgram.ProcessProgramAcknowledge(1); _gem.SendReply(acknowledge, e.TransactionID); } } protected bool DownLoadRecipe(string name, string content, out string reason) { bool ret = false; reason = string.Empty; ret = RecipeFileManager.Instance.SaveSequence(name, content, false); if (!ret) { reason = string.Format("save recipe content failed,recipeName:{0}", name); LOG.Write(reason); } return ret; } protected bool DownLoadRecipeInByte(string name, byte[] content, out string reason) { bool ret = false; reason = string.Empty; ret = RecipeFileManager.Instance.SaveSequence(name, System.Text.Encoding.UTF8.GetString(content), false); if (!ret) { reason = string.Format("save recipe content failed,recipeName:{0}", name); LOG.Write(reason); } return ret; } /// /* 0 = OK 1 = Already have 2 = No space 3 = Invalid PPID 4 = Busy, try later 5 = Will not accept >5 = Other error 6-63 Reserved */ /// /// private void Handle_S7F1(SECsPrimaryInEventArgs e) { try { LOG.Write("Handle_S7F1, Process Program Load Inquire"); string ppid = e.Inputs.DataItem[0][0].Value.ToString(); var recipeList = RecipeFileManager.Instance.GetSequenceNameList(); int ppGrant = 0; if (!recipeList.Contains(ppid)) ppGrant = 3; SECsMessage reply = _gem.Services.CustomMessage.CreateMessage(7, 2, false); reply.DataItem.Add("PPGNT", ppGrant, SECsFormat.Binary); _gem.SendReply(reply, e.TransactionID); } catch (Exception ex) { LOG.Write("Handle_S7F1 exception:" + ex.Message); SECsMessage reply = _gem.Services.CustomMessage.CreateMessage(7, 0, false); _gem.SendReply(reply, e.TransactionID); } } private void Handle_S7F5(SECsPrimaryInEventArgs e) { try { LOG.Write("Handle_S7F5,Used to request the transfer of a process program"); string ppid = e.Inputs.DataItem[0].ToString(); string reason = string.Empty; string data = RecipeFileManager.Instance.GetSequence(ppid, false); if (!string.IsNullOrEmpty(data)) { byte[] ppbody = System.Text.Encoding.UTF8.GetBytes(data); SECsMessage recipetToReply = _gem.Services.ProcessProgram.ProcessProgramData(ppid, ppbody); _gem.SendReply(recipetToReply, e.TransactionID); } else { SECsMessage reply = _gem.Services.CustomMessage.CreateMessage(7, 6, false); reply.DataItem.AddList(); reply.DataItem[0].Add("RVIACK", 0x01/*abnormal*/, SECsFormat.Binary); reply.DataItem[0].Add("CPVALUE", reason, SECsFormat.Ascii); _gem.SendReply(reply, e.TransactionID); } } catch (Exception ex) { LOG.Write("Handle_S7F6 exception:" + ex.Message); SECsMessage reply = _gem.Services.CustomMessage.CreateMessage(7, 6, false); reply.DataItem.AddList(); _gem.SendReply(reply, e.TransactionID); } } private void Handle_S7F19(SECsPrimaryInEventArgs e) { try { string reason = string.Empty; LOG.Write("Handle_S7F19, Request the transmission of the current equipment process program directory"); var recipeList = RecipeFileManager.Instance.GetSequenceNameList(); SECsMessage reply = _gem.Services.CustomMessage.CreateMessage(7, 20, false); reply.DataItem.AddList(); for (int rNum = 0; rNum < recipeList.Count; rNum++) { reply.DataItem[0].Add("PPID", recipeList[rNum], SECsFormat.Ascii); } _gem.SendReply(reply, e.TransactionID); } catch (Exception ex) { LOG.Write("Handle_S7F19 exception:" + ex.Message); SECsMessage reply = _gem.Services.CustomMessage.CreateMessage(7, 0, false); _gem.SendReply(reply, e.TransactionID); } } private void Handle_S7F25(SECsPrimaryInEventArgs e) { try { var ppid = e.Inputs.DataItem[0].Value.ToString(); LOG.Write("Handle_S7F25, request a particular process program from the other"); SECsMessage reply = _gem.Services.CustomMessage.CreateMessage(7, 26, false); reply.DataItem.AddList(); var parameters = GetRecipe(ppid, out string reason); if (parameters != null) { reply.DataItem[0].Add("PPID", ppid, SECsFormat.Ascii); reply.DataItem[0].Add("MDLN", _gem.EquipmentModel.Nameable.model, SECsFormat.Ascii); reply.DataItem[0].Add("SOFTREV", _gem.EquipmentModel.Nameable.softwareRev, SECsFormat.Ascii); reply.DataItem[0].AddList(); reply.DataItem[0][3].AddList(); reply.DataItem[0][3][0].Add("CCODE", 1, SECsFormat.U1); reply.DataItem[0][3][0].AddList(); for (int i = 0; i < parameters.Count; i++) { reply.DataItem[0][3][0][1].Add($"PPARM{i + 1}", parameters[i], SECsFormat.Ascii); } } else { EV.PostWarningLog("FA", $"Request {ppid} failed, {reason}"); } _gem.SendReply(reply, e.TransactionID); } catch (Exception ex) { LOG.Write("Handle_S7F25 exception:" + ex.Message); SECsMessage reply = _gem.Services.CustomMessage.CreateMessage(7, 0, false); _gem.SendReply(reply, e.TransactionID); } } public List GetRecipe(string pathName, out string reason) { List result = new List(); reason = string.Empty; try { string content = string.Empty; if (RecipeFileManager.Instance.CheckSequenceFileExist(pathName)) content = RecipeFileManager.Instance.GetSequence(pathName, false); if (string.IsNullOrEmpty(content)) { if (pathName.StartsWith("PMA") && !pathName.StartsWith("PMA\\Process") && !pathName.StartsWith("PMA\\Clean")) pathName = pathName.Replace("PMA", "PMA\\Process"); if (pathName.StartsWith("PMB") && !pathName.StartsWith("PMB\\Process") && !pathName.StartsWith("PMB\\Clean")) pathName = pathName.Replace("PMB", "PMB\\Process"); content = RecipeFileManager.Instance.LoadRecipe("", pathName, false); } result.Add(content); } catch (Exception ex) { LOG.Write(ex); reason = ex.Message; } return result; } public List GetFormattedSequence(string sequenceName, out string reason) { List result = new List(); try { string sequenceData = RecipeFileManager.Instance.GetSequence(sequenceName, false); XmlDocument recipeDoc = new XmlDocument(); recipeDoc.LoadXml(sequenceData); XmlNodeList stepNodeList = recipeDoc.SelectNodes("/Aitex/TableSequenceData/Step"); for (int i = 0; i < stepNodeList.Count; i++) { XmlElement stepNode = stepNodeList[i] as XmlElement; Dictionary dic = new Dictionary(); //遍历Step节点 foreach (XmlAttribute att in stepNode.Attributes) { result.Add($"STEP{i + 1}/{att.Name}:{att.Value}"); } } reason = string.Empty; } catch (Exception ex) { LOG.Write(ex); reason = ex.Message; } return result; } public List GetFormattedRecipe(string recipeName, out string reason) { List result = new List(); try { string sequenceData = RecipeFileManager.Instance.LoadRecipe("", recipeName, false); XmlDocument recipeDoc = new XmlDocument(); recipeDoc.LoadXml(sequenceData); XmlNodeList stepNodeList = recipeDoc.SelectNodes("/TableRecipeData/Step"); for (int i = 0; i < stepNodeList.Count; i++) { XmlElement stepNode = stepNodeList[i] as XmlElement; Dictionary dic = new Dictionary(); //遍历Step节点 foreach (XmlAttribute att in stepNode.Attributes) { result.Add($"STEP{i + 1}/{att.Name}:{att.Value}"); } //遍历Step子节点中所有的attribute属性节点 foreach (XmlElement subStepNode in stepNode.ChildNodes) { foreach (XmlAttribute att in subStepNode.Attributes) { result.Add($"STEP{i + 1}/{subStepNode.Name}/{att.Name}:{att.Value}"); } //遍历Step子节点的子节点中所有的attribute属性节点 foreach (XmlElement subsubStepNode in subStepNode.ChildNodes) { foreach (XmlAttribute att in subsubStepNode.Attributes) { result.Add($"STEP{i + 1}/{subStepNode.Name}/{subsubStepNode.Name}/{att.Name}:{att.Value}"); } } } } reason = string.Empty; } catch (Exception ex) { LOG.Write(ex); reason = ex.Message; } return result; } /// /// Single terminal message process /// S10F3 Format /// L,2 /// 1. /// 2. /// /// private void Handle_S10F3(SECsPrimaryInEventArgs e) { try { LOG.Write("Process host single terminal message"); string terminalMessage = e.Inputs.DataItem["Ln"]["TEXT"].ToString(); // notify equipment to show terminal message OnReceivedSingleTerminalMessage(terminalMessage); // acknowledge Host SECsMessage reply = _gem.Services.TerminalDisplay.TerminalDisplayAcknowledge(0); reply.Function = 4; _gem.SendReply(reply, e.TransactionID); } catch (Exception ex) { LOG.Write("Handle_S10F3 Exception: " + ex.Message); SECsMessage reply = _gem.Services.TerminalDisplay.TerminalDisplayAcknowledge(1/*will not display*/); reply.Function = 4; _gem.SendReply(reply, e.TransactionID); } } /// /// Multiple terminal message process /// S10F5 Format /// L,2 /// 1. /// 2. L,N /// 1. /// . /// . /// n. /// /// private void Handle_S10F5(SECsPrimaryInEventArgs e) { try { LOG.Write("Process host multiple terminal message"); List receivedMultipleMessages = new List(); string message = string.Empty; for (int i = 0; i < e.Inputs.DataItem[0][1].Count; i++) { receivedMultipleMessages.Add((string)e.Inputs.DataItem[0][1][i].Value); } // notify equipment to show terminal message OnReceivedMultipleTerminalMessage(receivedMultipleMessages); // acknowledge Host SECsMessage reply = _gem.Services.TerminalDisplay.TerminalDisplayAcknowledge(0); reply.Function = 6; _gem.SendReply(reply, e.TransactionID); } catch (Exception ex) { LOG.Write("Handle_S10F5 Exception: " + ex.Message); SECsMessage reply = _gem.Services.TerminalDisplay.TerminalDisplayAcknowledge(1/*will not display*/); reply.Function = 6; _gem.SendReply(reply, e.TransactionID); } } /// /// On single terminal message received /// /// protected virtual void OnReceivedSingleTerminalMessage(string terminalMessage) { EV.Notify("Host", EventTerminalMessage, terminalMessage); } /// /// On multiple terminal message received /// /// protected virtual void OnReceivedMultipleTerminalMessage(List terminalMessage) { foreach (var message in terminalMessage) { EV.Notify("Host", EventTerminalMessage, message); } } public void Terminate() { _faMonitorThread.Stop(); _logCleaner.Stop(); } public void Enable() { _gem.SetEnable(); } public void Disable() { _gem.SetDisable(); } public void NotifyEvent(string eventName, Dictionary dvid, Dictionary objDvid) { _lstEvent.Enqueue(new FaEventItem() { dvid = dvid, objDvid = objDvid, EventName = eventName, IsAlarm = false }); } public void NotifyAlarm(string alarmName, Dictionary dvid, Dictionary objDvid, string text) { _lstEvent.Enqueue(new FaEventItem() { dvid = dvid, objDvid = objDvid, EventName = alarmName, IsAlarm = true, Text = text}); } public void NotifyEvent(string eventName, Dictionary dvid) { _lstEvent.Enqueue(new FaEventItem() { dvid = dvid, EventName = eventName, IsAlarm = false }); } public void NotifyAlarm(string alarmName, Dictionary dvid, string text) { _lstEvent.Enqueue(new FaEventItem() { dvid = dvid, EventName = alarmName, IsAlarm = true, Text = text}); } public void SetLocalControl() { Task.Factory.StartNew(() => { _gem.SetLocal(); }); } public void SetRemoteControl() { Task.Factory.StartNew(() => { _gem.SetRemote(); }); } public void SetEnableSpooling() { Task.Factory.StartNew(() => { _gem.SetAttribute(GEMVariables.SpoolEnabled, AttributeType.EC, "true"); }); } public void SetDisableSpooling() { Task.Factory.StartNew(() => { _gem.SetAttribute(GEMVariables.SpoolEnabled, AttributeType.EC, "false"); }); } public bool MonitorFaTask() { try { SynchronizeSVIDValue(); FaEventItem ev; while (_lstEvent.TryDequeue(out ev)) { if (ev.dvid != null) { foreach (var dvid in ev.dvid) { SetDVIDValue(dvid.Key, dvid.Value); } } if (ev.objDvid != null) { foreach (var dvid in ev.objDvid) { SetDVIDValue(dvid.Key, dvid.Value); } } SetDVIDValue(DVIDName.EventName, ev.EventName); if (ev.IsAlarm) { SetAlarm(ev.EventName, ev.Text); } else { SendEvent(ev.EventName); } //Thread.Sleep(500); } } catch (Exception ex) { System.Diagnostics.Trace.WriteLine(ex); } return true; } private void OnCommunicationStateChanged(object sender, SECsEventArgs e) { if (_gem.CommunicationState == CommunicationState.EnabledCommunicating) { if (SC.ContainsItem("System.FA.DefaultToOnline") && SC.GetValue("System.FA.DefaultToOnline")) _gem.SetOnline(); if (SC.ContainsItem("System.FA.DefaultToRemote") && SC.GetValue("System.FA.DefaultToRemote")) _gem.SetRemote(); } } /// /// Send terminal message to host /// /// public void SendTerminalMessageToHost(string message) { if (!_gem.IsConnected || _gem.CommunicationState == CommunicationState.Disabled || _gem.CommunicationState == CommunicationState.WaitDelay || _gem.CommunicationState == CommunicationState.EnabledNotCommunicating) { EV.PostWarningLog("FA", "Host not connected, send terminal message failed."); return; } LOG.Write("Send terminal message to host:" + message); Task.Factory.StartNew(() => { SECsMessage secsMsg = _gem.Services.TerminalDisplay.TerminalRequest(message); _gem.Send(secsMsg); }); } public void SynchronizeSVIDValue() { try { foreach (SVID sv in _gem.EquipmentModel.StatusVariables.SVIDCollection) { if (sv != null && !_systemBuildInVariables.Contains(sv.logicalName)) { if (sv.valueType == SECSFormats.List) { List svData = _equipment.GetListSvidValue(sv.logicalName); if (svData != null && svData.Count > 0) { SECsDataItem data = new SECsDataItem(SECsFormat.List); foreach (var item in svData) { data.Add(item, item); } _gem.SetListAttribute(sv.logicalName, AttributeType.SV, data); } else { SECsDataItem data = new SECsDataItem(SECsFormat.List); data.Clear(); _gem.SetListAttribute(sv.logicalName, AttributeType.SV, data); } } else { string svDataValue = _equipment.GetSvidValue(sv.logicalName); if (!string.IsNullOrEmpty(svDataValue)) { if (sv.valueType == SECSFormats.Boolean) svDataValue = ConvertToBoolean(svDataValue).ToString(); _gem.SetAttribute(sv.logicalName, AttributeType.SV, svDataValue); } } } } } catch (Exception ex) { LOG.Write("Synchronize FA Model Data exception:" + ex.Message); } } private bool SetDVIDValue(string localName, object value) { if (value == null) return false; if (_gem.EquipmentModel == null || !_gem.EquipmentModel.DataVariables.DVIDCollection.IsExistLogicalName(localName)) return false; foreach (VariableType dv in _gem.EquipmentModel.DataVariables.DVIDCollection) { if (dv.logicalName == localName) { if (localName == "RecipeStepEndDataSummary") { List lwsdata = (List)value; SECsDataItem ldata = new SECsDataItem(SECsFormat.List); foreach (FdcDataItem wsd in lwsdata) { SECsDataItem data = new SECsDataItem(SECsFormat.List); data.Add("Process item", wsd.Name); data.Add("Min", wsd.MinValue.ToString()); data.Add("Max", wsd.MaxValue.ToString()); data.Add("Mean", wsd.MeanValue.ToString()); data.Add("SetPoint", wsd.SetPoint.ToString()); data.Add("Std", wsd.StdValue.ToString()); data.Add("SampleCount", wsd.SampleCount.ToString()); ldata.Add(data); } _gem.SetListAttribute(localName, AttributeType.DV, ldata); return true; } if (dv.valueType == SECSFormats.Boolean) { value = ConvertToBoolean(Convert.ToString(value)).ToString(); } break; } } _gem.SetAttribute(localName, AttributeType.DV, Convert.ToString(value)); return true; } private bool SetDVIDValue(string localName, string value) { if (value == null) return false; //if (!string.IsNullOrEmpty(value)) //{ if (_gem.EquipmentModel == null || !_gem.EquipmentModel.DataVariables.DVIDCollection.IsExistLogicalName(localName)) return false; foreach (VariableType dv in _gem.EquipmentModel.DataVariables.DVIDCollection) { if (dv.logicalName == localName) { if (dv.valueType == SECSFormats.Boolean) { value = ConvertToBoolean(value).ToString(); } break; } } _gem.SetAttribute(localName, AttributeType.DV, value); //} return true; } private bool ConvertToBoolean(string value) { if (value == "0" || value.ToLower() == "false") return false; else return true; } private void SendEvent(string eventName) { try { if (_gem.EquipmentModel != null) { var allEvents = _gem.GetAllEnabledEvents(); if (allEvents.Contains(eventName)) { _gem.SendCollectionEvent(eventName); } else { //LOG.Write(string.Format("sendEvent failed,not find:", eventName)); } //LOG.Write(string.Format("【FA2SendEvent--{0}】", eventName)); } } catch (Exception ex) { LOG.Write("SendEvent Error:" + ex.Message); } } public void SetAlarm(string alarmTag, string text) { Task.Factory.StartNew(() => { //if (_gem.IsAlarmSet(alarmTag)) //{ // _gem.ClearAlarm(alarmTag); //} var allAlarms = _gem.GetAllEnabledAlarms(); if (allAlarms.Contains(alarmTag)) { _gem.EquipmentModel.Alarms[alarmTag].description = text; _gem.SetAlarm(alarmTag); } else { //WriteLog(string.Format("sendAlarm failed,not find:", alarmTag)); } //WriteLog(string.Format("【FA3SetAlarm--{0}】", alarmTag)); }); } public void ClearAlarm(string alarmTag) { try { var allsetalarms = _gem.GetAllSetAlarms(); if (allsetalarms != null && allsetalarms.Count > 0 && allsetalarms.Contains(alarmTag)) _gem.ClearAlarm(alarmTag); if (string.IsNullOrEmpty(alarmTag)) { foreach (var allsetalarm in allsetalarms) { _gem.ClearAlarm(allsetalarm.ToString()); } } } catch (Exception ex) { LOG.Write($"Clear Alarm Error : {ex.Message}"); } } } }