using Modbus.Net; using Modbus.Net.Modbus; using MultipleMachinesJobScheduler = Modbus.Net.MultipleMachinesJobScheduler; namespace SampleModbusRtuServer.Service { /// /// Modbus RTU 服务器后台工作服务 /// 监听串口,响应 Modbus RTU 读写请求 /// public class Worker : BackgroundService { private readonly ILogger _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 logger) { _logger = logger; } 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(); // Redis 值字典(未使用) var redisValues = new Dictionary>(); // 处理写操作 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")))}"); } } catch (Exception ex) { _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; } else { 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 bitList = new List(); // 将字节数组转换为位列表 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(); 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()); } } }