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;
}
}
}