570 lines
22 KiB
C#
570 lines
22 KiB
C#
using Aitex.Core.RT.DataCenter;
|
|
using Aitex.Core.RT.OperationCenter;
|
|
using Aitex.Core.Util;
|
|
using Kxware.Common;
|
|
using Kxware.ToolAutomation.Capabilities;
|
|
using Kxware.ToolAutomation;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Kxware.Connectivity.Remoting;
|
|
using Aitex.Common.Util;
|
|
using Kxware.Automation.Secs;
|
|
using System.IO;
|
|
|
|
|
|
namespace SicRT.Gem
|
|
{
|
|
public class GemManager : Singleton<GemManager>, ITerminalMessageHandler
|
|
{
|
|
public string PPID { get; set; }
|
|
|
|
public string LotID { get; set; }
|
|
|
|
private string JobID = "";
|
|
|
|
private string RtStatus => DATA.Poll("Rt.Status").ToString();
|
|
|
|
private Kxware.ToolAutomation.Equipment _equipment;
|
|
public Kxware.ToolAutomation.Equipment Equipment => _equipment;
|
|
|
|
private PeriodicJob _monitorJob;
|
|
|
|
public string CommunicationState => _equipment.GetCommunicationState().ToString();
|
|
|
|
public string ControlMode => _equipment.GetControlState().ToString();
|
|
|
|
public string SequenceContent { get; set; }
|
|
|
|
public string ProgramLog { get;set; }
|
|
|
|
public string SecsLog { get; set; }
|
|
|
|
public List<string> AlarmInfoList { get; set; } = new List<string>();
|
|
|
|
public List<string> EventInfoList { get; set; } = new List<string>();
|
|
|
|
public List<string> SequenceNameList
|
|
{
|
|
get
|
|
{
|
|
return _equipment?.GetRecipeList(_equipment.MasterConnectionName);
|
|
}
|
|
}
|
|
|
|
public List<Kxware.Connectivity.Remoting.VariableData> VariableList
|
|
{
|
|
get
|
|
{
|
|
var result = _equipment?.GetVariables()?.Select(p => new Kxware.Connectivity.Remoting.VariableData()
|
|
{
|
|
ConnectionName = p.ConnectionName,
|
|
SmlValue = p.GetSecsValue().ToSML(SmlFormat.RoundBracket),
|
|
Default = p.Default?.ToString(),
|
|
Description = p.Description,
|
|
HostVisible = p.HostVisible,
|
|
Id = p.Id,
|
|
IsCommonVariable = p.IsCommonVariable,
|
|
LimitEventName = p.LimitEvent?.Name,
|
|
Name = p.Name,
|
|
Max = p.Max?.ToString(),
|
|
Min = p.Min?.ToString(),
|
|
Persistent = p.Persistent,
|
|
Unit = p.Unit,
|
|
VariableType = (enumVariableTypes)p.VariableType
|
|
}).ToList();
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
public List<Kxware.Connectivity.Remoting.EventData> EventList
|
|
{
|
|
get
|
|
{
|
|
var result = _equipment?.GetEvents()?.Select(p => new Kxware.Connectivity.Remoting.EventData()
|
|
{
|
|
EventName = p.Name,
|
|
EventId = p.Id,
|
|
Description = p.Description
|
|
}).ToList();
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
public List<Kxware.Connectivity.Remoting.AlarmData> AlarmList
|
|
{
|
|
get
|
|
{
|
|
var result = _equipment?.GetAlarms()?.Select(p => new Kxware.Connectivity.Remoting.AlarmData()
|
|
{
|
|
Name = p.Name,
|
|
AlarmID = p.ALID,
|
|
ALCD = p.ALCD,
|
|
AlarmText = p.ALTX,
|
|
Description = p.Description
|
|
}).ToList();
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
public void Initialize()
|
|
{
|
|
_equipment = new Kxware.ToolAutomation.Equipment("SicMachine");
|
|
|
|
_equipment.RegisteringVariables += new RegisterVariableDelegate(RegisteringVariables);
|
|
|
|
//_equipment.RegisteringAlarms += new RegisterAlarmDelegate(RegisteringAlarms);
|
|
|
|
_equipment.AlarmSET += new OnAlarmSET(AlarmSET);
|
|
|
|
_equipment.AlarmCLEAR += new OnAlarmCLEAR(AlarmCLEAR);
|
|
|
|
Log.NewMessage += Log_NewMessage;
|
|
|
|
_equipment.DataMessageReceived += DataMessageReceived;
|
|
_equipment.DataMessageSent += DataMessageSent;
|
|
|
|
//initialize SECS/ GEM service from configruation file
|
|
_equipment.Initialize(PathManager.GetCfgDir() + "Gem.exml");
|
|
|
|
//设置Recipe路径
|
|
_equipment.SetValue("PPDirectory", "./Recipes/Sequence");
|
|
_equipment.SetValue("PPFileExtension", "seq");
|
|
|
|
//设置Log路径
|
|
//Log.SetLoggingDirectory("");
|
|
|
|
//register terminal message handler
|
|
_equipment.RegisterTerminalMessageHandler(this);
|
|
|
|
//注册远程操作指令
|
|
_equipment.RegisterRemoteCommandHandler("PP-SELECT", OnReceivedRemoteCommand);
|
|
_equipment.RegisterRemoteCommandHandler("CREATE-JOB", OnReceivedRemoteCommand);
|
|
_equipment.RegisterRemoteCommandHandler("START", OnReceivedRemoteCommand);
|
|
_equipment.RegisterRemoteCommandHandler("ABORT", OnReceivedRemoteCommand);
|
|
|
|
_equipment.EventTriggered += EventTriggered;
|
|
|
|
//enable GEM
|
|
_equipment.GemEnable();
|
|
_equipment.OnlineLocal(_equipment.MasterConnectionName);
|
|
|
|
//注册
|
|
DATA.Subscribe("GEM.CommunicationState", () => CommunicationState);
|
|
DATA.Subscribe("GEM.ControlMode", () => ControlMode);
|
|
DATA.Subscribe("GEM.VariableList", () => VariableList);
|
|
DATA.Subscribe("GEM.EventList", () => EventList);
|
|
DATA.Subscribe("GEM.EventInfoList", () => EventInfoList);
|
|
DATA.Subscribe("GEM.AlarmList", () => AlarmList);
|
|
DATA.Subscribe("GEM.AlarmInfoList", () => AlarmInfoList);
|
|
DATA.Subscribe("GEM.SequenceNameList", () => SequenceNameList);
|
|
DATA.Subscribe("GEM.SequenceContent", () => SequenceContent);
|
|
DATA.Subscribe("GEM.ProgramLog", () => ProgramLog);
|
|
DATA.Subscribe("GEM.SecsLog", () => SecsLog);
|
|
DATA.Subscribe("GEM.PPID", () => PPID);
|
|
DATA.Subscribe("GEM.LotID", () => LotID);
|
|
|
|
OP.Subscribe($"GEM_SetEnable", (string cmd, object[] args) => SetEnable((bool)args[0]));
|
|
OP.Subscribe($"GEM_SetControlMode", (string cmd, object[] args) => SetControlMode(args[0].ToString()));
|
|
OP.Subscribe($"GEM_SelectRecipeChanged", (string cmd, object[] args) => SelectRecipeChanged(args[0].ToString()));
|
|
OP.Subscribe($"GEM_ClearProgramLog", (string cmd, object[] args) => ClearProgramLog());
|
|
OP.Subscribe($"GEM_ClearSecsLog", (string cmd, object[] args) => ClearSecsLog());
|
|
|
|
_monitorJob = new PeriodicJob(1000, Monitor, "GEM_Monitor", true);
|
|
}
|
|
|
|
private bool Monitor()
|
|
{
|
|
try
|
|
{
|
|
//设置变量值
|
|
SetValue();
|
|
|
|
return true;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private void SetValue()
|
|
{
|
|
SortedDictionary<string, Func<object>> keyValues = Singleton<DataManager>.Instance.GetDBRecorderList();
|
|
|
|
foreach (KeyValuePair<string, Func<object>> item in keyValues)
|
|
{
|
|
//判断类型,排除引用类型
|
|
object obj = item.Value();
|
|
if (obj != null)
|
|
{
|
|
Type type = obj.GetType();
|
|
if (type == typeof(bool) || type == typeof(double) || type == typeof(float) || type == typeof(int) || type == typeof(ushort) ||
|
|
type == typeof(short) || type == typeof(long))
|
|
{
|
|
_equipment.SetValue(item.Key, obj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private IEnumerable<VariableDefinition> RegisteringVariables()
|
|
{
|
|
List<VariableDefinition> variableDefinitionList = new List<VariableDefinition>();
|
|
|
|
int index = 0;
|
|
|
|
SortedDictionary<string, Func<object>> keyValues = Singleton<DataManager>.Instance.GetDBRecorderList();
|
|
|
|
foreach (KeyValuePair<string, Func<object>> item in keyValues)
|
|
{
|
|
//判断类型,排除引用类型
|
|
object obj = item.Value();
|
|
if (obj != null)
|
|
{
|
|
Type type = obj.GetType();
|
|
if (type == typeof(bool) || type == typeof(double) || type == typeof(float) || type == typeof(int) || type == typeof(ushort) ||
|
|
type == typeof(short) || type == typeof(long))
|
|
{
|
|
ValueFormat valueFormat = ValueFormat.F8;
|
|
|
|
if(type == typeof(bool))
|
|
{
|
|
valueFormat = ValueFormat.BOOLEAN;
|
|
}
|
|
else if (type == typeof(int))
|
|
{
|
|
valueFormat = ValueFormat.I8;
|
|
}
|
|
else if (type == typeof(long))
|
|
{
|
|
valueFormat = ValueFormat.I8;
|
|
}
|
|
else if (type == typeof(short))
|
|
{
|
|
valueFormat = ValueFormat.I8;
|
|
}
|
|
else if (type == typeof(ushort))
|
|
{
|
|
valueFormat = ValueFormat.U8;
|
|
}
|
|
else if (type == typeof(float))
|
|
{
|
|
valueFormat = ValueFormat.F4;
|
|
}
|
|
else if(type == typeof(double))
|
|
{
|
|
valueFormat = ValueFormat.F4;
|
|
}
|
|
|
|
variableDefinitionList.Add(new VariableDefinition(Kxware.Common.VariableType.DV, valueFormat, (ulong)(1000000 + index), item.Key, (string)null, (string)null, (string)null, (string)null, (string)null, false, true, ""));
|
|
|
|
index++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return variableDefinitionList;
|
|
}
|
|
|
|
private IEnumerable<AlarmDefinition> RegisteringAlarms()
|
|
{
|
|
List<AlarmDefinition> alarmDefinitionList = new List<AlarmDefinition>();
|
|
|
|
int index = 0;
|
|
|
|
SortedDictionary<string, Func<object>> keyValues = Singleton<DataManager>.Instance.GetDBRecorderList();
|
|
|
|
foreach (KeyValuePair<string, Func<object>> item in keyValues)
|
|
{
|
|
//alarmDefinitionList.Add(new AlarmDefinition((ulong)(1000000 + index),));
|
|
|
|
index++;
|
|
}
|
|
|
|
return alarmDefinitionList;
|
|
}
|
|
|
|
private void AlarmSET(string equipmentName, string connectionName, ulong alarmID, string alarmName, string altx, Kxware.Common.AlarmCode alcd, string alarmDescription, string alarmAdditionalInformation, string setEvent, string clearEvent, DateTime occurredTime, Kxware.Common.VariableData[] associatedVariables, DataMessage alarmSecsMessage)
|
|
{
|
|
if (connectionName == _equipment.MasterConnectionName)
|
|
{
|
|
AlarmInfoList.Add($"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")} 【触发】 ID:{alarmID} {altx}");
|
|
while(AlarmInfoList.Count > 100)
|
|
{
|
|
AlarmInfoList.RemoveAt(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void AlarmCLEAR(string equipmentName, string connectionName, ulong alarmID, string alarmName, string altx, Kxware.Common.AlarmCode alcd, string alarmDescription, string alarmAdditionalInformation, string setEvent, string clearEvent, DateTime occurredTime, Kxware.Common.VariableData[] associatedVariables, DataMessage alarmSecsMessage)
|
|
{
|
|
if (connectionName == _equipment.MasterConnectionName)
|
|
{
|
|
AlarmInfoList.Add($"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")} 【清除】 ID:{alarmID} {altx}");
|
|
while (AlarmInfoList.Count > 100)
|
|
{
|
|
AlarmInfoList.RemoveAt(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DataMessageSent(string equipmentName, string connectionName, DateTime time, Kxware.Automation.Secs.DataMessage message)
|
|
{
|
|
if (connectionName == _equipment.MasterConnectionName)
|
|
{
|
|
string msg = $"{time.ToString("yyyy/MM/dd HH:mm:ss.fff")} 【发送】\r\n{message.ToSML()}\r\n";
|
|
Log.Info("GEM", msg);
|
|
SecsLog += msg;
|
|
|
|
//if (SecsLog.Length > 10000)
|
|
//{
|
|
// SecsLog = SecsLog.Substring(SecsLog.Length - 10000);
|
|
//}
|
|
}
|
|
}
|
|
|
|
private void Log_NewMessage(LogType loggingType, string customizedLogTypeName, DateTime timeStamp, string processName, int threadID, string source, string message)
|
|
{
|
|
// make a filter, do not show SML logging message at current window
|
|
if (message.Contains("SECS MSG Recv]") || message.Contains("SECS MSG Sent]"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ProgramLog += $"{timeStamp.ToString("yyyy/MM/dd HH:mm:ss,fff")} {source} 线程ID:{threadID} 内容:{message}\r\n";
|
|
}
|
|
|
|
private void DataMessageReceived(string equipmentName, string connectionName, DateTime time, Kxware.Automation.Secs.DataMessage message)
|
|
{
|
|
if (connectionName == _equipment.MasterConnectionName)
|
|
{
|
|
string msg = $"{time.ToString("yyyy/MM/dd HH:mm:ss.fff")} 【接受】\r\n{message.ToSML()}\r\n";
|
|
Log.Info("GEM",msg);
|
|
SecsLog += msg;
|
|
|
|
//if (SecsLog.Length > 10000)
|
|
//{
|
|
// SecsLog = SecsLog.Substring(SecsLog.Length - 10000);
|
|
//}
|
|
}
|
|
}
|
|
|
|
private void EventTriggered(string equipmentName, string connectionName, ulong eventID, string eventName, string eventDescription, Kxware.Common.VariableData[] associatedVariables, Kxware.Automation.Secs.DataMessage eventSecsMessage)
|
|
{
|
|
if (connectionName == _equipment.MasterConnectionName)
|
|
{
|
|
var associatedDataInfo = "";
|
|
if (associatedVariables.Length > 0)
|
|
{
|
|
associatedDataInfo = "关联数据:" + string.Join(",", associatedVariables.Select(p => p.Name + " " + p.Value.ToSML(Kxware.Common.SmlFormat.RoundBracket)));
|
|
}
|
|
EventInfoList.Add($"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")} 编号:{eventID} 事件名:{eventName} {associatedDataInfo}");
|
|
}
|
|
}
|
|
|
|
private enumRemoteCommandAckCode OnReceivedRemoteCommand(string connectionName, string remoteCommand, List<RemoteCommandParameter> remoteCommandParameters)
|
|
{
|
|
var result = enumRemoteCommandAckCode.CommandDoesNotExist;
|
|
switch (remoteCommand)
|
|
{
|
|
//PP-SELECT指令附加参数(PPID&LotID)
|
|
case "PP-SELECT":
|
|
{
|
|
if (RtStatus != "AutoIdle")
|
|
{
|
|
Log.Warn("GEM", $"Cannot perform 'PP-SELECT' command. Equipment State is not in AutoIdle.");
|
|
result = enumRemoteCommandAckCode.CannotPerformNow;
|
|
}
|
|
else if (remoteCommandParameters.Find(p => p.CPNAME == "PPID") == null)
|
|
{
|
|
Log.Warn("GEM", $"Cannot perform 'PP-SELECT' command. PPID is not specified.");
|
|
result = enumRemoteCommandAckCode.AtLeastOneParameterIsInvalid;
|
|
}
|
|
else if (remoteCommandParameters.Find(p => p.CPNAME == "LotID") == null)
|
|
{
|
|
Log.Warn("GEM", $"Cannot perform 'PP-SELECT' command. LotID is not specified.");
|
|
result = enumRemoteCommandAckCode.AtLeastOneParameterIsInvalid;
|
|
}
|
|
else
|
|
{
|
|
PPID = remoteCommandParameters.Find(p => p.CPNAME == "PPID").CPVAL.GetValueAsString().Replace(".seq","");
|
|
LotID = remoteCommandParameters.Find(p => p.CPNAME == "LotID").CPVAL.GetValueAsString();
|
|
Log.Info("GEM", $"Received 'PP-SELECT' command. PPID={PPID}.seq, LotID={LotID}");
|
|
result = enumRemoteCommandAckCode.AcknowledgeCommandWillBePerformed;
|
|
}
|
|
}
|
|
break;
|
|
|
|
//CREATE-JOB指令没有附加参数
|
|
case "CREATE-JOB":
|
|
{
|
|
if (RtStatus != "AutoIdle")
|
|
{
|
|
Log.Warn("GEM", $"Cannot perform 'CREATE-JOB' command. Equipment State is not in AutoIdle.");
|
|
result = enumRemoteCommandAckCode.CannotPerformNow;
|
|
}
|
|
else
|
|
{
|
|
Log.Info("GEM", $"Received 'CREATE-JOB' command.");
|
|
result = enumRemoteCommandAckCode.AcknowledgeCommandPerformed;
|
|
|
|
//具体操作
|
|
JobID = "CJ_Local_Load" + "_" + DateTime.Now.ToString("HHmmss");
|
|
Dictionary<string, object> dictionary = new Dictionary<string, object>
|
|
{
|
|
{ "JobId", JobID},
|
|
{ "Module", "LoadLock" },
|
|
{"SlotSequence",new string[]{PPID} },
|
|
{ "LotId", LotID },
|
|
{ "AutoStart", true }
|
|
};
|
|
|
|
OP.DoOperation("System.CreateJob", dictionary);
|
|
}
|
|
}
|
|
break;
|
|
|
|
//START指令没有附加参数
|
|
case "START":
|
|
{
|
|
if (RtStatus != "AutoIdle")
|
|
{
|
|
Log.Warn("GEM", $"Cannot perform 'STRAT' command. Equipment State is not in AutoIdle.");
|
|
result = enumRemoteCommandAckCode.CannotPerformNow;
|
|
}
|
|
else
|
|
{
|
|
Log.Info("GEM", $"Received 'STRAT' command.");
|
|
result = enumRemoteCommandAckCode.AcknowledgeCommandPerformed;
|
|
|
|
//具体操作
|
|
OP.DoOperation("System.StartJob", JobID);
|
|
}
|
|
}
|
|
break;
|
|
|
|
//ABORT指令没有附加参数
|
|
case "ABORT":
|
|
{
|
|
if (RtStatus != "AutoRunning")
|
|
{
|
|
Log.Warn("GEM", $"Cannot perform 'ABORT' command. Equipment State is not in AutoRunning.");
|
|
result = enumRemoteCommandAckCode.CannotPerformNow;
|
|
}
|
|
else
|
|
{
|
|
Log.Info("GEM", $"Received 'ABORT' command.");
|
|
result = enumRemoteCommandAckCode.AcknowledgeCommandPerformed;
|
|
|
|
//具体操作
|
|
OP.DoOperation("System.Abort");
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
Log.Error("GEM", $"Un-handled remote command: {remoteCommand}");
|
|
}
|
|
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//private string Base64Encode(string name)
|
|
//{
|
|
// var nameBytes = Encoding.UTF8.GetBytes(name);
|
|
// return UrlBase64.Encode(nameBytes);
|
|
//}
|
|
|
|
//private string Base64Decode(string base64EncodedData)
|
|
//{
|
|
// byte[] encodeBytes = UrlBase64.Decode(base64EncodedData);
|
|
// return Encoding.UTF8.GetString(encodeBytes);
|
|
//}
|
|
|
|
private bool SetEnable(bool enable)
|
|
{
|
|
if (enable)
|
|
{
|
|
_equipment.GemEnable();
|
|
}
|
|
else
|
|
{
|
|
_equipment.GemDisable();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool SetControlMode(string mode)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case "OnlineRemote":
|
|
{
|
|
_equipment.OnlineRemote(_equipment.MasterConnectionName);
|
|
}
|
|
break;
|
|
|
|
case "OnlineLocal":
|
|
{
|
|
_equipment.OnlineLocal(_equipment.MasterConnectionName);
|
|
}
|
|
break;
|
|
|
|
case "Offline":
|
|
{
|
|
_equipment.Offline(_equipment.MasterConnectionName);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool SelectRecipeChanged(string recipeName)
|
|
{
|
|
if (!string.IsNullOrEmpty(recipeName))
|
|
{
|
|
SequenceContent = File.ReadAllText(".\\Recipes\\Sequence\\" + recipeName);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool ClearProgramLog()
|
|
{
|
|
ProgramLog = "";
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool ClearSecsLog()
|
|
{
|
|
SecsLog = "";
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
public enumACKC10 TerminalMessageReceived(Kxware.ToolAutomation.Equipment equipment, string connectionName, int tid, List<string> texts)
|
|
{
|
|
Log.Info("GEM", $"Received TERMIAL message :{connectionName} :{tid}, {string.Join(",", texts)}");
|
|
|
|
return enumACKC10.AcceptedForDisplay;
|
|
}
|
|
|
|
public void Terminate()
|
|
{
|
|
_equipment?.GemDisable();
|
|
_equipment = null;
|
|
}
|
|
}
|
|
}
|