alielie
2025-03-17 c401a2f2fd1a509cdeb33e850bb5c2827074af48
完善框架,新增两个close方法;
46个文件已修改
4个文件已添加
1140 ■■■■■ 已修改文件
HMI.Base/PluginControl.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Base/PluginLoader.cs 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Api/IPlugin.cs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Api/IPluginContainer.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Entity/UserInfo.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/HMI.Core.csproj 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Setting/SystemSetting.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Util/JsonUtil.cs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Util/LangUtil.cs 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/MenuManage.xaml.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Modal/IconSelector.xaml.cs 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/RoleManage.xaml.cs 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/UserManage.xaml.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/HMI.UI.csproj 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Resources/Font/iconfont.json 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Resources/Font/iconfont.ttf 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/PanuonResource.xaml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Util/MsgBoxHelper.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Util/HMI.Util.csproj 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/DataTable.xaml.cs 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/InfoTable.xaml.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/Plugin.TestResult.csproj 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/SingleCell.xaml.cs 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/TestResultView.xaml.cs 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/ViewControl.xaml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/ViewControl.xaml.cs 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TsExecution/Plugin.TsExecution.csproj 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TsExecution/SingleControl.xaml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TsExecution/SingleControl.xaml.cs 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TsExecution/TsExecution.xaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TsExecution/TsExecution.xaml.cs 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/App.xaml 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/App.xaml.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Lang/en-US/ParamManage.json 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Lang/en-US/TestExecution.json 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Lang/en-US/app.json 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Lang/zh-CN/ParamManage.json 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Lang/zh-CN/TestExecution.json 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Lang/zh-CN/app.json 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Libs/SuperSocket.SocketEngine.dll 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/LoadingWindow.xaml.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Login.xaml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Login.xaml.cs 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/PanuonWindow.xaml 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/PanuonWindow.xaml.cs 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/TextFile1.txt 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Utils/BinUtil.cs 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Utils/Encryptor.cs 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/WPFProject.csproj 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/WPFProject.sln 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Base/PluginControl.cs
@@ -1,4 +1,5 @@
using System.ComponentModel;
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Controls;
using HMI.Core.Api;
@@ -64,5 +65,15 @@
        {
            
        }
        public virtual void OnClose(object sender, CancelEventArgs e)
        {
        }
        public virtual void DoClose(object sender, EventArgs e)
        {
        }
    }
}
HMI.Base/PluginLoader.cs
@@ -44,6 +44,14 @@
            sqliteHelper = new SqliteHelper(PathHelper.DatabasePath);
        }
        public static void Reset()
        {
            if (Instance != null)
            {
                Instance = null;
            }
        }
        public static PluginLoader GetLoader()
        {
            if (Instance == null)
@@ -53,79 +61,29 @@
            return Instance;
        }
        public void LoadMenuAndPlugin(Dispatcher dispatcher)
        private IPluginContainer pluginContainer;
        public void LoadMenuAndPlugin(IPluginContainer container, Dispatcher dispatcher)
        {
            pluginContainer = container;
            if (Menus == null)
            {
                Plugins = new List<PluginInfo>();
                Menus = LoadTreeMenu(dispatcher);
                sqliteHelper.Close();
                dispatcher.Invoke(() =>
                {
                    LoadMessageAppend?.Invoke(this, new LoadMsg { Type = LoadMsgType.FINISHED });
                });
            }
        }
        private void LoadChildrenMenu(PluginInfo menu, Dispatcher dispatcher)
        {
            LoadMessageAppend?.Invoke(this, new LoadMsg
            {
                Type = LoadMsgType.LOADING,
                Message = $"正在加载{menu.Name}菜单..."
            });
            try
            {
                Application.Current.Dispatcher.Invoke(() =>
                {
                    if (!string.IsNullOrEmpty(menu.DllPath))
                    {
                        string dllPath = Path.Combine(PluginFolder, menu.DllPath);
                        Assembly ass = Assembly.LoadFrom(dllPath);
                        Type ClassType = ass.GetType(menu.ClassName);
                        object Instance = Activator.CreateInstance(ClassType);
                        menu.Content = Instance;
                        if (Instance is IPlugin plugin)
                        {
                            plugin.SetLogger(LogManager.GetLogger(menu.Code));
                            string configFolder = Path.Combine(PathHelper.ConfigFolder, menu.Code);
                            if (!Directory.Exists(configFolder))
                            {
                                Directory.CreateDirectory(configFolder);
                            }
                            plugin.SetBaseFolder(configFolder);
                            plugin.InitFinish();
                        }
                        Type SettingType = ass.GetType($"{menu.ClassName}_Setting");
                        if (SettingType != null)
                        {
                            object SettingInstance = Activator.CreateInstance(SettingType);
                            menu.SettingControl = SettingInstance;
                        }
                    }
                });
                Thread.Sleep(100);
            }
            catch (Exception ex)
            {
                LoadMessageAppend?.Invoke(this,
                     new LoadMsg
                     {
                         Type = LoadMsgType.LOADING,
                         Result = false,
                         Message = $"{ex.Message} {ex.StackTrace}"
                     });
            }
        }
        private void LoadChildrenMenu()
        {
            foreach (var menu in Plugins)
            {
                CountdownEvent c = new CountdownEvent(1);
                Thread.Sleep(100);
                Thread.Sleep(80);
                Application.Current.Dispatcher.Invoke(new Action(() =>
                {
                    try
                    {
                        LoadMessageAppend?.Invoke(this,
@@ -152,6 +110,8 @@
                                }
                                plugin.SetBaseFolder(configFolder);
                                plugin.InitFinish();
                                pluginContainer.OnCloseEvent += plugin.OnClose;
                                pluginContainer.DoCloseEvent += plugin.DoClose;
                                Type SettingType = ass.GetType($"{menu.ClassName}_Setting");
                                if (SettingType != null)
                                {
HMI.Core/Api/IPlugin.cs
@@ -1,4 +1,6 @@
using log4net;
using System;
using System.ComponentModel;
namespace HMI.Core.Api
{
@@ -10,12 +12,17 @@
        /// <returns></returns>
        object Init();
        /// <summary>
        /// 初始化完成(执行完构造器)
        /// </summary>
        void InitFinish();
        /// <summary>
        /// 插件面板被激活时的操作
        /// </summary>
        /// <returns></returns>
        object Active();
        /// <summary>
        /// 失活时的操作
        /// </summary>
@@ -30,5 +37,17 @@
        void OnTabChange(object sender, object args);
        void SetLogger(ILog logger);
        /// <summary>
        /// 将要关闭程序事件
        /// </summary>
        void OnClose(object sender, CancelEventArgs e);
        /// <summary>
        /// 关闭程序事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void DoClose(object sender, EventArgs e);
    }
}
HMI.Core/Api/IPluginContainer.cs
New file
@@ -0,0 +1,12 @@
using System;
using System.ComponentModel;
namespace HMI.Core.Api
{
    public interface IPluginContainer
    {
        event CancelEventHandler OnCloseEvent;
        event EventHandler DoCloseEvent;
        event EventHandler LoadComplateEvent;
    }
}
HMI.Core/Entity/UserInfo.cs
@@ -2,17 +2,19 @@
{
    public class UserInfo
    {
        public static UserInfo User { get; private set; } = new UserInfo("", "admin", "");
        public UserInfo(string id, string username, string name)
        public static UserInfo User { get; private set; }
        public UserInfo(string id, string username, string name, string password = null)
        {
            Id = id;
            Username = username;
            Name = name;
            Password = password;
        }
        public string Id { get; private set; }
        public string Username { get; private set; }
        public string Name { get; private set; }
        public string Password { get; private set; }
        public static void Login(UserInfo info)
        {
HMI.Core/HMI.Core.csproj
@@ -90,6 +90,7 @@
    <Compile Include="Api\IConfigRefresh.cs" />
    <Compile Include="Api\IFolderOwner.cs" />
    <Compile Include="Api\IPlugin.cs" />
    <Compile Include="Api\IPluginContainer.cs" />
    <Compile Include="Api\IProgram.cs" />
    <Compile Include="Api\ISequnce.cs" />
    <Compile Include="Api\IStep.cs" />
HMI.Core/Setting/SystemSetting.cs
@@ -47,6 +47,11 @@
            }
        }
        public static SystemSettingModel GetModel()
        {
            return XmlHelper.DeserializeFromXml<SystemSettingModel>(FilePath);
        }
        public static void ReLoad()
        {
            if (!File.Exists(FilePath))
HMI.Core/Util/JsonUtil.cs
@@ -1,11 +1,26 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IO;
namespace HMI.Core.Util
{
    public class JsonUtil
    {
        static readonly JsonSerializerSettings JsSetting = new JsonSerializerSettings()
        {
            NullValueHandling = NullValueHandling.Ignore
        };
        public static T Deserialize<T>(string jsonStr)
        {
            return JsonConvert.DeserializeObject<T>(jsonStr);
        }
       
        public static string Serialize(object obj)
        {
            return JsonConvert.SerializeObject(obj);
        }
        public static string Stringfiy(object obj)
        {
            return JsonConvert.SerializeObject(obj, Formatting.Indented, JsSetting);
        }
    }
}
HMI.Core/Util/LangUtil.cs
@@ -6,7 +6,6 @@
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices.ComTypes;
using System.Windows;
using System.Windows.Markup;
HMI.MenuManage/MenuManage.xaml.cs
@@ -5,6 +5,7 @@
using HMI.UI.Util;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
@@ -133,6 +134,18 @@
            }
        }
        public override void DoClose(object sender, EventArgs e)
        {
            try
            {
                Model?.sqliteHelper?.Close();
            }
            catch (Exception ex)
            {
                log.Warn("Do Close Failed.", ex);
            }
        }
        private void DataGrid_Loaded(object sender, RoutedEventArgs e)
        {
            Model.LoadData();
HMI.MenuManage/Modal/IconSelector.xaml.cs
@@ -1,22 +1,13 @@
using HMI.Core.Annotation;
using HMI.Core.Util;
using HMI.MenuManage.Entity;
using HMI.UI.DpUtil;
using Microsoft.Win32;
using HMI.MenuManage.Entity;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Panuon.WPF.UI;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Xml.Linq;
namespace HMI.MenuManage
{
HMI.MenuManage/RoleManage.xaml.cs
@@ -35,6 +35,17 @@
            this.Loaded -= RoleManage_Loaded;
        }
        public override void DoClose(object sender, EventArgs e)
        {
            try
            {
                Model?.sqliteHelper?.Close();
            }
            catch (Exception ex)
            {
                log.Warn("Do Close Failed.", ex);
            }
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            try
HMI.MenuManage/UserManage.xaml.cs
@@ -32,6 +32,18 @@
            this.Loaded -= UserManage_Loaded;
        }
        public override void DoClose(object sender, EventArgs e)
        {
            try
            {
                Model?.sqliteHelper?.Close();
            }
            catch (Exception ex)
            {
                log.Warn("Do Close Failed.", ex);
            }
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Button btn = (Button)sender;
HMI.UI/HMI.UI.csproj
@@ -161,7 +161,7 @@
      <Generator>SettingsSingleFileGenerator</Generator>
      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
    </None>
    <Resource Include="Resources\Font\iconfont.json" />
    <EmbeddedResource Include="Resources\Font\iconfont.json" />
    <Resource Include="Resources\Font\iconfont.ttf" />
  </ItemGroup>
  <ItemGroup>
HMI.UI/Resources/Font/iconfont.json
@@ -6,6 +6,34 @@
  "description": "",
  "glyphs": [
    {
      "icon_id": "1376055",
      "name": "退出登录",
      "font_class": "tuichudenglu",
      "unicode": "e61d",
      "unicode_decimal": 58909
    },
    {
      "icon_id": "7095289",
      "name": "重启",
      "font_class": "zhongqi",
      "unicode": "e867",
      "unicode_decimal": 59495
    },
    {
      "icon_id": "30661341",
      "name": "PLC信息管理",
      "font_class": "a-PLCinformationmanagement",
      "unicode": "e63e",
      "unicode_decimal": 58942
    },
    {
      "icon_id": "12889884",
      "name": "系统管理-菜单管理",
      "font_class": "xitongguanli-caidanguanli",
      "unicode": "e628",
      "unicode_decimal": 58920
    },
    {
      "icon_id": "12939974",
      "name": "编辑",
      "font_class": "bianji1",
HMI.UI/Resources/Font/iconfont.ttf
Binary files differ
HMI.UI/Style/PanuonResource.xaml
@@ -281,9 +281,9 @@
        <Setter Property="pu:WindowXCaption.Buttons" Value="Close"/>
        <Setter Property="pu:WindowXModalDialog.Buttons" Value="OKCancel"/>
        <Setter Property="pu:WindowXModalDialog.CancelButtonContent" 
                Value="{Binding Converter={StaticResource lc},ConverterParameter='App.Cancel'}"/>
                Value="{DynamicResource App.Cancel}"/>
        <Setter Property="pu:WindowXModalDialog.OKButtonContent" 
                Value="{Binding Converter={StaticResource lc},ConverterParameter='App.Save'}"/>
                Value="{DynamicResource App.Save}"/>
        <Setter Property="pu:WindowXModalDialog.ButtonPanelMargin" Value="0,30,10,10"/>
    </Style>
</ResourceDictionary>
HMI.UI/Util/MsgBoxHelper.cs
@@ -71,7 +71,7 @@
            MessageBoxResult res = MessageBoxResult.None;
            if (!(owner is Window window))
            {
                window = Window.GetWindow(owner);
                window = Window.GetWindow(owner) ?? Application.Current.MainWindow;
            }
            window.Dispatcher.Invoke(() =>
            {
HMI.Util/HMI.Util.csproj
@@ -30,6 +30,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="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>
Plugin.TestResult/DataTable.xaml.cs
@@ -1,4 +1,5 @@
using HMI.Core.Util;
using HMI.Util;
using Plugin.TestResult_WPF.Model;
using SuperSocket.SocketBase.Protocol;
using System;
@@ -73,9 +74,13 @@
        private SingleCell[] cells;
        private TcpServer tcpServer;
        private string CountIniPath;
        public DataTable(TestResultView parent,int index, string title, BaseConfig config)
        public DataTable(TestResultView parent, int index, string title,
            string countIniPath,
            BaseConfig config)
        {
            this.CountIniPath = countIniPath;
            this.config = config;
            this.index = index;
            this.parent = parent;
@@ -86,7 +91,8 @@
            DataContext = this;
            InitializeComponent();
            this.chart = new Chart(this.chartTitle, false, config.MaxSize, config.RefreshInterval);
            this.multiChart = new Chart(config.DetailChartTitle, true, config.MaxSize, config.RefreshInterval) {
            this.multiChart = new Chart(config.DetailChartTitle, true, config.MaxSize, config.RefreshInterval)
            {
            BorderThickness = new Thickness(0)
            };
            this.grid.Children.Add(this.chart);
@@ -99,6 +105,7 @@
                mainGrid.Children.Add(cell);
                cells[i] = cell;
            }
            ReadCount();
        }
        public int GetSize()
@@ -196,6 +203,9 @@
            multiChart.Clear();
        }
        /// <summary>
        /// 启动Tcp服务器
        /// </summary>
        private void StartTcpServer()
        {
            try
@@ -204,6 +214,20 @@
                {
                    tcpServer = new TcpServer();
                    tcpServer.NewRequestReceived += TcpServer_NewRequestReceived;
                    if (!tcpServer.Setup(config.PortStart + index))
                    {
                        ChangeTcpServerStatus("ERROR", this.lang("Tr.TcpServer.SettingFailed"));
                        return;
                    }
                    if (!tcpServer.Start())
                    {
                        ChangeTcpServerStatus("ERROR", this.lang("Tr.TcpServer.StartFailed"));
                        return;
                    }
                    ChangeTcpServerStatus("RUNNING", this.lang("Tr.TcpServer.Running"));
                }
                else if (tcpServer.State != SuperSocket.SocketBase.ServerState.Running)
                {
                    if (!tcpServer.Setup(config.PortStart + index))
                    {
                        ChangeTcpServerStatus("ERROR", this.lang("Tr.TcpServer.SettingFailed"));
@@ -225,20 +249,11 @@
            }
        }
        private void StopTcpServer()
        {
            if (tcpServer == null) return;
            try
            {
                tcpServer.Stop();
                tcpServer = null;
            }
            catch (Exception ex)
            {
            }
        }
        /// <summary>
        /// 更新Tcp服务器状态
        /// </summary>
        /// <param name="status"></param>
        /// <param name="info"></param>
        private void ChangeTcpServerStatus(string status, string info)
        {
            Dispatcher.Invoke(() =>
@@ -248,6 +263,35 @@
            });
        }
        /// <summary>
        /// 停止Tcp服务器
        /// </summary>
        private void StopTcpServer()
        {
            try
            {
                tcpServer?.Stop();
                tcpServer = null;
            }
            catch (Exception ex)
            {
            }
        }
        /// <summary>
        /// 释放资源
        /// </summary>
        public void RelaseResource()
        {
            StopTcpServer();
        }
        /// <summary>
        /// Tcp消息处理
        /// </summary>
        /// <param name="session"></param>
        /// <param name="requestInfo"></param>
        private void TcpServer_NewRequestReceived(TcpSession session, StringRequestInfo requestInfo)
        {
            string msg = requestInfo.Body;
@@ -268,7 +312,45 @@
                condition = values[4],
                updateTime = values[5]
            });
            session.Send("1");
            //session.Send("1");
        }
        public void RefreshCount(int ok, int ng)
        {
            infoTable.UpdateNumber(infoTable.okCount + ok, infoTable.ngCount + ng);
            IniFileHelper.WriteIniData($"Oven_{index}", "Pass", infoTable.okCount.ToString(),
                this.CountIniPath);
            IniFileHelper.WriteIniData($"Oven_{index}", "Fail", infoTable.ngCount.ToString(),
                this.CountIniPath);
        }
        /// <summary>
        /// 读取保存的数据
        /// </summary>
        public void ReadCount()
        {
            try
            {
                int ok = Convert.ToInt32(IniFileHelper.ReadIniData($"Oven_{index}", "Pass", "", CountIniPath));
                int ng = Convert.ToInt32(IniFileHelper.ReadIniData($"Oven_{index}", "Fail", "", CountIniPath));
                infoTable.UpdateNumber(ok, ng);
            }
            catch (Exception ex)
            {
                //do nothing
            }
        }
        /// <summary>
        /// 计数清零
        /// </summary>
        public void ClearNumber()
        {
            infoTable.UpdateNumber(0, 0);
            IniFileHelper.WriteIniData($"Oven_{index}", "Pass", infoTable.okCount.ToString(),
                this.CountIniPath);
            IniFileHelper.WriteIniData($"Oven_{index}", "Fail", infoTable.ngCount.ToString(),
                this.CountIniPath);
        }
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
Plugin.TestResult/InfoTable.xaml.cs
@@ -1,4 +1,5 @@
using Plugin.TestResult_WPF.Constant;
using HMI.Util;
using Plugin.TestResult_WPF.Constant;
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
@@ -66,7 +67,6 @@
            set
            {
                RaiseAndSetIfchanged(ref _okCount, value);
                RefreshCount();
            }
        }
@@ -78,7 +78,6 @@
            set
            {
                RaiseAndSetIfchanged(ref _ngCount, value);
                RefreshCount();
            }
        }
@@ -137,10 +136,11 @@
            set => RaiseAndSetIfchanged(ref _endTimeStr, value);
        }
        private void RefreshCount()
        public void UpdateNumber(int ok, int ng)
        {
            this.allCount = this.okCount + this.ngCount;
            this._okCount = ok;
            this._ngCount = ng;
            this._allCount = this._okCount + this._ngCount;
            RaisePropertyChanged("countStr");
        }
Plugin.TestResult/Plugin.TestResult.csproj
@@ -52,9 +52,6 @@
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="CommonLibs">
      <HintPath>..\Libs\CommonLibs.dll</HintPath>
    </Reference>
    <Reference Include="GLWpfControl, Version=3.3.0.0, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\OpenTK.GLWpfControl.3.3.0\lib\net452\GLWpfControl.dll</HintPath>
    </Reference>
@@ -317,6 +314,10 @@
      <Project>{f17de6b5-af1d-48c6-94a0-6b763a6546a6}</Project>
      <Name>HMI.UI</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.Util\HMI.Util.csproj">
      <Project>{AFEA100A-9223-448D-AB10-3015D93DD98C}</Project>
      <Name>HMI.Util</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <Import Project="..\WPFProject\packages\HarfBuzzSharp.NativeAssets.macOS.7.3.0.3\build\net462\HarfBuzzSharp.NativeAssets.macOS.targets" Condition="Exists('..\WPFProject\packages\HarfBuzzSharp.NativeAssets.macOS.7.3.0.3\build\net462\HarfBuzzSharp.NativeAssets.macOS.targets')" />
Plugin.TestResult/SingleCell.xaml.cs
@@ -145,31 +145,34 @@
            if (window != null) window.UpdateSource(this.infos);
        }
        public void SetInfos(string[,] infos)
        {
            var data = new ObservableCollection<Info>();
            for (int i = 0; i < infos.GetLength(0); i++)
            {
                data.Add(new Info
                {
                    step = infos[i, 0],
                    result = infos[i, 1]
                });
            }
            this.infos = data;
            if (window != null) window.UpdateSource(this.infos);
        }
        public void SetInfos(Info info)
        {
            if (queue.Count >= maxSize)
            {
                queue.TryDequeue(out var first);
            }
            queue.Enqueue(info);
            var list = new ObservableCollection<Info>(queue);
            this.infos = list;
                this.infos = new ObservableCollection<Info>(queue);
            if (window != null) window.UpdateSource(this.infos);
            }
            else
            {
                queue.Enqueue(info);
                if (window != null)
                {
                    window.Dispatcher.Invoke(() =>
                    {
                        this.infos.Add(info);
                    });
                }
                else
                {
                    Dispatcher.Invoke(() =>
                    {
                        this.infos.Add(info);
                    });
                }
                return;
            }
        }
        public void ClearInfo()
@@ -180,16 +183,5 @@
            if (window != null) window.UpdateSource(this.infos);
        }
        public void SetInfos(string step, string result)
        {
            if (queue.Count >= maxSize)
            {
                queue.TryDequeue(out var info);
            }
            queue.Enqueue(new Info() { step = step, result = result });
            var list = new ObservableCollection<Info>(queue);
            this.infos = list;
            if (window != null) window.UpdateSource(this.infos);
        }
    }
}
Plugin.TestResult/TestResultView.xaml.cs
@@ -72,7 +72,7 @@
            ((System.ComponentModel.ISupportInitialize)(this.testApp)).BeginInit();//myClient为第三方控件名
            host.Child = testApp;
            ((System.ComponentModel.ISupportInitialize)(this.testApp)).EndInit();
            InitMainGrid();
            //InitTestStandEngine();
            //StartTestSequence();
        }
