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