AgentM.py 12 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

import os
pwd = os.environ['PROJECT_ROOT_PATH']
if pwd not in sys.path:
    sys.path.append(pwd)

from src.core.pyros_django.majordome.agent.Agent import Agent, build_agent, log, parse_args

# PM 20190416 recycle code
from src.core.pyros_django.devices.PLC import PLCController
from src.core.pyros_django.env_monitor.plc_checker import PlcChecker
from common.models import *
from env_monitor.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

    ##_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, simulated_computer=None):
        if name is None:
            name = self.__class__.__name__
        super().__init__(simulated_computer=simulated_computer)
        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)}")

    # Read device config to get all monitoring_names and their info (qualities)
    # make function to read it
    # @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()



    # @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_iter_end_body(self):
        log.info("in routine process iter end body")
        log.debug("in routine_process_body()")
        #status_plc = self.plcController.getStatus()
        print("Save weather")
        self.saveWeather()

    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):
        print("saving weather")
        datetimenow = datetime.now(timezone.utc)
        # get sensor config to read quality and take the device with the best quality value
        monitoring_devices = self.get_config().get_monitoring_devices()
        devices = self.get_config().get_devices()
        sensors_data_with_qualities = {}
        #for sensor_data in Sensors_data_last_value.objects.all():

        for device in monitoring_devices:
                
            output_data = self.get_config().get_output_data_device(device)
            for sensor in output_data.keys():
                for key in output_data[sensor]:
                    try:
                        sensor_data = Sensors_data_last_value.objects.get(key=key)
                    except Sensors_data_last_value.DoesNotExist:
                        # No value found
                        pass
                    quality = output_data[sensor][key].get("quality")
                    monitoring_name = output_data[sensor][key].get("monitoring_name")
                    label = output_data[sensor][key].get("label")
                    if sensors_data_with_qualities.get(monitoring_name) is None:
                        sensors_data_with_qualities[monitoring_name] = {
                            key:{
                                "quality":float(quality),
                                "label":label
                            }
                        }
                    else:
                        sensors_data_with_qualities[monitoring_name][key] = {
                            "quality":float(quality),
                            "label":label
                        }
        for monitoring_name in sensors_data_with_qualities.keys():
            max_quality = 0
            associated_key = ""
            for sensor_data in sensors_data_with_qualities[monitoring_name].keys():
                # make sorted list by quality & device valid -> device_valid = error_code
                key = sensor_data
                quality = sensors_data_with_qualities[monitoring_name][sensor_data]["quality"]
                # Also use error_code 
                if quality >= max_quality:
                    if Sensors_data_last_value.objects.filter(key=key).exists():
                        max_quality = quality
                        associated_key = key
                        label = sensors_data_with_qualities[monitoring_name][sensor_data]["label"]
            # Data doesn't exist in database ?
            if associated_key != "":
                
                do_update = True
                try:
                    env_data = Env_data.objects.get(monitoring_name=monitoring_name)
                except Env_data.DoesNotExist:
                    # it's a creation
                    do_update = False
                    env_data = Env_data.objects.create(
                        monitoring_name=monitoring_name,
                        value = Sensors_data_last_value.objects.get(key=associated_key).value,
                        label=label
                    )
                if do_update:
                # it's an update    
                    env_data.value = Sensors_data_last_value.objects.get(key=associated_key).value
                    env_data.monitoring_name = monitoring_name 
                    env_data.label = label
                    env_data.save()
        self.setGlobalStatus()
        
        latest_entry_of_history = Env_data_hist.objects.all().order_by("-created").first()
        if latest_entry_of_history == None:
            for env_data in Env_data.objects.all():
                env_data_hist = Env_data_hist.objects.create(
                    monitoring_name=env_data.monitoring_name,
                    value=env_data.value,
                    label=env_data.label
                )
        else:
            time_between_history_and_latest_entry = datetimenow - latest_entry_of_history.created  
            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:
                for env_data in Env_data.objects.all():
                    env_data_hist = Env_data_hist.objects.create(
                        monitoring_name=env_data.monitoring_name,
                        value=env_data.value,
                        label=env_data.label
                    )
        log.info("End of agentM saving weather")

    def setGlobalStatus(self):
        global_status = ""
        # Old monitoring
        # if self.doors and self.doors.find("open") != -1:
        #     self.global_status += "DOOR_OPEN "
        # if self.lights and self.lights == "on":
        #     self.global_status += "LIGHTS_ON "
        # if self.humidity and float(self.humidity) > 80:
        #     self.global_status += "HUMIDITY_TOO_HIGH "
        # New monitoring
        if Env_data.objects.filter(monitoring_name="Temp_ext").exists():
            if float(Env_data.objects.get(monitoring_name="Temp_ext").value) > 40:
                global_status += "TOO_HOT "
        if Env_data.objects.filter(monitoring_name="Rain").exists():
            if float(Env_data.objects.get(monitoring_name="Rain").value) > 0:
                global_status += "RAINING "
        if Env_data.objects.filter(monitoring_name="Wind_speed").exists():
            if float(Env_data.objects.get(monitoring_name="Wind_speed").value) > 5:
                global_status += "WINDY "
        if Env_data.objects.filter(monitoring_name="Humidity_ext").exists():
            if float(Env_data.objects.get(monitoring_name="Humidity_ext").value) > 80:
                global_status += "HUMIDITY_TOO_HIGH "
        if global_status == "":
           global_status = "OK"
        do_update = True
        try:
            global_status_db = Env_data.objects.get(monitoring_name="global_status")
        except Env_data.DoesNotExist:
            do_update = False
            global_status_db = Env_data.objects.create(monitoring_name="global_status",value=global_status,label="Global status")
        if do_update:
            global_status_db.monitoring_name = "global_status"
            global_status_db.label = "Global status"
            global_status_db.value = global_status
            global_status_db.save()

    def isInsideMonitoring(self,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
    args =  parse_args(sys.argv[1:])    
    agent = build_agent(AgentM,param_constr=args)
    '''
    TEST_MODE, configfile = extract_parameters()
    agent = AgentM("AgentM", configfile, RUN_IN_THREAD)
    agent.setSimulatorMode(TEST_MODE)
    '''
    print(agent)
    agent.run()