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);
// ......
}
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);
}
/// 子设备命令下发处理,网关需要转发给子设备,需要子类实现
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);
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));
}
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();
English | 简体中文
iot-device-sdk-cSharp开发指南
**在开始使用SDK其它功能之前,我们建议先阅读准备工作和快速体验**。同时,本文档中各个功能都有对应的demo演示,后面每个介绍功能的章节最前面会有一个包含
Demo源码,Demo名称名称的列表,示例如下:Demo源码可以供您参考API使用的细节,Demo名称用于在配置文件里指定运行哪个Demo,具体如何使用Demo名称运行不同的Demo可参考后面的配置与启动demo的第二步。--helpOutput--fastqsa File of Filenames for Input FASTQs--max_cpusvs--cpus--genome_sizefor Error Correction and Coverage Reduction--keep_cachefor Intermediate Filescaller and reporter
epialleleRpackageepialleleRat Bioconductor.vueImports in TS版本更新说明
0.前言
本文通过实例讲述iot-device-sdk-cSharp(以下简称SDK)帮助用户将设备经MQTT协议快速连接到华为IoTDA服务。
1.SDK简介
1.1 SDK功能支持
SDK面向运算、存储能力较强的嵌入式终端设备,开发者通过调用SDK接口,便可实现设备与IoTDA服务(后续简出现的“服务”、“平台“均指IoTDA服务)双向行通讯。 SDK当前支持的功能有:
1.1 SDK目录结构
2.准备工作
3.快速体验
本章将使用
SmokeDetectorDemo 指导您快速体验产品模型的创建、Demo的配置与启动、以及SDK基本功能的使用。3.1 创建产品模型
为了方便体验,我们提供了一个烟感的产品模型,烟感会上报烟雾值、温度、湿度、烟雾报警、还支持响铃报警命令。 下面的子章节将以烟感为例,体验消息上报、属性上报等功能。 下面创建产品模型的流程也可以参考向导式体验智慧烟感接入平台。
访问IoTDA平台并进入设备接入控制台,如果是第一次使用需要注册并登录.
登录成功后自动跳转,在设备接入控制台选择“产品”,单击右上角的“创建产品”,在弹出的页面中,填写“产品名称”、“协议类型”、“数据格式”、“厂商名称”、“所属行业”、“设备类型”等信息,然后点击右下角“立即创建”。
产品创建成功后,单击“详情”进入产品详情,在功能定义页面,单击“上传模型文件”,上传烟感产品模型
3.2 创建设备
点击“设备”-> “所有设备”,点击右上角“注册设备”,选择产品所在的“资源空间”,选择上方创建的产品,填写设备标识码(一般是IMEI、MAC地址等),自定义“设备名称”,“密钥”如果不自定义,平台会自动生成。全部填写完毕后,点击“确定”。
可以直接复制设备秘钥,点击“保存并关闭”将自动将设备ID以txt文本形式下载到本地。
点击”设备“->“所有设备”,在最上方可看到该设备的状态是未激活。
3.3 配置与启动Demo
获取接入地址,可在控制台的“总览”->“接入信息”中查看。
打开文件 iot-device-demo/config/DemoConfigDefault.json,将之前获取到的接入地址、设备ID以及设备密钥填入对应位置,或者将配置填入下面模板后再覆盖iot-device-demo/config/DemoConfigDefault.json:
其中
DemoName用于指定运行的Demo,此处为SmokeDetector指定基于烟雾报警器产品模型的物模型编程Demo。该Sdk能运行的Demo名称可以参后面章节,几乎每一个功能点介绍的小节最前面会标注Demo名称。或者进入sdk目录下运行下面命令查看所有Demo名称列表:编译&运行
进入sdk目录下,执行下面命令运行demo,命令末尾加参数为配置文件路径:
如果您使用旧版本5.0+的SDK并且无法升级到.NET 8.0,可以修改以下文件的
TargetFramework。但请注意,目前除了.NET 8.0和.NET 6.0属于长期支持的版本外,其它更旧版版本均已不再支持:上面使用
run命令会自动编译,如果要手动编译某一个project,可以行下面命令:输出示例如下,如果输出里有
connect success则表示连接成功:查看设备运行情况
在控制台筛选出该设备并进入设备详情页面
详情页下放”物模型数据“页面可以看到最新上报的数据

