alielie
2025-03-03 1fca2167068e9ef2c079ec742f20313fc6697ab1
完善代码;
63个文件已修改
28个文件已添加
3 文件已重命名
4个文件已删除
4904 ■■■■■ 已修改文件
HMI.AlermInfoManage/AlarmInfoManage.xaml 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/AlarmInfoManage.xaml.cs 405 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/AlarmInfoManage_Setting.xaml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/AlarmInfoManage_Setting.xaml.cs 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/AlarmSettingList.xaml 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/AlarmSettingList.xaml.cs 215 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/AlermInfoManage.xaml.cs 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/AlermInfoManage_Setting.xaml.cs 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/Entity/AimSetting.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/Entity/AlarmSetting.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/Entity/AlarmText.cs 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/HMI.AlarmInfoManage.csproj 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/Modal/AlarmSettingModal.xaml 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/Modal/AlarmSettingModal.xaml.cs 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/Model/UIModel.cs 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/Model/XmlModel.cs 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/Properties/AssemblyInfo.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/Properties/Resources.Designer.cs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/Properties/Settings.Designer.cs 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/packages.config 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Base/DtConverter.cs 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Base/HMI.Base.csproj 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Entity/NotifyBase.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Entity/SelectBase.cs 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/HMI.Core.csproj 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Setting/SystemSetting.cs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Util/HexUtil.cs 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Util/MSMQHelper.cs 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Util/SqliteHelper.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Util/XmlHelper.cs 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.DataViewer/DataViewer.xaml 287 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.DataViewer/DataViewer.xaml.cs 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.DataViewer/DbSettingModal.xaml 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.DataViewer/DbSettingModal.xaml.cs 248 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.DataViewer/Entity/DataBase_Setting.cs 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.DataViewer/Entity/QueryCondition.cs 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.DataViewer/Entity/QueryData.cs 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.DataViewer/HMI.DataViewer.csproj 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/Entity/DIO.cs 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/Entity/DioData.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/Entity/IPManage.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/HMI.IOMonitor.csproj 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/IOMonitor.xaml 308 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/IOMonitor.xaml.cs 477 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/IPManageModal.xaml 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/IPManageModal.xaml.cs 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/packages.config 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/MenuManage.xaml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/MenuManage.xaml.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Modal/MenuModal.xaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Modal/RoleModal.xaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Modal/UserModal.xaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Model/MenuManageModel.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Model/RoleManageModel.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Model/UserManageModel.cs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/RoleManage.xaml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/RoleManage.xaml.cs 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/UserManage.xaml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/UserManage.xaml.cs 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/ParameterManage.xaml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/ParameterModal.xaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/ProductModal.xaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.SettingControl/SettingControl.xaml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.SettingControl/SettingControl.xaml.cs 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.SettingControl/SystemConfig.xaml 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.SettingControl/SystemConfig.xaml.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/DpUtil/FormGroupHelper.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/DpUtil/GridHelper.cs 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/DpUtil/StackPanelHelper.cs 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/DpUtil/TreeViewHelper.cs 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/HMI.UI.csproj 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Resources/Font/iconfont.ttf 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/BaseWindow.xaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/Colors.xaml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/FormStyle.xaml 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/MsgBoxStyle.xaml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/PanuonResource.xaml 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/WindowResources.xaml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Util/MsgBoxHelper.cs 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Util/ToastHelper.cs 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/MainWindow.xaml.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Libs/OpcInterface.dll 补丁 | 查看 | 原始文档 | blame | 历史
Libs/SimensDriver.dll 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/App.config 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/App.xaml.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Controls/MainTabControl.xaml 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Controls/MinInnerWindow.xaml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Libs/SQLite.Interop.dll 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/LoadingWindow.xaml.cs 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/MainWindow.xaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/PanuonWindow.xaml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/PanuonWindow.xaml.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/SQLite.Interop.dll 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/TestWindow.xaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Utils/MainToastHandler.cs 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/WPFProject.csproj 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/WPFProject.sln 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/test.xml 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/AlarmInfoManage.xaml
File was renamed from HMI.AlermInfoManage/AlermInfoManage.xaml
@@ -1,10 +1,10 @@
<hb:PluginControl x:Class="HMI.AlermInfoManage.AlermInfoManage"
<hb:PluginControl x:Class="HMI.AlarmInfoManage.AlarmInfoManage"
             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:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
             xmlns:local="clr-namespace:HMI.AlermInfoManage"
             xmlns:local="clr-namespace:HMI.AlarmInfoManage"
             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
             xmlns:pw="https://opensource.panuon.com/wpf"
             xmlns:pu="https://opensource.panuon.com/wpf-ui"
@@ -29,18 +29,24 @@
    <Border Style="{StaticResource CardBoarder}">
        <StackPanel>
            <DockPanel Margin="0,0,0,10">
                <Button DockPanel.Dock="Right" pu:ButtonHelper.Icon="&#xe934;" Style="{StaticResource DangerButton}">删除</Button>
                <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
pu:IconHelper.FontFamily="{StaticResource iconfont}"
pu:ButtonHelper.Icon="&#xe631;" Margin="0,0,10,0" x:Name="refreshBtn" Click="Button_Click">刷新</Button>
                <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
            pu:IconHelper.FontFamily="{StaticResource iconfont}"
            pu:ButtonHelper.Icon="&#xe61a;" Margin="0,0,10,0" x:Name="addBtn" Click="Button_Click">新增</Button>
                <StackPanel DockPanel.Dock="Right" Style="{StaticResource ButtonGroup}">
                    <Button Style="{StaticResource PrimaryButton}"
pu:ButtonHelper.Icon="&#xe94d;" x:Name="settingBtn" Click="Button_Click">设置</Button>
                    <Button Style="{StaticResource PrimaryButton}"
pu:ButtonHelper.Icon="&#xe631;" x:Name="refreshBtn" Click="Button_Click">刷新</Button>
                    <Button Style="{StaticResource PrimaryButton}"
pu:ButtonHelper.Icon="&#xe639;" x:Name="importBtn" Click="Button_Click">导入</Button>
                </StackPanel>
                <!--<Button DockPanel.Dock="Right" pu:ButtonHelper.Icon="&#xe934;"
                        Style="{StaticResource DangerButton}">删除</Button>-->
                <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}" 
                           VerticalAlignment="Center" HorizontalAlignment="Left" Text="报警信息列表"/>
            </DockPanel>
            <DataGrid x:Name="table" Style="{StaticResource DefaultDataGrid}" ItemsSource="{Binding DataSource}">
            <DataGrid x:Name="table" Style="{StaticResource DefaultDataGrid}" ItemsSource="{Binding Datasource}">
                <DataGrid.Columns>
                    <DataGridTemplateColumn Header="" Width="Auto" CanUserResize="False">
                        <DataGridTemplateColumn.CellTemplate>
HMI.AlermInfoManage/AlarmInfoManage.xaml.cs
New file
@@ -0,0 +1,405 @@
using HMI.AlarmInfoManage.Entity;
using HMI.AlarmInfoManage.Model;
using HMI.Base;
using HMI.Core.Setting;
using HMI.Core.Util;
using HMI.UI.Util;
using Microsoft.Win32;
using NPOI.SS.Formula.Functions;
using NPOI.XSSF.UserModel;
using Sharp7;
using SimensDriver;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
namespace HMI.AlarmInfoManage
{
    /// <summary>
    /// UserControl1.xaml 的交互逻辑
    /// </summary>
    public partial class AlarmInfoManage : PluginControl
    {
        private string blockPath;
        private string markPath;
        private string textSettingPath;
        private Simens plc = new Simens();
        private XmlUIModel Model;
        private bool isHaveAlarm;
        private bool isNewAlarm;
        private SqliteHelper sqliteHelper;
        private Timer MonitorTimer;
        private Timer PcTimer;
        /// <summary>
        /// pc警告数字信号
        /// </summary>
        private int isPcAlarmnumber = 0;
        /// <summary>
        /// 当前报警信息
        /// </summary>
        private string currentInfo = null;
        /// <summary>
        /// 接收报警的队列
        /// </summary>
        private MSMQHelper alarmQueue = new MSMQHelper("LTHMI_Alarm");
        /// <summary>
        /// 日志生成器
        /// </summary>
        private MSMQHelper loggeralarm = new MSMQHelper("LTHMI_Logger");
        /// <summary>
        /// 刷新报警的队列
        /// </summary>
        private MSMQHelper alarmRefershToMain = new MSMQHelper("LTHMI_Alarm_Rrfresh");
        /// <summary>
        /// 报警
        /// </summary>
        private byte[] alarmCodeBytes;
        /// <summary>
        /// 报警信息缓存
        /// </summary>
        private List<AlarmText> currentAlarmCachePlc = new List<AlarmText>();
        /// <summary>
        /// PLC报警信息缓存
        /// </summary>
        private List<AlarmText> preAlarmCachePlc = new List<AlarmText>();
        /// <summary>
        /// 报警信息缓存
        /// </summary>
        private List<AlarmText> currentAlarmCachePc = new List<AlarmText>();
        /// <summary>
        /// 代码管理
        /// </summary>
        private List<AlarmSetting> alarmSettings = new List<AlarmSetting>();
        // <summary>
        /// 标志位报警管理
        /// </summary>
        private List<AlarmSetting> signAlarmSetting = new List<AlarmSetting>();
        /// <summary>
        /// 报警文本列表
        /// </summary>
        private List<AlarmText> alarmTexts = new List<AlarmText>();
        /// <summary>
        /// 传至主窗体的报警信息
        /// </summary>
        private List<AlarmText> currentAlarmCachePlcToMain = new List<AlarmText>();
        private AlarmText alarm;
        private SystemSetting systemSetting;
        public AlarmInfoManage()
        {
            systemSetting = SystemSetting.Instance;
            InitializeComponent();
        }
        public override void InitFinish()
        {
            Model = new XmlUIModel(BaseFolder);
            DataContext = Model;
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Button btn = sender as Button;
            switch (btn.Name)
            {
                case "settingBtn":
                    OpenSettingWindow();
                    break;
                case "importBtn":
                    ImportText();
                    break;
                default:
                    break;
            }
        }
        private void OpenSettingWindow()
        {
            AlarmSettingList listModal = new AlarmSettingList(blockPath, markPath, log)
            {
                Owner = Window.GetWindow(this)
            };
            listModal.ShowDialog();
        }
        private void ImportText()
        {
            try
            {
                OpenFileDialog openDialog = new OpenFileDialog();
                openDialog.Filter = "Files (*.xlsm)|*.xlsm";
                if (openDialog.ShowDialog() == true)
                {
                    this.ImportExcelAlarmText(openDialog.FileName);
                }
            }
            catch (Exception ex)
            {
                log.Error("AlarmText Import Failed.", ex);
                ToastHelper.Error(Window.GetWindow(this), $"导入失败!{ex.Message}");
            }
        }
        /// <summary>
        /// 导入报警代码文本
        /// </summary>
        /// <param name="excelFile">电子设计表格文件</param>
        private void ImportExcelAlarmText(string excelFile)
        {
            int index = 0;
            try
            {
                if (excelFile.IndexOf(".xlsm") < 0)
                {
                    this.Warning("仅支持xlsm格式文件");
                    return;
                }
                this.alarmTexts.Clear();
                XSSFWorkbook workbook = new XSSFWorkbook(excelFile);
                XSSFSheet sheet = (XSSFSheet)workbook.GetSheet("sheet1");
                XSSFRow row = null;
                for (int i = 2; i < (sheet.LastRowNum + 1); i++)
                {
                    index = i;
                    row = (XSSFRow)sheet.GetRow(i);
                    if (row != null)
                    {
                        AlarmText alarm = new AlarmText();
                        alarm.AlarmCode = row.Cells[0].StringCellValue;
                        alarm.OpNo = row.Cells[1].StringCellValue;
                        alarm.ModuleNo = row.Cells[2].StringCellValue;
                        alarm.ModuleName_CN = row.Cells[3].StringCellValue;
                        alarm.ModuleName_EN = row.Cells[4].StringCellValue;
                        alarm.MAlarmCode = row.Cells[5].StringCellValue;
                        alarm.MAlarmText_CN = row.Cells[6].StringCellValue;
                        alarm.MAlarmText_EN = row.Cells[7].StringCellValue;
                        this.alarmTexts.Add(alarm);
                    }
                }
                if (sheet.LastRowNum > 0)
                {
                    XmlHelper.SerializeToXml(textSettingPath, this.alarmTexts);
                }
                this.Success("导入成功");
            }
            catch (Exception ex)
            {
                log.Error($"Row {index} AlarmText Import Failed.", ex);
                this.Error($"第{index}行导入异常!{ex.Message}");
            }
        }
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            if (!string.IsNullOrEmpty(systemSetting.PlcIp))
            {
                if (this.plc.isConnection)
                {
                    this.plc.Simens_S7_ConnectPLC(systemSetting.PlcIp, 102, 0, 1, string.Empty);
                }
            }
            MonitorTimer = new Timer
            {
                Interval = 100,
            };
            MonitorTimer.Elapsed += MonitorTimer_Elapsed;
            PcTimer = new Timer
            {
                Interval = 100,
            };
            PcTimer.Elapsed += PcTimer_Elapsed; ;
            Loaded -= UserControl_Loaded;
        }
        private void PcTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                PcTimer.Enabled = false;
                this.alarmQueue.Receive(100, out string MessageID, out string MsgStr);
                if (MessageID != string.Empty)
                {
                    log.Info(MsgStr);
                    switch (MessageID)
                    {
                        case "Clear":
                            ////this.currentAlarmCachePc.RemoveAll(alarm => alarm.Source == "PC");
                            this.currentAlarmCachePc.Clear();
                            this.RefershAlarm();
                            break;
                        default:
                            AlarmText aalarm = this.alarmTexts.Find(alarm => alarm.AlarmCode == MsgStr);
                            aalarm.Source = "PC";
                            aalarm.Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                            if ((aalarm != null) && (this.currentAlarmCachePc.Find(alarm => alarm.AlarmCode == aalarm.AlarmCode) == null))
                            {
                                this.currentAlarmCachePc.Add(aalarm);
                                this.RefershAlarm();
                                this.isPcAlarmnumber = this.currentAlarmCachePc.Count;
                            }
                            break;
                    }
                }
            }
            catch (Exception ex)
            {
                log.Error("PcTimer Execute Failed", ex);
            }
            finally
            {
                PcTimer.Enabled = true;
            }
        }
        private void MonitorTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                this.MonitorTimer.Enabled = false;
                isHaveAlarm = false;
                foreach (var item in signAlarmSetting)
                {
                    isHaveAlarm = isHaveAlarm ||
                        this.plc.Simens_S7_ReadBool_DB(item.DBAddress, item.StartAddress, item.Length);
                }
                ////有报警
                if (isHaveAlarm)
                {
                    this.isNewAlarm = false;
                    this.currentAlarmCachePlc.Clear();
                    RefreshDBSetting();
                    for (int i = 0; i < ((int)alarmCodeBytes.Length / 4); i++)
                    {
                        int code = S7.GetDIntAt(alarmCodeBytes, 4 * i);
                        ////有报警
                        if (code != 0)
                        {
                            string acode = string.Format("{0:d10}", code);
                            alarm = this.alarmTexts.Find(item => item.AlarmCode == acode);
                            if (alarm != null)
                            {
                                alarm.Source = "PLC";
                                alarm.Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                                this.currentAlarmCachePlc.Add(alarm);
                                ////有新报警
                                if (this.preAlarmCachePlc.Find(alarm => alarm.AlarmCode == acode) == null)
                                {
                                    this.isNewAlarm = true;
                                    this.SendLogMsg(alarm.ModuleName_CN + alarm.MAlarmText_CN);
                                }
                            }
                        }
                    }
                    ////有新报警和报警减少时刷新报警表
                    if (this.isNewAlarm || (this.currentAlarmCachePlc.Count != this.preAlarmCachePlc.Count))
                    {
                        this.currentAlarmCachePlcToMain = XmlHelper.DeepClone(this.currentAlarmCachePlc);
                        this.RefershAlarm();
                    }
                    this.preAlarmCachePlc.Clear();
                    this.preAlarmCachePlc = XmlHelper.DeepClone(this.currentAlarmCachePlc);
                }
                else
                {
                    if (this.preAlarmCachePlc.Count != 0 || this.currentAlarmCachePlcToMain.Count != 0 || this.currentAlarmCachePlc.Count != 0)
                    {
                        this.preAlarmCachePlc.Clear();
                        this.currentAlarmCachePlcToMain.Clear();
                        this.currentAlarmCachePlc.Clear();
                        this.RefershAlarm();
                    }
                }
            }
            catch (Exception ex)
            {
                log.Error("sdadsa", ex);
            }
            finally
            {
                this.MonitorTimer.Enabled = true;
            }
        }
        /// <summary>
        /// 刷新报警块信息
        /// </summary>
        private void RefreshDBSetting()
        {
            List<byte> bytes = new List<byte>();
            foreach (var item in alarmSettings)
            {
                byte[] itemalarmCodeBytes = this.plc.Simens_S7_ReadBytes_DB("DB", item.DBAddress, item.StartAddress, item.Length);
                bytes.AddRange(itemalarmCodeBytes.ToList());
            }
            alarmCodeBytes = bytes.ToArray();
        }
        /// <summary>
        /// 刷新报警信息
        /// </summary>
        private void RefershAlarm()
        {
            this.alarmRefershToMain.Send("AlarmRefersh", string.Empty);
            foreach (AlarmText alarm in this.currentAlarmCachePlc)
            {
                this.currentInfo = alarm.ModuleName_CN + alarm.MAlarmText_CN;
                Model.Datasource.Add(alarm);
                string sqladd = string.Format("INSERT INTO Alarm (source, alarmcode, alarminfo, time) VALUES( '{0}','{1}','{2}','{3}');",
                    alarm.Source, alarm.AlarmCode, this.currentInfo, alarm.Time);
                sqliteHelper.ExecuteSql(sqladd);
            }
            foreach (AlarmText alarm in this.currentAlarmCachePc)
            {
                Model.Datasource.Add(alarm);
                string sqladd2 = string.Format("INSERT INTO Alarm (source, alarmcode, alarminfo, time) VALUES( '{0}','{1}','{2}','{3}');", alarm.Source, alarm.AlarmCode, this.currentInfo, alarm.Time);
                this.sqliteHelper.ExecuteSql(sqladd2);
            }
        }
        /// <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);
        }
        public override void SetBaseFolder(string folder)
        {
            base.SetBaseFolder(folder);
            blockPath = Path.Combine(BaseFolder, "BlockSetting.xml");
            markPath = Path.Combine(BaseFolder, "MarkSetting.xml");
            textSettingPath = Path.Combine(BaseFolder, "textSetting.xml");
        }
    }
}
HMI.AlermInfoManage/AlarmInfoManage_Setting.xaml
File was renamed from HMI.AlermInfoManage/AlermInfoManage_Setting.xaml
@@ -1,9 +1,9 @@
<UserControl x:Class="HMI.AlermInfoManage.AlermInfoManage_Setting"
<UserControl x:Class="HMI.AlarmInfoManage.AlarmInfoManage_Setting"
             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.AlermInfoManage"
             xmlns:local="clr-namespace:HMI.AlarmInfoManage"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
             Loaded="UserControl_Loaded"
HMI.AlermInfoManage/AlarmInfoManage_Setting.xaml.cs
New file
@@ -0,0 +1,33 @@
using HMI.AlarmInfoManage.Entity;
using HMI.Core.Util;
using System.Windows;
using System.Windows.Controls;
namespace HMI.AlarmInfoManage
{
    /// <summary>
    /// AlarmInfoManage_Setting.xaml 的交互逻辑
    /// </summary>
    public partial class AlarmInfoManage_Setting : UserControl
    {
        public AlarmInfoManage_Setting()
        {
            InitializeComponent();
        }
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            DataContext = AimSetting.Instance.GetModel();
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            AimSettingModel model = (AimSettingModel)DataContext;
            AimSetting newSetting = AimSetting.FromModel(model);
            XmlHelper.SerializeToXml(PathHelper.ConfigFolder + "\\AimSetting.xml", model);
        }
    }
}
HMI.AlermInfoManage/AlarmSettingList.xaml
New file
@@ -0,0 +1,133 @@
<pu:WindowX x:Class="HMI.AlarmInfoManage.AlarmSettingList"
            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:HMI.AlarmInfoManage"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
            xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
            xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
            mc:Ignorable="d"
            Title="{Binding SelectedValue,Mode=OneWay}"
            SizeToContent="Height"
            Width="800"
            Height="600"
            BorderBrush="#3E3E45"
            BorderThickness="1"
            Foreground="Black"
            pu:WindowXCaption.Background="{StaticResource PrimaryColor}"
            pu:WindowXModalDialog.ButtonStyle="{StaticResource ModalButton}"
            pu:WindowXCaption.Foreground="#FFF"
            pu:WindowXModalDialog.Buttons="OK"
            pu:WindowXModalDialog.OKButtonContent="关闭"
            Loaded="WindowX_Loaded"
            WindowStartupLocation="CenterScreen"
            pu:WindowXModalDialog.ButtonPanelMargin="0,30,10,10">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <pu:StyleDictionary Includes="TabControl"/>
                <ResourceDictionary Source="/HMI.UI;component/Resources/Font/Font.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/Colors.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Grid Margin="10">
        <Border BorderThickness="1" BorderBrush="Gray">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition />
                </Grid.RowDefinitions>
                <ListBox ItemsSource="{Binding Items}" SelectedValue="{Binding SelectedValue}" BorderThickness="0">
                    <ListBox.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="Horizontal"/>
                        </ItemsPanelTemplate>
                    </ListBox.ItemsPanel>
                    <ListBox.ItemContainerStyle>
                        <Style TargetType="ListBoxItem">
                            <Setter Property="Margin" Value="0"/>
                            <Setter Property="Padding" Value="0"/>
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="ListBoxItem">
                                        <Border x:Name="border" Background="LightGray"
Padding="10" Margin="0" BorderThickness="0">
                                            <!-- 内容呈现器 -->
                                            <StackPanel>
                                                <ContentPresenter  Content="{TemplateBinding Content}"
                   ContentStringFormat="{TemplateBinding ContentStringFormat}"
                   ContentTemplate="{TemplateBinding ContentTemplate}"  />
                                            </StackPanel>
                                        </Border>
                                        <ControlTemplate.Triggers>
                                            <Trigger Property="IsSelected" Value="True">
                                                <Setter TargetName="border" Property="Background" Value="White"/>
                                            </Trigger>
                                        </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </ListBox.ItemContainerStyle>
                </ListBox>
                <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right"
                            Margin="0,0,0,10"
                                    dp:StackPanelHelper.ChildSpace="10">
                    <Button Style="{StaticResource PrimaryButton}" pu:ButtonHelper.Icon="&#xe631;" x:Name="refreshBtn" Click="Button_Click">刷新</Button>
                    <Button Style="{StaticResource PrimaryButton}" pu:ButtonHelper.Icon="&#xe631;" x:Name="addBtn" Click="Button_Click">新增</Button>
                    <Button Style="{StaticResource DangerButton}" pu:ButtonHelper.Icon="&#xe631;" x:Name="delBatchBtn" Click="Button_Click">删除</Button>
                </StackPanel>
                <DataGrid x:Name="table" Grid.Row="2"
         Style="{StaticResource DefaultDataGrid}" ItemsSource="{Binding Datasource}">
                    <DataGrid.Columns>
                        <DataGridTemplateColumn Header="" Width="Auto" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox VerticalAlignment="Center"
                                              IsChecked="{Binding IsSelected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTemplateColumn Header="#" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Converter={StaticResource rowNumber},
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Width="*" Binding="{Binding DBAddress}" Header="DB地址" />
                        <DataGridTextColumn Width="*" Binding="{Binding StartAddress}" Header="起始地址" />
                        <DataGridTextColumn Width="*" Binding="{Binding Length}" Header="DB长度" />
                        <DataGridTemplateColumn Header="操作" Width="100" IsReadOnly="True" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <Button x:Name="editBtn" Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
                                                Style="{StaticResource RowLink}"
        Margin="0,0,10,0"
        Click="Button_Click"
        DataContext="{Binding }">编辑</Button>
                                        <Button x:Name="delBtn" Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
                                                Style="{StaticResource DangerLinkButton}"
        DataContext="{Binding }"
        Click="Button_Click">删除</Button>
                                    </StackPanel>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </Grid>
        </Border>
    </Grid>
