alielie
2025-11-21 3a471ae651b228231bebb3a2d438baa1d4ac0261
优化
65个文件已修改
41个文件已添加
3 文件已重命名
102个文件已删除
4 文件已复制
18640 ■■■■■ 已修改文件
ConnectionUtil/ConnectionUtil.csproj 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConnectionUtil/PipeClient.cs 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConnectionUtil/PipeClient2.cs 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConnectionUtil/PipeServer.cs 347 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConnectionUtil/PipeServer2.cs 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConnectionUtil/Properties/AssemblyInfo.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConnectionUtil/SCADAApi.cs 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConnectionUtil/TcpClient.cs 254 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleTest/ConsoleTest.csproj 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleTest/Program.cs 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleTest/Properties/app.manifest 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/AlarmInfoManage.xaml.cs 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/AlarmSettingList.xaml 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/Entity/AlarmSetting.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/Entity/AlarmText.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/HMI.AlarmInfoManage.csproj 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/app.config 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Setting/SystemSetting.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.DataViewer/DataViewer.xaml.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/Entity/DIO.cs 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/Entity/IPManage.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/HMI.IOMonitor.csproj 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/IOMonitor.xaml 216 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/IOMonitor.xaml.cs 493 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/IPManageModal.xaml 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/IPManageModal.xaml.cs 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/Properties/AssemblyInfo.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/Properties/Resources.Designer.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/Properties/Resources.resx 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/Properties/Settings.Designer.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/Properties/Settings.settings 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/packages.config 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/MenuManage.xaml.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/ParameterModal.xaml.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/SettingModal.xaml.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.SettingControl/SystemConfig.xaml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.StdControl/HMI.StdControl.csproj 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.StdControl/Properties/AssemblyInfo.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.StdControl/StdEnableMes.xaml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.StdControl/StdEnableMes.xaml.cs 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.StdControl/StdUIEventArg.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Controls/LightBall.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/HMI.UI.csproj 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/ScrollLabel.cs 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/Colors.xaml 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/MsgBoxStyle.xaml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Themes/Generic.xaml 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/HMI.UnitTest.csproj 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/MainWindow.xaml.cs 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Libs/HslCommunication.dll 补丁 | 查看 | 原始文档 | blame | 历史
Libs/lmes.dll 补丁 | 查看 | 原始文档 | blame | 历史
ModBusTcpController/ModBusTcpController.cs 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ModBusTcpController/ModBusTcpController.csproj 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ModBusTcpController/Properties/AssemblyInfo.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
OPCClient/OPCClient.csproj 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
OPCClient/app.config 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
OPCClient/packages.config 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ChangeModel/ChangeModel.xaml 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ChangeModel/ChangeModel.xaml.cs 430 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ChangeModel/Entity/CmLogData.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ChangeModel/Entity/ModelData.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ChangeModel/Entity/WorkOrderData.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ChangeModel/Plugin.ChangeModel.csproj 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/AssemblyInfo.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/BurnInCell.xaml 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/BurnInCell.xaml.cs 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/BurnInOven.xaml 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/BurnInOven.xaml.cs 233 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/BurnInOvenCollection.xaml 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/BurnInOvenCollection.xaml.cs 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/BurnInSettingModal.xaml 159 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/BurnInSettingModal.xaml.cs 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/ControlPage.xaml 284 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/ControlPage.xaml.cs 410 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/DataModal.xaml 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/DataModal.xaml.cs 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/Entity/BaseConfig.cs 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/Entity/OPCContext.cs 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/Entity/PLCTagData.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/Entity/S7Context.cs 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/Entity/SensorData.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/Plugin.CurrentSensorControl.csproj 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/SingleSensor.xaml 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.CurrentSensorControl/SingleSensor.xaml.cs 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.DOControl/Plugin.DOControl.csproj 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.DOControl/app.config 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.DODriverControl/Api/IDIOController.cs 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.DODriverControl/Api/ModBusTcpController.cs 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.DODriverControl/Api/PCIController.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.DODriverControl/Plugin.DODriverControl.csproj 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.DODriverControl/SingleControl.xaml.cs 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.DataBoard/DataBoard.xaml 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.DataBoard/DataBoard.xaml.cs 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.DataBoard/Properties/AssemblyInfo.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.JarUI/JarUI.xaml.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.MaterialOpt/AssemblyInfo.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.MaterialOpt/MaterialOpt.xaml 251 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.MaterialOpt/MaterialOpt.xaml.cs 429 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.MaterialOpt/Plugin.MaterialOpt.csproj 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SNGenerateTool/Entity/DataInfo.cs 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SNGenerateTool/Entity/UserSetting.cs 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SNGenerateTool/Form1.Designer.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SNGenerateTool/Form1.cs 1984 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SNGenerateTool/HashPlus.cs 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SNGenerateTool/Plugin.SNGenerateTool.csproj 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SNGenerateTool/Properties/AssemblyInfo.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SNGenerateTool/Properties/Resources.Designer.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SNGenerateTool/Properties/Resources.resx 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SNGenerateTool/Properties/Settings.Designer.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SNGenerateTool/Properties/Settings.settings 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SNGenerateTool/UserControl1.xaml 265 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SNGenerateTool/UserControl1.xaml.cs 251 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SNGenerateTool/app.config 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SNGenerateTool/packages.config 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ServoManage/Entity/ServoData.cs 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ServoManage/Entity/ServoProductData.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ServoManage/Plugin.ServoManage.csproj 166 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ServoManage/Properties/AssemblyInfo.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ServoManage/Properties/Resources.Designer.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ServoManage/Properties/Resources.resx 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ServoManage/Properties/Settings.Designer.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ServoManage/Properties/Settings.settings 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ServoManage/ServoManage.xaml 526 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ServoManage/ServoManage.xaml.cs 768 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ServoManage/ServoModal.xaml 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ServoManage/ServoModal.xaml.cs 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ServoManage/ServoProductModal.xaml 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ServoManage/ServoProductModal.xaml.cs 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ServoManage/ServoSetting.xaml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ServoManage/ServoSetting.xaml.cs 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.ServoManage/packages.config 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Container/FunctionSetting.xaml 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Container/FunctionSetting.xaml.cs 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Container/ProgramList.xaml 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Container/ProgramList.xaml.cs 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Container/SequnceList.xaml 204 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Container/SequnceList.xaml.cs 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Container/StepSetting.xaml 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Container/StepSetting.xaml.cs 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Entity/CanvasPosition.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Entity/SeqData.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Model/FuncModel.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Model/Program.cs 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Model/Sequence.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Model/Step.cs 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Model/Variable.cs 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Plugin.TestBuilder.csproj 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Properties/AssemblyInfo.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Properties/Resources.Designer.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Properties/Resources.resx 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Properties/Settings.Designer.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Properties/Settings.settings 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/TestBuilder.xaml 238 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/TestBuilder.xaml.cs 237 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/UI/BottomBar.xaml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/UI/BottomBar.xaml.cs 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/UI/DdDragHandler.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/UI/DdDropHandler.cs 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/UI/InnerWindow.xaml 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/UI/InnerWindow.xaml.cs 262 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/UI/MinInnerWindow.xaml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/UI/MinInnerWindow.xaml.cs 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/UI/Resize.cs 625 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Workspace.xaml 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/Workspace.xaml.cs 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestBuilder/packages.config 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/Constant/CommonConstant.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/DataTable.xaml 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/DataTable.xaml.cs 226 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/InfoTable.xaml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/InfoWindow.xaml 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/InfoWindow.xaml.cs 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/Model/BaseConfig.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/Model/Info.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/Plugin.TestResult.csproj 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/Properties/AssemblyInfo.cs 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/Resource/abort.png 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/Scanner.xaml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/Scanner.xaml.cs 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/SingleCell.xaml 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/SingleCell.xaml.cs 252 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/TestResultView.xaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/TestResultView.xaml.cs 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/Util/CommonCodeParser.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/Util/ICodeParser.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/Util/JKCodeParser.cs 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/ViewControl.xaml 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/ViewControl.xaml.cs 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TsExecution/Properties/AssemblyInfo.cs 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TsExecution/SingleControl.xaml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TsExecution/SingleControl.xaml.cs 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TsExecution/TsExecution.xaml.cs 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SimpleUtil/DIOController/DIOController.csproj 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SimpleUtil/HMI.Simple.Util/ProductUtil.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SimpleUtil/HMI.Util.Base/AlarmUtil.cs 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SimpleUtil/HMI.Util.Base/HMI.Util.Base.csproj 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Engine/Runner.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Engine/app.config 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Run/Properties/AssemblyInfo.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Run/Properties/Resources.Designer.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Run/Properties/Resources.resx 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Run/Properties/Settings.Designer.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Run/Properties/Settings.settings 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Run/VisionBuilder.Run.csproj 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Test/PluginOne.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Test/Properties/AssemblyInfo.cs 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Test/VisionBuilder.Test.csproj 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Test/app.config 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/App.config 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Config/log4net.config 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/LoadingWindow.xaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Login.xaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/PanuonWindow.xaml.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/WPFProject.csproj 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/WPFProject.sln 188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConnectionUtil/ConnectionUtil.csproj
File was renamed from VisionBuilder.Engine/VisionBuilder.Engine.csproj
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{8E5E8A68-0B9D-4A6A-B325-730C64603FDF}</ProjectGuid>
    <ProjectGuid>{D0806FA6-3F6D-4304-81B8-EAD9DA7CB5B2}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>VisionBuilder.Engine</RootNamespace>
    <AssemblyName>VisionBuilder.Engine</AssemblyName>
    <RootNamespace>ConnectionUtil</RootNamespace>
    <AssemblyName>ConnectionUtil</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <Deterministic>true</Deterministic>
@@ -59,14 +59,13 @@
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Runner.cs" />
    <Compile Include="PipeClient.cs" />
    <Compile Include="PipeClient2.cs" />
    <Compile Include="PipeServer.cs" />
    <Compile Include="PipeServer2.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\HMI.Core\HMI.Core.csproj">
      <Project>{3e3bf44a-0b69-45c5-9512-528cb48a7106}</Project>
      <Name>HMI.Core</Name>
    </ProjectReference>
    <Compile Include="SCADAApi.cs" />
    <Compile Include="TcpClient.cs" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
