Files
2026-04-04 17:25:15 +08:00

226 lines
9.4 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Modbus.Net;
using Modbus.Net.Modbus;
using Quartz;
using Quartz.Impl;
using Quartz.Impl.Matchers;
using BaseUtility = Modbus.Net.BaseUtility<byte[], byte[], Modbus.Net.ProtocolUnit<byte[], byte[]>, Modbus.Net.PipeUnit>;
using MultipleMachinesJobScheduler = Modbus.Net.MultipleMachinesJobScheduler<Modbus.Net.IMachineMethodDatas, string, double>;
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)
{
_logger = logger;
}
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;
context.JobDetail.JobDataMap.TryGetValue("UtilityRead", out utilityReadObject);
context.JobDetail.JobDataMap.TryGetValue("UtilityWrite", out utilityWriteObject);
context.JobDetail.JobDataMap.TryGetValue("UtilityReadWriteGroup", out utilityReadWriteGroupObject);
var readUtility = (BaseUtility)utilityReadObject;
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)
{
Console.WriteLine("success");
}
}
else
{
Console.WriteLine("failed");
}
}
}
/// <summary>
/// 字节数组转对象数组
/// 将每个字节转换为独立的对象
/// </summary>
public static object[] ByteArrayToObjectArray(byte[] arrBytes)
{
List<object> objArray = new List<object>();
foreach (byte b in arrBytes)
{
objArray.Add(b);
}
return objArray.ToArray();
}
}
/// <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; }
}
}