</pu:WindowX>
HMI.AlermInfoManage/AlarmSettingList.xaml.cs
New file
@@ -0,0 +1,215 @@
using HMI.AlarmInfoManage.Entity;
using HMI.Core.Util;
using HMI.UI.Util;
using log4net;
using Panuon.WPF.UI;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace HMI.AlarmInfoManage
{
    /// <summary>
    /// AlarmSettingList.xaml 的交互逻辑
    /// </summary>
    public partial class AlarmSettingList : WindowX
    {
        private ILog log;
        public AlarmSettingList(string blockFile, string markFile, ILog log)
        {
            this.blockFile = blockFile;
            this.markFile = markFile;
            this.log = log;
            DataContext = this;
            InitializeComponent();
        }
        private readonly string blockFile;
        private readonly string markFile;
        private ObservableCollection<AlarmSetting> datasource;
        public ObservableCollection<AlarmSetting> Datasource
        {
            get => datasource;
            set { Set(ref datasource, value); }
        }
        private ObservableCollection<AlarmSetting> markDatasource;
        public ObservableCollection<AlarmSetting> MarkDatasource
        {
            get => markDatasource;
            set { Set(ref markDatasource, value); }
        }
        private ObservableCollection<string> items =
            new ObservableCollection<string> { "DB块报警", "标志位报警" };
        public ObservableCollection<string> Items
        {
            get => items;
            set { Set(ref items, value); }
        }
        private string selectedValue;
        public string SelectedValue
        {
            get { return selectedValue; }
            set
            {
                Set(ref selectedValue, value);
                LoadData();
            }
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Button btn = sender as Button;
            DataGridRow row = btn?.Tag as DataGridRow;
            switch (btn.Name)
            {
                case "refreshBtn":
                    LoadData();
                    break;
                case "addBtn":
                    AddData(new AlarmSetting());
                    break;
                case "delBatchBtn":
                    DeleteBatchData();
                    break;
                case "editBtn":
                    EditData(row.GetIndex(), btn.DataContext as AlarmSetting);
                    break;
                case "delBtn":
                    DeleteData(row.GetIndex());
                    break;
                default:
                    break;
            }
        }
        private void LoadData()
        {
            try
            {
                ObservableCollection<AlarmSetting> datasource = new ObservableCollection<AlarmSetting>();
                if (SelectedValue == "DB块报警" && File.Exists(blockFile))
                {
                    datasource =
                        XmlHelper.DeserializeFromXml<ObservableCollection<AlarmSetting>>(blockFile);
                }
                else if (SelectedValue == "标志位报警" && File.Exists(markFile))
                {
                    datasource =
                        XmlHelper.DeserializeFromXml<ObservableCollection<AlarmSetting>>(markFile);
                }
                Datasource = datasource;
            }
            catch (Exception ex)
            {
                ToastHelper.Error(this, $"加载错误!{ex.Message}");
            }
        }
        private void AddData(AlarmSetting data)
        {
            try
            {
                var file = SelectedValue == "DB块报警" ? blockFile : markFile;
                var modal = new AlarmSettingModal(log)
                {
                    Title = $"新增{SelectedValue}",
                    Owner = Window.GetWindow(this),
                };
                if (modal.Open(-1, data, Datasource, file) == true)
                {
                    LoadData();
                }
            }
            catch (Exception ex)
            {
                log.Error("Add AlarmSetting Failed.", ex);
                ToastHelper.Error(this, $"新增失败!{ex.Message}");
            }
        }
        private void EditData(int index, AlarmSetting data)
        {
            try
            {
                var file = SelectedValue == "DB块报警" ? blockFile : markFile;
                var modal = new AlarmSettingModal(log)
                {
                    Title = $"编辑{SelectedValue}",
                    Owner = Window.GetWindow(this),
                };
                if (modal.Open(index, data, Datasource, file) == true)
                {
                    LoadData();
                }
            }
            catch (Exception ex)
            {
                log.Error("Edit AlarmSetting Failed.", ex);
                ToastHelper.Error(this, $"编辑失败!{ex.Message}");
            }
        }
        private void DeleteData(int index)
        {
            try
            {
                if (MsgBoxHelper.WarnMessage(Window.GetWindow(this), "确定要删除该项吗?该操作不可逆") == MessageBoxResult.OK)
                {
                    var file = SelectedValue == "DB块报警" ? blockFile : markFile;
                    var temp = XmlHelper.DeepClone(Datasource);
                    temp.RemoveAt(index);
                    XmlHelper.SerializeToXml(file, temp);
                    LoadData();
                }
            }
            catch (Exception ex)
            {
                log.Error("Delete AlarmSetting Failed.", ex);
                ToastHelper.Error(this, $"删除失败!{ex.Message}");
            }
        }
        private void DeleteBatchData()
        {
            try
            {
                var temp = Datasource.Where(e => e.IsSelected).ToList();
                if (temp == null || temp.Count <= 0)
                {
                    ToastHelper.Warning(Window.GetWindow(this), "请选择要删除的数据");
                    return;
                }
                if (MsgBoxHelper.WarnMessage(Window.GetWindow(this), $"确定要删除{temp.Count}项数据吗?该操作不可逆") == MessageBoxResult.OK)
                {
                    var file = SelectedValue == "DB块报警" ? blockFile : markFile;
                    temp = Datasource.Where(e => !e.IsSelected).ToList();
                    XmlHelper.SerializeToXml(file, temp);
                    LoadData();
                }
            }
            catch (Exception ex)
            {
                log.Error("Delete Batch AlarmSetting Failed.", ex);
                ToastHelper.Error(this, $"删除失败!{ex.Message}");
            }
        }
        private void WindowX_Loaded(object sender, RoutedEventArgs e)
        {
            SelectedValue = "DB块报警";
            Loaded -= WindowX_Loaded;
        }
    }
}
HMI.AlermInfoManage/AlermInfoManage.xaml.cs
File was deleted
HMI.AlermInfoManage/AlermInfoManage_Setting.xaml.cs
File was deleted
HMI.AlermInfoManage/Entity/AimSetting.cs
@@ -1,7 +1,7 @@
using HMI.Core.Entity;
using HMI.Core.Util;
namespace HMI.AlermInfoManage.Entity
namespace HMI.AlarmInfoManage.Entity
{
    public class AimSetting
    {
HMI.AlermInfoManage/Entity/AlarmSetting.cs
New file
@@ -0,0 +1,24 @@
using HMI.Core.Entity;
namespace HMI.AlarmInfoManage.Entity
{
    public class AlarmSetting : SelectBase
    {
        public static readonly string BLOCK_TYPE = "BLOCK";
        public static readonly string MARK_TYPE = "MARK";
        /// <summary>
        /// 开始DB地址
        /// </summary>
        public int DBAddress { get; set; }
        /// <summary>
        /// 起始Byte位
        /// </summary>
        public int StartAddress { get; set; }
        /// <summary>
        /// 长度
        /// </summary>
        public int Length { get; set; }
    }
}
HMI.AlermInfoManage/Entity/AlarmText.cs
New file
@@ -0,0 +1,59 @@
using System.Xml.Serialization;
namespace HMI.AlarmInfoManage.Entity
{
    public class AlarmText
    {
        /// <summary>
        /// 报警代码
        /// </summary>
        public string AlarmCode { get; set; }
        /// <summary>
        /// 工位编号
        /// </summary>
        public string OpNo { get; set; }
        /// <summary>
        /// 模块编号
        /// </summary>
        public string ModuleNo { get; set; }
        /// <summary>
        /// 模块名称(CN)
        /// </summary>
        public string ModuleName_CN { get; set; }
        /// <summary>
        /// 模块名称(EN)
        /// </summary>
        public string ModuleName_EN { get; set; }
        /// <summary>
        /// 模块报警代码
        /// </summary>
        public string MAlarmCode { get; set; }
        /// <summary>
        /// 模块报警文本(CN)
        /// </summary>
        public string MAlarmText_CN { get; set; }
        /// <summary>
        /// 模块报警文本(EN)
        /// </summary>
        public string MAlarmText_EN { get; set; }
        /// <summary>
        /// 报警时间
        /// </summary>
        public string Time { get; set; }
        /// <summary>
        /// 报警来源
        /// </summary>
        [XmlIgnore]
        public string Source { get; set; }
    }
}
HMI.AlermInfoManage/HMI.AlarmInfoManage.csproj
File was renamed from HMI.AlermInfoManage/HMI.AlermInfoManage.csproj
@@ -6,8 +6,8 @@
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{945C2577-4558-45E3-891A-096CB3047304}</ProjectGuid>
    <OutputType>library</OutputType>
    <RootNamespace>HMI.AlermInfoManage</RootNamespace>
    <AssemblyName>HMI.AlermInfoManage</AssemblyName>
    <RootNamespace>HMI.AlarmInfoManage</RootNamespace>
    <AssemblyName>HMI.AlarmInfoManage</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
@@ -50,14 +50,32 @@
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="ICSharpCode.SharpZipLib, Version=1.0.0.999, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\SharpZipLib.1.0.0\lib\net45\ICSharpCode.SharpZipLib.dll</HintPath>
    </Reference>
    <Reference Include="log4net, Version=3.0.3.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\log4net.3.0.3\lib\net462\log4net.dll</HintPath>
    </Reference>
    <Reference Include="NPOI, Version=2.4.1.0, Culture=neutral, PublicKeyToken=0df73ec7942b34e1, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\NPOI.2.4.1\lib\net45\NPOI.dll</HintPath>
    </Reference>
    <Reference Include="NPOI.OOXML, Version=2.4.1.0, Culture=neutral, PublicKeyToken=0df73ec7942b34e1, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\NPOI.2.4.1\lib\net45\NPOI.OOXML.dll</HintPath>
    </Reference>
    <Reference Include="NPOI.OpenXml4Net, Version=2.4.1.0, Culture=neutral, PublicKeyToken=0df73ec7942b34e1, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\NPOI.2.4.1\lib\net45\NPOI.OpenXml4Net.dll</HintPath>
    </Reference>
    <Reference Include="NPOI.OpenXmlFormats, Version=2.4.1.0, Culture=neutral, PublicKeyToken=0df73ec7942b34e1, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\NPOI.2.4.1\lib\net45\NPOI.OpenXmlFormats.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF, Version=1.1.2.2, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Panuon.WPF.1.1.2.2\lib\net472\Panuon.WPF.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF.UI, Version=1.2.4.8, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Panuon.WPF.UI.1.2.4.9\lib\net472\Panuon.WPF.UI.dll</HintPath>
    </Reference>
    <Reference Include="SimensDriver">
      <HintPath>..\Libs\SimensDriver.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Configuration" />
@@ -77,24 +95,42 @@
    <Reference Include="PresentationFramework" />
  </ItemGroup>
  <ItemGroup>
    <Page Include="AlermInfoManage.xaml">
    <Page Include="AlarmInfoManage.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Compile Include="AlermInfoManage.xaml.cs">
      <DependentUpon>AlermInfoManage.xaml</DependentUpon>
    <Compile Include="AlarmInfoManage.xaml.cs">
      <DependentUpon>AlarmInfoManage.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Compile>
    <Page Include="AlermInfoManage_Setting.xaml">
    <Page Include="AlarmInfoManage_Setting.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="AlarmSettingList.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="Modal\AlarmSettingModal.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="AlermInfoManage_Setting.xaml.cs">
      <DependentUpon>AlermInfoManage_Setting.xaml</DependentUpon>
    <Compile Include="AlarmInfoManage_Setting.xaml.cs">
      <DependentUpon>AlarmInfoManage_Setting.xaml</DependentUpon>
    </Compile>
    <Compile Include="AlarmSettingList.xaml.cs">
      <DependentUpon>AlarmSettingList.xaml</DependentUpon>
    </Compile>
    <Compile Include="Entity\AimSetting.cs" />
    <Compile Include="Entity\AlarmSetting.cs" />
    <Compile Include="Entity\AlarmText.cs" />
    <Compile Include="Modal\AlarmSettingModal.xaml.cs">
      <DependentUpon>AlarmSettingModal.xaml</DependentUpon>
    </Compile>
    <Compile Include="Model\XmlModel.cs" />
    <Compile Include="Model\UIModel.cs" />
    <Compile Include="Properties\AssemblyInfo.cs">
      <SubType>Code</SubType>
    </Compile>
@@ -132,5 +168,10 @@
      <Name>HMI.UI</Name>
    </ProjectReference>
  </ItemGroup>
  <ItemGroup />
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <PropertyGroup>
    <PostBuildEvent>copy /Y "$(TargetPath)" "$(SolutionDir)bin\$(PlatformName)\$(ConfigurationName)\Plugin\$(TargetFileName)"
copy /Y "$(TargetDir)$(TargetName).pdb" "$(SolutionDir)bin\$(PlatformName)\$(ConfigurationName)\Plugin\$(TargetName).pdb"</PostBuildEvent>
  </PropertyGroup>
</Project>
HMI.AlermInfoManage/Modal/AlarmSettingModal.xaml
New file
@@ -0,0 +1,47 @@
<pu:WindowX x:Class="HMI.AlarmInfoManage.AlarmSettingModal"
            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:HMI.AlarmInfoManage"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
            xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
            xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
            mc:Ignorable="d"
            Title="Form"
            SizeToContent="Height"
            Width="600"
            WindowStartupLocation="CenterOwner"
            BorderBrush="#3E3E45"
            BorderThickness="1"
            Foreground="Black"
            pu:WindowXCaption.Background="{StaticResource PrimaryColor}"
            pu:WindowXModalDialog.ButtonStyle="{StaticResource ModalButton}"
            pu:WindowXCaption.Foreground="#FFF"
            pu:WindowXCaption.Buttons="Close"
            pu:WindowXModalDialog.Buttons="OKCancel"
            pu:WindowXModalDialog.CancelButtonContent="取消"
            pu:WindowXModalDialog.OKButtonContent="保存"
            pu:WindowXModalDialog.ButtonPanelMargin="0,30,10,10">
    <Grid Margin="10">
        <StackPanel x:Name="formPanel">
            <pu:FormGroup ui:FormGroupHelper.IsRequired="True"
                                ui:FormGroupHelper.ValidateMethod="ValidateDBAddress"
                          Header="DB地址" GroupName="MyFormGroup">
                <pu:NumberInput Style="{StaticResource FormNumberInput}"
                                x:Name="dbAddress" Interval="1"/>
            </pu:FormGroup>
            <pu:FormGroup ui:FormGroupHelper.IsRequired="True"
              Header="起始地址" GroupName="MyFormGroup">
                <pu:NumberInput Style="{StaticResource FormNumberInput}" x:Name="startAddress" Interval="1"/>
            </pu:FormGroup>
            <pu:FormGroup ui:FormGroupHelper.IsRequired="True"
              Header="长度" GroupName="MyFormGroup">
                <pu:NumberInput Style="{StaticResource FormNumberInput}" x:Name="length" Interval="1"/>
            </pu:FormGroup>
        </StackPanel>
    </Grid>
</pu:WindowX>
HMI.AlermInfoManage/Modal/AlarmSettingModal.xaml.cs
New file
@@ -0,0 +1,134 @@
using HMI.AlarmInfoManage.Entity;
using HMI.Core.Util;
using HMI.UI;
using HMI.UI.Util;
using log4net;
using Panuon.WPF.UI;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
namespace HMI.AlarmInfoManage
{
    /// <summary>
    /// ModalWindow.xaml 的交互逻辑
    /// </summary>
    public partial class AlarmSettingModal : WindowX
    {
        public AlarmSettingModal(ILog log)
        {
            this.log = log;
            this.DataContext = this;
            InitializeComponent();
        }
        private ILog log;
        public string FilePath { get; private set; }
        public int DataIndex { get; private set; }
        public AlarmSetting Data { get; private set; }
        public ObservableCollection<AlarmSetting> 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)
                {
                    log.Error("AlarmSetting Form Submit Failed.", ex);
                    ToastHelper.Error(this, $"未知异常!{ex.Message}");
                    e.Cancel = true;
                    return;
                }
                finally
                {
                    _isProcessing = false;
                    ButtonHelper.SetIsPending(okButton, false);
                }
            }
            base.OnClosing(e);
        }
        public bool ValidateDBAddress(object value, out string err)
        {
            err = null;
            int dbAddress = Convert.ToInt32(value);
            if (Datasource.FirstOrDefault(e => e.DBAddress == dbAddress) != null)
            {
                err = "DB地址已存在";
                return false;
            }
            return true;
        }
        private bool ValidateForm()
        {
            bool flag = FormGroupHelper.ValidateForm(formPanel);
            if (flag)
            {
                Data.DBAddress = Convert.ToInt32(dbAddress.Value);
                Data.StartAddress = Convert.ToInt32(startAddress.Value);
                Data.Length = Convert.ToInt32(length.Value);
            }
            return flag;
        }
        public bool? Open(int index, AlarmSetting data,
            ObservableCollection<AlarmSetting> dataSource,
            string filePath)
        {
            FilePath = filePath;
            DataIndex = index;
            Data = data;
            Datasource = dataSource;
            dbAddress.Value = Data.DBAddress;
            startAddress.Value = Data.StartAddress;
            length.Value = Data.Length;
            return this.ShowDialog();
        }
        private void SaveOrUpdateData()
        {
            if (DataIndex == -1)
            {
                InsertData(Data);
            }
            else
            {
                UpdateData(DataIndex, Data);
            }
        }
        public void InsertData(AlarmSetting data)
        {
            Datasource.Add(data);
            XmlHelper.SerializeToXml(FilePath, Datasource);
        }
        public void UpdateData(int index, AlarmSetting data)
        {
            Datasource[index] = data;
            XmlHelper.SerializeToXml(FilePath, Datasource);
        }
    }
}
HMI.AlermInfoManage/Model/UIModel.cs
New file
@@ -0,0 +1,37 @@
using HMI.AlarmInfoManage.Entity;
using HMI.Core.Entity;
using HMI.Core.Util;
using System.Collections.ObjectModel;
using System.Data;
using System.IO;
namespace HMI.AlarmInfoManage.Model
{
    public class UIModel : NotifyBase
    {
        public SqliteHelper sqliteHelper;
        private ObservableCollection<AlarmSetting> datasource;
        public ObservableCollection<AlarmSetting> Datasource
        {
            get { return datasource; }
            set { Set(ref datasource, value); }
        }
        public UIModel(string filePath)
        {
            if (!File.Exists(filePath))
            {
                SqliteHelper.CreateDatabase(filePath);
            }
            sqliteHelper = new SqliteHelper(filePath);
        }
        public void LoadData() {
            ObservableCollection<AlarmSetting> list = new ObservableCollection<AlarmSetting>();
            DataTable dt = sqliteHelper.GetDataTable("SELECT * FROM alerm_setting;");
        }
    }
}
HMI.AlermInfoManage/Model/XmlModel.cs
New file
@@ -0,0 +1,36 @@
using HMI.AlarmInfoManage.Entity;
using HMI.Core.Entity;
using HMI.Core.Util;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HMI.AlarmInfoManage.Model
{
    public class XmlUIModel : NotifyBase
    {
        public string FileFolder { get; private set; }
        public string AlermTextPath { get; private set; }
        public string AlermSettingPath { get; private set; }
        private ObservableCollection<AlarmText> datasource = new ObservableCollection<AlarmText>();
        public ObservableCollection<AlarmText> Datasource
        {
            get { return datasource; }
            set { Set(ref datasource, value); }
        }
        public XmlUIModel(string FileFolder)
        {
            this.FileFolder = FileFolder;
            this.AlermTextPath = Path.Combine(FileFolder, "AlermText.xml");
            this.AlermSettingPath = Path.Combine(FileFolder, "AlermSetting.xml");
        }
    }
}
HMI.AlermInfoManage/Properties/AssemblyInfo.cs
@@ -8,11 +8,11 @@
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("HMI.AlermInfoManage")]
[assembly: AssemblyTitle("HMI.AlarmInfoManage")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Organization")]
[assembly: AssemblyProduct("HMI.AlermInfoManage")]
[assembly: AssemblyProduct("HMI.AlarmInfoManage")]
[assembly: AssemblyCopyright("Copyright © Organization 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -51,4 +51,4 @@
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: PluginClassName("HMI.AlermInfoManage.AlermInfoManage")]
[assembly: PluginClassName("HMI.AlarmInfoManage.AlarmInfoManage")]
HMI.AlermInfoManage/Properties/Resources.Designer.cs
@@ -1,24 +1,25 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     此代码由工具生成。
//     运行时版本: 4.0.30319.42000
//     运行时版本:4.0.30319.42000
//
//     对此文件的更改可能导致不正确的行为,如果
//     重新生成代码,则所做更改将丢失。
//     对此文件的更改可能会导致不正确的行为,并且如果
//     重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.AlermInfoManage.Properties {
namespace HMI.AlarmInfoManage.Properties {
    using System;
    
    
    /// <summary>
    ///   强类型资源类,用于查找本地化字符串等。
    ///   一个强类型的资源类,用于查找本地化的字符串等。
    /// </summary>
    // 此类是由 StronglyTypedResourceBuilder
    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
    // (以 /str 作为命令选项),或重新生成 VS 项目。
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    internal class Resources {
@@ -32,13 +33,13 @@
        }
        
        /// <summary>
        ///   返回此类使用的缓存 ResourceManager 实例。
        ///   返回此类使用的缓存的 ResourceManager 实例。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Resources.ResourceManager ResourceManager {
            get {
                if ((resourceMan == null)) {
                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HMI.AlermInfoManage.Properties.Resources", typeof(Resources).Assembly);
                if (object.ReferenceEquals(resourceMan, null)) {
                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HMI.AlarmInfoManage.Properties.Resources", typeof(Resources).Assembly);
                    resourceMan = temp;
                }
                return resourceMan;
HMI.AlermInfoManage/Properties/Settings.Designer.cs
@@ -1,28 +1,24 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//     此代码由工具生成。
//     运行时版本:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
//     对此文件的更改可能会导致不正确的行为,并且如果
//     重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.AlermInfoManage.Properties
{
namespace HMI.AlarmInfoManage.Properties {
    
    
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
    {
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.13.0.0")]
    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
        
        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
        
        public static Settings Default
        {
            get
            {
        public static Settings Default {
            get {
                return defaultInstance;
            }
        }
HMI.AlermInfoManage/packages.config
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="log4net" version="3.0.3" targetFramework="net472" />
  <package id="NPOI" version="2.4.1" targetFramework="net472" />
  <package id="Panuon.WPF" version="1.1.2.2" targetFramework="net472" />
  <package id="Panuon.WPF.UI" version="1.2.4.9" targetFramework="net472" />
  <package id="SharpZipLib" version="1.0.0" targetFramework="net472" />
</packages>
HMI.Base/DtConverter.cs
New file
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
namespace HMI.Base
{
    public class DtConverter
    {
        public static void DoConvert<T>(DataTable dataTable, ICollection<T> target) where T : new()
        {
            var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (DataRow row in dataTable.Rows)
            {
                var item = new T();
                foreach (var property in properties)
                {
                    if (dataTable.Columns.Contains(property.Name) && row[property.Name] != DBNull.Value)
                    {
                        property.SetValue(item, Convert.ChangeType(row[property.Name], property.PropertyType));
                    }
                }
                target.Add(item);
            }
        }
    }
}
HMI.Base/HMI.Base.csproj
@@ -31,6 +31,24 @@
    <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="log4net, Version=3.0.3.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\log4net.3.0.3\lib\net462\log4net.dll</HintPath>
@@ -53,6 +71,7 @@
    <Reference Include="PresentationFramework" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="DtConverter.cs" />
    <Compile Include="PluginControl.cs" />
    <Compile Include="PluginInfo.cs" />
    <Compile Include="PluginLoader.cs" />
HMI.Core/Entity/NotifyBase.cs
@@ -29,8 +29,6 @@
            }
        }
        #endregion
    }
    public class RelayCommand<T> : ICommand
HMI.Core/Entity/SelectBase.cs
New file
@@ -0,0 +1,15 @@
using System.Xml.Serialization;
namespace HMI.Core.Entity
{
    public class SelectBase : NotifyBase
    {
        private bool isSelected;
        [XmlIgnore]
        public virtual bool IsSelected
        {
            get => isSelected;
            set { Set(ref isSelected, value); }
        }
    }
}
HMI.Core/HMI.Core.csproj
@@ -69,6 +69,7 @@
    <Reference Include="System.Data.SQLite.Linq">
      <HintPath>..\Libs\System.Data.SQLite.Linq.dll</HintPath>
    </Reference>
    <Reference Include="System.Messaging" />
    <Reference Include="System.Numerics" />
    <Reference Include="System.Web" />
    <Reference Include="System.Xml.Linq" />
@@ -89,12 +90,15 @@
    <Compile Include="Annotation\InitAttribute.cs" />
    <Compile Include="Entity\NotifyBase.cs" />
    <Compile Include="Entity\Param.cs" />
    <Compile Include="Entity\SelectBase.cs" />
    <Compile Include="Event\TabChange.cs" />
    <Compile Include="Exceptions\FormValidateException.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="Setting\SystemSetting.cs" />
    <Compile Include="Util\BeanUtil.cs" />
    <Compile Include="Util\DllHelper.cs" />
    <Compile Include="Util\HexUtil.cs" />
    <Compile Include="Util\MSMQHelper.cs" />
    <Compile Include="Util\PathHelper.cs" />
    <Compile Include="Util\SqliteHelper.cs" />
    <Compile Include="Util\UtilContainer.cs" />
HMI.Core/Setting/SystemSetting.cs
@@ -8,6 +8,9 @@
    public class SystemSetting
    {
        public static SystemSetting Instance { get; private set; }
        /// <summary>
        /// 系统设置改变事件
        /// </summary>
        public static event EventHandler<SystemSetting> SystemSettingChanged;
        static SystemSetting()
@@ -22,6 +25,8 @@
        public string LogoPath { get; private set; }
        public string WindowTitle { get; private set; }
        public string PlcIp { get; private set; }
        public bool PlcEnabled { get; private set; }
        private static void LoadFromXml()
        {
@@ -74,5 +79,19 @@
            get => windowTitle;
            set { Set(ref windowTitle, value); }
        }
        private string plcIp;
        public string PlcIp
        {
            get => plcIp;
            set { Set(ref plcIp, value); }
        }
        private bool plcEnabled;
        public bool PlcEnabled
        {
            get => plcEnabled;
            set { Set(ref plcEnabled, value); }
        }
    }
}
HMI.Core/Util/HexUtil.cs
New file
@@ -0,0 +1,16 @@
using System;
namespace HMI.Core.Util
{
    public class HexUtil
    {
        public static byte[] ToBytesFromHexString(string hexString)
        {
            //以 ' ' 分割字符串,并去掉空字符
            string[] chars = hexString.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            byte[] returnBytes;
            returnBytes = BitConverter.GetBytes(Convert.ToInt32(chars[0]));
            return returnBytes;
        }
    }
}
HMI.Core/Util/MSMQHelper.cs
New file
@@ -0,0 +1,144 @@
//-----------------------------------------------------------------------
// <copyright file="MSMQHelper.cs" company="iPilotWhale">
//      Suzhou Linktron Systems Co.,Ltd . All rights reserved.
// </copyright>
// <author>温向超</author>
// <description>消息队列操作辅助类</description>
//-----------------------------------------------------------------------
using System;
using System.Messaging;
namespace HMI.Core.Util
{
    /// <summary>
    /// 消息队列操作类
    /// </summary>
    public class MSMQHelper
    {
        /// <summary>
        /// 消息队列名称
        /// </summary>
        private string msmqName = string.Empty;
        /// <summary>
        /// 消息队列路径
        /// </summary>
        private string msmqPath = string.Empty;
        /// <summary>
        /// 要操作的消息队列
        /// </summary>
        private MessageQueue msgQueue = null;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="Name">消息队列名称</param>
        public MSMQHelper(string Name)
        {
            try
            {
                this.msmqName = Name;
                this.msmqPath = ".\\private$\\" + this.msmqName;
                if (MessageQueue.Exists(this.msmqPath))
                {
                    // 如果当前消息队列已经存在
                    this.msgQueue = new MessageQueue(this.msmqPath);
                }
                else
                {
                    // 如果消息队列不存在,则创建消息对垒
                    this.msgQueue = MessageQueue.Create(this.msmqPath);
                }
                this.msgQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
            }
            catch(Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="MessageID">消息ID</param>
        /// <param name="MsgString">要发送的字符串</param>
        public void Send(string MessageID,string MsgString)
        {
            try
            {
                this.msgQueue.Send(MsgString, MessageID);
            }
            catch(Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        /// <summary>
        /// 接收消息队列中的消息
        /// </summary>
        /// <param name="TimeOut">超时时间(ms)</param>
        /// <param name="MessageID">消息ID</param>
        /// <param name="MsgString">消息内容</param>
        public void Receive(long TimeOut,out string MessageID,out string MsgString)
        {
            try
            {
                MessageID = string.Empty;
                MsgString = string.Empty;
                TimeSpan Time = new TimeSpan(TimeOut*10000);
                Message MsgReceived = null;
                try
                {
                    MsgReceived = this.msgQueue.Receive(Time);
                }
                catch(Exception ex)
                {
                    // do nothing
                }
                if(MsgReceived!=null)
                {
                    MessageID = MsgReceived.Label;
                    MsgString = MsgReceived.Body.ToString();
                }
            }
            catch(Exception ex)
            {
                throw new Exception("接收消息失败:" + ex.Message);
            }
        }
        /// <summary>
        /// 清除消息队列中的消息
        /// </summary>
        public void Clear()
        {
            try
            {
                this.msgQueue.Purge();
            }
            catch(Exception ex)
            {
                throw new Exception("清除消息队列失败:" + ex.Message);
            }
        }
        /// <summary>
        /// 关闭当前队列
        /// </summary>
        public void Close()
        {
            this.msgQueue.Close();
        }
        /// <summary>
        /// 删除当前队列
        /// </summary>
        public void Delete()
        {
            MessageQueue.Delete(this.msmqPath);
        }
    }
}
HMI.Core/Util/SqliteHelper.cs
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.Odbc;
using System.Data.SQLite;
namespace HMI.Core.Util
@@ -188,7 +186,7 @@
            }
            catch (Exception ex)
            {
                throw new Exception("获取字段名称失败:" + ex.Message);
                throw new Exception("获取表字段名称失败:" + ex.Message);
            }
        }
@@ -210,7 +208,7 @@
            }
            catch (Exception ex)
            {
                throw new Exception("获取字段名称失败:" + ex.Message);
                throw new Exception("获取表名失败:" + ex.Message);
            }
        }
HMI.Core/Util/XmlHelper.cs
@@ -1,6 +1,10 @@
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Web.UI.WebControls;
using System.Xml;
using System.Xml.Serialization;
using static System.Net.WebRequestMethods;
@@ -9,19 +13,27 @@
{
    public static class XmlHelper
    {
        static readonly XmlSerializerNamespaces DEFAULT_NS = new XmlSerializerNamespaces();
        static XmlHelper()
        {
            DEFAULT_NS.Add("", "");
        }
        /// <summary>
        /// serialize object to xml file.
        /// </summary>
        /// <param name="path">the path to save the xml file</param>
        /// <param name="obj">the object you want to serialize</param>
        public static void SerializeToXml(string path, object obj)
        {
        {
            XmlSerializer serializer = new XmlSerializer(obj.GetType());
            string content = string.Empty;
            //serialize
            using (StringWriter writer = new StringWriter())
            {
                serializer.Serialize(writer, obj);
                serializer.Serialize(writer, obj, DEFAULT_NS);
                content = writer.ToString();
            }
            //save to file
@@ -45,13 +57,15 @@
                return (T)serializer.Deserialize(reader);
            }
        }
        public static bool ReadFromXml<T>(T obj, string path) {
        public static bool ReadFromXml<T>(T obj, string path)
        {
            XmlDocument xd = new XmlDocument();
            xd.Load(path);
            Type type = typeof(T);
            XmlNode root = xd.SelectSingleNode(type.Name);
            if (root == null) {
            if (root == null)
            {
                return false;
            }
@@ -70,5 +84,15 @@
            return true;
        }
        public static T DeepClone<T>(T data)
        {
            using (MemoryStream stream = new MemoryStream())
            {
                XmlSerializer serializer = new XmlSerializer(typeof(T));
                serializer.Serialize(stream, data);
                stream.Seek(0, SeekOrigin.Begin);
                return (T)serializer.Deserialize(stream);
            }
        }
    }
}
HMI.DataViewer/DataViewer.xaml
@@ -9,7 +9,9 @@
             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
             xmlns:pw="https://opensource.panuon.com/wpf"
             xmlns:pu="https://opensource.panuon.com/wpf-ui"
             xmlns:dp="clr-namespace:HMI.UI;assembly=HMI.UI"
             xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
             Background="{StaticResource WorkspaceBackground}"
             Loaded="PluginControl_Loaded"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <ResourceDictionary>
@@ -23,105 +25,210 @@
            <uic:DataGridRowNumberConverter x:Key="rowNumber"/>
        </ResourceDictionary>
    </UserControl.Resources>
    <Border Style="{StaticResource CardBoarder}">
        <Grid dp:GridHelper.Space="10">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="7*"/>
            <ColumnDefinition Width="3*"/>
        </Grid.ColumnDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="7*"/>
                <ColumnDefinition Width="3*"/>
            </Grid.ColumnDefinitions>
        <Border Style="{StaticResource CardBoarder}" Margin="0,0,2,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10">
                    <Button DockPanel.Dock="Right" pu:ButtonHelper.Icon="&#xe934;" Style="{StaticResource DangerButton}">删除</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
pu:IconHelper.FontFamily="{StaticResource iconfont}"
pu:ButtonHelper.Icon="&#xe631;" Margin="0,0,10,0" x:Name="refreshBtn" Click="Button_Click">刷新</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
            pu:IconHelper.FontFamily="{StaticResource iconfont}"
            pu:ButtonHelper.Icon="&#xe61a;" Margin="0,0,10,0" x:Name="addBtn" Click="Button_Click">新增</Button>
                    <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
                           VerticalAlignment="Center" HorizontalAlignment="Left" Text="菜单列表"/>
                </DockPanel>
            <DockPanel Grid.ColumnSpan="2">
                <StackPanel DockPanel.Dock="Right" Style="{StaticResource ButtonGroup}">
                    <Button Style="{StaticResource AddBtn}" x:Name="addBtn" Click="Button_Click"/>
                    <Button Style="{StaticResource RfhBtn}" x:Name="rfhBtn" Click="Button_Click"/>
                    <Button Style="{StaticResource DelBtn}" x:Name="delBtn" Click="Button_Click"/>
                </StackPanel>
                <TextBlock Style="{StaticResource TitleBox}" Text="数据查询"/>
            </DockPanel>
                <DataGrid Grid.Column="1" x:Name="table" Style="{StaticResource DefaultDataGrid}" ItemsSource="{Binding DataSource}">
            <!--<Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
            </Grid>-->
            <StackPanel Grid.Row="1" Grid.ColumnSpan="2" HorizontalAlignment="Left" Style="{StaticResource ButtonGroup}">
                <TextBlock Style="{StaticResource TitleBox}" Text="开始时间"/>
                <pu:DateTimePicker Width="190" Style="{StaticResource TimePicker}" SelectedDateTime="{x:Null}" x:Name="startTimeInput"/>
                <TextBlock Style="{StaticResource TitleBox}" Text="结束时间"/>
                <pu:DateTimePicker Width="190" Style="{StaticResource TimePicker}" SelectedDateTime="{x:Null}" x:Name="endTimeInput"/>
                <Button Style="{StaticResource PrimaryButton}" pu:ButtonHelper.Icon="&#xe752;"
                        x:Name="srhBtn" Click="Button_Click">查询</Button>
                <Button Style="{StaticResource PrimaryButton}" pu:ButtonHelper.Icon="&#xe752;"
                        x:Name="resetBtn" Click="Button_Click">重置</Button>
                <Menu x:Name="anyBtn" >
                    <MenuItem Header="分析">
                        <MenuItem Header="CPK" />
                        <MenuItem Header="RandR" />
                    </MenuItem>
                </Menu>
            </StackPanel>
            <DockPanel Grid.Row="2" Grid.ColumnSpan="2" Margin="0,10,0,0">
                <pu:Drawer DockPanel.Dock="Left" x:Name="DrwMenu" MinWidth="34" MaxWidth="400"
                           ShadowColor="{x:Null}"
                           AnimationDuration="0" BorderThickness="2,2,0,2" BorderBrush="{StaticResource CommonBoarder}">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition/>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition/>
                            <ColumnDefinition Width="Auto"/>
                        </Grid.ColumnDefinitions>
                        <DockPanel Grid.ColumnSpan="2" Background="{StaticResource CommonBoarder}">
                            <ToggleButton Grid.Column="1" x:Name="drawerBtn" BorderThickness="0"
                                          DockPanel.Dock="Right"
                                          Background="Transparent"
                                          Width="34" Height="34" HorizontalAlignment="Right"
                                          RenderTransformOrigin="0.5,0.5"
                                          FontFamily="{StaticResource iconfont}"
                                          Content="&#xe60e;" IsChecked="{Binding IsOpen, ElementName=DrwMenu, Mode=TwoWay}">
                                <ToggleButton.Style>
                                    <Style TargetType="ToggleButton">
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding IsOpen, ElementName=DrwMenu}" Value="False">
                                                <Setter Property="RenderTransform">
                                                    <Setter.Value>
                                                        <RotateTransform Angle="-90"/>
                                                    </Setter.Value>
                                                </Setter>
                                            </DataTrigger>
                                            <DataTrigger Binding="{Binding IsOpen, ElementName=DrwMenu}" Value="True">
                                                <Setter Property="Background" Value="Red"/>
                                                <Setter Property="RenderTransform">
                                                    <Setter.Value>
                                                        <RotateTransform Angle="90"/>
                                                    </Setter.Value>
                                                </Setter>
                                            </DataTrigger>
                                            <Trigger Property="IsChecked" Value="True">
                                                <Setter Property="Background" Value="Red"/>
                                            </Trigger>
                                        </Style.Triggers>
                                    </Style>
                                </ToggleButton.Style>
                            </ToggleButton>
                            <TextBlock Text="高级查询" FontSize="16" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                        </DockPanel>
                        <Grid Grid.Row="1">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition/>
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition/>
                            </Grid.ColumnDefinitions>
                            <DockPanel Margin="10,10,10,0" Grid.ColumnSpan="2">
                                <Label Content="选择查询项目"/>
                                <ComboBox Grid.Column="1" x:Name="programInput" SelectionChanged="programInput_SelectionChanged"/>
                            </DockPanel>
                            <DataGrid Margin="10" Grid.Row="1" Grid.ColumnSpan="2" Style="{StaticResource DefaultDataGrid}"
                                      IsReadOnly="False"
                                      BorderThickness="0"
                                      ItemsSource="{Binding Datas}">
                                <DataGrid.Columns>
                                    <DataGridTemplateColumn Header="#" IsReadOnly="True" CanUserResize="False">
                                        <DataGridTemplateColumn.CellTemplate>
                                            <DataTemplate>
                                                <TextBlock Text="{Binding Converter={StaticResource rowNumber},
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"/>
                                            </DataTemplate>
                                        </DataGridTemplateColumn.CellTemplate>
                                    </DataGridTemplateColumn>
                                    <DataGridTextColumn IsReadOnly="True" Width="*" Binding="{Binding Key}" Header="筛选条件" />
                                    <DataGridTemplateColumn Width="*" Header="对比方式">
                                        <DataGridTemplateColumn.CellTemplate>
                                            <DataTemplate>
                                                <TextBlock Text="{Binding Condition}"/>
                                            </DataTemplate>
                                        </DataGridTemplateColumn.CellTemplate>
                                        <DataGridTemplateColumn.CellEditingTemplate>
                                            <DataTemplate>
                                                <ComboBox SelectedValue="{Binding Condition}"
                                                          SelectedValuePath="Content" >
                                                    <ComboBoxItem Content="="/>
                                                    <ComboBoxItem Content="&lt;"/>
                                                    <ComboBoxItem Content="&gt;"/>
                                                    <ComboBoxItem Content="&lt;="/>
                                                    <ComboBoxItem Content="&gt;="/>
                                                </ComboBox>
                                            </DataTemplate>
                                        </DataGridTemplateColumn.CellEditingTemplate>
                                    </DataGridTemplateColumn>
                                    <DataGridTextColumn Width="*" Binding="{Binding Value}" Header="筛选值" >
                                        <DataGridTextColumn.HeaderTemplate>
                                            <DataTemplate>
                                                <StackPanel Orientation="Horizontal">
                                                    <TextBlock Text="*" Foreground="Red" />
                                                    <ContentControl Content="{Binding }"/>
                                                </StackPanel>
                                            </DataTemplate>
                                        </DataGridTextColumn.HeaderTemplate>
                                    </DataGridTextColumn>
                                </DataGrid.Columns>
                            </DataGrid>
                        </Grid>
                        <Label Grid.Row="1" Grid.Column="1"  Width="100" Height="34"
                               VerticalContentAlignment="Center"
                               HorizontalContentAlignment="Center"
                                   Content="高级查询"
                                   FontSize="16">
                            <Label.LayoutTransform>
                                <RotateTransform CenterX="25" CenterY="25" Angle="270"/>
                            </Label.LayoutTransform>
                            <Label.Style>
                                <Style TargetType="Label">
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding IsOpen, ElementName=DrwMenu,Mode=OneWay}" Value="True">
                                            <Setter Property="Visibility" Value="Collapsed"/>
                                        </DataTrigger>
                                        <DataTrigger Binding="{Binding IsOpen, ElementName=DrwMenu}" Value="False">
                                            <Setter Property="Visibility" Value="Visible"/>
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </Label.Style>
                        </Label>
                    </Grid>
                </pu:Drawer>
                <DataGrid BorderThickness="2" BorderBrush="{StaticResource CommonBoarder}" x:Name="dataTable" Style="{StaticResource DefaultDataGrid}">
                    <DataGrid.Columns>
                        <DataGridTemplateColumn Header="" Width="Auto" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox VerticalAlignment="Center" IsChecked="{Binding Selected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTemplateColumn Header="#" CanUserResize="False">
                        <DataGridTemplateColumn Header="#" IsReadOnly="True" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Converter={StaticResource rowNumber},
                    RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Binding="{Binding Code}" Header="菜单编号" />
                        <DataGridTextColumn Binding="{Binding Name}" Header="菜单名称" />
                        <DataGridTextColumn Binding="{Binding Level}" Header="等级" />
                        <DataGridTextColumn Binding="{Binding Type}" Header="类型" />
                        <DataGridTextColumn Binding="{Binding Name}" Header="菜单名称" />
                        <DataGridTextColumn Binding="{Binding ParentCode}" Header="父菜单编号" />
                        <DataGridTextColumn Binding="{Binding ParentName}" Header="父菜单名称" />
                        <DataGridTextColumn Width="*" Binding="{Binding DllPath}" Header="Dll路径" />
                        <DataGridTextColumn Width="*" Binding="{Binding ClassName}" Header="类名" />
                        <DataGridTemplateColumn Header="操作" Width="100" IsReadOnly="True" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <Button x:Name="editBtn" Style="{StaticResource LinkButton}"
                            Margin="0,0,10,0"
                            Click="Button_Click"
                            DataContext="{Binding }">编辑</Button>
                                        <Button x:Name="delBtn" Style="{StaticResource DangerLinkButton}"
                            DataContext="{Binding }"
                            Click="Button_Click">删除</Button>
                                    </StackPanel>
                                    <DataTemplate.Triggers>
                                        <DataTrigger Binding="{Binding Path=IsMouseOver,
                        RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
                                 Value="True">
                                            <Setter TargetName="editBtn" Property="Foreground" Value="#FFF"/>
                                        </DataTrigger>
                                        <DataTrigger Binding="{Binding Path=IsSelected,
                        RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
                                 Value="True">
                                            <Setter TargetName="editBtn" Property="Foreground" Value="#FFF"/>
                                        </DataTrigger>
                                    </DataTemplate.Triggers>
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </StackPanel>
        </Border>
        <Border Grid.Column="1" Style="{StaticResource CardBoarder}" Margin="2,0,0,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10" >
                    <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
                   VerticalAlignment="Center" HorizontalAlignment="Left" Text="菜单结构"/>
                </DockPanel>
                <TreeView x:Name="treeViewer" Style="{StaticResource DefaultTreeView}" ItemsSource="{Binding TreeDataSource}">
                    <TreeView.ItemTemplate>
                        <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                            <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
                                <Image Width="{StaticResource ContentFontSize}" Height="{StaticResource ContentFontSize}" Margin="5"
                                       Source="{Binding IconPath,Converter={StaticResource path2Image},Mode=OneWay}"/>
                                <TextBlock VerticalAlignment="Center" FontSize="{StaticResource ContentFontSize}" Text="{Binding Name}"/>
                            </StackPanel>
                        </HierarchicalDataTemplate>
                    </TreeView.ItemTemplate>
                </TreeView>
            </StackPanel>
        </Border>
    </Grid>
            </DockPanel>
        </Grid>
    </Border>
</hb:PluginControl>
HMI.DataViewer/DataViewer.xaml.cs
@@ -1,6 +1,16 @@
using System.Windows;
using HMI.Base;
using HMI.Core.Util;
using HMI.DataViewer.Entity;
using HMI.UI.Util;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using HMI.Base;
using System.Windows.Data;
namespace HMI.DataViewer
{
@@ -11,12 +21,194 @@
    {
        public DataViewer()
        {
            DataContext = this;
            InitializeComponent();
        }
        private string SourceFilePath;
        private ObservableCollection<DataBase_Setting> settings = new ObservableCollection<DataBase_Setting>();
        public ObservableCollection<DataBase_Setting> Settings
        {
            get => settings;
            set { Set(ref settings, value); }
        }
        private ObservableCollection<QueryData> datas = new ObservableCollection<QueryData>();
        public ObservableCollection<QueryData> Datas
        {
            get => datas;
            set { Set(ref datas, value); }
        }
        private Dictionary<string, DataBase_Setting> Map = new Dictionary<string, DataBase_Setting>();
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Button btn = sender as Button;
            switch (btn.Name)
            {
                case "addBtn":
                    OpenSettingModal(new DataBase_Setting());
                    break;
                case "rfhBtn":
                    break;
                case "delBtn":
                    break;
                case "srhBtn":
                    SearchData();
                    break;
                case "resetBtn":
                    ResetDataTable();
                    break;
                default:
                    break;
            }
        }
        private void OpenSettingModal(DataBase_Setting data)
        {
            DbSettingModal modal = new DbSettingModal(log)
            {
                Title = "新增数据源",
                Owner = Window.GetWindow(this)
            };
            if (modal.Open(-1, data, Settings, SourceFilePath) == true)
            {
                LoadData();
            }
        }
        private void SearchData()
        {
            string program = programInput.Text;
            if (!Map.ContainsKey(program))
            {
                ToastHelper.Error(Window.GetWindow(this), "参数异常,请刷新重试");
                return;
            }
            var datas = Datas?.Where(d => !string.IsNullOrEmpty(d.Value)).ToList();
            if (datas == null || datas.Count <= 0)
            {
                ToastHelper.Warning(Window.GetWindow(this), "请设置查询条件值");
                return;
            }
            DataBase_Setting setting = Map[program];
            string fields = string.Join(",", Datas?.Select(d => $"[{d.Key}]").ToList());
            string conditon = string.Join(" AND ", datas.Select(d => $"[{d.Key}] {d.Condition} '{d.Value}'").ToList());
            string timeCol = setting.TimeCol;
            if (string.IsNullOrEmpty(timeCol))
            {
                timeCol = "time";
            }
            if (!string.IsNullOrEmpty(startTimeInput.Text))
            {
                conditon += $" AND {timeCol} >= '{startTimeInput.Text}'";
            }
            if (!string.IsNullOrEmpty(endTimeInput.Text))
            {
                conditon += $" AND {timeCol} < '{endTimeInput.Text}'";
            }
            conditon = string.IsNullOrEmpty(conditon) ? string.Empty : $" WHERE {conditon}";
            SqliteHelper helper = null;
            string sql = string.Empty;
            try
            {
                helper = new SqliteHelper(setting.DBFilePath);
                sql = $"SELECT {fields} FROM {setting.TableName} {conditon}";
                DataTable dt = helper.GetDataTable(sql);
                dataTable.ItemsSource = dt.DefaultView;
            }
            catch (Exception ex)
            {
                log.Error($"Select Data Failed. SQL: {sql}", ex);
                ToastHelper.Error(Window.GetWindow(this), $"查询失败!{ex.Message}");
            }
            finally
            {
                helper?.Close();
            }
        }
        private void ResetDataTable()
        {
            startTimeInput.SelectedDateTime = null;
            endTimeInput.SelectedDateTime = null;
            dataTable.ItemsSource = null;
            foreach (var data in Datas)
            {
                data.Value = null;
            }
        }
        public override void SetBaseFolder(string folder)
        {
            base.SetBaseFolder(folder);
            SourceFilePath = Path.Combine(BaseFolder, "SourceData.setting");
        }
        private void PluginControl_Loaded(object sender, RoutedEventArgs e)
        {
            LoadData();
            Loaded -= PluginControl_Loaded;
        }
        private void LoadData()
        {
            if (File.Exists(SourceFilePath))
            {
                List<string> names = new List<string>();
                Settings = XmlHelper.DeserializeFromXml<ObservableCollection<DataBase_Setting>>(SourceFilePath);
                Map.Clear();
                foreach (var data in Settings)
                {
                    Map.Add(data.Program, data);
                    names.Add(data.Program);
                }
                programInput.ItemsSource = names;
            }
        }
        private void programInput_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var key = e.AddedItems[0].ToString();
            if (!Map.ContainsKey(key))
            {
                ToastHelper.Error(Window.GetWindow(this), "参数异常,请刷新重试");
                return;
            }
            DataBase_Setting setting = Map[key];
            Datas = new ObservableCollection<QueryData>(
                setting?.Condition?.Select(d => new QueryData { Key = d.Key, Name = d.Desc, Condition = "=" }).ToList());
            var temp = dataTable.Columns[0];
            dataTable.Columns.Clear();
            dataTable.Columns.Add(temp);
            foreach (var data in Datas)
            {
                AddDataGridTextColumn(data.Name, data.Key);
            }
        }
        private void AddDataGridTextColumn(string header, string bindingPath)
        {
            DataGridTextColumn textColumn = new DataGridTextColumn
            {
                Header = header,
                Binding = new Binding(bindingPath)
                ,
                Width = new DataGridLength(1, DataGridLengthUnitType.Star)
            };
            dataTable.Columns.Add(textColumn);
        }
    }
}
HMI.DataViewer/DbSettingModal.xaml
New file
@@ -0,0 +1,113 @@
<pu:WindowX x:Class="HMI.DataViewer.DbSettingModal"
            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:HMI.DataViewer"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
            xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
            xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
            xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
            mc:Ignorable="d"
            Title="Form"
            SizeToContent="Height"
            Width="800"
            WindowStartupLocation="CenterOwner"
            BorderBrush="#3E3E45"
            BorderThickness="1"
            Foreground="Black"
            pu:WindowXCaption.Background="{StaticResource PrimaryColor}"
            pu:WindowXModalDialog.ButtonStyle="{StaticResource ModalButton}"
            pu:WindowXCaption.Foreground="#FFF"
            pu:WindowXCaption.Buttons="Close"
            pu:WindowXModalDialog.Buttons="OKCancel"
            pu:WindowXModalDialog.CancelButtonContent="取消"
            pu:WindowXModalDialog.OKButtonContent="保存"
            pu:WindowXModalDialog.ButtonPanelMargin="0,30,10,10">
    <Grid Margin="10">
        <StackPanel x:Name="formPanel">
            <pu:FormGroup ui:FormGroupHelper.IsRequired="True" Header="名称" GroupName="MyFormGroup">
                <TextBox Style="{StaticResource EditorTextBoxStyle}" x:Name="programInput"/>
            </pu:FormGroup>
            <pu:FormGroup ui:FormGroupHelper.IsRequired="True" Header="数据文件" GroupName="MyFormGroup">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <TextBox Style="{StaticResource EditorTextBoxStyle}" IsReadOnly="True" x:Name="dbFilePathInput"/>
                    <Button Style="{StaticResource SearchButton}" x:Name="searchBtn" Click="Button_Click" Grid.Column="1"/>
                </Grid>
            </pu:FormGroup>
            <pu:FormGroup ui:FormGroupHelper.IsRequired="True" Header="数据表" GroupName="MyFormGroup">
                <ComboBox Style="{StaticResource FormComboxStyle}" SelectedValuePath="Content"
                          x:Name="tableNameInput" SelectionChanged="ComboxSelectionChanged"/>
            </pu:FormGroup>
            <pu:FormGroup Header="字段设置" Orientation="Vertical" GroupName="MyFormGroup">
                <Grid dp:GridHelper.Space="10">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <DataGrid Style="{StaticResource DefaultDataGrid}"
                              BorderThickness="1"
                              pu:DataGridHelper.ColumnHeaderFontSize="12"
                              SelectionMode="Extended"
                              ItemsSource="{Binding Columns}" x:Name="columnTable">
                        <DataGrid.Columns>
                            <DataGridTemplateColumn Header="#" CanUserResize="False">
                                <DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding Converter={StaticResource rowNumber},
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"/>
                                    </DataTemplate>
                                </DataGridTemplateColumn.CellTemplate>
                            </DataGridTemplateColumn>
                            <DataGridTextColumn Width="*" Binding="{Binding }" Header="字段名" />
                        </DataGrid.Columns>
                    </DataGrid>
                    <StackPanel Grid.Column="1" VerticalAlignment="Stretch" dp:StackPanelHelper.ChildSpace="10">
                        <Button Style="{StaticResource PrimaryButton}" x:Name="selectAllBtn" Click="Button_Click">全选</Button>
                        <Button Style="{StaticResource PrimaryButton}" x:Name="addBtn" Click="Button_Click">添加</Button>
                        <Button Style="{StaticResource PrimaryButton}" x:Name="delBtn" Click="Button_Click">删除</Button>
                    </StackPanel>
                    <DataGrid Grid.Column="2" Style="{StaticResource DefaultDataGrid}"
                              BorderThickness="1"
                              IsReadOnly="False"
                              pu:DataGridHelper.ColumnHeaderFontSize="12"
                              ItemsSource="{Binding Conditions}" x:Name="conditionTable">
                        <DataGrid.Columns>
                            <DataGridTemplateColumn Header="#" CanUserResize="False" IsReadOnly="True">
                                <DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding Converter={StaticResource rowNumber},
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"/>
                                    </DataTemplate>
                                </DataGridTemplateColumn.CellTemplate>
                            </DataGridTemplateColumn>
                            <DataGridTextColumn Width="*" IsReadOnly="True" Binding="{Binding Key}" Header="字段名" />
                            <DataGridTextColumn Width="*" Binding="{Binding Desc}" Header="显示名" />
                            <DataGridTemplateColumn Width="*" Header="映射时间" IsReadOnly="True">
                                <DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <RadioButton IsChecked="{Binding IsTimeCol,Mode=OneWay}"
                                                     Click="RadioButton_Click"
                                                     ></RadioButton>
                                    </DataTemplate>
                                </DataGridTemplateColumn.CellTemplate>
                            </DataGridTemplateColumn>
                        </DataGrid.Columns>
                    </DataGrid>
                </Grid>
            </pu:FormGroup>
        </StackPanel>
    </Grid>
</pu:WindowX>
HMI.DataViewer/DbSettingModal.xaml.cs
New file
@@ -0,0 +1,248 @@
using HMI.Core.Util;
using HMI.DataViewer.Entity;
using HMI.UI;
using HMI.UI.Util;
using log4net;
using Microsoft.Win32;
using Panuon.WPF.UI;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows.Controls;
namespace HMI.DataViewer
{
    /// <summary>
    /// ModalWindow.xaml 的交互逻辑
    /// </summary>
    public partial class DbSettingModal : WindowX
    {
        public DbSettingModal(ILog log)
        {
            this.log = log;
            this.DataContext = this;
            InitializeComponent();
        }
        private ILog log;
        public string FilePath { get; private set; }
        public int DataIndex { get; private set; }
        public DataBase_Setting Data { get; private set; }
        public ObservableCollection<DataBase_Setting> Datasource { get; private set; }
        private SqliteHelper helper;
        public SqliteHelper Helper
        {
            get => helper;
            set
            {
                if (helper == value) return;
                helper?.Close();
                helper = value;
            }
        }
        private ObservableCollection<string> columns = new ObservableCollection<string>();
        public ObservableCollection<string> Columns
        {
            get => columns;
            set { Set(ref columns, value); }
        }
        private ObservableCollection<QueryCondition> conditions =
            new ObservableCollection<QueryCondition>();
        public ObservableCollection<QueryCondition> Conditions
        {
            get => conditions;
            set { Set(ref conditions, 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);
                    if (!ValidateForm())
                    {
                        e.Cancel = true;
                        return;
                    }
                    SaveOrUpdateData();
                    Helper?.Close();
                }
                catch (Exception ex)
                {
                    log.Error("IPManage Form Submit Failed.", ex);
                    ToastHelper.Error(this, $"未知异常!{ex.Message}");
                    e.Cancel = true;
                    return;
                }
                finally
                {
                    _isProcessing = false;
                    ButtonHelper.SetIsPending(okButton, false);
                }
            }
            base.OnClosing(e);
        }
        public bool ValidateDBAddress(object value, out string err)
        {
            err = null;
            return true;
        }
        private bool ValidateForm()
        {
            bool flag = FormGroupHelper.ValidateForm(formPanel);
            if (flag)
            {
                Data.Program = programInput.Text;
                Data.DBFilePath = dbFilePathInput.Text;
                Data.TableName = tableNameInput.Text;
                Data.TimeCol = Conditions.FirstOrDefault(c => c.IsTimeCol)?.Key;
                Data.Condition = Conditions;
            }
            return flag;
        }
        public bool? Open(int index, DataBase_Setting data,
            ObservableCollection<DataBase_Setting> dataSource,
            string filePath)
        {
            FilePath = filePath;
            DataIndex = index;
            Data = data;
            Datasource = dataSource;
            programInput.Text = Data.Program;
            dbFilePathInput.Text = Data.DBFilePath;
            tableNameInput.Text = Data.TableName;
            return this.ShowDialog();
        }
        private void SaveOrUpdateData()
        {
            if (DataIndex == -1)
            {
                InsertData(Data);
            }
            else
            {
                UpdateData(DataIndex, Data);
            }
        }
        public void InsertData(DataBase_Setting data)
        {
            Datasource.Add(data);
            XmlHelper.SerializeToXml(FilePath, Datasource);
        }
        public void UpdateData(int index, DataBase_Setting data)
        {
            Datasource[index] = data;
            XmlHelper.SerializeToXml(FilePath, Datasource);
        }
        private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            Button btn = sender as Button;
            switch (btn.Name) {
                case "searchBtn":
                    OpenSelectDbDialog();
                    break;
                case "selectAllBtn":
                    SelectAll();
                    break;
                case "addBtn":
                    AddCondition();
                    break;
                case "delBtn":
                    break;
                default:
                    break;
            }
        }
        private void OpenSelectDbDialog()
        {
            string BasePath = PathHelper.IconFolder;
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "(*.db)|*.db";
            openFileDialog.InitialDirectory = BasePath;
            openFileDialog.Title = "选择数据库文件";
            openFileDialog.Multiselect = false;
            if (openFileDialog.ShowDialog() == true)
            {
                string FileName = openFileDialog.FileName;
                dbFilePathInput.Text = FileName;
                Helper = new SqliteHelper(FileName);
                string[] tableNames = Helper.GetAllTableInFile();
                tableNameInput.ItemsSource = tableNames;
            }
        }
        private void SelectAll()
        {
            columnTable.SelectAll();
            AddCondition();
        }
        private void AddCondition()
        {
            if (columnTable.SelectedItems == null || columnTable.SelectedItems.Count <= 0) return;
            var temp = new ObservableCollection<QueryCondition>();
            foreach (var column in columnTable.SelectedItems)
            {
                temp.Add(new QueryCondition { Key = column.ToString(), Desc = column.ToString() });
            }
            Conditions = temp;
        }
        private void ComboxSelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
        {
            string[] cols = Helper.GetColumnNameFromTable(e.AddedItems[0].ToString());
            Columns = new ObservableCollection<string>(cols);
        }
        private void RadioButton_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            RadioButton btn = sender as RadioButton;
            var condition = btn.DataContext as QueryCondition;
            if (condition.IsTimeCol == true)
            {
                condition.IsTimeCol = false;
            }
            else
            {
                condition.IsTimeCol = true;
                foreach (var c in Conditions)
                {
                    if (c != condition) c.IsTimeCol = false;
                }
            }
        }
    }
}
HMI.DataViewer/Entity/DataBase_Setting.cs
New file
@@ -0,0 +1,106 @@
using HMI.Core.Entity;
using HMI.Core.Util;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace HMI.DataViewer.Entity
{
    /// <summary>
    /// 数据库配置类
    /// </summary>
    [Serializable]
    public class DataBase_Setting : NotifyBase
    {
        /// <summary>
        /// 数据库文件路径
        /// </summary>
        private string dBFilePath = string.Empty;
        /// <summary>
        /// 数据库操作类
        /// </summary>
        private SqliteHelper sql = null;
        /// <summary>
        /// 数据表名称
        /// </summary>
        private string tableName = string.Empty;
        /// <summary>
        /// 设定程序名
        /// </summary>
        private string program = string.Empty;
        private string timeCol = string.Empty;
        /// <summary>
        /// 高级查询条件
        /// </summary>
        private ObservableCollection<QueryCondition> condition = new ObservableCollection<QueryCondition>();
        /// <summary>
        /// 构造函数
        /// </summary>
        public DataBase_Setting()
        {
            // do nothing
        }
        /// <summary>
        /// 数据库文件的存储路径
        /// </summary>
        public string DBFilePath
        {
            get => dBFilePath;
            set { Set(ref dBFilePath, value); }
        }
        public string TimeCol
        {
            get => timeCol;
            set { Set(ref timeCol, value); }
        }
        /// <summary>
        /// 数据库操作句柄
        /// </summary>
        [XmlIgnore]
        public SqliteHelper SQL
        {
            get { return this.sql; }
            set { this.sql = value; }
        }
        /// <summary>
        /// 高级查询条件
        /// </summary>
        public ObservableCollection<QueryCondition> Condition
        {
            get { return this.condition; }
            set { Set(ref condition, value); }
        }
        /// <summary>
        /// 数据表名称
        /// </summary>
        public string TableName
        {
            get { return this.tableName; }
            set { Set(ref tableName, value); }
        }
        /// <summary>
        /// 设定程序名
        /// </summary>
        public string Program
        {
            get { return this.program; }
            set { Set(ref program, value); }
        }
    }
}
HMI.DataViewer/Entity/QueryCondition.cs
New file
@@ -0,0 +1,50 @@
using HMI.Core.Entity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HMI.DataViewer.Entity
{
    /// <summary>
    /// 筛选条件类
    /// </summary>
    public class QueryCondition : NotifyBase
    {
        /// <summary>
        /// 查询的键值
        /// </summary>
        private string key = string.Empty;
        /// <summary>
        /// 键值描述
        /// </summary>
        private string desc = string.Empty;
        /// <summary>
        /// 查询的键值
        /// </summary>
        public string Key
        {
            get { return this.key; }
            set { Set(ref key, value); }
        }
        /// <summary>
        /// 键值描述
        /// </summary>
        public string Desc
        {
            get { return this.desc; }
            set { Set(ref desc, value); }
        }
        private bool isTimeCol;
        public bool IsTimeCol
        {
            get => isTimeCol;
            set { Set(ref isTimeCol, value); }
        }
    }
}
HMI.DataViewer/Entity/QueryData.cs
New file
@@ -0,0 +1,36 @@
using HMI.Core.Entity;
namespace HMI.DataViewer.Entity
{
    public class QueryData : NotifyBase
    {
        private string key;
        private string name;
        private string condition;
        private string _value;
        public string Key
        {
            get => key;
            set => Set(ref key, value);
        }
        public string Name
        {
            get => name;
            set => Set(ref name, value);
        }
        public string Condition
        {
            get => condition;
            set => Set(ref condition, value);
        }
        public string Value
        {
            get => _value;
            set => Set(ref _value, value);
        }
    }
}
HMI.DataViewer/HMI.DataViewer.csproj
@@ -85,8 +85,18 @@
      <DependentUpon>DataViewer.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Compile>
    <Page Include="DbSettingModal.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Entity\QueryCondition.cs" />
    <Compile Include="Entity\DataBase_Setting.cs" />
    <Compile Include="DbSettingModal.xaml.cs">
      <DependentUpon>DbSettingModal.xaml</DependentUpon>
    </Compile>
    <Compile Include="Entity\QueryData.cs" />
    <Compile Include="Properties\AssemblyInfo.cs">
      <SubType>Code</SubType>
    </Compile>
@@ -125,4 +135,8 @@
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <PropertyGroup>
    <PostBuildEvent>copy /Y "$(TargetPath)" "$(SolutionDir)bin\$(PlatformName)\$(ConfigurationName)\Plugin\$(TargetFileName)"
copy /Y "$(TargetDir)$(TargetName).pdb" "$(SolutionDir)bin\$(PlatformName)\$(ConfigurationName)\Plugin\$(TargetName).pdb"</PostBuildEvent>
  </PropertyGroup>
</Project>
HMI.IOMonitor/Entity/DIO.cs
New file
@@ -0,0 +1,61 @@
using HMI.Core.Entity;
using System;
namespace HMI.IOMonitor.Entity
{
    /// <summary>
    /// DIO属性类
    /// </summary>
    [Serializable]
    public class DIO :NotifyBase
    {
        public DIO()
        {
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="diotype">DIO类型</param>
        /// <param name="name">DIO名称</param>
        /// <param name="no">DIO编号</param>
        /// <param name="value">DIO的值</param>
        public DIO(string diotype, string name, string no, bool value)
        {
            this.DIOType = diotype;
            this.Name = name;
            this.No = no;
            this.Value = value;
        }
        /// <summary>
        /// DIO类型
        /// </summary>
        private string dIOType;
        public string DIOType { get => dIOType; set { Set(ref dIOType, value); } }
        /// <summary>
        /// 纯编号
        /// </summary>
        private decimal dioNo;
        public decimal DIO_No { get => dioNo; set { Set(ref dioNo, value); } }
        /// <summary>
        /// 名称
        /// </summary>
        private string name;
        public string Name { get => name; set { Set(ref name, value); } }
        /// <summary>
        /// DIO编号
        /// </summary>
        public string No { get; set; }
        ////[XmlIgnore]
        /// <summary>
        /// DIO的值
        /// </summary>
        public bool Value { get; set; }
    }
}
HMI.IOMonitor/Entity/DioData.cs
File was deleted
HMI.IOMonitor/Entity/IPManage.cs
New file
@@ -0,0 +1,18 @@
namespace HMI.IOMonitor.Entity
{
    /// <summary>
    /// IP管理类
    /// </summary>
    public class IPManage
    {
        /// <summary>
        /// itag
        /// </summary>
        public string DItag { get; set; }
        /// <summary>
        /// qtag
        /// </summary>
        public string DQtag { get; set; }
    }
}
HMI.IOMonitor/HMI.IOMonitor.csproj
@@ -50,14 +50,35 @@
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="ICSharpCode.SharpZipLib, Version=1.0.0.999, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\SharpZipLib.1.0.0\lib\net45\ICSharpCode.SharpZipLib.dll</HintPath>
    </Reference>
    <Reference Include="log4net, Version=3.0.3.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\log4net.3.0.3\lib\net462\log4net.dll</HintPath>
    </Reference>
    <Reference Include="NPOI, Version=2.4.1.0, Culture=neutral, PublicKeyToken=0df73ec7942b34e1, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\NPOI.2.4.1\lib\net45\NPOI.dll</HintPath>
    </Reference>
    <Reference Include="NPOI.OOXML, Version=2.4.1.0, Culture=neutral, PublicKeyToken=0df73ec7942b34e1, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\NPOI.2.4.1\lib\net45\NPOI.OOXML.dll</HintPath>
    </Reference>
    <Reference Include="NPOI.OpenXml4Net, Version=2.4.1.0, Culture=neutral, PublicKeyToken=0df73ec7942b34e1, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\NPOI.2.4.1\lib\net45\NPOI.OpenXml4Net.dll</HintPath>
    </Reference>
    <Reference Include="NPOI.OpenXmlFormats, Version=2.4.1.0, Culture=neutral, PublicKeyToken=0df73ec7942b34e1, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\NPOI.2.4.1\lib\net45\NPOI.OpenXmlFormats.dll</HintPath>
    </Reference>
    <Reference Include="OpcInterface">
      <HintPath>..\Libs\OpcInterface.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF, Version=1.1.2.2, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Panuon.WPF.1.1.2.2\lib\net472\Panuon.WPF.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF.UI, Version=1.2.4.8, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Panuon.WPF.UI.1.2.4.9\lib\net472\Panuon.WPF.UI.dll</HintPath>
    </Reference>
    <Reference Include="SimensDriver">
      <HintPath>..\Libs\SimensDriver.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Configuration" />
@@ -77,11 +98,19 @@
    <Reference Include="PresentationFramework" />
  </ItemGroup>
  <ItemGroup>
    <Page Include="IPManageModal.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Page Include="IOMonitor.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Compile Include="Entity\DioData.cs" />
    <Compile Include="IPManageModal.xaml.cs">
      <DependentUpon>IPManageModal.xaml</DependentUpon>
    </Compile>
    <Compile Include="Entity\DIO.cs" />
    <Compile Include="Entity\IPManage.cs" />
    <Compile Include="IOMonitor.xaml.cs">
      <DependentUpon>IOMonitor.xaml</DependentUpon>
      <SubType>Code</SubType>
HMI.IOMonitor/IOMonitor.xaml
@@ -9,118 +9,222 @@
             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:dp="clr-namespace:HMI.UI;assembly=HMI.UI"
             mc:Ignorable="d"
             Padding="10"
             xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
             mc:Ignorable="d"
             Loaded="UserControl_Loaded"
             Background="{StaticResource WorkspaceBackground}"
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="5*"/>
            <ColumnDefinition Width="5*"/>
        </Grid.ColumnDefinitions>
        <Border Style="{StaticResource CardBoarder}" Margin="0,0,2,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10">
                    <Button DockPanel.Dock="Right" pu:ButtonHelper.Icon="&#xe934;" Style="{StaticResource DangerButton}">删除</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
pu:IconHelper.FontFamily="{StaticResource iconfont}"
pu:ButtonHelper.Icon="&#xe631;" Margin="0,0,10,0" x:Name="refreshBtn" Click="Button_Click">刷新</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
            pu:IconHelper.FontFamily="{StaticResource iconfont}"
            pu:ButtonHelper.Icon="&#xe61a;" Margin="0,0,10,0" x:Name="addBtn" Click="Button_Click">新增</Button>
                    <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
                           VerticalAlignment="Center" HorizontalAlignment="Left" Text="菜单列表"/>
                </DockPanel>
    <UserControl.Resources>
        <Style TargetType="Label">
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
        </Style>
        <CollectionViewSource x:Key="disFilter" Source="{Binding Dis}" Filter="CollectionViewSource_Filter"/>
        <CollectionViewSource x:Key="dosFilter" Source="{Binding Dos}" Filter="CollectionViewSource_Filter"/>
    </UserControl.Resources>
                <DataGrid Grid.Column="1" x:Name="diTable" Style="{StaticResource DefaultDataGrid}" ItemsSource="{Binding DataSource}">
                    <DataGrid.Columns>
                        <DataGridTemplateColumn Header="" Width="Auto" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox VerticalAlignment="Center" IsChecked="{Binding Selected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTemplateColumn Header="#" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Converter={StaticResource rowNumber},
                    RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Width="*" Binding="{Binding Code}" Header="编号" />
                        <DataGridTextColumn Width="*" Binding="{Binding Name}" Header="名称" />
                        <DataGridTextColumn Width="*" Binding="{Binding Type}" Header="类型" />
                        <DataGridTextColumn Width="*" Binding="{Binding Value}" Header="DI值" />
                        <DataGridTemplateColumn Header="操作" Width="100" IsReadOnly="True" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <Button x:Name="editBtn" Style="{StaticResource RowLink}"
                            Margin="0,0,10,0"
                            Click="Button_Click"
                            DataContext="{Binding }">编辑</Button>
                                        <Button x:Name="delBtn" Style="{StaticResource DangerLinkButton}"
                            DataContext="{Binding }"
                            Click="Button_Click">删除</Button>
                                    </StackPanel>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </StackPanel>
        </Border>
    <Border Grid.Row="1" Style="{StaticResource CardBoarder}">
        <Grid dp:GridHelper.Space="10">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
        <Border Grid.Column="1" Style="{StaticResource CardBoarder}" Margin="2,0,0,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10" >
                    <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
                   VerticalAlignment="Center" HorizontalAlignment="Left" Text="菜单结构"/>
                </DockPanel>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="5*"/>
                <ColumnDefinition Width="5*"/>
            </Grid.ColumnDefinitions>
                <DataGrid Grid.Column="1" x:Name="doTable" Style="{StaticResource DefaultDataGrid}" ItemsSource="{Binding DataSource}">
                    <DataGrid.Columns>
                        <DataGridTemplateColumn Header="" Width="Auto" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox VerticalAlignment="Center" IsChecked="{Binding Selected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTemplateColumn Header="#" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Converter={StaticResource rowNumber},
            <DockPanel Grid.Row="0" Grid.ColumnSpan="2">
                <StackPanel DockPanel.Dock="Right" Style="{StaticResource ButtonGroup}">
                    <Button Style="{StaticResource PrimaryButton}" x:Name="addBtn" Click="Button_Click">新增</Button>
                    <Button Style="{StaticResource PrimaryButton}" x:Name="importBtn" Click="Button_Click">导入</Button>
                </StackPanel>
                <TextBlock DockPanel.Dock="Left" VerticalAlignment="Center"
                       FontSize="{StaticResource TitleFontSize}" Text="DIO监控"/>
            </DockPanel>
            <Border Grid.Row="1" Style="{StaticResource CardBoarder}" Padding="0">
                <Grid dp:GridHelper.Space="10">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition />
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <Label Grid.Row="0" Grid.Column="0" Content="DI名称"
                       FontSize="{StaticResource TitleFontSize}"/>
                    <ComboBox Grid.Row="0" Grid.Column="1" x:Name="diName"
                          IsEditable="True" Style="{StaticResource FormComboxStyle}"/>
                    <Button Style="{StaticResource PrimaryButton}" Grid.Row="0" Grid.Column="2"
                        x:Name="diSelect" Click="Button_Click"
                        >DI查询</Button>
                    <Button Style="{StaticResource PrimaryButton}" Grid.Row="0" Grid.Column="3"
                        x:Name="diReset" Click="Button_Click">DI重置</Button>
                    <Label Grid.Row="1" Grid.Column="0" Content="DI编号"
       FontSize="{StaticResource TitleFontSize}"/>
                    <ComboBox Grid.Row="1" Grid.Column="1" x:Name="diCode"
                          IsEditable="True"
                          Style="{StaticResource FormComboxStyle}"/>
                    <Label Grid.Row="2" Grid.Column="0" Content="DI分组"
       FontSize="{StaticResource TitleFontSize}"/>
                    <ComboBox Grid.Row="2" Grid.Column="1" x:Name="diGroup"
                          IsEditable="True" Style="{StaticResource FormComboxStyle}"/>
                    <DataGrid Grid.Row="3" Grid.ColumnSpan="4" x:Name="diTable" Style="{StaticResource DefaultDataGrid}"
                          ItemsSource="{Binding Source={StaticResource disFilter}}">
                        <DataGrid.RowStyle>
                            <Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridRowStyle}}">
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding Value}" Value="True">
                                        <Setter Property="Background" Value="{StaticResource DangerColor}"/>
                                        <Setter Property="Foreground" Value="{StaticResource DangerColorContrast}"/>
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </DataGrid.RowStyle>
                        <DataGrid.Columns>
                            <!--<DataGridTemplateColumn Header="" Width="Auto" CanUserResize="False">
                                <DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <CheckBox VerticalAlignment="Center" IsChecked="{Binding Selected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                                    </DataTemplate>
                                </DataGridTemplateColumn.CellTemplate>
                            </DataGridTemplateColumn>-->
                            <DataGridTemplateColumn Header="#" CanUserResize="False">
                                <DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding Converter={StaticResource rowNumber},
    RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Width="*" Binding="{Binding Code}" Header="编号"/>
                        <DataGridTextColumn Width="*" Binding="{Binding Name}" Header="名称" />
                        <DataGridTextColumn Width="*" Binding="{Binding Type}" Header="类型" />
                        <DataGridTextColumn Width="*" Binding="{Binding Value}" Header="DO值" />
                        <DataGridTemplateColumn Header="操作" Width="100" IsReadOnly="True" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal" ui:StackPanelHelper.ChildSpace="10">
                                        <Button x:Name="editBtn" Style="{StaticResource RowLink}"
                                    </DataTemplate>
                                </DataGridTemplateColumn.CellTemplate>
                            </DataGridTemplateColumn>
                            <DataGridTextColumn Width="*" Binding="{Binding Code}" Header="编号" />
                            <DataGridTextColumn Width="*" Binding="{Binding Name}" Header="名称" />
                            <DataGridTextColumn Width="*" Binding="{Binding Type}" Header="类型" />
                            <DataGridTextColumn Width="*" Binding="{Binding Value}" Header="DI值" />
                            <!--<DataGridTemplateColumn Header="操作" Width="100" IsReadOnly="True" CanUserResize="False">
                                <DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <StackPanel Orientation="Horizontal">
                                            <Button x:Name="editBtn" Style="{StaticResource RowLink}"
            Margin="0,0,10,0"
            Click="Button_Click"
            DataContext="{Binding }">编辑</Button>
                                        <Button x:Name="delBtn" Style="{StaticResource DangerLinkButton}"
                                            <Button x:Name="delBtn" Style="{StaticResource DangerLinkButton}"
            DataContext="{Binding }"
            Click="Button_Click">删除</Button>
                                    </StackPanel>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </StackPanel>
        </Border>
    </Grid>
                                        </StackPanel>
                                    </DataTemplate>
                                </DataGridTemplateColumn.CellTemplate>
                            </DataGridTemplateColumn>-->
                        </DataGrid.Columns>
                    </DataGrid>
                </Grid>
            </Border>
            <Border Grid.Row="1" Grid.Column="1" Style="{StaticResource CardBoarder}" Padding="0">
                <Grid dp:GridHelper.Space="10">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition />
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <Label Grid.Row="0" Grid.Column="0" Content="DO名称"
                   FontSize="{StaticResource TitleFontSize}"/>
                    <ComboBox Grid.Row="0" Grid.Column="1" x:Name="doName"
                          IsEditable="True" Style="{StaticResource FormComboxStyle}"/>
                    <Button Style="{StaticResource PrimaryButton}" Grid.Row="0" Grid.Column="2"
                        x:Name="doSelect" Click="Button_Click">DO查询</Button>
                    <Button Style="{StaticResource PrimaryButton}" Grid.Row="0" Grid.Column="3"
                        x:Name="doReset" Click="Button_Click"
                        >DO重置</Button>
                    <Label Grid.Row="1" Grid.Column="0" Content="DO编号"
   FontSize="{StaticResource TitleFontSize}"/>
                    <ComboBox Grid.Row="1" Grid.Column="1" x:Name="doCode"
                          IsEditable="True" Style="{StaticResource FormComboxStyle}"/>
                    <Label Grid.Row="2" Grid.Column="0" Content="DO分组"
   FontSize="{StaticResource TitleFontSize}"/>
                    <ComboBox Grid.Row="2" Grid.Column="1" x:Name="doGroup"
                          IsEditable="True" Style="{StaticResource FormComboxStyle}"/>
                    <DataGrid Grid.Row="3" Grid.ColumnSpan="4" x:Name="doTable" Style="{StaticResource DefaultDataGrid}"
                      ItemsSource="{Binding Source={StaticResource dosFilter}}">
                        <DataGrid.RowStyle>
                            <Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridRowStyle}}">
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding Value}" Value="True">
                                        <Setter Property="Background" Value="{StaticResource DangerColor}"/>
                                        <Setter Property="Foreground" Value="{StaticResource DangerColorContrast}"/>
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </DataGrid.RowStyle>
                        <DataGrid.Columns>
                            <!--<DataGridTemplateColumn Header="" Width="Auto" CanUserResize="False">
                                <DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <CheckBox VerticalAlignment="Center" IsChecked="{Binding Selected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                                    </DataTemplate>
                                </DataGridTemplateColumn.CellTemplate>
                            </DataGridTemplateColumn>-->
                            <DataGridTemplateColumn Header="#" CanUserResize="False">
                                <DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding Converter={StaticResource rowNumber},
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"/>
                                    </DataTemplate>
                                </DataGridTemplateColumn.CellTemplate>
                            </DataGridTemplateColumn>
                            <DataGridTextColumn Width="*" Binding="{Binding Code}" Header="编号" />
                            <DataGridTextColumn Width="*" Binding="{Binding Name}" Header="名称" />
                            <DataGridTextColumn Width="*" Binding="{Binding Type}" Header="类型" />
                            <DataGridTextColumn Width="*" Binding="{Binding Value}" Header="DO值" />
                            <!--<DataGridTemplateColumn Header="操作" Width="100" IsReadOnly="True" CanUserResize="False">
                                <DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <StackPanel Orientation="Horizontal">
                                            <Button x:Name="editBtn" Style="{StaticResource RowLink}"
        Margin="0,0,10,0"
        Click="Button_Click"
        DataContext="{Binding }">编辑</Button>
                                            <Button x:Name="delBtn" Style="{StaticResource DangerLinkButton}"
        DataContext="{Binding }"
        Click="Button_Click">删除</Button>
                                        </StackPanel>
                                    </DataTemplate>
                                </DataGridTemplateColumn.CellTemplate>
                            </DataGridTemplateColumn>-->
                        </DataGrid.Columns>
                    </DataGrid>
                </Grid>
            </Border>
        </Grid>
    </Border>
