Source code for glaciation.cdu.api

# coding=utf-8
import time
import json
from io import open
from threading import Thread
from multiprocessing import Process, Value
import glaciation.cdu._lib.Glaciation as Glaciation
import glaciation.cdu._lib.GlaciationPXI as GlaciationPXI  # for PXI and DAQ

SYNC = {}  # Object to monitorize whether cdu is sincronized or not
MON = {}  # Object to monitorize whether cdu is monitoring or not
WRITE = {}  # Object to monitorize whether cdu is writing or not
PROCESSES = {}  # Processes created by CduAPIWrapper class
THREADS = {}  # Threads created by CduAPIWrapper class


[docs]class CduAPIWrapper(): """Wrapper to call CduApi using Json files. \b It will start syncronizations or monitorizations with remote cdu in new threads. Parameters ---------- jsonFile : string Path of a json file Attributes ---------- connections : object A formatted dictionary obtained from a json file and containing all the info for multiple vardbs Methods ------- runAll(vardb=None,retries = 10) Starts all synchronization and monitorization in parallel startSync(vardb=None,retries = 5) Starts a syncronization with a remote CDU for later read or write variables using vardb variables using vardbvar.py library startMon(vardb=None,retries = 5) Starts a monitorization with a remote CDU and creates a csv file with the period frecuency defined in the json isSincronized(vardb = None) Check if vardb is syncronized isMonitoring(vardb = None) Check if vardb is monitoring stopSync(vardb = None)) Stop synchronization stopMon(vardb = None)) Stop monitorization writeVar(vardb, container, variableList, valueList, quality=True, force=True) Write a variable in same way as Timon does it disconnect(vardb = None): Kill started CDU sync and mon """ """ Global vars ---------- It will create global dictionaries (Sincronized, Monitoring... ) to get the monitoring/syncronization state of all initialized connections: Sincronized = {} # Object to monitorize whether cdu is sincronized or not Monitoring = {} # Object to monitorize whether cdu is monitoring or not Processes = {} # Processes created by CduAPIWrapper class Threads = {} # Threads created by CduAPIWrapper class """ __aart_enabled = False def __init__(self, jsonFile): self.connections = self.__parseJson(jsonFile)
[docs] def startSync(self, vardb=None, retries=5): """Starts a syncronization with a remote CDU for later read or write variables using vardb. \b If not vardb given it will try to connect to all vardbs in the json. Parameters ---------- vardb : string vardb name given to the connection (default is None, which implies all conexions defined in json file) retries : int Retries in case the sync fails (default is 5) """ SYNC[vardb]['state'] = Value('b', False) # shared parameter to check state SYNC[vardb]['stop'] = Value('b', False) # shared parameter to stop the process if retries == 0: raise Exception("Error: 0 Retries. Could not be syncronized to CDU: " + vardb) if vardb != None: print(str("Calling CDU sync %s. Attempts left %s \n" % (vardb, retries))) # Start each sync in a different Process for multiprocessing p = Process(target=self.__executeSyncProcess__, args=[str(vardb), SYNC[vardb]['state'], SYNC[vardb]['stop']]) p.start() PROCESSES[vardb + "_sync"] = p time.sleep(15) if not self.isSincronized(vardb)[0]: try: p.terminate() # kill process p.join() except Exception as e: print(e) try: self.startSync(vardb, retries - 1) except Exception as e: THREADS[vardb + "_error"] = e # save error print(e) else: print(str("\n----------> %s SYNCHRONIZATION OK <----------\n" % (vardb))) else: for vardb in self.connections: if ("SYNC" in self.connections[vardb]): # only sync connections THREADS[vardb] = Thread(target=self.startSync, args=[vardb, retries]) # Start all connections in parallel THREADS[vardb].daemon = True THREADS[vardb].start() for vardb in self.connections: if ("SYNC" in self.connections[vardb]): # only sync connections THREADS[vardb].join()
[docs] def startMon(self, vardb=None, retries=5): """Start CDU monitorization and create binary/csv file. \b If not vardb given it will try to connect to all vardbs in the json. Parameters ---------- vardb : string vardb name given to the connection (default is None, which implies all conexions defined in json file) retries : int Retries in case the sync fails (default is 5) """ MON[vardb]["state"] = Value('b', False) # shared parameter to check state MON[vardb]["stop"] = Value('b', False) # shared parameter to stop the process if retries == 0: raise Exception("0 Retries. Monitoring error using CduApi: " + vardb) if vardb != None: print(str("Calling CDU mon %s. Attempts left %s \n" % (vardb, retries))) # Start each mon in a different Process for multiprocessing p = Process(target=self.__executeMonProcess__, args=[str(vardb), MON[vardb]["state"], MON[vardb]["stop"]]) p.start() PROCESSES[vardb + "_mon"] = p time.sleep(15) if not self.isMonitoring(vardb)[0]: p.terminate() # kill process p.join() try: self.startMon(vardb, retries - 1) except Exception as e: THREADS[vardb + "_monError"] = e # save error print(str(e)) else: print(str("\n----------> %s MONITORIZATION OK <----------\n" % (vardb))) else: for vardb in self.connections: if ("MONIT" in self.connections[vardb]): # only monitoring connections THREADS[vardb + "_mon"] = Thread(target=self.startMon, args=[vardb, retries]) # Start all connections in parallel THREADS[vardb + "_mon"].daemon = True THREADS[vardb + "_mon"].start() for vardb in self.connections: if ("MONIT" in self.connections[vardb]): # only monitoring connections THREADS[vardb + "_mon"].join()
[docs] def writeVar(self, vardb, container, variableList, valueList, quality=True, force=True): """Solicitar la escritura puntual e independiente de una variable mediante CDU api. \b Write/Force a variable in the same way as timon does. Parameters ---------- vardb : string vardb name given to the connection container : string name of the container variableList : string/list list of variable names to be written. Can be only a one variable (a String) valueList : int/list list of value(s) to assign to variable(s). Can be only a one variable (a int) quality : bool True/false quality to assign to variable quality : bool True to force, False to unforce """ if type(variableList) != list: variableList = [variableList] if type(valueList) != list: valueList = [valueList] # Start writing variable in a new thread THREADS[vardb + "_write"] = Thread(target=self.__startWriteProcess__, args=[vardb, container, variableList, [int(i) for i in valueList], quality, force]) THREADS[vardb + "_write"].daemon = True THREADS[vardb + "_write"].start() THREADS[vardb + "_write"].join() time.sleep(3) # once force it remains forced until writing again with force=False self.stopWriteVar(vardb, container, variableList)
[docs] def isSincronized(self, vardb=None): """Check if vardb is syncronized with remote CDU. \b If not vardb given check in all vardbs. \b return false if any sync is down Parameters ---------- vardb : string vardb name given to the connection (default is None, which implies all conexions defined in json file) Returns ------- Bool True if connection/s are synchronizes ErrorList List of Not connected vardbs """ if vardb != None: return SYNC[vardb]['state'].value, [vardb] else: notConnectedCDUs = [] for vardb in SYNC: if SYNC[vardb]['state'].value != 1: notConnectedCDUs.append(vardb) return not notConnectedCDUs, notConnectedCDUs
[docs] def stopSync(self, vardb=None): """ Stop CDU synchronization. \b If not vardb given it will stop all sync processes. Parameters ---------- vardb : string vardb name given to the connection (default is None, which implies all conexions defined in json file) """ if vardb != None: if vardb in SYNC: SYNC[vardb]['stop'].value = True # send stop signal to process return for vardb in self.connections: self.stopMon(vardb)
[docs] def isMonitoring(self, vardb=None): """Check if vardb is monitoring. \b If not vardb given check in all vardbs. \b return false if any mon is down. Parameters ---------- vardb : string vardb name given to the connection (default is None, which implies all conexions defined in json file) Returns ------- Bool True if all connection/s are monitoring ErrorList List of Not connected vardbs """ if vardb != None: return MON[vardb]["state"].value, [vardb] else: notConnectedCDUs = [] for vardb in MON: if MON[vardb]["state"].value != 1: notConnectedCDUs.append(vardb) return not notConnectedCDUs, notConnectedCDUs
[docs] def stopMon(self, vardb=None): """ Stop CDU monitorization. \b If not vardb given it will stop all monitoring processes. Parameters ---------- vardb : string vardb name given to the connection (default is None, which implies all conexions defined in json file) """ if vardb != None: if vardb in MON: MON[vardb]["stop"].value = True # send stop signal to process return for vardb in self.connections: self.stopMon(vardb)
[docs] def isWriting(self, vardb, container, variableList): """Check if cdu is writing a variable. \b If not vardb given it will check all writing processes. \b return false if any writting is down. Parameters ---------- vardb : string vardb name given to the connection container : string name of the container variableList : string/list Returns ------- Bool True if all writing/s are OK """ if type(variableList) != list: variableList = [variableList] return WRITE[vardb+"_"+container+"_"+variableList[0]]["state"].value
[docs] def stopWriteVar(self, vardb, container, variableList): """Stop CDU writing var Parameters ---------- vardb : string vardb name given to the connection container : string name of the container variableList : string/list list of variable names to stop writing. Can be only a one variable (a String) """ if type(variableList) != list: variableList = [variableList] WRITE[vardb+"_"+container+"_"+variableList[0]]["stop"].value = True # send stop signal to process # give 5 seconds time to detect flag and unforce variable to the external Process time.sleep(5)
[docs] def disconnect(self, vardb=None): """Kill all started CDU sync and mon. \b If not vardb given it will kill all Parameters ---------- vardb : string vardb name given to the connection (default is None, which implies all conexions defined in json file) """ if vardb != None: if vardb + "_sync" in PROCESSES: PROCESSES[vardb + "_sync"].terminate() PROCESSES[vardb + "_sync"].join() if vardb + "_mon" in PROCESSES: PROCESSES[vardb + "_mon"].terminate() PROCESSES[vardb + "_mon"].join() if vardb + "_write" in PROCESSES: PROCESSES[vardb + "_write"].terminate() PROCESSES[vardb + "_write"].join() return for vardb in self.connections: self.disconnect(vardb)
[docs] @staticmethod def disconnectAll(): """Kill all started CDU sync and mon in any CduAPIWrapper instance. """ for elem in PROCESSES: try: elem.terminate() elem.join() except Exception as e: pass
[docs] def runAll(self, vardb=None, retries=10, runSync=True, runMon=True): """Launch CDU syncronization and monitorization. \b If not vardb given it will try to connect to all vardbs in the json Parameters ---------- vardb : string vardb name given to the connection (default is None, which implies all conexions defined in json file) retries : int Retries in case the sync fails (default is 5) runSync : bool True to run syncronization. False to not run sync. (defaul is True) runMon : bool True to run monitorization. False to not run mon. (defaul is True) Raises ------ Exception Could not be syncronized/monitorize """ if vardb != None: if ("SYNC" in self.connections[vardb] and runSync): THREADS[vardb] = Thread(target=self.startSync, args=[vardb, retries]) # sync THREADS[vardb].daemon = True THREADS[vardb].start() if ("MONIT" in self.connections[vardb] and runMon): # mon THREADS[vardb + "_mon"] = Thread(target=self.startMon, args=[vardb, retries]) # Start all connections in parallel THREADS[vardb + "_mon"].daemon = True THREADS[vardb + "_mon"].start() # join threads if ("SYNC" in self.connections[vardb] and runSync): THREADS[vardb].join() if ("MONIT" in self.connections[vardb] and runMon): THREADS[vardb + "_mon"].join() else: for vardb in self.connections: if ("SYNC" in self.connections[vardb] and runSync): # sync connections THREADS[vardb] = Thread(target=self.startSync, args=[vardb, retries]) # Start all connections in parallel THREADS[vardb].daemon = True THREADS[vardb].start() if ("MONIT" in self.connections[vardb] and runMon): # monitoring connections THREADS[vardb + "_mon"] = Thread(target=self.startMon, args=[vardb, retries]) # Start all connections in parallel THREADS[vardb + "_mon"].daemon = True THREADS[vardb + "_mon"].start() # join all for vardb in self.connections: if ("SYNC" in self.connections[vardb] and runSync): THREADS[vardb].join() # sync connections if ("MONIT" in self.connections[vardb] and runMon): THREADS[vardb + "_mon"].join() # monitoring connections error = '' if runMon and not self.isMonitoring()[0]: error = "Error: CDU Monitoring error in: " + ' '.join(map(str, self.isMonitoring()[1])) + "\n" if runSync and not self.isSincronized()[0]: error = error + "Error: Could not be syncronized to: " + ' '.join(map(str, self.isSincronized()[1])) + "\n" if error: raise Exception(error)
def __executeSyncProcess__(self, vardb, syncState, stop, reconnectTime=0): """ Connect to CduApi->VarSync @param vardb: VARDB_NAME given to the connection @param syncState: True if synchronization is Ok, False if down @param stop: stop signal to be send to stop the process any time @param reconnectTime: try reconnecting every reconnectTime second. 0 => no reconnection """ connString = str(self.connections[vardb]["CONNSTRING"]) dll = str(self.connections[vardb]["DLL"]) readVars = self.connections[vardb]["SYNC"]["READ_VARS"] writeVars = self.connections[vardb]["SYNC"]["WRITE_VARS"] period = int(self.connections[vardb]["SYNC"]["PERIOD"]) try: CDU = self.__initCduConnection(connString, self.asciiTime(vardb), dll) except Exception as e: if reconnectTime: time.sleep(reconnectTime) self.__executeSyncProcess__(vardb, syncState, stop, reconnectTime) exit(-1) syncState.value = True # Start synchronizing res = CDU.StartSync(vardb, period, writeVars, readVars) if res != Glaciation.GLACIATION_SUCCESS: syncState.value = False print("Error syncronizing to CDU: " + vardb) if reconnectTime: self.__executeSyncProcess__(vardb, syncState, stop, reconnectTime) time.sleep(5) # Check if remote cdu server is a normal CDU or PXI/DAQ if self.__checkIfCDUApi(connString): print(str("%s sync running at %s miliseconds \n" % (vardb, CDU.GetRealPeriod() / 1000))) while True: if stop.value: CDU.StopSync() # main threads send stop signal print("Stop Sincronization: " + vardb) exit(0) syncState.value = CDU.isSynchronized() and CDU.isConnected() if not syncState.value: print("Error!!!!! Sincronization NOT OK: " + vardb) break time.sleep(2) # try reconnecting every 10s self.__executeSyncProcess__(vardb, syncState, stop, 10) def __executeMonProcess__(self, vardb, monState, stop, reconnectTime=0): """ CDU monitoring, create CSV file using CDU->VarMoni @param vardb: VARDB_NAME given to the connection @param monState: True if monitorization is Ok, False if down @param stop: stop signal to be send to stop the process any time """ connString = str(self.connections[vardb]["CONNSTRING"]) monitVars = self.connections[vardb]["MONIT"]["VARIABLES"] fileName = str(self.connections[vardb]["MONIT"]["FILE_NAME"]) period = int(self.connections[vardb]["MONIT"]["PERIOD"]) binType = int(self.connections[vardb]["MONIT"]["TYPE"]) try: CDU = self.__initCduConnection(connString, self.asciiMonit(vardb)) except Exception as e: if reconnectTime: time.sleep(reconnectTime) self.__executeMonProcess__(vardb, monState, stop, reconnectTime) exit(-1) monState.value = True # Start monitoring res = CDU.StartMoni(fileName, period, monitVars, binType) if res != Glaciation.GLACIATION_SUCCESS: monState.value = False print("Error monitoring: " + vardb) if reconnectTime: self.__executeMonProcess__(vardb, monState, stop, reconnectTime) time.sleep(5) while True: if stop.value: CDU.StopMoni() # main threads send stop signal print("Stop Monitorization: " + vardb) exit(0) monState.value = CDU.isMonitoring() and CDU.isConnected() if not monState.value: print("Warning!!!!! Monitorization error: " + vardb) break time.sleep(3) # try reconnecting every 10s self.__executeMonProcess__(vardb, monState, stop, 10) def __startWriteProcess__(self, vardb, container, variableList, valueList, quality, force): WRITE[vardb+"_"+container+"_"+variableList[0]] = {} WRITE[vardb+"_"+container+"_"+variableList[0]]["state"] = Value('b', False) # shared parameter to check state WRITE[vardb+"_"+container+"_"+variableList[0]]["stop"] = Value('b', False) # shared parameter to stop the process print("Writing variable with CDU in %s.\n" % (vardb)) # Start each writing in a different Process for multiprocessing p = Process(target=self.__executeWriteProcess__, args=[ WRITE[vardb+"_"+container+"_"+variableList[0]]["state"], WRITE[vardb+"_"+container+"_"+variableList[0]]["stop"], str(vardb),str(container), variableList, valueList, quality, force ]) p.start() PROCESSES[vardb + "_write"] = p ''' time.sleep(5) if not self.isWriting(vardb, container, variable): p.terminate() # kill process p.join() print(str("Error in %s writting %s\n" % (vardb, variable))) else: print(str("\n %s Writting %s OK \n" % (vardb, variable))) ''' def __executeWriteProcess__(self, writeState, stop, vardb, container, variablesList, valueList, quality, force): """ Punctual write using CDU->CduApiVarInspectWriteVar @param vardb: VARDB_NAME given to the connection @param writeState: True if monitorization is Ok, False if down @param stop: stop signal to be send to stop the process any time """ connString = str(self.connections[vardb]["CONNSTRING"]) CDU = self.__initCduConnection(connString) # Start monitoring res = CDU.WriteVar(container, variablesList, valueList, quality, force) if res != Glaciation.GLACIATION_SUCCESS: writeState.value = False print("Error writing var(s) in %s" % (vardb)) exit(-1) time.sleep(5) while True: if stop.value: CDU.StopWriteVar() # main threads send stop signal print("Stop writing variable(s) in %s" % (vardb)) exit(-1) writeState.value = CDU.isWriting() and CDU.isConnected() if not writeState.value: print("Warning!!!!! Writing error var(s) in %s" % (vardb)) exit(-1) time.sleep(3) def __checkIfCDUApi(self, connString): """ Check if connection type is CDU or PXI/DAQ @param connString: connection string to be used in cduapi Returns: True if CDUapi, False if PXI/DAQ """ if "HOST" in connString: return True # CDU return False # PXI def __initCduConnection(self, connString, action=None, dll=None): """ CDUApi->Connect @param connString: connection string to be used in cduapi @param action: action text to be printed in cmd """ if action is not None: # call fuction (print) action # Check if remote cdu server is a normal CDU or PXI/DAQ if self.__checkIfCDUApi(connString): CDU = Glaciation.Glaciation() else: CDU = GlaciationPXI.Glaciation(dll) # Conectar y comprobar si está conectado res = CDU.Connect(connString) if not CDU.isConnected(): print(str("Error conecting to CDU: %s " % (connString))) raise ("Error conecting to CDU: %s " % (connString)) return CDU def __parseJson(self, jsonFile): """ Get a Json a shape self.connections object @param jsonFile: json file path """ connections = {} with open(jsonFile, encoding='utf-8') as f: data = json.load(f) for vardb in data["vardbs"]: vardbData = data["vardbs"][vardb] elem = { "CONNSTRING": vardbData["CONNSTRING"], "VARDB_NAME": vardbData["VARDB_NAME"], "DLL": vardbData["DLL"] } if "VARIABLES" in vardbData: SYNC[vardb] = {} elem["SYNC"] = {} elem["SYNC"]["PERIOD"] = vardbData["PERIOD"] if "PERIOD" in vardbData else 64 elem["SYNC"]["WRITE_VARS"] = self.__addPrefixToVars(vardbData["VARIABLES"]["WRITE"]) elem["SYNC"]["READ_VARS"] = self.__addPrefixToVars(vardbData["VARIABLES"]["READ"]) if "MONIT" in vardbData: MON[vardb] = {} elem["MONIT"] = vardbData["MONIT"] elem["MONIT"]["PERIOD"] = vardbData["MONIT"]["PERIOD"] if "PERIOD" in vardbData["MONIT"] else 64 elem["MONIT"]["VARIABLES"] = self.__addPrefixToVars(vardbData["MONIT"]["VARIABLES"]) if len(vardbData["VARDB_NAME"]) > 8: error = str("!!!!!ALERT VARDB NAME (%s) MAX SIZE = 8 (DLL Limitation)" % (vardbData["VARDB_NAME"])) print(error) raise Exception(error) exit(-1) connections[vardb] = elem return connections def __addPrefixToVars(self, containers): """ Add container prefix to variable name @param prefixes: object=> example: "fwk_internal": ["CDU_PERIOD","TEMPERATURE"] Returns example: ["fwk_internal::CDU_PERIOD","fwk_internal::TEMPERATURE"] """ fullNameVars = [] for container in containers: for varName in containers[container]: if len(container) > 0: fullNameVars.append(str("%s::%s" % (container, varName))) else: fullNameVars.append(str(varName)) return fullNameVars def asciiTime(self, vardb): """ :meta private: """ if self.__aart_enabled: print(""" _*_ ,;-^-:. .------------------'` \ `'-----------.---. | o o o (( o--)) |- | `------------------.. ',-----------'---' Synchronizing to `:._.;' %s `*' """ % (vardb)) else: print("Synchronizing to %s" % (vardb)) def asciiMonit(self, vardb): """ :meta private: """ if self.__aart_enabled: print(""" Monitoring %s +--------------------------------+ / +----------------------------+ / / // ,,,,, / / / // ( o o ) / / / +----------m---U---m----------+/ +--------------------------------+ """ % (vardb)) else: print("Monitoring %s" % (vardb)) @staticmethod def asciiGlaciation(self=None): """ :meta private: """ if self and self.__aart_enabled: print(""" ..######...##..........###.....######..####....###....########.####..#######..##....## KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK .##....##..##.........##.##...##....##..##....##.##......##.....##..##.....##.###...## KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK .##........##........##...##..##........##...##...##.....##.....##..##.....##.####..## KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK .##...####.##.......##.....##.##........##..##.....##....##.....##..##.....##.##.##.## KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK .##....##..##.......#########.##........##..#########....##.....##..##.....##.##..#### KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK .##....##..##.......##.....##.##....##..##..##.....##....##.....##..##.....##.##...### KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK ..######...########.##.....##..######..####.##.....##....##....####..#######..##....## KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK0OOkkxxxxxddddddddddddddddxxkkkxxxxkkO000KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK00Okxxddoolllllooooooollodddddolcccccclllccc::cclloxkO0KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK0Oxxddoollooldxxddooddddolclolllllooollllcccccccccc:::::;:ldxddkOO0KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK0Okxdollodddddddolllccllc:;;::;;;;::cllodolccclodollooooooollllloddldxdxOKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK0kxdlllodddxdddolc::::;,,;;,,,,,,',;cloolcc:clloooxddxkkOOkxxxooxxdookolddxxk0KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKkdolcooodxddoolc::;,,,,,,,,'',,,''';ldddl:::coxxddodxxkkxdddxkOOkdooxxoddolod:;kKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKKKKKK0xl:cllclooolll:;,,,,,,,''''''''''''.:xxc::,:dxkkkkxxdodo:,,,'..':okxooddxdodddo:,oKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKKKK0xc;:lolcoolc::;,,,''''''''''''.......;dd:;::lxxooollcc:,,'':dc;c;...;okkxooddddddold0KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKKKOl;;:coddocc:;;''''''''''..',;cllllc::cxx:;:cokkxxoclddlcc;;:xOc;l;....,cxOkkOxddxdoodxkO0KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKKx:,;:coxkxdc;;,'.'''''.',;clxkO0KKK00000Odc,:odOOkxdx0OOOkxdc;okl.....,'.,:coxkOOkxxdxxxxxxk0KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKd;,,;clooddc;,,''''..',:ox0KKKKKKKKKKKKK0xl:;,:xOOOkdkKK000Okdc;:lc,',;,..,:cc,;coxkO00OOKKKKKK0OKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKk;',,:cldkkxo:,''.',:ldk00KXXXXKKKKKKKKKKkc,co:;xOkkxxxO000K00Oxo:,,,,,,',:cloc,;cllldkc':dOKKK0oo0KKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKK0o',,,:ccoddolc;'';okO000KKXKKXXXKK0KKKKKKx:',od;;xkkOOxddddxO000OkdollloddxkOOkkxkkkxdxl,';:dOkkolOKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKOc'',;:cllooxxoc;,ckO000KKKKKKKXXKKKKXXXXKo';c:oo:;lkOkkkxxdoox0000OkkxxkOO00000000000Okkdll:cxkddxx0KKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKO:.,',;;codolooll:;lxOOO00OOKXNXXXKKKKKKKOc':dl;;::clooxkk0K0kxdxddxxkkkkkkkkkkkOO0000000O0OOkxxolookKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKK0c''',;;:codlloddol::oxO0OO0KXXXXXK0OO000kl,;ccc,.cOxoooooxkOOOkkxdddddddxxxxxxkkkxxxkkkkxxdxdoolcclkKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKd,.',,;;::clooollddlc:lxO0KKKKKKK0OOO0O00x:';cl;'':odxkOkdooooooooooooolllllodkO0Okkxxdooolllcc:cok0KKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKK0l'.',,;;:clodxddxdloocclodxO0000Oxxkkk00xc'';looc,,;lO000K00kxxdolllllodxkO0KKKK00K0Okxdolloodxk0KKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKk:'''',,;;:clodddoloxddollcllloooodkO000kl,:lc::;cdxk00000OOOkxdoooloxOKKKKKKKKKKKKKK0000O00KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKK0Oko:,,,,,,;;::cllcoxxxolddxxdoclllcccloollc;,cl:,';dxxdxkOkxdddolll:;cddddooooodxO00KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKK00Oxdl:,',,;;;;::c::lddddlokkxxdccdxdddoodoollllccclccccc:cccc::::;;;;;;,,,;,,,,,,;;:ccclodk0KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKK0K0kxolc;'',,,,,;;:cclllol:ldxdxdlldxxxxxxxxxoooddooloooodoolloolcccllcc;:ccc:;::;;;:;;;;,,;cx0KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKK0KKK00kxxkOo;',,;;;:::ccccc:;:loolodlccodddddxdolldxdoccloddddocldoddoccloolc:lllc:cc:,;;:;;,,,;cooddk0KKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKK00KXK0OOO0K00o;'',,;;;;;;;;:::ccccclllc;cloooolcclooooool::colllooooool:cooool::lll:;c:,;;;,,,,;;:ccc::loxOKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKOOKXXKKKK0000Oo:,,''',,,,;:::cc::cccc:;;ccllcccccclllllcll:;ccloollllc::lllccc;:cc:;;:;',,''',;::cccccllc:cdk0KKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKK0dd0KXKK0000000Oxlc;,,,,,,;;;;::;;;;;;::ccccccc:;:cccccc:;;;clcclcccc:;:cccc::;;:::;,,,'.''.',,;:;;:::ccclc:;;lkKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKO:,xKKKKK0OOOOOOkdolc:,,,,,',,,,,,;;;::::::::;;;:::::;;:;:ccccccc::;;:cc:;:;,,;;;;,'''''...'''',,;;;::::colc:;,;o0KKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKk:'cOKKKKKK0000000Okxdlc:;''';::;,,,,,,,,,,,,;;;;;;,,;dOkdoc:::;;,,;;;;:oxxdlc:;,'..'''.',,,''''''',,,;:ccclccc;;lOKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKk:,;ok0KKKKXKKKKKKKKK0Okxxxdxk00kdoloddc:llc:;''',,,,oKXXK0Oo:,''',,,,,cOXXXXK0kl,'''',;;::::::;,,''',,:ccclccc:;;lOKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKOc,,,;cdOKKXXKKKKKKKKXXXXKKXXKKKKXXKKKKKKKXK0xl:,,',l0K000000kdolc:;:clxO0KK0KKKK0kdoooodddddxkxdl:;,'',::cllcc:;,,o0KKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKK0l,,,,',:lx0KKXXKKKKKKXXXXXKKKKKKXXXXXXXKKKKKKK0Okxk0KKOkkkkOOOOOOkkxkkkOOOOO0000KKXK00OOOO000KK0Okdl:,,;:clllcll:,:kKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKx;,;,;;,',cx0KKKKKKKKXXXXXXXKKKKXXXKXXXXXKKKKKKKKKKKXX0OOO0KKK0OOOOkkkkOkkkkkkkOOOO00KKKKKKK0OOOkkkxdl;;clcllcoOkl;o0KKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKK0l,,,,;::;,:lodk00KKKKKKKKKKKKKKKKKKKXXXXKKKXXXXXXXXKXXXXXXXXXXKOOOkkkOOkkkkxxxxkkkOOkkOOOOOOOkkxxxxdoccllllc:ckK0dlOKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKOl:oolc:;:,''';cddllcloxO000OO000KKKKXXXKKKXXXXXXXXXXXXXXXXXKKKK0O0KKKK0000K00KKKKKK0OOOkkkOOkkkkkkdlcccccclok00k:;kKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKK00KKXOl;,;;,,,,,''..'',;clc::::coxO0KKXXXKKKKKXXXXXXXXXXXXXXKKKKKKKKKKXXXKKKKXKKKKKKKKK00000OO00KK0kkkkkxk0K0kxd:;xKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKK00KKKK0dc:;;;;,;;;;,,,,,;,,,,,''',:ox0KKKKXXKKKKKKKKKKKKKKKKKXKKKKKKKKKKKKXXKKKKKKXXXKXXXKKKKKKKKKKKKXXKK00kl,,,,;xKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKK0000KKKK0KK0Ol,,,,,;;;;;,;;;;;::c:;;;,',:ok000KKKKKKKKKKKKKKKKKKKXXXXKXKKKKKKKKKKKKKKKKKKKK000KKKKKKKKKK00Okol:'',,;:kKKKKKKKKKKKKKKK KKKKKKKKKKKK000OO00000000000O0Odc;,,cdxO0xc;;;;::::;;;::;,';clllccccok0KKKKKKKKKKKKKKKXXXKKKKKKKKKKKKK0000KKKKKKKKKKKK00kdl:,'',;,,,,l0KKKKKKKKKKKKKKK KKKKKKKKK0OOkkxxkOOkkkkkOkkkxkkkxo:o0XXKXX0O0Od:,,;:::;;:;;;,'''''''',cxOOOOO00OOxxxkO0KKKKKKKKKKKKKKKKK0K0OOkkkxdolokOo;'',,,,,,,'';xKKKKKKKKKKKKKKKK KKKKKKKKK0OkkkxxxxxxkkO0000Okkkxxk0KXXKKKKKKKKK0o,,,;;;,;;;;;;;,;;;;,'.';;;;:ll:,'.',:ldkOkddkkkdoxO0000Odl:;,,,''''.:c'';;;;,,;;,',o0KKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKK0000KKKK000000OOO0KKKKKKKKKKKKKK0x:'',;;;,,,;::;;;;;;,,,,''''.'''''''''';;,'',;,..,;cxkl;',,,,,,',,,,;,',,,,',;;,';o0KKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKK0000OOOOkkkOO00KKKKK00KKKKKKK00Ol,'',''',;::;,',;:;;;,,,,,;;;;;,,,,;;;,'',,;;,'',,::'',;,;;,,;;;;,,,,;;,,,,,,';oO0KKKKKKKKKKKKKKKKK KKKKKKKKKK00000OOOOkkkkkxxkO0000000000000000KKKK000Oxoddc,':xOkkxc,,,,,,,,,,;:;;;;;,,,;;;;;,,,,;;,,,;,',;;;,,',;;;;,,,,,,''',,,;cxkkkOKKKKKKKKKKKKKKKK KKKKKKKKK0Okkkxxxxxxxxxxdxk000OOOOOOOOOOOOO0000000OOOOOOOxoOKKKKK0dc,'',,,,,,''',;,;;,,,,;;,,,,,;;,,,'',;,,,,,,,,,'',,,,,''',:lxkkkkkO0KKKKKKKKKKKKKKK KKKKKKKKKK0000000000000OOOOOkkkkkO00OOkkkkkkkkkOOOOOOxxkO0KKKKKKKK00xc;,''''''''',,,,,'''',,,,,,'',,,,,,'''',,,,''',,'''',:coxkkkkkkO0KKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKK00Oxxxddddodxxxxkxdddddddddddddddk000000000000000Okxdllc:;;;,,'''''''''''''''''''''''''''''',,;;:clodxxkkkkkOO00KKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKK0OOOOkkkkkkxxxxxxxxxxxxxkkkkkkOOOOOOOOkkkkkkkkOOOO0000Okxxxddooollllcccccccc:cccccccclllloodxxxkkkkkOOOO000KKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK0OkkxxxxxxxddddddddddddxxxxxxxkkO0OOOOOOOOOOOOOOOkkkkkkkOOOOkkkkOOOOOOOO000KKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK00000OOOOOOOkkkkOkkOOOOOOOO00000KKKKKKKKKKKKKK000000000000000KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK """) else: print("### GLACIATION ###")