135 lines
4.5 KiB
C#
135 lines
4.5 KiB
C#
using System;
|
|
using System.IO.Ports;
|
|
using System.Runtime;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
using System.Threading;
|
|
|
|
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
|
|
static void BuildMessage(byte address, byte type, ushort start, ushort registers, 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[4] = (byte)(registers >> 8);
|
|
message[5] = (byte)registers;
|
|
|
|
GetCRC(message, ref CRC);
|
|
message[message.Length - 2] = CRC[0];
|
|
message[message.Length - 1] = CRC[1];
|
|
string msg = BitConverter.ToString(message);
|
|
Console.WriteLine("Message: " + msg);
|
|
}
|
|
#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<bool> ReadRegAsync(SerialPort port, byte regType, byte address, ushort start, ushort registers)
|
|
{
|
|
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:
|
|
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);
|
|
//Send modbus message to Serial Port:
|
|
try
|
|
{
|
|
await Task.Run(() => { port.Write(message, 0, message.Length); });
|
|
return true;
|
|
}
|
|
catch (Exception err)
|
|
{
|
|
MessageBox.Show(err.Message);
|
|
port.Close();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MessageBox.Show("Порт не открыт");
|
|
return false;
|
|
}
|
|
|
|
}
|
|
#endregion
|
|
|
|
}
|
|
}
|
|
|