ConnectionUtil/PipeClient.cs
New file
@@ -0,0 +1,100 @@
using System;
using System.IO;
using System.IO.Pipes;
using System.Threading;
using System.Threading.Tasks;
namespace ConnectionUtil
{
    public class PipeClient : IDisposable
    {
        private readonly string _pipeName;
        private readonly string _serverName;
        private readonly CancellationTokenSource _cancellationTokenSource;
        private NamedPipeClientStream _pipeClient;
        private bool _isDisposed;
        private bool _isConnected;
        private readonly object _sendLock = new object();
        // 事件定义
        public event EventHandler<PipeDataEventArgs> OnData;
        public event EventHandler OnConnected;
        public event EventHandler OnDisconnected;
        public PipeClient(string pipeName, string serverName = ".")
        {
            _pipeName = pipeName;
            _serverName = serverName;
            _cancellationTokenSource = new CancellationTokenSource();
        }
        public void Connect(int timeoutMs = 5000)
        {
            if (_isConnected)
                return;
            _pipeClient = new NamedPipeClientStream(_pipeName);
            _pipeClient.Connect(timeoutMs);
            _pipeClient.ReadMode = PipeTransmissionMode.Message;
            _isConnected = true;
            Console.WriteLine("连接成功!");
            // 触发连接成功事件
            OnConnected?.Invoke(this, EventArgs.Empty);
        }
        public bool SendMessage(string message, out string response, int timeoutMs = 5000)
        {
            if (!_isConnected)
            {
                response = "客户端未连接到服务器";
                return false;
            }
            response = null;
            lock (_sendLock)
            {
                try
                {
                    // 发送消息
                    _pipeClient.WriteMessage(message);
                    return true;
                    while (_isConnected && !_cancellationTokenSource.Token.IsCancellationRequested)
                    {
                        try
                        {
                            response = _pipeClient.ReadMessage();
                            break;
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"接收数据时发生错误: {ex.Message}");
                            break;
                        }
                    }
                    return true;
                }
                catch (Exception ex)
                {
                    return false;
                }
            }
        }
        public void Disconnect()
        {
            _isConnected = false;
            _cancellationTokenSource.Cancel();
        }
        public void Dispose()
        {
            if (!_isDisposed)
            {
                Disconnect();
                _pipeClient?.Dispose();
                _cancellationTokenSource.Dispose();
                _isDisposed = true;
            }
        }
    }
}
ConnectionUtil/PipeClient2.cs
New file
@@ -0,0 +1,130 @@
using System;
using System.IO;
using System.IO.Pipes;
using System.Threading;
namespace ConnectionUtil
{
    public class PipeClient2 : IDisposable
    {
        private readonly string _pipeName;
        private readonly string _serverName;
        private readonly CancellationTokenSource _cancellationTokenSource;
        private readonly ReaderWriterLockSlim _lock;
        private NamedPipeClientStream _pipeClient;
        private bool _isDisposed;
        private bool _isConnected;
        private Thread _receiveThread;
        // 事件定义
        public event EventHandler<PipeDataEventArgs> OnData;
        public event EventHandler OnConnected;
        public event EventHandler OnDisconnected;
        public PipeClient2(string pipeName, string serverName = ".")
        {
            _pipeName = pipeName;
            _serverName = serverName;
            _cancellationTokenSource = new CancellationTokenSource();
            _lock = new ReaderWriterLockSlim();
        }
        public void Connect(int timeoutMs = 5000)
        {
            if (_isConnected)
                return;
            try
            {
                // 加上 PipeOptions.Asynchronous 可以异步接受服务器发送的数据,但是很影响性能
                _pipeClient = new NamedPipeClientStream(_serverName, _pipeName, PipeDirection.InOut);
                // 创建超时取消令牌
                using (var timeoutCts = new CancellationTokenSource(timeoutMs))
                {
                    _pipeClient.Connect(timeoutMs);
                    _pipeClient.ReadMode = PipeTransmissionMode.Message;
                    _isConnected = true;
                    // 触发连接成功事件
                    OnConnected?.Invoke(this, EventArgs.Empty);
                    // 开始接收数据
                    //_receiveThread = new Thread(ReceiveData);
                    //_receiveThread.Start();
                }
            }
            catch (Exception ex)
            {
                throw;
            }
        }
        private void ReceiveData()
        {
            try
            {
                while (_isConnected && !_cancellationTokenSource.Token.IsCancellationRequested)
                {
                    string message = _pipeClient.ReadMessage();
                    if (string.IsNullOrEmpty(message))
                        break;
                    // 触发数据接收事件
                    OnData?.Invoke(this, new PipeDataEventArgs("", message));
                }
            }
            finally
            {
                _isConnected = false;
                // 触发断开连接事件
                OnDisconnected?.Invoke(this, EventArgs.Empty);
            }
        }
        public bool SendMessage(string message, out string response, int timeoutMs = 5000)
        {
            response = null;
            if (!_isConnected)
                return false;
            try
            {
                _lock.EnterWriteLock();
                try
                {
                    _pipeClient.WriteMessage(message);
                }
                finally
                {
                    _lock.ExitWriteLock();
                }
                response = _pipeClient.ReadMessage();
                return true;
            }
            catch (Exception ex)
            {
                response = ex.Message;
                return false;
            }
        }
        public void Disconnect()
        {
            _isConnected = false;
            _cancellationTokenSource.Cancel();
            _receiveThread?.Join(1000); // 等待接收线程结束
        }
        public void Dispose()
        {
            if (!_isDisposed)
            {
                Disconnect();
                _pipeClient?.Dispose();
                _cancellationTokenSource.Dispose();
                _lock.Dispose();
                _isDisposed = true;
            }
        }
    }
}
ConnectionUtil/PipeServer.cs
New file
@@ -0,0 +1,347 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConnectionUtil
{
    public class PipeServer : IDisposable
    {
        private readonly string _pipeName;
        private readonly int _maxNumberOfServerInstances;
        private readonly CancellationTokenSource _cancellationTokenSource;
        private readonly ConcurrentDictionary<string, NamedPipeServerStream> _activeConnections;
        private bool _isDisposed;
        // 事件定义
        public event EventHandler<PipeDataEventArgs> OnData;
        public event EventHandler<PipeConnectionEventArgs> OnNewConnection;
        public event EventHandler<PipeConnectionEventArgs> OnClientDisconnected;
        public event EventHandler<PipeErrorEventArgs> OnError;
        public PipeServer(string pipeName, int maxNumberOfServerInstances = NamedPipeServerStream.MaxAllowedServerInstances)
        {
            _pipeName = pipeName;
            _maxNumberOfServerInstances = maxNumberOfServerInstances;
            _cancellationTokenSource = new CancellationTokenSource();
            _activeConnections = new ConcurrentDictionary<string, NamedPipeServerStream>();
        }
        public void Start()
        {
            Task.Run(() =>
            {
                try
                {
                    while (!_cancellationTokenSource.Token.IsCancellationRequested)
                    {
                        var pipeServer = new NamedPipeServerStream(_pipeName, PipeDirection.InOut,
                            _maxNumberOfServerInstances, PipeTransmissionMode.Message);
                        try
                        {
                            pipeServer.WaitForConnection();
                            // 生成唯一的连接ID
                            string connectionId = Guid.NewGuid().ToString();
                            _activeConnections.TryAdd(connectionId, pipeServer);
                            // 触发新连接事件
                            OnNewConnection?.Invoke(this, new PipeConnectionEventArgs(connectionId));
                            // 开始处理该连接的数据
                            HandleClient(connectionId, pipeServer);
                        }
                        catch (OperationCanceledException)
                        {
                            pipeServer.Dispose();
                            break;
                        }
                        catch (Exception ex)
                        {
                            pipeServer.Dispose();
                            OnError?.Invoke(this, new PipeErrorEventArgs("等待连接时发生错误", ex));
                        }
                    }
                }
                catch (Exception ex)
                {
                    OnError?.Invoke(this, new PipeErrorEventArgs("服务器运行时发生错误", ex));
                }
            });
        }
        static string ReadMessage(PipeStream s)
        {
            MemoryStream ms = new MemoryStream();
            byte[] buffer = new byte[0x1000]; // Read in 4 KB blocks
            do
            {
                ms.Write(buffer, 0, s.Read(buffer, 0, buffer.Length));
            }
            while (!s.IsMessageComplete);
            return Encoding.UTF8.GetString(ms.ToArray());
        }
        private void HandleClient(string connectionId, NamedPipeServerStream pipeServer)
        {
            Task.Run(() =>
            {
                try
                {
                    while (pipeServer.IsConnected && !_cancellationTokenSource.Token.IsCancellationRequested)
                    {
                        try
                        {
                            string message = ReadMessage(pipeServer);
                            if (string.IsNullOrEmpty(message))
                                continue;
                            Console.WriteLine(message);
                            if (message == "ping")
                            {
                                message = "pong";
                            }
                            else
                            {
                                message += "Response";
                            }
                            SendMessage(connectionId, message);
                        }
                        catch (Exception ex)
                        {
                            OnError?.Invoke(this, new PipeErrorEventArgs($"处理客户端 {connectionId} 数据时发生错误", ex));
                            break;
                        }
                    }
                }
                finally
                {
                    // 清理连接
                    _activeConnections.TryRemove(connectionId, out _);
                    pipeServer.Dispose();
                    // 触发断开连接事件
                    OnClientDisconnected?.Invoke(this, new PipeConnectionEventArgs(connectionId));
                }
            });
        }
        public void SendMessage(string connectionId, string message)
        {
            if (_activeConnections.TryGetValue(connectionId, out var pipeServer))
            {
                try
                {
                    var msg = Encoding.UTF8.GetBytes(message);
                    pipeServer.Write(msg, 0, msg.Length);
                }
                catch (Exception ex)
                {
                    OnError?.Invoke(this, new PipeErrorEventArgs($"向客户端 {connectionId} 发送消息时发生错误", ex));
                }
            }
        }
        public async Task StartAsync()
        {
            try
            {
                while (!_cancellationTokenSource.Token.IsCancellationRequested)
                {
                    var pipeServer = new NamedPipeServerStream(_pipeName, PipeDirection.InOut,
                        _maxNumberOfServerInstances, PipeTransmissionMode.Message);
                    try
                    {
                        await pipeServer.WaitForConnectionAsync(_cancellationTokenSource.Token);
                        // 生成唯一的连接ID
                        string connectionId = Guid.NewGuid().ToString();
                        _activeConnections.TryAdd(connectionId, pipeServer);
                        // 触发新连接事件
                        OnNewConnection?.Invoke(this, new PipeConnectionEventArgs(connectionId));
                        // 开始处理该连接的数据
                        _ = HandleClientAsync(connectionId, pipeServer);
                    }
                    catch (OperationCanceledException)
                    {
                        pipeServer.Dispose();
                        break;
                    }
                    catch (Exception ex)
                    {
                        pipeServer.Dispose();
                        OnError?.Invoke(this, new PipeErrorEventArgs("等待连接时发生错误", ex));
                    }
                }
            }
            catch (Exception ex)
            {
                OnError?.Invoke(this, new PipeErrorEventArgs("服务器运行时发生错误", ex));
            }
        }
        private async Task HandleClientAsync(string connectionId, NamedPipeServerStream pipeServer)
        {
            try
            {
                using (var reader = new StreamReader(pipeServer))
                using (var writer = new StreamWriter(pipeServer))
                {
                    writer.AutoFlush = true;
                    while (pipeServer.IsConnected && !_cancellationTokenSource.Token.IsCancellationRequested)
                    {
                        try
                        {
                            string message = await reader.ReadLineAsync();
                            if (string.IsNullOrEmpty(message))
                                continue;
                            // 触发数据接收事件
                            OnData?.Invoke(this, new PipeDataEventArgs(connectionId, message));
                            // 发送响应
                            await writer.WriteLineAsync($"服务器已收到消息: {message}");
                        }
                        catch (Exception ex)
                        {
                            OnError?.Invoke(this, new PipeErrorEventArgs($"处理客户端 {connectionId} 数据时发生错误", ex));
                            break;
                        }
                    }
                }
            }
            finally
            {
                // 清理连接
                _activeConnections.TryRemove(connectionId, out _);
                pipeServer.Dispose();
                // 触发断开连接事件
                OnClientDisconnected?.Invoke(this, new PipeConnectionEventArgs(connectionId));
            }
        }
        public async Task SendMessageAsync(string connectionId, string message)
        {
            if (_activeConnections.TryGetValue(connectionId, out var pipeServer))
            {
                try
                {
                    using (var writer = new StreamWriter(pipeServer))
                    {
                        writer.AutoFlush = true;
                        await writer.WriteLineAsync(message);
                    }
                }
                catch (Exception ex)
                {
                    OnError?.Invoke(this, new PipeErrorEventArgs($"向客户端 {connectionId} 发送消息时发生错误", ex));
                }
            }
        }
        public void Stop()
        {
            _cancellationTokenSource.Cancel();
        }
        public void Dispose()
        {
            if (!_isDisposed)
            {
                Stop();
                foreach (var connection in _activeConnections.Values)
                {
                    connection.Dispose();
                }
                _activeConnections.Clear();
                _cancellationTokenSource.Dispose();
                _isDisposed = true;
            }
        }
    }
    public class PipeDataEventArgs : EventArgs
    {
        public string ConnectionId { get; }
        public string Data { get; }
        public PipeDataEventArgs(string connectionId, string data)
        {
            ConnectionId = connectionId;
            Data = data;
        }
    }
    public class PipeConnectionEventArgs : EventArgs
    {
        public string ConnectionId { get; }
        public PipeConnectionEventArgs(string connectionId)
        {
            ConnectionId = connectionId;
        }
    }
    public class PipeErrorEventArgs : EventArgs
    {
        public string Message { get; }
        public Exception Exception { get; }
        public PipeErrorEventArgs(string message, Exception exception)
        {
            Message = message;
            Exception = exception;
        }
    }
    public class StreamString
    {
        private Stream ioStream;
        private UnicodeEncoding streamEncoding;
        public StreamString(Stream ioStream)
        {
            this.ioStream = ioStream;
            streamEncoding = new UnicodeEncoding();
        }
        public string ReadString()
        {
            int len = 0;
            len = ioStream.ReadByte() * 256;
            len += ioStream.ReadByte();
            byte[] inBuffer = new byte[len];
            ioStream.Read(inBuffer, 0, len);
            return streamEncoding.GetString(inBuffer);
        }
        public int WriteString(string outString)
        {
            byte[] outBuffer = streamEncoding.GetBytes(outString);
            int len = outBuffer.Length;
            if (len > UInt16.MaxValue)
            {
                len = (int)UInt16.MaxValue;
            }
            ioStream.WriteByte((byte)(len / 256));
            ioStream.WriteByte((byte)(len & 255));
            ioStream.Write(outBuffer, 0, len);
            ioStream.Flush();
            return outBuffer.Length + 2;
        }
    }
}
ConnectionUtil/PipeServer2.cs
New file
@@ -0,0 +1,192 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConnectionUtil
{
    public static class PipeStreamUtil
    {
        public static string ReadMessage(this PipeStream ps)
        {
            MemoryStream ms = new MemoryStream();
            byte[] buffer = new byte[0x1000];
            do
            {
                ms.Write(buffer, 0, ps.Read(buffer, 0, buffer.Length));
            }
            while (!ps.IsMessageComplete);
            return Encoding.UTF8.GetString(ms.ToArray());
        }
        public static void WriteMessage(this PipeStream ps, string message)
        {
            var msg = Encoding.UTF8.GetBytes(message);
            ps.Write(msg, 0, msg.Length);
            ps.Flush();
        }
    }
    public class PipeServer2 : IDisposable
    {
        private readonly string _pipeName;
        private readonly int _maxNumberOfServerInstances;
        private readonly CancellationTokenSource _cancellationTokenSource;
        private readonly ConcurrentDictionary<string, NamedPipeServerStream> _activeConnections;
        private readonly ReaderWriterLockSlim _lock;
        private bool _isDisposed;
        private bool _isRunning;
        // 事件定义
        public event EventHandler<PipeDataEventArgs> OnData;
        public event EventHandler<PipeConnectionEventArgs> OnClientConnected;
        public event EventHandler<PipeConnectionEventArgs> OnClientDisconnected;
        public event EventHandler<PipeErrorEventArgs> OnError;
        public PipeServer2(string pipeName, int maxNumberOfServerInstances = 254)
        {
            _pipeName = pipeName;
            _maxNumberOfServerInstances = maxNumberOfServerInstances;
            _cancellationTokenSource = new CancellationTokenSource();
            _activeConnections = new ConcurrentDictionary<string, NamedPipeServerStream>();
            _lock = new ReaderWriterLockSlim();
        }
        public void Start()
        {
            if (_isRunning)
                return;
            _isRunning = true;
            Task.Run(() =>
            {
                while (_isRunning && !_cancellationTokenSource.Token.IsCancellationRequested)
                {
                    try
                    {
                        var pipeServer = new NamedPipeServerStream(_pipeName, PipeDirection.InOut, _maxNumberOfServerInstances
                            , PipeTransmissionMode.Message);
                        pipeServer.WaitForConnection();
                        string clientId = Guid.NewGuid().ToString();
                        if (_activeConnections.TryAdd(clientId, pipeServer))
                        {
                            // 启动处理客户端消息的线程
                            Thread clientThread = new Thread(() => HandleClient(clientId, pipeServer));
                            clientThread.Start();
                            OnClientConnected?.Invoke(this, new PipeConnectionEventArgs(clientId));
                        }
                    }
                    catch (Exception ex)
                    {
                        OnError?.Invoke(this, new PipeErrorEventArgs("Connect", ex));
                        Thread.Sleep(1000); // 发生错误时等待一秒再重试
                    }
                }
            });
        }
        private void HandleClient(string clientId, NamedPipeServerStream pipeServer)
        {
            try
            {
                while (!_cancellationTokenSource.Token.IsCancellationRequested)
                {
                    try
                    {
                        // 等待客户端发送消息
                        string message = pipeServer.ReadMessage();
                        if (string.IsNullOrEmpty(message))
                            break;
                        OnData?.Invoke(this, new PipeDataEventArgs(clientId, message));
                    }
                    catch (Exception ex)
                    {
                        OnError?.Invoke(this, new PipeErrorEventArgs("HandleClientMessageError", ex));
                        break;
                    }
                }
            }
            finally
            {
                if (_activeConnections.TryRemove(clientId, out var pipe))
                {
                    pipe.Dispose();
                }
                OnClientDisconnected?.Invoke(this, new PipeConnectionEventArgs(clientId));
            }
        }
        public void SendMessage(string clientId, string message)
        {
            if (!_activeConnections.TryGetValue(clientId, out var pipeServer))
            {
                throw new InvalidOperationException($"找不到客户端 {clientId}");
            }
            _lock.EnterWriteLock();
            try
            {
                try
                {
                    pipeServer.WriteMessage(message);
                }
                catch (Exception ex)
                {
                    OnError?.Invoke(this, new PipeErrorEventArgs($"向客户端 {clientId} 发送消息时发生错误", ex));
                }
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }
        public void BroadcastMessage(string message)
        {
            _lock.EnterWriteLock();
            try
            {
                foreach (var pipeServer in _activeConnections.Values)
                {
                    pipeServer.WriteMessage(message);
                }
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }
        public void Stop()
        {
            _isRunning = false;
            _cancellationTokenSource.Cancel();
        }
        public void Dispose()
        {
            if (!_isDisposed)
            {
                Stop();
                foreach (var pipeServer in _activeConnections.Values)
                {
                    pipeServer.Dispose();
                }
                _activeConnections.Clear();
                _cancellationTokenSource.Dispose();
                _lock.Dispose();
                _isDisposed = true;
            }
        }
    }
}
ConnectionUtil/Properties/AssemblyInfo.cs
copy from VisionBuilder.Engine/Properties/AssemblyInfo.cs copy to ConnectionUtil/Properties/AssemblyInfo.cs
File was copied from VisionBuilder.Engine/Properties/AssemblyInfo.cs
@@ -5,12 +5,12 @@
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("VisionBuilder.Engine")]
[assembly: AssemblyTitle("ConnectionUtil")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("HP Inc.")]
[assembly: AssemblyProduct("VisionBuilder.Engine")]
[assembly: AssemblyCopyright("Copyright © HP Inc. 2024")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ConnectionUtil")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -20,7 +20,7 @@
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("8e5e8a68-0b9d-4a6a-b325-730c64603fdf")]
[assembly: Guid("d0806fa6-3f6d-4304-81b8-ead9da7cb5b2")]
// 程序集的版本信息由下列四个值组成: 
//
@@ -29,8 +29,5 @@
//      生成号
//      修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
ConnectionUtil/SCADAApi.cs
New file
@@ -0,0 +1,161 @@
using System;
using System.Collections.Generic;
using System.Net.Sockets;
namespace ConnectionUtil
{
    public interface SCADAApi : IDisposable
    {
        /// <summary>
        /// 连接方法
        /// </summary>
        /// <param name="config"></param>
        /// <param name="errMsg"></param>
        /// <returns></returns>
        bool Connect(object config, out string errMsg);
        /// <summary>
        /// IP连接Scada服务器
        /// </summary>
        /// <param name="IP">MES服务器IP</param>
        /// <param name="Port"> MES服务器Port口</param>
        /// <param name="LocalIP">本地连接IP</param>
        /// <param name="LocalPort">本地连接端口</param>
        /// <returns></returns>
        bool Connect(string IP, int Port, string LocalIP, int LocalPort);
        /// <summary>
        /// 连接Scada管理服务器
        /// </summary>
        /// <param name="IP"> Scada服务器IP</param>
        /// <param name="Port">ScadPort口</param>
        /// <param name="LocalPort">本地连接端口</param>
        /// <param name="errMsg"></param>
        /// <param name="socket"></param>
        /// <returns></returns>
        bool Connect2(string IP, int Port, int LocalPort, out string errMsg, Socket socket = null);
        /// <summary>
        ///  连接Scada管理服务器
        /// </summary>
        /// <param name="IP">ScadaManagerIP</param>
        /// <param name="Port">ScadaManagerPort口</param>
        /// <param name="LocalPort">本地连接端口</param>
        /// <param name="errMsg"></param>
        /// <returns></returns>
        bool ConnectManager(string IP, int Port, int LocalPort, out string errMsg);
        /// <summary>
        /// 断开服务器连接
        /// </summary>
        void DisConnect();
        /// <summary>
        /// 批量写增加参数
        /// </summary>
        /// <param name="id"></param>
        /// <param name="par"></param>
        /// <returns></returns>
        bool DoAddParWriteMultiple(int id, string par);
        /// <summary>
        /// 换型
        /// </summary>
        /// <param name="_ProductLineName"></param>
        /// <param name="_ProductName"></param>
        /// <param name="errMsg"></param>
        /// <param name="_WorkStationName"></param>
        /// <returns></returns>
        bool DoDoChangeModel(string _ProductLineName, string _ProductName, out string errMsg, string _WorkStationName = null);
        /// <summary>
        /// 批量读值
        /// </summary>
        /// <param name="_Id"></param>
        /// <returns></returns>
        string[,] DoFctReadMultiple(int[] _Id);
        /// <summary>
        /// 单一读值
        /// </summary>
        /// <param name="_name"></param>
        /// <param name="result"></param>
        /// <param name="errMsg"></param>
        /// <returns></returns>
        bool DoFctReadSingle(string _name, out string result, out string errMsg);
        /// <summary>
        /// 单一写值
        /// </summary>
        /// <param name="errMsg"></param>
        /// <returns></returns>
        bool DoFctWriteMultiple(out string errMsg);
        /// <summary>
        /// 写单一值
        /// </summary>
        /// <param name="_name"></param>
        /// <param name="_value"></param>
        /// <param name="errMsg"></param>
        /// <returns></returns>
        bool DoFctWriteSingle(string _name, string _value, out string errMsg);
        /// <summary>
        /// mqtt发布消息
        /// </summary>
        /// <param name="topic">主题</param>
        /// <param name="_value">值</param>
        /// <param name="errMsg">错误信息></param>
        /// <returns></returns>
        bool DoMqttPublish(string topic, string _value, out string errMsg);
        /// <summary>
        ///
        /// </summary>
        /// <param name="_user"></param>
        /// <param name="_password"></param>
        /// <param name="errMsg"></param>
        /// <returns></returns>
        bool DoLogin(string _user, string _password, out string errMsg);
        /// <summary>
        /// 重启DataServer服务器
        /// </summary>
        /// <param name="errMsg"></param>
        /// <returns></returns>
        bool DoReSETDataServer(out string errMsg);
        /// <summary>
        ///  获取动态IP
        /// </summary>
        /// <returns></returns>
        string getLocalIP();
        /// <summary>
        ///
        /// </summary>
        /// <param name="_paraMPairs"></param>
        /// <param name="errMsg"></param>
        /// <returns></returns>
        short[,] GetLowerComputerStatus(short[] _paraMPairs, out string errMsg);
        /// <summary>
        /// 初始化批量写参数
        /// </summary>
        void InitParWriteMultiple();
        /// <summary>
        /// 检查与LTSCADA是否断开连接了
        /// </summary>
        /// <param name="errMsg"></param>
        /// <returns></returns>
        bool IsConnected(out string errMsg);
        /// <summary>
        /// 批量写
        /// </summary>
        /// <param name="_paraMPairs">参数列表</param>
        /// <param name="errMsg">错误信息</param>
        /// <returns>处理结果</returns>
        bool ToFctWriteMultiple(Dictionary<int, string> _paraMPairs, out string errMsg);
    }
}
ConnectionUtil/TcpClient.cs
New file
@@ -0,0 +1,254 @@
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConnectionUtil
{
    /// <summary>
    /// TCP客户端包装类
    /// </summary>
    /// <summary>
    /// TCP客户端包装类
    /// </summary>
    public class TcpClientWrapper : IDisposable
    {
        private System.Net.Sockets.TcpClient _client;
        private NetworkStream _stream;
        private readonly string _host;
        private readonly int _port;
        private bool _isConnected;
        private readonly CancellationTokenSource _cts;
        private Task _heartbeatTask;
        private readonly SemaphoreSlim _sendReceiveLock;
        /// <summary>
        /// 初始化TCP客户端
        /// </summary>
        /// <param name="host">服务器地址</param>
        /// <param name="port">服务器端口</param>
        public TcpClientWrapper(string host = "127.0.0.1", int port = 6543)
        {
            _host = host;
            _port = port;
            _cts = new CancellationTokenSource();
            _sendReceiveLock = new SemaphoreSlim(1, 1);
        }
        /// <summary>
        /// 异步连接到服务器
        /// </summary>
        /// <returns>连接是否成功</returns>
        public async Task<bool> ConnectAsync()
        {
            try
            {
                _client = new System.Net.Sockets.TcpClient();
                await _client.ConnectAsync(_host, _port);
                _stream = _client.GetStream();
                _isConnected = true;
                // 启动心跳任务
                _heartbeatTask = StartHeartbeatAsync(_cts.Token);
                Console.WriteLine($"成功连接到服务器 {_host}:{_port}");
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"连接服务器失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// 同步连接到服务器
        /// </summary>
        /// <returns>连接是否成功</returns>
        public bool Connect()
        {
            try
            {
                _client = new System.Net.Sockets.TcpClient();
                _client.Connect(_host, _port);
                _stream = _client.GetStream();
                _isConnected = true;
                // 启动心跳任务
                _heartbeatTask = StartHeartbeatAsync(_cts.Token);
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"连接服务器失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// 启动心跳检测
        /// </summary>
        private async Task StartHeartbeatAsync(CancellationToken token)
        {
            while (!token.IsCancellationRequested && _isConnected)
            {
                try
                {
                    await Task.Delay(20000, token); // 20秒发送一次心跳
                    if (_isConnected)
                    {
                        await SendMessageAsync("ping");
                    }
                }
                catch (OperationCanceledException)
                {
                    break;
                }
                catch (Exception)
                {
                    _isConnected = false;
                    break;
                }
            }
        }
        /// <summary>
        /// 发送消息并接收响应
        /// </summary>
        /// <typeparam name="T">响应数据类型</typeparam>
        /// <param name="data">要发送的数据对象</param>
        /// <param name="timeout">超时时间</param>
        /// <returns>服务器响应数据</returns>
        public async Task<string> SendMessageAsync(string message, TimeSpan? timeout = null)
        {
            if (!_isConnected)
            {
                throw new InvalidOperationException("未连接到服务器");
            }
            TimeSpan actualTimeout = timeout ?? TimeSpan.FromSeconds(30);
            if (!await _sendReceiveLock.WaitAsync(actualTimeout))
            {
                throw new TimeoutException("等待发送超时");
            }
            try
            {
                byte[] messageBytes = Encoding.UTF8.GetBytes(message + "\r\n");
                // 发送消息
                await _stream.WriteAsync(messageBytes, 0, messageBytes.Length);
                // 接收响应
                byte[] buffer = new byte[1024];
                StringBuilder response = new StringBuilder();
                int bytesRead;
                CancellationTokenSource cts = new CancellationTokenSource(actualTimeout);
                try
                {
                    // 读取直到遇到换行符
                    while ((bytesRead = await _stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
                    {
                        string received = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                        response.Append(received);
                        if (received.EndsWith("\r\n"))
                        {
                            break;
                        }
                        if (cts.Token.IsCancellationRequested)
                        {
                            throw new TimeoutException("接收响应超时");
                        }
                    }
                }
                finally
                {
                    cts.Dispose();
                }
                string jsonResponse = response.ToString().TrimEnd('\r', '\n');
                Console.WriteLine($"收到服务器响应:{jsonResponse}");
                // 反序列化响应
                return jsonResponse;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"发送消息失败:{ex.Message}");
                _isConnected = false;
                throw;
            }
            finally
            {
                _sendReceiveLock.Release();
            }
        }
        /// <summary>
        /// 同步发送消息并接收响应
        /// </summary>
        /// <typeparam name="T">响应数据类型</typeparam>
        /// <param name="data">要发送的数据对象</param>
        /// <param name="timeout">超时时间</param>
        /// <returns>服务器响应数据</returns>
        public string SendMessage(string message, TimeSpan? timeout = null)
        {
            timeout = timeout ?? TimeSpan.FromSeconds(30);
            try
            {
                // 使用 Task.Run 来执行异步操作,并等待结果
                return Task.Run(() => SendMessageAsync(message, timeout)).Result;
            }
            catch (AggregateException ae)
            {
                // 解包异常
                throw ae.InnerException;
            }
        }
        /// <summary>
        /// 检查是否已连接
        /// </summary>
        public bool IsConnected
        {
            get { return _isConnected; }
        }
        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            _cts.Cancel();
            _isConnected = false;
            if (_stream != null)
            {
                _stream.Dispose();
            }
            if (_client != null)
            {
                _client.Dispose();
            }
            _cts.Dispose();
            if (_sendReceiveLock != null)
            {
                _sendReceiveLock.Dispose();
            }
        }
        /// <summary>
        /// 关闭连接
        /// </summary>
        public void Close()
        {
            Dispose();
        }
    }
}
ConsoleTest/ConsoleTest.csproj
@@ -32,6 +32,26 @@
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x64\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
    <Prefer32Bit>true</Prefer32Bit>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
    <OutputPath>bin\x64\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>true</Optimize>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
    <Prefer32Bit>true</Prefer32Bit>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
@@ -48,12 +68,21 @@
  </ItemGroup>
  <ItemGroup>
    <None Include="App.config" />
    <None Include="Properties\app.manifest" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\..\ModBusTcpController\ModBusTcpController\ModBusTcpController.csproj">
      <Project>{49db0c9b-b239-4608-a91e-f5ed43afaf96}</Project>
      <Name>ModBusTcpController</Name>
    </ProjectReference>
    <ProjectReference Include="..\SimpleUtil\DIOController\DIOController.csproj">
      <Project>{34ffbafe-478f-4b6e-95e7-92a949011a9b}</Project>
      <Name>DIOController</Name>
    </ProjectReference>
    <ProjectReference Include="..\SimpleUtil\HMI.Util.Base\HMI.Util.Base.csproj">
      <Project>{d59e25d1-9b27-46a1-8438-92661dd23fae}</Project>
      <Name>HMI.Util.Base</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
ConsoleTest/Program.cs
@@ -1,4 +1,5 @@
using DIOController;
using HMI.Util.Base;
using ModBusTcp;
using System;
using System.Text;
@@ -8,6 +9,26 @@
    {
        static void Main(string[] args)
        {
            string msg;
            ModBusTcpController controller = new ModBusTcpController("127.0.0.1", 502);
            if (controller.Start(out msg))
            {
                bool res;
                int? target;
                controller.WriteBool("1", true, out msg);
                controller.ReadDOBool("1", out res, out msg);
                Console.WriteLine(res);
                controller.ReadDOInt("1", out target, out msg);
                Console.WriteLine(target);
                controller.WriteInt("1", 1, out msg);
                controller.ReadDOInt("1", out target, out msg);
                Console.WriteLine(target);
            }
            controller.Close();
            //AlarmUtil.GetProcessIdByName("TestPlatform");
            /// ModbusTcp
            //IDIOController controller = DIOControllerCreator
            //    .CreateController("C:\\Workspace\\VisualStudio\\WPFProject\\SimpleUtil\\DIOController\\bin\\Release\\modbus.ini");
@@ -30,17 +51,17 @@
            //controller.ReadDOBool("0", out res, out msg);
            //controller.WriteBool("0", true, out msg);
            //controller.Dispose();
            string recvstr = "62 FD 82 39 39 39 37 32 32 36 36 30 38 34 33 39 35 33 38 53 38 44 41 30 30 32 32";
            string recvData;
            if (recvstr.Contains("FF"))
            {
                recvData = recvstr.Replace(" ", "").Substring(6);
            }
            else
            {
                byte[] bus = HexToByteArray(recvstr.Replace(" ", "").Substring(6));
                recvData = Encoding.Default.GetString(bus);
            }
            //string recvstr = "62 FD 82 39 39 39 37 32 32 36 36 30 38 34 33 39 35 33 38 53 38 44 41 30 30 32 32";
            //string recvData;
            //if (recvstr.Contains("FF"))
            //{
            //    recvData = recvstr.Replace(" ", "").Substring(6);
            //}
            //else
            //{
            //    byte[] bus = HexToByteArray(recvstr.Replace(" ", "").Substring(6));
            //    recvData = Encoding.Default.GetString(bus);
            //}
        }
        public static byte[] HexToByteArray(string hexString)
ConsoleTest/Properties/app.manifest
New file
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
    <assemblyIdentity version="1.0.0.0" name="MyApplication.app" />
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
        <security>
            <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
                <!-- UAC 清单选项
             如果想要更改 Windows 用户帐户控制级别,请使用
             以下节点之一替换 requestedExecutionLevel 节点。
        <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
        <requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />
        <requestedExecutionLevel  level="highestAvailable" uiAccess="false" />
            指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。
            如果你的应用程序需要此虚拟化来实现向后兼容性,则移除此
            元素。
        -->
                <requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />
            </requestedPrivileges>
            <applicationRequestMinimum>
                <defaultAssemblyRequest permissionSetReference="Custom" />
                <PermissionSet class="System.Security.PermissionSet" version="1" ID="Custom" SameSite="site" Unrestricted="true" />
            </applicationRequestMinimum>
        </security>
    </trustInfo>
    <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
        <application>
            <!-- 设计此应用程序与其一起工作且已针对此应用程序进行测试的
           Windows 版本的列表。取消评论适当的元素,
           Windows 将自动选择最兼容的环境。 -->
            <!-- Windows Vista -->
            <!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
            <!-- Windows 7 -->
            <!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
            <!-- Windows 8 -->
            <!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
            <!-- Windows 8.1 -->
            <!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
            <!-- Windows 10 -->
            <!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
        </application>
    </compatibility>
    <!-- 指示该应用程序可感知 DPI 且 Windows 在 DPI 较高时将不会对其进行
       自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI,无需
       选择加入。选择加入此设置的 Windows 窗体应用程序(面向 .NET Framework 4.6)还应
       在其 app.config 中将 "EnableWindowsFormsHighDpiAutoResizing" 设置设置为 "true"。
       将应用程序设为感知长路径。请参阅 https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
    <!--
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
      <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
    </windowsSettings>
  </application>
  -->
    <!-- 启用 Windows 公共控件和对话框的主题(Windows XP 和更高版本) -->
    <!--
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
          type="win32"
          name="Microsoft.Windows.Common-Controls"
          version="6.0.0.0"
          processorArchitecture="*"
          publicKeyToken="6595b64144ccf1df"
          language="*"
        />
    </dependentAssembly>
  </dependency>
  -->
</assembly>
HMI.AlermInfoManage/AlarmInfoManage.xaml.cs
@@ -93,7 +93,40 @@
        /// <summary>
        /// 代码管理
        /// </summary>
        private ObservableCollection<AlarmSetting> alarmSettings = new ObservableCollection<AlarmSetting>();
        private ObservableCollection<AlarmSetting> blockAlarmSettings = new ObservableCollection<AlarmSetting>();
        public ObservableCollection<AlarmSetting> BlockAlarmSettings
        {
            get => blockAlarmSettings;
            set
            {
                blockAlarmSettings = value;
                RefreshBlockCache();
            }
        }
        private Dictionary<string, Dictionary<string, string>> BlockMap = new Dictionary<string, Dictionary<string, string>>();
        private Dictionary<string, string> BlockReadCache = new Dictionary<string, string>();
        private void RefreshBlockCache()
        {
            BlockMap.Clear();
            BlockReadCache.Clear();
            if (BlockAlarmSettings == null || BlockAlarmSettings.Count <= 0)
            {
                return;
            }
            foreach (var setting in BlockAlarmSettings)
            {
                string key = $"{setting.DBAddress}.{setting.StartAddress}";
                if (!BlockMap.ContainsKey(key))
                {
                    BlockMap[key] = new Dictionary<string, string>();
                }
                var codes = BlockMap[key];
                codes[setting.AlarmCode] = setting.Description;
            }
        }
        // <summary>
        /// 标志位报警管理
        /// </summary>
@@ -104,7 +137,7 @@
        /// </summary>
        private List<AlarmText> currentAlarmCachePlcToMain = new List<AlarmText>();
        private AlarmText alarm;
        //private AlarmText alarm;
        private SystemSetting systemSetting;
        private ObservableCollection<AlarmText> datasource = new ObservableCollection<AlarmText>();
@@ -186,7 +219,7 @@
            MonitorTimer = new Timer
            {
                Interval = 100,
                Interval = 1000,
            };
            MonitorTimer.Elapsed += MonitorTimer_Elapsed;
            if (!lTSCADAApi.Connect2(opcIP, opcPort, localPort, out string errMsg))
@@ -200,7 +233,7 @@
            PcTimer = new Timer
            {
                Interval = 100,
                Interval = 1000,
                AutoReset = false
            };
            PcTimer.Elapsed += PcTimer_Elapsed;
@@ -239,12 +272,12 @@
        private void OpenSettingWindow()
        {
            AlarmSettingList listModal = new AlarmSettingList(blockPath, markPath,alarmSettings,markAlarmSetting, log)
            AlarmSettingList listModal = new AlarmSettingList(blockPath, markPath, BlockAlarmSettings, markAlarmSetting, log)
            {
                Owner = Window.GetWindow(this)
            };
            listModal.ShowDialog();
            alarmSettings = listModal.Datasource;
            BlockAlarmSettings = listModal.Datasource;
            markAlarmSetting = listModal.MarkDatasource;
        }
@@ -420,12 +453,12 @@
        {
            try
            {
                alarmSettings = XmlHelper.DeserializeFromXml<ObservableCollection<AlarmSetting>>(blockPath);
                BlockAlarmSettings = XmlHelper.DeserializeFromXml<ObservableCollection<AlarmSetting>>(blockPath);
            }
            catch (Exception ex)
            {
                log.Error("LoadAlarmSetting Error.", ex);
                alarmSettings = new ObservableCollection<AlarmSetting>();
                BlockAlarmSettings = new ObservableCollection<AlarmSetting>();
            }
            try
@@ -469,7 +502,8 @@
                        case "AlarmCode":
                            AlarmText aalarm = AlarmTexts.FirstOrDefault(alarm => alarm.AlarmCode == MsgStr);
                            if (aalarm == null) break;
                            var data = new AlarmText {
                            var data = new AlarmText
                            {
                                Source = "PC",
                                AlarmCode = aalarm.AlarmCode,
                                AlarmInfo = aalarm.MAlarmText_CN,
@@ -596,6 +630,11 @@
        //    }
        //}
        /// <summary>
        /// PLC报警读取
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MonitorTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
@@ -614,26 +653,51 @@
                isHaveAlarm = false;
                List<AlarmText> cache = new List<AlarmText>();
                foreach (var item in alarmSettings)
                //foreach (var item in BlockAlarmSettings)
                //{
                //    var name = $"{item.DBAddress}.{item.StartAddress}";
                //    if (lTSCADAApi.DoFctReadSingle(name, out string alarmCode, out errMsg) && alarmCode != "0")
                //    {
                //        isHaveAlarm = true;
                //        if (!textMap.TryGetValue(alarmCode, out alarm)) {
                //            continue;
                //        }
                //        var newAlarm = new AlarmText
                //        {
                //            Source = "PLC",
                //            AlarmCode = alarm.AlarmCode,
                //            ModuleName_CN = alarm.ModuleName_CN,
                //            MAlarmText_CN = alarm.MAlarmText_CN,
                //            Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                //            AlarmInfo = $"{alarm.ModuleName_CN} {alarm.MAlarmText_CN}"
                //        };
                //        if (this.currentAlarmCachePlc
                //                .Find(alarm => alarm.AlarmCode == newAlarm.AlarmCode) == null)
                //        {
                //            this.currentAlarmCachePlc.Add(newAlarm);
                //        }
                //    }
                //}
                foreach (var key in BlockMap.Keys)
                {
                    var name = $"{item.DBAddress}.{item.StartAddress}";
                    if (lTSCADAApi.DoFctReadSingle(name, out string alarmCode, out errMsg) && alarmCode != "0")
                    if (lTSCADAApi.DoFctReadSingle(key, out string alarmCode, out errMsg) && BlockMap[key].ContainsKey(alarmCode))
                    {
                        //if(BlockReadCache.ContainsKey(key) && alarmCode)
                        isHaveAlarm = true;
                        if (!textMap.TryGetValue(alarmCode, out alarm)) {
                            continue;
                        }
                        var text = BlockMap[key][alarmCode];
                        var newAlarm = new AlarmText
                        {
                            PLC_DB_BLOCK = key,
                            Source = "PLC",
                            AlarmCode = alarm.AlarmCode,
                            ModuleName_CN = alarm.ModuleName_CN,
                            MAlarmText_CN = alarm.MAlarmText_CN,
                            AlarmCode = alarmCode,
                            ModuleName_CN = text,
                            MAlarmText_CN = text,
                            Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                            AlarmInfo = $"{alarm.ModuleName_CN} {alarm.MAlarmText_CN}"
                            AlarmInfo = $"{alarmCode} {text}"
                        };
                        if (this.currentAlarmCachePlc
                                .Find(alarm => alarm.AlarmCode == newAlarm.AlarmCode) == null)
                                .Find(alarm => alarm.AlarmCode == newAlarm.AlarmCode && alarm.PLC_DB_BLOCK == newAlarm.PLC_DB_BLOCK) == null)
                        {
                            this.currentAlarmCachePlc.Add(newAlarm);
                        }
@@ -681,7 +745,7 @@
        private void RefreshDBSetting()
        {
            List<byte> bytes = new List<byte>();
            foreach (var item in alarmSettings)
            foreach (var item in BlockAlarmSettings)
            {
                //byte[] itemalarmCodeBytes = this.plc.Simens_S7_ReadBytes_DB("DB", item.DBAddress, item.StartAddress, item.Length);
                //bytes.AddRange(itemalarmCodeBytes.ToList());
@@ -719,16 +783,6 @@
                }
            }
            this.currentAlarmCachePc.Clear();
        }
        /// <summary>
        /// 发送日志消息
        /// </summary>
        /// <param name="Msg">日志消息</param>
        private void SendLogMsg(string Msg)
        {
            string LogMsg = "{\"Type\":\"Alarm\",\"Source\":\"" + alarm.Source + "\",\"Msg\":\"" + Msg + "\",\"Time\":\"" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\"}";
            this.loggeralarm.Send("Log", LogMsg);
        }
    }
}
HMI.AlermInfoManage/AlarmSettingList.xaml
@@ -85,7 +85,7 @@
                                    </GridViewColumn.CellTemplate>
                                </GridViewColumn>
                                <GridViewColumn Width="300">
                                <GridViewColumn Width="100">
                                    <GridViewColumn.Header>
                                        <TextBlock Text="{DynamicResource Alm.DbAddress}"/>
                                    </GridViewColumn.Header>
@@ -98,7 +98,7 @@
                                        </DataTemplate>
                                    </GridViewColumn.CellTemplate>
                                </GridViewColumn>
                                <GridViewColumn Width="200">
                                <GridViewColumn Width="150">
                                    <GridViewColumn.Header>
                                        <TextBlock Text="{DynamicResource Alm.StartAddress}"/>
                                    </GridViewColumn.Header>
@@ -111,7 +111,7 @@
                                        </DataTemplate>
                                    </GridViewColumn.CellTemplate>
                                </GridViewColumn>
                                <GridViewColumn Width="200">
                                <GridViewColumn Width="100">
                                    <GridViewColumn.Header>
                                        <TextBlock Text="{DynamicResource Alm.DbLength}"/>
                                    </GridViewColumn.Header>
@@ -124,6 +124,35 @@
                                        </DataTemplate>
                                    </GridViewColumn.CellTemplate>
                                </GridViewColumn>
                                <GridViewColumn Width="150">
                                    <GridViewColumn.Header>
                                        <TextBlock Text="报警代码"/>
                                    </GridViewColumn.Header>
                                    <GridViewColumn.CellTemplate>
                                        <DataTemplate>
                                            <Border Style="{StaticResource CellBorder}">
                                                <TextBlock Style="{StaticResource ListTableTextBlock}"
                   Text="{Binding AlarmCode}"/>
                                            </Border>
                                        </DataTemplate>
                                    </GridViewColumn.CellTemplate>
                                </GridViewColumn>
                                <GridViewColumn Width="250">
                                    <GridViewColumn.Header>
                                        <TextBlock Text="报警文本"/>
                                    </GridViewColumn.Header>
                                    <GridViewColumn.CellTemplate>
                                        <DataTemplate>
                                            <Border Style="{StaticResource CellBorder}">
                                                <TextBlock Style="{StaticResource ListTableTextBlock}"
Text="{Binding Description}"/>
                                            </Border>
                                        </DataTemplate>
                                    </GridViewColumn.CellTemplate>
                                </GridViewColumn>
                                <GridViewColumn Width="120">
                                    <GridViewColumn.Header>
                                        <TextBlock Text="{DynamicResource App.Opt}"/>
HMI.AlermInfoManage/Entity/AlarmSetting.cs
@@ -25,6 +25,9 @@
        [ExcelColumn("长度")]
        public int Length { get; set; }
        [ExcelColumn("报警代码")]
        public string AlarmCode { get; set; }
        [ExcelColumn("报警描述")]
        public string Description { get; set; }
    }
HMI.AlermInfoManage/Entity/AlarmText.cs
@@ -67,5 +67,8 @@
        /// </summary> 
        [XmlIgnore]
        public string Source { get; set; }
        [XmlIgnore]
        public string PLC_DB_BLOCK { get; set; }
    }
}
HMI.AlermInfoManage/HMI.AlarmInfoManage.csproj
@@ -155,6 +155,7 @@
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
    <None Include="app.config" />
    <None Include="packages.config" />
    <None Include="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
@@ -162,8 +163,8 @@
    </None>
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\..\ConnectionUtil\ConnectionUtil\ConnectionUtil.csproj">
      <Project>{D0806FA6-3F6D-4304-81B8-EAD9DA7CB5B2}</Project>
    <ProjectReference Include="..\ConnectionUtil\ConnectionUtil.csproj">
      <Project>{d0806fa6-3f6d-4304-81b8-ead9da7cb5b2}</Project>
      <Name>ConnectionUtil</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.Base\HMI.Base.csproj">
HMI.AlermInfoManage/app.config
New file
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
HMI.Core/Setting/SystemSetting.cs
@@ -27,7 +27,8 @@
        public string LogoPath { get; private set; } = ".\\JingCeLogo.png";
        public string WindowTitle { get; private set; } = "产品测试平台";
        public string PlcIp { get; private set; }
        public string PlcIp { get; private set; } = "127.0.0.1";
        public int PlcPort { get; private set; } = 102;
        public bool PlcEnabled { get; private set; }
        public string CurrentLanguage { get; private set; } = "zh-CN";
        public int LogExpiredDays { get; private set; } = 7;
@@ -94,6 +95,13 @@
            set { Set(ref plcIp, value); }
        }
        private int plcPort;
        public int PlcPort
        {
            get => plcPort;
            set { Set(ref plcPort, value); }
        }
        private bool plcEnabled;
        public bool PlcEnabled
        {
HMI.DataViewer/DataViewer.xaml.cs
@@ -176,7 +176,7 @@
            Map.Clear();
            foreach (var data in settings)
            {
                Map.Add(data.Program, data);
                Map[data.Program] = data;
                names.Add(data.Program);
            }
            programInput.ItemsSource = names;
HMI.IOMonitor/Entity/DIO.cs
File was deleted
HMI.IOMonitor/Entity/IPManage.cs
File was deleted
HMI.IOMonitor/HMI.IOMonitor.csproj
File was deleted
HMI.IOMonitor/IOMonitor.xaml
File was deleted
HMI.IOMonitor/IOMonitor.xaml.cs
File was deleted
HMI.IOMonitor/IPManageModal.xaml
File was deleted
HMI.IOMonitor/IPManageModal.xaml.cs
File was deleted
HMI.IOMonitor/Properties/AssemblyInfo.cs
File was deleted
HMI.IOMonitor/Properties/Resources.Designer.cs
File was deleted
HMI.IOMonitor/Properties/Resources.resx
File was deleted
HMI.IOMonitor/Properties/Settings.Designer.cs
File was deleted
HMI.IOMonitor/Properties/Settings.settings
File was deleted
HMI.IOMonitor/packages.config
File was deleted
HMI.MenuManage/MenuManage.xaml.cs
@@ -84,7 +84,7 @@
        {
            ObservableCollection<MenuData> data = new ObservableCollection<MenuData>();
            ObservableCollection<MenuData> treeData = new ObservableCollection<MenuData>();
            DataTable dt = sqliteHelper.GetDataTable("SELECT m1.*,m2.name AS parentName FROM menu AS m1 LEFT JOIN menu AS m2 ON m1.parentCode = m2.code Order By m1.sort,m1.id;");
            DataTable dt = sqliteHelper.GetDataTable("SELECT m1.*,m2.name AS parentName FROM menu AS m1 LEFT JOIN menu AS m2 ON m1.parentCode = m2.code Order By IIF(m1.level=1,m1.sort,m2.sort),IIF(m1.level=1,0,m1.sort),m1.id;");
            Dictionary<string, MenuData> cache = new Dictionary<string, MenuData>();
            if (dt != null)
            {
HMI.ParameterManage/ParameterModal.xaml.cs
@@ -73,9 +73,10 @@
        private bool ValidateForm()
        {
            bool flag = FormGroupHelper.ValidateForm(formPanel, new Dictionary<string, object> {
            var cache = new Dictionary<string, object> {
                { "FgValue",GetTargetValue()}
            });
            };
            bool flag = FormGroupHelper.ValidateForm(formPanel, cache);
            if (flag)
            {
                Data.Name = TbName.Text;
@@ -106,11 +107,12 @@
        private string GetTargetValue()
        {
            if (CbType.Text == "Int" || Data.Type == "Double")
            var type = CbType.Text;
            if (type == "Int" || type == "Double")
            {
                return NiNumberValue.Text;
            }
            else if (CbType.Text == "Bool")
            else if (type == "Bool")
            {
                return CbBoolValue.Text;
            }
HMI.ParameterManage/SettingModal.xaml.cs
@@ -122,11 +122,12 @@
        private string GetTargetValue()
        {
            if (CbType.Text == "Int" || Data.Type == "Double")
            var type = CbType.Text;
            if (type == "Int" || type == "Double")
            {
                return NiNumberValue.Text;
            }
            else if (CbType.Text == "Bool")
            else if (type == "Bool")
            {
                return CbBoolValue.Text;
            }
HMI.SettingControl/SystemConfig.xaml
@@ -67,9 +67,11 @@
        <GroupBox Grid.Row="1" Header="{DynamicResource Setting.PlcSetting}" Padding="10">
            <StackPanel>
                <pu:FormGroup Header="{DynamicResource Setting.PlcIp}" HeaderWidth="Auto" GroupName="MyFormGroup">
                    <TextBox Text="{Binding PlcIp}"/>
                    <TextBox Text="{Binding PlcIp,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                </pu:FormGroup>
                <pu:FormGroup Header="PLC Port" HeaderWidth="Auto" GroupName="MyFormGroup">
                    <pu:NumberInput Value="{Binding PlcPort,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                </pu:FormGroup>
                <pu:FormGroup Header="{DynamicResource Setting.PlcEnabled}" HeaderWidth="Auto" GroupName="MyFormGroup">
                    <CheckBox IsChecked="{Binding PlcEnabled,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                </pu:FormGroup>
HMI.StdControl/HMI.StdControl.csproj
copy from VisionBuilder.Engine/VisionBuilder.Engine.csproj copy to HMI.StdControl/HMI.StdControl.csproj
File was copied from VisionBuilder.Engine/VisionBuilder.Engine.csproj
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{8E5E8A68-0B9D-4A6A-B325-730C64603FDF}</ProjectGuid>
    <ProjectGuid>{7A066856-182F-4435-B648-E9FB91AFF657}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>VisionBuilder.Engine</RootNamespace>
    <AssemblyName>VisionBuilder.Engine</AssemblyName>
    <RootNamespace>HMI.StdControl</RootNamespace>
    <AssemblyName>HMI.StdControl</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <Deterministic>true</Deterministic>
@@ -30,41 +30,36 @@
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x64\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
    <OutputPath>bin\x64\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>true</Optimize>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xaml" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xml" />
    <Reference Include="WindowsBase" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Runner.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="StdEnableMes.xaml.cs">
      <DependentUpon>StdEnableMes.xaml</DependentUpon>
    </Compile>
    <Compile Include="StdUIEventArg.cs" />
  </ItemGroup>
  <ItemGroup>
    <Page Include="StdEnableMes.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\HMI.Core\HMI.Core.csproj">
      <Project>{3e3bf44a-0b69-45c5-9512-528cb48a7106}</Project>
      <Project>{3E3BF44A-0B69-45C5-9512-528CB48A7106}</Project>
      <Name>HMI.Core</Name>
    </ProjectReference>
  </ItemGroup>
HMI.StdControl/Properties/AssemblyInfo.cs
copy from VisionBuilder.Engine/Properties/AssemblyInfo.cs copy to HMI.StdControl/Properties/AssemblyInfo.cs
File was copied from VisionBuilder.Engine/Properties/AssemblyInfo.cs
@@ -5,12 +5,12 @@
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("VisionBuilder.Engine")]
[assembly: AssemblyTitle("HMI.StdControl")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("HP Inc.")]
[assembly: AssemblyProduct("VisionBuilder.Engine")]
[assembly: AssemblyCopyright("Copyright © HP Inc. 2024")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("HMI.StdControl")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -20,7 +20,7 @@
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("8e5e8a68-0b9d-4a6a-b325-730c64603fdf")]
[assembly: Guid("7a066856-182f-4435-b648-e9fb91aff657")]
// 程序集的版本信息由下列四个值组成: 
//
@@ -29,8 +29,5 @@
//      生成号
//      修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
HMI.StdControl/StdEnableMes.xaml
New file
@@ -0,0 +1,12 @@
<UserControl x:Class="HMI.StdControl.StdEnableMes"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:HMI.StdControl"
             mc:Ignorable="d"
             d:DesignHeight="450" d:DesignWidth="800">
    <CheckBox x:Name="CbEnableMes" IsChecked="{Binding EnableMes,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
              VerticalContentAlignment="Center"
              Content="{DynamicResource Ts.EnableMes}" />
</UserControl>
HMI.StdControl/StdEnableMes.xaml.cs
New file
@@ -0,0 +1,102 @@
using HMI.Core.Manager;
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Controls;
namespace HMI.StdControl
{
    /// <summary>
    /// StdEnableMes.xaml 的交互逻辑
    /// </summary>
    public partial class StdEnableMes : UserControl, INotifyPropertyChanged
    {
        #region ...
        public virtual event PropertyChangedEventHandler PropertyChanged;
        protected virtual void Set([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        protected void Set<T>(ref T old, T @new, [CallerMemberName] string propertyName = null)
        {
            old = @new;
            if (propertyName != null)
            {
                Set(propertyName);
            }
        }
        #endregion
        private event EventHandler<StdUIEventArg> valueChanged;
        public event EventHandler<StdUIEventArg> ValueChanged
        {
            add
            {
                valueChanged += value;
            }
            remove
            {
                valueChanged -= value;
            }
        }
        private bool enableMes;
        public bool EnableMes
        {
            get => enableMes;
            set
            {
                if (WriteConfig("EnableMes", value))
                {
                    Set(ref enableMes, value);
                }
                Set(ref enableMes, value);
            }
        }
        public StdEnableMes()
        {
            DataContext = this;
            InitializeComponent();
            try
            {
                var value = ParameterManager.GetCommonParameterByName("EnableMes");
                if (value == null)
                {
                    this.Visibility = System.Windows.Visibility.Collapsed;
                }
                else
                {
                    enableMes = bool.Parse(value.Value);
                    Set("EnableMes");
                }
            }
            catch
            {
                enableMes = false;
                Set("EnableMes");
            }
        }
        private bool WriteConfig(string name, bool value)
        {
            try
            {
                valueChanged?.Invoke(this, new StdUIEventArg()
                {
                    StringValue = name,
                    BoolValue = value
                });
                return ParameterManager.UpdateCommonParameter(name, value, out string errMsg);
            }
            catch (Exception ex)
            {
                return false;
            }
        }
    }
}
HMI.StdControl/StdUIEventArg.cs
New file
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HMI.StdControl
{
   public class StdUIEventArg
    {
        public string StringValue { get; set; }
        public bool BoolValue { get; set; }
    }
}
HMI.UI/Controls/LightBall.cs
@@ -12,7 +12,7 @@
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
                            "Value",
                            typeof(bool),
                            typeof(bool?),
                            typeof(LightBall),
                            new FrameworkPropertyMetadata(false));
@@ -31,9 +31,9 @@
            }
        }
        public bool Value
        public bool? Value
        {
            get => (bool)GetValue(ValueProperty);
            get => (bool?)GetValue(ValueProperty);
            set
            {
                SetValue(ValueProperty, value);
HMI.UI/HMI.UI.csproj
@@ -140,6 +140,7 @@
    <Compile Include="ScannerSetting.xaml.cs">
      <DependentUpon>ScannerSetting.xaml</DependentUpon>
    </Compile>
    <Compile Include="ScrollLabel.cs" />
    <Compile Include="Style\BaseWindow.xaml.cs">
      <DependentUpon>BaseWindow.xaml</DependentUpon>
    </Compile>
HMI.UI/ScrollLabel.cs
New file
@@ -0,0 +1,143 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
namespace HMI.UI
{
    public class ScrollLabel : Control
    {
        static ScrollLabel()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ScrollLabel), new FrameworkPropertyMetadata(typeof(ScrollLabel)));
        }
        private DoubleAnimation animation;
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            // 在模板应用后启动初始动画
            var scrollViewer = GetTemplateChild("slv") as ScrollViewer;
            var label = GetTemplateChild("label") as Label;
            if (scrollViewer != null)
            {
                // 等待布局完成后再启动动画
                scrollViewer.Loaded += (s, e) =>
                {
                    var storyboard = new Storyboard();
                    animation = new DoubleAnimation
                    {
                        From = 0,
                        To = scrollViewer.ScrollableWidth,
                        Duration = new Duration(TimeSpan.FromSeconds(3)),
                        AutoReverse = true,
                        RepeatBehavior = RepeatBehavior.Forever,
                        BeginTime = TimeSpan.FromSeconds(0.2)
                    };
                    Storyboard.SetTarget(animation, scrollViewer);
                    Storyboard.SetTargetProperty(animation, new PropertyPath(ScrollViewerBehavior.HorizontalOffsetProperty));
                    storyboard.Children.Add(animation);
                    scrollViewer.Resources["ScrollAnimation"] = storyboard;
                    storyboard.Begin();
                };
            }
        }
        private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            //RefreshAnimation();
        }
        private void RefreshAnimation()
        {
            // 查找ScrollViewer并重新启动动画
            var scrollViewer = GetTemplateChild("slv") as ScrollViewer;
            if (scrollViewer != null)
            {
                scrollViewer.Dispatcher.BeginInvoke(new Action(() =>
                {
                    // 停止当前动画
                    var storyboard = scrollViewer.Resources["ScrollAnimation"] as Storyboard;
                    if (storyboard != null)
                    {
                        storyboard.Stop();
                        if (animation != null)
                        {
                            animation.To = scrollViewer.ScrollableWidth;
                        }
                        storyboard.Begin();
                    }
                }), System.Windows.Threading.DispatcherPriority.Background);
            }
        }
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.RegisterAttached("Value", typeof(string), typeof(ScrollLabel),
                new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnValueChanged)));
        private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var scrollLabel = obj as ScrollLabel;
            scrollLabel?.RefreshAnimation();
        }
        public event EventHandler<string> ValueChanged;
        public string Value
        {
            get { return (string)GetValue(ValueProperty); }
            set
            {
                SetValue(ValueProperty, value);
            }
        }
    }
    public static class ScrollViewerBehavior
    {
        public static readonly DependencyProperty HorizontalOffsetProperty =
            DependencyProperty.RegisterAttached("HorizontalOffset", typeof(double), typeof(ScrollViewerBehavior),
                new UIPropertyMetadata(0.0, OnHorizontalOffsetChanged));
        public static void SetHorizontalOffset(FrameworkElement target, double value)
        {
            target.SetValue(HorizontalOffsetProperty, value);
        }
        public static double GetHorizontalOffset(FrameworkElement target)
        {
            return (double)target.GetValue(HorizontalOffsetProperty);
        }
        private static void OnHorizontalOffsetChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            var view = target as ScrollViewer;
            if (view != null)
            {
                view.ScrollToHorizontalOffset((double)e.NewValue);
            }
        }
        public static readonly DependencyProperty VerticalOffsetProperty = DependencyProperty.RegisterAttached("VerticalOffset", typeof(double), typeof(ScrollViewerBehavior), new UIPropertyMetadata(0.0, OnVerticalOffsetChanged));
        public static void SetVerticalOffset(FrameworkElement target, double value)
        {
            target.SetValue(VerticalOffsetProperty, value);
        }
        public static double GetVerticalOffset(FrameworkElement target)
        {
            return (double)target.GetValue(VerticalOffsetProperty);
        }
        private static void OnVerticalOffsetChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            var view = target as ScrollViewer;
            if (view != null)
            {
                view.ScrollToVerticalOffset((double)e.NewValue);
            }
        }
    }
}
HMI.UI/Style/Colors.xaml
@@ -28,14 +28,19 @@
    <Brush x:Key="CellValue">#f6f7d7</Brush>
    <LinearGradientBrush x:Key="PrimaryColor1" StartPoint="0.5,0" EndPoint="0.5,1">
        <!--<GradientStop Color="#369bdb" Offset="0.027"/>
        <GradientStop Color="#2b577f" Offset="0.994"/>-->
    <!--<LinearGradientBrush x:Key="PrimaryColor1" StartPoint="0.5,0" EndPoint="0.5,1">
        <GradientStop Color="#557ec8" Offset="0.027"/>
        <GradientStop Color="#375aa3" Offset="0.994"/>
    </LinearGradientBrush>
    </LinearGradientBrush>-->
    <LinearGradientBrush x:Key="HoriBar" StartPoint="0,0.5" EndPoint="1,0.5">
    <Brush x:Key="PrimaryColor1">#22448b</Brush>
    <Brush x:Key="HoriBar">#22448b</Brush>
    <Brush x:Key="ReHoriBar">#22448b</Brush>
    <Brush x:Key="TableHeader">#22448b</Brush>
    <Brush x:Key="VerBar">#22448b</Brush>
    <Brush x:Key="ReVerBar">#22448b</Brush>
    <!--<LinearGradientBrush x:Key="HoriBar" StartPoint="0,0.5" EndPoint="1,0.5">
        <GradientStop Color="#5d87d2" Offset="0"/>
        <GradientStop Color="#22448b" Offset="1"/>
    </LinearGradientBrush>
