skip to Main Content

I’m trying to debug my custom Azure IoT Edge module in Python using Visual Studio Code, however when I run

client = IoTHubModuleClient.create_from_edge_environment()

I get the following error:

Exception has occurred: FileNotFoundError
[Errno 2] No such file or directory: ''

I’m thinking this may be because I am missing a step in connecting to the Azure IoT Hub but I can’t identify what I might be missing. For reference, I am following the tutorials at https://learn.microsoft.com/en-us/azure/iot-edge/tutorial-python-module?view=iotedge-1.4 and https://learn.microsoft.com/en-us/azure/iot-edge/how-to-vs-code-develop-module?view=iotedge-1.4&tabs=csharp&pivots=iotedge-dev-cli.

I’m simply just trying to build a custom IoT Edge module in Python and debug it locally to confirm it’s working as expected. Thanks in advance for any suggestions.

2

Answers


  1. Chosen as BEST ANSWER

    For those still struggling, I was able to successfully run the module code locally by running the Azure IoT Edge: Start IoT Edge Hub Simulator for Single Module command from the command palette in Visual Studio Code.

    This resolved the FileNotFound error when executing the client = IoTHubModuleClient.create_from_edge_environment() line in my connect_client() method.


  2. Note The following approach has to be used to debug and test the Python modules that are deployed on the Azure IoT Edge device

    I have done some extensive research and testing to debug the Azure IoT Edge modules remotely. Here is what worked for me. The approach to use ptvsd library for remote debugging has been deprecated. Instead, we can use debugpy to debug the python code remotely.

    To enable remote debugging, open inbound TCP port 5678 on the VM where Azure IoT Edge modules are set up. Within the Python program add import debugpy and
    debugpy.listen((‘0.0.0.0’,5678)) statements. Here is my Python code I have tested and deployed to the Azure IoT Edge Device.

    import debugpy
    debugpy.listen(('0.0.0.0',5678))
    
    import json
    import asyncio
    import sys
    import signal
    import threading
    from azure.iot.device.aio import IoTHubModuleClient
    
    
    # Event indicating client stop
    stop_event = threading.Event()
    # global counters
    TEMPERATURE_THRESHOLD = 25
    TWIN_CALLBACKS = 0
    RECEIVED_MESSAGES = 0
    
    def create_client():
        client = IoTHubModuleClient.create_from_edge_environment()
    
        # Define function for handling received messages
        async def receive_message_handler(message):
            global RECEIVED_MESSAGES
            print("Message received")
            size = len(message.data)
            message_text = message.data.decode('utf-8')
            print("    Data: <<<{data}>>> & Size={size}".format(data=message.data, size=size))
            print("    Properties: {}".format(message.custom_properties))
            RECEIVED_MESSAGES += 1
            print("Total messages received: {}".format(RECEIVED_MESSAGES))
    
            if message.input_name == "input1":
                message_json = json.loads(message_text)
                if "machine" in message_json and "temperature" in message_json["machine"] and message_json["machine"]["temperature"] > TEMPERATURE_THRESHOLD:
                    message.custom_properties["MessageType"] = "Alert"
                    print("ALERT: Machine temperature {temp} exceeds threshold {threshold}".format(
                        temp=message_json["machine"]["temperature"], threshold=TEMPERATURE_THRESHOLD
                    ))
                    await client.send_message_to_output(message, "output1")
    
        # Define function for handling received twin patches
        async def receive_twin_patch_handler(twin_patch):
            global TEMPERATURE_THRESHOLD
            global TWIN_CALLBACKS
            print("Twin Patch received")
            print("     {}".format(twin_patch))
            if "TemperatureThreshold" in twin_patch:
                TEMPERATURE_THRESHOLD = twin_patch["TemperatureThreshold"]
            TWIN_CALLBACKS += 1
            print("Total calls confirmed: {}".format(TWIN_CALLBACKS))
    
        try:
            # Set handler on the client
            client.on_message_received = receive_message_handler
            client.on_twin_desired_properties_patch_received = receive_twin_patch_handler
        except:
            # Cleanup if failure occurs
            client.shutdown()
            raise
    
        return client
    
    
    async def run_sample(client):
        # Customize this coroutine to do whatever tasks the module initiates
        # e.g. sending messages
        while True:
            await asyncio.sleep(1000)
    
    
    def main():
        if not sys.version >= "3.5.3":
            raise Exception( "The sample requires python 3.5.3+. Current version of Python: %s" % sys.version )
        print ( "IoT Hub Client for Python" )
    
        # NOTE: Client is implicitly connected due to the handler being set on it
        client = create_client()
    
        # Define a handler to cleanup when module is is terminated by Edge
        def module_termination_handler(signal, frame):
            print ("IoTHubClient sample stopped by Edge")
            stop_event.set()
    
        # Set the Edge termination handler
        signal.signal(signal.SIGTERM, module_termination_handler)
    
        # Run the sample
        loop = asyncio.get_event_loop()
        try:
            loop.run_until_complete(run_sample(client))
        except Exception as e:
            print("Unexpected error %s " % e)
            raise
        finally:
            print("Shutting down IoT Hub Client...")
            loop.run_until_complete(client.shutdown())
            loop.close()
    
    
    if __name__ == "__main__":
        main()
    

    Before building the docker image and deploying the Module, edit the Dockerfile.amd64.debug file to install debugpy on the IoT Edge device. Please refer below for the file content.

    FROM amd64/python:3.7-slim-buster
    
    WORKDIR /app
    
    RUN pip install debugpy
    COPY requirements.txt ./
    RUN pip install -r requirements.txt
    
    COPY . .
    
    CMD [ "python3", "-u", "./main.py" ]
    

    Here is the luanch.json file format for debugging the Python module remotely.

    {
      "version": "0.2.0",
      "configurations": [
        {
          "name": "PythonModule Remote Debug (Python)",
          "type": "python",
          "request": "attach",
          "port": 5678,
          "host": "<host VM IP>",
          "logToFile": true,
          "redirectOutput": true,
          "pathMappings": [
            {
              "localRoot": "${workspaceFolder}/modules/PythonModule",
              "remoteRoot": "/app"
            }
          ],
          "windows": {
            "pathMappings": [
              {
                "localRoot": "${workspaceFolder}\modules\PythonModule",
                "remoteRoot": "/app"
              }
            ]
          }
        },
        {
          "name": "PythonModule Local Debug (Python)",
          "type": "python",
          "request": "launch",
          "program": "${workspaceFolder}/modules/PythonModule/main.py",
          "console": "integratedTerminal",
          "env": {
            "EdgeHubConnectionString": "${config:azure-iot-edge.EdgeHubConnectionString}",
            "EdgeModuleCACertificateFile": "${config:azure-iot-edge.EdgeModuleCACertificateFile}"
          },
          "windows": {
            "program": "${workspaceFolder}\modules\PythonModule\main.py"
          }
        }
      ]
    }
    

    Sign into Docker by entering the following command in the terminal. Sign in with the username, password, and login server from your Azure container registry. You can retrieve these values from the Access keys section of your registry in the Azure portal.

    docker login -u <ACR username> -p <ACR password> <ACR login server>
    

    In the VS Code explorer, right-click the deployment.debug.template.json file and select Build and Push IoT Edge Solution The build and push command starts three operations. First, it creates a new folder in the solution called config that holds the full deployment manifest, built out of information in the deployment template and other solution files. Second, it runs docker build to build the container image based on the appropriate dockerfile for your target architecture. Then, it runs docker push to push the image repository to your container registry.

    You can then deploy the Module to the Azure IoT Edge device using the steps provided in the section Deploy your module. If you have used deployment.debug.template.json file, you would have deployment.debug.amd64 source file generated in the config folder.

    Once the module is deployed and running, you can put a break point in the VS Code and start debugging your IoT Edge Node Module remotely. Please find the below image showing the breakpoint getting hit when I restart the Node Module on the Azure IoT Edge device from Azure portal.

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search