diff --git a/app/adaptiveorbit.py b/app/adaptiveorbit.py index 7889ac0..af1f089 100644 --- a/app/adaptiveorbit.py +++ b/app/adaptiveorbit.py @@ -47,9 +47,12 @@ class AdaptiveOrbit: print("Initializing EPICS Channels") pvs = [] for x in chx: - if ':X1' in x or ':X2' in x: + if ':X1' in x: pvs.append(PV(x.replace(':X1',':X-REF-FB'))) pvs.append(PV(x.replace(':X1',':Y-REF-FB'))) + elif ':X2' in x: + pvs.append(PV(x.replace(':X2',':X-REF-FB'))) + pvs.append(PV(x.replace(':X2',':Y-REF-FB'))) else: pvs.append(PV(x)) con = [pv.wait_for_connection(timeout=0.2) for pv in pvs] diff --git a/template/ServerTemplate.py b/template/ServerTemplate.py new file mode 100644 index 0000000..69bc95c --- /dev/null +++ b/template/ServerTemplate.py @@ -0,0 +1,33 @@ + +import signal + +import ServerBase + +class ServerTemplate(ServerBase.ServerBase): + def __init__(self, PVroot = 'MyServer', debug = False): + self.version='1.0.0' + self.program ='Server Template' + super(ServerTemplate, self).__init__(PVroot,debug,'127.0.0.1', 5678) # last too numbers are the IP adress and port of watchdog + + # connect to the individual handler, which must have the function terminate() implemented + # each handler should be their own thread to not interfere with the main process-loop + self.handler={} + + def terminateSubThreads(self): + for subserver in self.handler.keys(): + self.handler[subserver].terminate() + +if __name__ == '__main__': + + debug = True + server = ServerTemplate('SF-BC-SERVER', debug) + signal.signal(signal.SIGTERM,server.terminate) + try: + server.run() + except KeyboardInterrupt: + server.terminate(None,None) + + + + + diff --git a/util/__init__.py b/util/__init__.py index e69de29..31dccc0 100644 --- a/util/__init__.py +++ b/util/__init__.py @@ -0,0 +1,2 @@ +from .serverbase import ServerBase +from .zmqbase import ZMQBase diff --git a/util/serverbase.py b/util/serverbase.py new file mode 100644 index 0000000..9fa7e0c --- /dev/null +++ b/util/serverbase.py @@ -0,0 +1,91 @@ +import sys +import signal +import os +import socket +import logging +import logging.handlers +from logging.handlers import RotatingFileHandler +from datetime import datetime +import time + +from epics import PV +import ZMQBase + +class ServerBase (ZMQBase): + def __init__(self, root = 'MyServer', debug = False, WDServer = '127.0.0.1', WDPort = 5678): + + super(ServerBase,self).__init__(WDServer,WDPort) + + self.debug = debug + self.root = root + self.suffix='' + if self.debug: + self.suffix='-SIMU' + self.host = socket.gethostname() + self.pid = os.getpid() # process ID + + # enabling logging + self.logfilename="/sf/data/applications/BD-SERVER/%s.log" % self.root + handler = RotatingFileHandler(filename=self.logfilename, + mode='a', + maxBytes=5 * 1024 * 1024, + backupCount=1, + delay=0) + if self.debug: + logging.basicConfig(level=logging.INFO, + format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', + datefmt='%m-%d %H:%M:%S') + else: + logging.basicConfig(level=logging.INFO, + format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', + datefmt='%m-%d %H:%M:%S', + handlers=[handler,]) + self.logger=logging.getLogger(self.program) + + # setting up ZMQ interface + self.ZMQServerInfo(self.root,self.host,self.pid) + + # individual channels of main thread + self.PVstop = PV('%s:STOP%s' % (self.root,self.suffix)) + self.PVstop.value = 0 + self.PVstop.add_callback(self.stop) + self.PVping = PV('%s:PING%s' % (self.root,self.suffix)) + self.PVlog = PV('%s:LOG%s' % (self.root,self.suffix)) + + def stop(self,pvname=None,value=None,**kws): + self.logger.info('PV:STOP triggered at %s' % datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + if value > 0: + self.running=False + + def start(self): + self.logger.info('Starting Server: %s at %s' % (self.root,datetime.now().strftime('%Y-%m-%d %H:%M:%S'))) + self.logger.info('PV Root: %s' % self.root + self.logger.info('Version: %s' % self.version) + self.logger.info('Host: %s' % self.host) + self.logger.info('PID: %d' % self.pid) + if self.debug: + self.logger.info('Debug Mode') + + def run(self): + self.start() + self.running=True + while self.running: + time.sleep(1) + if self.ZMQPoll(): + self.logger.info('Watchdog Server requested termination') + self.running = False + self.PVping.value = datetime.now().strftime('Last active at %Y-%m-%d %H:%M:%S') + self.terminate(None,None) + + def terminate(self,signum,frame): + # stopping any sub thread with the server specific function terminateSubThreads + self.terminateSubThreads() + self.logger.info('Terminating Server at %s' % datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + self.ZMQPoll('quit') # informing the watchdog + print('Bunch Compressor Server is quitting...') + sys.exit(0) + + + + + diff --git a/util/zmqbase.py b/util/zmqbase.py new file mode 100644 index 0000000..1e883d3 --- /dev/null +++ b/util/zmqbase.py @@ -0,0 +1,66 @@ +import zmq +import socket +import sys + +class ZMQBase: + def __init__(self, host='127.0.0.1',port = 5678): + + self.host=host + self.port = port + self.msg={'action':'','PV':'','host':'','pid':0} + + self.REQUEST_TIMEOUT = 500 + self.REQUEST_RETRIES = 2 + self.SERVER_ENDPOINT = "tcp://%s:%d" % (host,port) + self.serverIsOffline=False # assume that it is online + + def ZMQServerInfo(self,PVroot,host,pid): + self.msg['PV']=PVroot + self.msg['host']=host + self.msg['pid']=pid + + + def ZMQPoll(self,tag='ping'): + self.msg['action']=tag + context = zmq.Context() + client = context.socket(zmq.REQ) + client.connect(self.SERVER_ENDPOINT) + client.send_pyobj(self.msg) + + retries_left = self.REQUEST_RETRIES + while True: + if (client.poll(self.REQUEST_TIMEOUT) & zmq.POLLIN) != 0: + reply = client.recv_pyobj() + check = self.ZMQIdentifyReply(reply) + if self.serverIsOffline: + self.logger.info("Watchdog server came online") + self.serverIsOffline=False + if check: + return (reply['action'] == 'quit') + else: + self.logger.warning("Malformed reply from server") + continue + + retries_left -= 1 + # Socket is confused. Close and remove it. + client.setsockopt(zmq.LINGER, 0) + client.close() + if retries_left == 0: + if not self.serverIsOffline: + self.logger.info("Watchdog server seems to be offline") + self.serverIsOffline=True + return False + + # Create new connection + client = context.socket(zmq.REQ) + client.connect(self.SERVER_ENDPOINT) + client.send_pyobj(self.msg) + + + def ZMQIdentifyReply(self,reply): + for field in ['PV','host','pid']: + if not reply[field] == self.msg[field]: + return False + return True + +