Compare commits

...

3 Commits

5 changed files with 84 additions and 27 deletions

View File

@ -1,18 +1,18 @@
# Parameters file for Surveillance Bot # Parameters file for Surveillance Bot
datapath: "/data/SDS/" # SC3 Datapath datapath: "/data/SDS/" # SC3 Datapath
networks: ["1Y", "HA"] networks: ["1Y", "HA"] # select networks, list or str
stations: "*" stations: "*" # select stations, list or str
locations: "*" locations: "*" # select locations, list or str
channels: ["EX1", "EX2", "EX3", "VEI"] # Specify SOH channels, currently supported EX[1-3] and VEI channels: ["EX1", "EX2", "EX3", "VEI"] # Specify SOH channels, currently supported EX[1-3] and VEI
channel_names: ["Temperature (°C)", "230V/12V Status (V)", "Router/Charger State (V)", "Logger Voltage (V)"] # names for plotting (optional) stations_blacklist: ["TEST", "EREA"] # exclude these stations
stations_blacklist: ["TEST", "EREA"] networks_blacklist: [] # exclude these networks
networks_blacklist: []
interval: 60 # Perform checks every x seconds interval: 60 # Perform checks every x seconds
n_track: 120 # wait n_track * intervals before performing an action (i.e. send mail/end highlight status) n_track: 300 # wait n_track * intervals before performing an action (i.e. send mail/end highlight status)
timespan: 7 # Check data of the recent x days timespan: 7 # Check data of the recent x days
verbosity: 0 verbosity: 0 # verbosity flag
track_changes: True # tracks all changes since GUI startup by text highlighting (GUI only) track_changes: True # tracks all changes since GUI startup by text highlighting (GUI only)
warn_count: False # show number of warnings and errors in table warn_count: False # show number of warnings and errors in table
min_sample: 3 # minimum samples for raising Warn/FAIL
dt_thresh: [300, 1800] # threshold (s) for timing delay colourisation (yellow/red) dt_thresh: [300, 1800] # threshold (s) for timing delay colourisation (yellow/red)
html_figures: True # Create html figure directory and links html_figures: True # Create html figure directory and links
reread_parameters: True # reread parameters file (change parameters on runtime, not for itself/GUI refresh/datapath) reread_parameters: True # reread parameters file (change parameters on runtime, not for itself/GUI refresh/datapath)
@ -44,20 +44,31 @@ THRESHOLDS:
high_volt: 14.8 # max voltage for over voltage warning high_volt: 14.8 # max voltage for over voltage warning
unclassified: 5 # min voltage samples not classified for warning unclassified: 5 # min voltage samples not classified for warning
# ---------------------------------------- OPTIONAL PARAMETERS ---------------------------------------------------------
# add links to html table with specified key as column and value as relative link, interpretable string parameters: # add links to html table with specified key as column and value as relative link, interpretable string parameters:
# nw (e.g. 1Y), st (e.g. GR01A), nwst_id (e.g. 1Y.GR01A) # nw (e.g. 1Y), st (e.g. GR01A), nwst_id (e.g. 1Y.GR01A)
# optional!
add_links: add_links:
slmon: {"URL": "../slmon/{nw}_{st}.html", "text": "show"} # for example: slmon: {"URL": "path/{nw}_{st}.html", "text": "link"} # for example: slmon: {"URL": "path/{nw}_{st}.html", "text": "link"}
slmon: {"URL": "../slmon/{nw}_{st}.html", "text": "show"}
24h-plot: {"URL": "../scheli/{nw}/{st}.png", "text": "plot"} 24h-plot: {"URL": "../scheli/{nw}/{st}.png", "text": "plot"}
# E-mail notifications (optional) # E-mail notifications
EMAIL: EMAIL:
mailserver: "localhost" mailserver: "localhost"
addresses: ["marcel.paffrath@rub.de", "kasper.fischer@rub.de"] # list of mail addresses for info mails addresses: ["marcel.paffrath@rub.de", "kasper.fischer@rub.de"] # list of mail addresses for info mails
sender: "webmaster@geophysik.ruhr-uni-bochum.de" # mail sender sender: "webmaster@geophysik.ruhr-uni-bochum.de" # mail sender
# Factor for channel to SI-units (for plotting, optional) # names for plotting of the above defined parameter "channels" in the same order
channel_names: ["Temperature (°C)", "230V/12V Status (V)", "Router/Charger State (V)", "Logger Voltage (V)"]
# specify y-ticks (and ylims) giving, (ymin, ymax, step) for each of the above channels (0: default)
CHANNEL_TICKS:
- [-10, 50, 10]
- [1, 5, 1]
- [1, 5, 1]
- [9, 15, 1]
# Factor for channel to SI-units (for plotting)
CHANNEL_UNITS: CHANNEL_UNITS:
EX1: 1e-6 EX1: 1e-6
EX2: 1e-6 EX2: 1e-6
@ -65,8 +76,7 @@ CHANNEL_UNITS:
VEI: 1e-3 VEI: 1e-3
# Transform channel for plotting, perform arithmetic operations in given order, e.g.: PBox EX1 V to deg C: 20 * x -20 # Transform channel for plotting, perform arithmetic operations in given order, e.g.: PBox EX1 V to deg C: 20 * x -20
# optional!
CHANNEL_TRANSFORM: CHANNEL_TRANSFORM:
EX1: EX1:
- ["*", 20] - ["*", 20]
- ["-", 20] - ["-", 20]

