Search code examples
python-asynciopyserialopc-ua

Why do i get loop already running when using classes


So i am trying to read data from a serial port with python.

I am planning on feeding this data to my asyncua server in the future. Comming from c# i always have the urge to make classes. However when i run the code i get the Exception that the loop is already running.

As far as i understood tasks will be put into the loop and then executed so i think i am missing something here.

From a best practice approach does it make sense to make classes in python like this or is there a "better" way? I appreciate all input.

I have made a Class which is supposed to handle the reading from the serial port and later should assign the value to the nodes on the opc server.

For the reading i used the examples made here pySerialDocumentation

import asyncio
import serial_asyncio
from async_serial_output import OutputProtocol,InputChunkProtocol
import logging


class EboxAndino():
    """
    Class which manages the connection to the serial port
    """
    ebox = None
    timeout: int
    counter1Opc = None
    counter2Opc = None
    input1Opc = None
    input2Opc = None
    Heartbeat = None
    logger = None
    debug = False
    setup_dict = {}
    counterValueDict = {}

    hwPort = None
    baud = None
    timeout = None
    debug = False
    logger = logging.getLogger('foo')
    hBeat = False
    ser = None
    rts = False

    def __init__(self, ebox, hwPort, baud, timeout, debug, loggerName) -> None:
        """
              @param ebox: opcObj
              @param hwPort: string
              @param baud: int
              @param timeout: int
              @param debug: bool
              @param loggerName: string
              """
        # self = EboxAndino('Ebox')
        self.ebox = ebox
        self.hwPort = hwPort
        self.baud = baud
        self.timeout = timeout
        self.debug = debug
        self.hBeat = False
        self.ser = None
        self.rts = False
        print('created!')


    async def initCounter(self):
        
        self.hBeat = False
        self.counterValueDict = {"Counter1": None,
                                 "Counter2": None,
                               
                                 "Input1": None,
                                 "Input2": None,
                                }
        setup_list = [*self.counterValueDict]
       
        print ('Counter is up!')

    def get_values(self):
        if self.counterValueDict:
            return self.counterValueDict
        else:
            return None

    async def line_reader(self,loop):
        coro = await serial_asyncio.create_serial_connection(loop, OutputProtocol, '/dev/ttyAMA0', baudrate = 38400)
        await asyncio.sleep(1)
        transport, protocol = loop.run_until_complete(coro)

        await asyncio.sleep(0.5)
        loop.run_forever()
        loop.close()

With my main beeing

async def main():

    loop = asyncio.get_event_loop()
    andino = EboxAndino('foo', '/dev/ttyAMA0', 38400, 1, True, 'foo')
    await andino.initCounter()
    
    await andino.line_reader(loop)
loop = asyncio.get_event_loop()
asyncio.sleep(2)
loop.run_until_complete(main())

Running this will give me the first line split into 4 recieved packages. After it it will get an exception that the loop is already running. Why?

As a follow up question where would be the best place to grab and store the values to the opc server?

Thank you everyone!


Solution

  • In asyncio you can only run one loop. The example you use is for an synchrnous context. But you are now in the async context already. So there is no need to call loop.run_until.

     async def line_reader(self,loop):
            transport, protocol = await serial_asyncio.create_serial_connection(loop, OutputProtocol, '/dev/ttyAMA0', baudrate = 38400)
            await asyncio.sleep(1)
            await asyncio.sleep(0.5)