using System;
using System.Collections.Generic;
using Aitex.Core.Common;
using Aitex.Core.RT.DataCenter;
using Aitex.Core.RT.Device;
using Aitex.Core.RT.Event;
using Aitex.Core.RT.Fsm;
using Aitex.Core.RT.OperationCenter;
using Aitex.Core.RT.Routine;
using Aitex.Core.Util;
using Aitex.Core.Utilities;
using MECF.Framework.Common.Equipment;
using MECF.Framework.Common.Fsm;
using MECF.Framework.Common.SubstrateTrackings;
using MECF.Framework.RT.Core.IoProviders;
using SicRT.Instances;
using SicRT.Modules;
using System.Linq;
using System.Threading.Tasks;
using Aitex.Core.RT.Device.Unit;
using Aitex.Core.RT.SCCore;
using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.UPS;
using Aitex.Core.RT.Device.Devices;
using Aitex.Core.RT.Log;
using MECF.Framework.RT.EquipmentLibrary.Devices;
using SicModules.Aligners;
using SicModules.Buffers;
using SicModules.Cassettes;
using SicModules.Devices;
using SicModules.EFEMs;
using SicModules.LLs;
using SicModules.PMs;
using SicModules.TMs;
using SicModules.UnLoads;
using System.Diagnostics;
using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.UnLoad;
using MECF.Framework.Common.Gem;
using Kxware.Common;


namespace SicRT.Equipments.Systems
{
    public enum RtState
    {
        Init,

        Initializing,
        Idle,

        Transfer,
        AutoRunning,
        AutoIdle,

        Cycle,

        PlatformCycle,

        ReturnAllWafer,

        Error,
        ShutDown = 999

    }
    public class EquipmentManager : FsmDevice
    {
        public enum MSG
        {
            HOME,
            RESET,
            Abort,
            ERROR,

            MoveWafer,
            ReturnAllWafer,
            PlatformCycle,

            HomeUnit,

            PauseAuto,
            ResumeAuto,

            Stop,
            StopPlatformCycle,

            StartCycle,

            SetAutoMode,
            SetManualMode,

            CreateJob,
            PauseJob,
            ResumeJob,
            StartJob,
            StopJob,
            AbortJob,

            JobDone,

            FAJobCommand,

            SetOnline,
            SetOffline,

            ModuleError,
            ShutDown = 999,
        }

        public Dictionary<ModuleName, ModuleFsmDevice> Modules { get; set; }

        public bool IsAutoMode
        {
            get
            {
                return FsmState == (int)RtState.AutoRunning;
            }
        }

        public bool IsInit
        {
            get { return FsmState == (int)RtState.Init; }
        }

        public bool IsOnline
        {
            get;
            set;
        }

        public bool IsIdle
        {
            get { return FsmState == (int)RtState.Idle; }
        }
        public bool IsAlarm
        {
            get { return FsmState == (int)RtState.Error; }
        }

        public bool IsAutoRunning
        {
            get
            {
                return FsmState == (int)RtState.Initializing
                       || FsmState == (int)RtState.Transfer
                       || FsmState == (int)RtState.Cycle
                       || FsmState == (int)RtState.AutoRunning;
            }
        }

        private ManualTransfer _manualTransfer;
        private AutoTransfer _auto = null;
        private HomeAll _homeAll;

        private ReturnAllWafer _returnAll;
        private List<string> _modules = new List<string>();
        //readonly IEnumerable<PropertyInfo> IEntityModules;

        private ITAUPS _upsPM1A = null;
        private ITAUPS _upsPM1B = null;

        private ITAUPS _upsPM2A = null;
        private ITAUPS _upsPM2B = null;

        private IoUPS _pm1UPS = null;
        private IoUPS _pm2UPS = null;
        //AETEmp
        //private AETemp _aeTemp = null;
        //private SicAETemp _pmAETemp1 = null;
        //private SicAETemp _pmAETemp2 = null;

        private IoInterLock _pm1InterLock = null;
        private IoInterLock _pm2InterLock = null;

        private IoInterLockEx _tmInterLock = null;
        private IoSlitValve _pm1SlitValve = null;
        private IoSlitValve _pm2SlitValve = null;
        private PeriodicJob _thread;
        //
        private IoTC _tc = null;

        private R_TRIG _notInitialized = new R_TRIG();
        private R_TRIG _initializing = new R_TRIG();
        private R_TRIG _idle = new R_TRIG();
        private R_TRIG _autoIdle = new R_TRIG();
        private R_TRIG _autoRunning = new R_TRIG();

        /// <summary>
        /// 主三色灯对象。
        /// 主三色灯判断方式参考<see cref="GetMainSignalTower"/>方法。
        /// </summary>
        private IoSignalTower _mainSignalTower;

        public EquipmentManager()
        {
            Module = "System";
            Name = "System";

            Modules = new Dictionary<ModuleName, ModuleFsmDevice>();
        }

        public override bool Initialize()
        {
            InitModules();

            GetMainSignalTower();

            EnumLoop<RtState>.ForEach((item) =>
            {
                MapState((int)item, item.ToString());
            });

            EnumLoop<MSG>.ForEach((item) =>
            {
                MapMessage((int)item, item.ToString());
            });
            EnableFsm(100, RtState.Init);

            InitFsm();

            InitData();

            InitOp();

            Singleton<EventManager>.Instance.OnAlarmEvent += Instance_OnAlarmEvent;

            _manualTransfer = new ManualTransfer();
            _homeAll = new HomeAll();
            _auto = new AutoTransfer();
            _returnAll = new ReturnAllWafer();


            SC.RegisterValueChangedCallback("TM.ProcessPressure", SetTansferPressure1);

            SC.RegisterValueChangedCallback("TM.VacuumPressureBaseOffset", SetTansferPressure2);

            return true;
        }

