[robotpy] Mirror most other subprojects (#8208)

GitOrigin-RevId: ac60fd3cf4a24023184376687da28373d14b781a

This mirrors the robotpy files for the following projects:
- apriltag
- datalog
- hal
- ntcore
- romiVendordep
- wpilibc
- wpimath
- xrpVendordep

This excludes cscore and the halsim wrappers for at this time.

NOTE: This does not hook these projects up to the build system, just simply mirrors the files. The building will take place in a follow up PR to make it easier to review the changes necessary to build.
This commit is contained in:
PJ Reiniger
2025-10-24 01:28:04 -04:00
committed by GitHub
parent 8992dcdc99
commit 44b9cc1398
545 changed files with 27293 additions and 38 deletions

View File

@@ -0,0 +1,4 @@
pyntcore
========
Python pybind11 wrappers around the C++ ntcore library.

View File

@@ -0,0 +1,64 @@
#!/usr/bin/env python3
import sphinxify
from cxxheaderparser.simple import parse_string
from cxxheaderparser.tokfmt import tokfmt
if __name__ == "__main__":
with open("ntcore/include/ntcore_cpp.h") as fp:
data = parse_string(fp.read())
for c in data.namespace.namespaces["nt"].classes:
if c.class_decl.typename.format() == "struct PubSubOptions":
params = []
docs = []
for f in c.fields:
if f.static or f.name == "structSize":
continue
if f.type.format() == "NT_Publisher":
params.append(
(
"std::optional<std::shared_ptr<nt::Publisher>>",
f.name,
f"{f.name}.has_value() ? {f.name}.value()->GetHandle() : {f.value.format()}",
"std::nullopt",
)
)
else:
v = f.value.format()
if v == "kDefaultPeriodic":
v = f"nt::PubSubOptions::{v}"
params.append((f.type, f.name, f.name, v))
if f.doxygen:
docs.append(f"@param {f.name} {f.doxygen}")
paramstr = ",\n ".join(f"{t.format()} {n}" for t, n, _, _ in params)
args = ",\n ".join(f'py::arg("{n}") = {v}' for _, n, _, v in params)
options = ",\n ".join(f".{fn} = {n}" for _, fn, n, _ in params)
doc = "\n ".join(
sphinxify.process_raw("\n".join(docs)).splitlines()
)
print(
f"""
// autogenerated by gen-pubsub.py
.def(py::init([](
{paramstr}
) -> nt::PubSubOptions {{
return nt::PubSubOptions{{
{options}
}};
}}),
py::kw_only(),
{args},
R"(
{doc}
)"
)
"""
)

View File

@@ -0,0 +1,43 @@
[build-system]
build-backend = "hatchling.build"
requires = [
"hatchling",
"hatch-nativelib~=0.2.0",
"hatch-robotpy~=0.2.1",
"robotpy-native-wpiutil==2027.0.0a2",
"robotpy-native-wpinet==2027.0.0a2",
"robotpy-native-datalog==2027.0.0a2",
]
[project]
name = "robotpy-native-ntcore"
version = "2027.0.0a2"
description = "WPILib NetworkTables Library"
license = "BSD-3-Clause"
dependencies = [
"robotpy-native-wpiutil==2027.0.0a2",
"robotpy-native-wpinet==2027.0.0a2",
"robotpy-native-datalog==2027.0.0a2",
]
[tool.hatch.build.targets.wheel]
packages = ["src/native"]
[[tool.hatch.build.hooks.robotpy.maven_lib_download]]
artifact_id = "ntcore-cpp"
group_id = "edu.wpi.first.ntcore"
repo_url = "https://frcmaven.wpi.edu/artifactory/release-2027"
version = "2027.0.0-alpha-2"
extract_to = "src/native/ntcore"
libs = ["ntcore"]
[[tool.hatch.build.hooks.nativelib.pcfile]]
pcfile = "src/native/ntcore/robotpy-native-ntcore.pc"
name = "ntcore"
includedir = "src/native/ntcore/include"
libdir = "src/native/ntcore/lib"
shared_libraries = ["ntcore"]
requires = ["robotpy-native-datalog", "robotpy-native-wpinet", "robotpy-native-wpiutil"]

View File

@@ -0,0 +1,195 @@
from . import _init__ntcore
# autogenerated by 'semiwrap create-imports ntcore ntcore._ntcore'
from ._ntcore import (
BooleanArrayEntry,
BooleanArrayPublisher,
BooleanArraySubscriber,
BooleanArrayTopic,
BooleanEntry,
BooleanPublisher,
BooleanSubscriber,
BooleanTopic,
ConnectionInfo,
DoubleArrayEntry,
DoubleArrayPublisher,
DoubleArraySubscriber,
DoubleArrayTopic,
DoubleEntry,
DoublePublisher,
DoubleSubscriber,
DoubleTopic,
Event,
EventFlags,
FloatArrayEntry,
FloatArrayPublisher,
FloatArraySubscriber,
FloatArrayTopic,
FloatEntry,
FloatPublisher,
FloatSubscriber,
FloatTopic,
GenericEntry,
GenericPublisher,
GenericSubscriber,
IntegerArrayEntry,
IntegerArrayPublisher,
IntegerArraySubscriber,
IntegerArrayTopic,
IntegerEntry,
IntegerPublisher,
IntegerSubscriber,
IntegerTopic,
LogMessage,
MultiSubscriber,
NTSendable,
NTSendableBuilder,
NetworkTable,
NetworkTableEntry,
NetworkTableInstance,
NetworkTableListener,
NetworkTableListenerPoller,
NetworkTableType,
PubSubOptions,
Publisher,
RawEntry,
RawPublisher,
RawSubscriber,
RawTopic,
StringArrayEntry,
StringArrayPublisher,
StringArraySubscriber,
StringArrayTopic,
StringEntry,
StringPublisher,
StringSubscriber,
StringTopic,
StructArrayEntry,
StructArrayPublisher,
StructArraySubscriber,
StructArrayTopic,
StructEntry,
StructPublisher,
StructSubscriber,
StructTopic,
Subscriber,
TimeSyncEventData,
TimestampedBoolean,
TimestampedBooleanArray,
TimestampedDouble,
TimestampedDoubleArray,
TimestampedFloat,
TimestampedFloatArray,
TimestampedInteger,
TimestampedIntegerArray,
TimestampedRaw,
TimestampedString,
TimestampedStringArray,
TimestampedStruct,
TimestampedStructArray,
Topic,
TopicInfo,
Value,
ValueEventData,
)
__all__ = [
"BooleanArrayEntry",
"BooleanArrayPublisher",
"BooleanArraySubscriber",
"BooleanArrayTopic",
"BooleanEntry",
"BooleanPublisher",
"BooleanSubscriber",
"BooleanTopic",
"ConnectionInfo",
"DoubleArrayEntry",
"DoubleArrayPublisher",
"DoubleArraySubscriber",
"DoubleArrayTopic",
"DoubleEntry",
"DoublePublisher",
"DoubleSubscriber",
"DoubleTopic",
"Event",
"EventFlags",
"FloatArrayEntry",
"FloatArrayPublisher",
"FloatArraySubscriber",
"FloatArrayTopic",
"FloatEntry",
"FloatPublisher",
"FloatSubscriber",
"FloatTopic",
"GenericEntry",
"GenericPublisher",
"GenericSubscriber",
"IntegerArrayEntry",
"IntegerArrayPublisher",
"IntegerArraySubscriber",
"IntegerArrayTopic",
"IntegerEntry",
"IntegerPublisher",
"IntegerSubscriber",
"IntegerTopic",
"LogMessage",
"MultiSubscriber",
"NTSendable",
"NTSendableBuilder",
"NetworkTable",
"NetworkTableEntry",
"NetworkTableInstance",
"NetworkTableListener",
"NetworkTableListenerPoller",
"NetworkTableType",
"PubSubOptions",
"Publisher",
"RawEntry",
"RawPublisher",
"RawSubscriber",
"RawTopic",
"StringArrayEntry",
"StringArrayPublisher",
"StringArraySubscriber",
"StringArrayTopic",
"StringEntry",
"StringPublisher",
"StringSubscriber",
"StringTopic",
"StructArrayEntry",
"StructArrayPublisher",
"StructArraySubscriber",
"StructArrayTopic",
"StructEntry",
"StructPublisher",
"StructSubscriber",
"StructTopic",
"Subscriber",
"TimeSyncEventData",
"TimestampedBoolean",
"TimestampedBooleanArray",
"TimestampedDouble",
"TimestampedDoubleArray",
"TimestampedFloat",
"TimestampedFloatArray",
"TimestampedInteger",
"TimestampedIntegerArray",
"TimestampedRaw",
"TimestampedString",
"TimestampedStringArray",
"TimestampedStruct",
"TimestampedStructArray",
"Topic",
"TopicInfo",
"Value",
"ValueEventData",
]
from ._ntcore import _now, _setNow
__all__ += ["_now", "_setNow"]
try:
from .version import version as __version__
except ImportError:
__version__ = "master"

View File

@@ -0,0 +1,123 @@
import atexit
import logging
import threading
from . import _ntcore
import wpiutil.sync
class InstanceAlreadyStartedError(Exception):
pass
class NtLogForwarder:
"""
Forwards ntcore's logger to python's logging system
"""
_instlock = threading.Lock()
_instances = {}
_instcfg = {}
@classmethod
def config_logging(
cls,
instance: _ntcore.NetworkTableInstance,
minLevel: _ntcore.NetworkTableInstance.LogLevel,
maxLevel: _ntcore.NetworkTableInstance.LogLevel,
logName: str,
):
handle = instance._getHandle()
with cls._instlock:
if handle in cls._instances:
raise InstanceAlreadyStartedError(
"cannot configure logging after instance has been started"
)
cls._instcfg[handle] = (minLevel, maxLevel, logName)
@classmethod
def onInstanceStart(cls, instance: _ntcore.NetworkTableInstance):
handle = instance._getHandle()
with cls._instlock:
if handle in cls._instances:
return
default_cfg = (
_ntcore.NetworkTableInstance.LogLevel.kLogInfo,
_ntcore.NetworkTableInstance.LogLevel.kLogCritical,
"nt",
)
minLevel, maxLevel, logName = cls._instcfg.get(handle, default_cfg)
cls._instances[handle] = cls(instance, logName, minLevel, maxLevel)
@classmethod
def onInstanceDestroy(cls, instance: _ntcore.NetworkTableInstance):
handle = instance._getHandle()
with cls._instlock:
lfwd = cls._instances.pop(handle, None)
if lfwd:
lfwd.destroy()
def __init__(
self,
instance: _ntcore.NetworkTableInstance,
logName: str,
minLevel: _ntcore.NetworkTableInstance.LogLevel,
maxLevel: _ntcore.NetworkTableInstance.LogLevel,
):
self.lock = threading.Lock()
self.poller = _ntcore.NetworkTableListenerPoller(instance)
ntLogger = self.poller.addLogger(minLevel, maxLevel)
self.thread = threading.Thread(
target=self._logging_thread,
name=logName + "-log-thread",
daemon=True,
args=(self.poller, logName, ntLogger),
)
self.thread.start()
atexit.register(self.destroy)
def _logging_thread(
self, poller: _ntcore.NetworkTableListenerPoller, logName: str, ntLogger: int
):
logger = logging.getLogger(logName)
_waitForObject = wpiutil.sync.waitForObject
handle = poller.getHandle()
while True:
if not _waitForObject(handle):
break
messages = poller.readQueue()
if not messages:
continue
for msg in messages:
msg = msg.data
if logger.isEnabledFor(msg.level):
lr = logger.makeRecord(
logName,
msg.level,
msg.filename,
msg.line,
"%s",
(msg.message,),
None,
)
logger.handle(lr)
def destroy(self):
with self.lock:
if self.poller:
self.poller.close()
self.thread.join(timeout=1)
self.poller = None
_config_logging = NtLogForwarder.config_logging

View File

@@ -0,0 +1,28 @@
# autogenerated by 'semiwrap create-imports ntcore.meta ntcore._ntcore.meta'
from .._ntcore.meta import (
Client,
ClientPublisher,
ClientSubscriber,
SubscriberOptions,
TopicPublisher,
TopicSubscriber,
decodeClientPublishers,
decodeClientSubscribers,
decodeClients,
decodeTopicPublishers,
decodeTopicSubscribers,
)
__all__ = [
"Client",
"ClientPublisher",
"ClientSubscriber",
"SubscriberOptions",
"TopicPublisher",
"TopicSubscriber",
"decodeClientPublishers",
"decodeClientSubscribers",
"decodeClients",
"decodeTopicPublishers",
"decodeTopicSubscribers",
]

View File

View File

@@ -0,0 +1,56 @@
cls_NetworkTable
.def("getValue", [](const NetworkTable &self, std::string_view key, py::object defaultValue) -> py::object {
nt::NetworkTableEntry entry;
{
py::gil_scoped_release release;
entry = self.GetEntry(key);
}
return pyntcore::GetValueEntry(entry, defaultValue);
}, py::arg("key"), py::arg("value"))
// double overload must come before boolean version
.def("putValue", [](nt::NetworkTable *self, std::string_view key, double value) {
return self->PutValue(key, nt::Value::MakeDouble(value));
}, py::arg("key"), py::arg("value"), release_gil())
.def("putValue", [](nt::NetworkTable *self, std::string_view key, bool value) {
return self->PutValue(key, nt::Value::MakeBoolean(value));
}, py::arg("key"), py::arg("value"), release_gil())
.def("putValue", [](nt::NetworkTable *self, std::string_view key, py::bytes value) {
auto v = nt::Value::MakeRaw(value.cast<std::span<const uint8_t>>());
py::gil_scoped_release release;
return self->PutValue(key, v);
}, py::arg("key"), py::arg("value"))
.def("putValue", [](nt::NetworkTable *self, std::string_view key, std::string value) {
return self->PutValue(key, nt::Value::MakeString(std::move(value)));
}, py::arg("key"), py::arg("value"), release_gil())
.def("putValue", [](nt::NetworkTable *self, std::string_view key, py::sequence value) {
auto v = pyntcore::py2ntvalue(value);
py::gil_scoped_release release;
return self->PutValue(key, v);
}, py::arg("key"), py::arg("value"))
// double overload must come before boolean version
.def("setDefaultValue", [](nt::NetworkTable *self, std::string_view key, double value) {
return self->SetDefaultValue(key, nt::Value::MakeDouble(value));
}, py::arg("key"), py::arg("value"), release_gil())
.def("setDefaultValue", [](nt::NetworkTable *self, std::string_view key, bool value) {
return self->SetDefaultValue(key, nt::Value::MakeBoolean(value));
}, py::arg("key"), py::arg("value"), release_gil())
.def("setDefaultValue", [](nt::NetworkTable *self, std::string_view key, py::bytes value) {
auto v = nt::Value::MakeRaw(value.cast<std::span<const uint8_t>>());
py::gil_scoped_release release;
return self->SetDefaultValue(key, v);
}, py::arg("key"), py::arg("value"))
.def("setDefaultValue", [](nt::NetworkTable *self, std::string_view key, std::string value) {
return self->SetDefaultValue(key, nt::Value::MakeString(std::move(value)));
}, py::arg("key"), py::arg("value"), release_gil())
.def("setDefaultValue", [](nt::NetworkTable *self, std::string_view key, py::sequence value) {
auto v = pyntcore::py2ntvalue(value);
py::gil_scoped_release release;
return self->SetDefaultValue(key, v);
}, py::arg("key"), py::arg("value"))
.def("__contains__", [](const nt::NetworkTable &self, std::string_view key) -> bool {
return self.ContainsKey(key);
}, release_gil())
;

View File

@@ -0,0 +1,48 @@
cls_NetworkTableEntry
.def_property_readonly("value", [](const nt::NetworkTableEntry &self) {
nt::Value v;
{
py::gil_scoped_release release;
v = self.GetValue();
}
return pyntcore::ntvalue2py(v);
})
// double overload must come before boolean version
.def("setValue", [](nt::NetworkTableEntry *self, double value) {
return self->SetValue(nt::Value::MakeDouble(value));
}, py::arg("value"), release_gil())
.def("setValue", [](nt::NetworkTableEntry *self, bool value) {
return self->SetValue(nt::Value::MakeBoolean(value));
}, py::arg("value"), release_gil())
.def("setValue", [](nt::NetworkTableEntry *self, py::bytes value) {
auto v = nt::Value::MakeRaw(value.cast<std::span<const uint8_t>>());
py::gil_scoped_release release;
return self->SetValue(v);
}, py::arg("value"))
.def("setValue", [](nt::NetworkTableEntry *self, std::string value) {
return self->SetValue(nt::Value::MakeString(value));
}, py::arg("value"), release_gil())
.def("setValue", [](nt::NetworkTableEntry *self, py::sequence value) {
return self->SetValue(pyntcore::py2ntvalue(value));
}, py::arg("value"))
// double overload must come before boolean version
.def("setDefaultValue", [](nt::NetworkTableEntry *self, double value) {
return self->SetDefaultValue(nt::Value::MakeDouble(value));
}, py::arg("value"), release_gil())
.def("setDefaultValue", [](nt::NetworkTableEntry *self, bool value) {
return self->SetDefaultValue(nt::Value::MakeBoolean(value));
}, py::arg("value"), release_gil())
.def("setDefaultValue", [](nt::NetworkTableEntry *self, py::bytes value) {
auto v = nt::Value::MakeRaw(value.cast<std::span<const uint8_t>>());
py::gil_scoped_release release;
return self->SetDefaultValue(v);
}, py::arg("value"))
.def("setDefaultValue", [](nt::NetworkTableEntry *self, std::string value) {
return self->SetDefaultValue(nt::Value::MakeString(value));
}, py::arg("value"), release_gil())
.def("setDefaultValue", [](nt::NetworkTableEntry *self, py::sequence value) {
return self->SetDefaultValue(pyntcore::py2ntvalue(value));
}, py::arg("value"))
;

View File

@@ -0,0 +1,56 @@
#include <semiwrap.h>
#include "nt_instance.h"
#include "ntcore_cpp.h"
#include <set>
// only accessed under GIL
static std::set<NT_Inst> g_known_instances;
namespace pyntcore {
void onInstanceStart(nt::NetworkTableInstance *instance) {
g_known_instances.emplace(instance->GetHandle());
py::module::import("ntcore._logutil")
.attr("NtLogForwarder").attr("onInstanceStart")(instance);
}
void onInstancePreReset(nt::NetworkTableInstance *instance) {
py::module::import("ntcore._logutil")
.attr("NtLogForwarder").attr("onInstanceDestroy")(instance);
}
void onInstancePostReset(nt::NetworkTableInstance *instance) {
py::module::import("ntcore.util")
.attr("_NtProperty").attr("onInstancePostReset")(instance);
}
void onInstanceDestroy(nt::NetworkTableInstance *instance) {
py::module::import("ntcore._logutil")
.attr("NtLogForwarder").attr("onInstanceDestroy")(instance);
py::module::import("ntcore.util")
.attr("_NtProperty").attr("onInstanceDestroy")(instance);
g_known_instances.erase(instance->GetHandle());
}
// reset all instances to clear out any potential python references that
// might be hanging around in a callback or something
void resetAllInstances()
{
std::set<NT_Inst> known_instances;
known_instances.swap(g_known_instances);
// always reset the default instance
known_instances.emplace(nt::GetDefaultInstance());
py::gil_scoped_release unlock;
for (auto &inst: known_instances) {
nt::ResetInstance(inst);
}
}
}; // namespace pyntcore

View File

@@ -0,0 +1,16 @@
#pragma once
#include <ntcore.h>
#include <networktables/NetworkTableInstance.h>
namespace pyntcore {
void onInstanceStart(nt::NetworkTableInstance *instance);
void onInstancePreReset(nt::NetworkTableInstance *instance);
void onInstancePostReset(nt::NetworkTableInstance *instance);
void onInstanceDestroy(nt::NetworkTableInstance *instance);
void resetAllInstances();
}; // namespace pyntcore

View File

@@ -0,0 +1,39 @@
#pragma once
#include <pybind11/pybind11.h>
namespace pybind11 {
namespace detail {
// ntcore uses std::vector<uint8_t> anytime there is a raw value, so
// add this specialization to convert to/from bytes directly
template<>
struct type_caster<std::vector<uint8_t>> {
using vector_type = std::vector<uint8_t>;
PYBIND11_TYPE_CASTER(vector_type, const_name("bytes"));
bool load(handle src, bool convert) {
if (!isinstance<buffer>(src)) {
return false;
}
auto buf = reinterpret_borrow<buffer>(src);
auto req = buf.request();
if (req.ndim != 1) {
return false;
}
auto begin = (const uint8_t*)req.ptr;
auto end = begin + req.size*req.itemsize;
value = std::vector<uint8_t>(begin, end);
return true;
}
static handle cast(const std::vector<uint8_t> &src, return_value_policy policy, handle parent) {
return py::bytes((char*)src.data(), src.size()).release();
}
};
}
}

View File

@@ -0,0 +1,14 @@
#include <semiwrap_init.ntcore._ntcore.hpp>
#include "nt_instance.h"
SEMIWRAP_PYBIND11_MODULE(m) {
initWrapper(m);
static int unused;
py::capsule cleanup(&unused, [](void *) {
pyntcore::resetAllInstances();
});
m.add_object("_st_cleanup", cleanup);
}

View File

@@ -0,0 +1,180 @@
#include "py2value.h"
#include <vector>
// type casters
#include <pybind11/stl.h>
#include <wpi_span_type_caster.h>
namespace pyntcore {
const char * nttype2str(NT_Type type) {
switch (type) {
case NT_BOOLEAN:
return "bool";
case NT_DOUBLE:
return "double";
case NT_STRING:
return "string";
case NT_RAW:
return "raw";
case NT_BOOLEAN_ARRAY:
return "bool[]";
case NT_DOUBLE_ARRAY:
return "double[]";
case NT_STRING_ARRAY:
return "string[]";
case NT_INTEGER:
return "int";
case NT_FLOAT:
return "float";
case NT_INTEGER_ARRAY:
return "int[]";
case NT_FLOAT_ARRAY:
return "float[]";
default:
return "invalid";
}
}
py::object ntvalue2py(const nt::Value &ntvalue) {
auto &v = ntvalue.value();
switch (v.type) {
case NT_BOOLEAN:
return py::bool_(v.data.v_boolean);
case NT_DOUBLE:
return py::float_(v.data.v_double);
case NT_STRING:
return py::str(v.data.v_string.str, v.data.v_string.len);
case NT_RAW:
return py::bytes((const char *)v.data.v_raw.data, v.data.v_raw.size);
case NT_BOOLEAN_ARRAY: {
py::list l(v.data.arr_boolean.size);
for (size_t i = 0; i < v.data.arr_boolean.size; i++) {
auto b = py::bool_(v.data.arr_boolean.arr[i]);
PyList_SET_ITEM(l.ptr(), i, b.release().ptr());
}
return std::move(l);
}
case NT_DOUBLE_ARRAY: {
py::list l(v.data.arr_double.size);
for (size_t i = 0; i < v.data.arr_double.size; i++) {
auto d = py::float_(v.data.arr_double.arr[i]);
PyList_SET_ITEM(l.ptr(), i, d.release().ptr());
}
return std::move(l);
}
case NT_STRING_ARRAY: {
return py::cast(ntvalue.GetStringArray());
}
case NT_INTEGER: {
return py::int_(v.data.v_int);
}
case NT_FLOAT: {
return py::float_(v.data.v_float);
}
case NT_INTEGER_ARRAY: {
py::list l(v.data.arr_int.size);
for (size_t i = 0; i < v.data.arr_int.size; i++) {
auto d = py::int_(v.data.arr_int.arr[i]);
PyList_SET_ITEM(l.ptr(), i, d.release().ptr());
}
return std::move(l);
}
case NT_FLOAT_ARRAY: {
py::list l(v.data.arr_float.size);
for (size_t i = 0; i < v.data.arr_float.size; i++) {
auto d = py::float_(v.data.arr_float.arr[i]);
PyList_SET_ITEM(l.ptr(), i, d.release().ptr());
}
return std::move(l);
}
default:
return py::none();
}
}
nt::Value py2ntvalue(py::handle h) {
if (py::isinstance<py::bool_>(h)) {
return nt::Value::MakeBoolean(h.cast<bool>());
} else if (py::isinstance<py::float_>(h)) {
return nt::Value::MakeDouble(h.cast<double>());
} else if (py::isinstance<py::int_>(h)) {
return nt::Value::MakeInteger(h.cast<int64_t>());
} else if (py::isinstance<py::str>(h)) {
return nt::Value::MakeString(h.cast<std::string>());
} else if (py::isinstance<py::bytes>(h)) {
return nt::Value::MakeRaw(h.cast<std::span<const uint8_t>>());
} else if (py::isinstance<py::none>(h)) {
throw py::value_error("Cannot put None into NetworkTable");
}
auto seq = h.cast<py::sequence>();
if (seq.size() == 0) {
throw py::type_error("If you use a list here, cannot be empty");
}
// check the first item
auto i1 = seq[0];
if (py::isinstance<py::bool_>(i1)) {
auto v = h.cast<std::vector<int>>();
return nt::Value::MakeBooleanArray(v);
} else if (py::isinstance<py::float_>(i1)) {
auto v = h.cast<std::vector<double>>();
return nt::Value::MakeDoubleArray(v);
} else if (py::isinstance<py::int_>(i1)) {
auto v = h.cast<std::vector<int64_t>>();
return nt::Value::MakeIntegerArray(v);
} else if (py::isinstance<py::str>(i1)) {
auto v = h.cast<std::vector<std::string>>();
return nt::Value::MakeStringArray(v);
} else {
throw py::value_error("Can only put bool/int/float/str/bytes or lists/tuples of them");
}
}
py::function valueFactoryByType(nt::NetworkTableType type) {
py::object PyNtValue = py::module::import("ntcore").attr("Value");
switch (type) {
case nt::NetworkTableType::kBoolean:
return PyNtValue.attr("makeBoolean");
case nt::NetworkTableType::kDouble:
return PyNtValue.attr("makeDouble");
case nt::NetworkTableType::kString:
return PyNtValue.attr("makeString");
case nt::NetworkTableType::kRaw:
return PyNtValue.attr("makeRaw");
case nt::NetworkTableType::kBooleanArray:
return PyNtValue.attr("makeBooleanArray");
case nt::NetworkTableType::kDoubleArray:
return PyNtValue.attr("makeDoubleArray");
case nt::NetworkTableType::kStringArray:
return PyNtValue.attr("makeStringArray");
case nt::NetworkTableType::kInteger:
return PyNtValue.attr("makeInteger");
case nt::NetworkTableType::kFloat:
return PyNtValue.attr("makeFloat");
case nt::NetworkTableType::kIntegerArray:
return PyNtValue.attr("makeIntegerArray");
case nt::NetworkTableType::kFloatArray:
return PyNtValue.attr("makeFloatArray");
default:
throw py::type_error("empty nt value");
}
}
}

View File

@@ -0,0 +1,25 @@
#include <semiwrap.h>
#include <networktables/NetworkTableValue.h>
#include <networktables/NetworkTableType.h>
#include <fmt/format.h>
namespace pyntcore {
const char * nttype2str(NT_Type type);
py::object ntvalue2py(const nt::Value &ntvalue);
nt::Value py2ntvalue(py::handle h);
py::function valueFactoryByType(nt::NetworkTableType type);
inline void ensure_value_is(NT_Type expected, nt::Value *v) {
if (v->type() != expected) {
throw py::value_error(fmt::format(
"Value type is {}, not {}", nttype2str(v->type()), nttype2str(expected)
));
}
}
};

View File

@@ -0,0 +1,141 @@
#include "pyentry.h"
#include "py2value.h"
#include <pybind11/stl.h>
#include <wpi_span_type_caster.h>
namespace pyntcore {
py::object GetBooleanEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) {
nt::Value value;
{
py::gil_scoped_release release;
value = nt::GetEntryValue(entry.GetHandle());
}
if (!value || value.type() != NT_BOOLEAN) return defaultValue;
return py::cast(value.GetBoolean());
}
py::object GetDoubleEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) {
nt::Value value;
{
py::gil_scoped_release release;
value = nt::GetEntryValue(entry.GetHandle());
}
if (!value || value.type() != NT_DOUBLE) return defaultValue;
return py::cast(value.GetDouble());
}
py::object GetFloatEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) {
nt::Value value;
{
py::gil_scoped_release release;
value = nt::GetEntryValue(entry.GetHandle());
}
if (!value || value.type() != NT_FLOAT) return defaultValue;
return py::cast(value.GetFloat());
}
py::object GetIntegerEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) {
nt::Value value;
{
py::gil_scoped_release release;
value = nt::GetEntryValue(entry.GetHandle());
}
if (!value || value.type() != NT_INTEGER) return defaultValue;
return py::cast(value.GetInteger());
}
py::object GetStringEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) {
nt::Value value;
{
py::gil_scoped_release release;
value = nt::GetEntryValue(entry.GetHandle());
}
if (!value || value.type() != NT_STRING) return defaultValue;
auto s = value.GetString();
return py::str(s.data(), s.size());
}
py::object GetRawEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) {
nt::Value value;
{
py::gil_scoped_release release;
value = nt::GetEntryValue(entry.GetHandle());
}
if (!value || value.type() != NT_RAW) return defaultValue;
return py::cast(value.GetRaw());
}
py::object GetBooleanArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) {
nt::Value value;
{
py::gil_scoped_release release;
value = nt::GetEntryValue(entry.GetHandle());
}
if (!value || value.type() != NT_BOOLEAN_ARRAY) return defaultValue;
// ntcore will return bit vector by default. Convert to List[bool]
auto v = value.value();
py::list l(v.data.arr_boolean.size);
for (size_t i = 0; i < v.data.arr_boolean.size; i++) {
auto b = py::bool_(v.data.arr_boolean.arr[i]);
PyList_SET_ITEM(l.ptr(), i, b.release().ptr());
}
return std::move(l);
}
py::object GetDoubleArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) {
nt::Value value;
{
py::gil_scoped_release release;
value = nt::GetEntryValue(entry.GetHandle());
}
if (!value || value.type() != NT_DOUBLE_ARRAY) return defaultValue;
return py::cast(value.GetDoubleArray());
}
py::object GetFloatArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) {
nt::Value value;
{
py::gil_scoped_release release;
value = nt::GetEntryValue(entry.GetHandle());
}
if (!value || value.type() != NT_FLOAT_ARRAY) return defaultValue;
return py::cast(value.GetFloatArray());
}
py::object GetIntegerArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) {
nt::Value value;
{
py::gil_scoped_release release;
value = nt::GetEntryValue(entry.GetHandle());
}
if (!value || value.type() != NT_INTEGER_ARRAY) return defaultValue;
return py::cast(value.GetIntegerArray());
}
py::object GetStringArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) {
nt::Value value;
{
py::gil_scoped_release release;
value = nt::GetEntryValue(entry.GetHandle());
}
if (!value || value.type() != NT_STRING_ARRAY) return defaultValue;
std::span<const std::string> rval = value.GetStringArray();
return py::cast(rval);
}
py::object GetValueEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) {
nt::Value value;
{
py::gil_scoped_release release;
value = nt::GetEntryValue(entry.GetHandle());
}
if (!value) return defaultValue;
return ntvalue2py(value);
}
}; // pyntcore

