Quellcode durchsuchen

优化PLC数据读取功能

callm vor 21 Stunden
Ursprung
Commit
dfbec50327

+ 18 - 0
UAS_MES_ZKLH/.workbuddy/memory/2026-09-17.md

@@ -30,6 +30,24 @@
 - **假设项**:主键序列名 SINGLEBOARD_SEQ 也是按规律猜的;SB_STEPCODE 用 'CLEAN'/'DRY',如实际值不同需改两个文件顶部常量。
 - 布局与 Make_PLCMonitor 同风格:GroupBoxWithBorder + NormalButton + EnterTextBox + asc 自适应。
 
+### PLCMonitor 读取超时修复(2026-09-22,双协议支持)
+- 现象:连接成功但读数报"连接方没有正确答复"超时。原因:串口转网口模块是透传模式,
+  需要发 Modbus RTU 裸帧(站号+功能码+CRC16),原代码只发了带 MBAP 头的 Modbus TCP 帧。
+- 改造:ReadRegisters 按界面协议单选分发 → `ReadRtuRegisters`(RTU裸帧+CRC16校验,CRC低字节在前,
+  校验响应CRC并提示串口参数不一致)/ `ReadTcpRegisters`(原MBAP实现)。
+- Designer 加 rdoRTU(默认)/rdoTCP 单选;ReceiveTimeout/SendTimeout 3s→5s;
+  读取失败日志带协议模式 + 切换提示。
+- CRC16 已用标准向量验证:01 03 00 00 00 02 → C4 0B(低前) ✓;D141 读2个 → 54 20。
+
+### PLCMonitor 二次修复(2026-09-22,关键:D寄存器地址偏移)
+- 模块型号确认:**ETH-DVP-2P**(东莞艾莫迅,台达DVP以太网通讯处理器),协议 **MODBUS TCP**、端口固定502、
+  默认IP 192.168.1.150、网页账号密码 amx666。手册在 `C:\Users\callm\Desktop\客户资料\中科力函\20250528_233757_.pdf`。
+- **核心坑:D寄存器 Modbus 地址不是 D 编号本身**。手册第七章寄存器表:
+  D0~4095 → Modbus地址 = 0x1000 + D编号(如 D141 → 0x108D=4237);D4096+ 是 0x9000 起另一段。
+  之前直接发 141(0x008D) 所以超时。
+- 修复:新增 `DVP_D_OFFSET = 0x1000`,读取地址 = DVP_D_OFFSET + 141;默认协议改为 ModbusTCP(rdoTCP.Checked=true)。
+- RTU 模式保留(适配纯透传串口模块的场景),本模块用不到。
+
 ### 温度入库(2026-09-20,表 TEMPLOG)
 - 每次采集成功后往 TEMPLOG 插 2 条:TL_ID=TEMPLOG_SEQ.nextval、TL_DATE=sysdate、
   TL_DEVICEID=舱号('1#'/'2#')、TL_TP=按小数位换算后的温度值。

BIN
UAS_MES_ZKLH/.workbuddy/tmp_pdf_img/page20_1.png


BIN
UAS_MES_ZKLH/.workbuddy/tmp_pdf_img/page20_2.png


BIN
UAS_MES_ZKLH/.workbuddy/tmp_pdf_img/page21_1.png


BIN
UAS_MES_ZKLH/.workbuddy/tmp_pdf_img/page21_2.png


+ 72 - 21
UAS_MES_ZKLH/FunctionCode/Make/Make_PLCMonitor.Designer.cs

@@ -29,6 +29,7 @@
         private void InitializeComponent()
         {
             this.components = new System.ComponentModel.Container();
+            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Make_PLCMonitor));
             this.grpConnect = new UAS_MES_NEW.CustomControl.GroupBoxWithBorder.GroupBoxWithBorder();
             this.lblIp = new System.Windows.Forms.Label();
             this.ip = new UAS_MES_NEW.CustomControl.TextBoxWithIcon.EnterTextBox();
@@ -40,6 +41,8 @@
             this.interval = new System.Windows.Forms.NumericUpDown();
             this.btnConnect = new UAS_MES_NEW.CustomControl.ButtonUtil.NormalButton();
             this.btnDisconnect = new UAS_MES_NEW.CustomControl.ButtonUtil.NormalButton();
+            this.rdoRTU = new System.Windows.Forms.RadioButton();
+            this.rdoTCP = new System.Windows.Forms.RadioButton();
             this.grpData = new UAS_MES_NEW.CustomControl.GroupBoxWithBorder.GroupBoxWithBorder();
             this.lblD141 = new System.Windows.Forms.Label();
             this.lblD141Value = new System.Windows.Forms.Label();
@@ -53,10 +56,10 @@
             this.lblStatus = new System.Windows.Forms.Label();
             this.timer1 = new System.Windows.Forms.Timer(this.components);
             this.grpConnect.SuspendLayout();
-            this.grpData.SuspendLayout();
             ((System.ComponentModel.ISupportInitialize)(this.port)).BeginInit();
             ((System.ComponentModel.ISupportInitialize)(this.unitid)).BeginInit();
             ((System.ComponentModel.ISupportInitialize)(this.interval)).BeginInit();
+            this.grpData.SuspendLayout();
             ((System.ComponentModel.ISupportInitialize)(this.decimals)).BeginInit();
             this.SuspendLayout();
             // 
@@ -73,6 +76,8 @@
             this.grpConnect.Controls.Add(this.interval);
             this.grpConnect.Controls.Add(this.btnConnect);
             this.grpConnect.Controls.Add(this.btnDisconnect);
