01234567890123456789012345678901234567890123456789012345678901234567890123456789
123456789101112131415161718 19202122232425262728293031323334353637383940414243444546474849505152 5354555657585960616263646566676869707172 256257258259260261262263264265266267268269270271272273274275 276277278279280281282283284285286287288289290291292293294295 435436437438439440441442443444445446447448449450451452453454 455456457458459460461462463464465466467468469470471472473474 69016902690369046905690669076908690969106911691269136914691569166917691869196920 692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959 69606961696269636964696569666967696869696970697169726973697469756976697769786979 70207021702270237024702570267027702870297030703170327033703470357036703770387039 70407041704270437044704570467047704870497050705170527053705470557056705770587059 71617162716371647165716671677168716971707171717271737174717571767177717871797180 71817182718371847185718671877188718971907191719271937194719571967197719871997200 | #!/usr/bin/python3 import datetime import io import json import math import multiprocessing import numbers import os import pickle import queue import re import shutil import signal import statistics import sys import textwrap import time import bs4 import dateutils import numpy import matplotlib import matplotlib.pyplot import psutil import pycurl import pytz import requests import tzlocal import unidecode # pylint: disable=line-too-long from constants import RASPBERRY_PI, MESSAGEBOARD_PATH, WEBSERVER_PATH, KEY, SECRET, SUBSCRIPTION_ID # pylint: enable=line-too-long import arduino if RASPBERRY_PI: import gpiozero # pylint: disable=E0401 import RPi.GPIO # pylint: disable=E0401 VERBOSE = False # additional messages logged SHUTDOWN_SIGNAL = '' REBOOT_SIGNAL = False SIMULATION = False SIMULATION_COUNTER = 0 SIMULATION_PREFIX = 'SIM_' PICKLE_DUMP_JSON_FILE = 'pickle/dump_json.pk' PICKLE_FA_JSON_FILE = 'pickle/fa_json.pk' DUMP_JSONS = None # loaded only if in simulation mode FA_JSONS = None # loaded only if in simulation mode HOME_LAT = 37.64406 HOME_LON = -122.43463 HOME = (HOME_LAT, HOME_LON) # lat / lon tuple of antenna HOME_ALT = 29 #altitude in meters RADIUS = 6371.0e3 # radius of earth in meters FEET_IN_METER = 3.28084 FEET_IN_MILE = 5280 METERS_PER_SECOND_IN_KNOTS = 0.514444 # only planes within this distance will be detailed MIN_METERS = 5000/FEET_IN_METER # planes not seen within MIN_METERS in PERSISTENCE_SECONDS seconds will be # dropped from the nearby list PERSISTENCE_SECONDS = 300 TRUNCATE = 50 # max number of keys to include in a histogram image file # number of seconds to pause between each radio poll / command processing loop <----SKIPPED LINES----> 'Undefined condition set to true', 'Undefined condition set to false', 8, 'Unused', False) # GPIO pushbutton connections - (GPIO pin switch in; GPIO pin LED out) GPIO_SOFT_RESET = (20, 21) GOOGLE_ANALYTICS_TAG = ( '<!-- Global site tag (gtag.js) - Google Analytics -->\n' '<script async src="https://www.googletagmanager.com/gtag/' 'js?id=UA-99931533-2"></script>\n' '<script>\n' ' window.dataLayer = window.dataLayer || [];\n' ' function gtag(){dataLayer.push(arguments);}\n' " gtag('js', new Date());\n" " gtag('config', 'UA-99931533-2');\n" '</script>\n') #if running on raspberry, then need to prepend path to file names if RASPBERRY_PI: PICKLE_FLIGHTS = MESSAGEBOARD_PATH + PICKLE_FLIGHTS PICKLE_DASHBOARD = MESSAGEBOARD_PATH + PICKLE_DASHBOARD LOGFILE = MESSAGEBOARD_PATH + LOGFILE PICKLE_DUMP_JSON_FILE = MESSAGEBOARD_PATH + PICKLE_DUMP_JSON_FILE PICKLE_FA_JSON_FILE = MESSAGEBOARD_PATH + PICKLE_FA_JSON_FILE PICKLE_SCREENS = MESSAGEBOARD_PATH + PICKLE_SCREENS CODE_REPOSITORY = MESSAGEBOARD_PATH HISTOGRAM_CONFIG_FILE = WEBSERVER_PATH + HISTOGRAM_CONFIG_FILE CONFIG_FILE = WEBSERVER_PATH + CONFIG_FILE ROLLING_MESSAGE_FILE = WEBSERVER_PATH + ROLLING_MESSAGE_FILE ALL_MESSAGE_FILE = WEBSERVER_PATH + ALL_MESSAGE_FILE ROLLING_LOGFILE = WEBSERVER_PATH + ROLLING_LOGFILE STDERR_FILE = WEBSERVER_PATH + STDERR_FILE BACKUP_FILE = WEBSERVER_PATH + BACKUP_FILE SERVICE_VERIFICATION_FILE = WEBSERVER_PATH + SERVICE_VERIFICATION_FILE UPTIMES_FILE = WEBSERVER_PATH + UPTIMES_FILE CODE_HISTORY_FILE = WEBSERVER_PATH + CODE_HISTORY_FILE NEW_AIRCRAFT_FILE = WEBSERVER_PATH + NEW_AIRCRAFT_FILE TEMPERATURE_LOG = WEBSERVER_PATH + TEMPERATURE_LOG <----SKIPPED LINES----> AIRCRAFT_LENGTH['Cessna Citation X (twin-jet)'] = 22.04 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 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----> def MakeVersionCopy(python_prefix): """Copies current instance of python file into repository.""" file_extension = '.py' live_name = python_prefix + '.py' live_path = os.path.join(CODE_REPOSITORY, live_name) epoch = os.path.getmtime(live_path) last_modified_suffix = EpochDisplayTime( epoch, format_string='-%Y-%m-%d-%H%M') version_name = python_prefix + last_modified_suffix + file_extension version_path = os.path.join(VERSION_REPOSITORY, version_name) if not os.path.exists(version_path): shutil.copyfile(live_path, version_path) return version_name def main(): """Traffic cop between radio, configuration, and messageboard. This is the main logic, checking for new flights, augmenting the radio signal with additional web-scraped data, and generating messages in a form presentable to the messageboard. """ VersionControl() # Since this clears log files, it should occur first before we start logging if '-s' in sys.argv: global SIMULATION_COUNTER SimulationSetup() last_heartbeat_time = HeartbeatRestart() init_timing = [(time.time(), 0)] # This flag slows down simulation time around a flight, great for # debugging the arduinos simulation_slowdown = bool('-f' in sys.argv) # Redirect any errors to a log file instead of the screen, and add a datestamp if not SIMULATION: sys.stderr = open(STDERR_FILE, 'a') Log('', STDERR_FILE) init_timing.append((time.time(), 1)) Log('Starting up process %d' % os.getpid()) already_running_ids = FindRunningParents() if already_running_ids: for pid in already_running_ids: Log('Sending termination signal to %d' % pid) os.kill(pid, signal.SIGTERM) init_timing.append((time.time(), 2)) SetPinMode() configuration = ReadAndParseSettings(CONFIG_FILE) Log('Read CONFIG_FILE at %s: %s' % (CONFIG_FILE, str(configuration))) startup_time = time.time() json_desc_dict = {} init_timing.append((time.time(), 3)) flights = UnpickleObjectFromFile( PICKLE_FLIGHTS, True, max_days=MAX_INSIGHT_HORIZON_DAYS, heartbeat=True) # Clear the loaded flight of any cached data, identified by keys # with a specific suffix, since code fixes may change the values for # some of those cached elements init_timing.append((time.time(), 4)) for flight in flights: for key in list(flight.keys()): if key.endswith(CACHED_ELEMENT_PREFIX): flight.pop(key) init_timing.append((time.time(), 5)) screen_history = UnpickleObjectFromFile(PICKLE_SCREENS, True, max_days=2) # If we're displaying just a single insight message, we want it to be <----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 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 # updated since it was last read. <----SKIPPED LINES----> if message_queue: # Any personal message displayed has been cleared personal_message = None # check time & if appropriate, display next message from queue next_message_time = ManageMessageQueue( 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) 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( 'main()', 'messageboard_stats-%s.profile' % <----SKIPPED LINES----> |
01234567890123456789012345678901234567890123456789012345678901234567890123456789
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374 258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478 6905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022 70637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106 720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252 | #!/usr/bin/python3 import datetime import io import json import math import multiprocessing import numbers import os import pickle import queue import re import shutil import signal import statistics import sys import textwrap import time import tracemalloc import bs4 import dateutils import numpy import matplotlib import matplotlib.pyplot import psutil import pycurl import pytz import requests import tzlocal import unidecode # pylint: disable=line-too-long from constants import RASPBERRY_PI, MESSAGEBOARD_PATH, WEBSERVER_PATH, KEY, SECRET, SUBSCRIPTION_ID # pylint: enable=line-too-long import arduino if RASPBERRY_PI: import gpiozero # pylint: disable=E0401 import RPi.GPIO # pylint: disable=E0401 VERBOSE = False # additional messages logged SHUTDOWN_SIGNAL = '' REBOOT_SIGNAL = False SIMULATION = False SIMULATION_COUNTER = 0 SIMULATION_PREFIX = 'SIM_' PICKLE_DUMP_JSON_FILE = 'pickle/dump_json.pk' PICKLE_FA_JSON_FILE = 'pickle/fa_json.pk' MEMORY_DIRECTORY = 'memory/' DUMP_JSONS = None # loaded only if in simulation mode FA_JSONS = None # loaded only if in simulation mode HOME_LAT = 37.64406 HOME_LON = -122.43463 HOME = (HOME_LAT, HOME_LON) # lat / lon tuple of antenna HOME_ALT = 29 #altitude in meters RADIUS = 6371.0e3 # radius of earth in meters FEET_IN_METER = 3.28084 FEET_IN_MILE = 5280 METERS_PER_SECOND_IN_KNOTS = 0.514444 # only planes within this distance will be detailed MIN_METERS = 5000/FEET_IN_METER # planes not seen within MIN_METERS in PERSISTENCE_SECONDS seconds will be # dropped from the nearby list PERSISTENCE_SECONDS = 300 TRUNCATE = 50 # max number of keys to include in a histogram image file # number of seconds to pause between each radio poll / command processing loop <----SKIPPED LINES----> 'Undefined condition set to true', 'Undefined condition set to false', 8, 'Unused', False) # GPIO pushbutton connections - (GPIO pin switch in; GPIO pin LED out) GPIO_SOFT_RESET = (20, 21) GOOGLE_ANALYTICS_TAG = ( '<!-- Global site tag (gtag.js) - Google Analytics -->\n' '<script async src="https://www.googletagmanager.com/gtag/' 'js?id=UA-99931533-2"></script>\n' '<script>\n' ' window.dataLayer = window.dataLayer || [];\n' ' function gtag(){dataLayer.push(arguments);}\n' " gtag('js', new Date());\n" " gtag('config', 'UA-99931533-2');\n" '</script>\n') #if running on raspberry, then need to prepend path to file names if RASPBERRY_PI: MEMORY_DIRECTORY = MESSAGEBOARD_PATH + MEMORY_DIRECTORY PICKLE_FLIGHTS = MESSAGEBOARD_PATH + PICKLE_FLIGHTS PICKLE_DASHBOARD = MESSAGEBOARD_PATH + PICKLE_DASHBOARD LOGFILE = MESSAGEBOARD_PATH + LOGFILE PICKLE_DUMP_JSON_FILE = MESSAGEBOARD_PATH + PICKLE_DUMP_JSON_FILE PICKLE_FA_JSON_FILE = MESSAGEBOARD_PATH + PICKLE_FA_JSON_FILE PICKLE_SCREENS = MESSAGEBOARD_PATH + PICKLE_SCREENS CODE_REPOSITORY = MESSAGEBOARD_PATH HISTOGRAM_CONFIG_FILE = WEBSERVER_PATH + HISTOGRAM_CONFIG_FILE CONFIG_FILE = WEBSERVER_PATH + CONFIG_FILE ROLLING_MESSAGE_FILE = WEBSERVER_PATH + ROLLING_MESSAGE_FILE ALL_MESSAGE_FILE = WEBSERVER_PATH + ALL_MESSAGE_FILE ROLLING_LOGFILE = WEBSERVER_PATH + ROLLING_LOGFILE STDERR_FILE = WEBSERVER_PATH + STDERR_FILE BACKUP_FILE = WEBSERVER_PATH + BACKUP_FILE SERVICE_VERIFICATION_FILE = WEBSERVER_PATH + SERVICE_VERIFICATION_FILE UPTIMES_FILE = WEBSERVER_PATH + UPTIMES_FILE CODE_HISTORY_FILE = WEBSERVER_PATH + CODE_HISTORY_FILE NEW_AIRCRAFT_FILE = WEBSERVER_PATH + NEW_AIRCRAFT_FILE TEMPERATURE_LOG = WEBSERVER_PATH + TEMPERATURE_LOG <----SKIPPED LINES----> AIRCRAFT_LENGTH['Cessna Citation X (twin-jet)'] = 22.04 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----> def MakeVersionCopy(python_prefix): """Copies current instance of python file into repository.""" file_extension = '.py' live_name = python_prefix + '.py' live_path = os.path.join(CODE_REPOSITORY, live_name) epoch = os.path.getmtime(live_path) last_modified_suffix = EpochDisplayTime( epoch, format_string='-%Y-%m-%d-%H%M') version_name = python_prefix + last_modified_suffix + file_extension version_path = os.path.join(VERSION_REPOSITORY, version_name) if not os.path.exists(version_path): shutil.copyfile(live_path, version_path) return version_name def DumpMemorySnapsnot(configuration, iteration, main_start_time): """Dump the memory snapshot to disk for later analysis. This dumps the memory snapshot to disk with a file name defined by the timestamp and iteration, in the directory MEMORY_DIRECTORY, for later analysis by tracemalloc load class; this can be used to do a memory leak detection by looking at a single snapshot, or potentially by doing a compare_to to look at the deltas from an earlier snapshot. If the 'memory' configuration is greater than 0, then snapshotting is enabled and we dump the configuration every time iteration is evenly divisible by 1000 times the 'memory' setting (because the setting as described on the settings.php page is in thousands of iterations). Args: configuration: dictionary of configuration attributes. iteration: counter that indicates how many times through the main loop we are. main_start_time: time stamp of when the program first started, to be recorded in file names, so that associated memory dumps can be more easily grouped, analyzed, and purged together. """ iteration_divisor = configuration.get('memory', 0) if not iteration_divisor: return if not iteration % (iteration_divisor * 1000): main_time_stamp = EpochDisplayTime( main_start_time, format_string='%m-%d-%H%M%S') this_time_stamp = EpochDisplayTime( time.time(), format_string='%Y-%m-%d-%H%M') file_name = '%s-%s-%d.dump' % (main_time_stamp, this_time_stamp, iteration) full_path_name = os.path.join(MEMORY_DIRECTORY, file_name) snapshot = tracemalloc.take_snapshot() snapshot.dump(full_path_name) def main(): """Traffic cop between radio, configuration, and messageboard. This is the main logic, checking for new flights, augmenting the radio signal with additional web-scraped data, and generating messages in a form presentable to the messageboard. """ VersionControl() # Since this clears log files, it should occur first before we start logging if '-s' in sys.argv: global SIMULATION_COUNTER SimulationSetup() last_heartbeat_time = HeartbeatRestart() init_timing = [(time.time(), 0)] # This flag slows down simulation time around a flight, great for # debugging the arduinos simulation_slowdown = bool('-f' in sys.argv) # Redirect any errors to a log file instead of the screen, and add a datestamp if not SIMULATION: sys.stderr = open(STDERR_FILE, 'a') Log('', STDERR_FILE) init_timing.append((time.time(), 1)) Log('Starting up process %d' % os.getpid()) already_running_ids = FindRunningParents() if already_running_ids: for pid in already_running_ids: Log('Sending termination signal to %d' % pid) os.kill(pid, signal.SIGTERM) init_timing.append((time.time(), 2)) SetPinMode() configuration = ReadAndParseSettings(CONFIG_FILE) Log('Read CONFIG_FILE at %s: %s' % (CONFIG_FILE, str(configuration))) if configuration.get('memory', 0): tracemalloc.start(25) # keep at least 25 frames startup_time = time.time() json_desc_dict = {} init_timing.append((time.time(), 3)) flights = UnpickleObjectFromFile( PICKLE_FLIGHTS, True, max_days=MAX_INSIGHT_HORIZON_DAYS, heartbeat=True) # Clear the loaded flight of any cached data, identified by keys # with a specific suffix, since code fixes may change the values for # some of those cached elements init_timing.append((time.time(), 4)) for flight in flights: for key in list(flight.keys()): if key.endswith(CACHED_ELEMENT_PREFIX): flight.pop(key) init_timing.append((time.time(), 5)) screen_history = UnpickleObjectFromFile(PICKLE_SCREENS, True, max_days=2) # If we're displaying just a single insight message, we want it to be <----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() DumpMemorySnapsnot(configuration, iteration, start_time) 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 # updated since it was last read. <----SKIPPED LINES----> if message_queue: # Any personal message displayed has been cleared personal_message = None # check time & if appropriate, display next message from queue next_message_time = ManageMessageQueue( 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 DumpMemorySnapsnot(configuration, iteration, start_time) 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( 'main()', 'messageboard_stats-%s.profile' % <----SKIPPED LINES----> |