using Aitex.Core.Common; using Aitex.Core.RT.Device; using Aitex.Core.RT.Log; using Aitex.Core.RT.SCCore; using Aitex.Core.Util; using MECF.Framework.Common.Communications; using MECF.Framework.Common.Equipment; using MECF.Framework.Common.SubstrateTrackings; using System; using System.Collections.Generic; using System.IO.Ports; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Venus_Core; using Venus_RT.Devices.EFEM; using Venus_RT.Devices.TM; using Venus_RT.Modules; namespace Venus_RT.Devices.VCE { public class SunWayVce : VCEModuleBase { #region 私有变量 private AsyncSerialPort _serialport; private string _portname; private string _newline = "\r\n";//终止符 0D 0A private object _locker = new object(); private bool _IsAsciiMode; private LinkedList _lstAsciiMsgs = new LinkedList(); private PeriodicJob _thread; private Regex _match_ReadMsg = new Regex(@"X,.*"); private Regex _matchErrorCode = new Regex(@"(?<=_ERR,)(.*)"); private ModuleName _moduleName; private RState _status; private string _currentMsg; private VceMessage _currentVceMessage; private bool _HasReceiveMsg; private bool _IsDashWaferError; private int _currentSlot = 0; public override int CurrentSlot => _currentSlot; private ModuleName _baseLPIndex { get { switch (RtInstance.ConfigType) { case ConfigType.VenusSE: return ModuleName.LP1; case ConfigType.VenusDE: return _moduleName == ModuleName.VCEA ? ModuleName.LP1 : ModuleName.LP2; default: return ModuleName.System; } } } private Loadport[] _LPMs = new Loadport[1]; public override ILoadport this[ModuleName mod] { get { if (!ModuleHelper.IsLoadPort(mod)) throw new ApplicationException($"{mod} is NOT Loadport"); return _LPMs[mod - _baseLPIndex]; } } //待补充 private Dictionary _ErrorCode2Reason = new Dictionary() { {"221","错误的 ARM 参数" }, {"233","伸缩信号未使能" }, {"304","未识别的错误码" }, {"305","未识别的指令" }, {"309","不支持的指令" }, {"401","ZWAFER 参数错误" }, {"402","非法的 slot 参数" }, {"403","运动指令 RX 参数错误" }, {"404","晶圆夹取失败" }, {"405","晶圆释放失败" }, {"406","气缸压力表检测错误" }, {"407","Mapping 传感器伸出失败" }, {"408","Mapping 传感器缩回失败" }, {"409","Mapping 传感器位置数据不正确" }, {"411","Mapping 传感器高度不一致" }, {"412","工位 VIA 参数未使能" }, {"413","不是有效的 Mapping 工位" }, {"414","手指有晶圆无法 mapping" }, {"415","采集的数据个数错误" }, {"416","采集的晶圆厚度过小" }, {"417","晶圆位置超出有效范围" }, {"418","晶圆上下沿槽数错误" }, {"419","斜片上下沿槽数错误" }, {"550","工位号超范围" }, {"552","VIA 参数错误" }, {"553","MAPVIA 参数错误" }, {"600","系统未上电" }, {"602","指令正在执行中" }, {"603","系统上电失败" }, {"604","示教器控制中" }, {"605","机械手运动中停止" }, {"609","系统存在错误" }, {"610","示教盒急停启动" }, {"611","驱动器报警" }, {"629","驱动器存在报警" }, {"705","LOAD 状态未知" }, {"712","真空吸附动作失败" }, {"730","PLACE 前 LOAD 状态错误" }, {"731","PLACE 后 LOAD 状态错误" }, {"734","PLACE 执行 Extend 过程中未检测到晶圆" }, {"736","PLACE 执行 Retract 过程中检测到晶圆" }, {"740","PICK 前 LOAD 状态错误" }, {"741","PICK 后 LOAD 状态错误" }, {"744","PICK 执行 Extend 过程中检测到晶圆" }, {"745","PICK 执行 Retract 过程中未检测到晶圆" }, {"1006","非法的 IO 端口号" }, {"1314","PITCH 轴超界" }, {"1315","关节位置超界" }, {"1316","关节位置超硬限" }, {"1320","机械手跟踪差超限错误" }, {"1321","机械手发生碰撞" }, {"1322","机械手超出工作区" }, {"1323","关节速度超限" }, {"1401","当前位置不正确" }, {"1407","大气压力不足" }, {"1412","晶圆已滑出" }, {"1500","打开门失败" }, {"1501","关闭门失败" }, {"1502","门开关状态不对" }, {"1503","门松开失败" }, {"1504","门夹紧失败" }, {"1506","当前不是自动门模式" }, {"1507","开盖检测互锁" }, {"1508","开门不允许运动" }, {"1509","无 casette 盒不允许运动" }, {"1510","安全检测触发" }, {"1511","R 轴未运动到位" }, {"1512","门动作开关未使能" }, {"1513","运动开关未使能" }, }; // #endregion #region 暴露变量 public override bool IsConnected => _serialport.IsOpen(); public override RState Status => _status; public override bool IsReady => _status == RState.Init || _status == RState.End; public override bool IsError => _status == RState.Failed || _status == RState.Timeout; public override bool IsInit => _status == RState.Init; public override bool IsDashWaferError => _IsDashWaferError; private string[] _slotMap = new string[25]; public string SlotMap { get { WaferInfo[] wafers = WaferManager.Instance.GetWafers(ModuleHelper.Converter(Name)); string slot = ""; for (int i = 0; i < 25; i++) { slot += wafers[i].IsEmpty ? "0" : "1"; } return slot; } } private bool _OutDoorIsOpen { get { switch (_moduleName) { case ModuleName.VCE1: //2024-05-20 16:35:34 泓浒四边形硬件还未实现 //DEVICE.GetDevice("SETM").VCEACassPresent return _vcedoorflag; case ModuleName.VCEA: if (DEVICE.GetDevice("TM").VCEACassPresent) { _LPMs[0].HasCassette = true; } else { _LPMs[0].HasCassette = false; WaferManager.Instance.DeleteWafer(_LPMs[0].Module, 0, 25); } return !DEVICE.GetDevice("TM").VCEALOCKED; case ModuleName.VCEB: if (DEVICE.GetDevice("TM").VCEBCassPresent) { _LPMs[0].HasCassette = true; } else { _LPMs[0].HasCassette = false; WaferManager.Instance.DeleteWafer(_LPMs[0].Module, 0, 25); } return !DEVICE.GetDevice("TM").VCEBLOCKED; default: return false; } } } public override bool OutDoorIsOpen => _OutDoorIsOpen; private bool _vcedoorflag; private bool _hasProtrusion { get { switch (_moduleName) { case ModuleName.VCE1: //2024-05-20 16:35:34 泓浒四边形硬件还未实现 //DEVICE.GetDevice("SETM").VCEProtrusion return true; case ModuleName.VCEA: if (DEVICE.GetDevice("TM").VCEAProtrusion) { _LPMs[0].Protrusion = true; return true; } else { _LPMs[0].Protrusion = false; return false; } case ModuleName.VCEB: if (DEVICE.GetDevice("TM").VCEBProtrusion) { _LPMs[0].Protrusion = true; return true; } else { _LPMs[0].Protrusion = false; return false; } default: return false; } } } #endregion public SunWayVce(int slot, ModuleName moduleName) : base(slot, moduleName) { _moduleName = moduleName; _vcedoorflag = false; _IsAsciiMode = true; _portname = SC.GetStringValue($"{moduleName}.Port"); _serialport = new AsyncSerialPort(_portname, 9600, 8, Parity.None, StopBits.One, _newline, _IsAsciiMode); _serialport.Open(); _status = RState.Init; _serialport.OnDataChanged += onDataChange; _thread = new PeriodicJob(50, fnTimer, _moduleName.ToString(), true); if (moduleName == ModuleName.VCE1) _LPMs[0] = new Loadport(ModuleName.LP1); else _LPMs[0] = new Loadport((moduleName - ModuleName.VCEA) + ModuleName.LP1); CarrierManager.Instance.DeleteCarrier(_LPMs[0].Module.ToString()); WaferManager.Instance.DeleteWafer(_LPMs[0].Module, 0, 25); CarrierManager.Instance.SubscribeLocation(_LPMs[0].Module.ToString(), 1); Action _subscribeLoc = (ModuleName module, int waferCount) => { if (ModuleHelper.IsInstalled(module)) { WaferManager.Instance.SubscribeLocation(module, waferCount); } }; _subscribeLoc(_LPMs[0].Module, slot); } private bool fnTimer() { lock (_locker) { //采用ascii传输 if (_IsAsciiMode) { //有数据尚未处理 while (_lstAsciiMsgs.Count > 0) { string _needHandle = _lstAsciiMsgs.First.Value; HandleSingleMsg(_needHandle); _lstAsciiMsgs.RemoveFirst(); } } //采用binary else { } } return true; } private void HandleSingleMsg(string rawmsgs) { string[] msgs = rawmsgs.Split('\r'); foreach (var Msg in msgs) { string msg = Msg.Trim(); LOG.Write(eEvent.EV_VCE_COMMON_INFO, _moduleName, $"{_moduleName} Receive msg=>{msg}"); if (!string.IsNullOrEmpty(msg)) { //action set petrify _BKGRDY结束 switch (_currentVceMessage.Head) { case VceMessageHead.Action: case VceMessageHead.Set: case VceMessageHead.Petrify: switch (msg) { //设备收到 开始运行 目前状态在下发 case "_RDY": LOG.Write(eEvent.EV_VCE_COMMON_INFO, _moduleName, $"vce {_currentVceMessage.Head} over"); switch (_currentVceMessage.Command) { case VceCommand.Home: case VceCommand.Map: case VceCommand.GotoLP: _currentSlot = 0; break; case VceCommand.DoorClose: _vcedoorflag = false; break; case VceCommand.DoorOpen: _vcedoorflag = true; break; } _status = RState.End; break; //异常处理 default: _status = RState.Failed; string reason; Errorhandle(msg, out reason); LOG.Write(eEvent.ERR_VCE_COMMON_Failed, _moduleName, reason); break; } break; case VceMessageHead.Read: //如果收到的信息符合 if (_match_ReadMsg.IsMatch(msg)) { //收到消息 用于结束 _HasReceiveMsg = true; switch (_currentVceMessage.Command) { //处理wafer 信息为map数据 case VceCommand.ReadMap: ReadMapData(msg); break; case VceCommand.CheckStatus: ReadStatus(msg); break; } } //_RDY查询结束 else { if (msg == "_RDY") { if (_HasReceiveMsg) { _status = RState.End; } else { LOG.Write(eEvent.ERR_VCE_COMMON_Failed, _moduleName, $"Read Message is over but not receive msg! raw message:{_currentMsg}"); _status = RState.Failed; } } else { _status = RState.Failed; LOG.Write(eEvent.ERR_VCE_COMMON_Failed, _moduleName, $"Read Message is invalid: receive message {msg} and send message {_currentMsg}"); } } break; } } } } private void ReadStatus(string msg) { try { //BRa,SLbb,CPc,WPd,ERe string[] status = msg.Split(','); _currentSlot = Convert.ToInt32(status[4].Substring(2, 2)); } catch (Exception ex) { LOG.Write(eEvent.ERR_VCE_COMMON_Failed, _moduleName, $"illegal msg:{msg}, {ex.Message}"); } } private void ReadMapData(string msg) { string waferinfo = ""; string[] waferitems = msg.Split(','); //智能模式 可以识别叠片 for (int i = 3; i < waferitems.Length - 1; ++i) { //如果包含只需要逐个检查 if (waferitems[i].Contains('?')) { foreach (char j in waferitems[i]) { if (waferinfo.Length >= 25) break; else waferinfo += j; } } else waferinfo += waferitems[i]; } for (int i = 0; i < waferinfo.Length; ++i) { int slotnum = i; if (slotnum < 25) { switch (waferinfo[i]) { case '0': WaferManager.Instance.DeleteWafer(_LPMs[0].Module, slotnum); break; case 'X': WaferManager.Instance.CreateWafer(_LPMs[0].Module, slotnum, WaferStatus.Normal); break; case 'C': LOG.Write(eEvent.ERR_VCE_COMMON_Failed, _moduleName, $"Slot {i + 1}:double or dummy wafer."); WaferManager.Instance.CreateWafer(_LPMs[0].Module, slotnum, WaferStatus.Double); break; case '?': LOG.Write(eEvent.ERR_VCE_COMMON_Failed, _moduleName, $"Slot {i + 1}:Crossed wafer."); WaferManager.Instance.CreateWafer(_LPMs[0].Module, slotnum, WaferStatus.Crossed); break; } } } _LPMs[0].IsMapped = true; } private void Errorhandle(string msg, out string reason) { if (_matchErrorCode.IsMatch(msg)) { //若是匹配 //包含原因 string errorcode = _matchErrorCode.Match(msg).Value; if (_ErrorCode2Reason.ContainsKey(errorcode)) { if (errorcode == "L13") _IsDashWaferError = true; reason = _ErrorCode2Reason[errorcode]; } else { reason = "未找到相关Error Code"; } } else { //若不匹配 reason = "回复消息不符合标准格式"; } } private void onDataChange(string oneLineMessage) { lock (_locker) { if (string.IsNullOrEmpty(_newline))//没有CR { _lstAsciiMsgs.AddLast(oneLineMessage);//将消息添加到最后 return; } string[] array = oneLineMessage.Split(_newline.ToCharArray());//按照cr分开通讯数据 foreach (string text in array) { if (!string.IsNullOrEmpty(text)) { _lstAsciiMsgs.AddLast(text + _newline);//存进list中等待处理 } } } } public override bool ServerUp() { if (!CheckVceStatus()) return false; _currentVceMessage = new VceMessage { Head = VceMessageHead.Set, Command = VceCommand.ServerUp, Param = "ALL,ON" }; _currentMsg = _currentVceMessage.toSunWayString() + _newline; _status = RState.Running; return _serialport.Write(_currentMsg); } public override bool HomeALL() { _currentVceMessage = new VceMessage { Head = VceMessageHead.Action, Command = VceCommand.Home, Param = "ALL" }; _currentMsg = _currentVceMessage.toSunWayString() + _newline; _status = RState.Running; return _serialport.Write(_currentMsg); } public override bool Home(string axis) { _currentVceMessage = new VceMessage { Head = VceMessageHead.Action, Command = VceCommand.Home, Param = axis }; _currentMsg = _currentVceMessage.toSunWayString() + _newline; _status = RState.Running; return _serialport.Write(_currentMsg); } public override bool CheckStatus() { if (!CheckVceStatus()) return false; _currentVceMessage = new VceMessage { Head = VceMessageHead.Read, Command = VceCommand.CheckStatus }; _currentMsg = _currentVceMessage.toSunWayString() + _newline; _status = RState.Running; return _serialport.Write(_currentMsg); } public override bool CloseDoor() { if (!CheckVceStatus()) return false; _currentVceMessage = new VceMessage { Head = VceMessageHead.Action, Command = VceCommand.DoorClose }; _currentMsg = _currentVceMessage.toSunWayString() + _newline; _status = RState.Running; return _serialport.Write(_currentMsg); } /// /// 开门提示 /// 在honghuVCE中没有ATM信号的内部卡控 可能会导致开门的压差 /// 因此每一次都要增加判断,只要引用此处功能的,前面都需要有判断 /// /// /// public override bool OpenDoor() { //如果其他指令正在执行 且 //if (!CheckVceStatus()) // return false; if (_IsDashWaferError) _IsDashWaferError = false; _currentVceMessage = new VceMessage { Head = VceMessageHead.Action, Command = VceCommand.DoorOpen }; _currentMsg = _currentVceMessage.toSunWayString() + _newline; _status = RState.Running; return _serialport.Write(_currentMsg); } public override bool Load() { if (!CheckVceStatus()) return false; _currentVceMessage = new VceMessage { Head = VceMessageHead.Action, Command = VceCommand.Load }; _currentMsg = _currentVceMessage.toSunWayString() + _newline; _status = RState.Running; return _serialport.Write(_currentMsg); } public override bool UnLoad() { if (!CheckVceStatus()) return false; _currentVceMessage = new VceMessage { Head = VceMessageHead.Action, Command = VceCommand.UnLoad }; _currentMsg = _currentVceMessage.toSunWayString() + _newline; _status = RState.Running; return _serialport.Write(_currentMsg); } public override bool Map() { if (!CheckVceStatus()) return false; _currentVceMessage = new VceMessage { Head = VceMessageHead.Action, Command = VceCommand.Map }; _currentMsg = _currentVceMessage.toSunWayString() + _newline; _status = RState.Running; return _serialport.Write(_currentMsg); } public override bool ReadMap() { if (!CheckVceStatus()) return false; _currentVceMessage = new VceMessage { Head = VceMessageHead.Read, Command = VceCommand.ReadMap, Param = "S" }; _currentMsg = _currentVceMessage.toSunWayString() + _newline; _status = RState.Running; _HasReceiveMsg = false; return _serialport.Write(_currentMsg); } public override bool Goto(int Targetslot) { if (!CheckVceStatus()) return false; LOG.Write(eEvent.EV_VCE_COMMON_INFO, _moduleName, $"SlotNum:{Targetslot}"); _currentVceMessage = new VceMessage { Head = VceMessageHead.Action, Command = VceCommand.Goto, Param = (Targetslot + 1).ToString().PadLeft(2, '0') }; _currentMsg = _currentVceMessage.toSunWayString() + _newline; _status = RState.Running; return _serialport.Write(_currentMsg); } public override bool GotoLP() { if (!CheckVceStatus()) return false; _currentVceMessage = new VceMessage { Head = VceMessageHead.Action, Command = VceCommand.GotoLP }; _currentMsg = _currentVceMessage.toSunWayString() + _newline; _status = RState.Running; return _serialport.Write(_currentMsg); } public override bool ClearError() { _currentVceMessage = new VceMessage { Head = VceMessageHead.Set, Command = VceCommand.ClearError }; _currentMsg = _currentVceMessage.toSunWayString() + _newline; _status = RState.Running; return _serialport.Write(_currentMsg); } } }