Shun %!s(int64=2) %!d(string=hai) anos
pai
achega
7038ca2df1

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 13 - 0
src/YSAI.Core.Wpf/resources/svg/WindowSvg.xaml


+ 2 - 2
src/YSAI.DAQ.Samples/YSAI.DAQ.Samples.csproj

@@ -98,8 +98,8 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="YSAI.Mqtt" Version="23.359.7955" />
-    <PackageReference Include="YSAI.Opc" Version="23.360.5691" />
+    <PackageReference Include="YSAI.Mqtt" Version="23.360.22275" />
+    <PackageReference Include="YSAI.Opc" Version="23.360.7834" />
   </ItemGroup>
 
   <ItemGroup>

+ 1 - 1
src/YSAI.Mqtt/YSAI.Mqtt.csproj

@@ -3,7 +3,7 @@
     <TargetFrameworks>net6.0;net8.0</TargetFrameworks>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
-    <Version>23.359.7955</Version>
+    <Version>23.360.22275</Version>
     <PackageOutputPath Condition="'$(Configuration)' == 'Release'">../YSAI.Publish/Release</PackageOutputPath>
     <PackageOutputPath Condition="'$(Configuration)' == 'Debug'">../YSAI.Publish/Debug</PackageOutputPath>
     <Authors>Shun</Authors>

+ 2 - 2
src/YSAI.Mqtt/client/MqttClientData.cs

@@ -31,14 +31,14 @@ namespace YSAI.Mqtt.client
             [Description("IP地址")]
             [Verify(@"^(25[0-4]|2[0-4]\\d]|[01]?\\d{2}|[1-9])\\.(25[0-5]|2[0-4]\\d]|[01]?\\d?\\d)\\.(25[0-5]|2[0-4]\\d]|[01]?\\d?\\d)\\.(25[0-4]|2[0-4]\\d]|[01]?\\d{2}|[1-9])$", "输入有误")]
             [Display(true, true, true, ParamStructure.dataCate.text)]
-            public string? Ip { get; set; }
+            public string? Ip { get; set; } = "127.0.0.1";
 
             /// <summary>
             /// 端口
             /// </summary>
             [Description("端口")]
             [Display(true, true, true, ParamStructure.dataCate.unmber)]
-            public int Port { get; set; }
+            public int Port { get; set; } = 6688;
 
             /// <summary>
             /// 用户

+ 1 - 1
src/YSAI.Mqtt/service/MqttServiceData.cs

@@ -20,7 +20,7 @@ namespace YSAI.Mqtt.service
             [Category("基础数据")]
             [Description("端口")]
             [Display(true, true, true, ParamStructure.dataCate.unmber)]
-            public int Port { get; set; }
+            public int Port { get; set; } = 6688;
 
             /// <summary>
             /// 用户

+ 3 - 3
src/YSAI.Mqtt/service/websocket/MqttWebSocketServiceData.cs

@@ -27,14 +27,14 @@ namespace YSAI.Mqtt.service.websocket
             [Category("基础数据")]
             [Description("端口")]
             [Display(true, true, true, ParamStructure.dataCate.unmber)]
-            public int Port { get; set; }
+            public int Port { get; set; } = 6688;
 
             /// <summary>
             /// 端口 Mqtt WEBSOCKET 使用
             /// </summary>
             [Description("WS端口")]
             [Display(true, true, true, ParamStructure.dataCate.unmber)]
-            public int WsPort { get; set; }
+            public int WsPort { get; set; } = 8866;
 
             /// <summary>
             /// 用户
@@ -55,7 +55,7 @@ namespace YSAI.Mqtt.service.websocket
             /// </summary>
             [Description("地址")]
             [Display(true, true, true, ParamStructure.dataCate.text)]
-            public string? Uri { get; set; }
+            public string? Uri { get; set; } = "shun";
         }
 
         /// <summary>

+ 1 - 1
src/YSAI.Opc/YSAI.Opc.csproj

@@ -3,7 +3,7 @@
     <TargetFrameworks>net6.0;net8.0</TargetFrameworks>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
-    <Version>23.360.5691</Version>
+    <Version>23.360.7834</Version>
     <PackageOutputPath Condition="'$(Configuration)' == 'Release'">../YSAI.Publish/Release</PackageOutputPath>
     <PackageOutputPath Condition="'$(Configuration)' == 'Debug'">../YSAI.Publish/Debug</PackageOutputPath>
     <Authors>Shun</Authors>

+ 1 - 1
src/YSAI.Opc/da/client/OpcDaClientData.cs

@@ -37,7 +37,7 @@ namespace YSAI.Opc.da.client
             [Description("接口版本")]
             [JsonConverter(typeof(StringEnumConverter))]
             [Display(true, true, true, ParamStructure.dataCate.select)]
-            public ApiVerType ApiVerType { get; set; } = ApiVerType.COM_DA_30;
+            public ApiVerType ApiVerType { get; set; } = ApiVerType.COM_DA_20;
 
             /// <summary>
             /// 更新频率

+ 1 - 1
src/YSAI.Opc/da/client/OpcDaClientOperate.cs

@@ -793,7 +793,7 @@ namespace YSAI.Opc.da.client
             {
                 return Break(SN, false, ex.Message, Exception: ex);
             }
-            return Break(SN, false, "关闭失败");
+            return Break(SN, true);
         }
 
         public Task<OperateResult> ReadAsync(Address address)

+ 1 - 1
src/YSAI.Relay.Samples/YSAI.Relay.Samples.csproj

@@ -8,7 +8,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="YSAI.Mqtt" Version="23.359.7955" />
+    <PackageReference Include="YSAI.Mqtt" Version="23.360.22275" />
   </ItemGroup>
 
 </Project>

+ 1 - 1
src/YSAI.Tool.Wpf/Main.xaml.cs

@@ -33,7 +33,7 @@ namespace YSAI.Tool.Wpf
                     if (navigationViews.Count > 0)
                     {
                         //第一次打开显示的界面
-                        navigationViews[0].Navigate(typeof(DaHttp));
+                        navigationViews[0].Navigate(typeof(MqttClient));
                     }
                 });
             });

+ 3 - 2
src/YSAI.Tool.Wpf/YSAI.Tool.Wpf.csproj

@@ -7,6 +7,7 @@
     <ImplicitUsings>enable</ImplicitUsings>
     <UseWPF>true</UseWPF>
 	  <SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
+	  <PlatformTarget>x86</PlatformTarget>
   </PropertyGroup>
 
   <ItemGroup>
@@ -24,11 +25,11 @@
 		<PackageReference Include="YSAI.Mewtocol" Version="23.360.5691" />
 		<PackageReference Include="YSAI.Mitsubishi" Version="23.360.5691" />
 		<PackageReference Include="YSAI.Modbus" Version="23.360.5691" />
-		<PackageReference Include="YSAI.Mqtt" Version="23.359.7955" />
+		<PackageReference Include="YSAI.Mqtt" Version="23.360.22275" />
 		<PackageReference Include="YSAI.NetMQ" Version="23.352.38022" />
 		<PackageReference Include="YSAI.Netty" Version="23.352.38022" />
 		<PackageReference Include="YSAI.Omron" Version="23.360.5691" />
-		<PackageReference Include="YSAI.Opc" Version="23.360.5691" />
+		<PackageReference Include="YSAI.Opc" Version="23.360.7834" />
 		<PackageReference Include="YSAI.RabbitMQ" Version="23.352.38022" />
 		<PackageReference Include="YSAI.Redis" Version="23.352.38022" />
 		<PackageReference Include="YSAI.Siemens" Version="23.360.5691" />

+ 5 - 907
src/YSAI.Tool.Wpf/controllers/DaClientController.cs

@@ -1,24 +1,14 @@
-using CommunityToolkit.Mvvm.Input;
-using System.Collections.Concurrent;
-using System.IO;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-using YSAI.Core.Wpf;
-using YSAI.Core.Wpf.mvvm;
-using YSAI.Model.data;
+using System.Windows;
 using YSAI.Opc.da.client;
