Skip to content

Student API

benliao1 edited this page Jan 14, 2021 · 13 revisions

Student API

The student API is the set of all functions that the students have available to them to communicate with the robot.

Cython

The student API is written entirely in Cython, which is a static compiler from Python to C. It provides static typing and lets you easily call C functions within Python. You can learn more about Cython at our wiki page https://github.com/pioneers/runtime/wiki/Cython.

In Cython, the .pxd files act as header files while .pyx files act as source files. In runtime.pxd, you will see many extern declarations to C functions in Runtime. In studentapi.pyx, each API function converts the Python arguments into C arguments and calls the corresponding shm_wrapper C functions to access shared memory. We will now discuss each API function in more detail.

API

print

We actually modify the default print function by changing builtins.print so that any print statement used by the student code is instead redirected to log_printf in the logger to be sent over the network. This also includes changing the default sys.stderr file stream to have the exception tracebacks be properly formatted when printed.

Robot.get_value

This function will retrieve a device parameter for a specific device id from shared memory. This uses the device_read_uid function from shm_wrapper. The device id input is of the form {type}_{uid}. The proper exceptions are thrown for invalid parameters.

Robot.set_value

This function will set a device parameter for a specific device id in shared memory. This uses the device_write_uid function from shm_wrapper. The device id input is of the form {type}_{uid}. The proper exceptions are thrown for invalid parameters.

Robot.run

This is a purely Python function that will start the given function in a separate thread using the threading module. We have to create a new Thread subclass called ThreadWrapper to have us that will properly print out the exception traceback. It will also set a threading.Event to ensure that exceptions in this action thread are correctly raised to the executor to stop running the mode.

Robot.is_running

This just returns whether the given function is being run asynchronously in another thread.

Robot.log

This allows students to log values other than device parameters on the Dawn dashboard. This uses the log_data_write function from shm_wrapper. It will also raise an exception if the type of the value is not an int, float, or bool.

Robot.sleep

We had some issues figuring out how to cancel running the student code when it is busy doing other calculations or when it is sleeping. To make this possible, all sleeps in the student code should use Robot.sleep instead of time.sleep. This function is different in that if the sleep is called in the main thread, it will use the threading.Event.wait function, which is cancellable by the executor by calling threading.Event.set. If this is called in an action thread, then it uses the normal time.sleep as these threads are killed immediately when their parent process is killed.

The reason we needed to use the threading.Event is that the Python code is being executed in the main thread of the executor process along with our C code. So if we sleep in the main thread, none of our C error-checking/timeout code will run, which is not good as it is very unsafe if we can't cancel the robot at any point in time.

`Gamepad.get_value

This function will receive a gamepad value from shared memory, which is populated by the net_handler. This uses the input_read function in shm_wrapper and will throw an exception if the parameter is invalid.

Keyboard.get_value

This function will retrieve data about the keyboard state from shared memory, which is populated by net_handler. It uses the input_read function in shm_wrapper and will throw an exception if the parameter is invalid.

Device Subscriptions

To reduce the amount of data we communicate over Serial, we have to specify to the Lowcar devices which parameters we want to receive. To know which parameters the students use, we parse the student code using get_all_params to find all devices and their corresponding parameters in any Robot.get_value or Robot.set_value functions using regex. Then we request the subscriptions in executor by calling make_device_subs, which then calls place_sub_requests from shm_wrapper. One significant drawback that still exists with this is that if the devices or parameters are local variables instead of being global variables or literals, then the parsing will break which might cause the student code to give incorrect device values. This is an ongoing bug, and thus the students need to be warned to not do this.