@@ -106,11 +106,21 @@
                    var cell = table.GetSingleCell(random.Next(table.GetSize()));
                    cell.SetStatus(CommonStatus.GetRandomStatus());
                    cell.SetInfos(random.Next(1000) + "", random.Next(1000) + "");
                    cell.SetInfos(new Info
                    {
                        step = random.Next(1000) + "",
                        value = random.Next(1000) + ""
                    });
                    table.SetStatusText(DateTime.Now + "");
                };
                sb.Start();
            }
        }
        private string CountIniPath;
        public void SetCountIniPath(string countIniPath)
        {
            CountIniPath = countIniPath;
        }
        public void InitMainGrid()
@@ -131,7 +141,7 @@
            for (int n = 0; n < cellCount; n++)
            {
                string name = n < names.Length ? names[n] : "";
                DataTable child = new DataTable(this, n, name, config);
                DataTable child = new DataTable(this, n, name, CountIniPath, config);
                mainGrid.Children.Add(child);
                tables[n] = child;
                nameTables[name] = child;
@@ -319,6 +329,16 @@
                    int cellIndex = int.Parse(datas_10015[1]);
                    string status_10015 = datas_10015[2];
                    tables[ovenIndex].GetSingleCell(cellIndex).SetStatus(status_10015);
                    if (status_10015 == "OK" || status_10015 == "ok")
                    {
                        tables[ovenIndex].RefreshCount(1, 0);
                    }
                    else if (status_10015 == "ng" || status_10015 == "NG")
                    {
                        tables[ovenIndex].RefreshCount(0, 1);
                    }
                    return;
                }
                if (MethodCode == 7)
