jesus christ the serial port in sharp is so clunky

This commit is contained in:
nikzori
2024-12-06 16:32:10 +03:00
parent 5716811f6f
commit c26d6084f3
5 changed files with 175 additions and 92 deletions

30
Datasheet.Designer.cs generated
View File

@@ -28,37 +28,43 @@
/// </summary> /// </summary>
private void InitializeComponent() private void InitializeComponent()
{ {
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Datasheet)); 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(); 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.Left)
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
this.listView1.HideSelection = false; this.DGV_Device.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.listView1.Location = new System.Drawing.Point(12, 137); this.DGV_Device.Location = new System.Drawing.Point(13, 60);
this.listView1.Name = "listView1"; this.DGV_Device.MultiSelect = false;
this.listView1.Size = new System.Drawing.Size(716, 301); this.DGV_Device.Name = "DGV_Device";
this.listView1.TabIndex = 0; this.DGV_Device.RowHeadersVisible = false;
this.listView1.UseCompatibleStateImageBehavior = false; this.DGV_Device.Size = new System.Drawing.Size(567, 378);
this.DGV_Device.TabIndex = 0;
// //
// Datasheet // Datasheet
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(740, 450); this.ClientSize = new System.Drawing.Size(592, 450);
this.Controls.Add(this.listView1); this.Controls.Add(this.DGV_Device);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "Datasheet"; this.Name = "Datasheet";
this.Text = "Datasheet"; this.Text = "Datasheet";
((System.ComponentModel.ISupportInitialize)(this.DGV_Device)).EndInit();
this.ResumeLayout(false); this.ResumeLayout(false);
} }
#endregion #endregion
private System.Windows.Forms.ListView listView1; private System.Windows.Forms.DataGridView DGV_Device;
} }
} }

View File

@@ -3,7 +3,9 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Data; using System.Data;
using System.Drawing; using System.Drawing;
using System.IO.Ports;
using System.Linq; using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
@@ -12,30 +14,85 @@ namespace Gidrolock_Modbus_Scanner
{ {
public partial class Datasheet : Form public partial class Datasheet : Form
{ {
int timeout = 3000;
int pollDelay = 250; // delay between each entry poll, ms int pollDelay = 250; // delay between each entry poll, ms
byte slaveID;
Device device = App.device; Device device = App.device;
public Datasheet() List<Entry> entries;
SerialPort port = App.port;
public Datasheet(byte slaveID)
{ {
this.slaveID = slaveID;
entries = device.entries;
InitializeComponent(); InitializeComponent();
listView1.AllowColumnReorder = true;
listView1.CheckBoxes = true;
listView1.FullRowSelect = true;
listView1.GridLines = true;
listView1.Columns.Add("#", -2, HorizontalAlignment.Left); DGV_Device.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
listView1.Columns.Add("Name", -2, HorizontalAlignment.Left); DGV_Device.MultiSelect = false;
listView1.Columns.Add("Value", -2, HorizontalAlignment.Left); DGV_Device.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders;
listView1.Columns.Add("Address", -2, HorizontalAlignment.Left); 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()); DGV_Device.Rows.Add(i.ToString(), entries[i].name, "", entries[i].address);
item.SubItems.Add(device.entries[i].name); }
item.SubItems.Add(" "); Task.Delay(1000).ContinueWith(_ => AutoPollAsync());
item.SubItems.Add(device.entries[i].address.ToString()); }
listView1.Items.Add(item); public async Task<bool> 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<string> 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";
} }
} }
} }

View File

