Skip to content

Commit

Permalink
v2.1 新增异步驱动接口IAsyncDriver
Browse files Browse the repository at this point in the history
  • Loading branch information
nnhy committed Jan 10, 2024
1 parent 1acf4d6 commit 8205c6c
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 26 deletions.
4 changes: 2 additions & 2 deletions NewLife.IoT/DataHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public static class DataHelper
using var span = DefaultTracer.Instance?.NewSpan(nameof(EncodeByThingModel), $"name={point.Name} data={data} type={type.Name} rawType={point.Type}");

// 找到物属性定义
var pt = spec?.ExtendedProperties?.FirstOrDefault(e => e.Id.EqualIgnoreCase(point.Name));
var pt = point.Name.IsNullOrEmpty() ? null : spec?.ExtendedProperties?.FirstOrDefault(e => e.Id.EqualIgnoreCase(point.Name));
if (pt != null)
{
// 反向操作常量因子和缩放因子
Expand Down Expand Up @@ -80,7 +80,7 @@ public static class DataHelper
try
{
// 找到物属性定义
var pt = spec?.ExtendedProperties?.FirstOrDefault(e => e.Id.EqualIgnoreCase(point.Name));
var pt = point.Name.IsNullOrEmpty() ? null : spec?.ExtendedProperties?.FirstOrDefault(e => e.Id.EqualIgnoreCase(point.Name));
if (pt != null)
{
if (type == typeof(Boolean)) return data[0];
Expand Down
153 changes: 153 additions & 0 deletions NewLife.IoT/Drivers/AsyncDriverBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
using NewLife.IoT.ThingModels;
#if !NET40
using TaskEx = System.Threading.Tasks.Task;
#endif

namespace NewLife.IoT.Drivers;

/// <summary>异步协议驱动基类。抽象各种硬件设备的数据采集及远程控制</summary>
/// <typeparam name="TNode">节点类型,可使用默认Node</typeparam>
/// <typeparam name="TParameter"></typeparam>
public class AsyncDriverBase<TNode, TParameter> : AsyncDriverBase
where TNode : INode, new()
where TParameter : IDriverParameter, new()
{
#region 元数据
/// <summary>创建驱动参数对象,分析参数配置或创建默认参数</summary>
/// <returns></returns>
protected override IDriverParameter OnCreateParameter() => new TParameter();
#endregion

#region 核心方法
/// <summary>
/// 打开设备驱动,传入参数。一个物理设备可能有多个逻辑设备共用,需要以节点来区分
/// </summary>
/// <param name="device">逻辑设备</param>
/// <param name="parameter">参数。不同驱动的参数设置相差较大,对象字典具有较好灵活性,其对应IDriverParameter</param>
/// <returns>节点对象,可存储站号等信息,仅驱动自己识别</returns>
public override INode Open(IDevice device, IDriverParameter? parameter)
{
var node = new TNode
{
Driver = this,
Device = device,
Parameter = parameter,
};

return node;
}
#endregion
}

/// <summary>异步协议驱动基类。抽象各种硬件设备的数据采集及远程控制</summary>
/// <remarks>
/// 在Modbus协议上,一个通信链路(串口/ModbusTcp地址)即是IDriver,可能有多个物理设备共用,各自表示为INode。
/// 即使是一个物理设备,也可能因为管理需要而划分为多个逻辑设备,例如变配电网关等Modbus汇集网关。
///
/// 架构设计需要,本类继承自DriverBase,将来可能移除该继承关系。
/// </remarks>
public abstract class AsyncDriverBase : DriverBase, IAsyncDriver
{
#region 核心方法
/// <summary>
/// 打开设备驱动,传入参数。一个物理设备可能有多个逻辑设备共用,需要以节点来区分
/// </summary>
/// <param name="device">逻辑设备</param>
/// <param name="parameter">参数。不同驱动的参数设置相差较大,对象字典具有较好灵活性,其对应IDriverParameter</param>
/// <returns>节点对象,可存储站号等信息,仅驱动自己识别</returns>
public virtual Task<INode> OpenAsync(IDevice device, IDriverParameter? parameter)
{
var node = new Node
{
Driver = this,
Device = device,
};

return TaskEx.FromResult(node as INode);
}

/// <summary>
/// 关闭设备节点。多节点共用通信链路时,需等最后一个节点关闭才能断开
/// </summary>
/// <param name="node"></param>
#if NET40 || NET45
public virtual Task CloseAsync(INode node) => TaskEx.FromResult(0);
#else
public virtual Task CloseAsync(INode node) => TaskEx.CompletedTask;
#endif

/// <summary>读取数据</summary>
/// <remarks>
/// 驱动实现数据采集的核心方法,各驱动全力以赴实现好该接口。
/// 其中点位表名称和地址,仅该驱动能够识别。类型和长度等信息,则由物联网平台统一规范。
/// </remarks>
/// <param name="node">节点对象,可存储站号等信息,仅驱动自己识别</param>
/// <param name="points">点位集合</param>
/// <returns></returns>
public virtual Task<IDictionary<String, Object?>> ReadAsync(INode node, IPoint[] points) => throw new NotImplementedException();

/// <summary>写入数据</summary>
/// <remarks>
/// 驱动实现远程控制的核心方法,各驱动全力以赴实现好该接口。
/// 其中点位表名称和地址,仅该驱动能够识别。类型和长度等信息,则由物联网平台统一规范。
/// </remarks>
/// <param name="node">节点对象,可存储站号等信息,仅驱动自己识别</param>
/// <param name="point">点位</param>
/// <param name="value">数值</param>
public virtual Task<Object?> WriteAsync(INode node, IPoint point, Object? value) => throw new NotImplementedException();

/// <summary>控制设备,特殊功能使用</summary>
/// <remarks>
/// 除了点位读写之外的其它控制功能。
/// 其中点位表名称和地址,仅该驱动能够识别。类型和长度等信息,则由物联网平台统一规范。
/// </remarks>
/// <param name="node">节点对象,可存储站号等信息,仅驱动自己识别</param>
/// <param name="parameters">参数</param>
public virtual Task<Object?> ControlAsync(INode node, IDictionary<String, Object?> parameters) => throw new NotImplementedException();
#endregion

#region 覆盖同步接口
/// <summary>
/// 打开设备驱动,传入参数。一个物理设备可能有多个逻辑设备共用,需要以节点来区分
/// </summary>
/// <param name="device">逻辑设备</param>
/// <param name="parameter">参数。不同驱动的参数设置相差较大,对象字典具有较好灵活性,其对应IDriverParameter</param>
/// <returns>节点对象,可存储站号等信息,仅驱动自己识别</returns>
public override INode Open(IDevice device, IDriverParameter? parameter) => OpenAsync(device, parameter).ConfigureAwait(false).GetAwaiter().GetResult();

/// <summary>
/// 关闭设备节点。多节点共用通信链路时,需等最后一个节点关闭才能断开
/// </summary>
/// <param name="node"></param>
public override void Close(INode node) => CloseAsync(node).ConfigureAwait(false).GetAwaiter().GetResult();

/// <summary>读取数据</summary>
/// <remarks>
/// 驱动实现数据采集的核心方法,各驱动全力以赴实现好该接口。
/// 其中点位表名称和地址,仅该驱动能够识别。类型和长度等信息,则由物联网平台统一规范。
/// </remarks>
/// <param name="node">节点对象,可存储站号等信息,仅驱动自己识别</param>
/// <param name="points">点位集合</param>
/// <returns></returns>
public override IDictionary<String, Object?> Read(INode node, IPoint[] points) => ReadAsync(node, points).ConfigureAwait(false).GetAwaiter().GetResult();

/// <summary>写入数据</summary>
/// <remarks>
/// 驱动实现远程控制的核心方法,各驱动全力以赴实现好该接口。
/// 其中点位表名称和地址,仅该驱动能够识别。类型和长度等信息,则由物联网平台统一规范。
/// </remarks>
/// <param name="node">节点对象,可存储站号等信息,仅驱动自己识别</param>
/// <param name="point">点位</param>
/// <param name="value">数值</param>
public override Object? Write(INode node, IPoint point, Object? value) => WriteAsync(node, point, value).ConfigureAwait(false).GetAwaiter().GetResult();

/// <summary>控制设备,特殊功能使用</summary>
/// <remarks>
/// 除了点位读写之外的其它控制功能。
/// 其中点位表名称和地址,仅该驱动能够识别。类型和长度等信息,则由物联网平台统一规范。
/// </remarks>
/// <param name="node">节点对象,可存储站号等信息,仅驱动自己识别</param>
/// <param name="parameters">参数</param>
public override Object? Control(INode node, IDictionary<String, Object?> parameters) => ControlAsync(node, parameters).ConfigureAwait(false).GetAwaiter().GetResult();
#endregion
}
81 changes: 81 additions & 0 deletions NewLife.IoT/Drivers/IAsyncDriver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using NewLife.IoT.ThingModels;
using NewLife.IoT.ThingSpecification;

namespace NewLife.IoT.Drivers;

/// <summary>协议驱动异步接口。抽象各种硬件设备的数据采集及远程控制</summary>
/// <remarks>
/// 在Modbus协议上,一个通信链路(串口/ModbusTcp地址)即是IDriver,可能有多个物理设备共用,各自表示为INode。
/// 即使是一个物理设备,也可能因为管理需要而划分为多个逻辑设备,例如变配电网关等Modbus汇集网关。
///
/// 除了具体设备实例化驱动对象,在物联网平台扫描驱动时,也有可能实例化驱动对象,以获取默认参数与产品物模型。
/// </remarks>
public interface IAsyncDriver
{
#region 元数据
/// <summary>创建驱动参数对象,分析参数配置或创建默认参数</summary>
/// <remarks>
/// 可序列化成Xml/Json作为该协议的参数模板。由于Xml需要良好的注释特性,优先使用。
/// 获取后,按新版本覆盖旧版本。
/// </remarks>
/// <param name="parameter">Xml/Json参数配置,为空时仅创建默认参数</param>
/// <returns></returns>
IDriverParameter? CreateParameter(String? parameter = null);

/// <summary>获取产品物模型</summary>
/// <remarks>
/// 如果设备有固定点位属性、服务和事件,则直接返回,否则返回空。
/// 物联网平台有两种情况调用该接口:
/// 1,打开设备后。常见于OPC/BACnet等,此时可获取特定设备场景的物模型。
/// 2,扫描设备时。此时未连接任何设备,只能返回该类设备的通用物模型,常用于具体硬件产品,例如各种传感器。
/// 获取后,按新版本覆盖旧版本。
/// </remarks>
/// <returns></returns>
ThingSpec? GetSpecification();
#endregion

#region 核心方法
/// <summary>
/// 打开设备驱动,传入参数。一个物理设备可能有多个逻辑设备共用,需要以节点来区分
/// </summary>
/// <param name="device">逻辑设备</param>
/// <param name="parameter">参数。不同驱动的参数设置相差较大,对象字典具有较好灵活性,其对应IDriverParameter</param>
/// <returns>节点对象,可存储站号等信息,仅驱动自己识别</returns>
Task<INode> OpenAsync(IDevice device, IDriverParameter? parameter);

/// <summary>
/// 关闭设备节点。多节点共用通信链路时,需等最后一个节点关闭才能断开
/// </summary>
/// <param name="node"></param>
Task CloseAsync(INode node);

/// <summary>读取数据</summary>
/// <remarks>
/// 驱动实现数据采集的核心方法,各驱动全力以赴实现好该接口。
/// 其中点位表名称和地址,仅该驱动能够识别。类型和长度等信息,则由物联网平台统一规范。
/// </remarks>
/// <param name="node">节点对象,可存储站号等信息,仅驱动自己识别</param>
/// <param name="points">点位集合</param>
/// <returns></returns>
Task<IDictionary<String, Object?>> ReadAsync(INode node, IPoint[] points);

/// <summary>写入数据</summary>
/// <remarks>
/// 驱动实现远程控制的核心方法,各驱动全力以赴实现好该接口。
/// 其中点位表名称和地址,仅该驱动能够识别。类型和长度等信息,则由物联网平台统一规范。
/// </remarks>
/// <param name="node">节点对象,可存储站号等信息,仅驱动自己识别</param>
/// <param name="point">点位</param>
/// <param name="value">数值</param>
Task<Object?> WriteAsync(INode node, IPoint point, Object? value);

/// <summary>控制设备,特殊功能使用</summary>
/// <remarks>
/// 除了点位读写之外的其它控制功能。
/// 其中点位表名称和地址,仅该驱动能够识别。类型和长度等信息,则由物联网平台统一规范。
/// </remarks>
/// <param name="node">节点对象,可存储站号等信息,仅驱动自己识别</param>
/// <param name="parameters">参数</param>
Task<Object?> ControlAsync(INode node, IDictionary<String, Object?> parameters);
#endregion
}
5 changes: 2 additions & 3 deletions NewLife.IoT/Drivers/IDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,9 @@ public static class DriverExtensions
/// <param name="device">逻辑设备</param>
/// <param name="parameters">参数对象</param>
/// <returns></returns>
public static INode Open(this IDriver driver, IDevice device, IDictionary<String, Object> parameters)
public static INode Open(this IDriver driver, IDevice device, IDictionary<String, Object?> parameters)
{
var type = driver.CreateParameter()?.GetType();
if (type == null) throw new InvalidOperationException();
var type = (driver.CreateParameter()?.GetType()) ?? throw new InvalidOperationException();

var ps = JsonHelper.Default.Convert(parameters, type) as IDriverParameter;
var node = driver.Open(device, ps);
Expand Down
4 changes: 2 additions & 2 deletions NewLife.IoT/NewLife.IoT.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<Description>IoT standard library, which defines various communication protocol standards and specifications in the Internet of Things field, without specific implementation. Used for IoT platform construction and unifying various hardware driver protocols. IoT标准库,定义物联网领域的各种通信协议标准规范,不含具体实现。用于IoT平台建设,以及统一各种硬件驱动协议</Description>
<Company>新生命开发团队</Company>
<Copyright>©2002-2024 新生命开发团队</Copyright>
<VersionPrefix>2.0</VersionPrefix>
<VersionPrefix>2.1</VersionPrefix>
<VersionSuffix>$([System.DateTime]::Now.ToString(`yyyy.MMdd`))</VersionSuffix>
<Version>$(VersionPrefix).$(VersionSuffix)</Version>
<FileVersion>$(Version)</FileVersion>
Expand All @@ -28,7 +28,7 @@
<RepositoryUrl>https://github.com/NewLifeX/NewLife.IoT</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>物联网;IoT;边缘计算;Edge;新生命团队;NewLife;$(AssemblyName)</PackageTags>
<PackageReleaseNotes>增加控制器层,新增输入输出口、串口、Modbus接口和板卡接口等接口统一定义,约定各种板卡及工业计算机所具备的基础功能</PackageReleaseNotes>
<PackageReleaseNotes>新增异步驱动接口IAsyncDriver</PackageReleaseNotes>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
Expand Down
Loading

0 comments on commit 8205c6c

Please sign in to comment.