diff --git a/Form1.Designer.cs b/Form1.Designer.cs index cb689e6..9906083 100644 --- a/Form1.Designer.cs +++ b/Form1.Designer.cs @@ -37,11 +37,28 @@ this.panel1 = new System.Windows.Forms.Panel(); this.CBox_Ports = new System.Windows.Forms.ComboBox(); this.label1 = new System.Windows.Forms.Label(); - this.Label_Status = new System.Windows.Forms.Label(); + this.TextBox_Log = new System.Windows.Forms.TextBox(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.panel5 = new System.Windows.Forms.Panel(); + this.UpDown_RegLength = new System.Windows.Forms.NumericUpDown(); + this.label5 = new System.Windows.Forms.Label(); + this.Button_SendCommand = new System.Windows.Forms.Button(); + this.panel3 = new System.Windows.Forms.Panel(); + this.UpDown_RegAddress = new System.Windows.Forms.NumericUpDown(); + this.label3 = new System.Windows.Forms.Label(); + this.panel4 = new System.Windows.Forms.Panel(); + this.CBox_Function = new System.Windows.Forms.ComboBox(); + this.label4 = new System.Windows.Forms.Label(); this.groupBox1.SuspendLayout(); this.panel2.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.UpDown_ModbusID)).BeginInit(); this.panel1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.panel5.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.UpDown_RegLength)).BeginInit(); + this.panel3.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.UpDown_RegAddress)).BeginInit(); + this.panel4.SuspendLayout(); this.SuspendLayout(); // // groupBox1 @@ -51,7 +68,7 @@ this.groupBox1.Controls.Add(this.panel1); this.groupBox1.Location = new System.Drawing.Point(12, 12); this.groupBox1.Name = "groupBox1"; - this.groupBox1.Size = new System.Drawing.Size(310, 63); + this.groupBox1.Size = new System.Drawing.Size(344, 63); this.groupBox1.TabIndex = 0; this.groupBox1.TabStop = false; this.groupBox1.Text = "Подключение"; @@ -60,7 +77,7 @@ // this.ButtonConnect.Location = new System.Drawing.Point(162, 34); this.ButtonConnect.Name = "ButtonConnect"; - this.ButtonConnect.Size = new System.Drawing.Size(146, 23); + this.ButtonConnect.Size = new System.Drawing.Size(176, 23); this.ButtonConnect.TabIndex = 4; this.ButtonConnect.Text = "Подключиться"; this.ButtonConnect.UseVisualStyleBackColor = true; @@ -119,23 +136,126 @@ this.label1.TabIndex = 0; this.label1.Text = "Порт"; // - // Label_Status + // TextBox_Log // - this.Label_Status.Location = new System.Drawing.Point(15, 87); - this.Label_Status.Name = "Label_Status"; - this.Label_Status.Size = new System.Drawing.Size(307, 40); - this.Label_Status.TabIndex = 5; - this.Label_Status.Text = "Статус: истекло время ожидания ответа. Проверьте указаный порт, Modbus ID и стату" + - "с устройства."; + this.TextBox_Log.Location = new System.Drawing.Point(12, 82); + this.TextBox_Log.Multiline = true; + this.TextBox_Log.Name = "TextBox_Log"; + this.TextBox_Log.ReadOnly = true; + this.TextBox_Log.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.TextBox_Log.Size = new System.Drawing.Size(417, 87); + this.TextBox_Log.TabIndex = 1; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.panel5); + this.groupBox2.Controls.Add(this.Button_SendCommand); + this.groupBox2.Controls.Add(this.panel3); + this.groupBox2.Controls.Add(this.panel4); + this.groupBox2.Location = new System.Drawing.Point(12, 175); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(417, 63); + this.groupBox2.TabIndex = 5; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Команды"; + // + // panel5 + // + this.panel5.Controls.Add(this.UpDown_RegLength); + this.panel5.Controls.Add(this.label5); + this.panel5.Location = new System.Drawing.Point(259, 20); + this.panel5.Name = "panel5"; + this.panel5.Size = new System.Drawing.Size(75, 43); + this.panel5.TabIndex = 3; + // + // UpDown_RegLength + // + this.UpDown_RegLength.Location = new System.Drawing.Point(6, 17); + this.UpDown_RegLength.Name = "UpDown_RegLength"; + this.UpDown_RegLength.Size = new System.Drawing.Size(66, 20); + this.UpDown_RegLength.TabIndex = 1; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(3, 0); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(40, 13); + this.label5.TabIndex = 0; + this.label5.Text = "Длина"; + // + // Button_SendCommand + // + this.Button_SendCommand.Location = new System.Drawing.Point(337, 35); + this.Button_SendCommand.Name = "Button_SendCommand"; + this.Button_SendCommand.Size = new System.Drawing.Size(71, 23); + this.Button_SendCommand.TabIndex = 4; + this.Button_SendCommand.Text = "Отправить"; + this.Button_SendCommand.UseVisualStyleBackColor = true; + this.Button_SendCommand.Click += new System.EventHandler(this.Button_SendCommand_Click); + // + // panel3 + // + this.panel3.Controls.Add(this.UpDown_RegAddress); + this.panel3.Controls.Add(this.label3); + this.panel3.Location = new System.Drawing.Point(162, 20); + this.panel3.Name = "panel3"; + this.panel3.Size = new System.Drawing.Size(91, 43); + this.panel3.TabIndex = 2; + // + // UpDown_RegAddress + // + this.UpDown_RegAddress.Location = new System.Drawing.Point(6, 17); + this.UpDown_RegAddress.Name = "UpDown_RegAddress"; + this.UpDown_RegAddress.Size = new System.Drawing.Size(82, 20); + this.UpDown_RegAddress.TabIndex = 1; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(3, 0); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(38, 13); + this.label3.TabIndex = 0; + this.label3.Text = "Адрес"; + // + // panel4 + // + this.panel4.Controls.Add(this.CBox_Function); + this.panel4.Controls.Add(this.label4); + this.panel4.Location = new System.Drawing.Point(0, 20); + this.panel4.Name = "panel4"; + this.panel4.Size = new System.Drawing.Size(156, 43); + this.panel4.TabIndex = 0; + // + // CBox_Function + // + this.CBox_Function.FormattingEnabled = true; + this.CBox_Function.Location = new System.Drawing.Point(6, 17); + this.CBox_Function.Name = "CBox_Function"; + this.CBox_Function.Size = new System.Drawing.Size(147, 21); + this.CBox_Function.TabIndex = 1; + this.CBox_Function.Text = "01 - Read Coil"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(3, 0); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(53, 13); + this.label4.TabIndex = 0; + this.label4.Text = "Функция"; // // App // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(333, 84); - this.Controls.Add(this.Label_Status); + this.ClientSize = new System.Drawing.Size(432, 242); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.TextBox_Log); this.Controls.Add(this.groupBox1); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MaximizeBox = false; this.Name = "App"; this.Text = "Gidrolock Modbus Scanner"; this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.App_FormClosed); @@ -146,7 +266,17 @@ ((System.ComponentModel.ISupportInitialize)(this.UpDown_ModbusID)).EndInit(); this.panel1.ResumeLayout(false); this.panel1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.panel5.ResumeLayout(false); + this.panel5.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.UpDown_RegLength)).EndInit(); + this.panel3.ResumeLayout(false); + this.panel3.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.UpDown_RegAddress)).EndInit(); + this.panel4.ResumeLayout(false); + this.panel4.PerformLayout(); this.ResumeLayout(false); + this.PerformLayout(); } @@ -160,7 +290,18 @@ private System.Windows.Forms.ComboBox CBox_Ports; private System.Windows.Forms.Label label1; private System.Windows.Forms.Button ButtonConnect; - private System.Windows.Forms.Label Label_Status; + private System.Windows.Forms.TextBox TextBox_Log; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.Panel panel5; + private System.Windows.Forms.NumericUpDown UpDown_RegLength; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Button Button_SendCommand; + private System.Windows.Forms.Panel panel3; + private System.Windows.Forms.NumericUpDown UpDown_RegAddress; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Panel panel4; + private System.Windows.Forms.ComboBox CBox_Function; + private System.Windows.Forms.Label label4; } } diff --git a/Form1.cs b/Form1.cs index be2e7a6..b044d80 100644 --- a/Form1.cs +++ b/Form1.cs @@ -17,15 +17,28 @@ namespace Gidrolock_Modbus_Scanner { public partial class App : Form { + int offset = 0; + byte[] data = new byte[255]; public bool isAwaitingResponse = false; public short[] res = new short[12]; public SerialPort port = new SerialPort(); + public int expectedLength = 0; public App() { InitializeComponent(); this.port.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(PortDataReceived); this.UpDown_ModbusID.Value = 30; + TextBox_Log.Text = "Приложение готово к работе."; + + CBox_Function.Items.Add("01 - Read Coil"); + CBox_Function.Items.Add("02 - Read Discrete Input"); + CBox_Function.Items.Add("03 - Read Holding Register"); + CBox_Function.Items.Add("04 - Read Input Register"); + CBox_Function.SelectedItem = CBox_Function.Items[0]; + + UpDown_RegAddress.Minimum = 0; + UpDown_RegAddress.Maximum = 65536; } void App_FormClosed(object sender, FormClosedEventArgs e) { @@ -49,13 +62,14 @@ namespace Gidrolock_Modbus_Scanner else ButtonConnect.Text = "Подключиться"; } - private async void ButtonConnect_Click(object sender, EventArgs e) - { + async Task SendMessageAsync(FunctionCode functionCode, ushort address, ushort length) + { if (CBox_Ports.Text == "") MessageBox.Show("Необходимо выбрать COM порт.", "Ошибка", MessageBoxButtons.OK); if (UpDown_ModbusID.Value == 0) MessageBox.Show("Глобальное вещание пока не поддерживается"); - try + + try { if (port.IsOpen) port.Close(); @@ -67,19 +81,29 @@ namespace Gidrolock_Modbus_Scanner port.DataBits = 8; port.StopBits = StopBits.One; - port.ReadTimeout = 10; - port.WriteTimeout = 10; + port.ReadTimeout = 1000; + port.WriteTimeout = 1000; - + offset = 0; + data = new byte[255]; port.Open(); - var send = await Modbus.ReadRegAsync(port, (byte)FunctionCode.HoldingRegister, (byte)UpDown_ModbusID.Value, 128, 1); + + byte[] message = new byte[8]; + Modbus.BuildMessage((byte)UpDown_ModbusID.Value, (byte)(1 + functionCode), address, length, ref message); + string messageParsed = Modbus.ParseByteArray(message); + + var send = await Modbus.ReadRegAsync(port, functionCode, (byte)UpDown_ModbusID.Value, address, length); + AddLog("Отправка сообщения: " + messageParsed); isAwaitingResponse = true; Task timer = Task.Delay(2000); - await timer.ContinueWith( _ => + await timer.ContinueWith(_ => { if (isAwaitingResponse) + { MessageBox.Show("Истекло время ожидания ответа.", "Ошибка"); + port.Close(); + } }); } @@ -90,6 +114,11 @@ namespace Gidrolock_Modbus_Scanner } } + private async void ButtonConnect_Click(object sender, EventArgs e) + { + await SendMessageAsync(FunctionCode.HoldingRegister, 128, 1); + } + void CBox_Ports_Click(object sender, EventArgs e) { CBox_Ports.Items.Clear(); @@ -100,40 +129,37 @@ namespace Gidrolock_Modbus_Scanner { Console.WriteLine("Data receieved on Serial Port"); isAwaitingResponse = false; - int len; - byte rdxLen = 0; - try { - len = port.BytesToRead; - byte[] data = new byte[len]; - port.Read(data, rdxLen, len); - rdxLen += (byte)len; - int lastByte = len - 1; - for (int i = len - 1; i >= 0; i--) - { - if (data[i] != 0) - { - lastByte = i; - break; - } - } - string dataString = BitConverter.ToString(data); - string dataCleaned = ""; - for (int i = 0; i < dataString.Length; i++) - { - if (dataString[i] == '-') - dataCleaned += " "; - else dataCleaned += dataString[i]; - } - MessageBox.Show("Получен ответ от устройства: " + dataCleaned, "Успех", MessageBoxButtons.OK); + int len = port.BytesToRead; + Console.WriteLine("Data length: " + len); + port.Read(data, offset, len); + offset += len; + string dataCleaned = Modbus.ParseByteArray(data); + + TextBox_Log.Invoke((MethodInvoker)delegate { AddLog("Получен ответ: " + dataCleaned); }); + //MessageBox.Show("Получен ответ от устройства: " + dataCleaned, "Успех", MessageBoxButtons.OK); } catch (Exception err) { MessageBox.Show(err.Message); } + //port.Close(); - port.Close(); + } + + void AddLog(string message) + { + TextBox_Log.AppendText(Environment.NewLine + message); + } + + private async void Button_SendCommand_Click(object sender, EventArgs e) + { + FunctionCode functionCode = (FunctionCode)CBox_Function.SelectedIndex; + ushort address = (ushort)UpDown_RegAddress.Value; + ushort length = (ushort)UpDown_RegLength.Value; + + await SendMessageAsync(functionCode, address, length); } } } diff --git a/Gidrolock Modbus Scanner.csproj b/Gidrolock Modbus Scanner.csproj index d599bfd..fa5836e 100644 --- a/Gidrolock Modbus Scanner.csproj +++ b/Gidrolock Modbus Scanner.csproj @@ -83,6 +83,7 @@ True Resources.resx + SettingsSingleFileGenerator Settings.Designer.cs @@ -92,6 +93,7 @@ Settings.settings True + diff --git a/Modbus.cs b/Modbus.cs index 583fe8b..8d94ac4 100644 --- a/Modbus.cs +++ b/Modbus.cs @@ -42,7 +42,7 @@ namespace Gidrolock_Modbus_Scanner #endregion #region Build Message - static void BuildMessage(byte address, byte type, ushort start, ushort registers, ref byte[] message) + public static void BuildMessage(byte address, byte type, ushort start, ushort length, ref byte[] message) { //Array to receive CRC bytes: byte[] CRC = new byte[2]; @@ -51,8 +51,8 @@ namespace Gidrolock_Modbus_Scanner message[1] = type; message[2] = (byte)(start >> 8); message[3] = (byte)start; - message[4] = (byte)(registers >> 8); - message[5] = (byte)registers; + message[4] = (byte)(length >> 8); + message[5] = (byte)length; GetCRC(message, ref CRC); message[message.Length - 2] = CRC[0]; @@ -62,51 +62,22 @@ namespace Gidrolock_Modbus_Scanner } #endregion - #region Check Response - static bool CheckResponse(byte[] response) - { - //Perform a basic CRC check: - byte[] CRC = new byte[2]; - GetCRC(response, ref CRC); - if (CRC[0] == response[response.Length - 2] && CRC[1] == response[response.Length - 1]) - return true; - else - return false; - } - #endregion - - #region Get Response - static void GetResponse(SerialPort port, ref byte[] response) - { - Console.WriteLine("Got response from port"); - if (port.BytesToRead == 0) - return; - //There is a bug in .Net 2.0 DataReceived Event that prevents people from using this - //event as an interrupt to handle data (it doesn't fire all of the time). Therefore - //we have to use the ReadByte command for a fixed length as it's been shown to be reliable. - for (int i = 0; i < response.Length; i++) - { - response[i] = (byte)(port.ReadByte()); - } - } - #endregion - #region Function 3 - Read Holding Registers - public static async Task ReadRegAsync(SerialPort port, byte regType, byte address, ushort start, ushort registers) + public static async Task ReadRegAsync(SerialPort port, FunctionCode functionCode, byte address, ushort start, ushort length) { - short[] values = new short[8]; //Ensure port is open: if (port.IsOpen) { //Clear in/out buffers: port.DiscardOutBuffer(); port.DiscardInBuffer(); - //Function 3 request is always 8 bytes: + + //Read functions are always 8 bytes long byte[] message = new byte[8]; - //Function 3 response buffer: - byte[] response = new byte[5 + 2 * registers]; + //Build outgoing modbus message: - BuildMessage(address, (byte)3, start, registers, ref message); + BuildMessage(address, (byte)(1 + (int)functionCode), start, length, ref message); + //Send modbus message to Serial Port: try { @@ -129,6 +100,39 @@ namespace Gidrolock_Modbus_Scanner } #endregion + /// + /// Parses a byte array into a string. + /// + /// + /// string + public static string ParseByteArray(byte[] bytes) + { + int length = bytes.Length - 1; + // snip off the empty bytes at the end + for (int i = length; i >= 0; i--) + { + if (bytes[i] != 0) + { + length = i + 1; + break; + } + } + + byte[] res = new byte[length]; + for (int i = 0; i < length; i++) + res[i] = bytes[i]; + + string dataString = BitConverter.ToString(res); + string result = ""; + for (int i = 0; i < dataString.Length; i++) + { + if (dataString[i] == '-') + result += " "; + else result += dataString[i]; + } + + return result; + } } }