HJ212 and Documents
This commit is contained in:
@@ -4,15 +4,24 @@ using MultipleMachinesJobScheduler = Modbus.Net.MultipleMachinesJobScheduler<Mod
|
||||
|
||||
namespace SampleModbusRtuServer.Service
|
||||
{
|
||||
/// <summary>
|
||||
/// Modbus RTU 服务器后台工作服务
|
||||
/// 监听串口,响应 Modbus RTU 读写请求
|
||||
/// </summary>
|
||||
public class Worker : BackgroundService
|
||||
{
|
||||
private readonly ILogger<Worker> _logger;
|
||||
|
||||
// 线圈数据数组(0X 区,10000 点)
|
||||
private bool[] zerox = new bool[10000];
|
||||
|
||||
// 保持寄存器数据数组(4X 区,20000 字节=10000 个寄存器)
|
||||
private byte[] threex = new byte[20000];
|
||||
|
||||
// 数据更新标志
|
||||
private bool _isUpdate = false;
|
||||
|
||||
// 最后更新时间
|
||||
private DateTime _updateTime = DateTime.MinValue;
|
||||
|
||||
public Worker(ILogger<Worker> logger)
|
||||
@@ -22,18 +31,28 @@ namespace SampleModbusRtuServer.Service
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
// 创建 Modbus RTU 协议接收器,监听 COM2 端口,从站地址 1
|
||||
ModbusRtuProtocolReceiver receiver = new ModbusRtuProtocolReceiver("COM2", 1);
|
||||
|
||||
// 设置数据处理回调
|
||||
receiver.DataProcess = receiveContent =>
|
||||
{
|
||||
byte[]? returnBytes = null;
|
||||
// 读取内容缓冲区(每个寄存器 2 字节)
|
||||
var readContent = new byte[receiveContent.Count * 2];
|
||||
// 写入内容
|
||||
var values = receiveContent.WriteContent;
|
||||
// 值字典(未使用)
|
||||
var valueDic = new Dictionary<string, double>();
|
||||
// Redis 值字典(未使用)
|
||||
var redisValues = new Dictionary<string, ReturnUnit<double>>();
|
||||
|
||||
// 处理写操作
|
||||
if (values != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 如果处于更新模式且距离上次更新超过 9.5 秒,记录日志
|
||||
if (_isUpdate && DateTime.Now - _updateTime > TimeSpan.FromSeconds(9.5))
|
||||
{
|
||||
_logger.LogDebug($"receive content { String.Concat(receiveContent.WriteContent.Select(p => " " + p.ToString("X2")))}");
|
||||
@@ -43,17 +62,24 @@ namespace SampleModbusRtuServer.Service
|
||||
{
|
||||
_logger.LogError(ex, "Error");
|
||||
}
|
||||
|
||||
// 根据功能码处理不同的写操作
|
||||
switch (receiveContent.FunctionCode)
|
||||
{
|
||||
case (byte)ModbusProtocolFunctionCode.WriteMultiRegister:
|
||||
{
|
||||
// 写多个寄存器(功能码 16)
|
||||
// 将写入的数据复制到保持寄存器数组
|
||||
Array.Copy(receiveContent.WriteContent, 0, threex, receiveContent.StartAddress * 2, receiveContent.WriteContent.Length);
|
||||
// 生成响应帧
|
||||
returnBytes = new WriteDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, receiveContent.StartAddress, receiveContent.Count);
|
||||
_isUpdate = true;
|
||||
break;
|
||||
}
|
||||
case (byte)ModbusProtocolFunctionCode.WriteSingleCoil:
|
||||
{
|
||||
// 写单个线圈(功能码 5)
|
||||
// 255=ON, 0=OFF
|
||||
if (receiveContent.WriteContent[0] == 255)
|
||||
{
|
||||
zerox[receiveContent.StartAddress] = true;
|
||||
@@ -62,80 +88,100 @@ namespace SampleModbusRtuServer.Service
|
||||
{
|
||||
zerox[receiveContent.StartAddress] = false;
|
||||
}
|
||||
// 生成响应帧
|
||||
returnBytes = new WriteDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, receiveContent.StartAddress, receiveContent.WriteContent);
|
||||
_isUpdate = true;
|
||||
break;
|
||||
}
|
||||
case (byte)ModbusProtocolFunctionCode.WriteMultiCoil:
|
||||
{
|
||||
// 写多个线圈(功能码 15)
|
||||
var pos = 0;
|
||||
List<bool> bitList = new List<bool>();
|
||||
// 将字节数组转换为位列表
|
||||
for (int i = 0; i < receiveContent.WriteByteCount; i++)
|
||||
{
|
||||
var bitArray = BigEndianLsbValueHelper.Instance.GetBits(receiveContent.WriteContent, ref pos);
|
||||
bitList.AddRange(bitArray.ToList());
|
||||
}
|
||||
// 将位列表复制到线圈数组
|
||||
Array.Copy(bitList.ToArray(), 0, zerox, receiveContent.StartAddress, bitList.Count);
|
||||
// 生成响应帧
|
||||
returnBytes = new WriteDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, receiveContent.StartAddress, receiveContent.Count);
|
||||
_isUpdate = true;
|
||||
break;
|
||||
}
|
||||
case (byte)ModbusProtocolFunctionCode.WriteSingleRegister:
|
||||
{
|
||||
// 写单个寄存器(功能码 6)
|
||||
// 将写入的数据复制到保持寄存器数组
|
||||
Array.Copy(receiveContent.WriteContent, 0, threex, receiveContent.StartAddress * 2, receiveContent.Count * 2);
|
||||
// 生成响应帧
|
||||
returnBytes = new WriteDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, receiveContent.StartAddress, receiveContent.Count);
|
||||
_isUpdate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 处理读操作
|
||||
else
|
||||
{
|
||||
switch (receiveContent.FunctionCode)
|
||||
{
|
||||
case (byte)ModbusProtocolFunctionCode.ReadHoldRegister:
|
||||
{
|
||||
// 读保持寄存器(功能码 3)
|
||||
// 从保持寄存器数组复制数据到读取缓冲区
|
||||
Array.Copy(threex, receiveContent.StartAddress, readContent, 0, readContent.Length);
|
||||
// 生成响应帧
|
||||
returnBytes = new ReadDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, (byte)receiveContent.Count, readContent);
|
||||
break;
|
||||
}
|
||||
case (byte)ModbusProtocolFunctionCode.ReadCoilStatus:
|
||||
{
|
||||
// 读线圈状态(功能码 1)
|
||||
// 计算需要读取的位数
|
||||
var bitCount = receiveContent.WriteByteCount * 8;
|
||||
var boolContent = new bool[bitCount];
|
||||
// 从线圈数组复制数据
|
||||
Array.Copy(zerox, receiveContent.StartAddress, boolContent, 0, bitCount);
|
||||
|
||||
// 将位数组打包为字节数组
|
||||
var byteList = new List<byte>();
|
||||
for (int i = 0; i < receiveContent.WriteByteCount; i++)
|
||||
{
|
||||
byte result = 0;
|
||||
for (int j = i; j < i + 8; j++)
|
||||
{
|
||||
// 将布尔值转换为对应的位
|
||||
|
||||
// 将布尔值转换为位(1 或 0)
|
||||
byte bit = boolContent[j] ? (byte)1 : (byte)0;
|
||||
|
||||
// 使用左移位运算将位合并到结果字节中
|
||||
|
||||
// 使用位移运算将位合并为字节
|
||||
result = (byte)((result << 1) | bit);
|
||||
}
|
||||
byteList.Add(result);
|
||||
}
|
||||
readContent = byteList.ToArray();
|
||||
// 生成响应帧
|
||||
returnBytes = new ReadDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, receiveContent.WriteByteCount, readContent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 返回响应帧,如果无法处理则返回 null
|
||||
if (returnBytes != null) return returnBytes;
|
||||
else return null;
|
||||
};
|
||||
|
||||
// 连接接收器(打开串口)
|
||||
await receiver.ConnectAsync();
|
||||
}
|
||||
|
||||
public override Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// 停止所有作业调度
|
||||
return Task.Run(() => MultipleMachinesJobScheduler.CancelJob());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user