Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4b121a2
[EXECUTOR] Modified to remove internal loop for student code
BrandonWong14 May 29, 2024
545ae14
[EXECUTOR] removed timing and loop argument from run_py_function, rem…
BrandonWong14 Jun 10, 2024
5b5a4c3
updated last 4 student code files to work with new executor
3142008956 Jun 12, 2024
0c01724
added while True to tc_212_a.py
3142008956 Jun 12, 2024
9a558ab
Merge pull request #277 from pioneers/student_code_update
BrandonWong14 Jun 12, 2024
58d70f0
modified first 5 student_code tests
ishan-monie Jun 19, 2024
f37b42f
modified first 5 student_code tests 2
ishan-monie Jun 19, 2024
39a145f
modified first 5 student_code tests 3
ishan-monie Jun 19, 2024
c05d7f2
modified first 5 student_code tests 4
ishan-monie Jun 19, 2024
3c9a81f
Merge pull request #279 from pioneers/ishan
ishan-monie Jun 19, 2024
d7fb74a
Revert "modified first 5 student_code tests"
3142008956 Jun 19, 2024
366e8b3
Merge pull request #280 from pioneers/revert-279-ishan
BrandonWong14 Jun 19, 2024
bf94d1a
Modified studentcode files
3142008956 Sep 5, 2024
e013d93
Merge pull request #283 from pioneers/first_five_tests
BrandonWong14 Sep 12, 2024
c328edf
[EXECUTOR] confirmed executor rewrite works, made small bug fixes
BrandonWong14 Feb 13, 2025
db7dbd2
Merge branch 'executor_rewrite' of github.com:pioneers/runtime into e…
BrandonWong14 Feb 13, 2025
6b75055
merged master into executor_rewrite branch
BrandonWong14 Mar 6, 2025
4ccbb21
merged master into executor_rewrite branch, correctly
BrandonWong14 Mar 6, 2025
e6e384f
[TESTS] fixed tc_71_2 and tc_71_15 to account for updates executor co…
BrandonWong14 Mar 6, 2025
3502020
[TESTS] fixed tc_71_12, attempting fix for tc_71_15
BrandonWong14 Mar 6, 2025
b715646
[TESTS] attempting fix for tc_71_15
BrandonWong14 Mar 6, 2025
e391b36
[TESTS] modified tc_71_2 so it expects teleop rather than teleop_main
BrandonWong14 Mar 6, 2025
f4f8db0
[EXECUTOR][SYSTEMD] changed python version to rasbian default, update…
BrandonWong14 Mar 14, 2025
0e9c15e
[EXECUTOR] returned python version to 3.12 to github CI tests
BrandonWong14 Mar 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 40 additions & 82 deletions executor/executor.c
Original file line number Diff line number Diff line change
Expand Up @@ -195,77 +195,56 @@ static void executor_init(char* student_code) {
* 3: Timed out by executor
* 4: Unable to find the given function in the student code
*/
static uint8_t run_py_function(const char* func_name, struct timespec* timeout, int loop, PyObject* args, PyObject** py_ret) {
static uint8_t run_py_function(const char* func_name, struct timespec* timeout, PyObject* args, PyObject** py_ret) {
uint8_t ret = 0;

// retrieve the Python function from the student code
PyObject* pFunc = PyObject_GetAttrString(pModule, func_name);
PyObject* pValue = NULL;
log_printf(ERROR, "%s", func_name);
if (pFunc && PyCallable_Check(pFunc)) {
struct timespec start, end;
uint64_t time, max_time = 0;
if (timeout != NULL) {
max_time = timeout->tv_sec * 1e9 + timeout->tv_nsec;
pValue = PyObject_CallObject(pFunc, args); // make call to Python function

// Set return value
if (py_ret != NULL) {
Py_XDECREF(*py_ret); // Decrement previous reference, if it exists
*py_ret = pValue;
} else {
Py_XDECREF(pValue);
}

do {
clock_gettime(CLOCK_MONOTONIC, &start);
pValue = PyObject_CallObject(pFunc, args); // make call to Python function
clock_gettime(CLOCK_MONOTONIC, &end);

// if the time the Python function took was greater than max_time, warn that it's taking too long
time = (end.tv_sec - start.tv_sec) * 1e9 + (end.tv_nsec - start.tv_nsec);
if (timeout != NULL && time > max_time) {
log_printf(WARN, "Function %s is taking longer than %lu milliseconds, indicating a loop or sleep in the code. You probably forgot to put a Robot.sleep call into a robot action instead of a regular function.", func_name, (long) (max_time / 1e6));
}
// if the time the Python function took was less than min_time, sleep to slow down execution
if (time < min_time) {
usleep((min_time - time) / 1000); // Need to convert nanoseconds to microseconds
}

// Set return value
if (py_ret != NULL) {
Py_XDECREF(*py_ret); // Decrement previous reference, if it exists
*py_ret = pValue;
// catch execution error
if (pValue == NULL) {
if (!PyErr_ExceptionMatches(PyExc_TimeoutError)) {
PyErr_Print();
log_printf(ERROR, "Python function %s call failed", func_name);
ret = 2;
} else {
Py_XDECREF(pValue);
ret = 3; // Timed out by parent process
}

// catch execution error
if (pValue == NULL) {
} else if (mode == AUTO || mode == TELEOP) {
// Need to check if error occurred in action thread
PyObject* event = PyObject_GetAttrString(pRobot, "error_event");
if (event == NULL) {
PyErr_Print();
log_printf(ERROR, "Could not get error_event from Robot instance");
exit(2);
}
PyObject* event_set = PyObject_CallMethod(event, "is_set", NULL);
if (event_set == NULL) {
if (!PyErr_ExceptionMatches(PyExc_TimeoutError)) {
PyErr_Print();
log_printf(ERROR, "Python function %s call failed", func_name);
ret = 2;
log_printf(DEBUG, "Could not get if error is set from error_event");
exit(2);
} else {
ret = 3; // Timed out by parent process
}
break;
} else if (mode == AUTO || mode == TELEOP) {
// Need to check if error occurred in action thread
PyObject* event = PyObject_GetAttrString(pRobot, "error_event");
if (event == NULL) {
PyErr_Print();
log_printf(ERROR, "Could not get error_event from Robot instance");
exit(2);
}
PyObject* event_set = PyObject_CallMethod(event, "is_set", NULL);
if (event_set == NULL) {
if (!PyErr_ExceptionMatches(PyExc_TimeoutError)) {
PyErr_Print();
log_printf(DEBUG, "Could not get if error is set from error_event");
exit(2);
} else {
ret = 3; // Timed out by parent process
}
break;
} else if (PyObject_IsTrue(event_set) == 1) {
log_printf(ERROR, "Stopping %s due to error in action", func_name);
ret = 1;
break;
}
} else if (PyObject_IsTrue(event_set) == 1) {
log_printf(ERROR, "Stopping %s due to error in action", func_name);
ret = 1;
}
} while (loop);
}

Py_DECREF(pFunc);
} else {
if (PyErr_Occurred()) {
Expand All @@ -278,32 +257,6 @@ static uint8_t run_py_function(const char* func_name, struct timespec* timeout,
}


/**
* Begins the given game mode and calls setup and main appropriately. Will run main forever.
*
* Behavior: This is a blocking function and will block the calling thread forever.
* This should only be run as a separate thread.
*
* Inputs:
* args: string of the mode to start running
*/
static void run_mode(robot_desc_val_t mode) {
// Set up the arguments to the threads that will run the setup and main threads
char* mode_str = get_mode_str(mode);
char setup_str[20], main_str[20];
sprintf(setup_str, "%s_setup", mode_str);
sprintf(main_str, "%s_main", mode_str);

int err = run_py_function(setup_str, &setup_time, 0, NULL, NULL); // Run setup function once
if (err == 0) {
err = run_py_function(main_str, &main_interval, 1, NULL, NULL); // Run main function on loop
} else {
log_printf(WARN, "Won't run %s due to error %d in %s", main_str, err, setup_str);
}
return;
}


/**
* Handler for killing the child mode subprocess
*/
Expand Down Expand Up @@ -361,7 +314,12 @@ static pid_t start_mode_subprocess(char* student_code) {
signal(SIGINT, SIG_IGN); // Disable Ctrl+C for child process
executor_init(student_code);
signal(SIGTERM, python_exit_handler); // Set handler for killing subprocess
run_mode(mode);

char* mode_str = get_mode_str(mode);
int err = run_py_function(mode_str, &main_interval, NULL, NULL); // Run main function
if (err) {
log_printf(WARN, "NEED TO EDIT STATEMENT"); // "Problem Child"
}
exit(0);
return pid; // Never reach this statement due to exit, needed to fix compiler warning
} else {
Expand Down
15 changes: 4 additions & 11 deletions executor/studentcode.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
def autonomous():
print("Autonomous has begun!")

def autonomous_setup():
print('Autonomous setup has begun!')

def autonomous_main():
pass

def teleop_setup():
print('Teleop setup has begun!')

def teleop_main():
pass
def teleop():
print("Teleop has begun!")
6 changes: 3 additions & 3 deletions systemd/runtime_update.service
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ Description=Update process for Runtime

[Service]
Type=oneshot
User=pi
WorkingDirectory=/home/pi/runtime
ExecStart=/home/pi/runtime/scripts/update.sh
User=ubuntu
WorkingDirectory=/home/ubuntu/runtime
ExecStart=/home/ubuntu/runtime/scripts/update.sh
KillSignal=SIGINT

[Install]
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/tc_71_2.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

char check_output_6[] =
" File \"/home/runner/work/runtime/runtime/tests/student_code/executor_sanity.py\", line 25, in teleop_main\n"
" File \"/home/runner/work/runtime/runtime/tests/student_code/executor_sanity.py\", line 19, in teleop\n"
" oops = 1 / 0\n"
" ~~^~~\n"
"ZeroDivisionError: division by zero\n";
Expand Down Expand Up @@ -37,7 +37,7 @@ int main() {
send_run_mode(SHEPHERD, TELEOP);
add_ordered_string_output("Traceback (most recent call last):\n");
add_ordered_string_output(check_output_6);
add_ordered_string_output("Python function teleop_main call failed\n");
add_ordered_string_output("Python function teleop call failed\n");
check_run_mode(TELEOP);

send_run_mode(DAWN, IDLE);
Expand Down
10 changes: 2 additions & 8 deletions tests/student_code/executor_sanity.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,12 @@ def constant_print(msg):
print(f"{msg} printing again")
time.sleep(2)

def autonomous_setup():
def autonomous():
print('Autonomous setup has begun!')
print(f"Starting position: {Robot.start_pos}")
Robot.run(constant_print, "autonomous")

def autonomous_main():
pass

# This teleop code generates a DivisionByZero Python error

def teleop_setup():
pass

def teleop_main():
def teleop():
oops = 1 / 0
21 changes: 8 additions & 13 deletions tests/student_code/keyboard_input.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@

simple_device = "62_20"

def autonomous_setup():
def autonomous():
pass

def autonomous_main():
pass

def teleop_setup():
pass

def teleop_main():
if Keyboard.get_value('a'):
Robot.set_value(simple_device, "MY_INT", 123454321)
elif Keyboard.get_value('y'):
print(Robot.get_value(simple_device, "MY_INT"))
Robot.sleep(.45)
def teleop():
while True:
if Keyboard.get_value('a'):
Robot.set_value(simple_device, "MY_INT", 123454321)
elif Keyboard.get_value('y'):
print(Robot.get_value(simple_device, "MY_INT"))
Robot.sleep(.45)
11 changes: 5 additions & 6 deletions tests/student_code/net_handler_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,12 @@ def modify_my_int():
Robot.set_value(SIMPLE_DEV, "MY_INT", Robot.get_value(SIMPLE_DEV, "MY_INT") - 1)
Robot.sleep(1)

def teleop_setup():
def teleop():
print("Teleop setup has begun!")
Robot.run(print_if_button_a)
Robot.run(modify_my_int)

def teleop_main():
global i
if i < 3 and Gamepad.get_value('joystick_left_x') != 0.0:
print("Left joystick moved in x direction!")
i += 1
while True:
if i < 3 and Gamepad.get_value('joystick_left_x') != 0.0:
print("Left joystick moved in x direction!")
i += 1
15 changes: 5 additions & 10 deletions tests/student_code/runtime_latency.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,10 @@

time_dev = '60_123'

def autonomous_setup():
def autonomous():
pass

def autonomous_main():
pass

def teleop_setup():
pass

def teleop_main():
if Gamepad.get_value('button_a'):
Robot.set_value(time_dev, "GET_TIME", True)
def teleop():
while True:
if Gamepad.get_value('button_a'):
Robot.set_value(time_dev, "GET_TIME", True)
14 changes: 5 additions & 9 deletions tests/student_code/sanity_write.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,14 @@ def constant_write():
Robot.set_value(GeneralTestDevice, "RED_INT", int_val)
Robot.set_value(GeneralTestDevice, "ORANGE_FLOAT", float_val)
Robot.set_value(GeneralTestDevice, "YELLOW_BOOL", bool_val)
int_val += 2;
int_val += 2
float_val += 3.14
bool_val = not bool_val

def autonomous_setup():
def autonomous():
Robot.run(constant_write)
while True:
pass

def autonomous_main():
pass

def teleop_setup():
pass

def teleop_main():
def teleop():
pass
44 changes: 20 additions & 24 deletions tests/student_code/sound_device.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Student code that plays pitches from keyboard inputs
# Commented out print_buttons and play_notes functions as they are not run
import time

SOUND = '59_1'
Expand All @@ -23,8 +24,8 @@

################################## AUTONOMOUS ##################################

def autonomous_setup():
print("Now executing AUTONOMOUS SETUP")
def autonomous():
print("Now executing AUTONOMOUS")
# Write pitches
for note in NOTES:
if (note == ' '):
Expand All @@ -35,35 +36,30 @@ def autonomous_setup():
Robot.set_value(SOUND, "PITCH", MAP[note])
time.sleep(NOTE_DURATION)

def autonomous_main():
pass

#################################### TELEOP ####################################

def teleop_setup():
print("Now executing TELEOP SETUP")
def teleop():
print("Now executing TELEOP")
# Robot.run(print_button)
# Robot.run(play_notes)
pass

def teleop_main():
if Gamepad.get_value('button_a'):
Robot.set_value(SOUND, "PITCH", MAP['C'])
print("Wrote Button A: Pitch C")
time.sleep(NOTE_DURATION);
if Gamepad.get_value('button_b'):
Robot.set_value(SOUND, "PITCH", MAP['B'])
print("Wrote Button B: Pitch B")
time.sleep(NOTE_DURATION);
while True:
if Gamepad.get_value('button_a'):
Robot.set_value(SOUND, "PITCH", MAP['C'])
print("Wrote Button A: Pitch C")
time.sleep(NOTE_DURATION);
if Gamepad.get_value('button_b'):
Robot.set_value(SOUND, "PITCH", MAP['B'])
print("Wrote Button B: Pitch B")
time.sleep(NOTE_DURATION);

################################### THREADS ####################################

def print_button():
while (1):
if Gamepad.get_value('button_a'):
print("BUTTON A IS PRESSED")
if Gamepad.get_value('button_b'):
print("BUTTON B IS PRESSED")
# def print_button():
# while (1):
# if Gamepad.get_value('button_a'):
# print("BUTTON A IS PRESSED")
# if Gamepad.get_value('button_b'):
# print("BUTTON B IS PRESSED")

# def play_notes():
# while (1):
Expand Down
Loading