01234567890123456789012345678901234567890123456789012345678901234567890123456789
7374757677787980818283848586878889909192 93949596979899100101102103104105106107108109110111112 110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132 1133113411351136113711381139114011411142114311441145114611471148114911501151115211531154 13841385138613871388138913901391139213931394139513961397139813991400140114021403 14041405140614071408140914101411141214131414141514161417141814191420142114221423 46854686468746884689469046914692469346944695469646974698469947004701470247034704 47054706470747084709471047114712471347144715471647174718471947204721472247234724 53885389539053915392539353945395539653975398539954005401540254035404540554065407 540854095410541154125413541454155416541754185419542054215422542354245425542654275428 56075608560956105611561256135614561556165617561856195620562156225623562456255626 562756285629563056315632563356345635563656375638563956405641564256435644564556465647 |
<----SKIPPED LINES---->
# 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'
# 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.
<----SKIPPED LINES---->
files = [full_path]
else:
return []
data = []
if filenames:
return files
for file in files:
try:
with open(file, 'rb') as f:
while True:
data.append(pickle.load(f))
except (EOFError, pickle.UnpicklingError):
pass
return data
def PickleObjectToFile(data, full_path, date_segmentation):
"""Append one pickled flight to the end of binary file.
Args:
data: data to pickle
full_path: name (potentially including path) of the pickled file
date_segmentation: boolean indicating whether the date string yyyy-mm-dd should be
prepended to the file name in full_path based on the current date, so that
pickled files are segmented by date.
"""
if date_segmentation:
full_path = PrependFileName(full_path, EpochDisplayTime(time.time(), '%Y-%m-%d-'))
try:
with open(full_path, 'ab') as f:
f.write(pickle.dumps(data))
except IOError:
Log('Unable to append pickle ' + full_path)
def UpdateAircraftList(persistent_nearby_aircraft, current_nearby_aircraft, now):
"""Identifies newly seen aircraft and removes aircraft that haven't been seen recently.
Updates persistent_nearby_aircraft as follows: flights that have been last seen more
than PERSISTENCE_SECONDS seconds ago are removed; new flights in current_nearby_aircraft
are added. Also identifies newly-seen aircraft and updates the last-seen timestamp of
flights that have been seen again.
Args:
persistent_nearby_aircraft: dictionary where keys are flight number / squawk tuples,
and the values are the time the flight was last seen.
<----SKIPPED LINES---->
Args:
dump_json: The text representation of the json message from dump1090-mutability
persistent_path: dictionary where keys are flight numbers, and the values are a
sequential list of the location-attributes in the json file; allows for tracking
the flight path over time.
Returns:
Return tuple:
- dictionary of all nearby planes, where keys are flight numbers (i.e.: 'SWA7543'),
and the value is itself a dictionary of attributes.
- time stamp in the json file.
- dictionary of attributes about the radio range
- persistent dictionary of the track of recent flights, where keys are the flight
numbers and the value is a tuple, the first element being when the flight was last
seen in this radio, and the second is a list of dictionaries with past location info
from the radio where it's been seen, i.e.: d[flight] = (timestamp, [{}, {}, {}])
"""
parsed = json.loads(dump_json)
now = parsed['now']
nearby_aircraft = {}
# Build dictionary summarizing characteristics of the dump_json itself
json_desc_dict = DescribeDumpJson(parsed)
for aircraft in parsed['aircraft']:
simplified_aircraft = {}
simplified_aircraft['now'] = now
# flight_number
flight_number = aircraft.get('flight')
if flight_number:
flight_number = flight_number.strip()
# squawk
squawk = aircraft.get('squawk')
if squawk:
squawk = squawk.strip()
<----SKIPPED LINES---->
global ALL_MESSAGE_FILE
ALL_MESSAGE_FILE = PrependFileName(ALL_MESSAGE_FILE, SIMULATION_PREFIX)
ClearFile(ALL_MESSAGE_FILE)
global LOGFILE
LOGFILE = PrependFileName(LOGFILE, SIMULATION_PREFIX)
ClearFile(LOGFILE)
global ROLLING_LOGFILE
ROLLING_LOGFILE = PrependFileName(ROLLING_LOGFILE, SIMULATION_PREFIX)
ClearFile(ROLLING_LOGFILE)
global ROLLING_MESSAGE_FILE
ROLLING_MESSAGE_FILE = PrependFileName(ROLLING_MESSAGE_FILE, SIMULATION_PREFIX)
ClearFile(ROLLING_MESSAGE_FILE)
global PICKLE_FLIGHTS
PICKLE_FLIGHTS = PrependFileName(PICKLE_FLIGHTS, SIMULATION_PREFIX)
ClearFile(PICKLE_FLIGHTS)
def SimulationEnd(message_queue, flights):
"""Clears message buffer, exercises histograms, and other misc test & status code.
Args:
message_queue: List of flight messages that have not yet been printed.
flights: List of flights dictionaries.
"""
if flights:
histogram = {
'type': 'both',
'histogram':'all',
'histogram_history':'30d',
'histogram_max_screens': '_2',
'histogram_data_summary': 'on'}
message_queue.extend(TriggerHistograms(flights, histogram))
while message_queue:
ManageMessageQueue(message_queue, 0, {'setting_delay': 0})
<----SKIPPED LINES---->
# There is potential complication in that the last flight and the new flight
# crossed into a new day, and we are using date segmentation so that the last
# flight exists in yesterday's file
max_days = 1
if not SIMULATION and DisplayTime(flight, '%x') != DisplayTime(last_flight, '%x'):
max_days = 2
message += (
'; in repickling, we crossed days, so pickled flights that might otherwise'
' be in %s file are now all located in %s file' % (
DisplayTime(last_flight, '%x'), DisplayTime(flight, '%x')))
Log(message)
args = (PICKLE_FLIGHTS, not SIMULATION, max_days)
saved_flights = UnpickleObjectFromFile(*args)[:-1]
files_to_overwrite = UnpickleObjectFromFile(*args, filenames=True)
for file in files_to_overwrite:
os.remove(file)
for f in saved_flights:
PickleObjectToFile(f, PICKLE_FLIGHTS, not SIMULATION)
return False
def HeartbeatRestart():
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):
if SIMULATION:
return last_heartbeat_time
now = time.time()
if now - last_heartbeat_time > HEARTBEAT_SECONDS:
UpdateDashboard(False)
last_heartbeat_time = now
return last_heartbeat_time
<----SKIPPED LINES---->
# 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, not SIMULATION)
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)
<----SKIPPED LINES---->
|
01234567890123456789012345678901234567890123456789012345678901234567890123456789
737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159 13891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429 4691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733 53975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440 56195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662 |
<----SKIPPED LINES---->
# 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_'
# 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.
<----SKIPPED LINES---->
files = [full_path]
else:
return []
data = []
if filenames:
return files
for file in files:
try:
with open(file, 'rb') as f:
while True:
data.append(pickle.load(f))
except (EOFError, pickle.UnpicklingError):
pass
return data
def PickleObjectToFile(data, full_path, date_segmentation, date_suffix=None):
"""Append one pickled flight to the end of binary file.
Args:
data: data to pickle
full_path: name (potentially including path) of the pickled file
date_segmentation: boolean indicating whether the date string yyyy-mm-dd should be
prepended to the file name in full_path based on the current date, so that
pickled files are segmented by date.
"""
if not date_suffix:
date_suffix = EpochDisplayTime(time.time(), '%Y-%m-%d-')
if date_segmentation:
full_path = PrependFileName(full_path, date_suffix)
try:
with open(full_path, 'ab') as f:
f.write(pickle.dumps(data))
except IOError:
Log('Unable to append pickle ' + full_path)
def UpdateAircraftList(persistent_nearby_aircraft, current_nearby_aircraft, now):
"""Identifies newly seen aircraft and removes aircraft that haven't been seen recently.
Updates persistent_nearby_aircraft as follows: flights that have been last seen more
than PERSISTENCE_SECONDS seconds ago are removed; new flights in current_nearby_aircraft
are added. Also identifies newly-seen aircraft and updates the last-seen timestamp of
flights that have been seen again.
Args:
persistent_nearby_aircraft: dictionary where keys are flight number / squawk tuples,
and the values are the time the flight was last seen.
<----SKIPPED LINES---->
Args:
dump_json: The text representation of the json message from dump1090-mutability
persistent_path: dictionary where keys are flight numbers, and the values are a
sequential list of the location-attributes in the json file; allows for tracking
the flight path over time.
Returns:
Return tuple:
- dictionary of all nearby planes, where keys are flight numbers (i.e.: 'SWA7543'),
and the value is itself a dictionary of attributes.
- time stamp in the json file.
- dictionary of attributes about the radio range
- persistent dictionary of the track of recent flights, where keys are the flight
numbers and the value is a tuple, the first element being when the flight was last
seen in this radio, and the second is a list of dictionaries with past location info
from the radio where it's been seen, i.e.: d[flight] = (timestamp, [{}, {}, {}])
"""
parsed = json.loads(dump_json)
now = parsed['now']
print(EpochDisplayTime(now)) #TODO
nearby_aircraft = {}
# Build dictionary summarizing characteristics of the dump_json itself
json_desc_dict = DescribeDumpJson(parsed)
for aircraft in parsed['aircraft']:
simplified_aircraft = {}
simplified_aircraft['now'] = now
# flight_number
flight_number = aircraft.get('flight')
if flight_number:
flight_number = flight_number.strip()
# squawk
squawk = aircraft.get('squawk')
if squawk:
squawk = squawk.strip()
<----SKIPPED LINES---->
global ALL_MESSAGE_FILE
ALL_MESSAGE_FILE = PrependFileName(ALL_MESSAGE_FILE, SIMULATION_PREFIX)
ClearFile(ALL_MESSAGE_FILE)
global LOGFILE
LOGFILE = PrependFileName(LOGFILE, SIMULATION_PREFIX)
ClearFile(LOGFILE)
global ROLLING_LOGFILE
ROLLING_LOGFILE = PrependFileName(ROLLING_LOGFILE, SIMULATION_PREFIX)
ClearFile(ROLLING_LOGFILE)
global ROLLING_MESSAGE_FILE
ROLLING_MESSAGE_FILE = PrependFileName(ROLLING_MESSAGE_FILE, SIMULATION_PREFIX)
ClearFile(ROLLING_MESSAGE_FILE)
global PICKLE_FLIGHTS
PICKLE_FLIGHTS = PrependFileName(PICKLE_FLIGHTS, SIMULATION_PREFIX)
ClearFile(PICKLE_FLIGHTS)
filenames = UnpickleObjectFromFile(PICKLE_FLIGHTS, True, max_days=None, filenames=True)
for file in filenames:
ClearFile(file)
def SimulationEnd(message_queue, flights):
"""Clears message buffer, exercises histograms, and other misc test & status code.
Args:
message_queue: List of flight messages that have not yet been printed.
flights: List of flights dictionaries.
"""
if flights:
histogram = {
'type': 'both',
'histogram':'all',
'histogram_history':'30d',
'histogram_max_screens': '_2',
'histogram_data_summary': 'on'}
message_queue.extend(TriggerHistograms(flights, histogram))
while message_queue:
ManageMessageQueue(message_queue, 0, {'setting_delay': 0})
<----SKIPPED LINES---->
# There is potential complication in that the last flight and the new flight
# crossed into a new day, and we are using date segmentation so that the last
# flight exists in yesterday's file
max_days = 1
if not SIMULATION and DisplayTime(flight, '%x') != DisplayTime(last_flight, '%x'):
max_days = 2
message += (
'; in repickling, we crossed days, so pickled flights that might otherwise'
' be in %s file are now all located in %s file' % (
DisplayTime(last_flight, '%x'), DisplayTime(flight, '%x')))
Log(message)
args = (PICKLE_FLIGHTS, not SIMULATION, max_days)
saved_flights = UnpickleObjectFromFile(*args)[:-1]
files_to_overwrite = UnpickleObjectFromFile(*args, filenames=True)
for file in files_to_overwrite:
os.remove(file)
for f in saved_flights:
if SPLIT_SIMULATION_FLIGHT_PICKLE:
PickleObjectToFile(f, PICKLE_FLIGHTS, True, date_suffix=DisplayTime(f, '%Y-%m-%d-'))
else:
PickleObjectToFile(f, PICKLE_FLIGHTS, not SIMULATION)
return False
def HeartbeatRestart():
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):
if SIMULATION:
return last_heartbeat_time
now = time.time()
if now - last_heartbeat_time > HEARTBEAT_SECONDS:
UpdateDashboard(False)
last_heartbeat_time = now
return last_heartbeat_time
<----SKIPPED LINES---->
# 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'] = []
if SPLIT_SIMULATION_FLIGHT_PICKLE:
PickleObjectToFile(flight, PICKLE_FLIGHTS, True, date_suffix=DisplayTime(flight, '%Y-%m-%d-'))
else:
PickleObjectToFile(flight, PICKLE_FLIGHTS, not SIMULATION)
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)
<----SKIPPED LINES---->
|