@@ -381,9 +401,23 @@
            //bool val = this.SequenceContext.StationGlobals.GetValBoolean(key, 0);
        }
        public void SetSnrToSeqContext(int index, string snr)
        public void ReleaseResource()
        {
            foreach (var table in tables)
            {
                table.RelaseResource();
            }
        }
        /// <summary>
        /// 计数清零
        /// </summary>
        public void ClearNumber()
        {
            foreach (var table in tables)
            {
                table.ClearNumber();
            }
        }
    }
}
Plugin.TestResult/ViewControl.xaml
@@ -100,6 +100,9 @@
                    <Button x:Name="btnStop" Click="Stop_Click" Style="{StaticResource DangerButton}"
                            Content="{DynamicResource App.Stop}"/>
                    <Button x:Name="btnClear" Click="BtnClear_Click" Style="{StaticResource PrimaryButton}"
                            Content="{DynamicResource Ts.BtnClearNum}"/>
                    <Button x:Name="btnScan1" Click="Scan_Click_1" Style="{StaticResource PrimaryButton}"
                            Content="{DynamicResource Tr.Zone1Scan}"/>
Plugin.TestResult/ViewControl.xaml.cs
@@ -2,6 +2,7 @@
using HMI.Core.Util;
using HMI.ParameterManage.Entity;
using HMI.ParameterManage.Util;
using HMI.UI;
using HMI.UI.Util;
using log4net;
using NationalInstruments.TestStand.Interop.API;
@@ -10,6 +11,8 @@
using Plugin.TestResult_WPF.Util;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
@@ -70,6 +73,21 @@
            testResultView.testApp.ExecutionClosed += new _ApplicationMgrEvents_ExecutionClosedEventHandler(this.axApplicationMgr_ExecutionClosed);
        }
        public override void InitFinish()
        {
            base.InitFinish();
            string path = Path.Combine(BaseFolder, "Count.ini");
            testResultView.SetCountIniPath(path);
            testResultView.InitMainGrid();
            LoadProduct();
            testResultView.InitTestStandEngine();
        }
        public override void DoClose(object sender, EventArgs e)
        {
            testResultView.ReleaseResource();
        }
        private void axApplicationMgr_ExitApplication(object sender, EventArgs e)
        {
        }
