using Aitex.Common.Util;
using Aitex.Core.RT.Log;
using Aitex.Core.Util;
using MECF.Framework.Common.Beckhoff.AxisProvider;
using MECF.Framework.Common.Device.Galil;
using System;
using System.Collections.Generic;
using System.IO;

namespace MECF.Framework.Common.Simulator
{
    
    /// <summary>
    /// 电机运动模拟器
    /// </summary>
    public class MotorSimulator : Singleton<MotorSimulator>
    {
        
        #region 常量
        private const string TARGET_VELOCITY = "TargetVelocity";
        private const string TARGET_ACCEL = "TargetAcceleration";
        private const string TARGET_DECEL = "TargetDeceleration";
        private const string TARGET_POSITION = "TargetPosition";
        private const string SWITCH_SIGNAL = "SwitchSignal";
        private const string ACTUAL_POSITION = "ActualPosition";
        private const string AUXILIARY_POSITION = "AuxiliaryPosition";
        private const string HOMING_SIGNAL = "HomingSignal";
        private const string MOTION_SIGNAL = "MotionSignal";
        private const string STOP_SIGNAL = "StopSignal";
        /// <summary>
        /// 定时器间隔(ms)
        /// </summary>
        private const int TIMER_INTERVAL = 50;
        /// <summary>
        /// motor step factor
        /// </summary>
        private const int MOTOR_STEP_FACTOR = 10;
        #endregion

        #region 内部变量
        /// <summary>
        /// 定时器
        /// </summary>
        private PeriodicJob _periodicJob;      
        /// <summary>
        /// 电机数据字典(key:Name(module.name),value:Datas)
        /// </summary>
        private Dictionary<string, SimulatorMotionData> _motorNameDataDic = new Dictionary<string, SimulatorMotionData>();
        /// <summary>
        /// Key:moduleName, Value:minStep
        /// </summary>
        private Dictionary<string, int> _motorNameMinStepDic = new Dictionary<string, int>();
        #endregion

        #region 属性

        #endregion

        //delegate
        #region Delegate
        public delegate void UpdateVariableValueChanged(Dictionary<string, SimulatorMotionData> datasDic);
        #endregion