</hb:PluginControl>
HMI.IOMonitor/IOMonitor.xaml.cs
@@ -1,8 +1,26 @@
using System.Windows;
using System.Collections.Generic;
using System.Configuration;
using System;
using System.Windows;
using System.Windows.Controls;
using HMI.Base;
using HMI.Core.Api;
using HMI.IOMonitor.Entity;
using log4net;
using System.Linq;
using System.IO;
using HMI.Core.Util;
using System.Collections.ObjectModel;
using HMI.Core.Setting;
using HMI.UI.Util;
using System.Timers;
using System.Windows.Media;
using System.Threading.Tasks;
using System.Windows.Data;
using Microsoft.Win32;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using NPOI.HSSF.UserModel;
namespace HMI.IOMonitor
{
@@ -11,19 +29,474 @@
    /// </summary>
    public partial class IOMonitor : PluginControl
    {
        public string SettingFilePath { get; private set; }
        public string DataFilePath { get; private set; }
        /// <summary>
        /// DIO地址
        /// </summary>
        private string DITag = string.Empty;
        private string DQTag = string.Empty;
        private SystemSetting systemSetting;
        private Timer MonitorTimer;
        private OpcInterface.OpcInterface lTSCADAApi = new OpcInterface.OpcInterface();
        /// <summary>
        /// The diosde 反序列化的DIO对象
        /// </summary>
        private List<DIO> diosde = new List<DIO>();
        /// <summary>The dis
        /// DI口对象
        /// </summary>
        private ObservableCollection<DIO> _dis = new ObservableCollection<DIO>() {
            new DIO(null,null,null,false),
            new DIO(null,null,null,true),
        };
        public ObservableCollection<DIO> Dis
        {
            get => _dis;
            set { Set(ref _dis, value); }
        }
        private ObservableCollection<DIO> _dos = new ObservableCollection<DIO>() {
            new DIO(null,null,null,false),
            new DIO(null,null,null,true),
        };
        public ObservableCollection<DIO> Dos
        {
            get => _dos;
            set { Set(ref _dos, value); }
        }
        private ObservableCollection<IPManage> iPManages = new ObservableCollection<IPManage>();
        public ObservableCollection<IPManage> IPManages
        {
            get { return iPManages; }
            set { Set(ref iPManages, value); }
        }
        public IOMonitor()
        {
            systemSetting = SystemSetting.Instance;
            MonitorTimer = new Timer()
            {
                Interval = 100
            };
            MonitorTimer.Elapsed += MonitorTimer_Elapsed;
            DataContext = this;
            InitializeComponent();
        }
        private void MonitorTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            MonitorTimer.Enabled = false;
            try
            {
                // stopwatch.Start();
                if (lTSCADAApi.DoFctReadSingle(DITag, out string resulti, out string errMsgi))
                {
                    byte[] stri = HexUtil.ToBytesFromHexString(resulti);
                    Array.Reverse(stri);
                    if (stri != null)
                    {
                        for (int i = 0; i < Dis.Count; i++)
                        {
                            Dis[i].Value = Convert.ToBoolean(GetValue(stri, "bool", Dis[i].DIO_No.ToString()));
                        }
                    }
                }
                if (lTSCADAApi.DoFctReadSingle(DQTag, out string resulto, out string errMsgo))
                {
                    byte[] stro = HexUtil.ToBytesFromHexString(resulto);
                    if (stro != null)
                    {
                        for (int i = 0; i < Dos.Count; i++)
                        {
                            Dos[i].Value = Convert.ToBoolean(GetValue(stro, "bool", Dos[i].DIO_No.ToString()));
                        }
                    }
                }
                //stopwatch.Stop();
            }
            catch (Exception ex)
            {
                log.Warn("MonitorTimer_Elapsed Execute Failed.", ex);
                MonitorTimer.Enabled = true;
                return;
            }
            finally
            {
                this.MonitorTimer.Enabled = true;
            }
        }
        /// <summary>
        /// 初始化方法
        /// </summary>
        private void LoadData()
        {
            diosde.Clear();
            //Dis.Clear();
            //Dos.Clear();
            diName.Items.Clear();
            doName.Items.Clear();
            diGroup.Items.Clear();
            doGroup.Items.Clear();
            try
            {
                this.diosde = XmlHelper.DeserializeFromXml<List<DIO>>(DataFilePath);
            }
            catch (Exception ex)
            {
                MsgBoxHelper.Error(this, ex.Message);
            }
            if (diosde.Count != 0)
            {
                for (int i = 0; i < diosde.Count; i++)
                {
                    switch (diosde[i].DIOType)
                    {
                        case "I":
                            Dis.Add(diosde[i]);
                            break;
                        case "Q":
                            Dos.Add(diosde[i]);
                            break;
                        case "X":
                            Dis.Add(diosde[i]);
                            break;
                        case "Y":
                            Dos.Add(diosde[i]);
                            break;
                        default:
                            break;
                    }
                }
                Dis = new ObservableCollection<DIO>(Dis.OrderByDescending(t => t.DIO_No).ToList());
                Dos = new ObservableCollection<DIO>(Dos.OrderByDescending(t => t.DIO_No).ToList());
                List<string> listnew1 = new List<string>();
                foreach (var item in Dis)
                {
                    listnew1.Add(item.Name);
                }
                diName.ItemsSource = listnew1;
                List<string> listnew2 = new List<string>();
                foreach (var item in Dos)
                {
                    listnew2.Add(item.Name);
                }
                doName.ItemsSource = listnew2;
                List<string> listDis = new List<string>();
                int digroupmax = int.Parse(Dis[0].DIO_No.ToString().Split('.')[0]);
                switch (digroupmax % 2)
                {
                    case 1:
                        for (int i = 0; i < digroupmax; i++)
                        {
                            listDis.Add(i.ToString() + @".&" + (i + 1).ToString() + ".");
                        }
                        break;
                    case 0:
                        if (digroupmax >= 0)
                        {
                            for (int i = 0; i < (digroupmax - 1); i++)
                            {
                                listDis.Add(i.ToString() + @".&" + (i + 1).ToString() + ".");
                            }
                            listDis.Add(digroupmax.ToString() + ".");
                        }
                        break;
                    default:
                        break;
                }
                int dogroupmax = int.Parse(Dos[0].DIO_No.ToString().Split('.')[0]);
                diGroup.ItemsSource = listDis;
                List<string> listDos = new List<string>();
                switch (dogroupmax % 2)
                {
                    case 1:
                        for (int i = 0; i < dogroupmax; i++)
                        {
                            listDos.Add(i.ToString() + @".&" + (i + 1).ToString() + ".");
                        }
                        break;
                    case 0:
                        if (dogroupmax >= 0)
                        {
                            for (int i = 0; i < (dogroupmax - 1); i++)
                            {
                                listDos.Add(i.ToString() + @".&" + (i + 1).ToString() + ".");
                            }
                            listDos.Add(dogroupmax.ToString() + ".");
                        }
                        break;
                    default:
                        break;
                }
                doGroup.ItemsSource = listDos;
                diCode.ItemsSource = Dis;
                doCode.ItemsSource = Dos;
            }
            else
            {
                //
            }
        }
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            LoadData();
            Loaded -= UserControl_Loaded;
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Button btn = sender as Button;
            switch (btn.Name)
            {
                case "addBtn":
                    OpenAddModal();
                    break;
                case "importBtn":
                    ImportFromExcel();
                    break;
                case "diSelect":
                    CollectionViewSource disFilter = this.Resources["disFilter"] as CollectionViewSource;
                    disFilter?.View.Refresh();
                    break;
                case "diReset":
                    ResetInput("di");
                    break;
                case "doSelect":
                    CollectionViewSource dosFilter = this.Resources["dosFilter"] as CollectionViewSource;
                    dosFilter?.View.Refresh();
                    break;
                case "doReset":
                    ResetInput("do");
                    break;
                default:
                    break;
            }
        }
        private void OpenAddModal()
        {
            IPManageModal modal = new IPManageModal(log)
            {
                Title = "新增",
                Owner = Window.GetWindow(this),
            };
            if (modal.Open(-1, new IPManage(), IPManages, SettingFilePath) == true)
            {
                this.Success("新增成功");
            }
        }
        private void ImportFromExcel()
        {
            IWorkbook workbook = null;
            OpenFileDialog dialog = new OpenFileDialog();
            ////该值确定是否可以选择多个文件
            dialog.Multiselect = false;
            dialog.Title = "请选择文件";
            dialog.Filter = "execl文件(*.xlsx;*.xls)|*.xlsx;*.xls";
            if (dialog.ShowDialog() == true)
            {
                MonitorTimer.Enabled = false;
                string file = dialog.FileName;
                try
                {
                    FileStream fileStream1 = new FileStream(@file, FileMode.Open, FileAccess.Read);
                    if (file.IndexOf(".xlsx") >= 0)
                    {
                        workbook = new XSSFWorkbook(fileStream1);
                    }
                    else if (file.IndexOf(".xls") >= 0)
                    {
                        workbook = new HSSFWorkbook(fileStream1);
                    }
                    ISheet sheet = workbook.GetSheetAt(0);
                    List<DIO> dios1 = new List<DIO>();
                    IRow row;
                    for (int i = 1; i < (sheet.LastRowNum + 1); i++)
                    {
                        DIO[] dIO = new DIO[sheet.LastRowNum];
                        row = sheet.GetRow(i);
                        DIO diotest = new DIO();
                        if (row != null)
                        {
                            dIO[i - 1] = new DIO();
                            dIO[i - 1].Name = row.GetCell(4).ToString();
                            dIO[i - 1].No = row.GetCell(3).ToString();
                            dIO[i - 1].Value = false;
                            dIO[i - 1].DIO_No = Convert.ToDecimal(dIO[i - 1].No.Substring(2));
                            if (row.GetCell(3).ToString().IndexOf("I") >= 0)
                            {
                                dIO[i - 1].DIOType = "I";
                            }
                            else if (row.GetCell(3).ToString().IndexOf("Q") >= 0)
                            {
                                dIO[i - 1].DIOType = "Q";
                            }
                            else if (row.GetCell(3).ToString().IndexOf("X") >= 0)
                            {
                                dIO[i - 1].DIOType = "X";
                            }
                            else if (row.GetCell(3).ToString().IndexOf("Y") >= 0)
                            {
                                dIO[i - 1].DIOType = "Y";
                            }
                            dios1.Add(dIO[i - 1]);
                        }
                    }
                    fileStream1.Close();
                    workbook.Close();
                    XmlHelper.SerializeToXml(SettingFilePath, dios1);
                    lock (this)
                    {
                        MonitorTimer.Stop();
                        LoadData();
                    }
                }
                catch (Exception ex)
                {
                    log.Error("Import Failed.", ex);
                    MsgBoxHelper.Error(this, $"导入失败!{ex.Message}");
                }
            }
        }
        private void ResetInput(string flag)
        {
            if (flag == "di")
            {
                diName.Text = "";
                diCode.Text = "";
                diGroup.Text = "";
                CollectionViewSource source = this.Resources["disFilter"] as CollectionViewSource;
                source?.View.Refresh();
            }
            else
            {
                doName.Text = "";
                doCode.Text = "";
                doGroup.Text = "";
                CollectionViewSource source = this.Resources["dosFilter"] as CollectionViewSource;
                source?.View.Refresh();
            }
        }
        private string GetValue(byte[] bt, string valueType, string address)
        {
            string[] str = address.Split('.');
            switch (valueType.ToLower())
            {
                case "bool":
                    return Snap7.S7.GetBitAt(bt, int.Parse(str[0]), int.Parse(str[1])).ToString();
                default:
                    return "false";
            }
        }
        public override void SetBaseFolder(string folder)
        {
            base.SetBaseFolder(folder);
            this.SettingFilePath = Path.Combine(BaseFolder, "Setting.xml");
            this.DataFilePath = Path.Combine(BaseFolder, "Data.xml");
        }
        public override object Active()
        {
            try
            {
                if (File.Exists(SettingFilePath))
                {
                    ObservableCollection<IPManage> datas =
                        XmlHelper.DeserializeFromXml<ObservableCollection<IPManage>>(SettingFilePath);
                    IPManages = datas;
                    DITag = IPManages[0].DItag;
                    DQTag = IPManages[0].DQtag;
                }
                else
                {
                    IPManage tag = new IPManage();
                    tag.DItag = "";
                    tag.DQtag = "";
                    IPManages.Add(tag);
                    XmlHelper.SerializeToXml(SettingFilePath, IPManages);
                }
                if (systemSetting.PlcEnabled)
                {
                    if (!lTSCADAApi.Connect2("127.0.0.1", 6543, 18888, out string errMsg))
                    {
                        MsgBoxHelper.Error(this, errMsg);
                    }
                    else
                    {
                        this.MonitorTimer.Start();
                    }
                }
            }
            catch (Exception ex)
            {
                log.Error("Active Execute Failed.", ex);
                MsgBoxHelper.ErrorMessage(this, $"插件激活失败!{ex.Message}");
            }
            return null;
        }
        private void CollectionViewSource_Filter(object sender, System.Windows.Data.FilterEventArgs e)
        {
            DIO data = e.Item as DIO;
            if (data == null) return;
            CollectionViewSource source = sender as CollectionViewSource;
            DIO param = new DIO();
            if (source.Source == Dis)
            {
                param.Name = diName.Text;
                param.No = diCode.Text;
            }
            else
            {
                param.Name = doName.Text;
                param.No = doCode.Text;
            }
            if (!string.IsNullOrEmpty(param.Name) && data.Name != param.Name)
            {
                e.Accepted = false;
                return;
            }
            if (!string.IsNullOrEmpty(param.No) && data.No != param.No)
            {
                e.Accepted = false;
                return;
            }
            e.Accepted = true;
        }
    }
}
HMI.IOMonitor/IPManageModal.xaml
New file
@@ -0,0 +1,40 @@
<pu:WindowX x:Class="HMI.IOMonitor.IPManageModal"
            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:HMI.IOMonitor"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
            xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
            xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
            mc:Ignorable="d"
            Title="Form"
            SizeToContent="Height"
            Width="600"
            WindowStartupLocation="CenterOwner"
            BorderBrush="#3E3E45"
            BorderThickness="1"
            Foreground="Black"
            pu:WindowXCaption.Background="{StaticResource PrimaryColor}"
            pu:WindowXModalDialog.ButtonStyle="{StaticResource ModalButton}"
            pu:WindowXCaption.Foreground="#FFF"
            pu:WindowXCaption.Buttons="Close"
            pu:WindowXModalDialog.Buttons="OKCancel"
            pu:WindowXModalDialog.CancelButtonContent="取消"
            pu:WindowXModalDialog.OKButtonContent="保存"
            pu:WindowXModalDialog.ButtonPanelMargin="0,30,10,10">
    <Grid Margin="10">
        <StackPanel x:Name="formPanel">
            <pu:FormGroup ui:FormGroupHelper.IsRequired="True"
  Header="I点读取标签" GroupName="MyFormGroup">
                <TextBox Style="{StaticResource EditorTextBoxStyle}" x:Name="iTag"/>
            </pu:FormGroup>
            <pu:FormGroup ui:FormGroupHelper.IsRequired="True"
  Header="Q点读取标签" GroupName="MyFormGroup">
                <TextBox Style="{StaticResource EditorTextBoxStyle}" x:Name="qTag"/>
            </pu:FormGroup>
        </StackPanel>
    </Grid>
