using System; using System.Linq; using SHJX.Service.Dao; using System.Threading; using System.Diagnostics; using SHJX.Service.Model.Dao; using SHJX.Service.Model.Control; using System.Collections.Generic; using SHJX.Service.Common.Camera; using SHJX.Service.Control.Modules; using SHJX.Service.Common.Calculate; using SHJX.Service.Control.Interface; using SHJX.Service.Common.UserDelegate; using SHJX.Service.Common.Logging; using Microsoft.Extensions.Logging; using SHJX.Service.Common.ReadXML; namespace SHJX.Service.Control.Route.RouteController { /// /// 开始滴定 /// /// 硫酸亚铁铵 public class TitrationOperate : FlowControlOperateImp { private static readonly ILogger logger = LogFactory.BuildLogger(typeof(TitrationOperate)); private const string OpName = "FAS"; private int _signalTimeLen; private int _titrationCount; private double _amountValue; private DropLiquid _liquid; private ColorDataGraber _graber; private WriteWay _concentrationType; private TitrationValue _balanceVals; private OperateDataManager _dataManager; private string DripNozzleType = "DripNozzle"; public virtual bool Operate(ReadConfigUtil config, DataEventArgs data) { _graber = data.LiquidDrop.Graber; //if (data.Task.SampleConcentration == "High" || data.Task.To == "D4") // { DripNozzleType = "DripNozzle2"; // _graber = data.LiquidDrop2.Graber; //} _liquid = data.DataManager.QueryLiquid(OpName); if (!_liquid.Enable) { var dripNozzleRes = DataCentre.GetStorageContent.Factory(DripNozzleType).Stop(); return dripNozzleRes; } _signalTimeLen = 10; _titrationCount = 0; _dataManager = data.DataManager; _balanceVals = DataCentre.GetBalanceValue; var res = data.Task.RouteType switch { "Wash" => WashLiquid(data), _ => SampleDropLiquid(data), }; DataCentre.GetStorageContent.Factory(DripNozzleType).Stop(); // WriteDrip(data); return res; } /// /// 润洗 && 清洗 /// /// /// private bool WashLiquid(DataEventArgs data) { logger.LogInformation("进入润洗(清洗)流程"); var value = data.Task.TaskType switch { "润洗" => _liquid.WashVolume, "清洗" => _liquid.ClearVolume, _ => throw new ArgumentNullException(data.Task.TaskType), }; string[] types = new string[] { "Low", "High" }; bool res = true; foreach (var item in types) { (_concentrationType, _amountValue) = item switch { "Low" => (WriteWay.Normotopia, DataCentre.GetConfig.DropOnceAmounts.FirstOrDefault(item => item.Name.Equals("FAS")).LowValue), "High" => (WriteWay.Antiposition, DataCentre.GetConfig.DropOnceAmounts.FirstOrDefault(item => item.Name.Equals("FAS")).HighValue), _ => throw new ArgumentNullException(data.Task.SampleConcentration), }; ConvertConcentration(_concentrationType); var distance = Convert.ToInt64(Math.Round(value / _amountValue * 20000)); var portArgs = new PortArgs() { TypeName = OpName, WriteWay = WriteWay.Move, Distance = distance }; if (data.Task.TaskType== "清洗") { res = res && DataCentre.GetClient.Factory(OpName).Write(portArgs); Thread.Sleep((int)(distance / 20000 * 600)); } if (data.Task.TaskType == "润洗") { portArgs.Distance *= -1; res = res && DataCentre.GetClient.Factory(OpName).Write(portArgs); Thread.Sleep((int)(distance / 20000 * 600)); portArgs.Distance *= -11/10; res = res && DataCentre.GetClient.Factory(OpName).Write(portArgs); Thread.Sleep((int)(distance / 20000 * 700)); } string liquidname = "FAS"; if (data.Task.SampleConcentration == "High") { liquidname = "FAS2"; } LiquidTotal liquid1 = DataCentre._dataManager.Query().Where(it => it.LiquidName.Equals(liquidname)).First(); liquid1.Total -= value; //硫酸亚铁铵(高)减去20体积 res = res && DataCentre._dataManager.Update(liquid1) > 0; } logger.LogInformation("润洗(清洗)流程完成"); return res; } /// /// 液体滴定 /// /// /// private bool SampleDropLiquid(DataEventArgs data) { (_concentrationType, _amountValue) = data.Task.SampleConcentration switch { "Low" => (WriteWay.Normotopia, DataCentre.GetConfig.DropOnceAmounts.FirstOrDefault(item => item.Name.Equals("FAS")).LowValue), "High" => (WriteWay.Antiposition, DataCentre.GetConfig.DropOnceAmounts.FirstOrDefault(item => item.Name.Equals("FAS")).HighValue), _ => throw new ArgumentNullException(data.Task.SampleConcentration), }; ConvertConcentration(_concentrationType); var res = true; data.LiquidDrop.Camera.OpenCamera(); using (TitrationStirOperate ts = new()) { if (ts.StartStir) { Thread.Sleep(1000 * _balanceVals.BeforeWaitTime);//滴定之前等待时间 var task = data.Task; AdjustCamera(task.SampleConcentration); StartDropLiquid(ref task); data.Task = task; CalculateResult(task); //滴定完成需做计算 } else { res = ts.StartStir; } } string liquidname = "FAS"; if (data.Task.SampleConcentration == "High") { liquidname = "FAS2"; } LiquidTotal liquid1 = DataCentre._dataManager.Query().Where(it => it.LiquidName.Equals(liquidname)).First(); liquid1.Total -= data.Task.Amount; //硫酸亚铁铵(低)减去滴定体积 res = res && DataCentre._dataManager.Update(liquid1) > 0; Messager.Send("DropEnd"); return res; } /// /// 液体滴定 /// /// /// public bool ManualSampleDropLiquid(Dictionary data1) { EquipmentTask task1 = new EquipmentTask(); var data2 = task1.GetDataArgs(); data2.Task.QuickTitration = Convert.ToBoolean(data1["IfFastDiding"]); _graber = data2.LiquidDrop.Graber; _liquid = data2.DataManager.QueryLiquid(OpName); if (!_liquid.Enable) { var dripNozzleRes = DataCentre.GetStorageContent.Factory(DripNozzleType).Stop(); return dripNozzleRes; } _signalTimeLen = 10; _titrationCount = 0; _dataManager = data2.DataManager; _balanceVals = DataCentre.GetBalanceValue; _balanceVals.LowValue = Convert.ToDouble(data1["YuZhiValue"]); _balanceVals.HighValue = Convert.ToDouble(data1["YuZhiValue"]); _balanceVals.MaxVolume = Convert.ToDouble(data1["MaxTiJiValue"]); _balanceVals.AfterWaitTime = Convert.ToInt32(data1["CheckTimeValue"]); _balanceVals.QuickTitrationValue = Convert.ToDouble(data1["FastDidingValue"]); data2.Task.SampleConcentration = data1["NongDuValue"] switch { "低" =>"Low", "高" =>"High", _ => throw new ArgumentNullException(data1["NongDuValue"]), }; (_concentrationType, _amountValue) = data2.Task.SampleConcentration switch { "Low" => (WriteWay.Normotopia, Convert.ToDouble(data1["DropAmountValue"])), "High" => (WriteWay.Antiposition, Convert.ToDouble(data1["DropAmountValue"])), _ => throw new ArgumentNullException(data2.Task.SampleConcentration), }; ConvertConcentration(_concentrationType); var res = true; data2.LiquidDrop.Camera.OpenCamera(); using (TitrationStirOperate ts = new()) { if (ts.StartStir) { Thread.Sleep(1000 * Convert.ToInt32( data1["DengDaiTimeValue"] ));//滴定之前等待时间 var task = data2.Task; AdjustCamera(task.SampleConcentration); StartDropLiquid(ref task); data2.Task = task; CalculateResult(task); //滴定完成需做计算 } else { res = ts.StartStir; } } LiquidTotal liquid1 = DataCentre._dataManager.Query().Where(it => it.LiquidName.Equals("FAS")).First(); liquid1.Total -= data2.Task.Amount; //硫酸亚铁铵(低)减去滴定体积 res = res && DataCentre._dataManager.Update(liquid1) > 0; Messager.Send("DropEnd"); Messager.Send("DropEndWindows"); return res; } /// /// 滴定开始 /// private void StartDropLiquid(ref EquipmentTask task) { try { var jumpCount = 0; //突跃次数 var slowSpeedCount = 0; //减速次数 var isRunning = true; var balanceVal = GetBalanceVal(task); var currSignals = new List(); // 信号队列 for (var i = 0; i < 7; i++) { currSignals.Add(_graber.GrabSignal(500)); // 采集7段信号作为背景 } PortArgs portArgs = new() { TypeName = OpName, WriteWay = WriteWay.Move }; //快速滴定 if (task.QuickTitration) { var quickValue = Convert.ToInt32(Math.Round(_balanceVals.QuickTitrationValue / _amountValue * 20000)); portArgs.Distance = quickValue; var res = true; do { res = DataCentre.GetClient.Factory(OpName).Write(portArgs); if (res) { task.Amount += _balanceVals.QuickTitrationValue; } logger.LogInformation($"快速滴定一次性写入{quickValue},写入{(res ? "成功" : "失败")}"); } while (!res); Thread.Sleep(5 * 1000); logger.LogInformation("等待五秒"); } while (true) { if (!isRunning) break; if (task.Amount >= _balanceVals.MaxVolume) break; #region 滴液 portArgs.Distance = 20000; var res = DataCentre.GetClient.Factory(OpName).Write(portArgs); if (!res) { logger.LogInformation("写入失败,等待两秒后再次写入"); Thread.Sleep(2000); continue; } _titrationCount += 1; task.Amount += _amountValue; #endregion #region 摄像头采集信号 if (_titrationCount.Equals(currSignals.Count - 7)) { currSignals[currSignals.Count - 1] = _graber.GrabSignal(_signalTimeLen); } else { currSignals.Add(_graber.GrabSignal(_signalTimeLen)); } #endregion #region 颜色信号判断 if (currSignals.Count >= 7) // 信号判断 { var currpt = currSignals.Count - 7; // 计算起点 var subB = (currSignals[currpt + 6].GetBSV() - (currSignals[currpt + 0].GetBSV() + currSignals[currpt + 1].GetBSV()) / 2) * 100 / currSignals[currpt + 6].GetBSV(); logger.LogInformation($"当前第{_titrationCount}滴,体积为:{ task.Amount},滴定阈值获取为{subB}"); Dictionary dropValueArg = new() { { "Count", _titrationCount }, { "Amount", task.Amount }, { "BlueValue", subB } }; Messager.Send("DropValue", dropValueArg); if (subB <= balanceVal) { jumpCount++; var swten = new Stopwatch(); swten.Start(); var retreatCount = 1; //后退次数 while (true) // 继续采集信号,监测是否褪色 { currSignals[currpt + 6] = _graber.GrabSignal(_signalTimeLen); subB = (currSignals[currpt + 6].GetBSV() - (currSignals[currpt + 0].GetBSV() + currSignals[currpt + 1].GetBSV()) / 2) * 100 / currSignals[currpt + 6].GetBSV(); logger.LogInformation($"检测已到达设定阈值,观察是否褪色,观察阈值为{subB}"); if (subB <= balanceVal * 0.7) { retreatCount++; if (retreatCount > _balanceVals.AfterWaitTime) { break; } isRunning = false; Thread.Sleep(1000); continue; // 未褪色,继续监测 } isRunning = true; //当检测到的信号值 大于阈值时,则滴定未结束,需要继续滴定 if (retreatCount > 3) { slowSpeedCount = 10; //如果回退超过三次判定,则给定慢速10次 } break; } swten.Stop(); } else { jumpCount = jumpCount > 0 ? jumpCount - 1 : 0; slowSpeedCount = slowSpeedCount > 0 ? slowSpeedCount - 1 : slowSpeedCount; } } #endregion Thread.Sleep((jumpCount <= 1 && slowSpeedCount <= 1) ? 800 : 3000); } } catch (Exception ex) { throw new Exception(ex.Message); } } /// /// 获取液体滴定阈值 /// private double GetBalanceVal(EquipmentTask task) { var balanceVal = task.SampleConcentration switch { "Low" => _balanceVals.LowValue * -1, "High" => _balanceVals.HighValue * -1, _ => throw new ArgumentNullException(task.SampleConcentration) }; return balanceVal; } /// /// 摄像头调整 /// private void AdjustCamera(string level) { _graber.IsNoFiredEvent = true; // 调节曝光参数 if (TitrationSettings.SensorPara.UseAutoUpdateRoiPos) { _graber.UpdateRoiPos(); } switch (level) { case "Low": _graber.PresetLightParam(); break; case "High": _graber.WebCam.SetProperties(AForge.Video.DirectShow.CameraControlProperty.Exposure, -3, AForge.Video.DirectShow.CameraControlFlags.Manual); Thread.Sleep(2000); _graber.WebCam.GetProperties(AForge.Video.DirectShow.CameraControlProperty.Exposure, out var value, out var flags); logger.LogInformation($"高浓度设置了曝光值 ---> -2,当前曝光值为:{value}"); break; } _graber.IsNoFiredEvent = false; } /// /// 切换高低浓度 /// /// private bool ConvertConcentration(WriteWay way) { PortArgs switchArg = new() { TypeName = OpName, WriteWay = way }; return DataCentre.GetClient.Factory(OpName).Write(switchArg); } private static bool ConvertConcentrationM(WriteWay way) { PortArgs switchArg = new() { TypeName = OpName, WriteWay = way }; return DataCentre.GetClient.Factory(OpName).Write(switchArg); } /// /// 计算结果 /// private bool CalculateResult(EquipmentTask task) { try { task.Result = task.TaskType switch { "水样" => GetSampleResult(task), "空白" => 0, "标定" => 0, _ => throw new ArgumentNullException(task.TaskType), }; task.Result = Math.Round(task.Result, 2); //这里是要在界面显示的样子 task.Result= task.Result < 0 ? 0 : task.Result; task.Udf1 = task.SampleConcentration.Equals("Low") ? 0 < task.Result && task.Result < 4 ? "ND" : task.Result.ToString() : task.Result.ToString(); //task.Udf1 = task.Result.ToString(); task.Amount = Convert.ToDouble(Math.Round((decimal)task.Amount, 2)); _dataManager.Update(task); var resultArg = new Dictionary { {"LocalName", task.OriginLocalName}, {"Volume", Math.Round((decimal)task.Amount, 2).ToString()}, {"Result", task.Udf1} }; //通知更新 Messager.Send("SampleResult", resultArg); return true; } catch (Exception) { return false; } } /// /// 获取水样的结果 /// /// /// /// 计算公式 :ρ=((V * ( V0 - V1 ) * 8000 )/ V2 )* f public double GetSampleResult(EquipmentTask task) { var blankTasks = _dataManager.GetTaskByWaveKey(task.WaveKey, task.SampleConcentration, "空白") ?? _dataManager.GetLastOtherTaskByWaveKey("空白", task.SampleConcentration); SampleCalculate.tempCount = 0; blankTasks.GeBlankTaskResult(_balanceVals.MaxVolume, out var noneValue); var bdTasks = _dataManager.GetTaskByWaveKey(task.WaveKey, task.SampleConcentration, "标定") ?? _dataManager.GetLastOtherTaskByWaveKey("标定", task.SampleConcentration); bdTasks.GetBDTaskResult(_balanceVals.MaxVolume, task.SampleConcentration, out var bdValue); //var bdValue = 0.0; var Taskvolumn = Math.Round(task.Amount, 2); //计算参数 C:硫酸亚铁铵浓度 double avgValue = bdValue * (noneValue - Taskvolumn) * 8000 / 10 * task.GetSampleMultiple; return avgValue; } #if false /// /// 获取空白样的结果 /// /// /// private void GetNoneTaskResult(List noneTasks, out double noneValue) { if (noneTasks?.Count > 1) { var noneMaxValue = noneTasks.Max(item => item.Amount); var noneMinValue = noneTasks.Min(item => item.Amount); if (noneMaxValue - noneMinValue <= 0.5) { noneValue = noneTasks.Average(item => item.Amount); return; } if (noneMaxValue >= _balanceVals.MaxVolume) { GetNoneTaskResult(noneTasks.Where(item => item.Amount != noneMaxValue).ToList(), out noneValue); } else { noneValue = noneMaxValue; } } else { var first = noneTasks?.FirstOrDefault(); noneValue = first?.Amount ?? 0; } } #endif } }