        #region 事件
        /// <summary>
        /// 变量变更事件
        /// </summary>
        public event UpdateVariableValueChanged OnUpdateVariableValueChanged;
        #endregion
        /// <summary>
        /// 初始化
        /// </summary>
        public void Initialize()
        {
            _periodicJob = new PeriodicJob(TIMER_INTERVAL, OnTimer, "Motor Simulator Timer", true);
            Init();
        }
        /// <summary>
        /// 初始化数据
        /// </summary>
        private void Init()
        {
            //加载对应配置文件 GalilControllerCfg-Simulator.xml,初始化数据字典
            try
            {
                string oldXmlPath = PathManager.GetCfgDir();
                string newXmlPath = oldXmlPath.Replace("CyberX8_Simulator", "CyberX8_RT") + "Devices\\GalilControllerCfg-Simulator.xml";
                GalilControllerCfg cfg = CustomXmlSerializer.Deserialize<GalilControllerCfg>(new FileInfo(newXmlPath));
                if (cfg != null)
                {
                    foreach (GalilDeviceConfig config in cfg.GalilDeviceConfigs)
                    {
                        foreach (GalilAxisConfig item in config.GalilAxises)
                        {
                            _motorNameDataDic[$"{config.Module}.{item.Name}"] = new SimulatorMotionData();
                            _motorNameDataDic[$"{config.Module}.{item.Name}"].FwdSoftLimit = item.ForwardSoftwareLimit;
                            _motorNameDataDic[$"{config.Module}.{item.Name}"].RevSoftLimit = item.ReverseSoftwareLimit;
                            _motorNameDataDic[$"{config.Module}.{item.Name}"].NegativeTorqueLimit = item.NegativeTorqueLimit;
                            _motorNameDataDic[$"{config.Module}.{item.Name}"].PositiveTorqueLimit = item.PositiveTorqueLimit;
                            _motorNameDataDic[$"{config.Module}.{item.Name}"].SwitchSignal = true;
                        }
                    }
                }
            }
            catch
            {
                LOG.WriteLog(eEvent.ERR_GALIL, "Galil", "Load galil GalilControllerCfg-Simulator.xml failed");
            }                  
            
            //加载AxisProviderCfg.xml
            try
            {
                string oldXmlPath = PathManager.GetCfgDir();
                string newXmlPath = oldXmlPath.Replace("CyberX8_Simulator", "CyberX8_RT") + "Devices\\AxisProviderCfg.xml";
                BeckhoffAxisProviderCfg axisProviderCfg = CustomXmlSerializer.Deserialize<BeckhoffAxisProviderCfg>(new FileInfo(newXmlPath));
                if (axisProviderCfg != null)
                {
                    foreach (BeckhoffProviderAxis item in axisProviderCfg.Axes)
                    {
                        double value = item.ScaleFactor / 30;
                        _motorNameMinStepDic[item.Name] = (int)((value < 100) ? 100 : value);                        
                    }
                }
            }
            catch
            {
                LOG.WriteLog(eEvent.ERR_AXIS, "axisProvider", "Load AxisProviderCfg.xml failed");
            }
        }
        /// <summary>
        /// 定时器执行
        /// </summary>
        /// <returns></returns>
        private bool OnTimer()
        {
            //电机运动模型
            foreach(var motorItem in _motorNameDataDic)
            {
                //对应电机进行模拟
                MotorMotionSimulator(motorItem);
                //实时更新电机数据
                UpdateVariableValue(_motorNameDataDic);
            }
            
            return true;
        }
        /// <summary>
        /// 通知Galil模块数据变化
        /// </summary>
        /// <param name="data"></param>
        private void UpdateVariableValue(Dictionary<string, SimulatorMotionData> datasDic)
        {
            if (OnUpdateVariableValueChanged != null)
            {
                OnUpdateVariableValueChanged(datasDic);
            }
        }
        /// <summary>
        /// 设置电机数据
        /// </summary>
        /// <param name="axisName"></param>
        /// <param name="type"></param>
        /// <param name="value"></param>
        public void SetMotionData(string axisName, string type, object value)
        {
            switch (type)
            {
                case TARGET_VELOCITY:
                    _motorNameDataDic[axisName].TargetVelocity = (int)value;
                    break;
                case TARGET_ACCEL:
                    _motorNameDataDic[axisName].TargetAccel = (int)value;
                    break;
                case TARGET_DECEL:
                    _motorNameDataDic[axisName].TargetDecel = (int)value;
                    break;
                case TARGET_POSITION:
                    _motorNameDataDic[axisName].TargetPosition = (int)value;
                    break;
                case ACTUAL_POSITION:
                    _motorNameDataDic[axisName].ActualPosition = (int)value;
                    break;
                case SWITCH_SIGNAL:
                    _motorNameDataDic[axisName].SwitchSignal = (bool)value;
                    break;
                case STOP_SIGNAL:
                    _motorNameDataDic[axisName].StopSignal = true;
                    break;               
                case HOMING_SIGNAL:
                    _motorNameDataDic[axisName].HomingSignal = true;
                    break;
                case MOTION_SIGNAL:
                    _motorNameDataDic[axisName].MotionSignal = true;
                    break;
                case AUXILIARY_POSITION:
                    //++
                    break;
                default:
                    break;
            }
        }
        /// <summary>
        /// 运动模拟器
        /// </summary>
        private void MotorMotionSimulator(KeyValuePair<string, SimulatorMotionData> motor)
        {
            SimulatorMotionData motionData = motor.Value;
            string name = motor.Key;
            //上电检查
            if (!motionData.SwitchSignal) return;
            if (motionData.HomingSignal)
            {
                HomeOperation(motionData);
            }
            else
            {
                PositionOperation(motionData, name);
            }            
        }
        
