TitrationOperate3.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. using System;
  2. using System.Linq;
  3. using SHJX.Service.Dao;
  4. using System.Threading;
  5. using System.Diagnostics;
  6. using SHJX.Service.Model.Dao;
  7. using SHJX.Service.Model.Control;
  8. using System.Collections.Generic;
  9. using SHJX.Service.Common.Camera;
  10. using SHJX.Service.Control.Modules;
  11. using SHJX.Service.Common.Calculate;
  12. using SHJX.Service.Control.Interface;
  13. using SHJX.Service.Common.UserDelegate;
  14. using SHJX.Service.Common.Logging;
  15. using Microsoft.Extensions.Logging;
  16. using SHJX.Service.Common.ReadXML;
  17. namespace SHJX.Service.Control.Route.RouteController
  18. {
  19. /// <summary>
  20. /// 开始滴定
  21. /// </summary>
  22. /// 硫酸亚铁铵
  23. public class TitrationOperate : FlowControlOperateImp
  24. {
  25. private static readonly ILogger logger = LogFactory.BuildLogger(typeof(TitrationOperate));
  26. private const string OpName = "FAS";
  27. private int _signalTimeLen;
  28. private int _titrationCount;
  29. private double _amountValue;
  30. private DropLiquid _liquid;
  31. private ColorDataGraber _graber;
  32. private WriteWay _concentrationType;
  33. private TitrationValue _balanceVals;
  34. private OperateDataManager _dataManager;
  35. private string DripNozzleType = "DripNozzle";
  36. public virtual bool Operate(ReadConfigUtil config, DataEventArgs data)
  37. {
  38. _graber = data.LiquidDrop.Graber;
  39. //if (data.Task.SampleConcentration == "High" || data.Task.To == "D4")
  40. // { DripNozzleType = "DripNozzle2";
  41. // _graber = data.LiquidDrop2.Graber;
  42. //}
  43. _liquid = data.DataManager.QueryLiquid(OpName);
  44. if (!_liquid.Enable)
  45. {
  46. var dripNozzleRes = DataCentre.GetStorageContent.Factory(DripNozzleType).Stop();
  47. return dripNozzleRes;
  48. }
  49. _signalTimeLen = 10;
  50. _titrationCount = 0;
  51. _dataManager = data.DataManager;
  52. _balanceVals = DataCentre.GetBalanceValue;
  53. var res = data.Task.RouteType switch
  54. {
  55. "Wash" => WashLiquid(data),
  56. _ => SampleDropLiquid(data),
  57. };
  58. DataCentre.GetStorageContent.Factory(DripNozzleType).Stop(); // WriteDrip(data);
  59. return res;
  60. }
  61. /// <summary>
  62. /// 润洗 && 清洗
  63. /// </summary>
  64. /// <param name="data"></param>
  65. /// <returns></returns>
  66. private bool WashLiquid(DataEventArgs data)
  67. {
  68. logger.LogInformation("进入润洗(清洗)流程");
  69. var value = data.Task.TaskType switch
  70. {
  71. "润洗" => _liquid.WashVolume,
  72. "清洗" => _liquid.ClearVolume,
  73. _ => throw new ArgumentNullException(data.Task.TaskType),
  74. };
  75. string[] types = new string[] { "Low", "High" };
  76. bool res = true;
  77. foreach (var item in types)
  78. {
  79. (_concentrationType, _amountValue) = item switch
  80. {
  81. "Low" => (WriteWay.Normotopia, DataCentre.GetConfig.DropOnceAmounts.FirstOrDefault(item => item.Name.Equals("FAS")).LowValue),
  82. "High" => (WriteWay.Antiposition, DataCentre.GetConfig.DropOnceAmounts.FirstOrDefault(item => item.Name.Equals("FAS")).HighValue),
  83. _ => throw new ArgumentNullException(data.Task.SampleConcentration),
  84. };
  85. ConvertConcentration(_concentrationType);
  86. var distance = Convert.ToInt64(Math.Round(value / _amountValue * 20000));
  87. var portArgs = new PortArgs()
  88. {
  89. TypeName = OpName,
  90. WriteWay = WriteWay.Move,
  91. Distance = distance
  92. };
  93. if (data.Task.TaskType== "清洗")
  94. {
  95. res = res && DataCentre.GetClient.Factory(OpName).Write(portArgs);
  96. Thread.Sleep((int)(distance / 20000 * 600));
  97. }
  98. if (data.Task.TaskType == "润洗")
  99. {
  100. portArgs.Distance *= -1;
  101. res = res && DataCentre.GetClient.Factory(OpName).Write(portArgs);
  102. Thread.Sleep((int)(distance / 20000 * 600));
  103. portArgs.Distance *= -11/10;
  104. res = res && DataCentre.GetClient.Factory(OpName).Write(portArgs);
  105. Thread.Sleep((int)(distance / 20000 * 700));
  106. }
  107. string liquidname = "FAS";
  108. if (data.Task.SampleConcentration == "High")
  109. {
  110. liquidname = "FAS2";
  111. }
  112. LiquidTotal liquid1 = DataCentre._dataManager.Query<LiquidTotal>().Where(it => it.LiquidName.Equals(liquidname)).First();
  113. liquid1.Total -= value; //硫酸亚铁铵(高)减去20体积
  114. res = res && DataCentre._dataManager.Update<LiquidTotal>(liquid1) > 0;
  115. }
  116. logger.LogInformation("润洗(清洗)流程完成");
  117. return res;
  118. }
  119. /// <summary>
  120. /// 液体滴定
  121. /// </summary>
  122. /// <param name="data"></param>
  123. /// <returns></returns>
  124. private bool SampleDropLiquid(DataEventArgs data)
  125. {
  126. (_concentrationType, _amountValue) = data.Task.SampleConcentration switch
  127. {
  128. "Low" => (WriteWay.Normotopia, DataCentre.GetConfig.DropOnceAmounts.FirstOrDefault(item => item.Name.Equals("FAS")).LowValue),
  129. "High" => (WriteWay.Antiposition, DataCentre.GetConfig.DropOnceAmounts.FirstOrDefault(item => item.Name.Equals("FAS")).HighValue),
  130. _ => throw new ArgumentNullException(data.Task.SampleConcentration),
  131. };
  132. ConvertConcentration(_concentrationType);
  133. var res = true;
  134. data.LiquidDrop.Camera.OpenCamera();
  135. using (TitrationStirOperate ts = new())
  136. {
  137. if (ts.StartStir)
  138. {
  139. Thread.Sleep(1000 * _balanceVals.BeforeWaitTime);//滴定之前等待时间
  140. var task = data.Task;
  141. AdjustCamera(task.SampleConcentration);
  142. StartDropLiquid(ref task);
  143. data.Task = task;
  144. CalculateResult(task); //滴定完成需做计算
  145. }
  146. else
  147. {
  148. res = ts.StartStir;
  149. }
  150. }
  151. string liquidname = "FAS";
  152. if (data.Task.SampleConcentration == "High")
  153. {
  154. liquidname = "FAS2";
  155. }
  156. LiquidTotal liquid1 = DataCentre._dataManager.Query<LiquidTotal>().Where(it => it.LiquidName.Equals(liquidname)).First();
  157. liquid1.Total -= data.Task.Amount; //硫酸亚铁铵(低)减去滴定体积
  158. res = res && DataCentre._dataManager.Update<LiquidTotal>(liquid1) > 0;
  159. Messager.Send("DropEnd");
  160. return res;
  161. }
  162. /// <summary>
  163. /// 液体滴定
  164. /// </summary>
  165. /// <param name="data"></param>
  166. /// <returns></returns>
  167. public bool ManualSampleDropLiquid(Dictionary<string ,string> data1)
  168. {
  169. EquipmentTask task1 = new EquipmentTask();
  170. var data2 = task1.GetDataArgs();
  171. data2.Task.QuickTitration = Convert.ToBoolean(data1["IfFastDiding"]);
  172. _graber = data2.LiquidDrop.Graber;
  173. _liquid = data2.DataManager.QueryLiquid(OpName);
  174. if (!_liquid.Enable)
  175. {
  176. var dripNozzleRes = DataCentre.GetStorageContent.Factory(DripNozzleType).Stop();
  177. return dripNozzleRes;
  178. }
  179. _signalTimeLen = 10;
  180. _titrationCount = 0;
  181. _dataManager = data2.DataManager;
  182. _balanceVals = DataCentre.GetBalanceValue;
  183. _balanceVals.LowValue = Convert.ToDouble(data1["YuZhiValue"]);
  184. _balanceVals.HighValue = Convert.ToDouble(data1["YuZhiValue"]);
  185. _balanceVals.MaxVolume = Convert.ToDouble(data1["MaxTiJiValue"]);
  186. _balanceVals.AfterWaitTime = Convert.ToInt32(data1["CheckTimeValue"]);
  187. _balanceVals.QuickTitrationValue = Convert.ToDouble(data1["FastDidingValue"]);
  188. data2.Task.SampleConcentration = data1["NongDuValue"] switch
  189. {
  190. "低" =>"Low",
  191. "高" =>"High",
  192. _ => throw new ArgumentNullException(data1["NongDuValue"]),
  193. };
  194. (_concentrationType, _amountValue) = data2.Task.SampleConcentration switch
  195. {
  196. "Low" => (WriteWay.Normotopia, Convert.ToDouble(data1["DropAmountValue"])),
  197. "High" => (WriteWay.Antiposition, Convert.ToDouble(data1["DropAmountValue"])),
  198. _ => throw new ArgumentNullException(data2.Task.SampleConcentration),
  199. };
  200. ConvertConcentration(_concentrationType);
  201. var res = true;
  202. data2.LiquidDrop.Camera.OpenCamera();
  203. using (TitrationStirOperate ts = new())
  204. {
  205. if (ts.StartStir)
  206. {
  207. Thread.Sleep(1000 * Convert.ToInt32( data1["DengDaiTimeValue"] ));//滴定之前等待时间
  208. var task = data2.Task;
  209. AdjustCamera(task.SampleConcentration);
  210. StartDropLiquid(ref task);
  211. data2.Task = task;
  212. CalculateResult(task); //滴定完成需做计算
  213. }
  214. else
  215. {
  216. res = ts.StartStir;
  217. }
  218. }
  219. LiquidTotal liquid1 = DataCentre._dataManager.Query<LiquidTotal>().Where(it => it.LiquidName.Equals("FAS")).First();
  220. liquid1.Total -= data2.Task.Amount; //硫酸亚铁铵(低)减去滴定体积
  221. res = res && DataCentre._dataManager.Update<LiquidTotal>(liquid1) > 0;
  222. Messager.Send("DropEnd");
  223. Messager.Send("DropEndWindows");
  224. return res;
  225. }
  226. /// <summary>
  227. /// 滴定开始
  228. /// </summary>
  229. private void StartDropLiquid(ref EquipmentTask task)
  230. {
  231. try
  232. {
  233. var jumpCount = 0; //突跃次数
  234. var slowSpeedCount = 0; //减速次数
  235. var isRunning = true;
  236. var balanceVal = GetBalanceVal(task);
  237. var currSignals = new List<ColorDataPoint>(); // 信号队列
  238. for (var i = 0; i < 7; i++)
  239. {
  240. currSignals.Add(_graber.GrabSignal(500)); // 采集7段信号作为背景
  241. }
  242. PortArgs portArgs = new()
  243. {
  244. TypeName = OpName,
  245. WriteWay = WriteWay.Move
  246. };
  247. //快速滴定
  248. if (task.QuickTitration)
  249. {
  250. var quickValue = Convert.ToInt32(Math.Round(_balanceVals.QuickTitrationValue / _amountValue * 20000));
  251. portArgs.Distance = quickValue;
  252. var res = true;
  253. do
  254. {
  255. res = DataCentre.GetClient.Factory(OpName).Write(portArgs);
  256. if (res)
  257. {
  258. task.Amount += _balanceVals.QuickTitrationValue;
  259. }
  260. logger.LogInformation($"快速滴定一次性写入{quickValue},写入{(res ? "成功" : "失败")}");
  261. } while (!res);
  262. Thread.Sleep(5 * 1000);
  263. logger.LogInformation("等待五秒");
  264. }
  265. while (true)
  266. {
  267. if (!isRunning) break;
  268. if (task.Amount >= _balanceVals.MaxVolume) break;
  269. #region 滴液
  270. portArgs.Distance = 20000;
  271. var res = DataCentre.GetClient.Factory(OpName).Write(portArgs);
  272. if (!res)
  273. {
  274. logger.LogInformation("写入失败,等待两秒后再次写入");
  275. Thread.Sleep(2000);
  276. continue;
  277. }
  278. _titrationCount += 1;
  279. task.Amount += _amountValue;
  280. #endregion
  281. #region 摄像头采集信号
  282. if (_titrationCount.Equals(currSignals.Count - 7))
  283. {
  284. currSignals[currSignals.Count - 1] = _graber.GrabSignal(_signalTimeLen);
  285. }
  286. else
  287. {
  288. currSignals.Add(_graber.GrabSignal(_signalTimeLen));
  289. }
  290. #endregion
  291. #region 颜色信号判断
  292. if (currSignals.Count >= 7) // 信号判断
  293. {
  294. var currpt = currSignals.Count - 7; // 计算起点
  295. var subB = (currSignals[currpt + 6].GetBSV() - (currSignals[currpt + 0].GetBSV() +
  296. currSignals[currpt + 1].GetBSV()) / 2) * 100 / currSignals[currpt + 6].GetBSV();
  297. logger.LogInformation($"当前第{_titrationCount}滴,体积为:{ task.Amount},滴定阈值获取为{subB}");
  298. Dictionary<string, double> dropValueArg = new()
  299. {
  300. { "Count", _titrationCount },
  301. { "Amount", task.Amount },
  302. { "BlueValue", subB }
  303. };
  304. Messager.Send("DropValue", dropValueArg);
  305. if (subB <= balanceVal)
  306. {
  307. jumpCount++;
  308. var swten = new Stopwatch();
  309. swten.Start();
  310. var retreatCount = 1; //后退次数
  311. while (true) // 继续采集信号,监测是否褪色
  312. {
  313. currSignals[currpt + 6] = _graber.GrabSignal(_signalTimeLen);
  314. subB = (currSignals[currpt + 6].GetBSV() -
  315. (currSignals[currpt + 0].GetBSV() + currSignals[currpt + 1].GetBSV()) / 2) *
  316. 100 / currSignals[currpt + 6].GetBSV();
  317. logger.LogInformation($"检测已到达设定阈值,观察是否褪色,观察阈值为{subB}");
  318. if (subB <= balanceVal * 0.7)
  319. {
  320. retreatCount++;
  321. if (retreatCount > _balanceVals.AfterWaitTime)
  322. {
  323. break;
  324. }
  325. isRunning = false;
  326. Thread.Sleep(1000);
  327. continue; // 未褪色,继续监测
  328. }
  329. isRunning = true; //当检测到的信号值 大于阈值时,则滴定未结束,需要继续滴定
  330. if (retreatCount > 3)
  331. {
  332. slowSpeedCount = 10; //如果回退超过三次判定,则给定慢速10次
  333. }
  334. break;
  335. }
  336. swten.Stop();
  337. }
  338. else
  339. {
  340. jumpCount = jumpCount > 0 ? jumpCount - 1 : 0;
  341. slowSpeedCount = slowSpeedCount > 0 ? slowSpeedCount - 1 : slowSpeedCount;
  342. }
  343. }
  344. #endregion
  345. Thread.Sleep((jumpCount <= 1 && slowSpeedCount <= 1) ? 800 : 3000);
  346. }
  347. }
  348. catch (Exception ex)
  349. {
  350. throw new Exception(ex.Message);
  351. }
  352. }
  353. /// <summary>
  354. /// 获取液体滴定阈值
  355. /// </summary>
  356. private double GetBalanceVal(EquipmentTask task)
  357. {
  358. var balanceVal = task.SampleConcentration switch
  359. {
  360. "Low" => _balanceVals.LowValue * -1,
  361. "High" => _balanceVals.HighValue * -1,
  362. _ => throw new ArgumentNullException(task.SampleConcentration)
  363. };
  364. return balanceVal;
  365. }
  366. /// <summary>
  367. /// 摄像头调整
  368. /// </summary>
  369. private void AdjustCamera(string level)
  370. {
  371. _graber.IsNoFiredEvent = true; // 调节曝光参数
  372. if (TitrationSettings.SensorPara.UseAutoUpdateRoiPos)
  373. {
  374. _graber.UpdateRoiPos();
  375. }
  376. switch (level)
  377. {
  378. case "Low":
  379. _graber.PresetLightParam();
  380. break;
  381. case "High":
  382. _graber.WebCam.SetProperties(AForge.Video.DirectShow.CameraControlProperty.Exposure, -3, AForge.Video.DirectShow.CameraControlFlags.Manual);
  383. Thread.Sleep(2000);
  384. _graber.WebCam.GetProperties(AForge.Video.DirectShow.CameraControlProperty.Exposure, out var value, out var flags);
  385. logger.LogInformation($"高浓度设置了曝光值 ---> -2,当前曝光值为:{value}");
  386. break;
  387. }
  388. _graber.IsNoFiredEvent = false;
  389. }
  390. /// <summary>
  391. /// 切换高低浓度
  392. /// </summary>
  393. /// <param name="way"></param>
  394. private bool ConvertConcentration(WriteWay way)
  395. {
  396. PortArgs switchArg = new()
  397. {
  398. TypeName = OpName,
  399. WriteWay = way
  400. };
  401. return DataCentre.GetClient.Factory(OpName).Write(switchArg);
  402. }
  403. private static bool ConvertConcentrationM(WriteWay way)
  404. {
  405. PortArgs switchArg = new()
  406. {
  407. TypeName = OpName,
  408. WriteWay = way
  409. };
  410. return DataCentre.GetClient.Factory(OpName).Write(switchArg);
  411. }
  412. /// <summary>
  413. /// 计算结果
  414. /// </summary>
  415. private bool CalculateResult(EquipmentTask task)
  416. {
  417. try
  418. {
  419. task.Result = task.TaskType switch
  420. {
  421. "水样" => GetSampleResult(task),
  422. "空白" => 0,
  423. "标定" => 0,
  424. _ => throw new ArgumentNullException(task.TaskType),
  425. };
  426. task.Result = Math.Round(task.Result, 2);
  427. //这里是要在界面显示的样子
  428. task.Result= task.Result < 0 ? 0 : task.Result;
  429. task.Udf1 = task.SampleConcentration.Equals("Low") ? 0 < task.Result && task.Result < 4 ? "ND" : task.Result.ToString() : task.Result.ToString();
  430. //task.Udf1 = task.Result.ToString();
  431. task.Amount = Convert.ToDouble(Math.Round((decimal)task.Amount, 2));
  432. _dataManager.Update(task);
  433. var resultArg = new Dictionary<string, string>
  434. {
  435. {"LocalName", task.OriginLocalName},
  436. {"Volume", Math.Round((decimal)task.Amount, 2).ToString()},
  437. {"Result", task.Udf1}
  438. };
  439. //通知更新
  440. Messager.Send("SampleResult", resultArg);
  441. return true;
  442. }
  443. catch (Exception)
  444. {
  445. return false;
  446. }
  447. }
  448. /// <summary>
  449. /// 获取水样的结果
  450. /// </summary>
  451. /// <param name="task"></param>
  452. /// <returns></returns>
  453. /// 计算公式 :ρ=((V * ( V0 - V1 ) * 8000 )/ V2 )* f
  454. public double GetSampleResult(EquipmentTask task)
  455. {
  456. var blankTasks = _dataManager.GetTaskByWaveKey(task.WaveKey, task.SampleConcentration, "空白") ?? _dataManager.GetLastOtherTaskByWaveKey("空白", task.SampleConcentration);
  457. SampleCalculate.tempCount = 0;
  458. blankTasks.GeBlankTaskResult(_balanceVals.MaxVolume, out var noneValue);
  459. var bdTasks = _dataManager.GetTaskByWaveKey(task.WaveKey, task.SampleConcentration, "标定") ?? _dataManager.GetLastOtherTaskByWaveKey("标定", task.SampleConcentration);
  460. bdTasks.GetBDTaskResult(_balanceVals.MaxVolume, task.SampleConcentration, out var bdValue);
  461. //var bdValue = 0.0;
  462. var Taskvolumn = Math.Round(task.Amount, 2);
  463. //计算参数 C:硫酸亚铁铵浓度
  464. double avgValue = bdValue * (noneValue - Taskvolumn) * 8000 / 10 * task.GetSampleMultiple;
  465. return avgValue;
  466. }
  467. #if false
  468. /// <summary>
  469. /// 获取空白样的结果
  470. /// </summary>
  471. /// <param name="noneTasks"></param>
  472. /// <param name="noneValue"></param>
  473. private void GetNoneTaskResult(List<EquipmentTask> noneTasks, out double noneValue)
  474. {
  475. if (noneTasks?.Count > 1)
  476. {
  477. var noneMaxValue = noneTasks.Max(item => item.Amount);
  478. var noneMinValue = noneTasks.Min(item => item.Amount);
  479. if (noneMaxValue - noneMinValue <= 0.5)
  480. {
  481. noneValue = noneTasks.Average(item => item.Amount);
  482. return;
  483. }
  484. if (noneMaxValue >= _balanceVals.MaxVolume)
  485. {
  486. GetNoneTaskResult(noneTasks.Where(item => item.Amount != noneMaxValue).ToList(), out noneValue);
  487. }
  488. else
  489. {
  490. noneValue = noneMaxValue;
  491. }
  492. }
  493. else
  494. {
  495. var first = noneTasks?.FirstOrDefault();
  496. noneValue = first?.Amount ?? 0;
  497. }
  498. }
  499. #endif
  500. }
  501. }