Commit fcfc62001ca075034b035ec620f14809ded9c69f
1 parent
1a15d30a
Exists in
dev
ajout nouvelle commande "pyros.py dbshell" (+ update doc)
Showing
7 changed files
with
98 additions
and
1031 deletions
Show diff stats
.gitignore
README.md
@@ -67,15 +67,14 @@ This software has been tested and validated with the following configurations : | @@ -67,15 +67,14 @@ This software has been tested and validated with the following configurations : | ||
67 | -------------------------------------------------------------------------------------------- | 67 | -------------------------------------------------------------------------------------------- |
68 | ## LAST VERSION | 68 | ## LAST VERSION |
69 | 69 | ||
70 | -Date: 28/02/2019 | 70 | +Date: 04/03/2019 |
71 | 71 | ||
72 | Author: E. Pallier | 72 | Author: E. Pallier |
73 | 73 | ||
74 | -VERSION: 0.20.3 | 74 | +VERSION: 0.20.4 |
75 | 75 | ||
76 | Comment: | 76 | Comment: |
77 | - Agent.py improvements : | ||
78 | - - read_db_commands() : ajout algo | 77 | + pyros.py : ajout nouvelle commande "dbshell" |
79 | 78 | ||
80 | 79 | ||
81 | -------------------------------------------------------------------------------------------- | 80 | -------------------------------------------------------------------------------------------- |
pyros.py
@@ -234,6 +234,21 @@ def shell(): | @@ -234,6 +234,21 @@ def shell(): | ||
234 | return True | 234 | return True |
235 | 235 | ||
236 | 236 | ||
237 | +@pyros_launcher.command(help="Run a database (Mysql) shell") | ||
238 | +def dbshell(): | ||
239 | + print() | ||
240 | + print("Launching a database (mysql) shell") | ||
241 | + print("From this shell, type 'use database pyros;' to select the pyros database") | ||
242 | + print("Then type 'show tables;' to see all the pyros tables") | ||
243 | + print("Then for example, type 'select * from config;' to see the content of the 'config' table") | ||
244 | + print("Type 'exit' to quit") | ||
245 | + print() | ||
246 | + # execProcess("python install.py install") | ||
247 | + if not test_mode(): execProcessFromVenv("src/manage.py dbshell") | ||
248 | + # Go back to the initial dir | ||
249 | + return True | ||
250 | + | ||
251 | + | ||
237 | @pyros_launcher.command(help="Install the pyros software") | 252 | @pyros_launcher.command(help="Install the pyros software") |
238 | #@global_test_options | 253 | #@global_test_options |
239 | def install(): | 254 | def install(): |
pyros_old.py deleted
@@ -1,994 +0,0 @@ | @@ -1,994 +0,0 @@ | ||
1 | -#!/usr/bin/env python3 | ||
2 | - | ||
3 | -import sys | ||
4 | -import os | ||
5 | -import subprocess | ||
6 | -import platform | ||
7 | -import fileinput | ||
8 | -import argparse | ||
9 | -import time | ||
10 | -import signal | ||
11 | - | ||
12 | - | ||
13 | -DEBUG = True | ||
14 | -SIMULATOR_CONFIG_FILE = "conf.json" #default config file | ||
15 | - | ||
16 | -''' | ||
17 | - Pyros Manager, able to launch processes and handle all project commands | ||
18 | -''' | ||
19 | - | ||
20 | - | ||
21 | -class Utils: | ||
22 | - system = platform.system() | ||
23 | - columns = 100 | ||
24 | - row = 1000 | ||
25 | - disp = True | ||
26 | - | ||
27 | - def __init__(self): | ||
28 | - if (platform.system() != 'Windows'): | ||
29 | - try: | ||
30 | - rows, columns = os.popen('stty size', 'r').read().split() | ||
31 | - self.columns = int(columns) | ||
32 | - except: | ||
33 | - self.columns = 100 | ||
34 | - if DEBUG: | ||
35 | - print("Could not get terminal size") | ||
36 | - | ||
37 | - def printFullTerm(self, color, string): | ||
38 | - value = int(self.columns / 2 - len(string) / 2) | ||
39 | - self.printColor(color, "-" * value, eol='') | ||
40 | - self.printColor(color, string, eol='') | ||
41 | - value += len(string) | ||
42 | - self.printColor(color, "-" * (self.columns - value)) | ||
43 | - return 0 | ||
44 | - | ||
45 | - def changeDirectory(self, path): | ||
46 | - if DEBUG: | ||
47 | - print("Moving to : " + path) | ||
48 | - os.chdir(path) | ||
49 | - if DEBUG: | ||
50 | - print("Current directory : " + str(os.getcwd())) | ||
51 | - return 0 | ||
52 | - | ||
53 | - def replacePatternInFile(self, pattern, replacement, file_path): | ||
54 | - #try: | ||
55 | - with fileinput.FileInput(file_path, inplace=True, backup='.bak') as file: | ||
56 | - for line in file: | ||
57 | - print(line.replace(pattern, replacement), end='') | ||
58 | - ''' | ||
59 | - except: | ||
60 | - return 1 | ||
61 | - return 0 | ||
62 | - ''' | ||
63 | - # Now, check that replacement has been done or else ERROR !!! | ||
64 | - FOUND=False | ||
65 | - #with fileinput.FileInput(file_path) as file: | ||
66 | - with open(file_path) as file: | ||
67 | - for line in file: | ||
68 | - #if replacement in line: | ||
69 | - if line.startswith(replacement): | ||
70 | - FOUND = True | ||
71 | - break | ||
72 | - #if FOUND: print("pattern "+pattern+" found in file "+file_path) | ||
73 | - if not FOUND: raise(Exception("pattern "+replacement+" not found in file "+file_path)) | ||
74 | - | ||
75 | - | ||
76 | - def printColor(self, color, message, file=sys.stdout, eol=os.linesep, forced=False): | ||
77 | - if (self.disp == False and forced == False): | ||
78 | - return 0 | ||
79 | - if (self.system == 'Windows'): | ||
80 | - print(message, file=file, end=eol) | ||
81 | - else: | ||
82 | - print(color + message + Colors.ENDC, file=file, end=eol) | ||
83 | - return 0 | ||
84 | - | ||
85 | - def askQuestion(self, message, default = ""): | ||
86 | - self.printColor(Colors.BLUE, message, forced=True) | ||
87 | - self.printColor(Colors.BOLD, "Answer (default="+default+"): ", eol='', forced=True) | ||
88 | - sys.stdout.flush() | ||
89 | - ret = sys.stdin.readline().replace('\n', '').replace('\r', '') | ||
90 | - if ret == "": | ||
91 | - return default | ||
92 | - return ret | ||
93 | - | ||
94 | - def sleep(self, t): | ||
95 | - time.sleep(t) | ||
96 | - return 0 | ||
97 | - | ||
98 | -''' | ||
99 | - Manager class : manager of your project | ||
100 | -''' | ||
101 | - | ||
102 | - | ||
103 | -class AManager(Utils): | ||
104 | - path = os.path.realpath(__file__) | ||
105 | - path_dir = os.getcwd() | ||
106 | - path_dir_file = os.path.dirname(os.path.realpath(__file__)) | ||
107 | - python_path = sys.executable | ||
108 | - python_version = sys.version_info | ||
109 | - | ||
110 | - bin_dir = "" | ||
111 | - venv_pip = "pip" | ||
112 | - venv_bin = "python" | ||
113 | - wait = True | ||
114 | - current_command = "" | ||
115 | - config = None | ||
116 | - commandMatcher = {} | ||
117 | - commandDescription = {} | ||
118 | - commands = [] | ||
119 | - errors = {} | ||
120 | - executed = {} | ||
121 | - subproc = [] | ||
122 | - | ||
123 | - def __init__(self, param): | ||
124 | - super(AManager, self).__init__() | ||
125 | - self.wait = param.getWait() | ||
126 | - self.commands = param.getCommandList() | ||
127 | - self.disp = param.getPrint() | ||
128 | - config = param.getConfig() | ||
129 | - self.config = config | ||
130 | - signal.signal(signal.SIGINT, self.signal_handler) | ||
131 | - | ||
132 | - self.changeDirectory(self.path_dir_file) | ||
133 | - | ||
134 | - if self.system == 'Windows': | ||
135 | - self.bin_dir = "Scripts" | ||
136 | - self.bin_name = "python.exe" | ||
137 | - self.pip_name = "pip.exe" | ||
138 | - else: | ||
139 | - self.bin_dir = "bin" | ||
140 | - self.bin_name = "python" | ||
141 | - self.pip_name = "pip" | ||
142 | - self.venv_pip = self.path_dir_file + os.sep + config["path"] + os.sep + config["env"] + os.sep + self.bin_dir + os.sep + self.pip_name | ||
143 | - self.venv_bin = self.path_dir_file + os.sep + config["path"] + os.sep + config["env"] + os.sep + self.bin_dir + os.sep + self.bin_name | ||
144 | - | ||
145 | - def help(self): | ||
146 | - print("This function must be implemented") | ||
147 | - raise(NotImplementedError("Function not implemented")) | ||
148 | - | ||
149 | - | ||
150 | - def set_simulator_mode_to(self, set_it:bool): | ||
151 | - file_path = "pyros/settings.py" | ||
152 | - ''' | ||
153 | - if simulator: self.replacePatternInFile("SIMULATOR = False", "SIMULATOR = True", file_path) | ||
154 | - ''' | ||
155 | - self.replacePatternInFile("SIMULATOR = "+str(not set_it), "SIMULATOR = "+str(set_it), file_path) | ||
156 | - | ||
157 | - | ||
158 | - def signal_handler(self, signal, frame): | ||
159 | - self.printFullTerm(Colors.WARNING, "Ctrl-c catched") | ||
160 | - self.set_simulator_mode_to(False) | ||
161 | - for p in self.subproc: | ||
162 | - proc, name = p | ||
163 | - self.printColor(Colors.BLUE, "Killing process " + str(name)) | ||
164 | - proc.kill() | ||
165 | - self.printFullTerm(Colors.WARNING, "Exiting") | ||
166 | - sys.exit(0) | ||
167 | - | ||
168 | - def addExecuted(self, src, message): | ||
169 | - if (src in self.executed): | ||
170 | - self.executed[src].append(str(message)) | ||
171 | - else: | ||
172 | - self.executed[src] = [str(message)] | ||
173 | - return 0 | ||
174 | - | ||
175 | - def addError(self, src, message): | ||
176 | - if (src in self.errors): | ||
177 | - self.errors[src].append(str(message)) | ||
178 | - else: | ||
179 | - self.errors[src] = [str(message)] | ||
180 | - return 0 | ||
181 | - | ||
182 | - def execProcess(self, command): | ||
183 | - self.printFullTerm(Colors.BLUE, "Executing command [" + command + "]") | ||
184 | - process = subprocess.Popen(command, shell=True) | ||
185 | - process.wait() | ||
186 | - if process.returncode == 0: | ||
187 | - self.printFullTerm(Colors.GREEN, "Process executed successfully") | ||
188 | - self.addExecuted(self.current_command, command) | ||
189 | - else: | ||
190 | - self.printFullTerm(Colors.WARNING, "Process execution failed") | ||
191 | - self.addError(self.current_command, command) | ||
192 | - return process.returncode | ||
193 | - | ||
194 | - def execProcessSilent(self, command): | ||
195 | - process = subprocess.Popen(command, shell=True) | ||
196 | - process.wait() | ||
197 | - return process.returncode | ||
198 | - | ||
199 | - def execProcessFromVenv(self, command): | ||
200 | - args = command.split() | ||
201 | - self.printFullTerm(Colors.BLUE, "Executing command from venv [" + str(' '.join(args[1:])) + "]") | ||
202 | - process = subprocess.Popen(args) | ||
203 | - process.wait() | ||
204 | - if process.returncode == 0: | ||
205 | - self.printFullTerm(Colors.GREEN, "Process executed successfully") | ||
206 | - self.addExecuted(self.current_command, str(' '.join(args[1:]))) | ||
207 | - else: | ||
208 | - self.printFullTerm(Colors.WARNING, "Process execution failed") | ||
209 | - self.addError(self.current_command, str(' '.join(args[1:]))) | ||
210 | - return process.returncode | ||
211 | - | ||
212 | - def execProcessAsync(self, command): | ||
213 | - self.printFullTerm(Colors.BLUE, "Executing command [" + command + "]") | ||
214 | - p = subprocess.Popen(command, shell=True) | ||
215 | - self.subproc.append((p, command)) | ||
216 | - self.printFullTerm(Colors.GREEN, "Process launched successfully") | ||
217 | - self.addExecuted(self.current_command, command) | ||
218 | - return p | ||
219 | - | ||
220 | - def execProcessFromVenvAsync(self, command: str): | ||
221 | - args = command.split() | ||
222 | - self.printFullTerm(Colors.BLUE, "Executing command from venv [" + str(' '.join(args[1:])) + "]") | ||
223 | - p = subprocess.Popen(args) | ||
224 | - self.subproc.append((p, ' '.join(args[1:]))) | ||
225 | - self.printFullTerm(Colors.GREEN, "Process launched successfully") | ||
226 | - self.addExecuted(self.current_command, str(' '.join(args[1:]))) | ||
227 | - return p | ||
228 | - | ||
229 | - def waitProcesses(self): | ||
230 | - if (self.wait): | ||
231 | - for p in self.subproc: | ||
232 | - proc, name = p | ||
233 | - proc.wait() | ||
234 | - return 0 | ||
235 | - | ||
236 | - def end(self): | ||
237 | - count = 0 | ||
238 | - self.waitProcesses() | ||
239 | - self.printFullTerm(Colors.WARNING, "Summary") | ||
240 | - self.printColor(Colors.GREEN, "Success : ") | ||
241 | - for command, valid in self.executed.items(): | ||
242 | - if not valid: | ||
243 | - self.printColor(Colors.BLUE, "\t- Command : " + command + " success !") | ||
244 | - else: | ||
245 | - self.printColor(Colors.WARNING, "\t- In commmand : " + command) | ||
246 | - for exe in valid: | ||
247 | - self.printColor(Colors.GREEN, "\t\t - Command : " + exe + " success !") | ||
248 | - self.printColor(Colors.FAIL, "Errors : ") | ||
249 | - if not self.errors: | ||
250 | - self.printColor(Colors.GREEN, "\tNone") | ||
251 | - for command, items in self.errors.items(): | ||
252 | - count += 1 | ||
253 | - if (not items): | ||
254 | - self.printColor(Colors.FAIL, "Command : " + command + " failed !") | ||
255 | - else: | ||
256 | - self.printColor(Colors.WARNING, "\t- In commmand : " + command) | ||
257 | - for exe in items: | ||
258 | - self.printColor(Colors.FAIL, "\t\t - Command : " + exe) | ||
259 | - return count | ||
260 | - | ||
261 | - def exec(self): | ||
262 | - if (not self.commands): | ||
263 | - self.commandMatcher["help"]() | ||
264 | - return 0 | ||
265 | - for command in self.commands: | ||
266 | - self.current_command = command | ||
267 | - if command in self.commandMatcher: | ||
268 | - self.commandMatcher[command]() | ||
269 | - else: | ||
270 | - self.addError(str(command), "invalid command") | ||
271 | - return self.end() | ||
272 | - | ||
273 | - def logError(self, message): | ||
274 | - self.printColor(Colors.FAIL, "Pyros : An error occurred [" + message + "]", file=sys.stderr) | ||
275 | - return 0 | ||
276 | - | ||
277 | -''' | ||
278 | - Config file representation (able to parse and give informations) | ||
279 | -''' | ||
280 | - | ||
281 | - | ||
282 | -class Config: | ||
283 | - __parser = argparse.ArgumentParser("Project Pyros") | ||
284 | - __content = { | ||
285 | - "path": "private", | ||
286 | - "env": "venv_py3_pyros" | ||
287 | - } | ||
288 | - __wait = True | ||
289 | - __print = True | ||
290 | - usage = "" | ||
291 | - __command_list = [] | ||
292 | - | ||
293 | - def __init__(self): | ||
294 | - self.__parser.add_argument("command", nargs='*', default="help", help="The command you want to execute (default=help)") | ||
295 | - self.__parser.add_argument("--env", help="Your environment directory name default=venv") | ||
296 | - self.__parser.add_argument("--path", help="Path to the virtual env (from the source file directory) (default=private)") | ||
297 | - self.__parser.add_argument("--nowait", action='store_true', help="Don't wait the end of a program") | ||
298 | - self.__parser.add_argument("--noprint", action='store_true', help="Won't print") | ||
299 | - self.usage = self.__parser.format_usage() | ||
300 | - | ||
301 | - def parse(self): | ||
302 | - try: | ||
303 | - res = self.__parser.parse_args(), self.__parser.format_usage() | ||
304 | - return (res) | ||
305 | - except SystemExit as e: | ||
306 | - # print(e, file=sys.stderr) | ||
307 | - sys.exit(1) | ||
308 | - | ||
309 | - def parseConf(self): | ||
310 | - res, usage = self.parse() | ||
311 | - try: | ||
312 | - if (res.env): | ||
313 | - self.__content["env"] = res.env | ||
314 | - if (res.path): | ||
315 | - self.__content["path"] = res.path | ||
316 | - if (res.nowait): | ||
317 | - self.__wait = False | ||
318 | - if (res.noprint): | ||
319 | - self.__print = False | ||
320 | - self.__command_list.append(res.command[0]) | ||
321 | - #return | ||
322 | - return True | ||
323 | - except Exception as e: | ||
324 | - print(e, file=sys.stderr) | ||
325 | - #return 1 | ||
326 | - return False | ||
327 | - | ||
328 | - def getPath(self): | ||
329 | - return self.__content["path"] | ||
330 | - | ||
331 | - def getEnv(self): | ||
332 | - return self.__content["env"] | ||
333 | - | ||
334 | - def getWait(self): | ||
335 | - return self.__wait | ||
336 | - | ||
337 | - def getPrint(self): | ||
338 | - return self.__print | ||
339 | - | ||
340 | - def setPrint(self, value): | ||
341 | - self.__print = value | ||
342 | - return 0 | ||
343 | - | ||
344 | - def setWait(self, value): | ||
345 | - self.__wait = value | ||
346 | - return 0 | ||
347 | - | ||
348 | - def getCommandList(self): | ||
349 | - return self.__command_list | ||
350 | - | ||
351 | - def printUsage(self): | ||
352 | - print(self.usage, file=sys.stderr) | ||
353 | - | ||
354 | - def addConf(self, key, value): | ||
355 | - if isinstance(key, str) and isinstance(value, str): | ||
356 | - self.__content[key] = value | ||
357 | - return 0 | ||
358 | - return 1 | ||
359 | - | ||
360 | - def setPath(self, path): | ||
361 | - if (os.path.isdir(path)): | ||
362 | - if (path == ""): | ||
363 | - path = "." | ||
364 | - self.__content["path"] = path | ||
365 | - return 0 | ||
366 | - return 1 | ||
367 | - | ||
368 | - def setEnvName(self, name): | ||
369 | - self.__content["env"] = name | ||
370 | - return 0 | ||
371 | - | ||
372 | - def getConfig(self): | ||
373 | - return self.__content | ||
374 | - | ||
375 | - | ||
376 | -''' | ||
377 | - Color class | ||
378 | -''' | ||
379 | - | ||
380 | - | ||
381 | -class Colors: | ||
382 | - HEADER = '\033[95m' | ||
383 | - BLUE = '\033[94m' | ||
384 | - GREEN = '\033[92m' | ||
385 | - WARNING = '\033[93m' | ||
386 | - FAIL = '\033[91m' | ||
387 | - ENDC = '\033[0m' | ||
388 | - BOLD = '\033[1m' | ||
389 | - UNDERLINE = '\033[4m' | ||
390 | - | ||
391 | - | ||
392 | -''' | ||
393 | - Pyros class | ||
394 | -''' | ||
395 | - | ||
396 | - | ||
397 | -class Pyros(AManager): | ||
398 | - help_message = "python neo.py" | ||
399 | - init_fixture = "initial_fixture.json" | ||
400 | - | ||
401 | - def signal_handler(self, signal, frame): | ||
402 | - self.printFullTerm(Colors.WARNING, "Ctrl-c catched") | ||
403 | - for p in self.subproc: | ||
404 | - proc, name = p | ||
405 | - self.printColor(Colors.BLUE, "Killing process " + str(name)) | ||
406 | - proc.kill() | ||
407 | - if self.current_command == "simulator" or self.current_command == "simulator_development": | ||
408 | - self.changeDirectory(self.path_dir_file) | ||
409 | - self.kill_simulation() | ||
410 | - self.printFullTerm(Colors.WARNING, "Exiting") | ||
411 | - sys.exit(0) | ||
412 | - | ||
413 | - def install(self): | ||
414 | - if (self.system == "Windows"): | ||
415 | - self.execProcess("python install/install.py install") | ||
416 | - else: | ||
417 | - self.execProcess("python3 install/install.py install") | ||
418 | - return 0 | ||
419 | - | ||
420 | - def update(self): | ||
421 | - if (self.system == "Windows"): | ||
422 | - self.execProcess("python install/install.py update") | ||
423 | - else: | ||
424 | - self.execProcess("python3 install/install.py update") | ||
425 | - return 0 | ||
426 | - | ||
427 | - def server(self): | ||
428 | - self.changeDirectory("src") | ||
429 | - self.execProcessFromVenvAsync(self.venv_bin + " manage.py runserver") | ||
430 | - self.changeDirectory("..") | ||
431 | - return 0 | ||
432 | - | ||
433 | - def clean(self): | ||
434 | - return self.clean_logs() | ||
435 | - | ||
436 | - def clean_logs(self): | ||
437 | - return self.execProcess("rm logs/*.log") | ||
438 | - | ||
439 | - def test(self): | ||
440 | - self.changeDirectory("src") | ||
441 | - ##self.execProcessFromVenv(self.venv_bin + " manage.py test") | ||
442 | - self.execProcessFromVenvAsync(self.venv_bin + " manage.py test") | ||
443 | - self.changeDirectory("..") | ||
444 | - return 0 | ||
445 | - | ||
446 | - def migrate(self): | ||
447 | - self.changeDirectory("src") | ||
448 | - self.execProcessFromVenv(self.venv_bin + " manage.py migrate") | ||
449 | - self.changeDirectory("..") | ||
450 | - return 0 | ||
451 | - | ||
452 | - def makemigrations(self): | ||
453 | - self.changeDirectory("src") | ||
454 | - self.execProcessFromVenv(self.venv_bin + " manage.py makemigrations") | ||
455 | - self.changeDirectory("..") | ||
456 | - return 0 | ||
457 | - | ||
458 | - def help(self): | ||
459 | - count = 0 | ||
460 | - self.printFullTerm(Colors.WARNING, "Help Message") | ||
461 | - for command, message in self.commandDescription.items(): | ||
462 | - count += 1 | ||
463 | - if (self.columns > 100): | ||
464 | - self.printColor(Colors.BLUE, "\t"+str(count)+(' ' if count < 10 else '')+": " + command + ": ", eol='') | ||
465 | - else: | ||
466 | - self.printColor(Colors.BLUE, "-> " + command + ": ", eol='') | ||
467 | - self.printColor(Colors.GREEN, message) | ||
468 | - return 0 | ||
469 | - | ||
470 | - def updatedb(self): | ||
471 | - self.makemigrations() | ||
472 | - self.migrate() | ||
473 | - return 0 | ||
474 | - | ||
475 | - def reset_config(self): | ||
476 | - self.changeDirectory("src") | ||
477 | - self.set_simulator_mode_to(False) | ||
478 | - self.addExecuted(self.current_command, "reset configuration") | ||
479 | - self.changeDirectory("..") | ||
480 | - return 0 | ||
481 | - | ||
482 | - def unittest(self): | ||
483 | - #self.changeDirectory("src") | ||
484 | - apps = ['common', 'scheduler', 'routine_manager', 'user_manager', 'alert_manager.tests.TestStrategyChange'] | ||
485 | - for app in apps: | ||
486 | - self.loaddata() | ||
487 | - self.changeDirectory("src") | ||
488 | - self.execProcessFromVenv(self.venv_bin + ' manage.py test ' + app) | ||
489 | - self.changeDirectory("..") | ||
490 | - return 0 | ||
491 | - | ||
492 | - def test_all(self): | ||
493 | - self.unittest() | ||
494 | - return 0 | ||
495 | - | ||
496 | - #TODO: mettre la fixture en date naive (sans time zone) | ||
497 | - def loaddata(self): | ||
498 | - self.changeDirectory("src") | ||
499 | - self.execProcessFromVenv(self.venv_bin + " manage.py loaddata misc" + os.sep + "fixtures" + os.sep + self.init_fixture) | ||
500 | - self.changeDirectory("..") | ||
501 | - return 0 | ||
502 | - | ||
503 | - | ||
504 | - def init_database(self): | ||
505 | - self.makemigrations() | ||
506 | - self.migrate() | ||
507 | - self.loaddata() | ||
508 | - return 0 | ||
509 | - | ||
510 | - | ||
511 | - # Start the PyROS software | ||
512 | - | ||
513 | - # Start only the (django) Web server (pyros website) | ||
514 | - def start_web(self): | ||
515 | - self.start("web") | ||
516 | - #self.start(3) | ||
517 | - | ||
518 | - # Start only the agents (all of them) | ||
519 | - def start_agents(self): | ||
520 | - self.start("agents") | ||
521 | - | ||
522 | - # Start only 1 agent | ||
523 | - def start_agent_monitoring(self): | ||
524 | - self.start("monitoring") | ||
525 | - def start_agent_majordome(self): | ||
526 | - self.start("majordome") | ||
527 | - def start_agent_alertmanager(self): | ||
528 | - self.start("alert_manager") | ||
529 | - | ||
530 | - # Start the PyROS software | ||
531 | - # By default (what=None), start everything : all the agents and also the web server (website, web viewing, for global monitoring) | ||
532 | - def start(self, what:str = None): | ||
533 | - #agents = ("alert_manager", "majordome", "monitoring") | ||
534 | - agents = ("majordome", "monitoring") | ||
535 | - ''' | ||
536 | - what="monitoring" | ||
537 | - what="majordome" | ||
538 | - what="alert_manager" | ||
539 | - what="all" | ||
540 | - ''' | ||
541 | - | ||
542 | - # Go into src/ | ||
543 | - self.changeDirectory("src") | ||
544 | - #print("Current directory : " + str(os.getcwd())) | ||
545 | - | ||
546 | - # Start (Django) Web server (pyros website) | ||
547 | - if what=='web' or what is None: | ||
548 | - self.execProcessFromVenvAsync(self.venv_bin + " manage.py runserver") | ||
549 | - #self.changeDirectory("..") | ||
550 | - | ||
551 | - # DJANGO setup | ||
552 | - ''' (optional) | ||
553 | - import sys | ||
554 | - sys.path.append('/PROJECTS/GFT/SOFT/PYROS_SOFT/PYROS201802/src') | ||
555 | - ''' | ||
556 | - ''' | ||
557 | - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "src.pyros.settings") | ||
558 | - #os.environ['SECRET_KEY'] = 'abc' | ||
559 | - #os.environ['ENVIRONMENT'] = 'production' | ||
560 | - import django | ||
561 | - django.setup() | ||
562 | - ''' | ||
563 | - | ||
564 | - # Start Agents | ||
565 | - ''' | ||
566 | - if agent=="majordome" or agent=="all": | ||
567 | - from majordome.tasks import Majordome | ||
568 | - Majordome().run() | ||
569 | - if agent=="alert_manager" or agent=="all": | ||
570 | - from alert_manager.tasks import AlertListener | ||
571 | - AlertListener().run() | ||
572 | - ''' | ||
573 | - for agent in agents: | ||
574 | - if what==agent or what=="agents" or what is None: | ||
575 | - #os.chdir('src') | ||
576 | - ''' | ||
577 | - from monitoring.tasks import Monitoring | ||
578 | - Monitoring().run() | ||
579 | - ''' | ||
580 | - #self.changeDirectory("src/monitoring") | ||
581 | - #self.changeDirectory("src") | ||
582 | - self.changeDirectory(agent) | ||
583 | - self.execProcessFromVenvAsync(self.venv_bin + ' start_agent_'+agent+'.py') | ||
584 | - # Go back to src/ | ||
585 | - self.changeDirectory('..') | ||
586 | - #os.chdir('..') | ||
587 | - # Go back to root folder (/) | ||
588 | - self.changeDirectory('..') | ||
589 | - | ||
590 | - | ||
591 | - | ||
592 | - # Reset the database content | ||
593 | - def reset_database_sim(self): | ||
594 | - self.changeDirectory("src") | ||
595 | - self.set_simulator_mode_to(True, False) | ||
596 | - ''' | ||
597 | - Supprime toutes les données de la base de données | ||
598 | - et réexécute tout gestionnaire de post-synchronisation. | ||
599 | - La table contenant les informations d’application des migrations n’est pas effacée | ||
600 | - ''' | ||
601 | - #TODO: remplacer par manage.py --noinput flush pour eviter le "echo yes"... | ||
602 | - self.execProcess( self.venv_bin + " manage.py flush --noinput") | ||
603 | - self.set_simulator_mode_to(True, False) | ||
604 | - self.changeDirectory("..") | ||
605 | - | ||
606 | - | ||
607 | - # Simulation for the scheduler ONLY | ||
608 | - def simulator_development(self): | ||
609 | - self.simulator(False) | ||
610 | - | ||
611 | - ''' | ||
612 | - (EP) utile pour le debug avec phpmyadmin pour remettre à zéro la bd pyros_test | ||
613 | - delete from plan; | ||
614 | - delete from album; | ||
615 | - delete from schedule_has_sequences; | ||
616 | - delete from schedule; | ||
617 | - delete from sequence; | ||
618 | - delete from request; | ||
619 | - ''' | ||
620 | - | ||
621 | - # Simulation (by default, with ALL simulators) | ||
622 | - def simulator(self, TOTAL=True): | ||
623 | - self.changeDirectory("src") | ||
624 | - | ||
625 | - # Set SIMULATOR mode ON | ||
626 | - self.replacePatternInFile("SIMULATOR = False", "SIMULATOR = True", "pyros/settings.py") | ||
627 | - | ||
628 | - # 0) Reset the database (Empty the database from any data) | ||
629 | - #TODO: je crois qu'on n'utilise plus sqlite... donc ce code est inutile ? | ||
630 | - if self.system == "Windows" : self.execProcess("del /f testdb.sqlite3") | ||
631 | - else : self.execProcess("rm -f testdb.sqlite3") | ||
632 | - self.changeDirectory("..") | ||
633 | - if TOTAL: self.reset_database_sim() | ||
634 | - # EP added: | ||
635 | - if not TOTAL: self.reset_database_sim() | ||
636 | - # Actualise l’état de la DB en accord avec l’ensemble des modèles et des migrations actuels | ||
637 | - self.makemigrations() | ||
638 | - self.migrate() | ||
639 | - # Load fixture initial_fixture.json | ||
640 | - self.loaddata() | ||
641 | - | ||
642 | - # | ||
643 | - # 1) Launch web server | ||
644 | - # | ||
645 | - self.server() | ||
646 | - self.sleep(2) | ||
647 | - | ||
648 | - self.printFullTerm(Colors.WARNING, "SUMMARY") | ||
649 | - self.printColor(Colors.GREEN, "The simulator has been successfully initialised") | ||
650 | - #TODO: (EP) verifier mais je pense que cette info est obsolete, c'est MySQL qui est utilisé, pas sqlite | ||
651 | - #self.printColor(Colors.GREEN, "The simulator runs on a temp database : src/testdb.sqlite3") | ||
652 | - self.printColor(Colors.GREEN, "The simulation will be ended by the task 'simulator herself'") | ||
653 | - self.printColor(Colors.GREEN, "If you want to shutdown the simulation, please run :") | ||
654 | - self.printColor(Colors.GREEN, "CTRL-C or pyros.py kill_simulation") | ||
655 | - self.printColor(Colors.GREEN, "If the simulation isn't correctly killed, please switch the variable") | ||
656 | - self.printColor(Colors.GREEN, "SIMULATOR in src/pyros/settings.py to False") | ||
657 | - self.printFullTerm(Colors.WARNING, "SUMMARY") | ||
658 | - | ||
659 | - # | ||
660 | - # 2) Start simulator(s) : | ||
661 | - # | ||
662 | - self.sims_launch(TOTAL) | ||
663 | - | ||
664 | - ''' | ||
665 | - # (DEFAULT NORMAL) TOTAL RUN | ||
666 | - # - Launch ALL simulators | ||
667 | - if TOTAL: | ||
668 | - # Launch ALL simulators and WAIT until finished: | ||
669 | - #self.sims_launch(conf) | ||
670 | - self.sims_launch(TOTAL) | ||
671 | - | ||
672 | - # (DEV) PARTIAL RUN | ||
673 | - # - Launch only USER simulator | ||
674 | - else: | ||
675 | - # EP: bugfix: inutile si TOTAL, car refait dans sims_launch !!! | ||
676 | - self.changeDirectory("simulators/config") | ||
677 | - self.printColor(Colors.BOLD, "Existing simulations : ", eol='') | ||
678 | - sys.stdout.flush() | ||
679 | - if (self.system == "Windows") : self.execProcessSilent("dir /B conf*.json") | ||
680 | - else: self.execProcessSilent("ls conf*.json") | ||
681 | - self.changeDirectory("..") | ||
682 | - conf = self.askQuestion("Which simulation do you want to use", default="conf.json") | ||
683 | - self.changeDirectory("..") | ||
684 | - | ||
685 | - procs = [] | ||
686 | - self.changeDirectory("simulators") | ||
687 | - self.changeDirectory("user") | ||
688 | - # Launch only 1 simulator : UserSimulator | ||
689 | - procs.append(self.execProcessFromVenvAsync(self.venv_bin + " userSimulator.py " + conf)) | ||
690 | - self.changeDirectory("..") | ||
691 | - for p in procs: | ||
692 | - p.wait() | ||
693 | - self.changeDirectory("..") | ||
694 | - ''' | ||
695 | - | ||
696 | - # When simulators are finished: | ||
697 | - #self.kill_simulation() | ||
698 | - return 0 | ||
699 | - | ||
700 | - | ||
701 | - | ||
702 | - | ||
703 | - # Test of the Majordome agent | ||
704 | - def test_majordome(self): | ||
705 | - self.changeDirectory("src") | ||
706 | - | ||
707 | - # Set SIMULATOR mode ON | ||
708 | - self.set_simulator_mode_to(True) | ||
709 | - | ||
710 | - # 0) Reset the database (Empty the database from any data) | ||
711 | - self.changeDirectory("..") | ||
712 | - self.reset_database_sim() | ||
713 | - # Actualise l’état de la DB en accord avec l’ensemble des modèles et des migrations actuels | ||
714 | - self.migrate() | ||
715 | - # Load fixture initial_fixture.json | ||
716 | - self.loaddata() | ||
717 | - return | ||
718 | - | ||
719 | - # | ||
720 | - # 1) Launch Django web server | ||
721 | - # | ||
722 | - ''' | ||
723 | - self.server() | ||
724 | - self.sleep(2) | ||
725 | - ''' | ||
726 | - | ||
727 | - self.printFullTerm(Colors.WARNING, "SUMMARY") | ||
728 | - self.printColor(Colors.GREEN, "The simulation will be ended by the task 'simulator herself'") | ||
729 | - self.printColor(Colors.GREEN, "If you want to shutdown the simulation, please run :") | ||
730 | - self.printColor(Colors.GREEN, "CTRL-C or pyros.py kill_simulation") | ||
731 | - self.printColor(Colors.GREEN, "If the simulation isn't correctly killed, please switch the variable") | ||
732 | - self.printColor(Colors.GREEN, "SIMULATOR in src/pyros/settings.py to False") | ||
733 | - self.printFullTerm(Colors.WARNING, "SUMMARY") | ||
734 | - | ||
735 | - # | ||
736 | - # 2) Start simulator(s) : | ||
737 | - # | ||
738 | - #self.sims_launch(True) | ||
739 | - agent='majordome' | ||
740 | - self.changeDirectory(agent) | ||
741 | - p = self.execProcessFromVenvAsync(self.venv_bin + ' start_agent_'+agent+'.py') | ||
742 | - os.chdir('..') | ||
743 | - time.sleep(5) | ||
744 | - | ||
745 | - | ||
746 | - # Wait for end of simulators : | ||
747 | - #p.wait() | ||
748 | - | ||
749 | - # When simulators are finished: | ||
750 | - #self.kill_simulation() | ||
751 | - #self.changeDirectory("src") | ||
752 | - self.set_simulator_mode_to(False) | ||
753 | - self.changeDirectory("..") | ||
754 | - self.execProcessAsync("ps aux | grep \"start_agent_majordome.py\" | awk '{ print $2 }' | xargs kill") | ||
755 | - | ||
756 | - return 0 | ||
757 | - | ||
758 | - | ||
759 | - | ||
760 | - | ||
761 | - | ||
762 | - | ||
763 | - | ||
764 | - def kill_server(self): | ||
765 | - self.execProcessAsync("fuser -k 8000/tcp") | ||
766 | - return 0 | ||
767 | - | ||
768 | - def kill_simulation(self): | ||
769 | - self.changeDirectory("src") | ||
770 | - self.set_simulator_mode_to(False) | ||
771 | - self.changeDirectory("..") | ||
772 | - if (self.system == "Windows"): | ||
773 | - self.execProcessAsync("taskkill /f /im python.exe") | ||
774 | - #self.execProcessAsync("del /f testdb.sqlite3") obsolete | ||
775 | - #self.changeDirectory("..") POURQUOI ? | ||
776 | - return 0 | ||
777 | - #else: self.execProcessAsync("rm -f testdb.sqlite3") | ||
778 | - | ||
779 | - # Kill web server (processes using TCP port 8000) : | ||
780 | - #self.execProcessAsync("fuser -k 8000/tcp") | ||
781 | - | ||
782 | - self.kill_server() | ||
783 | - | ||
784 | - # Kill all agents : | ||
785 | - self.execProcessAsync("ps aux | grep \"start_agent_alert_manager.py\" | awk '{ print $2 }' | xargs kill") | ||
786 | - self.execProcessAsync("ps aux | grep \"start_agent_monitoring.py\" | awk '{ print $2 }' | xargs kill") | ||
787 | - self.execProcessAsync("ps aux | grep \"start_agent_majordome.py\" | awk '{ print $2 }' | xargs kill") | ||
788 | - #self.execProcessAsync("ps aux | grep \"start_agent_majordome.py\" | awk '{ print $2 }' | xargs kill") | ||
789 | - | ||
790 | - # Kill all simulators : | ||
791 | - self.execProcessAsync("ps aux | grep \" domeSimulator.py\" | awk '{ print $2 }' | xargs kill") | ||
792 | - self.execProcessAsync("ps aux | grep \" userSimulator.py\" | awk '{ print $2 }' | xargs kill") | ||
793 | - self.execProcessAsync("ps aux | grep \" alertSimulator.py\" | awk '{ print $2 }' | xargs kill") | ||
794 | - self.execProcessAsync("ps aux | grep \" plcSimulator.py\" | awk '{ print $2 }' | xargs kill") | ||
795 | - self.execProcessAsync("ps aux | grep \" telescopeSimulator.py\" | awk '{ print $2 }' | xargs kill") | ||
796 | - self.execProcessAsync("ps aux | grep \" cameraNIRSimulator.py\" | awk '{ print $2 }' | xargs kill") | ||
797 | - self.execProcessAsync("ps aux | grep \" cameraVISSimulator.py\" | awk '{ print $2 }' | xargs kill") | ||
798 | - self.changeDirectory("..") | ||
799 | - | ||
800 | - self.printFullTerm(Colors.GREEN, "simulation ended") | ||
801 | - return 0 | ||
802 | - | ||
803 | - | ||
804 | - | ||
805 | - # EP 25/7/18 | ||
806 | - # Start one, some, or all agents and simulators | ||
807 | - | ||
808 | - def start_agents_and_simulators_for_majordome(self): | ||
809 | - self.start_agents_and_simulators("majordome") | ||
810 | - | ||
811 | - def start_agents_and_simulators(self, agent:str=None): | ||
812 | - | ||
813 | - simulators = ('user', 'plc', 'dome', 'alert', 'cameraVIS', 'cameraNIR', 'telescope') | ||
814 | - #simulators = ('plc', 'user') | ||
815 | - #simulators = () | ||
816 | - | ||
817 | - agents = ("alert_manager", "majordome", "monitoring") | ||
818 | - | ||
819 | - if agent=="majordome": | ||
820 | - simulators = ("plc",) | ||
821 | - agents = (agent, "monitoring") | ||
822 | - | ||
823 | - # Start simulators | ||
824 | - p_sims = [] | ||
825 | - self.changeDirectory("simulators/") | ||
826 | - for simulator in simulators: | ||
827 | - print() | ||
828 | - print("Start simulator", ' '+simulator+"Simulator.py ") | ||
829 | - self.changeDirectory(simulator) | ||
830 | - #p_sims.append( self.execProcessFromVenvAsync(self.venv_bin + ' '+simulator+"Simulator.py " + conf) ) | ||
831 | - p_sims.append( self.execProcessFromVenvAsync(self.venv_bin + ' '+simulator+"Simulator.py ") ) | ||
832 | - self.changeDirectory("../") | ||
833 | - | ||
834 | - # Start agents | ||
835 | - #self.start_agents() | ||
836 | - p_agents = [] | ||
837 | - self.changeDirectory("../src/") | ||
838 | - for agent in agents: | ||
839 | - print() | ||
840 | - print("Start agent", 'start_agent_'+agent+'.py') | ||
841 | - self.changeDirectory(agent) | ||
842 | - p_agents.append( self.execProcessFromVenvAsync(self.venv_bin + ' start_agent_'+agent+'.py') ) | ||
843 | - self.changeDirectory('..') | ||
844 | - | ||
845 | - | ||
846 | - | ||
847 | - # Simulators only | ||
848 | - def sims_launch(self, TOTAL=True): | ||
849 | - conf = SIMULATOR_CONFIG_FILE | ||
850 | - # Read simulators scenario file | ||
851 | - self.changeDirectory("simulators/config") | ||
852 | - # if (conf == ""): | ||
853 | - # self.printColor(Colors.BOLD, "Existing simulations : ", eol='') | ||
854 | - # sys.stdout.flush() | ||
855 | - # if (self.system == "Windows"): | ||
856 | - # self.execProcessSilent("dir /B conf*.json") | ||
857 | - # else: | ||
858 | - # self.execProcessSilent("ls conf*.json") | ||
859 | - # conf = self.askQuestion("Which simulation do you want to use ?", default="conf.json") | ||
860 | - if not os.path.isfile(conf): | ||
861 | - self.printColor(Colors.FAIL, "The simulation file " + conf + " does not exist") | ||
862 | - return 1 | ||
863 | - # Go back to simulators/ | ||
864 | - self.changeDirectory("..") | ||
865 | - | ||
866 | - procs = [] | ||
867 | - | ||
868 | - # Launch the User simulator: | ||
869 | - self.changeDirectory("user") | ||
870 | - procs.append(self.execProcessFromVenvAsync(self.venv_bin + " userSimulator.py " + conf)) | ||
871 | - self.changeDirectory("..") | ||
872 | - | ||
873 | - # (if TOTAL) Launch all other simulators | ||
874 | - if TOTAL: | ||
875 | - | ||
876 | - self.changeDirectory("dome") | ||
877 | - procs.append(self.execProcessFromVenvAsync(self.venv_bin + " domeSimulator.py " + conf)) | ||
878 | - self.changeDirectory("..") | ||
879 | - | ||
880 | - self.changeDirectory("alert") | ||
881 | - procs.append(self.execProcessFromVenvAsync(self.venv_bin + " alertSimulator.py " + conf)) | ||
882 | - self.changeDirectory("..") | ||
883 | - | ||
884 | - self.changeDirectory("plc") | ||
885 | - procs.append(self.execProcessFromVenvAsync(self.venv_bin + " plcSimulator.py " + conf)) | ||
886 | - self.changeDirectory("..") | ||
887 | - | ||
888 | - self.changeDirectory("camera") | ||
889 | - procs.append(self.execProcessFromVenvAsync(self.venv_bin + " cameraVISSimulator.py " + conf)) | ||
890 | - procs.append(self.execProcessFromVenvAsync(self.venv_bin + " cameraNIRSimulator.py " + conf)) | ||
891 | - self.changeDirectory("..") | ||
892 | - | ||
893 | - self.changeDirectory("telescope") | ||
894 | - procs.append(self.execProcessFromVenvAsync(self.venv_bin + " telescopeSimulator.py " + conf)) | ||
895 | - self.changeDirectory("..") | ||
896 | - | ||
897 | - # Get back to project root folder | ||
898 | - self.changeDirectory("..") | ||
899 | - | ||
900 | - # Launch agents (env monitor, major, alert mgr) | ||
901 | - self.start_agents() | ||
902 | - | ||
903 | - # Wait for end of simulators : | ||
904 | - for p in procs: p.wait() | ||
905 | - | ||
906 | - # Kill all processes | ||
907 | - self.kill_simulation() | ||
908 | - return 0 | ||
909 | - | ||
910 | - | ||
911 | - def mysql_on(self): | ||
912 | - self.changeDirectory("src") | ||
913 | - self.replacePatternInFile("MYSQL = False", "MYSQL = True", "pyros/settings.py") | ||
914 | - self.addExecuted(self.current_command, "Switch to mysql") | ||
915 | - self.changeDirectory("..") | ||
916 | - return 0 | ||
917 | - | ||
918 | - def mysql_off(self): | ||
919 | - self.changeDirectory("src") | ||
920 | - self.replacePatternInFile("MYSQL = True", "MYSQL = False", "pyros/settings.py") | ||
921 | - self.addExecuted(self.current_command, "Switch to sqlite") | ||
922 | - self.changeDirectory("..") | ||
923 | - return 0 | ||
924 | - | ||
925 | - def __init__(self, argv): | ||
926 | - super(Pyros, self).__init__(argv) | ||
927 | - self.commandMatcher = { | ||
928 | - "install": self.install, | ||
929 | - "update": self.update, | ||
930 | - "server": self.server, | ||
931 | - "clean": self.clean, | ||
932 | - "simulator_development": self.simulator_development, | ||
933 | - "clean_logs": self.clean_logs, | ||
934 | - "test": self.test, | ||
935 | - "migrate": self.migrate, | ||
936 | - "mysql_on": self.mysql_on, | ||
937 | - "mysql_off": self.mysql_off, | ||
938 | - "makemigrations": self.makemigrations, | ||
939 | - "updatedb": self.updatedb, | ||
940 | - "unittest": self.unittest, | ||
941 | - "reset_config": self.reset_config, | ||
942 | - "test_all": self.test_all, | ||
943 | - "reset_database_sim": self.reset_database_sim, | ||
944 | - "init_database": self.init_database, | ||
945 | - "kill_server": self.kill_server, | ||
946 | - "loaddata": self.loaddata, | ||
947 | - "start": self.start, | ||
948 | - "start_web": self.start_web, | ||
949 | - "start_agents": self.start_agents, | ||
950 | - "start_agents_and_simulators": self.start_agents_and_simulators, | ||
951 | - "start_agents_and_simulators_for_majordome": self.start_agents_and_simulators_for_majordome, | ||
952 | - "start_agent_monitoring": self.start_agent_monitoring, | ||
953 | - "start_agent_majordome": self.start_agent_majordome, | ||
954 | - "start_agent_alertmanager": self.start_agent_alertmanager, | ||
955 | - "test_majordome": self.test_majordome, | ||
956 | - | ||
957 | - "simulator": self.simulator, | ||
958 | - "kill_simulation": self.kill_simulation, | ||
959 | - "sims_launch": self.sims_launch, | ||
960 | - "help": self.help, | ||
961 | - } | ||
962 | - self.commandDescription = { | ||
963 | - "install": "Launch the server installation", | ||
964 | - "update": "Update the server", | ||
965 | - "server": "Launch the web server", | ||
966 | - "loaddata": "Load the initial fixture in database", | ||
967 | - "clean": "clean the repository", | ||
968 | - "clean_logs": "clean the log directory", | ||
969 | - "test": "launch the server tests", | ||
970 | - "migrate": "execute migrations", | ||
971 | - "mysql_on": "switch the database to be used to MYSQL", | ||
972 | - "mysql_off": "switch the database to be used usage to SQLITE", | ||
973 | - "makemigrations": "create new migrations", | ||
974 | - "reset_config": "Reset the configuration in settings.py", | ||
975 | - "reset_database_sim": "Reset the database content", | ||
976 | - "help": "Help message", | ||
977 | - "updatedb": "Update the database", | ||
978 | - "kill_server": "Kill the web server on port 8000", | ||
979 | - "init_database": "Create a standard initial context for pyros in db", | ||
980 | - "unittest": "Runs the unit tests", | ||
981 | - "test_all": "Run all the existing tests (this command needs to be updated when tests are added in the project", | ||
982 | - "simulator": "Launch a simulation", | ||
983 | - "simulator_development": "Simulation for the scheduler only", | ||
984 | - "kill_simulation": "kill the simulators & web server", | ||
985 | - "sims_launch": "Launch only the simulators", | ||
986 | - } | ||
987 | - | ||
988 | -if __name__ == "__main__": | ||
989 | - if len(sys.argv) == 3 and sys.argv[1].startswith("simulator"): | ||
990 | - SIMULATOR_CONFIG_FILE = sys.argv[2] | ||
991 | - conf = Config() | ||
992 | - if not conf.parseConf(): sys.exit(1) | ||
993 | - pyros = Pyros(conf) | ||
994 | - sys.exit(pyros.exec()) |
src/agent/Agent.py
@@ -115,8 +115,11 @@ class Agent: | @@ -115,8 +115,11 @@ class Agent: | ||
115 | self.config = ConfigPyros(config_filename) | 115 | self.config = ConfigPyros(config_filename) |
116 | if self.config.get_last_errno() != self.config.NO_ERROR: | 116 | if self.config.get_last_errno() != self.config.NO_ERROR: |
117 | raise Exception(f"Bad config file name '{config_filename}', error {str(self.config.get_last_errno())}: {str(self.config.get_last_errmsg())}") | 117 | raise Exception(f"Bad config file name '{config_filename}', error {str(self.config.get_last_errno())}: {str(self.config.get_last_errmsg())}") |
118 | - tmp = AgentsSurvey.objects.filter(name=self.name) | ||
119 | - if len(tmp) == 0: | 118 | + #tmp = AgentsSurvey.objects.filter(name=self.name) |
119 | + #if len(tmp) == 0: | ||
120 | + #nb_agents = AgentsSurvey.objects.filter(name=self.name).count() | ||
121 | + #if nb_agents == 0: | ||
122 | + if not AgentsSurvey.objects.filter(name=self.name).exists(): | ||
120 | self._agent_survey = AgentsSurvey.objects.create(name=self.name, validity_duration_sec=60, mode=self.mode, status=self.status) | 123 | self._agent_survey = AgentsSurvey.objects.create(name=self.name, validity_duration_sec=60, mode=self.mode, status=self.status) |
121 | #self._agent_survey = AgentsSurvey(name=self.name, validity_duration_sec=60, mode=self.mode, status=self.status) | 124 | #self._agent_survey = AgentsSurvey(name=self.name, validity_duration_sec=60, mode=self.mode, status=self.status) |
122 | #self._agent_survey.save() | 125 | #self._agent_survey.save() |
@@ -172,11 +175,12 @@ class Agent: | @@ -172,11 +175,12 @@ class Agent: | ||
172 | 175 | ||
173 | self.load_config() | 176 | self.load_config() |
174 | 177 | ||
175 | - self.update_db_survey() | ||
176 | - | ||
177 | - self.read_db_commands() | 178 | + self.update_survey() |
178 | 179 | ||
180 | + # generic cmd in json format | ||
181 | + cmd = self.read_next_command() | ||
179 | 182 | ||
183 | + self.general_process(cmd) | ||
180 | ''' | 184 | ''' |
181 | if self.config.majordome_state == "STOP": | 185 | if self.config.majordome_state == "STOP": |
182 | break | 186 | break |
@@ -190,7 +194,7 @@ class Agent: | @@ -190,7 +194,7 @@ class Agent: | ||
190 | # Sub-level loop (only if ACTIVE) | 194 | # Sub-level loop (only if ACTIVE) |
191 | if self.is_active(): | 195 | if self.is_active(): |
192 | self.set_status(self.STATUS_PROCESS_LOOP) | 196 | self.set_status(self.STATUS_PROCESS_LOOP) |
193 | - self.core_process() | 197 | + self.specific_process(cmd) |
194 | 198 | ||
195 | self.waitfor(self.mainloop_waittime) | 199 | self.waitfor(self.mainloop_waittime) |
196 | 200 | ||
@@ -200,6 +204,14 @@ class Agent: | @@ -200,6 +204,14 @@ class Agent: | ||
200 | 204 | ||
201 | 205 | ||
202 | 206 | ||
207 | + def general_process(self, cmd): | ||
208 | + pass | ||
209 | + | ||
210 | + # @abstract | ||
211 | + # to be implemented by subclasses | ||
212 | + def specific_process(self, cmd): | ||
213 | + raise NotImplemented() | ||
214 | + | ||
203 | def waitfor(self, nbsec): | 215 | def waitfor(self, nbsec): |
204 | print(f"Now, waiting for {nbsec} seconds...") | 216 | print(f"Now, waiting for {nbsec} seconds...") |
205 | time.sleep(nbsec) | 217 | time.sleep(nbsec) |
@@ -330,9 +342,9 @@ class Agent: | @@ -330,9 +342,9 @@ class Agent: | ||
330 | self._agent_survey.status = self.status | 342 | self._agent_survey.status = self.status |
331 | self._agent_survey.save() | 343 | self._agent_survey.save() |
332 | 344 | ||
333 | - def read_db_commands(self): | 345 | + def read_next_command(self): |
334 | print("Looking for new commands from the database ...") | 346 | print("Looking for new commands from the database ...") |
335 | - _agent_commands = AgentsCommand.objects.filter(receiver=self.name) | 347 | + _agent_commands = AgentsCommand.objects.filter(receiver=self.name, receiver_error_code=1) |
336 | # Is there any command for me to execute ? | 348 | # Is there any command for me to execute ? |
337 | if _agent_commands: | 349 | if _agent_commands: |
338 | print(f"I have received {len(_agent_commands)} new command(s) to execute") | 350 | print(f"I have received {len(_agent_commands)} new command(s) to execute") |
src/agent/AgentX.py
@@ -33,17 +33,17 @@ class AgentX(Agent): | @@ -33,17 +33,17 @@ class AgentX(Agent): | ||
33 | super().load_config() | 33 | super().load_config() |
34 | 34 | ||
35 | # @override | 35 | # @override |
36 | - def update_db_survey(self): | 36 | + def update_survey(self): |
37 | super().update_db_survey() | 37 | super().update_db_survey() |
38 | 38 | ||
39 | # @override | 39 | # @override |
40 | - def read_db_commands(self): | ||
41 | - super().read_db_commands() | 40 | + def read_next_command(self): |
41 | + return super().read_next_command() | ||
42 | 42 | ||
43 | # @override | 43 | # @override |
44 | def do_log(self): | 44 | def do_log(self): |
45 | super().do_log() | 45 | super().do_log() |
46 | 46 | ||
47 | # @override | 47 | # @override |
48 | - def core_process(self): | ||
49 | - super().core_process() | 48 | + def specific_process(self, cmd): |
49 | + pass | ||
50 | \ No newline at end of file | 50 | \ No newline at end of file |
src/common/models.py
1 | -from __future__ import unicode_literals | 1 | +##from __future__ import unicode_literals |
2 | + | ||
3 | +from enum import Enum | ||
2 | 4 | ||
3 | from django.contrib.auth.models import AbstractUser | 5 | from django.contrib.auth.models import AbstractUser |
4 | from django.db import models | 6 | from django.db import models |
5 | -from enum import Enum | 7 | +from django.core.validators import MaxValueValidator, MinValueValidator |
8 | + | ||
6 | 9 | ||
7 | 10 | ||
8 | ''' | 11 | ''' |
@@ -22,25 +25,24 @@ class PyrosState(Enum): | @@ -22,25 +25,24 @@ class PyrosState(Enum): | ||
22 | """ | 25 | """ |
23 | STYLE RULES | 26 | STYLE RULES |
24 | =========== | 27 | =========== |
25 | -(https://simpleisbetterthancomplex.com/tips/2018/02/10/django-tip-22-designing-better-models.html) | 28 | +https://simpleisbetterthancomplex.com/tips/2018/02/10/django-tip-22-designing-better-models.html |
29 | +https://steelkiwi.com/blog/best-practices-working-django-models-python/ | ||
26 | 30 | ||
27 | -- The model definition is a class, so always use CapWords convention (no underscores) | 31 | +- Model name => singular |
32 | + Call it Company instead of Companies. | ||
33 | + A model definition is the representation of a single object (the object in this example is a company), | ||
34 | + and not a collection of companies | ||
35 | + The model definition is a class, so always use CapWords convention (no underscores) | ||
28 | E.g. User, Permission, ContentType, etc. | 36 | E.g. User, Permission, ContentType, etc. |
29 | 37 | ||
30 | - For the model’s attributes use snake_case. | 38 | - For the model’s attributes use snake_case. |
31 | E.g. first_name, last_name, etc | 39 | E.g. first_name, last_name, etc |
32 | 40 | ||
33 | -- Always name your models using singular. | ||
34 | - Call it Company instead of Companies. | ||
35 | - A model definition is the representation of a single object (the object in this example is a company), | ||
36 | - and not a collection of companies | ||
37 | - | ||
38 | -- Blank and Null Fields | 41 | +- Blank and Null Fields (https://simpleisbetterthancomplex.com/tips/2016/07/25/django-tip-8-blank-or-null.html) |
39 | - Null: It is database-related. Defines if a given database column will accept null values or not. | 42 | - Null: It is database-related. Defines if a given database column will accept null values or not. |
40 | - Blank: It is validation-related. It will be used during forms validation, when calling form.is_valid(). | 43 | - Blank: It is validation-related. It will be used during forms validation, when calling form.is_valid(). |
41 | Do not use null=True for text-based fields that are optional. | 44 | Do not use null=True for text-based fields that are optional. |
42 | - Otherwise, you will end up having two possible values for “no data”, | ||
43 | - that is: None and an empty string. | 45 | + Otherwise, you will end up having two possible values for “no data”, that is: None and an empty string. |
44 | Having two possible values for “no data” is redundant. | 46 | Having two possible values for “no data” is redundant. |
45 | The Django convention is to use the empty string, not NULL. | 47 | The Django convention is to use the empty string, not NULL. |
46 | Example: | 48 | Example: |
@@ -49,6 +51,16 @@ STYLE RULES | @@ -49,6 +51,16 @@ STYLE RULES | ||
49 | name = models.CharField(max_length=255) # Mandatory | 51 | name = models.CharField(max_length=255) # Mandatory |
50 | bio = models.TextField(max_length=500, blank=True) # Optional (don't put null=True) | 52 | bio = models.TextField(max_length=500, blank=True) # Optional (don't put null=True) |
51 | birth_date = models.DateField(null=True, blank=True) # Optional (here you may add null=True) | 53 | birth_date = models.DateField(null=True, blank=True) # Optional (here you may add null=True) |
54 | + The default values of null and blank are False. | ||
55 | + Special case, when you need to accept NULL values for a BooleanField, use NullBooleanField instead. | ||
56 | + | ||
57 | +- Choices : you can use Choices from the model_utils library. Take model Article, for instance: | ||
58 | + from model_utils import Choices | ||
59 | + class Article(models.Model): | ||
60 | + STATUSES = Choices( | ||
61 | + (0, 'draft', _('draft')), | ||
62 | + (1, 'published', _('published')) ) | ||
63 | + status = models.IntegerField(choices=STATUSES, default=STATUSES.draft) | ||
52 | 64 | ||
53 | - Reverse Relationships | 65 | - Reverse Relationships |
54 | 66 | ||
@@ -203,10 +215,11 @@ class AgentsCommand(models.Model): | @@ -203,10 +215,11 @@ class AgentsCommand(models.Model): | ||
203 | (l'agent destinataire en profite pour supprimer les commandes périmées qui le concernent) | 215 | (l'agent destinataire en profite pour supprimer les commandes périmées qui le concernent) |
204 | """ | 216 | """ |
205 | #sender = models.CharField(max_length=50, blank=True, null=True, unique=True) | 217 | #sender = models.CharField(max_length=50, blank=True, null=True, unique=True) |
206 | - sender = models.CharField(max_length=50, unique=True) | 218 | + sender = models.CharField(max_length=50, unique=True, help_text='sender agent name') |
207 | receiver = models.CharField(max_length=50, unique=True) | 219 | receiver = models.CharField(max_length=50, unique=True) |
208 | command = models.CharField(max_length=400) | 220 | command = models.CharField(max_length=400) |
209 | - validity_duration_sec = models.PositiveIntegerField(default=60) | 221 | + #validity_duration_sec = models.PositiveIntegerField(default=60) |
222 | + validity_duration_sec = models.DurationField(default=60) | ||
210 | 223 | ||
211 | # Automatically set at table line creation (line created by the sender) | 224 | # Automatically set at table line creation (line created by the sender) |
212 | sender_deposit_time = models.DateTimeField(blank=True, null=True, auto_now_add=True) | 225 | sender_deposit_time = models.DateTimeField(blank=True, null=True, auto_now_add=True) |
@@ -231,6 +244,9 @@ class AgentsSurvey(models.Model): | @@ -231,6 +244,9 @@ class AgentsSurvey(models.Model): | ||
231 | """ | 244 | """ |
232 | | id | name | created | updated | validity_duration_sec (default=1mn) | mode (active/idle) | status (launch/init/loop/exit/...) | | 245 | | id | name | created | updated | validity_duration_sec (default=1mn) | mode (active/idle) | status (launch/init/loop/exit/...) | |
233 | """ | 246 | """ |
247 | + | ||
248 | + #STATUSES = Choices('new', 'verified', 'published') | ||
249 | + | ||
234 | # Statuses | 250 | # Statuses |
235 | STATUS_LAUNCH = "LAUNCHED" | 251 | STATUS_LAUNCH = "LAUNCHED" |
236 | STATUS_INIT = "INITIALIZING" | 252 | STATUS_INIT = "INITIALIZING" |
@@ -255,13 +271,15 @@ class AgentsSurvey(models.Model): | @@ -255,13 +271,15 @@ class AgentsSurvey(models.Model): | ||
255 | (STATUS_EXIT, "EXITING"), | 271 | (STATUS_EXIT, "EXITING"), |
256 | ) | 272 | ) |
257 | 273 | ||
258 | - name = models.CharField(max_length=50, blank=True, null=True, unique=True) | 274 | + name = models.CharField(max_length=50, unique=True) |
275 | + #name = models.CharField(max_length=50, blank=True, null=True, unique=True) | ||
259 | #created = models.DateTimeField(blank=True, null=True, auto_now_add=True) | 276 | #created = models.DateTimeField(blank=True, null=True, auto_now_add=True) |
260 | created = models.DateTimeField(blank=True, null=True, auto_now_add=True) | 277 | created = models.DateTimeField(blank=True, null=True, auto_now_add=True) |
261 | updated = models.DateTimeField(blank=True, null=True, auto_now=True) | 278 | updated = models.DateTimeField(blank=True, null=True, auto_now=True) |
262 | - validity_duration_sec = models.PositiveIntegerField(blank=True, null=True) | ||
263 | - mode = models.CharField('agent mode', max_length=15, blank=True, null=True, choices=MODE_CHOICES) | ||
264 | - status = models.CharField(max_length=15, blank=True, null=True, choices=STATUS_CHOICES) | 279 | + #validity_duration_sec = models.PositiveIntegerField(blank=True, null=True) |
280 | + validity_duration_sec = models.DurationField(default=90) | ||
281 | + mode = models.CharField('agent mode', max_length=15, blank=True, choices=MODE_CHOICES) | ||
282 | + status = models.CharField(max_length=15, blank=True, choices=STATUS_CHOICES) | ||
265 | 283 | ||
266 | class Meta: | 284 | class Meta: |
267 | managed = True | 285 | managed = True |
@@ -330,9 +348,25 @@ class Config(models.Model): | @@ -330,9 +348,25 @@ class Config(models.Model): | ||
330 | PYROS_STATE = ["Starting", "Passive", "Standby", "Remote", "Startup", "Scheduler", "Closing" ] | 348 | PYROS_STATE = ["Starting", "Passive", "Standby", "Remote", "Startup", "Scheduler", "Closing" ] |
331 | 349 | ||
332 | id = models.IntegerField(default='1', primary_key=True) | 350 | id = models.IntegerField(default='1', primary_key=True) |
333 | - latitude = models.FloatField(default=1) | 351 | + #latitude = models.FloatField(default=1) |
352 | + latitude = models.DecimalField( | ||
353 | + max_digits=4, decimal_places=2, | ||
354 | + default=1, | ||
355 | + validators=[ | ||
356 | + MaxValueValidator(90), | ||
357 | + MinValueValidator(-90) | ||
358 | + ] | ||
359 | + ) | ||
334 | local_time_zone = models.FloatField(default=1) | 360 | local_time_zone = models.FloatField(default=1) |
335 | - longitude = models.FloatField(default=1) | 361 | + #longitude = models.FloatField(default=1) |
362 | + longitude = models.DecimalField( | ||
363 | + max_digits=5, decimal_places=2, | ||
364 | + default=1, | ||
365 | + validators=[ | ||
366 | + MaxValueValidator(360), | ||
367 | + MinValueValidator(-360) | ||
368 | + ] | ||
369 | + ) | ||
336 | altitude = models.FloatField(default=1) | 370 | altitude = models.FloatField(default=1) |
337 | horizon_line = models.FloatField(default=1) | 371 | horizon_line = models.FloatField(default=1) |
338 | row_data_save_frequency = models.IntegerField(default='300') | 372 | row_data_save_frequency = models.IntegerField(default='300') |