        private void SetTansferPressure1(object value)
        {
            try
            {
                double.TryParse(value.ToString(), out double dbValue);

                double offset = SC.GetValue<double>("TM.VacuumPressureBaseOffset");

                double basePressure = offset + dbValue;

                SC.SetItemValue("TM.PressureBalance.BalancePressure", dbValue);

                SC.SetItemValue("TM.VacuumPressureBase", basePressure);

                SC.SetItemValue("TM.Purge.VentBasePressure", dbValue);

                SC.SetItemValue("PM.VacuumPressureBase", basePressure);

                SC.SetItemValue("PM.PM1.ProcessIdle.FinalPressure", dbValue);

                SC.SetItemValue("PM.PM1.PreProcess.ChamberPressure", dbValue);

                SC.SetItemValue("PM.PM2.ProcessIdle.FinalPressure", dbValue);

                SC.SetItemValue("PM.PM2.PreProcess.ChamberPressure", dbValue);

                SC.SetItemValue("LoadLock.VacuumPressureBase", basePressure);

                SC.SetItemValue("LoadLock.Purge.VentBasePressure", dbValue);

            }
            catch (Exception ex)
            {
                EV.PostWarningLog("System", ex.Message);
            }
        }

        private void SetTansferPressure2(object value)
        {
            try
            {
                double dbValue = SC.GetValue<double>("TM.ProcessPressure");

                double.TryParse(value.ToString(), out double offset);

                double basePressure = offset + dbValue;

                SC.SetItemValue("TM.PressureBalance.BalancePressure", dbValue);

                SC.SetItemValue("TM.VacuumPressureBase", basePressure);

                SC.SetItemValue("TM.Purge.VentBasePressure", dbValue);

                SC.SetItemValue("PM.VacuumPressureBase", basePressure);

                SC.SetItemValue("PM.PM1.ProcessIdle.FinalPressure", dbValue);

                SC.SetItemValue("PM.PM1.PreProcess.ChamberPressure", dbValue);

                SC.SetItemValue("PM.PM2.ProcessIdle.FinalPressure", dbValue);

                SC.SetItemValue("PM.PM2.PreProcess.ChamberPressure", dbValue);

                SC.SetItemValue("LoadLock.VacuumPressureBase", basePressure);

                SC.SetItemValue("LoadLock.Purge.VentBasePressure", dbValue);

            }
            catch (Exception ex)
            {
                EV.PostWarningLog("System", ex.Message);
            }
        }

        /// <summary>
        /// 创建指定的Module的实例。
        /// </summary>
        /// <typeparam name="T">Module类型。</typeparam>
        /// <param name="name">Module名称。</param>
        /// <param name="totalSlots">Module包含的Wafer槽位总数。</param>
        private void GenModule<T>(ModuleName name, int totalSlots = 1)
            where T : ModuleFsmDevice
        {
            ModuleFsmDevice module;

            if (totalSlots > 1)
                module = (ModuleFsmDevice)Activator.CreateInstance(typeof(T), name, totalSlots);
            else
                module = (ModuleFsmDevice)Activator.CreateInstance(typeof(T), name);

            Debug.Assert(module != null, $"Failed to create instance of module {name}");

            // 创建Module实例失败
            if (module == null)
            {
                LOG.Error($"Failed to create instance of module {name}");
                return;
            }

            // 如果Module在系统配置中配置为“未安装”，则不要注册Alarm事件
            /*if (!module.IsInstalled)
            {
                LOG.Warning($"{name} is configured as NotInstalled");
                return;
            }*/

            module.OnEnterError += OnModuleError;
            Modules[name] = module;
        }

        private void InitModules()
        {
            GenModule<TMModule>(ModuleName.TM);
            GenModule<LoadLockModule>(ModuleName.LoadLock);
            GenModule<UnLoadModule>(ModuleName.UnLoad);
            GenModule<BufferModule>(ModuleName.Buffer);
            GenModule<AlignerModule>(ModuleName.Aligner);
            //GenModule<WaferRobotModule>(ModuleName.WaferRobot);
            //GenModule<TrayRobotModule>(ModuleName.TrayRobot);
            GenModule<EFEMModule>(ModuleName.EFEM);
            GenModule<CassetteModule>(ModuleName.CassAL, 25);
            GenModule<CassetteModule>(ModuleName.CassAR, 25);
            GenModule<CassetteModule>(ModuleName.CassBL, 8);
            GenModule<PMModule>(ModuleName.PM1);
            GenModule<PMModule>(ModuleName.PM2);

            _modules = new List<string>() { "System" };
            foreach (var modulesKey in Modules.Keys)
                _modules.Add(modulesKey.ToString());

            foreach (var module in Modules.Values)
            {
                module.Initialize();

                if (!module.IsInstalled)
                {
                    // 如果Module设置为NotInstalled，则禁用其下的所有Device。
                    DEVICE.GetAllDevice().ForEach(x =>
                    {
                        if (x.Module == module.Module)
                            x.IsEnabled = false;
                    });
                }
            }

            _pm1SlitValve = DEVICE.GetDevice<IoSlitValve>("TM.PM1Door");
            _pm2SlitValve = DEVICE.GetDevice<IoSlitValve>("TM.PM2Door");
            _pm1InterLock = DEVICE.GetDevice<IoInterLock>("PM1.PMInterLock");
            _pm2InterLock = DEVICE.GetDevice<IoInterLock>("PM2.PMInterLock");

            _tmInterLock = DEVICE.GetDevice<IoInterLockEx>("TM.IoInterLock");

            _upsPM1A = DEVICE.GetDevice<ITAUPS>("PM1.ITAUPSA");
            _upsPM1B = DEVICE.GetDevice<ITAUPS>("PM1.ITAUPSB");
            _pm1UPS = DEVICE.GetDevice<IoUPS>("PM1.UPS");

            _upsPM2A = DEVICE.GetDevice<ITAUPS>("PM2.ITAUPSA");
            _upsPM2B = DEVICE.GetDevice<ITAUPS>("PM2.ITAUPSB");
            _pm2UPS = DEVICE.GetDevice<IoUPS>("PM2.UPS");


            _tc = DEVICE.GetDevice<IoTC>("PM1.TC1");

            
            //
            _thread = new PeriodicJob(200, OnTimer, "PmSlitDoor", false, true);
            Task.Delay(15000).ContinueWith((a) => _thread.Start());

            //IEntityModules = this.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)
            //   .Where(t => t.PropertyType.GetInterfaces().Contains(typeof(IEntity)) && t.PropertyType.GetInterfaces().Contains(typeof(IModuleEntity)));
        }
        
