Competition code and user created tasks

We’ve had some feedback recently from teams who have had issues with competition control. I’ve looked at some of the team code and noticed that often problems are being caused by tasks started in either the driver or autonomous section of the program being allowed to run during the other phase of the match. This can occur with programs written in either VEXcode or PROS (and I assume RobotMesh, but I’m not planning to specifically discuss that programming environment in this topic) although there are differences that are specific to each type of program.

TL;DR
If you create any new tasks at the beginning of autonomous or driver control that may interfere with the operation of the robot, those tasks should be disabled/stopped during the other phase of the match,

First a little background.
Competition control on the V5 is relatively simple. All competition control information is collected by the V5 controller either through the legacy competition control port on the back, from the match simulation controls built into the controller or, most recently, through the USB port when connected to a live remote event. The competition status only consists of four pieces of information.

  1. whether competition control is connected (or in the case of USB, is enabled)
  2. the type of competition control, a competition switch or a field controller.
  3. The current phase of the match, driver or autonomous.
  4. Whether the robot should be disabled or enabled.

This status is sent to the V5 brain in every message that the controller sends. The V5 brain, as we have discussed before, has two cpus that run vexos and user programs. The competition status is made available to both.

The cpu that runs vexos uses the status to do things such as disabling the motors and controller when the robot should be disabled and logging the different match phases. The cpu that runs user code is responsible for using the status to start and stop the driver and autonomous parts of your program. In the case of VEXcode, this is all part of the system code that’s included with vexos. An event system is monitoring the competition status and sending events to the VEXcode competition class. When using PROS, however, the competition status is monitored by PROS directly in the system daemon and tasks are created and deleted as necessary.

I’ll now focus on VEXcode and cover PROS in the next post.

VEXcode provides a competition class, it’s used to start one of two user provided tasks (threads and tasks are the same as far as this discussion is concerned). The default names for these are “autonomous” and “usercontrol” but any valid function can be registered with the competition class if you want to call them something else. The competition class will only allow one of these two tasks to run at any one time, it disables both when the robot is disabled. However, any tasks that your code creates in main, pre_auton or at the beginning of these tasks are not under control of the competition class and will continue to run as the competition control status changes. This may be desired behavior, I often use a separate task in my code that constantly displays the status of motors and sensors on the brain screen. I would want this task to run during the entire match, there’s no need to stop it. However, tasks running PID loops or other code that directly controls motors should only be running when needed.

One important thing to understand is that there is a good chance that your driver control code will have been called even before the autonomous phase of the match. Whether this happens will depend on when the user program is started and when the controller is connected to field control or the live remote system. If you run the program first and subsequently connect to field control, the driver code will have been started and the robot disabled only when field control is connected. Reversing the order, connect to the field first and then start the user program, will generally avoid this from happening. It’s also important to understand that event handlers (for example, the function that gets called when you register using pressed and release calls on buttons) should only ever be registered once, if you register them at the beginning of driver control additional events will be added if driver control is started more than once. So how do we deal with all of this, a couple of options exist.

  1. set the Competition.bStopAllTasksBetweenModes flag
    Competition.bStopAllTasksBetweenModes = true;

This tells the competition class to stop all tasks when the robot becomes disabled and also removes all event handlers. It effectively tries to reset the program back to the state it was when main was entered. The downside to this is that, as it says, all tasks will be stopped, even those you may wish to keep running. I’ll post a demo using this flag later on.

  1. Keep track of any tasks you start and make sure they are stopped between competition phases. Register any events in main or pre_auton rather than in the auton or driver tasks.

The easiest way of doing this is using global task (or thread) instances that are then used to stop tasks when required. As the competition class does not provide a callback when entering the disabled state you would need to add additional code to be able to do this, there are a couple of options.

  1. poll the IsEnabled() flag
bool bWasEnabled = false;
while (true) {
  bool bEnabled = Competition.isEnabled();

  // If you want to kill tasks when going disabled.
  // do it here.
  if( !bEnabled && bWasEnabled ) {
    killTasks();
  }

  // save enabled state
  bWasEnabled = bEnabled;

  this_thread::sleep_for(20);
}
  1. use an mevent
while (true) {
  if( Competition.DISABLED ) {
    killTasks();        
  }

  this_thread::sleep_for(20);
}

and what on earth is an “mevent” you ask ?