View File

@@ -0,0 +1,21 @@
#include <semiwrap.h>
#include <networktables/NetworkTableEntry.h>
#include <networktables/NetworkTableValue.h>
namespace pyntcore {
py::object GetBooleanEntry(const nt::NetworkTableEntry &entry, py::object defaultValue);
py::object GetDoubleEntry(const nt::NetworkTableEntry &entry, py::object defaultValue);
py::object GetFloatEntry(const nt::NetworkTableEntry &entry, py::object defaultValue);
py::object GetIntegerEntry(const nt::NetworkTableEntry &entry, py::object defaultValue);
py::object GetStringEntry(const nt::NetworkTableEntry &entry, py::object defaultValue);
py::object GetRawEntry(const nt::NetworkTableEntry &entry, py::object defaultValue);
py::object GetBooleanArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue);
py::object GetDoubleArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue);
py::object GetFloatArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue);
py::object GetIntegerArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue);
py::object GetStringArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue);
py::object GetValueEntry(const nt::NetworkTableEntry &entry, py::object defaultValue);
};

View File

@@ -0,0 +1,13 @@
from typing import Sequence, Union
ValueT = Union[
bool,
int,
float,
str,
bytes,
Sequence[bool],
Sequence[int],
Sequence[float],
Sequence[str],
]

View File

