diff --git a/App.config b/App.config new file mode 100644 index 0000000..fb0c1f3 --- /dev/null +++ b/App.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/App.xaml b/App.xaml new file mode 100644 index 0000000..5041b34 --- /dev/null +++ b/App.xaml @@ -0,0 +1,52 @@ + + + + + diff --git a/App.xaml.cs b/App.xaml.cs new file mode 100644 index 0000000..dfdc8cf --- /dev/null +++ b/App.xaml.cs @@ -0,0 +1,83 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Security.Principal; +using System.Windows; + +namespace ShanghaiEnvironmentalTechnology +{ + public partial class App : Application + { + public App() + { + // 捕获UI线程异常 + DispatcherUnhandledException += App_DispatcherUnhandledException; + // 捕获非UI线程异常 + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + // 捕获任务线程异常 + System.Threading.Tasks.TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; + } + + private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) + { + LogError(e.Exception, "UI线程异常"); + e.Handled = true; // 标记为已处理,避免崩溃(仅用于调试) + } + + private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + if (e.ExceptionObject is Exception ex) + LogError(ex, "应用域未处理异常"); + } + + private void TaskScheduler_UnobservedTaskException(object sender, System.Threading.Tasks.UnobservedTaskExceptionEventArgs e) + { + LogError(e.Exception, "任务线程异常"); + e.SetObserved(); + } + + private void LogError(Exception ex, string type) + { + string logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "error.log"); + string logContent = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {type}:\n{ex.ToString()}\n\n"; + File.AppendAllText(logPath, logContent); + MessageBox.Show($"发生错误,详情已记录到 {logPath}", "错误"); + } + + protected override void OnStartup(StartupEventArgs e) + { + base.OnStartup(e); + + if (!IsRunAsAdmin()) + { + RestartAsAdmin(); + Shutdown(); + } + } + + private bool IsRunAsAdmin() + { + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + WindowsPrincipal principal = new WindowsPrincipal(identity); + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } + + private void RestartAsAdmin() + { + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.UseShellExecute = true; + startInfo.WorkingDirectory = Environment.CurrentDirectory; + startInfo.FileName = Process.GetCurrentProcess().MainModule.FileName; + startInfo.Verb = "runas"; // Run as administrator + + try + { + Process.Start(startInfo); + } + catch (Exception ex) + { + MessageBox.Show("Error: " + ex.Message); + } + } + } +} \ No newline at end of file diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs new file mode 100644 index 0000000..b0ec827 --- /dev/null +++ b/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/Constant/CSConstant.cs b/Constant/CSConstant.cs new file mode 100644 index 0000000..e0cbd70 --- /dev/null +++ b/Constant/CSConstant.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShanghaiEnvironmentalTechnology +{ + public static class CSConstant + { + public static string DbConnectionString = "Data Source=FlowPressure.db;Version=3;"; + } + + /// + /// 排气流表映射 + /// + public class FlowPressureRecord + { + public int Id { get; set; } + public double Flow { get; set; } + public double Pressure { get; set; } + + public double BeginCO2 { get; set; } + public double EndCO2 { get; set; } + public double CO2Added { get; set; } + + public DateTime RecordTime { get; set; } + } +} diff --git a/Constant/PLCDevice.cs b/Constant/PLCDevice.cs new file mode 100644 index 0000000..a9054c1 --- /dev/null +++ b/Constant/PLCDevice.cs @@ -0,0 +1,11 @@ +using Modbus.Device; +using Modbus; +using System.Net.Sockets; + +namespace ShanghaiEnvironmentalTechnology +{ + public class PLCDevice + { + + } +} \ No newline at end of file diff --git a/Data/BoolSign.cs b/Data/BoolSign.cs new file mode 100644 index 0000000..8b91fb1 --- /dev/null +++ b/Data/BoolSign.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace 口罩泄露定制款.Data +{ + + public class BoolSignal + { + private bool _previousValue; + + public event Action OnRisingEdge; + // bool value = true; + public bool Value { get; set; } + + public void CheckRisingEdge() + { + if (Value && !_previousValue) + { + OnRisingEdge?.Invoke(); + } + _previousValue = Value; + } + } + +} diff --git a/Data/DataChange.cs b/Data/DataChange.cs new file mode 100644 index 0000000..4b8466a --- /dev/null +++ b/Data/DataChange.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace 口罩泄露定制款 +{ + public class DataChange + { + /// + /// ushort转为float类型 + /// + /// + /// + /// float型数据 + public float UshortToFloat(ushort P1, ushort P2) + { + int intSign, intSignRest, intExponent, intExponentRest; + float faResult, faDigit; + intSign = P1 / 32768; + intSignRest = P1 % 32768; + intExponent = intSignRest / 128; + intExponentRest = intSignRest % 128; + faDigit = (float)(intExponentRest * 65536 + P2) / 8388608; + faResult = (float)Math.Pow(-1, intSign) * (float)Math.Pow(2, intExponent - 127) * (faDigit + 1); + return faResult; + } + /// + /// ushort转为int类型 + /// + /// + /// + /// 返回int型数据 + public int UshortToInt1(ushort u1, ushort u2) + { + ushort[] maidong = new ushort[2] { u1, u2 }; + byte[] bytes = new byte[maidong.Length * 2]; + Buffer.BlockCopy(maidong, 0, bytes, 0, bytes.Length); + int result = BitConverter.ToInt32(bytes, 0); + // 将字节数组转换为32位无符号整数 + return result; + + } + /// + /// Float转为Ushort数组发送 + /// + /// + /// 返回ushort数组 + public ushort[] SplitFloatToUShortArray(float value) + { + byte[] floatBytes = BitConverter.GetBytes(value); + ushort[] ushortArray = new ushort[floatBytes.Length / 2]; + + for (int i = 0, j = 0; i < floatBytes.Length; i += 2, j++) + { + ushortArray[j] = BitConverter.ToUInt16(floatBytes, i); + } + + return ushortArray; + } + /// + /// Int转为ushort数组发送 + /// + /// + /// 返回ushort数组 + public ushort[] intToushorts(int res) + { + ushort ust1 = (ushort)(res >> 16); + ushort ust2 = (ushort)res; + return new ushort[] { ust2, ust1 }; + } + } +} diff --git a/Data/ExperData.cs b/Data/ExperData.cs new file mode 100644 index 0000000..590d24d --- /dev/null +++ b/Data/ExperData.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace 口罩泄露定制款 +{ + 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; + /// + /// 样品系数 + /// + public float YangPinXiShu { get { return _yangPinXiShu; } set { _yangPinXiShu = value; } } + static string _testStatus = ""; + + + /// + /// 实验状态 + /// + public string TestStatus { get { return _testStatus; } set { _testStatus = value; } } + static float benDiNongDu = 0.00f; + /// + /// 本底浓度 + /// + 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浓度 + /// + public float CO2NongDu_Indoor { get { return cO2NongDu; } set { cO2NongDu = value; } } + + static float inDoor_TSINongDu = 0.00f; + /// + /// 环境气溶胶浓度 + /// + 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; + /// + /// 口罩内气溶胶浓度 + /// + public float Mask_TSINongDu { get { return mask_TSINongDu; } set { mask_TSINongDu = value; } } + + static float _liuLiang = 0.00f; + /// + /// 流量 + /// + public float LiuLiang { get { return _liuLiang; } set { _liuLiang = value; } } + static float _xieloulv = 0.00f; + /// + /// 泄露率 + /// + public float XieLouLv { get { return _xieloulv; } set { _xieloulv = value; } } + + + #endregion + + #region 泄露率计算 + //单独动作泄露率 + public float CumulativeLeakageRate(List inDoor_Tsi,List out_Door_Tsi,float benDiNongdu,float xiShu) + { + if (inDoor_Tsi.Count != 0 && out_Door_Tsi.Count != 0&& benDiNongdu!=0.0f) + { + //计算室内Tsi浓度平均值 + float inDoor_Tsi_Avg = inDoor_Tsi.Average(); + //计算室外Tsi浓度平均值 + float out_Door_Tsi_Avg = out_Door_Tsi.Average(); + //计算泄露率 + float leakageRate = ((out_Door_Tsi_Avg - inDoor_Tsi_Avg) * xiShu) / benDiNongdu; + return leakageRate; + } + else + { + return 0.00f; + } + + } + //全部动作泄露率 + public float CumulativeLeakageRate_All(List All_Cv_List) + + { + return All_Cv_List.Average(); + } + #endregion + + } +} diff --git a/Data/Function.cs b/Data/Function.cs new file mode 100644 index 0000000..764c80f --- /dev/null +++ b/Data/Function.cs @@ -0,0 +1,185 @@ +using Modbus.Device; +using Modbus; +using Sunny.UI; +using System; +using System.Threading; +using System.Windows; +using System.Windows.Controls; + +namespace 口罩泄露定制款 +{ + public class Function + { + ModbusMaster master; + IModbusMaster modbusMaster; + DataChange dc = new DataChange(); + public enum ButtonType + { + 复归型, + 切换型, + 置位型, + 复位型 + } + public enum DataType + { + 整形, + 浮点型 + } + public Function(ModbusMaster master_in) + { + this.master = master_in; + } + + public Function(IModbusMaster modbusMaster) + { + this.modbusMaster = modbusMaster; + } + + public void BtnClickFunction(ButtonType buttonType, ushort address) + { + try + { + switch (buttonType) + { + case ButtonType.复归型: + master.WriteSingleCoil(1, address, true); + Thread.Sleep(100); + master.WriteSingleCoil(1, address, false); + Thread.Sleep(100); + break; + case ButtonType.切换型: + if (master.ReadCoils(1, address, 1)[0]) + { + master.WriteSingleCoil(1, address, false); Thread.Sleep(100); + } + else + { master.WriteSingleCoil(1, address, true); Thread.Sleep(100); } + break; + case ButtonType.置位型: + master.WriteSingleCoil(1, address, true); + Thread.Sleep(100); + break; + case ButtonType.复位型: + master.WriteSingleCoil(1, address, false); + Thread.Sleep(100); + break; + default: + break; + } + } + catch (Exception ex) + { + + } + + } + + public void BtnClickFunctionForNew(ButtonType buttonType, ushort address) + { + try + { + switch (buttonType) + { + case ButtonType.复归型: + modbusMaster.WriteSingleCoil(1, address, true); + Thread.Sleep(100); + modbusMaster.WriteSingleCoil(1, address, false); + Thread.Sleep(100); + break; + case ButtonType.切换型: + if (modbusMaster.ReadCoils(1, address, 1)[0]) + { + modbusMaster.WriteSingleCoil(1, address, false); Thread.Sleep(100); + } + else + { modbusMaster.WriteSingleCoil(1, address, true); Thread.Sleep(100); } + break; + case ButtonType.置位型: + modbusMaster.WriteSingleCoil(1, address, true); + Thread.Sleep(100); + break; + case ButtonType.复位型: + modbusMaster.WriteSingleCoil(1, address, false); + Thread.Sleep(100); + break; + default: + break; + } + } + catch (Exception ex) + { + + } + + } + public void WriteToPLC(string inPutValue, ushort address, DataType dataType) + { + + try + { + switch (dataType) + { + case DataType.浮点型: + double value = inPutValue.ToDouble(); + if (UIInputDialog.ShowInputDoubleDialog(ref value, UIStyle.Inherited, desc: "请输入值", showMask: false)) + { + + master.WriteMultipleRegisters(1, address, dc.SplitFloatToUShortArray((float)value)); + } + break; + case DataType.整形: + int value_int = inPutValue.ToInt(); + if (UIInputDialog.ShowInputIntegerDialog(ref value_int, UIStyle.Inherited, desc: "请输入数据:")) + { + + master.WriteMultipleRegisters(1, address, dc.intToushorts(value_int)); + } + break; + default: + break; + } + } + catch (Exception ex) + { + MessageBox.Show("操作失败!" + "\n" + "\n" + ex.Message, "错误"); + } + + } + + public void WriteToPLCForNew(string inPutValue, ushort address, DataType dataType) + { + try + { + KeyboardHelper.ShowSoftKeyboard(); + switch (dataType) + { + case DataType.浮点型: + double value = inPutValue.ToDouble(); + + if (UIInputDialog.ShowInputDoubleDialog(ref value, UIStyle.Inherited, desc: "请输入值", showMask: false)) + { + + modbusMaster.WriteMultipleRegisters(1, address, dc.SplitFloatToUShortArray((float)value)); + } + break; + case DataType.整形: + int value_int = inPutValue.ToInt(); + if (UIInputDialog.ShowInputIntegerDialog(ref value_int, UIStyle.Inherited, desc: "请输入数据:")) + { + modbusMaster.WriteMultipleRegisters(1, address, dc.intToushorts(value_int)); + } + break; + default: + break; + } + + KeyboardHelper.HideSoftKeyboard(); + } + catch (Exception ex) + { + MessageBox.Show("操作失败!" + "\n" + "\n" + ex.Message, "错误"); + } + + } + } +} diff --git a/Data/LoginData.cs b/Data/LoginData.cs new file mode 100644 index 0000000..4cb62c4 --- /dev/null +++ b/Data/LoginData.cs @@ -0,0 +1,21 @@ +namespace 口罩泄露定制款 +{ + public class LoginData + { + static string _userName = ""; + public string UserName + { + get { return _userName; } + set { _userName = value; } + } + + //登陆权限 + static int _userPower = 0;//0为普通用户,1为管理员 + public int UserPower + { + get { return _userPower; } + set { _userPower = value; } + } + + } +} diff --git a/Data/PLC_Data.cs b/Data/PLC_Data.cs new file mode 100644 index 0000000..8c05c69 --- /dev/null +++ b/Data/PLC_Data.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace 口罩泄露定制款.Data +{ + internal class PLC_Data + { + + } +} diff --git a/Data/keyboard.cs b/Data/keyboard.cs new file mode 100644 index 0000000..d1b52fb --- /dev/null +++ b/Data/keyboard.cs @@ -0,0 +1,60 @@ +using System; +using System.Diagnostics; +using System.Security.Principal; + +public class KeyboardHelper +{ + public static void ShowSoftKeyboard() + { + if (!IsRunAsAdmin()) + { + RestartAsAdmin(); + return; + } + + Process.Start(new ProcessStartInfo + { + FileName = "osk.exe", + UseShellExecute = true + }); + } + + public static void HideSoftKeyboard() + { + try + { + Process.Start("cmd.exe", "/C taskkill /IM osk.exe /F"); + } + catch (Exception ex) + { + Console.WriteLine("Error: " + ex.Message); + } + } + + private static bool IsRunAsAdmin() + { + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + WindowsPrincipal principal = new WindowsPrincipal(identity); + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } + + private static void RestartAsAdmin() + { + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.UseShellExecute = true; + startInfo.WorkingDirectory = Environment.CurrentDirectory; + startInfo.FileName = Process.GetCurrentProcess().MainModule.FileName; + startInfo.Verb = "runas"; + + try + { + Process.Start(startInfo); + } + catch (Exception ex) + { + Console.WriteLine("Error: " + ex.Message); + } + + Environment.Exit(0); + } +} diff --git a/Main.xaml b/Main.xaml new file mode 100644 index 0000000..cd64874 --- /dev/null +++ b/Main.xaml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +