TTL的好处与必要性spring-boot-cnbjvm-application-cnb

[] is equal ![]true is not equal ![], but not equal [] tooNaN is not a NaNObject.is() and === weird cases[] is truthy, but not truenull is falsy, but not falsedocument.all is an object, but it is undefinedundefined and NumberparseInt is a bad guytrue and falseNaN is [] and null are objects0.1 + 0.2Stringconstructor property__proto__`${{Object}}`try..catcharguments and arrow functionsNumber.toFixed() display different numbersMath.max() less than Math.min()null to 0{}{} is undefinedarguments bindingalert from hellsetTimeout objecttrue
Getting Started
Supporting the project
Major version changelog
Resources
Responsible disclosure
CamoFormer: Masked Separable Attention for Camouflaged Object Detection

👋 Olá! Eu sou o Pedro Chaves
Upcoming Maintenance Mode on February 25, 2026
Aquila :eagle:Uniqness_map for Aquila, check the details of hoffmanMappability to get the corresponding “k100.umap.bed.gz”, then run Aquila/bin/Get_uniqnessmap_for_Aquila.py to get the final Uniqness_map folder to run Aquila.Uniqness_map folder to run Aquila, check How_to_get_Umap for details.piawka DataRangers SDK - mp22.2.0| 版本号 | 变更类型 | 功能描述说明 |
|---|---|---|
| 1.3.4 | 功能增强 | 1.优化日志打印 2.oc开头SubscribeTopic 返回 topic 3.demo优化 4.网关接口bug修复 5. 升级目标框架。 以及其它优化 |
| 1.3.3 | 新增功能 | OTA升级支持网关模式 |
| 1.3.2 | 功能增强 | 更新服务器ca证书 |
| 1.3.1 | 修复 | 修复空指针异常,MQTT对象未释放等问题 |
| 1.3.0 | 新功能 | 支持通过OBS升级软固件包 |
| 1.2.0 | 新功能 | 增加泛协议功能 |
| 1.1.1 | 功能增强 | 添加网关删除子设备功能,完善中英文描述 |
| 1.1.0 | 新功能 | 新增网关与物模型功能 |
| 1.0.0 | 第一次发布 | 提供基础的设备接入能力,sdk预置了设备接入地址及华为IoTDA平台配套的CA证书 |
本文通过实例讲述iot-device-sdk-cSharp(以下简称SDK)帮助用户将设备经MQTT协议快速连接到华为IoTDA服务。
要进一步了解本sdk中的接口对应的MQTT接口,您可以查看 用户指南。
SDK面向运算、存储能力较强的嵌入式终端设备,开发者通过调用SDK接口,便可实现设备与IoTDA服务(后续简出现的“服务”、“平台“均指IoTDA服务)双向行通讯。 SDK当前支持的功能有:
| 功能 | 描述说明 |
|---|---|
| 设备连接鉴权 | 作为客户端使用MQTT协议接入到平台。分为证书鉴权与密钥鉴权两种认证方式。 |
| 断线重连 | 当设备由于网络不稳定 |
| 消息上报 | 用于设备将自定义数据上报给平台,平台将设备上报的消息转发给应用服务器或华为云其他云服务上进行存储和处理。 |
| 属性上报 | 用于设备按产品模型中定义的格式将属性数据上报给平台。 |
| 命令下发 | 用于平台向设备下发设备控制命令。平台下发命令后,需要设备及时将命令的执行结果返回给平台。 |
| 设备影子 | 用于存储设备的在线状态、设备最近一次上报的设备属性值、应用服务器期望下发的配置。 |
| 软固件(OTA)升级 | 用于与平台配合下载OTA升级包。 |
| 时间同步 | 设备向平台发起时间同步请求。 |
| 网关与子设备 | 网关设备:通过平台支持的协议,直接连接到平台的设备。子设备:针对未实现TCP/IP协议栈的设备,由于无法直接同平台通信,它需要通过网关进行数据转发。当前仅支持通过mqtt协议直连到平台的设备作为网关设备。 |
| 文件上传/下载 | 支持设备将运行日志,配置信息等文件上传至平台,便于用户进行日志分析、故障定位、设备数据备份等。 |
| 异常检测 | 提供安全检测能力,可持续检测设备的安全威胁。包括:1、内存泄漏检测 2、异常端口检测3、CPU使用率检测 4、磁盘空间检测 5、电池电量检测 |
| 规则引擎 | 通过条件触发,基于预设的规则,引发多设备的协同反应,实现设备联动、智能控制。目前平台支持两种联动规则:云端规则和端侧规则。 |
| 远程配置 | 提供远程配置功能,用户可用于在不中断设备运行的情况下,远程更新设备的系统参数、运行参数等配置信息。 |
| 远程登录 | 支持通过控制台远程SSH登录设备,可在控制台输入设备支持的命令,进行功能调试及问题定位,从而方便地实现设备管理及远程运维 |
| 泛协议接入 | 当非HTTP、MQTT、LWM2M等第三方协议接入时,需要在平台外部完成协议转换。推荐使用网关来完成协议转换,将第三方协议转成MQTT协议。 |
| 设备发放 | 分为证书认证、密钥认证。主要用于分发到不同局点、实例的场景,动态完成不同批次设备初始化配置。 |
本章将使用SmokeDetector Demo 指导您快速体验产品模型的创建、Demo的配置与启动、以及SDK基本功能的使用。
为了方便体验,我们提供了一个烟感的产品模型,烟感会上报烟雾值、温度、湿度、烟雾报警、还支持响铃报警命令。 下面的子章节将以烟感为例,体验消息上报、属性上报等功能。 下面创建产品模型的流程也可以参考向导式体验智慧烟感接入平台。
访问IoTDA平台并进入设备接入控制台,如果是第一次使用需要注册并登录.
登录成功后自动跳转,在设备接入控制台选择“产品”,单击右上角的“创建产品”,在弹出的页面中,填写“产品名称”、“协议类型”、“数据格式”、“厂商名称”、“所属行业”、“设备类型”等信息,然后点击右下角“立即创建”。

