A kávégép-eszközszimulátor definiálása
Az eszközfejlesztőnek gondoskodnia kell arról, hogy az eszköz implementálja az eszközmodellben meghatározott viselkedéseket, hogy az Azure IoT Central figyelhesse és felügyelhesse az eszközt.
Az IoT-kompatibilis kávégép-példában szimulálni fog egy eszközt, amely ellenőrzi a forgatókönyvet a fizikai eszköz csatlakoztatása előtt.
Itt láthatja, hogyan használja egy eszközfejlesztő a CoffeeMaker.jsonban definiált eszközmodellt egy olyan C#-alkalmazás írásához, amely egy kávégépet szimulál.
Az IoT Plug and Playhez kapcsolódó konvenciók
Az IoT Centralhoz csatlakozó eszközöknek követnie kell az IoT Plug and Play konvenciók készletét. Az egyik ilyen konvenció az, hogy az eszköznek a csatlakozáskor el kell küldenie a digitális ikermodell-azonosítót (DTMI ). A DTMI lehetővé teszi, hogy az IoT Central-alkalmazás a megfelelő eszközsablonhoz rendelje az eszközt.
Az Azure IoT-eszköz SDK-k támogatják az IoT Plug and Play konvencióinak támogatását.
A kód áttekintése
Az Azure IoT Central Dokumentációs GitHub-adattár mintakódja tartalmazza a mintakódot. A CoffeeMaker.cs, a Program.cs és a Parameters.cs fájlokat megnyitva megtekintheti a teljes kódot.
A Program.cs metódus a Main
SetupDeviceClientAsync metódust a következőre hívja:
- Az eszköz kiépítése és a
dtmi:com:example:ConnectedCoffeeMaker;1
modellazonosító elküldése hasznos adatként. - Hozzon létre egy eszközügyfél-példányt az IoT Centralhoz való csatlakozáshoz.
private static async Task<DeviceClient> SetupDeviceClientAsync(Parameters parameters, CancellationToken cancellationToken)
{
// Provision a device via DPS, by sending the PnP model Id as DPS payload.
using SecurityProvider symmetricKeyProvider = new SecurityProviderSymmetricKey(parameters.DeviceId, parameters.DevicePrimaryKey, null);
using ProvisioningTransportHandler mqttTransportHandler = new ProvisioningTransportHandlerMqtt();
ProvisioningDeviceClient pdc = ProvisioningDeviceClient.Create(DpsEndpoint, parameters.IdScope,
symmetricKeyProvider, mqttTransportHandler);
var pnpPayload = new ProvisioningRegistrationAdditionalData
{
JsonData = $"{{ \"modelId\": \"{ModelId}\" }}",
};
DeviceRegistrationResult dpsRegistrationResult = await pdc.RegisterAsync(pnpPayload, cancellationToken);
// Initialize the device client instance using symmetric key based authentication, over Mqtt protocol (TCP, with fallback over Websocket) and setting the ModelId into ClientOptions.
DeviceClient deviceClient;
var authMethod = new DeviceAuthenticationWithRegistrySymmetricKey(dpsRegistrationResult.DeviceId, parameters.DevicePrimaryKey);
var options = new ClientOptions
{
ModelId = ModelId,
};
deviceClient = DeviceClient.Create(dpsRegistrationResult.AssignedHub, authMethod, TransportType.Mqtt, options);
return deviceClient;
}
A fő metódus ezután létrehoz egy CoffeeMaker-példányt, és meghívja a PerformOperationsAsync
metódust az IoT Centrallal folytatott interakciók kezelésére.
A CoffeeMaker.cs-ben a PerformOperationsAsync
módszer:
- Beállítja a kezelők fogadását
SetMaintenanceMode
ésStartBrewing
a visszahívások parancsát. - Beállítja a kezelőt az ikereszköz kívánt tulajdonságváltozásának kezelésére
OptimalTemperature
. DeviceWarrantyExpired
Frissítések ikereszköz jelentett tulajdonsága az első indításkor.- Elindít egy ciklust a hőmérséklet- és páratartalom-telemetriai adatok küldéséhez, függetlenül attól, hogy éppen főzik-e, és ha egy csésze 1 másodpercenként észlelhető.
public async Task PerformOperationsAsync(CancellationToken cancellationToken)
{
Console.WriteLine($"Device successfully connected to Azure IoT Central");
Console.WriteLine($"- Set handler for \"SetMaintenanceMode\" command.");
await _deviceClient.SetMethodHandlerAsync("SetMaintenanceMode", HandleMaintenanceModeCommand, _deviceClient, cancellationToken);
Console.WriteLine($"- Set handler for \"StartBrewing\" command.");
await _deviceClient.SetMethodHandlerAsync("StartBrewing", HandleStartBrewingCommand, _deviceClient, cancellationToken);
Console.WriteLine($"- Set handler to receive \"OptimalTemperature\" updates.");
await _deviceClient.SetDesiredPropertyUpdateCallbackAsync(OptimalTemperatureUpdateCallbackAsync, _deviceClient, cancellationToken);
Console.WriteLine("- Update \"DeviceWarrantyExpired\" reported property on the initial startup.");
await UpdateDeviceWarranty(cancellationToken);
while (!cancellationToken.IsCancellationRequested)
{
await SendTelemetryAsync(cancellationToken);
await Task.Delay(1000, cancellationToken);
}
}
Telemetria
A SendTelemetryAsync
metódus az eszközmodell által megadott formátumban küld telemetriát.
//Send temperature and humidity telemetry, whether it's currently brewing and when a cup is detected.
private async Task SendTelemetryAsync(CancellationToken cancellationToken)
{
//Simulate the telemetry values
double temperature = _optimalTemperature + (_random.NextDouble() * 4) - 2;
double humidity = 20 + (_random.NextDouble() * 80);
// Cup timer - every 20 seconds randomly decide if the cup is present or not
if (_cupTimer > 0)
{
_cupTimer--;
if(_cupTimer == 0)
{
_cupState = _random.NextDouble() > 0.5 ? "detected" : "notdetected";
_cupTimer = 20;
}
}
// Brewing timer
if (_brewingTimer > 0)
{
_brewingTimer--;
//Finished brewing
if (_brewingTimer == 0)
{
_brewingState = "notbrewing";
}
}
// Create JSON message
string messageBody = JsonConvert.SerializeObject(
new
{
WaterTemperature = temperature,
AirHumidity = humidity,
CupDetected = _cupState,
Brewing = _brewingState
});
using var message = new Message(Encoding.ASCII.GetBytes(messageBody))
{
ContentType = "application/json",
ContentEncoding = "utf-8",
};
//Show the information in console
double infoTemperature = Math.Round(temperature, 1);
double infoHumidity = Math.Round(humidity, 1);
string infoCup = _cupState == "detected" ? "Y" : "N";
string infoBrewing = _brewingState == "brewing" ? "Y" : "N";
string infoMaintenance = _maintenanceState ? "Y" : "N";
Console.WriteLine($"Telemetry send: Temperature: {infoTemperature}ºC Humidity: {infoHumidity}% " +
$"Cup Detected: {infoCup} Brewing: {infoBrewing} Maintenance Mode: {infoMaintenance}");
//Send the message
await _deviceClient.SendEventAsync(message, cancellationToken);
}
Properties
A modell megadja a tulajdonságneveket és az adattípusokat, hogy az ikereszközön keresztül szinkronizálja a tulajdonságértékeket az eszköz és az IoT Central között. A metódus UpdateDeviceWarranty
elküldi az DeviceWarrantyExpired
eszköz állapotát, és a metódus OptimalTemperatureUpdateCallbackAsync
kezeli OptimalTemperature
az IoT Centralból érkező változásokat.
private async Task OptimalTemperatureUpdateCallbackAsync(TwinCollection desiredProperties, object userContext)
{
const string propertyName = "OptimalTemperature";
(bool optimalTempUpdateReceived, double optimalTemp) = GetPropertyFromTwin<double>(desiredProperties, propertyName);
if (optimalTempUpdateReceived)
{
Console.WriteLine($" * Property: Received - {{ \"{propertyName}\": {optimalTemp}°C }}.");
//Update reported property to In Progress
string jsonPropertyPending = $"{{ \"{propertyName}\": {{ \"value\": {optimalTemp}, \"ac\": {(int)StatusCode.InProgress}, " +
$"\"av\": {desiredProperties.Version}, \"ad\": \"In progress - reporting optimal temperature\" }} }}";
var reportedPropertyPending = new TwinCollection(jsonPropertyPending);
await _deviceClient.UpdateReportedPropertiesAsync(reportedPropertyPending);
Console.WriteLine($" * Property: Update - {{\"{propertyName} \": {optimalTemp}°C }} is {StatusCode.InProgress}.");
//Update the optimal temperature
_optimalTemperature = optimalTemp;
//Update reported property to Completed
string jsonProperty = $"{{ \"{propertyName}\": {{ \"value\": {optimalTemp}, \"ac\": {(int)StatusCode.Completed}, " +
$"\"av\": {desiredProperties.Version}, \"ad\": \"Successfully updated optimal temperature\" }} }}";
var reportedProperty = new TwinCollection(jsonProperty);
await _deviceClient.UpdateReportedPropertiesAsync(reportedProperty);
Console.WriteLine($" * Property: Update - {{\"{propertyName} \": {optimalTemp}°C }} is {StatusCode.Completed}.");
}
else
{
Console.WriteLine($" * Property: Received an unrecognized property update from service:\n{desiredProperties.ToJson()}");
}
}
private async Task UpdateDeviceWarranty(CancellationToken cancellationToken)
{
const string propertyName = "DeviceWarrantyExpired";
var reportedProperties = new TwinCollection();
reportedProperties[propertyName] = _warrantyState;
await _deviceClient.UpdateReportedPropertiesAsync(reportedProperties, cancellationToken);
Console.WriteLine($" * Property: Update - {{ \"{propertyName}\": {_warrantyState} }} is {StatusCode.Completed}.");
}
Parancsok
A modell megadja az eszköz által használandó parancsneveket és paramétereket. Az IoT Centraltól küldött parancsok metódusai HandleMaintenanceModeCommand
és HandleStartBrewingCommand
kezelése.
// The callback to handle "SetMaintenanceMode" command.
private Task<MethodResponse> HandleMaintenanceModeCommand(MethodRequest request, object userContext)
{
try
{
Console.WriteLine(" * Maintenance command received");
if (_maintenanceState)
{
Console.WriteLine(" - Warning: The device is already in the maintenance mode.");
}
//Set state
_maintenanceState = true;
//Send response
byte[] responsePayload = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject("Success"));
return Task.FromResult(new MethodResponse(responsePayload, (int)StatusCode.Completed));
}
catch (Exception ex)
{
Console.WriteLine($"Exception while handling \"SetMaintenanceMode\" command: {ex}");
return Task.FromResult(new MethodResponse((int)StatusCode.BadRequest));
}
}
// The callback to handle "StartBrewing" command.
private Task<MethodResponse> HandleStartBrewingCommand(MethodRequest request, object userContext)
{
try
{
Console.WriteLine(" * Start brewing command received");
if (_brewingState == "brewing")
{
Console.WriteLine(" - Warning: The device is already brewing.");
}
if (_cupState == "notdetected")
{
Console.WriteLine(" - Warning: There is no cup detected.");
}
if (_maintenanceState)
{
Console.WriteLine(" - Warning: The device is in maintenance mode.");
}
//Set state - brew for 30 seconds
if (_cupState == "detected" && _brewingState == "notbrewing" && !_maintenanceState)
{
_brewingState = "brewing";
_brewingTimer = 30;
}
//Send response
byte[] responsePayload = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject("Success"));
return Task.FromResult(new MethodResponse(responsePayload, (int)StatusCode.Completed));
}
catch (Exception ex)
{
Console.WriteLine($"Exception while handling \"StartBrewing\" command: {ex}");
return Task.FromResult(new MethodResponse((int)StatusCode.BadRequest));
}
}
Felhőtulajdonságok és nézetek
A felhőtulajdonságok és nézetek nem befolyásolják az eszközfejlesztő által az eszközmodell implementálásához írt kódot.