using Aitex.Core.RT.Device; using Aitex.Core.RT.Log; using Aitex.Core.RT.Routine; using Aitex.Core.RT.SCCore; using CyberX8_Core; using CyberX8_RT.Devices.Reservoir; using MECF.Framework.Common.Beckhoff.ModuleIO; using MECF.Framework.Common.IOCore; using MECF.Framework.Common.Persistent.Reservoirs; using MECF.Framework.Common.Routine; using MECF.Framework.Common.TwinCat; using System; namespace CyberX8_RT.Modules.Reservoir { public class ANTransferPumpOnRoutine : RoutineBase, IRoutine { private enum ANTransferPumpStep { CrossDoseValveOn, Reset, WaitReset, CheckReadyToEnable, Enable, CheckReady, WriteParameter, Execute, Wait, Done, End } private enum STMStatus { ReadyToEnable, Ready, Warning, Error } private enum POSStatus { Busy, InTarget, Warning, Error } private enum ENCStatus { SetCounterDone } #region 常量 private const string TRANSFER_PUMP_ENABLE = "TransferPumpEnable"; private const string TRANSFER_PUMP_RESET = "TransferPumpReset"; private const string TRANSFER_PUMP_EXECUTE = "TransferPumpExecute"; private const string TRANSFER_PUMP_TARGET_POSITION = "TransferPumpTargetPosition"; private const string TRANSFER_PUMP_SPEED = "TransferPumpSpeed"; private const string TRANSFER_PUMP_START_TYPE = "TransferPumpStartType"; private const string TRANSFER_PUMP_ACCELERATION = "TransferPumpAcceleration"; private const string TRANSFER_PUMP_DECELERATION = "TransferPumpDeceleration"; private const string CROSS_DOSE_ENABLE = "CrossDoseEnable"; #endregion #region 内部变量 /// /// Module Name /// private string _moduleName; /// /// CompactMembranReservoirDevice /// private CompactMembranReservoirDevice _cmReservoirDevice; /// /// 加速度 /// private int _acceleration; /// /// 减速度 /// private int _deceleration; /// /// 速度 /// private int _speed; /// /// 目标位置 /// private double _targetPosition; /// /// 开始位置 /// private double _startPosition; /// /// Start Type /// private int _startType = 1; //RELATIVE(相对位置) /// /// ReservoirsPersistentValue /// private ReservoirsPersistentValue _persistentValue; /// /// 超时时间 /// private int _timeOut = 2000; /// /// PumpFactor /// private double _pumpFactor; /// /// 最小流量 /// private double _flowRateMin; /// /// StartUpHoldOffTime /// private int _startUpHoldOffTime; /// /// Pump启动时刻 /// private int _startTime; #endregion #region 属性 public RState CurrentState { get { return Runner.Status; } } #endregion /// /// 构造函数 /// /// public ANTransferPumpOnRoutine(string module) : base(module) { _moduleName = module; } /// /// 中止 /// public void Abort() { Runner.Stop("Manual abort"); } /// /// 监控 /// /// public RState Monitor() { Runner.Run(ANTransferPumpStep.CrossDoseValveOn, OpenValve, _delay_1ms) .Run(ANTransferPumpStep.Reset, ResetCrossDose, _delay_1ms) .Wait(ANTransferPumpStep.WaitReset, WaitReset, _timeOut) .Run(ANTransferPumpStep.CheckReadyToEnable, CheckReadyToEnable, _delay_1ms) .Run(ANTransferPumpStep.Enable, EnableOperation, _delay_1ms) .Run(ANTransferPumpStep.CheckReady, CheckReady, _delay_1ms) .Run(ANTransferPumpStep.WriteParameter, WriteParameter, _delay_1ms) .Run(ANTransferPumpStep.Execute, ExecuteOperation, _delay_1ms) .WaitWithStopCondition(ANTransferPumpStep.Wait, WaitCompleteEndStatus, WaitCompleteErrorStatus) .Run(ANTransferPumpStep.Done, DoneOperation, _delay_1ms) .End(ANTransferPumpStep.End, NullFun); return Runner.Status; } /// /// 启动 /// /// /// public RState Start(params object[] objs) { _startType = 2; _cmReservoirDevice = DEVICE.GetDevice($"{_moduleName}"); if (_cmReservoirDevice == null) { LOG.WriteLog(eEvent.ERR_RESERVOIR, _moduleName, "Compact Membran Reservoir Device is not exist"); return RState.Failed; } _persistentValue = ReservoirsPersistentManager.Instance.GetReservoirsPersistentValue(_moduleName); if (_persistentValue == null) { LOG.WriteLog(eEvent.ERR_RESERVOIR, _moduleName, "Reservoir Persistent Value Object is not exist"); return RState.Failed; } //加速度(count/s/s) if (SC.ContainsItem($"Reservoir.{Module}.ANTransferPumpAcceleration")) { _acceleration = SC.GetValue($"Reservoir.{Module}.ANTransferPumpAcceleration"); if (_acceleration == 0) return RState.Failed; } //减速度(count/s/s) if (SC.ContainsItem($"Reservoir.{Module}.ANTransferPumpDeceleration")) { _deceleration = SC.GetValue($"Reservoir.{Module}.ANTransferPumpDeceleration"); if (_deceleration == 0) return RState.Failed; } //速度(count/s) if (SC.ContainsItem($"Reservoir.{Module}.ANTransferPumpSpeed")) { _speed = SC.GetValue($"Reservoir.{Module}.ANTransferPumpSpeed"); if (_speed == 0) return RState.Failed; } //目标位置(count)(1mL = 23809.96272 counts) _startPosition = _cmReservoirDevice.ReservoirData.TransferActualPosition; _targetPosition = (double)objs[0]; _pumpFactor = _persistentValue.CrossDosePumpFactor; if(_pumpFactor == 0) { LOG.WriteLog(eEvent.WARN_RESERVOIR, _moduleName, "Pump Factor is zero"); return RState.Failed; } //最小FlowRate(mL/min) if (SC.ContainsItem($"Reservoir.{Module}.FlowRateMin")) { _flowRateMin = SC.GetValue($"Reservoir.{Module}.FlowRateMin"); } //StartUpHoldOffTime(流量检测时刻,ms) if (SC.ContainsItem($"Reservoir.{Module}.StartUpHoldOffTime")) { _startUpHoldOffTime = SC.GetValue($"Reservoir.{Module}.StartUpHoldOffTime"); } return Runner.Start(Module, "AN Transfer Pump On"); } /// /// 打开CrossDose Valve /// /// private bool OpenValve() { if (!_cmReservoirDevice.CrossDoseOn("", null)) { LOG.WriteLog(eEvent.WARN_RESERVOIR, _moduleName, $"Close CrossDose Valve failed"); return false; } return true; } /// /// Reset CrossDose /// /// private bool ResetCrossDose() { return _cmReservoirDevice.ResetCrossDose("", null); } /// /// Reset CrossDose /// /// private bool WaitReset() { if (_cmReservoirDevice.ResetCrossDoseMonitor()) { return true; } return false; } /// /// 检查电机ReadyToEnable状态 /// /// private bool CheckReadyToEnable() { //STM:ReadyToEnable = 1 ushort status = (ushort)_cmReservoirDevice.ReservoirData.TransferPumpSTMStatus; if (!CheckSTMStatus(status, STMStatus.ReadyToEnable)) { LOG.WriteLog(eEvent.ERR_RESERVOIR, _moduleName, $"STMStatus: {status}. Ready to enable is false"); return false; } return true; } /// /// EnableOperation /// /// private bool EnableOperation() { string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_ENABLE}"); bool result = IOModuleManager.Instance.WriteIoValue(ioName, true); if (!result) { return false; } return true; } /// /// 检查电机Ready状态 /// /// private bool CheckReady() { //STM:Ready = 1 ushort status = (ushort)_cmReservoirDevice.ReservoirData.TransferPumpSTMStatus; if (!CheckSTMStatus(status, STMStatus.Ready)) { LOG.WriteLog(eEvent.ERR_RESERVOIR, _moduleName, $"STMStatus: {status}. Ready is false"); return false; } return true; } /// /// 设置电机参数 /// /// private bool WriteParameter() { //设置加速度 string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_ACCELERATION}"); bool result = IOModuleManager.Instance.WriteIoValue(ioName, _acceleration); if (!result) { return false; } //设置减速度 ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_DECELERATION}"); result = IOModuleManager.Instance.WriteIoValue(ioName, _deceleration); if (!result) { return false; } //设置速度 ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_SPEED}"); result = IOModuleManager.Instance.WriteIoValue(ioName, _speed); if (!result) { return false; } //设置StartType ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_START_TYPE}"); result = IOModuleManager.Instance.WriteIoValue(ioName, _startType); if (!result) { return false; } //设置目标位置(mL) ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_TARGET_POSITION}"); result = IOModuleManager.Instance.WriteIoValue(ioName, _targetPosition/ _pumpFactor); if (!result) { return false; } return true; } /// /// 运动到位置 /// /// private bool ExecuteOperation() { string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_EXECUTE}"); bool result = IOModuleManager.Instance.WriteIoValue(ioName, true); if (!result) { return false; } _startTime = Environment.TickCount; ReservoirsPersistentManager.Instance.UpdatePersistentValue(_moduleName); return true; } /// /// WaitCompleteEnd /// /// private bool WaitCompleteEndStatus() { ushort status = (ushort)_cmReservoirDevice.ReservoirData.TransferPumpPOSStatus; double dosedVolume = (_cmReservoirDevice.ReservoirData.TransferActualPosition - _startPosition) * _pumpFactor; _cmReservoirDevice.ReservoirData.RemainingCrossDoseVolume = Math.Round(_targetPosition - dosedVolume,2); if (CheckPOSStatus(status, POSStatus.InTarget) && !CheckPOSStatus(status, POSStatus.Busy)) { return true; } return false; } /// /// WaitCompleteError /// /// private bool WaitCompleteErrorStatus() { ushort status = (ushort)_cmReservoirDevice.ReservoirData.TransferPumpPOSStatus; //流量检测 if(_cmReservoirDevice.ANTransferFlow != null) { int ticks = Environment.TickCount - _startTime; double flow = _cmReservoirDevice.ANTransferFlow.CounterValue; if (ticks >= _startUpHoldOffTime && flow < _flowRateMin) { LOG.WriteLog(eEvent.ERR_RESERVOIR, _moduleName, $"Dose Flow {flow} mL/min is not over {_flowRateMin} mL/min"); return true; } } if (CheckPOSStatus(status, POSStatus.Error)) { LOG.WriteLog(eEvent.WARN_RESERVOIR, _moduleName, $"POSStatus:{status}. Error is true"); return true; } return false; } /// /// DoneOperation /// /// private bool DoneOperation() { string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_EXECUTE}"); bool result = IOModuleManager.Instance.WriteIoValue(ioName, false); if (!result) { return false; } ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_ENABLE}"); result = IOModuleManager.Instance.WriteIoValue(ioName, false); if (!result) { return false; } if (!_cmReservoirDevice.CrossDoseOff("", null)) { LOG.WriteLog(eEvent.WARN_RESERVOIR, _moduleName, $"Close CrossDose Valve failed"); return false; } _persistentValue.AutoCrossDoseStartTime = DateTime.MinValue; _persistentValue.AutoCrossDoseStartAmpHour = 0; _cmReservoirDevice.ReservoirData.RemainingCrossDoseVolume = 0; ReservoirsPersistentManager.Instance.UpdatePersistentValue(_moduleName); return true; } /// /// STM状态判断 /// /// /// /// private bool CheckSTMStatus(ushort status, STMStatus statusName) { bool result = false; switch (statusName) { case STMStatus.ReadyToEnable: byte bit0 = (byte)(status & 0b0001); result = (bit0 == 1) ? true : false; break; case STMStatus.Ready: byte bit1 = (byte)((status & 0b0010) >> 1); result = (bit1 == 1) ? true : false; break; case STMStatus.Warning: byte bit2 = (byte)((status & 0b0100) >> 2); result = (bit2 == 1) ? true : false; break; case STMStatus.Error: byte bit3 = (byte)((status & 0b1000) >> 3); result = (bit3 == 1) ? true : false; break; default: break; } return result; } /// /// POS状态判断 /// /// /// /// private bool CheckPOSStatus(ushort status, POSStatus statusName) { bool result = false; switch (statusName) { case POSStatus.Busy: byte bit0 = (byte)(status & 0b0001); result = (bit0 == 1) ? true : false; break; case POSStatus.InTarget: byte bit1 = (byte)((status & 0b0010) >> 1); result = (bit1 == 1) ? true : false; break; case POSStatus.Warning: byte bit2 = (byte)((status & 0b0100) >> 2); result = (bit2 == 1) ? true : false; break; case POSStatus.Error: byte bit3 = (byte)((status & 0b1000) >> 3); result = (bit3 == 1) ? true : false; break; default: break; } return result; } } }