def __init__(self, testing=False, soundloc=False, q=False): """ .. _pydub.AudioSegment: https://github.com/jiaaro/pydub/blob/master/API.markdown#audiosegment Initializes the alert sound listener thread. Needs a pydub.AudioSegment_ to play and a :class:`queue.Queue` to listen on. """ super().__init__() self.sender = 'AlertSound' self.alive = True self.testing = testing self.sound = soundloc self.devnull = open(os.devnull, 'w') self._init_sound() 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 run(self): """ Gets and distributes queue objects to another address and port on the network. """ printM('Opening socket...', sender=self.sender) socket_type = s.SOCK_DGRAM if os.name in 'nt' else s.SOCK_DGRAM | s.SO_REUSEADDR sock = s.socket(s.AF_INET, socket_type) printM('Forwarding %s data to %s:%s' % (self.chans, self.addr, self.port), sender=self.sender) try: while self.running: p = self.queue.get() # get a packet self.queue.task_done() # close the queue if 'TERM' in str( p): # shutdown if there's a TERM message on the queue self.alive = False printM('Exiting.', self.sender) sys.exit() if rs.getCHN(p) in self.chans: sock.sendto(p, (self.addr, self.port)) except Exception as e: self.alive = False printE('%s' % e, sender=self.sender) sys.exit(2)
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 _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 openSOCK(host=''): ''' .. role:: pycode(code) :language: python Initialize a socket at the port specified by :pycode:`rsudp.raspberryshake.port`. Called by :py:func:`rsudp.raspberryshake.initRSlib`, must be done before :py:func:`rsudp.raspberryshake.set_params`. :param str host: self-referential location at which to open a listening port (defaults to :pycode:`''` which resolves to :pycode:`'localhost'`) :raise IOError: if the library is not initialized (:py:func:`rsudp.raspberryshake.initRSlib`) prior to running this function :raise OSError: if the program cannot bind to the specified port number ''' global sockopen sockopen = False if initd: HP = '%s:%s' % ('localhost', port) printM("Opening socket on %s (HOST:PORT)" % HP, 'openSOCK') try: sock.bind((host, port)) sockopen = True except Exception as e: printE('Could not bind to port %s. Is another program using it?' % port) printE('Detail: %s' % e, announce=False) raise OSError(e) else: raise IOError( "Before opening a socket, you must initialize this raspberryshake library by calling initRSlib(dport=XXXXX, rssta='R0E05') first." )
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 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=False, interval=5, cha='HZ', deconv=False, fwaddr=False, fwport=False, fwformat='LITE', quiet=False, testing=False, *args, **kwargs): """ Initializes the RSAM analysis thread. """ super().__init__() self.sender = 'RSAM' self.alive = True self.testing = testing self.quiet = quiet # suppresses printing of transmission stats self.stn = rs.stn self.fwaddr = fwaddr self.fwport = fwport self.fwformat = fwformat.upper() self.sock = False self.interval = interval self.default_ch = 'HZ' self.args = args self.kwargs = kwargs self.raw = rs.Stream() self.stream = rs.Stream() self.units = 'counts' self._set_deconv(deconv) self._set_channel(cha) self.rsam = [1, 1, 1] 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 _when_img(self, d): ''' Send a tweet with an image in when you get an ``IMGPATH`` message. :param bytes d: queue message ''' if self.tweet_images: imgpath = helpers.get_msg_path(d) imgtime = helpers.fsec(helpers.get_msg_time(d)) message = '%s %s UTC' % (self.message1, imgtime.strftime(self.fmt)[:22]) response = None if os.path.exists(imgpath): with open(imgpath, 'rb') as image: try: printM('Uploading image to Twitter %s' % (imgpath), self.sender) response = self.twitter.upload_media(media=image) time.sleep(5.1) printM('Sending tweet...', sender=self.sender) response = self.twitter.update_status(status=message, media_ids=response['media_id'], lat=rs.inv[0][0].latitude, long=rs.inv[0][0].longitude, geo_enabled=True, display_coordinates=True) # location will only stick to tweets on accounts that have location enabled in Settings printM('Tweeted with image: %s' % (message), sender=self.sender) url = 'https://twitter.com/%s/status/%s' % (response['user']['screen_name'], response['id_str']) printM('Tweet URL: %s' % url) except Exception as e: printE('could not send multimedia tweet - %s' % (e)) try: printM('Waiting 5 seconds and trying to send tweet again...', sender=self.sender) time.sleep(5.1) self.auth() printM('Uploading image to Twitter (2nd try) %s' % (imgpath), self.sender) response = self.twitter.upload_media(media=image) time.sleep(5.1) printM('Sending tweet...', sender=self.sender) response = self.twitter.update_status(status=message, media_ids=response['media_id'], lat=rs.inv[0][0].latitude, long=rs.inv[0][0].longitude, geo_enabled=True, display_coordinates=True) # location will only stick to tweets on accounts that have location enabled in Settings printM('Tweeted with image: %s' % (message), sender=self.sender) url = 'https://twitter.com/%s/status/%s' % (response['user']['screen_name'], response['id_str']) printM('Tweet URL: %s' % url) except Exception as e: printE('could not send multimedia tweet (2nd try) - %s' % (e)) response = None else: printM('Could not find image: %s' % (imgpath), sender=self.sender)
def _when_img(self, d): ''' Send a telegram image in when you get an ``IMGPATH`` message. :param bytes d: queue message ''' if self.send_images: imgpath = helpers.get_msg_path(d) response = None if os.path.exists(imgpath): with open(imgpath, 'rb') as image: try: if not self.testing: printM( 'Uploading image to Telegram %s' % (imgpath), self.sender) response = self.telegram.sendPhoto( chat_id=self.chat_id, photo=image) printM('Sent image', sender=self.sender) else: printM('Image ready to send - %s' % (imgpath), self.sender) TEST['c_telegramimg'][1] = True except Exception as e: printE('Could not send image - %s' % (e)) try: if not self.testing: printM( 'Waiting 5 seconds and trying to send again...', sender=self.sender) time.sleep(5.1) self.auth() printM( 'Uploading image to Telegram (2nd try) %s' % (imgpath), self.sender) response = self.telegram.sendPhoto( chat_id=self.chat_id, photo=image) printM('Sent image', sender=self.sender) else: # if you are here in testing mode, there is a problem TEST['c_telegramimg'][1] = False except Exception as e: printE('Could not send image - %s' % (e)) response = None else: printM('Could not find image: %s' % (imgpath), sender=self.sender)
def permissions(dp): ''' Test write permissions for the specified directory. :param str dp: the directory path to test permissions for :rtype: bool :return: if ``True``, the test was successful, ``False`` otherwise ''' dp = os.path.join(dp, 'test') try: with open(dp, 'w') as f: f.write('testing\n') os.remove(dp) return True except Exception as e: printE(e) return False
def run(self): """ Gets and distributes queue objects to another address and port on the network. """ printM('Opening socket...', sender=self.sender) socket_type = s.SOCK_DGRAM if os.name in 'nt' else s.SOCK_DGRAM | s.SO_REUSEADDR sock = s.socket(s.AF_INET, socket_type) msg_data = '%s data' % (self.chans) if self.fwd_data else '' msg_and = ' and ' if (self.fwd_data and self.fwd_alarms) else '' msg_alarms = 'ALARM / RESET messages' if self.fwd_alarms else '' printM('Forwarding %s%s%s to %s:%s' % (msg_data, msg_and, msg_alarms, self.addr, self.port), sender=self.sender) try: while self.running: p = self.queue.get() # get a packet self.queue.task_done() # close the queue if 'TERM' in str( p): # shutdown if there's a TERM message on the queue self._exit() if 'IMGPATH' in str(p): continue if ('ALARM' in str(p)) or ('RESET' in str(p)): if self.fwd_alarms: sock.sendto(p, (self.addr, self.port)) continue if "{'" in str(p): if (self.fwd_data) and (rs.getCHN(p) in self.chans): sock.sendto(p, (self.addr, self.port)) if self.testing: TEST['c_forward'][1] = True except Exception as e: self.alive = False printE('%s' % e, sender=self.sender) if self.testing: TEST['c_forward'][1] = False sys.exit(2)
def __init__(self, q=False, testing=False): """ Initializing the data printing process. """ super().__init__() self.sender = 'Print' self.alive = True self.testing = testing if q: self.queue = q else: printE('no queue passed to consumer! Thread will exit now!', self.sender) sys.stdout.flush() self.alive = False sys.exit() printM('Starting.', self.sender)
def _set_channel(self, cha): ''' This function sets the channel to listen to. Allowed values are as follows: - "SHZ"``, ``"EHZ"``, ``"EHN"`` or ``"EHE"`` - velocity channels - ``"ENZ"``, ``"ENN"``, ``"ENE"`` - acceleration channels - ``"HDF"`` - pressure transducer channel - ``"all"`` - resolves to either ``"EHZ"`` or ``"SHZ"`` if available :param cha: the channel to listen to :type cha: str ''' cha = self.default_ch if (cha == 'all') else cha self.cha = cha if isinstance(cha, str) else cha[0] if self.cha in str(rs.chns): self._find_chn() else: printE('Could not find channel %s in list of channels! Please correct and restart.' % self.cha, self.sender) sys.exit(2)
def _when_alarm(self, d): ''' Send a telegram in an alert scenario. :param bytes d: queue message ''' event_time = helpers.fsec(helpers.get_msg_time(d)) self.last_event_str = '%s' % (event_time.strftime(self.fmt)[:22]) message = '%s %s UTC - %s' % (self.message0, self.last_event_str, self.livelink) response = None try: printM('Sending alert...', sender=self.sender) response = self.telegram.sendMessage(chat_id=self.chat_id, text=message) printM('Sent Telegram: %s' % (message), sender=self.sender) except Exception as e: printE('Could not send alert - %s' % (e)) try: printE('Waiting 5 seconds and trying to send again...', sender=self.sender, spaces=True) time.sleep(5) self.auth() response = self.telegram.sendMessage(chat_id=self.chat_id, text=message) printM('Sent Telegram: %s' % (message), sender=self.sender) except Exception as e: printE('Could not send alert - %s' % (e)) response = None
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 handler(signum, frame, ip=ip): ''' The signal handler for the nodata alarm. :param int signum: signal number :param int frame: frame number :param str ip: the IP of the box this program is running on (i.e. the device the Raspberry Shake should send data to) :raise IOError: on UNIX systems if no data is received ''' global port printE('No data received in %s seconds; aborting.' % (to), sender='Init') printE('Check that the Shake is forwarding data to:', sender='Init', announce=False, spaces=True) printE('IP address: %s Port: %s' % (ip, port), sender='Init', announce=False, spaces=True) printE('and that no firewall exists between the Shake and this computer.', sender='Init', announce=False, spaces=True) raise IOError('No data received')
def __init__(self, token, chat_id, q=False, send_images=False, sender='Telegram'): """ Initializing the Telegram message posting thread. """ super().__init__() self.sender = sender self.alive = True self.send_images = send_images self.token = token self.chat_id = chat_id self.fmt = '%Y-%m-%d %H:%M:%S.%f' self.region = ' - region: %s' % rs.region.title() if rs.region else '' if q: self.queue = q else: printE('no queue passed to consumer! Thread will exit now!', self.sender) sys.stdout.flush() self.alive = False sys.exit() self.telegram = tg.Bot(token=self.token) self.livelink = 'live feed ➡️ https://raspberryshake.net/stationview/#?net=%s&sta=%s' % ( rs.net, rs.stn) self.message0 = '(Raspberry Shake station %s.%s%s) Event detected at' % ( rs.net, rs.stn, self.region) printM('Starting.', self.sender)
def __init__(self, consumer_key, consumer_secret, access_token, access_token_secret, q=False, tweet_images=False, ): """ Initialize the process """ super().__init__() self.sender = 'Tweeter' self.alive = True self.tweet_images = tweet_images self.fmt = '%Y-%m-%d %H:%M:%S.%f' self.region = ' - region: %s' % rs.region.title() if rs.region else '' self.consumer_key = consumer_key self.consumer_secret = consumer_secret self.access_token = access_token self.access_token_secret = access_token_secret if q: self.queue = q else: printE('no queue passed to consumer! Thread will exit now!', self.sender) sys.stdout.flush() self.alive = False sys.exit() self.twitter = Twython( consumer_key, consumer_secret, access_token, access_token_secret ) self.livelink = 'live feed ➡️ https://raspberryshake.net/stationview/#?net=%s&sta=%s' % (rs.net, rs.stn) self.message0 = '(#RaspberryShake station %s.%s%s) Event detected at' % (rs.net, rs.stn, self.region) self.message1 = '(#RaspberryShake station %s.%s%s) Image of event detected at' % (rs.net, rs.stn, self.region) printM('Starting.', self.sender)
def _when_alarm(self, d): ''' Send a tweet when you get an ``ALARM`` message. :param bytes d: queue message ''' event_time = helpers.fsec(helpers.get_msg_time(d)) self.last_event_str = '%s' % (event_time.strftime(self.fmt)[:22]) message = '%s %s UTC%s - %s' % (self.message0, self.last_event_str, self.extra_text, self.livelink) response = None try: printM('Tweet: %s' % (message), sender=self.sender) if not self.testing: response = self.twitter.update_status( status=message, lat=rs.inv[0][0].latitude, long=rs.inv[0][0].longitude, geo_enabled=True, display_coordinates=True) # location will only stick to tweets on accounts that have location enabled in Settings url = 'https://twitter.com/%s/status/%s' % ( response['user']['screen_name'], response['id_str']) printM('Tweet URL: %s' % url) if self.testing: TEST['c_tweet'][1] = True except Exception as e: printE('could not send alert tweet - %s' % (e)) try: printE('Waiting 5 seconds and trying to send tweet again...', sender=self.sender, spaces=True) time.sleep(5.1) printM('Tweet: %s' % (message), sender=self.sender) if not self.testing: self.auth() response = self.twitter.update_status( status=message, lat=rs.inv[0][0].latitude, long=rs.inv[0][0].longitude, geo_enabled=True, display_coordinates=True) # location will only stick to tweets on accounts that have location enabled in Settings url = 'https://twitter.com/%s/status/%s' % ( response['user']['screen_name'], response['id_str']) printM('Tweet URL: %s' % url) except Exception as e: printE('could not send alert tweet - %s' % (e)) response = None self.last_message = message
def _when_alarm(self, d): ''' Send a telegram in an alert scenario. :param bytes d: queue message ''' event_time = helpers.fsec(helpers.get_msg_time(d)) self.last_event_str = '%s' % (event_time.strftime(self.fmt)[:22]) message = '%s %s UTC%s - %s' % (self.message0, self.last_event_str, self.extra_text, self.livelink) response = None try: printM('Sending alert...', sender=self.sender) printM('Telegram message: %s' % (message), sender=self.sender) if not self.testing: response = self.telegram.sendMessage(chat_id=self.chat_id, text=message) else: TEST['c_telegram'][1] = True except Exception as e: printE('Could not send alert - %s' % (e)) try: printE('Waiting 5 seconds and trying to send again...', sender=self.sender, spaces=True) time.sleep(5) self.auth() printM('Telegram message: %s' % (message), sender=self.sender) if not self.testing: response = self.telegram.sendMessage(chat_id=self.chat_id, text=message) else: # if you are here in testing mode, there is a problem TEST['c_telegram'][1] = False except Exception as e: printE('Could not send alert - %s' % (e)) response = None self.last_message = message
def make_trace(d): ''' Makes a trace and assigns it some values using a data packet. In this example, we make a trace object with some RS 1Dv7 data: .. code-block:: python >>> import rsudp.raspberryshake as rs >>> rs.initRSlib(dport=8888, rsstn='R3BCF') >>> d = rs.getDATA() >>> t = rs.make_trace(d) >>> print(t) AM.R3BCF.00.EHZ | 2020-02-21T19:58:50.292000Z - 2020-02-21T19:58:50.532000Z | 100.0 Hz, 25 samples :param d: The Raspberry Shake UDP data packet (:py:func:`rsudp.raspberryshake.getDATA`) to parse Trace information from :type d: bytes :rtype: obspy.core.trace.Trace :return: A fully formed Trace object to build a Stream with ''' global INVWARN ch = getCHN(d) # channel if ch: t = getTIME( d ) # unix epoch time since 1970-01-01 00:00:00Z; "timestamp" in obspy st = getSTREAM(d) # samples in data packet in list [] format tr = Trace(data=np.ma.MaskedArray( st, dtype=np.int32)) # create empty trace tr.stats.network = net # assign values tr.stats.location = '00' tr.stats.station = stn tr.stats.channel = ch tr.stats.sampling_rate = sps tr.stats.starttime = UTCDateTime(t, precision=3) if inv: try: tr.stats.response = inv.get_response(tr.id, tr.stats.starttime) except Exception as e: if not INVWARN: INVWARN = True printE(e, sender='make_trace') printE('Could not attach inventory response.', sender='make_trace') printE('Are you sure you set the station name correctly?', spaces=True, sender='make_trace') printE( 'This could indicate a mismatch in the number of data channels', spaces=True, sender='make_trace') printE( 'between the inventory and the stream. For example,', spaces=True, sender='make_trace') printE('if you are receiving RS4D data, please make sure', spaces=True, sender='make_trace') printE('the inventory you download has 4 channels.', spaces=True, sender='make_trace') else: pass return tr
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 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 __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)
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' ) import matplotlib.pyplot as plt import matplotlib.dates as mdates import matplotlib.image as mpimg from matplotlib import rcParams from matplotlib.ticker import EngFormatter rcParams['toolbar'] = 'None' plt.ion()
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 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