Commit 7138789eea6e2bb74e45d3ad0766df6b754509e4
1 parent
3d9bc073
Exists in
dev
new Agent with 3 states and better exceptions...
Showing
4 changed files
with
537 additions
and
136 deletions
Show diff stats
src/core/pyros_django/agent/Agent.py
1 | 1 | #!/usr/bin/env python3 |
2 | 2 | |
3 | -VERSION = "0.5" | |
3 | +VERSION = "1.0" | |
4 | 4 | |
5 | 5 | #DEBUG=True |
6 | 6 | #DEBUG=False |
... | ... | @@ -227,12 +227,46 @@ class StoppableThreadEvenWhenSleeping(threading.Thread): |
227 | 227 | ''' |
228 | 228 | |
229 | 229 | |
230 | +### | |
231 | +# ================================================================= | |
232 | +# EXCEPTIONS classes | |
233 | +# ================================================================= | |
234 | +### | |
230 | 235 | |
231 | -""" | |
232 | -================================================================= | |
233 | - class Agent | |
234 | -================================================================= | |
235 | -""" | |
236 | +class UnknownCmdException(Exception): | |
237 | + ''' Raised when an Agent (specific) cmd is NOT known by the agent ''' | |
238 | + # pass | |
239 | + def __init__(self, cmd_name): | |
240 | + self.cmd_name = cmd_name | |
241 | + def __str__(self): | |
242 | + return f"The Agent command '{self.cmd_name}' is unknown by the agent" | |
243 | + #return f"({type(self).__name__}): Device Generic command has no implementation in the controller" | |
244 | + | |
245 | +class AgentCmdUnimplementedException(Exception): | |
246 | + ''' Raised when an Agent Specific cmd is known by the agent but not implemented ''' | |
247 | + # pass | |
248 | + def __init__(self, cmd_name): | |
249 | + self.cmd_name = cmd_name | |
250 | + def __str__(self): | |
251 | + return f"The Agent (General or Specific) command '{self.cmd_name}' is known by the agent but not implemented" | |
252 | + #return f"({type(self).__name__}): Device Generic command has no implementation in the controller" | |
253 | + | |
254 | +class AgentCmdBadArgsException(Exception): | |
255 | + ''' Raised when an Agent cmd has bad or missing argument(s) ''' | |
256 | + # pass | |
257 | + def __init__(self, cmd_name): | |
258 | + self.cmd_name = cmd_name | |
259 | + def __str__(self): | |
260 | + return f"The Agent command '{self.cmd_name}' has bad or missing argument(s)" | |
261 | + #return f"({type(self).__name__}): Device Generic command has no implementation in the controller" | |
262 | + | |
263 | + | |
264 | + | |
265 | +### | |
266 | +# ================================================================= | |
267 | +# class Agent | |
268 | +# ================================================================= | |
269 | +### | |
236 | 270 | |
237 | 271 | class Agent: |
238 | 272 | """ |
... | ... | @@ -258,7 +292,7 @@ class Agent: |
258 | 292 | DEBUG_MODE = False |
259 | 293 | WITH_SIMULATOR = False |
260 | 294 | TEST_MODE = False |
261 | - | |
295 | + | |
262 | 296 | # Default LOG level is INFO |
263 | 297 | #PYROS_DEFAULT_GLOBAL_LOG_LEVEL = LogPyros.LOG_LEVEL_INFO # INFO |
264 | 298 | |
... | ... | @@ -285,9 +319,11 @@ class Agent: |
285 | 319 | # Default scenario to be executed |
286 | 320 | #TEST_COMMANDS = iter([ |
287 | 321 | TEST_COMMANDS_LIST = [ |
288 | - "set_state:active", | |
289 | - "set_state:idle", | |
322 | + "Agent set_state ATTENTIVE", | |
323 | + "Agent set_state ROUTINE", | |
324 | + "Agent set_state IDLE", | |
290 | 325 | |
326 | + ''' | |
291 | 327 | # specific0 not_executed_because_idle |
292 | 328 | "specific0", |
293 | 329 | |
... | ... | @@ -333,6 +369,7 @@ class Agent: |
333 | 369 | # and not even be added to the database command table |
334 | 370 | "set_state:active", |
335 | 371 | "specific10" |
372 | + ''' | |
336 | 373 | ] |
337 | 374 | #TEST_COMMANDS = iter(TEST_COMMANDS_LIST) |
338 | 375 | |
... | ... | @@ -367,9 +404,22 @@ class Agent: |
367 | 404 | ###STATUS_SPECIFIC_PROCESS = "IN_SPECIFIC_PROCESS" |
368 | 405 | STATUS_EXIT = "EXITING" |
369 | 406 | |
370 | - # Modes | |
371 | - MODE_ACTIVE = "ACTIVE" | |
407 | + | |
408 | + ''' | |
409 | + MODES | |
410 | + | |
411 | + In all modes, the Agent listens to commands sent to him and executes Agent level GENERAL ones. | |
412 | + | |
413 | + - MODE_IDLE : "idle" mode, does nothing, only executes Agent level GENERAL commands (DO_RESTART, DO_EXIT, DO_ABORT, DO_FLUSH...) | |
414 | + - MODE_ROUTINE : idem IDLE + executes routine process (before & after) | |
415 | + - MODE_ATTENTIVE : idem ROUTINE + executes Agent level SPECIFIC commands (commands specific to this agent, that only this agent understands and can execute) | |
416 | + | |
417 | + Default mode is MODE_ATTENTIVE (most active mode) | |
418 | + | |
419 | + ''' | |
372 | 420 | MODE_IDLE = "IDLE" |
421 | + MODE_ROUTINE = "ROUTINE" | |
422 | + MODE_ATTENTIVE = "ATTENTIVE" | |
373 | 423 | |
374 | 424 | ''' Moved to more central file : config.config_base |
375 | 425 | PYROS_DJANGO_BASE_DIR = Path("src/core/pyros_django") # pathlib |
... | ... | @@ -414,6 +464,8 @@ class Agent: |
414 | 464 | # new obsconfig init for agent: |
415 | 465 | ##def __init__(self, RUN_IN_THREAD=True): |
416 | 466 | def __init__(self): |
467 | + # Agent is by default in mode ATTENTIVE (most active mode) | |
468 | + self.mode = self.MODE_ATTENTIVE | |
417 | 469 | log.addHandler(handler_filebyagent(logging.INFO, self.__class__.__name__)) |
418 | 470 | log.debug("start Agent init") |
419 | 471 | obs_config_file_path = os.environ["PATH_TO_OBSCONF_FILE"] |
... | ... | @@ -440,7 +492,7 @@ class Agent: |
440 | 492 | self.TEST_COMMANDS = iter(self.TEST_COMMANDS_LIST) |
441 | 493 | ##self.RUN_IN_THREAD = RUN_IN_THREAD |
442 | 494 | self._set_status(self.STATUS_LAUNCH) |
443 | - self._set_idle() | |
495 | + ####self._set_idle() | |
444 | 496 | |
445 | 497 | # Create 1st survey if none |
446 | 498 | #tmp = AgentSurvey.objects.filter(name=self.name) |
... | ... | @@ -756,26 +808,16 @@ class Agent: |
756 | 808 | |
757 | 809 | # TEST MODE ONLY |
758 | 810 | # IF in test mode but with REAL devices (no SIMULATOR), delete all dangerous commands from the test commands list scenario: |
759 | - if self.TEST_MODE: | |
760 | - log.debug("\n!!! In TEST mode !!! => preparing to run a scenario of test commands") | |
761 | - log.debug("- Current test commands list scenario is:\n" + str(self.TEST_COMMANDS_LIST)) | |
762 | - if not self.WITH_SIMULATOR: | |
763 | - log.debug("\n!!! In TEST but no SIMULATOR mode (using REAL device) !!! => removing dangerous commands for real devices... :") | |
764 | - TEST_COMMANDS_LIST_copy = self.TEST_COMMANDS_LIST.copy() | |
765 | - for cmd in TEST_COMMANDS_LIST_copy: | |
766 | - cmd_key = cmd.split()[1] | |
767 | - if ("set_" in cmd_key) or ("do_start" in cmd_key) or cmd_key in ["do_init", "do_goto", "do_open", "do_close"]: | |
768 | - self.TEST_COMMANDS_LIST.remove(cmd) | |
769 | - log.debug("- NEW test commands list scenario is:\n" + self.TEST_COMMANDS_LIST, '\n') | |
770 | - | |
771 | - self._DO_EXIT = False | |
772 | - self._DO_RESTART = True | |
811 | + if self.TEST_MODE: self.TEST_prepare() | |
812 | + | |
813 | + #self._DO_EXIT = False | |
814 | + self.DO_RESTART_LOOP = True | |
773 | 815 | |
774 | 816 | # Will loop again only if _DO_RESTART is True |
775 | - while self._DO_RESTART: | |
776 | - self._DO_RESTART = False | |
817 | + while self.DO_RESTART_LOOP: | |
818 | + #self._DO_RESTART = False | |
777 | 819 | self.start_time = time.time() |
778 | - log.debug("on est ici: " + os.getcwd()) | |
820 | + #log.debug("on est ici: " + os.getcwd()) | |
779 | 821 | |
780 | 822 | self._load_config() |
781 | 823 | |
... | ... | @@ -794,12 +836,12 @@ class Agent: |
794 | 836 | AgentCmd.delete_commands_with_running_status_for_agent(self.name) |
795 | 837 | |
796 | 838 | self._iter_num = 1 |
797 | - self._DO_MAIN_LOOP = True | |
839 | + self.DO_MAIN_LOOP = True | |
798 | 840 | # Main loop |
799 | - while self._DO_MAIN_LOOP: | |
841 | + while self.DO_MAIN_LOOP: | |
800 | 842 | try: |
801 | 843 | self._main_loop(nb_iter,FOR_REAL) |
802 | - if not self._DO_MAIN_LOOP: break | |
844 | + #if not self.DO_MAIN_LOOP: break | |
803 | 845 | except KeyboardInterrupt: # CTRL-C |
804 | 846 | # In case of CTRL-C, kill the current thread (process) before dying (in error) |
805 | 847 | log.info("CTRL-C Interrupted, I kill the current thread (process) before exiting (if exists)") |
... | ... | @@ -814,15 +856,25 @@ class Agent: |
814 | 856 | |
815 | 857 | #def _kill_running_device_cmd_if_exists(self, abort_cmd_sender): |
816 | 858 | def do_things_before_exit(self, abort_cmd_sender): |
817 | - pass | |
859 | + #pass | |
860 | + log.info("Before exiting, Here are (if exists) the current (still) pending commands (time ordered) :") | |
861 | + commands = AgentCmd.get_pending_and_running_commands_for_agent(self.name) | |
862 | + AgentCmd.show_commands(commands, True) | |
863 | + print("TODO: flush commands !!!") | |
864 | + #if self.TEST_MODE and self.TEST_WITH_FINAL_TEST and self.TEST_COMMANDS_DEST == "myself": self.simulator_test_results() | |
865 | + if self.TEST_MODE and self.TEST_WITH_FINAL_TEST: | |
866 | + self._TEST_test_results() | |
867 | + #self._DO_EXIT=True | |
868 | + #exit(0) | |
869 | + | |
818 | 870 | |
819 | 871 | |
820 | 872 | def _main_loop(self, nb_iter:int=None, FOR_REAL:bool=True): |
821 | 873 | |
822 | 874 | self._main_loop_start(nb_iter) |
823 | - if not self._DO_MAIN_LOOP: return | |
875 | + #if not self.DO_MAIN_LOOP: return | |
824 | 876 | |
825 | - self._load_config() # only if changed | |
877 | + self._reload_config_if_changed() # only if changed | |
826 | 878 | |
827 | 879 | # better to do this in a subclass |
828 | 880 | #self.show_config() |
... | ... | @@ -833,20 +885,34 @@ class Agent: |
833 | 885 | |
834 | 886 | #self.printd("====== START COMMMANDS PROCESSING ======") |
835 | 887 | |
888 | + # SIMU | |
889 | + #self.send_cmd_to("AgentScheduler", "do_replan") | |
890 | + | |
836 | 891 | # ROUTINE process |
837 | 892 | # To be overriden to be specified by subclass |
838 | - self._routine_process_before() | |
893 | + if not self.IS_IDLE(): self._routine_process_before() | |
839 | 894 | #self.printd("I am IDLE, so I bypass the routine_process (do not send any new command)") |
840 | 895 | |
841 | 896 | # Processing the next pending command if exists |
842 | - self._command_process_if_exists() | |
897 | + # If Agent general level command like (DO_RESTART, DO_EXIT, DO_ABORT) | |
898 | + # => will set DO_MAIN_LOOP=False and/or DO_RESTART_LOOP | |
899 | + | |
900 | + print() | |
901 | + print() | |
902 | + log.info("*"*10 + " NEXT COMMAND PROCESSING (START) " + "*"*10 + '\n') | |
903 | + self._process_next_command_if_exists() | |
904 | + log.info("*"*10 + " NEXT COMMAND PROCESSING (END) " + "*"*10 + "\n") | |
905 | + | |
906 | + if not self.DO_MAIN_LOOP: return | |
843 | 907 | |
908 | + ''' | |
844 | 909 | # if restart, exit this loop to restart from beginning |
845 | - if self._DO_RESTART or self._DO_EXIT: | |
846 | - self._DO_MAIN_LOOP = False | |
910 | + if self.DO_RESTART or self.DO_EXIT or self.DO_ABORT: | |
911 | + self.DO_MAIN_LOOP = False | |
847 | 912 | return |
913 | + ''' | |
848 | 914 | |
849 | - self._routine_process_after() | |
915 | + if not self.IS_IDLE(): self._routine_process_after() | |
850 | 916 | |
851 | 917 | #self.printd("====== END COMMMANDS PROCESSING ======") |
852 | 918 | |
... | ... | @@ -858,6 +924,8 @@ class Agent: |
858 | 924 | self._iter_num += 1 |
859 | 925 | |
860 | 926 | |
927 | + | |
928 | + | |
861 | 929 | def _main_loop_start(self, nb_iter:int): |
862 | 930 | |
863 | 931 | for i in range(3): print() |
... | ... | @@ -872,7 +940,7 @@ class Agent: |
872 | 940 | # Bad number of iterations or nb iterations reached => exit |
873 | 941 | if nb_iter <= 0 or nb_iter < self._iter_num: |
874 | 942 | log.info(f"Exit because number of iterations asked ({nb_iter}) has been reached") |
875 | - self._DO_MAIN_LOOP = False | |
943 | + self.DO_MAIN_LOOP = False | |
876 | 944 | return |
877 | 945 | |
878 | 946 | # Wait a random number of sec before starting iteration |
... | ... | @@ -889,14 +957,15 @@ class Agent: |
889 | 957 | #self._kill_running_device_cmd_if_exists(self.name) |
890 | 958 | self.do_things_before_exit(self.name) |
891 | 959 | #if self.TEST_MODE and self.TEST_WITH_FINAL_TEST: self._TEST_test_results() |
892 | - self._DO_MAIN_LOOP = False | |
960 | + self.DO_MAIN_LOOP = False | |
893 | 961 | return |
894 | 962 | |
895 | 963 | self._set_status(self.STATUS_MAIN_LOOP) |
896 | 964 | self.show_mode_and_status() |
897 | 965 | |
898 | 966 | |
899 | - def process_other_level_cmd(self): | |
967 | + # To be overriden by subclass (AgentDevice) | |
968 | + def process_device_level_cmd(self): | |
900 | 969 | pass |
901 | 970 | ''' |
902 | 971 | log.info("(DEVICE LEVEL CMD)") |
... | ... | @@ -914,12 +983,12 @@ class Agent: |
914 | 983 | |
915 | 984 | |
916 | 985 | |
917 | - def _command_process_if_exists(self): | |
986 | + def _process_next_command_if_exists(self): | |
918 | 987 | ''' Processing the next pending command if exists ''' |
919 | 988 | |
920 | - print() | |
921 | - print() | |
922 | - log.info("*"*10 + " NEXT COMMAND PROCESSING (START) " + "*"*10 + '\n') | |
989 | + #print() | |
990 | + #print() | |
991 | + #log.info("*"*10 + " NEXT COMMAND PROCESSING (START) " + "*"*10 + '\n') | |
923 | 992 | |
924 | 993 | |
925 | 994 | # Purge commands (every N iterations, delete old commands) |
... | ... | @@ -933,49 +1002,101 @@ class Agent: |
933 | 1002 | cmd = self._get_next_valid_and_not_running_command() |
934 | 1003 | #self._set_status(self.STATUS_GENERAL_PROCESS) |
935 | 1004 | #if cmd: self.command_process(cmd) |
936 | - if cmd: | |
937 | - log.info('-'*6) | |
938 | - log.info('-'*6 + " RECEIVED NEW COMMAND TO PROCESS: ") | |
939 | - log.info('-'*6 + str(cmd)) | |
940 | - log.info('-'*6) | |
941 | - | |
942 | - # CASE 1 - AGENT LEVEL command | |
943 | - # => I process it directly without asking my DC | |
944 | - # => Simple action, short execution time, so I execute it directly (in current main thread, not in parallel) | |
945 | - if self._is_agent_level_cmd(cmd): | |
946 | - log.info("(AGENT LEVEL CMD)") | |
947 | - try: | |
948 | - self._exec_agent_cmd(cmd) | |
949 | - except AttributeError as e: | |
950 | - #self.log_e(f"EXCEPTION: Agent level specific command '{cmd.name}' unknown (not implemented as a function) :", e) | |
951 | - #self.log_e("Thus => I ignore this command...") | |
952 | - log.e(f"EXCEPTION: Agent level specific command '{cmd.name}' unknown (not implemented as a function) :", e) | |
953 | - log.e("Thus => I ignore this command...") | |
954 | - cmd.set_result("ERROR: INVALID AGENT LEVEL SPECIFIC COMMAND") | |
955 | - cmd.set_as_pending() | |
956 | - cmd.set_as_skipped() | |
957 | - | |
958 | - # CASE 2 - DEVICE LEVEL command | |
959 | - # => I delegate it to my DC | |
960 | - # => Execute it only if I am active and no currently running another device level cmd | |
961 | - # => Long execution time, so I will execute it in parallel (in a new thread or process) | |
962 | - #elif self.is_device_level_cmd(cmd): | |
963 | - elif self.is_other_level_cmd(cmd): | |
964 | - self.process_other_level_cmd(cmd) | |
965 | - | |
966 | - # CASE 3 - INVALID COMMAND | |
967 | - else: | |
968 | - #raise Exception("INVALID COMMAND: " + cmd.name) | |
969 | - log.warning("******************************************************") | |
970 | - log.warning("*************** ERROR: INVALID COMMAND (UNKNOWN) ***************") | |
971 | - log.warning("******************************************************") | |
972 | - log.warning("Thus => I ignore this command...") | |
973 | - cmd.set_result("ERROR: INVALID COMMAND") | |
974 | - cmd.set_as_skipped() | |
1005 | + | |
1006 | + # SIMU | |
1007 | + #cmd = "do_replan" | |
1008 | + #self.send_cmd_to("AgentScheduler", "do_replan") | |
1009 | + | |
1010 | + # No new command => nothing to do | |
1011 | + if not cmd: return | |
975 | 1012 | |
976 | - print() | |
977 | - log.info("*"*10 + " NEXT COMMAND PROCESSING (END) " + "*"*10 + "\n") | |
1013 | + log.info('-'*6) | |
1014 | + log.info('-'*6 + " RECEIVED NEW COMMAND TO PROCESS: ") | |
1015 | + log.info('-'*6 + str(cmd)) | |
1016 | + log.info('-'*6) | |
978 | 1017 | |
1018 | + cmd.set_read_time() | |
1019 | + | |
1020 | + # CASE 1 - AGENT GENERAL command | |
1021 | + # (DO_RESTART, DO_EXIST, DO_ABORT, DO_ABORT_COMMAND, DO_FLUSH_COMMANDS, ...) | |
1022 | + # This processing can set DO_MAIN_LOOP and DO_RESTART_LOOP to False | |
1023 | + if self._is_agent_general_cmd(cmd): | |
1024 | + try: | |
1025 | + self._process_agent_general_cmd(cmd) | |
1026 | + except AgentCmdUnimplementedException as e: | |
1027 | + print(e) | |
1028 | + cmd.set_as_skipped("ERROR: Unimplemented Agent General command") | |
1029 | + self._cleanup_before_exist() | |
1030 | + raise | |
1031 | + #except ValueError as e: | |
1032 | + except AgentCmdBadArgsException as e: | |
1033 | + print(e) | |
1034 | + cmd.set_as_skipped("ERROR: Bad Argument(s)") | |
1035 | + self._cleanup_before_exit() | |
1036 | + raise | |
1037 | + #raise AgentCmdBadArgsException(cmd.name) | |
1038 | + # Must I stop or restart ? | |
1039 | + if cmd.name in ('DO_RESTART','DO_EXIT','OO_ABORT') : | |
1040 | + self.DO_MAIN_LOOP = False | |
1041 | + if cmd.name == 'OO_ABORT': | |
1042 | + self._abort_current_running_cmd_if_exists() | |
1043 | + else : | |
1044 | + self._cleanup_before_exit() | |
1045 | + if cmd.name != 'OO_RESTART': | |
1046 | + self.DO_RESTART_LOOP = False | |
1047 | + return | |
1048 | + | |
1049 | + # CASE 2 - AGENT SPECIFIC command | |
1050 | + # => Simple action, short execution time, so I execute it directly (in current main thread, not in parallel) | |
1051 | + if self._is_agent_specific_cmd(cmd): | |
1052 | + #log.info("(AGENT LEVEL CMD)") | |
1053 | + if not self._IS_ATTENTIVE(): | |
1054 | + cmd.set_as_skipped("Skipped because I am not ATTENTIVE") | |
1055 | + return | |
1056 | + try: | |
1057 | + self._process_agent_specific_cmd(cmd) | |
1058 | + #self._exec_agent_cmd(cmd) | |
1059 | + #except AttributeError as e: | |
1060 | + except AgentCmdUnimplementedException as e: | |
1061 | + print(e) | |
1062 | + #self.log_e(f"EXCEPTION: Agent level specific command '{cmd.name}' unknown (not implemented as a function) :", e) | |
1063 | + #self.log_e("Thus => I ignore this command...") | |
1064 | + log.e(f"EXCEPTION: Agent specific command '{cmd.name}' unknown (not implemented as a function) :", e) | |
1065 | + #log.e("Thus => I ignore this command...") | |
1066 | + #cmd.set_result("ERROR: UNIMPLEMENTED AGENT SPECIFIC COMMAND", False) | |
1067 | + #cmd.set_as_pending() | |
1068 | + cmd.set_as_skipped("ERROR: UNIMPLEMENTED AGENT SPECIFIC COMMAND") | |
1069 | + self._cleanup_before_exit() | |
1070 | + raise AgentCmdUnimplementedException(cmd.name) | |
1071 | + return | |
1072 | + | |
1073 | + # CASE 3 - OTHER level command (DEVsICE level, only for AgentDevice) | |
1074 | + if self.is_device_level_cmd(cmd): | |
1075 | + self.process_device_level_cmd(cmd) | |
1076 | + return | |
1077 | + | |
1078 | + # CASE 4 - INVALID COMMAND | |
1079 | + #raise Exception("INVALID COMMAND: " + cmd.name) | |
1080 | + log.warning("******************************************************") | |
1081 | + log.warning("*************** ERROR: INVALID COMMAND (UNKNOWN) ***************") | |
1082 | + log.warning("******************************************************") | |
1083 | + #log.warning("Thus => I ignore this command...") | |
1084 | + #cmd.set_result("ERROR: INVALID COMMAND") | |
1085 | + cmd.set_as_skipped("ERROR: INVALID AGENT COMMAND") | |
1086 | + self._cleanup_before_exit() | |
1087 | + raise UnknownCmdException(cmd.name) | |
1088 | + | |
1089 | + #print() | |
1090 | + #log.info("*"*10 + " NEXT COMMAND PROCESSING (END) " + "*"*10 + "\n") | |
1091 | + | |
1092 | + | |
1093 | + | |
1094 | + | |
1095 | + def _abort_current_running_cmd_if_exists(self): | |
1096 | + pass | |
1097 | + | |
1098 | + def _cleanup_before_exit(self): | |
1099 | + self.do_things_before_exit() | |
979 | 1100 | |
980 | 1101 | |
981 | 1102 | def print_TEST_MODE(self): |
... | ... | @@ -1041,7 +1162,7 @@ class Agent: |
1041 | 1162 | |
1042 | 1163 | # To be overridden by subclasses |
1043 | 1164 | def routine_process_after_body(self): |
1044 | - if self.TEST_MODE: self._test_routine_process() | |
1165 | + if self.TEST_MODE: self._TEST_test_routine_process() | |
1045 | 1166 | |
1046 | 1167 | """ |
1047 | 1168 | def purge_commands(self): |
... | ... | @@ -1096,17 +1217,18 @@ class Agent: |
1096 | 1217 | log.info(f"[NEW MODE {mode}]") |
1097 | 1218 | self.mode = mode |
1098 | 1219 | |
1099 | - def _is_active(self): | |
1100 | - return self.mode == self.MODE_ACTIVE | |
1101 | - def _is_idle(self): | |
1102 | - return not self._is_active() | |
1220 | + # Test mode | |
1221 | + def IS_IDLE(self): return self.mode == self.MODE_IDLE | |
1222 | + def IS_ROUTINE(self): return self.mode == self.MODE_ROUTINE | |
1223 | + def IS_ATTENTIVE(self): return self.mode == self.MODE_ATTENTIVE | |
1103 | 1224 | |
1104 | - def _set_active(self): | |
1105 | - self._set_mode(self.MODE_ACTIVE) | |
1106 | - log.info("set active mode") | |
1107 | 1225 | |
1108 | - def _set_idle(self): | |
1226 | + def set_idle(self): | |
1109 | 1227 | self._set_mode(self.MODE_IDLE) |
1228 | + def set_routine(self): | |
1229 | + self._set_mode(self.MODE_ROUTINE) | |
1230 | + def set_attentive(self): | |
1231 | + self._set_mode(self.MODE_ATTENTIVE) | |
1110 | 1232 | |
1111 | 1233 | def show_mode_and_status(self): |
1112 | 1234 | log.info(f"CURRENT MODE is {self.mode} (status is {self.status})") |
... | ... | @@ -1149,7 +1271,8 @@ class Agent: |
1149 | 1271 | def _set_mode_from_config(self, agent_name): |
1150 | 1272 | # all agent are active ? |
1151 | 1273 | |
1152 | - mode = self.MODE_ACTIVE | |
1274 | + #mode = self.MODE_ACTIVE | |
1275 | + mode = self.MODE_ATTENTIVE | |
1153 | 1276 | self._set_mode(mode) |
1154 | 1277 | return True |
1155 | 1278 | # old config |
... | ... | @@ -1179,6 +1302,9 @@ class Agent: |
1179 | 1302 | log.debug("Initializing...") |
1180 | 1303 | self._set_status(self.STATUS_INIT) |
1181 | 1304 | |
1305 | + def _reload_config_if_changed(self): | |
1306 | + self._load_config() | |
1307 | + | |
1182 | 1308 | def _load_config(self): |
1183 | 1309 | """ |
1184 | 1310 | TODO: |
... | ... | @@ -1325,13 +1451,14 @@ class Agent: |
1325 | 1451 | real_agent_name = self._get_real_agent_name(to_agent) |
1326 | 1452 | except KeyError as e: |
1327 | 1453 | ''' |
1328 | - # real_agent_name = self._get_real_agent_name(to_agent) | |
1329 | - # if not real_agent_name: | |
1454 | + real_agent_name = self._get_real_agent_name(to_agent) | |
1455 | + if not real_agent_name: | |
1456 | + return AgentCmd.create(self.name, to_agent, cmd_name, cmd_args) | |
1330 | 1457 | # log.e("UNKNOWN AgentDevice ALIAS", to_agent) |
1331 | 1458 | # #self.log_e("Exception raised", e) |
1332 | 1459 | # log.e(f"=> Thus, I do not send this command '{cmd_name}'") |
1333 | 1460 | # return None |
1334 | - return AgentCmd.create(self.name, to_agent, cmd_name, cmd_args) | |
1461 | + return AgentCmd.create(self.name, real_agent_name, cmd_name, cmd_args) | |
1335 | 1462 | ''' |
1336 | 1463 | return AgentCmd( |
1337 | 1464 | sender=self.name, |
... | ... | @@ -1518,6 +1645,128 @@ class Agent: |
1518 | 1645 | |
1519 | 1646 | |
1520 | 1647 | |
1648 | + def _process_agent_general_cmd(self, cmd:AgentCmd): | |
1649 | + # GENERAL command (related to any agent) | |
1650 | + | |
1651 | + #self.print(f"Starting execution of an AGENT LEVEL cmd {cmd}...") | |
1652 | + log.info(f"Starting execution of an AGENT GENERAL cmd...") | |
1653 | + | |
1654 | + # Update read time to say that the command has been READ | |
1655 | + #cmd.set_read_time() | |
1656 | + cmd.set_as_running() | |
1657 | + | |
1658 | + log.info("(Agent level GENERAL CMD)") | |
1659 | + _,cmd_name,cmd_args = cmd.get_full_name_parts() | |
1660 | + #cmd_name, cmd_args = cmd.tokenize() | |
1661 | + #if cmd.name == "set_state:active": | |
1662 | + #elif cmd.name == "set_state:idle": | |
1663 | + | |
1664 | + # Default result (null) | |
1665 | + result = None | |
1666 | + | |
1667 | + #if cmd_name in ("do_abort", "do_exit", "do_restart_init"): | |
1668 | + if cmd_name in ("do_abort", "do_exit", "do_restart"): | |
1669 | + #self.printd("Current pending commands are:") | |
1670 | + #Command.show_commands(self._pending_commands) | |
1671 | + log.info("Stopping/Aborting current executing command if exists:") | |
1672 | + #self._kill_running_device_cmd_if_exists(cmd.sender) | |
1673 | + ''' | |
1674 | + self.do_things_before_exit(cmd.sender) | |
1675 | + if cmd_name == "do_restart_init": | |
1676 | + log.info("restart_init received: Restarting from init()") | |
1677 | + self._DO_RESTART=True | |
1678 | + elif cmd.name == "do_exit": | |
1679 | + self._DO_EXIT=True | |
1680 | + cmd.set_result('SHOULD BE DONE NOW') | |
1681 | + ''' | |
1682 | + result = "STOP or RESTART" | |
1683 | + | |
1684 | + elif cmd_name == "set_state": | |
1685 | + #if not cmd_args: raise ValueError() | |
1686 | + if not cmd_args: raise AgentCmdBadArgsException(cmd.name) | |
1687 | + state = cmd_args[0] | |
1688 | + if state == "IDLE": self.set_idle() | |
1689 | + elif state == "ROUTINE": self.set_routine() | |
1690 | + elif state == "ATTENTIVE": self.set_attentive() | |
1691 | + else: raise AgentCmdBadArgsException(cmd.name) | |
1692 | + #cmd.set_result("I am now " + state) | |
1693 | + result = "I am now " + state | |
1694 | + #time.sleep(1) | |
1695 | + #self.waitfor(1) | |
1696 | + | |
1697 | + #elif cmd_name in ("do_flush_commands"): | |
1698 | + elif cmd_name == "do_flush_commands": | |
1699 | + "flush_commands received: Delete all pending commands" | |
1700 | + AgentCmd.delete_pending_commands_for_agent(self.name) | |
1701 | + #cmd.set_result('DONE') | |
1702 | + result = "FLUSH DONE" | |
1703 | + | |
1704 | + elif cmd_name == "do_eval": | |
1705 | + #if not cmd_args: raise ValueError() | |
1706 | + if not cmd_args: raise AgentCmdBadArgsException(cmd.name) | |
1707 | + #cmd.set_result(eval(cmd_args)) | |
1708 | + result = eval(cmd_args) | |
1709 | + | |
1710 | + else: | |
1711 | + raise UnknownCmdException(cmd.name) | |
1712 | + ''' | |
1713 | + name = cmd.name | |
1714 | + args = None | |
1715 | + if " " in name: name,args = name.split() | |
1716 | + if name == "do_eval": | |
1717 | + if args==None: raise(ValueError) | |
1718 | + cmd.set_result(eval(args)) | |
1719 | + ''' | |
1720 | + | |
1721 | + cmd.set_as_processed(result) | |
1722 | + log.info("...Agent level GENERAL cmd has been executed") | |
1723 | + | |
1724 | + ''' | |
1725 | + # If cmd is "do_exit", kill myself (without any question, this is an order soldier !) | |
1726 | + # This "do_exit" should normally kill any current thread (to be checked...) | |
1727 | + if cmd.name == "do_exit": | |
1728 | + log.info("Before exiting, Here are (if exists) the current (still) pending commands (time ordered) :") | |
1729 | + commands = AgentCmd.get_pending_and_running_commands_for_agent(self.name) | |
1730 | + AgentCmd.show_commands(commands, True) | |
1731 | + #if self.TEST_MODE and self.TEST_WITH_FINAL_TEST and self.TEST_COMMANDS_DEST == "myself": self.simulator_test_results() | |
1732 | + if self.TEST_MODE and self.TEST_WITH_FINAL_TEST: | |
1733 | + self._TEST_test_results() | |
1734 | + #self._DO_EXIT=True | |
1735 | + #exit(0) | |
1736 | + ''' | |
1737 | + | |
1738 | + | |
1739 | + | |
1740 | + | |
1741 | + def _process_agent_specific_cmd(self, cmd:AgentCmd): | |
1742 | + | |
1743 | + #self.print(f"Starting execution of an AGENT LEVEL cmd {cmd}...") | |
1744 | + log.info(f"Starting execution of an AGENT LEVEL cmd...") | |
1745 | + | |
1746 | + # Update read time to say that the command has been READ | |
1747 | + #cmd.set_read_time(False) | |
1748 | + cmd.set_as_running() | |
1749 | + | |
1750 | + log.info("(Agent SPECIFIC cmd)") | |
1751 | + # Execute method self."cmd.name"() | |
1752 | + # This can raise an exception (caught by this method caller) | |
1753 | + self.exec_cmd_from_its_name(cmd) | |
1754 | + ''' | |
1755 | + try: | |
1756 | + except AttributeError as e: | |
1757 | + self.printd(f"EXCEPTION: Agent level specific command '{cmd.name}' unknown (not implemented as a function) :", e) | |
1758 | + self.printd("Thus => I ignore this command...") | |
1759 | + cmd.set_result("ERROR: INVALID AGENT LEVEL SPECIFIC COMMAND") | |
1760 | + cmd.set_as_pending() | |
1761 | + cmd.set_as_skipped() | |
1762 | + return | |
1763 | + ''' | |
1764 | + #cmd.set_result("Agent level SPECIFIC cmd done") | |
1765 | + cmd.set_as_processed("Agent SPECIFIC cmd done") | |
1766 | + log.info("...Agent SPECIFIC cmd has been executed") | |
1767 | + | |
1768 | + | |
1769 | + | |
1521 | 1770 | |
1522 | 1771 | ''' |
1523 | 1772 | def do_log(self): |
... | ... | @@ -1531,13 +1780,23 @@ class Agent: |
1531 | 1780 | |
1532 | 1781 | def exec_cmd_from_its_name(self, cmd:AgentCmd): |
1533 | 1782 | func = cmd.name |
1534 | - if cmd.args: | |
1535 | - return getattr(self, func)(*cmd.args) | |
1536 | - else: | |
1537 | - return getattr(self, func)() | |
1783 | + try: | |
1784 | + if cmd.args: | |
1785 | + return getattr(self, func)(*cmd.args) | |
1786 | + else: | |
1787 | + return getattr(self, func)() | |
1788 | + except AttributeError as e: | |
1789 | + # TODO: AgentCmdBadArgsException si la fonction existe dans le code de l'agent | |
1790 | + print("I know this specific command but it is not yet implemented : ", func) | |
1791 | + print(e) | |
1792 | + raise AgentCmdUnimplementedException(cmd.name) | |
1538 | 1793 | |
1539 | 1794 | def _is_agent_level_cmd(self, cmd:AgentCmd): |
1540 | - return cmd.is_agent_general_cmd() or self._is_agent_specific_cmd(cmd) | |
1795 | + return self._is_agent_general_cmd(cmd) or self._is_agent_specific_cmd(cmd) | |
1796 | + | |
1797 | + def _is_agent_general_cmd(self, cmd:AgentCmd): | |
1798 | + return cmd.is_agent_general_cmd() | |
1799 | + | |
1541 | 1800 | |
1542 | 1801 | ''' |
1543 | 1802 | def _exec_agent_cmd(self, cmd:Command): |
... | ... | @@ -1578,8 +1837,7 @@ class Agent: |
1578 | 1837 | |
1579 | 1838 | # To be overriden by subclass (AgentDevice...) |
1580 | 1839 | # @abstract |
1581 | - #def is_device_level_cmd(self, cmd): | |
1582 | - def is_other_level_cmd(self, cmd): | |
1840 | + def is_device_level_cmd(self, cmd): | |
1583 | 1841 | return False |
1584 | 1842 | |
1585 | 1843 | ''' |
... | ... | @@ -1641,7 +1899,7 @@ class Agent: |
1641 | 1899 | #self._nb_test_cmds += 1 |
1642 | 1900 | """ |
1643 | 1901 | |
1644 | - def _test_routine_process(self): | |
1902 | + def _TEST_test_routine_process(self): | |
1645 | 1903 | """ |
1646 | 1904 | TEST MODE ONLY |
1647 | 1905 | """ |
... | ... | @@ -1713,7 +1971,7 @@ class Agent: |
1713 | 1971 | self._cmdts = self._TEST_get_next_command_to_send() |
1714 | 1972 | # No more command to send (from simulator) => return and EXIT |
1715 | 1973 | if self._cmdts is None: |
1716 | - self._DO_MAIN_LOOP = False | |
1974 | + self.DO_MAIN_LOOP = False | |
1717 | 1975 | return |
1718 | 1976 | # Send cmd (= set as pending and save) |
1719 | 1977 | log.info("***") |
... | ... | @@ -1787,6 +2045,18 @@ class Agent: |
1787 | 2045 | nb_asserted2 = self.TEST_test_results_main(commands) |
1788 | 2046 | self._TEST_test_results_end(nb_asserted) |
1789 | 2047 | |
2048 | + def TEST_prepare(self): | |
2049 | + log.debug("\n!!! In TEST mode !!! => preparing to run a scenario of test commands") | |
2050 | + log.debug("- Current test commands list scenario is:\n" + str(self.TEST_COMMANDS_LIST)) | |
2051 | + if not self.WITH_SIMULATOR: | |
2052 | + log.debug("\n!!! In TEST but no SIMULATOR mode (using REAL device) !!! => removing dangerous commands for real devices... :") | |
2053 | + TEST_COMMANDS_LIST_copy = self.TEST_COMMANDS_LIST.copy() | |
2054 | + for cmd in TEST_COMMANDS_LIST_copy: | |
2055 | + cmd_key = cmd.split()[1] | |
2056 | + if ("set_" in cmd_key) or ("do_start" in cmd_key) or cmd_key in ["do_init", "do_goto", "do_open", "do_close"]: | |
2057 | + self.TEST_COMMANDS_LIST.remove(cmd) | |
2058 | + log.debug("- NEW test commands list scenario is:\n" + self.TEST_COMMANDS_LIST, '\n') | |
2059 | + | |
1790 | 2060 | # Can be overriden by subclass (AgentDevice) |
1791 | 2061 | def TEST_test_results_other(self, commands): |
1792 | 2062 | pass |
... | ... | @@ -1889,7 +2159,7 @@ def extract_parameters(): |
1889 | 2159 | |
1890 | 2160 | #def build_agent(Agent_type:Agent, name="GenericAgent", RUN_IN_THREAD=True): |
1891 | 2161 | #def build_agent(Agent_type:Agent, RUN_IN_THREAD=True): |
1892 | -def build_agent(Agent_type:Agent): | |
2162 | +def build_agent(Agent_type:Agent) -> Agent : | |
1893 | 2163 | #DEBUG_MODE, WITH_SIM, TEST_MODE, VERBOSE_MODE, configfile = extract_parameters() |
1894 | 2164 | DEBUG_MODE, TEST_MODE, VERBOSE_MODE = extract_parameters() |
1895 | 2165 | log.debug("debug mode is" + os.getenv("PYROS_DEBUG")) | ... | ... |
src/core/pyros_django/agent/AgentDevice.py
... | ... | @@ -295,14 +295,19 @@ class AgentDevice(Agent): |
295 | 295 | ================================================================================================ |
296 | 296 | """ |
297 | 297 | |
298 | + ''' | |
298 | 299 | # @override superclass (Agent) method |
299 | 300 | def is_other_level_cmd(self, cmd: AgentCmd): |
300 | 301 | return self.is_device_level_cmd(cmd) |
302 | + ''' | |
301 | 303 | |
304 | + # I delegate this command to my DC | |
305 | + # => Execute it only if I am active and no currently running another device level cmd | |
306 | + # => Long execution time, so I will execute it in parallel (in a new thread or process) | |
302 | 307 | def is_device_level_cmd(self, cmd: AgentCmd): |
303 | 308 | return self._device_ctrl.is_valid_cmd(DeviceCmd(cmd.full_name)) |
304 | 309 | |
305 | - def process_other_level_cmd(self): | |
310 | + def process_device_level_cmd(self): | |
306 | 311 | log.info("(DEVICE LEVEL CMD)") |
307 | 312 | try: |
308 | 313 | self.exec_device_cmd_if_possible(cmd) | ... | ... |
src/core/pyros_django/agent/doc/Agent_activity_diag.pu
1 | 1 | |
2 | 2 | @startuml |
3 | 3 | |
4 | -' --- Agent & AgentDevice ACTIVIY DIAGRAM (plantUML) --- | |
4 | +' --- Agent ACTIVIY DIAGRAM (plantUML) --- | |
5 | 5 | |
6 | 6 | ' NEW syntax => http://plantuml.com/fr/activity-diagram-beta |
7 | 7 | ' OLD syntax => http://plantuml.com/fr/activity-diagram-legacy |
... | ... | @@ -25,24 +25,143 @@ |
25 | 25 | |
26 | 26 | |
27 | 27 | title |
28 | -__**Agent & AgentDevice run() function : Activity Diagram**__ | |
29 | -<size:10><i>Version 19-11-2019</i></size> | |
28 | +__**Agent Activity Diagram**__ | |
29 | +<size:10><i>Version 10-06-2022</i></size> | |
30 | 30 | |
31 | 31 | end title |
32 | 32 | '(E. Pallier) |
33 | 33 | |
34 | + | |
35 | + | |
34 | 36 | '|Agent| |
35 | 37 | start |
36 | 38 | |
37 | -:DO_EXIT = False | |
38 | -DO_RESTART = True; | |
39 | +:STATUS = ATTENTIVE; | |
40 | +note right | |
41 | + Possible statuses : IDLE, ROUTINE, ATTENTIVE | |
42 | +end note | |
43 | + | |
44 | +:DO_RESTART = True; | |
45 | + | |
46 | +' *** RESTART loop *** | |
47 | +' while (DO_RESTART ?) is (yes) | |
48 | +repeat | |
39 | 49 | |
40 | -while (DO_RESTART ?) is (yes) | |
41 | 50 | :load_config(); |
42 | 51 | :init(); |
43 | 52 | :DO_MAIN_LOOP = True; |
53 | + | |
54 | + ' *** MAIN loop *** | |
55 | + 'while (DO_MAIN_LOOP ?) is (yes) | |
56 | + repeat | |
57 | + | |
58 | + :reload_config_if_changed(); | |
59 | + | |
60 | + :log_agent_status(); | |
61 | + note right | |
62 | + Log this agent status in DB | |
63 | + end note | |
64 | + | |
65 | + if (STATUS is 'IDLE' ?) then (yes) | |
66 | + else (no)) | |
67 | + #green:process_routine_before(); | |
68 | + endif | |
69 | + | |
70 | + :CMD = get_next_cmd_if_exists(); | |
71 | + if (CMD ?) then | |
72 | + | |
73 | + ' AGENT GENERAL level | |
74 | + if (is_agent_general_level(CMD) ?) then (yes) | |
75 | + | |
76 | + partition AGENT_GENERAL_level { | |
77 | + #green:process_agent_level_general_cmd(CMD); | |
78 | + | |
79 | + ' I must stop or restart | |
80 | + if (CMD is DO_ABORT || DO_EXIT || DO_RESTART ?) then (yes) | |
81 | + | |
82 | + :DO_MAIN_LOOP = False; | |
83 | + | |
84 | + if (CMD is DO_ABORT ?) then (yes) | |
85 | + :abort_current_running_cmd_if_exists(); | |
86 | + else (no) | |
87 | + :cleanup_before_exit(); | |
88 | + endif | |
89 | + | |
90 | + if (CMD is DO_RESTART ?) then (yes) | |
91 | + else (no) | |
92 | + :DO_RESTART = False; | |
93 | + endif | |
94 | + | |
95 | + ->// ==> GOTO "DO_MAIN_LOOP ?"//; | |
96 | + | |
97 | + break | |
98 | + | |
99 | + ' I don't have to stop | |
100 | + else (no) | |
101 | + ' end of "I must stop or restart ?" | |
102 | + ' ->NOK; | |
103 | + ' :Alert "toto"; | |
104 | + endif | |
105 | + } | |
106 | + | |
107 | + | |
108 | + | |
109 | + ' AGENT SPECIFIC level | |
110 | + else (no) | |
111 | + partition AGENT_SPECIFIC_level { | |
112 | + if (STATUS is 'ATTENTIVE' ?) then (yes) | |
113 | + if (is_agent_specific_level(CMD) ?) then (yes) | |
114 | + #green:process_agent_level_specific_cmd(CMD); | |
115 | + if (KO?) then (yes) | |
116 | + #pink:cleanup_before_exit() | |
117 | + EXC UnimplementedAgentLevelSpecificCmdExc; | |
118 | + kill | |
119 | + else (no) | |
120 | + endif | |
121 | + else (no) | |
122 | + #pink:cleanup_before_exit() | |
123 | + EXC UnknownCmdException; | |
124 | + kill | |
125 | + endif | |
126 | + else (no) | |
127 | + endif | |
128 | + } | |
129 | + | |
130 | + ' Agent level Command processing | |
131 | + endif | |
132 | + | |
133 | + ' No command to process | |
134 | + else (no) | |
135 | + | |
136 | + ' Command processing | |
137 | + endif | |
138 | + | |
139 | + if (STATUS is 'IDLE' ?) then (yes) | |
140 | + else (no) | |
141 | + #green:process_routine_after(); | |
142 | + endif | |
143 | + | |
144 | + | |
145 | + ' DO_MAIN_LOOP loop | |
146 | + 'endwhile (no) | |
147 | + repeat while (DO_MAIN_LOOP ?) is (yes) not (no) | |
148 | + ' ->//merged step//; | |
149 | + | |
150 | +' DO_RESTART loop | |
151 | +' endwhile (no) | |
152 | +repeat while (DO_RESTART ?) is (yes) not (no) | |
153 | + | |
154 | +stop | |
155 | + | |
156 | + | |
157 | + | |
158 | + | |
159 | + | |
160 | +/' | |
44 | 161 | while (DO_MAIN_LOOP ?) is (yes) |
162 | + | |
45 | 163 | partition main_loop() { |
164 | + | |
46 | 165 | :reload_config(); |
47 | 166 | note right |
48 | 167 | only if changed |
... | ... | @@ -53,7 +172,10 @@ while (DO_RESTART ?) is (yes) |
53 | 172 | Log this agent status in DB |
54 | 173 | end note |
55 | 174 | |
56 | - :routine_process(); | |
175 | + :routine_process_before(); | |
176 | + note right | |
177 | + Routine process at "loop start" | |
178 | + end note | |
57 | 179 | |
58 | 180 | :COMMAND = get_next_valid_command(); |
59 | 181 | if (COMMAND ?) then |
... | ... | @@ -82,8 +204,8 @@ while (DO_RESTART ?) is (yes) |
82 | 204 | :DO_MAIN_LOOP = False; |
83 | 205 | endif |
84 | 206 | |
85 | - | |
86 | 207 | } |
208 | + | |
87 | 209 | endwhile (no) |
88 | 210 | endwhile (no) |
89 | 211 | |
... | ... | @@ -91,9 +213,7 @@ stop |
91 | 213 | |
92 | 214 | |
93 | 215 | (A) |
94 | -/' | |
95 | - exec_agent_cmd() | |
96 | -'/ | |
216 | +' exec_agent_cmd() | |
97 | 217 | if (is_agent_specific_cmd(COMMAND) ?) then (yes) |
98 | 218 | :exec_cmd_from_its_name(COMMAND); |
99 | 219 | else (no) |
... | ... | @@ -105,9 +225,7 @@ detach |
105 | 225 | |
106 | 226 | |
107 | 227 | (D) |
108 | -/' | |
109 | - exec_device_cmd_if_possible() | |
110 | -'/ | |
228 | +' exec_device_cmd_if_possible() | |
111 | 229 | if (I am IDLE ?) then (yes) |
112 | 230 | :SKIP command (ignored); |
113 | 231 | else if (another device cmd running ?) then (yes) |
... | ... | @@ -130,4 +248,6 @@ else (no) |
130 | 248 | } |
131 | 249 | endif |
132 | 250 | |
251 | +'/ | |
252 | + | |
133 | 253 | @enduml | ... | ... |
src/core/pyros_django/common/models.py
1 | 1 | ##from __future__ import unicode_literals |
2 | 2 | |
3 | 3 | # Stdlib imports |
4 | +from numpy import False_ | |
4 | 5 | from src.device_controller.abstract_component.device_controller import DeviceCmd |
5 | 6 | from enum import Enum |
6 | 7 | from datetime import datetime, timedelta, date |
... | ... | @@ -488,7 +489,7 @@ class AgentCmd(models.Model): |
488 | 489 | # TODO: maybe à mettre au format json (key:value) |
489 | 490 | result = models.CharField(max_length=400, blank=True) |
490 | 491 | |
491 | - # on creation: (AUTO) Automatically set at table line creation (line created by the sender) | |
492 | + # - on creation: (AUTO) Automatically set at table line creation (line created by the sender) | |
492 | 493 | s_deposit_time = models.DateTimeField( |
493 | 494 | blank=True, null=True, auto_now_add=True) |
494 | 495 | # - on reading: |
... | ... | @@ -810,18 +811,18 @@ class AgentCmd(models.Model): |
810 | 811 | self.refresh_from_db() |
811 | 812 | return self.result |
812 | 813 | |
813 | - def set_result(self, result: str): | |
814 | + def set_result(self, result: str, do_save:bool=True): | |
814 | 815 | self.result = result |
815 | - self.save() | |
816 | + if do_save: self.save() | |
816 | 817 | |
817 | - def set_read_time(self): | |
818 | + def set_read_time(self, do_save:bool=True): | |
818 | 819 | self.r_read_time = datetime.utcnow().astimezone() |
819 | 820 | # Optimization: update only 1 field |
820 | - self.save(update_fields=["r_read_time"]) | |
821 | + if do_save: self.save(update_fields=["r_read_time"]) | |
821 | 822 | |
822 | - def set_as_processed(self): | |
823 | + def set_as_processed(self, result:str=None): | |
823 | 824 | printd(f"- Set command {self.name} as processed") |
824 | - self.set_state_to(self.CMD_STATUS_CODES.CMD_EXECUTED) | |
825 | + self.set_state_to(self.CMD_STATUS_CODES.CMD_EXECUTED, result=result) | |
825 | 826 | # print(self) |
826 | 827 | """ |
827 | 828 | self.state = self.CMD_STATUS_CODES.CMD_EXECUTED |
... | ... | @@ -839,8 +840,8 @@ class AgentCmd(models.Model): |
839 | 840 | def set_as_pending(self): |
840 | 841 | self.set_state_to(self.CMD_STATUS_CODES.CMD_PENDING) |
841 | 842 | |
842 | - def set_as_skipped(self): | |
843 | - self.set_state_to(self.CMD_STATUS_CODES.CMD_SKIPPED) | |
843 | + def set_as_skipped(self, result:str=None): | |
844 | + self.set_state_to(self.CMD_STATUS_CODES.CMD_SKIPPED, result=result) | |
844 | 845 | |
845 | 846 | def set_as_killed_by(self, author_agent: str): |
846 | 847 | printd(f"- Set command {self.name} as killed") |
... | ... | @@ -855,8 +856,13 @@ class AgentCmd(models.Model): |
855 | 856 | self.set_state_to(self.CMD_STATUS_CODES.CMD_EXECUTED) |
856 | 857 | ''' |
857 | 858 | |
858 | - def set_state_to(self, status: str, author_agent_name: str = None): | |
859 | + def set_state_to(self, status: str, author_agent_name: str = None, result:str=None): | |
860 | + ''' | |
861 | + If comment is present it will be set in the "result" field | |
862 | + ''' | |
859 | 863 | now_time = datetime.utcnow().astimezone() |
864 | + if result: | |
865 | + self.set_result(result, False) | |
860 | 866 | if status in (self.CMD_STATUS_CODES.CMD_RUNNING, self.CMD_STATUS_CODES.CMD_SKIPPED, self.CMD_STATUS_CODES.CMD_OUTOFDATE): |
861 | 867 | assert self.is_pending() |
862 | 868 | if status == self.CMD_STATUS_CODES.CMD_RUNNING: | ... | ... |