From 028728b816d825959467ab1fc57dd0fced4337f3 Mon Sep 17 00:00:00 2001 From: xyy <544939200@qq.com> Date: Thu, 22 Jan 2026 19:18:16 +0800 Subject: [PATCH] --- 口罩泄露定制款/Data/ExperData.cs | 161 +++++++++++++----- 口罩泄露定制款/Data/UserData.json | 36 ++++ 口罩泄露定制款/DatabaseHelper/SQLiteHelper.cs | 6 + 口罩泄露定制款/Form/frm_Main.cs | 112 +++++++----- 口罩泄露定制款/口罩泄露定制款山东.csproj | 1 + 5 files changed, 227 insertions(+), 89 deletions(-) create mode 100644 口罩泄露定制款/Data/UserData.json diff --git a/口罩泄露定制款/Data/ExperData.cs b/口罩泄露定制款/Data/ExperData.cs index 7cb1cc7..458368a 100644 --- a/口罩泄露定制款/Data/ExperData.cs +++ b/口罩泄露定制款/Data/ExperData.cs @@ -7,145 +7,212 @@ using System.Threading.Tasks; namespace 口罩泄露定制款 { - public class ExperData: INotifyPropertyChanged + public class ExperData : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; + #region 实验属性 static string experName = ""; /// /// 实验人员 /// public string ExperName { get { return experName; } set { experName = value; } } - + static string experDate = ""; /// /// 实验日期 /// public string ExperDate { get { return experDate; } set { experDate = value; } } - + static string experNum = ""; /// /// 实验编号 /// public string ExperNum { get { return experNum; } set { experNum = value; } } - + static string experType = " "; /// /// 实验种类 /// public string ExperType { get { return experType; } set { experType = value; } } + static float experMaskType = 2.0f; /// - /// 口罩类型 + /// 口罩类型泄漏率标准 /// public float ExperMaskType { get { return experMaskType; } set { experMaskType = value; } } + /// /// 面罩类型 /// static string maskType; public string MaskType { get { return maskType; } set { maskType = value; } } - static float _yangPinXiShu = 1.0f; + + static float _testDuration = 120f; // 测试时间改为120秒 /// - /// 样品系数 + /// 测试时长(秒) /// - public float YangPinXiShu { get { return _yangPinXiShu; } set { _yangPinXiShu = value; } } + public float TestDuration { get { return _testDuration; } set { _testDuration = value; } } + static string _testStatus = ""; - - /// /// 实验状态 /// public string TestStatus { get { return _testStatus; } set { _testStatus = value; } } + static float benDiNongDu = 0.00f; /// - /// 本底浓度 + /// 本底浓度(mg/m³) /// public float BenDiNongDu { get { return benDiNongDu; } set { benDiNongDu = value; } } - + static float huanJingWenDu = 0.00f; /// /// 环境温度 /// public float HuanJingWenDu { get { return huanJingWenDu; } set { huanJingWenDu = value; } } - - + static float huanJingShiDu = 0.00f; /// /// 环境湿度 /// public float HuanJingShiDu { get { return huanJingShiDu; } set { huanJingShiDu = value; } } - + static float o2NongDu = 0.00f; /// /// 环境氧浓度 /// public float O2NongDu { get { return o2NongDu; } set { o2NongDu = value; } } - + static float cO2NongDu = 0.00f; /// - /// 环境C02浓度 + /// 环境CO2浓度 /// public float CO2NongDu_Indoor { get { return cO2NongDu; } set { cO2NongDu = value; } } - + static float inDoor_TSINongDu = 0.00f; /// - /// 环境气溶胶浓度 + /// 环境气溶胶浓度(mg/m³) /// public float InDoor_TSINongDu { get { return inDoor_TSINongDu; } set { inDoor_TSINongDu = value; } } - + static float mask_CO2NongDu = 0.00f; /// /// 口罩内CO2浓度 /// public float Mask_CO2NongDu { get { return mask_CO2NongDu; } set { mask_CO2NongDu = value; } } - + static float mask_TSINongDu = 0.00f; /// - /// 口罩内气溶胶浓度 + /// 口罩内气溶胶浓度(mg/m³) /// public float Mask_TSINongDu { get { return mask_TSINongDu; } set { mask_TSINongDu = value; } } static float _liuLiang = 0.00f; /// - /// 流量 + /// 流量(L/min) /// public float LiuLiang { get { return _liuLiang; } set { _liuLiang = value; } } + static float _xieloulv = 0.00f; /// - /// 泄露率 + /// 泄露率(%) /// public float XieLouLv { get { return _xieloulv; } set { _xieloulv = value; } } + // 新增:采样流量和干燥气流流量(根据YY/T 0866-2024标准) + static float _samplingFlowRate = 2.0f; // 默认采样流量2L/min + public float SamplingFlowRate { get { return _samplingFlowRate; } set { _samplingFlowRate = value; } } + + static float _dryingFlowRate = 1.0f; // 默认干燥气流1L/min + public float DryingFlowRate { get { return _dryingFlowRate; } set { _dryingFlowRate = value; } } #endregion - #region 泄露率计算 - //单独动作泄露率 - public float CumulativeLeakageRate(List inDoor_Tsi,List out_Door_Tsi,float benDiNongdu,float xiShu) + #region 泄漏率计算方法(根据YY/T 0866-2024标准) + + /// + /// 连续采样法泄漏率计算(公式2) + /// P = 1.25 * ((C2 - C0) / C1) * ((S + D) / S) * 100 + /// + public float ContinuousSamplingLeakageRate(List indoorData, List outdoorData, + float backgroundConcentration, + float samplingFlow, float dryingFlow) { - if (inDoor_Tsi.Count != 0 && out_Door_Tsi.Count != 0) - { - //计算室内Tsi浓度平均值 - float inDoor_Tsi_Avg = inDoor_Tsi.Average(); - //计算室外Tsi浓度平均值 - float out_Door_Tsi_Avg = out_Door_Tsi.Average(); - //计算泄露率 - float leakageRate = ((out_Door_Tsi_Avg - benDiNongdu ) * xiShu)*100 / inDoor_Tsi_Avg; - return leakageRate; - } - else + if (indoorData.Count == 0 || outdoorData.Count == 0) + return 0.0f; + + // 使用最后100秒的数据(标准要求) + int dataCount = Math.Min(indoorData.Count, outdoorData.Count); + int startIndex = Math.Max(0, dataCount - 100); // 取最后100个数据点 + + // 计算最后100秒的平均值 + float C1 = 0, C2 = 0; + int validCount = 0; + + for (int i = startIndex; i < dataCount; i++) { - return 0.00f; + C1 += indoorData[i]; + C2 += outdoorData[i]; + validCount++; } - - } - //全部动作泄露率 - public float CumulativeLeakageRate_All(List All_Cv_List) + if (validCount == 0) + return 0.0f; + + C1 /= validCount; + C2 /= validCount; + + // 应用连续采样法公式 + if (C1 <= 0) + return 0.0f; + + // P = 1.25 * ((C2 - C0) / C1) * ((S + D) / S) * 100 + float leakageRate = 1.25f * ((C2 - backgroundConcentration) / C1) * + ((samplingFlow + dryingFlow) / samplingFlow) * 100; + + return Math.Max(0, leakageRate); // 确保非负 + } + + /// + /// 脉冲式采样法泄漏率计算(公式1) - 备用 + /// + public float PulsedSamplingLeakageRate(List indoorData, List outdoorData, + float backgroundConcentration, + float samplingFlow, float dryingFlow, + float totalInhaleTime, float totalExhaleTime) { - return All_Cv_List.Average(); - } - #endregion + if (indoorData.Count == 0 || outdoorData.Count == 0 || totalInhaleTime <= 0) + return 0.0f; + // 使用最后100秒的数据 + int dataCount = Math.Min(indoorData.Count, outdoorData.Count); + int startIndex = Math.Max(0, dataCount - 100); + + float C1 = 0, C2 = 0; + int validCount = 0; + + for (int i = startIndex; i < dataCount; i++) + { + C1 += indoorData[i]; + C2 += outdoorData[i]; + validCount++; + } + + if (validCount == 0 || C1 <= 0) + return 0.0f; + + C1 /= validCount; + C2 /= validCount; + + // P = ((C2 - C0) / C1) * ((t_w + t_cm) / t_w) * ((S + D) / S) * 100 + float leakageRate = ((C2 - backgroundConcentration) / C1) * + ((totalInhaleTime + totalExhaleTime) / totalInhaleTime) * + ((samplingFlow + dryingFlow) / samplingFlow) * 100; + + return Math.Max(0, leakageRate); + } + + #endregion } -} +} \ No newline at end of file diff --git a/口罩泄露定制款/Data/UserData.json b/口罩泄露定制款/Data/UserData.json new file mode 100644 index 0000000..2cfc260 --- /dev/null +++ b/口罩泄露定制款/Data/UserData.json @@ -0,0 +1,36 @@ +{ + "High": "191", + "Weight": "59", + "FaceWidth": "4", + "FaceLength": "2", + "EyesDistance": "5", + "Customer": "7", + "IsNeedRun": false, + "MaskType": "KN95", + "Category": "NaCl浓度", + "Threshold": "10.0", + "Concentration": "5", + "ParticleSize": "1.7", + "ContentString": "篇三", + "ScreenValue": "[1:选型决策树]\"问1:环境有缺氧风险吗?是→选正压式呼吸器!否→下一步!问2:危害物是粉尘/烟雾?是→选KN100/KP100口罩!否→下一步!问3:危害物是毒气/蒸气?是→选对应滤毒罐全面罩!——科学选型是防护第一步!\"[2:佩戴全流程]\"步骤1:预检查•滤棉无破损,头带弹性足!步骤2:规范佩戴•双手托口罩→下巴先入→拉紧下头带→再拉紧上头带!步骤3:密合检测•正压检测:手掌堵呼气阀,口罩应鼓起!•负压检测:捂住滤棉吸气,口罩应塌陷!\"[3:致命误区解析]\"误区1:'纱布口罩也能防尘'真相:PM2.5穿透率高达90%!误区2:'滤棉变黑才需换'真相:呼吸阻力增大就要换!误区3:'胡须长不影响密封'真相:胡须者必须用电动送风面罩!\"[4:法规与创新]\"GB2626-2019规定:KN95口罩盐性粉尘过滤率≥95%!新技术:智能口罩可监测呼吸阻力,报警提醒更换!——合规是底线,科技向未来!\"", + "CustomScreenValue": "1`2`3`4`5`6`7`8`9", + "Name": "qw", + "Sex": "男", + "Age": "30", + "IdNumber": "25", + "Company": "南京", + "CompanyAge": "3", + "No": "CSI-665B", + "FullMaskType": "随弃式面罩TIL-KN95", + "BenDiNongdu": 0.374, + "SamplingFlowRate": 2.0, + "DryingFlowRate": 1.0, + "MaskStandards": { + "KN90": 13.0, + "KP90": 13.0, + "KN95": 11.0, + "KP95": 11.0, + "KN100": 5.0, + "KP100": 5.0 +} +} \ No newline at end of file diff --git a/口罩泄露定制款/DatabaseHelper/SQLiteHelper.cs b/口罩泄露定制款/DatabaseHelper/SQLiteHelper.cs index 34d5ac8..5b0cb31 100644 --- a/口罩泄露定制款/DatabaseHelper/SQLiteHelper.cs +++ b/口罩泄露定制款/DatabaseHelper/SQLiteHelper.cs @@ -67,6 +67,12 @@ public class Experiment public float TsiIndoorAgv { get; set; } public float TsiOutdoorAgv { get; set; } public float Xieloulv { get; set; } + + public float SamplingFlowRate { get; set; } // 采样流量(L/min) + public float DryingFlowRate { get; set; } // 干燥气流(L/min) + + // 测试时长 + public int TestDuration { get; set; } // 测试时长(秒) } diff --git a/口罩泄露定制款/Form/frm_Main.cs b/口罩泄露定制款/Form/frm_Main.cs index c813b7b..b4cc5df 100644 --- a/口罩泄露定制款/Form/frm_Main.cs +++ b/口罩泄露定制款/Form/frm_Main.cs @@ -78,7 +78,7 @@ namespace 口罩泄露定制款 } } - // 3. 步骤初始化 + // 步骤初始化 - 根据新标准调整 private void InitStepList() { stepList.Clear(); @@ -86,11 +86,11 @@ namespace 口罩泄露定制款 stepList.Add("请将采样管插头插入测量接口"); if (isNeedRun) stepList.Add("请按下跑步机启动按钮"); - stepList.Add("头部静止、不说话,2 min"); - stepList.Add("左右转动头部看检测仓左右墙壁(大约15次),2 min"); - stepList.Add("抬头和低头看检测仓顶和地面(大约15次),2 min"); - stepList.Add("大声阅读屏幕显示文字,2 min"); - stepList.Add("头部静止、不说话,2 min"); + stepList.Add("头部静止、不说话,2 min"); + stepList.Add("左右转动头部看检测仓左右墙壁(大约15次),2 min"); + stepList.Add("抬头和低头看检测仓顶和地面(大约15次),2 min"); + stepList.Add("大声阅读屏幕显示文字,2 min"); + stepList.Add("头部静止、不说话,2 min"); stepList.Add("测试完成,拔下采样管插头,走出测量仓"); comboBox1.Items.Clear(); foreach (var step in stepList) @@ -541,18 +541,31 @@ namespace 口罩泄露定制款 if (dt_Show.Columns.Count != 0) { - if (list_Tsi_Indoor_Data.Count != 0 && list_Tsi_Outdoor_Data.Count != 0) - { - _TsiIndoorAgv = list_Tsi_Indoor_Data.Average(); - _TsiOutdoorAgv = list_Tsi_Outdoor_Data.Average(); + // 使用最后100秒数据计算(标准要求) + int dataCount = Math.Min(list_Tsi_Indoor_Data.Count, list_Tsi_Outdoor_Data.Count); + int startIndex = Math.Max(0, dataCount - 100); - // 关键修改:使用与 timer_UpdataChart_Tick 中相同的计算方法 - // 而不是简单的 lsit_XieLoulv.Average() - _Xieloulv = experData.CumulativeLeakageRate( - list_Tsi_Indoor_Data, - list_Tsi_Outdoor_Data, + List last100Indoor = new List(); + List last100Outdoor = new List(); + + for (int i = startIndex; i < dataCount; i++) + { + last100Indoor.Add(list_Tsi_Indoor_Data[i]); + last100Outdoor.Add(list_Tsi_Outdoor_Data[i]); + } + + if (last100Indoor.Count > 0 && last100Outdoor.Count > 0) + { + _TsiIndoorAgv = last100Indoor.Average(); + _TsiOutdoorAgv = last100Outdoor.Average(); + + // 使用连续采样法计算公式 + _Xieloulv = experData.ContinuousSamplingLeakageRate( + last100Indoor, + last100Outdoor, experData.BenDiNongDu, - experData.YangPinXiShu); + experData.SamplingFlowRate, + experData.DryingFlowRate); } else { @@ -581,12 +594,12 @@ namespace 口罩泄露定制款 experData.ExperNum, experData.ExperType, comboBox1.Text, - experData.HuanJingWenDu, - experData.HuanJingShiDu, - experData.BenDiNongDu, - _TsiIndoorAgv, - _TsiOutdoorAgv, - _Xieloulv); // 这里使用统一计算后的泄露率 + experData.HuanJingWenDu.ToString("F1"), + experData.HuanJingShiDu.ToString("F1"), + experData.BenDiNongDu.ToString("F3"), + _TsiIndoorAgv.ToString("F3"), + _TsiOutdoorAgv.ToString("F3"), + _Xieloulv.ToString("F2")); this.Invoke(new Action(() => { @@ -599,11 +612,12 @@ namespace 口罩泄露定制款 private void InsertDataToDatabase() { float _Xieloulv = 0.0f; - _Xieloulv = experData.CumulativeLeakageRate( + _Xieloulv = experData.ContinuousSamplingLeakageRate( list_Tsi_Indoor_Data, list_Tsi_Outdoor_Data, experData.BenDiNongDu, - experData.YangPinXiShu); + experData.SamplingFlowRate, + experData.DryingFlowRate); // 读取用户数据 @@ -979,6 +993,7 @@ namespace 口罩泄露定制款 List list_O2_Data = new List();//氧气浓度 List list_HuanJingWenDu_Data = new List();//环境内温度 List list_HuanJingShiDu_Data = new List();//环境湿度 + // 修改timer_UpdataChart_Tick - 实时泄漏率计算 private void timer_UpdataChart_Tick(object sender, EventArgs e) { int currindex = 0; @@ -990,18 +1005,43 @@ namespace 口罩泄露定制款 { currindex = 2; } + if (data_M130_M135[1] && currentStepIndex >= currindex) { list_Time.Add(DateTime.Now.ToString("HH:mm:ss")); list_Tsi_Indoor_Data.Add(experData.InDoor_TSINongDu); list_Tsi_Outdoor_Data.Add(experData.Mask_TSINongDu); - experData.XieLouLv = experData.CumulativeLeakageRate(list_Tsi_Indoor_Data, list_Tsi_Outdoor_Data, experData.BenDiNongDu, experData.YangPinXiShu); + + // 使用最后100秒数据计算实时泄漏率 + int dataCount = Math.Min(list_Tsi_Indoor_Data.Count, list_Tsi_Outdoor_Data.Count); + int startIndex = Math.Max(0, dataCount - 100); + + List last100Indoor = new List(); + List last100Outdoor = new List(); + + for (int i = startIndex; i < dataCount; i++) + { + last100Indoor.Add(list_Tsi_Indoor_Data[i]); + last100Outdoor.Add(list_Tsi_Outdoor_Data[i]); + } + + if (last100Indoor.Count > 0 && last100Outdoor.Count > 0) + { + experData.XieLouLv = experData.ContinuousSamplingLeakageRate( + last100Indoor, + last100Outdoor, + experData.BenDiNongDu, + experData.SamplingFlowRate, + experData.DryingFlowRate); + } + lsit_XieLoulv.Add(experData.XieLouLv); list_CO2_Indoor_Data.Add(experData.CO2NongDu_Indoor); list_CO2_Outdoor_Data.Add(experData.Mask_CO2NongDu); list_O2_Data.Add(experData.O2NongDu); list_HuanJingWenDu_Data.Add(experData.HuanJingWenDu); list_HuanJingShiDu_Data.Add(experData.HuanJingShiDu); + chart_TSI.Series[0].Points.DataBindXY(list_Time, list_Tsi_Indoor_Data); chart_TSI.Series[1].Points.DataBindXY(list_Time, list_Tsi_Outdoor_Data); chart_TSI.Series[2].Points.DataBindXY(list_Time, lsit_XieLoulv); @@ -1010,9 +1050,7 @@ namespace 口罩泄露定制款 chart2.Series[2].Points.DataBindXY(list_Time, list_O2_Data); chart2.Series[3].Points.DataBindXY(list_Time, list_HuanJingWenDu_Data); chart2.Series[4].Points.DataBindXY(list_Time, list_HuanJingShiDu_Data); - } - } //结束时写入Excel public static int comindex; @@ -1091,6 +1129,7 @@ namespace 口罩泄露定制款 #endregion #region 按钮事件 + // 修改btn_Start_Click - 加载配置参数 private void btn_Start_Click(object sender, EventArgs e) { string configPath = Path.Combine(Application.StartupPath, "UserData.json"); @@ -1099,23 +1138,15 @@ namespace 口罩泄露定制款 var json = File.ReadAllText(configPath); dynamic config = Newtonsoft.Json.JsonConvert.DeserializeObject(json); experData.BenDiNongDu = config.BenDiNongdu; - experData.YangPinXiShu = config.ParticleSize; + experData.SamplingFlowRate = config.SamplingFlowRate ?? 2.0f; + experData.DryingFlowRate = config.DryingFlowRate ?? 1.0f; experData.ExperName = config.Category; } + isPaused = false; Pause1.Text = "暂停测试"; Pause1.Style = UIStyle.Red; - - - //if (experData.BenDiNongDu == 0.0f)//add by xyy - ////if (false) - //{ - // MessageBox.Show("未校准本底浓度!"); - // return; - //} - //else - //{ // 清空数据 list_Time.Clear(); list_Tsi_Indoor_Data.Clear(); @@ -1130,7 +1161,6 @@ namespace 口罩泄露定制款 fc?.BtnClickFunction(Function.ButtonType.复归型, 130); - LoadIsNeedRunConfig(); InitStepList(); @@ -1139,10 +1169,8 @@ namespace 口罩泄露定制款 StartProcess(); _logger.Log(loginData.UserName, "点击了【测试开始】按钮", loginData.UserPower.ToString()); - //} } - private void btn_Stop_Click(object sender, EventArgs e) { isPaused = false; @@ -2169,7 +2197,7 @@ namespace 口罩泄露定制款 list_Tsi_Outdoor_Data.Clear(); //experData.XieLouLv = experData.CumulativeLeakageRate(list_Tsi_Indoor_Data, list_Tsi_Outdoor_Data, experData.BenDiNongDu, 1.0f); lsit_XieLoulv.Clear(); - list_CO2_Indoor_Data.Clear(); + list_CO2_Indoor_Data.Clear(); list_CO2_Outdoor_Data.Clear(); list_O2_Data.Clear(); list_HuanJingWenDu_Data.Clear(); diff --git a/口罩泄露定制款/口罩泄露定制款山东.csproj b/口罩泄露定制款/口罩泄露定制款山东.csproj index c8fc81e..387a136 100644 --- a/口罩泄露定制款/口罩泄露定制款山东.csproj +++ b/口罩泄露定制款/口罩泄露定制款山东.csproj @@ -281,6 +281,7 @@ TreeComboBox.cs +