@@ -61,9 +66,13 @@
        <GradientStop Color="#23458c" Offset="0"/>
        <GradientStop Color="#4268b1" Offset="0.5"/>
        <GradientStop Color="#5d87d2" Offset="1"/>
    </LinearGradientBrush>
    </LinearGradientBrush>-->
    <LinearGradientBrush x:Key="GreenVerBar" StartPoint="0.5,0" EndPoint="0.5,1">
    <Brush x:Key="GreenVerBar">#21c418</Brush>
    <Brush x:Key="DangerVerBar">#d64343</Brush>
    <!--<LinearGradientBrush x:Key="GreenVerBar" StartPoint="0.5,0" EndPoint="0.5,1">
        <GradientStop Color="#28ea1d" Offset="0"/>
        <GradientStop Color="#21c418" Offset="0.5"/>
        <GradientStop Color="#1b9e14" Offset="1"/>
@@ -73,7 +82,7 @@
        <GradientStop Color="#fd583e" Offset="0"/>
        <GradientStop Color="#d5341f" Offset="0.5"/>
        <GradientStop Color="#aa0e00" Offset="1"/>
    </LinearGradientBrush>
    </LinearGradientBrush>-->
    <RadialGradientBrush x:Key="Normal" GradientOrigin="0.5,0.5" 
                             RadiusX="0.75" 
HMI.UI/Style/MsgBoxStyle.xaml
@@ -5,7 +5,7 @@
xmlns:puconfig="clr-namespace:Panuon.WPF.UI.Configurations;assembly=Panuon.WPF.UI">
    <pu:MessageBoxXSettings x:Key="MsgBoxWarn" 
                                 InteropOwnersMask="True"
                                 InteropOwnersMask="False"
                                 InverseButtonsSequence="False">
        <pu:MessageBoxXSettings.WindowXStyle>
            <Style TargetType="pu:WindowX" BasedOn="{StaticResource {x:Static pu:MessageBoxX.WindowXStyleKey}}">
@@ -44,7 +44,7 @@
    </pu:MessageBoxXSettings>
    <pu:MessageBoxXSettings x:Key="MsgBoxError" 
                             InteropOwnersMask="True"
                             InteropOwnersMask="False"
                             InverseButtonsSequence="False">
        <pu:MessageBoxXSettings.WindowXStyle>
            <Style TargetType="pu:WindowX" BasedOn="{StaticResource {x:Static pu:MessageBoxX.WindowXStyleKey}}">
@@ -83,7 +83,7 @@
    </pu:MessageBoxXSettings>
    <pu:MessageBoxXSettings x:Key="MsgBoxInfo" 
                             InteropOwnersMask="True"
                             InteropOwnersMask="False"
                             InverseButtonsSequence="False">
        <pu:MessageBoxXSettings.WindowXStyle>
            <Style TargetType="pu:WindowX" BasedOn="{StaticResource {x:Static pu:MessageBoxX.WindowXStyleKey}}">
HMI.UI/Themes/Generic.xaml
@@ -29,10 +29,13 @@
                        <Ellipse Height="{TemplateBinding Size}" Width="{TemplateBinding Size}" VerticalAlignment="Center">
                            <Ellipse.Style>
                                <Style TargetType="Ellipse">
                                    <Setter Property="Fill" Value="{StaticResource RedLight}"/>
                                    <Setter Property="Fill" Value="Gray"/>
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding Value,RelativeSource={RelativeSource Mode=TemplatedParent}}" Value="True">
                                            <Setter Property="Fill" Value="{StaticResource GreenLight}"/>
                                        </DataTrigger>
                                        <DataTrigger Binding="{Binding Value,RelativeSource={RelativeSource Mode=TemplatedParent}}" Value="False">
                                            <Setter Property="Fill" Value="{StaticResource RedLight}"/>
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
@@ -142,4 +145,43 @@
            </Setter.Value>
        </Setter>
    </Style>
    <Style TargetType="{x:Type local:ScrollLabel}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ScrollLabel}">
                    <ScrollViewer Name="slv" IsHitTestVisible="False" Width="{TemplateBinding Width}"
                                  VerticalScrollBarVisibility="Hidden"
                                  BorderThickness="0"
                                  HorizontalScrollBarVisibility="Hidden">
                        <Label x:Name="label" Content="{Binding Path=Value, RelativeSource={RelativeSource TemplatedParent}}"
                               VerticalContentAlignment="Center"
                               BorderThickness="0"
                               Foreground="{TemplateBinding Foreground}"
                               Padding="{TemplateBinding Padding}"
                               Margin="{TemplateBinding Margin}"
                               HorizontalContentAlignment="Left" FontSize="{TemplateBinding FontSize}">
                            <!--<Label.Triggers>
                                <EventTrigger RoutedEvent="Label.Loaded">
                                    <BeginStoryboard HandoffBehavior="Compose">
                                        <Storyboard>
                                            <DoubleAnimation From="0"
                     To="{Binding ElementName=slv,Path=ScrollableWidth}"
                     Duration="0:0:3"
                     Storyboard.TargetName="slv"
                     AutoReverse="True"
                     RepeatBehavior="Forever"
                     BeginTime="0:0:0.2"
                     Storyboard.TargetProperty="(local:ScrollViewerBehavior.HorizontalOffset)">
                                            </DoubleAnimation>
                                        </Storyboard>
                                    </BeginStoryboard>
                                </EventTrigger>
                            </Label.Triggers>-->
                        </Label>
                    </ScrollViewer>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>
HMI.UnitTest/HMI.UnitTest.csproj
@@ -135,6 +135,10 @@
      <Project>{d0806fa6-3f6d-4304-81b8-ead9da7cb5b2}</Project>
      <Name>ConnectionUtil</Name>
    </ProjectReference>
    <ProjectReference Include="..\..\ModBusTcpController\ModBusTcpController\ModBusTcpController.csproj">
      <Project>{49db0c9b-b239-4608-a91e-f5ed43afaf96}</Project>
      <Name>ModBusTcpController</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.Base\HMI.Base.csproj">
      <Project>{2A6F9720-A890-4036-9D73-41A0DAE28341}</Project>
      <Name>HMI.Base</Name>
HMI.UnitTest/MainWindow.xaml.cs
@@ -3,6 +3,7 @@
using HMI.Core.Util;
using HMI.Simple.Util;
using log4net;
using ModBusTcp;
using Panuon.WPF.UI;
using Plugin.DataBoard;
using Plugin.TestResult_WPF;
@@ -25,7 +26,25 @@
            ProductModelHelper.GetModelBySNR("cc123", out string model, out string msg);
            LangUtil.Load();
            InitializeComponent();
            RebuildDb();
            //RebuildDb();
            ModBusTcpController controller = new ModBusTcpController("127.0.0.1", 502);
            if (controller.Start(out msg)) {
                bool res;
                int? target;
                controller.WriteBool("1", true, out msg);
                controller.ReadDOBool("1", out res, out msg);
                Console.WriteLine(res);
                controller.ReadDOInt("1", out target, out msg);
                Console.WriteLine(target);
                controller.WriteInt("1", 1, out msg);
                controller.ReadDOInt("1", out target, out msg);
                Console.WriteLine(target);
                Console.WriteLine(target);
            }
            //page.Children.Add(new UserControl1());
        }
@@ -123,38 +142,38 @@
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Task.Run(() =>
            {
                for (int j = 0; j < 1000; j++)
                {
                    string status = (j % 2 == 0) ? "Passed" : "Failed";
                    string d = (j % 2 == 0) ? "9" : "9999999999999999999999999999999999999999999999999999999999999999995";
                    tsExecution.AddTestData(0, $"ConnectivityCANRec,2,3,4,{d},6,{status}");
                    Thread.Sleep(5);
                }
            });
            //Task.Run(() =>
            //{
            //    for (int j = 0; j < 1000; j++)
            //    {
            //        string status = (j % 2 == 0) ? "Passed" : "Failed";
            //        string d = (j % 2 == 0) ? "9" : "9999999999999999999999999999999999999999999999999999999999999999995";
            //        tsExecution.AddTestData(0, $"ConnectivityCANRec,2,3,4,{d},6,{status}");
            //        Thread.Sleep(5);
            //    }
            //});
            Task.Run(() =>
            {
                for (int j = 0; j < 1000; j++)
                {
                    string status = (j % 2 == 0) ? "Passed" : "Failed";
                    string d = (j % 2 == 0) ? "9" : "9999999999999999999999999999999999999999999999999999999999999999995";
                    tsExecution.AddTestData(1, $"ConnectivityCANRec,2,3,4,{d},6,{status}");
                    Thread.Sleep(5);
                }
            });
            //Task.Run(() =>
            //{
            //    for (int j = 0; j < 1000; j++)
            //    {
            //        string status = (j % 2 == 0) ? "Passed" : "Failed";
            //        string d = (j % 2 == 0) ? "9" : "9999999999999999999999999999999999999999999999999999999999999999995";
            //        tsExecution.AddTestData(1, $"ConnectivityCANRec,2,3,4,{d},6,{status}");
            //        Thread.Sleep(5);
            //    }
            //});
            Task.Run(() =>
            {
                for (int j = 0; j < 1000; j++)
                {
                    string status = (j % 2 == 0) ? "Passed" : "Failed";
                    string d = (j % 2 == 0) ? "9" : "9999999999999999999999999999999999999999999999999999999999999999995";
                    tsExecution.AddTestData(2, $"ConnectivityCANRec,2,3,4,{d},6,{status}");
                    Thread.Sleep(5);
                }
            });
            //Task.Run(() =>
            //{
            //    for (int j = 0; j < 1000; j++)
            //    {
            //        string status = (j % 2 == 0) ? "Passed" : "Failed";
            //        string d = (j % 2 == 0) ? "9" : "9999999999999999999999999999999999999999999999999999999999999999995";
            //        tsExecution.AddTestData(2, $"ConnectivityCANRec,2,3,4,{d},6,{status}");
            //        Thread.Sleep(5);
            //    }
            //});
            //Task.Run(() =>
            //{
Libs/HslCommunication.dll
Binary files differ
Libs/lmes.dll
Binary files differ
ModBusTcpController/ModBusTcpController.cs
New file
@@ -0,0 +1,85 @@
using HslCommunication.ModBus;
namespace ModBusTcp
{
    public class ModBusTcpController
    {
        public string Ip { get; set; }
        public int Port { get; set; }
        private ModbusTcpNet client;
        public ModBusTcpController(string ip, int port)
        {
            Ip = ip;
            Port = port;
        }
        public bool Start(out string errMsg)
        {
            errMsg = string.Empty;
            client = new ModbusTcpNet(Ip, Port);
            var res = client.ConnectServer();
            errMsg = res.Message;
            return res.IsSuccess;
        }
        public bool ReadDIBool(string address, out bool res, out string msg)
        {
            var result = client?.ReadBool($"x=2;{address}");
            msg = result?.Message;
            res = result?.Content == true;
            return result?.IsSuccess == true;
        }
        public bool ReadDOBool(string address, out bool res, out string msg)
        {
            var result = client?.ReadBool(address);
            msg = result?.Message;
            res = result?.Content == true;
            return result?.IsSuccess == true;
        }
        public bool WriteBool(string address, bool value, out string msg)
        {
            msg = string.Empty;
            var res = client?.Write(address, value);
            msg = res.Message;
            return res?.IsSuccess == true;
        }
        public bool ReadDIInt(string address, out int? res, out string msg)
        {
            var result = client?.ReadInt32($"x=2;{address}");
            msg = result?.Message;
            res = result?.Content;
            return result?.IsSuccess == true;
        }
        public bool ReadDOInt(string address, out int? res, out string msg)
        {
            var result = client?.ReadInt32(address);
            msg = result?.Message;
            res = result?.Content;
            return result?.IsSuccess == true;
        }
        public bool WriteInt(string address, int value, out string msg)
        {
            msg = string.Empty;
            var res = client?.Write(address, value);
            msg = res.Message;
            return res?.IsSuccess == true;
        }
        public bool Close()
        {
            return client?.ConnectClose()?.IsSuccess == true;
        }
        public void Dispose()
        {
            client?.Dispose();
        }
    }
}
ModBusTcpController/ModBusTcpController.csproj
copy from VisionBuilder.Engine/VisionBuilder.Engine.csproj copy to ModBusTcpController/ModBusTcpController.csproj
File was copied from VisionBuilder.Engine/VisionBuilder.Engine.csproj
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{8E5E8A68-0B9D-4A6A-B325-730C64603FDF}</ProjectGuid>
    <ProjectGuid>{49DB0C9B-B239-4608-A91E-F5ED43AFAF96}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>VisionBuilder.Engine</RootNamespace>
    <AssemblyName>VisionBuilder.Engine</AssemblyName>
    <RootNamespace>ModBusTcpController</RootNamespace>
    <AssemblyName>ModBusTcpController</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <Deterministic>true</Deterministic>
@@ -30,24 +30,6 @@
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x64\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
    <OutputPath>bin\x64\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>true</Optimize>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
@@ -59,14 +41,8 @@
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Runner.cs" />
    <Compile Include="ModBusTcpController.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\HMI.Core\HMI.Core.csproj">
      <Project>{3e3bf44a-0b69-45c5-9512-528cb48a7106}</Project>
      <Name>HMI.Core</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
ModBusTcpController/Properties/AssemblyInfo.cs
File was renamed from VisionBuilder.Engine/Properties/AssemblyInfo.cs
@@ -5,12 +5,12 @@
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("VisionBuilder.Engine")]
[assembly: AssemblyTitle("ModBusTcpController")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("HP Inc.")]
[assembly: AssemblyProduct("VisionBuilder.Engine")]
[assembly: AssemblyCopyright("Copyright © HP Inc. 2024")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ModBusTcpController")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -20,7 +20,7 @@
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("8e5e8a68-0b9d-4a6a-b325-730c64603fdf")]
[assembly: Guid("49db0c9b-b239-4608-a91e-f5ed43afaf96")]
// 程序集的版本信息由下列四个值组成: 
//
@@ -29,8 +29,5 @@
//      生成号
//      修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
OPCClient/OPCClient.csproj
@@ -12,6 +12,8 @@
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <Deterministic>true</Deterministic>
    <NuGetPackageImportStamp>
    </NuGetPackageImportStamp>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
@@ -50,13 +52,14 @@
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
      <HintPath>..\WPFProject\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
    </Reference>
    <Reference Include="SupersocketCommon">
      <HintPath>..\Libs\SupersocketCommon.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Numerics" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
@@ -70,13 +73,14 @@
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\..\ConnectionUtil\ConnectionUtil\ConnectionUtil.csproj">
    <None Include="app.config" />
    <None Include="packages.config" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\ConnectionUtil\ConnectionUtil.csproj">
      <Project>{d0806fa6-3f6d-4304-81b8-ead9da7cb5b2}</Project>
      <Name>ConnectionUtil</Name>
    </ProjectReference>
  </ItemGroup>
  <ItemGroup>
    <None Include="packages.config" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
OPCClient/app.config
New file
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
OPCClient/packages.config
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Newtonsoft.Json" version="13.0.1" targetFramework="net472" />
  <package id="Newtonsoft.Json" version="13.0.3" targetFramework="net472" />
</packages>
Plugin.ChangeModel/ChangeModel.xaml
File was deleted
Plugin.ChangeModel/ChangeModel.xaml.cs
File was deleted
Plugin.ChangeModel/Entity/CmLogData.cs
File was deleted
Plugin.ChangeModel/Entity/ModelData.cs
File was deleted
Plugin.ChangeModel/Entity/WorkOrderData.cs
File was deleted
Plugin.ChangeModel/Plugin.ChangeModel.csproj
File was deleted
Plugin.CurrentSensorControl/AssemblyInfo.cs
File was renamed from Plugin.ChangeModel/AssemblyInfo.cs
@@ -1,3 +1,4 @@
using HMI.Core.Annotation;
using System.Windows;
[assembly:ThemeInfo(
@@ -8,3 +9,4 @@
                                                //(used if a resource is not found in the page,
                                                // app, or any theme specific resource dictionaries)
)]
[assembly: PluginClassName("Plugin.CurrentSensorControl.ControlPage")]
Plugin.CurrentSensorControl/BurnInCell.xaml
New file
@@ -0,0 +1,65 @@
<UserControl x:Class="Plugin.CurrentSensorControl.BurnInCell"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:Plugin.CurrentSensorControl"
             mc:Ignorable="d"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
xmlns:mdi="clr-namespace:HMI.UI.MDI;assembly=HMI.UI"
xmlns:pw="https://opensource.panuon.com/wpf"
xmlns:pu="https://opensource.panuon.com/wpf-ui"
xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <Style TargetType="pu:Switch">
            <Setter Property="ContentPlacement" Value="Left" />
            <Setter Property="BoxHeight" Value="16" />
            <Setter Property="CheckedBackground" Value="{StaticResource GreenVerBar}" />
            <Setter Property="Background" Value="{StaticResource DangerColor}" />
            <Setter Property="ToggleSize" Value="10" />
            <Setter Property="ToggleShadowColor" Value="{x:Null}" />
        </Style>
    </UserControl.Resources>
    <GroupBox Header="{Binding Header}">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <pu:Switch Grid.Row="0" Grid.Column="0" CheckedContent="电源开" x:Name="powerSw"
                       IsChecked="{Binding PowerStatus,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
                       Content="电源关"/>
            <pu:Switch Grid.Row="0" Grid.Column="1" CheckedContent="激活" x:Name="activationSw"
IsChecked="{Binding ActivationStatus,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Content="未激活"/>
            <pu:Switch Grid.Row="1" Grid.Column="0" CheckedContent="OK亮" x:Name="okLightSw"
                       IsChecked="{Binding OkLightOn,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Content="OK灭"/>
            <pu:Switch Grid.Row="1" Grid.Column="1" CheckedContent="NG亮" x:Name="ngLightSw"
                       IsChecked="{Binding NgLightOn,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Content="NG灭"/>
            <!--<ui:LightBall Grid.Row="1" Grid.Column="0" Value="{Binding CellStatus}" Content="OK/NG状态"/>-->
            <!--<TextBlock Grid.Row="0" Grid.Column="0"  Text="{Binding PowerStatus}"/>
            <TextBlock Grid.Row="0" Grid.Column="1"  Text="{Binding ActivationStatus}"/>-->
        </Grid>
    </GroupBox>
</UserControl>
Plugin.CurrentSensorControl/BurnInCell.xaml.cs
New file
@@ -0,0 +1,179 @@
using CommunityToolkit.Mvvm.ComponentModel;
using HslCommunication.Profinet.Siemens;
using Plugin.CurrentSensorControl.Entity;
using System.Windows.Controls;
namespace Plugin.CurrentSensorControl
{
    /// <summary>
    /// BurnInOven.xaml 的交互逻辑
    /// </summary>
    [ObservableObject]
    public partial class BurnInCell : UserControl
    {
        private BaseConfig config;
        [ObservableProperty]
        private string header;
        [ObservableProperty]
        private string req;
        [ObservableProperty]
        private bool lightOK;
        [ObservableProperty]
        private bool lightNG;
        private bool powerStatus;
        public bool PowerStatus
        {
            get => powerStatus;
            set
            {
                try
                {
                    string name = $"Oven{ovenIndex + 1}_Cell{cellIndex + 1}_Power";
                    if (S7Context.WriteData(api, name, value))
                    {
                        powerStatus = value;
                        OnPropertyChanged(nameof(PowerStatus));
                    }
                }
                catch (Exception ex)
                {
                }
            }
        }
        private bool activationStatus;
        public bool ActivationStatus
        {
            get => activationStatus;
            set
            {
                try
                {
                    string name = $"Oven{ovenIndex + 1}_Cell{cellIndex + 1}_Activation";
                    if (S7Context.WriteData(api, name, value))
                    {
                        activationStatus = value;
                        OnPropertyChanged(nameof(ActivationStatus));
                    }
                }
                catch (Exception ex)
                {
                }
            }
        }
        private bool okLightOn;
        public bool OkLightOn
        {
            get => okLightOn;
            set
            {
                try
                {
                    string name = $"Oven{ovenIndex + 1}_Cell{cellIndex + 1}_Light_OK";
                    if (S7Context.WriteData(api, name, value))
                    {
                        okLightOn = value;
                        OnPropertyChanged(nameof(OkLightOn));
                    }
                }
                catch (Exception ex)
                {
                }
            }
        }
        private bool ngLightOn;
        public bool NgLightOn
        {
            get => ngLightOn;
            set
            {
                try
                {
                    string name = $"Oven{ovenIndex + 1}_Cell{cellIndex + 1}_Light_NG";
                    if (S7Context.WriteData(api, name, value))
                    {
                        ngLightOn = value;
                        OnPropertyChanged(nameof(NgLightOn));
                    }
                }
                catch (Exception ex)
                {
                }
            }
        }
        [ObservableProperty]
        private bool? cellStatus;
        private int ovenIndex;
        private int cellIndex;
        private SiemensS7Net api;
        public BurnInCell(int ovenIndex, int cellIndex)
        {
            this.ovenIndex = ovenIndex;
            this.cellIndex = cellIndex;
            this.Header = $"Cell {cellIndex + 1}";
            DataContext = this;
            api = S7Context.Get($"1");
            config = BaseConfig.Instance;
            InitializeComponent();
        }
        public void Refresh()
        {
            try
            {
                string name = $"Oven{ovenIndex + 1}_Cell{cellIndex + 1}_Power";
                object val; string errMsg;
                if (S7Context.GetData(api, name, out val))
                {
                    powerStatus = (bool)val;
                    OnPropertyChanged("PowerStatus");
                }
                name = $"Oven{ovenIndex + 1}_Cell{cellIndex + 1}_Activation";
                if (S7Context.GetData(api, name, out val))
                {
                    activationStatus = (bool)val;
                    OnPropertyChanged("ActivationStatus");
                }
                name = $"Oven{ovenIndex + 1}_Cell{cellIndex + 1}_Light_OK";
                if (S7Context.GetData(api, name, out val))
                {
                    okLightOn = (bool)val;
                    OnPropertyChanged("OkLightOn");
                }
                name = $"Oven{ovenIndex + 1}_Cell{cellIndex + 1}_Light_NG";
                if (S7Context.GetData(api, name, out val))
                {
                    ngLightOn = (bool)val;
                    OnPropertyChanged("NgLightOn");
                }
            }
            catch (Exception ex)
            {
            }
        }
    }
}
Plugin.CurrentSensorControl/BurnInOven.xaml
New file
@@ -0,0 +1,78 @@
<UserControl x:Class="Plugin.CurrentSensorControl.BurnInOven"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:Plugin.CurrentSensorControl"
             mc:Ignorable="d"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
            xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
            xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
            xmlns:mdi="clr-namespace:HMI.UI.MDI;assembly=HMI.UI"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
            xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <Style x:Key="ovenTb" TargetType="TextBox" BasedOn="{StaticResource EditorTextBoxStyle}">
            <Setter Property="Height" Value="20"/>
            <Setter Property="MinWidth" Value="60"/>
            <Setter Property="FontSize" Value="14"/>
        </Style>
    </UserControl.Resources>
    <GroupBox Header="{Binding Header}">
        <Grid dp:GridHelper.Space="5" dp:GridHelper.SpaceIgnore="ALL">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition />
            </Grid.RowDefinitions>
            <StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal" dp:StackPanelHelper.ChildSpace="5">
                <pu:FormGroup Header="状态">
                    <TextBox Style="{StaticResource ovenTb}" IsReadOnly="True" Text="{Binding ReqStatus}"/>
                </pu:FormGroup>
                <pu:Switch CheckedContent="照明开" x:Name="lightSw"
                           CheckedBackground="Green"
                           Background="{StaticResource DangerColor}"
                           IsChecked="{Binding LightStatus,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
                           Content="照明关"/>
                <Button Background="{StaticResource PrimaryColor}" Foreground="{StaticResource PrimaryColorContract}"
                        Click="Button_Click"
                        Tag="0"
                        Content="停止等待"/>
                <Button Background="{StaticResource DangerColor}" Foreground="{StaticResource DangerColorContrast}"
                        Click="Button_Click"
                        Tag="1"
                        Content="加热启动"/>
                <Button Background="{StaticResource SuccessColor}" Foreground="{StaticResource SuccessColorContrast}"
                        Click="Button_Click"
                        Tag="2"
                        Content="冷却启动"/>
            </StackPanel>
            <StackPanel Grid.Row="1" Grid.Column="0" Orientation="Horizontal" dp:StackPanelHelper.ChildSpace="5">
                <pu:FormGroup Header="设定温度">
                    <TextBox Style="{StaticResource ovenTb}" IsReadOnly="True" Text="{Binding SetTemp}"/>
                </pu:FormGroup>
                <pu:FormGroup Header="安全温度">
                    <TextBox Style="{StaticResource ovenTb}" IsReadOnly="True" Text="{Binding SaveTemp}"/>
                </pu:FormGroup>
                <pu:FormGroup Header="温度下限">
                    <TextBox Style="{StaticResource ovenTb}" IsReadOnly="True" Text="{Binding TempDown}"/>
                </pu:FormGroup>
                <pu:FormGroup Header="温度上限">
                    <TextBox Style="{StaticResource ovenTb}" IsReadOnly="True" Text="{Binding TempUp}"/>
                </pu:FormGroup>
                <pu:FormGroup Header="平均温度">
                    <TextBox Style="{StaticResource ovenTb}" IsReadOnly="True" Text="{Binding AvgTemp}"/>
                </pu:FormGroup>
            </StackPanel>
            <UniformGrid Grid.Row="2" Grid.Column="0" x:Name="mainGrid"></UniformGrid>
        </Grid>
    </GroupBox>
</UserControl>
Plugin.CurrentSensorControl/BurnInOven.xaml.cs
New file
@@ -0,0 +1,233 @@
using CommunityToolkit.Mvvm.ComponentModel;
using ConnectionUtil;
using HslCommunication.Profinet.Siemens;
using Plugin.CurrentSensorControl.Entity;
using System.Drawing;
using System.Windows.Controls;
using System.Windows.Markup;
namespace Plugin.CurrentSensorControl
{
    /// <summary>
    /// BurnInOven.xaml 的交互逻辑
    /// </summary>
    [ObservableObject]
    public partial class BurnInOven : UserControl
    {
        private BaseConfig config;
        private static readonly string[] datas = new string[] { "_LEDLight", "_Req", "_SetTemp", "_SaveTemp", "_TempDown", "_TempUp", "_AvgTemp" };
        [ObservableProperty]
        private string header;
        [ObservableProperty]
        private string req;
        [ObservableProperty]
        private string reqStatus;
        [ObservableProperty]
        private string setTemp;
        [ObservableProperty]
        private string saveTemp;
        [ObservableProperty]
        private string tempDown;
        [ObservableProperty]
        private string tempUp;
        [ObservableProperty]
        private string avgTemp;
        private bool lightStatus;
        public bool LightStatus
        {
            get => lightStatus;
            set
            {
                try
                {
                    string name = $"Oven{ovenIndex + 1}_LEDLight";
                    if (S7Context.WriteData(api, name, value))
                    {
                        lightStatus = value;
                        OnPropertyChanged(nameof(lightStatus));
                    }
                }
                catch (Exception ex)
                {
                }
            }
        }
        private int ovenIndex;
        private SiemensS7Net api;
        private int cellCount;
        private BurnInCell[] cells;
        public BurnInOven(int ovenIndex)
        {
            this.ovenIndex = ovenIndex;
            config = BaseConfig.Instance;
            this.cellCount = config.PositionCount;
            this.cells = new BurnInCell[cellCount];
            DataContext = this;
            api = S7Context.Get($"1");
            InitializeComponent();
            InitGrid();
        }
        private void InitGrid()
        {
            mainGrid.Columns = config.PositionColumn;
            for (int i = 0; i < cellCount; i++)
            {
                var cell = new BurnInCell(ovenIndex, i);
                cells[i] = cell;
                mainGrid.Children.Add(cell);
            }
        }
        public void Refresh()
        {
            try
            {
                //Task[] allTask = new Task[cells.Length];
                //for (int i = 0; i < cells.Length; i++)
                //{
                //    var cell = cells[i];
                //    var task = Task.Factory.StartNew(() =>
                //    {
                //        cell.Refresh();
                //    });
                //    allTask[i] = task;
                //}
                //Task.WaitAll(allTask);
                foreach (var cell in cells)
                {
                    cell.Refresh();
                }
                string name;
                object v;
                foreach (var data in datas)
                {
                    name = $"Oven{ovenIndex + 1}{data}";
                    if (S7Context.GetData(api, name, out v))
                    {
                        var val = v?.ToString();
                        if (data == "_LEDLight")
                        {
                            lightStatus = bool.Parse(val);
                            OnPropertyChanged("LightStatus");
                        }
                        else if (data == "_Req")
                        {
                            Req = val;
                            if (val == "0")
                            {
                                ReqStatus = "停止等待";
                            }
                            else if (val == "1")
                            {
                                ReqStatus = "加热启动";
                            }
                            else if (val == "2")
                            {
                                ReqStatus = "冷却启动";
                            }
                        }
                        else if (data == "_SetTemp")
                        {
                            SetTemp = val;
                        }
                        else if (data == "_SaveTemp")
                        {
                            SaveTemp = val;
                        }
                        else if (data == "_TempDown")
                        {
                            TempDown = val;
                        }
                        else if (data == "_TempUp")
                        {
                            TempUp = val;
                        }
                        else if (data == "_AvgTemp")
                        {
                            AvgTemp = val;
                        }
                    }
                    //;
                    //if (api.DoFctReadSingle(name, out string val, out _))
                    //{
                    //    if (data == "_Req")
                    //    {
                    //        Req = val;
                    //        if (val == "0")
                    //        {
                    //            ReqStatus = "停止等待";
                    //        }
                    //        else if (val == "1")
                    //        {
                    //            ReqStatus = "加热启动";
                    //        }
                    //        else if (val == "2")
                    //        {
                    //            ReqStatus = "冷却启动";
                    //        }
                    //    }
                    //    else if (data == "_SetTemp")
                    //    {
                    //        SetTemp = val;
                    //    }
                    //    else if (data == "_SaveTemp")
                    //    {
                    //        SaveTemp = val;
                    //    }
                    //    else if (data == "_TempDown")
                    //    {
                    //        TempDown = val;
                    //    }
                    //    else if (data == "_TempUp")
                    //    {
                    //        TempUp = val;
                    //    }
                    //    else if (data == "_AvgTemp")
                    //    {
                    //        AvgTemp = val;
                    //    }
                    //}
                }
                //if (api.Connec   (out string errMsg))
                //{
                //}
                //else
                //{
                //    OPCContext.Connect($"Oven{ovenIndex}");
                //    Thread.Sleep(1000);
                //}
            }
            catch (Exception ex)
            {
            }
        }
        private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            var btn = sender as Button;
            string name = $"Oven{ovenIndex + 1}_Cmd";
            S7Context.WriteData(api, name, short.Parse(btn.Tag.ToString()));
            //api.DoFctWriteSingle(name, btn.Tag.ToString(), out string errMsg);
        }
    }
}
Plugin.CurrentSensorControl/BurnInOvenCollection.xaml
New file
@@ -0,0 +1,19 @@
<UserControl x:Class="Plugin.CurrentSensorControl.BurnInOvenCollection"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:Plugin.CurrentSensorControl"
             mc:Ignorable="d"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
xmlns:mdi="clr-namespace:HMI.UI.MDI;assembly=HMI.UI"
xmlns:pw="https://opensource.panuon.com/wpf"
xmlns:pu="https://opensource.panuon.com/wpf-ui"
xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid x:Name="mainGrid" dp:GridHelper.Space="6">
    </Grid>
</UserControl>
Plugin.CurrentSensorControl/BurnInOvenCollection.xaml.cs
New file
@@ -0,0 +1,91 @@
using Plugin.CurrentSensorControl.Entity;
using System.Drawing;
using System.Windows.Controls;
namespace Plugin.CurrentSensorControl
{
    /// <summary>
    /// BurnInOvenCollection.xaml 的交互逻辑
    /// </summary>
    public partial class BurnInOvenCollection : UserControl
    {
        private int rowCount;
        private int colCount;
        private int cellCount;
        private BurnInOven[] ovens;
        private BaseConfig config;
        public BurnInOvenCollection()
        {
            config = BaseConfig.Instance;
            InitializeComponent();
            InitMainGrid();
        }
        public void InitMainGrid()
        {
            this.rowCount = config.Row;
            this.colCount = config.Column;
            ovens = new BurnInOven[this.rowCount * this.colCount];
            if (config.CellCount < 0)
            {
                this.cellCount = rowCount * colCount;
            }
            else
            {
                this.cellCount = config.CellCount;
            }
            for (int r = 0; r < rowCount; r++)
            {
                RowDefinition row = new RowDefinition();
                mainGrid.RowDefinitions.Add(row);
            }
            for (int c = 0; c < colCount; c++)
            {
                ColumnDefinition col = new ColumnDefinition();
                mainGrid.ColumnDefinitions.Add(col);
            }
            string[] names = config.NameStr.Split(',');
            for (int n = 0; n < cellCount; n++)
            {
                string name = n < names.Length ? names[n] : "";
                BurnInOven child = new BurnInOven(n);
                child.Header = name;
                mainGrid.Children.Add(child);
                ovens[n] = child;
                int row = n / colCount;
                int col = n % colCount;
                Grid.SetRow(child, row);
                Grid.SetColumn(child, col);
            }
        }
        public void Refresh()
        {
            try
            {
                for (int i = 0; i < ovens.Length; i++)
                {
                    var oven = ovens[i];
                    oven.Refresh();
                }
                //Task[] allTask = new Task[ovens.Length];
                //for (int i = 0; i < ovens.Length; i++)
                //{
                //    var oven = ovens[i];
                //    var task = Task.Factory.StartNew(() =>
                //    {
                //        oven.Refresh();
                //    });
                //    allTask[i] = task;
                //}
                //Task.WaitAll(allTask);
            }
            catch (Exception ex)
            {
            }
        }
    }
}
Plugin.CurrentSensorControl/BurnInSettingModal.xaml
New file
@@ -0,0 +1,159 @@
<pu:WindowX x:Class="Plugin.CurrentSensorControl.BurnInSettingModal"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Plugin.CurrentSensorControl"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
            xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
            xmlns:sys="clr-namespace:System;assembly=mscorlib"
            mc:Ignorable="d"
            Title="Form"
            Width="1200"
            Height="900"
            WindowStartupLocation="CenterOwner"
            Style="{StaticResource ModalWindow}">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <pu:StyleDictionary Includes="TabControl" />
                <ResourceDictionary Source="/HMI.UI;component/Style/PanuonListView.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Grid Margin="5" dp:GridHelper.Space="5" dp:GridHelper.SpaceIgnore="ALL">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" dp:StackPanelHelper.ChildSpace="10">
            <Button Style="{StaticResource ExportBtn}" x:Name="exportBtn" Click="Button_Click"/>
            <Button Style="{StaticResource ImportBtn}" x:Name="importBtn" Click="Button_Click"/>
        </StackPanel>
        <ListView Grid.Row="1" Style="{StaticResource DefaultListTable}" x:Name="table"
                      ItemsSource="{Binding Datasource}"
                      VirtualizingPanel.IsContainerVirtualizable="True"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Standard">
            <ListView.Resources>
                <Style x:Key="RowLink" TargetType="Button" BasedOn="{StaticResource LinkButton}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Path=IsMouseOver,
                RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListViewItem,AncestorLevel=1}}"
         Value="True">
                            <Setter Property="Foreground" Value="#FFF"/>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding Path=IsSelected,
       RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListViewItem,AncestorLevel=1}}"
Value="True">
                            <Setter Property="Foreground" Value="#FFF"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
                <Style x:Key="DeleteLink" TargetType="Button" BasedOn="{StaticResource DangerLinkButton}">
                    <Setter Property="Content" Value="{DynamicResource App.Delete}"/>
                </Style>
                <Style x:Key="EditLink" TargetType="Button" BasedOn="{StaticResource RowLink}">
                    <Setter Property="Content" Value="{DynamicResource App.Edit}"/>
                </Style>
            </ListView.Resources>
            <ListView.View>
                <GridView AllowsColumnReorder="False">
                    <GridViewColumn Width="60" Header="#">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Converter={StaticResource lvRowNumber},
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListViewItem,AncestorLevel=1}}"/>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Width="200">
                        <GridViewColumn.Header>
                            <TextBlock Text="Name"/>
                        </GridViewColumn.Header>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Border Style="{StaticResource CellBorder}">
                                    <TextBlock Style="{StaticResource ListTableTextBlock}"
                                                           Text="{Binding Name}"/>
                                </Border>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Width="100">
                        <GridViewColumn.Header>
                            <TextBlock Text="Address"/>
                        </GridViewColumn.Header>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Border Style="{StaticResource CellBorder}">
                                    <TextBlock Style="{StaticResource ListTableTextBlock}"
   Text="{Binding Address}"/>
                                </Border>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Width="120">
                        <GridViewColumn.Header>
                            <TextBlock Text="Type"/>
                        </GridViewColumn.Header>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Border Style="{StaticResource CellBorder}">
                                    <TextBlock Style="{StaticResource ListTableTextBlock}"
                                                   Text="{Binding Type}"/>
                                </Border>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Width="120">
                        <GridViewColumn.Header>
                            <TextBlock Text="Length"/>
                        </GridViewColumn.Header>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Border Style="{StaticResource CellBorder}">
                                    <TextBlock Style="{StaticResource ListTableTextBlock}"