@@ -0,0 +1,224 @@
import threading
import weakref
from typing import Callable, Dict, Optional, Sequence
from ._ntcore import NetworkTableInstance, NetworkTableEntry, NetworkTableType, Value
__all__ = ["ntproperty", "ChooserControl"]
class _NtProperty:
"""
Don't use this directly, use @ntproperty instead
"""
entry: NetworkTableEntry
defaultValue: Value
_instlock = threading.Lock()
_instances: Dict[int, weakref.WeakSet] = {}
@classmethod
def attach(cls, self: "_NtProperty", inst: NetworkTableInstance):
with cls._instlock:
handle = inst._getHandle()
props = cls._instances.get(handle)
if not props:
props = weakref.WeakSet()
cls._instances[handle] = props
props.add(self)
@classmethod
def onInstancePostReset(cls, inst: NetworkTableInstance):
with cls._instlock:
props = cls._instances.get(inst._getHandle(), [])
for prop in props:
prop.reset()
@classmethod
def onInstanceDestroy(cls, inst: NetworkTableInstance):
with cls._instlock:
cls._instances.pop(inst._getHandle(), None)
def __init__(
self,
key: str,
defaultValue,
writeDefault: bool,
persistent: bool,
type: Optional[NetworkTableType],
inst: NetworkTableInstance,
) -> None:
# Autodetect the type if not provided, and store the default
# value as that specific type
if type is None:
self.defaultValue = Value.makeValue(defaultValue)
self.mkv = Value.getFactoryByType(self.defaultValue.type())
else:
self.mkv = Value.getFactoryByType(type)
self.defaultValue = self.mkv(defaultValue)
self.key = key
self.writeDefault = writeDefault
self.persistent = persistent
# never overwrite persistent values with defaults
if persistent:
self.writeDefault = False
self.inst = inst
_NtProperty.attach(self, inst)
# Set it up
self.reset()
def reset(self):
self.entry = self.inst.getEntry(self.key)
if self.writeDefault:
print("set v", self.defaultValue)
self.entry.setValue(self.defaultValue)
else:
print("set default", self.defaultValue)
self.entry.setDefaultValue(self.defaultValue)
if self.persistent:
self.entry.setPersistent()
def get(self, _):
return self.entry.value
def set(self, _, value):
self.entry.setValue(self.mkv(value))
def ntproperty(
key: str,
defaultValue,
*,
writeDefault: bool = True,
doc: str = None,
persistent: bool = False,
type: Optional[NetworkTableType] = None,
inst: Optional[NetworkTableInstance] = None
) -> property:
"""
A property that you can add to your classes to access NetworkTables
variables like a normal variable.
:param key: A full NetworkTables key (eg ``/SmartDashboard/foo``)
:param defaultValue: Default value to use if not in the table
:type defaultValue: any
:param writeDefault: If True, put the default value to the table,
overwriting existing values
:param doc: If given, will be the docstring of the property.
:param persistent: If True, persist set values across restarts.
*writeDefault* is ignored if this is True.
:param type: Specify the type of this entry. If not specified,
will autodetect the type from the default value
:param inst: The NetworkTables instance to use.
Example usage::
class Foo(object):
something = ntproperty('/SmartDashboard/something', True)
...
def do_thing(self):
if self.something: # reads from value
...
self.something = False # writes value
.. note:: When using empty lists/tuples, you must explicitly specify
the type.
.. warning::
This function assumes that the value's type
never changes. If it does, you'll get really strange
errors... so don't do that.
"""
if inst is None:
inst = NetworkTableInstance.getDefault()
ntprop = _NtProperty(key, defaultValue, writeDefault, persistent, type, inst)
return property(fget=ntprop.get, fset=ntprop.set, doc=doc)
class ChooserControl:
"""
Interacts with a :class:`wpilib.SendableChooser`
object over NetworkTables.
"""
def __init__(
self,
key: str,
on_choices: Optional[Callable[[Sequence[str]], None]] = None,
on_selected: Optional[Callable[[str], None]] = None,
*,
inst: Optional[NetworkTableInstance] = None
) -> None:
"""
:param key: NetworkTables key
:param on_choices: A function that will be called when the
choices change.
:param on_selection: A function that will be called when the
selection changes.
:param inst: The NetworkTables instance to use.
"""
if inst is None:
inst = NetworkTableInstance.getDefault()
self.subtable = inst.getTable("SmartDashboard").getSubTable(key)
self.on_choices = on_choices
self.on_selected = on_selected
if on_choices or on_selected:
self.subtable.addTableListener(self._on_change, True)
def close(self) -> None:
"""Stops listening for changes to the ``SendableChooser``"""
if self.on_choices or self.on_selected:
self.subtable.removeTableListener(self._on_change)
def getChoices(self) -> Sequence[str]:
"""
Returns the current choices. If the chooser doesn't exist, this
will return an empty tuple.
"""
return self.subtable.getStringArray("options", [])
def getSelected(self) -> Optional[str]:
"""
Returns the current selection or None
"""
selected = self.subtable.getString("selected", None)
if selected is None:
selected = self.subtable.getString("default", None)
return selected
def setSelected(self, selection: str) -> None:
"""
Sets the active selection on the chooser
:param selection: Active selection name
"""
self.subtable.putString("selected", selection)
def _on_change(self, table, key, value, isNew):
if key == "options":
if self.on_choices is not None:
self.on_choices(value)
elif key == "selected":
if self.on_selected is not None:
self.on_selected(value)
elif key == "default":
if (
self.on_selected is not None
and self.subtable.getString("selected", None) is None
):
self.on_selected(value)

View File

@@ -0,0 +1,96 @@
[build-system]
build-backend = "hatchling.build"
requires = [
"semiwrap~=0.1.7",
"hatch-meson~=0.1.0b2",
"hatch-robotpy~=0.2.1",
"hatchling",
"robotpy-native-ntcore==2027.0.0a2",
"robotpy-wpiutil==2027.0.0a2",
"robotpy-wpinet==2027.0.0a2",
"robotpy-wpilog==2027.0.0a2",
]
[project]
name = "pyntcore"
version = "2027.0.0a2"
description = "Binary wrappers for the FRC ntcore library"
authors = [
{name = "RobotPy Development Team", email = "robotpy@googlegroups.com"},
]
license = "BSD-3-Clause"
dependencies = [
"robotpy-native-ntcore==2027.0.0a2",
"robotpy-wpiutil==2027.0.0a2",
"robotpy-wpinet==2027.0.0a2",
"robotpy-wpilog==2027.0.0a2",
]
[project.urls]
"Source code" = "https://github.com/robotpy/mostrobotpy"
[tool.hatch.build.hooks.robotpy]
version_file = "ntcore/version.py"
[tool.hatch.build.hooks.semiwrap]
[tool.hatch.build.hooks.meson]
[tool.hatch.build.targets.wheel]
packages = ["ntcore"]
[tool.semiwrap]
update_init = [
"ntcore",
"ntcore.meta ntcore._ntcore.meta"
]
scan_headers_ignore = [
"networktables/ProtobufTopic.h",
"networktables/UnitTopic.h",
"ntcore.h",
"ntcore_c.h",
"ntcore_c_types.h",
"ntcore_test.h",
"src/*",
]
[tool.semiwrap.extension_modules."ntcore._ntcore"]
name = "ntcore"
wraps = ["robotpy-native-ntcore"]
depends = ["wpiutil", "wpinet", "wpilog"]
[tool.semiwrap.extension_modules."ntcore._ntcore".headers]
# networktables
BooleanArrayTopic = "networktables/BooleanArrayTopic.h"
BooleanTopic = "networktables/BooleanTopic.h"
DoubleArrayTopic = "networktables/DoubleArrayTopic.h"
DoubleTopic = "networktables/DoubleTopic.h"
FloatArrayTopic = "networktables/FloatArrayTopic.h"
FloatTopic = "networktables/FloatTopic.h"
GenericEntry = "networktables/GenericEntry.h"
IntegerArrayTopic = "networktables/IntegerArrayTopic.h"
IntegerTopic = "networktables/IntegerTopic.h"
MultiSubscriber = "networktables/MultiSubscriber.h"
NTSendable = "networktables/NTSendable.h"
NTSendableBuilder = "networktables/NTSendableBuilder.h"
NetworkTable = "networktables/NetworkTable.h"
NetworkTableEntry = "networktables/NetworkTableEntry.h"
NetworkTableInstance = "networktables/NetworkTableInstance.h"
NetworkTableListener = "networktables/NetworkTableListener.h"
NetworkTableType = "networktables/NetworkTableType.h"
NetworkTableValue = "networktables/NetworkTableValue.h"
RawTopic = "networktables/RawTopic.h"
StructTopic = "networktables/StructTopic.h"
StructArrayTopic = "networktables/StructArrayTopic.h"
StringArrayTopic = "networktables/StringArrayTopic.h"
StringTopic = "networktables/StringTopic.h"
Topic = "networktables/Topic.h"
ntcore_cpp = "ntcore_cpp.h"
ntcore_cpp_types = "ntcore_cpp_types.h"

View File

@@ -0,0 +1,115 @@
classes:
nt::BooleanArraySubscriber:
methods:
BooleanArraySubscriber:
overloads:
'':
ignore: true
NT_Subscriber, ParamType:
ignore: true
Get:
overloads:
'[const]':
ParamType [const]:
wpi::SmallVectorImpl<SmallElemType>& [const]:
ignore: true
wpi::SmallVectorImpl<SmallElemType>&, ParamType [const]:
ignore: true
GetAtomic:
overloads:
'[const]':
ParamType [const]:
wpi::SmallVectorImpl<SmallElemType>& [const]:
ignore: true
wpi::SmallVectorImpl<SmallElemType>&, ParamType [const]:
ignore: true
ReadQueue:
GetTopic:
inline_code: |
.def("close", [](BooleanArraySubscriber *self) {
py::gil_scoped_release release;
*self = BooleanArraySubscriber();
}, py::doc("Destroys the subscriber"))
.def("__enter__", [](BooleanArraySubscriber *self) {
return self;
})
.def("__exit__", [](BooleanArraySubscriber *self, py::args args) {
py::gil_scoped_release release;
*self = BooleanArraySubscriber();
})
nt::BooleanArrayPublisher:
methods:
BooleanArrayPublisher:
overloads:
'':
ignore: true
NT_Publisher:
ignore: true
Set:
SetDefault:
GetTopic:
inline_code: |
.def("close", [](BooleanArrayPublisher *self) {
py::gil_scoped_release release;
*self = BooleanArrayPublisher();
}, py::doc("Destroys the publisher"))
.def("__enter__", [](BooleanArrayPublisher *self) {
return self;
})
.def("__exit__", [](BooleanArrayPublisher *self, py::args args) {
py::gil_scoped_release release;
*self = BooleanArrayPublisher();
})
nt::BooleanArrayEntry:
methods:
BooleanArrayEntry:
overloads:
'':
ignore: true
NT_Entry, ParamType:
ignore: true
GetHandle:
ignore: true
GetTopic:
Unpublish:
inline_code: |
.def("close", [](BooleanArrayEntry *self) {
py::gil_scoped_release release;
*self = BooleanArrayEntry();
}, py::doc("Destroys the entry"))
.def("__enter__", [](BooleanArrayEntry *self) {
return self;
})
.def("__exit__", [](BooleanArrayEntry *self, py::args args) {
py::gil_scoped_release release;
*self = BooleanArrayEntry();
})
nt::BooleanArrayTopic:
attributes:
kTypeString:
methods:
BooleanArrayTopic:
overloads:
'':
ignore: true
NT_Topic:
ignore: true
Topic:
Subscribe:
SubscribeEx:
Publish:
PublishEx:
GetEntry:
GetEntryEx:
inline_code: |
.def("close", [](BooleanArrayTopic *self) {
py::gil_scoped_release release;
*self = BooleanArrayTopic();
}, py::doc("Destroys the topic"))
.def("__enter__", [](BooleanArrayTopic *self) {
return self;
})
.def("__exit__", [](BooleanArrayTopic *self, py::args args) {
py::gil_scoped_release release;
*self = BooleanArrayTopic();
})

View File

@@ -0,0 +1,107 @@
classes:
nt::BooleanSubscriber:
methods:
BooleanSubscriber:
overloads:
'':
ignore: true
NT_Subscriber, ParamType:
ignore: true
Get:
overloads:
'[const]':
ParamType [const]:
GetAtomic:
overloads:
'[const]':
ParamType [const]:
ReadQueue:
GetTopic:
inline_code: |
.def("close", [](BooleanSubscriber *self) {
py::gil_scoped_release release;
*self = BooleanSubscriber();
}, py::doc("Destroys the subscriber"))
.def("__enter__", [](BooleanSubscriber *self) {
return self;
})
.def("__exit__", [](BooleanSubscriber *self, py::args args) {
py::gil_scoped_release release;
*self = BooleanSubscriber();
})
nt::BooleanPublisher:
methods:
BooleanPublisher:
overloads:
'':
ignore: true
NT_Publisher:
ignore: true
Set:
SetDefault:
GetTopic:
inline_code: |
.def("close", [](BooleanPublisher *self) {
py::gil_scoped_release release;
*self = BooleanPublisher();
}, py::doc("Destroys the publisher"))
.def("__enter__", [](BooleanPublisher *self) {
return self;
})
.def("__exit__", [](BooleanPublisher *self, py::args args) {
py::gil_scoped_release release;
*self = BooleanPublisher();
})
nt::BooleanEntry:
methods:
BooleanEntry:
overloads:
'':
ignore: true
NT_Entry, ParamType:
ignore: true
GetHandle:
ignore: true
GetTopic:
Unpublish:
inline_code: |
.def("close", [](BooleanEntry *self) {
py::gil_scoped_release release;
*self = BooleanEntry();
}, py::doc("Destroys the entry"))
.def("__enter__", [](BooleanEntry *self) {
return self;
})
.def("__exit__", [](BooleanEntry *self, py::args args) {
py::gil_scoped_release release;
*self = BooleanEntry();
})
nt::BooleanTopic:
attributes:
kTypeString:
methods:
BooleanTopic:
overloads:
'':
ignore: true
NT_Topic:
ignore: true
Topic:
Subscribe:
SubscribeEx:
Publish:
PublishEx:
GetEntry:
GetEntryEx:
inline_code: |-
.def("close", [](BooleanTopic *self) {
py::gil_scoped_release release;
*self = BooleanTopic();
}, py::doc("Destroys the topic"))
.def("__enter__", [](BooleanTopic *self) {
return self;
})
.def("__exit__", [](BooleanTopic *self, py::args args) {
py::gil_scoped_release release;
*self = BooleanTopic();
})

View File

@@ -0,0 +1,115 @@
classes:
nt::DoubleArraySubscriber:
methods:
DoubleArraySubscriber:
overloads:
'':
ignore: true
NT_Subscriber, ParamType:
ignore: true
Get:
overloads:
'[const]':
ParamType [const]:
wpi::SmallVectorImpl<SmallElemType>& [const]:
ignore: true
wpi::SmallVectorImpl<SmallElemType>&, ParamType [const]:
ignore: true
GetAtomic:
overloads:
'[const]':
ParamType [const]:
wpi::SmallVectorImpl<SmallElemType>& [const]:
ignore: true
wpi::SmallVectorImpl<SmallElemType>&, ParamType [const]:
ignore: true
ReadQueue:
GetTopic:
inline_code: |
.def("close", [](DoubleArraySubscriber *self) {
py::gil_scoped_release release;
*self = DoubleArraySubscriber();
}, py::doc("Destroys the subscriber"))
.def("__enter__", [](DoubleArraySubscriber *self) {
return self;
})
.def("__exit__", [](DoubleArraySubscriber *self, py::args args) {
py::gil_scoped_release release;
*self = DoubleArraySubscriber();
})
nt::DoubleArrayPublisher:
methods:
DoubleArrayPublisher:
overloads:
'':
ignore: true
NT_Publisher:
ignore: true
Set:
SetDefault:
GetTopic:
inline_code: |
.def("close", [](DoubleArrayPublisher *self) {
py::gil_scoped_release release;
*self = DoubleArrayPublisher();
}, py::doc("Destroys the publisher"))
.def("__enter__", [](DoubleArrayPublisher *self) {
return self;
})
.def("__exit__", [](DoubleArrayPublisher *self, py::args args) {
py::gil_scoped_release release;
*self = DoubleArrayPublisher();
})
nt::DoubleArrayEntry:
methods:
DoubleArrayEntry:
overloads:
'':
ignore: true
NT_Entry, ParamType:
ignore: true
GetHandle:
ignore: true
GetTopic:
Unpublish:
inline_code: |
.def("close", [](DoubleArrayEntry *self) {
py::gil_scoped_release release;
*self = DoubleArrayEntry();
}, py::doc("Destroys the entry"))
.def("__enter__", [](DoubleArrayEntry *self) {
return self;
})
.def("__exit__", [](DoubleArrayEntry *self, py::args args) {
py::gil_scoped_release release;
*self = DoubleArrayEntry();
})
nt::DoubleArrayTopic:
attributes:
kTypeString:
methods:
DoubleArrayTopic:
overloads:
'':
ignore: true
NT_Topic:
ignore: true
Topic:
Subscribe:
SubscribeEx:
Publish:
PublishEx:
GetEntry:
GetEntryEx:
inline_code: |-
.def("close", [](DoubleArrayTopic *self) {
py::gil_scoped_release release;
*self = DoubleArrayTopic();
}, py::doc("Destroys the topic"))
.def("__enter__", [](DoubleArrayTopic *self) {
return self;
})
.def("__exit__", [](DoubleArrayTopic *self, py::args args) {
py::gil_scoped_release release;
*self = DoubleArrayTopic();
})

View File

@@ -0,0 +1,107 @@
classes:
nt::DoubleSubscriber:
methods:
DoubleSubscriber:
overloads:
'':
ignore: true
NT_Subscriber, ParamType:
ignore: true
Get:
overloads:
'[const]':
ParamType [const]:
GetAtomic:
overloads:
'[const]':
ParamType [const]:
ReadQueue:
GetTopic:
inline_code: |
.def("close", [](DoubleSubscriber *self) {
py::gil_scoped_release release;
*self = DoubleSubscriber();
}, py::doc("Destroys the subscriber"))
.def("__enter__", [](DoubleSubscriber *self) {
return self;
})
.def("__exit__", [](DoubleSubscriber *self, py::args args) {
py::gil_scoped_release release;
*self = DoubleSubscriber();
})
nt::DoublePublisher:
methods:
DoublePublisher:
overloads:
'':
ignore: true
NT_Publisher:
ignore: true
Set:
SetDefault:
GetTopic:
inline_code: |
.def("close", [](DoublePublisher *self) {
py::gil_scoped_release release;
*self = DoublePublisher();
}, py::doc("Destroys the publisher"))
.def("__enter__", [](DoublePublisher *self) {
return self;
})
.def("__exit__", [](DoublePublisher *self, py::args args) {
py::gil_scoped_release release;
*self = DoublePublisher();
})
nt::DoubleEntry:
methods:
DoubleEntry:
overloads:
'':
ignore: true
NT_Entry, ParamType:
ignore: true
GetHandle:
ignore: true
GetTopic:
Unpublish:
inline_code: |
.def("close", [](DoubleEntry *self) {
py::gil_scoped_release release;
*self = DoubleEntry();
}, py::doc("Destroys the entry"))
.def("__enter__", [](DoubleEntry *self) {
return self;
})
.def("__exit__", [](DoubleEntry *self, py::args args) {
py::gil_scoped_release release;
*self = DoubleEntry();
})
nt::DoubleTopic:
attributes:
kTypeString:
methods:
DoubleTopic:
overloads:
'':
ignore: true
NT_Topic:
ignore: true
Topic:
Subscribe:
SubscribeEx:
Publish:
PublishEx:
GetEntry:
GetEntryEx:
inline_code: |
.def("close", [](DoubleTopic *self) {
py::gil_scoped_release release;
*self = DoubleTopic();
}, py::doc("Destroys the topic"))
.def("__enter__", [](DoubleTopic *self) {
return self;
})
.def("__exit__", [](DoubleTopic *self, py::args args) {
py::gil_scoped_release release;
*self = DoubleTopic();
})