</pu:WindowX>
HMI.IOMonitor/IPManageModal.xaml.cs
New file
@@ -0,0 +1,126 @@
using HMI.Core.Util;
using HMI.IOMonitor.Entity;
using HMI.UI;
using HMI.UI.Util;
using log4net;
using Panuon.WPF.UI;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace HMI.IOMonitor
{
    /// <summary>
    /// ModalWindow.xaml 的交互逻辑
    /// </summary>
    public partial class IPManageModal : WindowX
    {
        public IPManageModal(ILog log)
        {
            this.log = log;
            this.DataContext = this;
            InitializeComponent();
        }
        private ILog log;
        public string FilePath { get; private set; }
        public int DataIndex { get; private set; }
        public IPManage Data { get; private set; }
        public ObservableCollection<IPManage> 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)
                {
                    log.Error("IPManage Form Submit Failed.", ex);
                    ToastHelper.Error(this, $"未知异常!{ex.Message}");
                    e.Cancel = true;
                    return;
                }
                finally
                {
                    _isProcessing = false;
                    ButtonHelper.SetIsPending(okButton, false);
                }
            }
            base.OnClosing(e);
        }
        public bool ValidateDBAddress(object value, out string err)
        {
            err = null;
            return true;
        }
        private bool ValidateForm()
        {
            bool flag = FormGroupHelper.ValidateForm(formPanel);
            if (flag)
            {
                Data.DItag =iTag.Text;
                Data.DQtag = qTag.Text;
            }
            return flag;
        }
        public bool? Open(int index, IPManage data,
            ObservableCollection<IPManage> dataSource,
            string filePath)
        {
            FilePath = filePath;
            DataIndex = index;
            Data = data;
            Datasource = dataSource;
            iTag.Text = Data.DItag;
            qTag.Text = Data.DQtag;
            return this.ShowDialog();
        }
        private void SaveOrUpdateData()
        {
            if (DataIndex == -1)
            {
                InsertData(Data);
            }
            else
            {
                UpdateData(DataIndex, Data);
            }
        }
        public void InsertData(IPManage data)
        {
            Datasource.Add(data);
            XmlHelper.SerializeToXml(FilePath, Datasource);
        }
        public void UpdateData(int index, IPManage data)
        {
            Datasource[index] = data;
            XmlHelper.SerializeToXml(FilePath, Datasource);
        }
    }
}
HMI.IOMonitor/packages.config
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="log4net" version="3.0.3" targetFramework="net472" />
  <package id="NPOI" version="2.4.1" targetFramework="net472" />
  <package id="Panuon.WPF" version="1.1.2.2" targetFramework="net472" />
  <package id="Panuon.WPF.UI" version="1.2.4.9" targetFramework="net472" />
  <package id="SharpZipLib" version="1.0.0" targetFramework="net472" />
