Compare commits

...

8 Commits

Author SHA1 Message Date
e2df92e6b4 [update] adds mass activation checks to StationQC class
Implements checks for mass channel activity to ensure proper functionality.

Introduces methods to verify if mass channels are active and to set errors when they are not connected.

Enhances reliability of data logging by avoiding unnecessary processing when mass channels are inactive.
2025-04-02 17:28:25 +02:00
8fa96d03d5 Merge branch 'develop' into feature/docker 2025-03-25 12:03:47 +01:00
4e25190fbc Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	parameters.yaml
2025-03-25 11:42:18 +01:00
8ac501e8dc [update] add parameter to disable PowBox usage for selected stations also to parameters.yaml, updated style sheets for html 2025-03-25 11:39:17 +01:00
fcba73fcc5 [bugfix] fix bug in connect_to_mail_server function 2025-03-21 22:42:23 +01:00
16fbbde3d9 [bugfix] fixed call of function connect_to_mail_server 2025-03-21 17:48:49 +01:00
b986da5fef [minor] fixed some typos and missing import 2025-03-21 17:43:41 +01:00
a6c1570539 [update] merge branch 'feature/docker' into develop
- add mail server configuration options to use authentication with user/password combination wit SSL or TLS connection
- add support for running survBot in a Docker container
- update gridengine submit script submit_bot.sh
- update stylesheets to latest changes
- fixed some typos
2025-03-21 16:20:02 +01:00
6 changed files with 59 additions and 37 deletions

4
.gitignore vendored
View File

@ -213,3 +213,7 @@ flycheck_*.el
/__simulate_fail.json
/mailing_list.yaml
.vscode/
*.code-workspace

View File

@ -3,7 +3,7 @@
version: 0.2
survBot is a small program used to track station quality channels of DSEBRA stations via PowBox output over SOH channels
by analysing contents of a Seiscomp3 datapath.
by analyzing contents of a Seiscomp data archive.
## Requirements
@ -60,33 +60,33 @@ The directory `/path/to/conf-dir` should contain the `parameters.yaml` file, and
The e-mail server settings can be configured in the `parameters.yaml` file. The following settings are available:
- `mailserver`: the address of the mail server
- `auth_type`: the authentication type for the mail server (`None`, `SSL`, `TLS`)
- `port`: the port of the mail server
- `user`: the username for the mail server (if required)
- `password`: the password for the mail server (if required)
* `mailserver`: the address of the mail server
* `auth_type`: the authentication type for the mail server (`None`, `SSL`, `TLS`)
* `port`: the port of the mail server
* `user`: the username for the mail server (if required)
* `password`: the password for the mail server (if required)
The `user` and `password` fields are optional, and can be left empty if the mail server does not require authentication. The `auth_type` field can be set to `None` if no authentication is required, `SSL` if the mail server requires SSL authentication, or `TLS` if the mail server requires TLS authentication. If the `user` or `password` fileds are set to `Docker` ore `ENV` the program will try to read the values from the docker secrets `mail_user` and `mail_password` or environment variables `MAIL_USER` and `MAIL_PASSWORD` respectively. Docker secrets are only available in Docker Swarm mode, i.e. if the program is run as a service.
The `user` and `password` fields are optional, and can be left empty if the mail server does not require authentication. The `auth_type` field can be set to `None` if no authentication is required, `SSL` if the mail server requires SSL authentication, or `TLS` if the mail server requires TLS authentication. If the `user` or `password` fields are set to `Docker` ore `ENV` the program will try to read the values from the docker secrets `mail_user` and `mail_password` or environment variables `MAIL_USER` and `MAIL_PASSWORD` respectively. Docker secrets are only available in Docker Swarm mode, i.e. if the program is run as a service.
## Version Changes
### 0.2
- surveillance of mass, clock and gaps
- individual mailing lists for different stations
- html mail with recent status information
- updated web page design
- restructured parameter file
- recognize if PBox is disconnected
* surveillance of mass, clock and gaps
* individual mailing lists for different stations
* html mail with recent status information
* updated web page design
* restructured parameter file
* recognize if PBox is disconnected
### 0.2-docker
- added Dockerfile for easy deployment
- added more settings for connection to a mail server
* added Dockerfile for easy deployment
* added more settings for connection to a mail server
## Staff
Original author: M.Paffrath (marcel.paffrath@rub.de)
Contributions by: Kasper D. Fischer (kasper.fischer@rub.de)
Original author: M.Paffrath (<marcel.paffrath@rub.de>)
Contributions: Kasper D. Fischer (<kasper.fischer@rub.de>)
Jan 2025

