messageboard-2022-09-03-1546.py
01234567890123456789012345678901234567890123456789012345678901234567890123456789









445446447448449450451452453454455456457458459460461462463464 465466467468469470471472473474475476477478479480481482483484








11901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230








57705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798 5799 58005801580258035804580558065807  5808580958105811   581258135814581558165817 58185819582058215822582358245825582658275828582958305831583258335834583558365837








71047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147








720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241                     72427243724472457246724772487249725072517252725372547255725672577258725972607261








72667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306











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




AIRCRAFT_LENGTH['Cessna Conquest 2 (twin-turboprop)'] = 11.89
AIRCRAFT_LENGTH['Cessna Skyhawk (piston-single)'] = 8.28
AIRCRAFT_LENGTH['Cessna Skylane (piston-single)'] = 8.84
AIRCRAFT_LENGTH['CESSNA T182 Turbo Skylane (piston-single)'] = 8.84
AIRCRAFT_LENGTH['Cessna T206 Turbo Stationair (piston-single)'] = 8.61
AIRCRAFT_LENGTH['Cessna 421 (twin-piston)'] = 11.09
AIRCRAFT_LENGTH['Cirrus SR-20 (piston-single)'] = 7.92
AIRCRAFT_LENGTH['Cirrus SR-22 (piston-single)'] = 7.92
AIRCRAFT_LENGTH['Cirrus SR22 Turbo (piston-single)'] = 7.92
AIRCRAFT_LENGTH['Cirrus Vision SF50 (single-jet)'] = 9.42
AIRCRAFT_LENGTH['Daher-Socata TBM-900 (single-turboprop)'] = 10.72
AIRCRAFT_LENGTH['Dassault Falcon 50 (tri-jet)'] = 18.52
AIRCRAFT_LENGTH['Dassault Falcon 2000 (twin-jet)'] = 20.23
AIRCRAFT_LENGTH['Dassault Falcon 900 (tri-jet)'] = 20.21
AIRCRAFT_LENGTH['Embraer 170/175 (twin-jet)'] = (29.90 + 31.68) / 2
AIRCRAFT_LENGTH['EMBRAER 175 (long wing) (twin-jet)'] = 31.68
AIRCRAFT_LENGTH['Embraer ERJ-135 (twin-jet)'] = 26.33
AIRCRAFT_LENGTH['Embraer ERJ-145 (twin-jet)'] = 29.87
AIRCRAFT_LENGTH['Embraer ERJ 175 (twin-jet)'] = 31.68
AIRCRAFT_LENGTH['Embraer ERJ 190 (twin-jet)'] = 36.25