@@ -116,17 +134,31 @@
            { }
        }
        private bool isLoaded;
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            LoadProduct();
            testResultView.InitTestStandEngine();
            new MaskWindow(Window.GetWindow(this),
                (win) =>
                {
                    Dispatcher.Invoke(() =>
                    {
                        try
                        {
            Start_Click(null, null);
            Loaded -= UserControl_Loaded;
                        }
                        finally
                        {
                            win.Close();
                        }
                    });
                }).Info(LangUtil.Get("Ts.SequenceIsStarting"));
        }
        public void LoadProduct()
        {
            List<ProductData> products = ParameterManager.GetProducts<List<ProductData>>();
            List<ProductData> products = ParameterManager.GetProducts<List<ProductData>>()
                ?? new List<ProductData>();
            this.modelSelect.Items.Clear();
            foreach (var prd in products)
            {
@@ -270,6 +302,16 @@
        {
        }
        /// <summary>
        /// 计数清零按钮事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnClear_Click(object sender, RoutedEventArgs e)
        {
            testResultView.ClearNumber();
        }
    }
}
Plugin.TsExecution/Plugin.TsExecution.csproj
@@ -196,6 +196,10 @@
      <Project>{f17de6b5-af1d-48c6-94a0-6b763a6546a6}</Project>
      <Name>HMI.UI</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.Util\HMI.Util.csproj">
      <Project>{AFEA100A-9223-448D-AB10-3015D93DD98C}</Project>
      <Name>HMI.Util</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <PropertyGroup>
Plugin.TsExecution/SingleControl.xaml
@@ -29,6 +29,8 @@
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid dp:GridHelper.InnerBorder="1" dp:GridHelper.Space="5" 
@@ -181,5 +183,18 @@
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
        <Grid Grid.Row="3" Height="20" Margin="0">
            <ProgressBar Grid.Row="3" x:Name="testProcess" />
            <Label Width="45" DockPanel.Dock="Right" x:Name="testProcessLabel" Padding="0"
                   VerticalContentAlignment="Center"/>
        </Grid>
        <DockPanel Grid.Row="4">
            <StatusBar>
                <StatusBarItem>
                    <Label Padding="0" VerticalContentAlignment="Center" Content="{Binding CurrentStep}"/>
                </StatusBarItem>
            </StatusBar>
        </DockPanel>
    </Grid>
</UserControl>
Plugin.TsExecution/SingleControl.xaml.cs
@@ -1,4 +1,5 @@
using HMI.Core.Util;
using HMI.Util;
using NationalInstruments.TestStand.Interop.API;
using System;
using System.ComponentModel;
@@ -35,7 +36,7 @@
        #endregion
        private bool isLoadOK = false;
        private int index;
        private string counterFilePath;
        private string CountIniPath;
        private string status = "Ready";
@@ -89,13 +90,6 @@
            get => this.lang("Ts.TestInfo", Barcode, ElapseTime);
        }
        private string currentStep;
        public string CurrentStep
        {
            get { return currentStep; }
            set { Set(ref currentStep, value); }
        }
        private string barcode = string.Empty;
        public string Barcode
        {
@@ -133,8 +127,19 @@
        private DataTable dt;
        public SingleControl(int Index, int passNum, int failNum)
        private string currentStep = "等待扫码";
        public string CurrentStep
        {
            get => currentStep;
            set
            {
                Set(ref currentStep, value);
            }
        }
        public SingleControl(int Index, string CountIniPath)
        {
            this.CountIniPath = CountIniPath;
            DataContext = this;
            this.index = Index;
            PassNum = passNum;
@@ -219,13 +224,13 @@
        /// <param name="ProcessVal"></param>
        public void SetTestProcess(int ProcessVal)
        {
            //this.TestProcess.Value = ProcessVal;
            //this.TestProcessLabel.Text = ProcessVal.ToString() + "%";
            //this.TestProcessLabel.BackColor = Color.Transparent;
            this.testProcess.Value = ProcessVal;
            this.testProcessLabel.Content = ProcessVal.ToString() + "%";
        }
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            ReadCounter();
            this.UpdateTestCounter(null);
        }
@@ -255,14 +260,31 @@
        private void WriteCounter()
        {
            IniFileHelper.WriteIniData("Socket" + this.index.ToString(), "Pass",
                this.passNum.ToString(), this.CountIniPath);
            IniFileHelper.WriteIniData("Socket" + this.index.ToString(), "Fail", this.failNum.ToString(),
                this.CountIniPath);
        }
        private void ReadCounter()
        {
            try
            {
                this.passNum = Convert.ToInt32(IniFileHelper.ReadIniData("Socket" + this.index.ToString(), "Pass", "", CountIniPath));
                this.failNum = Convert.ToInt32(IniFileHelper.ReadIniData("Socket" + this.index.ToString(), "Fail", "", CountIniPath));
            }
            catch (Exception ex)
            {
                //do nothing
            }
        }
        public void ResetCounter()
        {
            PassNum = 0;
            FailNum = 0;
            this.passNum = 0;
            this.failNum = 0;
            this.UpdateTestCounter(null);
        }
    }
}
Plugin.TsExecution/TsExecution.xaml
@@ -24,7 +24,7 @@
        <WindowsFormsHost x:Name="host" Visibility="Collapsed"></WindowsFormsHost>
        <Border>
            <UniformGrid x:Name="panel" Columns="2" dp:UniformGridHelper.InnerBorder="1" 
                         dp:UniformGridHelper.InnerBorderBrush="{StaticResource CommonBoarder}">
                         dp:UniformGridHelper.InnerBorderBrush="#3a3a3a">
            </UniformGrid>
        </Border>