        private void InitFsm()
        {
            //ShutDown
            Transition(RtState.Init, MSG.ShutDown, FsmStartShutDown, RtState.ShutDown);
            Transition(RtState.Idle, MSG.ShutDown, FsmStartShutDown, RtState.ShutDown);
            Transition(RtState.AutoIdle, MSG.ShutDown, FsmStartShutDown, RtState.ShutDown);
            EnterExitTransition<RtState, FSM_MSG>(RtState.ShutDown, FsmShutDown, null, null);

            //Init sequence
            Transition(RtState.Init, MSG.HOME, FsmStartHome, RtState.Initializing);
            Transition(RtState.Idle, MSG.HOME, FsmStartHome, RtState.Initializing);
            Transition(RtState.Error, MSG.HOME, FsmStartHome, RtState.Initializing);

            Transition(RtState.Initializing, FSM_MSG.TIMER, FsmMonitorHome, RtState.Idle);
            Transition(RtState.Initializing, MSG.ERROR, fError, RtState.Error);
            Transition(RtState.Initializing, MSG.Abort, FsmAbort, RtState.Init);


            //Online
            Transition(RtState.Idle, MSG.SetOnline, FsmStartSetOnline, RtState.Idle);
            Transition(RtState.Idle, MSG.SetOffline, FsmStartSetOffline, RtState.Idle);

            //Reset
            AnyStateTransition(MSG.RESET, fStartReset, RtState.Idle);

            AnyStateTransition(MSG.ERROR, fError, RtState.Error);
            AnyStateTransition((int)FSM_MSG.ALARM, fError, (int)RtState.Error);

            //Auto/Manual
            Transition(RtState.Idle, MSG.SetAutoMode, FsmSetAutoMode, RtState.AutoIdle);
            Transition(RtState.AutoIdle, MSG.SetManualMode, FsmSetManualMode, RtState.Idle);

            Transition(RtState.AutoRunning, FSM_MSG.TIMER, fAutoTransfer, RtState.AutoRunning);

            Transition(RtState.AutoRunning, MSG.Abort, fAbortAutoTransfer, RtState.AutoIdle);
            Transition(RtState.AutoRunning, MSG.JobDone, null, RtState.AutoIdle);

            Transition(RtState.AutoRunning, MSG.StartJob, FsmStartJob, RtState.AutoRunning);
            Transition(RtState.AutoRunning, MSG.PauseJob, FsmPauseJob, RtState.AutoRunning);
            Transition(RtState.AutoRunning, MSG.ResumeJob, FsmResumeJob, RtState.AutoRunning);
            Transition(RtState.AutoRunning, MSG.StopJob, FsmStopJob, RtState.AutoRunning);
            Transition(RtState.AutoRunning, MSG.AbortJob, FsmAbortJob, RtState.AutoRunning);
            
            Transition(RtState.AutoIdle, MSG.StartJob, FsmStartJob, RtState.AutoRunning);
            Transition(RtState.AutoIdle, MSG.Abort, FsmAbort, RtState.AutoIdle);
            Transition(RtState.AutoIdle, MSG.AbortJob, FsmAbortJob, RtState.AutoIdle);

            EnterExitTransition<RtState, FSM_MSG>(RtState.AutoRunning, null, FSM_MSG.NONE, fExitAutoTransfer);

            //return all wafer
            Transition(RtState.Idle, MSG.ReturnAllWafer, FsmStartReturnAllWafer, RtState.ReturnAllWafer);
            Transition(RtState.ReturnAllWafer, FSM_MSG.TIMER, FsmMonitorReturnAllWafer, RtState.Idle);
            Transition(RtState.ReturnAllWafer, MSG.Abort, FsmAbortReturnAllWafer, RtState.Idle);

            //Transfer
            Transition(RtState.Idle, MSG.MoveWafer, fStartTransfer, RtState.Transfer);
            Transition(RtState.Transfer, FSM_MSG.TIMER, fTransfer, RtState.Idle);

            EnterExitTransition<RtState, FSM_MSG>(RtState.Transfer, null, FSM_MSG.NONE, fExitTransfer);

            Transition(RtState.Transfer, MSG.Abort, FsmAbort, RtState.Idle);
        }

        private void InitData()
        {
            DATA.Subscribe("Rt.Status", () => StringFsmStatus);
            DATA.Subscribe("System.IsOnline", () => IsOnline);
            DATA.Subscribe("System.IsIdle", () => IsIdle || IsInit);
            DATA.Subscribe("System.IsAlarm", () => IsAlarm);
            DATA.Subscribe("System.IsBusy", () => IsAutoRunning);
            DATA.Subscribe("System.IsAutoRunning", () => IsAutoRunning);
            DATA.Subscribe("System.Modules", () => _modules);
            DATA.Subscribe("System.SignalTowerData", () => _mainSignalTower?.DeviceData);
        }

