mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-01 02:41:48 +00:00
Compare commits
39 Commits
v2023.1.1-
...
v2023.1.1-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b114006543 | ||
|
|
32fbfb7da6 | ||
|
|
02465920fb | ||
|
|
3a5a376465 | ||
|
|
1c3c86e9f1 | ||
|
|
dcda09f90a | ||
|
|
66157397c1 | ||
|
|
9e22ffbebf | ||
|
|
648ab6115c | ||
|
|
8bc3b04f5b | ||
|
|
cfb84a6083 | ||
|
|
02c47726e1 | ||
|
|
b2a0093294 | ||
|
|
2a98d6b5d7 | ||
|
|
9f36301dc8 | ||
|
|
901fc555f4 | ||
|
|
4170ec6107 | ||
|
|
fe400f68c5 | ||
|
|
794669b346 | ||
|
|
dcfa85a5d5 | ||
|
|
15ad855f1d | ||
|
|
11244a49d9 | ||
|
|
1d2e8eb153 | ||
|
|
ad53fb19b4 | ||
|
|
ba850bac3b | ||
|
|
023a5989f8 | ||
|
|
c970011ccc | ||
|
|
07a43c3d9a | ||
|
|
a05b212b04 | ||
|
|
09faf31b67 | ||
|
|
9e1f9c1133 | ||
|
|
f19d2b9b84 | ||
|
|
a28f93863c | ||
|
|
c9f61669b8 | ||
|
|
dcce5ad3b3 | ||
|
|
6836e5923d | ||
|
|
335188c652 | ||
|
|
60a29dcb99 | ||
|
|
b55d5b3034 |
2
.github/workflows/cmake.yml
vendored
2
.github/workflows/cmake.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
|
||||
- name: Set up Python 3.8 (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.8
|
||||
|
||||
|
||||
5
.github/workflows/documentation.yml
vendored
5
.github/workflows/documentation.yml
vendored
@@ -20,8 +20,9 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-java@v1
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 13
|
||||
- name: Install libclang-9
|
||||
run: sudo apt update && sudo apt install -y libclang-cpp9 libclang1-9
|
||||
@@ -42,7 +43,7 @@ jobs:
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew docs:generateJavaDocs docs:doxygen -PbuildServer ${{ env.EXTRA_GRADLE_ARGS }}
|
||||
- name: Install SSH Client 🔑
|
||||
uses: webfactory/ssh-agent@v0.4.1
|
||||
uses: webfactory/ssh-agent@v0.7.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.GH_DEPLOY_KEY }}
|
||||
- name: Deploy Java 🚀
|
||||
|
||||
19
.github/workflows/gradle.yml
vendored
19
.github/workflows/gradle.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
||||
env:
|
||||
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.artifact-name }}
|
||||
path: build/allOutputs
|
||||
@@ -80,8 +80,9 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v1
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 11
|
||||
architecture: ${{ matrix.architecture }}
|
||||
- name: Import Developer ID Certificate
|
||||
@@ -115,7 +116,7 @@ jobs:
|
||||
if: |
|
||||
matrix.artifact-name == 'macOS' && (github.repository_owner == 'wpilibsuite' &&
|
||||
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.artifact-name }}
|
||||
path: ${{ matrix.outputs }}
|
||||
@@ -127,8 +128,9 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v1
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 13
|
||||
- name: Install libclang-9
|
||||
run: sudo apt update && sudo apt install -y libclang-cpp9 libclang1-9
|
||||
@@ -140,7 +142,7 @@ jobs:
|
||||
env:
|
||||
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Documentation
|
||||
path: docs/build/outputs
|
||||
@@ -153,7 +155,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: wpilibsuite/build-tools
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: combiner/products/build/allOutputs
|
||||
- name: Flatten Artifacts
|
||||
@@ -162,8 +164,9 @@ jobs:
|
||||
run: |
|
||||
cat combiner/products/build/allOutputs/version.txt
|
||||
test -s combiner/products/build/allOutputs/version.txt
|
||||
- uses: actions/setup-java@v1
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 11
|
||||
- name: Combine
|
||||
if: |
|
||||
@@ -188,7 +191,7 @@ jobs:
|
||||
RUN_AZURE_ARTIFACTORY_RELEASE: "TRUE"
|
||||
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Maven
|
||||
path: ~/releases
|
||||
|
||||
17
.github/workflows/lint-format.yml
vendored
17
.github/workflows/lint-format.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
git checkout -b pr
|
||||
git branch -f main origin/main
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.8
|
||||
- name: Install clang-format
|
||||
@@ -41,11 +41,19 @@ jobs:
|
||||
- name: Generate diff
|
||||
run: git diff HEAD > wpiformat-fixes.patch
|
||||
if: ${{ failure() }}
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wpiformat fixes
|
||||
path: wpiformat-fixes.patch
|
||||
if: ${{ failure() }}
|
||||
- name: Write to job summary
|
||||
run: |
|
||||
echo '```diff' >> $GITHUB_STEP_SUMMARY
|
||||
cat wpiformat-fixes.patch >> $GITHUB_STEP_SUMMARY
|
||||
echo '' >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
if: ${{ failure() }}
|
||||
|
||||
tidy:
|
||||
name: "clang-tidy"
|
||||
runs-on: ubuntu-latest
|
||||
@@ -59,7 +67,7 @@ jobs:
|
||||
git checkout -b pr
|
||||
git branch -f main origin/main
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.8
|
||||
- name: Install clang-tidy
|
||||
@@ -102,8 +110,9 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v1
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 13
|
||||
- name: Install libclang-9
|
||||
run: sudo apt update && sudo apt install -y libclang-cpp9 libclang1-9
|
||||
|
||||
4
.github/workflows/sanitizers.yml
vendored
4
.github/workflows/sanitizers.yml
vendored
@@ -31,13 +31,13 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java python-is-python3
|
||||
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java python-is-python3 clang-14
|
||||
|
||||
- name: Install jinja
|
||||
run: python -m pip install jinja2
|
||||
|
||||
- name: configure
|
||||
run: mkdir build && cd build && cmake ${{ matrix.cmake-flags }} ..
|
||||
run: mkdir build && cd build && cmake -DCMAKE_C_COMPILER:FILEPATH=/usr/bin/clang-14 -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/clang++-14 ${{ matrix.cmake-flags }} ..
|
||||
|
||||
- name: build
|
||||
working-directory: build
|
||||
|
||||
2
.github/workflows/upstream-utils.yml
vendored
2
.github/workflows/upstream-utils.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
git checkout -b pr
|
||||
git branch -f main origin/main
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.9
|
||||
- name: Configure committer identity
|
||||
|
||||
@@ -37,8 +37,7 @@ So you want to contribute your changes back to WPILib. Great! We have a few cont
|
||||
|
||||
## Coding Guidelines
|
||||
|
||||
WPILib uses modified Google style guides for both C++ and Java, which can be found in the [styleguide repository](https://github.com/wpilibsuite/styleguide). Autoformatters are available for many popular editors at https://github.com/google/styleguide. Running wpiformat is required for all contributions and is enforced by our continuous integration system. We currently use clang-format 14.0 with wpiformat.
|
||||
|
||||
WPILib uses modified Google style guides for both C++ and Java, which can be found in the [styleguide repository](https://github.com/wpilibsuite/styleguide). Autoformatters are available for many popular editors at https://github.com/google/styleguide. Running wpiformat is required for all contributions and is enforced by our continuous integration system.
|
||||
While the library should be fully formatted according to the styles, additional elements of the style guide were not followed when the library was initially created. All new code should follow the guidelines. If you are looking for some easy ramp-up tasks, finding areas that don't follow the style guide and fixing them is very welcome.
|
||||
|
||||
When writing math expressions in documentation, use https://www.unicodeit.net/ to convert LaTeX to a Unicode equivalent that's easier to read. Not all expressions will translate (e.g., superscripts of superscripts) so focus on making it readable by someone who isn't familiar with LaTeX. If content on multiple lines needs to be aligned in Doxygen/Javadoc comments (e.g., integration/summation limits, matrices packed with square brackets and superscripts for them), put them in @verbatim/@endverbatim blocks in Doxygen or `<pre>` tags in Javadoc so they render with monospace font.
|
||||
|
||||
@@ -90,4 +90,4 @@ The following 3 tasks can be used for deployment:
|
||||
|
||||
Deploying any of these to the roboRIO will disable the current startup project until it is redeployed.
|
||||
|
||||
From here, ssh into the roboRIO using the `admin` account (`lvuser` will fail to run in many cases). In the admin home directory, a file for each deploy type will exist (`myRobotCpp`, `myRobotCppStatic` and `myRobotJavaRun`). These can be run to start up the corresponding project.
|
||||
From here, ssh into the roboRIO using the `lvuser` account and run `frcRunRobot.sh` (It's in path).
|
||||
|
||||
@@ -62,7 +62,7 @@ On macOS ARM, run `softwareupdate --install-rosetta`. This is necessary to be ab
|
||||
|
||||
Clone the WPILib repository and follow the instructions above for installing any required tooling.
|
||||
|
||||
See the [styleguide README](https://github.com/wpilibsuite/styleguide/blob/main/README.md) for wpiformat setup instructions.
|
||||
See the [styleguide README](https://github.com/wpilibsuite/styleguide/blob/main/README.md) for wpiformat setup instructions. We use clang-format 14.
|
||||
|
||||
## Building
|
||||
|
||||
@@ -137,7 +137,9 @@ make
|
||||
|
||||
#### wpiformat
|
||||
|
||||
wpiformat can be executed anywhere in the repository via `py -3 -m wpiformat` on Windows or `python3 -m wpiformat` on other platforms.
|
||||
wpiformat can be executed anywhere in the repository via `py -3 -m wpiformat -clang 14` on Windows or `python3 -m wpiformat -clang 14` on other platforms.
|
||||
|
||||
Once a PR has been submitted, formatting can be run in CI by commenting `/wpiformat` on the PR. A new commit will be pushed with the formatting changes.
|
||||
|
||||
#### Java Code Quality Tools
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ Drake wpimath/src/main/native/thirdparty/drake/
|
||||
wpimath/src/test/native/cpp/drake/
|
||||
wpimath/src/test/native/include/drake/
|
||||
V8 export-template wpiutil/src/main/native/include/wpi/SymbolExports.h
|
||||
GCEM wpimath/src/main/native/thirdparty/gcem/include/
|
||||
|
||||
==============================================================================
|
||||
Google Test License
|
||||
@@ -1224,3 +1225,20 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
============
|
||||
GCEM License
|
||||
============
|
||||
Copyright 2022 - ktholer (https://github.com/kthohr/gcem)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
@@ -5,5 +5,5 @@ repositories {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation "edu.wpi.first:native-utils:2023.2.7"
|
||||
implementation "edu.wpi.first:native-utils:2023.4.0"
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import groovy.transform.CompileStatic
|
||||
import javax.inject.Inject
|
||||
import edu.wpi.first.deployutils.deploy.artifact.MavenArtifact
|
||||
import edu.wpi.first.deployutils.deploy.context.DeployContext
|
||||
import org.gradle.api.Project
|
||||
import edu.wpi.first.deployutils.ActionWrapper
|
||||
import edu.wpi.first.deployutils.deploy.target.RemoteTarget
|
||||
import edu.wpi.first.deployutils.PredicateWrapper
|
||||
import groovy.transform.CompileStatic;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.util.function.Function
|
||||
import org.gradle.api.Project;
|
||||
|
||||
import edu.wpi.first.deployutils.deploy.CommandDeployResult;
|
||||
import edu.wpi.first.deployutils.deploy.artifact.MavenArtifact;
|
||||
import edu.wpi.first.deployutils.deploy.context.DeployContext;
|
||||
import edu.wpi.first.deployutils.deploy.target.RemoteTarget;
|
||||
import edu.wpi.first.deployutils.PredicateWrapper;
|
||||
import edu.wpi.first.deployutils.ActionWrapper;
|
||||
|
||||
@CompileStatic
|
||||
public class WPIJREArtifact extends MavenArtifact {
|
||||
@@ -17,6 +18,18 @@ public class WPIJREArtifact extends MavenArtifact {
|
||||
return configName;
|
||||
}
|
||||
|
||||
public boolean isCheckJreVersion() {
|
||||
return checkJreVersion;
|
||||
}
|
||||
|
||||
public void setCheckJreVersion(boolean checkJreVersion) {
|
||||
this.checkJreVersion = checkJreVersion;
|
||||
}
|
||||
|
||||
private boolean checkJreVersion = true;
|
||||
|
||||
private final String artifactLocation = "edu.wpi.first.jdk:roborio-2023:17.0.5u7-1"
|
||||
|
||||
@Inject
|
||||
public WPIJREArtifact(String name, RemoteTarget target) {
|
||||
super(name, target);
|
||||
@@ -24,10 +37,10 @@ public class WPIJREArtifact extends MavenArtifact {
|
||||
this.configName = configName;
|
||||
Project project = target.getProject();
|
||||
getConfiguration().set(project.getConfigurations().create(configName));
|
||||
getDependency().set(project.getDependencies().add(configName, "edu.wpi.first.jdk:roborio-2022:11.0.12u5-1"));
|
||||
getDependency().set(project.getDependencies().add(configName, artifactLocation));
|
||||
|
||||
setOnlyIf(new PredicateWrapper({ DeployContext ctx ->
|
||||
return jreMissing(ctx) || project.hasProperty("force-redeploy-jre");
|
||||
return jreMissing(ctx) || jreOutOfDate(ctx) || project.hasProperty("force-redeploy-jre");
|
||||
}));
|
||||
|
||||
getDirectory().set("/tmp");
|
||||
@@ -35,7 +48,7 @@ public class WPIJREArtifact extends MavenArtifact {
|
||||
|
||||
getPostdeploy().add(new ActionWrapper({ DeployContext ctx ->
|
||||
ctx.getLogger().log("Installing JRE...");
|
||||
ctx.execute("opkg remove frc2022-openjdk*; opkg install /tmp/frcjre.ipk; rm /tmp/frcjre.ipk");
|
||||
ctx.execute("opkg remove frc*-openjdk*; opkg install /tmp/frcjre.ipk; rm /tmp/frcjre.ipk");
|
||||
ctx.getLogger().log("JRE Deployed!");
|
||||
}));
|
||||
}
|
||||
@@ -44,5 +57,21 @@ public class WPIJREArtifact extends MavenArtifact {
|
||||
return ctx.execute("if [[ -f \"/usr/local/frc/JRE/bin/java\" ]]; then echo OK; else echo MISSING; fi").getResult().contains("MISSING");
|
||||
}
|
||||
|
||||
|
||||
private boolean jreOutOfDate(DeployContext ctx) {
|
||||
if (!checkJreVersion) {
|
||||
return false;
|
||||
}
|
||||
String version = getDependency().get().getVersion();
|
||||
CommandDeployResult cmdResult = ctx.execute("opkg list-installed | grep openjdk");
|
||||
if (cmdResult.getExitCode() != 0) {
|
||||
ctx.getLogger().log("JRE not found");
|
||||
return false;
|
||||
}
|
||||
String result = cmdResult.getResult().trim();
|
||||
ctx.getLogger().log("Searching for JRE " + version);
|
||||
ctx.getLogger().log("Found JRE " + result);
|
||||
boolean matches = result.contains(version);
|
||||
ctx.getLogger().log(matches ? "JRE Is Correct Version" : "JRE is mismatched. Reinstalling");
|
||||
return !matches;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ model {
|
||||
components {
|
||||
examplesMap.each { key, value ->
|
||||
if (key == "usbviewer") {
|
||||
if (!project.hasProperty('onlylinuxathena') && !project.hasProperty('onlylinuxarm32') && !project.hasProperty('onlylinuxarm64')) {
|
||||
if (!project.hasProperty('onlylinuxathena')) {
|
||||
"${key}"(NativeExecutableSpec) {
|
||||
targetBuildTypes 'debug'
|
||||
binaries.all {
|
||||
@@ -189,7 +189,7 @@ model {
|
||||
lib project: ':wpigui', library: 'wpigui', linkage: 'static'
|
||||
lib library: 'cscore', linkage: 'shared'
|
||||
nativeUtils.useRequiredLibrary(it, 'imgui_static')
|
||||
if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio || it.targetPlatform.name == nativeUtils.wpi.platforms.linuxarm32 || it.targetPlatform.name == nativeUtils.wpi.platforms.linuxarm64) {
|
||||
if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
|
||||
it.buildable = false
|
||||
return
|
||||
}
|
||||
@@ -199,6 +199,9 @@ model {
|
||||
it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
|
||||
} else {
|
||||
it.linker.args << '-lX11'
|
||||
if (it.targetPlatform.name.startsWith('linuxarm')) {
|
||||
it.linker.args << '-lGL'
|
||||
}
|
||||
}
|
||||
}
|
||||
sources {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
if (!project.hasProperty('onlylinuxathena') && !project.hasProperty('onlylinuxarm32') && !project.hasProperty('onlylinuxarm64')) {
|
||||
if (!project.hasProperty('onlylinuxathena')) {
|
||||
|
||||
description = "roboRIO Team Number Setter"
|
||||
|
||||
@@ -24,19 +24,7 @@ if (!project.hasProperty('onlylinuxathena') && !project.hasProperty('onlylinuxar
|
||||
def wpilibVersionFileInput = file("src/main/generate/WPILibVersion.cpp.in")
|
||||
def wpilibVersionFileOutput = file("$buildDir/generated/main/cpp/WPILibVersion.cpp")
|
||||
|
||||
nativeUtils {
|
||||
nativeDependencyContainer {
|
||||
libssh(getNativeDependencyTypeClass('WPIStaticMavenDependency')) {
|
||||
groupId = "edu.wpi.first.thirdparty.frc2023"
|
||||
artifactId = "libssh"
|
||||
headerClassifier = "headers"
|
||||
sourceClassifier = "sources"
|
||||
ext = "zip"
|
||||
version = '0.95-3'
|
||||
targetPlatforms.addAll(nativeUtils.wpi.platforms.desktopPlatforms)
|
||||
}
|
||||
}
|
||||
}
|
||||
apply from: "${rootDir}/shared/libssh.gradle"
|
||||
|
||||
task generateCppVersion() {
|
||||
description = 'Generates the wpilib version class'
|
||||
@@ -107,7 +95,7 @@ if (!project.hasProperty('onlylinuxathena') && !project.hasProperty('onlylinuxar
|
||||
}
|
||||
}
|
||||
binaries.all {
|
||||
if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio || it.targetPlatform.name == nativeUtils.wpi.platforms.linuxarm32 || it.targetPlatform.name == nativeUtils.wpi.platforms.linuxarm64) {
|
||||
if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
|
||||
it.buildable = false
|
||||
return
|
||||
}
|
||||
@@ -124,6 +112,9 @@ if (!project.hasProperty('onlylinuxathena') && !project.hasProperty('onlylinuxar
|
||||
it.linker.args << '-framework' << 'Kerberos'
|
||||
} else {
|
||||
it.linker.args << '-lX11'
|
||||
if (it.targetPlatform.name.startsWith('linuxarm')) {
|
||||
it.linker.args << '-lGL'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,20 @@ void Downloader::DisplayRemoteDirSelector() {
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Deselect All")) {
|
||||
for (auto&& download : m_downloadList) {
|
||||
download.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Select All")) {
|
||||
for (auto&& download : m_downloadList) {
|
||||
download.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Remote directory text box
|
||||
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20);
|
||||
if (ImGui::InputText("Remote Dir", &m_remoteDir,
|
||||
|
||||
@@ -4,6 +4,7 @@ plugins {
|
||||
}
|
||||
|
||||
evaluationDependsOn(':wpiutil')
|
||||
evaluationDependsOn(':wpinet')
|
||||
evaluationDependsOn(':ntcore')
|
||||
evaluationDependsOn(':cscore')
|
||||
evaluationDependsOn(':hal')
|
||||
@@ -28,6 +29,7 @@ def cppIncludeRoots = []
|
||||
|
||||
cppProjectZips.add(project(':hal').cppHeadersZip)
|
||||
cppProjectZips.add(project(':wpiutil').cppHeadersZip)
|
||||
cppProjectZips.add(project(':wpinet').cppHeadersZip)
|
||||
cppProjectZips.add(project(':ntcore').cppHeadersZip)
|
||||
cppProjectZips.add(project(':cscore').cppHeadersZip)
|
||||
cppProjectZips.add(project(':cameraserver').cppHeadersZip)
|
||||
@@ -109,7 +111,7 @@ doxygen {
|
||||
// libuv
|
||||
exclude 'uv.h'
|
||||
exclude 'uv/**'
|
||||
exclude 'wpi/uv/**'
|
||||
exclude 'wpinet/uv/**'
|
||||
|
||||
// json
|
||||
exclude 'wpi/json.h'
|
||||
@@ -124,6 +126,12 @@ doxygen {
|
||||
exclude 'units/**'
|
||||
}
|
||||
|
||||
aliases 'effects=\\par <i>Effects:</i>^^',
|
||||
'notes=\\par <i>Notes:</i>^^',
|
||||
'requires=\\par <i>Requires:</i>^^',
|
||||
'requiredbe=\\par <i>Required Behavior:</i>^^',
|
||||
'concept{2}=<a href=\"md_doc_concepts.html#\1\">\2</a>',
|
||||
'defaultbe=\\par <i>Default Behavior:</i>^^'
|
||||
case_sense_names false
|
||||
extension_mapping 'inc=C++', 'no_extension=C++'
|
||||
extract_all true
|
||||
@@ -200,6 +208,7 @@ task generateJavaDocs(type: Javadoc) {
|
||||
dependsOn project(':ntcore').ntcoreGenerateJavaTypes
|
||||
source project(':hal').sourceSets.main.java
|
||||
source project(':wpiutil').sourceSets.main.java
|
||||
source project(':wpinet').sourceSets.main.java
|
||||
source project(':cscore').sourceSets.main.java
|
||||
source project(':ntcore').sourceSets.main.java
|
||||
source project(':wpimath').sourceSets.main.java
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
if (!project.hasProperty('onlylinuxathena') && !project.hasProperty('onlylinuxarm32') && !project.hasProperty('onlylinuxarm64')) {
|
||||
if (!project.hasProperty('onlylinuxathena')) {
|
||||
|
||||
apply plugin: 'cpp'
|
||||
apply plugin: 'c'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
if (!project.hasProperty('onlylinuxathena') && !project.hasProperty('onlylinuxarm32') && !project.hasProperty('onlylinuxarm64')) {
|
||||
if (!project.hasProperty('onlylinuxathena')) {
|
||||
|
||||
description = "A different kind of dashboard"
|
||||
|
||||
@@ -100,7 +100,7 @@ if (!project.hasProperty('onlylinuxathena') && !project.hasProperty('onlylinuxar
|
||||
}
|
||||
}
|
||||
binaries.all {
|
||||
if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio || it.targetPlatform.name == nativeUtils.wpi.platforms.linuxarm32 || it.targetPlatform.name == nativeUtils.wpi.platforms.linuxarm64) {
|
||||
if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
|
||||
it.buildable = false
|
||||
return
|
||||
}
|
||||
@@ -128,7 +128,7 @@ if (!project.hasProperty('onlylinuxathena') && !project.hasProperty('onlylinuxar
|
||||
}
|
||||
}
|
||||
binaries.all {
|
||||
if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio || it.targetPlatform.name == nativeUtils.wpi.platforms.linuxarm32 || it.targetPlatform.name == nativeUtils.wpi.platforms.linuxarm64) {
|
||||
if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
|
||||
it.buildable = false
|
||||
return
|
||||
}
|
||||
@@ -169,7 +169,7 @@ if (!project.hasProperty('onlylinuxathena') && !project.hasProperty('onlylinuxar
|
||||
}
|
||||
}
|
||||
binaries.all {
|
||||
if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio || it.targetPlatform.name == nativeUtils.wpi.platforms.linuxarm32 || it.targetPlatform.name == nativeUtils.wpi.platforms.linuxarm64) {
|
||||
if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
|
||||
it.buildable = false
|
||||
return
|
||||
}
|
||||
@@ -190,6 +190,9 @@ if (!project.hasProperty('onlylinuxathena') && !project.hasProperty('onlylinuxar
|
||||
it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
|
||||
} else {
|
||||
it.linker.args << '-lX11'
|
||||
if (it.targetPlatform.name.startsWith('linuxarm')) {
|
||||
it.linker.args << '-lGL'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,44 +70,41 @@ static void RemapEnterKeyCallback(GLFWwindow* window, int key, int scancode,
|
||||
}
|
||||
|
||||
static void NtInitialize() {
|
||||
// update window title when connection status changes
|
||||
auto inst = nt::GetDefaultInstance();
|
||||
auto poller = nt::CreateConnectionListenerPoller(inst);
|
||||
nt::AddPolledConnectionListener(poller, true);
|
||||
auto poller = nt::CreateListenerPoller(inst);
|
||||
nt::AddPolledListener(
|
||||
poller, inst,
|
||||
NT_EVENT_CONNECTION | NT_EVENT_IMMEDIATE | NT_EVENT_LOGMESSAGE);
|
||||
gui::AddEarlyExecute([poller] {
|
||||
auto win = gui::GetSystemWindow();
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
for (auto&& event : nt::ReadConnectionListenerQueue(poller)) {
|
||||
if (event.connected) {
|
||||
glfwSetWindowTitle(
|
||||
win, fmt::format("Glass - Connected ({})", event.conn.remote_ip)
|
||||
.c_str());
|
||||
} else {
|
||||
glfwSetWindowTitle(win, "Glass - DISCONNECTED");
|
||||
for (auto&& event : nt::ReadListenerQueue(poller)) {
|
||||
if (auto connInfo = event.GetConnectionInfo()) {
|
||||
// update window title when connection status changes
|
||||
if ((event.flags & NT_EVENT_CONNECTED) != 0) {
|
||||
glfwSetWindowTitle(
|
||||
win, fmt::format("Glass - Connected ({})", connInfo->remote_ip)
|
||||
.c_str());
|
||||
} else {
|
||||
glfwSetWindowTitle(win, "Glass - DISCONNECTED");
|
||||
}
|
||||
} else if (auto msg = event.GetLogMessage()) {
|
||||
const char* level = "";
|
||||
if (msg->level >= NT_LOG_CRITICAL) {
|
||||
level = "CRITICAL: ";
|
||||
} else if (msg->level >= NT_LOG_ERROR) {
|
||||
level = "ERROR: ";
|
||||
} else if (msg->level >= NT_LOG_WARNING) {
|
||||
level = "WARNING: ";
|
||||
}
|
||||
gNetworkTablesLog.Append(fmt::format(
|
||||
"{}{} ({}:{})\n", level, msg->message, msg->filename, msg->line));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// handle NetworkTables log messages
|
||||
auto logPoller = nt::CreateLoggerPoller(inst);
|
||||
nt::AddPolledLogger(logPoller, NT_LOG_INFO, 100);
|
||||
gui::AddEarlyExecute([logPoller] {
|
||||
for (auto&& msg : nt::ReadLoggerQueue(logPoller)) {
|
||||
const char* level = "";
|
||||
if (msg.level >= NT_LOG_CRITICAL) {
|
||||
level = "CRITICAL: ";
|
||||
} else if (msg.level >= NT_LOG_ERROR) {
|
||||
level = "ERROR: ";
|
||||
} else if (msg.level >= NT_LOG_WARNING) {
|
||||
level = "WARNING: ";
|
||||
}
|
||||
gNetworkTablesLog.Append(fmt::format("{}{} ({}:{})\n", level, msg.message,
|
||||
msg.filename, msg.line));
|
||||
}
|
||||
});
|
||||
|
||||
gNetworkTablesLogWindow = std::make_unique<glass::Window>(
|
||||
glass::GetStorageRoot().GetChild("NetworkTables Log"),
|
||||
"NetworkTables Log", glass::Window::kHide);
|
||||
|
||||
@@ -113,7 +113,7 @@ class PopupState {
|
||||
struct DisplayOptions {
|
||||
explicit DisplayOptions(const gui::Texture& texture) : texture{texture} {}
|
||||
|
||||
enum Style { kBoxImage = 0, kLine, kLineClosed, kTrack };
|
||||
enum Style { kBoxImage = 0, kLine, kLineClosed, kTrack, kHidden };
|
||||
|
||||
static constexpr Style kDefaultStyle = kBoxImage;
|
||||
static constexpr float kDefaultWeight = 4.0f;
|
||||
@@ -547,7 +547,7 @@ ObjectInfo::ObjectInfo(Storage& storage)
|
||||
DisplayOptions::kDefaultLength.to<float>())},
|
||||
m_style{storage.GetString("style"),
|
||||
DisplayOptions::kDefaultStyle,
|
||||
{"Box/Image", "Line", "Line (Closed)", "Track"}},
|
||||
{"Box/Image", "Line", "Line (Closed)", "Track", "Hidden"}},
|
||||
m_weight{storage.GetFloat("weight", DisplayOptions::kDefaultWeight)},
|
||||
m_color{
|
||||
storage.GetFloatArray("color", DisplayOptions::kDefaultColorFloat)},
|
||||
@@ -840,6 +840,8 @@ void PoseFrameData::Draw(ImDrawList* drawList, std::vector<ImVec2>* center,
|
||||
left->emplace_back(m_corners[4]);
|
||||
right->emplace_back(m_corners[5]);
|
||||
break;
|
||||
case DisplayOptions::kHidden:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_displayOptions.arrows) {
|
||||
|
||||
@@ -117,58 +117,55 @@ NTField2DModel::NTField2DModel(nt::NetworkTableInstance inst,
|
||||
{{nt::PubSubOption::SendAll(true),
|
||||
nt::PubSubOption::Periodic(0.05)}}},
|
||||
m_nameTopic{inst.GetTopic(fmt::format("{}/.name", path))},
|
||||
m_topicListener{inst},
|
||||
m_valueListener{inst} {
|
||||
m_topicListener.Add(m_tableSub, NT_TOPIC_NOTIFY_PUBLISH |
|
||||
NT_TOPIC_NOTIFY_UNPUBLISH |
|
||||
NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
m_valueListener.Add(m_tableSub,
|
||||
NT_VALUE_NOTIFY_IMMEDIATE | NT_VALUE_NOTIFY_LOCAL);
|
||||
m_poller{inst} {
|
||||
m_poller.AddListener(m_tableSub, nt::EventFlags::kTopic |
|
||||
nt::EventFlags::kValueAll |
|
||||
nt::EventFlags::kImmediate);
|
||||
}
|
||||
|
||||
NTField2DModel::~NTField2DModel() = default;
|
||||
|
||||
void NTField2DModel::Update() {
|
||||
// handle publish/unpublish
|
||||
for (auto&& event : m_topicListener.ReadQueue()) {
|
||||
auto name = wpi::drop_front(event.info.name, m_path.size());
|
||||
if (name.empty() || name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
auto [it, match] = Find(event.info.name);
|
||||
if (event.flags & NT_TOPIC_NOTIFY_UNPUBLISH) {
|
||||
if (match) {
|
||||
m_objects.erase(it);
|
||||
for (auto&& event : m_poller.ReadQueue()) {
|
||||
if (auto info = event.GetTopicInfo()) {
|
||||
// handle publish/unpublish
|
||||
auto name = wpi::drop_front(info->name, m_path.size());
|
||||
if (name.empty() || name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
} else if (event.flags & NT_TOPIC_NOTIFY_PUBLISH) {
|
||||
if (!match) {
|
||||
it = m_objects.emplace(
|
||||
it, std::make_unique<ObjectModel>(
|
||||
event.info.name, nt::DoubleArrayTopic{event.info.topic}));
|
||||
auto [it, match] = Find(info->name);
|
||||
if (event.flags & nt::EventFlags::kUnpublish) {
|
||||
if (match) {
|
||||
m_objects.erase(it);
|
||||
}
|
||||
continue;
|
||||
} else if (event.flags & nt::EventFlags::kPublish) {
|
||||
if (!match) {
|
||||
it = m_objects.emplace(
|
||||
it, std::make_unique<ObjectModel>(
|
||||
info->name, nt::DoubleArrayTopic{info->topic}));
|
||||
}
|
||||
} else if (!match) {
|
||||
continue;
|
||||
}
|
||||
} else if (auto valueData = event.GetValueEventData()) {
|
||||
// update values
|
||||
// .name
|
||||
if (valueData->topic == m_nameTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsString()) {
|
||||
m_nameValue = valueData->value.GetString();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else if (!match) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// update values
|
||||
for (auto&& event : m_valueListener.ReadQueue()) {
|
||||
// .name
|
||||
if (event.topic == m_nameTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsString()) {
|
||||
m_nameValue = event.value.GetString();
|
||||
auto it =
|
||||
std::find_if(m_objects.begin(), m_objects.end(), [&](const auto& e) {
|
||||
return e->GetTopic().GetHandle() == valueData->topic;
|
||||
});
|
||||
if (it != m_objects.end()) {
|
||||
(*it)->NTUpdate(valueData->value);
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
auto it =
|
||||
std::find_if(m_objects.begin(), m_objects.end(), [&](const auto& e) {
|
||||
return e->GetTopic().GetHandle() == event.topic;
|
||||
});
|
||||
if (it != m_objects.end()) {
|
||||
(*it)->NTUpdate(event.value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,7 @@ class NTMechanismGroupImpl final {
|
||||
const char* GetName() const { return m_name.c_str(); }
|
||||
void ForEachObject(wpi::function_ref<void(MechanismObjectModel& model)> func);
|
||||
|
||||
void NTUpdate(const nt::TopicNotification& event, std::string_view name);
|
||||
void NTUpdate(const nt::ValueNotification& event, std::string_view name);
|
||||
void NTUpdate(const nt::Event& event, std::string_view name);
|
||||
|
||||
protected:
|
||||
nt::NetworkTableInstance m_inst;
|
||||
@@ -74,8 +73,7 @@ class NTMechanismObjectModel final : public MechanismObjectModel {
|
||||
frc::Rotation2d GetAngle() const final { return m_angleValue; }
|
||||
units::meter_t GetLength() const final { return m_lengthValue; }
|
||||
|
||||
bool NTUpdate(const nt::TopicNotification& event, std::string_view name);
|
||||
void NTUpdate(const nt::ValueNotification& event, std::string_view name);
|
||||
bool NTUpdate(const nt::Event& event, std::string_view name);
|
||||
|
||||
private:
|
||||
NTMechanismGroupImpl m_group;
|
||||
@@ -102,7 +100,7 @@ void NTMechanismGroupImpl::ForEachObject(
|
||||
}
|
||||
}
|
||||
|
||||
void NTMechanismGroupImpl::NTUpdate(const nt::TopicNotification& event,
|
||||
void NTMechanismGroupImpl::NTUpdate(const nt::Event& event,
|
||||
std::string_view name) {
|
||||
if (name.empty()) {
|
||||
return;
|
||||
@@ -118,83 +116,69 @@ void NTMechanismGroupImpl::NTUpdate(const nt::TopicNotification& event,
|
||||
[](const auto& e, std::string_view name) { return e->GetName() < name; });
|
||||
bool match = it != m_objects.end() && (*it)->GetName() == name;
|
||||
|
||||
if (event.flags & NT_TOPIC_NOTIFY_PUBLISH) {
|
||||
if (!match) {
|
||||
it = m_objects.emplace(
|
||||
it, std::make_unique<NTMechanismObjectModel>(
|
||||
m_inst, fmt::format("{}/{}", m_path, name), name));
|
||||
match = true;
|
||||
if (event.GetTopicInfo()) {
|
||||
if (event.flags & nt::EventFlags::kPublish) {
|
||||
if (!match) {
|
||||
it = m_objects.emplace(
|
||||
it, std::make_unique<NTMechanismObjectModel>(
|
||||
m_inst, fmt::format("{}/{}", m_path, name), name));
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
if ((*it)->NTUpdate(event, childName)) {
|
||||
m_objects.erase(it);
|
||||
if (match) {
|
||||
if ((*it)->NTUpdate(event, childName)) {
|
||||
m_objects.erase(it);
|
||||
}
|
||||
}
|
||||
} else if (event.GetValueEventData()) {
|
||||
if (match) {
|
||||
(*it)->NTUpdate(event, childName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NTMechanismGroupImpl::NTUpdate(const nt::ValueNotification& event,
|
||||
std::string_view name) {
|
||||
if (name.empty()) {
|
||||
return;
|
||||
}
|
||||
std::string_view childName;
|
||||
std::tie(name, childName) = wpi::split(name, '/');
|
||||
if (childName.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = std::lower_bound(
|
||||
m_objects.begin(), m_objects.end(), name,
|
||||
[](const auto& e, std::string_view name) { return e->GetName() < name; });
|
||||
if (it != m_objects.end() && (*it)->GetName() == name) {
|
||||
(*it)->NTUpdate(event, childName);
|
||||
}
|
||||
}
|
||||
|
||||
bool NTMechanismObjectModel::NTUpdate(const nt::TopicNotification& event,
|
||||
bool NTMechanismObjectModel::NTUpdate(const nt::Event& event,
|
||||
std::string_view childName) {
|
||||
if (event.info.topic == m_typeTopic.GetHandle()) {
|
||||
if (event.flags & NT_TOPIC_NOTIFY_UNPUBLISH) {
|
||||
return true;
|
||||
if (auto info = event.GetTopicInfo()) {
|
||||
if (info->topic == m_typeTopic.GetHandle()) {
|
||||
if (event.flags & nt::EventFlags::kUnpublish) {
|
||||
return true;
|
||||
}
|
||||
} else if (info->topic != m_colorTopic.GetHandle() &&
|
||||
info->topic != m_weightTopic.GetHandle() &&
|
||||
info->topic != m_angleTopic.GetHandle() &&
|
||||
info->topic != m_lengthTopic.GetHandle()) {
|
||||
m_group.NTUpdate(event, childName);
|
||||
}
|
||||
} else if (auto valueData = event.GetValueEventData()) {
|
||||
if (valueData->topic == m_typeTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsString()) {
|
||||
m_typeValue = valueData->value.GetString();
|
||||
}
|
||||
} else if (valueData->topic == m_colorTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsString()) {
|
||||
ConvertColor(valueData->value.GetString(), &m_colorValue);
|
||||
}
|
||||
} else if (valueData->topic == m_weightTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsDouble()) {
|
||||
m_weightValue = valueData->value.GetDouble();
|
||||
}
|
||||
} else if (valueData->topic == m_angleTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsDouble()) {
|
||||
m_angleValue = units::degree_t{valueData->value.GetDouble()};
|
||||
}
|
||||
} else if (valueData->topic == m_lengthTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsDouble()) {
|
||||
m_lengthValue = units::meter_t{valueData->value.GetDouble()};
|
||||
}
|
||||
} else {
|
||||
m_group.NTUpdate(event, childName);
|
||||
}
|
||||
} else if (event.info.topic != m_colorTopic.GetHandle() &&
|
||||
event.info.topic != m_weightTopic.GetHandle() &&
|
||||
event.info.topic != m_angleTopic.GetHandle() &&
|
||||
event.info.topic != m_lengthTopic.GetHandle()) {
|
||||
m_group.NTUpdate(event, childName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NTMechanismObjectModel::NTUpdate(const nt::ValueNotification& event,
|
||||
std::string_view childName) {
|
||||
if (event.topic == m_typeTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsString()) {
|
||||
m_typeValue = event.value.GetString();
|
||||
}
|
||||
} else if (event.topic == m_colorTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsString()) {
|
||||
ConvertColor(event.value.GetString(), &m_colorValue);
|
||||
}
|
||||
} else if (event.topic == m_weightTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsDouble()) {
|
||||
m_weightValue = event.value.GetDouble();
|
||||
}
|
||||
} else if (event.topic == m_angleTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsDouble()) {
|
||||
m_angleValue = units::degree_t{event.value.GetDouble()};
|
||||
}
|
||||
} else if (event.topic == m_lengthTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsDouble()) {
|
||||
m_lengthValue = units::meter_t{event.value.GetDouble()};
|
||||
}
|
||||
} else {
|
||||
m_group.NTUpdate(event, childName);
|
||||
}
|
||||
}
|
||||
|
||||
class NTMechanism2DModel::RootModel final : public MechanismRootModel {
|
||||
public:
|
||||
RootModel(nt::NetworkTableInstance inst, std::string_view path,
|
||||
@@ -209,8 +193,7 @@ class NTMechanism2DModel::RootModel final : public MechanismRootModel {
|
||||
m_group.ForEachObject(func);
|
||||
}
|
||||
|
||||
bool NTUpdate(const nt::TopicNotification& event, std::string_view childName);
|
||||
void NTUpdate(const nt::ValueNotification& event, std::string_view childName);
|
||||
bool NTUpdate(const nt::Event& event, std::string_view childName);
|
||||
|
||||
frc::Translation2d GetPosition() const override { return m_pos; };
|
||||
|
||||
@@ -221,36 +204,35 @@ class NTMechanism2DModel::RootModel final : public MechanismRootModel {
|
||||
frc::Translation2d m_pos;
|
||||
};
|
||||
|
||||
bool NTMechanism2DModel::RootModel::NTUpdate(const nt::TopicNotification& event,
|
||||
bool NTMechanism2DModel::RootModel::NTUpdate(const nt::Event& event,
|
||||
std::string_view childName) {
|
||||
if (event.info.topic == m_xTopic.GetHandle() ||
|
||||
event.info.topic == m_yTopic.GetHandle()) {
|
||||
if (event.flags & NT_TOPIC_NOTIFY_UNPUBLISH) {
|
||||
return true;
|
||||
if (auto info = event.GetTopicInfo()) {
|
||||
if (info->topic == m_xTopic.GetHandle() ||
|
||||
info->topic == m_yTopic.GetHandle()) {
|
||||
if (event.flags & nt::EventFlags::kUnpublish) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
m_group.NTUpdate(event, childName);
|
||||
}
|
||||
} else if (auto valueData = event.GetValueEventData()) {
|
||||
if (valueData->topic == m_xTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsDouble()) {
|
||||
m_pos = frc::Translation2d{units::meter_t{valueData->value.GetDouble()},
|
||||
m_pos.Y()};
|
||||
}
|
||||
} else if (valueData->topic == m_yTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsDouble()) {
|
||||
m_pos = frc::Translation2d{
|
||||
m_pos.X(), units::meter_t{valueData->value.GetDouble()}};
|
||||
}
|
||||
} else {
|
||||
m_group.NTUpdate(event, childName);
|
||||
}
|
||||
} else {
|
||||
m_group.NTUpdate(event, childName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NTMechanism2DModel::RootModel::NTUpdate(const nt::ValueNotification& event,
|
||||
std::string_view childName) {
|
||||
if (event.topic == m_xTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsDouble()) {
|
||||
m_pos = frc::Translation2d{units::meter_t{event.value.GetDouble()},
|
||||
m_pos.Y()};
|
||||
}
|
||||
} else if (event.topic == m_yTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsDouble()) {
|
||||
m_pos = frc::Translation2d{m_pos.X(),
|
||||
units::meter_t{event.value.GetDouble()}};
|
||||
}
|
||||
} else {
|
||||
m_group.NTUpdate(event, childName);
|
||||
}
|
||||
}
|
||||
|
||||
NTMechanism2DModel::NTMechanism2DModel(std::string_view path)
|
||||
: NTMechanism2DModel{nt::NetworkTableInstance::GetDefault(), path} {}
|
||||
|
||||
@@ -265,75 +247,72 @@ NTMechanism2DModel::NTMechanism2DModel(nt::NetworkTableInstance inst,
|
||||
m_nameTopic{m_inst.GetTopic(fmt::format("{}/.name", path))},
|
||||
m_dimensionsTopic{m_inst.GetTopic(fmt::format("{}/dims", path))},
|
||||
m_bgColorTopic{m_inst.GetTopic(fmt::format("{}/backgroundColor", path))},
|
||||
m_topicListener{m_inst},
|
||||
m_valueListener{m_inst},
|
||||
m_poller{m_inst},
|
||||
m_dimensionsValue{1_m, 1_m} {
|
||||
m_topicListener.Add(m_tableSub, NT_TOPIC_NOTIFY_PUBLISH |
|
||||
NT_TOPIC_NOTIFY_UNPUBLISH |
|
||||
NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
m_valueListener.Add(m_tableSub,
|
||||
NT_VALUE_NOTIFY_IMMEDIATE | NT_VALUE_NOTIFY_LOCAL);
|
||||
m_poller.AddListener(m_tableSub, nt::EventFlags::kTopic |
|
||||
nt::EventFlags::kValueAll |
|
||||
nt::EventFlags::kImmediate);
|
||||
}
|
||||
|
||||
NTMechanism2DModel::~NTMechanism2DModel() = default;
|
||||
|
||||
void NTMechanism2DModel::Update() {
|
||||
for (auto&& event : m_topicListener.ReadQueue()) {
|
||||
auto name = wpi::drop_front(event.info.name, m_path.size());
|
||||
if (name.empty() || name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
std::string_view childName;
|
||||
std::tie(name, childName) = wpi::split(name, '/');
|
||||
if (childName.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto it = std::lower_bound(m_roots.begin(), m_roots.end(), name,
|
||||
[](const auto& e, std::string_view name) {
|
||||
return e->GetName() < name;
|
||||
});
|
||||
bool match = it != m_roots.end() && (*it)->GetName() == name;
|
||||
|
||||
if (event.flags & NT_TOPIC_NOTIFY_PUBLISH) {
|
||||
if (!match) {
|
||||
it = m_roots.emplace(
|
||||
it, std::make_unique<RootModel>(
|
||||
m_inst, fmt::format("{}{}", m_path, name), name));
|
||||
match = true;
|
||||
for (auto&& event : m_poller.ReadQueue()) {
|
||||
if (auto info = event.GetTopicInfo()) {
|
||||
auto name = wpi::drop_front(info->name, m_path.size());
|
||||
if (name.empty() || name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
if ((*it)->NTUpdate(event, childName)) {
|
||||
m_roots.erase(it);
|
||||
std::string_view childName;
|
||||
std::tie(name, childName) = wpi::split(name, '/');
|
||||
if (childName.empty()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto&& event : m_valueListener.ReadQueue()) {
|
||||
// .name
|
||||
if (event.topic == m_nameTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsString()) {
|
||||
m_nameValue = event.value.GetString();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
auto it = std::lower_bound(m_roots.begin(), m_roots.end(), name,
|
||||
[](const auto& e, std::string_view name) {
|
||||
return e->GetName() < name;
|
||||
});
|
||||
bool match = it != m_roots.end() && (*it)->GetName() == name;
|
||||
|
||||
// dims
|
||||
if (event.topic == m_dimensionsTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsDoubleArray()) {
|
||||
auto arr = event.value.GetDoubleArray();
|
||||
if (arr.size() == 2) {
|
||||
m_dimensionsValue = frc::Translation2d{units::meter_t{arr[0]},
|
||||
units::meter_t{arr[1]}};
|
||||
if (event.flags & nt::EventFlags::kPublish) {
|
||||
if (!match) {
|
||||
it = m_roots.emplace(
|
||||
it, std::make_unique<RootModel>(
|
||||
m_inst, fmt::format("{}{}", m_path, name), name));
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
if ((*it)->NTUpdate(event, childName)) {
|
||||
m_roots.erase(it);
|
||||
}
|
||||
}
|
||||
} else if (auto valueData = event.GetValueEventData()) {
|
||||
// .name
|
||||
if (valueData->topic == m_nameTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsString()) {
|
||||
m_nameValue = valueData->value.GetString();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// backgroundColor
|
||||
if (event.topic == m_bgColorTopic.GetHandle()) {
|
||||
if (event.value && event.value.IsString()) {
|
||||
ConvertColor(event.value.GetString(), &m_bgColorValue);
|
||||
// dims
|
||||
if (valueData->topic == m_dimensionsTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsDoubleArray()) {
|
||||
auto arr = valueData->value.GetDoubleArray();
|
||||
if (arr.size() == 2) {
|
||||
m_dimensionsValue = frc::Translation2d{units::meter_t{arr[0]},
|
||||
units::meter_t{arr[1]}};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// backgroundColor
|
||||
if (valueData->topic == m_bgColorTopic.GetHandle()) {
|
||||
if (valueData->value && valueData->value.IsString()) {
|
||||
ConvertColor(valueData->value.GetString(), &m_bgColorValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,15 +110,10 @@ NetworkTablesModel::NetworkTablesModel()
|
||||
: NetworkTablesModel{nt::NetworkTableInstance::GetDefault()} {}
|
||||
|
||||
NetworkTablesModel::NetworkTablesModel(nt::NetworkTableInstance inst)
|
||||
: m_inst{inst},
|
||||
m_subscriber{nt::SubscribeMultiple(inst.GetHandle(), {{"", "$"}})},
|
||||
m_topicPoller{inst},
|
||||
m_valuePoller{inst} {
|
||||
m_topicPoller.Add({{""}},
|
||||
NT_TOPIC_NOTIFY_IMMEDIATE | NT_TOPIC_NOTIFY_PROPERTIES |
|
||||
NT_TOPIC_NOTIFY_PUBLISH | NT_TOPIC_NOTIFY_UNPUBLISH);
|
||||
m_valuePoller.Add(m_subscriber,
|
||||
NT_VALUE_NOTIFY_IMMEDIATE | NT_VALUE_NOTIFY_LOCAL);
|
||||
: m_inst{inst}, m_poller{inst} {
|
||||
m_poller.AddListener({{"", "$"}}, nt::EventFlags::kTopic |
|
||||
nt::EventFlags::kValueAll |
|
||||
nt::EventFlags::kImmediate);
|
||||
}
|
||||
|
||||
NetworkTablesModel::Entry::~Entry() {
|
||||
@@ -426,56 +421,57 @@ void NetworkTablesModel::ValueSource::UpdateFromValue(
|
||||
|
||||
void NetworkTablesModel::Update() {
|
||||
bool updateTree = false;
|
||||
for (auto&& event : m_topicPoller.ReadQueue()) {
|
||||
auto& entry = m_entries[event.info.topic];
|
||||
if (event.flags & NT_TOPIC_NOTIFY_PUBLISH) {
|
||||
if (!entry) {
|
||||
entry = std::make_unique<Entry>();
|
||||
m_sortedEntries.emplace_back(entry.get());
|
||||
for (auto&& event : m_poller.ReadQueue()) {
|
||||
if (auto info = event.GetTopicInfo()) {
|
||||
auto& entry = m_entries[info->topic];
|
||||
if (event.flags & nt::EventFlags::kPublish) {
|
||||
if (!entry) {
|
||||
entry = std::make_unique<Entry>();
|
||||
m_sortedEntries.emplace_back(entry.get());
|
||||
updateTree = true;
|
||||
}
|
||||
}
|
||||
if (event.flags & nt::EventFlags::kUnpublish) {
|
||||
auto it = std::find(m_sortedEntries.begin(), m_sortedEntries.end(),
|
||||
entry.get());
|
||||
// will be removed completely below
|
||||
if (it != m_sortedEntries.end()) {
|
||||
*it = nullptr;
|
||||
}
|
||||
m_entries.erase(info->topic);
|
||||
updateTree = true;
|
||||
continue;
|
||||
}
|
||||
if (event.flags & nt::EventFlags::kProperties) {
|
||||
updateTree = true;
|
||||
}
|
||||
}
|
||||
if (event.flags & NT_TOPIC_NOTIFY_UNPUBLISH) {
|
||||
auto it = std::find(m_sortedEntries.begin(), m_sortedEntries.end(),
|
||||
entry.get());
|
||||
// will be removed completely below
|
||||
if (it != m_sortedEntries.end()) {
|
||||
*it = nullptr;
|
||||
if (entry) {
|
||||
entry->UpdateTopic(std::move(event));
|
||||
}
|
||||
m_entries.erase(event.info.topic);
|
||||
updateTree = true;
|
||||
continue;
|
||||
}
|
||||
if (event.flags & NT_TOPIC_NOTIFY_PROPERTIES) {
|
||||
updateTree = true;
|
||||
}
|
||||
if (entry) {
|
||||
entry->UpdateTopic(std::move(event));
|
||||
}
|
||||
}
|
||||
for (auto&& event : m_valuePoller.ReadQueue()) {
|
||||
auto& entry = m_entries[event.topic];
|
||||
if (entry) {
|
||||
entry->UpdateFromValue(std::move(event.value), entry->info.name,
|
||||
entry->info.type_str);
|
||||
if (wpi::starts_with(entry->info.name, '$') && entry->value.IsRaw() &&
|
||||
entry->info.type_str == "msgpack") {
|
||||
// meta topic handling
|
||||
if (entry->info.name == "$clients") {
|
||||
UpdateClients(entry->value.GetRaw());
|
||||
} else if (entry->info.name == "$serverpub") {
|
||||
m_server.UpdatePublishers(entry->value.GetRaw());
|
||||
} else if (entry->info.name == "$serversub") {
|
||||
m_server.UpdateSubscribers(entry->value.GetRaw());
|
||||
} else if (wpi::starts_with(entry->info.name, "$clientpub$")) {
|
||||
auto it = m_clients.find(wpi::drop_front(entry->info.name, 11));
|
||||
if (it != m_clients.end()) {
|
||||
it->second.UpdatePublishers(entry->value.GetRaw());
|
||||
}
|
||||
} else if (wpi::starts_with(entry->info.name, "$clientsub$")) {
|
||||
auto it = m_clients.find(wpi::drop_front(entry->info.name, 11));
|
||||
if (it != m_clients.end()) {
|
||||
it->second.UpdateSubscribers(entry->value.GetRaw());
|
||||
} else if (auto valueData = event.GetValueEventData()) {
|
||||
auto& entry = m_entries[valueData->topic];
|
||||
if (entry) {
|
||||
entry->UpdateFromValue(std::move(valueData->value), entry->info.name,
|
||||
entry->info.type_str);
|
||||
if (wpi::starts_with(entry->info.name, '$') && entry->value.IsRaw() &&
|
||||
entry->info.type_str == "msgpack") {
|
||||
// meta topic handling
|
||||
if (entry->info.name == "$clients") {
|
||||
UpdateClients(entry->value.GetRaw());
|
||||
} else if (entry->info.name == "$serverpub") {
|
||||
m_server.UpdatePublishers(entry->value.GetRaw());
|
||||
} else if (entry->info.name == "$serversub") {
|
||||
m_server.UpdateSubscribers(entry->value.GetRaw());
|
||||
} else if (wpi::starts_with(entry->info.name, "$clientpub$")) {
|
||||
auto it = m_clients.find(wpi::drop_front(entry->info.name, 11));
|
||||
if (it != m_clients.end()) {
|
||||
it->second.UpdatePublishers(entry->value.GetRaw());
|
||||
}
|
||||
} else if (wpi::starts_with(entry->info.name, "$clientsub$")) {
|
||||
auto it = m_clients.find(wpi::drop_front(entry->info.name, 11));
|
||||
if (it != m_clients.end()) {
|
||||
it->second.UpdateSubscribers(entry->value.GetRaw());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,10 @@ NetworkTablesProvider::NetworkTablesProvider(Storage& storage,
|
||||
nt::NetworkTableInstance inst)
|
||||
: Provider{storage.GetChild("windows")},
|
||||
m_inst{inst},
|
||||
m_topicPoller{inst},
|
||||
m_valuePoller{inst},
|
||||
m_poller{inst},
|
||||
m_typeCache{storage.GetChild("types")} {
|
||||
storage.SetCustomApply([this] {
|
||||
m_topicListener = m_topicPoller.Add({{""}}, NT_TOPIC_NOTIFY_PUBLISH |
|
||||
NT_TOPIC_NOTIFY_UNPUBLISH |
|
||||
NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
m_listener = m_poller.AddListener({{""}}, nt::EventFlags::kTopic);
|
||||
for (auto&& childIt : m_storage.GetChildren()) {
|
||||
auto id = childIt.key();
|
||||
auto typePtr = m_typeCache.FindValue(id);
|
||||
@@ -51,8 +48,8 @@ NetworkTablesProvider::NetworkTablesProvider(Storage& storage,
|
||||
}
|
||||
});
|
||||
storage.SetCustomClear([this, &storage] {
|
||||
m_topicPoller.Remove(m_topicListener);
|
||||
m_topicListener = 0;
|
||||
m_poller.RemoveListener(m_listener);
|
||||
m_listener = 0;
|
||||
for (auto&& modelEntry : m_modelEntries) {
|
||||
modelEntry->model.reset();
|
||||
}
|
||||
@@ -102,59 +99,60 @@ void NetworkTablesProvider::DisplayMenu() {
|
||||
void NetworkTablesProvider::Update() {
|
||||
Provider::Update();
|
||||
|
||||
// add/remove entries from NT changes
|
||||
for (auto&& event : m_topicPoller.ReadQueue()) {
|
||||
// look for .type fields
|
||||
if (!wpi::ends_with(event.info.name, "/.type") ||
|
||||
event.info.type != NT_STRING || event.info.type_str != "string") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (event.flags & NT_TOPIC_NOTIFY_UNPUBLISH) {
|
||||
auto it = m_topicMap.find(event.info.topic);
|
||||
if (it != m_topicMap.end()) {
|
||||
m_valuePoller.Remove(it->second.listener);
|
||||
m_topicMap.erase(it);
|
||||
for (auto&& event : m_poller.ReadQueue()) {
|
||||
if (auto info = event.GetTopicInfo()) {
|
||||
// add/remove entries from NT changes
|
||||
// look for .type fields
|
||||
if (!wpi::ends_with(info->name, "/.type") || info->type != NT_STRING ||
|
||||
info->type_str != "string") {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto it2 = std::find_if(
|
||||
m_viewEntries.begin(), m_viewEntries.end(), [&](const auto& elem) {
|
||||
return static_cast<Entry*>(elem->modelEntry)
|
||||
->typeTopic.GetHandle() == event.info.topic;
|
||||
});
|
||||
if (it2 != m_viewEntries.end()) {
|
||||
m_viewEntries.erase(it2);
|
||||
if (event.flags & nt::EventFlags::kUnpublish) {
|
||||
auto it = m_topicMap.find(info->topic);
|
||||
if (it != m_topicMap.end()) {
|
||||
m_poller.RemoveListener(it->second.listener);
|
||||
m_topicMap.erase(it);
|
||||
}
|
||||
|
||||
auto it2 = std::find_if(
|
||||
m_viewEntries.begin(), m_viewEntries.end(), [&](const auto& elem) {
|
||||
return static_cast<Entry*>(elem->modelEntry)
|
||||
->typeTopic.GetHandle() == info->topic;
|
||||
});
|
||||
if (it2 != m_viewEntries.end()) {
|
||||
m_viewEntries.erase(it2);
|
||||
}
|
||||
} else if (event.flags & nt::EventFlags::kPublish) {
|
||||
// subscribe to it; use a subscriber so we only get string values
|
||||
SubListener sublistener;
|
||||
sublistener.subscriber = nt::StringTopic{info->topic}.Subscribe("");
|
||||
sublistener.listener = m_poller.AddListener(
|
||||
sublistener.subscriber,
|
||||
nt::EventFlags::kValueAll | nt::EventFlags::kImmediate);
|
||||
m_topicMap.try_emplace(info->topic, std::move(sublistener));
|
||||
}
|
||||
} else if (event.flags & NT_TOPIC_NOTIFY_PUBLISH) {
|
||||
// subscribe to it
|
||||
SubListener sublistener;
|
||||
sublistener.subscriber = nt::StringTopic{event.info.topic}.Subscribe("");
|
||||
sublistener.listener =
|
||||
m_valuePoller.Add(sublistener.subscriber,
|
||||
NT_VALUE_NOTIFY_LOCAL | NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
m_topicMap.try_emplace(event.info.topic, std::move(sublistener));
|
||||
} else if (auto valueData = event.GetValueEventData()) {
|
||||
// handle actual .type strings
|
||||
if (!valueData->value.IsString()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// only handle ones where we have a builder
|
||||
auto builderIt = m_typeMap.find(valueData->value.GetString());
|
||||
if (builderIt == m_typeMap.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto topicName = nt::GetTopicName(valueData->topic);
|
||||
auto tableName = wpi::drop_back(topicName, 6);
|
||||
|
||||
GetOrCreateView(builderIt->second, nt::Topic{valueData->topic},
|
||||
tableName);
|
||||
// cache the type
|
||||
m_typeCache.SetString(tableName, valueData->value.GetString());
|
||||
}
|
||||
}
|
||||
|
||||
// handle actual .type strings
|
||||
for (auto&& event : m_valuePoller.ReadQueue()) {
|
||||
if (!event.value.IsString()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// only handle ones where we have a builder
|
||||
auto builderIt = m_typeMap.find(event.value.GetString());
|
||||
if (builderIt == m_typeMap.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto topicName = nt::GetTopicName(event.topic);
|
||||
auto tableName = wpi::drop_back(topicName, 6);
|
||||
|
||||
GetOrCreateView(builderIt->second, nt::Topic{event.topic}, tableName);
|
||||
// cache the type
|
||||
m_typeCache.SetString(tableName, event.value.GetString());
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkTablesProvider::Register(std::string_view typeName,
|
||||
|
||||
@@ -12,9 +12,8 @@
|
||||
|
||||
#include <networktables/MultiSubscriber.h>
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
#include <networktables/NetworkTableListener.h>
|
||||
#include <networktables/StringTopic.h>
|
||||
#include <networktables/TopicListener.h>
|
||||
#include <networktables/ValueListener.h>
|
||||
#include <ntcore_cpp.h>
|
||||
|
||||
#include "glass/other/Field2D.h"
|
||||
@@ -48,8 +47,7 @@ class NTField2DModel : public Field2DModel {
|
||||
nt::NetworkTableInstance m_inst;
|
||||
nt::MultiSubscriber m_tableSub;
|
||||
nt::StringTopic m_nameTopic;
|
||||
nt::TopicListenerPoller m_topicListener;
|
||||
nt::ValueListenerPoller m_valueListener;
|
||||
nt::NetworkTableListenerPoller m_poller;
|
||||
std::string m_nameValue;
|
||||
|
||||
class ObjectModel;
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
#include <frc/geometry/Translation2d.h>
|
||||
#include <networktables/MultiSubscriber.h>
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
#include <networktables/TopicListener.h>
|
||||
#include <networktables/ValueListener.h>
|
||||
#include <networktables/NetworkTableListener.h>
|
||||
|
||||
#include "glass/other/Mechanism2D.h"
|
||||
|
||||
@@ -50,8 +49,7 @@ class NTMechanism2DModel : public Mechanism2DModel {
|
||||
nt::Topic m_nameTopic;
|
||||
nt::Topic m_dimensionsTopic;
|
||||
nt::Topic m_bgColorTopic;
|
||||
nt::TopicListenerPoller m_topicListener;
|
||||
nt::ValueListenerPoller m_valueListener;
|
||||
nt::NetworkTableListenerPoller m_poller;
|
||||
|
||||
std::string m_nameValue;
|
||||
frc::Translation2d m_dimensionsValue;
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
#include <networktables/TopicListener.h>
|
||||
#include <networktables/ValueListener.h>
|
||||
#include <networktables/NetworkTableListener.h>
|
||||
#include <ntcore_cpp.h>
|
||||
#include <wpi/DenseMap.h>
|
||||
#include <wpi/json.h>
|
||||
@@ -84,8 +83,10 @@ class NetworkTablesModel : public Model {
|
||||
Entry& operator=(const Entry&) = delete;
|
||||
~Entry();
|
||||
|
||||
void UpdateTopic(nt::TopicNotification&& event) {
|
||||
UpdateInfo(std::move(event.info));
|
||||
void UpdateTopic(nt::Event&& event) {
|
||||
if (std::holds_alternative<nt::TopicInfo>(event.data)) {
|
||||
UpdateInfo(std::get<nt::TopicInfo>(std::move(event.data)));
|
||||
}
|
||||
}
|
||||
void UpdateInfo(nt::TopicInfo&& info_);
|
||||
|
||||
@@ -179,9 +180,7 @@ class NetworkTablesModel : public Model {
|
||||
void UpdateClients(std::span<const uint8_t> data);
|
||||
|
||||
nt::NetworkTableInstance m_inst;
|
||||
NT_MultiSubscriber m_subscriber;
|
||||
nt::TopicListenerPoller m_topicPoller;
|
||||
nt::ValueListenerPoller m_valuePoller;
|
||||
nt::NetworkTableListenerPoller m_poller;
|
||||
wpi::DenseMap<NT_Topic, std::unique_ptr<Entry>> m_entries;
|
||||
|
||||
// sorted by name
|
||||
|
||||
@@ -10,10 +10,9 @@
|
||||
#include <vector>
|
||||
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
#include <networktables/NetworkTableListener.h>
|
||||
#include <networktables/StringTopic.h>
|
||||
#include <networktables/Topic.h>
|
||||
#include <networktables/TopicListener.h>
|
||||
#include <networktables/ValueListener.h>
|
||||
#include <wpi/DenseMap.h>
|
||||
#include <wpi/StringMap.h>
|
||||
|
||||
@@ -79,9 +78,8 @@ class NetworkTablesProvider : private Provider<detail::NTProviderFunctions> {
|
||||
void Update() override;
|
||||
|
||||
nt::NetworkTableInstance m_inst;
|
||||
nt::TopicListenerPoller m_topicPoller;
|
||||
NT_TopicListener m_topicListener{0};
|
||||
nt::ValueListenerPoller m_valuePoller;
|
||||
nt::NetworkTableListenerPoller m_poller;
|
||||
NT_Listener m_listener{0};
|
||||
|
||||
// cached mapping from table name to type string
|
||||
Storage& m_typeCache;
|
||||
@@ -96,7 +94,7 @@ class NetworkTablesProvider : private Provider<detail::NTProviderFunctions> {
|
||||
|
||||
struct SubListener {
|
||||
nt::StringSubscriber subscriber;
|
||||
NT_ValueListener listener;
|
||||
NT_Listener listener;
|
||||
};
|
||||
|
||||
// mapping from .type topic to subscriber/listener
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <FRC_FPGA_ChipObject/fpgainterfacecapi/NiFpga_HMB.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "ConstantsInternal.h"
|
||||
#include "DigitalInternal.h"
|
||||
#include "FPGACalls.h"
|
||||
#include "HALInitializer.h"
|
||||
#include "HALInternal.h"
|
||||
#include "PortsInternal.h"
|
||||
@@ -21,51 +21,6 @@
|
||||
|
||||
using namespace hal;
|
||||
|
||||
extern "C" {
|
||||
NiFpga_Status NiFpga_ClientFunctionCall(NiFpga_Session session, uint32_t group,
|
||||
uint32_t functionId,
|
||||
const void* inBuffer,
|
||||
size_t inBufferSize, void* outBuffer,
|
||||
size_t outBufferSize);
|
||||
} // extern "C"
|
||||
|
||||
// Shim for broken ChipObject function
|
||||
static const uint32_t clientFeature_hostMemoryBuffer = 0;
|
||||
static const uint32_t hostMemoryBufferFunction_open = 2;
|
||||
|
||||
// Input arguments for HMB open
|
||||
struct AtomicHMBOpenInputs {
|
||||
const char* memoryName;
|
||||
};
|
||||
|
||||
// Output arguments for HMB open
|
||||
struct AtomicHMBOpenOutputs {
|
||||
size_t size;
|
||||
void* virtualAddress;
|
||||
};
|
||||
|
||||
static NiFpga_Status OpenHostMemoryBuffer(NiFpga_Session session,
|
||||
const char* memoryName,
|
||||
void** virtualAddress, size_t* size) {
|
||||
struct AtomicHMBOpenOutputs outputs;
|
||||
|
||||
struct AtomicHMBOpenInputs inputs;
|
||||
inputs.memoryName = memoryName;
|
||||
|
||||
NiFpga_Status retval = NiFpga_ClientFunctionCall(
|
||||
session, clientFeature_hostMemoryBuffer, hostMemoryBufferFunction_open,
|
||||
&inputs, sizeof(struct AtomicHMBOpenInputs), &outputs,
|
||||
sizeof(struct AtomicHMBOpenOutputs));
|
||||
if (NiFpga_IsError(retval)) {
|
||||
return retval;
|
||||
}
|
||||
*virtualAddress = outputs.virtualAddress;
|
||||
if (size) {
|
||||
*size = outputs.size;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct AddressableLED {
|
||||
std::unique_ptr<tLED> led;
|
||||
@@ -89,6 +44,8 @@ void InitializeAddressableLED() {
|
||||
}
|
||||
} // namespace hal::init
|
||||
|
||||
static constexpr const char* HmbName = "HMB_0_LED";
|
||||
|
||||
extern "C" {
|
||||
|
||||
HAL_AddressableLEDHandle HAL_InitializeAddressableLED(
|
||||
@@ -146,8 +103,8 @@ HAL_AddressableLEDHandle HAL_InitializeAddressableLED(
|
||||
|
||||
uint32_t session = led->led->getSystemInterface()->getHandle();
|
||||
|
||||
*status = OpenHostMemoryBuffer(session, "HMB_0_LED", &led->ledBuffer,
|
||||
&led->ledBufferSize);
|
||||
*status = hal::HAL_NiFpga_OpenHmb(session, HmbName, &led->ledBufferSize,
|
||||
&led->ledBuffer);
|
||||
|
||||
if (*status != 0) {
|
||||
addressableLEDHandles->Free(handle);
|
||||
@@ -158,6 +115,12 @@ HAL_AddressableLEDHandle HAL_InitializeAddressableLED(
|
||||
}
|
||||
|
||||
void HAL_FreeAddressableLED(HAL_AddressableLEDHandle handle) {
|
||||
auto led = addressableLEDHandles->Get(handle);
|
||||
if (!led) {
|
||||
return;
|
||||
}
|
||||
uint32_t session = led->led->getSystemInterface()->getHandle();
|
||||
hal::HAL_NiFpga_CloseHmb(session, HmbName);
|
||||
addressableLEDHandles->Free(handle);
|
||||
}
|
||||
|
||||
|
||||
66
hal/src/main/native/athena/FPGACalls.cpp
Normal file
66
hal/src/main/native/athena/FPGACalls.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "FPGACalls.h"
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
#include "dlfcn.h"
|
||||
#include "hal/Errors.h"
|
||||
|
||||
static void* NiFpgaLibrary = nullptr;
|
||||
|
||||
namespace hal {
|
||||
HAL_NiFpga_ReserveIrqContextFunc HAL_NiFpga_ReserveIrqContext;
|
||||
HAL_NiFpga_UnreserveIrqContextFunc HAL_NiFpga_UnreserveIrqContext;
|
||||
HAL_NiFpga_WaitOnIrqsFunc HAL_NiFpga_WaitOnIrqs;
|
||||
HAL_NiFpga_AcknowledgeIrqsFunc HAL_NiFpga_AcknowledgeIrqs;
|
||||
HAL_NiFpga_OpenHmbFunc HAL_NiFpga_OpenHmb;
|
||||
HAL_NiFpga_CloseHmbFunc HAL_NiFpga_CloseHmb;
|
||||
|
||||
namespace init {
|
||||
int InitializeFPGA() {
|
||||
NiFpgaLibrary = dlopen("libNiFpga.so", RTLD_LAZY);
|
||||
if (!NiFpgaLibrary) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||
HAL_NiFpga_ReserveIrqContext =
|
||||
reinterpret_cast<HAL_NiFpga_ReserveIrqContextFunc>(
|
||||
dlsym(NiFpgaLibrary, "NiFpgaDll_ReserveIrqContext"));
|
||||
HAL_NiFpga_UnreserveIrqContext =
|
||||
reinterpret_cast<HAL_NiFpga_UnreserveIrqContextFunc>(
|
||||
dlsym(NiFpgaLibrary, "NiFpgaDll_UnreserveIrqContext"));
|
||||
HAL_NiFpga_WaitOnIrqs = reinterpret_cast<HAL_NiFpga_WaitOnIrqsFunc>(
|
||||
dlsym(NiFpgaLibrary, "NiFpgaDll_WaitOnIrqs"));
|
||||
HAL_NiFpga_AcknowledgeIrqs = reinterpret_cast<HAL_NiFpga_AcknowledgeIrqsFunc>(
|
||||
dlsym(NiFpgaLibrary, "NiFpgaDll_AcknowledgeIrqs"));
|
||||
HAL_NiFpga_OpenHmb = reinterpret_cast<HAL_NiFpga_OpenHmbFunc>(
|
||||
dlsym(NiFpgaLibrary, "NiFpgaDll_OpenHmb"));
|
||||
HAL_NiFpga_CloseHmb = reinterpret_cast<HAL_NiFpga_CloseHmbFunc>(
|
||||
dlsym(NiFpgaLibrary, "NiFpgaDll_CloseHmb"));
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
if (HAL_NiFpga_ReserveIrqContext == nullptr ||
|
||||
HAL_NiFpga_UnreserveIrqContext == nullptr ||
|
||||
HAL_NiFpga_WaitOnIrqs == nullptr ||
|
||||
HAL_NiFpga_AcknowledgeIrqs == nullptr || HAL_NiFpga_OpenHmb == nullptr ||
|
||||
HAL_NiFpga_CloseHmb == nullptr) {
|
||||
HAL_NiFpga_ReserveIrqContext = nullptr;
|
||||
HAL_NiFpga_UnreserveIrqContext = nullptr;
|
||||
HAL_NiFpga_WaitOnIrqs = nullptr;
|
||||
HAL_NiFpga_AcknowledgeIrqs = nullptr;
|
||||
HAL_NiFpga_OpenHmb = nullptr;
|
||||
HAL_NiFpga_CloseHmb = nullptr;
|
||||
dlclose(NiFpgaLibrary);
|
||||
NiFpgaLibrary = nullptr;
|
||||
return NO_AVAILABLE_RESOURCES;
|
||||
}
|
||||
|
||||
return HAL_SUCCESS;
|
||||
}
|
||||
} // namespace init
|
||||
} // namespace hal
|
||||
46
hal/src/main/native/athena/FPGACalls.h
Normal file
46
hal/src/main/native/athena/FPGACalls.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <FRC_FPGA_ChipObject/fpgainterfacecapi/NiFpga.h>
|
||||
|
||||
namespace hal {
|
||||
namespace init {
|
||||
[[nodiscard]] int InitializeFPGA();
|
||||
} // namespace init
|
||||
|
||||
using HAL_NiFpga_ReserveIrqContextFunc =
|
||||
NiFpga_Status (*)(NiFpga_Session session, NiFpga_IrqContext* context);
|
||||
|
||||
extern HAL_NiFpga_ReserveIrqContextFunc HAL_NiFpga_ReserveIrqContext;
|
||||
|
||||
using HAL_NiFpga_UnreserveIrqContextFunc =
|
||||
NiFpga_Status (*)(NiFpga_Session session, NiFpga_IrqContext context);
|
||||
|
||||
extern HAL_NiFpga_UnreserveIrqContextFunc HAL_NiFpga_UnreserveIrqContext;
|
||||
|
||||
using HAL_NiFpga_WaitOnIrqsFunc = NiFpga_Status (*)(
|
||||
NiFpga_Session session, NiFpga_IrqContext context, uint32_t irqs,
|
||||
uint32_t timeout, uint32_t* irqsAsserted, NiFpga_Bool* timedOut);
|
||||
|
||||
extern HAL_NiFpga_WaitOnIrqsFunc HAL_NiFpga_WaitOnIrqs;
|
||||
|
||||
using HAL_NiFpga_AcknowledgeIrqsFunc = NiFpga_Status (*)(NiFpga_Session session,
|
||||
uint32_t irqs);
|
||||
|
||||
extern HAL_NiFpga_AcknowledgeIrqsFunc HAL_NiFpga_AcknowledgeIrqs;
|
||||
|
||||
using HAL_NiFpga_OpenHmbFunc = NiFpga_Status (*)(const NiFpga_Session session,
|
||||
const char* memoryName,
|
||||
size_t* memorySize,
|
||||
void** virtualAddress);
|
||||
|
||||
extern HAL_NiFpga_OpenHmbFunc HAL_NiFpga_OpenHmb;
|
||||
|
||||
using HAL_NiFpga_CloseHmbFunc = NiFpga_Status (*)(const NiFpga_Session session,
|
||||
const char* memoryName);
|
||||
|
||||
extern HAL_NiFpga_CloseHmbFunc HAL_NiFpga_CloseHmb;
|
||||
} // namespace hal
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <wpi/mutex.h>
|
||||
#include <wpi/timestamp.h>
|
||||
|
||||
#include "FPGACalls.h"
|
||||
#include "HALInitializer.h"
|
||||
#include "HALInternal.h"
|
||||
#include "hal/ChipObject.h"
|
||||
@@ -394,6 +395,11 @@ HAL_Bool HAL_Initialize(int32_t timeout, int32_t mode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int fpgaInit = hal::init::InitializeFPGA();
|
||||
if (fpgaInit != HAL_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hal::init::InitializeHAL();
|
||||
|
||||
hal::init::HAL_IsInitialized.store(true);
|
||||
@@ -426,11 +432,7 @@ HAL_Bool HAL_Initialize(int32_t timeout, int32_t mode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
status = InterruptManager::Initialize(global->getSystemInterface());
|
||||
|
||||
if (status != 0) {
|
||||
return false;
|
||||
}
|
||||
InterruptManager::Initialize(global->getSystemInterface());
|
||||
|
||||
hal::InitializeDriverStation();
|
||||
|
||||
|
||||
@@ -6,34 +6,11 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "FPGACalls.h"
|
||||
#include "HALInternal.h"
|
||||
#include "dlfcn.h"
|
||||
#include "hal/Errors.h"
|
||||
|
||||
// Low level FPGA calls
|
||||
using HAL_NiFpga_ReserveIrqContextFunc =
|
||||
NiFpga_Status (*)(NiFpga_Session session, NiFpga_IrqContext* context);
|
||||
|
||||
static HAL_NiFpga_ReserveIrqContextFunc HAL_NiFpga_ReserveIrqContext;
|
||||
|
||||
using HAL_NiFpga_UnreserveIrqContextFunc =
|
||||
NiFpga_Status (*)(NiFpga_Session session, NiFpga_IrqContext context);
|
||||
|
||||
static HAL_NiFpga_UnreserveIrqContextFunc HAL_NiFpga_UnreserveIrqContext;
|
||||
|
||||
using HAL_NiFpga_WaitOnIrqsFunc = NiFpga_Status (*)(
|
||||
NiFpga_Session session, NiFpga_IrqContext context, uint32_t irqs,
|
||||
uint32_t timeout, uint32_t* irqsAsserted, NiFpga_Bool* timedOut);
|
||||
|
||||
static HAL_NiFpga_WaitOnIrqsFunc HAL_NiFpga_WaitOnIrqs;
|
||||
|
||||
using HAL_NiFpga_AcknowledgeIrqsFunc = NiFpga_Status (*)(NiFpga_Session session,
|
||||
uint32_t irqs);
|
||||
|
||||
static HAL_NiFpga_AcknowledgeIrqsFunc HAL_NiFpga_AcknowledgeIrqs;
|
||||
|
||||
static void* NiFpgaLibrary = nullptr;
|
||||
|
||||
using namespace hal;
|
||||
|
||||
InterruptManager& InterruptManager::GetInstance() {
|
||||
@@ -41,37 +18,9 @@ InterruptManager& InterruptManager::GetInstance() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
int32_t InterruptManager::Initialize(tSystemInterface* baseSystem) {
|
||||
void InterruptManager::Initialize(tSystemInterface* baseSystem) {
|
||||
auto& manager = GetInstance();
|
||||
manager.fpgaSession = baseSystem->getHandle();
|
||||
|
||||
NiFpgaLibrary = dlopen("libNiFpga.so", RTLD_LAZY);
|
||||
if (!NiFpgaLibrary) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||
HAL_NiFpga_ReserveIrqContext =
|
||||
reinterpret_cast<HAL_NiFpga_ReserveIrqContextFunc>(
|
||||
dlsym(NiFpgaLibrary, "NiFpgaDll_ReserveIrqContext"));
|
||||
HAL_NiFpga_UnreserveIrqContext =
|
||||
reinterpret_cast<HAL_NiFpga_UnreserveIrqContextFunc>(
|
||||
dlsym(NiFpgaLibrary, "NiFpgaDll_UnreserveIrqContext"));
|
||||
HAL_NiFpga_WaitOnIrqs = reinterpret_cast<HAL_NiFpga_WaitOnIrqsFunc>(
|
||||
dlsym(NiFpgaLibrary, "NiFpgaDll_WaitOnIrqs"));
|
||||
HAL_NiFpga_AcknowledgeIrqs = reinterpret_cast<HAL_NiFpga_AcknowledgeIrqsFunc>(
|
||||
dlsym(NiFpgaLibrary, "NiFpgaDll_AcknowledgeIrqs"));
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
if (HAL_NiFpga_ReserveIrqContext == nullptr ||
|
||||
HAL_NiFpga_UnreserveIrqContext == nullptr ||
|
||||
HAL_NiFpga_WaitOnIrqs == nullptr ||
|
||||
HAL_NiFpga_AcknowledgeIrqs == nullptr) {
|
||||
return NO_AVAILABLE_RESOURCES;
|
||||
}
|
||||
|
||||
return HAL_SUCCESS;
|
||||
}
|
||||
|
||||
NiFpga_IrqContext InterruptManager::GetContext() noexcept {
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace hal {
|
||||
class InterruptManager {
|
||||
public:
|
||||
static InterruptManager& GetInstance();
|
||||
static int32_t Initialize(tSystemInterface* baseSystem);
|
||||
static void Initialize(tSystemInterface* baseSystem);
|
||||
|
||||
NiFpga_IrqContext GetContext() noexcept;
|
||||
void ReleaseContext(NiFpga_IrqContext context) noexcept;
|
||||
|
||||
@@ -168,17 +168,26 @@ int32_t HAL_SendConsoleLine(const char* line) {
|
||||
}
|
||||
|
||||
int32_t HAL_GetControlWord(HAL_ControlWord* controlWord) {
|
||||
if (gShutdown) {
|
||||
return INCOMPATIBLE_STATE;
|
||||
}
|
||||
std::scoped_lock lock{driverStation->cacheMutex};
|
||||
*controlWord = newestControlWord;
|
||||
return 0;
|
||||
}
|
||||
|
||||
HAL_AllianceStationID HAL_GetAllianceStation(int32_t* status) {
|
||||
if (gShutdown) {
|
||||
return HAL_AllianceStationID_kRed1;
|
||||
}
|
||||
std::scoped_lock lock{driverStation->cacheMutex};
|
||||
return currentRead->allianceStation;
|
||||
}
|
||||
|
||||
int32_t HAL_GetJoystickAxes(int32_t joystickNum, HAL_JoystickAxes* axes) {
|
||||
if (gShutdown) {
|
||||
return INCOMPATIBLE_STATE;
|
||||
}
|
||||
CHECK_JOYSTICK_NUMBER(joystickNum);
|
||||
std::scoped_lock lock{driverStation->cacheMutex};
|
||||
*axes = currentRead->axes[joystickNum];
|
||||
@@ -186,6 +195,9 @@ int32_t HAL_GetJoystickAxes(int32_t joystickNum, HAL_JoystickAxes* axes) {
|
||||
}
|
||||
|
||||
int32_t HAL_GetJoystickPOVs(int32_t joystickNum, HAL_JoystickPOVs* povs) {
|
||||
if (gShutdown) {
|
||||
return INCOMPATIBLE_STATE;
|
||||
}
|
||||
CHECK_JOYSTICK_NUMBER(joystickNum);
|
||||
std::scoped_lock lock{driverStation->cacheMutex};
|
||||
*povs = currentRead->povs[joystickNum];
|
||||
@@ -194,6 +206,9 @@ int32_t HAL_GetJoystickPOVs(int32_t joystickNum, HAL_JoystickPOVs* povs) {
|
||||
|
||||
int32_t HAL_GetJoystickButtons(int32_t joystickNum,
|
||||
HAL_JoystickButtons* buttons) {
|
||||
if (gShutdown) {
|
||||
return INCOMPATIBLE_STATE;
|
||||
}
|
||||
CHECK_JOYSTICK_NUMBER(joystickNum);
|
||||
std::scoped_lock lock{driverStation->cacheMutex};
|
||||
*buttons = currentRead->buttons[joystickNum];
|
||||
@@ -202,6 +217,9 @@ int32_t HAL_GetJoystickButtons(int32_t joystickNum,
|
||||
|
||||
void HAL_GetAllJoystickData(HAL_JoystickAxes* axes, HAL_JoystickPOVs* povs,
|
||||
HAL_JoystickButtons* buttons) {
|
||||
if (gShutdown) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{driverStation->cacheMutex};
|
||||
std::memcpy(axes, currentRead->axes, sizeof(currentRead->axes));
|
||||
std::memcpy(povs, currentRead->povs, sizeof(currentRead->povs));
|
||||
@@ -252,6 +270,9 @@ int32_t HAL_SetJoystickOutputs(int32_t joystickNum, int64_t outputs,
|
||||
}
|
||||
|
||||
double HAL_GetMatchTime(int32_t* status) {
|
||||
if (gShutdown) {
|
||||
return 0;
|
||||
}
|
||||
std::scoped_lock lock{driverStation->cacheMutex};
|
||||
return currentRead->matchTime;
|
||||
}
|
||||
@@ -282,6 +303,9 @@ void HAL_ObserveUserProgramTest(void) {
|
||||
}
|
||||
|
||||
void HAL_RefreshDSData(void) {
|
||||
if (gShutdown) {
|
||||
return;
|
||||
}
|
||||
HAL_ControlWord controlWord;
|
||||
std::memset(&controlWord, 0, sizeof(controlWord));
|
||||
controlWord.enabled = SimDriverStationData->enabled;
|
||||
@@ -313,6 +337,9 @@ void HAL_RemoveNewDataEventHandle(WPI_EventHandle handle) {
|
||||
}
|
||||
|
||||
HAL_Bool HAL_GetOutputsEnabled(void) {
|
||||
if (gShutdown) {
|
||||
return false;
|
||||
}
|
||||
std::scoped_lock lock{driverStation->cacheMutex};
|
||||
return newestControlWord.enabled && newestControlWord.dsAttached;
|
||||
}
|
||||
@@ -321,6 +348,9 @@ HAL_Bool HAL_GetOutputsEnabled(void) {
|
||||
|
||||
namespace hal {
|
||||
void NewDriverStationData() {
|
||||
if (gShutdown) {
|
||||
return;
|
||||
}
|
||||
cacheToUpdate->Update();
|
||||
{
|
||||
std::scoped_lock lock{driverStation->cacheMutex};
|
||||
|
||||
@@ -58,7 +58,7 @@ def simProjects = ['halsim_gui']
|
||||
deploy {
|
||||
targets {
|
||||
roborio(RemoteTarget) {
|
||||
directory = '/home/admin'
|
||||
directory = '/home/lvuser'
|
||||
maxChannels = 4
|
||||
locations {
|
||||
ssh(SshDeployLocation) {
|
||||
@@ -78,7 +78,8 @@ deploy {
|
||||
artifacts {
|
||||
all {
|
||||
predeploy << { ctx ->
|
||||
ctx.execute('/usr/local/frc/bin/frcKillRobot.sh -t')
|
||||
ctx.execute('. /etc/profile.d/natinst-path.sh; /usr/local/frc/bin/frcKillRobot.sh -t 2> /dev/null')
|
||||
ctx.execute("sed -i -e 's/\"exec /\"/' /usr/local/frc/bin/frcRunRobot.sh")
|
||||
}
|
||||
postdeploy << { ctx ->
|
||||
ctx.execute("sync")
|
||||
@@ -92,6 +93,9 @@ deploy {
|
||||
excludes.add('**/*.so.debug')
|
||||
excludes.add('**/*.so.*.debug')
|
||||
postdeploy << { ctx ->
|
||||
ctx.execute("echo '/home/lvuser/myRobotCpp' > /home/lvuser/robotCommand")
|
||||
ctx.execute("chmod +x /home/lvuser/robotCommand; chown lvuser /home/lvuser/robotCommand")
|
||||
ctx.execute("setcap cap_sys_nice+eip \"/home/lvuser/myRobotCpp\"")
|
||||
ctx.execute('chmod +x myRobotCpp')
|
||||
}
|
||||
}
|
||||
@@ -99,18 +103,28 @@ deploy {
|
||||
myRobotCppStatic(NativeExecutableArtifact) {
|
||||
libraryDirectory = '/usr/local/frc/third-party/lib'
|
||||
postdeploy << { ctx ->
|
||||
ctx.execute("echo '/home/lvuser/myRobotCppStatic' > /home/lvuser/robotCommand")
|
||||
ctx.execute("chmod +x /home/lvuser/robotCommand; chown lvuser /home/lvuser/robotCommand")
|
||||
ctx.execute("setcap cap_sys_nice+eip \"/home/lvuser/myRobotCppStatic\"")
|
||||
ctx.execute('chmod +x myRobotCppStatic')
|
||||
}
|
||||
}
|
||||
|
||||
myRobotCppJava(NativeExecutableArtifact) {
|
||||
libraryDirectory = '/usr/local/frc/third-party/lib'
|
||||
def excludes = getLibraryFilter().getExcludes()
|
||||
excludes.add('**/*.so.debug')
|
||||
excludes.add('**/*.so.*.debug')
|
||||
}
|
||||
|
||||
jre(WPIJREArtifact) {
|
||||
}
|
||||
|
||||
myRobotJava(JavaArtifact) {
|
||||
jarTask = shadowJar
|
||||
postdeploy << { ctx ->
|
||||
ctx.execute("echo '/usr/local/frc/JRE/bin/java -XX:+UseConcMarkSweepGC -Djava.library.path=/usr/local/frc/third-party/lib -Djava.lang.invoke.stringConcat=BC_SB -jar /home/admin/myRobot-all.jar' > /home/admin/myRobotJavaRun")
|
||||
ctx.execute("chmod +x /home/admin/myRobotJavaRun; chown lvuser /home/admin/myRobotJavaRun")
|
||||
ctx.execute("echo '/usr/local/frc/JRE/bin/java -XX:+UseG1GC -XX:MaxGCPauseMillis=1 -XX:GCTimeRatio=1 -Djava.library.path=/usr/local/frc/third-party/lib -Djava.lang.invoke.stringConcat=BC_SB -jar /home/lvuser/myRobot-all.jar' > /home/lvuser/robotCommand")
|
||||
ctx.execute("chmod +x /home/lvuser/robotCommand; chown lvuser /home/lvuser/robotCommand")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,7 +136,7 @@ tasks.register('deployJava') {
|
||||
try {
|
||||
dependsOn tasks.named('deployjreroborio')
|
||||
dependsOn tasks.named('deploymyRobotJavaroborio')
|
||||
dependsOn tasks.named('deploymyRobotCpproborio') // Deploying shared C++ is how to get the Java shared libraries.
|
||||
dependsOn tasks.named('deploymyRobotCppJavaroborio') // Deploying shared C++ is how to get the Java shared libraries.
|
||||
} catch (ignored) {
|
||||
}
|
||||
}
|
||||
@@ -161,6 +175,7 @@ model {
|
||||
if (binary.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
|
||||
if (binary.buildType.name == 'debug') {
|
||||
deploy.targets.roborio.artifacts.myRobotCpp.binary = binary
|
||||
deploy.targets.roborio.artifacts.myRobotCppJava.binary = binary
|
||||
}
|
||||
}
|
||||
lib project: ':wpilibNewCommands', library: 'wpilibNewCommands', linkage: 'shared'
|
||||
|
||||
@@ -35,6 +35,7 @@ set_property(TARGET ntcore PROPERTY FOLDER "libraries")
|
||||
|
||||
install(TARGETS ntcore EXPORT ntcore DESTINATION "${main_lib_dest}")
|
||||
install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/ntcore")
|
||||
install(DIRECTORY ${WPILIB_BINARY_DIR}/ntcore/generated/main/native/include/ DESTINATION "${include_dest}/ntcore")
|
||||
|
||||
if (WITH_FLAT_INSTALL)
|
||||
set (ntcore_config_dir ${wpilib_dest})
|
||||
|
||||
@@ -5,11 +5,14 @@
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import edu.wpi.first.util.WPIUtilJNI;
|
||||
import edu.wpi.first.util.concurrent.Event;
|
||||
import edu.wpi.first.util.datalog.DataLog;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -30,6 +33,7 @@ import java.util.function.Consumer;
|
||||
* kept to the NetworkTableInstance returned by this function to keep it from being garbage
|
||||
* collected.
|
||||
*/
|
||||
@SuppressWarnings("PMD.CouplingBetweenObjects")
|
||||
public final class NetworkTableInstance implements AutoCloseable {
|
||||
/**
|
||||
* Client/server mode flag values (as returned by {@link #getNetworkMode()}). This is a bitmask.
|
||||
@@ -62,6 +66,7 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_owned && m_handle != 0) {
|
||||
m_listeners.close();
|
||||
NetworkTablesJNI.destroyInstance(m_handle);
|
||||
}
|
||||
}
|
||||
@@ -350,100 +355,314 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
* Callback Creation Functions
|
||||
*/
|
||||
|
||||
private final ReentrantLock m_connectionListenerLock = new ReentrantLock();
|
||||
private final Map<Integer, Consumer<ConnectionNotification>> m_connectionListeners =
|
||||
new HashMap<>();
|
||||
private int m_connectionListenerPoller;
|
||||
private static class ListenerStorage implements AutoCloseable {
|
||||
private final ReentrantLock m_lock = new ReentrantLock();
|
||||
private final Map<Integer, Consumer<NetworkTableEvent>> m_listeners = new HashMap<>();
|
||||
private Thread m_thread;
|
||||
private int m_poller;
|
||||
private boolean m_waitQueue;
|
||||
private final Event m_waitQueueEvent = new Event();
|
||||
private final Condition m_waitQueueCond = m_lock.newCondition();
|
||||
private final NetworkTableInstance m_inst;
|
||||
|
||||
@SuppressWarnings("PMD.AvoidCatchingThrowable")
|
||||
private void startConnectionListenerThread() {
|
||||
var connectionListenerThread =
|
||||
new Thread(
|
||||
() -> {
|
||||
boolean wasInterrupted = false;
|
||||
while (!Thread.interrupted()) {
|
||||
try {
|
||||
WPIUtilJNI.waitForObject(m_connectionListenerPoller);
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
// don't try to destroy poller, as its handle is likely no longer valid
|
||||
wasInterrupted = true;
|
||||
break;
|
||||
}
|
||||
ConnectionNotification[] events =
|
||||
NetworkTablesJNI.readConnectionListenerQueue(this, m_connectionListenerPoller);
|
||||
for (ConnectionNotification event : events) {
|
||||
Consumer<ConnectionNotification> listener;
|
||||
m_connectionListenerLock.lock();
|
||||
ListenerStorage(NetworkTableInstance inst) {
|
||||
m_inst = inst;
|
||||
}
|
||||
|
||||
int add(String[] prefixes, int mask, Consumer<NetworkTableEvent> listener) {
|
||||
m_lock.lock();
|
||||
try {
|
||||
if (m_poller == 0) {
|
||||
m_poller = NetworkTablesJNI.createListenerPoller(m_inst.getHandle());
|
||||
startThread();
|
||||
}
|
||||
int h = NetworkTablesJNI.addListener(m_poller, prefixes, mask);
|
||||
m_listeners.put(h, listener);
|
||||
return h;
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
int add(int handle, int mask, Consumer<NetworkTableEvent> listener) {
|
||||
m_lock.lock();
|
||||
try {
|
||||
if (m_poller == 0) {
|
||||
m_poller = NetworkTablesJNI.createListenerPoller(m_inst.getHandle());
|
||||
startThread();
|
||||
}
|
||||
int h = NetworkTablesJNI.addListener(m_poller, handle, mask);
|
||||
m_listeners.put(h, listener);
|
||||
return h;
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
int addLogger(int minLevel, int maxLevel, Consumer<NetworkTableEvent> listener) {
|
||||
m_lock.lock();
|
||||
try {
|
||||
if (m_poller == 0) {
|
||||
m_poller = NetworkTablesJNI.createListenerPoller(m_inst.getHandle());
|
||||
startThread();
|
||||
}
|
||||
int h = NetworkTablesJNI.addLogger(m_poller, minLevel, maxLevel);
|
||||
m_listeners.put(h, listener);
|
||||
return h;
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void remove(int listener) {
|
||||
m_lock.lock();
|
||||
try {
|
||||
m_listeners.remove(listener);
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
NetworkTablesJNI.removeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (m_poller != 0) {
|
||||
NetworkTablesJNI.destroyListenerPoller(m_poller);
|
||||
}
|
||||
m_poller = 0;
|
||||
}
|
||||
|
||||
private void startThread() {
|
||||
m_thread =
|
||||
new Thread(
|
||||
() -> {
|
||||
boolean wasInterrupted = false;
|
||||
int[] handles = new int[] { m_poller, m_waitQueueEvent.getHandle() };
|
||||
while (!Thread.interrupted()) {
|
||||
try {
|
||||
listener = m_connectionListeners.get(event.listener);
|
||||
} finally {
|
||||
m_connectionListenerLock.unlock();
|
||||
}
|
||||
if (listener != null) {
|
||||
WPIUtilJNI.waitForObjects(handles);
|
||||
} catch (InterruptedException ex) {
|
||||
m_lock.lock();
|
||||
try {
|
||||
listener.accept(event);
|
||||
} catch (Throwable throwable) {
|
||||
System.err.println(
|
||||
"Unhandled exception during connection listener callback: "
|
||||
+ throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
if (m_waitQueue) {
|
||||
m_waitQueue = false;
|
||||
m_waitQueueCond.signalAll();
|
||||
}
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
Thread.currentThread().interrupt();
|
||||
// don't try to destroy poller, as its handle is likely no longer valid
|
||||
wasInterrupted = true;
|
||||
break;
|
||||
}
|
||||
for (NetworkTableEvent event :
|
||||
NetworkTablesJNI.readListenerQueue(m_inst, m_poller)) {
|
||||
Consumer<NetworkTableEvent> listener;
|
||||
m_lock.lock();
|
||||
try {
|
||||
listener = m_listeners.get(event.listener);
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
if (listener != null) {
|
||||
try {
|
||||
listener.accept(event);
|
||||
} catch (Throwable throwable) {
|
||||
System.err.println(
|
||||
"Unhandled exception during listener callback: "
|
||||
+ throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
m_lock.lock();
|
||||
try {
|
||||
if (m_waitQueue) {
|
||||
m_waitQueue = false;
|
||||
m_waitQueueCond.signalAll();
|
||||
}
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
m_connectionListenerLock.lock();
|
||||
try {
|
||||
if (!wasInterrupted) {
|
||||
NetworkTablesJNI.destroyConnectionListenerPoller(m_connectionListenerPoller);
|
||||
m_lock.lock();
|
||||
try {
|
||||
if (!wasInterrupted) {
|
||||
NetworkTablesJNI.destroyListenerPoller(m_poller);
|
||||
}
|
||||
m_poller = 0;
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
m_connectionListenerPoller = 0;
|
||||
} finally {
|
||||
m_connectionListenerLock.unlock();
|
||||
}
|
||||
},
|
||||
"NTConnectionListener");
|
||||
connectionListenerThread.setDaemon(true);
|
||||
connectionListenerThread.start();
|
||||
}
|
||||
},
|
||||
"NTListener");
|
||||
m_thread.setDaemon(true);
|
||||
m_thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a connection listener.
|
||||
*
|
||||
* @param listener Listener to add
|
||||
* @param immediateNotify Notify listener of all existing connections
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addConnectionListener(
|
||||
Consumer<ConnectionNotification> listener, boolean immediateNotify) {
|
||||
m_connectionListenerLock.lock();
|
||||
try {
|
||||
if (m_connectionListenerPoller == 0) {
|
||||
m_connectionListenerPoller = NetworkTablesJNI.createConnectionListenerPoller(m_handle);
|
||||
startConnectionListenerThread();
|
||||
boolean waitForQueue(double timeout) {
|
||||
m_lock.lock();
|
||||
try {
|
||||
if (m_poller != 0) {
|
||||
m_waitQueue = true;
|
||||
m_waitQueueEvent.set();
|
||||
while (m_waitQueue) {
|
||||
try {
|
||||
if (timeout < 0) {
|
||||
m_waitQueueCond.await();
|
||||
} else {
|
||||
return m_waitQueueCond.await((long) (timeout * 1e9), TimeUnit.NANOSECONDS);
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
m_lock.unlock();
|
||||
}
|
||||
int handle =
|
||||
NetworkTablesJNI.addPolledConnectionListener(m_connectionListenerPoller, immediateNotify);
|
||||
m_connectionListeners.put(handle, listener);
|
||||
return handle;
|
||||
} finally {
|
||||
m_connectionListenerLock.unlock();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private ListenerStorage m_listeners = new ListenerStorage(this);
|
||||
|
||||
/**
|
||||
* Remove a connection listener.
|
||||
*
|
||||
* @param listener Listener handle to remove
|
||||
*/
|
||||
public void removeConnectionListener(int listener) {
|
||||
m_connectionListenerLock.lock();
|
||||
try {
|
||||
m_connectionListeners.remove(listener);
|
||||
} finally {
|
||||
m_connectionListenerLock.unlock();
|
||||
public void removeListener(int listener) {
|
||||
m_listeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the listener queue to be empty. This is primarily useful for deterministic
|
||||
* testing. This blocks until either the listener queue is empty (e.g. there are no
|
||||
* more events that need to be passed along to callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a negative value to
|
||||
* block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
public boolean waitForListenerQueue(double timeout) {
|
||||
return m_listeners.waitForQueue(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a connection listener. The callback function is called asynchronously on a separate
|
||||
* thread, so it's important to use synchronization or atomics when accessing any shared state
|
||||
* from the callback function.
|
||||
*
|
||||
* @param immediateNotify Notify listener of all existing connections
|
||||
* @param listener Listener to add
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addConnectionListener(
|
||||
boolean immediateNotify, Consumer<NetworkTableEvent> listener) {
|
||||
return m_listeners.add(m_handle,
|
||||
NetworkTableEvent.kConnection | (immediateNotify ? NetworkTableEvent.kImmediate : 0),
|
||||
listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener for changes on a particular topic. The callback function is called
|
||||
* asynchronously on a separate thread, so it's important to use synchronization or atomics when
|
||||
* accessing any shared state from the callback function.
|
||||
*
|
||||
* <p>This creates a corresponding internal subscriber with the lifetime of the
|
||||
* listener.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addListener(
|
||||
Topic topic, int eventMask, Consumer<NetworkTableEvent> listener) {
|
||||
if (topic.getInstance().getHandle() != m_handle) {
|
||||
throw new IllegalArgumentException("topic is not from this instance");
|
||||
}
|
||||
NetworkTablesJNI.removeConnectionListener(listener);
|
||||
return m_listeners.add(topic.getHandle(), eventMask, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener for changes on a subscriber. The callback function is called
|
||||
* asynchronously on a separate thread, so it's important to use synchronization or atomics when
|
||||
* accessing any shared state from the callback function. This does NOT keep the subscriber
|
||||
* active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addListener(
|
||||
Subscriber subscriber, int eventMask, Consumer<NetworkTableEvent> listener) {
|
||||
if (subscriber.getTopic().getInstance().getHandle() != m_handle) {
|
||||
throw new IllegalArgumentException("subscriber is not from this instance");
|
||||
}
|
||||
return m_listeners.add(subscriber.getHandle(), eventMask, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener for changes on a subscriber. The callback function is called
|
||||
* asynchronously on a separate thread, so it's important to use synchronization or atomics when
|
||||
* accessing any shared state from the callback function. This does NOT keep the subscriber
|
||||
* active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addListener(
|
||||
MultiSubscriber subscriber, int eventMask, Consumer<NetworkTableEvent> listener) {
|
||||
if (subscriber.getInstance().getHandle() != m_handle) {
|
||||
throw new IllegalArgumentException("subscriber is not from this instance");
|
||||
}
|
||||
return m_listeners.add(subscriber.getHandle(), eventMask, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener for changes on an entry. The callback function is called
|
||||
* asynchronously on a separate thread, so it's important to use synchronization or atomics when
|
||||
* accessing any shared state from the callback function.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addListener(
|
||||
NetworkTableEntry entry, int eventMask, Consumer<NetworkTableEvent> listener) {
|
||||
if (entry.getTopic().getInstance().getHandle() != m_handle) {
|
||||
throw new IllegalArgumentException("entry is not from this instance");
|
||||
}
|
||||
return m_listeners.add(entry.getHandle(), eventMask, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener for changes to topics with names that start with any of the given
|
||||
* prefixes. The callback function is called asynchronously on a separate thread, so it's
|
||||
* important to use synchronization or atomics when accessing any shared state from the callback
|
||||
* function.
|
||||
*
|
||||
* <p>This creates a corresponding internal subscriber with the lifetime of the
|
||||
* listener.
|
||||
*
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addListener(
|
||||
String[] prefixes,
|
||||
int eventMask,
|
||||
Consumer<NetworkTableEvent> listener) {
|
||||
return m_listeners.add(prefixes, eventMask, listener);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -468,8 +687,8 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops local-only operation. startServer or startClient can be called after this call to start a
|
||||
* server or client.
|
||||
* Stops local-only operation. startServer or startClient can be called after this call to start
|
||||
* a server or client.
|
||||
*/
|
||||
public void stopLocal() {
|
||||
NetworkTablesJNI.stopLocal(m_handle);
|
||||
@@ -600,8 +819,8 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets server addresses and ports for client (without restarting client). The client will attempt
|
||||
* to connect to each server in round robin fashion.
|
||||
* Sets server addresses and ports for client (without restarting client). The client will
|
||||
* attempt to connect to each server in round robin fashion.
|
||||
*
|
||||
* @param serverNames array of server names
|
||||
* @param ports array of port numbers (0=default)
|
||||
@@ -664,8 +883,8 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
|
||||
/**
|
||||
* Flushes all updated values immediately to the network. Note: This is rate-limited to protect
|
||||
* the network from flooding. This is primarily useful for synchronizing network updates with user
|
||||
* code.
|
||||
* the network from flooding. This is primarily useful for synchronizing network updates with
|
||||
* user code.
|
||||
*/
|
||||
public void flush() {
|
||||
NetworkTablesJNI.flush(m_handle);
|
||||
@@ -734,98 +953,19 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
NetworkTablesJNI.stopConnectionDataLog(logger);
|
||||
}
|
||||
|
||||
private final ReentrantLock m_loggerLock = new ReentrantLock();
|
||||
private final Map<Integer, Consumer<LogMessage>> m_loggers = new HashMap<>();
|
||||
private int m_loggerPoller;
|
||||
|
||||
@SuppressWarnings("PMD.AvoidCatchingThrowable")
|
||||
private void startLogThread() {
|
||||
var loggerThread =
|
||||
new Thread(
|
||||
() -> {
|
||||
boolean wasInterrupted = false;
|
||||
while (!Thread.interrupted()) {
|
||||
try {
|
||||
WPIUtilJNI.waitForObject(m_loggerPoller);
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
// don't try to destroy poller, as its handle is likely no longer valid
|
||||
wasInterrupted = true;
|
||||
break;
|
||||
}
|
||||
LogMessage[] events = NetworkTablesJNI.readLoggerQueue(this, m_loggerPoller);
|
||||
for (LogMessage event : events) {
|
||||
Consumer<LogMessage> logger;
|
||||
m_loggerLock.lock();
|
||||
try {
|
||||
logger = m_loggers.get(event.logger);
|
||||
} finally {
|
||||
m_loggerLock.unlock();
|
||||
}
|
||||
if (logger != null) {
|
||||
try {
|
||||
logger.accept(event);
|
||||
} catch (Throwable throwable) {
|
||||
System.err.println(
|
||||
"Unhandled exception during logger callback: " + throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_loggerLock.lock();
|
||||
try {
|
||||
if (!wasInterrupted) {
|
||||
NetworkTablesJNI.destroyLoggerPoller(m_loggerPoller);
|
||||
}
|
||||
} finally {
|
||||
m_loggerLock.unlock();
|
||||
}
|
||||
},
|
||||
"NTLogger");
|
||||
loggerThread.setDaemon(true);
|
||||
loggerThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add logger callback function. By default, log messages are sent to stderr; this function sends
|
||||
* log messages with the specified levels to the provided callback function instead. The callback
|
||||
* function will only be called for log messages with level greater than or equal to minLevel and
|
||||
* less than or equal to maxLevel; messages outside this range will be silently ignored.
|
||||
*
|
||||
* @param func log callback function
|
||||
* @param minLevel minimum log level
|
||||
* @param maxLevel maximum log level
|
||||
* @return Logger handle
|
||||
* @param func callback function
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addLogger(Consumer<LogMessage> func, int minLevel, int maxLevel) {
|
||||
m_loggerLock.lock();
|
||||
try {
|
||||
if (m_loggerPoller == 0) {
|
||||
m_loggerPoller = NetworkTablesJNI.createLoggerPoller(m_handle);
|
||||
startLogThread();
|
||||
}
|
||||
int handle = NetworkTablesJNI.addPolledLogger(m_loggerPoller, minLevel, maxLevel);
|
||||
m_loggers.put(handle, func);
|
||||
return handle;
|
||||
} finally {
|
||||
m_loggerLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a logger.
|
||||
*
|
||||
* @param logger Logger handle to remove
|
||||
*/
|
||||
public void removeLogger(int logger) {
|
||||
m_loggerLock.lock();
|
||||
try {
|
||||
m_loggers.remove(logger);
|
||||
} finally {
|
||||
m_loggerLock.unlock();
|
||||
}
|
||||
NetworkTablesJNI.removeLogger(logger);
|
||||
public int addLogger(int minLevel, int maxLevel, Consumer<NetworkTableEvent> func) {
|
||||
return m_listeners.addLogger(minLevel, maxLevel, func);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -199,40 +199,18 @@ public final class NetworkTablesJNI {
|
||||
|
||||
public static native TopicInfo getTopicInfo(NetworkTableInstance inst, int topic);
|
||||
|
||||
public static native int createTopicListenerPoller(int inst);
|
||||
public static native int createListenerPoller(int inst);
|
||||
|
||||
public static native void destroyTopicListenerPoller(int poller);
|
||||
public static native void destroyListenerPoller(int poller);
|
||||
|
||||
public static native int addPolledTopicListener(int poller, String[] prefixes, int flags);
|
||||
public static native int addListener(int poller, String[] prefixes, int mask);
|
||||
|
||||
public static native int addPolledTopicListener(int poller, int handle, int flags);
|
||||
public static native int addListener(int poller, int handle, int mask);
|
||||
|
||||
public static native TopicNotification[] readTopicListenerQueue(
|
||||
public static native NetworkTableEvent[] readListenerQueue(
|
||||
NetworkTableInstance inst, int poller);
|
||||
|
||||
public static native void removeTopicListener(int topicListener);
|
||||
|
||||
public static native int createValueListenerPoller(int inst);
|
||||
|
||||
public static native void destroyValueListenerPoller(int poller);
|
||||
|
||||
public static native int addPolledValueListener(int poller, int subentry, int flags);
|
||||
|
||||
public static native ValueNotification[] readValueListenerQueue(
|
||||
NetworkTableInstance inst, int poller);
|
||||
|
||||
public static native void removeValueListener(int valueListener);
|
||||
|
||||
public static native int createConnectionListenerPoller(int inst);
|
||||
|
||||
public static native void destroyConnectionListenerPoller(int poller);
|
||||
|
||||
public static native int addPolledConnectionListener(int poller, boolean immediateNotify);
|
||||
|
||||
public static native ConnectionNotification[] readConnectionListenerQueue(
|
||||
NetworkTableInstance inst, int poller);
|
||||
|
||||
public static native void removeConnectionListener(int connListener);
|
||||
public static native void removeListener(int listener);
|
||||
|
||||
public static native int getNetworkMode(int inst);
|
||||
|
||||
@@ -287,13 +265,5 @@ public final class NetworkTablesJNI {
|
||||
|
||||
public static native void stopConnectionDataLog(int logger);
|
||||
|
||||
public static native int createLoggerPoller(int inst);
|
||||
|
||||
public static native void destroyLoggerPoller(int poller);
|
||||
|
||||
public static native int addPolledLogger(int poller, int minLevel, int maxLevel);
|
||||
|
||||
public static native LogMessage[] readLoggerQueue(NetworkTableInstance inst, int poller);
|
||||
|
||||
public static native void removeLogger(int logger);
|
||||
public static native int addLogger(int poller, int minLevel, int maxLevel);
|
||||
}
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import edu.wpi.first.util.WPIUtilJNI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Connection listener. This calls back to a callback function when a connection change occurs. The
|
||||
* callback function is called asynchronously on a separate thread, so it's important to use
|
||||
* synchronization or atomics when accessing any shared state from the callback function.
|
||||
*/
|
||||
public final class ConnectionListener implements AutoCloseable {
|
||||
/**
|
||||
* Create a listener for connection changes.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param immediateNotify if notification should be immediately created for existing connections
|
||||
* @param listener Listener function
|
||||
*/
|
||||
public ConnectionListener(
|
||||
NetworkTableInstance inst,
|
||||
boolean immediateNotify,
|
||||
Consumer<ConnectionNotification> listener) {
|
||||
s_lock.lock();
|
||||
try {
|
||||
if (s_poller == 0) {
|
||||
s_inst = inst;
|
||||
s_poller = NetworkTablesJNI.createConnectionListenerPoller(inst.getHandle());
|
||||
startThread();
|
||||
}
|
||||
m_handle = NetworkTablesJNI.addPolledConnectionListener(s_poller, immediateNotify);
|
||||
s_listeners.put(m_handle, listener);
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_handle != 0) {
|
||||
s_lock.lock();
|
||||
try {
|
||||
s_listeners.remove(m_handle);
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
NetworkTablesJNI.removeConnectionListener(m_handle);
|
||||
m_handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
*
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Native handle
|
||||
*/
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
private int m_handle;
|
||||
|
||||
private static final ReentrantLock s_lock = new ReentrantLock();
|
||||
private static final Map<Integer, Consumer<ConnectionNotification>> s_listeners = new HashMap<>();
|
||||
private static Thread s_thread;
|
||||
private static NetworkTableInstance s_inst;
|
||||
private static int s_poller;
|
||||
private static boolean s_waitQueue;
|
||||
private static final Condition s_waitQueueCond = s_lock.newCondition();
|
||||
|
||||
private static void startThread() {
|
||||
s_thread =
|
||||
new Thread(
|
||||
() -> {
|
||||
boolean wasInterrupted = false;
|
||||
while (!Thread.interrupted()) {
|
||||
try {
|
||||
WPIUtilJNI.waitForObject(s_poller);
|
||||
} catch (InterruptedException ex) {
|
||||
s_lock.lock();
|
||||
try {
|
||||
if (s_waitQueue) {
|
||||
s_waitQueue = false;
|
||||
s_waitQueueCond.signalAll();
|
||||
continue;
|
||||
}
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
Thread.currentThread().interrupt();
|
||||
// don't try to destroy poller, as its handle is likely no longer valid
|
||||
wasInterrupted = true;
|
||||
break;
|
||||
}
|
||||
for (ConnectionNotification event :
|
||||
NetworkTablesJNI.readConnectionListenerQueue(s_inst, s_poller)) {
|
||||
Consumer<ConnectionNotification> listener;
|
||||
s_lock.lock();
|
||||
try {
|
||||
listener = s_listeners.get(event.listener);
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
if (listener != null) {
|
||||
try {
|
||||
listener.accept(event);
|
||||
} catch (Throwable throwable) {
|
||||
System.err.println(
|
||||
"Unhandled exception during listener callback: " + throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
s_lock.lock();
|
||||
try {
|
||||
if (!wasInterrupted) {
|
||||
NetworkTablesJNI.destroyConnectionListenerPoller(s_poller);
|
||||
}
|
||||
s_poller = 0;
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
},
|
||||
"ConnectionListener");
|
||||
s_thread.setDaemon(true);
|
||||
s_thread.start();
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* A connection listener. This queues connection notifications. Code using the listener must
|
||||
* periodically call readQueue() to read the notifications.
|
||||
*/
|
||||
public final class ConnectionListenerPoller implements AutoCloseable {
|
||||
/**
|
||||
* Construct a connection listener poller.
|
||||
*
|
||||
* @param inst Instance
|
||||
*/
|
||||
public ConnectionListenerPoller(NetworkTableInstance inst) {
|
||||
m_inst = inst;
|
||||
m_handle = NetworkTablesJNI.createConnectionListenerPoller(inst.getHandle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a connection listener.
|
||||
*
|
||||
* @param immediateNotify if notification should be immediately created for existing connections
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int add(boolean immediateNotify) {
|
||||
return NetworkTablesJNI.addPolledConnectionListener(m_handle, immediateNotify);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a connection listener.
|
||||
*
|
||||
* @param listener Listener handle
|
||||
*/
|
||||
public void remove(int listener) {
|
||||
NetworkTablesJNI.removeConnectionListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read connection notifications.
|
||||
*
|
||||
* @return Connection notifications since the previous call to readQueue()
|
||||
*/
|
||||
public ConnectionNotification[] readQueue() {
|
||||
return NetworkTablesJNI.readConnectionListenerQueue(m_inst, m_handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_handle != 0) {
|
||||
NetworkTablesJNI.destroyConnectionListenerPoller(m_handle);
|
||||
}
|
||||
m_handle = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
*
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
private final NetworkTableInstance m_inst;
|
||||
private int m_handle;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/** NetworkTables Connection notification. */
|
||||
@SuppressWarnings("MemberName")
|
||||
public final class ConnectionNotification {
|
||||
/**
|
||||
* Handle of listener that was triggered. ConnectionListener.getHandle() or the return value of
|
||||
* ConnectionListenerPoller.add() can be used to map this to a specific added listener.
|
||||
*/
|
||||
public final int listener;
|
||||
|
||||
/** True if event is due to connection being established. */
|
||||
public final boolean connected;
|
||||
|
||||
/** Connection information. */
|
||||
public final ConnectionInfo conn;
|
||||
|
||||
/**
|
||||
* Constructor. This should generally only be used internally to NetworkTables.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param listener Listener that was triggered
|
||||
* @param connected Connected if true
|
||||
* @param conn Connection information
|
||||
*/
|
||||
public ConnectionNotification(
|
||||
NetworkTableInstance inst, int listener, boolean connected, ConnectionInfo conn) {
|
||||
this.m_inst = inst;
|
||||
this.listener = listener;
|
||||
this.connected = connected;
|
||||
this.conn = conn;
|
||||
}
|
||||
|
||||
private final NetworkTableInstance m_inst;
|
||||
|
||||
public NetworkTableInstance getInstance() {
|
||||
return m_inst;
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,6 @@ public final class LogMessage {
|
||||
public static final int kDebug3 = 7;
|
||||
public static final int kDebug4 = 6;
|
||||
|
||||
/** The logger that generated the message. */
|
||||
public final int logger;
|
||||
|
||||
/** Log level of the message. */
|
||||
public final int level;
|
||||
|
||||
@@ -37,26 +34,15 @@ public final class LogMessage {
|
||||
/**
|
||||
* Constructor. This should generally only be used internally to NetworkTables.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param logger Logger
|
||||
* @param level Log level
|
||||
* @param filename Filename
|
||||
* @param line Line number
|
||||
* @param message Message
|
||||
*/
|
||||
public LogMessage(
|
||||
NetworkTableInstance inst, int logger, int level, String filename, int line, String message) {
|
||||
this.m_inst = inst;
|
||||
this.logger = logger;
|
||||
public LogMessage(int level, String filename, int line, String message) {
|
||||
this.level = level;
|
||||
this.filename = filename;
|
||||
this.line = line;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
private final NetworkTableInstance m_inst;
|
||||
|
||||
NetworkTableInstance getInstance() {
|
||||
return m_inst;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* NetworkTables event.
|
||||
*
|
||||
* <p>Events have flags. The flags are a bitmask and must be OR'ed together when listening to an
|
||||
* event to indicate the combination of events desired to be received.
|
||||
*/
|
||||
@SuppressWarnings("MemberName")
|
||||
public final class NetworkTableEvent {
|
||||
/** No flags. */
|
||||
public static final int kNone = 0;
|
||||
|
||||
/**
|
||||
* Initial listener addition. Set this flag to receive immediate notification of matches to the
|
||||
* flag criteria.
|
||||
*/
|
||||
public static final int kImmediate = 0x01;
|
||||
|
||||
/** Client connected (on server, any client connected). */
|
||||
public static final int kConnected = 0x02;
|
||||
|
||||
/** Client disconnected (on server, any client disconnected). */
|
||||
public static final int kDisconnected = 0x04;
|
||||
|
||||
/** Any connection event (connect or disconnect). */
|
||||
public static final int kConnection = kConnected | kDisconnected;
|
||||
|
||||
/** New topic published. */
|
||||
public static final int kPublish = 0x08;
|
||||
|
||||
/** Topic unpublished. */
|
||||
public static final int kUnpublish = 0x10;
|
||||
|
||||
/** Topic properties changed. */
|
||||
public static final int kProperties = 0x20;
|
||||
|
||||
/** Any topic event (publish, unpublish, or properties changed). */
|
||||
public static final int kTopic = kPublish | kUnpublish | kProperties;
|
||||
|
||||
/** Topic value updated (via network). */
|
||||
public static final int kValueRemote = 0x40;
|
||||
|
||||
/** Topic value updated (local). */
|
||||
public static final int kValueLocal = 0x80;
|
||||
|
||||
/** Topic value updated (network or local). */
|
||||
public static final int kValueAll = kValueRemote | kValueLocal;
|
||||
|
||||
/** Log message. */
|
||||
public static final int kLogMessage = 0x100;
|
||||
|
||||
/**
|
||||
* Handle of listener that was triggered. The value returned when adding the listener can be used
|
||||
* to map this to a specific added listener.
|
||||
*/
|
||||
public final int listener;
|
||||
|
||||
/**
|
||||
* Event flags. For example, kPublish if the topic was not previously published. Also indicates
|
||||
* the data included with the event:
|
||||
*
|
||||
* <ul>
|
||||
* <li>kConnected or kDisconnected: connInfo
|
||||
* <li>kPublish, kUnpublish, or kProperties: topicInfo
|
||||
* <li>kValueRemote, kValueLocal: valueData
|
||||
* <li>kLogMessage: logMessage
|
||||
* </ul>
|
||||
*/
|
||||
public final int flags;
|
||||
|
||||
/** Connection information (for connection events). */
|
||||
public final ConnectionInfo connInfo;
|
||||
|
||||
/** Topic information (for topic events). */
|
||||
public final TopicInfo topicInfo;
|
||||
|
||||
/** Value data (for value events). */
|
||||
public final ValueEventData valueData;
|
||||
|
||||
/** Log message (for log message events). */
|
||||
public final LogMessage logMessage;
|
||||
|
||||
/**
|
||||
* Constructor. This should generally only be used internally to NetworkTables.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param listener Listener that was triggered
|
||||
* @param flags Event flags
|
||||
* @param connInfo Connection information
|
||||
* @param topicInfo Topic information
|
||||
* @param valueData Value data
|
||||
* @param logMessage Log message
|
||||
*/
|
||||
public NetworkTableEvent(
|
||||
NetworkTableInstance inst,
|
||||
int listener,
|
||||
int flags,
|
||||
ConnectionInfo connInfo,
|
||||
TopicInfo topicInfo,
|
||||
ValueEventData valueData,
|
||||
LogMessage logMessage) {
|
||||
this.m_inst = inst;
|
||||
this.listener = listener;
|
||||
this.flags = flags;
|
||||
this.connInfo = connInfo;
|
||||
this.topicInfo = topicInfo;
|
||||
this.valueData = valueData;
|
||||
this.logMessage = logMessage;
|
||||
}
|
||||
|
||||
/* Network table instance. */
|
||||
private final NetworkTableInstance m_inst;
|
||||
|
||||
/**
|
||||
* Gets the instance associated with this event.
|
||||
*
|
||||
* @return Instance
|
||||
*/
|
||||
public NetworkTableInstance getInstance() {
|
||||
return m_inst;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Event listener. This calls back to a callback function when an event matching the specified mask
|
||||
* occurs. The callback function is called asynchronously on a separate thread, so it's important to
|
||||
* use synchronization or atomics when accessing any shared state from the callback function.
|
||||
*/
|
||||
public final class NetworkTableListener implements AutoCloseable {
|
||||
/**
|
||||
* Create a listener for changes to topics with names that start with any of the given prefixes.
|
||||
* This creates a corresponding internal subscriber with the lifetime of the listener.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param eventMask Bitmask of NetworkTableEvent flags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
public static NetworkTableListener createListener(
|
||||
NetworkTableInstance inst,
|
||||
String[] prefixes,
|
||||
int eventMask,
|
||||
Consumer<NetworkTableEvent> listener) {
|
||||
return new NetworkTableListener(inst, inst.addListener(prefixes, eventMask, listener));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for changes on a particular topic. This creates a corresponding internal
|
||||
* subscriber with the lifetime of the listener.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param eventMask Bitmask of NetworkTableEvent flags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
public static NetworkTableListener createListener(
|
||||
Topic topic, int eventMask, Consumer<NetworkTableEvent> listener) {
|
||||
NetworkTableInstance inst = topic.getInstance();
|
||||
return new NetworkTableListener(inst, inst.addListener(topic, eventMask, listener));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on a subscriber. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of NetworkTableEvent flags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
public static NetworkTableListener createListener(
|
||||
Subscriber subscriber, int eventMask, Consumer<NetworkTableEvent> listener) {
|
||||
NetworkTableInstance inst = subscriber.getTopic().getInstance();
|
||||
return new NetworkTableListener(inst, inst.addListener(subscriber, eventMask, listener));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on a subscriber. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of NetworkTableEvent flags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
public static NetworkTableListener createListener(
|
||||
MultiSubscriber subscriber, int eventMask, Consumer<NetworkTableEvent> listener) {
|
||||
NetworkTableInstance inst = subscriber.getInstance();
|
||||
return new NetworkTableListener(inst, inst.addListener(subscriber, eventMask, listener));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param eventMask Bitmask of NetworkTableEvent flags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
public static NetworkTableListener createListener(
|
||||
NetworkTableEntry entry, int eventMask, Consumer<NetworkTableEvent> listener) {
|
||||
NetworkTableInstance inst = entry.getInstance();
|
||||
return new NetworkTableListener(inst, inst.addListener(entry, eventMask, listener));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a connection listener.
|
||||
*
|
||||
* @param inst instance
|
||||
* @param immediateNotify notify listener of all existing connections
|
||||
* @param listener listener function
|
||||
* @return Listener
|
||||
*/
|
||||
public static NetworkTableListener createConnectionListener(
|
||||
NetworkTableInstance inst, boolean immediateNotify, Consumer<NetworkTableEvent> listener) {
|
||||
return new NetworkTableListener(inst, inst.addConnectionListener(immediateNotify, listener));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for log messages. By default, log messages are sent to stderr; this function
|
||||
* sends log messages with the specified levels to the provided callback function instead. The
|
||||
* callback function will only be called for log messages with level greater than or equal to
|
||||
* minLevel and less than or equal to maxLevel; messages outside this range will be silently
|
||||
* ignored.
|
||||
*
|
||||
* @param inst instance
|
||||
* @param minLevel minimum log level
|
||||
* @param maxLevel maximum log level
|
||||
* @param listener listener function
|
||||
* @return Listener
|
||||
*/
|
||||
public static NetworkTableListener createLogger(
|
||||
NetworkTableInstance inst, int minLevel, int maxLevel, Consumer<NetworkTableEvent> listener) {
|
||||
return new NetworkTableListener(inst, inst.addLogger(minLevel, maxLevel, listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_handle != 0) {
|
||||
m_inst.removeListener(m_handle);
|
||||
m_handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
*
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Native handle
|
||||
*/
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the topic listener queue to be empty. This is primarily useful for deterministic
|
||||
* testing. This blocks until either the topic listener queue is empty (e.g. there are no more
|
||||
* events that need to be passed along to callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a negative value to
|
||||
* block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
public boolean waitForQueue(double timeout) {
|
||||
return m_inst.waitForListenerQueue(timeout);
|
||||
}
|
||||
|
||||
private NetworkTableListener(NetworkTableInstance inst, int handle) {
|
||||
m_inst = inst;
|
||||
m_handle = handle;
|
||||
}
|
||||
|
||||
private final NetworkTableInstance m_inst;
|
||||
private int m_handle;
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* Topic change listener. This queues topic change events matching the specified mask. Code using
|
||||
* the listener must periodically call readQueue() to read the events.
|
||||
*/
|
||||
public final class NetworkTableListenerPoller implements AutoCloseable {
|
||||
/**
|
||||
* Construct a topic listener poller.
|
||||
*
|
||||
* @param inst Instance
|
||||
*/
|
||||
public NetworkTableListenerPoller(NetworkTableInstance inst) {
|
||||
m_inst = inst;
|
||||
m_handle = NetworkTablesJNI.createListenerPoller(inst.getHandle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to topic changes for topics with names that start with any of the given
|
||||
* prefixes. This creates a corresponding internal subscriber with the lifetime of the listener.
|
||||
*
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addListener(String[] prefixes, int eventMask) {
|
||||
return NetworkTablesJNI.addListener(m_handle, prefixes, eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to changes to a particular topic. This creates a corresponding internal
|
||||
* subscriber with the lifetime of the listener.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addListener(Topic topic, int eventMask) {
|
||||
return NetworkTablesJNI.addListener(m_handle, topic.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on a subscriber. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addListener(Subscriber subscriber, int eventMask) {
|
||||
return NetworkTablesJNI.addListener(m_handle, subscriber.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on a subscriber. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addListener(MultiSubscriber subscriber, int eventMask) {
|
||||
return NetworkTablesJNI.addListener(m_handle, subscriber.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addListener(NetworkTableEntry entry, int eventMask) {
|
||||
return NetworkTablesJNI.addListener(m_handle, entry.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a connection listener. The callback function is called asynchronously on a separate thread,
|
||||
* so it's important to use synchronization or atomics when accessing any shared state from the
|
||||
* callback function.
|
||||
*
|
||||
* @param immediateNotify notify listener of all existing connections
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addConnectionListener(boolean immediateNotify) {
|
||||
return NetworkTablesJNI.addListener(
|
||||
m_handle,
|
||||
m_inst.getHandle(),
|
||||
NetworkTableEvent.kConnection | (immediateNotify ? NetworkTableEvent.kImmediate : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add logger callback function. By default, log messages are sent to stderr; this function sends
|
||||
* log messages with the specified levels to the provided callback function instead. The callback
|
||||
* function will only be called for log messages with level greater than or equal to minLevel and
|
||||
* less than or equal to maxLevel; messages outside this range will be silently ignored.
|
||||
*
|
||||
* @param minLevel minimum log level
|
||||
* @param maxLevel maximum log level
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int addLogger(int minLevel, int maxLevel) {
|
||||
return NetworkTablesJNI.addLogger(m_handle, minLevel, maxLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a listener.
|
||||
*
|
||||
* @param listener Listener handle
|
||||
*/
|
||||
public void removeListener(int listener) {
|
||||
NetworkTablesJNI.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read topic notifications.
|
||||
*
|
||||
* @return Topic notifications since the previous call to readQueue()
|
||||
*/
|
||||
public NetworkTableEvent[] readQueue() {
|
||||
return NetworkTablesJNI.readListenerQueue(m_inst, m_handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_handle != 0) {
|
||||
NetworkTablesJNI.destroyListenerPoller(m_handle);
|
||||
}
|
||||
m_handle = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
*
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
private final NetworkTableInstance m_inst;
|
||||
private int m_handle;
|
||||
}
|
||||
@@ -42,6 +42,15 @@ public final class TopicInfo {
|
||||
/* Cached topic object. */
|
||||
private Topic m_topicObject;
|
||||
|
||||
/**
|
||||
* Get the instance.
|
||||
*
|
||||
* @return Instance
|
||||
*/
|
||||
public NetworkTableInstance getInstance() {
|
||||
return m_inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the topic as an object.
|
||||
*
|
||||
|
||||
@@ -1,240 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import edu.wpi.first.util.WPIUtilJNI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Topic change listener. This calls back to a callback function when a topic change matching the
|
||||
* specified mask occurs. The callback function is called asynchronously on a separate thread, so
|
||||
* it's important to use synchronization or atomics when accessing any shared state from the
|
||||
* callback function.
|
||||
*/
|
||||
public final class TopicListener implements AutoCloseable {
|
||||
/**
|
||||
* Create a listener for changes on a particular topic.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
public TopicListener(Topic topic, int eventMask, Consumer<TopicNotification> listener) {
|
||||
s_lock.lock();
|
||||
try {
|
||||
if (s_poller == 0) {
|
||||
s_inst = topic.getInstance();
|
||||
s_poller = NetworkTablesJNI.createTopicListenerPoller(s_inst.getHandle());
|
||||
startThread();
|
||||
}
|
||||
m_handle = NetworkTablesJNI.addPolledTopicListener(s_poller, topic.getHandle(), eventMask);
|
||||
s_listeners.put(m_handle, listener);
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on a subscriber.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
public TopicListener(Subscriber subscriber, int eventMask, Consumer<TopicNotification> listener) {
|
||||
s_lock.lock();
|
||||
try {
|
||||
if (s_poller == 0) {
|
||||
s_inst = subscriber.getTopic().getInstance();
|
||||
s_poller = NetworkTablesJNI.createTopicListenerPoller(s_inst.getHandle());
|
||||
startThread();
|
||||
}
|
||||
m_handle =
|
||||
NetworkTablesJNI.addPolledTopicListener(s_poller, subscriber.getHandle(), eventMask);
|
||||
s_listeners.put(m_handle, listener);
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on a subscriber.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
public TopicListener(
|
||||
MultiSubscriber subscriber, int eventMask, Consumer<TopicNotification> listener) {
|
||||
s_lock.lock();
|
||||
try {
|
||||
if (s_poller == 0) {
|
||||
s_inst = subscriber.getInstance();
|
||||
s_poller = NetworkTablesJNI.createTopicListenerPoller(s_inst.getHandle());
|
||||
startThread();
|
||||
}
|
||||
m_handle =
|
||||
NetworkTablesJNI.addPolledTopicListener(s_poller, subscriber.getHandle(), eventMask);
|
||||
s_listeners.put(m_handle, listener);
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
public TopicListener(
|
||||
NetworkTableEntry entry, int eventMask, Consumer<TopicNotification> listener) {
|
||||
s_lock.lock();
|
||||
try {
|
||||
if (s_poller == 0) {
|
||||
s_inst = entry.getInstance();
|
||||
s_poller = NetworkTablesJNI.createTopicListenerPoller(s_inst.getHandle());
|
||||
startThread();
|
||||
}
|
||||
m_handle = NetworkTablesJNI.addPolledTopicListener(s_poller, entry.getHandle(), eventMask);
|
||||
s_listeners.put(m_handle, listener);
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for changes to topics with names that start with any of the given prefixes.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
public TopicListener(
|
||||
NetworkTableInstance inst,
|
||||
String[] prefixes,
|
||||
int eventMask,
|
||||
Consumer<TopicNotification> listener) {
|
||||
s_lock.lock();
|
||||
try {
|
||||
if (s_poller == 0) {
|
||||
s_inst = inst;
|
||||
s_poller = NetworkTablesJNI.createTopicListenerPoller(inst.getHandle());
|
||||
startThread();
|
||||
}
|
||||
m_handle = NetworkTablesJNI.addPolledTopicListener(s_poller, prefixes, eventMask);
|
||||
s_listeners.put(m_handle, listener);
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_handle != 0) {
|
||||
s_lock.lock();
|
||||
try {
|
||||
s_listeners.remove(m_handle);
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
NetworkTablesJNI.removeTopicListener(m_handle);
|
||||
m_handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
*
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Native handle
|
||||
*/
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
private int m_handle;
|
||||
|
||||
private static final ReentrantLock s_lock = new ReentrantLock();
|
||||
private static final Map<Integer, Consumer<TopicNotification>> s_listeners = new HashMap<>();
|
||||
private static Thread s_thread;
|
||||
private static NetworkTableInstance s_inst;
|
||||
private static int s_poller;
|
||||
private static boolean s_waitQueue;
|
||||
private static final Condition s_waitQueueCond = s_lock.newCondition();
|
||||
|
||||
private static void startThread() {
|
||||
s_thread =
|
||||
new Thread(
|
||||
() -> {
|
||||
boolean wasInterrupted = false;
|
||||
while (!Thread.interrupted()) {
|
||||
try {
|
||||
WPIUtilJNI.waitForObject(s_poller);
|
||||
} catch (InterruptedException ex) {
|
||||
s_lock.lock();
|
||||
try {
|
||||
if (s_waitQueue) {
|
||||
s_waitQueue = false;
|
||||
s_waitQueueCond.signalAll();
|
||||
continue;
|
||||
}
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
Thread.currentThread().interrupt();
|
||||
// don't try to destroy poller, as its handle is likely no longer valid
|
||||
wasInterrupted = true;
|
||||
break;
|
||||
}
|
||||
for (TopicNotification event :
|
||||
NetworkTablesJNI.readTopicListenerQueue(s_inst, s_poller)) {
|
||||
Consumer<TopicNotification> listener;
|
||||
s_lock.lock();
|
||||
try {
|
||||
listener = s_listeners.get(event.listener);
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
if (listener != null) {
|
||||
try {
|
||||
listener.accept(event);
|
||||
} catch (Throwable throwable) {
|
||||
System.err.println(
|
||||
"Unhandled exception during listener callback: " + throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
s_lock.lock();
|
||||
try {
|
||||
if (!wasInterrupted) {
|
||||
NetworkTablesJNI.destroyTopicListenerPoller(s_poller);
|
||||
}
|
||||
s_poller = 0;
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
},
|
||||
"TopicListener");
|
||||
s_thread.setDaemon(true);
|
||||
s_thread.start();
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* Flag values for use with topic listeners.
|
||||
*
|
||||
* <p>The flags are a bitmask and must be OR'ed together to indicate the combination of events
|
||||
* desired to be received.
|
||||
*
|
||||
* <p>The constants kPublish, kUnpublish, and kProperties represent different events that can occur
|
||||
* to topics.
|
||||
*/
|
||||
public enum TopicListenerFlags {
|
||||
; // no enum values
|
||||
|
||||
/**
|
||||
* Initial listener addition.
|
||||
*
|
||||
* <p>Set this flag to receive immediate notification of topics matching the flag criteria
|
||||
* (generally only useful when combined with kPublish).
|
||||
*/
|
||||
public static final int kImmediate = 0x01;
|
||||
|
||||
/**
|
||||
* Newly published topic.
|
||||
*
|
||||
* <p>Set this flag to receive a notification when a topic is initially published.
|
||||
*/
|
||||
public static final int kPublish = 0x02;
|
||||
|
||||
/**
|
||||
* Topic has no more publishers.
|
||||
*
|
||||
* <p>Set this flag to receive a notification when a topic has no more publishers.
|
||||
*/
|
||||
public static final int kUnpublish = 0x04;
|
||||
|
||||
/**
|
||||
* Topic's properties changed.
|
||||
*
|
||||
* <p>Set this flag to receive a notification when an topic's properties change.
|
||||
*/
|
||||
public static final int kProperties = 0x08;
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* Topic change listener. This queues topic change events matching the specified mask. Code using
|
||||
* the listener must periodically call readQueue() to read the events.
|
||||
*/
|
||||
public final class TopicListenerPoller implements AutoCloseable {
|
||||
/**
|
||||
* Construct a topic listener poller.
|
||||
*
|
||||
* @param inst Instance
|
||||
*/
|
||||
public TopicListenerPoller(NetworkTableInstance inst) {
|
||||
m_inst = inst;
|
||||
m_handle = NetworkTablesJNI.createTopicListenerPoller(inst.getHandle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to changes to a particular topic.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int add(Topic topic, int eventMask) {
|
||||
return NetworkTablesJNI.addPolledTopicListener(m_handle, topic.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on a subscriber.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int add(Subscriber subscriber, int eventMask) {
|
||||
return NetworkTablesJNI.addPolledTopicListener(m_handle, subscriber.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on a subscriber.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int add(MultiSubscriber subscriber, int eventMask) {
|
||||
return NetworkTablesJNI.addPolledTopicListener(m_handle, subscriber.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int add(NetworkTableEntry entry, int eventMask) {
|
||||
return NetworkTablesJNI.addPolledTopicListener(m_handle, entry.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to topic changes for topics with names that start with any of the given
|
||||
* prefixes.
|
||||
*
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param eventMask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int add(String[] prefixes, int eventMask) {
|
||||
return NetworkTablesJNI.addPolledTopicListener(m_handle, prefixes, eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a listener.
|
||||
*
|
||||
* @param listener Listener handle
|
||||
*/
|
||||
public void remove(int listener) {
|
||||
NetworkTablesJNI.removeTopicListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read topic notifications.
|
||||
*
|
||||
* @return Topic notifications since the previous call to readQueue()
|
||||
*/
|
||||
public TopicNotification[] readQueue() {
|
||||
return NetworkTablesJNI.readTopicListenerQueue(m_inst, m_handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_handle != 0) {
|
||||
NetworkTablesJNI.destroyTopicListenerPoller(m_handle);
|
||||
}
|
||||
m_handle = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
*
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
private final NetworkTableInstance m_inst;
|
||||
private int m_handle;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/** NetworkTables topic notification. */
|
||||
@SuppressWarnings("MemberName")
|
||||
public final class TopicNotification {
|
||||
/**
|
||||
* Handle of listener that was triggered. TopicListener.getHandle() or the return value of
|
||||
* TopicListenerPoller.add() can be used to map this to a specific added listener.
|
||||
*/
|
||||
public final int listener;
|
||||
|
||||
/** Topic information. */
|
||||
public final TopicInfo info;
|
||||
|
||||
/**
|
||||
* Update flags. For example, {@link TopicListenerFlags#kPublish} if the topic was not previously
|
||||
* published.
|
||||
*/
|
||||
public final int flags;
|
||||
|
||||
/**
|
||||
* Constructor. This should generally only be used internally to NetworkTables.
|
||||
*
|
||||
* @param listener Listener that was triggered
|
||||
* @param info Topic information
|
||||
* @param flags Update flags
|
||||
*/
|
||||
public TopicNotification(int listener, TopicInfo info, int flags) {
|
||||
this.listener = listener;
|
||||
this.info = info;
|
||||
this.flags = flags;
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,9 @@
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/** NetworkTables value notification. */
|
||||
/** NetworkTables value event data. */
|
||||
@SuppressWarnings("MemberName")
|
||||
public final class ValueNotification {
|
||||
/**
|
||||
* Handle of listener that was triggered. ValueListener.getHandle() or the return value of
|
||||
* ValueListenerPoller.add() can be used to map this to a specific added listener.
|
||||
*/
|
||||
public final int listener;
|
||||
|
||||
public final class ValueEventData {
|
||||
/** Topic handle. Topic.getHandle() can be used to map this to the corresponding Topic object. */
|
||||
public final int topic;
|
||||
|
||||
@@ -25,39 +19,26 @@ public final class ValueNotification {
|
||||
/** The new value. */
|
||||
public final NetworkTableValue value;
|
||||
|
||||
/** Update flags. */
|
||||
public final int flags;
|
||||
|
||||
/**
|
||||
* Constructor. This should generally only be used internally to NetworkTables.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param listener Listener that was triggered
|
||||
* @param topic Topic handle
|
||||
* @param subentry Subscriber/entry handle
|
||||
* @param value The new value
|
||||
* @param flags Update flags
|
||||
*/
|
||||
public ValueNotification(
|
||||
NetworkTableInstance inst,
|
||||
int listener,
|
||||
int topic,
|
||||
int subentry,
|
||||
NetworkTableValue value,
|
||||
int flags) {
|
||||
public ValueEventData(
|
||||
NetworkTableInstance inst, int topic, int subentry, NetworkTableValue value) {
|
||||
this.m_inst = inst;
|
||||
this.listener = listener;
|
||||
this.topic = topic;
|
||||
this.subentry = subentry;
|
||||
this.value = value;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
/* Network table instance. */
|
||||
private final NetworkTableInstance m_inst;
|
||||
|
||||
/* Cached topic object. */
|
||||
Topic m_topicObject;
|
||||
private Topic m_topicObject;
|
||||
|
||||
private final NetworkTableInstance m_inst;
|
||||
|
||||
/**
|
||||
* Get the topic as an object.
|
||||
@@ -1,191 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import edu.wpi.first.util.WPIUtilJNI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Value change listener. This calls back to a callback function when a value change matching the
|
||||
* specified mask occurs. The callback function is called asynchronously on a separate thread, so
|
||||
* it's important to use synchronization or atomics when accessing any shared state from the
|
||||
* callback function.
|
||||
*/
|
||||
public final class ValueListener implements AutoCloseable {
|
||||
/**
|
||||
* Create a listener for value changes on a subscriber.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
public ValueListener(Subscriber subscriber, int eventMask, Consumer<ValueNotification> listener) {
|
||||
s_lock.lock();
|
||||
try {
|
||||
if (s_poller == 0) {
|
||||
s_inst = subscriber.getTopic().getInstance();
|
||||
s_poller = NetworkTablesJNI.createValueListenerPoller(s_inst.getHandle());
|
||||
startThread();
|
||||
}
|
||||
m_handle =
|
||||
NetworkTablesJNI.addPolledValueListener(s_poller, subscriber.getHandle(), eventMask);
|
||||
s_listeners.put(m_handle, listener);
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for value changes on a subscriber.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
public ValueListener(
|
||||
MultiSubscriber subscriber, int eventMask, Consumer<ValueNotification> listener) {
|
||||
s_lock.lock();
|
||||
try {
|
||||
if (s_poller == 0) {
|
||||
s_inst = subscriber.getInstance();
|
||||
s_poller = NetworkTablesJNI.createValueListenerPoller(s_inst.getHandle());
|
||||
startThread();
|
||||
}
|
||||
m_handle =
|
||||
NetworkTablesJNI.addPolledValueListener(s_poller, subscriber.getHandle(), eventMask);
|
||||
s_listeners.put(m_handle, listener);
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener for value changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param eventMask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
public ValueListener(
|
||||
NetworkTableEntry entry, int eventMask, Consumer<ValueNotification> listener) {
|
||||
s_lock.lock();
|
||||
try {
|
||||
if (s_poller == 0) {
|
||||
s_inst = entry.getInstance();
|
||||
s_poller = NetworkTablesJNI.createValueListenerPoller(s_inst.getHandle());
|
||||
startThread();
|
||||
}
|
||||
m_handle = NetworkTablesJNI.addPolledValueListener(s_poller, entry.getHandle(), eventMask);
|
||||
s_listeners.put(m_handle, listener);
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_handle != 0) {
|
||||
s_lock.lock();
|
||||
try {
|
||||
s_listeners.remove(m_handle);
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
NetworkTablesJNI.removeValueListener(m_handle);
|
||||
m_handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
*
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Native handle
|
||||
*/
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
private int m_handle;
|
||||
|
||||
private static final ReentrantLock s_lock = new ReentrantLock();
|
||||
private static final Map<Integer, Consumer<ValueNotification>> s_listeners = new HashMap<>();
|
||||
private static Thread s_thread;
|
||||
private static NetworkTableInstance s_inst;
|
||||
private static int s_poller;
|
||||
private static boolean s_waitQueue;
|
||||
private static final Condition s_waitQueueCond = s_lock.newCondition();
|
||||
|
||||
private static void startThread() {
|
||||
s_thread =
|
||||
new Thread(
|
||||
() -> {
|
||||
boolean wasInterrupted = false;
|
||||
while (!Thread.interrupted()) {
|
||||
try {
|
||||
WPIUtilJNI.waitForObject(s_poller);
|
||||
} catch (InterruptedException ex) {
|
||||
s_lock.lock();
|
||||
try {
|
||||
if (s_waitQueue) {
|
||||
s_waitQueue = false;
|
||||
s_waitQueueCond.signalAll();
|
||||
continue;
|
||||
}
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
Thread.currentThread().interrupt();
|
||||
// don't try to destroy poller, as its handle is likely no longer valid
|
||||
wasInterrupted = true;
|
||||
break;
|
||||
}
|
||||
for (ValueNotification event :
|
||||
NetworkTablesJNI.readValueListenerQueue(s_inst, s_poller)) {
|
||||
Consumer<ValueNotification> listener;
|
||||
s_lock.lock();
|
||||
try {
|
||||
listener = s_listeners.get(event.listener);
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
if (listener != null) {
|
||||
try {
|
||||
listener.accept(event);
|
||||
} catch (Throwable throwable) {
|
||||
System.err.println(
|
||||
"Unhandled exception during listener callback: " + throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
s_lock.lock();
|
||||
try {
|
||||
if (!wasInterrupted) {
|
||||
NetworkTablesJNI.destroyValueListenerPoller(s_poller);
|
||||
}
|
||||
s_poller = 0;
|
||||
} finally {
|
||||
s_lock.unlock();
|
||||
}
|
||||
},
|
||||
"ValueListener");
|
||||
s_thread.setDaemon(true);
|
||||
s_thread.start();
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* Flag values for use with value listeners.
|
||||
*
|
||||
* <p>The flags are a bitmask and must be OR'ed together to indicate the combination of events
|
||||
* desired to be received.
|
||||
*
|
||||
* <p>By default, notifications are only generated for remote changes occurring after the listener
|
||||
* is created. The constants kImmediate and kLocal are modifiers that cause notifications to be
|
||||
* generated at other times.
|
||||
*/
|
||||
public enum ValueListenerFlags {
|
||||
; // no enum values
|
||||
|
||||
/**
|
||||
* Initial listener addition.
|
||||
*
|
||||
* <p>Set this flag to receive immediate notification of the current value.
|
||||
*/
|
||||
public static final int kImmediate = 0x01;
|
||||
|
||||
/**
|
||||
* Changed locally.
|
||||
*
|
||||
* <p>Set this flag to receive notification of both local changes and changes coming from remote
|
||||
* nodes. By default, notifications are only generated for remote changes.
|
||||
*/
|
||||
public static final int kLocal = 0x02;
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
/**
|
||||
* Value change listener. This queues value change events matching the specified mask. Code using
|
||||
* the listener must periodically call readQueue() to read the events.
|
||||
*/
|
||||
public final class ValueListenerPoller implements AutoCloseable {
|
||||
/**
|
||||
* Construct a value listener poller.
|
||||
*
|
||||
* @param inst Instance
|
||||
*/
|
||||
public ValueListenerPoller(NetworkTableInstance inst) {
|
||||
m_inst = inst;
|
||||
m_handle = NetworkTablesJNI.createValueListenerPoller(inst.getHandle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to value changes on a subscriber.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of ValueListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int add(Subscriber subscriber, int eventMask) {
|
||||
return NetworkTablesJNI.addPolledValueListener(m_handle, subscriber.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to value changes on a subscriber.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of ValueListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int add(MultiSubscriber subscriber, int eventMask) {
|
||||
return NetworkTablesJNI.addPolledValueListener(m_handle, subscriber.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to value changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param eventMask Bitmask of ValueListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
public int add(NetworkTableEntry entry, int eventMask) {
|
||||
return NetworkTablesJNI.addPolledValueListener(m_handle, entry.getHandle(), eventMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a listener.
|
||||
*
|
||||
* @param listener Listener handle
|
||||
*/
|
||||
public void remove(int listener) {
|
||||
NetworkTablesJNI.removeValueListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read value notifications.
|
||||
*
|
||||
* @return Value notifications since the previous call to readQueue()
|
||||
*/
|
||||
public ValueNotification[] readQueue() {
|
||||
return NetworkTablesJNI.readValueListenerQueue(m_inst, m_handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_handle != 0) {
|
||||
NetworkTablesJNI.destroyValueListenerPoller(m_handle);
|
||||
}
|
||||
m_handle = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
*
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
private final NetworkTableInstance m_inst;
|
||||
private int m_handle;
|
||||
}
|
||||
@@ -4,184 +4,16 @@
|
||||
|
||||
#include "ConnectionList.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <optional>
|
||||
|
||||
#include <wpi/DataLog.h>
|
||||
#include <wpi/DenseMap.h>
|
||||
#include <wpi/SafeThread.h>
|
||||
#include <wpi/Synchronization.h>
|
||||
#include <wpi/UidVector.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <wpi/json_serializer.h>
|
||||
#include <wpi/raw_ostream.h>
|
||||
|
||||
#include "HandleMap.h"
|
||||
#include "IListenerStorage.h"
|
||||
#include "ntcore_c.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
namespace {
|
||||
|
||||
struct PollerData {
|
||||
static constexpr auto kType = Handle::kConnectionListenerPoller;
|
||||
|
||||
explicit PollerData(NT_ConnectionListenerPoller handle) : handle{handle} {}
|
||||
|
||||
wpi::SignalObject<NT_ConnectionListenerPoller> handle;
|
||||
std::vector<ConnectionNotification> queue;
|
||||
};
|
||||
|
||||
struct ListenerData {
|
||||
static constexpr auto kType = Handle::kConnectionListener;
|
||||
|
||||
ListenerData(NT_ConnectionListener handle, PollerData* poller)
|
||||
: handle{handle}, poller{poller} {}
|
||||
|
||||
wpi::SignalObject<NT_ConnectionListener> handle;
|
||||
PollerData* poller;
|
||||
};
|
||||
|
||||
struct DataLoggerData {
|
||||
static constexpr auto kType = Handle::kConnectionDataLogger;
|
||||
|
||||
DataLoggerData(NT_ConnectionDataLogger handle, wpi::log::DataLog& log,
|
||||
std::string_view name, int64_t time)
|
||||
: handle{handle},
|
||||
entry{log, name, "{\"schema\":\"NTConnectionInfo\",\"source\":\"NT\"}",
|
||||
"json", time} {}
|
||||
|
||||
NT_ConnectionDataLogger handle;
|
||||
wpi::log::StringLogEntry entry;
|
||||
};
|
||||
|
||||
class ListenerThread final : public wpi::SafeThreadEvent {
|
||||
public:
|
||||
explicit ListenerThread(NT_ConnectionListenerPoller poller)
|
||||
: m_poller{poller} {}
|
||||
|
||||
void Main() final;
|
||||
|
||||
NT_ConnectionListenerPoller m_poller;
|
||||
wpi::DenseMap<NT_ConnectionListener,
|
||||
std::function<void(const ConnectionNotification& event)>>
|
||||
m_callbacks;
|
||||
};
|
||||
|
||||
class CLImpl {
|
||||
public:
|
||||
explicit CLImpl(int inst) : m_inst{inst} {}
|
||||
|
||||
int m_inst;
|
||||
|
||||
// shared with user (must be atomic or mutex-protected)
|
||||
std::atomic_bool m_connected{false};
|
||||
wpi::UidVector<std::optional<ConnectionInfo>, 8> m_connections;
|
||||
|
||||
HandleMap<PollerData, 8> m_pollers;
|
||||
HandleMap<ListenerData, 8> m_listeners;
|
||||
HandleMap<DataLoggerData, 8> m_dataloggers;
|
||||
|
||||
wpi::SafeThreadOwner<ListenerThread> m_listenerThread;
|
||||
|
||||
NT_ConnectionListener AddListener(
|
||||
std::function<void(const ConnectionNotification& event)> callback,
|
||||
bool immediateNotify);
|
||||
PollerData* CreateListenerPoller() { return m_pollers.Add(m_inst); }
|
||||
void DestroyListenerPoller(NT_ConnectionListenerPoller pollerHandle);
|
||||
NT_ConnectionListener AddPolledListener(
|
||||
NT_ConnectionListenerPoller pollerHandle, bool immediateNotify);
|
||||
void RemoveListener(NT_ConnectionListener listenerHandle);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void ListenerThread::Main() {
|
||||
while (m_active) {
|
||||
WPI_Handle signaledBuf[2];
|
||||
auto signaled =
|
||||
wpi::WaitForObjects({m_poller, m_stopEvent.GetHandle()}, signaledBuf);
|
||||
if (signaled.empty() || !m_active) {
|
||||
return;
|
||||
}
|
||||
// call all the way back out to the C++ API to ensure valid handle
|
||||
auto events = nt::ReadConnectionListenerQueue(m_poller);
|
||||
if (events.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::unique_lock lock{m_mutex};
|
||||
for (auto&& event : events) {
|
||||
auto callbackIt = m_callbacks.find(event.listener);
|
||||
if (callbackIt != m_callbacks.end()) {
|
||||
auto callback = callbackIt->second;
|
||||
lock.unlock();
|
||||
callback(event);
|
||||
lock.lock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NT_ConnectionListener CLImpl::AddListener(
|
||||
std::function<void(const ConnectionNotification& event)> callback,
|
||||
bool immediateNotify) {
|
||||
if (!m_listenerThread) {
|
||||
m_listenerThread.Start(CreateListenerPoller()->handle);
|
||||
}
|
||||
if (auto thr = m_listenerThread.GetThread()) {
|
||||
auto listener = AddPolledListener(thr->m_poller, immediateNotify);
|
||||
if (listener) {
|
||||
thr->m_callbacks.try_emplace(listener, std::move(callback));
|
||||
}
|
||||
return listener;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void CLImpl::DestroyListenerPoller(NT_ConnectionListenerPoller pollerHandle) {
|
||||
if (auto poller = m_pollers.Remove(pollerHandle)) {
|
||||
// ensure all listeners that use this poller are removed
|
||||
wpi::SmallVector<NT_ConnectionListener, 16> toRemove;
|
||||
for (auto&& listener : m_listeners) {
|
||||
if (listener->poller == poller.get()) {
|
||||
toRemove.emplace_back(listener->handle);
|
||||
}
|
||||
}
|
||||
for (auto handle : toRemove) {
|
||||
RemoveListener(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NT_ConnectionListener CLImpl::AddPolledListener(
|
||||
NT_ConnectionListenerPoller pollerHandle, bool immediateNotify) {
|
||||
auto poller = m_pollers.Get(pollerHandle);
|
||||
if (!poller) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto listener = m_listeners.Add(m_inst, poller);
|
||||
if (immediateNotify && !m_connections.empty()) {
|
||||
for (auto&& conn : m_connections) {
|
||||
listener->poller->queue.emplace_back(listener->handle.GetHandle(), true,
|
||||
*conn);
|
||||
}
|
||||
listener->poller->handle.Set();
|
||||
listener->handle.Set();
|
||||
}
|
||||
return listener->handle;
|
||||
}
|
||||
|
||||
void CLImpl::RemoveListener(NT_ConnectionListener listenerHandle) {
|
||||
if (auto listener = m_listeners.Remove(listenerHandle)) {
|
||||
if (auto thr = m_listenerThread.GetThread()) {
|
||||
if (thr->m_poller == listener->poller->handle) {
|
||||
thr->m_callbacks.erase(listenerHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::string ConnInfoToJson(bool connected, const ConnectionInfo& info) {
|
||||
std::string str;
|
||||
wpi::raw_string_ostream os{str};
|
||||
@@ -200,50 +32,35 @@ static std::string ConnInfoToJson(bool connected, const ConnectionInfo& info) {
|
||||
return str;
|
||||
}
|
||||
|
||||
class ConnectionList::Impl : public CLImpl {
|
||||
public:
|
||||
explicit Impl(int inst) : CLImpl{inst} {}
|
||||
};
|
||||
|
||||
ConnectionList::ConnectionList(int inst)
|
||||
: m_impl{std::make_unique<Impl>(inst)} {}
|
||||
ConnectionList::ConnectionList(int inst, IListenerStorage& listenerStorage)
|
||||
: m_inst{inst}, m_listenerStorage{listenerStorage} {}
|
||||
|
||||
ConnectionList::~ConnectionList() = default;
|
||||
|
||||
int ConnectionList::AddConnection(const ConnectionInfo& info) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_impl->m_connected = true;
|
||||
for (auto&& listener : m_impl->m_listeners) {
|
||||
listener->poller->queue.emplace_back(listener->handle.GetHandle(), true,
|
||||
info);
|
||||
listener->poller->handle.Set();
|
||||
listener->handle.Set();
|
||||
}
|
||||
if (!m_impl->m_dataloggers.empty()) {
|
||||
m_connected = true;
|
||||
m_listenerStorage.Notify({}, NT_EVENT_CONNECTED, &info);
|
||||
if (!m_dataloggers.empty()) {
|
||||
auto now = Now();
|
||||
for (auto&& datalogger : m_impl->m_dataloggers) {
|
||||
for (auto&& datalogger : m_dataloggers) {
|
||||
datalogger->entry.Append(ConnInfoToJson(true, info), now);
|
||||
}
|
||||
}
|
||||
return m_impl->m_connections.emplace_back(info);
|
||||
return m_connections.emplace_back(info);
|
||||
}
|
||||
|
||||
void ConnectionList::RemoveConnection(int handle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
auto val = m_impl->m_connections.erase(handle);
|
||||
if (m_impl->m_connections.empty()) {
|
||||
m_impl->m_connected = false;
|
||||
auto val = m_connections.erase(handle);
|
||||
if (m_connections.empty()) {
|
||||
m_connected = false;
|
||||
}
|
||||
if (val) {
|
||||
for (auto&& listener : m_impl->m_listeners) {
|
||||
listener->poller->queue.emplace_back(listener->handle.GetHandle(), false,
|
||||
*val);
|
||||
listener->poller->handle.Set();
|
||||
listener->handle.Set();
|
||||
}
|
||||
if (!m_impl->m_dataloggers.empty()) {
|
||||
m_listenerStorage.Notify({}, NT_EVENT_DISCONNECTED, &(*val));
|
||||
if (!m_dataloggers.empty()) {
|
||||
auto now = Now();
|
||||
for (auto&& datalogger : m_impl->m_dataloggers) {
|
||||
for (auto&& datalogger : m_dataloggers) {
|
||||
datalogger->entry.Append(ConnInfoToJson(false, *val), now);
|
||||
}
|
||||
}
|
||||
@@ -252,79 +69,50 @@ void ConnectionList::RemoveConnection(int handle) {
|
||||
|
||||
void ConnectionList::ClearConnections() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_impl->m_connected = false;
|
||||
for (auto&& conn : m_impl->m_connections) {
|
||||
for (auto&& listener : m_impl->m_listeners) {
|
||||
listener->poller->queue.emplace_back(listener->handle.GetHandle(), false,
|
||||
*conn);
|
||||
listener->poller->handle.Set();
|
||||
listener->handle.Set();
|
||||
}
|
||||
m_connected = false;
|
||||
for (auto&& conn : m_connections) {
|
||||
m_listenerStorage.Notify({}, NT_EVENT_DISCONNECTED, &(*conn));
|
||||
}
|
||||
m_impl->m_connections.clear();
|
||||
m_connections.clear();
|
||||
}
|
||||
|
||||
std::vector<ConnectionInfo> ConnectionList::GetConnections() const {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
std::vector<ConnectionInfo> info;
|
||||
info.reserve(m_impl->m_connections.size());
|
||||
for (auto&& conn : m_impl->m_connections) {
|
||||
info.reserve(m_connections.size());
|
||||
for (auto&& conn : m_connections) {
|
||||
info.emplace_back(*conn);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
bool ConnectionList::IsConnected() const {
|
||||
return m_impl->m_connected;
|
||||
return m_connected;
|
||||
}
|
||||
|
||||
NT_ConnectionListener ConnectionList::AddListener(
|
||||
std::function<void(const ConnectionNotification& event)> callback,
|
||||
bool immediateNotify) {
|
||||
void ConnectionList::AddListener(NT_Listener listener, unsigned int eventMask) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return m_impl->AddListener(std::move(callback), immediateNotify);
|
||||
}
|
||||
|
||||
NT_ConnectionListenerPoller ConnectionList::CreateListenerPoller() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return m_impl->CreateListenerPoller()->handle;
|
||||
}
|
||||
|
||||
void ConnectionList::DestroyListenerPoller(
|
||||
NT_ConnectionListenerPoller pollerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_impl->DestroyListenerPoller(pollerHandle);
|
||||
}
|
||||
|
||||
NT_ConnectionListener ConnectionList::AddPolledListener(
|
||||
NT_ConnectionListenerPoller pollerHandle, bool immediateNotify) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return m_impl->AddPolledListener(pollerHandle, immediateNotify);
|
||||
}
|
||||
|
||||
std::vector<ConnectionNotification> ConnectionList::ReadListenerQueue(
|
||||
NT_ConnectionListenerPoller pollerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (auto poller = m_impl->m_pollers.Get(pollerHandle)) {
|
||||
std::vector<ConnectionNotification> rv;
|
||||
rv.swap(poller->queue);
|
||||
return rv;
|
||||
} else {
|
||||
return {};
|
||||
eventMask &= (NT_EVENT_CONNECTION | NT_EVENT_IMMEDIATE);
|
||||
m_listenerStorage.Activate(listener, eventMask);
|
||||
if ((eventMask & (NT_EVENT_CONNECTED | NT_EVENT_IMMEDIATE)) ==
|
||||
(NT_EVENT_CONNECTED | NT_EVENT_IMMEDIATE) &&
|
||||
!m_connections.empty()) {
|
||||
wpi::SmallVector<const ConnectionInfo*, 16> infos;
|
||||
infos.reserve(m_connections.size());
|
||||
for (auto&& conn : m_connections) {
|
||||
infos.emplace_back(&(*conn));
|
||||
}
|
||||
m_listenerStorage.Notify({&listener, 1},
|
||||
NT_EVENT_CONNECTED | NT_EVENT_IMMEDIATE, infos);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionList::RemoveListener(NT_ConnectionListener listenerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_impl->RemoveListener(listenerHandle);
|
||||
}
|
||||
|
||||
NT_ConnectionDataLogger ConnectionList::StartDataLog(wpi::log::DataLog& log,
|
||||
std::string_view name) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
auto now = Now();
|
||||
auto datalogger = m_impl->m_dataloggers.Add(m_impl->m_inst, log, name, now);
|
||||
for (auto&& conn : m_impl->m_connections) {
|
||||
auto datalogger = m_dataloggers.Add(m_inst, log, name, now);
|
||||
for (auto&& conn : m_connections) {
|
||||
datalogger->entry.Append(ConnInfoToJson(true, *conn), now);
|
||||
}
|
||||
return datalogger->handle;
|
||||
@@ -332,7 +120,7 @@ NT_ConnectionDataLogger ConnectionList::StartDataLog(wpi::log::DataLog& log,
|
||||
|
||||
void ConnectionList::StopDataLog(NT_ConnectionDataLogger logger) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (auto datalogger = m_impl->m_dataloggers.Remove(logger)) {
|
||||
if (auto datalogger = m_dataloggers.Remove(logger)) {
|
||||
datalogger->entry.Finish(Now());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,21 +4,31 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/DataLog.h>
|
||||
#include <wpi/UidVector.h>
|
||||
#include <wpi/mutex.h>
|
||||
|
||||
#include "Handle.h"
|
||||
#include "HandleMap.h"
|
||||
#include "IConnectionList.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class IListenerStorage;
|
||||
|
||||
class ConnectionList final : public IConnectionList {
|
||||
public:
|
||||
explicit ConnectionList(int inst);
|
||||
ConnectionList(int inst, IListenerStorage& listenerStorage);
|
||||
~ConnectionList() final;
|
||||
|
||||
// IConnectionList interface
|
||||
@@ -30,27 +40,35 @@ class ConnectionList final : public IConnectionList {
|
||||
std::vector<ConnectionInfo> GetConnections() const final;
|
||||
bool IsConnected() const final;
|
||||
|
||||
NT_ConnectionListener AddListener(
|
||||
std::function<void(const ConnectionNotification& event)> callback,
|
||||
bool immediateNotify);
|
||||
|
||||
NT_ConnectionListenerPoller CreateListenerPoller();
|
||||
void DestroyListenerPoller(NT_ConnectionListenerPoller pollerHandle);
|
||||
NT_ConnectionListener AddPolledListener(
|
||||
NT_ConnectionListenerPoller pollerHandle, bool immediateNotify);
|
||||
std::vector<ConnectionNotification> ReadListenerQueue(
|
||||
NT_ConnectionListenerPoller pollerHandle);
|
||||
void RemoveListener(NT_ConnectionListener listenerHandle);
|
||||
void AddListener(NT_Listener listener, unsigned int eventMask);
|
||||
|
||||
NT_ConnectionDataLogger StartDataLog(wpi::log::DataLog& log,
|
||||
std::string_view name);
|
||||
void StopDataLog(NT_ConnectionDataLogger logger);
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
int m_inst;
|
||||
IListenerStorage& m_listenerStorage;
|
||||
mutable wpi::mutex m_mutex;
|
||||
|
||||
// shared with user (must be atomic or mutex-protected)
|
||||
std::atomic_bool m_connected{false};
|
||||
wpi::UidVector<std::optional<ConnectionInfo>, 8> m_connections;
|
||||
|
||||
struct DataLoggerData {
|
||||
static constexpr auto kType = Handle::kConnectionDataLogger;
|
||||
|
||||
DataLoggerData(NT_ConnectionDataLogger handle, wpi::log::DataLog& log,
|
||||
std::string_view name, int64_t time)
|
||||
: handle{handle},
|
||||
entry{log, name,
|
||||
"{\"schema\":\"NTConnectionInfo\",\"source\":\"NT\"}", "json",
|
||||
time} {}
|
||||
|
||||
NT_ConnectionDataLogger handle;
|
||||
wpi::log::StringLogEntry entry;
|
||||
};
|
||||
HandleMap<DataLoggerData, 8> m_dataloggers;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
@@ -18,24 +18,16 @@ namespace nt {
|
||||
class Handle {
|
||||
public:
|
||||
enum Type {
|
||||
kConnectionListener = wpi::kHandleTypeNTBase,
|
||||
kConnectionListenerPoller,
|
||||
kListener = wpi::kHandleTypeNTBase,
|
||||
kListenerPoller,
|
||||
kEntry,
|
||||
kEntryListener,
|
||||
kEntryListenerPoller,
|
||||
kInstance,
|
||||
kLogger,
|
||||
kLoggerPoller,
|
||||
kDataLogger,
|
||||
kConnectionDataLogger,
|
||||
kMultiSubscriber,
|
||||
kTopic,
|
||||
kTopicListener,
|
||||
kTopicListenerPoller,
|
||||
kSubscriber,
|
||||
kPublisher,
|
||||
kValueListener,
|
||||
kValueListenerPoller,
|
||||
kTypeMax
|
||||
};
|
||||
static_assert(kTypeMax <= wpi::kHandleTypeHALBase);
|
||||
|
||||
49
ntcore/src/main/native/cpp/IListenerStorage.h
Normal file
49
ntcore/src/main/native/cpp/IListenerStorage.h
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class IListenerStorage {
|
||||
public:
|
||||
// Return false if event should not be issued (final check).
|
||||
// This is called only during Notify() processing.
|
||||
using FinishEventFunc = std::function<bool(unsigned int mask, Event* event)>;
|
||||
|
||||
virtual ~IListenerStorage() = default;
|
||||
|
||||
virtual void Activate(NT_Listener listener, unsigned int mask,
|
||||
FinishEventFunc finishEvent = {}) = 0;
|
||||
|
||||
// If handles is not empty, notifies ONLY those listeners
|
||||
virtual void Notify(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
std::span<ConnectionInfo const* const> infos) = 0;
|
||||
virtual void Notify(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
std::span<const TopicInfo> infos) = 0;
|
||||
virtual void Notify(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
NT_Topic topic, NT_Handle subentry,
|
||||
const Value& value) = 0;
|
||||
virtual void Notify(unsigned int flags, unsigned int level,
|
||||
std::string_view filename, unsigned int line,
|
||||
std::string_view message) = 0;
|
||||
|
||||
void Notify(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
const ConnectionInfo* info) {
|
||||
Notify(handles, flags, {&info, 1});
|
||||
}
|
||||
void Notify(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
const TopicInfo& info) {
|
||||
Notify(handles, flags, {&info, 1});
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
@@ -13,11 +13,12 @@ wpi::mutex InstanceImpl::s_mutex;
|
||||
using namespace std::placeholders;
|
||||
|
||||
InstanceImpl::InstanceImpl(int inst)
|
||||
: logger_impl(inst),
|
||||
logger(
|
||||
std::bind(&LoggerImpl::Log, &logger_impl, _1, _2, _3, _4)), // NOLINT
|
||||
connectionList(inst),
|
||||
localStorage(inst, logger),
|
||||
: listenerStorage{inst},
|
||||
logger_impl{listenerStorage},
|
||||
logger{
|
||||
std::bind(&LoggerImpl::Log, &logger_impl, _1, _2, _3, _4)}, // NOLINT
|
||||
connectionList{inst, listenerStorage},
|
||||
localStorage{inst, listenerStorage, logger},
|
||||
m_inst{inst} {
|
||||
logger.set_min_level(logger_impl.GetMinLevel());
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "ConnectionList.h"
|
||||
#include "Handle.h"
|
||||
#include "ListenerStorage.h"
|
||||
#include "LocalStorage.h"
|
||||
#include "Log.h"
|
||||
#include "LoggerImpl.h"
|
||||
@@ -55,6 +56,7 @@ class InstanceImpl {
|
||||
std::shared_ptr<NetworkServer> GetServer();
|
||||
std::shared_ptr<INetworkClient> GetClient();
|
||||
|
||||
ListenerStorage listenerStorage;
|
||||
LoggerImpl logger_impl;
|
||||
wpi::Logger logger;
|
||||
ConnectionList connectionList;
|
||||
|
||||
351
ntcore/src/main/native/cpp/ListenerStorage.cpp
Normal file
351
ntcore/src/main/native/cpp/ListenerStorage.cpp
Normal file
@@ -0,0 +1,351 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "ListenerStorage.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <wpi/DenseMap.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
|
||||
#include "ntcore_c.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
class ListenerStorage::Thread final : public wpi::SafeThreadEvent {
|
||||
public:
|
||||
explicit Thread(NT_ListenerPoller poller) : m_poller{poller} {}
|
||||
|
||||
void Main() final;
|
||||
|
||||
NT_ListenerPoller m_poller;
|
||||
wpi::DenseMap<NT_Listener, ListenerCallback> m_callbacks;
|
||||
wpi::Event m_waitQueueWakeup;
|
||||
wpi::Event m_waitQueueWaiter;
|
||||
};
|
||||
|
||||
void ListenerStorage::Thread::Main() {
|
||||
while (m_active) {
|
||||
WPI_Handle signaledBuf[3];
|
||||
auto signaled = wpi::WaitForObjects(
|
||||
{m_poller, m_stopEvent.GetHandle(), m_waitQueueWakeup.GetHandle()},
|
||||
signaledBuf);
|
||||
if (signaled.empty() || !m_active) {
|
||||
return;
|
||||
}
|
||||
// call all the way back out to the C++ API to ensure valid handle
|
||||
auto events = nt::ReadListenerQueue(m_poller);
|
||||
if (events.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::unique_lock lock{m_mutex};
|
||||
for (auto&& event : events) {
|
||||
auto callbackIt = m_callbacks.find(event.listener);
|
||||
if (callbackIt != m_callbacks.end()) {
|
||||
auto callback = callbackIt->second;
|
||||
lock.unlock();
|
||||
callback(event);
|
||||
lock.lock();
|
||||
}
|
||||
}
|
||||
if (std::find(signaled.begin(), signaled.end(),
|
||||
m_waitQueueWakeup.GetHandle()) != signaled.end()) {
|
||||
m_waitQueueWaiter.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListenerStorage::ListenerStorage(int inst) : m_inst{inst} {}
|
||||
|
||||
ListenerStorage::~ListenerStorage() = default;
|
||||
|
||||
void ListenerStorage::Activate(NT_Listener listenerHandle, unsigned int mask,
|
||||
FinishEventFunc finishEvent) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (auto listener = m_listeners.Get(listenerHandle)) {
|
||||
listener->sources.emplace_back(std::move(finishEvent), mask);
|
||||
unsigned int deltaMask = mask & (~listener->eventMask);
|
||||
listener->eventMask |= mask;
|
||||
|
||||
if ((deltaMask & NT_EVENT_CONNECTION) != 0) {
|
||||
m_connListeners.Add(listener);
|
||||
}
|
||||
if ((deltaMask & NT_EVENT_TOPIC) != 0) {
|
||||
m_topicListeners.Add(listener);
|
||||
}
|
||||
if ((deltaMask & NT_EVENT_VALUE_ALL) != 0) {
|
||||
m_valueListeners.Add(listener);
|
||||
}
|
||||
// detect the higher log bits too; see LoggerImpl
|
||||
if ((deltaMask & NT_EVENT_LOGMESSAGE) != 0 ||
|
||||
(deltaMask & 0x1ff0000) != 0) {
|
||||
m_logListeners.Add(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ListenerStorage::Notify(std::span<const NT_Listener> handles,
|
||||
unsigned int flags,
|
||||
std::span<ConnectionInfo const* const> infos) {
|
||||
if (flags == 0) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
auto doSignal = [&](ListenerData& listener) {
|
||||
if ((flags & listener.eventMask) != 0) {
|
||||
for (auto&& [finishEvent, mask] : listener.sources) {
|
||||
if ((flags & mask) != 0) {
|
||||
for (auto&& info : infos) {
|
||||
listener.poller->queue.emplace_back(listener.handle, flags, *info);
|
||||
// finishEvent is never set (see ConnectionList)
|
||||
}
|
||||
}
|
||||
}
|
||||
listener.handle.Set();
|
||||
listener.poller->handle.Set();
|
||||
}
|
||||
};
|
||||
|
||||
if (!handles.empty()) {
|
||||
for (auto handle : handles) {
|
||||
if (auto listener = m_listeners.Get(handle)) {
|
||||
doSignal(*listener);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto&& listener : m_connListeners) {
|
||||
doSignal(*listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ListenerStorage::Notify(std::span<const NT_Listener> handles,
|
||||
unsigned int flags,
|
||||
std::span<const TopicInfo> infos) {
|
||||
if (flags == 0) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
auto doSignal = [&](ListenerData& listener) {
|
||||
if ((flags & listener.eventMask) != 0) {
|
||||
int count = 0;
|
||||
for (auto&& [finishEvent, mask] : listener.sources) {
|
||||
if ((flags & mask) != 0) {
|
||||
for (auto&& info : infos) {
|
||||
listener.poller->queue.emplace_back(listener.handle, flags, info);
|
||||
if (finishEvent &&
|
||||
!finishEvent(mask, &listener.poller->queue.back())) {
|
||||
listener.poller->queue.pop_back();
|
||||
} else {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
listener.handle.Set();
|
||||
listener.poller->handle.Set();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!handles.empty()) {
|
||||
for (auto handle : handles) {
|
||||
if (auto listener = m_listeners.Get(handle)) {
|
||||
doSignal(*listener);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto&& listener : m_topicListeners) {
|
||||
doSignal(*listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ListenerStorage::Notify(std::span<const NT_Listener> handles,
|
||||
unsigned int flags, NT_Topic topic,
|
||||
NT_Handle subentry, const Value& value) {
|
||||
if (flags == 0) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
auto doSignal = [&](ListenerData& listener) {
|
||||
if ((flags & listener.eventMask) != 0) {
|
||||
int count = 0;
|
||||
for (auto&& [finishEvent, mask] : listener.sources) {
|
||||
if ((flags & mask) != 0) {
|
||||
listener.poller->queue.emplace_back(listener.handle, flags, topic,
|
||||
subentry, value);
|
||||
if (finishEvent &&
|
||||
!finishEvent(mask, &listener.poller->queue.back())) {
|
||||
listener.poller->queue.pop_back();
|
||||
} else {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
listener.handle.Set();
|
||||
listener.poller->handle.Set();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!handles.empty()) {
|
||||
for (auto handle : handles) {
|
||||
if (auto listener = m_listeners.Get(handle)) {
|
||||
doSignal(*listener);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto&& listener : m_valueListeners) {
|
||||
doSignal(*listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ListenerStorage::Notify(unsigned int flags, unsigned int level,
|
||||
std::string_view filename, unsigned int line,
|
||||
std::string_view message) {
|
||||
if (flags == 0) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{m_mutex};
|
||||
for (auto&& listener : m_logListeners) {
|
||||
if ((flags & listener->eventMask) != 0) {
|
||||
int count = 0;
|
||||
for (auto&& [finishEvent, mask] : listener->sources) {
|
||||
if ((flags & mask) != 0) {
|
||||
listener->poller->queue.emplace_back(listener->handle, flags, level,
|
||||
filename, line, message);
|
||||
if (finishEvent &&
|
||||
!finishEvent(mask, &listener->poller->queue.back())) {
|
||||
listener->poller->queue.pop_back();
|
||||
} else {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
listener->handle.Set();
|
||||
listener->poller->handle.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NT_Listener ListenerStorage::AddListener(ListenerCallback callback) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (!m_thread) {
|
||||
m_thread.Start(m_pollers.Add(m_inst)->handle);
|
||||
}
|
||||
if (auto thr = m_thread.GetThread()) {
|
||||
auto listener = DoAddListener(thr->m_poller);
|
||||
if (listener) {
|
||||
thr->m_callbacks.try_emplace(listener, std::move(callback));
|
||||
}
|
||||
return listener;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
NT_Listener ListenerStorage::AddListener(NT_ListenerPoller pollerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return DoAddListener(pollerHandle);
|
||||
}
|
||||
|
||||
NT_Listener ListenerStorage::DoAddListener(NT_ListenerPoller pollerHandle) {
|
||||
if (auto poller = m_pollers.Get(pollerHandle)) {
|
||||
return m_listeners.Add(m_inst, poller)->handle;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
NT_ListenerPoller ListenerStorage::CreateListenerPoller() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return m_pollers.Add(m_inst)->handle;
|
||||
}
|
||||
|
||||
std::vector<std::pair<NT_Listener, unsigned int>>
|
||||
ListenerStorage::DestroyListenerPoller(NT_ListenerPoller pollerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (auto poller = m_pollers.Remove(pollerHandle)) {
|
||||
// ensure all listeners that use this poller are removed
|
||||
wpi::SmallVector<NT_Listener, 16> toRemove;
|
||||
for (auto&& listener : m_listeners) {
|
||||
if (listener->poller == poller.get()) {
|
||||
toRemove.emplace_back(listener->handle);
|
||||
}
|
||||
}
|
||||
return DoRemoveListeners(toRemove);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Event> ListenerStorage::ReadListenerQueue(
|
||||
NT_ListenerPoller pollerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (auto poller = m_pollers.Get(pollerHandle)) {
|
||||
std::vector<Event> rv;
|
||||
rv.swap(poller->queue);
|
||||
return rv;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<NT_Listener, unsigned int>>
|
||||
ListenerStorage::RemoveListener(NT_Listener listenerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return DoRemoveListeners({&listenerHandle, 1});
|
||||
}
|
||||
|
||||
bool ListenerStorage::WaitForListenerQueue(double timeout) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
WPI_EventHandle h;
|
||||
if (auto thr = m_thread.GetThread()) {
|
||||
h = thr->m_waitQueueWaiter.GetHandle();
|
||||
thr->m_waitQueueWakeup.Set();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
bool timedOut;
|
||||
return wpi::WaitForObject(h, timeout, &timedOut);
|
||||
}
|
||||
|
||||
std::vector<std::pair<NT_Listener, unsigned int>>
|
||||
ListenerStorage::DoRemoveListeners(std::span<const NT_Listener> handles) {
|
||||
std::vector<std::pair<NT_Listener, unsigned int>> rv;
|
||||
auto thr = m_thread.GetThread();
|
||||
for (auto handle : handles) {
|
||||
if (auto listener = m_listeners.Remove(handle)) {
|
||||
rv.emplace_back(handle, listener->eventMask);
|
||||
if (thr) {
|
||||
if (thr->m_poller == listener->poller->handle) {
|
||||
thr->m_callbacks.erase(handle);
|
||||
}
|
||||
}
|
||||
if ((listener->eventMask & NT_EVENT_CONNECTION) != 0) {
|
||||
m_connListeners.Remove(listener.get());
|
||||
}
|
||||
if ((listener->eventMask & NT_EVENT_TOPIC) != 0) {
|
||||
m_topicListeners.Remove(listener.get());
|
||||
}
|
||||
if ((listener->eventMask & NT_EVENT_VALUE_ALL) != 0) {
|
||||
m_valueListeners.Remove(listener.get());
|
||||
}
|
||||
if ((listener->eventMask & NT_EVENT_LOGMESSAGE) != 0 ||
|
||||
(listener->eventMask & 0x1ff0000) != 0) {
|
||||
m_logListeners.Remove(listener.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
111
ntcore/src/main/native/cpp/ListenerStorage.h
Normal file
111
ntcore/src/main/native/cpp/ListenerStorage.h
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/SafeThread.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <wpi/Synchronization.h>
|
||||
#include <wpi/mutex.h>
|
||||
|
||||
#include "Handle.h"
|
||||
#include "HandleMap.h"
|
||||
#include "IListenerStorage.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class ListenerStorage final : public IListenerStorage {
|
||||
public:
|
||||
explicit ListenerStorage(int inst);
|
||||
ListenerStorage(const ListenerStorage&) = delete;
|
||||
ListenerStorage& operator=(const ListenerStorage&) = delete;
|
||||
~ListenerStorage() final;
|
||||
|
||||
// IListenerStorage interface
|
||||
void Activate(NT_Listener listenerHandle, unsigned int mask,
|
||||
FinishEventFunc finishEvent = {}) final;
|
||||
void Notify(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
std::span<ConnectionInfo const* const> infos) final;
|
||||
void Notify(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
std::span<const TopicInfo> infos) final;
|
||||
void Notify(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
NT_Topic topic, NT_Handle subentry, const Value& value) final;
|
||||
void Notify(unsigned int flags, unsigned int level, std::string_view filename,
|
||||
unsigned int line, std::string_view message) final;
|
||||
|
||||
// user-facing functions
|
||||
NT_Listener AddListener(ListenerCallback callback);
|
||||
NT_Listener AddListener(NT_ListenerPoller pollerHandle);
|
||||
NT_ListenerPoller CreateListenerPoller();
|
||||
|
||||
// returns listener handle and mask for each listener that was destroyed
|
||||
[[nodiscard]] std::vector<std::pair<NT_Listener, unsigned int>>
|
||||
DestroyListenerPoller(NT_ListenerPoller pollerHandle);
|
||||
|
||||
std::vector<Event> ReadListenerQueue(NT_ListenerPoller pollerHandle);
|
||||
|
||||
// returns listener handle and mask for each listener that was destroyed
|
||||
[[nodiscard]] std::vector<std::pair<NT_Listener, unsigned int>>
|
||||
RemoveListener(NT_Listener listenerHandle);
|
||||
|
||||
bool WaitForListenerQueue(double timeout);
|
||||
|
||||
private:
|
||||
// these assume the mutex is already held
|
||||
NT_Listener DoAddListener(NT_ListenerPoller pollerHandle);
|
||||
std::vector<std::pair<NT_Listener, unsigned int>> DoRemoveListeners(
|
||||
std::span<const NT_Listener> handles);
|
||||
|
||||
int m_inst;
|
||||
mutable wpi::mutex m_mutex;
|
||||
|
||||
struct PollerData {
|
||||
static constexpr auto kType = Handle::kListenerPoller;
|
||||
|
||||
explicit PollerData(NT_ListenerPoller handle) : handle{handle} {}
|
||||
|
||||
wpi::SignalObject<NT_ListenerPoller> handle;
|
||||
std::vector<Event> queue;
|
||||
};
|
||||
HandleMap<PollerData, 8> m_pollers;
|
||||
|
||||
struct ListenerData {
|
||||
static constexpr auto kType = Handle::kListener;
|
||||
|
||||
ListenerData(NT_Listener handle, PollerData* poller)
|
||||
: handle{handle}, poller{poller} {}
|
||||
|
||||
wpi::SignalObject<NT_Listener> handle;
|
||||
PollerData* poller;
|
||||
wpi::SmallVector<std::pair<FinishEventFunc, unsigned int>, 2> sources;
|
||||
unsigned int eventMask{0};
|
||||
};
|
||||
HandleMap<ListenerData, 8> m_listeners;
|
||||
|
||||
// Utility wrapper for making a set-like vector
|
||||
template <typename T>
|
||||
class VectorSet : public std::vector<T> {
|
||||
public:
|
||||
void Add(T value) { this->push_back(value); }
|
||||
void Remove(T value) { std::erase(*this, value); }
|
||||
};
|
||||
|
||||
VectorSet<ListenerData*> m_connListeners;
|
||||
VectorSet<ListenerData*> m_topicListeners;
|
||||
VectorSet<ListenerData*> m_valueListeners;
|
||||
VectorSet<ListenerData*> m_logListeners;
|
||||
|
||||
class Thread;
|
||||
wpi::SafeThreadOwner<Thread> m_thread;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
File diff suppressed because it is too large
Load Diff
@@ -25,9 +25,12 @@ class Logger;
|
||||
|
||||
namespace nt {
|
||||
|
||||
class IListenerStorage;
|
||||
|
||||
class LocalStorage final : public net::ILocalStorage {
|
||||
public:
|
||||
LocalStorage(int inst, wpi::Logger& logger);
|
||||
LocalStorage(int inst, IListenerStorage& listenerStorage,
|
||||
wpi::Logger& logger);
|
||||
LocalStorage(const LocalStorage&) = delete;
|
||||
LocalStorage& operator=(const LocalStorage&) = delete;
|
||||
~LocalStorage() final;
|
||||
@@ -189,49 +192,15 @@ class LocalStorage final : public net::ILocalStorage {
|
||||
int64_t GetEntryLastChange(NT_Entry entry);
|
||||
|
||||
//
|
||||
// Topic listener functions
|
||||
// Listener functions
|
||||
//
|
||||
|
||||
NT_TopicListener AddTopicListener(
|
||||
std::span<const std::string_view> prefixes, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> callback);
|
||||
NT_TopicListener AddTopicListener(
|
||||
NT_Handle handle, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> callback);
|
||||
void AddListener(NT_Listener listener,
|
||||
std::span<const std::string_view> prefixes,
|
||||
unsigned int mask);
|
||||
void AddListener(NT_Listener listener, NT_Handle handle, unsigned int mask);
|
||||
|
||||
NT_TopicListenerPoller CreateTopicListenerPoller();
|
||||
void DestroyTopicListenerPoller(NT_TopicListenerPoller poller);
|
||||
|
||||
NT_TopicListener AddPolledTopicListener(
|
||||
NT_TopicListenerPoller poller, std::span<const std::string_view> prefixes,
|
||||
unsigned int mask);
|
||||
NT_TopicListener AddPolledTopicListener(NT_TopicListenerPoller poller,
|
||||
NT_Handle handle, unsigned int mask);
|
||||
|
||||
std::vector<TopicNotification> ReadTopicListenerQueue(
|
||||
NT_TopicListenerPoller poller);
|
||||
|
||||
void RemoveTopicListener(NT_TopicListener listener);
|
||||
|
||||
//
|
||||
// Value listener functions
|
||||
//
|
||||
|
||||
NT_ValueListener AddValueListener(
|
||||
NT_Handle subentry, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> callback);
|
||||
|
||||
NT_ValueListenerPoller CreateValueListenerPoller();
|
||||
void DestroyValueListenerPoller(NT_ValueListenerPoller poller);
|
||||
|
||||
NT_ValueListener AddPolledValueListener(NT_ValueListenerPoller poller,
|
||||
NT_Handle subentry,
|
||||
unsigned int mask);
|
||||
|
||||
std::vector<ValueNotification> ReadValueListenerQueue(
|
||||
NT_ValueListenerPoller poller);
|
||||
|
||||
void RemoveValueListener(NT_ValueListener listener);
|
||||
void RemoveListener(NT_Listener listener, unsigned int mask);
|
||||
|
||||
//
|
||||
// Data log functions
|
||||
|
||||
@@ -5,24 +5,27 @@
|
||||
#include "LoggerImpl.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <wpi/DenseMap.h>
|
||||
#include <wpi/Logger.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <wpi/fs.h>
|
||||
|
||||
#include "IListenerStorage.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
static void DefaultLogger(unsigned int level, const char* file,
|
||||
unsigned int line, const char* msg) {
|
||||
if (level == 20) {
|
||||
if (level == wpi::WPI_LOG_INFO) {
|
||||
fmt::print(stderr, "NT: {}\n", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string_view levelmsg;
|
||||
if (level >= 50) {
|
||||
if (level >= wpi::WPI_LOG_CRITICAL) {
|
||||
levelmsg = "CRITICAL";
|
||||
} else if (level >= 40) {
|
||||
} else if (level >= wpi::WPI_LOG_ERROR) {
|
||||
levelmsg = "ERROR";
|
||||
} else if (level >= 30) {
|
||||
} else if (level >= wpi::WPI_LOG_WARNING) {
|
||||
levelmsg = "WARNING";
|
||||
} else {
|
||||
return;
|
||||
@@ -30,108 +33,107 @@ static void DefaultLogger(unsigned int level, const char* file,
|
||||
fmt::print(stderr, "NT: {}: {} ({}:{})\n", levelmsg, msg, file, line);
|
||||
}
|
||||
|
||||
class LoggerImpl::Thread final : public wpi::SafeThreadEvent {
|
||||
public:
|
||||
explicit Thread(NT_LoggerPoller poller) : m_poller{poller} {}
|
||||
static constexpr unsigned int kFlagCritical = 1u << 16;
|
||||
static constexpr unsigned int kFlagError = 1u << 17;
|
||||
static constexpr unsigned int kFlagWarning = 1u << 18;
|
||||
static constexpr unsigned int kFlagInfo = 1u << 19;
|
||||
static constexpr unsigned int kFlagDebug = 1u << 20;
|
||||
static constexpr unsigned int kFlagDebug1 = 1u << 21;
|
||||
static constexpr unsigned int kFlagDebug2 = 1u << 22;
|
||||
static constexpr unsigned int kFlagDebug3 = 1u << 23;
|
||||
static constexpr unsigned int kFlagDebug4 = 1u << 24;
|
||||
|
||||
void Main() final;
|
||||
|
||||
NT_LoggerPoller m_poller;
|
||||
wpi::DenseMap<NT_Logger, std::function<void(const LogMessage& msg)>>
|
||||
m_callbacks;
|
||||
};
|
||||
|
||||
void LoggerImpl::Thread::Main() {
|
||||
while (m_active) {
|
||||
WPI_Handle signaledBuf[2];
|
||||
auto signaled =
|
||||
wpi::WaitForObjects({m_poller, m_stopEvent.GetHandle()}, signaledBuf);
|
||||
if (signaled.empty() || !m_active) {
|
||||
return;
|
||||
}
|
||||
// call all the way back out to the C++ API to ensure valid handle
|
||||
auto events = nt::ReadLoggerQueue(m_poller);
|
||||
if (events.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::unique_lock lock{m_mutex};
|
||||
for (auto&& event : events) {
|
||||
auto callbackIt = m_callbacks.find(event.logger);
|
||||
if (callbackIt != m_callbacks.end()) {
|
||||
auto callback = callbackIt->second;
|
||||
lock.unlock();
|
||||
callback(event);
|
||||
lock.lock();
|
||||
}
|
||||
}
|
||||
static unsigned int LevelToFlag(unsigned int level) {
|
||||
if (level >= wpi::WPI_LOG_CRITICAL) {
|
||||
return EventFlags::kLogMessage | kFlagCritical;
|
||||
} else if (level >= wpi::WPI_LOG_ERROR) {
|
||||
return EventFlags::kLogMessage | kFlagError;
|
||||
} else if (level >= wpi::WPI_LOG_WARNING) {
|
||||
return EventFlags::kLogMessage | kFlagWarning;
|
||||
} else if (level >= wpi::WPI_LOG_INFO) {
|
||||
return EventFlags::kLogMessage | kFlagInfo;
|
||||
} else if (level >= wpi::WPI_LOG_DEBUG) {
|
||||
return EventFlags::kLogMessage | kFlagDebug;
|
||||
} else if (level >= wpi::WPI_LOG_DEBUG1) {
|
||||
return EventFlags::kLogMessage | kFlagDebug1;
|
||||
} else if (level >= wpi::WPI_LOG_DEBUG2) {
|
||||
return EventFlags::kLogMessage | kFlagDebug2;
|
||||
} else if (level >= wpi::WPI_LOG_DEBUG3) {
|
||||
return EventFlags::kLogMessage | kFlagDebug3;
|
||||
} else if (level >= wpi::WPI_LOG_DEBUG4) {
|
||||
return EventFlags::kLogMessage | kFlagDebug4;
|
||||
} else {
|
||||
return EventFlags::kLogMessage;
|
||||
}
|
||||
}
|
||||
|
||||
LoggerImpl::LoggerImpl(int inst) : m_inst{inst} {}
|
||||
static unsigned int LevelsToEventMask(unsigned int minLevel,
|
||||
unsigned int maxLevel) {
|
||||
unsigned int mask = 0;
|
||||
if (minLevel <= wpi::WPI_LOG_CRITICAL && maxLevel >= wpi::WPI_LOG_CRITICAL) {
|
||||
mask |= kFlagCritical;
|
||||
}
|
||||
if (minLevel <= wpi::WPI_LOG_ERROR && maxLevel >= wpi::WPI_LOG_ERROR) {
|
||||
mask |= kFlagError;
|
||||
}
|
||||
if (minLevel <= wpi::WPI_LOG_WARNING && maxLevel >= wpi::WPI_LOG_WARNING) {
|
||||
mask |= kFlagWarning;
|
||||
}
|
||||
if (minLevel <= wpi::WPI_LOG_INFO && maxLevel >= wpi::WPI_LOG_INFO) {
|
||||
mask |= kFlagInfo;
|
||||
}
|
||||
if (minLevel <= wpi::WPI_LOG_DEBUG && maxLevel >= wpi::WPI_LOG_DEBUG) {
|
||||
mask |= kFlagDebug;
|
||||
}
|
||||
if (minLevel <= wpi::WPI_LOG_DEBUG1 && maxLevel >= wpi::WPI_LOG_DEBUG1) {
|
||||
mask |= kFlagDebug1;
|
||||
}
|
||||
if (minLevel <= wpi::WPI_LOG_DEBUG2 && maxLevel >= wpi::WPI_LOG_DEBUG2) {
|
||||
mask |= kFlagDebug2;
|
||||
}
|
||||
if (minLevel <= wpi::WPI_LOG_DEBUG3 && maxLevel >= wpi::WPI_LOG_DEBUG3) {
|
||||
mask |= kFlagDebug3;
|
||||
}
|
||||
if (minLevel <= wpi::WPI_LOG_DEBUG4 && maxLevel >= wpi::WPI_LOG_DEBUG4) {
|
||||
mask |= kFlagDebug4;
|
||||
}
|
||||
if (mask == 0) {
|
||||
mask = EventFlags::kLogMessage;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
LoggerImpl::LoggerImpl(IListenerStorage& listenerStorage)
|
||||
: m_listenerStorage{listenerStorage} {}
|
||||
|
||||
LoggerImpl::~LoggerImpl() = default;
|
||||
|
||||
NT_Logger LoggerImpl::Add(std::function<void(const LogMessage& msg)> callback,
|
||||
unsigned int minLevel, unsigned int maxLevel) {
|
||||
if (!m_thread) {
|
||||
m_thread.Start(CreatePoller());
|
||||
}
|
||||
if (auto thr = m_thread.GetThread()) {
|
||||
auto listener = AddPolled(thr->m_poller, minLevel, maxLevel);
|
||||
if (listener) {
|
||||
thr->m_callbacks.try_emplace(listener, std::move(callback));
|
||||
}
|
||||
return listener;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
void LoggerImpl::AddListener(NT_Listener listener, unsigned int minLevel,
|
||||
unsigned int maxLevel) {
|
||||
++m_listenerCount;
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_listenerLevels.emplace_back(listener, minLevel, maxLevel);
|
||||
m_listenerStorage.Activate(listener, LevelsToEventMask(minLevel, maxLevel),
|
||||
[](unsigned int mask, Event* event) {
|
||||
event->flags = NT_EVENT_LOGMESSAGE;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
NT_LoggerPoller LoggerImpl::CreatePoller() {
|
||||
void LoggerImpl::RemoveListener(NT_Listener listener) {
|
||||
--m_listenerCount;
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return m_pollers.Add(m_inst)->handle;
|
||||
}
|
||||
|
||||
void LoggerImpl::DestroyPoller(NT_LoggerPoller pollerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_pollers.Remove(pollerHandle);
|
||||
}
|
||||
|
||||
NT_Logger LoggerImpl::AddPolled(NT_LoggerPoller pollerHandle,
|
||||
unsigned int minLevel, unsigned int maxLevel) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (auto poller = m_pollers.Get(pollerHandle)) {
|
||||
return m_listeners.Add(m_inst, poller, minLevel, maxLevel)->handle;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<LogMessage> LoggerImpl::ReadQueue(NT_LoggerPoller pollerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (auto poller = m_pollers.Get(pollerHandle)) {
|
||||
std::vector<LogMessage> rv;
|
||||
rv.swap(poller->queue);
|
||||
return rv;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void LoggerImpl::Remove(NT_Logger listenerHandle) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_listeners.Remove(listenerHandle);
|
||||
if (auto thr = m_thread.GetThread()) {
|
||||
thr->m_callbacks.erase(listenerHandle);
|
||||
}
|
||||
std::erase_if(m_listenerLevels,
|
||||
[&](auto& v) { return v.listener == listener; });
|
||||
}
|
||||
|
||||
unsigned int LoggerImpl::GetMinLevel() {
|
||||
// return 0;
|
||||
std::scoped_lock lock{m_mutex};
|
||||
unsigned int level = NT_LOG_INFO;
|
||||
for (auto&& listener : m_listeners) {
|
||||
if (listener && listener->minLevel < level) {
|
||||
level = listener->minLevel;
|
||||
for (auto&& listenerLevel : m_listenerLevels) {
|
||||
if (listenerLevel.minLevel < level) {
|
||||
level = listenerLevel.minLevel;
|
||||
}
|
||||
}
|
||||
return level;
|
||||
@@ -140,19 +142,10 @@ unsigned int LoggerImpl::GetMinLevel() {
|
||||
void LoggerImpl::Log(unsigned int level, const char* file, unsigned int line,
|
||||
const char* msg) {
|
||||
auto filename = fs::path{file}.filename();
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (m_listeners.empty()) {
|
||||
DefaultLogger(level, filename.string().c_str(), line, msg);
|
||||
} else {
|
||||
for (auto&& listener : m_listeners) {
|
||||
if (level >= listener->minLevel && level <= listener->maxLevel) {
|
||||
listener->poller->queue.emplace_back(listener->handle.GetHandle(),
|
||||
level, file, line, msg);
|
||||
listener->poller->handle.Set();
|
||||
listener->handle.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_listenerCount == 0) {
|
||||
DefaultLogger(level, filename.string().c_str(), line, msg);
|
||||
} else {
|
||||
m_listenerStorage.Notify(LevelToFlag(level), level, filename.string(), line,
|
||||
msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,34 +4,29 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/SafeThread.h>
|
||||
#include <wpi/Synchronization.h>
|
||||
#include <wpi/mutex.h>
|
||||
|
||||
#include "Handle.h"
|
||||
#include "HandleMap.h"
|
||||
#include "IListenerStorage.h"
|
||||
#include "ntcore_c.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class IListenerStorage;
|
||||
|
||||
class LoggerImpl {
|
||||
public:
|
||||
explicit LoggerImpl(int inst);
|
||||
explicit LoggerImpl(IListenerStorage& listenerStorage);
|
||||
LoggerImpl(const LoggerImpl&) = delete;
|
||||
LoggerImpl& operator=(const LoggerImpl&) = delete;
|
||||
~LoggerImpl();
|
||||
|
||||
NT_Logger Add(std::function<void(const LogMessage& msg)> callback,
|
||||
unsigned int minLevel, unsigned int maxLevel);
|
||||
|
||||
NT_LoggerPoller CreatePoller();
|
||||
void DestroyPoller(NT_LoggerPoller pollerHandle);
|
||||
NT_Logger AddPolled(NT_LoggerPoller pollerHandle, unsigned int minLevel,
|
||||
unsigned int maxLevel);
|
||||
std::vector<LogMessage> ReadQueue(NT_LoggerPoller pollerHandle);
|
||||
void Remove(NT_Logger listenerHandle);
|
||||
void AddListener(NT_Listener listener, unsigned int minLevel,
|
||||
unsigned int maxLevel);
|
||||
void RemoveListener(NT_Listener listener);
|
||||
|
||||
unsigned int GetMinLevel();
|
||||
|
||||
@@ -39,38 +34,20 @@ class LoggerImpl {
|
||||
const char* msg);
|
||||
|
||||
private:
|
||||
int m_inst;
|
||||
mutable wpi::mutex m_mutex;
|
||||
IListenerStorage& m_listenerStorage;
|
||||
std::atomic_int m_listenerCount{0};
|
||||
wpi::mutex m_mutex;
|
||||
|
||||
struct PollerData {
|
||||
static constexpr auto kType = Handle::kLoggerPoller;
|
||||
struct ListenerLevels {
|
||||
ListenerLevels(NT_Listener listener, unsigned int minLevel,
|
||||
unsigned int maxLevel)
|
||||
: listener{listener}, minLevel{minLevel}, maxLevel{maxLevel} {}
|
||||
|
||||
explicit PollerData(NT_LoggerPoller handle) : handle{handle} {}
|
||||
|
||||
wpi::SignalObject<NT_LoggerPoller> handle;
|
||||
std::vector<LogMessage> queue;
|
||||
};
|
||||
HandleMap<PollerData, 8> m_pollers;
|
||||
|
||||
struct ListenerData {
|
||||
static constexpr auto kType = Handle::kLogger;
|
||||
|
||||
ListenerData(NT_Logger handle, PollerData* poller, unsigned int minLevel,
|
||||
unsigned int maxLevel)
|
||||
: handle{handle},
|
||||
poller{poller},
|
||||
minLevel{minLevel},
|
||||
maxLevel{maxLevel} {}
|
||||
|
||||
wpi::SignalObject<NT_Logger> handle;
|
||||
PollerData* poller;
|
||||
NT_Listener listener;
|
||||
unsigned int minLevel;
|
||||
unsigned int maxLevel;
|
||||
};
|
||||
HandleMap<ListenerData, 8> m_listeners;
|
||||
|
||||
class Thread;
|
||||
wpi::SafeThreadOwner<Thread> m_thread;
|
||||
std::vector<ListenerLevels> m_listenerLevels;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
@@ -242,7 +242,9 @@ NCImpl3::~NCImpl3() {
|
||||
|
||||
void NCImpl3::HandleLocal() {
|
||||
m_localQueue.ReadQueue(&m_localMsgs);
|
||||
m_clientImpl->HandleLocal(m_localMsgs);
|
||||
if (m_clientImpl) {
|
||||
m_clientImpl->HandleLocal(m_localMsgs);
|
||||
}
|
||||
}
|
||||
|
||||
void NCImpl3::TcpConnected(uv::Tcp& tcp) {
|
||||
@@ -354,8 +356,10 @@ NCImpl4::NCImpl4(int inst, std::string_view id,
|
||||
// set up flush async
|
||||
m_flush = uv::Async<>::Create(m_loop);
|
||||
m_flush->wakeup.connect([this] {
|
||||
HandleLocal();
|
||||
m_clientImpl->SendValues(m_loop.Now().count());
|
||||
if (m_clientImpl) {
|
||||
HandleLocal();
|
||||
m_clientImpl->SendValues(m_loop.Now().count());
|
||||
}
|
||||
});
|
||||
m_flushAtomic = m_flush.get();
|
||||
|
||||
|
||||
@@ -287,7 +287,7 @@ ServerConnection3::ServerConnection3(std::shared_ptr<uv::Stream> stream,
|
||||
m_info.remote_id = name;
|
||||
m_info.protocol_version = proto;
|
||||
m_server.AddConnection(this, m_info);
|
||||
INFO("CONNECTED NT4 client '{}' (from {})", name, m_connInfo);
|
||||
INFO("CONNECTED NT3 client '{}' (from {})", name, m_connInfo);
|
||||
},
|
||||
[this](uint32_t repeatMs) { UpdatePeriodicTimer(repeatMs); });
|
||||
|
||||
|
||||
@@ -34,15 +34,14 @@ void JNI_UnloadTypes(JNIEnv* env);
|
||||
static JavaVM* jvm = nullptr;
|
||||
static JClass booleanCls;
|
||||
static JClass connectionInfoCls;
|
||||
static JClass connectionNotificationCls;
|
||||
static JClass doubleCls;
|
||||
static JClass eventCls;
|
||||
static JClass floatCls;
|
||||
static JClass logMessageCls;
|
||||
static JClass longCls;
|
||||
static JClass topicInfoCls;
|
||||
static JClass topicNotificationCls;
|
||||
static JClass valueCls;
|
||||
static JClass valueNotificationCls;
|
||||
static JClass valueEventDataCls;
|
||||
static JException illegalArgEx;
|
||||
static JException interruptedEx;
|
||||
static JException nullPointerEx;
|
||||
@@ -50,16 +49,14 @@ static JException nullPointerEx;
|
||||
static const JClassInit classes[] = {
|
||||
{"java/lang/Boolean", &booleanCls},
|
||||
{"edu/wpi/first/networktables/ConnectionInfo", &connectionInfoCls},
|
||||
{"edu/wpi/first/networktables/ConnectionNotification",
|
||||
&connectionNotificationCls},
|
||||
{"java/lang/Double", &doubleCls},
|
||||
{"edu/wpi/first/networktables/NetworkTableEvent", &eventCls},
|
||||
{"java/lang/Float", &floatCls},
|
||||
{"edu/wpi/first/networktables/LogMessage", &logMessageCls},
|
||||
{"java/lang/Long", &longCls},
|
||||
{"edu/wpi/first/networktables/TopicInfo", &topicInfoCls},
|
||||
{"edu/wpi/first/networktables/TopicNotification", &topicNotificationCls},
|
||||
{"edu/wpi/first/networktables/NetworkTableValue", &valueCls},
|
||||
{"edu/wpi/first/networktables/ValueNotification", &valueNotificationCls}};
|
||||
{"edu/wpi/first/networktables/ValueEventData", &valueEventDataCls}};
|
||||
|
||||
static const JExceptionInit exceptions[] = {
|
||||
{"java/lang/IllegalArgumentException", &illegalArgEx},
|
||||
@@ -216,29 +213,12 @@ static jobject MakeJObject(JNIEnv* env, const nt::ConnectionInfo& info) {
|
||||
static_cast<jint>(info.protocol_version));
|
||||
}
|
||||
|
||||
static jobject MakeJObject(JNIEnv* env, jobject inst,
|
||||
const nt::ConnectionNotification& notification) {
|
||||
static jobject MakeJObject(JNIEnv* env, const nt::LogMessage& msg) {
|
||||
static jmethodID constructor = env->GetMethodID(
|
||||
connectionNotificationCls, "<init>",
|
||||
"(Ledu/wpi/first/networktables/NetworkTableInstance;IZLedu/wpi/first/"
|
||||
"networktables/ConnectionInfo;)V");
|
||||
JLocal<jobject> conn{env, MakeJObject(env, notification.conn)};
|
||||
return env->NewObject(connectionNotificationCls, constructor, inst,
|
||||
static_cast<jint>(notification.listener),
|
||||
static_cast<jboolean>(notification.connected),
|
||||
conn.obj());
|
||||
}
|
||||
|
||||
static jobject MakeJObject(JNIEnv* env, jobject inst,
|
||||
const nt::LogMessage& msg) {
|
||||
static jmethodID constructor = env->GetMethodID(
|
||||
logMessageCls, "<init>",
|
||||
"(Ledu/wpi/first/networktables/NetworkTableInstance;IILjava/lang/"
|
||||
"String;ILjava/lang/String;)V");
|
||||
logMessageCls, "<init>", "(ILjava/lang/String;ILjava/lang/String;)V");
|
||||
JLocal<jstring> filename{env, MakeJString(env, msg.filename)};
|
||||
JLocal<jstring> message{env, MakeJString(env, msg.message)};
|
||||
return env->NewObject(logMessageCls, constructor, inst,
|
||||
static_cast<jint>(msg.logger),
|
||||
return env->NewObject(logMessageCls, constructor,
|
||||
static_cast<jint>(msg.level), filename.obj(),
|
||||
static_cast<jint>(msg.line), message.obj());
|
||||
}
|
||||
@@ -257,28 +237,42 @@ static jobject MakeJObject(JNIEnv* env, jobject inst,
|
||||
}
|
||||
|
||||
static jobject MakeJObject(JNIEnv* env, jobject inst,
|
||||
const nt::TopicNotification& notification) {
|
||||
const nt::ValueEventData& data) {
|
||||
static jmethodID constructor =
|
||||
env->GetMethodID(topicNotificationCls, "<init>",
|
||||
"(ILedu/wpi/first/networktables/TopicInfo;I)V");
|
||||
JLocal<jobject> info{env, MakeJObject(env, inst, notification.info)};
|
||||
return env->NewObject(topicNotificationCls, constructor,
|
||||
static_cast<jint>(notification.listener), info.obj(),
|
||||
static_cast<jint>(notification.flags));
|
||||
env->GetMethodID(valueEventDataCls, "<init>",
|
||||
"(Ledu/wpi/first/networktables/NetworkTableInstance;II"
|
||||
"Ledu/wpi/first/networktables/NetworkTableValue;)V");
|
||||
JLocal<jobject> value{env, MakeJValue(env, data.value)};
|
||||
return env->NewObject(valueEventDataCls, constructor, inst,
|
||||
static_cast<jint>(data.topic),
|
||||
static_cast<jint>(data.subentry), value.obj());
|
||||
}
|
||||
|
||||
static jobject MakeJObject(JNIEnv* env, jobject inst,
|
||||
const nt::ValueNotification& notification) {
|
||||
static jobject MakeJObject(JNIEnv* env, jobject inst, const nt::Event& event) {
|
||||
static jmethodID constructor =
|
||||
env->GetMethodID(valueNotificationCls, "<init>",
|
||||
"(Ledu/wpi/first/networktables/NetworkTableInstance;III"
|
||||
"Ledu/wpi/first/networktables/NetworkTableValue;I)V");
|
||||
JLocal<jobject> value{env, MakeJValue(env, notification.value)};
|
||||
return env->NewObject(valueNotificationCls, constructor, inst,
|
||||
static_cast<jint>(notification.listener),
|
||||
static_cast<jint>(notification.topic),
|
||||
static_cast<jint>(notification.subentry), value.obj(),
|
||||
static_cast<jint>(notification.flags));
|
||||
env->GetMethodID(eventCls, "<init>",
|
||||
"(Ledu/wpi/first/networktables/NetworkTableInstance;II"
|
||||
"Ledu/wpi/first/networktables/ConnectionInfo;"
|
||||
"Ledu/wpi/first/networktables/TopicInfo;"
|
||||
"Ledu/wpi/first/networktables/ValueEventData;"
|
||||
"Ledu/wpi/first/networktables/LogMessage;)V");
|
||||
JLocal<jobject> connInfo{env, nullptr};
|
||||
JLocal<jobject> topicInfo{env, nullptr};
|
||||
JLocal<jobject> valueData{env, nullptr};
|
||||
JLocal<jobject> logMessage{env, nullptr};
|
||||
if (auto v = event.GetConnectionInfo()) {
|
||||
connInfo = JLocal<jobject>{env, MakeJObject(env, *v)};
|
||||
} else if (auto v = event.GetTopicInfo()) {
|
||||
topicInfo = JLocal<jobject>{env, MakeJObject(env, inst, *v)};
|
||||
} else if (auto v = event.GetValueEventData()) {
|
||||
valueData = JLocal<jobject>{env, MakeJObject(env, inst, *v)};
|
||||
} else if (auto v = event.GetLogMessage()) {
|
||||
logMessage = JLocal<jobject>{env, MakeJObject(env, *v)};
|
||||
}
|
||||
return env->NewObject(eventCls, constructor, inst,
|
||||
static_cast<jint>(event.listener),
|
||||
static_cast<jint>(event.flags), connInfo.obj(),
|
||||
topicInfo.obj(), valueData.obj(), logMessage.obj());
|
||||
}
|
||||
|
||||
static jobjectArray MakeJObject(JNIEnv* env, std::span<const nt::Value> arr) {
|
||||
@@ -293,52 +287,9 @@ static jobjectArray MakeJObject(JNIEnv* env, std::span<const nt::Value> arr) {
|
||||
return jarr;
|
||||
}
|
||||
|
||||
static jobjectArray MakeJObject(
|
||||
JNIEnv* env, jobject inst,
|
||||
std::span<const nt::ConnectionNotification> arr) {
|
||||
jobjectArray jarr =
|
||||
env->NewObjectArray(arr.size(), connectionNotificationCls, nullptr);
|
||||
if (!jarr) {
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t i = 0; i < arr.size(); ++i) {
|
||||
JLocal<jobject> elem{env, MakeJObject(env, inst, arr[i])};
|
||||
env->SetObjectArrayElement(jarr, i, elem.obj());
|
||||
}
|
||||
return jarr;
|
||||
}
|
||||
|
||||
static jobjectArray MakeJObject(JNIEnv* env, jobject inst,
|
||||
std::span<const nt::LogMessage> arr) {
|
||||
jobjectArray jarr = env->NewObjectArray(arr.size(), logMessageCls, nullptr);
|
||||
if (!jarr) {
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t i = 0; i < arr.size(); ++i) {
|
||||
JLocal<jobject> elem{env, MakeJObject(env, inst, arr[i])};
|
||||
env->SetObjectArrayElement(jarr, i, elem.obj());
|
||||
}
|
||||
return jarr;
|
||||
}
|
||||
|
||||
static jobjectArray MakeJObject(JNIEnv* env, jobject inst,
|
||||
std::span<const nt::TopicNotification> arr) {
|
||||
jobjectArray jarr =
|
||||
env->NewObjectArray(arr.size(), topicNotificationCls, nullptr);
|
||||
if (!jarr) {
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t i = 0; i < arr.size(); ++i) {
|
||||
JLocal<jobject> elem{env, MakeJObject(env, inst, arr[i])};
|
||||
env->SetObjectArrayElement(jarr, i, elem.obj());
|
||||
}
|
||||
return jarr;
|
||||
}
|
||||
|
||||
static jobjectArray MakeJObject(JNIEnv* env, jobject inst,
|
||||
std::span<const nt::ValueNotification> arr) {
|
||||
jobjectArray jarr =
|
||||
env->NewObjectArray(arr.size(), valueNotificationCls, nullptr);
|
||||
std::span<const nt::Event> arr) {
|
||||
jobjectArray jarr = env->NewObjectArray(arr.size(), eventCls, nullptr);
|
||||
if (!jarr) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1013,35 +964,35 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_getTopicInfo
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: createTopicListenerPoller
|
||||
* Method: createListenerPoller
|
||||
* Signature: (I)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_createTopicListenerPoller
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_createListenerPoller
|
||||
(JNIEnv*, jclass, jint inst)
|
||||
{
|
||||
return nt::CreateTopicListenerPoller(inst);
|
||||
return nt::CreateListenerPoller(inst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: destroyTopicListenerPoller
|
||||
* Method: destroyListenerPoller
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_destroyTopicListenerPoller
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_destroyListenerPoller
|
||||
(JNIEnv*, jclass, jint poller)
|
||||
{
|
||||
nt::DestroyTopicListenerPoller(poller);
|
||||
nt::DestroyListenerPoller(poller);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: addPolledTopicListener
|
||||
* Method: addListener
|
||||
* Signature: (I[Ljava/lang/Object;I)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_addPolledTopicListener__I_3Ljava_lang_String_2I
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_addListener__I_3Ljava_lang_String_2I
|
||||
(JNIEnv* env, jclass, jint poller, jobjectArray prefixes, jint flags)
|
||||
{
|
||||
if (!prefixes) {
|
||||
@@ -1066,163 +1017,43 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_addPolledTopicListener__I_3Lja
|
||||
arrview.emplace_back(arr.back());
|
||||
}
|
||||
|
||||
return nt::AddPolledTopicListener(poller, arrview, flags);
|
||||
return nt::AddPolledListener(poller, arrview, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: addPolledTopicListener
|
||||
* Method: addListener
|
||||
* Signature: (III)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_addPolledTopicListener__III
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_addListener__III
|
||||
(JNIEnv* env, jclass, jint poller, jint handle, jint flags)
|
||||
{
|
||||
return nt::AddPolledTopicListener(poller, handle, flags);
|
||||
return nt::AddPolledListener(poller, handle, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: readTopicListenerQueue
|
||||
* Method: readListenerQueue
|
||||
* Signature: (Ljava/lang/Object;I)[Ljava/lang/Object;
|
||||
*/
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_readTopicListenerQueue
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_readListenerQueue
|
||||
(JNIEnv* env, jclass, jobject inst, jint poller)
|
||||
{
|
||||
return MakeJObject(env, inst, nt::ReadTopicListenerQueue(poller));
|
||||
return MakeJObject(env, inst, nt::ReadListenerQueue(poller));
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: removeTopicListener
|
||||
* Method: removeListener
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_removeTopicListener
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_removeListener
|
||||
(JNIEnv*, jclass, jint topicListener)
|
||||
{
|
||||
nt::RemoveTopicListener(topicListener);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: createValueListenerPoller
|
||||
* Signature: (I)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_createValueListenerPoller
|
||||
(JNIEnv*, jclass, jint inst)
|
||||
{
|
||||
return nt::CreateValueListenerPoller(inst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: destroyValueListenerPoller
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_destroyValueListenerPoller
|
||||
(JNIEnv*, jclass, jint poller)
|
||||
{
|
||||
nt::DestroyValueListenerPoller(poller);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: addPolledValueListener
|
||||
* Signature: (III)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_addPolledValueListener
|
||||
(JNIEnv* env, jclass, jint poller, jint topic, jint flags)
|
||||
{
|
||||
return nt::AddPolledValueListener(poller, topic, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: readValueListenerQueue
|
||||
* Signature: (Ljava/lang/Object;I)[Ljava/lang/Object;
|
||||
*/
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_readValueListenerQueue
|
||||
(JNIEnv* env, jclass, jobject inst, jint poller)
|
||||
{
|
||||
return MakeJObject(env, inst, nt::ReadValueListenerQueue(poller));
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: removeValueListener
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_removeValueListener
|
||||
(JNIEnv*, jclass, jint topicListener)
|
||||
{
|
||||
nt::RemoveValueListener(topicListener);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: createConnectionListenerPoller
|
||||
* Signature: (I)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_createConnectionListenerPoller
|
||||
(JNIEnv*, jclass, jint inst)
|
||||
{
|
||||
return nt::CreateConnectionListenerPoller(inst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: destroyConnectionListenerPoller
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_destroyConnectionListenerPoller
|
||||
(JNIEnv*, jclass, jint poller)
|
||||
{
|
||||
nt::DestroyConnectionListenerPoller(poller);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: addPolledConnectionListener
|
||||
* Signature: (IZ)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_addPolledConnectionListener
|
||||
(JNIEnv* env, jclass, jint poller, jboolean immediateNotify)
|
||||
{
|
||||
return nt::AddPolledConnectionListener(poller, immediateNotify);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: readConnectionListenerQueue
|
||||
* Signature: (Ljava/lang/Object;I)[Ljava/lang/Object;
|
||||
*/
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_readConnectionListenerQueue
|
||||
(JNIEnv* env, jclass, jobject inst, jint poller)
|
||||
{
|
||||
return MakeJObject(env, inst, nt::ReadConnectionListenerQueue(poller));
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: removeConnectionListener
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_removeConnectionListener
|
||||
(JNIEnv*, jclass, jint connListenerUid)
|
||||
{
|
||||
nt::RemoveConnectionListener(connListenerUid);
|
||||
nt::RemoveListener(topicListener);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1561,62 +1392,14 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_stopConnectionDataLog
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: createLoggerPoller
|
||||
* Signature: (I)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_createLoggerPoller
|
||||
(JNIEnv*, jclass, jint inst)
|
||||
{
|
||||
return nt::CreateLoggerPoller(inst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: destroyLoggerPoller
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_destroyLoggerPoller
|
||||
(JNIEnv*, jclass, jint poller)
|
||||
{
|
||||
nt::DestroyLoggerPoller(poller);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: addPolledLogger
|
||||
* Method: addLogger
|
||||
* Signature: (III)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_addPolledLogger
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_addLogger
|
||||
(JNIEnv*, jclass, jint poller, jint minLevel, jint maxLevel)
|
||||
{
|
||||
return nt::AddPolledLogger(poller, minLevel, maxLevel);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: readLoggerQueue
|
||||
* Signature: (Ljava/lang/Object;I)[Ljava/lang/Object;
|
||||
*/
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_readLoggerQueue
|
||||
(JNIEnv* env, jclass, jobject inst, jint poller)
|
||||
{
|
||||
return MakeJObject(env, inst, nt::ReadLoggerQueue(poller));
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: removeLogger
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_removeLogger
|
||||
(JNIEnv*, jclass, jint logger)
|
||||
{
|
||||
nt::RemoveLogger(logger);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "networktables/FloatTopic.h"
|
||||
#include "networktables/IntegerArrayTopic.h"
|
||||
#include "networktables/IntegerTopic.h"
|
||||
#include "networktables/MultiSubscriber.h"
|
||||
#include "networktables/RawTopic.h"
|
||||
#include "networktables/StringArrayTopic.h"
|
||||
#include "networktables/StringTopic.h"
|
||||
@@ -100,8 +101,9 @@ void NetworkTableInstance::SetServer(std::span<const std::string_view> servers,
|
||||
SetServer(serversArr);
|
||||
}
|
||||
|
||||
NT_ConnectionListener NetworkTableInstance::AddConnectionListener(
|
||||
std::function<void(const ConnectionNotification& event)> callback,
|
||||
bool immediate_notify) const {
|
||||
return ::nt::AddConnectionListener(m_handle, callback, immediate_notify);
|
||||
NT_Listener NetworkTableInstance::AddListener(MultiSubscriber& subscriber,
|
||||
int eventMask,
|
||||
ListenerCallback listener) {
|
||||
return ::nt::AddListener(subscriber.GetHandle(), eventMask,
|
||||
std::move(listener));
|
||||
}
|
||||
|
||||
@@ -37,35 +37,42 @@ static void ConvertToC(const ConnectionInfo& in, NT_ConnectionInfo* out) {
|
||||
out->protocol_version = in.protocol_version;
|
||||
}
|
||||
|
||||
static void ConvertToC(const TopicNotification& in, NT_TopicNotification* out) {
|
||||
out->listener = in.listener;
|
||||
ConvertToC(in.info, &out->info);
|
||||
out->flags = in.flags;
|
||||
}
|
||||
|
||||
static void ConvertToC(const ValueNotification& in, NT_ValueNotification* out) {
|
||||
out->listener = in.listener;
|
||||
static void ConvertToC(const ValueEventData& in, NT_ValueEventData* out) {
|
||||
out->topic = in.topic;
|
||||
out->subentry = in.subentry;
|
||||
ConvertToC(in.value, &out->value);
|
||||
out->flags = in.flags;
|
||||
}
|
||||
|
||||
static void ConvertToC(const ConnectionNotification& in,
|
||||
NT_ConnectionNotification* out) {
|
||||
out->listener = in.listener;
|
||||
out->connected = in.connected;
|
||||
ConvertToC(in.conn, &out->conn);
|
||||
}
|
||||
|
||||
static void ConvertToC(const LogMessage& in, NT_LogMessage* out) {
|
||||
out->logger = in.logger;
|
||||
out->level = in.level;
|
||||
ConvertToC(in.filename, &out->filename);
|
||||
out->line = in.line;
|
||||
ConvertToC(in.message, &out->message);
|
||||
}
|
||||
|
||||
static void ConvertToC(const Event& in, NT_Event* out) {
|
||||
out->listener = in.listener;
|
||||
out->flags = in.flags;
|
||||
if ((in.flags & NT_EVENT_VALUE_ALL) != 0) {
|
||||
if (auto v = in.GetValueEventData()) {
|
||||
return ConvertToC(*v, &out->data.valueData);
|
||||
}
|
||||
} else if ((in.flags & NT_EVENT_TOPIC) != 0) {
|
||||
if (auto v = in.GetTopicInfo()) {
|
||||
return ConvertToC(*v, &out->data.topicInfo);
|
||||
}
|
||||
} else if ((in.flags & NT_EVENT_CONNECTION) != 0) {
|
||||
if (auto v = in.GetConnectionInfo()) {
|
||||
return ConvertToC(*v, &out->data.connInfo);
|
||||
}
|
||||
} else if ((in.flags & NT_EVENT_LOGMESSAGE) != 0) {
|
||||
if (auto v = in.GetLogMessage()) {
|
||||
return ConvertToC(*v, &out->data.logMessage);
|
||||
}
|
||||
}
|
||||
out->flags = NT_EVENT_NONE; // sanity to make sure we don't dispose
|
||||
}
|
||||
|
||||
static void DisposeConnectionInfo(NT_ConnectionInfo* info) {
|
||||
std::free(info->remote_id.str);
|
||||
std::free(info->remote_ip.str);
|
||||
@@ -77,16 +84,21 @@ static void DisposeTopicInfo(NT_TopicInfo* info) {
|
||||
std::free(info->properties.str);
|
||||
}
|
||||
|
||||
static void DisposeTopicNotification(NT_TopicNotification* info) {
|
||||
DisposeTopicInfo(&info->info);
|
||||
static void DisposeLogMessage(NT_LogMessage* msg) {
|
||||
std::free(msg->filename);
|
||||
std::free(msg->message);
|
||||
}
|
||||
|
||||
static void DisposeValueNotification(NT_ValueNotification* info) {
|
||||
NT_DisposeValue(&info->value);
|
||||
}
|
||||
|
||||
static void DisposeConnectionNotification(NT_ConnectionNotification* info) {
|
||||
DisposeConnectionInfo(&info->conn);
|
||||
static void DisposeEvent(NT_Event* event) {
|
||||
if ((event->flags & NT_EVENT_VALUE_ALL) != 0) {
|
||||
NT_DisposeValue(&event->data.valueData.value);
|
||||
} else if ((event->flags & NT_EVENT_TOPIC) != 0) {
|
||||
DisposeTopicInfo(&event->data.topicInfo);
|
||||
} else if ((event->flags & NT_EVENT_CONNECTION) != 0) {
|
||||
DisposeConnectionInfo(&event->data.connInfo);
|
||||
} else if ((event->flags & NT_EVENT_LOGMESSAGE) != 0) {
|
||||
DisposeLogMessage(&event->data.logMessage);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
@@ -383,159 +395,87 @@ NT_Topic NT_GetTopicFromHandle(NT_Handle pubsubentry) {
|
||||
* Callback Creation Functions
|
||||
*/
|
||||
|
||||
NT_TopicListener NT_AddTopicListener(NT_Inst inst, const char* prefix,
|
||||
size_t prefix_len, unsigned int mask,
|
||||
void* data,
|
||||
NT_TopicListenerCallback callback) {
|
||||
NT_ListenerPoller NT_CreateListenerPoller(NT_Inst inst) {
|
||||
return nt::CreateListenerPoller(inst);
|
||||
}
|
||||
|
||||
void NT_DestroyListenerPoller(NT_ListenerPoller poller) {
|
||||
nt::DestroyListenerPoller(poller);
|
||||
}
|
||||
|
||||
struct NT_Event* NT_ReadListenerQueue(NT_ListenerPoller poller, size_t* len) {
|
||||
auto arr_cpp = nt::ReadListenerQueue(poller);
|
||||
return ConvertToC<NT_Event>(arr_cpp, len);
|
||||
}
|
||||
|
||||
void NT_RemoveListener(NT_Listener listener) {
|
||||
nt::RemoveListener(listener);
|
||||
}
|
||||
|
||||
NT_Bool NT_WaitForListenerQueue(NT_Handle handle, double timeout) {
|
||||
return nt::WaitForListenerQueue(handle, timeout);
|
||||
}
|
||||
|
||||
NT_Listener NT_AddListenerSingle(NT_Inst inst, const char* prefix,
|
||||
size_t prefix_len, unsigned int mask,
|
||||
void* data, NT_ListenerCallback callback) {
|
||||
std::string_view p{prefix, prefix_len};
|
||||
return nt::AddTopicListener(inst, {{p}}, mask, [=](auto& event) {
|
||||
NT_TopicNotification event_c;
|
||||
return nt::AddListener(inst, {{p}}, mask, [=](auto& event) {
|
||||
NT_Event event_c;
|
||||
ConvertToC(event, &event_c);
|
||||
callback(data, &event_c);
|
||||
DisposeTopicNotification(&event_c);
|
||||
DisposeEvent(&event_c);
|
||||
});
|
||||
}
|
||||
|
||||
NT_TopicListener NT_AddTopicListenerMultiple(
|
||||
NT_Inst inst, const NT_String* prefixes, size_t prefixes_len,
|
||||
unsigned int mask, void* data, NT_TopicListenerCallback callback) {
|
||||
NT_Listener NT_AddListenerMultiple(NT_Inst inst, const NT_String* prefixes,
|
||||
size_t prefixes_len, unsigned int mask,
|
||||
void* data, NT_ListenerCallback callback) {
|
||||
wpi::SmallVector<std::string_view, 8> p;
|
||||
p.reserve(prefixes_len);
|
||||
for (size_t i = 0; i < prefixes_len; ++i) {
|
||||
p.emplace_back(prefixes[i].str, prefixes[i].len);
|
||||
}
|
||||
return nt::AddTopicListener(inst, p, mask, [=](auto& event) {
|
||||
NT_TopicNotification event_c;
|
||||
return nt::AddListener(inst, p, mask, [=](auto& event) {
|
||||
NT_Event event_c;
|
||||
ConvertToC(event, &event_c);
|
||||
callback(data, &event_c);
|
||||
DisposeTopicNotification(&event_c);
|
||||
DisposeEvent(&event_c);
|
||||
});
|
||||
}
|
||||
|
||||
NT_TopicListener NT_AddTopicListenerSingle(NT_Topic topic, unsigned int mask,
|
||||
void* data,
|
||||
NT_TopicListenerCallback callback) {
|
||||
return nt::AddTopicListener(topic, mask, [=](auto& event) {
|
||||
NT_TopicNotification event_c;
|
||||
NT_Listener NT_AddListener(NT_Topic topic, unsigned int mask, void* data,
|
||||
NT_ListenerCallback callback) {
|
||||
return nt::AddListener(topic, mask, [=](auto& event) {
|
||||
NT_Event event_c;
|
||||
ConvertToC(event, &event_c);
|
||||
callback(data, &event_c);
|
||||
DisposeTopicNotification(&event_c);
|
||||
DisposeEvent(&event_c);
|
||||
});
|
||||
}
|
||||
|
||||
NT_TopicListenerPoller NT_CreateTopicListenerPoller(NT_Inst inst) {
|
||||
return nt::CreateTopicListenerPoller(inst);
|
||||
}
|
||||
|
||||
void NT_DestroyTopicListenerPoller(NT_TopicListenerPoller poller) {
|
||||
nt::DestroyTopicListenerPoller(poller);
|
||||
}
|
||||
|
||||
NT_TopicListener NT_AddPolledTopicListener(NT_TopicListenerPoller poller,
|
||||
const char* prefix,
|
||||
size_t prefix_len,
|
||||
unsigned int mask) {
|
||||
NT_Listener NT_AddPolledListenerSingle(NT_ListenerPoller poller,
|
||||
const char* prefix, size_t prefix_len,
|
||||
unsigned int mask) {
|
||||
std::string_view p{prefix, prefix_len};
|
||||
return nt::AddPolledTopicListener(poller, {{p}}, mask);
|
||||
return nt::AddPolledListener(poller, {{p}}, mask);
|
||||
}
|
||||
|
||||
NT_TopicListener NT_AddPolledTopicListenerMultiple(
|
||||
NT_TopicListenerPoller poller, const NT_String* prefixes,
|
||||
size_t prefixes_len, unsigned int mask) {
|
||||
NT_Listener NT_AddPolledListenerMultiple(NT_ListenerPoller poller,
|
||||
const NT_String* prefixes,
|
||||
size_t prefixes_len,
|
||||
unsigned int mask) {
|
||||
wpi::SmallVector<std::string_view, 8> p;
|
||||
p.reserve(prefixes_len);
|
||||
for (size_t i = 0; i < prefixes_len; ++i) {
|
||||
p.emplace_back(prefixes[i].str, prefixes[i].len);
|
||||
}
|
||||
return nt::AddPolledTopicListener(poller, p, mask);
|
||||
return nt::AddPolledListener(poller, p, mask);
|
||||
}
|
||||
|
||||
NT_TopicListener NT_AddPolledTopicListenerSingle(NT_TopicListenerPoller poller,
|
||||
NT_Topic topic,
|
||||
unsigned int mask) {
|
||||
return nt::AddPolledTopicListener(poller, topic, mask);
|
||||
}
|
||||
|
||||
struct NT_TopicNotification* NT_ReadTopicListenerQueue(
|
||||
NT_TopicListenerPoller poller, size_t* len) {
|
||||
auto arr_cpp = nt::ReadTopicListenerQueue(poller);
|
||||
return ConvertToC<NT_TopicNotification>(arr_cpp, len);
|
||||
}
|
||||
|
||||
void NT_RemoveTopicListener(NT_TopicListener topic_listener) {
|
||||
nt::RemoveTopicListener(topic_listener);
|
||||
}
|
||||
|
||||
NT_ValueListener NT_AddValueListener(NT_Handle subentry, unsigned int mask,
|
||||
void* data,
|
||||
NT_ValueListenerCallback callback) {
|
||||
return nt::AddValueListener(subentry, mask, [=](auto& event) {
|
||||
NT_ValueNotification event_c;
|
||||
ConvertToC(event, &event_c);
|
||||
callback(data, &event_c);
|
||||
DisposeValueNotification(&event_c);
|
||||
});
|
||||
}
|
||||
|
||||
NT_ValueListenerPoller NT_CreateValueListenerPoller(NT_Inst inst) {
|
||||
return nt::CreateValueListenerPoller(inst);
|
||||
}
|
||||
|
||||
void NT_DestroyValueListenerPoller(NT_ValueListenerPoller poller) {
|
||||
nt::DestroyValueListenerPoller(poller);
|
||||
}
|
||||
|
||||
NT_ValueListener NT_AddPolledValueListener(NT_ValueListenerPoller poller,
|
||||
NT_Handle subentry,
|
||||
unsigned int mask) {
|
||||
return nt::AddPolledValueListener(poller, subentry, mask);
|
||||
}
|
||||
|
||||
struct NT_ValueNotification* NT_ReadValueListenerQueue(
|
||||
NT_ValueListenerPoller poller, size_t* len) {
|
||||
auto arr_cpp = nt::ReadValueListenerQueue(poller);
|
||||
return ConvertToC<NT_ValueNotification>(arr_cpp, len);
|
||||
}
|
||||
|
||||
void NT_RemoveValueListener(NT_ValueListener value_listener) {
|
||||
nt::RemoveValueListener(value_listener);
|
||||
}
|
||||
|
||||
NT_ConnectionListener NT_AddConnectionListener(
|
||||
NT_Inst inst, void* data, NT_ConnectionListenerCallback callback,
|
||||
NT_Bool immediate_notify) {
|
||||
return nt::AddConnectionListener(
|
||||
inst,
|
||||
[=](const ConnectionNotification& event) {
|
||||
NT_ConnectionNotification event_c;
|
||||
ConvertToC(event, &event_c);
|
||||
callback(data, &event_c);
|
||||
DisposeConnectionNotification(&event_c);
|
||||
},
|
||||
immediate_notify != 0);
|
||||
}
|
||||
|
||||
NT_ConnectionListenerPoller NT_CreateConnectionListenerPoller(NT_Inst inst) {
|
||||
return nt::CreateConnectionListenerPoller(inst);
|
||||
}
|
||||
|
||||
void NT_DestroyConnectionListenerPoller(NT_ConnectionListenerPoller poller) {
|
||||
nt::DestroyConnectionListenerPoller(poller);
|
||||
}
|
||||
|
||||
NT_ConnectionListener NT_AddPolledConnectionListener(
|
||||
NT_ConnectionListenerPoller poller, NT_Bool immediate_notify) {
|
||||
return nt::AddPolledConnectionListener(poller, immediate_notify);
|
||||
}
|
||||
|
||||
struct NT_ConnectionNotification* NT_ReadConnectionListenerQueue(
|
||||
NT_ConnectionListenerPoller poller, size_t* len) {
|
||||
auto arr_cpp = nt::ReadConnectionListenerQueue(poller);
|
||||
return ConvertToC<NT_ConnectionNotification>(arr_cpp, len);
|
||||
}
|
||||
|
||||
void NT_RemoveConnectionListener(NT_ConnectionListener conn_listener) {
|
||||
nt::RemoveConnectionListener(conn_listener);
|
||||
NT_Listener NT_AddPolledListener(NT_ListenerPoller poller, NT_Topic topic,
|
||||
unsigned int mask) {
|
||||
return nt::AddPolledListener(poller, topic, mask);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -631,41 +571,22 @@ void NT_SetNow(uint64_t timestamp) {
|
||||
nt::SetNow(timestamp);
|
||||
}
|
||||
|
||||
NT_Logger NT_AddLogger(NT_Inst inst, void* data, NT_LogFunc func,
|
||||
unsigned int min_level, unsigned int max_level) {
|
||||
return nt::AddLogger(
|
||||
inst,
|
||||
[=](const LogMessage& msg) {
|
||||
NT_LogMessage msg_c;
|
||||
ConvertToC(msg, &msg_c);
|
||||
func(data, &msg_c);
|
||||
NT_DisposeLogMessage(&msg_c);
|
||||
},
|
||||
min_level, max_level);
|
||||
NT_Listener NT_AddLogger(NT_Inst inst, unsigned int min_level,
|
||||
unsigned int max_level, void* data,
|
||||
NT_ListenerCallback func) {
|
||||
return nt::AddLogger(inst, min_level, max_level, [=](auto& event) {
|
||||
NT_Event event_c;
|
||||
ConvertToC(event, &event_c);
|
||||
func(data, &event_c);
|
||||
NT_DisposeEvent(&event_c);
|
||||
});
|
||||
}
|
||||
|
||||
NT_LoggerPoller NT_CreateLoggerPoller(NT_Inst inst) {
|
||||
return nt::CreateLoggerPoller(inst);
|
||||
}
|
||||
|
||||
void NT_DestroyLoggerPoller(NT_LoggerPoller poller) {
|
||||
nt::DestroyLoggerPoller(poller);
|
||||
}
|
||||
|
||||
NT_Logger NT_AddPolledLogger(NT_LoggerPoller poller, unsigned int min_level,
|
||||
unsigned int max_level) {
|
||||
NT_Listener NT_AddPolledLogger(NT_ListenerPoller poller, unsigned int min_level,
|
||||
unsigned int max_level) {
|
||||
return nt::AddPolledLogger(poller, min_level, max_level);
|
||||
}
|
||||
|
||||
struct NT_LogMessage* NT_ReadLoggerQueue(NT_LoggerPoller poller, size_t* len) {
|
||||
auto arr_cpp = nt::ReadLoggerQueue(poller);
|
||||
return ConvertToC<NT_LogMessage>(arr_cpp, len);
|
||||
}
|
||||
|
||||
void NT_RemoveLogger(NT_Logger logger) {
|
||||
nt::RemoveLogger(logger);
|
||||
}
|
||||
|
||||
void NT_DisposeValue(NT_Value* value) {
|
||||
switch (value->type) {
|
||||
case NT_UNASSIGNED:
|
||||
@@ -749,50 +670,15 @@ void NT_DisposeTopicInfo(NT_TopicInfo* info) {
|
||||
DisposeTopicInfo(info);
|
||||
}
|
||||
|
||||
void NT_DisposeTopicNotificationArray(NT_TopicNotification* arr, size_t count) {
|
||||
void NT_DisposeEventArray(NT_Event* arr, size_t count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
DisposeTopicNotification(&arr[i]);
|
||||
DisposeEvent(&arr[i]);
|
||||
}
|
||||
std::free(arr);
|
||||
}
|
||||
|
||||
void NT_DisposeTopicNotification(NT_TopicNotification* info) {
|
||||
DisposeTopicNotification(info);
|
||||
}
|
||||
|
||||
void NT_DisposeValueNotificationArray(NT_ValueNotification* arr, size_t count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
DisposeValueNotification(&arr[i]);
|
||||
}
|
||||
std::free(arr);
|
||||
}
|
||||
|
||||
void NT_DisposeValueNotification(NT_ValueNotification* info) {
|
||||
DisposeValueNotification(info);
|
||||
}
|
||||
|
||||
void NT_DisposeConnectionNotificationArray(NT_ConnectionNotification* arr,
|
||||
size_t count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
DisposeConnectionNotification(&arr[i]);
|
||||
}
|
||||
std::free(arr);
|
||||
}
|
||||
|
||||
void NT_DisposeConnectionNotification(NT_ConnectionNotification* info) {
|
||||
DisposeConnectionNotification(info);
|
||||
}
|
||||
|
||||
void NT_DisposeLogMessageArray(NT_LogMessage* arr, size_t count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
NT_DisposeLogMessage(&arr[i]);
|
||||
}
|
||||
std::free(arr);
|
||||
}
|
||||
|
||||
void NT_DisposeLogMessage(NT_LogMessage* info) {
|
||||
std::free(info->filename);
|
||||
std::free(info->message);
|
||||
void NT_DisposeEvent(NT_Event* event) {
|
||||
DisposeEvent(event);
|
||||
}
|
||||
|
||||
/* Interop Utility Functions */
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <wpi/json.h>
|
||||
#include <wpi/timestamp.h>
|
||||
|
||||
@@ -17,6 +18,7 @@
|
||||
#include "Log.h"
|
||||
#include "Types_internal.h"
|
||||
#include "ntcore.h"
|
||||
#include "ntcore_c.h"
|
||||
|
||||
static std::atomic_bool gNowSet{false};
|
||||
static std::atomic<int64_t> gNowTime;
|
||||
@@ -54,7 +56,7 @@ void DestroyInstance(NT_Inst inst) {
|
||||
NT_Inst GetInstanceFromHandle(NT_Handle handle) {
|
||||
Handle h{handle};
|
||||
auto type = h.GetType();
|
||||
if (type >= Handle::kConnectionListener && type < Handle::kTypeMax) {
|
||||
if (type >= Handle::kListener && type < Handle::kTypeMax) {
|
||||
return Handle(h.GetInst(), 0, Handle::kInstance);
|
||||
}
|
||||
|
||||
@@ -386,177 +388,123 @@ void UnsubscribeMultiple(NT_MultiSubscriber sub) {
|
||||
* Callback Creation Functions
|
||||
*/
|
||||
|
||||
NT_TopicListener AddTopicListener(
|
||||
NT_Inst inst, std::span<const std::string_view> prefixes, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> callback) {
|
||||
static void CleanupListeners(
|
||||
InstanceImpl& ii,
|
||||
std::span<const std::pair<NT_Listener, unsigned int>> listeners) {
|
||||
bool updateMinLevel = false;
|
||||
for (auto&& [listener, mask] : listeners) {
|
||||
// connection doesn't need removal notification
|
||||
if ((mask & (NT_EVENT_TOPIC | NT_EVENT_VALUE_ALL)) != 0) {
|
||||
ii.localStorage.RemoveListener(listener, mask);
|
||||
}
|
||||
if ((mask & NT_EVENT_LOGMESSAGE) != 0) {
|
||||
ii.logger_impl.RemoveListener(listener);
|
||||
updateMinLevel = true;
|
||||
}
|
||||
}
|
||||
if (updateMinLevel) {
|
||||
ii.logger.set_min_level(ii.logger_impl.GetMinLevel());
|
||||
}
|
||||
}
|
||||
|
||||
static void DoAddListener(InstanceImpl& ii, NT_Listener listener,
|
||||
NT_Handle handle, unsigned int mask) {
|
||||
if (Handle{handle}.IsType(Handle::kInstance)) {
|
||||
if ((mask & NT_EVENT_CONNECTION) != 0) {
|
||||
ii.connectionList.AddListener(listener, mask);
|
||||
}
|
||||
if ((mask & NT_EVENT_LOGMESSAGE) != 0) {
|
||||
ii.logger_impl.AddListener(listener, NT_LOG_INFO, UINT_MAX);
|
||||
}
|
||||
} else if ((mask & (NT_EVENT_TOPIC | NT_EVENT_VALUE_ALL)) != 0) {
|
||||
ii.localStorage.AddListener(listener, handle, mask);
|
||||
}
|
||||
}
|
||||
|
||||
NT_ListenerPoller CreateListenerPoller(NT_Inst inst) {
|
||||
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
|
||||
return ii->localStorage.AddTopicListener(prefixes, mask,
|
||||
std::move(callback));
|
||||
return ii->listenerStorage.CreateListenerPoller();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
NT_TopicListener AddTopicListener(
|
||||
NT_Handle handle, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> callback) {
|
||||
if (auto ii = InstanceImpl::GetTyped(handle, Handle::kTopic)) {
|
||||
return ii->localStorage.AddTopicListener(handle, mask, std::move(callback));
|
||||
void DestroyListenerPoller(NT_ListenerPoller poller) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kListenerPoller)) {
|
||||
CleanupListeners(*ii, ii->listenerStorage.DestroyListenerPoller(poller));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Event> ReadListenerQueue(NT_ListenerPoller poller) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kListenerPoller)) {
|
||||
return ii->listenerStorage.ReadListenerQueue(poller);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
NT_TopicListenerPoller CreateTopicListenerPoller(NT_Inst inst) {
|
||||
void RemoveListener(NT_Listener listener) {
|
||||
if (auto ii = InstanceImpl::GetTyped(listener, Handle::kListener)) {
|
||||
CleanupListeners(*ii, ii->listenerStorage.RemoveListener(listener));
|
||||
}
|
||||
}
|
||||
|
||||
bool WaitForListenerQueue(NT_Handle handle, double timeout) {
|
||||
if (auto ii = InstanceImpl::GetHandle(handle)) {
|
||||
return ii->listenerStorage.WaitForListenerQueue(timeout);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
NT_Listener AddListener(NT_Inst inst,
|
||||
std::span<const std::string_view> prefixes,
|
||||
unsigned int mask, ListenerCallback callback) {
|
||||
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
|
||||
return ii->localStorage.CreateTopicListenerPoller();
|
||||
if ((mask & (NT_EVENT_TOPIC | NT_EVENT_VALUE_ALL)) != 0) {
|
||||
auto listener = ii->listenerStorage.AddListener(std::move(callback));
|
||||
ii->localStorage.AddListener(listener, prefixes, mask);
|
||||
return listener;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
NT_Listener AddListener(NT_Handle handle, unsigned int mask,
|
||||
ListenerCallback callback) {
|
||||
if (auto ii = InstanceImpl::GetHandle(handle)) {
|
||||
auto listener = ii->listenerStorage.AddListener(std::move(callback));
|
||||
DoAddListener(*ii, listener, handle, mask);
|
||||
return listener;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyTopicListenerPoller(NT_TopicListenerPoller poller) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kTopicListenerPoller)) {
|
||||
ii->localStorage.DestroyTopicListenerPoller(poller);
|
||||
NT_Listener AddPolledListener(NT_ListenerPoller poller,
|
||||
std::span<const std::string_view> prefixes,
|
||||
unsigned int mask) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kListenerPoller)) {
|
||||
if ((mask & (NT_EVENT_TOPIC | NT_EVENT_VALUE_ALL)) != 0) {
|
||||
auto listener = ii->listenerStorage.AddListener(poller);
|
||||
ii->localStorage.AddListener(listener, prefixes, mask);
|
||||
return listener;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
NT_TopicListener AddPolledTopicListener(
|
||||
NT_TopicListenerPoller poller, std::span<const std::string_view> prefixes,
|
||||
unsigned int mask) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kTopicListenerPoller)) {
|
||||
return ii->localStorage.AddPolledTopicListener(poller, prefixes, mask);
|
||||
NT_Listener AddPolledListener(NT_ListenerPoller poller, NT_Handle handle,
|
||||
unsigned int mask) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kListenerPoller)) {
|
||||
auto listener = ii->listenerStorage.AddListener(poller);
|
||||
DoAddListener(*ii, listener, handle, mask);
|
||||
return listener;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
NT_TopicListener AddPolledTopicListener(NT_TopicListenerPoller poller,
|
||||
NT_Handle handle, unsigned int mask) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kTopicListenerPoller)) {
|
||||
return ii->localStorage.AddPolledTopicListener(poller, handle, mask);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<TopicNotification> ReadTopicListenerQueue(
|
||||
NT_TopicListenerPoller poller) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kTopicListenerPoller)) {
|
||||
return ii->localStorage.ReadTopicListenerQueue(poller);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveTopicListener(NT_TopicListener listener) {
|
||||
if (auto ii = InstanceImpl::GetTyped(listener, Handle::kTopicListener)) {
|
||||
return ii->localStorage.RemoveTopicListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
NT_ValueListener AddValueListener(
|
||||
NT_Handle subentry, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> callback) {
|
||||
if (auto ii = InstanceImpl::GetHandle(subentry)) {
|
||||
return ii->localStorage.AddValueListener(subentry, mask,
|
||||
std::move(callback));
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
NT_ValueListenerPoller CreateValueListenerPoller(NT_Inst inst) {
|
||||
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
|
||||
return ii->localStorage.CreateValueListenerPoller();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyValueListenerPoller(NT_ValueListenerPoller poller) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kValueListenerPoller)) {
|
||||
ii->localStorage.DestroyValueListenerPoller(poller);
|
||||
}
|
||||
}
|
||||
|
||||
NT_ValueListener AddPolledValueListener(NT_ValueListenerPoller poller,
|
||||
NT_Handle subentry, unsigned int mask) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kValueListenerPoller)) {
|
||||
return ii->localStorage.AddPolledValueListener(poller, subentry, mask);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ValueNotification> ReadValueListenerQueue(
|
||||
NT_ValueListenerPoller poller) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kValueListenerPoller)) {
|
||||
return ii->localStorage.ReadValueListenerQueue(poller);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveValueListener(NT_ValueListener listener) {
|
||||
if (auto ii = InstanceImpl::GetTyped(listener, Handle::kValueListener)) {
|
||||
return ii->localStorage.RemoveValueListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
NT_ConnectionListener AddConnectionListener(
|
||||
NT_Inst inst,
|
||||
std::function<void(const ConnectionNotification& event)> callback,
|
||||
bool immediate_notify) {
|
||||
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
|
||||
return ii->connectionList.AddListener(std::move(callback),
|
||||
immediate_notify);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
NT_ConnectionListenerPoller CreateConnectionListenerPoller(NT_Inst inst) {
|
||||
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
|
||||
return ii->connectionList.CreateListenerPoller();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyConnectionListenerPoller(NT_ConnectionListenerPoller poller) {
|
||||
if (auto ii =
|
||||
InstanceImpl::GetTyped(poller, Handle::kConnectionListenerPoller)) {
|
||||
return ii->connectionList.DestroyListenerPoller(poller);
|
||||
}
|
||||
}
|
||||
|
||||
NT_ConnectionListener AddPolledConnectionListener(
|
||||
NT_ConnectionListenerPoller poller, bool immediate_notify) {
|
||||
if (auto ii =
|
||||
InstanceImpl::GetTyped(poller, Handle::kConnectionListenerPoller)) {
|
||||
return ii->connectionList.AddPolledListener(poller, immediate_notify);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ConnectionNotification> ReadConnectionListenerQueue(
|
||||
NT_ConnectionListenerPoller poller) {
|
||||
if (auto ii =
|
||||
InstanceImpl::GetTyped(poller, Handle::kConnectionListenerPoller)) {
|
||||
return ii->connectionList.ReadListenerQueue(poller);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveConnectionListener(NT_ConnectionListener listener) {
|
||||
if (auto ii = InstanceImpl::GetTyped(listener, Handle::kConnectionListener)) {
|
||||
return ii->connectionList.RemoveListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
int64_t Now() {
|
||||
if (gNowSet) {
|
||||
return gNowTime;
|
||||
@@ -773,58 +721,32 @@ bool IsConnected(NT_Inst inst) {
|
||||
}
|
||||
}
|
||||
|
||||
NT_Logger AddLogger(NT_Inst inst,
|
||||
std::function<void(const LogMessage& msg)> func,
|
||||
unsigned int minLevel, unsigned int maxLevel) {
|
||||
NT_Listener AddLogger(NT_Inst inst, unsigned int minLevel,
|
||||
unsigned int maxLevel, ListenerCallback func) {
|
||||
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
|
||||
if (minLevel < ii->logger.min_level()) {
|
||||
ii->logger.set_min_level(minLevel);
|
||||
}
|
||||
return ii->logger_impl.Add(std::move(func), minLevel, maxLevel);
|
||||
auto listener = ii->listenerStorage.AddListener(std::move(func));
|
||||
ii->logger_impl.AddListener(listener, minLevel, maxLevel);
|
||||
return listener;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
NT_LoggerPoller CreateLoggerPoller(NT_Inst inst) {
|
||||
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
|
||||
return ii->logger_impl.CreatePoller();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyLoggerPoller(NT_LoggerPoller poller) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kLoggerPoller)) {
|
||||
ii->logger_impl.DestroyPoller(poller);
|
||||
}
|
||||
}
|
||||
|
||||
NT_Logger AddPolledLogger(NT_LoggerPoller poller, unsigned int min_level,
|
||||
unsigned int max_level) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kLoggerPoller)) {
|
||||
if (min_level < ii->logger.min_level()) {
|
||||
ii->logger.set_min_level(min_level);
|
||||
NT_Listener AddPolledLogger(NT_ListenerPoller poller, unsigned int minLevel,
|
||||
unsigned int maxLevel) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kListenerPoller)) {
|
||||
if (minLevel < ii->logger.min_level()) {
|
||||
ii->logger.set_min_level(minLevel);
|
||||
}
|
||||
return ii->logger_impl.AddPolled(poller, min_level, max_level);
|
||||
auto listener = ii->listenerStorage.AddListener(poller);
|
||||
ii->logger_impl.AddListener(listener, minLevel, maxLevel);
|
||||
return listener;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<LogMessage> ReadLoggerQueue(NT_LoggerPoller poller) {
|
||||
if (auto ii = InstanceImpl::GetTyped(poller, Handle::kLoggerPoller)) {
|
||||
return ii->logger_impl.ReadQueue(poller);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveLogger(NT_Logger logger) {
|
||||
if (auto ii = InstanceImpl::GetTyped(logger, Handle::kLogger)) {
|
||||
ii->logger_impl.Remove(logger);
|
||||
ii->logger.set_min_level(ii->logger_impl.GetMinLevel());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class NetworkTableInstance;
|
||||
|
||||
/**
|
||||
* Connection listener. This calls back to a callback function when a connection
|
||||
* change occurs. The callback function is called asynchronously on a separate
|
||||
* thread, so it's important to use synchronization or atomics when accessing
|
||||
* any shared state from the callback function.
|
||||
*/
|
||||
class ConnectionListener final {
|
||||
public:
|
||||
ConnectionListener() = default;
|
||||
|
||||
/**
|
||||
* Create a listener for connection changes.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param immediateNotify if notification should be immediately created for
|
||||
* existing connections
|
||||
* @param listener Listener function
|
||||
*/
|
||||
ConnectionListener(
|
||||
NetworkTableInstance inst, bool immediateNotify,
|
||||
std::function<void(const ConnectionNotification&)> listener);
|
||||
|
||||
ConnectionListener(const ConnectionListener&) = delete;
|
||||
ConnectionListener& operator=(const ConnectionListener&) = delete;
|
||||
ConnectionListener(ConnectionListener&& rhs);
|
||||
ConnectionListener& operator=(ConnectionListener&& rhs);
|
||||
~ConnectionListener();
|
||||
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
NT_ConnectionListener GetHandle() const { return m_handle; }
|
||||
|
||||
private:
|
||||
NT_ConnectionListener m_handle{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* A connection listener. This queues connection notifications. Code using
|
||||
* the listener must periodically call readQueue() to read the notifications.
|
||||
*/
|
||||
class ConnectionListenerPoller final {
|
||||
public:
|
||||
ConnectionListenerPoller() = default;
|
||||
|
||||
/**
|
||||
* Construct a connection listener poller.
|
||||
*
|
||||
* @param inst Instance
|
||||
*/
|
||||
explicit ConnectionListenerPoller(NetworkTableInstance inst);
|
||||
|
||||
ConnectionListenerPoller(const ConnectionListenerPoller&) = delete;
|
||||
ConnectionListenerPoller& operator=(const ConnectionListenerPoller&) = delete;
|
||||
ConnectionListenerPoller(ConnectionListenerPoller&& rhs);
|
||||
ConnectionListenerPoller& operator=(ConnectionListenerPoller&& rhs);
|
||||
~ConnectionListenerPoller();
|
||||
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
NT_ConnectionListenerPoller GetHandle() const { return m_handle; }
|
||||
|
||||
/**
|
||||
* Create a connection listener.
|
||||
*
|
||||
* @param immediateNotify if notification should be immediately created for
|
||||
* existing connections
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ConnectionListener Add(bool immediateNotify);
|
||||
|
||||
/**
|
||||
* Remove a connection listener.
|
||||
*
|
||||
* @param listener Listener handle
|
||||
*/
|
||||
void Remove(NT_ConnectionListener listener);
|
||||
|
||||
/**
|
||||
* Read connection notifications.
|
||||
*
|
||||
* @return Connection notifications since the previous call to readQueue()
|
||||
*/
|
||||
std::vector<ConnectionNotification> ReadQueue();
|
||||
|
||||
private:
|
||||
NT_ConnectionListenerPoller m_handle{0};
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#include "ConnectionListener.inc"
|
||||
@@ -1,75 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "networktables/ConnectionListener.h"
|
||||
#include "networktables/NetworkTableInstance.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
inline ConnectionListener::ConnectionListener(
|
||||
NetworkTableInstance inst, bool immediateNotify,
|
||||
std::function<void(const ConnectionNotification&)> listener)
|
||||
: m_handle{
|
||||
AddConnectionListener(inst.GetHandle(), listener, immediateNotify)} {}
|
||||
|
||||
inline ConnectionListener::ConnectionListener(ConnectionListener&& rhs)
|
||||
: m_handle(rhs.m_handle) {
|
||||
rhs.m_handle = 0;
|
||||
}
|
||||
|
||||
inline ConnectionListener& ConnectionListener::operator=(
|
||||
ConnectionListener&& rhs) {
|
||||
std::swap(m_handle, rhs.m_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ConnectionListener::~ConnectionListener() {
|
||||
if (m_handle != 0) {
|
||||
nt::RemoveConnectionListener(m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
inline ConnectionListenerPoller::ConnectionListenerPoller(
|
||||
NetworkTableInstance inst)
|
||||
: m_handle(nt::CreateConnectionListenerPoller(inst.GetHandle())) {}
|
||||
|
||||
inline ConnectionListenerPoller::ConnectionListenerPoller(
|
||||
ConnectionListenerPoller&& rhs)
|
||||
: m_handle(rhs.m_handle) {
|
||||
rhs.m_handle = 0;
|
||||
}
|
||||
|
||||
inline ConnectionListenerPoller& ConnectionListenerPoller::operator=(
|
||||
ConnectionListenerPoller&& rhs) {
|
||||
std::swap(m_handle, rhs.m_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ConnectionListenerPoller::~ConnectionListenerPoller() {
|
||||
if (m_handle != 0) {
|
||||
nt::DestroyConnectionListenerPoller(m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
inline NT_ConnectionListener ConnectionListenerPoller::Add(
|
||||
bool immediateNotify) {
|
||||
return nt::AddPolledConnectionListener(m_handle, immediateNotify);
|
||||
}
|
||||
|
||||
inline void ConnectionListenerPoller::Remove(NT_ConnectionListener listener) {
|
||||
nt::RemoveConnectionListener(listener);
|
||||
}
|
||||
|
||||
inline std::vector<ConnectionNotification>
|
||||
ConnectionListenerPoller::ReadQueue() {
|
||||
return nt::ReadConnectionListenerQueue(m_handle);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
@@ -27,9 +27,11 @@ class FloatArrayTopic;
|
||||
class FloatTopic;
|
||||
class IntegerArrayTopic;
|
||||
class IntegerTopic;
|
||||
class MultiSubscriber;
|
||||
class RawTopic;
|
||||
class StringArrayTopic;
|
||||
class StringTopic;
|
||||
class Subscriber;
|
||||
class Topic;
|
||||
|
||||
/**
|
||||
@@ -355,26 +357,115 @@ class NetworkTableInstance final {
|
||||
|
||||
/**
|
||||
* @{
|
||||
* @name Connection Listener Functions
|
||||
* @name Listener Functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add a connection listener.
|
||||
* Remove a listener.
|
||||
*
|
||||
* @param listener Listener handle to remove
|
||||
*/
|
||||
static void RemoveListener(NT_Listener listener);
|
||||
|
||||
/**
|
||||
* Wait for the listener queue to be empty. This is primarily
|
||||
* useful for deterministic testing. This blocks until either the
|
||||
* listener queue is empty (e.g. there are no more events that need to be
|
||||
* passed along to callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or
|
||||
* a negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForListenerQueue(double timeout);
|
||||
|
||||
/**
|
||||
* Add a connection listener. The callback function is called asynchronously
|
||||
* on a separate thread, so it's important to use synchronization or atomics
|
||||
* when accessing any shared state from the callback function.
|
||||
*
|
||||
* @param callback listener to add
|
||||
* @param immediate_notify notify listener of all existing connections
|
||||
* @param callback listener to add
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ConnectionListener AddConnectionListener(
|
||||
std::function<void(const ConnectionNotification& event)> callback,
|
||||
bool immediate_notify) const;
|
||||
NT_Listener AddConnectionListener(bool immediate_notify,
|
||||
ListenerCallback callback) const;
|
||||
|
||||
/**
|
||||
* Remove a connection listener.
|
||||
* Add a listener for changes on a particular topic. The callback
|
||||
* function is called asynchronously on a separate thread, so it's important
|
||||
* to use synchronization or atomics when accessing any shared state from the
|
||||
* callback function.
|
||||
*
|
||||
* @param conn_listener Listener handle to remove
|
||||
* This creates a corresponding internal subscriber with the lifetime of the
|
||||
* listener.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param eventMask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
static void RemoveConnectionListener(NT_ConnectionListener conn_listener);
|
||||
NT_Listener AddListener(Topic topic, unsigned int eventMask,
|
||||
ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Add a listener for changes on a subscriber. The callback
|
||||
* function is called asynchronously on a separate thread, so it's important
|
||||
* to use synchronization or atomics when accessing any shared state from the
|
||||
* callback function. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener AddListener(Subscriber& subscriber, unsigned int eventMask,
|
||||
ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Add a listener for changes on a subscriber. The callback
|
||||
* function is called asynchronously on a separate thread, so it's important
|
||||
* to use synchronization or atomics when accessing any shared state from the
|
||||
* callback function. This does NOT keep the subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param eventMask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener AddListener(MultiSubscriber& subscriber, int eventMask,
|
||||
ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Add a listener for changes on an entry. The callback function
|
||||
* is called asynchronously on a separate thread, so it's important to use
|
||||
* synchronization or atomics when accessing any shared state from the
|
||||
* callback function.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param eventMask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener AddListener(NetworkTableEntry& entry, int eventMask,
|
||||
ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Add a listener for changes to topics with names that start with any
|
||||
* of the given prefixes. The callback function is called asynchronously on a
|
||||
* separate thread, so it's important to use synchronization or atomics when
|
||||
* accessing any shared state from the callback function.
|
||||
*
|
||||
* This creates a corresponding internal subscriber with the lifetime of the
|
||||
* listener.
|
||||
*
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param eventMask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener AddListener(std::span<const std::string_view> prefixes,
|
||||
int eventMask, ListenerCallback listener);
|
||||
|
||||
/** @} */
|
||||
|
||||
@@ -583,20 +674,13 @@ class NetworkTableInstance final {
|
||||
* log messages with level greater than or equal to minLevel and less than or
|
||||
* equal to maxLevel; messages outside this range will be silently ignored.
|
||||
*
|
||||
* @param func log callback function
|
||||
* @param minLevel minimum log level
|
||||
* @param maxLevel maximum log level
|
||||
* @return Logger handle
|
||||
* @param func callback function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Logger AddLogger(std::function<void(const LogMessage& msg)> func,
|
||||
unsigned int minLevel, unsigned int maxLevel);
|
||||
|
||||
/**
|
||||
* Remove a logger.
|
||||
*
|
||||
* @param logger Logger handle to remove
|
||||
*/
|
||||
static void RemoveLogger(NT_Logger logger);
|
||||
NT_Listener AddLogger(unsigned int minLevel, unsigned int maxLevel,
|
||||
ListenerCallback func);
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "networktables/NetworkTableInstance.h"
|
||||
#include "networktables/Topic.h"
|
||||
#include "ntcore_c.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
@@ -82,9 +83,42 @@ inline NetworkTableEntry NetworkTableInstance::GetEntry(std::string_view name) {
|
||||
return NetworkTableEntry{::nt::GetEntry(m_handle, name)};
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::RemoveConnectionListener(
|
||||
NT_ConnectionListener conn_listener) {
|
||||
::nt::RemoveConnectionListener(conn_listener);
|
||||
inline bool NetworkTableInstance::WaitForListenerQueue(double timeout) {
|
||||
return ::nt::WaitForListenerQueue(m_handle, timeout);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::RemoveListener(NT_Listener listener) {
|
||||
::nt::RemoveListener(listener);
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableInstance::AddConnectionListener(
|
||||
bool immediate_notify, ListenerCallback callback) const {
|
||||
return ::nt::AddListener(
|
||||
m_handle,
|
||||
NT_EVENT_CONNECTION | (immediate_notify ? NT_EVENT_IMMEDIATE : 0),
|
||||
std::move(callback));
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableInstance::AddListener(
|
||||
Topic topic, unsigned int eventMask, ListenerCallback listener) {
|
||||
return ::nt::AddListener(topic.GetHandle(), eventMask, std::move(listener));
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableInstance::AddListener(
|
||||
Subscriber& subscriber, unsigned int eventMask, ListenerCallback listener) {
|
||||
return ::nt::AddListener(subscriber.GetHandle(), eventMask,
|
||||
std::move(listener));
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableInstance::AddListener(
|
||||
NetworkTableEntry& entry, int eventMask, ListenerCallback listener) {
|
||||
return ::nt::AddListener(entry.GetHandle(), eventMask, std::move(listener));
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableInstance::AddListener(
|
||||
std::span<const std::string_view> prefixes, int eventMask,
|
||||
ListenerCallback listener) {
|
||||
return ::nt::AddListener(m_handle, prefixes, eventMask, std::move(listener));
|
||||
}
|
||||
|
||||
inline unsigned int NetworkTableInstance::GetNetworkMode() const {
|
||||
@@ -182,14 +216,10 @@ inline void NetworkTableInstance::StopConnectionDataLog(
|
||||
::nt::StopConnectionDataLog(logger);
|
||||
}
|
||||
|
||||
inline NT_Logger NetworkTableInstance::AddLogger(
|
||||
std::function<void(const LogMessage& msg)> func, unsigned int min_level,
|
||||
unsigned int max_level) {
|
||||
return ::nt::AddLogger(m_handle, func, min_level, max_level);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::RemoveLogger(NT_Logger logger) {
|
||||
::nt::RemoveLogger(logger);
|
||||
inline NT_Listener NetworkTableInstance::AddLogger(unsigned int min_level,
|
||||
unsigned int max_level,
|
||||
ListenerCallback func) {
|
||||
return ::nt::AddLogger(m_handle, min_level, max_level, std::move(func));
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class MultiSubscriber;
|
||||
class NetworkTableEntry;
|
||||
class NetworkTableInstance;
|
||||
class Subscriber;
|
||||
class Topic;
|
||||
|
||||
/**
|
||||
* Event listener. This calls back to a callback function when an event
|
||||
* matching the specified mask occurs. The callback function is called
|
||||
* asynchronously on a separate thread, so it's important to use synchronization
|
||||
* or atomics when accessing any shared state from the callback function.
|
||||
*/
|
||||
class NetworkTableListener final {
|
||||
public:
|
||||
NetworkTableListener() = default;
|
||||
|
||||
/**
|
||||
* Create a listener for changes to topics with names that start with any of
|
||||
* the given prefixes. This creates a corresponding internal subscriber with
|
||||
* the lifetime of the listener.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
static NetworkTableListener CreateListener(
|
||||
NetworkTableInstance inst, std::span<const std::string_view> prefixes,
|
||||
unsigned int mask, ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Create a listener for changes on a particular topic. This creates a
|
||||
* corresponding internal subscriber with the lifetime of the listener.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
static NetworkTableListener CreateListener(Topic topic, unsigned int mask,
|
||||
ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on a subscriber. This does NOT keep the
|
||||
* subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
static NetworkTableListener CreateListener(Subscriber& subscriber,
|
||||
unsigned int mask,
|
||||
ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on a subscriber. This does NOT keep the
|
||||
* subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
static NetworkTableListener CreateListener(MultiSubscriber& subscriber,
|
||||
unsigned int mask,
|
||||
ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @param listener Listener function
|
||||
* @return Listener
|
||||
*/
|
||||
static NetworkTableListener CreateListener(NetworkTableEntry& entry,
|
||||
unsigned int mask,
|
||||
ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Create a connection listener.
|
||||
*
|
||||
* @param inst instance
|
||||
* @param immediate_notify notify listener of all existing connections
|
||||
* @param listener listener function
|
||||
* @return Listener
|
||||
*/
|
||||
static NetworkTableListener CreateConnectionListener(
|
||||
NetworkTableInstance inst, bool immediate_notify,
|
||||
ListenerCallback listener);
|
||||
|
||||
/**
|
||||
* Create a listener for log messages. By default, log messages are sent to
|
||||
* stderr; this function sends log messages with the specified levels to the
|
||||
* provided callback function instead. The callback function will only be
|
||||
* called for log messages with level greater than or equal to minLevel and
|
||||
* less than or equal to maxLevel; messages outside this range will be
|
||||
* silently ignored.
|
||||
*
|
||||
* @param inst instance
|
||||
* @param minLevel minimum log level
|
||||
* @param maxLevel maximum log level
|
||||
* @param listener listener function
|
||||
* @return Listener
|
||||
*/
|
||||
static NetworkTableListener CreateLogger(NetworkTableInstance inst,
|
||||
unsigned int minLevel,
|
||||
unsigned int maxLevel,
|
||||
ListenerCallback listener);
|
||||
|
||||
NetworkTableListener(const NetworkTableListener&) = delete;
|
||||
NetworkTableListener& operator=(const NetworkTableListener&) = delete;
|
||||
NetworkTableListener(NetworkTableListener&& rhs);
|
||||
NetworkTableListener& operator=(NetworkTableListener&& rhs);
|
||||
~NetworkTableListener();
|
||||
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
NT_Listener GetHandle() const { return m_handle; }
|
||||
|
||||
/**
|
||||
* Wait for the listener queue to be empty. This is primarily useful for
|
||||
* deterministic testing. This blocks until either the listener queue is
|
||||
* empty (e.g. there are no more events that need to be passed along to
|
||||
* callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or
|
||||
* a negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForQueue(double timeout);
|
||||
|
||||
private:
|
||||
explicit NetworkTableListener(NT_Listener handle) : m_handle{handle} {}
|
||||
|
||||
NT_Listener m_handle{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* Event polled listener. This queues events matching the specified mask. Code
|
||||
* using the listener must periodically call ReadQueue() to read the
|
||||
* events.
|
||||
*/
|
||||
class NetworkTableListenerPoller final {
|
||||
public:
|
||||
NetworkTableListenerPoller() = default;
|
||||
|
||||
/**
|
||||
* Construct a listener poller.
|
||||
*
|
||||
* @param inst Instance
|
||||
*/
|
||||
explicit NetworkTableListenerPoller(NetworkTableInstance inst);
|
||||
|
||||
NetworkTableListenerPoller(const NetworkTableListenerPoller&) = delete;
|
||||
NetworkTableListenerPoller& operator=(const NetworkTableListenerPoller&) =
|
||||
delete;
|
||||
NetworkTableListenerPoller(NetworkTableListenerPoller&& rhs);
|
||||
NetworkTableListenerPoller& operator=(NetworkTableListenerPoller&& rhs);
|
||||
~NetworkTableListenerPoller();
|
||||
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
NT_ListenerPoller GetHandle() const { return m_handle; }
|
||||
|
||||
/**
|
||||
* Start listening to topic changes for topics with names that start with any
|
||||
* of the given prefixes. This creates a corresponding internal subscriber
|
||||
* with the lifetime of the listener.
|
||||
*
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener AddListener(std::span<const std::string_view> prefixes,
|
||||
unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to changes to a particular topic. This creates a
|
||||
* corresponding internal subscriber with the lifetime of the listener.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener AddListener(Topic topic, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on a subscriber. This does NOT keep the
|
||||
* subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener AddListener(Subscriber& subscriber, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on a subscriber. This does NOT keep the
|
||||
* subscriber active.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener AddListener(MultiSubscriber& subscriber, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param mask Bitmask of EventFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener AddListener(NetworkTableEntry& entry, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Add a connection listener. The callback function is called asynchronously
|
||||
* on a separate thread, so it's important to use synchronization or atomics
|
||||
* when accessing any shared state from the callback function.
|
||||
*
|
||||
* @param immediate_notify notify listener of all existing connections
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener AddConnectionListener(bool immediate_notify);
|
||||
|
||||
/**
|
||||
* Add logger callback function. By default, log messages are sent to stderr;
|
||||
* this function sends log messages with the specified levels to the provided
|
||||
* callback function instead. The callback function will only be called for
|
||||
* log messages with level greater than or equal to minLevel and less than or
|
||||
* equal to maxLevel; messages outside this range will be silently ignored.
|
||||
*
|
||||
* @param minLevel minimum log level
|
||||
* @param maxLevel maximum log level
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener AddLogger(unsigned int minLevel, unsigned int maxLevel);
|
||||
|
||||
/**
|
||||
* Remove a listener.
|
||||
*
|
||||
* @param listener Listener handle
|
||||
*/
|
||||
void RemoveListener(NT_Listener listener);
|
||||
|
||||
/**
|
||||
* Read events.
|
||||
*
|
||||
* @return Events since the previous call to ReadQueue()
|
||||
*/
|
||||
std::vector<Event> ReadQueue();
|
||||
|
||||
private:
|
||||
NT_ListenerPoller m_handle{0};
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#include "NetworkTableListener.inc"
|
||||
@@ -0,0 +1,160 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "networktables/MultiSubscriber.h"
|
||||
#include "networktables/NetworkTableEntry.h"
|
||||
#include "networktables/NetworkTableInstance.h"
|
||||
#include "networktables/NetworkTableListener.h"
|
||||
#include "networktables/Topic.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
inline NetworkTableListener NetworkTableListener::CreateListener(
|
||||
NetworkTableInstance inst, std::span<const std::string_view> prefixes,
|
||||
unsigned int mask, ListenerCallback listener) {
|
||||
return NetworkTableListener{
|
||||
::nt::AddListener(inst.GetHandle(), prefixes, mask, std::move(listener))};
|
||||
}
|
||||
|
||||
inline NetworkTableListener NetworkTableListener::CreateListener(
|
||||
Topic topic, unsigned int mask, ListenerCallback listener) {
|
||||
return NetworkTableListener{
|
||||
nt::AddListener(topic.GetHandle(), mask, std::move(listener))};
|
||||
}
|
||||
|
||||
inline NetworkTableListener NetworkTableListener::CreateListener(
|
||||
Subscriber& subscriber, unsigned int mask, ListenerCallback listener) {
|
||||
return NetworkTableListener{
|
||||
::nt::AddListener(subscriber.GetHandle(), mask, std::move(listener))};
|
||||
}
|
||||
|
||||
inline NetworkTableListener NetworkTableListener::CreateListener(
|
||||
MultiSubscriber& subscriber, unsigned int mask, ListenerCallback listener) {
|
||||
return NetworkTableListener{
|
||||
::nt::AddListener(subscriber.GetHandle(), mask, std::move(listener))};
|
||||
}
|
||||
|
||||
inline NetworkTableListener NetworkTableListener::CreateListener(
|
||||
NetworkTableEntry& entry, unsigned int mask, ListenerCallback listener) {
|
||||
return NetworkTableListener{
|
||||
::nt::AddListener(entry.GetHandle(), mask, std::move(listener))};
|
||||
}
|
||||
|
||||
inline NetworkTableListener NetworkTableListener::CreateConnectionListener(
|
||||
NetworkTableInstance inst, bool immediate_notify,
|
||||
ListenerCallback listener) {
|
||||
return NetworkTableListener{::nt::AddListener(
|
||||
inst.GetHandle(),
|
||||
NT_EVENT_CONNECTION | (immediate_notify ? NT_EVENT_IMMEDIATE : 0),
|
||||
std::move(listener))};
|
||||
}
|
||||
|
||||
inline NetworkTableListener NetworkTableListener::CreateLogger(
|
||||
NetworkTableInstance inst, unsigned int minLevel, unsigned int maxLevel,
|
||||
ListenerCallback listener) {
|
||||
return NetworkTableListener{::nt::AddLogger(inst.GetHandle(), minLevel,
|
||||
maxLevel, std::move(listener))};
|
||||
}
|
||||
|
||||
inline NetworkTableListener::NetworkTableListener(NetworkTableListener&& rhs)
|
||||
: m_handle(rhs.m_handle) {
|
||||
rhs.m_handle = 0;
|
||||
}
|
||||
|
||||
inline NetworkTableListener& NetworkTableListener::operator=(
|
||||
NetworkTableListener&& rhs) {
|
||||
std::swap(m_handle, rhs.m_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline NetworkTableListener::~NetworkTableListener() {
|
||||
if (m_handle != 0) {
|
||||
nt::RemoveListener(m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool NetworkTableListener::WaitForQueue(double timeout) {
|
||||
if (m_handle != 0) {
|
||||
return nt::WaitForListenerQueue(m_handle, timeout);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline NetworkTableListenerPoller::NetworkTableListenerPoller(
|
||||
NetworkTableInstance inst)
|
||||
: m_handle(nt::CreateListenerPoller(inst.GetHandle())) {}
|
||||
|
||||
inline NetworkTableListenerPoller::NetworkTableListenerPoller(
|
||||
NetworkTableListenerPoller&& rhs)
|
||||
: m_handle(rhs.m_handle) {
|
||||
rhs.m_handle = 0;
|
||||
}
|
||||
|
||||
inline NetworkTableListenerPoller& NetworkTableListenerPoller::operator=(
|
||||
NetworkTableListenerPoller&& rhs) {
|
||||
std::swap(m_handle, rhs.m_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline NetworkTableListenerPoller::~NetworkTableListenerPoller() {
|
||||
if (m_handle != 0) {
|
||||
nt::DestroyListenerPoller(m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableListenerPoller::AddListener(
|
||||
std::span<const std::string_view> prefixes, unsigned int mask) {
|
||||
return nt::AddPolledListener(m_handle, prefixes, mask);
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableListenerPoller::AddListener(Topic topic,
|
||||
unsigned int mask) {
|
||||
return ::nt::AddPolledListener(m_handle, topic.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableListenerPoller::AddListener(
|
||||
Subscriber& subscriber, unsigned int mask) {
|
||||
return ::nt::AddPolledListener(m_handle, subscriber.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableListenerPoller::AddListener(
|
||||
MultiSubscriber& subscriber, unsigned int mask) {
|
||||
return ::nt::AddPolledListener(m_handle, subscriber.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableListenerPoller::AddListener(
|
||||
NetworkTableEntry& entry, unsigned int mask) {
|
||||
return ::nt::AddPolledListener(m_handle, entry.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableListenerPoller::AddConnectionListener(
|
||||
bool immediate_notify) {
|
||||
return ::nt::AddPolledListener(
|
||||
m_handle, ::nt::GetInstanceFromHandle(m_handle),
|
||||
NT_EVENT_CONNECTION | (immediate_notify ? NT_EVENT_IMMEDIATE : 0));
|
||||
}
|
||||
|
||||
inline NT_Listener NetworkTableListenerPoller::AddLogger(
|
||||
unsigned int minLevel, unsigned int maxLevel) {
|
||||
return ::nt::AddPolledLogger(m_handle, minLevel, maxLevel);
|
||||
}
|
||||
|
||||
inline void NetworkTableListenerPoller::RemoveListener(NT_Listener listener) {
|
||||
::nt::RemoveListener(listener);
|
||||
}
|
||||
|
||||
inline std::vector<Event> NetworkTableListenerPoller::ReadQueue() {
|
||||
return ::nt::ReadListenerQueue(m_handle);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
@@ -1,247 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class MultiSubscriber;
|
||||
class NetworkTableEntry;
|
||||
class NetworkTableInstance;
|
||||
class Subscriber;
|
||||
class Topic;
|
||||
|
||||
/**
|
||||
* Flag values for use with topic listeners.
|
||||
*
|
||||
* The flags are a bitmask and must be OR'ed together to indicate the
|
||||
* combination of events desired to be received.
|
||||
*
|
||||
* The constants kPublish, kUnpublish, and kProperties represent different
|
||||
* events that can occur to entries.
|
||||
*
|
||||
* @ingroup ntcore_cpp_api
|
||||
*/
|
||||
struct TopicListenerFlags {
|
||||
TopicListenerFlags() = delete;
|
||||
|
||||
/**
|
||||
* Initial listener addition.
|
||||
* Set this flag to receive immediate notification of entries matching the
|
||||
* flag criteria (generally only useful when combined with kPublish).
|
||||
*/
|
||||
static constexpr unsigned int kImmediate = NT_TOPIC_NOTIFY_IMMEDIATE;
|
||||
|
||||
/**
|
||||
* Newly published topic.
|
||||
*
|
||||
* Set this flag to receive a notification when a topic is initially
|
||||
* published.
|
||||
*/
|
||||
static constexpr unsigned int kPublish = NT_TOPIC_NOTIFY_PUBLISH;
|
||||
|
||||
/**
|
||||
* Topic has no more publishers.
|
||||
*
|
||||
* Set this flag to receive a notification when a topic has no more
|
||||
* publishers.
|
||||
*/
|
||||
static constexpr unsigned int kUnpublish = NT_TOPIC_NOTIFY_UNPUBLISH;
|
||||
|
||||
/**
|
||||
* Topic's properties changed.
|
||||
*
|
||||
* Set this flag to receive a notification when an topic's properties change.
|
||||
*/
|
||||
static constexpr unsigned int kProperties = NT_TOPIC_NOTIFY_PROPERTIES;
|
||||
};
|
||||
|
||||
/**
|
||||
* Topic change listener. This calls back to a callback function when a topic
|
||||
* change matching the specified mask occurs. The callback function is called
|
||||
* asynchronously on a separate thread, so it's important to use synchronization
|
||||
* or atomics when accessing any shared state from the callback function.
|
||||
*/
|
||||
class TopicListener final {
|
||||
public:
|
||||
TopicListener() = default;
|
||||
|
||||
/**
|
||||
* Create a listener for changes to topics with names that start with any of
|
||||
* the given prefixes.
|
||||
*
|
||||
* @param inst Instance
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
TopicListener(NetworkTableInstance inst,
|
||||
std::span<const std::string_view> prefixes, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> listener);
|
||||
|
||||
/**
|
||||
* Create a listener for changes on a particular topic.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
TopicListener(Topic topic, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> listener);
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on a subscriber.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
TopicListener(const Subscriber& subscriber, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> listener);
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on a subscriber.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
TopicListener(const MultiSubscriber& subscriber, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> listener);
|
||||
|
||||
/**
|
||||
* Create a listener for topic changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
TopicListener(const NetworkTableEntry& entry, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> listener);
|
||||
|
||||
TopicListener(const TopicListener&) = delete;
|
||||
TopicListener& operator=(const TopicListener&) = delete;
|
||||
TopicListener(TopicListener&& rhs);
|
||||
TopicListener& operator=(TopicListener&& rhs);
|
||||
~TopicListener();
|
||||
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
NT_TopicListener GetHandle() const { return m_handle; }
|
||||
|
||||
private:
|
||||
NT_TopicListener m_handle{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* Topic change listener. This queues topic change events matching the specified
|
||||
* mask. Code using the listener must periodically call readQueue() to read the
|
||||
* events.
|
||||
*/
|
||||
class TopicListenerPoller final {
|
||||
public:
|
||||
TopicListenerPoller() = default;
|
||||
|
||||
/**
|
||||
* Construct a topic listener poller.
|
||||
*
|
||||
* @param inst Instance
|
||||
*/
|
||||
explicit TopicListenerPoller(NetworkTableInstance inst);
|
||||
|
||||
TopicListenerPoller(const TopicListenerPoller&) = delete;
|
||||
TopicListenerPoller& operator=(const TopicListenerPoller&) = delete;
|
||||
TopicListenerPoller(TopicListenerPoller&& rhs);
|
||||
TopicListenerPoller& operator=(TopicListenerPoller&& rhs);
|
||||
~TopicListenerPoller();
|
||||
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
NT_TopicListenerPoller GetHandle() const { return m_handle; }
|
||||
|
||||
/**
|
||||
* Start listening to changes to a particular topic.
|
||||
*
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener Add(std::span<const std::string_view> prefixes,
|
||||
unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to topic changes for topics with names that start with any
|
||||
* of the given prefixes.
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener Add(Topic topic, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on a subscriber.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener Add(const Subscriber& subscriber, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on a subscriber.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener Add(const MultiSubscriber& subscriber, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to topic changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param mask Bitmask of TopicListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener Add(const NetworkTableEntry& entry, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Remove a listener.
|
||||
*
|
||||
* @param listener Listener handle
|
||||
*/
|
||||
void Remove(NT_TopicListener listener);
|
||||
|
||||
/**
|
||||
* Read topic notifications.
|
||||
*
|
||||
* @return Topic notifications since the previous call to readQueue()
|
||||
*/
|
||||
std::vector<TopicNotification> ReadQueue();
|
||||
|
||||
private:
|
||||
NT_TopicListenerPoller m_handle{0};
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#include "TopicListener.inc"
|
||||
@@ -1,115 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "networktables/MultiSubscriber.h"
|
||||
#include "networktables/NetworkTableEntry.h"
|
||||
#include "networktables/NetworkTableInstance.h"
|
||||
#include "networktables/Topic.h"
|
||||
#include "networktables/TopicListener.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
inline TopicListener::TopicListener(
|
||||
NetworkTableInstance inst, std::span<const std::string_view> prefixes,
|
||||
unsigned int mask, std::function<void(const TopicNotification&)> listener)
|
||||
: m_handle{AddTopicListener(inst.GetHandle(), prefixes, mask, listener)} {}
|
||||
|
||||
inline TopicListener::TopicListener(
|
||||
Topic topic, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> listener)
|
||||
: m_handle{AddTopicListener(topic.GetHandle(), mask, listener)} {}
|
||||
|
||||
inline TopicListener::TopicListener(
|
||||
const Subscriber& subscriber, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> listener)
|
||||
: m_handle{AddTopicListener(subscriber.GetHandle(), mask, listener)} {}
|
||||
|
||||
inline TopicListener::TopicListener(
|
||||
const MultiSubscriber& subscriber, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> listener)
|
||||
: m_handle{AddTopicListener(subscriber.GetHandle(), mask, listener)} {}
|
||||
|
||||
inline TopicListener::TopicListener(
|
||||
const NetworkTableEntry& entry, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> listener)
|
||||
: m_handle{AddTopicListener(entry.GetHandle(), mask, listener)} {}
|
||||
|
||||
inline TopicListener::TopicListener(TopicListener&& rhs)
|
||||
: m_handle(rhs.m_handle) {
|
||||
rhs.m_handle = 0;
|
||||
}
|
||||
|
||||
inline TopicListener& TopicListener::operator=(TopicListener&& rhs) {
|
||||
std::swap(m_handle, rhs.m_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline TopicListener::~TopicListener() {
|
||||
if (m_handle != 0) {
|
||||
nt::RemoveTopicListener(m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
inline TopicListenerPoller::TopicListenerPoller(NetworkTableInstance inst)
|
||||
: m_handle(nt::CreateTopicListenerPoller(inst.GetHandle())) {}
|
||||
|
||||
inline TopicListenerPoller::TopicListenerPoller(TopicListenerPoller&& rhs)
|
||||
: m_handle(rhs.m_handle) {
|
||||
rhs.m_handle = 0;
|
||||
}
|
||||
|
||||
inline TopicListenerPoller& TopicListenerPoller::operator=(
|
||||
TopicListenerPoller&& rhs) {
|
||||
std::swap(m_handle, rhs.m_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline TopicListenerPoller::~TopicListenerPoller() {
|
||||
if (m_handle != 0) {
|
||||
nt::DestroyTopicListenerPoller(m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
inline NT_TopicListener TopicListenerPoller::Add(
|
||||
std::span<const std::string_view> prefixes, unsigned int mask) {
|
||||
return nt::AddPolledTopicListener(m_handle, prefixes, mask);
|
||||
}
|
||||
|
||||
inline NT_TopicListener TopicListenerPoller::Add(Topic topic,
|
||||
unsigned int mask) {
|
||||
return nt::AddPolledTopicListener(m_handle, topic.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_TopicListener TopicListenerPoller::Add(const Subscriber& subscriber,
|
||||
unsigned int mask) {
|
||||
return nt::AddPolledTopicListener(m_handle, subscriber.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_TopicListener TopicListenerPoller::Add(
|
||||
const MultiSubscriber& subscriber, unsigned int mask) {
|
||||
return nt::AddPolledTopicListener(m_handle, subscriber.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_TopicListener TopicListenerPoller::Add(const NetworkTableEntry& entry,
|
||||
unsigned int mask) {
|
||||
return nt::AddPolledTopicListener(m_handle, entry.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline void TopicListenerPoller::Remove(NT_TopicListener listener) {
|
||||
nt::RemoveTopicListener(listener);
|
||||
}
|
||||
|
||||
inline std::vector<TopicNotification> TopicListenerPoller::ReadQueue() {
|
||||
return nt::ReadTopicListenerQueue(m_handle);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
411
ntcore/src/main/native/include/networktables/UnitTopic.h
Normal file
411
ntcore/src/main/native/include/networktables/UnitTopic.h
Normal file
@@ -0,0 +1,411 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "networktables/Topic.h"
|
||||
|
||||
namespace wpi {
|
||||
class json;
|
||||
} // namespace wpi
|
||||
|
||||
namespace nt {
|
||||
|
||||
template <typename T>
|
||||
class UnitTopic;
|
||||
|
||||
/**
|
||||
* Timestamped unit.
|
||||
*
|
||||
* @tparam T unit type, e.g. units::meter_t
|
||||
*/
|
||||
template <typename T>
|
||||
struct TimestampedUnit {
|
||||
TimestampedUnit() = default;
|
||||
TimestampedUnit(int64_t time, int64_t serverTime, T value)
|
||||
: time{time}, serverTime{serverTime}, value{value} {}
|
||||
|
||||
/**
|
||||
* Time in local time base.
|
||||
*/
|
||||
int64_t time = 0;
|
||||
|
||||
/**
|
||||
* Time in server time base. May be 0 or 1 for locally set values.
|
||||
*/
|
||||
int64_t serverTime = 0;
|
||||
|
||||
/**
|
||||
* Value.
|
||||
*/
|
||||
T value = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* NetworkTables unit-typed subscriber.
|
||||
*
|
||||
* @tparam T unit type, e.g. units::meter_t
|
||||
*/
|
||||
template <typename T>
|
||||
class UnitSubscriber : public Subscriber {
|
||||
public:
|
||||
using TopicType = UnitTopic<T>;
|
||||
using ValueType = T;
|
||||
using ParamType = T;
|
||||
using TimestampedValueType = TimestampedUnit<T>;
|
||||
|
||||
UnitSubscriber() = default;
|
||||
|
||||
/**
|
||||
* Construct from a subscriber handle; recommended to use
|
||||
* UnitTopic::Subscribe() instead.
|
||||
*
|
||||
* @param handle Native handle
|
||||
* @param defaultValue Default value
|
||||
*/
|
||||
UnitSubscriber(NT_Subscriber handle, ParamType defaultValue);
|
||||
|
||||
/**
|
||||
* Get the last published value.
|
||||
* If no value has been published, returns the stored default value.
|
||||
*
|
||||
* @return value
|
||||
*/
|
||||
ValueType Get() const;
|
||||
|
||||
/**
|
||||
* Get the last published value.
|
||||
* If no value has been published, returns the passed defaultValue.
|
||||
*
|
||||
* @param defaultValue default value to return if no value has been published
|
||||
* @return value
|
||||
*/
|
||||
ValueType Get(ParamType defaultValue) const;
|
||||
|
||||
/**
|
||||
* Get the last published value along with its timestamp
|
||||
* If no value has been published, returns the stored default value and a
|
||||
* timestamp of 0.
|
||||
*
|
||||
* @return timestamped value
|
||||
*/
|
||||
TimestampedValueType GetAtomic() const;
|
||||
|
||||
/**
|
||||
* Get the last published value along with its timestamp.
|
||||
* If no value has been published, returns the passed defaultValue and a
|
||||
* timestamp of 0.
|
||||
*
|
||||
* @param defaultValue default value to return if no value has been published
|
||||
* @return timestamped value
|
||||
*/
|
||||
TimestampedValueType GetAtomic(ParamType defaultValue) const;
|
||||
|
||||
/**
|
||||
* Get an array of all value changes since the last call to ReadQueue.
|
||||
* Also provides a timestamp for each value.
|
||||
*
|
||||
* @note The "poll storage" subscribe option can be used to set the queue
|
||||
* depth.
|
||||
*
|
||||
* @return Array of timestamped values; empty array if no new changes have
|
||||
* been published since the previous call.
|
||||
*/
|
||||
std::vector<TimestampedValueType> ReadQueue();
|
||||
|
||||
/**
|
||||
* Get the corresponding topic.
|
||||
*
|
||||
* @return Topic
|
||||
*/
|
||||
TopicType GetTopic() const;
|
||||
|
||||
private:
|
||||
ValueType m_defaultValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* NetworkTables unit-typed publisher.
|
||||
*
|
||||
* @tparam T unit type, e.g. units::meter_t
|
||||
*/
|
||||
template <typename T>
|
||||
class UnitPublisher : public Publisher {
|
||||
public:
|
||||
using TopicType = UnitTopic<T>;
|
||||
using ValueType = T;
|
||||
using ParamType = T;
|
||||
|
||||
using TimestampedValueType = TimestampedUnit<T>;
|
||||
|
||||
UnitPublisher() = default;
|
||||
|
||||
/**
|
||||
* Construct from a publisher handle; recommended to use
|
||||
* UnitTopic::Publish() instead.
|
||||
*
|
||||
* @param handle Native handle
|
||||
*/
|
||||
explicit UnitPublisher(NT_Publisher handle);
|
||||
|
||||
/**
|
||||
* Publish a new value.
|
||||
*
|
||||
* @param value value to publish
|
||||
* @param time timestamp; 0 indicates current NT time should be used
|
||||
*/
|
||||
void Set(ParamType value, int64_t time = 0);
|
||||
|
||||
/**
|
||||
* Publish a default value.
|
||||
* On reconnect, a default value will never be used in preference to a
|
||||
* published value.
|
||||
*
|
||||
* @param value value
|
||||
*/
|
||||
void SetDefault(ParamType value);
|
||||
|
||||
/**
|
||||
* Get the corresponding topic.
|
||||
*
|
||||
* @return Topic
|
||||
*/
|
||||
TopicType GetTopic() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* NetworkTables unit-typed entry.
|
||||
*
|
||||
* @note Unlike NetworkTableEntry, the entry goes away when this is destroyed.
|
||||
*
|
||||
* @tparam T unit type, e.g. units::meter_t
|
||||
*/
|
||||
template <typename T>
|
||||
class UnitEntry final : public UnitSubscriber<T>, public UnitPublisher<T> {
|
||||
public:
|
||||
using SubscriberType = UnitSubscriber<T>;
|
||||
using PublisherType = UnitPublisher<T>;
|
||||
using TopicType = UnitTopic<T>;
|
||||
using ValueType = T;
|
||||
using ParamType = T;
|
||||
|
||||
using TimestampedValueType = TimestampedUnit<T>;
|
||||
|
||||
UnitEntry() = default;
|
||||
|
||||
/**
|
||||
* Construct from an entry handle; recommended to use
|
||||
* UnitTopic::GetEntry() instead.
|
||||
*
|
||||
* @param handle Native handle
|
||||
* @param defaultValue Default value
|
||||
*/
|
||||
UnitEntry(NT_Entry handle, ParamType defaultValue);
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
*
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
explicit operator bool() const { return this->m_subHandle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle for the entry.
|
||||
*
|
||||
* @return Native handle
|
||||
*/
|
||||
NT_Entry GetHandle() const { return this->m_subHandle; }
|
||||
|
||||
/**
|
||||
* Get the corresponding topic.
|
||||
*
|
||||
* @return Topic
|
||||
*/
|
||||
TopicType GetTopic() const;
|
||||
|
||||
/**
|
||||
* Stops publishing the entry if it's published.
|
||||
*/
|
||||
void Unpublish();
|
||||
};
|
||||
|
||||
/**
|
||||
* NetworkTables unit-typed topic. Publishers publish the type name (e.g.
|
||||
* "meter") as the "unit" property. Type conversions are not performed--for
|
||||
* correct behavior the publisher and subscriber must use the same unit type,
|
||||
* but this can be checked at runtime using IsMatchingUnit().
|
||||
*
|
||||
* @tparam T unit type, e.g. units::meter_t
|
||||
*/
|
||||
template <typename T>
|
||||
class UnitTopic final : public Topic {
|
||||
public:
|
||||
using SubscriberType = UnitSubscriber<T>;
|
||||
using PublisherType = UnitPublisher<T>;
|
||||
using EntryType = UnitEntry<T>;
|
||||
using ValueType = T;
|
||||
using ParamType = T;
|
||||
using TimestampedValueType = TimestampedUnit<T>;
|
||||
/** The default type string for this topic type. */
|
||||
static constexpr std::string_view kTypeString = "double";
|
||||
|
||||
UnitTopic() = default;
|
||||
|
||||
/**
|
||||
* Construct from a topic handle.
|
||||
*
|
||||
* @param handle Native handle
|
||||
*/
|
||||
explicit UnitTopic(NT_Topic handle) : Topic{handle} {}
|
||||
|
||||
/**
|
||||
* Construct from a generic topic.
|
||||
*
|
||||
* @param topic Topic
|
||||
*/
|
||||
explicit UnitTopic(Topic topic) : Topic{topic} {}
|
||||
|
||||
/**
|
||||
* Verify the topic has a matching unit type (if the topic is published).
|
||||
*
|
||||
* @return True if unit matches, false if not matching or topic not published.
|
||||
*/
|
||||
bool IsMatchingUnit() const;
|
||||
|
||||
/**
|
||||
* Create a new subscriber to the topic.
|
||||
*
|
||||
* <p>The subscriber is only active as long as the returned object
|
||||
* is not destroyed.
|
||||
*
|
||||
* @note Subscribers that do not match the published data type do not return
|
||||
* any values. To determine if the data type matches, use the appropriate
|
||||
* Topic functions.
|
||||
*
|
||||
* @param defaultValue default value used when a default is not provided to a
|
||||
* getter function
|
||||
* @param options subscribe options
|
||||
* @return subscriber
|
||||
*/
|
||||
[[nodiscard]] SubscriberType Subscribe(
|
||||
ParamType defaultValue, std::span<const PubSubOption> options = {});
|
||||
|
||||
/**
|
||||
* Create a new subscriber to the topic, with specific type string.
|
||||
*
|
||||
* <p>The subscriber is only active as long as the returned object
|
||||
* is not destroyed.
|
||||
*
|
||||
* @note Subscribers that do not match the published data type do not return
|
||||
* any values. To determine if the data type matches, use the appropriate
|
||||
* Topic functions.
|
||||
*
|
||||
* @param typeString type string
|
||||
* @param defaultValue default value used when a default is not provided to a
|
||||
* getter function
|
||||
* @param options subscribe options
|
||||
* @return subscriber
|
||||
*/
|
||||
[[nodiscard]] SubscriberType SubscribeEx(
|
||||
std::string_view typeString, ParamType defaultValue,
|
||||
std::span<const PubSubOption> options = {});
|
||||
|
||||
/**
|
||||
* Create a new publisher to the topic.
|
||||
*
|
||||
* The publisher is only active as long as the returned object
|
||||
* is not destroyed.
|
||||
*
|
||||
* @note It is not possible to publish two different data types to the same
|
||||
* topic. Conflicts between publishers are typically resolved by the
|
||||
* server on a first-come, first-served basis. Any published values that
|
||||
* do not match the topic's data type are dropped (ignored). To determine
|
||||
* if the data type matches, use the appropriate Topic functions.
|
||||
*
|
||||
* @param options publish options
|
||||
* @return publisher
|
||||
*/
|
||||
[[nodiscard]] PublisherType Publish(
|
||||
std::span<const PubSubOption> options = {});
|
||||
|
||||
/**
|
||||
* Create a new publisher to the topic, with type string and initial
|
||||
* properties.
|
||||
*
|
||||
* The publisher is only active as long as the returned object
|
||||
* is not destroyed.
|
||||
*
|
||||
* @note It is not possible to publish two different data types to the same
|
||||
* topic. Conflicts between publishers are typically resolved by the
|
||||
* server on a first-come, first-served basis. Any published values that
|
||||
* do not match the topic's data type are dropped (ignored). To determine
|
||||
* if the data type matches, use the appropriate Topic functions.
|
||||
*
|
||||
* @param typeString type string
|
||||
* @param properties JSON properties
|
||||
* @param options publish options
|
||||
* @return publisher
|
||||
*/
|
||||
[[nodiscard]] PublisherType PublishEx(
|
||||
std::string_view typeString, const wpi::json& properties,
|
||||
std::span<const PubSubOption> options = {});
|
||||
|
||||
/**
|
||||
* Create a new entry for the topic.
|
||||
*
|
||||
* Entries act as a combination of a subscriber and a weak publisher. The
|
||||
* subscriber is active as long as the entry is not destroyed. The publisher
|
||||
* is created when the entry is first written to, and remains active until
|
||||
* either Unpublish() is called or the entry is destroyed.
|
||||
*
|
||||
* @note It is not possible to use two different data types with the same
|
||||
* topic. Conflicts between publishers are typically resolved by the
|
||||
* server on a first-come, first-served basis. Any published values that
|
||||
* do not match the topic's data type are dropped (ignored), and the entry
|
||||
* will show no new values if the data type does not match. To determine
|
||||
* if the data type matches, use the appropriate Topic functions.
|
||||
*
|
||||
* @param defaultValue default value used when a default is not provided to a
|
||||
* getter function
|
||||
* @param options publish and/or subscribe options
|
||||
* @return entry
|
||||
*/
|
||||
[[nodiscard]] EntryType GetEntry(ParamType defaultValue,
|
||||
std::span<const PubSubOption> options = {});
|
||||
|
||||
/**
|
||||
* Create a new entry for the topic, with specific type string.
|
||||
*
|
||||
* Entries act as a combination of a subscriber and a weak publisher. The
|
||||
* subscriber is active as long as the entry is not destroyed. The publisher
|
||||
* is created when the entry is first written to, and remains active until
|
||||
* either Unpublish() is called or the entry is destroyed.
|
||||
*
|
||||
* @note It is not possible to use two different data types with the same
|
||||
* topic. Conflicts between publishers are typically resolved by the
|
||||
* server on a first-come, first-served basis. Any published values that
|
||||
* do not match the topic's data type are dropped (ignored), and the entry
|
||||
* will show no new values if the data type does not match. To determine
|
||||
* if the data type matches, use the appropriate Topic functions.
|
||||
*
|
||||
* @param typeString type string
|
||||
* @param defaultValue default value used when a default is not provided to a
|
||||
* getter function
|
||||
* @param options publish and/or subscribe options
|
||||
* @return entry
|
||||
*/
|
||||
[[nodiscard]] EntryType GetEntryEx(
|
||||
std::string_view typeString, ParamType defaultValue,
|
||||
std::span<const PubSubOption> options = {});
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#include "networktables/UnitTopic.inc"
|
||||
142
ntcore/src/main/native/include/networktables/UnitTopic.inc
Normal file
142
ntcore/src/main/native/include/networktables/UnitTopic.inc
Normal file
@@ -0,0 +1,142 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/json.h>
|
||||
|
||||
#include "networktables/NetworkTableType.h"
|
||||
#include "networktables/UnitTopic.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
template <typename T>
|
||||
inline UnitSubscriber<T>::UnitSubscriber(NT_Subscriber handle, T defaultValue)
|
||||
: Subscriber{handle}, m_defaultValue{defaultValue} {}
|
||||
|
||||
template <typename T>
|
||||
inline T UnitSubscriber<T>::Get() const {
|
||||
return Get(m_defaultValue);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T UnitSubscriber<T>::Get(T defaultValue) const {
|
||||
return T{::nt::GetDouble(m_subHandle, defaultValue.value())};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline TimestampedUnit<T> UnitSubscriber<T>::GetAtomic() const {
|
||||
return GetAtomic(m_defaultValue);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline TimestampedUnit<T> UnitSubscriber<T>::GetAtomic(T defaultValue) const {
|
||||
auto doubleVal = ::nt::GetAtomicDouble(m_subHandle, defaultValue.value());
|
||||
return {doubleVal.time, doubleVal.serverTime, doubleVal.value};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::vector<TimestampedUnit<T>> UnitSubscriber<T>::ReadQueue() {
|
||||
std::vector<TimestampedUnit<T>> vals;
|
||||
auto doubleVals = ::nt::ReadQueueDouble(m_subHandle);
|
||||
vals.reserve(doubleVals.size());
|
||||
for (auto&& val : doubleVals) {
|
||||
vals.emplace_back(val.time, val.serverTime, val.value);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitTopic<T> UnitSubscriber<T>::GetTopic() const {
|
||||
return UnitTopic<T>{::nt::GetTopicFromHandle(m_subHandle)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitPublisher<T>::UnitPublisher(NT_Publisher handle)
|
||||
: Publisher{handle} {}
|
||||
|
||||
template <typename T>
|
||||
inline void UnitPublisher<T>::Set(T value, int64_t time) {
|
||||
::nt::SetDouble(m_pubHandle, value.value(), time);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void UnitPublisher<T>::SetDefault(T value) {
|
||||
::nt::SetDefaultDouble(m_pubHandle, value.value());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitTopic<T> UnitPublisher<T>::GetTopic() const {
|
||||
return UnitTopic<T>{::nt::GetTopicFromHandle(m_pubHandle)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitEntry<T>::UnitEntry(NT_Entry handle, T defaultValue)
|
||||
: UnitSubscriber<T>{handle, defaultValue}, UnitPublisher<T>{handle} {}
|
||||
|
||||
template <typename T>
|
||||
inline UnitTopic<T> UnitEntry<T>::GetTopic() const {
|
||||
return UnitTopic<T>{::nt::GetTopicFromHandle(this->m_subHandle)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void UnitEntry<T>::Unpublish() {
|
||||
::nt::Unpublish(this->m_pubHandle);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool UnitTopic<T>::IsMatchingUnit() const {
|
||||
return GetProperty("unit") == T{}.name();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitSubscriber<T> UnitTopic<T>::Subscribe(
|
||||
T defaultValue, std::span<const PubSubOption> options) {
|
||||
return UnitSubscriber<T>{
|
||||
::nt::Subscribe(m_handle, NT_DOUBLE, "double", options), defaultValue};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitSubscriber<T> UnitTopic<T>::SubscribeEx(
|
||||
std::string_view typeString, T defaultValue,
|
||||
std::span<const PubSubOption> options) {
|
||||
return UnitSubscriber<T>{
|
||||
::nt::Subscribe(m_handle, NT_DOUBLE, typeString, options), defaultValue};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitPublisher<T> UnitTopic<T>::Publish(
|
||||
std::span<const PubSubOption> options) {
|
||||
return UnitPublisher<T>{::nt::PublishEx(m_handle, NT_DOUBLE, "double",
|
||||
{{"unit", T{}.name()}}, options)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitPublisher<T> UnitTopic<T>::PublishEx(
|
||||
std::string_view typeString, const wpi::json& properties,
|
||||
std::span<const PubSubOption> options) {
|
||||
wpi::json props = properties;
|
||||
props["unit"] = T{}.name();
|
||||
return UnitPublisher<T>{
|
||||
::nt::PublishEx(m_handle, NT_DOUBLE, typeString, props, options)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitEntry<T> UnitTopic<T>::GetEntry(
|
||||
T defaultValue, std::span<const PubSubOption> options) {
|
||||
return UnitEntry<T>{::nt::GetEntry(m_handle, NT_DOUBLE, "double", options),
|
||||
defaultValue};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitEntry<T> UnitTopic<T>::GetEntryEx(
|
||||
std::string_view typeString, T defaultValue,
|
||||
std::span<const PubSubOption> options) {
|
||||
return UnitEntry<T>{::nt::GetEntry(m_handle, NT_DOUBLE, typeString, options),
|
||||
defaultValue};
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
@@ -1,206 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class MultiSubscriber;
|
||||
class NetworkTableEntry;
|
||||
class NetworkTableInstance;
|
||||
class Subscriber;
|
||||
|
||||
/**
|
||||
* Flag values for use with value listeners.
|
||||
*
|
||||
* The flags are a bitmask and must be OR'ed together to indicate the
|
||||
* combination of events desired to be received.
|
||||
*
|
||||
* By default, notifications are only generated for remote changes occurring
|
||||
* after the listener is created. The constants kImmediate and kLocal are
|
||||
* modifiers that cause notifications to be generated at other times.
|
||||
*/
|
||||
struct ValueListenerFlags {
|
||||
ValueListenerFlags() = delete;
|
||||
|
||||
/**
|
||||
* Initial listener addition.
|
||||
*
|
||||
* Set this flag to receive immediate notification of the current value.
|
||||
*/
|
||||
static constexpr unsigned int kImmediate = NT_VALUE_NOTIFY_IMMEDIATE;
|
||||
|
||||
/**
|
||||
* Changed locally.
|
||||
*
|
||||
* Set this flag to receive notification of both local changes and changes
|
||||
* coming from remote nodes. By default, notifications are only generated for
|
||||
* remote changes.
|
||||
*/
|
||||
static constexpr unsigned int kLocal = NT_VALUE_NOTIFY_LOCAL;
|
||||
};
|
||||
|
||||
/**
|
||||
* Value change listener. This calls back to a callback function when a value
|
||||
* change matching the specified mask occurs. The callback function is called
|
||||
* asynchronously on a separate thread, so it's important to use synchronization
|
||||
* or atomics when accessing any shared state from the callback function.
|
||||
*/
|
||||
class ValueListener final {
|
||||
public:
|
||||
ValueListener() = default;
|
||||
|
||||
/**
|
||||
* Create a listener for value changes on a subscriber.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
ValueListener(const Subscriber& subscriber, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> listener);
|
||||
|
||||
/**
|
||||
* Create a listener for value changes on a subscriber.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
ValueListener(const MultiSubscriber& subscriber, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> listener);
|
||||
|
||||
/**
|
||||
* Create a listener for value changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param mask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
ValueListener(const NetworkTableEntry& entry, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> listener);
|
||||
|
||||
/**
|
||||
* Create a listener for value changes on a subscriber/entry handle.
|
||||
*
|
||||
* @param subentry Subscriber/entry handle
|
||||
* @param mask Bitmask of ValueListenerFlags values
|
||||
* @param listener Listener function
|
||||
*/
|
||||
ValueListener(NT_Handle subentry, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> listener);
|
||||
|
||||
ValueListener(const ValueListener&) = delete;
|
||||
ValueListener& operator=(const ValueListener&) = delete;
|
||||
ValueListener(ValueListener&& rhs);
|
||||
ValueListener& operator=(ValueListener&& rhs);
|
||||
~ValueListener();
|
||||
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
NT_ValueListener GetHandle() const { return m_handle; }
|
||||
|
||||
private:
|
||||
NT_ValueListener m_handle{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* Value change listener. This queues value change events matching the specified
|
||||
* mask. Code using the listener must periodically call readQueue() to read the
|
||||
* events.
|
||||
*/
|
||||
class ValueListenerPoller final {
|
||||
public:
|
||||
ValueListenerPoller() = default;
|
||||
|
||||
/**
|
||||
* Construct a value listener poller.
|
||||
*
|
||||
* @param inst Instance
|
||||
*/
|
||||
explicit ValueListenerPoller(NetworkTableInstance inst);
|
||||
|
||||
ValueListenerPoller(const ValueListenerPoller&) = delete;
|
||||
ValueListenerPoller& operator=(const ValueListenerPoller&) = delete;
|
||||
ValueListenerPoller(ValueListenerPoller&& rhs);
|
||||
ValueListenerPoller& operator=(ValueListenerPoller&& rhs);
|
||||
~ValueListenerPoller();
|
||||
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
NT_ValueListenerPoller GetHandle() const { return m_handle; }
|
||||
|
||||
/**
|
||||
* Start listening to value changes on a subscriber.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of ValueListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ValueListener Add(const Subscriber& subscriber, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to value changes on a subscriber.
|
||||
*
|
||||
* @param subscriber Subscriber
|
||||
* @param mask Bitmask of ValueListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ValueListener Add(const MultiSubscriber& subscriber, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to value changes on an entry.
|
||||
*
|
||||
* @param entry Entry
|
||||
* @param mask Bitmask of ValueListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ValueListener Add(const NetworkTableEntry& entry, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Start listening to value changes on a subscriber/entry handle.
|
||||
*
|
||||
* @param subentry Subscriber/entry handle
|
||||
* @param mask Bitmask of ValueListenerFlags values
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ValueListener Add(NT_Handle subentry, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Remove a listener.
|
||||
*
|
||||
* @param listener Listener handle
|
||||
*/
|
||||
void Remove(NT_ValueListener listener);
|
||||
|
||||
/**
|
||||
* Reads value listener queue (all value changes since last call).
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @return Array of value notifications.
|
||||
*/
|
||||
std::vector<ValueNotification> ReadQueue();
|
||||
|
||||
private:
|
||||
NT_ValueListenerPoller m_handle{0};
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#include "ValueListener.inc"
|
||||
@@ -1,103 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "networktables/MultiSubscriber.h"
|
||||
#include "networktables/NetworkTableEntry.h"
|
||||
#include "networktables/NetworkTableInstance.h"
|
||||
#include "networktables/Topic.h"
|
||||
#include "networktables/ValueListener.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
inline ValueListener::ValueListener(
|
||||
const Subscriber& subscriber, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> listener)
|
||||
: m_handle{AddValueListener(subscriber.GetHandle(), mask, listener)} {}
|
||||
|
||||
inline ValueListener::ValueListener(
|
||||
const MultiSubscriber& subscriber, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> listener)
|
||||
: m_handle{AddValueListener(subscriber.GetHandle(), mask, listener)} {}
|
||||
|
||||
inline ValueListener::ValueListener(
|
||||
const NetworkTableEntry& entry, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> listener)
|
||||
: m_handle{AddValueListener(entry.GetHandle(), mask, listener)} {}
|
||||
|
||||
inline ValueListener::ValueListener(
|
||||
NT_Handle subentry, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> listener)
|
||||
: m_handle{AddValueListener(subentry, mask, listener)} {}
|
||||
|
||||
inline ValueListener::ValueListener(ValueListener&& rhs)
|
||||
: m_handle(rhs.m_handle) {
|
||||
rhs.m_handle = 0;
|
||||
}
|
||||
|
||||
inline ValueListener& ValueListener::operator=(ValueListener&& rhs) {
|
||||
std::swap(m_handle, rhs.m_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ValueListener::~ValueListener() {
|
||||
if (m_handle != 0) {
|
||||
nt::RemoveValueListener(m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
inline ValueListenerPoller::ValueListenerPoller(NetworkTableInstance inst)
|
||||
: m_handle(nt::CreateValueListenerPoller(inst.GetHandle())) {}
|
||||
|
||||
inline ValueListenerPoller::ValueListenerPoller(ValueListenerPoller&& rhs)
|
||||
: m_handle(rhs.m_handle) {
|
||||
rhs.m_handle = 0;
|
||||
}
|
||||
|
||||
inline ValueListenerPoller& ValueListenerPoller::operator=(
|
||||
ValueListenerPoller&& rhs) {
|
||||
std::swap(m_handle, rhs.m_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ValueListenerPoller::~ValueListenerPoller() {
|
||||
if (m_handle != 0) {
|
||||
nt::DestroyValueListenerPoller(m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
inline NT_ValueListener ValueListenerPoller::Add(const Subscriber& subscriber,
|
||||
unsigned int mask) {
|
||||
return Add(subscriber.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_ValueListener ValueListenerPoller::Add(
|
||||
const MultiSubscriber& subscriber, unsigned int mask) {
|
||||
return Add(subscriber.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_ValueListener ValueListenerPoller::Add(const NetworkTableEntry& entry,
|
||||
unsigned int mask) {
|
||||
return Add(entry.GetHandle(), mask);
|
||||
}
|
||||
|
||||
inline NT_ValueListener ValueListenerPoller::Add(NT_Handle subentry,
|
||||
unsigned int mask) {
|
||||
return nt::AddPolledValueListener(m_handle, subentry, mask);
|
||||
}
|
||||
|
||||
inline void ValueListenerPoller::Remove(NT_ValueListener listener) {
|
||||
nt::RemoveValueListener(listener);
|
||||
}
|
||||
|
||||
inline std::vector<ValueNotification> ValueListenerPoller::ReadQueue() {
|
||||
return nt::ReadValueListenerQueue(m_handle);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
@@ -29,21 +29,15 @@ typedef int NT_Bool;
|
||||
|
||||
typedef unsigned int NT_Handle;
|
||||
typedef NT_Handle NT_ConnectionDataLogger;
|
||||
typedef NT_Handle NT_ConnectionListener;
|
||||
typedef NT_Handle NT_ConnectionListenerPoller;
|
||||
typedef NT_Handle NT_DataLogger;
|
||||
typedef NT_Handle NT_Entry;
|
||||
typedef NT_Handle NT_Inst;
|
||||
typedef NT_Handle NT_Logger;
|
||||
typedef NT_Handle NT_LoggerPoller;
|
||||
typedef NT_Handle NT_Listener;
|
||||
typedef NT_Handle NT_ListenerPoller;
|
||||
typedef NT_Handle NT_MultiSubscriber;
|
||||
typedef NT_Handle NT_Topic;
|
||||
typedef NT_Handle NT_TopicListener;
|
||||
typedef NT_Handle NT_TopicListenerPoller;
|
||||
typedef NT_Handle NT_Subscriber;
|
||||
typedef NT_Handle NT_Publisher;
|
||||
typedef NT_Handle NT_ValueListener;
|
||||
typedef NT_Handle NT_ValueListenerPoller;
|
||||
|
||||
/** Default network tables port number (NT3) */
|
||||
#define NT_DEFAULT_PORT3 1735
|
||||
@@ -103,20 +97,33 @@ enum NT_PubSubOptionType {
|
||||
NT_PUBSUB_KEEPDUPLICATES, /* preserve duplicate values */
|
||||
};
|
||||
|
||||
/** Topic notification flags. */
|
||||
enum NT_TopicNotifyKind {
|
||||
NT_TOPIC_NOTIFY_NONE = 0,
|
||||
NT_TOPIC_NOTIFY_IMMEDIATE = 0x01, /* initial listener addition */
|
||||
NT_TOPIC_NOTIFY_PUBLISH = 0x02, /* initially published */
|
||||
NT_TOPIC_NOTIFY_UNPUBLISH = 0x04, /* no more publishers */
|
||||
NT_TOPIC_NOTIFY_PROPERTIES = 0x08, /* properties changed */
|
||||
};
|
||||
|
||||
/** Value notification flags. */
|
||||
enum NT_ValueNotifyKind {
|
||||
NT_VALUE_NOTIFY_NONE = 0,
|
||||
NT_VALUE_NOTIFY_IMMEDIATE = 0x01, /* initial listener addition */
|
||||
NT_VALUE_NOTIFY_LOCAL = 0x02, /* changed locally */
|
||||
/** Event notification flags. */
|
||||
enum NT_EventFlags {
|
||||
NT_EVENT_NONE = 0,
|
||||
/** Initial listener addition. */
|
||||
NT_EVENT_IMMEDIATE = 0x01,
|
||||
/** Client connected (on server, any client connected). */
|
||||
NT_EVENT_CONNECTED = 0x02,
|
||||
/** Client disconnected (on server, any client disconnected). */
|
||||
NT_EVENT_DISCONNECTED = 0x04,
|
||||
/** Any connection event (connect or disconnect). */
|
||||
NT_EVENT_CONNECTION = NT_EVENT_CONNECTED | NT_EVENT_DISCONNECTED,
|
||||
/** New topic published. */
|
||||
NT_EVENT_PUBLISH = 0x08,
|
||||
/** Topic unpublished. */
|
||||
NT_EVENT_UNPUBLISH = 0x10,
|
||||
/** Topic properties changed. */
|
||||
NT_EVENT_PROPERTIES = 0x20,
|
||||
/** Any topic event (publish, unpublish, or properties changed). */
|
||||
NT_EVENT_TOPIC = NT_EVENT_PUBLISH | NT_EVENT_UNPUBLISH | NT_EVENT_PROPERTIES,
|
||||
/** Topic value updated (via network). */
|
||||
NT_EVENT_VALUE_REMOTE = 0x40,
|
||||
/** Topic value updated (local). */
|
||||
NT_EVENT_VALUE_LOCAL = 0x80,
|
||||
/** Topic value updated (network or local). */
|
||||
NT_EVENT_VALUE_ALL = NT_EVENT_VALUE_REMOTE | NT_EVENT_VALUE_LOCAL,
|
||||
/** Log message. */
|
||||
NT_EVENT_LOGMESSAGE = 0x100,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -178,6 +185,7 @@ struct NT_Value {
|
||||
} data;
|
||||
};
|
||||
|
||||
/** NetworkTables Topic Information */
|
||||
struct NT_TopicInfo {
|
||||
/** Topic handle */
|
||||
NT_Topic topic;
|
||||
@@ -221,23 +229,8 @@ struct NT_ConnectionInfo {
|
||||
unsigned int protocol_version;
|
||||
};
|
||||
|
||||
/** NetworkTables Topic Notification */
|
||||
struct NT_TopicNotification {
|
||||
/** Listener that was triggered. */
|
||||
NT_TopicListener listener;
|
||||
|
||||
/** Topic info. */
|
||||
struct NT_TopicInfo info;
|
||||
|
||||
/** Update flags. */
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
/** NetworkTables Value Notification */
|
||||
struct NT_ValueNotification {
|
||||
/** Listener that was triggered. */
|
||||
NT_ValueListener listener;
|
||||
|
||||
/** NetworkTables value event data. */
|
||||
struct NT_ValueEventData {
|
||||
/** Topic handle. */
|
||||
NT_Topic topic;
|
||||
|
||||
@@ -246,31 +239,10 @@ struct NT_ValueNotification {
|
||||
|
||||
/** The new value. */
|
||||
struct NT_Value value;
|
||||
|
||||
/**
|
||||
* Update flags. For example, NT_NOTIFY_NEW if the key did not previously
|
||||
* exist.
|
||||
*/
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
/** NetworkTables Connection Notification */
|
||||
struct NT_ConnectionNotification {
|
||||
/** Listener that was triggered. */
|
||||
NT_ConnectionListener listener;
|
||||
|
||||
/** True if event is due to connection being established. */
|
||||
NT_Bool connected;
|
||||
|
||||
/** Connection info. */
|
||||
struct NT_ConnectionInfo conn;
|
||||
};
|
||||
|
||||
/** NetworkTables log message. */
|
||||
struct NT_LogMessage {
|
||||
/** The logger that generated the message. */
|
||||
NT_Logger logger;
|
||||
|
||||
/** Log level of the message. See NT_LogLevel. */
|
||||
unsigned int level;
|
||||
|
||||
@@ -284,6 +256,30 @@ struct NT_LogMessage {
|
||||
char* message;
|
||||
};
|
||||
|
||||
/** NetworkTables event */
|
||||
struct NT_Event {
|
||||
/** Listener that triggered this event. */
|
||||
NT_Handle listener;
|
||||
|
||||
/**
|
||||
* Event flags (NT_EventFlags). Also indicates the data included with the
|
||||
* event:
|
||||
* - NT_EVENT_CONNECTED or NT_EVENT_DISCONNECTED: connInfo
|
||||
* - NT_EVENT_PUBLISH, NT_EVENT_UNPUBLISH, or NT_EVENT_PROPERTIES: topicInfo
|
||||
* - NT_EVENT_VALUE_REMOTE, NT_NOTIFY_VALUE_LOCAL: valueData
|
||||
* - NT_EVENT_LOGMESSAGE: logMessage
|
||||
*/
|
||||
unsigned int flags;
|
||||
|
||||
/** Event data; content depends on flags. */
|
||||
union {
|
||||
struct NT_ConnectionInfo connInfo;
|
||||
struct NT_TopicInfo topicInfo;
|
||||
struct NT_ValueEventData valueData;
|
||||
struct NT_LogMessage logMessage;
|
||||
} data;
|
||||
};
|
||||
|
||||
/** NetworkTables publish/subscribe option. */
|
||||
struct NT_PubSubOption {
|
||||
/** Option type. */
|
||||
@@ -804,307 +800,184 @@ void NT_UnsubscribeMultiple(NT_MultiSubscriber sub);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup ntcore_topiclistener_cfunc Topic Listener Functions
|
||||
* @defgroup ntcore_listener_cfunc Listener Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Topic listener callback function.
|
||||
* Event listener callback function.
|
||||
*
|
||||
* @param data data pointer provided to callback creation function
|
||||
* @param event event info
|
||||
*/
|
||||
typedef void (*NT_TopicListenerCallback)(
|
||||
void* data, const struct NT_TopicNotification* event);
|
||||
typedef void (*NT_ListenerCallback)(void* data, const struct NT_Event* event);
|
||||
|
||||
/**
|
||||
* Creates a listener poller.
|
||||
*
|
||||
* A poller provides a single queue of poll events. Events linked to this
|
||||
* poller (using NT_AddPolledXListener()) will be stored in the queue and
|
||||
* must be collected by calling NT_ReadListenerQueue().
|
||||
* The returned handle must be destroyed with NT_DestroyListenerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_ListenerPoller NT_CreateListenerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroys a listener poller. This will abort any blocked polling
|
||||
* call and prevent additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void NT_DestroyListenerPoller(NT_ListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Read notifications.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param len length of returned array (output)
|
||||
* @return Array of events. Returns NULL and len=0 if no events since last
|
||||
* call.
|
||||
*/
|
||||
struct NT_Event* NT_ReadListenerQueue(NT_ListenerPoller poller, size_t* len);
|
||||
|
||||
/**
|
||||
* Removes a listener.
|
||||
*
|
||||
* @param listener Listener handle to remove
|
||||
*/
|
||||
void NT_RemoveListener(NT_Listener listener);
|
||||
|
||||
/**
|
||||
* Wait for the listener queue to be empty. This is primarily useful
|
||||
* for deterministic testing. This blocks until either the listener
|
||||
* queue is empty (e.g. there are no more events that need to be passed along to
|
||||
* callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param handle handle
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a
|
||||
* negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
NT_Bool NT_WaitForListenerQueue(NT_Handle handle, double timeout);
|
||||
|
||||
/**
|
||||
* Create a listener for changes to topics with names that start with
|
||||
* the given prefix.
|
||||
* the given prefix. This creates a corresponding internal subscriber with the
|
||||
* lifetime of the listener.
|
||||
*
|
||||
* @param inst Instance handle
|
||||
* @param prefix Topic name string prefix
|
||||
* @param prefix_len Length of topic name string prefix
|
||||
* @param mask Bitmask of NT_TopicListenerFlags values
|
||||
* @param mask Bitmask of NT_EventFlags values (only topic and value events will
|
||||
* be generated)
|
||||
* @param data Data passed to callback function
|
||||
* @param callback Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener NT_AddTopicListener(NT_Inst inst, const char* prefix,
|
||||
size_t prefix_len, unsigned int mask,
|
||||
void* data,
|
||||
NT_TopicListenerCallback callback);
|
||||
NT_Listener NT_AddListenerSingle(NT_Inst inst, const char* prefix,
|
||||
size_t prefix_len, unsigned int mask,
|
||||
void* data, NT_ListenerCallback callback);
|
||||
|
||||
/**
|
||||
* Create a listener for changes to topics with names that start with any of
|
||||
* the given prefixes.
|
||||
* the given prefixes. This creates a corresponding internal subscriber with the
|
||||
* lifetime of the listener.
|
||||
*
|
||||
* @param inst Instance handle
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param prefixes_len Number of elements in prefixes array
|
||||
* @param mask Bitmask of NT_TopicListenerFlags values
|
||||
* @param mask Bitmask of NT_EventFlags values (only topic and value events will
|
||||
* be generated)
|
||||
* @param data Data passed to callback function
|
||||
* @param callback Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener NT_AddTopicListenerMultiple(NT_Inst inst,
|
||||
const struct NT_String* prefixes,
|
||||
size_t prefixes_len,
|
||||
unsigned int mask, void* data,
|
||||
NT_TopicListenerCallback callback);
|
||||
NT_Listener NT_AddListenerMultiple(NT_Inst inst,
|
||||
const struct NT_String* prefixes,
|
||||
size_t prefixes_len, unsigned int mask,
|
||||
void* data, NT_ListenerCallback callback);
|
||||
|
||||
/**
|
||||
* Create a listener for changes on a particular topic.
|
||||
* Create a listener.
|
||||
*
|
||||
* @param topic Topic handle
|
||||
* @param mask Bitmask of NT_TopicListenerFlags values
|
||||
* Some combinations of handle and mask have no effect:
|
||||
* - connection and log message events are only generated on instances
|
||||
* - topic and value events are only generated on non-instances
|
||||
*
|
||||
* Adding value and topic events on a topic will create a corresponding internal
|
||||
* subscriber with the lifetime of the listener.
|
||||
*
|
||||
* Adding a log message listener through this function will only result in
|
||||
* events at NT_LOG_INFO or higher; for more customized settings, use
|
||||
* NT_AddLogger().
|
||||
*
|
||||
* @param handle Handle
|
||||
* @param mask Bitmask of NT_EventFlags values
|
||||
* @param data Data passed to callback function
|
||||
* @param callback Listener function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener NT_AddTopicListenerSingle(NT_Topic topic, unsigned int mask,
|
||||
void* data,
|
||||
NT_TopicListenerCallback callback);
|
||||
NT_Listener NT_AddListener(NT_Handle handle, unsigned int mask, void* data,
|
||||
NT_ListenerCallback callback);
|
||||
|
||||
/**
|
||||
* Creates a topic listener poller.
|
||||
*
|
||||
* A poller provides a single queue of poll events. Events linked to this
|
||||
* poller (using NT_AddPolledTopicListener()) will be stored in the queue and
|
||||
* must be collected by calling NT_ReadTopicListenerQueue().
|
||||
* The returned handle must be destroyed with NT_DestroyTopicListenerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_TopicListenerPoller NT_CreateTopicListenerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroys a topic listener poller. This will abort any blocked polling
|
||||
* call and prevent additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void NT_DestroyTopicListenerPoller(NT_TopicListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Read topic notifications.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param len length of returned array (output)
|
||||
* @return Array of topic notifications. Returns NULL and len=0 if no
|
||||
* notifications since last call.
|
||||
*/
|
||||
struct NT_TopicNotification* NT_ReadTopicListenerQueue(
|
||||
NT_TopicListenerPoller poller, size_t* len);
|
||||
|
||||
/**
|
||||
* Creates a polled topic listener.
|
||||
* The caller is responsible for calling NT_ReadTopicListenerQueue() to poll.
|
||||
* Creates a polled topic listener. This creates a corresponding internal
|
||||
* subscriber with the lifetime of the listener.
|
||||
* The caller is responsible for calling NT_ReadListenerQueue() to poll.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param prefix UTF-8 string prefix
|
||||
* @param prefix_len Length of UTF-8 string prefix
|
||||
* @param mask NT_NotifyKind bitmask
|
||||
* @param mask NT_EventFlags bitmask (only topic and value events
|
||||
* will be generated)
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener NT_AddPolledTopicListener(NT_TopicListenerPoller poller,
|
||||
const char* prefix,
|
||||
size_t prefix_len,
|
||||
unsigned int mask);
|
||||
NT_Listener NT_AddPolledListenerSingle(NT_ListenerPoller poller,
|
||||
const char* prefix, size_t prefix_len,
|
||||
unsigned int mask);
|
||||
|
||||
/**
|
||||
* Creates a polled topic listener.
|
||||
* The caller is responsible for calling NT_ReadTopicListenerQueue() to poll.
|
||||
* Creates a polled topic listener. This creates a corresponding internal
|
||||
* subscriber with the lifetime of the listener.
|
||||
* The caller is responsible for calling NT_ReadListenerQueue() to poll.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param prefixes array of UTF-8 string prefixes
|
||||
* @param prefixes_len Length of prefixes array
|
||||
* @param mask NT_EventFlags bitmask (only topic and value events
|
||||
* will be generated)
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Listener NT_AddPolledListenerMultiple(NT_ListenerPoller poller,
|
||||
const struct NT_String* prefixes,
|
||||
size_t prefixes_len,
|
||||
unsigned int mask);
|
||||
|
||||
/**
|
||||
* Creates a polled listener.
|
||||
* The caller is responsible for calling NT_ReadListenerQueue() to poll.
|
||||
*
|
||||
* Some combinations of handle and mask have no effect:
|
||||
* - connection and log message events are only generated on instances
|
||||
* - topic and value events are only generated on non-instances
|
||||
*
|
||||
* Adding value and topic events on a topic will create a corresponding internal
|
||||
* subscriber with the lifetime of the listener.
|
||||
*
|
||||
* Adding a log message listener through this function will only result in
|
||||
* events at NT_LOG_INFO or higher; for more customized settings, use
|
||||
* NT_AddPolledLogger().
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param handle handle
|
||||
* @param mask NT_NotifyKind bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener NT_AddPolledTopicListenerMultiple(
|
||||
NT_TopicListenerPoller poller, const struct NT_String* prefixes,
|
||||
size_t prefixes_len, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Creates a polled topic listener.
|
||||
* The caller is responsible for calling NT_ReadTopicListenerQueue() to poll.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param topic topic
|
||||
* @param mask NT_NotifyKind bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener NT_AddPolledTopicListenerSingle(NT_TopicListenerPoller poller,
|
||||
NT_Topic topic,
|
||||
unsigned int mask);
|
||||
|
||||
/**
|
||||
* Removes a topic listener.
|
||||
*
|
||||
* @param topic_listener Listener handle to remove
|
||||
*/
|
||||
void NT_RemoveTopicListener(NT_TopicListener topic_listener);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup ntcore_valuelistener_cfunc Value Listener Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Value listener callback function.
|
||||
*
|
||||
* @param data data pointer provided to callback creation function
|
||||
* @param event event info
|
||||
*/
|
||||
typedef void (*NT_ValueListenerCallback)(
|
||||
void* data, const struct NT_ValueNotification* event);
|
||||
|
||||
/**
|
||||
* Create a listener for value changes on a subscriber.
|
||||
*
|
||||
* @param subentry Subscriber/entry
|
||||
* @param mask Bitmask of NT_ValueListenerFlags values
|
||||
* @param data Data passed to listener function
|
||||
* @param callback Listener function
|
||||
*/
|
||||
NT_ValueListener NT_AddValueListener(NT_Handle subentry, unsigned int mask,
|
||||
void* data,
|
||||
NT_ValueListenerCallback callback);
|
||||
|
||||
/**
|
||||
* Create a value listener poller.
|
||||
*
|
||||
* A poller provides a single queue of poll events. Events linked to this
|
||||
* poller (using NT_AddPolledValueListener()) will be stored in the queue and
|
||||
* must be collected by calling NT_ReadValueListenerQueue().
|
||||
* The returned handle must be destroyed with NT_DestroyValueListenerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_ValueListenerPoller NT_CreateValueListenerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroy a value listener poller. This will abort any blocked polling
|
||||
* call and prevent additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void NT_DestroyValueListenerPoller(NT_ValueListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Reads value listener queue (all value changes since last call).
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param len length of returned array (output)
|
||||
* @return Array of value notifications. Returns NULL and len=0 if no
|
||||
* notifications since last call.
|
||||
*/
|
||||
struct NT_ValueNotification* NT_ReadValueListenerQueue(
|
||||
NT_ValueListenerPoller poller, size_t* len);
|
||||
|
||||
/**
|
||||
* Create a polled value listener.
|
||||
* The caller is responsible for calling NT_ReadValueListenerQueue() to poll.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param subentry subscriber or entry handle
|
||||
* @param flags NT_NotifyKind bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ValueListener NT_AddPolledValueListener(NT_ValueListenerPoller poller,
|
||||
NT_Handle subentry,
|
||||
unsigned int flags);
|
||||
|
||||
/**
|
||||
* Remove a value listener.
|
||||
*
|
||||
* @param value_listener Listener handle to remove
|
||||
*/
|
||||
void NT_RemoveValueListener(NT_ValueListener value_listener);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup ntcore_connectionlistener_cfunc Connection Listener Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Connection listener callback function.
|
||||
* Called when a network connection is made or lost.
|
||||
*
|
||||
* @param data data pointer provided to callback creation function
|
||||
* @param event event info
|
||||
*/
|
||||
typedef void (*NT_ConnectionListenerCallback)(
|
||||
void* data, const struct NT_ConnectionNotification* event);
|
||||
|
||||
/**
|
||||
* Add a connection listener.
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @param data data pointer to pass to callback
|
||||
* @param callback listener to add
|
||||
* @param immediate_notify notify listener of all existing connections
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ConnectionListener NT_AddConnectionListener(
|
||||
NT_Inst inst, void* data, NT_ConnectionListenerCallback callback,
|
||||
NT_Bool immediate_notify);
|
||||
|
||||
/**
|
||||
* Create a connection listener poller.
|
||||
* A poller provides a single queue of poll events. Events linked to this
|
||||
* poller (using NT_AddPolledConnectionListener()) will be stored in the queue
|
||||
* and must be collected by calling NT_PollConnectionListener().
|
||||
* The returned handle must be destroyed with
|
||||
* NT_DestroyConnectionListenerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_ConnectionListenerPoller NT_CreateConnectionListenerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroy a connection listener poller. This will abort any blocked polling
|
||||
* call and prevent additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void NT_DestroyConnectionListenerPoller(NT_ConnectionListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Create a polled connection listener.
|
||||
* The caller is responsible for calling NT_PollConnectionListener() to poll.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param immediate_notify notify listener of all existing connections
|
||||
*/
|
||||
NT_ConnectionListener NT_AddPolledConnectionListener(
|
||||
NT_ConnectionListenerPoller poller, NT_Bool immediate_notify);
|
||||
|
||||
/**
|
||||
* Get the next connection event. This blocks until the next connect or
|
||||
* disconnect occurs. This is intended to be used with
|
||||
* NT_AddPolledConnectionListener(); connection listeners created using
|
||||
* NT_AddConnectionListener() will not be serviced through this function.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param len length of returned array (output)
|
||||
* @return Array of information on the connection events. Only returns NULL
|
||||
* if an error occurred (e.g. the instance was invalid or is shutting
|
||||
* down).
|
||||
*/
|
||||
struct NT_ConnectionNotification* NT_ReadConnectionListenerQueue(
|
||||
NT_ConnectionListenerPoller poller, size_t* len);
|
||||
|
||||
/**
|
||||
* Remove a connection listener.
|
||||
*
|
||||
* @param conn_listener Listener handle to remove
|
||||
*/
|
||||
void NT_RemoveConnectionListener(NT_ConnectionListener conn_listener);
|
||||
NT_Listener NT_AddPolledListener(NT_ListenerPoller poller, NT_Handle handle,
|
||||
unsigned int mask);
|
||||
|
||||
/** @} */
|
||||
|
||||
@@ -1351,67 +1224,19 @@ void NT_DisposeTopicInfoArray(struct NT_TopicInfo* arr, size_t count);
|
||||
void NT_DisposeTopicInfo(struct NT_TopicInfo* info);
|
||||
|
||||
/**
|
||||
* Disposes an topic notification array.
|
||||
* Disposes an event array.
|
||||
*
|
||||
* @param arr pointer to the array to dispose
|
||||
* @param count number of elements in the array
|
||||
*/
|
||||
void NT_DisposeTopicNotificationArray(struct NT_TopicNotification* arr,
|
||||
size_t count);
|
||||
void NT_DisposeEventArray(struct NT_Event* arr, size_t count);
|
||||
|
||||
/**
|
||||
* Disposes a single topic notification.
|
||||
* Disposes a single event.
|
||||
*
|
||||
* @param info pointer to the info to dispose
|
||||
* @param event pointer to the event to dispose
|
||||
*/
|
||||
void NT_DisposeTopicNotification(struct NT_TopicNotification* info);
|
||||
|
||||
/**
|
||||
* Disposes an value notification array.
|
||||
*
|
||||
* @param arr pointer to the array to dispose
|
||||
* @param count number of elements in the array
|
||||
*/
|
||||
void NT_DisposeValueNotificationArray(struct NT_ValueNotification* arr,
|
||||
size_t count);
|
||||
|
||||
/**
|
||||
* Disposes a single value notification.
|
||||
*
|
||||
* @param info pointer to the info to dispose
|
||||
*/
|
||||
void NT_DisposeValueNotification(struct NT_ValueNotification* info);
|
||||
|
||||
/**
|
||||
* Disposes a connection notification array.
|
||||
*
|
||||
* @param arr pointer to the array to dispose
|
||||
* @param count number of elements in the array
|
||||
*/
|
||||
void NT_DisposeConnectionNotificationArray(
|
||||
struct NT_ConnectionNotification* arr, size_t count);
|
||||
|
||||
/**
|
||||
* Disposes a single connection notification.
|
||||
*
|
||||
* @param info pointer to the info to dispose
|
||||
*/
|
||||
void NT_DisposeConnectionNotification(struct NT_ConnectionNotification* info);
|
||||
|
||||
/**
|
||||
* Disposes a log message array.
|
||||
*
|
||||
* @param arr pointer to the array to dispose
|
||||
* @param count number of elements in the array
|
||||
*/
|
||||
void NT_DisposeLogMessageArray(struct NT_LogMessage* arr, size_t count);
|
||||
|
||||
/**
|
||||
* Disposes a single log message.
|
||||
*
|
||||
* @param info pointer to the info to dispose
|
||||
*/
|
||||
void NT_DisposeLogMessage(struct NT_LogMessage* info);
|
||||
void NT_DisposeEvent(struct NT_Event* event);
|
||||
|
||||
/**
|
||||
* Returns monotonic current time in 1 us increments.
|
||||
@@ -1442,14 +1267,6 @@ void NT_SetNow(uint64_t timestamp);
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Log function.
|
||||
*
|
||||
* @param data data pointer passed to NT_AddLogger()
|
||||
* @param msg message information
|
||||
*/
|
||||
typedef void (*NT_LogFunc)(void* data, const struct NT_LogMessage* msg);
|
||||
|
||||
/**
|
||||
* Add logger callback function. By default, log messages are sent to stderr;
|
||||
* this function sends log messages to the provided callback function instead.
|
||||
@@ -1458,61 +1275,28 @@ typedef void (*NT_LogFunc)(void* data, const struct NT_LogMessage* msg);
|
||||
* messages outside this range will be silently ignored.
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @param data data pointer to pass to func
|
||||
* @param func log callback function
|
||||
* @param min_level minimum log level
|
||||
* @param max_level maximum log level
|
||||
* @return Logger handle
|
||||
* @param data data pointer to pass to func
|
||||
* @param func listener callback function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Logger NT_AddLogger(NT_Inst inst, void* data, NT_LogFunc func,
|
||||
unsigned int min_level, unsigned int max_level);
|
||||
NT_Listener NT_AddLogger(NT_Inst inst, unsigned int min_level,
|
||||
unsigned int max_level, void* data,
|
||||
NT_ListenerCallback func);
|
||||
|
||||
/**
|
||||
* Create a log poller. A poller provides a single queue of poll events.
|
||||
* The returned handle must be destroyed with NT_DestroyLoggerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_LoggerPoller NT_CreateLoggerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroy a log poller. This will abort any blocked polling call and prevent
|
||||
* additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void NT_DestroyLoggerPoller(NT_LoggerPoller poller);
|
||||
|
||||
/**
|
||||
* Set the log level for a log poller. Events will only be generated for
|
||||
* Set the log level for a listener poller. Events will only be generated for
|
||||
* log messages with level greater than or equal to min_level and less than or
|
||||
* equal to max_level; messages outside this range will be silently ignored.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param min_level minimum log level
|
||||
* @param max_level maximum log level
|
||||
* @return Logger handle
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Logger NT_AddPolledLogger(NT_LoggerPoller poller, unsigned int min_level,
|
||||
unsigned int max_level);
|
||||
|
||||
/**
|
||||
* Get the next log event. This blocks until the next log occurs.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param len length of returned array (output)
|
||||
* @return Array of information on the log events. Only returns NULL if an
|
||||
* error occurred (e.g. the instance was invalid or is shutting down).
|
||||
*/
|
||||
struct NT_LogMessage* NT_ReadLoggerQueue(NT_LoggerPoller poller, size_t* len);
|
||||
|
||||
/**
|
||||
* Remove a logger.
|
||||
*
|
||||
* @param logger Logger handle to remove
|
||||
*/
|
||||
void NT_RemoveLogger(NT_Logger logger);
|
||||
NT_Listener NT_AddPolledLogger(NT_ListenerPoller poller, unsigned int min_level,
|
||||
unsigned int max_level);
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
@@ -40,6 +41,47 @@ namespace nt {
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Event notification flags.
|
||||
*
|
||||
* The flags are a bitmask and must be OR'ed together to indicate the
|
||||
* combination of events desired to be received.
|
||||
*
|
||||
*/
|
||||
struct EventFlags {
|
||||
EventFlags() = delete;
|
||||
|
||||
static constexpr unsigned int kNone = NT_EVENT_NONE;
|
||||
/**
|
||||
* Initial listener addition.
|
||||
* Set this flag to receive immediate notification of matches to the
|
||||
* flag criteria.
|
||||
*/
|
||||
static constexpr unsigned int kImmediate = NT_EVENT_IMMEDIATE;
|
||||
/** Client connected (on server, any client connected). */
|
||||
static constexpr unsigned int kConnected = NT_EVENT_CONNECTED;
|
||||
/** Client disconnected (on server, any client disconnected). */
|
||||
static constexpr unsigned int kDisconnected = NT_EVENT_DISCONNECTED;
|
||||
/** Any connection event (connect or disconnect). */
|
||||
static constexpr unsigned int kConnection = kConnected | kDisconnected;
|
||||
/** New topic published. */
|
||||
static constexpr unsigned int kPublish = NT_EVENT_PUBLISH;
|
||||
/** Topic unpublished. */
|
||||
static constexpr unsigned int kUnpublish = NT_EVENT_UNPUBLISH;
|
||||
/** Topic properties changed. */
|
||||
static constexpr unsigned int kProperties = NT_EVENT_PROPERTIES;
|
||||
/** Any topic event (publish, unpublish, or properties changed). */
|
||||
static constexpr unsigned int kTopic = kPublish | kUnpublish | kProperties;
|
||||
/** Topic value updated (via network). */
|
||||
static constexpr unsigned int kValueRemote = NT_EVENT_VALUE_REMOTE;
|
||||
/** Topic value updated (local). */
|
||||
static constexpr unsigned int kValueLocal = NT_EVENT_VALUE_LOCAL;
|
||||
/** Topic value updated (network or local). */
|
||||
static constexpr unsigned int kValueAll = kValueRemote | kValueLocal;
|
||||
/** Log message. */
|
||||
static constexpr unsigned int kLogMessage = NT_EVENT_LOGMESSAGE;
|
||||
};
|
||||
|
||||
/** NetworkTables Topic Information */
|
||||
struct TopicInfo {
|
||||
/** Topic handle */
|
||||
@@ -106,47 +148,12 @@ struct ConnectionInfo {
|
||||
}
|
||||
};
|
||||
|
||||
/** NetworkTables Topic Notification */
|
||||
class TopicNotification {
|
||||
/** NetworkTables Value Event Data */
|
||||
class ValueEventData {
|
||||
public:
|
||||
TopicNotification() = default;
|
||||
TopicNotification(NT_TopicListener listener_, TopicInfo info_,
|
||||
unsigned int flags_)
|
||||
: listener(listener_), info(std::move(info_)), flags(flags_) {}
|
||||
|
||||
/** Listener that was triggered. */
|
||||
NT_TopicListener listener{0};
|
||||
|
||||
/** Topic info. */
|
||||
TopicInfo info;
|
||||
|
||||
/**
|
||||
* Notification flags.
|
||||
*/
|
||||
unsigned int flags{0};
|
||||
|
||||
friend void swap(TopicNotification& first, TopicNotification& second) {
|
||||
using std::swap;
|
||||
swap(first.listener, second.listener);
|
||||
swap(first.info, second.info);
|
||||
swap(first.flags, second.flags);
|
||||
}
|
||||
};
|
||||
|
||||
/** NetworkTables Value Notification */
|
||||
class ValueNotification {
|
||||
public:
|
||||
ValueNotification() = default;
|
||||
ValueNotification(NT_ValueListener listener_, NT_Topic topic_,
|
||||
NT_Handle subentry_, Value value_, unsigned int flags_)
|
||||
: listener(listener_),
|
||||
topic(topic_),
|
||||
subentry(subentry_),
|
||||
value(std::move(value_)),
|
||||
flags(flags_) {}
|
||||
|
||||
/** Listener that was triggered. */
|
||||
NT_ValueListener listener{0};
|
||||
ValueEventData() = default;
|
||||
ValueEventData(NT_Topic topic, NT_Handle subentry, Value value)
|
||||
: topic{topic}, subentry{subentry}, value{std::move(value)} {}
|
||||
|
||||
/** Topic handle. */
|
||||
NT_Topic topic{0};
|
||||
@@ -156,63 +163,15 @@ class ValueNotification {
|
||||
|
||||
/** The new value. */
|
||||
Value value;
|
||||
|
||||
/**
|
||||
* Update flags. For example, NT_NOTIFY_NEW if the key did not previously
|
||||
* exist.
|
||||
*/
|
||||
unsigned int flags{0};
|
||||
|
||||
friend void swap(ValueNotification& first, ValueNotification& second) {
|
||||
using std::swap;
|
||||
swap(first.listener, second.listener);
|
||||
swap(first.topic, second.topic);
|
||||
swap(first.subentry, second.subentry);
|
||||
swap(first.value, second.value);
|
||||
swap(first.flags, second.flags);
|
||||
}
|
||||
};
|
||||
|
||||
/** NetworkTables Connection Notification */
|
||||
class ConnectionNotification {
|
||||
public:
|
||||
ConnectionNotification() = default;
|
||||
ConnectionNotification(NT_ConnectionListener listener_, bool connected_,
|
||||
ConnectionInfo conn_)
|
||||
: listener(listener_), connected(connected_), conn(std::move(conn_)) {}
|
||||
|
||||
/** Listener that was triggered. */
|
||||
NT_ConnectionListener listener{0};
|
||||
|
||||
/** True if event is due to connection being established. */
|
||||
bool connected = false;
|
||||
|
||||
/** Connection info. */
|
||||
ConnectionInfo conn;
|
||||
|
||||
friend void swap(ConnectionNotification& first,
|
||||
ConnectionNotification& second) {
|
||||
using std::swap;
|
||||
swap(first.listener, second.listener);
|
||||
swap(first.connected, second.connected);
|
||||
swap(first.conn, second.conn);
|
||||
}
|
||||
};
|
||||
|
||||
/** NetworkTables log message. */
|
||||
class LogMessage {
|
||||
public:
|
||||
LogMessage() = default;
|
||||
LogMessage(NT_Logger logger_, unsigned int level_, std::string_view filename_,
|
||||
unsigned int line_, std::string_view message_)
|
||||
: logger(logger_),
|
||||
level(level_),
|
||||
filename(filename_),
|
||||
line(line_),
|
||||
message(message_) {}
|
||||
|
||||
/** The logger that generated the message. */
|
||||
NT_Logger logger{0};
|
||||
LogMessage(unsigned int level, std::string_view filename, unsigned int line,
|
||||
std::string_view message)
|
||||
: level{level}, filename{filename}, line{line}, message{message} {}
|
||||
|
||||
/** Log level of the message. See NT_LogLevel. */
|
||||
unsigned int level{0};
|
||||
@@ -225,15 +184,71 @@ class LogMessage {
|
||||
|
||||
/** The message. */
|
||||
std::string message;
|
||||
};
|
||||
|
||||
friend void swap(LogMessage& first, LogMessage& second) {
|
||||
using std::swap;
|
||||
swap(first.logger, second.logger);
|
||||
swap(first.level, second.level);
|
||||
swap(first.filename, second.filename);
|
||||
swap(first.line, second.line);
|
||||
swap(first.message, second.message);
|
||||
/** NetworkTables event */
|
||||
class Event {
|
||||
public:
|
||||
Event() = default;
|
||||
Event(NT_Listener listener, unsigned int flags, ConnectionInfo info)
|
||||
: listener{listener}, flags{flags}, data{std::move(info)} {}
|
||||
Event(NT_Listener listener, unsigned int flags, TopicInfo info)
|
||||
: listener{listener}, flags{flags}, data{std::move(info)} {}
|
||||
Event(NT_Listener listener, unsigned int flags, ValueEventData data)
|
||||
: listener{listener}, flags{flags}, data{std::move(data)} {}
|
||||
Event(NT_Listener listener, unsigned int flags, LogMessage msg)
|
||||
: listener{listener}, flags{flags}, data{std::move(msg)} {}
|
||||
Event(NT_Listener listener, unsigned int flags, NT_Topic topic,
|
||||
NT_Handle subentry, Value value)
|
||||
: listener{listener},
|
||||
flags{flags},
|
||||
data{ValueEventData{topic, subentry, std::move(value)}} {}
|
||||
Event(NT_Listener listener, unsigned int flags, unsigned int level,
|
||||
std::string_view filename, unsigned int line, std::string_view message)
|
||||
: listener{listener},
|
||||
flags{flags},
|
||||
data{LogMessage{level, filename, line, message}} {}
|
||||
|
||||
/** Listener that triggered this event. */
|
||||
NT_Listener listener{0};
|
||||
|
||||
/**
|
||||
* Event flags (NT_EventFlags). Also indicates the data included with the
|
||||
* event:
|
||||
* - NT_NOTIFY_CONNECTED or NT_NOTIFY_DISCONNECTED: GetConnectionInfo()
|
||||
* - NT_NOTIFY_PUBLISH, NT_NOTIFY_UNPUBLISH, or NT_NOTIFY_PROPERTIES:
|
||||
* GetTopicInfo()
|
||||
* - NT_NOTIFY_VALUE, NT_NOTIFY_VALUE_LOCAL: GetValueData()
|
||||
* - NT_NOTIFY_LOGMESSAGE: GetLogMessage()
|
||||
*/
|
||||
unsigned int flags{0};
|
||||
|
||||
/** Event data; content depends on flags. */
|
||||
std::variant<ConnectionInfo, TopicInfo, ValueEventData, LogMessage> data;
|
||||
|
||||
const ConnectionInfo* GetConnectionInfo() const {
|
||||
return std::get_if<nt::ConnectionInfo>(&data);
|
||||
}
|
||||
ConnectionInfo* GetConnectionInfo() {
|
||||
return std::get_if<nt::ConnectionInfo>(&data);
|
||||
}
|
||||
|
||||
const TopicInfo* GetTopicInfo() const {
|
||||
return std::get_if<nt::TopicInfo>(&data);
|
||||
}
|
||||
TopicInfo* GetTopicInfo() { return std::get_if<nt::TopicInfo>(&data); }
|
||||
|
||||
const ValueEventData* GetValueEventData() const {
|
||||
return std::get_if<nt::ValueEventData>(&data);
|
||||
}
|
||||
ValueEventData* GetValueEventData() {
|
||||
return std::get_if<nt::ValueEventData>(&data);
|
||||
}
|
||||
|
||||
const LogMessage* GetLogMessage() const {
|
||||
return std::get_if<nt::LogMessage>(&data);
|
||||
}
|
||||
LogMessage* GetLogMessage() { return std::get_if<nt::LogMessage>(&data); }
|
||||
};
|
||||
|
||||
/** NetworkTables publish/subscribe option. */
|
||||
@@ -771,234 +786,134 @@ void UnsubscribeMultiple(NT_MultiSubscriber sub);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup ntcore_topiclistener_func Topic Listener Functions
|
||||
* @defgroup ntcore_listener_func Listener Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
using ListenerCallback = std::function<void(const Event&)>;
|
||||
|
||||
/**
|
||||
* Creates a listener poller.
|
||||
*
|
||||
* A poller provides a single queue of poll events. Events linked to this
|
||||
* poller (using AddPolledListener()) will be stored in the queue and
|
||||
* must be collected by calling ReadListenerQueue().
|
||||
* The returned handle must be destroyed with DestroyListenerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_ListenerPoller CreateListenerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroys a listener poller. This will abort any blocked polling
|
||||
* call and prevent additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void DestroyListenerPoller(NT_ListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Read notifications.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @return Array of events. Returns empty array if no events since last call.
|
||||
*/
|
||||
std::vector<Event> ReadListenerQueue(NT_ListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Removes a listener.
|
||||
*
|
||||
* @param listener Listener handle to remove
|
||||
*/
|
||||
void RemoveListener(NT_Listener listener);
|
||||
|
||||
/**
|
||||
* Wait for the listener queue to be empty. This is primarily useful
|
||||
* for deterministic testing. This blocks until either the listener
|
||||
* queue is empty (e.g. there are no more events that need to be passed along to
|
||||
* callbacks or poll queues) or the timeout expires.
|
||||
*
|
||||
* @param handle handle
|
||||
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a
|
||||
* negative value to block indefinitely
|
||||
* @return False if timed out, otherwise true.
|
||||
*/
|
||||
bool WaitForListenerQueue(NT_Handle handle, double timeout);
|
||||
|
||||
/**
|
||||
* Create a listener for changes to topics with names that start with any of
|
||||
* the given prefixes.
|
||||
* the given prefixes. This creates a corresponding internal subscriber with the
|
||||
* lifetime of the listener.
|
||||
*
|
||||
* @param inst Instance handle
|
||||
* @param prefixes Topic name string prefixes
|
||||
* @param mask Bitmask of NT_TopicListenerFlags values
|
||||
* @param mask Bitmask of NT_EventFlags values (only topic and value events will
|
||||
* be generated)
|
||||
* @param callback Listener function
|
||||
*/
|
||||
NT_TopicListener AddTopicListener(
|
||||
NT_Inst inst, std::span<const std::string_view> prefixes, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> callback);
|
||||
NT_Listener AddListener(NT_Inst inst,
|
||||
std::span<const std::string_view> prefixes,
|
||||
unsigned int mask, ListenerCallback callback);
|
||||
|
||||
/**
|
||||
* Create a listener for changes on a particular topic.
|
||||
* Create a listener.
|
||||
*
|
||||
* @param handle Topic, subscriber, multi-subscriber, or entry handle
|
||||
* @param mask Bitmask of NT_TopicListenerFlags values
|
||||
* Some combinations of handle and mask have no effect:
|
||||
* - connection and log message events are only generated on instances
|
||||
* - topic and value events are only generated on non-instances
|
||||
*
|
||||
* Adding value and topic events on a topic will create a corresponding internal
|
||||
* subscriber with the lifetime of the listener.
|
||||
*
|
||||
* Adding a log message listener through this function will only result in
|
||||
* events at NT_LOG_INFO or higher; for more customized settings, use
|
||||
* AddLogger().
|
||||
*
|
||||
* @param handle Instance, topic, subscriber, multi-subscriber, or entry handle
|
||||
* @param mask Bitmask of NT_EventFlags values
|
||||
* @param callback Listener function
|
||||
*/
|
||||
NT_TopicListener AddTopicListener(
|
||||
NT_Handle handle, unsigned int mask,
|
||||
std::function<void(const TopicNotification&)> callback);
|
||||
NT_Listener AddListener(NT_Handle handle, unsigned int mask,
|
||||
ListenerCallback callback);
|
||||
|
||||
/**
|
||||
* Creates a topic listener poller.
|
||||
* Creates a polled listener. This creates a corresponding internal subscriber
|
||||
* with the lifetime of the listener.
|
||||
* The caller is responsible for calling ReadListenerQueue() to poll.
|
||||
*
|
||||
* A poller provides a single queue of poll events. Events linked to this
|
||||
* poller (using AddPolledTopicListener()) will be stored in the queue and
|
||||
* must be collected by calling ReadTopicListenerQueue().
|
||||
* The returned handle must be destroyed with DestroyTopicListenerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_TopicListenerPoller CreateTopicListenerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroys a topic listener poller. This will abort any blocked polling
|
||||
* call and prevent additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void DestroyTopicListenerPoller(NT_TopicListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Read topic notifications.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @return Array of topic notifications. Returns empty array if no
|
||||
* notifications since last call.
|
||||
*/
|
||||
std::vector<TopicNotification> ReadTopicListenerQueue(
|
||||
NT_TopicListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Creates a polled topic listener.
|
||||
* The caller is responsible for calling ReadTopicListenerQueue() to poll.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param prefixes array of UTF-8 string prefixes
|
||||
* @param mask NT_NotifyKind bitmask
|
||||
* @param poller poller handle
|
||||
* @param prefixes array of UTF-8 string prefixes
|
||||
* @param mask Bitmask of NT_EventFlags values (only topic and value events will
|
||||
* be generated)
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener AddPolledTopicListener(
|
||||
NT_TopicListenerPoller poller, std::span<const std::string_view> prefixes,
|
||||
unsigned int mask);
|
||||
NT_Listener AddPolledListener(NT_ListenerPoller poller,
|
||||
std::span<const std::string_view> prefixes,
|
||||
unsigned int mask);
|
||||
|
||||
/**
|
||||
* Creates a polled topic listener.
|
||||
* The caller is responsible for calling ReadTopicListenerQueue() to poll.
|
||||
* Creates a polled listener.
|
||||
* The caller is responsible for calling ReadListenerQueue() to poll.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param handle topic, subscriber, multi-subscriber, or entry handle
|
||||
* @param mask NT_NotifyKind bitmask
|
||||
* Some combinations of handle and mask have no effect:
|
||||
* - connection and log message events are only generated on instances
|
||||
* - topic and value events are only generated on non-instances
|
||||
*
|
||||
* Adding value and topic events on a topic will create a corresponding internal
|
||||
* subscriber with the lifetime of the listener.
|
||||
*
|
||||
* Adding a log message listener through this function will only result in
|
||||
* events at NT_LOG_INFO or higher; for more customized settings, use
|
||||
* AddPolledLogger().
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param handle instance, topic, subscriber, multi-subscriber, or entry handle
|
||||
* @param mask NT_EventFlags bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_TopicListener AddPolledTopicListener(NT_TopicListenerPoller poller,
|
||||
NT_Handle handle, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Removes a topic listener.
|
||||
*
|
||||
* @param listener Listener handle to remove
|
||||
*/
|
||||
void RemoveTopicListener(NT_TopicListener listener);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup ntcore_valuelistener_func Value Listener Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a listener for value changes on a subscriber.
|
||||
*
|
||||
* @param subentry Subscriber/entry
|
||||
* @param mask Bitmask of NT_ValueListenerFlags values
|
||||
* @param callback Listener function
|
||||
*/
|
||||
NT_ValueListener AddValueListener(
|
||||
NT_Handle subentry, unsigned int mask,
|
||||
std::function<void(const ValueNotification&)> callback);
|
||||
|
||||
/**
|
||||
* Create a value listener poller.
|
||||
*
|
||||
* A poller provides a single queue of poll events. Events linked to this
|
||||
* poller (using AddPolledValueListener()) will be stored in the queue and
|
||||
* must be collected by calling ReadValueListenerQueue().
|
||||
* The returned handle must be destroyed with DestroyValueListenerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_ValueListenerPoller CreateValueListenerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroy a value listener poller. This will abort any blocked polling
|
||||
* call and prevent additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void DestroyValueListenerPoller(NT_ValueListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Reads value listener queue (all value changes since last call).
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @return Array of value notifications.
|
||||
*/
|
||||
std::vector<ValueNotification> ReadValueListenerQueue(
|
||||
NT_ValueListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Create a polled value listener.
|
||||
* The caller is responsible for calling ReadValueListenerQueue() to poll.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param subentry subscriber or entry handle
|
||||
* @param mask NT_NotifyKind bitmask
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ValueListener AddPolledValueListener(NT_ValueListenerPoller poller,
|
||||
NT_Handle subentry, unsigned int mask);
|
||||
|
||||
/**
|
||||
* Remove a value listener.
|
||||
*
|
||||
* @param listener Listener handle to remove
|
||||
*/
|
||||
void RemoveValueListener(NT_ValueListener listener);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup ntcore_connectionlistener_func Connection Listener Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add a connection listener.
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @param callback listener to add
|
||||
* @param immediate_notify notify listener of all existing connections
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_ConnectionListener AddConnectionListener(
|
||||
NT_Inst inst,
|
||||
std::function<void(const ConnectionNotification& event)> callback,
|
||||
bool immediate_notify);
|
||||
|
||||
/**
|
||||
* Create a connection listener poller.
|
||||
*
|
||||
* A poller provides a single queue of poll events. Events linked to this
|
||||
* poller (using AddPolledConnectionListener()) will be stored in the queue and
|
||||
* must be collected by calling PollConnectionListener().
|
||||
* The returned handle must be destroyed with DestroyConnectionListenerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_ConnectionListenerPoller CreateConnectionListenerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroy a connection listener poller. This will abort any blocked polling
|
||||
* call and prevent additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void DestroyConnectionListenerPoller(NT_ConnectionListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Create a polled connection listener.
|
||||
* The caller is responsible for calling PollConnectionListener() to poll.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @param immediate_notify notify listener of all existing connections
|
||||
*/
|
||||
NT_ConnectionListener AddPolledConnectionListener(
|
||||
NT_ConnectionListenerPoller poller, bool immediate_notify);
|
||||
|
||||
/**
|
||||
* Get the next connection event. This blocks until the next connect or
|
||||
* disconnect occurs. This is intended to be used with
|
||||
* AddPolledConnectionListener(); connection listeners created using
|
||||
* AddConnectionListener() will not be serviced through this function.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @return Information on the connection events. Only returns empty if an
|
||||
* error occurred (e.g. the instance was invalid or is shutting down).
|
||||
*/
|
||||
std::vector<ConnectionNotification> ReadConnectionListenerQueue(
|
||||
NT_ConnectionListenerPoller poller);
|
||||
|
||||
/**
|
||||
* Remove a connection listener.
|
||||
*
|
||||
* @param conn_listener Listener handle to remove
|
||||
*/
|
||||
void RemoveConnectionListener(NT_ConnectionListener conn_listener);
|
||||
NT_Listener AddPolledListener(NT_ListenerPoller poller, NT_Handle handle,
|
||||
unsigned int mask);
|
||||
|
||||
/** @} */
|
||||
|
||||
@@ -1275,31 +1190,13 @@ void StopConnectionDataLog(NT_ConnectionDataLogger logger);
|
||||
* messages outside this range will be silently ignored.
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @param func log callback function
|
||||
* @param min_level minimum log level
|
||||
* @param max_level maximum log level
|
||||
* @return Logger handle
|
||||
* @param func listener callback function
|
||||
* @return Listener handle
|
||||
*/
|
||||
NT_Logger AddLogger(NT_Inst inst,
|
||||
std::function<void(const LogMessage& msg)> func,
|
||||
unsigned int min_level, unsigned int max_level);
|
||||
|
||||
/**
|
||||
* Create a log poller. A poller provides a single queue of poll events.
|
||||
* The returned handle must be destroyed with DestroyLoggerPoller().
|
||||
*
|
||||
* @param inst instance handle
|
||||
* @return poller handle
|
||||
*/
|
||||
NT_LoggerPoller CreateLoggerPoller(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Destroy a log poller. This will abort any blocked polling call and prevent
|
||||
* additional events from being generated for this poller.
|
||||
*
|
||||
* @param poller poller handle
|
||||
*/
|
||||
void DestroyLoggerPoller(NT_LoggerPoller poller);
|
||||
NT_Listener AddLogger(NT_Inst inst, unsigned int min_level,
|
||||
unsigned int max_level, ListenerCallback func);
|
||||
|
||||
/**
|
||||
* Set the log level for a log poller. Events will only be generated for
|
||||
@@ -1311,24 +1208,8 @@ void DestroyLoggerPoller(NT_LoggerPoller poller);
|
||||
* @param max_level maximum log level
|
||||
* @return Logger handle
|
||||
*/
|
||||
NT_Logger AddPolledLogger(NT_LoggerPoller poller, unsigned int min_level,
|
||||
unsigned int max_level);
|
||||
|
||||
/**
|
||||
* Get the next log event. This blocks until the next log occurs.
|
||||
*
|
||||
* @param poller poller handle
|
||||
* @return Information on the log events. Only returns empty if an error
|
||||
* occurred (e.g. the instance was invalid or is shutting down).
|
||||
*/
|
||||
std::vector<LogMessage> ReadLoggerQueue(NT_LoggerPoller poller);
|
||||
|
||||
/**
|
||||
* Remove a logger.
|
||||
*
|
||||
* @param logger Logger handle to remove
|
||||
*/
|
||||
void RemoveLogger(NT_Logger logger);
|
||||
NT_Listener AddPolledLogger(NT_ListenerPoller poller, unsigned int min_level,
|
||||
unsigned int max_level);
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
||||
@@ -8,7 +8,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import edu.wpi.first.util.WPIUtilJNI;
|
||||
@@ -61,9 +60,11 @@ class ConnectionListenerTest {
|
||||
@Test
|
||||
void testJNI() {
|
||||
// set up the poller
|
||||
int poller = NetworkTablesJNI.createConnectionListenerPoller(m_serverInst.getHandle());
|
||||
int poller = NetworkTablesJNI.createListenerPoller(m_serverInst.getHandle());
|
||||
assertNotSame(poller, 0, "bad poller handle");
|
||||
int handle = NetworkTablesJNI.addPolledConnectionListener(poller, false);
|
||||
int handle =
|
||||
NetworkTablesJNI.addListener(
|
||||
poller, m_serverInst.getHandle(), NetworkTableEvent.kConnection);
|
||||
assertNotSame(handle, 0, "bad listener handle");
|
||||
|
||||
// trigger a connect event
|
||||
@@ -75,13 +76,13 @@ class ConnectionListenerTest {
|
||||
} catch (InterruptedException ex) {
|
||||
fail("interrupted while waiting for queue");
|
||||
}
|
||||
ConnectionNotification[] events =
|
||||
NetworkTablesJNI.readConnectionListenerQueue(m_serverInst, poller);
|
||||
NetworkTableEvent[] events = NetworkTablesJNI.readListenerQueue(m_serverInst, poller);
|
||||
|
||||
assertNotNull(events);
|
||||
assertEquals(1, events.length);
|
||||
assertEquals(handle, events[0].listener);
|
||||
assertTrue(events[0].connected);
|
||||
assertNotNull(events[0].connInfo);
|
||||
assertEquals(events[0].flags, NetworkTableEvent.kConnected);
|
||||
|
||||
// trigger a disconnect event
|
||||
m_clientInst.stopClient();
|
||||
@@ -97,12 +98,12 @@ class ConnectionListenerTest {
|
||||
} catch (InterruptedException ex) {
|
||||
fail("interrupted while waiting for queue");
|
||||
}
|
||||
events = NetworkTablesJNI.readConnectionListenerQueue(m_serverInst, poller);
|
||||
events = NetworkTablesJNI.readListenerQueue(m_serverInst, poller);
|
||||
|
||||
assertNotNull(events);
|
||||
assertEquals(1, events.length);
|
||||
assertEquals(handle, events[0].listener);
|
||||
assertFalse(events[0].connected);
|
||||
assertEquals(events[0].flags, NetworkTableEvent.kDisconnected);
|
||||
}
|
||||
|
||||
private static int threadedPort = 10001;
|
||||
@@ -111,15 +112,15 @@ class ConnectionListenerTest {
|
||||
@ValueSource(strings = {"127.0.0.1", "127.0.0.1 ", " 127.0.0.1 "})
|
||||
void testThreaded(String address) {
|
||||
m_serverInst.startServer("connectionlistenertest.json", address, 0, threadedPort);
|
||||
List<ConnectionNotification> events = new ArrayList<>();
|
||||
List<NetworkTableEvent> events = new ArrayList<>();
|
||||
final int handle =
|
||||
m_serverInst.addConnectionListener(
|
||||
false,
|
||||
e -> {
|
||||
synchronized (events) {
|
||||
events.add(e);
|
||||
}
|
||||
},
|
||||
false);
|
||||
});
|
||||
|
||||
// trigger a connect event
|
||||
m_clientInst.startClient4("client");
|
||||
@@ -146,11 +147,15 @@ class ConnectionListenerTest {
|
||||
fail("interrupted while waiting for queue");
|
||||
}
|
||||
|
||||
// wait for thread
|
||||
m_serverInst.waitForListenerQueue(1.0);
|
||||
|
||||
// get the event
|
||||
synchronized (events) {
|
||||
assertEquals(1, events.size());
|
||||
assertEquals(handle, events.get(0).listener);
|
||||
assertTrue(events.get(0).connected);
|
||||
assertNotNull(events.get(0).connInfo);
|
||||
assertEquals(events.get(0).flags, NetworkTableEvent.kConnected);
|
||||
events.clear();
|
||||
}
|
||||
|
||||
@@ -162,6 +167,9 @@ class ConnectionListenerTest {
|
||||
fail("interrupted while waiting for client to stop");
|
||||
}
|
||||
|
||||
// wait for thread
|
||||
m_serverInst.waitForListenerQueue(1.0);
|
||||
|
||||
// get the event
|
||||
try {
|
||||
assertFalse(WPIUtilJNI.waitForObjectTimeout(handle, 1.0));
|
||||
@@ -171,7 +179,8 @@ class ConnectionListenerTest {
|
||||
synchronized (events) {
|
||||
assertEquals(1, events.size());
|
||||
assertEquals(handle, events.get(0).listener);
|
||||
assertFalse(events.get(0).connected);
|
||||
assertNotNull(events.get(0).connInfo);
|
||||
assertEquals(events.get(0).flags, NetworkTableEvent.kDisconnected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ class LoggerTest {
|
||||
|
||||
@Test
|
||||
void addMessageTest() {
|
||||
List<LogMessage> msgs = new ArrayList<>();
|
||||
m_clientInst.addLogger(msgs::add, LogMessage.kInfo, 100);
|
||||
List<NetworkTableEvent> msgs = new ArrayList<>();
|
||||
m_clientInst.addLogger(LogMessage.kInfo, 100, msgs::add);
|
||||
|
||||
m_clientInst.startClient4("client");
|
||||
m_clientInst.setServer("127.0.0.1", 10000);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package edu.wpi.first.networktables;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import edu.wpi.first.util.WPIUtilJNI;
|
||||
@@ -35,8 +36,8 @@ class TopicListenerTest {
|
||||
m_clientInst.setServer("127.0.0.1", 10010);
|
||||
|
||||
// Use connection listener to ensure we've connected
|
||||
int poller = NetworkTablesJNI.createConnectionListenerPoller(m_clientInst.getHandle());
|
||||
NetworkTablesJNI.addPolledConnectionListener(poller, false);
|
||||
int poller = NetworkTablesJNI.createListenerPoller(m_clientInst.getHandle());
|
||||
NetworkTablesJNI.addListener(poller, m_clientInst.getHandle(), NetworkTableEvent.kConnected);
|
||||
try {
|
||||
if (WPIUtilJNI.waitForObjectTimeout(poller, 1.0)) {
|
||||
fail("client didn't connect to server");
|
||||
@@ -52,10 +53,9 @@ class TopicListenerTest {
|
||||
@Test
|
||||
void testPrefixNewRemote() {
|
||||
connect();
|
||||
final int poller = NetworkTablesJNI.createTopicListenerPoller(m_serverInst.getHandle());
|
||||
final int poller = NetworkTablesJNI.createListenerPoller(m_serverInst.getHandle());
|
||||
final int handle =
|
||||
NetworkTablesJNI.addPolledTopicListener(
|
||||
poller, new String[] {"/foo"}, TopicListenerFlags.kPublish);
|
||||
NetworkTablesJNI.addListener(poller, new String[] {"/foo"}, NetworkTableEvent.kPublish);
|
||||
|
||||
// Trigger an event
|
||||
m_clientInst.getEntry("/foo/bar").setDouble(1.0);
|
||||
@@ -75,13 +75,14 @@ class TopicListenerTest {
|
||||
Thread.currentThread().interrupt();
|
||||
fail("interrupted while waiting for signal");
|
||||
}
|
||||
TopicNotification[] events = NetworkTablesJNI.readTopicListenerQueue(m_serverInst, poller);
|
||||
NetworkTableEvent[] events = NetworkTablesJNI.readListenerQueue(m_serverInst, poller);
|
||||
|
||||
// Check the event
|
||||
assertEquals(1, events.length);
|
||||
assertEquals(handle, events[0].listener);
|
||||
assertEquals(m_serverInst.getTopic("/foo/bar"), events[0].info.getTopic());
|
||||
assertEquals("/foo/bar", events[0].info.name);
|
||||
assertEquals(TopicListenerFlags.kPublish, events[0].flags);
|
||||
assertNotNull(events[0].topicInfo);
|
||||
assertEquals(m_serverInst.getTopic("/foo/bar"), events[0].topicInfo.getTopic());
|
||||
assertEquals("/foo/bar", events[0].topicInfo.name);
|
||||
assertEquals(NetworkTableEvent.kPublish, events[0].flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,10 +49,10 @@ void ConnectionListenerTest::Connect(const char* address, unsigned int port3,
|
||||
|
||||
TEST_F(ConnectionListenerTest, Polled) {
|
||||
// set up the poller
|
||||
NT_ConnectionListenerPoller poller =
|
||||
nt::CreateConnectionListenerPoller(server_inst);
|
||||
NT_ListenerPoller poller = nt::CreateListenerPoller(server_inst);
|
||||
ASSERT_NE(poller, 0u);
|
||||
NT_ConnectionListener handle = nt::AddPolledConnectionListener(poller, false);
|
||||
NT_Listener handle =
|
||||
nt::AddPolledListener(poller, server_inst, nt::EventFlags::kConnection);
|
||||
ASSERT_NE(handle, 0u);
|
||||
|
||||
// trigger a connect event
|
||||
@@ -62,10 +62,11 @@ TEST_F(ConnectionListenerTest, Polled) {
|
||||
bool timed_out = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timed_out));
|
||||
ASSERT_FALSE(timed_out);
|
||||
auto result = nt::ReadConnectionListenerQueue(poller);
|
||||
auto result = nt::ReadListenerQueue(poller);
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_TRUE(result[0].connected);
|
||||
EXPECT_TRUE(result[0].GetConnectionInfo());
|
||||
EXPECT_EQ(result[0].flags, nt::EventFlags::kConnected);
|
||||
|
||||
// trigger a disconnect event
|
||||
nt::StopClient(client_inst);
|
||||
@@ -75,10 +76,11 @@ TEST_F(ConnectionListenerTest, Polled) {
|
||||
timed_out = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timed_out));
|
||||
ASSERT_FALSE(timed_out);
|
||||
result = nt::ReadConnectionListenerQueue(poller);
|
||||
result = nt::ReadListenerQueue(poller);
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_FALSE(result[0].connected);
|
||||
EXPECT_TRUE(result[0].GetConnectionInfo());
|
||||
EXPECT_EQ(result[0].flags, nt::EventFlags::kDisconnected);
|
||||
}
|
||||
|
||||
class ConnectionListenerVariantTest
|
||||
@@ -87,14 +89,12 @@ class ConnectionListenerVariantTest
|
||||
|
||||
TEST_P(ConnectionListenerVariantTest, Threaded) {
|
||||
wpi::mutex m;
|
||||
std::vector<nt::ConnectionNotification> result;
|
||||
auto handle = nt::AddConnectionListener(
|
||||
server_inst,
|
||||
[&](const nt::ConnectionNotification& event) {
|
||||
std::scoped_lock lock{m};
|
||||
result.push_back(event);
|
||||
},
|
||||
false);
|
||||
std::vector<nt::Event> result;
|
||||
auto handle = nt::AddListener(server_inst, nt::EventFlags::kConnection,
|
||||
[&](auto& event) {
|
||||
std::scoped_lock lock{m};
|
||||
result.push_back(event);
|
||||
});
|
||||
|
||||
// trigger a connect event
|
||||
Connect(GetParam().first, 0, 20001 + GetParam().second);
|
||||
@@ -108,7 +108,8 @@ TEST_P(ConnectionListenerVariantTest, Threaded) {
|
||||
std::scoped_lock lock{m};
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_TRUE(result[0].connected);
|
||||
EXPECT_TRUE(result[0].GetConnectionInfo());
|
||||
EXPECT_EQ(result[0].flags, nt::EventFlags::kConnected);
|
||||
result.clear();
|
||||
}
|
||||
|
||||
@@ -116,12 +117,16 @@ TEST_P(ConnectionListenerVariantTest, Threaded) {
|
||||
nt::StopClient(client_inst);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
// wait for thread
|
||||
nt::WaitForListenerQueue(server_inst, 1.0);
|
||||
|
||||
// get the event
|
||||
{
|
||||
std::scoped_lock lock{m};
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_FALSE(result[0].connected);
|
||||
EXPECT_TRUE(result[0].GetConnectionInfo());
|
||||
EXPECT_EQ(result[0].flags, nt::EventFlags::kDisconnected);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "LocalStorage.h"
|
||||
#include "MockListenerStorage.h"
|
||||
#include "MockLogger.h"
|
||||
#include "PubSubOptionsMatcher.h"
|
||||
#include "SpanMatcher.h"
|
||||
@@ -36,7 +37,8 @@ class LocalStorageTest : public ::testing::Test {
|
||||
::testing::StrictMock<net::MockNetworkStartupInterface> startup;
|
||||
::testing::StrictMock<net::MockNetworkInterface> network;
|
||||
wpi::MockLogger logger;
|
||||
LocalStorage storage{0, logger};
|
||||
MockListenerStorage listenerStorage;
|
||||
LocalStorage storage{0, listenerStorage, logger};
|
||||
NT_Topic fooTopic{storage.GetTopic("foo")};
|
||||
NT_Topic barTopic{storage.GetTopic("bar")};
|
||||
NT_Topic bazTopic{storage.GetTopic("baz")};
|
||||
@@ -63,6 +65,14 @@ TEST_F(LocalStorageTest, GetEntryEmptyName) {
|
||||
EXPECT_EQ(storage.GetEntry(""), 0u);
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, GetEntryCached) {
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"tocache"}}),
|
||||
IsPubSubOptions({})));
|
||||
|
||||
auto entry1 = storage.GetEntry("tocache");
|
||||
EXPECT_EQ(entry1, storage.GetEntry("tocache"));
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, GetTopicName) {
|
||||
EXPECT_EQ(storage.GetTopicName(fooTopic), "foo");
|
||||
EXPECT_EQ(storage.GetTopicName(barTopic), "bar");
|
||||
|
||||
96
ntcore/src/test/native/cpp/LoggerTest.cpp
Normal file
96
ntcore/src/test/native/cpp/LoggerTest.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include <wpi/Synchronization.h>
|
||||
|
||||
#include "Handle.h"
|
||||
#include "TestPrinters.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
class LoggerTest : public ::testing::Test {
|
||||
public:
|
||||
LoggerTest() : m_inst(nt::CreateInstance()) {}
|
||||
|
||||
~LoggerTest() override { nt::DestroyInstance(m_inst); }
|
||||
|
||||
void Generate();
|
||||
void Check(const std::vector<nt::Event>& events, NT_Listener handle,
|
||||
bool infoMsg, bool errMsg);
|
||||
|
||||
protected:
|
||||
NT_Inst m_inst;
|
||||
};
|
||||
|
||||
void LoggerTest::Generate() {
|
||||
// generate info message
|
||||
nt::StartClient4(m_inst, "");
|
||||
|
||||
// generate error message
|
||||
nt::Publish(nt::Handle(nt::Handle{m_inst}.GetInst(), 5, nt::Handle::kTopic),
|
||||
NT_DOUBLE, "");
|
||||
}
|
||||
|
||||
void LoggerTest::Check(const std::vector<nt::Event>& events, NT_Listener handle,
|
||||
bool infoMsg, bool errMsg) {
|
||||
size_t count = (infoMsg ? 1u : 0u) + (errMsg ? 1u : 0u);
|
||||
ASSERT_EQ(events.size(), count);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
ASSERT_EQ(events[i].listener, handle);
|
||||
ASSERT_EQ(events[i].flags & nt::EventFlags::kLogMessage,
|
||||
nt::EventFlags::kLogMessage);
|
||||
auto log = events[i].GetLogMessage();
|
||||
ASSERT_TRUE(log);
|
||||
if (infoMsg) {
|
||||
ASSERT_EQ(log->message, "starting network client");
|
||||
ASSERT_EQ(log->level, NT_LOG_INFO);
|
||||
infoMsg = false;
|
||||
} else if (errMsg) {
|
||||
ASSERT_EQ(log->message,
|
||||
"trying to publish invalid topic handle (386924549)");
|
||||
ASSERT_EQ(log->level, NT_LOG_ERROR);
|
||||
errMsg = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(LoggerTest, DefaultLogRange) {
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto handle =
|
||||
nt::AddPolledListener(poller, m_inst, nt::EventFlags::kLogMessage);
|
||||
|
||||
Generate();
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
|
||||
Check(events, handle, true, true);
|
||||
}
|
||||
|
||||
TEST_F(LoggerTest, InfoOnly) {
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto handle = nt::AddPolledLogger(poller, NT_LOG_INFO, NT_LOG_INFO);
|
||||
|
||||
Generate();
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
|
||||
Check(events, handle, true, false);
|
||||
}
|
||||
|
||||
TEST_F(LoggerTest, Error) {
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto handle = nt::AddPolledLogger(poller, NT_LOG_ERROR, 100);
|
||||
|
||||
Generate();
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
|
||||
Check(events, handle, false, true);
|
||||
}
|
||||
41
ntcore/src/test/native/cpp/MockListenerStorage.h
Normal file
41
ntcore/src/test/native/cpp/MockListenerStorage.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
#include "IListenerStorage.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class MockListenerStorage : public IListenerStorage {
|
||||
public:
|
||||
MOCK_METHOD(void, Activate,
|
||||
(NT_Listener listenerHandle, unsigned int mask,
|
||||
FinishEventFunc finishEvent),
|
||||
(override));
|
||||
MOCK_METHOD(void, Notify,
|
||||
(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
std::span<ConnectionInfo const* const> infos),
|
||||
(override));
|
||||
MOCK_METHOD(void, Notify,
|
||||
(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
std::span<const TopicInfo> infos),
|
||||
(override));
|
||||
MOCK_METHOD(void, Notify,
|
||||
(std::span<const NT_Listener> handles, unsigned int flags,
|
||||
NT_Topic topic, NT_Handle subentry, const Value& value),
|
||||
(override));
|
||||
MOCK_METHOD(void, Notify,
|
||||
(unsigned int flags, unsigned int level,
|
||||
std::string_view filename, unsigned int line,
|
||||
std::string_view message),
|
||||
(override));
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
@@ -20,44 +20,32 @@ void PrintTo(const json& val, ::std::ostream* os) {
|
||||
} // namespace wpi
|
||||
|
||||
namespace nt {
|
||||
#if 0
|
||||
void PrintTo(const EntryNotification& event, std::ostream* os) {
|
||||
*os << "EntryNotification{listener=";
|
||||
|
||||
void PrintTo(const Event& event, std::ostream* os) {
|
||||
*os << "Event{listener=";
|
||||
PrintTo(Handle{event.listener}, os);
|
||||
*os << ", entry=";
|
||||
PrintTo(Handle{event.entry}, os);
|
||||
*os << ", name=\"" << event.name << "\", flags=" << event.flags << ", value=";
|
||||
PrintTo(event.value, os);
|
||||
*os << ", flags=" << event.flags;
|
||||
// *os << ", name=\"" << event.name << "\", flags=" << event.flags
|
||||
// << "value=";
|
||||
// PrintTo(event.value, os);
|
||||
*os << '}';
|
||||
}
|
||||
#endif
|
||||
|
||||
void PrintTo(const Handle& handle, std::ostream* os) {
|
||||
*os << "Handle{";
|
||||
switch (handle.GetType()) {
|
||||
case Handle::kConnectionListener:
|
||||
*os << "kConnectionListener";
|
||||
case Handle::kListener:
|
||||
*os << "kListener";
|
||||
break;
|
||||
case Handle::kConnectionListenerPoller:
|
||||
*os << "kConnectionListenerPoller";
|
||||
case Handle::kListenerPoller:
|
||||
*os << "kListenerPoller";
|
||||
break;
|
||||
case Handle::kEntry:
|
||||
*os << "kEntry";
|
||||
break;
|
||||
case Handle::kEntryListener:
|
||||
*os << "kEntryListener";
|
||||
break;
|
||||
case Handle::kEntryListenerPoller:
|
||||
*os << "kEntryListenerPoller";
|
||||
break;
|
||||
case Handle::kInstance:
|
||||
*os << "kInstance";
|
||||
break;
|
||||
case Handle::kLogger:
|
||||
*os << "kLogger";
|
||||
break;
|
||||
case Handle::kLoggerPoller:
|
||||
*os << "kLoggerPoller";
|
||||
break;
|
||||
case Handle::kTopic:
|
||||
*os << "kTopic";
|
||||
break;
|
||||
|
||||
@@ -49,12 +49,12 @@ struct ClientMessage;
|
||||
struct ServerMessage;
|
||||
} // namespace net
|
||||
|
||||
// class EntryNotification;
|
||||
class Event;
|
||||
class Handle;
|
||||
class PubSubOptions;
|
||||
class Value;
|
||||
|
||||
// void PrintTo(const EntryNotification& event, std::ostream* os);
|
||||
void PrintTo(const Event& event, std::ostream* os);
|
||||
void PrintTo(const Handle& handle, std::ostream* os);
|
||||
void PrintTo(const net3::Message3& msg, std::ostream* os);
|
||||
void PrintTo(const net::ClientMessage& msg, std::ostream* os);
|
||||
|
||||
@@ -19,16 +19,16 @@ class TopicListenerTest : public ::testing::Test {
|
||||
TopicListenerTest()
|
||||
: m_serverInst(nt::CreateInstance()), m_clientInst(nt::CreateInstance()) {
|
||||
#if 0
|
||||
nt::AddLogger(server_inst,
|
||||
[](const nt::LogMessage& msg) {
|
||||
std::fprintf(stderr, "SERVER: %s\n", msg.message.c_str());
|
||||
},
|
||||
0, UINT_MAX);
|
||||
nt::AddLogger(client_inst,
|
||||
[](const nt::LogMessage& msg) {
|
||||
std::fprintf(stderr, "CLIENT: %s\n", msg.message.c_str());
|
||||
},
|
||||
0, UINT_MAX);
|
||||
nt::AddLogger(m_serverInst, 0, UINT_MAX, [](auto& event) {
|
||||
if (auto msg = event.GetLogMessage()) {
|
||||
std::fprintf(stderr, "SERVER: %s\n", msg->message.c_str());
|
||||
}
|
||||
});
|
||||
nt::AddLogger(m_clientInst, 0, UINT_MAX, [](auto& event) {
|
||||
if (auto msg = event.GetLogMessage()) {
|
||||
std::fprintf(stderr, "CLIENT: %s\n", msg.message.c_str());
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -39,9 +39,8 @@ class TopicListenerTest : public ::testing::Test {
|
||||
|
||||
void Connect(unsigned int port);
|
||||
static void PublishTopics(NT_Inst inst);
|
||||
void CheckEvents(const std::vector<nt::TopicNotification>& events,
|
||||
NT_TopicListener handle, unsigned int flags,
|
||||
std::string_view topicName = "/foo/bar");
|
||||
void CheckEvents(const std::vector<nt::Event>& events, NT_Listener handle,
|
||||
unsigned int flags, std::string_view topicName = "/foo/bar");
|
||||
|
||||
protected:
|
||||
NT_Inst m_serverInst;
|
||||
@@ -54,9 +53,8 @@ void TopicListenerTest::Connect(unsigned int port) {
|
||||
nt::SetServer(m_clientInst, "127.0.0.1", port);
|
||||
|
||||
// Use connection listener to ensure we've connected
|
||||
NT_ConnectionListenerPoller poller =
|
||||
nt::CreateConnectionListenerPoller(m_clientInst);
|
||||
nt::AddPolledConnectionListener(poller, false);
|
||||
NT_ListenerPoller poller = nt::CreateListenerPoller(m_clientInst);
|
||||
nt::AddPolledListener(poller, m_clientInst, nt::EventFlags::kConnected);
|
||||
bool timedOut = false;
|
||||
if (!wpi::WaitForObject(poller, 1.0, &timedOut)) {
|
||||
FAIL() << "client didn't connect to server";
|
||||
@@ -69,27 +67,29 @@ void TopicListenerTest::PublishTopics(NT_Inst inst) {
|
||||
nt::Publish(nt::GetTopic(inst, "/baz"), NT_DOUBLE, "double");
|
||||
}
|
||||
|
||||
void TopicListenerTest::CheckEvents(
|
||||
const std::vector<nt::TopicNotification>& events, NT_TopicListener handle,
|
||||
unsigned int flags, std::string_view topicName) {
|
||||
void TopicListenerTest::CheckEvents(const std::vector<nt::Event>& events,
|
||||
NT_Listener handle, unsigned int flags,
|
||||
std::string_view topicName) {
|
||||
ASSERT_EQ(events.size(), 1u);
|
||||
ASSERT_EQ(events[0].listener, handle);
|
||||
ASSERT_EQ(events[0].info.topic, nt::GetTopic(m_serverInst, topicName));
|
||||
ASSERT_EQ(events[0].info.name, topicName);
|
||||
ASSERT_EQ(events[0].flags, flags);
|
||||
auto topicInfo = events[0].GetTopicInfo();
|
||||
ASSERT_TRUE(topicInfo);
|
||||
ASSERT_EQ(topicInfo->topic, nt::GetTopic(m_serverInst, topicName));
|
||||
ASSERT_EQ(topicInfo->name, topicName);
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, TopicNewLocal) {
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledTopicListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), NT_TOPIC_NOTIFY_PUBLISH);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), nt::EventFlags::kPublish);
|
||||
|
||||
PublishTopics(m_serverInst);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_PUBLISH, "/foo");
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle, nt::EventFlags::kPublish, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, DISABLED_TopicNewRemote) {
|
||||
@@ -97,9 +97,9 @@ TEST_F(TopicListenerTest, DISABLED_TopicNewRemote) {
|
||||
if (HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledTopicListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), NT_TOPIC_NOTIFY_PUBLISH);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), nt::EventFlags::kPublish);
|
||||
|
||||
PublishTopics(m_clientInst);
|
||||
|
||||
@@ -108,54 +108,54 @@ TEST_F(TopicListenerTest, DISABLED_TopicNewRemote) {
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_PUBLISH, "/foo");
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle, nt::EventFlags::kPublish, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, TopicPublishImm) {
|
||||
PublishTopics(m_serverInst);
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledTopicListener(
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"),
|
||||
NT_TOPIC_NOTIFY_PUBLISH | NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
nt::EventFlags::kPublish | nt::EventFlags::kImmediate);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle,
|
||||
NT_TOPIC_NOTIFY_PUBLISH | NT_TOPIC_NOTIFY_IMMEDIATE, "/foo");
|
||||
nt::EventFlags::kPublish | nt::EventFlags::kImmediate, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, TopicUnpublishPropsImm) {
|
||||
PublishTopics(m_serverInst);
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
nt::AddPolledTopicListener(poller, nt::GetTopic(m_serverInst, "/foo"),
|
||||
NT_TOPIC_NOTIFY_UNPUBLISH |
|
||||
NT_TOPIC_NOTIFY_PROPERTIES |
|
||||
NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
nt::AddPolledListener(poller, nt::GetTopic(m_serverInst, "/foo"),
|
||||
nt::EventFlags::kUnpublish |
|
||||
nt::EventFlags::kProperties |
|
||||
nt::EventFlags::kImmediate);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_FALSE(wpi::WaitForObject(poller, 0.02, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
ASSERT_TRUE(events.empty());
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, TopicUnpublishLocal) {
|
||||
auto topic = nt::GetTopic(m_serverInst, "/foo");
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle =
|
||||
nt::AddPolledTopicListener(poller, topic, NT_TOPIC_NOTIFY_UNPUBLISH);
|
||||
nt::AddPolledListener(poller, topic, nt::EventFlags::kUnpublish);
|
||||
|
||||
auto pub = nt::Publish(topic, NT_DOUBLE, "double");
|
||||
nt::Unpublish(pub);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_UNPUBLISH, "/foo");
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle, nt::EventFlags::kUnpublish, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, DISABLED_TopicUnpublishRemote) {
|
||||
@@ -163,9 +163,9 @@ TEST_F(TopicListenerTest, DISABLED_TopicUnpublishRemote) {
|
||||
if (HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledTopicListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), NT_TOPIC_NOTIFY_UNPUBLISH);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), nt::EventFlags::kUnpublish);
|
||||
|
||||
auto pub =
|
||||
nt::Publish(nt::GetTopic(m_clientInst, "/foo"), NT_DOUBLE, "double");
|
||||
@@ -179,23 +179,23 @@ TEST_F(TopicListenerTest, DISABLED_TopicUnpublishRemote) {
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_UNPUBLISH, "/foo");
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle, nt::EventFlags::kUnpublish, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, TopicPropertiesLocal) {
|
||||
auto topic = nt::GetTopic(m_serverInst, "/foo");
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle =
|
||||
nt::AddPolledTopicListener(poller, topic, NT_TOPIC_NOTIFY_PROPERTIES);
|
||||
nt::AddPolledListener(poller, topic, nt::EventFlags::kProperties);
|
||||
|
||||
nt::SetTopicProperty(topic, "foo", 5);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_PROPERTIES, "/foo");
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle, nt::EventFlags::kProperties, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, DISABLED_TopicPropertiesRemote) {
|
||||
@@ -206,9 +206,9 @@ TEST_F(TopicListenerTest, DISABLED_TopicPropertiesRemote) {
|
||||
// the topic needs to actually exist
|
||||
nt::Publish(nt::GetTopic(m_serverInst, "/foo"), NT_BOOLEAN, "boolean");
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledTopicListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), NT_TOPIC_NOTIFY_PROPERTIES);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), nt::EventFlags::kProperties);
|
||||
nt::FlushLocal(m_serverInst);
|
||||
|
||||
nt::SetTopicProperty(nt::GetTopic(m_clientInst, "/foo"), "foo", 5);
|
||||
@@ -217,21 +217,21 @@ TEST_F(TopicListenerTest, DISABLED_TopicPropertiesRemote) {
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_PROPERTIES, "/foo");
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle, nt::EventFlags::kProperties, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, PrefixPublishLocal) {
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle =
|
||||
nt::AddPolledTopicListener(poller, {{"/foo/"}}, NT_TOPIC_NOTIFY_PUBLISH);
|
||||
nt::AddPolledListener(poller, {{"/foo/"}}, nt::EventFlags::kPublish);
|
||||
|
||||
PublishTopics(m_serverInst);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_PUBLISH);
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle, nt::EventFlags::kPublish);
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, DISABLED_PrefixPublishRemote) {
|
||||
@@ -239,9 +239,9 @@ TEST_F(TopicListenerTest, DISABLED_PrefixPublishRemote) {
|
||||
if (HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle =
|
||||
nt::AddPolledTopicListener(poller, {{"/foo/"}}, NT_TOPIC_NOTIFY_PUBLISH);
|
||||
nt::AddPolledListener(poller, {{"/foo/"}}, nt::EventFlags::kPublish);
|
||||
|
||||
PublishTopics(m_clientInst);
|
||||
|
||||
@@ -250,35 +250,36 @@ TEST_F(TopicListenerTest, DISABLED_PrefixPublishRemote) {
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_PUBLISH);
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle, nt::EventFlags::kPublish);
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, PrefixPublishImm) {
|
||||
PublishTopics(m_serverInst);
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledTopicListener(
|
||||
poller, {{"/foo/"}}, NT_TOPIC_NOTIFY_PUBLISH | NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledListener(
|
||||
poller, {{"/foo/"}},
|
||||
nt::EventFlags::kPublish | nt::EventFlags::kImmediate);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
CheckEvents(events, handle,
|
||||
NT_TOPIC_NOTIFY_PUBLISH | NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
nt::EventFlags::kPublish | nt::EventFlags::kImmediate);
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, PrefixUnpublishPropsImm) {
|
||||
PublishTopics(m_serverInst);
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
nt::AddPolledTopicListener(poller, {{"/foo/"}},
|
||||
NT_TOPIC_NOTIFY_UNPUBLISH |
|
||||
NT_TOPIC_NOTIFY_PROPERTIES |
|
||||
NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
auto poller = nt::CreateListenerPoller(m_serverInst);
|
||||
nt::AddPolledListener(poller, {{"/foo/"}},
|
||||
nt::EventFlags::kUnpublish |
|
||||
nt::EventFlags::kProperties |
|
||||
nt::EventFlags::kImmediate);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_FALSE(wpi::WaitForObject(poller, 0.02, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
auto events = nt::ReadListenerQueue(poller);
|
||||
ASSERT_TRUE(events.empty());
|
||||
}
|
||||
|
||||
@@ -34,12 +34,12 @@ TEST_F(ValueListenerTest, MultiPollSub) {
|
||||
auto pub = nt::Publish(topic, NT_DOUBLE, "double");
|
||||
auto sub = nt::Subscribe(topic, NT_DOUBLE, "double");
|
||||
|
||||
auto poller1 = nt::CreateValueListenerPoller(m_inst);
|
||||
auto poller2 = nt::CreateValueListenerPoller(m_inst);
|
||||
auto poller3 = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h1 = nt::AddPolledValueListener(poller1, sub, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto h2 = nt::AddPolledValueListener(poller2, sub, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto h3 = nt::AddPolledValueListener(poller3, sub, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto poller1 = nt::CreateListenerPoller(m_inst);
|
||||
auto poller2 = nt::CreateListenerPoller(m_inst);
|
||||
auto poller3 = nt::CreateListenerPoller(m_inst);
|
||||
auto h1 = nt::AddPolledListener(poller1, sub, nt::EventFlags::kValueLocal);
|
||||
auto h2 = nt::AddPolledListener(poller2, sub, nt::EventFlags::kValueLocal);
|
||||
auto h3 = nt::AddPolledListener(poller3, sub, nt::EventFlags::kValueLocal);
|
||||
|
||||
nt::SetDouble(pub, 0);
|
||||
|
||||
@@ -50,30 +50,36 @@ TEST_F(ValueListenerTest, MultiPollSub) {
|
||||
ASSERT_FALSE(timedOut);
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller3, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results1 = nt::ReadValueListenerQueue(poller1);
|
||||
auto results2 = nt::ReadValueListenerQueue(poller2);
|
||||
auto results3 = nt::ReadValueListenerQueue(poller3);
|
||||
auto results1 = nt::ReadListenerQueue(poller1);
|
||||
auto results2 = nt::ReadListenerQueue(poller2);
|
||||
auto results3 = nt::ReadListenerQueue(poller3);
|
||||
|
||||
ASSERT_EQ(results1.size(), 1u);
|
||||
EXPECT_EQ(results1[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results1[0].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results1[0].listener, h1);
|
||||
EXPECT_EQ(results1[0].subentry, sub);
|
||||
EXPECT_EQ(results1[0].topic, topic);
|
||||
EXPECT_EQ(results1[0].value, nt::Value::MakeDouble(0.0));
|
||||
auto valueData = results1[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub);
|
||||
EXPECT_EQ(valueData->topic, topic);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
ASSERT_EQ(results2.size(), 1u);
|
||||
EXPECT_EQ(results2[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results2[0].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results2[0].listener, h2);
|
||||
EXPECT_EQ(results2[0].subentry, sub);
|
||||
EXPECT_EQ(results2[0].topic, topic);
|
||||
EXPECT_EQ(results2[0].value, nt::Value::MakeDouble(0.0));
|
||||
valueData = results2[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub);
|
||||
EXPECT_EQ(valueData->topic, topic);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
ASSERT_EQ(results3.size(), 1u);
|
||||
EXPECT_EQ(results3[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results3[0].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results3[0].listener, h3);
|
||||
EXPECT_EQ(results3[0].subentry, sub);
|
||||
EXPECT_EQ(results3[0].topic, topic);
|
||||
EXPECT_EQ(results3[0].value, nt::Value::MakeDouble(0.0));
|
||||
valueData = results3[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub);
|
||||
EXPECT_EQ(valueData->topic, topic);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollMultiSub) {
|
||||
@@ -82,29 +88,33 @@ TEST_F(ValueListenerTest, PollMultiSub) {
|
||||
auto sub1 = nt::Subscribe(topic, NT_DOUBLE, "double");
|
||||
auto sub2 = nt::Subscribe(topic, NT_DOUBLE, "double");
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h1 = nt::AddPolledValueListener(poller, sub1, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto h2 = nt::AddPolledValueListener(poller, sub2, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto h1 = nt::AddPolledListener(poller, sub1, nt::EventFlags::kValueLocal);
|
||||
auto h2 = nt::AddPolledListener(poller, sub2, nt::EventFlags::kValueLocal);
|
||||
|
||||
nt::SetDouble(pub, 0);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
auto results = nt::ReadListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 2u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[0].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[0].listener, h1);
|
||||
EXPECT_EQ(results[0].subentry, sub1);
|
||||
EXPECT_EQ(results[0].topic, topic);
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
auto valueData = results[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub1);
|
||||
EXPECT_EQ(valueData->topic, topic);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
EXPECT_EQ(results[1].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[1].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[1].listener, h2);
|
||||
EXPECT_EQ(results[1].subentry, sub2);
|
||||
EXPECT_EQ(results[1].topic, topic);
|
||||
EXPECT_EQ(results[1].value, nt::Value::MakeDouble(0.0));
|
||||
valueData = results[1].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub2);
|
||||
EXPECT_EQ(valueData->topic, topic);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollMultiSubTopic) {
|
||||
@@ -115,9 +125,9 @@ TEST_F(ValueListenerTest, PollMultiSubTopic) {
|
||||
auto sub1 = nt::Subscribe(topic1, NT_DOUBLE, "double");
|
||||
auto sub2 = nt::Subscribe(topic2, NT_DOUBLE, "double");
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h1 = nt::AddPolledValueListener(poller, sub1, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto h2 = nt::AddPolledValueListener(poller, sub2, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto h1 = nt::AddPolledListener(poller, sub1, nt::EventFlags::kValueLocal);
|
||||
auto h2 = nt::AddPolledListener(poller, sub2, nt::EventFlags::kValueLocal);
|
||||
|
||||
nt::SetDouble(pub1, 0);
|
||||
nt::SetDouble(pub2, 1);
|
||||
@@ -125,20 +135,24 @@ TEST_F(ValueListenerTest, PollMultiSubTopic) {
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
auto results = nt::ReadListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 2u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[0].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[0].listener, h1);
|
||||
EXPECT_EQ(results[0].subentry, sub1);
|
||||
EXPECT_EQ(results[0].topic, topic1);
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
auto valueData = results[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub1);
|
||||
EXPECT_EQ(valueData->topic, topic1);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
EXPECT_EQ(results[1].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[1].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[1].listener, h2);
|
||||
EXPECT_EQ(results[1].subentry, sub2);
|
||||
EXPECT_EQ(results[1].topic, topic2);
|
||||
EXPECT_EQ(results[1].value, nt::Value::MakeDouble(1.0));
|
||||
valueData = results[1].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub2);
|
||||
EXPECT_EQ(valueData->topic, topic2);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(1.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollSubMultiple) {
|
||||
@@ -148,8 +162,8 @@ TEST_F(ValueListenerTest, PollSubMultiple) {
|
||||
auto pub2 = nt::Publish(topic2, NT_DOUBLE, "double");
|
||||
auto sub = nt::SubscribeMultiple(m_inst, {{"foo"}});
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledValueListener(poller, sub, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledListener(poller, sub, nt::EventFlags::kValueLocal);
|
||||
|
||||
nt::SetDouble(pub1, 0);
|
||||
nt::SetDouble(pub2, 1);
|
||||
@@ -157,90 +171,139 @@ TEST_F(ValueListenerTest, PollSubMultiple) {
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
auto results = nt::ReadListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 2u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[0].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[0].listener, h);
|
||||
EXPECT_EQ(results[0].subentry, sub);
|
||||
EXPECT_EQ(results[0].topic, topic1);
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
auto valueData = results[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub);
|
||||
EXPECT_EQ(valueData->topic, topic1);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
EXPECT_EQ(results[1].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[1].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[1].listener, h);
|
||||
EXPECT_EQ(results[1].subentry, sub);
|
||||
EXPECT_EQ(results[1].topic, topic2);
|
||||
EXPECT_EQ(results[1].value, nt::Value::MakeDouble(1.0));
|
||||
valueData = results[1].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub);
|
||||
EXPECT_EQ(valueData->topic, topic2);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(1.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollSubPrefixCreated) {
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto h =
|
||||
nt::AddPolledListener(poller, {{"foo"}}, nt::EventFlags::kValueLocal);
|
||||
|
||||
auto topic1 = nt::GetTopic(m_inst, "foo/1");
|
||||
auto topic2 = nt::GetTopic(m_inst, "foo/2");
|
||||
auto topic3 = nt::GetTopic(m_inst, "bar/3");
|
||||
auto pub1 = nt::Publish(topic1, NT_DOUBLE, "double");
|
||||
auto pub2 = nt::Publish(topic2, NT_DOUBLE, "double");
|
||||
auto pub3 = nt::Publish(topic3, NT_DOUBLE, "double");
|
||||
|
||||
nt::SetDouble(pub1, 0);
|
||||
nt::SetDouble(pub2, 1);
|
||||
nt::SetDouble(pub3, 1);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 2u);
|
||||
EXPECT_EQ(results[0].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[0].listener, h);
|
||||
auto valueData = results[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->topic, topic1);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
EXPECT_EQ(results[1].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[1].listener, h);
|
||||
valueData = results[1].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->topic, topic2);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(1.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollEntry) {
|
||||
auto entry = nt::GetEntry(m_inst, "foo");
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledValueListener(poller, entry, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledListener(poller, entry, nt::EventFlags::kValueLocal);
|
||||
|
||||
ASSERT_TRUE(nt::SetDouble(entry, 0));
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
auto results = nt::ReadListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 1u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[0].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[0].listener, h);
|
||||
EXPECT_EQ(results[0].subentry, entry);
|
||||
EXPECT_EQ(results[0].topic, nt::GetTopic(m_inst, "foo"));
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
auto valueData = results[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, entry);
|
||||
EXPECT_EQ(valueData->topic, nt::GetTopic(m_inst, "foo"));
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollImmediate) {
|
||||
auto entry = nt::GetEntry(m_inst, "foo");
|
||||
ASSERT_TRUE(nt::SetDouble(entry, 0));
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledValueListener(
|
||||
poller, entry, NT_VALUE_NOTIFY_LOCAL | NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledListener(
|
||||
poller, entry, nt::EventFlags::kValueLocal | nt::EventFlags::kImmediate);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
auto results = nt::ReadListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 1u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
EXPECT_EQ(results[0].flags &
|
||||
(nt::EventFlags::kValueLocal | nt::EventFlags::kImmediate),
|
||||
nt::EventFlags::kValueLocal | nt::EventFlags::kImmediate);
|
||||
EXPECT_EQ(results[0].listener, h);
|
||||
EXPECT_EQ(results[0].subentry, entry);
|
||||
EXPECT_EQ(results[0].topic, nt::GetTopic(m_inst, "foo"));
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
auto valueData = results[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, entry);
|
||||
EXPECT_EQ(valueData->topic, nt::GetTopic(m_inst, "foo"));
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollImmediateNoValue) {
|
||||
auto entry = nt::GetEntry(m_inst, "foo");
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledValueListener(
|
||||
poller, entry, NT_VALUE_NOTIFY_LOCAL | NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledListener(
|
||||
poller, entry, nt::EventFlags::kValueLocal | nt::EventFlags::kImmediate);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_FALSE(wpi::WaitForObject(poller, 0.02, &timedOut));
|
||||
ASSERT_TRUE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
auto results = nt::ReadListenerQueue(poller);
|
||||
ASSERT_TRUE(results.empty());
|
||||
|
||||
// now set a value
|
||||
ASSERT_TRUE(nt::SetDouble(entry, 0));
|
||||
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
results = nt::ReadValueListenerQueue(poller);
|
||||
results = nt::ReadListenerQueue(poller);
|
||||
ASSERT_FALSE(timedOut);
|
||||
|
||||
ASSERT_EQ(results.size(), 1u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[0].flags, nt::EventFlags::kValueLocal);
|
||||
EXPECT_EQ(results[0].listener, h);
|
||||
EXPECT_EQ(results[0].subentry, entry);
|
||||
EXPECT_EQ(results[0].topic, nt::GetTopic(m_inst, "foo"));
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
auto valueData = results[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, entry);
|
||||
EXPECT_EQ(valueData->topic, nt::GetTopic(m_inst, "foo"));
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollImmediateSubMultiple) {
|
||||
@@ -252,27 +315,35 @@ TEST_F(ValueListenerTest, PollImmediateSubMultiple) {
|
||||
nt::SetDouble(pub1, 0);
|
||||
nt::SetDouble(pub2, 1);
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledValueListener(
|
||||
poller, sub, NT_VALUE_NOTIFY_LOCAL | NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
auto poller = nt::CreateListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledListener(
|
||||
poller, sub, nt::EventFlags::kValueLocal | nt::EventFlags::kImmediate);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
auto results = nt::ReadListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 2u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
EXPECT_EQ(results[0].flags &
|
||||
(nt::EventFlags::kValueLocal | nt::EventFlags::kImmediate),
|
||||
nt::EventFlags::kValueLocal | nt::EventFlags::kImmediate);
|
||||
EXPECT_EQ(results[0].listener, h);
|
||||
EXPECT_EQ(results[0].subentry, sub);
|
||||
EXPECT_EQ(results[0].topic, topic1);
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
auto valueData = results[0].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub);
|
||||
EXPECT_EQ(valueData->topic, topic1);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
EXPECT_EQ(results[1].flags, NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
EXPECT_EQ(results[1].flags &
|
||||
(nt::EventFlags::kValueLocal | nt::EventFlags::kImmediate),
|
||||
nt::EventFlags::kValueLocal | nt::EventFlags::kImmediate);
|
||||
EXPECT_EQ(results[1].listener, h);
|
||||
EXPECT_EQ(results[1].subentry, sub);
|
||||
EXPECT_EQ(results[1].topic, topic2);
|
||||
EXPECT_EQ(results[1].value, nt::Value::MakeDouble(1.0));
|
||||
valueData = results[1].GetValueEventData();
|
||||
ASSERT_TRUE(valueData);
|
||||
EXPECT_EQ(valueData->subentry, sub);
|
||||
EXPECT_EQ(valueData->topic, topic2);
|
||||
EXPECT_EQ(valueData->value, nt::Value::MakeDouble(1.0));
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
@@ -8,13 +8,12 @@
|
||||
#include "ntcore.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
nt::AddLogger(
|
||||
nt::GetDefaultInstance(),
|
||||
[](const nt::LogMessage& msg) {
|
||||
std::fputs(msg.message.c_str(), stderr);
|
||||
std::fputc('\n', stderr);
|
||||
},
|
||||
0, UINT_MAX);
|
||||
nt::AddLogger(nt::GetDefaultInstance(), 0, UINT_MAX, [](auto& event) {
|
||||
if (auto msg = event.GetLogMessage()) {
|
||||
std::fputs(msg->message.c_str(), stderr);
|
||||
std::fputc('\n', stderr);
|
||||
}
|
||||
});
|
||||
::testing::InitGoogleMock(&argc, argv);
|
||||
int ret = RUN_ALL_TESTS();
|
||||
return ret;
|
||||
|
||||
@@ -1,37 +1,25 @@
|
||||
NT_AddConnectionListener
|
||||
NT_AddListener
|
||||
NT_AddListenerMultiple
|
||||
NT_AddListenerSingle
|
||||
NT_AddLogger
|
||||
NT_AddPolledConnectionListener
|
||||
NT_AddPolledListener
|
||||
NT_AddPolledListenerMultiple
|
||||
NT_AddPolledListenerSingle
|
||||
NT_AddPolledLogger
|
||||
NT_AddPolledTopicListener
|
||||
NT_AddPolledTopicListenerMultiple
|
||||
NT_AddPolledTopicListenerSingle
|
||||
NT_AddPolledValueListener
|
||||
NT_AddTopicListener
|
||||
NT_AddTopicListenerMultiple
|
||||
NT_AddTopicListenerSingle
|
||||
NT_AddValueListener
|
||||
NT_AllocateBooleanArray
|
||||
NT_AllocateCharArray
|
||||
NT_AllocateDoubleArray
|
||||
NT_AllocateFloatArray
|
||||
NT_AllocateIntegerArray
|
||||
NT_AllocateStringArray
|
||||
NT_CreateConnectionListenerPoller
|
||||
NT_CreateInstance
|
||||
NT_CreateLoggerPoller
|
||||
NT_CreateTopicListenerPoller
|
||||
NT_CreateValueListenerPoller
|
||||
NT_CreateListenerPoller
|
||||
NT_DeleteTopicProperty
|
||||
NT_DestroyConnectionListenerPoller
|
||||
NT_DestroyInstance
|
||||
NT_DestroyLoggerPoller
|
||||
NT_DestroyTopicListenerPoller
|
||||
NT_DestroyValueListenerPoller
|
||||
NT_DestroyListenerPoller
|
||||
NT_DisposeConnectionInfoArray
|
||||
NT_DisposeConnectionNotification
|
||||
NT_DisposeConnectionNotificationArray
|
||||
NT_DisposeLogMessage
|
||||
NT_DisposeLogMessageArray
|
||||
NT_DisposeEvent
|
||||
NT_DisposeEventArray
|
||||
NT_DisposeString
|
||||
NT_DisposeTimestampedBoolean
|
||||
NT_DisposeTimestampedBooleanArray
|
||||
@@ -46,12 +34,8 @@ NT_DisposeTimestampedString
|
||||
NT_DisposeTimestampedStringArray
|
||||
NT_DisposeTopicInfo
|
||||
NT_DisposeTopicInfoArray
|
||||
NT_DisposeTopicNotification
|
||||
NT_DisposeTopicNotificationArray
|
||||
NT_DisposeValue
|
||||
NT_DisposeValueArray
|
||||
NT_DisposeValueNotification
|
||||
NT_DisposeValueNotificationArray
|
||||
NT_Flush
|
||||
NT_FlushLocal
|
||||
NT_FreeBooleanArray
|
||||
@@ -149,8 +133,7 @@ NT_IsConnected
|
||||
NT_Now
|
||||
NT_Publish
|
||||
NT_PublishEx
|
||||
NT_ReadConnectionListenerQueue
|
||||
NT_ReadLoggerQueue
|
||||
NT_ReadListenerQueue
|
||||
NT_ReadQueueBoolean
|
||||
NT_ReadQueueBooleanArray
|
||||
NT_ReadQueueDouble
|
||||
@@ -167,14 +150,9 @@ NT_ReadQueueValuesBoolean
|
||||
NT_ReadQueueValuesDouble
|
||||
NT_ReadQueueValuesFloat
|
||||
NT_ReadQueueValuesInteger
|
||||
NT_ReadTopicListenerQueue
|
||||
NT_ReadValueListenerQueue
|
||||
NT_Release
|
||||
NT_ReleaseEntry
|
||||
NT_RemoveConnectionListener
|
||||
NT_RemoveLogger
|
||||
NT_RemoveTopicListener
|
||||
NT_RemoveValueListener
|
||||
NT_RemoveListener
|
||||
NT_SetBoolean
|
||||
NT_SetBooleanArray
|
||||
NT_SetDefaultBoolean
|
||||
@@ -220,6 +198,7 @@ NT_StopServer
|
||||
NT_Subscribe
|
||||
NT_Unpublish
|
||||
NT_Unsubscribe
|
||||
NT_WaitForListenerQueue
|
||||
WPI_CreateEvent
|
||||
WPI_CreateSemaphore
|
||||
WPI_CreateSignalObject
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user