</packages>
HMI.MenuManage/MenuManage.xaml
@@ -36,7 +36,9 @@
        <Border Style="{StaticResource CardBoarder}" Margin="0,0,2,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10">
                    <Button DockPanel.Dock="Right" pu:ButtonHelper.Icon="&#xe934;" Style="{StaticResource DangerButton}">删除</Button>
                    <Button DockPanel.Dock="Right" pu:ButtonHelper.Icon="&#xe934;"
                            x:Name="deleteBatchBtn"
                            Style="{StaticResource DangerButton}" Click="Button_Click" Cursor="Hand">删除</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}" 
pu:IconHelper.FontFamily="{StaticResource iconfont}"
pu:ButtonHelper.Icon="&#xe631;" Margin="0,0,10,0" x:Name="refreshBtn" Click="Button_Click">刷新</Button>
HMI.MenuManage/MenuManage.xaml.cs
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
@@ -16,29 +18,8 @@
    /// </summary>
    public partial class MenuManage : PluginControl
    {
        #region ...
        public virtual event PropertyChangedEventHandler PropertyChanged;
        protected virtual void RaisePropertyChanged(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        protected void RaiseAndSetIfChanged<T>(ref T old, T @new, [CallerMemberName] string propertyName = null)
        {
            old = @new;
            if (propertyName != null)
            {
                RaisePropertyChanged(propertyName);
            }
        }
        #endregion
        #region 属性
        private MenuManageModel Model;
        private ILog log;
        #endregion
        public MenuManage()
@@ -74,6 +55,7 @@
                catch (Exception ex)
                {
                    log.Error("Add Menu Failed.", ex);
                    ToastHelper.Error(Window.GetWindow(this), $"新增失败!{ex.Message}");
                }
            }
            else if (btn.Name == "refreshBtn")
@@ -85,9 +67,34 @@
                catch (Exception ex)
                {
                    log.Error("Refresh Menu List Failed.", ex);
                    ToastHelper.Error(Window.GetWindow(this), $"刷新失败!{ex.Message}");
                }
            }
            else if (btn.Name == "deleteBtn") { }
            else if (btn.Name == "deleteBatchBtn")
            {
                try
                {
                    List<string> ids = Model.DataSource.Where(x => x.Selected).Select(x => x.Id).ToList();
                    if (ids == null || ids.Count <= 0)
                    {
                        ToastHelper.Warning(Window.GetWindow(this), "请选择要删除的菜单!");
                        return;
                    }
                    var res = MsgBoxHelper.WarnMessage(Window.GetWindow(this),
                        $"确定删除选中的[{ids.Count}]条菜单吗?该操作不可逆!");
                    if (res == MessageBoxResult.OK)
                    {
                        Model.DeleteMenuByIds(ids);
                        Model.LoadData();
                    }
                }
                catch (Exception ex)
                {
                    log.Error("Delete Menu Batch Failed.", ex);
                    ToastHelper.Error(Window.GetWindow(this), $"删除失败!{ex.Message}");
                }
            }
            else if (btn.Name == "editBtn")
            {
                try
@@ -107,8 +114,8 @@
                }
                catch (Exception ex)
                {
                    log.Error("Edit Menu Failed.", ex);
                    ToastHelper.Error(Window.GetWindow(this), $"编辑失败!{ex.Message}");
                }
            }
            else if (btn.Name == "delBtn")
