Browse Source

添加项目文件。

wujingwei 1 year ago
parent
commit
b50ad4fa1f
74 changed files with 9299 additions and 0 deletions
  1. 25 0
      GatewayTool.sln
  2. 6 0
      GatewayTool/App.config
  3. 567 0
      GatewayTool/Businesses/ServiceOptions.cs
  4. 86 0
      GatewayTool/Businesses/SoftwareConfigs.cs
  5. 129 0
      GatewayTool/FormAbout.Designer.cs
  6. 37 0
      GatewayTool/FormAbout.cs
  7. 120 0
      GatewayTool/FormAbout.resx
  8. 46 0
      GatewayTool/FormBase.Designer.cs
  9. 38 0
      GatewayTool/FormBase.cs
  10. 120 0
      GatewayTool/FormBase.resx
  11. 385 0
      GatewayTool/FormConfig.Designer.cs
  12. 206 0
      GatewayTool/FormConfig.cs
  13. 120 0
      GatewayTool/FormConfig.resx
  14. 101 0
      GatewayTool/FormJson.Designer.cs
  15. 44 0
      GatewayTool/FormJson.cs
  16. 120 0
      GatewayTool/FormJson.resx
  17. 444 0
      GatewayTool/FormMain.Designer.cs
  18. 592 0
      GatewayTool/FormMain.cs
  19. 154 0
      GatewayTool/FormMain.resx
  20. 203 0
      GatewayTool/FormParity.Designer.cs
  21. 99 0
      GatewayTool/FormParity.cs
  22. 120 0
      GatewayTool/FormParity.resx
  23. 177 0
      GatewayTool/FormPing.Designer.cs
  24. 117 0
      GatewayTool/FormPing.cs
  25. 409 0
      GatewayTool/FormPing.resx
  26. 265 0
      GatewayTool/GatewayTool.csproj
  27. 26 0
      GatewayTool/Models/DTU.cs
  28. 599 0
      GatewayTool/Models/DTUConfig.cs
  29. 75 0
      GatewayTool/Models/DTUConfigCollections.cs
  30. 192 0
      GatewayTool/Models/DTUConfigEnums.cs
  31. 311 0
      GatewayTool/Models/DTUSystemState.cs
  32. 36 0
      GatewayTool/Models/GatewayBase.cs
  33. 29 0
      GatewayTool/Models/SniffDeviceInfo.cs
  34. 57 0
      GatewayTool/Models/StringLengthAttribute.cs
  35. 56 0
      GatewayTool/Models/UpgradeInfo.cs
  36. 26 0
      GatewayTool/Program.cs
  37. 36 0
      GatewayTool/Properties/AssemblyInfo.cs
  38. 63 0
      GatewayTool/Properties/Resources.Designer.cs
  39. 117 0
      GatewayTool/Properties/Resources.resx
  40. 26 0
      GatewayTool/Properties/Settings.Designer.cs
  41. 7 0
      GatewayTool/Properties/Settings.settings
  42. 64 0
      GatewayTool/Protocols/CRC16.cs
  43. 110 0
      GatewayTool/Protocols/DTUProtocol.cs
  44. 114 0
      GatewayTool/Protocols/GatewayProtocolBase.cs
  45. 21 0
      GatewayTool/Protocols/SniffReplyInfo.cs
  46. 180 0
      GatewayTool/Protocols/XModem.cs
  47. 30 0
      GatewayTool/Protocols/XorSum16.cs
  48. 173 0
      GatewayTool/Resource.Designer.cs
  49. 154 0
      GatewayTool/Resource.resx
  50. BIN
      GatewayTool/Resources/config.ico
  51. BIN
      GatewayTool/Resources/device.png
  52. BIN
      GatewayTool/Resources/dtu.jpg
  53. BIN
      GatewayTool/Resources/dtu.png
  54. BIN
      GatewayTool/Resources/export.png
  55. BIN
      GatewayTool/Resources/favicon.ico
  56. BIN
      GatewayTool/Resources/favimg.png
  57. BIN
      GatewayTool/Resources/gateway.png
  58. BIN
      GatewayTool/Resources/import.png
  59. BIN
      GatewayTool/Resources/manage.ico
  60. BIN
      GatewayTool/Resources/open.png
  61. BIN
      GatewayTool/Resources/refresh.png
  62. BIN
      GatewayTool/Resources/start.png
  63. BIN
      GatewayTool/Resources/state.ico
  64. BIN
      GatewayTool/Resources/stop.png
  65. 229 0
      GatewayTool/StringMsg.cs
  66. 181 0
      GatewayTool/UserControls/CollectionEditorEx.cs
  67. 20 0
      GatewayTool/UserControls/ToolStripEx.cs
  68. 527 0
      GatewayTool/UserControls/UcDTUPanel.Designer.cs
  69. 463 0
      GatewayTool/UserControls/UcDTUPanel.cs
  70. 197 0
      GatewayTool/UserControls/UcDTUPanel.resx
  71. 108 0
      GatewayTool/UserControls/UcDeviceList.Designer.cs
  72. 157 0
      GatewayTool/UserControls/UcDeviceList.cs
  73. 185 0
      GatewayTool/UserControls/UcDeviceList.resx
  74. BIN
      GatewayTool/favicon.ico

+ 25 - 0
GatewayTool.sln

@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31205.134
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GatewayTool", "GatewayTool\GatewayTool.csproj", "{AE1B694E-19DB-4718-99C9-208D83A4F440}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{AE1B694E-19DB-4718-99C9-208D83A4F440}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{AE1B694E-19DB-4718-99C9-208D83A4F440}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{AE1B694E-19DB-4718-99C9-208D83A4F440}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{AE1B694E-19DB-4718-99C9-208D83A4F440}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {05F9E32F-DE2F-4341-9A2D-BC8952C8BCA2}
+	EndGlobalSection
+EndGlobal

+ 6 - 0
GatewayTool/App.config

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+    <startup> 
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>
+    </startup>
+</configuration>

+ 567 - 0
GatewayTool/Businesses/ServiceOptions.cs

@@ -0,0 +1,567 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Ports;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using WjwLib.API.Communication;
+using GatewayTool.Protocols;
+using GatewayTool.Models;
+
+namespace GatewayTool.Businesses
+{
+    /// <summary>
+    /// 服务操作类
+    /// </summary>
+    public class ServiceOptions
+    {
+        private static readonly object _locker = new object();
+        private List<ICommunication> _comList = new List<ICommunication>();
+
+        private DTUProtocol _protocol = new DTUProtocol();
+        private XModem _xModem = new XModem();
+
+        private const int SINGLE_PACK_LENGTH = 128;
+
+        public event EventHandler ExecDeviceListUpdated;
+        public event EventHandler<GatewayBase> ExecReadSystemState;
+        public event EventHandler<GatewayBase> ExecReadDeviceConfig;
+        public event EventHandler<bool> ExecWriteDeviceConfig;
+        public event EventHandler<GatewayBase> ExecUpgradeProgressUpdated;
+        public event EventHandler<GatewayBase> ExecUpgradeProgressFinished;
+        public event EventHandler<ServiceInteractiveData> ExecServiceDataInteractive;
+
+        private CommunicationTcpServer _TcpServer;
+        /// <summary>
+        /// Tcp Server实例
+        /// </summary>     
+        public CommunicationTcpServer TcpServer
+        {
+            get
+            {
+                if (_TcpServer == null)
+                {
+                    _TcpServer = new CommunicationTcpServer();
+                }
+                return _TcpServer;
+            }
+        }
+
+        private ICommunication _Udp;
+        /// <summary>
+        /// UDP实例
+        /// </summary>
+        public ICommunication Udp
+        {
+            get
+            {
+                if (_Udp == null)
+                {
+                    _Udp = new CommunicationServer(CommunicationType.CT_NETPORT_UDP);
+                    _Udp.ExecDataArrived += ReplyDataArrived;
+                }
+                return _Udp;
+            }
+        }
+
+        private List<GatewayBase> _FoundDeviceList = new List<GatewayBase>();
+        /// <summary>
+        /// 已发现网关设备列表
+        /// </summary>
+        public List<GatewayBase> FoundDeviceList
+        {
+            get => _FoundDeviceList;
+            set => _FoundDeviceList = value;
+        }
+
+        /// <summary>
+        /// 读取DTU系统状态命令数据包
+        /// </summary>
+        public byte[] ReadDTUSystemStateCommand
+        {
+            get
+            {
+                return _protocol.PackReadDTUSystemState();
+            }
+        }
+
+        /// <summary>
+        /// 读取DTU设备配置命令数据包
+        /// </summary>
+        public byte[] ReadDTUDeviceConfigCommand
+        {
+            get
+            {
+                return _protocol.PackReadDTUDeviceConfig();
+            }
+        }
+
+        /// <summary>
+        /// 写DTU设备配置命令数据包
+        /// </summary>
+        /// <param name="json">json配置</param>
+        public byte[] WriteDTUDeviceConfigCommand(string json)
+        {
+            return _protocol.PackWriteDTUDeviceConfig(json);
+        }
+
+        /// <summary>
+        /// 准备开始DTU设备固件升级数据包
+        /// </summary>
+        public byte[] PrepareToUpgradeCommand
+        {
+            get
+            {
+                return _protocol.PackPrepareToUpgrade();
+            }
+        }
+
+        /// <summary>
+        /// 启动串口嗅探
+        /// </summary>
+        /// <param name="baudrate">波特率</param>
+        /// <param name="databits">数据位</param>
+        /// <param name="stopbits">停止位</param>
+        /// <param name="parity">校验位</param>
+        public void StartComSniff(EnumBaudrate baudrate, EnumDataBits databits, EnumStopBits stopbits, EnumCheckBit parity)
+        {
+            ICommunication com = new CommunicationServer(CommunicationType.CT_SERIALPORT);
+            string[] portNames = com.GetAvailableCommPorts();
+            if (portNames != null)
+            {
+                foreach (string name in portNames)
+                {
+                    ICommunication existCom = GetComInstance(name);
+                    if (existCom == null)
+                    {
+                        com = new CommunicationServer(CommunicationType.CT_SERIALPORT);
+                        com.ExecDataArrived += ReplyDataArrived;
+                        com.OpenCommPort(new CommunicationSerialportArgs() { PortNum = name, BaudRate = (int)baudrate, DataBits = (int)databits, StopBits = ConvertStopBits(stopbits), Parity = ConvertParity(parity) });
+                        _comList.Add(com);
+                    }
+                    else
+                    {
+                        com = existCom;
+                    }
+                    if (com.IsOpen) 
+                    {
+                        byte[] bytes = _protocol.PackSniffDevice();
+                        com.SendData(bytes);
+                        ExecServiceDataInteractive?.Invoke(this, new ServiceInteractiveData() { DataType = InteractiveType.SEND, GatewaySN = "To All", Data = bytes });
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// 根据串口名从本地已有的串口列表中获取对应实例
+        /// </summary>
+        /// <param name="name">串口名</param>
+        /// <returns>串口实例</returns>
+        private ICommunication GetComInstance(string name)
+        {
+            if (_comList == null || _comList.Count == 0)
+            {
+                return null;
+            }
+            for (int i = _comList.Count - 1; i >= 0; i--)
+            {
+                CommunicationServer sp = _comList[i] as CommunicationServer;
+                if (sp != null && sp.GetCommPortCfg().Contains(name))
+                {
+                    return sp;
+                }
+            }
+            return null;
+        }
+
+        /// <summary>
+        /// 转换自定义停止位为标准停止位
+        /// </summary>
+        /// <param name="enumStopBits">自定义停止位</param>
+        /// <returns>标准停止位</returns>
+        private StopBits ConvertStopBits(EnumStopBits enumStopBits)
+        {
+            switch (enumStopBits)
+            {
+                case EnumStopBits.Bit_1:
+                    return StopBits.One;
+                case EnumStopBits.Bit_2:
+                    return StopBits.Two;
+                default:
+                    return StopBits.None;
+            }
+        }
+
+        /// <summary>
+        /// 转换自定义校验位为标准校验位
+        /// </summary>
+        /// <param name="enumStopBits">自定义校验位</param>
+        /// <returns>标准校验位</returns>
+        private Parity ConvertParity(EnumCheckBit enumCheckBit)
+        {
+            switch (enumCheckBit)
+            {
+                case EnumCheckBit.none:
+                    return Parity.None;
+                case EnumCheckBit.odd:
+                    return Parity.Odd;
+                case EnumCheckBit.even:
+                    return Parity.Even;
+                default:
+                    return Parity.None;
+            }
+        }
+
+        /// <summary>
+        /// 启动网络嗅探
+        /// </summary>
+        /// <param name="port">远程端口号</param>
+        /// <returns>操作结果:true成功,false失败</returns>
+        public bool StartNetSniff(int port)
+        {
+            if (!this.Udp.IsOpen)
+            {
+                bool res = this.Udp.OpenCommPort(new CommunicationNetportArgs()
+                {
+                    ServerArgs = new EndPointArgs() { IP = "", PortNum = port },
+                    ClientUdpArgs = new EndPointArgs() { IP = this.Udp.GetAvailableCommPorts()[0], PortNum = 100, PortNumGenType = PortNumGenType.Type_Specify }
+                });
+            }
+            if (!this.Udp.IsOpen) return false;
+            return this.Udp.SendData(_protocol.PackSniffDevice());
+        }
+
+        /// <summary>
+        /// 回复数据到达处理
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void ReplyDataArrived(object sender, ArriveDataEventArgs e)
+        {
+            lock (_locker)
+            {
+                if (e.Data.Length > 1)
+                {
+                    byte code = e.Data[1];
+                    switch (code)
+                    {
+                        case 0x01:
+                            ParseSniff(sender, e);
+                            break;
+                        case 0x02:
+                            ParseReadSystemState(sender, e);
+                            break;
+                        case 0x03:
+                            ParseReadDeviceConfig(sender, e);
+                            break;
+                        case 0x04:
+                            ParseWriteDeviceConfig(sender, e);
+                            break;
+                        default:
+                            break;
+                    }
+                    bool isConvertGB2312 = e.Data.Length > 3 && e.Data[0] == 0x4C && e.Data[1] == 0x4F && e.Data[2] == 0x47; //LOG标识
+                    ExecServiceDataInteractive?.Invoke(sender, new ServiceInteractiveData() { DataType = InteractiveType.RECV, GatewaySN = GetExistDevice(e.Source)?.SniffInfo.SN, Data = e.Data, IsConvertGB2312 = isConvertGB2312 }); //防止未及时嗅探出设备编号
+                }
+                else
+                {
+                    ExecServiceDataInteractive?.Invoke(sender, new ServiceInteractiveData() { DataType = InteractiveType.RECV, GatewaySN = GetExistDevice(e.Source)?.SniffInfo.SN, Data = e.Data }); //防止收到在发送前面
+
+                    if (e.Data[0] == 0x43) //收到字符'C'
+                    {
+                        ParseAllowUpgrade(sender, e);
+                    }
+                    else if (e.Data[0] == XModemConstants.ACK) // XModem ACK  继续发送
+                    {
+                        ParseGoOnNextPacket(sender, e);
+                    }
+                    else if (e.Data[0] == XModemConstants.NAK) // XModem NAK  重发分包
+                    {
+                        ParseResendLastPacket(sender, e);
+                    }
+                    else if (e.Data[0] == XModemConstants.CAN) // XModem CAN  强制停止
+                    {
+                        ParseTerminateUpgrade(sender, e);
+                    }
+                }                
+            }
+        }
+
+        /// <summary>
+        /// 解析XModem允许升级回复数据
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="data"></param>
+        private void ParseAllowUpgrade(object sender, ArriveDataEventArgs e)
+        {
+            GatewayBase gateway = GetExistDevice(e.Source);
+            if (gateway == null) return;
+            if (string.IsNullOrEmpty(gateway.UpgradeInfo.FirmwareFilePath)) return;
+            using (FileStream fs = new FileStream(gateway.UpgradeInfo.FirmwareFilePath, FileMode.Open))
+            {
+                using (BinaryReader br = new BinaryReader(fs))
+                {
+                    byte[] totalData = br.ReadBytes((int)fs.Length);
+                    int packNum;
+                    byte[] packet = _xModem.GetPacket(totalData, 1, SINGLE_PACK_LENGTH, out packNum);
+                    gateway.Communication.SendData(packet);
+                    ExecServiceDataInteractive?.Invoke(sender, new ServiceInteractiveData() { DataType = InteractiveType.SEND, GatewaySN = gateway.SniffInfo.SN, Data = packet });
+                    gateway.UpgradeInfo.SeperatePackNum = 1;
+                    gateway.UpgradeInfo.TotalPackNum = packNum;
+                    gateway.UpgradeInfo.TotalData = totalData;
+                }
+            }
+            ExecUpgradeProgressUpdated?.Invoke(sender, gateway);
+        }
+
+        /// <summary>
+        /// 解析XModem继续发送下一包回复数据
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="data"></param>
+        private void ParseGoOnNextPacket(object sender, ArriveDataEventArgs e)
+        {
+            GatewayBase gateway = GetExistDevice(e.Source);
+            if (gateway == null) return;
+            int packNum;
+            gateway.UpgradeInfo.SeperatePackNum += 1;
+            if (gateway.UpgradeInfo.SeperatePackNum == gateway.UpgradeInfo.TotalPackNum + 1) //收到最后一个分包应答
+            {
+                byte[] packet = new byte[] { XModemConstants.EOT };
+                gateway.Communication.SendData(packet);
+                ExecServiceDataInteractive?.Invoke(sender, new ServiceInteractiveData() { DataType = InteractiveType.SEND, GatewaySN = gateway.SniffInfo.SN, Data = packet });
+            }
+            else if (gateway.UpgradeInfo.SeperatePackNum == gateway.UpgradeInfo.TotalPackNum + 2) //收到EOT的回复,表明全部结束
+            {
+                ExecUpgradeProgressFinished?.Invoke(sender, gateway);
+            }
+            else //中间分包
+            {
+                byte[] packet = _xModem.GetPacket(gateway.UpgradeInfo.TotalData, gateway.UpgradeInfo.SeperatePackNum, SINGLE_PACK_LENGTH, out packNum);
+                gateway.Communication.SendData(packet);
+                ExecServiceDataInteractive?.Invoke(sender, new ServiceInteractiveData() { DataType = InteractiveType.SEND, GatewaySN = gateway.SniffInfo.SN, Data = packet });
+                ExecUpgradeProgressUpdated?.Invoke(sender, gateway);
+            }
+        }
+
+        /// <summary>
+        /// 解析XModem重新发送上一包回复数据
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="data"></param>
+        private void ParseResendLastPacket(object sender, ArriveDataEventArgs e)
+        {
+            GatewayBase gateway = GetExistDevice(e.Source);
+            if (gateway == null) return;
+            int packNum;
+            byte[] packet = _xModem.GetPacket(gateway.UpgradeInfo.TotalData, gateway.UpgradeInfo.SeperatePackNum, SINGLE_PACK_LENGTH, out packNum);
+            if (packet == null) //取不出数据时还要求发上一包,一定是EOT
+            {
+                byte[] bytes = new byte[] { XModemConstants.EOT };
+                gateway.Communication.SendData(bytes);
+                ExecServiceDataInteractive?.Invoke(sender, new ServiceInteractiveData() { DataType = InteractiveType.SEND, GatewaySN = gateway.SniffInfo.SN, Data = bytes});
+            }
+            else //中间分包
+            {
+                gateway.Communication.SendData(packet);
+                ExecServiceDataInteractive?.Invoke(sender, new ServiceInteractiveData() { DataType = InteractiveType.SEND, GatewaySN = gateway.SniffInfo.SN, Data = packet });
+                ExecUpgradeProgressUpdated?.Invoke(sender, gateway);
+            }
+        }
+
+        /// <summary>
+        /// 解析XModem终止升级回复数据
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="data"></param>
+        private void ParseTerminateUpgrade(object sender, ArriveDataEventArgs e)
+        {
+            GatewayBase gateway = GetExistDevice(e.Source);
+            if (gateway == null) return;
+            gateway.UpgradeInfo.SeperatePackNum = 0;
+            ExecUpgradeProgressUpdated?.Invoke(sender, gateway);
+        }
+
+        /// <summary>
+        /// 解析设备嗅探回复数据
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="data"></param>
+        private void ParseSniff(object sender, ArriveDataEventArgs e)
+        {
+            SniffReplyInfo info = _protocol.UnpackSniff(e.Data);
+            if (info != null)
+            {
+                SniffDeviceInfo sniffInfo = new SniffDeviceInfo();
+                sniffInfo.Source = e.Source.Split(':')[0];
+                sniffInfo.SN = info.SN;
+                sniffInfo.Version = info.Version;
+
+                GatewayBase gateway = GetExistDevice(sniffInfo);
+                bool isExisted = gateway != null;
+                if (!isExisted)
+                {
+                    if (sniffInfo.SN.StartsWith("DT")) //DTU标识符
+                    {
+                        DTU dtu = new DTU();
+                        dtu.SystemState = new DTUSystemState();
+                        dtu.Config = new DTUConfig();
+                        dtu.Config.DeviceId = info.SN; //初始化设备配置中的序列号
+                        dtu.UpgradeInfo = new UpgradeInfo();
+                        gateway = dtu; //先默认为DTU,后期根据识别符进行判定
+                    }
+                }
+                if (gateway != null)
+                {
+                    gateway.SniffInfo = sniffInfo;
+                    gateway.Communication = sender as ICommunication;
+                    if (!isExisted) _FoundDeviceList.Add(gateway);
+                    ExecDeviceListUpdated?.Invoke(sender, null);
+                }
+            }
+        }
+
+        /// <summary>
+        /// 解析读系统状态回复数据
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="data"></param>
+        private void ParseReadSystemState(object sender, ArriveDataEventArgs e)
+        {
+            DTUSystemState state = _protocol.UnpackReadDTUSystemState(e.Data);
+            if (state != null)
+            {
+                DTU dtu = (DTU)GetExistDevice(e.Source);
+                if (dtu != null)
+                {
+                    dtu.SystemState = state;
+                }
+                ExecReadSystemState?.Invoke(sender, dtu);
+            }
+        }
+
+        /// <summary>
+        /// 解析读设备配置回复数据
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="data"></param>
+        private void ParseReadDeviceConfig(object sender, ArriveDataEventArgs e)
+        {
+            DTUConfig config = _protocol.UnpackReadDTUDeviceConfig(e.Data);
+            if (config != null)
+            {
+                DTU dtu = (DTU)GetExistDevice(e.Source);
+                if (dtu != null)
+                {
+                    dtu.Config = config;
+                }
+                ExecReadDeviceConfig?.Invoke(sender, dtu);
+            }
+        }
+
+        /// <summary>
+        /// 解析写设备配置回复数据
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="data"></param>
+        private void ParseWriteDeviceConfig(object sender, ArriveDataEventArgs e)
+        {
+            bool res = _protocol.UnpackWriteDTUDeviceConfig(e.Data);
+            ExecWriteDeviceConfig?.Invoke(sender, res);
+        }
+
+        /// <summary>
+        /// 获取已存在于列表中的设备实例
+        /// </summary>
+        /// <param name="device">设备</param>
+        /// <returns>true是,false否</returns>
+        public GatewayBase GetExistDevice(SniffDeviceInfo device)
+        {
+            if (_FoundDeviceList == null || _FoundDeviceList.Count == 0) return null;
+            for (int i = _FoundDeviceList.Count - 1; i >= 0; i--)
+            {
+                if (_FoundDeviceList[i].SniffInfo.Source.Equals(device.Source) && _FoundDeviceList[i].SniffInfo.SN.Equals(device.SN))
+                {
+                    return _FoundDeviceList[i];
+                }
+            }
+            return null;
+        }
+
+        /// <summary>
+        /// 获取已存在的设备实例
+        /// </summary>
+        /// <param name="source">通信来源</param>
+        /// <returns>true是,false否</returns>
+        public GatewayBase GetExistDevice(string source)
+        {
+            if (_FoundDeviceList == null || _FoundDeviceList.Count == 0) return null;
+            for (int i = _FoundDeviceList.Count - 1; i >= 0; i--)
+            {
+                if (_FoundDeviceList[i].SniffInfo.Source.Equals(source))
+                {
+                    return _FoundDeviceList[i];
+                }
+            }
+            return null;
+        }
+
+        /// <summary>
+        /// 启动服务
+        /// </summary>
+        /// <param name="port">端口号</param>
+        /// <returns>启动结果,true成功,false失败</returns>
+        public bool StartService(int port)
+        {
+            return this.TcpServer.OpenServer(port);
+        }
+
+        /// <summary>
+        /// 停止服务
+        /// </summary>
+        public void StopService()
+        {
+            this.TcpServer.CloseServer();
+        }
+    }
+
+    /// <summary>
+    /// 交互类型
+    /// </summary>
+    public enum InteractiveType 
+    {
+        /// <summary>
+        /// 发送
+        /// </summary>
+        SEND,
+        /// <summary>
+        /// 接收
+        /// </summary>
+        RECV,
+    }
+
+    /// <summary>
+    /// 服务交互数据类
+    /// </summary>
+    public class ServiceInteractiveData
+    {
+        /// <summary>
+        /// 数据交互类型
+        /// </summary>
+        public InteractiveType DataType { get; set; }
+        /// <summary>
+        /// 网关序列号
+        /// </summary>
+        public string GatewaySN { get; set; }
+        /// <summary>
+        /// 数据
+        /// </summary>
+        public byte[] Data { get; set; }
+        /// <summary>
+        /// 是否需要将数据转为GB2312编码
+        /// </summary>
+        public bool IsConvertGB2312 { get; set; }
+    }
+}

+ 86 - 0
GatewayTool/Businesses/SoftwareConfigs.cs

@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using GatewayTool.Models;
+
+namespace GatewayTool.Businesses
+{
+    public class SoftwareConfigs
+    {
+        /// <summary>
+        /// 当前时间
+        /// </summary>
+        public static string Now
+        {
+            get => $"[{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")}]";
+        }
+
+        /// <summary>
+        /// 服务端口号
+        /// </summary>
+        public int ServicePortNum { get; set; } = 3333;
+
+        private string _LogFolderPath = $"{Environment.CurrentDirectory}\\Log";
+        /// <summary>
+        /// 日志文件夹路径
+        /// </summary>
+        public string LogFolderPath
+        {
+            get => _LogFolderPath;
+            set
+            {
+                _LogFolderPath = value;
+            }
+        }
+
+        /// <summary>
+        /// 日志文件路径
+        /// </summary>
+        public string LogFilePath
+        {
+            get => $"{_LogFolderPath}\\{DateTime.Now.ToString("yyyy-MM-dd")}.log";
+        }
+
+        /// <summary>
+        /// 实时日志最大显示行数
+        /// </summary>
+        public int ShowLogMaxLine { get; set; } = 20;
+
+        /// <summary>
+        /// 帮助文档路径
+        /// </summary>
+        public string HelpFilePath { get; set; } = $"{Environment.CurrentDirectory}\\Help.pdf";
+
+        /// <summary>
+        /// 配置文件路径
+        /// </summary>
+        public string ConfigFilePath { get; set; } = $"{Environment.CurrentDirectory}\\Config.ini";
+
+        /// <summary>
+        /// 嗅探远程端口号
+        /// </summary>
+        public int SniffRemotePortNum { get; set; } = 2009;
+
+        /// <summary>
+        /// 嗅探波特率
+        /// </summary>
+        public EnumBaudrate SniffBaudRate { get; set; } = EnumBaudrate.rate_9600;
+
+        /// <summary>
+        /// 嗅探数据位
+        /// </summary>
+        public EnumDataBits SniffDataBits { get; set; } = EnumDataBits.Bit_8;
+
+        /// <summary>
+        /// 嗅探停止位
+        /// </summary>
+        public EnumStopBits SniffStopBits { get; set; } = EnumStopBits.Bit_1;
+
+        /// <summary>
+        /// 嗅探校验位
+        /// </summary>
+        public EnumCheckBit SniffParity { get; set; } = EnumCheckBit.none;
+    }
+}

+ 129 - 0
GatewayTool/FormAbout.Designer.cs

@@ -0,0 +1,129 @@
+
+namespace GatewayTool
+{
+    partial class FormAbout
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.rtProfile = new System.Windows.Forms.RichTextBox();
+            this.lbCopyright = new System.Windows.Forms.Label();
+            this.label2 = new System.Windows.Forms.Label();
+            this.lbSoftwareName = new System.Windows.Forms.Label();
+            this.pictureBox1 = new System.Windows.Forms.PictureBox();
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
+            this.SuspendLayout();
+            // 
+            // rtProfile
+            // 
+            this.rtProfile.Location = new System.Drawing.Point(214, 176);
+            this.rtProfile.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
+            this.rtProfile.Name = "rtProfile";
+            this.rtProfile.ReadOnly = true;
+            this.rtProfile.Size = new System.Drawing.Size(327, 136);
+            this.rtProfile.TabIndex = 10;
+            this.rtProfile.Text = "本软件可对连接的自研物联网网关设备进行参数设置并显示数据收发结果,操作简便直观,欢迎使用!";
+            // 
+            // lbCopyright
+            // 
+            this.lbCopyright.AutoSize = true;
+            this.lbCopyright.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.lbCopyright.Location = new System.Drawing.Point(214, 133);
+            this.lbCopyright.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
+            this.lbCopyright.Name = "lbCopyright";
+            this.lbCopyright.Size = new System.Drawing.Size(174, 19);
+            this.lbCopyright.TabIndex = 7;
+            this.lbCopyright.Text = "传秀科技 版权所有©2023";
+            // 
+            // label2
+            // 
+            this.label2.AutoSize = true;
+            this.label2.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.label2.Location = new System.Drawing.Point(214, 94);
+            this.label2.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
+            this.label2.Name = "label2";
+            this.label2.Size = new System.Drawing.Size(54, 19);
+            this.label2.TabIndex = 8;
+            this.label2.Text = "V1.0.0";
+            // 
+            // lbSoftwareName
+            // 
+            this.lbSoftwareName.AutoSize = true;
+            this.lbSoftwareName.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.lbSoftwareName.Location = new System.Drawing.Point(214, 51);
+            this.lbSoftwareName.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
+            this.lbSoftwareName.Name = "lbSoftwareName";
+            this.lbSoftwareName.Size = new System.Drawing.Size(122, 22);
+            this.lbSoftwareName.TabIndex = 9;
+            this.lbSoftwareName.Text = "物联网网关工具";
+            // 
+            // pictureBox1
+            // 
+            this.pictureBox1.Image = global::GatewayTool.Resource.favimg;
+            this.pictureBox1.Location = new System.Drawing.Point(93, 51);
+            this.pictureBox1.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
+            this.pictureBox1.Name = "pictureBox1";
+            this.pictureBox1.Size = new System.Drawing.Size(91, 92);
+            this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
+            this.pictureBox1.TabIndex = 6;
+            this.pictureBox1.TabStop = false;
+            // 
+            // FormAbout
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(633, 331);
+            this.Controls.Add(this.rtProfile);
+            this.Controls.Add(this.lbCopyright);
+            this.Controls.Add(this.label2);
+            this.Controls.Add(this.lbSoftwareName);
+            this.Controls.Add(this.pictureBox1);
+            this.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.Margin = new System.Windows.Forms.Padding(3, 6, 3, 6);
+            this.Name = "FormAbout";
+            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+            this.Text = "FormAbout";
+            this.Load += new System.EventHandler(this.FormAbout_Load);
+            this.Controls.SetChildIndex(this.pictureBox1, 0);
+            this.Controls.SetChildIndex(this.lbSoftwareName, 0);
+            this.Controls.SetChildIndex(this.label2, 0);
+            this.Controls.SetChildIndex(this.lbCopyright, 0);
+            this.Controls.SetChildIndex(this.rtProfile, 0);
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.RichTextBox rtProfile;
+        private System.Windows.Forms.Label lbCopyright;
+        private System.Windows.Forms.Label label2;
+        private System.Windows.Forms.Label lbSoftwareName;
+        private System.Windows.Forms.PictureBox pictureBox1;
+    }
+}

+ 37 - 0
GatewayTool/FormAbout.cs

@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace GatewayTool
+{
+    public partial class FormAbout : FormBase
+    {
+        public FormAbout()
+        {
+            InitializeComponent();
+            this.IsMaximizable = false;
+            this.IsMinimizable = false;
+            this.Icon = Resource.favicon;
+            this.ShowInTaskbar = false;
+        }
+
+        private void InitStrings()
+        {
+            this.Title = StrMsg.About;
+            lbSoftwareName.Text = StrMsg.SoftwareName;
+            lbCopyright.Text = StrMsg.SoftwareCopyright;
+            rtProfile.Text = StrMsg.SoftwareProfile;
+        }
+
+        private void FormAbout_Load(object sender, EventArgs e)
+        {
+            InitStrings();
+        }
+    }
+}

+ 120 - 0
GatewayTool/FormAbout.resx

@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 46 - 0
GatewayTool/FormBase.Designer.cs

@@ -0,0 +1,46 @@
+
+namespace GatewayTool
+{
+    partial class FormBase
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.SuspendLayout();
+            // 
+            // FormBase
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(120F, 120F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
+            this.ClientSize = new System.Drawing.Size(800, 450);
+            this.Name = "FormBase";
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+    }
+}

+ 38 - 0
GatewayTool/FormBase.cs

@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using WjwLib.Frame.FormBase;
+using WjwLib.Control.WaitLayer;
+
+namespace GatewayTool
+{
+    public partial class FormBase : CustomFormBase
+    {
+        private static StringMsg _StrMsg = new StringMsg(Language.SIMPLIFIED_CHINESE);
+
+        public StringMsg StrMsg
+        {
+            get => _StrMsg;
+            set => _StrMsg = value;
+        }
+
+        private OpaqueCommand _WaitLayer = new OpaqueCommand();
+
+        protected OpaqueCommand WaitLayer
+        {
+            get => _WaitLayer;
+            set => _WaitLayer = value;
+        }
+
+        public FormBase()
+        {
+            InitializeComponent();
+        }
+    }
+}

+ 120 - 0
GatewayTool/FormBase.resx

@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 385 - 0
GatewayTool/FormConfig.Designer.cs

@@ -0,0 +1,385 @@
+
+namespace GatewayTool
+{
+    partial class FormConfig
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.tabControl1 = new System.Windows.Forms.TabControl();
+            this.tabPage1 = new System.Windows.Forms.TabPage();
+            this.numServicePort = new System.Windows.Forms.NumericUpDown();
+            this.lbServicePort = new System.Windows.Forms.Label();
+            this.tabPage2 = new System.Windows.Forms.TabPage();
+            this.btnBrowse = new System.Windows.Forms.Button();
+            this.txtFolderPath = new System.Windows.Forms.TextBox();
+            this.numShowMaxLine = new System.Windows.Forms.NumericUpDown();
+            this.lbFolderPath = new System.Windows.Forms.Label();
+            this.lbShowMaxLine = new System.Windows.Forms.Label();
+            this.tabPage3 = new System.Windows.Forms.TabPage();
+            this.cmbParity = new System.Windows.Forms.ComboBox();
+            this.cmbDataBits = new System.Windows.Forms.ComboBox();
+            this.cmbStopBits = new System.Windows.Forms.ComboBox();
+            this.lbParity = new System.Windows.Forms.Label();
+            this.cmbBaudRate = new System.Windows.Forms.ComboBox();
+            this.lbDataBits = new System.Windows.Forms.Label();
+            this.lbStopBits = new System.Windows.Forms.Label();
+            this.numRemotePort = new System.Windows.Forms.NumericUpDown();
+            this.lbBaudRate = new System.Windows.Forms.Label();
+            this.lbRemotePort = new System.Windows.Forms.Label();
+            this.btnOK = new System.Windows.Forms.Button();
+            this.btnCancel = new System.Windows.Forms.Button();
+            this.btnDefault = new System.Windows.Forms.Button();
+            this.tabControl1.SuspendLayout();
+            this.tabPage1.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.numServicePort)).BeginInit();
+            this.tabPage2.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.numShowMaxLine)).BeginInit();
+            this.tabPage3.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.numRemotePort)).BeginInit();
+            this.SuspendLayout();
+            // 
+            // tabControl1
+            // 
+            this.tabControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.tabControl1.Controls.Add(this.tabPage1);
+            this.tabControl1.Controls.Add(this.tabPage2);
+            this.tabControl1.Controls.Add(this.tabPage3);
+            this.tabControl1.Location = new System.Drawing.Point(12, 41);
+            this.tabControl1.Name = "tabControl1";
+            this.tabControl1.SelectedIndex = 0;
+            this.tabControl1.Size = new System.Drawing.Size(580, 192);
+            this.tabControl1.TabIndex = 6;
+            // 
+            // tabPage1
+            // 
+            this.tabPage1.Controls.Add(this.numServicePort);
+            this.tabPage1.Controls.Add(this.lbServicePort);
+            this.tabPage1.Location = new System.Drawing.Point(4, 29);
+            this.tabPage1.Name = "tabPage1";
+            this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
+            this.tabPage1.Size = new System.Drawing.Size(572, 159);
+            this.tabPage1.TabIndex = 0;
+            this.tabPage1.Text = "网络";
+            this.tabPage1.UseVisualStyleBackColor = true;
+            // 
+            // numServicePort
+            // 
+            this.numServicePort.Location = new System.Drawing.Point(127, 23);
+            this.numServicePort.Maximum = new decimal(new int[] {
+            65535,
+            0,
+            0,
+            0});
+            this.numServicePort.Name = "numServicePort";
+            this.numServicePort.Size = new System.Drawing.Size(120, 26);
+            this.numServicePort.TabIndex = 1;
+            // 
+            // lbServicePort
+            // 
+            this.lbServicePort.Location = new System.Drawing.Point(6, 23);
+            this.lbServicePort.Name = "lbServicePort";
+            this.lbServicePort.Size = new System.Drawing.Size(111, 26);
+            this.lbServicePort.TabIndex = 0;
+            this.lbServicePort.Text = "服务端口";
+            this.lbServicePort.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+            // 
+            // tabPage2
+            // 
+            this.tabPage2.Controls.Add(this.btnBrowse);
+            this.tabPage2.Controls.Add(this.txtFolderPath);
+            this.tabPage2.Controls.Add(this.numShowMaxLine);
+            this.tabPage2.Controls.Add(this.lbFolderPath);
+            this.tabPage2.Controls.Add(this.lbShowMaxLine);
+            this.tabPage2.Location = new System.Drawing.Point(4, 29);
+            this.tabPage2.Name = "tabPage2";
+            this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
+            this.tabPage2.Size = new System.Drawing.Size(572, 159);
+            this.tabPage2.TabIndex = 1;
+            this.tabPage2.Text = "日志";
+            this.tabPage2.UseVisualStyleBackColor = true;
+            // 
+            // btnBrowse
+            // 
+            this.btnBrowse.AutoSize = true;
+            this.btnBrowse.Location = new System.Drawing.Point(488, 69);
+            this.btnBrowse.Name = "btnBrowse";
+            this.btnBrowse.Size = new System.Drawing.Size(37, 30);
+            this.btnBrowse.TabIndex = 7;
+            this.btnBrowse.Text = "...";
+            this.btnBrowse.TextAlign = System.Drawing.ContentAlignment.TopCenter;
+            this.btnBrowse.UseVisualStyleBackColor = true;
+            this.btnBrowse.Click += new System.EventHandler(this.btnBrowse_Click);
+            // 
+            // txtFolderPath
+            // 
+            this.txtFolderPath.Location = new System.Drawing.Point(127, 71);
+            this.txtFolderPath.Name = "txtFolderPath";
+            this.txtFolderPath.ReadOnly = true;
+            this.txtFolderPath.Size = new System.Drawing.Size(355, 26);
+            this.txtFolderPath.TabIndex = 6;
+            // 
+            // numShowMaxLine
+            // 
+            this.numShowMaxLine.Location = new System.Drawing.Point(127, 23);
+            this.numShowMaxLine.Maximum = new decimal(new int[] {
+            1000,
+            0,
+            0,
+            0});
+            this.numShowMaxLine.Name = "numShowMaxLine";
+            this.numShowMaxLine.Size = new System.Drawing.Size(120, 26);
+            this.numShowMaxLine.TabIndex = 5;
+            // 
+            // lbFolderPath
+            // 
+            this.lbFolderPath.Location = new System.Drawing.Point(6, 71);
+            this.lbFolderPath.Name = "lbFolderPath";
+            this.lbFolderPath.Size = new System.Drawing.Size(111, 26);
+            this.lbFolderPath.TabIndex = 4;
+            this.lbFolderPath.Text = "文件夹路径";
+            this.lbFolderPath.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+            // 
+            // lbShowMaxLine
+            // 
+            this.lbShowMaxLine.Location = new System.Drawing.Point(6, 23);
+            this.lbShowMaxLine.Name = "lbShowMaxLine";
+            this.lbShowMaxLine.Size = new System.Drawing.Size(111, 26);
+            this.lbShowMaxLine.TabIndex = 4;
+            this.lbShowMaxLine.Text = "最大显示行数";
+            this.lbShowMaxLine.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+            // 
+            // tabPage3
+            // 
+            this.tabPage3.Controls.Add(this.cmbParity);
+            this.tabPage3.Controls.Add(this.cmbDataBits);
+            this.tabPage3.Controls.Add(this.cmbStopBits);
+            this.tabPage3.Controls.Add(this.lbParity);
+            this.tabPage3.Controls.Add(this.cmbBaudRate);
+            this.tabPage3.Controls.Add(this.lbDataBits);
+            this.tabPage3.Controls.Add(this.lbStopBits);
+            this.tabPage3.Controls.Add(this.numRemotePort);
+            this.tabPage3.Controls.Add(this.lbBaudRate);
+            this.tabPage3.Controls.Add(this.lbRemotePort);
+            this.tabPage3.Location = new System.Drawing.Point(4, 29);
+            this.tabPage3.Name = "tabPage3";
+            this.tabPage3.Size = new System.Drawing.Size(572, 159);
+            this.tabPage3.TabIndex = 2;
+            this.tabPage3.Text = "设备嗅探";
+            this.tabPage3.UseVisualStyleBackColor = true;
+            // 
+            // cmbParity
+            // 
+            this.cmbParity.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.cmbParity.FormattingEnabled = true;
+            this.cmbParity.Location = new System.Drawing.Point(378, 93);
+            this.cmbParity.Name = "cmbParity";
+            this.cmbParity.Size = new System.Drawing.Size(120, 28);
+            this.cmbParity.TabIndex = 4;
+            // 
+            // cmbDataBits
+            // 
+            this.cmbDataBits.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.cmbDataBits.FormattingEnabled = true;
+            this.cmbDataBits.Location = new System.Drawing.Point(127, 93);
+            this.cmbDataBits.Name = "cmbDataBits";
+            this.cmbDataBits.Size = new System.Drawing.Size(120, 28);
+            this.cmbDataBits.TabIndex = 4;
+            // 
+            // cmbStopBits
+            // 
+            this.cmbStopBits.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.cmbStopBits.FormattingEnabled = true;
+            this.cmbStopBits.Location = new System.Drawing.Point(378, 57);
+            this.cmbStopBits.Name = "cmbStopBits";
+            this.cmbStopBits.Size = new System.Drawing.Size(120, 28);
+            this.cmbStopBits.TabIndex = 4;
+            // 
+            // lbParity
+            // 
+            this.lbParity.Location = new System.Drawing.Point(257, 94);
+            this.lbParity.Name = "lbParity";
+            this.lbParity.Size = new System.Drawing.Size(111, 26);
+            this.lbParity.TabIndex = 2;
+            this.lbParity.Text = "校验位";
+            this.lbParity.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+            // 
+            // cmbBaudRate
+            // 
+            this.cmbBaudRate.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.cmbBaudRate.FormattingEnabled = true;
+            this.cmbBaudRate.Location = new System.Drawing.Point(127, 57);
+            this.cmbBaudRate.Name = "cmbBaudRate";
+            this.cmbBaudRate.Size = new System.Drawing.Size(120, 28);
+            this.cmbBaudRate.TabIndex = 4;
+            // 
+            // lbDataBits
+            // 
+            this.lbDataBits.Location = new System.Drawing.Point(6, 94);
+            this.lbDataBits.Name = "lbDataBits";
+            this.lbDataBits.Size = new System.Drawing.Size(111, 26);
+            this.lbDataBits.TabIndex = 2;
+            this.lbDataBits.Text = "数据位";
+            this.lbDataBits.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+            // 
+            // lbStopBits
+            // 
+            this.lbStopBits.Location = new System.Drawing.Point(257, 58);
+            this.lbStopBits.Name = "lbStopBits";
+            this.lbStopBits.Size = new System.Drawing.Size(111, 26);
+            this.lbStopBits.TabIndex = 2;
+            this.lbStopBits.Text = "停止位";
+            this.lbStopBits.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+            // 
+            // numRemotePort
+            // 
+            this.numRemotePort.Location = new System.Drawing.Point(127, 23);
+            this.numRemotePort.Maximum = new decimal(new int[] {
+            65535,
+            0,
+            0,
+            0});
+            this.numRemotePort.Name = "numRemotePort";
+            this.numRemotePort.Size = new System.Drawing.Size(120, 26);
+            this.numRemotePort.TabIndex = 3;
+            // 
+            // lbBaudRate
+            // 
+            this.lbBaudRate.Location = new System.Drawing.Point(6, 58);
+            this.lbBaudRate.Name = "lbBaudRate";
+            this.lbBaudRate.Size = new System.Drawing.Size(111, 26);
+            this.lbBaudRate.TabIndex = 2;
+            this.lbBaudRate.Text = "波特率";
+            this.lbBaudRate.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+            // 
+            // lbRemotePort
+            // 
+            this.lbRemotePort.Location = new System.Drawing.Point(6, 23);
+            this.lbRemotePort.Name = "lbRemotePort";
+            this.lbRemotePort.Size = new System.Drawing.Size(111, 26);
+            this.lbRemotePort.TabIndex = 2;
+            this.lbRemotePort.Text = "远程端口";
+            this.lbRemotePort.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+            // 
+            // btnOK
+            // 
+            this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.btnOK.Location = new System.Drawing.Point(430, 239);
+            this.btnOK.Name = "btnOK";
+            this.btnOK.Size = new System.Drawing.Size(75, 30);
+            this.btnOK.TabIndex = 7;
+            this.btnOK.Text = "确定";
+            this.btnOK.UseVisualStyleBackColor = true;
+            this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
+            // 
+            // btnCancel
+            // 
+            this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+            this.btnCancel.Location = new System.Drawing.Point(513, 239);
+            this.btnCancel.Name = "btnCancel";
+            this.btnCancel.Size = new System.Drawing.Size(75, 30);
+            this.btnCancel.TabIndex = 7;
+            this.btnCancel.Text = "取消";
+            this.btnCancel.UseVisualStyleBackColor = true;
+            // 
+            // btnDefault
+            // 
+            this.btnDefault.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.btnDefault.Location = new System.Drawing.Point(347, 239);
+            this.btnDefault.Name = "btnDefault";
+            this.btnDefault.Size = new System.Drawing.Size(75, 30);
+            this.btnDefault.TabIndex = 7;
+            this.btnDefault.Text = "默认值";
+            this.btnDefault.UseVisualStyleBackColor = true;
+            this.btnDefault.Click += new System.EventHandler(this.btnDefault_Click);
+            // 
+            // FormConfig
+            // 
+            this.AcceptButton = this.btnOK;
+            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.CancelButton = this.btnCancel;
+            this.ClientSize = new System.Drawing.Size(604, 281);
+            this.Controls.Add(this.btnCancel);
+            this.Controls.Add(this.btnDefault);
+            this.Controls.Add(this.btnOK);
+            this.Controls.Add(this.tabControl1);
+            this.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.IsMaximizable = false;
+            this.IsMinimizable = false;
+            this.Margin = new System.Windows.Forms.Padding(3, 5, 3, 5);
+            this.MaximizeBox = false;
+            this.MinimizeBox = false;
+            this.Name = "FormConfig";
+            this.Text = "FormConfig";
+            this.Load += new System.EventHandler(this.FormConfig_Load);
+            this.Controls.SetChildIndex(this.tabControl1, 0);
+            this.Controls.SetChildIndex(this.btnOK, 0);
+            this.Controls.SetChildIndex(this.btnDefault, 0);
+            this.Controls.SetChildIndex(this.btnCancel, 0);
+            this.tabControl1.ResumeLayout(false);
+            this.tabPage1.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.numServicePort)).EndInit();
+            this.tabPage2.ResumeLayout(false);
+            this.tabPage2.PerformLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.numShowMaxLine)).EndInit();
+            this.tabPage3.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.numRemotePort)).EndInit();
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.TabControl tabControl1;
+        private System.Windows.Forms.TabPage tabPage1;
+        private System.Windows.Forms.TabPage tabPage2;
+        private System.Windows.Forms.Button btnOK;
+        private System.Windows.Forms.Button btnCancel;
+        private System.Windows.Forms.TabPage tabPage3;
+        private System.Windows.Forms.NumericUpDown numServicePort;
+        private System.Windows.Forms.Label lbServicePort;
+        private System.Windows.Forms.NumericUpDown numRemotePort;
+        private System.Windows.Forms.Label lbRemotePort;
+        private System.Windows.Forms.NumericUpDown numShowMaxLine;
+        private System.Windows.Forms.Label lbShowMaxLine;
+        private System.Windows.Forms.Label lbFolderPath;
+        private System.Windows.Forms.Button btnBrowse;
+        private System.Windows.Forms.TextBox txtFolderPath;
+        private System.Windows.Forms.Button btnDefault;
+        private System.Windows.Forms.Label lbBaudRate;
+        private System.Windows.Forms.ComboBox cmbBaudRate;
+        private System.Windows.Forms.ComboBox cmbDataBits;
+        private System.Windows.Forms.Label lbDataBits;
+        private System.Windows.Forms.ComboBox cmbParity;
+        private System.Windows.Forms.ComboBox cmbStopBits;
+        private System.Windows.Forms.Label lbParity;
+        private System.Windows.Forms.Label lbStopBits;
+    }
+}

+ 206 - 0
GatewayTool/FormConfig.cs

@@ -0,0 +1,206 @@
+using GatewayTool.Businesses;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using WjwLib.API.Configuration;
+using GatewayTool.Models;
+using System.Reflection;
+
+namespace GatewayTool
+{
+    public partial class FormConfig : FormBase
+    {
+        private const string NAME = "Name";
+        private const string VALUE = "Value";
+
+        private SoftwareConfigs _softwareConfigs;
+        private static IConfig config = new ConfigServer(ConfigType.CT_INI);
+
+        public event EventHandler ExecConfigSaved;
+
+        public FormConfig(ref SoftwareConfigs softwareConfigs)
+        {
+            InitializeComponent();
+            this.IsMaximizable = false;
+            this.IsMinimizable = false;
+            this.Icon = Resource.favicon;
+            this.ShowInTaskbar = false;
+            _softwareConfigs = softwareConfigs;
+        }
+
+        private void InitStrings()
+        {
+            this.Title = StrMsg.Settings;
+            tabPage1.Text = StrMsg.Net;
+            lbServicePort.Text = StrMsg.ServicePort;
+
+            tabPage2.Text = StrMsg.Log;
+            lbShowMaxLine.Text = StrMsg.ShowMaxLine;
+            lbFolderPath.Text = StrMsg.FolderPath;
+
+            tabPage3.Text = StrMsg.DeviceSniff;
+            lbRemotePort.Text = StrMsg.RemotePort;
+            lbBaudRate.Text = StrMsg.Baudrate;
+            lbDataBits.Text = StrMsg.Databits;
+            lbStopBits.Text = StrMsg.Stopbits;
+            lbParity.Text = StrMsg.Parity;
+            btnDefault.Text = StrMsg.DefaultValue;
+            btnOK.Text = StrMsg.MessageBoxButtonOK;
+            btnCancel.Text = StrMsg.MessageBoxButtonCancel;
+        }
+
+        private void FormConfig_Load(object sender, EventArgs e)
+        {
+            InitComboxes();
+            InitStrings();
+            ModelToView();
+        }
+
+        private void InitComboxes()
+        {
+            InitCombox<EnumBaudrate>(cmbBaudRate, VALUE, VALUE);
+            InitCombox<EnumDataBits>(cmbDataBits, VALUE, VALUE);
+            InitCombox<EnumStopBits>(cmbStopBits, VALUE, VALUE);
+            InitCombox<EnumCheckBit>(cmbParity, NAME, VALUE);
+        }
+
+        private void InitCombox<T>(ComboBox cmb, string displayMember, string valueMember)
+        {
+            cmb.Items.Clear();
+            cmb.DataSource = GetDataTable<T>();
+            cmb.DisplayMember = displayMember;
+            cmb.ValueMember = valueMember;
+        }
+
+        private DataTable GetDataTable<T>()
+        {
+            Type t = typeof(T);  //创建类型
+            FieldInfo[] fieldinfo = t.GetFields(); //获取字段信息对象集合
+
+            DataTable table = new DataTable();
+            table.Columns.Add("Name", typeof(String));
+            table.Columns.Add("Value", typeof(Int32));
+
+            foreach (FieldInfo field in fieldinfo)
+            {
+                if (!field.IsSpecialName)
+                {
+                    DataRow row = table.NewRow();
+                    row[0] = field.Name;   //获取文本字段
+                    row[1] = (int)field.GetRawConstantValue();  //获取int数值
+                    table.Rows.Add(row);
+                }
+            }
+            return table;
+        }
+
+        private void ModelToView()
+        {
+            numServicePort.Value = _softwareConfigs.ServicePortNum;
+            numShowMaxLine.Value = _softwareConfigs.ShowLogMaxLine;
+            txtFolderPath.Text = _softwareConfigs.LogFolderPath;
+            numRemotePort.Value = _softwareConfigs.SniffRemotePortNum;
+            cmbBaudRate.Text = Convert.ToInt32(Enum.Parse(typeof(EnumBaudrate), _softwareConfigs.SniffBaudRate.ToString())).ToString();
+            cmbDataBits.Text = Convert.ToInt32(Enum.Parse(typeof(EnumDataBits), _softwareConfigs.SniffDataBits.ToString())).ToString();
+            cmbStopBits.Text = Convert.ToInt32(Enum.Parse(typeof(EnumStopBits), _softwareConfigs.SniffStopBits.ToString())).ToString();
+            cmbParity.Text = Enum.Parse(typeof(EnumCheckBit), _softwareConfigs.SniffParity.ToString()).ToString();
+        }
+
+        private void ViewToModel()
+        {
+            _softwareConfigs.ServicePortNum = Convert.ToInt32(numServicePort.Value);
+            _softwareConfigs.ShowLogMaxLine = Convert.ToInt32(numShowMaxLine.Value);
+            _softwareConfigs.LogFolderPath = txtFolderPath.Text.Trim();
+            _softwareConfigs.SniffRemotePortNum = Convert.ToInt32(numRemotePort.Value);
+            _softwareConfigs.SniffBaudRate = (EnumBaudrate)cmbBaudRate.SelectedValue;
+            _softwareConfigs.SniffDataBits = (EnumDataBits)cmbDataBits.SelectedValue;
+            _softwareConfigs.SniffStopBits = (EnumStopBits)cmbStopBits.SelectedValue;
+            _softwareConfigs.SniffParity = (EnumCheckBit)cmbParity.SelectedValue;
+        }
+
+        private void DefaultToModel()
+        {
+            SoftwareConfigs defaultConfig = new SoftwareConfigs();
+            _softwareConfigs.ServicePortNum = defaultConfig.ServicePortNum;
+            _softwareConfigs.ShowLogMaxLine = defaultConfig.ShowLogMaxLine;
+            _softwareConfigs.LogFolderPath = defaultConfig.LogFolderPath;
+            _softwareConfigs.SniffRemotePortNum = defaultConfig.SniffRemotePortNum;
+            _softwareConfigs.SniffBaudRate = defaultConfig.SniffBaudRate;
+            _softwareConfigs.SniffDataBits = defaultConfig.SniffDataBits;
+            _softwareConfigs.SniffStopBits = defaultConfig.SniffStopBits;
+            _softwareConfigs.SniffParity = defaultConfig.SniffParity;
+        }
+
+        public static void ConfigToModel(ref SoftwareConfigs softwareConfigs)
+        {
+            try
+            {
+                softwareConfigs.ServicePortNum = Convert.ToInt32(config.ReadConfig("Net", "ServicePortNum", softwareConfigs.ConfigFilePath));
+                softwareConfigs.ShowLogMaxLine = Convert.ToInt32(config.ReadConfig("Log", "ShowLogMaxLine", softwareConfigs.ConfigFilePath));
+                softwareConfigs.LogFolderPath = config.ReadConfig("Log", "LogFolderPath", softwareConfigs.ConfigFilePath);
+                softwareConfigs.SniffRemotePortNum = Convert.ToInt32(config.ReadConfig("Sniff", "SniffRemotePortNum", softwareConfigs.ConfigFilePath));
+                softwareConfigs.SniffBaudRate = (EnumBaudrate)Enum.Parse(typeof(EnumBaudrate), config.ReadConfig("Sniff", "SniffBaudRate", softwareConfigs.ConfigFilePath));
+                softwareConfigs.SniffDataBits = (EnumDataBits)Enum.Parse(typeof(EnumDataBits), config.ReadConfig("Sniff", "SniffDataBits", softwareConfigs.ConfigFilePath));
+                softwareConfigs.SniffStopBits = (EnumStopBits)Enum.Parse(typeof(EnumStopBits), config.ReadConfig("Sniff", "SniffStopBits", softwareConfigs.ConfigFilePath));
+                softwareConfigs.SniffParity = (EnumCheckBit)Enum.Parse(typeof(EnumCheckBit), config.ReadConfig("Sniff", "SniffParity", softwareConfigs.ConfigFilePath));
+            }
+            catch { }
+        }
+
+        private bool SaveConfigs()
+        {
+            if (_softwareConfigs == null) return false;
+            try
+            {
+                ViewToModel();
+                bool res = config.WriteConfig("Net", "ServicePortNum", _softwareConfigs.ServicePortNum.ToString(), _softwareConfigs.ConfigFilePath);
+                if (res) res = config.WriteConfig("Log", "ShowLogMaxLine", _softwareConfigs.ShowLogMaxLine.ToString(), _softwareConfigs.ConfigFilePath);
+                if (res) res = config.WriteConfig("Log", "LogFolderPath", _softwareConfigs.LogFolderPath, _softwareConfigs.ConfigFilePath);
+                if (res) res = config.WriteConfig("Sniff", "SniffRemotePortNum", _softwareConfigs.SniffRemotePortNum.ToString(), _softwareConfigs.ConfigFilePath);
+                if (res) res = config.WriteConfig("Sniff", "SniffBaudRate", _softwareConfigs.SniffBaudRate.ToString(), _softwareConfigs.ConfigFilePath);
+                if (res) res = config.WriteConfig("Sniff", "SniffDataBits", _softwareConfigs.SniffDataBits.ToString(), _softwareConfigs.ConfigFilePath);
+                if (res) res = config.WriteConfig("Sniff", "SniffStopBits", _softwareConfigs.SniffStopBits.ToString(), _softwareConfigs.ConfigFilePath);
+                if (res) res = config.WriteConfig("Sniff", "SniffParity", _softwareConfigs.SniffParity.ToString(), _softwareConfigs.ConfigFilePath);
+                return res;
+            }
+            catch
+            {
+                return false;
+            }
+        }
+
+        private void btnOK_Click(object sender, EventArgs e)
+        {
+            if (SaveConfigs())
+            {
+                ExecConfigSaved?.Invoke(sender, e);
+                MessageBox.Show(StrMsg.SaveConfigOK, StrMsg.Prompt, MessageBoxButtons.OK, MessageBoxIcon.Information);
+            }
+            else
+            {
+                MessageBox.Show(StrMsg.SaveConfigFail, StrMsg.Attention, MessageBoxButtons.OK, MessageBoxIcon.Error);
+            }
+        }
+
+        private void btnBrowse_Click(object sender, EventArgs e)
+        {
+            FolderBrowserDialog folderDlg = new FolderBrowserDialog();
+            if (folderDlg.ShowDialog() == DialogResult.OK)
+            {
+                txtFolderPath.Text = folderDlg.SelectedPath;
+            }
+        }
+
+        private void btnDefault_Click(object sender, EventArgs e)
+        {
+            DefaultToModel();
+            ModelToView();
+        }
+    }
+}

+ 120 - 0
GatewayTool/FormConfig.resx

@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 101 - 0
GatewayTool/FormJson.Designer.cs

@@ -0,0 +1,101 @@
+
+namespace GatewayTool
+{
+    partial class FormJson
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.btnBrowse = new System.Windows.Forms.Button();
+            this.rtResult = new System.Windows.Forms.RichTextBox();
+            this.txtPath = new System.Windows.Forms.TextBox();
+            this.SuspendLayout();
+            // 
+            // btnBrowse
+            // 
+            this.btnBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+            this.btnBrowse.Location = new System.Drawing.Point(717, 37);
+            this.btnBrowse.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
+            this.btnBrowse.Name = "btnBrowse";
+            this.btnBrowse.Size = new System.Drawing.Size(32, 28);
+            this.btnBrowse.TabIndex = 8;
+            this.btnBrowse.Text = "...";
+            this.btnBrowse.UseVisualStyleBackColor = true;
+            this.btnBrowse.Click += new System.EventHandler(this.btnBrowse_Click);
+            // 
+            // rtResult
+            // 
+            this.rtResult.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.rtResult.BackColor = System.Drawing.Color.Black;
+            this.rtResult.ForeColor = System.Drawing.Color.Lime;
+            this.rtResult.Location = new System.Drawing.Point(10, 71);
+            this.rtResult.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
+            this.rtResult.Name = "rtResult";
+            this.rtResult.ReadOnly = true;
+            this.rtResult.Size = new System.Drawing.Size(739, 302);
+            this.rtResult.TabIndex = 9;
+            this.rtResult.Text = "";
+            // 
+            // txtPath
+            // 
+            this.txtPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.txtPath.Enabled = false;
+            this.txtPath.Location = new System.Drawing.Point(10, 38);
+            this.txtPath.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
+            this.txtPath.Name = "txtPath";
+            this.txtPath.Size = new System.Drawing.Size(700, 26);
+            this.txtPath.TabIndex = 12;
+            // 
+            // FormJson
+            // 
+            this.AcceptButton = this.btnBrowse;
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
+            this.ClientSize = new System.Drawing.Size(758, 378);
+            this.Controls.Add(this.txtPath);
+            this.Controls.Add(this.rtResult);
+            this.Controls.Add(this.btnBrowse);
+            this.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
+            this.Name = "FormJson";
+            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+            this.Text = "FormPing";
+            this.Controls.SetChildIndex(this.btnBrowse, 0);
+            this.Controls.SetChildIndex(this.rtResult, 0);
+            this.Controls.SetChildIndex(this.txtPath, 0);
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+        private System.Windows.Forms.Button btnBrowse;
+        private System.Windows.Forms.RichTextBox rtResult;
+        private System.Windows.Forms.TextBox txtPath;
+    }
+}

+ 44 - 0
GatewayTool/FormJson.cs

@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Net.NetworkInformation;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using WjwLib.API.System;
+
+namespace GatewayTool
+{
+    public partial class FormJson : FormBase
+    {
+        private const string jsonFilter = "json文件(*.json)|*.json";
+
+        public FormJson()
+        {
+            InitializeComponent();
+            this.IsMinimizable = false;
+            this.ShowInTaskbar = false;
+        }
+
+        private void btnBrowse_Click(object sender, EventArgs e)
+        {
+            OpenFileDialog dialog = new OpenFileDialog();
+            dialog.Filter = jsonFilter;
+            dialog.Multiselect = false;
+            dialog.RestoreDirectory = true;
+            if (dialog.ShowDialog() == DialogResult.OK)
+            {
+                string path = dialog.FileName;
+                string str = WjwLib.API.File.PlainText.GetFileContent(path, Encoding.Default);
+                txtPath.Text = path;
+                rtResult.Clear();
+                rtResult.AppendText(WjwLib.API.System.JsonHelper.ConvertJsonString(str));
+            }
+        }
+    }
+}

+ 120 - 0
GatewayTool/FormJson.resx

@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 444 - 0
GatewayTool/FormMain.Designer.cs

@@ -0,0 +1,444 @@
+
+namespace GatewayTool
+{
+    partial class FormMain
+    {
+        /// <summary>
+        /// 必需的设计器变量。
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// 清理所有正在使用的资源。
+        /// </summary>
+        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows 窗体设计器生成的代码
+
+        /// <summary>
+        /// 设计器支持所需的方法 - 不要修改
+        /// 使用代码编辑器修改此方法的内容。
+        /// </summary>
+        private void InitializeComponent()
+        {
+            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormMain));
+            this.menuStrip1 = new System.Windows.Forms.MenuStrip();
+            this.menuSystem = new System.Windows.Forms.ToolStripMenuItem();
+            this.menuLanguage = new System.Windows.Forms.ToolStripMenuItem();
+            this.menuSimplifiedChinese = new System.Windows.Forms.ToolStripMenuItem();
+            this.menuEnglish = new System.Windows.Forms.ToolStripMenuItem();
+            this.menuSettings = new System.Windows.Forms.ToolStripMenuItem();
+            this.menuTool = new System.Windows.Forms.ToolStripMenuItem();
+            this.menuPing = new System.Windows.Forms.ToolStripMenuItem();
+            this.menuParity = new System.Windows.Forms.ToolStripMenuItem();
+            this.menuJson = new System.Windows.Forms.ToolStripMenuItem();
+            this.menuView = new System.Windows.Forms.ToolStripMenuItem();
+            this.menuLog = new System.Windows.Forms.ToolStripMenuItem();
+            this.menuNail = new System.Windows.Forms.ToolStripMenuItem();
+            this.menuHelp = new System.Windows.Forms.ToolStripMenuItem();
+            this.menuUsage = new System.Windows.Forms.ToolStripMenuItem();
+            this.menuAbout = new System.Windows.Forms.ToolStripMenuItem();
+            this.statusStrip1 = new System.Windows.Forms.StatusStrip();
+            this.toolStripProgressBar1 = new System.Windows.Forms.ToolStripStatusLabel();
+            this.lbStatusCopyright = new System.Windows.Forms.ToolStripStatusLabel();
+            this.toolStrip1 = new GatewayTool.UserControls.ToolStripEx();
+            this.btnServiceCtrl = new System.Windows.Forms.ToolStripButton();
+            this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
+            this.btnRefreshList = new System.Windows.Forms.ToolStripButton();
+            this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
+            this.btnOpenLog = new System.Windows.Forms.ToolStripButton();
+            this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+            this.pnlMain = new System.Windows.Forms.Panel();
+            this.splitter2 = new System.Windows.Forms.Splitter();
+            this.ucDeviceList = new GatewayTool.UserControls.UcDeviceList();
+            this.ucLogWindow1 = new WjwLib.Control.LogWindow.UcLogWindow();
+            this.menuStrip1.SuspendLayout();
+            this.statusStrip1.SuspendLayout();
+            this.toolStrip1.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
+            this.splitContainer1.Panel1.SuspendLayout();
+            this.splitContainer1.Panel2.SuspendLayout();
+            this.splitContainer1.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // menuStrip1
+            // 
+            this.menuStrip1.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.menuStrip1.ImageScalingSize = new System.Drawing.Size(20, 20);
+            this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.menuSystem,
+            this.menuTool,
+            this.menuView,
+            this.menuHelp});
+            this.menuStrip1.Location = new System.Drawing.Point(2, 35);
+            this.menuStrip1.Name = "menuStrip1";
+            this.menuStrip1.Padding = new System.Windows.Forms.Padding(7, 2, 0, 2);
+            this.menuStrip1.Size = new System.Drawing.Size(1025, 28);
+            this.menuStrip1.TabIndex = 6;
+            this.menuStrip1.Text = "menuStrip1";
+            // 
+            // menuSystem
+            // 
+            this.menuSystem.CheckOnClick = true;
+            this.menuSystem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.menuLanguage,
+            this.menuSettings});
+            this.menuSystem.Name = "menuSystem";
+            this.menuSystem.Size = new System.Drawing.Size(49, 24);
+            this.menuSystem.Text = "系统";
+            // 
+            // menuLanguage
+            // 
+            this.menuLanguage.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.menuSimplifiedChinese,
+            this.menuEnglish});
+            this.menuLanguage.Name = "menuLanguage";
+            this.menuLanguage.Size = new System.Drawing.Size(106, 24);
+            this.menuLanguage.Text = "语言";
+            // 
+            // menuSimplifiedChinese
+            // 
+            this.menuSimplifiedChinese.Checked = true;
+            this.menuSimplifiedChinese.CheckOnClick = true;
+            this.menuSimplifiedChinese.CheckState = System.Windows.Forms.CheckState.Checked;
+            this.menuSimplifiedChinese.Name = "menuSimplifiedChinese";
+            this.menuSimplifiedChinese.Size = new System.Drawing.Size(134, 24);
+            this.menuSimplifiedChinese.Text = "简体中文";
+            this.menuSimplifiedChinese.Click += new System.EventHandler(this.menuLanguageItem_Click);
+            // 
+            // menuEnglish
+            // 
+            this.menuEnglish.CheckOnClick = true;
+            this.menuEnglish.Name = "menuEnglish";
+            this.menuEnglish.Size = new System.Drawing.Size(134, 24);
+            this.menuEnglish.Text = "English";
+            this.menuEnglish.Click += new System.EventHandler(this.menuLanguageItem_Click);
+            // 
+            // menuSettings
+            // 
+            this.menuSettings.Name = "menuSettings";
+            this.menuSettings.Size = new System.Drawing.Size(106, 24);
+            this.menuSettings.Text = "设置";
+            this.menuSettings.Click += new System.EventHandler(this.menuSettings_Click);
+            // 
+            // menuTool
+            // 
+            this.menuTool.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.menuPing,
+            this.menuParity,
+            this.menuJson});
+            this.menuTool.Name = "menuTool";
+            this.menuTool.ShortcutKeyDisplayString = "";
+            this.menuTool.Size = new System.Drawing.Size(49, 24);
+            this.menuTool.Text = "工具";
+            // 
+            // menuPing
+            // 
+            this.menuPing.Name = "menuPing";
+            this.menuPing.Size = new System.Drawing.Size(178, 24);
+            this.menuPing.Text = "Ping";
+            this.menuPing.Click += new System.EventHandler(this.menuToolItem_Click);
+            // 
+            // menuParity
+            // 
+            this.menuParity.Name = "menuParity";
+            this.menuParity.Size = new System.Drawing.Size(178, 24);
+            this.menuParity.Text = "校验计算器";
+            this.menuParity.Click += new System.EventHandler(this.menuToolItem_Click);
+            // 
+            // menuJson
+            // 
+            this.menuJson.Name = "menuJson";
+            this.menuJson.Size = new System.Drawing.Size(178, 24);
+            this.menuJson.Text = "Json文件查看器";
+            this.menuJson.Click += new System.EventHandler(this.menuToolItem_Click);
+            // 
+            // menuView
+            // 
+            this.menuView.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.menuLog,
+            this.menuNail});
+            this.menuView.Name = "menuView";
+            this.menuView.Size = new System.Drawing.Size(49, 24);
+            this.menuView.Text = "视图";
+            // 
+            // menuLog
+            // 
+            this.menuLog.Checked = true;
+            this.menuLog.CheckOnClick = true;
+            this.menuLog.CheckState = System.Windows.Forms.CheckState.Checked;
+            this.menuLog.Name = "menuLog";
+            this.menuLog.Size = new System.Drawing.Size(134, 24);
+            this.menuLog.Text = "日志";
+            this.menuLog.Click += new System.EventHandler(this.menuViewItem_Click);
+            // 
+            // menuNail
+            // 
+            this.menuNail.CheckOnClick = true;
+            this.menuNail.Name = "menuNail";
+            this.menuNail.Size = new System.Drawing.Size(134, 24);
+            this.menuNail.Text = "钉在最前";
+            this.menuNail.Click += new System.EventHandler(this.menuViewItem_Click);
+            // 
+            // menuHelp
+            // 
+            this.menuHelp.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.menuUsage,
+            this.menuAbout});
+            this.menuHelp.Name = "menuHelp";
+            this.menuHelp.Size = new System.Drawing.Size(70, 24);
+            this.menuHelp.Text = "帮助(&H)";
+            // 
+            // menuUsage
+            // 
+            this.menuUsage.Name = "menuUsage";
+            this.menuUsage.Size = new System.Drawing.Size(134, 24);
+            this.menuUsage.Text = "使用手册";
+            this.menuUsage.Click += new System.EventHandler(this.menuUsage_Click);
+            // 
+            // menuAbout
+            // 
+            this.menuAbout.Name = "menuAbout";
+            this.menuAbout.Size = new System.Drawing.Size(134, 24);
+            this.menuAbout.Text = "关于";
+            this.menuAbout.Click += new System.EventHandler(this.menuAbout_Click);
+            // 
+            // statusStrip1
+            // 
+            this.statusStrip1.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.statusStrip1.ImageScalingSize = new System.Drawing.Size(20, 20);
+            this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.toolStripProgressBar1,
+            this.lbStatusCopyright});
+            this.statusStrip1.Location = new System.Drawing.Point(2, 679);
+            this.statusStrip1.Name = "statusStrip1";
+            this.statusStrip1.Padding = new System.Windows.Forms.Padding(1, 0, 16, 0);
+            this.statusStrip1.Size = new System.Drawing.Size(1025, 25);
+            this.statusStrip1.SizingGrip = false;
+            this.statusStrip1.TabIndex = 7;
+            this.statusStrip1.Text = "statusStrip1";
+            // 
+            // toolStripProgressBar1
+            // 
+            this.toolStripProgressBar1.Name = "toolStripProgressBar1";
+            this.toolStripProgressBar1.Size = new System.Drawing.Size(0, 20);
+            // 
+            // lbStatusCopyright
+            // 
+            this.lbStatusCopyright.Name = "lbStatusCopyright";
+            this.lbStatusCopyright.Size = new System.Drawing.Size(1008, 20);
+            this.lbStatusCopyright.Spring = true;
+            this.lbStatusCopyright.Text = "--";
+            this.lbStatusCopyright.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+            // 
+            // toolStrip1
+            // 
+            this.toolStrip1.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.toolStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
+            this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.btnServiceCtrl,
+            this.toolStripSeparator1,
+            this.btnRefreshList,
+            this.toolStripSeparator2,
+            this.btnOpenLog});
+            this.toolStrip1.Location = new System.Drawing.Point(2, 63);
+            this.toolStrip1.Name = "toolStrip1";
+            this.toolStrip1.Size = new System.Drawing.Size(1025, 75);
+            this.toolStrip1.TabIndex = 14;
+            this.toolStrip1.Text = "toolStrip1";
+            // 
+            // btnServiceCtrl
+            // 
+            this.btnServiceCtrl.Image = global::GatewayTool.Resource.start;
+            this.btnServiceCtrl.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
+            this.btnServiceCtrl.ImageTransparentColor = System.Drawing.Color.Magenta;
+            this.btnServiceCtrl.Name = "btnServiceCtrl";
+            this.btnServiceCtrl.Size = new System.Drawing.Size(69, 72);
+            this.btnServiceCtrl.Text = "启动服务";
+            this.btnServiceCtrl.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
+            this.btnServiceCtrl.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText;
+            this.btnServiceCtrl.Click += new System.EventHandler(this.btnServiceCtrl_Click);
+            // 
+            // toolStripSeparator1
+            // 
+            this.toolStripSeparator1.Name = "toolStripSeparator1";
+            this.toolStripSeparator1.Size = new System.Drawing.Size(6, 75);
+            // 
+            // btnRefreshList
+            // 
+            this.btnRefreshList.Image = ((System.Drawing.Image)(resources.GetObject("btnRefreshList.Image")));
+            this.btnRefreshList.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
+            this.btnRefreshList.ImageTransparentColor = System.Drawing.Color.Magenta;
+            this.btnRefreshList.Name = "btnRefreshList";
+            this.btnRefreshList.Size = new System.Drawing.Size(69, 72);
+            this.btnRefreshList.Text = "刷新列表";
+            this.btnRefreshList.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
+            this.btnRefreshList.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText;
+            this.btnRefreshList.Click += new System.EventHandler(this.btnRefreshList_Click);
+            // 
+            // toolStripSeparator2
+            // 
+            this.toolStripSeparator2.Name = "toolStripSeparator2";
+            this.toolStripSeparator2.Size = new System.Drawing.Size(6, 75);
+            // 
+            // btnOpenLog
+            // 
+            this.btnOpenLog.Image = global::GatewayTool.Resource.open;
+            this.btnOpenLog.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
+            this.btnOpenLog.ImageTransparentColor = System.Drawing.Color.Magenta;
+            this.btnOpenLog.Name = "btnOpenLog";
+            this.btnOpenLog.Size = new System.Drawing.Size(69, 72);
+            this.btnOpenLog.Text = "浏览日志";
+            this.btnOpenLog.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
+            this.btnOpenLog.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText;
+            this.btnOpenLog.Click += new System.EventHandler(this.btnOpenLog_Click);
+            // 
+            // splitContainer1
+            // 
+            this.splitContainer1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
+            this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.splitContainer1.Location = new System.Drawing.Point(2, 138);
+            this.splitContainer1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.splitContainer1.Name = "splitContainer1";
+            this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal;
+            // 
+            // splitContainer1.Panel1
+            // 
+            this.splitContainer1.Panel1.Controls.Add(this.pnlMain);
+            this.splitContainer1.Panel1.Controls.Add(this.splitter2);
+            this.splitContainer1.Panel1.Controls.Add(this.ucDeviceList);
+            // 
+            // splitContainer1.Panel2
+            // 
+            this.splitContainer1.Panel2.Controls.Add(this.ucLogWindow1);
+            this.splitContainer1.Size = new System.Drawing.Size(1025, 541);
+            this.splitContainer1.SplitterDistance = 462;
+            this.splitContainer1.SplitterWidth = 5;
+            this.splitContainer1.TabIndex = 17;
+            // 
+            // pnlMain
+            // 
+            this.pnlMain.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
+            this.pnlMain.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.pnlMain.Location = new System.Drawing.Point(362, 0);
+            this.pnlMain.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.pnlMain.Name = "pnlMain";
+            this.pnlMain.Size = new System.Drawing.Size(659, 458);
+            this.pnlMain.TabIndex = 18;
+            // 
+            // splitter2
+            // 
+            this.splitter2.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
+            this.splitter2.Location = new System.Drawing.Point(356, 0);
+            this.splitter2.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.splitter2.Name = "splitter2";
+            this.splitter2.Size = new System.Drawing.Size(6, 458);
+            this.splitter2.TabIndex = 17;
+            this.splitter2.TabStop = false;
+            // 
+            // ucDeviceList
+            // 
+            this.ucDeviceList.Dock = System.Windows.Forms.DockStyle.Left;
+            this.ucDeviceList.FirmwareVersion = "固件版本";
+            this.ucDeviceList.ID = "序号";
+            this.ucDeviceList.Location = new System.Drawing.Point(0, 0);
+            this.ucDeviceList.Margin = new System.Windows.Forms.Padding(3, 6, 3, 6);
+            this.ucDeviceList.ModuleName = "设备列表";
+            this.ucDeviceList.Name = "ucDeviceList";
+            this.ucDeviceList.Size = new System.Drawing.Size(356, 458);
+            this.ucDeviceList.SN = "设备编号";
+            this.ucDeviceList.Source = "数据来源";
+            this.ucDeviceList.TabIndex = 16;
+            // 
+            // ucLogWindow1
+            // 
+            this.ucLogWindow1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.ucLogWindow1.FontColor = System.Drawing.Color.White;
+            this.ucLogWindow1.Location = new System.Drawing.Point(0, 0);
+            this.ucLogWindow1.Margin = new System.Windows.Forms.Padding(3, 5, 3, 5);
+            this.ucLogWindow1.MaxLines = 20;
+            this.ucLogWindow1.MenuClearText = null;
+            this.ucLogWindow1.MenuCopyText = null;
+            this.ucLogWindow1.MenuSelectAllText = null;
+            this.ucLogWindow1.Name = "ucLogWindow1";
+            this.ucLogWindow1.Size = new System.Drawing.Size(1021, 70);
+            this.ucLogWindow1.TabIndex = 0;
+            this.ucLogWindow1.WindowColor = System.Drawing.Color.Black;
+            // 
+            // FormMain
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(1029, 706);
+            this.Controls.Add(this.splitContainer1);
+            this.Controls.Add(this.toolStrip1);
+            this.Controls.Add(this.statusStrip1);
+            this.Controls.Add(this.menuStrip1);
+            this.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.MainMenuStrip = this.menuStrip1;
+            this.Margin = new System.Windows.Forms.Padding(3, 6, 3, 6);
+            this.Name = "FormMain";
+            this.Text = "FormMain";
+            this.Load += new System.EventHandler(this.FormMain_Load);
+            this.Controls.SetChildIndex(this.menuStrip1, 0);
+            this.Controls.SetChildIndex(this.statusStrip1, 0);
+            this.Controls.SetChildIndex(this.toolStrip1, 0);
+            this.Controls.SetChildIndex(this.splitContainer1, 0);
+            this.menuStrip1.ResumeLayout(false);
+            this.menuStrip1.PerformLayout();
+            this.statusStrip1.ResumeLayout(false);
+            this.statusStrip1.PerformLayout();
+            this.toolStrip1.ResumeLayout(false);
+            this.toolStrip1.PerformLayout();
+            this.splitContainer1.Panel1.ResumeLayout(false);
+            this.splitContainer1.Panel2.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
+            this.splitContainer1.ResumeLayout(false);
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.MenuStrip menuStrip1;
+        private System.Windows.Forms.ToolStripMenuItem menuSystem;
+        private System.Windows.Forms.ToolStripMenuItem menuLanguage;
+        private System.Windows.Forms.ToolStripMenuItem menuSimplifiedChinese;
+        private System.Windows.Forms.ToolStripMenuItem menuEnglish;
+        private System.Windows.Forms.StatusStrip statusStrip1;
+        private System.Windows.Forms.ToolStripMenuItem menuTool;
+        private System.Windows.Forms.ToolStripMenuItem menuPing;
+        private System.Windows.Forms.ToolStripMenuItem menuParity;
+        private GatewayTool.UserControls.ToolStripEx toolStrip1;
+        private System.Windows.Forms.ToolStripButton btnServiceCtrl;
+        private System.Windows.Forms.SplitContainer splitContainer1;
+        private System.Windows.Forms.Splitter splitter2;
+        private UserControls.UcDeviceList ucDeviceList;
+        private System.Windows.Forms.ToolStripMenuItem menuView;
+        private System.Windows.Forms.ToolStripMenuItem menuLog;
+        private WjwLib.Control.LogWindow.UcLogWindow ucLogWindow1;
+        private System.Windows.Forms.Panel pnlMain;
+        private System.Windows.Forms.ToolStripMenuItem menuSettings;
+        private System.Windows.Forms.ToolStripButton btnRefreshList;
+        private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
+        private System.Windows.Forms.ToolStripMenuItem menuNail;
+        private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
+        private System.Windows.Forms.ToolStripButton btnOpenLog;
+        private System.Windows.Forms.ToolStripMenuItem menuHelp;
+        private System.Windows.Forms.ToolStripMenuItem menuUsage;
+        private System.Windows.Forms.ToolStripMenuItem menuAbout;
+        private System.Windows.Forms.ToolStripMenuItem menuJson;
+        private System.Windows.Forms.ToolStripStatusLabel lbStatusCopyright;
+        private System.Windows.Forms.ToolStripStatusLabel toolStripProgressBar1;
+    }
+}
+

+ 592 - 0
GatewayTool/FormMain.cs

@@ -0,0 +1,592 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using WjwLib.API.System;
+using WjwLib.Frame.FormBase;
+using WjwLib.Frame.Log;
+using GatewayTool.Models;
+using GatewayTool.Businesses;
+using Newtonsoft.Json;
+using System.IO;
+using System.Diagnostics;
+using GatewayTool.UserControls;
+using System.Reflection;
+using WjwLib.API.Algorithm;
+using GatewayTool.Protocols;
+
+namespace GatewayTool
+{
+    public partial class FormMain : FormBase
+    {
+        private FormPing _pingForm = null;
+        private FormParity _parityForm = null;
+        private FormJson _jsonForm = null;
+        private bool _isServiceStart = false;
+
+        private CommandDataItemCollection _lastCommandData = new CommandDataItemCollection();
+        private ServiceOptions _serviceOptions = new ServiceOptions();
+        private SoftwareConfigs _softwareConfigs = new SoftwareConfigs();
+
+        public FormMain()
+        {
+            InitializeComponent();
+            this.Icon = Resource.favicon;
+            FormConfig.ConfigToModel(ref _softwareConfigs);
+            ApplyConfig();
+        }
+
+        private void ApplyConfig()
+        {
+            this.ucLogWindow1.MaxLines = _softwareConfigs.ShowLogMaxLine;
+        }
+
+        private void menuAbout_Click(object sender, EventArgs e)
+        {
+            FormAbout formAbout = new FormAbout();
+            formAbout.ShowDialog(this);
+        }
+
+        private void menuLanguageItem_Click(object sender, EventArgs e)
+        {
+            if (sender == menuSimplifiedChinese)
+            {
+                StrMsg = new StringMsg(Language.SIMPLIFIED_CHINESE);
+            }
+            else if (sender == menuEnglish)
+            {
+                StrMsg = new StringMsg(Language.ENGLISH);
+            }
+            foreach (ToolStripMenuItem menu in menuLanguage.DropDownItems)
+            {
+                menu.Checked = menu == sender;
+            }
+            InitStrings();
+        }
+
+        private void RegisterMsgBox()
+        {
+            MessageBoxManager.OK = StrMsg.MessageBoxButtonOK;
+            MessageBoxManager.Yes = StrMsg.MessageBoxButtonYes;
+            MessageBoxManager.No = StrMsg.MessageBoxButtonNo;
+            MessageBoxManager.Cancel = StrMsg.MessageBoxButtonCancel;
+        }
+
+        private void InitStrings()
+        {
+            this.Title = StrMsg.SoftwareName;
+
+            menuSystem.Text = StrMsg.System;
+            menuLanguage.Text = StrMsg.Language;
+            menuSettings.Text = StrMsg.Settings;
+
+            menuTool.Text = StrMsg.Tool;
+            menuParity.Text = StrMsg.ParityCalculator;
+            menuJson.Text = StrMsg.JsonFileViewer;
+
+            menuView.Text = StrMsg.View;
+            menuLog.Text = StrMsg.Log;
+            menuNail.Text = StrMsg.Nail;
+
+            menuHelp.Text = StrMsg.Help;
+            menuUsage.Text = StrMsg.Usage;
+            menuAbout.Text = StrMsg.About;
+
+            btnServiceCtrl.Text = _isServiceStart ? StrMsg.StopService : StrMsg.StartService;
+            btnRefreshList.Text = StrMsg.RefreshList;
+            btnOpenLog.Text = StrMsg.BrowseLog;
+
+            this.ucDeviceList.ModuleName = StrMsg.DeviceList;
+            this.ucDeviceList.ID = StrMsg.ID;
+            this.ucDeviceList.SN = StrMsg.SN;
+            this.ucDeviceList.Source = StrMsg.Source;
+            this.ucDeviceList.FirmwareVersion = StrMsg.FirmwareVersion;
+
+            this.ucLogWindow1.MenuSelectAllText = StrMsg.MenuSelectAll;
+            this.ucLogWindow1.MenuCopyText = StrMsg.MenuCopy;
+            this.ucLogWindow1.MenuClearText = StrMsg.MenuClear;
+
+            lbStatusCopyright.Text = StrMsg.SoftwareCopyright;
+
+            RegisterMsgBox();
+
+            if (pnlMain.Controls.Count != 0)
+            {
+                Control ctrl = pnlMain.Controls[0];
+                if (ctrl is UcDTUPanel)
+                {
+                    UcDTUPanel dTUPanel = ctrl as UcDTUPanel;
+                    dTUPanel.StrMsg = StrMsg;
+                }
+            }
+        }
+
+        private void FormMain_Load(object sender, EventArgs e)
+        {
+            InitStrings();
+
+            btnServiceCtrl_Click(sender, e);
+            _serviceOptions.ExecDeviceListUpdated += _serviceOptions_ExecDeviceListUpdated;
+            _serviceOptions.ExecReadSystemState += _serviceOptions_ExecReadSystemState;
+            _serviceOptions.ExecReadDeviceConfig += _serviceOptions_ExecReadDeviceConfig;
+            _serviceOptions.ExecWriteDeviceConfig += _serviceOptions_ExecWriteDeviceConfig;
+            _serviceOptions.ExecUpgradeProgressUpdated += ExecUpgradeProgressUpdated;
+            _serviceOptions.ExecUpgradeProgressFinished += _serviceOptions_ExecUpgradeProgressFinished;
+            _serviceOptions.ExecServiceDataInteractive += _serviceOptions_ExecServiceDataInteractive;
+            _serviceOptions.StartNetSniff(_softwareConfigs.SniffRemotePortNum);
+            _serviceOptions.StartComSniff(_softwareConfigs.SniffBaudRate, _softwareConfigs.SniffDataBits, _softwareConfigs.SniffStopBits, _softwareConfigs.SniffParity);
+
+            this.ucDeviceList.ExecItemClicked += UcDeviceList_ExecItemClicked;
+        }        
+
+        private void _serviceOptions_ExecDeviceListUpdated(object sender, EventArgs e)
+        {
+            this.Invoke((EventHandler)delegate
+            {
+                this.ucDeviceList.UpdateDeviceListUI(_serviceOptions.FoundDeviceList);
+            });
+        }
+
+        private void _serviceOptions_ExecReadSystemState(object sender, GatewayBase e)
+        {
+            this.Invoke((EventHandler)delegate
+            {
+                if (this.pnlMain.Controls.Count == 0) return;
+                UcDTUPanel dtuPanel = this.pnlMain.Controls[0] as UcDTUPanel;
+                if (dtuPanel != null && e != null)
+                {
+                    DTU dtu = e as DTU;
+                    if (dtu != null)
+                    {
+                        if (dtuPanel.BindDTU.SniffInfo.SN.Equals(e.SniffInfo.SN))
+                        {
+                            dtuPanel.DeviceSystemStateObj = dtu.SystemState;
+                        }
+                    }
+                }
+            });
+        }
+
+        private void _serviceOptions_ExecReadDeviceConfig(object sender, GatewayBase e)
+        {
+            this.Invoke((EventHandler)delegate
+            {
+                UcDTUPanel dtuPanel = this.pnlMain.Controls[0] as UcDTUPanel;
+                if (dtuPanel != null && e != null)
+                {
+                    DTU dtu = e as DTU;
+                    if (dtu != null)
+                    {
+                        if (dtuPanel.BindDTU.SniffInfo.SN.Equals(e.SniffInfo.SN))
+                        {
+                            dtuPanel.DeviceConfigObj = dtu.Config;
+                        }
+                    }
+                }
+            });
+        }
+
+        private void _serviceOptions_ExecWriteDeviceConfig(object sender, bool e)
+        {
+            if (e)
+            {
+                MessageBox.Show("配置写入设备成功!", StrMsg.Prompt, MessageBoxButtons.OK, MessageBoxIcon.Information);
+            }
+            else
+            {
+                MessageBox.Show("配置写入设备失败!", StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
+            }
+        }
+
+        private void ExecUpgradeProgressUpdated(object sender, GatewayBase e)
+        {
+            this.Invoke((EventHandler)delegate
+            {
+                UcDTUPanel dtuPanel = this.pnlMain.Controls[0] as UcDTUPanel;
+                if (dtuPanel != null && e != null)
+                {
+                    DTU dtu = e as DTU;
+                    if (dtu != null)
+                    {
+                        if (dtuPanel.BindDTU.SniffInfo.SN.Equals(e.SniffInfo.SN))
+                        {
+                            dtuPanel.UpgradeProgress = dtu.UpgradeInfo.UpgradeProgress; //更新进度
+                        }
+                    }
+                }
+            });
+        }
+
+        private void _serviceOptions_ExecUpgradeProgressFinished(object sender, GatewayBase e)
+        {
+            MessageBox.Show("固件更新完毕!", StrMsg.Prompt, MessageBoxButtons.OK, MessageBoxIcon.Information);
+        }
+
+        private void _serviceOptions_ExecServiceDataInteractive(object sender, ServiceInteractiveData e)
+        {
+            string action = string.Empty;
+            switch (e.DataType)
+            {
+                case InteractiveType.SEND:
+                    action = "发送";
+                    break;
+                case InteractiveType.RECV:
+                    action = "收到";
+                    break;
+                default:
+                    break;
+            }
+            string content = e.IsConvertGB2312 ? Encoding.GetEncoding("gb2312").GetString(e.Data) : BitConverter.ToString(e.Data);
+            HandleLog($"{SoftwareConfigs.Now} {action} {e.GatewaySN} :{content}");
+        }
+
+        private void UcDeviceList_ExecItemClicked(object sender, ListViewItem e)
+        {
+            SniffDeviceInfo info = new SniffDeviceInfo() { SN = e.SubItems[1].Text, Source = e.SubItems[2].Text };
+
+            UcDTUPanel dtuPanel = this.pnlMain.Controls.Count == 0 ? null : (UcDTUPanel)this.pnlMain.Controls[0];
+            DoReadDTUSystemState(dtuPanel);
+            if (dtuPanel != null)
+            {
+                //若当前面板对应的正是双击的列表项,则跳过初始化
+                if (dtuPanel.BindDTU.SniffInfo.SN.Equals(info.SN)) return;
+            }
+            else
+            {
+                this.pnlMain.Controls.Clear();
+                GatewayBase dev = _serviceOptions.GetExistDevice(info);
+                LoadDTUPanel(dev as DTU);
+            }
+        }
+
+        private void LoadDTUPanel(DTU dtu)
+        {
+            UcDTUPanel dtuPanel = new UcDTUPanel(dtu);
+            dtuPanel.StrMsg = StrMsg;
+            dtuPanel.Load += DtuPanel_Load;
+            dtuPanel.ExecRefreshState += DtuPanel_ExecRefreshState;
+            dtuPanel.ExecPropertyChanged += DtuPanel_ExecPropertyChanged;
+            dtuPanel.ExecBindObjChanged += DtuPanel_ExecBindObjChanged;
+            dtuPanel.ExecReadConfig += DtuPanel_ExecReadConfig;
+            dtuPanel.ExecDownloadConfig += DtuPanel_ExecDownloadConfig;
+            dtuPanel.ExecSelectTabIndexChanged += DtuPanel_ExecSelectTabIndexChanged;
+            dtuPanel.ExecUpgrade += DtuPanel_ExecUpgrade;
+            dtuPanel.Dock = DockStyle.Fill;
+            this.pnlMain.Controls.Add(dtuPanel);
+        }
+
+        private void DtuPanel_Load(object sender, EventArgs e)
+        {
+            DoReadDTUSystemState(sender);
+        }
+
+        private void DtuPanel_ExecReadConfig(object sender, EventArgs e)
+        {
+            DoReadDTUConfig(sender);
+        }
+
+        private void DtuPanel_ExecRefreshState(object sender, EventArgs e)
+        {
+            DoReadDTUSystemState(sender);
+        }
+
+        private void DtuPanel_ExecSelectTabIndexChanged(object sender, int e)
+        {
+            if (e == 0)
+            {
+                DoReadDTUSystemState(sender);
+            }
+        }
+
+        private void DtuPanel_ExecUpgrade(object sender, string e)
+        {
+            DTU dtu = GetDtuFromSender(sender);
+            if (dtu == null) return;
+            dtu.UpgradeInfo.FirmwareFilePath = e;
+            dtu.Communication.SendData(_serviceOptions.PrepareToUpgradeCommand);
+            HandleLog($"{SoftwareConfigs.Now} 发送 {dtu.SniffInfo.SN} 准备升级命令: {BitConverter.ToString(_serviceOptions.PrepareToUpgradeCommand)}");
+        }
+
+        private DTU GetDtuFromSender(object sender)
+        {
+            UcDTUPanel dtuPanel = sender as UcDTUPanel;
+            if (dtuPanel == null) return null;
+            DTU dtu = dtuPanel.BindDTU;
+            if (dtu == null || dtu.Communication == null || !dtu.Communication.IsOpen) return null;
+            return dtu;
+        }
+
+        private void DoReadDTUSystemState(object sender)
+        {
+            DTU dtu = GetDtuFromSender(sender);
+            if (dtu == null) return;
+            dtu.Communication.SendData(_serviceOptions.ReadDTUSystemStateCommand);
+            HandleLog($"{SoftwareConfigs.Now} 发送 {dtu.SniffInfo.SN} 读系统状态命令: {BitConverter.ToString(_serviceOptions.ReadDTUSystemStateCommand)}");
+        }
+
+        private void DoReadDTUConfig(object sender)
+        {
+            DTU dtu = GetDtuFromSender(sender);
+            if (dtu == null) return;
+            dtu.Communication.SendData(_serviceOptions.ReadDTUDeviceConfigCommand);
+            HandleLog($"{SoftwareConfigs.Now} 发送 {dtu.SniffInfo.SN} 读设备配置命令: {BitConverter.ToString(_serviceOptions.ReadDTUDeviceConfigCommand)}");
+        }
+
+        private void HandleLog(string log)
+        {
+            this.ucLogWindow1.ShowText(log);
+            LogHelper.Log(LogTarget.Txt, _softwareConfigs.LogFilePath, log);
+        }
+
+        private void DtuPanel_ExecDownloadConfig(object sender, string e)
+        {
+            if (e != null)
+            {
+                UcDTUPanel dtuPanel = sender as UcDTUPanel;
+                if (dtuPanel != null)
+                {
+                    DTU dtu = dtuPanel.BindDTU;
+                    if (dtu == null || dtu.Communication == null)
+                    {
+                        MessageBox.Show("设备或通讯实例为空!", StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
+                    }
+                    else if (!dtu.Communication.IsOpen)
+                    {
+                        MessageBox.Show("设备通讯端口未打开!", StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
+                    }
+                    else if (dtu.Communication.SendData(_serviceOptions.WriteDTUDeviceConfigCommand(e)))
+                    {
+                        MessageBox.Show("命令发送成功!", StrMsg.Prompt, MessageBoxButtons.OK, MessageBoxIcon.Information);
+                    }
+                    else
+                    {
+                        MessageBox.Show("命令发送失败!", StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
+                    }
+                }
+            }
+        }
+
+        private void DtuPanel_ExecBindObjChanged(object sender, EventArgs e)
+        {
+            var grid = sender as PropertyGrid;
+            DTUConfig config = (DTUConfig)grid.SelectedObject;
+            HandlePropertiesWith645(config);
+        }
+
+        private void HandlePropertiesWith645(DTUConfig config)
+        {
+            if (config == null) return;
+            bool isTempAdd = false;
+            bool isProtocol645 = config.DataSource == EnumDataSource.Protocol_645;
+            PropertyGridHelper.SetPropertyVisibility(config, "Version645", isProtocol645);
+            PropertyGridHelper.SetPropertyVisibility(config, "CommandData", !isProtocol645);
+
+            if (isProtocol645)
+            {
+                _lastCommandData.Clear();
+                for (int i = config.CommandData.Count - 1; i >= 0; i--)
+                {
+                    _lastCommandData.Add(config.CommandData[i]);
+                }
+                config.CommandData.Clear();
+            }
+            else
+            {
+                if (_lastCommandData != null)
+                {
+                    for (int i = _lastCommandData.Count - 1; i >= 0; i--)
+                    {
+                        config.CommandData.Add(_lastCommandData[i]);
+                    }
+                }
+            }
+            if (config.SensorData != null)
+            {
+                if (config.SensorData.Count == 0)
+                {
+                    config.SensorData.Add(new SensorDataItem());
+                    isTempAdd = true;
+                }
+                foreach (var item in config.SensorData)
+                {
+                    PropertyInfo[] pInfo = item.GetType().GetProperties();
+                    foreach (PropertyInfo p in pInfo)
+                    {
+                        foreach (CustomAttributeData attrData in p.CustomAttributes)
+                        {
+                            if (attrData.AttributeType.Equals(typeof(CategoryAttribute)))
+                            {
+                                string category = attrData.ConstructorArguments[0].Value.ToString().ToUpper();
+                                if (category.Contains("MODBUS"))
+                                {
+                                    PropertyGridHelper.SetPropertyVisibility(item, p.Name, !isProtocol645);
+                                }
+                                else if (category.Contains("645"))
+                                {
+                                    PropertyGridHelper.SetPropertyVisibility(item, p.Name, isProtocol645);
+                                }
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+            if (isTempAdd)
+            {
+                config.SensorData.Clear();
+                isTempAdd = false;
+            }
+            if (config.CommandData != null)
+            {
+                foreach (var item in config.CommandData)
+                {
+                    PropertyInfo[] pInfo = item.GetType().GetProperties();
+                    foreach (PropertyInfo p in pInfo)
+                    {
+                        PropertyGridHelper.SetPropertyVisibility(item, p.Name, !isProtocol645);
+                    }
+                }
+            }
+        }
+
+        private void DtuPanel_ExecPropertyChanged(object sender, GridPropertyChangedArgs e)
+        {
+            var grid = e.Obj as PropertyGrid;
+            var obj = grid.SelectedObject;
+            var val = e.Args.ChangedItem.Value;
+            string changeItemName = e.Args.ChangedItem.PropertyDescriptor.Name;
+            if (changeItemName.Equals("DataSource"))
+            {
+                HandlePropertiesWith645((DTUConfig)obj);
+            }
+            else if (changeItemName.Equals("Host"))
+            {
+                bool res = Validation.IsIP(val.ToString());
+                if (!res)
+                {
+                    MessageBox.Show("非法IP地址", StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
+                    (obj as DTUConfig).Host = e.Args.OldValue?.ToString(); //恢复先前值
+                }
+            }
+
+            grid.SelectedObject = obj;
+        }
+
+        private void menuToolItem_Click(object sender, EventArgs e)
+        {
+            if (sender == menuPing)
+            {
+                _pingForm = _pingForm == null ? new FormPing() : _pingForm;
+                _pingForm.DomainText = StrMsg.Domain;
+                _pingForm.ShowDialog(this);
+            }
+            else if (sender == menuParity)
+            {
+                _parityForm = _parityForm == null ? new FormParity() : _parityForm;
+                _parityForm.Title = StrMsg.ParityCalculator;
+                _parityForm.ShowDialog(this);
+            }
+            else if (sender == menuJson)
+            {
+                _jsonForm = _jsonForm == null ? new FormJson() : _jsonForm;
+                _jsonForm.Title = StrMsg.JsonFileViewer;
+                _jsonForm.ShowDialog(this);
+            }
+        }
+
+        private void menuViewItem_Click(object sender, EventArgs e)
+        {
+            if (sender == menuLog)
+            {
+                splitContainer1.Panel2Collapsed = !menuLog.Checked;
+            }
+            else if (sender == menuNail)
+            {
+                this.TopMost = menuNail.Checked;
+            }
+        }
+
+        private void btnServiceCtrl_Click(object sender, EventArgs e)
+        {
+            string log = "";
+            if (btnServiceCtrl.Text.Equals(StrMsg.StartService))
+            {
+                _isServiceStart = _serviceOptions.StartService(_softwareConfigs.ServicePortNum);
+                log = $"{SoftwareConfigs.Now} {_softwareConfigs.ServicePortNum}服务端口打开{(_isServiceStart ? "成功" : "失败")}";
+                this.ucLogWindow1.ShowText(log);
+                LogHelper.Log(LogTarget.Txt, _softwareConfigs.LogFilePath, log);
+            }
+            else
+            {
+                _serviceOptions.StopService();
+                _isServiceStart = false;
+                log = $"{SoftwareConfigs.Now} 服务端口关闭";
+                this.ucLogWindow1.ShowText(log, Color.Red);
+                LogHelper.Log(LogTarget.Txt, _softwareConfigs.LogFilePath, log);
+            }
+            btnServiceCtrl.Text = _isServiceStart ? StrMsg.StopService : StrMsg.StartService;
+            btnServiceCtrl.Image = _isServiceStart ? Resource.stop : Resource.start;
+        }
+
+        private void btnRefreshList_Click(object sender, EventArgs e)
+        {
+            _serviceOptions.StartNetSniff(_softwareConfigs.SniffRemotePortNum);
+            _serviceOptions.StartComSniff(_softwareConfigs.SniffBaudRate, _softwareConfigs.SniffDataBits, _softwareConfigs.SniffStopBits, _softwareConfigs.SniffParity);
+        }
+
+        private void btnOpenLog_Click(object sender, EventArgs e)
+        {
+            try
+            {
+                if (Directory.Exists(_softwareConfigs.LogFolderPath))
+                {
+                    Process.Start(_softwareConfigs.LogFolderPath);
+                }
+                else
+                {
+                    MessageBox.Show("未找到日志文件夹,可能被移动或删除!", StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
+                }
+            }
+            catch
+            {
+                MessageBox.Show("打开日志文件夹异常!", StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
+            }
+        }
+
+        private void menuUsage_Click(object sender, EventArgs e)
+        {
+            try
+            {
+                if (File.Exists(_softwareConfigs.HelpFilePath))
+                {
+                    Process.Start(_softwareConfigs.HelpFilePath);
+                }
+                else
+                {
+                    MessageBox.Show("未找到帮助文档,可能被移动或删除!", StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
+                }
+            }
+            catch
+            {
+                MessageBox.Show("打开帮助文档异常!", StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
+            }
+        }
+
+        private void menuSettings_Click(object sender, EventArgs e)
+        {
+            FormConfig formConfig = new FormConfig(ref _softwareConfigs);
+            formConfig.ExecConfigSaved += FormConfig_ExecConfigSaved;
+            formConfig.ShowDialog(this);
+        }
+
+        private void FormConfig_ExecConfigSaved(object sender, EventArgs e)
+        {
+            ApplyConfig();
+        }
+    }
+}

+ 154 - 0
GatewayTool/FormMain.resx

@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>159, 17</value>
+  </metadata>
+  <metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>280, 17</value>
+  </metadata>
+  <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+  <data name="btnRefreshList.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAQzSURBVGhD1Vo7aBRRFN0ujYqFhRaCiFrYCH7BmUkUIQS0
+        UBCJYKFErEwlijEzs2JECIIgYhE/SBqxUBDEQoxYpfFXCSKKIiiKiGAKweK98ZyZN2R3c2d2ZvaXOXDY
+        Te599973u+++ma20C7an99ieuuT4ahqcsV31zvHUHBl+x/8oow51TbPewq7qfQhqynHVN8cPglwM26gp
+        2jDmuoOBarDE9vWo5as3YmAFSFu0SdvGTfsB4ysdX49hOXyQgmgHI9t6jL6M2/ag39PbonUsO2436Ys+
+        jfvWAEO7JSfdIH2bMIoB6/KYZLibZAwmnHxAqjsjGewFGYsJKxuwkUYkQ72lHjHhpcPx9F7ZwCIgYjNh
+        ymD66mSabJWMLTXFMgdLDRcX9ZgJtx48Bdsy+p56Hg6EGxx1qnowzGSeugI+tX31Q2yTg2YWFp7YPMql
+        Btmppgaqeqsxl4iog1L77GSsxtw8itc26q3tBVuMGcykXme7+iBGewLBPsLnNRRsw86EXk096st2spOx
+        GncRnHE9KCk2I4J7YUxUhkZ1nzOurkt6HSFiNq65fNRlUSmNWAqmecU5pzdbrvou6qXRVZPWeLArZrhv
+        JD2BjNm4RwCu+iIpJRK1vF0N1rJt0b2DZbUhthGDBZykKxIxh42samCLCim0vOA823K9S/KmNLPHUedn
+        /wW9Ptzcrnov6ieQsaPm4TVQVpCpfjsX9aowAE89k3WasKYDlqs3hd99dUfUTSFjx+HFO6ysIBGO7tMh
+        pn+/JM/Emg4gr98L7eGeLOqmUk2zA7hsS0KZSJHDdIi6xJXkWYgN/xmH0RoceKf5N76vCG266k+jbjrV
+        TAUjkOumhc23kc4wfQ8keRFi9C3axOx+kuRJZOw8GeckYRK3V/Uy04GvkrwIccCdoE3E8lKSJxKxL4oO
+        OK4+TptYWq9FeRLZgcWxhKJSBAH9lORJjJZQDzZxHT31d+iq7sNGXi7KU4lNzFQkC2W2JY3WEGfJbGjP
+        LWIPaZSHgSxMYt1BNivrZGd/VR+KbAXnJXkaGXtLpURYgAny7MQIAkijO3AG/JN1ksnY2b61Ys5Vt0Sd
+        JkQ1+St0DmBZPpR0UhkXc0TL5XSOMjikq26YphgAfUTUacL6crrghSbegAT3BdbkbUlvnuqj5ekDpkn+
+        jtey9kJDYBoLXSkR9ON4UxONV0rYvYmUe5LrtfYyXmTTxmSsxsw8WrrUY08woMYLSiN2TuqlLBvQ5tUC
+        GzkoXuo5OtiQLT9WwYw8cbzgLNd2WC77+rDl61Po5F0soVxli0TGWDuTdSj1gy0CPSv3o0Wi1A93Y2Ca
+        yvt4PUapX3DEQAYp7yumGKV+yReDT8yQAcr5mjUG0xdzcCfTbGS7Ay+6a8FTkEd50dpJIm3RJm0bN90B
+        f6iB8qBcP/ZIAh8NRtfTbv7cplL5D1+WmiVqPplsAAAAAElFTkSuQmCC
+</value>
+  </data>
+</root>

+ 203 - 0
GatewayTool/FormParity.Designer.cs

@@ -0,0 +1,203 @@
+
+namespace GatewayTool
+{
+    partial class FormParity
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.groupBox1 = new System.Windows.Forms.GroupBox();
+            this.rtHex = new System.Windows.Forms.RichTextBox();
+            this.btnCalc = new System.Windows.Forms.Button();
+            this.txtParity = new System.Windows.Forms.TextBox();
+            this.btnClear = new System.Windows.Forms.Button();
+            this.lbParity = new System.Windows.Forms.Label();
+            this.groupBox2 = new System.Windows.Forms.GroupBox();
+            this.rdoCrc = new System.Windows.Forms.RadioButton();
+            this.rdoXor = new System.Windows.Forms.RadioButton();
+            this.rdoSum = new System.Windows.Forms.RadioButton();
+            this.groupBox1.SuspendLayout();
+            this.groupBox2.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // groupBox1
+            // 
+            this.groupBox1.Controls.Add(this.rtHex);
+            this.groupBox1.Location = new System.Drawing.Point(14, 55);
+            this.groupBox1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.groupBox1.Name = "groupBox1";
+            this.groupBox1.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.groupBox1.Size = new System.Drawing.Size(729, 142);
+            this.groupBox1.TabIndex = 6;
+            this.groupBox1.TabStop = false;
+            this.groupBox1.Text = "待计算的十六进制数据";
+            // 
+            // rtHex
+            // 
+            this.rtHex.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.rtHex.Location = new System.Drawing.Point(3, 23);
+            this.rtHex.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.rtHex.Name = "rtHex";
+            this.rtHex.Size = new System.Drawing.Size(723, 115);
+            this.rtHex.TabIndex = 0;
+            this.rtHex.Text = "";
+            this.rtHex.TextChanged += new System.EventHandler(this.rtHex_TextChanged);
+            this.rtHex.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.rtHex_KeyPress);
+            // 
+            // btnCalc
+            // 
+            this.btnCalc.Enabled = false;
+            this.btnCalc.Location = new System.Drawing.Point(274, 321);
+            this.btnCalc.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.btnCalc.Name = "btnCalc";
+            this.btnCalc.Size = new System.Drawing.Size(94, 36);
+            this.btnCalc.TabIndex = 7;
+            this.btnCalc.Text = "计算";
+            this.btnCalc.UseVisualStyleBackColor = true;
+            this.btnCalc.Click += new System.EventHandler(this.btnCalc_Click);
+            // 
+            // txtParity
+            // 
+            this.txtParity.Location = new System.Drawing.Point(114, 274);
+            this.txtParity.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.txtParity.Name = "txtParity";
+            this.txtParity.ReadOnly = true;
+            this.txtParity.Size = new System.Drawing.Size(625, 26);
+            this.txtParity.TabIndex = 8;
+            // 
+            // btnClear
+            // 
+            this.btnClear.Enabled = false;
+            this.btnClear.Location = new System.Drawing.Point(390, 321);
+            this.btnClear.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.btnClear.Name = "btnClear";
+            this.btnClear.Size = new System.Drawing.Size(94, 36);
+            this.btnClear.TabIndex = 7;
+            this.btnClear.Text = "清空";
+            this.btnClear.UseVisualStyleBackColor = true;
+            this.btnClear.Click += new System.EventHandler(this.btnClear_Click);
+            // 
+            // lbParity
+            // 
+            this.lbParity.AutoSize = true;
+            this.lbParity.Location = new System.Drawing.Point(26, 278);
+            this.lbParity.Name = "lbParity";
+            this.lbParity.Size = new System.Drawing.Size(51, 20);
+            this.lbParity.TabIndex = 9;
+            this.lbParity.Text = "校验码";
+            // 
+            // groupBox2
+            // 
+            this.groupBox2.Controls.Add(this.rdoCrc);
+            this.groupBox2.Controls.Add(this.rdoXor);
+            this.groupBox2.Controls.Add(this.rdoSum);
+            this.groupBox2.Location = new System.Drawing.Point(17, 205);
+            this.groupBox2.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.groupBox2.Name = "groupBox2";
+            this.groupBox2.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.groupBox2.Size = new System.Drawing.Size(722, 56);
+            this.groupBox2.TabIndex = 10;
+            this.groupBox2.TabStop = false;
+            this.groupBox2.Text = "校验类型";
+            // 
+            // rdoCrc
+            // 
+            this.rdoCrc.AutoSize = true;
+            this.rdoCrc.Location = new System.Drawing.Point(383, 20);
+            this.rdoCrc.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.rdoCrc.Name = "rdoCrc";
+            this.rdoCrc.Size = new System.Drawing.Size(166, 24);
+            this.rdoCrc.TabIndex = 0;
+            this.rdoCrc.Text = "CRC-16/Modbus校验";
+            this.rdoCrc.UseVisualStyleBackColor = true;
+            // 
+            // rdoXor
+            // 
+            this.rdoXor.AutoSize = true;
+            this.rdoXor.Location = new System.Drawing.Point(249, 20);
+            this.rdoXor.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.rdoXor.Name = "rdoXor";
+            this.rdoXor.Size = new System.Drawing.Size(83, 24);
+            this.rdoXor.TabIndex = 0;
+            this.rdoXor.Text = "异或校验";
+            this.rdoXor.UseVisualStyleBackColor = true;
+            // 
+            // rdoSum
+            // 
+            this.rdoSum.AutoSize = true;
+            this.rdoSum.Checked = true;
+            this.rdoSum.Location = new System.Drawing.Point(133, 20);
+            this.rdoSum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.rdoSum.Name = "rdoSum";
+            this.rdoSum.Size = new System.Drawing.Size(69, 24);
+            this.rdoSum.TabIndex = 0;
+            this.rdoSum.TabStop = true;
+            this.rdoSum.Text = "和校验";
+            this.rdoSum.UseVisualStyleBackColor = true;
+            // 
+            // FormParity
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(758, 378);
+            this.Controls.Add(this.groupBox2);
+            this.Controls.Add(this.lbParity);
+            this.Controls.Add(this.txtParity);
+            this.Controls.Add(this.btnClear);
+            this.Controls.Add(this.btnCalc);
+            this.Controls.Add(this.groupBox1);
+            this.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.Margin = new System.Windows.Forms.Padding(3, 6, 3, 6);
+            this.Name = "FormParity";
+            this.Text = "FormParity";
+            this.Controls.SetChildIndex(this.groupBox1, 0);
+            this.Controls.SetChildIndex(this.btnCalc, 0);
+            this.Controls.SetChildIndex(this.btnClear, 0);
+            this.Controls.SetChildIndex(this.txtParity, 0);
+            this.Controls.SetChildIndex(this.lbParity, 0);
+            this.Controls.SetChildIndex(this.groupBox2, 0);
+            this.groupBox1.ResumeLayout(false);
+            this.groupBox2.ResumeLayout(false);
+            this.groupBox2.PerformLayout();
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.GroupBox groupBox1;
+        private System.Windows.Forms.RichTextBox rtHex;
+        private System.Windows.Forms.Button btnCalc;
+        private System.Windows.Forms.TextBox txtParity;
+        private System.Windows.Forms.Button btnClear;
+        private System.Windows.Forms.Label lbParity;
+        private System.Windows.Forms.GroupBox groupBox2;
+        private System.Windows.Forms.RadioButton rdoCrc;
+        private System.Windows.Forms.RadioButton rdoXor;
+        private System.Windows.Forms.RadioButton rdoSum;
+    }
+}

+ 99 - 0
GatewayTool/FormParity.cs

@@ -0,0 +1,99 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using WjwLib.API.System;
+using WjwLib.API.Algorithm;
+
+namespace GatewayTool
+{
+    public partial class FormParity : FormBase
+    {
+        private char _splitter = ',';
+
+        //public string TitleText
+        //{
+        //    get => this.Title;
+        //    set => this.Title = value;
+        //}
+
+        public FormParity()
+        {
+            InitializeComponent();
+            this.IsMinimizable = false;
+            this.IsMaximizable = false;
+            this.Title = "校验计算器";
+            this.ShowInTaskbar = false;
+        }
+
+        private void btnClear_Click(object sender, EventArgs e)
+        {
+            rtHex.Clear();
+            txtParity.Clear();
+            rtHex.Focus();
+        }
+
+        private void btnCalc_Click(object sender, EventArgs e)
+        {
+            string data = rtHex.Text.Trim().Replace("\n", "");
+            bool isLegal = Regex.IsMatch(data.Replace(" ", ""), "^[0-9A-Fa-f]+$");
+            if (isLegal)
+            {
+                string hex = ConvertionUtility.FreeHexStringToFormal(data, _splitter);
+                rtHex.Text = hex.Replace(_splitter, ' ');
+                byte[] bytes = ConvertionUtility.HexStringToByteArray(hex, _splitter.ToString());
+                string result = string.Empty;
+                bool res = true;
+                int sum = 0;
+                if (rdoSum.Checked)
+                {
+                    res = Parity.Sum(bytes.ToList(), out sum);
+                    result = sum.ToString("X2");
+                }
+                else if (rdoXor.Checked)
+                {
+                    res = Parity.Xor(bytes.ToList(), out sum);
+                    result = sum.ToString("X2");
+                }
+                else if (rdoCrc.Checked)
+                {
+                    byte[] crc = Parity.CRC16(bytes);
+                    result = string.Format("{0} {1} (低字节在前,高字节在后)", crc[0].ToString("X2"), crc[1].ToString("X2"));
+                }
+                if (res)
+                {
+                    txtParity.Text = result;
+                }
+                else
+                {
+                    MessageBox.Show("计算失败!", "注意", MessageBoxButtons.OK, MessageBoxIcon.Error);
+                }
+            }
+            else
+            {
+                MessageBox.Show("数据包含非十六进制的字符,无法计算!", "注意", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+            }
+        }
+
+        private void rtHex_TextChanged(object sender, EventArgs e)
+        {
+            btnCalc.Enabled = !string.IsNullOrEmpty(rtHex.Text.Trim());
+            btnClear.Enabled = !string.IsNullOrEmpty(rtHex.Text.Trim());
+            if (string.IsNullOrEmpty(rtHex.Text.Trim()))
+            {
+                txtParity.Clear();
+            }
+        }
+
+        private void rtHex_KeyPress(object sender, KeyPressEventArgs e)
+        {
+            e.Handled = "0123456789ABCDEF ".IndexOf(char.ToUpper(e.KeyChar)) < 0;
+        }
+    }
+}

+ 120 - 0
GatewayTool/FormParity.resx

@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 177 - 0
GatewayTool/FormPing.Designer.cs

@@ -0,0 +1,177 @@
+
+namespace GatewayTool
+{
+    partial class FormPing
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.components = new System.ComponentModel.Container();
+            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormPing));
+            this.ipAddressBox1 = new WjwLib.Control.IPAddressBox.IPAddressBox();
+            this.label1 = new System.Windows.Forms.Label();
+            this.btnPing = new System.Windows.Forms.Button();
+            this.rtResult = new System.Windows.Forms.RichTextBox();
+            this.imageList1 = new System.Windows.Forms.ImageList(this.components);
+            this.pictureBox1 = new System.Windows.Forms.PictureBox();
+            this.chkDomain = new System.Windows.Forms.CheckBox();
+            this.txtDomain = new System.Windows.Forms.TextBox();
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
+            this.SuspendLayout();
+            // 
+            // ipAddressBox1
+            // 
+            this.ipAddressBox1.AllowInternalTab = false;
+            this.ipAddressBox1.AutoHeight = true;
+            this.ipAddressBox1.BackColor = System.Drawing.SystemColors.Window;
+            this.ipAddressBox1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
+            this.ipAddressBox1.Cursor = System.Windows.Forms.Cursors.IBeam;
+            this.ipAddressBox1.Location = new System.Drawing.Point(48, 38);
+            this.ipAddressBox1.Margin = new System.Windows.Forms.Padding(4);
+            this.ipAddressBox1.MinimumSize = new System.Drawing.Size(111, 26);
+            this.ipAddressBox1.Name = "ipAddressBox1";
+            this.ipAddressBox1.ReadOnly = false;
+            this.ipAddressBox1.Size = new System.Drawing.Size(160, 26);
+            this.ipAddressBox1.TabIndex = 6;
+            this.ipAddressBox1.Text = "...";
+            // 
+            // label1
+            // 
+            this.label1.AutoSize = true;
+            this.label1.Location = new System.Drawing.Point(8, 41);
+            this.label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+            this.label1.Name = "label1";
+            this.label1.Size = new System.Drawing.Size(22, 20);
+            this.label1.TabIndex = 7;
+            this.label1.Text = "IP";
+            // 
+            // btnPing
+            // 
+            this.btnPing.Location = new System.Drawing.Point(439, 37);
+            this.btnPing.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
+            this.btnPing.Name = "btnPing";
+            this.btnPing.Size = new System.Drawing.Size(66, 28);
+            this.btnPing.TabIndex = 8;
+            this.btnPing.Text = "Ping";
+            this.btnPing.UseVisualStyleBackColor = true;
+            this.btnPing.Click += new System.EventHandler(this.btnPing_Click);
+            // 
+            // rtResult
+            // 
+            this.rtResult.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.rtResult.BackColor = System.Drawing.Color.Black;
+            this.rtResult.ForeColor = System.Drawing.Color.White;
+            this.rtResult.Location = new System.Drawing.Point(10, 71);
+            this.rtResult.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
+            this.rtResult.Name = "rtResult";
+            this.rtResult.ReadOnly = true;
+            this.rtResult.Size = new System.Drawing.Size(739, 302);
+            this.rtResult.TabIndex = 9;
+            this.rtResult.Text = "";
+            // 
+            // imageList1
+            // 
+            this.imageList1.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageList1.ImageStream")));
+            this.imageList1.TransparentColor = System.Drawing.Color.Transparent;
+            this.imageList1.Images.SetKeyName(0, "Green");
+            this.imageList1.Images.SetKeyName(1, "Grey");
+            this.imageList1.Images.SetKeyName(2, "Red");
+            // 
+            // pictureBox1
+            // 
+            this.pictureBox1.Location = new System.Drawing.Point(515, 41);
+            this.pictureBox1.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
+            this.pictureBox1.Name = "pictureBox1";
+            this.pictureBox1.Size = new System.Drawing.Size(19, 20);
+            this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
+            this.pictureBox1.TabIndex = 10;
+            this.pictureBox1.TabStop = false;
+            // 
+            // chkDomain
+            // 
+            this.chkDomain.AutoSize = true;
+            this.chkDomain.Location = new System.Drawing.Point(222, 39);
+            this.chkDomain.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
+            this.chkDomain.Name = "chkDomain";
+            this.chkDomain.Size = new System.Drawing.Size(56, 24);
+            this.chkDomain.TabIndex = 11;
+            this.chkDomain.Text = "域名";
+            this.chkDomain.UseVisualStyleBackColor = true;
+            this.chkDomain.CheckedChanged += new System.EventHandler(this.chkDomain_CheckedChanged);
+            // 
+            // txtDomain
+            // 
+            this.txtDomain.Enabled = false;
+            this.txtDomain.Location = new System.Drawing.Point(300, 38);
+            this.txtDomain.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
+            this.txtDomain.Name = "txtDomain";
+            this.txtDomain.Size = new System.Drawing.Size(129, 26);
+            this.txtDomain.TabIndex = 12;
+            // 
+            // FormPing
+            // 
+            this.AcceptButton = this.btnPing;
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
+            this.ClientSize = new System.Drawing.Size(758, 378);
+            this.Controls.Add(this.txtDomain);
+            this.Controls.Add(this.chkDomain);
+            this.Controls.Add(this.pictureBox1);
+            this.Controls.Add(this.rtResult);
+            this.Controls.Add(this.btnPing);
+            this.Controls.Add(this.label1);
+            this.Controls.Add(this.ipAddressBox1);
+            this.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
+            this.Name = "FormPing";
+            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+            this.Text = "FormPing";
+            this.Controls.SetChildIndex(this.ipAddressBox1, 0);
+            this.Controls.SetChildIndex(this.label1, 0);
+            this.Controls.SetChildIndex(this.btnPing, 0);
+            this.Controls.SetChildIndex(this.rtResult, 0);
+            this.Controls.SetChildIndex(this.pictureBox1, 0);
+            this.Controls.SetChildIndex(this.chkDomain, 0);
+            this.Controls.SetChildIndex(this.txtDomain, 0);
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private WjwLib.Control.IPAddressBox.IPAddressBox ipAddressBox1;
+        private System.Windows.Forms.Label label1;
+        private System.Windows.Forms.Button btnPing;
+        private System.Windows.Forms.RichTextBox rtResult;
+        private System.Windows.Forms.ImageList imageList1;
+        private System.Windows.Forms.PictureBox pictureBox1;
+        private System.Windows.Forms.CheckBox chkDomain;
+        private System.Windows.Forms.TextBox txtDomain;
+    }
+}

+ 117 - 0
GatewayTool/FormPing.cs

@@ -0,0 +1,117 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Net.NetworkInformation;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using WjwLib.API.System;
+
+namespace GatewayTool
+{
+    public partial class FormPing : FormBase
+    {
+        public string DomainText 
+        {
+            get => chkDomain.Text;
+            set => chkDomain.Text = value;
+        }
+
+        public FormPing()
+        {
+            InitializeComponent();
+            this.IsMinimizable = false;
+            this.Title = "Ping";
+            this.ShowInTaskbar = false;
+            this.pictureBox1.Image = imageList1.Images["Grey"];
+        }
+
+        private void ShowInfo(string info)
+        {
+            this.Invoke((EventHandler)delegate
+            {
+                rtResult.AppendText(info);
+                rtResult.AppendText(System.Environment.NewLine);
+                rtResult.ScrollToCaret();
+            });
+        }
+
+        private long TimeSecondDif(DateTime tim)
+        {
+            return (DateTime.Now.Ticks - tim.Ticks) / 10000000;
+        }
+
+        private async void PingAsync(string ip, int sec)
+        {
+            DateTime time = DateTime.Now;
+            string info = string.Empty;
+            int cnt = 0;
+            await Task.Run(() =>
+            {
+                while (true)
+                {
+                    try
+                    {
+                        Thread.Sleep(50);
+                        if (TimeSecondDif(time) > sec)
+                        {
+                            time = DateTime.Now;
+                            DateTime dt = DateTime.Now;
+                            cnt += 1;
+                            PingReply reply = new Ping().Send(ip, 1000);
+                            if (reply.Status == IPStatus.Success)
+                            {
+                                info = $"{DateTime.Now}:来自 {reply.Address} 的回复:字节={reply.Buffer.Length} 时间={reply.RoundtripTime}ms TTL={reply.Options.Ttl}";
+                                this.pictureBox1.Image = imageList1.Images["Green"];
+                            }
+                            else if (reply.Status == IPStatus.DestinationHostUnreachable)
+                            {
+                                info = $"{DateTime.Now}:来自 {NetworkUtility.GetLocalIP()} 的回复:无法访问目标主机。";
+                                this.pictureBox1.Image = imageList1.Images["Red"];
+                            }
+                            else if (reply.Status == IPStatus.TimedOut)
+                            {
+                                info = "请求超时。";
+                                this.pictureBox1.Image = imageList1.Images["Red"];
+                            }
+                            ShowInfo(info);
+                        }
+                    }
+                    catch (Exception e)
+                    {
+                        ShowInfo(e.Message);
+                        this.pictureBox1.Image = imageList1.Images["Red"];
+                    }
+
+                    if (cnt == 4)
+                    {
+                        ShowInfo($"Ping {ip} 结束。");
+                        return;
+                    }
+                }
+            });
+        }
+
+        private void btnPing_Click(object sender, EventArgs e)
+        {
+            this.pictureBox1.Image = imageList1.Images["Grey"];
+            string ip = ipAddressBox1.Text;
+            if (chkDomain.Checked)
+            {
+                ip = txtDomain.Text.Trim();
+            }
+            ShowInfo($"正在 Ping {ip} 具有 32 字节的数据:");
+            PingAsync(ip, 0);
+        }
+
+        private void chkDomain_CheckedChanged(object sender, EventArgs e)
+        {
+            txtDomain.Enabled = chkDomain.Checked;
+            ipAddressBox1.Enabled = !chkDomain.Checked;
+        }
+    }
+}

+ 409 - 0
GatewayTool/FormPing.resx

@@ -0,0 +1,409 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="imageList1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <data name="imageList1.ImageStream" mimetype="application/x-microsoft.net.object.binary.base64">
+    <value>
+        AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
+        LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
+        ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAAu
+        QQAAAk1TRnQBSQFMAgEBAwEAAVABAAFQAQABIAEAASABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAGA
+        AwABIAMAAQEBAAEgBgABQEEAAQEDBwEKAxIBGQMcAScDIAEuAx8BLQMaASQDEAEWAwcBCQMAAQFbAAEB
+        AwcBCgMSARkDHAEnAyABLgMfAS0DGgEkAxABFgMHAQkDAAEBWwABAQMHAQoDEgEZAxwBJwMgAS4DHwEt
+        AxoBJAMQARYDBwEJAwABAdAAAwcBCgMhATADPQFoA1EBnANZAb8DXQHSA18B2QNgAdcDXAHMA1YBtANK
+        AYwDNQFXAxoBJQMGAQhIAAMHAQoDIQEwAz0BaANRAZwDWQG/A10B0gNfAdkDYAHXA1wBzANWAbQDSgGM
+        AzUBVwMaASUDBgEISAADBwEKAyEBMAM9AWgDUQGcA1kBvwNdAdIDXwHZA2AB1wNcAcwDVgG0A0oBjAM1
+        AVcDGgElAwYBCMAAAwUBBwMqAUADUQGcAWYCZQHgAZUBkQGMAfwBnAGoAZcB/wGDAaEBgAH/AWUBnAFk
+        Af8BXgGdAV8B/wFiAaEBYwH/AXMBqgF0Af8BmgG2AZkB/wG6AcMBtwH/AY0CiwH0A1sBywNFAX0DHwEs
+        AwQBBjgAAwUBBwMqAUADUQGcAWYCZQHgAaEBlQGXAfwBvQK3Af8BwgK9Af8BxgLCAf8BygLGAf8BzALJ
+        Af8BzgLKAf8BzwLKAf8BzwLLAf8BjgKLAfQDWwHLA0UBfQMfASwDBAEGOAADBQEHAyoBQANRAZwBZgJl
+        AeABlQGNAZcB/AGcAZsBuAH/AYMBiQG/Af8BZQFzAcQB/wFfAXEByAH/AWYBggHLAf8BhQGRAcsB/wGi
+        AacBywH/Ab8BvQHLAf8BjgKLAfQDWwHLA0UBfQMfASwDBAEGtAADFgEeA0oBiQNqAegBngGiAZcB/wFa
+        AYwBVwH/ASMBcAEjAf8BBAFpAQcB/wEAAWsBAAH/AQABbwEAAf8BAAF0AQAB/wEAAYIBAAH/AQABhwEA
+        Af8BAAGMAQcB/wEUAZYBHQH/AUoBpwFPAf8BnwHAAZ4B/wK3AbUB/ANbAcsDOgFiAw4BEzAAAxYBHgNK
+        AYkBagJpAegBtQKuAf8BugK3Af8BxALCAf8BzQLMAf8B1ALTAf8B2QLYAf8B3QLcAf8B4QLfAf8B4wLh
+        Af8B4wLhAf8B3wLeAf8B2QLWAf8B1gLTAf8BugK5AfwDWwHLAzoBYgMOARMwAAMWAR4DSgGJAmoBaQHo
+        AZ4BmAGuAf8BWgFeAbkB/wEjATMBxgH/AQQBIAHRAf8BAAEcAdgB/wEAASAB3QH/AQABJgHgAf8BAAEu
+        AeMB/wEPATgB5QH/ASEBRAHkAf8BPQFWAeAB/wFpAYEB2AH/Aa0BsAHTAf8BuAG2AbkB/ANbAcsDOgFi
+        Aw4BE6wAAyMBMwNYAbsBqAGkAZ8B/wFZAYkBVQH/AQ8BYwEOAf8BAAFdAQAB/wEAAWEBAAH/AQABZwEA
+        Af8BAAFsAQAB/wEAAXEBAAH/AQABgAEAAf8BAAGFAQAB/wEAAYoBAQH/AQABjwECAf8BAAGXAQYB/wED
+        AaABDQH/ARUBpwEcAf8BTQGzAU8B/wG1Ac0BswH/A38B7wNKAYsDFgEeAwABASQAAyMBMwNYAbsBsQKp
+        Af8BtwGyAbMB/wHDAsIB/wPMAf8D0QH/AdUC1AH/AdgC1wH/AdsC2gH/Ad8C3QH/AeIC4QH/AeYC5AH/
+        AeoC6AH/Ae0C7AH/AfAC7wH/AewC6wH/Ad8C3QH/AdsC2QH/A38B7wNKAYsDFgEeAwABASQAAyMBMwNY
+        AbsBqAGfAakB/wFZAVgBswH/AQ8BGAHFAf8BAAEHAdEB/wEAAQ0B1gH/AQABFQHZAf8BAAEcAdwB/wEA
+        ASMB3gH/AQABKgHhAf8BBgEzAeQB/wEUATwB6AH/ASIBRAHrAf8BLwFMAe4B/wE7AVMB8QH/AUwBXgHs
+        Af8BdQGHAd4B/wHBAcAB2QH/A38B7wNKAYsDFgEeAwABAaAAAyYBOQNdAc8BlwGcAY8B/wEsAW4BKwH/
+        AQABXQEAAf8BAAFcAQAB/wEAAV4BAAH/AQABYwEAAf8BAAFoAQAB/wEAAW0BAAH/AQABcgEAAf8BAAGB
+        AQAB/wEAAYYBAAH/AQABiwEBAf8BAAGQAQMB/wEAAZkBCgH/AQkBpAESAf8BFAGuARoB/wEcAbgBIAH/
+        ATQBuwE2Af8BlgHHAZQB/wKsAasB+gNQAZoDFgEfAwABARwAAyYBOQNdAc8BsAGoAakB/wG8AroB/wPL
+        Af8DzQH/A80B/wHRAtAB/wHVAtQB/wHYAtcB/wHcAtoB/wHfAt4B/wHjAuEB/wHmAuUB/wHqAugB/wHt
+        AuwB/wHxAvAB/wH1AvQB/wH4AvcB/wPtAf8B3ALaAf8BrgKsAfoDUAGaAxYBHwMAAQEcAAMmATkDXQHP
+        AZcBjwGoAf8BLAEqAbgB/wIAAcoB/wEAAQEB0AH/AQABCQHTAf8BAAEQAdYB/wEAARcB2QH/AQABHQHc
+        Af8BAAEkAd8B/wEAASsB4gH/AQgBNAHlAf8BFwE9AegB/wElAUYB6wH/ATMBTwHuAf8BQgFYAfEB/wFP
+        AWAB9QH/AVwBaAH4Af8BbgFzAe0B/wGuAa0B2gH/Aa0CrAH6A1ABmgMWAR8DAAEBmAADHQEpA1wByQGQ
+        AZkBiQH/ARoBagEaAf8BAAFeAQAB/wEAAV0BAAH/AQABXAEAAf8BAAFeAQAB/wEAAWMBAAH/AQABaAEA
+        Af8BAAFtAQAB/wEAAXIBAAH/AQABgQEAAf8BAAGGAQAB/wEAAYsBAQH/AQABkQEDAf8BAAGaAQoB/wEK
+        AaQBEwH/ARUBrwEbAf8BIAG5ASQB/wEpAcQBKwH/ATIBxAEyAf8BggG/AYEB/wGpAagBpwH6A0sBjgMP
+        ARQYAAMdASkDXAHJAbACqAH/AcEBvwHAAf8DzgH/A80B/wPMAf8DzQH/A9EB/wHVAtQB/wHYAtcB/wHc
+        AtoB/wHfAt4B/wHjAuEB/wHnAuUB/wHqAukB/wHuAuwB/wHxAvAB/wP0Af8B+AL3Af8D/AH/AfYC9QH/
+        AdoC1wH/AaoCqQH6A0sBjgMPARQYAAMdASkDXAHJAZABiQGnAf8BGgEYAbwB/wIAAcoB/wIAAc0B/wEA
+        AQIB0AH/AQABCQHTAf8BAAEQAdYB/wEAARcB2QH/AQABHQHcAf8BAAEkAd8B/wEAASwB4gH/AQgBNAHl
+        Af8BFwE9AegB/wElAUYB6wH/ATQBTwHuAf8BQgFYAfEB/wFRAWEB9QH/AV8BagH4Af8BbAFyAfwB/wFy
+        AXUB9QH/AqAB1wH/AakBpwGpAfoDSwGOAw8BFJQAAwsBDwNTAagBmgGcAZIB/wEdAWwBHQH/AQABYQEA
+        Af8BAAFfAQAB/wEAAV0BAAH/AQABXAEAAf8BAAFeAQAB/wEAAWMBAAH/AQABaAEAAf8BAAFtAQAB/wEA
+        AXIBAAH/AQABgQEAAf8BAAGFAQAB/wEAAYoBAQH/AQABkAEDAf8BAAGYAQgB/wEIAaIBEQH/ARIBrAEZ
+        Af8BGwG1ASAB/wEjAb0BJwH/ASYBwQEpAf8BJQG4ASgB/wGEAbgBgwH/A4YB8QM9AWgDBQEHEAADCwEP
+        A1MBqAGuAqYB/wHBAr8B/wPRAf8BzwLOAf8DzQH/A8wB/wPNAf8B0QLQAf8D1AH/AdgC1wH/AdwC2gH/
+        Ad8C3QH/AeMC4QH/AeYC5AH/AeoC6AH/Ae0C7AH/AfAC7wH/AfQC8wH/AfcC9gH/A/kB/wP7Af8D8gH/
+        AdMC0AH/AYYChQHxAz0BaAMFAQcQAAMLAQ8DUwGoAZoBkQGlAf8BHQEbAbsB/wIAAcsB/wIAAcoB/wIA
+        AcwB/wEAAQIBzwH/AQABCQHSAf8BAAEQAdUB/wEAARYB2QH/AQABHQHcAf8BAAEkAd8B/wEAASsB4gH/
+        AQcBMwHlAf8BFQE8AegB/wEjAUUB6wH/ATEBTgHuAf8BPwFWAfEB/wFNAV8B9AH/AVkBZgH2Af8BZAFt
+        AfkB/wFoAXAB+wH/AWIBawHyAf8BmwGdAdAB/wKGAYUB8QM9AWgDBQEHkAADPAFmAYwBiwGKAfkBNgGA
+        ATUB/wEAAWUBAAH/AQABYgEAAf8BAAFfAQAB/wEAAV0BAAH/AQABXAEAAf8BAAFdAQAB/wEAAWIBAAH/
+        AQABZwEAAf8BAAFsAQAB/wEAAXEBAAH/AQABgAEAAf8BAAGEAQAB/wEAAYkBAQH/AQABjgECAf8BAAGU
+        AQYB/wEDAZ4BDQH/AQwBpwEUAf8BFAGuARsB/wEaAbQBHwH/ARwBtgEhAf8BGAG0AR4B/wEeAakBJAH/
+        AZ4BuQGcAf8DXgHRAyIBMhAAAzwBZgGNAosB+QG8ArgB/wPTAf8B0QLQAf8BzwLOAf8DzQH/A8wB/wPN
+        Af8D0AH/AdQC0wH/AdcC1gH/AdsC2QH/Ad4C3QH/AeIC4AH/AeUC4wH/AekC5wH/AewC6gH/Ae8C7gH/
+        AfIC8QH/A/QB/wH2AvUB/wH3AvYB/wH3AvYB/wHoAucB/wHNAsgB/wNeAdEDIgEyEAADPAFmAYwBigGL
+        AfkBNgEzAbQB/wIAAcwB/wIAAcsB/wIAAcoB/wIAAcwB/wEAAQEBzwH/AQABCAHSAf8BAAEPAdUB/wEA
+        ARUB2AH/AQABHAHbAf8BAAEiAd4B/wEAASkB4QH/AQMBMQHkAf8BEQE6AecB/wEfAUIB6gH/ASwBSwHt
+        Af8BOQFTAfAB/wFFAVoB8gH/AVABYAH0Af8BVwFlAfYB/wFaAWcB9wH/AVYBZQH3Af8BUwFiAegB/wGq
+        AasByQH/A14B0QMiATKMAAMUARwDXgHOAWoBkAFlAf8BAAFqAQAB/wEAAWYBAAH/AQABYwEAAf8BAAFg
+        AQAB/wEAAV4BAAH/AQABXAEAAf8BAAFdAQAB/wEAAWEBAAH/AQABZgEAAf8BAAFqAQAB/wEAAW8BAAH/
+        AQABdAEAAf8BAAGDAQAB/wEAAYcBAAH/AQABjAEBAf8BAAGQAQMB/wEAAZcBCAH/AQUBnwEOAf8BCwGl
+        ARQB/wEPAaoBFwH/AREBqwEYAf8BDwGqARcB/wEIAaUBEQH/ATIBnQE3Af8BqwGsAaYB/gNJAYgDCAEL
+        CAADFAEcA14BzgGzAqwB/wHSAtEB/wPUAf8D0QH/A88B/wPNAf8DzAH/A8wB/wPPAf8B0wLSAf8B1gLV
+        Af8B2gLZAf8B3QLcAf8B4QLfAf8B5ALiAf8B5wLlAf8B6gLpAf8B7QLsAf8B7wLuAf8B8QLwAf8B8wLy
+        Af8B8wLyAf8B8wLyAf8B8gLxAf8B1gLTAf8BtAKvAf4DSQGIAwgBCwgAAxQBHANeAc4BagFjAaoB/wIA
+        AcoB/wIAAc0B/wIAAcsB/wIAAcoB/wIAAcwB/wIAAc8B/wEAAQcB0QH/AQABDQHUAf8BAAEUAdcB/wEA
+        ARoB2gH/AQABIAHdAf8BAAEnAeAB/wEAAS4B4wH/AQwBNwHmAf8BGQE/AekB/wElAUYB6wH/ATEBTQHu
+        Af8BOwFUAfAB/wFEAVkB8gH/AUoBXQHzAf8BTAFeAfQB/wFJAV0B8wH/AUIBWAHzAf8BVwFmAdUB/wGt
+        AakBrwH+A0kBiAMIAQuIAAM7AWQBiQGGAYEB/AEgAXUBIwH/AQABawEAAf8BAAFnAQAB/wEAAWMBAAH/
+        AQABYAEAAf8BAAFeAQAB/wEAAV0BAAH/AQABXAEAAf8BAAFfAQAB/wEAAWQBAAH/AQABaQEAAf8BAAFt
+        AQAB/wEAAXIBAAH/AQABgAEAAf8BAAGFAQAB/wEAAYkBAQH/AQABjQECAf8BAAGRAQQB/wEAAZcBCAH/
+        AQEBnAEMAf8BBQGfAQ8B/wEGAaEBEAH/AQUBnwEOAf8BAQGcAQsB/wEAAZQBCQH/AXQBpQF0Af8BYQFf
+        AWEB1QMfAS0IAAM7AWQBlQKKAfwBxgLDAf8B2QLYAf8B1QLUAf8B0gLRAf8DzwH/A84B/wHNAswB/wPM
+        Af8DzgH/AdIC0QH/AdUC1AH/AdkC1wH/AdwC2gH/Ad8C3QH/AeIC4AH/AeUC4wH/AegC5gH/AeoC6QH/
+        Ae0C6wH/Ae4C7QH/Ae8C7gH/AfAC7wH/Ae8C7gH/Ae8C7QH/AecC5gH/AcUCwAH/AWECXwHVAx8BLQgA
+        AzsBZAGJAYEBigH8ASABHgG+Af8CAAHRAf8CAAHNAf8CAAHLAf8CAAHKAf8CAAHLAf8CAAHOAf8BAAEF
+        AdEB/wEAAQsB1AH/AQABEgHWAf8BAAEYAdkB/wEAAR4B3AH/AQABJAHfAf8BAAErAeIB/wEFATIB5AH/
+        AREBOgHnAf8BHAFBAekB/wEnAUcB7AH/ATABTQHuAf8BNwFRAe8B/wE8AVQB8AH/AT0BVQHwAf8BOwFU
+        AfAB/wE2AVEB7wH/ATABTAHoAf8BjQGSAcEB/wJhAV8B1QMfAS2EAAMIAQsDVQGwAYoBnQGFAf8BAwF1
+        AQoB/wEAAW0BAAH/AQABaAEAAf8BAAFkAQAB/wEAAWEBAAH/AQABXwEAAf8BAAFdAQAB/wEAAVwBAAH/
+        AQABXgEAAf8BAAFiAQAB/wEAAWcBAAH/AQABawEAAf8BAAFwAQAB/wEAAXQBAAH/AQABggEAAf8BAAGG
+        AQAB/wEAAYkBAQH/AQABjQECAf8BAAGQAQMB/wEAAZMBBQH/AQABlQEGAf8BAAGWAQcB/wEAAZUBBgH/
+        AQABkgEFAf8BAAGPAQAB/wEsAY8BMQH/AZQBkgGQAfkDPAFmAwIBAwMIAQsDVQGwAbgCsQH/AdkC1wH/
+        AdkC2AH/A9UB/wPSAf8D0AH/A84B/wPNAf8DzAH/A80B/wPQAf8B1ALTAf8B1wLWAf8B2gLZAf8B3QLc
+        Af8B4ALeAf8B4wLhAf8B5QLkAf8B6ALmAf8B6gLoAf8B6wLqAf8B7ALrAf8B7ALrAf8B7ALrAf8B6wLq
+        Af8B6gLpAf8BzQLKAf8BmAKSAfkDPAFmAwIBAwMIAQsDVQGwAYoBgwGwAf8CAwHQAf8CAAHRAf8CAAHO
+        Af8CAAHMAf8CAAHKAf8CAAHKAf8CAAHNAf8BAAEDAdAB/wEAAQkB0gH/AQABDwHVAf8BAAEVAdgB/wEA
+        ARsB2wH/AQABIQHdAf8BAAEnAeAB/wEAAS0B4gH/AQgBNAHlAf8BEgE6AecB/wEcAUAB6QH/ASMBRQHr
+        Af8BKQFJAewB/wEtAUsB7QH/AS8BTAHtAf8BLQFLAe0B/wEpAUkB7AH/ASEBQwHsAf8BRQFaAcwB/wGU
+        ApIB+QM8AWYDAgEDgAADHwEtAWYCZQHhAWsBoQFrAf8BJwGXAS0B/wEAAXEBAQH/AQABaQEAAf8BAAFm
+        AQAB/wEAAWIBAAH/AQABYAEAAf8BAAFeAQAB/wEAAVwBAAH/AQABXQEAAf8BAAFgAQAB/wEAAWQBAAH/
+        AQABaQEAAf8BAAFtAQAB/wEAAXEBAAH/AQABdQEAAf8BAAGCAQAB/wEAAYYBAAH/AQABiQEAAf8BAAGL
+        AQEB/wEAAY0BAgH/AQABjgECAf8BAAGOAQIB/wEAAY4BAgH/AQABjQECAf8BAAGLAQAB/wEEAYYBDQH/
+        AZwBqAGXAf8DUAGeAwoBDgMfAS0BZgJlAeEByAHDAcQB/wHlAuQB/wHbAtoB/wHWAtUB/wPTAf8B0QLQ
+        Af8DzwH/A80B/wPMAf8DzAH/A88B/wPSAf8B1QLUAf8B2ALXAf8B2wLaAf8B3gLcAf8B4ALfAf8B4wLh
+        Af8B5QLjAf8B5gLlAf8B6ALmAf8B6QLnAf8B6QLnAf8B6ALnAf8B6ALmAf8B5wLlAf8B2QLWAf8BuwK1
+        Af8DUAGeAwoBDgMfAS0BZgJlAeEBawFoAcEB/wInAd4B/wIAAdIB/wIAAc4B/wIAAcwB/wIAAcsB/wIA
+        AcoB/wIAAcwB/wIAAc8B/wEAAQYB0QH/AQABDAHUAf8BAAESAdcB/wEAARgB2QH/AQABHQHcAf8BAAEj
+        Ad4B/wEAASgB4AH/AQABLgHjAf8BBwE0AeUB/wEQATkB5wH/ARcBPQHoAf8BHAFAAekB/wEfAUIB6gH/
+        ASABQwHqAf8BHwFCAeoB/wEbAUAB6QH/ARUBPQHpAf8BGwE/AdoB/wGfAZ4BtQH/A1ABngMKAQ6AAAM1
+        AVcBjAGKAYkB9wFVAaMBWQH/AT8BpQFEAf8BJgGTASsB/wEAAW8BAQH/AQABZgEAAf8BAAFjAQAB/wEA
+        AWEBAAH/AQABXgEAAf8BAAFdAQAB/wEAAVwBAAH/AQABXgEAAf8BAAFiAQAB/wEAAWYBAAH/AQABagEA
+        Af8BAAFuAQAB/wEAAXEBAAH/AQABdQEAAf8BAAGCAQAB/wEAAYQBAAH/AQABhgEAAf8BAAGIAQAB/wEA
+        AYkBAQH/AQABiQEBAf8BAAGJAQEB/wEAAYgBAAH/AQABhgEAAf8BAAGCAQAB/wFsAZgBaQH/A1oBxQMX
+        ASADNQFXAY4CiwH3AdUC0gH/AeoC6QH/AeIC4QH/AdkC2AH/A9QB/wHSAtEB/wHQAs8B/wPOAf8DzQH/
+        A8wB/wPNAf8D0AH/AdMC0gH/AdYC1QH/AdkC2AH/AdsC2gH/Ad4C3AH/AeAC3gH/AeIC4AH/AeMC4QH/
+        AeQC4wH/AeUC4wH/AeUC5AH/AeUC4wH/AeQC4gH/AeMC4QH/Ad4C3AH/AbsCtAH/A1oBxQMXASADNQFX
+        AYwBiQGLAfcBVQFUAc8B/wI/AeMB/wImAdsB/wIAAdEB/wIAAc0B/wIAAcsB/wIAAcoB/wIAAcsB/wIA
+        Ac0B/wEAAQMB0AH/AQABCQHSAf8BAAEOAdUB/wEAARQB2AH/AQABGQHaAf8BAAEeAdwB/wEAASMB3gH/
+        AQABKAHgAf8BAAEsAeIB/wEDATEB5AH/AQkBNQHlAf8BDgE4AeYB/wERAToB5wH/ARIBOgHnAf8BEQE5
+        AecB/wEOATgB5gH/AQkBNQHlAf8BAwEwAeAB/wFvAYEBtgH/A1oBxQMXASCAAANEAXsBmAGbAZIB/gFI
+        AaUBTgH/AT0BpgFDAf8BPwGiAUQB/wEpAZIBLgH/AQEBbwEEAf8BAAFkAQAB/wEAAWIBAAH/AQABXwEA
+        Af8BAAFeAQAB/wEAAVwBAAH/AQABXAEAAf8BAAFfAQAB/wEAAWMBAAH/AQABZwEAAf8BAAFqAQAB/wEA
+        AW0BAAH/AQABcQEAAf8BAAFzAQAB/wEAAYABAAH/AQABggEAAf8BAAGDAQAB/wEAAYQBAAH/AQABhAEA
+        Af8BAAGEAQAB/wEAAYMBAAH/AQABggEAAf8BAAF0AQAB/wFOAYwBTQH/A2EB2wMjATMDRAF7AagCoAH+
+        Ad0C2wH/AeoC6QH/AecC5gH/AeEC4AH/AdgC1wH/AdMC0gH/AdEC0AH/Ac8CzgH/A80B/wPMAf8DzAH/
+        A84B/wHRAtAB/wHUAtMB/wHWAtUB/wHZAtcB/wHbAtkB/wHdAtsB/wHeAt0B/wHgAt4B/wHhAt8B/wHi
+        AuAB/wHiAuAB/wHiAuAB/wHhAt8B/wHgAt4B/wHeAtwB/wG8ArcB/wNhAdsDIwEzA0QBewGYAZEBoAH+
+        AkgB2QH/Aj0B5AH/Aj8B4AH/AikB2gH/AgEB0AH/AgABzAH/AgABywH/AgABygH/AgABzAH/AgABzgH/
+        AQABBQHRAf8BAAEKAdMB/wEAARAB1gH/AQABFQHYAf8BAAEaAdoB/wEAAR4B3AH/AQABIgHeAf8BAAEm
+        AeAB/wEAASkB4QH/AQABLQHiAf8BAAEvAeMB/wECATEB5AH/AQMBMQHkAf8BAgExAeQB/wEAAS8B4wH/
+        AQABLAHiAf8BAAEnAeEB/wFOAWABuQH/A2EB2wMjATOAAANNAZEBpgGwAaIB/wFCAacBSQH/ATsBpgFC
+        Af8BPQGiAUIB/wE/AaABQwH/ATEBlQE1Af8BCwFyAQ4B/wEAAWQBAAH/AQABYAEAAf8BAAFfAQAB/wEA
+        AV0BAAH/AQABXAEAAf8BAAFdAQAB/wEAAV8BAAH/AQABYwEAAf8BAAFmAQAB/wEAAWoBAAH/AQABbAEA
+        Af8BAAFvAQAB/wEAAXEBAAH/AQABcwEAAf8BAAF0AQAB/wEAAXUBAAH/AQABdQEAAf8BAAF1AQAB/wEA
+        AXQBAAH/AQABcwEAAf8BAAFwAQAB/wE+AYUBPQH/A2oB5gMqAUADTQGRAb8CuAH/AeEC3wH/AeoC6QH/
+        AegC5wH/AeUC5AH/AeEC4AH/AdgC1wH/AdIC0QH/AdACzwH/A84B/wPNAf8DzAH/A8wB/wPOAf8D0QH/
+        A9MB/wHWAtUB/wHYAtcB/wHaAtgB/wHbAtoB/wHcAtsB/wHdAtwB/wHeAtwB/wHeAtwB/wHeAtwB/wHd
+        AtwB/wHcAtsB/wHcAtoB/wG9ArkB/wNqAeYDKgFAA00BkQGmAaABuQH/AUIBQQHeAf8COwHlAf8CPQHh
+        Af8CPwHfAf8CMQHaAf8CCwHRAf8CAAHLAf8CAAHKAf8CAAHKAf8CAAHNAf8BAAEBAc8B/wEAAQYB0QH/
+        AQABCwHUAf8BAAEQAdYB/wEAARUB2AH/AQABGQHaAf8BAAEdAdsB/wEAASAB3QH/AQABIwHeAf8BAAEl
+        Ad8B/wEAAScB4AH/AQABKAHhAf8BAAEpAeEB/wEAASgB4QH/AQABJwHgAf8BAAElAd8B/wEAASEB3wH/
+        AT4BUQG7Af8DagHmAyoBQIAAA08BlwGnAbMBowH/AT8BqQFIAf8BOQGoAUAB/wE6AaMBQQH/ATwBoAFA
+        Af8BPgGdAUIB/wE5AZgBPAH/AR4BhgEgAf8BAAFoAQEB/wEAAWABAAH/AQABXgEAAf8BAAFdAQAB/wEA
+        AVwBAAH/AQABXQEAAf8BAAFfAQAB/wEAAWIBAAH/AQABZQEAAf8BAAFoAQAB/wEAAWoBAAH/AQABbAEA
+        Af8BAAFuAQAB/wEAAW8BAAH/AQABcAEAAf8BAAFwAQAB/wEAAXABAAH/AQABbwEAAf8BAAFuAQAB/wEA
+        AWsBAAH/ATkBggE5Af8BagJpAegDLAFDA08BlwHCArwB/wHiAuAB/wHqAukB/wHpAugB/wHmAuUB/wHk
+        AuMB/wHhAuAB/wHaAtkB/wHTAtIB/wPPAf8BzgLNAf8DzQH/A8wB/wPMAf8DzgH/AdEC0AH/AdMC0gH/
+        AdUC1AH/AdYC1QH/AdgC1wH/AdkC2AH/AdoC2QH/AdoC2QH/AdsC2QH/AdoC2QH/AdoC2QH/AdkC2AH/
+        AdgC1wH/Ab0BuAG5Af8BagJpAegDLAFDA08BlwGnAaIBvAH/Aj8B4QH/AjkB5gH/AjoB4gH/AjwB4AH/
+        Aj4B3gH/AjkB2wH/Ah4B1AH/AgABzQH/AgABygH/AgABywH/AgABzQH/AQABAgHPAf8BAAEHAdEB/wEA
+        AQsB0wH/AQABDwHVAf8BAAETAdcB/wEAARcB2QH/AQABGgHaAf8BAAEdAdsB/wEAAR8B3AH/AQABIAHd
+        Af8BAAEhAd4B/wEAASIB3gH/AQABIQHeAf8BAAEgAd0B/wEAAR8B3AH/AQABGgHcAf8BOQFKAbsB/wFq
+        AmkB6AMsAUOAAANKAYwBrgG3AaoB/wFBAaoBSQH/ATYBqQE/Af8BOAGlAT8B/wE5AaEBPwH/ATsBngE/
+        Af8BPAGbAUAB/wE+AZkBQAH/ATMBkQE0Af8BFwF1ARgB/wEAAWUBAAH/AQABXgEAAf8BAAFcAQAB/wEA
+        AVwBAAH/AQABXAEAAf8BAAFeAQAB/wEAAWEBAAH/AQABZAEAAf8BAAFmAQAB/wEAAWgBAAH/AQABaQEA
+        Af8BAAFqAQAB/wEAAWsBAAH/AQABawEAAf8BAAFrAQAB/wEAAWoBAAH/AQABaAEAAf8BAAFmAQAB/wFD
+        AYQBQQH/A2UB4wMnATsDSgGMAcQCvgH/AeAC3wH/AeoC6QH/AeoC6AH/AegC5wH/AeUC5AH/AeMC4gH/
+        AeEC4AH/A90B/wPWAf8D0AH/Ac4CzQH/A80B/wPMAf8DzAH/A84B/wHQAs8B/wHSAtEB/wHTAtIB/wPU
+        Af8D1QH/AdYC1QH/AdcC1gH/AdcC1gH/AdcC1gH/AdYC1QH/AdUC1AH/A9QB/wG7ArcB/wNlAeMDJwE7
+        A0oBjAGuAagBvwH/AUEBQAHhAf8CNgHoAf8COAHkAf8COQHhAf8COwHeAf8CPAHdAf8CPgHbAf8CMwHY
+        Af8CFwHRAf8CAAHMAf8CAAHLAf8CAAHNAf8BAAEBAc8B/wEAAQYB0QH/AQABCgHTAf8BAAEOAdUB/wEA
+        AREB1gH/AQABFAHXAf8BAAEWAdkB/wEAARgB2QH/AQABGgHaAf8BAAEbAdsB/wEAARsB2wH/AQABGwHb
+        Af8BAAEZAdoB/wEAARcB2QH/AQABEwHZAf8BQwFPAboB/wNlAeMDJwE7gAADQAFxAa8BsQGqAf0BSAGr
+        AVAB/wE0AaoBPQH/ATYBpgE+Af8BNwGiAT4B/wE4AZ8BPQH/AToBnAE+Af8BOwGaAT4B/wE9AZgBPwH/
+        AT0BlgE/Af8BNAGPATUB/wEfAYIBIAH/AQoBagELAf8BAAFhAQAB/wEAAV0BAAH/AQABWwEAAf8BAAFc
+        AQAB/wEAAV8BAAH/AQABYQEAAf8BAAFiAQAB/wEAAWQBAAH/AQABZQEAAf8BAAFlAQAB/wEAAWUBAAH/
+        AQABZgEAAf8BAAFoAQAB/wEJAW8BCwH/ASIBhwEkAf8BgAGeAXIB/wNfAdUDHQEqA0ABcQG3ArUB/QHd
+        AtsB/wHqAukB/wHpAugB/wHpAugB/wHnAuUB/wHkAuMB/wHiAuEB/wPgAf8B3wLeAf8D2wH/AdcC1gH/
+        A9EB/wPOAf8DzAH/A8wB/wPNAf8DzgH/AdACzwH/AdEC0AH/AdIC0QH/A9IB/wHTAtIB/wHTAtIB/wHT
+        AtIB/wHUAtMB/wPVAf8D2QH/AcMCvgH/A18B1QMdASoDQAFxAa8BqQG1Af0CSAHdAf8CNAHpAf8CNgHl
+        Af8CNwHiAf8COAHgAf8COgHeAf8COwHcAf8CPQHbAf8CPQHaAf8CNAHXAf8CHwHTAf8BCgEMAc8B/wEA
+        AQMBzgH/AQABAQHPAf8BAAEDAdAB/wEAAQcB0gH/AQABCgHTAf8BAAENAdUB/wEAAQ8B1gH/AQABEQHW
+        Af8BAAESAdcB/wEAARMB1wH/AQABEwHXAf8BAAEUAdcB/wEAARgB2AH/AQkBIwHaAf8BIgE4Ad0B/wGA
+        AYMBwAH/A18B1QMdASqAAAMvAUoBhwKEAfIBWwGtAWAB/wExAasBOwH/ATQBqAE9Af8BNQGkAT0B/wE2
+        AaEBPAH/ATcBngE8Af8BOQGbAT0B/wE6AZkBPQH/ATsBlwE9Af8BPQGWAT4B/wE+AZYBPwH/ATwBkwE9
+        Af8BMwGMATQB/wEmAYQBJgH/ARkBcQEZAf8BDwFrAQ8B/wEIAWcBCAH/AQQBZgEFAf8BAwFmAQQB/wEE
+        AWgBBQH/AQgBawEJAf8BDwFvARAB/wEaAYABGwH/ASkBiQErAf8BOwGTATwB/wFJAZoBSgH/AVUBnwFW
+        Af8BnQGnAZcB/wNYAbkDEAEVAy8BSgGIAoQB8gHYAtUB/wHqAugB/wHpAugB/wHpAugB/wHoAucB/wHm
+        AuUB/wPjAf8D4QH/AeAC3wH/Ad8C3gH/Ad4C3QH/A9wB/wHaAtkB/wHXAtYB/wPTAf8D0QH/A9AB/wPQ
+        Af8D0AH/A9EB/wHTAtIB/wPUAf8D1gH/A9kB/wPcAf8D3gH/A94B/wG+ArgB/wNYAbkDEAEVAy8BSgGH
+        AoQB8gFbAVoB1wH/AjEB6gH/AjQB5wH/AjUB5AH/AjYB4QH/AjcB3wH/AjkB3QH/AjoB2wH/AjsB2gH/
+        Aj0B2gH/Aj4B2QH/AjwB2AH/ATMBNAHXAf8BJgEqAdYB/wEZASAB1QH/AQ8BGgHUAf8BCAEWAdQB/wEE
+        ARUB1QH/AQMBFgHVAf8BBAEYAdYB/wEIAR0B1wH/AQ8BJAHZAf8BGgEuAdsB/wEpATwB3QH/ATsBSwHg
+        Af8BSQFYAeIB/wFVAWEB4QH/AZ0BmQG5Af8DWAG5AxABFYAAAxcBIANgAdcBhwG1AYkB/wEwAaoBOgH/
+        ATIBqgE7Af8BMwGnATsB/wE0AaMBOwH/ATUBnwE7Af8BNgGdATsB/wE4AZoBPAH/ATkBmAE8Af8BOgGX
+        ATwB/wE7AZUBPQH/AT0BlQE+Af8BPgGUAT8B/wFAAZQBQQH/AUEBlAFBAf8BQAGTAUAB/wE/AZIBPwH/
+        AT4BkQE+Af8BPgGRAT4B/wFAAZIBQAH/AUMBlQFDAf8BRwGXAUcB/wFKAZkBSgH/AUwBmgFMAf8BTQGb
+        AU0B/wFNAZoBTQH/AV8BnwFeAf8BqAGlAaAB/wNKAYkDBAEGAxcBIANgAdcB0gLPAf8B6ALmAf8B6QLn
+        Af8B6QLnAf8B6QLoAf8B6ALmAf8B5QLkAf8B4wLiAf8B4QLgAf8B4ALfAf8D3gH/Ad4C3QH/A90B/wHd
+        AtwB/wPcAf8D3AH/A9sB/wPbAf8D2wH/A9sB/wPbAf8D3AH/A90B/wPeAf8D3gH/A94B/wHXAtYB/wG0
+        AqwB/wNKAYkDBAEGAxcBIANgAdcBhwGFAdAB/wIwAekB/wIyAekB/wIzAeYB/wI0AeMB/wI1AeAB/wI2
+        Ad4B/wI4Ad0B/wI5AdsB/wI6AdoB/wI7AdoB/wI9AdkB/wI+AdkB/wFAAUEB2gH/AUEBQwHaAf8BQAFE
+        AdsB/wE/AUUB3AH/AT4BRgHdAf8BPgFHAd0B/wFAAUoB3gH/AUMBTgHfAf8BRwFSAeAB/wFKAVUB4QH/
+        AUwBVwHhAf8BTQFYAeEB/wFNAVYB4QH/AV8BZQHYAf8BqAGgAawB/wNKAYkDBAEGgAADAgEDA1EBnAGz
+        AcIBsQH/ATsBqQFEAf8BLwGqATkB/wExAakBOgH/ATIBpQE6Af8BMwGiAToB/wE0AZ8BOgH/ATUBnAE6
+        Af8BNwGaATsB/wE4AZgBOwH/ATkBlgE7Af8BOgGVATwB/wE7AZUBPQH/AT0BlAE+Af8BPgGUAT8B/wE/
+        AZQBQAH/AUABlAFBAf8BQgGUAUIB/wFDAZUBQwH/AUQBlQFEAf8BRQGWAUUB/wFGAZcBRgH/AUcBlwFH
+        Af8BSQGYAUkB/wFKAZkBSgH/AUoBmgFKAf8BggGjAXUB/wF7AnkB8gMwAUsEAAMCAQMDUQGcAdACzQH/
+        AeEC4AH/AekC5wH/AekC5wH/AekC5wH/AekC5wH/AecC5gH/AeUC5AH/AeMC4gH/AeEC4AH/AeAC3wH/
+        Ad8C3gH/Ad4C3QH/A90B/wHdAtwB/wPcAf8D3AH/A9wB/wPcAf8D3AH/A9wB/wPcAf8B3QLcAf8D3QH/
+        A90B/wPeAf8BywLIAf8BewJ5AfIDMAFLBAADAgEDA1EBnAGzAa8BzQH/AjsB4gH/Ai8B6QH/AjEB6AH/
+        AjIB5QH/AjMB4gH/AjQB4AH/AjUB3gH/AjcB3AH/AjgB2wH/AjkB2gH/AjoB2gH/AjsB2QH/Aj0B2QH/
+        Aj4B2QH/AT8BQAHZAf8BQAFCAdoB/wFCAUUB2wH/AUMBSAHcAf8BRAFKAd0B/wFFAUwB3QH/AUYBTQHe
+        Af8BRwFOAd4B/wFJAU8B3gH/AUoBUAHeAf8BSgFQAd8B/wGCAYEByAH/AXsCeQHyAzABS4gAAy8BSgGS
+        AY4BjQH1AWcBsgFrAf8BLAGoATYB/wEuAakBOAH/ATABqAE5Af8BMQGkATkB/wEyAaEBOQH/ATMBngE5
+        Af8BNAGcATkB/wE2AZkBOgH/ATcBmAE6Af8BOAGWATsB/wE5AZYBOwH/AToBlQE8Af8BPAGUAT0B/wE9
+        AZQBPgH/AT4BlAE/Af8BPwGUAUAB/wFAAZUBQQH/AUIBlQFCAf8BQwGWAUMB/wFEAZYBRQH/AUUBlwFG
+        Af8BRgGXAUcB/wFHAZgBSAH/AVQBnQFUAf8BnwGlAZgB/wNZAb4DEQEXCAADLwFKAZICjgH1AdgC1gH/
+        AegC5wH/AegC5wH/AekC5wH/AekC5wH/AekC5wH/AecC5gH/AeUC5AH/AeMC4gH/AeIC4QH/A+AB/wPf
+        Af8D3gH/Ad4C3QH/A90B/wPdAf8B3QLcAf8D3AH/A9wB/wPcAf8B3QLcAf8D3QH/A90B/wPeAf8B2wLa
+        Af8BuQKyAf8DWQG+AxEBFwgAAy8BSgGSAY0BjgH1AWcBZgHXAf8CLAHpAf8CLgHpAf8CMAHoAf8CMQHl
+        Af8CMgHiAf8CMwHgAf8CNAHeAf8CNgHdAf8CNwHbAf8COAHaAf8COQHaAf8COgHZAf8CPAHZAf8CPQHZ
+        Af8CPgHZAf8CPwHZAf8BQAFBAdoB/wFCAUMB2gH/AUMBRQHbAf8BRAFGAdsB/wFFAUcB3AH/AUYBSQHc
+        Af8BRwFJAdwB/wFUAVUB2QH/AZ8BmAGyAf8DWQG+AxEBF4gAAwkBDANWAbMBtwHJAbYB/wE8AakBRAH/
+        ASsBqAE1Af8BLQGpATcB/wEvAacBOAH/ATABpAE4Af8BMQGhATgB/wEyAZ4BOAH/ATMBnAE5Af8BNQGa
+        ATkB/wE2AZgBOgH/ATcBlwE6Af8BOAGWATsB/wE5AZYBOwH/ATsBlQE8Af8BPAGVAT0B/wE9AZUBPgH/
+        AT4BlQE/Af8BPwGVAUAB/wFBAZYBQgH/AUIBlgFDAf8BQwGXAUQB/wFEAZgBRQH/AUYBmQFHAf8BgQGk
+        AXUB/wGOAogB+AM6AWIDAAEBCAADCQEMA1YBswHXAtQB/wHhAuAB/wHoAucB/wHoAucB/wHoAucB/wHp
+        AucB/wHpAucB/wHoAuYB/wHmAuQB/wHkAuMB/wHiAuEB/wHhAuAB/wHgAt8B/wPfAf8D3gH/A94B/wHe
+        At0B/wPdAf8D3QH/A90B/wPdAf8D3QH/A94B/wPfAf8BywLIAf8BjgKIAfgDOgFiAwABAQgAAwkBDANW
+        AbMBtwG0AdUB/wI8AeIB/wIrAeoB/wItAekB/wIvAegB/wIwAeUB/wIxAeIB/wIyAeAB/wIzAd4B/wI1
+        Ad0B/wI2AdwB/wI3AdsB/wI4AdoB/wI5AdoB/wI7AdkB/wI8AdkB/wI9AdkB/wI+AdkB/wI/AdkB/wJB
+        AdoB/wJCAdoB/wJDAdoB/wJEAdoB/wJGAdsB/wGBAXQBxgH/AY4CiAH4AzoBYgMAAQGMAAMsAUMDfgHt
+        AY8BvgGSAf8BLgGnATgB/wErAagBNQH/ASwBqAE2Af8BLgGnATcB/wEvAaQBNwH/ATABoQE4Af8BMQGf
+        ATgB/wEyAZwBOAH/ATQBmwE4Af8BNQGZATkB/wE2AZgBOgH/ATcBlwE6Af8BOAGWATsB/wE6AZYBPAH/
+        ATsBlgE9Af8BPAGWAT4B/wE9AZYBPwH/AT4BlwFAAf8BQAGXAUEB/wFBAZgBQgH/AUEBmQFCAf8BXwGh
+        AV8B/wGmAaUBnQH/A1UBsAMPARQQAAMsAUMDfgHtAdkC1wH/AeYC5AH/AegC5wH/AegC5wH/AegC5wH/
+        AegC5wH/AekC5wH/AegC5gH/AeYC5QH/AeUC4wH/AeMC4gH/AeIC4QH/AeEC4AH/AeAC3wH/A98B/wPf
+        Af8B3wLeAf8B3wLeAf8B3wLeAf8B3wLeAf8B3wLeAf8B4ALfAf8B1wLWAf8BtAKsAf8DVQGwAw8BFBAA
+        AywBQwN+Ae0BjwGOAdkB/wIuAecB/wIrAekB/wIsAekB/wIuAegB/wIvAeUB/wIwAeMB/wIxAeEB/wIy
+        Ad8B/wI0Ad4B/wI1AdwB/wI2AdsB/wI3AdsB/wI4AdoB/wI6AdoB/wI7AdoB/wI8AdoB/wI9AdoB/wI+
+        AdoB/wJAAdoB/wJBAdoB/wJBAdsB/wJfAdMB/wGlAZ0BqwH/A1UBsAMPARSQAAMBAQIDRgF+Ab4BvwG9
+        Af0BbgG3AXMB/wErAacBNQH/ASoBpwE0Af8BKwGoATUB/wEtAacBNgH/AS4BpQE3Af8BLwGiATcB/wEw
+        AaABNwH/ATEBnQE3Af8BMwGcATgB/wE0AZsBOQH/ATUBmQE5Af8BNgGYAToB/wE3AZgBOwH/ATkBmAE8
+        Af8BOgGYAT0B/wE7AZgBPgH/ATwBmAE/Af8BPQGZAUAB/wE+AZkBQAH/AVUBoQFWAf8BnQGnAZcB/wFh
+        Al8B2wMmATgUAAMBAQIDRgF+AcMCwQH9AdwC2gH/AecC5QH/AegC5gH/AegC5gH/AegC5wH/AegC5wH/
+        AegC5wH/AegC5wH/AecC5gH/AeYC5QH/AeUC4wH/AeMC4gH/AeIC4QH/AeIC4QH/AeEC4AH/AeEC4AH/
+        A+AB/wPgAf8D4AH/A+EB/wHcAtsB/wG8ArUB/wFhAl8B2wMmATgUAAMBAQIDRgF+Ab4BvQHBAf0BbgFt
+        AdwB/wIrAegB/wIqAekB/wIrAekB/wItAegB/wIuAeYB/wIvAeQB/wIwAeIB/wIxAeAB/wIzAd8B/wI0
+        Ad0B/wI1AdwB/wI2AdwB/wI3AdsB/wI5AdsB/wI6AdsB/wI7AdsB/wI8AdsB/wI9AdsB/wI+AdwB/wFV
+        AVQB1wH/AZ0BlwG0Af8BYQJfAdsDJgE4mAADCgEOA1EBnwHbAeAB2gH/AW0BuAFzAf8BLQGnATcB/wEo
+        AacBMgH/ASoBpwE0Af8BLAGnATUB/wEtAaYBNgH/AS4BpAE3Af8BLwGhATcB/wEwAZ8BNwH/ATIBngE3
+        Af8BMwGcATgB/wE0AZsBOQH/ATUBmwE6Af8BNgGaATsB/wE4AZoBOwH/ATkBmgE9Af8BOgGaAT0B/wE7
+        AZsBPwH/AVUBowFXAf8BmgGpAZUB/wFsAmoB6QM1AVYDAQECGAADCgEOA1EBnwHlAuQB/wHdAtwB/wHm
+        AuQB/wHoAuYB/wHoAuYB/wHoAucB/wHoAucB/wHoAucB/wHoAucB/wHoAucB/wHnAuYB/wHmAuUB/wHl
+        AuQB/wHkAuMB/wPjAf8B4wLiAf8B4wLiAf8B4wLiAf8D4wH/Ad0C3AH/Ab8CuQH/AWsCagHpAzUBVgMB
+        AQIYAAMKAQ4DUQGfAdsB2QHkAf8CbQHdAf8CLQHmAf8CKAHpAf8CKgHpAf8CLAHpAf8CLQHnAf8CLgHl
+        Af8CLwHjAf8CMAHhAf8CMgHgAf8CMwHfAf8CNAHeAf8CNQHdAf8CNgHdAf8COAHdAf8COQHcAf8COgHc
+        Af8COwHdAf8BVQFUAdcB/wGaAZUBuAH/AWwCagHpAzUBVgMBAQKcAAMQARUDUgGhAcoBzQHJAf4BjgHA
+        AZIB/wE5AaoBQgH/ASgBpwEyAf8BKQGnATMB/wEqAacBNAH/ASwBpwE1Af8BLQGlATYB/wEuAaMBNwH/
+        AS8BogE3Af8BMQGgATgB/wEyAZ8BOAH/ATMBngE5Af8BNAGdATkB/wE1AZ0BOgH/ATYBnQE6Af8BPQGg
+        AUIB/wFiAacBZAH/AaEBqQGbAf8BagFnAWoB5QM4AV0DAwEEIAADEAEVA1IBoQHRAs8B/gHdAtsB/wHj
+        AuEB/wHoAuYB/wHoAuYB/wHoAuYB/wHoAucB/wHoAucB/wHoAucB/wHoAucB/wHoAucB/wHoAuYB/wHn
+        AuYB/wHnAuUB/wHmAuUB/wHmAuUB/wHlAuQB/wHZAtcB/wG8ArYB/wFqAmcB5QM4AV0DAwEEIAADEAEV
+        A1IBoQHKAcgBzwH+AY4BjQHcAf8COQHjAf8CKAHpAf8CKQHpAf8CKgHpAf8CLAHoAf8CLQHmAf8CLgHl
+        Af8CLwHjAf8CMQHiAf8CMgHhAf8CMwHgAf8CNAHfAf8CNQHfAf8CNgHfAf8CPQHfAf8BYgFhAdMB/wGh
+        AZoBtQH/AmoBZwHlAzgBXQMDAQSkAAMLAQ8DSAGDAYYChAHwAbYBzQG2Af8BYgG0AWgB/wE2AaoBQAH/
+        ASoBpwE0Af8BKQGnATMB/wEqAacBNAH/ASsBpwE1Af8BLQGmATYB/wEuAaQBNwH/AS8BowE3Af8BMAGi
+        ATcB/wEyAaIBOQH/AToBpAFAAf8BUwGoAVcB/wGLAa0BigH/AasBqQGjAf8DXAHMAy8BSQMBAQIoAAML
+        AQ8DSAGDAYYChAHwAd0C2gH/Ad0C3AH/AeQC4wH/AegC5gH/AekC5wH/AegC5wH/AegC5wH/AegC5wH/
+        AegC5wH/AekC5wH/AekC6AH/AeoC6AH/AegC5wH/AeEC3wH/Ac0CyQH/AbUCrQH/A1wBzAMvAUkDAQEC
+        KAADCwEPA0gBgwGGAoQB8AG2AbQB2wH/AWIBYQHdAf8CNgHlAf8CKgHpAf8BKAEpAekB/wIqAekB/wIr
+        AegB/wItAecB/wIuAeYB/wIvAeUB/wIwAeQB/wIyAeMB/wI6AeIB/wJTAdsB/wGLAYcBxwH/AasBowGt
+        Af8DXAHMAy8BSQMBAQKsAAMCAQMDLwFKA1oBvQGoAqYB+QGyAcgBsgH/AYIBugGGAf8BVQGxAVwB/wFB
+        Aa0BSgH/ATkBqwFCAf8BNwGrAUEB/wE5AasBQgH/AUABrAFJAf8BTwGuAVUB/wFoAbABawH/AZIBsQGR
+        Af8BqwGuAaUB/wFsAmsB5wNLAY4DGQEjNAADAgEDAy8BSgNaAb0BqgKoAfkB2ALVAf8B2gLYAf8B4ALe
+        Af8B4wLiAf8B5gLkAf8B5gLlAf8B5gLlAf8B5QLkAf8B4QLgAf8B2gLXAf8BzALIAf8BvAK0Af8DawHn
+        A0sBjgMZASM0AAMCAQMDLwFKA1oBvQGoAaUBqAH5AbIBrwHWAf8BggGBAdkB/wFVAVQB3wH/AkEB5AH/
+        AjkB5gH/AjcB5wH/ATkBOgHnAf8CQAHkAf8BTwFOAeAB/wFoAWYB1gH/AZIBjgHHAf8BqwGkAbQB/wFs
+        AmsB5wNLAY4DGQEjvAADDQERAzUBVwNWAasDaAHiAp4BnAH5AbkBwwG2Af8BrQG/AasB/wGnAbwBpQH/
+        AacBugGkAf8BrAG4AagB/wGfAaIBmgH+AYACfgHxA10BzwNKAYkDJAE1AwMBBEAAAw0BEQM1AVcDVgGr
+        A2gB4gGkAqEB+QHPAsoB/wHPAssB/wHOAsoB/wHMAscB/wHHAsIB/wGtAqcB/gGAAn4B8QNdAc8DSgGJ
+        AyQBNQMDAQRAAAMNAREDNQFXA1YBqwNoAeIBngGcAaEB+QG5AbUBywH/Aa0BqQHLAf8BpwGjAcsB/wGn
+        AaIByAH/AawBpgHCAf8BnwGZAacB/gGAAn4B8QNdAc8DSgGJAyQBNQMDAQTIAAMGAQgDHwEtAzkBXwNK
+        AYsDUgGnA1YBswNVAbADUAGdA0QBegMvAUoDFAEcAwABAVAAAwYBCAMfAS0DOQFfA0oBiwNSAacDVgGz
+        A1UBsANQAZ0DRAF6Ay8BSgMUARwDAAEBUAADBgEIAx8BLQM5AV8DSgGLA1IBpwNWAbMDVQGwA1ABnQNE
+        AXoDLwFKAxQBHAMAAQGoAAFCAU0BPgcAAT4DAAEoAwABgAMAASADAAEBAQABAQYAAQIWAAP/AQAB/wHg
+        AQcC/wHgAQcC/wHgAQcB/wQAAf8BgAEBAv8BgAEBAv8BgAEBAf8EAAH+AgABfwH+AgABfwH+AgABfwQA
+        AfwCAAE/AfwCAAE/AfwCAAE/BAAB+AIAAQ8B+AIAAQ8B+AIAAQ8EAAHwAgABBwHwAgABBwHwAgABBwQA
+        AeACAAEHAeACAAEHAeACAAEHBAABwAIAAQMBwAIAAQMBwAIAAQMEAAHAAgABAwHAAgABAwHAAgABAwQA
+        AYACAAEBAYACAAEBAYACAAEBBAABgAIAAQEBgAIAAQEBgAIAAQGnAAEBAwABAQMAAQEEAAGAAgABAQGA
+        AgABAQGAAgABAQQAAYACAAEBAYACAAEBAYACAAEBBAABwAIAAQMBwAIAAQMBwAIAAQMEAAHAAgABBwHA
+        AgABBwHAAgABBwQAAeACAAEHAeACAAEHAeACAAEHBAAB8AIAAQ8B8AIAAQ8B8AIAAQ8EAAH4AgABHwH4
+        AgABHwH4AgABHwQAAfwCAAF/AfwCAAF/AfwCAAF/BAAB/wIAAv8CAAL/AgAB/wQAAf8BwAEDAv8BwAED
+        Av8BwAEDAf8EAAs=
+</value>
+  </data>
+</root>

+ 265 - 0
GatewayTool/GatewayTool.csproj

@@ -0,0 +1,265 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{AE1B694E-19DB-4718-99C9-208D83A4F440}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <RootNamespace>GatewayTool</RootNamespace>
+    <AssemblyName>GatewayTool</AssemblyName>
+    <TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+    <Deterministic>true</Deterministic>
+    <TargetFrameworkProfile />
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup>
+    <ApplicationIcon>favicon.ico</ApplicationIcon>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>bin\Debug\Newtonsoft.Json.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Design" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Deployment" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Windows.Forms" />
+    <Reference Include="System.Xml" />
+    <Reference Include="WjwLib.API.Algorithm, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>bin\Debug\WjwLib.API.Algorithm.dll</HintPath>
+    </Reference>
+    <Reference Include="WjwLib.API.Communication, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>bin\Debug\WjwLib.API.Communication.dll</HintPath>
+    </Reference>
+    <Reference Include="WjwLib.API.Configuration, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>bin\Debug\WjwLib.API.Configuration.dll</HintPath>
+    </Reference>
+    <Reference Include="WjwLib.API.File, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>bin\Debug\WjwLib.API.File.dll</HintPath>
+    </Reference>
+    <Reference Include="WjwLib.API.System, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>bin\Debug\WjwLib.API.System.dll</HintPath>
+    </Reference>
+    <Reference Include="WjwLib.Control.IPAddressBox, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>bin\Debug\WjwLib.Control.IPAddressBox.dll</HintPath>
+    </Reference>
+    <Reference Include="WjwLib.Control.LEDNumber, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>bin\Debug\WjwLib.Control.LEDNumber.dll</HintPath>
+    </Reference>
+    <Reference Include="WjwLib.Control.LogWindow, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>bin\Debug\WjwLib.Control.LogWindow.dll</HintPath>
+    </Reference>
+    <Reference Include="WjwLib.Control.Switcher, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>bin\Debug\WjwLib.Control.Switcher.dll</HintPath>
+    </Reference>
+    <Reference Include="WjwLib.Control.WaitLayer, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>bin\Debug\WjwLib.Control.WaitLayer.dll</HintPath>
+    </Reference>
+    <Reference Include="WjwLib.Frame.FormBase, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>bin\Debug\WjwLib.Frame.FormBase.dll</HintPath>
+    </Reference>
+    <Reference Include="WjwLib.Frame.Log, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>bin\Debug\WjwLib.Frame.Log.dll</HintPath>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Businesses\ServiceOptions.cs" />
+    <Compile Include="Businesses\SoftwareConfigs.cs" />
+    <Compile Include="FormAbout.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="FormAbout.Designer.cs">
+      <DependentUpon>FormAbout.cs</DependentUpon>
+    </Compile>
+    <Compile Include="FormBase.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="FormBase.Designer.cs">
+      <DependentUpon>FormBase.cs</DependentUpon>
+    </Compile>
+    <Compile Include="FormConfig.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="FormConfig.Designer.cs">
+      <DependentUpon>FormConfig.cs</DependentUpon>
+    </Compile>
+    <Compile Include="FormJson.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="FormJson.Designer.cs">
+      <DependentUpon>FormJson.cs</DependentUpon>
+    </Compile>
+    <Compile Include="FormMain.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="FormMain.Designer.cs">
+      <DependentUpon>FormMain.cs</DependentUpon>
+    </Compile>
+    <Compile Include="FormParity.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="FormParity.Designer.cs">
+      <DependentUpon>FormParity.cs</DependentUpon>
+    </Compile>
+    <Compile Include="FormPing.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="FormPing.Designer.cs">
+      <DependentUpon>FormPing.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Models\DTU.cs" />
+    <Compile Include="Models\DTUConfig.cs" />
+    <Compile Include="Models\DTUConfigCollections.cs" />
+    <Compile Include="Models\DTUConfigEnums.cs" />
+    <Compile Include="Models\DTUSystemState.cs" />
+    <Compile Include="Models\GatewayBase.cs" />
+    <Compile Include="Models\SniffDeviceInfo.cs" />
+    <Compile Include="Models\StringLengthAttribute.cs" />
+    <Compile Include="Models\UpgradeInfo.cs" />
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Protocols\GatewayProtocolBase.cs" />
+    <Compile Include="Protocols\XModem.cs" />
+    <Compile Include="Protocols\XorSum16.cs" />
+    <Compile Include="Protocols\CRC16.cs" />
+    <Compile Include="Protocols\DTUProtocol.cs" />
+    <Compile Include="Protocols\SniffReplyInfo.cs" />
+    <Compile Include="Resource.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
+      <DependentUpon>Resource.resx</DependentUpon>
+    </Compile>
+    <Compile Include="StringMsg.cs" />
+    <Compile Include="UserControls\CollectionEditorEx.cs" />
+    <Compile Include="UserControls\ToolStripEx.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="UserControls\UcDeviceList.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="UserControls\UcDeviceList.Designer.cs">
+      <DependentUpon>UcDeviceList.cs</DependentUpon>
+    </Compile>
+    <Compile Include="UserControls\UcDTUPanel.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="UserControls\UcDTUPanel.Designer.cs">
+      <DependentUpon>UcDTUPanel.cs</DependentUpon>
+    </Compile>
+    <EmbeddedResource Include="FormAbout.resx">
+      <DependentUpon>FormAbout.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="FormBase.resx">
+      <DependentUpon>FormBase.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="FormConfig.resx">
+      <DependentUpon>FormConfig.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="FormJson.resx">
+      <DependentUpon>FormJson.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="FormMain.resx">
+      <DependentUpon>FormMain.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="FormParity.resx">
+      <DependentUpon>FormParity.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="FormPing.resx">
+      <DependentUpon>FormPing.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+      <SubType>Designer</SubType>
+    </EmbeddedResource>
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Resources.resx</DependentUpon>
+      <DesignTime>True</DesignTime>
+    </Compile>
+    <EmbeddedResource Include="Resource.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resource.Designer.cs</LastGenOutput>
+      <SubType>Designer</SubType>
+    </EmbeddedResource>
+    <EmbeddedResource Include="UserControls\UcDeviceList.resx">
+      <DependentUpon>UcDeviceList.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="UserControls\UcDTUPanel.resx">
+      <DependentUpon>UcDTUPanel.cs</DependentUpon>
+    </EmbeddedResource>
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Settings.settings</DependentUpon>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\gateway.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\favicon.ico" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="favicon.ico" />
+    <None Include="Resources\dtu.png" />
+    <None Include="Resources\Refresh.png" />
+    <None Include="Resources\open.png" />
+    <None Include="Resources\device.png" />
+    <None Include="Resources\dtu.jpg" />
+    <None Include="Resources\export.png" />
+    <None Include="Resources\import.png" />
+    <None Include="Resources\start.png" />
+    <None Include="Resources\stop.png" />
+    <None Include="Resources\favimg.png" />
+  </ItemGroup>
+  <ItemGroup />
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>

+ 26 - 0
GatewayTool/Models/DTU.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using WjwLib.API.Communication;
+
+namespace GatewayTool.Models
+{
+    /// <summary>
+    /// DTU设备类
+    /// </summary>
+    public class DTU : GatewayBase
+    {
+        /// <summary>
+        /// 系统状态
+        /// </summary>
+        public DTUSystemState SystemState { get; set; }
+
+        /// <summary>
+        /// 设备配置
+        /// </summary>
+        public DTUConfig Config { get; set; }
+    }
+}

+ 599 - 0
GatewayTool/Models/DTUConfig.cs

@@ -0,0 +1,599 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using WjwLib.API.System;
+
+namespace GatewayTool.Models
+{
+    /// <summary>
+    /// DTU配置
+    /// </summary>
+    public class DTUConfig
+    {
+        /// <summary>
+        /// mqtt主机地址
+        /// </summary>
+        [Category("\tMQTT设置")]
+        [DisplayName("服务器地址")]
+        [JsonProperty(PropertyName = "host")]
+        public string Host { get; set; } = string.Empty;
+
+        /// <summary>
+        /// mqtt端口
+        /// </summary>
+        [Category("\tMQTT设置")]
+        [DisplayName("端口号")]
+        [JsonProperty(PropertyName = "port")]
+        public UInt16 Port { get; set; }
+
+        private string _MessageTopic = string.Empty;
+        /// <summary>
+        /// mqtt发布主题
+        /// </summary>
+        [Category("\tMQTT设置")]
+        [DisplayName("发布主题")]
+        [JsonProperty(PropertyName = "messageTopic")]
+        [Description("字符串长度不能超过50")]
+        public string MessageTopic
+        {
+            get => _MessageTopic;
+            set
+            {
+                if (value.Length > 50)
+                {
+                    throw new Exception("字符串长度不能超过50!");
+                }
+                else
+                {
+                    _MessageTopic = value;
+                }
+            }
+        }
+
+        private string _CommandTopic = string.Empty;
+        /// <summary>
+        /// mqtt订阅主题
+        /// </summary>
+        [Category("\tMQTT设置")]
+        [DisplayName("订阅主题")]
+        [JsonProperty(PropertyName = "commandTopic")]
+        [Description("字符串长度不能超过50")]
+        public string CommandTopic
+        {
+            get => _CommandTopic;
+            set
+            {
+                if (value.Length > 50)
+                {
+                    throw new Exception("字符串长度不能超过50!");
+                }
+                else
+                {
+                    _CommandTopic = value;
+                }
+            }
+        }
+
+        /// <summary>
+        /// 波特率
+        /// </summary>
+        [Category("\t串口设置")]
+        [DisplayName("波特率")]
+        [JsonProperty(PropertyName = "baudrate")]
+        [TypeConverter(typeof(EnumTypeConverter))]
+        public EnumBaudrate Baudrate { get; set; } = EnumBaudrate.rate_9600;
+
+        /// <summary>
+        /// 数据位
+        /// </summary>
+        [Category("\t串口设置")]
+        [DisplayName("数据位")]
+        [JsonProperty(PropertyName = "dataBit")]
+        [TypeConverter(typeof(EnumTypeConverter))]
+        public EnumDataBits DataBits { get; set; } = EnumDataBits.Bit_8;
+
+        /// <summary>
+        /// 停止位
+        /// </summary>
+        [Category("\t串口设置")]
+        [DisplayName("停止位")]
+        [JsonProperty(PropertyName = "stopBit")]
+        [TypeConverter(typeof(EnumTypeConverter))]
+        public EnumStopBits StopBit { get; set; } = EnumStopBits.Bit_1;
+
+        /// <summary>
+        /// 校验位(none,odd,even,mark,Space)
+        /// </summary>
+        [Category("\t串口设置")]
+        [DisplayName("校验位")]
+        [JsonProperty(PropertyName = "checkBit")]
+        [Description("none-无,odd-奇,even-偶,mark-标记,space-空格")]
+        [TypeConverter(typeof(EnumTypeConverter))]
+        public EnumCheckBit CheckBit { get; set; } = EnumCheckBit.none;
+
+        /// <summary>
+        /// 流控制(hardware,software,none,custom)
+        /// </summary>
+        [Category("\t串口设置")]
+        [DisplayName("流控制")]
+        [JsonProperty(PropertyName = "flowControl")]
+        [Description("none-无,hardware-硬件,software-软件,custom-自定义")]
+        [TypeConverter(typeof(EnumTypeConverter))]
+        public EnumFlowControl FlowControl { get; set; } = EnumFlowControl.none;
+
+        /// <summary>
+        /// DTU设备ID
+        /// </summary>
+        [ReadOnly(true)]
+        [Category("\t协议设置")]
+        [DisplayName("DTU设备ID")]
+        [JsonProperty(PropertyName = "deviceId")]
+        [Description("网关设备序列号,最大长度24位,参数值不可更改")]
+        public string DeviceId { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 数据来源(1-645协议,2-mdb协议...)
+        /// </summary>
+        [Category("\t协议设置")]
+        [DisplayName("数据来源")]
+        [JsonProperty(PropertyName = "dataSource")]
+        [TypeConverter(typeof(EnumTypeConverter))]
+        public EnumDataSource DataSource { get; set; } = EnumDataSource.Protocol_645;
+
+        /// <summary>
+        /// 645协议版本(1-07版,2-97版)
+        /// </summary>
+        [Category("\t协议设置")]
+        [DisplayName("645协议版本")]
+        [JsonProperty(PropertyName = "version645")]
+        [TypeConverter(typeof(EnumTypeConverter))]
+        [Browsable(true)]
+        public EnumVersion645 Version645 { get; set; } = EnumVersion645.Version_07;
+
+        /// <summary>
+        /// 上发时间(单位:毫秒)
+        /// </summary>
+        [Category("\t协议设置")]
+        [DisplayName("上发时间")]
+        [JsonProperty(PropertyName = "inboundTime")]
+        [Description("设备采集数据后多久上发一次到服务器。单位:毫秒")]
+        public UInt64 InboundTime { get; set; }
+
+        /// <summary>
+        /// 轮询时间(单位:毫秒)
+        /// </summary>
+        [Category("\t协议设置")]
+        [DisplayName("轮询时间")]
+        [JsonProperty(PropertyName = "pollTime")]
+        [Description("单位:毫秒(只在有本地存储的设备含有此字段)")]
+        public UInt64 PollTime { get; set; }
+
+        private SensorDataItemCollection _SensorDataCollection = new SensorDataItemCollection();
+        /// <summary>
+        /// 上发配置
+        /// </summary>
+        [Category("\t上发设置")]
+        [DisplayName("上发配置")]
+        [JsonProperty(PropertyName = "sensorData")]
+        [Editor(typeof(SensorDataItemCollectionEditor), typeof(System.Drawing.Design.UITypeEditor))]
+        public SensorDataItemCollection SensorData
+        {
+            get => _SensorDataCollection;
+            set => _SensorDataCollection = value;
+        }
+
+        private CommandDataItemCollection _CommandDataCollection = new CommandDataItemCollection();
+        /// <summary>
+        /// 下发配置
+        /// </summary>
+        [Category("\t下发设置")]
+        [DisplayName("下发配置")]
+        [JsonProperty(PropertyName = "commandData")]
+        [Editor(typeof(CommandDataItemCollectionEditor), typeof(System.Drawing.Design.UITypeEditor))]
+        [Browsable(false)]
+        public CommandDataItemCollection CommandData
+        {
+            get => _CommandDataCollection;
+            set => _CommandDataCollection = value;
+        }
+    }
+
+    /// <summary>
+    /// 上发配置项
+    /// </summary>   
+    public class SensorDataItem
+    {
+        /// <summary>
+        /// Mdb功能码_读
+        /// </summary>
+        [Category("\tModbus设置")]
+        [DisplayName("Mdb功能码_读(输入0x开头的数)")]
+        [JsonProperty(PropertyName = "rFunctionCode")]
+        [Browsable(false)]
+        public UInt16 RFunctionCode { get; set; }
+
+        /// <summary>
+        /// Mdb寄存器地址
+        /// </summary>
+        [Category("\tModbus设置")]
+        [DisplayName("Mdb寄存器地址(输入0x开头的数)")]
+        [JsonProperty(PropertyName = "registerAddress")]
+        [Browsable(false)]
+        public UInt16 RegisterAddress { get; set; }
+
+        /// <summary>
+        /// Mdb从站地址
+        /// </summary>
+        [Category("\tModbus设置")]
+        [DisplayName("Mdb从站地址(输入0x开头的数)")]
+        [JsonProperty(PropertyName = "slaveAddress")]
+        [Browsable(false)]
+        public UInt16 SlaveAddress { get; set; }
+
+        /// <summary>
+        /// 寄存器字节数
+        /// </summary>
+        [Category("\tModbus设置")]
+        [DisplayName("寄存器字节数(输入0x开头的数)")]
+        [JsonProperty(PropertyName = "registerByteNum")]
+        [Browsable(false)]
+        public UInt16 RegisterByteNum { get; set; }
+
+        private string _Identifier = string.Empty;
+        /// <summary>
+        /// 唯一标识符
+        /// </summary>
+        [Category("\t通用设置")]
+        [DisplayName("唯一标识符")]
+        [JsonProperty(PropertyName = "identifier")]
+        [Description("字符串长度不能超过20")]
+        [StringLength(0, 20)]
+        public string Identifier
+        {
+            get => _Identifier;
+            set
+            {
+                if (value.Length > 20)
+                {
+                    throw new Exception("字符串长度不能超过20!");
+                }
+                else
+                {
+                    _Identifier = value;
+                }
+            }
+        }
+
+        private string _DeviceId = string.Empty;
+        /// <summary>
+        /// 子设备序列号
+        /// </summary>
+        [Category("\t通用设置")]
+        [DisplayName("子设备序列号")]
+        [JsonProperty(PropertyName = "deviceId")]
+        [Description("字符串长度不能超过20")]
+        [StringLength(0, 20)]
+        public string DeviceId
+        {
+            get => _DeviceId;
+            set
+            {
+                if (value.Length > 20)
+                {
+                    throw new Exception("字符串长度不能超过20!");
+                }
+                else
+                {
+                    _DeviceId = value;
+                }
+            }
+        }
+
+        /// <summary>
+        /// 645数据标识(上发到DTU为十进制)
+        /// </summary>
+        [Category("\t645设置")]
+        [DisplayName("645数据标识")]
+        [JsonProperty(PropertyName = "identifier645")]
+        [Description("上发到DTU为十进制")]
+        [Browsable(true)]
+        public UInt64 Identifier645 { get; set; }
+
+        /// <summary>
+        /// 645设备ID(如表号等)
+        /// </summary>
+        [Category("\t645设置")]
+        [DisplayName("645设备ID")]
+        [JsonProperty(PropertyName = "deviceID645")]
+        [Description("如表号等")]
+        [Browsable(true)]
+        public string DeviceID645 { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 保留小数点位数
+        /// </summary>
+        [Category("\tModbus设置")]
+        [DisplayName("保留小数点位数")]
+        [JsonProperty(PropertyName = "precise")]
+        [Browsable(false)]
+        public UInt16 Precise { get; set; }
+
+        /// <summary>
+        /// 大小端格式(1大端,2小端)
+        /// </summary>
+        [Category("\tModbus设置")]
+        [DisplayName("大小端格式")]
+        [JsonProperty(PropertyName = "bigLittleFormat")]
+        [TypeConverter(typeof(EnumTypeConverter))]
+        [Browsable(false)]
+        public EnumBigLittleFormat BigLittleFormat { get; set; } = EnumBigLittleFormat.Big;
+
+        /// <summary>
+        /// 是否序列化645设备ID属性
+        /// </summary>
+        /// <returns>true是,false否</returns>
+        public bool ShouldSerializeDeviceID645()
+        {
+            return IsPropertyBrowsable("DeviceID645"); //可见就可序列化
+        }
+
+        /// <summary>
+        /// 是否序列化645数据标识属性
+        /// </summary>
+        /// <returns>true是,false否</returns>
+        public bool ShouldSerializeIdentifier645()
+        {
+            return IsPropertyBrowsable("Identifier645"); //可见就可序列化
+        }
+
+        /// <summary>
+        /// 是否序列化保留小数点位数属性
+        /// </summary>
+        /// <returns>true是,false否</returns>
+        public bool ShouldSerializePrecise()
+        {
+            return IsPropertyBrowsable("Precise"); //可见就可序列化
+        }
+
+        /// <summary>
+        /// 是否序列化寄存器地址属性
+        /// </summary>
+        /// <returns>true是,false否</returns>
+        public bool ShouldSerializeRegisterAddress()
+        {
+            return IsPropertyBrowsable("RegisterAddress"); //可见就可序列化
+        }
+
+        /// <summary>
+        /// 是否序列化从站地址属性
+        /// </summary>
+        /// <returns>true是,false否</returns>
+        public bool ShouldSerializeSlaveAddress()
+        {
+            return IsPropertyBrowsable("SlaveAddress"); //可见就可序列化
+        }
+
+        /// <summary>
+        /// 是否序列化Mdb读功能码属性
+        /// </summary>
+        /// <returns>true是,false否</returns>
+        public bool ShouldSerializeRFunctionCode()
+        {
+            return IsPropertyBrowsable("RFunctionCode"); //可见就可序列化
+        }
+
+        /// <summary>
+        /// 是否序列化寄存器字节数属性
+        /// </summary>
+        /// <returns>true是,false否</returns>
+        public bool ShouldSerializeRegisterByteNum()
+        {
+            return IsPropertyBrowsable("RegisterByteNum"); //可见就可序列化
+        }
+
+        /// <summary>
+        /// 是否序列化大小端格式属性
+        /// </summary>
+        /// <returns>true是,false否</returns>
+        public bool ShouldSerializeBigLittleFormat()
+        {
+            return IsPropertyBrowsable("BigLittleFormat"); //可见就可序列化
+        }
+
+        /// <summary>
+        /// 获取属性的可见性
+        /// </summary>
+        /// <param name="propertyName">属性名称</param>
+        /// <returns>true可见,false不可见</returns>
+        private bool IsPropertyBrowsable(string propertyName)
+        {
+            AttributeCollection attributes = TypeDescriptor.GetProperties(this)[propertyName].Attributes;
+            BrowsableAttribute myAttribute = (BrowsableAttribute)attributes[typeof(BrowsableAttribute)];
+            return myAttribute.Browsable;
+        }
+    }
+
+    /// <summary>
+    /// 下发配置项
+    /// </summary>
+    public class CommandDataItem
+    {
+        private string _DeviceId = string.Empty;
+        /// <summary>
+        /// 子设备序列号
+        /// </summary>
+        [Category("\t通用设置")]
+        [DisplayName("子设备序列号")]
+        [JsonProperty(PropertyName = "deviceId")]
+        [Browsable(true)]
+        [Description("字符串长度不能超过20")]
+        [StringLength(0, 20)]
+        public string DeviceId
+        {
+            get => _DeviceId;
+            set
+            {
+                if (value.Length > 20)
+                {
+                    throw new Exception("字符串长度不能超过20!");
+                }
+                else
+                {
+                    _DeviceId = value;
+                }
+            }
+        }
+
+        private string _Identifier = string.Empty;
+        /// <summary>
+        /// 唯一标识符
+        /// </summary>
+        [Category("\t通用设置")]
+        [DisplayName("唯一标识符")]
+        [JsonProperty(PropertyName = "identifier")]
+        [Browsable(true)]
+        [Description("字符串长度不能超过20")]
+        [StringLength(0, 20)]
+        public string Identifier
+        {
+            get => _Identifier;
+            set
+            {
+                if (value.Length > 20)
+                {
+                    throw new Exception("字符串长度不能超过20!");
+                }
+                else
+                {
+                    _Identifier = value;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Mdb寄存器地址
+        /// </summary>
+        [Category("\tModbus设置")]
+        [DisplayName("Mdb寄存器地址(输入0x开头的数)")]
+        [JsonProperty(PropertyName = "registerAddress")]
+        [Browsable(true)]
+        public UInt16 RegisterAddress { get; set; }
+
+        /// <summary>
+        /// Mdb从站地址
+        /// </summary>
+        [Category("\tModbus设置")]
+        [DisplayName("Mdb从站地址(输入0x开头的数)")]
+        [JsonProperty(PropertyName = "slaveAddress")]
+        [Browsable(true)]
+        public UInt16 SlaveAddress { get; set; }
+
+        /// <summary>
+        /// Mdb功能码_写
+        /// </summary>
+        [Category("\tModbus设置")]
+        [DisplayName("Mdb功能码_写(输入0x开头的数)")]
+        [JsonProperty(PropertyName = "wFunctionCode")]
+        [Browsable(true)]
+        public UInt16 WFunctionCode { get; set; }
+
+        /// <summary>
+        /// 寄存器字节数
+        /// </summary>
+        [Category("\tModbus设置")]
+        [DisplayName("寄存器字节数(输入0x开头的数)")]
+        [JsonProperty(PropertyName = "registerByteNum")]
+        [Browsable(true)]
+        public UInt16 RegisterByteNum { get; set; }
+
+        /// <summary>
+        /// 大小端格式(1大端,2小端)
+        /// </summary>
+        [Category("\tModbus设置")]
+        [DisplayName("大小端格式")]
+        [JsonProperty(PropertyName = "bigLittleFormat")]
+        [TypeConverter(typeof(EnumTypeConverter))]
+        [Browsable(true)]
+        public EnumBigLittleFormat BigLittleFormat { get; set; } = EnumBigLittleFormat.Big;
+
+        /// <summary>
+        /// 是否序列化子设备序列号属性
+        /// </summary>
+        /// <returns>true是,false否</returns>
+        public bool ShouldSerializeDeviceId()
+        {
+            return IsPropertyBrowsable("DeviceId"); //可见就可序列化
+        }
+
+        /// <summary>
+        /// 是否序列化唯一标识符属性
+        /// </summary>
+        /// <returns>true是,false否</returns>
+        public bool ShouldSerializeIdentifier()
+        {
+            return IsPropertyBrowsable("Identifier"); //可见就可序列化
+        }
+
+        /// <summary>
+        /// 是否序列化Mdb寄存器地址属性
+        /// </summary>
+        /// <returns>true是,false否</returns>
+        public bool ShouldSerializeRegisterAddress()
+        {
+            return IsPropertyBrowsable("RegisterAddress"); //可见就可序列化
+        }
+
+        /// <summary>
+        /// 是否序列化Mdb从站地址属性
+        /// </summary>
+        /// <returns>true是,false否</returns>
+        public bool ShouldSerializeSlaveAddress()
+        {
+            return IsPropertyBrowsable("SlaveAddress"); //可见就可序列化
+        }
+
+        /// <summary>
+        /// 是否序列化Modbus写功能码属性
+        /// </summary>
+        /// <returns>true是,false否</returns>
+        public bool ShouldSerializeWFunctionCode()
+        {
+            return IsPropertyBrowsable("WFunctionCode"); //可见就可序列化
+        }
+
+        /// <summary>
+        /// 是否序列化寄存器字节数属性
+        /// </summary>
+        /// <returns>true是,false否</returns>
+        public bool ShouldSerializeRegisterByteNum()
+        {
+            return IsPropertyBrowsable("RegisterByteNum"); //可见就可序列化
+        }
+
+        /// <summary>
+        /// 是否序列化大小端格式属性
+        /// </summary>
+        /// <returns>true是,false否</returns>
+        public bool ShouldSerializeBigLittleFormat()
+        {
+            return IsPropertyBrowsable("BigLittleFormat"); //可见就可序列化
+        }
+
+        /// <summary>
+        /// 获取属性的可见性
+        /// </summary>
+        /// <param name="propertyName">属性名称</param>
+        /// <returns>true可见,false不可见</returns>
+        private bool IsPropertyBrowsable(string propertyName)
+        {
+            AttributeCollection attributes = TypeDescriptor.GetProperties(this)[propertyName].Attributes;
+            BrowsableAttribute myAttribute = (BrowsableAttribute)attributes[typeof(BrowsableAttribute)];
+            return myAttribute.Browsable;
+        }
+    }
+}

+ 75 - 0
GatewayTool/Models/DTUConfigCollections.cs

@@ -0,0 +1,75 @@
+using GatewayTool.UserControls;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel.Design;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GatewayTool.Models
+{
+    public class SensorDataItemCollection : CollectionBase
+    {
+        public SensorDataItem this[int index]
+        {
+            get { return (SensorDataItem)List[index]; }
+        }
+
+        public void Add(SensorDataItem item)
+        {
+            List.Add(item);
+        }
+
+        public void Remove(SensorDataItem item)
+        {
+            List.Remove(item);
+        }
+    }
+
+    public class SensorDataItemCollectionEditor : CollectionEditorEx
+    {
+        public SensorDataItemCollectionEditor(Type type) : base(type)
+        {
+        }
+
+        protected override string GetDisplayText(object value)
+        {
+            SensorDataItem item = new SensorDataItem();
+            item = (SensorDataItem)value;
+            return base.GetDisplayText($"SensorDataItem");
+        }
+    }
+
+    public class CommandDataItemCollection : CollectionBase
+    {
+        public CommandDataItem this[int index]
+        {
+            get { return (CommandDataItem)List[index]; }
+        }
+
+        public void Add(CommandDataItem item)
+        {
+            List.Add(item);
+        }
+
+        public void Remove(CommandDataItem item)
+        {
+            List.Remove(item);
+        }
+    }
+
+    public class CommandDataItemCollectionEditor : CollectionEditorEx
+    {
+        public CommandDataItemCollectionEditor(Type type) : base(type)
+        {
+        }
+
+        protected override string GetDisplayText(object value)
+        {
+            CommandDataItem item = new CommandDataItem();
+            item = (CommandDataItem)value;
+            return base.GetDisplayText($"CommandDataItem");
+        }
+    }
+}

+ 192 - 0
GatewayTool/Models/DTUConfigEnums.cs

@@ -0,0 +1,192 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GatewayTool.Models
+{
+    /// <summary>
+    /// 波特率枚举
+    /// </summary>  
+    public enum EnumBaudrate
+    {
+        //[Description("")]
+        //None = 0,
+        [Description("110")]
+        rate_110 = 110,
+        [Description("300")]
+        rate_300 = 300,
+        [Description("600")]
+        rate_600 = 600,
+        [Description("1200")]
+        rate_1200 = 1200,
+        [Description("2400")]
+        rate_2400 = 2400,
+        [Description("4800")]
+        rate_4800 = 4800,
+        [Description("9600")]
+        rate_9600 = 9600,
+        [Description("14400")]
+        rate_14400 = 14400,
+        [Description("19200")]
+        rate_19200 = 19200,
+        [Description("38400")]
+        rate_38400 = 38400,
+        [Description("43000")]
+        rate_43000 = 43000,
+        [Description("56000")]
+        rate_56000 = 56000,
+        [Description("57600")]
+        rate_57600 = 57600,
+        [Description("115200")]
+        rate_115200 = 115200,
+        [Description("128000")]
+        rate_128000 = 128000,
+        [Description("230400")]
+        rate_230400 = 230400,
+        [Description("256000")]
+        rate_256000 = 256000,
+        [Description("460800")]
+        rate_460800 = 460800,
+        [Description("500000")]
+        rate_500000 = 500000,
+        [Description("512000")]
+        rate_512000 = 512000,
+        [Description("600000")]
+        rate_600000 = 600000,
+        [Description("750000")]
+        rate_750000 = 750000,
+        [Description("921600")]
+        rate_921600 = 921600,
+        [Description("1000000")]
+        rate_1000000 = 1000000,
+        [Description("1500000")]
+        rate_1500000 = 1500000,
+        [Description("2000000")]
+        rate_2000000 = 2000000,
+    }
+
+    /// <summary>
+    /// 数据位枚举
+    /// </summary>   
+    public enum EnumDataBits
+    {
+        //[Description("")]
+        //None = 0,
+        //[Description("5")]
+        //Bit_5 = 5,
+        //[Description("6")]
+        //Bit_6 = 6,
+        //[Description("7")]
+        //Bit_7 = 7,
+        [Description("8")]
+        Bit_8 = 8,
+        [Description("9")]
+        Bit_9 = 9,
+    }
+
+    /// <summary>
+    /// 停止位枚举
+    /// </summary>   
+    public enum EnumStopBits
+    {
+        //[Description("")]
+        //None = 0,
+        [Description("1")]
+        Bit_1 = 1,
+        [Description("2")]
+        Bit_2 = 2,
+    }
+
+    /// <summary>
+    /// 校验位枚举
+    /// </summary>
+    //[JsonConverter(typeof(StringEnumConverter))]
+    public enum EnumCheckBit
+    {
+        [Description("无")]
+        none = 0,
+        [Description("奇")]
+        odd = 1,
+        [Description("偶")]
+        even = 2,
+        //[Description("标记")]
+        //mark,
+        //[Description("空格")]
+        //space,
+    }
+
+    /// <summary>
+    /// 流控制枚举
+    /// </summary>
+    //[JsonConverter(typeof(StringEnumConverter))]
+    public enum EnumFlowControl
+    {
+        [Description("无")]
+        none = 0,
+        //[Description("硬件")]
+        //hardware,
+        //[Description("软件")]
+        //software,
+        //[Description("自定义")]
+        //custom,
+    }
+
+    /// <summary>
+    /// 数据来源枚举
+    /// </summary>    
+    public enum EnumDataSource
+    {
+        //[Description("")]
+        //None = 0,
+        [Description("645协议")]
+        Protocol_645 = 1,
+        [Description("mdb协议")]
+        Protocol_mdb = 2,
+    }
+
+    /// <summary>
+    /// 645协议版本枚举
+    /// </summary>    
+    public enum EnumVersion645
+    {
+        //[Description("")]
+        //None = 0,
+        [Description("07版")]
+        Version_07 = 1,
+        [Description("97版")]
+        Version_97 = 2,
+    }
+
+    /// <summary>
+    /// 数据格式枚举
+    /// </summary>    
+    public enum EnumDataFormat
+    {
+        //[Description("")]
+        //None = 0,
+        [Description("整型")]
+        Integer = 1,
+        [Description("布尔")]
+        Boolean = 2,
+        [Description("浮点")]
+        Float = 3,
+    }
+
+    /// <summary>
+    /// 大小端格式枚举
+    /// </summary>    
+    public enum EnumBigLittleFormat
+    {
+        //[Description("")]
+        //None = 0,
+        [Description("大端")]
+        Big = 1,
+        [Description("小端")]
+        Little = 2,
+    }
+}

+ 311 - 0
GatewayTool/Models/DTUSystemState.cs

@@ -0,0 +1,311 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using WjwLib.API.System;
+
+namespace GatewayTool.Models
+{
+    [Serializable]
+    /// <summary>
+    /// DTU系统状态
+    /// </summary>
+    public class DTUSystemState
+    {
+        /// <summary>
+        /// 设备编号
+        /// </summary>
+        [Category("\t基本信息")]
+        [DisplayName("设备编号")]
+        [ReadOnly(true)]
+        [JsonProperty(PropertyName = "UniqueId")]
+        public string DeviceID { get; set; }
+
+        /// <summary>
+        /// 设备名称
+        /// </summary>
+        [Category("\t基本信息")]
+        [DisplayName("设备名称")]
+        [ReadOnly(true)]
+        [JsonProperty(PropertyName = "Equipmentname")]
+        public string DeviceName { get; set; }
+
+        /// <summary>
+        /// 设备型号
+        /// </summary>
+        [Category("\t基本信息")]
+        [DisplayName("设备型号")]
+        [ReadOnly(true)]
+        [JsonProperty(PropertyName = "Equipmenttype")]
+        public string DeviceType { get; set; }
+
+        /// <summary>
+        /// 当前工作模式
+        /// </summary>
+        [Category("\t基本信息")]
+        [DisplayName("当前工作模式")]
+        [ReadOnly(true)]
+        [JsonProperty(PropertyName = "Workmode")]
+        public string CurrentWorkMode { get; set; }
+
+        private string _OperatorType = string.Empty;
+        /// <summary>
+        /// 运营商类型
+        /// </summary>
+        [Category("SIM卡信息")]
+        [DisplayName("运营商类型")]
+        [ReadOnly(true)]
+        [JsonProperty(PropertyName = "Operatortype")]
+        public string OperatorType
+        {
+            get => _OperatorType;
+            set
+            {
+                switch (value)
+                {
+                    case "CHN-CT":
+                        _OperatorType = "中国电信";
+                        break;
+                    case "CHN-UNICOM":
+                        _OperatorType = "中国联通";
+                        break;
+                    case "CHINA MOBILE":
+                        _OperatorType = "中国移动";
+                        break;
+                    default:
+                        _OperatorType = value;
+                        break;
+                }
+            }
+        }
+
+        private string _NetworkType = string.Empty;
+        /// <summary>
+        /// 网络类型
+        /// </summary>
+        [Category("SIM卡信息")]
+        [DisplayName("网络类型")]
+        [ReadOnly(true)]
+        [JsonProperty(PropertyName = "Networktype")]
+        public string NetworkType
+        {
+            get => _NetworkType;
+            set
+            {
+                switch (value)
+                {
+                    case "0":
+                        _NetworkType = "GSM";
+                        break;
+                    case "2":
+                        _NetworkType = "UTRAN";
+                        break;
+                    case "3":
+                        _NetworkType = "GSM W/EGPRS";
+                        break;
+                    case "4":
+                        _NetworkType = "UTRAN W/HSDPA";
+                        break;
+                    case "5":
+                        _NetworkType = "UTRAN W/HSUPA";
+                        break;
+                    case "6":
+                        _NetworkType = "UTRAN W/HSDPA and HSUPA";
+                        break;
+                    case "7":
+                        _NetworkType = "E-UTRAN";
+                        break;
+                    case "8":
+                        _NetworkType = "UTRAN HSPA+";
+                        break;
+                    default:
+                        _NetworkType = value;
+                        break;
+                }
+            }
+        }
+
+        private string _NetworkState = string.Empty;
+        /// <summary>
+        /// 网络状态
+        /// </summary>
+        [Category("SIM卡信息")]
+        [DisplayName("网络状态")]
+        [ReadOnly(true)]
+        [JsonProperty(PropertyName = "Networkstatus")]
+        public string NetworkState
+        {
+            get => _NetworkState;
+            set
+            {
+                switch (value)
+                {
+                    case "0":
+                        _NetworkState = "未注册到网络";
+                        break;
+                    case "1":
+                        _NetworkState = "注册到网络";
+                        break;
+                    default:
+                        _NetworkState = value;
+                        break;
+                }
+            }
+        }
+
+        private string _SIMCardState = string.Empty;
+        /// <summary>
+        /// SIM卡状态
+        /// </summary>
+        [Category("SIM卡信息")]
+        [DisplayName("SIM卡状态")]
+        [ReadOnly(true)]
+        [JsonProperty(PropertyName = "SIMcardstatus")]
+        public string SIMCardState
+        {
+            get => _SIMCardState;
+            set
+            {
+                switch (value)
+                {
+                    case "0":
+                        _SIMCardState = "正常";
+                        break;
+                    case "1":
+                        _SIMCardState = "异常";
+                        break;
+                    default:
+                        _SIMCardState = value;
+                        break;
+                }
+            }
+        }
+
+        /// <summary>
+        /// SIM卡ICCID
+        /// </summary>
+        [Category("SIM卡信息")]
+        [DisplayName("SIM卡ICCID")]
+        [ReadOnly(true)]
+        [JsonProperty(PropertyName = "SIMcardICCID")]
+        public string SIMCardICCID { get; set; }
+
+        /// <summary>
+        /// IMEI
+        /// </summary>
+        [Category("SIM卡信息")]
+        [DisplayName("IMEI号")]
+        [ReadOnly(true)]
+        [JsonProperty(PropertyName = "IMEInumber")]
+        public string IMEI { get; set; }
+
+        /// <summary>
+        /// 信号强度
+        /// </summary>
+        [Category("SIM卡信息")]
+        [DisplayName("信号强度(%)")]
+        [ReadOnly(true)]
+        [JsonProperty(PropertyName = "Signalstrength")]
+        public string SignalStrength { get; set; }
+
+        ///// <summary>
+        ///// 基站定位
+        ///// </summary>
+        //[Category("SIM卡信息")]
+        //[DisplayName("基站定位")]
+        //[ReadOnly(true)]
+        //[JsonProperty(PropertyName = "Basestationlocation")]
+        //public string BasePosition { get; set; }
+
+        ///// <summary>
+        ///// MAC地址
+        ///// </summary>
+        //[Category("网络信息")]
+        //[DisplayName("MAC地址")]
+        //[ReadOnly(true)]
+        //public string MAC { get; set; } = "5A:34:12:0C:26:08";
+
+        ///// <summary>
+        ///// 公网IP地址
+        ///// </summary>
+        //[Category("网络信息")]
+        //[DisplayName("公网IP地址")]
+        //[ReadOnly(true)]
+        //public string PublicIP { get; set; } = "10.67.98.74";
+
+        ///// <summary>
+        ///// LAN口IP地址
+        ///// </summary>
+        //[Category("网络信息")]
+        //[DisplayName("LAN口IP地址")]
+        //[ReadOnly(true)]
+        //public string LANIP { get; set; } = "192.168.10.1";
+
+        ///// <summary>
+        ///// LAN口子网掩码
+        ///// </summary>
+        //[Category("网络信息")]
+        //[DisplayName("LAN口子网掩码")]
+        //[ReadOnly(true)]
+        //public string LANSubnetMask { get; set; } = "255.255.255.0";
+
+        ///// <summary>
+        ///// DNS地址
+        ///// </summary>
+        //[Category("网络信息")]
+        //[DisplayName("DNS地址")]
+        //[ReadOnly(true)]
+        //public string DNS { get; set; } = "222.61.74.8";
+
+        ///// <summary>
+        ///// 串口号
+        ///// </summary>
+        //[Category("485信息")]
+        //[DisplayName("串口号")]
+        //[ReadOnly(true)]
+        //public string PortNum { get; set; } = "COM1";
+
+        /// <summary>
+        /// 波特率
+        /// </summary>
+        [Category("485信息")]
+        [DisplayName("波特率")]
+        [ReadOnly(true)]
+        [JsonProperty(PropertyName = "baudrate")]
+        [TypeConverter(typeof(EnumTypeConverter))]
+        public EnumBaudrate BaudRate { get; set; } = EnumBaudrate.rate_9600;
+
+        /// <summary>
+        /// 数据位
+        /// </summary>
+        [Category("485信息")]
+        [DisplayName("数据位")]
+        [ReadOnly(true)]
+        [JsonProperty(PropertyName = "dataBit")]
+        [TypeConverter(typeof(EnumTypeConverter))]
+        public EnumDataBits DataBits { get; set; } = EnumDataBits.Bit_8;
+
+        /// <summary>
+        /// 停止位
+        /// </summary>
+        [Category("485信息")]
+        [DisplayName("停止位")]
+        [ReadOnly(true)]
+        [JsonProperty(PropertyName = "stopBit")]
+        [TypeConverter(typeof(EnumTypeConverter))]
+        public EnumStopBits StopBits { get; set; } = EnumStopBits.Bit_1;
+
+        /// <summary>
+        /// 校验位
+        /// </summary>
+        [Category("485信息")]
+        [DisplayName("校验位")]
+        [ReadOnly(true)]
+        [JsonProperty(PropertyName = "checkBit")]
+        [TypeConverter(typeof(EnumTypeConverter))]
+        public EnumCheckBit Parity { get; set; } = EnumCheckBit.none;
+    }
+}

+ 36 - 0
GatewayTool/Models/GatewayBase.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using WjwLib.API.Communication;
+
+namespace GatewayTool.Models
+{
+    /// <summary>
+    /// 网关基类
+    /// </summary>
+    public class GatewayBase
+    {
+        /// <summary>
+        /// 通讯实例
+        /// </summary>
+        public ICommunication Communication;
+
+        /// <summary>
+        /// 设备嗅探信息
+        /// </summary>
+        public SniffDeviceInfo SniffInfo { get; set; }
+
+        /// <summary>
+        /// 设备图片
+        /// </summary>
+        public Image DeviceImage { get; set; } = Resource.dtu;
+
+        /// <summary>
+        /// 固件升级信息
+        /// </summary>
+        public UpgradeInfo UpgradeInfo { get; set; }
+    }
+}

+ 29 - 0
GatewayTool/Models/SniffDeviceInfo.cs

@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GatewayTool.Models
+{
+    /// <summary>
+    /// 嗅探设备信息类
+    /// </summary>
+    public class SniffDeviceInfo
+    {
+        /// <summary>
+        /// 设备通信数据来源
+        /// </summary>
+        public String Source { get; set; }
+
+        /// <summary>
+        /// 设备唯一编号
+        /// </summary>
+        public String SN { get; set; }
+
+        /// <summary>
+        /// 设备固件版本号
+        /// </summary>
+        public String Version { get; set; }
+    }
+}

+ 57 - 0
GatewayTool/Models/StringLengthAttribute.cs

@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GatewayTool.Models
+{
+    public abstract class AbstractValidateAttribute : Attribute
+    {
+        //和接口类似,抽象方法只声明方法
+        public abstract bool Validate(object objValue);
+    }
+
+    [AttributeUsage(AttributeTargets.Property)]
+    public class StringLengthAttribute : AbstractValidateAttribute
+    {
+        //字段
+        private int _Mni = 0;
+        private int _Max = 0;
+
+        public StringLengthAttribute(int min, int max)
+        {
+            this._Max = max;
+            this._Mni = min;
+        }
+        public override bool Validate(object objValue)
+        {
+            return objValue != null
+                  && objValue.ToString().Length >= this._Mni
+                  && objValue.ToString().Length <= this._Max;
+        }
+    }
+
+    public static class AttributeExtend
+    {
+        public static bool Validate<T>(this T t)
+        {
+            Type type = t.GetType();
+            foreach (var property in type.GetProperties())//属性上查找
+            {
+                if (property.IsDefined(typeof(AbstractValidateAttribute), true))
+                {
+                    object objValue = property.GetValue(t);
+                    foreach (AbstractValidateAttribute attribute in property.GetCustomAttributes(typeof(AbstractValidateAttribute), true))
+                    {
+                        if (!attribute.Validate(objValue))//如果成功了以后 就继续验证,否则就直接返回
+                        {
+                            return false;
+                        }
+                    }
+                }
+            }
+            return true;
+        }
+    }
+}

+ 56 - 0
GatewayTool/Models/UpgradeInfo.cs

@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GatewayTool.Models
+{
+    /// <summary>
+    /// 固件升级信息类
+    /// </summary>
+    public class UpgradeInfo
+    {
+        /// <summary>
+        /// 固件文件路径
+        /// </summary>
+        public string FirmwareFilePath { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 固件文件总字节数据
+        /// </summary>
+        public byte[] TotalData { get; set; }
+
+        private int _SeperatePackNum = 0;
+        /// <summary>
+        /// 当前分包计数
+        /// </summary>
+        public int SeperatePackNum
+        {
+            get => _SeperatePackNum;
+            set => _SeperatePackNum = value;
+        }
+
+        private int _TotalPackNum = 0;
+        /// <summary>
+        /// 总分包数
+        /// </summary>
+        public int TotalPackNum
+        {
+            get => _TotalPackNum;
+            set => _TotalPackNum = value;
+        }
+
+        /// <summary>
+        /// 固件升级进度(%)
+        /// </summary>
+        public int UpgradeProgress
+        {
+            get
+            {
+                if (_TotalPackNum == 0 || _SeperatePackNum == 0) return 0;
+                return (int)(Math.Round(((decimal)_SeperatePackNum / _TotalPackNum), 2) * 100);
+            }
+        }
+    }
+}

+ 26 - 0
GatewayTool/Program.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace GatewayTool
+{
+    static class Program
+    {
+        /// <summary>
+        /// 应用程序的主入口点。
+        /// </summary>
+        [STAThread]
+        static void Main()
+        {
+            WjwLib.API.System.MessageBoxManager.Register();
+
+            Application.EnableVisualStyles();
+            Application.SetCompatibleTextRenderingDefault(false);
+            //Application.Run(new FormMain());
+
+            WjwLib.API.System.EnsureOneInstance.Handle(new FormMain());
+        }
+    }
+}

+ 36 - 0
GatewayTool/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 有关程序集的一般信息由以下
+// 控制。更改这些特性值可修改
+// 与程序集关联的信息。
+[assembly: AssemblyTitle("GatewayTool")]
+[assembly: AssemblyDescription("自研物联网网关配套工具软件")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("传秀科技")]
+[assembly: AssemblyProduct("GatewayTool")]
+[assembly: AssemblyCopyright("Copyright © CXKJ 2023")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 将 ComVisible 设置为 false 会使此程序集中的类型
+//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
+//请将此类型的 ComVisible 特性设置为 true。
+[assembly: ComVisible(false)]
+
+// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
+[assembly: Guid("ae1b694e-19db-4718-99c9-208d83a4f440")]
+
+// 程序集的版本信息由下列四个值组成: 
+//
+//      主版本
+//      次版本
+//      生成号
+//      修订号
+//
+//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
+//通过使用 "*",如下所示:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 63 - 0
GatewayTool/Properties/Resources.Designer.cs

@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     此代码由工具生成。
+//     运行时版本:4.0.30319.42000
+//
+//     对此文件的更改可能会导致不正确的行为,并且如果
+//     重新生成代码,这些更改将会丢失。
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace GatewayTool.Properties {
+    using System;
+    
+    
+    /// <summary>
+    ///   一个强类型的资源类,用于查找本地化的字符串等。
+    /// </summary>
+    // 此类是由 StronglyTypedResourceBuilder
+    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
+    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
+    // (以 /str 作为命令选项),或重新生成 VS 项目。
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() {
+        }
+        
+        /// <summary>
+        ///   返回此类使用的缓存的 ResourceManager 实例。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GatewayTool.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   重写当前线程的 CurrentUICulture 属性,对
+        ///   使用此强类型资源类的所有资源查找执行重写。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+    }
+}

+ 117 - 0
GatewayTool/Properties/Resources.resx

@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 26 - 0
GatewayTool/Properties/Settings.Designer.cs

@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     此代码由工具生成。
+//     运行时版本:4.0.30319.42000
+//
+//     对此文件的更改可能会导致不正确的行为,并且如果
+//     重新生成代码,这些更改将会丢失。
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace GatewayTool.Properties {
+    
+    
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+        
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+        
+        public static Settings Default {
+            get {
+                return defaultInstance;
+            }
+        }
+    }
+}

+ 7 - 0
GatewayTool/Properties/Settings.settings

@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
+  <Profiles>
+    <Profile Name="(Default)" />
+  </Profiles>
+  <Settings />
+</SettingsFile>

+ 64 - 0
GatewayTool/Protocols/CRC16.cs

@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GatewayTool.Protocols
+{
+    class CRC16
+    {
+        public static byte[] GetCRC(byte[] data)
+        {
+            UInt16 i, j, carry_flag, a;
+            UInt16 ret = 0xffff;
+
+            int len = data.Length;
+
+            for (i = 0; i < len; i++)
+            {
+                ret = (UInt16)(ret ^ data[i]);
+                for (j = 0; j < 8; j++)
+                {
+                    a = ret;
+                    carry_flag = (UInt16)(a & 0x0001);
+                    ret = (UInt16)(ret >> 1);
+                    if (carry_flag == 1)
+                    {
+                        ret = (UInt16)(ret ^ 0xa001);
+                    }
+                }
+            }
+
+            byte[] rett = { 0, 0 };
+            rett[0] = (byte)((ret & 0xff00) >> 8);//high 8
+            rett[1] = (byte)(ret & 0x00ff);//low 8
+
+            return rett;
+        }
+
+        public static bool CheckCRC(byte[] data)
+        {
+            int len = data.Length;
+            if (len < 2) return false;
+
+            byte crcLow = data[len - 1];
+            byte crcHigh = data[len - 2];
+
+            byte[] orgData = data.Take(len - 2).ToArray();//  2:crc
+            byte[] crc = GetCRC(orgData);
+
+            if (crc.Length < 2)
+            {
+                return false;
+            }
+
+            if (crcLow == crc[1] && crcHigh == crc[0])
+            {
+                return true;
+            }
+
+            return false;
+        }
+    }
+}

+ 110 - 0
GatewayTool/Protocols/DTUProtocol.cs

@@ -0,0 +1,110 @@
+using GatewayTool.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace GatewayTool.Protocols
+{
+    public class DTUProtocol : GatewayProtocolBase
+    {
+        /// <summary>
+        /// 读DTU系统状态
+        /// </summary>
+        /// <returns></returns>
+        public byte[] PackReadDTUSystemState()
+        {
+            byte[] data = { 0x02 };
+            return Pack(data);
+        }
+
+        /// <summary>
+        /// 读DTU设备配置
+        /// </summary>
+        /// <returns></returns>
+        public byte[] PackReadDTUDeviceConfig()
+        {
+            byte[] data = { 0x03 };
+            return Pack(data);
+        }
+
+        /// <summary>
+        /// 写DTU设备配置
+        /// </summary>
+        /// <param name="json">json配置字符串</param>
+        /// <returns></returns>
+        public byte[] PackWriteDTUDeviceConfig(string json)
+        {
+            byte[] data = { 0x04 };
+            return PackJson(data, json);
+        }
+
+        /// <summary>
+        /// 准备升级固件
+        /// </summary>
+        /// <returns></returns>
+        public byte[] PackPrepareToUpgrade()
+        {
+            byte[] data = { 0x05 };
+            return Pack(data);
+        }
+
+        /// <summary>
+        /// 解析读DTU系统状态
+        /// </summary>
+        /// <param name="data"></param>
+        /// <returns></returns>
+        public DTUSystemState UnpackReadDTUSystemState(byte[] data)
+        {
+            try
+            {
+                byte[] data2 = Unpack(data);
+                string json = Encoding.ASCII.GetString(data2);
+                return JsonConvert.DeserializeObject<DTUSystemState>(json);
+            }
+            catch
+            {
+                return null;
+            }
+        }
+
+        /// <summary>
+        /// 解析读DTU设备配置
+        /// </summary>
+        /// <param name="data"></param>
+        /// <returns></returns>
+        public DTUConfig UnpackReadDTUDeviceConfig(byte[] data)
+        {
+            try
+            {
+                byte[] data2 = Unpack(data);
+                string json = Encoding.ASCII.GetString(data2);
+                return JsonConvert.DeserializeObject<DTUConfig>(json);
+            }
+            catch
+            {
+                return null;
+            }
+        }
+
+        /// <summary>
+        /// 解析写DTU设备配置
+        /// </summary>
+        /// <param name="data"></param>
+        /// <returns></returns>
+        public bool UnpackWriteDTUDeviceConfig(byte[] data)
+        {
+            try
+            {
+                byte[] data2 = Unpack(data);
+                return data2[0] == 0x01;
+            }
+            catch
+            {
+                return false;
+            }
+        }
+    }
+}

+ 114 - 0
GatewayTool/Protocols/GatewayProtocolBase.cs

@@ -0,0 +1,114 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GatewayTool.Protocols
+{
+    public class GatewayProtocolBase
+    {
+        public static readonly byte[] head = { 0xee };
+        public static readonly byte[] tail = { 0xbb };
+
+        public byte[] Pack(byte[] data)
+        {
+            byte[] ret = { };
+            byte[] crc16 = { };
+
+            crc16 = CRC16.GetCRC(data);
+
+            ret = ret.Concat(head).ToArray();
+            ret = ret.Concat(data).ToArray();
+            ret = ret.Concat(crc16).ToArray();
+            ret = ret.Concat(tail).ToArray();
+
+            return ret;
+        }
+
+        public byte[] PackJson(byte[] data, string json)
+        {
+            byte[] ret = { };
+            byte[] crc16 = { };
+
+            crc16 = CRC16.GetCRC(data);
+
+            ret = ret.Concat(head).ToArray();
+            ret = ret.Concat(data).ToArray();
+            ret = ret.Concat(crc16).ToArray();
+            ret = ret.Concat(tail).ToArray();
+
+            byte[] bytes = Encoding.ASCII.GetBytes(json);
+            ret = ret.Concat(bytes).ToArray();
+
+            return ret;
+        }
+
+        public byte[] Unpack(byte[] data)
+        {
+            int length = data.Length;
+            if (data[0] == head[0] && data[length - 1] == tail[0])  //a pkg is finished
+            {
+                //skip pkg's head and tail
+                int head = 1;
+                int tail = length - 1;
+                int i = 0;
+                byte[] pkg = new byte[length - 2];
+
+                for (int a = head; a < tail; a++)
+                {
+                    pkg[i++] = data[a];
+                }
+
+                if (CRC16.CheckCRC(pkg))
+                {
+                    byte[] data2 = pkg.Skip(1).Take(pkg.Length - 3).ToArray(); //remove function code & crc data                    
+                    return data2;
+                }
+            }
+            return null;
+        }
+
+        public byte[] PackSniffDevice()
+        {
+            byte[] data = { 0x01 };
+            return Pack(data);
+        }
+
+        public SniffReplyInfo UnpackSniff(byte[] data)
+        {
+            byte[] data2 = Unpack(data);
+            return ProcessFindingDevice(data2);
+        }
+
+        private SniffReplyInfo ProcessFindingDevice(byte[] pkg)
+        {
+            if (pkg == null || pkg.Length == 0) return null;
+            SniffReplyInfo info = null;
+            string message = Encoding.ASCII.GetString(pkg, 0, pkg.Length);
+
+            if (message.Contains(";"))
+            {
+                string[] strArray = message.Split(';');
+                info = new SniffReplyInfo();
+
+                for (int index = 0; index < strArray.Length; index++)
+                {
+                    switch (index)
+                    {
+                        case 0:
+                            info.SN = strArray[index];
+                            break;
+                        case 1:
+                            info.Version = strArray[index];
+                            break;
+                        default:
+                            break;
+                    }
+                }
+            }
+
+            return info;
+        }
+    }
+}

+ 21 - 0
GatewayTool/Protocols/SniffReplyInfo.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GatewayTool.Protocols
+{
+    public class SniffReplyInfo
+    {
+        /// <summary>
+        /// 设备唯一编号
+        /// </summary>
+        public String SN { get; set; }
+
+        /// <summary>
+        /// 设备固件版本号
+        /// </summary>
+        public String Version { get; set; }
+    }
+}

+ 180 - 0
GatewayTool/Protocols/XModem.cs

@@ -0,0 +1,180 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+using System.IO.Ports;
+using WjwLib.API.Communication;
+
+namespace GatewayTool.Protocols
+{
+    /// <summary>
+    /// CRC16 XModem
+    /// </summary>
+    class XModemCRC16
+    {
+        #region Members
+
+        //size = 256
+        private static ushort[] crc16tab = {
+            0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
+            0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
+            0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
+            0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
+            0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
+            0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
+            0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
+            0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
+            0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
+            0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
+            0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
+            0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
+            0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
+            0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
+            0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
+            0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
+            0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
+            0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
+            0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
+            0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
+            0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
+            0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
+            0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
+            0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
+            0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
+            0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
+            0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
+            0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
+            0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
+            0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
+            0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
+            0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
+        };
+
+        #endregion
+
+        #region Implementation
+
+        static public ushort CRC16_ccitt(byte[] buf, int index, int len)
+        {
+            int counter;
+            ushort crc = 0;
+            for (counter = 0; counter < len; counter++)
+                crc = (ushort)((crc << 8) ^ crc16tab[((crc >> 8) ^ buf[index + counter]) & 0x00FF]);
+            return crc;
+        }
+
+        #endregion
+    }
+
+    /// <summary>
+    /// XModem常量标识符
+    /// </summary>
+    static class XModemConstants
+    {
+        /// <summary>
+        /// Xmodem数据头
+        /// </summary>
+        public const byte SOH = 0x01;
+        /// <summary>
+        /// 1K-Xmodem数据头
+        /// </summary>
+        public const byte STX = 0x02;
+        /// <summary>
+        /// 发送结束
+        /// </summary>
+        public const byte EOT = 0x04;
+        /// <summary>
+        /// 认可响应
+        /// </summary>
+        public const byte ACK = 0x06;
+        /// <summary>
+        /// 不认可响应
+        /// </summary>
+        public const byte NAK = 0x15;
+        /// <summary>
+        /// 终止传送
+        /// </summary>
+        public const byte CAN = 0x18;
+        /// <summary>
+        /// 填充数据包
+        /// </summary>
+        public const byte CTRLZ = 0x1A;
+    }
+
+    public class XModem
+    {
+        /// <summary>
+        /// 获取分包
+        /// </summary>
+        /// <param name="totalData">总数据</param>
+        /// <param name="SPN">分包序号(从1开始,超过255后重新从1开始)</param>
+        /// <param name="length">分包数据长度(128或1024)</param>
+        /// <param name="packNum">总分包数</param>
+        /// <returns>分包</returns>
+        public byte[] GetPacket(byte[] totalData, int SPN, int length, out int packNum)
+        {
+            packNum = 0;
+            if (totalData == null) return null;
+            if (totalData.Length == 0) return new byte[0];
+
+            packNum = totalData.Length / length + (totalData.Length % length == 0 ? 0 : 1);
+            if (SPN > packNum) return null;
+
+            byte[] fullStream = new byte[length];
+            if (SPN < packNum) //是完整长度的包
+            {
+                Array.Copy(totalData, (SPN - 1) * length, fullStream, 0, length);
+            }
+            else if (SPN == packNum) //可能是,也可能不是完整长度的包
+            {
+                int lastLen = totalData.Length - (SPN - 1) * length;
+                if (lastLen == length) //是完整包
+                {
+                    Array.Copy(totalData, (SPN - 1) * length, fullStream, 0, length);
+                }
+                else if (lastLen < length) //长度不足,进行补齐处理
+                {
+                    int addLen = length - lastLen; //补齐长度
+                    byte[] addData = new byte[addLen];
+                    for (int i = 0; i < addLen; i++)
+                    {
+                        addData[i] = XModemConstants.CTRLZ;
+                    }
+                    Array.Copy(totalData, (SPN - 1) * length, fullStream, 0, lastLen);
+                    Array.Copy(addData, 0, fullStream, lastLen, addLen);
+                }
+            }
+            return GeneratePacket(fullStream, SPN, length);
+        }
+
+        /// <summary>
+        /// 生成完整分包
+        /// </summary>
+        /// <param name="data">分包的数据部分</param>
+        /// <param name="SPN">分包序号</param>
+        /// <param name="length">分包数据长度</param>
+        /// <returns>完整数据包</returns>
+        private byte[] GeneratePacket(byte[] data, int SPN, int length)
+        {
+            List<byte> list = new List<byte>();
+            if (length == 128)
+            {
+                list.Add(XModemConstants.SOH);
+            }
+            else if (length == 1024)
+            {
+                list.Add(XModemConstants.STX);
+            }
+            byte spn = SPN > 255 ? (byte)(SPN % 255) : (byte)SPN;
+            list.Add(spn);
+            list.Add((byte)(~spn));
+            list.AddRange(data);
+
+            ushort crcVal = XModemCRC16.CRC16_ccitt(data, 0, length);
+            list.Add((byte)((crcVal >> 8) & 0xFF));
+            list.Add((byte)(crcVal & 0xFF));
+
+            return list.ToArray();
+        }
+    }
+}

+ 30 - 0
GatewayTool/Protocols/XorSum16.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GatewayTool.Protocols
+{
+    class XorSum16
+    {
+        public static string CheckSum(string message)
+        {
+            char[] chars = message.ToCharArray();
+            int checksum = 0;
+            int left = chars.Length % 2;
+            int evenLength = chars.Length - left;
+            for (int i = 0; i < evenLength; i += 2)
+            {
+                int temp = (int)chars[i] * 256 + (int)chars[i + 1];
+                checksum ^= temp;
+            }
+            if (left != 0)
+            {
+                int val = (int)(chars[chars.Length - 1] << 8);
+                checksum ^= val;
+            }
+            return Convert.ToString(checksum, 16).ToUpper();
+        }
+    }
+}

+ 173 - 0
GatewayTool/Resource.Designer.cs

@@ -0,0 +1,173 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     此代码由工具生成。
+//     运行时版本:4.0.30319.42000
+//
+//     对此文件的更改可能会导致不正确的行为,并且如果
+//     重新生成代码,这些更改将会丢失。
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace GatewayTool {
+    using System;
+    
+    
+    /// <summary>
+    ///   一个强类型的资源类,用于查找本地化的字符串等。
+    /// </summary>
+    // 此类是由 StronglyTypedResourceBuilder
+    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
+    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
+    // (以 /str 作为命令选项),或重新生成 VS 项目。
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resource {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resource() {
+        }
+        
+        /// <summary>
+        ///   返回此类使用的缓存的 ResourceManager 实例。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GatewayTool.Resource", typeof(Resource).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   重写当前线程的 CurrentUICulture 属性,对
+        ///   使用此强类型资源类的所有资源查找执行重写。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap device {
+            get {
+                object obj = ResourceManager.GetObject("device", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap dtu {
+            get {
+                object obj = ResourceManager.GetObject("dtu", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap export {
+            get {
+                object obj = ResourceManager.GetObject("export", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   查找类似于 (图标) 的 System.Drawing.Icon 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Icon favicon {
+            get {
+                object obj = ResourceManager.GetObject("favicon", resourceCulture);
+                return ((System.Drawing.Icon)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap favimg {
+            get {
+                object obj = ResourceManager.GetObject("favimg", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap gateway {
+            get {
+                object obj = ResourceManager.GetObject("gateway", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap import {
+            get {
+                object obj = ResourceManager.GetObject("import", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap open {
+            get {
+                object obj = ResourceManager.GetObject("open", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap refresh {
+            get {
+                object obj = ResourceManager.GetObject("refresh", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap start {
+            get {
+                object obj = ResourceManager.GetObject("start", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap stop {
+            get {
+                object obj = ResourceManager.GetObject("stop", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+    }
+}

+ 154 - 0
GatewayTool/Resource.resx

@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+  <data name="stop" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>Resources\stop.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="start" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>Resources\start.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="gateway" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>Resources\gateway.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="refresh" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>Resources\refresh.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="favimg" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>Resources\favimg.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="export" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>Resources\export.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="favicon" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>Resources\favicon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="import" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>Resources\import.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="open" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>Resources\open.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="device" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>Resources\device.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="dtu" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>Resources\dtu.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+</root>

BIN
GatewayTool/Resources/config.ico


BIN
GatewayTool/Resources/device.png


BIN
GatewayTool/Resources/dtu.jpg


BIN
GatewayTool/Resources/dtu.png


BIN
GatewayTool/Resources/export.png


BIN
GatewayTool/Resources/favicon.ico


BIN
GatewayTool/Resources/favimg.png


BIN
GatewayTool/Resources/gateway.png


BIN
GatewayTool/Resources/import.png


BIN
GatewayTool/Resources/manage.ico


BIN
GatewayTool/Resources/open.png


BIN
GatewayTool/Resources/refresh.png


BIN
GatewayTool/Resources/start.png


BIN
GatewayTool/Resources/state.ico


BIN
GatewayTool/Resources/stop.png


+ 229 - 0
GatewayTool/StringMsg.cs

@@ -0,0 +1,229 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GatewayTool
+{
+    public enum Language
+    {
+        SIMPLIFIED_CHINESE = 0,
+        ENGLISH = 1,
+    }
+
+    public class StringMsg
+    {
+        private int i = 0;
+
+        #region Item Definitions
+        public string SoftwareName;
+        public string SoftwareCopyright;
+        public string SoftwareProfile;
+
+        public string MessageBoxButtonYes;
+        public string MessageBoxButtonNo;
+        public string MessageBoxButtonOK;
+        public string MessageBoxButtonCancel;
+
+        public string DialogSaveAs;
+        public string DialogOpen;
+
+        public string CollectionEditorTitle;
+        public string CollectionEditorMember;
+        public string CollectionEditorAdd;
+        public string CollectionEditorCopy;
+        public string CollectionEditorRemove;
+        public string CollectionEditorProperty;
+        public string CollectionEditorOK;
+        public string CollectionEditorCancel;
+
+        public string MenuSelectAll;
+        public string MenuCopy;
+        public string MenuClear;
+
+        public string Prompt;
+        public string Attention;
+        public string Error;
+
+        public string System;
+        public string Language;
+        public string Settings;
+
+        public string Tool;
+        public string ParityCalculator;
+        public string Domain;
+        public string JsonFileViewer;
+
+        public string View;
+        public string Log;
+        public string Nail;
+
+        public string Help;
+        public string Usage;
+        public string About;
+
+        public string StartService;
+        public string StopService;
+        public string RefreshList;
+        public string BrowseLog;
+
+        public string DeviceList;
+        public string ID;
+        public string SN;
+        public string Source;
+        public string FirmwareVersion;
+
+        public string SystemState;
+        public string DeviceImage;
+        public string StateInfo;
+        public string Refresh;
+
+        public string DeviceConfig;
+        public string Export;
+        public string Import;
+        public string Read;
+        public string Write;
+
+        public string ExportSuccess;
+        public string ExportFail;
+        public string ImportSuccess;
+        public string ImportFail;
+        public string ParseFail;
+
+        public string DeviceManage;
+        public string UpgradeFirmware;
+        public string ChooseFile;
+        public string UpgradeProgress;
+        public string Upgrade;
+        public string RestartDevice;
+        public string Restart;
+        public string ModifyPassword;
+        public string NewPassword;
+        public string ConfirmNewPassword;
+        public string Modify;
+
+        public string Net;
+        public string ServicePort;
+        public string ShowMaxLine;
+        public string FolderPath;
+        public string DeviceSniff;
+        public string RemotePort;
+        public string Baudrate;
+        public string Databits;
+        public string Stopbits;
+        public string Parity;
+        public string SaveConfigOK;
+        public string SaveConfigFail;
+        public string DefaultValue;
+
+        #endregion
+
+        public StringMsg(Language l)
+        {
+            i = (int)l;
+
+            SoftwareName = new string[] { "物联网网关工具", "Gateway Tool" }[i];
+            SoftwareCopyright = new string[] { "传秀科技 版权所有©2023", "Copyright © 2023 ChuanXiu" }[i];
+            SoftwareProfile = new string[] { "本软件可对连接的自研物联网网关设备进行参数设置并显示数据收发结果,操作简便直观,欢迎使用!"
+                                           , "This software can set parameters of the connected self-developed IoT gateway equipment and display data transmission and receiving results. It is easy to operate and intuitive. Welcome to use!" }[i];
+
+            MessageBoxButtonYes = new string[] { "是", "Yes" }[i];
+            MessageBoxButtonNo = new string[] { "否", "No" }[i];
+            MessageBoxButtonOK = new string[] { "确定", "OK" }[i];
+            MessageBoxButtonCancel = new string[] { "取消", "Cancel" }[i];
+
+            DialogSaveAs = new string[] { "另存为", "Save as" }[i];
+            DialogOpen = new string[] { "打开", "Open" }[i];
+
+            CollectionEditorTitle = new string[] { "集合编辑器", "Collection Editor" }[i];
+            CollectionEditorMember = new string[] { "成员(&M):", "Members(&M):" }[i];
+            CollectionEditorAdd = new string[] { "添加(&A)", "Add(&A)" }[i];
+            CollectionEditorCopy = new string[] { "复制(&C)", "Copy(&C)" }[i];
+            CollectionEditorRemove = new string[] { "移除(&R)", "Remove(&R)" }[i];
+            CollectionEditorProperty = new string[] { "属性(&P):", "Properties(&P):" }[i];
+            CollectionEditorOK = new string[] { "确定", "OK" }[i];
+            CollectionEditorCancel = new string[] { "取消", "Cancel" }[i];
+
+            MenuSelectAll = new string[] { "全选", "Select all" }[i];
+            MenuCopy = new string[] { "复制", "Copy" }[i];
+            MenuClear = new string[] { "清空", "Clear" }[i];
+
+            Prompt = new string[] { "提示", "Prompt" }[i];
+            Attention = new string[] { "注意", "Attention" }[i];
+            Error = new string[] { "错误", "Error" }[i];
+
+            System = new string[] { "系统(&S)", "&System" }[i];
+            Language = new string[] { "语言", "Language" }[i];
+            Settings = new string[] { "设置", "Settings" }[i];
+
+            Tool = new string[] { "工具(&T)", "&Tool" }[i];
+            ParityCalculator = new string[] { "校验计算器", "Parity calculator" }[i];
+            Domain = new string[] { "域名", "Domain" }[i];
+            JsonFileViewer = new string[] { "Json文件查看器", "Json file viewer" }[i];
+
+            View = new string[] { "视图(&V)", "&View" }[i];
+            Log = new string[] { "日志", "Log" }[i];
+            Nail = new string[] { "钉在最前", "Nail to the front" }[i];
+
+            Help = new string[] { "帮助(&H)", "&Help" }[i];
+            Usage = new string[] { "使用帮助", "Usage" }[i];
+            About = new string[] { "关于", "About" }[i];
+
+            StartService = new string[] { "启动服务", "Start service" }[i];
+            StopService = new string[] { "停止服务", "Stop service" }[i];
+            RefreshList = new string[] { "刷新列表", "Refresh list" }[i];
+            BrowseLog = new string[] { "浏览日志", "Browse log" }[i];
+
+            DeviceList = new string[] { "设备列表", "Device List" }[i];
+            ID = new string[] { "序号", "ID" }[i];
+            SN = new string[] { "设备编号", "SN" }[i];
+            Source = new string[] { "数据来源", "Data source" }[i];
+            FirmwareVersion = new string[] { "固件版本", "Firmware version" }[i];
+
+            SystemState = new string[] { "系统状态", "System state" }[i];
+            DeviceImage = new string[] { "设备图片", "Device image" }[i];
+            StateInfo = new string[] { "状态信息", "State information" }[i];
+            Refresh = new string[] { "刷新", "Refresh" }[i];
+
+            DeviceConfig = new string[] { "设备配置", "Device configuration" }[i];
+            Export = new string[] { "导出", "Export" }[i];
+            Import = new string[] { "导入", "Import" }[i];
+            Read = new string[] { "读取", "Read" }[i];
+            Write = new string[] { "写入", "Write" }[i];
+
+            ExportSuccess = new string[] { "导出成功!", "Export successfully!" }[i];
+            ExportFail = new string[] { "导出失败!", "Fail to export!" }[i];
+            ImportSuccess = new string[] { "导入成功!", "Import successfully!" }[i];
+            ImportFail = new string[] { "导入失败!", "Fail to import!" }[i];
+            ParseFail = new string[] { "解析失败!", "Fail to parse!" }[i];
+
+            DeviceManage = new string[] { "设备管理", "Device management" }[i];
+            UpgradeFirmware = new string[] { "更新固件", "Upgrade firmware" }[i];
+            ChooseFile = new string[] { "选择文件", "Choose file" }[i];
+            UpgradeProgress = new string[] { "更新进度", "Progress" }[i];
+            Upgrade = new string[] { "更新", "Upgrade" }[i];
+            RestartDevice = new string[] { "重启设备", "Restart device" }[i];
+            Restart = new string[] { "重启", "Restart" }[i];
+            ModifyPassword = new string[] { "修改密码", "Modify password" }[i];
+            NewPassword = new string[] { "新密码", "New password" }[i];
+            ConfirmNewPassword = new string[] { "确认新密码", "Confirm new password" }[i];
+            Modify = new string[] { "修改", "Modify" }[i];
+
+            Net = new string[] { "网络", "Net" }[i];
+            ServicePort = new string[] { "服务端口", "Service port" }[i];
+            ShowMaxLine = new string[] { "最大显示行数", "Max lines" }[i];
+            FolderPath = new string[] { "文件夹路径", "Folder path" }[i];
+            DeviceSniff = new string[] { "设备嗅探", "Device sniffing" }[i];
+            RemotePort = new string[] { "远程端口", "Remote port" }[i];
+            Baudrate = new string[] { "波特率", "Baudrate" }[i];
+            Databits = new string[] { "数据位", "Databits" }[i];
+            Stopbits = new string[] { "停止位", "Stopbits" }[i];
+            Parity = new string[] { "校验位", "Parity" }[i];
+            SaveConfigOK = new string[] { "配置保存成功!", "Save configurations OK!" }[i];
+            SaveConfigFail = new string[] { "配置保存失败!", "Failed to save configurations!" }[i];
+            DefaultValue = new string[] { "默认值", "Default" }[i];
+
+        }
+    }
+}

+ 181 - 0
GatewayTool/UserControls/CollectionEditorEx.cs

@@ -0,0 +1,181 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel.Design;
+using System.Data;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using Newtonsoft.Json;
+using System.Drawing;
+using GatewayTool.Models;
+
+namespace GatewayTool.UserControls
+{
+    public class CollectionEditorEx : CollectionEditor
+    {
+        private string propertyText = "";
+        private bool isCopyExist = false;
+        private Button copyButton;
+        private ListBox listBox;
+        private Label propertiesLabel;
+        private PropertyGrid propertyBrowser;
+
+        public CollectionEditorEx(Type type) : base(type)
+        {
+        }
+
+        protected override CollectionForm CreateCollectionForm()
+        {
+            CollectionForm form = base.CreateCollectionForm();
+            form.Font = new System.Drawing.Font("微软雅黑", 10.5f, System.Drawing.GraphicsUnit.Point);
+            form.Shown += delegate
+            {
+                ShowDescription(form);
+            };            
+
+            var addButton = (ButtonBase)form.Controls.Find("addButton", true).First();
+            var removeButton = (ButtonBase)form.Controls.Find("removeButton", true).First();
+            var upButton = (ButtonBase)form.Controls.Find("upButton", true).First();
+            var downButton = (ButtonBase)form.Controls.Find("downButton", true).First();
+            listBox = (ListBox)form.Controls.Find("listbox", true).First();
+            var cancelButton = (ButtonBase)form.Controls.Find("cancelButton", true).First();
+            var okButton = (ButtonBase)form.Controls.Find("okButton", true).First();
+            propertiesLabel = (Label)form.Controls.Find("propertiesLabel", true).First();
+            var membersLabel = (Label)form.Controls.Find("membersLabel", true).First();
+            propertyBrowser = (PropertyGrid)form.Controls.Find("propertyBrowser", true).First();
+            propertyBrowser.ToolbarVisible = false;
+
+            StringMsg strMsg = new FormBase().StrMsg;
+            string name = form.Text.Split(' ')[0];
+            form.Text = name + " " + strMsg.CollectionEditorTitle;
+            membersLabel.Text = strMsg.CollectionEditorMember;
+            addButton.Text = strMsg.CollectionEditorAdd;
+            removeButton.Text = strMsg.CollectionEditorRemove;
+            propertiesLabel.Text = strMsg.CollectionEditorProperty;
+            propertyText = strMsg.CollectionEditorProperty;
+            okButton.Text = strMsg.CollectionEditorOK;
+            cancelButton.Text = strMsg.CollectionEditorCancel;
+
+            listBox.SelectedIndexChanged += ListBox_SelectedIndexChanged;
+            propertiesLabel.TextChanged += PropertiesLabel_TextChanged;
+            okButton.Click += OkButton_Click;
+
+            Control owner = removeButton.Parent;
+            TableLayoutPanel pnl = owner as TableLayoutPanel;
+
+            copyButton = new Button();
+            copyButton.Name = "copyButton";
+            copyButton.Anchor = AnchorStyles.Left | AnchorStyles.Right;
+            copyButton.AutoSize = false;
+            copyButton.Text = strMsg.CollectionEditorCopy;
+            copyButton.Enabled = listBox.Items.Count > 0;
+            pnl.Controls.Add(copyButton);
+            copyButton.Width = (pnl.Width - addButton.Margin.Right * 2 - removeButton.Margin.Left * 2 - copyButton.Margin.Left * 2) / pnl.Controls.Count;
+            copyButton.Height = addButton.Height;
+
+            copyButton.Click += (sender, e) =>
+            {
+                isCopyExist = !isCopyExist;
+                ChangeCopyButtonUI();
+
+                //object selObj = listBox.SelectedItem;
+                //string val = JsonConvert.SerializeObject(selObj);
+                //Type type = selObj.GetType();
+                //object newObj = JsonConvert.DeserializeObject(val);
+
+                //System.ComponentModel.Design.CollectionEditor+CollectionEditorCollectionForm+ListItem
+
+                //object selObj = propertyBrowser.SelectedObject;
+                //listBox.Items.Add(selObj);
+            };
+
+            return form;
+        }
+
+        private void OkButton_Click(object sender, EventArgs e)
+        {
+            //object arr = listBox.DataSource;
+            //for (int i = listBox.Items.Count - 1; i >= 0; i--)
+            //{
+            //    object obj = listBox.Items[i];
+            //    if (!obj.Validate())
+            //    {
+            //        MessageBox.Show($"Item {i} has invalid value!","PropertyGrid");
+            //        return;
+            //    }
+            //}
+        }
+
+        protected override object CreateInstance(Type itemType)
+        {
+            object newObj = base.CreateInstance(itemType);
+
+            if (isCopyExist)
+            {
+                object selObj = propertyBrowser.SelectedObject;
+                PropertyInfo[] newInfo = newObj.GetType().GetProperties();
+                if (selObj != null)
+                {
+                    PropertyInfo[] selInfo = selObj.GetType().GetProperties();
+                    foreach (PropertyInfo newP in newInfo)
+                    {
+                        foreach (PropertyInfo selP in selInfo)
+                        {
+                            if (newP.Name.Equals(selP.Name))
+                            {
+                                newP.SetValue(newObj, selP.GetValue(selObj));
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+
+            return newObj;
+        }
+
+        private void PropertiesLabel_TextChanged(object sender, EventArgs e)
+        {
+            ChangePropertiesLabelText();
+        }
+
+        private void ChangeCopyButtonUI() 
+        {
+            copyButton.ForeColor = isCopyExist ? Color.Blue : Color.Black;
+        }
+
+        private void ChangePropertiesLabelText()
+        {
+            if (propertiesLabel.Text.Contains(" "))
+            {
+                propertiesLabel.Text = propertiesLabel.Text.Split(' ')[0] + " " + propertyText;
+            }
+            else
+            {
+                propertiesLabel.Text = propertyText;
+            }
+        }
+
+        private void ListBox_SelectedIndexChanged(object sender, EventArgs e)
+        {
+            copyButton.Enabled = listBox.Items.Count > 0;
+            ChangePropertiesLabelText();
+        }
+
+        void ShowDescription(Control control)
+        {
+            propertiesLabel.Text = propertyText;
+            ChangeCopyButtonUI();
+
+            PropertyGrid grid = control as PropertyGrid;
+            if (grid != null) grid.HelpVisible = true;
+            foreach (Control child in control.Controls)
+            {
+                ShowDescription(child);
+            }
+        }
+    }
+}

+ 20 - 0
GatewayTool/UserControls/ToolStripEx.cs

@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace GatewayTool.UserControls
+{
+    public class ToolStripEx : ToolStrip
+    {
+        protected override void WndProc(ref Message m)
+        {
+            const int WM_MOUSEACTIVATE = 0x21;
+            if (m.Msg == WM_MOUSEACTIVATE && this.CanFocus && !this.Focused)
+                this.Focus();
+            base.WndProc(ref m);
+        }
+    }
+}

+ 527 - 0
GatewayTool/UserControls/UcDTUPanel.Designer.cs

@@ -0,0 +1,527 @@
+
+namespace GatewayTool.UserControls
+{
+    partial class UcDTUPanel
+    {
+        /// <summary> 
+        /// 必需的设计器变量。
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary> 
+        /// 清理所有正在使用的资源。
+        /// </summary>
+        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region 组件设计器生成的代码
+
+        /// <summary> 
+        /// 设计器支持所需的方法 - 不要修改
+        /// 使用代码编辑器修改此方法的内容。
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.components = new System.ComponentModel.Container();
+            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(UcDTUPanel));
+            this.tabControl1 = new System.Windows.Forms.TabControl();
+            this.tabSystemState = new System.Windows.Forms.TabPage();
+            this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+            this.gbDeviceImage = new System.Windows.Forms.GroupBox();
+            this.picDTU = new System.Windows.Forms.PictureBox();
+            this.gbStateInfo = new System.Windows.Forms.GroupBox();
+            this.btnRefreshState = new System.Windows.Forms.Button();
+            this.gridDTU = new System.Windows.Forms.PropertyGrid();
+            this.tabDeviceConfig = new System.Windows.Forms.TabPage();
+            this.btnExport = new System.Windows.Forms.Button();
+            this.btnRead = new System.Windows.Forms.Button();
+            this.btnImport = new System.Windows.Forms.Button();
+            this.btnDownload = new System.Windows.Forms.Button();
+            this.gridConfig = new System.Windows.Forms.PropertyGrid();
+            this.tabDeviceManage = new System.Windows.Forms.TabPage();
+            this.gbModifyPassword = new System.Windows.Forms.GroupBox();
+            this.textBox3 = new System.Windows.Forms.TextBox();
+            this.textBox2 = new System.Windows.Forms.TextBox();
+            this.lbConfirmNewPassword = new System.Windows.Forms.Label();
+            this.btnModify = new System.Windows.Forms.Button();
+            this.lbNewPassword = new System.Windows.Forms.Label();
+            this.gbRestartDevice = new System.Windows.Forms.GroupBox();
+            this.btnRestart = new System.Windows.Forms.Button();
+            this.gbUpgradeFirmware = new System.Windows.Forms.GroupBox();
+            this.progressBar1 = new System.Windows.Forms.ProgressBar();
+            this.txtFilePath = new System.Windows.Forms.TextBox();
+            this.lbUpgradeProgress = new System.Windows.Forms.Label();
+            this.lbChooseFile = new System.Windows.Forms.Label();
+            this.btnBrowse = new System.Windows.Forms.Button();
+            this.btnUpgrade = new System.Windows.Forms.Button();
+            this.imageList1 = new System.Windows.Forms.ImageList(this.components);
+            this.lbProgressVal = new System.Windows.Forms.Label();
+            this.tabControl1.SuspendLayout();
+            this.tabSystemState.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
+            this.splitContainer1.Panel1.SuspendLayout();
+            this.splitContainer1.Panel2.SuspendLayout();
+            this.splitContainer1.SuspendLayout();
+            this.gbDeviceImage.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.picDTU)).BeginInit();
+            this.gbStateInfo.SuspendLayout();
+            this.tabDeviceConfig.SuspendLayout();
+            this.tabDeviceManage.SuspendLayout();
+            this.gbModifyPassword.SuspendLayout();
+            this.gbRestartDevice.SuspendLayout();
+            this.gbUpgradeFirmware.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // tabControl1
+            // 
+            this.tabControl1.Controls.Add(this.tabSystemState);
+            this.tabControl1.Controls.Add(this.tabDeviceConfig);
+            this.tabControl1.Controls.Add(this.tabDeviceManage);
+            this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.tabControl1.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.tabControl1.ImageList = this.imageList1;
+            this.tabControl1.ItemSize = new System.Drawing.Size(125, 25);
+            this.tabControl1.Location = new System.Drawing.Point(0, 0);
+            this.tabControl1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.tabControl1.Multiline = true;
+            this.tabControl1.Name = "tabControl1";
+            this.tabControl1.SelectedIndex = 0;
+            this.tabControl1.Size = new System.Drawing.Size(805, 359);
+            this.tabControl1.TabIndex = 0;
+            this.tabControl1.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.tabControl1_DrawItem);
+            this.tabControl1.SelectedIndexChanged += new System.EventHandler(this.tabControl1_SelectedIndexChanged);
+            // 
+            // tabSystemState
+            // 
+            this.tabSystemState.Controls.Add(this.splitContainer1);
+            this.tabSystemState.ImageKey = "state.ico";
+            this.tabSystemState.Location = new System.Drawing.Point(4, 29);
+            this.tabSystemState.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.tabSystemState.Name = "tabSystemState";
+            this.tabSystemState.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.tabSystemState.Size = new System.Drawing.Size(797, 326);
+            this.tabSystemState.TabIndex = 0;
+            this.tabSystemState.Text = "系统状态";
+            this.tabSystemState.UseVisualStyleBackColor = true;
+            // 
+            // splitContainer1
+            // 
+            this.splitContainer1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+            this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.splitContainer1.Location = new System.Drawing.Point(3, 4);
+            this.splitContainer1.Name = "splitContainer1";
+            // 
+            // splitContainer1.Panel1
+            // 
+            this.splitContainer1.Panel1.Controls.Add(this.gbDeviceImage);
+            // 
+            // splitContainer1.Panel2
+            // 
+            this.splitContainer1.Panel2.Controls.Add(this.gbStateInfo);
+            this.splitContainer1.Size = new System.Drawing.Size(791, 318);
+            this.splitContainer1.SplitterDistance = 395;
+            this.splitContainer1.TabIndex = 0;
+            // 
+            // gbDeviceImage
+            // 
+            this.gbDeviceImage.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.gbDeviceImage.Controls.Add(this.picDTU);
+            this.gbDeviceImage.Location = new System.Drawing.Point(13, 16);
+            this.gbDeviceImage.Name = "gbDeviceImage";
+            this.gbDeviceImage.Size = new System.Drawing.Size(366, 288);
+            this.gbDeviceImage.TabIndex = 1;
+            this.gbDeviceImage.TabStop = false;
+            this.gbDeviceImage.Text = "设备图片";
+            // 
+            // picDTU
+            // 
+            this.picDTU.BackColor = System.Drawing.Color.Transparent;
+            this.picDTU.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom;
+            this.picDTU.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.picDTU.Location = new System.Drawing.Point(3, 22);
+            this.picDTU.Name = "picDTU";
+            this.picDTU.Size = new System.Drawing.Size(360, 263);
+            this.picDTU.TabIndex = 1;
+            this.picDTU.TabStop = false;
+            // 
+            // gbStateInfo
+            // 
+            this.gbStateInfo.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.gbStateInfo.Controls.Add(this.btnRefreshState);
+            this.gbStateInfo.Controls.Add(this.gridDTU);
+            this.gbStateInfo.Location = new System.Drawing.Point(13, 16);
+            this.gbStateInfo.Name = "gbStateInfo";
+            this.gbStateInfo.Size = new System.Drawing.Size(366, 288);
+            this.gbStateInfo.TabIndex = 1;
+            this.gbStateInfo.TabStop = false;
+            this.gbStateInfo.Text = "状态信息";
+            // 
+            // btnRefreshState
+            // 
+            this.btnRefreshState.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.btnRefreshState.Location = new System.Drawing.Point(285, 253);
+            this.btnRefreshState.Name = "btnRefreshState";
+            this.btnRefreshState.Size = new System.Drawing.Size(75, 28);
+            this.btnRefreshState.TabIndex = 2;
+            this.btnRefreshState.Text = "刷新";
+            this.btnRefreshState.UseVisualStyleBackColor = true;
+            this.btnRefreshState.Click += new System.EventHandler(this.btnRefreshState_Click);
+            // 
+            // gridDTU
+            // 
+            this.gridDTU.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.gridDTU.DisabledItemForeColor = System.Drawing.SystemColors.ControlDarkDark;
+            this.gridDTU.HelpVisible = false;
+            this.gridDTU.Location = new System.Drawing.Point(3, 22);
+            this.gridDTU.Name = "gridDTU";
+            this.gridDTU.PropertySort = System.Windows.Forms.PropertySort.Categorized;
+            this.gridDTU.Size = new System.Drawing.Size(360, 225);
+            this.gridDTU.TabIndex = 1;
+            this.gridDTU.ToolbarVisible = false;
+            this.gridDTU.ViewBorderColor = System.Drawing.Color.White;
+            // 
+            // tabDeviceConfig
+            // 
+            this.tabDeviceConfig.Controls.Add(this.btnExport);
+            this.tabDeviceConfig.Controls.Add(this.btnRead);
+            this.tabDeviceConfig.Controls.Add(this.btnImport);
+            this.tabDeviceConfig.Controls.Add(this.btnDownload);
+            this.tabDeviceConfig.Controls.Add(this.gridConfig);
+            this.tabDeviceConfig.ImageKey = "config.ico";
+            this.tabDeviceConfig.Location = new System.Drawing.Point(4, 29);
+            this.tabDeviceConfig.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.tabDeviceConfig.Name = "tabDeviceConfig";
+            this.tabDeviceConfig.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.tabDeviceConfig.Size = new System.Drawing.Size(797, 326);
+            this.tabDeviceConfig.TabIndex = 1;
+            this.tabDeviceConfig.Text = "设备配置";
+            this.tabDeviceConfig.UseVisualStyleBackColor = true;
+            // 
+            // btnExport
+            // 
+            this.btnExport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.btnExport.Location = new System.Drawing.Point(470, 291);
+            this.btnExport.Name = "btnExport";
+            this.btnExport.Size = new System.Drawing.Size(75, 28);
+            this.btnExport.TabIndex = 2;
+            this.btnExport.Text = "导出";
+            this.btnExport.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText;
+            this.btnExport.UseVisualStyleBackColor = true;
+            this.btnExport.Click += new System.EventHandler(this.btnExport_Click);
+            // 
+            // btnRead
+            // 
+            this.btnRead.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.btnRead.Location = new System.Drawing.Point(636, 291);
+            this.btnRead.Name = "btnRead";
+            this.btnRead.Size = new System.Drawing.Size(75, 28);
+            this.btnRead.TabIndex = 2;
+            this.btnRead.Text = "读取";
+            this.btnRead.UseVisualStyleBackColor = true;
+            this.btnRead.Click += new System.EventHandler(this.btnRead_Click);
+            // 
+            // btnImport
+            // 
+            this.btnImport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.btnImport.Location = new System.Drawing.Point(553, 291);
+            this.btnImport.Name = "btnImport";
+            this.btnImport.Size = new System.Drawing.Size(75, 28);
+            this.btnImport.TabIndex = 2;
+            this.btnImport.Text = "导入";
+            this.btnImport.UseVisualStyleBackColor = true;
+            this.btnImport.Click += new System.EventHandler(this.btnImport_Click);
+            // 
+            // btnDownload
+            // 
+            this.btnDownload.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.btnDownload.Location = new System.Drawing.Point(719, 291);
+            this.btnDownload.Name = "btnDownload";
+            this.btnDownload.Size = new System.Drawing.Size(75, 28);
+            this.btnDownload.TabIndex = 2;
+            this.btnDownload.Text = "下发";
+            this.btnDownload.UseVisualStyleBackColor = true;
+            this.btnDownload.Click += new System.EventHandler(this.btnDownload_Click);
+            // 
+            // gridConfig
+            // 
+            this.gridConfig.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.gridConfig.Location = new System.Drawing.Point(3, 4);
+            this.gridConfig.Name = "gridConfig";
+            this.gridConfig.PropertySort = System.Windows.Forms.PropertySort.Categorized;
+            this.gridConfig.Size = new System.Drawing.Size(791, 281);
+            this.gridConfig.TabIndex = 0;
+            this.gridConfig.ToolbarVisible = false;
+            // 
+            // tabDeviceManage
+            // 
+            this.tabDeviceManage.AutoScroll = true;
+            this.tabDeviceManage.Controls.Add(this.gbModifyPassword);
+            this.tabDeviceManage.Controls.Add(this.gbRestartDevice);
+            this.tabDeviceManage.Controls.Add(this.gbUpgradeFirmware);
+            this.tabDeviceManage.ImageKey = "manage.ico";
+            this.tabDeviceManage.Location = new System.Drawing.Point(4, 29);
+            this.tabDeviceManage.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.tabDeviceManage.Name = "tabDeviceManage";
+            this.tabDeviceManage.Size = new System.Drawing.Size(797, 326);
+            this.tabDeviceManage.TabIndex = 2;
+            this.tabDeviceManage.Text = "设备管理";
+            this.tabDeviceManage.UseVisualStyleBackColor = true;
+            // 
+            // gbModifyPassword
+            // 
+            this.gbModifyPassword.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.gbModifyPassword.Controls.Add(this.textBox3);
+            this.gbModifyPassword.Controls.Add(this.textBox2);
+            this.gbModifyPassword.Controls.Add(this.lbConfirmNewPassword);
+            this.gbModifyPassword.Controls.Add(this.btnModify);
+            this.gbModifyPassword.Controls.Add(this.lbNewPassword);
+            this.gbModifyPassword.Location = new System.Drawing.Point(15, 197);
+            this.gbModifyPassword.Name = "gbModifyPassword";
+            this.gbModifyPassword.Size = new System.Drawing.Size(768, 85);
+            this.gbModifyPassword.TabIndex = 0;
+            this.gbModifyPassword.TabStop = false;
+            this.gbModifyPassword.Text = "修改密码";
+            // 
+            // textBox3
+            // 
+            this.textBox3.Location = new System.Drawing.Point(487, 35);
+            this.textBox3.Name = "textBox3";
+            this.textBox3.PasswordChar = '*';
+            this.textBox3.Size = new System.Drawing.Size(151, 26);
+            this.textBox3.TabIndex = 3;
+            // 
+            // textBox2
+            // 
+            this.textBox2.Location = new System.Drawing.Point(139, 35);
+            this.textBox2.Name = "textBox2";
+            this.textBox2.PasswordChar = '*';
+            this.textBox2.Size = new System.Drawing.Size(151, 26);
+            this.textBox2.TabIndex = 3;
+            // 
+            // lbConfirmNewPassword
+            // 
+            this.lbConfirmNewPassword.Location = new System.Drawing.Point(302, 38);
+            this.lbConfirmNewPassword.Name = "lbConfirmNewPassword";
+            this.lbConfirmNewPassword.Size = new System.Drawing.Size(166, 20);
+            this.lbConfirmNewPassword.TabIndex = 2;
+            this.lbConfirmNewPassword.Text = "确认新密码";
+            this.lbConfirmNewPassword.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+            // 
+            // btnModify
+            // 
+            this.btnModify.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+            this.btnModify.Location = new System.Drawing.Point(668, 34);
+            this.btnModify.Name = "btnModify";
+            this.btnModify.Size = new System.Drawing.Size(75, 28);
+            this.btnModify.TabIndex = 1;
+            this.btnModify.Text = "修改";
+            this.btnModify.UseVisualStyleBackColor = true;
+            // 
+            // lbNewPassword
+            // 
+            this.lbNewPassword.Location = new System.Drawing.Point(16, 38);
+            this.lbNewPassword.Name = "lbNewPassword";
+            this.lbNewPassword.Size = new System.Drawing.Size(106, 20);
+            this.lbNewPassword.TabIndex = 2;
+            this.lbNewPassword.Text = "新密码";
+            this.lbNewPassword.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+            // 
+            // gbRestartDevice
+            // 
+            this.gbRestartDevice.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.gbRestartDevice.Controls.Add(this.btnRestart);
+            this.gbRestartDevice.Location = new System.Drawing.Point(15, 106);
+            this.gbRestartDevice.Name = "gbRestartDevice";
+            this.gbRestartDevice.Size = new System.Drawing.Size(768, 85);
+            this.gbRestartDevice.TabIndex = 0;
+            this.gbRestartDevice.TabStop = false;
+            this.gbRestartDevice.Text = "重启设备";
+            // 
+            // btnRestart
+            // 
+            this.btnRestart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+            this.btnRestart.Location = new System.Drawing.Point(668, 33);
+            this.btnRestart.Name = "btnRestart";
+            this.btnRestart.Size = new System.Drawing.Size(75, 28);
+            this.btnRestart.TabIndex = 1;
+            this.btnRestart.Text = "重启";
+            this.btnRestart.UseVisualStyleBackColor = true;
+            // 
+            // gbUpgradeFirmware
+            // 
+            this.gbUpgradeFirmware.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.gbUpgradeFirmware.Controls.Add(this.lbProgressVal);
+            this.gbUpgradeFirmware.Controls.Add(this.progressBar1);
+            this.gbUpgradeFirmware.Controls.Add(this.txtFilePath);
+            this.gbUpgradeFirmware.Controls.Add(this.lbUpgradeProgress);
+            this.gbUpgradeFirmware.Controls.Add(this.lbChooseFile);
+            this.gbUpgradeFirmware.Controls.Add(this.btnBrowse);
+            this.gbUpgradeFirmware.Controls.Add(this.btnUpgrade);
+            this.gbUpgradeFirmware.Location = new System.Drawing.Point(15, 15);
+            this.gbUpgradeFirmware.Name = "gbUpgradeFirmware";
+            this.gbUpgradeFirmware.Size = new System.Drawing.Size(768, 85);
+            this.gbUpgradeFirmware.TabIndex = 0;
+            this.gbUpgradeFirmware.TabStop = false;
+            this.gbUpgradeFirmware.Text = "更新固件";
+            // 
+            // progressBar1
+            // 
+            this.progressBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.progressBar1.Location = new System.Drawing.Point(139, 53);
+            this.progressBar1.Name = "progressBar1";
+            this.progressBar1.Size = new System.Drawing.Size(451, 20);
+            this.progressBar1.TabIndex = 4;
+            // 
+            // txtFilePath
+            // 
+            this.txtFilePath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.txtFilePath.Location = new System.Drawing.Point(139, 21);
+            this.txtFilePath.Name = "txtFilePath";
+            this.txtFilePath.ReadOnly = true;
+            this.txtFilePath.Size = new System.Drawing.Size(451, 26);
+            this.txtFilePath.TabIndex = 3;
+            // 
+            // lbUpgradeProgress
+            // 
+            this.lbUpgradeProgress.Location = new System.Drawing.Point(16, 53);
+            this.lbUpgradeProgress.Name = "lbUpgradeProgress";
+            this.lbUpgradeProgress.Size = new System.Drawing.Size(106, 20);
+            this.lbUpgradeProgress.TabIndex = 2;
+            this.lbUpgradeProgress.Text = "更新进度";
+            this.lbUpgradeProgress.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+            // 
+            // lbChooseFile
+            // 
+            this.lbChooseFile.Location = new System.Drawing.Point(16, 24);
+            this.lbChooseFile.Name = "lbChooseFile";
+            this.lbChooseFile.Size = new System.Drawing.Size(106, 20);
+            this.lbChooseFile.TabIndex = 2;
+            this.lbChooseFile.Text = "选择文件";
+            this.lbChooseFile.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+            // 
+            // btnBrowse
+            // 
+            this.btnBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+            this.btnBrowse.Location = new System.Drawing.Point(596, 22);
+            this.btnBrowse.Name = "btnBrowse";
+            this.btnBrowse.Size = new System.Drawing.Size(42, 25);
+            this.btnBrowse.TabIndex = 1;
+            this.btnBrowse.Text = "...";
+            this.btnBrowse.TextAlign = System.Drawing.ContentAlignment.TopCenter;
+            this.btnBrowse.UseVisualStyleBackColor = true;
+            this.btnBrowse.Click += new System.EventHandler(this.btnBrowse_Click);
+            // 
+            // btnUpgrade
+            // 
+            this.btnUpgrade.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+            this.btnUpgrade.Location = new System.Drawing.Point(668, 20);
+            this.btnUpgrade.Name = "btnUpgrade";
+            this.btnUpgrade.Size = new System.Drawing.Size(75, 28);
+            this.btnUpgrade.TabIndex = 1;
+            this.btnUpgrade.Text = "更新";
+            this.btnUpgrade.UseVisualStyleBackColor = true;
+            this.btnUpgrade.Click += new System.EventHandler(this.btnUpgrade_Click);
+            // 
+            // imageList1
+            // 
+            this.imageList1.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageList1.ImageStream")));
+            this.imageList1.TransparentColor = System.Drawing.Color.Transparent;
+            this.imageList1.Images.SetKeyName(0, "config.ico");
+            this.imageList1.Images.SetKeyName(1, "manage.ico");
+            this.imageList1.Images.SetKeyName(2, "state.ico");
+            // 
+            // lbProgressVal
+            // 
+            this.lbProgressVal.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+            this.lbProgressVal.AutoSize = true;
+            this.lbProgressVal.Location = new System.Drawing.Point(596, 53);
+            this.lbProgressVal.Name = "lbProgressVal";
+            this.lbProgressVal.Size = new System.Drawing.Size(29, 20);
+            this.lbProgressVal.TabIndex = 5;
+            this.lbProgressVal.Text = "0%";
+            // 
+            // UcDTUPanel
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.Controls.Add(this.tabControl1);
+            this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
+            this.Name = "UcDTUPanel";
+            this.Size = new System.Drawing.Size(805, 359);
+            this.tabControl1.ResumeLayout(false);
+            this.tabSystemState.ResumeLayout(false);
+            this.splitContainer1.Panel1.ResumeLayout(false);
+            this.splitContainer1.Panel2.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
+            this.splitContainer1.ResumeLayout(false);
+            this.gbDeviceImage.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.picDTU)).EndInit();
+            this.gbStateInfo.ResumeLayout(false);
+            this.tabDeviceConfig.ResumeLayout(false);
+            this.tabDeviceManage.ResumeLayout(false);
+            this.gbModifyPassword.ResumeLayout(false);
+            this.gbModifyPassword.PerformLayout();
+            this.gbRestartDevice.ResumeLayout(false);
+            this.gbUpgradeFirmware.ResumeLayout(false);
+            this.gbUpgradeFirmware.PerformLayout();
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.TabControl tabControl1;
+        private System.Windows.Forms.TabPage tabSystemState;
+        private System.Windows.Forms.TabPage tabDeviceConfig;
+        private System.Windows.Forms.TabPage tabDeviceManage;
+        private System.Windows.Forms.GroupBox gbUpgradeFirmware;
+        private System.Windows.Forms.Button btnUpgrade;
+        private System.Windows.Forms.TextBox txtFilePath;
+        private System.Windows.Forms.Label lbChooseFile;
+        private System.Windows.Forms.Button btnBrowse;
+        private System.Windows.Forms.GroupBox gbRestartDevice;
+        private System.Windows.Forms.Button btnRestart;
+        private System.Windows.Forms.GroupBox gbModifyPassword;
+        private System.Windows.Forms.Button btnModify;
+        private System.Windows.Forms.TextBox textBox3;
+        private System.Windows.Forms.TextBox textBox2;
+        private System.Windows.Forms.Label lbConfirmNewPassword;
+        private System.Windows.Forms.Label lbNewPassword;
+        private System.Windows.Forms.SplitContainer splitContainer1;
+        private System.Windows.Forms.GroupBox gbDeviceImage;
+        private System.Windows.Forms.PictureBox picDTU;
+        private System.Windows.Forms.GroupBox gbStateInfo;
+        private System.Windows.Forms.PropertyGrid gridDTU;
+        private System.Windows.Forms.PropertyGrid gridConfig;
+        private System.Windows.Forms.Button btnDownload;
+        private System.Windows.Forms.Button btnExport;
+        private System.Windows.Forms.Button btnImport;
+        private System.Windows.Forms.Button btnRead;
+        private System.Windows.Forms.ImageList imageList1;
+        private System.Windows.Forms.Button btnRefreshState;
+        private System.Windows.Forms.ProgressBar progressBar1;
+        private System.Windows.Forms.Label lbUpgradeProgress;
+        private System.Windows.Forms.Label lbProgressVal;
+    }
+}

+ 463 - 0
GatewayTool/UserControls/UcDTUPanel.cs

@@ -0,0 +1,463 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using Newtonsoft.Json;
+using GatewayTool.Models;
+using Newtonsoft.Json.Serialization;
+using WjwLib.API.File;
+using GatewayTool.Protocols;
+
+namespace GatewayTool.UserControls
+{
+    public partial class UcDTUPanel : UserControl
+    {
+        private const string jsonFilter = "json(*.json)|*.json";
+        private const string binFilter = "bin(*.bin)|*.bin";
+
+        public event EventHandler<GridPropertyChangedArgs> ExecPropertyChanged;
+        public event EventHandler ExecBindObjChanged;
+        public event EventHandler ExecRefreshState;
+        public event EventHandler ExecReadConfig;
+        public event EventHandler<string> ExecDownloadConfig;
+        public event EventHandler<int> ExecSelectTabIndexChanged;
+        public event EventHandler<string> ExecUpgrade;
+
+        private StringMsg _StrMsg;
+        /// <summary>
+        /// 字符串信息资源
+        /// </summary>
+        public StringMsg StrMsg
+        {
+            get => _StrMsg;
+            set
+            {
+                _StrMsg = value;
+                if (_StrMsg != null)
+                {
+                    InitStrings();
+                }
+            }
+        }
+
+        private DTU _BindDTU;
+        /// <summary>
+        /// 绑定的DTU
+        /// </summary>
+        public DTU BindDTU
+        {
+            get => _BindDTU;
+            set => _BindDTU = value;
+        }
+
+        /// <summary>
+        /// 设备图片
+        /// </summary>
+        public Image DeviceImage
+        {
+            get => picDTU.BackgroundImage;
+            set
+            {
+                picDTU.BackgroundImage = value;
+            }
+        }
+
+        /// <summary>
+        /// 设备系统状态对象
+        /// </summary>
+        public Object DeviceSystemStateObj
+        {
+            get => gridDTU.SelectedObject;
+            set
+            {
+                gridDTU.SelectedObject = value;
+            }
+        }
+
+        /// <summary>
+        /// 设备配置对象
+        /// </summary>
+        public Object DeviceConfigObj
+        {
+            get => gridConfig.SelectedObject;
+            set
+            {
+                DTUConfig obj = value as DTUConfig;
+                HandleDTUConfigObj(ref obj);
+                gridConfig.SelectedObject = obj;
+            }
+        }
+
+        /// <summary>
+        /// 升级文件路径
+        /// </summary>
+        public string UpgradeFilePath 
+        {
+            get => this.txtFilePath.Text.Trim();
+            set 
+            {
+                this.txtFilePath.Text = value;
+                this.btnUpgrade.Enabled = !string.IsNullOrEmpty(value); //不为空时更新按钮才可用
+            }
+        }
+
+        /// <summary>
+        /// 固件升级进度
+        /// </summary>
+        public int UpgradeProgress
+        {
+            get => progressBar1.Value;
+            set
+            {
+                if (value <= progressBar1.Maximum)
+                {
+                    progressBar1.Value = value;
+                    lbProgressVal.Text = $"{value}%";
+                }
+            }
+        }
+
+        public UcDTUPanel(DTU dtu)
+        {
+            InitializeComponent();
+
+            //this.tabDeviceManage.Parent = null;  //隐藏设备管理选项卡
+            this.gbRestartDevice.Visible = false;  //隐藏重启设备区域
+            this.gbModifyPassword.Visible = false; //隐藏修改密码区域
+
+            this.Load += UcDTUPanel_Load;
+
+            _BindDTU = dtu;
+            this.DeviceImage = _BindDTU?.DeviceImage;
+            this.DeviceSystemStateObj = _BindDTU?.SystemState;
+            this.DeviceConfigObj = _BindDTU?.Config;
+
+            this.UpgradeFilePath = _BindDTU?.UpgradeInfo.FirmwareFilePath;
+            this.UpgradeProgress = _BindDTU == null ? 0 : _BindDTU.UpgradeInfo.UpgradeProgress;
+
+            this.gridConfig.PropertyValueChanged += GridConfig_PropertyValueChanged;
+            this.gridConfig.SelectedObjectsChanged += GridConfig_SelectedObjectsChanged;
+        }
+
+        private void UcDTUPanel_Load(object sender, EventArgs e)
+        {
+            InitStrings();
+        }
+
+        private void InitStrings()
+        {
+            this.tabSystemState.Text = _StrMsg.SystemState;
+            this.gbDeviceImage.Text = _StrMsg.DeviceImage;
+            this.gbStateInfo.Text = _StrMsg.StateInfo;
+            this.btnRefreshState.Text = _StrMsg.Refresh;
+
+            this.tabDeviceConfig.Text = _StrMsg.DeviceConfig;
+            this.btnExport.Text = _StrMsg.Export;
+            this.btnImport.Text = _StrMsg.Import;
+            this.btnRead.Text = _StrMsg.Read;
+            this.btnDownload.Text = _StrMsg.Write;
+
+            this.tabDeviceManage.Text = _StrMsg.DeviceManage;
+            this.gbUpgradeFirmware.Text = _StrMsg.UpgradeFirmware;
+            this.lbChooseFile.Text = _StrMsg.ChooseFile;
+            this.lbUpgradeProgress.Text = _StrMsg.UpgradeProgress;
+            this.btnUpgrade.Text = _StrMsg.Upgrade;
+            this.gbRestartDevice.Text = _StrMsg.RestartDevice;
+            this.btnRestart.Text = _StrMsg.Restart;
+            this.gbModifyPassword.Text = _StrMsg.ModifyPassword;
+            this.lbNewPassword.Text = _StrMsg.NewPassword;
+            this.lbConfirmNewPassword.Text = _StrMsg.ConfirmNewPassword;
+            this.btnModify.Text = _StrMsg.Modify;
+        }
+
+        private void GridConfig_SelectedObjectsChanged(object sender, EventArgs e)
+        {
+            this.ExecBindObjChanged?.Invoke(sender, e);
+        }
+
+        private void GridConfig_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
+        {
+            this.ExecPropertyChanged?.Invoke(this, new GridPropertyChangedArgs() { Obj = s, Args = e });
+        }
+
+        private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
+        {
+            //Rectangle mainRec = tabControl1.ClientRectangle;
+            //e.Graphics.FillRectangle(new SolidBrush(Color.White), mainRec);
+            //for (int i = 0; i < tabControl1.TabPages.Count; i++)
+            //{
+            //    Rectangle rec = tabControl1.GetTabRect(i);
+            //    if (tabControl1.SelectedTab == tabControl1.TabPages[i])
+            //    {
+            //        e.Graphics.FillRectangle(new SolidBrush(Color.DeepSkyBlue), rec);
+            //        e.Graphics.DrawString(tabControl1.TabPages[i].Text, new Font("黑体", 12F, FontStyle.Bold), new SolidBrush(Color.White), rec, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
+            //    }
+            //    else
+            //    {
+            //        e.Graphics.FillRectangle(new SolidBrush(Color.White), rec);
+            //        e.Graphics.DrawString(tabControl1.TabPages[i].Text, new Font("黑体", 12F, FontStyle.Bold), new SolidBrush(Color.Black), rec, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
+            //    }
+            //}
+        }
+
+        /// <summary>
+        /// 浏览bin文件按钮单击
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void btnBrowse_Click(object sender, EventArgs e)
+        {
+            string filePath;
+            string content = OpenBinFile(out filePath);
+            if (filePath != null) //非取消操作时
+            {
+                txtFilePath.Text = filePath;
+                this.btnUpgrade.Enabled = true;
+            }
+        }
+
+        /// <summary>
+        /// 更新按钮单击
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void btnUpgrade_Click(object sender, EventArgs e)
+        {           
+            ExecUpgrade?.Invoke(this, txtFilePath.Text.Trim());
+        }
+
+        /// <summary>
+        /// 打开BIN文件得到路径,读取其中的二进制内容
+        /// </summary>
+        /// <param name="filePath">打开的文件路径</param>
+        /// <returns>返回二进制数字符串</returns>       
+        private string OpenBinFile(out string filePath)
+        {
+            filePath = string.Empty;
+            string bin_str = "";
+            byte[] binchar = new byte[] { };
+            int file_len;
+
+            //打开文件类
+            OpenFileDialog dialog = new OpenFileDialog();
+            dialog.Title = _StrMsg.DialogOpen;
+            //使用当前目录作为初始目录
+            //dialog.InitialDirectory = System.AppDomain.CurrentDomain.BaseDirectory;
+            //文件过滤,仅打开bin
+            dialog.Filter = binFilter;
+            //关闭选择多文件
+            dialog.Multiselect = false;
+            dialog.RestoreDirectory = true;
+            DialogResult res = dialog.ShowDialog();
+            if (res == DialogResult.OK)
+            {
+                //获取打开文件的完整路径
+                filePath = dialog.FileName;
+                //文件流类//用于文件的读写与关闭//来自于System.IO 
+                FileStream fileStream = new FileStream(filePath, FileMode.Open);
+                //读二进制文件类
+                BinaryReader br = new BinaryReader(fileStream, Encoding.UTF8);
+                //获取bin文件长度
+                file_len = (int)fileStream.Length;
+                //得到所有字节
+                binchar = br.ReadBytes(file_len);
+                //累加每字节数组转字符串
+                bin_str = BitConverter.ToString(binchar);
+                //关闭 BinaryReader 对象和基础流
+                br.Close();
+            }
+            else if (res == DialogResult.Cancel)
+            {
+                filePath = null; //取消时置为null
+            }
+
+            //返回字符串
+            return bin_str;
+        }
+
+        /// <summary>
+        /// 导出按钮单击
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void btnExport_Click(object sender, EventArgs e)
+        {
+            string str = JsonConvert.SerializeObject(gridConfig.SelectedObject);
+            SaveFileDialog saveFileDialog = new SaveFileDialog();
+            saveFileDialog.Title = _StrMsg.DialogSaveAs;
+            saveFileDialog.Filter = jsonFilter;
+            saveFileDialog.DefaultExt = "*.json";
+            //saveFileDialog.InitialDirectory = Environment.CurrentDirectory;
+            saveFileDialog.RestoreDirectory = true;
+            if (saveFileDialog.ShowDialog() == DialogResult.OK)
+            {
+                bool res = PlainText.WriteFileContent(saveFileDialog.FileName, str);
+                if (res)
+                {
+                    MessageBox.Show(_StrMsg.ExportSuccess, _StrMsg.Prompt, MessageBoxButtons.OK, MessageBoxIcon.Information);
+                }
+                else
+                {
+                    MessageBox.Show(_StrMsg.ExportFail, _StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
+                }
+            }
+        }
+
+        /// <summary>
+        /// 导入按钮单击
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void btnImport_Click(object sender, EventArgs e)
+        {
+            OpenFileDialog dialog = new OpenFileDialog();
+            dialog.Title = _StrMsg.DialogOpen;
+            dialog.Filter = jsonFilter;
+            dialog.Multiselect = false;
+            dialog.RestoreDirectory = true;
+            if (dialog.ShowDialog() == DialogResult.OK)
+            {
+                try
+                {
+                    string str = PlainText.GetFileContent(dialog.FileName);
+                    DTUConfig obj = JsonConvert.DeserializeObject<DTUConfig>(str);
+                    if (obj == null)
+                    {
+                        MessageBox.Show(_StrMsg.ImportFail, _StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
+                    }
+                    else
+                    {
+                        DTUConfig rawConfig = (DTUConfig)gridConfig.SelectedObject; //原始配置对象
+                        obj.DeviceId = rawConfig.DeviceId; //获取原始网关设备ID,防止被导入值修改
+                        if (HandleDTUConfigObj(ref obj))
+                        {
+                            MessageBox.Show(_StrMsg.ImportSuccess, _StrMsg.Prompt, MessageBoxButtons.OK, MessageBoxIcon.Information);
+                        }
+                    }
+                    gridConfig.SelectedObject = obj;
+                }
+                catch
+                {
+                    gridConfig.SelectedObject = new DTUConfig();
+                    MessageBox.Show(_StrMsg.ParseFail, _StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
+                }
+            }
+        }
+
+        /// <summary>
+        /// 处理DTU配置对象及其子对象
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <returns></returns>
+        private bool HandleDTUConfigObj(ref DTUConfig obj) 
+        {
+            if (obj == null) return false;
+            SensorDataItemCollection sensorCollection = new SensorDataItemCollection();
+            foreach (var item in obj.SensorData)
+            {
+                string itemStr = item.ToString();
+                SensorDataItem dataItem = JsonConvert.DeserializeObject<SensorDataItem>(itemStr);
+                if (dataItem != null)
+                {
+                    sensorCollection.Add(dataItem);
+                }
+            }
+            obj.SensorData = sensorCollection;
+
+            CommandDataItemCollection commandCollection = new CommandDataItemCollection();
+            foreach (var item in obj.CommandData)
+            {
+                string itemStr = item.ToString();
+                CommandDataItem dataItem = JsonConvert.DeserializeObject<CommandDataItem>(itemStr);
+                if (dataItem != null)
+                {
+                    commandCollection.Add(dataItem);
+                }
+            }
+            obj.CommandData = commandCollection;
+            return true;
+        }
+
+        /// <summary>
+        /// 下发按钮单击
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void btnDownload_Click(object sender, EventArgs e)
+        {
+            if (gridConfig.SelectedObject == null)
+            {
+                ExecDownloadConfig?.Invoke(this, null);
+            }
+            else
+            {
+                DTUConfig config = (DTUConfig)gridConfig.SelectedObject;
+                for (int i = config.CommandData.Count - 1; i >= 0; i--)
+                {
+                    if (!config.CommandData[i].Validate())
+                    {
+                        MessageBox.Show($"CommandDataItem{i}中存在非法数值", "数据非法", MessageBoxButtons.OK, MessageBoxIcon.Error);
+                        return;
+                    }
+                }
+
+                string str = JsonConvert.SerializeObject(gridConfig.SelectedObject);
+                str += $"+QFDWL: {str.Length},{XorSum16.CheckSum(str)}";
+                MessageBox.Show(WjwLib.API.System.JsonHelper.ConvertJsonString(str));
+                ExecDownloadConfig?.Invoke(this, str);
+            }
+        }      
+
+        /// <summary>
+        /// 选项卡切换事件
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
+        {
+            ExecSelectTabIndexChanged?.Invoke(this, tabControl1.SelectedIndex);
+        }
+
+        /// <summary>
+        /// 刷新按钮单击
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void btnRefreshState_Click(object sender, EventArgs e)
+        {
+            ExecRefreshState?.Invoke(this, e);
+        }
+
+        /// <summary>
+        /// 读取按钮单击
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void btnRead_Click(object sender, EventArgs e)
+        {
+            ExecReadConfig?.Invoke(this, e);
+        }
+    }
+
+    /// <summary>
+    /// 表格属性更改参数
+    /// </summary>
+    public class GridPropertyChangedArgs
+    {
+        /// <summary>
+        /// 表格实例
+        /// </summary>
+        public object Obj { get; set; }
+
+        /// <summary>
+        /// 属性值更改事件参数
+        /// </summary>
+        public PropertyValueChangedEventArgs Args { get; set; }
+    }
+}

+ 197 - 0
GatewayTool/UserControls/UcDTUPanel.resx

@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="imageList1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <data name="imageList1.ImageStream" mimetype="application/x-microsoft.net.object.binary.base64">
+    <value>
+        AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
+        LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
+        ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACm
+        DwAAAk1TRnQBSQFMAgEBAwEAASgBAAEoAQABGAEAARgBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
+        AwABYAMAARgDAAEBAQABCAYAAQkYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA
+        AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5
+        AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA
+        AWYDAAGZAwABzAIAATMDAAIzAgABMwFmAgABMwGZAgABMwHMAgABMwH/AgABZgMAAWYBMwIAAmYCAAFm
+        AZkCAAFmAcwCAAFmAf8CAAGZAwABmQEzAgABmQFmAgACmQIAAZkBzAIAAZkB/wIAAcwDAAHMATMCAAHM
+        AWYCAAHMAZkCAALMAgABzAH/AgAB/wFmAgAB/wGZAgAB/wHMAQABMwH/AgAB/wEAATMBAAEzAQABZgEA
+        ATMBAAGZAQABMwEAAcwBAAEzAQAB/wEAAf8BMwIAAzMBAAIzAWYBAAIzAZkBAAIzAcwBAAIzAf8BAAEz
+        AWYCAAEzAWYBMwEAATMCZgEAATMBZgGZAQABMwFmAcwBAAEzAWYB/wEAATMBmQIAATMBmQEzAQABMwGZ
+        AWYBAAEzApkBAAEzAZkBzAEAATMBmQH/AQABMwHMAgABMwHMATMBAAEzAcwBZgEAATMBzAGZAQABMwLM
+        AQABMwHMAf8BAAEzAf8BMwEAATMB/wFmAQABMwH/AZkBAAEzAf8BzAEAATMC/wEAAWYDAAFmAQABMwEA
+        AWYBAAFmAQABZgEAAZkBAAFmAQABzAEAAWYBAAH/AQABZgEzAgABZgIzAQABZgEzAWYBAAFmATMBmQEA
+        AWYBMwHMAQABZgEzAf8BAAJmAgACZgEzAQADZgEAAmYBmQEAAmYBzAEAAWYBmQIAAWYBmQEzAQABZgGZ
+        AWYBAAFmApkBAAFmAZkBzAEAAWYBmQH/AQABZgHMAgABZgHMATMBAAFmAcwBmQEAAWYCzAEAAWYBzAH/
+        AQABZgH/AgABZgH/ATMBAAFmAf8BmQEAAWYB/wHMAQABzAEAAf8BAAH/AQABzAEAApkCAAGZATMBmQEA
+        AZkBAAGZAQABmQEAAcwBAAGZAwABmQIzAQABmQEAAWYBAAGZATMBzAEAAZkBAAH/AQABmQFmAgABmQFm
+        ATMBAAGZATMBZgEAAZkBZgGZAQABmQFmAcwBAAGZATMB/wEAApkBMwEAApkBZgEAA5kBAAKZAcwBAAKZ
+        Af8BAAGZAcwCAAGZAcwBMwEAAWYBzAFmAQABmQHMAZkBAAGZAswBAAGZAcwB/wEAAZkB/wIAAZkB/wEz
+        AQABmQHMAWYBAAGZAf8BmQEAAZkB/wHMAQABmQL/AQABzAMAAZkBAAEzAQABzAEAAWYBAAHMAQABmQEA
+        AcwBAAHMAQABmQEzAgABzAIzAQABzAEzAWYBAAHMATMBmQEAAcwBMwHMAQABzAEzAf8BAAHMAWYCAAHM
+        AWYBMwEAAZkCZgEAAcwBZgGZAQABzAFmAcwBAAGZAWYB/wEAAcwBmQIAAcwBmQEzAQABzAGZAWYBAAHM
+        ApkBAAHMAZkBzAEAAcwBmQH/AQACzAIAAswBMwEAAswBZgEAAswBmQEAA8wBAALMAf8BAAHMAf8CAAHM
+        Af8BMwEAAZkB/wFmAQABzAH/AZkBAAHMAf8BzAEAAcwC/wEAAcwBAAEzAQAB/wEAAWYBAAH/AQABmQEA
+        AcwBMwIAAf8CMwEAAf8BMwFmAQAB/wEzAZkBAAH/ATMBzAEAAf8BMwH/AQAB/wFmAgAB/wFmATMBAAHM
+        AmYBAAH/AWYBmQEAAf8BZgHMAQABzAFmAf8BAAH/AZkCAAH/AZkBMwEAAf8BmQFmAQAB/wKZAQAB/wGZ
+        AcwBAAH/AZkB/wEAAf8BzAIAAf8BzAEzAQAB/wHMAWYBAAH/AcwBmQEAAf8CzAEAAf8BzAH/AQAC/wEz
+        AQABzAH/AWYBAAL/AZkBAAL/AcwBAAJmAf8BAAFmAf8BZgEAAWYC/wEAAf8CZgEAAf8BZgH/AQAC/wFm
+        AQABIQEAAaUBAANfAQADdwEAA4YBAAOWAQADywEAA7IBAAPXAQAD3QEAA+MBAAPqAQAD8QEAA/gBAAHw
+        AfsB/wEAAaQCoAEAA4ADAAH/AgAB/wMAAv8BAAH/AwAB/wEAAf8BAAL/AgAD/wYAAfQB9wHsBW0B6gEH
+        B/IB9AIABf8BbA+uAfcEABHyAf8gAAHvAfcB9AHxAfACvAG7AZEBrgeRAfcBAAH/AfME7wGuBt0ICQG1
+        AfcEAAGuBbUDtAeRAa4B8h4AAfcCZgH3ARkC1QKtAbwBkQG1Bt0BtQH3AQAB/wHyAbsDvAHsAhkCRwGT
+        ARkCrAEZAZgCLQLdAbUB9wQAAa4BGQLzBRkH3QGRAfIeAAH3AfMBBwG1AfQBGQHdAgkB3QGRAYoBGQGY
+        Ai0C3QG1AfcBAAH/AfIBBwEJAbQBvAGSAvMCTQEHARkCrAEZAZgCLQLdAbUB9wQAAa4CGQHzAU0BRwEZ
+        AdwCrAEZAXEBLQFxAt0BkQHyGwABvAP3ARkBCQG1AfQBGQLdAQkB8QGRAawBGQGYAS0BLgLdAbUB9wEA
+        Af8B8gG8Aa0B1QHPAe8C9AJ1ARoB8wLTARkBmAIuAt0BtQH3BAABrgEZAfMBGQJNARkB3AKsARkBlwEt
+        AZcC3QGRAfIbAAH3AfEC9wL/AbUBGQLVAq0B8wGRAawBGQGYAlUC3QG1AfcBAAH/AfIBvAH0Aa0B1QGt
+        Av8BmgP0AtQBGQGYAlUC3QG1AfcEAAGuARkB9AEZAk0B8wHcAtMBGQGXAVUBlwLdAZEB8hsAAfcB9AEZ
+        AbUBGQEJAbsB9AEZAd0BGQH/AfABkQGzARkBmAJVARkB3QG1AfcBAAH/AfIBBwH0Ad0BtAHVAbQB8wFY
+        AZoBGwL0AvMBCAJWARkB3QG1AfcEAAGuAfMD9ALzAQkC2gEZAZcBVQGXAt0BkQHyGwAB9wH0ARkBtQEZ
+        AQkBBwH0ARkB3QEZAfABrgHqAbUBGQEIAlYBGQHdAbUB9wIAAfMBBwL0AfEBzwHVAbQBcwJ5AXoB8wH0
+        AfMB8AIIAhkBtQH3BAABrgHzBPQBGQLzAhkBeAFWAXgBGQHdAZEB8hsAAbUBGQHVAbsC/wG7BP8CvAGu
+        AfAB8wUZAbUB9wIAAfMBBwL0Af8B8QG0AdUBrQFzAVgCwwHyAZIC7AG8AhkBuwH3BAABrgHzBfQBGQPz
+        BRkBkQHyGwABtQH0ARkBBwEZAgkE8wEHAa4B8QO8AQcB8QIZAbsB9wIAAfMBvAL0ARoCeQHtAd0B7AF6
+        AXkBGgHwAesBbQESAfcCGQEJAfcEAAHsAfQB8wS8AfMB8gO8AQcB8QIZAbQB8hsAAe8B9AEZAbsC/wLz
+        AgcBkQFtAZIBvAESAxUBkgIZAbsB9wIAAfMBvAH/AfIBmQFSAXoBeQJ0AVgBWQGaARsC9APzARkBCQH3
+        BAABkQH0AQcEFQLvBBUB7wIZAbQB8hsAAbsBGQHVAQkE/wEHAWYB8QX0A/MCGQEJAfcCAAHzAbwD/wHw
+        AXQBegFZAaABeQGZAcMB8gHtAuwBvALzAQkB9wQAAewB9AH/B/QBGQPzAhkBtQHyGwABuwL/AvACBwH3
+        Au8CvAHyAfMEvAHxAvMBCQH3AgAB8wG8Av8B8QG8ApoBWAKaAcMB8gG8AesBbQHqAe8BGQHzAQkB9wQA
+        AZEC9AS8AvMEvAHxAfMBGQG1AfIbAAEHBP8BBwGuAfcB6gMUAfcB8AHqAxQB9wEZAfMBCQH3AgAB8wHw
+        Af8B8QHrAe8B9gHDAZoBwwH0Av8G9AEZAQkB9wQAAe0B9AEHBBQCBwQUAQcC8wG1AfIbAAQHAbsB9wG8
+        Bf8H9AEZAfMBCQH3AgAB8wHwA/8B7wL/AfEB7wH3AZIC8QPsAbwC9AEJAfcEAAHtBf8I9AEZAfMBtQHy
+        IAABvAL/AfEDvAHzAfQB8AO8AfIB9AHzAQkB9wIAAfMB8AL/AfABtQL/AbwD7AK8A+sB7wL0AQkB9wQA
+        AfcB/wH0AfADvALzBLwB8wH0ARkB9wHyIAABvAH/AfIBbQMTAfcB8QFtAxMB9wL0AQkB9wIAAfMB8QH/
+        AfIBbQG1Cf8D9AG8AQcBtQH3BAAB9wH/AbwEEwIHBBMBBwL0AfcB8iAAAbwJ/wb0AQkB9wIAAfMB8QP/
+        AbUC/wG8BuwB8QH0AZEDZgH3BAAB9wn/BvQB9wHyIAABvAL/AfEF8AK8AfMB9AG8ApEBrgH3AgAB8wHx
+        Af8B9AHwAe8C/wG8BuwBvAH/AZEBvAL3BQABtQH/AfQF8AK8AfAC9AEHAZECrgHyIAABvAH/AfMB7APq
+        BBIBvAH0AewBkgGRAWYB9AIAAfMB8gH/AfMB7AG1C/8BkQK8BgABtQH/AbwE6gMSAesB8QH0Au0BkQHt
+        Af8gAAHwDP8BkQHwAZEB/wMAAfMB8gP/Ae8G/wT0AfMB7QEHBwAB7wz/AbUBvAFsIgAB8Ar/AvQBkQEH
+        AfQEAAHzAfID/wO8BAcBtQHtA7wBBwgAAbUK/wL0AfcBBwH/IgAB8AW8BQcC7wHtAfQFAAHzAQcDvAUH
+        Au8BtQG8DAAB7wW8BQcD7wH/fgABQgFNAT4HAAE+AwABKAMAAWADAAEYAwABAQEAAQEFAAEgAQEWAAP/
+        AQAB+AEAAQEBgAEAAQEB4AEAAQcDAAH4AQABAQIAAQEB4AEAAQcDAAHgAQABAQIAAQEB4AEAAQcDAAHg
+        AQABAQIAAQEB4AEAAQcFAAEBAgABAQHgAQABBwUAAQECAAEBAeABAAEHBQABAQIAAQEB4AEAAQcFAAEB
+        AYABAAEBAeABAAEHBQABAQGAAQABAQHgAQABBwUAAQEBgAEAAQEB4AEAAQcFAAEBAYABAAEBAeABAAEH
+        BQABAQGAAQABAQHgAQABBwUAAQEBgAEAAQEB4AEAAQcFAAEBAYABAAEBAeABAAEHBQABAQGAAQABAQHg
+        AQABBwMAAfgBAAEBAYABAAEBAeABAAEHAwAB+AEAAQEBgAEAAQEB4AEAAQcDAAH4AQABAQGAAQABAQHg
+        AQABBwMAAfgBAAEBAYABAAEDAeABAAEHAwAB+AEAAQEBgAEAAQcB4AEAAQcDAAH4AQABAwGAAQABDwHg
+        AQABHwMAAfgBAAEHAYABAAEfAeABAAEfAwAB+AEAAQ8BgAEBAf8B4AEAAT8DAAn/AwAL
+</value>
+  </data>
+</root>

+ 108 - 0
GatewayTool/UserControls/UcDeviceList.Designer.cs

@@ -0,0 +1,108 @@
+
+namespace GatewayTool.UserControls
+{
+    partial class UcDeviceList
+    {
+        /// <summary> 
+        /// 必需的设计器变量。
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary> 
+        /// 清理所有正在使用的资源。
+        /// </summary>
+        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region 组件设计器生成的代码
+
+        /// <summary> 
+        /// 设计器支持所需的方法 - 不要修改
+        /// 使用代码编辑器修改此方法的内容。
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.components = new System.ComponentModel.Container();
+            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(UcDeviceList));
+            this.panel1 = new System.Windows.Forms.Panel();
+            this.listView1 = new System.Windows.Forms.ListView();
+            this.lbDeviceList = new System.Windows.Forms.Label();
+            this.imageList1 = new System.Windows.Forms.ImageList(this.components);
+            this.panel1.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // panel1
+            // 
+            this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
+            this.panel1.Controls.Add(this.listView1);
+            this.panel1.Controls.Add(this.lbDeviceList);
+            this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.panel1.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.panel1.Location = new System.Drawing.Point(0, 0);
+            this.panel1.Margin = new System.Windows.Forms.Padding(2);
+            this.panel1.Name = "panel1";
+            this.panel1.Size = new System.Drawing.Size(120, 122);
+            this.panel1.TabIndex = 1;
+            // 
+            // listView1
+            // 
+            this.listView1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.listView1.FullRowSelect = true;
+            this.listView1.HideSelection = false;
+            this.listView1.Location = new System.Drawing.Point(0, 25);
+            this.listView1.Name = "listView1";
+            this.listView1.Size = new System.Drawing.Size(116, 93);
+            this.listView1.TabIndex = 1;
+            this.listView1.UseCompatibleStateImageBehavior = false;
+            this.listView1.View = System.Windows.Forms.View.Details;
+            this.listView1.SizeChanged += new System.EventHandler(this.listView1_SizeChanged);
+            this.listView1.MouseClick += new System.Windows.Forms.MouseEventHandler(this.listView1_MouseClick);
+            // 
+            // lbDeviceList
+            // 
+            this.lbDeviceList.BackColor = System.Drawing.SystemColors.ActiveCaption;
+            this.lbDeviceList.Dock = System.Windows.Forms.DockStyle.Top;
+            this.lbDeviceList.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.lbDeviceList.Location = new System.Drawing.Point(0, 0);
+            this.lbDeviceList.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+            this.lbDeviceList.Name = "lbDeviceList";
+            this.lbDeviceList.Size = new System.Drawing.Size(116, 25);
+            this.lbDeviceList.TabIndex = 0;
+            this.lbDeviceList.Text = "设备列表";
+            this.lbDeviceList.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+            // 
+            // imageList1
+            // 
+            this.imageList1.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageList1.ImageStream")));
+            this.imageList1.TransparentColor = System.Drawing.Color.Transparent;
+            this.imageList1.Images.SetKeyName(0, "common");
+            this.imageList1.Images.SetKeyName(1, "dtu");
+            // 
+            // UcDeviceList
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
+            this.Controls.Add(this.panel1);
+            this.Margin = new System.Windows.Forms.Padding(2);
+            this.Name = "UcDeviceList";
+            this.Size = new System.Drawing.Size(120, 122);
+            this.panel1.ResumeLayout(false);
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Panel panel1;
+        private System.Windows.Forms.Label lbDeviceList;
+        private System.Windows.Forms.ImageList imageList1;
+        private System.Windows.Forms.ListView listView1;
+    }
+}

+ 157 - 0
GatewayTool/UserControls/UcDeviceList.cs

@@ -0,0 +1,157 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using GatewayTool.Models;
+
+namespace GatewayTool.UserControls
+{
+    public partial class UcDeviceList : UserControl
+    {
+        public event EventHandler<ListViewItem> ExecItemClicked;
+
+        /// <summary>
+        /// 模块名称(标题)
+        /// </summary>
+        public string ModuleName
+        {
+            get => lbDeviceList.Text;
+            set => lbDeviceList.Text = value;
+        }
+
+        /// <summary>
+        /// 序号
+        /// </summary>
+        public string ID
+        {
+            get => this.listView1.Columns[0].Text;
+            set => this.listView1.Columns[0].Text = value;
+        }
+
+        /// <summary>
+        /// 设备编号
+        /// </summary>
+        public string SN
+        {
+            get => this.listView1.Columns[1].Text;
+            set => this.listView1.Columns[1].Text = value;
+        }
+
+        /// <summary>
+        /// 数据来源
+        /// </summary>
+        public string Source
+        {
+            get => this.listView1.Columns[2].Text;
+            set => this.listView1.Columns[2].Text = value;
+        }
+
+        /// <summary>
+        /// 固件版本
+        /// </summary>
+        public string FirmwareVersion
+        {
+            get => this.listView1.Columns[3].Text;
+            set => this.listView1.Columns[3].Text = value;
+        }
+
+        //自动调整ListView的列宽的方法
+        public void AutoResizeColumnWidth(ListView lv)
+        {
+            int count = lv.Columns.Count;
+            int MaxWidth = 0;
+            Graphics graphics = lv.CreateGraphics();
+            int width;
+            lv.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
+            for (int i = 0; i < count; i++)
+            {
+                string str = lv.Columns[i].Text;
+                MaxWidth = lv.Columns[i].Width;
+
+                foreach (ListViewItem item in lv.Items)
+                {
+                    str = item.SubItems[i].Text;
+                    width = (int)graphics.MeasureString(str, lv.Font).Width;
+                    if (width > MaxWidth)
+                    {
+                        MaxWidth = width;
+                    }
+                }
+            }
+        }
+
+        public UcDeviceList()
+        {
+            InitializeComponent();
+            InitNetDeviceListUI();
+        }
+
+        /// <summary>
+        /// 初始化设备列表UI
+        /// </summary>
+        private void InitNetDeviceListUI()
+        {
+            ColumnHeader ch = new ColumnHeader();
+            ch.Text = "序号";
+            ch.Width = 40;
+            ch.TextAlign = HorizontalAlignment.Center;
+            this.listView1.Columns.Add(ch);
+
+            ColumnHeader ch2 = new ColumnHeader();
+            ch2.Text = "设备编号";
+            ch2.Width = 80;
+            ch2.TextAlign = HorizontalAlignment.Center;
+            this.listView1.Columns.Add(ch2);
+
+            ColumnHeader ch3 = new ColumnHeader();
+            ch3.Text = "数据来源";
+            ch3.Width = 100;
+            ch3.TextAlign = HorizontalAlignment.Center;
+            this.listView1.Columns.Add(ch3);
+
+            ColumnHeader ch4 = new ColumnHeader();
+            ch4.Text = "固件版本";
+            ch4.Width = 126;
+            ch4.TextAlign = HorizontalAlignment.Center;
+            this.listView1.Columns.Add(ch4);
+        }
+
+        /// <summary>
+        /// 更新设备列表UI
+        /// </summary>
+        /// <param name="deviceList">设备列表</param>
+        public void UpdateDeviceListUI(List<GatewayBase> deviceList)
+        {
+            this.listView1.BeginUpdate();   //数据更新,UI暂时挂起,直到EndUpdate绘制控件,可以有效避免闪烁并大大提高加载速度
+            GatewayBase device = null;
+            int count = deviceList.Count;
+            this.listView1.Items.Clear();
+            for (int i = 0; i < count; i++)   //添加数据  
+            {
+                device = deviceList[i];
+                ListViewItem lv = new ListViewItem();
+                lv.Text = (i + 1).ToString();
+                lv.SubItems.Add(device.SniffInfo.SN);
+                lv.SubItems.Add(device.SniffInfo.Source);
+                lv.SubItems.Add(device.SniffInfo.Version);
+                this.listView1.Items.Add(lv);
+            }
+            this.listView1.EndUpdate();  //结束数据处理,UI界面一次性绘制。 
+        }
+
+        private void listView1_SizeChanged(object sender, EventArgs e)
+        {
+            //AutoResizeColumnWidth(this.listView1);
+        }
+
+        private void listView1_MouseClick(object sender, MouseEventArgs e)
+        {
+            ExecItemClicked?.Invoke(this, listView1.SelectedItems[0]); //输出选中条目
+        }
+    }
+}

+ 185 - 0
GatewayTool/UserControls/UcDeviceList.resx

@@ -0,0 +1,185 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="imageList1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <data name="imageList1.ImageStream" mimetype="application/x-microsoft.net.object.binary.base64">
+    <value>
+        AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
+        LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
+        ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAC8
+        DAAAAk1TRnQBSQFMAgEBAgEAASABAAEgAQABJAEAASQBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
+        AwABkAMAASQDAAEBAQABCAUAAUABFBgAAYACAAGAAwACgAEAAYADAAGAAQABgAEAAoACAAPAAQABwAHc
+        AcABAAHwAcoBpgEAATMFAAEzAQABMwEAATMBAAIzAgADFgEAAxwBAAMiAQADKQEAA1UBAANNAQADQgEA
+        AzkBAAGAAXwB/wEAAlAB/wEAAZMBAAHWAQAB/wHsAcwBAAHGAdYB7wEAAdYC5wEAAZABqQGtAgAB/wEz
+        AwABZgMAAZkDAAHMAgABMwMAAjMCAAEzAWYCAAEzAZkCAAEzAcwCAAEzAf8CAAFmAwABZgEzAgACZgIA
+        AWYBmQIAAWYBzAIAAWYB/wIAAZkDAAGZATMCAAGZAWYCAAKZAgABmQHMAgABmQH/AgABzAMAAcwBMwIA
+        AcwBZgIAAcwBmQIAAswCAAHMAf8CAAH/AWYCAAH/AZkCAAH/AcwBAAEzAf8CAAH/AQABMwEAATMBAAFm
+        AQABMwEAAZkBAAEzAQABzAEAATMBAAH/AQAB/wEzAgADMwEAAjMBZgEAAjMBmQEAAjMBzAEAAjMB/wEA
+        ATMBZgIAATMBZgEzAQABMwJmAQABMwFmAZkBAAEzAWYBzAEAATMBZgH/AQABMwGZAgABMwGZATMBAAEz
+        AZkBZgEAATMCmQEAATMBmQHMAQABMwGZAf8BAAEzAcwCAAEzAcwBMwEAATMBzAFmAQABMwHMAZkBAAEz
+        AswBAAEzAcwB/wEAATMB/wEzAQABMwH/AWYBAAEzAf8BmQEAATMB/wHMAQABMwL/AQABZgMAAWYBAAEz
+        AQABZgEAAWYBAAFmAQABmQEAAWYBAAHMAQABZgEAAf8BAAFmATMCAAFmAjMBAAFmATMBZgEAAWYBMwGZ
+        AQABZgEzAcwBAAFmATMB/wEAAmYCAAJmATMBAANmAQACZgGZAQACZgHMAQABZgGZAgABZgGZATMBAAFm
+        AZkBZgEAAWYCmQEAAWYBmQHMAQABZgGZAf8BAAFmAcwCAAFmAcwBMwEAAWYBzAGZAQABZgLMAQABZgHM
+        Af8BAAFmAf8CAAFmAf8BMwEAAWYB/wGZAQABZgH/AcwBAAHMAQAB/wEAAf8BAAHMAQACmQIAAZkBMwGZ
+        AQABmQEAAZkBAAGZAQABzAEAAZkDAAGZAjMBAAGZAQABZgEAAZkBMwHMAQABmQEAAf8BAAGZAWYCAAGZ
+        AWYBMwEAAZkBMwFmAQABmQFmAZkBAAGZAWYBzAEAAZkBMwH/AQACmQEzAQACmQFmAQADmQEAApkBzAEA
+        ApkB/wEAAZkBzAIAAZkBzAEzAQABZgHMAWYBAAGZAcwBmQEAAZkCzAEAAZkBzAH/AQABmQH/AgABmQH/
+        ATMBAAGZAcwBZgEAAZkB/wGZAQABmQH/AcwBAAGZAv8BAAHMAwABmQEAATMBAAHMAQABZgEAAcwBAAGZ
+        AQABzAEAAcwBAAGZATMCAAHMAjMBAAHMATMBZgEAAcwBMwGZAQABzAEzAcwBAAHMATMB/wEAAcwBZgIA
+        AcwBZgEzAQABmQJmAQABzAFmAZkBAAHMAWYBzAEAAZkBZgH/AQABzAGZAgABzAGZATMBAAHMAZkBZgEA
+        AcwCmQEAAcwBmQHMAQABzAGZAf8BAALMAgACzAEzAQACzAFmAQACzAGZAQADzAEAAswB/wEAAcwB/wIA
+        AcwB/wEzAQABmQH/AWYBAAHMAf8BmQEAAcwB/wHMAQABzAL/AQABzAEAATMBAAH/AQABZgEAAf8BAAGZ
+        AQABzAEzAgAB/wIzAQAB/wEzAWYBAAH/ATMBmQEAAf8BMwHMAQAB/wEzAf8BAAH/AWYCAAH/AWYBMwEA
+        AcwCZgEAAf8BZgGZAQAB/wFmAcwBAAHMAWYB/wEAAf8BmQIAAf8BmQEzAQAB/wGZAWYBAAH/ApkBAAH/
+        AZkBzAEAAf8BmQH/AQAB/wHMAgAB/wHMATMBAAH/AcwBZgEAAf8BzAGZAQAB/wLMAQAB/wHMAf8BAAL/
+        ATMBAAHMAf8BZgEAAv8BmQEAAv8BzAEAAmYB/wEAAWYB/wFmAQABZgL/AQAB/wJmAQAB/wFmAf8BAAL/
+        AWYBAAEhAQABpQEAA18BAAN3AQADhgEAA5YBAAPLAQADsgEAA9cBAAPdAQAD4wEAA+oBAAPxAQAD+AEA
+        AfAB+wH/AQABpAKgAQADgAMAAf8CAAH/AwAC/wEAAf8DAAH/AQAB/wEAAv8CAAP//wC2AAH/AfQB8wH/
+        FQAB/wL0Af9yAAH/AbQCrQG1Af8TAAH/AbUCrQG1Af9xAAG1Aa0B3QHzAa0BCRMAAQkBrQHdAQkBrQEJ
+        cQABrQG0Af8BAAEJAbQB/wEAAv8MAAH0Av8BtAEJAQAB/wG0Aa0JAAH/FfQB/1EAAbQBrQL0AbQBtQEJ
+        AvMBCQQAAfQBAAH0BQABCQG0AQkBtQG0Av8CtAgAAfQWrgG8Af9QAAH/Aa0CxwGtAvMBrQG0AccB/wEA
+        Af8B3QGtAf8BrQHdAf8CAAH/AQkBrQLzAa0DxwH0CAABBxdlAf9RAAH/AgkB9AIJAfMBCQGtAfQB8wG0
+        AscB/wLHAbQB8gH/AbQBCQHzAgkB8wG1AQkB9AkAAQcXZQH/VAAB/wG1ArQBCQHzAbQBrQPHAf8DxwGt
+        AbQB8wHdAbQBCQL/DAABBxdlAf9VAAHxAbQB/wEJAfMFxwH/BccB8wG0Aa0BtA4AAQcXZQH/WQAB8wXH
+        Af8FxwHzAv8B8w4AAQcHZQGGAfMB8gGLAa4B9AG1AWUBvAL0AfMBrgNlAf9ZAAHzBMcBrQH/Aa0ExwHz
+        EQABBwdlAYYB/wH0Aa4BtQH/AbwBZQHzAgAB/wGuA2UB/1kAAfMDxwG0Af8B8QH0AbQDxwHzEQABBxdl
+        Af9ZAAHzAawBtAHdAfEBtAHHAbQB8QEJAbQBrAHzEQABBxdlAf9ZAAH0AQkB8QG0Aa0DxwGtAbQC3QH0
+        EQABBxdlAf9aAAEJAawHxwGsAQkSAAEHAmUSrgGLAmUB/1oAAf8B3QGtBccBrQEJAf8SAAHzAmUB/xEA
+        AbUBZQGuAf9cAAH/AQkBrQHHAa0BCQH0Af8TAAH/AYYBZQHwEQABiwFlAQdfAAH0AbUB9BcAAbUBZQG1
+        Ef8BZQGGAfNeAAEJAbQB/wG0Af8WAAG8AWYBZRGuAWUBrgH0XQAB8wGtAbQB/wG0AfQWAAH0AYsTZQEH
+        Af9eAAHzAbUB/wKtAfMWAAH/AfMBtQFlAQcL8wFlAYYBvAH0YAAB/wEJAf8BrQHdGQABtQFlAQcKAAH0
+        AWUBhgHzYQAB/wHdAgkB/xkAAbUBZQEHCgAB9AFlAYYB82EAAQkBrAHHAawBCRkAAbUBZQEHCgAB9AFl
+        AYYB82AAAf8BrQEJAf8BCQGtAf8YAAG1AWUBBwoAAfQBZQGGAfNgAAHzAa0B9AEAAfMBrQH0GAABtQFl
+        AQcKAAH0AWUBhgHzYAAB/wGsAQkB/wEJAa0B/xgAAbwBZQH0CwABZQGuAfRhAAEJAa0BxwGtAd0ZAAH/
+        AfQMAAH/AfRiAAH/AfQB8wH0Af//AP8AvQABQgFNAT4HAAE+AwABKAMAAZADAAEkAwABAQEAAQEFAAHQ
+        AQIWAAP/AQAJ/wsACf8LAAn/CwAB8AL/AfgBfwT/CwAB4AF/Af8B8AE/BP8LAAHgAX8B/wHwAT8E/wsA
+        AeIBJwH/AYIBPwHgAgABPwsAAeABBwGvAYABPwHAAgABHwsAAeABAgEDAQABPwHAAgABHwsAAfADAAF/
+        AcACAAEfCwAB/gIAAQMB/wHAAgABHwsAAf8CAAEPAf8BwAIAAR8LAAH/AfABAAEPAf8BwAIAAR8LAAH/
+        AfABAAF/Af8BwAEAARgBHwsAAf8B8AEAAX8B/wHAAgABHwsAAf8B8AEAAX8B/wHAAgABHwsAAf8B8AEA
+        AX8B/wHAAgABHwsAAf8B+AEAAv8BwAIAAR8LAAH/AfgBAAL/AcMB/wH+AR8LAAH/Af4BAQL/AcMB/wH+
+        AT8LAAL/AY8C/wHgAgABPwsAAv8BBwL/AeACAAE/CwAB/wH+AQcC/wHgAgABPwsAAv8BAwL/AfACAAH/
+        CwAC/wEHAv8B/AF/AeEB/wsAAv8BBwL/AfwBfwHhAf8LAAL/AQcC/wH8AX8B4QH/CwAB/wH+AQMC/wH8
+        AX8B4QH/CwAB/wH+ASMC/wH8AX8B4QH/CwAB/wH+AQMC/wH8AX8B8QH/CwAC/wEHAv8B/AH/AfMB/wsA
+        Av8BBwb/CwAJ/wsACf8LAAn/CwAJ/wsACw==
+</value>
+  </data>
+</root>

BIN
GatewayTool/favicon.ico