AgentDevice.py 10.4 KB
#!/usr/bin/env python3


import sys
##import utils.Logger as L
import threading
import time

##from .Agent import Agent
sys.path.append("..")
from agent.Agent import Agent, build_agent
from common.models import AgentDeviceStatus, Command, get_or_create_unique_row_from_model


sys.path.append("../../..")
from device_controller.abstract_component.device_controller import DeviceController, DCCNotFoundException, NotImplementedGenericCmdException

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



class AgentDevice(Agent):

    # Default host and port of the device controller
    _device_ctrl = None
    _device_sim = None
    HOST, PORT = None, None
    _thread_device_simulator = None

    _agent_device_status = None

    # FOR TEST ONLY
    # Run this agent in simulator mode
    TEST_MODE = False
    # Run the assertion tests at the end
    TEST_WITH_FINAL_TEST = False
    #TEST_MAX_DURATION_SEC = None
    TEST_MAX_DURATION_SEC = 100
    # Who should I send commands to ?
    #TEST_COMMANDS_DEST = "myself"
    TEST_COMMANDS_DEST = "AgentB"
    # Scenario to be executed
    TEST_COMMANDS_LIST = [
        # Ask receiver to delete all its previous commands 
        "flush_commands",

        "go_active",

        # Because of this command, the receiver agent :
        # - will no more send any new command
        # - will only execute "generic" commands (and not the "specific" ones)
        "go_idle",

        # Not executed (skipped) because receiver agent is now "idle"
        #"specific0",

        # Because of this command, the receiver agent
        # will now be able to send new commands
        "go_active",

        # Executed because recipient agent is now "active"
        "specific1",
        # should abort previous command (specific1)
        "abort",

        # Executed completely because no abort
        "specific2",

        # fully executed, result is 7
        "eval 4+3",

        "go_idle",
        "exit",
    ]



    """
    =================================================================
        FUNCTIONS RUN INSIDE MAIN THREAD
    =================================================================
    """

    # @override
    #def __init__(self, name:str=None, config_filename=None, RUN_IN_THREAD=True, device_controller, host, port):
    #def __init__(self, name:str, config_filename, RUN_IN_THREAD, device_controller, host, port, device_simulator):
    ##def __init__(self, config_filename, RUN_IN_THREAD, device_controller:DeviceController, host, port, device_simulator):
    def __init__(self, config_filename, RUN_IN_THREAD, device_controller:DeviceController, host, port):
        ##if name is None: name = self.__class__.__name__
        #super().__init__(name, config_filename, RUN_IN_THREAD)
        super().__init__(config_filename, RUN_IN_THREAD)
        self.HOST, self.PORT = host, port
        self._device_ctrl = device_controller
        ##self._device_sim = device_simulator

        # Initialize the device table status
        # If table is empty, create a default 1st row
        ##self._agent_device_status = get_or_create_unique_row_from_model(AgentDeviceStatus)
        self._agent_device_status = AgentDeviceStatus.getStatusForAgent(self.name)
        """
        if not AgentDeviceTelescopeStatus.objects.exists():
            print("CREATE first row")
            self._agent_device_status = AgentDeviceTelescopeStatus.objects.create(id=1)
        # Get 1st row (will be updated at each iteration by routine_process() with current device status)
        print("GET first row")
        self._agent_device_status = AgentDeviceTelescopeStatus.objects.get(id=1)
        """

        # Initialize the device socket
        # Port local AK 8085 = redirigé sur l’IP du tele 192.168.0.12 sur port 11110
        ##HOST, PORT = "82.64.28.71", 11110
        #HOST, PORT = "localhost", 11110
        #self._device_ctrl = TelescopeControllerGEMINI(host, port, True)
        ##self._device_ctrl = device_controller(HOST, PORT, True)
        ##self._device_ctrl = device_controller(host, port, True)
        self._log.print("init done")


    # @override
    def init(self):

        super().init()
        # --- Set the mode according the startmode value
        ##agent_alias = self.__class__.__name__
        ##self.set_mode_from_config(agent_alias)

        # If in test and simulator mode ==> set my DC as connected to localhost (simulator server)
        if self.is_in_test_mode() and self.WITH_SIMULATOR:
            # START device SIMULATOR (in a thread) so that we can connect to it in place of the real device
            self.HOST = "localhost"
            '''
            self._thread_device_simulator = threading.Thread(target=self.device_simulator_run)
            self._thread_device_simulator.start()
            '''

        # Create instance of device controller (device client)
        # Ex: this can be the Gemini or the SBIG (...) DC
        self._device_ctrl = self._device_ctrl(self.HOST, self.PORT, True)

        # Device socket init
        # (optional) Only useful for TCP (does nothing for UDP)
        self._device_ctrl._connect_to_device()
        self._device_ctrl.print_available_commands()

        # Telescope (long) init
        # TODO:


    def is_using_simulator(self):
        return self.WITH_SIMULATOR

    '''
    def device_simulator_run(self):
        #HOST, PORT = "localhost", 11110
        #with get_SocketServer_UDP_TCP(HOST, PORT, "UDP") as myserver:
        print("Starting device simulator on (host, port): ", self.HOST, self.PORT)
        self._device_sim.serve_forever(self.PORT)
        #with get_SocketServer_UDP_TCP(self.HOST, self.PORT, "UDP") as myserver: myserver.serve_forever()
        #''
        myserver = get_SocketServer_UDP_TCP(self.HOST, self.PORT, "UDP")
        myserver.serve_forever()
        #''
    '''

    '''
    # @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
    def routine_process(self):
        self.print("ROUTINE PROCESS START: reading my dedicated device status information and storing it in DB)...")

        # Save current device status to DB
        #AgentDeviceTelescopeStatus.objects.create(radec=myradec)
        #if not self.is_running_specific_cmd():
        self._save_device_status()

        self.print("Status saved in DB")

        #time.sleep(3)
        self.print("ROUTINE PROCESS END")


    def _save_device_status(self):
        self._agent_device_status.status = self.get_device_status()
        self._agent_device_status.save()


    # To be overriden by subclass
    def get_device_status(self):
        return 'Abstract status'



    # @override
    def kill_running_specific_cmd_if_exists(self, abort_sender):
        super().kill_running_specific_cmd_if_exists(abort_sender)
        print("Close device socket")
        self._device_ctrl.close()
        '''
        # Stop device simulator (only if used)
        if self.is_using_simulator():
            print("Stopping device simulator")
            self._device_sim.stop()
        '''


    """
    # @override
    def specific_process(self, cmd):
        cmd.set_read_time()
        cmd.set_as_running()
        res = self._device_ctrl.execute_cmd(cmd.name)
        cmd.set_result(str(res))
        print("result is", str(res))
        if res.ok: print("OK")
        cmd.set_as_processed()
        time.sleep(1)
    """

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


    # This method is also called DIRECTLY from routine_process().get_device_status() (NOT FROM A THREAD)
    # @override parent class (Agent)
    def exec_specific_cmd(self, cmd:Command):
        #cmd = self._current_specific_cmd
        self.printd("*** SPECIFIC cmd name is", cmd.name)
        self.printd("*** PASS IT TO DEVICE TYPE", cmd.device_type)
        try:
            res = self._device_ctrl.execute_cmd(cmd.full_name)
        except (DCCNotFoundException, NotImplementedGenericCmdException) as e:
            print(f"EXCEPTION caught by {type(self).__name__} (from AD)", e)
            raise
        print("result is", str(res))
        if res.ok: self.printd("OK")
        time.sleep(1)
        return res

    # @override superclass (Agent) method
    def OLD_cmd_step_OLD(self, step:int):
        cmd = self._current_specific_cmd
        print("cmd name is", cmd.name)
        #res = self._device_ctrl.execute_cmd(cmd.name_and_args)
        res = self._device_ctrl.execute_cmd(cmd.full_name)
        ##cmd.set_result(str(res))
        print("result is", str(res))
        if res.ok: print("OK")
        #cmd.set_as_processed()
        time.sleep(1)
        #cmd.set_result(f"in step #{step}/{self._thread_total_steps_number}")
        cmd.set_result(res)

    # @override
    def OLD_thread_exec_specific_cmd_main_OLD(self):
        # This is optional
        self.thread_set_total_steps_number(1)
        # HERE, write your own scenario
        self.thread_exec_specific_cmd_step(1, self.cmd_step, 1)
        # ... as many as you need
        """ other 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)
    '''




"""
=================================================================
    MAIN FUNCTION
=================================================================
"""
if __name__ == "__main__":
    # with thread
    RUN_IN_THREAD=True
    # with process
    #RUN_IN_THREAD=False

    agent = build_agent(AgentDevice, RUN_IN_THREAD=RUN_IN_THREAD)
    '''
    TEST_MODE, WITH_SIM, configfile = extract_parameters()
    #agent = AgentX()
    agent = AgentDevice("AgentDevice", configfile, RUN_IN_THREAD)
    #agent.setSimulatorMode(TEST_MODE)
    agent.setTestMode(TEST_MODE)
    agent.setWithSimulator(WITH_SIM)
    print(agent)
    '''
    agent.run()