01234567890123456789012345678901234567890123456789012345678901234567890123456789
239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 41564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196 44274428442944304431443244334434443544364437443844394440444144424443444444454446 444744484449445044514452445344544455445644574458445944604461446244634464446544664467 47324733473447354736473747384739474047414742474347444745474647474748474947504751 475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802 57105711571257135714571557165717571857195720572157225723572457255726572757285729 5730 5731 57325733573457355736573757385739574057415742574357445745574657475748574957505751 | <----SKIPPED LINES----> #if running on raspberry, then need to prepend path to file names if RASPBERRY_PI: PICKLE_FLIGHTS = MESSAGEBOARD_PATH + PICKLE_FLIGHTS PICKLE_DASHBOARD = MESSAGEBOARD_PATH + PICKLE_DASHBOARD LOGFILE = MESSAGEBOARD_PATH + LOGFILE PICKLE_DUMP_JSON_FILE = MESSAGEBOARD_PATH + PICKLE_DUMP_JSON_FILE PICKLE_FA_JSON_FILE = MESSAGEBOARD_PATH + PICKLE_FA_JSON_FILE CODE_REPOSITORY = MESSAGEBOARD_PATH HISTOGRAM_CONFIG_FILE = WEBSERVER_PATH + HISTOGRAM_CONFIG_FILE CONFIG_FILE = WEBSERVER_PATH + CONFIG_FILE ROLLING_MESSAGE_FILE = WEBSERVER_PATH + ROLLING_MESSAGE_FILE ALL_MESSAGE_FILE = WEBSERVER_PATH + ALL_MESSAGE_FILE ROLLING_LOGFILE = WEBSERVER_PATH + ROLLING_LOGFILE STDERR_FILE = WEBSERVER_PATH + STDERR_FILE BACKUP_FILE = WEBSERVER_PATH + BACKUP_FILE SERVICE_VERIFICATION_FILE = WEBSERVER_PATH + SERVICE_VERIFICATION_FILE UPTIMES_FILE = WEBSERVER_PATH + UPTIMES_FILE HISTOGRAM_IMAGE_PREFIX = WEBSERVER_PATH + WEBSERVER_IMAGE_FOLDER + HISTOGRAM_IMAGE_PREFIX HISTOGRAM_IMAGE_HTML = WEBSERVER_PATH + HISTOGRAM_IMAGE_HTML HOURLY_IMAGE_FILE = WEBSERVER_PATH + WEBSERVER_IMAGE_FOLDER + HOURLY_IMAGE_FILE VERSION_REPOSITORY = WEBSERVER_PATH + VERSION_REPOSITORY TIMEZONE = 'US/Pacific' # timezone of display TZ = pytz.timezone(TIMEZONE) KNOWN_AIRPORTS = ('SJC', 'SFO', 'OAK') # iata codes that we don't need to expand SPLITFLAP_CHARS_PER_LINE = 22 SPLITFLAP_LINE_COUNT = 6 DIRECTIONS_4 = ['N', 'E', 'S', 'W'] DIRECTIONS_8 = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'] DIRECTIONS_16 = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'] HOURS = ['12a', ' 1a', ' 2a', ' 3a', ' 4a', ' 5a', ' 6a', ' 7a', ' 8a', ' 9a', '10a', '11a', '12p', ' 1p', ' 2p', ' 3p', ' 4p', ' 5p', ' 6p', ' 7p', ' 8p', ' 9p', '10p', '11p'] <----SKIPPED LINES----> max_distance_feet=max_distance_feet, max_altitude_feet=max_altitude_feet, normalize_factor=normalize_factor, exhaustive=exhaustive) if position: matplotlib.pyplot.subplot(*position) matplotlib.pyplot.figure(figsize=figsize_inches) values_coordinates = numpy.arange(len(keys)) matplotlib.pyplot.bar(values_coordinates, values) # The filtering may have removed any flight data, or there may be none to start if not filtered_data: return earliest_flight_time = int(filtered_data[0]['now']) last_flight_time = int(filtered_data[-1]['now']) date_range_string = ' %d flights over last %s hours' % ( sum(values), SecondsToDdHh(last_flight_time - earliest_flight_time)) timestamp_string = 'Last updated %s' % EpochDisplayTime( time.time(), format_string='%b %-d %H:%M') full_title = '\n'.join([title, date_range_string, timestamp_string]) matplotlib.pyplot.title(full_title) matplotlib.pyplot.subplots_adjust(bottom=0.15, left=0.09, right=0.99, top=0.89) matplotlib.pyplot.xticks( values_coordinates, keys, rotation='vertical', wrap=True, horizontalalignment='right', verticalalignment='center') def HistogramSettingsHours(how_much_history): """Extracts the desired history (in hours) from the histogram configuration string. Args: how_much_history: string from the histogram config file. Returns: <----SKIPPED LINES----> if this_histogram == 'all': this_histogram = histogram['generate'] (key, sort, title, hours) = HistogramSettingsKeySortTitle( this_histogram, hours, flights) # if multiple histograms are getting generated, this might take a few seconds; # logging a heartbeat with each histogram ensures that monitoring.py does not # mistake this pause for a hang. if heartbeat: Heartbeat() CreateSingleHistogramChart( flights, key, sort, title, truncate=histogram.get('truncate', TRUNCATE), hours=hours, exhaustive=histogram.get('exhaustive', False)) filename = filename_prefix + histogram['generate'] + '.' + filename_suffix matplotlib.pyplot.savefig(filename) matplotlib.pyplot.close() histograms_generated.append((histogram['generate'], filename)) return histograms_generated def MessageboardHistograms( flights, which_histograms, how_much_history, max_screens, data_summary): """Generates multiple split flap screen histograms. Args: flights: the iterable of the raw data from which the histogram will be generated; each element of the iterable is a dictionary, that contains at least the key 'now', and depending on other parameters, also potentially 'min_feet' amongst others. which_histograms: string paramater indicating which histogram(s) to generate, which <----SKIPPED LINES----> Returns: List of histogram messages, if text-based histograms are selected; empty list otherwise. """ histogram_messages = [] if histogram_settings['type'] in ('messageboard', 'both'): histogram_messages = MessageboardHistograms( flights, histogram_settings['histogram'], histogram_settings['histogram_history'], histogram_settings['histogram_max_screens'], histogram_settings.get('histogram_data_summary', False)) if histogram_settings['type'] in ('images', 'both'): # Since Google Chrome seems to ignore all instructions to not cache, we need # to make sure we do not reuse file names - hence the epoch_string - and then # we need to 1) update the histograms.html file with the correct file links, and # 2) delete the images that are now obsolete. epoch_string = '%d_' % round(time.time()) generated_histograms = ImageHistograms( flights, histogram_settings['histogram'], histogram_settings['histogram_history'], filename_prefix=HISTOGRAM_IMAGE_PREFIX + epoch_string) html_lines = ReadFile(HISTOGRAM_IMAGE_HTML).split('\n') replaced_images = [] for identifier, new_filename in generated_histograms: # for each histogram, find the html_line with the matching id # Example line: <img id='destination' src='images/histogram_destination.png'><p> n, line = None, None # addresses pylint complaint for n, line in enumerate(html_lines): if identifier in line: break start_char = line.find(WEBSERVER_IMAGE_FOLDER) + len(WEBSERVER_IMAGE_FOLDER) end_character = ( line.find(HISTOGRAM_IMAGE_SUFFIX, start_char) + len(HISTOGRAM_IMAGE_SUFFIX)) old_filename = line[start_char:end_character] line = line.replace(old_filename, new_filename) html_lines[n] = line replaced_images.append(line[start_char:end_character]) new_html = '\n'.join(html_lines) WriteFile(HISTOGRAM_IMAGE_HTML, new_html) # Remove those obsoleted files for f in old_filename: if os.path.exists(f): try: os.remove(f) except PermissionError: pass return histogram_messages def SaveFlightsByAltitudeDistanceCSV( flights, max_days=0, filename='flights_by_alt_dist.csv', precision=100): """Extracts hourly histogram into text file for a variety of altitudes and distances. Generates a csv with 26 columns: - col#1: altitude (in feet) - col#2: distance (in feet) - cols#3-26: hour of the day The first row is a header row; subsequent rows list the number of flights that have occurred in the last max_days with an altitude and min distance less than that identified in the first two columns. Each row increments elevation or altitude by precision feet, up to the max determined by the max altitude and max distance amongst all the flights. <----SKIPPED LINES----> (False). subsystem: A tuple describing the system; though that description may have multiple attributes, the 0th element is the numeric identifier of that system. monitoring.py depends on other attributes of that tuple being present as well. Since the overall system does not have a tuple defined for it, it gets a default identifier of 0. failure_message: an (optional) message describing why the system / subsystem is being disabled or failing. """ versions = (VERSION_MESSAGEBOARD, VERSION_ARDUINO) if subsystem: subsystem = subsystem[0] PickleObjectToFile( (time.time(), subsystem, value, versions, failure_message), PICKLE_DASHBOARD, True) def RemoveFile(file): """Removes a file if it exists, returning a boolean indicating if it had existed.""" if os.path.exists(file): os.remove(file) return True return False def ConfirmNewFlight(flight, flights): """Replaces last-seen flight with new flight if otherwise identical but for identifiers. Flights are identified by the radio over time by a tuple of identifiers: flight_number and squawk. Due to unknown communication issues, one or the other may not always be transmitted. However, as soon as a new flight is identified that has at least one of those identifiers, we report on it and log it to the pickle repository, etc. This function checks if the newly identified flight is indeed a duplicate of the immediate prior flight by virtue of having the same squawk and/or flight number, and further, if the paths overlap. If the paths do not overlap, then its likely that the same flight was seen some minutes apart, and should legitimately be treated as a different flight. If the new flight is an updated version, then we should replace the prior-pickled-to- disk flight and replace the last flight in flights with this new version. <----SKIPPED LINES----> |
01234567890123456789012345678901234567890123456789012345678901234567890123456789
239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 41564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196 442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468 473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780 47814782478347844785478647874788478947904791479247934794479547964797479847994800 57085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754 | <----SKIPPED LINES----> #if running on raspberry, then need to prepend path to file names if RASPBERRY_PI: PICKLE_FLIGHTS = MESSAGEBOARD_PATH + PICKLE_FLIGHTS PICKLE_DASHBOARD = MESSAGEBOARD_PATH + PICKLE_DASHBOARD LOGFILE = MESSAGEBOARD_PATH + LOGFILE PICKLE_DUMP_JSON_FILE = MESSAGEBOARD_PATH + PICKLE_DUMP_JSON_FILE PICKLE_FA_JSON_FILE = MESSAGEBOARD_PATH + PICKLE_FA_JSON_FILE CODE_REPOSITORY = MESSAGEBOARD_PATH HISTOGRAM_CONFIG_FILE = WEBSERVER_PATH + HISTOGRAM_CONFIG_FILE CONFIG_FILE = WEBSERVER_PATH + CONFIG_FILE ROLLING_MESSAGE_FILE = WEBSERVER_PATH + ROLLING_MESSAGE_FILE ALL_MESSAGE_FILE = WEBSERVER_PATH + ALL_MESSAGE_FILE ROLLING_LOGFILE = WEBSERVER_PATH + ROLLING_LOGFILE STDERR_FILE = WEBSERVER_PATH + STDERR_FILE BACKUP_FILE = WEBSERVER_PATH + BACKUP_FILE SERVICE_VERIFICATION_FILE = WEBSERVER_PATH + SERVICE_VERIFICATION_FILE UPTIMES_FILE = WEBSERVER_PATH + UPTIMES_FILE WEBSERVER_IMAGE_FOLDER = WEBSERVER_PATH + WEBSERVER_IMAGE_FOLDER HISTOGRAM_IMAGE_HTML = WEBSERVER_PATH + HISTOGRAM_IMAGE_HTML HOURLY_IMAGE_FILE = WEBSERVER_IMAGE_FOLDER + HOURLY_IMAGE_FILE VERSION_REPOSITORY = WEBSERVER_PATH + VERSION_REPOSITORY TIMEZONE = 'US/Pacific' # timezone of display TZ = pytz.timezone(TIMEZONE) KNOWN_AIRPORTS = ('SJC', 'SFO', 'OAK') # iata codes that we don't need to expand SPLITFLAP_CHARS_PER_LINE = 22 SPLITFLAP_LINE_COUNT = 6 DIRECTIONS_4 = ['N', 'E', 'S', 'W'] DIRECTIONS_8 = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'] DIRECTIONS_16 = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'] HOURS = ['12a', ' 1a', ' 2a', ' 3a', ' 4a', ' 5a', ' 6a', ' 7a', ' 8a', ' 9a', '10a', '11a', '12p', ' 1p', ' 2p', ' 3p', ' 4p', ' 5p', ' 6p', ' 7p', ' 8p', ' 9p', '10p', '11p'] <----SKIPPED LINES----> max_distance_feet=max_distance_feet, max_altitude_feet=max_altitude_feet, normalize_factor=normalize_factor, exhaustive=exhaustive) if position: matplotlib.pyplot.subplot(*position) matplotlib.pyplot.figure(figsize=figsize_inches) values_coordinates = numpy.arange(len(keys)) matplotlib.pyplot.bar(values_coordinates, values) # The filtering may have removed any flight data, or there may be none to start if not filtered_data: return earliest_flight_time = int(filtered_data[0]['now']) last_flight_time = int(filtered_data[-1]['now']) date_range_string = ' %d flights over last %s hours' % ( sum(values), SecondsToDdHh(last_flight_time - earliest_flight_time)) timestamp_string = 'Last updated %s' % EpochDisplayTime( time.time(), format_string='%b %-d, %-I:%M%p') full_title = '\n'.join([title, date_range_string, timestamp_string]) matplotlib.pyplot.title(full_title) matplotlib.pyplot.subplots_adjust(bottom=0.15, left=0.09, right=0.99, top=0.89) matplotlib.pyplot.xticks( values_coordinates, keys, rotation='vertical', wrap=True, horizontalalignment='right', verticalalignment='center') def HistogramSettingsHours(how_much_history): """Extracts the desired history (in hours) from the histogram configuration string. Args: how_much_history: string from the histogram config file. Returns: <----SKIPPED LINES----> if this_histogram == 'all': this_histogram = histogram['generate'] (key, sort, title, hours) = HistogramSettingsKeySortTitle( this_histogram, hours, flights) # if multiple histograms are getting generated, this might take a few seconds; # logging a heartbeat with each histogram ensures that monitoring.py does not # mistake this pause for a hang. if heartbeat: Heartbeat() CreateSingleHistogramChart( flights, key, sort, title, truncate=histogram.get('truncate', TRUNCATE), hours=hours, exhaustive=histogram.get('exhaustive', False)) filename = filename_prefix + histogram['generate'] + '.' + filename_suffix filepath = WEBSERVER_IMAGE_FOLDER + filename matplotlib.pyplot.savefig(filepath) matplotlib.pyplot.close() histograms_generated.append((histogram['generate'], filename)) return histograms_generated def MessageboardHistograms( flights, which_histograms, how_much_history, max_screens, data_summary): """Generates multiple split flap screen histograms. Args: flights: the iterable of the raw data from which the histogram will be generated; each element of the iterable is a dictionary, that contains at least the key 'now', and depending on other parameters, also potentially 'min_feet' amongst others. which_histograms: string paramater indicating which histogram(s) to generate, which <----SKIPPED LINES----> Returns: List of histogram messages, if text-based histograms are selected; empty list otherwise. """ histogram_messages = [] if histogram_settings['type'] in ('messageboard', 'both'): histogram_messages = MessageboardHistograms( flights, histogram_settings['histogram'], histogram_settings['histogram_history'], histogram_settings['histogram_max_screens'], histogram_settings.get('histogram_data_summary', False)) if histogram_settings['type'] in ('images', 'both'): # Since Google Chrome seems to ignore all instructions to not cache, we need # to make sure we do not reuse file names - hence the epoch_string - and then # we need to 1) update the histograms.html file with the correct file links, and # 2) delete the images that are now obsolete. epoch_string = '%d_' % round(time.time()) generated_histograms = ImageHistograms( flights, histogram_settings['histogram'], histogram_settings['histogram_history'], filename_prefix=HISTOGRAM_IMAGE_PREFIX + epoch_string) html_lines = ReadFile(HISTOGRAM_IMAGE_HTML).split('\n') replaced_images = [] for identifier, new_filename in generated_histograms: # for each histogram, find the html_line with the matching id # Example line: <img id='destination' src='images/histogram_destination.png'><p> n, line = None, None # addresses pylint complaint for n, line in enumerate(html_lines): if identifier in line: break start_char = line.find(WEBSERVER_IMAGE_FOLDER) + len(WEBSERVER_IMAGE_FOLDER) end_character = ( line.find(HISTOGRAM_IMAGE_SUFFIX, start_char) + len(HISTOGRAM_IMAGE_SUFFIX)) old_filename = line[start_char:end_character] line = line.replace(old_filename, new_filename) html_lines[n] = line replaced_images.append(line[start_char:end_character]) new_html = '\n'.join(html_lines) WriteFile(HISTOGRAM_IMAGE_HTML, new_html) # Remove those obsoleted files for f in old_filename: RemoveFile(f) return histogram_messages def SaveFlightsByAltitudeDistanceCSV( flights, max_days=0, filename='flights_by_alt_dist.csv', precision=100): """Extracts hourly histogram into text file for a variety of altitudes and distances. Generates a csv with 26 columns: - col#1: altitude (in feet) - col#2: distance (in feet) - cols#3-26: hour of the day The first row is a header row; subsequent rows list the number of flights that have occurred in the last max_days with an altitude and min distance less than that identified in the first two columns. Each row increments elevation or altitude by precision feet, up to the max determined by the max altitude and max distance amongst all the flights. <----SKIPPED LINES----> (False). subsystem: A tuple describing the system; though that description may have multiple attributes, the 0th element is the numeric identifier of that system. monitoring.py depends on other attributes of that tuple being present as well. Since the overall system does not have a tuple defined for it, it gets a default identifier of 0. failure_message: an (optional) message describing why the system / subsystem is being disabled or failing. """ versions = (VERSION_MESSAGEBOARD, VERSION_ARDUINO) if subsystem: subsystem = subsystem[0] PickleObjectToFile( (time.time(), subsystem, value, versions, failure_message), PICKLE_DASHBOARD, True) def RemoveFile(file): """Removes a file if it exists, returning a boolean indicating if it had existed.""" if os.path.exists(file): try: os.remove(file) except PermissionError: return False return True return False def ConfirmNewFlight(flight, flights): """Replaces last-seen flight with new flight if otherwise identical but for identifiers. Flights are identified by the radio over time by a tuple of identifiers: flight_number and squawk. Due to unknown communication issues, one or the other may not always be transmitted. However, as soon as a new flight is identified that has at least one of those identifiers, we report on it and log it to the pickle repository, etc. This function checks if the newly identified flight is indeed a duplicate of the immediate prior flight by virtue of having the same squawk and/or flight number, and further, if the paths overlap. If the paths do not overlap, then its likely that the same flight was seen some minutes apart, and should legitimately be treated as a different flight. If the new flight is an updated version, then we should replace the prior-pickled-to- disk flight and replace the last flight in flights with this new version. <----SKIPPED LINES----> |