Commit 980acc36efbb94a44ee8576c3101e4f876e3a8a9
Exists in
dev
Merge branch 'dev' of https://gitlab.irap.omp.eu/epallier/pyros into dev
Showing
11 changed files
with
718 additions
and
429 deletions
Show diff stats
CHANGELOG
1 | +25-11-2022 (AKo): v0.6.13.1 | |
2 | + - Add debug mode to agentSST | |
3 | + - UI changes on agent_detail | |
4 | + - Enable log file of agent with the right name | |
5 | + | |
1 | 6 | 25-11-2022 (AKo): v0.6.13.0 |
2 | 7 | - Add is_active field for agents in tnc obsconfig |
3 | 8 | - Add new colors in agents_cmds and agent_detail for cmd status | ... | ... |
VERSION
privatedev/plugin/agent/AgentBasic.py
src/core/pyros_django/agent/Agent.py
... | ... | @@ -42,7 +42,7 @@ and execute them on reception at each iteration : |
42 | 42 | # For cmd parsing |
43 | 43 | from array import array |
44 | 44 | from datetime import datetime |
45 | -from typing import Dict, List, Tuple, Union, Any, Optional, Literal | |
45 | +from typing import Final, Sequence, Iterable, Mapping, MutableMapping, Dict, List, Tuple, Union, Any, Optional, Literal | |
46 | 46 | import ast |
47 | 47 | from inspect import signature |
48 | 48 | |
... | ... | @@ -384,7 +384,9 @@ class Agent: |
384 | 384 | # |
385 | 385 | #_TEST_COMMANDS_LIST: List[ Tuple[ bool, str, int, Union[str,None], Union[int,None] ] ] = [ |
386 | 386 | ##_TEST_COMMANDS_LIST: List[ Tuple[ bool, str, int, Optional[str], AgentCmd.CMD_STATUS_CODES ] ] = [ |
387 | - _TEST_COMMANDS_LIST: List[ Tuple[ bool, str, Optional[int], Optional[str], Optional[int]] ] = [ | |
387 | + TestCommand = Tuple[ bool, str, Optional[int], Optional[str], Optional[int]] | |
388 | + #_TEST_COMMANDS_LIST: List[ Tuple[ bool, str, Optional[int], Optional[str], Optional[int]] ] = [ | |
389 | + _TEST_COMMANDS_LIST: List[ TestCommand ] = [ | |
388 | 390 | # Format : (DO_IT, "self cmd_name cmd_args", validity, "expected_result", expected_status), |
389 | 391 | |
390 | 392 | #("self do_stop now", 200, '15.5', None), |
... | ... | @@ -602,24 +604,25 @@ class Agent: |
602 | 604 | ##def __init__(self, RUN_IN_THREAD=True): |
603 | 605 | def __init__(self,simulated_computer=None): |
604 | 606 | |
605 | - # Declaration of Instance attributes, default values | |
607 | + # Instance attributes declaration (with default values, or None) | |
608 | + self.__UP_SINCE : Final = datetime.now(tz=timezone.utc) | |
606 | 609 | #self.UP_SINCE = datetime.utcnow() |
607 | - self.__UP_SINCE = datetime.now(tz=timezone.utc) | |
608 | 610 | self.__ROUTINE_ITER_START_IS_RUNNING:bool = False |
609 | 611 | self.__ROUTINE_ITER_END_IS_RUNNING:bool = False |
610 | 612 | self.__test_cmd_received_num:int = 0 # only for tests |
611 | 613 | # Current Command running |
612 | - self.__CC :Optional[AgentCmd] = None | |
613 | - self.__CC_thread :Union[StoppableThreadEvenWhenSleeping, multiprocessing.Process] = None | |
614 | + self.__CC :Optional[AgentCmd] #= None | |
615 | + self.__CC_thread :Union[StoppableThreadEvenWhenSleeping, multiprocessing.Process] #= None | |
614 | 616 | # Previous Command running |
615 | 617 | ##self.__CC_prev :Optional[AgentCmd] = None |
616 | 618 | # Current Command exception (if occurs) |
617 | - self.__CCE :Optional[Exception] = None | |
619 | + self.__CCE :Optional[Exception] #= None | |
618 | 620 | self.name = "Generic Agent" |
619 | - self.__status :str = None | |
620 | - self.__mode :str = None | |
621 | - self.unit = None | |
622 | - self.TEST_COMMANDS = None | |
621 | + self.__status :str #= None | |
622 | + self.__mode :str #= None | |
623 | + self.unit :str #= None | |
624 | + #self.TEST_COMMANDS :List #= None | |
625 | + self.TEST_COMMANDS :Iterable[Agent.TestCommand] #= None | |
623 | 626 | self.__iter_num :int = 0 |
624 | 627 | #print(AgentSurvey.MODE_CHOICES.IDLE) |
625 | 628 | #sys.exit() |
... | ... | @@ -628,23 +631,24 @@ class Agent: |
628 | 631 | #self.__mode = self.MODE_ATTENTIVE |
629 | 632 | self.set_mode_attentive() |
630 | 633 | #self._set_mode(MODES.) |
631 | - | |
632 | - log.addHandler(handler_filebyagent(logging.INFO, self.__class__.__name__)) | |
633 | - #log.addHandler(handler_filebyagent(logging.INFO, self.name)) | |
634 | - log.debug("start Agent init") | |
635 | 634 | obs_config_file_path = os.environ["PATH_TO_OBSCONF_FILE"] |
636 | 635 | path_to_obs_config_folder = os.environ["PATH_TO_OBSCONF_FOLDER"] |
637 | 636 | unit = os.environ["unit_name"] |
638 | 637 | oc = OBSConfig(obs_config_file_path,unit) |
639 | 638 | self.set_config(oc, obs_config_file_path, path_to_obs_config_folder, unit) |
639 | + agent_name_from_config = self.get_config().get_agent_name_from_config(self.__class__.__name__,simulated_computer) | |
640 | + if agent_name_from_config: | |
641 | + self.name = agent_name_from_config | |
642 | + else: | |
643 | + self.name = self.__class__.__name__ | |
644 | + | |
645 | + log.addHandler(handler_filebyagent(logging.INFO, self.name)) | |
646 | + #log.addHandler(handler_filebyagent(logging.INFO, self.name)) | |
647 | + log.debug("start Agent init") | |
640 | 648 | |
641 | - self.name = self.__class__.__name__ | |
642 | 649 | # set real unit name (the current unit used) |
643 | 650 | if unit == "": |
644 | 651 | unit = oc.unit_name |
645 | - agent_name_from_config = self.get_config().get_agent_name_from_config(self.__class__.__name__,simulated_computer) | |
646 | - if agent_name_from_config: | |
647 | - self.name = agent_name_from_config | |
648 | 652 | self.unit = unit |
649 | 653 | print(f"Agent name : {self.name}") |
650 | 654 | print(f"Unit name : {self.unit}") |
... | ... | @@ -3044,7 +3048,7 @@ class Agent: |
3044 | 3048 | #cmd_full_name, validity, res_expected, after_status = next(self.TEST_COMMANDS, (None,None,None,None)) |
3045 | 3049 | DO_IT = False |
3046 | 3050 | while not DO_IT: |
3047 | - cmd_params = next(self.TEST_COMMANDS, (False,None,None,None,None)) | |
3051 | + cmd_params:Agent.TestCommand = next(self.TEST_COMMANDS, (False,None,None,None,None)) | |
3048 | 3052 | #print(cmd_params) |
3049 | 3053 | DO_IT, cmd_full_name, validity, expected_res, expected_status = cmd_params |
3050 | 3054 | ###DO_IT, cmd_full_name, validity, expected_res, expected_status = next(self.TEST_COMMANDS, (False,None,None,None,None)) | ... | ... |
src/core/pyros_django/agent/AgentBasic.py
src/core/pyros_django/agent/AgentSST.py
... | ... | @@ -147,6 +147,8 @@ class AgentSST(Agent): |
147 | 147 | cmd += test_mode |
148 | 148 | if self.simulated_computer: |
149 | 149 | cmd += f" --computer {self.simulated_computer}" |
150 | + if self.DEBUG_MODE: | |
151 | + cmd += " -d" | |
150 | 152 | process = subprocess.Popen(f"{cmd}",shell=True) |
151 | 153 | process.poll() |
152 | 154 | if agent not in self.subprocess_dict: |
... | ... | @@ -187,6 +189,8 @@ class AgentSST(Agent): |
187 | 189 | cmd += test_mode |
188 | 190 | if self.simulated_computer: |
189 | 191 | cmd += f" --computer {self.simulated_computer}" |
192 | + if self.DEBUG_MODE: | |
193 | + cmd += " -d" | |
190 | 194 | # process = subprocess.Popen(f"{cmd}", shell=True, stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT) |
191 | 195 | process = subprocess.Popen(f"{cmd}", shell=True) |
192 | 196 | self.subprocess_dict[agent] = {} |
... | ... | @@ -256,7 +260,6 @@ class AgentSST(Agent): |
256 | 260 | def _do_things_before_exit(self,abort_cmd_sender): |
257 | 261 | kill_agent_commands = {} |
258 | 262 | for agent in self.subprocess_dict.keys(): |
259 | - print(agent) | |
260 | 263 | if AgentSurvey.objects.get(name=agent).status != "EXITING": |
261 | 264 | self.do_stop_agent(agent) |
262 | 265 | cmd = AgentCmd.objects.filter(full_name="do_stop asap",recipient=agent).latest("s_deposit_time") |
... | ... | @@ -358,6 +361,7 @@ if __name__ == "__main__": |
358 | 361 | parser.add_argument("--computer",dest="computer",help='Launch agent with simulated computer hostname',action="store") |
359 | 362 | parser.add_argument("--agent",dest="agent",help='Launch an specific agent ',action="store") |
360 | 363 | parser.add_argument("-t", action="store_true" ) |
364 | + parser.add_argument("-d", action="store_true" ) | |
361 | 365 | args = vars(parser.parse_args()) |
362 | 366 | agent = build_agent(AgentSST,param_constr=args) |
363 | 367 | # agent = build_agent(AgentSST) | ... | ... |
src/core/pyros_django/common/admin.py
... | ... | @@ -7,6 +7,9 @@ from django.conf import settings |
7 | 7 | |
8 | 8 | from common.models import * |
9 | 9 | |
10 | +# Necessary since new device/models.py file | |
11 | +from devices.models import Detector, Filter, AgentDeviceStatus, FilterWheel, Telescope, PlcDevice, PlcDeviceStatus | |
12 | + | |
10 | 13 | |
11 | 14 | # EP added |
12 | 15 | class ReadOnlyModelAdmin(admin.ModelAdmin): | ... | ... |
src/core/pyros_django/common/models.py
... | ... | @@ -4,9 +4,7 @@ |
4 | 4 | from __future__ import annotations |
5 | 5 | |
6 | 6 | # Stdlib imports |
7 | -from numpy import False_ | |
8 | 7 | from src.device_controller.abstract_component.device_controller import DeviceCmd |
9 | -from enum import Enum | |
10 | 8 | from datetime import datetime, timedelta, date |
11 | 9 | from dateutil.relativedelta import relativedelta |
12 | 10 | import os |
... | ... | @@ -31,17 +29,7 @@ from django.utils import timezone |
31 | 29 | # DeviceCommand is used by class Command |
32 | 30 | sys.path.append("../../..") |
33 | 31 | |
34 | -''' | |
35 | -NOT USED - to be removed | |
36 | -class PyrosState(Enum): | |
37 | - START = 'Starting' | |
38 | - PA = 'Passive' | |
39 | - INI = "INIT" | |
40 | - STAND = "Standby" | |
41 | - SCHED_START = 'Scheduler startup' | |
42 | - SCHED = 'Scheduler' | |
43 | - SCHED_CLOSE = 'Scheduler closing' | |
44 | -''' | |
32 | + | |
45 | 33 | |
46 | 34 | |
47 | 35 | """ |
... | ... | @@ -176,43 +164,28 @@ class Company(models.Model): |
176 | 164 | """ |
177 | 165 | |
178 | 166 | |
179 | -def printd(*args, **kwargs): | |
180 | - if os.environ.get('PYROS_DEBUG', '0') == '1': | |
181 | - print('(MODEL)', *args, **kwargs) | |
182 | - | |
183 | 167 | # --- |
184 | 168 | # --- Utility functions |
185 | 169 | # --- |
186 | 170 | |
171 | +def printd(*args, **kwargs): | |
172 | + if os.environ.get('PYROS_DEBUG', '0') == '1': | |
173 | + print('(MODEL)', *args, **kwargs) | |
187 | 174 | |
188 | 175 | def get_or_create_unique_row_from_model(model: models.Model): |
189 | 176 | # return model.objects.get(id=1) if model.objects.exists() else model.objects.create(id=1) |
190 | 177 | return model.objects.first() if model.objects.exists() else model.objects.create(id=1) |
191 | 178 | |
192 | 179 | |
180 | + | |
181 | + | |
182 | + | |
193 | 183 | """ |
194 | 184 | ------------------------ |
195 | 185 | BASE MODEL CLASSES |
196 | 186 | ------------------------ |
197 | 187 | """ |
198 | 188 | |
199 | - | |
200 | -class Device(models.Model): | |
201 | - name = models.CharField(max_length=45, blank=True, null=True) | |
202 | - desc = models.TextField(blank=True, null=True) | |
203 | - created = models.DateTimeField(blank=True, null=True, auto_now_add=True) | |
204 | - updated = models.DateTimeField(blank=True, null=True, auto_now=True) | |
205 | - is_online = models.BooleanField(default=False) | |
206 | - status = models.CharField(max_length=11, blank=True, null=True) | |
207 | - maintenance_date = models.DateTimeField(blank=True, null=True) | |
208 | - | |
209 | - class Meta: | |
210 | - abstract = True | |
211 | - | |
212 | - def __str__(self): | |
213 | - return (str(self.name)) | |
214 | - | |
215 | - | |
216 | 189 | class Request(models.Model): |
217 | 190 | pyros_user = models.ForeignKey( |
218 | 191 | 'PyrosUser', on_delete=models.DO_NOTHING, related_name="requests") |
... | ... | @@ -245,64 +218,51 @@ class Request(models.Model): |
245 | 218 | ------------------------ |
246 | 219 | """ |
247 | 220 | |
248 | -# TODO: A VIRER car remplacรฉ par AgentDeviceStatus | |
249 | 221 | |
250 | 222 | |
251 | -class AgentDeviceTelescopeStatus(models.Model): | |
252 | - #created = models.DateTimeField('status date', blank=True, null=True, auto_now_add=True) | |
253 | - updated = models.DateTimeField( | |
254 | - 'status date', blank=True, null=True, auto_now=True) | |
255 | - radec = models.CharField('agent mode', max_length=30, blank=True) | |
223 | +# TODO: A mettre dans device/models.py ? | |
224 | +class Image(models.Model): | |
225 | + plan = models.ForeignKey( | |
226 | + 'Plan', on_delete=models.CASCADE, related_name="images") | |
227 | + nrtanalysis = models.ForeignKey( | |
228 | + 'NrtAnalysis', models.DO_NOTHING, blank=True, null=True, related_name="images") | |
229 | + name = models.CharField(max_length=45, blank=True, null=True) | |
230 | + desc = models.TextField(blank=True, null=True) | |
231 | + created = models.DateTimeField(blank=True, null=True, auto_now_add=True) | |
232 | + updated = models.DateTimeField(blank=True, null=True, auto_now=True) | |
233 | + date_from_gps = models.CharField(max_length=45, blank=True, null=True) | |
234 | + level = models.IntegerField(blank=True, null=True) | |
235 | + type = models.CharField(max_length=5, blank=True, null=True) | |
236 | + quality = models.CharField(max_length=45, blank=True, null=True) | |
237 | + flaggps = models.CharField(max_length=45, blank=True, null=True) | |
238 | + exposure = models.CharField(max_length=45, blank=True, null=True) | |
239 | + tempext = models.CharField(max_length=45, blank=True, null=True) | |
240 | + pressure = models.CharField(max_length=45, blank=True, null=True) | |
241 | + humidext = models.CharField(max_length=45, blank=True, null=True) | |
242 | + wind = models.CharField(max_length=45, blank=True, null=True) | |
243 | + wind_dir = models.CharField(max_length=45, blank=True, null=True) | |
244 | + dwnimg = models.CharField(max_length=45, blank=True, null=True) | |
245 | + dwncata = models.CharField(max_length=45, blank=True, null=True) | |
246 | + dwn = models.CharField(max_length=45, blank=True, null=True) | |
247 | + level0_fits_name = models.CharField(max_length=45, blank=True, null=True) | |
248 | + level1a_fits_name = models.CharField(max_length=45, blank=True, null=True) | |
249 | + level1b_fits_name = models.CharField(max_length=45, blank=True, null=True) | |
256 | 250 | |
257 | 251 | class Meta: |
258 | 252 | managed = True |
259 | - db_table = 'agent_device_telescope_status' | |
260 | - #verbose_name = "agent survey" | |
261 | - #verbose_name_plural = "agents survey" | |
253 | + db_table = 'image' | |
262 | 254 | |
263 | - """ | |
264 | 255 | def __str__(self): |
265 | - return (f"Agent {self.name} at {self.updated} in mode {self.mode} and status {self.status}") | |
266 | - """ | |
256 | + return (str(self.name)) | |
257 | + | |
258 | + | |
267 | 259 | |
268 | 260 | |
269 | -class AgentDeviceStatus(models.Model): | |
270 | - """Table storing various status parameters for EACH Device. | |
271 | 261 | |
272 | - Attributes: | |
273 | - attr1 (str): Description of `attr1`. | |
274 | - attr2 (:obj:`int`, optional): Description of `attr2`. | |
275 | 262 | |
276 | - """ | |
277 | - #created = models.DateTimeField('status date', blank=True, null=True, auto_now_add=True) | |
278 | - agent = models.CharField( | |
279 | - 'Name of the agent that saved this parameter', max_length=45, blank=True, null=True) | |
280 | - #radec = models.CharField('agent mode', max_length=30, blank=True) | |
281 | - status = models.CharField( | |
282 | - 'status parameters json dictionnary (ex: {radec:..., speed:...})', max_length=300, blank=True, null=True) | |
283 | - date_updated = models.DateTimeField( | |
284 | - 'status parameter date', blank=True, null=True, auto_now=True) | |
285 | 263 | |
286 | - class Meta: | |
287 | - managed = True | |
288 | - db_table = 'agent_device_status' | |
289 | - verbose_name = "agent device status" | |
290 | - verbose_name_plural = "agent devices status" | |
291 | 264 | |
292 | - def __str__(self): | |
293 | - return (f"Agent {self.agent} last status is ({self.status}) (saved at {self.date_updated})") | |
294 | 265 | |
295 | - @classmethod | |
296 | - def getStatusForAgent(cls, agent: str) -> str: | |
297 | - return cls.objects.filter(agent=agent)[0] if cls.objects.filter(agent=agent).exists() else cls.objects.create(agent=agent) | |
298 | - ''' | |
299 | - return cls.objects.filter(agent=agent)[0].status if cls.objects.filter(agent=agent).exists() else cls.objects.create(agent=agent).status | |
300 | - agent_status = cls.objects.filter(agent=agent) | |
301 | - if agent_status.exists(): | |
302 | - return agent_status[0].status | |
303 | - else: | |
304 | - return cls.objects.create(agent=agent) | |
305 | - ''' | |
306 | 266 | |
307 | 267 | |
308 | 268 | class AgentLogs(models.Model): |
... | ... | @@ -1379,130 +1339,6 @@ class Country(models.Model): |
1379 | 1339 | return (str(self.name)) |
1380 | 1340 | |
1381 | 1341 | |
1382 | -class Detector(Device): | |
1383 | - VIS = "Visible camera" | |
1384 | - NIR = "Cagire" | |
1385 | - | |
1386 | - telescope = models.ForeignKey( | |
1387 | - 'Telescope', models.DO_NOTHING, related_name="detectors") | |
1388 | - nb_photo_x = models.IntegerField(blank=True, null=True) | |
1389 | - nb_photo_y = models.IntegerField(blank=True, null=True) | |
1390 | - photo_size_x = models.IntegerField(blank=True, null=True) | |
1391 | - photo_size_y = models.IntegerField(blank=True, null=True) | |
1392 | - has_shutter = models.BooleanField(default=False) | |
1393 | - equivalent_foc_len = models.CharField(max_length=45, blank=True, null=True) | |
1394 | - acq_start = models.DateTimeField(blank=True, null=True) | |
1395 | - acq_stop = models.DateTimeField(blank=True, null=True) | |
1396 | - check_temp = models.FloatField(blank=True, null=True) | |
1397 | - gain = models.FloatField(blank=True, null=True) | |
1398 | - readout_noise = models.FloatField(blank=True, null=True) | |
1399 | - readout_time = models.FloatField(blank=True, null=True) | |
1400 | - idcam_readout_mode = models.IntegerField(blank=True, null=True) | |
1401 | - | |
1402 | - class Meta: | |
1403 | - managed = True | |
1404 | - db_table = 'detector' | |
1405 | - | |
1406 | - def __str__(self): | |
1407 | - return str(self.name) | |
1408 | - | |
1409 | - def device_name(self): | |
1410 | - return self.__str__() | |
1411 | - device_name.short_description = "Name" | |
1412 | - | |
1413 | - | |
1414 | -class Dome(Device): | |
1415 | - DOME = "Dome" | |
1416 | - | |
1417 | - open = models.BooleanField(default=False, blank=True) | |
1418 | - | |
1419 | - class Meta: | |
1420 | - managed = True | |
1421 | - db_table = 'dome' | |
1422 | - | |
1423 | - def __str__(self): | |
1424 | - return str(self.name) | |
1425 | - | |
1426 | - def device_name(self): | |
1427 | - return self.__str__() | |
1428 | - device_name.short_description = "Name" | |
1429 | - | |
1430 | - | |
1431 | -class Filter(Device): | |
1432 | - VIS_FILTER_1 = "First visible filter" | |
1433 | - VIS_FILTER_2 = "Second visible filter" | |
1434 | - NIR_FILTER_1 = "First infrared filter" | |
1435 | - NIR_FILTER_2 = "Second infrared filter" | |
1436 | - | |
1437 | - filter_wheel = models.ForeignKey( | |
1438 | - "FilterWheel", models.DO_NOTHING, related_name="filters", blank=True, null=True) | |
1439 | - category = models.CharField(max_length=1, blank=True, null=True) | |
1440 | - transmission_curve_doc = models.CharField( | |
1441 | - max_length=45, blank=True, null=True) | |
1442 | - | |
1443 | - class Meta: | |
1444 | - managed = True | |
1445 | - db_table = 'filter' | |
1446 | - | |
1447 | - def __str__(self): | |
1448 | - return (str(self.name)) | |
1449 | - | |
1450 | - def device_name(self): | |
1451 | - return self.__str__() | |
1452 | - device_name.short_description = "Name" | |
1453 | - | |
1454 | - | |
1455 | -class FilterWheel(Device): | |
1456 | - detector = models.OneToOneField(Detector, on_delete=models.CASCADE, | |
1457 | - related_name="filter_wheel", blank=True, null=True) | |
1458 | - | |
1459 | - class Meta: | |
1460 | - managed = True | |
1461 | - db_table = 'filter_wheel' | |
1462 | - | |
1463 | - def __str__(self): | |
1464 | - return (str(self.name)) | |
1465 | - | |
1466 | - def device_name(self): | |
1467 | - return self.__str__() | |
1468 | - device_name.short_description = "Name" | |
1469 | - | |
1470 | - | |
1471 | -class Image(models.Model): | |
1472 | - plan = models.ForeignKey( | |
1473 | - 'Plan', on_delete=models.CASCADE, related_name="images") | |
1474 | - nrtanalysis = models.ForeignKey( | |
1475 | - 'NrtAnalysis', models.DO_NOTHING, blank=True, null=True, related_name="images") | |
1476 | - name = models.CharField(max_length=45, blank=True, null=True) | |
1477 | - desc = models.TextField(blank=True, null=True) | |
1478 | - created = models.DateTimeField(blank=True, null=True, auto_now_add=True) | |
1479 | - updated = models.DateTimeField(blank=True, null=True, auto_now=True) | |
1480 | - date_from_gps = models.CharField(max_length=45, blank=True, null=True) | |
1481 | - level = models.IntegerField(blank=True, null=True) | |
1482 | - type = models.CharField(max_length=5, blank=True, null=True) | |
1483 | - quality = models.CharField(max_length=45, blank=True, null=True) | |
1484 | - flaggps = models.CharField(max_length=45, blank=True, null=True) | |
1485 | - exposure = models.CharField(max_length=45, blank=True, null=True) | |
1486 | - tempext = models.CharField(max_length=45, blank=True, null=True) | |
1487 | - pressure = models.CharField(max_length=45, blank=True, null=True) | |
1488 | - humidext = models.CharField(max_length=45, blank=True, null=True) | |
1489 | - wind = models.CharField(max_length=45, blank=True, null=True) | |
1490 | - wind_dir = models.CharField(max_length=45, blank=True, null=True) | |
1491 | - dwnimg = models.CharField(max_length=45, blank=True, null=True) | |
1492 | - dwncata = models.CharField(max_length=45, blank=True, null=True) | |
1493 | - dwn = models.CharField(max_length=45, blank=True, null=True) | |
1494 | - level0_fits_name = models.CharField(max_length=45, blank=True, null=True) | |
1495 | - level1a_fits_name = models.CharField(max_length=45, blank=True, null=True) | |
1496 | - level1b_fits_name = models.CharField(max_length=45, blank=True, null=True) | |
1497 | - | |
1498 | - class Meta: | |
1499 | - managed = True | |
1500 | - db_table = 'image' | |
1501 | - | |
1502 | - def __str__(self): | |
1503 | - return (str(self.name)) | |
1504 | - | |
1505 | - | |
1506 | 1342 | class Log(models.Model): |
1507 | 1343 | agent = models.CharField(max_length=45, blank=True, null=True) |
1508 | 1344 | created = models.DateTimeField(blank=True, null=True, auto_now_add=True) |
... | ... | @@ -1573,156 +1409,6 @@ class Plan(models.Model): |
1573 | 1409 | def __str__(self) -> str: |
1574 | 1410 | return f"Plan of Album {self.album.name} has {self.nb_images} image(s)" |
1575 | 1411 | |
1576 | -class PlcDeviceStatus(models.Model): | |
1577 | - device = models.ForeignKey( | |
1578 | - 'PlcDevice', on_delete=models.CASCADE, related_name='current_status') | |
1579 | - created = models.DateTimeField( | |
1580 | - auto_now_add=True, editable=False, blank=True) | |
1581 | - outside_temp = models.DecimalField( | |
1582 | - max_digits=15, decimal_places=8, blank=True, null=True) | |
1583 | - outside_temp_unit = models.CharField(max_length=45, blank=True, null=True) | |
1584 | - outside_humidity = models.DecimalField( | |
1585 | - max_digits=15, decimal_places=8, blank=True, null=True) | |
1586 | - outside_humidity_unit = models.CharField( | |
1587 | - max_length=45, blank=True, null=True) | |
1588 | - pressure = models.DecimalField( | |
1589 | - max_digits=15, decimal_places=8, blank=True, null=True) | |
1590 | - pressure_unit = models.CharField(max_length=45, blank=True, null=True) | |
1591 | - rain_rate = models.DecimalField( | |
1592 | - max_digits=15, decimal_places=8, blank=True, null=True) | |
1593 | - rain_rate_unit = models.CharField(max_length=45, blank=True, null=True) | |
1594 | - wind_speed = models.DecimalField( | |
1595 | - max_digits=15, decimal_places=8, blank=True, null=True) | |
1596 | - wind_speed_unit = models.CharField(max_length=45, blank=True, null=True) | |
1597 | - wind_dir = models.DecimalField( | |
1598 | - max_digits=15, decimal_places=8, blank=True, null=True) | |
1599 | - wind_dir_unit = models.CharField(max_length=45, blank=True, null=True) | |
1600 | - dew_point = models.DecimalField( | |
1601 | - max_digits=15, decimal_places=8, blank=True, null=True) | |
1602 | - dew_point_unit = models.CharField(max_length=45, blank=True, null=True) | |
1603 | - analog = models.DecimalField( | |
1604 | - max_digits=15, decimal_places=8, blank=True, null=True) | |
1605 | - analog_unit = models.CharField(max_length=45, blank=True, null=True) | |
1606 | - digital = models.DecimalField( | |
1607 | - max_digits=15, decimal_places=8, blank=True, null=True) | |
1608 | - digital_unit = models.CharField(max_length=45, blank=True, null=True) | |
1609 | - inside_temp = models.DecimalField( | |
1610 | - max_digits=15, decimal_places=8, blank=True, null=True) | |
1611 | - inside_temp_unit = models.CharField(max_length=45, blank=True, null=True) | |
1612 | - inside_humidity = models.DecimalField( | |
1613 | - max_digits=15, decimal_places=8, blank=True, null=True) | |
1614 | - inside_humidity_unit = models.CharField( | |
1615 | - max_length=45, blank=True, null=True) | |
1616 | - wind_dir_cardinal = models.CharField(max_length=45, blank=True, null=True) | |
1617 | - wind_dir_cardinal_unit = models.CharField( | |
1618 | - max_length=45, blank=True, null=True) | |
1619 | - sensor_temperature = models.DecimalField( | |
1620 | - max_digits=15, decimal_places=8, blank=True, null=True) | |
1621 | - sensor_temperature_unit = models.CharField( | |
1622 | - max_length=45, blank=True, null=True) | |
1623 | - sky_temperature = models.DecimalField( | |
1624 | - max_digits=15, decimal_places=8, blank=True, null=True) | |
1625 | - sky_temperature_unit = models.CharField( | |
1626 | - max_length=45, blank=True, null=True) | |
1627 | - status = models.CharField(max_length=45, blank=True, null=True) | |
1628 | - current = models.DecimalField( | |
1629 | - max_digits=15, decimal_places=8, blank=True, null=True) | |
1630 | - current_unit = models.CharField(max_length=45, blank=True, null=True) | |
1631 | - is_safe = models.BooleanField(default=True) | |
1632 | - plc_mode = models.CharField(max_length=4, null=True) | |
1633 | - lights = models.CharField(max_length=3, null=True) | |
1634 | - shutters = models.CharField(max_length=5, null=True) | |
1635 | - | |
1636 | - class Meta: | |
1637 | - managed = True | |
1638 | - db_table = 'plc_devices_status' | |
1639 | - | |
1640 | - def __str__(self): | |
1641 | - return (str(self.__dict__)) | |
1642 | - | |
1643 | - ''' | |
1644 | - TODO : This function is Ugly, | |
1645 | - we should change this with a function pointer array | |
1646 | - and setters getters for each attribute | |
1647 | - ''' | |
1648 | - | |
1649 | - def setValue(self, key, value, unit=""): | |
1650 | - if key == "Temperature_outside": | |
1651 | - self.outside_temp = value | |
1652 | - self.outside_temp_unit = unit | |
1653 | - elif key == "Humidity_outside": | |
1654 | - self.outside_humidity = value | |
1655 | - self.outside_humidity_unit = unit | |
1656 | - elif key == "_Pressure": | |
1657 | - self.pressure = value | |
1658 | - self.pressure_unit = unit | |
1659 | - elif key == "Rain_boolean": # RainRate | |
1660 | - self.rain_rate = value | |
1661 | - self.rain_rate_unit = 'boulean' | |
1662 | - elif key == "Wind_speed": | |
1663 | - self.wind_speed = value | |
1664 | - self.wind_speed_unit = unit | |
1665 | - elif key == "Wind_dir": | |
1666 | - self.wind_dir = value | |
1667 | - self.wind_dir_unit = unit | |
1668 | - elif key == "_DewPoint": | |
1669 | - self.dew_point = value | |
1670 | - self.dew_point_unit = unit | |
1671 | - elif key == "_analog": | |
1672 | - self.analog = value | |
1673 | - self.analog_unit = unit | |
1674 | - elif key == "_digital": | |
1675 | - self.digital = value | |
1676 | - self.digital_unit = unit | |
1677 | - elif key == "_InsideTemp": | |
1678 | - self.inside_temp = value | |
1679 | - self.inside_temp_unit = unit | |
1680 | - elif key == "_InsideHumidity": | |
1681 | - self.inside_humidity = value | |
1682 | - self.inside_humidity_unit = unit | |
1683 | - elif key == "_WindDirCardinal": | |
1684 | - self.wind_dir_cardinal = value | |
1685 | - self.wind_dir_cardinal_unit = unit | |
1686 | - elif key == "_SensorTemperature": | |
1687 | - self.sensor_temperature = value | |
1688 | - self.sensor_temperature_unit = unit | |
1689 | - elif key == "_SkyTemperature": | |
1690 | - self.sky_temperature = value | |
1691 | - self.sky_temperature_unit = unit | |
1692 | - # PM 20190222 try patch | |
1693 | - elif key == "Error_code": | |
1694 | - self.status = value | |
1695 | - elif key == "current": | |
1696 | - self.current = value | |
1697 | - self.current_unit = unit | |
1698 | - elif key == "mode": | |
1699 | - self.plc_mode = value | |
1700 | - elif key == "is_safe": | |
1701 | - self.is_safe = value | |
1702 | - elif key == "LIGHTS": | |
1703 | - self.lights = value | |
1704 | - elif key == "SHUTTERS": | |
1705 | - self.shutters = value | |
1706 | - else: | |
1707 | - # PM 20190222 ignore unrecognized | |
1708 | - #raise KeyError("Key " + str(key) + " unrecognized") | |
1709 | - pass | |
1710 | - | |
1711 | - | |
1712 | -class PlcDevice(Device): | |
1713 | - #device = models.ForeignKey('Plc', on_delete=models.CASCADE, related_name='plc_devices') | |
1714 | - name = models.CharField(max_length=45, blank=True, null=True) | |
1715 | - desc = models.TextField(blank=True, null=True) | |
1716 | - created = models.DateTimeField(blank=True, null=True, auto_now_add=True) | |
1717 | - updated = models.DateTimeField(blank=True, null=True, auto_now=True) | |
1718 | - | |
1719 | - class Meta: | |
1720 | - managed = True | |
1721 | - db_table = 'plc_devices' | |
1722 | - | |
1723 | - def __str__(self): | |
1724 | - return str(self.name) | |
1725 | - | |
1726 | 1412 | |
1727 | 1413 | # class Plc(Device): |
1728 | 1414 | # last_update_status = models.DateTimeField(blank=True, null=True) |
... | ... | @@ -2474,50 +2160,6 @@ class TaskId(models.Model): |
2474 | 2160 | return (str(self.task) + " - " + str(self.task_id)) |
2475 | 2161 | |
2476 | 2162 | |
2477 | -class Telescope(Device): | |
2478 | - TELESCOPE = "Telescope" | |
2479 | - | |
2480 | - mount_type = models.CharField(max_length=9, blank=True, null=True) | |
2481 | - diameter = models.FloatField(blank=True, null=True) | |
2482 | - latitude = models.FloatField(blank=True, null=True) | |
2483 | - longitude = models.FloatField(blank=True, null=True) | |
2484 | - sens = models.CharField(max_length=1, blank=True, null=True) | |
2485 | - altitude = models.FloatField(blank=True, null=True) | |
2486 | - readout_time = models.IntegerField(blank=True, null=True) | |
2487 | - slew_time = models.IntegerField(blank=True, null=True) | |
2488 | - slew_dead = models.IntegerField(blank=True, null=True) | |
2489 | - slew_rate_max = models.FloatField(blank=True, null=True) | |
2490 | - horizon_type = models.CharField(max_length=45, blank=True, null=True) | |
2491 | - horizon_def = models.FloatField(blank=True, null=True) | |
2492 | - lim_dec_max = models.FloatField(blank=True, null=True) | |
2493 | - lim_dec_min = models.FloatField(blank=True, null=True) | |
2494 | - lim_ha_rise = models.FloatField(blank=True, null=True) | |
2495 | - lim_ha_set = models.FloatField(blank=True, null=True) | |
2496 | - address = models.CharField(max_length=45, blank=True, null=True) | |
2497 | - night_elev_sun = models.FloatField(blank=True, null=True) | |
2498 | - mpc_code = models.CharField(max_length=45, blank=True, null=True) | |
2499 | - | |
2500 | - class Meta: | |
2501 | - managed = True | |
2502 | - db_table = 'telescope' | |
2503 | - | |
2504 | - def __str__(self): | |
2505 | - return (self.name) | |
2506 | - | |
2507 | - | |
2508 | -class TelescopeCommand(models.Model): | |
2509 | - created = models.DateTimeField(blank=True, null=True, auto_now_add=True) | |
2510 | - answered = models.DateTimeField(blank=True, null=True) | |
2511 | - request = models.CharField(blank=False, null=False, max_length=255) | |
2512 | - answer = models.TextField(null=True, blank=True) | |
2513 | - | |
2514 | - class Meta: | |
2515 | - managed = True | |
2516 | - db_table = "telescopecommand" | |
2517 | - | |
2518 | - def __str__(self): | |
2519 | - return str(self.request) + str(self.created) | |
2520 | - | |
2521 | 2163 | |
2522 | 2164 | class UserLevel(models.Model): |
2523 | 2165 | name = models.CharField(max_length=45, blank=True, null=True) |
... | ... | @@ -2678,6 +2320,9 @@ class Majordome(models.Model): |
2678 | 2320 | default = DAY_MODE, |
2679 | 2321 | max_length=15 |
2680 | 2322 | ) |
2323 | + class Meta: | |
2324 | + managed = True | |
2325 | + db_table = 'majordome' | |
2681 | 2326 | |
2682 | 2327 | @classmethod |
2683 | 2328 | def object(cls): |
... | ... | @@ -2710,4 +2355,9 @@ class Tickets: |
2710 | 2355 | (LEVEL_FOUR,"Known issue without immediate solution"), |
2711 | 2356 | (LEVEL_FIVE,"Issue not categorized until it happened") |
2712 | 2357 | ) |
2713 | - security_level = models.TextField(choices=SECURITY_LEVEL_CHOICES, default=LEVEL_ONE) | |
2714 | 2358 | \ No newline at end of file |
2359 | + security_level = models.TextField(choices=SECURITY_LEVEL_CHOICES, default=LEVEL_ONE) | |
2360 | + | |
2361 | + class Meta: | |
2362 | + managed = True | |
2363 | + db_table = 'tickets' | |
2364 | + verbose_name_plural = "tickets" | |
2715 | 2365 | \ No newline at end of file | ... | ... |
src/core/pyros_django/dashboard/templates/dashboard/agent_detail.html
... | ... | @@ -33,7 +33,10 @@ |
33 | 33 | .violet{ |
34 | 34 | color : violet; |
35 | 35 | } |
36 | - .table{ | |
36 | + #agentsst_table{ | |
37 | + max-width: fit-content; | |
38 | + } | |
39 | + #cmd_table{ | |
37 | 40 | /*max-width: 90vw;*/ |
38 | 41 | table-layout: fixed; |
39 | 42 | } |
... | ... | @@ -46,12 +49,17 @@ |
46 | 49 | overflow:hidden; |
47 | 50 | word-wrap: break-word; |
48 | 51 | } |
52 | + .date_td{ | |
53 | + max-width: fit-content; | |
54 | + word-wrap: none; | |
55 | + overflow: none; | |
56 | + } | |
49 | 57 | #td_result{ |
50 | 58 | max-width:20vw; |
51 | 59 | } |
52 | 60 | .theader{ |
53 | 61 | text-align: center; |
54 | - min-width: 7vw; | |
62 | + //min-width:7vw; | |
55 | 63 | max-width: 12vw; |
56 | 64 | vertical-align:top; |
57 | 65 | } |
... | ... | @@ -119,13 +127,13 @@ |
119 | 127 | {% endcomment %} |
120 | 128 | |
121 | 129 | <h2 id="cmd_unimplemented_message" style="color:red;"></h2> |
122 | - <h2 id="cmdform_exiting" style="color:red;display:none" > {{ agent_name }} is not available.</h2> | |
130 | + <h2 id="cmdform_exiting" style="color:red;display:none" > {{ agent_name }} is OFF.</h2> | |
123 | 131 | |
124 | 132 | {% endif %} |
125 | 133 | {% if managed_agents != None %} |
126 | 134 | <div> |
127 | 135 | <h2> Managed agents </h2> |
128 | - <table | |
136 | + <table id="agentsst_table" | |
129 | 137 | class="table table-sm table-bordered tablesorter"> |
130 | 138 | <thead> |
131 | 139 | <tr> |
... | ... | @@ -202,10 +210,10 @@ |
202 | 210 | <th class="theader align-top">Status </th> |
203 | 211 | <th class="theader align-top small-cln">Validity<br> (s)</th> |
204 | 212 | <th class="theader align-top small-cln">Timeout<br> (s)</th> |
205 | - <th class="theader align-top">Deposit<br> (UTC)</th> | |
206 | - <th class="theader align-top">Read<br> (UTC)</th> | |
207 | - <th class="theader align-top">Exec start <br>(UTC)</th> | |
208 | - <th class="theader align-top">Exec end <br>(UTC)</th> | |
213 | + <th class="theader align-top date_td">Deposit<br> (UTC)</th> | |
214 | + <th class="theader align-top date_td">Read<br> (UTC)</th> | |
215 | + <th class="theader align-top date_td">Exec start <br>(UTC)</th> | |
216 | + <th class="theader align-top date_td">Exec end <br>(UTC)</th> | |
209 | 217 | <th class="theader align-top small-cln">Exec time <br>(s)</th> |
210 | 218 | <th class="theader align-top">Result</th> |
211 | 219 | </tr> | ... | ... |
src/core/pyros_django/dashboard/views.py
... | ... | @@ -9,7 +9,11 @@ from django.contrib.auth.decorators import login_required |
9 | 9 | import datetime |
10 | 10 | from datetime import timezone |
11 | 11 | from django.core.paginator import Paginator |
12 | -from common.models import Log, SP_Period_Guest, WeatherWatch, SiteWatch, ScientificProgram, Config, PyrosUser, PlcDeviceStatus, Telescope, TelescopeCommand, UserLevel, WeatherWatchHistory, Majordome, AgentSurvey, AgentCmd | |
12 | + | |
13 | +#from common.models import Log, SP_Period_Guest, WeatherWatch, SiteWatch, ScientificProgram, Config, PyrosUser, PlcDeviceStatus, Telescope, TelescopeCommand, UserLevel, WeatherWatchHistory, Majordome, AgentSurvey, AgentCmd | |
14 | +from common.models import Log, SP_Period_Guest, WeatherWatch, SiteWatch, ScientificProgram, Config, PyrosUser, UserLevel, WeatherWatchHistory, Majordome, AgentSurvey, AgentCmd | |
15 | +from devices.models import PlcDeviceStatus, Telescope, TelescopeCommand | |
16 | + | |
13 | 17 | from django.core import serializers |
14 | 18 | import utils.Logger as l |
15 | 19 | from django.forms import modelformset_factory | ... | ... |
src/core/pyros_django/devices/models.py
1 | -from django.db import models | |
1 | +#from django.db import models | |
2 | 2 | |
3 | 3 | # Create your models here. |
4 | + | |
5 | +##from __future__ import unicode_literals | |
6 | + | |
7 | +# (EP 21/9/22) To allow autoreferencing (ex: AgentCmd.create() returns a AgentCmd) | |
8 | +from __future__ import annotations | |
9 | + | |
10 | +# Stdlib imports | |
11 | +from numpy import False_ | |
12 | +from src.device_controller.abstract_component.device_controller import DeviceCmd | |
13 | +from enum import Enum | |
14 | +from datetime import datetime, timedelta, date | |
15 | +from dateutil.relativedelta import relativedelta | |
16 | +import os | |
17 | +import sys | |
18 | +from typing import Any, List, Tuple, Optional | |
19 | +import re | |
20 | + | |
21 | +# Django imports | |
22 | +from django.core.validators import MaxValueValidator, MinValueValidator | |
23 | + | |
24 | +# DJANGO imports | |
25 | +from django.contrib.auth.models import AbstractUser, UserManager | |
26 | +from django.db import models | |
27 | +from django.db.models import Q, Max | |
28 | +from django.core.validators import MaxValueValidator, MinValueValidator | |
29 | +from django.db.models.deletion import DO_NOTHING | |
30 | +from django.db.models.expressions import F | |
31 | +from django.db.models.query import QuerySet | |
32 | +from model_utils import Choices | |
33 | +from django.utils import timezone | |
34 | +# Project imports | |
35 | +# DeviceCommand is used by class Command | |
36 | +sys.path.append("../../..") | |
37 | + | |
38 | +''' | |
39 | +NOT USED - to be removed | |
40 | +class PyrosState(Enum): | |
41 | + START = 'Starting' | |
42 | + PA = 'Passive' | |
43 | + INI = "INIT" | |
44 | + STAND = "Standby" | |
45 | + SCHED_START = 'Scheduler startup' | |
46 | + SCHED = 'Scheduler' | |
47 | + SCHED_CLOSE = 'Scheduler closing' | |
48 | +''' | |
49 | + | |
50 | + | |
51 | +""" | |
52 | +STYLE RULES | |
53 | +=========== | |
54 | +https://simpleisbetterthancomplex.com/tips/2018/02/10/django-tip-22-designing-better-models.html | |
55 | +https://steelkiwi.com/blog/best-practices-working-django-models-python/ | |
56 | + | |
57 | +- Model name => singular | |
58 | + Call it Company instead of Companies. | |
59 | + A model definition is the representation of a single object (the object in this example is a company), | |
60 | + and not a collection of companies | |
61 | + The model definition is a class, so always use CapWords convention (no underscores) | |
62 | + E.g. User, Permission, ContentType, etc. | |
63 | + | |
64 | +- For the modelโs attributes use snake_case. | |
65 | + E.g. first_name, last_name, etc | |
66 | + | |
67 | +- Blank and Null Fields (https://simpleisbetterthancomplex.com/tips/2016/07/25/django-tip-8-blank-or-null.html) | |
68 | + - Null: It is database-related. Defines if a given database column will accept null values or not. | |
69 | + - Blank: It is validation-related. It will be used during forms validation, when calling form.is_valid(). | |
70 | + Do not use null=True for text-based fields that are optional. | |
71 | + Otherwise, you will end up having two possible values for โno dataโ, that is: None and an empty string. | |
72 | + Having two possible values for โno dataโ is redundant. | |
73 | + The Django convention is to use the empty string, not NULL. | |
74 | + Example: | |
75 | + # The default values of `null` and `blank` are `False`. | |
76 | + class Person(models.Model): | |
77 | + name = models.CharField(max_length=255) # Mandatory | |
78 | + bio = models.TextField(max_length=500, blank=True) # Optional (don't put null=True) | |
79 | + birth_date = models.DateField(null=True, blank=True) # Optional (here you may add null=True) | |
80 | + The default values of null and blank are False. | |
81 | + Special case, when you need to accept NULL values for a BooleanField, use NullBooleanField instead. | |
82 | + | |
83 | +- Choices : you can use Choices from the model_utils library. Take model Article, for instance: | |
84 | + from model_utils import Choices | |
85 | + class Article(models.Model): | |
86 | + STATUSES = Choices( | |
87 | + (0, 'draft', _('draft')), | |
88 | + (1, 'published', _('published')) ) | |
89 | + status = models.IntegerField(choices=STATUSES, default=STATUSES.draft) | |
90 | + | |
91 | +- Reverse Relationships | |
92 | + | |
93 | + - related_name : | |
94 | + Rule of thumb: if you are not sure what would be the related_name, | |
95 | + use the plural of the model holding the ForeignKey. | |
96 | + ex: | |
97 | + class Company: | |
98 | + name = models.CharField(max_length=30) | |
99 | + class Employee: | |
100 | + first_name = models.CharField(max_length=30) | |
101 | + last_name = models.CharField(max_length=30) | |
102 | + company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='employees') | |
103 | + usage: | |
104 | + google = Company.objects.get(name='Google') | |
105 | + google.employees.all() | |
106 | + You can also use the reverse relationship to modify the company field on the Employee instances: | |
107 | + vitor = Employee.objects.get(first_name='Vitor') | |
108 | + google = Company.objects.get(name='Google') | |
109 | + google.employees.add(vitor) | |
110 | + | |
111 | + - related_query_name : | |
112 | + This kind of relationship also applies to query filters. | |
113 | + For example, if I wanted to list all companies that employs people named โVitorโ, I could do the following: | |
114 | + companies = Company.objects.filter(employee__first_name='Vitor') | |
115 | + If you want to customize the name of this relationship, here is how we do it: | |
116 | + class Employee: | |
117 | + first_name = models.CharField(max_length=30) | |
118 | + last_name = models.CharField(max_length=30) | |
119 | + company = models.ForeignKey( | |
120 | + Company, | |
121 | + on_delete=models.CASCADE, | |
122 | + related_name='employees', | |
123 | + related_query_name='person' | |
124 | + ) | |
125 | + Then the usage would be: | |
126 | + companies = Company.objects.filter(person__first_name='Vitor') | |
127 | + | |
128 | + To use it consistently, related_name goes as plural and related_query_name goes as singular. | |
129 | + | |
130 | + | |
131 | +GENERAL EXAMPLE | |
132 | +======= | |
133 | + | |
134 | +from django.db import models | |
135 | +from django.urls import reverse | |
136 | + | |
137 | +class Company(models.Model): | |
138 | + # CHOICES | |
139 | + PUBLIC_LIMITED_COMPANY = 'PLC' | |
140 | + PRIVATE_COMPANY_LIMITED = 'LTD' | |
141 | + LIMITED_LIABILITY_PARTNERSHIP = 'LLP' | |
142 | + COMPANY_TYPE_CHOICES = ( | |
143 | + (PUBLIC_LIMITED_COMPANY, 'Public limited company'), | |
144 | + (PRIVATE_COMPANY_LIMITED, 'Private company limited by shares'), | |
145 | + (LIMITED_LIABILITY_PARTNERSHIP, 'Limited liability partnership'), | |
146 | + ) | |
147 | + | |
148 | + # DATABASE FIELDS | |
149 | + name = models.CharField('name', max_length=30) | |
150 | + vat_identification_number = models.CharField('VAT', max_length=20) | |
151 | + company_type = models.CharField('type', max_length=3, choices=COMPANY_TYPE_CHOICES) | |
152 | + | |
153 | + # MANAGERS | |
154 | + objects = models.Manager() | |
155 | + limited_companies = LimitedCompanyManager() | |
156 | + | |
157 | + # META CLASS | |
158 | + class Meta: | |
159 | + verbose_name = 'company' | |
160 | + verbose_name_plural = 'companies' | |
161 | + | |
162 | + # TO STRING METHOD | |
163 | + def __str__(self): | |
164 | + return self.name | |
165 | + | |
166 | + # SAVE METHOD | |
167 | + def save(self, *args, **kwargs): | |
168 | + do_something() | |
169 | + super().save(*args, **kwargs) # Call the "real" save() method. | |
170 | + do_something_else() | |
171 | + | |
172 | + # ABSOLUTE URL METHOD | |
173 | + def get_absolute_url(self): | |
174 | + return reverse('company_details', kwargs={'pk': self.id}) | |
175 | + | |
176 | + # OTHER METHODS | |
177 | + def process_invoices(self): | |
178 | + do_something() | |
179 | + | |
180 | +""" | |
181 | + | |
182 | + | |
183 | +''' | |
184 | +# --- | |
185 | +# --- Utility functions | |
186 | +# --- | |
187 | + | |
188 | +def printd(*args, **kwargs): | |
189 | + if os.environ.get('PYROS_DEBUG', '0') == '1': | |
190 | + print('(MODEL)', *args, **kwargs) | |
191 | + | |
192 | +def get_or_create_unique_row_from_model(model: models.Model): | |
193 | + # return model.objects.get(id=1) if model.objects.exists() else model.objects.create(id=1) | |
194 | + return model.objects.first() if model.objects.exists() else model.objects.create(id=1) | |
195 | +''' | |
196 | + | |
197 | + | |
198 | +""" | |
199 | +------------------------ | |
200 | + BASE MODEL CLASSES | |
201 | +------------------------ | |
202 | +""" | |
203 | + | |
204 | + | |
205 | +class Device(models.Model): | |
206 | + name = models.CharField(max_length=45, blank=True, null=True) | |
207 | + desc = models.TextField(blank=True, null=True) | |
208 | + created = models.DateTimeField(blank=True, null=True, auto_now_add=True) | |
209 | + updated = models.DateTimeField(blank=True, null=True, auto_now=True) | |
210 | + is_online = models.BooleanField(default=False) | |
211 | + status = models.CharField(max_length=11, blank=True, null=True) | |
212 | + maintenance_date = models.DateTimeField(blank=True, null=True) | |
213 | + | |
214 | + class Meta: | |
215 | + abstract = True | |
216 | + | |
217 | + def __str__(self): | |
218 | + return (str(self.name)) | |
219 | + | |
220 | + | |
221 | +''' | |
222 | +class Image(models.Model): | |
223 | + plan = models.ForeignKey( | |
224 | + 'Plan', on_delete=models.CASCADE, related_name="images") | |
225 | + nrtanalysis = models.ForeignKey( | |
226 | + 'NrtAnalysis', models.DO_NOTHING, blank=True, null=True, related_name="images") | |
227 | + name = models.CharField(max_length=45, blank=True, null=True) | |
228 | + desc = models.TextField(blank=True, null=True) | |
229 | + created = models.DateTimeField(blank=True, null=True, auto_now_add=True) | |
230 | + updated = models.DateTimeField(blank=True, null=True, auto_now=True) | |
231 | + date_from_gps = models.CharField(max_length=45, blank=True, null=True) | |
232 | + level = models.IntegerField(blank=True, null=True) | |
233 | + type = models.CharField(max_length=5, blank=True, null=True) | |
234 | + quality = models.CharField(max_length=45, blank=True, null=True) | |
235 | + flaggps = models.CharField(max_length=45, blank=True, null=True) | |
236 | + exposure = models.CharField(max_length=45, blank=True, null=True) | |
237 | + tempext = models.CharField(max_length=45, blank=True, null=True) | |
238 | + pressure = models.CharField(max_length=45, blank=True, null=True) | |
239 | + humidext = models.CharField(max_length=45, blank=True, null=True) | |
240 | + wind = models.CharField(max_length=45, blank=True, null=True) | |
241 | + wind_dir = models.CharField(max_length=45, blank=True, null=True) | |
242 | + dwnimg = models.CharField(max_length=45, blank=True, null=True) | |
243 | + dwncata = models.CharField(max_length=45, blank=True, null=True) | |
244 | + dwn = models.CharField(max_length=45, blank=True, null=True) | |
245 | + level0_fits_name = models.CharField(max_length=45, blank=True, null=True) | |
246 | + level1a_fits_name = models.CharField(max_length=45, blank=True, null=True) | |
247 | + level1b_fits_name = models.CharField(max_length=45, blank=True, null=True) | |
248 | + | |
249 | + class Meta: | |
250 | + managed = True | |
251 | + db_table = 'image' | |
252 | + | |
253 | + def __str__(self): | |
254 | + return (str(self.name)) | |
255 | +''' | |
256 | + | |
257 | + | |
258 | +""" | |
259 | +------------------------ | |
260 | + OTHER MODEL CLASSES | |
261 | +------------------------ | |
262 | +""" | |
263 | + | |
264 | +# TODO: A VIRER car remplacรฉ par AgentDeviceStatus | |
265 | + | |
266 | + | |
267 | +class AgentDeviceTelescopeStatus(models.Model): | |
268 | + #created = models.DateTimeField('status date', blank=True, null=True, auto_now_add=True) | |
269 | + updated = models.DateTimeField( | |
270 | + 'status date', blank=True, null=True, auto_now=True) | |
271 | + radec = models.CharField('agent mode', max_length=30, blank=True) | |
272 | + | |
273 | + class Meta: | |
274 | + managed = True | |
275 | + db_table = 'agent_device_telescope_status' | |
276 | + #verbose_name = "agent survey" | |
277 | + #verbose_name_plural = "agents survey" | |
278 | + | |
279 | + """ | |
280 | + def __str__(self): | |
281 | + return (f"Agent {self.name} at {self.updated} in mode {self.mode} and status {self.status}") | |
282 | + """ | |
283 | + | |
284 | + | |
285 | +class AgentDeviceStatus(models.Model): | |
286 | + """Table storing various status parameters for EACH Device. | |
287 | + | |
288 | + Attributes: | |
289 | + attr1 (str): Description of `attr1`. | |
290 | + attr2 (:obj:`int`, optional): Description of `attr2`. | |
291 | + | |
292 | + """ | |
293 | + #created = models.DateTimeField('status date', blank=True, null=True, auto_now_add=True) | |
294 | + agent = models.CharField( | |
295 | + 'Name of the agent that saved this parameter', max_length=45, blank=True, null=True) | |
296 | + #radec = models.CharField('agent mode', max_length=30, blank=True) | |
297 | + status = models.CharField( | |
298 | + 'status parameters json dictionnary (ex: {radec:..., speed:...})', max_length=300, blank=True, null=True) | |
299 | + date_updated = models.DateTimeField( | |
300 | + 'status parameter date', blank=True, null=True, auto_now=True) | |
301 | + | |
302 | + class Meta: | |
303 | + managed = True | |
304 | + db_table = 'agent_device_status' | |
305 | + verbose_name = "agent device status" | |
306 | + verbose_name_plural = "agent devices status" | |
307 | + | |
308 | + def __str__(self): | |
309 | + return (f"Agent {self.agent} last status is ({self.status}) (saved at {self.date_updated})") | |
310 | + | |
311 | + @classmethod | |
312 | + def getStatusForAgent(cls, agent: str) -> str: | |
313 | + return cls.objects.filter(agent=agent)[0] if cls.objects.filter(agent=agent).exists() else cls.objects.create(agent=agent) | |
314 | + ''' | |
315 | + return cls.objects.filter(agent=agent)[0].status if cls.objects.filter(agent=agent).exists() else cls.objects.create(agent=agent).status | |
316 | + agent_status = cls.objects.filter(agent=agent) | |
317 | + if agent_status.exists(): | |
318 | + return agent_status[0].status | |
319 | + else: | |
320 | + return cls.objects.create(agent=agent) | |
321 | + ''' | |
322 | + | |
323 | + | |
324 | + | |
325 | +class PlcDeviceStatus(models.Model): | |
326 | + device = models.ForeignKey( | |
327 | + 'PlcDevice', on_delete=models.CASCADE, related_name='current_status') | |
328 | + created = models.DateTimeField( | |
329 | + auto_now_add=True, editable=False, blank=True) | |
330 | + outside_temp = models.DecimalField( | |
331 | + max_digits=15, decimal_places=8, blank=True, null=True) | |
332 | + outside_temp_unit = models.CharField(max_length=45, blank=True, null=True) | |
333 | + outside_humidity = models.DecimalField( | |
334 | + max_digits=15, decimal_places=8, blank=True, null=True) | |
335 | + outside_humidity_unit = models.CharField( | |
336 | + max_length=45, blank=True, null=True) | |
337 | + pressure = models.DecimalField( | |
338 | + max_digits=15, decimal_places=8, blank=True, null=True) | |
339 | + pressure_unit = models.CharField(max_length=45, blank=True, null=True) | |
340 | + rain_rate = models.DecimalField( | |
341 | + max_digits=15, decimal_places=8, blank=True, null=True) | |
342 | + rain_rate_unit = models.CharField(max_length=45, blank=True, null=True) | |
343 | + wind_speed = models.DecimalField( | |
344 | + max_digits=15, decimal_places=8, blank=True, null=True) | |
345 | + wind_speed_unit = models.CharField(max_length=45, blank=True, null=True) | |
346 | + wind_dir = models.DecimalField( | |
347 | + max_digits=15, decimal_places=8, blank=True, null=True) | |
348 | + wind_dir_unit = models.CharField(max_length=45, blank=True, null=True) | |
349 | + dew_point = models.DecimalField( | |
350 | + max_digits=15, decimal_places=8, blank=True, null=True) | |
351 | + dew_point_unit = models.CharField(max_length=45, blank=True, null=True) | |
352 | + analog = models.DecimalField( | |
353 | + max_digits=15, decimal_places=8, blank=True, null=True) | |
354 | + analog_unit = models.CharField(max_length=45, blank=True, null=True) | |
355 | + digital = models.DecimalField( | |
356 | + max_digits=15, decimal_places=8, blank=True, null=True) | |
357 | + digital_unit = models.CharField(max_length=45, blank=True, null=True) | |
358 | + inside_temp = models.DecimalField( | |
359 | + max_digits=15, decimal_places=8, blank=True, null=True) | |
360 | + inside_temp_unit = models.CharField(max_length=45, blank=True, null=True) | |
361 | + inside_humidity = models.DecimalField( | |
362 | + max_digits=15, decimal_places=8, blank=True, null=True) | |
363 | + inside_humidity_unit = models.CharField( | |
364 | + max_length=45, blank=True, null=True) | |
365 | + wind_dir_cardinal = models.CharField(max_length=45, blank=True, null=True) | |
366 | + wind_dir_cardinal_unit = models.CharField( | |
367 | + max_length=45, blank=True, null=True) | |
368 | + sensor_temperature = models.DecimalField( | |
369 | + max_digits=15, decimal_places=8, blank=True, null=True) | |
370 | + sensor_temperature_unit = models.CharField( | |
371 | + max_length=45, blank=True, null=True) | |
372 | + sky_temperature = models.DecimalField( | |
373 | + max_digits=15, decimal_places=8, blank=True, null=True) | |
374 | + sky_temperature_unit = models.CharField( | |
375 | + max_length=45, blank=True, null=True) | |
376 | + status = models.CharField(max_length=45, blank=True, null=True) | |
377 | + current = models.DecimalField( | |
378 | + max_digits=15, decimal_places=8, blank=True, null=True) | |
379 | + current_unit = models.CharField(max_length=45, blank=True, null=True) | |
380 | + is_safe = models.BooleanField(default=True) | |
381 | + plc_mode = models.CharField(max_length=4, null=True) | |
382 | + lights = models.CharField(max_length=3, null=True) | |
383 | + shutters = models.CharField(max_length=5, null=True) | |
384 | + | |
385 | + class Meta: | |
386 | + managed = True | |
387 | + db_table = 'plc_devices_status' | |
388 | + | |
389 | + def __str__(self): | |
390 | + return (str(self.__dict__)) | |
391 | + | |
392 | + """ | |
393 | + TODO : This function is Ugly, | |
394 | + we should change this with a function pointer array | |
395 | + and setters getters for each attribute | |
396 | + """ | |
397 | + def setValue(self, key, value, unit=""): | |
398 | + if key == "Temperature_outside": | |
399 | + self.outside_temp = value | |
400 | + self.outside_temp_unit = unit | |
401 | + elif key == "Humidity_outside": | |
402 | + self.outside_humidity = value | |
403 | + self.outside_humidity_unit = unit | |
404 | + elif key == "_Pressure": | |
405 | + self.pressure = value | |
406 | + self.pressure_unit = unit | |
407 | + elif key == "Rain_boolean": # RainRate | |
408 | + self.rain_rate = value | |
409 | + self.rain_rate_unit = 'boulean' | |
410 | + elif key == "Wind_speed": | |
411 | + self.wind_speed = value | |
412 | + self.wind_speed_unit = unit | |
413 | + elif key == "Wind_dir": | |
414 | + self.wind_dir = value | |
415 | + self.wind_dir_unit = unit | |
416 | + elif key == "_DewPoint": | |
417 | + self.dew_point = value | |
418 | + self.dew_point_unit = unit | |
419 | + elif key == "_analog": | |
420 | + self.analog = value | |
421 | + self.analog_unit = unit | |
422 | + elif key == "_digital": | |
423 | + self.digital = value | |
424 | + self.digital_unit = unit | |
425 | + elif key == "_InsideTemp": | |
426 | + self.inside_temp = value | |
427 | + self.inside_temp_unit = unit | |
428 | + elif key == "_InsideHumidity": | |
429 | + self.inside_humidity = value | |
430 | + self.inside_humidity_unit = unit | |
431 | + elif key == "_WindDirCardinal": | |
432 | + self.wind_dir_cardinal = value | |
433 | + self.wind_dir_cardinal_unit = unit | |
434 | + elif key == "_SensorTemperature": | |
435 | + self.sensor_temperature = value | |
436 | + self.sensor_temperature_unit = unit | |
437 | + elif key == "_SkyTemperature": | |
438 | + self.sky_temperature = value | |
439 | + self.sky_temperature_unit = unit | |
440 | + # PM 20190222 try patch | |
441 | + elif key == "Error_code": | |
442 | + self.status = value | |
443 | + elif key == "current": | |
444 | + self.current = value | |
445 | + self.current_unit = unit | |
446 | + elif key == "mode": | |
447 | + self.plc_mode = value | |
448 | + elif key == "is_safe": | |
449 | + self.is_safe = value | |
450 | + elif key == "LIGHTS": | |
451 | + self.lights = value | |
452 | + elif key == "SHUTTERS": | |
453 | + self.shutters = value | |
454 | + else: | |
455 | + # PM 20190222 ignore unrecognized | |
456 | + #raise KeyError("Key " + str(key) + " unrecognized") | |
457 | + pass | |
458 | + | |
459 | + | |
460 | +class PlcDevice(Device): | |
461 | + #device = models.ForeignKey('Plc', on_delete=models.CASCADE, related_name='plc_devices') | |
462 | + name = models.CharField(max_length=45, blank=True, null=True) | |
463 | + desc = models.TextField(blank=True, null=True) | |
464 | + created = models.DateTimeField(blank=True, null=True, auto_now_add=True) | |
465 | + updated = models.DateTimeField(blank=True, null=True, auto_now=True) | |
466 | + | |
467 | + class Meta: | |
468 | + managed = True | |
469 | + db_table = 'plc_devices' | |
470 | + | |
471 | + def __str__(self): | |
472 | + return str(self.name) | |
473 | + | |
474 | + | |
475 | + | |
476 | + | |
477 | + | |
478 | +class Detector(Device): | |
479 | + VIS = "Visible camera" | |
480 | + NIR = "Cagire" | |
481 | + | |
482 | + telescope = models.ForeignKey( | |
483 | + 'Telescope', models.DO_NOTHING, related_name="detectors") | |
484 | + nb_photo_x = models.IntegerField(blank=True, null=True) | |
485 | + nb_photo_y = models.IntegerField(blank=True, null=True) | |
486 | + photo_size_x = models.IntegerField(blank=True, null=True) | |
487 | + photo_size_y = models.IntegerField(blank=True, null=True) | |
488 | + has_shutter = models.BooleanField(default=False) | |
489 | + equivalent_foc_len = models.CharField(max_length=45, blank=True, null=True) | |
490 | + acq_start = models.DateTimeField(blank=True, null=True) | |
491 | + acq_stop = models.DateTimeField(blank=True, null=True) | |
492 | + check_temp = models.FloatField(blank=True, null=True) | |
493 | + gain = models.FloatField(blank=True, null=True) | |
494 | + readout_noise = models.FloatField(blank=True, null=True) | |
495 | + readout_time = models.FloatField(blank=True, null=True) | |
496 | + idcam_readout_mode = models.IntegerField(blank=True, null=True) | |
497 | + | |
498 | + class Meta: | |
499 | + managed = True | |
500 | + db_table = 'detector' | |
501 | + | |
502 | + def __str__(self): | |
503 | + return str(self.name) | |
504 | + | |
505 | + def device_name(self): | |
506 | + return self.__str__() | |
507 | + device_name.short_description = "Name" | |
508 | + | |
509 | + | |
510 | +class Dome(Device): | |
511 | + DOME = "Dome" | |
512 | + | |
513 | + open = models.BooleanField(default=False, blank=True) | |
514 | + | |
515 | + class Meta: | |
516 | + managed = True | |
517 | + db_table = 'dome' | |
518 | + | |
519 | + def __str__(self): | |
520 | + return str(self.name) | |
521 | + | |
522 | + def device_name(self): | |
523 | + return self.__str__() | |
524 | + device_name.short_description = "Name" | |
525 | + | |
526 | + | |
527 | +class Filter(Device): | |
528 | + VIS_FILTER_1 = "First visible filter" | |
529 | + VIS_FILTER_2 = "Second visible filter" | |
530 | + NIR_FILTER_1 = "First infrared filter" | |
531 | + NIR_FILTER_2 = "Second infrared filter" | |
532 | + | |
533 | + filter_wheel = models.ForeignKey( | |
534 | + "FilterWheel", models.DO_NOTHING, related_name="filters", blank=True, null=True) | |
535 | + category = models.CharField(max_length=1, blank=True, null=True) | |
536 | + transmission_curve_doc = models.CharField( | |
537 | + max_length=45, blank=True, null=True) | |
538 | + | |
539 | + class Meta: | |
540 | + managed = True | |
541 | + db_table = 'filter' | |
542 | + | |
543 | + def __str__(self): | |
544 | + return (str(self.name)) | |
545 | + | |
546 | + def device_name(self): | |
547 | + return self.__str__() | |
548 | + device_name.short_description = "Name" | |
549 | + | |
550 | + | |
551 | +class FilterWheel(Device): | |
552 | + detector = models.OneToOneField(Detector, on_delete=models.CASCADE, | |
553 | + related_name="filter_wheel", blank=True, null=True) | |
554 | + | |
555 | + class Meta: | |
556 | + managed = True | |
557 | + db_table = 'filter_wheel' | |
558 | + | |
559 | + def __str__(self): | |
560 | + return (str(self.name)) | |
561 | + | |
562 | + def device_name(self): | |
563 | + return self.__str__() | |
564 | + device_name.short_description = "Name" | |
565 | + | |
566 | + | |
567 | +class Telescope(Device): | |
568 | + TELESCOPE = "Telescope" | |
569 | + | |
570 | + mount_type = models.CharField(max_length=9, blank=True, null=True) | |
571 | + diameter = models.FloatField(blank=True, null=True) | |
572 | + latitude = models.FloatField(blank=True, null=True) | |
573 | + longitude = models.FloatField(blank=True, null=True) | |
574 | + sens = models.CharField(max_length=1, blank=True, null=True) | |
575 | + altitude = models.FloatField(blank=True, null=True) | |
576 | + readout_time = models.IntegerField(blank=True, null=True) | |
577 | + slew_time = models.IntegerField(blank=True, null=True) | |
578 | + slew_dead = models.IntegerField(blank=True, null=True) | |
579 | + slew_rate_max = models.FloatField(blank=True, null=True) | |
580 | + horizon_type = models.CharField(max_length=45, blank=True, null=True) | |
581 | + horizon_def = models.FloatField(blank=True, null=True) | |
582 | + lim_dec_max = models.FloatField(blank=True, null=True) | |
583 | + lim_dec_min = models.FloatField(blank=True, null=True) | |
584 | + lim_ha_rise = models.FloatField(blank=True, null=True) | |
585 | + lim_ha_set = models.FloatField(blank=True, null=True) | |
586 | + address = models.CharField(max_length=45, blank=True, null=True) | |
587 | + night_elev_sun = models.FloatField(blank=True, null=True) | |
588 | + mpc_code = models.CharField(max_length=45, blank=True, null=True) | |
589 | + | |
590 | + class Meta: | |
591 | + managed = True | |
592 | + db_table = 'telescope' | |
593 | + | |
594 | + def __str__(self): | |
595 | + return (self.name) | |
596 | + | |
597 | + | |
598 | +class TelescopeCommand(models.Model): | |
599 | + created = models.DateTimeField(blank=True, null=True, auto_now_add=True) | |
600 | + answered = models.DateTimeField(blank=True, null=True) | |
601 | + request = models.CharField(blank=False, null=False, max_length=255) | |
602 | + answer = models.TextField(null=True, blank=True) | |
603 | + | |
604 | + class Meta: | |
605 | + managed = True | |
606 | + db_table = "telescopecommand" | |
607 | + | |
608 | + def __str__(self): | |
609 | + return str(self.request) + str(self.created) | |
610 | + | |
4 | 611 | \ No newline at end of file | ... | ... |