messageboard-2020-06-09-1741.py
01234567890123456789012345678901234567890123456789012345678901234567890123456789









6263646566676869707172737475767778798081828384858687888990919293949596979899100101102








444544464447444844494450445144524453445444554456445744584459446044614462446344644465 44664467446844694470447144724473447444754476447744784479   44804481448244834484448544864487448844894490449144924493449444954496449744984499








473447354736473747384739474047414742474347444745474647474748474947504751475247534754 47554756475747584759476047614762476347644765476647674768476947704771477247734774








58285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868











                            <----SKIPPED LINES---->




METERS_PER_SECOND_IN_KNOTS = 0.514444

MIN_METERS = 5000/FEET_IN_METER # only planes within this distance will be detailed
# planes not seen within MIN_METERS in PERSISTENCE_SECONDS seconds will be dropped from
# the nearby list
PERSISTENCE_SECONDS = 300
TRUNCATE = 50  # max number of keys to include in a histogram image file
# number of seconds to pause between each radio poll / command processing loop
LOOP_DELAY_SECONDS = 1

# number of seconds to wait between recording heartbeats to the status file
HEARTBEAT_SECONDS = 10

# version control directory
CODE_REPOSITORY = ''
VERSION_REPOSITORY = 'versions/'
VERSION_WEBSITE_PATH = VERSION_REPOSITORY
VERSION_MESSAGEBOARD = None
VERSION_ARDUINO = None

MAX_INSIGHT_HORIZON_DAYS = 30

# This file is where the radio drops its json file
DUMP_JSON_FILE = '/run/readsb/aircraft.json'

