Add ability to end startCompetition() main loop (#2032)

This is useful for both cleanly exiting from simulation and for unit testing
at a framework level.

This change required removing move constructor/assignment from IterativeRobot.
This commit is contained in:
Peter Johnson
2019-11-05 21:33:09 -08:00
committed by GitHub
parent f5b4be16db
commit 7508aada93
14 changed files with 185 additions and 35 deletions

View File

@@ -394,6 +394,11 @@ class DriverStation : public ErrorBase {
*/
void InTest(bool entering) { m_userInTest = entering; }
/**
* Forces WaitForData() to return immediately.
*/
void WakeupWaitForData();
protected:
/**
* Copy data from the DS task for the user.

View File

@@ -1,5 +1,5 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2008-2018 FIRST. All Rights Reserved. */
/* Copyright (c) 2008-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
@@ -7,6 +7,8 @@
#pragma once
#include <atomic>
#include "frc/IterativeRobotBase.h"
namespace frc {
@@ -28,9 +30,6 @@ class IterativeRobot : public IterativeRobotBase {
IterativeRobot();
virtual ~IterativeRobot() = default;
IterativeRobot(IterativeRobot&&) = default;
IterativeRobot& operator=(IterativeRobot&&) = default;
/**
* Provide an alternate "main loop" via StartCompetition().
*
@@ -38,6 +37,14 @@ class IterativeRobot : public IterativeRobotBase {
* with the DS packets.
*/
void StartCompetition() override;
/**
* Ends the main loop in StartCompetition().
*/
void EndCompetition() override;
private:
std::atomic<bool> m_exit{false};
};
} // namespace frc

View File

@@ -7,9 +7,12 @@
#pragma once
#include <chrono>
#include <thread>
#include <hal/Main.h>
#include <wpi/condition_variable.h>
#include <wpi/mutex.h>
#include <wpi/raw_ostream.h>
#include "frc/Base.h"
@@ -23,9 +26,13 @@ int RunHALInitialization();
namespace impl {
template <class Robot>
void RunRobot() {
static Robot robot;
robot.StartCompetition();
void RunRobot(wpi::mutex& m, Robot** robot) {
static Robot theRobot;
{
std::scoped_lock lock{m};
*robot = &theRobot;
}
theRobot.StartCompetition();
}
} // namespace impl
@@ -36,20 +43,50 @@ int StartRobot() {
if (halInit != 0) {
return halInit;
}
static wpi::mutex m;
static wpi::condition_variable cv;
static Robot* robot = nullptr;
static bool exited = false;
if (HAL_HasMain()) {
std::thread([] {
std::thread thr([] {
try {
impl::RunRobot<Robot>();
impl::RunRobot<Robot>(m, &robot);
} catch (...) {
HAL_ExitMain();
{
std::scoped_lock lock{m};
robot = nullptr;
exited = true;
}
cv.notify_all();
throw;
}
HAL_ExitMain();
})
.detach();
{
std::scoped_lock lock{m};
robot = nullptr;
exited = true;
}
cv.notify_all();
});
HAL_RunMain();
// signal loop to exit
if (robot) robot->EndCompetition();
// prefer to join, but detach to exit if it doesn't exit in a timely manner
using namespace std::chrono_literals;
std::unique_lock lock{m};
if (cv.wait_for(lock, 1s, [] { return exited; }))
thr.join();
else
thr.detach();
} else {
impl::RunRobot<Robot>();
impl::RunRobot<Robot>(m, &robot);
}
return 0;
@@ -127,6 +164,8 @@ class RobotBase {
virtual void StartCompetition() = 0;
virtual void EndCompetition() = 0;
static constexpr bool IsReal() {
#ifdef __FRC_ROBORIO__
return true;

View File

@@ -34,6 +34,11 @@ class TimedRobot : public IterativeRobotBase, public ErrorBase {
*/
void StartCompetition() override;
/**
* Ends the main loop in StartCompetition().
*/
void EndCompetition() override;
/**
* Get the time period between calls to Periodic() functions.
*/