Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions akit/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import org.gradle.external.javadoc.StandardJavadocDocletOptions
import org.gradle.internal.os.OperatingSystem

plugins {
id("cpp")
id("java")
id("jacoco")
id("google-test")
id("edu.wpi.first.wpilib.repositories.WPILibRepositoriesPlugin") version "2025.0"
id("edu.wpi.first.NativeUtils") version "2026.0.1"
Expand Down Expand Up @@ -56,8 +58,58 @@ tasks.withType<Javadoc> {
}
}

// Determine the NativeUtils platform classifier for the current OS/arch so we
// can locate the WPI native libraries that were extracted by the C++ build.
val wpilibNativePlatform: String by lazy {
val os = OperatingSystem.current()
when {
os.isMacOsX -> "osxuniversal"
os.isLinux -> {
val arch = System.getProperty("os.arch") ?: "amd64"
if (arch.contains("aarch64") || arch.contains("arm64")) "linuxarm64"
else if (arch.contains("arm")) "linuxarm32"
else "linuxx86-64"
}
os.isWindows -> "windowsx86-64"
else -> throw GradleException("Unsupported platform for WPI native library detection")
}
}

tasks.named<Test>("test") {
useJUnitPlatform()
finalizedBy(tasks.named("jacocoTestReport"))

// The C++ install task extracts all WPI shared libraries (libwpiHaljni, libwpiutil, etc.)
// into build/install/wpilibioTest/<platform>/release/lib/. Depend on that task so the
// libraries exist before the JVM test process starts, then add the directory to
// java.library.path so RuntimeLoader can find them without extracting from JARs.
//
// NativeUtils creates the install tasks via the old Gradle software model, so they are
// not available via tasks.named() at configuration time. Use tasks.matching() instead
// (which is lazy and resolves after the model is realised).
val installTaskName =
"installWpilibioTest${wpilibNativePlatform.replaceFirstChar { it.uppercaseChar() }}ReleaseGoogleTestExe"
dependsOn(tasks.matching { it.name == installTaskName })

val wpiNativeLibDir =
layout.buildDirectory.dir("install/wpilibioTest/$wpilibNativePlatform/release/lib")
val wpilibioSharedLibDir =
layout.buildDirectory.dir("libs/wpilibio/shared/$wpilibNativePlatform/release")

doFirst {
jvmArgs(
"-Djava.library.path=${wpiNativeLibDir.get().asFile.absolutePath}${File.pathSeparator}${wpilibioSharedLibDir.get().asFile.absolutePath}"
)
}
}

tasks.named<JacocoReport>("jacocoTestReport") {
dependsOn(tasks.named("test"))
reports {
xml.required.set(false)
html.required.set(true)
csv.required.set(true)
}
}

java {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ private LogFileUtil() {}
* @return The new path.
*/
public static String addPathSuffix(String path, String suffix) {
if (suffix.isEmpty()) return path;
int dotIndex = path.lastIndexOf(".");
if (dotIndex == -1) {
return path;
Expand Down Expand Up @@ -100,7 +101,9 @@ static String findReplayLogAdvantageScope() {
Paths.get(System.getProperty("java.io.tmpdir"), advantageScopeFileName);
String advantageScopeLogPath = null;
try (Scanner fileScanner = new Scanner(advantageScopeTempPath)) {
advantageScopeLogPath = fileScanner.nextLine();
if (fileScanner.hasNextLine()) {
advantageScopeLogPath = fileScanner.nextLine();
}
} catch (IOException e) {
}
return advantageScopeLogPath;
Expand Down
24 changes: 14 additions & 10 deletions akit/src/main/java/org/littletonrobotics/junction/LogTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -928,7 +928,8 @@ public LogValue get(String key) {
*/
public byte[] get(String key, byte[] defaultValue) {
if (data.containsKey(prefix + key)) {
return get(key).getRaw(defaultValue);
byte[] stored = get(key).getRaw(defaultValue);
return stored == defaultValue ? stored : stored.clone();
} else {
return defaultValue;
}
Expand Down Expand Up @@ -977,7 +978,8 @@ public boolean get(String key, boolean defaultValue) {
*/
public boolean[] get(String key, boolean[] defaultValue) {
if (data.containsKey(prefix + key)) {
return get(key).getBooleanArray(defaultValue);
boolean[] stored = get(key).getBooleanArray(defaultValue);
return stored == defaultValue ? stored : stored.clone();
} else {
return defaultValue;
}
Expand Down Expand Up @@ -1084,7 +1086,8 @@ public long get(String key, long defaultValue) {
*/
public long[] get(String key, long[] defaultValue) {
if (data.containsKey(prefix + key)) {
return get(key).getIntegerArray(defaultValue);
long[] stored = get(key).getIntegerArray(defaultValue);
return stored == defaultValue ? stored : stored.clone();
} else {
return defaultValue;
}
Expand Down Expand Up @@ -1133,7 +1136,8 @@ public float get(String key, float defaultValue) {
*/
public float[] get(String key, float[] defaultValue) {
if (data.containsKey(prefix + key)) {
return get(key).getFloatArray(defaultValue);
float[] stored = get(key).getFloatArray(defaultValue);
return stored == defaultValue ? stored : stored.clone();
} else {
return defaultValue;
}
Expand Down Expand Up @@ -1182,7 +1186,8 @@ public double get(String key, double defaultValue) {
*/
public double[] get(String key, double[] defaultValue) {
if (data.containsKey(prefix + key)) {
return get(key).getDoubleArray(defaultValue);
double[] stored = get(key).getDoubleArray(defaultValue);
return stored == defaultValue ? stored : stored.clone();
} else {
return defaultValue;
}
Expand Down Expand Up @@ -1231,7 +1236,8 @@ public String get(String key, String defaultValue) {
*/
public String[] get(String key, String[] defaultValue) {
if (data.containsKey(prefix + key)) {
return get(key).getStringArray(defaultValue);
String[] stored = get(key).getStringArray(defaultValue);
return stored == defaultValue ? stored : stored.clone();
} else {
return defaultValue;
}
Expand Down Expand Up @@ -2130,10 +2136,8 @@ public boolean equals(Object other) {
if (other instanceof LogValue) {
LogValue otherValue = (LogValue) other;
if (otherValue.type.equals(type)
&& customTypeStr == otherValue.customTypeStr
&& unitStr == otherValue.unitStr
&& (customTypeStr == null || otherValue.customTypeStr.equals(customTypeStr))
&& (unitStr == null || otherValue.unitStr.equals(unitStr))) {
&& Objects.equals(customTypeStr, otherValue.customTypeStr)
&& Objects.equals(unitStr, otherValue.unitStr)) {
switch (type) {
case Raw:
return Arrays.equals(getRaw(), otherValue.getRaw());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,16 @@ public void run() {
while (true) {
LogTable entry = queue.take(); // Wait for data

// Send data to receivers
// Send data to receivers — catch InterruptedException per-receiver so that a single
// receiver throwing it does not prematurely exit the loop and starve other receivers.
for (int i = 0; i < dataReceivers.size(); i++) {
dataReceivers.get(i).putTable(entry);
try {
dataReceivers.get(i).putTable(entry);
} catch (InterruptedException e) {
// A receiver signalled an interrupt; treat it as a receiver-level error and
// re-interrupt the thread so the outer catch can initiate clean shutdown.
Thread.currentThread().interrupt();
}
}
}
} catch (InterruptedException exception) {
Expand Down
Loading