def start_udp_receiver(bad_things_queue, state_queue, sm_pipe): try: recv_class = UDPRecvClass(bad_things_queue, state_queue, sm_pipe) recv_class.start() except Exception as e: bad_things_queue.put( BadThing(sys.exc_info(), str(e), event=BAD_EVENTS.UDP_RECV_ERROR))
def start_tcp(bad_things_queue, state_queue, sm_pipe): try: tcp_class = TCPClass(bad_things_queue, state_queue, sm_pipe) tcp_class.start() except Exception as e: bad_things_queue.put( BadThing(sys.exc_info(), str(e), event=BAD_EVENTS.TCP_ERROR))
def start_udp_sender(bad_things_queue, state_queue, sm_pipe): try: send_class = UDPSendClass(bad_things_queue, state_queue, sm_pipe) send_class.start() except Exception as e: bad_things_queue.put( BadThing(sys.exc_info(), str(e), event=BAD_EVENTS.UDP_SEND_ERROR))
async def main_loop(): exec_count = 0 while not terminated and (exception_cell[0] is None) and ( max_iter is None or exec_count < max_iter): next_call = loop.time( ) + 1. / RUNTIME_CONFIG.STUDENT_CODE_HZ.value studentCode.Robot._get_all_sensors() # pylint: disable=protected-access studentCode.Gamepad._get_gamepad() # pylint: disable=protected-access check_timed_out(main_fn) # Throttle sending print statements if (exec_count % 5) == 0: studentCode.Robot._send_prints() # pylint: disable=protected-access sleep_time = max(next_call - loop.time(), 0.) state_queue.put([SM_COMMANDS.STUDENT_MAIN_OK, []]) exec_count += 1 await asyncio.sleep(sleep_time) if exception_cell[0] is not None: raise exception_cell[0] # pylint: disable=raising-bad-type if not terminated: bad_things_queue.put( BadThing(sys.exc_info(), "Process Ended", event=BAD_EVENTS.END_EVENT))
def start_state_manager(bad_things_queue, state_queue, runtime_pipe): try: state_manager = StateManager(bad_things_queue, state_queue, runtime_pipe) state_manager.start() except Exception as e: bad_things_queue.put( BadThing(sys.exc_info(), str(e), event=BAD_EVENTS.STATE_MANAGER_CRASH))
def start_hibike(bad_things_queue, state_queue, pipe): # bad_things_queue - queue to runtime # state_queue - queue to StateManager # pipe - pipe from statemanager def add_paths(): """Modify sys.path so we can find hibike. """ path = os.path.dirname(os.path.abspath(__file__)) parent_path = path.rstrip("runtime") hibike = os.path.join(parent_path, "hibike") sys.path.insert(1, hibike) try: add_paths() import hibike_process # pylint: disable=import-error hibike_process.hibike_process(bad_things_queue, state_queue, pipe) except Exception as e: bad_things_queue.put(BadThing(sys.exc_info(), str(e)))
def runtime(test_name=""): # pylint: disable=too-many-statements test_mode = test_name != "" max_iter = 3 if test_mode else None def non_test_mode_print(*args): """Prints only if we are NOT in test_mode""" if not test_mode: print(args) bad_things_queue = multiprocessing.Queue() state_queue = multiprocessing.Queue() spawn_process = process_factory(bad_things_queue, state_queue) restart_count = 0 emergency_stopped = False try: spawn_process(PROCESS_NAMES.STATE_MANAGER, start_state_manager) spawn_process(PROCESS_NAMES.UDP_RECEIVE_PROCESS, start_udp_receiver) spawn_process(PROCESS_NAMES.HIBIKE, start_hibike) def fc_server_target(): fc_server = FieldControlServer(state_queue) # pylint: disable=no-member asyncio.run(run_field_control_server(fc_server, '0.0.0.0', 6020)) fc_thread = threading.Thread(target=fc_server_target, daemon=True) fc_thread.start() control_state = "idle" dawn_connected = False while True: if test_mode: # Automatically enter telop mode when running tests bad_things_queue.put( BadThing(sys.exc_info(), "Sending initial command to enter teleop", event=BAD_EVENTS.ENTER_TELEOP, printStackTrace=False)) if restart_count >= 3: non_test_mode_print( RUNTIME_CONFIG.DEBUG_DELIMITER_STRING.value) non_test_mode_print("Too many restarts, terminating") break if emergency_stopped: non_test_mode_print( RUNTIME_CONFIG.DEBUG_DELIMITER_STRING.value) non_test_mode_print("terminating due to E-Stop") break non_test_mode_print(RUNTIME_CONFIG.DEBUG_DELIMITER_STRING.value) non_test_mode_print("Starting studentCode attempt: %s" % (restart_count, )) while True: new_bad_thing = bad_things_queue.get(block=True) if new_bad_thing.event == BAD_EVENTS.NEW_IP and not dawn_connected: spawn_process(PROCESS_NAMES.UDP_SEND_PROCESS, start_udp_sender) spawn_process(PROCESS_NAMES.TCP_PROCESS, start_tcp) dawn_connected = True continue elif new_bad_thing.event == BAD_EVENTS.DAWN_DISCONNECTED and dawn_connected: terminate_process(PROCESS_NAMES.UDP_RECEIVE_PROCESS) terminate_process(PROCESS_NAMES.UDP_SEND_PROCESS) terminate_process(PROCESS_NAMES.TCP_PROCESS) spawn_process(PROCESS_NAMES.UDP_RECEIVE_PROCESS, start_udp_receiver) dawn_connected = False control_state = "idle" break elif new_bad_thing.event == BAD_EVENTS.ENTER_TELEOP and control_state != "teleop": terminate_process(PROCESS_NAMES.STUDENT_CODE) name = test_name or "teleop" spawn_process(PROCESS_NAMES.STUDENT_CODE, run_student_code, name, max_iter) control_state = "teleop" continue elif new_bad_thing.event == BAD_EVENTS.ENTER_AUTO and control_state != "auto": terminate_process(PROCESS_NAMES.STUDENT_CODE) spawn_process(PROCESS_NAMES.STUDENT_CODE, run_student_code, "autonomous") control_state = "auto" continue elif new_bad_thing.event == BAD_EVENTS.ENTER_IDLE and control_state != "idle": control_state = "idle" break print(new_bad_thing.event) non_test_mode_print(new_bad_thing.data) if new_bad_thing.event in restartEvents: state_queue.put([ SM_COMMANDS.SEND_CONSOLE, [new_bad_thing.getStudentError()] ]) control_state = "idle" if test_mode: restart_count += 1 if not emergency_stopped and new_bad_thing.event is BAD_EVENTS.EMERGENCY_STOP: emergency_stopped = True break if test_mode: state_queue.put([SM_COMMANDS.RESET, []]) terminate_process(PROCESS_NAMES.STUDENT_CODE) state_queue.put([ SM_COMMANDS.SET_VAL, [ runtime_pb2.RuntimeData.STUDENT_STOPPED, ["studentCodeState"], False ] ]) state_queue.put([SM_COMMANDS.END_STUDENT_CODE, []]) state_queue.put([HIBIKE_COMMANDS.DISABLE, []]) non_test_mode_print(RUNTIME_CONFIG.DEBUG_DELIMITER_STRING.value) print("Funtime Runtime is done having fun.") print("TERMINATING") except Exception as e: print(RUNTIME_CONFIG.DEBUG_DELIMITER_STRING.value) print("Funtime Runtime had too much fun.") print(e) print("".join(traceback.format_tb(sys.exc_info()[2])))
def run_student_code(bad_things_queue, state_queue, pipe, test_name="", max_iter=None): try: terminated = False def sig_term_handler(*_): nonlocal terminated terminated = True signal.signal(signal.SIGTERM, sig_term_handler) def timed_out_handler(*_): raise TimeoutError("studentCode timed out") signal.signal(signal.SIGALRM, timed_out_handler) def check_timed_out(func, *args): signal.alarm(RUNTIME_CONFIG.STUDENT_CODE_TIMELIMIT.value) func(*args) signal.alarm(0) signal.alarm(RUNTIME_CONFIG.STUDENT_CODE_TIMELIMIT.value) try: import studentCode except SyntaxError as e: raise RuntimeError("Student code has a syntax error: {}".format(e)) signal.alarm(0) if test_name != "": test_name += "_" try: setup_fn = getattr(studentCode, test_name + "setup") except AttributeError: raise RuntimeError( "Student code failed to define '{}'".format(test_name + "setup")) try: main_fn = getattr(studentCode, test_name + "main") except AttributeError: raise RuntimeError( "Student code failed to define '{}'".format(test_name + "main")) ensure_is_function(test_name + "setup", setup_fn) ensure_is_function(test_name + "main", main_fn) ensure_not_overridden(studentCode, "Robot") studentCode.Robot = Robot(state_queue, pipe) studentCode.Gamepad = Gamepad(state_queue, pipe) studentCode.Field = Field(state_queue, pipe) studentCode.Actions = Actions studentCode.print = studentCode.Robot._print # pylint: disable=protected-access # remapping for non-class student API commands studentCode.get_gamepad_value = studentCode.Gamepad.get_value studentCode.get_robot_value = studentCode.Robot.get_value studentCode.set_robot_value = studentCode.Robot.set_value studentCode.is_robot_running = studentCode.Robot.is_running studentCode.run_async = studentCode.Robot.run studentCode.sleep_duration = studentCode.Actions.sleep check_timed_out(setup_fn) exception_cell = [None] clarify_coroutine_warnings(exception_cell) async def main_loop(): exec_count = 0 while not terminated and (exception_cell[0] is None) and ( max_iter is None or exec_count < max_iter): next_call = loop.time( ) + 1. / RUNTIME_CONFIG.STUDENT_CODE_HZ.value studentCode.Robot._get_all_sensors() # pylint: disable=protected-access studentCode.Gamepad._get_gamepad() # pylint: disable=protected-access check_timed_out(main_fn) # Throttle sending print statements if (exec_count % 5) == 0: studentCode.Robot._send_prints() # pylint: disable=protected-access sleep_time = max(next_call - loop.time(), 0.) state_queue.put([SM_COMMANDS.STUDENT_MAIN_OK, []]) exec_count += 1 await asyncio.sleep(sleep_time) if exception_cell[0] is not None: raise exception_cell[0] # pylint: disable=raising-bad-type if not terminated: bad_things_queue.put( BadThing(sys.exc_info(), "Process Ended", event=BAD_EVENTS.END_EVENT)) loop = asyncio.get_event_loop() def my_exception_handler(_loop, context): if exception_cell[0] is None: exception_cell[0] = context["exception"] loop.set_exception_handler(my_exception_handler) loop.run_until_complete(main_loop()) except TimeoutError: event = BAD_EVENTS.STUDENT_CODE_TIMEOUT bad_things_queue.put(BadThing(sys.exc_info(), event.value, event=event)) except StudentAPIError: event = BAD_EVENTS.STUDENT_CODE_VALUE_ERROR bad_things_queue.put(BadThing(sys.exc_info(), event.value, event=event)) except Exception as e: # something broke in student code bad_things_queue.put( BadThing(sys.exc_info(), str(e), event=BAD_EVENTS.STUDENT_CODE_ERROR))
def run_student_code(bad_things_queue, state_queue, pipe, test_name="", max_iter=None): # pylint: disable=too-many-locals try: terminated = False def sig_term_handler(*_): nonlocal terminated terminated = True signal.signal(signal.SIGTERM, sig_term_handler) def timed_out_handler(*_): raise TimeoutError("studentCode timed out") signal.signal(signal.SIGALRM, timed_out_handler) def check_timed_out(func, *args): signal.alarm(RUNTIME_CONFIG.STUDENT_CODE_TIMELIMIT.value) func(*args) signal.alarm(0) signal.alarm(RUNTIME_CONFIG.STUDENT_CODE_TIMELIMIT.value) try: import studentCode except SyntaxError as e: raise RuntimeError("Student code has a syntax error: {}".format(e)) signal.alarm(0) if test_name != "": test_name += "_" try: setup_fn = getattr(studentCode, test_name + "setup") except AttributeError: raise RuntimeError( "Student code failed to define '{}'".format(test_name + "setup")) try: main_fn = getattr(studentCode, test_name + "main") except AttributeError: raise RuntimeError( "Student code failed to define '{}'".format(test_name + "main")) ensure_is_function(test_name + "setup", setup_fn) ensure_is_function(test_name + "main", main_fn) ensure_not_overridden(studentCode, "Robot") # Solar Scramble specific handling def stub_out(funcname): def stub(_): line1 = "Failed to generate power-up code: " line2 = "you haven't defined {}".format(funcname) raise AttributeError(line1 + line2) return stub def get_or_stub_out(funcname): try: return getattr(studentCode, funcname) except AttributeError: return stub_out(funcname) def identity(value): ''' Used only in the (hopefully) rare event that none of the other functions are bijections with a given domain of RFIDs ''' return value def limit_input_to(limit): '''Generate a function to limit size of inputs''' def retval(input_val): while input_val > limit: input_val = (input_val % limit) + (input_val // limit) return input_val return retval def compose_funcs(func_a, func_b): ''' Composes two single-input functions together, A(B(x)) ''' return lambda x: func_a(func_b(x)) next_power = get_or_stub_out("next_power") reverse_digits = get_or_stub_out("reverse_digits") smallest_prime_fact = get_or_stub_out("smallest_prime_fact") double_caesar_cipher = get_or_stub_out("double_caesar_cipher") silly_base_two = get_or_stub_out("silly_base_two") most_common_digit = get_or_stub_out("most_common_digit") valid_isbn_ten = get_or_stub_out("valid_isbn_ten") simd_four_square = get_or_stub_out("simd_four_square") func_map = [ identity, next_power, reverse_digits, compose_funcs(smallest_prime_fact, limit_input_to(1000000)), double_caesar_cipher, silly_base_two, most_common_digit, valid_isbn_ten, simd_four_square ] studentCode.Robot = Robot(state_queue, pipe, func_map) studentCode.Gamepad = Gamepad(state_queue, pipe) studentCode.Actions = Actions studentCode.print = studentCode.Robot._print # pylint: disable=protected-access # remapping for non-class student API commands studentCode.get_gamepad_value = studentCode.Gamepad.get_value studentCode.get_robot_value = studentCode.Robot.get_value studentCode.set_robot_value = studentCode.Robot.set_value studentCode.is_robot_running = studentCode.Robot.is_running studentCode.run_async = studentCode.Robot.run studentCode.sleep_duration = studentCode.Actions.sleep check_timed_out(setup_fn) exception_cell = [None] clarify_coroutine_warnings(exception_cell) async def main_loop(): exec_count = 0 while not terminated and (exception_cell[0] is None) and ( max_iter is None or exec_count < max_iter): next_call = loop.time( ) + 1. / RUNTIME_CONFIG.STUDENT_CODE_HZ.value studentCode.Robot._get_all_sensors() # pylint: disable=protected-access studentCode.Gamepad._get_gamepad() # pylint: disable=protected-access check_timed_out(main_fn) # Throttle sending print statements if (exec_count % 5) == 0: studentCode.Robot._send_prints() # pylint: disable=protected-access sleep_time = max(next_call - loop.time(), 0.) state_queue.put([SM_COMMANDS.STUDENT_MAIN_OK, []]) exec_count += 1 await asyncio.sleep(sleep_time) if exception_cell[0] is not None: raise exception_cell[0] # pylint: disable=raising-bad-type if not terminated: bad_things_queue.put( BadThing(sys.exc_info(), "Process Ended", event=BAD_EVENTS.END_EVENT)) loop = asyncio.get_event_loop() def my_exception_handler(_loop, context): if exception_cell[0] is None: exception_cell[0] = context["exception"] loop.set_exception_handler(my_exception_handler) loop.run_until_complete(main_loop()) except TimeoutError: event = BAD_EVENTS.STUDENT_CODE_TIMEOUT bad_things_queue.put(BadThing(sys.exc_info(), event.value, event=event)) except StudentAPIError: event = BAD_EVENTS.STUDENT_CODE_VALUE_ERROR bad_things_queue.put(BadThing(sys.exc_info(), event.value, event=event)) except Exception as e: # something broke in student code bad_things_queue.put( BadThing(sys.exc_info(), str(e), event=BAD_EVENTS.STUDENT_CODE_ERROR))