Plugin.TsExecution/TsExecution.xaml.cs
@@ -5,6 +5,7 @@
using HMI.ParameterManage.Util;
using HMI.UI;
using HMI.UI.Util;
using HMI.Util;
using NationalInstruments.TestStand.Interop.API;
using NationalInstruments.TestStand.Interop.UI.Ax;
using Panuon.WPF.UI;
@@ -14,6 +15,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
@@ -101,6 +103,8 @@
            }
        }
        private Pie pie;
        private string CountIniPath;
        public TsExecution()
        {
@@ -111,63 +115,29 @@
            InitTsEngine();
            //初始化TestStand引擎
            this.InitTestStandEngine();
        }
        public override void SetBaseFolder(string folder)
        {
            CountIniPath = Path.Combine(folder, "Count.ini");
            base.SetBaseFolder(folder);
        }
        public override void InitFinish()
        {
            this.ReadCount(out int pass, out int fail);
            this.DisplayCount(pass, fail);
            UpdateChart(pass, fail);
            InitProducts();
            base.InitFinish();
        }
        private Pie pie;
        /// <summary>
        /// 初始化Winform图表
        /// </summary>
        private void InitChart()
        {
            //ChartArea chartArea1 = new ChartArea();
            //Legend legend1 = new Legend();
            //Series series1 = new Series();
            //DataPoint dataPoint1 = new DataPoint(0D, 10D);
            //DataPoint dataPoint2 = new DataPoint(0D, 10D);
            //chart = new Chart();
            ////
            //// chart
            ////
            //this.chart.BackColor = System.Drawing.SystemColors.Control;
            //chartArea1.BackColor = System.Drawing.SystemColors.Control;
            //chartArea1.Name = "ChartArea1";
            //this.chart.ChartAreas.Add(chartArea1);
            //this.chart.Dock = System.Windows.Forms.DockStyle.Fill;
            //legend1.BackColor = System.Drawing.SystemColors.Control;
            //legend1.Name = "Legend1";
            //this.chart.Legends.Add(legend1);
            //this.chart.Location = new System.Drawing.Point(0, 0);
            //this.chart.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
            //this.chart.Name = "chart";
            //this.chart.Palette = ChartColorPalette.None;
            //series1.BorderColor = System.Drawing.SystemColors.Control;
            //series1.ChartArea = "ChartArea1";
            //series1.ChartType = SeriesChartType.Pie;
            //series1.Color = System.Drawing.SystemColors.Control;
            //series1.LabelBackColor = System.Drawing.Color.White;
            //series1.Legend = "Legend1";
            //series1.Name = "Series1";
            //dataPoint1.AxisLabel = "OK";
            //dataPoint1.Color = Color.Green;
            //dataPoint1.IsVisibleInLegend = true;
            //dataPoint1.Label = "";
            //dataPoint2.AxisLabel = "NG";
            //dataPoint2.Color = Color.FromArgb(230, 0, 18);
            //series1.Points.Add(dataPoint1);
            //series1.Points.Add(dataPoint2);
            //this.chart.Series.Add(series1);
            //this.chart.Size = new System.Drawing.Size(215, 191);
            //this.chart.TabIndex = 0;
            //this.chart.Text = "chart1";
            //((System.ComponentModel.ISupportInitialize)(chart)).BeginInit();
            //chartHost.Child = chart;
            //((System.ComponentModel.ISupportInitialize)(chart)).EndInit();
            pieChart.Plot.Axes.Frameless();
            pieChart.Plot.HideGrid();
            pieChart.Plot.DataBackground.Color = Colors.Transparent;
@@ -274,7 +244,7 @@
                //重新生成子面板
                for (int i = 0; i < this.socketNum; i++)
                {
                    SingleControl control = new SingleControl(i, 0, 0);
                    SingleControl control = new SingleControl(i, CountIniPath);
                    panel.Children.Add(control);
                    tsSockets.Add(control);
                }
@@ -308,14 +278,14 @@
        /// <param name="fail">不合格数</param>
        private void ReadCount(out int pass, out int fail)
        {
            pass = 0; //Convert.ToInt32(OperateIniFile.ReadIniData("Count", "Pass", "", CountiniPath));
            fail = 0; //Convert.ToInt32(OperateIniFile.ReadIniData("Count", "Fail", "", CountiniPath));
            pass = Convert.ToInt32(IniFileHelper.ReadIniData("Count", "Pass", "", CountIniPath));
            fail = Convert.ToInt32(IniFileHelper.ReadIniData("Count", "Fail", "", CountIniPath));
        }
        private void WriteCount(int pass, int fail)
        {
            //OperateIniFile.WriteIniData("Count", "Pass", pass.ToString(), CountiniPath);
            //OperateIniFile.WriteIniData("Count", "Fail", fail.ToString(), CountiniPath);
            IniFileHelper.WriteIniData("Count", "Pass", pass.ToString(), CountIniPath);
            IniFileHelper.WriteIniData("Count", "Fail", fail.ToString(), CountIniPath);
        }
        /// <summary>
@@ -400,6 +370,26 @@
            }
        }
        public void StopSequenceClick()
        {
            new MaskWindow((WindowX)Window.GetWindow(this),
                    (win) =>
                    {
                        Dispatcher.Invoke(() =>
                        {
                            try
                            {
                                StopSequence();
                            }
                            finally
                            {
                                win.Close();
                            }
                        });
                    }
                    ).Info(LangUtil.Get("App.SequenceIsStopping"));
        }
        /// <summary>
        /// 停止测试序列
        /// </summary>