Text="{Binding Length}"/>
                                </Border>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Width="120">
                        <GridViewColumn.Header>
                            <TextBlock Text="Desc"/>
                        </GridViewColumn.Header>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Border Style="{StaticResource CellBorder}">
                                    <TextBlock Style="{StaticResource ListTableTextBlock}"
                   Text="{Binding Description}"/>
                                </Border>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Width="120">
                        <GridViewColumn.Header>
                            <TextBlock Text="{DynamicResource App.Opt}"/>
                        </GridViewColumn.Header>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <StackPanel Orientation="Horizontal" dp:StackPanelHelper.ChildSpace="10">
                                    <Button x:Name="editBtn" Style="{StaticResource EditLink}"
                Click="Button_Click" Tag="{Binding }"/>
                                    <Button x:Name="delBtn" Style="{StaticResource DeleteLink}"
                Click="Button_Click" Tag="{Binding }"/>
                                </StackPanel>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</pu:WindowX>
Plugin.CurrentSensorControl/BurnInSettingModal.xaml.cs
New file
@@ -0,0 +1,143 @@
using HMI.UI.DpUtil;
using HMI.UI.Util;
using HMI.Util;
using Microsoft.Win32;
using Panuon.WPF.UI;
using Plugin.CurrentSensorControl.Entity;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Controls;
namespace Plugin.CurrentSensorControl
{
    /// <summary>
    /// ModalWindow.xaml 的交互逻辑
    /// </summary>
    public partial class BurnInSettingModal : WindowX
    {
        public BurnInSettingModal()
        {
            this.DataContext = this;
            InitializeComponent();
        }
        private ObservableCollection<PLCTagData> datasource;
        public ObservableCollection<PLCTagData> Datasource
        {
            get => datasource;
            set
            {
                Set(ref datasource, value);
            }
        }
        private bool _isProcessing;
        protected override void OnClosing(CancelEventArgs e)
        {
            //Press Continue Button
            if (DialogResult == true)
            {
                if (_isProcessing)
                {
                    e.Cancel = true;
                    return;
                }
                var okButton = WindowXModalDialog.GetOKButton(this);
                try
                {
                    _isProcessing = true;
                    ButtonHelper.SetIsPending(okButton, true);
                }
                catch (Exception ex)
                {
                    ToastHelper.Error(this, $"Error!{ex.Message}");
                    e.Cancel = true;
                    return;
                }
                finally
                {
                    _isProcessing = false;
                    ButtonHelper.SetIsPending(okButton, false);
                }
            }
            base.OnClosing(e);
        }
        private bool ValidateName(object value, out string err)
        {
            err = null;
            //string group = TbGroup.Text;
            //var temp = Datasource.FirstOrDefault(d => d.Group == group && d.Name == value.ToString());
            //if (temp != null && temp != Data)
            //{
            //    err = this.lang("App.MustBeUnique", $"{this.lang("doc.group")},{this.lang("doc.name")}");
            //    return false;
            //}
            return true;
        }
        private bool ValidateAddress(object value, out string err)
        {
            err = null;
            //string group = TbGroup.Text;
            //var temp = Datasource.FirstOrDefault(d => d.Address == value.ToString());
            //if (temp != null && temp != Data)
            //{
            //    err = this.lang("App.MustBeUnique", this.lang("doc.Address"));
            //    return false;
            //}
            return true;
        }
        public bool? Open( ObservableCollection<PLCTagData> datasource)
        {
            this.Datasource = datasource;
            return this.ShowDialog();
        }
        private void DoExport()
        {
            SaveFileDialog sfd = new SaveFileDialog();
            if (sfd.ShowDialog() == true)
            {
                ExcelHelper.ExportExcel(sfd.FileName, Datasource);
            }
        }
        private void DoImport()
        {
            OpenFileDialog ofd = new OpenFileDialog();
            if (ofd.ShowDialog() == true)
            {
                try
                {
                    Datasource = new ObservableCollection<PLCTagData>(ExcelHelper.ReadFromExcel<PLCTagData>(ofd.FileName));
                }
                catch (Exception ex)
                {
                    ControlPage.Logger.Error("Import Error.", ex);
                    ToastHelper.Error(ex.Message);
                }
            }
        }
        private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            var btn = sender as Button;
            switch (btn.Name) {
                case "exportBtn":
                    DoExport();
                    break;
                case "importBtn":
                    DoImport();
                    break;
                default:
                    break;
            }
        }
    }
}
Plugin.CurrentSensorControl/ControlPage.xaml
New file
@@ -0,0 +1,284 @@
<hb:SimpleMaskControl x:Class="Plugin.CurrentSensorControl.ControlPage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:Plugin.CurrentSensorControl"
             mc:Ignorable="d"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
xmlns:mdi="clr-namespace:HMI.UI.MDI;assembly=HMI.UI"
xmlns:pw="https://opensource.panuon.com/wpf"
xmlns:pu="https://opensource.panuon.com/wpf-ui"
xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
                      Loaded="SimpleMaskControl_Loaded"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <pu:StyleDictionary Includes="TabControl" />
                <ResourceDictionary Source="/HMI.UI;component/Style/PanuonListView.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>
    <Border Style="{StaticResource CardBoarder}" Padding="0" Background="#FFF">
        <TabControl
pu:TabControlHelper.HeaderPanelAlignment="Stretch"
pu:TabControlHelper.ItemsBackground="{DynamicResource VerBar}"
pu:TabControlHelper.ItemsForeground="#FFFFFF"
pu:TabControlHelper.ItemsFontSize="14"
pu:TabControlHelper.ItemsHeight="30"
pu:TabControlHelper.ItemsHorizontalHeaderAlignment="Left"
pu:TabControlHelper.ItemsHoverBackground="{DynamicResource SecondPrimaryColor}"
pu:TabControlHelper.ItemsHoverForeground="#FFFFFF"
pu:TabControlHelper.ItemsPadding="20,0,20,0"
pu:TabControlHelper.ItemsRibbonLineCornerRadius="4"
pu:TabControlHelper.ItemsRibbonLineMargin="0,5,0,5"
pu:TabControlHelper.ItemsRibbonLineThickness="4"
pu:TabControlHelper.ItemsRibbonLineVisibility="Visible"
pu:TabControlHelper.ItemsSelectedBackground="#FFF"
pu:TabControlHelper.ItemsSelectedRibbonLineBrush="#FFFFFF"
pu:TabControlHelper.ItemsSelectedRibbonLineThickness="2">
            <TabControl.Template>
                <ControlTemplate TargetType="{x:Type TabControl}">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="*"/>
                        </Grid.RowDefinitions>
                        <Border BorderThickness="1,1,1,0" BorderBrush="#acacac" Background="{DynamicResource VerBar}" Padding="2,2,10,0">
                            <TabPanel x:Name="HeaderPanel" Grid.Row="0" Panel.ZIndex="1" IsItemsHost="True"/>
                        </Border>
                        <Border Grid.Row="1" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="1,0,1,1" Background="{TemplateBinding Background}">
                            <ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent"/>
                        </Border>
                    </Grid>
                </ControlTemplate>
            </TabControl.Template>
            <TabItem Header="老化调试">
                <Grid dp:GridHelper.Space="5" dp:GridHelper.SpaceIgnore="ALL">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <StackPanel Orientation="Horizontal">
                        <Button Style="{StaticResource SettingBtn}" x:Name="burnInSettingBtn" Click="Button_Click"/>
                    </StackPanel>
                    <local:BurnInOvenCollection Grid.Row="1" Grid.Column="0" x:Name="ovenCollection"/>
                </Grid>
            </TabItem>
            <TabItem Header="电流监控">
                <Grid dp:GridHelper.Space="8">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <DockPanel>
                        <StackPanel dp:StackPanelHelper.ChildSpace="10" DockPanel.Dock="Right" Orientation="Horizontal">
                            <Button Style="{StaticResource AddBtn}" x:Name="addBtn" Click="Button_Click"/>
                            <Button Style="{StaticResource RfhBtn}" x:Name="rfhBtn" Click="Button_Click"/>
                            <Button Style="{StaticResource ExportBtn}" x:Name="exportBtn" Click="Button_Click"/>
                            <Button Style="{StaticResource ImportBtn}" x:Name="importBtn" Click="Button_Click"/>
                            <!--<Button Style="{StaticResource DelBtn}"/>-->
                        </StackPanel>
                        <StackPanel Orientation="Horizontal" VerticalAlignment="Center" dp:StackPanelHelper.ChildSpace="10">
                            <pu:FormGroup FontSize="18" Header="电流传感器" Orientation="Horizontal">
                            </pu:FormGroup>
                            <Button Style="{StaticResource ResetBtn}"
            x:Name="connectAllBtn" Click="Button_Click" Content="全部连接" />
                            <Button Style="{StaticResource SettingBtn}" Content="全部断开" x:Name="disConnectAllBtn" Click="Button_Click"/>
                            <!--<Button Style="{StaticResource SettingBtn}" Content="设置" x:Name="settingBtn" Click="Button_Click"/>-->
                        </StackPanel>
                    </DockPanel>
                    <TabControl Grid.Row="1"
pu:TabControlHelper.HeaderPanelAlignment="Stretch"
pu:TabControlHelper.ItemsBackground="{DynamicResource VerBar}"
pu:TabControlHelper.ItemsForeground="#FFFFFF"
pu:TabControlHelper.ItemsFontSize="14"
pu:TabControlHelper.ItemsHeight="30"
pu:TabControlHelper.ItemsHorizontalHeaderAlignment="Left"
pu:TabControlHelper.ItemsHoverBackground="{DynamicResource SecondPrimaryColor}"
pu:TabControlHelper.ItemsHoverForeground="#FFFFFF"
pu:TabControlHelper.ItemsPadding="20,0,0,0"
pu:TabControlHelper.ItemsRibbonLineCornerRadius="4"
pu:TabControlHelper.ItemsRibbonLineMargin="0,5,0,5"
pu:TabControlHelper.ItemsRibbonLineThickness="4"
pu:TabControlHelper.ItemsRibbonLineVisibility="Visible"
pu:TabControlHelper.ItemsSelectedBackground="#FFF"
pu:TabControlHelper.ItemsSelectedRibbonLineBrush="#FFFFFF"
pu:TabControlHelper.ItemsSelectedRibbonLineThickness="2"
pu:TabControlHelper.ItemsWidth="100" >
                        <TabControl.Template>
                            <ControlTemplate TargetType="{x:Type TabControl}">
                                <Grid>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="Auto"/>
                                        <RowDefinition Height="*"/>
                                    </Grid.RowDefinitions>
                                    <Border BorderThickness="1,1,1,0" BorderBrush="#acacac" Background="{DynamicResource VerBar}" Padding="2,2,10,0">
                                        <TabPanel x:Name="HeaderPanel" Grid.Row="0" Panel.ZIndex="1" IsItemsHost="True"/>
                                    </Border>
                                    <Border Grid.Row="1" BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="1,0,1,1" Background="{TemplateBinding Background}">
                                        <ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent"/>
                                    </Border>
                                </Grid>
                            </ControlTemplate>
                        </TabControl.Template>
                        <TabItem Header="控件">
                            <ScrollViewer Grid.Row="1" Grid.Column="0"
              Style="{StaticResource DefaultScrollViewer}"
              VerticalScrollBarVisibility="Auto">
                                <WrapPanel Background="#FFF" x:Name="diWrap"/>
                            </ScrollViewer>
                        </TabItem>
                        <TabItem Header="列表">
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition/>
                                </Grid.RowDefinitions>
                                <ListView Grid.Row="1" Style="{StaticResource DefaultListTable}" x:Name="table"
                      ItemsSource="{Binding Datasource}"
                      VirtualizingPanel.IsContainerVirtualizable="True"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Standard">
                                    <ListView.Resources>
                                        <Style x:Key="RowLink" TargetType="Button" BasedOn="{StaticResource LinkButton}">
                                            <Style.Triggers>
                                                <DataTrigger Binding="{Binding Path=IsMouseOver,
                RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListViewItem,AncestorLevel=1}}"
         Value="True">
                                                    <Setter Property="Foreground" Value="#FFF"/>
                                                </DataTrigger>
                                                <DataTrigger Binding="{Binding Path=IsSelected,
       RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListViewItem,AncestorLevel=1}}"
Value="True">
                                                    <Setter Property="Foreground" Value="#FFF"/>
                                                </DataTrigger>
                                            </Style.Triggers>
                                        </Style>
                                        <Style x:Key="DeleteLink" TargetType="Button" BasedOn="{StaticResource DangerLinkButton}">
                                            <Setter Property="Content" Value="{DynamicResource App.Delete}"/>
                                        </Style>
                                        <Style x:Key="EditLink" TargetType="Button" BasedOn="{StaticResource RowLink}">
                                            <Setter Property="Content" Value="{DynamicResource App.Edit}"/>
                                        </Style>
                                    </ListView.Resources>
                                    <ListView.View>
                                        <GridView AllowsColumnReorder="False">
                                            <GridViewColumn Width="60" Header="#">
                                                <GridViewColumn.CellTemplate>
                                                    <DataTemplate>
                                                        <TextBlock Text="{Binding Converter={StaticResource lvRowNumber},
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListViewItem,AncestorLevel=1}}"/>
                                                    </DataTemplate>
                                                </GridViewColumn.CellTemplate>
                                            </GridViewColumn>
                                            <GridViewColumn Width="700">
                                                <GridViewColumn.Header>
                                                    <TextBlock Text="{DynamicResource doc.name}"/>
                                                </GridViewColumn.Header>
                                                <GridViewColumn.CellTemplate>
                                                    <DataTemplate>
                                                        <Border Style="{StaticResource CellBorder}">
                                                            <TextBlock Style="{StaticResource ListTableTextBlock}"
                                                           Text="{Binding Name}"/>
                                                        </Border>
                                                    </DataTemplate>
                                                </GridViewColumn.CellTemplate>
                                            </GridViewColumn>
                                            <GridViewColumn Width="100">
                                                <GridViewColumn.Header>
                                                    <TextBlock Text="IP"/>
                                                </GridViewColumn.Header>
                                                <GridViewColumn.CellTemplate>
                                                    <DataTemplate>
                                                        <Border Style="{StaticResource CellBorder}">
                                                            <TextBlock Style="{StaticResource ListTableTextBlock}"
   Text="{Binding Ip}"/>
                                                        </Border>
                                                    </DataTemplate>
                                                </GridViewColumn.CellTemplate>
                                            </GridViewColumn>
                                            <GridViewColumn Width="120">
                                                <GridViewColumn.Header>
                                                    <TextBlock Text="端口"/>
                                                </GridViewColumn.Header>
                                                <GridViewColumn.CellTemplate>
                                                    <DataTemplate>
                                                        <Border Style="{StaticResource CellBorder}">
                                                            <TextBlock Style="{StaticResource ListTableTextBlock}"
                                                   Text="{Binding Port}"/>
                                                        </Border>
                                                    </DataTemplate>
                                                </GridViewColumn.CellTemplate>
                                            </GridViewColumn>
                                            <GridViewColumn Width="120">
                                                <GridViewColumn.Header>
                                                    <TextBlock Text="地址"/>
                                                </GridViewColumn.Header>
                                                <GridViewColumn.CellTemplate>
                                                    <DataTemplate>
                                                        <Border Style="{StaticResource CellBorder}">
                                                            <TextBlock Style="{StaticResource ListTableTextBlock}"
Text="{Binding Address}"/>
                                                        </Border>
                                                    </DataTemplate>
                                                </GridViewColumn.CellTemplate>
                                            </GridViewColumn>
                                            <GridViewColumn Width="120">
                                                <GridViewColumn.Header>
                                                    <TextBlock Text="通道"/>
                                                </GridViewColumn.Header>
                                                <GridViewColumn.CellTemplate>
                                                    <DataTemplate>
                                                        <Border Style="{StaticResource CellBorder}">
                                                            <TextBlock Style="{StaticResource ListTableTextBlock}"
                   Text="{Binding Channel}"/>
                                                        </Border>
                                                    </DataTemplate>
                                                </GridViewColumn.CellTemplate>
                                            </GridViewColumn>
                                            <GridViewColumn Width="120">
                                                <GridViewColumn.Header>
                                                    <TextBlock Text="{DynamicResource App.Opt}"/>
                                                </GridViewColumn.Header>
                                                <GridViewColumn.CellTemplate>
                                                    <DataTemplate>
                                                        <StackPanel Orientation="Horizontal" dp:StackPanelHelper.ChildSpace="10">
                                                            <Button x:Name="editBtn" Style="{StaticResource EditLink}"
                Click="Button_Click" Tag="{Binding }"/>
                                                            <Button x:Name="delBtn" Style="{StaticResource DeleteLink}"
                Click="Button_Click" Tag="{Binding }"/>
                                                        </StackPanel>
                                                    </DataTemplate>
                                                </GridViewColumn.CellTemplate>
                                            </GridViewColumn>
                                        </GridView>
                                    </ListView.View>
                                </ListView>
                            </Grid>
                        </TabItem>
                    </TabControl>
                </Grid>
            </TabItem>
        </TabControl>
    </Border>
</hb:SimpleMaskControl>
Plugin.CurrentSensorControl/ControlPage.xaml.cs
New file
@@ -0,0 +1,410 @@
using CommunityToolkit.Mvvm.ComponentModel;
using ConnectionUtil;
using HMI.Base;
using HMI.Core.Setting;
using HMI.Core.Util;
using HMI.UI.Util;
using HMI.Util;
using log4net;
using Microsoft.Win32;
using Plugin.CurrentSensorControl.Entity;
using System.Collections.ObjectModel;
using System.IO;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
namespace Plugin.CurrentSensorControl
{
    /// <summary>
    /// ControlPage.xaml 的交互逻辑
    /// </summary>
    [ObservableObject]
    public partial class ControlPage : SimpleMaskControl
    {
        [ObservableProperty]
        private ObservableCollection<SensorData> datasource = new ObservableCollection<SensorData>();
        private List<SingleSensor> controls = new List<SingleSensor>();
        private System.Timers.Timer timer;
        private System.Timers.Timer opcTimer;
        private SCADAApi api;
        public static ILog Logger { get; private set; }
        private volatile bool needRead = false;
        public ControlPage()
        {
            timer = new System.Timers.Timer();
            timer.Elapsed += Timer_Elapsed;
            timer.AutoReset = false;
            timer.Interval = 1000;
            opcTimer = new System.Timers.Timer();
            opcTimer.Elapsed += OpcTimer_Elapsed; ;
            opcTimer.AutoReset = false;
            opcTimer.Interval = 1000;
            S7Context.Init(SystemSetting.Instance.PlcIp, SystemSetting.Instance.PlcPort);
            DataContext = this;
            InitializeComponent();
        }
        private void OpcTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            opcTimer.Enabled = false;
            try
            {
                var time = DateTime.Now;
                ovenCollection.Refresh();
                log.Debug($"Time Cast: {(DateTime.Now - time).TotalMilliseconds}ms");
            }
            catch (Exception ex)
            {
            }
            finally
            {
                if (needRead) opcTimer.Enabled = true;
            }
        }
        private void Timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            timer.Enabled = false;
            try
            {
                foreach (var ctl in controls)
                {
                    if (!needRead) return;
                    if (ctl is SingleSensor ss)
                    {
                        ss.ReadCurrent();
                        Thread.Sleep(50);
                    }
                }
            }
            catch (Exception ex)
            {
            }
            finally
            {
                if (needRead) timer.Enabled = true;
            }
        }
        public override void InitFinish()
        {
            Logger = log;
        }
        public override void Active()
        {
            StartReadCurrent();
        }
        public override void DeActive()
        {
            StopReadCurrent();
        }
        public override void DoClose(object sender, EventArgs e)
        {
            StopReadCurrent();
            base.DoClose(sender, e);
        }
        public void StartReadCurrent()
        {
            needRead = true;
            timer.Enabled = true;
            opcTimer.Enabled = true;
        }
        public void StopReadCurrent()
        {
            needRead = false;
            timer.Enabled = false;
            opcTimer.Enabled = false;
        }
        private void LoadSensorData()
        {
            string path = Path.Combine(BaseFolder, "sensor.data");
            var list = new ObservableCollection<SensorData>();
            try
            {
                if (File.Exists(path))
                {
                    list = XmlHelper.DeserializeFromXml<ObservableCollection<SensorData>>(path);
                }
            }
            catch (Exception ex)
            {
                log.Error("Load Sensor Data Failed.", ex);
            }
            Datasource = list;
        }
        private void ClearSensorControl()
        {
            foreach (var ctl in diWrap.Children)
            {
                if (ctl is SingleSensor ss)
                {
                    ss.Close();
                }
            }
            diWrap.Children.Clear();
            controls.Clear();
        }
        private void RefreshSensorControl(bool start)
        {
            StopReadCurrent();
            ClearSensorControl();
            foreach (var data in Datasource)
            {
                SingleSensor ss = new SingleSensor(data.Name, data.Ip, data.Port, data.Address, data.Channel)
                {
                    Width = 240
                };
                diWrap.Children.Add(ss);
                controls.Add(ss);
                Task.Factory.StartNew(() =>
                {
                    ss.Connect();
                });
            }
            if (start) StartReadCurrent();
        }
        private void ConnectAllSensor()
        {
            foreach (var ctl in diWrap.Children)
            {
                if (ctl is SingleSensor ss)
                {
                    ss.Connect();
                }
            }
        }
        private void DisconnectAllSensor()
        {
            foreach (var ctl in diWrap.Children)
            {
                if (ctl is SingleSensor ss)
                {
                    ss.Close();
                }
            }
        }
        private void SimpleMaskControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
        {
            LoadSensorData();
            RefreshSensorControl(needRead);
            LoadBurnInSetting();
            Loaded -= SimpleMaskControl_Loaded;
        }
        private void OpenDataModal(SensorData data)
        {
            DataModal modal = new DataModal()
            {
                Title = "新增",
                Owner = Window.GetWindow(this)
            };
            if (modal.Open(data, Datasource) == true)
            {
                string path = Path.Combine(BaseFolder, "sensor.data");
                try
                {
                    XmlHelper.SerializeToXml(path, Datasource);
                    LoadSensorData();
                    RefreshSensorControl(needRead);
                }
                catch (Exception ex)
                {
                    ToastHelper.Error("保存失败");
                    log.Error("Save Sensor Data Failed.", ex);
                }
            }
        }
        private void DoExport()
        {
            try
            {
                SaveFileDialog sfd = new SaveFileDialog();
                sfd.Filter = "Excel 文件 (*.xls)|*.xls";
                if (sfd.ShowDialog() == true)
                {
                    ExcelHelper.ExportExcel(sfd.FileName, Datasource);
                }
                ToastHelper.Success(this.lang("App.ExportSuccess"));
            }
            catch (Exception ex)
            {
                log.Error("Export Failed.", ex);
                ToastHelper.Error(this.lang("App.ExportFailed"));
            }
        }
        private void DoImport()
        {
            try
            {
                OpenFileDialog ofd = new OpenFileDialog();
                ofd.Filter = "Excel 文件 (*.xls)|*.xls";
                if (ofd.ShowDialog() == true)
                {
                    var datas = ExcelHelper.ReadFromExcel<SensorData>(ofd.FileName);
                    string path = Path.Combine(BaseFolder, "sensor.data");
                    XmlHelper.SerializeToXml(path, datas);
                    DoRefresh();
                    ToastHelper.Success(this.lang("App.ExportSuccess"));
                }
            }
            catch (Exception ex)
            {
                log.Error("Export Failed.", ex);
                ToastHelper.Error(this.lang("App.ExportFailed"));
            }
        }
        private void SaveDatasouce(object datasource)
        {
            string path = Path.Combine(BaseFolder, "sensor.data");
            XmlHelper.SerializeToXml(path, datasource);
        }
        private void DoRefresh()
        {
            ShowMask(() =>
            {
                Dispatcher.BeginInvoke(new Action(() =>
                {
                    LoadSensorData();
                    RefreshSensorControl(needRead);
                }));
            }, (ex) =>
            {
                log.Error("Refresh Sensor Data Failed.", ex);
            }, "请稍后", "正在加载...");
        }
        private void DoDelete(SensorData data)
        {
            if (MsgBoxHelper2.WarnChoose("删除?") == MessageBoxResult.OK)
            {
                var index = Datasource.IndexOf(data);
                if (index >= 0)
                {
                    Datasource.RemoveAt(index);
                    SaveDatasouce(Datasource);
                    DoRefresh();
                }
            }
        }
        private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            var btn = sender as Button;
            var tag = btn?.Tag;
            switch (btn?.Name)
            {
                case "addBtn":
                    OpenDataModal(new SensorData());
                    break;
                case "rfhBtn":
                    DoRefresh();
                    break;
                case "editBtn":
                    OpenDataModal((SensorData)tag);
                    break;
                case "delBtn":
                    DoDelete((SensorData)tag);
                    break;
                case "connectAllBtn":
                    ConnectAllSensor();
                    break;
                case "disConnectAllBtn":
                    DisconnectAllSensor();
                    break;
                case "exportBtn":
                    DoExport();
                    break;
                case "importBtn":
                    DoImport();
                    break;
                case "burnInSettingBtn":
                    OpenBurnSettingModal();
                    break;
                default:
                    break;
            }
        }
        [ObservableProperty]
        private ObservableCollection<PLCTagData> burnInSettingData;
        private void LoadBurnInSetting()
        {
            S7Context.dataCache.Clear();
            ObservableCollection<PLCTagData> datas;
            try
            {
                string path = Path.Combine(BaseFolder, "BurnInSettingData.data");
                if (File.Exists(path))
                {
                    datas = XmlHelper.DeserializeFromXml<ObservableCollection<PLCTagData>>(path);
                }
                else
                {
                    datas = new ObservableCollection<PLCTagData>();
                }
            }
            catch (Exception ex)
            {
                datas = new ObservableCollection<PLCTagData>();
            }
            BurnInSettingData = datas;
            LoadBurnInSettingCache();
        }
        private void LoadBurnInSettingCache()
        {
            foreach (var data in BurnInSettingData)
            {
                S7Context.dataCache[data.Name] = data;
            }
        }
        private void OpenBurnSettingModal()
        {
            BurnInSettingModal modal = new BurnInSettingModal
            {
                Owner = Window.GetWindow(this)
            };
            if (modal.Open(BurnInSettingData) == true)
            {
                var datas = modal.Datasource;
                try
                {
                    string path = Path.Combine(BaseFolder, "BurnInSettingData.data");
                    XmlHelper.SerializeToXml(path, datas);
                    BurnInSettingData = datas;
                    LoadBurnInSettingCache();
                }
                catch (Exception ex)
                {
                }
            }
        }
    }
}
Plugin.CurrentSensorControl/DataModal.xaml
New file
@@ -0,0 +1,59 @@
<pu:WindowX x:Class="Plugin.CurrentSensorControl.DataModal"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Plugin.CurrentSensorControl"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
            xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
            xmlns:sys="clr-namespace:System;assembly=mscorlib"
            mc:Ignorable="d"
            Title="Form"
            SizeToContent="Height"
            Width="600"
            WindowStartupLocation="CenterOwner"
            Style="{StaticResource ModalWindow}">
    <Window.Resources>
    </Window.Resources>
    <Grid Margin="10">
        <StackPanel x:Name="formPanel">
            <pu:FormGroup x:Name="FgName" Header="名称"
  Style="{StaticResource InputFormGroupStyle}"
  dp:FormGroupHelper.IsRequired="True"
  GroupName="MyFormGroup">
                <TextBox x:Name="TbName" Style="{StaticResource EditorTextBoxStyle}" />
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgIP" Header="IP"
              Style="{StaticResource InputFormGroupStyle}"
              dp:FormGroupHelper.IsRequired="True"
              GroupName="MyFormGroup">
                <TextBox x:Name="TbIP" Style="{StaticResource EditorTextBoxStyle}" />
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgPort" Header="Port"
                          Style="{StaticResource InputFormGroupStyle}"
  dp:FormGroupHelper.IsRequired="True"
  GroupName="MyFormGroup">
                <pu:NumberInput x:Name="NiPort" Style="{StaticResource FormNumberInput}" />
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgAddress" Header="Address"
                        Style="{StaticResource InputFormGroupStyle}"
dp:FormGroupHelper.IsRequired="True"
GroupName="MyFormGroup">
                <pu:NumberInput x:Name="NiAddress" Style="{StaticResource FormNumberInput}" />
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgChannel" Header="Channel"
                        Style="{StaticResource InputFormGroupStyle}"
dp:FormGroupHelper.IsRequired="True"
GroupName="MyFormGroup">
                <pu:NumberInput x:Name="NiChannel" Style="{StaticResource FormNumberInput}" />
            </pu:FormGroup>
        </StackPanel>
    </Grid>
</pu:WindowX>
Plugin.CurrentSensorControl/DataModal.xaml.cs
New file
@@ -0,0 +1,128 @@
using HMI.UI.DpUtil;
using HMI.UI.Util;
using Panuon.WPF.UI;
using Plugin.CurrentSensorControl.Entity;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace Plugin.CurrentSensorControl
{
    /// <summary>
    /// ModalWindow.xaml 的交互逻辑
    /// </summary>
    public partial class DataModal : WindowX
    {
        public DataModal()
        {
            this.DataContext = this;
            InitializeComponent();
        }
        public SensorData data { get; private set; }
        public ObservableCollection<SensorData> datasource { get; private set; }
        private bool _isProcessing;
        protected override void OnClosing(CancelEventArgs e)
        {
            //Press Continue Button
            if (DialogResult == true)
            {
                if (_isProcessing)
                {
                    e.Cancel = true;
                    return;
                }
                var okButton = WindowXModalDialog.GetOKButton(this);
                try
                {
                    _isProcessing = true;
                    ButtonHelper.SetIsPending(okButton, true);
                    if (!ValidateForm())
                    {
                        e.Cancel = true;
                        return;
                    }
                    SaveOrUpdateData();
                }
                catch (Exception ex)
                {
                    ToastHelper.Error(this, $"Error!{ex.Message}");
                    e.Cancel = true;
                    return;
                }
                finally
                {
                    _isProcessing = false;
                    ButtonHelper.SetIsPending(okButton, false);
                }
            }
            base.OnClosing(e);
        }
        private bool ValidateName(object value, out string err)
        {
            err = null;
            //string group = TbGroup.Text;
            //var temp = Datasource.FirstOrDefault(d => d.Group == group && d.Name == value.ToString());
            //if (temp != null && temp != Data)
            //{
            //    err = this.lang("App.MustBeUnique", $"{this.lang("doc.group")},{this.lang("doc.name")}");
            //    return false;
            //}
            return true;
        }
        private bool ValidateAddress(object value, out string err)
        {
            err = null;
            //string group = TbGroup.Text;
            //var temp = Datasource.FirstOrDefault(d => d.Address == value.ToString());
            //if (temp != null && temp != Data)
            //{
            //    err = this.lang("App.MustBeUnique", this.lang("doc.Address"));
            //    return false;
            //}
            return true;
        }
        private bool ValidateForm()
        {
            bool flag = FormGroupHelper.ValidateForm(formPanel);
            if (flag)
            {
                data.Name = TbName.Text;
                data.Ip = TbIP.Text;
                data.Port = ((int?)NiPort.Value) ?? 0;
                data.Address = ((int?)NiAddress.Value) ?? 0;
                data.Channel = ((int?)NiChannel.Value) ?? 0;
            }
            return flag;
        }
        private void SaveOrUpdateData()
        {
            var index = datasource.IndexOf(data);
            if (index > -1)
            {
                datasource[index] = data;
            }
            else
            {
                datasource.Add(data);
            }
        }
        public bool? Open(SensorData data, ObservableCollection<SensorData> datasource)
        {
            this.data = data;
            this.datasource = datasource;
            TbName.Text = data.Name;
            TbIP.Text = data.Ip;
            NiPort.Value = data.Port;
            NiAddress.Value = data.Address;
            NiChannel.Value = data.Channel;
            return this.ShowDialog();
        }
    }
}
Plugin.CurrentSensorControl/Entity/BaseConfig.cs
New file
@@ -0,0 +1,187 @@
using System.Collections;
using System.Configuration;
using System.Runtime.CompilerServices;
namespace Plugin.CurrentSensorControl.Entity
{
    public class BaseConfig
    {
        private static Hashtable customConfig = (Hashtable)ConfigurationManager.GetSection("TestResultConfig");
        public static readonly BaseConfig Instance = new BaseConfig();
        private BaseConfig() {
        }
        /// <summary>
        /// 首页布局行数
        /// </summary>
        private int row = 1;
        public int Row
        {
            get
            {
                var data = GetValue();
                return data == null ? row : int.Parse(data);
            }
        }
        private string GetValue([CallerMemberName] string propertyName = null)
        {
            return (customConfig != null && customConfig.ContainsKey(propertyName)) ? customConfig[propertyName].ToString() : null;
        }
        /// <summary>
        /// 首页布局列数
        /// </summary>
        private int column = 2;
        public int Column
        {
            get
            {
                var data = GetValue();
                return data == null ? column : int.Parse(data);
            }
        }
        /// <summary>
        /// 炉数 -1 就是row*column
        /// </summary>
        private int cellCount = -1;
        public int CellCount
        {
            get
            {
                var data = GetValue();
                return data == null ? cellCount : int.Parse(data);
            }
        }
        /// <summary>
        /// 点位列数
        /// </summary>
        private int positionColumn = 6;
        public int PositionColumn
        {
            get
            {
                var data = GetValue();
                return data == null ? positionColumn : int.Parse(data);
            }
        }
        /// <summary>
        /// 每个炉子的点位数
        /// </summary>
        private int positionCount = 54;
        public int PositionCount
        {
            get
            {
                var data = GetValue();
                return data == null ? positionCount : int.Parse(data);
            }
        }
        /// <summary>
        /// 曲线最多保存多少数据
        /// </summary>
        private int maxSize = 10000;
        public int MaxSize
        {
            get
            {
                var data = GetValue();
                return data == null ? maxSize : int.Parse(data);
            }
        }
        /// <summary>
        /// 图表刷新频率 ms
        /// </summary>
        private int refreshInterval = 500;
        public int RefreshInterval
        {
            get
            {
                var data = GetValue();
                return data == null ? refreshInterval : int.Parse(data);
            }
        }
        /// <summary>
        /// 炉子名称
        /// </summary>
        private string nameStr = "A,B,C,D,E,F,G";
        public string NameStr
        {
            get
            {
                var data = GetValue();
                return data == null ? nameStr : data;
            }
        }
        /// <summary>
        /// 默认展示的图表标题
        /// </summary>
        private string mainChartTitle = "平均温度";
        public string MainChartTitle
        {
            get
            {
                var data = GetValue();
                return data == null ? mainChartTitle : data;
            }
        }
        /// <summary>
        /// 双击打开的详细图表标题
        /// </summary>
        private string detailChartTitle = "详细温度";
        public string DetailChartTitle
        {
            get
            {
                var data = GetValue();
                return data == null ? detailChartTitle : data;
            }
        }
        /// <summary>
        /// 穴位Step列表最大容量
        /// </summary>
        private int cellListMaxSize = 10000;
        public int CellListMaxSize
        {
            get
            {
                var data = GetValue();
                return data == null ? cellListMaxSize : int.Parse(data);
            }
        }
        private int portStart = 6660;
        public int PortStart
        {
            get
            {
                var data = GetValue();
                return data == null ? portStart : int.Parse(data);
            }
        }
        private string codeParserType;
        public string CodeParserType
        {
            get
            {
                var data = GetValue();
                return data == null ? codeParserType : data.ToString();
            }
        }
        public bool testing = false;
    }
}
Plugin.CurrentSensorControl/Entity/OPCContext.cs
New file
@@ -0,0 +1,40 @@
using ConnectionUtil;
using OPCConnection;
namespace Plugin.CurrentSensorControl.Entity
{
    public class OPCContext
    {
        private static Dictionary<string, SCADAApi> cache = new Dictionary<string, SCADAApi>();
        private static string opcIP = "127.0.0.1";
        private static int opcPort = 6543;
        private static int localPort = 18888;
        public static SCADAApi Get(string name)
        {
            if (!cache.ContainsKey(name))
            {
                var client = new OPCClient();
                if (!client.Connect2(opcIP, opcPort, localPort, out string errMsg))
                {
                }
                else
                {
                }
                cache[name] = client;
            }
            return cache[name];
        }
        public static bool Connect(string name)
        {
            if (cache.ContainsKey(name))
            {
                return cache[name].Connect2(opcIP, opcPort, localPort, out string errMsg) == true;
            }
            return false;
        }
    }
}
Plugin.CurrentSensorControl/Entity/PLCTagData.cs
New file
@@ -0,0 +1,25 @@
using HMI.Core;
namespace Plugin.CurrentSensorControl.Entity
{
    public enum PLCTagType {
        BOOL,
        SHORT,
        INT,
        FLOAT
    }
    public class PLCTagData
    {
        [ExcelColumn("名称")]
        public string Name { get; set; }
        [ExcelColumn("地址")]
        public string Address { get; set; }
        [ExcelColumn("类型")]
        public PLCTagType Type { get; set; }
        [ExcelColumn("长度")]
        public int Length { get; set; }
        [ExcelColumn("描述")]
        public string Description { get; set; }
    }
}
Plugin.CurrentSensorControl/Entity/S7Context.cs
New file
@@ -0,0 +1,114 @@
using HslCommunication;
using HslCommunication.Core.Pipe;
using HslCommunication.Profinet.Siemens;
namespace Plugin.CurrentSensorControl.Entity
{
    public class S7Context
    {
        private static Dictionary<string, SiemensS7Net> cache = new Dictionary<string, SiemensS7Net>();
        public static Dictionary<string, PLCTagData> dataCache = new Dictionary<string, PLCTagData>();
        private static string Ip;
        private static int Port;
        public static void Init(string ip, int port)
        {
            Ip = ip;
            Port = port;
        }
        public static SiemensS7Net Get(string name)
        {
            if (!cache.ContainsKey(name))
            {
                var client = new SiemensS7Net(SiemensPLCS.S1500);
                cache[name] = client;
                PipeTcpNet pipeTcpNet = new PipeTcpNet(Ip, Port);
                pipeTcpNet.ConnectTimeOut = 5000;
                pipeTcpNet.ReceiveTimeOut = 10000;
                client.CommunicationPipe = pipeTcpNet;
                client.ConnectServer();
            }
            return cache[name];
        }
        public static bool Connect(string name)
        {
            return false;
        }
        public static bool GetData(SiemensS7Net api, string name, out object val)
        {
            val = null;
            if (dataCache.ContainsKey(name))
            {
                var data = dataCache[name];
                string address = data.Address;
                if (data.Type == PLCTagType.BOOL)
                {
                    var res = api.ReadBool(address);
                    val = res.Content;
                    return res.IsSuccess;
                }
                else if (data.Type == PLCTagType.SHORT)
                {
                    var res = api.ReadInt16(address);
                    val = res.Content;
                    return res.IsSuccess;
                }
                else if (data.Type == PLCTagType.INT)
                {
                    var res = api.ReadInt32(address);
                    val = res.Content;
                    return res.IsSuccess;
                }
                else if (data.Type == PLCTagType.FLOAT)
                {
                    var res = api.ReadFloat(address);
                    val = res.Content;
                    return res.IsSuccess;
                }
                return false;
            }
            else
            {
                return false;
            }
        }
        public static bool WriteData(SiemensS7Net api, string name, object val)
        {
            if (dataCache.ContainsKey(name))
            {
                var data = dataCache[name];
                string address = data.Address;
                OperateResult res;
                switch (data.Type)
                {
                    case PLCTagType.BOOL:
                        res = api.Write(address, (bool)val);
                        break;
                    case PLCTagType.SHORT:
                        res = api.Write(address, (short)val);
                        break;
                    case PLCTagType.INT:
                        res = api.Write(address, (int)val);
                        break;
                    case PLCTagType.FLOAT:
                        res = api.Write(address, (float)val);
                        break;
                    default:
                        return true;
                }
                return res.IsSuccess;
            }
            else
            {
                return false;
            }
        }
    }
}
Plugin.CurrentSensorControl/Entity/SensorData.cs
New file
@@ -0,0 +1,20 @@
using HMI.Core;
namespace Plugin.CurrentSensorControl.Entity
{
    public class SensorData
    {
        [ExcelColumn("名称")]
        public string Name { get; set; }
        [ExcelColumn("IP")]
        public string Ip { get; set; }
        [ExcelColumn("端口")]
        public int Port { get; set; }
        [ExcelColumn("地址")]
        public int Address { get; set; }
        [ExcelColumn("通道")]
        public int Channel { get; set; }
    }
}
Plugin.CurrentSensorControl/Plugin.CurrentSensorControl.csproj
New file
@@ -0,0 +1,44 @@
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Library</OutputType>
    <TargetFramework>net472</TargetFramework>
    <Nullable>enable</Nullable>
    <LangVersion>10.0</LangVersion>
    <ImplicitUsings>enable</ImplicitUsings>
    <UseWPF>true</UseWPF>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
    <PackageReference Include="log4net" Version="3.0.4" />
    <PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
    <PackageReference Include="Panuon.WPF.UI" Version="1.3.0.2" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\HMI.Base\HMI.Base.csproj" />
    <ProjectReference Include="..\HMI.Core\HMI.Core.csproj" />
    <ProjectReference Include="..\HMI.UI\HMI.UI.csproj" />
    <ProjectReference Include="..\OPCClient\OPCClient.csproj" />
  </ItemGroup>
  <ItemGroup>
    <Reference Include="CurrentReadHelper">
      <HintPath>..\..\..\..\Users\chrry\Desktop\CurrentReadHelper\builds\CurrentReadHelper.dll</HintPath>
    </Reference>
    <Reference Include="HslCommunication">
      <HintPath>..\Libs\HslCommunication.dll</HintPath>
    </Reference>
    <Reference Include="SimensDriver">
      <HintPath>..\Libs\SimensDriver.dll</HintPath>
    </Reference>
  </ItemGroup>
  <ItemGroup>
    <Compile Update="BurnInCell.xaml.cs">
      <SubType>Code</SubType>
    </Compile>
  </ItemGroup>