ok, quick digression.

A mevent is a little discussed feature of VEXcode that is part way between polling (ie. checking something periodically in a loop) and a full event handler where a callback (a fancy name for a function called by some system code) is registered. An mevent is something that can be checked occasionally in a loop but remembers if something has happened since the last time it was checked. mevents are available on everything (I think) where a normal event handler can be used, for example, controller buttons all have mevents in the form of

Controller.ButtonL1.PRESSED or Controller.ButtonL1.RELEASED

and bonus points to anyone who can tell me why they are named mevent.

anyway, lots of words, perhaps easiest thing is to look at examples.

I’ll add those in the next couple of posts

22 Likes

The first example is using the bStopAllTasksBetweenModes flag.

Advantages of this approach are primarily its simplicity, set the flag and all tasks and events created after main is started will be stopped or removed every time that the disabled state is entered. The disadvantage is pretty much the same, that everything is stopped and events are reset, that means that tasks and/or events that you want to be permanent will need to be restarted at the beginning of both driver and autonomous periods of the match. The exception to this would be tasks or events that were created by the constructor of global class instances, that is, if you have created a C++ class that creates tasks or events in its constructor then, as they are created before main is entered, they would persist.

The example code create two additional tasks in both autonomous and driver control periods, these tasks are stopped when the robot enters the disabled state.

demo code
/*----------------------------------------------------------------------------*/
/*                                                                            */
/*    Module:       main.cpp                                                  */
/*    Author:       James                                                     */
/*    Created:      Sat Feb 13 2021                                           */
/*    Description:  Competition Demo code                                     */
/*                                                                            */
/*----------------------------------------------------------------------------*/

// ---- START VEXCODE CONFIGURED DEVICES ----
// ---- END VEXCODE CONFIGURED DEVICES ----

#include "vex.h"

using namespace vex;

// A global instance of competition
competition Competition;

// define your global instances of motors and other devices here
vex::thread    op1;
vex::thread    op2;

vex::thread    at1;
vex::thread    at2;

vex::motor     motor1( PORT1 );

void pre_auton(void) {
    vexcodeInit();
}


/*----------------------------------------------------------------------------*/
// tasks started by autonomous code.
// two additional tasks doing who knows what
//
int autonTask1() {
    int count = 0;

    while(true) {
      Brain.Screen.printAt(10, 60, "Auton 1 %d", count++ );
      this_thread::sleep_for(100);
    }

    return(0);
}
int autonTask2() {
    int count = 0;

    while(true) {
      Brain.Screen.printAt(10, 80, "Auton 2 %d", count++ );
      this_thread::sleep_for(100);
    }

    return(0);
}

/*----------------------------------------------------------------------------*/
// autonomous control callback
// this task will be started/restarted every time the competition state changes
// to the autonomous period of the match.  The program can be in either the
// disabled or driver control state before this code is entered.
//
void autonomous(void) {
    static int count = 0;
    static int call_count = 1;
    Brain.Screen.printAt(10, 100, "Auton called %d", call_count++ );

    at1 = thread( autonTask1 );
    at2 = thread( autonTask2 );

    while(1) {
      Brain.Screen.printAt(10, 40, "Auton main task %d", count++ );
      motor1.spin( forward, 25, percent );
      this_thread::sleep_for(1000);
      motor1.stop();
      this_thread::sleep_for(1000);
    }
}

/*----------------------------------------------------------------------------*/
// tasks started by driver control.
// two additional tasks doing who knows what
//
int driverTask1() {
    int count = 0;

    while(true) {
      Brain.Screen.printAt(250, 60, "Driver 1 %d", count++ );
      this_thread::sleep_for(100);
    }

    return(0);
}
int driverTask2() {
    int count = 0;

    while(true) {
      Brain.Screen.printAt(250, 80, "Driver 2 %d", count++ );
      this_thread::sleep_for(100);
    }

    return(0);
}

/*----------------------------------------------------------------------------*/
// driver control callback
// this task will be started/restarted every time the competition state changes
// to the driver control period of the match.  The program can be in either the
// disabled or autonomous state before this code is entered.
// This function will also be called if the program is started (with a controller
// connected) without field control (or a comp switch) connected.
//
void usercontrol(void) {
    static int count = 0;
    static int call_count = 1;
    Brain.Screen.printAt(250, 100, "Driver call count %d", call_count++ );

    op1 = thread( driverTask1 );
    op2 = thread( driverTask2 );

    while (1) {
      Brain.Screen.printAt(250, 40, "Driver main task %d", count++ );
      // some drive code
      this_thread::sleep_for(500);
    }
}