View File

@@ -0,0 +1,115 @@
classes:
nt::FloatArraySubscriber:
methods:
FloatArraySubscriber:
overloads:
'':
ignore: true
NT_Subscriber, ParamType:
ignore: true
Get:
overloads:
'[const]':
ParamType [const]:
wpi::SmallVectorImpl<SmallElemType>& [const]:
ignore: true
wpi::SmallVectorImpl<SmallElemType>&, ParamType [const]:
ignore: true
GetAtomic:
overloads:
'[const]':
ParamType [const]:
wpi::SmallVectorImpl<SmallElemType>& [const]:
ignore: true
wpi::SmallVectorImpl<SmallElemType>&, ParamType [const]:
ignore: true
ReadQueue:
GetTopic:
inline_code: |
.def("close", [](FloatArraySubscriber *self) {
py::gil_scoped_release release;
*self = FloatArraySubscriber();
}, py::doc("Destroys the subscriber"))
.def("__enter__", [](FloatArraySubscriber *self) {
return self;
})
.def("__exit__", [](FloatArraySubscriber *self, py::args args) {
py::gil_scoped_release release;
*self = FloatArraySubscriber();
})
nt::FloatArrayPublisher:
methods:
FloatArrayPublisher:
overloads:
'':
ignore: true
NT_Publisher:
ignore: true
Set:
SetDefault:
GetTopic:
inline_code: |
.def("close", [](FloatArrayPublisher *self) {
py::gil_scoped_release release;
*self = FloatArrayPublisher();
}, py::doc("Destroys the publisher"))
.def("__enter__", [](FloatArrayPublisher *self) {
return self;
})
.def("__exit__", [](FloatArrayPublisher *self, py::args args) {
py::gil_scoped_release release;
*self = FloatArrayPublisher();
})
nt::FloatArrayEntry:
methods:
FloatArrayEntry:
overloads:
'':
ignore: true
NT_Entry, ParamType:
ignore: true
GetHandle:
ignore: true
GetTopic:
Unpublish:
inline_code: |
.def("close", [](FloatArrayEntry *self) {
py::gil_scoped_release release;
*self = FloatArrayEntry();
}, py::doc("Destroys the entry"))
.def("__enter__", [](FloatArrayEntry *self) {
return self;
})
.def("__exit__", [](FloatArrayEntry *self, py::args args) {
py::gil_scoped_release release;
*self = FloatArrayEntry();
})
nt::FloatArrayTopic:
attributes:
kTypeString:
methods:
FloatArrayTopic:
overloads:
'':
ignore: true
NT_Topic:
ignore: true
Topic:
Subscribe:
SubscribeEx:
Publish:
PublishEx:
GetEntry:
GetEntryEx:
inline_code: |
.def("close", [](FloatArrayTopic *self) {
py::gil_scoped_release release;
*self = FloatArrayTopic();
}, py::doc("Destroys the topic"))
.def("__enter__", [](FloatArrayTopic *self) {
return self;
})
.def("__exit__", [](FloatArrayTopic *self, py::args args) {
py::gil_scoped_release release;
*self = FloatArrayTopic();
})

View File

@@ -0,0 +1,107 @@
classes:
nt::FloatSubscriber:
methods:
FloatSubscriber:
overloads:
'':
ignore: true
NT_Subscriber, ParamType:
ignore: true
Get:
overloads:
'[const]':
ParamType [const]:
GetAtomic:
overloads:
'[const]':
ParamType [const]:
ReadQueue:
GetTopic:
inline_code: |
.def("close", [](FloatSubscriber *self) {
py::gil_scoped_release release;
*self = FloatSubscriber();
}, py::doc("Destroys the subscriber"))
.def("__enter__", [](FloatSubscriber *self) {
return self;
})
.def("__exit__", [](FloatSubscriber *self, py::args args) {
py::gil_scoped_release release;
*self = FloatSubscriber();
})
nt::FloatPublisher:
methods:
FloatPublisher:
overloads:
'':
ignore: true
NT_Publisher:
ignore: true
Set:
SetDefault:
GetTopic:
inline_code: |
.def("close", [](FloatPublisher *self) {
py::gil_scoped_release release;
*self = FloatPublisher();
}, py::doc("Destroys the publisher"))
.def("__enter__", [](FloatPublisher *self) {
return self;
})
.def("__exit__", [](FloatPublisher *self, py::args args) {
py::gil_scoped_release release;
*self = FloatPublisher();
})
nt::FloatEntry:
methods:
FloatEntry:
overloads:
'':
ignore: true
NT_Entry, ParamType:
ignore: true
GetHandle:
ignore: true
GetTopic:
Unpublish:
inline_code: |
.def("close", [](FloatEntry *self) {
py::gil_scoped_release release;
*self = FloatEntry();
}, py::doc("Destroys the entry"))
.def("__enter__", [](FloatEntry *self) {
return self;
})
.def("__exit__", [](FloatEntry *self, py::args args) {
py::gil_scoped_release release;
*self = FloatEntry();
})
nt::FloatTopic:
attributes:
kTypeString:
methods:
FloatTopic:
overloads:
'':
ignore: true
NT_Topic:
ignore: true
Topic:
Subscribe:
SubscribeEx:
Publish:
PublishEx:
GetEntry:
GetEntryEx:
inline_code: |
.def("close", [](FloatTopic *self) {
py::gil_scoped_release release;
*self = FloatTopic();
}, py::doc("Destroys the topic"))
.def("__enter__", [](FloatTopic *self) {
return self;
})
.def("__exit__", [](FloatTopic *self, py::args args) {
py::gil_scoped_release release;
*self = FloatTopic();
})

View File

@@ -0,0 +1,86 @@
extra_includes:
- src/nt_type_caster.h
classes:
nt::GenericSubscriber:
methods:
GenericSubscriber:
overloads:
'':
ignore: true
NT_Subscriber:
ignore: true
Get:
GetBoolean:
GetInteger:
GetFloat:
GetDouble:
GetString:
GetRaw:
GetBooleanArray:
GetIntegerArray:
GetFloatArray:
GetDoubleArray:
GetStringArray:
ReadQueue:
GetTopic:
nt::GenericPublisher:
methods:
GenericPublisher:
overloads:
'':
ignore: true
NT_Publisher:
ignore: true
Set:
SetBoolean:
SetInteger:
SetFloat:
SetDouble:
SetString:
SetRaw:
SetBooleanArray:
overloads:
std::span<const bool>, int64_t:
std::span<const int>, int64_t:
SetIntegerArray:
SetFloatArray:
SetDoubleArray:
SetStringArray:
SetDefault:
SetDefaultBoolean:
SetDefaultInteger:
SetDefaultFloat:
SetDefaultDouble:
SetDefaultString:
SetDefaultRaw:
SetDefaultBooleanArray:
SetDefaultIntegerArray:
SetDefaultFloatArray:
SetDefaultDoubleArray:
SetDefaultStringArray:
GetTopic:
nt::GenericEntry:
methods:
GenericEntry:
overloads:
'':
ignore: true
NT_Entry:
ignore: true
GetHandle:
ignore: true
GetTopic:
Unpublish:
inline_code: |
.def("close", [](GenericEntry *self) {
py::gil_scoped_release release;
*self = GenericEntry();
}, py::doc("Destroys the entry"))
.def("__enter__", [](GenericEntry *self) {
return self;
})
.def("__exit__", [](GenericEntry *self, py::args args) {
py::gil_scoped_release release;
*self = GenericEntry();
})

View File

@@ -0,0 +1,115 @@
classes:
nt::IntegerArraySubscriber:
methods:
IntegerArraySubscriber:
overloads:
'':
ignore: true
NT_Subscriber, ParamType:
ignore: true
Get:
overloads:
'[const]':
ParamType [const]:
wpi::SmallVectorImpl<SmallElemType>& [const]:
ignore: true
wpi::SmallVectorImpl<SmallElemType>&, ParamType [const]:
ignore: true
GetAtomic:
overloads:
'[const]':
ParamType [const]:
wpi::SmallVectorImpl<SmallElemType>& [const]:
ignore: true
wpi::SmallVectorImpl<SmallElemType>&, ParamType [const]:
ignore: true
ReadQueue:
GetTopic:
inline_code: |
.def("close", [](IntegerArraySubscriber *self) {
py::gil_scoped_release release;
*self = IntegerArraySubscriber();
}, py::doc("Destroys the subscriber"))
.def("__enter__", [](IntegerArraySubscriber *self) {
return self;
})
.def("__exit__", [](IntegerArraySubscriber *self, py::args args) {
py::gil_scoped_release release;
*self = IntegerArraySubscriber();
})
nt::IntegerArrayPublisher:
methods:
IntegerArrayPublisher:
overloads:
'':
ignore: true
NT_Publisher:
ignore: true
Set:
SetDefault:
GetTopic:
inline_code: |
.def("close", [](IntegerArrayPublisher *self) {
py::gil_scoped_release release;
*self = IntegerArrayPublisher();
}, py::doc("Destroys the publisher"))
.def("__enter__", [](IntegerArrayPublisher *self) {
return self;
})
.def("__exit__", [](IntegerArrayPublisher *self, py::args args) {
py::gil_scoped_release release;
*self = IntegerArrayPublisher();
})
nt::IntegerArrayEntry:
methods:
IntegerArrayEntry:
overloads:
'':
ignore: true
NT_Entry, ParamType:
ignore: true
GetHandle:
ignore: true
GetTopic:
Unpublish:
inline_code: |
.def("close", [](IntegerArrayEntry *self) {
py::gil_scoped_release release;
*self = IntegerArrayEntry();
}, py::doc("Destroys the entry"))
.def("__enter__", [](IntegerArrayEntry *self) {
return self;
})
.def("__exit__", [](IntegerArrayEntry *self, py::args args) {
py::gil_scoped_release release;
*self = IntegerArrayEntry();
})
nt::IntegerArrayTopic:
attributes:
kTypeString:
methods:
IntegerArrayTopic:
overloads:
'':
ignore: true
NT_Topic:
ignore: true
Topic:
Subscribe:
SubscribeEx:
Publish:
PublishEx:
GetEntry:
GetEntryEx:
inline_code: |
.def("close", [](IntegerArrayTopic *self) {
py::gil_scoped_release release;
*self = IntegerArrayTopic();
}, py::doc("Destroys the topic"))
.def("__enter__", [](IntegerArrayTopic *self) {
return self;
})
.def("__exit__", [](IntegerArrayTopic *self, py::args args) {
py::gil_scoped_release release;
*self = IntegerArrayTopic();
})

View File

@@ -0,0 +1,107 @@
classes:
nt::IntegerSubscriber:
methods:
IntegerSubscriber:
overloads:
'':
ignore: true
NT_Subscriber, ParamType:
ignore: true
Get:
overloads:
'[const]':
ParamType [const]:
GetAtomic:
overloads:
'[const]':
ParamType [const]:
ReadQueue:
GetTopic:
inline_code: |
.def("close", [](IntegerSubscriber *self) {
py::gil_scoped_release release;
*self = IntegerSubscriber();
}, py::doc("Destroys the subscriber"))
.def("__enter__", [](IntegerSubscriber *self) {
return self;
})
.def("__exit__", [](IntegerSubscriber *self, py::args args) {
py::gil_scoped_release release;
*self = IntegerSubscriber();
})
nt::IntegerPublisher:
methods:
IntegerPublisher:
overloads:
'':
ignore: true
NT_Publisher:
ignore: true
Set:
SetDefault:
GetTopic:
inline_code: |
.def("close", [](IntegerPublisher *self) {
py::gil_scoped_release release;
*self = IntegerPublisher();
}, py::doc("Destroys the publisher"))
.def("__enter__", [](IntegerPublisher *self) {
return self;
})
.def("__exit__", [](IntegerPublisher *self, py::args args) {
py::gil_scoped_release release;
*self = IntegerPublisher();
})
nt::IntegerEntry:
methods:
IntegerEntry:
overloads:
'':
ignore: true
NT_Entry, ParamType:
ignore: true
GetHandle:
ignore: true
GetTopic:
Unpublish:
inline_code: |
.def("close", [](IntegerEntry *self) {
py::gil_scoped_release release;
*self = IntegerEntry();
}, py::doc("Destroys the entry"))
.def("__enter__", [](IntegerEntry *self) {
return self;
})
.def("__exit__", [](IntegerEntry *self, py::args args) {
py::gil_scoped_release release;
*self = IntegerEntry();
})
nt::IntegerTopic:
attributes:
kTypeString:
methods:
IntegerTopic:
overloads:
'':
ignore: true
NT_Topic:
ignore: true
Topic:
Subscribe:
SubscribeEx:
Publish:
PublishEx:
GetEntry:
GetEntryEx:
inline_code: |
.def("close", [](IntegerTopic *self) {
py::gil_scoped_release release;
*self = IntegerTopic();
}, py::doc("Destroys the topic"))
.def("__enter__", [](IntegerTopic *self) {
return self;
})
.def("__exit__", [](IntegerTopic *self, py::args args) {
py::gil_scoped_release release;
*self = IntegerTopic();
})

View File

@@ -0,0 +1,22 @@
classes:
nt::MultiSubscriber:
methods:
MultiSubscriber:
overloads:
'':
ignore: true
NetworkTableInstance, std::span<const std::string_view>, const PubSubOptions&:
GetHandle:
ignore: true
inline_code: |
.def("close", [](MultiSubscriber *self) {
py::gil_scoped_release release;
*self = MultiSubscriber();
}, py::doc("Destroys the subscriber"))
.def("__enter__", [](MultiSubscriber *self) {
return self;
})
.def("__exit__", [](MultiSubscriber *self, py::args args) {
py::gil_scoped_release release;
*self = MultiSubscriber();
})

View File

@@ -0,0 +1,20 @@
extra_includes:
- networktables/NTSendableBuilder.h
classes:
nt::NTSendable:
methods:
InitSendable:
overloads:
NTSendableBuilder&:
virtual_xform: |
[&](py::function fn) {
auto builderHandle = py::cast(builder, py::return_value_policy::reference);
fn(builderHandle);
}
wpi::SendableBuilder&:
virtual_xform: |
[&](py::function fn) {
auto builderHandle = py::cast(builder, py::return_value_policy::reference);
fn(builderHandle);
}

View File

@@ -0,0 +1,15 @@
classes:
nt::NTSendableBuilder:
force_type_casters:
- std::function
typealias:
- BackendKind = wpi::SendableBuilder::BackendKind
methods:
SetUpdateTable:
cpp_code: |
[](NTSendableBuilder *self, std::function<void()> func) {
self->SetUpdateTable(std::move(func));
}
GetTopic:
GetTable:
GetBackendKind:

View File