        private void InitOp()
        {
            OP.Subscribe("CreateWafer", InvokeCreateWafer);

            OP.Subscribe("CreateTray", InvokeCreateTray);

            OP.Subscribe("DeleteWafer", InvokeDeleteWafer);

            OP.Subscribe("DeleteTray", InvokeDeleteTray);

            OP.Subscribe("ReturnWafer", InvokeReturnWafer);

            OP.Subscribe("AlterWaferInfo", InvokeAlterWaferInfo);

            OP.Subscribe("UpdatTrayBelongTo", (string cmd, object[] args) =>
            {
                WaferManager.Instance.UpdatTrayBelongTo((ModuleName)args[0], (int)args[1], (string)args[2]);
                return true;
            });

            OP.Subscribe("System.ReturnAllWafer", (string cmd, object[] args) =>
            {
                return CheckToPostMessage((int)MSG.ReturnAllWafer);
            });

            OP.Subscribe("System.MoveWafer", (string cmd, object[] args) =>
            {
                if (!Enum.TryParse((string)args[0], out ModuleName source))
                {
                    EV.PostWarningLog(Name, $"Parameter source {(string)args[0]} not valid");
                    return false;
                }

                if (!Enum.TryParse((string)args[2], out ModuleName destination))
                {
                    EV.PostWarningLog(Name, $"Parameter destination {(string)args[1]} not valid");
                    return false;
                }

                if (args.Length > 9 )
                {
                    if ((bool)args[9] && (bool)args[10])
                    {
                        WaferManager.Instance.WaferTrayMoved(source, (int)args[1], destination, (int)args[3]);
                        return true;
                    }
                    else if ((bool)args[9])
                    {
                        WaferManager.Instance.WaferMoved(source, (int)args[1], destination, (int)args[3]);
                        return true;
                    }
                    else if ((bool)args[10])
                    {
                        WaferManager.Instance.TrayMoved(source, (int)args[1], destination, (int)args[3]);
                        return true;
                    }
                }

                if (args.Length >= 8)
                {
                    return CheckToPostMessage((int)MSG.MoveWafer, source, (int)args[1], destination, (int)args[3],
                        (bool)args[4], (int)args[5], (bool)args[6], (int)args[7]);
                }
                if (args.Length == 5)
                {
                    return CheckToPostMessage((int)MSG.MoveWafer, source, (int)args[1], destination, (int)args[3], (bool)args[4]);
                }

                return CheckToPostMessage((int)MSG.MoveWafer, source, (int)args[1], destination, (int)args[3]);
            });

            OP.Subscribe("System.HomeAll", (string cmd, object[] args) =>
            {
                return CheckToPostMessage((int)MSG.HOME);
            });
            OP.Subscribe("System.Abort", (string cmd, object[] args) =>
            {
                return CheckToPostMessage((int)MSG.Abort);
            });

            OP.Subscribe("System.Reset", (string cmd, object[] args) =>
            {
                return CheckToPostMessage((int)MSG.RESET);
            });

            OP.Subscribe("System.SetAutoMode", (string cmd, object[] args) =>
            {
                return CheckToPostMessage((int)MSG.SetAutoMode,args);
            });

            OP.Subscribe("System.SetManualMode", (string cmd, object[] args) =>
            {
                return CheckToPostMessage((int)MSG.SetManualMode);
            });

            OP.Subscribe("System.CreateJob", (string cmd, object[] args) =>
            {
                return _auto.CreateJob((Dictionary<string, object>)args[0]);
            });

            OP.Subscribe("System.StartJob", (string cmd, object[] args) =>
            {
                return CheckToPostMessage((int)MSG.StartJob, args[0]);
            });

            OP.Subscribe("System.PauseJob", (string cmd, object[] args) =>
            {
                return CheckToPostMessage((int)MSG.PauseJob, args[0]);
            });

            OP.Subscribe("System.ResumeJob", (string cmd, object[] args) =>
            {
                return CheckToPostMessage((int)MSG.ResumeJob, args[0]);
            });

            OP.Subscribe("System.StopJob", (string cmd, object[] args) =>
            {
                return CheckToPostMessage((int)MSG.StopJob, args[0]);
            });

            OP.Subscribe("System.AbortJob", (string cmd, object[] args) =>
            {
                return CheckToPostMessage((int)MSG.AbortJob, args[0]);
            });

            OP.Subscribe("System.AbortSingleJob", (string cmd, object[] args) =>
            {
                _auto.AbortSingleJob(args[0].ToString(), args[1].ToString());
                return true;
            });

            OP.Subscribe("System.SetOnline", (string cmd, object[] args) =>
            {
                return CheckToPostMessage((int)MSG.SetOnline);
            });

            OP.Subscribe("System.SetOffline", (string cmd, object[] args) =>
            {
                return CheckToPostMessage((int)MSG.SetOffline);
            });
            OP.Subscribe("System.ShutDown", (string cmd, object[] args) =>
            {
                return CheckToPostMessage((int)MSG.ShutDown, null);
            });

            OP.Subscribe("System.AlertJobDone", (s, args) =>
            {
                var module = ModuleName.System.ToString();
                var slot = 0;
                if (args is { Length: 2 })
                {
                    module = args[0].ToString();
                    int.TryParse(args[1].ToString(), out slot);
                }

                EV.PostMessage(module, EventEnum.PJ_DONE, module, slot);
                _mainSignalTower.AlertJobDone();
                return true;
            });


            OP.Subscribe("System.BuzzerOff", (s, objects) =>
            {
                EV.AckAlarmEvents();
                _mainSignalTower.SwitchOffBuzzer(true);
                return true;
            });

            #region Recipe Editor Lock Password Management

            const string SC_PATH_REQ_RESET_PASS = "System.Recipe.ResetEditorPassword";
            const string SC_PATH_PASSWORD = "System.Recipe.EditorPassword";

            OP.Subscribe("System.SetNewRecipeEditorPassword", (string cmd, object[] args) =>
            {
                if (args.Length == 2 && args[0] is string oldPass
                    && args[1] is string newPass)
                {
                    var oldPassInSc = SC.GetStringValue(SC_PATH_PASSWORD);
                    if (oldPass != oldPassInSc)
                    {
                        EV.PostWarningLog(Module, "Incorrect old password");
                        return false;
                    }

                    SC.SetItemValue(SC_PATH_REQ_RESET_PASS, false);
                    SC.SetItemValue(SC_PATH_PASSWORD, newPass);
                    EV.PostInfoLog(Module, "Recipe Editor Password has been changed");
                    return true;
                }
                else
                {
                    EV.PostWarningLog(Module, "Incorrect args of setting new Recipe Editor password");
                    return false;
                }
            });

            OP.Subscribe("System.ResetRecipeEditorPassword", (string cmd, object[] args) =>
            {
                if (args.Length == 1 && args[0] is string defPass)
                {

                    SC.SetItemValue(SC_PATH_REQ_RESET_PASS, false);
                    SC.SetItemValue(SC_PATH_PASSWORD, defPass);
                    EV.PostInfoLog(Module, "Recipe Editor Password has been reset");
                    return true;
                }
                else
                {
                    EV.PostWarningLog(Module, "Incorrect args of setting new Recipe Editor password");
                    return false;
                }
            });

            // 程序启动时禁用复位密码选项，写在这里方便移植
            SC.SetItemValue(SC_PATH_REQ_RESET_PASS, false);

            #endregion
        }
 
