Beispiel #1
0
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)
Beispiel #2
0
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
Beispiel #3
0
    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
Beispiel #4
0
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
Beispiel #5
0
    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)
Beispiel #6
0
 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)
Beispiel #7
0
 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)
Beispiel #8
0
	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)
Beispiel #9
0
    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)
Beispiel #10
0
    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()
Beispiel #11
0
    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)
Beispiel #12
0
    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.')
Beispiel #13
0
    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()
Beispiel #14
0
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'
            )
Beispiel #15
0
 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)
Beispiel #16
0
    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)
Beispiel #17
0
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
Beispiel #18
0
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)
Beispiel #19
0
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)
Beispiel #20
0
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)
Beispiel #21
0
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