-using YSAI.Unility;
+using YSAI.Tool.Wpf.template;
 using static YSAI.Opc.da.client.OpcDaClientData;
 
 namespace YSAI.Tool.Wpf.controllers
 {
-    public class DaClientController : NotifyObject
+    public class DaClientController : DaqController<Basics>
     {
         public DaClientController()
         {
-            //当皮肤切换触发事件
-            WindowHelper.OnSkinSwitchEvent += WindowHelper_OnSkinSwitchEvent;
             //初始化基础数据
             BasicsData = new Basics();
             //工具标题
@@ -31,900 +21,8 @@ namespace YSAI.Tool.Wpf.controllers
             HexVisibility = Visibility.Collapsed;
             //信息格式;  0 ASCII ; 1 HEX
             InfoFormat = 0;
+            //设置采集对象
+            Daq = new OpcDaClientOperate();
         }
-        private void WindowHelper_OnSkinSwitchEvent(object? sender, EventArgs e)
-        {
-
-        }
-
-        #region 统一需要的数据
-        /// <summary>
-        /// 导出的文件名
-        /// </summary>
-        private string FileName { get; set; }
-        /// <summary>
-        /// 工具标题
-        /// </summary>
-        public string ToolTitle
-        {
-            get => GetProperty(() => ToolTitle);
-            set => SetProperty(() => ToolTitle, value);
-        }
-        /// <summary>
-        /// 数据源
-        /// </summary>
-        public Basics BasicsData
-        {
-            get => GetProperty(() => BasicsData);
-            set => SetProperty(() => BasicsData, value);
-        }
-
-        /// <summary>
-        /// 导入基础数据命令
-        /// </summary>
-        public ICommand IncBasics { get => new RelayCommand(OnIncBasics); }
-
-        /// <summary>
-        /// 导入基础数据
-        /// </summary>
-        public void OnIncBasics()
-        {
-            (string RetFilePath, string RetFileName) Data = Unility.Windows.FileTool.SelectFiles("json");
-            if (!string.IsNullOrEmpty(Data.RetFilePath))
-            {
-                Basics? basics = FileTool.FileToString(Data.RetFilePath).ToJsonEntity<Basics>();
-                if (basics == null)
-                {
-                    Output("导入失败");
-                }
-                else
-                {
-                    Output("导入成功");
-                    BasicsData = basics;
-                }
-            }
-        }
-
-        /// <summary>
-        /// 导出基础数据命令
-        /// </summary>
-        public ICommand ExpBasics { get => new RelayCommand(OnExpBasics); }
-
-        /// <summary>
-        /// 导出基础数据
-        /// </summary>
-        public void OnExpBasics()
-        {
-            string path = Unility.Windows.FileTool.SelectFolder();
-            if (!string.IsNullOrEmpty(path))
-            {
-                FileTool.StringToFile(Path.Combine(path, $"{FileName}[{DateTime.Now.ToString("yyyyMMddHHmmss")}].json"), BasicsData.ToJson());
-                Output("导出成功");
-            }
-        }
-
-        private string _info = string.Empty;
-        /// <summary>
-        /// 信息
-        /// </summary>
-        public string Info
-        {
-            get => _info;
-            set => SetProperty(ref _info, value);
-        }
-
-        /// <summary>
-        /// 信息框事件
-        /// </summary>
-        public ICommand Info_TextChanged { get => new RelayCommand<TextChangedEventArgs>(OnInfo_TextChanged); }
-
-        /// <summary>
-        /// 信息框事件
-        /// 让滚动条一直处在最下方
-        /// </summary>
-        public void OnInfo_TextChanged(System.Windows.Controls.TextChangedEventArgs e)
-        {
-            System.Windows.Controls.TextBox textBox = (System.Windows.Controls.TextBox)e.Source;
-            textBox.SelectionStart = textBox.Text.Length;
-            textBox.SelectionLength = 0;
-            textBox.ScrollToEnd();
-        }
-
-        /// <summary>
-        /// 输出标准消息
-        /// </summary>
-        /// <param name="Data">数据</param>
-        /// <param name="IsDate">需要时间</param>
-        private Task Output(string Data, bool IsDate = true)
-        {
-            return Task.Run(() =>
-            {
-                System.Windows.Application.Current?.Dispatcher.Invoke(delegate ()
-                {
-                    if (Info?.Length > 10000)
-                    {
-                        Info = string.Empty;
-                    }
-                    if (IsDate)
-                    {
-                        Info += $" {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} : {Data}\r\n";
-                    }
-                    else
-                    {
-                        Info += $"{Data}\r\n";
-                    }
-                });
-            });
-        }
-        /// <summary>
-        /// ascii
-        /// </summary>
-        public Visibility AsciiVisibility
-        {
-            get => GetProperty(() => AsciiVisibility);
-            set => SetProperty(() => AsciiVisibility, value);
-        }
-        /// <summary>
-        /// hex
-        /// </summary>
-        public Visibility HexVisibility
-        {
-            get => GetProperty(() => HexVisibility);
-            set => SetProperty(() => HexVisibility, value);
-        }
-
-        /// <summary>
-        /// 接收数据格式
-        /// 0 ASCII
-        /// 1 HEX
-        /// </summary>
-        public int InfoFormat
-        {
-            get => GetProperty(() => InfoFormat);
-            set => SetProperty(() => InfoFormat, value);
-        }
-
-        /// <summary>
-        /// 清空信息命令
-        /// </summary>
-        public ICommand Clear { get => new RelayCommand(OnClear); }
-
-        /// <summary>
-        /// 清空信息
-        /// </summary>
-        public void OnClear()
-        {
-            Info = string.Empty;
-        }
-
-        /// <summary>
-        /// 基础数据的显示隐藏
-        /// </summary>
-        public bool BasicsData_ToggleButtonIsChecked
-        {
-            get => GetProperty(() => BasicsData_ToggleButtonIsChecked);
-            set => SetProperty(() => BasicsData_ToggleButtonIsChecked, value);
-        }
-
-
-        #endregion 统一需要的数据
-
-        /// <summary>
-        /// 通信
-        /// </summary>
-        private OpcDaClientOperate communication;
-
-        /// <summary>
-        /// 加载节点的生命周期
-        /// </summary>
-        private CancellationTokenSource tokenSource;
-
-        /// <summary>
-        /// 数据类型
-        /// </summary>
-        public int DataType
-        {
-            get => GetProperty(() => DataType);
-            set => SetProperty(() => DataType, value);
-        }
-        /// <summary>
-        /// 节点浏览是否点击
-        /// </summary>
-        public bool NodeBrowseIsChecked
-        {
-            get => GetProperty(() => NodeBrowseIsChecked);
-            set => SetProperty(() => NodeBrowseIsChecked, value);
-        }
-
-        /// <summary>
-        /// 节点地址
-        /// </summary>
-        public string DotAddress
-        {
-            get => GetProperty(() => DotAddress);
-            set => SetProperty(() => DotAddress, value);
-        }
-
-        /// <summary>
-        /// 写入数据
-        /// </summary>
-        public string WriteInData
-        {
-            get => GetProperty(() => WriteInData);
-            set => SetProperty(() => WriteInData, value);
-        }
-
-
-
-        /// <summary>
-        /// 启动
-        /// </summary>
-        public ICommand Start { get => new RelayCommand(OnStart); }
-        private void OnStart()
-        {
-            communication = OpcDaClientOperate.Instance(BasicsData);
-            OperateResult operateResult = communication.On();
-            if (operateResult.State)
-            {
-                communication.OnEvent -= Communication_OnEvent;
-                communication.OnEvent += Communication_OnEvent;
-            }
-            Output(operateResult.ToJson().JsonFormatting());
-        }
-
-
-        /// <summary>
-        /// 停止
-        /// </summary>
-        public ICommand Stop { get => new RelayCommand(OnStop); }
-        private void OnStop()
-        {
-            if (communication == null) return;
-            OperateResult operateResult = communication.Off();
-            Output(operateResult.ToJson().JsonFormatting());
-        }
-
-
-        /// <summary>
-        /// 显示节点浏览命令
-        /// </summary>
-        public ICommand ToggleButton_Checked { get => new RelayCommand<RoutedEventArgs>(OnToggleButton_Checked); }
-        private void OnToggleButton_Checked(RoutedEventArgs e)
-        {
-            if (communication != null && communication.GetStatus().State)
-            {
-                NodeBrowseIsChecked = true;
-                BasicsData_ToggleButtonIsChecked = true;
-                if (tokenSource == null)
-                {
-                    tokenSource = new CancellationTokenSource();
-                }
-                //实例化对象
-                Node = new ObservableCollection<NodeStructuralBody>();
-                //获取所有节点
-                GetNodeInform(null, null, tokenSource);
-            }
-            else
-            {
-                Output("请先打开连接");
-                NodeBrowseIsChecked = false;
-            }
-        }
-
-        /// <summary>
-        /// 隐藏节点浏览命令
-        /// </summary>
-        public ICommand ToggleButton_Unchecked { get => new RelayCommand<RoutedEventArgs>(OnToggleButton_Unchecked); }
-        private void OnToggleButton_Unchecked(RoutedEventArgs e)
-        {
-            BasicsData_ToggleButtonIsChecked = false;
-            if (tokenSource != null)
-            {
-                tokenSource.Cancel();
-                tokenSource = null;
-            }
-            Node?.Clear();
-            NodeMessage?.Clear();
-        }
-
-        /// <summary>
-        /// 读取
-        /// </summary>
-        public ICommand Read { get => new RelayCommand(OnRead); }
-        public void OnRead()
-        {
-
-            Address? address = OrganizationAddress();
-            if (address == null) return;
-            OperateResult operateResult = communication.Read(address);
-            Output(operateResult.ToJson().JsonFormatting());
-        }
-
-        /// <summary>
-        /// 订阅
-        /// </summary>
-        public ICommand Subscribe { get => new RelayCommand(OnSubscribe); }
-        public void OnSubscribe()
-        {
-            Address? address = OrganizationAddress();
-            if (address == null) return;
-            //订阅
-            OperateResult operateResult = communication.Subscribe(address);
-            Output(operateResult.ToJson().JsonFormatting());
-        }
-
-        /// <summary>
-        /// 写入
-        /// </summary>
-        public ICommand Write { get => new RelayCommand(OnWrite); }
-        public void OnWrite()
-        {
-            ConcurrentDictionary<string, object> pairs = new ConcurrentDictionary<string, object>();
-            try
-            {
-                switch (DataType)
-                {
-                    case 0:  //string
-                        pairs.TryAdd(DotAddress, WriteInData);
-                        break;
-                    case 1:  //double
-                        pairs.TryAdd(DotAddress, double.Parse(WriteInData));
-                        break;
-                    case 2:  //float
-                        pairs.TryAdd(DotAddress, float.Parse(WriteInData));
-                        break;
-                    case 3:  //int
-                        pairs.TryAdd(DotAddress, int.Parse(WriteInData));
-                        break;
-                    case 4:  //bool
-                        pairs.TryAdd(DotAddress, bool.Parse(WriteInData));
-                        break;
-                }
-            }
-            catch (Exception ex)
-            {
-                Output($"数据格式不正确:{ex.Message}");
-                return;
-            }
-            //写入
-            OperateResult operateResult = communication.Write(pairs);
-            //信息输出
-            Output(operateResult.ToJson().JsonFormatting());
-        }
-
-        /// <summary>
-        /// 取消订阅
-        /// </summary>
-        public ICommand UnSubscribe { get => new RelayCommand(OnUnSubscribe); }
-        public void OnUnSubscribe()
-        {
-            Address? address = OrganizationAddress();
-            if (address == null) return;
-            //取消订阅
-            OperateResult operateResult = communication.UnSubscribe(address);
-            //信息输出
-            Output(operateResult.ToJson().JsonFormatting());
-        }
-
-        /// <summary>
-        /// 树控件被选中
-        /// </summary>
-        public ICommand TreeView_SelectedItemChanged { get => new RelayCommand<System.Windows.RoutedPropertyChangedEventArgs<object>>(OnTreeView_SelectedItemChanged); }
-        public void OnTreeView_SelectedItemChanged(System.Windows.RoutedPropertyChangedEventArgs<object> e)
-        {
-            NodeStructuralBody? nodeStructuralBody = e.NewValue as NodeStructuralBody;
-            ReferenceDescription? referenceDescription = nodeStructuralBody?.NodeID as ReferenceDescription;
-            NodeId? nodeId = (NodeId)referenceDescription?.NodeId;
-            if (nodeStructuralBody?.Children.Count == 1 && string.IsNullOrWhiteSpace(nodeStructuralBody?.Children[0].Name))
-            {
-                nodeStructuralBody?.Children.Clear();
-                GetNodeInform(nodeId, nodeStructuralBody, tokenSource);
-            }
-            if (nodeId != null)
-            {
-                //节点地址赋值
-                DotAddress = nodeId.ToString();
-
-                //实例化节点数据
-                List<NodeId> nodeIds = new List<NodeId>();
-
-                if (nodeStructuralBody?.Children.Count > 0)  //读取节点下的数据
-                {
-                    //汇总节点数据
-                    foreach (var item in nodeStructuralBody.Children)
-                    {
-                        nodeIds.Add((NodeId)(item?.NodeID as ReferenceDescription)?.NodeId);
-                    }
-                }
-                else   //读取当前节点数据
-                {
-                    nodeIds.Add(nodeId);
-                }
-
-                DataValue[] dataValues = communication.DetailedReadAllNodeData(nodeIds);
-
-                if (dataValues != null)
-                {
-                    ShowDetailedMessage(dataValues, nodeIds);
-                }
-            }
-        }
-
-        /// <summary>
-        /// 表格列被选中
-        /// </summary>
-        public ICommand DataGrid_SelectedCellsChanged { get => new RelayCommand<object>(OnDataGrid_SelectedCellsChanged); }
-        public void OnDataGrid_SelectedCellsChanged(object e)
-        {
-            if (NodeMessageSelectedItem?.Name == null)
-            {
-                return;
-            }
-            DotAddress = NodeMessageSelectedItem?.Address;
-        }
-
-        /// <summary>
-        /// treeviewitem  被展开
-        /// </summary>
-        public ICommand TreeViewItem_Expanded { get => new RelayCommand<RoutedEventArgs>(OnTreeViewItem_Expanded); }
-        private void OnTreeViewItem_Expanded(RoutedEventArgs e)
-        {
-            TreeViewItem? treeViewItem = e.OriginalSource as TreeViewItem;
-            NodeStructuralBody? nodeStructuralBody = treeViewItem?.DataContext as NodeStructuralBody;
-            ReferenceDescription? referenceDescription = nodeStructuralBody?.NodeID as ReferenceDescription;
-            NodeId? nodeId = (NodeId)referenceDescription?.NodeId;
-            if (nodeStructuralBody?.Children.Count == 1 && string.IsNullOrWhiteSpace(nodeStructuralBody?.Children[0].Name))
-            {
-                nodeStructuralBody?.Children.Clear();
-                GetNodeInform(nodeId, nodeStructuralBody, tokenSource);
-            }
-        }
-
-        /// <summary>
-        /// object[] 分割
-        /// </summary>
-        /// <param name="Data">数据</param>
-        /// <param name="Segmentation">几位几位分割</param>
-        private List<object[]> Segmentation(object[] Data, int Segmentation)
-        {
-            List<object[]> bytels = new List<object[]>();
-            for (int i = 0; i < Data.Length; i += Segmentation)
-            {
-                object[] bytes = new object[Segmentation];
-                for (int ic = 0; ic < Segmentation; ic++)
-                {
-                    bytes[ic] = Data[i + ic];
-                }
-                bytels.Add(bytes);
-                if (bytels.Count.Equals(Data.Length / Segmentation)) break;
-            }
-            return bytels;
-        }
-
-        /// <summary>
-        /// 显示节点或节点下的详细数据
-        /// </summary>
-        private void ShowDetailedMessage(DataValue[] dataValues, List<NodeId> nodeIds)
-        {
-            //数据先分割
-            List<object[]> objects = Segmentation(dataValues, 5);
-            //实例化
-            if (NodeMessage == null)
-            {
-                NodeMessage = new ObservableCollection<NodeMessageStructuralBody>();
-            }
-            //清空历史数据
-            NodeMessage.Clear();
-
-            for (int i = 0; i < nodeIds.Count; i++)
-            {
-                //名称
-                string name = nodeIds[i].ToString().Split('=')[nodeIds[i].ToString().Split('=').Length - 1];
-                //地址
-                string address = nodeIds[i].ToString();
-                //值
-                string value = objects[i][1].ToString();
-                //类型
-                string type = (communication.GetNodeValueType(nodeIds[i])).ToString();
-                //描述
-                string des = objects[i][4].ToString();
-                //访问级别
-                string accessLevel = communication.GetAccessLevel((DataValue)objects[i][2]);
-
-                NodeMessage.Add(new NodeMessageStructuralBody() { AccessLevel = accessLevel, Name = name, Type = type, Value = value, Description = des, Address = address });
-            }
-        }
-
-        /// <summary>
-        /// 获取节点信息
-        /// </summary>
-        /// <param name="nodeId">节点信息</param>
-        /// <param name="nodeStructuralBody">节点存储地方</param>
-        /// <param name="token">生命周期令牌</param>
-        /// <param name="depth">获取的深度</param>
-        /// <returns></returns>
-        private Task GetNodeInform(NodeId? nodeId, NodeStructuralBody? nodeStructuralBody, CancellationTokenSource token)
-        {
-            //异步
-            return Task.Run(() =>
-            {
-                //起一个新线程
-                return Task.Factory.StartNew(() =>
-                {
-                    try
-                    {
-                        //当前列下所有节点
-                        ReferenceDescriptionCollection references;
-
-                        //说明这是最开始的获取,没有nodeid
-                        if (nodeId == null)
-                        {
-                            //第一次获取
-                            references = communication.GetAllNode(communication.GetNodeID()).Result;
-                        }
-                        else
-                        {
-                            //这是获取下一层了
-                            references = communication.GetAllNode(nodeId).Result;
-                        }
-
-                        //获取成功
-                        if (references != null && references.Count > 0)
-                        {
-                            //遍历数据
-                            foreach (var item in references)
-                            {
-                                //当前节点信息
-                                ReferenceDescription target = item;
-                                //得到这个节点下有多少个节点
-                                ReferenceDescriptionCollection descriptions = communication.GetAllNode((NodeId)target.NodeId).Result;
-                                if (descriptions != null)
-                                {
-                                    System.Windows.Application.Current?.Dispatcher.Invoke(delegate ()
-                                    {
-                                        try
-                                        {
-                                            //当前节点下有多少节点数量
-                                            int count = descriptions.Count;
-                                            //获取展示图标
-                                            string imageName = communication.GetNodeIconType(target, communication.GetNodeID());
-                                            DrawingImage? drawingImage = (DrawingImage)System.Windows.Application.Current.FindResource(imageName);
-
-                                            //新建一个结构体
-                                            NodeStructuralBody structuralBody = new NodeStructuralBody() { Name = target.BrowseName.Name, Icon = drawingImage, NodeID = target, Count = count > 0 ? $"( {count} )" : string.Empty };
-
-                                            //看此节点下是否存在节点
-                                            if (count > 0)
-                                            {
-                                                //有数据的话,随便加一个,让其展示折叠图标
-                                                structuralBody.Children.Add(new NodeStructuralBody());
-                                            }
-                                            //当没传为空时,则使用集合添加
-                                            if (nodeStructuralBody == null)
-                                            {
-                                                Node.Add(structuralBody);
-                                            }
-                                            else
-                                            {
-                                                //添加新的节点
-                                                nodeStructuralBody.Children.Add(structuralBody);
-                                            }
-                                        }
-                                        catch { }
-                                    });
-                                }
-                            }
-                        }
-                    }
-                    catch { }
-                }, token.Token);
-            }, token.Token);
-        }
-
-        /// <summary>
-        /// 组织地址
-        /// </summary>
-        /// <returns></returns>
-        private Address? OrganizationAddress()
-        {
-            YSAI.Model.@enum.DataType dataType = Model.@enum.DataType.String;
-            try
-            {
-                switch (DataType)
-                {
-                    case 0:  //string
-                        dataType = Model.@enum.DataType.String;
-                        break;
-                    case 1:  //double
-                        dataType = Model.@enum.DataType.Double;
-                        break;
-                    case 2:  //float
-                        dataType = Model.@enum.DataType.Float;
-                        break;
-                    case 3:  //int
-                        dataType = Model.@enum.DataType.Int;
-                        break;
-                    case 4:  //bool
-                        dataType = Model.@enum.DataType.Bool;
-                        break;
-                }
-            }
-            catch (Exception ex)
-            {
-                Output($"数据格式不正确:{ex.Message}");
-                return null;
-            }
-
-            Address address = new Address();
-            address.AddressArray = new List<AddressDetails>()
-            {
-                new AddressDetails()
-                {
-                    AddressName = DotAddress,
-                    AddressDataType=dataType,
-                }
-            };
-            return address;
-        }
-        private void Communication_OnEvent(object? sender, EventResult e)
-        {
-            if (e.RData != null && e.RData is ConcurrentDictionary<string, AddressValue>)
-            {
-                ConcurrentDictionary<string, AddressValue>? pairs = e.GetRData<ConcurrentDictionary<string, AddressValue>>();
-                if (pairs != null)
-                {
-                    foreach (var itemc in pairs)
-                    {
-                        Output($"{e.Message}\r\n键:{itemc.Key}\r\n值:{itemc.Value.Value}\r\n", false);
-                    }
-                }
-            }
-        }
-
-        #region 右键菜单功能
-        /*
-        /// <summary>
-        /// 导出节点
-        /// </summary>
-        /// <param name="body"></param>
-        private async Task<NodeJsonStructuralBody> ExpNodes(NodeStructuralBody node, NodeJsonStructuralBody nodeJson = null)
-        {
-            if (nodeJson == null)
-            {
-                nodeJson = new NodeJsonStructuralBody();
-            }
-
-            List<NodeId> nodeIds = new List<NodeId>();
-            nodeIds.Add((NodeId)(node.NodeID as ReferenceDescription).NodeId);
-            DataValue[] dataValues = communication.DetailedReadAllNodeData(nodeIds);  //获取此节点信息
-
-            nodeJson.DataType = (communication.GetNodeValueType((NodeId)(node.NodeID as ReferenceDescription).NodeId)).ToString();
-            nodeJson.Name = dataValues[3].ToString();
-            nodeJson.Description = dataValues[4].ToString();
-            if (node.Children.Count > 0)  //父级
-            {
-                if (nodeJson.Nodes == null)
-                {
-                    nodeJson.Nodes = new List<NodeJsonStructuralBody>();
-                }
-                foreach (var item in node.Children)
-                {
-                    nodeJson.Nodes.Add(await ExpNodes(item, new NodeJsonStructuralBody()));
-                }
-            }
-            else
-            {
-                nodeJson.Address = (node.NodeID as ReferenceDescription).NodeId.ToString();
-                nodeJson.Name = nodeJson.Address.Split('=')[nodeJson.Address.Split('=').Length - 1];
-            }
-            return nodeJson;
-        }
-
-        /// <summary>
-        /// 订阅节点  只获取节点的订阅
-        /// </summary>
-        /// <param name="node"></param>
-        private List<string> SubscribeNodes(NodeStructuralBody node, List<string> str = null)
-        {
-            if (str == null)
-            {
-                str = new List<string>();
-            }
-            if (node.Children.Count == 0)
-            {
-                str.Add((node.NodeID as ReferenceDescription).NodeId.ToString());
-            }
-            else
-            {
-                foreach (var item in node.Children)
-                {
-                    str.AddRange(SubscribeNodes(item, new List<string>()));
-                }
-            }
-            return str;
-        }
-
-
-        /// <summary>
-        /// 树控件让其选中
-        /// </summary>
-        /// <returns></returns>
-        private DependencyObject VisualUpwardSearch<T>(DependencyObject source)
-        {
-            while (source != null && source.GetType() != typeof(T))
-                source = VisualTreeHelper.GetParent(source);
-            return source;
-        }
-
-        /// <summary>
-        /// 鼠标右键在树控件按下 让其选中
-        /// </summary>
-        /// <param name="e"></param>
-        public void OnTreeView_PreviewMouseRightButtonDown(MouseButtonEventArgs e)
-        {
-            TreeViewItem? treeView = VisualUpwardSearch<TreeViewItem>(e.OriginalSource as DependencyObject) as TreeViewItem;
-            if (treeView == null)
-            {
-                NodeSelectedItem = null;
-            }
-            else
-            {
-                NodeSelectedItem = treeView.DataContext as NodeStructuralBody;
-            }
-        }
-
-
-        /// <summary>
-        /// 鼠标右键在树控件按下
-        /// </summary>
-        public ICommand TreeView_PreviewMouseRightButtonDown { get => new RelayCommand<MouseButtonEventArgs>(OnTreeView_PreviewMouseRightButtonDown); }
-
-        /// <summary>
-        /// 批量订阅
-        /// </summary>
-        public ICommand ContextMenu_Subscribe { get => new RelayCommand<object>(OnContextMenu_Subscribe); }
-
-        /// <summary>
-        /// 取消批量订阅
-        /// </summary>
-        public ICommand ContextMenu_UnSubscribe { get => new RelayCommand<Object>(OnContextMenu_UnSubscribe); }
-
-        /// <summary>
-        /// 导出节点
-        /// </summary>
-        public ICommand ContextMenu_ExpNode { get => new RelayCommand<object>(OnContextMenu_ExpNode); }
-
-
-        /// <summary>
-        /// 右键菜单点击取消批量订阅
-        /// </summary>
-        /// <param name="e"></param>
-        public void OnContextMenu_UnSubscribe(object e)
-        {
-            if (NodeSelectedItem == null)
-            {
-                Output("请选中节点后操作");
-                return;
-            }
-            Address address = new Address();
-            address.AddressArray = new List<AddressDetails>();
-            foreach (var item in SubscribeNodes(NodeSelectedItem))
-            {
-                address.AddressArray.Add(new AddressDetails()
-                {
-                    AddressName = item
-                });
-            }
-            //取消订阅
-            OperateResult operateResult = communication.UnSubscribe(address);
-            Output(operateResult.ToJson().JsonFormatting());
-        }
-
-        /// <summary>
-        /// 右键菜单点击批量订阅
-        /// </summary>
-        /// <param name="e"></param>
-        public async void OnContextMenu_Subscribe(object e)
-        {
-            if (NodeSelectedItem == null)
-            {
-                Output("请选中节点后操作");
-                return;
-            }
-
-            Address address = new Address();
-            address.AddressArray = new List<AddressDetails>();
-            foreach (var item in SubscribeNodes(NodeSelectedItem))
-            {
-                address.AddressArray.Add(new AddressDetails()
-                {
-                    AddressName = item
-                });
-            }
-
-            //订阅
-            OperateResult operateResult = communication.Subscribe(address);
-            Output(operateResult.ToJson().JsonFormatting());
-        }
-
-        private List<NodeJsonStructuralBody> nodes;
-
-        private void ec(NodeJsonStructuralBody node)
-        {
-            if (nodes == null)
-            {
-                nodes = new List<NodeJsonStructuralBody>();
-            }
-            if (node.Nodes != null)
-            {
-                foreach (var item in node.Nodes)
-                {
-                    if (item.Nodes != null)
-                    {
-                        foreach (var no in item.Nodes)
-                        {
-                            ec(no);
-                        }
-                    }
-                    else
-                    {
-                        nodes.Add(item);
-                    }
-                }
-            }
-            else
-            {
-                nodes.Add(node);
-            }
-        }
-
-        /// <summary>
-        /// 导出节点
-        /// </summary>
-        /// <param name="e"></param>
-        public async void OnContextMenu_ExpNode(object e)
-        {
-            if (NodeSelectedItem == null)
-            {
-                Output("请选中节点后操作");
-                return;
-            }
-            string str = YSAI.Unility.Windows.FileTool.SelectFolder();
-            if (string.IsNullOrEmpty(str))
-            {
-                Output("取消节点导出,未选择存储路径");
-            }
-            else
-            {
-                NodeJsonStructuralBody nodeStructuralBody = await ExpNodes(NodeSelectedItem);
-                string Path = str + $"\\[{nodeStructuralBody.Name}]Node {DateTime.Now.ToString("yyyyMMddHHmmssffff")}.json";
-                //设置json序列化反序列化格式
-                JsonSerializerSettings JsonSetting = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
-                //当有空数据时不做解析,且进行缩进处理
-                string Json = JsonConvert.SerializeObject(nodeStructuralBody, Newtonsoft.Json.Formatting.None, JsonSetting);
-                //写入文件
-                FileTool.StringToFile(Path, Json.JsonFormatting());
-
-                ec(nodeStructuralBody);//, str + $"\\[{nodeStructuralBody.Name}]Node_Address {DateTime.Now.ToString("yyyyMMddHHmmssffff")}.json");
-
-                Address address = new Address();
-                address.AddressArray = new List<AddressDetails>();
-                foreach (var item in nodes)
-                {
-                    address.AddressArray.Add(new AddressDetails
-                    {
-                        AddressName = item.Address,
-                        AddressDescribe = item.Description,
-                        AddressDataType = communication.TypeConvert((BuiltInType)Enum.Parse(typeof(BuiltInType), item.DataType))
-                    });
-                }
-                Path = str + $"\\[{nodeStructuralBody.Name}]Node_Address {DateTime.Now.ToString("yyyyMMddHHmmssffff")}.json";
-                //写入文件
-                FileTool.StringToFile(Path, address.ToJson().JsonFormatting());
-
-                Output($"节点成功导出至:{Path}");
-            }
-        }*/
-        #endregion
     }
 }

