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 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 } }