# 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 ###")