/*----------------------------------------------------------------------------*/
// program entry
// programs always start here
//
int main() {
    Brain.Screen.printAt(10, 20, "Competition test code");
    // Set up callbacks for autonomous and driver control periods.
    // technically this is optional if you have used the default names of
    // autonomous and usercontrol as the competition class code will do it
    // automatically, however, it's best to keep this in case the functio
    // names are ever changed.
    Competition.autonomous(autonomous);
    Competition.drivercontrol(usercontrol);

    // this will stop tasks and remove events when
    // going into disabled state
    // without this flag, only auton or driver will be disabled
    // all other events and tasks will still run which may be desirable
    // if you want to create them in pre_auto.  Essentially this flag
    // tries to reset the program state to what it was when main was entered.
    Competition.bStopAllTasksBetweenModes = true;

    // Run the pre-autonomous function.
    // pre_auton is not a special function, any code can be executed
    // here inline or by calling one or more other functions.
    // pre-auton simply means code that is run before the match starts.
    pre_auton();

    // Prevent main from exiting with an infinite loop.
    while (true) {
      this_thread::sleep_for(20);
    }
}
9 Likes

The second example shows an alternative approach that manually stops the tasks in each of the competition phases. The advantage here is more control over which tasks are stopped. The disadvantage is that the program has to more carefully decide where events are registered and slightly more complex code.

This demo code is using the thread class, both thread and task classes are very similar and either could be used. Internally they both work in the same way and use the same underlying resources within vexos. The task class was modeled after RobotC tasks, thread was made to be similar to std::thread.

demo code
/*----------------------------------------------------------------------------*/
/*                                                                            */
/*    Module:       main.cpp                                                  */
/*    Author:       James                                                     */
/*    Created:      Sat Feb 13 2021                                           */
/*    Description:  Competition Demo code                                     */
/*                                                                            */
/*----------------------------------------------------------------------------*/

// ---- START VEXCODE CONFIGURED DEVICES ----
// ---- END VEXCODE CONFIGURED DEVICES ----

#include "vex.h"

using namespace vex;

// A global instance of competition
competition Competition;

// define your global instances of motors and other devices here
vex::thread    op1;
vex::thread    op2;

vex::thread    at1;
vex::thread    at2;

vex::motor     motor1( PORT1 );

void pre_auton(void) {
    vexcodeInit();
}

/*----------------------------------------------------------------------------*/
// stop all additional threads (tasks) that can be started by driver or
// autonomous code
// thread.interrupt() performs the same action as task.stop(), so if a task
// was started using
//   vex::task t1 = vex::task( callback );
// use
//   t1.stop();
//
// It's ok to stop instances of tasks that have never been assigned a callback.
// It's ok to stop instances of tasks that may not be currently running.
//
void
killTasks() {
    op1.interrupt();
    op2.interrupt();
    at1.interrupt();
    at2.interrupt();
}

/*----------------------------------------------------------------------------*/
// tasks started by autonomous code.
// two additional tasks doing who knows what
//
int autonTask1() {
    int count = 0;

    while(true) {
      Brain.Screen.printAt(10, 60, "Auton 1 %d", count++ );
      this_thread::sleep_for(100);
    }

    return(0);
}
int autonTask2() {
    int count = 0;

    while(true) {
      Brain.Screen.printAt(10, 80, "Auton 2 %d", count++ );
      this_thread::sleep_for(100);
    }

    return(0);
}

/*----------------------------------------------------------------------------*/
// autonomous control callback
// this task will be started/restarted every time the competition state changes
// to the autonomous period of the match.  The program can be in either the
// disabled or driver control state before this code is entered.
//
void autonomous(void) {
    static int count = 0;
    static int call_count = 1;
    Brain.Screen.printAt(10, 100, "Auton called %d", call_count++ );

    killTasks();

    at1 = thread( autonTask1 );
    at2 = thread( autonTask2 );

    while(1) {
      Brain.Screen.printAt(10, 40, "Auton main task %d", count++ );
      motor1.spin( forward, 25, percent );
      this_thread::sleep_for(1000);
      motor1.stop();
      this_thread::sleep_for(1000);
    }
}

