CrossDoseHelper.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  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 Aitex.Core.Util;
  6. using CyberX8_Core;
  7. using CyberX8_RT.Devices.Dose;
  8. using CyberX8_RT.Modules;
  9. using CyberX8_RT.Modules.Reservoir;
  10. using MECF.Framework.Common.Beckhoff.ModuleIO;
  11. using MECF.Framework.Common.IOCore;
  12. using MECF.Framework.Common.Persistent.Reservoirs;
  13. using MECF.Framework.Common.ProcessCell;
  14. using MECF.Framework.Common.RecipeCenter;
  15. using MECF.Framework.Common.ToolLayout;
  16. using MECF.Framework.Common.TwinCat;
  17. using System;
  18. using System.Reflection;
  19. using System.Windows;
  20. using static CyberX8_RT.Devices.Reservoir.DosingSystemHelper;
  21. namespace CyberX8_RT.Devices.Reservoir
  22. {
  23. public class CrossDoseHelper
  24. {
  25. #region 常量
  26. public enum CrossDoseOperation
  27. {
  28. None,
  29. ManualDosing,
  30. AutoDosing,
  31. }
  32. /// <summary>
  33. /// 补液体积(固定10mL)
  34. /// </summary>
  35. private const double DOSING_VOLUME = 10;
  36. #endregion
  37. #region 内部变量
  38. /// <summary>
  39. /// ModuleName
  40. /// </summary>
  41. private string _moduleName;
  42. /// <summary>
  43. /// ANTransferPumpOnRoutine
  44. /// </summary>
  45. private ANTransferPumpOnRoutine _anTransferPumpOnRoutine;
  46. /// <summary>
  47. /// CrossDoseResetRoutine
  48. /// </summary>
  49. private CrossDoseResetRoutine _crossDoseResetRoutine;
  50. /// <summary>
  51. /// ReservoirsPersistentValue
  52. /// </summary>
  53. private ReservoirsPersistentValue _persistentValue;
  54. /// <summary>
  55. /// CompactMembranReservoirDevice
  56. /// </summary>
  57. private CompactMembranReservoirDevice _cmReservoirDevice;
  58. /// <summary>
  59. /// CrossDoseVolume
  60. /// </summary>
  61. private double _crossDoseVolume;
  62. /// <summary>
  63. /// 当前CrossDose模式
  64. /// </summary>
  65. private CrossDoseOperation _currentCrossDoseOperation;
  66. /// <summary>
  67. /// Reservoir Usage
  68. /// </summary>
  69. private ReservoirUsage _reservoirUsage;
  70. private bool _flag;
  71. #endregion
  72. #region 属性
  73. /// <summary>
  74. /// 当前Dose模式
  75. /// </summary>
  76. public CrossDoseOperation CurrentDoseOperation{ get {return _currentCrossDoseOperation; } }
  77. /// <summary>
  78. /// CrossDose当前的RState
  79. /// </summary>
  80. public RState CrossDoseState { get { return _anTransferPumpOnRoutine.CurrentState; } }
  81. /// <summary>
  82. /// ResetRoutine当前的RState
  83. /// </summary>
  84. public RState ResetState { get { return _crossDoseResetRoutine.CurrentState; } }
  85. #endregion
  86. public CrossDoseHelper(string moduleName)
  87. {
  88. _moduleName = moduleName;
  89. _anTransferPumpOnRoutine = new ANTransferPumpOnRoutine(_moduleName);
  90. _crossDoseResetRoutine = new CrossDoseResetRoutine(_moduleName);
  91. _persistentValue = ReservoirsPersistentManager.Instance.GetReservoirsPersistentValue(_moduleName);
  92. _cmReservoirDevice = DEVICE.GetDevice<CompactMembranReservoirDevice>($"{_moduleName}");
  93. _currentCrossDoseOperation = CrossDoseOperation.None;
  94. }
  95. #region Operation
  96. /// <summary>
  97. /// 开始Dosing
  98. /// </summary>
  99. /// <returns></returns>
  100. public bool StartDosing(double crossDoseVolume)
  101. {
  102. _crossDoseVolume = crossDoseVolume;
  103. return _anTransferPumpOnRoutine.Start(crossDoseVolume) == RState.Running;
  104. }
  105. /// <summary>
  106. /// 停止Dosing
  107. /// </summary>
  108. /// <returns></returns>
  109. public bool HaltDosing()
  110. {
  111. _anTransferPumpOnRoutine.Abort();
  112. EnterError();
  113. return true;
  114. }
  115. /// <summary>
  116. /// CrossDosing监控
  117. /// </summary>
  118. public void CrossDoseStatusMonitor()
  119. {
  120. RState result = _anTransferPumpOnRoutine.Monitor();
  121. //监控Pump
  122. if (result == RState.Failed || result == RState.Timeout)
  123. {
  124. EnterError();
  125. LOG.WriteLog(eEvent.WARN_RESERVOIR, _moduleName, "Open AN Transfer Pump failed");
  126. _cmReservoirDevice.InitializeCrossDose(false);
  127. }
  128. }
  129. /// <summary>
  130. /// 自动CrossDose监控
  131. /// </summary>
  132. public void AutoCrossDoseMonitor(bool isInitialized)
  133. {
  134. if (!isInitialized)
  135. {
  136. if (!_flag)
  137. {
  138. LOG.WriteLog(eEvent.WARN_RESERVOIR, _moduleName, "Please initialize Cross Dose");
  139. _flag = true;
  140. }
  141. return;
  142. }
  143. _flag = false;
  144. if (_cmReservoirDevice.Recipe != null && (_cmReservoirDevice.Recipe.CurrentBased || _cmReservoirDevice.Recipe.TimeBased))
  145. {
  146. //非AutoDosing模式下进入AutoDosing模式,并记录开始数据
  147. if (_currentCrossDoseOperation != CrossDoseOperation.AutoDosing)
  148. {
  149. LOG.WriteLog(eEvent.INFO_RESERVOIR, _moduleName, $"Timing of AutoCrossDose has started now");
  150. RecordStartData();
  151. SetAutoDoseOperation();
  152. }
  153. else
  154. {
  155. //AutoDosing模式下,若上一次Dosing完成则再次记录数据
  156. if (_persistentValue.AutoCrossDoseStartTime == DateTime.MinValue && _persistentValue.AutoCrossDoseStartAmpHour == 0)
  157. {
  158. LOG.WriteLog(eEvent.INFO_RESERVOIR, _moduleName, $"Timing of AutoCrossDose has started again now");
  159. RecordStartData();
  160. }
  161. }
  162. }
  163. else
  164. {
  165. ResetDoseOperation();
  166. }
  167. //AutoDosing监控
  168. if (CrossDoseState != RState.Running && _currentCrossDoseOperation == CrossDoseOperation.AutoDosing && AutoDosingMonitor())
  169. {
  170. StartDosing(DOSING_VOLUME);
  171. }
  172. }
  173. /// <summary>
  174. /// CrossDoseOperation状态监控
  175. /// </summary>
  176. public bool CrossDoseOperationMonitor()
  177. {
  178. if (_currentCrossDoseOperation == CrossDoseOperation.ManualDosing && CrossDoseState != RState.Running)
  179. {
  180. _currentCrossDoseOperation = CrossDoseOperation.None;
  181. }
  182. if (_currentCrossDoseOperation == CrossDoseOperation.AutoDosing)
  183. {
  184. _currentCrossDoseOperation = CrossDoseOperation.None;
  185. }
  186. return true;
  187. }
  188. /// <summary>
  189. /// 自动补液触发时机判断
  190. /// </summary>
  191. /// <returns></returns>
  192. private bool AutoDosingMonitor()
  193. {
  194. ResRecipe recipe = _cmReservoirDevice.Recipe;
  195. bool result = false;
  196. double targetVolume = 0;
  197. double currentDosingAmpHour = 0;
  198. _reservoirUsage = ReservoirUsageManager.Instance.GetReservoirUsage(_moduleName);
  199. //当前电量
  200. if (_reservoirUsage != null) currentDosingAmpHour = _reservoirUsage.TotalUsage;
  201. //累计电量(Ah)
  202. double deltAmpHour = currentDosingAmpHour - _persistentValue.AutoCrossDoseStartAmpHour;
  203. //累计时间(Day)
  204. double deltDay = DateTime.Now.Subtract(_persistentValue.AutoCrossDoseStartTime).TotalDays;
  205. if (recipe.CurrentBased)
  206. {
  207. //基于电量补液
  208. targetVolume = deltAmpHour * recipe.CrossDoseCurrentRate;
  209. _persistentValue.TargetDosingVolume = Math.Round(targetVolume, 2);
  210. if (targetVolume >= DOSING_VOLUME)
  211. {
  212. result = true;
  213. }
  214. }
  215. else if (recipe.TimeBased)
  216. {
  217. //基于时间则开始补液
  218. targetVolume = deltDay * recipe.CrossDoseTimeRate;
  219. _persistentValue.TargetDosingVolume = Math.Round(targetVolume, 2);
  220. if (targetVolume >= DOSING_VOLUME)
  221. {
  222. result = true;
  223. }
  224. }
  225. ReservoirsPersistentManager.Instance.UpdatePersistentValue(_moduleName);
  226. return result;
  227. }
  228. /// <summary>
  229. /// 设置手动Dose模式
  230. /// </summary>
  231. public void SetManualDoseOperation()
  232. {
  233. _currentCrossDoseOperation = CrossDoseOperation.ManualDosing;
  234. }
  235. /// <summary>
  236. /// 设置自动Dose模式
  237. /// </summary>
  238. public void SetAutoDoseOperation()
  239. {
  240. _currentCrossDoseOperation = CrossDoseOperation.AutoDosing;
  241. }
  242. /// <summary>
  243. /// 重置Dose模式
  244. /// </summary>
  245. public void ResetDoseOperation()
  246. {
  247. _currentCrossDoseOperation = CrossDoseOperation.None;
  248. }
  249. /// <summary>
  250. /// EnterError
  251. /// </summary>
  252. private bool EnterError()
  253. {
  254. string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{_moduleName}.TransferPumpExecute");
  255. bool result = IOModuleManager.Instance.WriteIoValue(ioName, false);
  256. if (!result)
  257. {
  258. return false;
  259. }
  260. ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{_moduleName}.TransferPumpEnable");
  261. result = IOModuleManager.Instance.WriteIoValue(ioName, false);
  262. if (!result)
  263. {
  264. return false;
  265. }
  266. if (!_cmReservoirDevice.CrossDoseOff("", null))
  267. {
  268. LOG.WriteLog(eEvent.WARN_RESERVOIR, _moduleName, $"Close CrossDose Valve failed");
  269. return false;
  270. }
  271. return true;
  272. }
  273. /// <summary>
  274. /// 记录AutoCrossDose开始时的时间和电量
  275. /// </summary>
  276. private void RecordStartData()
  277. {
  278. _reservoirUsage = ReservoirUsageManager.Instance.GetReservoirUsage(_moduleName);
  279. _persistentValue.AutoCrossDoseStartTime = DateTime.Now;
  280. if (_reservoirUsage != null) _persistentValue.AutoCrossDoseStartAmpHour = _reservoirUsage.TotalUsage;
  281. ReservoirsPersistentManager.Instance.UpdatePersistentValue(_moduleName);
  282. }
  283. /// <summary>
  284. /// 设置PumpFactor
  285. /// </summary>
  286. public void SetPumpfactor(double targetPumpFactor)
  287. {
  288. _persistentValue.CrossDosePumpFactor = targetPumpFactor;
  289. ReservoirsPersistentManager.Instance.UpdatePersistentValue(_moduleName);
  290. }
  291. /// <summary>
  292. /// Reset Start
  293. /// </summary>
  294. public bool ResetCrossDose()
  295. {
  296. return _crossDoseResetRoutine.Start() == RState.Running;
  297. }
  298. /// <summary>
  299. /// Reset Start Monitor
  300. /// </summary>
  301. /// <returns></returns>
  302. public bool ResetCrossDoseMonitor()
  303. {
  304. RState result = _crossDoseResetRoutine.Monitor();
  305. if (result == RState.Failed || result == RState.Timeout)
  306. {
  307. LOG.WriteLog(eEvent.WARN_RESERVOIR, _moduleName, "Reset Cross Dose failed");
  308. return false;
  309. }
  310. return result == RState.End;
  311. }
  312. #endregion
  313. }
  314. }