@@ -407,15 +397,14 @@
        {
            try
            {
                if (this.tsSockets.Count == 0)
                    return;
                if (this.tsSockets[0].SequenceFileContext == null)
                    return;
                if (this.tsSockets.Count > 0 && this.tsSockets[0]?.SequenceFileContext != null)
                {
                bool val = this.tsSockets[0].SequenceFileContext.StationGlobals.Exists("StationState.StopTest", 0);
                if (val)
                {
                    tsSockets[0].SequenceFileContext.StationGlobals.SetValBoolean("StationState.StopTest", 0, true);
                    bool isStop = this.execution.WaitForEndEx(2000, true, null, null);
                    }
                }
                this.CbProduct.IsEnabled = true;
                this.BtnStartSeq.IsEnabled = true;
@@ -435,10 +424,9 @@
            {
                case "BtnStartSeq":
                    StartClick();
                    break;
                case "BtnStopSeq":
                    StopSequence();
                    StopSequenceClick();
                    break;
                case "BtnClearNum":
                    ClearNum();
@@ -627,13 +615,6 @@
            pie.Slices[0].Value = pass;
            pie.Slices[1].Value = fail;
            pieChart.Refresh();
            //Dispatcher.Invoke(() => {
            //    ((System.ComponentModel.ISupportInitialize)(chart)).BeginInit();
            //    chart.Series[0].Points[0].YValues[0] = pass;
            //    chart.Series[0].Points[1].YValues[0] = fail;
            //    ((System.ComponentModel.ISupportInitialize)(chart)).EndInit();
            //});
        }
        private void SetTestSeqFile(ProductData product)
@@ -659,6 +640,17 @@
            }
        }
        public override void OnClose(object sender, CancelEventArgs e)
        {
            StopSequence();
            this.engine?.ReleaseSequenceFileEx(this.testSeq, 0);
            this.engine?.ReleaseSequenceFileEx(this.modeSeq, 0);
            this.engine?.UnloadAllModules();
            this.testSeq = null;
            this.modeSeq = null;
            base.OnClose(sender, e);
        }
        private void PluginControl_SizeChanged(object sender, SizeChangedEventArgs e)
        {
WPFProject/App.xaml
@@ -6,6 +6,7 @@
             xmlns:pu="https://opensource.panuon.com/wpf-ui"
             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
             xmlns:hc="clr-namespace:HMI.Core;assembly=HMI.Core"
             ShutdownMode="OnExplicitShutdown"
             Startup="Application_Startup">
    <Application.Resources>
        <ResourceDictionary>
WPFProject/App.xaml.cs
@@ -1,8 +1,10 @@
using HMI.Core.Util;
using HMI.Core.Setting;
using HMI.Core.Util;
using HMI.UI.Util;
using log4net;
using Panuon.WPF.UI;
using System;
using System.Threading;
using System.Windows;
using WPFProject.Utils;
@@ -29,13 +31,29 @@
                    MessageBoxIcon.Error, DefaultButton.YesOK, 5);
        }
        private static Mutex mutex;
        private void Application_Startup(object sender, StartupEventArgs e)
        {
            mutex = new System.Threading.Mutex(true, "TestPlatform_OnlyRun_CRNS");
            if (mutex.WaitOne(0, false))
            {
            LangUtil.Load();
                SystemSetting.SystemSettingChanged += SystemSetting_SystemSettingChanged;
            ToastHelper.Register(new MainToastHandler());
            LoadingWindow window = new LoadingWindow();
            //Login window = new Login();
                //LoadingWindow window = new LoadingWindow();
                Login window = new Login();
            window.Show();
        }
            else
            {
                MessageBox.Show("程序已经在运行!", "提示");
                this.Shutdown();
            }
        }
        private void SystemSetting_SystemSettingChanged(object sender, SystemSetting e)
        {
            LangUtil.LoadLang();
        }
    }
}
WPFProject/Lang/en-US/ParamManage.json
@@ -10,9 +10,24 @@
  "Pm.Parameter.Name": "Name",
  "Pm.Parameter.Value": "Value",
  "Pm.Parameter.Unit": "Unit",
  "Pm.Parameter.Type": "Type",
  "Pm.Parameter.Desc": "Description",
  "Pm.Parameter.Group": "Group",
  "Pm.Parameter.Source": "Source",
  "Pm.Parameter.Add": "New parameter",
  "Pm.Parameter.Edit": "Edit parameter",
  "Pm.PleaseSelectProduct": "Please select a product"
  "Pm.PleaseSelectProduct": "Please select a product",
  "Pm.Setting.Title": "Parameter manage",
  "Pm.Ti.Title": "Test item manage",
  "Pm.Ti.Name": "Name",
  "Pm.Ti.Type": "Type",
  "Pm.Ti.Shineng": "Enable",
  "Pm.Ti.LimitDown": "Limit down",
  "Pm.Ti.LimitUp": "Limit up",
  "Pm.Ti.StrValue": "String",
  "Pm.Ti.Offset": "Offset",
  "Pm.Ti.Source": "Source",
  "Pm.Ti.PleaseSelectProduct": "Please select a product"
}
WPFProject/Lang/en-US/TestExecution.json
@@ -17,6 +17,7 @@
  "Ts.StopSequenceFail": "Stop sequence failed",
  "Ts.MakeSureClearNum": "Are you sure you want to clear the number?",
  "Ts.SequenceIsStarting": "Sequence is starting...",
  "Ts.SequenceIsStopping": "Sequence is stopping...",
  "Ts.CurrentSnr": "Current serial",
  "Ts.TimeSpan": "Elapsed time"
}
WPFProject/Lang/en-US/app.json
@@ -98,6 +98,10 @@
  "App.MenuLoadSuccess": "{0} is loaded!",
  "App.MenuLoadFailed": "{0} load failed: {1} {2}",
  "App.MenuCount": "There are {0} menus is loading...",
  "App.SelectIcon": "Select icon",
  "App.Welcome": "Welcom! {0}",
  "App.MakeSureLogout": "Are you sure you want to logout?",
  "App.MakeSureRestartApp": "Are you sure you want to restart?",
  "HMI.NoDeleteData": "Please select items you want to delete",
@@ -135,5 +139,8 @@
  "HMI.Role.Name": "Name",
  "HMI.Role.Permission": "Permission",
  "HMI.Role.Add": "New role",
  "HMI.Role.Edit": "Edit role"
  "HMI.Role.Edit": "Edit role",
  "HMI.Line": "Line",
  "HMI.Station": "Station"
}
WPFProject/Lang/zh-CN/ParamManage.json
@@ -18,7 +18,6 @@
  "Pm.Parameter.Edit": "编辑参数",
  "Pm.PleaseSelectProduct": "请先选择一项产品",
  "Pm.Setting.Title": "参数管理",
  "Pm.Ti.Title": "测试项管理",
WPFProject/Lang/zh-CN/TestExecution.json
@@ -17,6 +17,7 @@
  "Ts.StopSequenceFail": "停止测试序列失败",
  "Ts.MakeSureClearNum": "确定要清空计数吗?",
  "Ts.SequenceIsStarting": "正在启动测试序列...",
  "Ts.SequenceIsStopping": "正在停止测试序列...",
  "Ts.CurrentSnr": "当前序列号",
  "Ts.TimeSpan": "测试耗时"
}
WPFProject/Lang/zh-CN/app.json
@@ -98,6 +98,10 @@
  "App.MenuLoadSuccess": "{0}加载完成!",
  "App.MenuLoadFailed": "{0}加载失败: {1} {2}",
  "App.MenuCount": "共有{0}条菜单待加载...",
  "App.SelectIcon": "选择图标",
  "App.Welcome": "欢迎您,{0}",
  "App.MakeSureLogout": "确定要退出登陆吗?",
  "App.MakeSureRestartApp": "确定要重新启动吗?",
  "HMI.NoDeleteData": "请选择要删除的数据",