/*----------------------------------------------------------------------------*/
// tasks started by driver control.
// two additional tasks doing who knows what
//
int driverTask1() {
    int count = 0;

    while(true) {
      Brain.Screen.printAt(250, 60, "Driver 1 %d", count++ );
      this_thread::sleep_for(100);
    }

    return(0);
}
int driverTask2() {
    int count = 0;

    while(true) {
      Brain.Screen.printAt(250, 80, "Driver 2 %d", count++ );
      this_thread::sleep_for(100);
    }

    return(0);
}

/*----------------------------------------------------------------------------*/
// driver control callback
// this task will be started/restarted every time the competition state changes
// to the driver control period of the match.  The program can be in either the
// disabled or autonomous state before this code is entered.
// This function will also be called if the program is started (with a controller
// connected) without field control (or a comp switch) connected.
//
void usercontrol(void) {
    static int count = 0;
    static int call_count = 1;
    Brain.Screen.printAt(250, 100, "Driver call count %d", call_count++ );

    killTasks();

    op1 = thread( driverTask1 );
    op2 = thread( driverTask2 );

    while (1) {
      Brain.Screen.printAt(250, 40, "Driver main task %d", count++ );
      // some drive code
      this_thread::sleep_for(500);
    }
}

/*----------------------------------------------------------------------------*/
// program entry
// programs always start here
//
int main() {
    Brain.Screen.printAt(10, 20, "Competition test code");
    // Set up callbacks for autonomous and driver control periods.
    // technically this is optional if you have used the default names of
    // autonomous and usercontrol as the competition class code will do it
    // automatically, however, it's best to keep this in case the functio
    // names are ever changed.
    Competition.autonomous(autonomous);
    Competition.drivercontrol(usercontrol);

    // Run the pre-autonomous function.
    // pre_auton is not a special function, any code can be executed
    // here inline or by calling one or more other functions.
    // pre-auton simply means code that is run before the match starts.
    pre_auton();

    // monitor disabled state
    // The VEXcode competition class does not have a callback for the disabled state
    // however, it's relativly easy to just monitor the enabled state here and perform
    // any actions when it changes to disabled.
    bool bWasEnabled = false;
    while (true) {
      bool bEnabled = Competition.isEnabled();

      // If you want to kill tasks when going disabled.
      // do it here.
      if( !bEnabled && bWasEnabled ) {
        killTasks();
      }

      // save enabled state
      bWasEnabled = bEnabled;

      this_thread::sleep_for(20);
    }
}
8 Likes

Finally an example in PROS.

PROS works a little differently from VEXcode. There are five possible tasks that are automatically managed by PROS that are related to competition code.

  1. initialize
  2. competition initialize
  3. autonomous
  4. operator control
  5. disabled.

The initialize task is called as soon as the PROS program is executed, the system daemon will block until the initialize task finishes (which is why it behaves as a blocking function), it will only be ever called once for every program run.

The other four tasks are mutually exclusive, only one of the four can be running at any one time.

competition initialize should only ever be called once for any program run (although there’s a chance it may happen more than once). It is triggered by the PROS system daemon detecting that the field controller (or competition switch) is available. It will be triggered when the program runs if field control is already available otherwise it will be triggered by plugging in the field control cable.

The other three tasks are self explanatory, they will be created for for the different phases of the match.

Despite these differences, the issues that a PROS program may have are similar to a VEXcode program. Tasks created by the operator control or autonomous tasks will be persistent if user code does not disable them. In addition, unlike VEXcode that will simply restart a task that is created more than once, multiple copies of a PROS task can be created if the task creation function is called multiple times.

The same approach needs to be applied to a PROS program as a VEXcode program. Tasks that should not be running during different phases of a match need to be explicitly removed.

Here’s a demo program that demonstrates one approach to achieving this.

demo code
/*----------------------------------------------------------------------------*/
/*                                                                            */
/*    Module:     main.cpp                                                    */
/*    Author:     James Pearman                                               */
/*    Created:    29 Jan 2021                                                 */
/*                                                                            */
/*    Revisions:  V0.1                                                        */
/*                                                                            */
/*----------------------------------------------------------------------------*/
//
// Demo code to show how PROS tasks can/should be deleted when changing
// competition modes.
//
// Licensed under the MIT license. 
//
#include "main.h"

