diff --git a/NewLife.IoT/DataHelper.cs b/NewLife.IoT/DataHelper.cs
index c865148..b3ecff0 100644
--- a/NewLife.IoT/DataHelper.cs
+++ b/NewLife.IoT/DataHelper.cs
@@ -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)
{
// 反向操作常量因子和缩放因子
@@ -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];
diff --git a/NewLife.IoT/Drivers/AsyncDriverBase.cs b/NewLife.IoT/Drivers/AsyncDriverBase.cs
new file mode 100644
index 0000000..9a34075
--- /dev/null
+++ b/NewLife.IoT/Drivers/AsyncDriverBase.cs
@@ -0,0 +1,153 @@
+using NewLife.IoT.ThingModels;
+#if !NET40
+using TaskEx = System.Threading.Tasks.Task;
+#endif
+
+namespace NewLife.IoT.Drivers;
+
+/// 异步协议驱动基类。抽象各种硬件设备的数据采集及远程控制
+/// 节点类型,可使用默认Node
+///
+public class AsyncDriverBase : AsyncDriverBase
+ where TNode : INode, new()
+ where TParameter : IDriverParameter, new()
+{
+ #region 元数据
+ /// 创建驱动参数对象,分析参数配置或创建默认参数
+ ///
+ protected override IDriverParameter OnCreateParameter() => new TParameter();
+ #endregion
+
+ #region 核心方法
+ ///
+ /// 打开设备驱动,传入参数。一个物理设备可能有多个逻辑设备共用,需要以节点来区分
+ ///
+ /// 逻辑设备
+ /// 参数。不同驱动的参数设置相差较大,对象字典具有较好灵活性,其对应IDriverParameter
+ /// 节点对象,可存储站号等信息,仅驱动自己识别
+ public override INode Open(IDevice device, IDriverParameter? parameter)
+ {
+ var node = new TNode
+ {
+ Driver = this,
+ Device = device,
+ Parameter = parameter,
+ };
+
+ return node;
+ }
+ #endregion
+}
+
+/// 异步协议驱动基类。抽象各种硬件设备的数据采集及远程控制
+///
+/// 在Modbus协议上,一个通信链路(串口/ModbusTcp地址)即是IDriver,可能有多个物理设备共用,各自表示为INode。
+/// 即使是一个物理设备,也可能因为管理需要而划分为多个逻辑设备,例如变配电网关等Modbus汇集网关。
+///
+/// 架构设计需要,本类继承自DriverBase,将来可能移除该继承关系。
+///
+public abstract class AsyncDriverBase : DriverBase, IAsyncDriver
+{
+ #region 核心方法
+ ///
+ /// 打开设备驱动,传入参数。一个物理设备可能有多个逻辑设备共用,需要以节点来区分
+ ///
+ /// 逻辑设备
+ /// 参数。不同驱动的参数设置相差较大,对象字典具有较好灵活性,其对应IDriverParameter
+ /// 节点对象,可存储站号等信息,仅驱动自己识别
+ public virtual Task OpenAsync(IDevice device, IDriverParameter? parameter)
+ {
+ var node = new Node
+ {
+ Driver = this,
+ Device = device,
+ };
+
+ return TaskEx.FromResult(node as INode);
+ }
+
+ ///
+ /// 关闭设备节点。多节点共用通信链路时,需等最后一个节点关闭才能断开
+ ///
+ ///
+#if NET40 || NET45
+ public virtual Task CloseAsync(INode node) => TaskEx.FromResult(0);
+#else
+ public virtual Task CloseAsync(INode node) => TaskEx.CompletedTask;
+#endif
+
+ /// 读取数据
+ ///
+ /// 驱动实现数据采集的核心方法,各驱动全力以赴实现好该接口。
+ /// 其中点位表名称和地址,仅该驱动能够识别。类型和长度等信息,则由物联网平台统一规范。
+ ///
+ /// 节点对象,可存储站号等信息,仅驱动自己识别
+ /// 点位集合
+ ///
+ public virtual Task> ReadAsync(INode node, IPoint[] points) => throw new NotImplementedException();
+
+ /// 写入数据
+ ///
+ /// 驱动实现远程控制的核心方法,各驱动全力以赴实现好该接口。
+ /// 其中点位表名称和地址,仅该驱动能够识别。类型和长度等信息,则由物联网平台统一规范。
+ ///
+ /// 节点对象,可存储站号等信息,仅驱动自己识别
+ /// 点位
+ /// 数值
+ public virtual Task