我们之前学习和讨论过设备状态,都在关注设备的连接状态,而实际上设备的状态可以包含更多的信息:例如设备上散热风扇的启动频率和条件,设备上目前的网络是连接在上还是卡上,设备上的某个指示灯是亮着还是熄灭了,这些信息也是设备状态信息,那么设备是如何向云报告这些状态呢?
开始之前我们先要学习一下一个最佳实践:
每次设备和云之间的连接状态从其他状态变为之后,我们都需要让设备从云读取完整的, 并根据中包含的预期属性(), 对设备相应的组件进行相应的配置。
在每次连接建立并拉取设备完整的, 配置设备成功后,云是如何了解到该设备是不是将要求的配置配置到位了呢?答案是:设备可以利用向云反馈当前的状态。
官方的文档很多地方提到(预期属性)大多数时候和结合使用,但是实际的情况是不一定,在各种场景下完全可以单独利用来完成状态报告,例如:
利用定期上报连接状态,当然这个不能太频繁。
利用上报某个长时间运行工作的状态,例如更新某个组件。
利用上报某个组件的当前状态,例如风扇的转速,开关的闭合。
而后端的应用完全可以监听上报属性更改事件,从而对设备的某些及时状态做出反应。
我们下面通过一个例子来演示设备应用和后端应用如何配合利用上报属性。
注意
我们开始设置设备端的应用,需要注意的是,该设备端的应用设计,我们参考了文章:创建一个基于Windows Service的产线设备应用, 请仔细参考一下该文档,另外设备的所有代码您可以在这里找到:https://github.com/hylinux/azure-iot-hub-examples/tree/main/DeviceTwinDemo/DeviceApp
请按照上述的代码添加如下几个文件:
另外需要注意更改一下文件:
net7.0-windows
enable
enable
DeviceApp
exe
true
win-x64
x64
6ec0f197-3d58-4a02-93b8-e99574e46609
需要注意的是:
是按照如下的步骤自动生成的:
参考上述代码库和步骤,完成您的代码配置之后,就可以使用:
运行你的设备App了。
为了演示我们本章提到的两个知识点,我们需要仔细参考文件的相应方法:
演示最佳实践:当设备连接状态变为之后,立即让设备从云抓回,并解析对设备相应的组件进行配置,代码参考:
private async void ConnectionStatusChangeHandlerAsync(ConnectionStatus status, ConnectionStatusChangeReason reason)
{
_logger.LogDebug($"Connection status changed: status=, reason=");
s_connectionStatus = status;
switch (status)
{
case ConnectionStatus.Connected:
_logger.LogDebug("### The DeviceClient is CONNECTED; all operations will be carried out as normal.");
// Call GetTwinAndDetectChangesAsync() to retrieve twin values from the server once the connection status changes into Connected.
// This can get back "lost" twin updates in a device reconnection from status like Disconnected_Retrying or Disconnected.
//
// Howevever, considering how a fleet of devices connected to a hub may behave together, one must consider the implication of performing
// work on a device (e.g., get twin) when it comes online. If all the devices go offline and then come online at the same time (for example,
// during a servicing event) it could introduce increased latency or even throttling responses.
// For more information, see https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-quotas-throttling#traffic-shaping.
await GetTwinAndDetectChangesAsync(s_appCancellation!.Token);
_logger.LogDebug("The client has retrieved twin values after the connection status changes into CONNECTED.");
break;
上述代码是在监控设备连接状态变化的过程中,如果发现该连接的状态变为,立即调用异步方法, 我们看一下它的代码:
private async Task GetTwinAndDetectChangesAsync(CancellationToken cancellationToken)
{
// For the following call, we execute with a retry strategy with incrementally increasing delays between retry.
Twin twin = await s_deviceClient!.GetTwinAsync(cancellationToken);
_logger.LogInformation($"Device retrieving twin values: ");
_logger.LogInformation($"Device Id: ");
_logger.LogInformation($"Twin Etags: ");
_logger.LogInformation($"Twin Version:");
TwinCollection twinCollection = twin.Properties.Desired;
long serverDesiredPropertyVersion = twinCollection.Version;
// Check if the desired property version is outdated on the local side.
if (serverDesiredPropertyVersion > s_localDesiredPropertyVersion)
{
_logger.LogDebug($"The desired property version cached on local is changing from to .");
await HandleTwinUpdateNotificationsAsync(twinCollection, cancellationToken);
}
}
仔细观察上述代码,还有一个知识点,除了从云拿回,另外还将拿回来的的版本号和本地存储的版本号进行了对比:
TwinCollection twinCollection = twin.Properties.Desired;
long serverDesiredPropertyVersion = twinCollection.Version;
// Check if the desired property version is outdated on the local side.
if (serverDesiredPropertyVersion > s_localDesiredPropertyVersion)
{
_logger.LogDebug($"The desired property version cached on local is changing from to .");
await HandleTwinUpdateNotificationsAsync(twinCollection, cancellationToken);
}
如果服务端的版本号大于本地存储的版本号,才会调用方法:
我们再进一步看一下代码:
private async Task HandleTwinUpdateNotificationsAsync(TwinCollection twinUpdateRequest, object userContext)
{
var reportedProperties = new TwinCollection();
_logger.LogInformation($"Twin property update requested: \n");
// For the purpose of this sample, we'll blindly accept all twin property write requests.
foreach (KeyValuePair desiredProperty in twinUpdateRequest)
{
_logger.LogInformation($"Setting property to .");
reportedProperties[desiredProperty.Key] = desiredProperty.Value;
}
s_localDesiredPropertyVersion = twinUpdateRequest.Version;
_logger.LogDebug($"The desired property version on local is currently .");
try
{
// For the purpose of this sample, we'll blindly accept all twin property write requests.
await s_deviceClient!.UpdateReportedPropertiesAsync(reportedProperties, s_appCancellation!.Token);
}
catch (OperationCanceledException)
{
// Fail gracefully on sample exit.
}
}
这部分代码里需要注意的是更新本地版本号,读取预期属性,并在属性设置成功之后,调用方法更新
知识点2:如何从设备上向云上报上报属性,这个其实非常简单:
var reportedProperties = new TwinCollection();
reportedProperties[desiredProperty.Key] = desiredProperty.Value;
await s_deviceClient!.UpdateReportedPropertiesAsync(reportedProperties, s_appCancellation!.Token);
直接调用方法 即可完成上报属性的更新。
如上是代码的演示和说明,回到上报属性的更新上:
在上报属性里添加一个新的属性,表示添加一个新的属性。
在上报属性里更改已有属性的值,表示更新该属性。
在上报属性里将已有属性的值置为, 则表示删除该属性。
我们下一节演示如何通过来更改预期属性() 和监控查询上报属性()的值和更改事件。
领取专属 10元无门槛券
私享最新 技术干货