NewLife/ZeroIoT

更新到最新通信架构,改进指令下发,支持更多设备接入
智能大石头 authored at 2025-05-28 00:16:25
ca7fa67
Tree
1 Parent(s) d168058
Summary: 27 changed files with 529 additions and 352 deletions.
Modified +11 -0
Modified +182 -181
Modified +1 -1
Modified +16 -0
Modified +2 -2
Modified +0 -0
Modified +5 -2
Modified +15 -15
Modified +23 -23
Modified +23 -23
Modified +4 -4
Modified +1 -0
Modified +1 -0
Modified +10 -0
Modified +63 -2
Modified +1 -0
Added +12 -0
Added +21 -0
Modified +1 -2
Modified +6 -19
Modified +0 -14
Modified +6 -6
Modified +13 -24
Modified +3 -2
Modified +28 -0
Modified +76 -28
Modified +5 -4
Modified +11 -0
diff --git a/IoT.Data/Entity/IoT.htm b/IoT.Data/Entity/IoT.htm
index 9841b83..4903f2e 100644
--- a/IoT.Data/Entity/IoT.htm
+++ b/IoT.Data/Entity/IoT.htm
@@ -783,6 +783,17 @@
         </tr>
 
         <tr>
+            <td>WebSocket</td>
+            <td>长连接</td>
+            <td>Boolean</td>
+            <td></td>
+            <td></td>
+            <td></td>
+            <td>N</td>
+            <td>WebSocket长连接</td>
+        </tr>
+
+        <tr>
             <td>Delay</td>
             <td>延迟</td>
             <td>Int32</td>
Modified +182 -181
diff --git a/IoT.Data/Entity/Model.xml b/IoT.Data/Entity/Model.xml
index 5f49e3b..56cd461 100644
--- a/IoT.Data/Entity/Model.xml
+++ b/IoT.Data/Entity/Model.xml
@@ -1,206 +1,207 @@
 <?xml version="1.0" encoding="utf-8"?>
-<EntityModel xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:schemaLocation="https://newlifex.com https://newlifex.com/Model2023.xsd" xmlns="https://newlifex.com/Model2023.xsd">
+<EntityModel xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:schemaLocation="https://newlifex.com https://newlifex.com/Model202309.xsd" Document="https://newlifex.com/xcode/model" xmlns="https://newlifex.com/Model202309.xsd">
   <Option>
-    <!--输出目录-->
-    <Output>.\</Output>
-    <!--是否使用中文文件名。默认false-->
-    <ChineseFileName>False</ChineseFileName>
-    <!--基类。可能包含基类和接口,其中{name}替换为Table.Name-->
-    <BaseClass>Entity</BaseClass>
-    <!--命名空间-->
-    <Namespace>IoT.Data</Namespace>
     <!--类名模板。其中{name}替换为Table.Name,如{name}Model/I{name}Dto等-->
     <ClassNameTemplate />
     <!--显示名模板。其中{displayName}替换为Table.DisplayName-->
     <DisplayNameTemplate />
-    <!--用于生成拷贝函数的模型类。例如{name}或I{name}-->
+    <!--基类。可能包含基类和接口,其中{name}替换为Table.Name-->
+    <BaseClass>Entity</BaseClass>
+    <!--命名空间-->
+    <Namespace>IoT.Data</Namespace>
+    <!--输出目录-->
+    <Output>.\</Output>
+    <!--是否使用中文文件名。默认false-->
+    <ChineseFileName>False</ChineseFileName>
+    <!--用于生成Copy函数的参数类型。例如{name}或I{name}-->
     <ModelNameForCopy />
     <!--带有索引器。实现IModel接口-->
     <HasIModel>False</HasIModel>
-    <!--模型类模版-->
-    <ModelClass />
-    <!--模型接口模版-->
-    <ModelInterface />
+    <!--可为null上下文。生成String?等-->
+    <Nullable>False</Nullable>
     <!--数据库连接名-->
     <ConnName>IoT</ConnName>
+    <!--模型类模版。设置后生成模型类,用于接口数据传输,例如{name}Model-->
+    <ModelClass />
     <!--模型类输出目录。默认当前目录的Models子目录-->
     <ModelsOutput>.\Models\</ModelsOutput>
+    <!--模型接口模版。设置后生成模型接口,用于约束模型类和实体类,例如I{name}-->
+    <ModelInterface />
     <!--模型接口输出目录。默认当前目录的Interfaces子目录-->
     <InterfacesOutput>.\Interfaces\</InterfacesOutput>
     <!--用户实体转为模型类的模型类。例如{name}或{name}DTO-->
     <ModelNameForToModel />
     <!--命名格式。Default/Upper/Lower/Underline-->
     <NameFormat>Default</NameFormat>
-    <!--生成器版本-->
-    <Version>11.8.2023.0524</Version>
-    <!--帮助文档-->
-    <Document>https://newlifex.com/xcode/model</Document>
     <!--魔方区域显示名-->
     <DisplayName>设备管理</DisplayName>
     <!--魔方控制器输出目录-->
     <CubeOutput>../../IoTZero/Areas/IoT/</CubeOutput>
   </Option>
