Kahve makinesi cihaz simülatörünü tanımlama

Tamamlandı

Cihaz geliştiricisi, Azure IoT Central'ın cihazı izleyebilmesi ve yönetebilmesi için cihazın cihaz modelinde tanımlanan davranışları uyguladığından emin olmalıdır.

IoT özellikli kahve makineleri örneğinde, fiziksel bir cihazı bağlamadan önce senaryonuzu doğrulamak için bir cihazın benzetimini yapacaksınız.

Burada, bir cihaz geliştiricisinin kahve makinesi simülasyonu yapacak bir C# uygulaması yazmak için CoffeeMaker.json dosyasında tanımlanan cihaz modelini nasıl kullandığını göreceksiniz.

IoT Tak Çalıştır kuralları

IoT Central'a bağlanan cihazların IoT Tak Çalıştır kural kümesine uyması gerekir. Bu kurallardan biri, bir cihazın bağlandığında Dijital İkiz Modeli Tanımlayıcısı'nı (DTMI) göndermesi gerektiğidir. DTMI, IoT Central uygulamasının cihazı doğru cihaz şablonuna atamasını sağlar.

Azure IoT cihaz SDK'ları IoT Tak Çalıştır kuralları için destek içerir.

Kodu gözden geçirin

Azure IoT Central Belgeleri GitHub deposu için örnek kod, örnek kodu içerir. Kodun tamamını görmek için CoffeeMaker.cs, Program.cs ve Parameters.cs dosyalarını açabilirsiniz.

Program.cs dosyasında Main yöntemi SetupDeviceClientAsync öğesini şu şekilde çağırır:

  • Cihazı sağlayın ve model kimliğini yük olarak gönderin dtmi:com:example:ConnectedCoffeeMaker;1 .
  • IoT Central'a bağlanmak için bir cihaz istemci örneği oluşturun.
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;
}

Ardından ana yöntem bir CoffeeMaker örneği oluşturur ve IoT Central ile etkileşimleri işlemek için yöntemini çağırır PerformOperationsAsync .

CoffeeMaker.cs dosyasında yöntemiPerformOperationsAsync:

  • İşleyicileri geri çağırmaları alacak SetMaintenanceMode ve StartBrewing komutla çağıracak şekilde ayarlar.
  • cihaz ikizinde istenen özellik değişikliklerini işlemek OptimalTemperature için işleyiciyi ayarlar.
  • DeviceWarrantyExpired Güncelleştirmeler ilk başlangıçta cihaz ikizi bildirilen özelliği.
  • Sıcaklık ve nem telemetrisi göndermek için şu anda bira hazırlayıp hazırlamadığını ve her 1 saniyede bir fincan algılandığında bir döngü başlatır.
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

SendTelemetryAsync yöntemi, telemetri verilerini cihaz modelinin belirttiği biçimde gönderir.

//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);
}

Özellikler

Model, cihaz ikizi aracılığıyla cihaz ile IoT Central arasında özellik değerlerini eşitlemek için özellik adlarını ve veri türlerini belirtir. yöntemi UpdateDeviceWarranty cihazın durumunu gönderir DeviceWarrantyExpired ve yöntemi OptimalTemperatureUpdateCallbackAsync IoT Central'dan gelen değişiklikleri işler OptimalTemperature .

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}.");
}

Komutlar

Model, cihazın kullanması gereken komut adlarını ve parametreleri belirtir. HandleMaintenanceModeCommand Yöntemler ve HandleStartBrewingCommand IoT Central'dan gönderilen komutları işler.

// 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));
    }
}

Bulut özellikleri ve Görünümler

Bulut özellikleri ve Görünümler, bir cihaz geliştiricisinin cihaz modelini uygulamak için yazdığı kodu etkilemez.