@@ -0,0 +1,178 @@
extra_includes:
- networktables/BooleanArrayTopic.h
- networktables/BooleanTopic.h
- networktables/DoubleArrayTopic.h
- networktables/DoubleTopic.h
- networktables/FloatArrayTopic.h
- networktables/FloatTopic.h
- networktables/IntegerArrayTopic.h
- networktables/IntegerTopic.h
- networktables/NetworkTableInstance.h
- networktables/RawTopic.h
- networktables/StringArrayTopic.h
- networktables/StringTopic.h
- networktables/StructArrayTopic.h
- networktables/StructTopic.h
- networktables/Topic.h
- src/py2value.h
- src/pyentry.h
- wpystruct.h
classes:
nt::NetworkTable:
attributes:
PATH_SEPARATOR_CHAR:
methods:
BasenameKey:
NormalizeKey:
overloads:
std::string_view, bool:
std::string_view, wpi::SmallVectorImpl<char>&, bool:
ignore: true
GetHierarchy:
NetworkTable:
ignore: true
GetInstance:
GetEntry:
GetTopic:
GetBooleanTopic:
GetIntegerTopic:
GetFloatTopic:
GetDoubleTopic:
GetStringTopic:
GetRawTopic:
GetBooleanArrayTopic:
GetIntegerArrayTopic:
GetFloatArrayTopic:
GetDoubleArrayTopic:
GetStringArrayTopic:
GetProtobufTopic:
ignore: true
GetStructTopic:
param_override:
info:
name: type
cpp_code: |
[](const NetworkTable &self, std::string_view name, const py::type &t) {
WPyStructInfo info(t);
return self.GetStructTopic<WPyStruct, WPyStructInfo>(name, info);
}
GetStructArrayTopic:
param_override:
info:
name: type
cpp_code: |
[](const NetworkTable &self, std::string_view name, const py::type &t) {
WPyStructInfo info(t);
return self.GetStructArrayTopic<WPyStruct, WPyStructInfo>(name, info);
}
GetSubTable:
ContainsKey:
ContainsSubTable:
GetTopicInfo:
GetTopics:
GetKeys:
GetSubTables:
SetPersistent:
ClearPersistent:
IsPersistent:
PutNumber:
SetDefaultNumber:
GetNumber:
cpp_code: |
[](NetworkTable * table, std::string_view key, py::object defaultValue) -> py::object {
nt::NetworkTableEntry entry;
{
py::gil_scoped_release release;
entry = table->GetEntry(key);
}
return pyntcore::GetDoubleEntry(entry, defaultValue);
}
PutString:
SetDefaultString:
GetString:
cpp_code: |
[](NetworkTable * table, std::string_view key, py::object defaultValue) -> py::object {
nt::NetworkTableEntry entry;
{
py::gil_scoped_release release;
entry = table->GetEntry(key);
}
return pyntcore::GetStringEntry(entry, defaultValue);
}
PutBoolean:
SetDefaultBoolean:
GetBoolean:
cpp_code: |
[](NetworkTable * table, std::string_view key, py::object defaultValue) -> py::object {
nt::NetworkTableEntry entry;
{
py::gil_scoped_release release;
entry = table->GetEntry(key);
}
return pyntcore::GetBooleanEntry(entry, defaultValue);
}
PutBooleanArray:
SetDefaultBooleanArray:
GetBooleanArray:
cpp_code: |
[](NetworkTable * table, std::string_view key, py::object defaultValue) -> py::object {
nt::NetworkTableEntry entry;
{
py::gil_scoped_release release;
entry = table->GetEntry(key);
}
return pyntcore::GetBooleanArrayEntry(entry, defaultValue);
}
PutNumberArray:
SetDefaultNumberArray:
GetNumberArray:
cpp_code: |
[](NetworkTable * table, std::string_view key, py::object defaultValue) -> py::object {
nt::NetworkTableEntry entry;
{
py::gil_scoped_release release;
entry = table->GetEntry(key);
}
return pyntcore::GetDoubleArrayEntry(entry, defaultValue);
}
PutStringArray:
SetDefaultStringArray:
GetStringArray:
cpp_code: |
[](NetworkTable * table, std::string_view key, py::object defaultValue) -> py::object {
nt::NetworkTableEntry entry;
{
py::gil_scoped_release release;
entry = table->GetEntry(key);
}
return pyntcore::GetStringArrayEntry(entry, defaultValue);
}
PutRaw:
SetDefaultRaw:
GetRaw:
cpp_code: |
[](NetworkTable * table, std::string_view key, py::object defaultValue) -> py::object {
nt::NetworkTableEntry entry;
{
py::gil_scoped_release release;
entry = table->GetEntry(key);
}
return pyntcore::GetRawEntry(entry, defaultValue);
}
PutValue:
ignore: true
SetDefaultValue:
ignore: true
GetValue:
ignore: true
GetPath:
AddListener:
overloads:
int, TableEventListener:
std::string_view, int, TableEventListener:
AddSubTableListener:
RemoveListener:
inline_code: |
#include <src/NetworkTable.cpp.inl>

View File

@@ -0,0 +1,97 @@
extra_includes:
- networktables/NetworkTableInstance.h
- networktables/Topic.h
- src/py2value.h
- src/pyentry.h
inline_code: |
#include <src/NetworkTableEntry.cpp.inl>
classes:
nt::NetworkTableEntry:
methods:
NetworkTableEntry:
overloads:
'':
ignore: true
NT_Entry:
ignore: true
GetHandle:
ignore: true
GetInstance:
Exists:
GetName:
GetType:
GetLastChange:
GetValue:
GetBoolean:
cpp_code: |
&pyntcore::GetBooleanEntry
GetInteger:
cpp_code: |
&pyntcore::GetIntegerEntry
GetFloat:
cpp_code: |
&pyntcore::GetFloatEntry
GetDouble:
cpp_code: |
&pyntcore::GetDoubleEntry
GetString:
cpp_code: |
&pyntcore::GetStringEntry
GetRaw:
cpp_code: |
&pyntcore::GetRawEntry
GetBooleanArray:
cpp_code: |
&pyntcore::GetBooleanArrayEntry
GetIntegerArray:
cpp_code: |
&pyntcore::GetIntegerArrayEntry
GetFloatArray:
cpp_code: |
&pyntcore::GetFloatArrayEntry
GetDoubleArray:
cpp_code: |
&pyntcore::GetDoubleArrayEntry
GetStringArray:
cpp_code: |
&pyntcore::GetStringArrayEntry
ReadQueue:
SetDefaultValue:
SetDefaultBoolean:
SetDefaultInteger:
SetDefaultFloat:
SetDefaultDouble:
SetDefaultString:
SetDefaultRaw:
SetDefaultBooleanArray:
SetDefaultIntegerArray:
SetDefaultFloatArray:
SetDefaultDoubleArray:
SetDefaultStringArray:
SetValue:
SetBoolean:
SetInteger:
SetFloat:
SetDouble:
SetString:
SetRaw:
SetBooleanArray:
overloads:
std::span<const bool>, int64_t:
std::span<const int>, int64_t:
ignore: true
SetIntegerArray:
SetFloatArray:
SetDoubleArray:
SetStringArray:
SetPersistent:
ClearPersistent:
IsPersistent:
Unpublish:
GetTopic:
operator==:
inline_code: |
.def("__repr__", [](NetworkTableEntry self) {
return py::str("<NetworkTableEntry {!r}>").format(self.GetName());
})

View File

@@ -0,0 +1,181 @@
extra_includes:
- networktables/BooleanArrayTopic.h
- networktables/BooleanTopic.h
- networktables/DoubleArrayTopic.h
- networktables/DoubleTopic.h
- networktables/FloatArrayTopic.h
- networktables/FloatTopic.h
- networktables/IntegerArrayTopic.h
- networktables/IntegerTopic.h
- networktables/MultiSubscriber.h
- networktables/RawTopic.h
- networktables/StringArrayTopic.h
- networktables/StringTopic.h
- networktables/StructArrayTopic.h
- networktables/StructTopic.h
- networktables/Topic.h
- src/py2value.h
- src/nt_instance.h
- wpi/datalog/DataLog.h
- wpystruct.h
classes:
nt::NetworkTableInstance:
force_type_casters:
- std::function
attributes:
kDefaultPort:
enums:
NetworkMode:
arithmetic: true
inline_code: |
.value("kNetModeStarting", (nt::NetworkTableInstance::NetworkMode)NT_NET_MODE_STARTING)
LogLevel:
methods:
NetworkTableInstance:
overloads:
'':
ignore: true
NT_Inst:
ignore: true
GetDefault:
Create:
Destroy:
cpp_code: |
[](NetworkTableInstance * self) {
pyntcore::onInstanceDestroy(self);
py::gil_scoped_release release;
NetworkTableInstance::Destroy(*self);
}
GetHandle:
internal: true
GetTopic:
GetBooleanTopic:
GetIntegerTopic:
GetFloatTopic:
GetDoubleTopic:
GetStringTopic:
GetRawTopic:
GetBooleanArrayTopic:
GetIntegerArrayTopic:
GetFloatArrayTopic:
GetDoubleArrayTopic:
GetStringArrayTopic:
GetProtobufTopic:
ignore: true
GetStructTopic:
param_override:
info:
name: type
cpp_code: |
[](const NetworkTableInstance &self, std::string_view name, const py::type &t) {
WPyStructInfo info(t);
return self.GetStructTopic<WPyStruct, WPyStructInfo>(name, info);
}
GetStructArrayTopic:
param_override:
info:
name: type
cpp_code: |
[](const NetworkTableInstance &self, std::string_view name, const py::type &t) {
WPyStructInfo info(t);
return self.GetStructArrayTopic<WPyStruct, WPyStructInfo>(name, info);
}
GetTopics:
overloads:
'':
std::string_view:
std::string_view, unsigned int:
std::string_view, std::span<std::string_view>:
GetTopicInfo:
overloads:
'':
std::string_view:
std::string_view, unsigned int:
std::string_view, std::span<std::string_view>:
GetEntry:
GetTable:
RemoveListener:
WaitForListenerQueue:
AddConnectionListener:
AddTimeSyncListener:
AddListener:
overloads:
Topic, unsigned int, ListenerCallback:
Subscriber&, unsigned int, ListenerCallback:
MultiSubscriber&, int, ListenerCallback:
const NetworkTableEntry&, int, ListenerCallback:
std::span<const std::string_view>, int, ListenerCallback:
GetNetworkMode:
StartLocal:
cpp_code: |
[](NetworkTableInstance * self) {
pyntcore::onInstanceStart(self);
py::gil_scoped_release release;
self->StartLocal();
}
StopLocal:
StartServer:
cpp_code: |
[](NetworkTableInstance * self, std::string_view persist_filename, const char* listen_address,
unsigned int port) {
pyntcore::onInstanceStart(self);
py::gil_scoped_release release;
self->StartServer(persist_filename, listen_address, port);
}
StopServer:
StopClient:
SetServer:
overloads:
std::string_view, unsigned int:
std::span<const std::pair<std::string_view, unsigned int>>:
std::span<const std::string_view>, unsigned int:
SetServerTeam:
Disconnect:
StartDSClient:
StopDSClient:
FlushLocal:
Flush:
GetConnections:
IsConnected:
GetServerTimeOffset:
StartEntryDataLog:
StopEntryDataLog:
StartConnectionDataLog:
StopConnectionDataLog:
AddLogger:
HasSchema:
AddSchema:
overloads:
std::string_view, std::string_view, std::span<const uint8_t>:
std::string_view, std::string_view, std::string_view:
AddProtobufSchema:
ignore: true
AddStructSchema:
ignore: true
operator==:
StartClient:
inline_code: |
.def("configPythonLogging", [](NetworkTableInstance * self,
NetworkTableInstance::LogLevel minLevel, NetworkTableInstance::LogLevel maxLevel, py::str logName) {
py::module::import("ntcore._logutil").attr("_config_logging")(self, minLevel, maxLevel, logName);
}, py::kw_only(),
py::arg("min") = NetworkTableInstance::LogLevel::kLogInfo,
py::arg("max") = NetworkTableInstance::LogLevel::kLogCritical,
py::arg("name") = "nt",
py::doc("Configure python logging for this instance.\n"
"\n"
":param min: Minimum NT level to log\n"
":param max: Maximum NT level to log\n"
":param name: Name of python logger\n"
"\n"
".. note:: This must be called before the instance is started")
)
.def("_reset", [](NetworkTableInstance *self) {
pyntcore::onInstancePreReset(self);
{
py::gil_scoped_release release;
nt::ResetInstance(self->GetHandle());
}
pyntcore::onInstancePostReset(self);
})

View File

@@ -0,0 +1,63 @@
classes:
nt::NetworkTableListener:
force_type_casters:
- std::function
methods:
NetworkTableListener:
overloads:
'':
CreateListener:
overloads:
NetworkTableInstance, std::span<const std::string_view>, unsigned int, ListenerCallback:
Topic, unsigned int, ListenerCallback:
Subscriber&, unsigned int, ListenerCallback:
MultiSubscriber&, unsigned int, ListenerCallback:
NetworkTableEntry&, unsigned int, ListenerCallback:
CreateConnectionListener:
CreateTimeSyncListener:
CreateLogger:
GetHandle:
WaitForQueue:
inline_code: |
.def("close", [](NetworkTableListener *self) {
py::gil_scoped_release release;
*self = NetworkTableListener();
}, py::doc("Destroys the listener"))
.def("__enter__", [](NetworkTableListener *self) {
return self;
})
.def("__exit__", [](NetworkTableListener *self, py::args args) {
py::gil_scoped_release release;
*self = NetworkTableListener();
})
nt::NetworkTableListenerPoller:
methods:
NetworkTableListenerPoller:
overloads:
'':
NetworkTableInstance:
GetHandle:
AddListener:
overloads:
std::span<const std::string_view>, unsigned int:
Topic, unsigned int:
Subscriber&, unsigned int:
MultiSubscriber&, unsigned int:
NetworkTableEntry&, unsigned int:
AddConnectionListener:
AddTimeSyncListener:
AddLogger:
RemoveListener:
ReadQueue:
inline_code: |
.def("close", [](NetworkTableListenerPoller *self) {
py::gil_scoped_release release;
*self = NetworkTableListenerPoller();
}, py::doc("Destroys the poller"))
.def("__enter__", [](NetworkTableListenerPoller *self) {
return self;
})
.def("__exit__", [](NetworkTableListenerPoller *self, py::args args) {
py::gil_scoped_release release;
*self = NetworkTableListenerPoller();
})

View File

@@ -0,0 +1,2 @@
enums:
NetworkTableType:

View File

@@ -0,0 +1,184 @@
extra_includes:
- networktables/NetworkTableType.h
- src/py2value.h
functions:
Now:
ignore: true
classes:
nt::Value:
methods:
Value:
overloads:
'':
ignore: true
NT_Type, size_t, int64_t, const private_init&:
ignore: true
NT_Type, size_t, int64_t, int64_t, const private_init&:
ignore: true
type:
cpp_code: |
[](Value *t) {
return (NetworkTableType)t->type();
}
value:
cpp_code: |
[](const Value &self) -> py::object {
return pyntcore::ntvalue2py(self);
}
last_change:
time:
size:
SetTime:
server_time:
SetServerTime:
IsValid:
IsBoolean:
IsInteger:
IsFloat:
IsDouble:
IsString:
IsRaw:
IsBooleanArray:
IsIntegerArray:
IsFloatArray:
IsDoubleArray:
IsStringArray:
GetBoolean:
cpp_code: |
[](Value *self) {
pyntcore::ensure_value_is(NT_BOOLEAN, self);
return self->GetBoolean();
}
GetInteger:
cpp_code: |
[](Value *self) {
pyntcore::ensure_value_is(NT_INTEGER, self);
return self->GetInteger();
}
GetFloat:
cpp_code: |
[](Value *self) {
pyntcore::ensure_value_is(NT_FLOAT, self);
return self->GetFloat();
}
GetDouble:
cpp_code: |
[](Value *self) {
pyntcore::ensure_value_is(NT_DOUBLE, self);
return self->GetDouble();
}
GetString:
cpp_code: |
[](Value *self) {
pyntcore::ensure_value_is(NT_STRING, self);
return self->GetString();
}
GetRaw:
cpp_code: |
[](Value *self) {
pyntcore::ensure_value_is(NT_RAW, self);
return self->GetRaw();
}
GetBooleanArray:
cpp_code: |
[](Value *self) -> py::object {
pyntcore::ensure_value_is(NT_BOOLEAN_ARRAY, self);
auto v = self->value();
py::list l(v.data.arr_boolean.size);
for (size_t i = 0; i < v.data.arr_boolean.size; i++) {
auto b = py::bool_(v.data.arr_boolean.arr[i]);
PyList_SET_ITEM(l.ptr(), i, b.release().ptr());
}
return std::move(l);
}
GetIntegerArray:
cpp_code: |
[](Value *self) {
pyntcore::ensure_value_is(NT_INTEGER_ARRAY, self);
return self->GetIntegerArray();
}
GetFloatArray:
cpp_code: |
[](Value *self) {
pyntcore::ensure_value_is(NT_FLOAT_ARRAY, self);
return self->GetFloatArray();
}
GetDoubleArray:
cpp_code: |
[](Value *self) {
pyntcore::ensure_value_is(NT_DOUBLE_ARRAY, self);
return self->GetDoubleArray();
}
GetStringArray:
cpp_code: |
[](Value *self) {
pyntcore::ensure_value_is(NT_STRING_ARRAY, self);
return self->GetStringArray();
}
MakeBoolean:
MakeInteger:
MakeFloat:
MakeDouble:
MakeString:
overloads:
std::string_view, int64_t:
T&&, int64_t:
ignore: true
MakeRaw:
overloads:
std::span<const uint8_t>, int64_t:
T&&, int64_t:
ignore: true
MakeBooleanArray:
overloads:
std::span<const bool>, int64_t:
std::initializer_list<bool>, int64_t:
ignore: true
std::span<const int>, int64_t:
ignore: true
std::initializer_list<int>, int64_t:
ignore: true
std::vector<int>&&, int64_t:
ignore: true
MakeIntegerArray:
overloads:
std::span<const int64_t>, int64_t:
ignore: true
std::initializer_list<int64_t>, int64_t:
ignore: true
std::vector<int64_t>&&, int64_t:
MakeFloatArray:
overloads:
std::span<const float>, int64_t:
ignore: true
std::initializer_list<float>, int64_t:
ignore: true
std::vector<float>&&, int64_t:
MakeDoubleArray:
overloads:
std::span<const double>, int64_t:
ignore: true
std::initializer_list<double>, int64_t:
ignore: true
std::vector<double>&&, int64_t:
MakeStringArray:
overloads:
std::span<const std::string>, int64_t:
ignore: true
std::initializer_list<std::string>, int64_t:
ignore: true
std::vector<std::string>&&, int64_t:
inline_code: |-
.def_static("makeValue", [](py::handle value) {
return pyntcore::py2ntvalue(value);
}, py::arg("value"))
.def_static("getFactoryByType", [](nt::NetworkTableType type) {
return pyntcore::valueFactoryByType(type);
}, py::arg("type"))
.def("__repr__", [](const Value &self) -> py::str {
auto typestr = pyntcore::nttype2str(self.type());
auto valrepr = py::repr(pyntcore::ntvalue2py(self));
return py::str("<Value type={} value={}>").format(typestr, valrepr);
})

View File