AIRCRAFT_LENGTH['Embraer Legacy 450 (twin-jet)'] = 19.69
AIRCRAFT_LENGTH['Embraer Legacy 550 (twin-jet)'] = 20.74
AIRCRAFT_LENGTH['Embraer Legacy 600/650 (twin-jet)'] = 26.33
AIRCRAFT_LENGTH['Embraer Phenom 300 (twin-jet)'] = 15.9
AIRCRAFT_LENGTH['Eurocopter EC-635 (twin-turboshaft)'] = 10.21
AIRCRAFT_LENGTH['Fairchild Dornier 328JET (twin-jet)'] = 21.11
AIRCRAFT_LENGTH['Gulfstream Aerospace Gulfstream 3 (twin-jet)'] = 25.32
AIRCRAFT_LENGTH['Gulfstream Aerospace Gulfstream G450 (twin-jet)'] = 27.23
AIRCRAFT_LENGTH['Gulfstream Aerospace Gulfstream G550 (twin-jet)'] = 29.39
AIRCRAFT_LENGTH['Gulfstream Aerospace Gulfstream G650 (twin-jet)'] = 30.41
AIRCRAFT_LENGTH['Gulfstream Aerospace Gulfstream IV (twin-jet)'] = 26.92
AIRCRAFT_LENGTH['Gulfstream Aerospace Gulfstream V (twin-jet)'] = 29.4
AIRCRAFT_LENGTH['Hawker Beechcraft 4000 (twin-jet)'] = 21.08
AIRCRAFT_LENGTH['Honda HondaJet (twin-jet)'] = 12.99
AIRCRAFT_LENGTH['IAI Gulfstream G100 (twin-jet)'] = 16.94
AIRCRAFT_LENGTH['IAI Gulfstream G150 (twin-jet)'] = 16.94
AIRCRAFT_LENGTH['IAI Gulfstream G200 (twin-jet)'] = 18.97
AIRCRAFT_LENGTH['IAI Gulfstream G280 (twin-jet)'] = 20.3
AIRCRAFT_LENGTH['Learjet 31 (twin-jet)'] = 14.83
AIRCRAFT_LENGTH['Learjet 35 (twin-jet)'] = 14.83




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




  Args:
    filename: string of the filename to open, potentially also including the
      full path.
    log_exception: boolean indicating whether to log an exception if file not
      found.

  Returns:
    Return text string of file contents.
  """
  try:
    with open(filename, 'r') as content_file:
      file_contents = content_file.read()
  except IOError:
    if log_exception:
      Log('Unable to read '+filename)
    return ''
  return file_contents

# because reading is ~25x more expensive than getmtime, we will only read &
# parse if the getmtime is more recent than last call for this file. So this
# dict stores the a tuple, the last time read & the resulting parsed return
# value
CACHED_FILES = {}
def ReadAndParseSettings(filename):
  """Reads filename and parses the resulting key-value pairs into a dict."""
  global CACHED_FILES
  (last_read_time, settings) = CACHED_FILES.get(filename, (0, {}))
  if os.path.exists(filename):
    last_modified = os.path.getmtime(filename)
    if last_modified > last_read_time:
      setting_str = ReadFile(filename)
      settings = ParseSettings(setting_str)
      CACHED_FILES[filename] = (last_modified, settings)
    return settings

  # File does not - or at least no longer - exists; so remove the cache
  if filename in CACHED_FILES:
    CACHED_FILES.pop(filename)

  return {}





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




  whether the current active json changed from the prior one.

  Returns:
    Boolean - True if different (and processing needed), False if identical
  """
  if SIMULATION_COUNTER == 0:
    return True
  (this_json, unused_now) = DUMP_JSONS[SIMULATION_COUNTER]
  (last_json, unused_now) = DUMP_JSONS[SIMULATION_COUNTER - 1]
  return this_json != last_json


