NewLife/XCoder

升级基础组件,使用最新IPacket,其中Modbus还需要优化
大石头 authored at 2024-12-10 11:36:27
b8f3124
Tree
1 Parent(s) 4a3531d
Summary: 14 changed files with 646 additions and 637 deletions.
Modified +8 -8
Modified +26 -30
Modified +1 -0
Modified +35 -13
Modified +2 -4
Modified +1 -0
Modified +1 -1
Modified +4 -1
Modified +45 -47
Modified +1 -3
Modified +40 -42
Modified +3 -3
Modified +478 -484
Modified +1 -1
Modified +8 -8
diff --git a/XCoder/CrazyCoder.csproj b/XCoder/CrazyCoder.csproj
index 8b941f0..943e282 100644
--- a/XCoder/CrazyCoder.csproj
+++ b/XCoder/CrazyCoder.csproj
@@ -62,32 +62,32 @@
     <Content Include="数据库命名规范.txt" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="NewLife.Map" Version="2.6.2024.801" />
+    <PackageReference Include="NewLife.Map" Version="2.6.2024.1102" />
     <PackageReference Include="NewLife.ModbusRTU" Version="1.8.2024.426-beta1011" />
-    <PackageReference Include="NewLife.Remoting" Version="3.0.2024.902" />
+    <PackageReference Include="NewLife.Remoting" Version="3.2.2024.1206" />
     <PackageReference Include="System.Speech" Version="8.0.0" />
     <PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
     <PackageReference Include="Microsoft.VisualBasic" Version="10.3.0" />
     <PackageReference Include="NewLife.Core">
-      <Version>10.10.2024.902</Version>
+      <Version>11.1.2024.1206</Version>
     </PackageReference>
     <PackageReference Include="NewLife.MQTT">
       <Version>2.0.2024.708</Version>
     </PackageReference>
     <PackageReference Include="NewLife.Net">
-      <Version>4.3.2024.903-beta0724</Version>
+      <Version>4.4.2024.1202</Version>
     </PackageReference>
     <PackageReference Include="NewLife.Redis">
-      <Version>5.7.2024.801</Version>
+      <Version>6.0.2024.1205</Version>
     </PackageReference>
     <PackageReference Include="NewLife.Stardust">
-      <Version>3.0.2024.902</Version>
+      <Version>3.2.2024.1203</Version>
     </PackageReference>
     <PackageReference Include="NewLife.XCode">
-      <Version>11.15.2024.902</Version>
+      <Version>11.16.2024.1202</Version>
     </PackageReference>
     <PackageReference Include="SSH.NET">
-      <Version>2024.1.0</Version>
+      <Version>2024.2.0</Version>
     </PackageReference>
     <PackageReference Include="System.IO.Ports" Version="8.0.0" />
     <PackageReference Include="System.Management" Version="8.0.0" />
Modified +26 -30
diff --git a/XCoder/XApi/MyApiController.cs b/XCoder/XApi/MyApiController.cs
index 0d65851..f64b13d 100644
--- a/XCoder/XApi/MyApiController.cs
+++ b/XCoder/XApi/MyApiController.cs
@@ -1,43 +1,39 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
+using System.Text;
 using NewLife;
 using NewLife.Data;
 using NewLife.Remoting;
-using NewLife.Security;
 
-namespace XApi
+namespace XApi;
+
+/// <summary>API控制器</summary>
+//[AllowAnonymous]
+public class MyApiController
 {
-    /// <summary>API控制器</summary>
-    //[AllowAnonymous]
-    public class MyApiController
+    /// <summary>获取指定种类的环境信息</summary>
+    /// <param name="kind"></param>
+    /// <returns></returns>
+    public String Info(String kind)
     {
-        /// <summary>获取指定种类的环境信息</summary>
-        /// <param name="kind"></param>
-        /// <returns></returns>
-        public String Info(String kind)
+        switch ((kind + "").ToLower())
         {
-            switch ((kind + "").ToLower())
-            {
-                case "machine": return Environment.MachineName;
-                case "user": return Environment.UserName;
-                case "ip": return NetHelper.MyIP() + "";
-                case "time": return DateTime.Now.ToFullString();
-                default:
-                    throw new ApiException(505, "不支持类型" + kind);
-            }
+            case "machine": return Environment.MachineName;
+            case "user": return Environment.UserName;
+            case "ip": return NetHelper.MyIP() + "";
+            case "time": return DateTime.Now.ToFullString();
+            default:
+                throw new ApiException(505, "不支持类型" + kind);
         }
+    }
 
-        /// <summary>加密数据</summary>
-        /// <param name="data"></param>
-        /// <returns></returns>
-        public Packet Encrypt(Packet data)
-        {
-            //Log.XTrace.WriteLine("加密数据{0:n0}字节", data.Total);
+    /// <summary>加密数据</summary>
+    /// <param name="data"></param>
+    /// <returns></returns>
+    public IPacket Encrypt(IPacket data)
+    {
+        //Log.XTrace.WriteLine("加密数据{0:n0}字节", data.Total);
 
-            var buf = data.ToArray().RC4("NewLife".GetBytes());
+        var buf = data.ReadBytes().RC4("NewLife".GetBytes());
 
-            return buf;
-        }
+        return (ArrayPacket)buf;
     }
 }
\ No newline at end of file
Modified +1 -0
diff --git a/XCoder/XCom/SerialPortList.cs b/XCoder/XCom/SerialPortList.cs
index 7058c19..14ab5da 100644
--- a/XCoder/XCom/SerialPortList.cs
+++ b/XCoder/XCom/SerialPortList.cs
@@ -2,6 +2,7 @@
 using System.ComponentModel;
 using System.IO.Ports;
 using System.Text;
+using NewLife.Data;
 using NewLife.Log;
 using NewLife.Net;
 using NewLife.Threading;
Modified +35 -13
diff --git a/XCoder/XCom/SerialTransport.cs b/XCoder/XCom/SerialTransport.cs
index 0730da3..1fb4c54 100644
--- a/XCoder/XCom/SerialTransport.cs
+++ b/XCoder/XCom/SerialTransport.cs
@@ -165,7 +165,7 @@ public class SerialTransport : DisposeBase, ITransport
     #region 发送
     /// <summary>写入数据</summary>
     /// <param name="pk">数据包</param>
-    public virtual Int32 Send(Packet pk)
+    public virtual Int32 Send(IPacket pk)
     {
         if (!Open()) return -1;
 
@@ -174,16 +174,24 @@ public class SerialTransport : DisposeBase, ITransport
         var sp = Serial;
         lock (sp)
         {
-            sp.Write(pk.Data, pk.Offset, pk.Count);
+            if (pk.TryGetArray(out var seg))
+                sp.Write(seg.Array, seg.Offset, seg.Count);
+            else
+            {
+                var buf = pk.ReadBytes();
+                sp.Write(buf, 0, buf.Length);
+            }
         }
 
         return pk.Total;
     }
 
+    public Int32 Send(Byte[] buf) => Send((ArrayPacket)buf);
+
     /// <summary>异步发送数据并等待响应</summary>
     /// <param name="pk"></param>
     /// <returns></returns>