WPFProject/Libs/SuperSocket.SocketEngine.dll
Binary files differ
WPFProject/LoadingWindow.xaml.cs
@@ -1,4 +1,5 @@
using HMI.Base;
using HMI.Core.Api;
using HMI.UI.Util;
using Panuon.WPF.UI;
using System;
@@ -37,7 +38,8 @@
            }
        }
        public Visibility ConsoleVisiable {
        public Visibility ConsoleVisiable
        {
            get => showConsole ? Visibility.Visible : Visibility.Collapsed;
        }
@@ -65,7 +67,7 @@
                    Add(msg.Message, null);
                    break;
                case LoadMsgType.LOADING:
                    HasErr = msg.Result == false;
                    HasErr = HasErr || msg.Result == false;
                    if (msg.Result != null)
                    {
                        processBar.Value = ++finished / total * 100;
@@ -83,21 +85,21 @@
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            PanuonWindow window = new PanuonWindow();
            window.Show();
            this.Close();
        }
        private PanuonWindow window;
        private double total;
        private double finished;
        private void WindowX_ContentRendered(object sender, System.EventArgs e)
        {
            window = new PanuonWindow();
            PluginLoader loader = PluginLoader.GetLoader();
            loader.LoadMessageAppend += Loader_LoadMessageAppend;
            Task.Run(() =>
            {
                loader.LoadMenuAndPlugin(Dispatcher);
                loader.LoadMenuAndPlugin(window, Dispatcher);
                //
            });
        }
WPFProject/Login.xaml
@@ -13,7 +13,10 @@
            Height="420"
            Width="380"
            pu:WindowXCaption.Background="Transparent"
            WindowStartupLocation="CenterScreen"
            Background="{StaticResource VerBar}"
            Closed="window_Closed"
            ContentRendered="WindowX_ContentRendered"
            pu:WindowXCaption.Buttons="Close">
    <Window.Resources>
        <ResourceDictionary>
@@ -124,10 +127,13 @@
        <pu:Switch Grid.Row="3"
               HorizontalAlignment="Right"
               Margin="0,25,0,0"
                   x:Name="SwRememberMe"
                   Cursor="Hand"
                  Unchecked="SwRememberMe_Unchecked"
               Foreground="{StaticResource PrimaryColorContract}"
               Style="{StaticResource RememberMeSwitchStyle}"
               Content="{DynamicResource App.RememberMe}" />
        <Button Grid.Row="4"
            Margin="0,20,0,0"
            Height="40"
WPFProject/Login.xaml.cs
@@ -1,4 +1,5 @@
using HMI.Core.Entity;
using HMI.Core.Setting;
using HMI.Core.Util;
using HMI.UI.DpUtil;
using HMI.UI.Util;
@@ -6,8 +7,11 @@
using Panuon.WPF.UI;
using System;
using System.Data;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using WPFProject.Utils;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.StartPanel;
namespace WPFProject
{
@@ -30,6 +34,9 @@
            InitializeComponent();
        }
        private static readonly Encryptor encryptor = new Encryptor("aB3dE5fG7hI9jjK1lMnOpQrS2tUvWxYz",
            "8Wq9RsT4vYx6ZuB1");
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            if (FormGroupHelper.ValidateForm(formPanel))
@@ -37,29 +44,22 @@
                ButtonHelper.SetIsPending(BtnLogin, true);
                string username = TbAccount.Text;
                string password = PwdPassword.Password;
                bool rememberMe = SwRememberMe.IsChecked == true;
                Task.Run(() =>
                {
                    DoLogin(username, password);
                    DoLogin(username, password, rememberMe);
                });
            }
            //try
            //{
            //}
            //finally {
            //    ButtonHelper.SetIsPending(BtnLogin, false);
            //}
        }
        private void DoLogin(string username, string password)
        private void DoLogin(string username, string password, bool rememberMe)
        {
            SqliteHelper helper = null;
            try
            {
                helper = new SqliteHelper(PathHelper.DatabasePath);
                string sql = $"SELECT id,username,name From user WHERE username='{username}' and password='{password}'";
                string sql = $"SELECT id,username,password,name From user WHERE username='{username}' and password='{password}'";
                DataTable dt = helper.GetDataTable(sql);
                if (dt == null || dt.Rows.Count <= 0)
                {
@@ -70,10 +70,22 @@
                    dt.Rows[0]["username"].ToString(),
                    dt.Rows[0]["name"].ToString());
                UserInfo.Login(user);
                if (rememberMe)
                {
                    // 记住登录操作
                    string userToken =
                        JsonUtil.Serialize(
                            new UserInfo(null, user.Username, user.Name, dt.Rows[0]["password"].ToString()));
                    // 存储数据
                    BinUtil.SaveStrToBin(encryptor.Encrypt(userToken),
                        Path.Combine(PathHelper.DatabaseFolder, "user.bin"));
                }
                Dispatcher.Invoke(() =>
                {
                    LoadingWindow loading = new LoadingWindow();
                    loading.Show();
                    this.Closed -= window_Closed;
                    this.Close();
                });
            }
@@ -93,5 +105,37 @@
                });
            }
        }
        /// <summary>
        /// 渲染完成后,尝试自动登录
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void WindowX_ContentRendered(object sender, EventArgs e)
        {
            string userToken = string.Empty;
            if (BinUtil.LoadStrFromBin(ref userToken, Path.Combine(PathHelper.DatabaseFolder, "user.bin")))
            {
                userToken = encryptor.Decrypt(userToken);
                UserInfo userInfo = JsonUtil.Deserialize<UserInfo>(userToken);
                TbAccount.Text = userInfo.Username;
                PwdPassword.Password = userInfo.Password;
                SwRememberMe.IsChecked = true;
                Button_Click(null, null);
            }
        }
        private void window_Closed(object sender, EventArgs e)
        {
            Application.Current.Shutdown();
        }
        private void SwRememberMe_Unchecked(object sender, RoutedEventArgs e)
        {
            string path = Path.Combine(PathHelper.DatabaseFolder, "user.bin");
            if (File.Exists(path))
            {
                File.Delete(path);
            }
        }
    }
}
WPFProject/PanuonWindow.xaml
@@ -13,14 +13,29 @@
            x:Name="window"
            BorderBrush="Transparent"
            Loaded="window_Loaded"
            pu:WindowXCaption.Buttons="All"
            xmlns:sys="clr-namespace:System;assembly=mscorlib"
            xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
            xmlns:hc="clr-namespace:HMI.Core;assembly=HMI.Core"
            Closed="window_Closed"
            pu:WindowXCaption.Background="Transparent"
            pu:WindowXCaption.Height="80"
            pu:WindowXCaption.Foreground="#FFF" >
    <Window.Resources>
        <hc:LangConverter x:Key="lc"/>
        <Style x:Key="UserMenu" TargetType="Menu" BasedOn="{StaticResource {x:Static pu:StyleKeys.MenuStyle}}">
            <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"/>
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="Cursor" Value="Hand"/>
            <Setter Property="pu:MenuHelper.SubmenuItemsForeground" Value="#3a3a3a"/>
            <Setter Property="pu:MenuHelper.SubmenuItemsBackground" Value="White"/>
            <Setter Property="pu:MenuHelper.TopLevelItemsHoverBackground" Value="{x:Null}"/>
            <Setter Property="pu:MenuHelper.TopLevelDropDownPlacement" Value="BottomLeft"/>
        </Style>
    </Window.Resources>
    <Window.Background>
        <ImageBrush Stretch="Uniform" AlignmentX="Left" AlignmentY="Top"  
@@ -49,6 +64,24 @@
    <pu:WindowXCaption.ExtendControl>
        <StackPanel Orientation="Horizontal">
            <Menu Margin="0" Padding="0" VerticalAlignment="Top"
                  VerticalContentAlignment="Center" Style="{StaticResource UserMenu}"
                  Background="Transparent">
                <MenuItem x:Name="userMenu" FontSize="16">
                    <MenuItem
                        Icon="&#xe61d;"
                        Cursor="Hand" FontSize="14" Header="退出登录"
                              x:Name="MiLogout"
                              Click="MenuItem_Click"/>
                    <MenuItem Cursor="Hand" FontSize="14"
                        Icon="&#xe867;"
                              Header="重新启动"
                              x:Name="MiRestart"
                              Click="MenuItem_Click"/>
                </MenuItem>
            </Menu>
            <Button x:Name="BtnSwitchTheme"
                FontSize="18"
                FontFamily="/HMI.UI;component/Resources/Font/#iconfont"
