mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-27 02:01:42 +00:00
Add mechanism to control Shuffleboard recordings and add event markers (#1414)
This commit is contained in:
committed by
Peter Johnson
parent
69cb53b51b
commit
45f4472d42
@@ -0,0 +1,50 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2018 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "frc/shuffleboard/RecordingController.h"
|
||||
|
||||
#include "frc/DriverStation.h"
|
||||
|
||||
using namespace frc;
|
||||
using namespace frc::detail;
|
||||
|
||||
RecordingController::RecordingController(nt::NetworkTableInstance ntInstance)
|
||||
: m_recordingControlEntry(), m_recordingFileNameFormatEntry() {
|
||||
m_recordingControlEntry =
|
||||
ntInstance.GetEntry("/Shuffleboard/.recording/RecordData");
|
||||
m_recordingFileNameFormatEntry =
|
||||
ntInstance.GetEntry("/Shuffleboard/.recording/FileNameFormat");
|
||||
m_eventsTable = ntInstance.GetTable("/Shuffleboard/.recording/events");
|
||||
}
|
||||
|
||||
void RecordingController::StartRecording() {
|
||||
m_recordingControlEntry.SetBoolean(true);
|
||||
}
|
||||
|
||||
void RecordingController::StopRecording() {
|
||||
m_recordingControlEntry.SetBoolean(false);
|
||||
}
|
||||
|
||||
void RecordingController::SetRecordingFileNameFormat(wpi::StringRef format) {
|
||||
m_recordingFileNameFormatEntry.SetString(format);
|
||||
}
|
||||
|
||||
void RecordingController::ClearRecordingFileNameFormat() {
|
||||
m_recordingFileNameFormatEntry.Delete();
|
||||
}
|
||||
|
||||
void RecordingController::AddEventMarker(
|
||||
wpi::StringRef name, wpi::StringRef description,
|
||||
ShuffleboardEventImportance importance) {
|
||||
if (name.empty()) {
|
||||
DriverStation::ReportError("Shuffleboard event name was not specified");
|
||||
return;
|
||||
}
|
||||
auto arr = wpi::ArrayRef<std::string>{
|
||||
description, ShuffleboardEventImportanceName(importance)};
|
||||
m_eventsTable->GetSubTable(name)->GetEntry("Info").SetStringArray(arr);
|
||||
}
|
||||
@@ -29,8 +29,39 @@ void Shuffleboard::DisableActuatorWidgets() {
|
||||
GetInstance().DisableActuatorWidgets();
|
||||
}
|
||||
|
||||
void Shuffleboard::StartRecording() {
|
||||
GetRecordingController().StartRecording();
|
||||
}
|
||||
|
||||
void Shuffleboard::StopRecording() { GetRecordingController().StopRecording(); }
|
||||
|
||||
void Shuffleboard::SetRecordingFileNameFormat(wpi::StringRef format) {
|
||||
GetRecordingController().SetRecordingFileNameFormat(format);
|
||||
}
|
||||
|
||||
void Shuffleboard::ClearRecordingFileNameFormat() {
|
||||
GetRecordingController().ClearRecordingFileNameFormat();
|
||||
}
|
||||
|
||||
void Shuffleboard::AddEventMarker(wpi::StringRef name,
|
||||
wpi::StringRef description,
|
||||
ShuffleboardEventImportance importance) {
|
||||
GetRecordingController().AddEventMarker(name, description, importance);
|
||||
}
|
||||
|
||||
void Shuffleboard::AddEventMarker(wpi::StringRef name,
|
||||
ShuffleboardEventImportance importance) {
|
||||
AddEventMarker(name, "", importance);
|
||||
}
|
||||
|
||||
detail::ShuffleboardInstance& Shuffleboard::GetInstance() {
|
||||
static detail::ShuffleboardInstance inst(
|
||||
nt::NetworkTableInstance::GetDefault());
|
||||
return inst;
|
||||
}
|
||||
|
||||
detail::RecordingController& Shuffleboard::GetRecordingController() {
|
||||
static detail::RecordingController inst(
|
||||
nt::NetworkTableInstance::GetDefault());
|
||||
return inst;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2018 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <networktables/NetworkTable.h>
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <wpi/StringRef.h>
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardEventImportance.h"
|
||||
|
||||
namespace frc {
|
||||
namespace detail {
|
||||
|
||||
class RecordingController final {
|
||||
public:
|
||||
explicit RecordingController(nt::NetworkTableInstance ntInstance);
|
||||
virtual ~RecordingController() = default;
|
||||
|
||||
void StartRecording();
|
||||
void StopRecording();
|
||||
void SetRecordingFileNameFormat(wpi::StringRef format);
|
||||
void ClearRecordingFileNameFormat();
|
||||
|
||||
void AddEventMarker(wpi::StringRef name, wpi::StringRef description,
|
||||
ShuffleboardEventImportance importance);
|
||||
|
||||
private:
|
||||
nt::NetworkTableEntry m_recordingControlEntry;
|
||||
nt::NetworkTableEntry m_recordingFileNameFormatEntry;
|
||||
std::shared_ptr<nt::NetworkTable> m_eventsTable;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace frc
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include <wpi/StringRef.h>
|
||||
|
||||
#include "frc/shuffleboard/RecordingController.h"
|
||||
#include "frc/shuffleboard/ShuffleboardEventImportance.h"
|
||||
#include "frc/shuffleboard/ShuffleboardInstance.h"
|
||||
|
||||
namespace frc {
|
||||
@@ -97,8 +99,77 @@ class Shuffleboard final {
|
||||
*/
|
||||
static void DisableActuatorWidgets();
|
||||
|
||||
/**
|
||||
* Starts data recording on the dashboard. Has no effect if recording is
|
||||
* already in progress.
|
||||
*/
|
||||
static void StartRecording();
|
||||
|
||||
/**
|
||||
* Stops data recording on the dashboard. Has no effect if no recording is in
|
||||
* progress.
|
||||
*/
|
||||
static void StopRecording();
|
||||
|
||||
/**
|
||||
* Sets the file name format for new recording files to use. If recording is
|
||||
* in progress when this method is called, it will continue to use the same
|
||||
* file. New recordings will use the format.
|
||||
*
|
||||
* <p>To avoid recording files overwriting each other, make sure to use unique
|
||||
* recording file names. File name formats accept templates for inserting the
|
||||
* date and time when the recording started with the {@code ${date}} and
|
||||
* {@code ${time}} templates, respectively. For example, the default format is
|
||||
* {@code "recording-${time}"} and recording files created with it will have
|
||||
* names like {@code "recording-2018.01.15.sbr"}. Users are
|
||||
* <strong>strongly</strong> recommended to use the {@code ${time}} template
|
||||
* to ensure unique file names.
|
||||
* </p>
|
||||
*
|
||||
* @param format the format for the
|
||||
*/
|
||||
static void SetRecordingFileNameFormat(wpi::StringRef format);
|
||||
|
||||
/**
|
||||
* Clears the custom name format for recording files. New recordings will use
|
||||
* the default format.
|
||||
*
|
||||
* @see #setRecordingFileNameFormat(String)
|
||||
*/
|
||||
static void ClearRecordingFileNameFormat();
|
||||
|
||||
/**
|
||||
* Notifies Shuffleboard of an event. Events can range from as trivial as a
|
||||
* change in a command state to as critical as a total power loss or component
|
||||
* failure. If Shuffleboard is recording, the event will also be recorded.
|
||||
*
|
||||
* <p>If {@code name} is {@code null} or empty, no event will be sent and an
|
||||
* error will be printed to the driver station.
|
||||
*
|
||||
* @param name the name of the event
|
||||
* @param description a description of the event
|
||||
* @param importance the importance of the event
|
||||
*/
|
||||
static void AddEventMarker(wpi::StringRef name, wpi::StringRef description,
|
||||
ShuffleboardEventImportance importance);
|
||||
|
||||
/**
|
||||
* Notifies Shuffleboard of an event. Events can range from as trivial as a
|
||||
* change in a command state to as critical as a total power loss or component
|
||||
* failure. If Shuffleboard is recording, the event will also be recorded.
|
||||
*
|
||||
* <p>If {@code name} is {@code null} or empty, no event will be sent and an
|
||||
* error will be printed to the driver station.
|
||||
*
|
||||
* @param name the name of the event
|
||||
* @param importance the importance of the event
|
||||
*/
|
||||
static void AddEventMarker(wpi::StringRef name,
|
||||
ShuffleboardEventImportance importance);
|
||||
|
||||
private:
|
||||
static detail::ShuffleboardInstance& GetInstance();
|
||||
static detail::RecordingController& GetRecordingController();
|
||||
|
||||
// TODO usage reporting
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2018 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace frc {
|
||||
|
||||
// Maintainer note: this enum is mirrored in WPILibJ and in Shuffleboard
|
||||
// Modifying the enum or enum strings requires a corresponding change to the
|
||||
// Java enum and the enum in Shuffleboard
|
||||
|
||||
enum ShuffleboardEventImportance { kTrivial, kLow, kNormal, kHigh, kCritical };
|
||||
|
||||
inline wpi::StringRef ShuffleboardEventImportanceName(
|
||||
ShuffleboardEventImportance importance) {
|
||||
switch (importance) {
|
||||
case kTrivial:
|
||||
return "TRIVIAL";
|
||||
case kLow:
|
||||
return "LOW";
|
||||
case kNormal:
|
||||
return "NORMAL";
|
||||
case kHigh:
|
||||
return "HIGH";
|
||||
case kCritical:
|
||||
return "CRITICAL";
|
||||
default:
|
||||
return "NORMAL";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace frc
|
||||
@@ -0,0 +1,55 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2018 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
/**
|
||||
* The importance of an event marker in Shuffleboard. The exact meaning of each importance level is
|
||||
* up for interpretation on a team-to-team basis, but users should follow the general guidelines
|
||||
* of the various importance levels. The examples given are for reference and may be ignored or
|
||||
* considered to be more or less important from team to team.
|
||||
*/
|
||||
public enum EventImportance {
|
||||
// Maintainer note: this enum is mirrored in WPILibC and in Shuffleboard
|
||||
// Modifying the enum or enum strings requires a corresponding change to the C++ enum
|
||||
// and the enum in Shuffleboard
|
||||
|
||||
/**
|
||||
* A trivial event such as a change in command state.
|
||||
*/
|
||||
kTrivial("TRIVIAL"),
|
||||
|
||||
/**
|
||||
* A low importance event such as acquisition of a game piece.
|
||||
*/
|
||||
kLow("LOW"),
|
||||
|
||||
/**
|
||||
* A "normal" importance event, such as a transition from autonomous mode to teleoperated control.
|
||||
*/
|
||||
kNormal("NORMAL"),
|
||||
|
||||
/**
|
||||
* A high-importance event such as scoring a game piece.
|
||||
*/
|
||||
kHigh("HIGH"),
|
||||
|
||||
/**
|
||||
* A critically important event such as a brownout, component failure, or software deadlock.
|
||||
*/
|
||||
kCritical("CRITICAL");
|
||||
|
||||
private final String m_simpleName;
|
||||
|
||||
EventImportance(String simpleName) {
|
||||
m_simpleName = simpleName;
|
||||
}
|
||||
|
||||
public String getSimpleName() {
|
||||
return m_simpleName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2018 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.wpilibj.DriverStation;
|
||||
|
||||
/**
|
||||
* Controls Shuffleboard recordings via NetworkTables.
|
||||
*/
|
||||
final class RecordingController {
|
||||
private static final String kRecordingTableName = "/Shuffleboard/.recording/";
|
||||
private static final String kRecordingControlKey = kRecordingTableName + "RecordData";
|
||||
private static final String kRecordingFileNameFormatKey = kRecordingTableName + "FileNameFormat";
|
||||
private static final String kEventMarkerTableName = kRecordingTableName + "events";
|
||||
|
||||
private final NetworkTableEntry m_recordingControlEntry;
|
||||
private final NetworkTableEntry m_recordingFileNameFormatEntry;
|
||||
private final NetworkTable m_eventsTable;
|
||||
|
||||
RecordingController(NetworkTableInstance ntInstance) {
|
||||
m_recordingControlEntry = ntInstance.getEntry(kRecordingControlKey);
|
||||
m_recordingFileNameFormatEntry = ntInstance.getEntry(kRecordingFileNameFormatKey);
|
||||
m_eventsTable = ntInstance.getTable(kEventMarkerTableName);
|
||||
}
|
||||
|
||||
public void startRecording() {
|
||||
m_recordingControlEntry.setBoolean(true);
|
||||
}
|
||||
|
||||
public void stopRecording() {
|
||||
m_recordingControlEntry.setBoolean(false);
|
||||
}
|
||||
|
||||
public void setRecordingFileNameFormat(String format) {
|
||||
m_recordingFileNameFormatEntry.setString(format);
|
||||
}
|
||||
|
||||
public void clearRecordingFileNameFormat() {
|
||||
m_recordingFileNameFormatEntry.delete();
|
||||
}
|
||||
|
||||
public void addEventMarker(String name, String description, EventImportance importance) {
|
||||
if (name == null || name.isEmpty() || name.isBlank()) {
|
||||
DriverStation.reportError(
|
||||
"Shuffleboard event name was not specified", true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (importance == null) {
|
||||
DriverStation.reportError(
|
||||
"Shuffleboard event importance was null", true);
|
||||
return;
|
||||
}
|
||||
|
||||
String eventDescription = description == null || description.isBlank() ? "" : description;
|
||||
|
||||
m_eventsTable.getSubTable(name)
|
||||
.getEntry("Info")
|
||||
.setStringArray(new String[]{eventDescription, importance.getSimpleName()});
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,8 @@ public final class Shuffleboard {
|
||||
|
||||
private static final ShuffleboardRoot root =
|
||||
new ShuffleboardInstance(NetworkTableInstance.getDefault());
|
||||
private static final RecordingController recordingController =
|
||||
new RecordingController(NetworkTableInstance.getDefault());
|
||||
|
||||
// TODO usage reporting
|
||||
|
||||
@@ -101,4 +103,80 @@ public final class Shuffleboard {
|
||||
root.disableActuatorWidgets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts data recording on the dashboard. Has no effect if recording is already in progress.
|
||||
*
|
||||
* @see #stopRecording()
|
||||
*/
|
||||
public static void startRecording() {
|
||||
recordingController.startRecording();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops data recording on the dashboard. Has no effect if no recording is in progress.
|
||||
*
|
||||
* @see #startRecording()
|
||||
*/
|
||||
public static void stopRecording() {
|
||||
recordingController.stopRecording();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file name format for new recording files to use. If recording is in progress when this
|
||||
* method is called, it will continue to use the same file. New recordings will use the format.
|
||||
*
|
||||
* <p>To avoid recording files overwriting each other, make sure to use unique recording file
|
||||
* names. File name formats accept templates for inserting the date and time when the recording
|
||||
* started with the {@code ${date}} and {@code ${time}} templates, respectively. For example,
|
||||
* the default format is {@code "recording-${time}"} and recording files created with it will have
|
||||
* names like {@code "recording-2018.01.15.sbr"}. Users are <strong>strongly</strong> recommended
|
||||
* to use the {@code ${time}} template to ensure unique file names.
|
||||
* </p>
|
||||
*
|
||||
* @param format the format for the
|
||||
* @see #clearRecordingFileNameFormat()
|
||||
*/
|
||||
public static void setRecordingFileNameFormat(String format) {
|
||||
recordingController.setRecordingFileNameFormat(format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the custom name format for recording files. New recordings will use the default format.
|
||||
*
|
||||
* @see #setRecordingFileNameFormat(String)
|
||||
*/
|
||||
public static void clearRecordingFileNameFormat() {
|
||||
recordingController.clearRecordingFileNameFormat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies Shuffleboard of an event. Events can range from as trivial as a change in a command
|
||||
* state to as critical as a total power loss or component failure. If Shuffleboard is recording,
|
||||
* the event will also be recorded.
|
||||
*
|
||||
* <p>If {@code name} is {@code null} or empty, or {@code importance} is {@code null}, then
|
||||
* no event will be sent and an error will be printed to the driver station.
|
||||
*
|
||||
* @param name the name of the event
|
||||
* @param description a description of the event
|
||||
* @param importance the importance of the event
|
||||
*/
|
||||
public static void addEventMarker(String name, String description, EventImportance importance) {
|
||||
recordingController.addEventMarker(name, description, importance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies Shuffleboard of an event. Events can range from as trivial as a change in a command
|
||||
* state to as critical as a total power loss or component failure. If Shuffleboard is recording,
|
||||
* the event will also be recorded.
|
||||
*
|
||||
* <p>If {@code name} is {@code null} or empty, or {@code importance} is {@code null}, then
|
||||
* no event will be sent and an error will be printed to the driver station.
|
||||
*
|
||||
* @param name the name of the event
|
||||
* @param importance the importance of the event
|
||||
*/
|
||||
public static void addEventMarker(String name, EventImportance importance) {
|
||||
addEventMarker(name, "", importance);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user