Commit 128e1a71f8c3d25e70c7bc0a9a795bd772f0caa0
1 parent
f7972fc3
Exists in
dev
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
Showing
6 changed files
with
294 additions
and
8 deletions
Show diff stats
README.md
@@ -67,14 +67,22 @@ This software has been tested and validated with the following configurations : | @@ -67,14 +67,22 @@ This software has been tested and validated with the following configurations : | ||
67 | -------------------------------------------------------------------------------------------- | 67 | -------------------------------------------------------------------------------------------- |
68 | ## LAST VERSION | 68 | ## LAST VERSION |
69 | 69 | ||
70 | -Date: 17/04/2019 | 70 | +Date: 26/04/2019 |
71 | 71 | ||
72 | Author: E. Pallier | 72 | Author: E. Pallier |
73 | 73 | ||
74 | -VERSION: 0.20.38 | 74 | +VERSION: 0.20.40 |
75 | 75 | ||
76 | -Comment: | ||
77 | - Génération automatique des diagrammes PlantUML avec "pyros update" | 76 | +Comment: AgentDevice + AgentDeviceTelescopeStatus + AgentTelescopeRequester |
77 | + | ||
78 | + - Dans le détail : | ||
79 | + - AgentDevice met à jour la table AgentDeviceTelescopeStatus | ||
80 | + - AgentTelescopeRequester interroge AgentDevice | ||
81 | + - Quelques bugfixes | ||
82 | + - Mode opératoire: | ||
83 | + ./pyros start agentDevice | ||
84 | + ou, en mode simu: | ||
85 | + ./pyros -t start agentTelescopeRequester,agentDevice | ||
78 | 86 | ||
79 | - Scenario de test : | 87 | - Scenario de test : |
80 | - lancer agents A et B en mode simu (option -t): ./pyros.py -t start agentA,agentB | 88 | - lancer agents A et B en mode simu (option -t): ./pyros.py -t start agentA,agentB |
@@ -89,6 +97,7 @@ Comment: | @@ -89,6 +97,7 @@ Comment: | ||
89 | - pour utiliser thread ou processus : il suffit de mettre la constante RUN_IN_THREAD de AgentA (ou AgentB ou AgentX) à False ou True | 97 | - pour utiliser thread ou processus : il suffit de mettre la constante RUN_IN_THREAD de AgentA (ou AgentB ou AgentX) à False ou True |
90 | 98 | ||
91 | - Historique des nouveautés implémentées : | 99 | - Historique des nouveautés implémentées : |
100 | + - Génération automatique des diagrammes PlantUML avec "pyros update" | ||
92 | - Implémentation complète du "Command state diag" | 101 | - Implémentation complète du "Command state diag" |
93 | - Nouveaux diagrammes UML pour Command (state diag) et Agent (activity diag) | 102 | - Nouveaux diagrammes UML pour Command (state diag) et Agent (activity diag) |
94 | - Déplacé AgentM dans src/monitoring/ (pyros start agentM) | 103 | - Déplacé AgentM dans src/monitoring/ (pyros start agentM) |
pyros.py
@@ -31,6 +31,7 @@ AGENTS = { | @@ -31,6 +31,7 @@ AGENTS = { | ||
31 | "agentB" : "AgentB", | 31 | "agentB" : "AgentB", |
32 | "agentM" : "AgentM", | 32 | "agentM" : "AgentM", |
33 | "agentDevice" : "AgentDevice", | 33 | "agentDevice" : "AgentDevice", |
34 | + "agentTelescopeRequester" : "AgentTelescopeRequester", | ||
34 | "webserver" : "webserver", | 35 | "webserver" : "webserver", |
35 | "monitoring" : "monitoring", | 36 | "monitoring" : "monitoring", |
36 | "majordome" : "majordome", | 37 | "majordome" : "majordome", |
src/agent/Agent.py
@@ -490,8 +490,8 @@ class Agent: | @@ -490,8 +490,8 @@ class Agent: | ||
490 | if not self.DO_MAIN_LOOP: break | 490 | if not self.DO_MAIN_LOOP: break |
491 | except KeyboardInterrupt: | 491 | except KeyboardInterrupt: |
492 | # In case of CTRL-C, kill the current thread (process) before dying (in error) | 492 | # In case of CTRL-C, kill the current thread (process) before dying (in error) |
493 | - self.print("CTRL-C Interrupted, I kill the current thread (process) before exiting") | ||
494 | - self.kill_running_specific_cmd_if_exists() | 493 | + self.print("CTRL-C Interrupted, I kill the current thread (process) before exiting (if exists)") |
494 | + self.kill_running_specific_cmd_if_exists("USER_CTRLC") | ||
495 | exit(1) | 495 | exit(1) |
496 | #if self.DO_EXIT: exit(0) | 496 | #if self.DO_EXIT: exit(0) |
497 | 497 | ||
@@ -1025,9 +1025,9 @@ class Agent: | @@ -1025,9 +1025,9 @@ class Agent: | ||
1025 | 1025 | ||
1026 | def kill_running_specific_cmd_if_exists(self, abort_sender): | 1026 | def kill_running_specific_cmd_if_exists(self, abort_sender): |
1027 | if (self._current_specific_thread is None) or not self._current_specific_thread.is_alive(): | 1027 | if (self._current_specific_thread is None) or not self._current_specific_thread.is_alive(): |
1028 | - self.printd("...No current specific command thread to abort...") | 1028 | + self.print("...No current specific command thread to abort...") |
1029 | else: | 1029 | else: |
1030 | - self.printd(f"Killing command {self._current_specific_cmd.name}") | 1030 | + self.print(f"Killing command {self._current_specific_cmd.name}") |
1031 | # Ask the thread to stop itself | 1031 | # Ask the thread to stop itself |
1032 | #self._current_specific_thread.stop() | 1032 | #self._current_specific_thread.stop() |
1033 | #self._current_specific_thread._stop() | 1033 | #self._current_specific_thread._stop() |
src/agent/AgentDevice.py
@@ -8,6 +8,7 @@ import time | @@ -8,6 +8,7 @@ import time | ||
8 | ##from .Agent import Agent | 8 | ##from .Agent import Agent |
9 | sys.path.append("..") | 9 | sys.path.append("..") |
10 | from agent.Agent import Agent, extract_parameters | 10 | from agent.Agent import Agent, extract_parameters |
11 | +from common.models import AgentDeviceTelescopeStatus | ||
11 | from devices_channel.client.telescope_controller_gemini import TelescopeControllerGEMINI | 12 | from devices_channel.client.telescope_controller_gemini import TelescopeControllerGEMINI |
12 | 13 | ||
13 | ##log = L.setupLogger("AgentXTaskLogger", "AgentX") | 14 | ##log = L.setupLogger("AgentXTaskLogger", "AgentX") |
@@ -78,16 +79,24 @@ class AgentDevice(Agent): | @@ -78,16 +79,24 @@ class AgentDevice(Agent): | ||
78 | #HOST, PORT = "localhost", 11110 | 79 | #HOST, PORT = "localhost", 11110 |
79 | self.tele_ctrl = TelescopeControllerGEMINI(HOST, PORT, True) | 80 | self.tele_ctrl = TelescopeControllerGEMINI(HOST, PORT, True) |
80 | 81 | ||
82 | + | ||
81 | # @override | 83 | # @override |
82 | def init(self): | 84 | def init(self): |
85 | + | ||
83 | super().init() | 86 | super().init() |
84 | # --- Set the mode according the startmode value | 87 | # --- Set the mode according the startmode value |
85 | ##agent_alias = self.__class__.__name__ | 88 | ##agent_alias = self.__class__.__name__ |
86 | ##self.set_mode_from_config(agent_alias) | 89 | ##self.set_mode_from_config(agent_alias) |
90 | + | ||
91 | + # Device socket init | ||
87 | # (optional) Only useful for TCP (does nothing for UDP) | 92 | # (optional) Only useful for TCP (does nothing for UDP) |
88 | self.tele_ctrl._connect_to_device() | 93 | self.tele_ctrl._connect_to_device() |
89 | self.tele_ctrl.print_available_commands() | 94 | self.tele_ctrl.print_available_commands() |
90 | 95 | ||
96 | + # Telescope (long) init | ||
97 | + # TODO: | ||
98 | + | ||
99 | + | ||
91 | ''' | 100 | ''' |
92 | # @override | 101 | # @override |
93 | def load_config(self): | 102 | def load_config(self): |
@@ -129,11 +138,19 @@ class AgentDevice(Agent): | @@ -129,11 +138,19 @@ class AgentDevice(Agent): | ||
129 | cmd="get radec" | 138 | cmd="get radec" |
130 | res = self.tele_ctrl.execute_cmd(cmd) | 139 | res = self.tele_ctrl.execute_cmd(cmd) |
131 | print("result is", str(res)) | 140 | print("result is", str(res)) |
141 | + myradec = str(res) | ||
132 | if res.ok: print("OK") | 142 | if res.ok: print("OK") |
133 | 143 | ||
144 | + AgentDeviceTelescopeStatus.objects.create(radec=myradec) | ||
145 | + | ||
134 | time.sleep(3) | 146 | time.sleep(3) |
135 | self.print("ROUTINE PROCESS END") | 147 | self.print("ROUTINE PROCESS END") |
136 | 148 | ||
149 | + # @override | ||
150 | + def kill_running_specific_cmd_if_exists(self, abort_sender): | ||
151 | + super().kill_running_specific_cmd_if_exists(abort_sender) | ||
152 | + print("Close telescope socket") | ||
153 | + self.tele_ctrl.close() | ||
137 | 154 | ||
138 | 155 | ||
139 | 156 |
@@ -0,0 +1,245 @@ | @@ -0,0 +1,245 @@ | ||
1 | +#!/usr/bin/env python3 | ||
2 | + | ||
3 | + | ||
4 | +import sys | ||
5 | +##import utils.Logger as L | ||
6 | + | ||
7 | +##from .Agent import Agent | ||
8 | +sys.path.append("..") | ||
9 | +from agent.Agent import Agent, extract_parameters | ||
10 | + | ||
11 | + | ||
12 | +##log = L.setupLogger("AgentXTaskLogger", "AgentX") | ||
13 | + | ||
14 | + | ||
15 | + | ||
16 | +class AgentTelescopeRequester(Agent): | ||
17 | + | ||
18 | + # FOR TEST ONLY | ||
19 | + # Run this agent in simulator mode | ||
20 | + SIMULATOR_MODE = False | ||
21 | + # Run the assertion tests at the end | ||
22 | + SIMULATOR_WITH_TEST = True | ||
23 | + #SIMULATOR_MAX_DURATION_SEC = None | ||
24 | + SIMULATOR_MAX_DURATION_SEC = 100 | ||
25 | + # Who should I send commands to ? | ||
26 | + #SIMULATOR_COMMANDS_DEST = "myself" | ||
27 | + SIMULATOR_COMMANDS_DEST = "AgentDevice" | ||
28 | + # Scenario to be executed | ||
29 | + SIMULATOR_COMMANDS_LIST = [ | ||
30 | + # Ask receiver to delete all its previous commands | ||
31 | + "flush_commands", | ||
32 | + | ||
33 | + "go_active", | ||
34 | + | ||
35 | + # Because of this command, the receiver agent : | ||
36 | + # - will no more send any new command | ||
37 | + # - will only execute "generic" commands (and not the "specific" ones) | ||
38 | + "go_idle", | ||
39 | + | ||
40 | + # Not executed (skipped) because receiver agent is now "idle" | ||
41 | + #"specific0", | ||
42 | + | ||
43 | + # Because of this command, the receiver agent | ||
44 | + # will now be able to send new commands | ||
45 | + "go_active", | ||
46 | + | ||
47 | + # Executed because recipient agent is now "active" | ||
48 | + "specific1", | ||
49 | + # should abort previous command (specific1) | ||
50 | + "abort", | ||
51 | + | ||
52 | + # Executed completely because no abort | ||
53 | + "specific2", | ||
54 | + | ||
55 | + # fully executed, result is 7 | ||
56 | + "eval 4+3", | ||
57 | + | ||
58 | + "go_idle", | ||
59 | + "exit", | ||
60 | + ] | ||
61 | + | ||
62 | + | ||
63 | + | ||
64 | + """ | ||
65 | + ================================================================= | ||
66 | + FUNCTIONS RUN INSIDE MAIN THREAD | ||
67 | + ================================================================= | ||
68 | + """ | ||
69 | + | ||
70 | + # @override | ||
71 | + def __init__(self, name:str=None, config_filename=None, RUN_IN_THREAD=True): | ||
72 | + if name is None: name = self.__class__.__name__ | ||
73 | + super().__init__(name, config_filename, RUN_IN_THREAD) | ||
74 | + self._log.print(f"init done for {name}") | ||
75 | + | ||
76 | + # @override | ||
77 | + def init(self): | ||
78 | + super().init() | ||
79 | + # --- Set the mode according the startmode value | ||
80 | + ##agent_alias = self.__class__.__name__ | ||
81 | + ##self.set_mode_from_config(agent_alias) | ||
82 | + | ||
83 | + ''' | ||
84 | + # @override | ||
85 | + def load_config(self): | ||
86 | + super().load_config() | ||
87 | + ''' | ||
88 | + | ||
89 | + ''' | ||
90 | + # @override | ||
91 | + def update_survey(self): | ||
92 | + super().update_survey() | ||
93 | + ''' | ||
94 | + | ||
95 | + ''' | ||
96 | + # @override | ||
97 | + def get_next_command(self): | ||
98 | + return super().get_next_command() | ||
99 | + ''' | ||
100 | + | ||
101 | + # @override | ||
102 | + def do_log(self): | ||
103 | + super().do_log() | ||
104 | + | ||
105 | + | ||
106 | + | ||
107 | + """ | ||
108 | + ================================================================= | ||
109 | + FUNCTIONS RUN INSIDE A SUB-THREAD (OR A PROCESS) (thread_*()) | ||
110 | + ================================================================= | ||
111 | + """ | ||
112 | + | ||
113 | + # Define your own command step(s) here | ||
114 | + def cmd_step1(self, step:int): | ||
115 | + cmd = self._current_specific_cmd | ||
116 | + cmd.result = f"in step #{step}/{self._thread_total_steps_number}" | ||
117 | + cmd.save() | ||
118 | + """ | ||
119 | + if self.RUN_IN_THREAD: | ||
120 | + print("(save from thread)") | ||
121 | + cmd.save() | ||
122 | + else: | ||
123 | + #@transaction.atomic | ||
124 | + print("(save from process)") | ||
125 | + with transaction.atomic(): | ||
126 | + cmd.save() | ||
127 | + #Command.objects.select_for_update() | ||
128 | + """ | ||
129 | + | ||
130 | + def cmd_step2(self, step:int): | ||
131 | + self.cmd_step1(step) | ||
132 | + def cmd_step3(self, step:int): | ||
133 | + self.cmd_step1(step) | ||
134 | + def cmd_step4(self, step:int): | ||
135 | + self.cmd_step1(step) | ||
136 | + | ||
137 | + """ | ||
138 | + # @override | ||
139 | + def thread_exec_specific_cmd_step(self, step:int, sleep_time:float=1.0): | ||
140 | + self.thread_stop_if_asked() | ||
141 | + cmd = self._current_specific_cmd | ||
142 | + print(f">>>>> Thread (cmd {cmd.name}): step #{step}/5") | ||
143 | + self.sleep(sleep_time) | ||
144 | + """ | ||
145 | + | ||
146 | + ''' | ||
147 | + # @override | ||
148 | + def exec_specific_cmd_start(self, cmd:Command, from_thread=True): | ||
149 | + super().exec_specific_cmd_start(cmd, from_thread) | ||
150 | + ''' | ||
151 | + | ||
152 | + | ||
153 | + # @override | ||
154 | + def thread_exec_specific_cmd_main(self): | ||
155 | + # This is optional | ||
156 | + self.thread_set_total_steps_number(5) | ||
157 | + | ||
158 | + # HERE, write your own scenario | ||
159 | + | ||
160 | + # scenario OK | ||
161 | + self.thread_exec_specific_cmd_step(1, self.cmd_step1, 1) | ||
162 | + self.thread_exec_specific_cmd_step(2, self.cmd_step2, 3) | ||
163 | + self.thread_exec_specific_cmd_step(3, self.cmd_step1, 5) | ||
164 | + self.thread_exec_specific_cmd_step(4, self.cmd_step3, 10) | ||
165 | + self.thread_exec_specific_cmd_step(5, self.cmd_step1, 4) | ||
166 | + # ... as many as you need | ||
167 | + | ||
168 | + """ autre scenario | ||
169 | + self.thread_exec_specific_cmd_step(1, self.cmd_step1, 1) | ||
170 | + self.thread_exec_specific_cmd_step(2, self.cmd_step2, 2) | ||
171 | + self.thread_exec_specific_cmd_step(3, self.cmd_step1, 2) | ||
172 | + self.thread_exec_specific_cmd_step(4, self.cmd_step3, 2) | ||
173 | + self.thread_exec_specific_cmd_step(5, self.cmd_step1, 3) | ||
174 | + """ | ||
175 | + | ||
176 | + ''' | ||
177 | + # @override | ||
178 | + def exec_specific_cmd_end(self, cmd:Command, from_thread=True): | ||
179 | + super().exec_specific_cmd_end(cmd, from_thread) | ||
180 | + ''' | ||
181 | + | ||
182 | + # @override | ||
183 | + def simulator_test_results_main(self, commands): | ||
184 | + nb_asserted = 0 | ||
185 | + for cmd in commands: | ||
186 | + if cmd.name == "flush_commands": | ||
187 | + assert cmd.is_executed() | ||
188 | + nb_asserted+=1 | ||
189 | + # 2 times | ||
190 | + if cmd.name == "go_active": | ||
191 | + assert cmd.is_executed() | ||
192 | + nb_asserted+=1 | ||
193 | + # 2 times | ||
194 | + if cmd.name == "go_idle": | ||
195 | + assert cmd.is_executed() | ||
196 | + nb_asserted+=1 | ||
197 | + """ | ||
198 | + if cmd.name == "specific0": | ||
199 | + assert cmd.is_skipped() | ||
200 | + assert cmd.result == "in step #5/5" | ||
201 | + nb_asserted+=1 | ||
202 | + """ | ||
203 | + if cmd.name == "specific1": | ||
204 | + assert cmd.is_killed() | ||
205 | + nb_asserted+=1 | ||
206 | + if cmd.name == "specific2": | ||
207 | + assert cmd.is_executed() | ||
208 | + assert cmd.result == "in step #5/5" | ||
209 | + nb_asserted+=1 | ||
210 | + if cmd.name == "eval 4+3": | ||
211 | + assert cmd.is_executed() | ||
212 | + assert cmd.get_result() == "7" | ||
213 | + nb_asserted+=1 | ||
214 | + if cmd.name in ("abort"): | ||
215 | + assert cmd.is_executed() | ||
216 | + nb_asserted+=1 | ||
217 | + if cmd.name in ("exit"): | ||
218 | + assert cmd.is_executed() | ||
219 | + nb_asserted+=1 | ||
220 | + return nb_asserted | ||
221 | + | ||
222 | + | ||
223 | +""" | ||
224 | +================================================================= | ||
225 | + MAIN FUNCTION | ||
226 | +================================================================= | ||
227 | +""" | ||
228 | +if __name__ == "__main__": | ||
229 | + # with thread | ||
230 | + RUN_IN_THREAD=True | ||
231 | + # with process | ||
232 | + #RUN_IN_THREAD=False | ||
233 | + | ||
234 | + TEST_MODE, configfile = extract_parameters() | ||
235 | + """ | ||
236 | + configfile = None | ||
237 | + # arg 1 : config file | ||
238 | + if len(sys.argv) == 2: | ||
239 | + configfile = sys.argv[1] | ||
240 | + """ | ||
241 | + #agent = AgentX() | ||
242 | + agent = AgentTelescopeRequester("AgentTelescopeRequester", configfile, RUN_IN_THREAD) | ||
243 | + agent.setSimulatorMode(TEST_MODE) | ||
244 | + print(agent) | ||
245 | + agent.run() |
src/common/models.py
@@ -213,6 +213,20 @@ class Request(models.Model): | @@ -213,6 +213,20 @@ class Request(models.Model): | ||
213 | ------------------------ | 213 | ------------------------ |
214 | """ | 214 | """ |
215 | 215 | ||
216 | +class AgentDeviceTelescopeStatus(models.Model): | ||
217 | + created = models.DateTimeField('status date', blank=True, null=True, auto_now_add=True) | ||
218 | + radec = models.CharField('agent mode', max_length=30, blank=True) | ||
219 | + | ||
220 | + class Meta: | ||
221 | + managed = True | ||
222 | + db_table = 'agent_device_telescope_status' | ||
223 | + #verbose_name = "agent survey" | ||
224 | + #verbose_name_plural = "agents survey" | ||
225 | + | ||
226 | + """ | ||
227 | + def __str__(self): | ||
228 | + return (f"Agent {self.name} at {self.updated} in mode {self.mode} and status {self.status}") | ||
229 | + """ | ||
216 | 230 | ||
217 | class AgentSurvey(models.Model): | 231 | class AgentSurvey(models.Model): |
218 | """ | 232 | """ |