From c26d6084f369ab10cc7e723fca69cd8482d2743d Mon Sep 17 00:00:00 2001 From: nikzori Date: Fri, 6 Dec 2024 16:32:10 +0300 Subject: [PATCH] jesus christ the serial port in sharp is so clunky --- Datasheet.Designer.cs | 30 +++++++++------ Datasheet.cs | 89 +++++++++++++++++++++++++++++++++++-------- Json.cs | 6 +-- Main.cs | 59 +++++++++++++++++++--------- Modbus.cs | 83 +++++++++++++++++++--------------------- 5 files changed, 175 insertions(+), 92 deletions(-) diff --git a/Datasheet.Designer.cs b/Datasheet.Designer.cs index 8dd3f64..644cdb1 100644 --- a/Datasheet.Designer.cs +++ b/Datasheet.Designer.cs @@ -28,37 +28,43 @@ /// private void InitializeComponent() { + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Datasheet)); - this.listView1 = new System.Windows.Forms.ListView(); + this.DGV_Device = new System.Windows.Forms.DataGridView(); + ((System.ComponentModel.ISupportInitialize)(this.DGV_Device)).BeginInit(); this.SuspendLayout(); // - // listView1 + // DGV_Device // - this.listView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.DGV_Device.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle1; + this.DGV_Device.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.listView1.HideSelection = false; - this.listView1.Location = new System.Drawing.Point(12, 137); - this.listView1.Name = "listView1"; - this.listView1.Size = new System.Drawing.Size(716, 301); - this.listView1.TabIndex = 0; - this.listView1.UseCompatibleStateImageBehavior = false; + this.DGV_Device.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.DGV_Device.Location = new System.Drawing.Point(13, 60); + this.DGV_Device.MultiSelect = false; + this.DGV_Device.Name = "DGV_Device"; + this.DGV_Device.RowHeadersVisible = false; + this.DGV_Device.Size = new System.Drawing.Size(567, 378); + this.DGV_Device.TabIndex = 0; // // Datasheet // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(740, 450); - this.Controls.Add(this.listView1); + this.ClientSize = new System.Drawing.Size(592, 450); + this.Controls.Add(this.DGV_Device); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Name = "Datasheet"; this.Text = "Datasheet"; + ((System.ComponentModel.ISupportInitialize)(this.DGV_Device)).EndInit(); this.ResumeLayout(false); } #endregion - private System.Windows.Forms.ListView listView1; + private System.Windows.Forms.DataGridView DGV_Device; } } \ No newline at end of file diff --git a/Datasheet.cs b/Datasheet.cs index 138313b..e954c13 100644 --- a/Datasheet.cs +++ b/Datasheet.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; +using System.IO.Ports; using System.Linq; +using System.Runtime.Remoting.Messaging; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; @@ -12,31 +14,86 @@ namespace Gidrolock_Modbus_Scanner { public partial class Datasheet : Form { + int timeout = 3000; int pollDelay = 250; // delay between each entry poll, ms + byte slaveID; Device device = App.device; - public Datasheet() + List entries; + SerialPort port = App.port; + public Datasheet(byte slaveID) { + this.slaveID = slaveID; + entries = device.entries; + InitializeComponent(); - listView1.AllowColumnReorder = true; - listView1.CheckBoxes = true; - listView1.FullRowSelect = true; - listView1.GridLines = true; - listView1.Columns.Add("#", -2, HorizontalAlignment.Left); - listView1.Columns.Add("Name", -2, HorizontalAlignment.Left); - listView1.Columns.Add("Value", -2, HorizontalAlignment.Left); - listView1.Columns.Add("Address", -2, HorizontalAlignment.Left); + DGV_Device.SelectionMode = DataGridViewSelectionMode.FullRowSelect; + DGV_Device.MultiSelect = false; + DGV_Device.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders; + DGV_Device.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill; + DGV_Device.Columns.Add("#", "#"); + DGV_Device.Columns[0].FillWeight = 20; + DGV_Device.Columns.Add("Name", "Имя"); + DGV_Device.Columns[1].FillWeight = 40; + DGV_Device.Columns.Add("Value", "Значение"); + DGV_Device.Columns[2].FillWeight = 60; + DGV_Device.Columns.Add("Address", "Адрес"); + DGV_Device.Columns[3].FillWeight = 30; + DGV_Device.Columns.Add(new DataGridViewCheckBoxColumn()); + DGV_Device.Columns[4].Name = "Опрос"; + DGV_Device.Columns[4].FillWeight = 20; - for (int i = 0; i < device.entries.Count; i++) + for (int i = 0; i < entries.Count; i++) { - ListViewItem item = new ListViewItem(i.ToString()); - item.SubItems.Add(device.entries[i].name); - item.SubItems.Add(" "); - item.SubItems.Add(device.entries[i].address.ToString()); + DGV_Device.Rows.Add(i.ToString(), entries[i].name, "", entries[i].address); + } + Task.Delay(1000).ContinueWith(_ => AutoPollAsync()); + } - listView1.Items.Add(item); + public async Task AutoPollAsync() + { + port.Open(); + while (true) + { + for (int i = 0; i < entries.Count; i++) + { + if ((bool)DGV_Device.Rows[i].Cells[4].Value) + { + DGV_Device.Rows[i].Cells[2].Value = await PollForEntry(entries[i]); + await Task.Delay(pollDelay); + } + } + } + } + + public async Task PollForEntry(Entry entry) + { + byte[] result = new byte[] { 0xFF }; + try + { + await Modbus.ReadRegAsync(port, slaveID, (FunctionCode)entry.registerType, entry.address, entry.length); + var task = Task.Delay(10).ContinueWith(_ => + { + result = new byte[port.BytesToRead]; + port.Read(result, 0, port.BytesToRead); + }); + + if (await Task.WhenAny(Task.Delay(timeout + 10), task) == task) + { + if (result.Length > 5) + { + return Modbus.ByteArrayToString(result); + } + else return "N/A"; + } + else return "N/A"; + } + catch (Exception err) + { + MessageBox.Show(err.Message); + return "N/A"; } } } -} +} \ No newline at end of file diff --git a/Json.cs b/Json.cs index eaf7733..1d6a2db 100644 --- a/Json.cs +++ b/Json.cs @@ -25,12 +25,12 @@ namespace Gidrolock_Modbus_Scanner { public string name; public RegisterType registerType; - public int address; - public int length; + public ushort address; + public ushort length; public string dataType; public bool readOnce; - public Entry(string name, RegisterType registerType, int address, int length, string dataType, bool readOnce) + public Entry(string name, RegisterType registerType, ushort address, ushort length, string dataType, bool readOnce) { this.name = name; this.registerType = registerType; diff --git a/Main.cs b/Main.cs index effc279..895a8ed 100644 --- a/Main.cs +++ b/Main.cs @@ -29,7 +29,7 @@ namespace Gidrolock_Modbus_Scanner }; public static int[] DataBits = new int[] { 7, 8 }; Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp); - int offset = 0; + byte[] message = new byte[255]; public bool isAwaitingResponse = false; public bool isProcessingResponse = false; @@ -38,7 +38,7 @@ namespace Gidrolock_Modbus_Scanner public int expectedLength = 0; public static Device device; - + #region Initialization public App() { InitializeComponent(); @@ -123,7 +123,9 @@ namespace Gidrolock_Modbus_Scanner Button_Connect.Text = "Найти адрес"; else Button_Connect.Text = "Подключиться"; } + #endregion + // Send a custom message async Task SendMessageAsync(FunctionCode functionCode, ushort address, ushort length) { if (CBox_Ports.Text == "") @@ -131,7 +133,7 @@ namespace Gidrolock_Modbus_Scanner if (UpDown_ModbusID.Value == 0) MessageBox.Show("Глобальное вещание пока не поддерживается"); - // Port Setup + /* - Port Setup - */ if (port.IsOpen) port.Close(); @@ -146,21 +148,19 @@ namespace Gidrolock_Modbus_Scanner port.WriteTimeout = 3000; - offset = 0; message = new byte[255]; port.Open(); + /* - Reading from Registers - */ if (CBox_Function.SelectedIndex < 4) { try - { - byte[] request = new byte[8]; - Modbus.BuildMessage((byte)UpDown_ModbusID.Value, (byte)(1 + functionCode), address, length, ref request); - string messageParsed = Modbus.ParseByteArray(request); + { - var send = await Modbus.ReadRegAsync(port, functionCode, (byte)UpDown_ModbusID.Value, address, length); - AddLog("Отправка сообщения: " + messageParsed); + AddLog("Message goes here;"); + + var send = await Modbus.ReadRegAsync(port, (byte)UpDown_ModbusID.Value, functionCode, address, length); isAwaitingResponse = true; Task timer = Task.Delay(port.ReadTimeout); await timer.ContinueWith(_ => @@ -188,7 +188,7 @@ namespace Gidrolock_Modbus_Scanner { byte[] request = new byte[8]; Modbus.BuildMessage((byte)UpDown_ModbusID.Value, (byte)(1 + functionCode), address, length, ref request); - string messageParsed = Modbus.ParseByteArray(request); + string messageParsed = Modbus.ByteArrayToString(request); var send = await Modbus.WriteSingle(port, functionCode, (byte)UpDown_ModbusID.Value, address, (ushort)UpDown_Value.Value); } @@ -212,9 +212,34 @@ namespace Gidrolock_Modbus_Scanner MessageBox.Show("Выберите конфигурацию для подключения и опроса устройства."); else { + /* - Port Setup - */ + if (port.IsOpen) + port.Close(); + + port.Handshake = Handshake.None; + port.PortName = CBox_Ports.Text; + port.BaudRate = BaudRate[CBox_BaudRate.SelectedIndex]; + port.Parity = Parity.None; + port.DataBits = DataBits[CBox_DataBits.SelectedIndex]; + port.StopBits = (StopBits)CBox_StopBits.SelectedIndex; + + port.ReadTimeout = 3000; + port.WriteTimeout = 3000; + + + message = new byte[255]; + port.Open(); + AddLog("Попытка подключиться к устройству " + device.name); - Datasheet datasheet = new Datasheet(); - datasheet.Show(); + try + { + Datasheet datasheet = new Datasheet((byte)UpDown_ModbusID.Value); + datasheet.Show(); + } + catch (Exception err) + { + MessageBox.Show(err.Message); + } /* if (Radio_SerialPort.Checked) await SendMessageAsync(FunctionCode.InputRegister, 200, 6); @@ -241,7 +266,7 @@ namespace Gidrolock_Modbus_Scanner await socket.ConnectAsync(ipText, portParsed); byte[] data = new byte[8]; Modbus.BuildMessage(0x1E, 0x03, 128, 1, ref data); - AddLog("Sending to " + ipText + ":" + portText + ":" + Modbus.ParseByteArray(data)); + AddLog("Sending to " + ipText + ":" + portText + ":" + Modbus.ByteArrayToString(data)); // set up an event listener to receive the response await SocketDataTransfer(data); @@ -277,8 +302,8 @@ namespace Gidrolock_Modbus_Scanner { data[i] = message[i + 3]; } - Console.WriteLine("Data: " + Modbus.ParseByteArray(data)); - string dataCleaned = Modbus.ParseByteArray(message); + Console.WriteLine("Data: " + Modbus.ByteArrayToString(data)); + string dataCleaned = Modbus.ByteArrayToString(message); TextBox_Log.Invoke((MethodInvoker)delegate { AddLog("Получен ответ: " + dataCleaned); }); TextBox_Log.Invoke((MethodInvoker)delegate { AddLog("ASCII: " + "wip"); }); @@ -403,4 +428,4 @@ namespace Gidrolock_Modbus_Scanner } } -public enum FunctionCode { Coil, DiscreteInput, HoldingRegister, InputRegister, WriteCoil, WriteRegister, WriteMultCoils, WriteMultRegisters }; +public enum FunctionCode { ReadCoil, ReadDiscrete, ReadHolding, ReadInput, WriteCoil, WriteRegister, WriteMultCoils, WriteMultRegisters }; diff --git a/Modbus.cs b/Modbus.cs index 4fcac1c..58ba6c4 100644 --- a/Modbus.cs +++ b/Modbus.cs @@ -12,58 +12,30 @@ namespace Gidrolock_Modbus_Scanner { public static class Modbus { - - #region CRC Computation - static void GetCRC(byte[] message, ref byte[] CRC) - { - //Function expects a modbus message of any length as well as a 2 byte CRC array in which to - //return the CRC values: - - ushort CRCFull = 0xFFFF; - byte CRCHigh = 0xFF, CRCLow = 0xFF; - char CRCLSB; - - for (int i = 0; i < (message.Length) - 2; i++) - { - CRCFull = (ushort)(CRCFull ^ message[i]); - - for (int j = 0; j < 8; j++) - { - CRCLSB = (char)(CRCFull & 0x0001); - CRCFull = (ushort)((CRCFull >> 1) & 0x7FFF); - - if (CRCLSB == 1) - CRCFull = (ushort)(CRCFull ^ 0xA001); - } - } - CRC[1] = CRCHigh = (byte)((CRCFull >> 8) & 0xFF); - CRC[0] = CRCLow = (byte)(CRCFull & 0xFF); - } - #endregion - #region Build Message - public static void BuildMessage(byte address, byte type, ushort start, ushort length, ref byte[] message) + public static byte[] BuildMessage(byte modbusID, byte functionCode, ushort address, ushort length, ref byte[] message) { //Array to receive CRC bytes: byte[] CRC = new byte[2]; - message[0] = address; - message[1] = type; - message[2] = (byte)(start >> 8); - message[3] = (byte)start; + message[0] = modbusID; + message[1] = functionCode; + message[2] = (byte)(address >> 8); + message[3] = (byte)address; message[4] = (byte)(length >> 8); message[5] = (byte)length; GetCRC(message, ref CRC); message[message.Length - 2] = CRC[0]; message[message.Length - 1] = CRC[1]; - string msg = BitConverter.ToString(message); + string msg = ByteArrayToString(message); Console.WriteLine("Message: " + msg); + return message; } #endregion #region Read Functions - public static async Task ReadRegAsync(SerialPort port, FunctionCode functionCode, byte address, ushort start, ushort length) + public static async Task ReadRegAsync(SerialPort port, byte slaveID, FunctionCode functionCode, ushort address, ushort length) { //Ensure port is open: if (port.IsOpen) @@ -76,7 +48,7 @@ namespace Gidrolock_Modbus_Scanner byte[] message = new byte[8]; //Build outgoing modbus message: - BuildMessage(address, (byte)(1 + (int)functionCode), start, length, ref message); + BuildMessage(slaveID, (byte)(1 + (int)functionCode), address, length, ref message); //Send modbus message to Serial Port: try @@ -142,12 +114,7 @@ namespace Gidrolock_Modbus_Scanner } - /// - /// Parses a byte array into a string. - /// - /// - /// string - public static string ParseByteArray(byte[] bytes) + public static string ByteArrayToString(byte[] bytes) { int length = bytes.Length - 1; // snip off the empty bytes at the end @@ -175,5 +142,33 @@ namespace Gidrolock_Modbus_Scanner return result; } + + #region CRC Computation + static void GetCRC(byte[] message, ref byte[] CRC) + { + //Function expects a modbus message of any length as well as a 2 byte CRC array in which to + //return the CRC values: + + ushort CRCFull = 0xFFFF; + byte CRCHigh = 0xFF, CRCLow = 0xFF; + char CRCLSB; + + for (int i = 0; i < (message.Length) - 2; i++) + { + CRCFull = (ushort)(CRCFull ^ message[i]); + + for (int j = 0; j < 8; j++) + { + CRCLSB = (char)(CRCFull & 0x0001); + CRCFull = (ushort)((CRCFull >> 1) & 0x7FFF); + + if (CRCLSB == 1) + CRCFull = (ushort)(CRCFull ^ 0xA001); + } + } + CRC[1] = CRCHigh = (byte)((CRCFull >> 8) & 0xFF); + CRC[0] = CRCLow = (byte)(CRCFull & 0xFF); + } + #endregion } -} +} \ No newline at end of file