@@ -0,0 +1,114 @@
extra_includes:
- src/nt_type_caster.h
classes:
nt::RawSubscriber:
methods:
RawSubscriber:
overloads:
'':
ignore: true
NT_Subscriber, ParamType:
ignore: true
Get:
overloads:
'[const]':
ParamType [const]:
wpi::SmallVectorImpl<SmallElemType>& [const]:
ignore: true
wpi::SmallVectorImpl<SmallElemType>&, ParamType [const]:
ignore: true
GetAtomic:
overloads:
'[const]':
ParamType [const]:
wpi::SmallVectorImpl<SmallElemType>& [const]:
ignore: true
wpi::SmallVectorImpl<SmallElemType>&, ParamType [const]:
ignore: true
ReadQueue:
GetTopic:
inline_code: |
.def("close", [](RawSubscriber *self) {
py::gil_scoped_release release;
*self = RawSubscriber();
}, py::doc("Destroys the subscriber"))
.def("__enter__", [](RawSubscriber *self) {
return self;
})
.def("__exit__", [](RawSubscriber *self, py::args args) {
py::gil_scoped_release release;
*self = RawSubscriber();
})
nt::RawPublisher:
methods:
RawPublisher:
overloads:
'':
ignore: true
NT_Publisher:
ignore: true
Set:
SetDefault:
GetTopic:
inline_code: |
.def("close", [](RawPublisher *self) {
py::gil_scoped_release release;
*self = RawPublisher();
}, py::doc("Destroys the publisher"))
.def("__enter__", [](RawPublisher *self) {
return self;
})
.def("__exit__", [](RawPublisher *self, py::args args) {
py::gil_scoped_release release;
*self = RawPublisher();
})
nt::RawEntry:
methods:
RawEntry:
overloads:
'':
ignore: true
NT_Entry, ParamType:
ignore: true
GetHandle:
ignore: true
GetTopic:
Unpublish:
inline_code: |
.def("close", [](RawEntry *self) {
py::gil_scoped_release release;
*self = RawEntry();
}, py::doc("Destroys the entry"))
.def("__enter__", [](RawEntry *self) {
return self;
})
.def("__exit__", [](RawEntry *self, py::args args) {
py::gil_scoped_release release;
*self = RawEntry();
})
nt::RawTopic:
methods:
RawTopic:
overloads:
'':
ignore: true
NT_Topic:
ignore: true
Topic:
Subscribe:
Publish:
PublishEx:
GetEntry:
inline_code: |
.def("close", [](RawTopic *self) {
py::gil_scoped_release release;
*self = RawTopic();
}, py::doc("Destroys the topic"))
.def("__enter__", [](RawTopic *self) {
return self;
})
.def("__exit__", [](RawTopic *self, py::args args) {
py::gil_scoped_release release;
*self = RawTopic();
})

View File

@@ -0,0 +1,107 @@
classes:
nt::StringArraySubscriber:
methods:
StringArraySubscriber:
overloads:
'':
ignore: true
NT_Subscriber, ParamType:
ignore: true
Get:
overloads:
'[const]':
ParamType [const]:
GetAtomic:
overloads:
'[const]':
ParamType [const]:
ReadQueue:
GetTopic:
inline_code: |
.def("close", [](StringArraySubscriber *self) {
py::gil_scoped_release release;
*self = StringArraySubscriber();
}, py::doc("Destroys the subscriber"))
.def("__enter__", [](StringArraySubscriber *self) {
return self;
})
.def("__exit__", [](StringArraySubscriber *self, py::args args) {
py::gil_scoped_release release;
*self = StringArraySubscriber();
})
nt::StringArrayPublisher:
methods:
StringArrayPublisher:
overloads:
'':
ignore: true
NT_Publisher:
ignore: true
Set:
SetDefault:
GetTopic:
inline_code: |
.def("close", [](StringArrayPublisher *self) {
py::gil_scoped_release release;
*self = StringArrayPublisher();
}, py::doc("Destroys the publisher"))
.def("__enter__", [](StringArrayPublisher *self) {
return self;
})
.def("__exit__", [](StringArrayPublisher *self, py::args args) {
py::gil_scoped_release release;
*self = StringArrayPublisher();
})
nt::StringArrayEntry:
methods:
StringArrayEntry:
overloads:
'':
ignore: true
NT_Entry, ParamType:
ignore: true
GetHandle:
ignore: true
GetTopic:
Unpublish:
inline_code: |
.def("close", [](StringArrayEntry *self) {
py::gil_scoped_release release;
*self = StringArrayEntry();
}, py::doc("Destroys the entry"))
.def("__enter__", [](StringArrayEntry *self) {
return self;
})
.def("__exit__", [](StringArrayEntry *self, py::args args) {
py::gil_scoped_release release;
*self = StringArrayEntry();
})
nt::StringArrayTopic:
attributes:
kTypeString:
methods:
StringArrayTopic:
overloads:
'':
ignore: true
NT_Topic:
ignore: true
Topic:
Subscribe:
SubscribeEx:
Publish:
PublishEx:
GetEntry:
GetEntryEx:
inline_code: |
.def("close", [](StringArrayTopic *self) {
py::gil_scoped_release release;
*self = StringArrayTopic();
}, py::doc("Destroys the topic"))
.def("__enter__", [](StringArrayTopic *self) {
return self;
})
.def("__exit__", [](StringArrayTopic *self, py::args args) {
py::gil_scoped_release release;
*self = StringArrayTopic();
})

View File

@@ -0,0 +1,115 @@
classes:
nt::StringSubscriber:
methods:
StringSubscriber:
overloads:
'':
ignore: true
NT_Subscriber, ParamType:
ignore: true
Get:
overloads:
'[const]':
ParamType [const]:
wpi::SmallVectorImpl<SmallElemType>& [const]:
ignore: true
wpi::SmallVectorImpl<SmallElemType>&, ParamType [const]:
ignore: true
GetAtomic:
overloads:
'[const]':
ParamType [const]:
wpi::SmallVectorImpl<SmallElemType>& [const]:
ignore: true
wpi::SmallVectorImpl<SmallElemType>&, ParamType [const]:
ignore: true
ReadQueue:
GetTopic:
inline_code: |
.def("close", [](StringSubscriber *self) {
py::gil_scoped_release release;
*self = StringSubscriber();
}, py::doc("Destroys the subscriber"))
.def("__enter__", [](StringSubscriber *self) {
return self;
})
.def("__exit__", [](StringSubscriber *self, py::args args) {
py::gil_scoped_release release;
*self = StringSubscriber();
})
nt::StringPublisher:
methods:
StringPublisher:
overloads:
'':
ignore: true
NT_Publisher:
ignore: true
Set:
SetDefault:
GetTopic:
inline_code: |
.def("close", [](StringPublisher *self) {
py::gil_scoped_release release;
*self = StringPublisher();
}, py::doc("Destroys the publisher"))
.def("__enter__", [](StringPublisher *self) {
return self;
})
.def("__exit__", [](StringPublisher *self, py::args args) {
py::gil_scoped_release release;
*self = StringPublisher();
})
nt::StringEntry:
methods:
StringEntry:
overloads:
'':
ignore: true
NT_Entry, ParamType:
ignore: true
GetHandle:
ignore: true
GetTopic:
Unpublish:
inline_code: |
.def("close", [](StringEntry *self) {
py::gil_scoped_release release;
*self = StringEntry();
}, py::doc("Destroys the entry"))
.def("__enter__", [](StringEntry *self) {
return self;
})
.def("__exit__", [](StringEntry *self, py::args args) {
py::gil_scoped_release release;
*self = StringEntry();
})
nt::StringTopic:
attributes:
kTypeString:
methods:
StringTopic:
overloads:
'':
ignore: true
NT_Topic:
ignore: true
Topic:
Subscribe:
SubscribeEx:
Publish:
PublishEx:
GetEntry:
GetEntryEx:
inline_code: |
.def("close", [](StringTopic *self) {
py::gil_scoped_release release;
*self = StringTopic();
}, py::doc("Destroys the topic"))
.def("__enter__", [](StringTopic *self) {
return self;
})
.def("__exit__", [](StringTopic *self, py::args args) {
py::gil_scoped_release release;
*self = StringTopic();
})

View File

@@ -0,0 +1,167 @@
classes:
nt::StructArraySubscriber:
template_params:
- T
- I
methods:
StructArraySubscriber:
overloads:
'':
ignore: true
NT_Subscriber, U&&, I...:
ignore: true
Get:
overloads:
'[const]':
U&& [const]:
ignore: true
std::span<const T> [const]:
GetAtomic:
overloads:
'[const]':
U&& [const]:
ignore: true
std::span<const T> [const]:
ReadQueue:
GetTopic:
inline_code: |
.def("close", [](nt::StructArraySubscriber<T, I> *self) {
py::gil_scoped_release release;
*self = nt::StructArraySubscriber<T, I>();
}, py::doc("Destroys the subscriber"))
.def("__enter__", [](nt::StructArraySubscriber<T, I> *self) {
return self;
})
.def("__exit__", [](nt::StructArraySubscriber<T, I> *self, py::args args) {
py::gil_scoped_release release;
*self = nt::StructArraySubscriber<T, I>();
})
nt::StructArrayPublisher:
template_params:
- T
- I
methods:
StructArrayPublisher:
overloads:
'':
ignore: true
NT_Publisher, I...:
ignore: true
Set:
overloads:
U&&, int64_t:
ignore: true
std::span<const T>, int64_t:
SetDefault:
overloads:
U&&:
ignore: true
std::span<const T>:
GetTopic:
inline_code: |
.def("close", [](nt::StructArrayPublisher<T, I> *self) {
py::gil_scoped_release release;
*self = nt::StructArrayPublisher<T, I>();
}, py::doc("Destroys the publisher"))
.def("__enter__", [](nt::StructArrayPublisher<T, I> *self) {
return self;
})
.def("__exit__", [](nt::StructArrayPublisher<T, I> *self, py::args args) {
py::gil_scoped_release release;
*self = nt::StructArrayPublisher<T, I>();
})
nt::StructArrayEntry:
template_params:
- T
- I
base_qualnames:
StructArraySubscriber: nt::StructArraySubscriber<T, I>
StructArrayPublisher: nt::StructArrayPublisher<T, I>
methods:
StructArrayEntry:
overloads:
'':
ignore: true
NT_Entry, U&&, const I&...:
ignore: true
GetHandle:
ignore: true
GetTopic:
Unpublish:
inline_code: |
.def("close", [](nt::StructArrayEntry<T, I> *self) {
py::gil_scoped_release release;
*self = nt::StructArrayEntry<T, I>();
}, py::doc("Destroys the entry"))
.def("__enter__", [](nt::StructArrayEntry<T, I> *self) {
return self;
})
.def("__exit__", [](nt::StructArrayEntry<T, I> *self, py::args args) {
py::gil_scoped_release release;
*self = nt::StructArrayEntry<T, I>();
})
nt::StructArrayTopic:
template_params:
- T
- I
methods:
StructArrayTopic:
overloads:
'':
ignore: true
NT_Topic, I...:
ignore: true
Topic, I...:
param_override:
info:
name: type
cpp_code: |
[](Topic topic, const py::type &t) {
WPyStructInfo info(t);
return nt::StructArrayTopic<T, I>(topic, info);
}
Subscribe:
overloads:
U&&, const PubSubOptions&:
ignore: true
std::span<const T>, const PubSubOptions&:
Publish:
PublishEx:
GetEntry:
overloads:
U&&, const PubSubOptions&:
ignore: true
std::span<const T>, const PubSubOptions&:
inline_code: |
.def("close", [](nt::StructArrayTopic<T, I> *self) {
py::gil_scoped_release release;
*self = nt::StructArrayTopic<T, I>();
}, py::doc("Destroys the topic"))
.def("__enter__", [](nt::StructArrayTopic<T, I> *self) {
return self;
})
.def("__exit__", [](nt::StructArrayTopic<T, I> *self, py::args args) {
py::gil_scoped_release release;
*self = nt::StructArrayTopic<T, I>();
})
templates:
StructArraySubscriber:
qualname: nt::StructArraySubscriber
params:
- WPyStruct
- WPyStructInfo
StructArrayPublisher:
qualname: nt::StructArrayPublisher
params:
- WPyStruct
- WPyStructInfo
StructArrayEntry:
qualname: nt::StructArrayEntry
params:
- WPyStruct
- WPyStructInfo
StructArrayTopic:
qualname: nt::StructArrayTopic
params:
- WPyStruct
- WPyStructInfo

View File

@@ -0,0 +1,149 @@
classes:
nt::StructSubscriber:
template_params:
- T
- I
methods:
StructSubscriber:
overloads:
'':
ignore: true
NT_Subscriber, T, I...:
ignore: true
Get:
overloads:
'[const]':
const T& [const]:
GetInto:
ignore: true
GetAtomic:
overloads:
'[const]':
const T& [const]:
ReadQueue:
GetTopic:
inline_code: |
.def("close", [](nt::StructSubscriber<T, I> *self) {
py::gil_scoped_release release;
*self = nt::StructSubscriber<T, I>();
}, py::doc("Destroys the subscriber"))
.def("__enter__", [](nt::StructSubscriber<T, I> *self) {
return self;
})
.def("__exit__", [](nt::StructSubscriber<T, I> *self, py::args args) {
py::gil_scoped_release release;
*self = nt::StructSubscriber<T, I>();
})
nt::StructPublisher:
template_params:
- T
- I
methods:
StructPublisher:
overloads:
'':
ignore: true
NT_Publisher, I...:
ignore: true
Set:
SetDefault:
GetTopic:
inline_code: |
.def("close", [](nt::StructPublisher<T, I> *self) {
py::gil_scoped_release release;
*self = nt::StructPublisher<T, I>();
}, py::doc("Destroys the publisher"))
.def("__enter__", [](nt::StructPublisher<T, I> *self) {
return self;
})
.def("__exit__", [](nt::StructPublisher<T, I> *self, py::args args) {
py::gil_scoped_release release;
*self = nt::StructPublisher<T, I>();
})
nt::StructEntry:
template_params:
- T
- I
base_qualnames:
StructSubscriber: nt::StructSubscriber<T, I>
StructPublisher: nt::StructPublisher<T, I>
methods:
StructEntry:
overloads:
'':
ignore: true
NT_Entry, T, const I&...:
ignore: true
GetHandle:
ignore: true
GetTopic:
Unpublish:
inline_code: |
.def("close", [](nt::StructEntry<T, I> *self) {
py::gil_scoped_release release;
*self = nt::StructEntry<T, I>();
}, py::doc("Destroys the entry"))
.def("__enter__", [](nt::StructEntry<T, I> *self) {
return self;
})
.def("__exit__", [](nt::StructEntry<T, I> *self, py::args args) {
py::gil_scoped_release release;
*self = nt::StructEntry<T, I>();
})
nt::StructTopic:
template_params:
- T
- I
methods:
StructTopic:
overloads:
'':
ignore: true
NT_Topic, I...:
ignore: true
Topic, I...:
param_override:
info:
name: type
cpp_code: |
[](Topic topic, const py::type &t) {
WPyStructInfo info(t);
return nt::StructTopic<T, I>(topic, info);
}
Subscribe:
Publish:
PublishEx:
GetEntry:
inline_code: |
.def("close", [](nt::StructTopic<T, I> *self) {
py::gil_scoped_release release;
*self = nt::StructTopic<T, I>();
}, py::doc("Destroys the topic"))
.def("__enter__", [](nt::StructTopic<T, I> *self) {
return self;
})
.def("__exit__", [](nt::StructTopic<T, I> *self, py::args args) {
py::gil_scoped_release release;
*self = nt::StructTopic<T, I>();
})
templates:
StructSubscriber:
qualname: nt::StructSubscriber
params:
- WPyStruct
- WPyStructInfo
StructPublisher:
qualname: nt::StructPublisher
params:
- WPyStruct
- WPyStructInfo
StructEntry:
qualname: nt::StructEntry
params:
- WPyStruct
- WPyStructInfo
StructTopic:
qualname: nt::StructTopic
params:
- WPyStruct
- WPyStructInfo

View File

@@ -0,0 +1,89 @@
extra_includes:
- networktables/GenericEntry.h
- networktables/NetworkTableInstance.h
classes:
nt::Topic:
methods:
Topic:
overloads:
'':
ignore: true
NT_Topic:
ignore: true
GetHandle:
ignore: true
GetInstance:
GetName:
GetType:
GetTypeString:
SetPersistent:
IsPersistent:
SetRetained:
IsRetained:
SetCached:
IsCached:
Exists:
GetProperty:
SetProperty:
DeleteProperty:
GetProperties:
SetProperties:
GetInfo:
GenericSubscribe:
overloads:
const PubSubOptions&:
std::string_view, const PubSubOptions&:
GenericPublish:
GenericPublishEx:
GetGenericEntry:
overloads:
const PubSubOptions&:
std::string_view, const PubSubOptions&:
operator==:
inline_code: |
.def("__repr__", [](py::handle self) {
py::object type_name = self.get_type().attr("__qualname__");
std::string name = self.cast<Topic>().GetName();
return py::str("<{} {!r}>").format(type_name, name);
})
nt::Subscriber:
attributes:
m_subHandle:
methods:
GetHandle:
ignore: true
Exists:
GetLastChange:
GetTopic:
Subscriber:
overloads:
'':
ignore: true
NT_Subscriber:
ignore: true
inline_code: |
.def("__repr__", [](py::handle self) {
py::object type_name = self.get_type().attr("__qualname__");
auto topic = self.cast<const Subscriber&>().GetTopic();
return py::str("<{} {!r}>").format(type_name, topic.GetName());
})
nt::Publisher:
attributes:
m_pubHandle:
methods:
GetHandle:
ignore: true
GetTopic:
Publisher:
overloads:
'':
ignore: true
NT_Publisher:
ignore: true
inline_code: |
.def("__repr__", [](py::handle self) {
py::object type_name = self.get_type().attr("__qualname__");
auto topic = self.cast<const Publisher&>().GetTopic();
return py::str("<{} {!r}>").format(type_name, topic.GetName());
})

