Commit 0ca00ebdacc4277fc306d840f85630fc9672cfa2

Authored by Etienne Pallier
1 parent 4078466e
Exists in dev

refactorized and improved majordome

src/common/models.py
@@ -843,7 +843,11 @@ class Config(models.Model): @@ -843,7 +843,11 @@ class Config(models.Model):
843 telescope_ip_address = models.CharField(max_length=45, default="127.0.0.1") 843 telescope_ip_address = models.CharField(max_length=45, default="127.0.0.1")
844 camera_ip_address = models.CharField(max_length=45, default="127.0.0.1") 844 camera_ip_address = models.CharField(max_length=45, default="127.0.0.1")
845 plc_ip_address = models.CharField(max_length=45, default="127.0.0.1") 845 plc_ip_address = models.CharField(max_length=45, default="127.0.0.1")
  846 +
  847 + # TODO: changer ça, c'est pas clair du tout...
  848 + # True = mode Scheduler-standby, False = mode Remote !!!!
846 global_mode = models.BooleanField(default='True') 849 global_mode = models.BooleanField(default='True')
  850 +
847 ack = models.BooleanField(default='False') 851 ack = models.BooleanField(default='False')
848 bypass = models.BooleanField(default='True') 852 bypass = models.BooleanField(default='True')
849 lock = models.BooleanField(default='False') 853 lock = models.BooleanField(default='False')
src/dashboard/views.py
@@ -85,6 +85,7 @@ def retrieve_env_navbar(request): @@ -85,6 +85,7 @@ def retrieve_env_navbar(request):
85 plc_timeout = Config.objects.get(pk=1).plc_timeout_seconds 85 plc_timeout = Config.objects.get(pk=1).plc_timeout_seconds
86 timeout = (datetime.datetime.now() - plc_device_status.created).total_seconds() 86 timeout = (datetime.datetime.now() - plc_device_status.created).total_seconds()
87 weather[0]['max_sunelev'] = SUN_ELEV_DAY_THRESHOLD 87 weather[0]['max_sunelev'] = SUN_ELEV_DAY_THRESHOLD
  88 + #weather[0]['sunelev'] = 10
88 weather[0]['sunelev'] = get_sunelev() 89 weather[0]['sunelev'] = get_sunelev()
89 weather[0]["plc_mode"] = plc_mode 90 weather[0]["plc_mode"] = plc_mode
90 weather[0]["is_safe"] = is_safe 91 weather[0]["is_safe"] = is_safe
src/majordome/tasks.py
@@ -57,11 +57,15 @@ class Majordome(Task): @@ -57,11 +57,15 @@ class Majordome(Task):
57 57
58 config = None 58 config = None
59 plc_status = None 59 plc_status = None
60 - current_state = "Passive" 60 + current_state = "Starting"
61 61
62 # OCS-RESTART, OCS-SHUTDOWN, or NONE (default) 62 # OCS-RESTART, OCS-SHUTDOWN, or NONE (default)
63 closing_mode = "NONE" 63 closing_mode = "NONE"
64 64
  65 + NEED_TO_CLOSE = False
  66 + PLC_IS_SAFE = False
  67 +
  68 +