-  <Table Name="Product" Description="产品。设备的集合,通常指一组具有相同功能的设备。物联网平台为每个产品颁发全局唯一的ProductKey。">
-    <Columns>
-      <Column Name="Id" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
-      <Column Name="Name" DataType="String" Master="True" Description="名称" />
-      <Column Name="Code" DataType="String" Description="编码。ProductKey" />
-      <Column Name="Enable" DataType="Boolean" Description="启用。开发中/已发布" />
-      <Column Name="DeviceCount" DataType="Int32" Description="设备数量" />
-      <Column Name="CreateUser" DataType="String" Description="创建人" Model="False" Category="扩展" />
-      <Column Name="CreateUserId" DataType="Int32" Description="创建者" Model="False" Category="扩展" />
-      <Column Name="CreateTime" DataType="DateTime" Description="创建时间" Model="False" Category="扩展" />
-      <Column Name="CreateIP" DataType="String" Description="创建地址" Model="False" Category="扩展" />
-      <Column Name="UpdateUser" DataType="String" Description="更新人" Model="False" Category="扩展" />
-      <Column Name="UpdateUserId" DataType="Int32" Description="更新者" Model="False" Category="扩展" />
-      <Column Name="UpdateTime" DataType="DateTime" Description="更新时间" Model="False" Category="扩展" />
-      <Column Name="UpdateIP" DataType="String" Description="更新地址" Model="False" Category="扩展" />
-      <Column Name="Remark" DataType="String" Length="500" Description="描述" Category="扩展" />
-    </Columns>
-    <Indexes>
-      <Index Columns="Code" Unique="True" />
-    </Indexes>
-  </Table>
-  <Table Name="Device" Description="设备。归属于某个产品下的具体设备。物联网平台为设备颁发产品内唯一的证书DeviceName。设备可以直接连接物联网平台,也可以作为子设备通过网关连接物联网平台。">
-    <Columns>
-      <Column Name="Id" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
-      <Column Name="Name" DataType="String" Master="True" Description="名称" />
-      <Column Name="Code" DataType="String" Description="编码。设备唯一证书DeviceName,用于设备认证,在注册时由系统生成" />
-      <Column Name="Secret" DataType="String" Description="密钥。设备密钥DeviceSecret,用于设备认证,注册时由系统生成" />
-      <Column Name="ProductId" DataType="Int32" Description="产品" />
-      <Column Name="GroupId" DataType="Int32" Map="DeviceGroup@Id@Name@GroupPath" Description="分组" />
-      <Column Name="Enable" DataType="Boolean" Description="启用" />
-      <Column Name="Online" DataType="Boolean" Description="在线" />
-      <Column Name="Version" DataType="String" Description="版本" />
-      <Column Name="IP" DataType="String" Length="200" Description="本地IP" />
-      <Column Name="Uuid" DataType="String" Length="200" Description="唯一标识。硬件标识,或其它能够唯一区分设备的标记" />
-      <Column Name="Location" DataType="String" Description="位置。场地安装位置,或者经纬度" Category="登录信息" />
-      <Column Name="Period" DataType="Int32" Description="心跳周期。默认60秒" Category="参数设置" />
-      <Column Name="PollingTime" DataType="Int32" Description="采集间隔。默认1000ms" Category="参数设置" />
-      <Column Name="Logins" DataType="Int32" Description="登录次数" Category="登录信息" />
-      <Column Name="LastLogin" DataType="DateTime" Description="最后登录" Category="登录信息" />
-      <Column Name="LastLoginIP" DataType="String" Description="最后IP。最后的公网IP地址" Category="登录信息" />
-      <Column Name="OnlineTime" DataType="Int32" Description="在线时长。总时长,每次下线后累加,单位,秒" Category="登录信息" />
-      <Column Name="RegisterTime" DataType="DateTime" Description="激活时间" Category="登录信息" />
-      <Column Name="CreateUserId" DataType="Int32" Description="创建者" Model="False" Category="扩展" />
-      <Column Name="CreateTime" DataType="DateTime" Description="创建时间" Model="False" Category="扩展" />
-      <Column Name="CreateIP" DataType="String" Description="创建地址" Model="False" Category="扩展" />
-      <Column Name="UpdateUserId" DataType="Int32" Description="更新者" Model="False" Category="扩展" />
-      <Column Name="UpdateTime" DataType="DateTime" Description="更新时间" Model="False" Category="扩展" />
-      <Column Name="UpdateIP" DataType="String" Description="更新地址" Model="False" Category="扩展" />
-      <Column Name="Remark" DataType="String" Length="500" Description="描述" Category="扩展" />
-    </Columns>
-    <Indexes>
-      <Index Columns="Code" Unique="True" />
-      <Index Columns="ProductId" />
-      <Index Columns="Uuid" />
-      <Index Columns="UpdateTime" />
-    </Indexes>
-  </Table>
-  <Table Name="DeviceGroup" Description="设备分组。物联网平台支持建立设备分组,分组中可包含不同产品下的设备。通过设备组来进行跨产品管理设备。" BaseType="EntityTree">
-    <Columns>
-      <Column Name="Id" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
-      <Column Name="Name" DataType="String" Master="True" Description="名称" />
-      <Column Name="ParentId" DataType="Int32" Description="父级" />
-      <Column Name="Sort" DataType="Int32" Description="排序" />
-      <Column Name="Devices" DataType="Int32" Description="设备总数" />
-      <Column Name="Activations" DataType="Int32" Description="激活设备" />
-      <Column Name="Onlines" DataType="Int32" Description="当前在线" />
-      <Column Name="CreateUserId" DataType="Int32" Description="创建者" Model="False" Category="扩展" />
-      <Column Name="CreateTime" DataType="DateTime" Description="创建时间" Model="False" Category="扩展" />
-      <Column Name="CreateIP" DataType="String" Description="创建地址" Model="False" Category="扩展" />
-      <Column Name="UpdateUserId" DataType="Int32" Description="更新者" Model="False" Category="扩展" />
-      <Column Name="UpdateTime" DataType="DateTime" Description="更新时间" Model="False" Category="扩展" />
-      <Column Name="UpdateIP" DataType="String" Description="更新地址" Model="False" Category="扩展" />
-      <Column Name="Remark" DataType="String" Length="500" Description="描述" Category="扩展" />
-    </Columns>
-    <Indexes>
-      <Index Columns="ParentId,Name" Unique="True" />
-      <Index Columns="Name" />
-    </Indexes>
-  </Table>
-  <Table Name="DeviceOnline" Description="设备在线">
-    <Columns>
-      <Column Name="Id" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
-      <Column Name="SessionId" DataType="String" Description="会话" />
-      <Column Name="ProductId" DataType="Int32" Description="产品" />
-      <Column Name="DeviceId" DataType="Int32" Description="设备" />
-      <Column Name="Name" DataType="String" Master="True" Description="名称" />
-      <Column Name="IP" DataType="String" Length="200" Description="本地IP" />
-      <Column Name="GroupPath" DataType="String" Description="分组" />
-      <Column Name="Pings" DataType="Int32" Description="心跳" />
-      <Column Name="Delay" DataType="Int32" Description="延迟。网络延迟,单位ms" />
-      <Column Name="Offset" DataType="Int32" Description="偏移。客户端时间减服务端时间,单位s" />
-      <Column Name="LocalTime" DataType="DateTime" Description="本地时间" />
-      <Column Name="Token" DataType="String" Length="200" Description="令牌" />
-      <Column Name="Creator" DataType="String" Description="创建者。服务端设备" />
-      <Column Name="CreateTime" DataType="DateTime" Description="创建时间" Model="False" />
-      <Column Name="CreateIP" DataType="String" Description="创建地址" Model="False" />
-      <Column Name="UpdateTime" DataType="DateTime" Description="更新时间" Model="False" />
-      <Column Name="Remark" DataType="String" Length="500" Description="备注" />
-    </Columns>
-    <Indexes>
-      <Index Columns="SessionId" Unique="True" />
-      <Index Columns="ProductId" />
-      <Index Columns="UpdateTime" />
-    </Indexes>
-  </Table>
-  <Table Name="DeviceHistory" Description="设备历史。记录设备上线下线等操作">
-    <Columns>
-      <Column Name="Id" DataType="Int64" PrimaryKey="True" Description="编号" />
-      <Column Name="DeviceId" DataType="Int32" Description="设备" />
-      <Column Name="Name" DataType="String" Master="True" Description="名称" />
-      <Column Name="Action" DataType="String" Description="操作" />
-      <Column Name="Success" DataType="Boolean" Description="成功" />
-      <Column Name="TraceId" DataType="String" Description="追踪。用于记录调用链追踪标识,在APM查找调用链" />
-      <Column Name="Creator" DataType="String" Description="创建者。服务端设备" />
-      <Column Name="CreateTime" DataType="DateTime" Description="创建时间" Model="False" />
-      <Column Name="CreateIP" DataType="String" Description="创建地址" Model="False" />
-      <Column Name="Remark" DataType="String" Length="2000" Description="内容" />
-    </Columns>
-    <Indexes>
-      <Index Columns="DeviceId,Id" />
-      <Index Columns="DeviceId,Action,Id" />
-    </Indexes>
-  </Table>
-  <Table Name="DeviceProperty" Description="设备属性。设备的功能模型之一,一般用于描述设备运行时的状态,如环境监测设备所读取的当前环境温度等。一个设备有多个属性,名值表">
-    <Columns>
-      <Column Name="Id" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
-      <Column Name="DeviceId" DataType="Int32" Description="设备" />
-      <Column Name="Name" DataType="String" Master="True" Description="名称" />
-      <Column Name="NickName" DataType="String" Description="昵称" />
-      <Column Name="Type" DataType="String" Description="类型" />
-      <Column Name="Value" DataType="String" Length="-1" Description="数值。设备上报数值" />
-      <Column Name="Unit" DataType="String" Description="单位" />
-      <Column Name="Enable" DataType="Boolean" Description="启用" />
-      <Column Name="TraceId" DataType="String" Description="追踪。用于记录调用链追踪标识,在APM查找调用链" Model="False" Category="扩展" />
-      <Column Name="CreateTime" DataType="DateTime" Description="创建时间" Model="False" Category="扩展" />
-      <Column Name="CreateIP" DataType="String" Description="创建地址" Model="False" Category="扩展" />
-      <Column Name="UpdateTime" DataType="DateTime" Description="更新时间" Model="False" Category="扩展" />
-      <Column Name="UpdateIP" DataType="String" Description="更新地址" Model="False" Category="扩展" />
-    </Columns>
-    <Indexes>
-      <Index Columns="DeviceId,Name" Unique="True" />
-      <Index Columns="UpdateTime" />
-    </Indexes>
-  </Table>
-  <Table Name="DeviceData" Description="设备数据。设备采集原始数据,按天分表存储">
-    <Columns>
-      <Column Name="Id" DataType="Int64" PrimaryKey="True" Description="编号" />
-      <Column Name="DeviceId" DataType="Int32" Description="设备" />
-      <Column Name="Name" DataType="String" Master="True" Description="名称。MQTT的Topic,或者属性名" />
-      <Column Name="Kind" DataType="String" Description="类型。数据来源,如PostProperty/PostData/MqttPostData" />
-      <Column Name="Value" DataType="String" Length="2000" Description="数值" />
-      <Column Name="Timestamp" DataType="Int64" Description="时间戳。设备生成数据时的UTC毫秒" />
-      <Column Name="TraceId" DataType="String" Description="追踪标识。用于记录调用链追踪标识,在APM查找调用链" Model="False" Category="扩展" />
-      <Column Name="Creator" DataType="String" Description="创建者。服务端设备" Model="False" Category="扩展" />
-      <Column Name="CreateTime" DataType="DateTime" Description="创建时间" Model="False" Category="扩展" />
-      <Column Name="CreateIP" DataType="String" Description="创建地址" Model="False" Category="扩展" />
-    </Columns>
-    <Indexes>
-      <Index Columns="DeviceId,Id" />
-      <Index Columns="DeviceId,Name,Id" />
-      <Index Columns="DeviceId,Kind,Id" />
-    </Indexes>
-  </Table>
+  <Tables>
+    <Table Name="Product" Description="产品。设备的集合,通常指一组具有相同功能的设备。物联网平台为每个产品颁发全局唯一的ProductKey。">
+      <Columns>
+        <Column Name="Id" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
+        <Column Name="Name" DataType="String" Master="True" Description="名称" />
+        <Column Name="Code" DataType="String" Description="编码。ProductKey" />
+        <Column Name="Enable" DataType="Boolean" Description="启用。开发中/已发布" />
+        <Column Name="DeviceCount" DataType="Int32" Description="设备数量" />
+        <Column Name="CreateUser" DataType="String" Description="创建人" Model="False" Category="扩展" />
+        <Column Name="CreateUserId" DataType="Int32" Description="创建者" Model="False" Category="扩展" />
+        <Column Name="CreateTime" DataType="DateTime" Description="创建时间" Model="False" Category="扩展" />
+        <Column Name="CreateIP" DataType="String" Description="创建地址" Model="False" Category="扩展" />
+        <Column Name="UpdateUser" DataType="String" Description="更新人" Model="False" Category="扩展" />
+        <Column Name="UpdateUserId" DataType="Int32" Description="更新者" Model="False" Category="扩展" />
+        <Column Name="UpdateTime" DataType="DateTime" Description="更新时间" Model="False" Category="扩展" />
+        <Column Name="UpdateIP" DataType="String" Description="更新地址" Model="False" Category="扩展" />
+        <Column Name="Remark" DataType="String" Length="500" Description="描述" Category="扩展" />
+      </Columns>
+      <Indexes>
+        <Index Columns="Code" Unique="True" />
+      </Indexes>
+    </Table>
+    <Table Name="Device" Description="设备。归属于某个产品下的具体设备。物联网平台为设备颁发产品内唯一的证书DeviceName。设备可以直接连接物联网平台,也可以作为子设备通过网关连接物联网平台。">
+      <Columns>
+        <Column Name="Id" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
+        <Column Name="Name" DataType="String" Master="True" Description="名称" />
+        <Column Name="Code" DataType="String" Description="编码。设备唯一证书DeviceName,用于设备认证,在注册时由系统生成" />
+        <Column Name="Secret" DataType="String" Description="密钥。设备密钥DeviceSecret,用于设备认证,注册时由系统生成" />
+        <Column Name="ProductId" DataType="Int32" Description="产品" />
+        <Column Name="GroupId" DataType="Int32" Map="DeviceGroup@Id@Name@GroupPath" Description="分组" />
+        <Column Name="Enable" DataType="Boolean" Description="启用" />
+        <Column Name="Online" DataType="Boolean" Description="在线" />
+        <Column Name="Version" DataType="String" Description="版本" />
+        <Column Name="IP" DataType="String" Length="200" Description="本地IP" />
+        <Column Name="Uuid" DataType="String" Length="200" Description="唯一标识。硬件标识,或其它能够唯一区分设备的标记" />
+        <Column Name="Location" DataType="String" Description="位置。场地安装位置,或者经纬度" Category="登录信息" />
+        <Column Name="Period" DataType="Int32" Description="心跳周期。默认60秒" Category="参数设置" />
+        <Column Name="PollingTime" DataType="Int32" Description="采集间隔。默认1000ms" Category="参数设置" />
+        <Column Name="Logins" DataType="Int32" Description="登录次数" Category="登录信息" />
+        <Column Name="LastLogin" DataType="DateTime" Description="最后登录" Category="登录信息" />
+        <Column Name="LastLoginIP" DataType="String" Description="最后IP。最后的公网IP地址" Category="登录信息" />
+        <Column Name="OnlineTime" DataType="Int32" Description="在线时长。总时长,每次下线后累加,单位,秒" Category="登录信息" />
+        <Column Name="RegisterTime" DataType="DateTime" Description="激活时间" Category="登录信息" />
+        <Column Name="CreateUserId" DataType="Int32" Description="创建者" Model="False" Category="扩展" />
+        <Column Name="CreateTime" DataType="DateTime" Description="创建时间" Model="False" Category="扩展" />
+        <Column Name="CreateIP" DataType="String" Description="创建地址" Model="False" Category="扩展" />
+        <Column Name="UpdateUserId" DataType="Int32" Description="更新者" Model="False" Category="扩展" />
+        <Column Name="UpdateTime" DataType="DateTime" Description="更新时间" Model="False" Category="扩展" />
+        <Column Name="UpdateIP" DataType="String" Description="更新地址" Model="False" Category="扩展" />
+        <Column Name="Remark" DataType="String" Length="500" Description="描述" Category="扩展" />
+      </Columns>
+      <Indexes>
+        <Index Columns="Code" Unique="True" />
+        <Index Columns="ProductId" />
+        <Index Columns="Uuid" />
+        <Index Columns="UpdateTime" />
+      </Indexes>
+    </Table>
+    <Table Name="DeviceGroup" Description="设备分组。物联网平台支持建立设备分组,分组中可包含不同产品下的设备。通过设备组来进行跨产品管理设备。" BaseType="EntityTree">
+      <Columns>
+        <Column Name="Id" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
+        <Column Name="Name" DataType="String" Master="True" Description="名称" />
+        <Column Name="ParentId" DataType="Int32" Description="父级" />
+        <Column Name="Sort" DataType="Int32" Description="排序" />
+        <Column Name="Devices" DataType="Int32" Description="设备总数" />
+        <Column Name="Activations" DataType="Int32" Description="激活设备" />
+        <Column Name="Onlines" DataType="Int32" Description="当前在线" />
+        <Column Name="CreateUserId" DataType="Int32" Description="创建者" Model="False" Category="扩展" />
+        <Column Name="CreateTime" DataType="DateTime" Description="创建时间" Model="False" Category="扩展" />
+        <Column Name="CreateIP" DataType="String" Description="创建地址" Model="False" Category="扩展" />
+        <Column Name="UpdateUserId" DataType="Int32" Description="更新者" Model="False" Category="扩展" />
+        <Column Name="UpdateTime" DataType="DateTime" Description="更新时间" Model="False" Category="扩展" />
+        <Column Name="UpdateIP" DataType="String" Description="更新地址" Model="False" Category="扩展" />
+        <Column Name="Remark" DataType="String" Length="500" Description="描述" Category="扩展" />
+      </Columns>
+      <Indexes>
+        <Index Columns="ParentId,Name" Unique="True" />
+        <Index Columns="Name" />
+      </Indexes>
+    </Table>
+    <Table Name="DeviceOnline" Description="设备在线">
+      <Columns>
+        <Column Name="Id" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
+        <Column Name="SessionId" DataType="String" Description="会话" />
+        <Column Name="ProductId" DataType="Int32" Description="产品" />
+        <Column Name="DeviceId" DataType="Int32" Description="设备" />
+        <Column Name="Name" DataType="String" Master="True" Description="名称" />
+        <Column Name="IP" DataType="String" Length="200" Description="本地IP" />
+        <Column Name="GroupPath" DataType="String" Description="分组" />
+        <Column Name="Pings" DataType="Int32" Description="心跳" />
+        <Column Name="WebSocket" DataType="Boolean" Description="长连接。WebSocket长连接" />
+        <Column Name="Delay" DataType="Int32" Description="延迟。网络延迟,单位ms" />
+        <Column Name="Offset" DataType="Int32" Description="偏移。客户端时间减服务端时间,单位s" />
+        <Column Name="LocalTime" DataType="DateTime" Description="本地时间" />
+        <Column Name="Token" DataType="String" Length="200" Description="令牌" />
+        <Column Name="Creator" DataType="String" Description="创建者。服务端设备" />
+        <Column Name="CreateTime" DataType="DateTime" Description="创建时间" Model="False" />
+        <Column Name="CreateIP" DataType="String" Description="创建地址" Model="False" />
+        <Column Name="UpdateTime" DataType="DateTime" Description="更新时间" Model="False" />
+        <Column Name="Remark" DataType="String" Length="500" Description="备注" />
+      </Columns>
+      <Indexes>
+        <Index Columns="SessionId" Unique="True" />
+        <Index Columns="ProductId" />
+        <Index Columns="UpdateTime" />
+      </Indexes>
+    </Table>
+    <Table Name="DeviceHistory" Description="设备历史。记录设备上线下线等操作">
+      <Columns>
+        <Column Name="Id" DataType="Int64" PrimaryKey="True" Description="编号" />
+        <Column Name="DeviceId" DataType="Int32" Description="设备" />
+        <Column Name="Name" DataType="String" Master="True" Description="名称" />
+        <Column Name="Action" DataType="String" Description="操作" />
+        <Column Name="Success" DataType="Boolean" Description="成功" />
+        <Column Name="TraceId" DataType="String" Description="追踪。用于记录调用链追踪标识,在APM查找调用链" />
+        <Column Name="Creator" DataType="String" Description="创建者。服务端设备" />
+        <Column Name="CreateTime" DataType="DateTime" Description="创建时间" Model="False" />
+        <Column Name="CreateIP" DataType="String" Description="创建地址" Model="False" />
+        <Column Name="Remark" DataType="String" Length="2000" Description="内容" />
+      </Columns>
+      <Indexes>
+        <Index Columns="DeviceId,Id" />
+        <Index Columns="DeviceId,Action,Id" />
+      </Indexes>
+    </Table>
+    <Table Name="DeviceProperty" Description="设备属性。设备的功能模型之一,一般用于描述设备运行时的状态,如环境监测设备所读取的当前环境温度等。一个设备有多个属性,名值表">
+      <Columns>
+        <Column Name="Id" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
+        <Column Name="DeviceId" DataType="Int32" Description="设备" />
+        <Column Name="Name" DataType="String" Master="True" Description="名称" />
+        <Column Name="NickName" DataType="String" Description="昵称" />
+        <Column Name="Type" DataType="String" Description="类型" />
+        <Column Name="Value" DataType="String" Length="-1" Description="数值。设备上报数值" />
+        <Column Name="Unit" DataType="String" Description="单位" />
+        <Column Name="Enable" DataType="Boolean" Description="启用" />
+        <Column Name="TraceId" DataType="String" Description="追踪。用于记录调用链追踪标识,在APM查找调用链" Model="False" Category="扩展" />
+        <Column Name="CreateTime" DataType="DateTime" Description="创建时间" Model="False" Category="扩展" />
+        <Column Name="CreateIP" DataType="String" Description="创建地址" Model="False" Category="扩展" />
+        <Column Name="UpdateTime" DataType="DateTime" Description="更新时间" Model="False" Category="扩展" />
+        <Column Name="UpdateIP" DataType="String" Description="更新地址" Model="False" Category="扩展" />
+      </Columns>
+      <Indexes>
+        <Index Columns="DeviceId,Name" Unique="True" />
+        <Index Columns="UpdateTime" />
+      </Indexes>
+    </Table>
+    <Table Name="DeviceData" Description="设备数据。设备采集原始数据,按天分表存储">
+      <Columns>
+        <Column Name="Id" DataType="Int64" PrimaryKey="True" Description="编号" />
+        <Column Name="DeviceId" DataType="Int32" Description="设备" />
+        <Column Name="Name" DataType="String" Master="True" Description="名称。MQTT的Topic,或者属性名" />
+        <Column Name="Kind" DataType="String" Description="类型。数据来源,如PostProperty/PostData/MqttPostData" />
+        <Column Name="Value" DataType="String" Length="2000" Description="数值" />
+        <Column Name="Timestamp" DataType="Int64" Description="时间戳。设备生成数据时的UTC毫秒" />
+        <Column Name="TraceId" DataType="String" Description="追踪标识。用于记录调用链追踪标识,在APM查找调用链" Model="False" Category="扩展" />
+        <Column Name="Creator" DataType="String" Description="创建者。服务端设备" Model="False" Category="扩展" />
+        <Column Name="CreateTime" DataType="DateTime" Description="创建时间" Model="False" Category="扩展" />
+        <Column Name="CreateIP" DataType="String" Description="创建地址" Model="False" Category="扩展" />
+      </Columns>
+      <Indexes>
+        <Index Columns="DeviceId,Id" />
+        <Index Columns="DeviceId,Name,Id" />
+        <Index Columns="DeviceId,Kind,Id" />
+      </Indexes>
+    </Table>
+  </Tables>
 </EntityModel>
