Skip to main content

使用.Net Service SDK调用设备的Direct Method方法

分类:  Azure物联网 标签:  #Azure #IoT Hub # 发布于: 2023-06-13 20:44:52

在我们这个例子中使用.Net Device SDK实现一个模拟设备,并在该设备中准备好需要相应的Direct method方法,之后我们在使用工具来调用该方法,之后使用.Net Serivce SDK来实现该方法的调用。

使用.Net Device SDK创建一个模拟设备

通过Azure Cli IoT扩展工具找到SAS key以及连接字符串。

az iot hub device-identity connection-string show --device-id simDevice --hub-name MyIoThubByCli

该语句返回的结果就是我们需要的连接字符串,那到该以SAS Key为基础的连接字符串之后,我们可以使用如下的代码来连接Azure IoT Hub以及向Azure IoT Hub发送遥测数据。

注意
代码是基于.Net 6

使用.Net SDK连接到Azure IoT Hub

使用如下的命令创建一个项目,并引入需要的SDK包:

dotnet new console -o SimDeviceWithCommand
cd SimDeviceWithCommand
dotnet add package Microsoft.Azure.Devices.Client
code .

打开VS code或者其他编辑器之后,在根目录下添加一个文件GlobalUsing.cs, 打开该文件,添加如下的引用:

global using Microsoft.Azure.Devices.Client;
global using System.Text.Json;
global using System.Text;

然后打开文件Program.cs, 以如下的内容进行替换:

Console.WriteLine("使用SAS Key连接字符串连接Azure IoT Hub, 并相应直接方法的实例");

string s_connectionString = "<你的IoT Hub连接字符串>";

//验证连接字符串是否是正确的
ValidateConnectionString(args);

DeviceClient s_deviceClient = DeviceClient.CreateFromConnectionString(s_connectionString);

Console.WriteLine("请同时按 Control - C 退出应用");

using var cts = new CancellationTokenSource();
Console.CancelKeyPress += (sender, eventArgs) => 
{
    eventArgs.Cancel = true;
    cts.Cancel();
    Console.WriteLine("退出程序......");
};

//设定1秒的时间间隔
var s_telemetryInterval = TimeSpan.FromSeconds(1);

//设定直接方法调用的相应方法
await s_deviceClient.SetMethodHandlerAsync("SetTelemetryInterval",  SetTelemetryInterval, null );



await SendDeviceToCloudMessageAsync(cts.Token);

await s_deviceClient.CloseAsync();

s_deviceClient.Dispose();
Console.WriteLine("模拟设备退出!");

async Task SendDeviceToCloudMessageAsync(CancellationToken ct)
{
    //初始化遥测的配置
    double minTemperature = 20;
    double minHumidity = 60;
    var rand = new Random();

    while ( !ct.IsCancellationRequested )
    {
        double currentTemperature = minTemperature + rand.NextDouble() * 15;
        double currentHumidity = minHumidity + rand.NextDouble() * 20;

        //创建Json编码的消息
        string messageBody = JsonSerializer.Serialize(
            new {
                temperature = currentTemperature,
                humidity = currentHumidity,
            }
        );

        using var message = new Message(Encoding.ASCII.GetBytes(messageBody))
        {
            ContentType = "application/json",
            ContentEncoding = "utf-8",
        };

        message.Properties.Add("temperatureAlert", (currentTemperature> 30 )? "true":"false");

        await s_deviceClient.SendEventAsync(message);
        Console.WriteLine($"{DateTime.Now} > 发送消息:{messageBody}");

        await Task.Delay(s_telemetryInterval, ct);

    }
}

void ValidateConnectionString(string[] appArgs)
{
    if ( appArgs.Any() )
    {
        try 
        {
            var cs = IotHubConnectionStringBuilder.Create(appArgs[0]);
            s_connectionString = cs.ToString();

        } catch ( Exception )
        {
            Console.WriteLine($"错误:无法识别连接字符串参数 `{appArgs[0]}");
            Environment.Exit(-1);
        }

    }
    else
    {
        try 
        {
            _ = IotHubConnectionStringBuilder.Create(s_connectionString);
        } catch(Exception )
        {
            Console.WriteLine("这个Demo需要使用连接字符串连接到Azure IoT Hub");
            Environment.Exit(-1);
        }
    }
}