65 ''' 69 '''
66 OLD //// Function called by celery task 70 OLD //// Function called by celery task
67 Behavior: 71 Behavior:
@@ -85,8 +89,7 @@ class Majordome(Task): @@ -85,8 +89,7 @@ class Majordome(Task):
85 #self.loop() 89 #self.loop()
86 90
87 91
88 - RESTART = True  
89 - while RESTART: 92 + while True:
90 93
91 # SETUP 94 # SETUP
92 try : 95 try :
@@ -94,7 +97,7 @@ class Majordome(Task): @@ -94,7 +97,7 @@ class Majordome(Task):
94 except Config.ObjectDoesNotExist: 97 except Config.ObjectDoesNotExist:
95 return -1 98 return -1
96 99
97 - self.config.ntc = False 100 + #self.config.ntc = False
98 101
99 # Set STATE to STARTING 102 # Set STATE to STARTING
100 self.changeState("STARTING") 103 self.changeState("STARTING")
@@ -118,46 +121,60 @@ class Majordome(Task): @@ -118,46 +121,60 @@ class Majordome(Task):
118 121
119 # MAIN LOOP: iterate on current state 122 # MAIN LOOP: iterate on current state
120 #while self.plc_is_connected() != False and self.closing_mode == "RUNNING" : 123 #while self.plc_is_connected() != False and self.closing_mode == "RUNNING" :
121 - while self.plc_is_connected() and not self.is_closing_or_restarting(): 124 + while self.plc_is_connected() and not self.is_shuttingdown_or_restarting():
122 # Call behavior for the current state and set new current state (can stay the same if no state change) 125 # Call behavior for the current state and set new current state (can stay the same if no state change)
123 #self.behavior[self.current_state](self) 126 #self.behavior[self.current_state](self)
124 self.current_state = self.behavior[self.current_state](self) 127 self.current_state = self.behavior[self.current_state](self)
125 time.sleep(2) 128 time.sleep(2)
126 ''' 129 '''
127 while True: 130 while True:
128 - if self.is_closing_or_restarting(): break 131 + if self.current_state in ['STARTING', 'PASSIVE_NO_PLC', 'PASSIVE']:
  132 + if self.is_shuttingdown_or_restarting() and not self.NEED_TO_CLOSE: break
  133 + '''
129 if self.current_state not in ['STARTING', 'PASSIVE_NO_PLC']: 134 if self.current_state not in ['STARTING', 'PASSIVE_NO_PLC']:
130 - if not self.plc_is_connected():  
131 - self.changeState("PASSIVE") 135 + if self.plc_is_not_auto(): self.changeState("PASSIVE")
  136 + '''
132 self.current_state = self.do_behavior_for_state(self.current_state) 137 self.current_state = self.do_behavior_for_state(self.current_state)
133 138
134 -  
135 - # EXIT 139 +
  140 + '''
136 # Shutdown options change by the main program 141 # Shutdown options change by the main program
137 if self.closing_mode == "OCS-SHUTDOWN": 142 if self.closing_mode == "OCS-SHUTDOWN":
  143 + self.shutdown()
138 #self.closeBehaviour(self) 144 #self.closeBehaviour(self)
139 - self.behavior_closing()  
140 - #TODO: self.kill_all_agents()  
141 - RESTART = False 145 + #self.sub_behavior_closing()
  146 + break
  147 + '''
142 148
143 - elif self.closing_mode == "OCS-RESTART": 149 + if self.closing_mode == "OCS-RESTART":
144 self.config.majordome_restarted = True 150 self.config.majordome_restarted = True
145 self.config.save() 151 self.config.save()
146 #TODO: self.kill_all_agents() 152 #TODO: self.kill_all_agents()
147 - self.send_alarm_if_not_closed() 153 + #self.send_alarm_if_not_closed()
148 #self.run() 154 #self.run()
149 -  
150 - #TODO: rester dans la main loop ???  
151 - #elif self.closing_mode == "RUNNING" and self.plc_is_connected() == False: 155 +
  156 + '''
152 elif not self.plc_is_connected(): 157 elif not self.plc_is_connected():
153 #self.config.majordome_restarted = True 158 #self.config.majordome_restarted = True
154 #self.config.save() 159 #self.config.save()
155 self.changeState("Passive No PLC") 160 self.changeState("Passive No PLC")
156 self.send_alarm_if_not_closed() 161 self.send_alarm_if_not_closed()
157 #self.run() 162 #self.run()
  163 + '''
  164 +
  165 + self.shutdown()
  166 +
158 167
159 168
160 - def is_closing_or_restarting(self): 169 + def shutdown(self):
  170 + #TODO: write shutdown code (kill agents...)
  171 + print("OCS SHUTDOWN")
  172 +
  173 + def is_restarting(self):
  174 + return self.closing_mode == "OCS-RESTART"
  175 + def is_shuttingdown(self):
  176 + return self.closing_mode == "OCS-SHUTDOWN"
  177 + def is_shuttingdown_or_restarting(self):