\ No newline at end of file
Modified +1 -1
diff --git "a/IoT.Data/Entity/\350\256\276\345\244\207\345\234\250\347\272\277.Biz.cs" "b/IoT.Data/Entity/\350\256\276\345\244\207\345\234\250\347\272\277.Biz.cs"
index bec8005..c962a35 100644
--- "a/IoT.Data/Entity/\350\256\276\345\244\207\345\234\250\347\272\277.Biz.cs"
+++ "b/IoT.Data/Entity/\350\256\276\345\244\207\345\234\250\347\272\277.Biz.cs"
@@ -141,7 +141,7 @@ public partial class DeviceOnline : Entity<DeviceOnline>, IOnlineModel
     /// <returns>实体列表</returns>
     public static IList<DeviceOnline> FindAllByProductId(Int32 productId)
     {
-        if (productId <= 0) return new List<DeviceOnline>();
+        if (productId <= 0) return [];
 
         // 实体缓存
         if (Meta.Session.Count < 1000) return Meta.Cache.FindAll(e => e.ProductId == productId);
Modified +16 -0
diff --git "a/IoT.Data/Entity/\350\256\276\345\244\207\345\234\250\347\272\277.cs" "b/IoT.Data/Entity/\350\256\276\345\244\207\345\234\250\347\272\277.cs"
index 23168ac..9fc0382 100644
--- "a/IoT.Data/Entity/\350\256\276\345\244\207\345\234\250\347\272\277.cs"
+++ "b/IoT.Data/Entity/\350\256\276\345\244\207\345\234\250\347\272\277.cs"
@@ -88,6 +88,14 @@ public partial class DeviceOnline
     [BindColumn("Pings", "心跳", "")]
     public Int32 Pings { get => _Pings; set { if (OnPropertyChanging("Pings", value)) { _Pings = value; OnPropertyChanged("Pings"); } } }
 
+    private Boolean _WebSocket;
+    /// <summary>长连接。WebSocket长连接</summary>
+    [DisplayName("长连接")]
+    [Description("长连接。WebSocket长连接")]
+    [DataObjectField(false, false, false, 0)]
+    [BindColumn("WebSocket", "长连接。WebSocket长连接", "")]
+    public Boolean WebSocket { get => _WebSocket; set { if (OnPropertyChanging("WebSocket", value)) { _WebSocket = value; OnPropertyChanged("WebSocket"); } } }
+
     private Int32 _Delay;
     /// <summary>延迟。网络延迟,单位ms</summary>
     [DisplayName("延迟")]
@@ -177,6 +185,7 @@ public partial class DeviceOnline
             "IP" => _IP,
             "GroupPath" => _GroupPath,
             "Pings" => _Pings,
+            "WebSocket" => _WebSocket,
             "Delay" => _Delay,
             "Offset" => _Offset,
             "LocalTime" => _LocalTime,
@@ -200,6 +209,7 @@ public partial class DeviceOnline
                 case "IP": _IP = Convert.ToString(value); break;
                 case "GroupPath": _GroupPath = Convert.ToString(value); break;
                 case "Pings": _Pings = value.ToInt(); break;
+                case "WebSocket": _WebSocket = value.ToBoolean(); break;
                 case "Delay": _Delay = value.ToInt(); break;
                 case "Offset": _Offset = value.ToInt(); break;
                 case "LocalTime": _LocalTime = value.ToDateTime(); break;
@@ -246,6 +256,9 @@ public partial class DeviceOnline
         /// <summary>心跳</summary>
         public static readonly Field Pings = FindByName("Pings");
 
+        /// <summary>长连接。WebSocket长连接</summary>
+        public static readonly Field WebSocket = FindByName("WebSocket");
+
         /// <summary>延迟。网络延迟,单位ms</summary>
         public static readonly Field Delay = FindByName("Delay");
 
@@ -303,6 +316,9 @@ public partial class DeviceOnline
         /// <summary>心跳</summary>
         public const String Pings = "Pings";
 
+        /// <summary>长连接。WebSocket长连接</summary>
+        public const String WebSocket = "WebSocket";
+
         /// <summary>延迟。网络延迟,单位ms</summary>
         public const String Delay = "Delay";
 
Modified +2 -2
diff --git a/IoT.Data/IoT.Data.csproj b/IoT.Data/IoT.Data.csproj
index df4c2cf..99649fe 100644
--- a/IoT.Data/IoT.Data.csproj
+++ b/IoT.Data/IoT.Data.csproj
@@ -41,8 +41,8 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="NewLife.IoT" Version="2.2.2024.501" />
-    <PackageReference Include="NewLife.XCode" Version="11.13.2024.606" />
+    <PackageReference Include="NewLife.IoT" Version="2.4.2025.501" />
+    <PackageReference Include="NewLife.XCode" Version="11.19.2025.501" />
   </ItemGroup>
 
   <ItemGroup>
Modified +0 -0
diff --git a/IoT.Data/xcodetool.exe b/IoT.Data/xcodetool.exe
index 51c2c0b..7eb8d6c 100644
Binary files a/IoT.Data/xcodetool.exe and b/IoT.Data/xcodetool.exe differ
Modified +5 -2
diff --git a/IoTCore/IoTCore.csproj b/IoTCore/IoTCore.csproj
index 9207712..6108dff 100644
--- a/IoTCore/IoTCore.csproj
+++ b/IoTCore/IoTCore.csproj
@@ -19,12 +19,15 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <Compile Remove="Models\LoginResponse.cs" />
+    <Compile Remove="Models\LogoutResponse.cs" />
+    <Compile Remove="Models\PingResponse.cs" />
     <Compile Remove="Models\UpgradeInfo.cs" />
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="NewLife.IoT" Version="2.2.2024.501" />
-    <PackageReference Include="NewLife.Remoting" Version="3.0.2024.620-beta1407" />
+    <PackageReference Include="NewLife.IoT" Version="2.4.2025.501" />
+    <PackageReference Include="NewLife.Remoting" Version="3.3.2025.501" />
   </ItemGroup>
 
 </Project>
Modified +15 -15
diff --git a/IoTCore/Models/LoginInfo.cs b/IoTCore/Models/LoginInfo.cs
index e97818a..26b25cb 100644
--- a/IoTCore/Models/LoginInfo.cs
+++ b/IoTCore/Models/LoginInfo.cs
@@ -3,14 +3,14 @@
 namespace NewLife.IoT.Models;
 
 /// <summary>节点登录信息</summary>
-public class LoginInfo : ILoginRequest
+public class LoginInfo : LoginRequest
 {
     #region 属性
-    /// <summary>设备编码</summary>
-    public String Code { get; set; }
+    ///// <summary>设备编码</summary>
+    //public String Code { get; set; }
 
-    /// <summary>设备密钥</summary>
-    public String Secret { get; set; }
+    ///// <summary>设备密钥</summary>
+    //public String Secret { get; set; }
 
     /// <summary>产品证书</summary>
     public String ProductKey { get; set; }
@@ -18,22 +18,22 @@ public class LoginInfo : ILoginRequest
     /// <summary>产品密钥</summary>
     public String ProductSecret { get; set; }
 
-    /// <summary>实例。应用可能多实例部署,ip@proccessid</summary>
-    public String ClientId { get; set; }
+    ///// <summary>实例。应用可能多实例部署,ip@proccessid</summary>
+    //public String ClientId { get; set; }
 
     /// <summary>名称。可用于标识设备的名称</summary>
     public String Name { get; set; }
 
-    /// <summary>版本</summary>
-    public String Version { get; set; }
+    ///// <summary>版本</summary>
+    //public String Version { get; set; }
 
-    /// <summary>本地IP地址</summary>
-    public String IP { get; set; }
+    ///// <summary>本地IP地址</summary>
+    //public String IP { get; set; }
 
-    /// <summary>唯一标识</summary>
-    public String UUID { get; set; }
+    ///// <summary>唯一标识</summary>
+    //public String UUID { get; set; }
 
-    /// <summary>本地UTC时间</summary>
-    public Int64 Time { get; set; }
+    ///// <summary>本地UTC时间</summary>
+    //public Int64 Time { get; set; }
     #endregion
 }
\ No newline at end of file
Modified +23 -23
diff --git a/IoTCore/Models/PingInfo.cs b/IoTCore/Models/PingInfo.cs
index ca2fbaa..9b2c1a8 100644
--- a/IoTCore/Models/PingInfo.cs
+++ b/IoTCore/Models/PingInfo.cs
@@ -3,40 +3,40 @@
 namespace NewLife.IoT.Models;
 
 /// <summary>心跳信息</summary>
-public class PingInfo : IPingRequest
+public class PingInfo : PingRequest
 {
     #region 属性
-    /// <summary>内存大小</summary>
-    public UInt64 Memory { get; set; }
+    ///// <summary>内存大小</summary>
+    //public UInt64 Memory { get; set; }
 
-    /// <summary>可用内存大小</summary>
-    public UInt64 AvailableMemory { get; set; }
+    ///// <summary>可用内存大小</summary>
+    //public UInt64 AvailableMemory { get; set; }
 
-    /// <summary>磁盘大小。应用所在盘</summary>
-    public UInt64 TotalSize { get; set; }
+    ///// <summary>磁盘大小。应用所在盘</summary>
+    //public UInt64 TotalSize { get; set; }
 
-    /// <summary>磁盘可用空间。应用所在盘</summary>
-    public UInt64 AvailableFreeSpace { get; set; }
+    ///// <summary>磁盘可用空间。应用所在盘</summary>
+    //public UInt64 AvailableFreeSpace { get; set; }
 
-    /// <summary>CPU使用率</summary>
-    public Double CpuRate { get; set; }
+    ///// <summary>CPU使用率</summary>
+    //public Double CpuRate { get; set; }
 
-    /// <summary>温度</summary>
-    public Double Temperature { get; set; }
+    ///// <summary>温度</summary>
+    //public Double Temperature { get; set; }
 
-    /// <summary>电量</summary>
-    public Double Battery { get; set; }
+    ///// <summary>电量</summary>
+    //public Double Battery { get; set; }
 
-    /// <summary>本地IP</summary>
-    public String IP { get; set; }
+    ///// <summary>本地IP</summary>
+    //public String IP { get; set; }
 
-    /// <summary>开机时间,单位s</summary>
-    public Int32 Uptime { get; set; }
+    ///// <summary>开机时间,单位s</summary>
+    //public Int32 Uptime { get; set; }
 
-    /// <summary>本地UTC时间。ms毫秒</summary>
-    public Int64 Time { get; set; }
+    ///// <summary>本地UTC时间。ms毫秒</summary>
+    //public Int64 Time { get; set; }
 
-    /// <summary>延迟。ms毫秒</summary>
-    public Int32 Delay { get; set; }
+    ///// <summary>延迟。ms毫秒</summary>
+    //public Int32 Delay { get; set; }
     #endregion
 }
\ No newline at end of file
Modified +23 -23
diff --git a/IoTEdge/HttpDevice.cs b/IoTEdge/HttpDevice.cs
index ab556d5..05d1d98 100644
--- a/IoTEdge/HttpDevice.cs
+++ b/IoTEdge/HttpDevice.cs
@@ -1,6 +1,6 @@
-using NewLife.IoT.Models;
+using NewLife;
+using NewLife.IoT.Models;
 using NewLife.IoT.ThingModels;
-using NewLife.Log;
 using NewLife.Model;
 using NewLife.Remoting.Clients;
 using NewLife.Remoting.Models;
@@ -15,17 +15,24 @@ public class HttpDevice : ClientBase
     /// <summary>产品编码。从IoT管理平台获取</summary>
     public String ProductKey { get; set; }
 
+    /// <summary>产品密钥</summary>
+    public String ProductSecret { get; set; }
+
     private readonly ClientSetting _setting;
     #endregion
 
     #region 构造
-    public HttpDevice() => Prefix = "Device/";
-
     public HttpDevice(ClientSetting setting) : base(setting)
     {
+        // 设置动作,开启下行通知
+        Features = Features.Login | Features.Logout | Features.Ping | Features.Notify | Features.Upgrade | Features.PostEvent;
+        SetActions("Device/");
+        Actions[Features.CommandReply] = "Thing/ServiceReply";
+
         _setting = setting;
 
         ProductKey = setting.ProductKey;
+        ProductSecret = setting.DeviceSecret;
     }
     #endregion
 
@@ -34,6 +41,8 @@ public class HttpDevice : ClientBase
     {
         var provider = ServiceProvider ??= ObjectContainer.Provider;
 
+        PasswordProvider = new SaltPasswordProvider { Algorithm = "md5", SaltTime = 60 };
+
         // 找到容器,注册默认的模型实现,供后续InvokeAsync时自动创建正确的模型对象
         var container = ModelExtension.GetService<IObjectContainer>(provider) ?? ObjectContainer.Current;
         if (container != null)
@@ -50,14 +59,15 @@ public class HttpDevice : ClientBase
     }
     #endregion
 
-    #region 登录注销
+    #region 登录
     public override ILoginRequest BuildLoginRequest()
     {
-        var request = base.BuildLoginRequest();
-        if (request is LoginInfo info)
-        {
-            info.ProductKey = ProductKey;
-        }
+        var request = new LoginInfo();
+        FillLoginRequest(request);
+
+        request.ProductKey = ProductKey;
+        request.ProductSecret = ProductSecret;
+        request.Name = Environment.MachineName;
 
         return request;
     }
@@ -66,21 +76,11 @@ public class HttpDevice : ClientBase
     #region 心跳
     public override IPingRequest BuildPingRequest()
     {
-        var request = base.BuildPingRequest();
-        if (request is PingInfo info)
-        {
-
-        }
+        var request = new PingInfo();
+        FillPingRequest(request);
 
         return request;
     }
-
-    public override Task<Object> CommandReply(CommandReplyModel model) => InvokeAsync<Object>("Thing/ServiceReply", new ServiceReplyModel
-    {
-        Id = model.Id,
-        Status = (ServiceStatus)model.Status,
-        Data = model.Data,
-    });
     #endregion
 
     #region 数据
@@ -88,7 +88,7 @@ public class HttpDevice : ClientBase
     /// <returns></returns>
     public async Task PostDataAsync()
     {
-        if (Tracer != null) DefaultSpan.Current = null;
+        //if (Tracer != null) DefaultSpan.Current = null;
 
         using var span = Tracer?.NewSpan("PostData");
         try
Modified +4 -4
diff --git a/IoTEdge/IoTEdge.csproj b/IoTEdge/IoTEdge.csproj
index 0be314a..cb8c529 100644
--- a/IoTEdge/IoTEdge.csproj
+++ b/IoTEdge/IoTEdge.csproj
@@ -21,16 +21,16 @@
 
   <ItemGroup>
     <PackageReference Include="NewLife.BACnet" Version="1.0.2023.520-beta0022" />
-    <PackageReference Include="NewLife.Core" Version="10.10.2024.601" />
-    <PackageReference Include="NewLife.IoT" Version="2.2.2024.501" />
+    <PackageReference Include="NewLife.Core" Version="11.5.2025.501" />
+    <PackageReference Include="NewLife.IoT" Version="2.4.2025.501" />
     <PackageReference Include="NewLife.Modbus" Version="1.8.2024.217" />
     <PackageReference Include="NewLife.ModbusRTU" Version="1.8.2024.217" />
-    <PackageReference Include="NewLife.MQTT" Version="2.0.2024.516" />
+    <PackageReference Include="NewLife.MQTT" Version="2.0.2025.415" />
     <PackageReference Include="NewLife.NetPing" Version="1.1.2024.217" />
     <PackageReference Include="NewLife.PC" Version="1.0.2024.217" />
     <PackageReference Include="NewLife.Schneider" Version="1.0.2024.218" />
     <PackageReference Include="NewLife.Siemens" Version="1.1.2024.218" />
-    <PackageReference Include="NewLife.Stardust" Version="2.9.2024.402" />
+    <PackageReference Include="NewLife.Stardust" Version="3.3.2025.506" />
     <PackageReference Include="SmartA2" Version="1.1.2024.218" />
     <PackageReference Include="SmartA4" Version="1.0.2023.606-beta1305" />
   </ItemGroup>
Modified +1 -0
diff --git a/IoTZero/Areas/IoT/Controllers/DeviceController.cs b/IoTZero/Areas/IoT/Controllers/DeviceController.cs
index 086cae9..c0fa0d2 100644
--- a/IoTZero/Areas/IoT/Controllers/DeviceController.cs
+++ b/IoTZero/Areas/IoT/Controllers/DeviceController.cs
@@ -1,5 +1,6 @@
 using System.ComponentModel;
 using IoT.Data;
+using NewLife;
 using NewLife.Cube;
 using NewLife.Log;
 using NewLife.Web;
Modified +1 -0
diff --git a/IoTZero/Areas/IoT/Controllers/DeviceGroupController.cs b/IoTZero/Areas/IoT/Controllers/DeviceGroupController.cs
index ddb050d..09190f7 100644
--- a/IoTZero/Areas/IoT/Controllers/DeviceGroupController.cs
+++ b/IoTZero/Areas/IoT/Controllers/DeviceGroupController.cs
@@ -1,5 +1,6 @@
 using IoT.Data;
 using Microsoft.AspNetCore.Mvc;
+using NewLife;
 using NewLife.Cube;
 using NewLife.Web;
 using XCode.Membership;
Modified +10 -0
diff --git a/IoTZero/Areas/IoT/Controllers/DeviceHistoryController.cs b/IoTZero/Areas/IoT/Controllers/DeviceHistoryController.cs
index 1da5870..3f5ada2 100644
--- a/IoTZero/Areas/IoT/Controllers/DeviceHistoryController.cs
+++ b/IoTZero/Areas/IoT/Controllers/DeviceHistoryController.cs
@@ -1,5 +1,7 @@
 using IoT.Data;
+using NewLife;
 using NewLife.Cube;
+using NewLife.Cube.Extensions;
 using NewLife.Web;
 using XCode.Membership;
 
@@ -9,6 +11,14 @@ namespace IoTZero.Areas.IoT.Controllers;
 [Menu(60, true)]
 public class DeviceHistoryController : ReadOnlyEntityController<DeviceHistory>
 {
+    static DeviceHistoryController()
+    {
+        ListFields.RemoveField("Id");
+        ListFields.AddListField("Remark", null, "Success");
+
+        ListFields.TraceUrl();
+    }
+
     protected override IEnumerable<DeviceHistory> Search(Pager p)
     {
         var deviceId = p["deviceId"].ToInt(-1);
Modified +63 -2
diff --git a/IoTZero/Areas/IoT/Controllers/DeviceOnlineController.cs b/IoTZero/Areas/IoT/Controllers/DeviceOnlineController.cs
index 140afa0..2d4313c 100644
--- a/IoTZero/Areas/IoT/Controllers/DeviceOnlineController.cs
+++ b/IoTZero/Areas/IoT/Controllers/DeviceOnlineController.cs
@@ -1,6 +1,11 @@
-using IoT.Data;
+using System.ComponentModel;
+using IoT.Data;
+using Microsoft.AspNetCore.Mvc;
+using NewLife;
 using NewLife.Cube;
 using NewLife.Cube.ViewModels;
+using NewLife.Remoting.Extensions.Services;
+using NewLife.Remoting.Models;
 using NewLife.Web;
 using XCode.Membership;
 
@@ -11,11 +16,13 @@ namespace IoTZero.Areas.IoT.Controllers;
 [IoTArea]
 public class DeviceOnlineController : EntityController<DeviceOnline>
 {
+    private readonly IDeviceService _deviceService;
+
     static DeviceOnlineController()
     {
         //LogOnChange = true;
 
-        //ListFields.RemoveField("Id", "Creator");
+        ListFields.RemoveField("Token");
         ListFields.RemoveCreateField().RemoveRemarkField();
 
         {
@@ -34,6 +41,8 @@ public class DeviceOnlineController : EntityController<DeviceOnline>
         }
     }
 
+    public DeviceOnlineController(IDeviceService deviceService) => _deviceService = deviceService;
+
     /// <summary>高级搜索。列表页查询、导出Excel、导出Json、分享页等使用</summary>
     /// <param name="p">分页器。包含分页排序参数,以及Http请求参数</param>
     /// <returns></returns>
@@ -46,4 +55,56 @@ public class DeviceOnlineController : EntityController<DeviceOnline>
 
         return DeviceOnline.Search(null, productId, start, end, p["Q"], p);
     }
+
+    [DisplayName("检查更新")]
+    [EntityAuthorize((PermissionFlags)16)]
+    public async Task<ActionResult> CheckUpgrade()
+    {
+        var ts = new List<Task>();
+        foreach (var item in SelectKeys)
+        {
+            var online = DeviceOnline.FindById(item.ToInt());
+            if (online?.Device != null)
+            {
+                var cmd = new CommandModel
+                {
+                    Command = "device/upgrade",
+                    Expire = DateTime.UtcNow.AddSeconds(600),
+                };
+                ts.Add(_deviceService.SendCommand(online.Device, cmd, HttpContext.RequestAborted));
+            }
+        }
+
+        await Task.WhenAll(ts);
+
+        return JsonRefresh("操作成功!");
+    }
+
+    [DisplayName("执行命令")]
+    [EntityAuthorize((PermissionFlags)16)]
+    public async Task<ActionResult> Execute(String command, String argument)
+    {
+        if (GetRequest("keys") == null) throw new ArgumentNullException(nameof(SelectKeys));
+        if (command.IsNullOrEmpty()) throw new ArgumentNullException(nameof(command));
+
+        var ts = new List<Task<Int32>>();
+        foreach (var item in SelectKeys)
+        {
+            var online = DeviceOnline.FindById(item.ToInt());
+            if (online?.Device != null)
+            {
+                var cmd = new CommandModel
+                {
+                    Command = command,
+                    Argument = argument,
+                    Expire = DateTime.UtcNow.AddSeconds(30),
+                };
+                ts.Add(_deviceService.SendCommand(online.Device, cmd, HttpContext.RequestAborted));
+            }
+        }
+
+        var rs = await Task.WhenAll(ts);
+
+        return JsonRefresh($"操作成功!下发指令{rs.Length}个,成功{rs.Count(e => e > 0)}个");
+    }
 }
\ No newline at end of file
Modified +1 -0
diff --git a/IoTZero/Areas/IoT/Controllers/ProductController.cs b/IoTZero/Areas/IoT/Controllers/ProductController.cs
index ec5857c..40c83e2 100644
--- a/IoTZero/Areas/IoT/Controllers/ProductController.cs
+++ b/IoTZero/Areas/IoT/Controllers/ProductController.cs
@@ -1,4 +1,5 @@
 using IoT.Data;
+using NewLife;
 using NewLife.Cube;
 using NewLife.Web;
 
Added +12 -0
diff --git a/IoTZero/Areas/IoT/Views/DeviceOnline/_List_Search.cshtml b/IoTZero/Areas/IoT/Views/DeviceOnline/_List_Search.cshtml
new file mode 100644
index 0000000..fc05d4c
--- /dev/null
+++ b/IoTZero/Areas/IoT/Views/DeviceOnline/_List_Search.cshtml
@@ -0,0 +1,12 @@
+@using NewLife;
+@using NewLife.Web;
+@using XCode;
+@{
+    var fact = ViewBag.Factory as IEntityFactory;
+    var page = ViewBag.Page as Pager;
+}
+<div class="form-group">
+    <label for="productId" class="control-label">产品:</label>
+    @Html.ForDropDownList("productId", Product.FindAllWithCache(), page["productId"], "全部", true)
+</div>
+@await Html.PartialAsync("_Area2", "")
Added +21 -0
diff --git a/IoTZero/Areas/IoT/Views/DeviceOnline/_List_Toolbar_Batch.cshtml b/IoTZero/Areas/IoT/Views/DeviceOnline/_List_Toolbar_Batch.cshtml
new file mode 100644
index 0000000..efd35df
--- /dev/null
+++ b/IoTZero/Areas/IoT/Views/DeviceOnline/_List_Toolbar_Batch.cshtml
@@ -0,0 +1,21 @@
+@using NewLife.Common;
+@using System.Collections.Generic;
+@{
+    var set = ViewBag.PageSetting as PageSetting;
+    var page = ViewBag.Page as Pager;
+}
+@if (set.EnableSelect)
+{
+    <button type="button" class="btn btn-success btn-sm" data-action="action" data-url="@Url.Action("CheckUpgrade")" data-fields="keys" disabled>
+        检查更新
+    </button>
+    <div class="form-group">
+        <label for="command" class="control-label">命令:</label>
+        @Html.TextBox("command", page["command"])
+        <label for="argument" class="control-label">参数:</label>
+        @Html.TextBox("argument", page["argument"])
+    </div>
+    <button type="button" class="btn btn-purple btn-sm" data-action="action" data-url="@Url.Action("Execute")" data-fields="keys,command,argument">
+        执行命令
+    </button>
+}
\ No newline at end of file
Modified +1 -2
diff --git a/IoTZero/Controllers/AppController.cs b/IoTZero/Controllers/AppController.cs
index 7796929..b23784d 100644
--- a/IoTZero/Controllers/AppController.cs
+++ b/IoTZero/Controllers/AppController.cs
@@ -23,8 +23,7 @@ public class AppController : BaseController
     /// <summary>
     /// 实例化应用管理服务
     /// </summary>
-    /// <param name="queue"></param>
-    /// <param name="deviceService"></param>
+    /// <param name="serviceProvider"></param>
     /// <param name="thingService"></param>
     /// <param name="tracer"></param>
     public AppController(IServiceProvider serviceProvider, ThingService thingService, ITracer tracer) : base(serviceProvider)
Modified +6 -19
diff --git a/IoTZero/Controllers/DeviceController.cs b/IoTZero/Controllers/DeviceController.cs
index fdfe300..0af205f 100644
--- a/IoTZero/Controllers/DeviceController.cs
+++ b/IoTZero/Controllers/DeviceController.cs
@@ -7,6 +7,7 @@ using NewLife.IoT.ThingModels;
 using NewLife.Log;
 using NewLife.Remoting;
 using NewLife.Remoting.Extensions;
+using NewLife.Remoting.Extensions.Services;
 using NewLife.Remoting.Models;
 
 namespace IoTZero.Controllers;
@@ -20,18 +21,18 @@ public class DeviceController : BaseDeviceController
     /// <summary>当前设备</summary>
     public Device Device { get; set; }
 
+    private readonly MyDeviceService _deviceService;
     private readonly ThingService _thingService;
     private readonly ITracer _tracer;
 
     #region 构造
     /// <summary>实例化设备控制器</summary>
     /// <param name="serviceProvider"></param>
-    /// <param name="queue"></param>
-    /// <param name="deviceService"></param>
     /// <param name="thingService"></param>
     /// <param name="tracer"></param>
     public DeviceController(IServiceProvider serviceProvider, ThingService thingService, ITracer tracer) : base(serviceProvider)
     {
+        _deviceService = serviceProvider.GetRequiredService<IDeviceService>() as MyDeviceService;
         _thingService = thingService;
         _tracer = tracer;
     }
@@ -47,13 +48,12 @@ public class DeviceController : BaseDeviceController
     #endregion
 
     #region 心跳
-    /// <summary>设备心跳</summary>
+    /// <summary>心跳</summary>
     /// <param name="request"></param>
     /// <returns></returns>
-    [HttpPost(nameof(Ping))]
-    public override IPingResponse Ping([FromBody] IPingRequest request)
+    protected override IPingResponse OnPing(IPingRequest request)
     {
-        var rs = base.Ping(request);
+        var rs = base.OnPing(request);
 
         var device = Device;
         if (device != null && rs != null)
@@ -65,19 +65,6 @@ public class DeviceController : BaseDeviceController
     }
     #endregion
 
-    #region 升级
-    /// <summary>升级检查</summary>
-    /// <returns></returns>
-    [HttpGet(nameof(Upgrade))]
-    public override IUpgradeInfo Upgrade()
-    {
-        var device = Device ?? throw new ApiException(ApiCode.Unauthorized, "节点未登录");
-
-        //throw new NotImplementedException();
-        return new UpgradeInfo { };
-    }
-    #endregion
-
     #region 设备通道
     /// <summary>获取设备信息,包括主设备和子设备</summary>
     /// <returns></returns>
Modified +0 -14
diff --git a/IoTZero/IoTSetting.cs b/IoTZero/IoTSetting.cs
index 4d34d69..68fc8b5 100644
--- a/IoTZero/IoTSetting.cs
+++ b/IoTZero/IoTSetting.cs
@@ -15,20 +15,6 @@ public class IoTSetting : Config<IoTSetting>, ITokenSetting
     static IoTSetting() => Provider = new DbConfigProvider { UserId = 0, Category = "IoTServer" };
     #endregion
 
-    #region 属性
-    ///// <summary>MQTT服务端口。默认1883</summary>
-    //[Description("MQTT服务端口。默认1883")]
-    //public Int32 MqttPort { get; set; } = 1883;
-
-    ///// <summary>MQTT证书地址。设置了才启用安全连接,默认为空</summary>
-    //[Description("MQTT证书地址。设置了才启用安全连接,默认为空")]
-    //public String MqttCertPath { get; set; }
-
-    ///// <summary>MMQTT证书密码</summary>
-    //[Description("MQTT证书密码")]
-    //public String MqttCertPassword { get; set; }
-    #endregion
-
     #region 设备管理
     /// <summary>令牌密钥。用于生成JWT令牌的算法和密钥,如HS256:ABCD1234</summary>
     [Description("令牌密钥。用于生成JWT令牌的算法和密钥,如HS256:ABCD1234")]
Modified +6 -6
diff --git a/IoTZero/IoTZero.csproj b/IoTZero/IoTZero.csproj
index a000fd8..437661f 100644
--- a/IoTZero/IoTZero.csproj
+++ b/IoTZero/IoTZero.csproj
@@ -29,12 +29,12 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="NewLife.Cube.Core" Version="6.1.2024.403" />
-    <PackageReference Include="NewLife.IoT" Version="2.2.2024.501" />
-    <PackageReference Include="NewLife.MQTT" Version="2.0.2024.516" />
-    <PackageReference Include="NewLife.Redis" Version="5.7.2024.602" />
-    <PackageReference Include="NewLife.Remoting.Extensions" Version="3.0.2024.620-beta1407" />
-    <PackageReference Include="NewLife.Stardust.Extensions" Version="2.9.2024.402" />
+    <PackageReference Include="NewLife.Cube.Core" Version="6.4.2025.513" />
+    <PackageReference Include="NewLife.IoT" Version="2.4.2025.501" />
+    <PackageReference Include="NewLife.MQTT" Version="2.0.2025.415" />
+    <PackageReference Include="NewLife.Redis" Version="6.2.2025.503" />
+    <PackageReference Include="NewLife.Remoting.Extensions" Version="3.3.2025.501" />
+    <PackageReference Include="NewLife.Stardust.Extensions" Version="3.3.2025.506" />
   </ItemGroup>
 
   <ItemGroup>
Modified +13 -24
diff --git a/IoTZero/Program.cs b/IoTZero/Program.cs
index f8dede0..ba31b2f 100644
--- a/IoTZero/Program.cs
+++ b/IoTZero/Program.cs
@@ -1,13 +1,18 @@
 using IoTZero;
 using IoTZero.Services;
-using NewLife.Caching;
 using NewLife.Cube;
 using NewLife.Log;
+using NewLife.Reflection;
+using NewLife.Remoting.Extensions;
 using XCode;
 
 // 日志输出到控制台,并拦截全局异常
 XTrace.UseConsole();
 
+#if DEBUG
+XTrace.Log.Level = NewLife.Log.LogLevel.Debug;
+#endif
+
 var builder = WebApplication.CreateBuilder(args);
 var services = builder.Services;
 
@@ -20,21 +25,13 @@ var star = services.AddStardust(null);
 var set = IoTSetting.Current;
 services.AddSingleton(set);
 
-// 逐个注册每一个用到的服务,必须做到清晰明了
-services.AddSingleton<ThingService>();
-services.AddSingleton<DataService>();
-services.AddSingleton<QueueService>();
+// 注册Redis缓存提供者
+//services.AddSingleton<ICacheProvider, RedisCacheProvider>();
 
-// 注册IoT
+// 注册Remoting所必须的服务
 services.AddIoT(set);
 //services.AddRemoting(set);
 
-services.AddSingleton<ICache, MemoryCache>();
-
-// 后台服务
-services.AddHostedService<ShardTableService>();
-services.AddHostedService<DeviceOnlineService>();
-
 // 启用接口响应压缩
 services.AddResponseCompression();
 
@@ -45,24 +42,16 @@ services.AddCube();
 
 var app = builder.Build();
 
-// 预热数据层,执行反向工程建表等操作
-EntityFactory.InitConnection("Membership");
-EntityFactory.InitConnection("Log");
-EntityFactory.InitConnection("Cube");
-EntityFactory.InitConnection("IoT");
-
 // 使用Cube前添加自己的管道
 if (app.Environment.IsDevelopment())
     app.UseDeveloperExceptionPage();
 else
     app.UseExceptionHandler("/CubeHome/Error");
 
-app.UseResponseCompression();
+if (Environment.GetEnvironmentVariable("__ASPNETCORE_BROWSER_TOOLS") is null)
+    app.UseResponseCompression();
 
-app.UseWebSockets(new WebSocketOptions()
-{
-    KeepAliveInterval = TimeSpan.FromSeconds(60),
-});
+app.UseRemoting();
 
 // 使用魔方
 app.UseCube(app.Environment);
@@ -73,7 +62,7 @@ app.MapControllerRoute(
     name: "default",
     pattern: "{controller=CubeHome}/{action=Index}/{id?}");
 
-app.RegisterService("AlarmServer", null, app.Environment.EnvironmentName);
+app.RegisterService(star.AppId, null, app.Environment.EnvironmentName);
 
 app.Run();
 
Modified +3 -2
diff --git a/IoTZero/Services/DeviceOnlineService.cs b/IoTZero/Services/DeviceOnlineService.cs
index edcd9d5..d5de1a6 100644
--- a/IoTZero/Services/DeviceOnlineService.cs
+++ b/IoTZero/Services/DeviceOnlineService.cs
@@ -1,6 +1,7 @@
 using IoT.Data;
 using NewLife;
 using NewLife.Log;
+using NewLife.Remoting.Extensions.Models;
 using NewLife.Remoting.Extensions.Services;
 using NewLife.Threading;
 
@@ -12,7 +13,7 @@ public class DeviceOnlineService : IHostedService
     #region 属性
     private TimerX _timer;
     private readonly IDeviceService _deviceService;
-    private readonly IoTSetting _setting;
+    private readonly ITokenSetting _setting;
     private readonly ITracer _tracer;
     #endregion
 
@@ -23,7 +24,7 @@ public class DeviceOnlineService : IHostedService
     /// <param name="deviceService"></param>
     /// <param name="setting"></param>
     /// <param name="tracer"></param>
-    public DeviceOnlineService(IDeviceService deviceService, IoTSetting setting, ITracer tracer)
+    public DeviceOnlineService(IDeviceService deviceService, ITokenSetting setting, ITracer tracer)
     {
         _deviceService = deviceService;
         _setting = setting;
Modified +28 -0
diff --git a/IoTZero/Services/IoTExtensions.cs b/IoTZero/Services/IoTExtensions.cs
index e8cfd5e..c1c6d8e 100644
--- a/IoTZero/Services/IoTExtensions.cs
+++ b/IoTZero/Services/IoTExtensions.cs
@@ -9,10 +9,27 @@ namespace IoTZero.Services;
 /// <summary>IoT扩展</summary>
 public static class IoTExtensions
 {
+    /// <summary>添加IoT客户端服务端架构服务,支持客户端登录、心跳、更新以及指令下发等操作</summary>
+    /// <remarks>
+    /// 注册登录心跳等模型类,可在此扩展模型类,传输更多内容;
+    /// 注册IDeviceService服务,提供登录心跳等基础实现;
+    /// 注册TokenService令牌服务,提供令牌颁发与验证服务;
+    /// 注册密码提供者,用于通信过程中保护密钥,避免明文传输;
+    /// 注册缓存提供者的默认实现;
+    /// 注册节点在线后台服务,定时检查节点在线状态;
+    /// </remarks>
+    /// <param name="services"></param>
+    /// <param name="setting"></param>
+    /// <returns></returns>
     public static IServiceCollection AddIoT(this IServiceCollection services, ITokenSetting setting)
     {
         ArgumentNullException.ThrowIfNull(setting);
 
+        // 逐个注册每一个用到的服务,必须做到清晰明了
+        services.AddSingleton<ThingService>();
+        services.AddSingleton<DataService>();
+        services.AddSingleton<QueueService>();
+
         services.AddSingleton<IDeviceService, MyDeviceService>();
 
         services.AddTransient<ILoginRequest, LoginInfo>();
@@ -21,6 +38,17 @@ public static class IoTExtensions
         // 注册Remoting所必须的服务
         services.AddRemoting(setting);
 
+        // 后台服务
+        services.AddHostedService<ShardTableService>();
+        services.AddHostedService<DeviceOnlineService>();
+
         return services;
     }
+
+    /// <summary>使用IoT客户端服务端架构服务,启用WebSocket</summary>
+    /// <param name="app"></param>
+    public static void UseIoT(this IApplicationBuilder app)
+    {
+        app.UseRemoting();
+    }
 }
Modified +76 -28
diff --git a/IoTZero/Services/MyDeviceService.cs b/IoTZero/Services/MyDeviceService.cs
index 9abcb0a..3323bf8 100644
--- a/IoTZero/Services/MyDeviceService.cs
+++ b/IoTZero/Services/MyDeviceService.cs
@@ -2,16 +2,16 @@
 using IoT.Data;
 using NewLife;
 using NewLife.Caching;
-using NewLife.Caching.Queues;
 using NewLife.IoT.Models;
 using NewLife.Log;
 using NewLife.Remoting;
+using NewLife.Remoting.Extensions.Models;
 using NewLife.Remoting.Extensions.Services;
 using NewLife.Remoting.Models;
+using NewLife.Remoting.Services;
 using NewLife.Security;
 using NewLife.Serialization;
 using NewLife.Web;
-using LoginResponse = NewLife.Remoting.Models.LoginResponse;
 
 namespace IoTZero.Services;
 
@@ -20,20 +20,21 @@ public class MyDeviceService : IDeviceService
 {
     private readonly ICacheProvider _cacheProvider;
     private readonly ICache _cache;
+    private readonly ISessionManager _sessionManager;
     private readonly IPasswordProvider _passwordProvider;
-    private readonly IoTSetting _setting;
+    private readonly ITokenSetting _setting;
     private readonly ITracer _tracer;
 
     /// <summary>
     /// 实例化设备服务
     /// </summary>
     /// <param name="passwordProvider"></param>
-    /// <param name="dataService"></param>
     /// <param name="cacheProvider"></param>
     /// <param name="setting"></param>
     /// <param name="tracer"></param>
-    public MyDeviceService(IPasswordProvider passwordProvider, ICacheProvider cacheProvider, IoTSetting setting, ITracer tracer)
+    public MyDeviceService(ISessionManager sessionManager, IPasswordProvider passwordProvider, ICacheProvider cacheProvider, ITokenSetting setting, ITracer tracer)
     {
+        _sessionManager = sessionManager;
         _passwordProvider = passwordProvider;
         _cacheProvider = cacheProvider;
         _cache = cacheProvider.InnerCache;
@@ -41,7 +42,7 @@ public class MyDeviceService : IDeviceService
         _tracer = tracer;
     }
 
-    #region 登录
+    #region 登录注销
     /// <summary>
     /// 设备登录验证,内部支持动态注册
     /// </summary>
@@ -89,7 +90,7 @@ public class MyDeviceService : IDeviceService
 
         //if (dv != null && !dv.Enable) throw new ApiException(99, "禁止登录");
 
-        if (dv == null) throw new ApiException(ApiCode.Unauthorized, "节点鉴权失败");
+        if (dv == null) throw new ApiException(ApiCode.Unauthorized, "登录失败");
 
         dv.Login(inf, ip);
 
@@ -100,7 +101,7 @@ public class MyDeviceService : IDeviceService
         //SetChildOnline(dv, ip);
 
         // 登录历史
-        WriteHistory(dv, source + "设备鉴权", true, $"[{dv.Name}/{dv.Code}]鉴权成功 " + inf.ToJson(false, false, false), ip);
+        WriteHistory(dv, source + "登录", true, $"[{dv.Name}/{dv.Code}]登录成功 " + inf.ToJson(false, false, false), ip);
 
         var rs = new LoginResponse
         {
@@ -218,13 +219,14 @@ public class MyDeviceService : IDeviceService
     }
     #endregion
 
-    #region 心跳
+    #region 心跳保活
     /// <summary>心跳</summary>
-    /// <param name="inf"></param>
+    /// <param name="device"></param>
+    /// <param name="request"></param>
     /// <param name="token"></param>
     /// <param name="ip"></param>
     /// <returns></returns>
-    public IOnlineModel Ping(IDeviceModel device, IPingRequest? request, String token, String ip)
+    public IOnlineModel Ping(IDeviceModel device, IPingRequest request, String token, String ip)
     {
         var dv = device as Device;
         var inf = request as PingInfo;
@@ -245,11 +247,35 @@ public class MyDeviceService : IDeviceService
         return olt;
     }
 
+    /// <summary>设置设备的长连接上线/下线</summary>
+    /// <param name="device"></param>
+    /// <param name="online"></param>
+    /// <param name="token"></param>
+    /// <param name="ip"></param>
+    /// <returns></returns>
+    public IOnlineModel SetOnline(IDeviceModel device, Boolean online, String token, String ip)
+    {
+        if (device is Device dv)
+        {
+            // 上线打标记
+            var olt = GetOnline(dv, ip);
+            if (olt != null)
+            {
+                olt.WebSocket = online;
+                olt.Update();
+            }
+
+            return olt;
+        }
+
+        return null;
+    }
+
     /// <summary></summary>
     /// <param name="device"></param>
     /// <param name="ip"></param>
     /// <returns></returns>
-    protected virtual DeviceOnline GetOnline(Device device, String ip)
+    public virtual DeviceOnline GetOnline(Device device, String ip)
     {
         var sid = $"{device.Id}@{ip}";
         var olt = _cache.Get<DeviceOnline>($"DeviceOnline:{sid}");
@@ -266,7 +292,7 @@ public class MyDeviceService : IDeviceService
     /// <param name="device"></param>
     /// <param name="ip"></param>
     /// <returns></returns>
-    protected virtual DeviceOnline CreateOnline(Device device, String ip)
+    public virtual DeviceOnline CreateOnline(Device device, String ip)
     {
         var sid = $"{device.Id}@{ip}";
         var olt = DeviceOnline.GetOrAdd(sid);
@@ -295,6 +321,41 @@ public class MyDeviceService : IDeviceService
     }
     #endregion
 
+    #region 升级更新
+    /// <summary>升级检查</summary>
+    /// <param name="channel">更新通道</param>
+    /// <returns></returns>
+    public IUpgradeInfo Upgrade(IDeviceModel device, String channel, String ip)
+    {
+        //return new UpgradeInfo();
+        return null;
+    }
+    #endregion
+
+    #region 下行通知
+    /// <summary>发送命令</summary>
+    /// <param name="device"></param>
+    /// <param name="command"></param>
+    /// <returns></returns>
+    public Task<Int32> SendCommand(IDeviceModel device, CommandModel command, CancellationToken cancellationToken) => _sessionManager.PublishAsync(device.Code, command, null, cancellationToken);
+    #endregion
+
+    #region 事件上报
+    /// <summary>命令响应</summary>
+    /// <param name="device"></param>
+    /// <param name="model"></param>
+    /// <param name="ip"></param>
+    /// <returns></returns>
+    public Int32 CommandReply(IDeviceModel device, CommandReplyModel model, String ip) => 0;
+
+    /// <summary>上报事件</summary>
+    /// <param name="device"></param>
+    /// <param name="events"></param>
+    /// <param name="ip"></param>
+    /// <returns></returns>
+    public Int32 PostEvents(IDeviceModel device, EventModel[] events, String ip) => 0;
+    #endregion
+
     #region 辅助
     /// <summary>
     /// 颁发令牌
@@ -302,7 +363,7 @@ public class MyDeviceService : IDeviceService
     /// <param name="name"></param>
     /// <param name="set"></param>
     /// <returns></returns>
-    public TokenModel IssueToken(String name, IoTSetting set)
+    public TokenModel IssueToken(String name, ITokenSetting set)
     {
         // 颁发令牌
         var ss = set.TokenSecret.Split(':');
@@ -359,7 +420,7 @@ public class MyDeviceService : IDeviceService
     /// <param name="deviceCode"></param>
     /// <param name="token"></param>
     /// <returns></returns>
-    public TokenModel ValidAndIssueToken(String deviceCode, String? token)
+    public TokenModel ValidAndIssueToken(String deviceCode, String token)
     {
         if (token.IsNullOrEmpty()) return null;
 
@@ -378,19 +439,6 @@ public class MyDeviceService : IDeviceService
         return null;
     }
 
-    /// <summary>
-    /// 获取指定设备的命令队列
-    /// </summary>
-    /// <param name="deviceCode"></param>
-    /// <returns></returns>
-    public IProducerConsumer<String> GetQueue(String deviceCode)
-    {
-        var q = _cacheProvider.GetQueue<String>($"cmd:{deviceCode}");
-        if (q is QueueBase qb) qb.TraceName = "ServiceQueue";
-
-        return q;
-    }
-
     /// <summary>查找设备</summary>
     /// <param name="code"></param>
     /// <returns></returns>
Modified +5 -4
diff --git a/IoTZero/Services/ThingService.cs b/IoTZero/Services/ThingService.cs
index 6e333ad..b211b0c 100644
--- a/IoTZero/Services/ThingService.cs
+++ b/IoTZero/Services/ThingService.cs
@@ -4,6 +4,7 @@ using NewLife.Caching;
 using NewLife.Data;
 using NewLife.IoT.ThingModels;
 using NewLife.Log;
+using NewLife.Remoting.Extensions.Models;
 using NewLife.Remoting.Extensions.Services;
 using NewLife.Security;
 
@@ -16,7 +17,7 @@ public class ThingService
     private readonly QueueService _queueService;
     private readonly IDeviceService _deviceService;
     private readonly ICacheProvider _cacheProvider;
-    private readonly IoTSetting _setting;
+    private readonly ITokenSetting _setting;
     private readonly ITracer _tracer;
     static Snowflake _snowflake = new();
 
@@ -25,13 +26,11 @@ public class ThingService
     /// </summary>
     /// <param name="dataService"></param>
     /// <param name="queueService"></param>
-    /// <param name="ruleService"></param>
-    /// <param name="segmentService"></param>
     /// <param name="deviceService"></param>
     /// <param name="cacheProvider"></param>
     /// <param name="setting"></param>
     /// <param name="tracer"></param>
-    public ThingService(DataService dataService, QueueService queueService, IDeviceService deviceService, ICacheProvider cacheProvider, IoTSetting setting, ITracer tracer)
+    public ThingService(DataService dataService, QueueService queueService, IDeviceService deviceService, ICacheProvider cacheProvider, ITokenSetting setting, ITracer tracer)
     {
         _dataService = dataService;
         _queueService = queueService;
@@ -294,6 +293,8 @@ public class ThingService
         // 挂起等待。借助redis队列,等待响应
         if (timeout > 1000)
         {
+            await Task.Delay(1000);
+
             throw new NotImplementedException();
         }