From f5a0df8e585146b9eed0dc4e9d1805975375aea8 Mon Sep 17 00:00:00 2001 From: BennyZhao Date: Sat, 2 Nov 2019 17:22:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9EModBusRtu=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E7=9A=84=E4=BB=BF=E7=9C=9F=E6=A8=A1=E6=8B=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ModBus/ModBusTcpClient_tests.cs | 1 - .../Controls/ModBusRtuControl.Designer.cs | 116 +++++--- IoTClient.Tool/Controls/ModBusRtuControl.cs | 255 +++++++++++------- .../Controls/ModBusTcpControl.Designer.cs | 29 +- IoTClient.Tool/Controls/ModBusTcpControl.cs | 14 +- IoTClient.Tool/Controls/SiemensControl.cs | 4 +- IoTClient.Tool/IndexForm.Designer.cs | 4 +- IoTClient/Clients/ModBus/ModBusRtuClient.cs | 70 ++++- IoTClient/Clients/ModBus/ModBusTcpClient.cs | 97 +++++-- IoTClient/Clients/PLC/SiemensClient.cs | 1 + IoTClient/IoTClient/IoTClient.xml | 46 +++- IoTClient/Models/Result.cs | 61 +++++ IoTClient/Models/SiemensData.cs | 6 +- IoTClient/SocketBase.cs | 3 +- IoTServer/IoTServer/IoTServer.xml | 27 ++ IoTServer/Servers/ModBus/ModBusRtuServer.cs | 117 ++++++++ IoTServer/Servers/ModBus/ModBusTcpServer.cs | 6 +- IoTServer/Servers/PLC/SiemensServer.cs | 5 +- README.md | 12 +- 19 files changed, 666 insertions(+), 208 deletions(-) create mode 100644 IoTClient/Models/Result.cs create mode 100644 IoTServer/Servers/ModBus/ModBusRtuServer.cs diff --git a/IoTClient.Tests/ModBus/ModBusTcpClient_tests.cs b/IoTClient.Tests/ModBus/ModBusTcpClient_tests.cs index 99cea14..962be3a 100644 --- a/IoTClient.Tests/ModBus/ModBusTcpClient_tests.cs +++ b/IoTClient.Tests/ModBus/ModBusTcpClient_tests.cs @@ -1,5 +1,4 @@ using IoTClient.Clients.ModBus; -using IoTServer.Common; using System.Net; using System.Threading.Tasks; using Xunit; diff --git a/IoTClient.Tool/Controls/ModBusRtuControl.Designer.cs b/IoTClient.Tool/Controls/ModBusRtuControl.Designer.cs index 754a595..5f1b3ec 100644 --- a/IoTClient.Tool/Controls/ModBusRtuControl.Designer.cs +++ b/IoTClient.Tool/Controls/ModBusRtuControl.Designer.cs @@ -40,6 +40,7 @@ this.label4 = new System.Windows.Forms.Label(); this.button6 = new System.Windows.Forms.Button(); this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.rd_discrete = new System.Windows.Forms.RadioButton(); this.rd_float = new System.Windows.Forms.RadioButton(); this.rd_double = new System.Windows.Forms.RadioButton(); this.rd_short = new System.Windows.Forms.RadioButton(); @@ -58,13 +59,15 @@ this.txt_dataBit = new System.Windows.Forms.TextBox(); this.cb_portNameSend = new System.Windows.Forms.ComboBox(); this.but_close = new System.Windows.Forms.Button(); - this.button2 = new System.Windows.Forms.Button(); - this.button1 = new System.Windows.Forms.Button(); + this.but_server_close = new System.Windows.Forms.Button(); + this.but_server_open = new System.Windows.Forms.Button(); this.but_open = new System.Windows.Forms.Button(); this.label5 = new System.Windows.Forms.Label(); this.txt_stationNumber = new System.Windows.Forms.TextBox(); this.label1 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); + this.cb_portNameSend_server = new System.Windows.Forms.ComboBox(); + this.label8 = new System.Windows.Forms.Label(); this.groupBox3.SuspendLayout(); this.groupBox1.SuspendLayout(); this.groupBox2.SuspendLayout(); @@ -104,7 +107,7 @@ this.but_write.TabIndex = 10; this.but_write.Text = "写入"; this.but_write.UseVisualStyleBackColor = true; - this.but_write.Click += new System.EventHandler(this.butWrite_Click); + this.but_write.Click += new System.EventHandler(this.but_write_Click); // // txt_value // @@ -158,6 +161,7 @@ // // groupBox1 // + this.groupBox1.Controls.Add(this.rd_discrete); this.groupBox1.Controls.Add(this.button6); this.groupBox1.Controls.Add(this.rd_float); this.groupBox1.Controls.Add(this.rd_double); @@ -174,10 +178,20 @@ this.groupBox1.TabIndex = 32; this.groupBox1.TabStop = false; // + // rd_discrete + // + this.rd_discrete.AutoSize = true; + this.rd_discrete.Location = new System.Drawing.Point(64, 23); + this.rd_discrete.Name = "rd_discrete"; + this.rd_discrete.Size = new System.Drawing.Size(47, 16); + this.rd_discrete.TabIndex = 24; + this.rd_discrete.Text = "离散"; + this.rd_discrete.UseVisualStyleBackColor = true; + // // rd_float // this.rd_float.AutoSize = true; - this.rd_float.Location = new System.Drawing.Point(436, 23); + this.rd_float.Location = new System.Drawing.Point(486, 23); this.rd_float.Name = "rd_float"; this.rd_float.Size = new System.Drawing.Size(53, 16); this.rd_float.TabIndex = 22; @@ -187,7 +201,7 @@ // rd_double // this.rd_double.AutoSize = true; - this.rd_double.Location = new System.Drawing.Point(495, 23); + this.rd_double.Location = new System.Drawing.Point(545, 23); this.rd_double.Name = "rd_double"; this.rd_double.Size = new System.Drawing.Size(59, 16); this.rd_double.TabIndex = 23; @@ -198,7 +212,7 @@ // this.rd_short.AutoSize = true; this.rd_short.Checked = true; - this.rd_short.Location = new System.Drawing.Point(70, 23); + this.rd_short.Location = new System.Drawing.Point(120, 23); this.rd_short.Name = "rd_short"; this.rd_short.Size = new System.Drawing.Size(53, 16); this.rd_short.TabIndex = 6; @@ -219,7 +233,7 @@ // rd_ulong // this.rd_ulong.AutoSize = true; - this.rd_ulong.Location = new System.Drawing.Point(374, 23); + this.rd_ulong.Location = new System.Drawing.Point(424, 23); this.rd_ulong.Name = "rd_ulong"; this.rd_ulong.Size = new System.Drawing.Size(53, 16); this.rd_ulong.TabIndex = 21; @@ -229,7 +243,7 @@ // rd_ushort // this.rd_ushort.AutoSize = true; - this.rd_ushort.Location = new System.Drawing.Point(129, 23); + this.rd_ushort.Location = new System.Drawing.Point(179, 23); this.rd_ushort.Name = "rd_ushort"; this.rd_ushort.Size = new System.Drawing.Size(59, 16); this.rd_ushort.TabIndex = 17; @@ -239,7 +253,7 @@ // rd_long // this.rd_long.AutoSize = true; - this.rd_long.Location = new System.Drawing.Point(315, 23); + this.rd_long.Location = new System.Drawing.Point(365, 23); this.rd_long.Name = "rd_long"; this.rd_long.Size = new System.Drawing.Size(47, 16); this.rd_long.TabIndex = 20; @@ -249,7 +263,7 @@ // rd_int // this.rd_int.AutoSize = true; - this.rd_int.Location = new System.Drawing.Point(194, 23); + this.rd_int.Location = new System.Drawing.Point(244, 23); this.rd_int.Name = "rd_int"; this.rd_int.Size = new System.Drawing.Size(41, 16); this.rd_int.TabIndex = 18; @@ -259,7 +273,7 @@ // rd_uint // this.rd_uint.AutoSize = true; - this.rd_uint.Location = new System.Drawing.Point(253, 23); + this.rd_uint.Location = new System.Drawing.Point(303, 23); this.rd_uint.Name = "rd_uint"; this.rd_uint.Size = new System.Drawing.Size(47, 16); this.rd_uint.TabIndex = 19; @@ -268,7 +282,7 @@ // // txt_baudRate // - this.txt_baudRate.Location = new System.Drawing.Point(131, 19); + this.txt_baudRate.Location = new System.Drawing.Point(127, 19); this.txt_baudRate.Name = "txt_baudRate"; this.txt_baudRate.Size = new System.Drawing.Size(42, 21); this.txt_baudRate.TabIndex = 5; @@ -286,14 +300,16 @@ // // groupBox2 // + this.groupBox2.Controls.Add(this.cb_portNameSend_server); + this.groupBox2.Controls.Add(this.label8); this.groupBox2.Controls.Add(this.label7); this.groupBox2.Controls.Add(this.txt_stopBit); this.groupBox2.Controls.Add(this.label6); this.groupBox2.Controls.Add(this.txt_dataBit); this.groupBox2.Controls.Add(this.cb_portNameSend); this.groupBox2.Controls.Add(this.but_close); - this.groupBox2.Controls.Add(this.button2); - this.groupBox2.Controls.Add(this.button1); + this.groupBox2.Controls.Add(this.but_server_close); + this.groupBox2.Controls.Add(this.but_server_open); this.groupBox2.Controls.Add(this.but_open); this.groupBox2.Controls.Add(this.label5); this.groupBox2.Controls.Add(this.txt_stationNumber); @@ -309,7 +325,7 @@ // label7 // this.label7.AutoSize = true; - this.label7.Location = new System.Drawing.Point(253, 23); + this.label7.Location = new System.Drawing.Point(250, 23); this.label7.Name = "label7"; this.label7.Size = new System.Drawing.Size(41, 12); this.label7.TabIndex = 21; @@ -317,7 +333,7 @@ // // txt_stopBit // - this.txt_stopBit.Location = new System.Drawing.Point(296, 19); + this.txt_stopBit.Location = new System.Drawing.Point(292, 19); this.txt_stopBit.Name = "txt_stopBit"; this.txt_stopBit.Size = new System.Drawing.Size(24, 21); this.txt_stopBit.TabIndex = 22; @@ -326,7 +342,7 @@ // label6 // this.label6.AutoSize = true; - this.label6.Location = new System.Drawing.Point(178, 24); + this.label6.Location = new System.Drawing.Point(176, 24); this.label6.Name = "label6"; this.label6.Size = new System.Drawing.Size(41, 12); this.label6.TabIndex = 19; @@ -334,7 +350,7 @@ // // txt_dataBit // - this.txt_dataBit.Location = new System.Drawing.Point(219, 20); + this.txt_dataBit.Location = new System.Drawing.Point(217, 20); this.txt_dataBit.Name = "txt_dataBit"; this.txt_dataBit.Size = new System.Drawing.Size(24, 21); this.txt_dataBit.TabIndex = 20; @@ -343,14 +359,14 @@ // cb_portNameSend // this.cb_portNameSend.FormattingEnabled = true; - this.cb_portNameSend.Location = new System.Drawing.Point(35, 20); + this.cb_portNameSend.Location = new System.Drawing.Point(34, 20); this.cb_portNameSend.Name = "cb_portNameSend"; this.cb_portNameSend.Size = new System.Drawing.Size(47, 20); this.cb_portNameSend.TabIndex = 18; // // but_close // - this.but_close.Location = new System.Drawing.Point(419, 19); + this.but_close.Location = new System.Drawing.Point(466, 19); this.but_close.Name = "but_close"; this.but_close.Size = new System.Drawing.Size(75, 23); this.but_close.TabIndex = 17; @@ -360,25 +376,27 @@ // // button2 // - this.button2.Location = new System.Drawing.Point(768, 19); - this.button2.Name = "button2"; - this.button2.Size = new System.Drawing.Size(75, 23); - this.button2.TabIndex = 16; - this.button2.Text = "关闭服务"; - this.button2.UseVisualStyleBackColor = true; + this.but_server_close.Location = new System.Drawing.Point(768, 19); + this.but_server_close.Name = "button2"; + this.but_server_close.Size = new System.Drawing.Size(75, 23); + this.but_server_close.TabIndex = 16; + this.but_server_close.Text = "关闭服务"; + this.but_server_close.UseVisualStyleBackColor = true; + this.but_server_close.Click += new System.EventHandler(this.but_close_server_Click); // // button1 // - this.button1.Location = new System.Drawing.Point(663, 19); - this.button1.Name = "button1"; - this.button1.Size = new System.Drawing.Size(99, 23); - this.button1.TabIndex = 0; - this.button1.Text = "本地模拟服务"; - this.button1.UseVisualStyleBackColor = true; + this.but_server_open.Location = new System.Drawing.Point(665, 19); + this.but_server_open.Name = "button1"; + this.but_server_open.Size = new System.Drawing.Size(99, 23); + this.but_server_open.TabIndex = 0; + this.but_server_open.Text = "本地模拟服务"; + this.but_server_open.UseVisualStyleBackColor = true; + this.but_server_open.Click += new System.EventHandler(this.but_open_server_Click); // // but_open // - this.but_open.Location = new System.Drawing.Point(338, 18); + this.but_open.Location = new System.Drawing.Point(385, 18); this.but_open.Name = "but_open"; this.but_open.Size = new System.Drawing.Size(75, 23); this.but_open.TabIndex = 1; @@ -389,7 +407,7 @@ // label5 // this.label5.AutoSize = true; - this.label5.Location = new System.Drawing.Point(528, 23); + this.label5.Location = new System.Drawing.Point(324, 23); this.label5.Name = "label5"; this.label5.Size = new System.Drawing.Size(29, 12); this.label5.TabIndex = 15; @@ -397,9 +415,9 @@ // // txt_stationNumber // - this.txt_stationNumber.Location = new System.Drawing.Point(563, 19); + this.txt_stationNumber.Location = new System.Drawing.Point(353, 19); this.txt_stationNumber.Name = "txt_stationNumber"; - this.txt_stationNumber.Size = new System.Drawing.Size(35, 21); + this.txt_stationNumber.Size = new System.Drawing.Size(25, 21); this.txt_stationNumber.TabIndex = 14; this.txt_stationNumber.Text = "1"; // @@ -415,12 +433,29 @@ // label2 // this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(90, 24); + this.label2.Location = new System.Drawing.Point(86, 24); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(41, 12); this.label2.TabIndex = 4; this.label2.Text = "波特率"; // + // cb_portNameSend_server + // + this.cb_portNameSend_server.FormattingEnabled = true; + this.cb_portNameSend_server.Location = new System.Drawing.Point(614, 21); + this.cb_portNameSend_server.Name = "cb_portNameSend_server"; + this.cb_portNameSend_server.Size = new System.Drawing.Size(47, 20); + this.cb_portNameSend_server.TabIndex = 24; + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(587, 25); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(29, 12); + this.label8.TabIndex = 23; + this.label8.Text = "端口"; + // // ModBusRtuControl // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); @@ -468,8 +503,8 @@ private System.Windows.Forms.TextBox txt_content; private System.Windows.Forms.GroupBox groupBox2; private System.Windows.Forms.Button but_close; - private System.Windows.Forms.Button button2; - private System.Windows.Forms.Button button1; + private System.Windows.Forms.Button but_server_close; + private System.Windows.Forms.Button but_server_open; private System.Windows.Forms.Button but_open; private System.Windows.Forms.Label label5; private System.Windows.Forms.TextBox txt_stationNumber; @@ -480,5 +515,8 @@ private System.Windows.Forms.TextBox txt_stopBit; private System.Windows.Forms.Label label6; private System.Windows.Forms.TextBox txt_dataBit; + private System.Windows.Forms.RadioButton rd_discrete; + private System.Windows.Forms.ComboBox cb_portNameSend_server; + private System.Windows.Forms.Label label8; } } diff --git a/IoTClient.Tool/Controls/ModBusRtuControl.cs b/IoTClient.Tool/Controls/ModBusRtuControl.cs index 489d302..1552358 100644 --- a/IoTClient.Tool/Controls/ModBusRtuControl.cs +++ b/IoTClient.Tool/Controls/ModBusRtuControl.cs @@ -1,20 +1,16 @@ using IoTClient.Clients.ModBus; +using IoTServer.Servers.ModBus; using System; using System.Drawing; using System.IO.Ports; -using System.Text; using System.Windows.Forms; namespace IoTClient.Tool.Controls { public partial class ModBusRtuControl : UserControl { - /// - /// 串行端口对象 - /// - //private SerialPort serialPort; - private ModBusRtuClient modBusRtuClient; - + private ModBusRtuClient client; + private ModBusRtuServer server; public ModBusRtuControl() { InitializeComponent(); @@ -29,10 +25,11 @@ namespace IoTClient.Tool.Controls txt_content.Location = new Point(13, 160); but_read.Enabled = false; but_write.Enabled = false; - button2.Enabled = false; + but_server_close.Enabled = false; but_close.Enabled = false; UpdatePortNames(); cb_portNameSend.DropDownStyle = ComboBoxStyle.DropDownList; + cb_portNameSend_server.DropDownStyle = ComboBoxStyle.DropDownList; } /// @@ -40,9 +37,15 @@ namespace IoTClient.Tool.Controls /// public void UpdatePortNames() { - cb_portNameSend.DataSource = SerialPort.GetPortNames(); + cb_portNameSend.DataSource = ModBusRtuClient.GetPortNames(); + cb_portNameSend_server.DataSource = ModBusRtuClient.GetPortNames(); } + /// + /// 打开连接 + /// + /// + /// private void but_open_Click(object sender, EventArgs e) { try @@ -51,9 +54,9 @@ namespace IoTClient.Tool.Controls var BaudRate = int.Parse(txt_baudRate.Text.ToString()); var DataBits = int.Parse(txt_dataBit.Text.ToString()); var StopBits = (StopBits)int.Parse(txt_stopBit.Text.ToString()); - - if (modBusRtuClient == null) modBusRtuClient = new ModBusRtuClient(PortName, BaudRate, DataBits, StopBits); - var result = modBusRtuClient.Open(); + client?.Close(); + client = new ModBusRtuClient(PortName, BaudRate, DataBits, StopBits); + var result = client.Open(); if (result.IsSucceed) { but_open.Enabled = false; @@ -66,7 +69,6 @@ namespace IoTClient.Tool.Controls } else AppendText($"连接失败:{result.Err}"); - UpdatePortNames(); } catch (Exception ex) { @@ -74,22 +76,101 @@ namespace IoTClient.Tool.Controls } } - private void AppendText(string content) - { - txt_content.Invoke((Action)(() => - { - txt_content.AppendText($"[{DateTime.Now.ToLongTimeString()}]{content}\r\n"); - })); - } - /// - /// 发送数据 + /// 关闭连接 /// /// /// - private void butWrite_Click(object sender, EventArgs e) + private void butClose_Click(object sender, EventArgs e) { - var address = txt_address.Text?.Trim(); + client?.Close(); + AppendText("关闭连接"); + but_open.Enabled = true; + but_close.Enabled = false; + cb_portNameSend.Enabled = true; + } + + /// + /// 读数据 + /// + /// + /// + private void but_read_Click(object sender, EventArgs e) + { + byte.TryParse(txt_stationNumber.Text?.Trim(), out byte stationNumber); + if (string.IsNullOrWhiteSpace(txt_address.Text)) + { + MessageBox.Show("请输入地址"); + return; + } + try + { + dynamic result = null; + if (rd_bit.Checked) + { + result = client.ReadCoil(txt_address.Text, stationNumber); + } + else if (rd_short.Checked) + { + result = client.ReadInt16(txt_address.Text, stationNumber); + } + else if (rd_ushort.Checked) + { + result = client.ReadUInt16(txt_address.Text, stationNumber); + } + else if (rd_int.Checked) + { + result = client.ReadInt32(txt_address.Text, stationNumber); + } + else if (rd_uint.Checked) + { + result = client.ReadUInt32(txt_address.Text, stationNumber); + } + else if (rd_long.Checked) + { + result = client.ReadInt64(txt_address.Text, stationNumber); + } + else if (rd_ulong.Checked) + { + result = client.ReadUInt64(txt_address.Text, stationNumber); + } + else if (rd_float.Checked) + { + result = client.ReadFloat(txt_address.Text, stationNumber); + } + else if (rd_double.Checked) + { + result = client.ReadDouble(txt_address.Text, stationNumber); + } + else if (rd_discrete.Checked) + { + result = client.ReadDiscrete(txt_address.Text, stationNumber); + } + + if (result.IsSucceed) + txt_content.AppendText($"[{DateTime.Now.ToLongTimeString()}][读取 {txt_address.Text?.Trim()} 成功]:{result.Value}\r\n"); + else + txt_content.AppendText($"[{DateTime.Now.ToLongTimeString()}][读取 {txt_address.Text?.Trim()} 失败]:{result.Err}\r\n"); + if (checkBox1.Checked) + { + txt_content.AppendText($"[请求报文]{result.Requst}\r\n"); + txt_content.AppendText($"[响应报文]{result.Response}\r\n\r\n"); + } + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + } + } + + /// + /// 写数据 + /// + /// + /// + private void but_write_Click(object sender, EventArgs e) + { + var address = txt_address.Text?.Trim(); byte.TryParse(txt_stationNumber.Text?.Trim(), out byte stationNumber); if (string.IsNullOrWhiteSpace(txt_address.Text)) { @@ -119,40 +200,45 @@ namespace IoTClient.Tool.Controls return; } } - result = modBusRtuClient.Write(address, coil, stationNumber); + result = client.Write(address, coil, stationNumber); } else if (rd_short.Checked) { - result = modBusRtuClient.Write(address, short.Parse(txt_value.Text?.Trim()), stationNumber); + result = client.Write(address, short.Parse(txt_value.Text?.Trim()), stationNumber); } else if (rd_ushort.Checked) { - result = modBusRtuClient.Write(address, ushort.Parse(txt_value.Text?.Trim()), stationNumber); + result = client.Write(address, ushort.Parse(txt_value.Text?.Trim()), stationNumber); } else if (rd_int.Checked) { - result = modBusRtuClient.Write(address, int.Parse(txt_value.Text?.Trim()), stationNumber); + result = client.Write(address, int.Parse(txt_value.Text?.Trim()), stationNumber); } else if (rd_uint.Checked) { - result = modBusRtuClient.Write(address, uint.Parse(txt_value.Text?.Trim()), stationNumber); + result = client.Write(address, uint.Parse(txt_value.Text?.Trim()), stationNumber); } else if (rd_long.Checked) { - result = modBusRtuClient.Write(address, long.Parse(txt_value.Text?.Trim()), stationNumber); + result = client.Write(address, long.Parse(txt_value.Text?.Trim()), stationNumber); } else if (rd_ulong.Checked) { - result = modBusRtuClient.Write(address, ulong.Parse(txt_value.Text?.Trim()), stationNumber); + result = client.Write(address, ulong.Parse(txt_value.Text?.Trim()), stationNumber); } else if (rd_float.Checked) { - result = modBusRtuClient.Write(address, float.Parse(txt_value.Text?.Trim()), stationNumber); + result = client.Write(address, float.Parse(txt_value.Text?.Trim()), stationNumber); } else if (rd_double.Checked) { - result = modBusRtuClient.Write(address, double.Parse(txt_value.Text?.Trim()), stationNumber); - } + result = client.Write(address, double.Parse(txt_value.Text?.Trim()), stationNumber); + } + else if (rd_discrete.Checked) + { + txt_content.AppendText($"[{DateTime.Now.ToLongTimeString()}]离散类型只读\r\n"); + return; + } if (result.IsSucceed) txt_content.AppendText($"[{DateTime.Now.ToLongTimeString()}][写入 {address?.Trim()} 成功]:{txt_value.Text?.Trim()} OK\r\n"); @@ -170,76 +256,53 @@ namespace IoTClient.Tool.Controls } } - private void butClose_Click(object sender, EventArgs e) + /// + /// 启动仿真服务 + /// + /// + /// + private void but_open_server_Click(object sender, EventArgs e) { - modBusRtuClient?.Close(); - AppendText("关闭连接"); - but_open.Enabled = true; - but_close.Enabled = false; - } - - private void but_read_Click(object sender, EventArgs e) - { - byte.TryParse(txt_stationNumber.Text?.Trim(), out byte stationNumber); - if (string.IsNullOrWhiteSpace(txt_address.Text)) - { - MessageBox.Show("请输入地址"); - return; - } try { - dynamic result = null; - if (rd_bit.Checked) - { - result = modBusRtuClient.ReadCoil(txt_address.Text, stationNumber); - } - else if (rd_short.Checked) - { - result = modBusRtuClient.ReadInt16(txt_address.Text, stationNumber); - } - else if (rd_ushort.Checked) - { - result = modBusRtuClient.ReadUInt16(txt_address.Text, stationNumber); - } - else if (rd_int.Checked) - { - result = modBusRtuClient.ReadInt32(txt_address.Text, stationNumber); - } - else if (rd_uint.Checked) - { - result = modBusRtuClient.ReadUInt32(txt_address.Text, stationNumber); - } - else if (rd_long.Checked) - { - result = modBusRtuClient.ReadInt64(txt_address.Text, stationNumber); - } - else if (rd_ulong.Checked) - { - result = modBusRtuClient.ReadUInt64(txt_address.Text, stationNumber); - } - else if (rd_float.Checked) - { - result = modBusRtuClient.ReadFloat(txt_address.Text, stationNumber); - } - else if (rd_double.Checked) - { - result = modBusRtuClient.ReadDouble(txt_address.Text, stationNumber); - } - - if (result.IsSucceed) - txt_content.AppendText($"[{DateTime.Now.ToLongTimeString()}][读取 {txt_address.Text?.Trim()} 成功]:{result.Value}\r\n"); - else - txt_content.AppendText($"[{DateTime.Now.ToLongTimeString()}][读取 {txt_address.Text?.Trim()} 失败]:{result.Err}\r\n"); - if (checkBox1.Checked) - { - txt_content.AppendText($"[请求报文]{result.Requst}\r\n"); - txt_content.AppendText($"[响应报文]{result.Response}\r\n\r\n"); - } + var PortName = cb_portNameSend_server.Text.ToString(); + var BaudRate = int.Parse(txt_baudRate.Text.ToString()); + var DataBits = int.Parse(txt_dataBit.Text.ToString()); + var StopBits = (StopBits)int.Parse(txt_stopBit.Text.ToString()); + server?.Stop(); + server = new ModBusRtuServer(PortName, BaudRate, DataBits, StopBits); + server.Start(); + AppendText("开启仿真服务"); + but_server_open.Enabled = false; + but_server_close.Enabled = true; + cb_portNameSend_server.Enabled = false; } catch (Exception ex) { MessageBox.Show(ex.Message); } } + + /// + /// 关闭仿真服务 + /// + /// + /// + private void but_close_server_Click(object sender, EventArgs e) + { + server?.Stop(); + AppendText("关闭仿真服务"); + but_server_open.Enabled = true; + but_server_close.Enabled = false; + cb_portNameSend_server.Enabled = true; + } + + private void AppendText(string content) + { + txt_content.Invoke((Action)(() => + { + txt_content.AppendText($"[{DateTime.Now.ToLongTimeString()}]{content}\r\n"); + })); + } } } diff --git a/IoTClient.Tool/Controls/ModBusTcpControl.Designer.cs b/IoTClient.Tool/Controls/ModBusTcpControl.Designer.cs index b2c1e4d..9bf8160 100644 --- a/IoTClient.Tool/Controls/ModBusTcpControl.Designer.cs +++ b/IoTClient.Tool/Controls/ModBusTcpControl.Designer.cs @@ -61,6 +61,7 @@ this.label4 = new System.Windows.Forms.Label(); this.txt_content = new System.Windows.Forms.TextBox(); this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.rd_discrete = new System.Windows.Forms.RadioButton(); this.groupBox2.SuspendLayout(); this.groupBox1.SuspendLayout(); this.groupBox3.SuspendLayout(); @@ -187,6 +188,7 @@ // // groupBox1 // + this.groupBox1.Controls.Add(this.rd_discrete); this.groupBox1.Controls.Add(this.button6); this.groupBox1.Controls.Add(this.rd_float); this.groupBox1.Controls.Add(this.rd_double); @@ -216,7 +218,7 @@ // rd_float // this.rd_float.AutoSize = true; - this.rd_float.Location = new System.Drawing.Point(436, 23); + this.rd_float.Location = new System.Drawing.Point(486, 23); this.rd_float.Name = "rd_float"; this.rd_float.Size = new System.Drawing.Size(53, 16); this.rd_float.TabIndex = 22; @@ -226,7 +228,7 @@ // rd_double // this.rd_double.AutoSize = true; - this.rd_double.Location = new System.Drawing.Point(495, 23); + this.rd_double.Location = new System.Drawing.Point(545, 23); this.rd_double.Name = "rd_double"; this.rd_double.Size = new System.Drawing.Size(59, 16); this.rd_double.TabIndex = 23; @@ -237,7 +239,7 @@ // this.rd_short.AutoSize = true; this.rd_short.Checked = true; - this.rd_short.Location = new System.Drawing.Point(70, 23); + this.rd_short.Location = new System.Drawing.Point(120, 23); this.rd_short.Name = "rd_short"; this.rd_short.Size = new System.Drawing.Size(53, 16); this.rd_short.TabIndex = 6; @@ -258,7 +260,7 @@ // rd_ulong // this.rd_ulong.AutoSize = true; - this.rd_ulong.Location = new System.Drawing.Point(374, 23); + this.rd_ulong.Location = new System.Drawing.Point(424, 23); this.rd_ulong.Name = "rd_ulong"; this.rd_ulong.Size = new System.Drawing.Size(53, 16); this.rd_ulong.TabIndex = 21; @@ -268,7 +270,7 @@ // rd_ushort // this.rd_ushort.AutoSize = true; - this.rd_ushort.Location = new System.Drawing.Point(129, 23); + this.rd_ushort.Location = new System.Drawing.Point(179, 23); this.rd_ushort.Name = "rd_ushort"; this.rd_ushort.Size = new System.Drawing.Size(59, 16); this.rd_ushort.TabIndex = 17; @@ -278,7 +280,7 @@ // rd_long // this.rd_long.AutoSize = true; - this.rd_long.Location = new System.Drawing.Point(315, 23); + this.rd_long.Location = new System.Drawing.Point(365, 23); this.rd_long.Name = "rd_long"; this.rd_long.Size = new System.Drawing.Size(47, 16); this.rd_long.TabIndex = 20; @@ -288,7 +290,7 @@ // rd_int // this.rd_int.AutoSize = true; - this.rd_int.Location = new System.Drawing.Point(194, 23); + this.rd_int.Location = new System.Drawing.Point(244, 23); this.rd_int.Name = "rd_int"; this.rd_int.Size = new System.Drawing.Size(41, 16); this.rd_int.TabIndex = 18; @@ -298,7 +300,7 @@ // rd_uint // this.rd_uint.AutoSize = true; - this.rd_uint.Location = new System.Drawing.Point(253, 23); + this.rd_uint.Location = new System.Drawing.Point(303, 23); this.rd_uint.Name = "rd_uint"; this.rd_uint.Size = new System.Drawing.Size(47, 16); this.rd_uint.TabIndex = 19; @@ -382,6 +384,16 @@ this.txt_content.Size = new System.Drawing.Size(855, 272); this.txt_content.TabIndex = 27; // + // radioButton1 + // + this.rd_discrete.AutoSize = true; + this.rd_discrete.Location = new System.Drawing.Point(64, 23); + this.rd_discrete.Name = "radioButton1"; + this.rd_discrete.Size = new System.Drawing.Size(47, 16); + this.rd_discrete.TabIndex = 24; + this.rd_discrete.Text = "离散"; + this.rd_discrete.UseVisualStyleBackColor = true; + // // ModBusTcpControl // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); @@ -437,5 +449,6 @@ private System.Windows.Forms.Button button2; private System.Windows.Forms.Button button5; private System.Windows.Forms.Button button6; + private System.Windows.Forms.RadioButton rd_discrete; } } diff --git a/IoTClient.Tool/Controls/ModBusTcpControl.cs b/IoTClient.Tool/Controls/ModBusTcpControl.cs index 88d4038..485c324 100644 --- a/IoTClient.Tool/Controls/ModBusTcpControl.cs +++ b/IoTClient.Tool/Controls/ModBusTcpControl.cs @@ -32,7 +32,7 @@ namespace IoTClient.Tool private void button1_Click(object sender, EventArgs e) { - server?.Close(); + server?.Stop(); server = new ModBusTcpServer(502); server.Start(); button1.Enabled = false; @@ -42,7 +42,7 @@ namespace IoTClient.Tool private void button2_Click(object sender, EventArgs e) { - server?.Close(); + server?.Stop(); button1.Enabled = true; button2.Enabled = false; txt_content.AppendText($"[{DateTime.Now.ToLongTimeString()}]关闭仿真模拟服务\r\n"); @@ -120,6 +120,10 @@ namespace IoTClient.Tool { result = client.ReadDouble(txt_address.Text, stationNumber); } + else if (rd_discrete.Checked) + { + result = client.ReadDiscrete(txt_address.Text, stationNumber); + } if (result.IsSucceed) txt_content.AppendText($"[{DateTime.Now.ToLongTimeString()}][读取 {txt_address.Text?.Trim()} 成功]:{result.Value}\r\n"); @@ -202,7 +206,11 @@ namespace IoTClient.Tool { result = client.Write(txt_address.Text, double.Parse(txt_value.Text?.Trim()), stationNumber); } - + else if (rd_discrete.Checked) + { + txt_content.AppendText($"[{DateTime.Now.ToLongTimeString()}]离散类型只读\r\n"); + return; + } if (result.IsSucceed) txt_content.AppendText($"[{DateTime.Now.ToLongTimeString()}][写入 {txt_address.Text?.Trim()} 成功]:{txt_value.Text?.Trim()} OK\r\n"); diff --git a/IoTClient.Tool/Controls/SiemensControl.cs b/IoTClient.Tool/Controls/SiemensControl.cs index d7a9758..5ddc7f5 100644 --- a/IoTClient.Tool/Controls/SiemensControl.cs +++ b/IoTClient.Tool/Controls/SiemensControl.cs @@ -31,7 +31,7 @@ namespace IoTClient.Tool private void but_server_Click(object sender, EventArgs e) { - server?.Close(); + server?.Stop(); server = new SiemensServer(int.Parse(txt_port.Text.Trim())); server.Start(); but_server.Enabled = false; @@ -41,7 +41,7 @@ namespace IoTClient.Tool private void button2_Click(object sender, EventArgs e) { - server?.Close(); + server?.Stop(); but_server.Enabled = true; button2.Enabled = false; txt_content.AppendText($"[{DateTime.Now.ToLongTimeString()}]关闭仿真模拟服务\r\n"); diff --git a/IoTClient.Tool/IndexForm.Designer.cs b/IoTClient.Tool/IndexForm.Designer.cs index f303381..7915266 100644 --- a/IoTClient.Tool/IndexForm.Designer.cs +++ b/IoTClient.Tool/IndexForm.Designer.cs @@ -182,8 +182,8 @@ // this.toolStripMenuItem1.ForeColor = System.Drawing.Color.White; this.toolStripMenuItem1.Name = "toolStripMenuItem1"; - this.toolStripMenuItem1.Size = new System.Drawing.Size(87, 21); - this.toolStripMenuItem1.Text = "测试版 0.0.3"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(75, 21); + this.toolStripMenuItem1.Text = "版本 0.0.3"; this.toolStripMenuItem1.Click += new System.EventHandler(this.toolStripMenuItem1_Click); // // IndexForm diff --git a/IoTClient/Clients/ModBus/ModBusRtuClient.cs b/IoTClient/Clients/ModBus/ModBusRtuClient.cs index e361903..609b4a7 100644 --- a/IoTClient/Clients/ModBus/ModBusRtuClient.cs +++ b/IoTClient/Clients/ModBus/ModBusRtuClient.cs @@ -1,5 +1,5 @@ using IoTClient.Common.Helpers; -using IoTClient.Core; +using IoTClient.Models; using System; using System.IO.Ports; using System.Linq; @@ -47,7 +47,7 @@ namespace IoTClient.Clients.ModBus /// 获取设备上的COM端口集合 /// /// - public string[] GetPortNames() + public static string[] GetPortNames() { return SerialPort.GetPortNames(); } @@ -138,6 +138,19 @@ namespace IoTClient.Clients.ModBus //3 获取响应报文 var responsePackage = SerialPortRead(serialPort); + if (!responsePackage.Any()) + { + result.IsSucceed = false; + result.Err = "响应结果为空,请检查是否连上了服务端"; + return result; + } + else if (!CRC16.CheckCRC16(responsePackage)) + { + result.IsSucceed = false; + result.Err = "响应结果CRC16验证失败"; + return result; + } + byte[] resultData = new byte[responsePackage.Length - 2]; Array.Copy(responsePackage, 0, resultData, 0, resultData.Length); @@ -363,7 +376,30 @@ namespace IoTClient.Clients.ModBus if (result.IsSucceed) result.Value = BitConverter.ToBoolean(readResut.Value, 0); return result; - } + } + + /// + /// 读取离散 + /// + /// + /// + /// + /// + public Result ReadDiscrete(string address, byte stationNumber = 1, byte functionCode = 2) + { + var readResut = Read(address, stationNumber, functionCode); + var result = new Result() + { + IsSucceed = readResut.IsSucceed, + Err = readResut.Err, + ErrList = readResut.ErrList, + Requst = readResut.Requst, + Response = readResut.Response, + }; + if (result.IsSucceed) + result.Value = BitConverter.ToBoolean(readResut.Value, 0); + return result; + } #endregion #region Write 写入 @@ -385,10 +421,16 @@ namespace IoTClient.Clients.ModBus serialPort.Write(commandCRC16, 0, commandCRC16.Length); result.Requst = string.Join(" ", commandCRC16.Select(t => t.ToString("X2"))); //3 获取响应报文 - var dataPackage = SerialPortRead(serialPort); - byte[] resultBuffer = new byte[dataPackage.Length - 2]; - Array.Copy(dataPackage, 0, resultBuffer, 0, resultBuffer.Length); - result.Response = string.Join(" ", dataPackage.Select(t => t.ToString("X2"))); + var responsePackage = SerialPortRead(serialPort); + if (!CRC16.CheckCRC16(responsePackage)) + { + result.IsSucceed = false; + result.Err = "响应结果CRC16验证失败"; + return result; + } + byte[] resultBuffer = new byte[responsePackage.Length - 2]; + Array.Copy(responsePackage, 0, resultBuffer, 0, resultBuffer.Length); + result.Response = string.Join(" ", responsePackage.Select(t => t.ToString("X2"))); } catch (Exception ex) { @@ -424,10 +466,16 @@ namespace IoTClient.Clients.ModBus serialPort.Write(commandCRC16, 0, commandCRC16.Length); result.Requst = string.Join(" ", commandCRC16.Select(t => t.ToString("X2"))); //3 获取响应报文 - var dataPackage = SerialPortRead(serialPort); - byte[] resultBuffer = new byte[dataPackage.Length - 2]; - Array.Copy(dataPackage, 0, resultBuffer, 0, resultBuffer.Length); - result.Response = string.Join(" ", dataPackage.Select(t => t.ToString("X2"))); + var responsePackage = SerialPortRead(serialPort); + if (!CRC16.CheckCRC16(responsePackage)) + { + result.IsSucceed = false; + result.Err = "响应结果CRC16验证失败"; + return result; + } + byte[] resultBuffer = new byte[responsePackage.Length - 2]; + Array.Copy(responsePackage, 0, resultBuffer, 0, resultBuffer.Length); + result.Response = string.Join(" ", responsePackage.Select(t => t.ToString("X2"))); } catch (Exception ex) { diff --git a/IoTClient/Clients/ModBus/ModBusTcpClient.cs b/IoTClient/Clients/ModBus/ModBusTcpClient.cs index 9386566..9f46b70 100644 --- a/IoTClient/Clients/ModBus/ModBusTcpClient.cs +++ b/IoTClient/Clients/ModBus/ModBusTcpClient.cs @@ -1,4 +1,5 @@ using IoTClient.Core; +using IoTClient.Models; using System; using System.Linq; using System.Net; @@ -73,8 +74,9 @@ namespace IoTClient.Clients.ModBus var result = new Result(); try { + var chenkHead = GetCheckHead(functionCode); //1 获取命令(组装报文) - byte[] command = GetReadCommand(address, stationNumber, functionCode, readLength); + byte[] command = GetReadCommand(address, stationNumber, functionCode, readLength, chenkHead); result.Requst = string.Join(" ", command.Select(t => t.ToString("X2"))); //2 发送命令 socket.Send(command); @@ -82,13 +84,17 @@ namespace IoTClient.Clients.ModBus var headPackage = SocketRead(socket, 8); int length = headPackage[4] * 256 + headPackage[5] - 2; var dataPackage = SocketRead(socket, length); - byte[] resultBuffer = new byte[dataPackage.Length - 1]; Array.Copy(dataPackage, 1, resultBuffer, 0, resultBuffer.Length); - result.Response = string.Join(" ", headPackage.Concat(dataPackage).Select(t => t.ToString("X2"))); //4 获取响应报文数据(字节数组形式) result.Value = resultBuffer.Reverse().ToArray(); + if (chenkHead[0] != headPackage[0] || chenkHead[1] != headPackage[1]) + { + result.IsSucceed = false; + result.Err = "响应结果校验失败"; + result.ErrList.Add("响应结果校验失败"); + } } catch (SocketException ex) { @@ -319,6 +325,29 @@ namespace IoTClient.Clients.ModBus result.Value = BitConverter.ToBoolean(readResut.Value, 0); return result; } + + /// + /// 读取离散 + /// + /// + /// + /// + /// + public Result ReadDiscrete(string address, byte stationNumber = 1, byte functionCode = 2) + { + var readResut = Read(address, stationNumber, functionCode); + var result = new Result() + { + IsSucceed = readResut.IsSucceed, + Err = readResut.Err, + ErrList = readResut.ErrList, + Requst = readResut.Requst, + Response = readResut.Response, + }; + if (result.IsSucceed) + result.Value = BitConverter.ToBoolean(readResut.Value, 0); + return result; + } #endregion #region Write 写入 @@ -336,14 +365,21 @@ namespace IoTClient.Clients.ModBus var result = new Result(); try { - var command = GetWriteCoilCommand(address, value, stationNumber, functionCode); + var chenkHead = GetCheckHead(functionCode); + var command = GetWriteCoilCommand(address, value, stationNumber, functionCode, chenkHead); socket.Send(command); result.Requst = string.Join(" ", command.Select(t => t.ToString("X2"))); //获取响应报文 - var headBytes = SocketRead(socket, 8); - int length = headBytes[4] * 256 + headBytes[5] - 2; - var dataBytes = SocketRead(socket, length); - result.Response = string.Join(" ", headBytes.Concat(dataBytes).Select(t => t.ToString("X2"))); + var headPackage = SocketRead(socket, 8); + int length = headPackage[4] * 256 + headPackage[5] - 2; + var dataPackage = SocketRead(socket, length); + result.Response = string.Join(" ", headPackage.Concat(dataPackage).Select(t => t.ToString("X2"))); + if (chenkHead[0] != headPackage[0] || chenkHead[1] != headPackage[1]) + { + result.IsSucceed = false; + result.Err = "响应结果校验失败"; + result.ErrList.Add("响应结果校验失败"); + } } catch (SocketException ex) { @@ -383,14 +419,21 @@ namespace IoTClient.Clients.ModBus var result = new Result(); try { - var command = GetWriteCommand(address, values, stationNumber, functionCode); + var chenkHead = GetCheckHead(functionCode); + var command = GetWriteCommand(address, values, stationNumber, functionCode, chenkHead); socket.Send(command); result.Requst = string.Join(" ", command.Select(t => t.ToString("X2"))); //获取响应报文 - var headBytes = SocketRead(socket, 8); - int length = headBytes[4] * 256 + headBytes[5] - 2; - var dataBytes = SocketRead(socket, length); - result.Response = string.Join(" ", headBytes.Concat(dataBytes).Select(t => t.ToString("X2"))); + var headPackage = SocketRead(socket, 8); + int length = headPackage[4] * 256 + headPackage[5] - 2; + var dataPackage = SocketRead(socket, length); + result.Response = string.Join(" ", headPackage.Concat(dataPackage).Select(t => t.ToString("X2"))); + if (chenkHead[0] != headPackage[0] || chenkHead[1] != headPackage[1]) + { + result.IsSucceed = false; + result.Err = "响应结果校验失败"; + result.ErrList.Add("响应结果校验失败"); + } } catch (SocketException ex) { @@ -522,6 +565,16 @@ namespace IoTClient.Clients.ModBus #region 获取命令 + /// + /// 获取随机校验头 + /// + /// + private byte[] GetCheckHead(int seed) + { + var random = new Random(DateTime.Now.Millisecond + seed); + return new byte[] { (byte)random.Next(255), (byte)random.Next(255) }; + } + /// /// 获取读取命令 /// @@ -530,12 +583,12 @@ namespace IoTClient.Clients.ModBus /// 功能码 /// 读取长度 /// - public byte[] GetReadCommand(string address, byte stationNumber, byte functionCode, ushort length) + public byte[] GetReadCommand(string address, byte stationNumber, byte functionCode, ushort length, byte[] check = null) { var readAddress = ushort.Parse(address?.Trim()); byte[] buffer = new byte[12]; - buffer[0] = 0x19; - buffer[1] = 0xB2;//Client发出的检验信息 + buffer[0] = check?[0] ?? 0x19; + buffer[1] = check?[1] ?? 0xB2;//Client发出的检验信息 buffer[2] = 0x00; buffer[3] = 0x00;//表示tcp/ip 的协议的modbus的协议 buffer[4] = 0x00; @@ -558,12 +611,12 @@ namespace IoTClient.Clients.ModBus /// 站号 /// 功能码 /// - public byte[] GetWriteCommand(string address, byte[] values, byte stationNumber, byte functionCode) + public byte[] GetWriteCommand(string address, byte[] values, byte stationNumber, byte functionCode, byte[] check = null) { var writeAddress = ushort.Parse(address?.Trim()); byte[] buffer = new byte[13 + values.Length]; - buffer[0] = 0x19; - buffer[1] = 0xB2;//检验信息,用来验证response是否串数据了 + buffer[0] = check?[0] ?? 0x19; + buffer[1] = check?[1] ?? 0xB2;//检验信息,用来验证response是否串数据了 buffer[4] = BitConverter.GetBytes(7 + values.Length)[1]; buffer[5] = BitConverter.GetBytes(7 + values.Length)[0];//表示的是header handle后面还有多长的字节 @@ -586,12 +639,12 @@ namespace IoTClient.Clients.ModBus /// 站号 /// 功能码 /// - public byte[] GetWriteCoilCommand(string address, bool value, byte stationNumber, byte functionCode) + public byte[] GetWriteCoilCommand(string address, bool value, byte stationNumber, byte functionCode, byte[] check = null) { var writeAddress = ushort.Parse(address?.Trim()); byte[] buffer = new byte[12]; - buffer[0] = 0x19; - buffer[1] = 0xB2;//Client发出的检验信息 + buffer[0] = check?[0] ?? 0x19; + buffer[1] = check?[1] ?? 0xB2;//Client发出的检验信息 buffer[4] = 0x00; buffer[5] = 0x06;//表示的是该字节以后的字节长度 diff --git a/IoTClient/Clients/PLC/SiemensClient.cs b/IoTClient/Clients/PLC/SiemensClient.cs index 809e03f..a4a2485 100644 --- a/IoTClient/Clients/PLC/SiemensClient.cs +++ b/IoTClient/Clients/PLC/SiemensClient.cs @@ -2,6 +2,7 @@ using IoTClient.Common.Enums; using IoTClient.Core; using IoTClient.Core.Models; +using IoTClient.Models; using System; using System.Collections.Generic; using System.Linq; diff --git a/IoTClient/IoTClient/IoTClient.xml b/IoTClient/IoTClient/IoTClient.xml index 62f1831..bffc7b9 100644 --- a/IoTClient/IoTClient/IoTClient.xml +++ b/IoTClient/IoTClient/IoTClient.xml @@ -149,6 +149,15 @@ 功能码 + + + 读取离散 + + + + + + 线圈写入 @@ -379,6 +388,15 @@ 功能码 + + + 读取离散 + + + + + + 线圈写入 @@ -470,7 +488,13 @@ 站号 功能码 - + + + 获取随机校验头 + + + + 获取读取命令 @@ -480,7 +504,7 @@ 读取长度 - + 获取写入命令 @@ -490,7 +514,7 @@ 功能码 - + 获取线圈写入命令 @@ -805,42 +829,42 @@ 西门子S7-200Smar - + 请求结果 - + 是否成功 - + 异常消息 - + 异常集合 - + 请求报文 - + 响应报文 - + 请求结果 - + 数据结果 diff --git a/IoTClient/Models/Result.cs b/IoTClient/Models/Result.cs new file mode 100644 index 0000000..979f617 --- /dev/null +++ b/IoTClient/Models/Result.cs @@ -0,0 +1,61 @@ +using System.Collections.Generic; + +namespace IoTClient.Models +{ + /// + /// 请求结果 + /// + public class Result + { + public Result() + { + } + + /// + /// 是否成功 + /// + public bool IsSucceed { get; set; } = true; + + /// + /// 异常消息 + /// + public string Err { get; set; } + + /// + /// 异常集合 + /// + public List ErrList { get; set; } = new List(); + + /// + /// 请求报文 + /// + public string Requst { get; set; } + + /// + /// 响应报文 + /// + public string Response { get; set; } + } + + /// + /// 请求结果 + /// + public class Result : Result + { + public Result() + { + } + + public Result(T data) + { + Value = data; + } + + /// + /// 数据结果 + /// + public T Value { get; set; } + } + + +} diff --git a/IoTClient/Models/SiemensData.cs b/IoTClient/Models/SiemensData.cs index 8c105f9..9313c92 100644 --- a/IoTClient/Models/SiemensData.cs +++ b/IoTClient/Models/SiemensData.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace IoTClient.Core.Models +namespace IoTClient.Core.Models { public class SiemensData { diff --git a/IoTClient/SocketBase.cs b/IoTClient/SocketBase.cs index 8db2e7c..b95503d 100644 --- a/IoTClient/SocketBase.cs +++ b/IoTClient/SocketBase.cs @@ -1,4 +1,5 @@ -using System; +using IoTClient.Models; +using System; using System.Net.Sockets; namespace IoTClient.Core diff --git a/IoTServer/IoTServer/IoTServer.xml b/IoTServer/IoTServer/IoTServer.xml index aa22e21..3ab6e6c 100644 --- a/IoTServer/IoTServer/IoTServer.xml +++ b/IoTServer/IoTServer/IoTServer.xml @@ -39,11 +39,33 @@ 初始化加载数据 + + + 启动服务 + + + + + 停止服务 + + + + + 接收数据回调用 + + + + 启动服务 + + + 停止服务 + + 客户端连接到服务端 @@ -61,6 +83,11 @@ 启动服务 + + + 停止服务 + + 客户端连接到服务端 diff --git a/IoTServer/Servers/ModBus/ModBusRtuServer.cs b/IoTServer/Servers/ModBus/ModBusRtuServer.cs new file mode 100644 index 0000000..a7b54a7 --- /dev/null +++ b/IoTServer/Servers/ModBus/ModBusRtuServer.cs @@ -0,0 +1,117 @@ +using IoTClient.Common.Helpers; +using IoTServer.Common; +using Newtonsoft.Json; +using System; +using System.IO.Ports; +using System.Linq; +using System.Text; + +namespace IoTServer.Servers.ModBus +{ + public class ModBusRtuServer + { + private SerialPort serialPort; + DataPersist dataPersist; + public ModBusRtuServer(string portName, int baudRate, int dataBits, StopBits stopBits) + { + if (serialPort == null) serialPort = new SerialPort(); + serialPort.PortName = portName; + serialPort.BaudRate = baudRate; + serialPort.DataBits = dataBits; + serialPort.StopBits = stopBits; + serialPort.Encoding = Encoding.ASCII; +#if !DEBUG + serialPort.ReadTimeout = 1000;//1秒 +#endif + serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceived); + dataPersist = new DataPersist("ModBusTcpServer"); + } + + /// + /// 启动服务 + /// + public void Start() + { + serialPort.Open(); + } + + /// + /// 停止服务 + /// + public void Stop() + { + serialPort.Close(); + serialPort.Dispose(); + } + + /// + /// 接收数据回调用 + /// + /// + /// + private void DataReceived(object sender, SerialDataReceivedEventArgs e) + { + try + { + byte[] requetData = new byte[serialPort.BytesToRead]; + serialPort.Read(requetData, 0, requetData.Length); + var address = requetData[2] * 256 + requetData[3]; + switch (requetData[1]) + { + //读取线圈 + case 1: + { + var value = dataPersist.Read(address); + var bytes = JsonConvert.DeserializeObject(value).Reverse().ToArray(); + var tempData = new byte[4]; + tempData[0] = 1; + tempData[1] = 1; + Buffer.BlockCopy(bytes, 0, tempData, 2, 2); + var responseData = CRC16.GetCRC16(tempData); + serialPort.Write(responseData, 0, responseData.Length); + } + break; + //写入线圈 + case 5: + { + var value = new byte[2]; + Buffer.BlockCopy(requetData, requetData.Length - 4, value, 0, value.Length); + dataPersist.Write(address, JsonConvert.SerializeObject(value)); + serialPort.Write(requetData, 0, requetData.Length); + } + break; + //读取 + case 3: + { + var value = dataPersist.Read(address); + var bytes = JsonConvert.DeserializeObject(value); + if (bytes == null) + { + var length = requetData[requetData.Length - 4] * 256 + requetData[requetData.Length - 5]; + bytes = new byte[length * 2]; + } + var dataHead = new byte[] { 1, 3, (byte)bytes.Length }; + var tempData = dataHead.Concat(bytes).ToArray(); + var responseData = CRC16.GetCRC16(tempData); + serialPort.Write(responseData, 0, responseData.Length); + } + break; + //写入 + case 16: + { + var value = new byte[requetData[6]]; + Buffer.BlockCopy(requetData, 7, value, 0, value.Length); + dataPersist.Write(address, JsonConvert.SerializeObject(value)); + var tempData = new byte[6]; + Buffer.BlockCopy(requetData, 0, tempData, 0, tempData.Length); + var responseData = CRC16.GetCRC16(tempData); + serialPort.Write(responseData, 0, responseData.Length); + } + break; + } + } + catch (Exception ex) + { } + } + } +} diff --git a/IoTServer/Servers/ModBus/ModBusTcpServer.cs b/IoTServer/Servers/ModBus/ModBusTcpServer.cs index 8c9a43c..5beaeae 100644 --- a/IoTServer/Servers/ModBus/ModBusTcpServer.cs +++ b/IoTServer/Servers/ModBus/ModBusTcpServer.cs @@ -42,7 +42,10 @@ namespace IoTServer.Servers.ModBus Task.Run(() => { Accept(socketServer); }); } - public void Close() + /// + /// 停止服务 + /// + public void Stop() { if (socketServer?.Connected ?? false) socketServer.Shutdown(SocketShutdown.Both); @@ -148,7 +151,6 @@ namespace IoTServer.Servers.ModBus //读取 case 3: { - var value = dataPersist.Read(address);// 数据存在 8、9 var bytes = JsonConvert.DeserializeObject(value); if (bytes == null) diff --git a/IoTServer/Servers/PLC/SiemensServer.cs b/IoTServer/Servers/PLC/SiemensServer.cs index 6f6aca8..f8c8c64 100644 --- a/IoTServer/Servers/PLC/SiemensServer.cs +++ b/IoTServer/Servers/PLC/SiemensServer.cs @@ -44,7 +44,10 @@ namespace IoTServer.Servers.PLC Task.Run(() => { Accept(socketServer); }); } - public void Close() + /// + /// 停止服务 + /// + public void Stop() { if (socketServer?.Connected ?? false) socketServer.Shutdown(SocketShutdown.Both); diff --git a/README.md b/README.md index f0d4692..4e2693e 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,14 @@ QQ交流群:[995475200](https://jq.qq.com/?_wv=1027&k=5bz0ne5) # Demo效果图 -![image](https://user-images.githubusercontent.com/5820324/67485072-f27aab00-f69b-11e9-9219-0a0ccb4caae0.png) +![image](https://user-images.githubusercontent.com/5820324/68068792-1a50c980-fd94-11e9-9ad6-3c8cf683cf6d.png) -![image](https://user-images.githubusercontent.com/5820324/67485248-3e2d5480-f69c-11e9-8dea-0ef5ebc3369c.png) +![image](https://user-images.githubusercontent.com/5820324/68068801-305e8a00-fd94-11e9-9745-98afa11c968e.png) -![image](https://user-images.githubusercontent.com/5820324/67485565-e3482d00-f69c-11e9-99b1-e86d3c78fcec.png) +![image](https://user-images.githubusercontent.com/5820324/68068805-3c4a4c00-fd94-11e9-899e-cec0b4b70fa8.png) -![image](https://user-images.githubusercontent.com/5820324/67488504-86e80c00-f6a2-11e9-9612-7d92aebb2394.png) \ No newline at end of file +![image](https://user-images.githubusercontent.com/5820324/68068874-bf6ba200-fd94-11e9-817d-62ed251e258f.png) + +![image](https://user-images.githubusercontent.com/5820324/68068818-63a11900-fd94-11e9-932e-fa0bd5941861.png) + +![image](https://user-images.githubusercontent.com/5820324/68068890-e75b0580-fd94-11e9-9370-b914e5af9590.png)