//直接方法call back函数
Task<MethodResponse> SetTelemetryInterval(MethodRequest methodRequest, object userContext)
{
    var data = Encoding.UTF8.GetString(methodRequest.Data);

    // 检查应用数据
    if (int.TryParse(data, out int telemetryIntervalInSeconds))
    {
        s_telemetryInterval = TimeSpan.FromSeconds(telemetryIntervalInSeconds);

        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine($"遥测数据发送时间间隔更改为 {s_telemetryInterval}");
        Console.ResetColor();

        // Acknowlege the direct method call with a 200 success message
        string result = $"{调用结果:调用直接方法结果: {methodRequest.Name}}";
        return Task.FromResult(new MethodResponse(Encoding.UTF8.GetBytes(result), 200));
    }
    else
    {
        // Acknowlege the direct method call with a 400 error message
        string result = "{调用结果 : 参数非法}";
        return Task.FromResult(new MethodResponse(Encoding.UTF8.GetBytes(result), 400));
    }
}

注意这段代码里几段需要注意的地方:

//设定直接方法调用的相应方法
await s_deviceClient.SetMethodHandlerAsync("SetTelemetryInterval",  SetTelemetryInterval, null );

该方法是在Device SDK上调用,设定直接方法的call back函数。

另外需要注意的是方法SetTelemetryInterval的定义形式。

在控制台中运行该应用: dotnet run

等候应用运行起来后,我们登录到Azure Portal找到Azure IoT Hub资源,并在其中找到设备列表,找到这个设备,点击该设备之后,在如下界面直接调用直接方法:


然后如下图调用:


直接调用之后,可以有如下效果:


使用.Net Service SDK调用直接方法

我们使用.Net Service SDK调用直接方法,我们需要新建一个应用,使用如下的步骤来新建一个应用。

在次之前我们先使用Azure Cli IoT扩展工具取得Service SDK需要的连接字符串:

az iot hub connection-string show --hub-name MyIoTHubByCli --policy-name service
dotnet new console -o CallDirectMethodByServiceSDK
cd CallDirectMethodByServiceSDK
dotnet add package Microsoft.Azure.Devices
code .

打开VS code或者其他编辑器之后,在根目录下添加一个文件GlobalUsing.cs, 打开该文件,添加如下的引用:

global using Microsoft.Azure.Devices;
global using System.Text.Json;
global using System.Text;

然后打开文件Program.cs, 以如下的内容进行替换:

string s_connectionString = "您的Service SDK连接字符串";

Console.WriteLine("使用.Net Service SDK直接调用设备直接方法实例");
//验证连接字符串
ValidateConnectionString(args);

ServiceClient s_serviceClient = ServiceClient.CreateFromConnectionString(s_connectionString);

await InvokeMethodAsync();

s_serviceClient.Dispose();

Console.WriteLine("按任意键退出应用");
Console.ReadKey();

// 调用直接方法
async Task InvokeMethodAsync()
{
    var methodInvocation = new CloudToDeviceMethod("SetTelemetryInterval")
    {
        ResponseTimeout = TimeSpan.FromSeconds(30),
    };
    methodInvocation.SetPayloadJson("10"); //延迟10秒

    // 调用直接方法,并等待结果
    var response = await s_serviceClient.InvokeDeviceMethodAsync("simDevice", methodInvocation);

    Console.WriteLine($"\n调用结果: {response.Status}, 数据:\n\t{response.GetPayloadAsJson()}");
}

void ValidateConnectionString(string[] appArgs)
{
    if ( appArgs.Any() )
    {
        try 
        {
            var cs = IotHubConnectionStringBuilder.Create(appArgs[0]);
            s_connectionString = cs.ToString();

        } catch ( Exception )
        {
            Console.WriteLine($"错误:无法识别连接字符串参数 `{appArgs[0]}");
            Environment.Exit(-1);
        }

    }
    else
    {
        try 
        {
            _ = IotHubConnectionStringBuilder.Create(s_connectionString);
        } catch(Exception )
        {
            Console.WriteLine("这个Demo需要使用连接字符串连接到Azure IoT Hub");
            Environment.Exit(-1);
        }
    }
}

开两个控制台窗口,同时运行该应用就可以看到效果了。