HJ212 and Documents
This commit is contained in:
@@ -1,38 +1,56 @@
|
||||
using ModbusTcpToRtu;
|
||||
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();
|
||||
|
||||
|
||||
@@ -8,12 +8,18 @@ using MultipleMachinesJobScheduler = Modbus.Net.MultipleMachinesJobScheduler<Mod
|
||||
|
||||
namespace ModbusTcpToRtu
|
||||
{
|
||||
/// <summary>
|
||||
/// Modbus TCP 转 RTU 后台工作服务
|
||||
/// 定期从 Modbus TCP 设备读取数据并写入到 Modbus RTU 设备
|
||||
/// </summary>
|
||||
public class Worker : BackgroundService
|
||||
{
|
||||
private readonly ILogger<Worker> _logger;
|
||||
|
||||
// Modbus 读取工具(TCP)
|
||||
private BaseUtility readUtility;
|
||||
|
||||
// Modbus 写入工具(RTU)
|
||||
private BaseUtility writeUtility;
|
||||
|
||||
public Worker(ILogger<Worker> logger)
|
||||
@@ -23,92 +29,120 @@ namespace ModbusTcpToRtu
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
// 配置 Quartz 调度器的触发器和作业键
|
||||
var triggerKey = "Modbus.Net.Job.Utility.SchedulerTrigger";
|
||||
var jobKey = "Modbus.Net.Job.Utility.JobKey";
|
||||
|
||||
// 从配置读取调度间隔(秒转毫秒)
|
||||
var intervalMilliSecond = int.Parse(ConfigurationReader.GetValue("Utility", "interval")) * 1000;
|
||||
// 读取重复次数(-1 表示无限重复)
|
||||
var count = int.Parse(ConfigurationReader.GetValue("Utility", "count"));
|
||||
|
||||
// 读取读写配置组
|
||||
var readWriteGroup = ConfigurationReader.GetContent<List<ReadWriteGroup>>("Utility", "readwrite");
|
||||
|
||||
// 读取 Modbus TCP 配置
|
||||
var readType = Enum.Parse<ModbusType>(ConfigurationReader.GetValue("Utility:read", "type"));
|
||||
var readAddress = ConfigurationReader.GetValue("Utility:read", "address");
|
||||
var readSlaveAddress = byte.Parse(ConfigurationReader.GetValue("Utility:read", "slaveAddress"));
|
||||
var readMasterAddress = byte.Parse(ConfigurationReader.GetValue("Utility:read", "masterAddress"));
|
||||
|
||||
// 读取 Modbus RTU 配置
|
||||
var writeType = Enum.Parse<ModbusType>(ConfigurationReader.GetValue("Utility:write", "type"));
|
||||
var writeAddress = ConfigurationReader.GetValue("Utility:write", "address");
|
||||
var writeSlaveAddress = byte.Parse(ConfigurationReader.GetValue("Utility:write", "slaveAddress"));
|
||||
var writeMasterAddress = byte.Parse(ConfigurationReader.GetValue("Utility:write", "masterAddress"));
|
||||
|
||||
// 创建 Modbus 工具实例
|
||||
readUtility = new ModbusUtility(readType, readAddress, readSlaveAddress, readMasterAddress, Endian.BigEndianLsb);
|
||||
writeUtility = new ModbusUtility(writeType, writeAddress, writeSlaveAddress, writeMasterAddress, Endian.BigEndianLsb);
|
||||
|
||||
// 获取 Quartz 调度器
|
||||
IScheduler scheduler = await StdSchedulerFactory.GetDefaultScheduler();
|
||||
|
||||
// 启动调度器
|
||||
await scheduler.Start();
|
||||
|
||||
// 创建触发器
|
||||
ITrigger trigger;
|
||||
if (intervalMilliSecond <= 0)
|
||||
{
|
||||
// 间隔<=0,立即执行一次
|
||||
trigger = TriggerBuilder.Create()
|
||||
.WithIdentity(triggerKey, "Modbus.Net.DataQuery.Group." + triggerKey)
|
||||
.StartNow()
|
||||
.Build();
|
||||
}
|
||||
else if (count >= 0)
|
||||
// 指定重复次数
|
||||
trigger = TriggerBuilder.Create()
|
||||
.WithIdentity(triggerKey, "Modbus.Net.DataQuery.Group." + triggerKey)
|
||||
.StartNow()
|
||||
.WithSimpleSchedule(b => b.WithInterval(TimeSpan.FromMilliseconds(intervalMilliSecond)).WithRepeatCount(count))
|
||||
.Build();
|
||||
else
|
||||
// 无限重复
|
||||
trigger = TriggerBuilder.Create()
|
||||
.WithIdentity(triggerKey, "Modbus.Net.DataQuery.Group." + triggerKey)
|
||||
.StartNow()
|
||||
.WithSimpleSchedule(b => b.WithInterval(TimeSpan.FromMilliseconds(intervalMilliSecond)).RepeatForever())
|
||||
.Build();
|
||||
|
||||
// 创建作业监听器
|
||||
IJobListener listener;
|
||||
if (intervalMilliSecond <= 0)
|
||||
{
|
||||
// 重复指定次数的监听器
|
||||
listener = new JobChainingJobLIstenerWithDataMapRepeated("Modbus.Net.DataQuery.Chain." + triggerKey, new string[2] { "Value", "SetValue" }, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 无限重复的监听器
|
||||
listener = new JobChainingJobListenerWithDataMap("Modbus.Net.DataQuery.Chain." + triggerKey, new string[2] { "Value", "SetValue" });
|
||||
}
|
||||
// 添加作业监听器到调度器
|
||||
scheduler.ListenerManager.AddJobListener(listener, GroupMatcher<JobKey>.GroupEquals("Modbus.Net.DataQuery.Group." + triggerKey));
|
||||
|
||||
// 如果触发器已存在,先删除
|
||||
if (await scheduler.GetTrigger(new TriggerKey(triggerKey)) != null)
|
||||
{
|
||||
await scheduler.UnscheduleJob(new TriggerKey(triggerKey, "Modbus.Net.DataQuery.Group." + triggerKey));
|
||||
}
|
||||
// 删除已存在的作业
|
||||
var jobKeys = await scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals("Modbus.Net.DataQuery.Group." + triggerKey));
|
||||
await scheduler.DeleteJobs(jobKeys);
|
||||
|
||||
// 创建数据传递作业
|
||||
var job = JobBuilder.Create<UtilityPassDataJob>()
|
||||
.WithIdentity(jobKey)
|
||||
.StoreDurably(true)
|
||||
.Build();
|
||||
|
||||
// 将工具实例放入作业数据映射
|
||||
job.JobDataMap.Put("UtilityRead", readUtility);
|
||||
job.JobDataMap.Put("UtilityReadWriteGroup", readWriteGroup);
|
||||
job.JobDataMap.Put("UtilityWrite", writeUtility);
|
||||
|
||||
// 调度作业
|
||||
await scheduler.ScheduleJob(job, trigger);
|
||||
|
||||
}
|
||||
|
||||
public override Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// 停止所有作业调度
|
||||
return Task.Run(() => MultipleMachinesJobScheduler.CancelJob());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modbus 数据传递作业
|
||||
/// 从读取工具读取数据并写入到写入工具
|
||||
/// </summary>
|
||||
public class UtilityPassDataJob : IJob
|
||||
{
|
||||
public async Task Execute(IJobExecutionContext context)
|
||||
{
|
||||
// 从作业数据映射获取工具实例
|
||||
object utilityReadObject;
|
||||
object utilityWriteObject;
|
||||
object utilityReadWriteGroupObject;
|
||||
@@ -121,15 +155,22 @@ namespace ModbusTcpToRtu
|
||||
var writeUtility = (BaseUtility)utilityWriteObject;
|
||||
var utilityReadWriteGroup = (List<ReadWriteGroup>)utilityReadWriteGroupObject;
|
||||
|
||||
// 连接读取工具
|
||||
if (readUtility.IsConnected != true)
|
||||
await readUtility.ConnectAsync();
|
||||
// 连接写入工具
|
||||
if (writeUtility.IsConnected != true)
|
||||
await writeUtility.ConnectAsync();
|
||||
|
||||
// 遍历每个读写组
|
||||
foreach (var rwGroup in utilityReadWriteGroup)
|
||||
{
|
||||
// 从 Modbus TCP 读取数据
|
||||
// 地址格式:"4X 1" = 区域 + 地址
|
||||
var datas = await readUtility.GetDatasAsync(rwGroup.ReadStart / 10000 + "X " + rwGroup.ReadStart % 10000, rwGroup.ReadCount * 2, rwGroup.ReadCount);
|
||||
if (datas.IsSuccess == true)
|
||||
{
|
||||
// 将字节数组转换为对象数组并写入 Modbus RTU
|
||||
var ans = await writeUtility.SetDatasAsync(rwGroup.WriteStart / 10000 + "X " + rwGroup.WriteStart % 10000, ByteArrayToObjectArray(datas.Datas), rwGroup.ReadCount);
|
||||
if (ans.Datas)
|
||||
{
|
||||
@@ -143,6 +184,10 @@ namespace ModbusTcpToRtu
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 字节数组转对象数组
|
||||
/// 将每个字节转换为独立的对象
|
||||
/// </summary>
|
||||
public static object[] ByteArrayToObjectArray(byte[] arrBytes)
|
||||
{
|
||||
List<object> objArray = new List<object>();
|
||||
@@ -154,10 +199,27 @@ namespace ModbusTcpToRtu
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读写配置组
|
||||
/// 定义从哪个地址读取多少个数据,写入到哪个地址
|
||||
/// </summary>
|
||||
public class ReadWriteGroup
|
||||
{
|
||||
/// <summary>
|
||||
/// 读取起始地址
|
||||
/// 格式:区域*10000 + 地址,如 40001 表示 4X 区地址 1
|
||||
/// </summary>
|
||||
public int ReadStart { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 读取数量
|
||||
/// </summary>
|
||||
public int ReadCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 写入起始地址
|
||||
/// 格式:区域*10000 + 地址
|
||||
/// </summary>
|
||||
public int WriteStart { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user