[ntcore] Pass pub/sub options as a unified PubSubOptions struct (#4794)

In Java, PubSubOption is still used for passing options, but this
simplifies C++ use substantially, as it allows aggregate construction.
This commit is contained in:
Peter Johnson
2022-12-12 19:28:15 -08:00
committed by GitHub
parent f66a667321
commit a865f48e96
53 changed files with 695 additions and 623 deletions

View File

@@ -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} {

View File

@@ -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)},

View File

@@ -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)},

View File

@@ -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 |

View File

@@ -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} {}

View File

@@ -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)},

View File

@@ -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))},

View File

@@ -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)},

View File

@@ -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)},

View File

@@ -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);

View File

@@ -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 %}
};

View File

@@ -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};

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
@@ -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) {
@@ -1642,7 +1642,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 +1669,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 +1678,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 +1689,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 +1739,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

View File

@@ -93,24 +93,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);

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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));
}
/*

View File

@@ -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;
@@ -67,7 +67,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);
@@ -282,7 +282,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);
@@ -445,7 +446,7 @@ ClientStartup::~ClientStartup() {
void ClientStartup::Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
std::string_view name, std::string_view typeStr,
const wpi::json& properties,
const PubSubOptions& options) {
const PubSubOptionsImpl& options) {
WPI_DEBUG4(m_client.m_impl->m_logger, "StartupPublish({}, {}, {}, {})",
pubHandle, topicHandle, name, typeStr);
m_client.m_impl->Publish(pubHandle, topicHandle, name, typeStr, properties,
@@ -456,7 +457,7 @@ void ClientStartup::Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
void ClientStartup::Subscribe(NT_Subscriber subHandle,
std::span<const std::string> prefixes,
const PubSubOptions& options) {
const PubSubOptionsImpl& options) {
WPI_DEBUG4(m_client.m_impl->m_logger, "StartupSubscribe({})", subHandle);
WireEncodeSubscribe(m_textWriter.Add(), Handle{subHandle}.GetIndex(),
prefixes, options);

View File

@@ -21,7 +21,7 @@ class Logger;
} // namespace wpi
namespace nt {
class PubSubOptions;
class PubSubOptionsImpl;
class Value;
} // namespace nt
@@ -64,9 +64,10 @@ class ClientStartup final : public NetworkStartupInterface {
// 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;
const wpi::json& properties,
const PubSubOptionsImpl& options) final;
void Subscribe(NT_Subscriber subHandle, std::span<const std::string> prefixes,
const PubSubOptions& options) final;
const PubSubOptionsImpl& options) final;
void SetValue(NT_Publisher pubHandle, const Value& value) final;
private:

View File

@@ -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 {

View File

@@ -15,7 +15,7 @@ class json;
} // namespace wpi
namespace nt {
class PubSubOptions;
class PubSubOptionsImpl;
class Value;
} // namespace nt
@@ -42,10 +42,10 @@ class NetworkStartupInterface {
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;
const PubSubOptionsImpl& options) = 0;
virtual void Subscribe(NT_Subscriber subHandle,
std::span<const std::string> topicNames,
const PubSubOptions& options) = 0;
const PubSubOptionsImpl& options) = 0;
virtual void SetValue(NT_Publisher pubHandle, const Value& value) = 0;
};

View File

@@ -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;

View File

@@ -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}});

View File

@@ -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);
@@ -362,7 +362,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 +374,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 +388,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 +463,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 +477,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 +617,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];
@@ -2365,14 +2366,14 @@ std::string ServerImpl::LoadPersistent(std::string_view 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) {
const PubSubOptionsImpl& 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) {
const PubSubOptionsImpl& options) {
m_server.m_impl->m_localClient->ClientSubscribe(subHandle, topicNames,
options);
}

View File

@@ -83,10 +83,11 @@ class ServerStartup final : public NetworkStartupInterface {
// 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;
const wpi::json& properties,
const PubSubOptionsImpl& options) final;
void Subscribe(NT_Subscriber subHandle,
std::span<const std::string> topicNames,
const PubSubOptions& options) final;
const PubSubOptionsImpl& options) final;
void SetValue(NT_Publisher pubHandle, const Value& value) final;
private:

View File

@@ -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;
}

View File

@@ -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;
};

View File

@@ -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);
}

View File

@@ -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)

View File

@@ -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()) {
@@ -647,7 +648,7 @@ 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) {
const PubSubOptionsImpl& options) {
WPI_DEBUG4(m_client.m_impl->m_logger, "StartupPublish({}, {}, {}, {})",
pubHandle, topicHandle, name, typeStr);
m_client.m_impl->Publish(pubHandle, topicHandle, name, typeStr, properties,
@@ -656,7 +657,7 @@ void ClientStartup3::Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
void ClientStartup3::Subscribe(NT_Subscriber subHandle,
std::span<const std::string> prefixes,
const PubSubOptions& options) {
const PubSubOptionsImpl& options) {
// NT3 ignores subscribes, so no action required
}

View File

@@ -60,9 +60,10 @@ class ClientStartup3 final : public net::NetworkStartupInterface {
// 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;
const wpi::json& properties,
const PubSubOptionsImpl& options) final;
void Subscribe(NT_Subscriber subHandle, std::span<const std::string> prefixes,
const PubSubOptions& options) final;
const PubSubOptionsImpl& options) final;
void SetValue(NT_Publisher pubHandle, const Value& value) final;
private:

View File

@@ -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)};
}

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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};
}

View File

@@ -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.

View File

@@ -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.

View File

@@ -25,14 +25,19 @@ 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 {
@@ -72,7 +77,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 +104,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 +114,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 +125,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 +142,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 +179,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 +187,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 +205,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 +251,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 +281,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 +298,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 +319,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 +335,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 +349,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 +366,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 +378,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 +397,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 +405,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 +415,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 +434,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 +455,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 +465,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 +473,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 +505,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 +843,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 +878,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 +900,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);

View File

@@ -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) {

View File

@@ -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)));
}

View File

@@ -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 << '}';
}

View File

@@ -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

View File

@@ -33,11 +33,11 @@ class MockNetworkStartupInterface : public NetworkStartupInterface {
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, Subscribe,
(NT_Subscriber subHandle, std::span<const std::string> prefixes,
const PubSubOptions& options),
const PubSubOptionsImpl& options),
(override));
MOCK_METHOD(void, SetValue, (NT_Publisher pubHandle, const Value& value),
(override));
@@ -48,7 +48,7 @@ class MockNetworkInterface : public NetworkInterface {
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 +58,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),

View File

@@ -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));
};

View File

@@ -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"}},

View File

@@ -130,8 +130,8 @@ void SendableBuilderImpl::AddPropertyImpl(Topic topic, Getter getter,
};
}
if (setter) {
prop->sub = topic.Subscribe(
{}, {{nt::PubSubOption::ExcludePublisher(prop->pub.GetHandle())}});
prop->sub =
topic.Subscribe({}, {.excludePublisher = prop->pub.GetHandle()});
prop->updateLocal = [=](auto& sub) {
for (auto&& val : sub.ReadQueue()) {
setter(val.value);
@@ -224,9 +224,8 @@ void SendableBuilderImpl::AddRawProperty(
};
}
if (setter) {
prop->sub = topic.Subscribe(
typeString, {},
{{nt::PubSubOption::ExcludePublisher(prop->pub.GetHandle())}});
prop->sub = topic.Subscribe(typeString, {},
{.excludePublisher = prop->pub.GetHandle()});
prop->updateLocal = [=](auto& sub) {
for (auto&& val : sub.ReadQueue()) {
setter(val.value);
@@ -249,8 +248,8 @@ void SendableBuilderImpl::AddSmallPropertyImpl(Topic topic, Getter getter,
};
}
if (setter) {
prop->sub = topic.Subscribe(
{}, {{nt::PubSubOption::ExcludePublisher(prop->pub.GetHandle())}});
prop->sub =
topic.Subscribe({}, {.excludePublisher = prop->pub.GetHandle()});
prop->updateLocal = [=](auto& sub) {
for (auto&& val : sub.ReadQueue()) {
setter(val.value);
@@ -328,9 +327,8 @@ void SendableBuilderImpl::AddSmallRawProperty(
};
}
if (setter) {
prop->sub = topic.Subscribe(
typeString, {},
{{nt::PubSubOption::ExcludePublisher(prop->pub.GetHandle())}});
prop->sub = topic.Subscribe(typeString, {},
{.excludePublisher = prop->pub.GetHandle()});
prop->updateLocal = [=](auto& sub) {
for (auto&& val : sub.ReadQueue()) {
setter(val.value);