HJ212 and Documents

This commit is contained in:
OpenClaw
2026-04-04 17:25:15 +08:00
parent e7cad88bcf
commit 3b223bc440
125 changed files with 18829 additions and 9380 deletions

View File

@@ -2,38 +2,56 @@ using SampleModbusRtuServer;
using SampleModbusRtuServer.Service;
using Serilog;
// 配置主机服务,作为 Windows 服务运行
IHost host = Host.CreateDefaultBuilder(args).UseWindowsService()
// 配置应用程序配置
.ConfigureAppConfiguration((hostingContext, config) =>
{
// 构建配置
var configuration = config
// 设置配置文件的基础路径
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
// 添加 appsettings.json 配置文件
.AddJsonFile("appsettings.json")
// 根据环境变量添加对应的配置文件(开发/生产等)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Production"}.json", true)
// 添加环境变量配置
.AddEnvironmentVariables()
.Build();
// 设置当前工作目录
Directory.SetCurrentDirectory(hostingContext.HostingEnvironment.ContentRootPath);
// 配置 Serilog 日志
Log.Logger = new LoggerConfiguration()
// 从配置读取日志设置
.ReadFrom.Configuration(configuration)
// 从日志上下文丰富日志信息
.Enrich.FromLogContext()
// 输出到控制台
.WriteTo.Console()
// 输出到文件(错误级别,按天滚动)
.WriteTo.File("Log\\log..txt", Serilog.Events.LogEventLevel.Error, shared: true, rollingInterval: RollingInterval.Day)
.CreateLogger();
// 创建日志工厂并添加 Serilog
var loggerFactory = new LoggerFactory().AddSerilog(Log.Logger);
// 设置 Quartz 日志提供者
Quartz.Logging.LogProvider.SetCurrentLogProvider(new ConsoleLogProvider());
// 设置 Modbus.Net 日志提供者
Modbus.Net.LogProvider.SetLogProvider(loggerFactory);
}
)
})
// 配置服务
.ConfigureServices(services =>
{
// 添加后台服务 Worker
services.AddHostedService<Worker>();
// 添加日志服务
services.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(Log.Logger, true));
})
.Build();
// 运行主机
await host.RunAsync();

View File

@@ -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());
}
}
}
}