+            this.grpConnect.Controls.Add(this.rdoRTU);
+            this.grpConnect.Controls.Add(this.rdoTCP);
             this.grpConnect.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
             this.grpConnect.Location = new System.Drawing.Point(32, 30);
             this.grpConnect.Margin = new System.Windows.Forms.Padding(6, 4, 6, 4);
@@ -90,7 +95,7 @@
             this.lblIp.Location = new System.Drawing.Point(32, 50);
             this.lblIp.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0);
             this.lblIp.Name = "lblIp";
-            this.lblIp.Size = new System.Drawing.Size(122, 52);
+            this.lblIp.Size = new System.Drawing.Size(138, 52);
             this.lblIp.TabIndex = 0;
             this.lblIp.Text = "IP地址";
             // 
@@ -104,7 +109,7 @@
             this.ip.Margin = new System.Windows.Forms.Padding(6, 4, 6, 4);
             this.ip.Name = "ip";
             this.ip.Power = null;
-            this.ip.Size = new System.Drawing.Size(320, 55);
+            this.ip.Size = new System.Drawing.Size(320, 53);
             this.ip.Str = null;
             this.ip.Str1 = null;
             this.ip.Str2 = null;
@@ -117,7 +122,7 @@
             this.lblPort.Location = new System.Drawing.Point(620, 50);
             this.lblPort.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0);
             this.lblPort.Name = "lblPort";
-            this.lblPort.Size = new System.Drawing.Size(82, 52);
+            this.lblPort.Size = new System.Drawing.Size(102, 52);
             this.lblPort.TabIndex = 2;
             this.lblPort.Text = "端口";
             // 
@@ -132,7 +137,7 @@
             0,
             0});
             this.port.Name = "port";
-            this.port.Size = new System.Drawing.Size(160, 51);
+            this.port.Size = new System.Drawing.Size(160, 53);
             this.port.TabIndex = 3;
             // 
             // lblUnit
@@ -142,7 +147,7 @@
             this.lblUnit.Location = new System.Drawing.Point(970, 50);
             this.lblUnit.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0);
             this.lblUnit.Name = "lblUnit";
-            this.lblUnit.Size = new System.Drawing.Size(82, 52);
+            this.lblUnit.Size = new System.Drawing.Size(102, 52);
             this.lblUnit.TabIndex = 4;
             this.lblUnit.Text = "站号";
             // 
@@ -157,7 +162,7 @@
             0,
             0});
             this.unitid.Name = "unitid";
-            this.unitid.Size = new System.Drawing.Size(140, 51);
+            this.unitid.Size = new System.Drawing.Size(140, 53);
             this.unitid.TabIndex = 5;
             // 
             // lblInterval
@@ -167,7 +172,7 @@
             this.lblInterval.Location = new System.Drawing.Point(1290, 50);
             this.lblInterval.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0);
             this.lblInterval.Name = "lblInterval";
-            this.lblInterval.Size = new System.Drawing.Size(122, 52);
+            this.lblInterval.Size = new System.Drawing.Size(168, 52);
             this.lblInterval.TabIndex = 6;
             this.lblInterval.Text = "间隔(秒)";
             // 
