diff --git a/photon-client/src/components/settings/NetworkingCard.vue b/photon-client/src/components/settings/NetworkingCard.vue index 1b3b7bb56..e3b80e2d9 100644 --- a/photon-client/src/components/settings/NetworkingCard.vue +++ b/photon-client/src/components/settings/NetworkingCard.vue @@ -281,11 +281,29 @@ watchEffect(() => { + + Physical cameras will be strictly matched to camera configurations using physical USB port they are plugged + into, in addition to device name and other USB metadata. Additionally, no new cameras are allowed to be added. + This setting is useful for guaranteeing that an already known and configured camera can never be matched as an + "unknown"/"new" camera, which resets pipelines and calibration data. +

+ Cameras will NOT be matched if they change USB ports, and new cameras plugged into this coprocessor will NOT + be automatically recognized or configured for vision processing. +

+ To add a new camera to this coprocessor, disable this setting, connect the camera, and re-enable. + This also disables creating new CameraConfigurations for detected "new" cameras. */ public boolean matchCamerasOnlyByPath = false; diff --git a/photon-core/src/main/java/org/photonvision/vision/camera/CameraInfo.java b/photon-core/src/main/java/org/photonvision/vision/camera/CameraInfo.java index e7a9f4b04..32441a308 100644 --- a/photon-core/src/main/java/org/photonvision/vision/camera/CameraInfo.java +++ b/photon-core/src/main/java/org/photonvision/vision/camera/CameraInfo.java @@ -101,6 +101,8 @@ public class CameraInfo extends UsbCameraInfo { + vendorId + ", pid=" + productId + + ", path=" + + path + ", otherPaths=" + Arrays.toString(otherPaths) + "]"; diff --git a/photon-core/src/main/java/org/photonvision/vision/camera/USBCameraSource.java b/photon-core/src/main/java/org/photonvision/vision/camera/USBCameraSource.java index 1a29a9af2..dbecd1ef5 100644 --- a/photon-core/src/main/java/org/photonvision/vision/camera/USBCameraSource.java +++ b/photon-core/src/main/java/org/photonvision/vision/camera/USBCameraSource.java @@ -57,8 +57,8 @@ public class USBCameraSource extends VisionSource { cvSink = CameraServer.getVideo(this.camera); // set vid/pid if not done already for future matching - if (config.usbVID < 0) config.usbVID = this.camera.getInfo().vendorId; - if (config.usbPID < 0) config.usbPID = this.camera.getInfo().productId; + if (config.usbVID <= 0) config.usbVID = this.camera.getInfo().vendorId; + if (config.usbPID <= 0) config.usbPID = this.camera.getInfo().productId; if (getCameraConfiguration().cameraQuirks == null) getCameraConfiguration().cameraQuirks = diff --git a/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java b/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java index d5ceda85a..ef06d474b 100644 --- a/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java +++ b/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java @@ -317,8 +317,7 @@ public class VisionSourceManager { logger.info("Matching by usb port & name & USB VID/PID..."); cameraConfigurations.addAll( matchCamerasByStrategy(detectedCameraList, unloadedConfigs, true, true, true, false)); - } else - logger.debug("Skipping match by usb port/name/vid/pid, no configs or cameras left to match"); + } // On windows, the v4l path is actually useful and tells us the port the camera is physically // connected to which is neat @@ -327,16 +326,23 @@ public class VisionSourceManager { logger.info("Matching by windows-path & USB VID/PID only..."); cameraConfigurations.addAll( matchCamerasByStrategy(detectedCameraList, unloadedConfigs, false, true, true, true)); - } else - logger.debug( - "Skipping matching by windiws-path/name/vid/pid, no configs or cameras left to match"); + } } if (detectedCameraList.size() > 0 || unloadedConfigs.size() > 0) { logger.info("Matching by usb port & USB VID/PID..."); cameraConfigurations.addAll( matchCamerasByStrategy(detectedCameraList, unloadedConfigs, true, true, false, false)); - } else logger.debug("Skipping match by port/vid/pid, no configs or cameras left to match"); + } + + // Legacy migration -- VID/PID will be unset, so we have to try with our most relaxed strategy + // at least once. We _should_ still have a valid USB path (assuming cameras have not moved), so + // try that first, then fallback to base name only beloow + if (detectedCameraList.size() > 0 || unloadedConfigs.size() > 0) { + logger.info("Matching by base-name & usb port..."); + cameraConfigurations.addAll( + matchCamerasByStrategy(detectedCameraList, unloadedConfigs, true, false, true, false)); + } // handle disabling only-by-base-name matching if (!matchCamerasOnlyByPath) { @@ -344,13 +350,29 @@ public class VisionSourceManager { logger.info("Matching by base-name & USB VID/PID only..."); cameraConfigurations.addAll( matchCamerasByStrategy(detectedCameraList, unloadedConfigs, false, true, true, false)); - } else - logger.debug("Skipping match by base-name/viid/pid, no configs or cameras left to match"); + } + + // Legacy migration for if no USB VID/PID set + if (detectedCameraList.size() > 0 || unloadedConfigs.size() > 0) { + logger.info("Matching by base-name only..."); + cameraConfigurations.addAll( + matchCamerasByStrategy(detectedCameraList, unloadedConfigs, false, false, true, false)); + } } else logger.info("Skipping match by filepath/vid/pid, disabled by user"); if (detectedCameraList.size() > 0) { - cameraConfigurations.addAll( - createConfigsForCameras(detectedCameraList, unloadedConfigs, cameraConfigurations)); + // handle disabling only-by-base-name matching + if (!matchCamerasOnlyByPath) { + cameraConfigurations.addAll( + createConfigsForCameras(detectedCameraList, unloadedConfigs, cameraConfigurations)); + } else { + logger.warn( + "Not creating 'new' Photon CameraConfigurations for [" + + detectedCamInfos.stream() + .map(CameraInfo::toString) + .collect(Collectors.joining(";")) + + "], disabled by user"); + } } logger.debug("Matched or created " + cameraConfigurations.size() + " camera configs!");