Search code examples
pythonvolttron

VOLTTRON agent run different "tasks" asynchronously


I'm creating VOLTTRON control agents with the agent creation wizard, is it possible to run different tasks asynchronously inside the agent code?

For example under a particular condition (a demand response event) I am hoping to use the actuator agent to read zone temperatures every 60 seconds and store that data inside a data structure and then every 300 seconds (possibly more often) evaluate the sensor data to make some decisions if some zones can be BACnet released via the actuator agent depending sensor readings/conditions. And another task running every 5 seconds to see if the demand response event is still active or expired.

Hopefully this isn't a silly question, but could more than one periodic be utilized inside an agent to run different "tasks"? Sort of like this below note the agent onstart:

class Agent:
    def __init__(self):
        self.stuff1 = False
        self.stuff2 = False

    def get_sensor_readings(self):
        print("Getting sensor readings!!!")
        

    def evauluate_data(self):
        print("Evaluating the data!!!")
        
        
    def checker(self):
        print("Checking the event status!!!")
  


        

@Core.receiver("onstart")
def onstart(self, sender) -> None:
    """The agent has started."""
    _log.info(f"Sender {sender}")
    _log.info("Starting agent...")

        
    self.core.periodic(60, self.get_sensor_readings)
    self.core.periodic(300, self.evauluate_data)
    self.core.periodic(5, self.checker)
    

@Core.receiver("onstop")
def onstop(self, sender, **kwargs):

Or could I use asyncio inside an agent? Maybe something like this where the anyncio tasks get cancelled when not in use...

import asyncio

class Agent:
    def __init__(self):
        self.stuff1 = False
        self.stuff2 = False

    async def get_sensor_readings(self):
        print("Getting sensor readings!!!")
        asyncio.sleep(60)
        

    async def evauluate_data(self):
        print("Evaluating the data!!!")
        asyncio.sleep(300)
        
        
    async def checker(self):
        print("Checking the event status!!!")
        asyncio.sleep(5)        


    async def main(self):


        readings = asyncio.ensure_future(self.get_sensor_readings())
        analysis = asyncio.ensure_future(self.evauluate_data())
        checker = asyncio.ensure_future(self.check_time())

        await readings # run every 60 seconds
        await analysis # run every 300 seconds     
        await checker  # run every 5 seconds
                
                



async def main():
    agent= Agent()
    await agent.main()



@Core.receiver("onstart")
def onstart(self, sender) -> None:
    
    asyncio.run(main())



@Core.receiver("onstop")
def onstop(self, sender, **kwargs):

    # cancel asyncio tasks
    readings.cancel()
    analysis.cancel()
    checker.cancel()

Any best practices greatly appreciated!!!!


Solution

  • Sure you can do more than one asynchronous thing at a time. Currently the core api has a spawn function that creates a greenlet for you. As for different schedules you can create multiple periodics and/or if you need to you can use a scheduler to do this.

    self._platform_scan_event = self.core.schedule(
        next_platform_scan, self._scan_platform_connect_disconnect)
    

    https://github.com/VOLTTRON/volttron/blob/main/services/core/VolttronCentral/volttroncentral/agent.py#L300

    the to perform the next call of _scan_plaltform_connect_disconnect is calculated and then rescheduled.

    I have not uses the asyncio module in any timing functions so I cannot comment on whether this is good or not.