From 128e1a71f8c3d25e70c7bc0a9a795bd772f0caa0 Mon Sep 17 00:00:00 2001 From: Etienne Pallier Date: Fri, 26 Apr 2019 08:55:14 +0200 Subject: [PATCH] AgentDevice + AgentDeviceTelescopeStatus + AgentTelescopeRequester --- README.md | 17 +++++++++++++---- pyros.py | 1 + src/agent/Agent.py | 8 ++++---- src/agent/AgentDevice.py | 17 +++++++++++++++++ src/agent/AgentTelescopeRequester.py | 245 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/common/models.py | 14 ++++++++++++++ 6 files changed, 294 insertions(+), 8 deletions(-) create mode 100755 src/agent/AgentTelescopeRequester.py diff --git a/README.md b/README.md index 6caafd0..cfe3d29 100644 --- a/README.md +++ b/README.md @@ -67,14 +67,22 @@ This software has been tested and validated with the following configurations : -------------------------------------------------------------------------------------------- ## LAST VERSION -Date: 17/04/2019 +Date: 26/04/2019 Author: E. Pallier -VERSION: 0.20.38 +VERSION: 0.20.40 -Comment: - Génération automatique des diagrammes PlantUML avec "pyros update" +Comment: AgentDevice + AgentDeviceTelescopeStatus + AgentTelescopeRequester + + - Dans le détail : + - AgentDevice met à jour la table AgentDeviceTelescopeStatus + - AgentTelescopeRequester interroge AgentDevice + - Quelques bugfixes + - Mode opératoire: + ./pyros start agentDevice + ou, en mode simu: + ./pyros -t start agentTelescopeRequester,agentDevice - Scenario de test : - lancer agents A et B en mode simu (option -t): ./pyros.py -t start agentA,agentB @@ -89,6 +97,7 @@ Comment: - pour utiliser thread ou processus : il suffit de mettre la constante RUN_IN_THREAD de AgentA (ou AgentB ou AgentX) à False ou True - Historique des nouveautés implémentées : + - Génération automatique des diagrammes PlantUML avec "pyros update" - Implémentation complète du "Command state diag" - Nouveaux diagrammes UML pour Command (state diag) et Agent (activity diag) - Déplacé AgentM dans src/monitoring/ (pyros start agentM) diff --git a/pyros.py b/pyros.py index c8eafdf..c172f81 100755 --- a/pyros.py +++ b/pyros.py @@ -31,6 +31,7 @@ AGENTS = { "agentB" : "AgentB", "agentM" : "AgentM", "agentDevice" : "AgentDevice", + "agentTelescopeRequester" : "AgentTelescopeRequester", "webserver" : "webserver", "monitoring" : "monitoring", "majordome" : "majordome", diff --git a/src/agent/Agent.py b/src/agent/Agent.py index c0e7312..11ed9f8 100755 --- a/src/agent/Agent.py +++ b/src/agent/Agent.py @@ -490,8 +490,8 @@ class Agent: if not self.DO_MAIN_LOOP: break except KeyboardInterrupt: # In case of CTRL-C, kill the current thread (process) before dying (in error) - self.print("CTRL-C Interrupted, I kill the current thread (process) before exiting") - self.kill_running_specific_cmd_if_exists() + self.print("CTRL-C Interrupted, I kill the current thread (process) before exiting (if exists)") + self.kill_running_specific_cmd_if_exists("USER_CTRLC") exit(1) #if self.DO_EXIT: exit(0) @@ -1025,9 +1025,9 @@ class Agent: def kill_running_specific_cmd_if_exists(self, abort_sender): if (self._current_specific_thread is None) or not self._current_specific_thread.is_alive(): - self.printd("...No current specific command thread to abort...") + self.print("...No current specific command thread to abort...") else: - self.printd(f"Killing command {self._current_specific_cmd.name}") + self.print(f"Killing command {self._current_specific_cmd.name}") # Ask the thread to stop itself #self._current_specific_thread.stop() #self._current_specific_thread._stop() diff --git a/src/agent/AgentDevice.py b/src/agent/AgentDevice.py index 9cc8340..8cdef4a 100755 --- a/src/agent/AgentDevice.py +++ b/src/agent/AgentDevice.py @@ -8,6 +8,7 @@ import time ##from .Agent import Agent sys.path.append("..") from agent.Agent import Agent, extract_parameters +from common.models import AgentDeviceTelescopeStatus from devices_channel.client.telescope_controller_gemini import TelescopeControllerGEMINI ##log = L.setupLogger("AgentXTaskLogger", "AgentX") @@ -78,16 +79,24 @@ class AgentDevice(Agent): #HOST, PORT = "localhost", 11110 self.tele_ctrl = TelescopeControllerGEMINI(HOST, PORT, True) + # @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) + + # Device socket init # (optional) Only useful for TCP (does nothing for UDP) self.tele_ctrl._connect_to_device() self.tele_ctrl.print_available_commands() + # Telescope (long) init + # TODO: + + ''' # @override def load_config(self): @@ -129,11 +138,19 @@ class AgentDevice(Agent): cmd="get radec" res = self.tele_ctrl.execute_cmd(cmd) print("result is", str(res)) + myradec = str(res) if res.ok: print("OK") + AgentDeviceTelescopeStatus.objects.create(radec=myradec) + time.sleep(3) self.print("ROUTINE PROCESS END") + # @override + def kill_running_specific_cmd_if_exists(self, abort_sender): + super().kill_running_specific_cmd_if_exists(abort_sender) + print("Close telescope socket") + self.tele_ctrl.close() diff --git a/src/agent/AgentTelescopeRequester.py b/src/agent/AgentTelescopeRequester.py new file mode 100755 index 0000000..950079c --- /dev/null +++ b/src/agent/AgentTelescopeRequester.py @@ -0,0 +1,245 @@ +#!/usr/bin/env python3 + + +import sys +##import utils.Logger as L + +##from .Agent import Agent +sys.path.append("..") +from agent.Agent import Agent, extract_parameters + + +##log = L.setupLogger("AgentXTaskLogger", "AgentX") + + + +class AgentTelescopeRequester(Agent): + + # FOR TEST ONLY + # Run this agent in simulator mode + SIMULATOR_MODE = False + # Run the assertion tests at the end + SIMULATOR_WITH_TEST = True + #SIMULATOR_MAX_DURATION_SEC = None + SIMULATOR_MAX_DURATION_SEC = 100 + # Who should I send commands to ? + #SIMULATOR_COMMANDS_DEST = "myself" + SIMULATOR_COMMANDS_DEST = "AgentDevice" + # Scenario to be executed + SIMULATOR_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): + if name is None: name = self.__class__.__name__ + super().__init__(name, config_filename, RUN_IN_THREAD) + self._log.print(f"init done for {name}") + + # @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) + + ''' + # @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 + 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) + ''' + + # @override + def simulator_test_results_main(self, commands): + nb_asserted = 0 + for cmd in commands: + if cmd.name == "flush_commands": + assert cmd.is_executed() + nb_asserted+=1 + # 2 times + if cmd.name == "go_active": + assert cmd.is_executed() + nb_asserted+=1 + # 2 times + if cmd.name == "go_idle": + assert cmd.is_executed() + nb_asserted+=1 + """ + if cmd.name == "specific0": + assert cmd.is_skipped() + assert cmd.result == "in step #5/5" + nb_asserted+=1 + """ + if cmd.name == "specific1": + assert cmd.is_killed() + nb_asserted+=1 + if cmd.name == "specific2": + assert cmd.is_executed() + assert cmd.result == "in step #5/5" + nb_asserted+=1 + if cmd.name == "eval 4+3": + assert cmd.is_executed() + assert cmd.get_result() == "7" + nb_asserted+=1 + if cmd.name in ("abort"): + assert cmd.is_executed() + nb_asserted+=1 + if cmd.name in ("exit"): + assert cmd.is_executed() + nb_asserted+=1 + return nb_asserted + + +""" +================================================================= + MAIN FUNCTION +================================================================= +""" +if __name__ == "__main__": + # with thread + RUN_IN_THREAD=True + # with process + #RUN_IN_THREAD=False + + TEST_MODE, configfile = extract_parameters() + """ + configfile = None + # arg 1 : config file + if len(sys.argv) == 2: + configfile = sys.argv[1] + """ + #agent = AgentX() + agent = AgentTelescopeRequester("AgentTelescopeRequester", configfile, RUN_IN_THREAD) + agent.setSimulatorMode(TEST_MODE) + print(agent) + agent.run() diff --git a/src/common/models.py b/src/common/models.py index 37ef1a9..8cf01f2 100644 --- a/src/common/models.py +++ b/src/common/models.py @@ -213,6 +213,20 @@ class Request(models.Model): ------------------------ """ +class AgentDeviceTelescopeStatus(models.Model): + created = models.DateTimeField('status date', blank=True, null=True, auto_now_add=True) + radec = models.CharField('agent mode', max_length=30, blank=True) + + class Meta: + managed = True + db_table = 'agent_device_telescope_status' + #verbose_name = "agent survey" + #verbose_name_plural = "agents survey" + + """ + def __str__(self): + return (f"Agent {self.name} at {self.updated} in mode {self.mode} and status {self.status}") + """ class AgentSurvey(models.Model): """ -- libgit2 0.21.2