def CheckRebootNeeded(
    startup_time, message_queue, json_desc_dict, configuration):
  """Reboot based on duration instance has been running.

  Reboot needed in one of the following situations:
  - All quiet: if running for over 24 hours and all is quiet (message queue
    empty, no planes in radio, and backup not currently in process).
  - Mostly quiet: if running for over 36 hours and message queue is empty and
    it's 3a.
  - Reboot requested via html form.

  Also checks if reset requested via html form.
  """
  reboot = False
  global SHUTDOWN_SIGNAL

  running_hours = (time.time() - startup_time) / SECONDS_IN_HOUR



  if (
      running_hours >= HOURS_IN_DAY and
      not message_queue and
      not json_desc_dict.get('radio_range_flights') and
      # script /home/pi/splitflap/backup.sh creates temp file in this
      # directory; after it is copied to the NAS, it is deleted
      not os.listdir('/media/backup')):
    msg = 'All quiet reboot needed after running for %.2f hours' % running_hours


    SHUTDOWN_SIGNAL = msg
    Log(msg)
    reboot = True




  if (
      running_hours > HOURS_IN_DAY * 1.5 and
      not message_queue and
      int(EpochDisplayTime(time.time(), '%-H')) >= 3):
    msg = ('Early morning reboot needed after running for %.2f hours'
           % running_hours)

    SHUTDOWN_SIGNAL = msg
    Log(msg)
    reboot = True

  if 'soft_reboot' in configuration:
    msg = 'Soft reboot requested via web form'
    SHUTDOWN_SIGNAL = msg
    Log(msg)
    reboot = True
    RemoveSetting(configuration, 'soft_reboot')

  if 'end_process' in configuration:
    msg = 'Process end requested via web form'
    SHUTDOWN_SIGNAL = msg
    Log(msg)
    RemoveSetting(configuration, 'end_process')

  return reboot






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




  # We repeat the loop every x seconds; this ensures that if the processing
  # time is long, we don't wait another x seconds after processing completes
  next_loop_time = time.time() + LOOP_DELAY_SECONDS

  # These files are read only if the version on disk has been modified more
  # recently than the last time it was read
  last_dump_json_timestamp = 0

  init_timing.append((time.time(), 7))
  WaitUntilKillComplete(already_running_ids)
  init_timing.append((time.time(), 8))

  personal_message = None  # Unknown what personal message is displayed

  temp_last_logged = 0  # Keeps track of when temperature was last logged

  LogTimes(init_timing)
  reboot = False

  iteration = 0  # counter that tracks how many times thru while loop
  start_time = time.time()
  if initial_memory_dump:
    DumpMemorySnapsnot(
        configuration, iteration, start_time, initial_frame_count)

  Log('Finishing initialization of %d; starting radio polling loop' %
      os.getpid())
  while ((not SIMULATION or SIMULATION_COUNTER < len(DUMP_JSONS))
         and not SHUTDOWN_SIGNAL):

    last_heartbeat_time = Heartbeat(last_heartbeat_time)

    new_configuration = ReadAndParseSettings(CONFIG_FILE)
    UpdateRollingLogSize(new_configuration)
    CheckForNewFilterCriteria(
        configuration, new_configuration, message_queue, flights)
    configuration = new_configuration

    ResetLogs(configuration)  # clear the logs if requested
    UpdateRollingLogSize(configuration)

    # if this is a SIMULATION, then process every diff dump. But if it
    # isn't a simulation, then only read & do related processing for the
    # next dump if the last-modified timestamp indicates the file has been




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




          insight_messages = CreateFlightInsights(
              flights,
              configuration.get('insights'),
              insight_message_distribution)
          if configuration.get('next_flight', 'off') == 'on':
            next_flight_text = FlightInsightNextFlight(flights, configuration)
            if next_flight_text:
              insight_messages.insert(0, next_flight_text)

          insight_messages = [(FLAG_MSG_INSIGHT, m) for m in insight_messages]

          for insight_message in insight_messages:
            message_queue.insert(0, insight_message)

        else:  # flight didn't meet display criteria
          flight['insight_types'] = []

        PickleObjectToFile(
            flight, PICKLE_FLIGHTS, True, timestamp=flight['now'])

        # now that we've saved the flight, we have no more need to keep the
        # memory-hogging persistent_path key of that flight in live memory
        if 'persistent_path' in flights[-1]:
          del flights[-1]['persistent_path']

      else:
        remote, servo = RefreshArduinos(
            remote, servo,
            to_remote_q, to_servo_q, to_main_q, shutdown,
            flights, json_desc_dict, configuration, screen_history)

    message_queue, next_message_time = ProcessArduinoCommmands(
        to_main_q, flights, configuration, message_queue, next_message_time)

    personal_message = PersonalMessage(
        configuration, message_queue, personal_message)






















    if SIMULATION:
      if now:
        simulated_hour = EpochDisplayTime(now, '%Y-%m-%d %H:00%z')
      if simulated_hour != prev_simulated_hour:
        print(simulated_hour)
        prev_simulated_hour = simulated_hour

    histogram = ReadAndParseSettings(HISTOGRAM_CONFIG_FILE)
    RemoveFile(HISTOGRAM_CONFIG_FILE)

    # We also need to make sure there are flights on which to generate a
    # histogram! Why might there not be any flights? Primarily during a
    # simulation, if there's a lingering histogram file at the time of
    # history restart.
    if histogram and not flights:
      Log('Histogram requested (%s) but no flights in memory' % histogram)
    if histogram and flights:
      message_queue.extend(TriggerHistograms(flights, histogram))
      if message_queue:  # Any personal message displayed has been cleared




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




        message_queue, next_message_time, configuration, screen_history)

    reboot = CheckRebootNeeded(
        startup_time, message_queue, json_desc_dict, configuration)

    temp_last_logged = CheckTemperature(configuration, temp_last_logged)

    if not SIMULATION:
      time.sleep(max(0, next_loop_time - time.time()))
      next_loop_time = time.time() + LOOP_DELAY_SECONDS
    else:
      SIMULATION_COUNTER += 1
      if simulation_slowdown:
        SimulationSlowdownNearFlight(flights, persistent_nearby_aircraft)

    # now that we've completed the loop, lets potentially dump the
    # memory snapshot
    iteration += 1  # this completes the iteration-th time thru the loop
    if initial_memory_dump:
      DumpMemorySnapsnot(
          configuration, iteration, start_time, initial_frame_count)

  if SIMULATION:
    SimulationEnd(message_queue, flights, screen_history)
  PerformGracefulShutdown(shutdown, reboot)