View File

@@ -0,0 +1,317 @@
defaults:
ignore: true
report_ignored_missing: false
extra_includes:
- pybind11/stl.h
- networktables/Topic.h
functions:
RemoveListener:
internal: true
Now:
internal: true
SetNow:
internal: true
AddPolledLogger:
internal: true
HasSchema:
ignore: true
DecodeTopicPublishers:
subpackage: meta
DecodeTopicSubscribers:
subpackage: meta
DecodeClientPublishers:
subpackage: meta
DecodeClientSubscribers:
subpackage: meta
DecodeClients:
subpackage: meta
classes:
nt::EventFlags:
attributes:
kNone:
kImmediate:
kConnected:
kDisconnected:
kConnection:
kPublish:
kUnpublish:
kProperties:
kTopic:
kValueRemote:
kValueLocal:
kValueAll:
kLogMessage:
kTimeSync:
nt::TopicInfo:
attributes:
name:
type_str:
properties:
topic:
ignore: true
type:
ignore: true
methods:
GetProperties:
inline_code: |
.def_property_readonly("topic", [](const TopicInfo &self) {
return std::make_shared<nt::Topic>(self.topic);
})
.def_property_readonly("type", [](const TopicInfo &self) {
return nt::NetworkTableType(self.type);
})
.def("__repr__", [](const TopicInfo &self) -> py::str {
return py::str("<TopicInfo name={} type={}>")
.format(self.name, self.type_str);
})
nt::ConnectionInfo:
attributes:
remote_id:
remote_ip:
remote_port:
last_update:
protocol_version:
inline_code: |
.def("__repr__", [](const ConnectionInfo &self) -> py::str {
return py::str("<ConnectionInfo id={} addr={}:{} last_update={} protocol={}>")
.format(self.remote_id, self.remote_ip, self.remote_port,
self.last_update, self.protocol_version);
})
nt::ValueEventData:
attributes:
topic:
ignore: true
subentry:
ignore: true
value:
methods:
ValueEventData:
overloads:
'':
ignore: true
NT_Topic, NT_Handle, Value:
ignore: true
inline_code: |
.def_property_readonly("topic", [](const ValueEventData &self) {
return std::make_shared<nt::Topic>(self.topic);
})
.def("__repr__", [](const ValueEventData &self) -> py::str {
auto topicInfo = nt::GetTopicInfo(self.topic);
return py::str("<ValueEventData topic_name=\"{}\" topic_type={} value={}>")
.format(topicInfo.name, topicInfo.type_str, self.value);
})
nt::LogMessage:
attributes:
level:
filename:
line:
message:
methods:
LogMessage:
overloads:
'':
ignore: true
unsigned int, std::string_view, unsigned int, std::string_view:
ignore: true
nt::TimeSyncEventData:
attributes:
serverTimeOffset:
rtt2:
valid:
methods:
TimeSyncEventData:
overloads:
'':
ignore: true
int64_t, int64_t, bool:
nt::Event:
attributes:
listener:
flags:
data:
ignore: true
methods:
Event:
overloads:
'':
ignore: true
NT_Listener, unsigned int, ConnectionInfo:
ignore: true
NT_Listener, unsigned int, TopicInfo:
ignore: true
NT_Listener, unsigned int, ValueEventData:
ignore: true
NT_Listener, unsigned int, LogMessage:
ignore: true
NT_Listener, unsigned int, NT_Topic, NT_Handle, Value:
ignore: true
NT_Listener, unsigned int, unsigned int, std::string_view, unsigned int, std::string_view:
ignore: true
NT_Listener, unsigned int, int64_t, int64_t, bool:
ignore: true
Is:
rename: is_
GetConnectionInfo:
overloads:
'[const]':
ignore: true
'':
ignore: true
GetTopicInfo:
overloads:
'[const]':
ignore: true
'':
ignore: true
GetValueEventData:
overloads:
'[const]':
ignore: true
'':
ignore: true
GetLogMessage:
overloads:
'[const]':
ignore: true
'':
ignore: true
GetTimeSyncEventData:
overloads:
'[const]':
ignore: true
'':
ignore: true
inline_code: |
.def_property_readonly("data", [](nt::Event *self){
return self->data;
})
.def("__repr__", [](const nt::Event &self) -> py::str {
return py::str("<Event listener={} flags={} data={}>")
.format(self.listener, self.flags, self.data);
})
nt::PubSubOptions:
force_no_default_constructor: true
attributes:
kDefaultPeriodic:
structSize:
ignore: true
pollStorage:
periodic:
excludePublisher:
sendAll:
topicsOnly:
keepDuplicates:
prefixMatch:
disableRemote:
disableLocal:
excludeSelf:
hidden:
inline_code: |
// autogenerated by gen-pubsub.py
.def(py::init([](
unsigned int pollStorage,
double periodic,
std::optional<std::shared_ptr<nt::Publisher>> excludePublisher,
bool sendAll,
bool topicsOnly,
bool keepDuplicates,
bool prefixMatch,
bool disableRemote,
bool disableLocal,
bool excludeSelf,
bool hidden
) -> nt::PubSubOptions {
return nt::PubSubOptions{
.pollStorage = pollStorage,
.periodic = periodic,
.excludePublisher = excludePublisher.has_value() ? excludePublisher.value()->GetHandle() : 0,
.sendAll = sendAll,
.topicsOnly = topicsOnly,
.keepDuplicates = keepDuplicates,
.prefixMatch = prefixMatch,
.disableRemote = disableRemote,
.disableLocal = disableLocal,
.excludeSelf = excludeSelf,
.hidden = hidden
};
}),
py::kw_only(),
py::arg("pollStorage") = 0,
py::arg("periodic") = nt::PubSubOptions::kDefaultPeriodic,
py::arg("excludePublisher") = std::nullopt,
py::arg("sendAll") = false,
py::arg("topicsOnly") = false,
py::arg("keepDuplicates") = false,
py::arg("prefixMatch") = false,
py::arg("disableRemote") = false,
py::arg("disableLocal") = false,
py::arg("excludeSelf") = false,
py::arg("hidden") = false,
R"(
:param pollStorage: 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.
:param periodic: 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.
:param excludePublisher: For subscriptions, if non-zero, value updates for ReadQueue() are not
queued for this publisher.
:param sendAll: Send all value changes over the network.
:param topicsOnly: For subscriptions, don't ask for value changes (only topic announcements).
:param keepDuplicates: Preserve duplicate value changes (rather than ignoring them).
:param prefixMatch: 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.
:param disableRemote: For subscriptions, if remote value updates should not be queued for
ReadQueue(). See also disableLocal.
:param disableLocal: For subscriptions, if local value updates should not be queued for
ReadQueue(). See also disableRemote.
:param excludeSelf: For entries, don't queue (for ReadQueue) value updates for the entry's
internal publisher.
:param hidden: For subscriptions, don't share the existence of the subscription with the
network. Note this means updates will not be received from the network
unless another subscription overlaps with this one, and the subscription
will not appear in metatopics.
)"
)
nt::meta::SubscriberOptions:
subpackage: meta
attributes:
periodic:
topicsOnly:
sendAll:
prefixMatch:
nt::meta::TopicPublisher:
subpackage: meta
attributes:
client:
pubuid:
nt::meta::TopicSubscriber:
subpackage: meta
attributes:
client:
subuid:
options:
nt::meta::ClientPublisher:
subpackage: meta
attributes:
uid:
topic:
nt::meta::ClientSubscriber:
subpackage: meta
attributes:
uid:
topics:
options:
nt::meta::Client:
subpackage: meta
attributes:
id:
conn:
version:

View File

@@ -0,0 +1,82 @@
extra_includes:
- src/nt_type_caster.h
defaults:
ignore: true
report_ignored_missing: false
classes:
nt::Timestamped:
template_params:
- T
attributes:
time:
serverTime:
value:
methods:
Timestamped:
overloads:
'':
int64_t, int64_t, T:
inline_code: |
;
// capture class name by value
auto clsname = clsName;
cls_Timestamped.def("__repr__", [clsname](const Timestamped<T>& self) -> py::str {
return py::str("{}(time={}, serverTime={}, value={!r})")
.format(clsname, self.time, self.serverTime, self.value);
})
templates:
TimestampedBoolean:
qualname: nt::Timestamped
params:
- bool
TimestampedInteger:
qualname: nt::Timestamped
params:
- int64_t
TimestampedFloat:
qualname: nt::Timestamped
params:
- float
TimestampedDouble:
qualname: nt::Timestamped
params:
- double
TimestampedString:
qualname: nt::Timestamped
params:
- std::string
TimestampedRaw:
qualname: nt::Timestamped
params:
- std::vector<uint8_t>
TimestampedBooleanArray:
qualname: nt::Timestamped
params:
- std::vector<int>
TimestampedIntegerArray:
qualname: nt::Timestamped
params:
- std::vector<int64_t>
TimestampedFloatArray:
qualname: nt::Timestamped
params:
- std::vector<float>
TimestampedDoubleArray:
qualname: nt::Timestamped
params:
- std::vector<double>
TimestampedStringArray:
qualname: nt::Timestamped
params:
- std::vector<std::string>
TimestampedStruct:
qualname: nt::Timestamped
params:
- WPyStruct
TimestampedStructArray:
qualname: nt::Timestamped
params:
- std::vector<WPyStruct>

View File

@@ -0,0 +1,165 @@
#
# Useful fixtures
#
from contextlib import contextmanager
from threading import Condition
import logging
logger = logging.getLogger("conftest")
import pytest
from ntcore import NetworkTableInstance, MultiSubscriber, Event, EventFlags
#
# Fixtures for a usable in-memory version of networktables
#
@pytest.fixture
def cfg_logging(caplog):
caplog.set_level(logging.INFO)
@pytest.fixture(scope="function")
def nt(cfg_logging):
instance = NetworkTableInstance.create()
instance.startLocal()
try:
yield instance
finally:
NetworkTableInstance.destroy(instance)
#
# Live NT instance fixtures
#
class NtTestBase:
"""
Object for managing a live pair of NT server/client
"""
_wait_lock = None
def __init__(self):
self.reset()
def reset(self):
self._impl = NetworkTableInstance.create()
self.getTable = self._impl.getTable
self.isConnected = self._impl.isConnected
def shutdown(self):
logger.info("shutting down %s", self.__class__.__name__)
if self._impl:
NetworkTableInstance.destroy(self._impl)
self._impl = None
# def disconnect(self):
# self._api.dispatcher.stop()
def _init_common(self):
# This resets the instance to be independent
self.shutdown()
self.reset()
# self._wait_init()
def _init_server(self, port=23232):
self._init_common()
self.port = port
def _init_client(self):
self._init_common()
def _wait_init(self):
logger.info("wait-init %s", self.__class__.__name__)
self._wait_lock = Condition()
self._wait = 0
self.msub = MultiSubscriber(self._impl, [""])
self.vl = self._impl.addListener(
self.msub, EventFlags.kValueRemote, self._wait_cb
)
def _wait_cb(self, evt: Event):
logger.info("wait-callback %s: %s", self.__class__.__name__, evt)
with self._wait_lock:
self._wait += 1
self._wait_lock.notify()
@contextmanager
def expect_changes(self, count):
"""Use this on the *other* instance that you're making
changes on, to wait for the changes to propagate to the
other instance"""
if self._wait_lock is None:
self._wait_init()
with self._wait_lock:
self._wait = 0
logger.info("Begin actions")
yield
logger.info("Waiting for %s changes", count)
with self._wait_lock:
self._wait_lock.wait_for(lambda: self._wait >= count, 4)
logger.info("expect_changes: %s == %s", self._wait, count)
msg = "Failed waiting for exactly %s changes (got %s)" % (count, self._wait)
assert self._wait == count, msg
@pytest.fixture()
def nt_server(request, cfg_logging):
class NtServer(NtTestBase):
_test_saved_port = None
def start_test(self):
logger.info("NtServer::start_test")
# Restore server port on restart
if self._test_saved_port is not None:
self.port = self._test_saved_port
self._impl.startServer(listen_address="127.0.0.1", port=self.port)
self._test_saved_port = self.port
server = NtServer()
server._init_server()
try:
yield server
finally:
server.shutdown()
@pytest.fixture()
def nt_client(request, nt_server):
class NtClient(NtTestBase):
def start_test(self):
self._impl.startClient("C4")
self._impl.setServer("127.0.0.1", nt_server.port)
client = NtClient()
client._init_client()
yield client
client.shutdown()
@pytest.fixture
def nt_live(nt_server, nt_client):
"""This fixture automatically starts the client and server"""
nt_server.start_test()
nt_client.start_test()
return nt_server, nt_client

View File

@@ -0,0 +1,155 @@
#
# These tests stand up a separate client and server instance of
# networktables and tests the 'real' user API to ensure that it
# works correctly
#
from __future__ import print_function
import pytest
import logging
logger = logging.getLogger("test")
# test defaults
def doc(nt):
t = nt.getTable("nope")
assert t.getBoolean("b", None) is None
assert t.getNumber("n", None) is None
assert t.getString("s", None) is None
assert t.getBooleanArray("ba", None) is None
assert t.getNumberArray("na", None) is None
assert t.getStringArray("sa", None) is None
assert t.getValue("v", None) is None
assert t.getBoolean("b", True) is True
assert t.getNumber("n", 1) == 1
assert t.getString("s", "sss") == "sss"
assert t.getBooleanArray("ba", (True,)) == (True,)
assert t.getNumberArray("na", (1,)) == (1,)
assert t.getStringArray("sa", ("ss",)) == ("ss",)
assert t.getValue("v", "vvv") == "vvv"
def do(nt1, nt2, t):
t1 = nt1.getTable(t)
with nt2.expect_changes(8):
t1.putBoolean("bool", True)
t1.putNumber("number1", 1)
t1.putNumber("number2", 1.5)
t1.putString("string", "string")
t1.putString("unicode", "\xa9") # copyright symbol
t1.putBooleanArray("ba", (True, False))
t1.putNumberArray("na", (1, 2))
t1.putStringArray("sa", ("s", "t"))
logger.info("put is done")
t2 = nt2.getTable(t)
assert t2.getBoolean("bool", None) is True
assert t2.getNumber("number1", None) == 1
assert t2.getNumber("number2", None) == 1.5
assert t2.getString("string", None) == "string"
assert t2.getString("unicode", None) == "\xa9" # copyright symbol
assert t2.getBooleanArray("ba", None) == [True, False]
assert t2.getNumberArray("na", None) == [1, 2]
assert t2.getStringArray("sa", None) == ["s", "t"]
# Value testing
with nt2.expect_changes(6):
t1.putValue("v_b", False)
t1.putValue("v_n1", 2)
t1.putValue("v_n2", 2.5)
t1.putValue("v_s", "ssss")
t1.putValue("v_s2", "\xa9")
t1.putValue("v_v", 0)
print(t2.getKeys())
assert t2.getBoolean("v_b", None) is False
assert t2.getNumber("v_n1", None) == 2
assert t2.getNumber("v_n2", None) == 2.5
assert t2.getString("v_s", None) == "ssss"
assert t2.getString("v_s2", None) == "\xa9"
assert t2.getValue("v_v", None) == 0
# Ensure that updating values work!
with nt2.expect_changes(8):
t1.putBoolean("bool", False)
t1.putNumber("number1", 2)
t1.putNumber("number2", 2.5)
t1.putString("string", "sss")
t1.putString("unicode", "\u2122") # (tm)
t1.putBooleanArray("ba", (False, True, False))
t1.putNumberArray("na", (2, 1))
t1.putStringArray("sa", ("t", "s"))
t2 = nt2.getTable(t)
assert t2.getBoolean("bool", None) is False
assert t2.getNumber("number1", None) == 2
assert t2.getNumber("number2", None) == 2.5
assert t2.getString("string", None) == "sss"
assert t2.getString("unicode", None) == "\u2122"
assert t2.getBooleanArray("ba", None) == [False, True, False]
assert t2.getNumberArray("na", None) == [2, 1]
assert t2.getStringArray("sa", None) == ["t", "s"]
@pytest.mark.xfail(reason="ntcore is broken")
def test_basic(nt_live):
nt_server, nt_client = nt_live
# assert nt_server.isServer()
# assert not nt_client.isServer()
doc(nt_client)
doc(nt_server)
# server -> client
do(nt_server, nt_client, "server2client")
# client -> server
do(nt_client, nt_server, "client2server")
assert nt_client.isConnected()
assert nt_server.isConnected()
# def test_reconnect(nt_live):
# nt_server, nt_client = nt_live
# with nt_server.expect_changes(1):
# ct = nt_client.getTable("t")
# ct.putBoolean("foo", True)
# st = nt_server.getTable("t")
# assert st.getBoolean("foo", None) == True
# # Client disconnect testing
# nt_client.shutdown()
# logger.info("Shutdown the client")
# with nt_client.expect_changes(1):
# nt_client.start_test()
# ct = nt_client.getTable("t")
# assert ct.getBoolean("foo", None) == True
# # Server disconnect testing
# nt_server.shutdown()
# logger.info("Shutdown the server")
# # synchronization change: if the client doesn't touch the entry locally,
# # then it won't get transferred back to the server on reconnect. Touch
# # it here to ensure that it comes back
# ct.putBoolean("foo", True)
# with nt_server.expect_changes(1):
# nt_server.start_test()
# st = nt_server.getTable("t")
# assert st.getBoolean("foo", None) == True