</Project>
Plugin.CurrentSensorControl/SingleSensor.xaml
New file
@@ -0,0 +1,74 @@
<UserControl x:Class="Plugin.CurrentSensorControl.SingleSensor"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:Plugin.CurrentSensorControl"
             mc:Ignorable="d"
            xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
            xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
            xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
            xmlns:mdi="clr-namespace:HMI.UI.MDI;assembly=HMI.UI"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
            xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
             Background="#FFF"
             Padding="5"
             Width="Auto"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <pu:StyleDictionary Includes="TabControl" />
                <ResourceDictionary Source="/HMI.UI;component/Style/PanuonListView.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <Style TargetType="TextBox" x:Key="labelText">
                <Setter Property="Height" Value="35"/>
                <Setter Property="FontSize" Value="20"/>
                <Setter Property="VerticalContentAlignment" Value="Center"/>
                <Setter Property="BorderThickness" Value="0"/>
                <Setter Property="Background" Value="Transparent"/>
                <Setter Property="Width" Value="120"/>
            </Style>
        </ResourceDictionary>
    </UserControl.Resources>
    <GroupBox Header="{x:Null}" Padding="5">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="12"/>
            </Grid.RowDefinitions>
            <StackPanel Grid.Row="0" Orientation="Horizontal" VerticalAlignment="Center" dp:StackPanelHelper.ChildSpace="5">
                <!--<TextBox IsReadOnly="True" Text="{Binding SensorName}" Style="{StaticResource labelText}">
                    <TextBox.ToolTip>
                        <ToolTip Padding="5" BorderThickness="1" BorderBrush="Black" Background="Yellow" Content="1231231"/>
                    </TextBox.ToolTip>
                </TextBox>-->
                <ui:ScrollLabel Width="80" Value="{Binding SensorName}"/>
                <ui:LightBall Size="30" Value="{Binding IsConnected}"/>
                <Button x:Name="controlBtn" Click="controlBtn_Click">
                    <Button.Style>
                        <Style TargetType="Button" BasedOn="{StaticResource ResetBtn}">
                            <Setter Property="Content" Value="连接"/>
                            <Setter Property="Background" Value="{DynamicResource GreenVerBar}"/>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding IsConnected}" Value="True">
                                    <Setter Property="Background" Value="{DynamicResource DangerVerBar}"/>
                                    <Setter Property="Content" Value="断开"/>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </Button.Style>
                </Button>
            </StackPanel>
            <pu:FormGroup Grid.Row="1" Header="电流(A)" Style="{StaticResource InputFormGroupStyle}">
                <TextBox IsReadOnly="True" Text="{Binding Current}" Style="{StaticResource EditorTextBoxStyle}"/>
            </pu:FormGroup>
            <Label Grid.Row="2" Foreground="Red" Padding="0" FontSize="10" Content="{Binding ErrMsg}"/>
        </Grid>
    </GroupBox>
</UserControl>
Plugin.CurrentSensorControl/SingleSensor.xaml.cs
New file
@@ -0,0 +1,180 @@
using CommunityToolkit.Mvvm.ComponentModel;
using HslCommunication;
using HslCommunication.ModBus;
using System.Windows;
using System.Windows.Controls;
namespace Plugin.CurrentSensorControl
{
    /// <summary>
    /// SingleSensor.xaml 的交互逻辑
    /// </summary>
    [ObservableObject]
    public partial class SingleSensor : UserControl
    {
        private static Dictionary<string, ModbusRtuOverTcp> clients = new Dictionary<string, ModbusRtuOverTcp>();
        private static Dictionary<ModbusRtuOverTcp, bool> status = new Dictionary<ModbusRtuOverTcp, bool>();
        [ObservableProperty]
        private string sensorName;
        [ObservableProperty]
        private bool isConnected;
        [ObservableProperty]
        private string current;
        [ObservableProperty]
        private string ip;
        [ObservableProperty]
        private int port;
        /// <summary>
        /// 地址(站号)
        /// </summary>
        [ObservableProperty]
        private int address;
        [ObservableProperty]
        private string errMsg;
        /// <summary>
        /// 通道
        /// </summary>
        [ObservableProperty]
        private int channel;
        private ModbusRtuOverTcp client;
        public SingleSensor()
        {
            DataContext = this;
            InitializeComponent();
        }
        public SingleSensor(string name, string ip, int port, int address, int channel) : this()
        {
            SensorName = name;
            Ip = ip;
            Port = port;
            Address = address;
            Channel = channel;
            string key = $"{Ip}:{Port}:{Address}";
            if (!clients.ContainsKey(key))
            {
                client = new ModbusRtuOverTcp(Ip, Port, (byte)Address);
                clients[key] = client;
            }
            else
            {
                client = clients[key];
            }
        }
        private void controlBtn_Click(object sender, RoutedEventArgs e)
        {
            if (IsConnected)
            {
                this.Close();
            }
            else
            {
                Task.Factory.StartNew(() => { this.Connect(); });
            }
        }
        public void Connect()
        {
            lock (client)
            {
                if (!status.ContainsKey(client) || !status[client])
                {
                    var res = client.ConnectServer();
                    IsConnected = res.IsSuccess;
                    status[client] = IsConnected;
                }
                else
                {
                    IsConnected = status[client];
                }
            }
        }
        public void ReadCurrent()
        {
            //if (status.ContainsKey(client) && status[client])
            //{
            //    OperateResult<double[]> res;
            //    lock (client)
            //    {
            //        res = client.ReadDouble(channel.ToString(), 1);
            //    }
            //    Dispatcher.Invoke(() =>
            //    {
            //        if (res.IsSuccess)
            //        {
            //            Current = res.Content[0].ToString();
            //            ErrMsg = string.Empty;
            //        }
            //        else
            //        {
            //            ErrMsg = res.Message;
            //            //Close();
            //        }
            //    });
            //}
            //else
            //{
            //    IsConnected = false;
            //}
            var sb = status[client];
            if (IsConnected)
            {
                OperateResult<double[]> res;
                lock (client)
                {
                    res = client.ReadDouble(channel.ToString(), 1);
                }
                Dispatcher.Invoke(() =>
                {
                    if (res.IsSuccess)
                    {
                        Current = res.Content[0].ToString();
                        ErrMsg = string.Empty;
                    }
                    else
                    {
                        ErrMsg = res.Message;
                        //Close();
                    }
                });
            }
        }
        public void Close()
        {
            lock (client)
            {
                var connected = status[client];
                if (connected)
                {
                    var res = client.ConnectClose();
                    if (res.IsSuccess)
                    {
                        client.Dispose();
                    }
                    IsConnected = !res.IsSuccess;
                    status[client] = IsConnected;
                }
                else
                {
                    IsConnected = connected;
                }
            }
        }
    }
}
Plugin.DOControl/Plugin.DOControl.csproj
@@ -118,6 +118,7 @@
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
    <None Include="app.config" />
    <None Include="packages.config" />
    <None Include="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
@@ -125,8 +126,8 @@
    </None>
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\..\ConnectionUtil\ConnectionUtil\ConnectionUtil.csproj">
      <Project>{D0806FA6-3F6D-4304-81B8-EAD9DA7CB5B2}</Project>
    <ProjectReference Include="..\ConnectionUtil\ConnectionUtil.csproj">
      <Project>{d0806fa6-3f6d-4304-81b8-ead9da7cb5b2}</Project>
      <Name>ConnectionUtil</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.Base\HMI.Base.csproj">
Plugin.DOControl/app.config
New file
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
Plugin.DODriverControl/Api/IDIOController.cs
@@ -5,6 +5,7 @@
        bool Start(out string msg);
        bool ReadDIBool(string address, out bool res, out string msg);
        bool ReadDIDouble(string address, out double? res, out string msg);
        bool ReadDOBool(string address, out bool res, out string msg);
Plugin.DODriverControl/Api/ModBusTcpController.cs
@@ -51,5 +51,13 @@
        {
            client?.Dispose();
        }
        public bool ReadDIDouble(string address, out double? res, out string msg)
        {
            var result = client?.ReadDouble($"x=2;{address}");
            msg = result?.Message;
            res = result?.Content;
            return result?.IsSuccess == true;
        }
    }
}
Plugin.DODriverControl/Api/PCIController.cs
@@ -45,5 +45,10 @@
        {
            this.Close();
        }
        public bool ReadDIDouble(string address, out double? res, out string msg)
        {
            throw new NotImplementedException();
        }
    }
}
Plugin.DODriverControl/Plugin.DODriverControl.csproj
@@ -18,11 +18,16 @@
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\..\hslCommunicationV12.0.3带源码demo\HslCommunication\HslCommunication.csproj" />
    <ProjectReference Include="..\HMI.Base\HMI.Base.csproj" />
    <ProjectReference Include="..\HMI.Core\HMI.Core.csproj" />
    <ProjectReference Include="..\HMI.UI\HMI.UI.csproj" />
    <ProjectReference Include="..\HMI.Util\HMI.Util.csproj" />
  </ItemGroup>
  <ItemGroup>
    <Reference Include="HslCommunication">
      <HintPath>..\Libs\HslCommunication.dll</HintPath>
    </Reference>
  </ItemGroup>
</Project>
Plugin.DODriverControl/SingleControl.xaml.cs
@@ -311,11 +311,21 @@
        {
            if (data.Dio == DIOType.DI)
            {
                if (data.ControlType == ControlType.BOOL)
                {
                return new LightBall
                {
                    Size = 40,
                    DataContext = data
                };
                }
                else
                {
                    return new TextBox
                    {
                        IsReadOnly = true
                    };
                }
            }
            Style btnStyle = this.FindResource("PrimaryButton") as Style;
            var btn = new Button
@@ -513,9 +523,10 @@
                            case ControlType.LREAL:
                            case ControlType.STRING:
                            case ControlType.WSTRING:
                                ((Button)ui).Content = value;
                                ((TextBox)ui).Text = value;
                                break;
                            default:
                                ((TextBox)ui).Text = value;
                                break;
                        }
                    });
@@ -539,6 +550,7 @@
                value = null;
                return false;
            }
            string msg;
            switch (data.ControlType)
            {
                case ControlType.BOOL:
@@ -546,11 +558,11 @@
                    bool res = false;
                    if (data.Dio == DIOType.DI)
                    {
                        res = controller.ReadDIBool(data.Address, out val, out string msg);
                        res = controller.ReadDIBool(data.Address, out val, out msg);
                    }
                    else
                    {
                        res = controller.ReadDOBool(data.Address, out val, out string msg);
                        res = controller.ReadDOBool(data.Address, out val, out msg);
                    }
                    if (res == true)
                    {
@@ -560,6 +572,10 @@
                    value = null;
                    return false;
                default:
                    if (controller.ReadDIDouble(data.Address, out double? dVal, out msg))
                    {
                        value = dVal.ToString();
                    }
                    value = null;
                    return true;
            }
Plugin.DataBoard/DataBoard.xaml
@@ -14,8 +14,11 @@
                  Loaded="PluginControl_Loaded"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <Style TargetType="ComboBox" BasedOn="{StaticResource FormComboxStyle}">
        <Style x:Key="cbDate" TargetType="ComboBox" BasedOn="{StaticResource FormComboxStyle}">
            <EventSetter Event="SelectionChanged" Handler="ComboBox_SelectionChanged"/>
            <Setter Property="pu:ComboBoxHelper.ClearButtonVisibility" Value="Collapsed"/>
            <Setter Property="SelectedValuePath" Value="Value"/>
            <Setter Property="DisplayMemberPath" Value="Name"/>
        </Style>
        <Style TargetType="pu:DateTimePicker" BasedOn="{StaticResource TimePicker}">
            <Setter Property="MinWidth" Value="0"/>
@@ -28,6 +31,7 @@
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
@@ -37,17 +41,32 @@
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <ComboBox x:Name="cb0" ItemsSource="{Binding Options}" SelectedValuePath="Value" DisplayMemberPath="Name"/>
            <pu:FormGroup Header="型号">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <ComboBox x:Name="modelSelect" Style="{StaticResource FormComboxStyle}"
                          pu:ComboBoxHelper.ClearButtonVisibility="Visible" SelectionChanged="modelSelect_SelectionChanged"/>
                    <Button x:Name="btnAll" Grid.Column="1" Click="Button_Click"
                            Style="{StaticResource RfhBtn}" Content="全部刷新"/>
                </Grid>
            </pu:FormGroup>
            <ComboBox x:Name="cb1" Grid.Column="1" ItemsSource="{Binding Options}"
                      SelectedValuePath="Value" DisplayMemberPath="Name"/>
            <ComboBox x:Name="cb2" Grid.Column="2" ItemsSource="{Binding Options}"
                      SelectedValuePath="Value" DisplayMemberPath="Name"/>
            <ComboBox Grid.Row="1" Grid.Column="0" x:Name="cb0" ItemsSource="{Binding Options}"
                      Style="{StaticResource cbDate}"/>
            <ComboBox x:Name="cb3" Grid.Column="3" ItemsSource="{Binding Options}"
                      SelectedValuePath="Value" DisplayMemberPath="Name"/>
            <ComboBox Grid.Row="1" Grid.Column="1" x:Name="cb1" ItemsSource="{Binding Options}"
                      Style="{StaticResource cbDate}"/>
            <Grid Grid.Row="1" Grid.Column="0">
            <ComboBox Grid.Row="1" Grid.Column="2" x:Name="cb2" ItemsSource="{Binding Options}"
                      Style="{StaticResource cbDate}"/>
            <ComboBox Grid.Row="1" Grid.Column="3" x:Name="cb3" ItemsSource="{Binding Options}"
                      Style="{StaticResource cbDate}"/>
            <Grid Grid.Row="2" Grid.Column="0">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition Width="Auto"/>
@@ -63,7 +82,7 @@
                        Content="{DynamicResource bd.btn}"/>
            </Grid>
            <Grid Grid.Row="1" Grid.Column="1">
            <Grid Grid.Row="2" Grid.Column="1">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition Width="Auto"/>
@@ -79,7 +98,7 @@
        Content="{DynamicResource bd.btn}"/>
            </Grid>
            <Grid Grid.Row="1" Grid.Column="2">
            <Grid Grid.Row="2" Grid.Column="2">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition Width="Auto"/>
@@ -95,7 +114,7 @@
        Content="{DynamicResource bd.btn}"/>
            </Grid>
            <Grid Grid.Row="1" Grid.Column="3">
            <Grid Grid.Row="2" Grid.Column="3">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition Width="Auto"/>
@@ -111,7 +130,7 @@
        Content="{DynamicResource bd.btn}"/>
            </Grid>
            <wv2:WebView2 Grid.Row="2" Grid.ColumnSpan="4" Name = "webview2"
            <wv2:WebView2 Grid.Row="3" Grid.ColumnSpan="4" Name = "webview2"
                      SizeChanged="webview2_SizeChanged"
                      NavigationCompleted="Webview2_NavigationCompleted"
                      Loaded="webview2_Loaded"/>
Plugin.DataBoard/DataBoard.xaml.cs
@@ -1,4 +1,6 @@
using HMI.Base;
using HMI.Core.Entity;
using HMI.Core.Manager;
using HMI.Core.Util;
using HMI.UI.Util;
using HMI.Util;
@@ -41,9 +43,78 @@
            set { Set(ref _options, value); }
        }
        private string iniFile;
        public override void InitFinish()
        {
            iniFile = Path.Combine(BaseFolder, "Config.ini");
            if (!File.Exists(iniFile))
            {
                File.Create(iniFile);
            }
            LoadProduct();
        }
        private void PluginControl_Loaded(object sender, RoutedEventArgs e)
        {
        }
        private void LoadProduct()
        {
            List<ProductData> products = ProductManager.GetProducts<List<ProductData>>()
               ?? new List<ProductData>();
            this.modelSelect.Items.Clear();
            List<string> productSource = new List<string>();
            foreach (var prd in products)
            {
                this.modelSelect.Items.Add(prd.Name);
                productSource.Add(prd.Name);
            }
            string model = IniFileHelper.ReadIniData("Config", "Model", "", iniFile);
            if (!string.IsNullOrEmpty(model))
            {
                this.modelSelect.SelectedItem = model;
            }
            else if (products.Count > 0)
            {
                this.modelSelect.SelectedIndex = 0;
            }
        }
        private void RefreshAll()
        {
            try
            {
                var start0 = dtp00.SelectedDateTime ?? DateTime.Now;
                var end0 = dtp01.SelectedDateTime ?? DateTime.Now;
                RefreshChart0(start0, end0);
            }
            catch { }
            try
            {
                var start1 = dtp10.SelectedDateTime ?? DateTime.Now;
                var end1 = dtp11.SelectedDateTime ?? DateTime.Now;
                RefreshChart1(start1, end1);
            }
            catch { }
            try
            {
                var start2 = dtp20.SelectedDateTime ?? DateTime.Now;
                var end2 = dtp21.SelectedDateTime ?? DateTime.Now;
                RefreshChart2(start2, end2);
            }
            catch { }
            try
            {
                var start3 = dtp30.SelectedDateTime ?? DateTime.Now;
                var end3 = dtp31.SelectedDateTime ?? DateTime.Now;
                RefreshChart3(start3, end3);
            }
            catch { }
        }
        private void Button_Click(object sender, RoutedEventArgs e)
@@ -52,6 +123,9 @@
            DateTime start, end;
            switch (btn.Name)
            {
                case "btnAll":
                    RefreshAll();
                    break;
                case "btn0":
                    start = dtp00.SelectedDateTime ?? DateTime.Now;
                    end = dtp01.SelectedDateTime ?? DateTime.Now;
@@ -99,7 +173,7 @@
        }
        private List<SerialData> SelectSerialDataByTime(DateTime start, DateTime end)
        private List<SerialData> SelectSerialDataByTime(string model, DateTime start, DateTime end)
        {
            var s1 = DateTime.Now;
            string[] files = GetDbFiles(BaseFolder, start, end);
@@ -107,7 +181,14 @@
            {
                return new List<SerialData>();
            }
            string sql = $"SELECT * FROM serialData WHERE time BETWEEN '{start:yyyy-MM-dd HH:mm:ss}' AND '{end:yyyy-MM-dd HH:mm:ss}' ORDER BY time DESC;";
            string segment = string.Empty;
            if (!string.IsNullOrEmpty(model))
            {
                segment = $"AND model = '{model}'";
            }
            string sql = $@"SELECT * FROM serialData WHERE time BETWEEN '{start:yyyy-MM-dd HH:mm:ss}' AND '{end:yyyy-MM-dd HH:mm:ss}'
{segment} ORDER BY time DESC;";
            DataTable dt = new DataTable();
            foreach (string file in files)
@@ -124,14 +205,21 @@
            return data;
        }
        private List<SerialData> SelectResultDataByTime(DateTime start, DateTime end)
        private List<SerialData> SelectResultDataByTime(string model, DateTime start, DateTime end)
        {
            string[] files = GetDbFiles(BaseFolder, start, end);
            if (files == null || files.Length <= 0)
            {
                return new List<SerialData>();
            }
            string sql = $"SELECT * FROM testResult WHERE time BETWEEN '{start:yyyy-MM-dd HH:mm:ss}' AND '{end:yyyy-MM-dd HH:mm:ss}';";
            string segment = string.Empty;
            if (!string.IsNullOrEmpty(model))
            {
                segment = $"AND model = '{model}'";
            }
            string sql = $@"SELECT * FROM testResult WHERE time BETWEEN '{start:yyyy-MM-dd HH:mm:ss}' AND '{end:yyyy-MM-dd HH:mm:ss}' {segment};";
            DataTable dt = new DataTable();
            foreach (string file in files)
@@ -206,6 +294,7 @@
        {
            dtp00.SelectedDateTime = start;
            dtp01.SelectedDateTime = end;
            string model = modelSelect.SelectedItem?.ToString();
            Task.Run(() =>
            {
                try
@@ -214,7 +303,7 @@
                    {
                        webview2.CoreWebView2?.ExecuteScriptAsync($"loading0('加载中,请稍后...')");
                    }));
                    List<SerialData> datas = SelectResultDataByTime(start, end);
                    List<SerialData> datas = SelectResultDataByTime(model, start, end);
                    datas.OrderByDescending(e => e.time);
                    int all = 0, output = 0;
                    HashSet<string> snCache = new HashSet<string>();
@@ -243,6 +332,7 @@
        {
            dtp10.SelectedDateTime = start;
            dtp11.SelectedDateTime = end;
            string model = modelSelect.SelectedItem?.ToString();
            Task.Run(() =>
            {
                try
@@ -251,7 +341,7 @@
                    {
                        webview2.CoreWebView2?.ExecuteScriptAsync($"loading1('加载中,请稍后...')");
                    }));
                    List<SerialData> datas = SelectResultDataByTime(start, end);
                    List<SerialData> datas = SelectResultDataByTime(model, start, end);
                    datas = datas.OrderByDescending(e => e.time).ToList();
                    Dictionary<string, int?> pieData = new Dictionary<string, int?> {
@@ -367,6 +457,7 @@
        {
            dtp20.SelectedDateTime = start;
            dtp21.SelectedDateTime = end;
            string model = modelSelect.SelectedItem?.ToString();
            Task.Run(() =>
            {
                try
@@ -375,7 +466,7 @@
                    {
                        webview2.CoreWebView2?.ExecuteScriptAsync($"loading2('加载中,请稍后...')");
                    }));
                    List<SerialData> datas = SelectResultDataByTime(start, end);
                    List<SerialData> datas = SelectResultDataByTime(model, start, end);
                    var groups =
                        datas.GroupBy(d => d.time.ToString("yyyy-MM-dd"));
@@ -446,6 +537,7 @@
        {
            dtp30.SelectedDateTime = start;
            dtp31.SelectedDateTime = end;
            string model = modelSelect.SelectedItem?.ToString();
            Task.Run(() =>
            {
                try
@@ -455,7 +547,7 @@
                        webview2.CoreWebView2?.ExecuteScriptAsync($"loading3('加载中,请稍后...')");
                    }));
                    var st = DateTime.Now;
                    List<SerialData> datas = SelectSerialDataByTime(start, end);
                    List<SerialData> datas = SelectSerialDataByTime(model, start, end);
                    var s2 = DateTime.Now;
                    log.Info($"RefreshChart3 Data Cast {(s2 - st).TotalMilliseconds}");
@@ -544,5 +636,13 @@
                    break;
            }
        }
        private void modelSelect_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count > 0)
            {
                IniFileHelper.WriteIniData("Config", "Model", e.AddedItems[0].ToString(), iniFile);
            }
        }
    }
}
Plugin.DataBoard/Properties/AssemblyInfo.cs
@@ -1,7 +1,5 @@
using HMI.Core.Annotation;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
Plugin.JarUI/JarUI.xaml.cs
@@ -446,6 +446,7 @@
            try
            {
                SequenceContext?.StationGlobals.SetValBoolean("StationState.StopTest", 1, true);
                log.Info($"Stop Result: {SequenceContext?.StationGlobals.GetValBoolean("StationState.StopTest", 1)}");
                if (this.execution == null) return true;
                return this.execution.WaitForEndEx(2000, true, null, null);
            }
@@ -591,7 +592,7 @@
                try
                {
                    //log.Info($"Event: {EventCase}, NumericData: {MethodCode}, StringData: {strData}");
                    log.Info($"Event: {EventCase}, NumericData: {MethodCode}, StringData: {strData}");
                }
                catch
                {
@@ -615,6 +616,9 @@
                        int index2 = int.Parse(arr2[0]);
                        this.tsSockets[index2].SetInfo(arr2[1]);
                        break;
                    case 8:
                        IsRunning = false;
                        break;
                    default:
                        break;
                }
Plugin.MaterialOpt/AssemblyInfo.cs
File was deleted
Plugin.MaterialOpt/MaterialOpt.xaml
File was deleted
Plugin.MaterialOpt/MaterialOpt.xaml.cs
File was deleted
Plugin.MaterialOpt/Plugin.MaterialOpt.csproj
File was deleted
Plugin.SNGenerateTool/Entity/DataInfo.cs
File was deleted
Plugin.SNGenerateTool/Entity/UserSetting.cs
File was deleted
Plugin.SNGenerateTool/Form1.Designer.cs
File was deleted
Plugin.SNGenerateTool/Form1.cs
File was deleted
Plugin.SNGenerateTool/HashPlus.cs
File was deleted
Plugin.SNGenerateTool/Plugin.SNGenerateTool.csproj
File was deleted
Plugin.SNGenerateTool/Properties/AssemblyInfo.cs
File was deleted
Plugin.SNGenerateTool/Properties/Resources.Designer.cs
File was deleted
Plugin.SNGenerateTool/Properties/Resources.resx
File was deleted
Plugin.SNGenerateTool/Properties/Settings.Designer.cs
File was deleted
Plugin.SNGenerateTool/Properties/Settings.settings
File was deleted
Plugin.SNGenerateTool/UserControl1.xaml
File was deleted
Plugin.SNGenerateTool/UserControl1.xaml.cs
File was deleted
Plugin.SNGenerateTool/app.config
File was deleted
Plugin.SNGenerateTool/packages.config
File was deleted
Plugin.ServoManage/Entity/ServoData.cs
File was deleted
Plugin.ServoManage/Entity/ServoProductData.cs
File was deleted
Plugin.ServoManage/Plugin.ServoManage.csproj
File was deleted
Plugin.ServoManage/Properties/AssemblyInfo.cs
File was deleted
Plugin.ServoManage/Properties/Resources.Designer.cs
File was deleted
Plugin.ServoManage/Properties/Resources.resx
File was deleted
Plugin.ServoManage/Properties/Settings.Designer.cs
File was deleted
Plugin.ServoManage/Properties/Settings.settings
File was deleted
Plugin.ServoManage/ServoManage.xaml
File was deleted
Plugin.ServoManage/ServoManage.xaml.cs
File was deleted
Plugin.ServoManage/ServoModal.xaml
File was deleted
Plugin.ServoManage/ServoModal.xaml.cs
File was deleted
Plugin.ServoManage/ServoProductModal.xaml
File was deleted
Plugin.ServoManage/ServoProductModal.xaml.cs
File was deleted
Plugin.ServoManage/ServoSetting.xaml
File was deleted
Plugin.ServoManage/ServoSetting.xaml.cs
File was deleted
Plugin.ServoManage/packages.config
File was deleted
Plugin.TestBuilder/Container/FunctionSetting.xaml
File was deleted
Plugin.TestBuilder/Container/FunctionSetting.xaml.cs
File was deleted
Plugin.TestBuilder/Container/ProgramList.xaml
File was deleted
Plugin.TestBuilder/Container/ProgramList.xaml.cs
File was deleted
Plugin.TestBuilder/Container/SequnceList.xaml
File was deleted
Plugin.TestBuilder/Container/SequnceList.xaml.cs
File was deleted
Plugin.TestBuilder/Container/StepSetting.xaml
File was deleted
Plugin.TestBuilder/Container/StepSetting.xaml.cs
File was deleted
Plugin.TestBuilder/Entity/CanvasPosition.cs
File was deleted
Plugin.TestBuilder/Entity/SeqData.cs
File was deleted
Plugin.TestBuilder/Model/FuncModel.cs
File was deleted
Plugin.TestBuilder/Model/Program.cs
File was deleted
Plugin.TestBuilder/Model/Sequence.cs
File was deleted
Plugin.TestBuilder/Model/Step.cs
File was deleted
Plugin.TestBuilder/Model/Variable.cs
File was deleted
Plugin.TestBuilder/Plugin.TestBuilder.csproj
File was deleted
Plugin.TestBuilder/Properties/AssemblyInfo.cs
File was deleted
Plugin.TestBuilder/Properties/Resources.Designer.cs
File was deleted
Plugin.TestBuilder/Properties/Resources.resx
File was deleted
Plugin.TestBuilder/Properties/Settings.Designer.cs
File was deleted
Plugin.TestBuilder/Properties/Settings.settings
File was deleted
Plugin.TestBuilder/TestBuilder.xaml
File was deleted
Plugin.TestBuilder/TestBuilder.xaml.cs
File was deleted
Plugin.TestBuilder/UI/BottomBar.xaml
File was deleted
Plugin.TestBuilder/UI/BottomBar.xaml.cs
File was deleted
Plugin.TestBuilder/UI/DdDragHandler.cs
File was deleted
Plugin.TestBuilder/UI/DdDropHandler.cs
File was deleted
Plugin.TestBuilder/UI/InnerWindow.xaml
File was deleted
Plugin.TestBuilder/UI/InnerWindow.xaml.cs
File was deleted
Plugin.TestBuilder/UI/MinInnerWindow.xaml
File was deleted
Plugin.TestBuilder/UI/MinInnerWindow.xaml.cs
File was deleted
Plugin.TestBuilder/UI/Resize.cs
File was deleted
Plugin.TestBuilder/Workspace.xaml
File was deleted
Plugin.TestBuilder/Workspace.xaml.cs
File was deleted
Plugin.TestBuilder/packages.config
File was deleted
Plugin.TestResult/Constant/CommonConstant.cs
@@ -28,6 +28,7 @@
            { "testing","测试中"},
            { "alert","报警"},
            { "ready","准备就绪"},
            { "abort","手动停止"},
        };
        public static readonly Dictionary<string, SolidBrush> COLOR_MAP = new Dictionary<string, SolidBrush>() {
@@ -37,6 +38,7 @@
            { "testing",new SolidBrush(Color.FromArgb(0,0,0,0))},
            { "checkInFailed",new SolidBrush(Color.FromArgb(0,0,0,0))},
            { "checkIn",new SolidBrush(Color.FromArgb(0,0,0,0))},
            { "abort",new SolidBrush(Color.FromArgb(0,0,0,0))},
        };
        public static string GetRandomStatus()
Plugin.TestResult/DataTable.xaml
@@ -40,7 +40,7 @@
            </Grid.ColumnDefinitions>
            
            <TextBlock FontSize="{Binding ActualWidth,RelativeSource={RelativeSource AncestorType=UserControl}, 
Converter={StaticResource FontSizeConverter}, ConverterParameter='30,940'}"
Converter={StaticResource FontSizeConverter}, ConverterParameter='20,940'}"
                   FontWeight="Bold"
                   Margin="0"
                   HorizontalAlignment="Center"
@@ -51,6 +51,7 @@
                      Style="{StaticResource FormComboxStyle}"
                      pu:ComboBoxHelper.ClearButtonVisibility="Collapsed"
                      BorderBrush="Black"
                      Height="26"
                      BorderThickness="0,1,0,0"
                      pu:ComboBoxHelper.Watermark="{DynamicResource Tr.ProductInfo}"
                      Width="{Binding ActualWidth,RelativeSource={RelativeSource AncestorType=UserControl}, 
@@ -61,7 +62,7 @@
            <local:InfoTable x:Name="infoTable" Grid.Row="0" Grid.Column="1"
                             Grid.RowSpan="2"
               FontSize="{Binding ActualWidth,RelativeSource={RelativeSource AncestorType=UserControl}, 
            Converter={StaticResource FontSizeConverter}, ConverterParameter='15,940'}"></local:InfoTable>
            Converter={StaticResource FontSizeConverter}, ConverterParameter='12,940'}"></local:InfoTable>
        </Grid>
        <UniformGrid x:Name="mainGrid" Grid.Row="1" dp:UniformGridHelper.InnerBorder="1"
@@ -71,7 +72,7 @@
        <!--<local:Chart Grid.Row="2" x:Name="chart" BorderThickness="0,1,0,1" BorderBrush="Black" 
                      MouseDoubleClick="chart_MouseDoubleClick"/>-->
        <StatusBar Grid.Row="3">
        <StatusBar FontSize="18" Grid.Row="3">
            <StatusBar.ItemsPanel>
                <ItemsPanelTemplate>
                    <DockPanel/>
@@ -83,7 +84,7 @@
            <StatusBarItem DockPanel.Dock="Right">
                <StackPanel Orientation="Horizontal" dp:StackPanelHelper.ChildSpace="8">
                    <Button Style="{StaticResource DeleteLink}" Click="Button_Click_2" Content="停止测试"/>
                    <!--<Button Style="{StaticResource DeleteLink}" Click="Button_Click_2" Content="停止测试"/>-->
                    <StackPanel Orientation="Horizontal">
                        <Ellipse Width="10" Height="10">
@@ -122,8 +123,21 @@
        Content="{DynamicResource Tr.Scan}"/>
            <Button Grid.Column="1" Click="ClearAllSnr_Click" Style="{StaticResource DangerButton}"
