mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-22 01:11:40 +00:00
Auto reconnect to unconnected cameras (#40)
* Auto reconnect to unconnected cameras * Refactor vision source manager to not suck * Filter allowed video modes * Run spotless * Fix filtering * Update VisionSourceManager.java * Remove debug stuff * Add unit tests, rebase * run spotless * Fix config test * Fix settings not being saved * run spotless * oop * kil debug prints if we have no usb cams to match * whoooops * add braudcast * Actually save pipeline settings * fix memes * get right coeffs * Run spotless * run spotless * rebase and add some prints * Sorry min * Oops reimpl matching * Oops
This commit is contained in:
@@ -34,9 +34,9 @@ import org.photonvision.common.networking.NetworkManager;
|
|||||||
import org.photonvision.common.util.TestUtils;
|
import org.photonvision.common.util.TestUtils;
|
||||||
import org.photonvision.server.Server;
|
import org.photonvision.server.Server;
|
||||||
import org.photonvision.vision.camera.FileVisionSource;
|
import org.photonvision.vision.camera.FileVisionSource;
|
||||||
import org.photonvision.vision.camera.USBCameraSource;
|
|
||||||
import org.photonvision.vision.pipeline.CVPipelineSettings;
|
import org.photonvision.vision.pipeline.CVPipelineSettings;
|
||||||
import org.photonvision.vision.pipeline.ReflectivePipelineSettings;
|
import org.photonvision.vision.pipeline.ReflectivePipelineSettings;
|
||||||
|
import org.photonvision.vision.processes.VisionModule;
|
||||||
import org.photonvision.vision.processes.VisionModuleManager;
|
import org.photonvision.vision.processes.VisionModuleManager;
|
||||||
import org.photonvision.vision.processes.VisionSource;
|
import org.photonvision.vision.processes.VisionSource;
|
||||||
import org.photonvision.vision.processes.VisionSourceManager;
|
import org.photonvision.vision.processes.VisionSourceManager;
|
||||||
@@ -84,58 +84,44 @@ public class Main {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HashMap<VisionSource, List<CVPipelineSettings>> gatherSources() {
|
private static void addTestModeSources() {
|
||||||
var collectedSources = new HashMap<VisionSource, List<CVPipelineSettings>>();
|
var collectedSources = new HashMap<VisionSource, List<CVPipelineSettings>>();
|
||||||
if (!isTestMode) {
|
|
||||||
var camConfigs = ConfigManager.getInstance().getConfig().getCameraConfigurations();
|
|
||||||
logger.info("Loaded " + camConfigs.size() + " configs from disk.");
|
|
||||||
var sources = VisionSourceManager.loadAllSources(camConfigs.values());
|
|
||||||
|
|
||||||
for (var src : sources) {
|
var camConf2019 =
|
||||||
var usbSrc = (USBCameraSource) src;
|
new CameraConfiguration("WPI2019", TestUtils.getTestMode2019ImagePath().toString());
|
||||||
collectedSources.put(usbSrc, usbSrc.configuration.pipelineSettings);
|
camConf2019.FOV = TestUtils.WPI2019Image.FOV;
|
||||||
logger.debug(
|
camConf2019.calibrations.add(TestUtils.get2019LifeCamCoeffs(true));
|
||||||
() ->
|
|
||||||
"Matched config for camera \""
|
|
||||||
+ src.getFrameProvider().getName()
|
|
||||||
+ "\" and loaded "
|
|
||||||
+ usbSrc.configuration.pipelineSettings.size()
|
|
||||||
+ " pipelines");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var camConf2019 =
|
|
||||||
new CameraConfiguration("WPI2019", TestUtils.getTestMode2019ImagePath().toString());
|
|
||||||
camConf2019.FOV = TestUtils.WPI2019Image.FOV;
|
|
||||||
camConf2019.calibrations.add(TestUtils.get2019LifeCamCoeffs(true));
|
|
||||||
|
|
||||||
var pipeline2019 = new ReflectivePipelineSettings();
|
var pipeline2019 = new ReflectivePipelineSettings();
|
||||||
pipeline2019.pipelineNickname = "CargoShip";
|
pipeline2019.pipelineNickname = "CargoShip";
|
||||||
pipeline2019.targetModel = TargetModel.get2019Target();
|
pipeline2019.targetModel = TargetModel.get2019Target();
|
||||||
|
|
||||||
var psList2019 = new ArrayList<CVPipelineSettings>();
|
var psList2019 = new ArrayList<CVPipelineSettings>();
|
||||||
psList2019.add(pipeline2019);
|
psList2019.add(pipeline2019);
|
||||||
|
|
||||||
var fvs2019 = new FileVisionSource(camConf2019);
|
var fvs2019 = new FileVisionSource(camConf2019);
|
||||||
|
|
||||||
var camConf2020 =
|
var camConf2020 =
|
||||||
new CameraConfiguration("WPI2020", TestUtils.getTestMode2020ImagePath().toString());
|
new CameraConfiguration("WPI2020", TestUtils.getTestMode2020ImagePath().toString());
|
||||||
camConf2020.FOV = TestUtils.WPI2020Image.FOV;
|
camConf2020.FOV = TestUtils.WPI2020Image.FOV;
|
||||||
camConf2019.calibrations.add(TestUtils.get2019LifeCamCoeffs(true));
|
camConf2019.calibrations.add(TestUtils.get2019LifeCamCoeffs(true));
|
||||||
|
|
||||||
var pipeline2020 = new ReflectivePipelineSettings();
|
var pipeline2020 = new ReflectivePipelineSettings();
|
||||||
pipeline2020.pipelineNickname = "OuterPort";
|
pipeline2020.pipelineNickname = "OuterPort";
|
||||||
pipeline2020.targetModel = TargetModel.get2020Target();
|
pipeline2020.targetModel = TargetModel.get2020Target();
|
||||||
camConf2019.calibrations.add(TestUtils.get2019LifeCamCoeffs(true));
|
camConf2020.calibrations.add(TestUtils.get2020LifeCamCoeffs(true));
|
||||||
|
|
||||||
var psList2020 = new ArrayList<CVPipelineSettings>();
|
var psList2020 = new ArrayList<CVPipelineSettings>();
|
||||||
psList2020.add(pipeline2020);
|
psList2020.add(pipeline2020);
|
||||||
|
|
||||||
var fvs2020 = new FileVisionSource(camConf2020);
|
var fvs2020 = new FileVisionSource(camConf2020);
|
||||||
|
|
||||||
collectedSources.put(fvs2019, psList2019);
|
collectedSources.put(fvs2019, psList2019);
|
||||||
collectedSources.put(fvs2020, psList2020);
|
collectedSources.put(fvs2020, psList2020);
|
||||||
}
|
|
||||||
return collectedSources;
|
// logger.info("Adding " + allSources.size() + " configs to VMM.");
|
||||||
|
VisionModuleManager.getInstance().addSources(collectedSources).forEach(VisionModule::start);
|
||||||
|
ConfigManager.getInstance().addCameraConfigurations(collectedSources);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
@@ -145,7 +131,7 @@ public class Main {
|
|||||||
logger.error("Failed to parse command-line options!", e);
|
logger.error("Failed to parse command-line options!", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("Running in " + (isRelease ? "release" : "development") + " mode!");
|
logger.info("Running in " + (isRelease ? "release" : "development") + " mode!");
|
||||||
var logLevel = (isRelease || printDebugLogs) ? LogLevel.INFO : LogLevel.DEBUG;
|
var logLevel = (isRelease || printDebugLogs) ? LogLevel.INFO : LogLevel.DEBUG;
|
||||||
Logger.setLevel(LogGroup.Camera, logLevel);
|
Logger.setLevel(LogGroup.Camera, logLevel);
|
||||||
Logger.setLevel(LogGroup.WebServer, logLevel);
|
Logger.setLevel(LogGroup.WebServer, logLevel);
|
||||||
@@ -169,22 +155,32 @@ public class Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ConfigManager.getInstance().load(); // init config manager
|
ConfigManager.getInstance().load(); // init config manager
|
||||||
|
ConfigManager.getInstance().requestSave();
|
||||||
|
|
||||||
NetworkManager.getInstance().initialize(false);
|
NetworkManager.getInstance().initialize(false);
|
||||||
|
|
||||||
NetworkTablesManager.getInstance()
|
NetworkTablesManager.getInstance()
|
||||||
.setConfig(ConfigManager.getInstance().getConfig().getNetworkConfig());
|
.setConfig(ConfigManager.getInstance().getConfig().getNetworkConfig());
|
||||||
|
|
||||||
HashMap<VisionSource, List<CVPipelineSettings>> allSources = gatherSources();
|
// HashMap<VisionSource, List<CVPipelineSettings>> allSources = gatherSources();
|
||||||
|
|
||||||
logger.info("Adding " + allSources.size() + " configs to VMM.");
|
// logger.info("Adding " + allSources.size() + " configs to VMM.");
|
||||||
VisionModuleManager.getInstance().addSources(allSources);
|
// VisionModuleManager.getInstance().addSources(allSources);
|
||||||
ConfigManager.getInstance().addCameraConfigurations(allSources);
|
// ConfigManager.getInstance().addCameraConfigurations(allSources);
|
||||||
|
|
||||||
|
if (!isTestMode) {
|
||||||
|
VisionSourceManager.getInstance()
|
||||||
|
.registerLoadedConfigs(
|
||||||
|
ConfigManager.getInstance().getConfig().getCameraConfigurations().values());
|
||||||
|
VisionSourceManager.getInstance().registerTimedTask();
|
||||||
|
} else {
|
||||||
|
addTestModeSources();
|
||||||
|
}
|
||||||
|
|
||||||
// Add hardware config to hardware manager
|
// Add hardware config to hardware manager
|
||||||
HardwareManager.getInstance()
|
HardwareManager.getInstance()
|
||||||
.setConfig(ConfigManager.getInstance().getConfig().getHardwareConfig());
|
.setConfig(ConfigManager.getInstance().getConfig().getHardwareConfig());
|
||||||
|
|
||||||
VisionModuleManager.getInstance().startModules();
|
|
||||||
Server.main(DEFAULT_WEBPORT);
|
Server.main(DEFAULT_WEBPORT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import org.photonvision.common.dataflow.DataChangeService;
|
|||||||
import org.photonvision.common.dataflow.events.OutgoingUIEvent;
|
import org.photonvision.common.dataflow.events.OutgoingUIEvent;
|
||||||
import org.photonvision.common.dataflow.networktables.NTDataPublisher;
|
import org.photonvision.common.dataflow.networktables.NTDataPublisher;
|
||||||
import org.photonvision.common.dataflow.websocket.UIDataPublisher;
|
import org.photonvision.common.dataflow.websocket.UIDataPublisher;
|
||||||
import org.photonvision.common.hardware.HardwareManager;
|
|
||||||
import org.photonvision.common.logging.LogGroup;
|
import org.photonvision.common.logging.LogGroup;
|
||||||
import org.photonvision.common.logging.Logger;
|
import org.photonvision.common.logging.Logger;
|
||||||
import org.photonvision.common.util.SerializationUtils;
|
import org.photonvision.common.util.SerializationUtils;
|
||||||
@@ -131,6 +130,8 @@ public class VisionModule {
|
|||||||
logger.info("Setting FOV of vendor camera to " + fov);
|
logger.info("Setting FOV of vendor camera to " + fov);
|
||||||
visionSource.getSettables().setFOV(fov);
|
visionSource.getSettables().setFOV(fov);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
saveAndBroadcastAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDriverMode(boolean isDriverMode) {
|
void setDriverMode(boolean isDriverMode) {
|
||||||
@@ -312,7 +313,7 @@ public class VisionModule {
|
|||||||
ret.calibrations = calList;
|
ret.calibrations = calList;
|
||||||
|
|
||||||
ret.isFovConfigurable =
|
ret.isFovConfigurable =
|
||||||
!(HardwareManager.getInstance().getConfig().hasPresetFOV()
|
!(ConfigManager.getInstance().getConfig().getHardwareConfig().hasPresetFOV()
|
||||||
&& cameraQuirks.hasQuirk(CameraQuirk.PiCam));
|
&& cameraQuirks.hasQuirk(CameraQuirk.PiCam));
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@@ -52,20 +52,16 @@ public class VisionModuleManager {
|
|||||||
return visionModules.get(i);
|
return visionModules.get(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSources(HashMap<VisionSource, List<CVPipelineSettings>> visionSources) {
|
public List<VisionModule> addSources(
|
||||||
|
HashMap<VisionSource, List<CVPipelineSettings>> visionSources) {
|
||||||
|
var addedModules = new ArrayList<VisionModule>();
|
||||||
for (var entry : visionSources.entrySet()) {
|
for (var entry : visionSources.entrySet()) {
|
||||||
var visionSource = entry.getKey();
|
var visionSource = entry.getKey();
|
||||||
var pipelineManager = new PipelineManager(entry.getValue());
|
var pipelineManager = new PipelineManager(entry.getValue());
|
||||||
var module = new VisionModule(pipelineManager, visionSource, visionModules.size());
|
var module = new VisionModule(pipelineManager, visionSource, visionModules.size());
|
||||||
visionModules.add(module);
|
visionModules.add(module);
|
||||||
// todo: logging
|
addedModules.add(module);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startModules() {
|
|
||||||
for (var visionModule : visionModules) {
|
|
||||||
visionModule.start();
|
|
||||||
// todo: logging
|
|
||||||
}
|
}
|
||||||
|
return addedModules;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,67 +20,150 @@ package org.photonvision.vision.processes;
|
|||||||
import edu.wpi.cscore.UsbCamera;
|
import edu.wpi.cscore.UsbCamera;
|
||||||
import edu.wpi.cscore.UsbCameraInfo;
|
import edu.wpi.cscore.UsbCameraInfo;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import org.photonvision.common.configuration.CameraConfiguration;
|
import org.photonvision.common.configuration.CameraConfiguration;
|
||||||
|
import org.photonvision.common.configuration.ConfigManager;
|
||||||
|
import org.photonvision.common.dataflow.DataChangeService;
|
||||||
|
import org.photonvision.common.dataflow.events.OutgoingUIEvent;
|
||||||
import org.photonvision.common.logging.LogGroup;
|
import org.photonvision.common.logging.LogGroup;
|
||||||
import org.photonvision.common.logging.Logger;
|
import org.photonvision.common.logging.Logger;
|
||||||
|
import org.photonvision.common.util.TimedTaskManager;
|
||||||
import org.photonvision.vision.camera.CameraType;
|
import org.photonvision.vision.camera.CameraType;
|
||||||
import org.photonvision.vision.camera.USBCameraSource;
|
import org.photonvision.vision.camera.USBCameraSource;
|
||||||
|
import org.photonvision.vision.pipeline.CVPipelineSettings;
|
||||||
|
|
||||||
public class VisionSourceManager {
|
public class VisionSourceManager {
|
||||||
|
|
||||||
private static final Logger logger = new Logger(VisionSourceManager.class, LogGroup.Camera);
|
private static final Logger logger = new Logger(VisionSourceManager.class, LogGroup.Camera);
|
||||||
private static final List<String> deviceBlacklist = List.of("bcm2835-isp");
|
private static final List<String> deviceBlacklist = List.of("bcm2835-isp");
|
||||||
|
|
||||||
/**
|
final List<UsbCameraInfo> knownUsbCameras = new CopyOnWriteArrayList<>();
|
||||||
* Load vision sources based on currently connected hardware.
|
final List<CameraConfiguration> unmatchedLoadedConfigs = new CopyOnWriteArrayList<>();
|
||||||
*
|
|
||||||
* @param loadedConfigs The {@link CameraConfiguration}s loaded from disk.
|
private static class SingletonHolder {
|
||||||
*/
|
private static final VisionSourceManager INSTANCE = new VisionSourceManager();
|
||||||
public static List<VisionSource> loadAllSources(Collection<CameraConfiguration> loadedConfigs) {
|
}
|
||||||
return loadAllSources(
|
|
||||||
loadedConfigs, filterAllowedDevices(Arrays.asList(UsbCamera.enumerateUsbCameras())));
|
public static VisionSourceManager getInstance() {
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VisionSourceManager() {}
|
||||||
|
|
||||||
|
public void registerTimedTask() {
|
||||||
|
TimedTaskManager.getInstance().addTask("VisionSourceManager", this::tryMatchUSBCams, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerLoadedConfigs(CameraConfiguration... configs) {
|
||||||
|
registerLoadedConfigs(Arrays.asList(configs));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load vision sources based on given cameras and configs.
|
* Register new camera configs loaded from disk. This will add them to the list of configs to try
|
||||||
|
* to match, and also automatically spawn new vision processes as necessary.
|
||||||
*
|
*
|
||||||
* @param loadedConfigs The configs loaded from disk.
|
* @param configs The loaded camera configs.
|
||||||
* @param detectedCamInfos The cameras to attempt connection to.
|
|
||||||
*/
|
*/
|
||||||
public static List<VisionSource> loadAllSources(
|
public void registerLoadedConfigs(Collection<CameraConfiguration> configs) {
|
||||||
Collection<CameraConfiguration> loadedConfigs, List<UsbCameraInfo> detectedCamInfos) {
|
unmatchedLoadedConfigs.addAll(configs);
|
||||||
var loadedUsbCamConfigs =
|
|
||||||
loadedConfigs.stream()
|
|
||||||
.filter(configuration -> configuration.cameraType == CameraType.UsbCamera)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
var matchedCameras = matchUSBCameras(detectedCamInfos, loadedUsbCamConfigs);
|
|
||||||
|
|
||||||
// turn the matched cameras into VisionSources
|
|
||||||
return loadVisionSourcesFromCamConfigs(matchedCameras);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<UsbCameraInfo> filterAllowedDevices(List<UsbCameraInfo> allDevices) {
|
protected Supplier<List<UsbCameraInfo>> cameraInfoSupplier =
|
||||||
List<UsbCameraInfo> filteredDevices = new ArrayList<>();
|
() -> List.of(UsbCamera.enumerateUsbCameras());
|
||||||
for (var device : allDevices) {
|
|
||||||
var deviceInfoStr =
|
protected void tryMatchUSBCams() {
|
||||||
"\""
|
var visionSourceMap = tryMatchUSBCamImpl();
|
||||||
+ device.name
|
if (visionSourceMap == null) return;
|
||||||
+ "\" at \""
|
|
||||||
+ device.path
|
logger.info("Adding " + visionSourceMap.size() + " configs to VMM.");
|
||||||
+ "\" with USB ID \""
|
ConfigManager.getInstance().addCameraConfigurations(visionSourceMap);
|
||||||
+ device.vendorId
|
var addedSources = VisionModuleManager.getInstance().addSources(visionSourceMap);
|
||||||
+ ":"
|
addedSources.forEach(VisionModule::start);
|
||||||
+ device.productId
|
DataChangeService.getInstance()
|
||||||
+ "\"";
|
.publishEvent(
|
||||||
if (deviceBlacklist.contains(device.name)) {
|
new OutgoingUIEvent<>(
|
||||||
logger.info("Skipping blacklisted device - " + deviceInfoStr);
|
"fullsettings", ConfigManager.getInstance().getConfig().toHashMap()));
|
||||||
} else {
|
}
|
||||||
filteredDevices.add(device);
|
|
||||||
logger.info("Adding local video device - " + deviceInfoStr);
|
protected HashMap<VisionSource, List<CVPipelineSettings>> tryMatchUSBCamImpl() {
|
||||||
|
// Detect cameras using CSCore
|
||||||
|
List<UsbCameraInfo> connectedCameras =
|
||||||
|
new ArrayList<>(filterAllowedDevices(cameraInfoSupplier.get()));
|
||||||
|
|
||||||
|
// Remove all known devices
|
||||||
|
var notYetLoadedCams = new ArrayList<UsbCameraInfo>();
|
||||||
|
for (var connectedCam : connectedCameras) {
|
||||||
|
boolean cameraIsUnknown = true;
|
||||||
|
for (UsbCameraInfo knownCam : this.knownUsbCameras) {
|
||||||
|
if (usbCamEquals(knownCam, connectedCam)) {
|
||||||
|
cameraIsUnknown = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cameraIsUnknown) {
|
||||||
|
notYetLoadedCams.add(connectedCam);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return filteredDevices;
|
|
||||||
|
if (notYetLoadedCams.isEmpty()) return null;
|
||||||
|
|
||||||
|
if (connectedCameras.isEmpty()) {
|
||||||
|
logger.warn(
|
||||||
|
"No USB cameras were detected! Check that all cameras are connected, and that the path is correct.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
logger.debug("Matching " + notYetLoadedCams.size() + " new cameras!");
|
||||||
|
|
||||||
|
// Sort out just the USB cams
|
||||||
|
var usbCamConfigs = new ArrayList<>();
|
||||||
|
for (var config : unmatchedLoadedConfigs) {
|
||||||
|
if (config.cameraType == CameraType.UsbCamera) usbCamConfigs.add(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug prints
|
||||||
|
for (var info : notYetLoadedCams) {
|
||||||
|
logger.info("Adding local video device - \"" + info.name + "\" at \"" + info.path + "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!usbCamConfigs.isEmpty())
|
||||||
|
logger.debug("Trying to match " + usbCamConfigs.size() + " unmatched configs...");
|
||||||
|
|
||||||
|
// Match camera configs to physical cameras
|
||||||
|
var matchedCameras = matchUSBCameras(notYetLoadedCams, unmatchedLoadedConfigs);
|
||||||
|
unmatchedLoadedConfigs.removeAll(matchedCameras);
|
||||||
|
if (!unmatchedLoadedConfigs.isEmpty())
|
||||||
|
logger.warn(
|
||||||
|
() ->
|
||||||
|
"After matching, "
|
||||||
|
+ unmatchedLoadedConfigs.size()
|
||||||
|
+ " configs remained unmatched. Is your camera disconnected?");
|
||||||
|
|
||||||
|
// We add the matched cameras to the known camera list
|
||||||
|
for (var cam : notYetLoadedCams) {
|
||||||
|
if (this.knownUsbCameras.stream().noneMatch(it -> usbCamEquals(it, cam))) {
|
||||||
|
this.knownUsbCameras.add(cam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matchedCameras.isEmpty()) return null;
|
||||||
|
|
||||||
|
// Turn these camera configs into vision sources
|
||||||
|
var sources = loadVisionSourcesFromCamConfigs(matchedCameras);
|
||||||
|
|
||||||
|
// These sources can be turned into USB cameras, which can be added to the config manager
|
||||||
|
var visionSourceMap = new HashMap<VisionSource, List<CVPipelineSettings>>();
|
||||||
|
for (var src : sources) {
|
||||||
|
var usbSrc = (USBCameraSource) src;
|
||||||
|
visionSourceMap.put(usbSrc, usbSrc.configuration.pipelineSettings);
|
||||||
|
logger.debug(
|
||||||
|
() ->
|
||||||
|
"Matched config for camera \""
|
||||||
|
+ src.getFrameProvider().getName()
|
||||||
|
+ "\" and loaded "
|
||||||
|
+ usbSrc.configuration.pipelineSettings.size()
|
||||||
|
+ " pipelines");
|
||||||
|
}
|
||||||
|
return visionSourceMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,11 +172,12 @@ public class VisionSourceManager {
|
|||||||
*
|
*
|
||||||
* @param detectedCamInfos Information about currently connected USB cameras.
|
* @param detectedCamInfos Information about currently connected USB cameras.
|
||||||
* @param loadedUsbCamConfigs The USB {@link CameraConfiguration}s loaded from disk.
|
* @param loadedUsbCamConfigs The USB {@link CameraConfiguration}s loaded from disk.
|
||||||
|
* @return the matched configurations.
|
||||||
*/
|
*/
|
||||||
private static List<CameraConfiguration> matchUSBCameras(
|
private List<CameraConfiguration> matchUSBCameras(
|
||||||
List<UsbCameraInfo> detectedCamInfos, List<CameraConfiguration> loadedUsbCamConfigs) {
|
List<UsbCameraInfo> detectedCamInfos, List<CameraConfiguration> loadedUsbCamConfigs) {
|
||||||
ArrayList<UsbCameraInfo> detectedCameraList = new ArrayList<>(detectedCamInfos);
|
var detectedCameraList = new ArrayList<>(detectedCamInfos);
|
||||||
List<CameraConfiguration> cameraConfigurations = new ArrayList<>();
|
ArrayList<CameraConfiguration> cameraConfigurations = new ArrayList<>();
|
||||||
|
|
||||||
// loop over all the configs loaded from disk
|
// loop over all the configs loaded from disk
|
||||||
for (CameraConfiguration config : loadedUsbCamConfigs) {
|
for (CameraConfiguration config : loadedUsbCamConfigs) {
|
||||||
@@ -160,6 +244,35 @@ public class VisionSourceManager {
|
|||||||
return cameraConfigurations;
|
return cameraConfigurations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<VisionSource> loadVisionSourcesFromCamConfigs(List<CameraConfiguration> camConfigs) {
|
||||||
|
List<VisionSource> usbCameraSources = new ArrayList<>();
|
||||||
|
camConfigs.forEach(configuration -> usbCameraSources.add(new USBCameraSource(configuration)));
|
||||||
|
return usbCameraSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<UsbCameraInfo> filterAllowedDevices(List<UsbCameraInfo> allDevices) {
|
||||||
|
List<UsbCameraInfo> filteredDevices = new ArrayList<>();
|
||||||
|
for (var device : allDevices) {
|
||||||
|
if (deviceBlacklist.contains(device.name)) {
|
||||||
|
logger.trace(
|
||||||
|
"Skipping blacklisted device: \"" + device.name + "\" at \"" + device.path + "\"");
|
||||||
|
} else {
|
||||||
|
filteredDevices.add(device);
|
||||||
|
logger.trace(
|
||||||
|
"Adding local video device - \"" + device.name + "\" at \"" + device.path + "\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filteredDevices;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean usbCamEquals(UsbCameraInfo a, UsbCameraInfo b) {
|
||||||
|
return a.path.equals(b.path)
|
||||||
|
&& a.dev == b.dev
|
||||||
|
&& a.name.equals(b.name)
|
||||||
|
&& a.productId == b.productId
|
||||||
|
&& a.vendorId == b.vendorId;
|
||||||
|
}
|
||||||
|
|
||||||
// Remove all non-ASCII characters
|
// Remove all non-ASCII characters
|
||||||
private static String cameraNameToBaseName(String cameraName) {
|
private static String cameraNameToBaseName(String cameraName) {
|
||||||
return cameraName.replaceAll("[^\\x00-\\x7F]", "");
|
return cameraName.replaceAll("[^\\x00-\\x7F]", "");
|
||||||
@@ -170,13 +283,6 @@ public class VisionSourceManager {
|
|||||||
return baseName.replaceAll(" ", "_");
|
return baseName.replaceAll(" ", "_");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<VisionSource> loadVisionSourcesFromCamConfigs(
|
|
||||||
List<CameraConfiguration> camConfigs) {
|
|
||||||
List<VisionSource> usbCameraSources = new ArrayList<>();
|
|
||||||
camConfigs.forEach(configuration -> usbCameraSources.add(new USBCameraSource(configuration)));
|
|
||||||
return usbCameraSources;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a given config list contains the given unique name.
|
* Check if a given config list contains the given unique name.
|
||||||
*
|
*
|
||||||
@@ -184,7 +290,7 @@ public class VisionSourceManager {
|
|||||||
* @param uniqueName The unique name.
|
* @param uniqueName The unique name.
|
||||||
* @return If the list of configs contains the unique name.
|
* @return If the list of configs contains the unique name.
|
||||||
*/
|
*/
|
||||||
private static boolean containsName(
|
private boolean containsName(
|
||||||
final List<CameraConfiguration> configList, final String uniqueName) {
|
final List<CameraConfiguration> configList, final String uniqueName) {
|
||||||
return configList.stream()
|
return configList.stream()
|
||||||
.anyMatch(configuration -> configuration.uniqueName.equals(uniqueName));
|
.anyMatch(configuration -> configuration.uniqueName.equals(uniqueName));
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ public class ConfigTest {
|
|||||||
TestUtils.loadLibraries();
|
TestUtils.loadLibraries();
|
||||||
configMgr = new ConfigManager(Path.of("testconfigdir"));
|
configMgr = new ConfigManager(Path.of("testconfigdir"));
|
||||||
configMgr.load();
|
configMgr.load();
|
||||||
|
|
||||||
Logger.setLevel(LogGroup.General, LogLevel.TRACE);
|
Logger.setLevel(LogGroup.General, LogLevel.TRACE);
|
||||||
|
|
||||||
REFLECTIVE_PIPELINE_SETTINGS = new ReflectivePipelineSettings();
|
REFLECTIVE_PIPELINE_SETTINGS = new ReflectivePipelineSettings();
|
||||||
|
|||||||
@@ -114,12 +114,12 @@ public class VisionModuleManagerTest {
|
|||||||
TestUtils.WPI2019Image.FOV)),
|
TestUtils.WPI2019Image.FOV)),
|
||||||
List.of());
|
List.of());
|
||||||
|
|
||||||
VisionModuleManager.getInstance().addSources(sources);
|
var modules = VisionModuleManager.getInstance().addSources(sources);
|
||||||
var module0DataConsumer = new TestDataConsumer();
|
var module0DataConsumer = new TestDataConsumer();
|
||||||
|
|
||||||
VisionModuleManager.getInstance().visionModules.get(0).addResultConsumer(module0DataConsumer);
|
VisionModuleManager.getInstance().visionModules.get(0).addResultConsumer(module0DataConsumer);
|
||||||
|
|
||||||
VisionModuleManager.getInstance().startModules();
|
modules.forEach(VisionModule::start);
|
||||||
|
|
||||||
sleep(500);
|
sleep(500);
|
||||||
|
|
||||||
|
|||||||
@@ -17,14 +17,15 @@
|
|||||||
|
|
||||||
package org.photonvision.vision.processes;
|
package org.photonvision.vision.processes;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import edu.wpi.cscore.UsbCameraInfo;
|
import edu.wpi.cscore.UsbCameraInfo;
|
||||||
import java.util.List;
|
import java.util.ArrayList;
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.photonvision.common.configuration.CameraConfiguration;
|
import org.photonvision.common.configuration.CameraConfiguration;
|
||||||
import org.photonvision.common.util.TestUtils;
|
import org.photonvision.common.util.TestUtils;
|
||||||
import org.photonvision.vision.camera.USBCameraSource;
|
|
||||||
|
|
||||||
public class VisionSourceManagerTest {
|
public class VisionSourceManagerTest {
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
@@ -32,39 +33,32 @@ public class VisionSourceManagerTest {
|
|||||||
TestUtils.loadLibraries();
|
TestUtils.loadLibraries();
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<UsbCameraInfo> usbCameraInfos =
|
|
||||||
List.of(
|
|
||||||
new UsbCameraInfo(0, "/this-is-a-real-path", "cameraByPath", new String[] {""}, 1, 1),
|
|
||||||
new UsbCameraInfo(2, "/this-is-a-fake-path1", "cameraById", new String[] {""}, 420, 1),
|
|
||||||
new UsbCameraInfo(1, "/this-is-a-real-path2", "cameraByPath", new String[] {""}, 1, 1),
|
|
||||||
new UsbCameraInfo(3, "/this-is-a-fake-path2", "cameraById", new String[] {""}, 420, 1),
|
|
||||||
new UsbCameraInfo(4, "/fake-path420", "notExisting", new String[] {""}, 420, 1),
|
|
||||||
new UsbCameraInfo(5, "/fake-path421", "notExisting", new String[] {""}, 420, 1));
|
|
||||||
|
|
||||||
final List<CameraConfiguration> camConfig =
|
|
||||||
List.of(
|
|
||||||
new CameraConfiguration("cameraByPath", "dank meme", "good name", "/this-is-a-real-path"),
|
|
||||||
new CameraConfiguration(
|
|
||||||
"cameraByPath", "dank meme2", "very original", "/this-is-a-real-path2"),
|
|
||||||
new CameraConfiguration("cameraById", "camera", "my camera", "2"),
|
|
||||||
new CameraConfiguration("cameraById", "camera2", "my camera", "3"));
|
|
||||||
|
|
||||||
final List<USBCameraSource> usbCameraSources =
|
|
||||||
List.of(
|
|
||||||
new USBCameraSource(camConfig.get(0)),
|
|
||||||
new USBCameraSource(camConfig.get(1)),
|
|
||||||
new USBCameraSource(camConfig.get(2)),
|
|
||||||
new USBCameraSource(camConfig.get(3)),
|
|
||||||
new USBCameraSource(
|
|
||||||
new CameraConfiguration("notExisting", "notExisting", "notExisting", "4")),
|
|
||||||
new USBCameraSource(
|
|
||||||
new CameraConfiguration("notExisting", "notExisting (1)", "notExisting (1)", "5")));
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void visionSourceTest() {
|
public void visionSourceTest() {
|
||||||
List<VisionSource> i = VisionSourceManager.loadAllSources(camConfig, usbCameraInfos);
|
var inst = new VisionSourceManager();
|
||||||
for (var source : i) {
|
var infoList = new ArrayList<UsbCameraInfo>();
|
||||||
Assertions.assertEquals(source, usbCameraSources.get(i.indexOf(source)));
|
inst.cameraInfoSupplier = () -> infoList;
|
||||||
}
|
|
||||||
|
inst.tryMatchUSBCamImpl();
|
||||||
|
var config = new CameraConfiguration("secondTestVideo", "dev/video1");
|
||||||
|
UsbCameraInfo info1 = new UsbCameraInfo(0, "dev/video0", "testVideo", new String[0], 1, 2);
|
||||||
|
infoList.add(info1);
|
||||||
|
|
||||||
|
inst.registerLoadedConfigs(config);
|
||||||
|
inst.tryMatchUSBCamImpl();
|
||||||
|
inst.tryMatchUSBCamImpl();
|
||||||
|
|
||||||
|
assertTrue(inst.knownUsbCameras.contains(info1));
|
||||||
|
assertEquals(1, inst.unmatchedLoadedConfigs.size());
|
||||||
|
|
||||||
|
UsbCameraInfo info2 =
|
||||||
|
new UsbCameraInfo(0, "dev/video1", "secondTestVideo", new String[0], 2, 1);
|
||||||
|
infoList.add(info2);
|
||||||
|
|
||||||
|
inst.tryMatchUSBCamImpl();
|
||||||
|
|
||||||
|
assertTrue(inst.knownUsbCameras.contains(info2));
|
||||||
|
assertEquals(2, inst.knownUsbCameras.size());
|
||||||
|
assertEquals(0, inst.unmatchedLoadedConfigs.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user