View File

@@ -0,0 +1,71 @@
#
# Ensure that the NetworkTableEntry objects work
#
def test_entry_repr(nt):
e = nt.getEntry("/k1")
assert repr(e) == "<NetworkTableEntry '/k1'>"
def test_topic_repr(nt):
topic = nt.getIntegerTopic("/int")
assert repr(topic) == "<IntegerTopic '/int'>"
pub = topic.publish()
assert repr(pub) == "<IntegerPublisher '/int'>"
entry = topic.getEntry(0)
assert repr(entry) == "<IntegerEntry '/int'>"
generic_entry = topic.getGenericEntry()
assert repr(generic_entry) == "<GenericEntry '/int'>"
def test_entry_string(nt):
e = nt.getEntry("/k1")
assert e.getString(None) is None
e.setString("value")
assert e.getString(None) == "value"
assert e.getValue().value() == "value"
assert e.value == "value"
e.unpublish()
assert e.getString(None) is None
e.setString("value")
assert e.getString(None) == "value"
def test_entry_string_array(nt):
e = nt.getEntry("/k1")
assert e.getStringArray(None) is None
e.setStringArray(["value"])
assert e.getStringArray(None) == ["value"]
assert e.getValue().value() == ["value"]
assert e.value == ["value"]
e.unpublish()
assert e.getStringArray(None) is None
e.setStringArray(["value"])
assert e.getStringArray(None) == ["value"]
def test_entry_persistence(nt):
e = nt.getEntry("/k2")
for _ in range(2):
assert not e.isPersistent()
# persistent flag cannot be set unless the entry has a value
e.setString("value")
assert not e.isPersistent()
e.setPersistent()
assert e.isPersistent()
e.clearPersistent()
assert not e.isPersistent()
e.unpublish()
def test_entry_publish_empty_double_array(nt):
topic = nt.getDoubleArrayTopic("/Topic")
pub = topic.publish()
pub.set([])

View File

@@ -0,0 +1,176 @@
#
# These tests are leftover from the original pynetworktables tests
#
import pytest
@pytest.fixture(scope="function")
def table1(nt):
return nt.getTable("/test1")
@pytest.fixture(scope="function")
def table2(nt):
return nt.getTable("/test2")
def test_put_double(table1):
table1.putNumber("double", 42.42)
assert table1.getNumber("double", None) == 42.42
assert table1.getNumber("Non-Existant", 44.44) == 44.44
def test_put_boolean(table1):
table1.putBoolean("boolean", True)
assert table1.getBoolean("boolean", None) == True
assert table1.getBoolean("Non-Existant", False) == False
def test_put_string(table1):
table1.putString("String", "Test 1")
assert table1.getString("String", None) == "Test 1"
assert table1.getString("Non-Existant", "Test 3") == "Test 3"
def test_getvalue_overloads(table1):
table1.putValue("float", 35.5)
assert table1.getValue("float", None) == pytest.approx(35.5)
table1.putValue("integer", 950)
assert table1.getValue("integer", None) == pytest.approx(950)
table1.putValue("boolean", True)
assert table1.getValue("boolean", None) is True
def test_multi_data_type(table1):
table1.putNumber("double1", 1)
table1.putNumber("double2", 2)
table1.putNumber("double3", 3)
table1.putBoolean("bool1", False)
table1.putBoolean("bool2", True)
table1.putString("string1", "String 1")
table1.putString("string2", "String 2")
table1.putString("string3", "String 3")
assert table1.getNumber("double1", None) == 1
assert table1.getNumber("double2", None) == 2
assert table1.getNumber("double3", None) == 3
assert table1.getBoolean("bool1", None) == False
assert table1.getBoolean("bool2", None) == True
assert table1.getString("string1", None) == "String 1"
assert table1.getString("string2", None) == "String 2"
assert table1.getString("string3", None) == "String 3"
table1.putNumber("double1", 4)
table1.putNumber("double2", 5)
table1.putNumber("double3", 6)
table1.putBoolean("bool1", True)
table1.putBoolean("bool2", False)
table1.putString("string1", "String 4")
table1.putString("string2", "String 5")
table1.putString("string3", "String 6")
assert table1.getNumber("double1", None) == 4
assert table1.getNumber("double2", None) == 5
assert table1.getNumber("double3", None) == 6
assert table1.getBoolean("bool1", None) == True
assert table1.getBoolean("bool2", None) == False
assert table1.getString("string1", None) == "String 4"
assert table1.getString("string2", None) == "String 5"
assert table1.getString("string3", None) == "String 6"
def test_multi_table(table1, table2):
table1.putNumber("table1double", 1)
table1.putBoolean("table1boolean", True)
table1.putString("table1string", "Table 1")
assert table2.getNumber("table1double", None) == None
assert table2.getBoolean("table1boolean", None) == None
assert table2.getString("table1string", None) == None
table2.putNumber("table2double", 2)
table2.putBoolean("table2boolean", False)
table2.putString("table2string", "Table 2")
assert table1.getNumber("table2double", None) == None
assert table1.getBoolean("table2boolean", None) == None
assert table1.getString("table2string", None) == None
# def test_get_table(nt, table1, table2):
# assert nt.getTable("test1") is table1
# assert nt.getTable("test2") is table2
# assert nt.getTable("/test1") is table1
# assert nt.getTable("/test2") is table2
# assert nt.getTable("/test1/") is table1
# assert nt.getTable("/test1/").path == "/test1"
# assert table1 is not table2
# table3 = nt.getTable("/test3")
# assert table1 is not table3
# assert table2 is not table3
# def test_get_subtable(nt, table1):
# assert not table1.containsSubTable("test1")
# st1 = table1.getSubTable("test1")
# assert nt.getTable("/test1/test1") is st1
# assert table1.getSubTable("test1") is st1
# # weird, but true -- subtable only exists when key exists
# assert not table1.containsSubTable("test1")
# st1.putBoolean("hi", True)
# assert table1.containsSubTable("test1")
# assert table1.getSubTables() == ["test1"]
# assert st1.getSubTables() == []
def test_getkeys(table1):
assert table1.getKeys() == []
assert not table1.containsKey("hi")
assert "hi" not in table1
table1.putBoolean("hi", True)
assert table1.getKeys() == ["hi"]
assert table1.containsKey("hi")
assert "hi" in table1
def test_flags(table1):
table1.putBoolean("foo", True)
assert not table1.isPersistent("foo")
table1.setPersistent("foo")
assert table1.isPersistent("foo")
table1.clearPersistent("foo")
assert not table1.isPersistent("foo")
# def test_delete(table1):
# table1.putBoolean("foo", True)
# assert table1.getBoolean("foo", None) == True
# table1.delete("foo")
# assert table1.getBoolean("foo", None) == None
def test_different_type(table1):
assert table1.putBoolean("foo", True)
assert table1.getBoolean("foo", None) == True
assert not table1.putNumber("foo", 1)
assert table1.getBoolean("foo", None) == True

View File

@@ -0,0 +1,55 @@
import dataclasses
import ntcore
from wpiutil import wpistruct
@wpistruct.make_wpistruct
@dataclasses.dataclass
class MyStruct:
x: int
y: bool
z: wpistruct.double
def test_mystruct(nt: ntcore.NetworkTableInstance):
topic = nt.getStructTopic("mystruct", MyStruct)
pub = topic.publish()
sub = topic.subscribe(MyStruct(1, True, 2.0))
assert topic.getTypeString() == "struct:MyStruct"
default = MyStruct(6, False, 7.0)
pub.setDefault(default)
val = sub.get()
assert val == default
# val2 = MyStruct(0, False, 0)
# sub.getInto(val2)
# assert val2 == default
vals = sub.readQueue()
assert len(vals) == 1
assert vals[0].value == default
def test_mystruct_array(nt: ntcore.NetworkTableInstance):
topic = nt.getStructArrayTopic("mystruct", MyStruct)
pub = topic.publish()
sub = topic.subscribe([])
assert topic.getTypeString() == "struct:MyStruct[]"
default = MyStruct(8, True, 9.0)
pub.setDefault([default])
val = sub.get()
assert val == [default]
val2 = MyStruct(0, False, 0)
pub.set([val2])
atomicVal = sub.getAtomic()
assert atomicVal.value == [val2]
vals = sub.readQueue()
assert len(vals) == 1
assert vals[0].value == [val2]

View File

@@ -0,0 +1,153 @@
import pytest
from ntcore import NetworkTableInstance, NetworkTableType
from ntcore.util import ntproperty, ChooserControl
# def test_autoupdatevalue(nt):
# # tricksy: make sure that this works *before* initialization
# # of network tables happens!
# nt.shutdown()
# foo = nt.getGlobalAutoUpdateValue("/SmartDashboard/foo", True, True)
# assert foo.value == True
# assert foo.get() == True
# nt.startTestMode()
# assert foo.value == True
# assert foo.get() == True
# t = nt.getTable("/SmartDashboard")
# assert t.getBoolean("foo", None) == True
# t.putBoolean("foo", False)
# assert foo.value == False
def test_ntproperty(nt: NetworkTableInstance):
class Foo(object):
robotTime = ntproperty(
"/SmartDashboard/robotTime", 0.0, writeDefault=False, inst=nt
)
dsTime = ntproperty("/SmartDashboard/dsTime", 0.0, writeDefault=True, inst=nt)
testIntArray = ntproperty(
"/SmartDashboard/testIntArray", [1, 2, 3], writeDefault=True, inst=nt
)
testBoolArray = ntproperty(
"/SmartDashboard/testBoolArray", [True, False], writeDefault=True, inst=nt
)
testFloatArray = ntproperty(
"/SmartDashboard/testFloatArray",
[1.1, 1.2, 1.3],
writeDefault=True,
type=NetworkTableType.kFloatArray,
inst=nt,
)
f = Foo()
t = nt.getTable("/SmartDashboard")
assert f.robotTime == 0
assert t.getNumber("robotTime", None) == 0
f.robotTime = 2
assert t.getNumber("robotTime", None) == 2
t.putNumber("robotTime", 4)
assert f.robotTime == 4
assert f.testIntArray == [1, 2, 3]
f.testIntArray = [4, 5, 6]
assert f.testIntArray == [4, 5, 6]
assert f.testBoolArray == [True, False]
f.testBoolArray = [False, True]
assert f.testBoolArray == [False, True]
assert f.testFloatArray == [
pytest.approx(1.1),
pytest.approx(1.2),
pytest.approx(1.3),
]
f.testFloatArray = [4.1, 5.1, 6.1]
assert f.testFloatArray == [
pytest.approx(4.1),
pytest.approx(5.1),
pytest.approx(6.1),
]
def test_ntproperty_emptyarray(nt: NetworkTableInstance):
with pytest.raises(TypeError):
class Foo1(object):
testArray = ntproperty(
"/SmartDashboard/testArray", [], writeDefault=True, inst=nt
)
with pytest.raises(TypeError):
class Foo2(object):
testArray = ntproperty(
"/SmartDashboard/testArray", [], writeDefault=False, inst=nt
)
def test_ntproperty_multitest(nt: NetworkTableInstance):
"""
Checks to see that ntproperties still work between NT restarts
This is needed to ensure that ntproperty gets reset between
pyfrc tests
"""
class Foo(object):
robotTime = ntproperty(
"/SmartDashboard/robotTime", 0.0, writeDefault=False, inst=nt
)
dsTime = ntproperty("/SmartDashboard/dsTime", 0.0, writeDefault=True, inst=nt)
for i in range(3):
print("Iteration", i)
f = Foo()
t = nt.getTable("/SmartDashboard")
assert f.robotTime == 0
assert f.dsTime == 0
assert t.getNumber("robotTime", None) == 0
assert t.getNumber("dsTime", None) == 0
f.robotTime = 2
assert t.getNumber("robotTime", None) == 2
assert t.getNumber("dsTime", None) == 0
t.putNumber("robotTime", 4)
assert f.robotTime == 4
assert f.dsTime == 0
nt.stopLocal()
nt._reset()
nt.startLocal()
def test_chooser_control(nt: NetworkTableInstance):
c = ChooserControl("Autonomous Mode", inst=nt)
assert c.getChoices() == []
assert c.getSelected() is None
c.setSelected("foo")
assert c.getSelected() == "foo"
t = nt.getTable("/SmartDashboard/Autonomous Mode")
assert t.getString("selected", None) == "foo"
t.putStringArray("options", ("option1", "option2"))
assert c.getChoices() == ["option1", "option2"]

View File

@@ -0,0 +1,196 @@
import ntcore
import pytest
def test_value_bool():
v = ntcore.Value.makeBoolean(True)
assert v.getBoolean() == True
assert v.value() == True
def test_mkvalue_bool():
v = ntcore.Value.makeValue(True)
assert v.getBoolean() == True
assert v.value() == True
def test_bool_factory():
f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kBoolean)
v = f(True)
assert v.getBoolean() == True
def test_value_int():
v = ntcore.Value.makeInteger(2)
assert v.getInteger() == 2
assert v.value() == 2
def test_mkvalue_int():
v = ntcore.Value.makeValue(2)
assert v.getInteger() == 2
assert v.value() == 2
def test_int_factory():
f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kInteger)
v = f(2)
assert v.getInteger() == 2
def test_value_double():
v = ntcore.Value.makeDouble(2.5)
assert v.getDouble() == pytest.approx(2.5)
assert v.value() == pytest.approx(2.5)
def test_mkvalue_double():
v = ntcore.Value.makeValue(2.5)
assert v.getDouble() == pytest.approx(2.5)
assert v.value() == pytest.approx(2.5)
def test_double_factory():
f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kDouble)
v = f(2.2)
assert v.getDouble() == pytest.approx(2.2)
def test_value_float():
v = ntcore.Value.makeFloat(2.5)
assert v.getFloat() == pytest.approx(2.5)
assert v.value() == pytest.approx(2.5)
def test_mkvalue_float():
pass # makeValue cannot create a float value
def test_float_factory():
f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kFloat)
v = f(2.2)
assert v.getFloat() == pytest.approx(2.2)
def test_value_str():
v = ntcore.Value.makeString("s")
assert v.getString() == "s"
assert v.value() == "s"
def test_mkvalue_str():
v = ntcore.Value.makeValue("s")
assert v.getString() == "s"
assert v.value() == "s"
def test_str_factory():
f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kString)
v = f("abc")
assert v.getString() == "abc"
def test_value_raw():
v = ntcore.Value.makeRaw(b"raw")
assert v.getRaw() == b"raw"
assert v.value() == b"raw"
def test_mkvalue_raw():
v = ntcore.Value.makeValue(b"raw")
assert v.getRaw() == b"raw"
assert v.value() == b"raw"
def test_raw_factory():
f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kRaw)
v = f(b"raw")
assert v.getRaw() == b"raw"
def test_value_bool_list():
v = ntcore.Value.makeBooleanArray([True, False])
assert v.getBooleanArray() == [True, False]
assert v.value() == [True, False]
def test_mkvalue_bool_list():
v = ntcore.Value.makeValue([True, False])
assert v.getBooleanArray() == [True, False]
assert v.value() == [True, False]
def test_bool_list_factory():
f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kBooleanArray)
v = f([True, False])
assert v.getBooleanArray() == [True, False]
def test_value_int_list():
v = ntcore.Value.makeIntegerArray([1, 2])
assert v.getIntegerArray() == [1, 2]
assert v.value() == [1, 2]
def test_mkvalue_int_list():
v = ntcore.Value.makeValue([1, 2])
assert v.getIntegerArray() == [1, 2]
assert v.value() == [1, 2]
def test_int_list_factory():
f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kIntegerArray)
v = f([1, 2])
assert v.getIntegerArray() == [1, 2]
def test_value_double_list():
v = ntcore.Value.makeDoubleArray([1.1, 2.2])
assert v.getDoubleArray() == [pytest.approx(1.1), pytest.approx(2.2)]
assert v.value() == [pytest.approx(1.1), pytest.approx(2.2)]
def test_mkvalue_double_list():
v = ntcore.Value.makeValue([1.1, 2.2])
assert v.getDoubleArray() == [pytest.approx(1.1), pytest.approx(2.2)]
assert v.value() == [pytest.approx(1.1), pytest.approx(2.2)]
def test_double_list_factory():
f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kDoubleArray)
v = f([1.1, 2.2])
assert v.getDoubleArray() == [pytest.approx(1.1), pytest.approx(2.2)]
def test_value_float_list():
v = ntcore.Value.makeFloatArray([1.1, 2.2])
assert v.getFloatArray() == [pytest.approx(1.1), pytest.approx(2.2)]
assert v.value() == [pytest.approx(1.1), pytest.approx(2.2)]
def test_mkvalue_float_list():
pass # not possible to use makeValue to make a float list
def test_float_list_factory():
f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kFloatArray)
v = f([1.1, 2.2])
assert v.getFloatArray() == [pytest.approx(1.1), pytest.approx(2.2)]
def test_value_str_list():
v = ntcore.Value.makeStringArray(["a", "b"])
assert v.getStringArray() == ["a", "b"]
assert v.value() == ["a", "b"]
def test_mkvalue_str_list():
v = ntcore.Value.makeValue(["a", "b"])
assert v.getStringArray() == ["a", "b"]
assert v.value() == ["a", "b"]
def test_str_list_factory():
f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kStringArray)
v = f(["a", "b"])
assert v.getStringArray() == ["a", "b"]