产品创建成功后,单击“详情”进入产品详情,在功能定义页面,单击“上传模型文件”,上传烟感产品模型

点击“设备”->
“所有设备”,点击右上角“注册设备”,选择产品所在的“资源空间”,选择上方创建的产品,填写设备标识码(一般是IMEI、MAC地址等),自定义“设备名称”,“密钥”如果不自定义,平台会自动生成。全部填写完毕后,点击“确定”。

可以直接复制设备秘钥,点击“保存并关闭”将自动将设备ID以txt文本形式下载到本地。

点击”设备“->“所有设备”,在最上方可看到该设备的状态是未激活。

获取接入地址,可在控制台的“总览”->“接入信息”中查看。

打开文件 iot-device-demo/config/DemoConfigDefault.json,将之前获取到的接入地址、设备ID以及设备密钥填入对应位置,或者将配置填入下面模板后再覆盖iot-device-demo/config/DemoConfigDefault.json:
{
"DemoName": "SmokeDetector",
"AuthConfig": {
"AuthUseCert": false,
"ServerAddress": "填入接入地址",
"ServerPort": 8883,
"DeviceId": "填入设备ID",
"DeviceSecret": "填入设备密钥"
}
}
其中DemoName用于指定运行的Demo,此处为SmokeDetector指定基于烟雾报警器产品模型的物模型编程Demo。该Sdk能运行的Demo名称可以参后面章节,几乎每一个功能点介绍的小节最前面会标注Demo名称。或者进入sdk目录下运行下面命令查看所有Demo名称列表:
cd iot-device-demo
dotnet run --project .\iot-device-demo.csproj

编译&运行
进入sdk目录下,执行下面命令运行demo,命令末尾加参数为配置文件路径:
cd iot-device-demo
dotnet run --project .\iot-device-demo.csproj .\config\DemoConfigDefault.json
如果您使用旧版本5.0+的SDK并且无法升级到.NET 8.0,可以修改以下文件的TargetFramework。但请注意,目前除了.NET 8.0和.NET 6.0属于长期支持的版本外,其它更旧版版本均已不再支持:

上面使用run命令会自动编译,如果要手动编译某一个project,可以行下面命令:
dotnet clean
dotnet build
输出示例如下,如果输出里有connect success则表示连接成功:

查看设备运行情况
在控制台筛选出该设备并进入设备详情页面
详情页下放”物模型数据“页面可以看到最新上报的数据

