AgentM.py 8.97 KB
#!/usr/bin/env python3

import sys
##import utils.Logger as L
#import threading #, multiprocessing, os
#import time

#from django.db import transaction
#from common.models import Command

sys.path.append("..")
sys.path.append("../../../..")
from src.core.pyros_django.agent.Agent import Agent, build_agent, log

# PM 20190416 recycle code
from src.core.pyros_django.devices.PLC import PLCController
from src.core.pyros_django.monitoring.plc_checker import PlcChecker
from common.models import *
from config.pyros.config_pyros import ConfigPyros



##log = L.setupLogger("AgentXTaskLogger", "AgentX")



class AgentM(Agent):


    # FOR TEST ONLY
    # Run this agent in simulator mode
    TEST_MODE = False
    # Run the assertion tests at the end
    TEST_WITH_FINAL_TEST = True
    TEST_MAX_DURATION_SEC = None
    #TEST_MAX_DURATION_SEC = 120

    # PM 20190416 fucking config path starting: /home/patrick/Dev/PYROS/start_agent.py agentM
    ##_path_data = 'config'
    _path_data = 'config/old_config'
    # PM 20190416 recycle code
    plcController = PLCController()
    print ("AGENT ENV: config PLC is (ip={}, port={})".format(plcController.ip, plcController.port))
    plc_checker = PlcChecker()

    log.debug("PLC instanciated")
    time_history_minutes = 4


    '''
    # Who should I send commands to ?
    #TEST_COMMANDS_DEST = "myself"
    TEST_COMMANDS_DEST = "AgentA"
    # Scenario to be executed
    TEST_COMMANDS_LIST = [
        "go_active",
        "go_idle",
        "go_active",
        "go_idle",
        "go_active",
        "go_idle",
        "exit",
    ]
    '''

    """
    =================================================================
        FUNCTIONS RUN INSIDE MAIN THREAD
    =================================================================
    """
    # old config
    # @override
    #def __init__(self, name:str=None, config_filename=None, RUN_IN_THREAD=True):
    # def __init__(self, config_filename=None, RUN_IN_THREAD=True):
    #     ##if name is None: name = self.__class__.__name__
    #     super().__init__(config_filename, RUN_IN_THREAD)

    # new config (obsconfig)
    def __init__(self, name:str=None):
        if name is None:
            name = self.__class__.__name__
        super().__init__()
        PYROS_CONFIG_FILE = os.environ.get("pyros_config_file")
        if PYROS_CONFIG_FILE:
            CONFIG_PYROS =  ConfigPyros(PYROS_CONFIG_FILE).pyros_config
            self.time_history_minutes = int(CONFIG_PYROS.get("ENV").get("time_history"))
            log.info(f"time_history_minutes set to {int(self.time_history_minutes)}")
    # @override
    def _init(self):
        super()._init()
       
        log.debug("end init()")
        # --- Set the mode according the startmode value
        ##agent_alias = self.__class__.__name__
        ##self.set_mode_from_config(agent_alias)

    '''
    # @override
    def load_config(self):
        super().load_config()
    '''

    '''
    # @override
    def update_survey(self):
        super().update_survey()
    '''

    '''
    # @override
    def get_next_command(self):
        return super().get_next_command()
    '''

    # @override
    def do_log(self):
        super().do_log()



    """
    =================================================================
        FUNCTIONS RUN INSIDE A SUB-THREAD (OR A PROCESS) (thread_*())
    =================================================================
    """

    # Define your own command step(s) here
    def cmd_step1(self, step:int):
        cmd = self._current_specific_cmd
        cmd.result = f"in step #{step}/{self._thread_total_steps_number}"
        cmd.save()
        """
        if self.RUN_IN_THREAD:
            print("(save from thread)")
            cmd.save()
        else:
            #@transaction.atomic
            print("(save from process)")
            with transaction.atomic():
                cmd.save()
                #Command.objects.select_for_update()
        """

    def cmd_step2(self, step:int):
        self.cmd_step1(step)
    def cmd_step3(self, step:int):
        self.cmd_step1(step)
    def cmd_step4(self, step:int):
        self.cmd_step1(step)

    """
    # @override
    def thread_exec_specific_cmd_step(self, step:int, sleep_time:float=1.0):
        self.thread_stop_if_asked()
        cmd = self._current_specific_cmd
        print(f">>>>> Thread (cmd {cmd.name}): step #{step}/5")
        self.sleep(sleep_time)
    """

    '''
    # @override
    def exec_specific_cmd_start(self, cmd:Command, from_thread=True):
        super().exec_specific_cmd_start(cmd, from_thread)
    '''

    # @override
    # previous name of function : routine_process
    # Note : in Agent.py, routine_process_body seems to be the main function of routine of the agent
    # We need to override routine_process_body and not routine_process
    def routine_process_body(self):
        log.debug("in routine_process_body()")
        print("TODO: we recycle code")
        status_plc = self.plcController.getStatus()
        if self.parseNewStatus(status_plc):
            self.saveWeather()
            #self.saveInternalMonitoring()

    def parseNewStatus(self,status_plc):
        # """ PM 20181009 parse new status for config
        # Find return string "plc_status" positin within status_plc
        if status_plc.find('PLC_STATUS') >= 0:
            self.plc_checker.chk_config(status_plc)
            return True
        return False

    def saveWeather(self):
        outside = WeatherWatch()
        inside = SiteWatch()
        datetimenow = datetime.now(timezone.utc)
        latest_entry_of_history = WeatherWatchHistory.objects.all().order_by("-datetime").first()
        if latest_entry_of_history != None:
            # Get last entry of WeatherWatchHistory as WeatherWatch
            latest_entry_of_history_as_weather = WeatherWatch.objects.get(id=latest_entry_of_history.weather.id)
        outside_attributes_values = {}
        for sensor in self.plc_checker.monitoring_names.keys():
            if sensor in self.plc_checker.inside_sensors:
                value = self.plc_checker.get_sensor(sensor)
                inside.setAttribute(sensor,value)
            else:
                value = self.plc_checker.get_sensor(sensor)
                outside.setAttribute(sensor, value)
                outside_attributes_values[sensor] = value
        outside.setGlobalStatus()
        outside.save()
        #inside.save()
        # We don't have an history for weatherwatch
        if latest_entry_of_history == None:
            weather_history = WeatherWatchHistory()
            weather_history.weather = outside
            for sensor in outside_attributes_values.keys():
                weather_history.setAttribute(sensor,outside_attributes_values.get(sensor))
            # save also sensors 
            weather_history.save()
        else:
            time_between_history_and_latest_entry = datetimenow - latest_entry_of_history_as_weather.updated  
            sec_diff = time_between_history_and_latest_entry.total_seconds() / 60
            # if diff between last entry of history and current time if greather than x then we save a new entry in history
            if int(sec_diff) > self.time_history_minutes:
                weather_history = WeatherWatchHistory()
                weather_history.weather = outside
                for sensor in outside_attributes_values.keys():
                    weather_history.setAttribute(sensor,outside_attributes_values.get(sensor))
                weather_history.save()
        log.debug("saved weather")

    def isInsideMonitoring(self,key):
        print(key)
        if key in ("Power_input","Roof_state"):
            return True
        else:
            return False
    # @override
    def thread_exec_specific_cmd_main(self):
        # This is optional
        self.thread_set_total_steps_number(5)

        # HERE, write your own scenario

        # scenario OK
        self.thread_exec_specific_cmd_step(1, self.cmd_step1, 1)
        self.thread_exec_specific_cmd_step(2, self.cmd_step2, 3)
        self.thread_exec_specific_cmd_step(3, self.cmd_step1, 5)
        self.thread_exec_specific_cmd_step(4, self.cmd_step3, 10)
        self.thread_exec_specific_cmd_step(5, self.cmd_step1, 4)
        # ... as many as you need

        """ autre scenario
        self.thread_exec_specific_cmd_step(1, self.cmd_step1, 1)
        self.thread_exec_specific_cmd_step(2, self.cmd_step2, 2)
        self.thread_exec_specific_cmd_step(3, self.cmd_step1, 2)
        self.thread_exec_specific_cmd_step(4, self.cmd_step3, 2)
        self.thread_exec_specific_cmd_step(5, self.cmd_step1, 3)
        """

    '''
    # @override
    def exec_specific_cmd_end(self, cmd:Command, from_thread=True):
        super().exec_specific_cmd_end(cmd, from_thread)
    '''


if __name__ == "__main__":

    # with thread
    RUN_IN_THREAD=True
    # with process
    #RUN_IN_THREAD=False

    agent = build_agent(AgentM)
    '''
    TEST_MODE, configfile = extract_parameters()
    agent = AgentM("AgentM", configfile, RUN_IN_THREAD)
    agent.setSimulatorMode(TEST_MODE)
    '''
    print(agent)
    agent.run()