pros::task_t op1 = (pros::task_t)NULL;
pros::task_t op2 = (pros::task_t)NULL;

pros::task_t at1 = (pros::task_t)NULL;
pros::task_t at2 = (pros::task_t)NULL;

pros::Motor motor_1 (1, pros::E_MOTOR_GEARSET_18, false, pros::E_MOTOR_ENCODER_DEGREES);

/*----------------------------------------------------------------------------*/
// task we keep running all the time to show the number of active rtos tasks
//
void display_task() {
    while(1) {
        pros::lcd::print(0, "Task Count %3d", pros::Task::get_count() );
        pros::delay(100);
    }
}

/*----------------------------------------------------------------------------*/
// called one time when the code first runs
//
void initialize() {
    pros::lcd::initialize();
    pros::lcd::set_text(7, "Competition test code");

    pros::Task d1(display_task);
}

/*----------------------------------------------------------------------------*/
//
// kill tasks, we have to use global C task pointers as there is no C++
// default constructor that lets us use global C++ task instances
// We test to see if the task is valid (check for NULL ptr) and remove
// it if necessary
//
void killTasks() {
    if( op1 ) {
        pros::Task(op1).remove();
        op1 = (pros::task_t)NULL;
    }
    if( op2 ) {
        pros::Task(op2).remove();
        op2 = (pros::task_t)NULL;
    }
    if( at1 ) {
        pros::Task(at1).remove();
        at1 = (pros::task_t)NULL;
    }
    if( at2 ) {
        pros::Task(at2).remove();
        at2 = (pros::task_t)NULL;
    }

    // small delay to allow tasks to be removed from run queue
    pros::delay(10);
}

/*----------------------------------------------------------------------------*/
// called when robot goes from driver or auton into disabled state
//
void disabled() {
    static int count = 1;
    pros::lcd::print(5, "Disabled called %d", count++ );

    // kill any tasks we may have started and do not need now
    killTasks();

    // disabled is actually a task as well
    // we can either return or block here doing something useful
    // the task will be deleted when driver or auton starts
    while(1) {
        pros::delay(1000);
    }
}

/*----------------------------------------------------------------------------*/
// called when comp cable is attached
//
void competition_initialize() {
    static int count = 1;
    pros::lcd::print(6, "Comp Init called %d", count++ );

    // if cable is removed and then attached we may need to
    killTasks();

    // competition_initialize is also a task, but in this demo we just return
}

/*----------------------------------------------------------------------------*/
// Autonomous code
// two additional tasks doing who knows what
//
void autonTask1() {
    int count = 0;

    while(true) {
        pros::lcd::print(1, "Auton 1 %d", count++ );
        pros::delay(100);
    }
}
void autonTask2() {
    int count = 0;

    while(true) {
        pros::lcd::print(2, "Auton 2 %d", count++ );
        pros::delay(20);
    }
}

void autonomous() {
    static int count = 1;
    pros::lcd::print(4, "Auton called %d", count++ );

    // make sure all tasks creted in driver are deleted
    // this is really only needed when going from driver to autonomous
    // directly without going through the disabled state 
    killTasks();

    // start some more tasks
    at1 = pros::Task( autonTask1 );
    at2 = pros::Task( autonTask2 );

    // turn on/off motor on port 1 for demo purposes
    while(1) {
        motor_1.move(25);
        pros::delay(1000);
        motor_1.move(0);
        pros::delay(1000);
    }
}

/*----------------------------------------------------------------------------*/
// Driver control code
// two additional tasks doing who knows what
//
void opcontrolTask1() {
    int count = 0;

    while(true) {
        pros::lcd::print(1, "Op1 %d", count++ );
        pros::delay(100);
    }
}
void opcontrolTask2() {
    int count = 0;

    while(true) {
        pros::lcd::print(2, "Op2 %d", count++ );
        pros::delay(20);
    }
}

void opcontrol() {
    static int count = 1;
    pros::lcd::print(3, "Driver called %d", count++ );

    // make sure all tasks creted in autonomous are deleted
    // this is really only needed when going from autonomous to driver
    // directly without going through the disabled state 
    killTasks();

    // start some more tasks
    op1 = pros::Task( opcontrolTask1 );
    op2 = pros::Task( opcontrolTask2 );
    
    while (true) {
        // some drive code
        pros::delay(500);
    }
}
13 Likes