        /// <summary>
        /// Home操作
        /// </summary>
        /// <param name="data"></param>
        private void HomeOperation(SimulatorMotionData motionData)
        {
            if (motionData.MotionSignal)
            {
                motionData.StopCode = 10;//HM操作停止码10
                motionData.MotionSignal = false;
                motionData.HomingSignal = false;
                motionData.MotionPhase = MotionPhase.Accelerating;
                //motionData.ActualPosition = 0;
                motionData.ActualVelocity = 0;
            }            
        }
        /// <summary>
        /// GoToPosition操作
        /// </summary>
        /// <param name="data"></param>
        private void PositionOperation(SimulatorMotionData motionData, string name)
        {
            if (motionData.MotionSignal)
            {
                //正向运动
                int motorStep = Math.Abs(motionData.ActualPosition - motionData.TargetPosition);
                //motionData.ActualVelocity = TrapezoidalSpeedControl(motionData);
                motionData.ActualVelocity = motionData.TargetVelocity;
                if (motionData.ActualPosition < motionData.TargetPosition)
                {
                    //motionData.ActualPosition += (motionData.ActualVelocity * TIMER_INTERVAL / 1000);
                    motionData.ActualPosition += ((motorStep / MOTOR_STEP_FACTOR < _motorNameMinStepDic[name]) ? _motorNameMinStepDic[name] : motorStep / MOTOR_STEP_FACTOR);
                    bool fwdLimit = motionData.FwdSoftLimit != 0 ? motionData.ActualPosition >= motionData.FwdSoftLimit : false;
                    if (fwdLimit || motionData.ActualPosition >= motionData.TargetPosition)
                    {
                        motionData.StopCode = 1;//正常运动停止码1
                        motionData.MotionSignal = false;
                        //motionData.MotionPhase = MotionPhase.Accelerating;
                        motionData.ActualPosition = fwdLimit ? motionData.FwdSoftLimit : motionData.TargetPosition;
                        motionData.ActualVelocity = 0;
                    }
                }
                //反向运动
                else if (motionData.ActualPosition > motionData.TargetPosition)
                {
                    motionData.ActualPosition -= ((motorStep / MOTOR_STEP_FACTOR < _motorNameMinStepDic[name]) ? _motorNameMinStepDic[name] : motorStep / MOTOR_STEP_FACTOR);
                    //motionData.ActualPosition -= (motionData.ActualVelocity * TIMER_INTERVAL / 1000);
                    bool revLimit = motionData.RevSoftLimit != 0 ? motionData.ActualPosition <= motionData.RevSoftLimit : false;
                    if (revLimit || motionData.ActualPosition <= motionData.TargetPosition)
                    {
                        motionData.StopCode = 1;//正常运动停止码1
                        motionData.MotionSignal = false;
                        //motionData.MotionPhase = MotionPhase.Accelerating;
                        motionData.ActualPosition = revLimit ? motionData.RevSoftLimit : motionData.TargetPosition;
                        motionData.ActualVelocity = 0;
                    }
                }
            }

            //停止信号
            if (motionData.StopSignal)
            {
                motionData.MotionSignal = false;
                motionData.StopCode = 4;//ST操作停止码4
                motionData.StopSignal = false;
            }
        }
        /// <summary>
        /// 梯形加减速运动控制
        /// </summary>
        private int TrapezoidalSpeedControl(SimulatorMotionData data)
        {
            int speed = 0;
            //制动距离
            int brakeDistance = (data.ActualVelocity * data.ActualVelocity) / (2 * data.TargetDecel);
            int remainingDistance = Math.Abs(data.ActualPosition - data.TargetPosition);
            if (brakeDistance >= remainingDistance)
            {
                data.MotionPhase = MotionPhase.Decelerating;
            }
            else if (data.ActualVelocity < data.TargetVelocity && data.MotionPhase != MotionPhase.Decelerating)
            {
                data.MotionPhase = MotionPhase.Accelerating;
            }
            else
            {
                data.MotionPhase = MotionPhase.ConstantSpeed;
            }

            // 速度更新
            switch (data.MotionPhase)
            {
                case MotionPhase.Accelerating:
                    speed = Math.Min(data.ActualVelocity + data.TargetAccel * TIMER_INTERVAL / 1000, data.TargetVelocity);
                    break;
                case MotionPhase.ConstantSpeed:
                    speed = data.TargetVelocity;
                    break;
                case MotionPhase.Decelerating:
                    //speed = Math.Max(data.ActualVelocity - data.TargetDecel * TIMER_INTERVAL / 1000, 10);
                    speed = Math.Max(data.ActualVelocity / 100, 10);
                    break;
            }
            return speed;
        }

    }
}