+ 3 - 3
src/YSAI.Tool.Wpf/controllers/MainController.cs

@@ -45,9 +45,9 @@ namespace YSAI.Tool.Wpf.controllers
                         Icon = new SymbolIcon { Symbol = SymbolRegular.Toolbox12 , Filled = true },
                         MenuItems = new object[]
                         {
-                            CreationControl("[ 客户端 ]", SymbolRegular.DocumentTextToolbox24, typeof(AboutUs)),
-                            CreationControl("[ 服务端 ]", SymbolRegular.DesktopToolbox24, typeof(AboutUs)),
-                            CreationControl(string.Format("{0,-8}{1}", "[ 服务端 ]", "WebSocket"), SymbolRegular.ClockToolbox24, typeof(AboutUs))
+                            CreationControl("[ 客户端 ]", SymbolRegular.DocumentTextToolbox24, typeof(MqttClient)),
+                            CreationControl("[ 服务端 ]", SymbolRegular.DesktopToolbox24, typeof(MqttService)),
+                            CreationControl(string.Format("{0,-8}{1}", "[ 服务端 ]", "WebSocket"), SymbolRegular.ClockToolbox24, typeof(MqttWsService))
                         }
                   },
                   new NavigationViewItem()

+ 28 - 0
src/YSAI.Tool.Wpf/controllers/MqttClientController.cs

@@ -0,0 +1,28 @@
+using System.Windows;
+using YSAI.Mqtt.client;
+using YSAI.Tool.Wpf.template;
+using static YSAI.Mqtt.client.MqttClientData;
+
+namespace YSAI.Tool.Wpf.controllers
+{
+    public class MqttClientController : RelayController<Basics>
+    {
+        public MqttClientController()
+        {
+            //初始化基础数据
+            BasicsData = new Basics();
+            //工具标题
+            ToolTitle = "Mqtt 客户端工具";
+            //配置文件名
+            FileName = typeof(MqttClientData).Name;
+            //Ascii是否显示
+            AsciiVisibility = Visibility.Collapsed;
+            //Hex是否显示
+            HexVisibility = Visibility.Collapsed;
+            //信息格式;  0 ASCII ; 1 HEX
+            InfoFormat = 0;
+
+            Relay = new MqttClientOperate();
+        }
+    }
+}

+ 400 - 0
src/YSAI.Tool.Wpf/template/DaqController.cs

@@ -0,0 +1,400 @@
+using CommunityToolkit.Mvvm.Input;
+using System.Collections.Concurrent;
+using System.IO;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using YSAI.Core.Wpf.mvvm;
+using YSAI.Model.data;
+using YSAI.Model.@interface;
+using YSAI.Unility;
+
+namespace YSAI.Tool.Wpf.template
+{
+    /// <summary>
+    /// 采集控制器
+    /// </summary>
+    public abstract class DaqController<T> : NotifyObject
+    {
+        #region 统一需要的数据
+        /// <summary>
+        /// 导出的文件名
+        /// </summary>
+        public string FileName { get; set; }
+        /// <summary>
+        /// 工具标题
+        /// </summary>
+        public string ToolTitle
+        {
+            get => GetProperty(() => ToolTitle);
+            set => SetProperty(() => ToolTitle, value);
+        }
+        /// <summary>
+        /// 数据源
+        /// </summary>
+        public T BasicsData
+        {
+            get => GetProperty(() => BasicsData);
+            set => SetProperty(() => BasicsData, value);
+        }
+
+        /// <summary>
+        /// 导入基础数据命令
+        /// </summary>
+        public ICommand IncBasics { get => new RelayCommand(OnIncBasics); }
+
+        /// <summary>
+        /// 导入基础数据
+        /// </summary>
+        public void OnIncBasics()
+        {
+            (string RetFilePath, string RetFileName) Data = Unility.Windows.FileTool.SelectFiles("json");
+            if (!string.IsNullOrEmpty(Data.RetFilePath))
+            {
+                T? basics = FileTool.FileToString(Data.RetFilePath).ToJsonEntity<T>();
+                if (basics == null)
+                {
+                    Output("导入失败");
+                }
+                else
+                {
+                    Output("导入成功");
+                    BasicsData = basics;
+                }
+            }
+        }
+
+        /// <summary>
+        /// 导出基础数据命令
+        /// </summary>
+        public ICommand ExpBasics { get => new RelayCommand(OnExpBasics); }
+
+        /// <summary>
+        /// 导出基础数据
+        /// </summary>
+        public void OnExpBasics()
+        {
+            string path = Unility.Windows.FileTool.SelectFolder();
+            if (!string.IsNullOrEmpty(path))
+            {
+                FileTool.StringToFile(Path.Combine(path, $"{FileName}[{DateTime.Now.ToString("yyyyMMddHHmmss")}].json"), BasicsData.ToJson());
+                Output("导出成功");
+            }
+        }
+
+        private string _info = string.Empty;
+        /// <summary>
+        /// 信息
+        /// </summary>
+        public string Info
+        {
+            get => _info;
+            set => SetProperty(ref _info, value);
+        }
+
+        /// <summary>
+        /// 信息框事件
+        /// </summary>
+        public ICommand Info_TextChanged { get => new RelayCommand<TextChangedEventArgs>(OnInfo_TextChanged); }
+
+        /// <summary>
+        /// 信息框事件
+        /// 让滚动条一直处在最下方
+        /// </summary>
+        public void OnInfo_TextChanged(TextChangedEventArgs e)
+        {
+            TextBox textBox = (TextBox)e.Source;
+            textBox.SelectionStart = textBox.Text.Length;
+            textBox.SelectionLength = 0;
+            textBox.ScrollToEnd();
+        }
+
+        /// <summary>
+        /// 输出标准消息
+        /// </summary>
+        /// <param name="Data">数据</param>
+        /// <param name="IsDate">需要时间</param>
+        private Task Output(string Data, bool IsDate = true)
+        {
+            return Task.Run(() =>
+            {
+                Application.Current?.Dispatcher.Invoke(delegate ()
+                {
+                    if (Info?.Length > 10000)
+                    {
+                        Info = string.Empty;
+                    }
+                    if (IsDate)
+                    {
+                        Info += $" {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} : {Data}\r\n";
+                    }
+                    else
+                    {
+                        Info += $"{Data}\r\n";
+                    }
+                });
+            });
+        }
+
+        /// <summary>
+        /// ascii
+        /// </summary>
+        public Visibility AsciiVisibility
+        {
+            get => GetProperty(() => AsciiVisibility);
+            set => SetProperty(() => AsciiVisibility, value);
+        }
+        /// <summary>
+        /// hex
+        /// </summary>
+        public Visibility HexVisibility
+        {
+            get => GetProperty(() => HexVisibility);
+            set => SetProperty(() => HexVisibility, value);
+        }
+
+        /// <summary>
+        /// 接收数据格式
+        /// 0 ASCII
+        /// 1 HEX
+        /// </summary>
+        public int InfoFormat
+        {
+            get => GetProperty(() => InfoFormat);
+            set => SetProperty(() => InfoFormat, value);
+        }
+
+        /// <summary>
+        /// 清空信息命令
+        /// </summary>
+        public ICommand Clear { get => new RelayCommand(OnClear); }
+
+        /// <summary>
+        /// 清空信息
+        /// </summary>
+        public void OnClear()
+        {
+            Info = string.Empty;
+        }
+
+        /// <summary>
+        /// 基础数据的显示隐藏
+        /// </summary>
+        public bool BasicsData_ToggleButtonIsChecked
+        {
+            get => GetProperty(() => BasicsData_ToggleButtonIsChecked);
+            set => SetProperty(() => BasicsData_ToggleButtonIsChecked, value);
+        }
+
+
+        #endregion 统一需要的数据
+
+        /// <summary>
+        /// DAQ对象
+        /// </summary>
+        public IDaq Daq { get; set; }
+
+        /// <summary>
+        /// 数据类型
+        /// </summary>
+        public int DataType
+        {
+            get => GetProperty(() => DataType);
+            set => SetProperty(() => DataType, value);
+        }
+
+        /// <summary>
+        /// 节点地址
+        /// </summary>
+        public string DotAddress
+        {
+            get => GetProperty(() => DotAddress);
+            set => SetProperty(() => DotAddress, value);
+        }
+
+        /// <summary>
+        /// 写入数据
+        /// </summary>
+        public string WriteInData
+        {
+            get => GetProperty(() => WriteInData);
+            set => SetProperty(() => WriteInData, value);
+        }
+
+        /// <summary>
+        /// 启动
+        /// </summary>
+        public ICommand Start { get => new RelayCommand(OnStart); }
+        private void OnStart()
+        {
+            Daq = Daq.CreateInstance(BasicsData).GetRData<IDaq>();
+            OperateResult operateResult = Daq.On();
+            if (operateResult.State)
+            {
+                Daq.OnEvent -= Communication_OnEvent;
+                Daq.OnEvent += Communication_OnEvent;
+            }
+            Output(operateResult.ToJson().JsonFormatting());
+        }
+
+
+        /// <summary>
+        /// 停止
+        /// </summary>
+        public ICommand Stop { get => new RelayCommand(OnStop); }
+        private void OnStop()
+        {
+            if (Daq == null) return;
+            OperateResult operateResult = Daq.Off();
+            Output(operateResult.ToJson().JsonFormatting());
+        }
+
+        /// <summary>
+        /// 读取
+        /// </summary>
+        public ICommand Read { get => new RelayCommand(OnRead); }
+        public void OnRead()
+        {
+            if (Daq == null) return;
+            Address? address = OrganizationAddress();
+            if (address == null) return;
+            OperateResult operateResult = Daq.Read(address);
+            Output(operateResult.ToJson().JsonFormatting());
+        }
+
+        /// <summary>
+        /// 订阅
+        /// </summary>
+        public ICommand Subscribe { get => new RelayCommand(OnSubscribe); }
+        public void OnSubscribe()
+        {
+            if (Daq == null) return;
+            Address? address = OrganizationAddress();
+            if (address == null) return;
+            //订阅
+            OperateResult operateResult = Daq.Subscribe(address);
+            Output(operateResult.ToJson().JsonFormatting());
+        }
+
+        /// <summary>
+        /// 写入
+        /// </summary>
+        public ICommand Write { get => new RelayCommand(OnWrite); }
+        public void OnWrite()
+        {
+            if (Daq == null) return;
+            ConcurrentDictionary<string, object> pairs = new ConcurrentDictionary<string, object>();
+            try
+            {
+                switch (DataType)
+                {
+                    case 0:  //string
+                        pairs.TryAdd(DotAddress, WriteInData);
+                        break;
+                    case 1:  //double
+                        pairs.TryAdd(DotAddress, double.Parse(WriteInData));
+                        break;
+                    case 2:  //float
+                        pairs.TryAdd(DotAddress, float.Parse(WriteInData));
+                        break;
+                    case 3:  //int
+                        pairs.TryAdd(DotAddress, int.Parse(WriteInData));
+                        break;
+                    case 4:  //bool
+                        pairs.TryAdd(DotAddress, bool.Parse(WriteInData));
+                        break;
+                }
+            }
+            catch (Exception ex)
+            {
+                Output($"数据格式不正确:{ex.Message}");
+                return;
+            }
+            //写入
+            OperateResult operateResult = Daq.Write(pairs);
+            //信息输出
+            Output(operateResult.ToJson().JsonFormatting());
+        }
+
+        /// <summary>
+        /// 取消订阅
+        /// </summary>
+        public ICommand UnSubscribe { get => new RelayCommand(OnUnSubscribe); }
+        public void OnUnSubscribe()
+        {
+            if (Daq == null) return;
+            Address? address = OrganizationAddress();
+            if (address == null) return;
+            //取消订阅
+            OperateResult operateResult = Daq.UnSubscribe(address);
+            //信息输出
+            Output(operateResult.ToJson().JsonFormatting());
+        }
+
+        /// <summary>
+        /// 组织地址
+        /// </summary>
+        /// <returns></returns>
+        private Address? OrganizationAddress()
+        {
+            Model.@enum.DataType dataType = Model.@enum.DataType.String;
+            try
+            {
+                switch (DataType)
+                {
+                    case 0:  //string
+                        dataType = Model.@enum.DataType.String;
+                        break;
+                    case 1:  //double
+                        dataType = Model.@enum.DataType.Double;
+                        break;
+                    case 2:  //float
+                        dataType = Model.@enum.DataType.Float;
+                        break;
+                    case 3:  //int
+                        dataType = Model.@enum.DataType.Int;
+                        break;
+                    case 4:  //bool
+                        dataType = Model.@enum.DataType.Bool;
+                        break;
+                }
+            }
+            catch (Exception ex)
+            {
+                Output($"数据格式不正确:{ex.Message}");
+                return null;
+            }
+
+            Address address = new Address();
+            address.AddressArray = new List<AddressDetails>()
+            {
+                new AddressDetails()
+                {
+                    AddressName = DotAddress,
+                    AddressDataType=dataType,
+                }
+            };
+            return address;
+        }
+
+        /// <summary>
+        /// 事件信息通知
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void Communication_OnEvent(object? sender, EventResult e)
+        {
+            if (e.RData != null && e.RData is ConcurrentDictionary<string, AddressValue>)
+            {
+                ConcurrentDictionary<string, AddressValue>? pairs = e.GetRData<ConcurrentDictionary<string, AddressValue>>();
+                if (pairs != null)
+                {
+                    foreach (var itemc in pairs)
+                    {
+                        Output($"{e.Message}\r\n键:{itemc.Key}\r\n值:{itemc.Value.Value}\r\n", false);
+                    }
+                }
+            }
+        }
+    }
+}

+ 397 - 0
src/YSAI.Tool.Wpf/template/DaqView.xaml

