using Aitex.Common.Util; using Aitex.Core.RT.Device; using Aitex.Core.RT.Log; using Aitex.Core.RT.Routine; using Aitex.Core.Util; using CyberX8_Core; using MECF.Framework.Common.Beckhoff.AxisProvider; using MECF.Framework.Common.Beckhoff.Station; using MECF.Framework.Common.Device.Galil; using MECF.Framework.Common.Equipment; using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices.ComTypes; namespace MECF.Framework.Common.Simulator { /// /// 电机运动模拟器 /// public class MotorSimulator : Singleton { #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"; private const string FESTO_DATABUFFER_TRANSPORT1_LOCK = "ProcessTransporterLock"; private const string FESTO_DATABUFFER_TRANSPORT2_LOCK = "LoaderTransporterLock"; /// /// 定时器间隔(ms) /// private const int TIMER_INTERVAL = 50; /// /// motor step factor /// private const int MOTOR_STEP_FACTOR = 10; #endregion #region 内部变量 /// /// 定时器 /// private PeriodicJob _periodicJob; /// /// 电机数据字典(key:Name(module.name),value:Datas) /// private Dictionary _motorNameDataDic = new Dictionary(); /// /// Key:moduleName, Value:minStep /// private Dictionary _motorNameMinStepDic = new Dictionary(); /// /// Key:stationName, Value:position /// private Dictionary _stationPositionDic = new Dictionary(); /// /// Key:stationName, Value:Tolerance /// private Dictionary _toleranceDic = new Dictionary(); /// /// Key:stationName, Value:BeckhoffProviderAxis /// private Dictionary _axisDataDic = new Dictionary(); /// /// 其他模块数据 /// private Dictionary _otherModuleDataDic = new Dictionary(); #endregion #region 属性 #endregion //delegate #region Delegate public delegate void UpdateVariableValueMotionDatasChanged(Dictionary datasDic); public delegate void UpdateVariableValueInputDatasChanged(string module, string VariableName, bool value); public delegate void UpdateVariableValueWagoDatasChanged(string sourceName,string targetName, bool value, bool invert); #endregion #region 事件 /// /// MotionDatas变更事件 /// public event UpdateVariableValueMotionDatasChanged OnUpdateMotionDatasChanged; /// /// InputDatas变更事件 /// public event UpdateVariableValueInputDatasChanged OnUpdateInputDatasChanged; /// /// WagoDatas变更事件 /// public event UpdateVariableValueWagoDatasChanged OnUpdateWagoDatasChanged; #endregion /// /// 初始化 /// public void Initialize() { _periodicJob = new PeriodicJob(TIMER_INTERVAL, OnTimer, "Motor Simulator Timer", true); Init(); } /// /// 初始化数据 /// private void Init() { SimulatorCommManager.Instance.OnUpdateVariableValueChanged += UpdataDataCausedByOtherModule; InitOtherModuleDatas(); //加载对应配置文件 GalilControllerCfg-Simulator.xml,初始化数据字典 try { string oldXmlPath = PathManager.GetCfgDir(); string newXmlPath = oldXmlPath.Replace("CyberX8_Simulator", "CyberX8_RT") + "Devices\\GalilControllerCfg-Simulator.xml"; GalilControllerCfg cfg = CustomXmlSerializer.Deserialize(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; _motorNameDataDic[$"{config.Module}.{item.Name}"].ModuleName = config.Module; } } } } 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(new FileInfo(newXmlPath)); if (axisProviderCfg != null) { foreach (BeckhoffProviderAxis item in axisProviderCfg.Axes) { double value = item.ScaleFactor / 30; _axisDataDic[item.Name] = item; _motorNameMinStepDic[item.Name] = (int)((value < 100) ? 100 : value); } } } catch { LOG.WriteLog(eEvent.ERR_AXIS, "axisProvider", "Load AxisProviderCfg.xml failed"); } //加载对应配置文件 StationPositionCfg-Simulator.xml,初始化数据字典 try { string oldXmlPath = PathManager.GetCfgDir(); string newXmlPath = oldXmlPath.Replace("CyberX8_Simulator", "CyberX8_RT") + "Station\\StationPositionsCfg_Simulator.xml"; StationPositionCfg cfg = CustomXmlSerializer.Deserialize(new FileInfo(newXmlPath)); if (cfg != null) { foreach (BeckhoffStationModule config in cfg.Module) { if (config.Name.Contains("Transporter")) { foreach (BeckhoffStationAxis item in config.Axises) { _toleranceDic[item.Name] = item.ToleranceDefault; foreach(var subItem in item.Stations) { _stationPositionDic[subItem.Name.ToLower()] = double.Parse(subItem.Position); } } } } } } catch { LOG.WriteLog(eEvent.ERR_GALIL, "StationPosition", "Load StationPositionCfg-Simulator.xml failed"); } } private void InitOtherModuleDatas() { _otherModuleDataDic[FESTO_DATABUFFER_TRANSPORT1_LOCK] = false; _otherModuleDataDic[FESTO_DATABUFFER_TRANSPORT2_LOCK] = false; } /// /// 定时器执行 /// /// private bool OnTimer() { //电机运动模型 foreach(var motorItem in _motorNameDataDic) { //对应电机进行模拟 MotorMotionSimulator(motorItem); //实时更新电机数据 UpdateMotionDatas(_motorNameDataDic); //更新InputDatas UpdateInputDatas(motorItem); } UpdateDatasByMutiAxisDatas(); return true; } /// /// 通知Galil模块motion数据变化 /// /// private void UpdateMotionDatas(Dictionary datasDic) { if (OnUpdateMotionDatasChanged != null) { OnUpdateMotionDatasChanged(datasDic); } } /// /// 通知Galil模块Input数据变化 /// /// /// private void UpdateInputDatas(KeyValuePair motorItem) { if (OnUpdateInputDatasChanged != null && CheckMotionData(motorItem, out string variableName, out bool value)) { OnUpdateInputDatasChanged(motorItem.Value.ModuleName, variableName, value); } } private void UpdateDatasByMutiAxisDatas() { if (OnUpdateWagoDatasChanged != null && CheckMutiMotionData(out string variableName, out bool value)) { OnUpdateWagoDatasChanged("",variableName, value, false); } } /// /// 设置电机数据 /// /// /// /// 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; } } /// /// 运动模拟器 /// private void MotorMotionSimulator(KeyValuePair motor) { SimulatorMotionData motionData = motor.Value; string name = motor.Key; //上电检查 if (!motionData.SwitchSignal) return; if (motionData.HomingSignal) { HomeOperation(motionData); } else { PositionOperation(motionData, name); } } /// /// Home操作 /// /// 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; } } /// /// GoToPosition操作 /// /// 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.ForwardLimit = fwdLimit; 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.ReverseLimit = revLimit; motionData.ActualVelocity = 0; } } } //停止信号 if (motionData.StopSignal) { motionData.MotionSignal = false; motionData.StopCode = 4;//ST操作停止码4 motionData.StopSignal = false; } } /// /// 梯形加减速运动控制 /// 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; } /// /// 检查单一电机数据 /// /// private bool CheckMotionData(KeyValuePair motor, out string name,out bool value) { bool result = false; value = false; name = ""; switch (motor.Key) { case "Transporter1.Elevator": name = "r_TRANSPORT1_WS_HOLD_PRESENT"; value = _otherModuleDataDic[FESTO_DATABUFFER_TRANSPORT1_LOCK] && CheckAtStation(motor.Key, motor.Value, "up") ? true : false; result = true; break; case "Transporter2.Elevator": name = "r_TRANSPORT2_WS_HOLD_PRESENT"; value = _otherModuleDataDic[FESTO_DATABUFFER_TRANSPORT2_LOCK] && CheckAtStation(motor.Key, motor.Value, "up") ? true : false; result = true; break; default: break; } return result; } /// /// 检查多个电机数据 /// /// private bool CheckMutiMotionData(out string variableName, out bool value) { bool result = false; variableName = ""; value = false; //Loader WS Present Sensor if (CheckAtStation("Transporter2.Elevator", _motorNameDataDic["Transporter2.Elevator"], "Loader") && CheckAtStation("Transporter2.Gantry", _motorNameDataDic["Transporter2.Gantry"], "Loader")) { value = !_otherModuleDataDic[FESTO_DATABUFFER_TRANSPORT2_LOCK] ? true : false; variableName = "r_Cathode_Present"; result = true; } return result; } /// /// 更新其他模块数据 /// /// /// private void UpdataDataCausedByOtherModule(string sourceName,string name, bool value, bool revert) { if (_otherModuleDataDic.ContainsKey(name)) { _otherModuleDataDic[name] = value; } } private bool CheckAtStation(string name, SimulatorMotionData data, string stationName) { double diff = Math.Abs(data.ActualPosition - _stationPositionDic[$"{name.ToLower()}.{stationName.ToLower()}"] * _axisDataDic[name].ScaleFactor); if (diff <= _toleranceDic[name] * _axisDataDic[name].ScaleFactor) { return true; } return false; } } }