using System;
using System.Collections.Generic;
using Aitex.Core.RT.Event;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.SCCore;
using Aitex.Core.Util;
using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Robots.RobotBase;
using MECF.Framework.Common.Equipment;
using MECF.Framework.Common.SubstrateTrackings;
using Aitex.Core.RT.DataCenter;
using System.Xml;
using Aitex.Core.RT.IOCore;
using Aitex.Core.RT.Routine;
using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Robots;
using MECF.Framework.Common.CommonData;
using EventType = Aitex.Core.RT.Event.EventType;

namespace Aitex.Core.RT.Device.Unit
{
    public class GAPlcRobot:RobotBaseDevice
    {
        public GAPlcRobot(string module,XmlElement node,string ioModule =""): base(node.GetAttribute("module"), node.GetAttribute("id"))
        {
            base.Module = node.GetAttribute("module");//string.IsNullOrEmpty(node.GetAttribute("module")) ? module : node.GetAttribute("module");
            base.Name = node.GetAttribute("id");
            ioModule = node.GetAttribute("ioModule");

            _aiCurrentXPos_L = ParseAiNode("AI_CurrentXPos_L", node, ioModule);
            _aiCurrentXPos_H = ParseAiNode("AI_CurrentXPos_H", node, ioModule);
            _aiCurrentYPos_L = ParseAiNode("AI_CurrentYPos_L", node, ioModule);
            _aiCurrentYPos_H = ParseAiNode("AI_CurrentYPos_H", node, ioModule);
            _aiCurrentZPos_L = ParseAiNode("AI_CurrentZPos_L", node, ioModule);
            _aiCurrentZPos_H = ParseAiNode("AI_CurrentZPos_H", node, ioModule);

            _aiZAxisPitch_L = ParseAiNode("AI_ZAxisPitch_L", node, ioModule);
            _aiZAxisPitch_H = ParseAiNode("AI_ZAxisPitch_H", node, ioModule);
            _aiZAxisFirstPos_L = ParseAiNode("AI_ZAxisFirstPos_L", node, ioModule);
            _aiZAxisFirstPos_H = ParseAiNode("AI_ZAxisFirstPos_H", node, ioModule);
            _aiZAxisPlacePos_L = ParseAiNode("AI_ZAxisPlacePos_L", node, ioModule);
            _aiZAxisPlacePos_H = ParseAiNode("AI_ZAxisPlacePos_H", node, ioModule);
            _aiXAxisMoveDistance_L = ParseAiNode("AI_XAxisMoveDistance_L", node, ioModule);
            _aiXAxisMoveDistance_H = ParseAiNode("AI_XAxisMoveDistance_H", node, ioModule);
            _aiYAxisMoveDistance_L = ParseAiNode("AI_YAxisMoveDistance_L", node, ioModule);
            _aiYAxisMoveDistance_H = ParseAiNode("AI_YAxisMoveDistance_H", node, ioModule);
            _aiZAxisMoveSpeed_L = ParseAiNode("AI_ZAxisMoveSpeed_L", node, ioModule);
            _aiZAxisMoveSpeed_H = ParseAiNode("AI_ZAxisMoveSpeed_H", node, ioModule);
            _aiXAxisMoveSpeed_L = ParseAiNode("AI_XAxisMoveSpeed_L", node, ioModule);
            _aiXAxisMoveSpeed_H = ParseAiNode("AI_XAxisMoveSpeed_H", node, ioModule);
            _aiYAxisMoveSpeed_L = ParseAiNode("AI_YAxisMoveSpeed_L", node, ioModule);
            _aiYAxisMoveSpeed_H = ParseAiNode("AI_YAxisMoveSpeed_H", node, ioModule);



            _aiTargetPos = ParseAiNode("AI_TargetPos", node, ioModule);






            _aoZAxisPitch_L = ParseAoNode("AO_ZAxisPitch_L", node, ioModule);
            _aoZAxisPitch_H = ParseAoNode("AO_ZAxisPitch_H", node, ioModule);
            _aoZAxisFirstPos_L = ParseAoNode("AO_ZAxisFirstPos_L", node, ioModule);
            _aoZAxisFirstPos_H = ParseAoNode("AO_ZAxisFirstPos_H", node, ioModule);
            _aoZAxisPlacePos_L = ParseAoNode("AO_ZAxisPlacePos_L", node, ioModule);
            _aoZAxisPlacePos_H = ParseAoNode("AO_ZAxisPlacePos_H", node, ioModule);
            _aoXAxisMoveDistance_L = ParseAoNode("AO_XAxisMoveDistance_L", node, ioModule);
            _aoXAxisMoveDistance_H = ParseAoNode("AO_XAxisMoveDistance_H", node, ioModule);
            _aoYAxisMoveDistance_L = ParseAoNode("AO_YAxisMoveDistance_L", node, ioModule);
            _aoYAxisMoveDistance_H = ParseAoNode("AO_YAxisMoveDistance_H", node, ioModule);
            _aoZAxisMoveSpeed_L = ParseAoNode("AO_ZAxisMoveSpeed_L", node, ioModule);
            _aoZAxisMoveSpeed_H = ParseAoNode("AO_ZAxisMoveSpeed_H", node, ioModule);
            _aoXAxisMoveSpeed_L = ParseAoNode("AO_XAxisMoveSpeed_L", node, ioModule);
            _aoXAxisMoveSpeed_H = ParseAoNode("AO_XAxisMoveSpeed_H", node, ioModule);
            _aoYAxisMoveSpeed_L = ParseAoNode("AO_YAxisMoveSpeed_L", node, ioModule);
            _aoYAxisMoveSpeed_H = ParseAoNode("AO_YAxisMoveSpeed_H", node, ioModule);
            _aoTargetPos = ParseAoNode("AO_TargetPos", node, ioModule);
            _aoXMoveFwdDistance_L = ParseAoNode("AO_XMoveFwdDistance_L", node, ioModule);
            _aoXMoveFwdDistance_H = ParseAoNode("AO_XMoveFwdDistance_H", node, ioModule);
            _aoYMoveFwdDistance_L = ParseAoNode("AO_YMoveFwdDistance_L", node, ioModule);
            _aoYMoveFwdDistance_H = ParseAoNode("AO_YMoveFwdDistance_H", node, ioModule);
            _aoZMoveFwdDistance_L = ParseAoNode("AO_ZMoveFwdDistance_L", node, ioModule);
            _aoZMoveFwdDistance_H = ParseAoNode("AO_ZMoveFwdDistance_H", node, ioModule);

            _aoXMoveRevDistance_L = ParseAoNode("AO_XMoveRevDistance_L", node, ioModule);
            _aoXMoveRevDistance_H = ParseAoNode("AO_XMoveRevDistance_H", node, ioModule);
            _aoYMoveRevDistance_L = ParseAoNode("AO_YMoveRevDistance_L", node, ioModule);
            _aoYMoveRevDistance_H = ParseAoNode("AO_YMoveRevDistance_H", node, ioModule);
            _aoZMoveRevDistance_L = ParseAoNode("AO_ZMoveRevDistance_L", node, ioModule);
            _aoZMoveRevDistance_H = ParseAoNode("AO_ZMoveRevDistance_H", node, ioModule);

            _diReset_Ack = ParseDiNode("DI_Reset_Ack", node, ioModule);
            _diHome_Ack = ParseDiNode("DI_Home_Ack", node, ioModule);
            _diSave_Ack = ParseDiNode("DI_Save_Ack", node, ioModule);
            _diPick_Ack = ParseDiNode("DI_Pick_Ack", node, ioModule);
            _diPlace_Ack = ParseDiNode("DI_Place_Ack", node, ioModule);
            _diX_Fwd_Ack = ParseDiNode("DI_X_Fwd_Ack", node, ioModule);
            _diX_Rev_Ack = ParseDiNode("DI_X_Rev_Ack", node, ioModule);
            _diY_Fwd_Ack = ParseDiNode("DI_Y_Fwd_Ack", node, ioModule);
            _diY_Rev_Ack = ParseDiNode("DI_Y_Rev_Ack", node, ioModule);
            _diZ_Fwd_Ack = ParseDiNode("DI_Z_Fwd_Ack", node, ioModule);
            _diZ_Rev_Ack = ParseDiNode("DI_Z_Rev_Ack", node, ioModule);
            _diPlugError = ParseDiNode("DI_PlugError", node, ioModule);
            _diZMotionError = ParseDiNode("DI_ZMotionError", node, ioModule);
            _diXMotionError = ParseDiNode("DI_XMotionError", node, ioModule);
            _diYMotionError = ParseDiNode("DI_YMotionError", node, ioModule);
            _diZMotionOutLimit = ParseDiNode("DI_ZMotionOutLimit", node, ioModule);
            _diXMotionOutLimit = ParseDiNode("DI_XMotionOutLimit", node, ioModule);
            _diYMotionOutLimit = ParseDiNode("DI_YMotionOutLimit", node, ioModule);
            _diPLCReady = ParseDiNode("DI_PLCReady", node, ioModule);
            _diMotionIdle = ParseDiNode("DI_MotionIdle", node, ioModule);

            _doResetCmd = ParseDoNode("DO_ResetCmd", node, ioModule);
            _doHomeCmd = ParseDoNode("DO_HomeCmd", node, ioModule);
            _doSaveCmd = ParseDoNode("DO_SaveCmd", node, ioModule);
            _doPickCmd = ParseDoNode("DO_PickCmd", node, ioModule);
            _doPlaceCmd = ParseDoNode("DO_PlaceCmd", node, ioModule);
            _doX_FwdCmd = ParseDoNode("DO_X_FwdCmd", node, ioModule);
            _doX_RevCmd = ParseDoNode("DO_X_RevCmd", node, ioModule);
            _doY_FwdCmd = ParseDoNode("DO_Y_FwdCmd", node, ioModule);
            _doY_RevCmd = ParseDoNode("DO_Y_RevCmd", node, ioModule);
            _doZ_FwdCmd = ParseDoNode("DO_Z_FwdCmd", node, ioModule);
            _doZ_RevCmd = ParseDoNode("DO_Z_RevCmd", node, ioModule);


            EV.Subscribe(new EventItem("Alarm", AlarmRobotError, $"Robot occurred error.", EventLevel.Alarm, EventType.EventUI_Notify));

            DATA.Subscribe($"{Name}.PLCReady", () => _diPLCReady.Value);
            DATA.Subscribe($"{Name}.MotionIdle", () => _diMotionIdle.Value);

            DATA.Subscribe($"{Name}.SlotPitch", () => _slotPitch);
            DATA.Subscribe($"{Name}.FirstZPosition", () => _zFirstPosition);
            DATA.Subscribe($"{Name}.PlaceWaferZPosition", () => _zPutWaferPosition);
            DATA.Subscribe($"{Name}.XMoveDistance", () => _xMoveDistance);
            DATA.Subscribe($"{Name}.YMoveDistance", () => _yMoveDistance);
            DATA.Subscribe($"{Name}.ZMoveSpeed", () => _zMoveSpeed);
            DATA.Subscribe($"{Name}.YMoveSpeed", () => _yMoveSpeed);
            DATA.Subscribe($"{Name}.XMoveSpeed", () => _xMoveSpeed);

            DATA.Subscribe($"{Name}.CurrentXPosition", () => _currentXPosition);
            DATA.Subscribe($"{Name}.CurrentYPosition", () => _currentYPosition);
            DATA.Subscribe($"{Name}.CurrentZPosition", () => _currentZPosition);


            _trigErrorOccurred = new R_TRIG();
            _thread = new PeriodicJob(10, OnTimer, $"{Module}.{Name} MonitorHandler", true);

        }
        private string AlarmRobotError = "RobotOccurredError";

        