Well, almost finally, I decided to also address VEXcode Python programs.

If you never create additional threads in your autonomous or driver control functions you can ignore the demo below.

If you do, then the same type of issues will arise as previously discussed, however, like PROS a Python function can be run in a Thread multiple times, so a little different from VEXcode C++.

There is an issue with the current Python VM and vexos 1.0.12. If you transition into auton or driver without going through the disabled state, the VM will probably crash. This only happens when additional threads have been created in either the driver control or autonomous functions, it’s fixed in vexos 1.0.13 which will probably be released by mid March.

The attached code does have a workaround for this issue, but it’s a bit complicated so perhaps DM me if you are using VEXcode Python for competition and have concerns.

demo code
#----------------------------------------------------------------------------
#
#    Module:       compDemo.py
#    Author:       James
#    Created:      Sat Feb 13 2021
#    Description:  Competition Demo code
#
#----------------------------------------------------------------------------

#----------------------------------------------------------------------------
#
# Important!, vexos 1.0.12 has an issue with Python when going directly from
# driver ro autonomous or vice verse.  This would not usually happen with match
# control as the disabled state is always selected between match phases.  It can
# happen if using the competition switch or the simulating match run on the
# controller
#
# This only impacts Python, not C++
#
# If you only use the default threads in Python is does not matter, it only
# applies if you are starting further threads in the auton or driver control
# code.
#
# vexos 1.0.13 will correct this issue.
#
# A workarounf is used in this example code, see the end.
#
from vex import *

brain = Brain()

op1 = None
op2 = None

at1 = None
at2 = None

motor1 = Motor(Ports.PORT1)

#----------------------------------------------------------------------------
# stop all additional threads that can be started by driver or
# autonomous code
def kill_tasks():
    global op1, op2, at1, at2

    if isinstance(op1, Thread):
        op1.stop()
        op1 = None

    if isinstance(op2, Thread):
        op2.stop()
        op2 = None

    if isinstance(at1, Thread):
        at1.stop()
        at1 = None

    if isinstance(at2, Thread):
        at2.stop()
        at2 = None

#----------------------------------------------------------------------------
# Threads started by autonomouasks code.
# two additional threads doing who knows what
#
def auton_task1():
    count = 0

    while True:
        brain.screen.print_at("Auton 1 %5d" % count, x=10, y=60)
        count += 1
        sleep(100)

def auton_task2():
    count = 0

    while True:
        brain.screen.print_at("Auton 2 %5d" % count, x=10, y=80)
        count += 1
        sleep(100)

#----------------------------------------------------------------------------
# autonomous control Thread
# this Thread will be started/restarted every time the competition state changes
# to the autonomous period of the match.  The program can be in either the
# disabled or driver control (see note at beginning) state before this code is entered.
#
auton_call_count = 1
def autonomous():
    global auton_call_count, at1, at2

    count = 0
    brain.screen.print_at("Auton called ", auton_call_count, x=10, y=100)
    auton_call_count += 1

    kill_tasks()

    at1 = Thread(auton_task1)
    at2 = Thread(auton_task2)

    while True:
        brain.screen.print_at("Auton main task %5d" % count, x=10, y=40)
        count += 1
        motor1.spin(FORWARD, 25, PERCENT)
        sleep(1000)
        motor1.stop()
        sleep(1000)

#----------------------------------------------------------------------------
# Threads started by driver control.
# two additional tasks doing who knows what
#
def driver_task1():
    count = 0

    while True:
        brain.screen.print_at("Driver 1 %5d " % count, x=250, y=60)
        count += 1
        sleep(100)

def driver_task2():
    count = 0

    while True:
        brain.screen.print_at("Driver 2 %5d" % count, x=250, y=80)
        count += 1
        sleep(100)

#----------------------------------------------------------------------------
# driver control Thread
# this task will be started/restarted every time the competition state changes
# to the driver control period of the match.  The program can be in either the
# disabled or autonomous (see note at beginning) state before this code is entered.
# This function will also be called if the program is started (with a controller
# connected) without field control (or a comp switch) connected.
#
driver_call_count = 1
def usercontrol():
    global driver_call_count, op1, op2

    count = 0
    brain.screen.print_at("Driver called %5d" % driver_call_count, x=250, y=100)
    driver_call_count += 1

    kill_tasks()

    op1 = Thread(driver_task1)
    op2 = Thread(driver_task2)

    while True:
        brain.screen.print_at("Driver main task ", count, x=250, y=40)
        count += 1
        sleep(500)