-    public virtual async Task<Packet> SendAsync(Packet pk)
+    public virtual async Task<IOwnerPacket> SendAsync(IPacket pk)
     {
         if (!Open()) return null;
 
@@ -191,14 +199,22 @@ public class SerialTransport : DisposeBase, ITransport
 
         //var task = Packet.Add(pk, null, Timeout);
 
-        _Source = new TaskCompletionSource<Packet>();
+        _Source = new TaskCompletionSource<IOwnerPacket>();
 
         if (pk != null)
         {
             WriteLog("SendAsync:{0}", pk.ToHex());
 
             // 发送数据
-            Serial.Write(pk.Data, pk.Offset, pk.Count);
+            //Serial.Write(pk.Data, pk.Offset, pk.Count);
+            var sp = Serial;
+            if (pk.TryGetArray(out var seg))
+                sp.Write(seg.Array, seg.Offset, seg.Count);
+            else
+            {
+                var buf = pk.ReadBytes();
+                sp.Write(buf, 0, buf.Length);
+            }
         }
 
         return await _Source.Task;
@@ -206,14 +222,18 @@ public class SerialTransport : DisposeBase, ITransport
 
     /// <summary>接收数据</summary>
     /// <returns></returns>
-    public virtual Packet Receive()
+    public virtual IOwnerPacket Receive()
     {
         if (!Open()) return null;
 
         var task = SendAsync(null);
         if (Timeout > 0 && !task.Wait(Timeout)) return null;
 
-        return task.Result;
+        //return task.Result;
+        var rs = task.ConfigureAwait(false).GetAwaiter().GetResult();
+        if (rs is OwnerPacket op) return op;
+
+        return rs;
     }
     #endregion
 
@@ -227,12 +247,14 @@ public class SerialTransport : DisposeBase, ITransport
             WaitMore();
             if (sp.BytesToRead > 0)
             {
-                var buf = new Byte[sp.BytesToRead];
+                //var buf = new Byte[sp.BytesToRead];
+                var pk = new OwnerPacket(sp.BytesToRead);
 
-                var count = sp.Read(buf, 0, buf.Length);
+                var count = sp.Read(pk.Buffer, 0, pk.Length);
                 //if (count != buf.Length) buf = buf.ReadBytes(0, count);
                 //var ms = new MemoryStream(buf, 0, count, false);
-                var pk = new Packet(buf, 0, count);
+                //var pk = new ArrayPacket(buf, 0, count);
+                pk.Resize(count);
 
                 ProcessReceive(pk);
             }
@@ -263,7 +285,7 @@ public class SerialTransport : DisposeBase, ITransport
         }
     }
 
-    void ProcessReceive(Packet pk)
+    void ProcessReceive(IOwnerPacket pk)
     {
         try
         {
@@ -284,10 +306,10 @@ public class SerialTransport : DisposeBase, ITransport
         }
     }
 
-    private TaskCompletionSource<Packet> _Source;
+    private TaskCompletionSource<IOwnerPacket> _Source;
     /// <summary>处理收到的数据。默认匹配同步接收委托</summary>
     /// <param name="pk"></param>