        private AIAccessor _aiZAxisPitch_L;
        private AIAccessor _aiZAxisPitch_H;
        private AIAccessor _aiZAxisFirstPos_L;
        private AIAccessor _aiZAxisFirstPos_H;
        private AIAccessor _aiZAxisPlacePos_L;
        private AIAccessor _aiZAxisPlacePos_H;
        private AIAccessor _aiXAxisMoveDistance_L;
        private AIAccessor _aiXAxisMoveDistance_H;
        private AIAccessor _aiYAxisMoveDistance_L;
        private AIAccessor _aiYAxisMoveDistance_H;
        private AIAccessor _aiZAxisMoveSpeed_L;
        private AIAccessor _aiZAxisMoveSpeed_H;
        private AIAccessor _aiXAxisMoveSpeed_L;
        private AIAccessor _aiXAxisMoveSpeed_H;
        private AIAccessor _aiYAxisMoveSpeed_L;
        private AIAccessor _aiYAxisMoveSpeed_H;
        private AIAccessor _aiTargetPos;

        private AIAccessor _aiCurrentXPos_L;
        private AIAccessor _aiCurrentXPos_H;

        private AIAccessor _aiCurrentYPos_L;
        private AIAccessor _aiCurrentYPos_H;

        private AIAccessor _aiCurrentZPos_L;
        private AIAccessor _aiCurrentZPos_H;

        private AOAccessor _aoZAxisPitch_L;
        private AOAccessor _aoZAxisPitch_H;
        private AOAccessor _aoZAxisFirstPos_L;
        private AOAccessor _aoZAxisFirstPos_H;
        private AOAccessor _aoZAxisPlacePos_L;
        private AOAccessor _aoZAxisPlacePos_H;
        private AOAccessor _aoXAxisMoveDistance_L;
        private AOAccessor _aoXAxisMoveDistance_H;
        private AOAccessor _aoYAxisMoveDistance_L;
        private AOAccessor _aoYAxisMoveDistance_H;
        private AOAccessor _aoZAxisMoveSpeed_L;
        private AOAccessor _aoZAxisMoveSpeed_H;
        private AOAccessor _aoXAxisMoveSpeed_L;
        private AOAccessor _aoXAxisMoveSpeed_H;
        private AOAccessor _aoYAxisMoveSpeed_L;
        private AOAccessor _aoYAxisMoveSpeed_H;
        private AOAccessor _aoTargetPos;
        private AOAccessor _aoXMoveFwdDistance_L;
        private AOAccessor _aoXMoveFwdDistance_H;
        private AOAccessor _aoYMoveFwdDistance_L;
        private AOAccessor _aoYMoveFwdDistance_H;
        private AOAccessor _aoZMoveFwdDistance_L;
        private AOAccessor _aoZMoveFwdDistance_H;

        private AOAccessor _aoXMoveRevDistance_L;
        private AOAccessor _aoXMoveRevDistance_H;
        private AOAccessor _aoYMoveRevDistance_L;
        private AOAccessor _aoYMoveRevDistance_H;
        private AOAccessor _aoZMoveRevDistance_L;
        private AOAccessor _aoZMoveRevDistance_H;


        private DIAccessor _diReset_Ack;
        private DIAccessor _diHome_Ack;
        private DIAccessor _diSave_Ack;
        private DIAccessor _diPick_Ack;
        private DIAccessor _diPlace_Ack;
        private DIAccessor _diX_Fwd_Ack;
        private DIAccessor _diX_Rev_Ack;
        private DIAccessor _diY_Fwd_Ack;
        private DIAccessor _diY_Rev_Ack;
        private DIAccessor _diZ_Fwd_Ack;
        private DIAccessor _diZ_Rev_Ack;
        private DIAccessor _diPlugError;
        private DIAccessor _diZMotionError;
        private DIAccessor _diXMotionError;
        private DIAccessor _diYMotionError;
        private DIAccessor _diZMotionOutLimit;
        private DIAccessor _diXMotionOutLimit;
        private DIAccessor _diYMotionOutLimit;
        private DIAccessor _diPLCReady;
        private DIAccessor _diMotionIdle;

        private DOAccessor _doResetCmd;
        private DOAccessor _doHomeCmd;
        private DOAccessor _doSaveCmd;
        private DOAccessor _doPickCmd;
        private DOAccessor _doPlaceCmd;
        private DOAccessor _doX_FwdCmd;
        private DOAccessor _doX_RevCmd;
        private DOAccessor _doY_FwdCmd;
        private DOAccessor _doY_RevCmd;
        private DOAccessor _doZ_FwdCmd;
        private DOAccessor _doZ_RevCmd;

        private PeriodicJob _thread;
        private R_TRIG _trigErrorOccurred;
        private int _currentXPosition
        {
            get
            {
                return TwoShortConvInt(_aiCurrentXPos_L.Value, _aiCurrentXPos_H.Value);
            }
        }
        private int _currentYPosition
        {
            get
            {
                return TwoShortConvInt(_aiCurrentYPos_L.Value, _aiCurrentYPos_H.Value);
            }
        }
        private int _currentZPosition
        {
            get
            {
                return TwoShortConvInt(_aiCurrentZPos_L.Value, _aiCurrentZPos_H.Value);
            }
        }