View File

@ -13,7 +13,7 @@ logging_level: WARN # set logging level (info, warning, debug)
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
min_sample: 5 # minimum samples for raising Warn/FAIL
dt_thresh: [300, 1800] # threshold (s) for timing delay colorization (yellow/red)
dt_thresh: [300, 1800] # threshold (s) for timing delay colourisation (yellow/red)
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)
@ -117,6 +117,11 @@ data_channels: ["HHZ", "HHN", "HHE"]
# ---------------------------------------- OPTIONAL PARAMETERS ---------------------------------------------------------
# deactivate powbox surveillance for stations (e.g. for solar powered), key: station, value: status message (abbr.)
no_pbox_stations: {'GR33': 'SOL', 'GR27B': 'DCN', 'RP01': 'noPBox', 'RP02': 'noPBox', 'RP03': 'noPBox', 'RP04': 'noPBox', 'RP05B': 'noPBox', 'RP06': 'noPBox', 'RP07': 'noPBox', 'RP08': 'noPBox'}
# deactivate mass position surveillance for stations without connected mass channels (e.g. STS-2 instruments), key: station, value: status message (abbr.)
no_mass_stations: {'BIA': 'DCN', 'KNEZ': 'DCN', 'KRUS': 'DCN', 'OHR': 'DCN', 'OTOV': 'DCN', 'SKO': 'DCN', 'STIP': 'DCN', 'VAY': 'DCN', 'ZUPA': 'DCN'}
# 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)
@ -130,7 +135,7 @@ add_links:
add_global_links:
# for example: - {"text": "our homepage", "URL": "https://www.rub.de"}
- {"text": "show recent events on map",
"URL": "https://fdsnws.geophysik.ruhr-uni-bochum.de/map/?lat=39.5&lon=21&zoom=7&baselayer=mapnik"}
"URL": "https://fdsnws.geophysik.ruhr-uni-bochum.de/map/?lat=39.5&lon=21&zoom=7&baselayer=osm"}
# html logo at page bottom (path relative to html directory)
html_logo: "logo.png"
@ -140,14 +145,14 @@ EMAIL:
# specify mail server and credentials
# port, auth_type, user and password are only required if mailserver is not set to "localhost"
# user and password can be set to "ENV" or "DOCKER" to read from environment variables or docker secrets
mailserver: "smtp.rub.de" # mail server
mailserver: "smtp.example.org" # mail server
auth_type: "SSL" # mail authentication type, can be "SSL", "TLS" or "None"
port: 465 # mail port, default 465 for SSL, 587 for TLS
user: "DOCKER" # mail user, read from environment variable if set to "ENV" or from docker secret if set to "DOCKER"
password: "DOCKER" # mail password, read from environment variable if set to "ENV" or from docker secret if set to "DOCKER"
# specify mail recipients, sender and blacklists
addresses: ["marcel.paffrath@rub.de", "kasper.fischer@rub.de"] # list of mail addresses for info mails
sender: "RUB SeisObs <seisobs@ruhr-uni-bochum.de>" # mail sender
addresses: ["test@example.org"] # list of mail addresses for info mails
sender: "<test@example.org>" # mail sender
stations_blacklist: [] # do not send emails for specific stations
networks_blacklist: [] # do not send emails for specific network
# specify recipients for single stations in a yaml: key = email-address, val = station list (e.g. [1Y.GR01, 1Y.GR02])

View File

@ -19,7 +19,7 @@ th {
background-color: #17365c;
color: #fff;
border-radius: 3px;
padding: 10px, 2px;
padding: 10px 2px;
position: sticky;
top: 0;
}

View File

