mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-03 03:01:44 +00:00
[hal,wpilib] Add DS Display API (#8975)
This commit is contained in:
@@ -346,6 +346,14 @@ public class DriverStationJNI extends JNIWrapper {
|
||||
*/
|
||||
public static native boolean getOutputsActive();
|
||||
|
||||
/**
|
||||
* Writes ANSI text to the display.
|
||||
*
|
||||
* @param data the ANSI text to write
|
||||
* @see "HAL_WriteDisplayAnsi"
|
||||
*/
|
||||
public static native void writeDisplayAnsi(String data);
|
||||
|
||||
/** Utility class. */
|
||||
private DriverStationJNI() {}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
@@ -279,6 +280,10 @@ HAL_Bool HAL_GetSystemTimeValid(int32_t* status) {
|
||||
return systemTimeValid;
|
||||
}
|
||||
|
||||
void HAL_WriteDisplayAnsi(const struct WPI_String* data) {
|
||||
mrcLibDs->writeDisplayAnsi(data);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
namespace wpi::hal {
|
||||
|
||||
@@ -444,4 +444,19 @@ Java_org_wpilib_hardware_hal_DriverStationJNI_getOutputsActive
|
||||
{
|
||||
return HAL_GetOutputsEnabled();
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_wpilib_hardware_hal_DriverStationJNI
|
||||
* Method: writeDisplayAnsi
|
||||
* Signature: (Ljava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_wpilib_hardware_hal_DriverStationJNI_writeDisplayAnsi
|
||||
(JNIEnv* env, jclass, jstring data)
|
||||
{
|
||||
JStringRef dataStr{env, data};
|
||||
WPI_String dataWpiStr = dataStr.wpi_str();
|
||||
HAL_WriteDisplayAnsi(&dataWpiStr);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -242,6 +242,8 @@ class MrcLibDsImpl : public MrcLibDs {
|
||||
|
||||
int32_t getSystemTimeValid(bool* systemTimeValid) override;
|
||||
|
||||
int32_t writeDisplayAnsi(const struct WPI_String* line) override;
|
||||
|
||||
wpi::util::EventVector newDataEvents;
|
||||
|
||||
private:
|
||||
@@ -661,6 +663,11 @@ int32_t MrcLibDsImpl::getSystemTimeValid(bool* systemTimeValid) {
|
||||
return status;
|
||||
}
|
||||
|
||||
int32_t MrcLibDsImpl::writeDisplayAnsi(const struct WPI_String* line) {
|
||||
MRC_String mrcLine = WPIStringToMRCString(line);
|
||||
return MRC_DsCommsControl_WriteAnsi(&mrcLine);
|
||||
}
|
||||
|
||||
void MrcLibDsImpl::provideNewDataEventHandle(WPI_EventHandle handle) {
|
||||
newDataEvents.Add(handle);
|
||||
}
|
||||
|
||||
@@ -315,6 +315,13 @@ void HAL_ObserveUserProgramStarting(void);
|
||||
*/
|
||||
void HAL_ObserveUserProgram(HAL_ControlWord word);
|
||||
|
||||
/**
|
||||
* Writes ANSI text to the display.
|
||||
*
|
||||
* @param data the ANSI text to write
|
||||
*/
|
||||
void HAL_WriteDisplayAnsi(const struct WPI_String* data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@@ -97,6 +97,14 @@ class MrcLibDs {
|
||||
virtual int32_t observeUserProgram(HAL_ControlWord controlWord) = 0;
|
||||
|
||||
virtual int32_t getSystemTimeValid(bool* systemTimeValid) = 0;
|
||||
|
||||
/**
|
||||
* Writes ANSI text to the Driver Station display.
|
||||
*
|
||||
* @param line the ANSI text to write
|
||||
* @return 0 on success, non-zero on error
|
||||
*/
|
||||
virtual int32_t writeDisplayAnsi(const struct WPI_String* line) = 0;
|
||||
};
|
||||
|
||||
MrcLibDs* GetMrcLibDs();
|
||||
|
||||
@@ -160,6 +160,8 @@ class MrcLibDsSimImpl : public MrcLibDs {
|
||||
|
||||
int32_t getSystemTimeValid(bool* systemTimeValid) override;
|
||||
|
||||
int32_t writeDisplayAnsi(const struct WPI_String* line) override;
|
||||
|
||||
wpi::util::EventVector newDataEvents;
|
||||
|
||||
void NewDriverStationData();
|
||||
@@ -506,6 +508,10 @@ int32_t MrcLibDsSimImpl::getSystemTimeValid(bool* systemTimeValid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t MrcLibDsSimImpl::writeDisplayAnsi(const struct WPI_String* line) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MrcLibDsSimImpl::NewDriverStationData() {
|
||||
SimDriverStationData->dsAttached = true;
|
||||
cacheToUpdate->Update();
|
||||
|
||||
@@ -45,3 +45,4 @@ functions:
|
||||
HAL_SetOpModeOptions:
|
||||
HAL_ObserveUserProgram:
|
||||
HAL_GetGameData:
|
||||
HAL_WriteDisplayAnsi:
|
||||
|
||||
2
shared/bazel/thirdparty/integrity_helper.py
vendored
2
shared/bazel/thirdparty/integrity_helper.py
vendored
@@ -157,7 +157,7 @@ def update_libssh():
|
||||
|
||||
def update_mrclib():
|
||||
# Keep in sync with shared/libmrclib.gradle
|
||||
version = "2027.1.0-alpha-1-50-gd008523"
|
||||
version = "2027.1.0-alpha-1-65-g21f308e"
|
||||
|
||||
has_headers = True
|
||||
classifiers = [
|
||||
|
||||
@@ -2,17 +2,17 @@ http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "ht
|
||||
|
||||
################################################################################
|
||||
# Generated by shared/bazel/thirdparty/integrity_helper.py
|
||||
url_pattern = "https://frcmaven.wpi.edu/artifactory/development-2027/org/wpilib/mrclib/mrclib-cpp/2027.1.0-alpha-1-50-gd008523/mrclib-cpp-2027.1.0-alpha-1-50-gd008523-%s.zip"
|
||||
url_pattern = "https://frcmaven.wpi.edu/artifactory/development-2027/org/wpilib/mrclib/mrclib-cpp/2027.1.0-alpha-1-65-g21f308e/mrclib-cpp-2027.1.0-alpha-1-65-g21f308e-%s.zip"
|
||||
|
||||
headers_integrity = "c71011e2c593749aca585eec308f394d5e7f34451c1a14cdd8e077ea3b1368b5"
|
||||
headers_integrity = "a8b38a00d584b50c3bb84800121df1022ac1500f59e4ecac85068adca7c0ac49"
|
||||
|
||||
_LIB_ARTIFACTS = {
|
||||
"linuxarm64": ("linux", "**/*.so*", "4673c573c25d1d9a03f1cf3d73c1e8a121718291171213813b95b451eea53f3a"),
|
||||
"linuxx86-64": ("linux", "**/*.so*", "8d986ed0ffb03be1215a3cf88716c31da82848f4524e723ac89aee4eca45eb12"),
|
||||
"osxuniversal": ("osx", "**/*.dylib", "af05c46bffa58a1ca63bd18f4d8865b827746d1cdf94c8512c7f86d58731d16f"),
|
||||
"linuxsystemcore": ("linux", "**/*.so*", "c1cad49fb96caa73fddbc852588c67a2110402539b39dba40255c21974114e84"),
|
||||
"windowsarm64": ("windows", "**/*.dll", "8205701efbde585c6822981c7246c0a0d8e4b4cdb702097ec77674aa98aff7bb"),
|
||||
"windowsx86-64": ("windows", "**/*.dll", "dd4eef6e1aaba5b7034889a1646e259b3e91435b3084d43b3ff3b2c6adcc2122"),
|
||||
"linuxarm64": ("linux", "**/*.so*", "38009110f0deeaeaec08b59fe37ce7e87d3ee4f3d86bd70a7003b22a75bcb43e"),
|
||||
"linuxx86-64": ("linux", "**/*.so*", "503b3107febd35d32d28e5a3135c48fbd8c09d49d0b1f404182bc9b3c6877d12"),
|
||||
"osxuniversal": ("osx", "**/*.dylib", "422a52e4b5f859ef3afb7a97e66ba72572894a73abd88126e09647929b1701c1"),
|
||||
"linuxsystemcore": ("linux", "**/*.so*", "f84d4a6852d52da5535c054944b45f6f0c3d98271756ebfa28103a6f45e073e5"),
|
||||
"windowsarm64": ("windows", "**/*.dll", "8fd028fa1144c1c18adf9facf4dd690f6d888565c73498bfae01849d0400dad0"),
|
||||
"windowsx86-64": ("windows", "**/*.dll", "6e905f298fd261e68abb2bfcad7d47334d57f8b6dd14046bf2b98f55c7a657d7"),
|
||||
}
|
||||
# End auto-gen
|
||||
################################################################################
|
||||
|
||||
@@ -46,7 +46,7 @@ def tagList = [
|
||||
"Swerve Drive",
|
||||
|
||||
/* --- Telemetry --- */
|
||||
"SmartDashboard", "Shuffleboard", "Sendable", "DataLog",
|
||||
"SmartDashboard", "Shuffleboard", "Sendable", "DataLog", "Driver Station",
|
||||
|
||||
/* --- Controls --- */
|
||||
"Exponential Profile", "PID", "State-Space", "LTVUnicycleController", "Path Following",
|
||||
|
||||
@@ -5,7 +5,7 @@ nativeUtils {
|
||||
artifactId = "mrclib-cpp"
|
||||
headerClassifier = "headers"
|
||||
ext = "zip"
|
||||
version = '2027.1.0-alpha-1-50-gd008523'
|
||||
version = '2027.1.0-alpha-1-65-g21f308e'
|
||||
skipAtRuntimePlatforms.add(nativeUtils.wpi.platforms.systemcore)
|
||||
targetPlatforms.addAll(nativeUtils.wpi.platforms.allPlatforms)
|
||||
noDebugSplit = true
|
||||
|
||||
10
wpilibc/robotpy_pybind_build_info.bzl
generated
10
wpilibc/robotpy_pybind_build_info.bzl
generated
@@ -145,6 +145,16 @@ def wpilib_extension(srcs = [], header_to_dat_deps = [], extra_hdrs = [], includ
|
||||
("wpi::DriverStation", "wpi__DriverStation.hpp"),
|
||||
],
|
||||
),
|
||||
struct(
|
||||
class_name = "DriverStationDisplay",
|
||||
yml_file = "semiwrap/DriverStationDisplay.yml",
|
||||
header_root = "$(execpath :robotpy-native-wpilib.copy_headers)",
|
||||
header_file = "$(execpath :robotpy-native-wpilib.copy_headers)/wpi/driverstation/DriverStationDisplay.hpp",
|
||||
tmpl_class_names = [],
|
||||
trampolines = [
|
||||
("wpi::DriverStationDisplay", "wpi__DriverStationDisplay.hpp"),
|
||||
],
|
||||
),
|
||||
struct(
|
||||
class_name = "MatchState",
|
||||
yml_file = "semiwrap/MatchState.yml",
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "wpi/driverstation/DriverStationDisplay.hpp"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "wpi/hal/DriverStation.h"
|
||||
#include "wpi/hal/monotonic_clock.hpp"
|
||||
#include "wpi/util/StringMap.hpp"
|
||||
#include "wpi/util/mutex.hpp"
|
||||
#include "wpi/util/string.hpp"
|
||||
|
||||
using namespace wpi;
|
||||
|
||||
static constexpr std::chrono::milliseconds UPDATE_PERIOD{230};
|
||||
|
||||
static constexpr std::string_view CLEAR_DISPLAY = "\033[0m\033[2J\033[H";
|
||||
|
||||
namespace {
|
||||
struct DriverStationDisplayStorage {
|
||||
wpi::util::mutex mutex;
|
||||
bool rawMode = false;
|
||||
wpi::util::StringMap<uint32_t> lineMap;
|
||||
std::vector<std::string> lines;
|
||||
std::string buffer;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static DriverStationDisplayStorage& GetDisplayStorage() {
|
||||
static DriverStationDisplayStorage storage;
|
||||
return storage;
|
||||
}
|
||||
|
||||
static void WriteDisplayAnsiToHal(std::string_view data) {
|
||||
WPI_String dataWpiStr = wpi::util::make_string(data);
|
||||
HAL_WriteDisplayAnsi(&dataWpiStr);
|
||||
}
|
||||
|
||||
void DriverStationDisplay::SetMode(Mode mode) {
|
||||
auto& storage = GetDisplayStorage();
|
||||
std::scoped_lock lock(storage.mutex);
|
||||
|
||||
switch (mode) {
|
||||
case Mode::Line:
|
||||
storage.rawMode = false;
|
||||
storage.lineMap.clear();
|
||||
storage.lines.clear();
|
||||
break;
|
||||
case Mode::RawAnsi:
|
||||
storage.rawMode = true;
|
||||
WriteDisplayAnsiToHal(CLEAR_DISPLAY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DriverStationDisplay::AddData(std::string_view caption,
|
||||
std::string_view line) {
|
||||
auto& storage = GetDisplayStorage();
|
||||
std::scoped_lock lock(storage.mutex);
|
||||
if (storage.rawMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasCaption = false;
|
||||
for (char ch : caption) {
|
||||
if (!std::isspace(static_cast<unsigned char>(ch))) {
|
||||
hasCaption = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasCaption) {
|
||||
storage.lines.emplace_back(line);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t lineNum;
|
||||
auto it = storage.lineMap.find(caption);
|
||||
if (it == storage.lineMap.end()) {
|
||||
lineNum = storage.lines.size();
|
||||
storage.lineMap[caption] = lineNum;
|
||||
storage.lines.emplace_back(line);
|
||||
} else {
|
||||
lineNum = it->second;
|
||||
if (lineNum < storage.lines.size()) {
|
||||
storage.lines[lineNum] = line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DriverStationDisplay::AddLine(std::string_view line) {
|
||||
AddData({}, line);
|
||||
}
|
||||
|
||||
void DriverStationDisplay::UpdateLines() {
|
||||
auto& storage = GetDisplayStorage();
|
||||
std::scoped_lock lock(storage.mutex);
|
||||
if (storage.rawMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
static auto lastDisplayUpdate =
|
||||
wpi::hal::monotonic_clock::now() - UPDATE_PERIOD;
|
||||
auto now = wpi::hal::monotonic_clock::now();
|
||||
if (now - lastDisplayUpdate < UPDATE_PERIOD) {
|
||||
storage.lineMap.clear();
|
||||
storage.lines.clear();
|
||||
return;
|
||||
}
|
||||
lastDisplayUpdate = now;
|
||||
|
||||
storage.buffer.clear();
|
||||
storage.buffer += CLEAR_DISPLAY;
|
||||
for (const auto& line : storage.lines) {
|
||||
storage.buffer += line;
|
||||
storage.buffer += "\033[0m\n";
|
||||
}
|
||||
|
||||
WPI_String wpiBuffer = wpi::util::make_string(storage.buffer);
|
||||
HAL_WriteDisplayAnsi(&wpiBuffer);
|
||||
|
||||
storage.lineMap.clear();
|
||||
storage.lines.clear();
|
||||
}
|
||||
|
||||
void DriverStationDisplay::WriteRawAnsi(std::string_view data) {
|
||||
auto& storage = GetDisplayStorage();
|
||||
std::scoped_lock lock(storage.mutex);
|
||||
if (!storage.rawMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
WriteDisplayAnsiToHal(data);
|
||||
}
|
||||
|
||||
void DriverStationDisplay::ClearRaw() {
|
||||
auto& storage = GetDisplayStorage();
|
||||
std::scoped_lock lock(storage.mutex);
|
||||
if (storage.rawMode) {
|
||||
WriteDisplayAnsiToHal(CLEAR_DISPLAY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace wpi {
|
||||
|
||||
/**
|
||||
* Provides access to the Driver Station display.
|
||||
*
|
||||
* Line mode is the default display mode.
|
||||
*/
|
||||
class DriverStationDisplay final {
|
||||
public:
|
||||
DriverStationDisplay() = delete;
|
||||
|
||||
/** Driver Station display mode. */
|
||||
enum class Mode {
|
||||
/** Display line mode. This is the default display mode. */
|
||||
Line,
|
||||
/** Raw ANSI text mode. */
|
||||
RawAnsi
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the display mode.
|
||||
*
|
||||
* Mode::Line is the default display mode. Setting Mode::Line clears any
|
||||
* pending display lines. Setting Mode::RawAnsi clears the display.
|
||||
*
|
||||
* @param mode display mode
|
||||
*/
|
||||
static void SetMode(Mode mode);
|
||||
|
||||
/**
|
||||
* Adds display data in line mode.
|
||||
*
|
||||
* Repeated calls with the same caption before UpdateLines() replace the
|
||||
* previous line. The caption is used to identify the line and is not
|
||||
* displayed. Empty or whitespace-only captions always append a new line.
|
||||
*
|
||||
* @param caption Line caption.
|
||||
* @param line Line contents.
|
||||
*/
|
||||
static void AddData(std::string_view caption, std::string_view line);
|
||||
|
||||
/**
|
||||
* Adds an uncaptioned display line in line mode.
|
||||
*
|
||||
* This is equivalent to calling AddData() with an empty caption, which always
|
||||
* appends a new line.
|
||||
*
|
||||
* @param line Line contents.
|
||||
*/
|
||||
static void AddLine(std::string_view line);
|
||||
|
||||
/**
|
||||
* Updates the display with all pending lines and clears the pending lines.
|
||||
*
|
||||
* Updates are sent at most once every 230 ms. If called before 230 ms has
|
||||
* elapsed since the last update, the pending lines are cleared without
|
||||
* sending.
|
||||
*/
|
||||
static void UpdateLines();
|
||||
|
||||
/**
|
||||
* Writes ANSI text directly to the display in raw ANSI mode.
|
||||
*
|
||||
* This call is ignored unless the display is in raw ANSI mode.
|
||||
*
|
||||
* @param data ANSI text to write.
|
||||
*/
|
||||
static void WriteRawAnsi(std::string_view data);
|
||||
|
||||
/**
|
||||
* Clears the display in raw ANSI mode.
|
||||
*
|
||||
* This call is ignored unless the display is in raw ANSI mode.
|
||||
*/
|
||||
static void ClearRaw();
|
||||
};
|
||||
|
||||
} // namespace wpi
|
||||
@@ -112,6 +112,7 @@ DriverStationBackend = "wpi/driverstation/internal/DriverStationBackend.hpp"
|
||||
Alliance = "wpi/driverstation/Alliance.hpp"
|
||||
Alert = "wpi/driverstation/Alert.hpp"
|
||||
DriverStation = "wpi/driverstation/DriverStation.hpp"
|
||||
DriverStationDisplay = "wpi/driverstation/DriverStationDisplay.hpp"
|
||||
MatchState = "wpi/driverstation/MatchState.hpp"
|
||||
MatchType = "wpi/driverstation/MatchType.hpp"
|
||||
POVDirection = "wpi/driverstation/POVDirection.hpp"
|
||||
|
||||
11
wpilibc/src/main/python/semiwrap/DriverStationDisplay.yml
Normal file
11
wpilibc/src/main/python/semiwrap/DriverStationDisplay.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
classes:
|
||||
wpi::DriverStationDisplay:
|
||||
enums:
|
||||
Mode:
|
||||
methods:
|
||||
SetMode:
|
||||
AddData:
|
||||
AddLine:
|
||||
UpdateLines:
|
||||
WriteRawAnsi:
|
||||
ClearRaw:
|
||||
@@ -25,6 +25,7 @@ from ._wpilib import (
|
||||
DoubleSolenoid,
|
||||
DriverStation,
|
||||
DriverStationBackend,
|
||||
DriverStationDisplay,
|
||||
DualSenseController,
|
||||
DutyCycle,
|
||||
DutyCycleEncoder,
|
||||
@@ -142,6 +143,7 @@ __all__ = [
|
||||
"DoubleSolenoid",
|
||||
"DriverStation",
|
||||
"DriverStationBackend",
|
||||
"DriverStationDisplay",
|
||||
"DualSenseController",
|
||||
"DutyCycle",
|
||||
"DutyCycleEncoder",
|
||||
|
||||
@@ -4,6 +4,8 @@ EXAMPLE_FOLDERS = [
|
||||
"DifferentialDriveBot",
|
||||
"DifferentialDrivePoseEstimator",
|
||||
"DriveDistanceOffboard",
|
||||
"DriverStationDisplayAnsi",
|
||||
"DriverStationDisplayLines",
|
||||
"DutyCycleEncoder",
|
||||
"ElevatorExponentialProfile",
|
||||
"ElevatorExponentialSimulation",
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "Robot.hpp"
|
||||
|
||||
#include "opmode/DefaultTeleop.hpp"
|
||||
|
||||
Robot::Robot() {
|
||||
AddOpMode<DefaultTeleop>(wpi::RobotMode::TELEOPERATED, "Default Teleop");
|
||||
PublishOpModes();
|
||||
}
|
||||
|
||||
#ifndef RUNNING_WPILIB_TESTS
|
||||
int main() {
|
||||
return wpi::StartRobot<Robot>();
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "opmode/DefaultTeleop.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
|
||||
#include "wpi/driverstation/DriverStationDisplay.hpp"
|
||||
|
||||
using namespace wpi::units::literals;
|
||||
|
||||
namespace {
|
||||
constexpr std::array<int, 20> COLORS = {196, 202, 208, 214, 220, 226, 118,
|
||||
46, 48, 51, 45, 39, 33, 27,
|
||||
21, 57, 93, 129, 165, 201};
|
||||
} // namespace
|
||||
|
||||
void DefaultTeleop::Start() {
|
||||
wpi::DriverStationDisplay::SetMode(wpi::DriverStationDisplay::Mode::RawAnsi);
|
||||
m_timer.Restart();
|
||||
m_seconds = 0;
|
||||
|
||||
wpi::DriverStationDisplay::WriteRawAnsi(
|
||||
"\033[2J\033[H"
|
||||
"DriverStationDisplay ANSI mode\n"
|
||||
"This header and footer stay on screen.\n"
|
||||
"Seconds elapsed: \n"
|
||||
"Only the number above is rewritten.");
|
||||
UpdateSecondsDisplay();
|
||||
}
|
||||
|
||||
void DefaultTeleop::Periodic() {
|
||||
if (m_timer.AdvanceIfElapsed(1_s)) {
|
||||
++m_seconds;
|
||||
UpdateSecondsDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
void DefaultTeleop::UpdateSecondsDisplay() {
|
||||
int color = COLORS[m_seconds % COLORS.size()];
|
||||
std::array<char, 64> buffer;
|
||||
std::snprintf(buffer.data(), buffer.size(),
|
||||
"\033[3;18H\033[38;5;%dm%5d\033[0m", color, m_seconds);
|
||||
wpi::DriverStationDisplay::WriteRawAnsi(buffer.data());
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "wpi/framework/OpModeRobot.hpp"
|
||||
|
||||
class Robot : public wpi::OpModeRobot<Robot> {
|
||||
public:
|
||||
Robot();
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "wpi/opmode/PeriodicOpMode.hpp"
|
||||
#include "wpi/system/Timer.hpp"
|
||||
|
||||
class DefaultTeleop : public wpi::PeriodicOpMode {
|
||||
public:
|
||||
void Start() override;
|
||||
void Periodic() override;
|
||||
|
||||
private:
|
||||
void UpdateSecondsDisplay();
|
||||
|
||||
wpi::Timer m_timer;
|
||||
int m_seconds = 0;
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "Robot.hpp"
|
||||
|
||||
#include "opmode/DefaultTeleop.hpp"
|
||||
|
||||
Robot::Robot() {
|
||||
AddOpMode<DefaultTeleop>(wpi::RobotMode::TELEOPERATED, "Default Teleop");
|
||||
PublishOpModes();
|
||||
}
|
||||
|
||||
#ifndef RUNNING_WPILIB_TESTS
|
||||
int main() {
|
||||
return wpi::StartRobot<Robot>();
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "opmode/DefaultTeleop.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "wpi/driverstation/DriverStationDisplay.hpp"
|
||||
|
||||
void DefaultTeleop::Start() {
|
||||
wpi::DriverStationDisplay::SetMode(wpi::DriverStationDisplay::Mode::Line);
|
||||
m_timer.Restart();
|
||||
m_loopCount = 0;
|
||||
}
|
||||
|
||||
void DefaultTeleop::Periodic() {
|
||||
std::ostringstream runtime;
|
||||
runtime << std::fixed << std::setprecision(1) << m_timer.Get().value()
|
||||
<< " seconds";
|
||||
|
||||
wpi::DriverStationDisplay::AddLine("DriverStationDisplay line mode");
|
||||
wpi::DriverStationDisplay::AddData("Runtime", runtime.str());
|
||||
wpi::DriverStationDisplay::AddData("Loop Count",
|
||||
std::to_string(m_loopCount++));
|
||||
wpi::DriverStationDisplay::UpdateLines();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "wpi/framework/OpModeRobot.hpp"
|
||||
|
||||
class Robot : public wpi::OpModeRobot<Robot> {
|
||||
public:
|
||||
Robot();
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "wpi/opmode/PeriodicOpMode.hpp"
|
||||
#include "wpi/system/Timer.hpp"
|
||||
|
||||
class DefaultTeleop : public wpi::PeriodicOpMode {
|
||||
public:
|
||||
void Start() override;
|
||||
void Periodic() override;
|
||||
|
||||
private:
|
||||
wpi::Timer m_timer;
|
||||
int m_loopCount = 0;
|
||||
};
|
||||
@@ -250,6 +250,28 @@
|
||||
"gradlebase": "cpp",
|
||||
"commandversion": 2
|
||||
},
|
||||
{
|
||||
"name": "Driver Station Display Lines",
|
||||
"description": "Display lines on the Driver Station display and update them every robot loop.",
|
||||
"tags": [
|
||||
"Basic Robot",
|
||||
"Driver Station"
|
||||
],
|
||||
"foldername": "DriverStationDisplayLines",
|
||||
"gradlebase": "cpp",
|
||||
"commandversion": 2
|
||||
},
|
||||
{
|
||||
"name": "Driver Station Display ANSI",
|
||||
"description": "Stream ANSI text to the Driver Station display and update a value in place once per second.",
|
||||
"tags": [
|
||||
"Basic Robot",
|
||||
"Driver Station"
|
||||
],
|
||||
"foldername": "DriverStationDisplayAnsi",
|
||||
"gradlebase": "cpp",
|
||||
"commandversion": 2
|
||||
},
|
||||
{
|
||||
"name": "DriveDistanceOffboard",
|
||||
"description": "Drive a differential drivetrain a set distance using TrapezoidProfile and smart motor controller PID.",
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package org.wpilib.driverstation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import org.wpilib.hardware.hal.DriverStationJNI;
|
||||
import org.wpilib.util.WPIUtilJNI;
|
||||
|
||||
/**
|
||||
* Provides access to the Driver Station display.
|
||||
*
|
||||
* <p>Line mode is the default display mode.
|
||||
*/
|
||||
public final class DriverStationDisplay {
|
||||
private static final long UPDATE_PERIOD_MICROS = 230_000L;
|
||||
private static final String CLEAR_DISPLAY = "\033[0m\033[2J\033[H";
|
||||
|
||||
private static final Lock m_displayLock = new ReentrantLock();
|
||||
private static boolean rawMode;
|
||||
private static final Map<String, Integer> lineMap = new HashMap<>();
|
||||
private static final List<String> lines = new ArrayList<>();
|
||||
private static long lastDisplayUpdate = WPIUtilJNI.now() - UPDATE_PERIOD_MICROS;
|
||||
|
||||
private DriverStationDisplay() {}
|
||||
|
||||
/** Driver Station display mode. */
|
||||
public enum Mode {
|
||||
/** Display line mode. This is the default display mode. */
|
||||
Line,
|
||||
|
||||
/** Raw ANSI text mode. */
|
||||
RawAnsi
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the display mode.
|
||||
*
|
||||
* <p>{@link Mode#Line} is the default display mode. Setting {@link Mode#Line} clears any pending
|
||||
* display lines. Setting {@link Mode#RawAnsi} clears the display.
|
||||
*
|
||||
* @param mode display mode
|
||||
*/
|
||||
public static void setMode(Mode mode) {
|
||||
m_displayLock.lock();
|
||||
try {
|
||||
switch (mode) {
|
||||
case Line -> {
|
||||
rawMode = false;
|
||||
lineMap.clear();
|
||||
lines.clear();
|
||||
}
|
||||
case RawAnsi -> {
|
||||
rawMode = true;
|
||||
DriverStationJNI.writeDisplayAnsi(CLEAR_DISPLAY);
|
||||
}
|
||||
default -> throw new IllegalArgumentException("Unknown display mode: " + mode);
|
||||
}
|
||||
} finally {
|
||||
m_displayLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds display data in line mode.
|
||||
*
|
||||
* <p>Repeated calls with the same caption before {@link #updateLines()} replace the previous
|
||||
* line. The caption is used to identify the line and is not displayed. Empty or whitespace-only
|
||||
* captions always append a new line.
|
||||
*
|
||||
* @param caption Line caption.
|
||||
* @param line Line contents.
|
||||
*/
|
||||
public static void addData(String caption, String line) {
|
||||
m_displayLock.lock();
|
||||
try {
|
||||
addDataUnderLock(caption, line);
|
||||
} finally {
|
||||
m_displayLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds display data in line mode.
|
||||
*
|
||||
* <p>Repeated calls with the same caption before {@link #updateLines()} replace the previous
|
||||
* line. The caption is used to identify the line and is not displayed. Empty or whitespace-only
|
||||
* captions always append a new line.
|
||||
*
|
||||
* <p>The value is converted to text with {@link String#valueOf(Object)}.
|
||||
*
|
||||
* @param caption Line caption.
|
||||
* @param value Line value.
|
||||
*/
|
||||
public static void addData(String caption, Object value) {
|
||||
addData(caption, String.valueOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds formatted display data in line mode.
|
||||
*
|
||||
* <p>Repeated calls with the same caption before {@link #updateLines()} replace the previous
|
||||
* line. The caption is used to identify the line and is not displayed. Empty or whitespace-only
|
||||
* captions always append a new line.
|
||||
*
|
||||
* @param caption Line caption.
|
||||
* @param format Format string.
|
||||
* @param args Format arguments.
|
||||
*/
|
||||
public static void addData(String caption, String format, Object... args) {
|
||||
addData(caption, String.format(format, args));
|
||||
}
|
||||
|
||||
private static void addDataUnderLock(String caption, String line) {
|
||||
if (rawMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
String captionText = nonNull(caption);
|
||||
String lineText = nonNull(line);
|
||||
|
||||
if (captionText.isBlank()) {
|
||||
lines.add(lineText);
|
||||
return;
|
||||
}
|
||||
|
||||
Integer lineNum = lineMap.get(captionText);
|
||||
if (lineNum == null) {
|
||||
lineMap.put(captionText, lines.size());
|
||||
lines.add(lineText);
|
||||
} else if (lineNum < lines.size()) {
|
||||
lines.set(lineNum, lineText);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an uncaptioned display line in line mode.
|
||||
*
|
||||
* <p>This is equivalent to calling {@link #addData(String, String)} with an empty caption, which
|
||||
* always appends a new line.
|
||||
*
|
||||
* @param line Line contents.
|
||||
*/
|
||||
public static void addLine(String line) {
|
||||
addData("", line);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the display with all pending lines and clears the pending lines.
|
||||
*
|
||||
* <p>Updates are sent at most once every 230 ms. If called before 230 ms has elapsed since the
|
||||
* last update, the pending lines are cleared without sending.
|
||||
*/
|
||||
public static void updateLines() {
|
||||
m_displayLock.lock();
|
||||
try {
|
||||
if (rawMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
long now = WPIUtilJNI.now();
|
||||
if (now - lastDisplayUpdate < UPDATE_PERIOD_MICROS) {
|
||||
lineMap.clear();
|
||||
lines.clear();
|
||||
return;
|
||||
}
|
||||
lastDisplayUpdate = now;
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(CLEAR_DISPLAY);
|
||||
for (String line : lines) {
|
||||
builder.append(line).append("\033[0m\n");
|
||||
}
|
||||
|
||||
DriverStationJNI.writeDisplayAnsi(builder.toString());
|
||||
|
||||
lineMap.clear();
|
||||
lines.clear();
|
||||
} finally {
|
||||
m_displayLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes ANSI text directly to the display in raw ANSI mode.
|
||||
*
|
||||
* <p>This call is ignored unless the display is in raw ANSI mode.
|
||||
*
|
||||
* @param data ANSI text to write.
|
||||
*/
|
||||
public static void writeRawAnsi(String data) {
|
||||
m_displayLock.lock();
|
||||
try {
|
||||
if (rawMode) {
|
||||
DriverStationJNI.writeDisplayAnsi(nonNull(data));
|
||||
}
|
||||
} finally {
|
||||
m_displayLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the display in raw ANSI mode.
|
||||
*
|
||||
* <p>This call is ignored unless the display is in raw ANSI mode.
|
||||
*/
|
||||
public static void clearRaw() {
|
||||
m_displayLock.lock();
|
||||
try {
|
||||
if (rawMode) {
|
||||
DriverStationJNI.writeDisplayAnsi(CLEAR_DISPLAY);
|
||||
}
|
||||
} finally {
|
||||
m_displayLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private static String nonNull(String value) {
|
||||
return value == null ? "" : value;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ EXAMPLE_FOLDERS = [
|
||||
"differentialdrivebot",
|
||||
"differentialdriveposeestimator",
|
||||
"drivedistanceoffboard",
|
||||
"driverstationdisplayansi",
|
||||
"driverstationdisplaylines",
|
||||
"dutycycleencoder",
|
||||
"elevatorexponentialprofile",
|
||||
"elevatorexponentialsimulation",
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package org.wpilib.examples.driverstationdisplayansi;
|
||||
|
||||
import org.wpilib.driverstation.DriverStationDisplay;
|
||||
import org.wpilib.driverstation.DriverStationDisplay.Mode;
|
||||
import org.wpilib.opmode.PeriodicOpMode;
|
||||
import org.wpilib.opmode.Teleop;
|
||||
import org.wpilib.system.Timer;
|
||||
|
||||
@Teleop(name = "Default Teleop")
|
||||
public class DefaultTeleop extends PeriodicOpMode {
|
||||
private static final int[] COLORS = {
|
||||
196, 202, 208, 214, 220, 226, 118, 46, 48, 51,
|
||||
45, 39, 33, 27, 21, 57, 93, 129, 165, 201
|
||||
};
|
||||
|
||||
private final Timer timer = new Timer();
|
||||
private int seconds;
|
||||
|
||||
/** Called once when the robot is enabled. */
|
||||
@Override
|
||||
public void start() {
|
||||
DriverStationDisplay.setMode(Mode.RawAnsi);
|
||||
timer.restart();
|
||||
seconds = 0;
|
||||
|
||||
DriverStationDisplay.writeRawAnsi(
|
||||
"\033[2J\033[H"
|
||||
+ "DriverStationDisplay ANSI mode\n"
|
||||
+ "This header and footer stay on screen.\n"
|
||||
+ "Seconds elapsed: \n"
|
||||
+ "Only the number above is rewritten.");
|
||||
updateSecondsDisplay();
|
||||
}
|
||||
|
||||
/** Called periodically while the robot is enabled. */
|
||||
@Override
|
||||
public void periodic() {
|
||||
if (timer.advanceIfElapsed(1.0)) {
|
||||
seconds++;
|
||||
updateSecondsDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSecondsDisplay() {
|
||||
int color = COLORS[seconds % COLORS.length];
|
||||
DriverStationDisplay.writeRawAnsi(
|
||||
String.format("\033[3;18H\033[38;5;%dm%5d\033[0m", color, seconds));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package org.wpilib.examples.driverstationdisplayansi;
|
||||
|
||||
import org.wpilib.framework.OpModeRobot;
|
||||
|
||||
/**
|
||||
* Demonstrates DriverStationDisplay raw ANSI mode. The default teleop opmode writes static display
|
||||
* text once, then updates a value and its color in place once per second.
|
||||
*/
|
||||
public class Robot extends OpModeRobot {}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package org.wpilib.examples.driverstationdisplaylines;
|
||||
|
||||
import org.wpilib.driverstation.DriverStationDisplay;
|
||||
import org.wpilib.driverstation.DriverStationDisplay.Mode;
|
||||
import org.wpilib.opmode.PeriodicOpMode;
|
||||
import org.wpilib.opmode.Teleop;
|
||||
import org.wpilib.system.Timer;
|
||||
|
||||
@Teleop(name = "Default Teleop")
|
||||
public class DefaultTeleop extends PeriodicOpMode {
|
||||
private final Timer timer = new Timer();
|
||||
private int loopCount;
|
||||
|
||||
/** Called once when the robot is enabled. */
|
||||
@Override
|
||||
public void start() {
|
||||
DriverStationDisplay.setMode(Mode.Line);
|
||||
timer.restart();
|
||||
loopCount = 0;
|
||||
}
|
||||
|
||||
/** Called periodically while the robot is enabled. */
|
||||
@Override
|
||||
public void periodic() {
|
||||
DriverStationDisplay.addLine("DriverStationDisplay line mode");
|
||||
DriverStationDisplay.addData("Runtime", "%.1f seconds", timer.get());
|
||||
DriverStationDisplay.addData("Loop Count", "%d", loopCount++);
|
||||
DriverStationDisplay.updateLines();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package org.wpilib.examples.driverstationdisplaylines;
|
||||
|
||||
import org.wpilib.framework.OpModeRobot;
|
||||
|
||||
/**
|
||||
* Demonstrates DriverStationDisplay line mode. The default teleop opmode adds all lines each loop
|
||||
* and updateLines() sends the pending set to the Driver Station display.
|
||||
*/
|
||||
public class Robot extends OpModeRobot {}
|
||||
@@ -289,6 +289,30 @@
|
||||
"robotclass": "Robot",
|
||||
"commandversion": 2
|
||||
},
|
||||
{
|
||||
"name": "Driver Station Display Lines",
|
||||
"description": "Display lines on the Driver Station display and update them every robot loop.",
|
||||
"tags": [
|
||||
"Basic Robot",
|
||||
"Driver Station"
|
||||
],
|
||||
"foldername": "driverstationdisplaylines",
|
||||
"gradlebase": "java",
|
||||
"robotclass": "Robot",
|
||||
"commandversion": 2
|
||||
},
|
||||
{
|
||||
"name": "Driver Station Display ANSI",
|
||||
"description": "Stream ANSI text to the Driver Station display and update a value in place once per second.",
|
||||
"tags": [
|
||||
"Basic Robot",
|
||||
"Driver Station"
|
||||
],
|
||||
"foldername": "driverstationdisplayansi",
|
||||
"gradlebase": "java",
|
||||
"robotclass": "Robot",
|
||||
"commandversion": 2
|
||||
},
|
||||
{
|
||||
"name": "DriveDistanceOffboard",
|
||||
"description": "Drive a differential drivetrain a set distance using TrapezoidProfile and smart motor controller PID.",
|
||||
|
||||
Reference in New Issue
Block a user