        private int _slotPitch
        {
            get
            {
                return TwoShortConvInt(_aiZAxisPitch_L.Value, _aiZAxisPitch_H.Value);
            }
        }

        private int _zFirstPosition
        {
            get
            {
                return TwoShortConvInt(_aiZAxisFirstPos_L .Value, _aiZAxisFirstPos_H.Value);
            }
        }

        private int _zPutWaferPosition
        {
            get
            {
                return TwoShortConvInt(_aiZAxisPlacePos_L.Value, _aiZAxisPlacePos_H.Value);
            }
        }


        private int _xMoveDistance
        {
            get
            {
                return TwoShortConvInt(_aiXAxisMoveDistance_L.Value, _aiXAxisMoveDistance_H.Value);
            }
        }
        private int _yMoveDistance
        {
            get
            {
                return TwoShortConvInt(_aiYAxisMoveDistance_L.Value, _aiYAxisMoveDistance_H.Value);
            }
        }
        private int _zMoveSpeed
        {
            get
            {
                return TwoShortConvInt(_aiZAxisMoveSpeed_L.Value, _aiZAxisMoveSpeed_H.Value);
            }
        }
        private int _yMoveSpeed
        {
            get
            {
                return TwoShortConvInt(_aiYAxisMoveSpeed_L.Value, _aiYAxisMoveSpeed_H.Value);
            }
        }
        private int _xMoveSpeed
        {
            get
            {
                return TwoShortConvInt(_aiXAxisMoveSpeed_L.Value, _aiXAxisMoveSpeed_H.Value);
            }
        }
        private bool OnTimer()
        {
            _trigErrorOccurred.CLK =
                _diPlugError.Value || _diZMotionError.Value
                || _diXMotionError.Value || _diYMotionError.Value || _diZMotionOutLimit.Value
                || _diXMotionOutLimit.Value || _diYMotionOutLimit.Value;

            if(_trigErrorOccurred.Q)
            {
                EV.Notify(AlarmRobotError);
                if (_diPlugError.Value)
                {
                    EV.PostAlarmLog("Robot", $"Robot occurred error: _diPlugError.");
                }
                if (_diZMotionError.Value)
                {
                    EV.PostAlarmLog("Robot", $"Robot occurred error: _diZMotionError.");
                }
                if (_diXMotionError.Value)
                {
                    EV.PostAlarmLog("Robot", $"Robot occurred error: _diXMotionError.");
                }
                if (_diYMotionError.Value)
                {
                    EV.PostAlarmLog("Robot", $"Robot occurred error: _diYMotionError.");
                }
                if (_diZMotionOutLimit.Value)
                {
                    EV.PostAlarmLog("Robot", $"Robot occurred error: _diZMotionOutLimit.");
                }
                if (_diYMotionOutLimit.Value)
                {
                    EV.PostAlarmLog("Robot", $"Robot occurred error: _diYMotionOutLimit.");
                }
                if (_diXMotionOutLimit.Value)
                {
                    EV.PostAlarmLog("Robot", $"Robot occurred error: _diXMotionOutLimit.");
                }

                OnError("Robot Error");

            }



            return true;
        }


        #region Override Robot base function

        private enum RobotStepEnum
        {
            ActionStep1,
            ActionStep2,
            ActionStep3,
            ActionStep4,
            ActionStep5,
            ActionStep6,
            ActionStep7,
            ActionStep8,

            ActionStep9,
            ActionStep10,
            ActionStep11,
            ActionStep12,

            ActionStep13,
            ActionStep14,
            ActionStep15,
            ActionStep16,
        }

        private DateTime _dtActionStart;
        
        protected override bool fClear(object[] param)
        {
            return true;
        }

        protected override bool fStartReadData(object[] param)
        {
            return true;
        }

        protected override bool fStartSetParameters(object[] param)
        {
            
            try
            {
                _aoZAxisPitch_L.Value = _aiZAxisPitch_L.Value;
                _aoZAxisPitch_H.Value = _aiZAxisPitch_H.Value;
                _aoZAxisFirstPos_L.Value = _aiZAxisFirstPos_L.Value;
                _aoZAxisFirstPos_H.Value = _aiZAxisFirstPos_H.Value;
                _aoZAxisPlacePos_L.Value = _aiZAxisPlacePos_L.Value;
                _aoZAxisPlacePos_H.Value = _aiZAxisPlacePos_H.Value;
                _aoXAxisMoveDistance_L.Value = _aiXAxisMoveDistance_L.Value;
                _aoXAxisMoveDistance_H.Value = _aiXAxisMoveDistance_H.Value;
                _aoYAxisMoveDistance_L.Value = _aiYAxisMoveDistance_L.Value;
                _aoYAxisMoveDistance_H.Value = _aiYAxisMoveDistance_H.Value;
                _aoZAxisMoveSpeed_L.Value = _aiZAxisMoveSpeed_L.Value;
                _aoZAxisMoveSpeed_H.Value = _aiZAxisMoveSpeed_H.Value;
                _aoXAxisMoveSpeed_L.Value = _aiXAxisMoveSpeed_L.Value;
                _aoXAxisMoveSpeed_H.Value = _aiXAxisMoveSpeed_H.Value;
                _aoYAxisMoveSpeed_L.Value = _aiYAxisMoveSpeed_L.Value;
                _aoYAxisMoveSpeed_H.Value = _aiYAxisMoveSpeed_H.Value;




                string setcommand = param[0].ToString();
                int setvalue = Convert.ToInt32(param[1].ToString());

                short lowbit=0, highbit=0;

                IntConvTwoShort(setvalue, ref lowbit, ref highbit);
                _dtActionStart = DateTime.Now;
                ResetRoutine();


                switch (setcommand)
                {
                    case "SlotPitch":
                        _aoZAxisPitch_L.Value = lowbit;
                        _aoZAxisPitch_H.Value = highbit;
                        break;
                    case "FirstZPosition":
                        _aoZAxisFirstPos_L.Value = lowbit;
                        _aoZAxisFirstPos_H.Value = highbit;
                        break;
                    case "PlaceWaferZPosition":
                        _aoZAxisPlacePos_L.Value = lowbit;
                        _aoZAxisPlacePos_H.Value = highbit;
                        break;
                    case "XMoveDistance":
                        _aoXAxisMoveDistance_L.Value = lowbit;
                        _aoXAxisMoveDistance_H.Value = highbit;
                        break;
                    case "YMoveDistance":
                        _aoYAxisMoveDistance_L.Value = lowbit;
                        _aoYAxisMoveDistance_H.Value = highbit;
                        break;
                    case "ZMoveSpeed":
                        _aoZAxisMoveSpeed_L.Value = lowbit;
                        _aoZAxisMoveSpeed_H.Value = highbit;
                        break;
                    case "XMoveSpeed":
                        _aoXAxisMoveSpeed_L.Value = lowbit;
                        _aoXAxisMoveSpeed_H.Value = highbit;
                        break;
                    case "YMoveSpeed":
                        _aoYAxisMoveSpeed_L.Value = lowbit;
                        _aoYAxisMoveSpeed_H.Value = highbit;
                        break;
                    default:
                        return false;
                }
            }
            catch(Exception ex)
            {
                LOG.Write(ex);
            }
            return true;
        }


        protected override bool fMonitorSetParamter(object[] param)
        {
            IsBusy = false;
            if (DateTime.Now - _dtActionStart > TimeSpan.FromSeconds(RobotCommandTimeout))
            {
                OnError("Command execution timeout");
                return true;
            }
            try
            {
                SetDoState((int)RobotStepEnum.ActionStep2, _doSaveCmd, true, Notify);
                WaitDiState((int)RobotStepEnum.ActionStep3, RobotCommandTimeout, _diSave_Ack, true, Notify, Stop);
                SetDoState((int)RobotStepEnum.ActionStep4, _doSaveCmd, false, Notify);
                WaitDiState((int)RobotStepEnum.ActionStep5, RobotCommandTimeout, _diSave_Ack, false, Notify, Stop);

            }
            catch (RoutineBreakException)
            {
                return false;
            }
            catch (RoutineFaildException)
            {
                EV.PostAlarmLog("Alarm", "Pick wafer failed.");
                OnError("PickFailed");
            }

            return true;
        }

