mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-02 02:51:42 +00:00
Compare commits
31 Commits
v2023.1.1-
...
v2023.1.1-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffbf6a1fa2 | ||
|
|
fbabd0ef15 | ||
|
|
7713f68772 | ||
|
|
701995d6cc | ||
|
|
bf7068ac27 | ||
|
|
aae0f52ca6 | ||
|
|
ee02fb7ba7 | ||
|
|
518916ba02 | ||
|
|
3997c6635b | ||
|
|
cc8675a4e5 | ||
|
|
fb2c170b6e | ||
|
|
7ba8a9ee1f | ||
|
|
c569d8e523 | ||
|
|
2a5e89fa97 | ||
|
|
cc003c6c38 | ||
|
|
5522916123 | ||
|
|
967b30de3a | ||
|
|
3270d4fc86 | ||
|
|
be39678447 | ||
|
|
61c75deb2a | ||
|
|
a865f48e96 | ||
|
|
f66a667321 | ||
|
|
f8d4e9866e | ||
|
|
7e84ea891f | ||
|
|
da3ec1be10 | ||
|
|
944dd7265d | ||
|
|
6948cea67a | ||
|
|
a31459bce6 | ||
|
|
4a0ad6b48c | ||
|
|
e6552d272e | ||
|
|
bde383f763 |
@@ -30,6 +30,7 @@ apply from: "${rootDir}/shared/opencv.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation project(':wpimath')
|
||||
devImplementation project(':wpimath')
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -4,10 +4,14 @@
|
||||
|
||||
package edu.wpi.first.apriltag;
|
||||
|
||||
import edu.wpi.first.apriltag.jni.AprilTagJNI;
|
||||
|
||||
public final class DevMain {
|
||||
/** Main entry point. */
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello World!");
|
||||
var detector = AprilTagJNI.aprilTagCreate("tag16h5", 2.0, 0.0, 1, false, false);
|
||||
AprilTagJNI.aprilTagDestroy(detector);
|
||||
}
|
||||
|
||||
private DevMain() {}
|
||||
|
||||
@@ -33,11 +33,10 @@ import java.util.Optional;
|
||||
* meters with "width" and "length" values. This is to account for arbitrary field sizes when
|
||||
* transforming the poses.
|
||||
*
|
||||
* <p>Pose3ds are assumed to be measured from the bottom-left corner of the field, when the blue
|
||||
* alliance is at the left. By default, Pose3ds will be returned as declared when calling {@link
|
||||
* AprilTagFieldLayout#getTagPose(int)}. {@link #setOrigin(OriginPosition)} can be used to transform
|
||||
* the poses returned from {@link AprilTagFieldLayout#getTagPose(int)} to be correct relative to a
|
||||
* different coordinate frame.
|
||||
* <p>Pose3ds in the JSON are measured using the normal FRC coordinate system, NWU with the origin
|
||||
* at the bottom-right corner of the blue alliance wall. {@link #setOrigin(OriginPosition)} can be
|
||||
* used to change the poses returned from {@link AprilTagFieldLayout#getTagPose(int)} to be from the
|
||||
* perspective of a specific alliance.
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
|
||||
@@ -29,7 +29,7 @@ AprilTagFieldLayout::AprilTagFieldLayout(std::string_view path) {
|
||||
m_apriltags[tag.ID] = tag;
|
||||
}
|
||||
m_fieldWidth = units::meter_t{json.at("field").at("width").get<double>()};
|
||||
m_fieldLength = units::meter_t{json.at("field").at("height").get<double>()};
|
||||
m_fieldLength = units::meter_t{json.at("field").at("length").get<double>()};
|
||||
}
|
||||
|
||||
AprilTagFieldLayout::AprilTagFieldLayout(std::vector<AprilTag> apriltags,
|
||||
|
||||
@@ -31,13 +31,10 @@ namespace frc {
|
||||
* "width" and "length" values. This is to account for arbitrary field sizes
|
||||
* when transforming the poses.
|
||||
*
|
||||
* Pose3ds are assumed to be measured from the bottom-left corner of the field,
|
||||
* when the blue alliance is at the left. By default, Pose3ds will be returned
|
||||
* as declared when calling GetTagPose(int).
|
||||
* SetOrigin(AprilTagFieldLayout::OriginPosition) can be used to transform the
|
||||
* poses returned by GetTagPose(int) to be correct relative to a different
|
||||
* coordinate frame.
|
||||
*/
|
||||
* Pose3ds in the JSON are measured using the normal FRC coordinate system, NWU
|
||||
* with the origin at the bottom-right corner of the blue alliance wall.
|
||||
* SetOrigin(OriginPosition) can be used to change the poses returned from
|
||||
* GetTagPose(int) to be from the perspective of a specific alliance. */
|
||||
class WPILIB_DLLEXPORT AprilTagFieldLayout {
|
||||
public:
|
||||
enum class OriginPosition {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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 edu.wpi.first.apriltag.jni;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class JNITest {
|
||||
@Test
|
||||
void jniLinkTest() {
|
||||
// Test to verify that the JNI test link works correctly.
|
||||
var detector = AprilTagJNI.aprilTagCreate("tag16h5", 2.0, 0.0, 1, false, false);
|
||||
AprilTagJNI.aprilTagDestroy(detector);
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ class FMSModel : public Model {
|
||||
virtual void SetEnabled(bool val) = 0;
|
||||
virtual void SetTest(bool val) = 0;
|
||||
virtual void SetAutonomous(bool val) = 0;
|
||||
virtual void SetGameSpecificMessage(const char* val) = 0;
|
||||
virtual void SetGameSpecificMessage(std::string_view val) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,8 +15,8 @@ NTDigitalInputModel::NTDigitalInputModel(std::string_view path)
|
||||
NTDigitalInputModel::NTDigitalInputModel(nt::NetworkTableInstance inst,
|
||||
std::string_view path)
|
||||
: m_inst{inst},
|
||||
m_value{inst.GetBooleanTopic(fmt::format("{}/Value", path))
|
||||
.Subscribe(false, {{nt::PubSubOption::SendAll(true)}})},
|
||||
m_value{
|
||||
inst.GetBooleanTopic(fmt::format("{}/Value", path)).Subscribe(false)},
|
||||
m_name{inst.GetStringTopic(fmt::format("{}/.name", path)).Subscribe("")},
|
||||
m_valueData{fmt::format("NT_DIn:{}", path)},
|
||||
m_nameValue{wpi::rsplit(path, '/').second} {
|
||||
|
||||
@@ -14,8 +14,8 @@ NTDigitalOutputModel::NTDigitalOutputModel(std::string_view path)
|
||||
NTDigitalOutputModel::NTDigitalOutputModel(nt::NetworkTableInstance inst,
|
||||
std::string_view path)
|
||||
: m_inst{inst},
|
||||
m_value{inst.GetBooleanTopic(fmt::format("{}/Value", path))
|
||||
.GetEntry(false, {{nt::PubSubOption::SendAll(true)}})},
|
||||
m_value{
|
||||
inst.GetBooleanTopic(fmt::format("{}/Value", path)).GetEntry(false)},
|
||||
m_name{inst.GetStringTopic(fmt::format("{}/.name", path)).Subscribe("")},
|
||||
m_controllable{inst.GetBooleanTopic(fmt::format("{}/.controllable", path))
|
||||
.Subscribe(false)},
|
||||
|
||||
@@ -21,11 +21,11 @@ NTFMSModel::NTFMSModel(nt::NetworkTableInstance inst, std::string_view path)
|
||||
inst.GetStringTopic(fmt::format("{}/GameSpecificMessage", path))
|
||||
.Subscribe("")},
|
||||
m_alliance{inst.GetBooleanTopic(fmt::format("{}/IsRedAlliance", path))
|
||||
.Subscribe(false, {{nt::PubSubOption::SendAll(true)}})},
|
||||
.Subscribe(false)},
|
||||
m_station{inst.GetIntegerTopic(fmt::format("{}/StationNumber", path))
|
||||
.Subscribe(0, {{nt::PubSubOption::SendAll(true)}})},
|
||||
.Subscribe(0)},
|
||||
m_controlWord{inst.GetIntegerTopic(fmt::format("{}/FMSControlData", path))
|
||||
.Subscribe(0, {{nt::PubSubOption::SendAll(true)}})},
|
||||
.Subscribe(0)},
|
||||
m_fmsAttached{fmt::format("NT_FMS:FMSAttached:{}", path)},
|
||||
m_dsAttached{fmt::format("NT_FMS:DSAttached:{}", path)},
|
||||
m_allianceStationId{fmt::format("NT_FMS:AllianceStationID:{}", path)},
|
||||
|
||||
@@ -112,10 +112,7 @@ NTField2DModel::NTField2DModel(nt::NetworkTableInstance inst,
|
||||
std::string_view path)
|
||||
: m_path{fmt::format("{}/", path)},
|
||||
m_inst{inst},
|
||||
m_tableSub{inst,
|
||||
{{m_path}},
|
||||
{{nt::PubSubOption::SendAll(true),
|
||||
nt::PubSubOption::Periodic(0.05)}}},
|
||||
m_tableSub{inst, {{m_path}}, {.periodic = 0.05, .sendAll = true}},
|
||||
m_nameTopic{inst.GetTopic(fmt::format("{}/.name", path))},
|
||||
m_poller{inst} {
|
||||
m_poller.AddListener(m_tableSub, nt::EventFlags::kTopic |
|
||||
|
||||
@@ -14,8 +14,7 @@ NTGyroModel::NTGyroModel(std::string_view path)
|
||||
|
||||
NTGyroModel::NTGyroModel(nt::NetworkTableInstance inst, std::string_view path)
|
||||
: m_inst{inst},
|
||||
m_angle{inst.GetDoubleTopic(fmt::format("{}/Value", path))
|
||||
.Subscribe(0, {{nt::PubSubOption::SendAll(true)}})},
|
||||
m_angle{inst.GetDoubleTopic(fmt::format("{}/Value", path)).Subscribe(0)},
|
||||
m_name{inst.GetStringTopic(fmt::format("{}/.name", path)).Subscribe({})},
|
||||
m_angleData{fmt::format("NT_Gyro:{}", path)},
|
||||
m_nameValue{wpi::rsplit(path, '/').second} {}
|
||||
|
||||
@@ -22,16 +22,16 @@ NTMecanumDriveModel::NTMecanumDriveModel(nt::NetworkTableInstance inst,
|
||||
.Subscribe(0)},
|
||||
m_flPercent{
|
||||
inst.GetDoubleTopic(fmt::format("{}/Front Left Motor Speed", path))
|
||||
.GetEntry(0, {{nt::PubSubOption::SendAll(true)}})},
|
||||
.GetEntry(0)},
|
||||
m_frPercent{
|
||||
inst.GetDoubleTopic(fmt::format("{}/Front Right Motor Speed", path))
|
||||
.GetEntry(0, {{nt::PubSubOption::SendAll(true)}})},
|
||||
.GetEntry(0)},
|
||||
m_rlPercent{
|
||||
inst.GetDoubleTopic(fmt::format("{}/Rear Left Motor Speed", path))
|
||||
.GetEntry(0, {{nt::PubSubOption::SendAll(true)}})},
|
||||
.GetEntry(0)},
|
||||
m_rrPercent{
|
||||
inst.GetDoubleTopic(fmt::format("{}/Rear Right Motor Speed", path))
|
||||
.GetEntry(0, {{nt::PubSubOption::SendAll(true)}})},
|
||||
.GetEntry(0)},
|
||||
m_nameValue{wpi::rsplit(path, '/').second},
|
||||
m_flPercentData{fmt::format("NTMcnmDriveFL:{}", path)},
|
||||
m_frPercentData{fmt::format("NTMcnmDriveFR:{}", path)},
|
||||
|
||||
@@ -240,10 +240,7 @@ NTMechanism2DModel::NTMechanism2DModel(nt::NetworkTableInstance inst,
|
||||
std::string_view path)
|
||||
: m_inst{inst},
|
||||
m_path{fmt::format("{}/", path)},
|
||||
m_tableSub{inst,
|
||||
{{m_path}},
|
||||
{{nt::PubSubOption::SendAll(true),
|
||||
nt::PubSubOption::Periodic(0.05)}}},
|
||||
m_tableSub{inst, {{m_path}}, {.periodic = 0.05, .sendAll = true}},
|
||||
m_nameTopic{m_inst.GetTopic(fmt::format("{}/.name", path))},
|
||||
m_dimensionsTopic{m_inst.GetTopic(fmt::format("{}/dims", path))},
|
||||
m_bgColorTopic{m_inst.GetTopic(fmt::format("{}/backgroundColor", path))},
|
||||
|
||||
@@ -15,8 +15,7 @@ NTMotorControllerModel::NTMotorControllerModel(std::string_view path)
|
||||
NTMotorControllerModel::NTMotorControllerModel(nt::NetworkTableInstance inst,
|
||||
std::string_view path)
|
||||
: m_inst{inst},
|
||||
m_value{inst.GetDoubleTopic(fmt::format("{}/Value", path))
|
||||
.GetEntry(0, {{nt::PubSubOption::SendAll(true)}})},
|
||||
m_value{inst.GetDoubleTopic(fmt::format("{}/Value", path)).GetEntry(0)},
|
||||
m_name{inst.GetStringTopic(fmt::format("{}/.name", path)).Subscribe("")},
|
||||
m_controllable{inst.GetBooleanTopic(fmt::format("{}/.controllable", path))
|
||||
.Subscribe(false)},
|
||||
|
||||
@@ -18,14 +18,11 @@ NTPIDControllerModel::NTPIDControllerModel(nt::NetworkTableInstance inst,
|
||||
m_name{inst.GetStringTopic(fmt::format("{}/.name", path)).Subscribe("")},
|
||||
m_controllable{inst.GetBooleanTopic(fmt::format("{}/.controllable", path))
|
||||
.Subscribe(false)},
|
||||
m_p{inst.GetDoubleTopic(fmt::format("{}/p", path))
|
||||
.GetEntry(0, {{nt::PubSubOption::SendAll(true)}})},
|
||||
m_i{inst.GetDoubleTopic(fmt::format("{}/i", path))
|
||||
.GetEntry(0, {{nt::PubSubOption::SendAll(true)}})},
|
||||
m_d{inst.GetDoubleTopic(fmt::format("{}/d", path))
|
||||
.GetEntry(0, {{nt::PubSubOption::SendAll(true)}})},
|
||||
m_setpoint{inst.GetDoubleTopic(fmt::format("{}/setpoint", path))
|
||||
.GetEntry(0, {{nt::PubSubOption::SendAll(true)}})},
|
||||
m_p{inst.GetDoubleTopic(fmt::format("{}/p", path)).GetEntry(0)},
|
||||
m_i{inst.GetDoubleTopic(fmt::format("{}/i", path)).GetEntry(0)},
|
||||
m_d{inst.GetDoubleTopic(fmt::format("{}/d", path)).GetEntry(0)},
|
||||
m_setpoint{
|
||||
inst.GetDoubleTopic(fmt::format("{}/setpoint", path)).GetEntry(0)},
|
||||
m_pData{fmt::format("NTPIDCtrlP:{}", path)},
|
||||
m_iData{fmt::format("NTPIDCtrlI:{}", path)},
|
||||
m_dData{fmt::format("NTPIDCtrlD:{}", path)},
|
||||
|
||||
@@ -478,6 +478,10 @@ void NetworkTablesModel::Update() {
|
||||
entry->info.type_str == "msgpack") {
|
||||
// meta topic handling
|
||||
if (entry->info.name == "$clients") {
|
||||
// need to remove deleted entries as UpdateClients() uses GetEntry()
|
||||
if (updateTree) {
|
||||
std::erase(m_sortedEntries, nullptr);
|
||||
}
|
||||
UpdateClients(entry->value.GetRaw());
|
||||
} else if (entry->info.name == "$serverpub") {
|
||||
m_server.UpdatePublishers(entry->value.GetRaw());
|
||||
@@ -505,9 +509,7 @@ void NetworkTablesModel::Update() {
|
||||
}
|
||||
|
||||
// remove deleted entries
|
||||
m_sortedEntries.erase(
|
||||
std::remove(m_sortedEntries.begin(), m_sortedEntries.end(), nullptr),
|
||||
m_sortedEntries.end());
|
||||
std::erase(m_sortedEntries, nullptr);
|
||||
|
||||
RebuildTree();
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ class NTFMSModel : public FMSModel {
|
||||
void SetEnabled(bool val) override {}
|
||||
void SetTest(bool val) override {}
|
||||
void SetAutonomous(bool val) override {}
|
||||
void SetGameSpecificMessage(const char* val) override {}
|
||||
void SetGameSpecificMessage(std::string_view val) override {}
|
||||
|
||||
void Update() override;
|
||||
bool Exists() override;
|
||||
|
||||
@@ -39,5 +39,7 @@ public class DIOJNI extends JNIWrapper {
|
||||
|
||||
public static native void setDigitalPWMDutyCycle(int pwmGenerator, double dutyCycle);
|
||||
|
||||
public static native void setDigitalPWMPPS(int pwmGenerator, double dutyCycle);
|
||||
|
||||
public static native void setDigitalPWMOutputChannel(int pwmGenerator, int channel);
|
||||
}
|
||||
|
||||
@@ -240,6 +240,30 @@ void HAL_SetDigitalPWMDutyCycle(HAL_DigitalPWMHandle pwmGenerator,
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_SetDigitalPWMPPS(HAL_DigitalPWMHandle pwmGenerator, double dutyCycle,
|
||||
int32_t* status) {
|
||||
auto port = digitalPWMHandles->Get(pwmGenerator);
|
||||
if (port == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return;
|
||||
}
|
||||
int32_t id = *port;
|
||||
digitalSystem->writePWMPeriodPower(0xffff, status);
|
||||
double rawDutyCycle = 31.0 * dutyCycle;
|
||||
if (rawDutyCycle > 30.5) {
|
||||
rawDutyCycle = 30.5;
|
||||
}
|
||||
{
|
||||
std::scoped_lock lock(digitalPwmMutex);
|
||||
if (id < 4)
|
||||
digitalSystem->writePWMDutyCycleA(id, static_cast<uint8_t>(rawDutyCycle),
|
||||
status);
|
||||
else
|
||||
digitalSystem->writePWMDutyCycleB(
|
||||
id - 4, static_cast<uint8_t>(rawDutyCycle), status);
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_SetDigitalPWMOutputChannel(HAL_DigitalPWMHandle pwmGenerator,
|
||||
int32_t channel, int32_t* status) {
|
||||
auto port = digitalPWMHandles->Get(pwmGenerator);
|
||||
|
||||
@@ -111,24 +111,12 @@ int32_t HAL_GetDutyCycleHighTime(HAL_DutyCycleHandle dutyCycleHandle,
|
||||
|
||||
// TODO Handle Overflow
|
||||
unsigned char overflow = 0;
|
||||
uint32_t freq0 = dutyCycle->dutyCycle->readFrequency(&overflow, status);
|
||||
uint32_t output = dutyCycle->dutyCycle->readOutput(&overflow, status);
|
||||
uint32_t freq1 = dutyCycle->dutyCycle->readFrequency(&overflow, status);
|
||||
uint32_t highTime = dutyCycle->dutyCycle->readHighTicks(&overflow, status);
|
||||
if (*status != 0) {
|
||||
return 0;
|
||||
}
|
||||
if (freq0 != freq1) {
|
||||
// Frequency rolled over. Reread output
|
||||
output = dutyCycle->dutyCycle->readOutput(&overflow, status);
|
||||
if (*status != 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (freq1 == 0) {
|
||||
return 0;
|
||||
}
|
||||
// Output will be at max 4e7, so x25 will still fit in a 32 bit signed int.
|
||||
return (output / freq1) * 25;
|
||||
return highTime * 25;
|
||||
}
|
||||
|
||||
int32_t HAL_GetDutyCycleOutputScaleFactor(HAL_DutyCycleHandle dutyCycleHandle,
|
||||
|
||||
@@ -43,7 +43,6 @@ struct JoystickDataCache {
|
||||
HAL_JoystickButtons buttons[HAL_kMaxJoysticks];
|
||||
HAL_AllianceStationID allianceStation;
|
||||
float matchTime;
|
||||
bool updated;
|
||||
};
|
||||
static_assert(std::is_standard_layout_v<JoystickDataCache>);
|
||||
// static_assert(std::is_trivial_v<JoystickDataCache>);
|
||||
@@ -114,7 +113,9 @@ void JoystickDataCache::Update() {
|
||||
static HAL_ControlWord newestControlWord;
|
||||
static JoystickDataCache caches[3];
|
||||
static JoystickDataCache* currentRead = &caches[0];
|
||||
static JoystickDataCache* currentCache = &caches[1];
|
||||
static JoystickDataCache* currentReadLocal = &caches[0];
|
||||
static std::atomic<JoystickDataCache*> currentCache{&caches[1]};
|
||||
static JoystickDataCache* lastGiven = &caches[1];
|
||||
static JoystickDataCache* cacheToUpdate = &caches[2];
|
||||
|
||||
static wpi::mutex cacheMutex;
|
||||
@@ -171,6 +172,38 @@ static int32_t HAL_GetMatchInfoInternal(HAL_MatchInfo* info) {
|
||||
return status;
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct TcpCache {
|
||||
TcpCache() { std::memset(this, 0, sizeof(*this)); }
|
||||
void Update(uint32_t mask);
|
||||
void CloneTo(TcpCache* other) { std::memcpy(other, this, sizeof(*this)); }
|
||||
|
||||
HAL_MatchInfo matchInfo;
|
||||
HAL_JoystickDescriptor descriptors[HAL_kMaxJoysticks];
|
||||
};
|
||||
static_assert(std::is_standard_layout_v<TcpCache>);
|
||||
} // namespace
|
||||
|
||||
static std::atomic_uint32_t tcpMask{0xFFFFFFFF};
|
||||
static TcpCache tcpCache;
|
||||
static TcpCache tcpCurrent;
|
||||
static wpi::mutex tcpCacheMutex;
|
||||
|
||||
constexpr uint32_t combinedMatchInfoMask = kTcpRecvMask_MatchInfoOld |
|
||||
kTcpRecvMask_MatchInfo |
|
||||
kTcpRecvMask_GameSpecific;
|
||||
|
||||
void TcpCache::Update(uint32_t mask) {
|
||||
if ((mask & combinedMatchInfoMask) != 0) {
|
||||
HAL_GetMatchInfoInternal(&matchInfo);
|
||||
}
|
||||
for (int i = 0; i < HAL_kMaxJoysticks; i++) {
|
||||
if ((mask & (1 << i)) != 0) {
|
||||
HAL_GetJoystickDescriptorInternal(i, &descriptors[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace hal::init {
|
||||
void InitializeFRCDriverStation() {
|
||||
std::memset(&newestControlWord, 0, sizeof(newestControlWord));
|
||||
@@ -339,11 +372,16 @@ void HAL_GetAllJoystickData(HAL_JoystickAxes* axes, HAL_JoystickPOVs* povs,
|
||||
|
||||
int32_t HAL_GetJoystickDescriptor(int32_t joystickNum,
|
||||
HAL_JoystickDescriptor* desc) {
|
||||
return HAL_GetJoystickDescriptorInternal(joystickNum, desc);
|
||||
CHECK_JOYSTICK_NUMBER(joystickNum);
|
||||
std::scoped_lock lock{tcpCacheMutex};
|
||||
*desc = tcpCurrent.descriptors[joystickNum];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t HAL_GetMatchInfo(HAL_MatchInfo* info) {
|
||||
return HAL_GetMatchInfoInternal(info);
|
||||
std::scoped_lock lock{tcpCacheMutex};
|
||||
*info = tcpCurrent.matchInfo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
HAL_AllianceStationID HAL_GetAllianceStation(int32_t* status) {
|
||||
@@ -388,7 +426,6 @@ void HAL_FreeJoystickName(char* name) {
|
||||
}
|
||||
|
||||
int32_t HAL_GetJoystickAxisType(int32_t joystickNum, int32_t axis) {
|
||||
CHECK_JOYSTICK_NUMBER(joystickNum);
|
||||
HAL_JoystickDescriptor joystickDesc;
|
||||
if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
|
||||
return -1;
|
||||
@@ -431,20 +468,44 @@ void HAL_ObserveUserProgramTest(void) {
|
||||
|
||||
// Constant number to be used for our occur handle
|
||||
constexpr int32_t refNumber = 42;
|
||||
constexpr int32_t tcpRefNumber = 94;
|
||||
|
||||
static void tcpOccur(void) {
|
||||
uint32_t mask = FRC_NetworkCommunication_getNewTcpRecvMask();
|
||||
tcpMask.fetch_or(mask);
|
||||
}
|
||||
|
||||
static void udpOccur(void) {
|
||||
cacheToUpdate->Update();
|
||||
|
||||
JoystickDataCache* given = cacheToUpdate;
|
||||
JoystickDataCache* prev = currentCache.exchange(cacheToUpdate);
|
||||
if (prev == nullptr) {
|
||||
cacheToUpdate = currentReadLocal;
|
||||
currentReadLocal = lastGiven;
|
||||
} else {
|
||||
// Current read local does not update
|
||||
cacheToUpdate = prev;
|
||||
}
|
||||
lastGiven = given;
|
||||
|
||||
driverStation->newDataEvents.Wakeup();
|
||||
}
|
||||
|
||||
static void newDataOccur(uint32_t refNum) {
|
||||
// Since we could get other values, require our specific handle
|
||||
// to signal our threads
|
||||
if (refNum != refNumber) {
|
||||
return;
|
||||
switch (refNum) {
|
||||
case refNumber:
|
||||
udpOccur();
|
||||
break;
|
||||
|
||||
case tcpRefNumber:
|
||||
tcpOccur();
|
||||
break;
|
||||
|
||||
default:
|
||||
std::printf("Unknown occur %u\n", refNum);
|
||||
break;
|
||||
}
|
||||
cacheToUpdate->Update();
|
||||
{
|
||||
std::scoped_lock lock{cacheMutex};
|
||||
std::swap(currentCache, cacheToUpdate);
|
||||
currentCache->updated = true;
|
||||
}
|
||||
driverStation->newDataEvents.Wakeup();
|
||||
}
|
||||
|
||||
void HAL_RefreshDSData(void) {
|
||||
@@ -453,11 +514,18 @@ void HAL_RefreshDSData(void) {
|
||||
FRC_NetworkCommunication_getControlWord(
|
||||
reinterpret_cast<ControlWord_t*>(&controlWord));
|
||||
std::scoped_lock lock{cacheMutex};
|
||||
if (currentCache->updated) {
|
||||
std::swap(currentCache, currentRead);
|
||||
currentCache->updated = false;
|
||||
JoystickDataCache* prev = currentCache.exchange(nullptr);
|
||||
if (prev != nullptr) {
|
||||
currentRead = prev;
|
||||
}
|
||||
newestControlWord = controlWord;
|
||||
|
||||
uint32_t mask = tcpMask.exchange(0);
|
||||
if (mask != 0) {
|
||||
tcpCache.Update(mask);
|
||||
std::scoped_lock tcpLock(tcpCacheMutex);
|
||||
tcpCache.CloneTo(&tcpCurrent);
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_ProvideNewDataEventHandle(WPI_EventHandle handle) {
|
||||
@@ -481,5 +549,6 @@ void InitializeDriverStation() {
|
||||
NetCommRPCProxy_SetOccurFuncPointer(newDataOccur);
|
||||
// Set up our occur reference number
|
||||
setNewDataOccurRef(refNumber);
|
||||
FRC_NetworkCommunication_setNewTcpDataOccurRef(tcpRefNumber);
|
||||
}
|
||||
} // namespace hal
|
||||
|
||||
@@ -103,13 +103,13 @@ void HALSIM_SetJoystickIsXbox(int32_t stick, HAL_Bool isXbox) {}
|
||||
|
||||
void HALSIM_SetJoystickType(int32_t stick, int32_t type) {}
|
||||
|
||||
void HALSIM_SetJoystickName(int32_t stick, const char* name) {}
|
||||
void HALSIM_SetJoystickName(int32_t stick, const char* name, size_t size) {}
|
||||
|
||||
void HALSIM_SetJoystickAxisType(int32_t stick, int32_t axis, int32_t type) {}
|
||||
|
||||
void HALSIM_SetGameSpecificMessage(const char* message) {}
|
||||
void HALSIM_SetGameSpecificMessage(const char* message, size_t size) {}
|
||||
|
||||
void HALSIM_SetEventName(const char* name) {}
|
||||
void HALSIM_SetEventName(const char* name, size_t size) {}
|
||||
|
||||
void HALSIM_SetMatchType(HAL_MatchType type) {}
|
||||
|
||||
|
||||
@@ -260,6 +260,20 @@ Java_edu_wpi_first_hal_DIOJNI_setDigitalPWMDutyCycle
|
||||
CheckStatus(env, status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_DIOJNI
|
||||
* Method: setDigitalPWMPPS
|
||||
* Signature: (ID)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_DIOJNI_setDigitalPWMPPS
|
||||
(JNIEnv* env, jclass, jint id, jdouble value)
|
||||
{
|
||||
int32_t status = 0;
|
||||
HAL_SetDigitalPWMPPS((HAL_DigitalPWMHandle)id, value, &status);
|
||||
CheckStatus(env, status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_DIOJNI
|
||||
* Method: setDigitalPWMOutputChannel
|
||||
|
||||
@@ -732,7 +732,8 @@ JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_DriverStationDataJNI_setJoystickName
|
||||
(JNIEnv* env, jclass, jint stick, jstring name)
|
||||
{
|
||||
HALSIM_SetJoystickName(stick, JStringRef{env, name}.c_str());
|
||||
JStringRef nameJString{env, name};
|
||||
HALSIM_SetJoystickName(stick, nameJString.c_str(), nameJString.size());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -756,7 +757,8 @@ JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_DriverStationDataJNI_setGameSpecificMessage
|
||||
(JNIEnv* env, jclass, jstring message)
|
||||
{
|
||||
HALSIM_SetGameSpecificMessage(JStringRef{env, message}.c_str());
|
||||
JStringRef messageJString{env, message};
|
||||
HALSIM_SetGameSpecificMessage(messageJString.c_str(), messageJString.size());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -768,7 +770,8 @@ JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_DriverStationDataJNI_setEventName
|
||||
(JNIEnv* env, jclass, jstring name)
|
||||
{
|
||||
HALSIM_SetEventName(JStringRef{env, name}.c_str());
|
||||
JStringRef nameJString{env, name};
|
||||
HALSIM_SetEventName(nameJString.c_str(), nameJString.size());
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -94,6 +94,16 @@ void HAL_SetDigitalPWMRate(double rate, int32_t* status);
|
||||
void HAL_SetDigitalPWMDutyCycle(HAL_DigitalPWMHandle pwmGenerator,
|
||||
double dutyCycle, int32_t* status);
|
||||
|
||||
/**
|
||||
* Configures the digital PWM to be a PPS signal with specified duty cycle.
|
||||
*
|
||||
* @param[in] pwmGenerator the digital PWM handle
|
||||
* @param[in] dutyCycle the percent duty cycle to output [0..1]
|
||||
* @param[out] status Error status variable. 0 on success.
|
||||
*/
|
||||
void HAL_SetDigitalPWMPPS(HAL_DigitalPWMHandle pwmGenerator, double dutyCycle,
|
||||
int32_t* status);
|
||||
|
||||
/**
|
||||
* Configures which DO channel the PWM signal is output on.
|
||||
*
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "hal/DriverStationTypes.h"
|
||||
#include "hal/Types.h"
|
||||
#include "hal/simulation/NotifyListener.h"
|
||||
@@ -145,11 +147,11 @@ void HALSIM_GetJoystickCounts(int32_t stick, int32_t* axisCount,
|
||||
|
||||
void HALSIM_SetJoystickIsXbox(int32_t stick, HAL_Bool isXbox);
|
||||
void HALSIM_SetJoystickType(int32_t stick, int32_t type);
|
||||
void HALSIM_SetJoystickName(int32_t stick, const char* name);
|
||||
void HALSIM_SetJoystickName(int32_t stick, const char* name, size_t size);
|
||||
void HALSIM_SetJoystickAxisType(int32_t stick, int32_t axis, int32_t type);
|
||||
|
||||
void HALSIM_SetGameSpecificMessage(const char* message);
|
||||
void HALSIM_SetEventName(const char* name);
|
||||
void HALSIM_SetGameSpecificMessage(const char* message, size_t size);
|
||||
void HALSIM_SetEventName(const char* name, size_t size);
|
||||
void HALSIM_SetMatchType(HAL_MatchType type);
|
||||
void HALSIM_SetMatchNumber(int32_t matchNumber);
|
||||
void HALSIM_SetReplayNumber(int32_t replayNumber);
|
||||
|
||||
@@ -151,6 +151,23 @@ void HAL_SetDigitalPWMDutyCycle(HAL_DigitalPWMHandle pwmGenerator,
|
||||
SimDigitalPWMData[id].dutyCycle = dutyCycle;
|
||||
}
|
||||
|
||||
void HAL_SetDigitalPWMPPS(HAL_DigitalPWMHandle pwmGenerator, double dutyCycle,
|
||||
int32_t* status) {
|
||||
auto port = digitalPWMHandles->Get(pwmGenerator);
|
||||
if (port == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return;
|
||||
}
|
||||
int32_t id = *port;
|
||||
if (dutyCycle > 1.0) {
|
||||
dutyCycle = 1.0;
|
||||
}
|
||||
if (dutyCycle < 0.0) {
|
||||
dutyCycle = 0.0;
|
||||
}
|
||||
SimDigitalPWMData[id].dutyCycle = dutyCycle;
|
||||
}
|
||||
|
||||
void HAL_SetDigitalPWMOutputChannel(HAL_DigitalPWMHandle pwmGenerator,
|
||||
int32_t channel, int32_t* status) {
|
||||
auto port = digitalPWMHandles->Get(pwmGenerator);
|
||||
|
||||
@@ -44,7 +44,6 @@ struct JoystickDataCache {
|
||||
HAL_JoystickButtons buttons[kJoystickPorts];
|
||||
HAL_AllianceStationID allianceStation;
|
||||
double matchTime;
|
||||
bool updated;
|
||||
};
|
||||
static_assert(std::is_standard_layout_v<JoystickDataCache>);
|
||||
// static_assert(std::is_trivial_v<JoystickDataCache>);
|
||||
@@ -75,7 +74,9 @@ void JoystickDataCache::Update() {
|
||||
static HAL_ControlWord newestControlWord;
|
||||
static JoystickDataCache caches[3];
|
||||
static JoystickDataCache* currentRead = &caches[0];
|
||||
static JoystickDataCache* currentCache = &caches[1];
|
||||
static JoystickDataCache* currentReadLocal = &caches[0];
|
||||
static std::atomic<JoystickDataCache*> currentCache{&caches[1]};
|
||||
static JoystickDataCache* lastGiven = &caches[1];
|
||||
static JoystickDataCache* cacheToUpdate = &caches[2];
|
||||
|
||||
static ::FRCDriverStation* driverStation;
|
||||
@@ -330,9 +331,9 @@ void HAL_RefreshDSData(void) {
|
||||
controlWord.fmsAttached = SimDriverStationData->fmsAttached;
|
||||
controlWord.dsAttached = SimDriverStationData->dsAttached;
|
||||
std::scoped_lock lock{driverStation->cacheMutex};
|
||||
if (currentCache->updated) {
|
||||
std::swap(currentCache, currentRead);
|
||||
currentCache->updated = false;
|
||||
JoystickDataCache* prev = currentCache.exchange(nullptr);
|
||||
if (prev != nullptr) {
|
||||
currentRead = prev;
|
||||
}
|
||||
newestControlWord = controlWord;
|
||||
}
|
||||
@@ -368,11 +369,18 @@ void NewDriverStationData() {
|
||||
return;
|
||||
}
|
||||
cacheToUpdate->Update();
|
||||
{
|
||||
std::scoped_lock lock{driverStation->cacheMutex};
|
||||
std::swap(currentCache, cacheToUpdate);
|
||||
currentCache->updated = true;
|
||||
|
||||
JoystickDataCache* given = cacheToUpdate;
|
||||
JoystickDataCache* prev = currentCache.exchange(cacheToUpdate);
|
||||
if (prev == nullptr) {
|
||||
cacheToUpdate = currentReadLocal;
|
||||
currentReadLocal = lastGiven;
|
||||
} else {
|
||||
// Current read local does not update
|
||||
cacheToUpdate = prev;
|
||||
}
|
||||
lastGiven = given;
|
||||
|
||||
driverStation->newDataEvents.Wakeup();
|
||||
SimDriverStationData->CallNewDataCallbacks();
|
||||
}
|
||||
|
||||
@@ -339,14 +339,17 @@ void DriverStationData::SetJoystickType(int32_t stick, int32_t type) {
|
||||
m_joystickDescriptorCallbacks(stick, &m_joystickData[stick].descriptor);
|
||||
}
|
||||
|
||||
void DriverStationData::SetJoystickName(int32_t stick, const char* name) {
|
||||
void DriverStationData::SetJoystickName(int32_t stick, const char* name,
|
||||
size_t size) {
|
||||
if (stick < 0 || stick >= kNumJoysticks) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock(m_joystickDataMutex);
|
||||
std::strncpy(m_joystickData[stick].descriptor.name, name,
|
||||
sizeof(m_joystickData[stick].descriptor.name) - 1);
|
||||
*(std::end(m_joystickData[stick].descriptor.name) - 1) = '\0';
|
||||
if (size > sizeof(m_joystickData[stick].descriptor.name) - 1) {
|
||||
size = sizeof(m_joystickData[stick].descriptor.name) - 1;
|
||||
}
|
||||
std::strncpy(m_joystickData[stick].descriptor.name, name, size);
|
||||
m_joystickData[stick].descriptor.name[size] = '\0';
|
||||
m_joystickDescriptorCallbacks(stick, &m_joystickData[stick].descriptor);
|
||||
}
|
||||
|
||||
@@ -363,19 +366,27 @@ void DriverStationData::SetJoystickAxisType(int32_t stick, int32_t axis,
|
||||
m_joystickDescriptorCallbacks(stick, &m_joystickData[stick].descriptor);
|
||||
}
|
||||
|
||||
void DriverStationData::SetGameSpecificMessage(const char* message) {
|
||||
void DriverStationData::SetGameSpecificMessage(const char* message,
|
||||
size_t size) {
|
||||
std::scoped_lock lock(m_matchInfoMutex);
|
||||
if (size > sizeof(m_matchInfo.gameSpecificMessage) - 1) {
|
||||
size = sizeof(m_matchInfo.gameSpecificMessage) - 1;
|
||||
}
|
||||
std::strncpy(reinterpret_cast<char*>(m_matchInfo.gameSpecificMessage),
|
||||
message, sizeof(m_matchInfo.gameSpecificMessage) - 1);
|
||||
*(std::end(m_matchInfo.gameSpecificMessage) - 1) = '\0';
|
||||
m_matchInfo.gameSpecificMessageSize = std::strlen(message);
|
||||
message, size);
|
||||
m_matchInfo.gameSpecificMessage[size] = '\0';
|
||||
m_matchInfo.gameSpecificMessageSize =
|
||||
std::strlen(reinterpret_cast<char*>(m_matchInfo.gameSpecificMessage));
|
||||
m_matchInfoCallbacks(&m_matchInfo);
|
||||
}
|
||||
|
||||
void DriverStationData::SetEventName(const char* name) {
|
||||
void DriverStationData::SetEventName(const char* name, size_t size) {
|
||||
std::scoped_lock lock(m_matchInfoMutex);
|
||||
std::strncpy(m_matchInfo.eventName, name, sizeof(m_matchInfo.eventName) - 1);
|
||||
*(std::end(m_matchInfo.eventName) - 1) = '\0';
|
||||
if (size > sizeof(m_matchInfo.eventName) - 1) {
|
||||
size = sizeof(m_matchInfo.eventName) - 1;
|
||||
}
|
||||
std::strncpy(m_matchInfo.eventName, name, size);
|
||||
m_matchInfo.eventName[size] = '\0';
|
||||
m_matchInfoCallbacks(&m_matchInfo);
|
||||
}
|
||||
|
||||
@@ -540,20 +551,20 @@ void HALSIM_SetJoystickType(int32_t stick, int32_t type) {
|
||||
SimDriverStationData->SetJoystickType(stick, type);
|
||||
}
|
||||
|
||||
void HALSIM_SetJoystickName(int32_t stick, const char* name) {
|
||||
SimDriverStationData->SetJoystickName(stick, name);
|
||||
void HALSIM_SetJoystickName(int32_t stick, const char* name, size_t size) {
|
||||
SimDriverStationData->SetJoystickName(stick, name, size);
|
||||
}
|
||||
|
||||
void HALSIM_SetJoystickAxisType(int32_t stick, int32_t axis, int32_t type) {
|
||||
SimDriverStationData->SetJoystickAxisType(stick, axis, type);
|
||||
}
|
||||
|
||||
void HALSIM_SetGameSpecificMessage(const char* message) {
|
||||
SimDriverStationData->SetGameSpecificMessage(message);
|
||||
void HALSIM_SetGameSpecificMessage(const char* message, size_t size) {
|
||||
SimDriverStationData->SetGameSpecificMessage(message, size);
|
||||
}
|
||||
|
||||
void HALSIM_SetEventName(const char* name) {
|
||||
SimDriverStationData->SetEventName(name);
|
||||
void HALSIM_SetEventName(const char* name, size_t size) {
|
||||
SimDriverStationData->SetEventName(name, size);
|
||||
}
|
||||
|
||||
void HALSIM_SetMatchType(HAL_MatchType type) {
|
||||
|
||||
@@ -107,11 +107,11 @@ class DriverStationData {
|
||||
|
||||
void SetJoystickIsXbox(int32_t stick, HAL_Bool isXbox);
|
||||
void SetJoystickType(int32_t stick, int32_t type);
|
||||
void SetJoystickName(int32_t stick, const char* name);
|
||||
void SetJoystickName(int32_t stick, const char* name, size_t size);
|
||||
void SetJoystickAxisType(int32_t stick, int32_t axis, int32_t type);
|
||||
|
||||
void SetGameSpecificMessage(const char* message);
|
||||
void SetEventName(const char* name);
|
||||
void SetGameSpecificMessage(const char* message, size_t size);
|
||||
void SetEventName(const char* name, size_t size);
|
||||
void SetMatchType(HAL_MatchType type);
|
||||
void SetMatchNumber(int32_t matchNumber);
|
||||
void SetReplayNumber(int32_t replayNumber);
|
||||
|
||||
@@ -63,8 +63,7 @@ void bench() {
|
||||
// add "typical" set of subscribers on client and server
|
||||
nt::SubscribeMultiple(client, {{std::string_view{}}});
|
||||
nt::Subscribe(nt::GetTopic(client, "highrate"), NT_DOUBLE, "double",
|
||||
{{nt::PubSubOption::KeepDuplicates(true),
|
||||
nt::PubSubOption::SendAll(true)}});
|
||||
{.sendAll = true, .keepDuplicates = true});
|
||||
nt::SubscribeMultiple(server, {{std::string_view{}}});
|
||||
auto pub = nt::Publish(nt::GetTopic(server, "highrate"), NT_DOUBLE, "double");
|
||||
nt::SetDouble(pub, 0);
|
||||
|
||||
@@ -311,7 +311,7 @@ class {{ TypeName }}Topic final : public Topic {
|
||||
[[nodiscard]]
|
||||
SubscriberType Subscribe(
|
||||
{% if not TypeString %}std::string_view typeString, {% endif %}ParamType defaultValue,
|
||||
std::span<const PubSubOption> options = {});
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
{%- if TypeString %}
|
||||
/**
|
||||
* Create a new subscriber to the topic, with specific type string.
|
||||
@@ -332,7 +332,7 @@ class {{ TypeName }}Topic final : public Topic {
|
||||
[[nodiscard]]
|
||||
SubscriberType SubscribeEx(
|
||||
std::string_view typeString, ParamType defaultValue,
|
||||
std::span<const PubSubOption> options = {});
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
{% endif %}
|
||||
/**
|
||||
* Create a new publisher to the topic.
|
||||
@@ -353,7 +353,7 @@ class {{ TypeName }}Topic final : public Topic {
|
||||
* @return publisher
|
||||
*/
|
||||
[[nodiscard]]
|
||||
PublisherType Publish({% if not TypeString %}std::string_view typeString, {% endif %}std::span<const PubSubOption> options = {});
|
||||
PublisherType Publish({% if not TypeString %}std::string_view typeString, {% endif %}const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
/**
|
||||
* Create a new publisher to the topic, with type string and initial
|
||||
@@ -375,7 +375,7 @@ class {{ TypeName }}Topic final : public Topic {
|
||||
*/
|
||||
[[nodiscard]]
|
||||
PublisherType PublishEx(std::string_view typeString,
|
||||
const wpi::json& properties, std::span<const PubSubOption> options = {});
|
||||
const wpi::json& properties, const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
/**
|
||||
* Create a new entry for the topic.
|
||||
@@ -402,7 +402,7 @@ class {{ TypeName }}Topic final : public Topic {
|
||||
*/
|
||||
[[nodiscard]]
|
||||
EntryType GetEntry({% if not TypeString %}std::string_view typeString, {% endif %}ParamType defaultValue,
|
||||
std::span<const PubSubOption> options = {});
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
{%- if TypeString %}
|
||||
/**
|
||||
* Create a new entry for the topic, with specific type string.
|
||||
@@ -427,7 +427,7 @@ class {{ TypeName }}Topic final : public Topic {
|
||||
*/
|
||||
[[nodiscard]]
|
||||
EntryType GetEntryEx(std::string_view typeString, ParamType defaultValue,
|
||||
std::span<const PubSubOption> options = {});
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
{% endif %}
|
||||
};
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ inline void {{ TypeName }}Entry::Unpublish() {
|
||||
|
||||
inline {{ TypeName }}Subscriber {{ TypeName }}Topic::Subscribe(
|
||||
{% if not TypeString %}std::string_view typeString, {% endif %}{{ cpp.ParamType }} defaultValue,
|
||||
std::span<const PubSubOption> options) {
|
||||
const PubSubOptions& options) {
|
||||
return {{ TypeName }}Subscriber{
|
||||
::nt::Subscribe(m_handle, NT_{{ cpp.TYPE_NAME }}, {{ TypeString|default('typeString') }}, options),
|
||||
defaultValue};
|
||||
@@ -97,28 +97,28 @@ inline {{ TypeName }}Subscriber {{ TypeName }}Topic::Subscribe(
|
||||
{%- if TypeString %}
|
||||
inline {{ TypeName }}Subscriber {{ TypeName }}Topic::SubscribeEx(
|
||||
std::string_view typeString, {{ cpp.ParamType }} defaultValue,
|
||||
std::span<const PubSubOption> options) {
|
||||
const PubSubOptions& options) {
|
||||
return {{ TypeName }}Subscriber{
|
||||
::nt::Subscribe(m_handle, NT_{{ cpp.TYPE_NAME }}, typeString, options),
|
||||
defaultValue};
|
||||
}
|
||||
{% endif %}
|
||||
inline {{ TypeName }}Publisher {{ TypeName }}Topic::Publish(
|
||||
{% if not TypeString %}std::string_view typeString, {% endif %}std::span<const PubSubOption> options) {
|
||||
{% if not TypeString %}std::string_view typeString, {% endif %}const PubSubOptions& options) {
|
||||
return {{ TypeName }}Publisher{
|
||||
::nt::Publish(m_handle, NT_{{ cpp.TYPE_NAME }}, {{ TypeString|default('typeString') }}, options)};
|
||||
}
|
||||
|
||||
inline {{ TypeName }}Publisher {{ TypeName }}Topic::PublishEx(
|
||||
std::string_view typeString,
|
||||
const wpi::json& properties, std::span<const PubSubOption> options) {
|
||||
const wpi::json& properties, const PubSubOptions& options) {
|
||||
return {{ TypeName }}Publisher{
|
||||
::nt::PublishEx(m_handle, NT_{{ cpp.TYPE_NAME }}, typeString, properties, options)};
|
||||
}
|
||||
|
||||
inline {{ TypeName }}Entry {{ TypeName }}Topic::GetEntry(
|
||||
{% if not TypeString %}std::string_view typeString, {% endif %}{{ cpp.ParamType }} defaultValue,
|
||||
std::span<const PubSubOption> options) {
|
||||
const PubSubOptions& options) {
|
||||
return {{ TypeName }}Entry{
|
||||
::nt::GetEntry(m_handle, NT_{{ cpp.TYPE_NAME }}, {{ TypeString|default('typeString') }}, options),
|
||||
defaultValue};
|
||||
@@ -126,7 +126,7 @@ inline {{ TypeName }}Entry {{ TypeName }}Topic::GetEntry(
|
||||
{%- if TypeString %}
|
||||
inline {{ TypeName }}Entry {{ TypeName }}Topic::GetEntryEx(
|
||||
std::string_view typeString, {{ cpp.ParamType }} defaultValue,
|
||||
std::span<const PubSubOption> options) {
|
||||
const PubSubOptions& options) {
|
||||
return {{ TypeName }}Entry{
|
||||
::nt::GetEntry(m_handle, NT_{{ cpp.TYPE_NAME }}, typeString, options),
|
||||
defaultValue};
|
||||
|
||||
@@ -57,20 +57,11 @@ public final class NetworkTablesJNI {
|
||||
libraryLoaded = true;
|
||||
}
|
||||
|
||||
private static int[] pubSubOptionTypes(PubSubOption... options) {
|
||||
int[] rv = new int[options.length];
|
||||
for (int i = 0; i < options.length; i++) {
|
||||
rv[i] = options[i].m_type;
|
||||
private static PubSubOptions buildOptions(PubSubOption... options) {
|
||||
if (options.length == 0) {
|
||||
return null; // optimize common case (JNI checks for null)
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
private static int[] pubSubOptionValues(PubSubOption... options) {
|
||||
int[] rv = new int[options.length];
|
||||
for (int i = 0; i < options.length; i++) {
|
||||
rv[i] = options[i].m_value;
|
||||
}
|
||||
return rv;
|
||||
return new PubSubOptions(options);
|
||||
}
|
||||
|
||||
public static native int getDefaultInstance();
|
||||
@@ -81,13 +72,19 @@ public final class NetworkTablesJNI {
|
||||
|
||||
public static native int getInstanceFromHandle(int handle);
|
||||
|
||||
private static native int getEntryImpl(
|
||||
int topic, int type, String typeStr, PubSubOptions options);
|
||||
|
||||
public static native int getEntry(int inst, String key);
|
||||
|
||||
private static native int getEntry(
|
||||
int topic, int type, String typeStr, int[] optionTypes, int[] optionValues);
|
||||
public static int getEntry(
|
||||
int topic, int type, String typeStr, PubSubOptions options) {
|
||||
return getEntryImpl(topic, type, typeStr, options);
|
||||
}
|
||||
|
||||
public static int getEntry(int topic, int type, String typeStr, PubSubOption... options) {
|
||||
return getEntry(topic, type, typeStr, pubSubOptionTypes(options), pubSubOptionValues(options));
|
||||
public static int getEntry(
|
||||
int topic, int type, String typeStr, PubSubOption... options) {
|
||||
return getEntryImpl(topic, type, typeStr, buildOptions(options));
|
||||
}
|
||||
|
||||
public static native String getEntryName(int entry);
|
||||
@@ -136,27 +133,30 @@ public final class NetworkTablesJNI {
|
||||
|
||||
public static native void setTopicProperties(int topic, String properties);
|
||||
|
||||
private static native int subscribe(
|
||||
int topic, int type, String typeStr, int[] optionTypes, int[] optionValues);
|
||||
public static native int subscribe(
|
||||
int topic, int type, String typeStr, PubSubOptions options);
|
||||
|
||||
public static int subscribe(int topic, int type, String typeStr, PubSubOption... options) {
|
||||
return subscribe(topic, type, typeStr, pubSubOptionTypes(options), pubSubOptionValues(options));
|
||||
public static int subscribe(
|
||||
int topic, int type, String typeStr, PubSubOption... options) {
|
||||
return subscribe(topic, type, typeStr, buildOptions(options));
|
||||
}
|
||||
|
||||
public static native void unsubscribe(int sub);
|
||||
|
||||
private static native int publish(
|
||||
int topic, int type, String typeStr, int[] optionTypes, int[] optionValues);
|
||||
public static native int publish(
|
||||
int topic, int type, String typeStr, PubSubOptions options);
|
||||
|
||||
public static int publish(int topic, int type, String typeStr, PubSubOption... options) {
|
||||
return publish(topic, type, typeStr, pubSubOptionTypes(options), pubSubOptionValues(options));
|
||||
public static int publish(
|
||||
int topic, int type, String typeStr, PubSubOption... options) {
|
||||
return publish(topic, type, typeStr, buildOptions(options));
|
||||
}
|
||||
|
||||
private static native int publishEx(
|
||||
int topic, int type, String typeStr, String properties, int[] optionTypes, int[] optionValues);
|
||||
public static native int publishEx(
|
||||
int topic, int type, String typeStr, String properties, PubSubOptions options);
|
||||
|
||||
public static int publishEx(int topic, int type, String typeStr, String properties, PubSubOption... options) {
|
||||
return publishEx(topic, type, typeStr, properties, pubSubOptionTypes(options), pubSubOptionValues(options));
|
||||
public static int publishEx(
|
||||
int topic, int type, String typeStr, String properties, PubSubOption... options) {
|
||||
return publishEx(topic, type, typeStr, properties, buildOptions(options));
|
||||
}
|
||||
|
||||
public static native void unpublish(int pubentry);
|
||||
@@ -167,12 +167,10 @@ public final class NetworkTablesJNI {
|
||||
|
||||
public static native int getTopicFromHandle(int pubsubentry);
|
||||
|
||||
private static native int subscribeMultiple(
|
||||
int inst, String[] prefixes, int[] optionTypes, int[] optionValues);
|
||||
public static native int subscribeMultiple(int inst, String[] prefixes, PubSubOptions options);
|
||||
|
||||
public static int subscribeMultiple(int inst, String[] prefixes, PubSubOption... options) {
|
||||
return subscribeMultiple(
|
||||
inst, prefixes, pubSubOptionTypes(options), pubSubOptionValues(options));
|
||||
return subscribeMultiple(inst, prefixes, buildOptions(options));
|
||||
}
|
||||
|
||||
public static native void unsubscribeMultiple(int sub);
|
||||
|
||||
@@ -22,7 +22,6 @@ public final class NetworkTable {
|
||||
private final String m_path;
|
||||
private final String m_pathWithSep;
|
||||
private final NetworkTableInstance m_inst;
|
||||
private final MultiSubscriber m_topicSub;
|
||||
|
||||
/**
|
||||
* Gets the "base name" of a key. For example, "/foo/bar" becomes "bar". If the key has a trailing
|
||||
@@ -115,8 +114,6 @@ public final class NetworkTable {
|
||||
m_path = path;
|
||||
m_pathWithSep = path + PATH_SEPARATOR;
|
||||
m_inst = inst;
|
||||
m_topicSub =
|
||||
new MultiSubscriber(inst, new String[] {m_pathWithSep}, PubSubOption.topicsOnly(true));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -416,7 +413,7 @@ public final class NetworkTable {
|
||||
* @param value the value that will be assigned
|
||||
* @return False if the table key already exists with a different type
|
||||
*/
|
||||
boolean putValue(String key, NetworkTableValue value) {
|
||||
public boolean putValue(String key, NetworkTableValue value) {
|
||||
return getEntry(key).setValue(value);
|
||||
}
|
||||
|
||||
@@ -427,7 +424,7 @@ public final class NetworkTable {
|
||||
* @param defaultValue the default value to set if key doesn't exist.
|
||||
* @return False if the table key exists with a different type
|
||||
*/
|
||||
boolean setDefaultValue(String key, NetworkTableValue defaultValue) {
|
||||
public boolean setDefaultValue(String key, NetworkTableValue defaultValue) {
|
||||
return getEntry(key).setDefaultValue(defaultValue);
|
||||
}
|
||||
|
||||
@@ -437,7 +434,7 @@ public final class NetworkTable {
|
||||
* @param key the key of the value to look up
|
||||
* @return the value associated with the given key, or nullptr if the key does not exist
|
||||
*/
|
||||
NetworkTableValue getValue(String key) {
|
||||
public NetworkTableValue getValue(String key) {
|
||||
return getEntry(key).getValue();
|
||||
}
|
||||
|
||||
@@ -533,7 +530,7 @@ public final class NetworkTable {
|
||||
final NetworkTable parent = this;
|
||||
|
||||
return m_inst.addListener(
|
||||
m_topicSub,
|
||||
new String[] {m_pathWithSep},
|
||||
EnumSet.of(NetworkTableEvent.Kind.kPublish, NetworkTableEvent.Kind.kImmediate),
|
||||
new Consumer<NetworkTableEvent>() {
|
||||
final Set<String> m_notifiedTables = new HashSet<>();
|
||||
@@ -583,8 +580,4 @@ public final class NetworkTable {
|
||||
public int hashCode() {
|
||||
return Objects.hash(m_inst, m_path);
|
||||
}
|
||||
|
||||
void close() {
|
||||
m_topicSub.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,52 +6,69 @@ package edu.wpi.first.networktables;
|
||||
|
||||
/** NetworkTables publish/subscribe option. */
|
||||
public class PubSubOption {
|
||||
private static final int kPeriodic = 1;
|
||||
private static final int kSendAll = 2;
|
||||
private static final int kTopicsOnly = 3;
|
||||
private static final int kPollStorage = 4;
|
||||
private static final int kKeepDuplicates = 5;
|
||||
private static final int kLocalRemote = 6;
|
||||
private static final int kExcludePub = 7;
|
||||
private static final int kExcludeSelf = 8;
|
||||
enum Kind {
|
||||
periodic,
|
||||
sendAll,
|
||||
topicsOnly,
|
||||
pollStorage,
|
||||
keepDuplicates,
|
||||
disableRemote,
|
||||
disableLocal,
|
||||
excludePublisher,
|
||||
excludeSelf;
|
||||
}
|
||||
|
||||
PubSubOption(int type, int value) {
|
||||
m_type = type;
|
||||
m_value = value;
|
||||
PubSubOption(Kind kind, boolean value) {
|
||||
m_kind = kind;
|
||||
m_bValue = value;
|
||||
m_iValue = 0;
|
||||
m_dValue = 0;
|
||||
}
|
||||
|
||||
PubSubOption(Kind kind, int value) {
|
||||
m_kind = kind;
|
||||
m_bValue = false;
|
||||
m_iValue = value;
|
||||
m_dValue = 0;
|
||||
}
|
||||
|
||||
PubSubOption(Kind kind, double value) {
|
||||
m_kind = kind;
|
||||
m_bValue = false;
|
||||
m_iValue = 0;
|
||||
m_dValue = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* How frequently changes will be sent over the network. NetworkTables may send more frequently
|
||||
* than this (e.g. use a combined minimum period for all values) or apply a restricted range to
|
||||
* this value. The default if unspecified (and the immediate flag is false) is 100 ms. This option
|
||||
* and the immediate option override each other.
|
||||
* this value. The default if unspecified is 100 ms.
|
||||
*
|
||||
* @param period time between updates, in seconds
|
||||
* @return option
|
||||
*/
|
||||
public static PubSubOption periodic(double period) {
|
||||
return new PubSubOption(kPeriodic, (int) (period * 1000));
|
||||
return new PubSubOption(Kind.periodic, period);
|
||||
}
|
||||
|
||||
/**
|
||||
* If enabled, sends all value changes over the network even if only sent periodically. This
|
||||
* option defaults to disabled.
|
||||
* If enabled, sends all value changes over the network. This option defaults to disabled.
|
||||
*
|
||||
* @param enabled True to enable, false to disable
|
||||
* @return option
|
||||
*/
|
||||
public static PubSubOption sendAll(boolean enabled) {
|
||||
return new PubSubOption(kSendAll, enabled ? 1 : 0);
|
||||
return new PubSubOption(Kind.sendAll, enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* If enabled, no value changes are sent over the network. This option defaults to disabled.
|
||||
* If enabled on a subscription, does not request value changes. This option defaults to disabled.
|
||||
*
|
||||
* @param enabled True to enable, false to disable
|
||||
* @return option
|
||||
*/
|
||||
public static PubSubOption topicsOnly(boolean enabled) {
|
||||
return new PubSubOption(kTopicsOnly, enabled ? 1 : 0);
|
||||
return new PubSubOption(Kind.topicsOnly, enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,7 +79,7 @@ public class PubSubOption {
|
||||
* @return option
|
||||
*/
|
||||
public static PubSubOption keepDuplicates(boolean enabled) {
|
||||
return new PubSubOption(kKeepDuplicates, enabled ? 1 : 0);
|
||||
return new PubSubOption(Kind.keepDuplicates, enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,37 +91,29 @@ public class PubSubOption {
|
||||
* @return option
|
||||
*/
|
||||
public static PubSubOption pollStorage(int depth) {
|
||||
return new PubSubOption(kPollStorage, depth);
|
||||
return new PubSubOption(Kind.pollStorage, depth);
|
||||
}
|
||||
|
||||
/**
|
||||
* If only local value updates should be queued for readQueue(). See also remoteOnly() and
|
||||
* allUpdates(). Default is allUpdates. Only has an effect on subscriptions.
|
||||
* For subscriptions, specify whether remote value updates should not be queued for readQueue().
|
||||
* See also disableLocal(). Defaults to false (remote value updates are queued).
|
||||
*
|
||||
* @param disabled True to disable, false to enable
|
||||
* @return option
|
||||
*/
|
||||
public static PubSubOption localOnly() {
|
||||
return new PubSubOption(kLocalRemote, 1);
|
||||
public static PubSubOption disableRemote(boolean disabled) {
|
||||
return new PubSubOption(Kind.disableRemote, disabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* If only remote value updates should be queued for readQueue(). See also localOnly() and
|
||||
* allUpdates(). Default is allUpdates. Only has an effect on subscriptions.
|
||||
* For subscriptions, specify whether local value updates should not be queued for readQueue().
|
||||
* See alse disableRemote(). Defaults to false (local value updates are queued).
|
||||
*
|
||||
* @param disabled True to disable, false to enable
|
||||
* @return option
|
||||
*/
|
||||
public static PubSubOption remoteOnly() {
|
||||
return new PubSubOption(kLocalRemote, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* If both local and remote value updates should be queued for readQueue(). See also localOnly()
|
||||
* and remoteOnly(). Default is allUpdates. Only has an effect on subscriptions.
|
||||
*
|
||||
* @return option
|
||||
*/
|
||||
public static PubSubOption allUpdates() {
|
||||
return new PubSubOption(kLocalRemote, 0);
|
||||
public static PubSubOption disableLocal(boolean disabled) {
|
||||
return new PubSubOption(Kind.disableLocal, disabled);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,7 +124,7 @@ public class PubSubOption {
|
||||
* @return option
|
||||
*/
|
||||
public static PubSubOption excludePublisher(int publisher) {
|
||||
return new PubSubOption(kExcludePub, publisher);
|
||||
return new PubSubOption(Kind.excludePublisher, publisher);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,7 +135,7 @@ public class PubSubOption {
|
||||
* @return option
|
||||
*/
|
||||
public static PubSubOption excludePublisher(Publisher publisher) {
|
||||
return new PubSubOption(kExcludePub, publisher != null ? publisher.getHandle() : 0);
|
||||
return excludePublisher(publisher != null ? publisher.getHandle() : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,9 +146,11 @@ public class PubSubOption {
|
||||
* @return option
|
||||
*/
|
||||
public static PubSubOption excludeSelf(boolean enabled) {
|
||||
return new PubSubOption(kExcludeSelf, enabled ? 1 : 0);
|
||||
return new PubSubOption(Kind.excludeSelf, enabled);
|
||||
}
|
||||
|
||||
final int m_type;
|
||||
final int m_value;
|
||||
final Kind m_kind;
|
||||
final boolean m_bValue;
|
||||
final int m_iValue;
|
||||
final double m_dValue;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
// 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 edu.wpi.first.networktables;
|
||||
|
||||
/** NetworkTables publish/subscribe options. */
|
||||
@SuppressWarnings("MemberName")
|
||||
public class PubSubOptions {
|
||||
/**
|
||||
* Construct from a list of options.
|
||||
*
|
||||
* @param options options
|
||||
*/
|
||||
public PubSubOptions(PubSubOption... options) {
|
||||
for (PubSubOption option : options) {
|
||||
switch (option.m_kind) {
|
||||
case periodic:
|
||||
periodic = option.m_dValue;
|
||||
break;
|
||||
case sendAll:
|
||||
sendAll = option.m_bValue;
|
||||
break;
|
||||
case topicsOnly:
|
||||
topicsOnly = option.m_bValue;
|
||||
break;
|
||||
case pollStorage:
|
||||
pollStorage = option.m_iValue;
|
||||
break;
|
||||
case keepDuplicates:
|
||||
keepDuplicates = option.m_bValue;
|
||||
break;
|
||||
case disableRemote:
|
||||
disableRemote = option.m_bValue;
|
||||
break;
|
||||
case disableLocal:
|
||||
disableLocal = option.m_bValue;
|
||||
break;
|
||||
case excludePublisher:
|
||||
excludePublisher = option.m_iValue;
|
||||
break;
|
||||
case excludeSelf:
|
||||
excludeSelf = option.m_bValue;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PubSubOptions(
|
||||
int pollStorage,
|
||||
double periodic,
|
||||
int excludePublisher,
|
||||
boolean sendAll,
|
||||
boolean topicsOnly,
|
||||
boolean keepDuplicates,
|
||||
boolean prefixMatch,
|
||||
boolean disableRemote,
|
||||
boolean disableLocal,
|
||||
boolean excludeSelf) {
|
||||
this.pollStorage = pollStorage;
|
||||
this.periodic = periodic;
|
||||
this.excludePublisher = excludePublisher;
|
||||
this.sendAll = sendAll;
|
||||
this.topicsOnly = topicsOnly;
|
||||
this.keepDuplicates = keepDuplicates;
|
||||
this.prefixMatch = prefixMatch;
|
||||
this.disableRemote = disableRemote;
|
||||
this.disableLocal = disableLocal;
|
||||
this.excludeSelf = excludeSelf;
|
||||
}
|
||||
|
||||
/** Default value of periodic. */
|
||||
public static final double kDefaultPeriodic = 0.1;
|
||||
|
||||
/**
|
||||
* Polling storage size for a subscription. Specifies the maximum number of updates NetworkTables
|
||||
* should store between calls to the subscriber's readQueue() function. If zero, defaults to 1 if
|
||||
* sendAll is false, 20 if sendAll is true.
|
||||
*/
|
||||
public int pollStorage;
|
||||
|
||||
/**
|
||||
* How frequently changes will be sent over the network, in seconds. NetworkTables may send more
|
||||
* frequently than this (e.g. use a combined minimum period for all values) or apply a restricted
|
||||
* range to this value. The default is 100 ms.
|
||||
*/
|
||||
public double periodic = kDefaultPeriodic;
|
||||
|
||||
/**
|
||||
* For subscriptions, if non-zero, value updates for readQueue() are not queued for this
|
||||
* publisher.
|
||||
*/
|
||||
public int excludePublisher;
|
||||
|
||||
/** Send all value changes over the network. */
|
||||
public boolean sendAll;
|
||||
|
||||
/** For subscriptions, don't ask for value changes (only topic announcements). */
|
||||
public boolean topicsOnly;
|
||||
|
||||
/** Preserve duplicate value changes (rather than ignoring them). */
|
||||
public boolean keepDuplicates;
|
||||
|
||||
/**
|
||||
* Perform prefix match on subscriber topic names. Is ignored/overridden by subscribe() functions;
|
||||
* only present in struct for the purposes of getting information about subscriptions.
|
||||
*/
|
||||
public boolean prefixMatch;
|
||||
|
||||
/**
|
||||
* For subscriptions, if remote value updates should not be queued for readQueue(). See also
|
||||
* disableLocal.
|
||||
*/
|
||||
public boolean disableRemote;
|
||||
|
||||
/**
|
||||
* For subscriptions, if local value updates should not be queued for readQueue(). See also
|
||||
* disableRemote.
|
||||
*/
|
||||
public boolean disableLocal;
|
||||
|
||||
/** For entries, don't queue (for readQueue) value updates for the entry's internal publisher. */
|
||||
public boolean excludeSelf;
|
||||
}
|
||||
@@ -107,11 +107,13 @@ struct TopicData {
|
||||
VectorSet<NT_Listener> listeners;
|
||||
};
|
||||
|
||||
struct PubSubConfig : public PubSubOptions {
|
||||
struct PubSubConfig : public PubSubOptionsImpl {
|
||||
PubSubConfig() = default;
|
||||
PubSubConfig(NT_Type type, std::string_view typeStr,
|
||||
std::span<const PubSubOption> options)
|
||||
: PubSubOptions{options}, type{type}, typeStr{typeStr} {}
|
||||
const PubSubOptions& options)
|
||||
: PubSubOptionsImpl{options}, type{type}, typeStr{typeStr} {
|
||||
prefixMatch = false;
|
||||
}
|
||||
|
||||
NT_Type type{NT_UNASSIGNED};
|
||||
std::string typeStr;
|
||||
@@ -141,7 +143,7 @@ struct SubscriberData {
|
||||
: handle{handle},
|
||||
topic{topic},
|
||||
config{std::move(config)},
|
||||
pollStorage{config.pollStorageSize} {}
|
||||
pollStorage{config.pollStorage} {}
|
||||
|
||||
void UpdateActive();
|
||||
|
||||
@@ -180,8 +182,8 @@ struct MultiSubscriberData {
|
||||
|
||||
MultiSubscriberData(NT_MultiSubscriber handle,
|
||||
std::span<const std::string_view> prefixes,
|
||||
PubSubOptions options)
|
||||
: handle{handle}, options{std::move(options)} {
|
||||
const PubSubOptionsImpl& options)
|
||||
: handle{handle}, options{options} {
|
||||
this->options.prefixMatch = true;
|
||||
this->prefixes.reserve(prefixes.size());
|
||||
for (auto&& prefix : prefixes) {
|
||||
@@ -194,7 +196,7 @@ struct MultiSubscriberData {
|
||||
// invariants
|
||||
wpi::SignalObject<NT_MultiSubscriber> handle;
|
||||
std::vector<std::string> prefixes;
|
||||
PubSubOptions options;
|
||||
PubSubOptionsImpl options;
|
||||
|
||||
// value listeners
|
||||
VectorSet<NT_Listener> valueListeners;
|
||||
@@ -425,7 +427,7 @@ void SubscriberData::UpdateActive() {
|
||||
}
|
||||
|
||||
void LSImpl::NotifyTopic(TopicData* topic, unsigned int eventFlags) {
|
||||
DEBUG4("NotifyTopic({}, {})\n", topic->name, eventFlags);
|
||||
DEBUG4("NotifyTopic({}, {})", topic->name, eventFlags);
|
||||
auto topicInfo = topic->GetTopicInfo();
|
||||
if (!topic->listeners.empty()) {
|
||||
m_listenerStorage.Notify(topic->listeners, eventFlags, topicInfo);
|
||||
@@ -517,10 +519,10 @@ void LSImpl::NotifyValue(TopicData* topic, unsigned int eventFlags,
|
||||
for (auto&& subscriber : topic->localSubscribers) {
|
||||
if (subscriber->active &&
|
||||
(subscriber->config.keepDuplicates || !isDuplicate) &&
|
||||
((isNetwork && subscriber->config.fromRemote) ||
|
||||
(!isNetwork && subscriber->config.fromLocal)) &&
|
||||
(!publisher ||
|
||||
(publisher && (subscriber->config.excludePub != publisher->handle)))) {
|
||||
((isNetwork && !subscriber->config.disableRemote) ||
|
||||
(!isNetwork && !subscriber->config.disableLocal)) &&
|
||||
(!publisher || (publisher && (subscriber->config.excludePublisher !=
|
||||
publisher->handle)))) {
|
||||
subscriber->pollStorage.emplace_back(topic->lastValue);
|
||||
subscriber->handle.Set();
|
||||
if (!subscriber->valueListeners.empty()) {
|
||||
@@ -1090,10 +1092,8 @@ void LSImpl::AddListener(NT_Listener listenerHandle,
|
||||
return;
|
||||
}
|
||||
// subscribe to make sure topic updates are received
|
||||
PubSubOptions options;
|
||||
options.topicsOnly = (eventMask & NT_EVENT_VALUE_ALL) == 0;
|
||||
options.prefixMatch = true;
|
||||
auto sub = AddMultiSubscriber(prefixes, options);
|
||||
auto sub = AddMultiSubscriber(
|
||||
prefixes, {.topicsOnly = (eventMask & NT_EVENT_VALUE_ALL) == 0});
|
||||
AddListenerImpl(listenerHandle, sub, eventMask, true);
|
||||
}
|
||||
|
||||
@@ -1266,7 +1266,7 @@ bool LSImpl::SetEntryValue(NT_Handle pubentryHandle, const Value& value) {
|
||||
if (auto entry = m_entries.Get(pubentryHandle)) {
|
||||
publisher = PublishEntry(entry, value.type());
|
||||
if (entry->subscriber->config.excludeSelf) {
|
||||
entry->subscriber->config.excludePub = publisher->handle;
|
||||
entry->subscriber->config.excludePublisher = publisher->handle;
|
||||
}
|
||||
}
|
||||
if (!publisher) {
|
||||
@@ -1380,36 +1380,37 @@ void LocalStorage::NetworkSetValue(NT_Topic topicHandle, const Value& value) {
|
||||
}
|
||||
}
|
||||
|
||||
void LocalStorage::StartNetwork(net::NetworkStartupInterface& startup,
|
||||
net::NetworkInterface* network) {
|
||||
void LocalStorage::StartNetwork(net::NetworkInterface* network) {
|
||||
WPI_DEBUG4(m_impl->m_logger, "StartNetwork()");
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_impl->m_network = network;
|
||||
// publish all active publishers to the network and send last values
|
||||
// only send value once per topic
|
||||
for (auto&& topic : m_impl->m_topics) {
|
||||
PublisherData* anyPublisher = nullptr;
|
||||
for (auto&& publisher : topic->localPublishers) {
|
||||
if (publisher->active) {
|
||||
startup.Publish(publisher->handle, topic->handle, topic->name,
|
||||
topic->typeStr, topic->properties, publisher->config);
|
||||
network->Publish(publisher->handle, topic->handle, topic->name,
|
||||
topic->typeStr, topic->properties, publisher->config);
|
||||
anyPublisher = publisher;
|
||||
}
|
||||
}
|
||||
if (anyPublisher && topic->lastValue) {
|
||||
startup.SetValue(anyPublisher->handle, topic->lastValue);
|
||||
network->SetValue(anyPublisher->handle, topic->lastValue);
|
||||
}
|
||||
}
|
||||
for (auto&& subscriber : m_impl->m_subscribers) {
|
||||
startup.Subscribe(subscriber->handle, {{subscriber->topic->name}},
|
||||
subscriber->config);
|
||||
network->Subscribe(subscriber->handle, {{subscriber->topic->name}},
|
||||
subscriber->config);
|
||||
}
|
||||
for (auto&& subscriber : m_impl->m_multiSubscribers) {
|
||||
startup.Subscribe(subscriber->handle, subscriber->prefixes,
|
||||
subscriber->options);
|
||||
network->Subscribe(subscriber->handle, subscriber->prefixes,
|
||||
subscriber->options);
|
||||
}
|
||||
m_impl->m_network = network;
|
||||
}
|
||||
|
||||
void LocalStorage::ClearNetwork() {
|
||||
WPI_DEBUG4(m_impl->m_logger, "ClearNetwork()");
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_impl->m_network = nullptr;
|
||||
// treat as an unannounce all from the network side
|
||||
@@ -1642,7 +1643,7 @@ TopicInfo LocalStorage::GetTopicInfo(NT_Topic topicHandle) {
|
||||
|
||||
NT_Subscriber LocalStorage::Subscribe(NT_Topic topicHandle, NT_Type type,
|
||||
std::string_view typeStr,
|
||||
std::span<const PubSubOption> options) {
|
||||
const PubSubOptions& options) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
// Get the topic
|
||||
@@ -1669,8 +1670,7 @@ void LocalStorage::Unsubscribe(NT_Subscriber subHandle) {
|
||||
}
|
||||
|
||||
NT_MultiSubscriber LocalStorage::SubscribeMultiple(
|
||||
std::span<const std::string_view> prefixes,
|
||||
std::span<const PubSubOption> options) {
|
||||
std::span<const std::string_view> prefixes, const PubSubOptions& options) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
if (m_impl->m_multiSubscribers.size() >= kMaxMultiSubscribers) {
|
||||
@@ -1679,9 +1679,7 @@ NT_MultiSubscriber LocalStorage::SubscribeMultiple(
|
||||
return 0;
|
||||
}
|
||||
|
||||
PubSubOptions opts{options};
|
||||
opts.prefixMatch = true;
|
||||
return m_impl->AddMultiSubscriber(prefixes, opts)->handle;
|
||||
return m_impl->AddMultiSubscriber(prefixes, options)->handle;
|
||||
}
|
||||
|
||||
void LocalStorage::UnsubscribeMultiple(NT_MultiSubscriber subHandle) {
|
||||
@@ -1692,7 +1690,7 @@ void LocalStorage::UnsubscribeMultiple(NT_MultiSubscriber subHandle) {
|
||||
NT_Publisher LocalStorage::Publish(NT_Topic topicHandle, NT_Type type,
|
||||
std::string_view typeStr,
|
||||
const wpi::json& properties,
|
||||
std::span<const PubSubOption> options) {
|
||||
const PubSubOptions& options) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
// Get the topic
|
||||
@@ -1742,7 +1740,7 @@ void LocalStorage::Unpublish(NT_Handle pubentryHandle) {
|
||||
|
||||
NT_Entry LocalStorage::GetEntry(NT_Topic topicHandle, NT_Type type,
|
||||
std::string_view typeStr,
|
||||
std::span<const PubSubOption> options) {
|
||||
const PubSubOptions& options) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
// Get the topic
|
||||
|
||||
@@ -44,8 +44,7 @@ class LocalStorage final : public net::ILocalStorage {
|
||||
bool ack) final;
|
||||
void NetworkSetValue(NT_Topic topicHandle, const Value& value) final;
|
||||
|
||||
void StartNetwork(net::NetworkStartupInterface& startup,
|
||||
net::NetworkInterface* network) final;
|
||||
void StartNetwork(net::NetworkInterface* network) final;
|
||||
void ClearNetwork() final;
|
||||
|
||||
// User functions. These are the actual implementations of the corresponding
|
||||
@@ -93,24 +92,23 @@ class LocalStorage final : public net::ILocalStorage {
|
||||
|
||||
NT_Subscriber Subscribe(NT_Topic topic, NT_Type type,
|
||||
std::string_view typeStr,
|
||||
std::span<const PubSubOption> options);
|
||||
const PubSubOptions& options);
|
||||
|
||||
void Unsubscribe(NT_Subscriber sub);
|
||||
|
||||
NT_MultiSubscriber SubscribeMultiple(
|
||||
std::span<const std::string_view> prefixes,
|
||||
std::span<const PubSubOption> options);
|
||||
std::span<const std::string_view> prefixes, const PubSubOptions& options);
|
||||
|
||||
void UnsubscribeMultiple(NT_MultiSubscriber subHandle);
|
||||
|
||||
NT_Publisher Publish(NT_Topic topic, NT_Type type, std::string_view typeStr,
|
||||
const wpi::json& properties,
|
||||
std::span<const PubSubOption> options);
|
||||
const PubSubOptions& options);
|
||||
|
||||
void Unpublish(NT_Handle pubentry);
|
||||
|
||||
NT_Entry GetEntry(NT_Topic topic, NT_Type type, std::string_view typeStr,
|
||||
std::span<const PubSubOption> options);
|
||||
const PubSubOptions& options);
|
||||
|
||||
void ReleaseEntry(NT_Entry entry);
|
||||
|
||||
|
||||
@@ -304,11 +304,9 @@ void NCImpl3::TcpConnected(uv::Tcp& tcp) {
|
||||
}
|
||||
});
|
||||
|
||||
{
|
||||
net3::ClientStartup3 startup{*m_clientImpl};
|
||||
m_localStorage.StartNetwork(startup, &m_localQueue);
|
||||
}
|
||||
m_clientImpl->SetLocal(&m_localStorage);
|
||||
m_localStorage.StartNetwork(&m_localQueue);
|
||||
HandleLocal();
|
||||
});
|
||||
|
||||
tcp.SetData(clientImpl);
|
||||
@@ -429,11 +427,10 @@ void NCImpl4::WsConnected(wpi::WebSocket& ws, uv::Tcp& tcp) {
|
||||
m_sendValuesTimer->Start(uv::Timer::Time{repeatMs},
|
||||
uv::Timer::Time{repeatMs});
|
||||
});
|
||||
{
|
||||
net::ClientStartup startup{*m_clientImpl};
|
||||
m_localStorage.StartNetwork(startup, &m_localQueue);
|
||||
}
|
||||
m_clientImpl->SetLocal(&m_localStorage);
|
||||
m_localStorage.StartNetwork(&m_localQueue);
|
||||
HandleLocal();
|
||||
m_clientImpl->SendInitial();
|
||||
ws.closed.connect([this, &ws](uint16_t, std::string_view reason) {
|
||||
if (!ws.GetStream().IsLoopClosing()) {
|
||||
Disconnect(reason);
|
||||
|
||||
@@ -334,11 +334,9 @@ NSImpl::NSImpl(std::string_view persistentFilename,
|
||||
m_localMsgs.reserve(net::NetworkLoopQueue::kInitialQueueSize);
|
||||
m_loopRunner.ExecAsync([=, this](uv::Loop& loop) {
|
||||
// connect local storage to server
|
||||
{
|
||||
net::ServerStartup startup{m_serverImpl};
|
||||
m_localStorage.StartNetwork(startup, &m_localQueue);
|
||||
}
|
||||
m_serverImpl.SetLocal(&m_localStorage);
|
||||
m_localStorage.StartNetwork(&m_localQueue);
|
||||
HandleLocal();
|
||||
|
||||
// load persistent file first, then initialize
|
||||
uv::QueueWork(
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
// 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 "PubSubOptions.h"
|
||||
|
||||
#include "ntcore_c.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
nt::PubSubOptions::PubSubOptions(std::span<const PubSubOption> options) {
|
||||
for (auto&& option : options) {
|
||||
switch (option.type) {
|
||||
case NT_PUBSUB_PERIODIC:
|
||||
periodicMs = option.value;
|
||||
break;
|
||||
case NT_PUBSUB_SENDALL:
|
||||
sendAll = option.value != 0;
|
||||
if (sendAll) {
|
||||
pollStorageSize = 20;
|
||||
}
|
||||
break;
|
||||
case NT_PUBSUB_TOPICSONLY:
|
||||
topicsOnly = option.value != 0;
|
||||
break;
|
||||
case NT_PUBSUB_KEEPDUPLICATES:
|
||||
keepDuplicates = option.value != 0;
|
||||
break;
|
||||
case NT_PUBSUB_POLLSTORAGE:
|
||||
pollStorageSize = static_cast<size_t>(option.value);
|
||||
break;
|
||||
case NT_PUBSUB_LOCALREMOTE:
|
||||
switch (static_cast<int>(option.value)) {
|
||||
case 0:
|
||||
case 3:
|
||||
fromLocal = true;
|
||||
fromRemote = true;
|
||||
break;
|
||||
case 1:
|
||||
fromLocal = true;
|
||||
fromRemote = false;
|
||||
break;
|
||||
case 2:
|
||||
fromLocal = false;
|
||||
fromRemote = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case NT_PUBSUB_EXCLUDEPUB:
|
||||
excludePub = option.value;
|
||||
break;
|
||||
case NT_PUBSUB_EXCLUDESELF:
|
||||
excludeSelf = option.value != 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,30 +4,33 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
// options built from array of PubSubOption
|
||||
class PubSubOptions {
|
||||
// internal helper class for PubSubOptions
|
||||
class PubSubOptionsImpl : public PubSubOptions {
|
||||
public:
|
||||
PubSubOptions() = default;
|
||||
explicit PubSubOptions(std::span<const PubSubOption> options);
|
||||
constexpr PubSubOptionsImpl() : PubSubOptionsImpl{kDefaultPubSubOptions} {}
|
||||
|
||||
/*implicit*/ constexpr PubSubOptionsImpl( // NOLINT
|
||||
const PubSubOptions& options)
|
||||
: PubSubOptions{options} {
|
||||
if (periodic == 0) {
|
||||
periodic = kDefaultPeriodic;
|
||||
}
|
||||
periodicMs = static_cast<unsigned int>(periodic * 1000);
|
||||
if (pollStorage == 0) {
|
||||
if (sendAll) {
|
||||
pollStorage = 20;
|
||||
} else {
|
||||
pollStorage = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr unsigned int kDefaultPeriodicMs = 100;
|
||||
|
||||
unsigned int periodicMs = kDefaultPeriodicMs;
|
||||
size_t pollStorageSize = 1;
|
||||
bool sendAll = false;
|
||||
bool topicsOnly = false;
|
||||
bool prefixMatch = false;
|
||||
bool keepDuplicates = false;
|
||||
bool fromRemote = true;
|
||||
bool fromLocal = true;
|
||||
unsigned int excludePub = 0;
|
||||
bool excludeSelf = false;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
@@ -39,6 +39,7 @@ static JClass eventCls;
|
||||
static JClass floatCls;
|
||||
static JClass logMessageCls;
|
||||
static JClass longCls;
|
||||
static JClass pubSubOptionsCls;
|
||||
static JClass topicInfoCls;
|
||||
static JClass valueCls;
|
||||
static JClass valueEventDataCls;
|
||||
@@ -54,6 +55,7 @@ static const JClassInit classes[] = {
|
||||
{"java/lang/Float", &floatCls},
|
||||
{"edu/wpi/first/networktables/LogMessage", &logMessageCls},
|
||||
{"java/lang/Long", &longCls},
|
||||
{"edu/wpi/first/networktables/PubSubOptions", &pubSubOptionsCls},
|
||||
{"edu/wpi/first/networktables/TopicInfo", &topicInfoCls},
|
||||
{"edu/wpi/first/networktables/NetworkTableValue", &valueCls},
|
||||
{"edu/wpi/first/networktables/ValueEventData", &valueEventDataCls}};
|
||||
@@ -117,20 +119,45 @@ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {
|
||||
// Conversions from Java objects to C++
|
||||
//
|
||||
|
||||
static std::span<const nt::PubSubOption> FromJavaPubSubOptions(
|
||||
JNIEnv* env, jintArray optionTypes, jintArray optionValues,
|
||||
wpi::SmallVectorImpl<nt::PubSubOption>& arr) {
|
||||
JIntArrayRef types{env, optionTypes};
|
||||
JIntArrayRef values{env, optionValues};
|
||||
if (types.size() != values.size()) {
|
||||
static nt::PubSubOptions FromJavaPubSubOptions(JNIEnv* env, jobject joptions) {
|
||||
if (!joptions) {
|
||||
return {};
|
||||
}
|
||||
arr.clear();
|
||||
arr.reserve(types.size());
|
||||
for (size_t i = 0, iend = types.size(); i != iend; ++i) {
|
||||
arr.emplace_back(static_cast<NT_PubSubOptionType>(types[i]), values[i]);
|
||||
#define FIELD(name, sig) \
|
||||
static jfieldID name##Field = nullptr; \
|
||||
if (!name##Field) { \
|
||||
name##Field = env->GetFieldID(pubSubOptionsCls, #name, sig); \
|
||||
}
|
||||
return arr;
|
||||
|
||||
FIELD(pollStorage, "I");
|
||||
FIELD(periodic, "D");
|
||||
FIELD(excludePublisher, "I");
|
||||
FIELD(sendAll, "Z");
|
||||
FIELD(topicsOnly, "Z");
|
||||
FIELD(keepDuplicates, "Z");
|
||||
FIELD(prefixMatch, "Z");
|
||||
FIELD(disableRemote, "Z");
|
||||
FIELD(disableLocal, "Z");
|
||||
FIELD(excludeSelf, "Z");
|
||||
|
||||
#undef FIELD
|
||||
|
||||
#define FIELD(ctype, jtype, name) \
|
||||
.name = static_cast<ctype>(env->Get##jtype##Field(joptions, name##Field))
|
||||
|
||||
return {FIELD(unsigned int, Int, pollStorage),
|
||||
FIELD(double, Double, periodic),
|
||||
FIELD(NT_Publisher, Int, excludePublisher),
|
||||
FIELD(bool, Boolean, sendAll),
|
||||
FIELD(bool, Boolean, topicsOnly),
|
||||
FIELD(bool, Boolean, keepDuplicates),
|
||||
FIELD(bool, Boolean, prefixMatch),
|
||||
FIELD(bool, Boolean, disableRemote),
|
||||
FIELD(bool, Boolean, disableLocal),
|
||||
FIELD(bool, Boolean, excludeSelf)};
|
||||
|
||||
#undef GET
|
||||
#undef FIELD
|
||||
}
|
||||
|
||||
//
|
||||
@@ -356,7 +383,7 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_getInstanceFromHandle
|
||||
* Signature: (ILjava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_getEntry__ILjava_lang_String_2
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_getEntry
|
||||
(JNIEnv* env, jclass, jint inst, jstring key)
|
||||
{
|
||||
if (!key) {
|
||||
@@ -720,17 +747,15 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_setTopicProperties
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: subscribe
|
||||
* Signature: (IILjava/lang/String;[I[I)I
|
||||
* Signature: (IILjava/lang/String;Ljava/lang/Object;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_subscribe
|
||||
(JNIEnv* env, jclass, jint topic, jint type, jstring typeStr,
|
||||
jintArray optionTypes, jintArray optionValues)
|
||||
(JNIEnv* env, jclass, jint topic, jint type, jstring typeStr, jobject options)
|
||||
{
|
||||
wpi::SmallVector<nt::PubSubOption, 4> options;
|
||||
return nt::Subscribe(
|
||||
topic, static_cast<NT_Type>(type), JStringRef{env, typeStr},
|
||||
FromJavaPubSubOptions(env, optionTypes, optionValues, options));
|
||||
return nt::Subscribe(topic, static_cast<NT_Type>(type),
|
||||
JStringRef{env, typeStr},
|
||||
FromJavaPubSubOptions(env, options));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -748,28 +773,26 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_unsubscribe
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: publish
|
||||
* Signature: (IILjava/lang/String;[I[I)I
|
||||
* Signature: (IILjava/lang/String;Ljava/lang/Object;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_publish
|
||||
(JNIEnv* env, jclass, jint topic, jint type, jstring typeStr,
|
||||
jintArray optionTypes, jintArray optionValues)
|
||||
(JNIEnv* env, jclass, jint topic, jint type, jstring typeStr, jobject options)
|
||||
{
|
||||
wpi::SmallVector<nt::PubSubOption, 4> options;
|
||||
return nt::Publish(
|
||||
topic, static_cast<NT_Type>(type), JStringRef{env, typeStr},
|
||||
FromJavaPubSubOptions(env, optionTypes, optionValues, options));
|
||||
return nt::Publish(topic, static_cast<NT_Type>(type),
|
||||
JStringRef{env, typeStr},
|
||||
FromJavaPubSubOptions(env, options));
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: publishEx
|
||||
* Signature: (IILjava/lang/String;Ljava/lang/String;[I[I)I
|
||||
* Signature: (IILjava/lang/String;Ljava/lang/String;Ljava/lang/Object;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_publishEx
|
||||
(JNIEnv* env, jclass, jint topic, jint type, jstring typeStr,
|
||||
jstring properties, jintArray optionTypes, jintArray optionValues)
|
||||
jstring properties, jobject options)
|
||||
{
|
||||
wpi::json j;
|
||||
try {
|
||||
@@ -783,10 +806,9 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_publishEx
|
||||
illegalArgEx.Throw(env, "properties is not a JSON object");
|
||||
return 0;
|
||||
}
|
||||
wpi::SmallVector<nt::PubSubOption, 4> options;
|
||||
return nt::PublishEx(
|
||||
topic, static_cast<NT_Type>(type), JStringRef{env, typeStr}, j,
|
||||
FromJavaPubSubOptions(env, optionTypes, optionValues, options));
|
||||
return nt::PublishEx(topic, static_cast<NT_Type>(type),
|
||||
JStringRef{env, typeStr}, j,
|
||||
FromJavaPubSubOptions(env, options));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -803,18 +825,16 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_unpublish
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: getEntry
|
||||
* Signature: (IILjava/lang/String;[I[I)I
|
||||
* Method: getEntryImpl
|
||||
* Signature: (IILjava/lang/String;Ljava/lang/Object;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_getEntry__IILjava_lang_String_2_3I_3I
|
||||
(JNIEnv* env, jclass, jint topic, jint type, jstring typeStr,
|
||||
jintArray optionTypes, jintArray optionValues)
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_getEntryImpl
|
||||
(JNIEnv* env, jclass, jint topic, jint type, jstring typeStr, jobject options)
|
||||
{
|
||||
wpi::SmallVector<nt::PubSubOption, 4> options;
|
||||
return nt::GetEntry(
|
||||
topic, static_cast<NT_Type>(type), JStringRef{env, typeStr},
|
||||
FromJavaPubSubOptions(env, optionTypes, optionValues, options));
|
||||
return nt::GetEntry(topic, static_cast<NT_Type>(type),
|
||||
JStringRef{env, typeStr},
|
||||
FromJavaPubSubOptions(env, options));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -856,12 +876,11 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_getTopicFromHandle
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: subscribeMultiple
|
||||
* Signature: (I[Ljava/lang/Object;[I[I)I
|
||||
* Signature: (I[Ljava/lang/Object;Ljava/lang/Object;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_subscribeMultiple
|
||||
(JNIEnv* env, jclass, jint inst, jobjectArray prefixes, jintArray optionTypes,
|
||||
jintArray optionValues)
|
||||
(JNIEnv* env, jclass, jint inst, jobjectArray prefixes, jobject options)
|
||||
{
|
||||
if (!prefixes) {
|
||||
nullPointerEx.Throw(env, "prefixes cannot be null");
|
||||
@@ -884,10 +903,8 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_subscribeMultiple
|
||||
prefixStringViews.emplace_back(prefixStrings.back());
|
||||
}
|
||||
|
||||
wpi::SmallVector<nt::PubSubOption, 4> options;
|
||||
return nt::SubscribeMultiple(
|
||||
inst, prefixStringViews,
|
||||
FromJavaPubSubOptions(env, optionTypes, optionValues, options));
|
||||
return nt::SubscribeMultiple(inst, prefixStringViews,
|
||||
FromJavaPubSubOptions(env, options));
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace {
|
||||
|
||||
struct PublisherData {
|
||||
NT_Publisher handle;
|
||||
PubSubOptions options;
|
||||
PubSubOptionsImpl options;
|
||||
// in options as double, but copy here as integer; rounded to the nearest
|
||||
// 10 ms
|
||||
uint32_t periodMs;
|
||||
@@ -55,6 +55,7 @@ class CImpl : public ServerMessageHandler {
|
||||
void HandleLocal(std::vector<ClientMessage>&& msgs);
|
||||
bool SendControl(uint64_t curTimeMs);
|
||||
void SendValues(uint64_t curTimeMs);
|
||||
void SendInitialValues();
|
||||
bool CheckNetworkReady();
|
||||
|
||||
// ServerMessageHandler interface
|
||||
@@ -67,7 +68,7 @@ class CImpl : public ServerMessageHandler {
|
||||
|
||||
void Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
|
||||
std::string_view name, std::string_view typeStr,
|
||||
const wpi::json& properties, const PubSubOptions& options);
|
||||
const wpi::json& properties, const PubSubOptionsImpl& options);
|
||||
bool Unpublish(NT_Publisher pubHandle, NT_Topic topicHandle);
|
||||
void SetValue(NT_Publisher pubHandle, const Value& value);
|
||||
|
||||
@@ -229,7 +230,7 @@ bool CImpl::SendControl(uint64_t curTimeMs) {
|
||||
}
|
||||
|
||||
void CImpl::SendValues(uint64_t curTimeMs) {
|
||||
DEBUG4("SendPeriodic({})", curTimeMs);
|
||||
DEBUG4("SendValues({})", curTimeMs);
|
||||
|
||||
// can't send value updates until we have a RTT
|
||||
if (!m_haveTimeOffset) {
|
||||
@@ -268,6 +269,36 @@ void CImpl::SendValues(uint64_t curTimeMs) {
|
||||
}
|
||||
}
|
||||
|
||||
void CImpl::SendInitialValues() {
|
||||
DEBUG4("SendInitialValues()");
|
||||
|
||||
// ensure all control messages are sent ahead of value updates
|
||||
if (!SendControl(0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// only send time=0 values (as we don't have a RTT yet)
|
||||
auto writer = m_wire.SendBinary();
|
||||
for (auto&& pub : m_publishers) {
|
||||
if (pub && !pub->outValues.empty()) {
|
||||
bool sent = false;
|
||||
for (auto&& val : pub->outValues) {
|
||||
if (val.server_time() == 0) {
|
||||
DEBUG4("Sending {} value time={} server_time={}", pub->handle,
|
||||
val.time(), val.server_time());
|
||||
WireEncodeBinary(writer.Add(), Handle{pub->handle}.GetIndex(), 0,
|
||||
val);
|
||||
sent = true;
|
||||
}
|
||||
}
|
||||
if (sent) {
|
||||
std::erase_if(pub->outValues,
|
||||
[](const auto& v) { return v.server_time() == 0; });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CImpl::CheckNetworkReady() {
|
||||
if (!m_wire.Ready()) {
|
||||
++m_notReadyCount;
|
||||
@@ -282,7 +313,8 @@ bool CImpl::CheckNetworkReady() {
|
||||
|
||||
void CImpl::Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
|
||||
std::string_view name, std::string_view typeStr,
|
||||
const wpi::json& properties, const PubSubOptions& options) {
|
||||
const wpi::json& properties,
|
||||
const PubSubOptionsImpl& options) {
|
||||
unsigned int index = Handle{pubHandle}.GetIndex();
|
||||
if (index >= m_publishers.size()) {
|
||||
m_publishers.resize(index + 1);
|
||||
@@ -433,46 +465,7 @@ void ClientImpl::SetLocal(LocalInterface* local) {
|
||||
m_impl->m_local = local;
|
||||
}
|
||||
|
||||
ClientStartup::ClientStartup(ClientImpl& client)
|
||||
: m_client{client},
|
||||
m_binaryWriter{client.m_impl->m_wire.SendBinary()},
|
||||
m_textWriter{client.m_impl->m_wire.SendText()} {}
|
||||
|
||||
ClientStartup::~ClientStartup() {
|
||||
m_client.m_impl->m_wire.Flush();
|
||||
}
|
||||
|
||||
void ClientStartup::Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
|
||||
std::string_view name, std::string_view typeStr,
|
||||
const wpi::json& properties,
|
||||
const PubSubOptions& options) {
|
||||
WPI_DEBUG4(m_client.m_impl->m_logger, "StartupPublish({}, {}, {}, {})",
|
||||
pubHandle, topicHandle, name, typeStr);
|
||||
m_client.m_impl->Publish(pubHandle, topicHandle, name, typeStr, properties,
|
||||
options);
|
||||
WireEncodePublish(m_textWriter.Add(), Handle{pubHandle}.GetIndex(), name,
|
||||
typeStr, properties);
|
||||
}
|
||||
|
||||
void ClientStartup::Subscribe(NT_Subscriber subHandle,
|
||||
std::span<const std::string> prefixes,
|
||||
const PubSubOptions& options) {
|
||||
WPI_DEBUG4(m_client.m_impl->m_logger, "StartupSubscribe({})", subHandle);
|
||||
WireEncodeSubscribe(m_textWriter.Add(), Handle{subHandle}.GetIndex(),
|
||||
prefixes, options);
|
||||
}
|
||||
|
||||
void ClientStartup::SetValue(NT_Publisher pubHandle, const Value& value) {
|
||||
WPI_DEBUG4(m_client.m_impl->m_logger, "StartupSetValue({})", pubHandle);
|
||||
// Similar to Client::SetValue(), except always set lastValue and send
|
||||
unsigned int index = Handle{pubHandle}.GetIndex();
|
||||
assert(index < m_client.m_impl->m_publishers.size() &&
|
||||
m_client.m_impl->m_publishers[index]);
|
||||
auto& publisher = *m_client.m_impl->m_publishers[index];
|
||||
// only send time 0 values until we have a RTT
|
||||
if (value.server_time() == 0) {
|
||||
WireEncodeBinary(m_binaryWriter.Add(), index, 0, value);
|
||||
} else {
|
||||
publisher.outValues.emplace_back(value);
|
||||
}
|
||||
void ClientImpl::SendInitial() {
|
||||
m_impl->SendInitialValues();
|
||||
m_impl->m_wire.Flush();
|
||||
}
|
||||
|
||||
@@ -21,19 +21,16 @@ class Logger;
|
||||
} // namespace wpi
|
||||
|
||||
namespace nt {
|
||||
class PubSubOptions;
|
||||
class PubSubOptionsImpl;
|
||||
class Value;
|
||||
} // namespace nt
|
||||
|
||||
namespace nt::net {
|
||||
|
||||
struct ClientMessage;
|
||||
class ClientStartup;
|
||||
class WireConnection;
|
||||
|
||||
class ClientImpl {
|
||||
friend class ClientStartup;
|
||||
|
||||
public:
|
||||
ClientImpl(uint64_t curTimeMs, int inst, WireConnection& wire,
|
||||
wpi::Logger& logger,
|
||||
@@ -48,31 +45,11 @@ class ClientImpl {
|
||||
void SendValues(uint64_t curTimeMs);
|
||||
|
||||
void SetLocal(LocalInterface* local);
|
||||
void SendInitial();
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
};
|
||||
|
||||
class ClientStartup final : public NetworkStartupInterface {
|
||||
public:
|
||||
explicit ClientStartup(ClientImpl& client);
|
||||
~ClientStartup() final;
|
||||
ClientStartup(const ClientStartup&) = delete;
|
||||
ClientStartup& operator=(const ClientStartup&) = delete;
|
||||
|
||||
// NetworkStartupInterface interface
|
||||
void Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
|
||||
std::string_view name, std::string_view typeStr,
|
||||
const wpi::json& properties, const PubSubOptions& options) final;
|
||||
void Subscribe(NT_Subscriber subHandle, std::span<const std::string> prefixes,
|
||||
const PubSubOptions& options) final;
|
||||
void SetValue(NT_Publisher pubHandle, const Value& value) final;
|
||||
|
||||
private:
|
||||
ClientImpl& m_client;
|
||||
BinaryWriter m_binaryWriter;
|
||||
TextWriter m_textWriter;
|
||||
};
|
||||
|
||||
} // namespace nt::net
|
||||
|
||||
@@ -24,7 +24,7 @@ struct PublishMsg {
|
||||
std::string name;
|
||||
std::string typeStr;
|
||||
wpi::json properties;
|
||||
PubSubOptions options; // will be empty when coming from network
|
||||
PubSubOptionsImpl options; // will be empty when coming from network
|
||||
};
|
||||
|
||||
struct UnpublishMsg {
|
||||
@@ -44,7 +44,7 @@ struct SubscribeMsg {
|
||||
static constexpr std::string_view kMethodStr = "subscribe";
|
||||
NT_Subscriber subHandle{0};
|
||||
std::vector<std::string> topicNames;
|
||||
PubSubOptions options;
|
||||
PubSubOptionsImpl options;
|
||||
};
|
||||
|
||||
struct UnsubscribeMsg {
|
||||
|
||||
@@ -15,7 +15,7 @@ class json;
|
||||
} // namespace wpi
|
||||
|
||||
namespace nt {
|
||||
class PubSubOptions;
|
||||
class PubSubOptionsImpl;
|
||||
class Value;
|
||||
} // namespace nt
|
||||
|
||||
@@ -35,32 +35,27 @@ class LocalInterface {
|
||||
virtual void NetworkSetValue(NT_Topic topicHandle, const Value& value) = 0;
|
||||
};
|
||||
|
||||
class NetworkStartupInterface {
|
||||
class NetworkInterface {
|
||||
public:
|
||||
virtual ~NetworkStartupInterface() = default;
|
||||
virtual ~NetworkInterface() = default;
|
||||
|
||||
virtual void Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
|
||||
std::string_view name, std::string_view typeStr,
|
||||
const wpi::json& properties,
|
||||
const PubSubOptions& options) = 0;
|
||||
virtual void Subscribe(NT_Subscriber subHandle,
|
||||
std::span<const std::string> topicNames,
|
||||
const PubSubOptions& options) = 0;
|
||||
virtual void SetValue(NT_Publisher pubHandle, const Value& value) = 0;
|
||||
};
|
||||
|
||||
class NetworkInterface : public NetworkStartupInterface {
|
||||
public:
|
||||
const PubSubOptionsImpl& options) = 0;
|
||||
virtual void Unpublish(NT_Publisher pubHandle, NT_Topic topicHandle) = 0;
|
||||
virtual void SetProperties(NT_Topic topicHandle, std::string_view name,
|
||||
const wpi::json& update) = 0;
|
||||
virtual void Subscribe(NT_Subscriber subHandle,
|
||||
std::span<const std::string> topicNames,
|
||||
const PubSubOptionsImpl& options) = 0;
|
||||
virtual void Unsubscribe(NT_Subscriber subHandle) = 0;
|
||||
virtual void SetValue(NT_Publisher pubHandle, const Value& value) = 0;
|
||||
};
|
||||
|
||||
class ILocalStorage : public LocalInterface {
|
||||
public:
|
||||
virtual void StartNetwork(NetworkStartupInterface& startup,
|
||||
NetworkInterface* network) = 0;
|
||||
virtual void StartNetwork(NetworkInterface* network) = 0;
|
||||
virtual void ClearNetwork() = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -33,13 +33,14 @@ class NetworkLoopQueue : public NetworkInterface {
|
||||
// NetworkInterface - calls to these append to the queue
|
||||
void Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
|
||||
std::string_view name, std::string_view typeStr,
|
||||
const wpi::json& properties, const PubSubOptions& options) final;
|
||||
const wpi::json& properties,
|
||||
const PubSubOptionsImpl& options) final;
|
||||
void Unpublish(NT_Publisher pubHandle, NT_Topic topicHandle) final;
|
||||
void SetProperties(NT_Topic topicHandle, std::string_view name,
|
||||
const wpi::json& update) final;
|
||||
void Subscribe(NT_Subscriber subHandle,
|
||||
std::span<const std::string> topicNames,
|
||||
const PubSubOptions& options) final;
|
||||
const PubSubOptionsImpl& options) final;
|
||||
void Unsubscribe(NT_Subscriber subHandle) final;
|
||||
void SetValue(NT_Publisher pubHandle, const Value& value) final;
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ inline void NetworkLoopQueue::Publish(NT_Publisher pubHandle,
|
||||
std::string_view name,
|
||||
std::string_view typeStr,
|
||||
const wpi::json& properties,
|
||||
const PubSubOptions& options) {
|
||||
const PubSubOptionsImpl& options) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_queue.emplace_back(
|
||||
ClientMessage{PublishMsg{pubHandle, topicHandle, std::string{name},
|
||||
@@ -57,7 +57,7 @@ inline void NetworkLoopQueue::SetProperties(NT_Topic topicHandle,
|
||||
|
||||
inline void NetworkLoopQueue::Subscribe(NT_Subscriber subHandle,
|
||||
std::span<const std::string> topicNames,
|
||||
const PubSubOptions& options) {
|
||||
const PubSubOptionsImpl& options) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_queue.emplace_back(ClientMessage{SubscribeMsg{
|
||||
subHandle, {topicNames.begin(), topicNames.end()}, options}});
|
||||
|
||||
@@ -159,7 +159,7 @@ class ClientData4Base : public ClientData, protected ClientMessageHandler {
|
||||
void ClientSetProperties(std::string_view name,
|
||||
const wpi::json& update) final;
|
||||
void ClientSubscribe(int64_t subuid, std::span<const std::string> topicNames,
|
||||
const PubSubOptions& options) final;
|
||||
const PubSubOptionsImpl& options) final;
|
||||
void ClientUnsubscribe(int64_t subuid) final;
|
||||
|
||||
void ClientSetValue(int64_t pubuid, const Value& value);
|
||||
@@ -168,8 +168,6 @@ class ClientData4Base : public ClientData, protected ClientMessageHandler {
|
||||
};
|
||||
|
||||
class ClientDataLocal final : public ClientData4Base {
|
||||
friend class net::ServerStartup;
|
||||
|
||||
public:
|
||||
ClientDataLocal(SImpl& server, int id, wpi::Logger& logger)
|
||||
: ClientData4Base{"", "", "", true, [](uint32_t) {}, server, id, logger} {
|
||||
@@ -362,7 +360,7 @@ struct PublisherData {
|
||||
|
||||
struct SubscriberData {
|
||||
SubscriberData(ClientData* client, std::span<const std::string> topicNames,
|
||||
int64_t subuid, const PubSubOptions& options)
|
||||
int64_t subuid, const PubSubOptionsImpl& options)
|
||||
: client{client},
|
||||
topicNames{topicNames.begin(), topicNames.end()},
|
||||
subuid{subuid},
|
||||
@@ -374,7 +372,7 @@ struct SubscriberData {
|
||||
}
|
||||
|
||||
void Update(std::span<const std::string> topicNames_,
|
||||
const PubSubOptions& options_) {
|
||||
const PubSubOptionsImpl& options_) {
|
||||
topicNames = {topicNames_.begin(), topicNames_.end()};
|
||||
options = options_;
|
||||
periodMs = std::lround(options_.periodicMs / 10.0) * 10;
|
||||
@@ -388,7 +386,7 @@ struct SubscriberData {
|
||||
ClientData* client;
|
||||
std::vector<std::string> topicNames;
|
||||
int64_t subuid;
|
||||
PubSubOptions options;
|
||||
PubSubOptionsImpl options;
|
||||
// in options as double, but copy here as integer; rounded to the nearest
|
||||
// 10 ms
|
||||
uint32_t periodMs;
|
||||
@@ -463,10 +461,11 @@ struct Writer : public mpack_writer_t {
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static void WriteOptions(mpack_writer_t& w, const PubSubOptions& options) {
|
||||
int size = (options.sendAll ? 1 : 0) + (options.topicsOnly ? 1 : 0) +
|
||||
(options.periodicMs != PubSubOptions::kDefaultPeriodicMs ? 1 : 0) +
|
||||
(options.prefixMatch ? 1 : 0);
|
||||
static void WriteOptions(mpack_writer_t& w, const PubSubOptionsImpl& options) {
|
||||
int size =
|
||||
(options.sendAll ? 1 : 0) + (options.topicsOnly ? 1 : 0) +
|
||||
(options.periodicMs != PubSubOptionsImpl::kDefaultPeriodicMs ? 1 : 0) +
|
||||
(options.prefixMatch ? 1 : 0);
|
||||
mpack_start_map(&w, size);
|
||||
if (options.sendAll) {
|
||||
mpack_write_str(&w, "all");
|
||||
@@ -476,7 +475,7 @@ static void WriteOptions(mpack_writer_t& w, const PubSubOptions& options) {
|
||||
mpack_write_str(&w, "topicsonly");
|
||||
mpack_write_bool(&w, true);
|
||||
}
|
||||
if (options.periodicMs != PubSubOptions::kDefaultPeriodicMs) {
|
||||
if (options.periodicMs != PubSubOptionsImpl::kDefaultPeriodicMs) {
|
||||
mpack_write_str(&w, "periodic");
|
||||
mpack_write_float(&w, options.periodicMs / 1000.0);
|
||||
}
|
||||
@@ -616,7 +615,7 @@ void ClientData4Base::ClientSetProperties(std::string_view name,
|
||||
|
||||
void ClientData4Base::ClientSubscribe(int64_t subuid,
|
||||
std::span<const std::string> topicNames,
|
||||
const PubSubOptions& options) {
|
||||
const PubSubOptionsImpl& options) {
|
||||
DEBUG4("ClientSubscribe({}, ({}), {})", m_id, fmt::join(topicNames, ","),
|
||||
subuid);
|
||||
auto& sub = m_subscribers[subuid];
|
||||
@@ -2299,6 +2298,7 @@ void ServerImpl::HandleLocal(std::span<const ClientMessage> msgs) {
|
||||
}
|
||||
|
||||
void ServerImpl::SetLocal(LocalInterface* local) {
|
||||
WPI_DEBUG4(m_impl->m_logger, "SetLocal()");
|
||||
m_impl->m_local = local;
|
||||
|
||||
// create server meta topics
|
||||
@@ -2361,22 +2361,3 @@ std::string ServerImpl::DumpPersistent() {
|
||||
std::string ServerImpl::LoadPersistent(std::string_view in) {
|
||||
return m_impl->LoadPersistent(in);
|
||||
}
|
||||
|
||||
void ServerStartup::Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
|
||||
std::string_view name, std::string_view typeStr,
|
||||
const wpi::json& properties,
|
||||
const PubSubOptions& options) {
|
||||
m_server.m_impl->m_localClient->ClientPublish(pubHandle, name, typeStr,
|
||||
properties);
|
||||
}
|
||||
|
||||
void ServerStartup::Subscribe(NT_Subscriber subHandle,
|
||||
std::span<const std::string> topicNames,
|
||||
const PubSubOptions& options) {
|
||||
m_server.m_impl->m_localClient->ClientSubscribe(subHandle, topicNames,
|
||||
options);
|
||||
}
|
||||
|
||||
void ServerStartup::SetValue(NT_Publisher pubHandle, const Value& value) {
|
||||
m_server.m_impl->m_localClient->ClientSetValue(pubHandle, value);
|
||||
}
|
||||
|
||||
@@ -29,12 +29,9 @@ namespace nt::net {
|
||||
|
||||
struct ClientMessage;
|
||||
class LocalInterface;
|
||||
class ServerStartup;
|
||||
class WireConnection;
|
||||
|
||||
class ServerImpl final {
|
||||
friend class ServerStartup;
|
||||
|
||||
public:
|
||||
using SetPeriodicFunc = std::function<void(uint32_t repeatMs)>;
|
||||
using Connected3Func =
|
||||
@@ -76,21 +73,4 @@ class ServerImpl final {
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
};
|
||||
|
||||
class ServerStartup final : public NetworkStartupInterface {
|
||||
public:
|
||||
explicit ServerStartup(ServerImpl& server) : m_server{server} {}
|
||||
|
||||
// NetworkStartupInterface interface
|
||||
void Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
|
||||
std::string_view name, std::string_view typeStr,
|
||||
const wpi::json& properties, const PubSubOptions& options) final;
|
||||
void Subscribe(NT_Subscriber subHandle,
|
||||
std::span<const std::string> topicNames,
|
||||
const PubSubOptions& options) final;
|
||||
void SetValue(NT_Publisher pubHandle, const Value& value) final;
|
||||
|
||||
private:
|
||||
ServerImpl& m_server;
|
||||
};
|
||||
|
||||
} // namespace nt::net
|
||||
|
||||
@@ -226,7 +226,7 @@ static void WireDecodeTextImpl(std::string_view in, T& out,
|
||||
}
|
||||
|
||||
// options
|
||||
PubSubOptions options;
|
||||
PubSubOptionsImpl options;
|
||||
auto optionsIt = params->find("options");
|
||||
if (optionsIt != params->end()) {
|
||||
auto joptions = optionsIt->second.get_ptr<wpi::json::object_t*>();
|
||||
@@ -243,6 +243,7 @@ static void WireDecodeTextImpl(std::string_view in, T& out,
|
||||
error = "periodic value must be a number";
|
||||
goto err;
|
||||
}
|
||||
options.periodic = val;
|
||||
options.periodicMs = val * 1000;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class json;
|
||||
} // namespace wpi
|
||||
|
||||
namespace nt {
|
||||
class PubSubOptions;
|
||||
class PubSubOptionsImpl;
|
||||
class Value;
|
||||
} // namespace nt
|
||||
|
||||
@@ -35,7 +35,7 @@ class ClientMessageHandler {
|
||||
const wpi::json& update) = 0;
|
||||
virtual void ClientSubscribe(int64_t subuid,
|
||||
std::span<const std::string> topicNames,
|
||||
const PubSubOptions& options) = 0;
|
||||
const PubSubOptionsImpl& options) = 0;
|
||||
virtual void ClientUnsubscribe(int64_t subuid) = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ static void EncodePrefixes(wpi::raw_ostream& os, std::span<const T> topicNames,
|
||||
template <typename T>
|
||||
static void WireEncodeSubscribeImpl(wpi::raw_ostream& os, int64_t subuid,
|
||||
std::span<const T> topicNames,
|
||||
const PubSubOptions& options) {
|
||||
const PubSubOptionsImpl& options) {
|
||||
wpi::json::serializer s{os, ' ', 0};
|
||||
os << "{\"method\":\"" << SubscribeMsg::kMethodStr << "\",\"params\":{";
|
||||
os << "\"options\":{";
|
||||
@@ -99,7 +99,7 @@ static void WireEncodeSubscribeImpl(wpi::raw_ostream& os, int64_t subuid,
|
||||
os << "\"prefix\":true";
|
||||
first = false;
|
||||
}
|
||||
if (options.periodicMs != PubSubOptions::kDefaultPeriodicMs) {
|
||||
if (options.periodicMs != PubSubOptionsImpl::kDefaultPeriodicMs) {
|
||||
if (!first) {
|
||||
os << ',';
|
||||
}
|
||||
@@ -115,13 +115,13 @@ static void WireEncodeSubscribeImpl(wpi::raw_ostream& os, int64_t subuid,
|
||||
|
||||
void nt::net::WireEncodeSubscribe(wpi::raw_ostream& os, int64_t subuid,
|
||||
std::span<const std::string_view> topicNames,
|
||||
const PubSubOptions& options) {
|
||||
const PubSubOptionsImpl& options) {
|
||||
WireEncodeSubscribeImpl(os, subuid, topicNames, options);
|
||||
}
|
||||
|
||||
void nt::net::WireEncodeSubscribe(wpi::raw_ostream& os, int64_t subuid,
|
||||
std::span<const std::string> topicNames,
|
||||
const PubSubOptions& options) {
|
||||
const PubSubOptionsImpl& options) {
|
||||
WireEncodeSubscribeImpl(os, subuid, topicNames, options);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ class raw_ostream;
|
||||
} // namespace wpi
|
||||
|
||||
namespace nt {
|
||||
class PubSubOptions;
|
||||
class PubSubOptionsImpl;
|
||||
class Value;
|
||||
} // namespace nt
|
||||
|
||||
@@ -33,10 +33,10 @@ void WireEncodeSetProperties(wpi::raw_ostream& os, std::string_view name,
|
||||
const wpi::json& update);
|
||||
void WireEncodeSubscribe(wpi::raw_ostream& os, int64_t subuid,
|
||||
std::span<const std::string_view> topicNames,
|
||||
const PubSubOptions& options);
|
||||
const PubSubOptionsImpl& options);
|
||||
void WireEncodeSubscribe(wpi::raw_ostream& os, int64_t subuid,
|
||||
std::span<const std::string> topicNames,
|
||||
const PubSubOptions& options);
|
||||
const PubSubOptionsImpl& options);
|
||||
void WireEncodeUnsubscribe(wpi::raw_ostream& os, int64_t subuid);
|
||||
|
||||
// encoders for server text messages (avoids need to construct a Message struct)
|
||||
|
||||
@@ -44,7 +44,7 @@ struct PublisherData {
|
||||
|
||||
Entry* entry;
|
||||
NT_Publisher handle;
|
||||
PubSubOptions options;
|
||||
PubSubOptionsImpl options;
|
||||
// in options as double, but copy here as integer; rounded to the nearest
|
||||
// 10 ms
|
||||
uint32_t periodMs;
|
||||
@@ -98,7 +98,7 @@ class CImpl : public MessageHandler3 {
|
||||
// Outgoing handlers
|
||||
void Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
|
||||
std::string_view name, std::string_view typeStr,
|
||||
const wpi::json& properties, const PubSubOptions& options);
|
||||
const wpi::json& properties, const PubSubOptionsImpl& options);
|
||||
void Unpublish(NT_Publisher pubHandle, NT_Topic topicHandle);
|
||||
void SetProperties(NT_Topic topicHandle, std::string_view name,
|
||||
const wpi::json& update);
|
||||
@@ -315,7 +315,8 @@ bool CImpl::CheckNetworkReady() {
|
||||
|
||||
void CImpl::Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
|
||||
std::string_view name, std::string_view typeStr,
|
||||
const wpi::json& properties, const PubSubOptions& options) {
|
||||
const wpi::json& properties,
|
||||
const PubSubOptionsImpl& options) {
|
||||
DEBUG4("Publish('{}', '{}')", name, typeStr);
|
||||
unsigned int index = Handle{pubHandle}.GetIndex();
|
||||
if (index >= m_publishers.size()) {
|
||||
@@ -639,34 +640,3 @@ void ClientImpl3::SendPeriodic(uint64_t curTimeMs) {
|
||||
void ClientImpl3::SetLocal(net::LocalInterface* local) {
|
||||
m_impl->m_local = local;
|
||||
}
|
||||
|
||||
ClientStartup3::ClientStartup3(ClientImpl3& client) : m_client{client} {}
|
||||
|
||||
ClientStartup3::~ClientStartup3() = default;
|
||||
|
||||
void ClientStartup3::Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
|
||||
std::string_view name, std::string_view typeStr,
|
||||
const wpi::json& properties,
|
||||
const PubSubOptions& options) {
|
||||
WPI_DEBUG4(m_client.m_impl->m_logger, "StartupPublish({}, {}, {}, {})",
|
||||
pubHandle, topicHandle, name, typeStr);
|
||||
m_client.m_impl->Publish(pubHandle, topicHandle, name, typeStr, properties,
|
||||
options);
|
||||
}
|
||||
|
||||
void ClientStartup3::Subscribe(NT_Subscriber subHandle,
|
||||
std::span<const std::string> prefixes,
|
||||
const PubSubOptions& options) {
|
||||
// NT3 ignores subscribes, so no action required
|
||||
}
|
||||
|
||||
void ClientStartup3::SetValue(NT_Publisher pubHandle, const Value& value) {
|
||||
WPI_DEBUG4(m_client.m_impl->m_logger, "StartupSetValue({})", pubHandle);
|
||||
// Similar to Client::SetValue(), except always set value and queue
|
||||
unsigned int index = Handle{pubHandle}.GetIndex();
|
||||
assert(index < m_client.m_impl->m_publishers.size() &&
|
||||
m_client.m_impl->m_publishers[index]);
|
||||
auto& publisher = *m_client.m_impl->m_publishers[index];
|
||||
publisher.entry->value = value;
|
||||
publisher.outValues.emplace_back(value);
|
||||
}
|
||||
|
||||
@@ -25,12 +25,9 @@ class LocalInterface;
|
||||
|
||||
namespace nt::net3 {
|
||||
|
||||
class ClientStartup3;
|
||||
class WireConnection3;
|
||||
|
||||
class ClientImpl3 {
|
||||
friend class ClientStartup3;
|
||||
|
||||
public:
|
||||
explicit ClientImpl3(uint64_t curTimeMs, int inst, WireConnection3& wire,
|
||||
wpi::Logger& logger,
|
||||
@@ -50,23 +47,4 @@ class ClientImpl3 {
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
};
|
||||
|
||||
class ClientStartup3 final : public net::NetworkStartupInterface {
|
||||
public:
|
||||
explicit ClientStartup3(ClientImpl3& client);
|
||||
~ClientStartup3() final;
|
||||
ClientStartup3(const ClientStartup3&) = delete;
|
||||
ClientStartup3& operator=(const ClientStartup3&) = delete;
|
||||
|
||||
// NetworkStartupInterface interface
|
||||
void Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
|
||||
std::string_view name, std::string_view typeStr,
|
||||
const wpi::json& properties, const PubSubOptions& options) final;
|
||||
void Subscribe(NT_Subscriber subHandle, std::span<const std::string> prefixes,
|
||||
const PubSubOptions& options) final;
|
||||
void SetValue(NT_Publisher pubHandle, const Value& value) final;
|
||||
|
||||
private:
|
||||
ClientImpl3& m_client;
|
||||
};
|
||||
|
||||
} // namespace nt::net3
|
||||
|
||||
@@ -88,14 +88,9 @@ std::vector<std::string> NetworkTable::GetHierarchy(std::string_view key) {
|
||||
|
||||
NetworkTable::NetworkTable(NT_Inst inst, std::string_view path,
|
||||
const private_init&)
|
||||
: m_inst(inst),
|
||||
m_path(path),
|
||||
m_topicSub{::nt::SubscribeMultiple(inst, {{fmt::format("{}/", path)}},
|
||||
{{PubSubOption::TopicsOnly(true)}})} {}
|
||||
: m_inst(inst), m_path(path) {}
|
||||
|
||||
NetworkTable::~NetworkTable() {
|
||||
::nt::UnsubscribeMultiple(m_topicSub);
|
||||
}
|
||||
NetworkTable::~NetworkTable() = default;
|
||||
|
||||
NetworkTableInstance NetworkTable::GetInstance() const {
|
||||
return NetworkTableInstance{m_inst};
|
||||
@@ -405,8 +400,8 @@ NT_Listener NetworkTable::AddSubTableListener(SubTableListener listener) {
|
||||
// a shared_ptr to it.
|
||||
auto notified_tables = std::make_shared<wpi::StringMap<char>>();
|
||||
|
||||
return ::nt::AddListener(
|
||||
m_topicSub, NT_EVENT_PUBLISH | NT_EVENT_IMMEDIATE,
|
||||
return NetworkTableInstance{m_inst}.AddListener(
|
||||
{{fmt::format("{}/", m_path)}}, NT_EVENT_PUBLISH | NT_EVENT_IMMEDIATE,
|
||||
[this, cb = std::move(listener), notified_tables](const Event& event) {
|
||||
auto topicInfo = event.GetTopicInfo();
|
||||
if (!topicInfo) {
|
||||
|
||||
@@ -22,37 +22,36 @@ wpi::json Topic::GetProperties() const {
|
||||
return ::nt::GetTopicProperties(m_handle);
|
||||
}
|
||||
|
||||
GenericSubscriber Topic::GenericSubscribe(
|
||||
std::span<const PubSubOption> options) {
|
||||
GenericSubscriber Topic::GenericSubscribe(const PubSubOptions& options) {
|
||||
return GenericSubscribe("", options);
|
||||
}
|
||||
|
||||
GenericSubscriber Topic::GenericSubscribe(
|
||||
std::string_view typeString, std::span<const PubSubOption> options) {
|
||||
GenericSubscriber Topic::GenericSubscribe(std::string_view typeString,
|
||||
const PubSubOptions& options) {
|
||||
return GenericSubscriber{::nt::Subscribe(
|
||||
m_handle, ::nt::GetTypeFromString(typeString), typeString, options)};
|
||||
}
|
||||
|
||||
GenericPublisher Topic::GenericPublish(std::string_view typeString,
|
||||
std::span<const PubSubOption> options) {
|
||||
const PubSubOptions& options) {
|
||||
return GenericPublisher{::nt::Publish(
|
||||
m_handle, ::nt::GetTypeFromString(typeString), typeString, options)};
|
||||
}
|
||||
|
||||
GenericPublisher Topic::GenericPublishEx(
|
||||
std::string_view typeString, const wpi::json& properties,
|
||||
std::span<const PubSubOption> options) {
|
||||
GenericPublisher Topic::GenericPublishEx(std::string_view typeString,
|
||||
const wpi::json& properties,
|
||||
const PubSubOptions& options) {
|
||||
return GenericPublisher{::nt::PublishEx(m_handle,
|
||||
::nt::GetTypeFromString(typeString),
|
||||
typeString, properties, options)};
|
||||
}
|
||||
|
||||
GenericEntry Topic::GetGenericEntry(std::span<const PubSubOption> options) {
|
||||
GenericEntry Topic::GetGenericEntry(const PubSubOptions& options) {
|
||||
return GetGenericEntry("", options);
|
||||
}
|
||||
|
||||
GenericEntry Topic::GetGenericEntry(std::string_view typeString,
|
||||
std::span<const PubSubOption> options) {
|
||||
const PubSubOptions& options) {
|
||||
return GenericEntry{::nt::GetEntry(
|
||||
m_handle, ::nt::GetTypeFromString(typeString), typeString, options)};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// 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 "ntcore_c.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cassert>
|
||||
@@ -101,6 +103,21 @@ static void DisposeEvent(NT_Event* event) {
|
||||
}
|
||||
}
|
||||
|
||||
static PubSubOptions ConvertToCpp(const NT_PubSubOptions* in) {
|
||||
PubSubOptions out;
|
||||
out.pollStorage = in->pollStorage;
|
||||
out.periodic = in->periodic;
|
||||
out.excludePublisher = in->excludePublisher;
|
||||
out.sendAll = in->sendAll;
|
||||
out.topicsOnly = in->topicsOnly;
|
||||
out.prefixMatch = in->prefixMatch;
|
||||
out.keepDuplicates = in->keepDuplicates;
|
||||
out.disableRemote = in->disableRemote;
|
||||
out.disableLocal = in->disableLocal;
|
||||
out.excludeSelf = in->excludeSelf;
|
||||
return out;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
/*
|
||||
@@ -314,14 +331,8 @@ NT_Bool NT_SetTopicProperties(NT_Topic topic, const char* properties) {
|
||||
}
|
||||
|
||||
NT_Subscriber NT_Subscribe(NT_Topic topic, NT_Type type, const char* typeStr,
|
||||
const struct NT_PubSubOption* options,
|
||||
size_t options_len) {
|
||||
wpi::SmallVector<nt::PubSubOption> o;
|
||||
o.reserve(options_len);
|
||||
for (size_t i = 0; i < options_len; ++i) {
|
||||
o.emplace_back(options[i].type, options[i].value);
|
||||
}
|
||||
return nt::Subscribe(topic, type, typeStr, o);
|
||||
const struct NT_PubSubOptions* options) {
|
||||
return nt::Subscribe(topic, type, typeStr, ConvertToCpp(options));
|
||||
}
|
||||
|
||||
void NT_Unsubscribe(NT_Subscriber sub) {
|
||||
@@ -329,20 +340,13 @@ void NT_Unsubscribe(NT_Subscriber sub) {
|
||||
}
|
||||
|
||||
NT_Publisher NT_Publish(NT_Topic topic, NT_Type type, const char* typeStr,
|
||||
const struct NT_PubSubOption* options,
|
||||
size_t options_len) {
|
||||
wpi::SmallVector<nt::PubSubOption> o;
|
||||
o.reserve(options_len);
|
||||
for (size_t i = 0; i < options_len; ++i) {
|
||||
o.emplace_back(options[i].type, options[i].value);
|
||||
}
|
||||
return nt::Publish(topic, type, typeStr, o);
|
||||
const struct NT_PubSubOptions* options) {
|
||||
return nt::Publish(topic, type, typeStr, ConvertToCpp(options));
|
||||
}
|
||||
|
||||
NT_Publisher NT_PublishEx(NT_Topic topic, NT_Type type, const char* typeStr,
|
||||
const char* properties,
|
||||
const struct NT_PubSubOption* options,
|
||||
size_t options_len) {
|
||||
const struct NT_PubSubOptions* options) {
|
||||
wpi::json j;
|
||||
if (properties[0] == '\0') {
|
||||
// gracefully handle empty string
|
||||
@@ -355,13 +359,7 @@ NT_Publisher NT_PublishEx(NT_Topic topic, NT_Type type, const char* typeStr,
|
||||
}
|
||||
}
|
||||
|
||||
wpi::SmallVector<nt::PubSubOption> o;
|
||||
o.reserve(options_len);
|
||||
for (size_t i = 0; i < options_len; ++i) {
|
||||
o.emplace_back(options[i].type, options[i].value);
|
||||
}
|
||||
|
||||
return nt::PublishEx(topic, type, typeStr, j, o);
|
||||
return nt::PublishEx(topic, type, typeStr, j, ConvertToCpp(options));
|
||||
}
|
||||
|
||||
void NT_Unpublish(NT_Handle pubentry) {
|
||||
@@ -369,14 +367,8 @@ void NT_Unpublish(NT_Handle pubentry) {
|
||||
}
|
||||
|
||||
NT_Entry NT_GetEntryEx(NT_Topic topic, NT_Type type, const char* typeStr,
|
||||
const struct NT_PubSubOption* options,
|
||||
size_t options_len) {
|
||||
wpi::SmallVector<nt::PubSubOption> o;
|
||||
o.reserve(options_len);
|
||||
for (size_t i = 0; i < options_len; ++i) {
|
||||
o.emplace_back(options[i].type, options[i].value);
|
||||
}
|
||||
return nt::GetEntry(topic, type, typeStr, o);
|
||||
const struct NT_PubSubOptions* options) {
|
||||
return nt::GetEntry(topic, type, typeStr, ConvertToCpp(options));
|
||||
}
|
||||
|
||||
void NT_ReleaseEntry(NT_Entry entry) {
|
||||
|
||||
@@ -310,7 +310,7 @@ bool SetTopicProperties(NT_Topic topic, const wpi::json& properties) {
|
||||
}
|
||||
|
||||
NT_Subscriber Subscribe(NT_Topic topic, NT_Type type, std::string_view typeStr,
|
||||
std::span<const PubSubOption> options) {
|
||||
const PubSubOptions& options) {
|
||||
if (auto ii = InstanceImpl::GetTyped(topic, Handle::kTopic)) {
|
||||
return ii->localStorage.Subscribe(topic, type, typeStr, options);
|
||||
} else {
|
||||
@@ -325,13 +325,13 @@ void Unsubscribe(NT_Subscriber sub) {
|
||||
}
|
||||
|
||||
NT_Publisher Publish(NT_Topic topic, NT_Type type, std::string_view typeStr,
|
||||
std::span<const PubSubOption> options) {
|
||||
const PubSubOptions& options) {
|
||||
return PublishEx(topic, type, typeStr, wpi::json::object(), options);
|
||||
}
|
||||
|
||||
NT_Publisher PublishEx(NT_Topic topic, NT_Type type, std::string_view typeStr,
|
||||
const wpi::json& properties,
|
||||
std::span<const PubSubOption> options) {
|
||||
const PubSubOptions& options) {
|
||||
if (auto ii = InstanceImpl::GetTyped(topic, Handle::kTopic)) {
|
||||
return ii->localStorage.Publish(topic, type, typeStr, properties, options);
|
||||
} else {
|
||||
@@ -346,7 +346,7 @@ void Unpublish(NT_Handle pubentry) {
|
||||
}
|
||||
|
||||
NT_Entry GetEntry(NT_Topic topic, NT_Type type, std::string_view typeStr,
|
||||
std::span<const PubSubOption> options) {
|
||||
const PubSubOptions& options) {
|
||||
if (auto ii = InstanceImpl::GetTyped(topic, Handle::kTopic)) {
|
||||
return ii->localStorage.GetEntry(topic, type, typeStr, options);
|
||||
} else {
|
||||
@@ -376,7 +376,7 @@ NT_Topic GetTopicFromHandle(NT_Handle pubsubentry) {
|
||||
|
||||
NT_MultiSubscriber SubscribeMultiple(NT_Inst inst,
|
||||
std::span<const std::string_view> prefixes,
|
||||
std::span<const PubSubOption> options) {
|
||||
const PubSubOptions& options) {
|
||||
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
|
||||
return ii->localStorage.SubscribeMultiple(prefixes, options);
|
||||
} else {
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
#include "Value_internal.h"
|
||||
|
||||
extern "C" {
|
||||
struct NT_String* NT_GetStringForTesting(const char* string, int* struct_size) {
|
||||
struct NT_String* str =
|
||||
struct NT_String* NT_GetStringForTesting(const char* str, int* struct_size) {
|
||||
struct NT_String* strout =
|
||||
static_cast<NT_String*>(wpi::safe_calloc(1, sizeof(NT_String)));
|
||||
nt::ConvertToC(string, str);
|
||||
nt::ConvertToC(str, strout);
|
||||
*struct_size = sizeof(NT_String);
|
||||
return str;
|
||||
return strout;
|
||||
}
|
||||
|
||||
struct NT_TopicInfo* NT_GetTopicInfoForTesting(const char* name,
|
||||
|
||||
@@ -30,7 +30,7 @@ class MultiSubscriber final {
|
||||
*/
|
||||
MultiSubscriber(NetworkTableInstance inst,
|
||||
std::span<const std::string_view> prefixes,
|
||||
std::span<const PubSubOption> options = {});
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
MultiSubscriber(const MultiSubscriber&) = delete;
|
||||
MultiSubscriber& operator=(const MultiSubscriber&) = delete;
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace nt {
|
||||
|
||||
inline MultiSubscriber::MultiSubscriber(
|
||||
NetworkTableInstance inst, std::span<const std::string_view> prefixes,
|
||||
std::span<const PubSubOption> options)
|
||||
const PubSubOptions& options)
|
||||
: m_handle{::nt::SubscribeMultiple(inst.GetHandle(), prefixes, options)} {}
|
||||
|
||||
inline MultiSubscriber::MultiSubscriber(MultiSubscriber&& rhs)
|
||||
|
||||
@@ -48,7 +48,6 @@ class NetworkTable final {
|
||||
private:
|
||||
NT_Inst m_inst;
|
||||
std::string m_path;
|
||||
NT_MultiSubscriber m_topicSub;
|
||||
mutable wpi::mutex m_mutex;
|
||||
mutable wpi::StringMap<NT_Entry> m_entries;
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
@@ -171,7 +170,7 @@ class Topic {
|
||||
* @return subscriber
|
||||
*/
|
||||
[[nodiscard]] GenericSubscriber GenericSubscribe(
|
||||
std::span<const PubSubOption> options = {});
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
/**
|
||||
* Create a new subscriber to the topic.
|
||||
@@ -188,7 +187,8 @@ class Topic {
|
||||
* @return subscriber
|
||||
*/
|
||||
[[nodiscard]] GenericSubscriber GenericSubscribe(
|
||||
std::string_view typeString, std::span<const PubSubOption> options = {});
|
||||
std::string_view typeString,
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
/**
|
||||
* Create a new publisher to the topic.
|
||||
@@ -207,7 +207,8 @@ class Topic {
|
||||
* @return publisher
|
||||
*/
|
||||
[[nodiscard]] GenericPublisher GenericPublish(
|
||||
std::string_view typeString, std::span<const PubSubOption> options = {});
|
||||
std::string_view typeString,
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
/**
|
||||
* Create a new publisher to the topic, with type string and initial
|
||||
@@ -229,7 +230,7 @@ class Topic {
|
||||
*/
|
||||
[[nodiscard]] GenericPublisher GenericPublishEx(
|
||||
std::string_view typeString, const wpi::json& properties,
|
||||
std::span<const PubSubOption> options = {});
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
/**
|
||||
* Create a new generic entry for the topic.
|
||||
@@ -250,7 +251,7 @@ class Topic {
|
||||
* @return entry
|
||||
*/
|
||||
[[nodiscard]] GenericEntry GetGenericEntry(
|
||||
std::span<const PubSubOption> options = {});
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
/**
|
||||
* Create a new generic entry for the topic.
|
||||
@@ -272,7 +273,8 @@ class Topic {
|
||||
* @return entry
|
||||
*/
|
||||
[[nodiscard]] GenericEntry GetGenericEntry(
|
||||
std::string_view typeString, std::span<const PubSubOption> options = {});
|
||||
std::string_view typeString,
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
/**
|
||||
* Equality operator. Returns true if both instances refer to the same
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "networktables/Topic.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace wpi {
|
||||
class json;
|
||||
@@ -295,7 +296,8 @@ class UnitTopic final : public Topic {
|
||||
* @return subscriber
|
||||
*/
|
||||
[[nodiscard]] SubscriberType Subscribe(
|
||||
ParamType defaultValue, std::span<const PubSubOption> options = {});
|
||||
ParamType defaultValue,
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
/**
|
||||
* Create a new subscriber to the topic, with specific type string.
|
||||
@@ -315,7 +317,7 @@ class UnitTopic final : public Topic {
|
||||
*/
|
||||
[[nodiscard]] SubscriberType SubscribeEx(
|
||||
std::string_view typeString, ParamType defaultValue,
|
||||
std::span<const PubSubOption> options = {});
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
/**
|
||||
* Create a new publisher to the topic.
|
||||
@@ -333,7 +335,7 @@ class UnitTopic final : public Topic {
|
||||
* @return publisher
|
||||
*/
|
||||
[[nodiscard]] PublisherType Publish(
|
||||
std::span<const PubSubOption> options = {});
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
/**
|
||||
* Create a new publisher to the topic, with type string and initial
|
||||
@@ -355,7 +357,7 @@ class UnitTopic final : public Topic {
|
||||
*/
|
||||
[[nodiscard]] PublisherType PublishEx(
|
||||
std::string_view typeString, const wpi::json& properties,
|
||||
std::span<const PubSubOption> options = {});
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
/**
|
||||
* Create a new entry for the topic.
|
||||
@@ -377,8 +379,9 @@ class UnitTopic final : public Topic {
|
||||
* @param options publish and/or subscribe options
|
||||
* @return entry
|
||||
*/
|
||||
[[nodiscard]] EntryType GetEntry(ParamType defaultValue,
|
||||
std::span<const PubSubOption> options = {});
|
||||
[[nodiscard]] EntryType GetEntry(
|
||||
ParamType defaultValue,
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
/**
|
||||
* Create a new entry for the topic, with specific type string.
|
||||
@@ -403,7 +406,7 @@ class UnitTopic final : public Topic {
|
||||
*/
|
||||
[[nodiscard]] EntryType GetEntryEx(
|
||||
std::string_view typeString, ParamType defaultValue,
|
||||
std::span<const PubSubOption> options = {});
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
@@ -93,31 +93,29 @@ inline bool UnitTopic<T>::IsMatchingUnit() const {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitSubscriber<T> UnitTopic<T>::Subscribe(
|
||||
T defaultValue, std::span<const PubSubOption> options) {
|
||||
inline UnitSubscriber<T> UnitTopic<T>::Subscribe(T defaultValue,
|
||||
const PubSubOptions& options) {
|
||||
return UnitSubscriber<T>{
|
||||
::nt::Subscribe(m_handle, NT_DOUBLE, "double", options), defaultValue};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitSubscriber<T> UnitTopic<T>::SubscribeEx(
|
||||
std::string_view typeString, T defaultValue,
|
||||
std::span<const PubSubOption> options) {
|
||||
std::string_view typeString, T defaultValue, const PubSubOptions& options) {
|
||||
return UnitSubscriber<T>{
|
||||
::nt::Subscribe(m_handle, NT_DOUBLE, typeString, options), defaultValue};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitPublisher<T> UnitTopic<T>::Publish(
|
||||
std::span<const PubSubOption> options) {
|
||||
inline UnitPublisher<T> UnitTopic<T>::Publish(const PubSubOptions& options) {
|
||||
return UnitPublisher<T>{::nt::PublishEx(m_handle, NT_DOUBLE, "double",
|
||||
{{"unit", T{}.name()}}, options)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitPublisher<T> UnitTopic<T>::PublishEx(
|
||||
std::string_view typeString, const wpi::json& properties,
|
||||
std::span<const PubSubOption> options) {
|
||||
inline UnitPublisher<T> UnitTopic<T>::PublishEx(std::string_view typeString,
|
||||
const wpi::json& properties,
|
||||
const PubSubOptions& options) {
|
||||
wpi::json props = properties;
|
||||
props["unit"] = T{}.name();
|
||||
return UnitPublisher<T>{
|
||||
@@ -125,16 +123,16 @@ inline UnitPublisher<T> UnitTopic<T>::PublishEx(
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitEntry<T> UnitTopic<T>::GetEntry(
|
||||
T defaultValue, std::span<const PubSubOption> options) {
|
||||
inline UnitEntry<T> UnitTopic<T>::GetEntry(T defaultValue,
|
||||
const PubSubOptions& options) {
|
||||
return UnitEntry<T>{::nt::GetEntry(m_handle, NT_DOUBLE, "double", options),
|
||||
defaultValue};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitEntry<T> UnitTopic<T>::GetEntryEx(
|
||||
std::string_view typeString, T defaultValue,
|
||||
std::span<const PubSubOption> options) {
|
||||
inline UnitEntry<T> UnitTopic<T>::GetEntryEx(std::string_view typeString,
|
||||
T defaultValue,
|
||||
const PubSubOptions& options) {
|
||||
return UnitEntry<T>{::nt::GetEntry(m_handle, NT_DOUBLE, typeString, options),
|
||||
defaultValue};
|
||||
}
|
||||
|
||||
@@ -88,18 +88,6 @@ enum NT_NetworkMode {
|
||||
NT_NET_MODE_LOCAL = 0x10, /* running in local-only mode */
|
||||
};
|
||||
|
||||
/** Pub/sub option types */
|
||||
enum NT_PubSubOptionType {
|
||||
NT_PUBSUB_PERIODIC = 1, /* period between transmissions */
|
||||
NT_PUBSUB_SENDALL, /* all value changes are sent */
|
||||
NT_PUBSUB_TOPICSONLY, /* only send topic changes, no value changes */
|
||||
NT_PUBSUB_POLLSTORAGE, /* polling storage for subscription */
|
||||
NT_PUBSUB_KEEPDUPLICATES, /* preserve duplicate values */
|
||||
NT_PUBSUB_LOCALREMOTE, /* local, remote, or any value changes */
|
||||
NT_PUBSUB_EXCLUDEPUB, /* exclude value changes made by given publisher */
|
||||
NT_PUBSUB_EXCLUDESELF, /* exclude value changes made by entry publisher */
|
||||
};
|
||||
|
||||
/** Event notification flags. */
|
||||
enum NT_EventFlags {
|
||||
NT_EVENT_NONE = 0,
|
||||
@@ -283,18 +271,74 @@ struct NT_Event {
|
||||
} data;
|
||||
};
|
||||
|
||||
/** NetworkTables publish/subscribe option. */
|
||||
struct NT_PubSubOption {
|
||||
/** Option type. */
|
||||
enum NT_PubSubOptionType type;
|
||||
/** NetworkTables publish/subscribe options. */
|
||||
struct NT_PubSubOptions {
|
||||
/**
|
||||
* Structure size. Must be set to sizeof(NT_PubSubOptions).
|
||||
*/
|
||||
unsigned int structSize;
|
||||
|
||||
/**
|
||||
* Option value. 1 (true) or 0 (false) for immediate and logging options,
|
||||
* time between updates, in milliseconds, for periodic option. For
|
||||
* local/remote option, 1=local only, 2=remote only, 0 or 3=both local and
|
||||
* remote. For exclude publisher, publisher handle.
|
||||
* Polling storage size for a subscription. Specifies the maximum number of
|
||||
* updates NetworkTables should store between calls to the subscriber's
|
||||
* ReadQueue() function. If zero, defaults to 1 if sendAll is false, 20 if
|
||||
* sendAll is true.
|
||||
*/
|
||||
unsigned int value;
|
||||
unsigned int pollStorage;
|
||||
|
||||
/**
|
||||
* How frequently changes will be sent over the network, in seconds.
|
||||
* NetworkTables may send more frequently than this (e.g. use a combined
|
||||
* minimum period for all values) or apply a restricted range to this value.
|
||||
* The default is 100 ms.
|
||||
*/
|
||||
double periodic;
|
||||
|
||||
/**
|
||||
* For subscriptions, if non-zero, value updates for ReadQueue() are not
|
||||
* queued for this publisher.
|
||||
*/
|
||||
NT_Publisher excludePublisher;
|
||||
|
||||
/**
|
||||
* Send all value changes over the network.
|
||||
*/
|
||||
NT_Bool sendAll;
|
||||
|
||||
/**
|
||||
* For subscriptions, don't ask for value changes (only topic announcements).
|
||||
*/
|
||||
NT_Bool topicsOnly;
|
||||
|
||||
/**
|
||||
* Perform prefix match on subscriber topic names. Is ignored/overridden by
|
||||
* Subscribe() functions; only present in struct for the purposes of getting
|
||||
* information about subscriptions.
|
||||
*/
|
||||
NT_Bool prefixMatch;
|
||||
|
||||
/**
|
||||
* Preserve duplicate value changes (rather than ignoring them).
|
||||
*/
|
||||
NT_Bool keepDuplicates;
|
||||
|
||||
/**
|
||||
* For subscriptions, if remote value updates should not be queued for
|
||||
* ReadQueue(). See also disableLocal.
|
||||
*/
|
||||
NT_Bool disableRemote;
|
||||
|
||||
/**
|
||||
* For subscriptions, if local value updates should not be queued for
|
||||
* ReadQueue(). See also disableRemote.
|
||||
*/
|
||||
NT_Bool disableLocal;
|
||||
|
||||
/**
|
||||
* For entries, don't queue (for ReadQueue) value updates for the entry's
|
||||
* internal publisher.
|
||||
*/
|
||||
NT_Bool excludeSelf;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -682,13 +726,11 @@ NT_Bool NT_SetTopicProperties(NT_Topic topic, const char* properties);
|
||||
* @param type expected type
|
||||
* @param typeStr expected type string
|
||||
* @param options subscription options
|
||||
* @param options_len number of elements in options array
|
||||
* @return Subscriber handle
|
||||
*/
|
||||
NT_Subscriber NT_Subscribe(NT_Topic topic, enum NT_Type type,
|
||||
const char* typeStr,
|
||||
const struct NT_PubSubOption* options,
|
||||
size_t options_len);
|
||||
const struct NT_PubSubOptions* options);
|
||||
|
||||
/**
|
||||
* Stops subscriber.
|
||||
@@ -704,12 +746,10 @@ void NT_Unsubscribe(NT_Subscriber sub);
|
||||
* @param type type
|
||||
* @param typeStr type string
|
||||
* @param options publish options
|
||||
* @param options_len number of elements in options array
|
||||
* @return Publisher handle
|
||||
*/
|
||||
NT_Publisher NT_Publish(NT_Topic topic, enum NT_Type type, const char* typeStr,
|
||||
const struct NT_PubSubOption* options,
|
||||
size_t options_len);
|
||||
const struct NT_PubSubOptions* options);
|
||||
|
||||
/**
|
||||
* Creates a new publisher to a topic.
|
||||
@@ -719,13 +759,11 @@ NT_Publisher NT_Publish(NT_Topic topic, enum NT_Type type, const char* typeStr,
|
||||
* @param typeStr type string
|
||||
* @param properties initial properties (JSON object)
|
||||
* @param options publish options
|
||||
* @param options_len number of elements in options array
|
||||
* @return Publisher handle
|
||||
*/
|
||||
NT_Publisher NT_PublishEx(NT_Topic topic, enum NT_Type type,
|
||||
const char* typeStr, const char* properties,
|
||||
const struct NT_PubSubOption* options,
|
||||
size_t options_len);
|
||||
const struct NT_PubSubOptions* options);
|
||||
|
||||
/**
|
||||
* Stops publisher.
|
||||
@@ -741,12 +779,10 @@ void NT_Unpublish(NT_Handle pubentry);
|
||||
* @param type type
|
||||
* @param typeStr type string
|
||||
* @param options publish options
|
||||
* @param options_len number of elements in options array
|
||||
* @return Entry handle
|
||||
*/
|
||||
NT_Entry NT_GetEntryEx(NT_Topic topic, enum NT_Type type, const char* typeStr,
|
||||
const struct NT_PubSubOption* options,
|
||||
size_t options_len);
|
||||
const struct NT_PubSubOptions* options);
|
||||
|
||||
/**
|
||||
* Stops entry subscriber/publisher.
|
||||
@@ -786,14 +822,12 @@ NT_Topic NT_GetTopicFromHandle(NT_Handle pubsubentry);
|
||||
* @param prefixes topic name prefixes
|
||||
* @param prefixes_len number of elements in prefixes array
|
||||
* @param options subscriber options
|
||||
* @param options_len number of elements in options array
|
||||
* @return subscriber handle
|
||||
*/
|
||||
NT_MultiSubscriber NT_SubscribeMultiple(NT_Inst inst,
|
||||
const struct NT_String* prefixes,
|
||||
size_t prefixes_len,
|
||||
const struct NT_PubSubOption* options,
|
||||
size_t options_len);
|
||||
const struct NT_PubSubOptions* options);
|
||||
|
||||
/**
|
||||
* Unsubscribes a multi-subscriber.
|
||||
|
||||
@@ -259,131 +259,86 @@ class Event {
|
||||
LogMessage* GetLogMessage() { return std::get_if<nt::LogMessage>(&data); }
|
||||
};
|
||||
|
||||
/** NetworkTables publish/subscribe option. */
|
||||
class PubSubOption {
|
||||
public:
|
||||
constexpr PubSubOption(NT_PubSubOptionType type, unsigned int value)
|
||||
: type{type}, value{value} {}
|
||||
/** NetworkTables publish/subscribe options. */
|
||||
struct PubSubOptions {
|
||||
/**
|
||||
* Default value of periodic.
|
||||
*/
|
||||
static constexpr double kDefaultPeriodic = 0.1;
|
||||
|
||||
/**
|
||||
* How frequently changes will be sent over the network. NetworkTables may
|
||||
* send more frequently than this (e.g. use a combined minimum period for all
|
||||
* values) or apply a restricted range to this value. The default if
|
||||
* unspecified (and the immediate flag is false) is 100 ms. This option and
|
||||
* the immediate option override each other.
|
||||
*
|
||||
* @param time time between updates, in seconds
|
||||
* @return option
|
||||
* Structure size. Must be set to sizeof(PubSubOptions).
|
||||
*/
|
||||
static constexpr PubSubOption Periodic(double time) {
|
||||
return PubSubOption{NT_PUBSUB_PERIODIC,
|
||||
static_cast<unsigned int>(time * 1000)};
|
||||
}
|
||||
unsigned int structSize = sizeof(PubSubOptions);
|
||||
|
||||
/**
|
||||
* If enabled, sends all value changes over the network even if only sent
|
||||
* periodically. This option defaults to disabled.
|
||||
*
|
||||
* @param enabled True to enable, false to disable
|
||||
* @return option
|
||||
* Polling storage size for a subscription. Specifies the maximum number of
|
||||
* updates NetworkTables should store between calls to the subscriber's
|
||||
* ReadQueue() function. If zero, defaults to 1 if sendAll is false, 20 if
|
||||
* sendAll is true.
|
||||
*/
|
||||
static constexpr PubSubOption SendAll(bool enabled) {
|
||||
return PubSubOption{NT_PUBSUB_SENDALL, enabled ? 1u : 0u};
|
||||
}
|
||||
unsigned int pollStorage = 0;
|
||||
|
||||
/**
|
||||
* If enabled, no value changes are sent over the network. This option
|
||||
* defaults to disabled.
|
||||
*
|
||||
* @param enabled True to enable, false to disable
|
||||
* @return option
|
||||
* How frequently changes will be sent over the network, in seconds.
|
||||
* NetworkTables may send more frequently than this (e.g. use a combined
|
||||
* minimum period for all values) or apply a restricted range to this value.
|
||||
* The default is 100 ms.
|
||||
*/
|
||||
static constexpr PubSubOption TopicsOnly(bool enabled) {
|
||||
return PubSubOption{NT_PUBSUB_TOPICSONLY, enabled ? 1u : 0u};
|
||||
}
|
||||
double periodic = kDefaultPeriodic;
|
||||
|
||||
/**
|
||||
* If enabled, preserves duplicate value changes (rather than ignoring them).
|
||||
* This option defaults to disabled.
|
||||
*
|
||||
* @param enabled True to enable, false to disable
|
||||
* @return option
|
||||
* For subscriptions, if non-zero, value updates for ReadQueue() are not
|
||||
* queued for this publisher.
|
||||
*/
|
||||
static constexpr PubSubOption KeepDuplicates(bool enabled) {
|
||||
return PubSubOption{NT_PUBSUB_KEEPDUPLICATES, enabled ? 1u : 0u};
|
||||
}
|
||||
NT_Publisher excludePublisher = 0;
|
||||
|
||||
/**
|
||||
* Polling storage for subscription. Specifies the maximum number of updates
|
||||
* NetworkTables should store between calls to the subscriber's ReadQueue()
|
||||
* function. Defaults to 1 if SendAll is false, 20 if SendAll is true.
|
||||
*
|
||||
* @param depth number of entries to save for polling.
|
||||
* @return option
|
||||
* Send all value changes over the network.
|
||||
*/
|
||||
static constexpr PubSubOption PollStorage(unsigned int depth) {
|
||||
return PubSubOption{NT_PUBSUB_POLLSTORAGE, depth};
|
||||
}
|
||||
bool sendAll = false;
|
||||
|
||||
/**
|
||||
* If only local value updates should be queued for ReadQueue(). See also
|
||||
* RemoteOnly() and AllUpdates(). Default is AllUpdates. Only has an effect on
|
||||
* subscriptions.
|
||||
*
|
||||
* @return option
|
||||
* For subscriptions, don't ask for value changes (only topic announcements).
|
||||
*/
|
||||
static constexpr PubSubOption LocalOnly() {
|
||||
return PubSubOption{NT_PUBSUB_LOCALREMOTE, 1u};
|
||||
}
|
||||
bool topicsOnly = false;
|
||||
|
||||
/**
|
||||
* If only remote value updates should be queued for ReadQueue(). See also
|
||||
* LocalOnly() and AllUpdates(). Default is AllUpdates. Only has an effect on
|
||||
* subscriptions.
|
||||
*
|
||||
* @return option
|
||||
* Preserve duplicate value changes (rather than ignoring them).
|
||||
*/
|
||||
static constexpr PubSubOption RemoteOnly() {
|
||||
return PubSubOption{NT_PUBSUB_LOCALREMOTE, 2u};
|
||||
}
|
||||
bool keepDuplicates = false;
|
||||
|
||||
/**
|
||||
* If both local and remote value updates should be queued for ReadQueue().
|
||||
* See also LocalOnly() and RemoteOnly(). Default is AllUpdates. Only has an
|
||||
* effect on subscriptions.
|
||||
*
|
||||
* @return option
|
||||
* Perform prefix match on subscriber topic names. Is ignored/overridden by
|
||||
* Subscribe() functions; only present in struct for the purposes of getting
|
||||
* information about subscriptions.
|
||||
*/
|
||||
static constexpr PubSubOption AllUpdates() {
|
||||
return PubSubOption{NT_PUBSUB_LOCALREMOTE, 0u};
|
||||
}
|
||||
bool prefixMatch = false;
|
||||
|
||||
/**
|
||||
* Don't queue value updates for the given publisher. Only has an effect on
|
||||
* subscriptions. Only one exclusion may be set.
|
||||
*
|
||||
* @param publisher publisher handle
|
||||
* @return option
|
||||
* For subscriptions, if remote value updates should not be queued for
|
||||
* ReadQueue(). See also disableLocal.
|
||||
*/
|
||||
static constexpr PubSubOption ExcludePublisher(NT_Publisher publisher) {
|
||||
return PubSubOption{NT_PUBSUB_EXCLUDEPUB, publisher};
|
||||
}
|
||||
bool disableRemote = false;
|
||||
|
||||
/**
|
||||
* Don't queue value updates for the internal publisher for an entry. Only has
|
||||
* an effect on entries.
|
||||
*
|
||||
* @param enabled True to enable, false to disable
|
||||
* @return option
|
||||
* For subscriptions, if local value updates should not be queued for
|
||||
* ReadQueue(). See also disableRemote.
|
||||
*/
|
||||
static constexpr PubSubOption ExcludeSelf(bool enabled) {
|
||||
return PubSubOption{NT_PUBSUB_EXCLUDESELF, enabled ? 1u : 0u};
|
||||
}
|
||||
bool disableLocal = false;
|
||||
|
||||
NT_PubSubOptionType type;
|
||||
unsigned int value;
|
||||
/**
|
||||
* For entries, don't queue (for ReadQueue) value updates for the entry's
|
||||
* internal publisher.
|
||||
*/
|
||||
bool excludeSelf = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Default publish/subscribe options.
|
||||
*/
|
||||
constexpr PubSubOptions kDefaultPubSubOptions;
|
||||
|
||||
/**
|
||||
* @defgroup ntcore_instance_func Instance Functions
|
||||
* @{
|
||||
@@ -750,7 +705,7 @@ bool SetTopicProperties(NT_Topic topic, const wpi::json& update);
|
||||
* @return Subscriber handle
|
||||
*/
|
||||
NT_Subscriber Subscribe(NT_Topic topic, NT_Type type, std::string_view typeStr,
|
||||
std::span<const PubSubOption> options = {});
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
/**
|
||||
* Stops subscriber.
|
||||
@@ -769,7 +724,7 @@ void Unsubscribe(NT_Subscriber sub);
|
||||
* @return Publisher handle
|
||||
*/
|
||||
NT_Publisher Publish(NT_Topic topic, NT_Type type, std::string_view typeStr,
|
||||
std::span<const PubSubOption> options = {});
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
/**
|
||||
* Creates a new publisher to a topic.
|
||||
@@ -783,7 +738,7 @@ NT_Publisher Publish(NT_Topic topic, NT_Type type, std::string_view typeStr,
|
||||
*/
|
||||
NT_Publisher PublishEx(NT_Topic topic, NT_Type type, std::string_view typeStr,
|
||||
const wpi::json& properties,
|
||||
std::span<const PubSubOption> options = {});
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
/**
|
||||
* Stops publisher.
|
||||
@@ -802,7 +757,7 @@ void Unpublish(NT_Handle pubentry);
|
||||
* @return Entry handle
|
||||
*/
|
||||
NT_Entry GetEntry(NT_Topic topic, NT_Type type, std::string_view typeStr,
|
||||
std::span<const PubSubOption> options = {});
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
/**
|
||||
* Stops entry subscriber/publisher.
|
||||
@@ -845,7 +800,7 @@ NT_Topic GetTopicFromHandle(NT_Handle pubsubentry);
|
||||
*/
|
||||
NT_MultiSubscriber SubscribeMultiple(
|
||||
NT_Inst inst, std::span<const std::string_view> prefixes,
|
||||
std::span<const PubSubOption> options = {});
|
||||
const PubSubOptions& options = kDefaultPubSubOptions);
|
||||
|
||||
/**
|
||||
* Unsubscribes a multi-subscriber.
|
||||
|
||||
@@ -6,14 +6,15 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ntcore.h"
|
||||
|
||||
// Functions in this header are to be used only for testing
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
struct NT_String* NT_GetStringForTesting(const char* string, int* struct_size);
|
||||
#endif
|
||||
|
||||
struct NT_String* NT_GetStringForTesting(const char* str, int* struct_size);
|
||||
// No need for free as one already exists in main library
|
||||
|
||||
struct NT_EntryInfo* NT_GetEntryInfoForTesting(const char* name,
|
||||
@@ -58,26 +59,6 @@ struct NT_Value* NT_GetValueStringArrayForTesting(uint64_t last_change,
|
||||
int* struct_size);
|
||||
// No need for free as one already exists in the main library
|
||||
|
||||
struct NT_RpcParamDef* NT_GetRpcParamDefForTesting(const char* name,
|
||||
const struct NT_Value* val,
|
||||
int* struct_size);
|
||||
|
||||
void NT_FreeRpcParamDefForTesting(struct NT_RpcParamDef* def);
|
||||
|
||||
struct NT_RpcResultDef* NT_GetRpcResultsDefForTesting(const char* name,
|
||||
enum NT_Type type,
|
||||
int* struct_size);
|
||||
|
||||
void NT_FreeRpcResultsDefForTesting(struct NT_RpcResultDef* def);
|
||||
|
||||
struct NT_RpcDefinition* NT_GetRpcDefinitionForTesting(
|
||||
unsigned int version, const char* name, size_t num_params,
|
||||
const struct NT_RpcParamDef* params, size_t num_results,
|
||||
const struct NT_RpcResultDef* results, int* struct_size);
|
||||
// No need for free as one already exists in the main library
|
||||
|
||||
struct NT_RpcCallInfo* NT_GetRpcCallInfoForTesting(
|
||||
unsigned int rpc_id, unsigned int call_uid, const char* name,
|
||||
const char* params, size_t params_len, int* struct_size);
|
||||
// No need for free as one already exists in the main library
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@@ -25,21 +25,25 @@ using ::testing::Return;
|
||||
|
||||
namespace nt {
|
||||
|
||||
::testing::Matcher<const PubSubOptions&> IsPubSubOptions(
|
||||
const PubSubOptions& good) {
|
||||
return AllOf(Field("periodic", &PubSubOptions::periodicMs, good.periodicMs),
|
||||
Field("pollStorageSize", &PubSubOptions::pollStorageSize,
|
||||
good.pollStorageSize),
|
||||
Field("logging", &PubSubOptions::sendAll, good.sendAll),
|
||||
Field("keepDuplicates", &PubSubOptions::keepDuplicates,
|
||||
good.keepDuplicates));
|
||||
::testing::Matcher<const PubSubOptionsImpl&> IsPubSubOptions(
|
||||
const PubSubOptionsImpl& good) {
|
||||
return AllOf(
|
||||
Field("periodic", &PubSubOptionsImpl::periodicMs, good.periodicMs),
|
||||
Field("pollStorage", &PubSubOptionsImpl::pollStorage, good.pollStorage),
|
||||
Field("sendAll", &PubSubOptionsImpl::sendAll, good.sendAll),
|
||||
Field("keepDuplicates", &PubSubOptionsImpl::keepDuplicates,
|
||||
good.keepDuplicates));
|
||||
}
|
||||
|
||||
::testing::Matcher<const PubSubOptionsImpl&> IsDefaultPubSubOptions() {
|
||||
static constexpr PubSubOptionsImpl kDefaultPubSubOptionsImpl;
|
||||
return IsPubSubOptions(kDefaultPubSubOptionsImpl);
|
||||
}
|
||||
|
||||
class LocalStorageTest : public ::testing::Test {
|
||||
public:
|
||||
LocalStorageTest() { storage.StartNetwork(startup, &network); }
|
||||
LocalStorageTest() { storage.StartNetwork(&network); }
|
||||
|
||||
::testing::StrictMock<net::MockNetworkStartupInterface> startup;
|
||||
::testing::StrictMock<net::MockNetworkInterface> network;
|
||||
wpi::MockLogger logger;
|
||||
MockListenerStorage listenerStorage;
|
||||
@@ -72,7 +76,7 @@ TEST_F(LocalStorageTest, GetEntryEmptyName) {
|
||||
|
||||
TEST_F(LocalStorageTest, GetEntryCached) {
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"tocache"}}),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
|
||||
auto entry1 = storage.GetEntry("tocache");
|
||||
EXPECT_EQ(entry1, storage.GetEntry("tocache"));
|
||||
@@ -99,7 +103,7 @@ TEST_F(LocalStorageTest, GetTopicInfoUnpublished) {
|
||||
TEST_F(LocalStorageTest, PublishNewNoProps) {
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
storage.Publish(fooTopic, NT_BOOLEAN, "boolean", wpi::json::object(), {});
|
||||
|
||||
auto info = storage.GetTopicInfo(fooTopic);
|
||||
@@ -109,7 +113,7 @@ TEST_F(LocalStorageTest, PublishNewNoProps) {
|
||||
TEST_F(LocalStorageTest, PublishNewNoPropsNull) {
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {});
|
||||
|
||||
auto info = storage.GetTopicInfo(fooTopic);
|
||||
@@ -120,7 +124,7 @@ TEST_F(LocalStorageTest, PublishNew) {
|
||||
wpi::json properties = {{"persistent", true}};
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, properties,
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {{"persistent", true}}, {});
|
||||
|
||||
auto info = storage.GetTopicInfo(fooTopic);
|
||||
@@ -137,12 +141,12 @@ TEST_F(LocalStorageTest, PublishNew) {
|
||||
|
||||
TEST_F(LocalStorageTest, SubscribeNoTypeLocalPubPost) {
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
auto sub = storage.Subscribe(fooTopic, NT_UNASSIGNED, "", {});
|
||||
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
auto pub = storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {});
|
||||
|
||||
auto val = Value::MakeBoolean(true, 5);
|
||||
@@ -174,7 +178,7 @@ TEST_F(LocalStorageTest, SubscribeNoTypeLocalPubPost) {
|
||||
TEST_F(LocalStorageTest, SubscribeNoTypeLocalPubPre) {
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
auto pub = storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {});
|
||||
|
||||
auto val = Value::MakeBoolean(true, 5);
|
||||
@@ -182,7 +186,7 @@ TEST_F(LocalStorageTest, SubscribeNoTypeLocalPubPre) {
|
||||
storage.SetEntryValue(pub, val);
|
||||
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
auto sub = storage.Subscribe(fooTopic, NT_UNASSIGNED, "", {});
|
||||
|
||||
EXPECT_EQ(storage.GetTopicType(fooTopic), NT_BOOLEAN);
|
||||
@@ -200,14 +204,14 @@ TEST_F(LocalStorageTest, SubscribeNoTypeLocalPubPre) {
|
||||
|
||||
TEST_F(LocalStorageTest, EntryNoTypeLocalSet) {
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
auto entry = storage.GetEntry(fooTopic, NT_UNASSIGNED, "", {});
|
||||
|
||||
// results in a publish and value set
|
||||
auto val = Value::MakeBoolean(true, 5);
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
EXPECT_CALL(network, SetValue(_, val));
|
||||
EXPECT_TRUE(storage.SetEntryValue(entry, val));
|
||||
|
||||
@@ -246,12 +250,12 @@ TEST_F(LocalStorageTest, EntryNoTypeLocalSet) {
|
||||
|
||||
TEST_F(LocalStorageTest, PubUnpubPub) {
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
auto sub = storage.Subscribe(fooTopic, NT_INTEGER, "int", {});
|
||||
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
EXPECT_CALL(logger, Call(NT_LOG_INFO, _, _,
|
||||
"local subscribe to 'foo' disabled due to type "
|
||||
"mismatch (wanted 'int', published as 'boolean')"));
|
||||
@@ -276,7 +280,7 @@ TEST_F(LocalStorageTest, PubUnpubPub) {
|
||||
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"int"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
pub = storage.Publish(fooTopic, NT_INTEGER, "int", {}, {});
|
||||
|
||||
val = Value::MakeInteger(3, 5);
|
||||
@@ -293,7 +297,7 @@ TEST_F(LocalStorageTest, PubUnpubPub) {
|
||||
TEST_F(LocalStorageTest, LocalPubConflict) {
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
auto pub1 = storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {});
|
||||
|
||||
EXPECT_CALL(logger, Call(NT_LOG_INFO, _, _,
|
||||
@@ -314,7 +318,7 @@ TEST_F(LocalStorageTest, LocalPubConflict) {
|
||||
EXPECT_CALL(network, Unpublish(pub1, fooTopic));
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"int"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
storage.Unpublish(pub1);
|
||||
|
||||
EXPECT_EQ(storage.GetTopicType(fooTopic), NT_INTEGER);
|
||||
@@ -330,11 +334,11 @@ TEST_F(LocalStorageTest, LocalPubConflict) {
|
||||
TEST_F(LocalStorageTest, LocalSubConflict) {
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {});
|
||||
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
EXPECT_CALL(logger, Call(NT_LOG_INFO, _, _,
|
||||
"local subscribe to 'foo' disabled due to type "
|
||||
"mismatch (wanted 'int', published as 'boolean')"));
|
||||
@@ -344,7 +348,7 @@ TEST_F(LocalStorageTest, LocalSubConflict) {
|
||||
TEST_F(LocalStorageTest, RemotePubConflict) {
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
|
||||
storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {});
|
||||
|
||||
@@ -361,7 +365,7 @@ TEST_F(LocalStorageTest, RemotePubConflict) {
|
||||
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
|
||||
storage.NetworkUnannounce("foo");
|
||||
|
||||
@@ -373,14 +377,14 @@ TEST_F(LocalStorageTest, RemotePubConflict) {
|
||||
TEST_F(LocalStorageTest, SubNonExist) {
|
||||
// makes sure no warning is emitted
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
storage.Subscribe(fooTopic, NT_BOOLEAN, "boolean", {});
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, SetDefaultSubscribe) {
|
||||
// no publish, no value on wire, this is just handled locally
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
auto sub = storage.Subscribe(fooTopic, NT_BOOLEAN, "boolean", {});
|
||||
EXPECT_TRUE(storage.SetDefaultEntryValue(sub, Value::MakeBoolean(true)));
|
||||
auto val = storage.GetEntryValue(sub);
|
||||
@@ -392,7 +396,7 @@ TEST_F(LocalStorageTest, SetDefaultSubscribe) {
|
||||
TEST_F(LocalStorageTest, SetDefaultPublish) {
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
auto pub = storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {});
|
||||
|
||||
// expect a value across the wire
|
||||
@@ -400,7 +404,7 @@ TEST_F(LocalStorageTest, SetDefaultPublish) {
|
||||
EXPECT_CALL(network, SetValue(pub, expectVal));
|
||||
EXPECT_TRUE(storage.SetDefaultEntryValue(pub, Value::MakeBoolean(true)));
|
||||
|
||||
EXPECT_CALL(network, Subscribe(_, _, IsPubSubOptions({})));
|
||||
EXPECT_CALL(network, Subscribe(_, _, IsDefaultPubSubOptions()));
|
||||
auto sub = storage.Subscribe(fooTopic, NT_BOOLEAN, "boolean", {});
|
||||
auto val = storage.GetEntryValue(sub);
|
||||
ASSERT_TRUE(val.IsBoolean());
|
||||
@@ -410,13 +414,13 @@ TEST_F(LocalStorageTest, SetDefaultPublish) {
|
||||
|
||||
TEST_F(LocalStorageTest, SetDefaultEntry) {
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
auto entry = storage.GetEntry(fooTopic, NT_BOOLEAN, "boolean", {});
|
||||
|
||||
// expect a publish and value
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
auto expectVal = Value::MakeBoolean(true, 0);
|
||||
EXPECT_CALL(network, SetValue(_, expectVal));
|
||||
EXPECT_TRUE(storage.SetDefaultEntryValue(entry, Value::MakeBoolean(true)));
|
||||
@@ -429,13 +433,13 @@ TEST_F(LocalStorageTest, SetDefaultEntry) {
|
||||
|
||||
TEST_F(LocalStorageTest, SetDefaultEntryUnassigned) {
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
auto entry = storage.GetEntry(fooTopic, NT_UNASSIGNED, "", {});
|
||||
|
||||
// expect a publish and value
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
auto expectVal = Value::MakeBoolean(true, 0);
|
||||
EXPECT_CALL(network, SetValue(_, expectVal));
|
||||
EXPECT_TRUE(storage.SetDefaultEntryValue(entry, Value::MakeBoolean(true)));
|
||||
@@ -450,7 +454,7 @@ TEST_F(LocalStorageTest, SetDefaultEntryUnassigned) {
|
||||
TEST_F(LocalStorageTest, SetDefaultEntryDiffType) {
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"string"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
auto pub = storage.Publish(fooTopic, NT_STRING, "string", {}, {});
|
||||
|
||||
EXPECT_FALSE(storage.SetDefaultEntryValue(pub, Value::MakeBoolean(true)));
|
||||
@@ -460,7 +464,7 @@ TEST_F(LocalStorageTest, SetDefaultEntryDiffType) {
|
||||
TEST_F(LocalStorageTest, SetValueEmptyValue) {
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"string"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
auto pub = storage.Publish(fooTopic, NT_STRING, "string", {}, {});
|
||||
|
||||
EXPECT_FALSE(storage.SetEntryValue(pub, {}));
|
||||
@@ -468,7 +472,7 @@ TEST_F(LocalStorageTest, SetValueEmptyValue) {
|
||||
|
||||
TEST_F(LocalStorageTest, SetValueEmptyUntypedEntry) {
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
IsDefaultPubSubOptions()));
|
||||
auto entry = storage.GetEntry(fooTopic, NT_UNASSIGNED, "", {});
|
||||
EXPECT_FALSE(storage.SetEntryValue(entry, {}));
|
||||
}
|
||||
@@ -500,22 +504,21 @@ class LocalStorageDuplicatesTest : public LocalStorageTest {
|
||||
};
|
||||
|
||||
void LocalStorageDuplicatesTest::SetupPubSub(bool keepPub, bool keepSub) {
|
||||
PubSubOptions pubOptions;
|
||||
PubSubOptionsImpl pubOptions;
|
||||
pubOptions.keepDuplicates = keepPub;
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"double"}, wpi::json::object(),
|
||||
IsPubSubOptions(pubOptions)));
|
||||
pub = storage.Publish(fooTopic, NT_DOUBLE, "double", {},
|
||||
{{PubSubOption::KeepDuplicates(keepPub)}});
|
||||
{.keepDuplicates = keepPub});
|
||||
|
||||
PubSubOptions subOptions;
|
||||
subOptions.pollStorageSize = 10;
|
||||
PubSubOptionsImpl subOptions;
|
||||
subOptions.pollStorage = 10;
|
||||
subOptions.keepDuplicates = keepSub;
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions(subOptions)));
|
||||
sub = storage.Subscribe(
|
||||
fooTopic, NT_DOUBLE, "double",
|
||||
{{PubSubOption::KeepDuplicates(keepSub), PubSubOption::PollStorage(10)}});
|
||||
sub = storage.Subscribe(fooTopic, NT_DOUBLE, "double",
|
||||
{.pollStorage = 10, .keepDuplicates = keepSub});
|
||||
}
|
||||
|
||||
void LocalStorageDuplicatesTest::SetValues() {
|
||||
@@ -839,12 +842,12 @@ TEST_F(LocalStorageTest, ReadQueueLocalRemote) {
|
||||
EXPECT_CALL(network, Subscribe(_, _, _)).Times(3);
|
||||
EXPECT_CALL(network, Publish(_, _, _, _, _, _)).Times(1);
|
||||
|
||||
auto subBoth = storage.Subscribe(fooTopic, NT_DOUBLE, "double",
|
||||
{{PubSubOption::AllUpdates()}});
|
||||
auto subLocal = storage.Subscribe(fooTopic, NT_DOUBLE, "double",
|
||||
{{PubSubOption::LocalOnly()}});
|
||||
auto subRemote = storage.Subscribe(fooTopic, NT_DOUBLE, "double",
|
||||
{{PubSubOption::RemoteOnly()}});
|
||||
auto subBoth =
|
||||
storage.Subscribe(fooTopic, NT_DOUBLE, "double", kDefaultPubSubOptions);
|
||||
auto subLocal =
|
||||
storage.Subscribe(fooTopic, NT_DOUBLE, "double", {.disableRemote = true});
|
||||
auto subRemote =
|
||||
storage.Subscribe(fooTopic, NT_DOUBLE, "double", {.disableLocal = true});
|
||||
auto pub = storage.Publish(fooTopic, NT_DOUBLE, "double", {}, {});
|
||||
auto remoteTopic =
|
||||
storage.NetworkAnnounce("foo", "double", wpi::json::object(), 0);
|
||||
@@ -874,7 +877,7 @@ TEST_F(LocalStorageTest, SubExcludePub) {
|
||||
auto pub = storage.Publish(fooTopic, NT_DOUBLE, "double", {}, {});
|
||||
auto subActive = storage.Subscribe(fooTopic, NT_DOUBLE, "double", {});
|
||||
auto subExclude = storage.Subscribe(fooTopic, NT_DOUBLE, "double",
|
||||
{{PubSubOption::ExcludePublisher(pub)}});
|
||||
{.excludePublisher = pub});
|
||||
auto remoteTopic =
|
||||
storage.NetworkAnnounce("foo", "double", wpi::json::object(), 0);
|
||||
|
||||
@@ -896,8 +899,8 @@ TEST_F(LocalStorageTest, SubExcludePub) {
|
||||
TEST_F(LocalStorageTest, EntryExcludeSelf) {
|
||||
EXPECT_CALL(network, Subscribe(_, _, _));
|
||||
|
||||
auto entry = storage.GetEntry(fooTopic, NT_DOUBLE, "double",
|
||||
{{PubSubOption::ExcludeSelf(true)}});
|
||||
auto entry =
|
||||
storage.GetEntry(fooTopic, NT_DOUBLE, "double", {.excludeSelf = true});
|
||||
auto remoteTopic =
|
||||
storage.NetworkAnnounce("foo", "double", wpi::json::object(), 0);
|
||||
|
||||
|
||||
@@ -9,18 +9,19 @@
|
||||
namespace nt {
|
||||
|
||||
bool PubSubOptionsMatcher::MatchAndExplain(
|
||||
const PubSubOptions& val, ::testing::MatchResultListener* listener) const {
|
||||
const PubSubOptionsImpl& val,
|
||||
::testing::MatchResultListener* listener) const {
|
||||
bool match = true;
|
||||
if (val.periodicMs != good.periodicMs) {
|
||||
*listener << "periodic mismatch ";
|
||||
match = false;
|
||||
}
|
||||
if (val.pollStorageSize != good.pollStorageSize) {
|
||||
*listener << "pollStorageSize mismatch ";
|
||||
if (val.pollStorage != good.pollStorage) {
|
||||
*listener << "pollStorage mismatch ";
|
||||
match = false;
|
||||
}
|
||||
if (val.sendAll != good.sendAll) {
|
||||
*listener << "logging mismatch ";
|
||||
*listener << "sendAll mismatch ";
|
||||
match = false;
|
||||
}
|
||||
if (val.keepDuplicates != good.keepDuplicates) {
|
||||
|
||||
@@ -13,21 +13,22 @@
|
||||
namespace nt {
|
||||
|
||||
class PubSubOptionsMatcher
|
||||
: public ::testing::MatcherInterface<const PubSubOptions&> {
|
||||
: public ::testing::MatcherInterface<const PubSubOptionsImpl&> {
|
||||
public:
|
||||
explicit PubSubOptionsMatcher(PubSubOptions good) : good{std::move(good)} {}
|
||||
explicit PubSubOptionsMatcher(PubSubOptionsImpl good)
|
||||
: good{std::move(good)} {}
|
||||
|
||||
bool MatchAndExplain(const PubSubOptions& val,
|
||||
bool MatchAndExplain(const PubSubOptionsImpl& val,
|
||||
::testing::MatchResultListener* listener) const override;
|
||||
void DescribeTo(::std::ostream* os) const override;
|
||||
void DescribeNegationTo(::std::ostream* os) const override;
|
||||
|
||||
private:
|
||||
PubSubOptions good;
|
||||
PubSubOptionsImpl good;
|
||||
};
|
||||
|
||||
inline ::testing::Matcher<const PubSubOptions&> PubSubOptionsEq(
|
||||
PubSubOptions good) {
|
||||
inline ::testing::Matcher<const PubSubOptionsImpl&> PubSubOptionsEq(
|
||||
PubSubOptionsImpl good) {
|
||||
return ::testing::MakeMatcher(new PubSubOptionsMatcher(std::move(good)));
|
||||
}
|
||||
|
||||
|
||||
@@ -160,10 +160,10 @@ void PrintTo(const Value& value, std::ostream* os) {
|
||||
*os << '}';
|
||||
}
|
||||
|
||||
void PrintTo(const PubSubOptions& options, std::ostream* os) {
|
||||
void PrintTo(const PubSubOptionsImpl& options, std::ostream* os) {
|
||||
*os << "PubSubOptions{periodicMs=" << options.periodicMs
|
||||
<< ", pollStorageSize=" << options.pollStorageSize
|
||||
<< ", logging=" << options.sendAll
|
||||
<< ", pollStorage=" << options.pollStorage
|
||||
<< ", sendAll=" << options.sendAll
|
||||
<< ", keepDuplicates=" << options.keepDuplicates << '}';
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ struct ServerMessage;
|
||||
|
||||
class Event;
|
||||
class Handle;
|
||||
class PubSubOptions;
|
||||
class PubSubOptionsImpl;
|
||||
class Value;
|
||||
|
||||
void PrintTo(const Event& event, std::ostream* os);
|
||||
@@ -60,6 +60,6 @@ void PrintTo(const net3::Message3& msg, std::ostream* os);
|
||||
void PrintTo(const net::ClientMessage& msg, std::ostream* os);
|
||||
void PrintTo(const net::ServerMessage& msg, std::ostream* os);
|
||||
void PrintTo(const Value& value, std::ostream* os);
|
||||
void PrintTo(const PubSubOptions& options, std::ostream* os);
|
||||
void PrintTo(const PubSubOptionsImpl& options, std::ostream* os);
|
||||
|
||||
} // namespace nt
|
||||
|
||||
@@ -28,27 +28,12 @@ class MockLocalInterface : public LocalInterface {
|
||||
(override));
|
||||
};
|
||||
|
||||
class MockNetworkStartupInterface : public NetworkStartupInterface {
|
||||
public:
|
||||
MOCK_METHOD(void, Publish,
|
||||
(NT_Publisher pubHandle, NT_Topic topicHandle,
|
||||
std::string_view name, std::string_view typeStr,
|
||||
const wpi::json& properties, const PubSubOptions& options),
|
||||
(override));
|
||||
MOCK_METHOD(void, Subscribe,
|
||||
(NT_Subscriber subHandle, std::span<const std::string> prefixes,
|
||||
const PubSubOptions& options),
|
||||
(override));
|
||||
MOCK_METHOD(void, SetValue, (NT_Publisher pubHandle, const Value& value),
|
||||
(override));
|
||||
};
|
||||
|
||||
class MockNetworkInterface : public NetworkInterface {
|
||||
public:
|
||||
MOCK_METHOD(void, Publish,
|
||||
(NT_Publisher pubHandle, NT_Topic topicHandle,
|
||||
std::string_view name, std::string_view typeStr,
|
||||
const wpi::json& properties, const PubSubOptions& options),
|
||||
const wpi::json& properties, const PubSubOptionsImpl& options),
|
||||
(override));
|
||||
MOCK_METHOD(void, Unpublish, (NT_Publisher pubHandle, NT_Topic topicHandle),
|
||||
(override));
|
||||
@@ -58,7 +43,7 @@ class MockNetworkInterface : public NetworkInterface {
|
||||
(override));
|
||||
MOCK_METHOD(void, Subscribe,
|
||||
(NT_Subscriber subHandle, std::span<const std::string> prefixes,
|
||||
const PubSubOptions& options),
|
||||
const PubSubOptionsImpl& options),
|
||||
(override));
|
||||
MOCK_METHOD(void, Unsubscribe, (NT_Subscriber subHandle), (override));
|
||||
MOCK_METHOD(void, SetValue, (NT_Publisher pubHandle, const Value& value),
|
||||
@@ -77,9 +62,7 @@ class MockLocalStorage : public ILocalStorage {
|
||||
(override));
|
||||
MOCK_METHOD(void, NetworkSetValue, (NT_Topic topicHandle, const Value& value),
|
||||
(override));
|
||||
MOCK_METHOD(void, StartNetwork,
|
||||
(NetworkStartupInterface & startup, NetworkInterface* network),
|
||||
(override));
|
||||
MOCK_METHOD(void, StartNetwork, (NetworkInterface * network), (override));
|
||||
MOCK_METHOD(void, ClearNetwork, (), (override));
|
||||
};
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class MockClientMessageHandler : public net::ClientMessageHandler {
|
||||
void(std::string_view name, const wpi::json& update));
|
||||
MOCK_METHOD3(ClientSubscribe,
|
||||
void(int64_t subuid, std::span<const std::string> prefixes,
|
||||
const PubSubOptions& options));
|
||||
const PubSubOptionsImpl& options));
|
||||
MOCK_METHOD1(ClientUnsubscribe, void(int64_t subuid));
|
||||
};
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ TEST_F(WireEncoderTextTest, Subscribe) {
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, SubscribeSendAll) {
|
||||
PubSubOptions options;
|
||||
PubSubOptionsImpl options;
|
||||
options.sendAll = true;
|
||||
net::WireEncodeSubscribe(os, 5, std::span<const std::string_view>{{"a", "b"}},
|
||||
options);
|
||||
@@ -85,7 +85,7 @@ TEST_F(WireEncoderTextTest, SubscribeSendAll) {
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, SubscribePeriodic) {
|
||||
PubSubOptions options;
|
||||
PubSubOptionsImpl options;
|
||||
options.periodicMs = 500u;
|
||||
net::WireEncodeSubscribe(os, 5, std::span<const std::string_view>{{"a", "b"}},
|
||||
options);
|
||||
@@ -96,7 +96,7 @@ TEST_F(WireEncoderTextTest, SubscribePeriodic) {
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, SubscribeAllOptions) {
|
||||
PubSubOptions options;
|
||||
PubSubOptionsImpl options;
|
||||
options.sendAll = true;
|
||||
options.periodicMs = 500u;
|
||||
net::WireEncodeSubscribe(os, 5, std::span<const std::string_view>{{"a", "b"}},
|
||||
|
||||
@@ -13,7 +13,7 @@ nativeUtils.withCrossLinuxArm64()
|
||||
nativeUtils {
|
||||
wpi {
|
||||
configureDependencies {
|
||||
niLibVersion = "2023.1.0"
|
||||
niLibVersion = "2023.2.0"
|
||||
opencvVersion = "4.6.0-3"
|
||||
googleTestVersion = "1.12.1-1"
|
||||
}
|
||||
|
||||
@@ -254,8 +254,8 @@ class FMSSimModel : public glass::FMSModel {
|
||||
void SetAutonomous(bool val) override {
|
||||
HALSIM_SetDriverStationAutonomous(val);
|
||||
}
|
||||
void SetGameSpecificMessage(const char* val) override {
|
||||
HALSIM_SetGameSpecificMessage(val);
|
||||
void SetGameSpecificMessage(std::string_view val) override {
|
||||
HALSIM_SetGameSpecificMessage(val.data(), val.size());
|
||||
}
|
||||
|
||||
void Update() override;
|
||||
|
||||
@@ -166,8 +166,8 @@ void HALSimWSProviderDriverStation::OnNetValueChanged(const wpi::json& json) {
|
||||
HALSIM_SetDriverStationMatchTime(it.value());
|
||||
}
|
||||
if ((it = json.find(">game_data")) != json.end()) {
|
||||
HALSIM_SetGameSpecificMessage(
|
||||
it.value().get_ref<const std::string&>().c_str());
|
||||
std::string message = it.value().get_ref<const std::string&>();
|
||||
HALSIM_SetGameSpecificMessage(message.c_str(), message.length());
|
||||
}
|
||||
|
||||
// Only notify usercode if we get the new data message
|
||||
|
||||
@@ -446,14 +446,15 @@ public interface Command {
|
||||
default void setName(String name) {}
|
||||
|
||||
/**
|
||||
* Decorates this Command with a name. Is an inline function for #setName(String);
|
||||
* Decorates this Command with a name.
|
||||
*
|
||||
* @param name name
|
||||
* @return the decorated Command
|
||||
*/
|
||||
default Command withName(String name) {
|
||||
this.setName(name);
|
||||
return this;
|
||||
default WrapperCommand withName(String name) {
|
||||
WrapperCommand wrapper = new WrapperCommand(Command.this) {};
|
||||
wrapper.setName(name);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,12 +56,6 @@ public abstract class CommandBase implements Sendable, Command {
|
||||
SendableRegistry.setName(this, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandBase withName(String name) {
|
||||
this.setName(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the subsystem name of this Command.
|
||||
*
|
||||
@@ -105,5 +99,8 @@ public abstract class CommandBase implements Sendable, Command {
|
||||
});
|
||||
builder.addBooleanProperty(
|
||||
".isParented", () -> CommandScheduler.getInstance().isComposed(this), null);
|
||||
builder.addStringProperty(
|
||||
"interruptBehavior", () -> getInterruptionBehavior().toString(), null);
|
||||
builder.addBooleanProperty("runsWhenDisabled", this::runsWhenDisabled, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,8 +217,8 @@ public final class CommandScheduler implements NTSendable, AutoCloseable {
|
||||
// Do nothing if the scheduler is disabled, the robot is disabled and the command doesn't
|
||||
// run when disabled, or the command is already scheduled.
|
||||
if (m_disabled
|
||||
|| RobotState.isDisabled() && !command.runsWhenDisabled()
|
||||
|| isScheduled(command)) {
|
||||
|| isScheduled(command)
|
||||
|| RobotState.isDisabled() && !command.runsWhenDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ package edu.wpi.first.wpilibj2.command;
|
||||
|
||||
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
|
||||
|
||||
import edu.wpi.first.util.sendable.SendableBuilder;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
/**
|
||||
@@ -71,4 +72,21 @@ public class ConditionalCommand extends CommandBase {
|
||||
public boolean runsWhenDisabled() {
|
||||
return m_onTrue.runsWhenDisabled() && m_onFalse.runsWhenDisabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initSendable(SendableBuilder builder) {
|
||||
super.initSendable(builder);
|
||||
builder.addStringProperty("onTrue", m_onTrue::getName, null);
|
||||
builder.addStringProperty("onFalse", m_onFalse::getName, null);
|
||||
builder.addStringProperty(
|
||||
"selected",
|
||||
() -> {
|
||||
if (m_selectedCommand == null) {
|
||||
return "null";
|
||||
} else {
|
||||
return m_selectedCommand.getName();
|
||||
}
|
||||
},
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
package edu.wpi.first.wpilibj2.command;
|
||||
|
||||
import edu.wpi.first.util.sendable.SendableBuilder;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -130,4 +131,11 @@ public class ParallelDeadlineGroup extends CommandGroupBase {
|
||||
public InterruptionBehavior getInterruptionBehavior() {
|
||||
return m_interruptBehavior;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initSendable(SendableBuilder builder) {
|
||||
super.initSendable(builder);
|
||||
|
||||
builder.addStringProperty("deadline", m_deadline::getName, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package edu.wpi.first.wpilibj2.command;
|
||||
|
||||
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
|
||||
|
||||
import edu.wpi.first.util.sendable.SendableBuilder;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
@@ -36,6 +37,7 @@ public class ProxyCommand extends CommandBase {
|
||||
*/
|
||||
public ProxyCommand(Command command) {
|
||||
this(() -> command);
|
||||
setName("Proxy(" + command.getName() + ")");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -73,4 +75,11 @@ public class ProxyCommand extends CommandBase {
|
||||
public boolean runsWhenDisabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initSendable(SendableBuilder builder) {
|
||||
super.initSendable(builder);
|
||||
builder.addStringProperty(
|
||||
"proxied", () -> m_command == null ? "null" : m_command.getName(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import edu.wpi.first.math.kinematics.ChassisSpeeds;
|
||||
import edu.wpi.first.math.kinematics.DifferentialDriveKinematics;
|
||||
import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds;
|
||||
import edu.wpi.first.math.trajectory.Trajectory;
|
||||
import edu.wpi.first.util.sendable.SendableBuilder;
|
||||
import edu.wpi.first.wpilibj.Timer;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
@@ -211,4 +212,11 @@ public class RamseteCommand extends CommandBase {
|
||||
public boolean isFinished() {
|
||||
return m_timer.hasElapsed(m_trajectory.getTotalTimeSeconds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initSendable(SendableBuilder builder) {
|
||||
super.initSendable(builder);
|
||||
builder.addDoubleProperty("leftVelocity", () -> m_prevSpeeds.leftMetersPerSecond, null);
|
||||
builder.addDoubleProperty("rightVelocity", () -> m_prevSpeeds.rightMetersPerSecond, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ package edu.wpi.first.wpilibj2.command;
|
||||
|
||||
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
|
||||
|
||||
import edu.wpi.first.util.sendable.SendableBuilder;
|
||||
|
||||
/**
|
||||
* A command that runs another command repeatedly, restarting it when it ends, until this command is
|
||||
* interrupted. Command instances that are passed to it cannot be added to any other groups, or
|
||||
@@ -31,6 +33,7 @@ public class RepeatCommand extends CommandBase {
|
||||
m_command = requireNonNullParam(command, "command", "RepeatCommand");
|
||||
CommandScheduler.getInstance().registerComposedCommands(command);
|
||||
m_requirements.addAll(command.getRequirements());
|
||||
setName("Repeat(" + command.getName() + ")");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,4 +75,10 @@ public class RepeatCommand extends CommandBase {
|
||||
public InterruptionBehavior getInterruptionBehavior() {
|
||||
return m_command.getInterruptionBehavior();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initSendable(SendableBuilder builder) {
|
||||
super.initSendable(builder);
|
||||
builder.addStringProperty("command", m_command::getName, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package edu.wpi.first.wpilibj2.command;
|
||||
|
||||
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
|
||||
|
||||
import edu.wpi.first.util.sendable.SendableBuilder;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -108,4 +109,12 @@ public class SelectCommand extends CommandBase {
|
||||
public InterruptionBehavior getInterruptionBehavior() {
|
||||
return m_interruptBehavior;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initSendable(SendableBuilder builder) {
|
||||
super.initSendable(builder);
|
||||
|
||||
builder.addStringProperty(
|
||||
"selected", () -> m_selectedCommand == null ? "null" : m_selectedCommand.getName(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
package edu.wpi.first.wpilibj2.command;
|
||||
|
||||
import edu.wpi.first.util.sendable.SendableBuilder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -104,4 +105,11 @@ public class SequentialCommandGroup extends CommandGroupBase {
|
||||
public InterruptionBehavior getInterruptionBehavior() {
|
||||
return m_interruptBehavior;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initSendable(SendableBuilder builder) {
|
||||
super.initSendable(builder);
|
||||
|
||||
builder.addIntegerProperty("index", () -> m_currentCommandIndex, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
package edu.wpi.first.wpilibj2.command;
|
||||
|
||||
import edu.wpi.first.util.sendable.SendableBuilder;
|
||||
import edu.wpi.first.util.sendable.SendableRegistry;
|
||||
import edu.wpi.first.wpilibj.Timer;
|
||||
|
||||
@@ -47,4 +48,10 @@ public class WaitCommand extends CommandBase {
|
||||
public boolean runsWhenDisabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initSendable(SendableBuilder builder) {
|
||||
super.initSendable(builder);
|
||||
builder.addDoubleProperty("duration", () -> m_duration, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ public abstract class WrapperCommand extends CommandBase {
|
||||
protected WrapperCommand(Command command) {
|
||||
CommandScheduler.getInstance().registerComposedCommands(command);
|
||||
m_command = command;
|
||||
// copy the wrapped command's name
|
||||
setName(command.getName());
|
||||
}
|
||||
|
||||
/** The initial subroutine of a command. Called once when the command is initially scheduled. */
|
||||
|
||||
@@ -49,7 +49,7 @@ public class CommandXboxController extends CommandGenericHID {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an event instance around the right bumper's digital signal.
|
||||
* Constructs an event instance around the left bumper's digital signal.
|
||||
*
|
||||
* @param loop the event loop instance to attach the event to.
|
||||
* @return an event instance representing the right bumper's digital signal attached to the given
|
||||
@@ -71,7 +71,7 @@ public class CommandXboxController extends CommandGenericHID {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an event instance around the left bumper's digital signal.
|
||||
* Constructs an event instance around the right bumper's digital signal.
|
||||
*
|
||||
* @param loop the event loop instance to attach the event to.
|
||||
* @return an event instance representing the left bumper's digital signal attached to the given
|
||||
|
||||
@@ -472,10 +472,10 @@ public class Trigger implements BooleanSupplier {
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes two triggers with logical OR.
|
||||
* Composes two triggers with logical AND.
|
||||
*
|
||||
* @param trigger the condition to compose with
|
||||
* @return A trigger which is active when either component trigger is active.
|
||||
* @return A trigger which is active when both component triggers are active.
|
||||
*/
|
||||
public Trigger and(BooleanSupplier trigger) {
|
||||
return new Trigger(() -> m_condition.getAsBoolean() && trigger.getAsBoolean());
|
||||
|
||||
@@ -105,8 +105,7 @@ CommandPtr Command::HandleInterrupt(std::function<void(void)> handler) && {
|
||||
}
|
||||
|
||||
CommandPtr Command::WithName(std::string_view name) && {
|
||||
SetName(name);
|
||||
return std::move(*this).ToPtr();
|
||||
return std::move(*this).ToPtr().WithName(name);
|
||||
}
|
||||
|
||||
void Command::Schedule() {
|
||||
|
||||
@@ -64,4 +64,21 @@ void CommandBase::InitSendable(wpi::SendableBuilder& builder) {
|
||||
Cancel();
|
||||
}
|
||||
});
|
||||
builder.AddBooleanProperty(
|
||||
".isParented", [this] { return IsComposed(); }, nullptr);
|
||||
builder.AddStringProperty(
|
||||
"interruptBehavior",
|
||||
[this] {
|
||||
switch (GetInterruptionBehavior()) {
|
||||
case Command::InterruptionBehavior::kCancelIncoming:
|
||||
return "kCancelIncoming";
|
||||
case Command::InterruptionBehavior::kCancelSelf:
|
||||
return "kCancelSelf";
|
||||
default:
|
||||
return "Invalid";
|
||||
}
|
||||
},
|
||||
nullptr);
|
||||
builder.AddBooleanProperty(
|
||||
"runsWhenDisabled", [this] { return RunsWhenDisabled(); }, nullptr);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user