#----------------------------------------------------------------------------
# for vexos 1.0.13 we can just check for disabled state
#
def comp_thread_vexos13():
    competition = Competition(usercontrol, autonomous)

    last_enabled = False
    while True:
        if not competition.is_enabled() and last_enabled:
            kill_tasks()

        last_enabled = competition.is_enabled()
        sleep(20)

#----------------------------------------------------------------------------
# for vexos 1.0.12 we need to implement our own comp control that stops
# tasks in the correct order when transitioning driver to autonomous or vice verse.
#
def comp_thread_vexos12():
    auton_thread = None
    driver_thread = None

    last_enabled = False
    last_mode = 'none'

    def disable_all():
        nonlocal last_mode, auton_thread, driver_thread

        kill_tasks()

        last_mode = 'disabled'
        if isinstance(auton_thread, Thread):
            auton_thread.stop()
            auton_thread = None
        if isinstance(driver_thread, Thread):
            driver_thread.stop()
            driver_thread = None

    while True:
        # to disabled
        if not Competition.is_enabled() and last_enabled:
            print('disabled')
            disable_all()

        if last_mode != 'auton' and Competition.is_enabled() and Competition.is_autonomous():
            print('auton')
            disable_all()
            auton_thread = Thread(autonomous)
            last_mode = 'auton'

        if last_mode != 'driver' and Competition.is_enabled() and Competition.is_driver_control():
            print('driver')
            disable_all()
            driver_thread = Thread(usercontrol)
            last_mode = 'driver'

        last_enabled = Competition.is_enabled()
        sleep(20)

#----------------------------------------------------------------------------
# we can use print output of brain instance to test the vexos version
# brain prints data in the form
# Brain 01000D04 01000D2C
# The first hex string is the vexos version
#
brain_data = str(brain).split()

if brain_data and len(brain_data) is 3:
    # compare versions, ignore any beta version info in lsb
    if (int(brain_data[1], 16) & 0xFFFFFF00) > 0x01000C00:
        print('vexos 1.0.13')
        Thread(comp_thread_vexos13)
    else:
        print('vexos 1.0.12')
        Thread(comp_thread_vexos12)

brain.screen.print_at("Competition test code", x=10, y=20)
``

`

10 Likes

@jpearman Thanks for posting this! This should really help teams that are planning to participate in LRT but still having issues. Don’t forget to make sure that your time is synced, take a look at this post from Microsoft to learn more. Also, please log in and use the new “Solo Practice Mode” by logging in to remote.robotevents.com. This will let you enter auton and driver modes as many times as possible. Good luck teams!

Best,
Brad

6 Likes

(People please actually listen to Brad and James here. LRT’s won’t work correctly for you otherwise.)

4 Likes

@jpearman Can you talk about competition initialization in VEXcode programs. I had a team that wanted to initialize the IMU, but the pre_auton wasn’t getting called.

@jpearman can motor stop(hold) while disabled using vexcode??

If you are referring to this:

I think it is not programming environment dependent, but rather a vexos “feature”, that is if it still behaves that way.

3 Likes

I tested it ,there is not brake power anymore when disabled!! maybe the "feature " or bug is fixed!

We changed the behavior of the motor when the robot is disabled in June 2020. Motors are set to coast mode and stopped any time the robot is disabled by competition control.

6 Likes

Is there a change log for each release of the firmware?

1 Like

There’s the changelog on the VEX website, thought it’s not very descriptive in terms of what bugs were fixed and whatnot, not sure if there’s anything more in depth.

2 Likes

Internally we obviously have detailed change logs, most of the small changes we do not disclose, especially if a team thought there may be a competitive advantage to not upgrade to a newer version of vexos. But the major changes are shown on the firmware page that @LuxembourgIsACountry linked.

4 Likes

Hi , thread has joinable( ) to check whether the thread has run done , while task in vexcode does not !
robotc has getTaskState( ) to do that。
and task in vexcode only supports int xxx( void ) or int xxx( void * ) callback , and void xxx ( void ) is not supported !