        protected override bool fStartTransferWafer(object[] param)
        {
            return true;
        }

        protected override bool fStartUnGrip(object[] param)
        {
            return false;
        }

        protected override bool fStartGrip(object[] param)
        {
            return false;
        }

        protected override bool fStartGoTo(object[] param)
        {
            return false;
        }

        protected override bool fStartMapWafer(object[] param)
        {
            return false;
        }

        protected override bool fStartSwapWafer(object[] param)
        {
            return false;
        }

        protected override bool fStartPlaceWafer(object[] param)
        {
            if(!_diPLCReady.Value)
            {
                EV.PostAlarmLog("Robot", "PLC ready signal is OFF.");
                return false;
            }
            if(!_diMotionIdle.Value)
            {
                EV.PostAlarmLog("Robot", "Motion idle signal is OFF.");
                return false;
            }
            _dtActionStart = DateTime.Now;
            ResetRoutine();
            try
            {
                //RobotArmEnum arm = (RobotArmEnum)param[0];
                ModuleName tempmodule = (ModuleName)Enum.Parse(typeof(ModuleName), param[1].ToString());

                int slotindex = int.Parse(param[2].ToString());

                if (ModuleHelper.IsLoadPort(tempmodule))
                {
                    _aoTargetPos.Value = (short)(slotindex + 1);
                }
                else
                    _aoTargetPos.Value = 26;

                Blade1Target = tempmodule;
                Blade2Target = tempmodule;


                CmdTarget = tempmodule;
                MoveInfo = new RobotMoveInfo()
                {
                    Action = RobotAction.Picking,
                    ArmTarget = CmdRobotArm == RobotArmEnum.Lower ? RobotArm.ArmA : RobotArm.ArmB,
                    BladeTarget = BuildBladeTarget(),
                };
            }
            catch (Exception ex)
            {                         
                LOG.Write(ex);
                return false;
            }

            return true;
        }

        protected override bool fMonitorPlace(object[] param)
        {
            IsBusy = false;
            if (DateTime.Now - _dtActionStart > TimeSpan.FromSeconds(RobotCommandTimeout))
            {
                OnError("Command execution timeout");
                return true;
            }
            try
            {
                WaitAiValue((int)RobotStepEnum.ActionStep1, RobotCommandTimeout, _aiTargetPos, _aoTargetPos.Value, Notify, Stop);
                SetDoState((int)RobotStepEnum.ActionStep2, _doPlaceCmd, true, Notify);
                WaitDiState((int)RobotStepEnum.ActionStep3, RobotCommandTimeout, _diPlace_Ack, true, Notify, Stop);
                SetDoState((int)RobotStepEnum.ActionStep4, _doPlaceCmd, false, Notify);
                WaitDiState((int)RobotStepEnum.ActionStep5, RobotCommandTimeout, _diPlace_Ack, false, Notify, Stop);

            }
            catch (RoutineBreakException)
            {
                return false;
            }
            catch (RoutineFaildException)
            {
                EV.PostAlarmLog("Alarm", "Pick wafer failed.");
                OnError("PickFailed");
                return true;
            }

            Blade1Target = ModuleName.System;
            Blade2Target = ModuleName.System;
            CmdTarget = ModuleName.System;
            MoveInfo = new RobotMoveInfo()
            {
                Action = RobotAction.Picking,
                ArmTarget = CmdRobotArm == RobotArmEnum.Lower ? RobotArm.ArmA : RobotArm.ArmB,
                BladeTarget = BuildBladeTarget(),
            };


            ModuleName sourcemodule;
            if (!Enum.TryParse(CurrentParamter[1].ToString(), out sourcemodule)) return false;
            int SourceslotIndex;
            if (!int.TryParse(CurrentParamter[2].ToString(), out SourceslotIndex)) return false;

            WaferManager.Instance.WaferMoved(RobotModuleName, 0,sourcemodule, SourceslotIndex);


            return true;
        }

        protected override bool fMonitorPick(object[] param)
        {
            IsBusy = false;
            if (DateTime.Now - _dtActionStart > TimeSpan.FromSeconds(RobotCommandTimeout))
            {
                OnError("Command execution timeout");
                return true;
            }
            try
            {
                WaitAiValue((int)RobotStepEnum.ActionStep1, RobotCommandTimeout, _aiTargetPos, _aoTargetPos.Value, Notify, Stop);
                SetDoState((int)RobotStepEnum.ActionStep2, _doPickCmd, true, Notify);
                WaitDiState((int)RobotStepEnum.ActionStep3, RobotCommandTimeout, _diPick_Ack, true, Notify, Stop);
                SetDoState((int)RobotStepEnum.ActionStep4, _doPickCmd, false, Notify);
                WaitDiState((int)RobotStepEnum.ActionStep5, RobotCommandTimeout, _diPick_Ack, false, Notify, Stop);

            }
            catch (RoutineBreakException)
            {
                return false;
            }
            catch (RoutineFaildException)
            {
                EV.PostAlarmLog("Alarm","Pick wafer failed.");
                OnError("PickFailed");
                return true;
            }

            Blade1Target = ModuleName.System;
            Blade2Target = ModuleName.System;
            CmdTarget = ModuleName.System;
            MoveInfo = new RobotMoveInfo()
            {
                Action = RobotAction.Picking,
                ArmTarget = CmdRobotArm == RobotArmEnum.Lower ? RobotArm.ArmA : RobotArm.ArmB,
                BladeTarget = BuildBladeTarget(),
            };

            ModuleName sourcemodule;
            if (!Enum.TryParse(CurrentParamter[1].ToString(), out sourcemodule)) return false;
            int SourceslotIndex;
            if (!int.TryParse(CurrentParamter[2].ToString(), out SourceslotIndex)) return false;

            WaferManager.Instance.WaferMoved(sourcemodule, SourceslotIndex, RobotModuleName, 0);


            return true;
        }

        protected override bool fStartPickWafer(object[] param)
        {
            if (!_diPLCReady.Value)
            {
                EV.PostAlarmLog("Robot", "PLC ready signal is OFF.");
                return false;
            }
            if (!_diMotionIdle.Value)
            {
                EV.PostAlarmLog("Robot", "Motion idle signal is OFF.");
                return false;
            }
            _dtActionStart = DateTime.Now;
            ResetRoutine();
            try
            {
                ModuleName tempmodule = (ModuleName)Enum.Parse(typeof(ModuleName), param[1].ToString());

                int slotindex = int.Parse(param[2].ToString());

                if (ModuleHelper.IsLoadPort(tempmodule))
                {
                    _aoTargetPos.Value = (short)(slotindex + 1);
                }
                else
                    _aoTargetPos.Value = 26;


                Blade1Target = tempmodule;
                Blade2Target = tempmodule;


                CmdTarget = tempmodule;
                MoveInfo = new RobotMoveInfo()
                {
                    Action = RobotAction.Picking,
                    ArmTarget = CmdRobotArm == RobotArmEnum.Lower ? RobotArm.ArmA : RobotArm.ArmB,
                    BladeTarget = BuildBladeTarget(),
                };



            }
            catch (Exception ex)
            {
                LOG.Write(ex);
                return false;
            }

            return true;
        }

        protected override bool fResetToReady(object[] param)
        {
            return true;
        }

