using Aitex.Core.Common; using Aitex.Core.Common.DeviceData; using Aitex.Core.RT.DataCenter; using Aitex.Core.RT.Device; using Aitex.Core.RT.Event; 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; using MECF.Framework.Common.SubstrateTrackings; using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Robot; using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using System.Xml; using VirgoCommon; using VirgoRT.Devices; using VirgoRT.Devices.YASKAWA; using VirgoRT.HostWrapper; using VirgoRT.Modules; namespace VirgoRT.Device { class EfemCommunicationBase { protected readonly AsyncSocket _socket = new AsyncSocket(""); protected EfemCommunicationBase(string sIP) { string ip_address = sIP; if (SC.GetValue("System.IsSimulatorMode")) { ip_address = "127.0.0.1:13001"; } _socket.Connect(ip_address); _socket.OnDataChanged += Hardware_OnDataChanged; _socket.OnErrorHappened += Hardware_OnErrorHappened; //if (!_socket.IsConnected) //{ // EV.PostAlarmLog(ModuleName.EFEM.ToString(), "Cannot connect to EFEM"); //} } private void Hardware_OnDataChanged(string message) { Singleton.Instance.EFEM.PostMsg(EfemEntity.MSG.RecHwMsg, message); } private void Hardware_OnErrorHappened(ErrorEventArgs args) { } public virtual void SendTo(string str) { } public virtual void SendTo(IEfemMessage yas_msg) { } } class EfemBase : BaseDevice, IDevice { // Fields // protected volatile LinkedList _actions = new LinkedList(); protected EfemCommunicationBase _comm; protected IMessageHandler _msgHandler; public virtual ILoadport this[ModuleName mod] { get { throw new ApplicationException(); } } // Properties // public new ModuleName Module { get; set; } public OnlineFlag OnlineFlag { get; set; } public bool CommunicationConnected { get; protected set; } public DeviceState Status { get; set; } public bool HasActions { get { lock (_lockerAction) { return _actions.Any(x => x.Status == ActionStatus.Pending); } } } public EfemCommunicationBase Comm => _comm; public IMessageHandler MsgHandler => _msgHandler; protected object _lockerAction = new object(); public string GripStateBlade1 { get; set; } public string GripStateBlade2 { get; set; } protected EfemBase(XmlElement xmlNode = null) { } public bool Initialize() { return true; } public void Monitor() { throw new NotImplementedException(); } public void Terminate() { throw new NotImplementedException(); } public void Reset() { throw new NotImplementedException(); } // Methods // public void ExecuteAction() { lock (_lockerAction) { if (!_actions.Any()) { LOG.Write("No Action in the EFEM Queue"); return; } if (_actions.All(x => x.Status != ActionStatus.Pending)) { LOG.Write("NO pending Action in EFEM Queue"); //System.Diagnostics.Trace.Assert(false); return; } var nextAction = _actions.First(a => a.Status == ActionStatus.Pending); if (nextAction != null) { LOG.Write($"found pending action : efem action [{nextAction.GetType().Name}] [{nextAction.ID}] 开始执行"); nextAction.Execute(); } } } public void ClearActions() { lock (_lockerAction) { _actions?.Clear(); LOG.Write($"clear all efem action"); } } public void AddAction(ActionBase cmd) { lock (_lockerAction) { _actions.AddLast(cmd); LOG.Write($"efem action [{cmd.GetType().Name}] [{cmd.ID}] add to queue"); if (cmd.IsBackground) { LOG.Write($"background, efem action [{cmd.GetType().Name}] [{cmd.ID}] 开始执行"); cmd.Execute(); } if (cmd is LedAction) { Thread.Sleep(50); } } } public void UpdateStatus(ushort Id, ActionStatus st) { lock (_lockerAction) { var cur = _actions.First(x => x.ID == Id); if (null == cur) { LOG.Write($"NO {Id} action in the queue"); return; } cur.Status = st; if (st == ActionStatus.Completed) { cur.OnPostWork(); _actions.Remove(cur); LOG.Write($"efem action [{cur.GetType().Name}] [{cur.ID}] removed from queue"); } } } public virtual void ReceiveMessage(string sRec) { throw new NotImplementedException(); } public void SetOnline(bool online) { this.OnlineFlag = online ? OnlineFlag.Online : OnlineFlag.Offline; } public virtual void SetOnline(ModuleName mod, bool online) { } public virtual void SetBusy(ModuleName mod, bool online) { } } } namespace VirgoRT.Device.YASKAWA { sealed class MessageHandler : IMessageHandler { // Fields // private readonly IList _msgQueue = new List(); private readonly EfemBase _efem; // Properties // public bool IsCompleted { get { if (_msgQueue.Count <= 0) return false; return EfemMessage.MsgHead.INF == _msgQueue[_msgQueue.Count - 1].Head; } } public event EventHandler CommandUpdated; public event EventHandler EventUpdated; public event EventHandler ErrorOccurred; // Constructor // public MessageHandler(EfemBase device) { _efem = device; } public void Send(IEfemMessage msg) { _efem.Comm.SendTo(msg); _msgQueue.Add(msg as EfemMessage); } public void ReceiveMessage(string sRec) { string[] msgs = sRec.Split('\r'); foreach (var msg in msgs) { if (string.IsNullOrWhiteSpace(msg)) continue; EfemMessage rec_msg = msg.ToMessage(); switch (rec_msg.Head) { case EfemMessage.MsgHead.ACK: _msgQueue.Add(rec_msg); OnCommandUpdated(new EfemActionArgs { Module = rec_msg.Port, CommandType = rec_msg.Operation, Status = ActionStatus.RecACK }); break; case EfemMessage.MsgHead.INF: OnCommandUpdated(new EfemActionArgs { Module = rec_msg.Port, CommandType = rec_msg.Operation, Status = ActionStatus.RecINF }); // 收到INF之后发送ACK确认 string strACK = rec_msg.RawString.Replace("INF", "ACK"); _efem.Comm.SendTo(strACK); EfemMessage ack_msg = strACK.ToMessage(); ack_msg.Direct = MsgDirection.To; _msgQueue.Add(ack_msg); OnCommandUpdated(new EfemActionArgs { Module = rec_msg.Port, CommandType = rec_msg.Operation, Status = ActionStatus.Completed, Data = rec_msg.Data.Count > 0 ? rec_msg.Data.First() : string.Empty }); break; case EfemMessage.MsgHead.EVT: OnEventUpdated(new EfemEventArgs { EvtStr = rec_msg.ToParamString(), Module = rec_msg.Port, CommandType = rec_msg.Operation, DataList = rec_msg.Data }); break; case EfemMessage.MsgHead.NAK: OnErrorOccurred(new EfemErrorArgs { Factor = rec_msg.Factor, Description = Constant.FactorString.ContainsKey(rec_msg.Factor) ? Constant.FactorString[rec_msg.Factor] : rec_msg.Factor, }); break; case EfemMessage.MsgHead.CAN: OnErrorOccurred(new EfemErrorArgs { Factor = rec_msg.Factor, Description = Constant.FactorString.ContainsKey(rec_msg.Factor) ? Constant.FactorString[rec_msg.Factor] : rec_msg.Factor, Message = rec_msg.Data[0] }); break; case EfemMessage.MsgHead.ABS: OnErrorOccurred(new EfemErrorArgs { Factor = rec_msg.Factor, Description = $"{rec_msg.Data[0]}, {rec_msg.Data[1]}" }); break; } } } private void OnCommandUpdated(EfemActionArgs args) { CommandUpdated?.Invoke(this, args); } private void OnEventUpdated(EfemEventArgs args) { EventUpdated?.Invoke(this, args); } private void OnErrorOccurred(EfemErrorArgs args) { ErrorOccurred?.Invoke(this, args); } } sealed class EfemComm : EfemCommunicationBase { public EfemComm() : base(SC.GetStringValue("EFEM.IPAddress")) { } public override void SendTo(string str) { _socket.Write(str + '\r'); } public override void SendTo(IEfemMessage msg) { if (msg is EfemMessage yas_msg) { //string str = EfemParser.Instance.TranslateBack(yas_msg); string str = yas_msg.ToString(); if (string.IsNullOrEmpty(str)) throw new ApplicationException("Yaskawa message translation error"); yas_msg.RawString = str; SendTo(str); } } } /// /// EFEM object class /// sealed class Efem : EfemBase, IEfem { //---------------------------------Fields---------------------------------------- // private readonly JetPM[] _pm = new JetPM[2]; private readonly Loadport[] _LPMs = new Loadport[2]; private readonly SignalTower _signalT = new SignalTower(); //private readonly CoolingStage[] _aligner = new CoolingStage[2]; private string _robotMoveAction; private LidState _CassetteDoor; private LidState _SideDoor; private RD_TRIG _bSysVacPressure1 = new RD_TRIG(); //Bit[0] System vacuum source pressure 1 private RD_TRIG _bIonizerCompressedAir = new RD_TRIG(); //Bit[1] Ionizer compressed air private RD_TRIG _bSysCompressedAirPressure = new RD_TRIG(); //Bit[2] System compressed air pressure 1 private RD_TRIG _bFlowGaugeSensor = new RD_TRIG(); //Bit[4] Flow gauge sensor private RD_TRIG _bLeakageSensor = new RD_TRIG(); //Bit[5] Leakage sensor private RD_TRIG _bDiffPressureSensorSetting1 = new RD_TRIG(); //Bit[8] Differential pressure sensor setting 1 private RD_TRIG _bDiffPressureSensorSetting2 = new RD_TRIG(); //Bit[9] Differential pressure sensor setting 2 private RD_TRIG _bIonizerAlarm = new RD_TRIG(); //Bit[10] Ionizer alarm private RD_TRIG _bFFuAlarm = new RD_TRIG(); private RD_TRIG _bAreaSensor = new RD_TRIG(); private RD_TRIG _bModeSwitch = new RD_TRIG(); private RD_TRIG _bCassetteDoorTrig = new RD_TRIG(); private RD_TRIG _bSideDoorTrig = new RD_TRIG(); public override ILoadport this[ModuleName mod] { get { if (!ModuleHelper.IsLoadPort(mod)) throw new ApplicationException($"{mod} is NOT Loadport"); return _LPMs[mod - ModuleName.LP1]; } } //---------------------------------Properties------------------------------------ // public LidState CassetteDoor { get => _CassetteDoor; set { _CassetteDoor = value; _bCassetteDoorTrig.CLK = _CassetteDoor == LidState.Close; if (_bCassetteDoorTrig.T) { EV.Notify(CassetteDoorOpen); EV.PostWarningLog(Module.ToString(), "Cassette door opened"); } if (_bCassetteDoorTrig.R) { EV.Notify(CassetteDoorClose); EV.PostInfoLog(Module.ToString(), "Cassette door closed"); } } } public LidState DoorSwitch { get => _SideDoor; set { _SideDoor = value; _bSideDoorTrig.CLK = _SideDoor == LidState.Close; if (_bSideDoorTrig.T) { EV.PostAlarmLog(Module.ToString(), "EFEM Side door open"); } if (_bSideDoorTrig.R) { EV.PostInfoLog(Module.ToString(), "EFEM Side door close"); } } } private string CassetteDoorOpen = "CassetteDoorOpen"; private string CassetteDoorClose = "CassetteDoorClose"; // Constructor // public Efem() { Module = ModuleName.EfemRobot; _pm[0] = DEVICE.GetDevice(ModuleName.PMA.ToString()); _pm[1] = DEVICE.GetDevice(ModuleName.PMB.ToString()); _comm = new EfemComm(); _LPMs[0] = new Loadport(ModuleName.LP1, this); _LPMs[1] = new Loadport(ModuleName.LP2, this); _msgHandler = new MessageHandler(this); _msgHandler.CommandUpdated += MsgOnCommandUpdated; _msgHandler.EventUpdated += MsgOnEventUpdated; _msgHandler.ErrorOccurred += MsgOnErrorOccurred; CarrierManager.Instance.SubscribeLocation(ModuleName.LP1.ToString(), 1); CarrierManager.Instance.SubscribeLocation(ModuleName.LP2.ToString(), 1); WaferManager.Instance.SubscribeLocation(ModuleName.EfemRobot, 2); WaferManager.Instance.SubscribeLocation(ModuleName.Aligner1, 1); WaferManager.Instance.SubscribeLocation(ModuleName.Aligner2, 1); WaferManager.Instance.SubscribeLocation(ModuleName.Cooling1, 1); WaferManager.Instance.SubscribeLocation(ModuleName.Cooling2, 1); WaferManager.Instance.SubscribeLocation(ModuleName.LP1, 25); WaferManager.Instance.SubscribeLocation(ModuleName.LP2, 25); DATA.Subscribe("EfemRobot.RobotMoveAction", () => _robotMoveAction); DATA.Subscribe("LP1.WaferSize", () => _LPMs[0].WaferSize.ToString()); DATA.Subscribe("LP1.CassetteState", () => _LPMs[0].HasCassette ? LoadportCassetteState.Normal : LoadportCassetteState.Absent); DATA.Subscribe("LP1.CassettePresent", () => _LPMs[0].HasCassette ? 1 : 0); DATA.Subscribe("LP1.SlotMap", () => _LPMs[0].SlotMap); DATA.Subscribe("LP1.IsWaferProtrude", () => _LPMs[0].Protrusion ? 1 : 0); DATA.Subscribe("LP1.JobDone", () => { return _LPMs[0].JobDone; }); DATA.Subscribe("LP1.NotifyJobDone", () => { if (!_LPMs[0].JobDone || !_LPMs[0].HasCassette) return false; if (SC.GetValue("System.Job.BuzzerTimeWhenJobDone") >= 0 && _LPMs[0].TimerNotifyJobDone.ElapsedMilliseconds > SC.GetValue("System.Job.BuzzerTimeWhenJobDone") * 1000) return false; return _LPMs[0].JobDone; }); DATA.Subscribe("LP2.WaferSize", () => _LPMs[1].WaferSize.ToString()); DATA.Subscribe("LP2.CassetteState", () => _LPMs[1].HasCassette ? LoadportCassetteState.Normal : LoadportCassetteState.Absent); DATA.Subscribe("LP2.CassettePresent", () => _LPMs[1].HasCassette ? 1 : 0); DATA.Subscribe("LP2.SlotMap", () => _LPMs[1].SlotMap); DATA.Subscribe("LP2.IsWaferProtrude", () => _LPMs[1].Protrusion ? 1 : 0); DATA.Subscribe("LP2.JobDone", () => { return _LPMs[1].JobDone; }); DATA.Subscribe("LP2.NotifyJobDone", () => { if (!_LPMs[1].JobDone || !_LPMs[1].HasCassette) return false; if (SC.GetValue("System.Job.BuzzerTimeWhenJobDone") >= 0 && _LPMs[1].TimerNotifyJobDone.ElapsedMilliseconds > SC.GetValue("System.Job.BuzzerTimeWhenJobDone") * 1000) return false; return _LPMs[1].JobDone; }); DATA.Subscribe("Aligner1.WaferSize", () => WaferManager.Instance.GetWafer(ModuleName.Aligner1, 0).Size.ToString()); DATA.Subscribe("Aligner2.WaferSize", () => WaferManager.Instance.GetWafer(ModuleName.Aligner2, 0).Size.ToString()); DATA.Subscribe("Cooling1.WaferSize", () => WaferManager.Instance.GetWafer(ModuleName.Cooling1, 0).Size.ToString()); DATA.Subscribe("Cooling2.WaferSize", () => WaferManager.Instance.GetWafer(ModuleName.Cooling2, 0).Size.ToString()); DATA.Subscribe("EfemRobot.WaferSize", () => WaferManager.Instance.GetWafer(ModuleName.EfemRobot, 0).Size.ToString()); DATA.Subscribe("EFEM.CassetteDoor", () => CassetteDoor); DATA.Subscribe("EfemRobot.GripStateBlade1", () => GripStateBlade1); DATA.Subscribe("EfemRobot.GripStateBlade2", () => GripStateBlade2); GripStateBlade1 = "Unknown"; GripStateBlade2 = "Unknown"; _bCassetteDoorTrig.CLK = true; _bSideDoorTrig.CLK = true; EV.Subscribe(new EventItem("Event", CassetteDoorOpen, "Cassette Door Open")); EV.Subscribe(new EventItem("Event", CassetteDoorClose, "Cassette Door Close")); } // Methods // public bool HomeAll() { AddAction(new HomeAllAction(this, ModuleName.EFEM)); AddAction(new OrgshAction(this, ModuleName.EFEM)); return true; } public bool Home(ModuleName mod) { AddAction(new HomeModuleAction(this, mod)); return true; } public bool ClearError() { AddAction(new ClearErrorAction(this)); return true; } public void AbortRobot() { AddAction(new AbortAction(this)); } public bool Pick(MoveParam mp) { if (!WaferManager.Instance.CheckWafer(mp.SrcModule, mp.SrcSlot, WaferStatus.Normal)) { EV.PostAlarmLog("System", $"{mp.SrcModule} slot {mp.SrcSlot + 1} wafer is not normal"); return false; } if (ModuleHelper.IsPm(mp.SrcModule)) { JetPM pM = GetPM(mp.SrcModule); if (pM.IsSlitDoorClosed) { EV.PostAlarmLog(pM.Module.ToString(), "Slit 门必须打开"); return false; } AddAction(new PinAction(mp.SrcModule, pM, MovementPosition.Up, false, mp.Arm, true)); AddAction(new ExtendAction(this, new ExtendParam { Module = mp.SrcModule, Arm = mp.Arm, Pos = ExtendPos.GB })); _robotMoveAction = string.Format($"{mp.SrcModule}.Picking"); AddAction(new PinAction(mp.SrcModule, pM, MovementPosition.Down, true, mp.Arm, true)); AddAction(new ExtendAction(this, new ExtendParam { Module = mp.SrcModule, Arm = mp.Arm, Pos = ExtendPos.G4 })); } else { _robotMoveAction = string.Format($"{mp.SrcModule}.Picking"); AddAction(new PickAction(this, mp)); } return true; } public bool Place(MoveParam mp) { if (ModuleHelper.IsPm(mp.DestModule)) { JetPM pM = GetPM(mp.DestModule); if (pM.IsSlitDoorClosed) { EV.PostAlarmLog(pM.Module.ToString(), "Slit 门必须打开"); return false; } AddAction(new ExtendAction(this, new ExtendParam { Module = mp.DestModule, Arm = mp.Arm, Pos = ExtendPos.PB })); _robotMoveAction = string.Format($"{mp.DestModule}.Placing"); AddAction(new PinAction(mp.DestModule, pM, MovementPosition.Up, true, mp.Arm, false)); AddAction(new ExtendAction(this, new ExtendParam { Module = mp.DestModule, Arm = mp.Arm, Pos = ExtendPos.P4 })); AddAction(new PinAction(mp.DestModule, pM, MovementPosition.Down, false, mp.Arm, false)); } else { _robotMoveAction = string.Format($"{mp.DestModule}.Placing"); AddAction(new PlaceAction(this, mp)); } return true; } public void PickAndPlace(MoveParam pickParam, MoveParam placeParam) { if (!WaferManager.Instance.CheckWafer(pickParam.SrcModule, pickParam.SrcSlot, WaferStatus.Normal)) { EV.PostAlarmLog("System", $"{pickParam.SrcModule} slot {pickParam.SrcSlot + 1} wafer is not normal"); return; } if (ModuleHelper.IsPm(pickParam.SrcModule)) { JetPM pM = GetPM(pickParam.SrcModule); if (pM.IsSlitDoorClosed) { EV.PostAlarmLog(pM.Module.ToString(), "Slit 门必须打开"); return; } AddAction(new PinAction(pickParam.SrcModule, pM, MovementPosition.Up, false, pickParam.Arm, true)); AddAction(new ExtendAction(this, new ExtendParam { Module = pickParam.SrcModule, Arm = pickParam.Arm, Pos = ExtendPos.GB })); _robotMoveAction = string.Format($"{pickParam.SrcModule}.Picking"); AddAction(new PinAction(pickParam.SrcModule, pM, MovementPosition.Down, true, pickParam.Arm, true)); AddAction(new ExtendAction(this, new ExtendParam { Module = pickParam.SrcModule, Arm = pickParam.Arm, Pos = ExtendPos.G4 })); AddAction(new ExtendAction(this, new ExtendParam { Module = placeParam.DestModule, Arm = placeParam.Arm, Pos = ExtendPos.PB })); _robotMoveAction = string.Format($"{placeParam.DestModule}.Placing"); AddAction(new PinAction(placeParam.DestModule, pM, MovementPosition.Up, true, placeParam.Arm,false)); AddAction(new ExtendAction(this, new ExtendParam { Module = placeParam.DestModule, Arm = placeParam.Arm, Pos = ExtendPos.P4 })); AddAction(new PinAction(placeParam.DestModule, pM, MovementPosition.Down, false, placeParam.Arm, false)); } else { _robotMoveAction = string.Format($"{pickParam.SrcModule}.Picking"); AddAction(new PickAction(this, pickParam)); _robotMoveAction = string.Format($"{placeParam.DestModule}.Placing"); AddAction(new PlaceAction(this, placeParam)); } } public bool Extend(ExtendParam ep) { AddAction(new ExtendAction(this, ep)); return true; } public void Goto(MoveParam ep) { //_robotMoveAction = string.Format($"{ep.SrcModule}.Goto"); AddAction(new GotoAction(this, ep)); } public bool Retract(ExtendParam ep) { AddAction(new ExtendAction(this, ep)); return true; } public bool Map(ModuleName mod) { if (_LPMs[mod - ModuleName.LP1].Protrusion) { EV.PostAlarmLog(mod.ToString(), $"{mod} wafer protrusion, can not do Map"); return false; } _LPMs[mod - ModuleName.LP1].Map(); return true; } public bool Grip(Hand blade, bool isGrip) { AddAction(new GripAction(this, blade, isGrip)); return true; } public bool SetPinUp(ModuleName mod) { AddAction(new LiftAction(this, mod)); return true; } public bool Align(ModuleName mod, float delayTime, WaferSize size) { AddAction(new AlignAction(this, mod, size)); return true; } public bool SetLamp(LightType light, LightStatus status) { AddAction(new LedAction(this, light, status)); return true; } public void TurnOffBuzzer() { AddAction(new LedAction(this, LightType.BUZZER1, LightStatus.OFF)); } //public void SwitchOnBuzzerAndRed() //{ // AddAction(new LedAction(this, LightType.RED, LightStatus.ON)); // AddAction(new LedAction(this, LightType.YELLOW, LightStatus.OFF)); // AddAction(new LedAction(this, LightType.GREEN, LightStatus.OFF)); // AddAction(new LedAction(this, LightType.BUZZER1, LightStatus.ON)); //} public override void ReceiveMessage(string sRec) { _msgHandler.ReceiveMessage(sRec); } public override void SetOnline(ModuleName mod, bool online) { this[mod].SetOnline(online); } public override void SetBusy(ModuleName mod, bool busy) { this[mod].Status = DeviceState.Busy; } //--------------------------------Constructor------------------------------------ // private JetPM GetPM(ModuleName mod) { if (!ModuleHelper.IsPm(mod)) throw new ArgumentException("Module argument error"); return _pm[mod - ModuleName.PMA]; } //----------------------------------Private Method------------------------------- // private void MsgOnEventUpdated(object sender, EventArgs e) { if (!(e is EfemEventArgs)) return; EfemEventArgs eArg = e as EfemEventArgs; switch (eArg.CommandType) { case EfemOperation.SigStatus: // EVT:SIGSTAT/Parameter/DATA1/DATA2; string sParam = eArg.DataList[0]; // "SYSTEM" or "Pn" // DATA1 & DATA2 int nData1 = Convert.ToInt32(eArg.DataList[1], 16); int nData2 = Convert.ToInt32(eArg.DataList[2], 16); BitArray baData1 = new BitArray(new int[] { nData1 }); BitArray baData2 = new BitArray(new int[] { nData2 }); if (0 == string.Compare(sParam, Constant.SYS, true)) { // EVT:SIGSTAT/System/00000000/00000004; // DATA1 _bSysVacPressure1.CLK = baData1[0]; // bit 0 _bIonizerCompressedAir.CLK = baData1[1]; // bit 1 _bSysCompressedAirPressure.CLK = baData1[2]; // bit 2 _bFlowGaugeSensor.CLK = baData1[4]; // bit 4 _bLeakageSensor.CLK = baData1[5]; // bit 5 this.DoorSwitch = baData1[6] ? LidState.Close : LidState.Open; // bit 6 //bDrivePower = baData1[7]; // bit 7 _bDiffPressureSensorSetting1.CLK = baData1[8]; // bit 8 _bDiffPressureSensorSetting2.CLK = baData1[9]; // bit 9 _bIonizerAlarm.CLK = baData1[10]; // bit 10 _bFFuAlarm.CLK = baData1[11]; // bit 11 _bAreaSensor.CLK = baData1[12]; // bit 12 _bModeSwitch.CLK = baData1[13]; // bit 13 this.CassetteDoor = baData1[15] ? LidState.Close : LidState.Open; // bit 15 // Post warning and alarm if (!baData1[0]) // Bit[0] ON=Normal, OFF=Abnormal { EV.PostAlarmLog(Module.ToString(), "EFEM System vacuum source pressure low"); Singleton.Instance.EFEM.PostMsg(EfemEntity.MSG.Error); } if (!baData1[1]) // Bit[1] ON=Normal, OFF=Abnormal { EV.PostAlarmLog(Module.ToString(), "EFEM Ionizer compressed air error"); Singleton.Instance.EFEM.PostMsg(EfemEntity.MSG.Error); } if (!baData1[2]) // Bit[2] ON=Normal, OFF=Abnormal { EV.PostAlarmLog(Module.ToString(), "EFEM System compressed air pressure low"); Singleton.Instance.EFEM.PostMsg(EfemEntity.MSG.Error); } if (!baData1[4]) // Bit[4] ON=Normal, OFF=Abnormal { EV.PostAlarmLog(Module.ToString(), "EFEM Flow gauge sensor error"); Singleton.Instance.EFEM.PostMsg(EfemEntity.MSG.Error); } if (!baData1[5]) // Bit[5] ON=Normal, OFF=Abnormal { EV.PostAlarmLog(Module.ToString(), "EFEM Leakage alarm"); Singleton.Instance.EFEM.PostMsg(EfemEntity.MSG.Error); } if (!baData1[10]) // Bit[10] ON=Normal, OFF=Abnormal { EV.PostAlarmLog(Module.ToString(), "EFEM Ionizer alarm"); Singleton.Instance.EFEM.PostMsg(EfemEntity.MSG.Error); } if (!baData1[11]) // Bit[11] ON=Normal, OFF=Abnormal { EV.PostAlarmLog(Module.ToString(), "FFU alarm"); Singleton.Instance.EFEM.PostMsg(EfemEntity.MSG.Error); } if (!baData1[13]) // Bit[13] ON=RUN, OFF=Maintain { EV.PostAlarmLog(Module.ToString(), "EFEM switch to Maintain mode, HomeAll to recover"); Singleton.Instance.EFEM.PostMsg(EfemEntity.MSG.ToInit); } // DATA2 _signalT.ChangeLightStatus(LightType.RED, baData2[0] ? LightStatus.ON : baData2[5] ? LightStatus.BLINK : LightStatus.OFF); _signalT.ChangeLightStatus(LightType.GREEN, baData2[1] ? LightStatus.ON : baData2[6] ? LightStatus.BLINK : LightStatus.OFF); _signalT.ChangeLightStatus(LightType.YELLOW, baData2[2] ? LightStatus.ON : baData2[7] ? LightStatus.BLINK : LightStatus.OFF); _signalT.ChangeLightStatus(LightType.BLUE, baData2[3] ? LightStatus.ON : baData2[8] ? LightStatus.BLINK : LightStatus.OFF); _signalT.ChangeLightStatus(LightType.WHITE, baData2[4] ? LightStatus.ON : baData2[9] ? LightStatus.BLINK : LightStatus.OFF); _signalT.ChangeLightStatus(LightType.BUZZER1, baData2[10] ? LightStatus.ON : LightStatus.OFF); /* EFEM 程序中目前没有实现 _RobotErr.CLK = baData2[27]; // bit 27 bool bArmNotExtendLLA = baData2[30]; // bit 30 bool bArmNotExtendLLB = baData2[31]; // bit 31 */ } // system event else { _LPMs[eArg.Module - ModuleName.LP1].HandleEvent(eArg); } // FOUP EVENT break; case EfemOperation.GetWaferInfo: _LPMs[eArg.Module - ModuleName.LP1].HandleEvent(eArg); break; default: break; } } private void MsgOnCommandUpdated(object sender, EventArgs e) { EfemActionArgs arg = e as EfemActionArgs; if (arg == null) throw new ArgumentNullException("Argument is Null"); if (arg.CommandType == EfemOperation.Ready) { this.CommunicationConnected = true; Singleton.Instance.EFEM.PostMsg(EfemEntity.MSG.CommReady); return; } EfemActionBase action = null; lock (_lockerAction) { foreach (var item in _actions) { if (item is EfemActionBase a1) { if (a1.Type == arg.CommandType && item.Status != ActionStatus.Completed) { action = a1; break; } } } if (action == null) { //EV.PostAlarmLog(arg.Module.ToString(), $"NO activated {arg.CommandType} in the queue"); LOG.Write($"NO activated [{arg.ID}] [{arg.CommandType}] in the queue"); return; } // 更新 action 状态 action.Status = arg.Status; if (arg.Status == ActionStatus.Completed) { ModuleName mod = action.Module; // Map 命令比较特别, module 是LPX但是用robot做mapping if (mod == ModuleName.EFEM || mod == ModuleName.EfemRobot || ModuleHelper.IsAligner(mod) || ModuleHelper.IsCooling(mod) || arg.CommandType == EfemOperation.Map) { if (action.Type != EfemOperation.Light) { Singleton.Instance.EFEM.PostMsg(EfemEntity.MSG.ActionDone, arg.CommandType); } } else if (ModuleHelper.IsLoadPort(mod)) { Singleton.Instance.EFEM.NotifyLP(mod, LoadportEntity.MSG.ActionDone); } _robotMoveAction = string.Format($"System.None"); action.OnPostWork(arg.Data); LOG.Write($"efem action [{action.GetType().Name}] [{action.ID}][{action.Type}] removed from queue"); _actions.Remove(action); } } } private void MsgOnErrorOccurred(object sender, EventArgs e) { if (e is EfemErrorArgs errArg) { this.Status = DeviceState.Error; lock (_lockerAction) { if (this._actions.Count > 0) { LOG.Write($"error occur, remove {_actions.Last.Value.ID} from queue"); this._actions.RemoveLast(); } } EV.PostAlarmLog(Module.ToString(), $"{errArg.Description}, [{errArg.Message}], [{errArg.Factor}]"); } } } /// /// Load port object class /// sealed class Loadport : ILoadport { private readonly EfemBase _efem; //---------------------------------Properties------------------------------------ // public OnlineFlag OnlineFlag { get; set; } public ModuleName Module { get; set; } public DeviceState Status { get; set; } public WaferSize WaferSize { get; set; } public WaferStatus[] WaferInfo { get; set; } public bool HasCassette { get; set; } public bool Protrusion { get; set; } public bool IsMapped { get; set; } public bool JobDone { get; set; } public Stopwatch TimerNotifyJobDone { get; set; } private string[] _slotMap = new string[25]; public string SlotMap { get { for (int i = 0; i < 25; i++) { _slotMap[i] = ((int)WaferManager.Instance.GetWafer(Module, i).Status).ToString(); } return string.Join("", _slotMap); } } private string Port1CassetteArrive = "Port1CassetteArrive"; private string Port1CassetteRemoved = "Port1CassetteRemoved"; private string Port1MappingComplete = "Port1MappingComplete"; private string Port1MappingFailed = "Port1MappingFailed"; private string Port2CassetteArrive = "Port2CassetteArrive"; private string Port2CassetteRemoved = "Port2CassetteRemoved"; private string Port2MappingComplete = "Port2MappingComplete"; private string Port2MappingFailed = "Port2MappingFailed"; // Constructor // public Loadport(ModuleName mod, EfemBase efem) { Module = mod; _efem = efem; TimerNotifyJobDone = new Stopwatch(); EV.Subscribe(new EventItem("Event", Port1CassetteArrive, "Port1CassetteArrive")); EV.Subscribe(new EventItem("Event", Port1CassetteRemoved, "Port1CassetteRemoved")); EV.Subscribe(new EventItem("Event", Port1MappingComplete, "Port1MappingComplete")); EV.Subscribe(new EventItem("Event", Port1MappingFailed, "Port1MappingFailed")); EV.Subscribe(new EventItem("Event", Port2CassetteArrive, "Port2CassetteArrive")); EV.Subscribe(new EventItem("Event", Port2CassetteRemoved, "Port2CassetteRemoved")); EV.Subscribe(new EventItem("Event", Port2MappingComplete, "Port2MappingComplete")); EV.Subscribe(new EventItem("Event", Port2MappingFailed, "Port2MappingFailed")); } // Methods // public void Home() { _efem.AddAction(new HomeAllAction(_efem, Module)); } public void NoteJobStart() { JobDone = false; } public void NoteJobComplete() { TimerNotifyJobDone.Restart(); JobDone = true; } public void Map() { _efem.AddAction(new MapAction(_efem, Module)); } public void HandleEvent(EfemEventArgs eArg) { switch (eArg.CommandType) { case EfemOperation.GetWaferInfo: string sWaferInfo = eArg.DataList[0]; bool resultNormal = true; for (byte index = 0; index < sWaferInfo.Length; index++) { int waferState = int.Parse(sWaferInfo.Substring(index, 1)); //合理的映射到内部支持的叠片/交叉片 if (waferState >= 7) waferState = 7; else if (waferState >= 2) waferState = 3; WaferStatus st = (WaferStatus)waferState; WaferManager.Instance.UpdateWaferSize(this.Module, index, WaferSize); if (st != WaferStatus.Empty) { WaferManager.Instance.CreateWafer(this.Module, index, st); if (st == WaferStatus.Normal) { EV.PostInfoLog(this.Module.ToString(), $"Found Wafer on Slot {index + 1} {WaferSize}"); } else { resultNormal = false; EV.PostWarningLog(this.Module.ToString(), $"Found {st} Wafer on Slot {index + 1} {WaferSize}"); } } } var dvidMap = new SerializableDictionary(); dvidMap[DVIDName.SlotMap] = sWaferInfo; dvidMap[DVIDName.PortID] = Module == ModuleName.LP1 ? "1" : "2"; if (resultNormal) { EV.Notify(Module == ModuleName.LP1 ? Port1MappingComplete : Port2MappingComplete, dvidMap); } else { EV.Notify(Module == ModuleName.LP1 ? Port1MappingFailed : Port2MappingFailed, dvidMap); } this.IsMapped = true; break; case EfemOperation.SigStatus: // EVT: SIGSTAT/P2/00000381/00000000; string sParam = eArg.DataList[0]; ModuleName mod = sParam.ToModule(); if (!ModuleHelper.IsLoadPort(mod)) return; // DATA1 & DATA2 int nData1 = Convert.ToInt32(eArg.DataList[1], 16); int nData2 = Convert.ToInt32(eArg.DataList[2], 16); BitArray baData1 = new BitArray(new int[] { nData1 }); BitArray baData2 = new BitArray(new int[] { nData2 }); // wafer size this.WaferSize = !baData1[6] ? WaferSize.WS3 : !baData1[7] ? WaferSize.WS4 : !baData1[8] ? WaferSize.WS6 : WaferSize.WS0; // placement & present bool bPlacement = baData1[0]; // bit 0 bool bPresence = !baData1[1]; // bit 1 bool bArrived = bPlacement && bPresence; if (HasCassette) { if (!bArrived) { this.HasCassette = false; this.IsMapped = false; EV.PostInfoLog(mod.ToString(), "Cassette removed"); OP.DoOperation("System.CassetteLeave"); //For unload light control off afer job done CarrierManager.Instance.DeleteCarrier(Module.ToString()); WaferManager.Instance.DeleteWafer(this.Module, 0, 25); SerializableDictionary dvid = new SerializableDictionary(); dvid["PortID"] = mod == ModuleName.LP1 ? "1" : "2"; EV.Notify(mod == ModuleName.LP1 ? Port1CassetteRemoved : Port2CassetteRemoved, dvid); JobDone = false; } } else { if (bArrived) { this.HasCassette = true; CarrierManager.Instance.CreateCarrier(Module.ToString()); EV.PostInfoLog(mod.ToString(), $"Cassette {WaferSize} arrived"); var dvid1 = new SerializableDictionary(); dvid1["PortID"] = mod == ModuleName.LP1 ? "1" : "2"; EV.Notify(mod == ModuleName.LP1 ? Port1CassetteArrive : Port2CassetteArrive, dvid1); JobDone = false; } } this.Protrusion = !baData1[9]; if (Protrusion) { EV.PostAlarmLog(Module.ToString(), "发现 wafer 突出"); Singleton.Instance.EFEM.PostMsg(EfemEntity.MSG.Error); } // DATA2, loadport上面的LED 灯, 暂时不需要用到 break; default: break; } } public void SetOnline(bool online) { OnlineFlag = online ? OnlineFlag.Online : OnlineFlag.Offline; } } sealed class SignalTower { // Fields // private readonly Dictionary _lights = new Dictionary(5); public SignalTower() { _lights.Add(LightType.RED, LightStatus.OFF); _lights.Add(LightType.YELLOW, LightStatus.OFF); _lights.Add(LightType.BLUE, LightStatus.OFF); _lights.Add(LightType.GREEN, LightStatus.OFF); _lights.Add(LightType.WHITE, LightStatus.OFF); _lights.Add(LightType.BUZZER1, LightStatus.OFF); DATA.Subscribe($"{ModuleName.EFEM}.SignalTower", () => { return new AITSignalTowerData { IsGreenLightOn = _lights[LightType.GREEN] == LightStatus.ON, IsRedLightOn = _lights[LightType.RED] == LightStatus.ON, IsYellowLightOn = _lights[LightType.YELLOW] == LightStatus.ON, IsWhiteLightOn = _lights[LightType.WHITE] == LightStatus.ON, IsBlueLightOn = _lights[LightType.BLUE] == LightStatus.ON, IsBuzzerOn = _lights[LightType.BUZZER1] == LightStatus.ON, }; }); } // Methods // public void ChangeLightStatus(LightType light, LightStatus st) { if (!_lights.ContainsKey(light)) throw new ApplicationException($"NO {light} configured"); _lights[light] = st; } } }