diff --git a/IoTClient.Demo/IoTClient.Demo.csproj b/IoTClient.Demo/IoTClient.Demo.csproj index 6620cd8..2227d00 100644 --- a/IoTClient.Demo/IoTClient.Demo.csproj +++ b/IoTClient.Demo/IoTClient.Demo.csproj @@ -83,16 +83,16 @@ - + Form - - ModBusTcp.cs + + ModBusTcpForm.cs - - ModBusTcp.cs + + ModBusTcpForm.cs ResXFileCodeGenerator diff --git a/IoTClient.Demo/ModBusTcpForm.Designer.cs b/IoTClient.Demo/ModBusTcpForm.Designer.cs new file mode 100644 index 0000000..e29b73d --- /dev/null +++ b/IoTClient.Demo/ModBusTcpForm.Designer.cs @@ -0,0 +1,391 @@ +namespace IoTClient.Demo +{ + partial class ModBusTcpForm + { + /// + /// 必需的设计器变量。 + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 清理所有正在使用的资源。 + /// + /// 如果应释放托管资源,为 true;否则为 false。 + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows 窗体设计器生成的代码 + + /// + /// 设计器支持所需的方法 - 不要修改 + /// 使用代码编辑器修改此方法的内容。 + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.button1 = new System.Windows.Forms.Button(); + this.but_open = new System.Windows.Forms.Button(); + this.txt_ip = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.txt_port = new System.Windows.Forms.TextBox(); + this.rd_short = new System.Windows.Forms.RadioButton(); + this.txt_address = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.button3 = new System.Windows.Forms.Button(); + this.txt_value = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.button4 = new System.Windows.Forms.Button(); + this.txt_content = new System.Windows.Forms.TextBox(); + this.txt_stationNumber = new System.Windows.Forms.TextBox(); + this.label5 = new System.Windows.Forms.Label(); + this.rd_bit = new System.Windows.Forms.RadioButton(); + this.rd_ushort = new System.Windows.Forms.RadioButton(); + this.rd_uint = new System.Windows.Forms.RadioButton(); + this.rd_int = new System.Windows.Forms.RadioButton(); + this.rd_ulong = new System.Windows.Forms.RadioButton(); + this.rd_long = new System.Windows.Forms.RadioButton(); + this.rd_double = new System.Windows.Forms.RadioButton(); + this.rd_float = new System.Windows.Forms.RadioButton(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.groupBox3.SuspendLayout(); + this.SuspendLayout(); + // + // button1 + // + this.button1.Location = new System.Drawing.Point(548, 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.button1.Click += new System.EventHandler(this.button1_Click); + // + // but_open + // + this.but_open.Location = new System.Drawing.Point(309, 18); + this.but_open.Name = "but_open"; + this.but_open.Size = new System.Drawing.Size(75, 23); + this.but_open.TabIndex = 1; + this.but_open.Text = "连接"; + this.but_open.UseVisualStyleBackColor = true; + this.but_open.Click += new System.EventHandler(this.button2_Click); + // + // txt_ip + // + this.txt_ip.Location = new System.Drawing.Point(37, 19); + this.txt_ip.Name = "txt_ip"; + this.txt_ip.Size = new System.Drawing.Size(100, 21); + this.txt_ip.TabIndex = 2; + this.txt_ip.Text = "127.0.0.1"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(8, 24); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(29, 12); + this.label1.TabIndex = 3; + this.label1.Text = "IP:"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(148, 24); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(41, 12); + this.label2.TabIndex = 4; + this.label2.Text = "端口:"; + // + // txt_port + // + this.txt_port.Location = new System.Drawing.Point(193, 19); + this.txt_port.Name = "txt_port"; + this.txt_port.Size = new System.Drawing.Size(100, 21); + this.txt_port.TabIndex = 5; + this.txt_port.Text = "502"; + // + // rd_short + // + this.rd_short.AutoSize = true; + this.rd_short.Checked = true; + this.rd_short.Location = new System.Drawing.Point(70, 23); + this.rd_short.Name = "rd_short"; + this.rd_short.Size = new System.Drawing.Size(53, 16); + this.rd_short.TabIndex = 6; + this.rd_short.TabStop = true; + this.rd_short.Text = "short"; + this.rd_short.UseVisualStyleBackColor = true; + // + // txt_address + // + this.txt_address.Location = new System.Drawing.Point(56, 16); + this.txt_address.Name = "txt_address"; + this.txt_address.Size = new System.Drawing.Size(121, 21); + this.txt_address.TabIndex = 9; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(9, 22); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(41, 12); + this.label3.TabIndex = 8; + this.label3.Text = "地址:"; + // + // button3 + // + this.button3.Location = new System.Drawing.Point(196, 14); + this.button3.Name = "button3"; + this.button3.Size = new System.Drawing.Size(75, 23); + this.button3.TabIndex = 7; + this.button3.Text = "读取"; + this.button3.UseVisualStyleBackColor = true; + this.button3.Click += new System.EventHandler(this.button3_Click); + // + // txt_value + // + this.txt_value.Location = new System.Drawing.Point(447, 16); + this.txt_value.Name = "txt_value"; + this.txt_value.Size = new System.Drawing.Size(100, 21); + this.txt_value.TabIndex = 12; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(400, 22); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(29, 12); + this.label4.TabIndex = 11; + this.label4.Text = "值:"; + // + // button4 + // + this.button4.Location = new System.Drawing.Point(567, 15); + this.button4.Name = "button4"; + this.button4.Size = new System.Drawing.Size(75, 23); + this.button4.TabIndex = 10; + this.button4.Text = "写入"; + this.button4.UseVisualStyleBackColor = true; + this.button4.Click += new System.EventHandler(this.button4_Click); + // + // txt_content + // + this.txt_content.Location = new System.Drawing.Point(25, 166); + this.txt_content.Multiline = true; + this.txt_content.Name = "txt_content"; + this.txt_content.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.txt_content.Size = new System.Drawing.Size(656, 272); + this.txt_content.TabIndex = 13; + // + // txt_stationNumber + // + this.txt_stationNumber.Location = new System.Drawing.Point(457, 18); + this.txt_stationNumber.Name = "txt_stationNumber"; + this.txt_stationNumber.Size = new System.Drawing.Size(35, 21); + this.txt_stationNumber.TabIndex = 14; + this.txt_stationNumber.Text = "1"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(410, 22); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(41, 12); + this.label5.TabIndex = 15; + this.label5.Text = "站号:"; + // + // rd_bit + // + this.rd_bit.AutoSize = true; + this.rd_bit.Location = new System.Drawing.Point(11, 23); + this.rd_bit.Name = "rd_bit"; + this.rd_bit.Size = new System.Drawing.Size(47, 16); + this.rd_bit.TabIndex = 16; + this.rd_bit.Text = "线圈"; + this.rd_bit.UseVisualStyleBackColor = true; + // + // rd_ushort + // + this.rd_ushort.AutoSize = true; + this.rd_ushort.Location = new System.Drawing.Point(129, 23); + this.rd_ushort.Name = "rd_ushort"; + this.rd_ushort.Size = new System.Drawing.Size(59, 16); + this.rd_ushort.TabIndex = 17; + this.rd_ushort.Text = "ushort"; + this.rd_ushort.UseVisualStyleBackColor = true; + // + // rd_uint + // + this.rd_uint.AutoSize = true; + this.rd_uint.Location = new System.Drawing.Point(253, 23); + this.rd_uint.Name = "rd_uint"; + this.rd_uint.Size = new System.Drawing.Size(47, 16); + this.rd_uint.TabIndex = 19; + this.rd_uint.Text = "uint"; + this.rd_uint.UseVisualStyleBackColor = true; + // + // rd_int + // + this.rd_int.AutoSize = true; + this.rd_int.Location = new System.Drawing.Point(194, 23); + this.rd_int.Name = "rd_int"; + this.rd_int.Size = new System.Drawing.Size(41, 16); + this.rd_int.TabIndex = 18; + this.rd_int.Text = "int"; + this.rd_int.UseVisualStyleBackColor = true; + // + // rd_ulong + // + this.rd_ulong.AutoSize = true; + this.rd_ulong.Location = new System.Drawing.Point(374, 23); + this.rd_ulong.Name = "rd_ulong"; + this.rd_ulong.Size = new System.Drawing.Size(53, 16); + this.rd_ulong.TabIndex = 21; + this.rd_ulong.Text = "ulong"; + this.rd_ulong.UseVisualStyleBackColor = true; + // + // rd_long + // + this.rd_long.AutoSize = true; + this.rd_long.Location = new System.Drawing.Point(315, 23); + this.rd_long.Name = "rd_long"; + this.rd_long.Size = new System.Drawing.Size(47, 16); + this.rd_long.TabIndex = 20; + this.rd_long.Text = "long"; + this.rd_long.UseVisualStyleBackColor = true; + // + // rd_double + // + this.rd_double.AutoSize = true; + this.rd_double.Location = new System.Drawing.Point(495, 23); + this.rd_double.Name = "rd_double"; + this.rd_double.Size = new System.Drawing.Size(59, 16); + this.rd_double.TabIndex = 23; + this.rd_double.Text = "double"; + this.rd_double.UseVisualStyleBackColor = true; + // + // rd_float + // + this.rd_float.AutoSize = true; + this.rd_float.Location = new System.Drawing.Point(436, 23); + this.rd_float.Name = "rd_float"; + this.rd_float.Size = new System.Drawing.Size(53, 16); + this.rd_float.TabIndex = 22; + this.rd_float.Text = "float"; + this.rd_float.UseVisualStyleBackColor = true; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.rd_float); + this.groupBox1.Controls.Add(this.rd_double); + this.groupBox1.Controls.Add(this.rd_short); + this.groupBox1.Controls.Add(this.rd_bit); + this.groupBox1.Controls.Add(this.rd_ulong); + this.groupBox1.Controls.Add(this.rd_ushort); + this.groupBox1.Controls.Add(this.rd_long); + this.groupBox1.Controls.Add(this.rd_int); + this.groupBox1.Controls.Add(this.rd_uint); + this.groupBox1.Location = new System.Drawing.Point(25, 61); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(656, 53); + this.groupBox1.TabIndex = 24; + this.groupBox1.TabStop = false; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.button1); + this.groupBox2.Controls.Add(this.but_open); + this.groupBox2.Controls.Add(this.label5); + this.groupBox2.Controls.Add(this.txt_ip); + this.groupBox2.Controls.Add(this.txt_stationNumber); + this.groupBox2.Controls.Add(this.label1); + this.groupBox2.Controls.Add(this.label2); + this.groupBox2.Controls.Add(this.txt_port); + this.groupBox2.Location = new System.Drawing.Point(25, 9); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(656, 54); + this.groupBox2.TabIndex = 25; + this.groupBox2.TabStop = false; + // + // groupBox3 + // + this.groupBox3.Controls.Add(this.button3); + this.groupBox3.Controls.Add(this.label3); + this.groupBox3.Controls.Add(this.txt_address); + this.groupBox3.Controls.Add(this.button4); + this.groupBox3.Controls.Add(this.txt_value); + this.groupBox3.Controls.Add(this.label4); + this.groupBox3.Location = new System.Drawing.Point(25, 113); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(656, 47); + this.groupBox3.TabIndex = 26; + this.groupBox3.TabStop = false; + // + // ModBusTcpForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(707, 450); + this.Controls.Add(this.groupBox3); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.txt_content); + this.Name = "ModBusTcpForm"; + this.Text = "ModBusTcp"; + this.Load += new System.EventHandler(this.ModBusTcp_Load); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.groupBox3.ResumeLayout(false); + this.groupBox3.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.ToolTip toolTip1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Button but_open; + private System.Windows.Forms.TextBox txt_ip; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox txt_port; + private System.Windows.Forms.RadioButton rd_short; + private System.Windows.Forms.TextBox txt_address; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Button button3; + private System.Windows.Forms.TextBox txt_value; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Button button4; + private System.Windows.Forms.TextBox txt_content; + private System.Windows.Forms.TextBox txt_stationNumber; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.RadioButton rd_bit; + private System.Windows.Forms.RadioButton rd_ushort; + private System.Windows.Forms.RadioButton rd_uint; + private System.Windows.Forms.RadioButton rd_int; + private System.Windows.Forms.RadioButton rd_ulong; + private System.Windows.Forms.RadioButton rd_long; + private System.Windows.Forms.RadioButton rd_double; + private System.Windows.Forms.RadioButton rd_float; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.GroupBox groupBox3; + } +} + diff --git a/IoTClient.Demo/ModBusTcpForm.cs b/IoTClient.Demo/ModBusTcpForm.cs new file mode 100644 index 0000000..81a7397 --- /dev/null +++ b/IoTClient.Demo/ModBusTcpForm.cs @@ -0,0 +1,194 @@ +using IoTClient.Clients.ModBus; +using IoTServer.Servers.ModBus; +using System; +using System.Windows.Forms; + +namespace IoTClient.Demo +{ + public partial class ModBusTcpForm : Form + { + ModBusTcpClient client; + ModBusTcpServer server; + public ModBusTcpForm() + { + InitializeComponent(); + button3.Enabled = false; + button4.Enabled = false; + toolTip1.SetToolTip(button1, "开启本地ModBusTcp服务端仿真模拟服务"); + toolTip1.SetToolTip(but_open, "点击打开连接"); + } + + private void button1_Click(object sender, EventArgs e) + { + if (server == null) + { + server = new ModBusTcpServer("127.0.0.1", 502); + + } + if (button1.Text == "本地模拟服务") + { + server.Start(); + button1.Text = "模拟服务已启动"; + } + else + { + server.Close(); + button1.Text = "本地模拟服务"; + } + } + + private void button2_Click(object sender, EventArgs e) + { + if (but_open.Text == "连接") + { + client?.Close(); + client = new ModBusTcpClient(txt_ip.Text?.Trim(), int.Parse(txt_port.Text?.Trim())); + if (client.Open()) + { + but_open.Text = "已连接"; + button3.Enabled = true; + button4.Enabled = true; + toolTip1.SetToolTip(but_open, "点击关闭连接"); + } + else + { + MessageBox.Show("连接失败"); + } + } + else + { + client?.Close(); + but_open.Text = "连接"; + toolTip1.SetToolTip(but_open, "点击打开连接"); + } + } + + private void button3_Click(object sender, EventArgs e) + { + byte.TryParse(txt_stationNumber.Text?.Trim(), out byte stationNumber); + if (string.IsNullOrWhiteSpace(txt_address.Text)) + { + MessageBox.Show("请输入地址"); + return; + } + 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); + } + + 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"); + } + + private void button4_Click(object sender, EventArgs e) + { + byte.TryParse(txt_stationNumber.Text?.Trim(), out byte stationNumber); + if (string.IsNullOrWhiteSpace(txt_address.Text)) + { + MessageBox.Show("请输入地址"); + return; + } + if (string.IsNullOrWhiteSpace(txt_value.Text)) + { + MessageBox.Show("请输入值"); + return; + } + + dynamic result = null; + if (rd_bit.Checked) + { + if (!bool.TryParse(txt_value.Text?.Trim(), out bool coil)) + { + if (txt_value.Text?.Trim() == "0") + coil = false; + else if (txt_value.Text?.Trim() == "1") + coil = true; + else + { + MessageBox.Show("请输入 True 或 False"); + return; + } + } + result = client.Write(txt_address.Text, coil, stationNumber); + } + else if (rd_short.Checked) + { + result = client.Write(txt_address.Text, short.Parse(txt_value.Text?.Trim()), stationNumber); + } + else if (rd_ushort.Checked) + { + result = client.Write(txt_address.Text, ushort.Parse(txt_value.Text?.Trim()), stationNumber); + } + else if (rd_int.Checked) + { + result = client.Write(txt_address.Text, int.Parse(txt_value.Text?.Trim()), stationNumber); + } + else if (rd_uint.Checked) + { + result = client.Write(txt_address.Text, uint.Parse(txt_value.Text?.Trim()), stationNumber); + } + else if (rd_long.Checked) + { + result = client.Write(txt_address.Text, long.Parse(txt_value.Text?.Trim()), stationNumber); + } + else if (rd_ulong.Checked) + { + result = client.Write(txt_address.Text, ulong.Parse(txt_value.Text?.Trim()), stationNumber); + } + else if (rd_float.Checked) + { + result = client.Write(txt_address.Text, float.Parse(txt_value.Text?.Trim()), stationNumber); + } + else if (rd_double.Checked) + { + result = client.Write(txt_address.Text, double.Parse(txt_value.Text?.Trim()), stationNumber); + } + + + if (result.IsSucceed) + txt_content.AppendText($"[{DateTime.Now.ToLongTimeString()}][写入 {txt_address.Text?.Trim()} 成功]:OK\r\n"); + else + txt_content.AppendText($"[{DateTime.Now.ToLongTimeString()}][写入 {txt_address.Text?.Trim()} 失败]:{result.Err}\r\n"); + } + + private void ModBusTcp_Load(object sender, EventArgs e) + { + + } + } +} diff --git a/IoTClient.Demo/ModBusTcpForm.resx b/IoTClient.Demo/ModBusTcpForm.resx new file mode 100644 index 0000000..df8339b --- /dev/null +++ b/IoTClient.Demo/ModBusTcpForm.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/IoTClient.Demo/Program.cs b/IoTClient.Demo/Program.cs index 28867e4..4e0fadf 100644 --- a/IoTClient.Demo/Program.cs +++ b/IoTClient.Demo/Program.cs @@ -16,7 +16,7 @@ namespace IoTClient.Demo { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new ModBusTcp()); + Application.Run(new ModBusTcpForm()); } } } diff --git a/IoTClient.Tests/ModBus/ModBusTcpClient_tests.cs b/IoTClient.Tests/ModBus/ModBusTcpClient_tests.cs index f0b0436..99cea14 100644 --- a/IoTClient.Tests/ModBus/ModBusTcpClient_tests.cs +++ b/IoTClient.Tests/ModBus/ModBusTcpClient_tests.cs @@ -25,23 +25,23 @@ namespace IoTClient.Tests.ModBus public async Task 短连接自动开关() { short Number = 33; - client.Write(4, Number, stationNumber); + client.Write("4", Number, stationNumber); await Task.Delay(500); - Assert.True(client.ReadInt16(4, stationNumber).Value == Number); + Assert.True(client.ReadInt16("4", stationNumber).Value == Number); Number = 34; - client.Write(4, Number, stationNumber); + client.Write("4", Number, stationNumber); await Task.Delay(500); - Assert.True(client.ReadInt16(4, stationNumber).Value == Number); + Assert.True(client.ReadInt16("4", stationNumber).Value == Number); Number = 1; - client.Write(12, Number, stationNumber); + client.Write("12", Number, stationNumber); await Task.Delay(500); - Assert.True(client.ReadInt16(12, stationNumber).Value == 1); + Assert.True(client.ReadInt16("12", stationNumber).Value == 1); Number = 0; - client.Write(12, Number, stationNumber); + client.Write("12", Number, stationNumber); await Task.Delay(500); - Assert.True(client.ReadInt16(12, stationNumber).Value == 0); + Assert.True(client.ReadInt16("12", stationNumber).Value == 0); } [Fact] @@ -50,23 +50,23 @@ namespace IoTClient.Tests.ModBus client.Open(); short Number = 33; - client.Write(4, Number, stationNumber); + client.Write("4", Number, stationNumber); await Task.Delay(500); - Assert.True(client.ReadInt16(4, stationNumber).Value == Number); + Assert.True(client.ReadInt16("4", stationNumber).Value == Number); Number = 34; - client.Write(4, Number, stationNumber); + client.Write("4", Number, stationNumber); await Task.Delay(500); - Assert.True(client.ReadInt16(4, stationNumber).Value == Number); + Assert.True(client.ReadInt16("4", stationNumber).Value == Number); Number = 1; - client.Write(12, Number, stationNumber); + client.Write("12", Number, stationNumber); await Task.Delay(500); - Assert.True(client.ReadInt16(12, stationNumber).Value == 1); + Assert.True(client.ReadInt16("12", stationNumber).Value == 1); Number = 0; - client.Write(12, Number, stationNumber); + client.Write("12", Number, stationNumber); await Task.Delay(500); - Assert.True(client.ReadInt16(12, stationNumber).Value == 0); + Assert.True(client.ReadInt16("12", stationNumber).Value == 0); client.Close(); } diff --git a/IoTClient/Core/Clients/ModBus/ModBusTcpClient.cs b/IoTClient/Core/Clients/ModBus/ModBusTcpClient.cs index d430419..9191cc7 100644 --- a/IoTClient/Core/Clients/ModBus/ModBusTcpClient.cs +++ b/IoTClient/Core/Clients/ModBus/ModBusTcpClient.cs @@ -28,6 +28,7 @@ namespace IoTClient.Clients.ModBus /// protected override bool Connect() { + socket?.Close(); socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { @@ -65,7 +66,7 @@ namespace IoTClient.Clients.ModBus /// 功能码 /// 读取长度 /// - public Result Read(ushort address, byte stationNumber = 1, byte functionCode = 3, ushort readLength = 1) + public Result Read(string address, byte stationNumber = 1, byte functionCode = 3, ushort readLength = 1) { ConnectManager(); var readResult = new Result(); @@ -113,7 +114,7 @@ namespace IoTClient.Clients.ModBus /// 站号 /// 功能码 /// - public Result ReadInt16(ushort address, byte stationNumber = 1, byte functionCode = 3) + public Result ReadInt16(string address, byte stationNumber = 1, byte functionCode = 3) { var readResut = Read(address, stationNumber, functionCode); var result = new Result() @@ -127,6 +128,27 @@ namespace IoTClient.Clients.ModBus return result; } + /// + /// 读取UInt16 + /// + /// 寄存器起始地址 + /// 站号 + /// 功能码 + /// + public Result ReadUInt16(string address, byte stationNumber = 1, byte functionCode = 3) + { + var readResut = Read(address, stationNumber, functionCode); + var result = new Result() + { + IsSucceed = readResut.IsSucceed, + Err = readResut.Err, + ErrList = readResut.ErrList, + }; + if (result.IsSucceed) + result.Value = BitConverter.ToUInt16(readResut.Value, 0); + return result; + } + /// /// 读取Int32 /// @@ -134,7 +156,7 @@ namespace IoTClient.Clients.ModBus /// 站号 /// 功能码 /// - public Result ReadInt32(ushort address, byte stationNumber = 1, byte functionCode = 3) + public Result ReadInt32(string address, byte stationNumber = 1, byte functionCode = 3) { var readResut = Read(address, stationNumber, functionCode, readLength: 2); var result = new Result() @@ -148,6 +170,27 @@ namespace IoTClient.Clients.ModBus return result; } + /// + /// 读取UInt32 + /// + /// 寄存器起始地址 + /// 站号 + /// 功能码 + /// + public Result ReadUInt32(string address, byte stationNumber = 1, byte functionCode = 3) + { + var readResut = Read(address, stationNumber, functionCode, readLength: 2); + var result = new Result() + { + IsSucceed = readResut.IsSucceed, + Err = readResut.Err, + ErrList = readResut.ErrList, + }; + if (result.IsSucceed) + result.Value = BitConverter.ToUInt32(readResut.Value, 0); + return result; + } + /// /// 读取Int64 /// @@ -155,9 +198,9 @@ namespace IoTClient.Clients.ModBus /// 站号 /// 功能码 /// - public Result ReadInt64(ushort address, byte stationNumber = 1, byte functionCode = 3) + public Result ReadInt64(string address, byte stationNumber = 1, byte functionCode = 3) { - var readResut = Read(address, stationNumber, functionCode, readLength: 2); + var readResut = Read(address, stationNumber, functionCode, readLength: 4); var result = new Result() { IsSucceed = readResut.IsSucceed, @@ -169,6 +212,27 @@ namespace IoTClient.Clients.ModBus return result; } + /// + /// 读取UInt64 + /// + /// 寄存器起始地址 + /// 站号 + /// 功能码 + /// + public Result ReadUInt64(string address, byte stationNumber = 1, byte functionCode = 3) + { + var readResut = Read(address, stationNumber, functionCode, readLength: 4); + var result = new Result() + { + IsSucceed = readResut.IsSucceed, + Err = readResut.Err, + ErrList = readResut.ErrList, + }; + if (result.IsSucceed) + result.Value = BitConverter.ToUInt64(readResut.Value, 0); + return result; + } + /// /// 读取Float /// @@ -176,7 +240,7 @@ namespace IoTClient.Clients.ModBus /// 站号 /// 功能码 /// - public Result ReadFloat(ushort address, byte stationNumber = 1, byte functionCode = 3) + public Result ReadFloat(string address, byte stationNumber = 1, byte functionCode = 3) { var readResut = Read(address, stationNumber, functionCode, readLength: 2); var result = new Result() @@ -197,9 +261,9 @@ namespace IoTClient.Clients.ModBus /// 站号 /// 功能码 /// - public Result ReadDouble(ushort address, byte stationNumber = 1, byte functionCode = 3) + public Result ReadDouble(string address, byte stationNumber = 1, byte functionCode = 3) { - var readResut = Read(address, stationNumber, functionCode, readLength: 2); + var readResut = Read(address, stationNumber, functionCode, readLength: 4); var result = new Result() { IsSucceed = readResut.IsSucceed, @@ -218,7 +282,7 @@ namespace IoTClient.Clients.ModBus /// 站号 /// 功能码 /// - public Result ReadCoil(ushort address, byte stationNumber = 1, byte functionCode = 1) + public Result ReadCoil(string address, byte stationNumber = 1, byte functionCode = 1) { var readResut = Read(address, stationNumber, functionCode); var result = new Result() @@ -234,6 +298,7 @@ namespace IoTClient.Clients.ModBus #endregion #region Write 写入 + /// /// 线圈写入 /// @@ -241,7 +306,7 @@ namespace IoTClient.Clients.ModBus /// /// /// - public Result Write(ushort address, bool value, byte stationNumber = 1, byte functionCode = 5) + public Result Write(string address, bool value, byte stationNumber = 1, byte functionCode = 5) { ConnectManager(); var result = new Result(); @@ -249,6 +314,11 @@ namespace IoTClient.Clients.ModBus { var command = GetWriteCoilCommand(address, value, stationNumber, functionCode); socket.Send(command); + + //获取响应报文 + var headBytes = SocketRead(socket, 8); + int length = headBytes[4] * 256 + headBytes[5] - 2; + SocketRead(socket, length); } catch (SocketException ex) { @@ -274,17 +344,17 @@ namespace IoTClient.Clients.ModBus /// /// 写入 /// - /// 寄存器地址 - /// 写入的值 - /// 站号 - /// 功能码 - public Result Write(ushort address, short value, byte stationNumber = 1, byte functionCode = 16) + /// + /// + /// + /// + /// + public Result Write(string address, byte[] values, byte stationNumber = 1, byte functionCode = 16) { ConnectManager(); var result = new Result(); try { - var values = BitConverter.GetBytes(value).Reverse().ToArray(); var command = GetWriteCommand(address, values, stationNumber, functionCode); socket.Send(command); @@ -321,40 +391,10 @@ namespace IoTClient.Clients.ModBus /// 写入的值 /// 站号 /// 功能码 - public Result Write(ushort address, int value, byte stationNumber = 1, byte functionCode = 16) + public Result Write(string address, short value, byte stationNumber = 1, byte functionCode = 16) { - ConnectManager(); - var result = new Result(); - try - { - var values = BitConverter.GetBytes(value).Reverse().ToArray(); - var command = GetWriteCommand(address, values, stationNumber, functionCode); - socket.Send(command); - - //获取响应报文 - var headBytes = SocketRead(socket, 8); - int length = headBytes[4] * 256 + headBytes[5] - 2; - SocketRead(socket, length); - } - catch (SocketException ex) - { - result.IsSucceed = false; - if (ex.SocketErrorCode == SocketError.TimedOut) - { - result.Err = "连接超时"; - result.ErrList.Add("连接超时"); - } - else - { - result.Err = ex.Message; - result.ErrList.Add(ex.Message); - } - } - finally - { - if (isAutoOpen.Value) Dispose(); - } - return result; + var values = BitConverter.GetBytes(value).Reverse().ToArray(); + return Write(address, values, stationNumber, functionCode); } /// @@ -364,40 +404,10 @@ namespace IoTClient.Clients.ModBus /// 写入的值 /// 站号 /// 功能码 - public Result Write(ushort address, long value, byte stationNumber = 1, byte functionCode = 16) + public Result Write(string address, ushort value, byte stationNumber = 1, byte functionCode = 16) { - ConnectManager(); - var result = new Result(); - try - { - var values = BitConverter.GetBytes(value).Reverse().ToArray(); - var command = GetWriteCommand(address, values, stationNumber, functionCode); - socket.Send(command); - - //获取响应报文 - var headBytes = SocketRead(socket, 8); - int length = headBytes[4] * 256 + headBytes[5] - 2; - SocketRead(socket, length); - } - catch (SocketException ex) - { - result.IsSucceed = false; - if (ex.SocketErrorCode == SocketError.TimedOut) - { - result.Err = "连接超时"; - result.ErrList.Add("连接超时"); - } - else - { - result.Err = ex.Message; - result.ErrList.Add(ex.Message); - } - } - finally - { - if (isAutoOpen.Value) Dispose(); - } - return result; + var values = BitConverter.GetBytes(value).Reverse().ToArray(); + return Write(address, values, stationNumber, functionCode); } /// @@ -407,40 +417,10 @@ namespace IoTClient.Clients.ModBus /// 写入的值 /// 站号 /// 功能码 - public Result Write(ushort address, float value, byte stationNumber = 1, byte functionCode = 16) + public Result Write(string address, int value, byte stationNumber = 1, byte functionCode = 16) { - ConnectManager(); - var result = new Result(); - try - { - var values = BitConverter.GetBytes(value).Reverse().ToArray(); - var command = GetWriteCommand(address, values, stationNumber, functionCode); - socket.Send(command); - - //获取响应报文 - var headBytes = SocketRead(socket, 8); - int length = headBytes[4] * 256 + headBytes[5] - 2; - SocketRead(socket, length); - } - catch (SocketException ex) - { - result.IsSucceed = false; - if (ex.SocketErrorCode == SocketError.TimedOut) - { - result.Err = "连接超时"; - result.ErrList.Add("连接超时"); - } - else - { - result.Err = ex.Message; - result.ErrList.Add(ex.Message); - } - } - finally - { - if (isAutoOpen.Value) Dispose(); - } - return result; + var values = BitConverter.GetBytes(value).Reverse().ToArray(); + return Write(address, values, stationNumber, functionCode); } /// @@ -450,40 +430,62 @@ namespace IoTClient.Clients.ModBus /// 写入的值 /// 站号 /// 功能码 - public Result Write(ushort address, double value, byte stationNumber = 1, byte functionCode = 16) + public Result Write(string address, uint value, byte stationNumber = 1, byte functionCode = 16) { - ConnectManager(); - var result = new Result(); - try - { - var values = BitConverter.GetBytes(value).Reverse().ToArray(); - var command = GetWriteCommand(address, values, stationNumber, functionCode); - socket.Send(command); + var values = BitConverter.GetBytes(value).Reverse().ToArray(); + return Write(address, values, stationNumber, functionCode); + } - //获取响应报文 - var headBytes = SocketRead(socket, 8); - int length = headBytes[4] * 256 + headBytes[5] - 2; - SocketRead(socket, length); - } - catch (SocketException ex) - { - result.IsSucceed = false; - if (ex.SocketErrorCode == SocketError.TimedOut) - { - result.Err = "连接超时"; - result.ErrList.Add("连接超时"); - } - else - { - result.Err = ex.Message; - result.ErrList.Add(ex.Message); - } - } - finally - { - if (isAutoOpen.Value) Dispose(); - } - return result; + /// + /// 写入 + /// + /// 寄存器地址 + /// 写入的值 + /// 站号 + /// 功能码 + public Result Write(string address, long value, byte stationNumber = 1, byte functionCode = 16) + { + var values = BitConverter.GetBytes(value).Reverse().ToArray(); + return Write(address, values, stationNumber, functionCode); + } + + /// + /// 写入 + /// + /// 寄存器地址 + /// 写入的值 + /// 站号 + /// 功能码 + public Result Write(string address, ulong value, byte stationNumber = 1, byte functionCode = 16) + { + var values = BitConverter.GetBytes(value).Reverse().ToArray(); + return Write(address, values, stationNumber, functionCode); + } + + /// + /// 写入 + /// + /// 寄存器地址 + /// 写入的值 + /// 站号 + /// 功能码 + public Result Write(string address, float value, byte stationNumber = 1, byte functionCode = 16) + { + var values = BitConverter.GetBytes(value).Reverse().ToArray(); + return Write(address, values, stationNumber, functionCode); + } + + /// + /// 写入 + /// + /// 寄存器地址 + /// 写入的值 + /// 站号 + /// 功能码 + public Result Write(string address, double value, byte stationNumber = 1, byte functionCode = 16) + { + var values = BitConverter.GetBytes(value).Reverse().ToArray(); + return Write(address, values, stationNumber, functionCode); } #endregion @@ -497,8 +499,9 @@ namespace IoTClient.Clients.ModBus /// 功能码 /// 读取长度 /// - public byte[] GetReadCommand(ushort address, byte stationNumber, byte functionCode, ushort length) + public byte[] GetReadCommand(string address, byte stationNumber, byte functionCode, ushort length) { + var readAddress = ushort.Parse(address?.Trim()); byte[] buffer = new byte[12]; buffer[0] = 0x19; buffer[1] = 0xB2;//Client发出的检验信息 @@ -509,8 +512,8 @@ namespace IoTClient.Clients.ModBus buffer[6] = stationNumber; //站号 buffer[7] = functionCode; //功能码 - buffer[8] = BitConverter.GetBytes(address)[1]; - buffer[9] = BitConverter.GetBytes(address)[0];//寄存器地址 + buffer[8] = BitConverter.GetBytes(readAddress)[1]; + buffer[9] = BitConverter.GetBytes(readAddress)[0];//寄存器地址 buffer[10] = BitConverter.GetBytes(length)[1]; buffer[11] = BitConverter.GetBytes(length)[0];//表示request 寄存器的长度(寄存器个数) return buffer; @@ -524,8 +527,9 @@ namespace IoTClient.Clients.ModBus /// 站号 /// 功能码 /// - public byte[] GetWriteCommand(ushort address, byte[] values, byte stationNumber, byte functionCode) + public byte[] GetWriteCommand(string address, byte[] values, byte stationNumber, byte functionCode) { + var writeAddress = ushort.Parse(address?.Trim()); byte[] buffer = new byte[13 + values.Length]; buffer[0] = 0x19; buffer[1] = 0xB2;//检验信息,用来验证response是否串数据了 @@ -534,8 +538,8 @@ namespace IoTClient.Clients.ModBus buffer[6] = stationNumber; //站号 buffer[7] = functionCode; //功能码 - buffer[8] = BitConverter.GetBytes(address)[1]; - buffer[9] = BitConverter.GetBytes(address)[0];//寄存器地址 + buffer[8] = BitConverter.GetBytes(writeAddress)[1]; + buffer[9] = BitConverter.GetBytes(writeAddress)[0];//寄存器地址 buffer[10] = (byte)(values.Length / 2 / 256); buffer[11] = (byte)(values.Length / 2 % 256);//写寄存器数量(除2是两个字节一个寄存器,寄存器16位。除以256是byte最大存储255。) buffer[12] = (byte)(values.Length); //写字节的个数 @@ -551,8 +555,9 @@ namespace IoTClient.Clients.ModBus /// 站号 /// 功能码 /// - public byte[] GetWriteCoilCommand(ushort address, bool value, byte stationNumber, byte functionCode) + public byte[] GetWriteCoilCommand(string address, bool value, byte stationNumber, byte functionCode) { + var writeAddress = ushort.Parse(address?.Trim()); byte[] buffer = new byte[12]; buffer[0] = 0x19; buffer[1] = 0xB2;//Client发出的检验信息 @@ -561,8 +566,8 @@ namespace IoTClient.Clients.ModBus buffer[6] = stationNumber;//站号 buffer[7] = functionCode; //功能码 - buffer[8] = BitConverter.GetBytes(address)[1]; - buffer[9] = BitConverter.GetBytes(address)[0];//寄存器地址 + buffer[8] = BitConverter.GetBytes(writeAddress)[1]; + buffer[9] = BitConverter.GetBytes(writeAddress)[0];//寄存器地址 buffer[10] = (byte)(value ? 0xFF : 0x00); //此处只可以是FF表示闭合00表示断开,其他数值非法 buffer[11] = 0x00; return buffer; diff --git a/IoTClient/Core/SocketBase.cs b/IoTClient/Core/SocketBase.cs index a1860aa..ef83c61 100644 --- a/IoTClient/Core/SocketBase.cs +++ b/IoTClient/Core/SocketBase.cs @@ -50,7 +50,7 @@ namespace IoTClient.Core { try { - socket?.Shutdown(SocketShutdown.Both);//正常关闭连接 + if (socket.Connected) socket?.Shutdown(SocketShutdown.Both);//正常关闭连接 socket?.Close(); return true; } diff --git a/IoTServer/Servers/ModBus/ModBusTcpServer.cs b/IoTServer/Servers/ModBus/ModBusTcpServer.cs index eeaaf7f..b202288 100644 --- a/IoTServer/Servers/ModBus/ModBusTcpServer.cs +++ b/IoTServer/Servers/ModBus/ModBusTcpServer.cs @@ -11,16 +11,13 @@ namespace IoTServer.Servers.ModBus { private Socket socketServer; private string redisConfig; + private string ip; + private int port; public ModBusTcpServer(string ip, int port, string redisConfig = null) { this.redisConfig = redisConfig; - - //1 创建Socket对象 - socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - - //2 绑定ip和端口 - IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse(ip), port); - socketServer.Bind(ipEndPoint); + this.ip = ip; + this.port = port; } /// @@ -28,12 +25,26 @@ namespace IoTServer.Servers.ModBus /// public void Start() { + //1 创建Socket对象 + socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + + //2 绑定ip和端口 + IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse(ip), port); + socketServer.Bind(ipEndPoint); + //3、开启侦听(等待客户机发出的连接),并设置最大客户端连接数为10 socketServer.Listen(10); Task.Run(() => { Accept(socketServer); }); } + public void Close() + { + if (socketServer?.Connected ?? false) + socketServer.Shutdown(SocketShutdown.Both); + socketServer?.Close(); + } + /// /// 客户端连接到服务端 /// @@ -42,9 +53,18 @@ namespace IoTServer.Servers.ModBus { while (true) { - //阻塞等待客户端连接 - Socket newSocket = socket.Accept(); - Task.Run(() => { Receive(newSocket); }); + try + { + //阻塞等待客户端连接 + Socket newSocket = socket.Accept(); + Task.Run(() => { Receive(newSocket); }); + } + catch (SocketException ex) + { + if (ex.SocketErrorCode != SocketError.Interrupted) + throw ex; + } + } } @@ -84,6 +104,14 @@ namespace IoTServer.Servers.ModBus switch (requetData[7]) { + //读取线圈 + case 1: + //TODO + break; + //写入线圈 + case 5: + //TODO + break; //读取 case 3: { @@ -102,7 +130,8 @@ namespace IoTServer.Servers.ModBus case 16: { //TODO - data.Write(requetData[9], requetData[requetData.Length - 1].ToString()); + var value = requetData[requetData.Length - 2] * 256 + requetData[requetData.Length - 1]; + data.Write(requetData[9], value.ToString()); var responseData = new byte[12]; Buffer.BlockCopy(requetData, 0, responseData, 0, responseData.Length); responseData[5] = 6; @@ -111,10 +140,11 @@ namespace IoTServer.Servers.ModBus break; } } - catch (Exception ex) + catch (SocketException ex) { //todo - throw ex; + if (ex.SocketErrorCode != SocketError.ConnectionRefused) + throw ex; } } }