161 return self.closing_mode != "NONE" 178 return self.closing_mode != "NONE"
162 179
163 180
@@ -172,14 +189,31 @@ class Majordome(Task): @@ -172,14 +189,31 @@ class Majordome(Task):
172 self.config.pyros_state = state 189 self.config.pyros_state = state
173 self.config.save() 190 self.config.save()
174 191
  192 + def _plc_is_not_auto(self):
  193 + if not self.plc_is_connected(): return True;
  194 + # now, self.plc_status has been updated, so check it:
  195 + return self.plc_status.plc_mode != "AUTO"
  196 + def plc_is_auto(self):
  197 + return not self._plc_is_not_auto()
  198 +
  199 + def plc_is_safe(self):
  200 + if not self.plc_is_connected(): return False;
  201 + # now, self.plc_status has been updated, so check it:
  202 + return self.plc_status.is_safe
  203 +
  204 +
  205 + def is_night(self):
  206 + return get_sunelev() < -10
175 207
  208 +
176 ''' 209 '''
177 Each function with behavior describes the behavior of a state, 210 Each function with behavior describes the behavior of a state,
178 they are all contained in a dictionary called in run() 211 they are all contained in a dictionary called in run()
179 ''' 212 '''
  213 +
180 def behavior_starting(self): 214 def behavior_starting(self):
181 #print("STARTING") 215 #print("STARTING")
182 - #if self.is_closing_or_restarting(): return 216 + #if self.is_shuttingdown_or_restarting(): return
183 if not self.config.majordome_restarted: 217 if not self.config.majordome_restarted:
184 #TODO: Do setup things... (start agents...) 218 #TODO: Do setup things... (start agents...)
185 time.sleep(2) 219 time.sleep(2)
@@ -194,90 +228,125 @@ class Majordome(Task): @@ -194,90 +228,125 @@ class Majordome(Task):
194 self.changeState("PASSIVE_NO_PLC") 228 self.changeState("PASSIVE_NO_PLC")
195 #return self.current_state 229 #return self.current_state
196 230
  231 +
197 def behavior_passive_no_plc(self): 232 def behavior_passive_no_plc(self):
198 #print("PASSIVE_NO_PLC") 233 #print("PASSIVE_NO_PLC")
199 - #if self.is_closing_or_restarting(): return 234 + #if self.is_shuttingdown_or_restarting(): return
200 #self.changeState("Passive No PLC") 235 #self.changeState("Passive No PLC")
201 print("Waiting for PLC connection") 236 print("Waiting for PLC connection")
202 # Wait until PCL is connected 237 # Wait until PCL is connected
203 while not self.plc_is_connected(): 238 while not self.plc_is_connected():
204 - if self.is_closing_or_restarting(): return 239 + time.sleep(2)
  240 + '''
  241 + if self.is_restarting():
  242 + self.changeState("STARTING")
  243 + return
  244 + '''
  245 + if self.is_shuttingdown_or_restarting(): return
205 print("PLC is connected") 246 print("PLC is connected")
206 # Set STATE to PASSIVE (with PLC) 247 # Set STATE to PASSIVE (with PLC)
207 self.changeState("Passive") 248 self.changeState("Passive")
208 #return self.current_state 249 #return self.current_state
209 250
  251 +
210 def behavior_passive(self): 252 def behavior_passive(self):
211 #print("Passive") 253 #print("Passive")
212 - #if self.is_closing_or_restarting(): return  
213 - if self.plc_status.plc_mode == "AUTO":  
214 - if not self.config.majordome_restarted: self.config.ntc = True 254 + #if self.is_shuttingdown_or_restarting(): return
  255 + #if self.plc_status.plc_mode == "AUTO":
  256 + if not self.plc_is_connected():
  257 + self.changeState("PASSIVE_NO_PLC")
  258 + return
  259 +
  260 + if self.plc_is_auto():
  261 + #if not self.config.majordome_restarted: self.config.ntc = True
  262 + if not self.config.majordome_restarted: self.NEED_TO_CLOSE=True
215 self.changeState("Standby") 263 self.changeState("Standby")
216 #return self.current_state 264 #return self.current_state
217 265
  266 +
218 def behavior_standby(self): 267 def behavior_standby(self):
219 #print("Standby") 268 #print("Standby")
220 - if self.config.ntc: 269 + #if self.config.ntc:
  270 + if self.is_shuttingdown() and not self.NEED_TO_CLOSE:
  271 + self.NEED_TO_CLOSE = True
  272 +
  273 + if self.NEED_TO_CLOSE:
