Tentukan simulator perangkat mesin kopi

Selesai

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 dan StartBrewing 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.