@@ -116,7 +123,8 @@
                try
                {
                    var data = (MenuData)((Button)sender).DataContext;
                    var res = MsgBoxHelper.WarnMessage(Window.GetWindow(this), $"确定删除[{data.Name}]吗?该操作不可逆!");
                    var res = MsgBoxHelper.WarnMessage(Window.GetWindow(this),
                        $"确定删除菜单[{data.Name}]吗?该操作不可逆!");
                    if (res == MessageBoxResult.OK)
                    {
                        Model.DeleteMenuById(data.Id);
@@ -126,6 +134,7 @@
                catch (Exception ex)
                {
                    log.Error("Delete Menu Failed.", ex);
                    ToastHelper.Error(Window.GetWindow(this), $"删除失败!{ex.Message}");
                }
            }
        }
@@ -139,11 +148,6 @@
        private void treeViewer_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            table.SelectedItem = e.NewValue;
        }
        public void SetLogger(ILog logger)
        {
            this.log = logger;
        }
    }
}
HMI.MenuManage/Modal/MenuModal.xaml
@@ -14,7 +14,7 @@
            BorderBrush="#3E3E45"
            BorderThickness="1"
            Foreground="Black"
            pu:WindowXCaption.Background="{StaticResource MainBorder}"
            pu:WindowXCaption.Background="{StaticResource PrimaryColor}"
            pu:WindowXModalDialog.ButtonStyle="{StaticResource ModalButton}"
            pu:WindowXCaption.Foreground="#FFF"
            pu:WindowXCaption.Buttons="Close"
HMI.MenuManage/Modal/RoleModal.xaml
@@ -14,7 +14,7 @@
            BorderBrush="#3E3E45"
            BorderThickness="1"
            Foreground="Black"
            pu:WindowXCaption.Background="{StaticResource MainBorder}"
            pu:WindowXCaption.Background="{StaticResource PrimaryColor}"
            pu:WindowXCaption.Foreground="#FFF"
            pu:WindowXCaption.Buttons="Close"
            pu:WindowXModalDialog.Buttons="OKCancel"
HMI.MenuManage/Modal/UserModal.xaml
@@ -15,7 +15,7 @@
            BorderBrush="#3E3E45"
            BorderThickness="1"
            Foreground="Black"
            pu:WindowXCaption.Background="{StaticResource MainBorder}"
            pu:WindowXCaption.Background="{StaticResource PrimaryColor}"
            pu:WindowXModalDialog.ButtonStyle="{StaticResource ModalButton}"
            pu:WindowXCaption.Foreground="#FFF"
            pu:WindowXCaption.Buttons="Close"
HMI.MenuManage/Model/MenuManageModel.cs
@@ -3,6 +3,7 @@
using System.Collections.ObjectModel;
using System.Data;
using System.IO;
using System.Linq;
using HMI.Core.Entity;
using HMI.Core.Util;
using HMI.MenuManage.Entity;
@@ -103,8 +104,15 @@
        public void DeleteMenuById(string id)
        {
            string sql = $"DELETE FROM menu WHERE id = {id}";
            string sql = $"DELETE FROM menu WHERE id = {id};";
            sqliteHelper.ExecuteSql(sql);
        }
        public void DeleteMenuByIds(IEnumerable<string> ids)
        {
            List<string> sqls = ids.Select(id => $"DELETE FROM menu WHERE id = {id};")
                .ToList();
            sqliteHelper.ExecuteBatchSql(sqls);
        }
    }
}
HMI.MenuManage/Model/RoleManageModel.cs
@@ -2,6 +2,7 @@
using System.Collections.ObjectModel;
using System.Data;
using System.IO;
using System.Windows.Markup;
using HMI.Core.Entity;
using HMI.Core.Util;
using HMI.MenuManage.Entity;
@@ -101,6 +102,18 @@
            });
        }
        public void DeleteRoleByCodes(IEnumerable<string> codes)
        {
            List<string> sqls = new List<string>();
            foreach (string code in codes)
            {
                sqls.Add($"DELETE FROM role WHERE code = '{code}';");
                sqls.Add($"DELETE FROM role_menu_rel WHERE roleCode = '{code}';");
                sqls.Add($"DELETE FROM user_role_rel WHERE roleCode = '{code}';");
            }
            sqliteHelper.ExecuteBatchSql(sqls);
        }
        public void SaveRelData(RoleData role)
        {
            if (role == null) return;
HMI.MenuManage/Model/UserManageModel.cs
@@ -94,5 +94,24 @@
        {
            UserRoleRelHelper.SaveUserRoleRel(sqliteHelper, Roles, SelectedUser, null, null);
        }
        public void DeleteUserByUsername(string username)
        {
            sqliteHelper.ExecuteBatchSql(new List<string> {
            $"DELETE FROM user WHERE username = '{username}';",
            $"DELETE FROM user_role_rel WHERE username = '{username}';"
            });
        }
        public void DeleteUserByUsernames(IEnumerable<string> usernames)
        {
            List<string> sqls = new List<string>();
            foreach (string username in usernames)
            {
                sqls.Add($"DELETE FROM user WHERE username = '{username}';");
                sqls.Add($"DELETE FROM user_role_rel WHERE username = '{username}';");
            }
            sqliteHelper.ExecuteBatchSql(sqls);
        }
    }
}
HMI.MenuManage/RoleManage.xaml
@@ -35,7 +35,9 @@
        <Border Style="{StaticResource CardBoarder}" Margin="0,0,2,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10">
                    <Button DockPanel.Dock="Right" pu:ButtonHelper.Icon="&#xe61a;" Style="{StaticResource DangerButton}">删除</Button>
                    <Button DockPanel.Dock="Right" pu:ButtonHelper.Icon="&#xe61a;"
                            Style="{StaticResource DangerButton}"
                            x:Name="deleteBatchBtn" Click="Button_Click">删除</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
pu:ButtonHelper.Icon="&#xe631;" Margin="0,0,10,0" x:Name="refreshBtn" Click="Button_Click">刷新</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
HMI.MenuManage/RoleManage.xaml.cs
@@ -1,15 +1,13 @@
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using log4net;
using log4net.Repository.Hierarchy;
using Panuon.WPF.UI;
using HMI.Core.Api;
using HMI.Base;
using HMI.MenuManage.Entity;
using HMI.MenuManage.Model;
using HMI.Base;
using HMI.UI.Util;
using Panuon.WPF.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace HMI.MenuManage
{
@@ -18,29 +16,8 @@
    /// </summary>
    public partial class RoleManage : PluginControl
    {
        #region ...
        public virtual event PropertyChangedEventHandler PropertyChanged;
        protected virtual void RaisePropertyChanged(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        protected void RaiseAndSetIfChanged<T>(ref T old, T @new, [CallerMemberName] string propertyName = null)
        {
            old = @new;
            if (propertyName != null)
            {
                RaisePropertyChanged(propertyName);
            }
        }
        #endregion
        #region 属性
        private RoleManageModel Model;
        private ILog log;
        #endregion
        public RoleManage()
@@ -60,26 +37,6 @@
        #region 属性
        #endregion
        public object Active()
        {
            return null;
        }
        public object DeActive()
        {
            return null;
        }
        public object Init()
        {
            return null;
        }
        public void OnTabChange(object sender, object args)
        {
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            try
@@ -87,48 +44,112 @@
                Button btn = (Button)sender;
                if (btn.Name == "addBtn")
                {
                    RoleModal modal = new RoleModal
                    try
                    {
                        Title = "新增角色",
                        Owner = Window.GetWindow(this),
                        sqliteHelper = Model.sqliteHelper
                    };
                    if (modal.Open(new RoleData()) == true)
                        RoleModal modal = new RoleModal
                        {
                            Title = "新增角色",
                            Owner = Window.GetWindow(this),
                            sqliteHelper = Model.sqliteHelper
                        };
                        if (modal.Open(new RoleData()) == true)
                        {
                            Model.LoadData();
                        }
                    }
                    catch (Exception ex)
                    {
                        Model.LoadData();
                        log.Error("Add Role Failed.", ex);
                        ToastHelper.Error(Window.GetWindow(this), $"新增失败!{ex.Message}");
                    }
                }
                else if (btn.Name == "refreshBtn")
                {
                    Model.LoadData();
                    try
                    {
                        Model.LoadData();
                    }
                    catch (Exception ex)
                    {
                        log.Error("Refresh Role Failed.", ex);
                        ToastHelper.Error(Window.GetWindow(this), $"刷新失败!{ex.Message}");
                    }
                }
                else if (btn.Name == "menuBtn")
                {
                    Model.LoadRelData((RoleData)btn.DataContext);
                    try
                    {
                        Model.LoadRelData((RoleData)btn.DataContext);
                    }
                    catch (Exception ex)
                    {
                        log.Error("Load Role-Menu Failed.", ex);
                        ToastHelper.Error(Window.GetWindow(this), $"刷新角色失败!{ex.Message}");
                    }
                }
                else if (btn.Name == "editBtn")
                {
                    RoleModal modal = new RoleModal
                    try
                    {
                        Title = "编辑角色",
                        Owner = Window.GetWindow(this),
                        sqliteHelper = Model.sqliteHelper
                    };
                    if (modal.Open((RoleData)btn.DataContext) == true)
                        RoleModal modal = new RoleModal
                        {
                            Title = "编辑角色",
                            Owner = Window.GetWindow(this),
                            sqliteHelper = Model.sqliteHelper
                        };
                        if (modal.Open((RoleData)btn.DataContext) == true)
                        {
                            Model.LoadData();
                        }
                    }
                    catch (Exception ex)
                    {
                        Model.LoadData();
                        log.Error("Edit Role Failed.", ex);
                        ToastHelper.Error(Window.GetWindow(this), $"编辑失败!{ex.Message}");
                    }
                }
                else if (btn.Name == "deleteBatchBtn")
                {
                    try
                    {
                        List<string> codes = Model.DataSource.Where(x => x.Selected).Select(x => x.Code).ToList();
                        if (codes == null || codes.Count <= 0)
                        {
                            ToastHelper.Warning(Window.GetWindow(this), "请选择要删除的角色!");
                            return;
                        }
                        var res = MsgBoxHelper.WarnMessage(Window.GetWindow(this),
                            $"确定删除选中的[{codes.Count}]条角色吗?该操作不可逆!");
                        if (res == MessageBoxResult.OK)
                        {
                            Model.DeleteRoleByCodes(codes);
                            Model.LoadData();
                        }
                    }
                    catch (Exception ex)
                    {
                        log.Error("Delete Role Batch Failed.", ex);
                        ToastHelper.Error(Window.GetWindow(this), $"删除失败!{ex.Message}");
                    }
                }
                else if (btn.Name == "delBtn")
                {
                    try
                    {
                        Model.DelSingleData((RoleData)btn.DataContext);
                        var data = (RoleData)((Button)sender).DataContext;
                        var res = MsgBoxHelper.WarnMessage(Window.GetWindow(this),
                            $"确定删除角色[{data.Name}]吗?该操作不可逆!");
                        if (res == MessageBoxResult.OK)
                        {
                            Model.DelSingleData(data);
                            Model.LoadData();
                        }
                    }
                    catch (Exception ex)
                    {
                        log.Error("Delete Role Failed.", ex);
                        Panuon.WPF.UI.Toast.Show((WindowX)Window.GetWindow(this), "删除失败", ToastPosition.Top);
                        Toast.Show((WindowX)Window.GetWindow(this), "删除失败", ToastPosition.Top);
                    }
                }
                else if (btn.Name == "saveMenuBtn")
@@ -136,24 +157,19 @@
                    try
                    {
                        Model.SaveRelData(Model.SelectedValue);
                        Panuon.WPF.UI.Toast.Show((WindowX)Window.GetWindow(this), "保存成功", ToastPosition.Top);
                        Toast.Show((WindowX)Window.GetWindow(this), "保存成功", ToastPosition.Top);
                    }
                    catch (Exception ex)
                    {
                        log.Error("Save Role-Menu Failed.", ex);
                        Panuon.WPF.UI.Toast.Show((WindowX)Window.GetWindow(this), "保存失败", ToastPosition.Top);
                        Toast.Show((WindowX)Window.GetWindow(this), "保存失败", ToastPosition.Top);
                    }
                }
            }
            catch (Exception ex)
            {
                log.Error("123", ex);
                log.Error("Something Error...", ex);
            }
        }
        public void SetLogger(ILog logger)
        {
            this.log = logger;
        }
    }
}
HMI.MenuManage/UserManage.xaml
@@ -32,7 +32,9 @@
        <Border Style="{StaticResource CardBoarder}" Margin="0,0,2,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10">
                    <Button DockPanel.Dock="Right" pu:ButtonHelper.Icon="&#xe60f;" Style="{StaticResource DangerButton}">删除</Button>
                    <Button DockPanel.Dock="Right" pu:ButtonHelper.Icon="&#xe60f;"
                            Style="{StaticResource DangerButton}"
                            x:Name="deleteBatchBtn" Click="Button_Click">删除</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}" 
                            pu:ButtonHelper.Icon="&#xe631;" Margin="0,0,10,0" x:Name="refreshBtn" Click="Button_Click">刷新</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
HMI.MenuManage/UserManage.xaml.cs
@@ -1,12 +1,12 @@
using System;
using System.Windows;
using System.Windows.Controls;
using HMI.Base;
using HMI.Core.Api;
using HMI.Base;
using HMI.MenuManage.Entity;
using HMI.MenuManage.Model;
using HMI.UI.Util;
using log4net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace HMI.MenuManage
{
@@ -16,7 +16,7 @@
    public partial class UserManage : PluginControl
    {
        UserManageModel Model;
        private ILog log;
        public UserManage()
        {
            Model = new UserManageModel();
@@ -36,35 +36,103 @@
            Button btn = (Button)sender;
            if (btn.Name == "addBtn")
            {
                UserModal userModal = new UserModal
                try
                {
                    Title = "新增用户",
                    Owner = Window.GetWindow(this),
                    sqliteHelper = Model.sqliteHelper
                };
                if (userModal.Open(new UserData()) == true)
                    UserModal userModal = new UserModal
                    {
                        Title = "新增用户",
                        Owner = Window.GetWindow(this),
                        sqliteHelper = Model.sqliteHelper
                    };
                    if (userModal.Open(new UserData()) == true)
                    {
                        Model.LoadData();
                    }
                }
                catch (Exception ex)
                {
                    Model.LoadData();
                    log.Error("Add User Failed.", ex);
                    ToastHelper.Error(Window.GetWindow(this), $"新增失败!{ex.Message}");
                }
            }
            else if (btn.Name == "refreshBtn")
            {
                Model.LoadData();
            }
            else if (btn.Name == "editBtn")
            {
                UserModal userModal = new UserModal
                {
                    Title = "编辑用户",
                    Owner = Window.GetWindow(this),
                    sqliteHelper = Model.sqliteHelper
                };
                if (userModal.Open((UserData)btn.DataContext) == true)
                try
                {
                    Model.LoadData();
                }
                catch (Exception ex)
                {
                    log.Error("Refresh User Failed.", ex);
                    ToastHelper.Error(Window.GetWindow(this), $"刷新失败!{ex.Message}");
                }
            }
            else if (btn.Name == "delBtn") { }
            else if (btn.Name == "editBtn")
            {
                try
                {
                    UserModal userModal = new UserModal
                    {
                        Title = "编辑用户",
                        Owner = Window.GetWindow(this),
                        sqliteHelper = Model.sqliteHelper
                    };
                    if (userModal.Open((UserData)btn.DataContext) == true)
                    {
                        Model.LoadData();
                    }
                }
                catch (Exception ex)
                {
                    log.Error("Edit User Failed.", ex);
                    ToastHelper.Error(Window.GetWindow(this), $"编辑失败!{ex.Message}");
                }
            }
            else if (btn.Name == "deleteBatchBtn")
            {
                try
                {
                    List<string> usernames = Model.DataSource.Where(x => x.Selected).Select(x => x.Username).ToList();
                    if (usernames == null || usernames.Count <= 0)
                    {
                        ToastHelper.Warning(Window.GetWindow(this), "请选择要删除的用户!");
                        return;
                    }
                    var res = MsgBoxHelper.WarnMessage(Window.GetWindow(this),
                        $"确定删除选中的[{usernames.Count}]条用户吗?该操作不可逆!");
                    if (res == MessageBoxResult.OK)
                    {
                        Model.DeleteUserByUsernames(usernames);
                        Model.LoadData();
                        ToastHelper.Success(Window.GetWindow(this), "删除成功");
                    }
                }
                catch (Exception ex)
                {
                    log.Error("Delete User Batch Failed.", ex);
                    ToastHelper.Error(Window.GetWindow(this), $"删除失败!{ex.Message}");
                }
            }
            else if (btn.Name == "delBtn")
            {
                try
                {
                    var data = (UserData)((Button)sender).DataContext;
                    var res = MsgBoxHelper.WarnMessage(Window.GetWindow(this),
                        $"确定删除用户[{data.Name}]吗?该操作不可逆!");
                    if (res == MessageBoxResult.OK)
                    {
                        Model.DeleteUserByUsername(data.Username);
                        Model.LoadData();
                    }
                }
                catch (Exception ex)
                {
                    log.Error("Delete User Failed.", ex);
                    ToastHelper.Error(Window.GetWindow(this), $"删除失败!{ex.Message}");
                }
            }
            else if (btn.Name == "saveRoleBtn")
            {
                try
@@ -76,18 +144,9 @@
                catch (Exception ex)
                {
                    log.Error("Update User-Role Rel Failed.", ex);
                    ToastHelper.Error(Window.GetWindow(this), $"保存失败!{ex.Message}");
                }
            }
        }
        public void SetLogger(ILog logger)
        {
            log = logger;
        }
        public void SetBaseFolder(string folder)
        {
            throw new NotImplementedException();
        }
    }
}
HMI.ParameterManage/ParameterManage.xaml
@@ -8,7 +8,7 @@
             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:dp="clr-namespace:HMI.UI;assembly=HMI.UI"
             xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
             mc:Ignorable="d" 
             Padding="10"
@@ -55,7 +55,7 @@
                        <DataGridTemplateColumn Header="操作" Width="100" IsReadOnly="True" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal" ui:StackPanelHelper.ChildSpace="10">
                                    <StackPanel Orientation="Horizontal" dp:StackPanelHelper.ChildSpace="10">
                                        <Button x:Name="editProductBtn" Style="{StaticResource RowLink}"  DataContext="{Binding }"
                    Click="Button_Click">编辑</Button>
                                        <Button x:Name="delProductBtn" Style="{StaticResource DangerLinkButton}"
HMI.ParameterManage/ParameterModal.xaml
@@ -16,7 +16,7 @@
            BorderBrush="#3E3E45"
            BorderThickness="1"
            Foreground="Black"
            pu:WindowXCaption.Background="{StaticResource MainBorder}"
            pu:WindowXCaption.Background="{StaticResource PrimaryColor}"
            pu:WindowXModalDialog.ButtonStyle="{StaticResource ModalButton}"
            pu:WindowXCaption.Foreground="#FFF"
            pu:WindowXCaption.Buttons="Close"
HMI.ParameterManage/ProductModal.xaml
@@ -15,7 +15,7 @@
            BorderBrush="#3E3E45"
            BorderThickness="1"
            Foreground="Black"
            pu:WindowXCaption.Background="{StaticResource MainBorder}"
            pu:WindowXCaption.Background="{StaticResource PrimaryColor}"
            pu:WindowXModalDialog.ButtonStyle="{StaticResource ModalButton}"
            pu:WindowXCaption.Foreground="#FFF"
            pu:WindowXCaption.Buttons="Close"
HMI.SettingControl/SettingControl.xaml
@@ -36,23 +36,7 @@
                <TextBlock VerticalAlignment="Center" Text="系统设置" Margin="0,0,0,10"
                           FontSize="{StaticResource TitleFontSize}" />
                <TabControl Grid.Row="1"
