采集插件开发
一、插件开发概述
1.1 什么是采集插件
ThingsGateway 采集插件是用于与各种工业设备进行通讯的模块,负责数据的采集和写入操作。插件系统采用了灵活的架构设计,允许开发者根据不同设备的通讯协议和特性,开发自定义的采集插件。
1.2 插件继承体系
ThingsGateway 提供了多个基类用于插件开发,开发者可以根据需求选择合适的基类:
- CollectBase:最基础的采集插件基类,提供了基本的框架和方法
- CollectFoundationBase:继承自 CollectBase,提供了更多的基础功能实现
- CollectFoundationPackPropertyBase:继承自 CollectFoundationBase,提供了打包读取的功能支持
1.3 插件开发流程
- 创建插件项目:创建一个类库项目,引用必要的依赖
- 继承基类:选择合适的基类继承
- 实现必要方法:根据设备特性实现必要的方法
- 配置插件属性:定义插件的配置项
- 编译打包:编译项目,生成DLL文件
- 部署测试:可以直接引用项目,F5调试
二、插件开发详解
2.1 创建插件项目
2.1.1 项目结构
- 创建类库项目:使用 Visual Studio 创建一个 .NET 类库项目
- 引用依赖:引用以下必要的依赖:
- ThingsGateway.Gateway.Application
2.1.2 项目配置
在项目文件中添加以下配置:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj" />
</ItemGroup>
</Project>
2.2 插件类实现
2.2.1 基本插件结构
using Newtonsoft.Json.Linq;
using ThingsGateway.Foundation;
using ThingsGateway.Gateway.Application;
using ThingsGateway.Razor;
namespace ThingsGateway.Plugin.YourPlugin;
/// <summary>
/// 插件类,继承<see cref="CollectFoundationBase"/> 实现采集插件
/// </summary>
public class YourCollectPlugin : CollectFoundationBase
{
/// <summary>
/// 插件配置项
/// </summary>
public override CollectPropertyBase CollectProperties => _property;
private YourCollectProperty? _property = new();
// 其他方法实现...
}
/// <summary>
/// 插件配置类
/// </summary>
public class YourCollectProperty : CollectFoundationPackPropertyBase
{
// 配置项定义...
}
2.2.2 关键方法实现
2.2.2.1 初始化方法
/// <summary>
/// 在插件初始化时调用,只会执行一次
/// </summary>
/// <param name="channel">插件默认的链路通道类</param>
/// <param name="cancellationToken">取消令牌</param>
protected override Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken)
{
// 初始化通讯参数
// 设置连接属性
// 初始化其他资源
return Task.CompletedTask;
}
2.2.2.2 变量打包方法
/// <summary>
/// 变量打包操作,将设备变量打包成源读取变量
/// </summary>
/// <param name="deviceVariables">设备变量列表</param>
/// <returns>源读取变量列表</returns>
protected override Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables)
{
var sourceReads = new List<VariableSourceRead>();
// 实现变量打包逻辑
// 例如:将多个连续的寄存器地址打包成一个读取操作
// 示例:打包多个Modbus寄存器
var addressGroups = deviceVariables
.GroupBy(v => GetBaseAddress(v.Variable.Address))
.OrderBy(g => g.Key);
foreach (var group in addressGroups)
{
var firstVar = group.First();
var lastVar = group.Last();
var startAddress = GetRegisterAddress(firstVar.Variable.Address);
var endAddress = GetRegisterAddress(lastVar.Variable.Address);
var length = endAddress - startAddress + 1;
var sourceRead = new VariableSourceRead
{
Address = startAddress.ToString(),
Length = length * 2, // 每个寄存器2字节
VariableRunTimes = group.ToList()
};
sourceReads.Add(sourceRead);
}
return Task.FromResult(sourceReads);
}
2.2.2.3 读取源变量方法
/// <summary>
/// 读取源变量,在VariableSourceReadsEnable为true时执行
/// </summary>
/// <param name="variableSourceRead">源读取变量</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>读取结果</returns>
protected override async ValueTask<OperResult<byte[]>> ReadSourceAsync(VariableSourceRead variableSourceRead, CancellationToken cancellationToken)
{
try
{
// 实现读取逻辑
// 例如:发送读取命令到设备,接收响应数据
// 示例:Modbus读取
var address = int.Parse(variableSourceRead.Address);
var length = variableSourceRead.Length / 2; // 每个寄存器2字节
// 发送读取命令
var result = await _modbusClient.ReadHoldingRegistersAsync(address, length, cancellationToken);
if (result.IsSuccess)
{
// 解析响应数据
var data = new byte[length * 2];
Buffer.BlockCopy(result.Content, 0, data, 0, data.Length);
// 更新变量值
foreach (var variable in variableSourceRead.VariableRunTimes)
{
var varAddress = GetRegisterAddress(variable.Variable.Address);
var offset = (varAddress - address) * 2;
var value = ParseValue(data, offset, variable.Variable.DataType);
variable.SetValue(value, DateTime.Now, true);
}
return new OperResult<byte[]>(data);
}
else
{
return new OperResult<byte[]>(result.ErrorCode, result.ErrorMessage);
}
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}