Content="{DynamicResource Tr.ClearAllSNR}"/>
            <Button Grid.Column="2" Click="SetStationGlobal_Click" Style="{StaticResource SuccessButton}"
        Content="{DynamicResource App.Start}"/>
            <!--<Button Grid.Column="2" Click="SetStationGlobal_Click" Style="{StaticResource SuccessButton}"
        Content="{DynamicResource App.Start}"/>-->
            <Button Grid.Column="2" Click="StartSingleClick">
                <Button.Style>
                    <Style TargetType="Button" BasedOn="{StaticResource SuccessButton}">
                        <Setter Property="Content" Value="{DynamicResource App.Start}"/>
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding IsTesting}" Value="True">
                                <Setter Property="Background" Value="{DynamicResource DangerVerBar}"/>
                                <Setter Property="Content" Value="停止测试"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Button.Style>
            </Button>
        </Grid>
        
    </Grid>
Plugin.TestResult/DataTable.xaml.cs
@@ -2,14 +2,19 @@
using HMI.UI.Util;
using HMI.Util;
using Plugin.TestResult;
using Plugin.TestResult.Util;
using Plugin.TestResult_WPF.Model;
using Plugin.TestResult_WPF.Util;
using SuperSocket.SocketBase.Config;
using SuperSocket.SocketBase.Protocol;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Net.WebSockets;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
@@ -51,6 +56,14 @@
            set => RaiseAndSetIfchanged(ref _statusText, value);
        }
        private bool _isTesting;
        public bool IsTesting
        {
            get { return _isTesting; }
            set => RaiseAndSetIfchanged(ref _isTesting, value);
        }
        private string tcpServerStatus;
        public string TcpServerStatus
@@ -67,7 +80,7 @@
            set { RaiseAndSetIfchanged(ref tcpServerInfo, value); }
        }
        private static CommonCodeParser codeParser = new CommonCodeParser();
        private static ICodeParser codeParser;
        private readonly int index;
        private readonly int cellCount;
        private readonly string chartTitle;
@@ -80,6 +93,10 @@
        private TcpServer tcpServer;
        private string CountIniPath;
        private DateTime? testStartTime;
        private Timer testTimeTimer;
        // 平均温度
        public List<double> tempratures = new List<double>();
@@ -112,11 +129,38 @@
            for (int i = 0; i < this.cellCount; i++)
            {
                var cell = new SingleCell(title, i, config.CellListMaxSize, countIniPath);
                var cellRow = (i / config.PositionColumn) + 1;
                var cellCol = (i % config.PositionColumn) + 1;
                var cellTitle = $"{title}-{cellRow}-{cellCol}";
                var cell = new SingleCell(this, cellTitle, i, config.CellListMaxSize, countIniPath);
                mainGrid.Children.Add(cell);
                cells[i] = cell;
            }
            if (config.CodeParserType == "JK01")
            {
                codeParser = new JKCodeParser();
            }
            else
            {
                codeParser = new CommonCodeParser();
            }
            ReadCount();
            testTimeTimer = new Timer();
            testTimeTimer.Interval = 10000;
            testTimeTimer.AutoReset = true;
            testTimeTimer.Elapsed += TestTimeTimer_Elapsed;
        }
        private void TestTimeTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (testStartTime == null) return;
            var now = DateTime.Now;
            Dispatcher.BeginInvoke(new Action(() =>
            {
                SetConsumeTime((now - testStartTime).Value.TotalSeconds);
            }));
        }
        public int GetSize()
@@ -159,7 +203,42 @@
        public void SetStatus(string status)
        {
            ViewControl.Logger.Debug($"In Status: {status}");
            Dispatcher.BeginInvoke(new Action(() =>
            {
                var lastStatus = infoTable.status;
                if (status == "preCheck")
                {
                    ViewControl.Logger.Debug($"In preCheck");
                    IsTesting = true;
                    return;
                }
                if (status == "stop")
                {
                    IsTesting = false;
                    return;
                }
                if (status == "abort")
                {
                    IsTesting = false;
                }
            infoTable.status = status;
                if (lastStatus != "testing" && status == "testing")
                {
                    testStartTime = DateTime.Now;
                    SetStartTime(testStartTime?.ToString("yyyy-MM-dd HH:mm:ss"));
                    testTimeTimer.Start();
                }
                if (lastStatus == "testing" && status != "testing")
                {
                    testTimeTimer.Stop();
                    var now = DateTime.Now;
                    SetConsumeTime((now - testStartTime).Value.TotalSeconds);
                    SetEndTime(now.ToString("yyyy-MM-dd HH:mm:ss"));
                    testStartTime = null;
                }
            }));
        }
        public void SetOkCount(int count)
@@ -215,8 +294,23 @@
        private void SetStationGlobal_Click(object sender, RoutedEventArgs e)
        {
        }
        private void StartSingleClick(object sender, RoutedEventArgs e)
        {
            if (IsTesting)
            {
                if (MsgBoxHelper2.WarnChoose($"确实停止老化炉[{title}]的测试吗") == MessageBoxResult.OK)
                {
                    parent.SetStartFlag(this.index, false);
                }
            }
            else
            {
            ClearChartData();
            parent.SetStartFlag(this.index, true);
            }
        }
        private void ScanWindow_Click(object sender, RoutedEventArgs e)
@@ -229,18 +323,27 @@
            if (scanner != null)
            {
                scanner.Activate();
                scanner.ShowDialog();
            }
            else
            {
                scanner = new Scanner(this.lang("Tr.ScanTitle", title))
                {
                    Owner = Window.GetWindow(this),
                    Tag = title
                };
                scanner.FinishBarcode += Scanner_FinishBarcode;
                scanner.ComplateClick += Scanner_ComplateClick; ;
                scanner.Closed += Scanner_Closed;
                scanner.Show();
                scanner.ShowDialog();
            }
        }
        private void Scanner_ComplateClick(object sender, RoutedEventArgs e)
        {
            string paramName = $"StationState.Scanner[{index}]";
            parent.engine.Globals.SetValBoolean(paramName, 1, true);
            //parent.engine.CommitGlobalsToDisk(promptOnSaveConflicts: false);
        }
        private void Scanner_Closed(object sender, EventArgs e)
@@ -251,24 +354,50 @@
        private void Scanner_FinishBarcode(object sender, BarcodeEventArg e)
        {
            if (parent.SequenceContext == null) return;
            CellInfo cellInfo = codeParser.GetCellInfoByCode(e.positionCode);
            string paramName = $"StationState.StartScanner_{title}";
            parent.engine.Globals.SetValBoolean(paramName, 0, true);
            CellInfo cellInfo = codeParser.GetCellInfoByCode(e.positionCode);
            string positionCode = e.positionCode, snrCode = e.snrCode;
            //TODO 需要增加条码校验功能
            //将读到的条码写入到SN中
            int dutNo = Convert.ToInt16(positionCode.Split('-')[2]) - 1;
            dutNo = this.index * this.cellCount + dutNo;
            int dutNo = this.index * this.cellCount + cellInfo.Index;
            string LookupStr = $"StationState.SN[{dutNo}]";
            ViewControl.Logger.Info($"PositionCode: [{e.positionCode}], SNR: [{e.snrCode}], LookUpStr: [{LookupStr}]");
            parent.engine.Globals.SetValString(LookupStr, 0, snrCode);
            ViewControl.Logger.Debug($"PositionCode: [{e.positionCode}], SNR: [{e.snrCode}], LookUpStr: [{LookupStr}]");
            ViewControl.Logger.Debug($"OvenName: [{cellInfo.OvenName}], CellIndex: [{cellInfo.Index}]");
            var cell = parent.nameTables[cellInfo.OvenName].GetSingleCell(cellInfo.Index);
            if (snrCode == null)
            {
                string oldLookUp = $"StationState.SN[{e.oldIndex}]";
                var oldCellInfo = codeParser.GetCellInfoByCode(e.oldPosition);
                var oldCell = parent.nameTables[oldCellInfo.OvenName].GetSingleCell(oldCellInfo.Index);
                if (oldCell.status != "hadCode")
                {
                    throw new Exception($"穴位[{e.oldPosition}]当前不允许清除条码!");
                }
                // 移除条码
                parent.engine.Globals.SetValString(oldLookUp, 1, "");
            parent.engine.CommitGlobalsToDisk(promptOnSaveConflicts: false);
            string code = parent.engine.Globals.GetValString(LookupStr, 0);
                oldCell.SetStatus("waiting");
                oldCell.sn = "";
                return;
            }
            /// 清掉上个条码的记录
            var lastSn = cell.sn;
            if (!string.IsNullOrEmpty(lastSn))
            {
                Scanner.RemoveSNR(lastSn);
            }
            // 写入条码
            parent.engine.Globals.SetValString(LookupStr, 1, snrCode);
            parent.engine.CommitGlobalsToDisk(promptOnSaveConflicts: false);
            parent.nameTables[cellInfo.OvenName].GetSingleCell(cellInfo.Index).SetStatus("hadCode");
            parent.nameTables[cellInfo.OvenName].GetSingleCell(cellInfo.Index).sn = snrCode;
            Scanner.InsertNewCode(snrCode, positionCode, dutNo);
        }
@@ -289,13 +418,18 @@
        /// </summary>
        private void StartTcpServer()
        {
            var serverConfig = new ServerConfig()
            {
                MaxRequestLength = 1024 * 1024,
                Port = config.PortStart + index
            };
            try
            {
                if (tcpServer == null)
                {
                    tcpServer = new TcpServer();
                    tcpServer.NewRequestReceived += TcpServer_NewRequestReceived;
                    if (!tcpServer.Setup(config.PortStart + index))
                    if (!tcpServer.Setup(serverConfig))
                    {
                        ChangeTcpServerStatus("ERROR", this.lang("Tr.TcpServer.SettingFailed"));
                        return;
@@ -309,7 +443,7 @@
                }
                else if (tcpServer.State != SuperSocket.SocketBase.ServerState.Running)
                {
                    if (!tcpServer.Setup(config.PortStart + index))
                    if (!tcpServer.Setup(serverConfig))
                    {
                        ChangeTcpServerStatus("ERROR", this.lang("Tr.TcpServer.SettingFailed"));
                        return;
@@ -365,6 +499,7 @@
        /// </summary>
        public void RelaseResource()
        {
            testTimeTimer?.Stop();
            StopTcpServer();
        }
@@ -376,7 +511,7 @@
        private void TcpServer_NewRequestReceived(TcpSession session, StringRequestInfo requestInfo)
        {
            string msg = requestInfo.Body;
            //ViewControl.Logger.Info($"TCP Data From {session.RemoteEndPoint}: {msg}.");
            ViewControl.Logger.Debug($"TCP Data From {session.RemoteEndPoint}: {msg}.");
            string[] datas_10015 = msg.Split(';');
            int cellIndex = int.Parse(datas_10015[1]);
@@ -389,10 +524,11 @@
            {
                step = values[0],
                value = values[1],
                result = values[2],
                range = values[3],
                condition = values[4],
                updateTime = values[5]
                limitUp = values[2],
                limitDown = values[3],
                unit = values[4],
                result = values[5],
                updateTime = values[6]
            });
            //session.Send("1");
        }
@@ -518,6 +654,14 @@
            }
        }
        public void ClearVData()
        {
            foreach (var cell in cells)
            {
                cell.ClearVData();
            }
        }
        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            ScannerSetting setting = new ScannerSetting()
@@ -529,7 +673,7 @@
        private void ClearAllSnr_Click(object sender, RoutedEventArgs e)
        {
            var str = $"StationState.Start_{title}";
            var str = $"StationState.DoorClose[{index}]";
            if (parent.engine.Globals.GetValBoolean(str, 0) == true)
            {
                MsgBoxHelper.Error(Window.GetWindow(this), "程序运行中,不能清空条码");
@@ -540,13 +684,7 @@
            {
                try
                {
                    for (int i = 0; i < this.cellCount; i++)
                    {
                        int dutNo = this.index * this.cellCount + i;
                        string LookupStr = $"StationState.SN[{dutNo}]";
                        parent.engine.Globals.SetValString(LookupStr, 0, "");
                        GetSingleCell(i).SetStatus("waiting");
                    }
                    ClearAllSN();
                    ToastHelper.Success(LangUtil.Get("App.DeleteSuccess"));
                }
                catch (Exception ex)
@@ -557,6 +695,38 @@
            }
        }
        public void ClearAllSN()
        {
            for (int i = 0; i < this.cellCount; i++)
            {
                ClearCellSN(i);
            }
        }
        public void TryClearCellSN(int cellIndex)
        {
            var str = $"StationState.DoorClose[{this.index}]";
            if (parent.engine.Globals.GetValBoolean(str, 0) == true)
            {
                MsgBoxHelper.Error(Window.GetWindow(this), "程序运行中,不能清空条码");
                return;
            }
            ClearCellSN(cellIndex);
        }
        private void ClearCellSN(int index)
        {
            var cell = GetSingleCell(index);
            int dutNo = this.index * this.cellCount + index;
            string LookupStr = $"StationState.SN[{dutNo}]";
            var lastSN = cell.sn;
            cell.sn = "";
            cell.SetStatus("waiting");
            parent.engine.Globals.SetValString(LookupStr, 1, "");
            parent.engine.CommitGlobalsToDisk(promptOnSaveConflicts: false);
            if (!string.IsNullOrEmpty(lastSN)) Scanner.RemoveSNR(lastSN);
        }
        private void Button_Click_2(object sender, RoutedEventArgs e)
        {
            if (MsgBoxHelper.WarnMessage(Window.GetWindow(this), "确定要停止该炉位的老化测试吗?") == MessageBoxResult.OK)
Plugin.TestResult/InfoTable.xaml
@@ -71,6 +71,10 @@
                        <DataTrigger Binding="{Binding status}" Value="ready">
                            <Setter Property="Background" Value="#FF08FF00"/>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding status}" Value="abort">
                            <Setter Property="Background" Value="Black"/>
                            <Setter Property="Foreground" Value="White"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Label.Style>
Plugin.TestResult/InfoWindow.xaml
@@ -15,15 +15,17 @@
        InteropOwnersMask="False"
        Topmost="False"
        Loaded="Window_Loaded"
        SizeToContent="Height"
        Unloaded="Window_Unloaded"
        SizeChanged="Window_SizeChanged"
        pu:WindowXModalDialog.Buttons="{x:Null}"
        Title="{Binding title}" Height="500" Width="800">
        Title="{Binding title}" Width="800" Height="825">
    <Grid x:Name="mainGrid">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="500"/>
            <RowDefinition Height="150"/>
            <RowDefinition Height="150"/>
            <RowDefinition Height="25"/>
        </Grid.RowDefinitions>
        <DataGrid ItemsSource="{Binding dataSource}" AutoGenerateColumns="False"
              CanUserAddRows="False"
@@ -41,16 +43,17 @@
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>-->
                <DataGridTextColumn Header="测试项名称" Binding="{Binding step}"/>
                <DataGridTextColumn Header="测试项" Binding="{Binding step}"/>
                <DataGridTextColumn Header="测试值" Binding="{Binding value}"/>
                <DataGridTextColumn Header="上限" Binding="{Binding limitUp}"/>
                <DataGridTextColumn Header="下限" Binding="{Binding limitDown}"/>
                <DataGridTextColumn Header="单位" Binding="{Binding unit}"/>
                <DataGridTextColumn Header="测试结果" Binding="{Binding result}"/>
                <DataGridTextColumn Header="测试范围" Binding="{Binding range}"/>
                <DataGridTextColumn Header="测试条件" Binding="{Binding condition}"/>
                <DataGridTextColumn Header="更新时间" Binding="{Binding updateTime}"/>
            </DataGrid.Columns>
        </DataGrid>
        <Grid Background="#f1eded" Grid.Row="2" Height="25">
        <Grid Background="#f1eded" Grid.Row="3">
            <DockPanel>
                <StackPanel DockPanel.Dock="Right" Orientation="Horizontal" Margin="0,0,30,0">
                    <Label Padding="0" FontSize="12" 
Plugin.TestResult/InfoWindow.xaml.cs
@@ -35,7 +35,8 @@
            set => Set(ref _dataSource, value);
        }
        private Chart electricChart;
        public InfoWindow(string title, ObservableCollection<Info> infos, Chart electricChart, int probeCount)
        private Chart vChart;
        public InfoWindow(string title, ObservableCollection<Info> infos, Chart electricChart, Chart vChart, int probeCount)
        {
            this.title = title;
            this.dataSource = infos;
@@ -53,6 +54,10 @@
            this.electricChart = electricChart;
            mainGrid.Children.Add(electricChart);
            Grid.SetRow(electricChart, 1);
            this.vChart = vChart;
            mainGrid.Children.Add(vChart);
            Grid.SetRow(vChart, 2);
        }
        private static int AlarmProbeCount;
@@ -119,16 +124,17 @@
        protected override void OnClosed(EventArgs e)
        {
            electricChart = null;
            vChart = null;
            mainGrid.Children.Clear();
            base.OnClosed(e);
        }
        private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (electricChart != null)
            {
                electricChart.Height = this.ActualHeight * 0.3;
            }
            //if (electricChart != null)
            //{
            //    electricChart.Height = this.ActualHeight * 0.3;
            //}
        }
        private void Button_Click(object sender, RoutedEventArgs e)
Plugin.TestResult/Model/BaseConfig.cs
@@ -165,6 +165,16 @@
            }
        }
        private string codeParserType;
        public string CodeParserType
        {
            get
            {
                var data = GetValue();
                return data == null ? codeParserType : data.ToString();
            }
        }
        public bool testing = false;
    }
}
Plugin.TestResult/Model/Info.cs
@@ -20,6 +20,15 @@
            set => RaiseAndSetIfchanged(ref _value, value);
        }
        private string _unit;
        public string unit
        {
            get => _unit;
            set => RaiseAndSetIfchanged(ref _unit, value);
        }
        private string _result;
        public string result
@@ -29,22 +38,22 @@
            set => RaiseAndSetIfchanged(ref _result, value);
        }
        private string _range;
        private string _limitDown;
        public string range
        public string limitDown
        {
            get => _range;
            get => _limitDown;
            set => RaiseAndSetIfchanged(ref _range, value);
            set => RaiseAndSetIfchanged(ref _limitDown, value);
        }
        private string _condition;
        private string _limitUp;
        public string condition
        public string limitUp
        {
            get => _condition;
            get => _limitUp;
            set => RaiseAndSetIfchanged(ref _condition, value);
            set => RaiseAndSetIfchanged(ref _limitUp, value);
        }
        private string _updateTime;
Plugin.TestResult/Plugin.TestResult.csproj
@@ -187,6 +187,8 @@
    <Compile Include="ScannerSetting.xaml.cs">
      <DependentUpon>ScannerSetting.xaml</DependentUpon>
    </Compile>
    <Compile Include="Util\ICodeParser.cs" />
    <Compile Include="Util\JKCodeParser.cs" />
    <Compile Include="Util\CommonCodeParser.cs" />
    <Compile Include="Util\FontSizeConverter.cs" />
    <Compile Include="ViewControl.xaml.cs">
@@ -313,6 +315,10 @@
      <Project>{736cd77d-b81e-4866-bb00-84b0b57110f0}</Project>
      <Name>HMI.ParameterManage</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.StdControl\HMI.StdControl.csproj">
      <Project>{7a066856-182f-4435-b648-e9fb91aff657}</Project>
      <Name>HMI.StdControl</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.UI\HMI.UI.csproj">
      <Project>{f17de6b5-af1d-48c6-94a0-6b763a6546a6}</Project>
      <Name>HMI.UI</Name>
@@ -326,6 +332,9 @@
      <Name>Plugin.TsSupport</Name>
    </ProjectReference>
  </ItemGroup>
  <ItemGroup>
    <Resource Include="Resource\abort.png" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <Import Project="..\WPFProject\packages\HarfBuzzSharp.NativeAssets.macOS.7.3.0.3\build\net462\HarfBuzzSharp.NativeAssets.macOS.targets" Condition="Exists('..\WPFProject\packages\HarfBuzzSharp.NativeAssets.macOS.7.3.0.3\build\net462\HarfBuzzSharp.NativeAssets.macOS.targets')" />
  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
Plugin.TestResult/Properties/AssemblyInfo.cs
@@ -52,3 +52,4 @@
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: PluginClassName("Plugin.TestResult_WPF.ViewControl")]
Plugin.TestResult/Resource/abort.png
Plugin.TestResult/Scanner.xaml
@@ -15,7 +15,7 @@
        xmlns:pu="https://opensource.panuon.com/wpf-ui"
        xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
        xmlns:cc="clr-namespace:HMI.Core;assembly=HMI.Core"
        InteropOwnersMask="False"
        InteropOwnersMask="True"
        SizeToContent="Height"
        pu:WindowXModalDialog.Buttons="{x:Null}"
        Title="{Binding title}" Width="600">
@@ -58,21 +58,21 @@
         Header="{DynamicResource Tr.PositionSnr}"
         dp:FormGroupHelper.IsRequired="True"
         Style="{StaticResource InputFormGroupStyle}">
                    <TextBox Style="{StaticResource EditorTextBoxStyle}" x:Name="positionText" TextWrapping="NoWrap"/>
                    <TextBox Style="{StaticResource EditorTextBoxStyle}" x:Name="positionText" TextWrapping="NoWrap" KeyDown="TextBox_KeyDown"/>
                </pu:FormGroup>
                <pu:FormGroup 
         Header="{DynamicResource Tr.ProductSnr}"
         dp:FormGroupHelper.IsRequired="True" Style="{StaticResource InputFormGroupStyle}">
                    <TextBox Style="{StaticResource EditorTextBoxStyle}" x:Name="snrText" TextWrapping="NoWrap"/>
                    <TextBox Style="{StaticResource EditorTextBoxStyle}" x:Name="snrText" TextWrapping="NoWrap" KeyDown="TextBox_KeyDown"/>
                </pu:FormGroup>
            </StackPanel>
        </GroupBox>
        <Button Grid.Row="2" Click="Con_Click" Style="{StaticResource PrimaryButton}"
        Content="{DynamicResource Tr.ContinueScan}"/>
        <Button Grid.Row="2" Grid.Column="1" Click="Ok_Click" Style="{StaticResource SuccessButton}"
        <!--<Button Grid.Row="2" Click="Con_Click" Style="{StaticResource PrimaryButton}"
        Content="{DynamicResource Tr.ContinueScan}"/>-->
        <Button Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Click="Ok_Click" Style="{StaticResource SuccessButton}"
        Content="{DynamicResource Tr.FinishScan}"/>
    </Grid>
</pu:WindowX>
Plugin.TestResult/Scanner.xaml.cs
@@ -1,12 +1,15 @@
using HMI.Core.Util;
using HMI.UI.MDI;
using HMI.UI.Util;
using HMI.Util;
using Panuon.WPF.UI;
using Plugin.TestResult.Model;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO.Ports;
using System.Windows;
using System.Windows.Input;
namespace Plugin.TestResult_WPF
{
@@ -15,12 +18,16 @@
        public string name { get; private set; }
        public string positionCode { get; private set; }
        public string snrCode { get; private set; }
        public string oldPosition { get; private set; }
        public int oldIndex { get; private set; }
        public BarcodeEventArg(string name, string positionCode, string snrCode)
        public BarcodeEventArg(string name, string positionCode, string snrCode, string oldPosition, int oldIndex)
        {
            this.name = name;
            this.positionCode = positionCode;
            this.snrCode = snrCode;
            this.oldPosition = oldPosition;
            this.oldIndex = oldIndex;
        }
    }
@@ -38,6 +45,10 @@
        }
        public event EventHandler<BarcodeEventArg> FinishBarcode;
        public event EventHandler<RoutedEventArgs> ComplateClick;
        private static Dictionary<string, string> snrCache = new Dictionary<string, string>();
        private static Dictionary<string, int> snrIndex = new Dictionary<string, int>();
        private DataTable dataTable;
        SerialPort serialPort;
@@ -142,6 +153,44 @@
            }
        }
        public static void InsertNewCode(string snr, string position, int index)
        {
            snrCache[snr] = position;
            snrIndex[snr] = index;
        }
        public static void RemoveSNR(string snr)
        {
            snrCache.Remove(snr);
            snrIndex.Remove(snr);
        }
        private void SendCode(string tag, string position, string snr)
        {
            if (snrCache.ContainsKey(snr))
            {
                var oldPosition = snrCache[snr];
                if (MsgBoxHelper2.WarnChoose($"条码[{snr}]已放置于[{oldPosition}]中,是否清除?") == MessageBoxResult.OK)
                {
                    try
                    {
                        FinishBarcode.Invoke(this, new BarcodeEventArg(tag, position, null, oldPosition, snrIndex[snr]));
                        snrCache.Remove(snr);
                        snrIndex.Remove(snr);
                        FinishBarcode.Invoke(this, new BarcodeEventArg(tag, position, snr, null, -1));
                    }
                    catch (Exception ex)
                    {
                        ToastHelper.Error(ex.Message);
                    }
                }
            }
            else
            {
                FinishBarcode.Invoke(this, new BarcodeEventArg(tag, position, snr, null, -1));
            }
        }
        private void Con_Click(object sender, RoutedEventArgs e)
        {
            Dispatcher.Invoke(() =>
@@ -154,9 +203,9 @@
                }
                try
                {
                    FinishBarcode.Invoke(this, new BarcodeEventArg(Tag.ToString(), position, snr));
                    //positionText.Text = "";
                    //snrText.Text = "";
                    SendCode(Tag.ToString(), position, snr);
                    positionText.Text = "";
                    snrText.Text = "";
                    positionText.Focus();
                }
                catch (Exception ex)
@@ -167,24 +216,35 @@
            });
        }
        /// <summary>
        /// 原来的完成扫描逻辑
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        //private void Ok_Click(object sender, RoutedEventArgs e)
        //{
        //    string position = positionText.Text, snr = snrText.Text;
        //    if (string.IsNullOrEmpty(position) || string.IsNullOrEmpty(snr))
        //    {
        //        ToastHelper.Warning(this, this.lang("Tr.PleaseInputSnrs"));
        //        return;
        //    }
        //    try
        //    {
        //        SendCode(Tag.ToString(), position, snr);
        //        this.Close();
        //    }
        //    catch (Exception ex)
        //    {
        //        ViewControl.Logger.Error("Con_Click Failed.", ex);
        //        ToastHelper.Warning(this, ex.Message);
        //    }
        //}
        private void Ok_Click(object sender, RoutedEventArgs e)
        {
            string position = positionText.Text, snr = snrText.Text;
            if (string.IsNullOrEmpty(position) || string.IsNullOrEmpty(snr))
            {
                ToastHelper.Warning(this, this.lang("Tr.PleaseInputSnrs"));
                return;
            }
            try
            {
                FinishBarcode.Invoke(this, new BarcodeEventArg(Tag.ToString(), position, snr));
            ComplateClick?.Invoke(this, e);
                this.Close();
            }
            catch (Exception ex)
            {
                ViewControl.Logger.Error("Con_Click Failed.", ex);
                ToastHelper.Warning(this, ex.Message);
            }
        }
        private string ReadDataFrom(SerialPort sp)
@@ -194,7 +254,7 @@
            {
                byte[] buffer = new byte[sp.BytesToRead];
                sp.Read(buffer, 0, buffer.Length);
                barcode += System.Text.Encoding.UTF8.GetString(buffer);
                barcode += System.Text.Encoding.ASCII.GetString(buffer);
                System.Threading.Thread.Sleep(50);
            }
            return barcode;
@@ -211,5 +271,24 @@
            {
            }
        }
        private void TextBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                //string barcode =
                Dispatcher.Invoke(() =>
                {
                    if (snrText.IsFocused)
                    {
                        Con_Click(null, null);
                    }
                    else
                    {
                        snrText.Focus();
                    }
                });
            }
        }
    }
}
Plugin.TestResult/SingleCell.xaml
@@ -7,8 +7,8 @@
             mc:Ignorable="d" 
             xmlns:pw="https://opensource.panuon.com/wpf"
xmlns:pu="https://opensource.panuon.com/wpf-ui"
xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
             x:Name="uc"
             Padding="5"
             MouseDoubleClick="UserControl_MouseDoubleClick"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
@@ -21,7 +21,8 @@
    <UserControl.ContextMenu>
        <ContextMenu>
            <MenuItem IsCheckable="True" IsChecked="{Binding CellIsDisabled,Mode=TwoWay}"  Header="禁用"/>
            <!--<MenuItem IsCheckable="True" IsChecked="{Binding CellIsDisabled,Mode=TwoWay}"  Header="禁用"/>-->
            <MenuItem IsCheckable="False" x:Name="clearSNRItem" Click="MenuItem_Click" Header="清除条码"/>
        </ContextMenu>
    </UserControl.ContextMenu>
@@ -55,26 +56,62 @@
                <DataTrigger Binding="{Binding status}" Value="hadCode">
                    <Setter Property="Background" Value="#FFF"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding status}" Value="abort">
                    <Setter Property="Background" Value="Black"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </UserControl.Style>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="18"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <!--<TextBox Grid.Row="0" IsReadOnly="True" TextAlignment="Center" Margin="0,0,0,3" FontSize="15"
                 Background="Transparent" BorderThickness="0" Text="{Binding title}"/>-->
        <Label Grid.Column="0" HorizontalContentAlignment="Left" VerticalContentAlignment="Top" Padding="0" Margin="0"
        <StackPanel Orientation="Horizontal" Background="LightGray" Grid.Row="1" Grid.Column="1" VerticalAlignment="Top"  HorizontalAlignment="Right">
            <TextBlock Foreground="Red" Text="探针预警">
                <TextBlock.Style>
                    <Style TargetType="TextBlock">
                        <Setter Property="Visibility" Value="Collapsed"/>
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding probeAlarm}" Value="True">
                                <Setter Property="Visibility" Value="Visible"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </TextBlock.Style>
            </TextBlock>
            <TextBlock Margin="0">
                <Run Text="{Binding ProbeCount}"/>
                <Run Text="/"/>
                <Run Text="{Binding probeWarn}"/>
            </TextBlock>
        </StackPanel>
        <Label Grid.Row="0" Grid.Column="0" HorizontalContentAlignment="Left" VerticalContentAlignment="Top" Padding="0" Margin="5,5,0,0"
               FontSize="{Binding ActualWidth,RelativeSource={RelativeSource AncestorType=UserControl}, 
            Converter={StaticResource FontSizeConverter}, ConverterParameter='15,103'}"
   Converter={StaticResource FontSizeConverter}, ConverterParameter='8,103'}"
               FontWeight="Bold"
         Background="Transparent" BorderThickness="0" Content="{Binding title}"/>
        <Image Grid.Column="1">
Background="Transparent" BorderThickness="0" Content="{Binding title}">
            <Label.Style>
                <Style TargetType="Label">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding status}" Value="abort">
                            <Setter Property="Foreground" Value="#FFF"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Label.Style>
        </Label>
        <ui:ScrollLabel Grid.Row="1" Grid.Column="0" Margin="3,0,0,5" Padding="0" Value="{Binding sn}"/>
        <Image Grid.Row="0" Grid.Column="1" Width="30" HorizontalAlignment="Right" Height="30" Margin="0,5,5,0">
            <Image.Style>
                <Style TargetType="Image">
                    <Setter Property="HorizontalAlignment" Value="Center"/>
                    <Setter Property="VerticalAlignment" Value="Bottom"/>
                    <Setter Property="VerticalAlignment" Value="Top"/>
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding status}" Value="ok">
                            <Setter Property="Source" Value="Resource/ok2.png"/>
@@ -103,6 +140,9 @@
                        <DataTrigger Binding="{Binding status}" Value="hadCode">
                            <Setter Property="Source" Value="Resource/barcode.png"/>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding status}" Value="abort">
                            <Setter Property="Source" Value="Resource/abort.png"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Image.Style>
Plugin.TestResult/SingleCell.xaml.cs
@@ -1,4 +1,7 @@
using HMI.Util;
using HMI.Core.Manager;
using HMI.Core.Util;
using HMI.UI.Util;
using HMI.Util;
using Plugin.TestResult_WPF.Constant;
using Plugin.TestResult_WPF.Model;
using System;
@@ -38,31 +41,61 @@
        private InfoWindow window;
        private int maxSize;
        private static string dataLogPath;
        private static int ProbeWarn;
        static SingleCell()
        {
            try
            {
                var value = ParameterManager.GetCommonParameterByName("TS_DataLogPath");
                dataLogPath = value?.Value ?? Path.Combine(PathHelper.ConfigFolder, "DataBoard");
            }
            catch
            {
                dataLogPath = Path.Combine(PathHelper.ConfigFolder, "DataBoard");
            }
            try
            {
                var value = ParameterManager.GetCommonParameterByName("ProbeWarn");
                ProbeWarn = value != null ? int.Parse(value.Value) : 1000;
            }
            catch
            {
                ProbeWarn = 1000;
            }
        }
        public SingleCell()
        {
            DataContext = this;
            InitializeComponent();
        }
        public SingleCell(string parentName, int index, int maxSize, string iniFile)
        private DataTable table;
        public SingleCell(DataTable table, string title, int index, int maxSize, string iniFile)
        {
            this.parentName = parentName;
            this.table = table;
            this.title = title;
            this.index = index;
            this.maxSize = maxSize;
            this.countIniPath = iniFile;
            this.probeWarn = ProbeWarn;
            DataContext = this;
            InitializeComponent();
            LoadProbeCount();
        }
        private string _parentName;
        public string parentName
        private string _sn;
        public string sn
        {
            get { return _parentName; }
            get { return _sn; }
            set
            {
                RaiseAndSetIfchanged(ref _parentName, value);
                RaisePropertyChanged("title");
                RaiseAndSetIfchanged(ref _sn, value);
                RaisePropertyChanged("sn");
            }
        }
@@ -89,8 +122,25 @@
            }
        }
        private string _title;
        public string title
        {
            get => _title;
            set
            {
                RaiseAndSetIfchanged(ref _title, value);
            }
        }
        public string title => $"{parentName}-{index + 1}";
        private int _probeWarn;
        public int probeWarn
        {
            get => _probeWarn;
            set
            {
                RaiseAndSetIfchanged(ref _probeWarn, value);
            }
        }
        private string _status = CommonStatus.WAITING;
@@ -118,6 +168,8 @@
        private ConcurrentQueue<Info> queue = new ConcurrentQueue<Info>();
        private List<Info> allInfos = new List<Info>();
        private ObservableCollection<Info> _infos = new ObservableCollection<Info>();
        public ObservableCollection<Info> infos
@@ -141,6 +193,17 @@
            set
            {
                RaiseAndSetIfchanged(ref probeCount, value);
                probeAlarm = value >= probeWarn;
            }
        }
        private bool _probeAlarm;
        public bool probeAlarm
        {
            get => _probeAlarm;
            set
            {
                RaiseAndSetIfchanged(ref _probeAlarm, value);
            }
        }
