Files
gidrolock-configurator/Modbus.cs

300 lines
10 KiB
C#
Raw Normal View History

2024-11-05 10:31:38 +03:00
using System;
using System.IO.Ports;
using System.Runtime.Remoting.Messaging;
2024-11-05 10:31:38 +03:00
using System.Text;
using System.Threading;
2024-11-05 10:31:38 +03:00
using System.Threading.Tasks;
using System.Windows.Forms;
2024-12-16 11:35:20 +03:00
using static System.Net.Mime.MediaTypeNames;
2024-11-05 10:31:38 +03:00
namespace Gidrolock_Modbus_Scanner
{
public static class Modbus
{
public static SerialPort port = new SerialPort();
public static event EventHandler<ModbusResponseEventArgs> ResponseReceived = delegate { };
public static void Init()
{
port.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(PortDataReceived);
}
2024-11-05 10:31:38 +03:00
#region Build Message
2024-12-17 16:36:42 +03:00
public static byte[] BuildReadMessage(byte modbusID, byte functionCode, ushort address, ushort length, ref byte[] message)
2024-11-05 10:31:38 +03:00
{
//Array to receive CRC bytes:
byte[] CRC = new byte[2];
message[0] = modbusID;
message[1] = functionCode;
message[2] = (byte)(address >> 8);
message[3] = (byte)address;
2024-11-05 16:57:05 +03:00
message[4] = (byte)(length >> 8);
message[5] = (byte)length;
2024-11-05 10:31:38 +03:00
GetCRC(message, ref CRC);
message[message.Length - 2] = CRC[0];
message[message.Length - 1] = CRC[1];
string msg = ByteArrayToString(message);
2024-12-09 16:24:02 +03:00
//Console.WriteLine("Message: " + msg);
return message;
2024-11-05 10:31:38 +03:00
}
2024-12-17 16:36:42 +03:00
public static byte[] BuildWriteSingleMessage(byte modbusID, byte functionCode, ushort address, byte[] data)
{
if (functionCode == 0x05 || functionCode == 0x06)
{
byte[] _message = new byte[8];
byte[] CRC = new byte[2];
_message[0] = modbusID;
_message[1] = functionCode;
_message[2] = (byte)(address >> 8);
_message[3] = (byte)address;
_message[4] = data[0];
_message[5] = data[1];
GetCRC(_message, ref CRC);
_message[6] = CRC[0];
_message[7] = CRC[1];
return _message;
}
else return new byte[1] { 0xFF };
}
2024-11-05 10:31:38 +03:00
#endregion
2024-11-11 10:43:58 +03:00
#region Read Functions
public static bool ReadRegAsync(SerialPort port, byte slaveID, FunctionCode functionCode, ushort address, ushort length)
2024-11-05 10:31:38 +03:00
{
//Ensure port is open:
if (port.IsOpen)
{
//Clear in/out buffers:
port.DiscardOutBuffer();
port.DiscardInBuffer();
2024-11-05 16:57:05 +03:00
//Read functions are always 8 bytes long
2024-11-05 10:31:38 +03:00
byte[] message = new byte[8];
2024-12-17 16:36:42 +03:00
2024-11-05 10:31:38 +03:00
//Build outgoing modbus message:
2024-12-17 16:36:42 +03:00
BuildReadMessage(slaveID, (byte)functionCode, address, length, ref message);
2024-12-18 14:28:06 +03:00
if (message.Length > 1)
2024-11-05 10:31:38 +03:00
{
//Send modbus message to Serial Port:
try
{
port.Write(message, 0, message.Length);
return true;
}
catch (Exception err)
{
MessageBox.Show(err.Message, "aeiou");
port.Close();
return false;
}
2024-11-05 10:31:38 +03:00
}
else return false;
2024-11-05 10:31:38 +03:00
}
else
{
MessageBox.Show("Порт не открыт");
return false;
}
}
#endregion
2024-12-17 16:36:42 +03:00
2024-11-11 10:43:58 +03:00
#region Write Single Coil/Register
public static bool WriteSingleAsync(SerialPort port, FunctionCode functionCode, byte slaveID, ushort address, ushort value)
2024-11-11 10:43:58 +03:00
{
2024-12-17 16:36:42 +03:00
//Ensure port is open:
if (!port.IsOpen)
{
try
{
port.Open();
}
catch (Exception err)
{
MessageBox.Show(err.Message);
return false;
}
}
//Clear in/out buffers:
port.DiscardOutBuffer();
port.DiscardInBuffer();
//Build outgoing modbus message:
byte[] _value = BitConverter.GetBytes(value);
Array.Reverse(_value);
byte[] message = BuildWriteSingleMessage(slaveID, (byte)functionCode, address, _value);
2024-12-18 14:28:06 +03:00
Console.WriteLine("Write message: " + ByteArrayToString(message));
2024-12-17 16:36:42 +03:00
//Send modbus message to Serial Port:
try
{
port.Write(message, 0, message.Length);
Console.WriteLine("Message sent successfully.");
2024-12-17 16:36:42 +03:00
return true;
}
catch (Exception err)
{
MessageBox.Show(err.Message);
port.Close();
return false;
}
2024-12-17 15:56:05 +03:00
2024-11-11 10:43:58 +03:00
}
2024-11-11 10:45:55 +03:00
#endregion
2024-11-11 10:43:58 +03:00
public static bool ParseResponse(byte[] res, ref string verbose)
{
try
{
int dataLength = (int)res[2];
if (res.Length < dataLength + 4)
{
verbose = "Сообщение устройства не соответствует ожидаемой длине!";
return false;
}
//TODO: Check CRC
return true;
}
catch
{
verbose = "Сообщение устройства не соответствует ожидаемой длине!";
return false;
}
2024-12-17 16:36:42 +03:00
2024-11-11 10:43:58 +03:00
}
2024-12-17 16:36:42 +03:00
public static string ByteArrayToString(byte[] bytes, bool cleanEmpty = true)
{
2024-12-17 16:36:42 +03:00
byte[] res;
if (cleanEmpty)
res = CleanByteArray(bytes);
else res = bytes;
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;
}
public static byte[] CleanByteArray(byte[] bytes)
2024-11-05 16:57:05 +03:00
{
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]; }
2024-11-05 16:57:05 +03:00
return res;
2024-11-05 16:57:05 +03:00
}
#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
static void PortDataReceived(object sender, EventArgs e)
{
try
{
Thread.Sleep(50);
byte[] message = new byte[port.BytesToRead];
//Console.WriteLine("Bytes to read:" + port.BytesToRead);
port.Read(message, 0, port.BytesToRead);
//Console.WriteLine("Incoming message: " + ByteArrayToString(message, false));
if (message[1] <= 0x04) // read functions
{
//Console.WriteLine("It's a read message");
ResponseReceived.Invoke(null, new ModbusResponseEventArgs(message, ModbusStatus.ReadSuccess));
}
else
{
if (message[1] <= 0x10) // write functions
{
//Console.WriteLine("It's a write message");
ResponseReceived.Invoke(null, new ModbusResponseEventArgs(message, ModbusStatus.WriteSuccess));
}
else // error codes
{
//Console.WriteLine("It's an error");
ResponseReceived.Invoke(null, new ModbusResponseEventArgs(message, ModbusStatus.Error));
}
}
}
catch (Exception err)
{
MessageBox.Show(err.Message, "Modbus message reception error");
}
port.DiscardInBuffer();
}
2024-11-05 10:31:38 +03:00
}
public class ModbusResponseEventArgs : EventArgs
{
2024-12-16 11:35:20 +03:00
public byte[] Message { get; set; }
public byte[] Data { get; set; }
public ModbusStatus Status { get; set; }
public ModbusResponseEventArgs(byte[] message, ModbusStatus status)
{
2024-12-16 11:35:20 +03:00
this.Message = message;
this.Status = status;
if (status == ModbusStatus.ReadSuccess)
{
int dataLength = message[2];
Data = new byte[dataLength];
for (int i = 0; i < dataLength; i++)
{
Data[i] = message[i + 3];
}
//Console.WriteLine("Read data: " + Modbus.ByteArrayToString(Data, false));
}
else Data = new byte[1] {0x0F};
}
}
public enum ModbusStatus { ReadSuccess, WriteSuccess, Error };
}