@@ -0,0 +1,397 @@
+<ResourceDictionary  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+                     xmlns:c="clr-namespace:YSAI.Tool.Wpf.controllers"
+                     xmlns:txt="clr-namespace:YSAI.Core.Wpf.controls.textbox;assembly=YSAI.Core.Wpf"
+                     xmlns:btn="clr-namespace:YSAI.Core.Wpf.controls.button;assembly=YSAI.Core.Wpf"
+                     xmlns:pt="http://propertytools.org/wpf"
+                     xmlns:helpers="clr-namespace:YSAI.Core.Wpf.style;assembly=YSAI.Core.Wpf"
+                     xmlns:cs="clr-namespace:YSAI.Core.Wpf.converters;assembly=YSAI.Core.Wpf"
+                     xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
+                     xmlns:mvvm="clr-namespace:YSAI.Core.Wpf.mvvm;assembly=YSAI.Core.Wpf">
+    
+    <DataTemplate x:Key="DAQ">
+        <DataTemplate.Resources>
+            <ResourceDictionary>
+                <Style TargetType="ToolTip">
+                    <Setter Property="Foreground" Value="{DynamicResource Font.Content.Foreground}"/>
+                    <Setter Property="BorderBrush" Value="{DynamicResource Control.Border.Color}"/>
+                </Style>
+                <cs:CheckConverter x:Key="CheckConverter" />
+                <ResourceDictionary.MergedDictionaries>
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_ComboBox.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_Button.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_DataGrid.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_Border.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_TbaControl.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_ScrollViewer.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_GroupBox.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_TextBox.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_CheckBox.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_RadioButton.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_DatePicker.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_ContextMenu.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_TreeView.xaml" />
+                </ResourceDictionary.MergedDictionaries>
+            </ResourceDictionary>
+        </DataTemplate.Resources>
+        <Border Background="#F0F2F0" Width="auto" Height="auto" CornerRadius="{DynamicResource WindowCornerRadius}" Margin="50">
+            <GroupBox Style="{StaticResource GroupBoxTab}" Margin="0"  >
+                <GroupBox.Header>
+                    <StackPanel Orientation="Horizontal">
+                        <Grid Background="Transparent">
+                            <Grid.ColumnDefinitions>
+                                <ColumnDefinition Width="auto" />
+                                <ColumnDefinition Width="*" />
+                            </Grid.ColumnDefinitions>
+                            <Image Grid.Column="0" Margin="10,0,8,0" Source="{DynamicResource Tool}" Width="14" />
+                            <TextBlock Text="{Binding ToolTitle}" FontSize="13"  Grid.Column="1" Foreground="{DynamicResource Font.Content.Foreground}" />
+                        </Grid>
+                    </StackPanel>
+                </GroupBox.Header>
+                <!--基础数据与功能区域-->
+                <Grid >
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="auto"/>
+                        <ColumnDefinition Width="*"/>
+                        <ColumnDefinition Width="auto"/>
+                    </Grid.ColumnDefinitions>
+
+                    <!--基础数据-->
+                    <Grid Grid.Column="0"  x:Name="propertyGrid" Margin="0,0,3,0" Width="550">
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="auto"/>
+                            <RowDefinition Height="auto"/>
+                        </Grid.RowDefinitions>
+
+                        <!--导入导出-->
+                        <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,0,-30" Panel.ZIndex="2">
+                            <btn:ButtonControl  
+                                          Width="80"
+                                          Command="{Binding IncBasics}" 
+                                          HorizontalAlignment="Center" 
+                                          VerticalAlignment="Center" 
+                                          IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
+                                          BorderBrush="{DynamicResource Control.Border.Color}"
+                                          IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
+                                          Foreground="{DynamicResource Font.Content.Foreground}"
+                                          Icon="{DynamicResource Inc}"
+                                          Content="导入"
+                                          BorderThickness="1,0,0,0" />
+                            <btn:ButtonControl  
+                                          Width="80"
+                                          Command="{Binding ExpBasics}" 
+                                          HorizontalAlignment="Center" 
+                                          VerticalAlignment="Center" 
+                                          IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
+                                          BorderBrush="{DynamicResource Control.Border.Color}"
+                                          IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
+                                          Foreground="{DynamicResource Font.Content.Foreground}"
+                                          Icon="{DynamicResource Exp}"
+                                          Content="导出"
+                                          BorderThickness="1,0,0,0" />
+                        </StackPanel>
+                        <!--属性表格-->
+                        <Grid Grid.Row="1" Panel.ZIndex="1">
+                            <Grid.Resources>
+                                <ResourceDictionary>
+                                    <Style TargetType="{x:Type Label}" >
+                                        <Setter Property="FontSize" Value="13"/>
+                                        <Setter Property="Foreground" Value="{DynamicResource Font.Content.Foreground}"/>
+                                        <Setter Property="HorizontalAlignment" Value="Left"/>
+                                    </Style>
+                                    <Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource DefaultComboBox}" >
+                                        <Setter Property="Height" Value="25"/>
+                                        <Setter Property="Background" Value="White"/>
+                                        <Setter Property="VerticalAlignment" Value="Center"/>
+                                        <Setter Property="helpers:Style_ComboBox.CornerRadius" Value="{DynamicResource WindowCornerRadius}"/>
+                                    </Style>
+                                    <Style TargetType="{x:Type pt:TextBoxEx}" BasedOn="{StaticResource TextBoxStyle2}">
+                                        <Setter Property="FontSize" Value="13"/>
+                                        <Setter Property="Foreground" Value="{DynamicResource Font.Content.Foreground}"/>
+                                        <Setter Property="BorderBrush" Value="{DynamicResource Control.Border.Color}"/>
+                                        <Setter Property="HorizontalContentAlignment" Value="Center"/>
+                                        <Setter Property="VerticalContentAlignment" Value="Center"/>
+                                    </Style>
+
+                                    <Style TargetType="CheckBox" BasedOn="{StaticResource CheckBoxStyle}"/>
+
+                                    <Style TargetType="ContentControl" >
+                                        <Setter Property="Foreground" Value="{DynamicResource Font.Content.Foreground}"/>
+                                    </Style>
+
+                                    <Style TargetType="Expander">
+                                        <Setter Property="Foreground" Value="{DynamicResource Font.Content.Foreground}"/>
+                                    </Style>
+
+                                    <Style TargetType="RadioButton" BasedOn="{StaticResource RadioButtonStyle}"/>
+
+
+                                    <Style TargetType="GroupBox" BasedOn="{StaticResource GroupBoxTab}"/>
+
+
+                                    <DataTemplate x:Key="HeaderTemplate">
+                                        <StackPanel Orientation="Horizontal">
+                                            <Grid>
+                                                <Grid.ColumnDefinitions>
+                                                    <ColumnDefinition Width="auto" />
+                                                    <ColumnDefinition Width="*" />
+                                                </Grid.ColumnDefinitions>
+                                                <Image Grid.Column="0" Margin="10,0,8,0" Source="{DynamicResource ConfigBase}" Width="14" />
+                                                <TextBlock Text="{Binding}" FontSize="13"  Grid.Column="1" Foreground="{DynamicResource Font.Content.Foreground}" />
+                                            </Grid>
+                                        </StackPanel>
+                                    </DataTemplate>
+
+                                </ResourceDictionary>
+                            </Grid.Resources>
+                            <pt:PropertyGrid  Margin="1,-4,1,0"
+                                              
+SelectedObject="{Binding BasicsData}"
+TabVisibility="VisibleIfMoreThanOne" 
+TextBlock.Foreground="{DynamicResource Font.Content.Foreground}" 
+Foreground="{DynamicResource Font.Content.Foreground}"
+BorderBrush="{DynamicResource Control.Border.Color}"
+CategoryHeaderTemplate="{StaticResource HeaderTemplate}"/>
+                        </Grid>
+                    </Grid>
+
+                    <!--功能模块与信息-->
+                    <Grid Grid.Column="1" >
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="auto"/>
+                            <RowDefinition Height="*"/>
+                        </Grid.RowDefinitions>
+
+                        <ToggleButton Grid.Row="0" Grid.RowSpan="2" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="8,8,0,0" ToolTip="基础数据[隐藏/显示]" Panel.ZIndex="3" IsChecked="{Binding BasicsData_ToggleButtonIsChecked}">
+                            <ToggleButton.Template>
+                                <ControlTemplate TargetType="{x:Type ToggleButton}">
+                                    <Image x:Name="image" Source="{DynamicResource GoLeft_Two}" Stretch="Fill" Width="15" Height="15" />
+                                    <ControlTemplate.Triggers>
+                                        <Trigger Property="IsChecked" Value="True">
+                                            <Setter Property="Source" TargetName="image" Value="{DynamicResource GoRight_Two}"/>
+                                        </Trigger>
+                                    </ControlTemplate.Triggers>
+                                </ControlTemplate>
+                            </ToggleButton.Template>
+                            <ToggleButton.Triggers>
+                                <EventTrigger RoutedEvent="ToggleButton.Unchecked">
+                                    <BeginStoryboard>
+                                        <Storyboard>
+                                            <DoubleAnimation Storyboard.TargetName="propertyGrid" Storyboard.TargetProperty="Width" From="0" To="550" Duration="0:0:0.7"/>
+                                            <ThicknessAnimationUsingKeyFrames Storyboard.TargetName="propertyGrid" Storyboard.TargetProperty="Margin" BeginTime="00:00:00">
+                                                <SplineThicknessKeyFrame KeyTime="00:00:00" Value="0" />
+                                                <SplineThicknessKeyFrame KeyTime="00:00:0.7" Value="0,0,3,0" />
+                                            </ThicknessAnimationUsingKeyFrames>
+                                        </Storyboard>
+                                    </BeginStoryboard>
+                                </EventTrigger>
+                                <EventTrigger RoutedEvent="ToggleButton.Checked">
+                                    <BeginStoryboard>
+                                        <Storyboard>
+                                            <DoubleAnimation Storyboard.TargetName="propertyGrid" Storyboard.TargetProperty="Width" From="550" To="0" Duration="0:0:0.7"/>
+                                            <ThicknessAnimationUsingKeyFrames Storyboard.TargetName="propertyGrid" Storyboard.TargetProperty="Margin" BeginTime="00:00:00">
+                                                <SplineThicknessKeyFrame KeyTime="00:00:00" Value="0,0,3,0" />
+                                                <SplineThicknessKeyFrame KeyTime="00:00:0.7" Value="0" />
+                                            </ThicknessAnimationUsingKeyFrames>
+                                        </Storyboard>
+                                    </BeginStoryboard>
+                                </EventTrigger>
+                            </ToggleButton.Triggers>
+                        </ToggleButton>
+
+                        <!--功能模块-->
+                        <GroupBox Style="{StaticResource GroupBoxTab}" Margin="0" Grid.Row="0">
+                            <GroupBox.Header>
+                                <StackPanel Orientation="Horizontal">
+                                    <Grid Background="transparent">
+                                        <Grid.ColumnDefinitions>
+                                            <ColumnDefinition Width="auto" />
+                                            <ColumnDefinition Width="*" />
+                                        </Grid.ColumnDefinitions>
+                                        <Image Grid.Column="0" Margin="10,0,8,0" Source="{DynamicResource Function}" Width="14" />
+                                        <TextBlock Text="功能" FontSize="13" Margin="0,0,10,0"  Grid.Column="1" Foreground="{DynamicResource Font.Content.Foreground}" />
+                                    </Grid>
+                                </StackPanel>
+                            </GroupBox.Header>
+                            <!--■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■-->
+
+                            <Grid>
+                                <Grid.RowDefinitions>
+                                    <RowDefinition Height="auto"/>
+                                    <RowDefinition Height="*"/>
+                                </Grid.RowDefinitions>
+                                <!--Start / Stop-->
+                                <Grid Grid.Row="0" Grid.Column="1"  Margin="0,-40,0,0">
+                                    <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" >
+                                        <btn:ButtonControl  Grid.Column="5"  Width="80"   Command="{Binding Start}" HorizontalAlignment="Right" VerticalAlignment="Center" BorderThickness="1,0,0,0"
+                                                        IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
+                                                        BorderBrush="{DynamicResource Control.Border.Color}"
+                                                        IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
+                                                        Foreground="{DynamicResource Font.Content.Foreground}"
+                                                        Icon="{DynamicResource ConnectBase}"
+                                                        Content="连接"/>
+
+                                        <btn:ButtonControl  Grid.Column="6"  Width="80"  Command="{Binding Stop}" HorizontalAlignment="Right" VerticalAlignment="Center" BorderThickness="1,0,0,0"
+                                                        IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
+                                                        BorderBrush="{DynamicResource Control.Border.Color}"
+                                                        IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
+                                                        Foreground="{DynamicResource Font.Content.Foreground}"
+                                                        Icon="{DynamicResource OffConnect}"
+                                                        Content="断开"/>
+                                    </StackPanel>
+                                </Grid>
+                                <!--发送区域-->
+                                <Grid Grid.Row="1" Grid.Column="1">
+                                    <Grid Margin="7">
+                                        <Grid.RowDefinitions>
+                                            <RowDefinition/>
+                                            <RowDefinition/>
+                                            <RowDefinition/>
+                                        </Grid.RowDefinitions>
+                                        <Grid.ColumnDefinitions>
+                                            <ColumnDefinition/>
+                                            <ColumnDefinition Width="auto"/>
+                                            <ColumnDefinition  Width="auto"/>
+                                        </Grid.ColumnDefinitions>
+
+                                        <GroupBox Grid.ColumnSpan="3"  Style="{StaticResource GroupBoxTab}" Margin="0,1,0,10">
+                                            <GroupBox.Header>
+                                                <StackPanel Orientation="Horizontal">
+                                                    <Grid Background="transparent">
+                                                        <Grid.ColumnDefinitions>
+                                                            <ColumnDefinition Width="auto" />
+                                                            <ColumnDefinition Width="*" />
+                                                        </Grid.ColumnDefinitions>
+                                                        <Image Grid.Column="0" Margin="10,0,8,0" Source="{DynamicResource DataType}" Width="14" />
+                                                        <TextBlock Text="数据类型" FontSize="13" Margin="0,0,10,0"  Grid.Column="1" Foreground="{DynamicResource Font.Content.Foreground}" />
+                                                    </Grid>
+                                                </StackPanel>
+                                            </GroupBox.Header>
+                                            <Grid Margin="7">
+                                                <Grid.ColumnDefinitions>
+                                                    <ColumnDefinition/>
+                                                    <ColumnDefinition/>
+                                                    <ColumnDefinition/>
+                                                    <ColumnDefinition/>
+                                                    <ColumnDefinition/>
+                                                </Grid.ColumnDefinitions>
+                                                <RadioButton Grid.Column="0" ToolTip="数据类型"  IsChecked="{Binding DataType,Mode=TwoWay,Converter={StaticResource CheckConverter},ConverterParameter=0}" Style="{StaticResource RadioButtonStyle}" Content="String" VerticalAlignment="Center" HorizontalAlignment="Center" />
+                                                <RadioButton Grid.Column="1" ToolTip="数据类型"  IsChecked="{Binding DataType,Mode=TwoWay,Converter={StaticResource CheckConverter},ConverterParameter=1}"  Style="{StaticResource RadioButtonStyle}" Content="Double"  VerticalAlignment="Center" HorizontalAlignment="Center" />
+                                                <RadioButton Grid.Column="2" ToolTip="数据类型"  IsChecked="{Binding DataType,Mode=TwoWay,Converter={StaticResource CheckConverter},ConverterParameter=2}" Style="{StaticResource RadioButtonStyle}" Content="Float" VerticalAlignment="Center" HorizontalAlignment="Center" />
+                                                <RadioButton Grid.Column="3" ToolTip="数据类型"  IsChecked="{Binding DataType,Mode=TwoWay,Converter={StaticResource CheckConverter},ConverterParameter=3}"  Style="{StaticResource RadioButtonStyle}" Content="Int"  VerticalAlignment="Center" HorizontalAlignment="Center" />
+                                                <RadioButton Grid.Column="4" ToolTip="数据类型"  IsChecked="{Binding DataType,Mode=TwoWay,Converter={StaticResource CheckConverter},ConverterParameter=4}" Style="{StaticResource RadioButtonStyle}" Content="Bool" VerticalAlignment="Center" HorizontalAlignment="Center" />
+                                            </Grid>
+                                        </GroupBox>
+
+                                        <txt:TextBoxControl Grid.Column="0" Grid.Row="1" FontSize="13" HorizontalContentAlignment="Center" Text="{Binding DotAddress}"
+                                                              SelectedColor="{DynamicResource Control.Border.Two.Color}"
+                                                              BorderBrush="{DynamicResource Control.Border.Color}"
+                                                              Foreground="{DynamicResource Font.Content.Foreground}"
+                                                              Icon="{DynamicResource Dot}"
+                                                              HintColor="{DynamicResource Control.Border.One.Color}"
+                                                              ShowIcon="True"
+                                                              Hint="节点地址"
+                                                              CornerRadius="{DynamicResource WindowCornerRadius}" />
+
+                                        <txt:TextBoxControl Grid.Column="0" Grid.Row="2" Margin="0,10,0,0" FontSize="13" HorizontalContentAlignment="Center" VerticalAlignment="Center" Text="{Binding WriteInData}"
+                                                             SelectedColor="{DynamicResource Control.Border.Two.Color}"
+                                                             BorderBrush="{DynamicResource Control.Border.Color}"
+                                                             Foreground="{DynamicResource Font.Content.Foreground}"
+                                                             Icon="{DynamicResource WriteIn}"
+                                                             HintColor="{DynamicResource Control.Border.One.Color}"
+                                                             ShowIcon="True"
+                                                             Hint="写入数据"
+                                                             CornerRadius="{DynamicResource WindowCornerRadius}" />
+
+                                        <btn:ButtonControl  Width="100" Grid.Row="1" Grid.Column="1" Command="{Binding Read}" HorizontalAlignment="Center" VerticalAlignment="Center"  CornerRadius="{DynamicResource WindowCornerRadius}"  Margin="10,0,0,0"
+                                                              IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
+                                                              BorderBrush="{DynamicResource Control.Border.Color}"
+                                                              IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
+                                                              Foreground="{DynamicResource Font.Content.Foreground}"
+                                                              Icon="{DynamicResource Read}"
+                                                              Content="读取"/>
+
+                                        <btn:ButtonControl  Width="100" Grid.Row="2" Grid.Column="1" Command="{Binding Write}" HorizontalAlignment="Center" VerticalAlignment="Center"   CornerRadius="{DynamicResource WindowCornerRadius}"  Margin="10,10,0,0"
+                                                              IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
+                                                              BorderBrush="{DynamicResource Control.Border.Color}"
+                                                              IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
+                                                              Foreground="{DynamicResource Font.Content.Foreground}"
+                                                              Icon="{DynamicResource Write}"
+                                                              Content="写入"/>
+
+                                        <btn:ButtonControl  Width="100" Grid.Row="1" Grid.Column="2" Command="{Binding Subscribe}" HorizontalAlignment="Center" VerticalAlignment="Center"  CornerRadius="{DynamicResource WindowCornerRadius}"  Margin="10,0,0,0"
+                                                                IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
+                                                                BorderBrush="{DynamicResource Control.Border.Color}"
+                                                                IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
+                                                                Foreground="{DynamicResource Font.Content.Foreground}"
+                                                                Icon="{DynamicResource Subscribe}"
+                                                                Content="订阅"/>
+
+                                        <btn:ButtonControl  Width="100" Grid.Row="2" Grid.Column="2" Command="{Binding UnSubscribe}" HorizontalAlignment="Center" VerticalAlignment="Center"   CornerRadius="{DynamicResource WindowCornerRadius}"  Margin="10,10,0,0"
+                                                                IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
+                                                                BorderBrush="{DynamicResource Control.Border.Color}"
+                                                                IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
+                                                                Foreground="{DynamicResource Font.Content.Foreground}"
+                                                                Icon="{DynamicResource UnSubscribe}"
+                                                                Content="取消订阅"/>
+
+                                    </Grid>
+                                </Grid>
+                            </Grid>
+
+
+
+
+                            <!--■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■-->
+                        </GroupBox>
+                        <!--信息-->
+                        <GroupBox Style="{StaticResource GroupBoxTab}" Margin="0,5,0,0"  Grid.Row="1">
+                            <GroupBox.Header>
+                                <StackPanel Orientation="Horizontal">
+                                    <Grid>
+                                        <Grid.ColumnDefinitions>
+                                            <ColumnDefinition Width="auto"/>
+                                            <ColumnDefinition Width="*"/>
+                                        </Grid.ColumnDefinitions>
+                                        <Image Grid.Column="0" Margin="10,0,8,0" Source="{DynamicResource Info}" Width="14" />
+                                        <TextBlock Text="信息" FontSize="13" Margin="0,0,10,0"  Grid.Column="1" Foreground="{DynamicResource Font.Content.Foreground}" VerticalAlignment="Center"/>
+                                    </Grid>
+                                </StackPanel>
+                            </GroupBox.Header>
+                            <Grid>
+                                <Grid.RowDefinitions>
+                                    <RowDefinition Height="auto" />
+                                    <RowDefinition />
+                                </Grid.RowDefinitions>
+                                <Grid.ColumnDefinitions>
+                                    <ColumnDefinition />
+                                    <ColumnDefinition Width="auto" />
+                                </Grid.ColumnDefinitions>
+                                <StackPanel Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Right" Margin="0,-40,0,0">
+                                    <RadioButton ToolTip="显示方式" Margin="0,0,15,0"  Visibility="{Binding AsciiVisibility}" IsChecked="{Binding InfoFormat,Mode=TwoWay,Converter={StaticResource CheckConverter},ConverterParameter=0}" Style="{StaticResource RadioButtonStyle}" Content="ASCII" VerticalAlignment="Center" HorizontalAlignment="Right" />
+                                    <RadioButton ToolTip="显示方式" Margin="0,0,15,0" Visibility="{Binding HexVisibility}" IsChecked="{Binding InfoFormat,Mode=TwoWay,Converter={StaticResource CheckConverter},ConverterParameter=1}"  Style="{StaticResource RadioButtonStyle}" Content="Hex"  VerticalAlignment="Center" HorizontalAlignment="Right" />
+                                    <btn:ButtonControl  Width="80"  Command="{Binding Clear}" HorizontalAlignment="Right" VerticalAlignment="Center"  BorderThickness="1,0,0,0"
+IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
+BorderBrush="{DynamicResource Control.Border.Color}"
+IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
+Foreground="{DynamicResource Font.Content.Foreground}"
+Icon="{DynamicResource Clear}"
+Content="清空"/>
+                                </StackPanel>
+                                <TextBox Grid.Row="1" Padding="5" Grid.ColumnSpan="2" Style="{DynamicResource TextBoxStyle2}"  FontSize="13" VerticalContentAlignment="Top" HorizontalContentAlignment="Left" TextWrapping="Wrap" AcceptsReturn="True" VerticalScrollBarVisibility="Visible"  Text="{Binding Info}" >
+                                    <i:Interaction.Triggers>
+                                        <i:EventTrigger EventName="TextChanged">
+                                            <mvvm:EventCommand  Command="{Binding Info_TextChanged}" />
+                                        </i:EventTrigger>
+                                    </i:Interaction.Triggers>
+                                </TextBox>
+                            </Grid>
+                        </GroupBox>
+                    </Grid>
+
+                </Grid>
+            </GroupBox>
+        </Border>
+    </DataTemplate>
+    
+</ResourceDictionary>