密钥鉴权支持mqtt(1883)和mqtts(8883)接入,为安全起见,推荐使用8883端口接入平台。使用8883端口接入时需要用到iot-device-demo/config/certificate/DigiCertGlobalRootCA.crt.pem,该文件默认已预置各个region的根证书,如果连接不成功则尝试从这里下载对应region的pem证书并替换。
使用Demo时时配置文件iot-device-demo/config/DemoConfigDefault.json中对应的配置项如下(无关配置项省略):
{
"AuthConfig": {
"AuthUseCert": false,
"ServerAddress": "接入地址",
"ServerPort": 8883,
"DeviceId": "设备Id",
"DeviceSecret": "设备密钥"
}
}
使用API实例化设备对象时参数如下:
// 接入地址,接入端口,设备ID, 设备密钥
IoTDevice device = new IoTDevice("接入地址", 8883, "设备Id", "设备密钥");
平台支持设备使用自己的X.509证书进行设备接入认证,使用前需要注册证书认证的设备并在设备接入控制台预置对应CA证书,详细指导请参考注册X.509证书认证的设备。
制作完调测证书后,参考以下命令转换成pfx格式:
openssl x509 -in deviceCert.pem -out deviceCert.crt #先生成crt格式的证书
openssl pkcs12 -export -out deviceCert.pfx -inkey deviceCert.key -in deviceCert.crt -certfile rootCA.pem
使用Demo时,把
iot-device-demo/config/DemoConfigDefault.json里的AuthUseCert改为true,同时DeviceCert改为生成的证书路径,示例如下:
{
"AuthConfig": {
"AuthUseCert": true,
"DeviceCert": "\config\certificate\deviceCert.pfx"
}
}
示例中证书路径为config/certificate/deviceCert.pfx,部分IDE(如Rider)会在Debug文件中运行程序,您需要在iot-device-demo/CoreCapability/iot-device-demo.csproj添加一行
<Content Include="config\certificate\deviceCert.pfx" CopyToOutputDirectory="PreserveNewest" />之后编译时证书也会被拷贝到Debug目录下。
如果要程序化创建设备,可以参考iot-device-demo/DemoUtil/DemoDeviceHelper.cs中的
CreateDevice函数。
设备命令由两步骤组成:
命令下发
命令下发由平台向设备发送,下发方式参考 命令下发使用示例, 或则使用CreateCommand API。
命令下发时设置对应listener用来接收平台下发的命令:
public class CommandSample : DeviceSample, CommandListener
{
// ......
protected override void BeforeInitDevice()
{
Device.GetClient().commandListener = this;
// ......
实现回调接口OnCommand对命令进行处理:
public void OnCommand(string requestId, string serviceId, string commandName, Dictionary<string, object> paras)
{
LOG.Info("onCommand, serviceId = {}", serviceId);
LOG.Info("onCommand, name = {}", commandName);
LOG.Info("onCommand, paras = {}", JsonUtil.ConvertObjectToJsonString(paras));
// ......
}
命令响应
紧接着命令处理完毕后,调用RespondCommand上报响应用于确认执行结果:
public void OnCommand(string requestId, string serviceId, string commandName, Dictionary<string, object> paras)
{
// ......
// Sends a command response.
Device.GetClient().RespondCommand(requestId, new CommandRsp(respCode, respParams));
}
消息是否上报到平台可以通过 消息跟踪查看, 或则使用ListDeviceMessages API,ShowDeviceMessage API。
调用ReportDeviceMessage上报预定格式的消息
// {"content":"hello word","object_device_id":null,"name":null,"id":null}
Device.GetClient().ReportDeviceMessage(new DeviceMessage("hello word", testMqttV5Data));
调用ReportRawDeviceMessage上报任意格式的消息:
// hello word
Device.GetClient().ReportRawDeviceMessage(new RawDeviceMessage("hello word", testMqttV5Data));
消息下发由平台向设备发送,下发方式参考 平台消息下发使用示例, 或则使用CreateMessage API。
SDK中需要进行以下操作:
实现RawDeviceMessageListener的类并创建实例同时设置其到对应的listener以监听下发消息:
protected override void BeforeInitDevice()
{
Device.GetClient().rawDeviceMessageListener = this;
}
在public void OnRawDeviceMessage(RawDeviceMessage message)实现消息处理逻辑:
public void OnRawDeviceMessage(RawDeviceMessage message)
{
var deviceMessage = message.ToDeviceMessage();
if (deviceMessage == null)
{
//....
}
else
{
//....
}
}
RawDeviceMessage本身只存储二进制payload,为了方便用户操作,此类提供下面的方法:
public string StrPayload以UTF8将字符串编码并存入payload,或则以UTF8解码payload为字符串并读取。
public string ToUtf8String()以UTF8解码payload为字符串并读取。
public DeviceMessage ToDeviceMessage()尝试将payload反序列化为预设格式DeviceMessage的对象,如果失败则返回null。
要使用自定topic,平台也需要进行相应配置,详细可阅读自定义Topic通信概述。本SDK自定义topic在断链时会自动重新订阅。
消息是否上报到平台可以通过 消息跟踪功能检测, 或则使用ListDeviceMessages API,ShowDeviceMessage API。
自定义topic消息分为$oc前缀和非$oc前缀两种,均通过创建CustomMessageTopic时通过设置OcPrefix成员指定:
// topic: "$oc/devices/{device-id}/user/ai"
private static readonly CustomMessageTopic OC_CUSTOM_TOPIC = new CustomMessageTopic
{ Suffix = "ai", OcPrefix = true };
// topic: "/ai/test/device/to/cloud"
private static readonly CustomMessageTopic NO_OC_CUSTOM_TOPIC = new CustomMessageTopic
{ Suffix = "/ai/test/device/to/cloud", OcPrefix = false };
调用ReportRawDeviceMessage(RawDeviceMessage, CustomMessageTopic)可以上报消息到自定topic:
private void RunCustomTopicDemo()
{
//....
// Reports a message with $oc custom topic.
Device.GetClient()
.ReportRawDeviceMessage(new RawDeviceMessage("hello $oc custom topic"), OC_CUSTOM_TOPIC);
// Reports a message with non-$oc custom topic.
Device.GetClient()
.ReportRawDeviceMessage(new RawDeviceMessage("hello non-$oc custom topic"), NO_OC_CUSTOM_TOPIC);
}
要使用自定topic,平台也需要进行相应配置,详细可阅读自定义Topic通信概述。本SDK自定义topic在断链时会自动重新订阅。
自定义topic消息下发由平台向设备发送,下发方式参考 平台消息下发使用示例 或则使用CreateMessage API。
接收自定义topic的消息前需要使用SubscribeTopic(CustomMessageTopic)订阅自定义topic:
private void RunCustomTopicDemo()
{
// You must configure the custom topic on the platform and set the topic prefix to $oc/devices/{device_id}/user/.
// Use Postman to simulate the scenario in which an application uses the custom topic to deliver a command.
Device.GetClient().SubscribeTopic(NO_OC_CUSTOM_TOPIC);
fullNoOcCustomTopic = Device.GetClient().SubscribeTopic(OC_CUSTOM_TOPIC);
// ......
}
然后实现RawDeviceMessageListener接口、创建其实例、设置其到对应的listener以监听下发消息:
protected override void BeforeInitDevice()
{
Device.GetClient().rawDeviceMessageListener = this;
}
在public void OnCustomRawDeviceMessage(string topic, bool topicStartsWithOc, RawDeviceMessage rawDeviceMessage)中实现处理消息的逻辑:
void OnCustomRawDeviceMessage(string topic, bool topicStartsWithOc, RawDeviceMessage rawDeviceMessage)
{
LOG.Info("custom message received, topic:{}, topic starts with oc:{}, data:{}",
topic, topicStartsWithOc, rawDeviceMessage.payload);
}
设备最近上报的属性可以在“设备详情”页面查看,详细可参考设备属性上报的使用说明
调用ReportProperties(List<ServiceProperty>)按产品模型中定义的格式将属性数据上报给平台:
Device.GetClient().ReportProperties(GenerateMockProperty());
上报成功后输出日志样例如下:
2024-06-28 14:31:50.6884 | Info | IoT.Device.Demo.PropertyReportSample.OnMessagePublished:69 - pubSuccessMessage: "{"services":[{"service_id":"smokeDetector","properties":{"alarm":1,"temperature":23.45812,"humidity":56.89013,"smokeConcentration":89.5672},"eventTime":null}]}"
设置设备属性请求由平台下发,需要通过UpdateProperties API触发。
实现PropertyListener接口并创建实例,同时设置其到对应的listener以监听设置属性的请求:
protected override void BeforeInitDevice()
{
Device.GetClient().messagePublishListener = this;
}
再在void OnPropertiesSet(string requestId, List<ServiceProperty> services)中实现修改设备上对应属性的逻辑。 处理完成后调用void RespondPropsSet(string requestId, IotResult iotResult)响应设置结果:
public void OnPropertiesSet(string requestId, List<ServiceProperty> services)
{
LOG.Info("set properties request id: {}", requestId);
LOG.Info("set properties services: {}", JsonUtil.ConvertObjectToJsonString(services));
Device.GetClient().RespondPropsSet(requestId, IotResult.SUCCESS);
}
查询设备属性请求由平台下发,需要通过ListProperties API触发。
创建监听器:实现PropertyListener接口并创建实例,同时设置其到对应的listener以监听设置属性的请求:
protected override void BeforeInitDevice()
{
Device.GetClient().messagePublishListener = this;
}
处理查询设备属性请求并响应: 在public void OnPropertiesGet(string requestId, string serviceId)
中实现获取serviceId对应属性的逻辑,之后调用void RespondPropsGet(string requestId, List<ServiceProperty> services)响应。
public void OnPropertiesGet(string requestId, string serviceId)
{
LOG.Info("requestId Get: {}", requestId);
LOG.Info("serviceId Get: {}", serviceId);
//.......
Device.GetClient().RespondPropsGet(requestId, properties);
}
请求设备影子:调用GetShadow(DeviceShadowRequest shadowRequest, string requestId = null)
接口上报获取设备请求,DeviceShadowRequest中的ServiceId不填时表示获取所有服务的影子数据,requestId不填时会自动生成并作为返回值:
protected override void RunDemo()
{
var reqId = Device.GetClient().GetShadow(new DeviceShadowRequest
{
ServiceId = null // service id in you product model, for example, "SmokeDetector"
});
LOG.Info("get shadow request id: {}", reqId);
}
接收影子数据:实现DeviceShadowListener接口并创建实例,然后设置其到对应的listener以监听设置属性的请求:
protected override void BeforeInitDevice()
{
Device.GetClient().deviceShadowListener = this;
}
处理影子消息:在OnShadowCommand(string requestId, string message)中实现的逻辑:
public void OnShadowCommand(string requestId, string message)
{
LOG.Info("receive device shadow, request id:{}, message:{}", requestId, message);
}
网关是一个特殊的设备,除具备一般设备功能之外,还具有子设备管理、子设备消息转发的功能。SDK提供了AbstractGateway抽象类来简化网关的实现。 该类提供了子设备管理功能,需要从平台获取子设备信息并保存(需要子类提供子设备持久化接口)、子设备下行消息转发功能(需要子类实现转发处理接口)、 以及上报子设备列表、上报子设备属性、上报子设备状态、上报子设备消息等接口。
Gateway Demo实现了一个简单TCP子设备模拟器、和一个对应的的网关,在此基础上您可以根据需要进行扩展。比如修改持久化方式、转发中增加消息格式的转换、实现其他子设备接入协议。 下面将按照使用步骤和功能阻隔介绍示例网关里的主要类。
启动Gateway
与配置与启动Demo中的命令相同,只需要将demo的名字改为Gateway即可。 Gateway的实现如下:
另外配置文件针对Gateway Demo提供额外的GatewayConfig参数:
{
"GatewayConfig": {
"ListenPort": 8081,
"PreDeleteSubDeviceIds": [
"要删除的设备的device id"
],
"PreAddSubDevice": [
{
"node_id": "要添加的设备的node id 1",
"product_id": "产品id 1"
},
{
"node_id": "要添加的设备的node id 2",
"product_id": "产品id 2"
}
]
}
}
参数介绍如下:
ListenPort: 监听端口,子设备模拟器链接用PreDeleteSubDeviceIds: Gateway启动后会立即演示删除子设备功能,子设备的id则放在这里配置PreAddSubDevice: Gateway启动后会立即演示添加子设备功能,子设备的信息则放在这里配置node_id:子设备node_id,加上product_id前缀后生成device idproduct_id:平台上的产品id。一个完整的配置示例可以参考iot-device-demo/config/DemoGatewayConfig.json
启动TCP子设备模拟器: TCP子设备模拟器是一个简单的TCP客户端,负责连接Gateway的端口并将用户输入的消息发送给Gateway:
cd iot-tcp-device
dotnet run --project .\iot-tcp-device.csproj localhost 8081 mySubDevice1
后面三个参数含义分别是:
切换回网关的日志,如果连接成功将会出现以下内容:

返送消息: 返回到设备模拟器的console,输入下面的内容上报消息:
message: this is a random message
发送属性: 在子设备模拟器的console,输入以下格式的内容上报属性:
properties: smokeDetector, {"alarm":0,"temperature":10}
切换回网关的日志,如果上报成功将会出现以下内容:

命令响应: 按照设备命令中操作向子设备下发命令时需要在子设备模拟器中手动回复命令响应,格式如下:
cmdresp: requestId, result
其中requestId为命令下发时对应的请求id,result则是命令执行结果,0代表成功,其它代表失败。示例如下,

AbstractGateway.cs提供了子设备消息上报、子设备消息下发处理,回调。上Demo中的SimpleGateway就是继承该类,实现了相关功能。
下面分类介绍哪些接口需要被实现或调用1以实现完整的子设备管理。
添加或删除子设备处理
网关可以主动向平台发起删除或添加子设备请求,接口如下:
ReportAddSubDevice(List<DeviceInfo> subDeviceInfo):void
ReportDeleteSubDevice(List<string> devicesId):void
同时平台也会下发删除或添加子设备的响应,由于AbstractGateway中已经用SubDevicesFilePersistence.cs类对象处理这一部分,您只需要实现一个SubDevicesFilePersistence.cs子类并在实例化AbstractGateway(的子类)时传入即可。
下行消息处理
网关收到平台下行消息时,需要转发给子设备。平台下行消息分为三种:设备消息、属性读写、命令、事件,您需要分别实现以下几个抽象接口以便处理消息:
/// 子设备命令下发处理,网关需要转发给子设备,需要子类实现
public abstract void OnSubdevCommand(string requestId, Command command);
/// 子设备属性设置,网关需要转发给子设备,需要子类实现
public abstract void OnSubdevPropertiesSet(string requestId, PropsSet propsSet);
/// 子设备读属性,网关需要转发给子设备,需要子类实现
public abstract void OnSubdevPropertiesGet(string requestId, PropsGet propsGet);
/// 子设备消息下发,网关需要转发给子设备,需要子类实现
public abstract void OnSubdevMessage(DeviceMessage message);
/// 子设备时间下发,网关需要转发给子设备,需要子类实现
OnSubdevEvent(string deviceId, DeviceEvent deviceEvent):void
上行消息处理 上行消息均复用原有接口,仅需将对应子设备得devieId填入即可,以属性上报为例:
var serviceProperty = new ServiceProperty
{
properties = new Dictionary<string, object>
{
// Sets properties based on the product model.
{ "alarm", 1 },
{ "temperature", 23.45812 },
{ "humidity", 56.89013 },
{ "smokeConcentration", 89.5672 }
},
serviceId = "smokeDetector" // The serviceId must be the same as that defined in the product model.
};
var deviceProperties = new DeviceProperties
{
DeviceId = "子设备id",
services = new List<ServiceProperty> { serviceProperty }
};
Device.GetClient().ReportProperties(deviceProperties);
其它
//上报子设备状态
ReportSubDeviceStatus(List<DeviceStatus> statuses):void
//同步子设备信息
SyncSubDevices(bool syncAll):void
软固件升级需要在平台先上传软固件并创建任务,然后SDK才会接收到升级请求并开始处理。详细操作可以参考MQTT协议设备OTA固件升级。默认情况下,软固件升级示例中下载的文件将被统一下载到demo运行路径下的download文件夹中。

如果您需要高度自定义软固件处理流程,您需要实现OTAListener以监听软固件升级请求:
public interface OTAListener
{
void OnQueryVersion(OTAQueryInfo queryInfo);
void OnNewPackage(OTAPackage pkg);
void OnNewPackageV2(OTAPackageV2 pkgV2);
}
然后处理请求中的url,最后再使用下面接口上报处理进度或结果:
class OtaService {
///.....
ReportOtaStatus(int result, int progress, string version, string description)
}
请求时间同步:调用TimeService.RequestTimeSync请求时间同步
protected override void RunDemo()
{
Device.timeSyncService.RequestTimeSync();
}
接收时间同步消息:实现TimeSyncListener接口并创建实例,然后设置其到对应的listener以监听响应:
protected override void BeforeInitDevice()
{
Device.timeSyncService.listener = this;
}
处理时间同步消息:在OnTimeSyncResponse(string requestId, string message)中实现设置设备时钟的逻辑,下面为示例
public void OnTimeSyncResponse(long deviceSendTime, long serverRecvTime, long serverSendTime)
{
long deviceRecvTime = Convert.ToInt64(IotUtil.GetTimeStamp());
long now = (serverRecvTime + serverSendTime + deviceRecvTime - deviceSendTime) / 2;
LOG.Info("now is {}", StampToDatetime(now));
}
上面demo中的算法来自设备时间同步响应。
调用Device.GetClient().ReportDeviceInfo("软件版本", "固件版本")上报设备信息:
protected override void RunDemo()
{
Device.GetClient().ReportDeviceInfo("v1.1", "v1.1");
}
上报成功后可以在设备详情里看到对应的软固件版本信息:

使用设备日志需要您先在平台配置“运行日志”功能,请参考文档运行日志使用说明。运行日志开启之后调用SDK中对应的API才能将日志上传到平台。运行日志上报支持两种方式:
显示调用日志上传接口。
使用Device.LogService.ReportLog接口直接生成日志消息并上报到到平台:
Device.LogService.ReportLog(DateTimeOffset.Now.ToUnixTimeSeconds().ToString(),
LogService.LogType.DeviceStatus, "ONLINE");
将经过NLog组件输出的日志抓发到平台。
SDK默认使用NLog日志组件并将上报到IoTDA作为一个可配置的target。打开iot-device-demo/CoreCapability/NLog.config,首先您可以看到一个名字为IoTDA类型为IoTDA的target,在这里我们主要为这个target配置了输出格式:
<target name="IoTDA" xsi:type="IoTDA" layout="${longdate} | ${level:uppercase=false} | ${callsite:fileName}:${callsite-linenumber} - ${message}${onexception:, error\: ${exception:format=type,message,method:innerExceptionSeparator= - :separator=. :maxInnerExceptionLevel=5:innerFormat=shortType,message,method} - stacktrace\: ${stacktrace}}" />
在下面的rules里的第三个logger则使用过滤规则指定了什么日志将被写入IoTDA。
<logger name="*" minlevel="Debug" writeTo="IoTDA">
<filters defaultAction='Ignore'>
<when condition="contains('${callsite:fileName}','DeviceReportLogSample') and not contains('${callsite:fileName}','DemoMessagePublishListener')" action="Log" />
</filters>
</logger>
注意我们这里这里主要使用的是白名单机制,即“指定需要收集的日志”。 如果过滤不慎则可能导致死循环,例如demo里的
public void OnMessagePublished(RawMessage message)和public void OnMessageUnPublished(RawMessage message)监听了消息推送的结果同时用NLog打印了日志,这意味着:
- 只要有消息被推送,这两个回调函数里就会产生日志。
- 如果不使用
not contains('${callsite:fileName}','DemoMessagePublishListener')排除这里的日志,那么这里的消息又会被日志收集上报。- 无论消息推送成功或失败,会再次触发这两个回调函数。
从而导致死循环。
使用远程配置需要先在设备接入控制台创建远程配置任务,操作步骤可以参考上列文档。 同时,SDK中需要配置对应的监听器处理回调。
实现并设置DeviceConfigListener:
public class DeviceConfigSample : DeviceSample, DeviceConfigListener
{
protected override void BeforeInitDevice()
{
Device.DeviceConfigService.DeviceConfigListener = this;
}
}
在OnDeviceConfig中处理配置,并以DeviceConfigResponse类型的处理结果作为返回值:
public DeviceConfigResponse OnDeviceConfig(Dictionary<string, object> configContent, string deviceId)
{
// use configuration to configure your device
// ....
return new DeviceConfigResponse { ResultCode = 0 };
}
在使用该功能前, 您其需要先在设备接入控制台配置obs 。详细操作参考文件上传。
SDK中FileManagerService类提供与MQTT接口一一对应的接口。但为了用户方便使用,SDK中还提供了SimpleLocalFileManager类,其包含了更加便捷的接口。如果您想直接使用FileManagerService接口,也可以参考SimpleLocalFileManager的源码。 使用详情可以参考iot-device-demo/CoreCapability/FileUploadDownloadSample.cs,这里演示了如何使用SimpleLocalFileManager将本地Nlog.config配置上传到obs、然后再将文件原样从obs下载回来的例子。
初始化SimpleLocalFileManager对象:
protected override void RunDemo()
{
var fsa = new SimpleLocalFileManager(Device.FileManagerService);
//...
调用UploadFile下载文件,其接收SimpleLocalFileManager.FileTransferRequest对象作为参数,主要参数如下:
Path:要上传的文件路径CompleteListener:文件上传完毕后的回调(无论成功或失败)FileName:上传到obs的文件名,不填时会自动从Path里提取并自动添加上时间戳: var fileName = fsa.UploadFile(new SimpleLocalFileManager.FileTransferRequest
{
Path = IotUtil.GetRootDirectory() + @"\Nlog.config",
CompleteListener = UploadCompleteListener
});
初始化SimpleLocalFileManager对象:
与同文件上传里相同。
调用UploadFile下载文件,其接收SimpleLocalFileManager.FileTransferRequest对象作为参数,主要参数如下:
Path:下载文件的存储路径CompleteListener:文件下载完毕后的回调(无论成功或失败)FileName:obs里的文件名,不填时会自动从Path里提取。注意这里和上传不同,自动生成的文件名不会自动添加上时间戳: // download the uploaded NLOG.config with generated new name(e.g. Nlog-20240527-034141-867.config)
fsa.DownloadFile(new SimpleLocalFileManager.FileTransferRequest
{
FileName = req.FileName, // filename in obs
Path = IotUtil.GetRootDirectory() + @"\" + req.FileName
});
端侧规则引擎在SDK中属于开箱即用功能,您只需在控制台编辑规则即可,SDK里只需通过开关开启该功能即可。在平台配置端侧规则引擎里有两种触发条件:
ReportPorperties设备属性上报接口或者物模型编程里Service自动上报属性时触发检查。注意:为了直观地看出效果,如果您使用iot-device-demo/CoreCapability/DeviceRuleSample.cs并在云端更新规则,则该demo中会自动连续上报20次属性,
Temperature,Humidity属性值则是随上报顺序被设置为从0到19,方便您观察属性条件的效果。
SDK中端侧规则引擎提供额外接口满足以下需求。注意,下面均需在AbstractDevice.Init()前调用:
开启规则引擎
规则引擎默认关闭,在Demo中使用以下代码开启规则引擎:
Device.DeviceRuleService.EnableDeviceRule = true;
规则引擎支持将规则引擎缓存到本地并在重启时优先从本地读取规则,文件路径使用下面代码配置:
Device.DeviceRuleService.DeviceRuleStoragePath = "rule.json";
规则引擎默认将触发的本设备命令发送到您设置的CommandListener
命令回调,当触发的命令不属于本设备时,您需要设置DeviceRuleCommandListener自行处理:
private class DemoDeviceRuleCommandListener : DeviceRuleCommandListener
{
public void OnDeviceRuleCommand(string requestId, Command command)
{
LOG.Info("get command sent to another device:{}", command);
}
}
//.....
Device.DeviceRuleService.DeviceRuleCommandListener = new DemoDeviceRuleCommandListener();
SDK支持远程登录且SDK端无需额外配置,平台上的操作步骤参考设备远程登录。
在使用该功能前您需要在设备接入控制台配置异常检测,参考操作步骤 。
实现SecurityInfoGetListener接口。
实现SecurityInfoGetListener接口以便异常检测服务定时获取系统信息,接口中各个方法的定义如下:
void OnStart(): 当异常检测服务启动时(任一检测项被开启)被调用,您可能需要在这个函数里分配资源以便后续获取系统信息void OnStop(): 当异常检测服务停止时(所有检测项被关闭)被调用,您可能需要在这个函数里释放前面分配的资源long OnGetMemoryTotalKb(): 获取内存总大小long OnGetMemoryUsedKb(): 获取使用中的内存大小IEnumerable<int> OnGetUsedPort(): 获取正在被使用的端口int OnGetCpuPercentage(): 获取CPU使用率long OnGetDiskTotalKb(): 获取所有磁盘的总大小long OnGetDiskUsedKb(): 获取所有磁盘的总使用大小int OnGetBatteryPercentage(): 获取电池使用率bool OnGetLocalLoginInfo(): 获取是否有用户通过本地方式登录设备bool OnGetFileTamperInfo(): 获取是否有关键文件被篡改bool OnGetBruteForceLoginInfo(): 获取是否有用户尝试暴力破解登录IEnumerable<IPAddress> OnGetMaliciousIp(): 获取所有恶意IP列表SDK默认提供了实现大部分接口的DefaultSecurityInfoGetListener(除了OnGetLocalLoginInfo(), OnGetFileTamperInfo(), OnGetBruteForceLoginInfo()),该Listener可以上报Linux/Windows相关信息,您可以参考该Listener实现相关逻辑。
设置Listener。 参考Demo,使用下面代码设置Listener:
Device.SecurityDetectionService.SecurityInfoGetListener = new DemoSecurityInfoGetListener();
配置上报周期。
参考Demo,使用下面代码设置上报周期:
Device.SecurityDetectionService.ReportPeriod = TimeSpan.FromSeconds(5);
设备任务暂不支持。
消息重传
SDK中使用mqtt的QoS来保障消息可靠性,默认等级为Qos1,若需要修改则请操作ClientConf中的Qos参数。
demo启动后会等待设备断链,您可以打开设备的消息跟踪后再将网络断开或者冻结设备。此时SDK中探测到断链后会不断尝试重连并自动上报5条设备消息。之后重新恢设备和网络,由于SDK中默认使用QoS1,那5条设备消息也能上报成功并且可以在消息跟踪中看到。
运行本章节demo需要关闭自动重连。
SDK默认开启断线重连,如果需要关闭则需要修改作ClientConf中的AutoReconnect为false。您可以使用ConnectListener来感知连接情况并使用DeviceClient的Close和Connect方法实现自定义重连逻辑,或则处理业务上的逻辑,如”重新订阅自定义topic“,详细可参考demo代码。
重连间隔使用指数延时,算法大致为每次失败后的延时将以指数级增长直到达到一个最大值,核心代码在CustomReconnectDelayMqttClientProxy.cs文件的BackDelayGenerator成员变量。
其中有几个关键参数可以修改:
MinBackoff: 最小退避时间,默认是1秒。MaxBackoff: 最大退避时间,默认是5分钟。DefaultBackoff: 退避时间基数,默认为1000秒。具体算法为:如果当前已经连续连接失败n次,每次延迟时间是退避时间基数x放大系数+最小退避时间, 其中放大系数是按指数趋势在2^(n-1)*0.8 和 2^(n-1)*1.2之间随机选一个。
如果带入默认参数,则为 1 + 2^(n-1)*0.8和1 + 2^(n-1)*1.2,下面给出前几次失败重连间隔的值:
设备发放服务主要用于设备在多个IoT平台实例的发放、接入和迁移。设备在出厂时只需要烧录一个地址,然后在平台上通过策略制不同的设备接入不同的IoT平台。 设备上电后收到设备发放下发的新的平台连接地址,直接连接新的地址,免去二次烧录设备信息,同时还支持初始化的设备信息同步。 目前支持两大种设备类型的发放,功能如下:
另外,下面的Demo种都需要用到scopeId, 您可以在设备发放页面点击设备->注册组列表查询:

设备发放需先获取发放接入地址,具体可参考终端节点。
静态策略
静态策略同时支持密钥和证书接入的设备,这里仅展示密钥接入,证书接入可以参考下面的“证书策略“。 更改以下配置iot-device-demo/config/DemoConfigDefault.json
{
"AuthConfig": {
"AuthUseCert": true,
"ServerAddress": "发放接入地址",
"ServerPort": 8883,
"DeviceId": "你的设备id",
"DeviceSecret": "你的设备密钥",
},
"BootstrapConfig": {
"ReportedData": "静态策略支持匹配上报消息,可以把关键字填到这里"
}
}
或者以程序化的方式初始化Bootstrapclient:
new BootstrapClient(bootstrapUri, port, deviceId, deviceSecret, null)
证书策略
证书策略需要设备通过证书接入,证书制作过程和控制台配置可以参考MQTT X.509证书认证设备使用证书策略发放示例。
更改以下配置iot-device-demo/config/DemoConfigDefault.json
{
"AuthConfig": {
"AuthUseCert": true,
"ServerAddress": "发放接入地址",
"ServerPort": 8883,
"DeviceId": "你的设备id",
"DeviceCert": "你生成的pfx证书",
"DeviceCertPassword": "pfx证书密码"
},
"BootstrapConfig": {
}
}
或者以程序化的方式初始化Bootstrapclient:
new BootstrapClient(bootstrapUri, port, deviceId, deviceCert, null)
自定义策略
该策略同时支持密钥和证书接入的设备,使用方式参考自定义策略。
静态策略
静态策略同时支持密钥和证书接入的设备,这里仅展示密钥接入,证书接入可以参考下面的“证书策略“。 密钥需要在创建注册组时获取,详细可参考MQTT 注册组密钥认证静态策略发放示例。
更改以下配置iot-device-demo/config/DemoConfigDefault.json
{
"AuthConfig": {
"AuthUseCert": true,
"ServerAddress": "发放接入地址",
"ServerPort": 8883,
"DeviceId": "你的设备id",
"DeviceSecret": "注意!这里现在填的时注册组密钥!",
},
"BootstrapConfig": {
"ScopeId": "上面获取到的scopeId",
"ReportedData": "静态策略支持匹配上报消息,可以把关键字填到这里"
}
}
或者以程序化的方式初始化Bootstrapclient:
new BootstrapClient(bootstrapUri, port, deviceId, regGroupSecret, scopeId)
证书策略
证书策略需要设备通过证书接入,证书制作过程和控制台配置可以参考 MQTT X.509证书认证设备使用证书策略发放示例。
更改以下配置iot-device-demo/config/DemoConfigDefault.json
{
"AuthConfig": {
"AuthUseCert": true,
"ServerAddress": "发放接入地址",
"ServerPort": 8883,
"DeviceId": "你的设备id",
"DeviceCert": "你生成的pfx证书",
"DeviceCertPassword": "pfx证书密码"
},
"BootstrapConfig": {
"ScopeId": "上面获取到的scopeId",
}
}
或者以程序化的方式初始化Bootstrapclient:
new BootstrapClient(bootstrapUri, port, deviceId, deviceCert, scopeId)
自定义策略
该策略同时支持密钥和证书接入的注册组,使用方式参考MQTT 注册组自定义策略发放示例。
在第三方协议设备不能直接接入平台场景下,泛协议接入提供了在平台外部协议转换的功能。详细介绍可参考通过协议转换网关实现泛协议设备接入
在使用泛协议之前您需要创建网桥。如果要通过平台界面创建,请参考[创建网桥]。
修改接入地址iot-device-demo/config/DemoConfigDefault.json,将之前获取到的网桥接入信息填入对应位置,下面为模板:
{
"DemoName": "Bridge",
"AuthConfig": {
"ServerAddress": "mqtt server地址,咨询后获取",
"ServerPort": "mqtt server端口,咨询后获取",
"DeviceId": "网桥id",
"DeviceSecret": "网桥密钥"
},
"BridgeConfig": {
"ListenPort": 8081
}
}
使用下面命令启动网桥demo,启动后网桥将作为一个tcp服务端开始监听请求:
cd iot-device-demo
dotnet run --project .\iot-device-demo.csproj
出现下面日志表示启动成功:

启动TCP设备模拟器: TCP设备模拟器是一个简单的TCP客户端,负责连接Bridge的端口并将用户输入的消息发送给Bridge:
cd iot-tcp-device
dotnet run --project .\iot-tcp-device.csproj localhost 8081 myDeviceId myDeviceName
后面三个参数含义分别是:
切换回网关的日志,如果连接成功将会出现以下内容:

返送消息: 返回到设备模拟器的console,输入下面的内容上报消息:
message: this is a random message

发送属性: 在设备模拟器的console,输入以下格式的内容上报属性:
properties: smokeDetector, {"alarm":1,"temperature":11.2}
切换回网关的日志,如果上报成功将会出现以下内容:

命令响应: 按照设备命令中操作向设备下发命令时需要在设备模拟器中手动回复命令响应,格式如下:
cmdresp: requestId, result
其中requestId为命令下发时对应的请求id,result则是命令执行结果,0代表成功,其它代表失败。示例如下,

设备ID ,即为SDK中deviceId的值。密钥 即为SDK中DeviceSecret的值。若是忘记密码,可在设备详情页面重置密钥。版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9
京公网安备 11010802032778号
English | 简体中文
iot-device-sdk-cSharp开发指南
**在开始使用SDK其它功能之前,我们建议先阅读准备工作和快速体验**。同时,本文档中各个功能都有对应的demo演示,后面每个介绍功能的章节最前面会有一个包含
Demo源码,Demo名称名称的列表,示例如下:Demo源码可以供您参考API使用的细节,Demo名称用于在配置文件里指定运行哪个Demo,具体如何使用Demo名称运行不同的Demo可参考后面的配置与启动demo的第二步。POST /polls/redis/<key>/<value>GET /polls/redis/<key>GET /polls/openidcompile_commands.jsonmodule.yamlscripts/setup.shsrctestsBlockly Developer Tools(WHAT YOU NEED)
(ماذا تحتاج)
(您需要什么)
(DE QUOI AS-TU BESOIN)
(O QUE VOCÊ PRECISA)
(NEYE İHTİYACIN VAR)
include_dirdef_pathssymbols--log-handler-scripttoo many publishes in Progresserror)protocartifactsx:npm startnpm testnpm run buildnpm run ejectnpm run buildfails to minify/rates/{timestamp}icu4c-dataicu4c-data