Creating a Client-side Program

If all you need to do is have your robot program communicate with a COTS coprocessor or a dashboard running on the Driver Station laptop, then the previous examples of writing robot programs are sufficient. But if you would like to write some custom client code that would run on the drivers station or on a coprocessor then you need to know how to build NetworkTables programs for those (non-roboRIO) platforms.

基本的客户端程序如下例所示。

import edu.wpi.first.networktables.DoubleSubscriber;
import edu.wpi.first.networktables.NetworkTable;
import edu.wpi.first.networktables.NetworkTableInstance;
import edu.wpi.first.networktables.NetworkTablesJNI;
import edu.wpi.first.util.CombinedRuntimeLoader;

import java.io.IOException;

import edu.wpi.first.cscore.CameraServerJNI;
import edu.wpi.first.math.WPIMathJNI;
import edu.wpi.first.util.WPIUtilJNI;


public class Program {
    public static void main(String[] args) throws IOException {
        NetworkTablesJNI.Helper.setExtractOnStaticLoad(false);
        WPIUtilJNI.Helper.setExtractOnStaticLoad(false);
        WPIMathJNI.Helper.setExtractOnStaticLoad(false);
        CameraServerJNI.Helper.setExtractOnStaticLoad(false);

        CombinedRuntimeLoader.loadLibraries(Program.class, "wpiutiljni", "wpimathjni", "ntcorejni",
                "cscorejnicvstatic");
        new Program().run();
    }

    public void run() {
        NetworkTableInstance inst = NetworkTableInstance.getDefault();
        NetworkTable table = inst.getTable("datatable");
        DoubleSubscriber xSub = table.getDoubleTopic("x").subscribe(0.0);
        DoubleSubscriber ySub = table.getDoubleTopic("y").subscribe(0.0);
        inst.startClient4("example client");
        inst.setServer("localhost"); // where TEAM=190, 294, etc, or use inst.setServer("hostname") or similar
        inst.startDSClient(); // recommended if running on DS computer; this gets the robot IP from the DS
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                System.out.println("interrupted");
                return;
            }
            double x = xSub.get();
            double y = ySub.get();
            System.out.println("X: " + x + " Y: " + y);
        }
    }
}
#include <chrono>
#include <thread>
#include <fmt/format.h>
#include <networktables/NetworkTableInstance.h>
#include <networktables/NetworkTable.h>
#include <networktables/DoubleTopic.h>

int main() {
  auto inst = nt::NetworkTableInstance::GetDefault();
  auto table = inst.GetTable("datatable");
  auto xSub = table->GetDoubleTopic("x").Subscribe(0.0);
  auto ySub = table->GetDoubleTopic("y").Subscribe(0.0);
  inst.StartClient4("example client");
  inst.SetServerTeam(TEAM);  // where TEAM=190, 294, etc, or use inst.setServer("hostname") or similar
  inst.StartDSClient();  // recommended if running on DS computer; this gets the robot IP from the DS
  while (true) {
    using namespace std::chrono_literals;
    std::this_thread::sleep_for(1s);
    double x = xSub.Get();
    double y = ySub.Get();
    fmt::print("X: {} Y: {}\n", x, y);
  }
}
#include <chrono>
#include <thread>
#include <fmt/format.h>
#include <ntcore_cpp.h>

int main() {
  NT_Inst inst = nt::GetDefaultInstance();
  NT_Subscriber xSub =
      nt::Subscribe(nt::GetTopic(inst, "/datatable/x"), NT_DOUBLE, "double");
  NT_Subscriber ySub =
      nt::Subscribe(nt::GetTopic(inst, "/datatable/y"), NT_DOUBLE, "double");
  nt::StartClient4(inst, "example client");
  nt::SetServerTeam(inst, TEAM, 0);  // where TEAM=190, 294, etc, or use inst.setServer("hostname") or similar
  nt::StartDSClient(inst, 0);  // recommended if running on DS computer; this gets the robot IP from the DS
  while (true) {
    using namespace std::chrono_literals;
    std::this_thread::sleep_for(1s);
    double x = nt::GetDouble(xSub, 0.0);
    double y = nt::GetDouble(ySub, 0.0);
    fmt::print("X: {} Y: {}\n", x, y);
  }
}
#include <stdio.h>
#include <threads.h>
#include <time.h>
#include <networktables/ntcore.h>

int main() {
  NT_Instance inst = NT_GetDefaultInstance();
  NT_Subscriber xSub =
      NT_Subscribe(NT_GetTopic(inst, "/datatable/x"), NT_DOUBLE, "double", NULL, 0);
  NT_Subscriber ySub =
      NT_Subscribe(NT_GetTopic(inst, "/datatable/y"), NT_DOUBLE, "double", NULL, 0);
  NT_StartClient4(inst, "example client");
  NT_SetServerTeam(inst, TEAM);  // where TEAM=190, 294, etc, or use inst.setServer("hostname") or similar
  NT_StartDSClient(inst);  // recommended if running on DS computer; this gets the robot IP from the DS
  while (true) {
    thrd_sleep(&(struct timespec){.tv_sec=1}, NULL);
    double x = NT_GetDouble(xSub, 0.0);
    double y = NT_GetDouble(ySub, 0.0);
    printf("X: %f Y: %f\n", x, y);
  }
}
#!/usr/bin/env python3

import ntcore
import time