        protected override bool fReset(object[] param)
        {
            _dtActionStart = DateTime.Now;
            ResetRoutine();
            return true;
        }
        protected override bool fMonitorReset(object[] param)
        {
            IsBusy = false;
            if (DateTime.Now - _dtActionStart > TimeSpan.FromSeconds(RobotCommandTimeout))
            {
                OnError("Command execution timeout");
                return true;
            }
            try
            {
                SetDoState((int)RobotStepEnum.ActionStep1, _doPickCmd, false, Notify);
                SetDoState((int)RobotStepEnum.ActionStep2, _doPlaceCmd, false, Notify);
                SetDoState((int)RobotStepEnum.ActionStep3, _doHomeCmd, false, Notify);
                SetDoState((int)RobotStepEnum.ActionStep4, _doSaveCmd, false, Notify);
                SetDoState((int)RobotStepEnum.ActionStep5, _doX_FwdCmd, false, Notify);
                SetDoState((int)RobotStepEnum.ActionStep6, _doX_RevCmd, false, Notify);
                SetDoState((int)RobotStepEnum.ActionStep7, _doY_FwdCmd, false, Notify);
                SetDoState((int)RobotStepEnum.ActionStep8, _doY_RevCmd, false, Notify);
                SetDoState((int)RobotStepEnum.ActionStep9, _doZ_FwdCmd, false, Notify);
                SetDoState((int)RobotStepEnum.ActionStep10, _doZ_RevCmd, false, Notify);

                SetDoState((int)RobotStepEnum.ActionStep11, _doResetCmd, true, Notify);
                WaitDiState((int)RobotStepEnum.ActionStep12, RobotCommandTimeout, _diReset_Ack, true, Notify, Stop);
                SetDoState((int)RobotStepEnum.ActionStep13, _doResetCmd, false, Notify);
                WaitDiState((int)RobotStepEnum.ActionStep14, RobotCommandTimeout, _diReset_Ack, false, Notify, Stop);

            }
            catch (RoutineBreakException)
            {
                return false;
            }
            catch (RoutineFaildException)
            {
                EV.PostAlarmLog("Alarm", "Pick wafer failed.");
                OnError("PickFailed");
                return true;
            }
            return true;
        }



        protected override bool fStartInit(object[] param)
        {
            _dtActionStart = DateTime.Now;
            ResetRoutine();
            return true;
        }
        protected override bool fMonitorInit(object[] param)
        {
            IsBusy = false;
            if (DateTime.Now - _dtActionStart > TimeSpan.FromSeconds(RobotCommandTimeout))
            {
                OnError("Command execution timeout");
                return true;
            }
            try
            {
                SetDoState((int)RobotStepEnum.ActionStep1, _doPickCmd, false, Notify);
                SetDoState((int)RobotStepEnum.ActionStep2, _doPlaceCmd, false, Notify);
                SetDoState((int)RobotStepEnum.ActionStep3, _doResetCmd, false, Notify);
                SetDoState((int)RobotStepEnum.ActionStep4, _doSaveCmd, false, Notify);
                SetDoState((int)RobotStepEnum.ActionStep5, _doX_FwdCmd, false, Notify);
                SetDoState((int)RobotStepEnum.ActionStep6, _doX_RevCmd, false, Notify);
                SetDoState((int)RobotStepEnum.ActionStep7, _doY_FwdCmd, false, Notify);
                SetDoState((int)RobotStepEnum.ActionStep8, _doY_RevCmd, false, Notify);
                SetDoState((int)RobotStepEnum.ActionStep9, _doZ_FwdCmd, false, Notify);
                SetDoState((int)RobotStepEnum.ActionStep10, _doZ_RevCmd, false, Notify);



                SetDoState((int)RobotStepEnum.ActionStep11, _doHomeCmd, true, Notify);
                WaitDiState((int)RobotStepEnum.ActionStep12, RobotCommandTimeout, _diHome_Ack, true, Notify, Stop);
                SetDoState((int)RobotStepEnum.ActionStep13, _doHomeCmd, false, Notify);
                WaitDiState((int)RobotStepEnum.ActionStep14, RobotCommandTimeout, _diHome_Ack, false, Notify, Stop);

            }
            catch (RoutineBreakException)
            {
                return false;
            }
            catch (RoutineFaildException)
            {
                EV.PostAlarmLog("Alarm", "Pick wafer failed.");
                OnError("PickFailed");
                return true;
            }

            Blade1Target = ModuleName.System;
            Blade2Target = ModuleName.System;
            CmdTarget = ModuleName.System;
            MoveInfo = new RobotMoveInfo()
            {
                Action = RobotAction.Picking,
                ArmTarget = CmdRobotArm == RobotArmEnum.Lower ? RobotArm.ArmA : RobotArm.ArmB,
                BladeTarget = BuildBladeTarget(),
            };



            return true;
        }

        protected override bool fStartExecuteCommand(object[] param)
        {
            CurrentExecuteCmd = param[0].ToString();
            if (string.IsNullOrEmpty(CurrentExecuteCmd))
                return false;

            _dtActionStart = DateTime.Now;
            ResetRoutine();


            return true;
            
        }

        public string CurrentExecuteCmd { get; private set; }

        protected override bool fMonitorExecuting(object[] param)
        {
            string command = CurrentParamter[0].ToString();
            Int32 pnum = Convert.ToInt32(CurrentParamter[1]);
            short high16bit = (short)(pnum >> 16);
            short low16bit = (short)(pnum & ushort.MaxValue);

            try
            {
                switch (command)
                {
                    case "XMoveFwd":
                        SetAoValue((int)RobotStepEnum.ActionStep1, _aoXMoveFwdDistance_H, high16bit, Notify);
                        SetAoValue((int)RobotStepEnum.ActionStep2, _aoXMoveFwdDistance_L, low16bit, Notify);
                        SetDoState((int)RobotStepEnum.ActionStep3, _doX_FwdCmd, true, Notify);
                        WaitDiState((int)RobotStepEnum.ActionStep4, RobotCommandTimeout, _diX_Fwd_Ack, true, Notify,Stop);
                        SetDoState((int)RobotStepEnum.ActionStep5, _doX_FwdCmd, false, Notify);
                        WaitDiState((int)RobotStepEnum.ActionStep6, RobotCommandTimeout, _diX_Fwd_Ack, false, Notify, Stop);
                        SetAoValue((int)RobotStepEnum.ActionStep7, _aoXMoveFwdDistance_H, 0, Notify);
                        SetAoValue((int)RobotStepEnum.ActionStep8, _aoXMoveFwdDistance_L, 0, Notify);

                        break;
                    case "XMoveRev":
                        high16bit = (short)((-1*pnum) >> 16);
                        low16bit = (short)((-1 * pnum) & ushort.MaxValue);

                        SetAoValue((int)RobotStepEnum.ActionStep1, _aoXMoveRevDistance_H, high16bit, Notify);
                        SetAoValue((int)RobotStepEnum.ActionStep2, _aoXMoveRevDistance_L, low16bit, Notify);
                        SetDoState((int)RobotStepEnum.ActionStep3, _doX_RevCmd, true, Notify);
                        WaitDiState((int)RobotStepEnum.ActionStep4, RobotCommandTimeout, _diX_Rev_Ack, true, Notify, Stop);
                        SetDoState((int)RobotStepEnum.ActionStep5, _doX_RevCmd, false, Notify);
                        WaitDiState((int)RobotStepEnum.ActionStep6, RobotCommandTimeout, _diX_Rev_Ack, false, Notify, Stop);
                        SetAoValue((int)RobotStepEnum.ActionStep7, _aoXMoveRevDistance_H, 0, Notify);
                        SetAoValue((int)RobotStepEnum.ActionStep8, _aoXMoveRevDistance_L, 0, Notify);
                        break;
                    case "YMoveFwd":
                        SetAoValue((int)RobotStepEnum.ActionStep1, _aoYMoveFwdDistance_H, high16bit, Notify);
                        SetAoValue((int)RobotStepEnum.ActionStep2, _aoYMoveFwdDistance_L, low16bit, Notify);
                        SetDoState((int)RobotStepEnum.ActionStep3, _doY_FwdCmd, true, Notify);
                        WaitDiState((int)RobotStepEnum.ActionStep4, RobotCommandTimeout, _diY_Fwd_Ack, true, Notify, Stop);
                        SetDoState((int)RobotStepEnum.ActionStep5, _doY_FwdCmd, false, Notify);
                        WaitDiState((int)RobotStepEnum.ActionStep6, RobotCommandTimeout, _diY_Fwd_Ack, false, Notify, Stop);
                        SetAoValue((int)RobotStepEnum.ActionStep7, _aoYMoveFwdDistance_H, 0, Notify);
                        SetAoValue((int)RobotStepEnum.ActionStep8, _aoYMoveFwdDistance_L, 0, Notify);
                        break;
                    case "YMoveRev":
                        high16bit = (short)((-1 * pnum) >> 16);
                        low16bit = (short)((-1 * pnum) & ushort.MaxValue);
                        SetAoValue((int)RobotStepEnum.ActionStep1, _aoYMoveRevDistance_H, high16bit, Notify);
                        SetAoValue((int)RobotStepEnum.ActionStep2, _aoYMoveRevDistance_L, low16bit, Notify);
                        SetDoState((int)RobotStepEnum.ActionStep3, _doY_RevCmd, true, Notify);
                        WaitDiState((int)RobotStepEnum.ActionStep4, RobotCommandTimeout, _diY_Rev_Ack, true, Notify, Stop);
                        SetDoState((int)RobotStepEnum.ActionStep5, _doY_RevCmd, false, Notify);
                        WaitDiState((int)RobotStepEnum.ActionStep6, RobotCommandTimeout, _diY_Rev_Ack, false, Notify, Stop);
                        SetAoValue((int)RobotStepEnum.ActionStep7, _aoYMoveRevDistance_H, 0, Notify);
                        SetAoValue((int)RobotStepEnum.ActionStep8, _aoYMoveRevDistance_L, 0, Notify);
                        break;
                    case "ZMoveFwd":
                        SetAoValue((int)RobotStepEnum.ActionStep1, _aoZMoveFwdDistance_H, high16bit, Notify);
                        SetAoValue((int)RobotStepEnum.ActionStep2, _aoZMoveFwdDistance_L, low16bit, Notify);
                        SetDoState((int)RobotStepEnum.ActionStep3, _doZ_FwdCmd, true, Notify);
                        WaitDiState((int)RobotStepEnum.ActionStep4, RobotCommandTimeout, _diZ_Fwd_Ack, true, Notify, Stop);
                        SetDoState((int)RobotStepEnum.ActionStep5, _doZ_FwdCmd, false, Notify);
                        WaitDiState((int)RobotStepEnum.ActionStep6, RobotCommandTimeout, _diZ_Fwd_Ack, false, Notify, Stop);
                        SetAoValue((int)RobotStepEnum.ActionStep7, _aoZMoveFwdDistance_H, 0, Notify);
                        SetAoValue((int)RobotStepEnum.ActionStep8, _aoZMoveFwdDistance_L, 0, Notify);
                        break;
                    case "ZMoveRev":
                        high16bit = (short)((-1 * pnum) >> 16);
                        low16bit = (short)((-1 * pnum) & ushort.MaxValue);
                        SetAoValue((int)RobotStepEnum.ActionStep1, _aoZMoveRevDistance_H, high16bit, Notify);
                        SetAoValue((int)RobotStepEnum.ActionStep2, _aoZMoveRevDistance_L, low16bit, Notify);
                        SetDoState((int)RobotStepEnum.ActionStep3, _doZ_RevCmd, true, Notify);
                        WaitDiState((int)RobotStepEnum.ActionStep4, RobotCommandTimeout, _diZ_Rev_Ack, true, Notify, Stop);
                        SetDoState((int)RobotStepEnum.ActionStep5, _doZ_RevCmd, false, Notify);
                        WaitDiState((int)RobotStepEnum.ActionStep6, RobotCommandTimeout, _diZ_Rev_Ack, false, Notify, Stop);
                        SetAoValue((int)RobotStepEnum.ActionStep7, _aoZMoveRevDistance_H, 0, Notify);
                        SetAoValue((int)RobotStepEnum.ActionStep8, _aoZMoveRevDistance_L, 0, Notify);
                        break;
                    default:
                        break;


                }
            }
            catch (RoutineBreakException)
            {
                return false;
            }
            catch (RoutineFaildException)
            {
                EV.PostAlarmLog("Alarm", $"Execute command:{command} failed.");
                OnError($"Execute {command} failed");
                return true;
            }
            return true;
        }

