01234567890123456789012345678901234567890123456789012345678901234567890123456789
144145146147148149150151152153154155156157158159160161162163164165 166167168169170171172173174175176177178179180181182183184185 898899900901902903904905906907908909910911912913914915916917 918919920921922923924925926927928929930931932933934935936937 202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070 26022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642 51525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192 52425243524452455246524752485249525052515252525352545255525652575258525952605261 52625263526452655266526752685269527052715272527352745275527652775278527952805281 57265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784 57855786578757885789579057915792579357945795579657975798579958005801580258035804 |
<----SKIPPED LINES---->
# of flights, by hour throughout the day, over the last seven days, normalized to
# flights per day. This allows those parameters to be fine-tuned in a useful way.
# This file is the location, on the webserver, of that image, which needs to be in
# alignment with the html page that displays it.
HOURLY_IMAGE_FILE = 'hours.png'
# This is all messages that have been sent to the board since the last time the
# file was manually cleared. Newest messages are at the bottom. It is visible at the
# webserver.
ALL_MESSAGE_FILE = 'all_messages.txt' #enumeration of all messages sent to board
# This shows the most recent n messages sent to the board. Newest messages are at the
# top for easier viewing of "what did I miss".
ROLLING_MESSAGE_FILE = 'rolling_messages.txt'
STDERR_FILE = 'stderr.txt'
BACKUP_FILE = 'backup.txt'
SERVICE_VERIFICATION_FILE = 'service-verification.txt'
UPTIMES_FILE = 'uptimes.html'
FLAG_MSG_FLIGHT = 1 # basic flight details
FLAG_MSG_INTERESTING = 2 # random tidbit about a flight
FLAG_MSG_HISTOGRAM = 3 # histogram message
FLAG_INSIGHT_LAST_SEEN = 0
FLAG_INSIGHT_DIFF_AIRCRAFT = 1
FLAG_INSIGHT_NTH_FLIGHT = 2
FLAG_INSIGHT_GROUNDSPEED = 3
FLAG_INSIGHT_ALTITUDE = 4
FLAG_INSIGHT_VERTRATE = 5
FLAG_INSIGHT_FIRST_DEST = 6
FLAG_INSIGHT_FIRST_ORIGIN = 7
FLAG_INSIGHT_FIRST_AIRLINE = 8
FLAG_INSIGHT_FIRST_AIRCRAFT = 9
FLAG_INSIGHT_LONGEST_DELAY = 10
FLAG_INSIGHT_FLIGHT_DELAY_FREQUENCY = 11
FLAG_INSIGHT_FLIGHT_DELAY_TIME = 12
FLAG_INSIGHT_AIRLINE_DELAY_FREQUENCY = 13
FLAG_INSIGHT_AIRLINE_DELAY_TIME = 14
FLAG_INSIGHT_DESTINATION_DELAY_FREQUENCY = 15
FLAG_INSIGHT_DESTINATION_DELAY_TIME = 16
FLAG_INSIGHT_HOUR_DELAY_FREQUENCY = 17
FLAG_INSIGHT_HOUR_DELAY_TIME = 18
<----SKIPPED LINES---->
def HourString(flight):
"""Formats now on flight into a a 3-digit string like '12a' or ' 1p'."""
time_string = DisplayTime(flight)
if time_string:
hour_string = time_string[11:13]
hour_0_23 = int(hour_string)
is_pm = int(hour_0_23/12) == 1
hour_number = hour_0_23 % 12
if hour_number == 0:
hour_number = 12
out_string = str(hour_number).rjust(2)
if is_pm:
out_string += 'p'
else:
out_string += 'a'
else:
out_string = KEY_NOT_PRESENT_STRING
return out_string
def HoursSinceMidnight(timezone=TIMEZONE):
"""Returns the float number of hours elapsed since midnight in the given timezone."""
tz = pytz.timezone(timezone)
now = datetime.datetime.now(tz)
seconds_since_midnight = (
now - now.replace(hour=0, minute=0, second=0, microsecond=0)).total_seconds()
hours = seconds_since_midnight / SECONDS_IN_HOUR
return hours
def HoursSinceFlight(now, then):
"""Returns the number of hours between a timestamp and a flight.
Args:
now: timezone-aware datetime representation of timestamp
then: epoch (float)
Returns:
Number of hours between now and then (i.e.: now - then; a positive return value
means now occurred after then).
<----SKIPPED LINES---->
if flight_altitude > config_max_altitude:
flight_meets_criteria = False
if log:
Log(
'%s not displayed because it fails altitude criteria - flight altitude: '
'%.0f; required altitude: %.0f' % (
DisplayFlightNumber(flight), flight_altitude, config_max_altitude))
else:
flight_distance = flight.get('min_feet', float('inf'))
config_max_distance = configuration['setting_max_distance']
if flight_distance > config_max_distance:
flight_meets_criteria = False
if log:
Log(
'%s not displayed because it fails distance criteria - flight distance: '
'%.0f; required distance: %.0f' % (
DisplayFlightNumber(flight), flight_distance, config_max_distance))
if not display_all_hours and flight_meets_criteria:
flight_timestamp = flight['now']
dt = datetime.datetime.fromtimestamp(flight_timestamp, TZ)
minute_of_day = dt.hour * MINUTES_IN_HOUR + dt.minute
if minute_of_day <= configuration['setting_on_time']:
flight_meets_criteria = False
if log:
Log(
'%s not displayed because it occurs too early - minute_of_day: '
'%d; setting_on_time: %d' % (
DisplayFlightNumber(flight), minute_of_day,
configuration['setting_on_time']))
elif minute_of_day > configuration['setting_off_time'] + 1:
flight_meets_criteria = False
if log:
Log(
'%s not displayed because it occurs too late - minute_of_day: '
'%d; setting_off_time: %d' % (
DisplayFlightNumber(flight), minute_of_day,
configuration['setting_off_time']))
elif configuration.get('setting_screen_enabled', 'off') == 'off':
flight_meets_criteria = False
if log:
Log(
<----SKIPPED LINES---->
def CheckForNewFilterCriteria(prev, new, message_queue, flights):
"""If filter criteria changed, generate new image and perhaps new message."""
if (new.get('setting_max_distance') != prev.get('setting_max_distance') or
new.get('setting_max_altitude') != prev.get('setting_max_altitude')):
FlightCriteriaHistogramPng(
flights,
new['setting_max_distance'],
new['setting_max_altitude'],
7,
last_max_distance_feet=prev.get('setting_max_distance'),
last_max_altitude_feet=prev.get('setting_max_altitude'))
if (new.get('setting_max_distance') != prev.get('setting_max_distance') or
new.get('setting_max_altitude') != prev.get('setting_max_altitude') or
new.get('setting_off_time') != prev.get('setting_off_time') or
new.get('setting_on_time') != prev.get('setting_on_time')):
if new.get('next_flight', 'off') == 'on':
next_flight_message = FlightInsightNextFlight(flights, new)
if next_flight_message:
message_queue.append((FLAG_MSG_INTERESTING, next_flight_message))
def PercentileScore(scores, value):
"""Returns the percentile that a particular value is in a list of numbers.
Roughly inverts numpy.percentile. That is, numpy.percentile(scores_list, percentile)
to get the value of the list that is at that percentile;
PercentileScore(scores_list, value) will yield back approximately that percentile.
If the value matches identical elements in the list, this function takes the average
position of those identical values to compute a percentile. Thus, for some lists
(i.e.: where there are lots of flights that have a 0 second delay, or a 100% delay
frequency), you may not get a percentile of 0 or 100 even with values equal to the
min or max element in the list.
Args:
scores: the list of numbers, including value.
value: the value for which we want to determine the percentile.
Returns:
<----SKIPPED LINES---->
Returns:
A 2-tuple of the (possibly-updated) message_queue and next_message_time.
"""
while not q.empty():
command, args = q.get()
if command == 'pin':
UpdateStatusLight(*args)
elif command == 'replay':
# a command might request info about flight to be (re)displayed, irrespective of
# whether the screen is on; if so, let's put that message at the front of the message
# queue, and delete any subsequent messages in queue because presumably the button
# was pushed either a) when the screen was off (so no messages in queue), or b)
# because the screen was on, but the last flight details got lost after other screens
# that we're no longer interested in
messageboard_flight_index = IdentifyFlightDisplayed(
flights, configuration, display_all_hours=True)
if messageboard_flight_index is not None:
message_queue = [m for m in message_queue if m[0] != FLAG_MSG_INTERESTING]
flight_message = CreateMessageAboutFlight(flights[messageboard_flight_index])
message_queue = [(FLAG_MSG_FLIGHT, flight_message)]
next_message_time = time.time()
elif command == 'histogram':
if not flights:
Log('Histogram requested by remote %s but no flights in memory' % str(args))
else:
histogram_type, histogram_history = args
message_queue.extend(MessageboardHistograms(
flights,
histogram_type,
histogram_history,
'_1',
False))
elif command == 'update_configuration':
WriteFile(CONFIG_FILE, *args)
else:
<----SKIPPED LINES---->
curl.setopt(pycurl.READDATA, body_as_file_object)
curl.setopt(pycurl.POSTFIELDSIZE, len(body_as_json_string))
failure_message = ''
try:
curl.perform()
except pycurl.error as e:
failure_message = 'curl.perform() failed with message %s' % e
Log('curl.perform() failed with message %s' % e)
error_code = True
else:
# you may want to check HTTP response code, e.g.
status_code = curl.getinfo(pycurl.RESPONSE_CODE)
if status_code != 200:
Log('Server returned HTTP status code %d for message %s' % (status_code, s))
error_code = True
curl.close()
UpdateStatusLight(GPIO_ERROR_VESTABOARD_CONNECTION, error_code, failure_message)
def ManageMessageQueue(message_queue, next_message_time, configuration):
"""Check time & if appropriate, display next message from queue.
Args:
message_queue: FIFO list of message tuples of (message type, message string).
next_message_time: epoch at which next message should be displayed
configuration: dictionary of configuration attributes.
Returns:
Next_message_time, potentially updated if a message has been displayed, or unchanged
if no message was displayed.
"""
if message_queue and (time.time() >= next_message_time or SIMULATION):
if SIMULATION: # drain the queue because the messages come so fast
messages_to_display = list(message_queue)
# passed by reference, so clear it out since we drained it to the display
del message_queue[:]
else: # display only one message, being mindful of the display timing
messages_to_display = [message_queue.pop(0)]
<----SKIPPED LINES---->
# process this if its a truly new flight
new_flight_flag = ConfirmNewFlight(flight, flights)
if new_flight_flag:
flights.append(flight)
remote, servo = RefreshArduinos(
remote, servo,
to_remote_q, to_servo_q, to_main_q, shutdown,
flights, json_desc_dict, configuration)
flight_meets_display_criteria = FlightMeetsDisplayCriteria(
flight, configuration, log=True)
if flight_meets_display_criteria:
flight_message = (FLAG_MSG_FLIGHT, CreateMessageAboutFlight(flight))
# display the next message about this flight now!
next_message_time = time.time()
message_queue.insert(0, flight_message)
# and delete any queued insight messages about other flights that have
# not yet displayed, since a newer flight has taken precedence
messages_to_delete = [m for m in message_queue if m[0] == FLAG_MSG_INTERESTING]
if messages_to_delete and VERBOSE:
Log(
'Deleting messages from queue due to new-found plane: %s' %
messages_to_delete)
message_queue = [m for m in message_queue if m[0] != FLAG_MSG_INTERESTING]
# Though we also manage the message queue outside this conditional as well,
# because it can take a half second to generate the flight insights, this allows
# this message to start displaying on the board immediately, so it's up there
# when it's most relevant
next_message_time = ManageMessageQueue(
message_queue, next_message_time, configuration)
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_INTERESTING, 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)
message_queue, next_message_time = ProcessArduinoCommmands(
to_main_q, flights, configuration, message_queue, next_message_time)
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))
# check time & if appropriate, display next message from queue
<----SKIPPED LINES---->
|
01234567890123456789012345678901234567890123456789012345678901234567890123456789
144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945 203720382039204020412042204320442045204620472048204920502051205220532054205520562057 20582059206020612062206320642065206620672068206920702071207220732074207520762077 26092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649 51595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199 5249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303 574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828 |
<----SKIPPED LINES---->
# of flights, by hour throughout the day, over the last seven days, normalized to
# flights per day. This allows those parameters to be fine-tuned in a useful way.
# This file is the location, on the webserver, of that image, which needs to be in
# alignment with the html page that displays it.
HOURLY_IMAGE_FILE = 'hours.png'
# This is all messages that have been sent to the board since the last time the
# file was manually cleared. Newest messages are at the bottom. It is visible at the
# webserver.
ALL_MESSAGE_FILE = 'all_messages.txt' #enumeration of all messages sent to board
# This shows the most recent n messages sent to the board. Newest messages are at the
# top for easier viewing of "what did I miss".
ROLLING_MESSAGE_FILE = 'rolling_messages.txt'
STDERR_FILE = 'stderr.txt'
BACKUP_FILE = 'backup.txt'
SERVICE_VERIFICATION_FILE = 'service-verification.txt'
UPTIMES_FILE = 'uptimes.html'
FLAG_MSG_FLIGHT = 1 # basic flight details
FLAG_MSG_INSIGHT = 2 # random tidbit about a flight
FLAG_MSG_HISTOGRAM = 3 # histogram message
FLAG_MSG_CLEAR = 4 # a blank message to clear the screen
FLAG_MSG_PERSONAL = 5 # user-entered message to display for some duration of time
FLAG_INSIGHT_LAST_SEEN = 0
FLAG_INSIGHT_DIFF_AIRCRAFT = 1
FLAG_INSIGHT_NTH_FLIGHT = 2
FLAG_INSIGHT_GROUNDSPEED = 3
FLAG_INSIGHT_ALTITUDE = 4
FLAG_INSIGHT_VERTRATE = 5
FLAG_INSIGHT_FIRST_DEST = 6
FLAG_INSIGHT_FIRST_ORIGIN = 7
FLAG_INSIGHT_FIRST_AIRLINE = 8
FLAG_INSIGHT_FIRST_AIRCRAFT = 9
FLAG_INSIGHT_LONGEST_DELAY = 10
FLAG_INSIGHT_FLIGHT_DELAY_FREQUENCY = 11
FLAG_INSIGHT_FLIGHT_DELAY_TIME = 12
FLAG_INSIGHT_AIRLINE_DELAY_FREQUENCY = 13
FLAG_INSIGHT_AIRLINE_DELAY_TIME = 14
FLAG_INSIGHT_DESTINATION_DELAY_FREQUENCY = 15
FLAG_INSIGHT_DESTINATION_DELAY_TIME = 16
FLAG_INSIGHT_HOUR_DELAY_FREQUENCY = 17
FLAG_INSIGHT_HOUR_DELAY_TIME = 18
<----SKIPPED LINES---->
def HourString(flight):
"""Formats now on flight into a a 3-digit string like '12a' or ' 1p'."""
time_string = DisplayTime(flight)
if time_string:
hour_string = time_string[11:13]
hour_0_23 = int(hour_string)
is_pm = int(hour_0_23/12) == 1
hour_number = hour_0_23 % 12
if hour_number == 0:
hour_number = 12
out_string = str(hour_number).rjust(2)
if is_pm:
out_string += 'p'
else:
out_string += 'a'
else:
out_string = KEY_NOT_PRESENT_STRING
return out_string
def MinuteOfDay(ts=time.time()):
dt = datetime.datetime.fromtimestamp(ts, TZ)
minute_of_day = dt.hour * MINUTES_IN_HOUR + dt.minute
return minute_of_day
def HoursSinceMidnight(timezone=TIMEZONE):
"""Returns the float number of hours elapsed since midnight in the given timezone."""
tz = pytz.timezone(timezone)
now = datetime.datetime.now(tz)
seconds_since_midnight = (
now - now.replace(hour=0, minute=0, second=0, microsecond=0)).total_seconds()
hours = seconds_since_midnight / SECONDS_IN_HOUR
return hours
def HoursSinceFlight(now, then):
"""Returns the number of hours between a timestamp and a flight.
Args:
now: timezone-aware datetime representation of timestamp
then: epoch (float)
Returns:
Number of hours between now and then (i.e.: now - then; a positive return value
means now occurred after then).
<----SKIPPED LINES---->
if flight_altitude > config_max_altitude:
flight_meets_criteria = False
if log:
Log(
'%s not displayed because it fails altitude criteria - flight altitude: '
'%.0f; required altitude: %.0f' % (
DisplayFlightNumber(flight), flight_altitude, config_max_altitude))
else:
flight_distance = flight.get('min_feet', float('inf'))
config_max_distance = configuration['setting_max_distance']
if flight_distance > config_max_distance:
flight_meets_criteria = False
if log:
Log(
'%s not displayed because it fails distance criteria - flight distance: '
'%.0f; required distance: %.0f' % (
DisplayFlightNumber(flight), flight_distance, config_max_distance))
if not display_all_hours and flight_meets_criteria:
flight_timestamp = flight['now']
minute_of_day = MinuteOfDay(flight_timestamp)
if minute_of_day <= configuration['setting_on_time']:
flight_meets_criteria = False
if log:
Log(
'%s not displayed because it occurs too early - minute_of_day: '
'%d; setting_on_time: %d' % (
DisplayFlightNumber(flight), minute_of_day,
configuration['setting_on_time']))
elif minute_of_day > configuration['setting_off_time'] + 1:
flight_meets_criteria = False
if log:
Log(
'%s not displayed because it occurs too late - minute_of_day: '
'%d; setting_off_time: %d' % (
DisplayFlightNumber(flight), minute_of_day,
configuration['setting_off_time']))
elif configuration.get('setting_screen_enabled', 'off') == 'off':
flight_meets_criteria = False
if log:
Log(
<----SKIPPED LINES---->
def CheckForNewFilterCriteria(prev, new, message_queue, flights):
"""If filter criteria changed, generate new image and perhaps new message."""
if (new.get('setting_max_distance') != prev.get('setting_max_distance') or
new.get('setting_max_altitude') != prev.get('setting_max_altitude')):
FlightCriteriaHistogramPng(
flights,
new['setting_max_distance'],
new['setting_max_altitude'],
7,
last_max_distance_feet=prev.get('setting_max_distance'),
last_max_altitude_feet=prev.get('setting_max_altitude'))
if (new.get('setting_max_distance') != prev.get('setting_max_distance') or
new.get('setting_max_altitude') != prev.get('setting_max_altitude') or
new.get('setting_off_time') != prev.get('setting_off_time') or
new.get('setting_on_time') != prev.get('setting_on_time')):
if new.get('next_flight', 'off') == 'on':
next_flight_message = FlightInsightNextFlight(flights, new)
if next_flight_message:
message_queue.append((FLAG_MSG_INSIGHT, next_flight_message))
def PercentileScore(scores, value):
"""Returns the percentile that a particular value is in a list of numbers.
Roughly inverts numpy.percentile. That is, numpy.percentile(scores_list, percentile)
to get the value of the list that is at that percentile;
PercentileScore(scores_list, value) will yield back approximately that percentile.
If the value matches identical elements in the list, this function takes the average
position of those identical values to compute a percentile. Thus, for some lists
(i.e.: where there are lots of flights that have a 0 second delay, or a 100% delay
frequency), you may not get a percentile of 0 or 100 even with values equal to the
min or max element in the list.
Args:
scores: the list of numbers, including value.
value: the value for which we want to determine the percentile.
Returns:
<----SKIPPED LINES---->
Returns:
A 2-tuple of the (possibly-updated) message_queue and next_message_time.
"""
while not q.empty():
command, args = q.get()
if command == 'pin':
UpdateStatusLight(*args)
elif command == 'replay':
# a command might request info about flight to be (re)displayed, irrespective of
# whether the screen is on; if so, let's put that message at the front of the message
# queue, and delete any subsequent messages in queue because presumably the button
# was pushed either a) when the screen was off (so no messages in queue), or b)
# because the screen was on, but the last flight details got lost after other screens
# that we're no longer interested in
messageboard_flight_index = IdentifyFlightDisplayed(
flights, configuration, display_all_hours=True)
if messageboard_flight_index is not None:
message_queue = [m for m in message_queue if m[0] != FLAG_MSG_INSIGHT]
flight_message = CreateMessageAboutFlight(flights[messageboard_flight_index])
message_queue = [(FLAG_MSG_FLIGHT, flight_message)]
next_message_time = time.time()
elif command == 'histogram':
if not flights:
Log('Histogram requested by remote %s but no flights in memory' % str(args))
else:
histogram_type, histogram_history = args
message_queue.extend(MessageboardHistograms(
flights,
histogram_type,
histogram_history,
'_1',
False))
elif command == 'update_configuration':
WriteFile(CONFIG_FILE, *args)
else:
<----SKIPPED LINES---->
curl.setopt(pycurl.READDATA, body_as_file_object)
curl.setopt(pycurl.POSTFIELDSIZE, len(body_as_json_string))
failure_message = ''
try:
curl.perform()
except pycurl.error as e:
failure_message = 'curl.perform() failed with message %s' % e
Log('curl.perform() failed with message %s' % e)
error_code = True
else:
# you may want to check HTTP response code, e.g.
status_code = curl.getinfo(pycurl.RESPONSE_CODE)
if status_code != 200:
Log('Server returned HTTP status code %d for message %s' % (status_code, s))
error_code = True
curl.close()
UpdateStatusLight(GPIO_ERROR_VESTABOARD_CONNECTION, error_code, failure_message)
def PersonalMessage(configuration, message_queue):
if 'clear_board' in configuration:
RemoveSetting(configuration, 'clear_board')
message_queue.append((FLAG_MSG_CLEAR, ''))
minute_of_day = MinuteOfDay()
if (
not message_queue and
'personal_message_enabled' in configuration and
configuration['personal_message'] and
minute_of_day <= configuration['personal_off_time'] and
minute_of_day > configuration['personal_on_time'] + 1):
message = configuration['personal_message'].split('\n')
message_queue.append((FLAG_MSG_PERSONAL, message))
def ManageMessageQueue(message_queue, next_message_time, configuration):
"""Check time & if appropriate, display next message from queue.
Args:
message_queue: FIFO list of message tuples of (message type, message string).
next_message_time: epoch at which next message should be displayed
configuration: dictionary of configuration attributes.
Returns:
Next_message_time, potentially updated if a message has been displayed, or unchanged
if no message was displayed.
"""
if message_queue and (time.time() >= next_message_time or SIMULATION):
if SIMULATION: # drain the queue because the messages come so fast
messages_to_display = list(message_queue)
# passed by reference, so clear it out since we drained it to the display
del message_queue[:]
else: # display only one message, being mindful of the display timing
messages_to_display = [message_queue.pop(0)]
<----SKIPPED LINES---->
# process this if its a truly new flight
new_flight_flag = ConfirmNewFlight(flight, flights)
if new_flight_flag:
flights.append(flight)
remote, servo = RefreshArduinos(
remote, servo,
to_remote_q, to_servo_q, to_main_q, shutdown,
flights, json_desc_dict, configuration)
flight_meets_display_criteria = FlightMeetsDisplayCriteria(
flight, configuration, log=True)
if flight_meets_display_criteria:
flight_message = (FLAG_MSG_FLIGHT, CreateMessageAboutFlight(flight))
# display the next message about this flight now!
next_message_time = time.time()
message_queue.insert(0, flight_message)
# and delete any queued insight messages about other flights that have
# not yet displayed, since a newer flight has taken precedence
messages_to_delete = [m for m in message_queue if m[0] == FLAG_MSG_INSIGHT]
if messages_to_delete and VERBOSE:
Log(
'Deleting messages from queue due to new-found plane: %s' %
messages_to_delete)
message_queue = [m for m in message_queue if m[0] != FLAG_MSG_INSIGHT]
# Though we also manage the message queue outside this conditional as well,
# because it can take a half second to generate the flight insights, this allows
# this message to start displaying on the board immediately, so it's up there
# when it's most relevant
next_message_time = ManageMessageQueue(
message_queue, next_message_time, configuration)
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)
message_queue, next_message_time = ProcessArduinoCommmands(
to_main_q, flights, configuration, message_queue, next_message_time)
PersonalMessage(configuration, message_queue)
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))
# check time & if appropriate, display next message from queue
<----SKIPPED LINES---->
|