@@ -176,29 +181,38 @@
             this.interval.Font = new System.Drawing.Font("微软雅黑", 13F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
             this.interval.Location = new System.Drawing.Point(1440, 52);
             this.interval.Margin = new System.Windows.Forms.Padding(6, 4, 6, 4);
-            this.interval.Minimum = new decimal(new int[] {
-            1,
+            this.interval.Maximum = new decimal(new int[] {
+            3600,
             0,
             0,
             0});
-            this.interval.Maximum = new decimal(new int[] {
-            3600,
+            this.interval.Minimum = new decimal(new int[] {
+            1,
             0,
             0,
             0});
             this.interval.Name = "interval";
-            this.interval.Size = new System.Drawing.Size(90, 51);
+            this.interval.Size = new System.Drawing.Size(90, 53);
             this.interval.TabIndex = 7;
+            this.interval.Value = new decimal(new int[] {
+            1,
+            0,
+            0,
+            0});
             // 
             // btnConnect
             // 
             this.btnConnect.AllPower = null;
             this.btnConnect.BackColor = System.Drawing.Color.Transparent;
+            this.btnConnect.DownImage = ((System.Drawing.Image)(resources.GetObject("btnConnect.DownImage")));
             this.btnConnect.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.btnConnect.Image = null;
             this.btnConnect.IsShowBorder = true;
             this.btnConnect.Location = new System.Drawing.Point(220, 125);
             this.btnConnect.Margin = new System.Windows.Forms.Padding(6, 4, 6, 4);
+            this.btnConnect.MoveImage = ((System.Drawing.Image)(resources.GetObject("btnConnect.MoveImage")));
             this.btnConnect.Name = "btnConnect";
+            this.btnConnect.NormalImage = ((System.Drawing.Image)(resources.GetObject("btnConnect.NormalImage")));
             this.btnConnect.Power = null;
             this.btnConnect.Size = new System.Drawing.Size(160, 52);
             this.btnConnect.TabIndex = 8;
@@ -210,11 +224,15 @@
             // 
             this.btnDisconnect.AllPower = null;
             this.btnDisconnect.BackColor = System.Drawing.Color.Transparent;
+            this.btnDisconnect.DownImage = ((System.Drawing.Image)(resources.GetObject("btnDisconnect.DownImage")));
             this.btnDisconnect.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.btnDisconnect.Image = null;
             this.btnDisconnect.IsShowBorder = true;
             this.btnDisconnect.Location = new System.Drawing.Point(420, 125);
             this.btnDisconnect.Margin = new System.Windows.Forms.Padding(6, 4, 6, 4);
+            this.btnDisconnect.MoveImage = ((System.Drawing.Image)(resources.GetObject("btnDisconnect.MoveImage")));
             this.btnDisconnect.Name = "btnDisconnect";
+            this.btnDisconnect.NormalImage = ((System.Drawing.Image)(resources.GetObject("btnDisconnect.NormalImage")));
             this.btnDisconnect.Power = null;
             this.btnDisconnect.Size = new System.Drawing.Size(160, 52);
             this.btnDisconnect.TabIndex = 9;
@@ -222,6 +240,32 @@
             this.btnDisconnect.UseVisualStyleBackColor = true;
             this.btnDisconnect.Click += new System.EventHandler(this.Disconnect_Click);
             // 
+            // rdoRTU
+            // 
+            this.rdoRTU.AutoSize = true;
+            this.rdoRTU.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.rdoRTU.Location = new System.Drawing.Point(660, 130);
+            this.rdoRTU.Margin = new System.Windows.Forms.Padding(6, 4, 6, 4);
+            this.rdoRTU.Name = "rdoRTU";
+            this.rdoRTU.Size = new System.Drawing.Size(388, 45);
+            this.rdoRTU.TabIndex = 10;
+            this.rdoRTU.Text = "ModbusRTU(串口透传)";
+            this.rdoRTU.UseVisualStyleBackColor = true;
+            // 
+            // rdoTCP
+            // 
+            this.rdoTCP.AutoSize = true;
+            this.rdoTCP.Checked = true;
+            this.rdoTCP.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.rdoTCP.Location = new System.Drawing.Point(1080, 130);
+            this.rdoTCP.Margin = new System.Windows.Forms.Padding(6, 4, 6, 4);
+            this.rdoTCP.Name = "rdoTCP";
+            this.rdoTCP.Size = new System.Drawing.Size(234, 45);
+            this.rdoTCP.TabIndex = 11;
+            this.rdoTCP.TabStop = true;
+            this.rdoTCP.Text = "ModbusTCP";
+            this.rdoTCP.UseVisualStyleBackColor = true;
+            // 
             // grpData
             // 
             this.grpData.BorderColor = System.Drawing.Color.Black;
@@ -249,7 +293,7 @@
             this.lblD141.Location = new System.Drawing.Point(32, 55);
             this.lblD141.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0);
             this.lblD141.Name = "lblD141";
-            this.lblD141.Size = new System.Drawing.Size(262, 52);
+            this.lblD141.Size = new System.Drawing.Size(328, 52);
             this.lblD141.TabIndex = 0;
             this.lblD141.Text = "1#舱温度 (D141)";
             // 
@@ -271,7 +315,7 @@
             this.lblD142.Location = new System.Drawing.Point(820, 55);
             this.lblD142.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0);
             this.lblD142.Name = "lblD142";
-            this.lblD142.Size = new System.Drawing.Size(262, 52);
+            this.lblD142.Size = new System.Drawing.Size(328, 52);
             this.lblD142.TabIndex = 2;
             this.lblD142.Text = "2#舱温度 (D142)";
             // 
@@ -293,7 +337,7 @@
             this.lblDecimals.Location = new System.Drawing.Point(32, 290);
             this.lblDecimals.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0);
             this.lblDecimals.Name = "lblDecimals";
-            this.lblDecimals.Size = new System.Drawing.Size(162, 52);
+            this.lblDecimals.Size = new System.Drawing.Size(222, 52);
             this.lblDecimals.TabIndex = 4;
             this.lblDecimals.Text = "温度小数位";
             // 
@@ -308,18 +352,22 @@
             0,
             0});
             this.decimals.Name = "decimals";
-            this.decimals.Size = new System.Drawing.Size(120, 51);
+            this.decimals.Size = new System.Drawing.Size(120, 53);
             this.decimals.TabIndex = 5;
             // 
             // btnReadNow
             // 
             this.btnReadNow.AllPower = null;
             this.btnReadNow.BackColor = System.Drawing.Color.Transparent;
+            this.btnReadNow.DownImage = ((System.Drawing.Image)(resources.GetObject("btnReadNow.DownImage")));
             this.btnReadNow.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.btnReadNow.Image = null;
             this.btnReadNow.IsShowBorder = true;
             this.btnReadNow.Location = new System.Drawing.Point(820, 285);
             this.btnReadNow.Margin = new System.Windows.Forms.Padding(6, 4, 6, 4);
+            this.btnReadNow.MoveImage = ((System.Drawing.Image)(resources.GetObject("btnReadNow.MoveImage")));
             this.btnReadNow.Name = "btnReadNow";
+            this.btnReadNow.NormalImage = ((System.Drawing.Image)(resources.GetObject("btnReadNow.NormalImage")));
             this.btnReadNow.Power = null;
             this.btnReadNow.Size = new System.Drawing.Size(200, 52);
             this.btnReadNow.TabIndex = 6;
@@ -334,7 +382,7 @@
             this.lblRecord.Location = new System.Drawing.Point(32, 670);
             this.lblRecord.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0);
             this.lblRecord.Name = "lblRecord";
-            this.lblRecord.Size = new System.Drawing.Size(82, 52);
+            this.lblRecord.Size = new System.Drawing.Size(102, 52);
             this.lblRecord.TabIndex = 2;
             this.lblRecord.Text = "记录";
             // 
@@ -356,7 +404,7 @@
             this.lblStatus.Location = new System.Drawing.Point(32, 1075);
             this.lblStatus.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0);
             this.lblStatus.Name = "lblStatus";