        protected override bool fError(object[] param)
        {
            return true;
        }

        protected override bool fStartExtendForPick(object[] param)
        {
            return true;
        }

        protected override bool fStartExtendForPlace(object[] param)
        {
            return true;
        }

        protected override bool fStartRetractFromPick(object[] param)
        {
            return true;
        }

        protected override bool fStartRetractFromPlace(object[] param)
        {
            return true;
        }

        public override RobotArmWaferStateEnum GetWaferState(RobotArmEnum arm)
        {
            if (WaferManager.Instance.CheckHasWafer(RobotModuleName, 0))
                return RobotArmWaferStateEnum.Present;
            return RobotArmWaferStateEnum.Absent;

        }

        private string BuildBladeTarget()
        {
            return (CmdRobotArm == RobotArmEnum.Upper ? "ArmB" : "ArmA") + "." + CmdTarget;
        }

        private static int TwoShortConvInt(float low16bit, float high16bit)
        {
            var lowShort = (short)low16bit;
            var highShort = (short)high16bit;
            return (highShort << 16) + lowShort + (lowShort < 0 ? ushort.MaxValue + 1 : 0);
        }

        private static void IntConvTwoShort(int num,ref short low16bit,ref short high16bit)
        {
            high16bit = (short)(num >> 16);
            low16bit = (short)(num & ushort.MaxValue);
        }

        #endregion















        public DOAccessor ParseDoNode(string name, XmlElement node, string ioModule = "")
        {
            if (!string.IsNullOrEmpty(node.GetAttribute(name).Trim()))
                return IO.DO[string.IsNullOrEmpty(ioModule) ? node.GetAttribute(name).Trim() : $"{ioModule}.{node.GetAttribute(name).Trim()}"];

            return null;
        }

        public DIAccessor ParseDiNode(string name, XmlElement node, string ioModule = "")
        {
            if (!string.IsNullOrEmpty(node.GetAttribute(name).Trim()))
                return IO.DI[string.IsNullOrEmpty(ioModule) ? node.GetAttribute(name).Trim() : $"{ioModule}.{node.GetAttribute(name).Trim()}"];
            return null;
        }

        public AOAccessor ParseAoNode(string name, XmlElement node, string ioModule = "")
        {
            if (!string.IsNullOrEmpty(node.GetAttribute(name).Trim()))
                return IO.AO[string.IsNullOrEmpty(ioModule) ? node.GetAttribute(name).Trim() : $"{ioModule}.{node.GetAttribute(name).Trim()}"];
            return null;
        }

        public AIAccessor ParseAiNode(string name, XmlElement node, string ioModule = "")
        {
            if (!string.IsNullOrEmpty(node.GetAttribute(name).Trim()))
                return IO.AI[string.IsNullOrEmpty(ioModule) ? node.GetAttribute(name).Trim() : $"{ioModule}.{node.GetAttribute(name).Trim()}"];
            return null;
        }
        public SCConfigItem ParseScNode(string name, XmlElement node, string ioModule = "", string defaultScPath = "")
        {
            SCConfigItem result = null;

            if (!string.IsNullOrEmpty(node.GetAttribute(name).Trim()))
                result = SC.GetConfigItem(node.GetAttribute(name));

            if (result == null && !string.IsNullOrEmpty(defaultScPath) && SC.ContainsItem(defaultScPath))
                result = SC.GetConfigItem(defaultScPath);

            return result;
        }

        public static T ParseDeviceNode<T>(string name, XmlElement node) where T : class, IDevice
        {
            if (!string.IsNullOrEmpty(node.GetAttribute(name).Trim()))
                return DEVICE.GetDevice<T>(node.GetAttribute(name));
            LOG.Write(string.Format("{0}，未定义{1}", node.InnerXml, name));
            return null;
        }

        public static T ParseDeviceNode<T>(string module, string name, XmlElement node) where T : class, IDevice
        {
            string device_id = node.GetAttribute(name);
            if (!string.IsNullOrEmpty(device_id) && !string.IsNullOrEmpty(device_id.Trim()))
            {
                return DEVICE.GetDevice<T>($"{module}.{device_id}");
            }
            LOG.Write(string.Format("{0},undefined {1}", node.InnerXml, name));
            return null;
        }
        public enum CarrierMode
        {
            Foup,
            Fosb,
            OpenCassette,
        }