        private void Instance_OnAlarmEvent(EventItem ev)
        {
            if (ev.Level is EventLevel.Alarm or EventLevel.Warning)
            {
                // 产生新的警告或报警时，取消Buzzer静音
                _mainSignalTower?.SwitchOffBuzzer(false);

                // 如果是Warning
                if (ev.Level == EventLevel.Warning)
                    return;

                if (ev.Source == null)
                    return;

                // 产生报警后，立即将报警设备所在的Module强制切换到Error状态。
                if (Modules.ContainsKey(ModuleHelper.Converter(ev.Source)))
                {
                    Modules[ModuleHelper.Converter(ev.Source)]?.PostMsg(FSM_MSG.ALARM, ev.Id, ev.Description);
                }
            }

        }

        /// <summary>
        /// 获取主三色灯对象。
        /// </summary>
        /// <remarks>
        /// 系统设备配置中配置多个三色灯对象，但软件仅控制唯一个三色灯。
        /// <br/>
        /// 该三色灯定义为主三色灯，其余三色灯对象将被禁用，其工作状态自动跟随主三色灯。
        /// <br/>
        /// 通常情况下，使用PM1的三色灯作为主三色灯。
        /// <br/>
        /// 如果PM1被卸载，则系统自动使用PM2控制的三色灯，依此类推。
        /// </remarks>
        /// <returns></returns>
        private void GetMainSignalTower()
        {
            // 系统配置，是否PM控制三色灯
            var isPmHostSt = SC.GetValue("System.IsPMHostSignalTower", true);

            // 获取三色灯列表，并按名称排序
            var sts = DEVICE.GetAllDevice()
                .Where(x => x is IoSignalTower)
                .OrderBy(x=>x.Module)
                .Cast<IoSignalTower>()
                .ToList();

            // 禁用所有三色灯，待确定完主灯后再使能主灯。
            foreach (var st in sts)
            {
                st.IsEnabled = false;
            }

            // 判断三色灯主灯
            if (isPmHostSt)
            {
                var pmAttachedSts = 
                    sts.Where(x => x.Module.StartsWith("PM"))
                    .OrderBy(x => x.Module)
                    .ToList();

                if (pmAttachedSts.Any())
                {
                    foreach (var st in pmAttachedSts)
                    {
                        if (st != null && Modules.TryGetValue(ModuleHelper.Converter(st.Module), out var module))
                        {
                            if (module.IsInstalled)
                            {
                                // 找到第一个有效的PM，获取其三色灯对象。
                                _mainSignalTower = st;
                                break;
                            }
                        }
                    }
                }
            }

            // 如果没找到PM三色灯，或系统配置指定使用TM三色灯
            // 获取隶属于TM的三色灯对象
            _mainSignalTower ??= sts.FirstOrDefault(x => x.Module.StartsWith("TM"));

            // 使能三色灯
            if (_mainSignalTower != null)
            {
                LOG.Write($"SignalTower attaches to {_mainSignalTower.Module}");
                _mainSignalTower.IsEnabled = true;
            }
            else
            {
                LOG.Error("SignalTower object not found");
            }

        }

        #region Init
        private bool FsmStartHome(object[] objs)
        {
            return _homeAll.Start() == Result.RUN;
        }

        private bool FsmMonitorHome(object[] objs)
        {
            Result ret = _homeAll.Monitor();
            if (ret == Result.DONE)
            {
                _homeAll.Clear();
                return true;
            }
            if (ret == Result.FAIL)
            {
                _homeAll.Clear();
                PostMsg(MSG.ERROR);
            }
            return false;
        }

        private bool fError(object[] objs)
        {
            IsOnline = false;

            if (FsmState == (int)RtState.Transfer)
            {
                _manualTransfer.Clear();
            }

            return true;
        }


        #endregion

        #region AutoTransfer

        private bool FsmSetManualMode(object[] objs)
        {
            if (_auto.HasJobRunning)
            {
                EV.PostWarningLog("System", "Can not change to manual mode, abort running job first");
                return false;
            }

            return true;
        }

        private bool FsmSetAutoMode(object[] objs)
        {
            Result ret = _auto.CheckAutoMode(objs);

            return ret == Result.RUN;
        }

        private bool fAutoTransfer(object[] objs)
        {
            Result ret = _auto.Monitor();

            if (_auto.CheckAllJobDone())
            {
                if (!CheckToPostMessage((int)MSG.JobDone))
                    return false;
            }

            return ret == Result.DONE;
        }

        private bool fExitAutoTransfer(object[] objs)
        {
            _auto.Clear();
            return true;
        }

        private bool fAbortAutoTransfer(object[] objs)
        {
            if (FsmState == (int)RtState.Transfer)
            {
                _manualTransfer.Clear();
            }

            if (FsmState == (int)RtState.AutoRunning)
            {
                _auto.Clear();
                _auto.Abort();
            }

            foreach (var modulesValue in Modules.Values)
            {
                if (!modulesValue.Module.Contains("PM"))
                {
                    modulesValue.PostMsg(MSG.Abort);
                }
            }

            GemManager.Instance.Equipment?.SetProcessState(ProcessStates.Aborting);

            return true;
        }

        #endregion


        #region  return all wafer

        private bool FsmAbortReturnAllWafer(object[] param)
        {
            //_returnAll.Clear();
            return true;
        }

        private bool FsmMonitorReturnAllWafer(object[] param)
        {
            return _returnAll.Monitor() == Result.DONE;
        }

        private bool FsmStartReturnAllWafer(object[] param)
        {
            return _returnAll.Start() == Result.RUN;
        }
        #endregion

        #region Transfer
        private bool fStartTransfer(object[] objs)
        {
            Result ret = _manualTransfer.Start(objs);
            if (ret == Result.FAIL || ret == Result.DONE)
                return false;
            return ret == Result.RUN;
        }


        private bool fTransfer(object[] objs)
        {
            Result ret = _manualTransfer.Monitor(objs);
            if (ret == Result.FAIL)
            {
                PostMsg(MSG.ERROR);
                return false;
            }
            return ret == Result.DONE;
        }

        private bool fExitTransfer(object[] objs)
        {
            _manualTransfer.Clear();

            return true;
        }

        private bool fAbortTransfer(object[] objs)
        {
            return true;
        }
        #endregion

        #region reset

