docs: 更新采集插件开发文档

This commit is contained in:
Diego
2025-06-25 14:22:58 +08:00
parent b2589fc634
commit 1000c8d38f
9 changed files with 211 additions and 52 deletions

View File

@@ -62,7 +62,7 @@ public class HardwareJob : IJob, IHardwareJob
if (historyHardwareInfos == null)
{
using var db = DbContext.GetDB<HistoryHardwareInfo>(); ;
historyHardwareInfos = await db.Queryable<HistoryHardwareInfo>().Where(a => a.Date > DateTime.Now.AddDays(-3)).ToListAsync().ConfigureAwait(false);
historyHardwareInfos = await db.Queryable<HistoryHardwareInfo>().Where(a => a.Date > DateTime.Now.AddDays(-3)).Take(1000).ToListAsync().ConfigureAwait(false);
MemoryCache.Set(CacheKey, historyHardwareInfos);
}

View File

@@ -238,7 +238,7 @@ public abstract class CollectBase : DriverBase, IRpcDriver
return variableTasks;
}
private void SetDeviceStatus(object? state, CancellationToken cancellationToken)
protected virtual void SetDeviceStatus(object? state, CancellationToken cancellationToken)
{
if (IsConnected())
{

View File

@@ -441,6 +441,9 @@ internal sealed class AlarmHostedService : BackgroundService, IAlarmHostedServic
//stopwatch.Stop();
//_logger.LogInformation("报警分析耗时:" + stopwatch.ElapsedMilliseconds + "ms");
}
catch (OperationCanceledException)
{
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Alarm analysis fail");

View File

@@ -1091,14 +1091,13 @@ public class OpcUaMaster : IDisposable
{
VariableNode variableNode = new VariableNode();
DataValue value;
DataValue value = values[1 + 2 * i];
if (!DataValue.IsGood(values[1 + 2 * i]))
{
//throw ServiceResultException.Create(values[1+2 * i].StatusCode, 1+2 * i, diagnosticInfos, responseHeader.StringTable);
}
//if (!DataValue.IsGood(value))
//{
// //throw ServiceResultException.Create(values[1+2 * i].StatusCode, 1+2 * i, diagnosticInfos, responseHeader.StringTable);
//}
// DataType Attribute
value = values[1 + 2 * i];
if (value == null)
{
@@ -1109,13 +1108,13 @@ public class OpcUaMaster : IDisposable
{
variableNode.DataType = (NodeId)value.GetValue(typeof(NodeId));
}
value = values[0 + 2 * i];
// NodeId Attribute
if (!DataValue.IsGood(values[0 + 2 * i]))
if (!DataValue.IsGood(value))
{
throw ServiceResultException.Create(values[0 + 2 * i].StatusCode, 0 + 2 * i, diagnosticInfos, responseHeader.StringTable);
throw ServiceResultException.Create(value.StatusCode, 0 + 2 * i, diagnosticInfos, responseHeader.StringTable);
}
value = values[0 + 2 * i];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Node does not support the NodeId attribute.");

View File

@@ -146,6 +146,8 @@ public class OpcUaMaster : CollectBase
}
}
}
private volatile bool checkLog;
private async Task CheckAsync(object? state, CancellationToken cancellationToken)
{
if (_plc.Session != null)
@@ -177,10 +179,13 @@ public class OpcUaMaster : CollectBase
}
}
LogMessage?.LogInformation("AddSubscriptions done");
checkLog = true;
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, "AddSubscriptions");
if (!checkLog)
LogMessage?.LogWarning(ex, "AddSubscriptions");
checkLog = false;
}
finally
{

View File

@@ -1,6 +1,6 @@
{
"HardwareInfo": {
"HistoryInterval": 60000, //历史更新间隔ms
"HistoryInterval": 300000, //历史更新间隔ms
"DaysAgo": 1, //保留1天数据
"Enable": true //启用硬件信息获取
}

View File

@@ -1,3 +1,5 @@
docker pull mcr.microsoft.com/dotnet/aspnet:8.0-noble
docker build -t registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway:latest .
docker push registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway

View File

@@ -1,3 +1,5 @@
docker pull mcr.microsoft.com/dotnet/aspnet:8.0-noble-arm64v8
docker build -f Dockerfile_arm64 -t registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64:latest .
docker push registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64

View File

@@ -1,46 +1,52 @@
// ------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
// ------------------------------------------------------------------------------

//using Newtonsoft.Json.Linq;
//using ThingsGateway.Foundation;
//using ThingsGateway.Gateway.Application;
//using ThingsGateway.Razor;
//namespace ThingsGateway.Server;
///// <summary>
///// 插件类
///// 插件类,默认实现了<see cref="IDevice"/>接口,继承<see cref="CollectFoundationBase"/> 实现采集插件
///// </summary>
//public class TestCollectPlugin : CollectFoundationBase
//{
// /// <summary>
// /// 调试UI Type如果不存在无需重写
// /// </summary>
// public override Type DriverDebugUIType => base.DriverDebugUIType;
// /// <summary>
// /// 插件属性UI Type继承<see cref="IPropertyUIBase"/>如果不存在无需重写
// /// </summary>
// public override Type DriverPropertyUIType => base.DriverPropertyUIType;
// /// <summary>
// /// 插件UI Type继承<see cref="IDriverUIBase"/>如果不存在无需重写
// /// </summary>
// public override Type DriverUIType => base.DriverUIType;
// /// <summary>
// /// 插件变量寄存器UI Type继承<see cref="IAddressUIBase"/>如果不存在无需重写
// /// </summary>
// public override Type DriverVariableAddressUIType => base.DriverVariableAddressUIType;
// /// <summary>
// /// 插件配置项,继承<see cref="CollectPropertyBase"/> 返回类实例
// /// </summary>
// public override CollectPropertyBase CollectProperties => _property;
// private TestCollectProperty? _property = new();
// /// <summary>
// /// 插件默认的PLC通讯类如未实现返回null
// /// </summary>
// public override IProtocol? Protocol => null;
// /// <summary>
// /// 在插件初始化时调用只会执行一次参数为插件默认的链路通道类如未实现可忽略l
// /// </summary>
// protected override Task InitChannelAsync(IChannel? channel ,CancellationToken cancellationToken)
// protected override Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken)
// {
// //做一些初始化操作
// return Task.CompletedTask;
// }
// /// <summary>
// /// 变量打包操作,会在Init方法执行,参数为设备变量列表,返回源读取变量列表
// /// 变量打包操作,会在默认的AfterVariablesChangedAsync方法执行,参数为设备变量列表,返回源读取变量列表
// /// </summary>
// protected override Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables)
// {
@@ -50,33 +56,175 @@
// //一般可操作 VariableRuntime 类中的 index, thingsgatewaybitconvter 等属性
// //一般可操作 VariableSourceRead 类中的 address, length 等属性
// return new List<VariableSourceRead>();
// return Task.FromResult(new List<VariableSourceRead>());
// }
// /// <summary>
// /// 实现<see cref="IDevice"/>
// /// </summary>
// public override IDevice? FoundationDevice => base.FoundationDevice;
// /// <summary>
// /// 特殊方法,添加<see cref="DynamicMethodAttribute"/> 特性 返回IOperResult
// /// 支持<see cref="CancellationToken"/> 参数,需放到最后
// /// 默认解析方式为英文分号
// /// 比如rpc参数为 test1;test2解析query1="test1",query2="test2"
// /// 也可以在变量地址中填入test1rpc参数传入test2解析query1="test1",query2="test2"
// /// </summary>
// [DynamicMethod("测试特殊方法")]
// public IOperResult<string> TestMethod(string query1, string query2, CancellationToken cancellationToken)
// {
// return new OperResult<string>() { Content = "测试特殊方法" };
// }
//}
///// <summary>
///// 插件类配置
///// </summary>
//public class TestCollectProperty : CollectFoundationPackPropertyBase
//{
// /// <summary>
// /// 添加<see cref="DynamicPropertyAttribute"/> 特性如需多语言配置可添加json资源参考其他插件
// /// </summary>
// [DynamicProperty(Description = null, Remark = null)]
// public string TestString { get; set; }
//}
///// <summary>
///// 插件类,完全自定义
///// </summary>
//public class TestCollectPlugin1 : CollectBase
//{
// /// <summary>
// /// 调试UI Type如果不存在无需重写
// /// </summary>
// public override Type DriverDebugUIType => base.DriverDebugUIType;
// /// <summary>
// /// 插件属性UI Type继承<see cref="IPropertyUIBase"/>如果不存在无需重写
// /// </summary>
// public override Type DriverPropertyUIType => base.DriverPropertyUIType;
// /// <summary>
// /// 插件UI Type继承<see cref="IDriverUIBase"/>如果不存在无需重写
// /// </summary>
// public override Type DriverUIType => base.DriverUIType;
// /// <summary>
// /// 插件变量寄存器UI Type继承<see cref="IAddressUIBase"/>如果不存在无需重写
// /// </summary>
// public override Type DriverVariableAddressUIType => base.DriverVariableAddressUIType;
// /// <summary>
// /// 插件配置项,继承<see cref="CollectPropertyBase"/> 返回类实例
// /// </summary>
// public override CollectPropertyBase CollectProperties => _property;
// private TestCollectProperty1? _property = new();
// /// <summary>
// /// 在插件初始化时调用只会执行一次参数为插件默认的链路通道类如未实现可忽略l
// /// </summary>
// protected override Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken)
// {
// //做一些初始化操作
// return Task.CompletedTask;
// }
// /// <summary>
// /// 变量打包操作会在默认的AfterVariablesChangedAsync方法里执行参数为设备变量列表返回源读取变量列表
// /// </summary>
// protected override Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables)
// {
// //实现将设备变量打包成源读取变量
// //比如如果需要实现MC中的字多读功能需将多个变量地址打包成一个源读取地址和读取长度根据一系列规则添加解析标识然后在返回的整个字节数组中解析出原来的变量地址代表的数据字节
// //一般可操作 VariableRuntime 类中的 index, thingsgatewaybitconvter 等属性
// //一般可操作 VariableSourceRead 类中的 address, length 等属性
// return Task.FromResult(new List<VariableSourceRead>());
// }
// /// <summary>
// /// 如果不实现ReadSourceAsync方法可以返回flase
// /// </summary>
// protected override bool VariableSourceReadsEnable => base.VariableSourceReadsEnable;
// /// <summary>
// /// 获取任务列表,默认会实现 TestOnline任务SetDeviceStatus任务以及 VariableSourceRead等任务VariableSourceRead任务启用条件为<see cref="VariableSourceReadsEnable"/> 为true。任务即是timer实现可通过<see cref="ScheduledTaskHelper.GetTask(string, Func{object?, CancellationToken, Task}, object?, TouchSocket.Core.ILog, CancellationToken)"/> 方法实现定时任务
// /// </summary>
// /// <param name="cancellationToken"></param>
// /// <returns></returns>
// protected override List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken)
// {
// return base.ProtectedGetTasks(cancellationToken);
// }
// /// <summary>
// /// 实现离线重连任务
// /// </summary>
// /// <param name="state"></param>
// /// <param name="cancellationToken"></param>
// /// <returns></returns>
// protected override Task TestOnline(object? state, CancellationToken cancellationToken)
// {
// return base.TestOnline(state, cancellationToken);
// }
// /// <summary>
// /// 返回是否成功连接设备
// /// </summary>
// /// <returns></returns>
// public override bool IsConnected()
// {
// return true;
// }
// /// <summary>
// /// 在变量发生组态变化后执行,默认会执行<see cref="ProtectedLoadSourceReadAsync"/> 方法重新获取源读取变量列表并且重新启动VariableSourceRead等任务
// /// </summary>
// /// <param name="cancellationToken"></param>
// /// <returns></returns>
// public override Task AfterVariablesChangedAsync(CancellationToken cancellationToken)
// {
// return base.AfterVariablesChangedAsync(cancellationToken);
// }
// /// <summary>
// /// 变量寄存器的字符串描述
// /// </summary>
// /// <returns></returns>
// public override string GetAddressDescription()
// {
// return base.GetAddressDescription();
// }
// /// <summary>
// /// 设备暂停时执行,默认会暂停所有任务
// /// </summary>
// /// <param name="pause"></param>
// public override void PauseThread(bool pause)
// {
// base.PauseThread(pause);
// }
// /// <summary>
// /// 开始前执行
// /// </summary>
// protected override Task ProtectedBeforeStartAsync(CancellationToken cancellationToken)
// protected override Task ProtectedStartAsync(CancellationToken cancellationToken)
// {
// //一般实现PLC长连接
// return base.ProtectedBeforeStartAsync(cancellationToken);
// return base.ProtectedStartAsync(cancellationToken);
// }
// /// <summary>
// /// 循环执行方法,父类会自动调用<see cref="ReadSourceAsync(VariableSourceRead, CancellationToken)"/>
// /// <br></br>
// /// 一般需要更新设备变量值,调用<see cref="VariableRuntime.SetValue(object?, DateTime, bool)"/>
// /// <br></br>
// /// 通讯失败时,更新设备状态,调用<see cref="DeviceRuntime.SetDeviceStatus(DateTime?, int?, string)"/>
// /// <br></br>
// /// </summary>
// protected override Task ProtectedExecuteAsync(CancellationToken cancellationToken)
// {
// return base.ProtectedExecuteAsync(cancellationToken);
// }
// /// <summary>
// /// 写入变量,实现设备写入操作
// /// 写入变量实现设备写入操作注意执行写锁using var writeLock = ReadWriteLock.WriterLock();
// /// </summary>
// protected override ValueTask<Dictionary<string, OperResult>> WriteValuesAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
// {
@@ -84,7 +232,7 @@
// }
// /// <summary>
// /// 读取源变量,如重写了<see cref="ProtectedExecuteAsync"/> ,此方法可能不会执行
// /// 读取源变量,<see cref="VariableSourceReadsEnable"/> 为true时添加源读取任务任务启动时会执行
// /// 一般需要更新设备变量值,调用<see cref="VariableRuntime.SetValue(object?, DateTime, bool)"/>
// /// </summary>
// protected override ValueTask<OperResult<byte[]>> ReadSourceAsync(VariableSourceRead variableSourceRead, CancellationToken cancellationToken)
@@ -103,7 +251,7 @@
// /// <summary>
// /// 特殊方法,添加<see cref="DynamicMethodAttribute"/> 特性
// /// 特殊方法,添加<see cref="DynamicMethodAttribute"/> 特性 返回IOperResult
// /// 支持<see cref="CancellationToken"/> 参数,需放到最后
// /// 默认解析方式为英文分号
// /// 比如rpc参数为 test1;test2解析query1="test1",query2="test2"
@@ -119,7 +267,7 @@
///// <summary>
///// 插件类配置
///// </summary>
//public class TestCollectProperty : CollectPropertyBase
//public class TestCollectProperty1 : CollectPropertyBase
//{
// /// <summary>
// /// 添加<see cref="DynamicPropertyAttribute"/> 特性如需多语言配置可添加json资源参考其他插件