        private void SetAoValue(int id, AOAccessor ao, short value, Action<string> notify)
        {
            var ret = Execute(id, () =>
            {
                notify($"{RobotModuleName} set {ao.Name} to {value}.");
                ao.Value = value;
                return true;
            });

            if (ret.Item1)
            {
                if (ret.Item2 == Result.FAIL)
                {
                    throw new RoutineFaildException();
                }
            }
        }
        private void WaitAiValue(int id, int time, AIAccessor ai, float value, Action<string> notify, Action<string> error)
        {
            var ret = ExecuteAndWait(id, () =>
            {
                notify($"Wait {RobotModuleName} {ai.Name} to be {value}");
                return true;
            }, () =>
            {
                if (ai.Value == value)
                    return true;

                return false;
            }, time * 1000);

            if (ret.Item1)
            {
                if (ret.Item2 == Result.FAIL)
                {
                    throw new RoutineFaildException();
                }
                else if (ret.Item2 == Result.TIMEOUT) //timeout
                {
                    //EV.Notify(RobotModuleName);
                    error($"Wait {RobotModuleName} {ai.Name} to be {value} timeout after {time} seconds");
                    throw new RoutineFaildException();
                }
                else
                {
                    throw new RoutineBreakException();
                }
            }
        }
        private void WaitDiState(int id, int time, DIAccessor di, bool state, Action<string> notify, Action<string> error)
        {
            var ret = ExecuteAndWait(id, () =>
            {
                notify($"Wait {RobotModuleName} {di.Name} to be {state}");
                return true;
            }, () =>
            {
                if (di.Value == state)
                    return true;

                return false;
            }, time * 1000);

            if (ret.Item1)
            {
                if (ret.Item2 == Result.FAIL)
                {
                    throw new RoutineFaildException();
                }
                else if (ret.Item2 == Result.TIMEOUT) //timeout
                {
                    //EV.Notify();
                    error($"Wait {RobotModuleName} {di.Name} to be {state} timeout after {time} seconds");
                    throw new RoutineFaildException();
                }
                else
                {
                    throw new RoutineBreakException();
                }
            }
        }
        private void SetDoState(int id, DOAccessor _do, bool state, Action<string> notify)
        {
            var ret = Execute(id, () =>
            {
                notify($"{RobotModuleName} start set {_do.Name} to {state}.");
                //if (_do.Value == state)
                //{
                //    _do.Value = !state;
                //    Thread.Sleep(500);
                //}
                return _do.SetValue(state, out _);
            });

            if (ret.Item1)
            {
                if (ret.Item2 == Result.FAIL)
                {
                    throw new RoutineFaildException();
                }
            }
        }





        protected void Notify(string message)
        {
            EV.PostMessage(Name, EventEnum.GeneralInfo, string.Format("{0}:{1}", Name, message));
        }
        protected void Stop(string failReason)
        {
            OnError(string.Format("Failed {0}, {1} ", Name, failReason));
        }



        #region Routine Executor
        //timer, 计算routine时间
        protected DeviceTimer counter = new DeviceTimer();
        protected DeviceTimer delayTimer = new DeviceTimer();

        private enum STATE
        {
            IDLE,
            WAIT,
        }

        public int TokenId
        {
            get { return _id; }
        }
        private int _id;         //step index

        /// <summary>
        /// already done steps
        /// </summary>
        private Stack<int> _steps = new Stack<int>();

        private STATE state;    //step state //idel,wait,

        //loop control
        private int loop = 0;
        private int loopCount = 0;
        private int loopID = 0;

        private DeviceTimer timer = new DeviceTimer();

        public int LoopCounter { get { return loop; } }
        public int LoopTotalTime { get { return loopCount; } }

        // public int Timeout { get { return (int)(timer.GetTotalTime() / 1000); } }

        //状态持续时间，单位为秒
        public int Elapsed { get { return (int)(timer.GetElapseTime() / 1000); } }

        protected RoutineResult RoutineToken = new RoutineResult() { Result = RoutineState.Running };

        public void ResetRoutine()
        {
            _id = 0;
            _steps.Clear();

            loop = 0;
            loopCount = 0;

            state = STATE.IDLE;
            counter.Start(60 * 60 * 100);   //默认1小时

            RoutineToken.Result = RoutineState.Running;
        }
        protected void PerformRoutineStep(int id, Func<RoutineState> execution, RoutineResult result)
        {
            if (!Acitve(id))
                return;

            result.Result = execution();
        }



        #region interface

        public void StopLoop()
        {
            loop = loopCount;
        }

        public Tuple<bool, Result> Loop<T>(T id, Func<bool> func, int count)
        {
            int idx = Convert.ToInt32(id);
            bool bActive = Acitve(idx);

            if (bActive)
            {
                if (!func())
                {
                    return Tuple.Create(bActive, Result.FAIL);   //执行错误
                }

                loopID = idx;
                loopCount = count;

                next();
                return Tuple.Create(true, Result.RUN);
            }

            return Tuple.Create(false, Result.RUN);
        }

        public Tuple<bool, Result> EndLoop<T>(T id, Func<bool> func)
        {
            int idx = Convert.ToInt32(id);
            bool bActive = Acitve(idx);

            if (bActive)
            {
                if (++loop >= loopCount)   //Loop 结束
                {
                    if (!func())
                    {
                        return Tuple.Create(bActive, Result.FAIL);   //执行错误
                    }

                    loop = 0;
                    loopCount = 0;  // Loop 结束时，当前loop和loop总数都清零

                    next();
                    return Tuple.Create(true, Result.RUN);
                }

                //继续下一LOOP

                next(loopID);

                return Tuple.Create(true, Result.RUN);
            }

            return Tuple.Create(false, Result.RUN);
        }

        public Tuple<bool, Result> ExecuteAndWait<T>(T id, IRoutine routine)
        {
            int idx = Convert.ToInt32(id);
            bool bActive = Acitve(idx);

            if (bActive)
            {
                if (state == STATE.IDLE)
                {
                    Result startRet = routine.Start();
                    if (startRet == Result.FAIL)
                    {
                        return Tuple.Create(true, Result.FAIL);   //执行错误
                    }
                    else if (startRet == Result.DONE)
                    {
                        next();
                        return Tuple.Create(true, Result.DONE);
                    }
                    state = STATE.WAIT;
                }

                Result ret = routine.Monitor();

                if (ret == Result.DONE)
                {
                    next();
                    return Tuple.Create(true, Result.DONE);
                }
                else if (ret == Result.FAIL || ret == Result.TIMEOUT)
                {
                    return Tuple.Create(true, Result.FAIL);
                }
                else
                {
                    return Tuple.Create(true, Result.RUN);
                }

            }

            return Tuple.Create(false, Result.RUN);
        }


        public Tuple<bool, Result> ExecuteAndWait<T>(T id, List<IRoutine> routines)
        {
            int idx = Convert.ToInt32(id);
            bool bActive = Acitve(idx);

            if (bActive)
            {
                if (state == STATE.IDLE)
                {
                    foreach (var item in routines)
                    {
                        if (item.Start() == Result.FAIL)
                            return Tuple.Create(true, Result.FAIL);
                    }

                    state = STATE.WAIT;
                }
                //wait all sub failed or completedboo

                bool bFail = false;
                bool bDone = true;

                foreach (var item in routines)
                {
                    Result ret = item.Monitor();

                    bDone &= (ret == Result.FAIL || ret == Result.DONE);
                    bFail |= ret == Result.FAIL;
                }

                if (bDone)
                {
                    next();

                    if (bFail)
                        return Tuple.Create(true, Result.FAIL);

                    return Tuple.Create(true, Result.DONE);
                }
                return Tuple.Create(true, Result.RUN);
            }

            return Tuple.Create(false, Result.RUN);
        }



        public Tuple<bool, Result> Check<T>(T id, Func<bool> func)   //顺序执行
        {
            return Check(Check(Convert.ToInt32(id), func));
        }

        public Tuple<bool, Result> Execute<T>(T id, Func<bool> func)   //顺序执行
        {
            return Check(execute(Convert.ToInt32(id), func));
        }

        public Tuple<bool, Result> Wait<T>(T id, Func<bool> func, double timeout = int.MaxValue)  //Wait condition
        {
            return Check(wait(Convert.ToInt32(id), func, timeout));
        }

        public Tuple<bool, Result> Wait<T>(T id, Func<bool?> func, double timeout = int.MaxValue)  //Wait condition
        {
            return Check(wait(Convert.ToInt32(id), func, timeout));
        }

        public Tuple<bool, Result> ExecuteAndWait<T>(T id, Func<bool> execute, Func<bool?> check, double timeout = int.MaxValue)
        {
            int idx = Convert.ToInt32(id);
            bool bActive = Acitve(idx);
            bool? bExecute = false;

            if (bActive)
            {
                if (state == STATE.IDLE)
                {
                    if (!execute())
                    {
                        return Tuple.Create(bActive, Result.FAIL);   //执行错误
                    }
                    timer.Start(timeout);
                    state = STATE.WAIT;
                }

                bExecute = check();

                if (bExecute == null)
                {
                    return Tuple.Create(bActive, Result.FAIL);    //Termianate
                }
                else
                {
                    if (bExecute.Value)       //检查Success, next
                    {
                        next();
                        return Tuple.Create(true, Result.RUN);
                    }
                }

                if (timer.IsTimeout())
                    return Tuple.Create(true, Result.TIMEOUT);

                return Tuple.Create(true, Result.RUN);
            }

            return Tuple.Create(false, Result.RUN);
        }