pu:TabControlHelper.HeaderPanelAlignment="Front"
pu:TabControlHelper.ItemsBackground="{StaticResource PrimaryColorContract}"
pu:TabControlHelper.ItemsForeground="{StaticResource PrimaryColor}"
pu:TabControlHelper.ItemsHeight="40"
pu:TabControlHelper.ItemsHorizontalHeaderAlignment="Left"
pu:TabControlHelper.ItemsHoverBackground="{StaticResource SecondPrimaryColor}"
pu:TabControlHelper.ItemsHoverForeground="{StaticResource SecondPrimaryColorContract}"
pu:TabControlHelper.ItemsRibbonLineMargin="0,5,0,5"
pu:TabControlHelper.ItemsRibbonLineThickness="4"
pu:TabControlHelper.ItemsRibbonLineVisibility="Visible"
pu:TabControlHelper.ItemsSelectedBackground="{StaticResource PrimaryColor}"
pu:TabControlHelper.ItemsSelectedForeground="{StaticResource PrimaryColorContract}"
pu:TabControlHelper.ItemsSelectedRibbonLineBrush="#FFFFFF"
pu:TabControlHelper.ItemsSelectedRibbonLineThickness="2"
TabStripPlacement="Left"
        BorderThickness="0"
        BorderBrush="{StaticResource CommonBoarder}"
        ItemsSource="{Binding TabItems}">
HMI.SettingControl/SettingControl.xaml.cs
@@ -14,62 +14,13 @@
    /// </summary>
    public partial class SettingControl : PluginControl
    {
        #region ...
        public virtual event PropertyChangedEventHandler PropertyChanged;
        protected virtual void Set(string name)
        {
            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
        #region 属性
        private ILog log;
        private ObservableCollection<TabItem> tabItems;
        public ObservableCollection<TabItem> TabItems
        {
            get { return tabItems; }
            set { Set(ref tabItems, value); }
        }
        #endregion
        #region PluginApi
        public object Init()
        {
            return null;
        }
        public object Active()
        {
            return null;
        }
        public object DeActive()
        {
            return null;
        }
        public void OnTabChange(object sender, object args)
        {
        }
        public void SetLogger(ILog logger)
        {
            log = logger;
        }
        #endregion
HMI.SettingControl/SystemConfig.xaml
@@ -25,10 +25,12 @@
    
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <GroupBox Header="程序设置" Padding="10">
        <GroupBox Header="基础设置" Padding="10">
            <StackPanel>
                <pu:FormGroup Header="Logo" GroupName="MyFormGroup">
                    <TextBox Text="{Binding LogoPath}"/>
@@ -39,7 +41,20 @@
                </pu:FormGroup>
            </StackPanel>
        </GroupBox>
        <DockPanel Grid.Row="1" LastChildFill="False">
        <GroupBox Grid.Row="1"  Header="PLC设置" Padding="10">
            <StackPanel>
                <pu:FormGroup Header="PLC Ip" GroupName="MyFormGroup">
                    <TextBox Text="{Binding PlcIp}"/>
                </pu:FormGroup>
                <pu:FormGroup Header="是否启用" GroupName="MyFormGroup">
                    <CheckBox IsChecked="{Binding PlcEnabled,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                </pu:FormGroup>
            </StackPanel>
        </GroupBox>
        <DockPanel Grid.Row="3" LastChildFill="False">
            <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}" Click="Button_Click">保存</Button>
        </DockPanel>
    </Grid>
HMI.SettingControl/SystemConfig.xaml.cs
@@ -1,6 +1,8 @@
using System.Windows.Controls;
using System;
using System.Windows.Controls;
using HMI.Core.Setting;
using HMI.Core.Util;
using HMI.UI.Util;
namespace HMI.SettingControl
{
@@ -19,9 +21,17 @@
        private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            SystemSettingModel settingModel = (SystemSettingModel)DataContext;
            XmlHelper.SerializeToXml(PathHelper.ConfigFolder + "\\SystemConfig.xml",settingModel);
            SystemSetting.ReLoad();
            try
            {
                SystemSettingModel settingModel = (SystemSettingModel)DataContext;
                XmlHelper.SerializeToXml(PathHelper.ConfigFolder + "\\SystemConfig.xml", settingModel);
                SystemSetting.ReLoad();
                this.Success("保存成功");
            }
            catch (Exception ex)
            {
                this.Error($"保存失败!{ex.Message}");
            }
        }
    }
}
HMI.UI/DpUtil/FormGroupHelper.cs
@@ -62,7 +62,7 @@
                {
                    object content = fg.Content;
                    object value = GetValidateValue(content);
                    if (GetIsRequired(fg) && string.IsNullOrEmpty(value.ToString()))
                    if (value != null && GetIsRequired(fg) && string.IsNullOrEmpty(value.ToString()))
                    {
                        fg.ValidateResult = ValidateResult.Error;
                        fg.Message = $"{fg.Header}不能为空";
@@ -93,6 +93,7 @@
            if (content is ComboBox cBox) return cBox.Text;
            if (content is Panel panel) return GetValidateValue(panel.Children);
            if (content is NumberInput numberInput) return numberInput.Value;
            return null;
        }
    }
HMI.UI/DpUtil/GridHelper.cs
New file
@@ -0,0 +1,67 @@
using System.Windows;
using System.Windows.Controls;
namespace HMI.UI.DpUtil
{
    public class GridHelper
    {
        public static double GetSpace(DependencyObject obj)
        {
            return (double)obj.GetValue(SpaceProperty);
        }
        public static void SetSpace(DependencyObject obj, double value)
        {
            obj.SetValue(SpaceProperty, value);
        }
        public static readonly DependencyProperty SpaceProperty =
            DependencyProperty.RegisterAttached("Space", typeof(double), typeof(Grid),
                new PropertyMetadata(0d, OnShowBorderChanged));
        private static void OnShowBorderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is Grid grid)
            {
                if (e.Property == SpaceProperty)
                {
                    grid.Loaded += GridSpaceOnLoaded;
                    return;
                }
            }
        }
        private static void GridSpaceOnLoaded(object sender, RoutedEventArgs e)
        {
            if (!(sender is Grid grid)) return;
            RefreshGridSpace(grid);
            grid.Loaded -= GridSpaceOnLoaded;
        }
        public static void RefreshGridSpace(Grid grid)
        {
            double space = GetSpace(grid);
            if (space <= 0)
            {
                return;
            }
            var rowCount = grid.RowDefinitions.Count;
            var columnCount = grid.ColumnDefinitions.Count;
            int totalCount = rowCount * columnCount;
            if (grid.Children == null) return;
            foreach (FrameworkElement cell in grid.Children)
            {
                int row = Grid.GetRow(cell);
                int col = Grid.GetColumn(cell);
                int rowSpan = Grid.GetRowSpan(cell);
                int colSpan = Grid.GetColumnSpan(cell);
                cell.Margin = new Thickness(space,
                    space,
                    (col + colSpan) == columnCount ? space : 0,
                    (row + rowSpan) == rowCount ? space : 0);
            }
        }
    }
}
HMI.UI/DpUtil/StackPanelHelper.cs
@@ -1,7 +1,7 @@
using System.Windows;
using System.Windows.Controls;
namespace HMI.UI
namespace HMI.UI.DpUtil
{
    public class StackPanelHelper
    {
@@ -20,19 +20,35 @@
            new PropertyMetadata(0d, (d, e) =>
            {
                if (!(d is StackPanel sp)) return;
                if (sp.Children == null) return;
                var children = sp.Children;
                double space = (double)e.NewValue;
                if (sp.Orientation == Orientation.Horizontal)
                sp.Loaded += Sp_Loaded;
            }));
        private static void Sp_Loaded(object sender, RoutedEventArgs e)
        {
            var sp = sender as StackPanel;
            var children = sp.Children;
            if (sp.Children == null) return;
            double space = GetChildSpace(sp);
            if (sp.Orientation == Orientation.Horizontal)
            {
                for (int i = 0; i < children.Count; i++)
                {
                    for (int i = 0; i < children.Count; i++)
                    if (children[i] is FrameworkElement ui)
                    {
                        if (children[i] is FrameworkElement ui)
                        {
                            ui.Margin = new Thickness(0, 0, (i == children.Count - 1) ? 0 : space, 0);
                        }
                        ui.Margin = new Thickness(0, 0, (i == children.Count - 1) ? 0 : space, 0);
                    }
                }
            }));
            }
            else
            {
                for (int i = 0; i < children.Count; i++)
                {
                    if (children[i] is FrameworkElement ui)
                    {
                        ui.Margin = new Thickness(0, 0, 0, (i == children.Count - 1) ? 0 : space);
                    }
                }
            }
        }
    }
}
}
HMI.UI/DpUtil/TreeViewHelper.cs
@@ -1,14 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace HMI.UI
namespace HMI.UI.DpUtil
{
    public class TreeViewHelper
    {
HMI.UI/HMI.UI.csproj
@@ -83,6 +83,7 @@
    <Compile Include="Converter\DataGridRowNumberConverter.cs" />
    <Compile Include="Converter\PathImageConverter.cs" />
    <Compile Include="DpUtil\FormGroupHelper.cs" />
    <Compile Include="DpUtil\GridHelper.cs" />
    <Compile Include="DpUtil\StackPanelHelper.cs" />
    <Compile Include="DpUtil\TreeViewHelper.cs" />
    <Compile Include="Style\BaseWindow.xaml.cs">
@@ -96,7 +97,6 @@
    <Resource Include="Resources\Font\Font.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Resource>
    <Page Include="Style\MsgBoxStyle.xaml">
      <SubType>Designer</SubType>
@@ -151,9 +151,7 @@
      <Generator>SettingsSingleFileGenerator</Generator>
      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
    </None>
    <Resource Include="Resources\Font\iconfont.ttf">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Resource>
    <Resource Include="Resources\Font\iconfont.ttf" />
  </ItemGroup>
  <ItemGroup>
    <Resource Include="Resources\Icon\xinzeng_1.png" />
HMI.UI/Resources/Font/iconfont.ttf
Binary files differ
HMI.UI/Style/BaseWindow.xaml
@@ -26,7 +26,7 @@
                                <RowDefinition/>
                            </Grid.RowDefinitions>
                            <Grid Grid.Row="0" Background="{StaticResource MainBorder}">
                            <Grid Grid.Row="0" Background="{StaticResource PrimaryColor}">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition/>
                                    <ColumnDefinition Width="Auto"/>
HMI.UI/Style/Colors.xaml
@@ -4,12 +4,12 @@
    <Brush x:Key="TableHeaderBackground">#fafafa</Brush>
    <Brush x:Key="TableBoarder">#e8e8e8</Brush>
    <Brush x:Key="CommonBoarder">#e8e8e8</Brush>
    <Brush x:Key="PrimaryColor">#0d4d84</Brush>
    <Brush x:Key="PrimaryColor">#22448b</Brush>
    <Brush x:Key="PrimaryColorContract">#FFF</Brush>
    <Brush x:Key="SecondPrimaryColor">#3692e1</Brush>
    <Brush x:Key="SecondPrimaryColor">#0092e0</Brush>
    <Brush x:Key="SecondPrimaryColorContract">#FFF</Brush>
    
    <Brush x:Key="DangerColor">#d64343</Brush>
@@ -21,19 +21,7 @@
    <Brush x:Key="WarningColor">#e17933</Brush>
    <Brush x:Key="WarningColorContract">#FFF</Brush>
    <Brush x:Key="WindowBar">#0d4d84</Brush>
    <Brush x:Key="MainBorder">#0d4d84</Brush>
    <Brush x:Key="MainMenu">#1d6eb2</Brush>
    <Brush x:Key="ChildBorder">#3692e1</Brush>
    <Brush x:Key="MainStatusBar">#1d6eb2</Brush>
    <Brush x:Key="SecondaryImportant">Orange</Brush>
    <Brush x:Key="Important">#e17933</Brush>
    <Brush x:Key="ImportantFont">#FFF</Brush>
    <Brush x:Key="ButtonColor">#103156</Brush>
    <Brush x:Key="ButtonSelected">#02579E</Brush>
    <Brush x:Key="ButtonFont">White</Brush>
    <Brush x:Key="ButtonFontSelected">White</Brush>
</ResourceDictionary>
HMI.UI/Style/FormStyle.xaml
@@ -95,6 +95,24 @@
        </Style.Triggers>
    </Style>
    <Style x:Key="FormNumberInput" TargetType="pu:NumberInput" BasedOn="{StaticResource {x:Type pu:NumberInput}}">
        <Setter Property="pu:TextBoxHelper.FocusedBorderBrush" Value="#6CBCEA" />
        <Setter Property="pu:TextBoxHelper.FocusedShadowColor" Value="#6CBCEA" />
        <Setter Property="pu:TextBoxHelper.CornerRadius" Value="0" />
        <Setter Property="pu:TextBoxHelper.WatermarkForeground" Value="#4A4A4A" />
        <Setter Property="pu:TextBoxHelper.FocusedWatermarkForeground" Value="#AAAAAA" />
        <Setter Property="Height" Value="35" />
        <Setter Property="Padding" Value="5,0" />
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Style.Triggers>
            <Trigger Property="IsReadOnly" Value="True">
                <Setter Property="Background" Value="LightGray"/>
            </Trigger>
        </Style.Triggers>
    </Style>
    <Style x:Key="MultilineEditorTextBoxStyle" TargetType="TextBox" BasedOn="{StaticResource EditorTextBoxStyle}">
        <Setter Property="Height" Value="100" />
        <Setter Property="Padding" Value="5,10" />
@@ -130,7 +148,7 @@
    <Style x:Key="FormComboxStyle" TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
        <Setter Property="pu:ComboBoxHelper.CornerRadius" Value="0"/>
        <Setter Property="pu:ComboBoxHelper.ItemsHoverBackground" Value="{StaticResource WindowBar}"/>
        <Setter Property="pu:ComboBoxHelper.ItemsHoverBackground" Value="{StaticResource SecondPrimaryColor}"/>
        <Setter Property="pu:ComboBoxHelper.ItemsHoverForeground" Value="#FFF"/>
        <Setter Property="pu:ComboBoxHelper.ItemsSelectedForeground" Value="Black"/>
        <Setter Property="pu:ComboBoxHelper.HoverBorderBrush" Value="#007ACC" />
@@ -145,4 +163,16 @@
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="SelectedValuePath" Value="Content"/>
    </Style>
    <Style x:Key="TimePicker" TargetType="pu:DateTimePicker">
        <Setter Property="Height" Value="35"/>
        <Setter Property="ClearButtonVisibility" Value="Visible"/>
        <Setter Property="ItemsCheckedBackground" Value="{StaticResource PrimaryColor}"/>
        <Setter Property="ItemsCheckedForeground" Value="{StaticResource SecondPrimaryColorContract}"/>
        <Setter Property="ItemsHoverBackground" Value="{StaticResource SecondPrimaryColor}"/>
        <Setter Property="ItemsHoverForeground" Value="{StaticResource SecondPrimaryColorContract}"/>
        <Setter Property="TextStringFormat" Value="yyyy-MM-dd HH:mm:ss"/>
        <Setter Property="Mode" Value="DateTime"/>
    </Style>
</ResourceDictionary>
HMI.UI/Style/MsgBoxStyle.xaml
@@ -130,4 +130,12 @@
        </pu:ToastSettings.LabelStyle>
    </pu:ToastSettings>
    <pu:ToastSettings x:Key="toastError" Spacing="25">
        <pu:ToastSettings.LabelStyle>
            <Style TargetType="Label" BasedOn="{StaticResource {x:Static pu:Toast.LabelStyleKey}}">
                <Setter Property="Background" Value="{StaticResource DangerColor}" />
                <Setter Property="Foreground" Value="{StaticResource DangerColorContrast}" />
            </Style>
        </pu:ToastSettings.LabelStyle>
    </pu:ToastSettings>
</ResourceDictionary>
HMI.UI/Style/PanuonResource.xaml
@@ -4,10 +4,10 @@
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    xmlns:pw="https://opensource.panuon.com/wpf"
                    xmlns:pu="https://opensource.panuon.com/wpf-ui"
             xmlns:dp="clr-namespace:HMI.UI;assembly=HMI.UI">
             xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI">
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="/HMI.UI;component/Style/Colors.xaml"/>
        <pu:StyleDictionary Includes="TextBox,ComboBox,Button,DataGrid,TreeView" />
        <pu:StyleDictionary Includes="TextBox,ComboBox,Button,DataGrid,TreeView,Menu" />
        <ResourceDictionary Source="/HMI.UI;component/Resources/Font/Font.xaml"/>
    </ResourceDictionary.MergedDictionaries>
@@ -15,7 +15,7 @@
    <sys:Double x:Key="ContentFontSize">16</sys:Double>
    <Style x:Key="PrimaryButton" TargetType="Button" BasedOn="{StaticResource {x:Static pu:StyleKeys.ButtonStyle}}">
        <Setter Property="Background" Value="{StaticResource WindowBar}"/>
        <Setter Property="Background" Value="{StaticResource PrimaryColor}"/>
        <Setter Property="Foreground" Value="#FFF"/>
        <Setter Property="pu:IconHelper.FontFamily" Value="{StaticResource iconfont}"/>
        <Setter Property="pu:IconHelper.Margin" Value="0,0,10,0"/>
@@ -33,6 +33,21 @@
    <Style x:Key="DangerButton" TargetType="Button" BasedOn="{StaticResource PrimaryButton}">
        <Setter Property="Background" Value="{StaticResource DangerColor}"/>
        <Setter Property="Foreground" Value="{StaticResource DangerColorContrast}"/>
    </Style>
    <Style x:Key="AddBtn" TargetType="Button" BasedOn="{StaticResource PrimaryButton}">
        <Setter Property="pu:ButtonHelper.Icon" Value="&#xe6a1;"/>
        <Setter Property="Content" Value="新增"/>
    </Style>
    <Style x:Key="RfhBtn" TargetType="Button" BasedOn="{StaticResource PrimaryButton}">
        <Setter Property="pu:ButtonHelper.Icon" Value="&#xe6a1;"/>
        <Setter Property="Content" Value="刷新"/>
    </Style>
    <Style x:Key="DelBtn" TargetType="Button" BasedOn="{StaticResource DangerButton}">
        <Setter Property="pu:ButtonHelper.Icon" Value="&#xe6a1;"/>
        <Setter Property="Content" Value="删除"/>
    </Style>
    <Style x:Key="LinkButton" TargetType="Button" BasedOn="{StaticResource {x:Static pu:StyleKeys.ButtonStyle}}">
@@ -72,6 +87,7 @@
            </Style>
            <Style TargetType="TextBlock">
                <Setter Property="VerticalAlignment" Value="Center"/>
                <Setter Property="Padding" Value="10"/>
            </Style>
        </Style.Resources>
@@ -80,12 +96,12 @@
        <Setter Property="pu:DataGridHelper.ColumnHeaderSeparatorBrush" Value="{StaticResource TableBoarder}"/>
        <Setter Property="pu:DataGridHelper.ColumnHeaderSeparatorVisibility" Value="Visible"/>
        <Setter Property="pu:DataGridHelper.ColumnHeaderVerticalContentAlignment" Value="Center"/>
        <Setter Property="pu:DataGridHelper.RowHeaderBackground" Value="#ffffff"/>
        <Setter Property="pu:DataGridHelper.RowHeaderBackground" Value="White"/>
        <Setter Property="pu:DataGridHelper.RowHeaderSeparatorBrush" Value="{StaticResource PrimaryColor}"/>
        <Setter Property="pu:DataGridHelper.RowHoverForeground" Value="{StaticResource SecondPrimaryColorContract}"/>
        <Setter Property="pu:DataGridHelper.RowHoverBackground" Value="{StaticResource SecondPrimaryColor}"/>
        <Setter Property="pu:DataGridHelper.RowSelectedBackground" Value="{StaticResource PrimaryColor}"/>
        <Setter Property="pu:DataGridHelper.RowSelectedForeground" Value="#ffffff"/>
        <Setter Property="pu:DataGridHelper.RowSelectedForeground" Value="White"/>
        <Setter Property="HorizontalGridLinesBrush" Value="{StaticResource TableBoarder}"/>
        <Setter Property="VerticalGridLinesBrush" Value="{StaticResource TableBoarder}"/>
        <Setter Property="BorderBrush" Value="{StaticResource TableBoarder}" />
@@ -96,11 +112,13 @@
        <Setter Property="RowHeaderWidth" Value="0"/>
        <Setter Property="IsReadOnly" Value="True"/>
        <Setter Property="GridLinesVisibility" Value="All"/>
        <Setter Property="BorderThickness" Value="1,1,0,0"/>
        <Setter Property="BorderThickness" Value="0"/>
        <Setter Property="FontSize" Value="{StaticResource ContentFontSize}"/>
        <Setter Property="RowHeight" Value="24"/>
        <Setter Property="ForceCursor" Value="True"/>
        <Setter Property="SelectionMode" Value="Single"/>
        <Setter Property="pu:DataGridHelper.ColumnHeaderBorderThickness" Value="0"/>
        <Setter Property="pu:DataGridHelper.ColumnHeaderPadding" Value="0"/>
    </Style>
    <Style x:Key="DefaultTreeView" TargetType="TreeView" BasedOn="{StaticResource {x:Static pu:StyleKeys.TreeViewStyle}}">
@@ -130,4 +148,27 @@
            </DataTrigger>
        </Style.Triggers>
    </Style>
    <Style x:Key="DefaultTabControl" TargetType="TabControl">
        <Setter Property="pu:TabControlHelper.ItemsHorizontalHeaderAlignment" Value="Left"/>
        <Setter Property="pu:TabControlHelper.ItemsHoverBackground" Value="{StaticResource SecondPrimaryColor}"/>
        <Setter Property="pu:TabControlHelper.ItemsHoverForeground" Value="{StaticResource SecondPrimaryColorContract}"/>
        <Setter Property="pu:TabControlHelper.ItemsRibbonLineMargin" Value="0,5,0,5"/>
        <Setter Property="pu:TabControlHelper.ItemsRibbonLineThickness" Value="4"/>
        <Setter Property="pu:TabControlHelper.ItemsRibbonLineVisibility" Value="Visible"/>
        <Setter Property="pu:TabControlHelper.ItemsSelectedRibbonLineBrush" Value="{StaticResource PrimaryColor}"/>
        <Setter Property="pu:TabControlHelper.ItemsSelectedRibbonLineThickness" Value="2"/>
    </Style>
    <Style x:Key="ButtonGroup" TargetType="StackPanel">
        <Setter Property="Orientation" Value="Horizontal"/>
        <Setter Property="HorizontalAlignment" Value="Right"/>
        <Setter Property="VerticalAlignment" Value="Center"/>
        <Setter Property="dp:StackPanelHelper.ChildSpace" Value="10"/>
    </Style>
    <Style x:Key="TitleBox" TargetType="TextBlock">
        <Setter Property="VerticalAlignment" Value="Center"/>
        <Setter Property="FontSize" Value="{StaticResource TitleFontSize}"/>
    </Style>
</ResourceDictionary>
HMI.UI/Style/WindowResources.xaml
@@ -8,8 +8,8 @@
    </ResourceDictionary.MergedDictionaries>
    <Style x:Key="WindowButton" TargetType="Button">
        <Setter Property="Background" Value="{StaticResource MainBorder}"/>
        <Setter Property="ui:DpPlugin.HoverColor" Value="{StaticResource ChildBorder}"/>
        <Setter Property="Background" Value="{StaticResource PrimaryColor}"/>
        <Setter Property="ui:DpPlugin.HoverColor" Value="{StaticResource SecondPrimaryColor}"/>
        <Setter Property="FontFamily" Value="{StaticResource iconfont}"/>
        <Setter Property="FontWeight" Value="SemiBold"/>
        <Setter Property="Foreground" Value="White"/>
@@ -49,8 +49,8 @@
    <Style x:Key="ChildWindowButton" BasedOn="{StaticResource WindowButton}" TargetType="Button">
        <Setter Property="FontSize" Value="15"/>
        <Setter Property="Width" Value="30"/>
        <Setter Property="Background" Value="{StaticResource ChildBorder}"/>
        <Setter Property="ui:DpPlugin.HoverColor" Value="{StaticResource MainBorder}"/>
        <Setter Property="Background" Value="{StaticResource SecondPrimaryColor}"/>
        <Setter Property="ui:DpPlugin.HoverColor" Value="{StaticResource PrimaryColor}"/>
    </Style>
    <Style x:Key="ChildWindowCloseButton" BasedOn="{StaticResource ChildWindowButton}" TargetType="Button">
@@ -58,9 +58,9 @@
    </Style>
    <Style x:Key="LinkButton" TargetType="Button">
        <Setter Property="ui:DpPlugin.HoverColor" Value="{StaticResource ChildBorder}"/>
        <Setter Property="ui:DpPlugin.HoverColor" Value="{StaticResource SecondPrimaryColor}"/>
        <Setter Property="FontFamily" Value="{StaticResource iconfont}"/>
        <Setter Property="Foreground" Value="{StaticResource ChildBorder}"/>
        <Setter Property="Foreground" Value="{StaticResource SecondPrimaryColor}"/>
        <Setter Property="Template">
            <Setter.Value>
HMI.UI/Util/MsgBoxHelper.cs
@@ -1,6 +1,7 @@
using Panuon.WPF.UI;
using System.Windows;
using Panuon.WPF.UI.Configurations;
using System.Windows.Threading;
namespace HMI.UI.Util
{
@@ -15,12 +16,12 @@
            DefaultButton defaultButton = DefaultButton.Unset, int? countdown = null)
        {
            var setting = Application.Current.FindResource("MsgBoxWarn") as MessageBoxXSetting;
            return MessageBoxX.Show(owner, message, caption, button, icon, defaultButton, setting, countdown);
            return ShowMessage(owner, message, caption, button, icon, defaultButton, setting, countdown);
        }
        #endregion
        #region Error
        public static MessageBoxResult ErrorMessage(Window owner,
        public static MessageBoxResult ErrorMessage(DependencyObject owner,
            string message,
            string caption = "错误",
            MessageBoxButton button = MessageBoxButton.OKCancel,
@@ -28,7 +29,18 @@
            DefaultButton defaultButton = DefaultButton.Unset, int? countdown = null)
        {
            var setting = Application.Current.FindResource("MsgBoxError") as MessageBoxXSetting;
            return MessageBoxX.Show(owner, message, caption, button, icon, defaultButton, setting, countdown);
            return ShowMessage(owner, message, caption, button, icon, defaultButton, setting, countdown);
        }
        public static MessageBoxResult Error(DependencyObject owner,
            string message,
            string caption = "错误",
            MessageBoxButton button = MessageBoxButton.OK,
            MessageBoxIcon icon = MessageBoxIcon.Error,
            DefaultButton defaultButton = DefaultButton.Unset, int? countdown = null)
        {
            var setting = Application.Current.FindResource("MsgBoxError") as MessageBoxXSetting;
            return ShowMessage(owner, message, caption, button, icon, defaultButton, setting, countdown);
        }
        #endregion
@@ -41,8 +53,31 @@
            DefaultButton defaultButton = DefaultButton.Unset, int? countdown = null)
        {
            var setting = Application.Current.FindResource("MsgBoxInfo") as MessageBoxXSetting;
            return MessageBoxX.Show(owner, message, caption, button, icon, defaultButton, setting, countdown);
            return ShowMessage(owner, message, caption, button, icon, defaultButton, setting, countdown);
        }
        #endregion
        public static MessageBoxResult ShowMessage(DependencyObject owner,
            string message,
            string caption,
            MessageBoxButton button,
            MessageBoxIcon icon,
            DefaultButton defaultButton, MessageBoxXSetting setting, int? countdown)
        {
            MessageBoxResult res = MessageBoxResult.None;
            Application.Current.Dispatcher.Invoke(() =>
            {
                if (owner is Window window)
                {
                    res = MessageBoxX.Show(window, message, caption, button, icon, defaultButton, setting, countdown);
                }
                else
                {
                    window = Window.GetWindow(owner);
                    res = MessageBoxX.Show(window, message, caption, button, icon, defaultButton, setting, countdown);
                }
            });
            return res;
        }
    }
}
HMI.UI/Util/ToastHelper.cs
@@ -1,5 +1,6 @@
using Panuon.WPF.UI;
using System.Windows;
using System.Windows.Controls;
namespace HMI.UI.Util
{
@@ -12,6 +13,18 @@
                ToastWindow targetWindow);
        void Warning(Window window,
                string message,
                MessageBoxIcon icon,
                ToastPosition position, double offset, int durationMs,
                ToastWindow targetWindow);
        void Error(Window window,
                string message,
                MessageBoxIcon icon,
                ToastPosition position, double offset, int durationMs,
                ToastWindow targetWindow);
        void Info(Window window,
                string message,
                MessageBoxIcon icon,
                ToastPosition position, double offset, int durationMs,
@@ -44,5 +57,100 @@
        {
            Instance.Warning(window, message, icon, position, offset, durationMs, targetWindow);
        }
        public static void Error(Window window,
            string message,
            MessageBoxIcon icon = MessageBoxIcon.Error,
            ToastPosition position = ToastPosition.Top, double offset = 0, int durationMs = 3000,
            ToastWindow targetWindow = ToastWindow.ActiveWindow)
        {
            Instance.Error(window, message, icon, position, offset, durationMs, targetWindow);
        }
        public static void Info(Window window,
            string message,
            MessageBoxIcon icon = MessageBoxIcon.Info,
            ToastPosition position = ToastPosition.Top, double offset = 0, int durationMs = 3000,
            ToastWindow targetWindow = ToastWindow.ActiveWindow)
        {
            Instance.Info(window, message, icon, position, offset, durationMs, targetWindow);
        }
    }
    public static class ToastUtil
    {
        public static void Success(this Window window,
            string message,
            MessageBoxIcon icon = MessageBoxIcon.Success,
            ToastPosition position = ToastPosition.Top, double offset = 0, int durationMs = 3000,
            ToastWindow targetWindow = ToastWindow.ActiveWindow)
        {
            ToastHelper.Success(window, message, icon, position, offset, durationMs, targetWindow);
        }
        public static void Warning(this Window window,
            string message,
            MessageBoxIcon icon = MessageBoxIcon.Warning,
            ToastPosition position = ToastPosition.Top, double offset = 0, int durationMs = 3000,
            ToastWindow targetWindow = ToastWindow.ActiveWindow)
        {
            ToastHelper.Warning(window, message, icon, position, offset, durationMs, targetWindow);
        }
        public static void Error(this Window window,
            string message,
            MessageBoxIcon icon = MessageBoxIcon.Error,
            ToastPosition position = ToastPosition.Top, double offset = 0, int durationMs = 3000,
            ToastWindow targetWindow = ToastWindow.ActiveWindow)
        {
            ToastHelper.Error(window, message, icon, position, offset, durationMs, targetWindow);
        }
        public static void Info(this Window window,
            string message,
            MessageBoxIcon icon = MessageBoxIcon.Info,
            ToastPosition position = ToastPosition.Top, double offset = 0, int durationMs = 3000,
            ToastWindow targetWindow = ToastWindow.ActiveWindow)
        {
            ToastHelper.Info(window, message, icon, position, offset, durationMs, targetWindow);
        }
        public static void Success(this UserControl control,
            string message,
            MessageBoxIcon icon = MessageBoxIcon.Success,
            ToastPosition position = ToastPosition.Top, double offset = 0, int durationMs = 3000,
            ToastWindow targetWindow = ToastWindow.ActiveWindow)
        {
            ToastHelper.Success(Window.GetWindow(control), message, icon, position, offset, durationMs, targetWindow);
        }
        public static void Warning(this UserControl control,
            string message,
            MessageBoxIcon icon = MessageBoxIcon.Warning,
            ToastPosition position = ToastPosition.Top, double offset = 0, int durationMs = 3000,
            ToastWindow targetWindow = ToastWindow.ActiveWindow)
        {
            ToastHelper.Warning(Window.GetWindow(control), message, icon, position, offset, durationMs, targetWindow);
        }
        public static void Error(this UserControl control,
            string message,
            MessageBoxIcon icon = MessageBoxIcon.Error,
            ToastPosition position = ToastPosition.Top, double offset = 0, int durationMs = 3000,
            ToastWindow targetWindow = ToastWindow.ActiveWindow)
        {
            ToastHelper.Error(Window.GetWindow(control), message, icon, position, offset, durationMs, targetWindow);
        }
        public static void Info(this UserControl control,
           string message,
           MessageBoxIcon icon = MessageBoxIcon.Error,
           ToastPosition position = ToastPosition.Top, double offset = 0, int durationMs = 3000,
           ToastWindow targetWindow = ToastWindow.ActiveWindow)
        {
            ToastHelper.Info(Window.GetWindow(control), message, icon, position, offset, durationMs, targetWindow);
        }
    }
}
HMI.UnitTest/MainWindow.xaml.cs
@@ -25,7 +25,7 @@
        {
            InitializeComponent();
            page.Children.Clear();
            page.Children.Add(new SettingControl());
            //page.Children.Add(new SettingControl());
        }
    }
}
Libs/OpcInterface.dll
Binary files differ
Libs/SimensDriver.dll
Binary files differ
WPFProject/App.config
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    </startup>
    <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;" />
