using System.Diagnostics; using System.Runtime.Intrinsics.Arm; using NPOI.SS.Formula.Eval; using shjxCamera; using SHJX.Service.Control.Common.Assets; namespace SHJX.Service.Control.Execute.TitrationController { public class TitrationExecute { private static readonly ILogger logger = LogFactory.BuildLogger(typeof(TitrationExecute)); public static bool Running = true; private static int titrationCount = 0; private static int slowSpeedCount = 0; private static int signalTimeLen = 10; private static int arriveRangeCount = 0; //突跃次数 private static List currSignals = new(); // 信号队列 private static List currSignalsCompare = new(); // 信号队列 private static ColorDataGraber _graber = Globals.Graber; private static double balanceVal = 0; private static bool IsClean = true; private static readonly string titrationCamera = "D1_camera"; #region 滴定 /// /// 滴定 /// /// 阈值 /// 体积 public static void Start(TitrationArg arg, ref double amount) { Messager.Send("TitrationStart"); if (Globals.Camera is null) { logger.LogDebug("11111111未找到摄像头,无法进行滴定操作"); return; } int threshold_gray = 5; // 色点过滤阈值 int threshold_hue = (int)arg.RangeValue; // 色相偏移阈值 int threshold_stable_check = arg.ArriveCheckCount; // 稳定检测时长 //string taskTag = $"{arg.BatchName}/{arg.TaskName}"; //Directory.CreateDirectory($"Data/{taskTag}"); //var csvWriter = new StreamWriter($"Data/{taskTag}.csv"); //csvWriter.Write("滴数,体积,红,绿,蓝,灰度,色调,饱和度,亮度\n"); //csvWriter.Flush(); // 滴定 int titrationCount = 0; List samplePoints = []; // 采样点 logger.LogCritical($"<11111111> 滴定开始(色相偏移阈值: {threshold_hue,2}, 稳定检测时长: {threshold_stable_check})"); // 快速滴定(一般用于标定) if (arg.WhetherQuicklyTitration) { bool quicklyTitrationRes = LiquidPipeSwitchTitration1.StartNew.SetLiquidName(EquipmentNames.Titration1PotassiumPermanganate).CheckWhetherArriveEndPoint().Execute(Convert.ToInt32(Math.Round(arg.QuicklyTitrationValue / arg.LiquidAmount * arg.LiquidConvertRatio))); if (quicklyTitrationRes) { amount += arg.QuicklyTitrationValue; logger.LogCritical("11111111快速滴定下发成功,等待15秒后继续滴定操作!"); Thread.Sleep(5 * 1000); } } if (!ConfigInstance.IsAutomaticInLiquid)//滴定1有自动吸液,滴定2无自动吸液 { int distance = Convert.ToInt32(Math.Round(arg.PumpCapacity / arg.LiquidAmount * arg.LiquidConvertRatio)); LiquidPipeSwitchTitration1.StartNew.SetLiquidName(EquipmentNames.Titration1PotassiumPermanganate).CheckWhetherArriveEndPoint().Execute(LiquidSwitchPattern.In, distance); } // 滴定前等待 logger.LogCritical($"<11111111> 滴定前等待({arg.BeforeWaitTime}s)"); Thread.Sleep(arg.BeforeWaitTime * 1000); // 第0滴颜色采样(背景采样) ColorPoint cp = Globals.Camera.SampleBackground(titrationCamera, threshold_gray, threshold_hue); if (cp.Hue < 10) { Messager.Send("TitrationStop"); logger.LogCritical($"<11111111> 采集信号有误,退出滴定"); return; } logger.LogCritical($"<11111111> 第{titrationCount:D3}滴({amount:F3}), HSB({cp.Hue,3},{cp.Saturation:F3},{cp.Brightness:F3}), RGBL({cp.R,3},{cp.G,3},{cp.B,3},{cp.Gray,3})"); //csvWriter.Write($"{titrationCount:D3},{amount:F3},{cp.R,3},{cp.G,3},{cp.B,3},{cp.Gray,3},{cp.Hue,3},{cp.Saturation:F3},{cp.Brightness:F3}\n"); //csvWriter.Flush(); samplePoints.Add(cp); Messager.Send("TitrationValueChange", new TitrationValueArgs(titrationCount, amount, cp.Hue)); Messager.Send("GraberColorValue", cp.Hue); while (true) { // 达到最大滴定体积,结束滴定 if (amount >= arg.MaxDropVolume) { logger.LogCritical($"<11111111> 滴定结束(最大滴定量)"); break; } bool arriveEndPoint = LiquidPipeSwitchTitration1.StartNew.SetLiquidName(EquipmentNames.Titration1PotassiumPermanganate).IsArriveEndPoint; if (arriveEndPoint) { int distance = Convert.ToInt32(Math.Round(arg.PumpCapacity / arg.LiquidAmount * arg.LiquidConvertRatio)); LiquidPipeSwitchTitration1.StartNew.SetLiquidName(EquipmentNames.Titration1PotassiumPermanganate).Execute(LiquidSwitchPattern.In, distance); } // 滴入试剂 LiquidPipeSwitchTitration1.StartNew.SetLiquidName(EquipmentNames.Titration1PotassiumPermanganate).Execute(LiquidSwitchPattern.Out, arg.LiquidConvertRatio); titrationCount += 1; amount += arg.LiquidAmount; // 第N滴颜色采样 Thread.Sleep(1000); cp = Globals.Camera.Sample($"11111111/{titrationCount:D3}S", threshold_gray, threshold_hue); //csvWriter.Write($"{titrationCount:D3},{amount:F3},{cp.R,3},{cp.G,3},{cp.B,3},{cp.Gray,3},{cp.Hue,3},{cp.Saturation:F3},{cp.Brightness:F3}\n"); //csvWriter.Flush(); samplePoints.Add(cp); logger.LogCritical($"<11111111> 第{titrationCount:D3}滴({amount:F3}), HSB({cp.Hue,3},{cp.Saturation:F3},{cp.Brightness:F3}), RGBL({cp.R,3},{cp.G,3},{cp.B,3},{cp.Gray,3})"); //Application.Current.Dispatcher.BeginInvoke(delegate () //{ // ChartHelper.SaveGraph(samplePoints, taskTag); //}); Messager.Send("TitrationValueChange", new TitrationValueArgs(titrationCount, amount, cp.Hue)); Messager.Send("GraberColorValue", cp.Hue); if (ColorPoint.HueDiff(samplePoints.First(), samplePoints.Last()) < threshold_hue) { continue; } // 稳定判断: 达到阈值设定,进入稳定(褪色)检测 logger.LogCritical($"<11111111> 稳定检测(开始)"); bool stable = false; int chkCnt = 0; int retCnt = 0; Stopwatch sw = new(); sw.Start(); while (true) { // 到达稳定检测截止时限,完成检测 if (sw.ElapsedMilliseconds / 1000 >= threshold_stable_check) { logger.LogCritical($"11111111 稳定检测(完成)"); sw.Stop(); stable = true; break; } // 第N滴第i次稳定检测颜色采样 chkCnt++; Thread.Sleep(1000); cp = Globals.Camera.Sample($"11111111/{titrationCount:D3}T-{chkCnt:D2}", threshold_gray, threshold_hue); //csvWriter.Write($"{titrationCount:D3},{amount:F3},{cp.R,3},{cp.G,3},{cp.B,3},{cp.Gray,3},{cp.Hue,3},{cp.Saturation:F3},{cp.Brightness:F3}\n"); //csvWriter.Flush(); samplePoints.Add(cp); logger.LogCritical($"11111111 稳定检测({chkCnt:D2}), HSB({cp.Hue,3},{cp.Saturation:F3},{cp.Brightness:F3}), RGBL({cp.R,3},{cp.G,3},{cp.B,3},{cp.Gray,3})"); Messager.Send("TitrationValueChange", new TitrationValueArgs(titrationCount, amount, cp.Hue)); Messager.Send("GraberColorValue", cp.Hue); if (ColorPoint.HueDiff(samplePoints.First(), samplePoints.Last()) < threshold_hue) { if (retCnt == 0) { retCnt++; continue; } logger.LogCritical($"11111111稳定检测(中止)"); break; } else retCnt = 0; } // 稳定检测通过,滴定完成 if (stable) { logger.LogCritical($"11111111滴定结束(共{titrationCount:D3}滴, {amount:F3}ml)"); break; } } //csvWriter.Close(); //// 保存图表 //string chartTitle = $"{taskTag} ({amount:F3}ml)"; //Application.Current.Dispatcher.BeginInvoke(delegate () //{ // ChartHelper.SaveGraph(samplePoints, $"{taskTag}", chartTitle); //}); Messager.Send("TitrationStop"); logger.LogCritical($"******************************11111111滴定结束******************************"); //try //{ // Messager.Send("TitrationStart"); // if (Globals.Camera is null) // { // logger.LogDebug("11111111未找到摄像头,无法进行滴定操作"); // return; // } // Running = true; // titrationCount = 0; // slowSpeedCount = 0; // signalTimeLen = 10; // arriveRangeCount = 0; //突跃次数 // currSignals = new(); // 信号队列 // currSignalsCompare = new(); // 信号队列 ////_graber = Globals.Graber; // balanceVal = -1 * arg.RangeValue; // if (!Globals.Camera.IsOpened) // { // Globals.Camera.OpenCamera(); // } // logger.LogCritical($"******************************11111111滴定开始******************************"); // if (arg.WhetherQuicklyTitration) //快速滴定判断 // { // bool quicklyTitrationRes = LiquidPipeSwitchTitration1.StartNew.SetLiquidName(EquipmentNames.Titration1PotassiumPermanganate).CheckWhetherArriveEndPoint().Execute(Convert.ToInt32(Math.Round(arg.QuicklyTitrationValue / arg.LiquidAmount * arg.LiquidConvertRatio))); // if (quicklyTitrationRes) // { // amount += arg.QuicklyTitrationValue; // logger.LogCritical("11111111快速滴定下发成功,等待15秒后继续滴定操作!"); // Thread.Sleep(5 * 1000); // } // } // Thread.Sleep(arg.BeforeWaitTime * 1000); // int distance = 0; // if (!ConfigInstance.IsAutomaticInLiquid)//滴定1有自动吸液,滴定2无自动吸液 // { // distance = Convert.ToInt32(Math.Round(arg.PumpCapacity / arg.LiquidAmount * arg.LiquidConvertRatio)); // LiquidPipeSwitchTitration1.StartNew.SetLiquidName(EquipmentNames.Titration1PotassiumPermanganate).CheckWhetherArriveEndPoint().Execute(LiquidSwitchPattern.In, distance); // } // _graber.PresetLightParammanual(arg.explosion, 10); // _graber.PresetLightParam(-9); // Thread.Sleep(2000); // for (int i = 0; i < 7; i++) // 采集7段信号作为背景 // { // currSignals.Add(_graber.GrabSignal(500)); // currSignalsCompare.Add(currSignals[i]); // } // int times = 3; // while ( JudgeManualTitration(currSignals)) // { // if (times == 0) // { // logger.LogCritical($"*******************11111111检测到异常样本!请尝试手工滴定*******************"); // return; // } // _graber.PresetLightParammanual(arg.explosion, 10); // _graber.PresetLightParam(-9); // Thread.Sleep(2000); // for (int i = 0; i < 7; i++) // 采集7段信号作为背景 // { // currSignals[i] = _graber.GrabSignal(500); // currSignalsCompare[i] = currSignals[i]; // } // times--; // } // if (IsClean) // { // logger.LogCritical($"*******************11111111干净样本滴定算法*******************"); // CleanSampleTitration(arg, ref amount); // } // else // { // logger.LogCritical($"*******************11111111复杂样本滴定算法*******************"); // ComplexSampleTitration(arg, ref amount); // } // //CalibrationTitrationColor(arg, ref amount, titrationCount, balanceVal); //} //catch (Exception ex) //{ // logger.LogError($"11111111滴定异常{DateTime.Now},ErrorInfo:{ex}"); //} //finally //{ // Messager.Send("TitrationStop"); // logger.LogCritical($"******************************11111111滴定结束******************************"); //} } private static bool JudgeManualTitration(List currSignals) { double max = currSignals[0].GetRSV() ; double min = currSignals[0].GetRSV(); for (int i = 1; i < currSignals.Count; i++) { if (currSignals[i].GetRSV() > max) max = currSignals[i].GetRSV(); if (currSignals[i].GetRSV() < min) min = currSignals[i].GetRSV(); } IsClean = max - min < 1.5 ? true : false; if (max - min == 0 || min < 110 || max > 120) return true; else return false; } private static void CleanSampleTitration(TitrationArg arg, ref double amount) { ColorDataPoint subRcurrSignals = new ColorDataPoint(); while (true) { if (currSignals.Count >= 7) // 信号判断 { double subR = 0.0; int currpt = currSignals.Count - 6; subRcurrSignals = _graber.GrabSignal(signalTimeLen); currSignalsCompare.Add(subRcurrSignals); subR = (subRcurrSignals.GetRSV() - ((currSignals[currpt - 1].GetRSV() + currSignals[currpt].GetRSV()) / 2)) * 100 / subRcurrSignals.GetRSV(); if (subR > 0) { currSignals.Add(subRcurrSignals); } logger.LogCritical($"11111111当前第{titrationCount:D3}滴,体积:{amount:F3},阈值:{subR:F2}"); Messager.Send("TitrationValueChange", new TitrationValueArgs(titrationCount, amount, subR)); if (subR <= balanceVal) { arriveRangeCount++; Stopwatch swten = new(); swten.Start(); int retreatCount = 1; //后退次数 int flag = 0; while (true) //继续采集信号,监测是否褪色 { currpt = currSignals.Count - 6; subRcurrSignals = _graber.GrabSignal(signalTimeLen); currSignalsCompare.Add(subRcurrSignals); subR = (subRcurrSignals.GetRSV() - ((currSignals[currpt - 1].GetRSV() + currSignals[currpt].GetRSV()) / 2)) * 100 / subRcurrSignals.GetRSV(); if (subR > 0) { currSignals.Add(subRcurrSignals); } logger.LogCritical($"11111111已达设定阈值,观察是否褪色,阈值为:{subR:F2}"); if (subR <= balanceVal) { flag = 0; retreatCount++; if (swten.ElapsedMilliseconds / 1000 > arg.ArriveCheckCount ) { break; } Running = false; Thread.Sleep(1000); continue; //颜色比较红,继续观察是否到30秒 } else if (++flag > 1) { Running = true; if (retreatCount > 2) { slowSpeedCount = 5; //如果回退超过三次判定,则给定慢速10次 logger.LogCritical($"11111111回退超过三次,给定慢速5次"); } break;//当检测到的信号值 大于阈值时,则滴定未结束,需要继续滴定 } else { retreatCount++; Running = false; Thread.Sleep(1000); continue; //颜色比较红,继续观察是否到30秒 } } swten.Stop(); } else { arriveRangeCount = arriveRangeCount > 0 ? arriveRangeCount - 1 : 0; slowSpeedCount = slowSpeedCount > 0 ? slowSpeedCount - 1 : slowSpeedCount; } if (amount >= arg.MaxDropVolume) break; if (!Running) break; bool arriveEndPoint = LiquidPipeSwitchTitration1.StartNew.SetLiquidName(EquipmentNames.Titration1PotassiumPermanganate).IsArriveEndPoint; if (arriveEndPoint) { int distance = Convert.ToInt32(Math.Round(arg.PumpCapacity / arg.LiquidAmount * arg.LiquidConvertRatio)); LiquidPipeSwitchTitration1.StartNew.SetLiquidName(EquipmentNames.Titration1PotassiumPermanganate).Execute(LiquidSwitchPattern.In, distance); } bool res = LiquidPipeSwitchTitration1.StartNew.SetLiquidName(EquipmentNames.Titration1PotassiumPermanganate).Execute(LiquidSwitchPattern.Out, arg.LiquidConvertRatio); if (!res) { logger.LogError("11111111写入失败,等待两秒后再次写入"); Thread.Sleep(2000); continue; } titrationCount += 1; amount += arg.LiquidAmount; Thread.Sleep((arriveRangeCount < 1 && slowSpeedCount < 1) ? arg.IntervalTime : 2000); } } for (int i = 0; i < currSignals.Count; i++) { logger.LogCritical($"11111111currSignals r={currSignals[i].GetRSV()},g={currSignals[i].GetGSV()},num={i}"); } for (int i = 0; i < currSignalsCompare.Count; i++) { logger.LogCritical($"11111111currSignalsCompare r={currSignalsCompare[i].GetRSV()},g={currSignalsCompare[i].GetGSV()},num={i}"); } } private static void ComplexSampleTitration(TitrationArg arg, ref double amount) { ColorDataPoint subRcurrSignals = new ColorDataPoint(); while (true) { if (currSignals.Count >= 7) // 信号判断 { double subR = 0.0; int currpt = currSignals.Count - 6; subRcurrSignals = _graber.GrabSignal(signalTimeLen); //currSignalsCompare.Add(subRcurrSignals); subR = (subRcurrSignals.GetRSV() - ((currSignals[currpt - 1].GetRSV() + currSignals[currpt].GetRSV()) / 2)) * 100 / subRcurrSignals.GetRSV(); //if (subR > 0) //{ currSignals.Add(subRcurrSignals); //} logger.LogCritical($"11111111当前第{titrationCount:D3}滴,体积:{amount:F3},阈值:{subR:F2}"); Messager.Send("TitrationValueChange", new TitrationValueArgs(titrationCount, amount, subR)); if (subR <= balanceVal) { arriveRangeCount++; Stopwatch swten = new(); swten.Start(); int retreatCount = 1; //后退次数 int flag = 0; while (true) //继续采集信号,监测是否褪色 { currpt = currSignals.Count - 6; subRcurrSignals = _graber.GrabSignal(signalTimeLen); //currSignalsCompare.Add(subRcurrSignals); subR = (subRcurrSignals.GetRSV() - ((currSignals[currpt - 1].GetRSV() + currSignals[currpt].GetRSV()) / 2)) * 100 / subRcurrSignals.GetRSV(); //if (subR > 0) //{ currSignals.Add(subRcurrSignals); //} logger.LogCritical($"11111111已达设定阈值,观察是否褪色,阈值为:{subR:F2}"); if (subR <= balanceVal*0.7) { flag = 0; retreatCount++; if (swten.ElapsedMilliseconds / 1000 > arg.ArriveCheckCount ) { break; } Running = false; Thread.Sleep(1000); continue; //颜色比较红,继续观察是否到30秒 } else if (++flag > 1) { Running = true; if (retreatCount > 2) { slowSpeedCount = 5; //如果回退超过三次判定,则给定慢速10次 logger.LogCritical($"11111111回退超过三次,给定慢速5次"); } break;//当检测到的信号值 大于阈值时,则滴定未结束,需要继续滴定 } else { retreatCount++; Running = false; Thread.Sleep(1000); continue; //颜色比较红,继续观察是否到30秒 } } swten.Stop(); } else { arriveRangeCount = arriveRangeCount > 0 ? arriveRangeCount - 1 : 0; slowSpeedCount = slowSpeedCount > 0 ? slowSpeedCount - 1 : slowSpeedCount; } if (amount >= arg.MaxDropVolume) break; if (!Running) break; bool arriveEndPoint = LiquidPipeSwitchTitration1.StartNew.SetLiquidName(EquipmentNames.Titration1PotassiumPermanganate).IsArriveEndPoint; if (arriveEndPoint) { int distance = Convert.ToInt32(Math.Round(arg.PumpCapacity / arg.LiquidAmount * arg.LiquidConvertRatio)); LiquidPipeSwitchTitration1.StartNew.SetLiquidName(EquipmentNames.Titration1PotassiumPermanganate).Execute(LiquidSwitchPattern.In, distance); } bool res = LiquidPipeSwitchTitration1.StartNew.SetLiquidName(EquipmentNames.Titration1PotassiumPermanganate).Execute(LiquidSwitchPattern.Out, arg.LiquidConvertRatio); if (!res) { logger.LogError("11111111写入失败,等待两秒后再次写入"); Thread.Sleep(2000); continue; } titrationCount += 1; amount += arg.LiquidAmount; Thread.Sleep((arriveRangeCount < 1 && slowSpeedCount < 1) ? arg.IntervalTime : 2000); } } for (int i = 0; i < currSignals.Count; i++) { logger.LogCritical($"11111111currSignals r={currSignals[i].GetRSV()},g={currSignals[i].GetGSV()},num={i}"); } } private static void CalibrationTitrationColor(TitrationArg arg, ref double amount,int titrationCount,double balanceVal) { int count = 0; if (amount >= 4 && amount < 6) { count = 1; } else if (amount >= 6 && amount < 8) { count = 2; } else if (amount >= 8 && amount < 9) { count = 3; } else if (amount >= 9) { count = 4; } for (int i = 0; i < count; i++) { bool arriveEndPoint = LiquidPipeSwitchTitration1.StartNew.SetLiquidName(EquipmentNames.Titration1PotassiumPermanganate).IsArriveEndPoint; if (arriveEndPoint) { int distance = Convert.ToInt32(Math.Round(arg.PumpCapacity / arg.LiquidAmount * arg.LiquidConvertRatio)); LiquidPipeSwitchTitration1.StartNew.SetLiquidName(EquipmentNames.Titration1PotassiumPermanganate).Execute(LiquidSwitchPattern.In, distance); } bool res = LiquidPipeSwitchTitration1.StartNew.SetLiquidName(EquipmentNames.Titration1PotassiumPermanganate).Execute(LiquidSwitchPattern.Out, arg.LiquidConvertRatio); if (!res) { logger.LogError("11111111写入失败,等待两秒后再次写入"); Thread.Sleep(2000); } titrationCount += 1; amount += arg.LiquidAmount; Messager.Send("TitrationValueChange", new TitrationValueArgs(titrationCount, amount, balanceVal)); logger.LogCritical($"11111111-------加入校准{i+1}滴"); Thread.Sleep(300); } } #endregion } }