if __name__ == "__main__":
  #interrupt, as in ctrl-c
  signal.signal(signal.SIGINT, InterruptShutdownFromSignal)

  #terminate, when another instance found or via kill
  signal.signal(signal.SIGTERM, InterruptShutdownFromSignal)

  if '-i' in sys.argv:
    BootstrapInsightList()
  else:
    main_settings = ReadAndParseSettings(CONFIG_FILE)
    if 'code_profiling_enabled' in main_settings:
      import cProfile
      cProfile.run(




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





01234567890123456789012345678901234567890123456789012345678901234567890123456789









445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485








11911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231








5771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846








71137114711571167117711871197120712171227123712471257126712771287129713071317132 71337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155








72147215721672177218721972207221722272237224722572267227722872297230723172327233     7234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285








72907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330











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




AIRCRAFT_LENGTH['Cessna Conquest 2 (twin-turboprop)'] = 11.89
AIRCRAFT_LENGTH['Cessna Skyhawk (piston-single)'] = 8.28
AIRCRAFT_LENGTH['Cessna Skylane (piston-single)'] = 8.84
AIRCRAFT_LENGTH['CESSNA T182 Turbo Skylane (piston-single)'] = 8.84
AIRCRAFT_LENGTH['Cessna T206 Turbo Stationair (piston-single)'] = 8.61
AIRCRAFT_LENGTH['Cessna 421 (twin-piston)'] = 11.09
AIRCRAFT_LENGTH['Cirrus SR-20 (piston-single)'] = 7.92
AIRCRAFT_LENGTH['Cirrus SR-22 (piston-single)'] = 7.92
AIRCRAFT_LENGTH['Cirrus SR22 Turbo (piston-single)'] = 7.92
AIRCRAFT_LENGTH['Cirrus Vision SF50 (single-jet)'] = 9.42
AIRCRAFT_LENGTH['Daher-Socata TBM-900 (single-turboprop)'] = 10.72
AIRCRAFT_LENGTH['Dassault Falcon 50 (tri-jet)'] = 18.52
AIRCRAFT_LENGTH['Dassault Falcon 2000 (twin-jet)'] = 20.23
AIRCRAFT_LENGTH['Dassault Falcon 900 (tri-jet)'] = 20.21
AIRCRAFT_LENGTH['Embraer 170/175 (twin-jet)'] = (29.90 + 31.68) / 2
AIRCRAFT_LENGTH['EMBRAER 175 (long wing) (twin-jet)'] = 31.68
AIRCRAFT_LENGTH['Embraer ERJ-135 (twin-jet)'] = 26.33
AIRCRAFT_LENGTH['Embraer ERJ-145 (twin-jet)'] = 29.87
AIRCRAFT_LENGTH['Embraer ERJ 175 (twin-jet)'] = 31.68
AIRCRAFT_LENGTH['Embraer ERJ 190 (twin-jet)'] = 36.25
AIRCRAFT_LENGTH['Embraer ERJ-190 (twin-jet)'] = 36.25
AIRCRAFT_LENGTH['Embraer Legacy 450 (twin-jet)'] = 19.69
AIRCRAFT_LENGTH['Embraer Legacy 550 (twin-jet)'] = 20.74
AIRCRAFT_LENGTH['Embraer Legacy 600/650 (twin-jet)'] = 26.33
AIRCRAFT_LENGTH['Embraer Phenom 300 (twin-jet)'] = 15.9
AIRCRAFT_LENGTH['Eurocopter EC-635 (twin-turboshaft)'] = 10.21
AIRCRAFT_LENGTH['Fairchild Dornier 328JET (twin-jet)'] = 21.11
AIRCRAFT_LENGTH['Gulfstream Aerospace Gulfstream 3 (twin-jet)'] = 25.32
AIRCRAFT_LENGTH['Gulfstream Aerospace Gulfstream G450 (twin-jet)'] = 27.23
AIRCRAFT_LENGTH['Gulfstream Aerospace Gulfstream G550 (twin-jet)'] = 29.39
AIRCRAFT_LENGTH['Gulfstream Aerospace Gulfstream G650 (twin-jet)'] = 30.41
AIRCRAFT_LENGTH['Gulfstream Aerospace Gulfstream IV (twin-jet)'] = 26.92
AIRCRAFT_LENGTH['Gulfstream Aerospace Gulfstream V (twin-jet)'] = 29.4
AIRCRAFT_LENGTH['Hawker Beechcraft 4000 (twin-jet)'] = 21.08
AIRCRAFT_LENGTH['Honda HondaJet (twin-jet)'] = 12.99
AIRCRAFT_LENGTH['IAI Gulfstream G100 (twin-jet)'] = 16.94
AIRCRAFT_LENGTH['IAI Gulfstream G150 (twin-jet)'] = 16.94
AIRCRAFT_LENGTH['IAI Gulfstream G200 (twin-jet)'] = 18.97
AIRCRAFT_LENGTH['IAI Gulfstream G280 (twin-jet)'] = 20.3
AIRCRAFT_LENGTH['Learjet 31 (twin-jet)'] = 14.83
AIRCRAFT_LENGTH['Learjet 35 (twin-jet)'] = 14.83




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




  Args:
    filename: string of the filename to open, potentially also including the
      full path.
    log_exception: boolean indicating whether to log an exception if file not
      found.

  Returns:
    Return text string of file contents.
  """
  try:
    with open(filename, 'r') as content_file:
      file_contents = content_file.read()
  except IOError:
    if log_exception:
      Log('Unable to read '+filename)
    return ''
  return file_contents

# because reading is ~25x more expensive than getmtime, we will only read &
# parse if the getmtime is more recent than last call for this file. So this
# dict stores a 2-tuple, the last time read & the resulting parsed return
# value
CACHED_FILES = {}
def ReadAndParseSettings(filename):
  """Reads filename and parses the resulting key-value pairs into a dict."""
  global CACHED_FILES
  (last_read_time, settings) = CACHED_FILES.get(filename, (0, {}))
  if os.path.exists(filename):
    last_modified = os.path.getmtime(filename)
    if last_modified > last_read_time:
      setting_str = ReadFile(filename)
      settings = ParseSettings(setting_str)
      CACHED_FILES[filename] = (last_modified, settings)
    return settings

  # File does not - or at least no longer - exists; so remove the cache
  if filename in CACHED_FILES:
    CACHED_FILES.pop(filename)

  return {}





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




  whether the current active json changed from the prior one.

  Returns:
    Boolean - True if different (and processing needed), False if identical
  """
  if SIMULATION_COUNTER == 0:
    return True
  (this_json, unused_now) = DUMP_JSONS[SIMULATION_COUNTER]
  (last_json, unused_now) = DUMP_JSONS[SIMULATION_COUNTER - 1]
  return this_json != last_json


def CheckRebootNeeded(
    startup_time, message_queue, json_desc_dict, configuration):
  """Reboot based on duration instance has been running.

  Reboot needed in one of the following situations:
  - All quiet: if running for over 24 hours and all is quiet (message queue
    empty, no planes in radio, and backup not currently in process).
  - Mostly quiet: if running for over 36 hours and message queue is empty and
    it's 4a.
  - Reboot requested via html form.

  Also checks if reset requested via html form.
  """
  reboot = False
  global SHUTDOWN_SIGNAL

  running_hours = (time.time() - startup_time) / SECONDS_IN_HOUR
  restart_days = configuration.get('restart_days', 1)

  min_hours = restart_days * HOURS_IN_DAY
  if (
      running_hours >= min_hours and
      not message_queue and
      not json_desc_dict.get('radio_range_flights') and
      # script /home/pi/splitflap/backup.sh creates temp file in this
      # directory; after it is copied to the NAS, it is deleted
      not os.listdir('/media/backup')):
    msg = ('All quiet reboot triggered based on %d days (%d hours); '
           'actual runtime: %.2f hours' %
           (restart_days, min_hours, running_hours))
    SHUTDOWN_SIGNAL = msg
    Log(msg)
    reboot = True

  # Wait another half day
  restart_days += 0.5
  min_hours = restart_days * HOURS_IN_DAY
  if (
      running_hours > min_hours and
      not message_queue and
      int(EpochDisplayTime(time.time(), '%-H')) >= 4):
    msg = ('Early morning reboot triggered based on %.1f (%d hours); '
           'actual runtime: %.2f hours' %
           (restart_days, min_hours, running_hours))
    SHUTDOWN_SIGNAL = msg
    Log(msg)
    reboot = True

  if 'soft_reboot' in configuration:
    msg = 'Soft reboot requested via web form'
    SHUTDOWN_SIGNAL = msg
    Log(msg)
    reboot = True
    RemoveSetting(configuration, 'soft_reboot')

  if 'end_process' in configuration:
    msg = 'Process end requested via web form'
    SHUTDOWN_SIGNAL = msg
    Log(msg)
    RemoveSetting(configuration, 'end_process')

  return reboot






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




  # We repeat the loop every x seconds; this ensures that if the processing
  # time is long, we don't wait another x seconds after processing completes
  next_loop_time = time.time() + LOOP_DELAY_SECONDS

  # These files are read only if the version on disk has been modified more
  # recently than the last time it was read
  last_dump_json_timestamp = 0

  init_timing.append((time.time(), 7))
  WaitUntilKillComplete(already_running_ids)
  init_timing.append((time.time(), 8))

  personal_message = None  # Unknown what personal message is displayed

  temp_last_logged = 0  # Keeps track of when temperature was last logged

  LogTimes(init_timing)
  reboot = False

  iteration = 0  # counter that tracks how many times thru while loop

  if initial_memory_dump:
    DumpMemorySnapsnot(
        configuration, iteration, startup_time, initial_frame_count)

  Log('Finishing initialization of %d; starting radio polling loop' %
      os.getpid())
  while ((not SIMULATION or SIMULATION_COUNTER < len(DUMP_JSONS))
         and not SHUTDOWN_SIGNAL):

    last_heartbeat_time = Heartbeat(last_heartbeat_time)

    new_configuration = ReadAndParseSettings(CONFIG_FILE)
    UpdateRollingLogSize(new_configuration)
    CheckForNewFilterCriteria(
        configuration, new_configuration, message_queue, flights)
    configuration = new_configuration

    ResetLogs(configuration)  # clear the logs if requested
    UpdateRollingLogSize(configuration)

    # if this is a SIMULATION, then process every diff dump. But if it
    # isn't a simulation, then only read & do related processing for the
    # next dump if the last-modified timestamp indicates the file has been




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




          insight_messages = CreateFlightInsights(
              flights,
              configuration.get('insights'),
              insight_message_distribution)
          if configuration.get('next_flight', 'off') == 'on':
            next_flight_text = FlightInsightNextFlight(flights, configuration)
            if next_flight_text:
              insight_messages.insert(0, next_flight_text)

          insight_messages = [(FLAG_MSG_INSIGHT, m) for m in insight_messages]

          for insight_message in insight_messages:
            message_queue.insert(0, insight_message)

        else:  # flight didn't meet display criteria
          flight['insight_types'] = []

        PickleObjectToFile(
            flight, PICKLE_FLIGHTS, True, timestamp=flight['now'])






      else:
        remote, servo = RefreshArduinos(
            remote, servo,
            to_remote_q, to_servo_q, to_main_q, shutdown,
            flights, json_desc_dict, configuration, screen_history)

    message_queue, next_message_time = ProcessArduinoCommmands(
        to_main_q, flights, configuration, message_queue, next_message_time)

    personal_message = PersonalMessage(
        configuration, message_queue, personal_message)

    # MEMORY MANAGEMENT
    # now that we've saved the flight, we have no more need to keep the
    # memory-hogging persistent_path key of that flight in live memory
    if 'persistent_path' in flights[-1]:
      del flights[-1]['persistent_path']
    # it turns out we only need the last screen in the screen history, not
    # the entire history, so we can purge all the rest from active memory
    # to eliminate a slow-growing memory hog
    screen_history = [screen_history[-1]]
    # we also do not need more than MAX_INSIGHT_HORIZON_DAYS of history
    # of the flights, so we can purge flights older than that as the flights
    # list slowly grows over time.  we can search the entire list every time
    # but that's unnecessary; instead we can just pop off the 0-th element
    # each time through the list if it's more than 1 + max days in the past.
    # Why + 1?  Because the histogram generation expects that many *full* days
    # of history, so to make sure we don't have a partial day if we were to
    # generate the monthly histograms mid-day, we will keep an extra day.
    if time.time() - flights[0]['now'] > SECONDS_IN_DAY * (
        MAX_INSIGHT_HORIZON_DAYS + 1):
      flights.pop(0)

    if SIMULATION:
      if now:
        simulated_hour = EpochDisplayTime(now, '%Y-%m-%d %H:00%z')
      if simulated_hour != prev_simulated_hour:
        print(simulated_hour)
        prev_simulated_hour = simulated_hour

    histogram = ReadAndParseSettings(HISTOGRAM_CONFIG_FILE)
    RemoveFile(HISTOGRAM_CONFIG_FILE)

    # We also need to make sure there are flights on which to generate a
    # histogram! Why might there not be any flights? Primarily during a
    # simulation, if there's a lingering histogram file at the time of
    # history restart.
    if histogram and not flights:
      Log('Histogram requested (%s) but no flights in memory' % histogram)
    if histogram and flights:
      message_queue.extend(TriggerHistograms(flights, histogram))
      if message_queue:  # Any personal message displayed has been cleared




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




        message_queue, next_message_time, configuration, screen_history)

    reboot = CheckRebootNeeded(
        startup_time, message_queue, json_desc_dict, configuration)

    temp_last_logged = CheckTemperature(configuration, temp_last_logged)

    if not SIMULATION:
      time.sleep(max(0, next_loop_time - time.time()))
      next_loop_time = time.time() + LOOP_DELAY_SECONDS
    else:
      SIMULATION_COUNTER += 1
      if simulation_slowdown:
        SimulationSlowdownNearFlight(flights, persistent_nearby_aircraft)

    # now that we've completed the loop, lets potentially dump the
    # memory snapshot
    iteration += 1  # this completes the iteration-th time thru the loop
    if initial_memory_dump:
      DumpMemorySnapsnot(
          configuration, iteration, startup_time, initial_frame_count)

  if SIMULATION:
    SimulationEnd(message_queue, flights, screen_history)
  PerformGracefulShutdown(shutdown, reboot)


if __name__ == "__main__":
  #interrupt, as in ctrl-c
  signal.signal(signal.SIGINT, InterruptShutdownFromSignal)

  #terminate, when another instance found or via kill
  signal.signal(signal.SIGTERM, InterruptShutdownFromSignal)

  if '-i' in sys.argv:
    BootstrapInsightList()
  else:
    main_settings = ReadAndParseSettings(CONFIG_FILE)
    if 'code_profiling_enabled' in main_settings:
      import cProfile
      cProfile.run(




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