        private bool fStartReset(object[] objs)
        {
            EV.ClearAlarmEvent();

            if (FsmState == (int)RtState.AutoRunning)
            {
                _auto.ResetTask();
            }

            Singleton<DeviceEntity>.Instance.PostMsg(DeviceEntity.MSG.RESET);

            foreach (var modulesValue in Modules.Values)
            {
                modulesValue.PostMsg(MSG.RESET);
            }


            IoProviderManager.Instance.Reset();

            if (FsmState == (int)RtState.Error)
                return true;


            return false;
        }

        #endregion

        private bool FsmAbortJob(object[] param)
        {
            _auto.AbortJob((string)param[0]);
            return true;
        }

        private bool FsmStopJob(object[] param)
        {
            _auto.StopJob((string)param[0]);
            return true;
        }

        private bool FsmResumeJob(object[] param)
        {
            _auto.ResumeJob((string)param[0]);
            return true;
        }

        private bool FsmPauseJob(object[] param)
        {
            _auto.PauseJob((string)param[0]);
            return true;
        }

        private bool FsmStartJob(object[] param)
        {
            _auto.StartJob((string)param[0]);
            return true;
        }

        private bool FsmAbort(object[] param)
        {
            if (FsmState == (int)RtState.Transfer)
            {
                _manualTransfer.Clear();
            }

            if (FsmState == (int)RtState.AutoRunning)
            {
                _auto.Clear();
            }

            foreach (var modulesValue in Modules.Values)
            {
                if (!modulesValue.Module.Contains("PM"))
                {
                    modulesValue.PostMsg(MSG.Abort);
                }
            }
            return true;
        }

        private bool FsmStartSetOffline(object[] param)
        {
            IsOnline = false;

            var tm = Modules[ModuleName.TM] as TMModule;
            tm.InvokeOffline();

            var ll = Modules[ModuleName.LoadLock] as LoadLockModule;
            ll.InvokeOffline();

            var buffer = Modules[ModuleName.Buffer] as BufferModule;
            buffer.InvokeOffline();

            var unLoad = Modules[ModuleName.UnLoad] as UnLoadModule;
            unLoad.InvokeOffline();

            var efem = Modules[ModuleName.EFEM] as EFEMModule;
            efem.InvokeOffline();

            var aligner = Modules[ModuleName.Aligner] as AlignerModule;
            aligner.InvokeOffline();

            if (SC.GetValue<bool>($"System.SetUp.IsPM1Installed"))
            {
                var pm1 = Modules[ModuleName.PM1] as PMModule;
                pm1.InvokeOffline();
            }
            if (SC.GetValue<bool>($"System.SetUp.IsPM2Installed"))
            {
                var pm2 = Modules[ModuleName.PM2] as PMModule;
                pm2.InvokeOffline();
            }

            var cassAL = Modules[ModuleName.CassAL] as CassetteModule;
            cassAL.InvokeOffline();

            var cassAR = Modules[ModuleName.CassAR] as CassetteModule;
            cassAR.InvokeOffline();

            var cassBL = Modules[ModuleName.CassBL] as CassetteModule;
            cassBL.InvokeOffline();

            return true;
        }

        private bool FsmStartSetOnline(object[] param)
        {
            IsOnline = true;

            var tm = Modules[ModuleName.TM] as TMModule;
            tm.InvokeOnline();

            var ll = Modules[ModuleName.LoadLock] as LoadLockModule;
            ll.InvokeOnline();

            var buffer = Modules[ModuleName.Buffer] as BufferModule;
            buffer.InvokeOnline();

            var unLoad = Modules[ModuleName.UnLoad] as UnLoadModule;
            unLoad.InvokeOnline();

            var efem = Modules[ModuleName.EFEM] as EFEMModule;
            efem.InvokeOnline();

            var aligner = Modules[ModuleName.Aligner] as AlignerModule;
            aligner.InvokeOnline();

            if (SC.GetValue<bool>($"System.SetUp.IsPM1Installed"))
            {
                var pm1 = Modules[ModuleName.PM1] as PMModule;
                pm1.InvokeOnline();
            }
            if (SC.GetValue<bool>($"System.SetUp.IsPM2Installed"))
            {
                var pm2 = Modules[ModuleName.PM2] as PMModule;
                pm2.InvokeOnline();
            }

            var cassAL = Modules[ModuleName.CassAL] as CassetteModule;
            cassAL.InvokeOnline();

            var cassAR = Modules[ModuleName.CassAR] as CassetteModule;
            cassAR.InvokeOnline();

            var cassBL = Modules[ModuleName.CassBL] as CassetteModule;
            cassBL.InvokeOnline();

            return true;
        }
        #region cassette popup menu
        private bool InvokeReturnWafer(string arg1, object[] args)
        {
            ModuleName target = ModuleHelper.Converter(args[0].ToString());
            int slot = (int)args[1];

            if (ModuleHelper.IsLoadPort(target))
            {
                EV.PostInfoLog("System", string.Format("Wafer already at LoadPort {0} {1}, return operation is not valid", target.ToString(), slot + 1));
                return false;
            }

            if (!WaferManager.Instance.IsWaferSlotLocationValid(target, slot))
            {
                EV.PostWarningLog("System", string.Format("Invalid position，{0}，{1}", target.ToString(), slot.ToString()));
                return false;
            }

            var wafer = WaferManager.Instance.GetWafer(target, slot);
            if (wafer.IsWaferEmpty)
            {
                EV.PostInfoLog("System", string.Format("No wafer at {0} {1}, return operation is not valid", target.ToString(), slot + 1));
                return false;
            }

            return CheckToPostMessage((int)MSG.MoveWafer,
                target, slot,
                wafer.WaferOriginStation.ToString(), wafer.WaferOriginSlot,
                false, 0, false, 0);
        }


        private bool InvokeDeleteWafer(string arg1, object[] args)
        {
            ModuleName chamber = ModuleHelper.Converter(args[0].ToString());
            int slot = (int)args[1];

            if (WaferManager.Instance.CheckHasWafer(chamber, slot))
            {
                WaferManager.Instance.DeleteWafer(chamber, slot);
            }
            else
            {
                EV.PostWarningLog("System", string.Format("Invalid position，{0}，{1}", chamber.ToString(), slot.ToString()));
                return false;
            }

            return true;
        }