-            this.lblStatus.Size = new System.Drawing.Size(102, 52);
+            this.lblStatus.Size = new System.Drawing.Size(142, 52);
             this.lblStatus.TabIndex = 4;
             this.lblStatus.Text = "状态:";
             // 
@@ -385,14 +433,15 @@
             this.SizeChanged += new System.EventHandler(this.Make_PLCMonitor_SizeChanged);
             this.grpConnect.ResumeLayout(true);
             this.grpConnect.PerformLayout();
-            this.grpData.ResumeLayout(true);
-            this.grpData.PerformLayout();
             ((System.ComponentModel.ISupportInitialize)(this.port)).EndInit();
             ((System.ComponentModel.ISupportInitialize)(this.unitid)).EndInit();
             ((System.ComponentModel.ISupportInitialize)(this.interval)).EndInit();
+            this.grpData.ResumeLayout(true);
+            this.grpData.PerformLayout();
             ((System.ComponentModel.ISupportInitialize)(this.decimals)).EndInit();
             this.ResumeLayout(true);
             this.PerformLayout();
+
         }
 
         #endregion
@@ -408,6 +457,8 @@
         private System.Windows.Forms.NumericUpDown interval;
         private CustomControl.ButtonUtil.NormalButton btnConnect;
         private CustomControl.ButtonUtil.NormalButton btnDisconnect;
+        private System.Windows.Forms.RadioButton rdoRTU;
+        private System.Windows.Forms.RadioButton rdoTCP;
         private CustomControl.GroupBoxWithBorder.GroupBoxWithBorder grpData;
         private System.Windows.Forms.Label lblD141;
         private System.Windows.Forms.Label lblD141Value;

+ 117 - 10
UAS_MES_ZKLH/FunctionCode/Make/Make_PLCMonitor.cs

@@ -24,9 +24,11 @@ namespace UAS_MES_NEW.Make
         ushort transactionId = 0;
         readonly object readLock = new object();
 
-        // 台达PLC数据寄存器:D寄存器对应Modbus保持寄存器(功能码0x03),地址=D编号
-        // 1#舱温度 D141 / 2#舱温度 D142,地址相邻一次读完
+        // 台达PLC数据寄存器:D寄存器对应Modbus保持寄存器(功能码0x03)
+        // ETH-DVP-2P模块手册寄存器表:D0~4095 的Modbus地址 = 0x1000 + D编号
+        // 如 D141 → 0x1000+141 = 0x108D;D4096以上是另一段(0x9000起),当前用不到
         const int D141_ADDR = 141;
+        const int DVP_D_OFFSET = 0x1000;
         const int READ_COUNT = 2;
 
         // 温度记录表:每次采集往里插一条(1#舱、2#舱各一条)
@@ -43,7 +45,7 @@ namespace UAS_MES_NEW.Make
         {
             asc.controllInitializeSize(this);
             dh = SystemInf.dh;
-            ip.Text = "192.168.0.1";
+            ip.Text = "192.168.1.150";
             port.Value = 502;
             unitid.Value = 1;
             interval.Value = 2;
@@ -69,8 +71,8 @@ namespace UAS_MES_NEW.Make
                     return;
                 }
                 plcClient.EndConnect(ar);
-                plcClient.ReceiveTimeout = 3000;
-                plcClient.SendTimeout = 3000;
+                plcClient.ReceiveTimeout = 5000;
+                plcClient.SendTimeout = 5000;
                 plcStream = plcClient.GetStream();
                 connected = true;
                 SetStatus("已连接 " + ip.Text.Trim() + ":" + port.Value);
@@ -111,7 +113,8 @@ namespace UAS_MES_NEW.Make
             }
             try
             {
-                int[] values = ReadRegisters((ushort)D141_ADDR, READ_COUNT);
+                // D寄存器地址要加台达DVP的0x1000偏移:D141→0x108D, D142→0x108E
+                int[] values = ReadRegisters((ushort)(DVP_D_OFFSET + D141_ADDR), READ_COUNT);
                 ShowValues(values);
                 InsertTempLog(values);
                 SetStatus("读取成功 " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
@@ -122,8 +125,13 @@ namespace UAS_MES_NEW.Make
                 timer1.Stop();
                 ClosePlc();
                 connected = false;
+                string mode = rdoTCP.Checked ? "ModbusTCP" : "ModbusRTU(透传)";
                 SetStatus("读取失败:" + ex.Message);
-                OperatResult.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "] 读取失败:" + ex.Message + "\n");
+                OperatResult.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "] 读取失败(" + mode + "):" + ex.Message + "\n");
+                if (ex.Message.Contains("超时") || ex.Message.Contains("没有正确答复") || ex.Message.Contains("连接已断开"))
+                {
+                    OperatResult.AppendText("[提示] 如果RTU(透传)模式一直超时,请尝试切换到ModbusTCP模式;反之亦然。同时确认模块透传设置、PLC串口参数(9600/8/N/1)和站号。\n");
+                }
                 // 自动重连
                 TryReconnect();
             }
@@ -159,8 +167,8 @@ namespace UAS_MES_NEW.Make
                     return;
                 }
                 plcClient.EndConnect(ar);
-                plcClient.ReceiveTimeout = 3000;
-                plcClient.SendTimeout = 3000;
+                plcClient.ReceiveTimeout = 5000;
+                plcClient.SendTimeout = 5000;
                 plcStream = plcClient.GetStream();
                 connected = true;
                 SetStatus("重连成功 " + DateTime.Now.ToString("HH:mm:ss"));
@@ -215,7 +223,82 @@ namespace UAS_MES_NEW.Make
         /// <summary>
         /// Modbus TCP 读保持寄存器(功能码0x03)
         /// </summary>