+ 289 - 0
src/YSAI.Tool.Wpf/template/RelayController.cs

@@ -0,0 +1,289 @@
+using CommunityToolkit.Mvvm.Input;
+using System.IO;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using YSAI.Core.Wpf.mvvm;
+using YSAI.Model.data;
+using YSAI.Model.@interface;
+using YSAI.Unility;
+
+namespace YSAI.Tool.Wpf.template
+{
+    public class RelayController<T> : NotifyObject
+    {
+        #region 统一需要的数据
+        /// <summary>
+        /// 导出的文件名
+        /// </summary>
+        public string FileName { get; set; }
+        /// <summary>
+        /// 工具标题
+        /// </summary>
+        public string ToolTitle
+        {
+            get => GetProperty(() => ToolTitle);
+            set => SetProperty(() => ToolTitle, value);
+        }
+        /// <summary>
+        /// 数据源
+        /// </summary>
+        public T BasicsData
+        {
+            get => GetProperty(() => BasicsData);
+            set => SetProperty(() => BasicsData, value);
+        }
+
+        /// <summary>
+        /// 导入基础数据命令
+        /// </summary>
+        public ICommand IncBasics { get => new RelayCommand(OnIncBasics); }
+
+        /// <summary>
+        /// 导入基础数据
+        /// </summary>
+        public void OnIncBasics()
+        {
+            (string RetFilePath, string RetFileName) Data = Unility.Windows.FileTool.SelectFiles("json");
+            if (!string.IsNullOrEmpty(Data.RetFilePath))
+            {
+                T? basics = FileTool.FileToString(Data.RetFilePath).ToJsonEntity<T>();
+                if (basics == null)
+                {
+                    Output("导入失败");
+                }
+                else
+                {
+                    Output("导入成功");
+                    BasicsData = basics;
+                }
+            }
+        }
+
+        /// <summary>
+        /// 导出基础数据命令
+        /// </summary>
+        public ICommand ExpBasics { get => new RelayCommand(OnExpBasics); }
+
+        /// <summary>
+        /// 导出基础数据
+        /// </summary>
+        public void OnExpBasics()
+        {
+            string path = Unility.Windows.FileTool.SelectFolder();
+            if (!string.IsNullOrEmpty(path))
+            {
+                FileTool.StringToFile(Path.Combine(path, $"{FileName}[{DateTime.Now.ToString("yyyyMMddHHmmss")}].json"), BasicsData.ToJson());
+                Output("导出成功");
+            }
+        }
+
+        private string _info = string.Empty;
+        /// <summary>
+        /// 信息
+        /// </summary>
+        public string Info
+        {
+            get => _info;
+            set => SetProperty(ref _info, value);
+        }
+
+        /// <summary>
+        /// 信息框事件
+        /// </summary>
+        public ICommand Info_TextChanged { get => new RelayCommand<TextChangedEventArgs>(OnInfo_TextChanged); }
+
+        /// <summary>
+        /// 信息框事件
+        /// 让滚动条一直处在最下方
+        /// </summary>
+        public void OnInfo_TextChanged(TextChangedEventArgs e)
+        {
+            TextBox textBox = (TextBox)e.Source;
+            textBox.SelectionStart = textBox.Text.Length;
+            textBox.SelectionLength = 0;
+            textBox.ScrollToEnd();
+        }
+
+        /// <summary>
+        /// 输出标准消息
+        /// </summary>
+        /// <param name="Data">数据</param>
+        /// <param name="IsDate">需要时间</param>
+        private Task Output(string Data, bool IsDate = true)
+        {
+            return Task.Run(() =>
+            {
+                Application.Current?.Dispatcher.Invoke(delegate ()
+                {
+                    if (Info?.Length > 10000)
+                    {
+                        Info = string.Empty;
+                    }
+                    if (IsDate)
+                    {
+                        Info += $" {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} : {Data}\r\n";
+                    }
+                    else
+                    {
+                        Info += $"{Data}\r\n";
+                    }
+                });
+            });
+        }
+
+        /// <summary>
+        /// ascii
+        /// </summary>
+        public Visibility AsciiVisibility
+        {
+            get => GetProperty(() => AsciiVisibility);
+            set => SetProperty(() => AsciiVisibility, value);
+        }
+        /// <summary>
+        /// hex
+        /// </summary>
+        public Visibility HexVisibility
+        {
+            get => GetProperty(() => HexVisibility);
+            set => SetProperty(() => HexVisibility, value);
+        }
+
+        /// <summary>
+        /// 接收数据格式
+        /// 0 ASCII
+        /// 1 HEX
+        /// </summary>
+        public int InfoFormat
+        {
+            get => GetProperty(() => InfoFormat);
+            set => SetProperty(() => InfoFormat, value);
+        }
+
+        /// <summary>
+        /// 清空信息命令
+        /// </summary>
+        public ICommand Clear { get => new RelayCommand(OnClear); }
+
+        /// <summary>
+        /// 清空信息
+        /// </summary>
+        public void OnClear()
+        {
+            Info = string.Empty;
+        }
+
+        /// <summary>
+        /// 基础数据的显示隐藏
+        /// </summary>
+        public bool BasicsData_ToggleButtonIsChecked
+        {
+            get => GetProperty(() => BasicsData_ToggleButtonIsChecked);
+            set => SetProperty(() => BasicsData_ToggleButtonIsChecked, value);
+        }
+
+
+        #endregion 统一需要的数据
+
+
+        /// <summary>
+        /// 主题
+        /// </summary>
+        public string Topic
+        {
+            get => GetProperty(() => Topic);
+            set => SetProperty(() => Topic, value);
+        }
+
+        /// <summary>
+        /// 内容
+        /// </summary>
+        public string Content
+        {
+            get => GetProperty(() => Content);
+            set => SetProperty(() => Content, value);
+        }
+
+        /// <summary>
+        /// Relay对象
+        /// </summary>
+        public IRelay Relay { get; set; }
+
+        /// <summary>
+        /// 启动
+        /// </summary>
+        public ICommand Start { get => new RelayCommand(OnStart); }
+        private void OnStart()
+        {
+            Relay = Relay.CreateInstance(BasicsData).GetRData<IRelay>();
+            OperateResult operateResult = Relay.On();
+            if (operateResult.State)
+            {
+                Relay.OnEvent -= Communication_OnEvent;
+                Relay.OnEvent += Communication_OnEvent;
+            }
+            Output(operateResult.ToJson().JsonFormatting());
+        }
+
+
+        /// <summary>
+        /// 停止
+        /// </summary>
+        public ICommand Stop { get => new RelayCommand(OnStop); }
+        private void OnStop()
+        {
+            if (Relay == null) return;
+            OperateResult operateResult = Relay.Off();
+            Output(operateResult.ToJson().JsonFormatting());
+        }
+
+        /// <summary>
+        /// 订阅
+        /// </summary>
+        public ICommand Subscribe { get => new RelayCommand(OnSubscribe); }
+        public void OnSubscribe()
+        {
+            if (Relay == null) return;
+            //订阅
+            OperateResult operateResult = Relay.Subscribe(Topic);
+            Output(operateResult.ToJson().JsonFormatting());
+        }
+
+        /// <summary>
+        /// 取消订阅
+        /// </summary>
+        public ICommand UnSubscribe { get => new RelayCommand(OnUnSubscribe); }
+        public void OnUnSubscribe()
+        {
+            if (Relay == null) return;
+            //取消订阅
+            OperateResult operateResult = Relay.UnSubscribe(Topic);
+            //信息输出
+            Output(operateResult.ToJson().JsonFormatting());
+        }
+
+        /// <summary>
+        /// 发布
+        /// </summary>
+        public ICommand Issue { get => new RelayCommand(OnIssue); }
+        public void OnIssue()
+        {
+            if (Relay == null) return;
+            //取消订阅
+            OperateResult operateResult = Relay.Produce(Topic, Content);
+            //信息输出
+            Output(operateResult.ToJson().JsonFormatting());
+        }
+
+
+        /// <summary>
+        /// 事件信息通知
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void Communication_OnEvent(object? sender, EventResult e)
+        {
+            Output(e.Message);
+        }
+    }
+}

+ 356 - 0
src/YSAI.Tool.Wpf/template/RelayView.xaml