        private bool InvokeCreateWafer(string arg1, object[] args)
        {
            ModuleName chamber = ModuleHelper.Converter(args[0].ToString());
            int slot = (int)args[1];
            WaferStatus state = WaferStatus.Normal;

            if (chamber == ModuleName.TrayRobot || chamber == ModuleName.CassBL)
            {
                if (WaferManager.Instance.IsWaferSlotLocationValid(chamber, slot))
                {
                    if (WaferManager.Instance.CheckHasTray(chamber, slot))
                    {
                        EV.PostInfoLog("System", string.Format("{0} slot {1} already has tray.create wafer is not valid", chamber, slot));
                    }
                    else if (WaferManager.Instance.CreateWafer(chamber, slot, state) != null)
                    {
                        EV.PostMessage(ModuleName.System.ToString(), EventEnum.WaferCreate, chamber.ToString(), slot + 1, state.ToString());
                    }
                }
                else
                {
                    EV.PostWarningLog("System", string.Format("Invalid position，{0}，{1}", chamber.ToString(), slot.ToString()));
                    return false;
                }
            }
            else
            {
                if (WaferManager.Instance.IsWaferSlotLocationValid(chamber, slot))
                {
                    if (WaferManager.Instance.CheckHasWafer(chamber, slot))
                    {
                        EV.PostInfoLog("System", string.Format("{0} slot {1} already has wafer.create wafer is not valid", chamber, slot));
                    }
                    else if (WaferManager.Instance.CreateWafer(chamber, slot, state) != null)
                    {
                        EV.PostMessage(ModuleName.System.ToString(), EventEnum.WaferCreate, chamber.ToString(), slot + 1, state.ToString());

                        if (WaferManager.Instance.CheckNoTray(chamber, slot))
                        {
                            WaferManager.Instance.CreateTray(chamber, slot);
                        }
                    }
                }
                else
                {
                    EV.PostWarningLog("System", string.Format("Invalid position，{0}，{1}", chamber.ToString(), slot.ToString()));
                    return false;
                }
            }
            return true;
        }

        private bool InvokeCreateTray(string arg1, object[] args)
        {
            ModuleName chamber = ModuleHelper.Converter(args[0].ToString());
            int slot = (int)args[1];
            TrayStatus state = TrayStatus.Normal;

            if (WaferManager.Instance.IsWaferSlotLocationValid(chamber, slot))
            {
                if (WaferManager.Instance.CheckHasTray(chamber, slot))
                {
                    EV.PostInfoLog("System", string.Format("{0} slot {1} already has tray.create tray is not valid", chamber, slot));
                }
                else if (WaferManager.Instance.CreateTray(chamber, slot, state) != null)
                {
                    //EV.PostMessage(ModuleName.System.ToString(), EventEnum.WaferCreate, chamber.ToString(), slot + 1, state.ToString());
                }
            }
            else
            {
                EV.PostWarningLog("System", string.Format("Invalid position，{0}，{1}", chamber.ToString(), slot.ToString()));
                return false;
            }

            return true;
        }

        private bool InvokeDeleteTray(string arg1, object[] args)
        {
            ModuleName chamber = ModuleHelper.Converter(args[0].ToString());
            int slot = (int)args[1];

            if (WaferManager.Instance.CheckHasWafer(chamber, slot))
            {
                WaferManager.Instance.DeleteWafer(chamber, slot);
            }

            if (WaferManager.Instance.CheckHasTray(chamber, slot))
            {
                //WaferManager.Instance.GetWafer(chamber, slot).TrayStatus = TrayStatus.Empty;
                //WaferManager.Instance.DelectGuid(chamber, slot);

                WaferManager.Instance.DeleteTray(chamber, slot);
            }

            return true;
        }

        private bool InvokeAlterWaferInfo(string arg1, object[] args)
        {
            ModuleName chamber = ModuleHelper.Converter(args[0].ToString());
            var slot = (int)args[1];
            var recipeName = args[2].ToString();
            var trayProcessCount = (int)args[3];
            var wafer = WaferManager.Instance.GetWafer(chamber, slot);
            if (WaferManager.Instance.CheckHasWafer(chamber, slot))
            {
                if(!String.IsNullOrEmpty(recipeName) && wafer != null && wafer.ProcessJob != null && wafer.ProcessJob.Sequence != null && wafer.ProcessState == WaferProcessStatus.Idle)
                {
                    for (int i = 0; i < wafer.ProcessJob.Sequence.Steps.Count; i++)
                    {
                        if (!String.IsNullOrEmpty(wafer.ProcessJob.Sequence.Steps[i].RecipeName))
                        {
                            wafer.ProcessJob.Sequence.Steps[i].RecipeName = recipeName;
                        }
                    }
                }                
            }
            if (WaferManager.Instance.CheckHasTray(chamber, slot))
            {
                if (wafer != null)
                {
                    wafer.TrayProcessCount = trayProcessCount;
                }
            }
            return true;
        }

        #endregion

        private void OnModuleError(object sender, string module)
        {
            if (FsmState == (int)RtState.AutoRunning)
            {
                ModuleName mod = ModuleHelper.Converter(module);

                PostMsg(MSG.ModuleError, module);
            }
        }

        #region ShutDown
        private bool FsmStartShutDown(object[] objs)
        {
            var InBusyModuleNames = this.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)
                .Where(t => t.PropertyType.GetInterfaces().Contains(typeof(IModuleEntity)) && ((IModuleEntity)t.GetValue(this, null)).IsBusy)
                .Select(n => n.Name).ToArray();

            if (InBusyModuleNames.Length > 0)
            {
                EV.PostWarningLog("System", $"Shut down fail , {string.Join(" , ", InBusyModuleNames)} is in busy");
                return false;
            }

            FinishPreparation();

            Term();
            EV.PostKickoutMessage("ShutDown");
            return true;
        }

        private bool FsmShutDown(object[] objs)
        {
            Task.Delay(3000).ContinueWith(a =>
            {
                Environment.Exit(0);
            });
            return true;
        }