        public Tuple<bool, Result> ExecuteAndWait<T>(T id, Func<bool> execute, Func<bool?> check, Func<double> time)
        {
            int idx = Convert.ToInt32(id);
            bool bActive = Acitve(idx);
            bool? bExecute = false;
            double timeout = 0;
            if (bActive)
            {
                if (state == STATE.IDLE)
                {
                    timeout = time();
                    if (!execute())
                    {
                        return Tuple.Create(true, Result.FAIL);   //执行错误
                    }
                    timer.Start(timeout);
                    state = STATE.WAIT;
                }

                bExecute = check();

                if (bExecute == null)
                {
                    return Tuple.Create(true, Result.FAIL);    //Termianate
                }
                if (bExecute.Value)       //检查Success, next
                {
                    next();
                    return Tuple.Create(true, Result.RUN);
                }

                if (timer.IsTimeout())
                    return Tuple.Create(true, Result.TIMEOUT);

                return Tuple.Create(true, Result.RUN);
            }

            return Tuple.Create(false, Result.RUN);
        }

        public Tuple<bool, Result> Wait<T>(T id, IRoutine rt)
        {
            int idx = Convert.ToInt32(id);
            bool bActive = Acitve(idx);

            if (bActive)
            {
                if (state == STATE.IDLE)
                {
                    rt.Start();
                    state = STATE.WAIT;
                }

                Result ret = rt.Monitor();

                return Tuple.Create(true, ret);
            }

            return Tuple.Create(false, Result.RUN);
        }

        //Monitor
        public Tuple<bool, Result> Monitor<T>(T id, Func<bool> func, Func<bool> check, double time)
        {
            int idx = Convert.ToInt32(id);
            bool bActive = Acitve(idx);
            bool bCheck = false;
            if (bActive)
            {
                if (state == STATE.IDLE)
                {
                    if ((func != null) && !func())
                    {
                        return Tuple.Create(true, Result.FAIL);
                    }

                    timer.Start(time);
                    state = STATE.WAIT;
                }

                bCheck = check();

                if (!bCheck)
                {
                    return Tuple.Create(true, Result.FAIL);    //Termianate
                }

                if (timer.IsTimeout())
                {
                    next();
                }
                return Tuple.Create(true, Result.RUN);
            }

            return Tuple.Create(false, Result.RUN);
        }

        //Delay
        public Tuple<bool, Result> Delay<T>(T id, Func<bool> func, double time)
        {
            int idx = Convert.ToInt32(id);
            bool bActive = Acitve(idx);
            if (bActive)
            {
                if (state == STATE.IDLE)
                {
                    if ((func != null) && !func())
                    {
                        return Tuple.Create(true, Result.FAIL);
                    }

                    timer.Start(time);
                    state = STATE.WAIT;
                }

                if (timer.IsTimeout())
                {
                    next();
                }
                return Tuple.Create(true, Result.RUN);
            }

            return Tuple.Create(false, Result.RUN);
        }

        //先delay 再运行
        public Tuple<bool, Result> DelayCheck<T>(T id, Func<bool> func, double time)
        {
            int idx = Convert.ToInt32(id);
            bool bActive = Acitve(idx);
            if (bActive)
            {
                if (state == STATE.IDLE)
                {
                    timer.Start(time);
                    state = STATE.WAIT;
                }

                if (timer.IsTimeout())
                {
                    if (func != null && !func())
                    {
                        return Tuple.Create(true, Result.FAIL);
                    }
                    next();
                }
                return Tuple.Create(true, Result.RUN);
            }

            return Tuple.Create(false, Result.RUN);
        }
        #endregion


        private Tuple<bool, bool> execute(int id, Func<bool> func)   //顺序执行
        {
            bool bActive = Acitve(id);
            bool bExecute = false;
            if (bActive)
            {
                bExecute = func();
                if (bExecute)
                {
                    next();
                }
            }

            return Tuple.Create(bActive, bExecute);
        }


        private Tuple<bool, bool> Check(int id, Func<bool> func)   //check
        {
            bool bActive = Acitve(id);
            bool bExecute = false;
            if (bActive)
            {
                bExecute = func();
                next();
            }

            return Tuple.Create(bActive, bExecute);
        }


        /// <summary>

        /// </summary>
        /// <param name="id"></param>
        /// <param name="func"></param>
        /// <param name="timeout"></param>
        /// <returns>
        ///  item1 Active
        ///  item2 execute
        ///  item3 Timeout
        ///</returns>

        private Tuple<bool, bool, bool> wait(int id, Func<bool> func, double timeout = int.MaxValue)  //Wait condition
        {
            bool bActive = Acitve(id);
            bool bExecute = false;
            bool bTimeout = false;

            if (bActive)
            {
                if (state == STATE.IDLE)
                {
                    timer.Start(timeout);
                    state = STATE.WAIT;
                }

                bExecute = func();
                if (bExecute)
                {
                    next();
                }

                bTimeout = timer.IsTimeout();
            }

            return Tuple.Create(bActive, bExecute, bTimeout);
        }

        private Tuple<bool, bool?, bool> wait(int id, Func<bool?> func, double timeout = int.MaxValue)  //Wait condition && Check error
        {
            bool bActive = Acitve(id);
            bool? bExecute = false;
            bool bTimeout = false;

            if (bActive)
            {
                if (state == STATE.IDLE)
                {
                    timer.Start(timeout);
                    state = STATE.WAIT;
                }

                bExecute = func();
                if (bExecute.HasValue && bExecute.Value)
                {
                    next();
                }

                bTimeout = timer.IsTimeout();
            }

            return Tuple.Create(bActive, bExecute, bTimeout);
        }

        /// <summary>      
        /// </summary>
        /// <param name="value"></param>
        /// <returns>
        /// item1 true, return item2
        /// </returns>
        private Tuple<bool, Result> Check(Tuple<bool, bool> value)
        {
            if (value.Item1)
            {
                if (!value.Item2)
                {
                    return Tuple.Create(true, Result.FAIL);
                }

                return Tuple.Create(true, Result.RUN);
            }

            return Tuple.Create(false, Result.RUN);
        }

        private Tuple<bool, Result> Check(Tuple<bool, bool, bool> value)
        {
            if (value.Item1)   // 当前执行
            {
                if (CheckTimeout(value))  //timeout
                {
                    return Tuple.Create(true, Result.TIMEOUT);
                }
                return Tuple.Create(true, Result.RUN);
            }

            return Tuple.Create(false, Result.RUN);
        }

        private Tuple<bool, Result> Check(Tuple<bool, bool?, bool> value)
        {
            if (value.Item1)   // 当前执行
            {
                if (value.Item2 == null)
                {
                    return Tuple.Create(true, Result.FAIL);
                }
                else
                {
                    if (value.Item2 == false && value.Item3 == true)  //timeout
                    {
                        return Tuple.Create(true, Result.TIMEOUT);
                    }
                    return Tuple.Create(true, Result.RUN);
                }
            }

            return Tuple.Create(false, Result.RUN);
        }

        private bool CheckTimeout(Tuple<bool, bool, bool> value)
        {
            return value.Item1 == true && value.Item2 == false && value.Item3 == true;
        }

        private bool Acitve(int id) //
        {
            if (_steps.Contains(id))
                return false;

            this._id = id;
            return true;
        }

        private void next()
        {
            _steps.Push(this._id);
            state = STATE.IDLE;
        }

        private void next(int step)   //loop
        {
            while (_steps.Pop() != step) ;

            state = STATE.IDLE;
        }


        public void Delay(int id, double delaySeconds)
        {
            Tuple<bool, Result> ret = Delay(id, () =>
            {
                return true;
            }, delaySeconds * 1000);

            if (ret.Item1)
            {
                if (ret.Item2 == Result.RUN)
                {
                    throw (new RoutineBreakException());
                }
            }
        }



        public bool IsActived(int id)
        {
            return _steps.Contains(id);
        }

       
        #endregion

    }
}
