#!/usr/bin/env python #************************************************************************** # Copyright (C) 2008-2015 by Walter Brisken * # * # This program is free software; you can redistribute it and/or modify * # it under the terms of the GNU General Public License as published by * # the Free Software Foundation; either version 3 of the License, or * # (at your option) any later version. * # * # This program is distributed in the hope that it will be useful, * # but WITHOUT ANY WARRANTY; without even the implied warranty of * # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * # GNU General Public License for more details. * # * # You should have received a copy of the GNU General Public License * # along with this program; if not, write to the * # Free Software Foundation, Inc., * # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * #************************************************************************** #=========================================================================== # SVN properties (DO NOT CHANGE) # # $Id$ # $HeadURL: $ # $LastChangedRevision$ # $Author$ # $LastChangedDate$ # #============================================================================ from string import split, strip, find, upper, lower from sys import argv, exit from os import popen, getenv from glob import glob from time import time import socket import struct from xml.parsers import expat from copy import deepcopy from optparse import OptionParser import curses import signal import os.path import subprocess def signal_handler(signal, frame): print('You pressed Ctrl+C!') raise KeyboardInterrupt verNum = '0.6' __author__="Walter Brisken" __prog__ = os.path.basename(__file__) __build__= "$Revision$" __date__ ="$Date$" __lastAuthor__="$Author$" def usage(): usage = "" usage += "A program to monitor the mark5 machines" return usage class Mk5state: def __init__(self): self.ok = False self.pid = 0 self.id = '' self.mk5 = '' self.seq = 0 self.vsnA = 'none' self.vsnB = 'none' self.stationA = ' ' self.stationB = ' ' self.bank = ' ' self.state = 'Unknown' self.scan = 0 self.name = '' self.pos = 0L self.rate = 0.0 self.date = 0.0 self.pollTime = 0.0 def getmk5(self): return self.mk5 class Parser: def __init__(self, options): self._parser = expat.ParserCreate() self._parser.StartElementHandler = self.start self._parser.EndElementHandler = self.end self._parser.CharacterDataHandler = self.data self.info = Mk5state() self.tmp = '' self.options = options def feed(self, data): try: self._parser.Parse(data, 0) except expat.ExpatError: print '\n\n XXX %s XXX' % data def close(self): self._parser.Parse("", 1) # end of data del self._parser # get rid of circular references def start(self, tag, attrs): pass def end(self, tag): if tag == 'mark5Status': self.info.ok = True if tag == 'bankAVSN': if len(self.tmp) != 8: self.info.vsnA = 'none' else: self.info.vsnA = upper(self.tmp) # obtain station code from DB (if enabled) if self.options.difxdb == True: child = subprocess.Popen(["difxdbModule", "--station", self.info.vsnA], stdout=subprocess.PIPE) (stationA, stderr) = child.communicate() self.info.stationA = strip(stationA) if tag == 'bankBVSN': if len(self.tmp) != 8: self.info.vsnB = 'none' else: self.info.vsnB = upper(self.tmp) # obtain station code from DB (if enabled) if self.options.difxdb == True: child = subprocess.Popen(["difxdbModule", "--station", self.info.vsnB], stdout=subprocess.PIPE) (stationB, stderr) = child.communicate() self.info.stationB = strip(stationB) if tag == 'from': self.info.mk5 = lower(self.tmp) if tag == 'state': self.info.state = self.tmp if tag == 'playRate': self.info.rate = float(self.tmp) if tag == 'dataMJD': self.info.date = float(self.tmp) if tag == 'position': self.info.pos = int(self.tmp) if tag == 'scanNumber': self.info.scan = int(self.tmp) if tag == 'scanName': self.info.name = self.tmp if tag == 'activeBank': self.info.bank = self.tmp def data(self, data): self.tmp = data def getinfo_old(self): return self.info.getmk5(), self.info.getstring() def getinfo(self): return self.info.getmk5(), self.info class Display: def __init__(self): self.info = {} self.rows = {} self.curs = curses.initscr() curses.noecho() curses.cbreak() curses.start_color() curses.use_default_colors() curses.init_pair(1, -1, -1) curses.init_pair(2, curses.COLOR_RED, -1) self.curs.keypad(1) self.curs.refresh() self.inactiveSecs = 30 # unit is considered inactive if no message has been received for this number of seconds def close(self): curses.nocbreak() self.curs.keypad(0) curses.echo() curses.endwin() def displayrow(self, info, row): b1 = ' ' b2 = ' ' if info.bank == 'A': b1 = '*' elif info.bank == 'B': b2 = '*' if info.state == "inactive": dateStr = '%5.1f' % (time() - info.pollTime) elif info.date > 0.0 and info.date < 1.0: dateStr = '%10.4f s' % (info.date*86400.0) else: dateStr = '%12.6f' % info.date stationA = info.stationA stationB = info.stationB if stationA == 'Unknown': stationA = " " if stationB == 'Unknown': stationB = " " if options.difxdb == True: rowStr = '%10s %c%8s(%2s) %c%8s(%2s) %14s %7.2f %14d %4d %17s %13s ' % \ (info.mk5, b1, info.vsnA, stationA, b2, info.vsnB, stationB, info.state, \ info.rate, info.pos, info.scan, info.name, dateStr) else: rowStr = '%10s %c%8s %c%8s %14s %7.2f %14d %4d %17s %13s ' % \ (info.mk5, b1, info.vsnA, b2, info.vsnB, info.state, \ info.rate, info.pos, info.scan, info.name, dateStr) if info.state == "inactive": self.curs.addstr(row, 0, rowStr, curses.color_pair(2)) elif info.state == "Error": self.curs.addstr(row, 0, rowStr, curses.color_pair(2)|curses.A_BOLD) else: self.curs.addstr(row, 0, rowStr, curses.color_pair(1)) def newdata(self, data): k = data[0] if self.rows.has_key(k): self.info[k] = data[1] self.displayrow(self.info[k], self.rows[k]) else: # a new machine has been found self.rows[k] = 0 self.info[k] = data[1] keys = self.rows.keys() keys.sort() i = 0 for k in keys: self.rows[k] = i i += 1 self.displayrow(self.info[k], self.rows[k]) # check for inactivity for k in self.rows.keys(): if time() - self.info[k].pollTime > self.inactiveSecs: self.info[k].state = "inactive" self.displayrow(self.info[k], self.rows[k]) self.curs.refresh() def listen(port, group, options): dt = 0.0 t = 0 maxtime = 6000 # Now listen for responses, until either time runs out or we get all we need s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 768000) s.bind(('', port)) mreq = struct.pack("4sl", socket.inet_aton(group), socket.INADDR_ANY) s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) s.settimeout(0.2) d = Display() try: while t < maxtime: try: message = s.recv(1500) except socket.timeout: t += dt continue if message[0] != '<': continue p = Parser(options) p.feed(message) machine, mk5info = p.getinfo() p.close() mk5info.pollTime = time() if mk5info.ok == False: continue d.newdata([machine, mk5info]) except KeyboardInterrupt: d.close() return 0 except: d.close() return 1 d.close() return 0 def readmachines(machinesfile, verbose): machines = [] cores = [] ismk5 = {} lines = open(machinesfile).readlines() for l in lines: l = split(strip(l), '#')[0] s = split(l) if len(s) >= 2: machines.append(s[0]) cores.append(int(s[1])) if len(s) >= 3: ismk5[s[0]] = int(s[2]) else: if s[0][:5] == 'mark5': ismk5[s[0]] = 1 else: ismk5[s[0]] = 0 if verbose > 1: print 'MACHINES = ', machines print 'CORES = ', cores print 'IS MK5 = ', ismk5 return machines,cores,ismk5 # main signal.signal(signal.SIGINT, signal_handler) version = "%s version %s (revision %s)\nOriginal author: %s\nLast changes by: %s\nLast changes on: %s" % (__prog__, verNum, __build__, __author__, __lastAuthor__, __date__) optParser = OptionParser(usage=usage(), version=version) optParser.add_option("-d", "--difxdb", dest="difxdb", action="store_true", help="use difxdb to obtain station codes") optParser.add_option("-p", "--port", dest="port", default = "", help="set the multicast message port (overrides DIX_MESSAGE_PORT environment)") optParser.add_option("-g", "--group", dest="group", default = "", help="set the multicast message group (overrides DIX_MESSAGE_GROUP environment)") (options, args) = optParser.parse_args() if len(args) > 0: optParser.print_help() exit(1) port = getenv('DIFX_MESSAGE_PORT') if port == None: if options.port != "": port = options.port else: print 'DIFX_MESSAGE_PORT needs to be defined, or use -p option' exit(1) port = int(port) group = getenv('DIFX_MESSAGE_GROUP') if group == None: if options.group != "": group = options.group else: print 'DIFX_MESSAGE_GROUP needs to be defined, or use -g option' exit(1) while(listen(port, group, options)): pass