@@ -18,6 +18,10 @@
                <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
                <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
            </dependentAssembly>
            <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>
WPFProject/App.xaml.cs
@@ -1,14 +1,8 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using HMI.UI.Util;
using System.Windows.Markup;
using HMI.UI.Util;
using log4net;
using Panuon.WPF.UI;
using System;
using System.Windows;
namespace WPFProject
{
@@ -26,10 +20,12 @@
        private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            log.Error("未知异常", e.ExceptionObject as Exception);
            var ex = e.ExceptionObject as Exception;
            log.Error("UnhandledException.", ex);
            Dispatcher.Invoke(() =>
            {
                MsgBoxHelper.ErrorMessage(Current.MainWindow, $"发生未知异常!程序即将关闭", "错误", MessageBoxButton.OK,
                MsgBoxHelper.ErrorMessage(Current.MainWindow, $"发生未知异常!程序即将关闭\r\n{ex?.Message}",
                    "错误", MessageBoxButton.OK,
                    MessageBoxIcon.Error, DefaultButton.YesOK, 5);
            });
        }
WPFProject/Controls/MainTabControl.xaml
@@ -13,29 +13,33 @@
            </ResourceDictionary.MergedDictionaries>
            <Style TargetType="ListBox" x:Key="ListBoxItemStyle1">
                <Setter Property="Background" Value="{StaticResource MainMenu}"/>
                <Setter Property="Background" Value="{StaticResource PrimaryColor}"/>
                <Setter Property="ItemContainerStyle">
                    <Setter.Value>
                        <Style TargetType="ListBoxItem">
                            <Setter Property="Margin" Value="0"/>
                            <Setter Property="Padding" Value="0"/>
                            <Setter Property="Foreground" Value="{StaticResource PrimaryColorContract}"/>
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="ListBoxItem">
                                        <Border x:Name="border" Background="{StaticResource MainMenu}"
                                                Padding="10" Margin="0" BorderThickness="0,0,1,0" BorderBrush="Gray">
                                        <Border x:Name="border" Background="{StaticResource PrimaryColor}"
                                                Padding="10" Margin="0" BorderThickness="0,0,1,1"
                                                BorderBrush="Gray">
                                            <!-- 内容呈现器 -->
                                            <StackPanel>
                                                <ContentPresenter  Content="{TemplateBinding Content}"
                                                <ContentPresenter x:Name="cp" Content="{TemplateBinding Content}"
                                                                   ContentStringFormat="{TemplateBinding ContentStringFormat}" 
                                                                   ContentTemplate="{TemplateBinding ContentTemplate}"  />
                                            </StackPanel>
                                        </Border>
                                        <ControlTemplate.Triggers>
                                            <Trigger Property="IsSelected" Value="True">
                                                <Setter TargetName="border" Property="Background" Value="{StaticResource ButtonSelected}" />
                                                <Setter TargetName="border" Property="Background"
                                                        Value="{StaticResource WorkspaceBackground}"/>
                                                <Setter Property="Foreground" Value="{StaticResource PrimaryColor}"/>
                                                <Setter TargetName="border" Property="BorderThickness" Value="0,0,0,0"/>
                                            </Trigger>
                                            <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=(ItemsControl.AlternationIndex)}" Value="0">
@@ -43,7 +47,8 @@
                                            </DataTrigger>
                                            <Trigger Property="IsMouseOver" Value="True">
                                                <Setter TargetName="border" Property="Background" Value="Red" />
                                                <Setter TargetName="border" Property="Background" Value="{StaticResource SecondPrimaryColor}" />
                                                <Setter Property="Foreground" Value="{StaticResource SecondPrimaryColorContract}" />
                                            </Trigger>
                                        </ControlTemplate.Triggers>
                                    </ControlTemplate>
@@ -64,8 +69,8 @@
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal" VerticalAlignment="Center" >
                                <Image Width="20" Height="20" Margin="5" Source="{Binding Icon}"/>
                                <Label x:Name="txt" Visibility="Visible" BorderThickness="0"
                                       Foreground="{StaticResource PrimaryColorContract}"
                                <Label x:Name="txt" BorderThickness="0"
                                       Foreground="{Binding Foreground,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListBoxItem}}"
                                       FontWeight="Bold" 
                                       FontSize="16"
                                       VerticalContentAlignment="Center" 
@@ -103,7 +108,7 @@
                 Visibility="{Binding MenuVisibility}"
                 SelectedItem="{Binding SelectedSubMenu}" AlternationCount="{Binding SubMenuItemsCount}"
                 SelectionChanged="SubMenu_SelectionChanged"
BorderThickness="0,0,0,1" ItemsSource="{Binding SubMenuItems}">
BorderThickness="0" ItemsSource="{Binding SubMenuItems}">
        </ListBox>
        <Border x:Name="page"  Grid.Row="2" BorderThickness="0" Margin="0" Padding="0">
WPFProject/Controls/MinInnerWindow.xaml
@@ -16,8 +16,8 @@
        </ResourceDictionary>
    </UserControl.Resources>
    <StackPanel Background="{StaticResource ChildBorder}" Orientation="Horizontal">
        <Label BorderBrush="{StaticResource MainBorder}" Foreground="#FFF" BorderThickness="2,0,0,0" VerticalContentAlignment="Center" ToolTip="{Binding Title}" Content="{Binding Title}"/>
    <StackPanel Background="{StaticResource SecondPrimaryColor}" Orientation="Horizontal">
        <Label BorderBrush="{StaticResource PrimaryColor}" Foreground="#FFF" BorderThickness="2,0,0,0" VerticalContentAlignment="Center" ToolTip="{Binding Title}" Content="{Binding Title}"/>
        <Button Style="{StaticResource ChildWindowButton}" Click="Max_Click" >&#xe602;</Button>
        <Button Style="{StaticResource ChildWindowCloseButton}" Click="CLose_Click">&#xe609;</Button>
    </StackPanel>
WPFProject/Libs/SQLite.Interop.dll
Binary files differ
WPFProject/LoadingWindow.xaml.cs
@@ -1,4 +1,5 @@
using System.Windows;
using System.Threading.Tasks;
using System.Windows;
using HMI.Base;
namespace WPFProject
@@ -23,7 +24,10 @@
        private void Loader_LoadMessageAppend(object sender, string e)
        {
            Console.Text += $"{e}\r\n";
            Dispatcher.BeginInvoke(() =>
            {
                Console.Text = Console.Text+$"{e}\r\n";
            });
        }
        private void Button_Click(object sender, RoutedEventArgs e)
WPFProject/MainWindow.xaml
@@ -54,6 +54,6 @@
        </ctl:MenuTabControl>-->
        <StatusBar Grid.Row="2" Background="{StaticResource MainStatusBar}">99999</StatusBar>
        <StatusBar Grid.Row="2" Background="{StaticResource PrimaryColor}">99999</StatusBar>
    </Grid>
</Window>
WPFProject/PanuonWindow.xaml
@@ -11,15 +11,15 @@
            WindowStartupLocation="CenterOwner"
            WindowState="Maximized"
            x:Name="window"
            BorderBrush="{StaticResource MainBorder}"
            BorderBrush="{StaticResource PrimaryColor}"
            Loaded="window_Loaded"
            pu:WindowXCaption.Background="{StaticResource MainBorder}"
            pu:WindowXCaption.Background="{StaticResource PrimaryColor}"
            pu:WindowXCaption.Foreground="#FFF" d:DesignHeight="115.146" d:DesignWidth="341.223">
    <pu:WindowXCaption.HeaderTemplate>
        <DataTemplate>
            <StackPanel DataContext="{Binding ElementName=window}" Orientation="Horizontal"
                    Margin="5,0,0,0">
                        Height="100" Margin="5,0,0,0">
                <Image Source="{Binding WindowLogo}"
                   RenderOptions.BitmapScalingMode="HighQuality"
                   Height="{StaticResource TitleFontSize}" />
@@ -53,6 +53,6 @@
        <!--<ctl:MenuTabControl Grid.Row="1" x:Name="menuTab" OnTabChange="menuTab_OnTabChange"> </ctl:MenuTabControl>-->
        <StatusBar Grid.Row="1" Background="{StaticResource MainStatusBar}">99999</StatusBar>
        <StatusBar Grid.Row="1" Background="{StaticResource PrimaryColor}">99999</StatusBar>
    </Grid>
</pu:WindowX>
WPFProject/PanuonWindow.xaml.cs
@@ -1,15 +1,13 @@
using System;
using HMI.Core.Setting;
using HMI.Core.Util;
using HMI.UI.Util;
using Panuon.WPF.UI;
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using HandyControl.Tools.Extension;
using HMI.Core.Api;
using HMI.Core.Setting;
using HMI.Core.Util;
using HMI.UI.Util;
using Panuon.WPF.UI;
using WPFProject.Utils;
namespace WPFProject
WPFProject/SQLite.Interop.dll
Binary files differ
WPFProject/TestWindow.xaml
@@ -32,6 +32,6 @@
        <!--<ctl:MenuTabControl Grid.Row="1" x:Name="menuTab" OnTabChange="menuTab_OnTabChange"> </ctl:MenuTabControl>-->
        <StatusBar Grid.Row="1" Background="{StaticResource MainStatusBar}">99999</StatusBar>
        <StatusBar Grid.Row="1" Background="{StaticResource PrimaryColor}">99999</StatusBar>
    </Grid>
</ui:BaseWindow>
WPFProject/Utils/MainToastHandler.cs
@@ -38,5 +38,33 @@
                Toast.Show(wx, message, icon, position, 0, durationMs, setting);
            }
        }
        public void Error(Window window, string message, MessageBoxIcon icon, ToastPosition position, double offset, int durationMs, ToastWindow targetWindow)
        {
            var setting = Application.Current.FindResource("toastError") as ToastSetting;
            if (window is PanuonWindow pw)
            {
                double num = 20 + (pw.menuTab.MenuVisibility == Visibility.Visible ? pw.menuTab.Menu.ActualHeight + pw.menuTab.SubMenu.ActualHeight : 0);
                Toast.Show(pw, message, icon, position, num, durationMs, setting);
            }
            else if (window is WindowX wx)
            {
                Toast.Show(wx, message, icon, position, 0, durationMs, setting);
            }
        }
        public void Info(Window window, string message, MessageBoxIcon icon, ToastPosition position, double offset, int durationMs, ToastWindow targetWindow)
        {
            var setting = Application.Current.FindResource("toastError") as ToastSetting;
            if (window is PanuonWindow pw)
            {
                double num = 20 + (pw.menuTab.MenuVisibility == Visibility.Visible ? pw.menuTab.Menu.ActualHeight + pw.menuTab.SubMenu.ActualHeight : 0);
                Toast.Show(pw, message, icon, position, num, durationMs, setting);
            }
            else if (window is WindowX wx)
            {
                Toast.Show(wx, message, icon, position, 0, durationMs, setting);
            }
        }
    }
}
WPFProject/WPFProject.csproj
@@ -328,6 +328,9 @@
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
    <Content Include="Libs\SQLite.Interop.dll">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
    <Content Include="log4net.config">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
@@ -350,13 +353,17 @@
    <Resource Include="Resources\Icon\xinzeng_1.png" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\HMI.AlermInfoManage\HMI.AlermInfoManage.csproj">
    <ProjectReference Include="..\HMI.AlermInfoManage\HMI.AlarmInfoManage.csproj">
      <Project>{945c2577-4558-45e3-891a-096cb3047304}</Project>
      <Name>HMI.AlermInfoManage</Name>
      <Name>HMI.AlarmInfoManage</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.Base\HMI.Base.csproj">
      <Project>{2a6f9720-a890-4036-9d73-41a0dae28341}</Project>
      <Name>HMI.Base</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.DataViewer\HMI.DataViewer.csproj">
      <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>
@@ -388,11 +395,13 @@
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
  </ItemGroup>
  <ItemGroup>
    <Folder Include="Config\" />
    <Folder Include="Plugin\" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <PropertyGroup>
    <PostBuildEvent>copy /Y "$(TargetDir)HMI.MenuManage.dll" "$(TargetDir)\Plugin\HMI.MenuManage.dll" 
copy /Y "$(TargetDir)HMI.MenuManage.pdb" "$(TargetDir)\Plugin\HMI.MenuManage.pdb"
copy /Y "$(TargetDir)HMI.AlermInfoManage.dll" "$(TargetDir)\Plugin\HMI.AlermInfoManage.dll"
copy /Y "$(TargetDir)HMI.AlermInfoManage.pdb" "$(TargetDir)\Plugin\HMI.AlermInfoManage.pdb"</PostBuildEvent>
copy /Y "$(TargetDir)HMI.MenuManage.pdb" "$(TargetDir)\Plugin\HMI.MenuManage.pdb"</PostBuildEvent>
  </PropertyGroup>
</Project>
WPFProject/WPFProject.sln
@@ -19,7 +19,7 @@
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.UnitTest", "..\HMI.UnitTest\HMI.UnitTest.csproj", "{325A7F52-FE51-44F0-B3AC-5F42F5356605}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.AlermInfoManage", "..\HMI.AlermInfoManage\HMI.AlermInfoManage.csproj", "{945C2577-4558-45E3-891A-096CB3047304}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.AlarmInfoManage", "..\HMI.AlermInfoManage\HMI.AlarmInfoManage.csproj", "{945C2577-4558-45E3-891A-096CB3047304}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.SettingControl", "..\HMI.SettingControl\HMI.SettingControl.csproj", "{3DD748AC-9708-49AD-B846-659214035588}"
EndProject
@@ -30,6 +30,8 @@
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}") = "CommonLibs", "..\..\HMI.Old\Framework\CommonLibs\CommonLibs\CommonLibs.csproj", "{0212E104-4E85-4F9F-AA70-FC4FAC25C3E5}"
EndProject
Global
    GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -121,8 +123,8 @@
        {3DD748AC-9708-49AD-B846-659214035588}.Release|x64.Build.0 = Release|Any CPU
        {2A6F9720-A890-4036-9D73-41A0DAE28341}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {2A6F9720-A890-4036-9D73-41A0DAE28341}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {2A6F9720-A890-4036-9D73-41A0DAE28341}.Debug|x64.ActiveCfg = Debug|Any CPU
        {2A6F9720-A890-4036-9D73-41A0DAE28341}.Debug|x64.Build.0 = Debug|Any CPU
        {2A6F9720-A890-4036-9D73-41A0DAE28341}.Debug|x64.ActiveCfg = Debug|x64
        {2A6F9720-A890-4036-9D73-41A0DAE28341}.Debug|x64.Build.0 = Debug|x64
        {2A6F9720-A890-4036-9D73-41A0DAE28341}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {2A6F9720-A890-4036-9D73-41A0DAE28341}.Release|Any CPU.Build.0 = Release|Any CPU
        {2A6F9720-A890-4036-9D73-41A0DAE28341}.Release|x64.ActiveCfg = Release|Any CPU
@@ -151,6 +153,14 @@
        {736CD77D-B81E-4866-BB00-84B0B57110F0}.Release|Any CPU.Build.0 = Release|Any CPU
        {736CD77D-B81E-4866-BB00-84B0B57110F0}.Release|x64.ActiveCfg = Release|Any CPU
        {736CD77D-B81E-4866-BB00-84B0B57110F0}.Release|x64.Build.0 = Release|Any CPU
        {0212E104-4E85-4F9F-AA70-FC4FAC25C3E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {0212E104-4E85-4F9F-AA70-FC4FAC25C3E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {0212E104-4E85-4F9F-AA70-FC4FAC25C3E5}.Debug|x64.ActiveCfg = Debug|x64
        {0212E104-4E85-4F9F-AA70-FC4FAC25C3E5}.Debug|x64.Build.0 = Debug|x64
        {0212E104-4E85-4F9F-AA70-FC4FAC25C3E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {0212E104-4E85-4F9F-AA70-FC4FAC25C3E5}.Release|Any CPU.Build.0 = Release|Any CPU
        {0212E104-4E85-4F9F-AA70-FC4FAC25C3E5}.Release|x64.ActiveCfg = Release|x64
        {0212E104-4E85-4F9F-AA70-FC4FAC25C3E5}.Release|x64.Build.0 = Release|x64
    EndGlobalSection
    GlobalSection(SolutionProperties) = preSolution
        HideSolutionNode = FALSE
WPFProject/test.xml
File was deleted