Azure IoT边缘模块客户端在另一个模块中调用直接方法



我使用的是在Windows主机操作系统上运行Linux容器的Azure IoT Edge运行时。我有两个模块,模块A和模块B。ModuleA有一个名为"MethodA"的注册直接方法,ModuleB有一种名为"method B"的注册间接方法。

当我调用MethodA时,我希望该方法调用位于另一个模块中的MethodB(但在同一个IoT Edge运行时中运行)。

我正在使用Azure IoT SDK for c#和在ModuleA的Init()函数中,我有:

await ioTHubModuleClient.SetMethodHandlerAsync("MethodA", MethodA, ioTHubModuleClient);

在ModuleB的Init()函数中,我有:

await ioTHubModuleClient.SetMethodHandlerAsync("MethodB", MethodB, null);

MethodA(其作用类似于代理方法):

static async Task<MethodResponse> MethodA(MethodRequest methodRequest, object moduleClient)
{
try 
{
ModuleClient ioTHubModuleClient = (ModuleClient)moduleClient;             
// Get deviced id of this device, exposed as a system variable by the iot edge runtime
var deviceId = System.Environment.GetEnvironmentVariable("IOTEDGE_DEVICEID");
// Create the request
MethodRequest request = new MethodRequest("MethodB", Encoding.UTF8.GetBytes("{ "Message": "Hello" }"));
// Execute request
var resp = await ioTHubModuleClient.InvokeMethodAsync(deviceId, "ModuleB", request);
return resp;               
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
return await Task.FromResult(new MethodResponse(500));
} 
}

我试图从MethodA:调用的MethodB

private static Task<MethodResponse> MethodB(MethodRequest methodRequest, object userContext)
{
Console.WriteLine("MethodB has been called");
// Get data but we do not do anything with it
var data = Encoding.UTF8.GetString(methodRequest.Data);
Console.WriteLine("Received data: " + data.ToString());
var methodResponse = new MethodResponse(Encoding.UTF8.GetBytes("{"status": "ok"}"), 200);
return Task.FromResult(methodResponse);
} 

请注意,我可以从azure门户单独调用这两个方法,也可以使用azure SDK for C#->ServiceClient类。

我的程序在线上崩溃

var resp = await ioTHubModuleClient.InvokeMethodAsync(deviceId, "ModuleB", request);

在MethodA和VS代码调试器中,我得到异常

引发异常:System.Private.CoreLib.dll 中的"Microsoft.Azure.Device.Client.Exceptions.IotHubCommunicationException">

当我检查我的Message/StackTrace打印的日志时,我得到:

发送请求时出错。位于Microsoft.Azure.DevicesClient.Transport.HttpClientHelper.d__21.MoveNext()---从引发异常的前一位置开始的堆栈结尾跟踪---在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Sthrow()位于System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)位于Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.d__172.MoveNext()---从引发异常的前一位置开始的堆栈结尾跟踪---在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Sthrow()位于System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)位于Microsoft.Azure.Devices.Client.ModuleClient.d_57.MoveNext()---从引发异常的前一位置开始的堆栈结尾跟踪---在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Sthrow()位于System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)位于System.Runtime.CompilerServices.TaskAwaiter1.GetResult()在/app/Program.cs:line 92 中的OrderGateway.Program.d_3.MoveNext()

我已经尝试使用带有TCP/WebSocket_Only的Amqp和带有TCP/Web Socket_Only的Mqtt来初始化ModuleClient。我还尝试过在Ubuntu 18.04虚拟机上使用这些模块运行IoT Edge运行时,但没有成功。

即使当我尝试用ioTHubModuleClient.IInvokeMethodAsync()调用位于同一模块中的方法时,我也会得到相同的异常/stacktrace。。。

我还试图从ModuleA的Init()函数调用MethodB,以尝试在回调上下文中不调用直接方法,但我确实得到了相同的异常。

github上有一个悬而未决的问题,您可以在这里找到:https://github.com/Azure/iotedge/issues/204

但感觉它有点停滞不前,我没有得到任何帮助。

据我所知,这应该是可能的,但我不知道我错过了什么?

我只是让这个场景发挥作用,但它与您的描述有点不同。

所以,以下是我所做的:1-创建了两个模块,模块A和模块B;2-在模块A中添加了您的MethodA,在模块B中添加了MethodB;3-在模块B(不是模块A)上,我添加了以下代码:

while(true)
{
try
{
Console.WriteLine($"Invoking method On moduleA");
var deviceId = System.Environment.GetEnvironmentVariable("IOTEDGE_DEVICEID");
MethodRequest request = new MethodRequest("MethodA", Encoding.UTF8.GetBytes("{ "Message": "Hello" }"));
var response = await ioTHubModuleClient.InvokeMethodAsync(deviceId, "ModuleA", request).ConfigureAwait(false);
Console.WriteLine($"Received response with status {response.Status}");
}
catch (Exception ex)
{
Console.WriteLine($"Error invoking method {ex}");
}
await Task.Delay(TimeSpan.FromSeconds(20)).ConfigureAwait(false);
}

就是这样。一开始你会得到一个像这样的例外:

调用方法Microsoft.Azure.Devices.Client.Exceptions.DeviceNotFoundException时出错:未注册设备{"message":"找不到客户端angelodTransparentGateway/ModuleA"、"exceptionMessage":"、"status":0、"payload":null}位于Microsoft.Azure.Device.Client.Transport.HttpClientHelper.ExecuteAsync(HttpMethod HttpMethod,Uri requestUri,Func3 modifyRequestMessageAsync, Func2 isSuccessful,Func3 processResponseMessageAsync, IDictionary2 errorMappingOverrides,CancellationToken cancel Token)位于Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.PostAsync[T1,T2](Uri-requestUri,T1实体,IDictionary2 errorMappingOverrides, IDictionary2 customHeaders,CancellationToken cancellionToken)位于Microsoft.Azure.Device.Client.ModuleClient.IInvokeMethodAsync(Uri-Uri、MethodRequest MethodRequest、CancellationToken cancellionToken)在/app/Program.cs:line 74 中的ModuleB.Program.Init()

但这是经过设计的,只是因为部署后创建模块需要时间,但在不到1分钟的时间内,我就成功地得到了结果:

已调用MethodA收到的数据:{"消息":"你好"}

如果从ModuleA调用MethodB,则不会调用MethodA。但为了确保它有效,我还测试了调用模块B的模块A。以下是代码:

while(true)
{
try
{
Console.WriteLine($"Invoking method On moduleB");
var deviceId = System.Environment.GetEnvironmentVariable("IOTEDGE_DEVICEID");
MethodRequest request = new MethodRequest("MethodB", Encoding.UTF8.GetBytes("{ "Message": "Hello" }"));

var response = await ioTHubModuleClient.InvokeMethodAsync(deviceId, "ModuleB", request).ConfigureAwait(false);
Console.WriteLine($"Received response with status {response.Status}");
}
catch (Exception ex)
{
Console.WriteLine($"Error invoking method {ex}");
}
await Task.Delay(TimeSpan.FromSeconds(20)).ConfigureAwait(false);
}

两人都工作了。不要忘记使用.NET 2.1版本(在你的dockerfile中更新)。

最新更新