4.SDK功能参考
4.1 设备连接鉴权
密钥鉴权
密钥鉴权支持mqtt(1883)和mqtts(8883)接入,为安全起见,推荐使用8883端口接入平台。使用8883端口接入时需要用到iot-device-demo/config/certificate/DigiCertGlobalRootCA.crt.pem,该文件默认已预置各个region的根证书,如果连接不成功则尝试从这里下载对应region的pem证书并替换。
使用Demo时时配置文件iot-device-demo/config/DemoConfigDefault.json中对应的配置项如下(无关配置项省略):
使用API实例化设备对象时参数如下:
证书鉴权
平台支持设备使用自己的X.509证书进行设备接入认证,使用前需要注册证书认证的设备并在设备接入控制台预置对应CA证书,详细指导请参考注册X.509证书认证的设备。
制作完调测证书后,参考以下命令转换成pfx格式:
使用Demo时,把 iot-device-demo/config/DemoConfigDefault.json里的
AuthUseCert改为true,同时DeviceCert改为生成的证书路径,示例如下:4.2 设备命令
设备命令由两步骤组成:
命令下发
命令下发时设置对应listener用来接收平台下发的命令:
实现回调接口
OnCommand对命令进行处理:命令响应
紧接着命令处理完毕后,调用
RespondCommand上报响应用于确认执行结果:4.3 设备消息
系统Topic消息上报
调用
ReportDeviceMessage上报预定格式的消息调用
ReportRawDeviceMessage上报任意格式的消息:系统Topic消息下发
SDK中需要进行以下操作: 实现
RawDeviceMessageListener的类并创建实例同时设置其到对应的listener以监听下发消息:在
public void OnRawDeviceMessage(RawDeviceMessage message)实现消息处理逻辑:自定义topic消息上报
自定义topic消息分为
$oc前缀和非$oc前缀两种,均通过创建CustomMessageTopic时通过设置OcPrefix成员指定:调用
ReportRawDeviceMessage(RawDeviceMessage, CustomMessageTopic)可以上报消息到自定topic:自定义topic消息下发
接收自定义topic的消息前需要使用
SubscribeTopic(CustomMessageTopic)订阅自定义topic:然后实现
RawDeviceMessageListener接口、创建其实例、设置其到对应的listener以监听下发消息:在
public void OnCustomRawDeviceMessage(string topic, bool topicStartsWithOc, RawDeviceMessage rawDeviceMessage)中实现处理消息的逻辑:4.4 设备属性
设备属性上报
调用
ReportProperties(List<ServiceProperty>)按产品模型中定义的格式将属性数据上报给平台:上报成功后输出日志样例如下:
平台设置设备属性
实现
PropertyListener接口并创建实例,同时设置其到对应的listener以监听设置属性的请求:再在
void OnPropertiesSet(string requestId, List<ServiceProperty> services)中实现修改设备上对应属性的逻辑。 处理完成后调用void RespondPropsSet(string requestId, IotResult iotResult)响应设置结果:平台查询设备属性
创建监听器:实现
PropertyListener接口并创建实例,同时设置其到对应的listener以监听设置属性的请求:处理查询设备属性请求并响应: 在
public void OnPropertiesGet(string requestId, string serviceId)中实现获取serviceId对应属性的逻辑,之后调用void RespondPropsGet(string requestId, List<ServiceProperty> services)响应。设备侧获取平台的设备影子数据
请求设备影子:调用
GetShadow(DeviceShadowRequest shadowRequest, string requestId = null)接口上报获取设备请求,DeviceShadowRequest中的ServiceId不填时表示获取所有服务的影子数据,requestId不填时会自动生成并作为返回值:接收影子数据:实现
DeviceShadowListener接口并创建实例,然后设置其到对应的listener以监听设置属性的请求:处理影子消息:在
OnShadowCommand(string requestId, string message)中实现的逻辑:4.5 网关与子设备管理
网关是一个特殊的设备,除具备一般设备功能之外,还具有子设备管理、子设备消息转发的功能。SDK提供了AbstractGateway抽象类来简化网关的实现。 该类提供了子设备管理功能,需要从平台获取子设备信息并保存(需要子类提供子设备持久化接口)、子设备下行消息转发功能(需要子类实现转发处理接口)、 以及上报子设备列表、上报子设备属性、上报子设备状态、上报子设备消息等接口。
Gateway Demo使用说明
Gateway Demo实现了一个简单TCP子设备模拟器、和一个对应的的网关,在此基础上您可以根据需要进行扩展。比如修改持久化方式、转发中增加消息格式的转换、实现其他子设备接入协议。 下面将按照使用步骤和功能阻隔介绍示例网关里的主要类。
启动Gateway
与配置与启动Demo中的命令相同,只需要将demo的名字改为
Gateway即可。 Gateway的实现如下:另外配置文件针对Gateway Demo提供额外的
GatewayConfig参数:参数介绍如下:
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:
后面三个参数含义分别是:
切换回网关的日志,如果连接成功将会出现以下内容:
返送消息: 返回到设备模拟器的console,输入下面的内容上报消息:
发送属性: 在子设备模拟器的console,输入以下格式的内容上报属性:
切换回网关的日志,如果上报成功将会出现以下内容:
命令响应: 按照设备命令中操作向子设备下发命令时需要在子设备模拟器中手动回复命令响应,格式如下:
其中
requestId为命令下发时对应的请求id,result则是命令执行结果,0代表成功,其它代表失败。示例如下,AbstractGateway
AbstractGateway.cs提供了子设备消息上报、子设备消息下发处理,回调。上Demo中的SimpleGateway就是继承该类,实现了相关功能。
下面分类介绍哪些接口需要被实现或调用1以实现完整的子设备管理。
添加或删除子设备处理
网关可以主动向平台发起删除或添加子设备请求,接口如下:
同时平台也会下发删除或添加子设备的响应,由于
AbstractGateway中已经用SubDevicesFilePersistence.cs类对象处理这一部分,您只需要实现一个SubDevicesFilePersistence.cs子类并在实例化AbstractGateway(的子类)时传入即可。下行消息处理
网关收到平台下行消息时,需要转发给子设备。平台下行消息分为三种:设备消息、属性读写、命令、事件,您需要分别实现以下几个抽象接口以便处理消息:
上行消息处理 上行消息均复用原有接口,仅需将对应子设备得devieId填入即可,以属性上报为例:
其它
4.6 软固件升级
软固件升级需要在平台先上传软固件并创建任务,然后SDK才会接收到升级请求并开始处理。详细操作可以参考MQTT协议设备OTA固件升级。默认情况下,软固件升级示例中下载的文件将被统一下载到demo运行路径下的
download文件夹中。如果您需要高度自定义软固件处理流程,您需要实现
OTAListener以监听软固件升级请求:然后处理请求中的
url,最后再使用下面接口上报处理进度或结果:4.7 设备时间同步
请求时间同步:调用
TimeService.RequestTimeSync请求时间同步接收时间同步消息:实现
TimeSyncListener接口并创建实例,然后设置其到对应的listener以监听响应:处理时间同步消息:在
OnTimeSyncResponse(string requestId, string message)中实现设置设备时钟的逻辑,下面为示例4.8 设备信息上报
调用
Device.GetClient().ReportDeviceInfo("软件版本", "固件版本")上报设备信息:上报成功后可以在设备详情里看到对应的软固件版本信息:
4.9 设备日志收集
使用设备日志需要您先在平台配置“运行日志”功能,请参考文档运行日志使用说明。运行日志开启之后调用SDK中对应的API才能将日志上传到平台。运行日志上报支持两种方式:
显示调用日志上传接口。
使用
Device.LogService.ReportLog接口直接生成日志消息并上报到到平台:将经过NLog组件输出的日志抓发到平台。
SDK默认使用NLog日志组件并将上报到IoTDA作为一个可配置的
target。打开iot-device-demo/CoreCapability/NLog.config,首先您可以看到一个名字为IoTDA类型为IoTDA的target,在这里我们主要为这个target配置了输出格式:在下面的
rules里的第三个logger则使用过滤规则指定了什么日志将被写入IoTDA。4.10 远程配置
使用远程配置需要先在设备接入控制台创建远程配置任务,操作步骤可以参考上列文档。 同时,SDK中需要配置对应的监听器处理回调。
实现并设置
DeviceConfigListener:在
OnDeviceConfig中处理配置,并以DeviceConfigResponse类型的处理结果作为返回值:4.11 文件上传下载
在使用该功能前, 您其需要先在设备接入控制台配置obs 。详细操作参考文件上传。
SDK中
FileManagerService类提供与MQTT接口一一对应的接口。但为了用户方便使用,SDK中还提供了SimpleLocalFileManager类,其包含了更加便捷的接口。如果您想直接使用FileManagerService接口,也可以参考SimpleLocalFileManager的源码。 使用详情可以参考iot-device-demo/CoreCapability/FileUploadDownloadSample.cs,这里演示了如何使用SimpleLocalFileManager将本地Nlog.config配置上传到obs、然后再将文件原样从obs下载回来的例子。文件上传
初始化
SimpleLocalFileManager对象:调用
UploadFile下载文件,其接收SimpleLocalFileManager.FileTransferRequest对象作为参数,主要参数如下:Path:要上传的文件路径CompleteListener:文件上传完毕后的回调(无论成功或失败)FileName:上传到obs的文件名,不填时会自动从Path里提取并自动添加上时间戳:文件下载
初始化
SimpleLocalFileManager对象:与同文件上传里相同。
调用
UploadFile下载文件,其接收SimpleLocalFileManager.FileTransferRequest对象作为参数,主要参数如下:Path:下载文件的存储路径CompleteListener:文件下载完毕后的回调(无论成功或失败)FileName:obs里的文件名,不填时会自动从Path里提取。注意这里和上传不同,自动生成的文件名不会自动添加上时间戳:4.12 规则引擎
端侧规则引擎在SDK中属于开箱即用功能,您只需在控制台编辑规则即可,SDK里只需通过开关开启该功能即可。在平台配置端侧规则引擎里有两种触发条件:
ReportPorperties设备属性上报接口或者物模型编程里Service自动上报属性时触发检查。SDK中端侧规则引擎提供额外接口满足以下需求。注意,下面均需在
AbstractDevice.Init()前调用:开启规则引擎
规则引擎默认关闭,在Demo中使用以下代码开启规则引擎:
规则引擎支持将规则引擎缓存到本地并在重启时优先从本地读取规则,文件路径使用下面代码配置:
规则引擎默认将触发的本设备命令发送到您设置的
CommandListener命令回调,当触发的命令不属于本设备时,您需要设置DeviceRuleCommandListener自行处理:4.13 远程登录
SDK支持远程登录且SDK端无需额外配置,平台上的操作步骤参考设备远程登录。
4.14 异常检测
在使用该功能前您需要在设备接入控制台配置异常检测,参考操作步骤 。
实现
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:
配置上报周期。
参考Demo,使用下面代码设置上报周期:
4.15 设备任务
设备任务暂不支持。
4.16 消息重传
消息重传 SDK中使用mqtt的QoS来保障消息可靠性,默认等级为
Qos1,若需要修改则请操作ClientConf中的Qos参数。demo启动后会等待设备断链,您可以打开设备的消息跟踪后再将网络断开或者冻结设备。此时SDK中探测到断链后会不断尝试重连并自动上报5条设备消息。之后重新恢设备和网络,由于SDK中默认使用
QoS1,那5条设备消息也能上报成功并且可以在消息跟踪中看到。4.17 断线重连
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,下面给出前几次失败重连间隔的值:5.设备发放
设备发放服务主要用于设备在多个IoT平台实例的发放、接入和迁移。设备在出厂时只需要烧录一个地址,然后在平台上通过策略制不同的设备接入不同的IoT平台。 设备上电后收到设备发放下发的新的平台连接地址,直接连接新的地址,免去二次烧录设备信息,同时还支持初始化的设备信息同步。 目前支持两大种设备类型的发放,功能如下:
另外,下面的Demo种都需要用到
scopeId, 您可以在设备发放页面点击设备->注册组列表查询:注册设备发放
设备发放需先获取发放接入地址,具体可参考终端节点。
静态策略
静态策略同时支持密钥和证书接入的设备,这里仅展示密钥接入,证书接入可以参考下面的“证书策略“。 更改以下配置iot-device-demo/config/DemoConfigDefault.json
或者以程序化的方式初始化
Bootstrapclient:证书策略
证书策略需要设备通过证书接入,证书制作过程和控制台配置可以参考MQTT X.509证书认证设备使用证书策略发放示例。
更改以下配置iot-device-demo/config/DemoConfigDefault.json
或者以程序化的方式初始化
Bootstrapclient:自定义策略
该策略同时支持密钥和证书接入的设备,使用方式参考自定义策略。
注册组发放
静态策略
静态策略同时支持密钥和证书接入的设备,这里仅展示密钥接入,证书接入可以参考下面的“证书策略“。 密钥需要在创建注册组时获取,详细可参考MQTT 注册组密钥认证静态策略发放示例。
更改以下配置iot-device-demo/config/DemoConfigDefault.json
或者以程序化的方式初始化
Bootstrapclient:证书策略
证书策略需要设备通过证书接入,证书制作过程和控制台配置可以参考 MQTT X.509证书认证设备使用证书策略发放示例。
更改以下配置iot-device-demo/config/DemoConfigDefault.json
或者以程序化的方式初始化
Bootstrapclient:自定义策略
该策略同时支持密钥和证书接入的注册组,使用方式参考MQTT 注册组自定义策略发放示例。
6.泛协议接入
在第三方协议设备不能直接接入平台场景下,泛协议接入提供了在平台外部协议转换的功能。详细介绍可参考通过协议转换网关实现泛协议设备接入
创建网桥
在使用泛协议之前您需要创建网桥。如果要通过平台界面创建,请参考[创建网桥]。
启动网桥
修改接入地址iot-device-demo/config/DemoConfigDefault.json,将之前获取到的网桥接入信息填入对应位置,下面为模板:
使用下面命令启动网桥demo,启动后网桥将作为一个tcp服务端开始监听请求:
出现下面日志表示启动成功:
模拟设备
启动TCP设备模拟器: TCP设备模拟器是一个简单的TCP客户端,负责连接Bridge的端口并将用户输入的消息发送给Bridge:
后面三个参数含义分别是:
切换回网关的日志,如果连接成功将会出现以下内容:
返送消息: 返回到设备模拟器的console,输入下面的内容上报消息:
发送属性: 在设备模拟器的console,输入以下格式的内容上报属性:
切换回网关的日志,如果上报成功将会出现以下内容:
命令响应: 按照设备命令中操作向设备下发命令时需要在设备模拟器中手动回复命令响应,格式如下:
其中
requestId为命令下发时对应的请求id,result则是命令执行结果,0代表成功,其它代表失败。示例如下,7.常见问题
设备ID,即为SDK中deviceId的值。密钥即为SDK中DeviceSecret的值。若是忘记密码,可在设备详情页面重置密钥。8.开源协议