diff --git a/build.gradle b/build.gradle index 907a2097d..978b93278 100644 --- a/build.gradle +++ b/build.gradle @@ -41,5 +41,6 @@ spotless { target "**/*.java" licenseHeaderFile "$rootDir/LicenseHeader.txt" targetExclude("photon-core/src/main/java/org/photonvision/PhotonVersion.java") + targetExclude("photon-lib/src/main/java/org/photonvision/PhotonVersion.java") } } diff --git a/photon-core/build.gradle b/photon-core/build.gradle index cf257268b..520f932d7 100644 --- a/photon-core/build.gradle +++ b/photon-core/build.gradle @@ -1,5 +1,4 @@ -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter +import java.nio.file.Path apply from: "${rootDir}/shared/common.gradle" @@ -30,23 +29,8 @@ dependencies { } task writeCurrentVersionJava { - String date = DateTimeFormatter.ofPattern("yyyy-M-d hh:mm:ss").format(LocalDateTime.now()) - File versionFile = new File(java.nio.file.Path.of("$projectDir", "src", "main", "java", "org", "photonvision", "PhotonVersion.java") - .toAbsolutePath().toString()) - versionFile.delete() - versionFile << "package org.photonvision;\n" + - "\n" + - "/*\n" + - " * Autogenerated file! Do not manually edit this file. This version is regenerated\n" + - " * any time the publish task is run, or when this file is deleted.\n" + - " */\n" + - "\n" + - "@SuppressWarnings(\"ALL\")\n" + - "public final class PhotonVersion {\n" + - " public static final String versionString = \"${versionString}\";\n" + - " public static final String buildDate = \"${date}\";\n" + - " public static final boolean isRelease = !versionString.startsWith(\"dev\");\n" + - "}" + writePhotonVersionFile(Path.of("$projectDir", "src", "main", "java", "org", "photonvision", "PhotonVersion.java"), + versionString) } build.dependsOn writeCurrentVersionJava diff --git a/photon-core/src/main/java/org/photonvision/common/dataflow/networktables/NetworkTablesManager.java b/photon-core/src/main/java/org/photonvision/common/dataflow/networktables/NetworkTablesManager.java index 6da54502f..fa6b48ab2 100644 --- a/photon-core/src/main/java/org/photonvision/common/dataflow/networktables/NetworkTablesManager.java +++ b/photon-core/src/main/java/org/photonvision/common/dataflow/networktables/NetworkTablesManager.java @@ -21,6 +21,7 @@ import edu.wpi.first.networktables.LogMessage; import edu.wpi.first.networktables.NetworkTable; import edu.wpi.first.networktables.NetworkTableInstance; import java.util.function.Consumer; +import org.photonvision.PhotonVersion; import org.photonvision.common.configuration.NetworkConfig; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; @@ -62,16 +63,23 @@ public class NetworkTablesManager { hasReportedConnectionFailure = false; lastConnectMessageMillis = System.currentTimeMillis(); ScriptManager.queueEvent(ScriptEventType.kNTConnected); + getInstance().broadcastVersion(); } } } + private void broadcastVersion() { + kRootTable.getEntry("version").setString(PhotonVersion.versionString); + kRootTable.getEntry("buildDate").setString(PhotonVersion.buildDate); + } + public void setConfig(NetworkConfig config) { if (config.runNTServer) { setServerMode(); } else { setClientMode(config.teamNumber); } + broadcastVersion(); } private void setClientMode(int teamNumber) { @@ -86,11 +94,13 @@ public class NetworkTablesManager { logger.error( "[NetworkTablesManager] Could not connect to the robot! Will retry in the background..."); } + broadcastVersion(); } private void setServerMode() { logger.info("Starting NT Server"); ntInstance.stopClient(); ntInstance.startServer(); + broadcastVersion(); } } diff --git a/photon-lib/build.gradle b/photon-lib/build.gradle index b8a4f3c3e..33f9441ea 100644 --- a/photon-lib/build.gradle +++ b/photon-lib/build.gradle @@ -1,14 +1,19 @@ +import java.nio.file.Path + apply plugin: "cpp" apply plugin: "java" apply plugin: "google-test-test-suite" apply plugin: "edu.wpi.first.NativeUtils" apply from: "${rootDir}/shared/config.gradle" +apply from: "${rootDir}/versioningHelper.gradle" test { useJUnitPlatform() } +def jniPlatforms = ['linuxaarch64bionic', 'linuxraspbian', 'linuxx86-64', 'osxx86-64', 'windowsx86-64'] + // Apply Java configuration dependencies { compile project(":photon-targeting") @@ -19,16 +24,15 @@ dependencies { implementation "edu.wpi.first.wpilibj:wpilibj-java:$wpilibVersion" implementation "edu.wpi.first.wpiutil:wpiutil-java:$wpilibVersion" implementation "edu.wpi.first.wpimath:wpimath-java:$wpilibVersion" - implementation "edu.wpi.first.hal:hal-java:$wpilibVersion" implementation "edu.wpi.first.thirdparty.frc2020.opencv:opencv-java:3.4.7-2" // NTCore implementation "edu.wpi.first.ntcore:ntcore-java:$wpilibVersion" - compile "edu.wpi.first.ntcore:ntcore-jni:$wpilibVersion:linuxaarch64bionic" - compile "edu.wpi.first.ntcore:ntcore-jni:$wpilibVersion:linuxraspbian" - compile "edu.wpi.first.ntcore:ntcore-jni:$wpilibVersion:linuxx86-64" - compile "edu.wpi.first.ntcore:ntcore-jni:$wpilibVersion:osxx86-64" - compile "edu.wpi.first.ntcore:ntcore-jni:$wpilibVersion:windowsx86-64" + jniPlatforms.each { compile "edu.wpi.first.ntcore:ntcore-jni:$wpilibVersion:$it" } + + // HAL + implementation "edu.wpi.first.hal:hal-java:$wpilibVersion" + jniPlatforms.each {compile "edu.wpi.first.hal:hal-jni:$wpilibVersion:$it"} // Junit testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.2") @@ -102,4 +106,12 @@ task generateVendorJson() { build.dependsOn generateVendorJson + +task writeCurrentVersionJava { + writePhotonVersionFile(Path.of("$projectDir", "src", "main", "java", "org", "photonvision", "PhotonVersion.java"), + versionString) +} + +build.dependsOn writeCurrentVersionJava + apply from: "publish.gradle" diff --git a/photon-lib/src/main/java/org/photonvision/PhotonCamera.java b/photon-lib/src/main/java/org/photonvision/PhotonCamera.java index 4cf88578a..d3a7914df 100644 --- a/photon-lib/src/main/java/org/photonvision/PhotonCamera.java +++ b/photon-lib/src/main/java/org/photonvision/PhotonCamera.java @@ -20,6 +20,7 @@ package org.photonvision; import edu.wpi.first.networktables.NetworkTable; import edu.wpi.first.networktables.NetworkTableEntry; import edu.wpi.first.networktables.NetworkTableInstance; +import edu.wpi.first.wpilibj.DriverStation; import org.photonvision.common.dataflow.structures.Packet; import org.photonvision.common.hardware.VisionLEDMode; import org.photonvision.targeting.PhotonPipelineResult; @@ -32,8 +33,10 @@ public class PhotonCamera { final NetworkTableEntry outputSaveImgEntry; final NetworkTableEntry pipelineIndexEntry; final NetworkTableEntry ledModeEntry; + final NetworkTableEntry versionEntry; final NetworkTable mainTable = NetworkTableInstance.getDefault().getTable("photonvision"); + private final String path; boolean driverMode; int pipelineIndex; @@ -47,12 +50,14 @@ public class PhotonCamera { * @param rootTable The root table that the camera is broadcasting information over. */ public PhotonCamera(NetworkTable rootTable) { + path = rootTable.getPath(); rawBytesEntry = rootTable.getEntry("rawBytes"); driverModeEntry = rootTable.getEntry("driverMode"); inputSaveImgEntry = rootTable.getEntry("inputSaveImgCmd"); outputSaveImgEntry = rootTable.getEntry("outputSaveImgCmd"); pipelineIndexEntry = rootTable.getEntry("pipelineIndex"); ledModeEntry = mainTable.getEntry("ledMode"); + versionEntry = mainTable.getEntry("version"); driverMode = driverModeEntry.getBoolean(false); pipelineIndex = pipelineIndexEntry.getNumber(0).intValue(); @@ -74,6 +79,8 @@ public class PhotonCamera { * @return The latest pipeline result. */ public PhotonPipelineResult getLatestResult() { + verifyVersion(); + // Clear the packet. packet.clear(); @@ -199,4 +206,20 @@ public class PhotonCamera { public boolean hasTargets() { return getLatestResult().hasTargets(); } + + private void verifyVersion() { + String versionString = versionEntry.getString(""); + if (versionString.equals("")) { + DriverStation.reportError( + "PhotonVision coprocessor at path " + path + " not found on NetworkTables!", true); + } else if (!PhotonVersion.versionMatches(versionString)) { + DriverStation.reportError( + "Photon version " + + PhotonVersion.versionString + + " does not match coprocessor version " + + versionString + + "!", + true); + } + } } diff --git a/photon-lib/src/test/java/org/photonvision/PhotonVersionTest.java b/photon-lib/src/test/java/org/photonvision/PhotonVersionTest.java new file mode 100644 index 000000000..86e750f30 --- /dev/null +++ b/photon-lib/src/test/java/org/photonvision/PhotonVersionTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.photonvision; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class PhotonVersionTest { + public static final boolean versionMatches(String versionString, String other) { + String c = versionString; + Pattern p = Pattern.compile("v[0-9]+.[0-9]+.[0-9]+"); + Matcher m = p.matcher(c); + if (m.find()) { + c = m.group(0); + } else { + return false; + } + m = p.matcher(other); + if (m.find()) { + other = m.group(0); + } else { + return false; + } + return c.equals(other); + } + + @Test + public void testVersion() { + Assertions.assertTrue(versionMatches("v2021.1.6", "v2021.1.6")); + Assertions.assertTrue(versionMatches("dev-v2021.1.6", "v2021.1.6")); + Assertions.assertTrue(versionMatches("dev-v2021.1.6-5-gca49ea50", "v2021.1.6")); + Assertions.assertFalse(versionMatches("", "v2021.1.6")); + Assertions.assertFalse(versionMatches("v2021.1.6", "")); + } +} diff --git a/shared/PhotonVersion.java.in b/shared/PhotonVersion.java.in new file mode 100644 index 000000000..3805b1393 --- /dev/null +++ b/shared/PhotonVersion.java.in @@ -0,0 +1,51 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.photonvision; + +/* +* Autogenerated file! Do not manually edit this file. This version is regenerated +* any time the publish task is run, or when this file is deleted. +*/ + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@SuppressWarnings("ALL") +public final class PhotonVersion { + public static final String versionString = "${version}"; + public static final String buildDate = "${date}"; + public static final boolean isRelease = !versionString.startsWith("dev"); + + public static final boolean versionMatches(String other) { + String c = versionString; + Pattern p = Pattern.compile("v[0-9]+.[0-9]+.[0-9]+"); + Matcher m = p.matcher(c); + if (m.find()) { + c = m.group(0); + } else { + return false; + } + m = p.matcher(other); + if (m.find()) { + other = m.group(0); + } else { + return false; + } + return c.equals(other); + } +} diff --git a/shared/common.gradle b/shared/common.gradle index 2e1d176e4..bc395c2f9 100644 --- a/shared/common.gradle +++ b/shared/common.gradle @@ -1,3 +1,7 @@ +import java.nio.file.Path +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + // Plugins apply plugin: "jacoco" apply plugin: "java" diff --git a/versioningHelper.gradle b/versioningHelper.gradle index 957220bee..7fd635c9e 100644 --- a/versioningHelper.gradle +++ b/versioningHelper.gradle @@ -1,6 +1,6 @@ +import java.nio.file.Path import java.time.LocalDateTime import java.time.format.DateTimeFormatter -import java.nio.file.Path gradle.allprojects { ext.getCurrentVersion = { -> @@ -12,18 +12,28 @@ gradle.allprojects { standardOutput = stdout } tagIsh = stdout.toString().trim().toLowerCase() - } catch(Exception e) { + } catch (Exception e) { tagIsh = "dev-Unknown" } // Dev tags: v2021.1.6-3-gf922466d // We're specifically looking to capture the middle -3- boolean isDev = tagIsh.matches(".*-[0-9]*-g[0-9a-f]*") - if(isDev && !tagIsh.startsWith("dev-")) tagIsh = "dev-" + tagIsh + if (isDev && !tagIsh.startsWith("dev-")) tagIsh = "dev-" + tagIsh println("Picked up version: " + tagIsh) return tagIsh } - if(!ext.has("versionString")) { + if (!ext.has("versionString")) { ext.versionString = getCurrentVersion() } + + ext.writePhotonVersionFile = { Path path, String version -> + println("Writing " + version + " to " + path.toAbsolutePath().toString()) + String date = DateTimeFormatter.ofPattern("yyyy-M-d hh:mm:ss").format(LocalDateTime.now()) + File versionFileOut = new File(path.toAbsolutePath().toString()) + versionFileOut.delete() + def versionFileIn = file("${rootDir}/shared/PhotonVersion.java.in") + def read = versionFileIn.text.replace('${version}', version).replace('${date}', date) + versionFileOut.write(read) + } }