【问题标题】:Azure IoT Edge ModuleClient invoke direct method in another moduleAzure IoT Edge ModuleClient 在另一个模块中调用直接方法
【发布时间】:2018-08-29 09:04:28
【问题描述】:

我正在使用 Azure IoT Edge 运行时在 Windows 主机操作系统上运行 Linux 容器。 我有两个模块,ModuleA 和 ModuleB。 ModuleA 有一个注册的直接方法,称为“MethodA”,ModuleB 有一个注册的直接方法,称为“MethodB”。

当我调用 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 Code 调试器中我得到了异常

抛出异常:System.Private.CoreLib.dll 中的“Microsoft.Azure.Devices.Client.Exceptions.IotHubCommunicationException”

当我检查我的 Message/StackTrace-print 日志时,我得到:

发送请求时出错。 在 Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.d__21.MoveNext() --- 从先前抛出异常的位置结束堆栈跟踪 --- 在 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务) 在 Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.d__172.MoveNext() --- 从先前抛出异常的位置结束堆栈跟踪 --- 在 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务) 在 Microsoft.Azure.Devices.Client.ModuleClient.d__57.MoveNext() --- 从先前抛出异常的位置结束堆栈跟踪 --- 在 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务) 在 System.Runtime.CompilerServices.TaskAwaiter1.GetResult() 在 OrderGateway.Program.d__3.MoveNext() 在 /app/Program.cs:line 92

我尝试使用带有 TCP/WebSocket_Only 的 Amqp 以及带有 TCP/WebSocket_Only 的 Mqtt 来初始化 ModuleClient。 我还尝试在 Ubuntu 18.04 VM 上使用这些模块运行 IoT Edge 运行时,但没有成功。

即使我尝试使用 ioTHubModuleClient.InvokeMethodAsync() 调用位于同一模块中的方法,我也会得到相同的异常/堆栈跟踪...

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

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

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

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

【问题讨论】:

    标签: c# azure azure-iot-hub azure-iot-sdk azure-iot-edge


    【解决方案1】:

    我刚刚制作了这个场景,但它与您的描述有点不同。

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

           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: Device {"message":"Client angelodTransparentGateway/ModuleA not found","exceptionMessage":"","status":0,"payload": null} 未注册 在 Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.ExecuteAsync(HttpMethod httpMethod,Uri requestUri,Func3 modifyRequestMessageAsync, Func2 isSuccessful,Func3 processResponseMessageAsync, IDictionary2 errorMappingOverrides,CancellationToken cancelToken) 在 Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.PostAsync[T1,T2](Uri requestUri,T1 实体,IDictionary2 errorMappingOverrides, IDictionary2 customHeaders,CancellationToken cancelToken) 在 Microsoft.Azure.Devices.Client.ModuleClient.InvokeMethodAsync(Uri uri,MethodRequest methodRequest,CancellationToken cancelToken) 在 /app/Program.cs:line 74 中的 ModuleB.Program.Init() 处

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

    MethodA 已被调用 接收到的数据:{"Message":"Hello"}

    如果您从 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 上更新)。

    【讨论】:

    • 感谢您的帮助。当您说不要忘记使用 .NET 2.1 版(在您的 dockerfile 上更新)。您的意思是将相应模块 .csproj 文件中的 netcoreapp2.0 更改为 netcoreapp2.1 吗?这就是我所做的,但现在我得到了:C:\Program Files\dotnet\sdk\2.1.202\Sdks\Microsoft.NET.Sdk\build\Microsoft.NET.TargetFrameworkInference.targets(135,5): error : The current .NET SDK does not support targeting .NET Core 2.1.
    • @NoExit,我已经验证了这个问题,正如 Angelo 所说,将 dotnet 运行时更改为 2.1,它可以工作。如果你使用linux容器,你应该在Dockerfile.windows-amd64文件中设置microsoft/dotnet:2.1-sdk AS build-env & microsoft/dotnet:2.1-runtime
    • 感谢@MichaelXu-MSFT,如果我使用 Windows 容器会怎样?我应该也将这些变量设置为相同吗?我会努力让它尽快工作。
    • @NoExit,是的,如果你使用 windows contianer,你应该在 Dockerfile.windows-amd64 中设置相同的值。
    • @MichaelXu-MSFT 当像你一样从 Init() 调用方法时,上面的示例现在可以正常工作。尽管在另一个直接方法回调中尝试执行相同操作时,我确实收到了错误: Device {"Message":"{\"errorCode\":404001,\"trackingId\":\"ID\",\"message \":\"设备不可用\",\"timestampUtc\":\"TIMESTAMP\"}","ExceptionMessage":""} 未注册。请查看我的后续问题:github.com/Azure/iotedge/issues/204 非常感谢任何帮助!
    猜你喜欢
    • 1970-01-01
    • 2023-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-01
    相关资源
    最近更新 更多