-    internal virtual void OnReceive(Packet pk)
+    internal virtual void OnReceive(IOwnerPacket pk)
     {
         //// 同步匹配
         //if (Packet != null && Packet.Match(pk, null)) return;
Modified +2 -4
diff --git a/XCoder/XNet/BenchHelper.cs b/XCoder/XNet/BenchHelper.cs
index 534fce0..127b84b 100644
--- a/XCoder/XNet/BenchHelper.cs
+++ b/XCoder/XNet/BenchHelper.cs
@@ -1,6 +1,4 @@
-using System;
-using System.Threading.Tasks;
-using NewLife.Data;
+using NewLife.Data;
 using NewLife.Net;
 
 namespace XCoder.XNet;
@@ -13,7 +11,7 @@ static class BenchHelper
     /// <param name="times">次数</param>
     /// <param name="msInterval">间隔</param>
     /// <returns></returns>
-    public static Task SendConcurrency(this ISocketRemote session, Packet pk, Int32 times, Int32 msInterval)
+    public static Task SendConcurrency(this ISocketRemote session, IPacket pk, Int32 times, Int32 msInterval)
     {
         var task = Task.Run(async () =>
         {
Modified +1 -0
diff --git a/XCoder/XNet/FrmApiDiscover.cs b/XCoder/XNet/FrmApiDiscover.cs
index 3a4eb79..f6a640b 100644
--- a/XCoder/XNet/FrmApiDiscover.cs
+++ b/XCoder/XNet/FrmApiDiscover.cs
@@ -2,6 +2,7 @@
 using System.Net;
 using System.Runtime.Serialization;
 using NewLife;
+using NewLife.Data;
 using NewLife.Log;
 using NewLife.Net;
 using NewLife.Remoting;
Modified +1 -1
diff --git a/XCoder/XNet/FrmMain.cs b/XCoder/XNet/FrmMain.cs
index 4697033..b7de6b8 100644
--- a/XCoder/XNet/FrmMain.cs
+++ b/XCoder/XNet/FrmMain.cs
@@ -409,7 +409,7 @@ public partial class FrmMain : Form, IXForm
                 for (var i = 0; i < count && _Server != null; i++)
                 {
                     var sw = Stopwatch.StartNew();
-                    var cs = await _Server.SendAllAsync(buf);
+                    var cs = await _Server.SendAllAsync(pk);
                     sw.Stop();
                     BizLog.Info("{3}/{4} 已向[{0}]个客户端发送[{1}]数据 {2:n0}ms", cs, buf.Length, sw.ElapsedMilliseconds, i + 1, count);
                     if (sleep > 0) await Task.Delay(sleep);
Modified +4 -1
diff --git a/XCoder/XNet/FrmModbusSlave.cs b/XCoder/XNet/FrmModbusSlave.cs
index 0e03438..77195b4 100644
--- a/XCoder/XNet/FrmModbusSlave.cs
+++ b/XCoder/XNet/FrmModbusSlave.cs
@@ -1,5 +1,6 @@
 using System.ComponentModel;
 using NewLife;
+using NewLife.Data;
 using NewLife.IoT.Protocols;
 using NewLife.Log;
 using NewLife.Net;
@@ -265,7 +266,9 @@ namespace XNet
             var session = sender as NetSession;
             if (session == null) return;
 
-            var msg = ModbusIpMessage.Read(e.Packet);
+            var pk = e.Packet as Packet;
+            if (pk == null) pk = new Packet(e.Packet.ReadBytes());
+            var msg = ModbusIpMessage.Read(pk);
             if (msg == null) return;
 
             session.Log?.Info("<= {0}", msg);
Modified +45 -47
diff --git a/XCoder/XNet/IPPacket.cs b/XCoder/XNet/IPPacket.cs
index fff72a9..c2deb40 100644
--- a/XCoder/XNet/IPPacket.cs
+++ b/XCoder/XNet/IPPacket.cs
@@ -1,54 +1,52 @@
-using System;
-using System.Net;
+using System.Net;
 using System.Net.Sockets;
 using NewLife.Data;
 
-namespace XCoder.XNet
+namespace XCoder.XNet;
+
+/// <summary>IP包</summary>
+public class IPPacket
 {
-    /// <summary>IP包</summary>
-    public class IPPacket
+    public Byte Version;
+    public Byte Length;
+    public Byte DiffServices;
+    public UInt16 DataLength;
+    public UInt16 Identification;
+    public Byte Flag;
+    public UInt16 Excursion;
+    public Byte TTL;
+    public ProtocolType Protocol;
+    public UInt16 CheckSum;
+    public IPAddress SrcAddr;
+    public IPAddress DestAddr;
+    public Byte[] Option;
+    public IPacket Data;
+
+    public IPPacket(IPacket pk)
     {
-        public Byte Version;
-        public Byte Length;
-        public Byte DiffServices;
-        public UInt16 DataLength;
-        public UInt16 Identification;
-        public Byte Flag;
-        public UInt16 Excursion;
-        public Byte TTL;
-        public ProtocolType Protocol;
-        public UInt16 CheckSum;
-        public IPAddress SrcAddr;
-        public IPAddress DestAddr;
-        public Byte[] Option;
-        public Packet Data;
-
-        public IPPacket(Packet pk)
-        {
-            if (pk == null) throw new ArgumentNullException(nameof(pk));
-
-            var data = pk.ReadBytes(0, 20);
-
-            Version = (Byte)((data[0] & 0xF0) >> 4);
-            Length = (Byte)((data[0] & 0x0F) * 4);
-            DiffServices = data[1];
-            DataLength = (UInt16)((data[2] << 8) + data[3]);
-            Identification = (UInt16)((data[4] << 8) + data[5]);
-            Flag = (Byte)(data[6] >> 5);
-            Excursion = (UInt16)(((data[6] & 0x1F) << 8) + data[7]);
-            TTL = data[8];
-            Protocol = (ProtocolType)data[9];
-            CheckSum = (UInt16)((data[10] << 8) + data[11]);
-
-            SrcAddr = new IPAddress(pk.ReadBytes(12, 4));
-            DestAddr = new IPAddress(pk.ReadBytes(16, 4));
-
-            // 可选项
-            if (Length > 20) Option = pk.ReadBytes(20, Length - 20);
-
-            Data = pk.Slice(Length, DataLength);
-        }
-
-        public override String ToString() => $"{SrcAddr} => {DestAddr} [{DataLength}]";
+        if (pk == null) throw new ArgumentNullException(nameof(pk));
+
+        var data = pk.ReadBytes(0, 20);
+
+        Version = (Byte)((data[0] & 0xF0) >> 4);
+        Length = (Byte)((data[0] & 0x0F) * 4);
+        DiffServices = data[1];
+        DataLength = (UInt16)((data[2] << 8) + data[3]);
+        Identification = (UInt16)((data[4] << 8) + data[5]);
+        Flag = (Byte)(data[6] >> 5);
+        Excursion = (UInt16)(((data[6] & 0x1F) << 8) + data[7]);
+        TTL = data[8];
+        Protocol = (ProtocolType)data[9];
+        CheckSum = (UInt16)((data[10] << 8) + data[11]);
+
+        SrcAddr = new IPAddress(pk.ReadBytes(12, 4));
+        DestAddr = new IPAddress(pk.ReadBytes(16, 4));
+
+        // 可选项
+        if (Length > 20) Option = pk.ReadBytes(20, Length - 20);
+
+        Data = pk.Slice(Length, DataLength);
     }
+
+    public override String ToString() => $"{SrcAddr} => {DestAddr} [{DataLength}]";
 }
\ No newline at end of file
Modified +1 -3
diff --git a/XCoder/XNet/MySqlParser.cs b/XCoder/XNet/MySqlParser.cs
index 768bf77..7c2e026 100644
--- a/XCoder/XNet/MySqlParser.cs
+++ b/XCoder/XNet/MySqlParser.cs
@@ -1,6 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Data;
+using System.Data;
 using System.Net;
 using System.Text;
 using NewLife.Data;
Modified +40 -42
diff --git a/XCoder/XNet/TcpPacket.cs b/XCoder/XNet/TcpPacket.cs
index 3774c43..616a1b5 100644
--- a/XCoder/XNet/TcpPacket.cs
+++ b/XCoder/XNet/TcpPacket.cs
@@ -1,56 +1,54 @@
-using System;
-using System.Net.Sockets;
+using System.Net.Sockets;
 using NewLife.Data;
 
-namespace XCoder.XNet
+namespace XCoder.XNet;
+
+/// <summary>Tcp包</summary>
+public class TcpPacket
 {
-    /// <summary>Tcp包</summary>
-    public class TcpPacket
+    public IPPacket IPPacket;
+
+    public UInt16 SrcPort;
+    public UInt16 DestPort;
+    public UInt32 SequenceNo;
+    public UInt32 NextSeqNo;
+    public Byte HeadLength;
+    public Byte Flag;
+    public UInt16 WindowSize;
+    public UInt16 CheckSum;
+    public UInt16 UrgPtr;
+    public Byte[] Option;
+    public IPacket Data;
+
+    public TcpPacket(IPPacket packet)
     {
-        public IPPacket IPPacket;
-
-        public UInt16 SrcPort;
-        public UInt16 DestPort;
-        public UInt32 SequenceNo;
-        public UInt32 NextSeqNo;
-        public Byte HeadLength;
-        public Byte Flag;
-        public UInt16 WindowSize;
-        public UInt16 CheckSum;
-        public UInt16 UrgPtr;
-        public Byte[] Option;
-        public Packet Data;
-
-        public TcpPacket(IPPacket packet)
-        {
-            if (packet == null) throw new ArgumentNullException(nameof(packet));
-            if (packet.Protocol != ProtocolType.Tcp) throw new NotSupportedException();
+        if (packet == null) throw new ArgumentNullException(nameof(packet));
+        if (packet.Protocol != ProtocolType.Tcp) throw new NotSupportedException();
 
-            IPPacket = packet;
+        IPPacket = packet;
 
-            var pk = packet.Data;
-            var data = pk.ReadBytes(0, 20);
+        var pk = packet.Data;
+        var data = pk.ReadBytes(0, 20);
 
-            SrcPort = (UInt16)((data[0] << 8) + data[1]);
-            DestPort = (UInt16)((data[2] << 8) + data[3]);
+        SrcPort = (UInt16)((data[0] << 8) + data[1]);
+        DestPort = (UInt16)((data[2] << 8) + data[3]);
 
-            SequenceNo = ((UInt32)data[7] << 24) + ((UInt32)data[6] << 16) + ((UInt32)data[5] << 8) + data[4];
-            NextSeqNo = ((UInt32)data[11] << 24) + ((UInt32)data[10] << 16) + ((UInt32)data[9] << 8) + data[8];
+        SequenceNo = ((UInt32)data[7] << 24) + ((UInt32)data[6] << 16) + ((UInt32)data[5] << 8) + data[4];
+        NextSeqNo = ((UInt32)data[11] << 24) + ((UInt32)data[10] << 16) + ((UInt32)data[9] << 8) + data[8];
 
-            HeadLength = (Byte)(((data[12] & 0xF0) >> 4) * 4);
+        HeadLength = (Byte)(((data[12] & 0xF0) >> 4) * 4);
 
-            // 6bit保留位
-            Flag = (Byte)(data[13] & 0x3F);
-            WindowSize = (UInt16)((data[14] << 8) + data[15]);
-            CheckSum = (UInt16)((data[16] << 8) + data[17]);
-            UrgPtr = (UInt16)((data[18] << 8) + data[19]);
+        // 6bit保留位
+        Flag = (Byte)(data[13] & 0x3F);
+        WindowSize = (UInt16)((data[14] << 8) + data[15]);
+        CheckSum = (UInt16)((data[16] << 8) + data[17]);
+        UrgPtr = (UInt16)((data[18] << 8) + data[19]);
 
-            // 可选项
-            if (HeadLength > 20) Option = pk.ReadBytes(20, HeadLength - 20);
+        // 可选项
+        if (HeadLength > 20) Option = pk.ReadBytes(20, HeadLength - 20);
 
-            Data = pk.Slice(HeadLength);
-        }
-
-        public override String ToString() => $"{IPPacket?.SrcAddr}:{SrcPort} => {IPPacket.DestAddr}:{DestPort} [{IPPacket.DataLength - HeadLength}]";
+        Data = pk.Slice(HeadLength);
     }
+
+    public override String ToString() => $"{IPPacket?.SrcAddr}:{SrcPort} => {IPPacket.DestAddr}:{DestPort} [{IPPacket.DataLength - HeadLength}]";
 }
\ No newline at end of file
Modified +3 -3
diff --git a/XCoderLinux/XCoderLinux.csproj b/XCoderLinux/XCoderLinux.csproj
index 3cdb3fc..b8f84aa 100644
--- a/XCoderLinux/XCoderLinux.csproj
+++ b/XCoderLinux/XCoderLinux.csproj
@@ -49,9 +49,9 @@
 
   <ItemGroup>
     <PackageReference Include="GtkSharp" Version="3.24.24.95" />
-    <PackageReference Include="NewLife.Core" Version="10.10.2024.902" />
-    <PackageReference Include="NewLife.Stardust" Version="3.0.2024.902" />
-    <PackageReference Include="NewLife.XCode" Version="11.15.2024.902" />
+    <PackageReference Include="NewLife.Core" Version="11.1.2024.1206" />
+    <PackageReference Include="NewLife.Stardust" Version="3.2.2024.1203" />
+    <PackageReference Include="NewLife.XCode" Version="11.16.2024.1202" />
     <PackageReference Include="System.IO.Ports" Version="8.0.0" />
     <PackageReference Include="System.Management" Version="8.0.0" />
   </ItemGroup>
Modified +478 -484
diff --git a/XCoderLinux/XCom/SerialPortList.cs b/XCoderLinux/XCom/SerialPortList.cs
index 8413054..45e11ac 100644
--- a/XCoderLinux/XCom/SerialPortList.cs
+++ b/XCoderLinux/XCom/SerialPortList.cs
@@ -1,565 +1,559 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.IO;
-using System.IO.Ports;
-using System.Linq;
+using System.ComponentModel;
 using System.Text;
 using Gtk;
+using NewLife.Data;
 using NewLife.Log;
 using NewLife.Net;
 using NewLife.Threading;
 using XCoder.Util;
 
-namespace NewLife.Windows
-{
-    /// <summary>串口列表控件</summary>
-    [DefaultEvent("ReceivedString")]
-    public partial class SerialPortList : HBox
-    {
-        #region 属性
-        /// <summary>端口</summary>
-        public SerialTransport Port { get; set; }
+namespace NewLife.Windows;
 
-        /// <summary>选择的端口</summary>
-        public String SelectedPort { get { return cbName.GetActiveObject() + ""; } set { } }
+/// <summary>串口列表控件</summary>
+[DefaultEvent("ReceivedString")]
+public partial class SerialPortList : HBox
+{
+    #region 属性
+    /// <summary>端口</summary>
+    public SerialTransport Port { get; set; }
 
-        /// <summary>收到的字节数</summary>
-        public Int32 BytesOfReceived { get; set; }
+    /// <summary>选择的端口</summary>
+    public String SelectedPort { get { return cbName.GetActiveObject() + ""; } set { } }
 
-        /// <summary>发送的字节数</summary>
-        public Int32 BytesOfSent { get; set; }
-        #endregion
+    /// <summary>收到的字节数</summary>
+    public Int32 BytesOfReceived { get; set; }
 
-        #region 构造
-        /// <summary></summary>
-        public SerialPortList()
-        {
-            InitializeComponent();
-        }
+    /// <summary>发送的字节数</summary>
+    public Int32 BytesOfSent { get; set; }
+    #endregion
 
-        TimerX _timer;
-        private void SerialPortList_Load(Object sender, EventArgs e)
-        {
-            LoadInfo();
+    #region 构造
+    /// <summary></summary>
+    public SerialPortList()
+    {
+        InitializeComponent();
+    }
 
-            // 挂载定时器
-            _timer = new TimerX(OnCheck, null, 300, 300) { Async = true };
+    TimerX _timer;
+    private void SerialPortList_Load(Object sender, EventArgs e)
+    {
+        LoadInfo();
 
-            //if (Parent is Form frm)
-            //{
-            //    frm.FormClosing += frm_FormClosing;
-            //}
-        }
+        // 挂载定时器
+        _timer = new TimerX(OnCheck, null, 300, 300) { Async = true };
 
-        //void frm_FormClosing(Object sender, FormClosingEventArgs e)
+        //if (Parent is Form frm)
         //{
-        //    SaveInfo();
-
-        //    _timer.Dispose();
-        //    _timer = null;
-
-        //    if (Port != null) Port.Close();
+        //    frm.FormClosing += frm_FormClosing;
         //}
-        #endregion
+    }
 
-        #region 加载保存信息
-        /// <summary>加载配置信息</summary>
-        public void LoadInfo()
-        {
-            ShowPorts();
+    //void frm_FormClosing(Object sender, FormClosingEventArgs e)
+    //{
+    //    SaveInfo();
 
-            //BindMenu(mi数据位, On数据位Click, new Int32[] { 5, 6, 7, 8 });
-            //BindMenu(mi停止位, On停止位Click, Enum.GetValues(typeof(StopBits)));
-            //BindMenu(mi校验, On校验Click, Enum.GetValues(typeof(Parity)));
+    //    _timer.Dispose();
+    //    _timer = null;
 
-            //var arr = new Int32[] { 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200, 128000, 194000, 256000, 512000, 1024000, 2048000 };
-            var bs = new List<Int32>(new Int32[] { 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200, 128000, 194000, 256000, 512000, 1024000, 2048000 });
+    //    if (Port != null) Port.Close();
+    //}
+    #endregion
 
-            var cfg = SerialPortConfig.Current;
-            if (cfg.BaudRate > 0 && !bs.Contains(cfg.BaudRate)) bs.Add(cfg.BaudRate);
-            cbBaudrate.AppendValues(bs);
-
-            cbName.SetActive(cfg.PortName);
-            cbBaudrate.SetActive(cfg.BaudRate);
-            //SetMenuItem(mi数据位, cfg.DataBits);
-            //SetMenuItem(mi停止位, cfg.StopBits);
-            //SetMenuItem(mi校验, cfg.Parity);
-
-            //cbEncoding.DataSource = new String[] { Encoding.UTF8.WebName, Encoding.ASCII.WebName, Encoding.UTF8.WebName };
-            // 添加编码子菜单
-            var encs = new Encoding[] { Encoding.UTF8, Encoding.ASCII, Encoding.UTF7, Encoding.Unicode, Encoding.BigEndianUnicode/*, Encoding.UTF32,Encoding.GetEncoding("GB2312") */};
-            var list = new List<Encoding>(encs);
-            // 暂时不用这么多编码
-            //list.AddRange(Encoding.GetEncodings().Select(e => e.GetEncoding()).Where(e => !encs.Contains(e)));
-            var k = 0;
-            //foreach (var item in list)
-            //{
-            //    if (k++ == encs.Length)
-            //    {
-            //        var sep = new ToolStripSeparator();
-            //        mi字符串编码.DropDownItems.Add(sep);
-            //    }
-            //    var ti = mi字符串编码.DropDownItems.Add(item.EncodingName) as ToolStripMenuItem;
-            //    ti.Name = item.WebName;
-            //    ti.Tag = item;
-            //    ti.Checked = item.WebName.EqualIgnoreCase(cfg.WebEncoding);
-            //    ti.Click += On编码Click;
-            //}
-
-            //miHEX编码接收.Checked = cfg.HexShow;
-            //mi字符串编码.Checked = !cfg.HexShow;
-            //miHex不换行.Checked = !cfg.HexNewLine;
-            //miHex自动换行.Checked = cfg.HexNewLine;
-            //miHEX编码发送.Checked = cfg.HexSend;
-            //miDTR.Checked = cfg.DtrEnable;
-            //miRTS.Checked = cfg.RtsEnable;
-            //miBreak.Checked = cfg.BreakState;
-        }
+    #region 加载保存信息
+    /// <summary>加载配置信息</summary>
+    public void LoadInfo()
+    {
+        ShowPorts();
+
+        //BindMenu(mi数据位, On数据位Click, new Int32[] { 5, 6, 7, 8 });
+        //BindMenu(mi停止位, On停止位Click, Enum.GetValues(typeof(StopBits)));
+        //BindMenu(mi校验, On校验Click, Enum.GetValues(typeof(Parity)));
+
+        //var arr = new Int32[] { 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200, 128000, 194000, 256000, 512000, 1024000, 2048000 };
+        var bs = new List<Int32>(new Int32[] { 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200, 128000, 194000, 256000, 512000, 1024000, 2048000 });
+
+        var cfg = SerialPortConfig.Current;
+        if (cfg.BaudRate > 0 && !bs.Contains(cfg.BaudRate)) bs.Add(cfg.BaudRate);
+        cbBaudrate.AppendValues(bs);
+
+        cbName.SetActive(cfg.PortName);
+        cbBaudrate.SetActive(cfg.BaudRate);
+        //SetMenuItem(mi数据位, cfg.DataBits);
+        //SetMenuItem(mi停止位, cfg.StopBits);
+        //SetMenuItem(mi校验, cfg.Parity);
+
+        //cbEncoding.DataSource = new String[] { Encoding.UTF8.WebName, Encoding.ASCII.WebName, Encoding.UTF8.WebName };
+        // 添加编码子菜单
+        var encs = new Encoding[] { Encoding.UTF8, Encoding.ASCII, Encoding.UTF7, Encoding.Unicode, Encoding.BigEndianUnicode/*, Encoding.UTF32,Encoding.GetEncoding("GB2312") */};
+        var list = new List<Encoding>(encs);
+        // 暂时不用这么多编码
+        //list.AddRange(Encoding.GetEncodings().Select(e => e.GetEncoding()).Where(e => !encs.Contains(e)));
+        var k = 0;
+        //foreach (var item in list)
+        //{
+        //    if (k++ == encs.Length)
+        //    {
+        //        var sep = new ToolStripSeparator();
+        //        mi字符串编码.DropDownItems.Add(sep);
+        //    }
+        //    var ti = mi字符串编码.DropDownItems.Add(item.EncodingName) as ToolStripMenuItem;
+        //    ti.Name = item.WebName;
+        //    ti.Tag = item;
+        //    ti.Checked = item.WebName.EqualIgnoreCase(cfg.WebEncoding);
+        //    ti.Click += On编码Click;
+        //}
 
-        /// <summary>保存配置信息</summary>
-        public void SaveInfo()
+        //miHEX编码接收.Checked = cfg.HexShow;
+        //mi字符串编码.Checked = !cfg.HexShow;
+        //miHex不换行.Checked = !cfg.HexNewLine;
+        //miHex自动换行.Checked = cfg.HexNewLine;
+        //miHEX编码发送.Checked = cfg.HexSend;
+        //miDTR.Checked = cfg.DtrEnable;
+        //miRTS.Checked = cfg.RtsEnable;
+        //miBreak.Checked = cfg.BreakState;
+    }
+
+    /// <summary>保存配置信息</summary>
+    public void SaveInfo()
+    {
+        try
         {
-            try
-            {
-                var cfg = SerialPortConfig.Current;
-                cfg.PortName = cbName.GetActiveObject() + "";
-                if (cbBaudrate.GetActiveObject() != null)
-                    cfg.BaudRate = cbBaudrate.GetActiveObject().ToInt();
-                //else
-                //    cfg.BaudRate = cbBaudrate.ToInt();
-                //cfg.DataBits = (Int32)cbDataBit.GetActiveObject();
-                //cfg.StopBits = (StopBits)cbStopBit.GetActiveObject();
-                //cfg.Parity = (Parity)cbParity.GetActiveObject();
-                //cfg.Encoding = (Encoding)cbEncoding.GetActiveObject();
-                //cfg.WebEncoding = cbEncoding.GetActiveObject() + "";
-
-                //cfg.HexSend = chkHEXSend.Checked;
-                //cfg.HexShow = chkHEXShow.Checked;
-
-                cfg.Save();
-            }
-            catch (Exception ex)
-            {
-                XTrace.WriteException(ex);
-            }
+            var cfg = SerialPortConfig.Current;
+            cfg.PortName = cbName.GetActiveObject() + "";
+            if (cbBaudrate.GetActiveObject() != null)
+                cfg.BaudRate = cbBaudrate.GetActiveObject().ToInt();
+            //else
+            //    cfg.BaudRate = cbBaudrate.ToInt();
+            //cfg.DataBits = (Int32)cbDataBit.GetActiveObject();
+            //cfg.StopBits = (StopBits)cbStopBit.GetActiveObject();
+            //cfg.Parity = (Parity)cbParity.GetActiveObject();
+            //cfg.Encoding = (Encoding)cbEncoding.GetActiveObject();
+            //cfg.WebEncoding = cbEncoding.GetActiveObject() + "";
+
+            //cfg.HexSend = chkHEXSend.Checked;
+            //cfg.HexShow = chkHEXShow.Checked;
+
+            cfg.Save();
         }
-
-        String _ports = null;
-        DateTime _nextport = DateTime.MinValue;
-        /// <summary>下拉框显示串口</summary>
-        public void ShowPorts()
+        catch (Exception ex)
         {
-            if (_nextport > DateTime.Now) return;
-            _nextport = DateTime.Now.AddSeconds(1);
-
-            var ps = SerialTransport.GetPortNames();
-            var str = String.Join(",", ps);
-            // 如果端口有所改变,则重新绑定
-            if (_ports != str)
-            {
-                if (_ports != null) "串口有改变".SpeechTip();
-
-                _ports = str;
-
-                Application.Invoke((s, e) =>
-                {
-                    var old = cbName.GetActiveObject() + "";
-                    cbName.AppendValues(ps.ToList());
-                    if (!String.IsNullOrEmpty(old) && Array.IndexOf(ps, old) >= 0) cbName.SetActive(old);
-                });
-            }
+            XTrace.WriteException(ex);
         }
-        #endregion
-
-        #region 菜单设置
-        ///// <summary>右键菜单</summary>
-        //public ContextMenuStrip Menu { get { return contextMenuStrip1; } }
-
-        //void On数据位Click(Object sender, EventArgs e)
-        //{
-        //    SelectMenu(sender);
-
-        //    var mi = sender as ToolStripMenuItem;
-        //    var cfg = SerialPortConfig.Current;
-        //    cfg.DataBits = (Int32)mi.Tag;
-        //}
-
-        //void On停止位Click(Object sender, EventArgs e)
-        //{
-        //    SelectMenu(sender);
-
-        //    var mi = sender as ToolStripMenuItem;
-        //    var cfg = SerialPortConfig.Current;
-        //    cfg.StopBits = (StopBits)mi.Tag;
-        //}
-
-        //void On校验Click(Object sender, EventArgs e)
-        //{
-        //    SelectMenu(sender);
-
-        //    var mi = sender as ToolStripMenuItem;
-        //    var cfg = SerialPortConfig.Current;
-        //    cfg.Parity = (Parity)mi.Tag;
-        //}
-
-        //void On编码Click(Object sender, EventArgs e)
-        //{
-        //    // 不要选其它
-        //    var mi = sender as ToolStripMenuItem;
-        //    if (mi == null) return;
-        //    //foreach (ToolStripMenuItem item in (mi.OwnerItem as ToolStripMenuItem).DropDownItems)
-        //    //{
-        //    //    item.Checked = item == mi;
-        //    //}
-        //    SelectMenu(sender);
-
-        //    // 保存编码
-        //    var cfg = SerialPortConfig.Current;
-        //    cfg.WebEncoding = mi.Name;
-        //}
-
-        //private void mi字符串编码_Click(Object sender, EventArgs e)
-        //{
-        //    var cfg = SerialPortConfig.Current;
-        //    //cfg.HexShow = miHEX编码接收.Checked = !mi字符串编码.Checked;
-        //    cfg.HexShow = SelectMenu(sender, mi字符串编码, miHEX编码接收) == 1;
-
-        //    // 收起菜单
-        //    contextMenuStrip1.Hide();
-        //}
-
-        //private void miHex自动换行_Click(Object sender, EventArgs e)
-        //{
-        //    var cfg = SerialPortConfig.Current;
-        //    //cfg.HexNewLine = ti.Tag.ToBoolean();
-        //    cfg.HexNewLine = SelectMenu(sender) == 1;
-        //}
-
-        //private void miHEX编码发送_Click(Object sender, EventArgs e)
-        //{
-        //    var cfg = SerialPortConfig.Current;
-        //    miHEX编码发送.Checked = !miHEX编码发送.Checked;
-        //    cfg.HexSend = miHEX编码发送.Checked;
-        //}
+    }
 
-        //private void miDTR_Click(Object sender, EventArgs e)
-        //{
-        //    var mi = sender as ToolStripMenuItem;
-        //    mi.Checked = !mi.Checked;
-        //}
-        #endregion
+    String _ports = null;
+    DateTime _nextport = DateTime.MinValue;
+    /// <summary>下拉框显示串口</summary>
+    public void ShowPorts()
+    {
+        if (_nextport > DateTime.Now) return;
+        _nextport = DateTime.Now.AddSeconds(1);
 
-        #region 窗体
-        /// <summary>连接串口</summary>
-        public void Connect()
+        var ps = SerialTransport.GetPortNames();
+        var str = String.Join(",", ps);
+        // 如果端口有所改变,则重新绑定
+        if (_ports != str)
         {
-            var name = cbName.GetActiveObject() + "";
-            if (String.IsNullOrEmpty(name))
-            {
-                "请选择串口".SpeechTip();
-                MessageBox.Show("请选择串口!");
-                //cbName.Focus();
-                return;
-            }
-            var p = name.IndexOf("(");
-            if (p > 0) name = name.Substring(0, p);
+            if (_ports != null) "串口有改变".SpeechTip();
 
-            SaveInfo();
-            var cfg = SerialPortConfig.Current;
+            _ports = str;
 
-            var pt = Port;
-            if (pt != null)
+            Application.Invoke((s, e) =>
             {
-                // 如果上次没有关闭,则关闭
-                try
-                {
-                    pt.Close();
-                }
-                catch
-                {
-                    pt = null;
-                }
-            }
-            if (pt == null) pt = new SerialTransport();
-            pt.PortName = name;
-            pt.BaudRate = cfg.BaudRate;
-            pt.Parity = cfg.Parity;
-            pt.DataBits = cfg.DataBits;
-            pt.StopBits = cfg.StopBits;
-
-            //pt.Open();
-
-            pt.Disconnected += Port_Disconnected;
-            pt.Received += OnReceived;
-            pt.Open();
-
-            //pt.EnsureCreate();
-
-            Port = pt;
-
-            var sp = pt.Serial;
-            // 这几个需要打开端口以后才能设置
-            //try
-            //{
-            //    if (sp.DtrEnable != miDTR.Checked) sp.DtrEnable = miDTR.Checked;
-            //    if (sp.RtsEnable != miRTS.Checked) sp.RtsEnable = miRTS.Checked;
-            //    if (sp.BreakState != miBreak.Checked) sp.BreakState = miBreak.Checked;
-            //}
-            //catch { }
-
-            Sensitive = false;
+                var old = cbName.GetActiveObject() + "";
+                cbName.AppendValues(ps.ToList());
+                if (!String.IsNullOrEmpty(old) && Array.IndexOf(ps, old) >= 0) cbName.SetActive(old);
+            });
         }
-
-        void Port_Disconnected(Object sender, EventArgs e)
+    }
+    #endregion
+
+    #region 菜单设置
+    ///// <summary>右键菜单</summary>
+    //public ContextMenuStrip Menu { get { return contextMenuStrip1; } }
+
+    //void On数据位Click(Object sender, EventArgs e)
+    //{
+    //    SelectMenu(sender);
+
+    //    var mi = sender as ToolStripMenuItem;
+    //    var cfg = SerialPortConfig.Current;
+    //    cfg.DataBits = (Int32)mi.Tag;
+    //}
+
+    //void On停止位Click(Object sender, EventArgs e)
+    //{
+    //    SelectMenu(sender);
+
+    //    var mi = sender as ToolStripMenuItem;
+    //    var cfg = SerialPortConfig.Current;
+    //    cfg.StopBits = (StopBits)mi.Tag;
+    //}
+
+    //void On校验Click(Object sender, EventArgs e)
+    //{
+    //    SelectMenu(sender);
+
+    //    var mi = sender as ToolStripMenuItem;
+    //    var cfg = SerialPortConfig.Current;
+    //    cfg.Parity = (Parity)mi.Tag;
+    //}
+
+    //void On编码Click(Object sender, EventArgs e)
+    //{
+    //    // 不要选其它
+    //    var mi = sender as ToolStripMenuItem;
+    //    if (mi == null) return;
+    //    //foreach (ToolStripMenuItem item in (mi.OwnerItem as ToolStripMenuItem).DropDownItems)
+    //    //{
+    //    //    item.Checked = item == mi;
+    //    //}
+    //    SelectMenu(sender);
+
+    //    // 保存编码
+    //    var cfg = SerialPortConfig.Current;
+    //    cfg.WebEncoding = mi.Name;
+    //}
+
+    //private void mi字符串编码_Click(Object sender, EventArgs e)
+    //{
+    //    var cfg = SerialPortConfig.Current;
+    //    //cfg.HexShow = miHEX编码接收.Checked = !mi字符串编码.Checked;
+    //    cfg.HexShow = SelectMenu(sender, mi字符串编码, miHEX编码接收) == 1;
+
+    //    // 收起菜单
+    //    contextMenuStrip1.Hide();
+    //}
+
+    //private void miHex自动换行_Click(Object sender, EventArgs e)
+    //{
+    //    var cfg = SerialPortConfig.Current;
+    //    //cfg.HexNewLine = ti.Tag.ToBoolean();
+    //    cfg.HexNewLine = SelectMenu(sender) == 1;
+    //}
+
+    //private void miHEX编码发送_Click(Object sender, EventArgs e)
+    //{
+    //    var cfg = SerialPortConfig.Current;
+    //    miHEX编码发送.Checked = !miHEX编码发送.Checked;
+    //    cfg.HexSend = miHEX编码发送.Checked;
+    //}
+
+    //private void miDTR_Click(Object sender, EventArgs e)
+    //{
+    //    var mi = sender as ToolStripMenuItem;
+    //    mi.Checked = !mi.Checked;
+    //}
+    #endregion
+
+    #region 窗体
+    /// <summary>连接串口</summary>
+    public void Connect()
+    {
+        var name = cbName.GetActiveObject() + "";
+        if (String.IsNullOrEmpty(name))
         {
-            Disconnect();
+            "请选择串口".SpeechTip();
+            MessageBox.Show("请选择串口!");
+            //cbName.Focus();
+            return;
         }
+        var p = name.IndexOf("(");
+        if (p > 0) name = name.Substring(0, p);
 
-        /// <summary>断开串口连接</summary>
-        public void Disconnect()
+        SaveInfo();
+        var cfg = SerialPortConfig.Current;
+
+        var pt = Port;
+        if (pt != null)
         {
-            var pt = Port;
-            if (pt != null)
+            // 如果上次没有关闭,则关闭
+            try
             {
-                Port = null;
-                pt.Disconnected -= Port_Disconnected;
-                pt.Received -= OnReceived;
                 pt.Close();
             }
+            catch
+            {
+                pt = null;
+            }
+        }
+        if (pt == null) pt = new SerialTransport();
+        pt.PortName = name;
+        pt.BaudRate = cfg.BaudRate;
+        pt.Parity = cfg.Parity;
+        pt.DataBits = cfg.DataBits;
+        pt.StopBits = cfg.StopBits;
 
-            ShowPorts();
+        //pt.Open();
 
-            Sensitive = true;
-        }
+        pt.Disconnected += Port_Disconnected;
+        pt.Received += OnReceived;
+        pt.Open();
 
-        private void OnCheck(Object state)
-        {
-            if (Sensitive) ShowPorts();
-        }
-        #endregion
+        //pt.EnsureCreate();
 
-        #region 菜单辅助
-        //ToolStripItem FindMenu(String name)
-        //{
-        //    return contextMenuStrip1.Items.Find(name, false)[0];
-        //}
+        Port = pt;
 
-        //void BindMenu(ToolStripMenuItem ti, EventHandler handler, IEnumerable em)
+        var sp = pt.Serial;
+        // 这几个需要打开端口以后才能设置
+        //try
         //{
-        //    ti.DropDownItems.Clear();
-        //    foreach (var item in em)
-        //    {
-        //        var tsi = ti.DropDownItems.Add(item + "");
-        //        tsi.Tag = item;
-        //        tsi.Click += handler;
-        //    }
+        //    if (sp.DtrEnable != miDTR.Checked) sp.DtrEnable = miDTR.Checked;
+        //    if (sp.RtsEnable != miRTS.Checked) sp.RtsEnable = miRTS.Checked;
+        //    if (sp.BreakState != miBreak.Checked) sp.BreakState = miBreak.Checked;
         //}
+        //catch { }
 
-        //void SetMenuItem(ToolStripMenuItem ti, Object value)
-        //{
-        //    foreach (ToolStripMenuItem item in ti.DropDownItems)
-        //    {
-        //        item.Checked = item + "" == value + "";
-        //    }
-        //}
+        Sensitive = false;
+    }
 
-        ///// <summary>在一个菜单列表中点击某一个菜单,改变菜单选择并返回选中的菜单索引</summary>
-        ///// <param name="sender"></param>
-        ///// <param name="ms"></param>
-        //Int32 SelectMenu(Object sender, params ToolStripMenuItem[] ms)
-        //{
-        //    var mi = sender as ToolStripMenuItem;
-        //    if (mi == null) return -1;
+    void Port_Disconnected(Object sender, EventArgs e)
+    {
+        Disconnect();
+    }
 
-        //    var idx = -1;
-        //    for (var i = 0; i < ms.Length; i++)
-        //    {
-        //        if (ms[i] == mi)
-        //        {
-        //            ms[i].Checked = true;
-        //            idx = i;
-        //        }
-        //        else
-        //            ms[i].Checked = false;
-        //    }
-        //    return idx;
-        //}
+    /// <summary>断开串口连接</summary>
+    public void Disconnect()
+    {
+        var pt = Port;
+        if (pt != null)
+        {
+            Port = null;
+            pt.Disconnected -= Port_Disconnected;
+            pt.Received -= OnReceived;
+            pt.Close();
+        }
 
-        ///// <summary>在同级菜单中选择</summary>
-        ///// <param name="sender"></param>
-        ///// <param name="tsi"></param>
-        ///// <returns></returns>
-        //Int32 SelectMenu(Object sender, ToolStripItem tsi = null)
-        //{
-        //    if (tsi == null) tsi = (sender as ToolStripMenuItem).OwnerItem;
+        ShowPorts();
 
-        //    var ms = (tsi as ToolStripMenuItem).DropDownItems;
-        //    if (ms == null) return -1;
+        Sensitive = true;
+    }
 
-        //    var arr = new ToolStripMenuItem[ms.Count];
-        //    ms.CopyTo(arr, 0);
+    private void OnCheck(Object state)
+    {
+        if (Sensitive) ShowPorts();
+    }
+    #endregion
+
+    #region 菜单辅助
+    //ToolStripItem FindMenu(String name)
+    //{
+    //    return contextMenuStrip1.Items.Find(name, false)[0];
+    //}
+
+    //void BindMenu(ToolStripMenuItem ti, EventHandler handler, IEnumerable em)
+    //{
+    //    ti.DropDownItems.Clear();
+    //    foreach (var item in em)
+    //    {
+    //        var tsi = ti.DropDownItems.Add(item + "");
+    //        tsi.Tag = item;
+    //        tsi.Click += handler;
+    //    }
+    //}
+
+    //void SetMenuItem(ToolStripMenuItem ti, Object value)
+    //{
+    //    foreach (ToolStripMenuItem item in ti.DropDownItems)
+    //    {
+    //        item.Checked = item + "" == value + "";
+    //    }
+    //}
+
+    ///// <summary>在一个菜单列表中点击某一个菜单,改变菜单选择并返回选中的菜单索引</summary>
+    ///// <param name="sender"></param>
+    ///// <param name="ms"></param>
+    //Int32 SelectMenu(Object sender, params ToolStripMenuItem[] ms)
+    //{
+    //    var mi = sender as ToolStripMenuItem;
+    //    if (mi == null) return -1;
+
+    //    var idx = -1;
+    //    for (var i = 0; i < ms.Length; i++)
+    //    {
+    //        if (ms[i] == mi)
+    //        {
+    //            ms[i].Checked = true;
+    //            idx = i;
+    //        }
+    //        else
+    //            ms[i].Checked = false;
+    //    }
+    //    return idx;
+    //}
+
+    ///// <summary>在同级菜单中选择</summary>
+    ///// <param name="sender"></param>
+    ///// <param name="tsi"></param>
+    ///// <returns></returns>
+    //Int32 SelectMenu(Object sender, ToolStripItem tsi = null)
+    //{
+    //    if (tsi == null) tsi = (sender as ToolStripMenuItem).OwnerItem;
+
+    //    var ms = (tsi as ToolStripMenuItem).DropDownItems;
+    //    if (ms == null) return -1;
+
+    //    var arr = new ToolStripMenuItem[ms.Count];
+    //    ms.CopyTo(arr, 0);
+
+    //    return SelectMenu(sender, arr);
+    //}
+    #endregion
+
+    #region 收发数据
+    /// <summary>发送字节数组</summary>
+    /// <param name="data"></param>
+    public void Send(Byte[] data)
+    {
+        if (data == null || data.Length <= 0) return;
 
-        //    return SelectMenu(sender, arr);
-        //}
-        #endregion
+        BytesOfSent += data.Length;
 
-        #region 收发数据
-        /// <summary>发送字节数组</summary>
-        /// <param name="data"></param>
-        public void Send(Byte[] data)
-        {
-            if (data == null || data.Length <= 0) return;
+        Port.Send(data);
+    }
 
-            BytesOfSent += data.Length;
+    /// <summary>发送字符串。根据配置进行十六进制编码</summary>
+    /// <param name="str"></param>
+    /// <returns>发送字节数</returns>
+    public Int32 Send(String str)
+    {
+        var cfg = SerialPortConfig.Current;
+        // 16进制发送
+        Byte[] data = null;
+        if (cfg.HexSend)
+            data = str.ToHex();
+        else
+            data = cfg.Encoding.GetBytes(str);
 
-            Port.Send(data);
-        }
+        Send(data);
 
-        /// <summary>发送字符串。根据配置进行十六进制编码</summary>
-        /// <param name="str"></param>
-        /// <returns>发送字节数</returns>
-        public Int32 Send(String str)
-        {
-            var cfg = SerialPortConfig.Current;
-            // 16进制发送
-            Byte[] data = null;
-            if (cfg.HexSend)
-                data = str.ToHex();
-            else
-                data = cfg.Encoding.GetBytes(str);
+        return data.Length;
+    }
 
-            Send(data);
+    //public event SerialReceived Received;
+    /// <summary>收到数据时触发。第一个参数是数据,第二个参数返回是否继续往下传递数据</summary>
+    [Browsable(true)]
+    [EditorBrowsable(EditorBrowsableState.Always)]
+    public event EventHandler<BufferEventArgs> Received;
+
+    /// <summary>收到数据时转为字符串后触发该事件。注意字符串编码和十六进制编码。</summary>
+    /// <remarks>如果需要收到的数据,可直接在<seealso cref="Port"/>上挂载事件</remarks>
+    [Browsable(true)]
+    [EditorBrowsable(EditorBrowsableState.Always)]
+    public event EventHandler<StringEventArgs> ReceivedString;
+
+    MemoryStream _stream;
+    StreamReader _reader;
+    void OnReceived(Object sender, ReceivedEventArgs e)
+    {
+        var data = e.Packet.ReadBytes();
+        if (data == null || data.Length < 1) return;
 
-            return data.Length;
-        }
+        BytesOfReceived += data.Length;
 
-        //public event SerialReceived Received;
-        /// <summary>收到数据时触发。第一个参数是数据,第二个参数返回是否继续往下传递数据</summary>
-        [Browsable(true)]
-        [EditorBrowsable(EditorBrowsableState.Always)]
-        public event EventHandler<BufferEventArgs> Received;
-
-        /// <summary>收到数据时转为字符串后触发该事件。注意字符串编码和十六进制编码。</summary>
-        /// <remarks>如果需要收到的数据,可直接在<seealso cref="Port"/>上挂载事件</remarks>
-        [Browsable(true)]
-        [EditorBrowsable(EditorBrowsableState.Always)]
-        public event EventHandler<StringEventArgs> ReceivedString;
-
-        MemoryStream _stream;
-        StreamReader _reader;
-        void OnReceived(Object sender, ReceivedEventArgs e)
+        // 处理数据委托
+        if (Received != null)
         {
-            var data = e.Packet.ReadBytes();
-            if (data == null || data.Length < 1) return;
-
-            BytesOfReceived += data.Length;
-
-            // 处理数据委托
-            if (Received != null)
-            {
-                var e2 = new BufferEventArgs { Value = data };
-                Received(this, e2);
-                if (!e2.Cancel) return;
-                // 外部可能修改了数据
-                data = e2.Value;
-                //if (!BufferEventArgs.Invoke(Received, data)) return null;
-            }
+            var e2 = new BufferEventArgs { Value = data };
+            Received(this, e2);
+            if (!e2.Cancel) return;
+            // 外部可能修改了数据
+            data = e2.Value;
+            //if (!BufferEventArgs.Invoke(Received, data)) return null;
+        }
 
-            // 处理字符串委托
-            if (ReceivedString == null) return;
+        // 处理字符串委托
+        if (ReceivedString == null) return;
 
-            var cfg = SerialPortConfig.Current;
+        var cfg = SerialPortConfig.Current;
 
-            var line = "";
-            if (cfg.HexShow)
-            {
-                if (data.Length > 32)
-                    line = $"[{data.Length}]=\r\n{data.ToHex("-", 32)}";
-                else
-                    line = $"[{data.Length}]={data.ToHex("-", 32)}";
-                if (cfg.HexNewLine) line += Environment.NewLine;
-            }
+        var line = "";
+        if (cfg.HexShow)
+        {
+            if (data.Length > 32)
+                line = $"[{data.Length}]=\r\n{data.ToHex("-", 32)}";
             else
-            {
-                line = cfg.Encoding.GetString(data);
-                if (_stream == null)
-                    _stream = new MemoryStream();
-                else if (_stream.Length > 10 * 1024 && _stream.Position == _stream.Length) // 达到最大大小时,从头开始使用
-                    _stream = new MemoryStream();
-                _stream.Write(data);
-                _stream.Seek(-1 * data.Length, SeekOrigin.Current);
-
-                if (_reader == null ||
-                    _reader.BaseStream != _stream ||
-                    _reader.CurrentEncoding != cfg.Encoding)
-                    _reader = new StreamReader(_stream, cfg.Encoding);
-                line = _reader.ReadToEnd();
-                // 替换掉无效数据
-                line = line.Replace("\0", null);
-            }
-
-            ReceivedString?.Invoke(this, new StringEventArgs { Value = line });
+                line = $"[{data.Length}]={data.ToHex("-", 32)}";
+            if (cfg.HexNewLine) line += Environment.NewLine;
         }
-        #endregion
-
-        #region 收发统计
-        /// <summary>清空发送</summary>
-        public void ClearSend()
+        else
         {
-            BytesOfSent = 0;
-
-            var sp = Port;
-            if (sp != null)
-            {
-                try
-                {
-                    if (sp.Serial != null) sp.Serial.DiscardOutBuffer();
-                }
-                catch { }
-            }
+            line = cfg.Encoding.GetString(data);
+            if (_stream == null)
+                _stream = new MemoryStream();
+            else if (_stream.Length > 10 * 1024 && _stream.Position == _stream.Length) // 达到最大大小时,从头开始使用
+                _stream = new MemoryStream();
+            _stream.Write(data);
+            _stream.Seek(-1 * data.Length, SeekOrigin.Current);
+
+            if (_reader == null ||
+                _reader.BaseStream != _stream ||
+                _reader.CurrentEncoding != cfg.Encoding)
+                _reader = new StreamReader(_stream, cfg.Encoding);
+            line = _reader.ReadToEnd();
+            // 替换掉无效数据
+            line = line.Replace("\0", null);
         }
 
-        /// <summary>清空接收</summary>
-        public void ClearReceive()
-        {
-            BytesOfReceived = 0;
-            if (_stream != null) _stream.SetLength(0);
+        ReceivedString?.Invoke(this, new StringEventArgs { Value = line });
+    }
+    #endregion
 
-            var sp = Port;
-            if (sp != null)
+    #region 收发统计
+    /// <summary>清空发送</summary>
+    public void ClearSend()
+    {
+        BytesOfSent = 0;
+
+        var sp = Port;
+        if (sp != null)
+        {
+            try
             {
-                try
-                {
-                    if (sp.Serial != null) sp.Serial.DiscardInBuffer();
-                }
-                catch { }
+                if (sp.Serial != null) sp.Serial.DiscardOutBuffer();
             }
+            catch { }
         }
-        #endregion
     }
 
-    /// <summary>字符串事件参数</summary>
-    public class StringEventArgs : EventArgs
+    /// <summary>清空接收</summary>
+    public void ClearReceive()
     {
-        private String _Value;
-        /// <summary>字符串值</summary>
-        public String Value { get { return _Value; } set { _Value = value; } }
+        BytesOfReceived = 0;
+        if (_stream != null) _stream.SetLength(0);
+
+        var sp = Port;
+        if (sp != null)
+        {
+            try
+            {
+                if (sp.Serial != null) sp.Serial.DiscardInBuffer();
+            }
+            catch { }
+        }
     }
+    #endregion
+}
 
-    /// <summary>缓冲区事件参数</summary>
-    public class BufferEventArgs : CancelEventArgs
-    {
-        private Byte[] _Value;
-        /// <summary>缓冲区数据</summary>
-        public Byte[] Value { get { return _Value; } set { _Value = value; } }
+/// <summary>字符串事件参数</summary>
+public class StringEventArgs : EventArgs
+{
+    private String _Value;
+    /// <summary>字符串值</summary>
+    public String Value { get { return _Value; } set { _Value = value; } }
+}
 
-        //public static Boolean Invoke(EventHandler<BufferEventArgs> handler, Object sender, Byte[] buf)
-        //{
-        //    var e = new BufferEventArgs { Value = buf, Cancel = false };
-        //    handler(sender, e);
-        //    return e.Cancel;
-        //}
-    }
+/// <summary>缓冲区事件参数</summary>
+public class BufferEventArgs : CancelEventArgs
+{
+    private Byte[] _Value;
+    /// <summary>缓冲区数据</summary>
+    public Byte[] Value { get { return _Value; } set { _Value = value; } }
+
+    //public static Boolean Invoke(EventHandler<BufferEventArgs> handler, Object sender, Byte[] buf)
+    //{
+    //    var e = new BufferEventArgs { Value = buf, Cancel = false };
+    //    handler(sender, e);
+    //    return e.Cancel;
+    //}
 }
\ No newline at end of file
Modified +1 -1
diff --git a/XCoderLinux/XNet/FrmMain.cs b/XCoderLinux/XNet/FrmMain.cs
index 90a320b..315b0c6 100644
--- a/XCoderLinux/XNet/FrmMain.cs
+++ b/XCoderLinux/XNet/FrmMain.cs
@@ -426,7 +426,7 @@ namespace XNet
                     for (var i = 0; i < count && _Server != null; i++)
                     {
                         var sw = Stopwatch.StartNew();
-                        var cs = await _Server.SendAllAsync(buf);
+                        var cs = await _Server.SendAllAsync((ArrayPacket)buf);
                         sw.Stop();
                         BizLog.Info("{3}/{4} 已向[{0}]个客户端发送[{1}]数据 {2:n0}ms", cs, buf.Length, sw.ElapsedMilliseconds, i + 1, count);
                         if (sleep > 0) await TaskEx.Delay(sleep);