diff --git a/.gitignore b/.gitignore index 7309e1817..52023405d 100644 --- a/.gitignore +++ b/.gitignore @@ -143,3 +143,4 @@ build/* build photon-lib/src/main/java/org/photonvision/PhotonVersion.java /photonlib-java-examples/bin/ +photon-lib/src/generate/native/include/PhotonVersion.h diff --git a/photon-core/build.gradle b/photon-core/build.gradle index d881bc0f4..51a643185 100644 --- a/photon-core/build.gradle +++ b/photon-core/build.gradle @@ -25,7 +25,8 @@ dependencies { } task writeCurrentVersionJava { - writePhotonVersionFile(Path.of("$projectDir", "src", "main", "java", "org", "photonvision", "PhotonVersion.java"), + def versionFileIn = file("${rootDir}/shared/PhotonVersion.java.in") + writePhotonVersionFile(versionFileIn, Path.of("$projectDir", "src", "main", "java", "org", "photonvision", "PhotonVersion.java"), versionString) } diff --git a/photon-lib/build.gradle b/photon-lib/build.gradle index 0294f26c5..1b9ee4b58 100644 --- a/photon-lib/build.gradle +++ b/photon-lib/build.gradle @@ -58,6 +58,7 @@ model { } exportedHeaders { srcDirs "src/main/native/include" + srcDirs "src/generate/native/include" } } } @@ -105,11 +106,15 @@ task generateVendorJson() { build.dependsOn generateVendorJson -task writeCurrentVersionJava { - writePhotonVersionFile(Path.of("$projectDir", "src", "main", "java", "org", "photonvision", "PhotonVersion.java"), +task writeCurrentVersion { + def versionFileIn = file("${rootDir}/shared/PhotonVersion.java.in") + writePhotonVersionFile(versionFileIn, Path.of("$projectDir", "src", "main", "java", "org", "photonvision", "PhotonVersion.java"), + versionString) + versionFileIn = file("${rootDir}/shared/PhotonVersion.h.in") + writePhotonVersionFile(versionFileIn, Path.of("$projectDir", "src", "generate", "native", "include", "PhotonVersion.h"), versionString) } -build.dependsOn writeCurrentVersionJava +build.dependsOn writeCurrentVersion 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 991cf446e..e0fbdd565 100644 --- a/photon-lib/src/main/java/org/photonvision/PhotonCamera.java +++ b/photon-lib/src/main/java/org/photonvision/PhotonCamera.java @@ -34,6 +34,7 @@ import org.photonvision.targeting.PhotonPipelineResult; /** Represents a camera that is connected to PhotonVision. */ public class PhotonCamera { + protected final NetworkTable rootTable; final NetworkTableEntry rawBytesEntry; final NetworkTableEntry driverModeEntry; final NetworkTableEntry inputSaveImgEntry; @@ -42,7 +43,6 @@ public class PhotonCamera { final NetworkTableEntry ledModeEntry; final NetworkTableEntry versionEntry; - final NetworkTable mainTable = NetworkTableInstance.getDefault().getTable("photonvision"); private final String path; Packet packet = new Packet(1); @@ -50,9 +50,14 @@ public class PhotonCamera { /** * Constructs a PhotonCamera from a root table. * - * @param rootTable The root table that the camera is broadcasting information over. + * @param instance The NetworkTableInstance to pull data from. This can be a custom instance in + * simulation, but should *usually* be the default NTInstance from + * NetworkTableInstance::getDefault + * @param cameraName The name of the camera, as seen in the UI. */ - public PhotonCamera(NetworkTable rootTable) { + public PhotonCamera(NetworkTableInstance instance, String cameraName) { + var mainTable = instance.getTable("photonvision"); + this.rootTable = mainTable.getSubTable(cameraName); path = rootTable.getPath(); rawBytesEntry = rootTable.getEntry("rawBytes"); driverModeEntry = rootTable.getEntry("driverMode"); @@ -69,7 +74,7 @@ public class PhotonCamera { * @param cameraName The nickname of the camera (found in the PhotonVision UI). */ public PhotonCamera(String cameraName) { - this(NetworkTableInstance.getDefault().getTable("photonvision").getSubTable(cameraName)); + this(NetworkTableInstance.getDefault(), cameraName); } /** diff --git a/photon-lib/src/main/java/org/photonvision/SimPhotonCamera.java b/photon-lib/src/main/java/org/photonvision/SimPhotonCamera.java index 904a43145..a4103e3ab 100644 --- a/photon-lib/src/main/java/org/photonvision/SimPhotonCamera.java +++ b/photon-lib/src/main/java/org/photonvision/SimPhotonCamera.java @@ -24,7 +24,6 @@ package org.photonvision; -import edu.wpi.first.networktables.NetworkTable; import edu.wpi.first.networktables.NetworkTableEntry; import edu.wpi.first.networktables.NetworkTableInstance; import java.util.Arrays; @@ -46,10 +45,13 @@ public class SimPhotonCamera extends PhotonCamera { /** * Constructs a Simulated PhotonCamera from a root table. * - * @param rootTable The root table that the camera is broadcasting information over. + * @param instance The NetworkTableInstance to pull data from. This can be a custom instance in + * simulation, but should *usually* be the default NTInstance from + * NetworkTableInstance::getDefault + * @param cameraName The name of the camera, as seen in the UI. */ - public SimPhotonCamera(NetworkTable rootTable) { - super(rootTable); + public SimPhotonCamera(NetworkTableInstance instance, String cameraName) { + super(instance, cameraName); latencyMillisEntry = rootTable.getEntry("latencyMillis"); hasTargetEntry = rootTable.getEntry("hasTargetEntry"); @@ -66,7 +68,7 @@ public class SimPhotonCamera extends PhotonCamera { * @param cameraName The nickname of the camera (found in the PhotonVision UI). */ public SimPhotonCamera(String cameraName) { - this(NetworkTableInstance.getDefault().getTable("photonvision").getSubTable(cameraName)); + this(NetworkTableInstance.getDefault(), cameraName); } /** diff --git a/photon-lib/src/main/native/cpp/photonlib/PhotonCamera.cpp b/photon-lib/src/main/native/cpp/photonlib/PhotonCamera.cpp index 714112c38..e497cc749 100644 --- a/photon-lib/src/main/native/cpp/photonlib/PhotonCamera.cpp +++ b/photon-lib/src/main/native/cpp/photonlib/PhotonCamera.cpp @@ -24,23 +24,34 @@ #include "photonlib/PhotonCamera.h" +#include + +#include "PhotonVersion.h" #include "photonlib/Packet.h" namespace photonlib { -PhotonCamera::PhotonCamera(std::shared_ptr rootTable) - : rawBytesEntry(rootTable->GetEntry("rawBytes")), +PhotonCamera::PhotonCamera(std::shared_ptr instance, + const std::string& cameraName) + : mainTable(instance->GetTable("photonvision")), + rootTable(mainTable->GetSubTable(cameraName)), + rawBytesEntry(rootTable->GetEntry("rawBytes")), driverModeEntry(rootTable->GetEntry("driverMode")), inputSaveImgEntry(rootTable->GetEntry("inputSaveImgCmd")), outputSaveImgEntry(rootTable->GetEntry("outputSaveImgCmd")), pipelineIndexEntry(rootTable->GetEntry("pipelineIndex")), - ledModeEntry(mainTable->GetEntry("ledMode")) {} + ledModeEntry(mainTable->GetEntry("ledMode")), + versionEntry(mainTable->GetEntry("version")), + path(rootTable->GetPath()) {} PhotonCamera::PhotonCamera(const std::string& cameraName) - : PhotonCamera(nt::NetworkTableInstance::GetDefault() - .GetTable("photonvision") - ->GetSubTable(cameraName)) {} + : PhotonCamera(std::make_shared( + nt::NetworkTableInstance::GetDefault()), + cameraName) {} PhotonPipelineResult PhotonCamera::GetLatestResult() const { + // Prints warning if not connected + VerifyVersion(); + // Clear the current packet. packet.Clear(); @@ -87,4 +98,20 @@ LEDMode PhotonCamera::GetLEDMode() const { void PhotonCamera::SetLEDMode(LEDMode mode) { ledModeEntry.SetDouble(static_cast(static_cast(mode))); } + +void PhotonCamera::VerifyVersion() const { + const std::string& versionString = versionEntry.GetString(""); + if (versionString.empty()) { + std::string path_ = path; + FRC_ReportError( + frc::warn::Warning, + "PhotonVision coprocessor at path {} not found on NetworkTables!", + path_); + } else if (!VersionMatches(versionString)) { + FRC_ReportError(frc::warn::Warning, + "Photon version {} does not match coprocessor version {}!", + PhotonVersion::versionString, versionString); + } +} + } // namespace photonlib diff --git a/photon-lib/src/main/native/cpp/photonlib/SimPhotonCamera.cpp b/photon-lib/src/main/native/cpp/photonlib/SimPhotonCamera.cpp index d01f546bd..6246276da 100644 --- a/photon-lib/src/main/native/cpp/photonlib/SimPhotonCamera.cpp +++ b/photon-lib/src/main/native/cpp/photonlib/SimPhotonCamera.cpp @@ -26,8 +26,10 @@ namespace photonlib { -SimPhotonCamera::SimPhotonCamera(std::shared_ptr rootTable) - : PhotonCamera(rootTable) {} +SimPhotonCamera::SimPhotonCamera( + std::shared_ptr instance, + const std::string& cameraName) + : PhotonCamera(instance, cameraName) {} SimPhotonCamera::SimPhotonCamera(const std::string& cameraName) : PhotonCamera(cameraName) {} diff --git a/photon-lib/src/main/native/include/photonlib/PhotonCamera.h b/photon-lib/src/main/native/include/photonlib/PhotonCamera.h index 4d08e2999..34610bd28 100644 --- a/photon-lib/src/main/native/include/photonlib/PhotonCamera.h +++ b/photon-lib/src/main/native/include/photonlib/PhotonCamera.h @@ -45,10 +45,15 @@ class PhotonCamera { public: /** * Constructs a PhotonCamera from a root table. - * @param rootTable The root table that the camera is broadcasting information + * + * @param instance The NetworkTableInstance to pull data from. This can be a + * custom instance in simulation, but should *usually* be the default + * NTInstance from {@link NetworkTableInstance::getDefault} + * @param cameraName The name of the camera, as seen in the UI. * over. */ - explicit PhotonCamera(std::shared_ptr rootTable); + explicit PhotonCamera(std::shared_ptr instance, + const std::string& cameraName); /** * Constructs a PhotonCamera from the name of the camera. @@ -133,19 +138,23 @@ class PhotonCamera { "This method should be replaced with PhotonPipelineResult::HasTargets()") bool HasTargets() const { return GetLatestResult().HasTargets(); } - private: - std::shared_ptr mainTable = - nt::NetworkTableInstance::GetDefault().GetTable("photonvision"); - protected: + std::shared_ptr mainTable; + std::shared_ptr rootTable; nt::NetworkTableEntry rawBytesEntry; nt::NetworkTableEntry driverModeEntry; nt::NetworkTableEntry inputSaveImgEntry; nt::NetworkTableEntry outputSaveImgEntry; nt::NetworkTableEntry pipelineIndexEntry; nt::NetworkTableEntry ledModeEntry; + nt::NetworkTableEntry versionEntry; + + std::string path; mutable Packet packet; + + private: + void VerifyVersion() const; }; } // namespace photonlib diff --git a/photon-lib/src/main/native/include/photonlib/SimPhotonCamera.h b/photon-lib/src/main/native/include/photonlib/SimPhotonCamera.h index 9abd92858..e1b3df987 100644 --- a/photon-lib/src/main/native/include/photonlib/SimPhotonCamera.h +++ b/photon-lib/src/main/native/include/photonlib/SimPhotonCamera.h @@ -44,10 +44,13 @@ class SimPhotonCamera : public PhotonCamera { /** * Constructs a Simulated PhotonCamera from a root table. * - * @param rootTable The root table that the camera is broadcasting information - * over. + * @param instance The NetworkTableInstance to pull data from. This can be a + * custom instance in simulation, but should *usually* be the default + * NTInstance from {@link NetworkTableInstance::getDefault} + * @param cameraName The name of the camera, as seen in the UI. */ - explicit SimPhotonCamera(std::shared_ptr rootTable); + explicit SimPhotonCamera(std::shared_ptr instance, + const std::string& cameraName); /** * Constructs a Simulated PhotonCamera from the name of the camera. diff --git a/shared/PhotonVersion.h.in b/shared/PhotonVersion.h.in new file mode 100644 index 000000000..435ae0190 --- /dev/null +++ b/shared/PhotonVersion.h.in @@ -0,0 +1,45 @@ +/* + * 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 . + */ + +#include +#include + +/* + * 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. + */ + +namespace photonlib { + namespace PhotonVersion { + const std::string versionString = "dev-v2022.1.4-2-ga22f8af0"; + const std::string buildDate = "2022-1-20 10:10:04"; + const bool isRelease = !(versionString.rfind("dev", 0) == 0); + } + + bool VersionMatches(const std::string& other) { + std::smatch match; + std::regex versionPattern{"v[0-9]+.[0-9]+.[0-9]+"}; + // Check that both versions are in the right format + if (std::regex_search(PhotonVersion::versionString, match, versionPattern) && + std::regex_search(other, match, versionPattern)) { + // If they are, check string equality + return (PhotonVersion::versionString == other); + } else { + return false; + } + } +} diff --git a/versioningHelper.gradle b/versioningHelper.gradle index fa432ead8..4b085f312 100644 --- a/versioningHelper.gradle +++ b/versioningHelper.gradle @@ -28,13 +28,14 @@ gradle.allprojects { ext.versionString = getCurrentVersion() } - ext.writePhotonVersionFile = { Path path, String version -> + ext.writePhotonVersionFile = {File versionFileIn, 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) + if (!versionFileOut.parentFile.exists()) versionFileOut.parentFile.mkdirs() + if (!versionFileOut.exists()) versionFileOut.createNewFile() versionFileOut.write(read) } }