221 self.changeState("Closing") 274 self.changeState("Closing")
222 - elif self.plc_status.plc_mode != "AUTO" or self.is_closing_or_restarting(): 275 + #self.sub_behavior_closing()
  276 + self.do_behavior_for_state("Closing")
  277 + self.changeState("Standby")
  278 +
  279 + #elif self.plc_status.plc_mode != "AUTO" or self.is_shuttingdown_or_restarting():
  280 + if not self.plc_is_auto() or self.is_shuttingdown_or_restarting():
223 self.changeState("Passive") 281 self.changeState("Passive")
224 - elif self.config.global_mode == False and self.config.lock == False: 282 + elif not self.config.global_mode and not self.config.lock:
225 self.changeState("Remote") 283 self.changeState("Remote")
226 - elif get_sunelev() < -10 and self.plc_status.is_safe == True and self.config.ack == True and self.config.lock == False : 284 +
  285 + elif self.is_night() and self.plc_is_safe() and self.config.ack and not self.config.lock :
227 self.changeState("Startup") 286 self.changeState("Startup")
228 #return self.current_state 287 #return self.current_state
229 288
  289 +
230 def behavior_remote(self): 290 def behavior_remote(self):
231 #print("Remote") 291 #print("Remote")
232 - if self.config.global_mode:  
233 - self.changeState("Standby")  
234 - elif self.config.lock:  
235 - self.config.ntc == True 292 + if self.config.global_mode or self.is_shuttingdown_or_restarting() or not self.plc_is_auto() or self.config.lock:
  293 + if not self.plc_is_auto() or self.config.lock or self.is_shuttingdown():
  294 + self.NEED_TO_CLOSE=True
236 self.changeState("Standby") 295 self.changeState("Standby")
237 - #TODO: get shutter from db status (then while until close/open)  
238 - #elif not self.plc_status.is_safe:  
239 - # response = TelescopeRemoteControlDefault("DOME SHUTTER CLOSE", expert_mode=True).exec_command()  
240 - #elif self.plc_status.is_safe == True:  
241 - # response = TelescopeRemoteControlDefault("DOME SHUTTER OPEN", expert_mode=True).exec_command()  
242 - elif self.plc_status.plc_mode != "AUTO":  
243 - self.changeState("Passive") 296 + return
  297 +
  298 + #TODO: get shutter state from db status (then wait until close/open)
  299 + if not self.plc_is_safe() and self.PLC_IS_SAFE:
  300 + PLC_IS_SAFE = False
  301 + response = TelescopeRemoteControlDefault("DOME SHUTTER CLOSE", expert_mode=True).exec_command()
  302 + if self.plc_is_safe() and not self.PLC_IS_SAFE:
  303 + PLC_IS_SAFE = True
  304 + response = TelescopeRemoteControlDefault("DOME SHUTTER OPEN", expert_mode=True).exec_command()
244 #return self.current_state 305 #return self.current_state
245 306
  307 +
246 def behavior_startup(self): 308 def behavior_startup(self):
247 - #TODO: get shutter and telescope from db status  
248 - #if dome_shutter == Open and telescope == Ready  
249 - #TelescopeRemoteControlDefault("DO DOME SHUTTER OPEN", expert_mode=True).exec_command()  
250 - #TelescopeRemoteControlDefault("DO START", expert_mode=True).exec_command()  
251 #print("Startup") 309 #print("Startup")
252 - if self.config.lock:  
253 - self.config.ntc = True 310 + if self.config.lock or not self.plc_is_auto():
  311 + self.NEED_TO_CLOSE=True
254 self.changeState("Standby") 312 self.changeState("Standby")
255 - else:  
256 - self.changeState("Scheduler") 313 + return
  314 +
  315 + #TODO: get shutter and telescope from db status
  316 + time.sleep(5)
  317 + '''
  318 + if not (dome_shutter == Open and telescope == Ready)
  319 + TelescopeRemoteControlDefault("DO DOME SHUTTER OPEN", expert_mode=True).exec_command()
  320 + TelescopeRemoteControlDefault("DO START", expert_mode=True).exec_command()
  321 + '''
  322 + self.changeState("Scheduler")