+        /// <summary>
+        /// 读保持寄存器(功能码0x03),按界面选择的协议分发:
+        /// ModbusRTU(串口透传):模块把TCP数据原样转发到串口,要发带CRC16的RTU裸帧
+        /// ModbusTCP:模块/PLC本身是标准Modbus TCP服务端,发带MBAP头的帧
+        /// </summary>
         private int[] ReadRegisters(ushort startAddr, ushort count)
+        {
+            if (rdoTCP.Checked)
+            {
+                return ReadTcpRegisters(startAddr, count);
+            }
+            return ReadRtuRegisters(startAddr, count);
+        }
+
+        /// <summary>
+        /// Modbus RTU over TCP(串口透传模式):站号+功能码+数据+CRC16
+        /// </summary>
+        private int[] ReadRtuRegisters(ushort startAddr, ushort count)
+        {
+            lock (readLock)
+            {
+                if (!connected || plcStream == null)
+                {
+                    throw new Exception("未连接PLC");
+                }
+                byte unit = (byte)unitid.Value;
+                byte[] req = new byte[8];
+                req[0] = unit;
+                // 功能码0x03 读保持寄存器
+                req[1] = 0x03;
+                req[2] = (byte)(startAddr >> 8);
+                req[3] = (byte)(startAddr & 0xFF);
+                req[4] = (byte)(count >> 8);
+                req[5] = (byte)(count & 0xFF);
+                ushort crc = CalcCRC16(req, 6);
+                // Modbus RTU CRC低字节在前
+                req[6] = (byte)(crc & 0xFF);
+                req[7] = (byte)((crc >> 8) & 0xFF);
+                plcStream.Write(req, 0, req.Length);
+
+                // 响应:站号+功能码+字节数
+                byte[] head = ReadBytes(plcStream, 3);
+                if (head[0] != unit)
+                {
+                    throw new Exception("响应站号不匹配(收到0x" + head[0].ToString("X2") + "),请检查站号设置");
+                }
+                if ((head[1] & 0x80) != 0)
+                {
+                    byte[] rest = ReadBytes(plcStream, 3);
+                    throw new Exception("PLC返回异常码 0x" + rest[0].ToString("X2"));
+                }
+                int byteCount = head[2];
+                byte[] rest2 = ReadBytes(plcStream, byteCount + 2);
+                // CRC校验:响应最后2字节是CRC
+                byte[] full = new byte[3 + byteCount + 2];
+                Array.Copy(head, 0, full, 0, 3);
+                Array.Copy(rest2, 0, full, 3, byteCount + 2);
+                ushort calcCrc = CalcCRC16(full, full.Length - 2);
+                ushort recvCrc = (ushort)(full[full.Length - 2] | (full[full.Length - 1] << 8));
+                if (calcCrc != recvCrc)
+                {
+                    throw new Exception("响应CRC校验失败,串口参数(波特率/校验位)可能与PLC不一致");
+                }
+                int[] values = new int[count];
+                for (int i = 0; i < count; i++)
+                {
+                    values[i] = (rest2[i * 2] << 8) | rest2[i * 2 + 1];
+                }
+                return values;
+            }
+        }
+
+        /// <summary>
+        /// Modbus TCP 读保持寄存器(功能码0x03),带MBAP头
+        /// </summary>
+        private int[] ReadTcpRegisters(ushort startAddr, ushort count)
         {
             lock (readLock)
             {
@@ -242,7 +325,7 @@ namespace UAS_MES_NEW.Make
                 plcStream.Write(req, 0, req.Length);
 
                 byte[] head = ReadBytes(plcStream, 9);
-                if (head[7] == 0x83 || (head[7] & 0x80) != 0)
+                if ((head[7] & 0x80) != 0)
                 {
                     byte[] ec = ReadBytes(plcStream, 1);
                     throw new Exception("PLC返回异常码 0x" + (ec.Length > 0 ? ec[0] : 0).ToString("X2"));
@@ -258,6 +341,30 @@ namespace UAS_MES_NEW.Make
             }
         }
 
+        /// <summary>
+        /// Modbus RTU CRC16校验
+        /// </summary>
+        private ushort CalcCRC16(byte[] data, int length)
+        {
+            ushort crc = 0xFFFF;
+            for (int i = 0; i < length; i++)
+            {
+                crc ^= data[i];
+                for (int j = 0; j < 8; j++)
+                {
+                    if ((crc & 0x0001) != 0)
+                    {
+                        crc = (ushort)((crc >> 1) ^ 0xA001);
+                    }
+                    else
+                    {
+                        crc = (ushort)(crc >> 1);
+                    }
+                }
+            }
+            return crc;
+        }
+
         /// <summary>
         /// 从流中读满指定长度,读不满视为连接异常
         /// </summary>

+ 105 - 26
UAS_MES_ZKLH/FunctionCode/Make/Make_PLCMonitor.resx

@@ -118,42 +118,121 @@
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
   <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
-  <data name="DeleteButton.DownImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+  <data name="btnConnect.DownImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
     <value>
         iVBORw0KGgoAAAANSUhEUgAAAEUAAAAWCAYAAACWl1FwAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
-        dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAFcSURBVFhH5di7UsJAFMbxtDT6QMI76IAFT0npIE0K
-        GcRORxHklpAIBHLfbILV8XxeNsMznJ35F5vu+82kWYuILD4N7oq75roCw27sb/x5WBdc27btntY64o/i
-        DnZjPxy4S6A0R6PHO1WUNHc9ep4uxIXd2D8eP/XZowWUThBG+n3p0stsKbbJ0qFDFFfscQuUbpzm9Pqx
-        Eh8c4PGLkuX0Nl+LDw4GJckUTRaO+OBQo+SKpitXfHAwKClfZuuN+OBgUDJV0NzxxAeHM5TFxhffGUpe
-        aFp5n+KDg0FRfHH8nfjgUKPoktztXnxwMCgFX7xdID441ChlRf7+ID44GBTNl21wFB8capSqot0xFB8c
-        /lE6Wa70IU5oH0Ziw/5cFebpoPUwHPbL04nCJKUgisWF3dgPB3gABc9v7fvBoBcnaVKdvkha2I39cPjx
-        oPrhusndcPinpIXd2N8gIusbTaFspKtR2SQAAAAASUVORK5CYII=
+        dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAFfSURBVFhH5dhNTsJgFIXhM2WiCxL2oAEHrJKhQSYM
+        JIgzjSLIX0sRKPSPtl+Lo2uOgVbWcJu8CzhP8nVwISIAUAFwA+AWQFNh3M39lZMHrgDUu91uyxjji8KP
+        u7mfDgCuiVLt958fkjSTie3I62iqLu7m/sHgpQ2gRpSG6/nmc2bL23imtuHMkp0f5ADuidIMoljev+bq
+        o8PpH4NmcIjlY7JQHx0KlPCQyHBqqY8OJUqcyGhuq48OBUoUJzJeLNVHhwLlkKQysRz10eECZbpcqe8C
+        JU6NzJ1v9dGhQElSI9Zqoz46lCgmE3u9VR8dCpTUZOJsXPXRoUTJclltd+qjQ4FislzW7l59dChR8lw2
+        e099dDijNA5xYnZBKFvPVxv3x0lanA5qT71eOzsexQsjcf1AXdzN/XQ4H5l4fqs/djqtIIzC/Pgj2uJu
+        7i/Okf8O11UAd6c3pS3u5v6/w/UvTaFspAxAMh4AAAAASUVORK5CYII=
 </value>
   </data>
-  <data name="DeleteButton.MoveImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+  <data name="btnConnect.MoveImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
     <value>
         iVBORw0KGgoAAAANSUhEUgAAAEUAAAAWCAYAAACWl1FwAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
-        dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAFzSURBVFhH5djNSgJRGMZxL2P2gTfQFbZvHSEZFBEW
-        CEIQBEIgVFiS9qXWoDmp4/fHzDmjM2a+vU/QqUVX0DvwXxxn9fwYXJwYEcXWNzIWt83dcq7AsBv7LXgA
-        ZI0r71/aaqjCJf8o7sFu7IcDFwdK4vimrv1oSZXRnArdQFzYjf3pQkOzRxIoVWc6X5X6M7ruBGIr9mbU
-        9MIVe9hAcXvBO125WnxwgMcXSpcPF20tPjgYlI5eUK6lxAcHg9Lmw3nTFx8cDEpLLSjr+OKDg0FpqojO
-        Gp744GBQ3vyITutT8cHBoDheRCe1ifjgYFBe+ZCxJ+KDg0GpT0NKv4zFBweDUpuEdFQdiQ8OBsXmw2Fl
-        JD44/KCM53TwNBQfHAzKMx/2Hgfig8M3SrXYUasUfz67DwOxYf9dV5urg+RWzta4aEnxn83OfV9c2I39
-        cIAHUOJceTNbUfmW94GX0sJu7IcDPH5fXCe4EvfXxe5/D7ux3yKi2CfkPhTy27lqkwAAAABJRU5ErkJg
-        gg==
+        dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAF3SURBVFhH5djbSgJRFMbxHsP7wBfYT7jvu46QDIoI
+        C4QNQRAIgVBRSdpxpgZN83wYx5nZOjNqrvgEd73DGvhfzdX3g32ztohoS0iVElLtCakehVQdhmE39qfg
+        AZBtIdX70Y0TjsJ4SQw/7MZ+OAip0kDJnD3UdJAsyXIjKvWm7MJu7M+X6lpIlQWK3ZhEq8pgRvfdKdvK
+        /Rk1/XglpHKA0ulPF3Tb0eyDAzzWKL3pgq7bmn1wMChdPadiK2QfHAxKW8/pqhmwDw4GpRXOqdAI2AcH
+        g9IME7qs++yDg0H5DhK6qE3YBweD0vATOq967IODQfnyE1KOxz44GJTaJKb855h9cDAoVS+mU9tlHxwM
+        iuPFdGK57IPDH8o4ouO3EfvgYFA+xhEdvg7ZB4cNil3uhquc5dLBy5Bt2P/U0+Z0kN0tOhqHlpzt0v7z
+        gF3Yjf1w2ByZ0jjD7RSs8K7l/+Ant7Ab+8058t/hOiOkquBNMQy7sX99uP4F5D4U8hdr4uMAAAAASUVO
+        RK5CYII=
 </value>
   </data>
-  <data name="DeleteButton.NormalImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+  <data name="btnConnect.NormalImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
     <value>
         iVBORw0KGgoAAAANSUhEUgAAAEUAAAAWCAYAAACWl1FwAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
-        dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGVSURBVFhH5di7agJRFIXhDSKoYBQEC8FCECwEQRBE
-        4uUVEtTiPKVlom2wttM6IfF+15njXHRnL19jD/zFmanWV8zAEDNTp9OJS6/Sm2QUht3YH4cHtdvtF6k7
-        mUwG1tqd3FR3YTf2w0FKUVOu6XT68Xgw3+8PtWH/bDYbCkeLGo1Gz/d9JwzvrL0gCKx49KlerxtIBUGo
-        PjjAg2q1mqDc2fN99cEBHlStVk0oB+t56oMDPKhSqZggDNm1N/XBAR5ULpeNvGD46rrqgwM8qFQqGV8O
-        56ujPjjAg4rFosFL5ni5qA8O8KBCoWBuns/701l9cIAH5fN5QfF4dzipDw7woFwuZ+zN483+oD44wIOy
-        2azB52i13asPDvCgTCZjHGt5sdmqDw7woHQ6bRzX8ny1UR8c4EHJZNJc5fC7XKsPDvCgRCLR2x9Pzt9q
-        zT+Lpdqw/3A6W/HoUywWa32Nx8OL4zwffM8X6sJu7IcDPCgajaak7udoNNjsdgc81BZ2Yz8c4PH8cR2J
-        ROJSU3qXjMKwG/vjzEz/skOI3Zqgv7AAAAAASUVORK5CYII=
+        dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGpSURBVFhH5dg/ayJBGMfxB4YFXTARBAvBQhAsBEEQ
+        ZLnNzFu4I6aYV5kyF9tgbad1jrsYY9T4Z3fcnV2f45e38Sx8q6l+n2IWhpiZjDFVY8wPY8xPY4wVGHZj
+        fxUepLW+0Vrfz+fzR+fclgV+2I39cNBa31Icx/FisXi6XpnL8io27F8ul89xHN9RFEWTPM+ToihZet57
+        F0XRA43HYwsp7wvxwQEeNBqNbFmWnOW5+OAADxoOh7YoS3ZZJj44wIMGg4H1RcGpu4gPDvCgfr9vvfd8
+        TlPxwQEe1Ov1bO49H8+J+OAAD+p2uxaXzNfpJD44wIM6nY69ZDnvDkfxwQEe1G637SXLeLs/iA8O8KBW
+        q2XdJePNbi8+OMCDms2mxe9o/bkTHxzgQY1GwybO8WrzKT44wIPq9bpNUsdv64344AAPqtVq9pw6/vv+
+        IT44wIPCMJzsvg7Jv/UH/1m9iw3794ejC8PwgSqVyt3LbPZ8SpLvg9e3lbiwG/vhAA8KguA2CIL739Pp
+        42a73eNQWtiN/XCAB+GhVilVVUrFSqlfSikrMOzG/u+H6/+yQ4jdXN8e9AAAAABJRU5ErkJggg==
 </value>
   </data>
+  <data name="btnDisconnect.DownImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAAEUAAAAWCAYAAACWl1FwAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
+        dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAFfSURBVFhH5dhNTsJgFIXhM2WiCxL2oAEHrJKhQSYM
+        JIgzjSLIX0sRKPSPtl+Lo2uOgVbWcJu8CzhP8nVwISIAUAFwA+AWQFNh3M39lZMHrgDUu91uyxjji8KP
+        u7mfDgCuiVLt958fkjSTie3I62iqLu7m/sHgpQ2gRpSG6/nmc2bL23imtuHMkp0f5ADuidIMoljev+bq
+        o8PpH4NmcIjlY7JQHx0KlPCQyHBqqY8OJUqcyGhuq48OBUoUJzJeLNVHhwLlkKQysRz10eECZbpcqe8C
+        JU6NzJ1v9dGhQElSI9Zqoz46lCgmE3u9VR8dCpTUZOJsXPXRoUTJclltd+qjQ4FislzW7l59dChR8lw2
+        e099dDijNA5xYnZBKFvPVxv3x0lanA5qT71eOzsexQsjcf1AXdzN/XQ4H5l4fqs/djqtIIzC/Pgj2uJu
+        7i/Okf8O11UAd6c3pS3u5v6/w/UvTaFspAxAMh4AAAAASUVORK5CYII=
+</value>
+  </data>
+  <data name="btnDisconnect.MoveImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAAEUAAAAWCAYAAACWl1FwAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
+        dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAF3SURBVFhH5djbSgJRFMbxHsP7wBfYT7jvu46QDIoI
+        C4QNQRAIgVBRSdpxpgZN83wYx5nZOjNqrvgEd73DGvhfzdX3g32ztohoS0iVElLtCakehVQdhmE39qfg
+        AZBtIdX70Y0TjsJ4SQw/7MZ+OAip0kDJnD3UdJAsyXIjKvWm7MJu7M+X6lpIlQWK3ZhEq8pgRvfdKdvK
+        /Rk1/XglpHKA0ulPF3Tb0eyDAzzWKL3pgq7bmn1wMChdPadiK2QfHAxKW8/pqhmwDw4GpRXOqdAI2AcH
+        g9IME7qs++yDg0H5DhK6qE3YBweD0vATOq967IODQfnyE1KOxz44GJTaJKb855h9cDAoVS+mU9tlHxwM
+        iuPFdGK57IPDH8o4ouO3EfvgYFA+xhEdvg7ZB4cNil3uhquc5dLBy5Bt2P/U0+Z0kN0tOhqHlpzt0v7z
+        gF3Yjf1w2ByZ0jjD7RSs8K7l/+Ant7Ab+8058t/hOiOkquBNMQy7sX99uP4F5D4U8hdr4uMAAAAASUVO
+        RK5CYII=
+</value>
+  </data>
+  <data name="btnDisconnect.NormalImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAAEUAAAAWCAYAAACWl1FwAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
+        dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGpSURBVFhH5dg/ayJBGMfxB4YFXTARBAvBQhAsBEEQ
+        ZLnNzFu4I6aYV5kyF9tgbad1jrsYY9T4Z3fcnV2f45e38Sx8q6l+n2IWhpiZjDFVY8wPY8xPY4wVGHZj
+        fxUepLW+0Vrfz+fzR+fclgV+2I39cNBa31Icx/FisXi6XpnL8io27F8ul89xHN9RFEWTPM+ToihZet57
+        F0XRA43HYwsp7wvxwQEeNBqNbFmWnOW5+OAADxoOh7YoS3ZZJj44wIMGg4H1RcGpu4gPDvCgfr9vvfd8
+        TlPxwQEe1Ov1bO49H8+J+OAAD+p2uxaXzNfpJD44wIM6nY69ZDnvDkfxwQEe1G637SXLeLs/iA8O8KBW
+        q2XdJePNbi8+OMCDms2mxe9o/bkTHxzgQY1GwybO8WrzKT44wIPq9bpNUsdv64344AAPqtVq9pw6/vv+
+        IT44wIPCMJzsvg7Jv/UH/1m9iw3794ejC8PwgSqVyt3LbPZ8SpLvg9e3lbiwG/vhAA8KguA2CIL739Pp
+        42a73eNQWtiN/XCAB+GhVilVVUrFSqlfSikrMOzG/u+H6/+yQ4jdXN8e9AAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <data name="btnReadNow.DownImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAAEUAAAAWCAYAAACWl1FwAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
+        dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAFfSURBVFhH5dhNTsJgFIXhM2WiCxL2oAEHrJKhQSYM
+        JIgzjSLIX0sRKPSPtl+Lo2uOgVbWcJu8CzhP8nVwISIAUAFwA+AWQFNh3M39lZMHrgDUu91uyxjji8KP
+        u7mfDgCuiVLt958fkjSTie3I62iqLu7m/sHgpQ2gRpSG6/nmc2bL23imtuHMkp0f5ADuidIMoljev+bq
+        o8PpH4NmcIjlY7JQHx0KlPCQyHBqqY8OJUqcyGhuq48OBUoUJzJeLNVHhwLlkKQysRz10eECZbpcqe8C
+        JU6NzJ1v9dGhQElSI9Zqoz46lCgmE3u9VR8dCpTUZOJsXPXRoUTJclltd+qjQ4FislzW7l59dChR8lw2
+        e099dDijNA5xYnZBKFvPVxv3x0lanA5qT71eOzsexQsjcf1AXdzN/XQ4H5l4fqs/djqtIIzC/Pgj2uJu
+        7i/Okf8O11UAd6c3pS3u5v6/w/UvTaFspAxAMh4AAAAASUVORK5CYII=
+</value>
+  </data>
+  <data name="btnReadNow.MoveImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAAEUAAAAWCAYAAACWl1FwAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
+        dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAF3SURBVFhH5djbSgJRFMbxHsP7wBfYT7jvu46QDIoI
+        C4QNQRAIgVBRSdpxpgZN83wYx5nZOjNqrvgEd73DGvhfzdX3g32ztohoS0iVElLtCakehVQdhmE39qfg
+        AZBtIdX70Y0TjsJ4SQw/7MZ+OAip0kDJnD3UdJAsyXIjKvWm7MJu7M+X6lpIlQWK3ZhEq8pgRvfdKdvK
+        /Rk1/XglpHKA0ulPF3Tb0eyDAzzWKL3pgq7bmn1wMChdPadiK2QfHAxKW8/pqhmwDw4GpRXOqdAI2AcH
+        g9IME7qs++yDg0H5DhK6qE3YBweD0vATOq967IODQfnyE1KOxz44GJTaJKb855h9cDAoVS+mU9tlHxwM
+        iuPFdGK57IPDH8o4ouO3EfvgYFA+xhEdvg7ZB4cNil3uhquc5dLBy5Bt2P/U0+Z0kN0tOhqHlpzt0v7z
+        gF3Yjf1w2ByZ0jjD7RSs8K7l/+Ant7Ab+8058t/hOiOkquBNMQy7sX99uP4F5D4U8hdr4uMAAAAASUVO
+        RK5CYII=
+</value>
+  </data>
+  <data name="btnReadNow.NormalImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAAEUAAAAWCAYAAACWl1FwAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
+        dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGpSURBVFhH5dg/ayJBGMfxB4YFXTARBAvBQhAsBEEQ
+        ZLnNzFu4I6aYV5kyF9tgbad1jrsYY9T4Z3fcnV2f45e38Sx8q6l+n2IWhpiZjDFVY8wPY8xPY4wVGHZj
+        fxUepLW+0Vrfz+fzR+fclgV+2I39cNBa31Icx/FisXi6XpnL8io27F8ul89xHN9RFEWTPM+ToihZet57
+        F0XRA43HYwsp7wvxwQEeNBqNbFmWnOW5+OAADxoOh7YoS3ZZJj44wIMGg4H1RcGpu4gPDvCgfr9vvfd8
+        TlPxwQEe1Ov1bO49H8+J+OAAD+p2uxaXzNfpJD44wIM6nY69ZDnvDkfxwQEe1G637SXLeLs/iA8O8KBW
+        q2XdJePNbi8+OMCDms2mxe9o/bkTHxzgQY1GwybO8WrzKT44wIPq9bpNUsdv64344AAPqtVq9pw6/vv+
+        IT44wIPCMJzsvg7Jv/UH/1m9iw3794ejC8PwgSqVyt3LbPZ8SpLvg9e3lbiwG/vhAA8KguA2CIL739Pp
+        42a73eNQWtiN/XCAB+GhVilVVUrFSqlfSikrMOzG/u+H6/+yQ4jdXN8e9AAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <metadata name="timer1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
 </root>