View File

@ -6,7 +6,7 @@ ulimit -s 8192
#$ -cwd #$ -cwd
#$ -pe smp 1 #$ -pe smp 1
#$ -N survBot_bg #$ -N survBot_bg
#$ -l os=*stretch ##$ -l os=*stretch
source /opt/anaconda3/etc/profile.d/conda.sh source /opt/anaconda3/etc/profile.d/conda.sh
conda activate py37 conda activate py37

View File

@ -19,7 +19,7 @@ from obspy.clients.filesystem.sds import Client
from write_utils import write_html_text, write_html_row, write_html_footer, write_html_header, get_print_title_str, \ from write_utils import write_html_text, write_html_row, write_html_footer, write_html_header, get_print_title_str, \
init_html_table, finish_html_table init_html_table, finish_html_table
from utils import get_bg_color, modify_stream_for_plot, annotate_trace_axes from utils import get_bg_color, modify_stream_for_plot, trace_ylabels, trace_yticks
try: try:
import smtplib import smtplib
@ -341,7 +341,8 @@ class SurveillanceBot(object):
try: try:
st = modify_stream_for_plot(st, parameters=self.parameters) st = modify_stream_for_plot(st, parameters=self.parameters)
st.plot(fig=fig, show=False, draw=False, block=False, equal_scale=False, method='full') st.plot(fig=fig, show=False, draw=False, block=False, equal_scale=False, method='full')
annotate_trace_axes(fig, self.parameters, self.verbosity) trace_ylabels(fig, self.parameters, self.verbosity)
trace_yticks(fig, self.parameters, self.verbosity)
except Exception as e: except Exception as e:
print(f'Could not generate plot for {nwst_id}:') print(f'Could not generate plot for {nwst_id}:')
print(traceback.format_exc()) print(traceback.format_exc())
@ -349,6 +350,8 @@ class SurveillanceBot(object):
ax = fig.axes[0] ax = fig.axes[0]
ax.set_title(f'Plot refreshed at (UTC) {UTCDateTime.now().strftime("%Y-%m-%d %H:%M:%S")}. ' ax.set_title(f'Plot refreshed at (UTC) {UTCDateTime.now().strftime("%Y-%m-%d %H:%M:%S")}. '
f'Refreshed hourly or on FAIL status.') f'Refreshed hourly or on FAIL status.')
for ax in fig.axes:
ax.grid(True, alpha=0.1)
fig.savefig(fnout, dpi=150., bbox_inches='tight') fig.savefig(fnout, dpi=150., bbox_inches='tight')
plt.close(fig) plt.close(fig)
@ -717,9 +720,6 @@ class StationQC(object):
self.status_ok(key, detailed_message=f'U={(voltage[-1])}V') self.status_ok(key, detailed_message=f'U={(voltage[-1])}V')
return return
n_overvolt = 0
n_undervolt = 0
warn_message = f'Trace {trace.get_id()}:' warn_message = f'Trace {trace.get_id()}:'
if len(overvolt) > 0: if len(overvolt) > 0:
# try calculate number of voltage peaks from gaps between indices # try calculate number of voltage peaks from gaps between indices
@ -832,8 +832,7 @@ class StationQC(object):
self.status_ok(key) self.status_ok(key)
continue continue
if volt_lvl > 1: if volt_lvl > 1:
# try calculate number of voltage peaks from gaps between indices n_occurrences = self.calc_occurrences(ind_array)
n_occurrences = len(np.where(np.diff(ind_array) > 1)[0]) + 1
self.warn(key=key, self.warn(key=key,
detailed_message=f'Trace {trace.get_id()}: ' detailed_message=f'Trace {trace.get_id()}: '
f'Found {n_occurrences} occurrence(s) of {volt_lvl}V: {key}: {message}' f'Found {n_occurrences} occurrence(s) of {volt_lvl}V: {key}: {message}'
@ -844,6 +843,32 @@ class StationQC(object):
if volt_lvl == last_val or (volt_lvl == -1 and last_val < 1): if volt_lvl == last_val or (volt_lvl == -1 and last_val < 1):
self.error(key, detailed_message=f'Last PowBox voltage state {last_val}V: {message}') self.error(key, detailed_message=f'Last PowBox voltage state {last_val}V: {message}')
def calc_occurrences(self, ind_array):
# try calculate number of voltage peaks/plateaus from gaps between indices
if len(ind_array) == 0:
return 0
else:
# start index at 1 if there are gaps (n_peaks = n_gaps + 1)
n_occurrences = 1
min_samples = self.parameters.get('min_sample')
if not min_samples:
min_samples = 1
# calculated differences in index array, diff > 1: gap, diff == 1: within peak/plateau
diffs = np.diff(ind_array)
gap_start_inds = np.where(np.diff(ind_array) > 1)[0]
# iterate over all gaps and check "min_samples" before the gap
for gsi in gap_start_inds:
# right boundary index of peak (gap index - 1)
peak_rb_ind = gsi - 1
# left boundary index of peak
peak_lb_ind = max([0, peak_rb_ind - min_samples])
if all(diffs[peak_lb_ind: peak_rb_ind] == 1):
n_occurrences += 1
return n_occurrences
def get_trace(self, stream, keys): def get_trace(self, stream, keys):
if not type(keys) == list: if not type(keys) == list:
keys = [keys] keys = [keys]

View File

@ -34,7 +34,7 @@ from obspy import UTCDateTime
from survBot import SurveillanceBot from survBot import SurveillanceBot
from write_utils import * from write_utils import *
from utils import get_bg_color, modify_stream_for_plot, annotate_trace_axes from utils import get_bg_color, modify_stream_for_plot, trace_ylabels, trace_yticks
try: try:
from rest_api.utils import get_station_iccid from rest_api.utils import get_station_iccid
@ -316,7 +316,8 @@ class MainWindow(QtWidgets.QMainWindow):
self.plot_widget.setWindowTitle(nwst_id) self.plot_widget.setWindowTitle(nwst_id)
st = modify_stream_for_plot(st, parameters=self.parameters) st = modify_stream_for_plot(st, parameters=self.parameters)
st.plot(equal_scale=False, method='full', block=False, fig=self.plot_widget.canvas.fig) st.plot(equal_scale=False, method='full', block=False, fig=self.plot_widget.canvas.fig)
annotate_trace_axes(fig=self.plot_widget.canvas.fig, parameters=self.parameters) trace_ylabels(fig=self.plot_widget.canvas.fig, parameters=self.parameters)
trace_yticks(fig=self.plot_widget.canvas.fig, parameters=self.parameters)
self.plot_widget.show() self.plot_widget.show()
def notification(self, text): def notification(self, text):

View File

@ -104,7 +104,7 @@ def transform_trace(data, transf):
return data return data
def annotate_trace_axes(fig, parameters, verbosity=0): def trace_ylabels(fig, parameters, verbosity=0):
""" """
Adds channel names to y-axis if defined in parameters. Adds channel names to y-axis if defined in parameters.
Can get mixed up if channel order in stream and channel names defined in parameters.yaml differ, but it is Can get mixed up if channel order in stream and channel names defined in parameters.yaml differ, but it is
@ -122,3 +122,24 @@ def annotate_trace_axes(fig, parameters, verbosity=0):
ax.set_ylabel(channel_name) ax.set_ylabel(channel_name)
def trace_yticks(fig, parameters, verbosity=0):
"""
Adds channel names to y-axis if defined in parameters.
Can get mixed up if channel order in stream and channel names defined in parameters.yaml differ, but it is
difficult to assess the correct order from Obspy plotting routing.
"""
ticks = parameters.get('CHANNEL_TICKS')
if not ticks:
return
if not len(ticks) == len(fig.axes):
if verbosity:
print('Mismatch in axis tick and label lengths. Not changing plot ticks.')
return
for ytick_tripple, ax in zip(ticks, fig.axes):
if not ytick_tripple:
continue
ymin, ymax, step = ytick_tripple
yticks = list(range(ymin, ymax + step, step))
ax.set_yticks(yticks)
ax.set_ylim(ymin - step, ymax + step)