Tentukan simulator perangkat mesin kopi
Pengembang perangkat harus memastikan bahwa perangkat menerapkan perilaku yang ditentukan dalam model perangkat sehingga Azure IoT Central dapat memantau dan mengelola perangkat.
Dalam contoh mesin kopi yang diaktifkan IoT, Anda akan mensimulasikan perangkat untuk memvalidasi skenario Anda sebelum menyambungkan perangkat fisik.
Di sini, Anda akan melihat bagaimana pengembang perangkat menggunakan model perangkat yang ditentukan dalam CoffeeMaker.json, untuk menulis aplikasi C# yang akan mensimulasikan mesin kopi.
Konvensi IoT Plug and Play
Perangkat yang terhubung ke IoT Central harus mengikuti serangkaian konvensi IoT Plug and Play. Salah satu konvensi ini adalah bahwa perangkat harus mengirim Pengidentifikasi Model Kembar Digital (DTMI) saat tersambung. DTMI memungkinkan aplikasi IoT Central menetapkan perangkat ke templat perangkat yang benar.
SDK perangkat Azure IoT menyertakan dukungan untuk konvensi IoT Plug and Play.
Mengulas kode
Kode Sampel untuk repositori GitHub Dokumentasi Azure IoT Central berisi kode sampel. Anda dapat membuka file CoffeeMaker.cs, Program.cs , dan Parameters.cs untuk melihat seluruh kode.
Di Program.cs, Main
metode memanggil SetupDeviceClientAsync ke:
- Provisikan perangkat dan kirim
dtmi:com:example:ConnectedCoffeeMaker;1
ID model sebagai payload. - Buat instans klien perangkat untuk menyambungkan ke IoT Central.
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;
}
Metode utama kemudian membuat instans CoffeeMaker dan memanggil PerformOperationsAsync
metode untuk menangani interaksi dengan IoT Central.
Dalam CoffeeMaker.cs, metode :PerformOperationsAsync
- Mengatur handler untuk menerima
SetMaintenanceMode
danStartBrewing
perintah panggilan balik. - Mengatur handler untuk menangani
OptimalTemperature
perubahan properti yang diinginkan pada kembar perangkat. - Memperbarui
DeviceWarrantyExpired
properti yang dilaporkan kembar perangkat pada startup awal. - Memulai perulangan untuk mengirim telemetri suhu dan kelembaban, baik saat ini sedang diseduh dan ketika cangkir terdeteksi setiap 1 detik.
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);
}
}
telemetri
Metode mengirim SendTelemetryAsync
telemetri dalam format yang ditentukan model perangkat.
//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);
}
Properti
Model menentukan nama properti dan jenis data untuk menyinkronkan nilai properti antara perangkat dan IoT Central melalui perangkat kembar. Metode UpdateDeviceWarranty
ini mengirim status DeviceWarrantyExpired
perangkat dan metode OptimalTemperatureUpdateCallbackAsync
menangani OptimalTemperature
perubahan yang berasal dari IoT Central.
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}.");
}
Perintah
Model menentukan nama perintah dan parameter yang harus digunakan perangkat. Metode HandleMaintenanceModeCommand
dan HandleStartBrewingCommand
menangani perintah yang dikirim dari IoT Central.
// 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));
}
}
Properti dan Tampilan cloud
Properti cloud dan Tampilan tidak memengaruhi kode yang ditulis pengembang perangkat untuk mengimplementasikan model perangkat.