        private void FinishPreparation()
        {
            
            if (SC.GetValue<bool>($"System.IsSimulatorMode"))
                return;

            if (SC.GetValue<bool>($"System.SetUp.IsPM1Installed") & SC.GetValue<bool>($"PM.PM1.ShutDownPLC"))
                OP.DoOperation($"PM1.ShutDownPLC");

            if (SC.GetValue<bool>($"System.SetUp.IsPM2Installed") & SC.GetValue<bool>($"PM.PM2.ShutDownPLC"))
                OP.DoOperation($"PM2.ShutDownPLC");
        }

        protected void Term()
        {
            foreach (var modulesValue in Modules.Values)
            {
                modulesValue.Terminate();
            }
        }

        public bool OnTimer()
        {
            MonitorPmTmInfo();
            MonitorUPSAlarm();
            MonitorGemSystemState();

            return true;
        }

        private void MonitorGemSystemState()
        {
            //SetProcessState指的是整个设备的状态,会触发ProcessStateChanged事件

            _notInitialized.CLK = FsmState == (int)RtState.Init;
            if (_notInitialized.Q)
            {
                GemManager.Instance.Equipment?.SetProcessState(ProcessStates.NotInitialized);
            }

            _initializing.CLK = FsmState == (int)RtState.Initializing;
            if (_initializing.Q)
            {
                GemManager.Instance.Equipment?.SetProcessState(ProcessStates.Initializing);
            }

            _idle.CLK = FsmState == (int)RtState.Idle;
            if (_idle.Q)
            {
                GemManager.Instance.Equipment?.SetProcessState(ProcessStates.Idle);
            }

            _autoIdle.CLK = FsmState == (int)RtState.AutoIdle;
            if (_autoIdle.Q)
            {
                GemManager.Instance.Equipment?.SetProcessState(ProcessStates.Ready);
            }

            _autoRunning.CLK = FsmState == (int)RtState.AutoRunning;
            if(_autoRunning.Q)
            {
                GemManager.Instance.Equipment?.SetProcessState(ProcessStates.Executing);
            }
        }

        private void MonitorPmTmInfo()
        {
            if (_pm1SlitValve != null)
            {
                if (_pm1InterLock != null && _pm1InterLock.DoPMASlitDoorClosed != _pm1SlitValve.IsClose)
                {
                    _pm1InterLock.DoPMASlitDoorClosed = _pm1SlitValve.IsClose;
                }

                if (_tmInterLock != null && _pm1InterLock != null)
                {
                    if (_tmInterLock.DoPm1LidClosed != _pm1InterLock.DiChamLidClosed)
                    {
                        _tmInterLock.DoPm1LidClosed = _pm1InterLock.DiChamLidClosed;
                    }

                    if (_tmInterLock.DoRectorAATMTransferReady != _pm1InterLock.DoReactorATMTransferReady)
                    {
                        _tmInterLock.DoRectorAATMTransferReady = _pm1InterLock.DoReactorATMTransferReady;
                    }
                    if (_tmInterLock.DoRectorAProcessTransferReady != _pm1InterLock.DoReactorVACTransferReady)
                    {
                        _tmInterLock.DoRectorAProcessTransferReady = _pm1InterLock.DoReactorVACTransferReady;
                    }
                }
            }

            if (_pm2SlitValve != null)
            {
                if (_pm2InterLock != null && _pm2InterLock.DoPMASlitDoorClosed != _pm2SlitValve.IsClose)
                {
                    _pm2InterLock.DoPMASlitDoorClosed = _pm2SlitValve.IsClose;
                }

                if (_tmInterLock != null && _pm2InterLock != null)
                {
                    if (_tmInterLock.DoPm2LidClosed != _pm2InterLock.DiChamLidClosed)
                    {
                        _tmInterLock.DoPm2LidClosed = _pm2InterLock.DiChamLidClosed;
                    }

                    if (_tmInterLock.DoRectorBATMTransferReady != _pm2InterLock.DoReactorATMTransferReady)
                    {
                        _tmInterLock.DoRectorBATMTransferReady = _pm2InterLock.DoReactorATMTransferReady;
                    }
                    if (_tmInterLock.DoRectorBProcessTransferReady != _pm2InterLock.DoReactorVACTransferReady)
                    {
                        _tmInterLock.DoRectorBProcessTransferReady = _pm2InterLock.DoReactorVACTransferReady;
                    }
                }
            }

            if (!_tmInterLock.DiTMLidClosed || !_tmInterLock.DiBufferLidClosed || 
                (!_tmInterLock.DiPM1DoorClosed && !_pm1InterLock.DiChamLidClosed) ||
                (!_tmInterLock.DiPM2DoorClosed && !_pm2InterLock.DiChamLidClosed))
            {
               _tmInterLock.DoTmCyclePurgeRoutineSuccessed = false;
            }
        }

        private void MonitorUPSAlarm()
        {
            string sReason;

            if (_upsPM1A != null && _upsPM1B != null && _pm1UPS != null)
            {
                bool _power1 = !(_upsPM1A.UPSPowerAlarm || _upsPM1B.UPSPowerAlarm);
                bool _lowerBattery1 = !(_upsPM1A.UPSLowerBatteryAlarm || _upsPM1B.UPSLowerBatteryAlarm);

                if (_pm1UPS.UPSEnable == _power1)
                {
                    _pm1UPS.SetUPSEnable(_power1, out sReason);
                }
                if (_pm1UPS.UPSLowBattery == _lowerBattery1)
                {
                    _pm1UPS.SetUPSLowBattery(_lowerBattery1, out sReason);
                }
            }

            if (_upsPM2A != null && _upsPM2B != null && _pm2UPS != null)
            {
                bool _power2 = !(_upsPM2A.UPSPowerAlarm || _upsPM2B.UPSPowerAlarm);
                bool _lowerBattery2 = !(_upsPM2A.UPSLowerBatteryAlarm || _upsPM2B.UPSLowerBatteryAlarm);

                if (_pm2UPS.UPSEnable == _power2)
                {
                    _pm2UPS.SetUPSEnable(_power2, out sReason);
                }
                if (_pm2UPS.UPSLowBattery == _lowerBattery2)
                {
                    _pm2UPS.SetUPSLowBattery(_lowerBattery2, out sReason);
                }
            }

        }

        #endregion

    }
}
