01234567890123456789012345678901234567890123456789012345678901234567890123456789
23456789101112131415161718192021222324252627282930313233343536373839404142 87888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 55735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613 | <----SKIPPED LINES----> import datetime import io import json import math import multiprocessing import numbers import os import pickle import queue import re import shutil import signal import statistics import sys import textwrap import time import bs4 import dateutil.relativedelta import filelock import numpy import matplotlib import matplotlib.pyplot import psutil import pycurl import pytz import requests import tzlocal import unidecode from constants import RASPBERRY_PI, MESSAGEBOARD_PATH, WEBSERVER_PATH import arduino if RASPBERRY_PI: import gpiozero # pylint: disable=E0401 import RPi.GPIO # pylint: disable=E0401 VERBOSE = False # additional messages logged <----SKIPPED LINES----> # At the time a flight is first identified as being of interest (in that it falls # within MIN_METERS meters of HOME), it - and core attributes derived from FlightAware, # if any - is appended to the end of this pickle file. However, since this file is # cached in working memory, flights older than 30 days are flushed from this periodically. PICKLE_FLIGHTS = 'pickle/flights.pk' # True splits all the flights created in simulation into separate date files, just like # the non-simulated runs; False consolidates all flights into one pickle file. SPLIT_SIMULATION_FLIGHT_PICKLE = False # Status data about messageboard - is it running, etc. Specifically, has tuples # of data (timestamp, system_id, status), where system_id is either the pin id of GPIO, # or a 0 to indicate overall system, and status is boolean PICKLE_DASHBOARD = 'pickle/dashboard.pk' CACHED_ELEMENT_PREFIX = 'cached_' # This web-exposed file is used for non-error messages that might highlight data or # code logic to check into. It is only cleared out manually. LOGFILE = 'log.txt' LOGFILE_LOCK = 'log.txt.lock' # Identical to the LOGFILE, except it includes just the most recent n lines. Newest # lines are at the end. ROLLING_LOGFILE = 'rolling_log.txt' #file for error messages # Users can trigger .png histograms analogous to the text ones from the web interface; # this is the folder (within WEBSERVER_PATH) where those files are placed WEBSERVER_IMAGE_FOLDER = 'images/' # Multiple histograms can be generated, i.e. for airline, aircraft, day of week, etc. # The output files are named by the prefix & suffix, i.e.: prefix + type + . + suffix, # as in histogram_aircraft.png. These names match up to the names expected by the html # page that displays the images. Also, note that the suffix is interpreted by matplotlib # to identify the image format to create. HISTOGRAM_IMAGE_PREFIX = 'histogram_' HISTOGRAM_IMAGE_SUFFIX = 'png' # For those of the approximately ten different types of histograms _not_ generated, # an empty image is copied into the location expected by the webpage instead; this is # the location of that "empty" image file. HISTOGRAM_EMPTY_IMAGE_FILE = 'empty.png' # This file indicates a pending request for histograms - either png, text-based, or <----SKIPPED LINES----> AIRCRAFT_LENGTH['Pilatus PC-12 (single-turboprop)'] = 14.4 def Log(message, file=None, rolling=None): """Write a message to a logfile along with a timestamp. Args: message: string message to write file: string representing file name and, if needed, path to the file to write to rolling: name of file that will keep only the last n files of file """ # can't define as a default parameter because LOGFILE name is potentially # modified based on SIMULATION flag if not file: file = LOGFILE # special case: for the main logfile, we always keep a rolling log if not rolling and file == LOGFILE: rolling = ROLLING_LOGFILE if file == LOGFILE: lock = filelock.FileLock(LOGFILE_LOCK) lock.acquire() try: with open(file, 'a') as f: # by excluding the timestamp, file diffs become easier between runs if not SIMULATION or file == LOGFILE: f.write('='*80+'\n') f.write(str(datetime.datetime.now(TZ))+'\n') f.write('\n') f.write(str(message)+'\n') except IOError: Log('Unable to append to ' + file) if rolling: existing_log_lines = ReadFile(file).splitlines() with open(rolling, 'w') as f: f.write('\n'.join(existing_log_lines[-1000:])) if file == LOGFILE: lock.release() def LogTimes(times): """Logs elapsed time messages from a list of epochs.""" msg = '' for n, t in enumerate(times[:-1]): msg += '%.2fs to get from reading %d to reading %s\n' % (times[n + 1] - t, n, n + 1) Log(msg) def MaintainRollingWebLog(message, max_count, filename=None): """Maintains a rolling text file of at most max_count printed messages. Newest data at top and oldest data at the end, of at most max_count messages, where the delimiter between each message is identified by a special fixed string. Args: message: text message to prepend to the file. max_count: maximum number of messages to keep in the file; the max_count+1st message is deleted. <----SKIPPED LINES----> version_name = python_prefix + last_modified_suffix + file_extension version_path = os.path.join(VERSION_REPOSITORY, version_name) if not os.path.exists(version_path): shutil.copyfile(live_path, version_path) return version_name global VERSION_MESSAGEBOARD global VERSION_ARDUINO VERSION_MESSAGEBOARD = MakeCopy('messageboard') VERSION_ARDUINO = MakeCopy('arduino') def main(): """Traffic cop between incoming radio flight messages, configuration, and messageboard. This is the main logic, checking for new flights, augmenting the radio signal with additional web-scraped data, and generating messages in a form presentable to the messageboard. """ RemoveFile(LOGFILE_LOCK) VersionControl() # Since this clears log files, it should occur first before we start logging if '-s' in sys.argv: global SIMULATION_COUNTER SimulationSetup() last_heartbeat_time = HeartbeatRestart() init_timing = [time.time()] # time 0 # This flag slows down simulation time around a flight, great for debugging the arduinos simulation_slowdown = bool('-f' in sys.argv) # Redirect any errors to a log file instead of the screen, and add a datestamp if not SIMULATION: sys.stderr = open(STDERR_FILE, 'a') Log('', STDERR_FILE) init_timing.append(time.time()) # time 1 Log('Starting up process %d' % os.getpid()) <----SKIPPED LINES----> |
01234567890123456789012345678901234567890123456789012345678901234567890123456789
23456789101112131415161718192021 2223242526272829303132333435363738394041 8687888990919293949596979899100101102103104105 106107108109110111112113114115116117118119120121122123124125 362363364365366367368369370371372373374375376377378379380381 382383384385386387388389390391392393394395396 397398399400401402403404405406407408409410411412413414415416 55645565556655675568556955705571557255735574557555765577557855795580558155825583 55845585558655875588558955905591559255935594559555965597559855995600560156025603 | <----SKIPPED LINES----> import datetime import io import json import math import multiprocessing import numbers import os import pickle import queue import re import shutil import signal import statistics import sys import textwrap import time import bs4 import dateutil.relativedelta import numpy import matplotlib import matplotlib.pyplot import psutil import pycurl import pytz import requests import tzlocal import unidecode from constants import RASPBERRY_PI, MESSAGEBOARD_PATH, WEBSERVER_PATH import arduino if RASPBERRY_PI: import gpiozero # pylint: disable=E0401 import RPi.GPIO # pylint: disable=E0401 VERBOSE = False # additional messages logged <----SKIPPED LINES----> # At the time a flight is first identified as being of interest (in that it falls # within MIN_METERS meters of HOME), it - and core attributes derived from FlightAware, # if any - is appended to the end of this pickle file. However, since this file is # cached in working memory, flights older than 30 days are flushed from this periodically. PICKLE_FLIGHTS = 'pickle/flights.pk' # True splits all the flights created in simulation into separate date files, just like # the non-simulated runs; False consolidates all flights into one pickle file. SPLIT_SIMULATION_FLIGHT_PICKLE = False # Status data about messageboard - is it running, etc. Specifically, has tuples # of data (timestamp, system_id, status), where system_id is either the pin id of GPIO, # or a 0 to indicate overall system, and status is boolean PICKLE_DASHBOARD = 'pickle/dashboard.pk' CACHED_ELEMENT_PREFIX = 'cached_' # This web-exposed file is used for non-error messages that might highlight data or # code logic to check into. It is only cleared out manually. LOGFILE = 'log.txt' # Identical to the LOGFILE, except it includes just the most recent n lines. Newest # lines are at the end. ROLLING_LOGFILE = 'rolling_log.txt' #file for error messages # Users can trigger .png histograms analogous to the text ones from the web interface; # this is the folder (within WEBSERVER_PATH) where those files are placed WEBSERVER_IMAGE_FOLDER = 'images/' # Multiple histograms can be generated, i.e. for airline, aircraft, day of week, etc. # The output files are named by the prefix & suffix, i.e.: prefix + type + . + suffix, # as in histogram_aircraft.png. These names match up to the names expected by the html # page that displays the images. Also, note that the suffix is interpreted by matplotlib # to identify the image format to create. HISTOGRAM_IMAGE_PREFIX = 'histogram_' HISTOGRAM_IMAGE_SUFFIX = 'png' # For those of the approximately ten different types of histograms _not_ generated, # an empty image is copied into the location expected by the webpage instead; this is # the location of that "empty" image file. HISTOGRAM_EMPTY_IMAGE_FILE = 'empty.png' # This file indicates a pending request for histograms - either png, text-based, or <----SKIPPED LINES----> AIRCRAFT_LENGTH['Pilatus PC-12 (single-turboprop)'] = 14.4 def Log(message, file=None, rolling=None): """Write a message to a logfile along with a timestamp. Args: message: string message to write file: string representing file name and, if needed, path to the file to write to rolling: name of file that will keep only the last n files of file """ # can't define as a default parameter because LOGFILE name is potentially # modified based on SIMULATION flag if not file: file = LOGFILE # special case: for the main logfile, we always keep a rolling log if not rolling and file == LOGFILE: rolling = ROLLING_LOGFILE try: with open(file, 'a') as f: # by excluding the timestamp, file diffs become easier between runs if not SIMULATION or file == LOGFILE: f.write('='*80+'\n') f.write(str(datetime.datetime.now(TZ))+'\n') f.write('\n') f.write(str(message)+'\n') except IOError: Log('Unable to append to ' + file) if rolling: existing_log_lines = ReadFile(file).splitlines() with open(rolling, 'w') as f: f.write('\n'.join(existing_log_lines[-1000:])) def LogTimes(times): """Logs elapsed time messages from a list of epochs.""" msg = '' for n, t in enumerate(times[:-1]): msg += '%.2fs to get from reading %d to reading %s\n' % (times[n + 1] - t, n, n + 1) Log(msg) def MaintainRollingWebLog(message, max_count, filename=None): """Maintains a rolling text file of at most max_count printed messages. Newest data at top and oldest data at the end, of at most max_count messages, where the delimiter between each message is identified by a special fixed string. Args: message: text message to prepend to the file. max_count: maximum number of messages to keep in the file; the max_count+1st message is deleted. <----SKIPPED LINES----> version_name = python_prefix + last_modified_suffix + file_extension version_path = os.path.join(VERSION_REPOSITORY, version_name) if not os.path.exists(version_path): shutil.copyfile(live_path, version_path) return version_name global VERSION_MESSAGEBOARD global VERSION_ARDUINO VERSION_MESSAGEBOARD = MakeCopy('messageboard') VERSION_ARDUINO = MakeCopy('arduino') def main(): """Traffic cop between incoming radio flight messages, configuration, and messageboard. This is the main logic, checking for new flights, augmenting the radio signal with additional web-scraped data, and generating messages in a form presentable to the messageboard. """ VersionControl() # Since this clears log files, it should occur first before we start logging if '-s' in sys.argv: global SIMULATION_COUNTER SimulationSetup() last_heartbeat_time = HeartbeatRestart() init_timing = [time.time()] # time 0 # This flag slows down simulation time around a flight, great for debugging the arduinos simulation_slowdown = bool('-f' in sys.argv) # Redirect any errors to a log file instead of the screen, and add a datestamp if not SIMULATION: sys.stderr = open(STDERR_FILE, 'a') Log('', STDERR_FILE) init_timing.append(time.time()) # time 1 Log('Starting up process %d' % os.getpid()) <----SKIPPED LINES----> |