257 #return self.current_state 323 #return self.current_state
258 324
259 325
260 def behavior_scheduler(self): 326 def behavior_scheduler(self):
261 #print("Scheduler") 327 #print("Scheduler")
262 - if get_sunelev() > -10 or self.plc_status.is_safe is False or self.config.lock is True :  
263 - self.config.ntc = True  
264 - self.changeState("Standby")  
265 - elif self.config.global_mode == False or self.plc_status.plc_mode != "AUTO": 328 + if not self.is_night() or not self.plc_status.is_safe or self.config.lock or not self.plc_is_auto() or not self.config.global_mode or self.is_shuttingdown_or_restarting():
  329 + if not ( not self.config.global_mode or self.is_restarting() ): self.NEED_TO_CLOSE = True
266 self.changeState("Standby") 330 self.changeState("Standby")
267 #return self.current_state 331 #return self.current_state
268 332
269 - def behavior_closing(self): 333 +
  334 + def sub_behavior_closing(self):
  335 + #print("CURRENT OCS (MAJORDOME) STATE: "+ self.current_state)
270 #print("Closing") 336 #print("Closing")
271 - self.config.ntc = False  
272 - self.config.save()  
273 - response = TelescopeRemoteControlDefault("DO DOME SHUTTER CLOSE", expert_mode=True).exec_command()  
274 - response = TelescopeRemoteControlDefault("DO DOME PARK", expert_mode=True).exec_command()  
275 - response = TelescopeRemoteControlDefault("DO TELESCOPE PARK", expert_mode=True).exec_command()  
276 - if self.plc_status.plc_mode != "AUTO":  
277 - self.changeState("Standby")  
278 - self.changeState("Standby")  
279 - # TODO: elif dome == Parked and dome_shutter == Close and telescospe == Parked  
280 - #self.changeState("Standby") 337 + #self.config.save()
  338 + if not self.plc_is_auto():
  339 + # PLC not AUTO => we can do nothing, so only send email if dome not closed...
  340 + self.send_alarm_if_not_closed()
  341 + else:
  342 + # These commands should do nothing if instruments are already closed/parked...
  343 + response = TelescopeRemoteControlDefault("DO DOME SHUTTER CLOSE", expert_mode=True).exec_command()
  344 + response = TelescopeRemoteControlDefault("DO DOME PARK", expert_mode=True).exec_command()
  345 + response = TelescopeRemoteControlDefault("DO TELESCOPE PARK", expert_mode=True).exec_command()
  346 +
  347 + self.NEED_TO_CLOSE = False
  348 +
  349 + #self.changeState("Standby")
281 #return self.current_state 350 #return self.current_state
282 351
283 behavior = { 352 behavior = {
@@ -288,17 +357,21 @@ class Majordome(Task): @@ -288,17 +357,21 @@ class Majordome(Task):
288 "Remote": behavior_remote, 357 "Remote": behavior_remote,
289 "Startup": behavior_startup, 358 "Startup": behavior_startup,
290 "Scheduler": behavior_scheduler, 359 "Scheduler": behavior_scheduler,
291 - "Closing": behavior_closing, 360 + #"Closing": sub_behavior_closing,
292 } 361 }
293 def do_behavior_for_state(self, current_state:str) -> str: 362 def do_behavior_for_state(self, current_state:str) -> str:
294 print("CURRENT OCS (MAJORDOME) STATE: "+current_state) 363 print("CURRENT OCS (MAJORDOME) STATE: "+current_state)
  364 + time.sleep(2)
295 # EXIT if PLC not connected 365 # EXIT if PLC not connected
296 #if not self.plc_is_connected(): return 366 #if not self.plc_is_connected(): return
297 # EXIT if closing or restarting 367 # EXIT if closing or restarting
298 - #if self.is_closing_or_restarting(): return 368 + #if self.is_shuttingdown_or_restarting(): return
299 # run behavior for this state 369 # run behavior for this state
300 #self.behavior[current_state](self) 370 #self.behavior[current_state](self)
301 - self.behavior[current_state](self) 371 + if current_state == "Closing":
  372 + self.sub_behavior_closing()
  373 + else:
  374 + self.behavior[current_state](self)
302 return self.current_state 375 return self.current_state
303 376
304 377