@ -1,8 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__version__ = '0.2-docker'
__author__ = 'Marcel Paffrath <marcel.paffrath@rub.de>'
__version__ = '0.3'
__author__ = ['Marcel Paffrath <marcel.paffrath@rub.de>', 'Kasper D. Fischer <kasper.fischer@rub.de>']
import os
import io
@ -234,7 +234,7 @@ class SurveillanceBot(object):
self.gaps = self.dataStream.get_gaps(min_gap=self.parameters['THRESHOLDS'].get('min_gap'))
self.dataStream.merge()
# organise data in dictionary with key for each station
# organize data in dictionary with key for each station
for trace in self.dataStream:
nwst_id = get_nwst_id(trace)
if not nwst_id in self.data.keys():
@ -351,7 +351,7 @@ class SurveillanceBot(object):
first_exec = False
def console_print(self, itemlist, str_len=21, sep='|', seplen=3):
assert len(sep) <= seplen, f'Make sure seperator has less than {seplen} characters'
assert len(sep) <= seplen, f'Make sure separator has less than {seplen} characters'
sl = sep.ljust(seplen)
sr = sep.rjust(seplen)
string = sl
@ -595,6 +595,7 @@ class StationQC(object):
self.status_track = status_track
self.powbox_active = self.is_pbox_activated_check()
self.mass_active = self.is_mass_activated_check()
self.start()
@ -1080,6 +1081,11 @@ class StationQC(object):
""" Analyse datalogger mass channels. """
key = 'mass'
# skip processing if mass is not active
if not self.mass_active:
self.set_mass_inactive_error(key)
return
# build stream with all channels
st = Stream()
for channel in channels:
@ -1299,7 +1305,7 @@ class StationQC(object):
# Warn in case of voltage under OK-level (1V)
if len(under) > 0:
# try calculate number of occurences from gaps between indices
# try calculate number of occurrences from gaps between indices
n_occurrences = len(np.where(np.diff(under) > 1)[0]) + 1
voltage_dict[-1] = under
self.status_other(detailed_message=f'Trace {trace.get_id()}: '
@ -1341,6 +1347,12 @@ class StationQC(object):
msg = self.parameters.get('no_pbox_stations')[self.station]
self.error(key, detailed_message=f'PowBox not connected', disc=msg)
def is_mass_activated_check(self):
return self.station not in self.parameters.get('no_mass_stations', [])
def set_mass_inactive_error(self, key):
msg = self.parameters.get('no_mass_stations')[self.station]
self.error(key, detailed_message=f'Mass channels not connected', disc=msg)
class Status(object):
""" Basic Status class. All status classes are derived from this class."""
@ -1395,15 +1407,15 @@ class StatusOK(Status):
class StatusWarn(Status):
def __init__(self, message='WARN', count=1, last_occurence=None, detailed_messages=None, show_count=False):
super(StatusWarn, self).__init__(message=message, count=count, last_occurrence=last_occurence,
def __init__(self, message='WARN', count=1, last_occurrence=None, detailed_messages=None, show_count=False):
super(StatusWarn, self).__init__(message=message, count=count, last_occurrence=last_occurrence,
detailed_messages=detailed_messages, show_count=show_count)
self.set_warn()
class StatusError(Status):
def __init__(self, message='FAIL', count=1, last_occurence=None, detailed_messages=None, show_count=False):
super(StatusError, self).__init__(message=message, count=count, last_occurrence=last_occurence,
def __init__(self, message='FAIL', count=1, last_occurrence=None, detailed_messages=None, show_count=False):
super(StatusError, self).__init__(message=message, count=count, last_occurrence=last_occurrence,
detailed_messages=detailed_messages, show_count=show_count)
self.set_error()
self.default_message = message
@ -1420,8 +1432,8 @@ class StatusError(Status):
class StatusOther(Status):
def __init__(self, messages=None, count=1, last_occurence=None, detailed_messages=None):
super(StatusOther, self).__init__(count=count, last_occurrence=last_occurence,
def __init__(self, messages=None, count=1, last_occurrence=None, detailed_messages=None):
super(StatusOther, self).__init__(count=count, last_occurrence=last_occurrence,
detailed_messages=detailed_messages)
if messages is None:
messages = []

View File

@ -6,6 +6,7 @@ import logging
import matplotlib
import numpy as np
import smtplib
import os
from obspy import Stream
@ -174,7 +175,7 @@ def transform_trace(data, transf):
elif operator_str == '/':
data = data / val
else:
raise IOError(f'Unknown arithmethic operator string: {operator_str}')
raise IOError(f'Unknown arithmetic operator string: {operator_str}')
return data
@ -301,7 +302,7 @@ def get_credential(source, param):
# return source if no credential was found
return source
def connect_to_mail_server(self, mail_params):
def connect_to_mail_server(mail_params):
"""
Connect to mail server and return server object.
"""
@ -325,5 +326,5 @@ def connect_to_mail_server(self, mail_params):
else:
logging.error('Unknown authentication type. Mails can not be sent')
return
s.login(mail_params.get('user'), mail_params.get('password'))
s.login(user, password)
return s