if __name__ == "__main__":
    inst = ntcore.NetworkTableInstance.getDefault()
    table = inst.getTable("datatable")
    xSub = table.getDoubleTopic("x").subscribe(0)
    ySub = table.getDoubleTopic("y").subscribe(0)
    inst.startClient4("example client")
    inst.setServerTeam(TEAM) # where TEAM=190, 294, etc, or use inst.setServer("hostname") or similar
    inst.startDSClient() # recommended if running on DS computer; this gets the robot IP from the DS

    while True:
        time.sleep(1)

        x = xSub.get()
        y = ySub.get()
        print(f"X: {x} Y: {y}")

In this example an instance of NetworkTables is created and subscribers are created to reference the values of “x” and “y” from a table called “datatable”.

然后,该实例作为具有组号的NetworkTables客户端启动(roboRIO始终是服务器)。此外,如果程序在机器操控台计算机上运行,则使用startDSClient()方法,NetworkTables将从机器操控台获取机械手IP地址。

然后,此示例程序简单地每秒循环一次,获取x和y的值并将其打印在控制台上。在现实的程序中,客户端可能正在处理或生成供机器人使用的值。

使用Gradle构建

Example build.gradle files are provided in the StandaloneAppSamples Repository Update the GradleRIO version to correspond to the desired WPILib version.

 1plugins {
 2    id "java"
 3    id 'application'
 4    id 'com.github.johnrengelman.shadow' version '8.1.1'
 5    id "edu.wpi.first.GradleRIO" version "2024.2.1"
 6    id 'edu.wpi.first.WpilibTools' version '1.3.0'
 7}
 8
 9application {
10    mainClass = 'Program'
11}
12
13wpilibTools.deps.wpilibVersion = wpi.versions.wpilibVersion.get()
14
15def nativeConfigName = 'wpilibNatives'
16def nativeConfig = configurations.create(nativeConfigName)
17
18def nativeTasks = wpilibTools.createExtractionTasks {
19    configurationName = nativeConfigName
20}
21
22nativeTasks.addToSourceSetResources(sourceSets.main)
23nativeConfig.dependencies.add wpilibTools.deps.wpilib("wpimath")
24nativeConfig.dependencies.add wpilibTools.deps.wpilib("wpinet")
25nativeConfig.dependencies.add wpilibTools.deps.wpilib("wpiutil")
26nativeConfig.dependencies.add wpilibTools.deps.wpilib("ntcore")
27nativeConfig.dependencies.add wpilibTools.deps.wpilib("cscore")
28nativeConfig.dependencies.add wpilibTools.deps.wpilibOpenCv("frc" + wpi.frcYear.get(), wpi.versions.opencvVersion.get())
29
30dependencies {
31    implementation wpilibTools.deps.wpilibJava("wpiutil")
32    implementation wpilibTools.deps.wpilibJava("wpimath")
33    implementation wpilibTools.deps.wpilibJava("wpinet")
34    implementation wpilibTools.deps.wpilibJava("ntcore")
35    implementation wpilibTools.deps.wpilibJava("cscore")
36    implementation wpilibTools.deps.wpilibJava("cameraserver")
37    implementation wpilibTools.deps.wpilibOpenCvJava("frc" + wpi.frcYear.get(), wpi.versions.opencvVersion.get())
38
39    implementation group: "com.fasterxml.jackson.core", name: "jackson-annotations", version: wpi.versions.jacksonVersion.get()
40    implementation group: "com.fasterxml.jackson.core", name: "jackson-core", version: wpi.versions.jacksonVersion.get()
41    implementation group: "com.fasterxml.jackson.core", name: "jackson-databind", version: wpi.versions.jacksonVersion.get()
42
43    implementation group: "org.ejml", name: "ejml-simple", version: wpi.versions.ejmlVersion.get()
44    implementation group: "us.hebi.quickbuf", name: "quickbuf-runtime", version: wpi.versions.quickbufVersion.get();
45}
46
47shadowJar {
48    archiveBaseName = "TestApplication"
49    archiveVersion = ""
50    exclude("module-info.class")
51    archiveClassifier.set(wpilibTools.currentPlatform.platformName)
52}
53
54wrapper {
55    gradleVersion = '8.5'
56}

Uncomment the appropriate platform as highlighted.

 1plugins {
 2    id "cpp"
 3    id "edu.wpi.first.GradleRIO" version "2024.2.1"
 4}
 5
 6// Disable local cache, as it won't have the cross artifact necessary
 7wpi.maven.useLocal = false
 8
 9// Set to true to run simulation in debug mode
10wpi.cpp.debugSimulation = false
11
12def appName = "TestApplication"
13
14nativeUtils.withCrossLinuxArm64()
15//nativeUtils.withCrossLinuxArm32() // Uncomment to build for arm32. targetPlatform below also needs to be fixed
16
17model {
18    components {
19        "${appName}"(NativeExecutableSpec) {
20            //targetPlatform wpi.platforms.desktop // Uncomment to build on whatever the native platform currently is
21            targetPlatform wpi.platforms.linuxarm64
22            //targetPlatform wpi.platforms.linuxarm32 // Uncomment to build for arm32
23
24            sources.cpp {
25                source {
26                    srcDir 'src/main/cpp'
27                    include '**/*.cpp', '**/*.cc'
28                }
29                exportedHeaders {
30                    srcDir 'src/main/include'
31                }
32            }
33
34            // Enable run tasks for this component
35            wpi.cpp.enableExternalTasks(it)
36
37            wpi.cpp.deps.wpilibStatic(it)
38        }
39    }
40}
41
42wrapper {
43    gradleVersion = '8.5'
44}

Building Python

For Python, refer to the RobotPy install documentation.