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.
Un programme client de base ressemble à l’exemple suivant.
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 ».
Ensuite, cette instance est démarrée en tant que client NetworkTables avec le numéro d’équipe (le roboRIO est toujours le serveur). De plus, si le programme est en cours d’exécution sur l’ordinateur de Driver Station, en utilisant la méthode startDSClient (), NetworkTables obtiendra l’adresse IP du robot à partir de Driver Station.
Ensuite, ce programme boucle simplement une fois par seconde et obtient les valeurs de x et y et les imprime sur la console. Dans un programme plus réaliste, le client pourrait traiter ou générer des valeurs calculés de ces lectures et les retourner au robot.
Construire avec 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.