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----> |