WPFProject/PanuonWindow.xaml.cs
@@ -1,9 +1,16 @@
using HMI.Core.Setting;
using HMI.Base;
using HMI.Core.Api;
using HMI.Core.Entity;
using HMI.Core.Setting;
using HMI.Core.Util;
using HMI.UI.Util;
using Panuon.WPF.UI;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
namespace WPFProject
@@ -11,13 +18,29 @@
    /// <summary>
    /// PanuonWindow.xaml 的交互逻辑
    /// </summary>
    public partial class PanuonWindow : WindowX
    public partial class PanuonWindow : WindowX, IPluginContainer
    {
        public PanuonWindow()
        {
            Loaded += PanuonWindow_Loaded;
        }
        private UserInfo currentUser;
        public UserInfo CurrentUser
        {
            get => currentUser;
            set
            {
                Set(ref currentUser, value);
            }
        }
        private void PanuonWindow_Loaded(object sender, RoutedEventArgs e)
        {
            DataContext = this;
            RefrshConfig();
            InitializeComponent();
            userMenu.Header = this.lang("App.Welcome", UserInfo.User.Name);
            ShowMenu = true;
        }
@@ -53,10 +76,15 @@
        private bool showMenu;
        public event CancelEventHandler OnCloseEvent;
        public event EventHandler DoCloseEvent;
        public event EventHandler LoadComplateEvent;
        public bool ShowMenu
        {
            get { return showMenu; }
            set {
            set
            {
                Set(ref showMenu, value);
                menuTab.ChangeItemVisibility(value);
            }
@@ -82,7 +110,67 @@
        private void SystemSetting_SystemSettingChanged(object sender, SystemSetting e)
        {
            RefrshConfig();
            LangUtil.LoadLang();
        }
        protected override void OnClosing(CancelEventArgs e)
        {
            OnCloseEvent?.Invoke(this, e);
            base.OnClosing(e);
        }
        protected override void OnClosed(EventArgs e)
        {
            DoCloseEvent?.Invoke(this, e);
            base.OnClosed(e);
        }
        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {MenuItem mi = sender as MenuItem;
            switch (mi.Name) {
                case "MiLogout":
                    Logout();
                    break;
                case "MiRestart":
                    RestartApp();
                    break;
                default:
                    break;
            }
        }
        private void Logout()
        {
            if (MsgBoxHelper.WarnMessage(this, this.lang("App.MakeSureLogout")) == MessageBoxResult.OK)
            {
                string path = Path.Combine(PathHelper.DatabaseFolder, "user.bin");
                if (File.Exists(path)) {
                    File.Delete(path);
                }
                this.Closed -= window_Closed;
                this.Close();
                PluginLoader.Reset();
                Login login = new Login();
                login.Show();
            }
        }
        private void RestartApp()
        {
            if (MsgBoxHelper.WarnMessage(this, this.lang("App.MakeSureRestartApp")) == MessageBoxResult.OK)
            {
                Application.Current.Exit += Current_Exit;
                this.Close();
            }
        }
        private void Current_Exit(object sender, ExitEventArgs e)
        {
            System.Windows.Forms.Application.Restart();
        }
        private void window_Closed(object sender, EventArgs e)
        {
            Application.Current.Shutdown();
        }
        //private void BaseWindow_Loaded(object sender, RoutedEventArgs e)
WPFProject/TextFile1.txt
@@ -17,16 +17,17 @@
set "source_folder=$(TargetDir)"
set "destination_folder=$(TargetDir)3rds"
if not exist "%destination_folder%" mkdir "%destination_folder%"
rmdir /S /Q "%destination_folder%"
mkdir "%destination_folder%"
for /d %%D in ("%source_folder%\*") do (
    if /I not "%%~nxD"=="Config" if /I not "%%~nxD"=="Icon" if /I not "%%~nxD"=="Lang" if /I not "%%~nxD"=="Libs" if /I not "%%~nxD"=="Log" if /I not "%%~nxD"=="Plugin" (
        move "%%D" "%destination_folder%"
        move /Y "%%D" "%destination_folder%"
    )
)
for %%F in ("%source_folder%\*") do (
    if /I not "%%~nxF"=="$(TargetFileName)" if /I not "%%~nxF"=="$(TargetFileName).config" if /I not "%%~nxF"=="$(TargetName).pdb" if /I not "%%~nxF"=="clear.bat" (
        move "%%F" "%destination_folder%"
        move /Y "%%F" "%destination_folder%"
    )
)
WPFProject/Utils/BinUtil.cs
New file
@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace WPFProject.Utils
{
    public class BinUtil
    {
        /// <summary>
        /// 把字符串以二进制存入bin文件中
        /// </summary>
        /// <param name="content">字符内容</param>
        /// <param name="filePath">存放路径</param>
        public static void SaveStrToBin(string content, string filePath = null)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                filePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().FullName), "bin");
            }
            byte[] bytes = Convert.FromBase64String(content);
            using (Stream stream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
            using (BinaryWriter writer = new BinaryWriter(stream))
            {
                writer.Write(bytes);
            }
        }
        /// <summary>
        /// 从bin中加载字符串
        /// </summary>
        /// <param name="content">字符串内容</param>
        /// <param name="filePath">bin文件路径</param>
        /// <returns>读取是否成功</returns>
        public static bool LoadStrFromBin(ref string content, string filePath = null)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                filePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().FullName), "bin");
            }
            if (!File.Exists(filePath))
            {
                return false;
            }
            using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            using (BinaryReader reader = new BinaryReader(stream))
            {
                byte[] bytes = new byte[stream.Length];
                reader.Read(bytes, 0, bytes.Length);
                content = Convert.ToBase64String(bytes);
                return true;
            }
        }
    }
}
WPFProject/Utils/Encryptor.cs
New file
@@ -0,0 +1,63 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace WPFProject.Utils
{
    public class Encryptor
    {
        private readonly byte[] key;
        private readonly byte[] iv;
        public Encryptor(string keyString, string ivString)
        {
            key = Encoding.UTF8.GetBytes(keyString);
            iv = Encoding.UTF8.GetBytes(ivString);
        }
        public string Encrypt(string plainText)
        {
            using (var aes = Aes.Create())
            {
                aes.Key = key;
                aes.IV = iv;
                ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {
                            swEncrypt.Write(plainText);
                        }
                        return Convert.ToBase64String(msEncrypt.ToArray());
                    }
                }
            }
        }
        public string Decrypt(string cipherText)
        {
            using (var aes = Aes.Create())
            {
                aes.Key = key;
                aes.IV = iv;
                ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
                byte[] arr = Convert.FromBase64String(cipherText);
                using (MemoryStream msDecrypt = new MemoryStream(arr))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {
                            return srDecrypt.ReadToEnd();
                        }
                    }
                }
            }
        }
    }
}
WPFProject/WPFProject.csproj
@@ -250,9 +250,11 @@
    <Compile Include="UI\WorkWindow.xaml.cs">
      <DependentUpon>WorkWindow.xaml</DependentUpon>
    </Compile>
    <Compile Include="Utils\BinUtil.cs" />
    <Compile Include="Utils\CanvasHelper.cs" />
    <Compile Include="Utils\DdDragHandler.cs" />
    <Compile Include="Utils\DdDropHandler.cs" />
    <Compile Include="Utils\Encryptor.cs" />
    <Compile Include="Utils\MainToastHandler.cs" />
    <Compile Include="Utils\Resize.cs" />
    <Compile Include="Utils\Win32Helper.cs" />
@@ -378,6 +380,9 @@
    <Content Include="x64\SQLite.Interop.dll">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
    <Content Include="Libs\SuperSocket.SocketEngine.dll">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
    <Resource Include="Icon\b1.jpg" />
    <Resource Include="Icon\b2.jpg" />
    <Resource Include="Icon\b3.jpg" />
@@ -406,9 +411,6 @@
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
    <Content Include="Libs\x64\SQLite.Interop.dll">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
    <Content Include="Config\log4net.config">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
@@ -421,9 +423,7 @@
    <Content Include="Lang\zh-CN\Setting.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
    <Content Include="Config\Database\database.db">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="Config\Database\database.db" />
    <Content Include="Lang\zh-CN\DataViewer.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
@@ -561,17 +561,18 @@
set "source_folder=$(TargetDir)"
set "destination_folder=$(TargetDir)3rds"
if not exist "%25destination_folder%25" mkdir "%25destination_folder%25"
rmdir /S /Q "%25destination_folder%25"
mkdir "%25destination_folder%25"
for /d %25%25D in ("%25source_folder%25\*") do (
    if /I not "%25%25~nxD"=="Config" if /I not "%25%25~nxD"=="Icon" if /I not "%25%25~nxD"=="Lang" if /I not "%25%25~nxD"=="Libs" if /I not "%25%25~nxD"=="Log" if /I not "%25%25~nxD"=="Plugin" (
        move "%25%25D" "%25destination_folder%25"
        move /Y "%25%25D" "%25destination_folder%25"
    )
)
for %25%25F in ("%25source_folder%25\*") do (
    if /I not "%25%25~nxF"=="$(TargetFileName)" if /I not "%25%25~nxF"=="$(TargetFileName).config" if /I not "%25%25~nxF"=="$(TargetName).pdb" if /I not "%25%25~nxF"=="clear.bat" (
        move "%25%25F" "%25destination_folder%25"
        move /Y "%25%25F" "%25destination_folder%25"
    )
)</PostBuildEvent>
  </PropertyGroup>
WPFProject/WPFProject.sln
@@ -185,8 +185,8 @@
        {202D9A7B-96B3-4AD4-9977-FC4263207A02}.Release|x64.Build.0 = Release|Any CPU
        {AFEA100A-9223-448D-AB10-3015D93DD98C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {AFEA100A-9223-448D-AB10-3015D93DD98C}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {AFEA100A-9223-448D-AB10-3015D93DD98C}.Debug|x64.ActiveCfg = Debug|Any CPU
        {AFEA100A-9223-448D-AB10-3015D93DD98C}.Debug|x64.Build.0 = Debug|Any CPU
        {AFEA100A-9223-448D-AB10-3015D93DD98C}.Debug|x64.ActiveCfg = Debug|x64
        {AFEA100A-9223-448D-AB10-3015D93DD98C}.Debug|x64.Build.0 = Debug|x64
        {AFEA100A-9223-448D-AB10-3015D93DD98C}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {AFEA100A-9223-448D-AB10-3015D93DD98C}.Release|Any CPU.Build.0 = Release|Any CPU
        {AFEA100A-9223-448D-AB10-3015D93DD98C}.Release|x64.ActiveCfg = Release|Any CPU