@@ -165,7 +228,7 @@
        {
            if (window == null)
            {
                window = new InfoWindow(title, infos, electricChart, ProbeCount);
                window = new InfoWindow(title, infos, electricChart, vChart, ProbeCount);
                window.Closed += Window_Closed;
                window.ClearProbeCountEvent += Window_ClearProbeCountEvent;
                window.Owner = Window.GetWindow(this);
@@ -189,8 +252,9 @@
        public void SetStatus(string status)
        {
            var oldStatus = this.status;
            this.status = status;
            if (status == "testing")
            if (oldStatus != "testing" && status == "testing")
            {
                UpdateProbeCount(ProbeCount + 1);
            }
@@ -198,6 +262,7 @@
        public void SetInfos(Info info)
        {
            allInfos.Add(info);
            if (queue.Count >= maxSize)
            {
                queue.TryDequeue(out var first);
@@ -228,13 +293,18 @@
        public void ClearInfo()
        {
            allInfos.Clear();
            queue = new ConcurrentQueue<Info>();
            var list = new ObservableCollection<Info>(queue);
            this.infos = list;
            if (window != null) window.UpdateSource(this.infos);
        }
        private Chart electricChart = new Chart("电流曲线(A)", false, 20);
        private Chart electricChart = new Chart("电流曲线(A)", false, 20)
        {
            Height = 150
        };
        public void AddElectricData(double data)
        {
            electricChart.AddData(data);
@@ -243,6 +313,21 @@
        public void ClearElectricData()
        {
            electricChart.Clear();
        }
        private Chart vChart = new Chart("电压曲线(V)", false, 20)
        {
            Height = 150
        };
        public void AddVData(double data)
        {
            vChart.AddData(data);
        }
        public void ClearVData()
        {
            vChart.Clear();
        }
        public void SaveElectricDataToCSV(string filePath)
@@ -276,5 +361,148 @@
                Console.WriteLine(ex.ToString());
            }
        }
        public void SaveVDataToCSV(string filePath)
        {
            try
            {
                List<double> datas = vChart?.GetSingleData();
                FileInfo fi = new FileInfo(filePath);
                if (!fi.Directory.Exists)
                {
                    fi.Directory.Create();  //文件不存在,创建文件
                }
                using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
                using (StreamWriter sw = new StreamWriter(fs, System.Text.Encoding.UTF8))
                {
                    string title = "电压数据(V)";
                    sw.WriteLine(title);
                    if (datas != null)
                    {
                        for (int i = 0; i < datas.Count; i++)
                        {
                            sw.WriteLine(datas[i].ToString());
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }
        /// <summary>
        /// 保存测试数据到CSV
        /// </summary>
        /// <param name="filePath"></param>
        public void SaveInfoToCSV(string filePath)
        {
            try
            {
                FileInfo fi = new FileInfo(filePath);
                if (!fi.Directory.Exists)
                {
                    fi.Directory.Create();  //文件不存在,创建文件
                }
                using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
                using (StreamWriter sw = new StreamWriter(fs, System.Text.Encoding.UTF8))
                {
                    string title = "测试项,测试值,下限,上限,单位,结果,测试时间";
                    sw.WriteLine(title);
                    foreach (var info in allInfos)
                    {
                        string row = $"{info.step},{info.value},{info.limitDown},{info.limitUp},{info.unit},{info.result},{info.updateTime}";
                        sw.WriteLine(row);
                    }
                }
            }
            catch (Exception ex)
            {
                ViewControl.Logger.Error($"Cell[{title}] SaveInfoToCSV Error.", ex);
            }
        }
        private SqliteHelper CheckSqlHelper()
        {
            string today = DateTime.Now.ToString("yyyyMMdd");
            string dbPath = Path.Combine(dataLogPath, $"Data_{today}.db");
            if (!File.Exists(dbPath))
            {
                SqliteHelper.CreateDatabase(dbPath);
            }
            var sqliteHelper = new SqliteHelper(dbPath);
            sqliteHelper.ExecuteSql(@"CREATE TABLE IF NOT EXISTS serialData (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    sn TEXT NOT NULL,
                    model TEXT,
                    station TEXT NOT NULL,
                    step TEXT NOT NULL,
                    position TEXT,
                    limitDown TEXT,
                    limitUp TEXT,
                    unit TEXT,
                    offset TEXT,
                    value TEXT,
                    status TEXT,
                    finalStatus TEXT,
                    consumeTime TEXT,
                    totalConsumeTime TEXT,
                    time DATETIME NOT NULL);");
            sqliteHelper.ExecuteSql(@"CREATE TABLE IF NOT EXISTS testResult (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    sn TEXT NOT NULL,
                    model TEXT,
                    station TEXT NOT NULL,
                    position TEXT,
                    status TEXT,
                    result TEXT NOT NULL,
                    time DATETIME NOT NULL);");
            return sqliteHelper;
        }
        /// <summary>
        /// 保存测试数据到Sqlite
        /// </summary>
        /// <param name="filePath"></param>
        public void SaveInfoToDB()
        {
            try
            {
                using (var helper = CheckSqlHelper())
                {
                    List<string> sqls = new List<string>();
                    var timestr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                    foreach (var info in allInfos)
                    {
                        string sql = $@"INSERT INTO serialData (sn,station,step,limitDown,limitUp,unit,value,status,finalStatus,time)
VALUES ('{sn}','','{info.step}','{info.limitDown}','{info.limitUp}','{info.unit}','{info.value}','{info.result}','{this.status}','{timestr}');";
                        sqls.Add(sql);
                    }
                    sqls.Add($"INSERT INTO testResult (sn,station,position,result,time) VALUES ('{sn}','','{title}','{this.status}','{timestr}');");
                    helper.ExecuteBatchSql(sqls);
                }
            }
            catch (Exception ex)
            {
                ViewControl.Logger.Error($"Cell[{title}] SaveToDB Error.", ex);
            }
        }
        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            var item = sender as MenuItem;
            switch (item?.Name)
            {
                case "clearSNRItem":
                    table.TryClearCellSN(this.index);
                    break;
                default:
                    break;
            }
        }
    }
}
Plugin.TestResult/TestResultView.xaml
@@ -14,7 +14,7 @@
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid x:Name="panel">
        <WindowsFormsHost x:Name="host" Visibility="Collapsed"></WindowsFormsHost>
        <Grid x:Name="mainGrid" dp:GridHelper.Space="10">
        <Grid x:Name="mainGrid" dp:GridHelper.Space="6">
        </Grid>
    </Grid>
Plugin.TestResult/TestResultView.xaml.cs
@@ -3,7 +3,6 @@
using NationalInstruments.TestStand.Interop.API;
using NationalInstruments.TestStand.Interop.UI.Ax;
using NationalInstruments.TestStand.Utility;
using Plugin.TestResult_WPF.Constant;
using Plugin.TestResult_WPF.Model;
using Plugin.TsSupport;
using System;
@@ -82,42 +81,6 @@
        private void Refresh()
        {
            for (int i = 0; i < tables.Length; i++)
            {
                var sb = new System.Timers.Timer()
                {
                    Interval = 1000,
                    AutoReset = true,
                };
                var table = tables[i];
                Random random = new Random(i * 13);
                sb.Elapsed += (s, e) =>
                {
                    table.RefreshCount(1, 0);
                    table.SetTemperature(random.Next(100));
                    for (int j = 0; j < 3; j++)
                    {
                        table.SetTemperature(j + "89", random.Next(100));
                    }
                    //table.SetOkCount(random.Next(100));
                    //table.SetNgCount(random.Next(100));
                    table.SetStatus(CommonStatus.GetRandomStatus());
                    table.SetStartTime(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                    table.SetEndTime(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                    table.SetConsumeTime(random.Next(10000));
                    tables[0].GetSingleCell(0).AddElectricData(random.Next(100));
                    var cell = tables[0].GetSingleCell(0);//table.GetSingleCell(random.Next(table.GetSize()));
                    cell.SetStatus(CommonStatus.GetRandomStatus());
                    cell.SetInfos(new Info
                    {
                        step = random.Next(1000) + "",
                        value = random.Next(1000) + ""
                    });
                    table.SetStatusText(DateTime.Now + "");
                };
                sb.Start();
            }
        }
        private string CountIniPath;
@@ -187,6 +150,7 @@
        {
            try
            {
                if (string.IsNullOrEmpty(testSeqFile)) return;
                this.modeSeq = this.engine.GetSequenceFileEx(@"C:\Program Files\National Instruments\TestStand 2020\Components\Models\TestStandModels\ParallelModel.seq");
                this.testSeq = this.engine.GetSequenceFileEx(this.testSeqFile, 0, TypeConflictHandlerTypes.ConflictHandler_UseGlobalType);
                this.execution = this.engine.NewExecution(this.testSeq, "Test UUTs", this.modeSeq, false, 0, null, null, null);
@@ -360,10 +324,11 @@
                    {
                        step = values[0],
                        value = values[1],
                        result = values[2],
                        range = values[3],
                        condition = values[4],
                        updateTime = values[5]
                        limitUp = values[2],
                        limitDown = values[3],
                        unit = values[4],
                        result = values[5],
                        updateTime = values[6]
                    });
                    return;
                }
@@ -387,7 +352,7 @@
                }
                if (MethodCode == 12)
                {
                    /// 保存温度文件
                    /// 保存炉体温度文件
                    strData = e.uiMsg.StringData;
                    string[] datas = strData.Split(';');
                    int ovenIndex = int.Parse(datas[0]);
@@ -408,21 +373,95 @@
                }
                if (MethodCode == 14)
                {
                    /// 清空电流数据
                    /// 清空电流和电压数据
                    strData = e.uiMsg.StringData;
                    int ovenIndex = int.Parse(strData);
                    tables[ovenIndex].ClearElectricData();
                    tables[ovenIndex].ClearVData();
                    return;
                }
                if (MethodCode == 15)
                {
                    /// 清空电流数据
                    /// 保存电流数据
                    strData = e.uiMsg.StringData;
                    string[] datas = strData.Split(';');
                    int ovenIndex = int.Parse(datas[0]);
                    int cellIndex = int.Parse(datas[1]);
                    string filePath = datas[2];
                    tables[ovenIndex].GetSingleCell(cellIndex).SaveElectricDataToCSV(filePath);
                    return;
                }
                if (MethodCode == 16)
                {
                    /// 添加电压数据
                    strData = e.uiMsg.StringData;
                    string[] datas = strData.Split(';');
                    int ovenIndex = int.Parse(datas[0]);
                    int cellIndex = int.Parse(datas[1]);
                    double eData = double.Parse(datas[2]);
                    tables[ovenIndex].GetSingleCell(cellIndex).AddVData(eData);
                    return;
                }
                if (MethodCode == 17)
                {
                    /// 保存电压数据
                    strData = e.uiMsg.StringData;
                    string[] datas = strData.Split(';');
                    int ovenIndex = int.Parse(datas[0]);
                    int cellIndex = int.Parse(datas[1]);
                    string filePath = datas[2];
                    tables[ovenIndex].GetSingleCell(cellIndex).SaveVDataToCSV(filePath);
                    return;
                }
                if (MethodCode == 18)
                {
                    /// 保存穴位测试数据到CSV
                    strData = e.uiMsg.StringData;
                    string[] datas = strData.Split(';');
                    int ovenIndex = int.Parse(datas[0]);
                    int cellIndex = int.Parse(datas[1]);
                    string filePath = datas[2];
                    tables[ovenIndex].GetSingleCell(cellIndex).SaveInfoToCSV(filePath);
                    return;
                }
                if (MethodCode == 19)
                {
                    /// 保存穴位测试数据到Sqlite
                    strData = e.uiMsg.StringData;
                    string[] datas = strData.Split(';');
                    int ovenIndex = int.Parse(datas[0]);
                    int cellIndex = int.Parse(datas[1]);
                    tables[ovenIndex].GetSingleCell(cellIndex).SaveInfoToDB();
                    return;
                }
                if (MethodCode == 20)
                {
                    /// 清空条码
                    strData = e.uiMsg.StringData;
                    int ovenIndex = int.Parse(strData);
                    tables[ovenIndex].ClearAllSN();
                    return;
                }
                if (MethodCode == 99)
                {
                    strData = e.uiMsg.StringData;
                    string[] datas = strData.Split(';');
                    int index = int.Parse(datas[0]);
                    if (MsgBoxHelper2.WarnChoose(datas[1], "提示") == MessageBoxResult.OK)
                    {
                        SequenceContext.StationGlobals.SetValNumber($"StationState.Pipe[{index}]", 1, 1);
                    }
                    else
                    {
                        SequenceContext.StationGlobals.SetValNumber($"StationState.Pipe[{index}]", 1, 0);
                    }
                    return;
                }
            }
@@ -436,7 +475,7 @@
        {
            if (this.SequenceContext == null) return;
            string name = tables[index].title;
            string key = $"StationState.Start_{name}";
            string key = $"StationState.Start[{index}]";
            this.engine.Globals.SetValBoolean(key, 1, data);
            //bool val = this.engine.Globals.GetValBoolean(key, 0);
            //this.SequenceContext.StationGlobals.SetValBoolean(key, 0, data);
@@ -475,16 +514,21 @@
        public void SetCurrentProduct(string currentProduct)
        {
            foreach (var table in tables)
            for (int i = 0; i < tables.Length; i++)
            {
                table.SetCurrentProduct(currentProduct);
                var table = tables[i];
                // table.SetCurrentProduct(currentProduct);
                string LookupStr = $"StationState.Model[{i}]";
                string tableModel = engine?.Globals?.GetValString(LookupStr, 1);
                table.SetCurrentProduct(tableModel);
            }
        }
        public void SetOvenProduct(int index, string product)
        {
            string LookupStr = $"StationState.ProductModel_{tables[index].title}";
            string LookupStr = $"StationState.Model[{index}]";
            engine?.Globals?.SetValString(LookupStr, 1, product);
            engine?.CommitGlobalsToDisk(promptOnSaveConflicts: false);
        }
    }
}
Plugin.TestResult/Util/CommonCodeParser.cs
@@ -1,13 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Plugin.TestResult.Util;
using Plugin.TestResult_WPF.Model;
using System;
namespace Plugin.TestResult_WPF.Util
{
    public class CommonCodeParser
    public class CommonCodeParser: ICodeParser
    {
        public CellInfo GetCellInfoByCode(string code)
        {
Plugin.TestResult/Util/ICodeParser.cs
New file
@@ -0,0 +1,9 @@
using Plugin.TestResult_WPF.Model;
namespace Plugin.TestResult.Util
{
    public interface ICodeParser
    {
        CellInfo GetCellInfoByCode(string code);
    }
}
Plugin.TestResult/Util/JKCodeParser.cs
New file
@@ -0,0 +1,26 @@
using Plugin.TestResult.Util;
using Plugin.TestResult_WPF.Model;
using System;
namespace Plugin.TestResult_WPF.Util
{
    public class JKCodeParser : ICodeParser
    {
        public CellInfo GetCellInfoByCode(string code)
        {
            string[] arr = code.Split('-');
            if (arr == null || arr.Length != 3)
            {
                throw new Exception("扫码格式异常");
            }
            int row = int.Parse(arr[1]);
            int index = int.Parse(arr[2]);
            return new CellInfo()
            {
                OvenName = arr[0],
                Index = (row - 1) * 4 + (index - 1),
            };
        }
    }
}
Plugin.TestResult/ViewControl.xaml
@@ -1,15 +1,13 @@
<hb:PluginMaskControl x:Class="Plugin.TestResult_WPF.ViewControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:Plugin.TestResult_WPF"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
                  xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
xmlns:pw="https://opensource.panuon.com/wpf"
xmlns:pu="https://opensource.panuon.com/wpf-ui"
                      xmlns:std="clr-namespace:HMI.StdControl;assembly=HMI.StdControl"
xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
                      xmlns:local="clr-namespace:Plugin.TestResult_WPF"
                      xmlns:pu="https://opensource.panuon.com/wpf-ui"
             mc:Ignorable="d" 
                      Style="{StaticResource pluginMaskControl}"
             Loaded="UserControl_Loaded"
@@ -34,20 +32,59 @@
        </Style>
    </UserControl.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <!--<Grid.ColumnDefinitions>
            <ColumnDefinition Width="7.5*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <local:TestResultView x:Name="testResultView" Grid.Column="0" Loaded="testResultView_Loaded" />
        <StackPanel Grid.Column="1" Background="#FFF">
            <GroupBox Header="{DynamicResource Tr.ProductInfo}">
                <ComboBox x:Name="modelSelect" Style="{StaticResource FormComboxStyle}"
        </Grid.ColumnDefinitions>-->
        <local:TestResultView x:Name="testResultView" Grid.Row="1" Grid.Column="0" Loaded="testResultView_Loaded" />
        <DockPanel Margin="10,0" Grid.Row="0" Grid.Column="1" Background="#FFF" VerticalAlignment="Center">
            <pu:FormGroup DockPanel.Dock="Right">
                <StackPanel Orientation="Horizontal" VerticalAlignment="Center" dp:StackPanelHelper.ChildSpace="20">
                    <Button x:Name="btnStart" Click="Start_Click" Style="{StaticResource SuccessButton}"
                       Width="100" FontSize="14" Padding="0" Height="30" Content="{DynamicResource App.Start}"/>
                    <Button x:Name="btnStop" Click="Stop_Click" Style="{StaticResource DangerButton}"
                            Width="100" FontSize="14" Padding="0" Height="30"
                    Content="{DynamicResource App.Stop}"/>
                    <Button x:Name="btnClear" Click="BtnClear_Click" Style="{StaticResource PrimaryButton}"
                            Width="100" FontSize="14" Padding="0" Height="30"
                    Content="{DynamicResource Ts.BtnClearNum}"/>
                    <Button x:Name="scannerSetting" Click="ScannerSetting_Click" Style="{StaticResource PrimaryButton}"
                            Width="100" FontSize="14" Padding="0" Height="30" Content="扫码枪设置" Cursor="Hand"/>
                    <!--<Button x:Name="btnScan1" Click="Scan_Click_1" Style="{StaticResource PrimaryButton}"
                    Content="{DynamicResource Tr.Zone1Scan}"/>
            <Button x:Name="btnScan2" Click="Scan_Click_2" Style="{StaticResource PrimaryButton}"
                    Content="{DynamicResource Tr.Zone2Scan}"/>-->
                </StackPanel>
            </pu:FormGroup>
            <pu:FormGroup Header="{DynamicResource Tr.ProductInfo}" FontSize="16" Visibility="Collapsed">
                <ComboBox x:Name="modelSelect" Style="{StaticResource FormComboxStyle}" Width="200"
                          Height="28"
                          pu:ComboBoxHelper.ClearButtonVisibility="Collapsed"
                          SelectionChanged="modelSelect_SelectionChanged"/>
            </GroupBox>
            <GroupBox Header="{DynamicResource Tr.Status}">
                <StackPanel>
                    <Label HorizontalContentAlignment="Center" HorizontalAlignment="Stretch">
            </pu:FormGroup>
            <pu:FormGroup Header="测试程序" FontSize="16">
                <TextBox IsReadOnly="True" x:Name="tbFileName" Style="{StaticResource EditorTextBoxStyle}" Width="200"
Height="28" Text="{Binding FileName}"/>
            </pu:FormGroup>
            <pu:FormGroup>
                <std:StdEnableMes DockPanel.Dock="Left" FontSize="20" ValueChanged="StdEnableMes_ValueChanged"/>
            </pu:FormGroup>
            <!--<pu:FormGroup Header="{DynamicResource Tr.Status}" FontSize="16">
                <StackPanel Orientation="Horizontal" dp:StackPanelHelper.ChildSpace="10">
                    <Label HorizontalContentAlignment="Center" VerticalAlignment="Center">
                        <Label.Style>
                            <Style TargetType="Label">
                                <Setter Property="HorizontalContentAlignment"  Value="Center"/>
@@ -73,6 +110,7 @@
                            <Style TargetType="Label">
                                <Setter Property="HorizontalContentAlignment"  Value="Center"/>
                                <Setter Property="HorizontalAlignment"  Value="Stretch"/>
                                <Setter Property="VerticalAlignment"  Value="Center"/>
                                <Setter Property="Content" Value="{Binding produceStatus}"/>
                                <Setter Property="Margin" Value="0,10"/>
                                <Style.Triggers>
@@ -93,28 +131,7 @@
                        </Label.Style>
                    </Label>
                </StackPanel>
            </GroupBox>
            <GroupBox Header="操作">
                <StackPanel dp:StackPanelHelper.ChildSpace="20">
                    <Button x:Name="btnStart" Click="Start_Click" Style="{StaticResource SuccessButton}"
                            Content="{DynamicResource App.Start}"/>
                    <Button x:Name="btnStop" Click="Stop_Click" Style="{StaticResource DangerButton}"
                            Content="{DynamicResource App.Stop}"/>
                    <Button x:Name="btnClear" Click="BtnClear_Click" Style="{StaticResource PrimaryButton}"
                            Content="{DynamicResource Ts.BtnClearNum}"/>
                    <Button x:Name="scannerSetting" Click="ScannerSetting_Click" Style="{StaticResource PrimaryButton}"
        Content="扫码枪设置"/>
                    <!--<Button x:Name="btnScan1" Click="Scan_Click_1" Style="{StaticResource PrimaryButton}"
                            Content="{DynamicResource Tr.Zone1Scan}"/>
                    <Button x:Name="btnScan2" Click="Scan_Click_2" Style="{StaticResource PrimaryButton}"
                            Content="{DynamicResource Tr.Zone2Scan}"/>-->
                </StackPanel>
            </GroupBox>
        </StackPanel>
            </pu:FormGroup>-->
        </DockPanel>
    </Grid>
</hb:PluginMaskControl>
Plugin.TestResult/ViewControl.xaml.cs
@@ -2,6 +2,7 @@
using HMI.Core.Entity;
using HMI.Core.Manager;
using HMI.Core.Util;
using HMI.StdControl;
using HMI.UI;
using HMI.UI.Util;
using log4net;
@@ -13,6 +14,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
@@ -30,7 +32,6 @@
        private double TestVoltage = 12;
        private double Time_Heating_Low = 300;
        private double Time_Heating_High = 2400;
        private string testSeqFile;
        private Scanner scannerWindow1;
        private Scanner scannerWindow2;
        /// <summary>
@@ -57,6 +58,14 @@
            set => Set(ref _produceStatus, value);
        }
        private string _fileName;
        public string FileName
        {
            get => _fileName;
            set => Set(ref _fileName, value);
        }
        public override void SetLogger(ILog logger)
        {
            base.SetLogger(logger);
@@ -76,6 +85,8 @@
                this.btnStop.IsEnabled = value;
            }
        }
        private bool AutoExecuteSeq = true;
        public ViewControl()
        {
@@ -107,6 +118,28 @@
            }
            SannerIniFile = path;
            LoadProduct();
            try
            {
                var value = ParameterManager.GetCommonParameterByName("AutoExecuteSeq");
                AutoExecuteSeq = bool.Parse(value.Value);
            }
            catch
            {
                AutoExecuteSeq = true;
            }
            try
            {
                var value = ParameterManager.GetCommonParameterByName("BurnInMainSeq");
                testResultView.testSeqFile = value.Value;
                FileName = Path.GetFileName(value.Value);
            }
            catch
            {
                testResultView.testSeqFile = string.Empty;
                FileName = string.Empty;
            }
        }
        public override void DoClose(object sender, EventArgs e)
@@ -136,16 +169,22 @@
                int MethodCode = Convert.ToInt16(e.uiMsg.NumericData);
                string strData;
                if (EventCase != 10014) return;
                try
                {
                    log.Info($"EventCase: {EventCase}, MethodCode: {MethodCode}, StrData: {e.uiMsg.StringData}");
                    log.Debug($"EventCase: {EventCase}, MethodCode: {MethodCode}, StrData: {e.uiMsg.StringData}");
                }
                catch
                {
                }
                if (EventCase == 8)
                {
                    IsRunning = false;
                    return;
                }
                if (EventCase != 10014) return;
                if (MethodCode == 0)
                {
@@ -178,24 +217,45 @@
        private bool isLoaded;
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            new MaskWindow(Window.GetWindow(this), (win) =>
            if (AutoExecuteSeq)
            {
                try
                ShowMask(() =>
                {
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        Start_Click(null, null);
                    }), System.Windows.Threading.DispatcherPriority.Background);
                }, (ex) =>
                {
                    ToastHelper.Error("Loaded Failed.");
                    log.Error("Loaded Failed.", ex);
                }, "请稍后", LangUtil.Get("Ts.SequenceIsStarting"));
            }
            else
            {
                IsRunning = false;
            }
                        Loaded -= UserControl_Loaded;
                    }));
                }
                finally
                {
                    Dispatcher.Invoke(() =>
                    {
                        win.Close();
                    });
                }
            }).Info(LangUtil.Get("Ts.SequenceIsStarting"), 100);
            //new MaskWindow(Window.GetWindow(this), (win) =>
            //{
            //    try
            //    {
            //        Dispatcher.BeginInvoke(new Action(() =>
            //        {
            //            Start_Click(null, null);
            //            Loaded -= UserControl_Loaded;
            //        }));
            //    }
            //    finally
            //    {
            //        Dispatcher.Invoke(() =>
            //        {
            //            win.Close();
            //        });
            //    }
            //}).Info(LangUtil.Get("Ts.SequenceIsStarting"), 100);
            //ShowMask(() =>
            //{
@@ -254,7 +314,7 @@
                //this.SaftyTemperature = Convert.ToDouble(this.parameterManager.Find("SaftyTemperature").ParmValue);
                //this.Time_Heating_Low = Convert.ToDouble(this.parameterManager.Find("Time_Heat_Low").ParmValue);
                //this.Time_Heating_High = Convert.ToDouble(this.parameterManager.Find("Time_Heat_High").ParmValue);
                testResultView.testSeqFile = parameters.FirstOrDefault(p => p.Name == "TestSequence")?.Value;
                //testResultView.testSeqFile = parameters.FirstOrDefault(p => p.Name == "TestSequence")?.Value;
                testResultView.engine.Globals.SetValString("ProductModel", 0, currentModel);
                testResultView.engine.CommitGlobalsToDisk(promptOnSaveConflicts: false);
@@ -269,6 +329,12 @@
        {
            try
            {
                if (string.IsNullOrEmpty(testResultView.testSeqFile))
                {
                    MsgBoxHelper2.Warn("请先设置BurnInMainSeq属性!");
                    IsRunning = false;
                    return;
                }
                testResultView.StartTestSequence();
                IsRunning = true;
            }
@@ -293,70 +359,6 @@
                MsgBoxHelper.Error(Window.GetWindow(this),
                    $"{this.lang("Ts.StopTestSequenceFailed")}: {ex.Message}");
            }
        }
        private void Scan_Click_1(object sender, RoutedEventArgs e)
        {
            if (scannerWindow1 == null)
            {
                scannerWindow1 = new Scanner(this.lang("Tr.Zone1Scan"))
                {
                    Tag = "Zone1"
                };
                scannerWindow1.FinishBarcode += ScannerWindow_FinishBarcode;
                //scannerWindow1.Owner = Window.GetWindow(this);
                scannerWindow1.Closed += ScannerWindow1_Closed;
                scannerWindow1.Show();
            }
            else
            {
                scannerWindow1.Activate();
            }
        }
        private void Scan_Click_2(object sender, RoutedEventArgs e)
        {
            if (scannerWindow2 == null)
            {
                scannerWindow2 = new Scanner(this.lang("Tr.Zone2Scan"))
                {
                    Tag = "Zone2"
                };
                scannerWindow2.FinishBarcode += ScannerWindow_FinishBarcode;
                scannerWindow2.Owner = Window.GetWindow(this);
                scannerWindow2.Closed += ScannerWindow2_Closed;
                scannerWindow2.Show();
            }
            else
            {
                scannerWindow2.Activate();
            }
        }
        /// <summary>
        /// 扫码完成逻辑
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ScannerWindow_FinishBarcode(object sender, BarcodeEventArg e)
        {
            if (this.SequenceContext == null) return;
            CellInfo cellInfo = codeParser.GetCellInfoByCode(e.positionCode);
            string paramName = e.name == "Zone1" ? "StationState.StartScanner_A" : "StationState.StartScanner_B";
            testResultView.engine.Globals.SetValBoolean(paramName, 0, true);
            string positionCode = e.positionCode, snrCode = e.snrCode;
            //需要增加条码校验功能
            //将读到的条码写入到SN中
            int dutNo = Convert.ToInt16(positionCode.Split('-')[2]) - 1;
            string LookupStr = e.name == "Zone1" ? $"StationState.SN[{dutNo}]" : $"StationState.SN[{(dutNo + 54)}]";
            //this.SequenceContext.StationGlobals.SetValString(LookupStr, 0, snrCode);
            //string code = this.SequenceContext.StationGlobals.GetValString(LookupStr, 0);
            testResultView.engine.Globals.SetValString(LookupStr, 0, snrCode);
            string code = testResultView.engine.Globals.GetValString(LookupStr, 0);
            testResultView.nameTables[cellInfo.OvenName].GetSingleCell(cellInfo.Index).SetStatus("hadCode");
        }
        private void ScannerWindow1_Closed(object sender, EventArgs e)
@@ -391,5 +393,10 @@
            };
            setting.Open(false, "Config");
        }
        private void StdEnableMes_ValueChanged(object sender, StdUIEventArg e)
        {
            testResultView.SequenceContext?.StationGlobals.SetValBoolean($"SystemSetting.{e.StringValue}", 1, e.BoolValue);
        }
    }
}
Plugin.TsExecution/Properties/AssemblyInfo.cs
@@ -49,6 +49,6 @@
//      生成号
//      修订号
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.1")]
[assembly: AssemblyVersion("1.0.0.2")]
[assembly: AssemblyFileVersion("1.0.0.2")]
[assembly: PluginClassName("Plugin.TsExecution.TsExecution")]
Plugin.TsExecution/SingleControl.xaml
@@ -354,8 +354,7 @@
                  RowHeight="{StaticResource nanD}"
                  ColumnHeaderHeight="{Binding ActualWidth,ElementName=control,
Converter={StaticResource fc}, ConverterParameter='32,1093,30,45'}"
                  ItemsSource="{Binding Datasource}"
                  x:Name="table">
                  ItemsSource="{Binding Datasource}" x:Name="table">
                <DataGrid.Resources>
                    <Style TargetType="DataGridCell" 
       BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridCellStyle}}">
Plugin.TsExecution/SingleControl.xaml.cs
@@ -25,7 +25,6 @@
        protected void Set([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
@@ -221,8 +220,8 @@
        public SingleControl(int Index, string CountIniPath, int probeWarn)
        {
            this.CountIniPath = CountIniPath;
            DataContext = this;
            this.CountIniPath = CountIniPath;
            this.index = Index;
            ProbeWarn = probeWarn;
@@ -550,14 +549,6 @@
                //var modal = scannerVisualModal;
                //Canvas.SetLeft(modal, (scannerPanel.ActualWidth - scannerVisualModal.ActualWidth) / 2);
                //Canvas.SetTop(modal, (scannerPanel.ActualHeight - scannerVisualModal.ActualHeight) / 2);
            }
        }
        private void TextBlock_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            DependencyObject parent = VisualTreeHelper.GetParent((DependencyObject)sender);
            while (parent != null) {
                parent = VisualTreeHelper.GetParent(parent);
            }
        }
    }
Plugin.TsExecution/TsExecution.xaml.cs
@@ -6,7 +6,6 @@
using HMI.Core.Util;
using HMI.MQUtil;
using HMI.UI;
using HMI.UI.Controls;
using HMI.UI.Util;
using HMI.Util;
using log4net;
@@ -26,7 +25,6 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms.DataVisualization.Charting;
using System.Windows.Media;
namespace Plugin.TsExecution
{
@@ -268,6 +266,7 @@
        private SqliteHelper sqliteHelper;
        private MQConnector mqConnector;
        private int probeWarn;
        public TsExecution()
        {
@@ -279,6 +278,7 @@
            //初始化TestStand引擎
            this.InitTestStandEngine();
        }
        public override void SetBaseFolder(string folder)
        {
            CountIniPath = Path.Combine(folder, "Count.ini");
@@ -330,8 +330,8 @@
        {
            this.ReadCount(out int pass, out int fail);
            this.DisplayCount(pass, fail);
            InitProducts();
            ReadConfig();
            InitProducts();
            CheckSqlHelper();
            base.InitFinish();
        }
@@ -352,30 +352,6 @@
            {
                HandleUIMessage(eventCode, index, strData);
            }));
        }
        void createChildInNewThread(int index, string configFile, int probeWarn)
        {
            HostVisual hostVisual = new HostVisual();
            UIElement content = new VisualHost(hostVisual);
            System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(() =>
            {
                VisualTarget visualTarget = new VisualTarget(hostVisual);
                var control = new SingleControl(index, configFile, probeWarn);
                control.Arrange(new Rect(new Point(), content.RenderSize));
                visualTarget.RootVisual = control;
                tsSockets.Add(control);
                System.Windows.Threading.Dispatcher.Run();
            }));
            //panel.Children.Add(content);
            thread.SetApartmentState(System.Threading.ApartmentState.STA);
            thread.IsBackground = true;
            thread.Start();
        }
        /// <summary>
@@ -421,28 +397,19 @@
            Products = ProductManager.GetProducts<ObservableCollection<ProductData>>();
            if (Products != null && Products.Count > 0)
            {
                List<ParameterData> parameters =
                    ParameterManager.GetCommonParameter<List<ParameterData>>();
                string tmpNum = parameters?.FirstOrDefault(p => p.Name == "SocketNum")?.Value?.ToString();
                if (!string.IsNullOrEmpty(tmpNum))
                {
                    this.socketNum = Convert.ToInt32(tmpNum);
                }
                string lastModel = engine?.Globals.GetValString("StationState.ProductModel", 1);
                var tempProduct = Products[0];
                if (!string.IsNullOrEmpty(lastModel))
                {
                    var product = Products.FirstOrDefault(p => p.Name == lastModel);
                    if (product != null)
                    {
                        SelectedProduct = product;
                        tempProduct = product;
                    }
                }
                SelectedProduct = tempProduct;
            }
                        RefershUI();
                        return;
                    }
                }
                SelectedProduct = Products[0];
                RefershUI();
            }
            SampleStatus = false;
        }
