ANTransferPumpOnRoutine.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. using Aitex.Core.RT.Device;
  2. using Aitex.Core.RT.Log;
  3. using Aitex.Core.RT.Routine;
  4. using Aitex.Core.RT.SCCore;
  5. using CyberX8_Core;
  6. using CyberX8_RT.Devices.Reservoir;
  7. using MECF.Framework.Common.Beckhoff.ModuleIO;
  8. using MECF.Framework.Common.IOCore;
  9. using MECF.Framework.Common.Persistent.Reservoirs;
  10. using MECF.Framework.Common.Routine;
  11. using MECF.Framework.Common.TwinCat;
  12. using System;
  13. namespace CyberX8_RT.Modules.Reservoir
  14. {
  15. public class ANTransferPumpOnRoutine : RoutineBase, IRoutine
  16. {
  17. private enum ANTransferPumpStep
  18. {
  19. CrossDoseValveOn,
  20. Reset,
  21. WaitReset,
  22. CheckReadyToEnable,
  23. Enable,
  24. CheckReady,
  25. WriteParameter,
  26. Execute,
  27. Wait,
  28. Done,
  29. End
  30. }
  31. private enum STMStatus
  32. {
  33. ReadyToEnable,
  34. Ready,
  35. Warning,
  36. Error
  37. }
  38. private enum POSStatus
  39. {
  40. Busy,
  41. InTarget,
  42. Warning,
  43. Error
  44. }
  45. private enum ENCStatus
  46. {
  47. SetCounterDone
  48. }
  49. #region 常量
  50. private const string TRANSFER_PUMP_ENABLE = "TransferPumpEnable";
  51. private const string TRANSFER_PUMP_RESET = "TransferPumpReset";
  52. private const string TRANSFER_PUMP_EXECUTE = "TransferPumpExecute";
  53. private const string TRANSFER_PUMP_TARGET_POSITION = "TransferPumpTargetPosition";
  54. private const string TRANSFER_PUMP_SPEED = "TransferPumpSpeed";
  55. private const string TRANSFER_PUMP_START_TYPE = "TransferPumpStartType";
  56. private const string TRANSFER_PUMP_ACCELERATION = "TransferPumpAcceleration";
  57. private const string TRANSFER_PUMP_DECELERATION = "TransferPumpDeceleration";
  58. private const string CROSS_DOSE_ENABLE = "CrossDoseEnable";
  59. #endregion
  60. #region 内部变量
  61. /// <summary>
  62. /// Module Name
  63. /// </summary>
  64. private string _moduleName;
  65. /// <summary>
  66. /// CompactMembranReservoirDevice
  67. /// </summary>
  68. private CompactMembranReservoirDevice _cmReservoirDevice;
  69. /// <summary>
  70. /// 加速度
  71. /// </summary>
  72. private int _acceleration;
  73. /// <summary>
  74. /// 减速度
  75. /// </summary>
  76. private int _deceleration;
  77. /// <summary>
  78. /// 速度
  79. /// </summary>
  80. private int _speed;
  81. /// <summary>
  82. /// 目标位置
  83. /// </summary>
  84. private double _targetPosition;
  85. /// <summary>
  86. /// 开始位置
  87. /// </summary>
  88. private double _startPosition;
  89. /// <summary>
  90. /// Start Type
  91. /// </summary>
  92. private int _startType = 1; //RELATIVE(相对位置)
  93. /// <summary>
  94. /// ReservoirsPersistentValue
  95. /// </summary>
  96. private ReservoirsPersistentValue _persistentValue;
  97. /// <summary>
  98. /// 超时时间
  99. /// </summary>
  100. private int _timeOut = 2000;
  101. /// <summary>
  102. /// PumpFactor
  103. /// </summary>
  104. private double _pumpFactor;
  105. /// <summary>
  106. /// 最小流量
  107. /// </summary>
  108. private double _flowRateMin;
  109. /// <summary>
  110. /// StartUpHoldOffTime
  111. /// </summary>
  112. private int _startUpHoldOffTime;
  113. /// <summary>
  114. /// Pump启动时刻
  115. /// </summary>
  116. private int _startTime;
  117. #endregion
  118. #region 属性
  119. public RState CurrentState { get { return Runner.Status; } }
  120. #endregion
  121. /// <summary>
  122. /// 构造函数
  123. /// </summary>
  124. /// <param name="module"></param>
  125. public ANTransferPumpOnRoutine(string module) : base(module)
  126. {
  127. _moduleName = module;
  128. }
  129. /// <summary>
  130. /// 中止
  131. /// </summary>
  132. public void Abort()
  133. {
  134. Runner.Stop("Manual abort");
  135. }
  136. /// <summary>
  137. /// 监控
  138. /// </summary>
  139. /// <returns></returns>
  140. public RState Monitor()
  141. {
  142. Runner.Run(ANTransferPumpStep.CrossDoseValveOn, OpenValve, _delay_1ms)
  143. .Run(ANTransferPumpStep.Reset, ResetCrossDose, _delay_1ms)
  144. .Wait(ANTransferPumpStep.WaitReset, WaitReset, _timeOut)
  145. .Run(ANTransferPumpStep.CheckReadyToEnable, CheckReadyToEnable, _delay_1ms)
  146. .Run(ANTransferPumpStep.Enable, EnableOperation, _delay_1ms)
  147. .Run(ANTransferPumpStep.CheckReady, CheckReady, _delay_1ms)
  148. .Run(ANTransferPumpStep.WriteParameter, WriteParameter, _delay_1ms)
  149. .Run(ANTransferPumpStep.Execute, ExecuteOperation, _delay_1ms)
  150. .WaitWithStopCondition(ANTransferPumpStep.Wait, WaitCompleteEndStatus, WaitCompleteErrorStatus)
  151. .Run(ANTransferPumpStep.Done, DoneOperation, _delay_1ms)
  152. .End(ANTransferPumpStep.End, NullFun);
  153. return Runner.Status;
  154. }
  155. /// <summary>
  156. /// 启动
  157. /// </summary>
  158. /// <param name="objs"></param>
  159. /// <returns></returns>
  160. public RState Start(params object[] objs)
  161. {
  162. _startType = 2;
  163. _cmReservoirDevice = DEVICE.GetDevice<CompactMembranReservoirDevice>($"{_moduleName}");
  164. if (_cmReservoirDevice == null)
  165. {
  166. LOG.WriteLog(eEvent.ERR_RESERVOIR, _moduleName, "Compact Membran Reservoir Device is not exist");
  167. return RState.Failed;
  168. }
  169. _persistentValue = ReservoirsPersistentManager.Instance.GetReservoirsPersistentValue(_moduleName);
  170. if (_persistentValue == null)
  171. {
  172. LOG.WriteLog(eEvent.ERR_RESERVOIR, _moduleName, "Reservoir Persistent Value Object is not exist");
  173. return RState.Failed;
  174. }
  175. //加速度(count/s/s)
  176. if (SC.ContainsItem($"Reservoir.{Module}.ANTransferPumpAcceleration"))
  177. {
  178. _acceleration = SC.GetValue<int>($"Reservoir.{Module}.ANTransferPumpAcceleration");
  179. if (_acceleration == 0) return RState.Failed;
  180. }
  181. //减速度(count/s/s)
  182. if (SC.ContainsItem($"Reservoir.{Module}.ANTransferPumpDeceleration"))
  183. {
  184. _deceleration = SC.GetValue<int>($"Reservoir.{Module}.ANTransferPumpDeceleration");
  185. if (_deceleration == 0) return RState.Failed;
  186. }
  187. //速度(count/s)
  188. if (SC.ContainsItem($"Reservoir.{Module}.ANTransferPumpSpeed"))
  189. {
  190. _speed = SC.GetValue<int>($"Reservoir.{Module}.ANTransferPumpSpeed");
  191. if (_speed == 0) return RState.Failed;
  192. }
  193. //目标位置(count)(1mL = 23809.96272 counts)
  194. _startPosition = _cmReservoirDevice.ReservoirData.TransferActualPosition;
  195. _targetPosition = (double)objs[0];
  196. _pumpFactor = _persistentValue.CrossDosePumpFactor;
  197. if(_pumpFactor == 0)
  198. {
  199. LOG.WriteLog(eEvent.WARN_RESERVOIR, _moduleName, "Pump Factor is zero");
  200. return RState.Failed;
  201. }
  202. //最小FlowRate(mL/min)
  203. if (SC.ContainsItem($"Reservoir.{Module}.FlowRateMin"))
  204. {
  205. _flowRateMin = SC.GetValue<double>($"Reservoir.{Module}.FlowRateMin");
  206. }
  207. //StartUpHoldOffTime(流量检测时刻,ms)
  208. if (SC.ContainsItem($"Reservoir.{Module}.StartUpHoldOffTime"))
  209. {
  210. _startUpHoldOffTime = SC.GetValue<int>($"Reservoir.{Module}.StartUpHoldOffTime");
  211. }
  212. return Runner.Start(Module, "AN Transfer Pump On");
  213. }
  214. /// <summary>
  215. /// 打开CrossDose Valve
  216. /// </summary>
  217. /// <returns></returns>
  218. private bool OpenValve()
  219. {
  220. if (!_cmReservoirDevice.CrossDoseOn("", null))
  221. {
  222. LOG.WriteLog(eEvent.WARN_RESERVOIR, _moduleName, $"Close CrossDose Valve failed");
  223. return false;
  224. }
  225. return true;
  226. }
  227. /// <summary>
  228. /// Reset CrossDose
  229. /// </summary>
  230. /// <returns></returns>
  231. private bool ResetCrossDose()
  232. {
  233. return _cmReservoirDevice.ResetCrossDose("", null);
  234. }
  235. /// <summary>
  236. /// Reset CrossDose
  237. /// </summary>
  238. /// <returns></returns>
  239. private bool WaitReset()
  240. {
  241. if (_cmReservoirDevice.ResetCrossDoseMonitor())
  242. {
  243. return true;
  244. }
  245. return false;
  246. }
  247. /// <summary>
  248. /// 检查电机ReadyToEnable状态
  249. /// </summary>
  250. /// <returns></returns>
  251. private bool CheckReadyToEnable()
  252. {
  253. //STM:ReadyToEnable = 1
  254. ushort status = (ushort)_cmReservoirDevice.ReservoirData.TransferPumpSTMStatus;
  255. if (!CheckSTMStatus(status, STMStatus.ReadyToEnable))
  256. {
  257. LOG.WriteLog(eEvent.ERR_RESERVOIR, _moduleName, $"STMStatus: {status}. Ready to enable is false");
  258. return false;
  259. }
  260. return true;
  261. }
  262. /// <summary>
  263. /// EnableOperation
  264. /// </summary>
  265. /// <returns></returns>
  266. private bool EnableOperation()
  267. {
  268. string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_ENABLE}");
  269. bool result = IOModuleManager.Instance.WriteIoValue(ioName, true);
  270. if (!result)
  271. {
  272. return false;
  273. }
  274. return true;
  275. }
  276. /// <summary>
  277. /// 检查电机Ready状态
  278. /// </summary>
  279. /// <returns></returns>
  280. private bool CheckReady()
  281. {
  282. //STM:Ready = 1
  283. ushort status = (ushort)_cmReservoirDevice.ReservoirData.TransferPumpSTMStatus;
  284. if (!CheckSTMStatus(status, STMStatus.Ready))
  285. {
  286. LOG.WriteLog(eEvent.ERR_RESERVOIR, _moduleName, $"STMStatus: {status}. Ready is false");
  287. return false;
  288. }
  289. return true;
  290. }
  291. /// <summary>
  292. /// 设置电机参数
  293. /// </summary>
  294. /// <returns></returns>
  295. private bool WriteParameter()
  296. {
  297. //设置加速度
  298. string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_ACCELERATION}");
  299. bool result = IOModuleManager.Instance.WriteIoValue(ioName, _acceleration);
  300. if (!result)
  301. {
  302. return false;
  303. }
  304. //设置减速度
  305. ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_DECELERATION}");
  306. result = IOModuleManager.Instance.WriteIoValue(ioName, _deceleration);
  307. if (!result)
  308. {
  309. return false;
  310. }
  311. //设置速度
  312. ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_SPEED}");
  313. result = IOModuleManager.Instance.WriteIoValue(ioName, _speed);
  314. if (!result)
  315. {
  316. return false;
  317. }
  318. //设置StartType
  319. ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_START_TYPE}");
  320. result = IOModuleManager.Instance.WriteIoValue(ioName, _startType);
  321. if (!result)
  322. {
  323. return false;
  324. }
  325. //设置目标位置(mL)
  326. ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_TARGET_POSITION}");
  327. result = IOModuleManager.Instance.WriteIoValue(ioName, _targetPosition/ _pumpFactor);
  328. if (!result)
  329. {
  330. return false;
  331. }
  332. return true;
  333. }
  334. /// <summary>
  335. /// 运动到位置
  336. /// </summary>
  337. /// <returns></returns>
  338. private bool ExecuteOperation()
  339. {
  340. string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_EXECUTE}");
  341. bool result = IOModuleManager.Instance.WriteIoValue(ioName, true);
  342. if (!result)
  343. {
  344. return false;
  345. }
  346. _startTime = Environment.TickCount;
  347. ReservoirsPersistentManager.Instance.UpdatePersistentValue(_moduleName);
  348. return true;
  349. }
  350. /// <summary>
  351. /// WaitCompleteEnd
  352. /// </summary>
  353. /// <returns></returns>
  354. private bool WaitCompleteEndStatus()
  355. {
  356. ushort status = (ushort)_cmReservoirDevice.ReservoirData.TransferPumpPOSStatus;
  357. double dosedVolume = (_cmReservoirDevice.ReservoirData.TransferActualPosition - _startPosition) * _pumpFactor;
  358. _cmReservoirDevice.ReservoirData.RemainingCrossDoseVolume = Math.Round(_targetPosition - dosedVolume,2);
  359. if (CheckPOSStatus(status, POSStatus.InTarget) && !CheckPOSStatus(status, POSStatus.Busy))
  360. {
  361. return true;
  362. }
  363. return false;
  364. }
  365. /// <summary>
  366. /// WaitCompleteError
  367. /// </summary>
  368. /// <returns></returns>
  369. private bool WaitCompleteErrorStatus()
  370. {
  371. ushort status = (ushort)_cmReservoirDevice.ReservoirData.TransferPumpPOSStatus;
  372. //流量检测
  373. if(_cmReservoirDevice.ANTransferFlow != null)
  374. {
  375. int ticks = Environment.TickCount - _startTime;
  376. double flow = _cmReservoirDevice.ANTransferFlow.CounterValue;
  377. if (ticks >= _startUpHoldOffTime && flow < _flowRateMin)
  378. {
  379. LOG.WriteLog(eEvent.ERR_RESERVOIR, _moduleName, $"Dose Flow {flow} mL/min is not over {_flowRateMin} mL/min");
  380. return true;
  381. }
  382. }
  383. if (CheckPOSStatus(status, POSStatus.Error))
  384. {
  385. LOG.WriteLog(eEvent.WARN_RESERVOIR, _moduleName, $"POSStatus:{status}. Error is true");
  386. return true;
  387. }
  388. return false;
  389. }
  390. /// <summary>
  391. /// DoneOperation
  392. /// </summary>
  393. /// <returns></returns>
  394. private bool DoneOperation()
  395. {
  396. string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_EXECUTE}");
  397. bool result = IOModuleManager.Instance.WriteIoValue(ioName, false);
  398. if (!result)
  399. {
  400. return false;
  401. }
  402. ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_ENABLE}");
  403. result = IOModuleManager.Instance.WriteIoValue(ioName, false);
  404. if (!result)
  405. {
  406. return false;
  407. }
  408. if (!_cmReservoirDevice.CrossDoseOff("", null))
  409. {
  410. LOG.WriteLog(eEvent.WARN_RESERVOIR, _moduleName, $"Close CrossDose Valve failed");
  411. return false;
  412. }
  413. _persistentValue.AutoCrossDoseStartTime = DateTime.MinValue;
  414. _persistentValue.AutoCrossDoseStartAmpHour = 0;
  415. _cmReservoirDevice.ReservoirData.RemainingCrossDoseVolume = 0;
  416. ReservoirsPersistentManager.Instance.UpdatePersistentValue(_moduleName);
  417. return true;
  418. }
  419. /// <summary>
  420. /// STM状态判断
  421. /// </summary>
  422. /// <param name="status"></param>
  423. /// <param name="statusName"></param>
  424. /// <returns></returns>
  425. private bool CheckSTMStatus(ushort status, STMStatus statusName)
  426. {
  427. bool result = false;
  428. switch (statusName)
  429. {
  430. case STMStatus.ReadyToEnable:
  431. byte bit0 = (byte)(status & 0b0001);
  432. result = (bit0 == 1) ? true : false;
  433. break;
  434. case STMStatus.Ready:
  435. byte bit1 = (byte)((status & 0b0010) >> 1);
  436. result = (bit1 == 1) ? true : false;
  437. break;
  438. case STMStatus.Warning:
  439. byte bit2 = (byte)((status & 0b0100) >> 2);
  440. result = (bit2 == 1) ? true : false;
  441. break;
  442. case STMStatus.Error:
  443. byte bit3 = (byte)((status & 0b1000) >> 3);
  444. result = (bit3 == 1) ? true : false;
  445. break;
  446. default:
  447. break;
  448. }
  449. return result;
  450. }
  451. /// <summary>
  452. /// POS状态判断
  453. /// </summary>
  454. /// <param name="status"></param>
  455. /// <param name="statusName"></param>
  456. /// <returns></returns>
  457. private bool CheckPOSStatus(ushort status, POSStatus statusName)
  458. {
  459. bool result = false;
  460. switch (statusName)
  461. {
  462. case POSStatus.Busy:
  463. byte bit0 = (byte)(status & 0b0001);
  464. result = (bit0 == 1) ? true : false;
  465. break;
  466. case POSStatus.InTarget:
  467. byte bit1 = (byte)((status & 0b0010) >> 1);
  468. result = (bit1 == 1) ? true : false;
  469. break;
  470. case POSStatus.Warning:
  471. byte bit2 = (byte)((status & 0b0100) >> 2);
  472. result = (bit2 == 1) ? true : false;
  473. break;
  474. case POSStatus.Error:
  475. byte bit3 = (byte)((status & 0b1000) >> 3);
  476. result = (bit3 == 1) ? true : false;
  477. break;
  478. default:
  479. break;
  480. }
  481. return result;
  482. }
  483. }
  484. }