def resolve_extra_text(extra_text, max_len, sender='helpers'): ''' .. role:: pycode(code) :language: python .. versionadded:: 1.0.3 A central helper function for the :class:`rsudp.c_telegram.Tweeter` and :class:`rsudp.c_telegram.Telegrammer` classes that checks whether the :pycode:`"extra_text"` parameter (in the settings file) is of appropriate length. This is done to avoid errors when posting alerts. The function will truncate longer messages. :param str extra_text: String of additional characters to post as part of the alert message (longer messages will be truncated). :param str max_len: Upper limit of characters accepted in message (280 for Twitter, 4096 for Telegram). :param str sender: String identifying the origin of the use of this function (:pycode:`self.sender` in the source function). :rtype: str :return: the message string to be incorporated ''' allowable_len = max_len - 177 # length of string allowable given maximum message text & region if ((extra_text == '') or (extra_text == None) or (extra_text == False)): return '' else: extra_text = str(extra_text) len_ex_txt = len(extra_text) if len_ex_txt > allowable_len: printW( 'extra_text parameter is longer than allowable (%s chars) and will be truncated. Please keep extra_text at or below %s characters.' % (len_ex_txt, allowable_len), sender=sender) extra_text = extra_text[:allowable_len] return ' %s' % (extra_text)
def get_inventory(sender='get_inventory'): ''' .. role:: pycode(code) :language: python Downloads the station inventory from the Raspberry Shake FDSN and stores it as an :py:class:`obspy.core.inventory.inventory.Inventory` object which is available globally. In this example, we get the R940D station inventory from the Raspberry Shake FDSN: .. code-block:: python >>> import rsudp.raspberryshake as rs >>> rs.initRSlib(dport=8888, rsstn='R940D') >>> inv = rs.get_inventory() >>> print(inv) Inventory created at 2020-02-21T20:37:34.246777Z Sending institution: SeisComP3 (gempa testbed) Contains: Networks (1): AM Stations (1): AM.R940D (Raspberry Shake Citizen Science Station) Channels (2): AM.R940D.00.EHZ, AM.R940D.00.HDF :param sender: `(optional)` The name of the function calling the :py:func:`rsudp.printM` logging function :type str: str or None :rtype: obspy.core.inventory.inventory.Inventory or bool :return: The inventory of the Raspberry Shake station in the :pycode:`rsudp.raspberryshake.stn` variable. ''' global inv, stn, region sender = 'get_inventory' if 'Z0000' in stn: printW('No station name given, continuing without inventory.', sender) inv = False else: try: printM('Fetching inventory for station %s.%s from Raspberry Shake FDSN.' % (net, stn), sender) url = 'https://fdsnws.raspberryshakedata.com/fdsnws/station/1/query?network=%s&station=%s&level=resp&nodata=404&format=xml' % ( net, stn)#, str(UTCDateTime.now()-timedelta(seconds=14400))) inv = read_inventory(url) region = FlinnEngdahl().get_region(inv[0][0].longitude, inv[0][0].latitude) printM('Inventory fetch successful. Station region is %s' % (region), sender) except (IndexError, HTTPError): printW('No inventory found for %s. Are you forwarding your Shake data?' % stn, sender) printW('Deconvolution will only be available if data forwarding is on.', sender, spaces=True) printW('Access the config page of the web front end for details.', sender, spaces=True) printW('More info at https://manual.raspberryshake.org/quickstart.html', sender, spaces=True) inv = False region = False except Exception as e: printE('Inventory fetch failed!', sender) printE('Error detail: %s' % e, sender, spaces=True) inv = False region = False return inv
def _load_sound(self): ''' Loads MP3 sound if possible, then writes to wav. Catches a ``FileNotFoundError`` when no player can be loaded. ''' try: soundloc = self.sound self.sound = AudioSegment.from_file(self.sound, format="mp3") printM('Loaded %.2f sec alert sound from %s' % (len(self.sound) / 1000., soundloc), sender=self.sender) self.wavloc = '%s.wav' % os.path.splitext(soundloc)[0] if 'ffplay' in PLAYER: self._write_wav() except FileNotFoundError as e: printE('Error loading player - %s' % (e), sender=self.sender) printW( "You have chosen to play a sound, but don't have ffmpeg or libav installed.", sender=self.sender) printW('Sound playback requires one of these dependencies.', sender=self.sender, spaces=True) printW("To install either dependency, follow the instructions at:", sender=self.sender, spaces=True) printW('https://github.com/jiaaro/pydub#playback', sender=self.sender, spaces=True) printW('The program will now continue without sound playback.', sender=self.sender, spaces=True) self.sound = False
def cancel_tests(settings, MPL, plot, quiet): ''' Cancel some tests if they don't need to be run. :param dict settings: the dictionary of settings for program execution :param bool plot: whether or not to plot (``False`` == no plot) :param bool quiet: whether or not to play sounds (``True`` == no sound) :rtype: dict :return: settings dictionary to test with ''' global TEST if plot: if MPL: TEST['d_matplotlib'][1] = True else: printW('matplotlib backend failed to load') else: settings['plot']['enabled'] = False del TEST['d_matplotlib'] del TEST['c_IMGPATH'] printM('Plot is disabled') if quiet: settings['alertsound']['enabled'] = False del TEST['d_pydub'] printM('Alert sound is disabled') return settings
def __init__(self, q=False, codefile=False, win_ovr=False): """ Initializes the custom code execution thread. """ super().__init__() self.sender = 'Custom' self.alive = True self.codefile = False self.win_ovr = win_ovr if codefile: if (os.path.exists(os.path.expanduser(codefile))) and ( os.path.splitext(codefile)[1]): self.codefile = os.path.expanduser(codefile) printM('Custom code file to run: %s' % self.codefile, sender=self.sender) else: printW( 'No python file exists at %s. No custom code will be run during alarms.' % codefile, sender=self.sender) else: printW( 'No custom code file set. No custom code will be run during alarms.', sender=self.sender) if (os.name in 'nt') and (not self.win_ovr): printE( 'Using Windows with custom alert code! Your code MUST have UNIX/Mac newline characters!' ) printE( 'Please use a conversion tool like dos2unix to convert line endings', spaces=True) printE( '(https://en.wikipedia.org/wiki/Unix2dos) to make your code file', spaces=True) printE('readable to the Python interpreter.', spaces=True) printE( 'Once you have done that, please set "win_override" to true', spaces=True) printE('in the settings file.', spaces=True) printE( '(see also footnote [1] on this page: https://docs.python.org/3/library/functions.html#id2)', spaces=True) printE('THREAD EXITING, please correct and restart!', self.sender, spaces=True) sys.exit(2) else: pass if q: self.queue = q else: printE('no queue passed to the consumer thread! We will exit now!', self.sender) sys.stdout.flush() self.alive = False sys.exit() printM('Starting.', self.sender)
def auth(self): if not self.testing: self.telegram = tg.Bot(token=self.token) else: printW( 'The Telegram module will not post to Telegram in Testing mode.', self.sender, announce=False)
def auth(self): if not self.testing: self.twitter = Twython(self.consumer_key, self.consumer_secret, self.access_token, self.access_token_secret) else: printW( 'The Twitter module will not post to Twitter in Testing mode.', self.sender, announce=False)
def exec_code(self): if self.codefile: # if the user has set a code file printM('Executing code from file: %s' % self.codefile, sender=self.sender) try: # try to execute some code exec(self.codefile) except Exception as e: # do something if it fails printE('Code execution failed. Error: %s' % e, sender=self.sender, announce=False) else: printW('No code to run, codefile variable not set correctly.', sender=self.sender)
def __init__(self, q): """ Initializes the custom code execution thread. """ super().__init__() self.sender = 'Testing' self.alive = True self.queue = q self.stream = rs.Stream() self.cha = rs.chns printW('Starting test consumer.', sender=self.sender, announce=False)
def run(self): ''' Start the testing thread and run until ``self.alive == False``. ''' if rs.inv: t.TEST['n_inventory'][1] = True self._datatests(self._getq()) while self.alive: self._getd() printW('Exiting.', sender=self.sender, announce=False) sys.exit()
def __init__(self, q, data_file, port): """ Initializes the data supplier thread. """ super().__init__() self.sender = 'TestData' self.data_file = data_file self.port = port self.addr = 'localhost' self.speed = 0 self.pos = 0 self.queue = q self.sock = False self.alive = True printW('Sending test data from %s' % self.data_file, sender=self.sender, announce=False)
def _set_icon(self): ''' Set RS plot icons. ''' mgr = plt.get_current_fig_manager() ico = pr.resource_filename('rsudp', os.path.join('img', ICON)) if QT: mgr.window.setWindowIcon(QtGui.QIcon(ico)) else: try: ico = PhotoImage(file=ico) mgr.window.tk.call('wm', 'iconphoto', mgr.window._w, ico) except: printW('Failed to set PNG icon image, trying .ico instead', sender=self.sender) try: ico = pr.resource_filename('rsudp', os.path.join('img', ICON2)) ico = PhotoImage(file=ico) mgr.window.tk.call('wm', 'iconphoto', mgr.window._w, ico) except: printE('Failed to set window icon.')
def run(self): ''' Start the thread. First, opens a file, determines the speed of data flow, then opens a socket and begins sending data at that transmission rate. Continues sending data until an ``ENDTEST`` packet arrives on the queue, or until the reader reaches the end of the file. Then, sends a ``TERM`` message to the localhost port and exits. ''' self.f = open(self.data_file, 'rb') self.f.seek(0) l = self.f.readline() l2 = self.f.readline() while (rs.getTIME(l2) == rs.getTIME(l)): l2 = self.f.readline() self.f.seek(0) self.speed = rs.getTIME(l2) - rs.getTIME(l) printW('Opening test socket...', sender=self.sender, announce=False) socket_type = s.SOCK_DGRAM if os.name in 'nt' else s.SOCK_DGRAM | s.SO_REUSEADDR self.sock = s.socket(s.AF_INET, socket_type) printW('Sending data to %s:%s every %s seconds' % (self.addr, self.port, self.speed), sender=self.sender, announce=False) while self.alive: try: q = self._getq() if q.decode('utf-8') in 'ENDTEST': self.alive = False break except Empty: self.send() time.sleep(self.speed) TEST['x_send'][1] = True self.f.close() self.sock.sendto(helpers.msg_term(), (self.addr, self.port)) printW('Exiting.', self.sender, announce=False) sys.exit()
from rsudp import printM, printW, printE, helpers import rsudp import linecache sender = 'plot.py' QT = False QtGui = False PhotoImage = False try: # test for matplotlib and exit if import fails from matplotlib import use try: # no way to know what machines can handle what software, but Tk is more universal use('Qt5Agg' ) # try for Qt because it's better and has less threatening errors from PyQt5 import QtGui QT = True except Exception as e: printW('Qt import failed. Trying Tk...') printW('detail: %s' % e, spaces=True) try: # fail over to the more reliable Tk use('TkAgg') from tkinter import PhotoImage except Exception as e: printE( 'Could not import either Qt or Tk, and the plot module requires at least one of them to run.', sender) printE('Please make sure either PyQt5 or Tkinter is installed.', sender, spaces=True) printE('detail: %s' % e, sender, spaces=True) raise ImportError( 'Could not import either Qt or Tk, and the plot module requires at least one of them to run' )
def _init_sound(self): if pydub_exists: if os.path.exists(self.sound): self._load_sound() else: printW("The file %s could not be found." % (self.sound), sender=self.sender) printW('The program will now continue without sound playback.', sender=self.sender, spaces=True) self.sound = False else: printE('Error importing pydub - %s' % ERR, sender=self.sender) printW("You don't have pydub installed, so no sound will play.", sender=self.sender) printW('To install pydub, follow the instructions at:', sender=self.sender, spaces=True) printW('https://github.com/jiaaro/pydub#installation', sender=self.sender, spaces=True) printW( 'Sound playback also requires you to install either ffmpeg or libav.', sender=self.sender, spaces=True)
def __init__(self, q, cha='all', seconds=30, spectrogram=True, fullscreen=False, kiosk=False, deconv=False, screencap=False, alert=True): """ Initialize the plot process. """ super().__init__() self.sender = 'Plot' self.alive = True self.alarm = False # don't touch this self.alarm_reset = False # don't touch this if MPL == False: sys.stdout.flush() sys.exit() if QT == False: printW( 'Running on %s machine, using Tk instead of Qt' % (platform.machine()), self.sender) self.queue = q self.master_queue = None # careful with this, this goes directly to the master consumer. gets set by main thread. self.stream = rs.Stream() self.raw = rs.Stream() self.stn = rs.stn self.net = rs.net self.chans = [] helpers.set_channels(self, cha) printM('Plotting %s channels: %s' % (len(self.chans), self.chans), self.sender) self.totchns = rs.numchns self.seconds = seconds self.pkts_in_period = rs.tr * rs.numchns * self.seconds # theoretical number of packets received in self.seconds self.spectrogram = spectrogram self._set_deconv(deconv) self.per_lap = 0.9 self.fullscreen = fullscreen self.kiosk = kiosk self.num_chans = len(self.chans) self.delay = rs.tr if (self.spectrogram) else 1 self.delay = 0.5 if (self.chans == ['SHZ']) else self.delay self.screencap = screencap self.save_timer = 0 self.save_pct = 0.7 self.save = [] self.events = 0 self.event_text = ' - detected events: 0' if alert else '' self.last_event = [] self.last_event_str = False # plot stuff self.bgcolor = '#202530' # background self.fgcolor = '0.8' # axis and label color self.linecolor = '#c28285' # seismogram color printM('Starting.', self.sender)
def run(settings, debug): ''' Main setup function. Takes configuration values and passes them to the appropriate threads and functions. :param dict settings: settings dictionary (see :ref:`defaults` for guidance) :param bool debug: whether or not to show debug output (should be turned off if starting as daemon) ''' global PLOTTER, SOUND # handler for the exit signal signal.signal(signal.SIGINT, handler) if TESTING: global TESTQUEUE # initialize the test data to read information from file and put it on the port TESTQUEUE = Queue() # separate from client library because this is not downstream of the producer tdata = TestData(q=TESTQUEUE, data_file=TESTFILE, port=settings['settings']['port']) tdata.start() # initialize the central library rs.initRSlib(dport=settings['settings']['port'], rsstn=settings['settings']['station']) H.conn_stats(TESTING) if TESTING: T.TEST['n_port'][1] = True # port has been opened if rs.sps == 0: printE('There is already a Raspberry Shake sending data to this port.', sender=SENDER) printE('For testing, please change the port in your settings file to an unused one.', sender=SENDER, spaces=True) _xit(1) output_dir = settings['settings']['output_dir'] if settings['printdata']['enabled']: # set up queue and process q = mk_q() prnt = PrintRaw(q) mk_p(prnt) if settings['write']['enabled']: # set up queue and process cha = settings['write']['channels'] q = mk_q() writer = Write(q=q, cha=cha) mk_p(writer) if settings['plot']['enabled'] and MPL: while True: if rs.numchns == 0: time.sleep(0.01) continue else: break cha = settings['plot']['channels'] sec = settings['plot']['duration'] spec = settings['plot']['spectrogram'] full = settings['plot']['fullscreen'] kiosk = settings['plot']['kiosk'] screencap = settings['plot']['eq_screenshots'] alert = settings['alert']['enabled'] if settings['plot']['deconvolve']: if settings['plot']['units'].upper() in rs.UNITS: deconv = settings['plot']['units'].upper() else: deconv = 'CHAN' else: deconv = False pq = mk_q() PLOTTER = Plot(cha=cha, seconds=sec, spectrogram=spec, fullscreen=full, kiosk=kiosk, deconv=deconv, q=pq, screencap=screencap, alert=alert) # no mk_p() here because the plotter must be controlled by the main thread (this one) if settings['forward']['enabled']: # put settings in namespace addr = settings['forward']['address'] port = settings['forward']['port'] cha = settings['forward']['channels'] # set up queue and process q = mk_q() forward = Forward(addr=addr, port=port, cha=cha, q=q) mk_p(forward) if settings['alert']['enabled']: # put settings in namespace sta = settings['alert']['sta'] lta = settings['alert']['lta'] thresh = settings['alert']['threshold'] reset = settings['alert']['reset'] bp = [settings['alert']['highpass'], settings['alert']['lowpass']] cha = settings['alert']['channel'] if settings['alert']['deconvolve']: if settings['alert']['units'].upper() in rs.UNITS: deconv = settings['alert']['units'].upper() else: deconv = 'CHAN' else: deconv = False # set up queue and process q = mk_q() alrt = Alert(sta=sta, lta=lta, thresh=thresh, reset=reset, bp=bp, cha=cha, debug=debug, q=q, deconv=deconv) mk_p(alrt) if settings['alertsound']['enabled']: sender = 'AlertSound' SOUND = False soundloc = False if PYDUB_EXISTS: soundloc = os.path.expanduser(os.path.expanduser(settings['alertsound']['mp3file'])) if soundloc in ['doorbell', 'alarm', 'beeps', 'sonar']: soundloc = pr.resource_filename('rsudp', os.path.join('rs_sounds', '%s.mp3' % soundloc)) if os.path.exists(soundloc): try: SOUND = AudioSegment.from_file(soundloc, format="mp3") printM('Loaded %.2f sec alert sound from %s' % (len(SOUND)/1000., soundloc), sender='AlertSound') except FileNotFoundError as e: printW("You have chosen to play a sound, but don't have ffmpeg or libav installed.", sender='AlertSound') printW('Sound playback requires one of these dependencies.', sender='AlertSound', spaces=True) printW("To install either dependency, follow the instructions at:", sender='AlertSound', spaces=True) printW('https://github.com/jiaaro/pydub#playback', sender='AlertSound', spaces=True) printW('The program will now continue without sound playback.', sender='AlertSound', spaces=True) SOUND = False else: printW("The file %s could not be found." % (soundloc), sender='AlertSound') printW('The program will now continue without sound playback.', sender='AlertSound', spaces=True) else: printW("You don't have pydub installed, so no sound will play.", sender='AlertSound') printW('To install pydub, follow the instructions at:', sender='AlertSound', spaces=True) printW('https://github.com/jiaaro/pydub#installation', sender='AlertSound', spaces=True) printW('Sound playback also requires you to install either ffmpeg or libav.', sender='AlertSound', spaces=True) q = mk_q() alsnd = AlertSound(q=q, sound=SOUND, soundloc=soundloc) mk_p(alsnd) runcustom = False try: f = False win_ovr = False if settings['custom']['enabled']: # put settings in namespace f = settings['custom']['codefile'] win_ovr = settings['custom']['win_override'] if f == 'n/a': f = False runcustom = True except KeyError as e: if settings['alert']['exec'] != 'eqAlert': printW('the custom code function has moved to its own module (rsudp.c_custom)', sender='Custom') f = settings['alert']['exec'] win_ovr = settings['alert']['win_override'] runcustom = True else: raise KeyError(e) if runcustom: # set up queue and process q = mk_q() cstm = Custom(q=q, codefile=f, win_ovr=win_ovr) mk_p(cstm) if settings['tweets']['enabled']: consumer_key = settings['tweets']['api_key'] consumer_secret = settings['tweets']['api_secret'] access_token = settings['tweets']['access_token'] access_token_secret = settings['tweets']['access_secret'] tweet_images = settings['tweets']['tweet_images'] q = mk_q() tweet = Tweeter(q=q, consumer_key=consumer_key, consumer_secret=consumer_secret, access_token=access_token, access_token_secret=access_token_secret, tweet_images=tweet_images) mk_p(tweet) if settings['telegram']['enabled']: token = settings['telegram']['token'] chat_ids = settings['telegram']['chat_id'].strip(' ').split(',') send_images = settings['telegram']['send_images'] for chat_id in chat_ids: sender = "Telegram id %s" % (chat_id) q = mk_q() telegram = Telegrammer(q=q, token=token, chat_id=chat_id, send_images=send_images, sender=sender) mk_p(telegram) if settings['rsam']['enabled']: # put settings in namespace fwaddr = settings['rsam']['fwaddr'] fwport = settings['rsam']['fwport'] fwformat = settings['rsam']['fwformat'] interval = settings['rsam']['interval'] cha = settings['rsam']['channel'] if settings['rsam']['deconvolve']: if settings['rsam']['units'].upper() in rs.UNITS: deconv = settings['rsam']['units'].upper() else: deconv = 'CHAN' else: deconv = False # set up queue and process q = mk_q() rsam = RSAM(q=q, debug=debug, interval=interval, cha=cha, deconv=deconv, fwaddr=fwaddr, fwport=fwport, fwformat=fwformat) mk_p(rsam) # start additional modules here! ################################ ################################ if TESTING: # initialize test consumer q = mk_q() test = Testing(q=q) mk_p(test) # start the producer, consumer, and activated modules start() PLOTTER = False if not TESTING: _xit() else: printW('Client has exited, ending tests...', sender=SENDER, announce=False) if SOUND: T.TEST['d_pydub'][1] = True
def main(): ''' Loads settings to start the main client. Supply -h from the command line to see help text. ''' hlp_txt = ''' ########################################### ## R A S P B E R R Y S H A K E ## ## Archive Forwarder ## ## by Ian Nesbitt ## ## GNU GPLv3 2021 ## ## ## ## Forward archived Shake data via UDP ## ## from RS UDP-packet-formatted ASCII ## ## text to IP/port network locations. ## ## ## ## Requires: ## ## - rsudp ## ## ## ########################################### Usage: rsaf -i FILE -d IP.ADR.OF.DST -p PORT where := { -i | --infile location of the input text file -d | --dest destination IP address (four 1-3 digit numbers separated by periods; example: 192.168.1.30) -p | --port destination port (a 1-5 digit number) } ''' inf, dest, port, phelp = False, False, False, False sender = 'main thread' try: opts = getopt.getopt(sys.argv[1:], 'hi:d:p:', ['help', 'infile=', 'dest=', 'port='])[0] for opt, arg in opts: if opt in ('-h', '--help'): print(hlp_txt) phelp = True if opt in ('-i', '--infile='): inf = arg if opt in ('-d', '--dest='): dest = arg if opt in ('-p', '--port='): port = int(arg) except Exception as e: start_logging(logname='rsaf.log') if debug: add_debug_handler() printE('%s' % e, announce=True, sender=sender) print(hlp_txt) phelp = True if inf and dest and port: if debug: add_debug_handler() start_logging(logname='rsaf.log') q = Queue(rs.qsize) t = RSAF(q=q, inf=inf, dest=dest, port=port) printW('Starting RSAF thread. Press CTRL+C to quit at any time.', sender=sender, announce=False) t.start() try: while t.alive: time.sleep(0.1) except KeyboardInterrupt: printW('Got interrupt keystroke. Ending transmission.', sender=sender, announce=False) q.put(helpers.msg_term()) else: if not phelp: print(hlp_txt)
def run(settings, debug): ''' Main setup function. Takes configuration values and passes them to the appropriate threads and functions. :param dict settings: settings dictionary (see :ref:`defaults` for guidance) :param bool debug: whether or not to show debug output (should be turned off if starting as daemon) ''' global PLOTTER, SOUND # handler for the exit signal signal.signal(signal.SIGINT, handler) if TESTING: global TESTQUEUE # initialize the test data to read information from file and put it on the port TESTQUEUE = Queue( ) # separate from client library because this is not downstream of the producer tdata = TestData(q=TESTQUEUE, data_file=TESTFILE, port=settings['settings']['port']) tdata.start() # initialize the central library rs.initRSlib(dport=settings['settings']['port'], rsstn=settings['settings']['station']) H.conn_stats(TESTING) if TESTING: T.TEST['n_port'][1] = True # port has been opened if rs.sps == 0: printE( 'There is already a Raspberry Shake sending data to this port.', sender=SENDER) printE( 'For testing, please change the port in your settings file to an unused one.', sender=SENDER, spaces=True) _xit(1) output_dir = settings['settings']['output_dir'] if settings['printdata']['enabled']: # set up queue and process q = mk_q() prnt = PrintRaw(q, testing=TESTING) mk_p(prnt) if settings['write']['enabled']: global WRITER # set up queue and process cha = settings['write']['channels'] q = mk_q() WRITER = Write(q=q, data_dir=output_dir, cha=cha, testing=TESTING) mk_p(WRITER) if settings['plot']['enabled'] and MPL: while True: if rs.numchns == 0: time.sleep(0.01) continue else: break cha = settings['plot']['channels'] sec = settings['plot']['duration'] spec = settings['plot']['spectrogram'] full = settings['plot']['fullscreen'] kiosk = settings['plot']['kiosk'] screencap = settings['plot']['eq_screenshots'] alert = settings['alert']['enabled'] if settings['plot']['deconvolve']: if settings['plot']['units'].upper() in rs.UNITS: deconv = settings['plot']['units'].upper() else: deconv = 'CHAN' else: deconv = False pq = mk_q() PLOTTER = Plot(cha=cha, seconds=sec, spectrogram=spec, fullscreen=full, kiosk=kiosk, deconv=deconv, q=pq, screencap=screencap, alert=alert, testing=TESTING) # no mk_p() here because the plotter must be controlled by the main thread (this one) if settings['forward']['enabled']: # put settings in namespace addr = settings['forward']['address'] port = settings['forward']['port'] cha = settings['forward']['channels'] fwd_data = settings['forward']['fwd_data'] fwd_alarms = settings['forward']['fwd_alarms'] # set up queue and process if len(addr) == len(port): printM('Initializing %s Forward threads' % (len(addr)), sender=SENDER) for i in range(len(addr)): q = mk_q() forward = Forward(num=i, addr=addr[i], port=int(port[i]), cha=cha, fwd_data=fwd_data, fwd_alarms=fwd_alarms, q=q, testing=TESTING) mk_p(forward) else: printE( 'List length mismatch: %s addresses and %s ports in forward section of settings file' % (len(addr), len(port)), sender=SENDER) _xit(1) if settings['alert']['enabled']: # put settings in namespace sta = settings['alert']['sta'] lta = settings['alert']['lta'] thresh = settings['alert']['threshold'] reset = settings['alert']['reset'] bp = [settings['alert']['highpass'], settings['alert']['lowpass']] cha = settings['alert']['channel'] if settings['alert']['deconvolve']: if settings['alert']['units'].upper() in rs.UNITS: deconv = settings['alert']['units'].upper() else: deconv = 'CHAN' else: deconv = False # set up queue and process q = mk_q() alrt = Alert(sta=sta, lta=lta, thresh=thresh, reset=reset, bp=bp, cha=cha, debug=debug, q=q, testing=TESTING, deconv=deconv) mk_p(alrt) if settings['alertsound']['enabled']: soundloc = os.path.expanduser( os.path.expanduser(settings['alertsound']['mp3file'])) if soundloc in ['doorbell', 'alarm', 'beeps', 'sonar']: soundloc = pr.resource_filename( 'rsudp', os.path.join('rs_sounds', '%s.mp3' % soundloc)) q = mk_q() alsnd = AlertSound(q=q, testing=TESTING, soundloc=soundloc) mk_p(alsnd) runcustom = False try: f = False win_ovr = False if settings['custom']['enabled']: # put settings in namespace f = settings['custom']['codefile'] win_ovr = settings['custom']['win_override'] if f == 'n/a': f = False runcustom = True except KeyError as e: if settings['alert']['exec'] != 'eqAlert': printW( 'the custom code function has moved to its own module (rsudp.c_custom)', sender='Custom') f = settings['alert']['exec'] win_ovr = settings['alert']['win_override'] runcustom = True else: raise KeyError(e) if runcustom: # set up queue and process q = mk_q() cstm = Custom(q=q, codefile=f, win_ovr=win_ovr, testing=TESTING) mk_p(cstm) if settings['tweets']['enabled']: global TWITTER consumer_key = settings['tweets']['api_key'] consumer_secret = settings['tweets']['api_secret'] access_token = settings['tweets']['access_token'] access_token_secret = settings['tweets']['access_secret'] tweet_images = settings['tweets']['tweet_images'] extra_text = settings['tweets']['extra_text'] q = mk_q() TWITTER = Tweeter(q=q, consumer_key=consumer_key, consumer_secret=consumer_secret, access_token=access_token, access_token_secret=access_token_secret, tweet_images=tweet_images, extra_text=extra_text, testing=TESTING) mk_p(TWITTER) if settings['telegram']['enabled']: global TELEGRAM token = settings['telegram']['token'] chat_ids = settings['telegram']['chat_id'].strip(' ').split(',') send_images = settings['telegram']['send_images'] extra_text = settings['telegram']['extra_text'] for chat_id in chat_ids: sender = "Telegram id %s" % (chat_id) q = mk_q() TELEGRAM = Telegrammer(q=q, token=token, chat_id=chat_id, send_images=send_images, extra_text=extra_text, sender=sender, testing=TESTING) mk_p(TELEGRAM) if settings['rsam']['enabled']: # put settings in namespace fwaddr = settings['rsam']['fwaddr'] fwport = settings['rsam']['fwport'] fwformat = settings['rsam']['fwformat'] interval = settings['rsam']['interval'] cha = settings['rsam']['channel'] quiet = settings['rsam']['quiet'] if settings['rsam']['deconvolve']: if settings['rsam']['units'].upper() in rs.UNITS: deconv = settings['rsam']['units'].upper() else: deconv = 'CHAN' else: deconv = False # set up queue and process q = mk_q() rsam = RSAM(q=q, interval=interval, cha=cha, deconv=deconv, fwaddr=fwaddr, fwport=fwport, fwformat=fwformat, quiet=quiet, testing=TESTING) mk_p(rsam) # start additional modules here! ################################ ################################ if TESTING: # initialize test consumer q = mk_q() test = Testing(q=q) mk_p(test) # start the producer, consumer, and activated modules start() PLOTTER = False if not TESTING: _xit() else: printW('Client has exited, ending tests...', sender=SENDER, announce=False)
def test(): ''' .. versionadded:: 0.4.3 Set up tests, run modules, report test results. For a list of tests run, see :py:mod:`rsudp.test`. ''' global TESTFILE hlp_txt = ''' ########################################### ## R A S P B E R R Y S H A K E ## ## Testing Module ## ## by Ian Nesbitt ## ## GNU GPLv3 2020 ## ## ## ## Test settings with archived Shake ## ## data to determine optimal ## ## configuration. ## ## ## ## Requires: ## ## - numpy, obspy, matplotlib 3 ## ## ## ########################################### Usage: rs-test [ OPTIONS ] where OPTIONS := { -h | --help display this help message -f | --file=default or /path/to/data/file specify the path to a seismic data file -s | --settings=/path/to/settings/json specify the path to a JSON-formatted settings file -b | --no-plot "blind mode", used when there is no display -q | --no-sound "quiet mode", used when there is no audio device/ffmpeg } rs-test with no arguments will start the test with default settings and the data file at %s ''' % (TESTFILE) test_mode(True) settings = H.default_settings(verbose=False) settings_are_default = True plot = True quiet = False customfile = False try: opts = getopt.getopt( sys.argv[1:], 'hf:s:bq', ['help', 'file=', 'settings=', 'no-plot', 'no-sound'])[0] except Exception as e: print(COLOR['red'] + 'ERROR: %s' % e + COLOR['white']) print(hlp_txt) exit(1) for o, a in opts: # parse options and arguments if o in ('-h', '--help'): print(hlp_txt) exit(0) if o in ('-f', '--file='): ''' The data file. ''' a = os.path.expanduser(a) if os.path.exists(a): try: out = '%s.txt' % (a) packetize(inf=a, outf=out, testing=True) TESTFILE = out customfile = True # using a custom miniseed file for testing except Exception as e: print(hlp_txt) print(COLOR['red'] + 'ERROR: %s' % e + COLOR['white']) exit(1) if o in ('-s', '--settings='): ''' Dump the settings to a file, specified after the `-d` flag, or `-d default` to let the software decide where to put it. ''' settings_loc = os.path.abspath(os.path.expanduser(a)).replace( '\\', '/') if os.path.exists(settings_loc): settings = H.read_settings(settings_loc) settings_are_default = False else: print(COLOR['red'] + 'ERROR: could not find settings file at %s' % (a) + COLOR['white']) exit(1) if o in ('-b', '--no-plot'): plot = False if o in ('-q', '--no-sound'): quiet = True if not customfile: # we are just using the default miniseed file packetize(inf=TESTFILE + '.ms', outf=TESTFILE, testing=True) T.TEST['n_internet'][1] = T.is_connected('www.google.com') if settings_are_default: settings = T.make_test_settings(settings=settings, inet=T.TEST['n_internet'][1]) T.TEST['p_log_dir'][1] = T.logdir_permissions() T.TEST['p_log_file'][1] = start_logging(testing=True) T.TEST['p_log_std'][1] = add_debug_handler(testing=True) T.TEST['p_output_dirs'][1] = init_dirs( os.path.expanduser(settings['settings']['output_dir'])) T.TEST['p_data_dir'][1] = T.datadir_permissions( os.path.expanduser(settings['settings']['output_dir'])) T.TEST['p_screenshot_dir'][1] = T.ss_permissions( os.path.expanduser(settings['settings']['output_dir'])) settings = T.cancel_tests(settings, MPL, plot, quiet) try: run(settings, debug=True) # client test ctest = 'client test' if (T.TEST['c_miniseed'] and WRITER): printM('Merging and testing MiniSEED file(s)...', sender=ctest) try: ms = rs.Stream() for outfile in WRITER.outfiles: if os.path.exists(outfile): T.TEST['c_miniseed'][1] = True ms = ms + rs.read(outfile) dn, fn = os.path.dirname(outfile), os.path.basename( outfile) os.replace(outfile, os.path.join(dn, 'test.' + fn)) else: raise FileNotFoundError('MiniSEED file not found: %s' % outfile) printM('Renamed test file(s).', sender=ctest) printM(ms.merge().__str__()) except Exception as e: printE(e) T.TEST['c_miniseed'][1] = False except Exception as e: printE(traceback.format_exc(), announce=False) printE('Ending tests.', sender=SENDER, announce=False) time.sleep(0.5) TESTQUEUE.put(b'ENDTEST') printW('Test finished.', sender=SENDER, announce=False) print() code = 0 printM('Test results:') for i in T.TEST: printM('%s: %s' % (T.TEST[i][0], T.TRANS[T.TEST[i][1]])) if not T.TEST[i][1]: # if a test fails, change the system exit code to indicate an error occurred code = 1 _xit(code)
def initRSlib(dport=port, rsstn='Z0000', timeout=10): ''' .. role:: pycode(code) :language: python Initializes this library (:py:func:`rsudp.raspberryshake`). Set values for data port, station, network, and port timeout prior to opening the socket. Calls both :py:func:`rsudp.raspberryshake.openSOCK` and :py:func:`rsudp.raspberryshake.set_params`. .. code-block:: python >>> import rsudp.raspberryshake as rs >>> rs.initRSlib(dport=8888, rsstn='R3BCF') The library is now initialized: .. code-block:: python >>> rs.initd True :param int dport: The local port the Raspberry Shake is sending UDP data packets to. Defaults to :pycode:`8888`. :param str rsstn: The name of the station (something like :pycode:`'RCB43'` or :pycode:`'S0CDE'`) :param int timeout: The number of seconds for :py:func:`rsudp.raspberryshake.set_params` to wait for data before an error is raised (zero for unlimited wait) :rtype: str :return: The instrument channel as a string ''' global port, stn, to, initd, port global producer sender = 'RS lib' printM('Initializing rsudp v %s.' % (__version__), sender) try: # set port value first if dport == int(dport): port = int(dport) else: port = int(dport) printW( 'Supplied port value was converted to integer. Non-integer port numbers are invalid.' ) except Exception as e: printE('Details - %s' % e) try: # set station name if len(rsstn) == 5: stn = str(rsstn).upper() else: stn = str(rsstn).upper() printW( 'Station name does not follow Raspberry Shake naming convention.' ) except ValueError as e: printE('Invalid station name supplied. Details: %s' % e) printE('reverting to station name Z0000', announce=False, spaces=True) except Exception as e: printE('Details - %s' % e) try: # set timeout value to = int(timeout) except ValueError as e: printW( 'You likely supplied a non-integer as the timeout value. Your value was: %s' % timeout) printW('Continuing with default timeout of %s sec' % (to), announce=False, spaces=True) printW('details: %s' % e, announce=False, spaces=True) except Exception as e: printE('Details - %s' % e) initd = True # if initialization goes correctly, set initd to true openSOCK() # open a socket printM('Waiting for UDP data on port %s...' % (port), sender) set_params() # get data and set parameters