# 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_'





                            <----SKIPPED LINES---->





    filename = (
        WEBSERVER_IMAGE_RELATIVE_FOLDER +  # i.e.: images/
        filename_prefix +                  # i.e.: histogram_
        histogram['generate'] +            # i.e.: destination
        '.' + filename_suffix)             # i.e.: .png
    filepath = WEBSERVER_PATH + filename   # i.e.: /var/www/html/ + 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
      can be either the special string 'all', or a string linked to a specific
      histogram.
    how_much_history: string parameter taking a value among ['today', '24h', '7d', '30d].
    max_screens: string parameter taking a value among ['_1', '_2', '_5', or 'all'].
    data_summary: parameter that evaluates to a boolean indicating whether the data
      summary screen in the histogram should be displayed.




  Returns:
    Returns a list of printable strings (with embedded new line characters) representing
    the histogram, for each screen in the histogram.
  """
  messages = []

  hours = HistogramSettingsHours(how_much_history)
  screen_limit = HistogramSettingsScreens(max_screens)

  histograms_to_generate = []
  if which_histograms in ['destination', 'all']:
    histograms_to_generate.append({
        'generate': 'destination',
        'suppress_percent_sign': True,
        'columns': 3})
  if which_histograms in ['origin', 'all']:
    histograms_to_generate.append({
        'generate': 'origin',
        'suppress_percent_sign': True,




                            <----SKIPPED LINES---->





  Args:
    flights: List of flight attribute dictionaries.
    histogram_settings: Dictionary of histogram parameters.
    heartbeat: boolean indicating whether we should log heartbeats between each histogram
      to make sure monitoring does not mistake this slow procedure for being hung; this
      should be set to false if this is called from outside of messageboard.main.

  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,
        heartbeat=heartbeat)
    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>
      identifier = '"%s"' % identifier  # add quotations to make sure its complete




                            <----SKIPPED LINES---->




        f, PICKLE_FLIGHTS, True, timestamp=f['now'], verify=False)

  return False


def HeartbeatRestart():
  """Logs a system down / system up pair of heartbeats as system is first starting."""
  if SIMULATION:
    return 0
  UpdateDashboard(True)  # Indicates that this wasn't running a moment before, ...
  UpdateDashboard(False)  # ... and now it is running!
  return time.time()


def Heartbeat(last_heartbeat_time=None):
  """Logs a system up heartbeat."""
  if SIMULATION:
    return last_heartbeat_time
  now = time.time()
  if not last_heartbeat_time or now - last_heartbeat_time > HEARTBEAT_SECONDS:
    UpdateDashboard(False)
    last_heartbeat_time = now
  return last_heartbeat_time


def VersionControl():
  """Copies the current instances of messageboard.py and arduino.py into a repository.

  To aid debugging, we want to keep past versions of the code easily accessible, and
  linked to the errors that have been logged. This function copies the python code
  into a version control directory after adding in a date / time stamp to the file name.
  """
  def MakeCopy(python_prefix):
    file_extension = '.py'

    live_name = python_prefix + '.py'
    live_path = os.path.join(CODE_REPOSITORY, live_name)

    epoch = os.path.getmtime(live_path)
    last_modified_suffix = EpochDisplayTime(epoch, format_string='-%Y-%m-%d-%H%M')
    version_name = python_prefix + last_modified_suffix + file_extension




                            <----SKIPPED LINES---->





01234567890123456789012345678901234567890123456789012345678901234567890123456789









6263646566676869707172737475767778798081828384858687888990919293949596979899100101102








44454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503








473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779








58335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873











                            <----SKIPPED LINES---->




METERS_PER_SECOND_IN_KNOTS = 0.514444

MIN_METERS = 5000/FEET_IN_METER # only planes within this distance will be detailed
# planes not seen within MIN_METERS in PERSISTENCE_SECONDS seconds will be dropped from
# the nearby list
PERSISTENCE_SECONDS = 300
TRUNCATE = 50  # max number of keys to include in a histogram image file
# number of seconds to pause between each radio poll / command processing loop
LOOP_DELAY_SECONDS = 1

# number of seconds to wait between recording heartbeats to the status file
HEARTBEAT_SECONDS = 10

# version control directory
CODE_REPOSITORY = ''
VERSION_REPOSITORY = 'versions/'
VERSION_WEBSITE_PATH = VERSION_REPOSITORY
VERSION_MESSAGEBOARD = None
VERSION_ARDUINO = None

MAX_INSIGHT_HORIZON_DAYS = 31  # histogram logic truncates to exactly 30 days of hours

# This file is where the radio drops its json file
DUMP_JSON_FILE = '/run/readsb/aircraft.json'

# 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_'





                            <----SKIPPED LINES---->





    filename = (
        WEBSERVER_IMAGE_RELATIVE_FOLDER +  # i.e.: images/
        filename_prefix +                  # i.e.: histogram_
        histogram['generate'] +            # i.e.: destination
        '.' + filename_suffix)             # i.e.: .png
    filepath = WEBSERVER_PATH + filename   # i.e.: /var/www/html/ + 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,
    heartbeat=True):
  """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
      can be either the special string 'all', or a string linked to a specific
      histogram.
    how_much_history: string parameter taking a value among ['today', '24h', '7d', '30d].
    max_screens: string parameter taking a value among ['_1', '_2', '_5', or 'all'].
    data_summary: parameter that evaluates to a boolean indicating whether the data
      summary screen in the histogram should be displayed.
    heartbeat: boolean indicating whether we should log heartbeats between each histogram
      to make sure monitoring does not mistake this slow procedure for being hung; this
      should be set to false if this is called from outside of messageboard.main.

  Returns:
    Returns a list of printable strings (with embedded new line characters) representing
    the histogram, for each screen in the histogram.
  """
  messages = []

  hours = HistogramSettingsHours(how_much_history)
  screen_limit = HistogramSettingsScreens(max_screens)

  histograms_to_generate = []
  if which_histograms in ['destination', 'all']:
    histograms_to_generate.append({
        'generate': 'destination',
        'suppress_percent_sign': True,
        'columns': 3})
  if which_histograms in ['origin', 'all']:
    histograms_to_generate.append({
        'generate': 'origin',
        'suppress_percent_sign': True,




                            <----SKIPPED LINES---->





  Args:
    flights: List of flight attribute dictionaries.
    histogram_settings: Dictionary of histogram parameters.
    heartbeat: boolean indicating whether we should log heartbeats between each histogram
      to make sure monitoring does not mistake this slow procedure for being hung; this
      should be set to false if this is called from outside of messageboard.main.

  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),
        heartbeat=heartbeat)
  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,
        heartbeat=heartbeat)
    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>
      identifier = '"%s"' % identifier  # add quotations to make sure its complete




                            <----SKIPPED LINES---->




        f, PICKLE_FLIGHTS, True, timestamp=f['now'], verify=False)

  return False


def HeartbeatRestart():
  """Logs a system down / system up pair of heartbeats as system is first starting."""
  if SIMULATION:
    return 0
  UpdateDashboard(True)  # Indicates that this wasn't running a moment before, ...
  UpdateDashboard(False)  # ... and now it is running!
  return time.time()


def Heartbeat(last_heartbeat_time=None):
  """Logs a system up heartbeat."""
  if SIMULATION:
    return last_heartbeat_time
  now = time.time()
  if not last_heartbeat_time or now - last_heartbeat_time > HEARTBEAT_SECONDS:
    UpdateDashboard(False)  # Send an all-clear message
    last_heartbeat_time = now
  return last_heartbeat_time


def VersionControl():
  """Copies the current instances of messageboard.py and arduino.py into a repository.

  To aid debugging, we want to keep past versions of the code easily accessible, and
  linked to the errors that have been logged. This function copies the python code
  into a version control directory after adding in a date / time stamp to the file name.
  """
  def MakeCopy(python_prefix):
    file_extension = '.py'

    live_name = python_prefix + '.py'
    live_path = os.path.join(CODE_REPOSITORY, live_name)

    epoch = os.path.getmtime(live_path)
    last_modified_suffix = EpochDisplayTime(epoch, format_string='-%Y-%m-%d-%H%M')
    version_name = python_prefix + last_modified_suffix + file_extension




                            <----SKIPPED LINES---->