@@ -0,0 +1,356 @@
+<ResourceDictionary  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+                     xmlns:c="clr-namespace:YSAI.Tool.Wpf.controllers"
+                     xmlns:txt="clr-namespace:YSAI.Core.Wpf.controls.textbox;assembly=YSAI.Core.Wpf"
+                     xmlns:btn="clr-namespace:YSAI.Core.Wpf.controls.button;assembly=YSAI.Core.Wpf"
+                     xmlns:pt="http://propertytools.org/wpf"
+                     xmlns:helpers="clr-namespace:YSAI.Core.Wpf.style;assembly=YSAI.Core.Wpf"
+                     xmlns:cs="clr-namespace:YSAI.Core.Wpf.converters;assembly=YSAI.Core.Wpf"
+                     xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
+                     xmlns:mvvm="clr-namespace:YSAI.Core.Wpf.mvvm;assembly=YSAI.Core.Wpf">
+    
+    <DataTemplate x:Key="RELAY">
+        <DataTemplate.Resources>
+            <ResourceDictionary>
+                <Style TargetType="ToolTip">
+                    <Setter Property="Foreground" Value="{DynamicResource Font.Content.Foreground}"/>
+                    <Setter Property="BorderBrush" Value="{DynamicResource Control.Border.Color}"/>
+                </Style>
+                <cs:CheckConverter x:Key="CheckConverter" />
+                <ResourceDictionary.MergedDictionaries>
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_ComboBox.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_Button.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_DataGrid.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_Border.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_TbaControl.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_ScrollViewer.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_GroupBox.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_TextBox.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_CheckBox.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_RadioButton.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_DatePicker.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_ContextMenu.xaml" />
+                    <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_TreeView.xaml" />
+                </ResourceDictionary.MergedDictionaries>
+            </ResourceDictionary>
+        </DataTemplate.Resources>
+        <Border Background="#F0F2F0" Width="auto" Height="auto" CornerRadius="{DynamicResource WindowCornerRadius}" Margin="50">
+            <GroupBox Style="{StaticResource GroupBoxTab}" Margin="0"  >
+                <GroupBox.Header>
+                    <StackPanel Orientation="Horizontal">
+                        <Grid Background="Transparent">
+                            <Grid.ColumnDefinitions>
+                                <ColumnDefinition Width="auto" />
+                                <ColumnDefinition Width="*" />
+                            </Grid.ColumnDefinitions>
+                            <Image Grid.Column="0" Margin="10,0,8,0" Source="{DynamicResource Tool}" Width="14" />
+                            <TextBlock Text="{Binding ToolTitle}" FontSize="13"  Grid.Column="1" Foreground="{DynamicResource Font.Content.Foreground}" />
+                        </Grid>
+                    </StackPanel>
+                </GroupBox.Header>
+                <!--基础数据与功能区域-->
+                <Grid >
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="auto"/>
+                        <ColumnDefinition Width="*"/>
+                        <ColumnDefinition Width="auto"/>
+                    </Grid.ColumnDefinitions>
+
+                    <!--基础数据-->
+                    <Grid Grid.Column="0"  x:Name="propertyGrid" Margin="0,0,3,0" Width="550">
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="auto"/>
+                            <RowDefinition Height="auto"/>
+                        </Grid.RowDefinitions>
+
+                        <!--导入导出-->
+                        <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,0,-30" Panel.ZIndex="2">
+                            <btn:ButtonControl  
+                                          Width="80"
+                                          Command="{Binding IncBasics}" 
+                                          HorizontalAlignment="Center" 
+                                          VerticalAlignment="Center" 
+                                          IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
+                                          BorderBrush="{DynamicResource Control.Border.Color}"
+                                          IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
+                                          Foreground="{DynamicResource Font.Content.Foreground}"
+                                          Icon="{DynamicResource Inc}"
+                                          Content="导入"
+                                          BorderThickness="1,0,0,0" />
+                            <btn:ButtonControl  
+                                          Width="80"
+                                          Command="{Binding ExpBasics}" 
+                                          HorizontalAlignment="Center" 
+                                          VerticalAlignment="Center" 
+                                          IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
+                                          BorderBrush="{DynamicResource Control.Border.Color}"
+                                          IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
+                                          Foreground="{DynamicResource Font.Content.Foreground}"
+                                          Icon="{DynamicResource Exp}"
+                                          Content="导出"
+                                          BorderThickness="1,0,0,0" />
+                        </StackPanel>
+                        <!--属性表格-->
+                        <Grid Grid.Row="1" Panel.ZIndex="1">
+                            <Grid.Resources>
+                                <ResourceDictionary>
+                                    <Style TargetType="{x:Type Label}" >
+                                        <Setter Property="FontSize" Value="13"/>
+                                        <Setter Property="Foreground" Value="{DynamicResource Font.Content.Foreground}"/>
+                                        <Setter Property="HorizontalAlignment" Value="Left"/>
+                                    </Style>
+                                    <Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource DefaultComboBox}" >
+                                        <Setter Property="Height" Value="25"/>
+                                        <Setter Property="Background" Value="White"/>
+                                        <Setter Property="VerticalAlignment" Value="Center"/>
+                                        <Setter Property="helpers:Style_ComboBox.CornerRadius" Value="{DynamicResource WindowCornerRadius}"/>
+                                    </Style>
+                                    <Style TargetType="{x:Type pt:TextBoxEx}" BasedOn="{StaticResource TextBoxStyle2}">
+                                        <Setter Property="FontSize" Value="13"/>
+                                        <Setter Property="Foreground" Value="{DynamicResource Font.Content.Foreground}"/>
+                                        <Setter Property="BorderBrush" Value="{DynamicResource Control.Border.Color}"/>
+                                        <Setter Property="HorizontalContentAlignment" Value="Center"/>
+                                        <Setter Property="VerticalContentAlignment" Value="Center"/>
+                                    </Style>
+
+                                    <Style TargetType="CheckBox" BasedOn="{StaticResource CheckBoxStyle}"/>
+
+                                    <Style TargetType="ContentControl" >
+                                        <Setter Property="Foreground" Value="{DynamicResource Font.Content.Foreground}"/>
+                                    </Style>
+
+                                    <Style TargetType="Expander">
+                                        <Setter Property="Foreground" Value="{DynamicResource Font.Content.Foreground}"/>
+                                    </Style>
+
+                                    <Style TargetType="RadioButton" BasedOn="{StaticResource RadioButtonStyle}"/>
+
+
+                                    <Style TargetType="GroupBox" BasedOn="{StaticResource GroupBoxTab}"/>
+
+
+                                    <DataTemplate x:Key="HeaderTemplate">
+                                        <StackPanel Orientation="Horizontal">
+                                            <Grid>
+                                                <Grid.ColumnDefinitions>
+                                                    <ColumnDefinition Width="auto" />
+                                                    <ColumnDefinition Width="*" />
+                                                </Grid.ColumnDefinitions>
+                                                <Image Grid.Column="0" Margin="10,0,8,0" Source="{DynamicResource ConfigBase}" Width="14" />
+                                                <TextBlock Text="{Binding}" FontSize="13"  Grid.Column="1" Foreground="{DynamicResource Font.Content.Foreground}" />
+                                            </Grid>
+                                        </StackPanel>
+                                    </DataTemplate>
+
+                                </ResourceDictionary>
+                            </Grid.Resources>
+                            <pt:PropertyGrid  Margin="1,-4,1,0"
+                                              
+SelectedObject="{Binding BasicsData}"
+TabVisibility="VisibleIfMoreThanOne" 
+TextBlock.Foreground="{DynamicResource Font.Content.Foreground}" 
+Foreground="{DynamicResource Font.Content.Foreground}"
+BorderBrush="{DynamicResource Control.Border.Color}"
+CategoryHeaderTemplate="{StaticResource HeaderTemplate}"/>
+                        </Grid>
+                    </Grid>
+
+                    <!--功能模块与信息-->
+                    <Grid Grid.Column="1" >
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="auto"/>
+                            <RowDefinition Height="*"/>
+                        </Grid.RowDefinitions>
+
+                        <ToggleButton Grid.Row="0" Grid.RowSpan="2" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="8,8,0,0" ToolTip="基础数据[隐藏/显示]" Panel.ZIndex="3" IsChecked="{Binding BasicsData_ToggleButtonIsChecked}">
+                            <ToggleButton.Template>
+                                <ControlTemplate TargetType="{x:Type ToggleButton}">
+                                    <Image x:Name="image" Source="{DynamicResource GoLeft_Two}" Stretch="Fill" Width="15" Height="15" />
+                                    <ControlTemplate.Triggers>
+                                        <Trigger Property="IsChecked" Value="True">
+                                            <Setter Property="Source" TargetName="image" Value="{DynamicResource GoRight_Two}"/>
+                                        </Trigger>
+                                    </ControlTemplate.Triggers>
+                                </ControlTemplate>
+                            </ToggleButton.Template>
+                            <ToggleButton.Triggers>
+                                <EventTrigger RoutedEvent="ToggleButton.Unchecked">
+                                    <BeginStoryboard>
+                                        <Storyboard>
+                                            <DoubleAnimation Storyboard.TargetName="propertyGrid" Storyboard.TargetProperty="Width" From="0" To="550" Duration="0:0:0.7"/>
+                                            <ThicknessAnimationUsingKeyFrames Storyboard.TargetName="propertyGrid" Storyboard.TargetProperty="Margin" BeginTime="00:00:00">
+                                                <SplineThicknessKeyFrame KeyTime="00:00:00" Value="0" />
+                                                <SplineThicknessKeyFrame KeyTime="00:00:0.7" Value="0,0,3,0" />
+                                            </ThicknessAnimationUsingKeyFrames>
+                                        </Storyboard>
+                                    </BeginStoryboard>
+                                </EventTrigger>
+                                <EventTrigger RoutedEvent="ToggleButton.Checked">
+                                    <BeginStoryboard>
+                                        <Storyboard>
+                                            <DoubleAnimation Storyboard.TargetName="propertyGrid" Storyboard.TargetProperty="Width" From="550" To="0" Duration="0:0:0.7"/>
+                                            <ThicknessAnimationUsingKeyFrames Storyboard.TargetName="propertyGrid" Storyboard.TargetProperty="Margin" BeginTime="00:00:00">
+                                                <SplineThicknessKeyFrame KeyTime="00:00:00" Value="0,0,3,0" />
+                                                <SplineThicknessKeyFrame KeyTime="00:00:0.7" Value="0" />
+                                            </ThicknessAnimationUsingKeyFrames>
+                                        </Storyboard>
+                                    </BeginStoryboard>
+                                </EventTrigger>
+                            </ToggleButton.Triggers>
+                        </ToggleButton>
+
+                        <!--功能模块-->
+                        <GroupBox Style="{StaticResource GroupBoxTab}" Margin="0" Grid.Row="0">
+                            <GroupBox.Header>
+                                <StackPanel Orientation="Horizontal">
+                                    <Grid Background="transparent">
+                                        <Grid.ColumnDefinitions>
+                                            <ColumnDefinition Width="auto" />
+                                            <ColumnDefinition Width="*" />
+                                        </Grid.ColumnDefinitions>
+                                        <Image Grid.Column="0" Margin="10,0,8,0" Source="{DynamicResource Function}" Width="14" />
+                                        <TextBlock Text="功能" FontSize="13" Margin="0,0,10,0"  Grid.Column="1" Foreground="{DynamicResource Font.Content.Foreground}" />
+                                    </Grid>
+                                </StackPanel>
+                            </GroupBox.Header>
+                            <!--■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■-->
+
+                            <Grid>
+                                <Grid.RowDefinitions>
+                                    <RowDefinition Height="auto"/>
+                                    <RowDefinition Height="*"/>
+                                </Grid.RowDefinitions>
+                                <!--Start / Stop-->
+                                <Grid Grid.Row="0" Grid.Column="1"  Margin="0,-40,0,0">
+                                    <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" >
+                                        <btn:ButtonControl  Grid.Column="5"  Width="80"   Command="{Binding Start}" HorizontalAlignment="Right" VerticalAlignment="Center" BorderThickness="1,0,0,0"
+                                 IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
+                                 BorderBrush="{DynamicResource Control.Border.Color}"
+                                 IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
+                                 Foreground="{DynamicResource Font.Content.Foreground}"
+                                 Icon="{DynamicResource ConnectBase}"
+                                 Content="连接"/>
+
+                                        <btn:ButtonControl  Grid.Column="6"  Width="80"  Command="{Binding Stop}" HorizontalAlignment="Right" VerticalAlignment="Center" BorderThickness="1,0,0,0"
+                                 IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
+                                 BorderBrush="{DynamicResource Control.Border.Color}"
+                                 IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
+                                 Foreground="{DynamicResource Font.Content.Foreground}"
+                                 Icon="{DynamicResource OffConnect}"
+                                 Content="断开"/>
+                                    </StackPanel>
+                                </Grid>
+                                <!--发送区域-->
+                                <Grid Grid.Row="1" Grid.Column="1">
+                                    <Grid Margin="7">
+                                        <Grid.RowDefinitions>
+                                            <RowDefinition/>
+                                            <RowDefinition/>
+                                        </Grid.RowDefinitions>
+                                        <Grid.ColumnDefinitions>
+                                            <ColumnDefinition/>
+                                            <ColumnDefinition Width="auto"/>
+                                            <ColumnDefinition  Width="auto"/>
+                                        </Grid.ColumnDefinitions>
+
+                                        <txt:TextBoxControl Grid.Column="0" Grid.Row="0" FontSize="13" HorizontalContentAlignment="Center" Text="{Binding Topic}"
+                                       SelectedColor="{DynamicResource Control.Border.Two.Color}"
+                                       BorderBrush="{DynamicResource Control.Border.Color}"
+                                       Foreground="{DynamicResource Font.Content.Foreground}"
+                                       Icon="{DynamicResource Topic}"
+                                       HintColor="{DynamicResource Control.Border.One.Color}"
+                                       ShowIcon="True"
+                                       Hint="主题"
+                                       CornerRadius="{DynamicResource WindowCornerRadius}" />
+
+                                        <txt:TextBoxControl Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1" Margin="0,10,0,0" FontSize="13" HorizontalContentAlignment="Center" VerticalAlignment="Center" Text="{Binding Content}"
+                                      SelectedColor="{DynamicResource Control.Border.Two.Color}"
+                                      BorderBrush="{DynamicResource Control.Border.Color}"
+                                      Foreground="{DynamicResource Font.Content.Foreground}"
+                                      Icon="{DynamicResource Content}"
+                                      HintColor="{DynamicResource Control.Border.One.Color}"
+                                      ShowIcon="True"
+                                      Hint="内容"
+                                      CornerRadius="{DynamicResource WindowCornerRadius}" />
+
+                                        <btn:ButtonControl  Width="100" Grid.Row="0" Grid.Column="1" Command="{Binding Issue}" HorizontalAlignment="Center" VerticalAlignment="Center"  CornerRadius="{DynamicResource WindowCornerRadius}"  Margin="10,0,0,0"
+                                       IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
+                                       BorderBrush="{DynamicResource Control.Border.Color}"
+                                       IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
+                                       Foreground="{DynamicResource Font.Content.Foreground}"
+                                       Icon="{DynamicResource Issue}"
+                                       Content="发布"/>
+
+                                        <btn:ButtonControl  Width="100" Grid.Row="0" Grid.Column="2" Command="{Binding Subscribe}" HorizontalAlignment="Center" VerticalAlignment="Center"  CornerRadius="{DynamicResource WindowCornerRadius}"  Margin="10,0,0,0"
+                                         IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
+                                         BorderBrush="{DynamicResource Control.Border.Color}"
+                                         IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
+                                         Foreground="{DynamicResource Font.Content.Foreground}"
+                                         Icon="{DynamicResource Subscribe}"
+                                         Content="订阅"/>
+
+                                        <btn:ButtonControl  Width="100" Grid.Row="1" Grid.Column="2" Command="{Binding UnSubscribe}" HorizontalAlignment="Center" VerticalAlignment="Center"   CornerRadius="{DynamicResource WindowCornerRadius}"  Margin="10,10,0,0"
+                                         IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
+                                         BorderBrush="{DynamicResource Control.Border.Color}"
+                                         IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
+                                         Foreground="{DynamicResource Font.Content.Foreground}"
+                                         Icon="{DynamicResource UnSubscribe}"
+                                         Content="取消订阅"/>
+
+                                    </Grid>
+                                </Grid>
+                            </Grid>
+
+                            <!--■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■-->
+                        </GroupBox>
+                        <!--信息-->
+                        <GroupBox Style="{StaticResource GroupBoxTab}" Margin="0,5,0,0"  Grid.Row="1">
+                            <GroupBox.Header>
+                                <StackPanel Orientation="Horizontal">
+                                    <Grid>
+                                        <Grid.ColumnDefinitions>
+                                            <ColumnDefinition Width="auto"/>
+                                            <ColumnDefinition Width="*"/>
+                                        </Grid.ColumnDefinitions>
+                                        <Image Grid.Column="0" Margin="10,0,8,0" Source="{DynamicResource Info}" Width="14" />
+                                        <TextBlock Text="信息" FontSize="13" Margin="0,0,10,0"  Grid.Column="1" Foreground="{DynamicResource Font.Content.Foreground}" VerticalAlignment="Center"/>
+                                    </Grid>
+                                </StackPanel>
+                            </GroupBox.Header>
+                            <Grid>
+                                <Grid.RowDefinitions>
+                                    <RowDefinition Height="auto" />
+                                    <RowDefinition />
+                                </Grid.RowDefinitions>
+                                <Grid.ColumnDefinitions>
+                                    <ColumnDefinition />
+                                    <ColumnDefinition Width="auto" />
+                                </Grid.ColumnDefinitions>
+                                <StackPanel Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Right" Margin="0,-40,0,0">
+                                    <RadioButton ToolTip="显示方式" Margin="0,0,15,0"  Visibility="{Binding AsciiVisibility}" IsChecked="{Binding InfoFormat,Mode=TwoWay,Converter={StaticResource CheckConverter},ConverterParameter=0}" Style="{StaticResource RadioButtonStyle}" Content="ASCII" VerticalAlignment="Center" HorizontalAlignment="Right" />
+                                    <RadioButton ToolTip="显示方式" Margin="0,0,15,0" Visibility="{Binding HexVisibility}" IsChecked="{Binding InfoFormat,Mode=TwoWay,Converter={StaticResource CheckConverter},ConverterParameter=1}"  Style="{StaticResource RadioButtonStyle}" Content="Hex"  VerticalAlignment="Center" HorizontalAlignment="Right" />
+                                    <btn:ButtonControl  Width="80"  Command="{Binding Clear}" HorizontalAlignment="Right" VerticalAlignment="Center"  BorderThickness="1,0,0,0"
+IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
+BorderBrush="{DynamicResource Control.Border.Color}"
+IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
+Foreground="{DynamicResource Font.Content.Foreground}"
+Icon="{DynamicResource Clear}"
+Content="清空"/>
+                                </StackPanel>
+                                <TextBox Grid.Row="1" Padding="5" Grid.ColumnSpan="2" Style="{DynamicResource TextBoxStyle2}"  FontSize="13" VerticalContentAlignment="Top" HorizontalContentAlignment="Left" TextWrapping="Wrap" AcceptsReturn="True" VerticalScrollBarVisibility="Visible"  Text="{Binding Info}" >
+                                    <i:Interaction.Triggers>
+                                        <i:EventTrigger EventName="TextChanged">
+                                            <mvvm:EventCommand  Command="{Binding Info_TextChanged}" />
+                                        </i:EventTrigger>
+                                    </i:Interaction.Triggers>
+                                </TextBox>
+                            </Grid>
+                        </GroupBox>
+                    </Grid>
+
+                </Grid>
+            </GroupBox>
+        </Border>
+    </DataTemplate>
+    
+</ResourceDictionary>

+ 8 - 589
src/YSAI.Tool.Wpf/views/DaClient.xaml

@@ -3,600 +3,19 @@
              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:NodeStructuralBody="clr-namespace:YSAI.Tool.Wpf.data"
              xmlns:c="clr-namespace:YSAI.Tool.Wpf.controllers"
-             xmlns:txt="clr-namespace:YSAI.Core.Wpf.controls.textbox;assembly=YSAI.Core.Wpf"
-             xmlns:btn="clr-namespace:YSAI.Core.Wpf.controls.button;assembly=YSAI.Core.Wpf"
-             xmlns:pt="http://propertytools.org/wpf"
-             xmlns:helpers="clr-namespace:YSAI.Core.Wpf.style;assembly=YSAI.Core.Wpf"
-             xmlns:cs="clr-namespace:YSAI.Core.Wpf.converters;assembly=YSAI.Core.Wpf"
-             xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
-             xmlns:mvvm="clr-namespace:YSAI.Core.Wpf.mvvm;assembly=YSAI.Core.Wpf"
              Background="{DynamicResource ContentBackgroundPicture}">
-    <UserControl.DataContext>
-        <c:DaClientController />
-    </UserControl.DataContext>
-
-
-
+    
     <!--资源加载-->
     <UserControl.Resources>
-        <ResourceDictionary>
-            <Style TargetType="ToolTip">
-                <Setter Property="Foreground" Value="{DynamicResource Font.Content.Foreground}"/>
-                <Setter Property="BorderBrush" Value="{DynamicResource Control.Border.Color}"/>
-            </Style>
-            <cs:CheckConverter x:Key="CheckConverter" />
-            <ResourceDictionary.MergedDictionaries>
-                <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_ComboBox.xaml" />
-                <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_Button.xaml" />
-                <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_DataGrid.xaml" />
-                <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_Border.xaml" />
-                <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_TbaControl.xaml" />
-                <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_ScrollViewer.xaml" />
-                <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_GroupBox.xaml" />
-                <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_TextBox.xaml" />
-                <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_CheckBox.xaml" />
-                <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_RadioButton.xaml" />
-                <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_DatePicker.xaml" />
-                <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_ContextMenu.xaml" />
-                <ResourceDictionary Source="pack://application:,,,/YSAI.Core.Wpf;component/resources/style/Style_TreeView.xaml" />
-            </ResourceDictionary.MergedDictionaries>
-        </ResourceDictionary>
+        <ResourceDictionary Source="pack://application:,,,/YSAI.Tool.Wpf;component/template/DaqView.xaml" />
     </UserControl.Resources>
