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.SCCore;
using Aitex.Core.Util;
using Aitex.Sorter.Common;
using MECF.Framework.Common.Equipment;
using MECF.Framework.Common.CommonData;
using MECF.Framework.Common.SubstrateTrackings;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using VirgoCommon;
using VirgoRT.Device;
using VirgoRT.Device.YASKAWA;
using VirgoRT.Devices.YASKAWA;
using VirgoRT.Modules;
using VirgoRT.Modules.LPs;
namespace VirgoRT.Devices.EFEM
{
    /// 
    /// EFEM object class
    /// 
    sealed class Efem : EfemBase, VirgoRT.Device.IEfem
    {
        //---------------------------------Fields----------------------------------------
        // 
        private readonly JetPM[] _pm = new JetPM[2];
        //private readonly Loadport[] _LPMs = new Loadport[2];
        private readonly Dictionary _LPMs = new Dictionary();
        private readonly SignalTower _signalT = new SignalTower();
        //private readonly CoolingStage[] _aligner = new CoolingStage[2];
        //private string _robotMoveAction;
        private RobotMoveInfo _robotMoveInfo = new RobotMoveInfo();
        private LidState _CassetteDoor;
        private LidState _SideDoor;
        private F_TRIG _bSysVacPressure1 = new F_TRIG();
        private F_TRIG _bSysCompressedAirPressure = new F_TRIG();
        private R_TRIG _bFlowGaugeSensor = new R_TRIG();
        private R_TRIG _bLeakageSensor = new R_TRIG();
        private F_TRIG _bDiffPressureSensorSetting1 = new F_TRIG();
        private F_TRIG _bDiffPressureSensorSetting2 = new F_TRIG();
        private F_TRIG _bIonizerAlarm = new F_TRIG();
        private F_TRIG _bFFuAlarm = new F_TRIG();
        private F_TRIG _bAreaSensor = new F_TRIG();
        private F_TRIG _bModeSwitch = new F_TRIG();
        private RD_TRIG _bCassetteDoorTrig = new RD_TRIG();
        private RD_TRIG _bSideDoorTrig = new RD_TRIG();
        public Dictionary IsBufferPinUp = new Dictionary();
        private Stopwatch _queryLpStateTimer = new Stopwatch();
        public override ILoadport this[ModuleName mod]
        {
            get
            {
                if (!ModuleHelper.IsLoadPort(mod))
                    throw new ApplicationException($"{mod} is NOT Loadport");
                //return _LPMs[mod - ModuleName.LP1];
                return _LPMs[mod];
            }
        }
        //---------------------------------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.Notify(EFEMSideDoorOpen);
                    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";
        private string EFEMSideDoorOpen = "EFEMSideDoorOpen";
        private string EFEMVacuumPressureError = "EFEMVacuumPressureError";
        private string EFEMCDAError = "EFEMCDAError";
        private string EFEMFlowGaugeSensorError = "EFEMFlowGaugeSensorError";
        private string EFEMLeakageAlarm = "EFEMLeakageAlarm";
        private string EFEMIonizerAlarm = "EFEMIonizerAlarm";
        private string EFEMFFUAlarm = "EFEMFFUAlarm";
        private string EFEMOffline = "EFEMOffline";
        private string EFEMCommunicationError = "EFEMCommunicationError";
        private string WaferTransferFailed = "WaferTransferFailed";
        private string EFEMError = "EFEMError";
        // Constructor
        //
        public Efem()
        {
            Module = ModuleName.EfemRobot;
            _pm[0] = DEVICE.GetDevice(ModuleName.PMA.ToString());
            _pm[1] = DEVICE.GetDevice(ModuleName.PMB.ToString());
            _comm = new EfemComm();
            _LPMs[ModuleName.LP1] = new Loadport(ModuleName.LP1, this);
            _LPMs[ModuleName.LP2] = new Loadport(ModuleName.LP2, this);
            _LPMs[ModuleName.Buffer] = new Loadport(ModuleName.Buffer, this);
            IsBufferPinUp[ModuleName.Aligner1] = false;
            IsBufferPinUp[ModuleName.Aligner2] = false;
            IsBufferPinUp[ModuleName.Cooling1] = false;
            IsBufferPinUp[ModuleName.Cooling2] = false;
            if ((EfemEntity.EfemType)SC.GetValue($"EFEM.EfemType") == EfemEntity.EfemType.BrooksEFEM)
            {
                BrooksProxy = new BrooksEFEMProxy();
                BrooksProxy.CommandUpdated += MsgOnCommandUpdated;
                BrooksProxy.EventUpdated += MsgOnEventUpdated;
                BrooksProxy.ErrorOccurred += MsgOnErrorOccurred;
            }
            _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);
            Action _subscribeLoc = (ModuleName module, int waferCount) => {
                if (ModuleHelper.IsInstalled(module))
                {
                    WaferManager.Instance.SubscribeLocation(module, waferCount);
                }
            };
            _subscribeLoc(ModuleName.EfemRobot, 2);
            _subscribeLoc(ModuleName.Aligner1, 1);
            _subscribeLoc(ModuleName.Aligner2, 1);
            _subscribeLoc(ModuleName.Cooling1, 1);
            _subscribeLoc(ModuleName.Cooling2, 1);
            _subscribeLoc(ModuleName.LP1, SC.GetValue("EFEM.LoadPort.SlotNumber"));
            _subscribeLoc(ModuleName.LP2, SC.GetValue("EFEM.LoadPort.SlotNumber"));
            _subscribeLoc(ModuleName.Flipper, 1);
            _subscribeLoc(ModuleName.Buffer, SC.GetValue("EFEM.Buffer.SlotNumber"));
            DATA.Subscribe("EfemRobot.RobotMoveAction", () => _robotMoveInfo);
            DATA.Subscribe("LP1.JobDone", () =>
            {
                return _LPMs[ModuleName.LP1].JobDone;
            });
            DATA.Subscribe("LP1.NotifyJobDone", () =>
            {
                if (!_LPMs[ModuleName.LP1].JobDone || !_LPMs[ModuleName.LP1].HasCassette)
                    return false;
                if (SC.GetValue("System.Job.BuzzerTimeWhenJobDone") >= 0
                    && _LPMs[ModuleName.LP1].TimerNotifyJobDone.ElapsedMilliseconds > SC.GetValue("System.Job.BuzzerTimeWhenJobDone") * 1000)
                    return false;
                return _LPMs[ModuleName.LP1].JobDone;
            });
            DATA.Subscribe("LP2.JobDone", () =>
            {
                return _LPMs[ModuleName.LP2].JobDone;
            });
            DATA.Subscribe("LP2.NotifyJobDone", () =>
            {
                if (!_LPMs[ModuleName.LP2].JobDone || !_LPMs[ModuleName.LP2].HasCassette)
                    return false;
                if (SC.GetValue("System.Job.BuzzerTimeWhenJobDone") >= 0
                    && _LPMs[ModuleName.LP2].TimerNotifyJobDone.ElapsedMilliseconds > SC.GetValue("System.Job.BuzzerTimeWhenJobDone") * 1000)
                    return false;
                return _LPMs[ModuleName.LP2].JobDone;
            });
            Func _waferSize = (ModuleName module, int slot) => {
                string sSize = WaferSize.WS12.ToString();
                if (ModuleHelper.IsInstalled(module))
                {
                    sSize = WaferManager.Instance.GetWafer(module, slot).Size.ToString();
                }
                else
                {
                    sSize = WaferSize.WS12.ToString();
                }
                return sSize;
            };
            DATA.Subscribe("Aligner1.WaferSize", () => _waferSize(ModuleName.Aligner1, 0));
            DATA.Subscribe("Aligner2.WaferSize", () => _waferSize(ModuleName.Aligner2, 0));
            DATA.Subscribe("Cooling1.WaferSize", () => _waferSize(ModuleName.Cooling1, 0));
            DATA.Subscribe("Cooling2.WaferSize", () => _waferSize(ModuleName.Cooling2, 0));
            DATA.Subscribe("EfemRobot.WaferSize", () => _waferSize(ModuleName.EfemRobot, 0));
            //DATA.Subscribe("Buffer.WaferSize", () => _waferSize(ModuleName.Buffer, 0));
            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"));
            EV.Subscribe(new EventItem("Event", EFEMSideDoorOpen, "EFEM Side Door Open", EventLevel.Alarm, EventType.HostNotification));
            EV.Subscribe(new EventItem("Event", EFEMVacuumPressureError, "EFEM Vacuum pressure error", EventLevel.Alarm, EventType.HostNotification));
            EV.Subscribe(new EventItem("Event", EFEMCDAError, "EFEM CDA error", EventLevel.Alarm, EventType.HostNotification));
            EV.Subscribe(new EventItem("Event", EFEMFlowGaugeSensorError, "EFEM flow gauge sensor error", EventLevel.Alarm, EventType.HostNotification));
            EV.Subscribe(new EventItem("Event", EFEMLeakageAlarm, "EFEM leakage alarm", EventLevel.Alarm, EventType.HostNotification));
            EV.Subscribe(new EventItem("Event", EFEMIonizerAlarm, "EFEM Ionizer alarm", EventLevel.Alarm, EventType.HostNotification));
            EV.Subscribe(new EventItem("Event", EFEMFFUAlarm, "EFEM FFU alarm", EventLevel.Alarm, EventType.HostNotification));
            EV.Subscribe(new EventItem("Event", EFEMOffline, "EFEM offline", EventLevel.Alarm, EventType.HostNotification));
            EV.Subscribe(new EventItem("Event", EFEMCommunicationError, "EFEM Communication error", EventLevel.Alarm, EventType.HostNotification));
            EV.Subscribe(new EventItem("Event", WaferTransferFailed, "Wafer Transfer Failed", EventLevel.Alarm, EventType.HostNotification));
            EV.Subscribe(new EventItem("Event", EFEMError, "EFEM Error", EventLevel.Alarm, EventType.HostNotification));
        }
        // Methods
        //
        public bool HomeAll()
        {
            AddAction(new ClearErrorAction(this));
            AddAction(new HomeAllAction(this, ModuleName.EFEM));
            AddAction(new OrgshAction(this, ModuleName.EFEM));
            AddAction(new TrackAction(this, ModuleName.EFEM));
            AddAction(new SIGSTATModuleAction(this, ModuleName.LP1, string.Empty));
            AddAction(new SIGSTATModuleAction(this, ModuleName.LP2, string.Empty));
            return true;
        }
        public bool Home(ModuleName mod, string param)
        {
            AddAction(new HomeModuleAction(this, mod, param));
            return true;
        }
        public bool SIGSTAT(ModuleName mod, string param)
        {
            AddAction(new SIGSTATModuleAction(this, mod, param));
            return true;
        }
        public bool Load(ModuleName mod)
        {
            AddAction(new LoadModuleAction(this, mod));
            return true;
        }
        public bool Unload(ModuleName mod)
        {
            AddAction(new UnloadModuleAction(this, mod));
            return true;
        }
        public bool ReadCarrierId(ModuleName mod)
        {
            AddAction(new ReadCarrierIdModuleAction(this, mod));
            return true;
        }
        public bool WriteCarrierId(ModuleName mod, string id)
        {
            AddAction(new WriteCarrierIdModuleAction(this, mod, id));
            return true;
        }
        public bool ReadTagData(ModuleName mod)
        {
            AddAction(new ReadTagDataModuleAction(this, mod));
            return true;
        }
        public bool WriteTagData(ModuleName mod, string tagData)
        {
            AddAction(new WriteTagDataModuleAction(this, mod, tagData));
            return true;
        }
        public bool Dock(ModuleName mod)
        {
            AddAction(new DockModuleAction(this, mod));
            return true;
        }
        public bool Undock(ModuleName mod)
        {
            AddAction(new UndockModuleAction(this, mod));
            return true;
        }
        public bool Clamp(ModuleName mod, bool isUnloadClamp)
        {
            AddAction(new ClampModuleAction(this, mod, isUnloadClamp));
            return true;
        }
        public bool Unclamp(ModuleName mod)
        {
            AddAction(new UnclampModuleAction(this, mod));
            return true;
        }
        public bool SetThick(ModuleName mod)
        {
            AddAction(new SetThicknessModuleAction(this, mod, "Thick"));
            return true;
        }
        public bool SetThin(ModuleName mod)
        {
            AddAction(new SetThicknessModuleAction(this, mod, "Thin"));
            return true;
        }
        public bool ClearError()
        {
            AddAction(new ClearErrorAction(this));
            return true;
        }
        public void AbortRobot()
        {
            AddAction(new AbortAction(this));
        }
        public void SetRobotMovingInfo(RobotAction action, Hand hand, ModuleName target)
        {
            _robotMoveInfo.Action = action;
            _robotMoveInfo.ArmTarget = hand == Hand.Blade1 ? RobotArm.ArmA : (hand == Hand.Both ? RobotArm.Both : RobotArm.ArmB);
            _robotMoveInfo.BladeTarget = $"{_robotMoveInfo.ArmTarget}.{target}";
        }
        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 (!CheckBladeEnable(mp,"Pick"))
            {
                return false;
            }
            if (ModuleHelper.IsPm(mp.SrcModule))
            {
                JetPM pM = GetPM(mp.SrcModule);
                if (pM.IsSlitDoorClosed)
                {
                    EV.PostAlarmLog(pM.Module.ToString(), "Slit door must be opened");
                    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 }));
                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
            {
                SetRobotMovingInfo(RobotAction.Picking, mp.Arm, mp.SrcModule);
                AddAction(new PickAction(this, mp));
            }
            return true;
        }
        private bool CheckBladeEnable(MoveParam mp, string title)
        {
            if (mp.Arm == Hand.Blade1 && !SC.GetValue("EFEM.EfemRobot.LowerBladeEnable"))
            {
                EV.PostAlarmLog("System", $"{title}:Lower Blade disable");
                return false;
            }
            if (mp.Arm == Hand.Blade2 && !SC.GetValue("EFEM.EfemRobot.UpperBladeEnable"))
            {
                EV.PostAlarmLog("System", $"{title}:Upper Blade disable");
                return false;
            }
            return true;
        }
        public bool Place(MoveParam mp)
        {
            if (!CheckBladeEnable(mp, "Place"))
            {
                return false;
            }
            if (ModuleHelper.IsPm(mp.DestModule))
            {
                JetPM pM = GetPM(mp.DestModule);
                if (pM.IsSlitDoorClosed)
                {
                    EV.PostAlarmLog(pM.Module.ToString(), "Slit door must be opened");
                    return false;
                }
                AddAction(new ExtendAction(this, new ExtendParam { Module = mp.DestModule, Arm = mp.Arm, Pos = ExtendPos.PB }));
                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.Up, false, mp.Arm, false));
            }
            else
            {
                SetRobotMovingInfo(RobotAction.Placing, mp.Arm, mp.DestModule);
                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 (!CheckBladeEnable(pickParam, "Swap") || !CheckBladeEnable(placeParam, "Swap"))
            {
                return;
            }
            if (ModuleHelper.IsPm(pickParam.SrcModule))
            {
                JetPM pM = GetPM(pickParam.SrcModule);
                if (pM.IsSlitDoorClosed)
                {
                    EV.PostAlarmLog(pM.Module.ToString(), "Slit door must be opened");
                    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 }));
                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 }));
                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.Up, false, placeParam.Arm, false));
            }
            else
            {
                SetRobotMovingInfo(RobotAction.Picking, pickParam.Arm, pickParam.SrcModule);
                AddAction(new PickAction(this, pickParam));
                SetRobotMovingInfo(RobotAction.Placing, placeParam.Arm, placeParam.DestModule);
                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].Protrusion)
            {
                EV.PostAlarmLog(mod.ToString(), $"{mod} wafer protrusion, can not do Map");
                return false;
            }
            _LPMs[mod].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, true));
            return true;
        }
        public bool SetPinDown(ModuleName mod)
        {
            AddAction(new LiftAction(this, mod, false));
            return true;
        }
        public bool Align(ModuleName mod, float delayTime, Aitex.Core.Common.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 bool Flip(string orient)
        {
            AddAction(new FlipAction(this, ModuleName.Flipper, orient));
            return true;
        }
        public bool EmsStop()
        {
            AddAction(new EmsStopAction(this, ModuleName.EfemRobot));
            return true;
        }
        public bool SetSpeed(string speed)
        {
            AddAction(new SetSpeedAction(this, ModuleName.EfemRobot, speed));
            return true;
        }
        //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
                        _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.Notify(EFEMVacuumPressureError);
                            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
                        {
                            if (!SC.ContainsItem("EFEM.IgnoreIonizerError") ||
                               !SC.GetValue("EFEM.IgnoreIonizerError"))
                            {
                                EV.Notify(EFEMIonizerAlarm);
                                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.Notify(EFEMCDAError);
                            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
                        {
                            if (!SC.ContainsItem("EFEM.IgnoreWaterFlowError") ||
                               !SC.GetValue("EFEM.IgnoreWaterFlowError"))
                            {
                                EV.Notify(EFEMFlowGaugeSensorError);
                                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.Notify(EFEMLeakageAlarm);
                            EV.PostAlarmLog(Module.ToString(), "EFEM Leakage alarm");
                            Singleton.Instance.EFEM.PostMsg(EfemEntity.MSG.Error);
                        }
                        if (!baData1[10])   // Bit[10] ON=Normal, OFF=Abnormal
                        {
                            EV.Notify(EFEMIonizerAlarm);
                            EV.PostAlarmLog(Module.ToString(), "EFEM Ionizer alarm");
                            Singleton.Instance.EFEM.PostMsg(EfemEntity.MSG.Error);
                        }
                        if (!baData1[11])   // Bit[11] ON=Normal, OFF=Abnormal
                        {
                            EV.Notify(EFEMFFUAlarm);
                            EV.PostAlarmLog(Module.ToString(), "FFU alarm");
                            Singleton.Instance.EFEM.PostMsg(EfemEntity.MSG.Error);
                        }
                        if (!baData1[13])   // Bit[13] ON=RUN, OFF=Maintain
                        {
                            EV.Notify(EFEMOffline);
                            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].HandleEvent(eArg);
                    } // FOUP EVENT
                    break;
                case EfemOperation.GetWaferInfo:
                    _LPMs[eArg.Module].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) && (action.Type != EfemOperation.Lift) && (action.Type != EfemOperation.QueryLPPresence))
                        {
                            Singleton.Instance.EFEM.PostMsg(EfemEntity.MSG.ActionDone, arg.CommandType);
                        }
                        if (action.Type == EfemOperation.Lift)
                        {
                            Singleton.Instance.EFEM.PostMsg(EfemEntity.MSG.LiftActionDone, arg.CommandType);
                        }
                    }
                    else if (ModuleHelper.IsLoadPort(mod))
                    {
                        Singleton.Instance.EFEM.NotifyLP(mod, LoadportEntity.MSG.ActionDone);
                        if (action.Type == EfemOperation.Load || action.Type == EfemOperation.Unload)
                        {
                            //Singleton.Instance.EFEM.PostMsg(EfemEntity.MSG.ActionDone, arg.CommandType);
                        }
                    }
                    if (arg.CommandType == EfemOperation.Pick || arg.CommandType == EfemOperation.Place)
                        SetRobotMovingInfo(RobotAction.None, Hand.Both, ModuleName.System);
                    if (arg.CommandType == EfemOperation.Home)
                    {
                        SetRobotMovingInfo(RobotAction.None, Hand.Both, ModuleName.System);
                        _queryLpStateTimer.Restart();
                    }
                        
                    action.OnPostWork(arg.Data);
                    LOG.Write($"efem action [{action.GetType().Name}] [{action.ID}][{action.Type}] removed from queue");
                    _actions.Remove(action);
                }
                else if (arg.Status == ActionStatus.Cancel)
                {
                    if (ModuleHelper.IsLoadPort(action.Module) || ModuleHelper.IsBuffer(action.Module))
                    {
                        _LPMs[action.Module].OnCancel();
                    }
                    LOG.Write($"efem action [{action.GetType().Name}] [{action.ID}][{action.Type}] canceled from queue");
                    _actions.Remove(action);
                }
            }
        }
        private void MsgOnErrorOccurred(object sender, EventArgs e)
        {
            if (!(e is EfemErrorArgs arg))
                return;
            this.Status = DeviceState.Error;
            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)
                {
                    LOG.Write($"NO activated [{arg.ID}] [{arg.Module}] [{arg.CommandType}] in the queue");
                    return;
                }
                ModuleName mod = action.Module;
                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.Error);
                    }
                }
                else if (ModuleHelper.IsLoadPort(mod))
                {
                    Singleton.Instance.EFEM.NotifyLPError(mod );
                }
                LOG.Write($"efem action [{action.GetType().Name}] [{action.ID}][{action.Module}][{action.Type}] removed from queue");
                _actions.Remove(action);
            }
            EV.Notify(EFEMError);
            EV.PostAlarmLog(Module.ToString(), $"{arg.Description}, [{arg.Message}], [{arg.Factor}]");
        }
    
        public void QueryLPState()
        {
            if (_queryLpStateTimer.IsRunning
                && _queryLpStateTimer.ElapsedMilliseconds >= 30000)
            {
                bool bExisting = false;
                bool bPending = false;
                EfemAction pendingAction = null;
                foreach (var action in _actions)
                {
                    EfemAction EfemAction = action as EfemAction;
                    if (EfemAction != null && EfemAction.Type == EfemOperation.QueryLPPresence)
                    {
                        bExisting = true;
                        if (_queryLpStateTimer.ElapsedMilliseconds > 600000)
                        {
                            bPending = true;
                            pendingAction = EfemAction;
                            break;
                        }
                    }
                }
                if (!bExisting)
                {
                    AddAction(new QueryLPStateAction(this, ModuleName.EFEM));
                    _queryLpStateTimer.Restart();
                }
                if (bPending)
                {
                    LOG.Write($"Found pending action [{pendingAction.GetType().Name}] [{pendingAction.ID}][{pendingAction.Type}], canceled from queue");
                    _actions.Remove(pendingAction);
                    _queryLpStateTimer.Restart();
                }
            }
        }
    }
}