01234567890123456789012345678901234567890123456789012345678901234567890123456789
721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799 800801802803804805806807808809810811812813814815816817818819 911912913914915916917918919920921922923924925926927928929930931 932933934935936937938939940941 942943944945946947948949950951952953954955956957958959960961 9991000100110021003100410051006100710081009101010111012101310141015101610171018 10191020102110221023102410251026102710281029103010311032103310341035103610371038 125312541255125612571258125912601261126212631264126512661267126812691270127112721273 127412751276 12771278127912801281128212831284128512861287128812891290129112921293129412951296 13111312131313141315131613171318131913201321132213231324132513261327132813291330 13311332133313341335133613371338133913401341134213431344134513461347134813491350 13781379138013811382138313841385138613871388138913901391139213931394139513961397 1398 | <----SKIPPED LINES----> Takes the latest flight from the to_arduino_q and converts that to the current azimuth and altitude of the plane on a hemisphere. """ sys.stderr = open(messageboard.STDERR_FILE, 'a') Log('Process started with process id %d' % os.getpid()) # Ensures that the child can exit if the parent exits unexpectedly # docs.python.org/2/library/multiprocessing.html # #multiprocessing.Queue.cancel_join_thread to_arduino_q.cancel_join_thread() to_parent_q.cancel_join_thread() # write_format: azimuth, altitude, R, G, & B intensity # read heartbeat: millis link = Serial( *SERVO_CONNECTION, read_timeout=60, error_pin=messageboard.GPIO_ERROR_ARDUINO_SERVO_CONNECTION, to_parent_q=to_parent_q, read_format='l', write_format='ff???', name='Servo') link.Open() last_flight = {} last_angles = (0, 0) flight, json_desc_dict, configuration, additional_attr = InitialMessageValues( to_arduino_q) next_read = 0 next_write = 0 now = GetNow(json_desc_dict, additional_attr) while not shutdown.value: SetLoggingGlobals(configuration) if not to_arduino_q.empty(): flight, json_desc_dict, configuration, additional_attr = to_arduino_q.get( block=False) if 'test_servos_ordinal' in configuration: messageboard.RemoveSetting(configuration, 'test_servos_ordinal') ServoTestOrdinal(link) elif 'test_servos_sweep' in configuration: messageboard.RemoveSetting(configuration, 'test_servos_sweep') ServoTestSweep(link) new_flight = DifferentFlights(flight, last_flight) if new_flight: Log('Flight changed from %s to %s' % ( messageboard.DisplayFlightNumber(last_flight), messageboard.DisplayFlightNumber(flight) ), ser=link) # Turn off laser so line isn't traced while it moves to new position link.Write((*last_angles, *LASER_OFF)) last_flight = flight if time.time() >= next_read: heartbeat = link.Read() # simple ack message sent by servos next_read = time.time() + READ_DELAY_TIME_SERVO if heartbeat and VERBOSE: Log('Heartbeat read by Servo: %s' % str(heartbeat)) now = GetNow(json_desc_dict, additional_attr) current_angles = AzimuthAltitude(flight, now) if current_angles and time.time() > next_write: if current_angles[1] >= configuration['minimum_altitude_servo_tracking']: if VERBOSE: Log('Flight #: %s current_angles: %s' % ( messageboard.DisplayFlightNumber(flight), str(current_angles))) laser_rgb = LaserRGBFlight(flight) link.Write((*current_angles, *laser_rgb)) last_angles = current_angles else: link.Write((*last_angles, *LASER_OFF)) next_write = time.time() + WRITE_DELAY_TIME link.Close(SHUTDOWN_TEXT) def LaserRGBFlight(flight): """Based on flight attributes, set the laser.""" # Possible assignment based on: # - ascending / descending / level # - to SFO / from SFO / other # - big plane / med plane / small plane # - low alt / med alt / high alt # - low speed / med speed / high speed # - rare destination / common destination aircraft_length = messageboard.AircraftLength(flight) if aircraft_length > 50: return LASER_RED if aircraft_length > 30: return LASER_GREEN return LASER_BLUE <----SKIPPED LINES----> else: values.append(d[k]) return tuple(values) def GetNow(json_desc_dict, additional_attr): """Identifies epoch to use in the Arduinos for the "current" time, i.e.: now. Simulations should use a timestamp contemporaneous with the flights, whereas live data should use the current timestamp. """ if not additional_attr: return 0 if additional_attr['simulation']: return json_desc_dict['now'] return time.time() def GenerateRemoteMessage( flight, json_desc_dict, configuration, additional_attr, display_mode): """Generates a value-tuple to be packed and sent to the arduino remote. Args: flight: dictionary describing the most recent flight. json_desc_dict: dictionary representing the current radio signal. configuration: dictionary representing the current state of the messageboard configuration. additional_attr: dictionary with miscellaneous attributes from messageboard. display_mode: integer specifying the display mode, so that the text display lines can be appropriately configured. Returns: Dictionary of values, where the dict keys and types are specified by RemoteMain.write_config. """ flight_last_seen = flight.get('now') # time flight was seen line1_decimal_mask = '00000000' line2_decimal_mask = '00000000' if display_mode == DISP_LAST_FLIGHT_NUMB_ORIG_DEST: # UAL1827 / SFO-LAX line1 = '' line2 = '' if flight: line1 = messageboard.DisplayFlightNumber(flight) origin = messageboard.DisplayOriginIata(flight)[:3] destination = messageboard.DisplayDestinationIata(flight)[:3] line2 = '%s-%s' % (origin, destination) elif display_mode == DISP_LAST_FLIGHT_AZIMUTH_ELEVATION: <----SKIPPED LINES----> if radio_range_flights != 1: plural = 'S' line2 = '%d PLANE%s' % (radio_range_flights, plural) d = {} setting_screen_enabled = False if 'setting_screen_enabled' in configuration: setting_screen_enabled = True d['setting_screen_enabled'] = setting_screen_enabled d['setting_max_distance'] = configuration['setting_max_distance'] d['setting_max_altitude'] = configuration['setting_max_altitude'] d['setting_on_time'] = configuration['setting_on_time'] d['setting_off_time'] = configuration['setting_off_time'] d['setting_delay'] = configuration['setting_delay'] d['last_flight_available'] = additional_attr['last_flight_available'] d['line1'] = line1.upper() d['line2'] = line2.upper() d['line1_dec_mask'] = int(line1_decimal_mask, 2) d['line2_dec_mask'] = int(line2_decimal_mask, 2) d['display_mode'] = display_mode return d def ExecuteArduinoCommand( command, configuration, display_mode, low_battery, to_parent_q, link): """Executes the request as communicated in the command string. The remote may make one of the following requests: - Update a setting - (Re)display a recent flight - Display a histogram - Send information for a different display mode - Indicate that the battery is low Args: command: dictionary representing all data fields from remote. configuration: dictionary representing the current state of the messageboard configuration. display_mode: current display mode; only passed so that we may <----SKIPPED LINES----> ('?', '?', 'h') and a string format specifier: '??h' """ k_tuple = tuple([t[0] for t in config]) f_tuple = tuple([t[1] for t in config]) f_string = ''.join(f_tuple) # https://docs.python.org/3/library/struct.html#struct-alignment f_string = '<' + f_string return k_tuple, f_tuple, f_string def SendRemoteMessage( flight, json_desc_dict, configuration, additional_attr, display_mode, write_keys, write_format_tuple, link): """Sends message to the remote with current settings & text for given mode.""" message_dict = GenerateRemoteMessage( flight, json_desc_dict, configuration, additional_attr, display_mode) message_tuple = DictToValueTuple(message_dict, write_keys, write_format_tuple) link.Write(message_tuple) next_write = time.time() + WRITE_DELAY_TIME return next_write def RemoteMain(to_arduino_q, to_parent_q, shutdown): """Main process for controlling the arduino-based remote control. Takes various data from the messageboard and formats it for display on the alphanumeric display on the remote control; provides latest configuration; and executes any commands such as histogram requests or setting updates. """ sys.stderr = open(messageboard.STDERR_FILE, 'a') Log('Process started with process id %d' % os.getpid()) # Ensures that the child can exit if the parent exits unexpectedly # docs.python.org/2/library/multiprocessing.html <----SKIPPED LINES----> ('last_plane', '?'), ('display_mode', 'H'), ('histogram_enabled', '?'), ('current_hist_type', 'H'), ('current_hist_history', 'H'), ('low_battery', '?')) write_config = ( ('setting_screen_enabled', '?'), # 1 bytes ('setting_max_distance', 'H'), # 2 bytes ('setting_max_altitude', 'L'), # 4 bytes ('setting_on_time', 'H'), # 2 bytes ('setting_off_time', 'H'), # 2 bytes ('setting_delay', 'H'), # 2 bytes ('line1', '9s'), # 9 bytes; 8 character & terminator ('line2', '9s'), # 9 bytes; 8 character & terminator ('line1_dec_mask', 'H'), # 2 bytes ('line2_dec_mask', 'H'), # 2 bytes ('display_mode', 'H'), # 2 bytes ('last_flight_available', '?'), # 1 byte ) #pylint: enable = bad-whitespace read_keys, unused_read_format_tuple, read_format_string = SplitFormat( read_config) write_keys, write_format_tuple, write_format_string = SplitFormat( write_config) values_d = {} low_batt = False to_parent_q.put(('pin', (messageboard.GPIO_ERROR_BATTERY_CHARGE, low_batt))) link = Serial( *REMOTE_CONNECTION, read_timeout=60, error_pin=messageboard.GPIO_ERROR_ARDUINO_REMOTE_CONNECTION, to_parent_q=to_parent_q, read_format=read_format_string, write_format=write_format_string, name='Remote') link.Open() # Read in the saved display mode, if it exists <----SKIPPED LINES----> TestDisplayMode(DISP_LAST_FLIGHT_NUMB_ORIG_DEST) TestDisplayMode(DISP_LAST_FLIGHT_AZIMUTH_ELEVATION) TestDisplayMode(DISP_FLIGHT_COUNT_LAST_SEEN) TestDisplayMode(DISP_RADIO_RANGE) if time.time() >= next_write: next_write = SendRemoteMessage( flight, json_desc_dict, configuration, additional_attr, display_mode, write_keys, write_format_tuple, link) if time.time() >= next_read: bytes_read = [] values_t = link.Read(bytes_read=bytes_read) values_d = dict(zip(read_keys, values_t)) if values_d.get('confirmed'): Log(str(bytes_read) + '\n' + str(values_t) + '\n' + str(values_d)) display_mode, low_batt = ExecuteArduinoCommand( values_d, configuration, display_mode, low_batt, to_parent_q, link) next_read = time.time() + READ_DELAY_TIME_REMOTE link.Close(SHUTDOWN_TEXT) |
01234567890123456789012345678901234567890123456789012345678901234567890123456789
721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821 913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965 10031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043 1258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303 13181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358 1386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410 | <----SKIPPED LINES----> Takes the latest flight from the to_arduino_q and converts that to the current azimuth and altitude of the plane on a hemisphere. """ sys.stderr = open(messageboard.STDERR_FILE, 'a') Log('Process started with process id %d' % os.getpid()) # Ensures that the child can exit if the parent exits unexpectedly # docs.python.org/2/library/multiprocessing.html # #multiprocessing.Queue.cancel_join_thread to_arduino_q.cancel_join_thread() to_parent_q.cancel_join_thread() # write_format: azimuth, altitude, R, G, & B intensity # read heartbeat: millis link = Serial( *SERVO_CONNECTION, read_timeout=60, error_pin=messageboard.GPIO_ERROR_ARDUINO_SERVO_CONNECTION, to_parent_q=to_parent_q, read_format='l', write_format='ff????', name='Servo') link.Open() last_flight = {} last_angles = (0, 0) flight, json_desc_dict, configuration, additional_attr = InitialMessageValues( to_arduino_q) next_read = 0 next_write = 0 now = GetNow(json_desc_dict, additional_attr) while not shutdown.value: SetLoggingGlobals(configuration) if not to_arduino_q.empty(): flight, json_desc_dict, configuration, additional_attr = to_arduino_q.get( block=False) if 'test_servos_ordinal' in configuration: messageboard.RemoveSetting(configuration, 'test_servos_ordinal') ServoTestOrdinal(link) elif 'test_servos_sweep' in configuration: messageboard.RemoveSetting(configuration, 'test_servos_sweep') ServoTestSweep(link) new_flight = DifferentFlights(flight, last_flight) if new_flight: Log('Flight changed from %s to %s' % ( messageboard.DisplayFlightNumber(last_flight), messageboard.DisplayFlightNumber(flight) ), ser=link) # Turn off laser so line isn't traced while it moves to new position link.Write((*last_angles, *LASER_OFF, False)) last_flight = flight if time.time() >= next_read: heartbeat = link.Read() # simple ack message sent by servos next_read = time.time() + READ_DELAY_TIME_SERVO if heartbeat and VERBOSE: Log('Heartbeat read by Servo: %s' % str(heartbeat)) now = GetNow(json_desc_dict, additional_attr) current_angles = AzimuthAltitude(flight, now) if current_angles and time.time() > next_write: if current_angles[1] >= configuration['minimum_altitude_servo_tracking']: if VERBOSE: Log('Flight #: %s current_angles: %s' % ( messageboard.DisplayFlightNumber(flight), str(current_angles))) laser_rgb = LaserRGBFlight(flight) link.Write((*current_angles, *laser_rgb, False)) last_angles = current_angles else: link.Write((*last_angles, *LASER_OFF, False)) next_write = time.time() + WRITE_DELAY_TIME # One final write telling Arduino to do a software reset link.Write((*last_angles, *LASER_OFF, True)) link.Close(SHUTDOWN_TEXT) def LaserRGBFlight(flight): """Based on flight attributes, set the laser.""" # Possible assignment based on: # - ascending / descending / level # - to SFO / from SFO / other # - big plane / med plane / small plane # - low alt / med alt / high alt # - low speed / med speed / high speed # - rare destination / common destination aircraft_length = messageboard.AircraftLength(flight) if aircraft_length > 50: return LASER_RED if aircraft_length > 30: return LASER_GREEN return LASER_BLUE <----SKIPPED LINES----> else: values.append(d[k]) return tuple(values) def GetNow(json_desc_dict, additional_attr): """Identifies epoch to use in the Arduinos for the "current" time, i.e.: now. Simulations should use a timestamp contemporaneous with the flights, whereas live data should use the current timestamp. """ if not additional_attr: return 0 if additional_attr['simulation']: return json_desc_dict['now'] return time.time() def GenerateRemoteMessage( flight, json_desc_dict, configuration, additional_attr, display_mode, reset): """Generates a value-tuple to be packed and sent to the arduino remote. Args: flight: dictionary describing the most recent flight. json_desc_dict: dictionary representing the current radio signal. configuration: dictionary representing the current state of the messageboard configuration. additional_attr: dictionary with miscellaneous attributes from messageboard. display_mode: integer specifying the display mode, so that the text display lines can be appropriately configured. reset: boolean telling Arduino if it should do a software reset. Returns: Dictionary of values, where the dict keys and types are specified by RemoteMain.write_config. """ flight_last_seen = flight.get('now') # time flight was seen line1_decimal_mask = '00000000' line2_decimal_mask = '00000000' if display_mode == DISP_LAST_FLIGHT_NUMB_ORIG_DEST: # UAL1827 / SFO-LAX line1 = '' line2 = '' if flight: line1 = messageboard.DisplayFlightNumber(flight) origin = messageboard.DisplayOriginIata(flight)[:3] destination = messageboard.DisplayDestinationIata(flight)[:3] line2 = '%s-%s' % (origin, destination) elif display_mode == DISP_LAST_FLIGHT_AZIMUTH_ELEVATION: <----SKIPPED LINES----> if radio_range_flights != 1: plural = 'S' line2 = '%d PLANE%s' % (radio_range_flights, plural) d = {} setting_screen_enabled = False if 'setting_screen_enabled' in configuration: setting_screen_enabled = True d['setting_screen_enabled'] = setting_screen_enabled d['setting_max_distance'] = configuration['setting_max_distance'] d['setting_max_altitude'] = configuration['setting_max_altitude'] d['setting_on_time'] = configuration['setting_on_time'] d['setting_off_time'] = configuration['setting_off_time'] d['setting_delay'] = configuration['setting_delay'] d['last_flight_available'] = additional_attr['last_flight_available'] d['line1'] = line1.upper() d['line2'] = line2.upper() d['line1_dec_mask'] = int(line1_decimal_mask, 2) d['line2_dec_mask'] = int(line2_decimal_mask, 2) d['display_mode'] = display_mode d['arduino_reset'] = reset return d def ExecuteArduinoCommand( command, configuration, display_mode, low_battery, to_parent_q, link): """Executes the request as communicated in the command string. The remote may make one of the following requests: - Update a setting - (Re)display a recent flight - Display a histogram - Send information for a different display mode - Indicate that the battery is low Args: command: dictionary representing all data fields from remote. configuration: dictionary representing the current state of the messageboard configuration. display_mode: current display mode; only passed so that we may <----SKIPPED LINES----> ('?', '?', 'h') and a string format specifier: '??h' """ k_tuple = tuple([t[0] for t in config]) f_tuple = tuple([t[1] for t in config]) f_string = ''.join(f_tuple) # https://docs.python.org/3/library/struct.html#struct-alignment f_string = '<' + f_string return k_tuple, f_tuple, f_string def SendRemoteMessage( flight, json_desc_dict, configuration, additional_attr, display_mode, write_keys, write_format_tuple, link, reset=False): """Sends message to the remote with current settings & text for given mode.""" message_dict = GenerateRemoteMessage( flight, json_desc_dict, configuration, additional_attr, display_mode, reset=reset) message_tuple = DictToValueTuple(message_dict, write_keys, write_format_tuple) link.Write(message_tuple) next_write = time.time() + WRITE_DELAY_TIME return next_write def RemoteMain(to_arduino_q, to_parent_q, shutdown): """Main process for controlling the arduino-based remote control. Takes various data from the messageboard and formats it for display on the alphanumeric display on the remote control; provides latest configuration; and executes any commands such as histogram requests or setting updates. """ sys.stderr = open(messageboard.STDERR_FILE, 'a') Log('Process started with process id %d' % os.getpid()) # Ensures that the child can exit if the parent exits unexpectedly # docs.python.org/2/library/multiprocessing.html <----SKIPPED LINES----> ('last_plane', '?'), ('display_mode', 'H'), ('histogram_enabled', '?'), ('current_hist_type', 'H'), ('current_hist_history', 'H'), ('low_battery', '?')) write_config = ( ('setting_screen_enabled', '?'), # 1 bytes ('setting_max_distance', 'H'), # 2 bytes ('setting_max_altitude', 'L'), # 4 bytes ('setting_on_time', 'H'), # 2 bytes ('setting_off_time', 'H'), # 2 bytes ('setting_delay', 'H'), # 2 bytes ('line1', '9s'), # 9 bytes; 8 character & terminator ('line2', '9s'), # 9 bytes; 8 character & terminator ('line1_dec_mask', 'H'), # 2 bytes ('line2_dec_mask', 'H'), # 2 bytes ('display_mode', 'H'), # 2 bytes ('last_flight_available', '?'), # 1 byte ('arduino_reset', '?'), # 1 byte ) #pylint: enable = bad-whitespace read_keys, unused_read_format_tuple, read_format_string = SplitFormat( read_config) write_keys, write_format_tuple, write_format_string = SplitFormat( write_config) values_d = {} low_batt = False to_parent_q.put(('pin', (messageboard.GPIO_ERROR_BATTERY_CHARGE, low_batt))) link = Serial( *REMOTE_CONNECTION, read_timeout=60, error_pin=messageboard.GPIO_ERROR_ARDUINO_REMOTE_CONNECTION, to_parent_q=to_parent_q, read_format=read_format_string, write_format=write_format_string, name='Remote') link.Open() # Read in the saved display mode, if it exists <----SKIPPED LINES----> TestDisplayMode(DISP_LAST_FLIGHT_NUMB_ORIG_DEST) TestDisplayMode(DISP_LAST_FLIGHT_AZIMUTH_ELEVATION) TestDisplayMode(DISP_FLIGHT_COUNT_LAST_SEEN) TestDisplayMode(DISP_RADIO_RANGE) if time.time() >= next_write: next_write = SendRemoteMessage( flight, json_desc_dict, configuration, additional_attr, display_mode, write_keys, write_format_tuple, link) if time.time() >= next_read: bytes_read = [] values_t = link.Read(bytes_read=bytes_read) values_d = dict(zip(read_keys, values_t)) if values_d.get('confirmed'): Log(str(bytes_read) + '\n' + str(values_t) + '\n' + str(values_d)) display_mode, low_batt = ExecuteArduinoCommand( values_d, configuration, display_mode, low_batt, to_parent_q, link) next_read = time.time() + READ_DELAY_TIME_REMOTE # send one final message telling Arduino to do a reset SendRemoteMessage( flight, json_desc_dict, configuration, additional_attr, display_mode, write_keys, write_format_tuple, link, reset=True) link.Close(SHUTDOWN_TEXT) |