@@ -25,12 +25,12 @@ namespace Gidrolock_Modbus_Scanner
{ {
public string name; public string name;
public RegisterType registerType; public RegisterType registerType;
public int address; public ushort address;
public int length; public ushort length;
public string dataType; public string dataType;
public bool readOnce; 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.name = name;
this.registerType = registerType; this.registerType = registerType;

55
Main.cs
View File

@@ -29,7 +29,7 @@ namespace Gidrolock_Modbus_Scanner
}; };
public static int[] DataBits = new int[] { 7, 8 }; public static int[] DataBits = new int[] { 7, 8 };
Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp); Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
int offset = 0;
byte[] message = new byte[255]; byte[] message = new byte[255];
public bool isAwaitingResponse = false; public bool isAwaitingResponse = false;
public bool isProcessingResponse = false; public bool isProcessingResponse = false;
@@ -38,7 +38,7 @@ namespace Gidrolock_Modbus_Scanner
public int expectedLength = 0; public int expectedLength = 0;
public static Device device; public static Device device;
#region Initialization
public App() public App()
{ {
InitializeComponent(); InitializeComponent();
@@ -123,7 +123,9 @@ namespace Gidrolock_Modbus_Scanner
Button_Connect.Text = "Найти адрес"; Button_Connect.Text = "Найти адрес";
else Button_Connect.Text = "Подключиться"; else Button_Connect.Text = "Подключиться";
} }
#endregion
// Send a custom message
async Task SendMessageAsync(FunctionCode functionCode, ushort address, ushort length) async Task SendMessageAsync(FunctionCode functionCode, ushort address, ushort length)
{ {
if (CBox_Ports.Text == "") if (CBox_Ports.Text == "")
@@ -131,7 +133,7 @@ namespace Gidrolock_Modbus_Scanner
if (UpDown_ModbusID.Value == 0) if (UpDown_ModbusID.Value == 0)
MessageBox.Show("Глобальное вещание пока не поддерживается"); MessageBox.Show("Глобальное вещание пока не поддерживается");
// Port Setup /* - Port Setup - */
if (port.IsOpen) if (port.IsOpen)
port.Close(); port.Close();
@@ -146,21 +148,19 @@ namespace Gidrolock_Modbus_Scanner
port.WriteTimeout = 3000; port.WriteTimeout = 3000;
offset = 0;
message = new byte[255]; message = new byte[255];
port.Open(); port.Open();
/* - Reading from Registers - */ /* - Reading from Registers - */
if (CBox_Function.SelectedIndex < 4) if (CBox_Function.SelectedIndex < 4)
{ {
try 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("Message goes here;");
AddLog("Отправка сообщения: " + messageParsed);
var send = await Modbus.ReadRegAsync(port, (byte)UpDown_ModbusID.Value, functionCode, address, length);
isAwaitingResponse = true; isAwaitingResponse = true;
Task timer = Task.Delay(port.ReadTimeout); Task timer = Task.Delay(port.ReadTimeout);
await timer.ContinueWith(_ => await timer.ContinueWith(_ =>
@@ -188,7 +188,7 @@ namespace Gidrolock_Modbus_Scanner
{ {
byte[] request = new byte[8]; byte[] request = new byte[8];
Modbus.BuildMessage((byte)UpDown_ModbusID.Value, (byte)(1 + functionCode), address, length, ref request); 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); 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("Выберите конфигурацию для подключения и опроса устройства."); MessageBox.Show("Выберите конфигурацию для подключения и опроса устройства.");
else 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); AddLog("Попытка подключиться к устройству " + device.name);
Datasheet datasheet = new Datasheet(); try
{
Datasheet datasheet = new Datasheet((byte)UpDown_ModbusID.Value);
datasheet.Show(); datasheet.Show();
}
catch (Exception err)
{
MessageBox.Show(err.Message);
}
/* /*
if (Radio_SerialPort.Checked) if (Radio_SerialPort.Checked)
await SendMessageAsync(FunctionCode.InputRegister, 200, 6); await SendMessageAsync(FunctionCode.InputRegister, 200, 6);
@@ -241,7 +266,7 @@ namespace Gidrolock_Modbus_Scanner
await socket.ConnectAsync(ipText, portParsed); await socket.ConnectAsync(ipText, portParsed);
byte[] data = new byte[8]; byte[] data = new byte[8];
Modbus.BuildMessage(0x1E, 0x03, 128, 1, ref data); 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 // set up an event listener to receive the response
await SocketDataTransfer(data); await SocketDataTransfer(data);
@@ -277,8 +302,8 @@ namespace Gidrolock_Modbus_Scanner
{ {
data[i] = message[i + 3]; data[i] = message[i + 3];
} }
Console.WriteLine("Data: " + Modbus.ParseByteArray(data)); Console.WriteLine("Data: " + Modbus.ByteArrayToString(data));
string dataCleaned = Modbus.ParseByteArray(message); string dataCleaned = Modbus.ByteArrayToString(message);
TextBox_Log.Invoke((MethodInvoker)delegate { AddLog("Получен ответ: " + dataCleaned); }); TextBox_Log.Invoke((MethodInvoker)delegate { AddLog("Получен ответ: " + dataCleaned); });
TextBox_Log.Invoke((MethodInvoker)delegate { AddLog("ASCII: " + "wip"); }); 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 };

View File

@@ -12,58 +12,30 @@ namespace Gidrolock_Modbus_Scanner
{ {
public static class Modbus 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 #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: //Array to receive CRC bytes:
byte[] CRC = new byte[2]; byte[] CRC = new byte[2];
message[0] = address; message[0] = modbusID;
message[1] = type; message[1] = functionCode;
message[2] = (byte)(start >> 8); message[2] = (byte)(address >> 8);
message[3] = (byte)start; message[3] = (byte)address;
message[4] = (byte)(length >> 8); message[4] = (byte)(length >> 8);
message[5] = (byte)length; message[5] = (byte)length;
GetCRC(message, ref CRC); GetCRC(message, ref CRC);
message[message.Length - 2] = CRC[0]; message[message.Length - 2] = CRC[0];
message[message.Length - 1] = CRC[1]; message[message.Length - 1] = CRC[1];
string msg = BitConverter.ToString(message); string msg = ByteArrayToString(message);
Console.WriteLine("Message: " + msg); Console.WriteLine("Message: " + msg);
return message;
} }
#endregion #endregion
#region Read Functions #region Read Functions
public static async Task<bool> ReadRegAsync(SerialPort port, FunctionCode functionCode, byte address, ushort start, ushort length) public static async Task<bool> ReadRegAsync(SerialPort port, byte slaveID, FunctionCode functionCode, ushort address, ushort length)
{ {
//Ensure port is open: //Ensure port is open:
if (port.IsOpen) if (port.IsOpen)
@@ -76,7 +48,7 @@ namespace Gidrolock_Modbus_Scanner
byte[] message = new byte[8]; byte[] message = new byte[8];
//Build outgoing modbus message: //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: //Send modbus message to Serial Port:
try try
@@ -142,12 +114,7 @@ namespace Gidrolock_Modbus_Scanner
} }
/// <summary> public static string ByteArrayToString(byte[] bytes)
/// Parses a byte array into a string.
/// </summary>
/// <param name="command"></param>
/// <returns>string</returns>
public static string ParseByteArray(byte[] bytes)
{ {
int length = bytes.Length - 1; int length = bytes.Length - 1;
// snip off the empty bytes at the end // snip off the empty bytes at the end
@@ -175,5 +142,33 @@ namespace Gidrolock_Modbus_Scanner
return result; 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
} }
} }