-
-    <Border Background="#F0F2F0" Width="auto" Height="auto" CornerRadius="{DynamicResource WindowCornerRadius}" Margin="50">
-        <GroupBox Style="{StaticResource GroupBoxTab}" Margin="0"  >
-            <GroupBox.Header>
-                <StackPanel Orientation="Horizontal">
-                    <Grid Background="Transparent">
-                        <Grid.ColumnDefinitions>
-                            <ColumnDefinition Width="auto" />
-                            <ColumnDefinition Width="*" />
-                        </Grid.ColumnDefinitions>
-                        <Image Grid.Column="0" Margin="10,0,8,0" Source="{DynamicResource Tool}" Width="14" />
-                        <TextBlock Text="{Binding ToolTitle}" FontSize="13"  Grid.Column="1" Foreground="{DynamicResource Font.Content.Foreground}" />
-                    </Grid>
-                </StackPanel>
-            </GroupBox.Header>
-            <!--基础数据与功能区域-->
-            <Grid >
-                <Grid.ColumnDefinitions>
-                    <ColumnDefinition Width="auto"/>
-                    <ColumnDefinition Width="*"/>
-                    <ColumnDefinition Width="auto"/>
-                </Grid.ColumnDefinitions>
-
-                <ToggleButton Grid.Column="0"  Grid.ColumnSpan="3" VerticalAlignment="Top" HorizontalAlignment="Right" Margin="0,-28,3,0" ToolTip="节点信息浏览[隐藏/显示]" Panel.ZIndex="3" IsChecked="{Binding NodeBrowseIsChecked}">
-                    <i:Interaction.Triggers>
-                        <i:EventTrigger EventName="Unchecked">
-                            <mvvm:EventCommand  Command="{Binding ToggleButton_Unchecked}" />
-                        </i:EventTrigger>
-                        <i:EventTrigger EventName="Checked">
-                            <mvvm:EventCommand  Command="{Binding ToggleButton_Checked}" />
-                        </i:EventTrigger>
-                    </i:Interaction.Triggers>
-                    <ToggleButton.Template>
-                        <ControlTemplate TargetType="{x:Type ToggleButton}">
-                            <Image x:Name="image" Source="{DynamicResource ZommOff}" Stretch="Fill" Width="15" Height="15" />
-                            <ControlTemplate.Triggers>
-                                <Trigger Property="IsChecked" Value="True">
-                                    <Setter Property="Source" TargetName="image" Value="{DynamicResource ZommOn}"/>
-                                </Trigger>
-                            </ControlTemplate.Triggers>
-                        </ControlTemplate>
-                    </ToggleButton.Template>
-                    <ToggleButton.Triggers>
-                        <EventTrigger RoutedEvent="ToggleButton.Unchecked">
-                            <BeginStoryboard>
-                                <Storyboard>
-                                    <DoubleAnimation Storyboard.TargetName="nodeBrowse" Storyboard.TargetProperty="MinWidth" From="1150" To="0" Duration="0:0:0.7"/>
-                                </Storyboard>
-                            </BeginStoryboard>
-                        </EventTrigger>
-                        <EventTrigger RoutedEvent="ToggleButton.Checked">
-                            <BeginStoryboard>
-                                <Storyboard>
-                                    <DoubleAnimation Storyboard.TargetName="nodeBrowse" Storyboard.TargetProperty="MinWidth" From="0" To="1150" Duration="0:0:0.7"/>
-                                </Storyboard>
-                            </BeginStoryboard>
-                        </EventTrigger>
-                    </ToggleButton.Triggers>
-                </ToggleButton>
-
-                <!--基础数据-->
-                <Grid Grid.Column="0"  x:Name="propertyGrid" Margin="0,0,3,0" Width="550">
-                    <Grid.RowDefinitions>
-                        <RowDefinition Height="auto"/>
-                        <RowDefinition Height="auto"/>
-                    </Grid.RowDefinitions>
-
-                    <!--导入导出-->
-                    <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,0,-30" Panel.ZIndex="2">
-                        <btn:ButtonControl  
-                                          Width="80"
-                                          Command="{Binding IncBasics}" 
-                                          HorizontalAlignment="Center" 
-                                          VerticalAlignment="Center" 
-                                          IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
-                                          BorderBrush="{DynamicResource Control.Border.Color}"
-                                          IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
-                                          Foreground="{DynamicResource Font.Content.Foreground}"
-                                          Icon="{DynamicResource Inc}"
-                                          Content="导入"
-                                          BorderThickness="1,0,0,0" />
-                        <btn:ButtonControl  
-                                          Width="80"
-                                          Command="{Binding ExpBasics}" 
-                                          HorizontalAlignment="Center" 
-                                          VerticalAlignment="Center" 
-                                          IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
-                                          BorderBrush="{DynamicResource Control.Border.Color}"
-                                          IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
-                                          Foreground="{DynamicResource Font.Content.Foreground}"
-                                          Icon="{DynamicResource Exp}"
-                                          Content="导出"
-                                          BorderThickness="1,0,0,0" />
-                    </StackPanel>
-                    <!--属性表格-->
-                    <Grid Grid.Row="1" Panel.ZIndex="1">
-                        <Grid.Resources>
-                            <ResourceDictionary>
-                                <Style TargetType="{x:Type Label}" >
-                                    <Setter Property="FontSize" Value="13"/>
-                                    <Setter Property="Foreground" Value="{DynamicResource Font.Content.Foreground}"/>
-                                    <Setter Property="HorizontalAlignment" Value="Left"/>
-                                </Style>
-                                <Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource DefaultComboBox}" >
-                                    <Setter Property="Height" Value="25"/>
-                                    <Setter Property="Background" Value="White"/>
-                                    <Setter Property="VerticalAlignment" Value="Center"/>
-                                    <Setter Property="helpers:Style_ComboBox.CornerRadius" Value="{DynamicResource WindowCornerRadius}"/>
-                                </Style>
-                                <Style TargetType="{x:Type pt:TextBoxEx}" BasedOn="{StaticResource TextBoxStyle2}">
-                                    <Setter Property="FontSize" Value="13"/>
-                                    <Setter Property="Foreground" Value="{DynamicResource Font.Content.Foreground}"/>
-                                    <Setter Property="BorderBrush" Value="{DynamicResource Control.Border.Color}"/>
-                                    <Setter Property="HorizontalContentAlignment" Value="Center"/>
-                                    <Setter Property="VerticalContentAlignment" Value="Center"/>
-                                </Style>
-
-                                <Style TargetType="CheckBox" BasedOn="{StaticResource CheckBoxStyle}"/>
-
-                                <Style TargetType="ContentControl" >
-                                    <Setter Property="Foreground" Value="{DynamicResource Font.Content.Foreground}"/>
-                                </Style>
-
-                                <Style TargetType="Expander">
-                                    <Setter Property="Foreground" Value="{DynamicResource Font.Content.Foreground}"/>
-                                </Style>
-
-                                <Style TargetType="RadioButton" BasedOn="{StaticResource RadioButtonStyle}"/>
-
-
-                                <Style TargetType="GroupBox" BasedOn="{StaticResource GroupBoxTab}"/>
-
-
-                                <DataTemplate x:Key="HeaderTemplate">
-                                    <StackPanel Orientation="Horizontal">
-                                        <Grid>
-                                            <Grid.ColumnDefinitions>
-                                                <ColumnDefinition Width="auto" />
-                                                <ColumnDefinition Width="*" />
-                                            </Grid.ColumnDefinitions>
-                                            <Image Grid.Column="0" Margin="10,0,8,0" Source="{DynamicResource ConfigBase}" Width="14" />
-                                            <TextBlock Text="{Binding}" FontSize="13"  Grid.Column="1" Foreground="{DynamicResource Font.Content.Foreground}" />
-                                        </Grid>
-                                    </StackPanel>
-                                </DataTemplate>
-
-                            </ResourceDictionary>
-                        </Grid.Resources>
-                        <pt:PropertyGrid  Margin="1,-4,1,0"
-SelectedObject="{Binding BasicsData}"
-TabVisibility="VisibleIfMoreThanOne" 
-TextBlock.Foreground="{DynamicResource Font.Content.Foreground}" 
-Foreground="{DynamicResource Font.Content.Foreground}"
-BorderBrush="{DynamicResource Control.Border.Color}"
-CategoryHeaderTemplate="{StaticResource HeaderTemplate}"/>
-                    </Grid>
-                </Grid>
-
-                <!--功能模块与信息-->
-                <Grid Grid.Column="1" >
-                    <Grid.RowDefinitions>
-                        <RowDefinition Height="auto"/>
-                        <RowDefinition Height="*"/>
-                    </Grid.RowDefinitions>
-
-                    <ToggleButton Grid.Row="0" Grid.RowSpan="2" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="8,8,0,0" ToolTip="基础数据[隐藏/显示]" Panel.ZIndex="3" IsChecked="{Binding BasicsData_ToggleButtonIsChecked}">
-                        <ToggleButton.Template>
-                            <ControlTemplate TargetType="{x:Type ToggleButton}">
-                                <Image x:Name="image" Source="{DynamicResource GoLeft_Two}" Stretch="Fill" Width="15" Height="15" />
-                                <ControlTemplate.Triggers>
-                                    <Trigger Property="IsChecked" Value="True">
-                                        <Setter Property="Source" TargetName="image" Value="{DynamicResource GoRight_Two}"/>
-                                    </Trigger>
-                                </ControlTemplate.Triggers>
-                            </ControlTemplate>
-                        </ToggleButton.Template>
-                        <ToggleButton.Triggers>
-                            <EventTrigger RoutedEvent="ToggleButton.Unchecked">
-                                <BeginStoryboard>
-                                    <Storyboard>
-                                        <DoubleAnimation Storyboard.TargetName="propertyGrid" Storyboard.TargetProperty="Width" From="0" To="550" Duration="0:0:0.7"/>
-                                        <ThicknessAnimationUsingKeyFrames Storyboard.TargetName="propertyGrid" Storyboard.TargetProperty="Margin" BeginTime="00:00:00">
-                                            <SplineThicknessKeyFrame KeyTime="00:00:00" Value="0" />
-                                            <SplineThicknessKeyFrame KeyTime="00:00:0.7" Value="0,0,3,0" />
-                                        </ThicknessAnimationUsingKeyFrames>
-                                    </Storyboard>
-                                </BeginStoryboard>
-                            </EventTrigger>
-                            <EventTrigger RoutedEvent="ToggleButton.Checked">
-                                <BeginStoryboard>
-                                    <Storyboard>
-                                        <DoubleAnimation Storyboard.TargetName="propertyGrid" Storyboard.TargetProperty="Width" From="550" To="0" Duration="0:0:0.7"/>
-                                        <ThicknessAnimationUsingKeyFrames Storyboard.TargetName="propertyGrid" Storyboard.TargetProperty="Margin" BeginTime="00:00:00">
-                                            <SplineThicknessKeyFrame KeyTime="00:00:00" Value="0,0,3,0" />
-                                            <SplineThicknessKeyFrame KeyTime="00:00:0.7" Value="0" />
-                                        </ThicknessAnimationUsingKeyFrames>
-                                    </Storyboard>
-                                </BeginStoryboard>
-                            </EventTrigger>
-                        </ToggleButton.Triggers>
-                    </ToggleButton>
-
-                    <!--功能模块-->
-                    <GroupBox Style="{StaticResource GroupBoxTab}" Margin="0" Grid.Row="0">
-                        <GroupBox.Header>
-                            <StackPanel Orientation="Horizontal">
-                                <Grid Background="transparent">
-                                    <Grid.ColumnDefinitions>
-                                        <ColumnDefinition Width="auto" />
-                                        <ColumnDefinition Width="*" />
-                                    </Grid.ColumnDefinitions>
-                                    <Image Grid.Column="0" Margin="10,0,8,0" Source="{DynamicResource Function}" Width="14" />
-                                    <TextBlock Text="功能" FontSize="13" Margin="0,0,10,0"  Grid.Column="1" Foreground="{DynamicResource Font.Content.Foreground}" />
-                                </Grid>
-                            </StackPanel>
-                        </GroupBox.Header>
-                        <!--■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■-->
-
-                        <Grid>
-                            <Grid.RowDefinitions>
-                                <RowDefinition Height="auto"/>
-                                <RowDefinition Height="*"/>
-                            </Grid.RowDefinitions>
-                            <!--Start / Stop-->
-                            <Grid Grid.Row="0" Grid.Column="1"  Margin="0,-40,0,0">
-                                <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" >
-                                    <btn:ButtonControl  Grid.Column="5"  Width="80"   Command="{Binding Start}" HorizontalAlignment="Right" VerticalAlignment="Center" BorderThickness="1,0,0,0"
-                                                        IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
-                                                        BorderBrush="{DynamicResource Control.Border.Color}"
-                                                        IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
-                                                        Foreground="{DynamicResource Font.Content.Foreground}"
-                                                        Icon="{DynamicResource ConnectBase}"
-                                                        Content="连接"/>
-
-                                    <btn:ButtonControl  Grid.Column="6"  Width="80"  Command="{Binding Stop}" HorizontalAlignment="Right" VerticalAlignment="Center" BorderThickness="1,0,0,0"
-                                                        IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
-                                                        BorderBrush="{DynamicResource Control.Border.Color}"
-                                                        IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
-                                                        Foreground="{DynamicResource Font.Content.Foreground}"
-                                                        Icon="{DynamicResource OffConnect}"
-                                                        Content="断开"/>
-                                </StackPanel>
-                            </Grid>
-                            <!--发送区域-->
-                            <Grid Grid.Row="1" Grid.Column="1">
-                                <Grid Margin="7">
-                                    <Grid.RowDefinitions>
-                                        <RowDefinition/>
-                                        <RowDefinition/>
-                                        <RowDefinition/>
-                                    </Grid.RowDefinitions>
-                                    <Grid.ColumnDefinitions>
-                                        <ColumnDefinition/>
-                                        <ColumnDefinition Width="auto"/>
-                                        <ColumnDefinition  Width="auto"/>
-                                    </Grid.ColumnDefinitions>
-
-                                    <GroupBox Grid.ColumnSpan="3"  Style="{StaticResource GroupBoxTab}" Margin="0,1,0,10">
-                                        <GroupBox.Header>
-                                            <StackPanel Orientation="Horizontal">
-                                                <Grid Background="transparent">
-                                                    <Grid.ColumnDefinitions>
-                                                        <ColumnDefinition Width="auto" />
-                                                        <ColumnDefinition Width="*" />
-                                                    </Grid.ColumnDefinitions>
-                                                    <Image Grid.Column="0" Margin="10,0,8,0" Source="{DynamicResource DataType}" Width="14" />
-                                                    <TextBlock Text="数据类型" FontSize="13" Margin="0,0,10,0"  Grid.Column="1" Foreground="{DynamicResource Font.Content.Foreground}" />
-                                                </Grid>
-                                            </StackPanel>
-                                        </GroupBox.Header>
-                                        <Grid Margin="7">
-                                            <Grid.ColumnDefinitions>
-                                                <ColumnDefinition/>
-                                                <ColumnDefinition/>
-                                                <ColumnDefinition/>
-                                                <ColumnDefinition/>
-                                                <ColumnDefinition/>
-                                            </Grid.ColumnDefinitions>
-                                            <RadioButton Grid.Column="0" ToolTip="数据类型"  IsChecked="{Binding DataType,Mode=TwoWay,Converter={StaticResource CheckConverter},ConverterParameter=0}" Style="{StaticResource RadioButtonStyle}" Content="String" VerticalAlignment="Center" HorizontalAlignment="Center" />
-                                            <RadioButton Grid.Column="1" ToolTip="数据类型"  IsChecked="{Binding DataType,Mode=TwoWay,Converter={StaticResource CheckConverter},ConverterParameter=1}"  Style="{StaticResource RadioButtonStyle}" Content="Double"  VerticalAlignment="Center" HorizontalAlignment="Center" />
-                                            <RadioButton Grid.Column="2" ToolTip="数据类型"  IsChecked="{Binding DataType,Mode=TwoWay,Converter={StaticResource CheckConverter},ConverterParameter=2}" Style="{StaticResource RadioButtonStyle}" Content="Float" VerticalAlignment="Center" HorizontalAlignment="Center" />
-                                            <RadioButton Grid.Column="3" ToolTip="数据类型"  IsChecked="{Binding DataType,Mode=TwoWay,Converter={StaticResource CheckConverter},ConverterParameter=3}"  Style="{StaticResource RadioButtonStyle}" Content="Int"  VerticalAlignment="Center" HorizontalAlignment="Center" />
-                                            <RadioButton Grid.Column="4" ToolTip="数据类型"  IsChecked="{Binding DataType,Mode=TwoWay,Converter={StaticResource CheckConverter},ConverterParameter=4}" Style="{StaticResource RadioButtonStyle}" Content="Bool" VerticalAlignment="Center" HorizontalAlignment="Center" />
-                                        </Grid>
-                                    </GroupBox>
-
-                                    <txt:TextBoxControl Grid.Column="0" Grid.Row="1" FontSize="13" HorizontalContentAlignment="Center" Text="{Binding DotAddress}"
-                                                              SelectedColor="{DynamicResource Control.Border.Two.Color}"
-                                                              BorderBrush="{DynamicResource Control.Border.Color}"
-                                                              Foreground="{DynamicResource Font.Content.Foreground}"
-                                                              Icon="{DynamicResource Dot}"
-                                                              HintColor="{DynamicResource Control.Border.One.Color}"
-                                                              ShowIcon="True"
-                                                              Hint="节点地址"
-                                                              CornerRadius="{DynamicResource WindowCornerRadius}" />
-
-                                    <txt:TextBoxControl Grid.Column="0" Grid.Row="2" Margin="0,10,0,0" FontSize="13" HorizontalContentAlignment="Center" VerticalAlignment="Center" Text="{Binding WriteInData}"
-                                                             SelectedColor="{DynamicResource Control.Border.Two.Color}"
-                                                             BorderBrush="{DynamicResource Control.Border.Color}"
-                                                             Foreground="{DynamicResource Font.Content.Foreground}"
-                                                             Icon="{DynamicResource WriteIn}"
-                                                             HintColor="{DynamicResource Control.Border.One.Color}"
-                                                             ShowIcon="True"
-                                                             Hint="写入数据"
-                                                             CornerRadius="{DynamicResource WindowCornerRadius}" />
-
-                                    <btn:ButtonControl  Width="100" Grid.Row="1" Grid.Column="1" Command="{Binding Read}" HorizontalAlignment="Center" VerticalAlignment="Center"  CornerRadius="{DynamicResource WindowCornerRadius}"  Margin="10,0,0,0"
-                                                              IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
-                                                              BorderBrush="{DynamicResource Control.Border.Color}"
-                                                              IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
-                                                              Foreground="{DynamicResource Font.Content.Foreground}"
-                                                              Icon="{DynamicResource Read}"
-                                                              Content="读取"/>
-
-                                    <btn:ButtonControl  Width="100" Grid.Row="2" Grid.Column="1" Command="{Binding Write}" HorizontalAlignment="Center" VerticalAlignment="Center"   CornerRadius="{DynamicResource WindowCornerRadius}"  Margin="10,10,0,0"
-                                                              IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
-                                                              BorderBrush="{DynamicResource Control.Border.Color}"
-                                                              IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
-                                                              Foreground="{DynamicResource Font.Content.Foreground}"
-                                                              Icon="{DynamicResource Write}"
-                                                              Content="写入"/>
-
-                                    <btn:ButtonControl  Width="100" Grid.Row="1" Grid.Column="2" Command="{Binding Subscribe}" HorizontalAlignment="Center" VerticalAlignment="Center"  CornerRadius="{DynamicResource WindowCornerRadius}"  Margin="10,0,0,0"
-                                                                IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
-                                                                BorderBrush="{DynamicResource Control.Border.Color}"
-                                                                IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
-                                                                Foreground="{DynamicResource Font.Content.Foreground}"
-                                                                Icon="{DynamicResource Subscribe}"
-                                                                Content="订阅"/>
-
-                                    <btn:ButtonControl  Width="100" Grid.Row="2" Grid.Column="2" Command="{Binding UnSubscribe}" HorizontalAlignment="Center" VerticalAlignment="Center"   CornerRadius="{DynamicResource WindowCornerRadius}"  Margin="10,10,0,0"
-                                                                IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
-                                                                BorderBrush="{DynamicResource Control.Border.Color}"
-                                                                IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
-                                                                Foreground="{DynamicResource Font.Content.Foreground}"
-                                                                Icon="{DynamicResource UnSubscribe}"
-                                                                Content="取消订阅"/>
-
-                                </Grid>
-                            </Grid>
-                        </Grid>
-
-
-
-
-                        <!--■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■-->
-                    </GroupBox>
-                    <!--信息-->
-                    <GroupBox Style="{StaticResource GroupBoxTab}" Margin="0,5,0,0"  Grid.Row="1">
-                        <GroupBox.Header>
-                            <StackPanel Orientation="Horizontal">
-                                <Grid>
-                                    <Grid.ColumnDefinitions>
-                                        <ColumnDefinition Width="auto"/>
-                                        <ColumnDefinition Width="*"/>
-                                    </Grid.ColumnDefinitions>
-                                    <Image Grid.Column="0" Margin="10,0,8,0" Source="{DynamicResource Info}" Width="14" />
-                                    <TextBlock Text="信息" FontSize="13" Margin="0,0,10,0"  Grid.Column="1" Foreground="{DynamicResource Font.Content.Foreground}" VerticalAlignment="Center"/>
-                                </Grid>
-                            </StackPanel>
-                        </GroupBox.Header>
-                        <Grid>
-                            <Grid.RowDefinitions>
-                                <RowDefinition Height="auto" />
-                                <RowDefinition />
-                            </Grid.RowDefinitions>
-                            <Grid.ColumnDefinitions>
-                                <ColumnDefinition />
-                                <ColumnDefinition Width="auto" />
-                            </Grid.ColumnDefinitions>
-                            <StackPanel Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Right" Margin="0,-40,0,0">
-                                <RadioButton ToolTip="显示方式" Margin="0,0,15,0"  Visibility="{Binding AsciiVisibility}" IsChecked="{Binding InfoFormat,Mode=TwoWay,Converter={StaticResource CheckConverter},ConverterParameter=0}" Style="{StaticResource RadioButtonStyle}" Content="ASCII" VerticalAlignment="Center" HorizontalAlignment="Right" />
-                                <RadioButton ToolTip="显示方式" Margin="0,0,15,0" Visibility="{Binding HexVisibility}" IsChecked="{Binding InfoFormat,Mode=TwoWay,Converter={StaticResource CheckConverter},ConverterParameter=1}"  Style="{StaticResource RadioButtonStyle}" Content="Hex"  VerticalAlignment="Center" HorizontalAlignment="Right" />
-                                <btn:ButtonControl  Width="80"  Command="{Binding Clear}" HorizontalAlignment="Right" VerticalAlignment="Center"  BorderThickness="1,0,0,0"
-IsMouseOverBorderBrushColor="{DynamicResource Control.Border.One.Color}"
-BorderBrush="{DynamicResource Control.Border.Color}"
-IsPressedBorderBrushColor="{DynamicResource Control.Border.Two.Color}"
-Foreground="{DynamicResource Font.Content.Foreground}"
-Icon="{DynamicResource Clear}"
-Content="清空"/>
-                            </StackPanel>
-                            <TextBox Grid.Row="1" Padding="5" Grid.ColumnSpan="2" Style="{DynamicResource TextBoxStyle2}"  FontSize="13" VerticalContentAlignment="Top" HorizontalContentAlignment="Left" TextWrapping="Wrap" AcceptsReturn="True" VerticalScrollBarVisibility="Visible"  Text="{Binding Info}" >
-                                <i:Interaction.Triggers>
-                                    <i:EventTrigger EventName="TextChanged">
-                                        <mvvm:EventCommand  Command="{Binding Info_TextChanged}" />
-                                    </i:EventTrigger>
-                                </i:Interaction.Triggers>
-                            </TextBox>
-                        </Grid>
-                    </GroupBox>
-                </Grid>
-
-
-
-
-                <!--■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■-->
-
-                <!--节点浏览-->
-                <Grid Grid.Column="2"  Width="0" x:Name="nodeBrowse">
-                    <GroupBox Style="{StaticResource GroupBoxTab}"  Margin="4,0,0,0">
-                        <GroupBox.Header>
-                            <StackPanel Orientation="Horizontal">
-                                <Grid Background="transparent">
-                                    <Grid.ColumnDefinitions>
-                                        <ColumnDefinition Width="auto" />
-                                        <ColumnDefinition Width="*" />
-                                    </Grid.ColumnDefinitions>
-                                    <Image Grid.Column="0" Margin="10,0,8,0" Source="{DynamicResource Menu}" Width="14" />
-                                    <TextBlock Text="节点信息浏览" FontSize="13"  Grid.Column="1" Foreground="{DynamicResource Font.Content.Foreground}" />
-                                </Grid>
-                            </StackPanel>
-                        </GroupBox.Header>
-                        <Grid>
-                            <Grid.ColumnDefinitions>
-                                <ColumnDefinition Width="260" />
-                                <ColumnDefinition />
-                            </Grid.ColumnDefinitions>
-                            <!--节点-->
-                            <Grid Grid.Column="0">
-                                <GroupBox Style="{StaticResource GroupBoxTab}" Margin="0,0,5,0">
-                                    <GroupBox.Header>
-                                        <StackPanel Orientation="Horizontal">
-                                            <Grid Background="transparent">
-                                                <Grid.ColumnDefinitions>
-                                                    <ColumnDefinition Width="auto" />
-                                                    <ColumnDefinition Width="*" />
-                                                </Grid.ColumnDefinitions>
-                                                <Image Grid.Column="0" Margin="10,0,8,0" Source="{DynamicResource DotBase}" Width="14" />
-                                                <TextBlock Text="节点" FontSize="13"  Grid.Column="1" Foreground="{DynamicResource Font.Content.Foreground}" />
-                                            </Grid>
-                                        </StackPanel>
-                                    </GroupBox.Header>
-                                    <TreeView  Background="Transparent" Margin="5,5,0,5"  BorderBrush="{DynamicResource Control.Border.Color}" ItemsSource="{Binding Node}" Style="{StaticResource TreeViewStyle}" >
-                                        <!--右键菜单-->
-                                        <TreeView.ContextMenu>
-                                            <ContextMenu VerticalContentAlignment="Center" FontSize="13">
-                                                <MenuItem Header="订阅" Foreground="{DynamicResource Font.Content.Foreground}">
-                                                    <i:Interaction.Triggers>
-                                                        <i:EventTrigger EventName="Click">
-                                                            <mvvm:EventCommand  Command="{Binding ContextMenu_Subscribe}" />
-                                                        </i:EventTrigger>
-                                                    </i:Interaction.Triggers>
-                                                    <MenuItem.Icon>
-                                                        <StackPanel Orientation="Horizontal">
-                                                            <Image Source="{DynamicResource Subscribe}"  Width="15" Height="15" VerticalAlignment="Center" />
-                                                        </StackPanel>
-                                                    </MenuItem.Icon>
-                                                </MenuItem>
-                                                <MenuItem Header="取消订阅" Foreground="{DynamicResource Font.Content.Foreground}">
-                                                    <i:Interaction.Triggers>
-                                                        <i:EventTrigger EventName="Click">
-                                                            <mvvm:EventCommand  Command="{Binding ContextMenu_UnSubscribe}" />
-                                                        </i:EventTrigger>
-                                                    </i:Interaction.Triggers>
-                                                    <MenuItem.Icon>
-                                                        <StackPanel Orientation="Horizontal">
-                                                            <Image Source="{DynamicResource UnSubscribe}"  Width="15" Height="15" VerticalAlignment="Center" />
-                                                        </StackPanel>
-                                                    </MenuItem.Icon>
-                                                </MenuItem>
-                                                <MenuItem Header="导出节点" Foreground="{DynamicResource Font.Content.Foreground}">
-                                                    <i:Interaction.Triggers>
-                                                        <i:EventTrigger EventName="Click">
-                                                            <mvvm:EventCommand  Command="{Binding ContextMenu_ExpNode}" />
-                                                        </i:EventTrigger>
-                                                    </i:Interaction.Triggers>
-                                                    <MenuItem.Icon>
-                                                        <StackPanel Orientation="Horizontal">
-                                                            <Image Source="{DynamicResource Exp}"  Width="15" Height="15" VerticalAlignment="Center" />
-                                                        </StackPanel>
-                                                    </MenuItem.Icon>
-                                                </MenuItem>
-                                            </ContextMenu>
-                                        </TreeView.ContextMenu>
-                                        <i:Interaction.Triggers>
-                                            <!--选中-->
-                                            <i:EventTrigger EventName="SelectedItemChanged">
-                                                <mvvm:EventCommand  Command="{Binding TreeView_SelectedItemChanged}" />
-                                            </i:EventTrigger>
-                                            <!--右键按下-->
-                                            <i:EventTrigger EventName="PreviewMouseRightButtonDown">
-                                                <mvvm:EventCommand  Command="{Binding TreeView_PreviewMouseRightButtonDown}" />
-                                            </i:EventTrigger>
-                                            <!--项被展开-->
-                                            <mvvm:RoutedEventTrigger RoutedEvent="TreeViewItem.Expanded">
-                                                <mvvm:EventCommand  Command="{Binding TreeViewItem_Expanded}" />
-                                            </mvvm:RoutedEventTrigger>
-                                        </i:Interaction.Triggers>
-                                        <TreeView.ItemTemplate>
-                                            <HierarchicalDataTemplate  DataType="{x:Type NodeStructuralBody:NodeStructuralBody}" ItemsSource="{Binding Children}">
-                                                <StackPanel  Margin="0,5,0,5">
-                                                    <Grid>
-                                                        <Grid.ColumnDefinitions>
-                                                            <ColumnDefinition />
-                                                            <ColumnDefinition />
-                                                            <ColumnDefinition />
-                                                        </Grid.ColumnDefinitions>
-                                                        <Image VerticalAlignment="Center" HorizontalAlignment="Center" Stretch="Fill"  Source="{Binding Icon}" Width="11" Height="11" Margin="0,0,5,0" Grid.Column="0" />
-                                                        <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding Name}" FontSize="13"   Foreground="{DynamicResource Font.Content.Foreground}" Grid.Column="1" Margin="0,0,5,0" />
-                                                        <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding Count}" FontSize="11" Foreground="{DynamicResource Font.Content.Foreground}" Grid.Column="2" />
-                                                    </Grid>
-                                                </StackPanel>
-                                            </HierarchicalDataTemplate>
-                                        </TreeView.ItemTemplate>
-                                    </TreeView>
-                                </GroupBox>
-                            </Grid>
-                            <!--信息-->
-                            <Grid Grid.Column="1">
-                                <GroupBox Style="{StaticResource GroupBoxTab}" Margin="0">
-                                    <GroupBox.Header>
-                                        <StackPanel Orientation="Horizontal">
-                                            <Grid Background="transparent">
-                                                <Grid.ColumnDefinitions>
-                                                    <ColumnDefinition Width="auto" />
-                                                    <ColumnDefinition Width="*" />
-                                                </Grid.ColumnDefinitions>
-                                                <Image Grid.Column="0" Margin="10,0,8,0" Source="{DynamicResource Default}" Width="14" />
-                                                <TextBlock Text="详情" FontSize="13"  Grid.Column="1" Foreground="{DynamicResource Font.Content.Foreground}" />
-                                            </Grid>
-                                        </StackPanel>
-                                    </GroupBox.Header>
-
-                                    <DataGrid ColumnHeaderHeight="40" ItemsSource="{Binding NodeMessage}" Grid.Row="0" FontSize="13" SelectedItem="{Binding NodeMessageSelectedItem}" BorderThickness="0">
-                                        <!--双击-->
-                                        <i:Interaction.Triggers>
-                                            <i:EventTrigger EventName="SelectedCellsChanged">
-                                                <mvvm:EventCommand  Command="{Binding DataGrid_SelectedCellsChanged}" />
-                                            </i:EventTrigger>
-                                        </i:Interaction.Triggers>
-                                        <DataGrid.Columns>
-                                            <DataGridTextColumn Header="名称"  Width="auto"  Binding="{Binding Name}" Visibility="Collapsed" />
-                                            <DataGridTextColumn Header="地址"  Width="*"  Binding="{Binding Address}" />
-                                            <DataGridTextColumn Header="值"  Width="150"  Binding="{Binding Value}" />
-                                            <DataGridTextColumn Header="类型" Width="120" Binding="{Binding Type}" />
-                                            <DataGridTextColumn Header="访问级别"  Width="150"  Binding="{Binding AccessLevel}" />
-                                            <DataGridTextColumn Header="描述"  Width="100"  Binding="{Binding Description}" />
-                                        </DataGrid.Columns>
-                                    </DataGrid>
-                                </GroupBox>
-                            </Grid>
-                        </Grid>
-                    </GroupBox>
-                </Grid>
-
-
-                <!--■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■-->
-
-
-            </Grid>
-        </GroupBox>
-    </Border>
+    <!--控制器绑定-->
+    <UserControl.DataContext>
+        <c:DaClientController />
+    </UserControl.DataContext>
+    <!--内容控件加载-->
+    <ContentControl Content="{Binding}" ContentTemplate="{DynamicResource DAQ}"/>
 </UserControl>
 
 

