#!/usr/bin/env seiscomp-python
from __future__ import print_function
from getopt import getopt, GetoptError
from time import time, gmtime
from datetime import datetime
import os, sys, signal, glob, re
from seiscomp.myconfig import MyConfig
import seiscomp.slclient
import seiscomp.kernel, seiscomp.config
usage_info = """
Usage:
slmon [options]
SeedLink monitor creating static web pages
Options:
-h, --help display this help message
-c ini_setup = arg
-s ini_stations = arg
-t refresh = float(arg) # XXX not yet used
-v verbose = 1
Examples:
Start slmon from the command line
slmon -c $SEISCOMP_ROOT/var/lib/slmon/config.ini
Restart slmon in order to update the web pages. Use crontab entries for
automatic restart, e.g.:
*/3 * * * * /home/sysop/seiscomp/bin/seiscomp check slmon >/dev/null 2>&1
"""
def usage(exitcode=0):
sys.stderr.write(usage_info)
exit(exitcode)
try:
seiscompRoot=os.environ["SEISCOMP_ROOT"]
except:
print("\nSEISCOMP_ROOT must be defined - EXIT\n", file=sys.stderr)
usage(exitcode=2)
ini_stations = os.path.join(seiscompRoot,'var/lib/slmon/stations.ini')
ini_setup = os.path.join(seiscompRoot,'var/lib/slmon/config.ini')
regexStreams = re.compile("[SLBVEH][HNLG][ZNE123]")
verbose = 0
class Module(seiscomp.kernel.Module):
def __init__(self, env):
seiscomp.kernel.Module.__init__(self, env, env.moduleName(__file__))
def printCrontab(self):
print("3 * * * * %s/bin/seiscomp check slmon >/dev/null 2>&1" % (self.env.SEISCOMP_ROOT))
class Status:
def __repr__(self):
return "%2s %-5s %2s %3s %1s %s %s" % \
(self.net, self.sta, self.loc, self.cha, self.typ, \
str(self.last_data), str(self.last_feed))
class StatusDict(dict):
def __init__(self, source=None):
if source:
self.read(source)
def fromSlinkTool(self,server="",stations=["GE_MALT","GE_MORC","GE_IBBN"]):
# later this shall use XML
cmd = "slinktool -nd 10 -nt 10 -Q %s" % server
print(cmd)
f = os.popen(cmd)
# regex = re.compile("[SLBVEH][HNLG][ZNE123]")
regex = regexStreams
for line in f:
net_sta = line[:2].strip() + "_" + line[3:8].strip()
if not net_sta in stations:
continue
typ = line[16]
if typ != "D":
continue
cha = line[12:15].strip()
if not regex.match(cha):
continue
d = Status()
d.net = line[ 0: 2].strip()
d.sta = line[ 3: 8].strip()
d.loc = line[ 9:11].strip()
d.cha = line[12:15]
d.typ = line[16]
d.last_data = seiscomp.slclient.timeparse(line[47:70])
d.last_feed = d.last_data
sec = "%s_%s" % (d.net, d.sta)
sec = "%s.%s.%s.%s.%c" % (d.net, d.sta, d.loc, d.cha, d.typ)
self[sec] = d
def read(self, source):
if type(source) == str:
source = file(source)
if type(source) == file:
source = source.readlines()
if type(source) != list:
raise TypeError('cannot read from %s' % str(type(source)))
for line in source:
d = Status()
d.net = line[ 0: 2]
d.sta = line[ 3: 8].strip()
d.loc = line[ 9:11].strip()
d.cha = line[12:15]
d.typ = line[16]
d.last_data = seiscomp.slclient.timeparse(line[18:41])
d.last_feed = seiscomp.slclient.timeparse(line[42:65])
if d.last_feed < d.last_data:
d.last_feed = d.last_data
sec = "%s_%s:%s.%s.%c" % (d.net, d.sta, d.loc, d.cha, d.typ)
self[sec] = d
def write(self, f):
if type(f) is str:
f = file(f, "w")
lines = []
for key in list(self.keys()):
lines.append(str(self[key]))
lines.sort()
f.write('\n'.join(lines)+'\n')
def colorLegend(htmlfile):
htmlfile.write("
Latencies:
\n" \
"\n\n" \
" <30 m | \n" \
" < 1 h | \n" \
" < 2 h | \n" \
" < 6 h | \n" \
" < 1 d | \n" \
" < 2 d | \n" \
" < 3 d | \n" \
" < 4 d | \n" \
" < 5 d | \n" \
" > 5 d | \n" \
"
\n
\n\n")
# encodes an email address so that it cannot (easily) be extracted
# from the web page. This is meant to be a spam protection.
def encode(txt): return ''.join(["%d;" % ord(c) for c in txt])
def total_seconds(td): return td.seconds + (td.days*86400)
def pageTrailer(htmlfile, config):
htmlfile.write("
\n" \
"\n" \
"\nLast updated %04d/%02d/%02d %02d:%02d:%02d UTC | \n" \
" %s | \n
\n" \
"
\n