From 0ca00ebdacc4277fc306d840f85630fc9672cfa2 Mon Sep 17 00:00:00 2001 From: Etienne Pallier Date: Wed, 18 Jul 2018 13:46:18 +0200 Subject: [PATCH] refactorized and improved majordome --- src/common/models.py | 4 ++++ src/dashboard/views.py | 1 + src/majordome/tasks.py | 205 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------ 3 files changed, 144 insertions(+), 66 deletions(-) diff --git a/src/common/models.py b/src/common/models.py index dd4edaf..3802d6a 100644 --- a/src/common/models.py +++ b/src/common/models.py @@ -843,7 +843,11 @@ class Config(models.Model): telescope_ip_address = models.CharField(max_length=45, default="127.0.0.1") camera_ip_address = models.CharField(max_length=45, default="127.0.0.1") plc_ip_address = models.CharField(max_length=45, default="127.0.0.1") + + # TODO: changer ça, c'est pas clair du tout... + # True = mode Scheduler-standby, False = mode Remote !!!! global_mode = models.BooleanField(default='True') + ack = models.BooleanField(default='False') bypass = models.BooleanField(default='True') lock = models.BooleanField(default='False') diff --git a/src/dashboard/views.py b/src/dashboard/views.py index 4354be3..9035878 100644 --- a/src/dashboard/views.py +++ b/src/dashboard/views.py @@ -85,6 +85,7 @@ def retrieve_env_navbar(request): plc_timeout = Config.objects.get(pk=1).plc_timeout_seconds timeout = (datetime.datetime.now() - plc_device_status.created).total_seconds() weather[0]['max_sunelev'] = SUN_ELEV_DAY_THRESHOLD + #weather[0]['sunelev'] = 10 weather[0]['sunelev'] = get_sunelev() weather[0]["plc_mode"] = plc_mode weather[0]["is_safe"] = is_safe diff --git a/src/majordome/tasks.py b/src/majordome/tasks.py index 57741c7..08f4d1c 100644 --- a/src/majordome/tasks.py +++ b/src/majordome/tasks.py @@ -57,11 +57,15 @@ class Majordome(Task): config = None plc_status = None - current_state = "Passive" + current_state = "Starting" # OCS-RESTART, OCS-SHUTDOWN, or NONE (default) closing_mode = "NONE" + NEED_TO_CLOSE = False + PLC_IS_SAFE = False + + ''' OLD //// Function called by celery task Behavior: @@ -85,8 +89,7 @@ class Majordome(Task): #self.loop() - RESTART = True - while RESTART: + while True: # SETUP try : @@ -94,7 +97,7 @@ class Majordome(Task): except Config.ObjectDoesNotExist: return -1 - self.config.ntc = False + #self.config.ntc = False # Set STATE to STARTING self.changeState("STARTING") @@ -118,46 +121,60 @@ class Majordome(Task): # MAIN LOOP: iterate on current state #while self.plc_is_connected() != False and self.closing_mode == "RUNNING" : - while self.plc_is_connected() and not self.is_closing_or_restarting(): + while self.plc_is_connected() and not self.is_shuttingdown_or_restarting(): # Call behavior for the current state and set new current state (can stay the same if no state change) #self.behavior[self.current_state](self) self.current_state = self.behavior[self.current_state](self) time.sleep(2) ''' while True: - if self.is_closing_or_restarting(): break + if self.current_state in ['STARTING', 'PASSIVE_NO_PLC', 'PASSIVE']: + if self.is_shuttingdown_or_restarting() and not self.NEED_TO_CLOSE: break + ''' if self.current_state not in ['STARTING', 'PASSIVE_NO_PLC']: - if not self.plc_is_connected(): - self.changeState("PASSIVE") + if self.plc_is_not_auto(): self.changeState("PASSIVE") + ''' self.current_state = self.do_behavior_for_state(self.current_state) - - # EXIT + + ''' # Shutdown options change by the main program if self.closing_mode == "OCS-SHUTDOWN": + self.shutdown() #self.closeBehaviour(self) - self.behavior_closing() - #TODO: self.kill_all_agents() - RESTART = False + #self.sub_behavior_closing() + break + ''' - elif self.closing_mode == "OCS-RESTART": + if self.closing_mode == "OCS-RESTART": self.config.majordome_restarted = True self.config.save() #TODO: self.kill_all_agents() - self.send_alarm_if_not_closed() + #self.send_alarm_if_not_closed() #self.run() - - #TODO: rester dans la main loop ??? - #elif self.closing_mode == "RUNNING" and self.plc_is_connected() == False: + + ''' elif not self.plc_is_connected(): #self.config.majordome_restarted = True #self.config.save() self.changeState("Passive No PLC") self.send_alarm_if_not_closed() #self.run() + ''' + + self.shutdown() + - def is_closing_or_restarting(self): + def shutdown(self): + #TODO: write shutdown code (kill agents...) + print("OCS SHUTDOWN") + + def is_restarting(self): + return self.closing_mode == "OCS-RESTART" + def is_shuttingdown(self): + return self.closing_mode == "OCS-SHUTDOWN" + def is_shuttingdown_or_restarting(self): return self.closing_mode != "NONE" @@ -172,14 +189,31 @@ class Majordome(Task): self.config.pyros_state = state self.config.save() + def _plc_is_not_auto(self): + if not self.plc_is_connected(): return True; + # now, self.plc_status has been updated, so check it: + return self.plc_status.plc_mode != "AUTO" + def plc_is_auto(self): + return not self._plc_is_not_auto() + + def plc_is_safe(self): + if not self.plc_is_connected(): return False; + # now, self.plc_status has been updated, so check it: + return self.plc_status.is_safe + + + def is_night(self): + return get_sunelev() < -10 + ''' Each function with behavior describes the behavior of a state, they are all contained in a dictionary called in run() ''' + def behavior_starting(self): #print("STARTING") - #if self.is_closing_or_restarting(): return + #if self.is_shuttingdown_or_restarting(): return if not self.config.majordome_restarted: #TODO: Do setup things... (start agents...) time.sleep(2) @@ -194,90 +228,125 @@ class Majordome(Task): self.changeState("PASSIVE_NO_PLC") #return self.current_state + def behavior_passive_no_plc(self): #print("PASSIVE_NO_PLC") - #if self.is_closing_or_restarting(): return + #if self.is_shuttingdown_or_restarting(): return #self.changeState("Passive No PLC") print("Waiting for PLC connection") # Wait until PCL is connected while not self.plc_is_connected(): - if self.is_closing_or_restarting(): return + time.sleep(2) + ''' + if self.is_restarting(): + self.changeState("STARTING") + return + ''' + if self.is_shuttingdown_or_restarting(): return print("PLC is connected") # Set STATE to PASSIVE (with PLC) self.changeState("Passive") #return self.current_state + def behavior_passive(self): #print("Passive") - #if self.is_closing_or_restarting(): return - if self.plc_status.plc_mode == "AUTO": - if not self.config.majordome_restarted: self.config.ntc = True + #if self.is_shuttingdown_or_restarting(): return + #if self.plc_status.plc_mode == "AUTO": + if not self.plc_is_connected(): + self.changeState("PASSIVE_NO_PLC") + return + + if self.plc_is_auto(): + #if not self.config.majordome_restarted: self.config.ntc = True + if not self.config.majordome_restarted: self.NEED_TO_CLOSE=True self.changeState("Standby") #return self.current_state + def behavior_standby(self): #print("Standby") - if self.config.ntc: + #if self.config.ntc: + if self.is_shuttingdown() and not self.NEED_TO_CLOSE: + self.NEED_TO_CLOSE = True + + if self.NEED_TO_CLOSE: self.changeState("Closing") - elif self.plc_status.plc_mode != "AUTO" or self.is_closing_or_restarting(): + #self.sub_behavior_closing() + self.do_behavior_for_state("Closing") + self.changeState("Standby") + + #elif self.plc_status.plc_mode != "AUTO" or self.is_shuttingdown_or_restarting(): + if not self.plc_is_auto() or self.is_shuttingdown_or_restarting(): self.changeState("Passive") - elif self.config.global_mode == False and self.config.lock == False: + elif not self.config.global_mode and not self.config.lock: self.changeState("Remote") - elif get_sunelev() < -10 and self.plc_status.is_safe == True and self.config.ack == True and self.config.lock == False : + + elif self.is_night() and self.plc_is_safe() and self.config.ack and not self.config.lock : self.changeState("Startup") #return self.current_state + def behavior_remote(self): #print("Remote") - if self.config.global_mode: - self.changeState("Standby") - elif self.config.lock: - self.config.ntc == True + if self.config.global_mode or self.is_shuttingdown_or_restarting() or not self.plc_is_auto() or self.config.lock: + if not self.plc_is_auto() or self.config.lock or self.is_shuttingdown(): + self.NEED_TO_CLOSE=True self.changeState("Standby") - #TODO: get shutter from db status (then while until close/open) - #elif not self.plc_status.is_safe: - # response = TelescopeRemoteControlDefault("DOME SHUTTER CLOSE", expert_mode=True).exec_command() - #elif self.plc_status.is_safe == True: - # response = TelescopeRemoteControlDefault("DOME SHUTTER OPEN", expert_mode=True).exec_command() - elif self.plc_status.plc_mode != "AUTO": - self.changeState("Passive") + return + + #TODO: get shutter state from db status (then wait until close/open) + if not self.plc_is_safe() and self.PLC_IS_SAFE: + PLC_IS_SAFE = False + response = TelescopeRemoteControlDefault("DOME SHUTTER CLOSE", expert_mode=True).exec_command() + if self.plc_is_safe() and not self.PLC_IS_SAFE: + PLC_IS_SAFE = True + response = TelescopeRemoteControlDefault("DOME SHUTTER OPEN", expert_mode=True).exec_command() #return self.current_state + def behavior_startup(self): - #TODO: get shutter and telescope from db status - #if dome_shutter == Open and telescope == Ready - #TelescopeRemoteControlDefault("DO DOME SHUTTER OPEN", expert_mode=True).exec_command() - #TelescopeRemoteControlDefault("DO START", expert_mode=True).exec_command() #print("Startup") - if self.config.lock: - self.config.ntc = True + if self.config.lock or not self.plc_is_auto(): + self.NEED_TO_CLOSE=True self.changeState("Standby") - else: - self.changeState("Scheduler") + return + + #TODO: get shutter and telescope from db status + time.sleep(5) + ''' + if not (dome_shutter == Open and telescope == Ready) + TelescopeRemoteControlDefault("DO DOME SHUTTER OPEN", expert_mode=True).exec_command() + TelescopeRemoteControlDefault("DO START", expert_mode=True).exec_command() + ''' + self.changeState("Scheduler") #return self.current_state def behavior_scheduler(self): #print("Scheduler") - if get_sunelev() > -10 or self.plc_status.is_safe is False or self.config.lock is True : - self.config.ntc = True - self.changeState("Standby") - elif self.config.global_mode == False or self.plc_status.plc_mode != "AUTO": + 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(): + if not ( not self.config.global_mode or self.is_restarting() ): self.NEED_TO_CLOSE = True self.changeState("Standby") #return self.current_state - def behavior_closing(self): + + def sub_behavior_closing(self): + #print("CURRENT OCS (MAJORDOME) STATE: "+ self.current_state) #print("Closing") - self.config.ntc = False - self.config.save() - response = TelescopeRemoteControlDefault("DO DOME SHUTTER CLOSE", expert_mode=True).exec_command() - response = TelescopeRemoteControlDefault("DO DOME PARK", expert_mode=True).exec_command() - response = TelescopeRemoteControlDefault("DO TELESCOPE PARK", expert_mode=True).exec_command() - if self.plc_status.plc_mode != "AUTO": - self.changeState("Standby") - self.changeState("Standby") - # TODO: elif dome == Parked and dome_shutter == Close and telescospe == Parked - #self.changeState("Standby") + #self.config.save() + if not self.plc_is_auto(): + # PLC not AUTO => we can do nothing, so only send email if dome not closed... + self.send_alarm_if_not_closed() + else: + # These commands should do nothing if instruments are already closed/parked... + response = TelescopeRemoteControlDefault("DO DOME SHUTTER CLOSE", expert_mode=True).exec_command() + response = TelescopeRemoteControlDefault("DO DOME PARK", expert_mode=True).exec_command() + response = TelescopeRemoteControlDefault("DO TELESCOPE PARK", expert_mode=True).exec_command() + + self.NEED_TO_CLOSE = False + + #self.changeState("Standby") #return self.current_state behavior = { @@ -288,17 +357,21 @@ class Majordome(Task): "Remote": behavior_remote, "Startup": behavior_startup, "Scheduler": behavior_scheduler, - "Closing": behavior_closing, + #"Closing": sub_behavior_closing, } def do_behavior_for_state(self, current_state:str) -> str: print("CURRENT OCS (MAJORDOME) STATE: "+current_state) + time.sleep(2) # EXIT if PLC not connected #if not self.plc_is_connected(): return # EXIT if closing or restarting - #if self.is_closing_or_restarting(): return + #if self.is_shuttingdown_or_restarting(): return # run behavior for this state #self.behavior[current_state](self) - self.behavior[current_state](self) + if current_state == "Closing": + self.sub_behavior_closing() + else: + self.behavior[current_state](self) return self.current_state -- libgit2 0.21.2