Refactor code structure for improved readability and maintainability
This commit is contained in:
306
DaireApplication/ViewModels/ModBusMaster.cs
Normal file
306
DaireApplication/ViewModels/ModBusMaster.cs
Normal file
@@ -0,0 +1,306 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Ports;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AvaloniaApplication1.ViewModels
|
||||
{
|
||||
public class ModBusMaster
|
||||
{
|
||||
public byte slaveId { get; set; } = 0x01; // Slave ID (your ESP slave ID)
|
||||
//byte functionCode = 0x05; // Function code for Write Single Coil
|
||||
//byte coilAddressHigh = 0x00; // Coil address (MSB)
|
||||
//byte coilAddressLow = 0x00; // Coil address (LSB)
|
||||
//byte coilValueHigh = 0x00; // Coil value (0xFF00 to turn on, 0x0000 to turn off)
|
||||
//byte coilValueLow = 0x00;
|
||||
|
||||
// Function to read coils
|
||||
public async Task<bool[]> ReadCoils(SerialPort port, ushort startAddress, ushort numberOfCoils)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte functionCode = 0x01; // Function code for Read Coils
|
||||
byte[] frame = new byte[6]; // Request frame size (without CRC)
|
||||
|
||||
// Construct the request frame
|
||||
frame[0] = slaveId; // Slave address
|
||||
frame[1] = functionCode; // Function code (0x01 for Read Coils)
|
||||
frame[2] = (byte)(startAddress >> 8); // Start address high byte
|
||||
frame[3] = (byte)(startAddress & 0xFF); // Start address low byte
|
||||
frame[4] = (byte)(numberOfCoils >> 8); // Number of coils high byte
|
||||
frame[5] = (byte)(numberOfCoils & 0xFF); // Number of coils low byte
|
||||
|
||||
// Calculate CRC and append it to the frame
|
||||
byte[] crc = CalculateCRC(frame);
|
||||
byte[] fullFrame = new byte[frame.Length + crc.Length];
|
||||
Array.Copy(frame, fullFrame, frame.Length);
|
||||
Array.Copy(crc, 0, fullFrame, frame.Length, crc.Length);
|
||||
|
||||
// Send the frame over the serial port
|
||||
port.DiscardOutBuffer();
|
||||
port.DiscardInBuffer();
|
||||
|
||||
port.Write(fullFrame, 0, fullFrame.Length);
|
||||
Thread.Sleep(500);
|
||||
Console.WriteLine("Read Coils request sent.");
|
||||
|
||||
// Buffer for the response (modbus slave response size can vary)
|
||||
byte[] response = new byte[256];
|
||||
int bytesRead = port.Read(response, 0, response.Length);
|
||||
Console.WriteLine("Response Coils sent.");
|
||||
|
||||
|
||||
// Ensure that the response is valid (check slave address and function code)
|
||||
if (response[0] != slaveId || response[1] != functionCode)
|
||||
{
|
||||
Console.WriteLine("Invalid response or error in slave response.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Calculate the number of bytes based on the number of coils
|
||||
int expectedByteCount = (numberOfCoils + 7) / 8;
|
||||
if (response[2] != expectedByteCount)
|
||||
{
|
||||
Console.WriteLine("Incorrect byte count in the response.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// The response bytes are in bit form, and we need to extract them
|
||||
bool[] coilStates = new bool[numberOfCoils]; // Array to store the coil states
|
||||
int byteIndex = 3; // Start reading from byte 3 (after slave ID and function code)
|
||||
byte coilByte = response[byteIndex]; // The byte representing the coil states
|
||||
|
||||
// Loop over all coils and check the corresponding bit
|
||||
for (int coilIndex = 0; coilIndex < numberOfCoils; coilIndex++)
|
||||
{
|
||||
// Check the state of the coil by checking the corresponding bit in coilByte
|
||||
coilStates[coilIndex] = coilByte == 1 ? true : false; /*(coilByte & (1 << (7 - coilIndex))) != 0;*/
|
||||
}
|
||||
if (coilStates==null)
|
||||
{
|
||||
Console.WriteLine("status wael: null");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"status: {coilStates[0]}");
|
||||
|
||||
}
|
||||
|
||||
return coilStates;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"error wael: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Function to write a single coil
|
||||
public async Task<byte[]> WriteSingleCoil(ushort coilAddress, bool state)
|
||||
{
|
||||
byte functionCode = 0x05;
|
||||
byte[] frame = new byte[6];
|
||||
|
||||
frame[0] = slaveId; // Slave address
|
||||
frame[1] = functionCode; // Function code
|
||||
frame[2] = (byte)(coilAddress >> 8); // Coil address high byte
|
||||
frame[3] = (byte)(coilAddress & 0xFF); // Coil address low byte
|
||||
frame[4] = (byte)(state ? 0xFF : 0x00); // Coil value (ON=0xFF, OFF=0x00)
|
||||
frame[5] = (byte)(0x00); // Coil value (ON=0xFF, OFF=0x00)
|
||||
|
||||
// Calculate CRC and append it
|
||||
byte[] crc = CalculateCRC(frame);
|
||||
byte[] fullFrame = new byte[frame.Length + crc.Length];
|
||||
Array.Copy(frame, fullFrame, frame.Length);
|
||||
Array.Copy(crc, 0, fullFrame, frame.Length, crc.Length);
|
||||
|
||||
return fullFrame;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Function to write multiple coils
|
||||
public byte[] WriteMultipleCoils( ushort startAddress, bool[] coilValues)
|
||||
{
|
||||
byte functionCode = 0x0F;
|
||||
int byteCount = (coilValues.Length + 7) / 8; // Calculate the number of bytes needed
|
||||
byte[] frame = new byte[5 + byteCount]; // Basic frame without CRC
|
||||
|
||||
frame[0] = slaveId; // Slave address
|
||||
frame[1] = functionCode; // Function code
|
||||
frame[2] = (byte)(startAddress >> 8); // Start address high byte
|
||||
frame[3] = (byte)(startAddress & 0xFF); // Start address low byte
|
||||
frame[4] = (byte)(coilValues.Length); // Number of coils
|
||||
|
||||
frame[5] = (byte)byteCount; // Number of bytes to follow (coil values)
|
||||
for (int i = 0; i < coilValues.Length; i++)
|
||||
{
|
||||
int byteIndex = 5 + 1 + (i / 8); // Start at byte 6, accounting for byte count
|
||||
if (coilValues[i])
|
||||
{
|
||||
frame[byteIndex] |= (byte)(1 << (i % 8)); // Set the bit corresponding to the coil
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate CRC and append it
|
||||
byte[] crc = CalculateCRC(frame);
|
||||
byte[] fullFrame = new byte[frame.Length + crc.Length];
|
||||
Array.Copy(frame, fullFrame, frame.Length);
|
||||
Array.Copy(crc, 0, fullFrame, frame.Length, crc.Length);
|
||||
|
||||
return fullFrame;
|
||||
}
|
||||
// Function to write multiple registers
|
||||
public async Task<byte[]> WriteSingleRegister(ushort startAddress, int value)
|
||||
{
|
||||
byte functionCode = 0x06; // Function code for writing a single register
|
||||
byte[] frame = new byte[6]; // Basic frame without CRC
|
||||
|
||||
frame[0] = slaveId; // Slave address
|
||||
frame[1] = functionCode; // Function code
|
||||
frame[2] = (byte)(startAddress >> 8); // Start address high byte
|
||||
frame[3] = (byte)(startAddress & 0xFF); // Start address low byte
|
||||
frame[4] = (byte)(value >> 8); // Register value high byte
|
||||
frame[5] = (byte)(value & 0xFF); // Register value low byte
|
||||
|
||||
// Calculate CRC and append it
|
||||
byte[] crc = CalculateCRC(frame);
|
||||
byte[] fullFrame = new byte[frame.Length + crc.Length];
|
||||
Array.Copy(frame, fullFrame, frame.Length);
|
||||
Array.Copy(crc, 0, fullFrame, frame.Length, crc.Length);
|
||||
|
||||
return fullFrame;
|
||||
}
|
||||
public async Task<byte[]> WriteSingleRegister(ushort startAddress, ushort value)
|
||||
{
|
||||
byte functionCode = 0x06; // Function code for writing a single register
|
||||
byte[] frame = new byte[6]; // Basic frame without CRC
|
||||
|
||||
frame[0] = slaveId; // Slave address
|
||||
frame[1] = functionCode; // Function code
|
||||
frame[2] = (byte)(startAddress >> 8); // Start address high byte
|
||||
frame[3] = (byte)(startAddress & 0xFF); // Start address low byte
|
||||
frame[4] = (byte)(value >> 8); // Register value high byte
|
||||
frame[5] = (byte)(value & 0xFF); // Register value low byte
|
||||
|
||||
// Calculate CRC and append it
|
||||
byte[] crc = CalculateCRC(frame);
|
||||
byte[] fullFrame = new byte[frame.Length + crc.Length];
|
||||
Array.Copy(frame, fullFrame, frame.Length);
|
||||
Array.Copy(crc, 0, fullFrame, frame.Length, crc.Length);
|
||||
|
||||
return fullFrame;
|
||||
}
|
||||
public async Task<byte[]> ReadHoldingRegister(ushort startAddress, ushort numberOfRegisters)
|
||||
{
|
||||
byte functionCode = 0x03; // Function code for reading holding registers
|
||||
byte[] frame = new byte[6]; // Basic frame without CRC
|
||||
|
||||
frame[0] = slaveId; // Slave address
|
||||
frame[1] = functionCode; // Function code
|
||||
frame[2] = (byte)(startAddress >> 8); // Start address high byte
|
||||
frame[3] = (byte)(startAddress & 0xFF); // Start address low byte
|
||||
frame[4] = (byte)(numberOfRegisters >> 8); // Number of registers high byte
|
||||
frame[5] = (byte)(numberOfRegisters & 0xFF); // Number of registers low byte
|
||||
|
||||
// Calculate CRC and append it
|
||||
byte[] crc = CalculateCRC(frame);
|
||||
byte[] fullFrame = new byte[frame.Length + crc.Length];
|
||||
Array.Copy(frame, fullFrame, frame.Length);
|
||||
Array.Copy(crc, 0, fullFrame, frame.Length, crc.Length);
|
||||
|
||||
return fullFrame;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<byte[]> ReadInputRegisters(ushort startAddress, ushort numberOfRegisters)
|
||||
{
|
||||
byte functionCode = 0x04; // Function code for reading input registers
|
||||
byte[] frame = new byte[6]; // Basic frame without CRC
|
||||
|
||||
frame[0] = slaveId; // Slave address
|
||||
frame[1] = functionCode; // Function code
|
||||
frame[2] = (byte)(startAddress >> 8); // Start address high byte
|
||||
frame[3] = (byte)(startAddress & 0xFF); // Start address low byte
|
||||
frame[4] = (byte)(numberOfRegisters >> 8); // Number of registers high byte
|
||||
frame[5] = (byte)(numberOfRegisters & 0xFF); // Number of registers low byte
|
||||
|
||||
// Calculate CRC and append it
|
||||
byte[] crc = CalculateCRC(frame);
|
||||
byte[] fullFrame = new byte[frame.Length + crc.Length];
|
||||
Array.Copy(frame, fullFrame, frame.Length);
|
||||
Array.Copy(crc, 0, fullFrame, frame.Length, crc.Length);
|
||||
|
||||
return fullFrame;
|
||||
}
|
||||
|
||||
|
||||
public async Task<byte[]> WriteMultipleRegisters(ushort startAddress, float[] values)
|
||||
{
|
||||
byte functionCode = 0x10; // Function code for writing multiple registers
|
||||
byte byteCount = (byte)(values.Length * 2); // Total number of bytes for values
|
||||
byte[] frame = new byte[7 + byteCount]; // Frame without CRC
|
||||
|
||||
frame[0] = slaveId; // Slave address
|
||||
frame[1] = functionCode; // Function code
|
||||
frame[2] = (byte)(startAddress >> 8); // Start address high byte
|
||||
frame[3] = (byte)(startAddress & 0xFF); // Start address low byte
|
||||
frame[4] = (byte)(values.Length >> 8); // Number of registers high byte
|
||||
frame[5] = (byte)(values.Length & 0xFF); // Number of registers low byte
|
||||
frame[6] = byteCount; // Byte count
|
||||
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
short val = unchecked((short)values[i]);
|
||||
frame[7 + i * 2] = (byte)(val >> 8); // Register value high byte
|
||||
frame[8 + i * 2] = (byte)(val & 0xFF); // Register value low byte1
|
||||
}
|
||||
|
||||
// Calculate CRC and append it
|
||||
byte[] crc = CalculateCRC(frame);
|
||||
byte[] fullFrame = new byte[frame.Length + crc.Length];
|
||||
Array.Copy(frame, fullFrame, frame.Length);
|
||||
Array.Copy(crc, 0, fullFrame, frame.Length, crc.Length);
|
||||
|
||||
return fullFrame;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Function to calculate CRC16 for Modbus RTU frame
|
||||
public byte[] CalculateCRC(byte[] data)
|
||||
{
|
||||
ushort crc = 0xFFFF;
|
||||
|
||||
foreach (byte byteData in data)
|
||||
{
|
||||
crc ^= byteData;
|
||||
|
||||
for (int i = 8; i > 0; i--)
|
||||
{
|
||||
if ((crc & 0x0001) == 0x0001)
|
||||
{
|
||||
crc >>= 1;
|
||||
crc ^= 0xA001;
|
||||
}
|
||||
else
|
||||
{
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new byte[] { (byte)(crc & 0xFF), (byte)((crc >> 8) & 0xFF) };
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user