+ 1 - 14
src/YSAI.Tool.Wpf/views/DaClient.xaml.cs

@@ -1,17 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Documents;
-using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Navigation;
-using System.Windows.Shapes;
+using System.Windows.Controls;
 
 namespace YSAI.Tool.Wpf.views
 {

+ 20 - 0
src/YSAI.Tool.Wpf/views/MqttClient.xaml

@@ -0,0 +1,20 @@
+<UserControl x:Class="YSAI.Tool.Wpf.views.MqttClient"
+             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:YSAI.Tool.Wpf.views"
+             xmlns:c="clr-namespace:YSAI.Tool.Wpf.controllers" 
+             Background="{DynamicResource ContentBackgroundPicture}">
+
+    <!--资源加载-->
+    <UserControl.Resources>
+        <ResourceDictionary Source="pack://application:,,,/YSAI.Tool.Wpf;component/template/RelayView.xaml" />
+    </UserControl.Resources>
+    <!--控制器绑定-->
+    <UserControl.DataContext>
+        <c:MqttClientController />
+    </UserControl.DataContext>
+    <!--内容控件加载-->
+    <ContentControl Content="{Binding}" ContentTemplate="{DynamicResource RELAY}"/>
+</UserControl>

+ 28 - 0
src/YSAI.Tool.Wpf/views/MqttClient.xaml.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace YSAI.Tool.Wpf.views
+{
+    /// <summary>
+    /// MqttClient.xaml 的交互逻辑
+    /// </summary>
+    public partial class MqttClient : UserControl
+    {
+        public MqttClient()
+        {
+            InitializeComponent();
+        }
+    }
+}

+ 12 - 0
src/YSAI.Tool.Wpf/views/MqttService.xaml

@@ -0,0 +1,12 @@
+<UserControl x:Class="YSAI.Tool.Wpf.views.MqttService"
+             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:YSAI.Tool.Wpf.views"
+             mc:Ignorable="d" 
+             d:DesignHeight="450" d:DesignWidth="800">
+    <Grid>
+            
+    </Grid>
+</UserControl>

+ 28 - 0
src/YSAI.Tool.Wpf/views/MqttService.xaml.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace YSAI.Tool.Wpf.views
+{
+    /// <summary>
+    /// MqttService.xaml 的交互逻辑
+    /// </summary>
+    public partial class MqttService : UserControl
+    {
+        public MqttService()
+        {
+            InitializeComponent();
+        }
+    }
+}

+ 12 - 0
src/YSAI.Tool.Wpf/views/MqttWsService.xaml

@@ -0,0 +1,12 @@
+<UserControl x:Class="YSAI.Tool.Wpf.views.MqttWsService"
+             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:YSAI.Tool.Wpf.views"
+             mc:Ignorable="d" 
+             d:DesignHeight="450" d:DesignWidth="800">
+    <Grid>
+            
+    </Grid>
+</UserControl>

+ 28 - 0
src/YSAI.Tool.Wpf/views/MqttWsService.xaml.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace YSAI.Tool.Wpf.views
+{
+    /// <summary>
+    /// MqttWsService.xaml 的交互逻辑
+    /// </summary>
+    public partial class MqttWsService : UserControl
+    {
+        public MqttWsService()
+        {
+            InitializeComponent();
+        }
+    }
+}