@@ -552,25 +519,11 @@
        private void RefershUI()
        {
            ParameterData p = ParameterManager.GetCommonParameterByName("ProbeWarn");
            int ProbeWarn = 0;
            if (p != null)
            {
                try
                {
                    ProbeWarn = Convert.ToInt32(p.Value);
                }
                catch
                {
                    ProbeWarn = 0;
                }
            }
            panel.Children.Clear();
            //重新生成子面板
            for (int i = 0; i < this.socketNum; i++)
            {
                SingleControl control = new SingleControl(i, CountIniPath, ProbeWarn);
                SingleControl control = new SingleControl(i, CountIniPath, this.probeWarn);
                panel.Children.Add(control);
                tsSockets.Add(control);
@@ -641,6 +594,19 @@
            catch
            {
                flag = false;
            }
            try
            {
                value = ParameterManager.GetCommonParameterByName("SocketNum");
                if (value != null)
                {
                    this.socketNum = int.Parse(value.Value);
                }
            }
            catch
            {
                this.socketNum = 2;
            }
            if (flag)
@@ -860,26 +826,23 @@
                tsModelPath = @"C:\Users\Public\Documents\National Instruments\TestStand 2020 (64-bit)\Components\Models\ParallelModel.seq";
            }
            try
            {
                value = ParameterManager.GetCommonParameterByName("ProbeWarn");
                probeWarn = int.Parse(value?.Value);
            }
            catch
            {
                probeWarn = 0;
            }
            //Convert.ToBoolean(IniFileHelper.ReadIniData("Config", "EnableMes", "true", CountIniPath));
            //EnableCanLog = Convert.ToBoolean(IniFileHelper.ReadIniData("Config", "EnableCanLog", "true", CountIniPath));
            //EnableDebugMode = Convert.ToBoolean(IniFileHelper.ReadIniData("Config", "EnableDebugMode", "true", CountIniPath));
        }
        //private void WriteToTsWhenInit()
        //{
        //    foreach (var ts in tsSockets)
        //    {
        //        ts?.SequenceFileContext?.StationGlobals.SetValBoolean($"SystemSetting.EnableMes", 1, EnableMes);
        //        ts?.SequenceFileContext?.StationGlobals.SetValBoolean($"SystemSetting.EnableNGPause", 1, EnableNGPause);
        //        ts?.SequenceFileContext?.StationGlobals.SetValBoolean($"SystemSetting.EnableCanLog", 1, EnableCanLog);
        //        ts?.SequenceFileContext?.StationGlobals.SetValBoolean($"SystemSetting.EnableDebugMode", 1, EnableDebugMode);
        //    }
        //}
        private bool WriteConfig(string name, bool value)
        {
            //IniFileHelper.WriteIniData("Config", name, value.ToString(), CountIniPath);
            try
            {
                if (ParameterManager.UpdateCommonParameter(name, value, out string errMsg))
@@ -1170,21 +1133,30 @@
            this.tsSockets[index].WriteToCSV(csvPath);
        }
        public void SaveTemplateCSV(int index)
        {
            var time = DateTime.Now;
            string status = this.tsSockets[index].Status == "Passed" ? "PASS" : "FAIL";
            string csvPath = Path.Combine(csvDir, SelectedProduct?.Name ?? "未知产品", time.ToString("yyyyMMdd"), status, $"{this.tsSockets[index].Barcode}-{time.ToString("HHmmss")}.csv");
            this.tsSockets[index].WriteToCSV(csvPath);
        }
        public void SavePositionDB(int index, string status)
        {
            CheckSqlHelper();
            var socket = this.tsSockets[index];
            var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            string snr = socket.Barcode;
            string model = SelectedProduct?.Name;
            List<string> sqls = new List<string>();
            foreach (var data in socket.Datasource)
            {
                sqls.Add($@"INSERT INTO serialData (sn,model,station,step,limitDown,limitUp,unit,value,status,finalStatus,consumeTime,totalConsumeTime,time) VALUES
('{snr}','{socket.Model}','','{data.Name}','{data.LimitDown}','{data.LimitUp}','{data.Unit}','{data.Value}','{data.Result}','{socket.Status}','{data.ConsumeTime}','{socket.ElapseTime}','{time}')");
                sqls.Add($@"INSERT INTO serialData (sn,model,station,step,position,limitDown,limitUp,unit,value,status,finalStatus,consumeTime,totalConsumeTime,time) VALUES
('{snr}','{model}','','{data.Name}','{index}','{data.LimitDown}','{data.LimitUp}','{data.Unit}','{data.Value}','{data.Result}','{socket.Status}','{data.ConsumeTime}','{socket.ElapseTime}','{time}')");
            }
            sqls.Add($@"INSERT INTO testResult (sn,model,station,result,time,position,status) VALUES 
('{snr}','{socket.Model}','','{socket.Status}','{time}','{index}','{status}')");
('{snr}','{model}','','{socket.Status}','{time}','{index}','{status}')");
            sqliteHelper.ExecuteBatchSql(sqls);
        }
@@ -1272,9 +1244,30 @@
                    this.tsSockets[index].SetTestProcess(ProcessVal);
                    break;
                case 10031://导出CSV
                    if (string.IsNullOrEmpty(strData))
                    {
                        SaveTemplateCSV(index);
                        return;
                    }
                    try
                    {
                    string[] datas = strData.Split(';');
                    SavePositionCSV(index, datas[0]);
                        if (datas.Length >= 2)
                        {
                    SavePositionDB(index, datas[1]);
                        }
                        else
                        {
                            var sss = this.tsSockets[index].Status;
                            SavePositionDB(index, sss);
                        }
                    }
                    catch (Exception ex)
                    {
                        log.Warn("10031 Error.", ex);
                        SaveTemplateCSV(index);
                    }
                    break;
                case 10032://按下标暂停测试
                    this.tsSockets[index].ShowPausePanel();
SimpleUtil/DIOController/DIOController.csproj
@@ -51,12 +51,6 @@
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\..\..\hslCommunicationV12.0.3带源码demo\HslCommunication\HslCommunication.csproj">
      <Project>{aa30059e-7269-415c-9df2-55c310dd5548}</Project>
      <Name>HslCommunication</Name>
    </ProjectReference>
  </ItemGroup>
  <ItemGroup>
    <Content Include="PCI-Dask64.dll">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
SimpleUtil/HMI.Simple.Util/ProductUtil.cs
@@ -16,7 +16,7 @@
            }
            else if (data.Type == "Int")
            {
                context.FileGlobals.SetValInteger64($"DUTSetting.{data.Name}", 1, int.Parse(data.Value));
                context.FileGlobals.SetValNumber($"DUTSetting.{data.Name}", 1, int.Parse(data.Value));
            }
            else if (data.Type == "Double")
            {
SimpleUtil/HMI.Util.Base/AlarmUtil.cs
New file
@@ -0,0 +1,66 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace HMI.Util.Base
{
    public interface AlarmBase
    {
        bool? Alarm(string message);
    }
    public class AlarmUtil
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr GetModuleHandle(string lpModuleName);
        [DllImport("user32.dll")]
        private static extern bool IsWindow(IntPtr hWnd);
        [DllImport("user32.dll")]
        private static extern int GetClassName(IntPtr hWnd, System.Text.StringBuilder lpClassName, int nMaxCount);
        [DllImport("user32.dll")]
        private static extern bool SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
        /// <summary>
        /// 根据窗口标题获取WPF窗口对象
        /// </summary>
        /// <param name="windowTitle">窗口标题</param>
        /// <returns>WPF窗口对象,如果未找到则返回null</returns>
        public static IntPtr GetProcessIdByName(string windowTitle)
        {
            Process[] processes = Process.GetProcesses();
            foreach (Process process in processes)
            {
                if (process.ProcessName == windowTitle)
                {
                    var handle = process.MainWindowHandle;
                    Console.WriteLine(process.MainWindowHandle.ToString());
                    Console.WriteLine(IsWindow(process.MainWindowHandle));
                    var className = new System.Text.StringBuilder(256);
                    GetClassName(handle, className, className.Capacity);
                    Console.WriteLine(className.ToString());
                    Console.WriteLine(SendMessage(handle, 0x12, IntPtr.Zero, IntPtr.Zero));
                    var source = HwndSource.FromHwnd(process.MainWindowHandle);
                    var v = source?.RootVisual;
                    if (v is AlarmBase ab) Console.WriteLine(ab.Alarm("123").ToString());
                    return process.MainWindowHandle;
                }
            }
            return IntPtr.Zero;
        }
    }
}
SimpleUtil/HMI.Util.Base/HMI.Util.Base.csproj
@@ -49,6 +49,8 @@
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
@@ -57,8 +59,10 @@
    <Reference Include="System.Data" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xml" />
    <Reference Include="WindowsBase" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="AlarmUtil.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="RegUtil.cs" />
  </ItemGroup>
VisionBuilder.Engine/Runner.cs
File was deleted
VisionBuilder.Engine/app.config
File was deleted
VisionBuilder.Run/Properties/AssemblyInfo.cs
File was deleted
VisionBuilder.Run/Properties/Resources.Designer.cs
File was deleted
VisionBuilder.Run/Properties/Resources.resx
File was deleted
VisionBuilder.Run/Properties/Settings.Designer.cs
File was deleted
VisionBuilder.Run/Properties/Settings.settings
File was deleted
VisionBuilder.Run/VisionBuilder.Run.csproj
File was deleted
VisionBuilder.Test/PluginOne.cs
File was deleted
VisionBuilder.Test/Properties/AssemblyInfo.cs
File was deleted
VisionBuilder.Test/VisionBuilder.Test.csproj
File was deleted
VisionBuilder.Test/app.config
File was deleted
WPFProject/App.config
@@ -3,13 +3,13 @@
    <configSections>
        <section name="TestResultConfig" type="System.Configuration.SingleTagSectionHandler" />
    </configSections>
    <TestResultConfig Row="2" Column="3" PortStart="6660" PositionColumn="3" PositionCount="18" NameStr="A1,A2,A3,B1,B2,B3" />
    <TestResultConfig Row="2" Column="2" PortStart="6660" PositionColumn="4" PositionCount="20" NameStr="A,B,C,D,E,F,G" CodeParserType="JK01" />
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    </startup>
    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <probing privatePath="Libs;ThirdParty;Plugin;3rds;" />
            <probing privatePath="Libs;ThirdParty;Plugin;3rds;Api;" />
            <dependentAssembly>
                <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
                <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
WPFProject/Config/log4net.config
@@ -31,7 +31,7 @@
    </appender>
    <root>
        <level value="INFO" />
        <level value="DEBUG" />
        <appender-ref ref="Console" />
        <appender-ref ref="RollingFile" />
    </root>
WPFProject/LoadingWindow.xaml
@@ -78,7 +78,7 @@
        <Image Height="25"
       Stretch="Uniform"
   HorizontalAlignment="Left"
   Source="/Resources/JingCeLogo.png"
   Source="/Icon/JingCeLogo.png"
               Margin="0,0,0,10"
   RenderOptions.BitmapScalingMode="HighQuality" />
WPFProject/Login.xaml
@@ -94,7 +94,7 @@
        <Image Height="25"
               Stretch="Uniform"
           HorizontalAlignment="Left"
           Source="/Resources/JingCeLogo.png"
           Source="/Icon/JingCeLogo.png"
           RenderOptions.BitmapScalingMode="HighQuality" />
        <StackPanel Margin="0,50,0,0" Grid.Row="1" x:Name="formPanel" dp:StackPanelHelper.ChildSpace="20" >
            <pu:FormGroup x:Name="FrmAccount"
WPFProject/PanuonWindow.xaml.cs
@@ -13,6 +13,8 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using HMI.Util.Base;
using System.Windows.Interop;
namespace WPFProject
{
@@ -29,7 +31,7 @@
    /// <summary>
    /// PanuonWindow.xaml 的交互逻辑
    /// </summary>
    public partial class PanuonWindow : WindowX, IPluginContainer,IAlarmShow
    public partial class PanuonWindow : WindowX, IPluginContainer,IAlarmShow, AlarmBase
    {
        public PanuonWindow()
        {
@@ -259,6 +261,11 @@
            sb.Loaded -= list_Loaded;
        }
        public bool? Alarm(string message)
        {
            return true;
        }
        //private void BaseWindow_Loaded(object sender, RoutedEventArgs e)
        //{
        //    var s = (Style)this.Resources["WindowButton"];
WPFProject/WPFProject.csproj
@@ -456,10 +456,6 @@
      <Project>{a580de4a-34e7-4efe-ad2c-0871396000e9}</Project>
      <Name>HMI.DataViewer</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.IOMonitor\HMI.IOMonitor.csproj">
      <Project>{f7663c79-7047-42f3-b129-73a2575ee7c6}</Project>
      <Name>HMI.IOMonitor</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.LogViewer\HMI.LogViewer.csproj">
      <Project>{20c23602-b029-4e21-8c11-b9e5f2223237}</Project>
      <Name>HMI.LogViewer</Name>
@@ -484,9 +480,9 @@
      <Project>{de4205c6-5923-46fd-a4b9-65169bf61c75}</Project>
      <Name>HMI.MenuManage</Name>
    </ProjectReference>
    <ProjectReference Include="..\Plugin.ChangeModel\Plugin.ChangeModel.csproj">
      <Project>{ef5972b1-dd20-404d-b2b3-37e70e2a478a}</Project>
      <Name>Plugin.ChangeModel</Name>
    <ProjectReference Include="..\Plugin.CurrentSensorControl\Plugin.CurrentSensorControl.csproj">
      <Project>{e4505d51-248b-4c21-b0e3-a4b2a7551d6a}</Project>
      <Name>Plugin.CurrentSensorControl</Name>
    </ProjectReference>
    <ProjectReference Include="..\Plugin.DataBoard\Plugin.DataBoard.csproj">
      <Project>{be42ab26-3270-43e1-84b8-6929d862ff8d}</Project>
@@ -504,10 +500,6 @@
      <Project>{968fe602-22d6-42bd-981c-601ad518a896}</Project>
      <Name>Plugin.JarUI</Name>
    </ProjectReference>
    <ProjectReference Include="..\Plugin.MaterialOpt\Plugin.MaterialOpt.csproj">
      <Project>{e1f90cbd-a0d4-48da-ab25-7f490935aa4a}</Project>
      <Name>Plugin.MaterialOpt</Name>
    </ProjectReference>
    <ProjectReference Include="..\Plugin.ModelManage\Plugin.ModelManage.csproj">
      <Project>{76ce4d21-702c-416f-95b4-f4f4c5b2d162}</Project>
      <Name>Plugin.ModelManage</Name>
@@ -515,10 +507,6 @@
    <ProjectReference Include="..\Plugin.SeqEditor\Plugin.SeqEditor.csproj">
      <Project>{717fd815-ff95-4f30-b4d9-9cea978ca40e}</Project>
      <Name>Plugin.SeqEditor</Name>
    </ProjectReference>
    <ProjectReference Include="..\Plugin.ServoManage\Plugin.ServoManage.csproj">
      <Project>{1853e00f-6865-4bc6-974b-6a5e1c00bafe}</Project>
      <Name>Plugin.ServoManage</Name>
    </ProjectReference>
    <ProjectReference Include="..\Plugin.TestResult\Plugin.TestResult.csproj">
      <Project>{7375b74c-b6bf-4814-9838-e1caacad1897}</Project>
@@ -539,6 +527,10 @@
    <ProjectReference Include="..\SimpleUtil\HMI.Simple.Util\HMI.Simple.Util.csproj">
      <Project>{cf5bebe9-2803-49c3-b773-73d082e0984a}</Project>
      <Name>HMI.Simple.Util</Name>
    </ProjectReference>
    <ProjectReference Include="..\SimpleUtil\HMI.Util.Base\HMI.Util.Base.csproj">
      <Project>{d59e25d1-9b27-46a1-8438-92661dd23fae}</Project>
      <Name>HMI.Util.Base</Name>
    </ProjectReference>
  </ItemGroup>
  <ItemGroup>
@@ -568,6 +560,7 @@
move /y "$(TargeDir)HMI.UI.dll" $(TargeDir)Libs\HMI.UI.dll"
move /y "$(TargeDir)HMI.Util.dll" $(TargeDir)Libs\HMI.Util.dll"
move /y "$(TargeDir)ConnectionUtil.dll" $(TargeDir)Libs\ConnectionUtil.dll"
move /y "$(TargeDir)HMI.StdControl.dll" $(TargeDir)Libs\HMI.StdControl.dll"
move /y "$(TargeDir)HMI.LogUtil.dll" $(TargeDir)Api\HMI.LogUtil.dll"
move /y "$(TargeDir)HMI.SerialData.Util.dll" $(TargeDir)Api\HMI.SerialData.Util.dll"
@@ -593,6 +586,7 @@
move /y "$(TargeDir)Plugin.MaterialOpt.dll" $(TargeDir)Plugin\Plugin.MaterialOpt.dll"
move /y "$(TargeDir)Plugin.DODriverControl.dll" $(TargeDir)Plugin\Plugin.DODriverControl.dll"
move /y "$(TargeDir)Plugin.ModelManage.dll" $(TargeDir)Plugin\Plugin.ModelManage.dll"
move /y "$(TargeDir)Plugin.CurrentSensorControl.dll" $(TargeDir)Plugin\Plugin.CurrentSensorControl.dll"
setlocal enabledelayedexpansion
WPFProject/WPFProject.sln
@@ -7,15 +7,9 @@
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.Core", "..\HMI.Core\HMI.Core.csproj", "{3E3BF44A-0B69-45C5-9512-528CB48A7106}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisionBuilder.Test", "..\VisionBuilder.Test\VisionBuilder.Test.csproj", "{6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisionBuilder.Engine", "..\VisionBuilder.Engine\VisionBuilder.Engine.csproj", "{8E5E8A68-0B9D-4A6A-B325-730C64603FDF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.UI", "..\HMI.UI\HMI.UI.csproj", "{F17DE6B5-AF1D-48C6-94A0-6B763A6546A6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.MenuManage", "..\HMI.MenuManage\HMI.MenuManage.csproj", "{DE4205C6-5923-46FD-A4B9-65169BF61C75}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisionBuilder.Run", "..\VisionBuilder.Run\VisionBuilder.Run.csproj", "{7C299AED-88CC-437E-A68B-1550AA72E6B0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.UnitTest", "..\HMI.UnitTest\HMI.UnitTest.csproj", "{325A7F52-FE51-44F0-B3AC-5F42F5356605}"
EndProject
@@ -27,8 +21,6 @@
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.DataViewer", "..\HMI.DataViewer\HMI.DataViewer.csproj", "{A580DE4A-34E7-4EFE-AD2C-0871396000E9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.IOMonitor", "..\HMI.IOMonitor\HMI.IOMonitor.csproj", "{F7663C79-7047-42F3-B129-73A2575EE7C6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.ParameterManage", "..\HMI.ParameterManage\HMI.ParameterManage.csproj", "{736CD77D-B81E-4866-BB00-84B0B57110F0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.LogViewer", "..\HMI.LogViewer\HMI.LogViewer.csproj", "{20C23602-B029-4E21-8C11-B9E5F2223237}"
@@ -39,15 +31,11 @@
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.Util", "..\HMI.Util\HMI.Util.csproj", "{AFEA100A-9223-448D-AB10-3015D93DD98C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.ServoManage", "..\Plugin.ServoManage\Plugin.ServoManage.csproj", "{1853E00F-6865-4BC6-974B-6A5E1C00BAFE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.DOControl", "..\Plugin.DOControl\Plugin.DOControl.csproj", "{306A83DC-A7DB-4057-BAA7-477D8756F5D9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConnectionUtil", "..\..\ConnectionUtil\ConnectionUtil\ConnectionUtil.csproj", "{D0806FA6-3F6D-4304-81B8-EAD9DA7CB5B2}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConnectionUtil", "..\ConnectionUtil\ConnectionUtil.csproj", "{D0806FA6-3F6D-4304-81B8-EAD9DA7CB5B2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OPCClient", "..\OPCClient\OPCClient.csproj", "{E5740BB5-9FC6-4A20-B7C2-CC90C281ED05}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.TestBuilder", "..\Plugin.TestBuilder\Plugin.TestBuilder.csproj", "{30201EC1-9A82-48DB-94B9-BD5C1190228B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.DataBoard", "..\Plugin.DataBoard\Plugin.DataBoard.csproj", "{BE42AB26-3270-43E1-84B8-6929D862FF8D}"
EndProject
@@ -65,17 +53,9 @@
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.SeqEditor", "..\Plugin.SeqEditor\Plugin.SeqEditor.csproj", "{717FD815-FF95-4F30-B4D9-9CEA978CA40E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.ChangeModel", "..\Plugin.ChangeModel\Plugin.ChangeModel.csproj", "{EF5972B1-DD20-404D-B2B3-37E70E2A478A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "lmes", "..\..\MesApi\lmes.csproj", "{376CA791-49D8-B4D7-6F98-6E86BD46CAEF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.MaterialOpt", "..\Plugin.MaterialOpt\Plugin.MaterialOpt.csproj", "{E1F90CBD-A0D4-48DA-AB25-7F490935AA4A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.MQUtil", "..\SimpleUtil\HMI.MQUtil\HMI.MQUtil.csproj", "{F8B8B6B6-821B-4FF6-B556-9CAB840EA57A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.DODriverControl", "..\Plugin.DODriverControl\Plugin.DODriverControl.csproj", "{61DF853D-829A-4426-BEF8-0135586005B6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HslCommunication", "..\..\hslCommunicationV12.0.3带源码demo\HslCommunication\HslCommunication.csproj", "{AA30059E-7269-415C-9DF2-55C310DD5548}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.TsSupport", "..\Plugin.TsSupport\Plugin.TsSupport.csproj", "{3068124E-F12B-473E-99A0-2DA5A0385D05}"
EndProject
@@ -84,6 +64,12 @@
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DIOController", "..\SimpleUtil\DIOController\DIOController.csproj", "{34FFBAFE-478F-4B6E-95E7-92A949011A9B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleTest", "..\ConsoleTest\ConsoleTest.csproj", "{2BDDB984-199B-4913-96BB-39C252E101F2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.CurrentSensorControl", "..\Plugin.CurrentSensorControl\Plugin.CurrentSensorControl.csproj", "{E4505D51-248B-4C21-B0E3-A4B2A7551D6A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModBusTcpController", "..\ModBusTcpController\ModBusTcpController.csproj", "{49DB0C9B-B239-4608-A91E-F5ED43AFAF96}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.StdControl", "..\HMI.StdControl\HMI.StdControl.csproj", "{7A066856-182F-4435-B648-E9FB91AFF657}"
EndProject
Global
    GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -119,30 +105,6 @@
        {3E3BF44A-0B69-45C5-9512-528CB48A7106}.Release|x64.Build.0 = Release|x64
        {3E3BF44A-0B69-45C5-9512-528CB48A7106}.Release|x86.ActiveCfg = Release|x86
        {3E3BF44A-0B69-45C5-9512-528CB48A7106}.Release|x86.Build.0 = Release|x86
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Debug|x64.ActiveCfg = Debug|x64
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Debug|x64.Build.0 = Debug|x64
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Debug|x86.ActiveCfg = Debug|Any CPU
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Debug|x86.Build.0 = Debug|Any CPU
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Release|Any CPU.Build.0 = Release|Any CPU
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Release|x64.ActiveCfg = Release|x64
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Release|x64.Build.0 = Release|x64
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Release|x86.ActiveCfg = Release|Any CPU
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Release|x86.Build.0 = Release|Any CPU
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Debug|x64.ActiveCfg = Debug|x64
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Debug|x64.Build.0 = Debug|x64
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Debug|x86.ActiveCfg = Debug|Any CPU
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Debug|x86.Build.0 = Debug|Any CPU
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Release|Any CPU.Build.0 = Release|Any CPU
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Release|x64.ActiveCfg = Release|x64
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Release|x64.Build.0 = Release|x64
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Release|x86.ActiveCfg = Release|Any CPU
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Release|x86.Build.0 = Release|Any CPU
        {F17DE6B5-AF1D-48C6-94A0-6B763A6546A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {F17DE6B5-AF1D-48C6-94A0-6B763A6546A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {F17DE6B5-AF1D-48C6-94A0-6B763A6546A6}.Debug|x64.ActiveCfg = Debug|x64
@@ -167,18 +129,6 @@
        {DE4205C6-5923-46FD-A4B9-65169BF61C75}.Release|x64.Build.0 = Release|Any CPU
        {DE4205C6-5923-46FD-A4B9-65169BF61C75}.Release|x86.ActiveCfg = Release|Any CPU
        {DE4205C6-5923-46FD-A4B9-65169BF61C75}.Release|x86.Build.0 = Release|Any CPU
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Debug|x64.ActiveCfg = Debug|x64
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Debug|x64.Build.0 = Debug|x64
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Debug|x86.ActiveCfg = Debug|Any CPU
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Debug|x86.Build.0 = Debug|Any CPU
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Release|Any CPU.Build.0 = Release|Any CPU
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Release|x64.ActiveCfg = Release|Any CPU
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Release|x64.Build.0 = Release|Any CPU
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Release|x86.ActiveCfg = Release|Any CPU
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Release|x86.Build.0 = Release|Any CPU
        {325A7F52-FE51-44F0-B3AC-5F42F5356605}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {325A7F52-FE51-44F0-B3AC-5F42F5356605}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {325A7F52-FE51-44F0-B3AC-5F42F5356605}.Debug|x64.ActiveCfg = Debug|x64
@@ -239,18 +189,6 @@
        {A580DE4A-34E7-4EFE-AD2C-0871396000E9}.Release|x64.Build.0 = Release|Any CPU
        {A580DE4A-34E7-4EFE-AD2C-0871396000E9}.Release|x86.ActiveCfg = Release|Any CPU
        {A580DE4A-34E7-4EFE-AD2C-0871396000E9}.Release|x86.Build.0 = Release|Any CPU
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Debug|x64.ActiveCfg = Debug|x64
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Debug|x64.Build.0 = Debug|x64
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Debug|x86.ActiveCfg = Debug|Any CPU
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Debug|x86.Build.0 = Debug|Any CPU
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Release|Any CPU.Build.0 = Release|Any CPU
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Release|x64.ActiveCfg = Release|Any CPU
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Release|x64.Build.0 = Release|Any CPU
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Release|x86.ActiveCfg = Release|Any CPU
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Release|x86.Build.0 = Release|Any CPU
        {736CD77D-B81E-4866-BB00-84B0B57110F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {736CD77D-B81E-4866-BB00-84B0B57110F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {736CD77D-B81E-4866-BB00-84B0B57110F0}.Debug|x64.ActiveCfg = Debug|x64
@@ -311,18 +249,6 @@
        {AFEA100A-9223-448D-AB10-3015D93DD98C}.Release|x64.Build.0 = Release|Any CPU
        {AFEA100A-9223-448D-AB10-3015D93DD98C}.Release|x86.ActiveCfg = Release|Any CPU
        {AFEA100A-9223-448D-AB10-3015D93DD98C}.Release|x86.Build.0 = Release|Any CPU
        {1853E00F-6865-4BC6-974B-6A5E1C00BAFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {1853E00F-6865-4BC6-974B-6A5E1C00BAFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {1853E00F-6865-4BC6-974B-6A5E1C00BAFE}.Debug|x64.ActiveCfg = Debug|x64
        {1853E00F-6865-4BC6-974B-6A5E1C00BAFE}.Debug|x64.Build.0 = Debug|x64
        {1853E00F-6865-4BC6-974B-6A5E1C00BAFE}.Debug|x86.ActiveCfg = Debug|Any CPU
        {1853E00F-6865-4BC6-974B-6A5E1C00BAFE}.Debug|x86.Build.0 = Debug|Any CPU
        {1853E00F-6865-4BC6-974B-6A5E1C00BAFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {1853E00F-6865-4BC6-974B-6A5E1C00BAFE}.Release|Any CPU.Build.0 = Release|Any CPU
        {1853E00F-6865-4BC6-974B-6A5E1C00BAFE}.Release|x64.ActiveCfg = Release|Any CPU
        {1853E00F-6865-4BC6-974B-6A5E1C00BAFE}.Release|x64.Build.0 = Release|Any CPU
        {1853E00F-6865-4BC6-974B-6A5E1C00BAFE}.Release|x86.ActiveCfg = Release|Any CPU
        {1853E00F-6865-4BC6-974B-6A5E1C00BAFE}.Release|x86.Build.0 = Release|Any CPU
        {306A83DC-A7DB-4057-BAA7-477D8756F5D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {306A83DC-A7DB-4057-BAA7-477D8756F5D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {306A83DC-A7DB-4057-BAA7-477D8756F5D9}.Debug|x64.ActiveCfg = Debug|x64
@@ -359,18 +285,6 @@
        {E5740BB5-9FC6-4A20-B7C2-CC90C281ED05}.Release|x64.Build.0 = Release|Any CPU
        {E5740BB5-9FC6-4A20-B7C2-CC90C281ED05}.Release|x86.ActiveCfg = Release|Any CPU
        {E5740BB5-9FC6-4A20-B7C2-CC90C281ED05}.Release|x86.Build.0 = Release|Any CPU
        {30201EC1-9A82-48DB-94B9-BD5C1190228B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {30201EC1-9A82-48DB-94B9-BD5C1190228B}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {30201EC1-9A82-48DB-94B9-BD5C1190228B}.Debug|x64.ActiveCfg = Debug|x64
        {30201EC1-9A82-48DB-94B9-BD5C1190228B}.Debug|x64.Build.0 = Debug|x64
        {30201EC1-9A82-48DB-94B9-BD5C1190228B}.Debug|x86.ActiveCfg = Debug|Any CPU
        {30201EC1-9A82-48DB-94B9-BD5C1190228B}.Debug|x86.Build.0 = Debug|Any CPU
        {30201EC1-9A82-48DB-94B9-BD5C1190228B}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {30201EC1-9A82-48DB-94B9-BD5C1190228B}.Release|Any CPU.Build.0 = Release|Any CPU
        {30201EC1-9A82-48DB-94B9-BD5C1190228B}.Release|x64.ActiveCfg = Release|Any CPU
        {30201EC1-9A82-48DB-94B9-BD5C1190228B}.Release|x64.Build.0 = Release|Any CPU
        {30201EC1-9A82-48DB-94B9-BD5C1190228B}.Release|x86.ActiveCfg = Release|Any CPU
        {30201EC1-9A82-48DB-94B9-BD5C1190228B}.Release|x86.Build.0 = Release|Any CPU
        {BE42AB26-3270-43E1-84B8-6929D862FF8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {BE42AB26-3270-43E1-84B8-6929D862FF8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {BE42AB26-3270-43E1-84B8-6929D862FF8D}.Debug|x64.ActiveCfg = Debug|x64
@@ -455,42 +369,6 @@
        {717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Release|x64.Build.0 = Release|x64
        {717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Release|x86.ActiveCfg = Release|x86
        {717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Release|x86.Build.0 = Release|x86
        {EF5972B1-DD20-404D-B2B3-37E70E2A478A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {EF5972B1-DD20-404D-B2B3-37E70E2A478A}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {EF5972B1-DD20-404D-B2B3-37E70E2A478A}.Debug|x64.ActiveCfg = Debug|x64
        {EF5972B1-DD20-404D-B2B3-37E70E2A478A}.Debug|x64.Build.0 = Debug|x64
        {EF5972B1-DD20-404D-B2B3-37E70E2A478A}.Debug|x86.ActiveCfg = Debug|Any CPU
        {EF5972B1-DD20-404D-B2B3-37E70E2A478A}.Debug|x86.Build.0 = Debug|Any CPU
        {EF5972B1-DD20-404D-B2B3-37E70E2A478A}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {EF5972B1-DD20-404D-B2B3-37E70E2A478A}.Release|Any CPU.Build.0 = Release|Any CPU
        {EF5972B1-DD20-404D-B2B3-37E70E2A478A}.Release|x64.ActiveCfg = Release|Any CPU
        {EF5972B1-DD20-404D-B2B3-37E70E2A478A}.Release|x64.Build.0 = Release|Any CPU
        {EF5972B1-DD20-404D-B2B3-37E70E2A478A}.Release|x86.ActiveCfg = Release|Any CPU
        {EF5972B1-DD20-404D-B2B3-37E70E2A478A}.Release|x86.Build.0 = Release|Any CPU
        {376CA791-49D8-B4D7-6F98-6E86BD46CAEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {376CA791-49D8-B4D7-6F98-6E86BD46CAEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {376CA791-49D8-B4D7-6F98-6E86BD46CAEF}.Debug|x64.ActiveCfg = Debug|Any CPU
        {376CA791-49D8-B4D7-6F98-6E86BD46CAEF}.Debug|x64.Build.0 = Debug|Any CPU
        {376CA791-49D8-B4D7-6F98-6E86BD46CAEF}.Debug|x86.ActiveCfg = Debug|Any CPU
        {376CA791-49D8-B4D7-6F98-6E86BD46CAEF}.Debug|x86.Build.0 = Debug|Any CPU
        {376CA791-49D8-B4D7-6F98-6E86BD46CAEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {376CA791-49D8-B4D7-6F98-6E86BD46CAEF}.Release|Any CPU.Build.0 = Release|Any CPU
        {376CA791-49D8-B4D7-6F98-6E86BD46CAEF}.Release|x64.ActiveCfg = Release|Any CPU
        {376CA791-49D8-B4D7-6F98-6E86BD46CAEF}.Release|x64.Build.0 = Release|Any CPU
        {376CA791-49D8-B4D7-6F98-6E86BD46CAEF}.Release|x86.ActiveCfg = Release|Any CPU
        {376CA791-49D8-B4D7-6F98-6E86BD46CAEF}.Release|x86.Build.0 = Release|Any CPU
        {E1F90CBD-A0D4-48DA-AB25-7F490935AA4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {E1F90CBD-A0D4-48DA-AB25-7F490935AA4A}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {E1F90CBD-A0D4-48DA-AB25-7F490935AA4A}.Debug|x64.ActiveCfg = Debug|x64
        {E1F90CBD-A0D4-48DA-AB25-7F490935AA4A}.Debug|x64.Build.0 = Debug|x64
        {E1F90CBD-A0D4-48DA-AB25-7F490935AA4A}.Debug|x86.ActiveCfg = Debug|Any CPU
        {E1F90CBD-A0D4-48DA-AB25-7F490935AA4A}.Debug|x86.Build.0 = Debug|Any CPU
        {E1F90CBD-A0D4-48DA-AB25-7F490935AA4A}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {E1F90CBD-A0D4-48DA-AB25-7F490935AA4A}.Release|Any CPU.Build.0 = Release|Any CPU
        {E1F90CBD-A0D4-48DA-AB25-7F490935AA4A}.Release|x64.ActiveCfg = Release|Any CPU
        {E1F90CBD-A0D4-48DA-AB25-7F490935AA4A}.Release|x64.Build.0 = Release|Any CPU
        {E1F90CBD-A0D4-48DA-AB25-7F490935AA4A}.Release|x86.ActiveCfg = Release|Any CPU
        {E1F90CBD-A0D4-48DA-AB25-7F490935AA4A}.Release|x86.Build.0 = Release|Any CPU
        {F8B8B6B6-821B-4FF6-B556-9CAB840EA57A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {F8B8B6B6-821B-4FF6-B556-9CAB840EA57A}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {F8B8B6B6-821B-4FF6-B556-9CAB840EA57A}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -515,18 +393,6 @@
        {61DF853D-829A-4426-BEF8-0135586005B6}.Release|x64.Build.0 = Release|Any CPU
        {61DF853D-829A-4426-BEF8-0135586005B6}.Release|x86.ActiveCfg = Release|Any CPU
        {61DF853D-829A-4426-BEF8-0135586005B6}.Release|x86.Build.0 = Release|Any CPU
        {AA30059E-7269-415C-9DF2-55C310DD5548}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {AA30059E-7269-415C-9DF2-55C310DD5548}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {AA30059E-7269-415C-9DF2-55C310DD5548}.Debug|x64.ActiveCfg = Debug|Any CPU
        {AA30059E-7269-415C-9DF2-55C310DD5548}.Debug|x64.Build.0 = Debug|Any CPU
        {AA30059E-7269-415C-9DF2-55C310DD5548}.Debug|x86.ActiveCfg = Debug|Any CPU
        {AA30059E-7269-415C-9DF2-55C310DD5548}.Debug|x86.Build.0 = Debug|Any CPU
        {AA30059E-7269-415C-9DF2-55C310DD5548}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {AA30059E-7269-415C-9DF2-55C310DD5548}.Release|Any CPU.Build.0 = Release|Any CPU
        {AA30059E-7269-415C-9DF2-55C310DD5548}.Release|x64.ActiveCfg = Release|Any CPU
        {AA30059E-7269-415C-9DF2-55C310DD5548}.Release|x64.Build.0 = Release|Any CPU
        {AA30059E-7269-415C-9DF2-55C310DD5548}.Release|x86.ActiveCfg = Release|Any CPU
        {AA30059E-7269-415C-9DF2-55C310DD5548}.Release|x86.Build.0 = Release|Any CPU
        {3068124E-F12B-473E-99A0-2DA5A0385D05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {3068124E-F12B-473E-99A0-2DA5A0385D05}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {3068124E-F12B-473E-99A0-2DA5A0385D05}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -565,8 +431,8 @@
        {34FFBAFE-478F-4B6E-95E7-92A949011A9B}.Release|x86.Build.0 = Release|Any CPU
        {2BDDB984-199B-4913-96BB-39C252E101F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {2BDDB984-199B-4913-96BB-39C252E101F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {2BDDB984-199B-4913-96BB-39C252E101F2}.Debug|x64.ActiveCfg = Debug|Any CPU
        {2BDDB984-199B-4913-96BB-39C252E101F2}.Debug|x64.Build.0 = Debug|Any CPU
        {2BDDB984-199B-4913-96BB-39C252E101F2}.Debug|x64.ActiveCfg = Debug|x64
        {2BDDB984-199B-4913-96BB-39C252E101F2}.Debug|x64.Build.0 = Debug|x64
        {2BDDB984-199B-4913-96BB-39C252E101F2}.Debug|x86.ActiveCfg = Debug|Any CPU
        {2BDDB984-199B-4913-96BB-39C252E101F2}.Debug|x86.Build.0 = Debug|Any CPU
        {2BDDB984-199B-4913-96BB-39C252E101F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -575,6 +441,42 @@
        {2BDDB984-199B-4913-96BB-39C252E101F2}.Release|x64.Build.0 = Release|Any CPU
        {2BDDB984-199B-4913-96BB-39C252E101F2}.Release|x86.ActiveCfg = Release|Any CPU
        {2BDDB984-199B-4913-96BB-39C252E101F2}.Release|x86.Build.0 = Release|Any CPU
        {E4505D51-248B-4C21-B0E3-A4B2A7551D6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {E4505D51-248B-4C21-B0E3-A4B2A7551D6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {E4505D51-248B-4C21-B0E3-A4B2A7551D6A}.Debug|x64.ActiveCfg = Debug|Any CPU
        {E4505D51-248B-4C21-B0E3-A4B2A7551D6A}.Debug|x64.Build.0 = Debug|Any CPU
        {E4505D51-248B-4C21-B0E3-A4B2A7551D6A}.Debug|x86.ActiveCfg = Debug|Any CPU
        {E4505D51-248B-4C21-B0E3-A4B2A7551D6A}.Debug|x86.Build.0 = Debug|Any CPU
        {E4505D51-248B-4C21-B0E3-A4B2A7551D6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {E4505D51-248B-4C21-B0E3-A4B2A7551D6A}.Release|Any CPU.Build.0 = Release|Any CPU
        {E4505D51-248B-4C21-B0E3-A4B2A7551D6A}.Release|x64.ActiveCfg = Release|Any CPU
        {E4505D51-248B-4C21-B0E3-A4B2A7551D6A}.Release|x64.Build.0 = Release|Any CPU
        {E4505D51-248B-4C21-B0E3-A4B2A7551D6A}.Release|x86.ActiveCfg = Release|Any CPU
        {E4505D51-248B-4C21-B0E3-A4B2A7551D6A}.Release|x86.Build.0 = Release|Any CPU
        {49DB0C9B-B239-4608-A91E-F5ED43AFAF96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {49DB0C9B-B239-4608-A91E-F5ED43AFAF96}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {49DB0C9B-B239-4608-A91E-F5ED43AFAF96}.Debug|x64.ActiveCfg = Debug|Any CPU
        {49DB0C9B-B239-4608-A91E-F5ED43AFAF96}.Debug|x64.Build.0 = Debug|Any CPU
        {49DB0C9B-B239-4608-A91E-F5ED43AFAF96}.Debug|x86.ActiveCfg = Debug|Any CPU
        {49DB0C9B-B239-4608-A91E-F5ED43AFAF96}.Debug|x86.Build.0 = Debug|Any CPU
        {49DB0C9B-B239-4608-A91E-F5ED43AFAF96}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {49DB0C9B-B239-4608-A91E-F5ED43AFAF96}.Release|Any CPU.Build.0 = Release|Any CPU
        {49DB0C9B-B239-4608-A91E-F5ED43AFAF96}.Release|x64.ActiveCfg = Release|Any CPU
        {49DB0C9B-B239-4608-A91E-F5ED43AFAF96}.Release|x64.Build.0 = Release|Any CPU
        {49DB0C9B-B239-4608-A91E-F5ED43AFAF96}.Release|x86.ActiveCfg = Release|Any CPU
        {49DB0C9B-B239-4608-A91E-F5ED43AFAF96}.Release|x86.Build.0 = Release|Any CPU
        {7A066856-182F-4435-B648-E9FB91AFF657}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {7A066856-182F-4435-B648-E9FB91AFF657}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {7A066856-182F-4435-B648-E9FB91AFF657}.Debug|x64.ActiveCfg = Debug|Any CPU
        {7A066856-182F-4435-B648-E9FB91AFF657}.Debug|x64.Build.0 = Debug|Any CPU
        {7A066856-182F-4435-B648-E9FB91AFF657}.Debug|x86.ActiveCfg = Debug|Any CPU
        {7A066856-182F-4435-B648-E9FB91AFF657}.Debug|x86.Build.0 = Debug|Any CPU
        {7A066856-182F-4435-B648-E9FB91AFF657}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {7A066856-182F-4435-B648-E9FB91AFF657}.Release|Any CPU.Build.0 = Release|Any CPU
        {7A066856-182F-4435-B648-E9FB91AFF657}.Release|x64.ActiveCfg = Release|Any CPU
        {7A066856-182F-4435-B648-E9FB91AFF657}.Release|x64.Build.0 = Release|Any CPU
        {7A066856-182F-4435-B648-E9FB91AFF657}.Release|x86.ActiveCfg = Release|Any CPU
        {7A066856-182F-4435-B648-E9FB91AFF657}.Release|x86.Build.0 = Release|Any CPU
    EndGlobalSection
    GlobalSection(SolutionProperties) = preSolution
        HideSolutionNode = FALSE