From f92bf670ded52b59a00352a4a49c277f01bae305 Mon Sep 17 00:00:00 2001 From: Ori agranat Date: Wed, 17 Jun 2020 11:12:19 -0700 Subject: [PATCH] Source manager (#99) * Add some config stuff, run format * Create JacksonUtils.java * Fix deserialization, run wpiformat * initial work on source manager * work on USB camera class * wip USB Camera * rename root folder * added USB Camera Source * Fix some errors, run spotless * finished VIsion source manager * bugfix unversioned git files and added default cam config constructor * Apply spotless, add simpler CameraConfiguration ctor * [WIP] unit tests * Fixed camera mocking * added equal test for class * finalized test * added another cam to test & rebase * test bugfix * Better handle nonexistant files * removed camera validation and mockito * Update WPI maven repo to dev, change to version with VideoCapture fix * added Quirky camera class * remove name check for cam quirk , apply spotless * added quirk test Co-authored-by: Matt Co-authored-by: Banks Troutman --- chameleon-server/.gitignore | 2 +- chameleon-server/build.gradle | 34 +---- .../configuration/CameraConfiguration.java | 40 +++++- .../configuration/ChameleonConfiguration.java | 2 +- .../common/configuration/ConfigManager.java | 47 ++++--- .../common/configuration/StreamDivisor.java | 14 ++ .../common/vision/camera/CameraQuirks.java | 5 + .../common/vision/camera/CameraType.java | 6 + .../common/vision/camera/QuirkyCamera.java | 35 +++++ .../common/vision/camera/USBCamera.java | 5 - .../common/vision/camera/USBCameraSource.java | 122 ++++++++++++++++++ .../frame/provider/USBFrameProvider.java | 19 ++- .../common/vision/processes/VisionSource.java | 1 + .../vision/processes/VisionSourceManager.java | 94 ++++++++++++++ .../processes/VisionSourceSettables.java | 45 +++++-- .../common/configuration/ConfigTest.java | 5 +- .../common/vision/QuirkyCameraTest.java | 22 ++++ .../processes/VisionModuleManagerTest.java | 13 +- .../processes/VisionSourceManagerTest.java | 54 ++++++++ 19 files changed, 491 insertions(+), 74 deletions(-) create mode 100644 chameleon-server/src/main/java/com/chameleonvision/common/configuration/StreamDivisor.java create mode 100644 chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/CameraQuirks.java create mode 100644 chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/CameraType.java create mode 100644 chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/QuirkyCamera.java delete mode 100644 chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/USBCamera.java create mode 100644 chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/USBCameraSource.java create mode 100644 chameleon-server/src/main/java/com/chameleonvision/common/vision/processes/VisionSourceManager.java create mode 100644 chameleon-server/src/test/java/com/chameleonvision/common/vision/QuirkyCameraTest.java create mode 100644 chameleon-server/src/test/java/com/chameleonvision/common/vision/processes/VisionSourceManagerTest.java diff --git a/chameleon-server/.gitignore b/chameleon-server/.gitignore index 87e45e9ff..429fde9ea 100644 --- a/chameleon-server/.gitignore +++ b/chameleon-server/.gitignore @@ -3,4 +3,4 @@ bin/* .project .classpath *.prefs -chameleonVision \ No newline at end of file +chameleon-vision \ No newline at end of file diff --git a/chameleon-server/build.gradle b/chameleon-server/build.gradle index 7f5047736..4942b2e8e 100644 --- a/chameleon-server/build.gradle +++ b/chameleon-server/build.gradle @@ -15,13 +15,14 @@ shadowJar { sourceCompatibility = 11 repositories { + jcenter() mavenCentral() maven { - url = 'https://frcmaven.wpi.edu:443/artifactory/release' + url = 'https://frcmaven.wpi.edu:443/artifactory/development' } } ext { - wpilibVersion = '2020.2.2' + wpilibVersion = '2020.3.2-75-g1557a4c' openCVVersion = '3.4.7-2' } @@ -68,35 +69,6 @@ dependencies { testCompile "ch.qos.logback:logback-classic:0.9.26" - // javacv (ew) -// def withoutJunk = { -// exclude group: 'org.bytedeco', module: 'artoolkitplus' -// exclude group: 'org.bytedeco', module: 'artoolkitplus-platform' -// exclude group: 'org.bytedeco', module: 'flandmark' -// exclude group: 'org.bytedeco', module: 'flandmark-platform' -// exclude group: 'org.bytedeco', module: 'flycapture' -// exclude group: 'org.bytedeco', module: 'flycapture-platform' -// exclude group: 'org.bytedeco', module: 'leptonica' -// exclude group: 'org.bytedeco', module: 'leptonica-platform' -// exclude group: 'org.bytedeco', module: 'libdc1394' -// exclude group: 'org.bytedeco', module: 'libdc1394-platform' -// exclude group: 'org.bytedeco', module: 'libfreenect' -// exclude group: 'org.bytedeco', module: 'libfreenect-platform' -// exclude group: 'org.bytedeco', module: 'libfreenect2' -// exclude group: 'org.bytedeco', module: 'libfreenect2-platform' -// exclude group: 'org.bytedeco', module: 'librealsense' -// exclude group: 'org.bytedeco', module: 'librealsense-platform' -// exclude group: 'org.bytedeco', module: 'librealsense2' -// exclude group: 'org.bytedeco', module: 'librealsense2-platform' -// exclude group: 'org.bytedeco', module: 'openblas' -// exclude group: 'org.bytedeco', module: 'openblas-platform' -// exclude group: 'org.bytedeco', module: 'tesseract' -// exclude group: 'org.bytedeco', module: 'tesseract-platform' -// exclude group: 'org.bytedeco', module: 'ffmpeg' -// exclude group: 'org.bytedeco', module: 'ffmpeg-platform' -// } -// compile 'org.bytedeco:javacv-platform:1.5.2', withoutJunk - // test stuff testImplementation('org.junit.jupiter:junit-jupiter:5.6.0') } diff --git a/chameleon-server/src/main/java/com/chameleonvision/common/configuration/CameraConfiguration.java b/chameleon-server/src/main/java/com/chameleonvision/common/configuration/CameraConfiguration.java index afd6d18ca..b9a9f09c4 100644 --- a/chameleon-server/src/main/java/com/chameleonvision/common/configuration/CameraConfiguration.java +++ b/chameleon-server/src/main/java/com/chameleonvision/common/configuration/CameraConfiguration.java @@ -3,20 +3,58 @@ package com.chameleonvision.common.configuration; import com.chameleonvision.common.calibration.CameraCalibrationCoefficients; import com.chameleonvision.common.logging.LogGroup; import com.chameleonvision.common.logging.Logger; +import com.chameleonvision.common.vision.camera.CameraType; import com.chameleonvision.common.vision.pipeline.CVPipelineSettings; import com.chameleonvision.common.vision.pipeline.DriverModePipelineSettings; import com.chameleonvision.common.vision.processes.PipelineManager; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import java.util.ArrayList; import java.util.List; public class CameraConfiguration { private static final Logger logger = new Logger(CameraConfiguration.class, LogGroup.Camera); - public String name = ""; + public String baseName = ""; + public String uniqueName = ""; public String nickname = ""; public double FOV = 70; + public String path = ""; + public CameraType cameraType = CameraType.UsbCamera; public CameraCalibrationCoefficients calibration; + public List CameraLEDs = new ArrayList<>(); + + public CameraConfiguration(String baseName, String path) { + this(baseName, baseName, baseName, path); + } + + public CameraConfiguration(String baseName, String uniqueName, String nickname, String path) { + this.baseName = baseName; + this.uniqueName = uniqueName; + this.nickname = nickname; + this.path = path; + } + + @JsonCreator + public CameraConfiguration( + @JsonProperty("baseName") String baseName, + @JsonProperty("uniqueName") String uniqueName, + @JsonProperty("nickname") String nickname, + @JsonProperty("FOV") double FOV, + @JsonProperty("path") String path, + @JsonProperty("cameraType") CameraType cameraType, + @JsonProperty("calibration") CameraCalibrationCoefficients calibration, + @JsonProperty("CameraLEDs") List cameraLEDs) { + this.baseName = baseName; + this.uniqueName = uniqueName; + this.nickname = nickname; + this.FOV = FOV; + this.path = path; + this.cameraType = cameraType; + this.calibration = calibration; + this.CameraLEDs = cameraLEDs; + } @JsonIgnore // this ignores the pipes as we serialize them to their own subfolder public final List pipelineSettings = new ArrayList<>(); diff --git a/chameleon-server/src/main/java/com/chameleonvision/common/configuration/ChameleonConfiguration.java b/chameleon-server/src/main/java/com/chameleonvision/common/configuration/ChameleonConfiguration.java index 988d360b7..1dc47162d 100644 --- a/chameleon-server/src/main/java/com/chameleonvision/common/configuration/ChameleonConfiguration.java +++ b/chameleon-server/src/main/java/com/chameleonvision/common/configuration/ChameleonConfiguration.java @@ -16,7 +16,7 @@ public class ChameleonConfiguration { } public void addCameraConfig(CameraConfiguration config) { - addCameraConfig(config.name, config); + addCameraConfig(config.uniqueName, config); } public void addCameraConfig(String name, CameraConfiguration config) { diff --git a/chameleon-server/src/main/java/com/chameleonvision/common/configuration/ConfigManager.java b/chameleon-server/src/main/java/com/chameleonvision/common/configuration/ConfigManager.java index 3cc3ac581..c611153f2 100644 --- a/chameleon-server/src/main/java/com/chameleonvision/common/configuration/ConfigManager.java +++ b/chameleon-server/src/main/java/com/chameleonvision/common/configuration/ConfigManager.java @@ -38,7 +38,7 @@ public class ConfigManager { } protected static Path getRootFolder() { - return Path.of("chameleon-vision"); // TODO change root folder? + return Path.of("chameleon-vision"); } private ConfigManager(Path rootFolder) { @@ -64,25 +64,36 @@ public class ConfigManager { HardwareConfig hardwareConfig; NetworkConfig networkConfig; - try { - hardwareConfig = JacksonUtils.deserialize(hardwareConfigFile.toPath(), HardwareConfig.class); - if (hardwareConfig == null) { + if (hardwareConfigFile.exists()) { + try { + hardwareConfig = + JacksonUtils.deserialize(hardwareConfigFile.toPath(), HardwareConfig.class); + if (hardwareConfig == null) { + logger.error("Could not deserialize hardware config! Loading defaults"); + hardwareConfig = new HardwareConfig(); + } + } catch (IOException e) { logger.error("Could not deserialize hardware config! Loading defaults"); hardwareConfig = new HardwareConfig(); } - } catch (IOException e) { - logger.error("Could not deserialize hardware config! Loading defaults"); + } else { + logger.info("Hardware config does not exist! Loading defaults"); hardwareConfig = new HardwareConfig(); } - try { - networkConfig = JacksonUtils.deserialize(networkConfigFile.toPath(), NetworkConfig.class); - if (networkConfig == null) { + if (networkConfigFile.exists()) { + try { + networkConfig = JacksonUtils.deserialize(networkConfigFile.toPath(), NetworkConfig.class); + if (networkConfig == null) { + logger.error("Could not deserialize network config! Loading defaults"); + networkConfig = new NetworkConfig(); + } + } catch (IOException e) { logger.error("Could not deserialize network config! Loading defaults"); networkConfig = new NetworkConfig(); } - } catch (IOException e) { - logger.error("Could not deserialize network config! Loading defaults"); + } else { + logger.info("Network config file does not exist! Loading defaults"); networkConfig = new NetworkConfig(); } @@ -164,11 +175,17 @@ public class ConfigManager { for (var subdir : subdirectories) { var cameraConfigPath = Path.of(subdir.toString(), "config.json"); - CameraConfiguration loadedConfig = - JacksonUtils.deserialize(cameraConfigPath.toAbsolutePath(), CameraConfiguration.class); - if (loadedConfig == null) { + CameraConfiguration loadedConfig = null; + try { + loadedConfig = + JacksonUtils.deserialize( + cameraConfigPath.toAbsolutePath(), CameraConfiguration.class); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + if (loadedConfig == null) { // If the file could not be deserialized logger.warn("Could not load camera " + subdir + "'s config.json! Loading " + "default"); - loadedConfig = new CameraConfiguration(); + continue; // TODO how do we later try to load this camera if it gets reconnected? } // At this point we have only loaded the base stuff diff --git a/chameleon-server/src/main/java/com/chameleonvision/common/configuration/StreamDivisor.java b/chameleon-server/src/main/java/com/chameleonvision/common/configuration/StreamDivisor.java new file mode 100644 index 000000000..cca8dc78d --- /dev/null +++ b/chameleon-server/src/main/java/com/chameleonvision/common/configuration/StreamDivisor.java @@ -0,0 +1,14 @@ +package com.chameleonvision.common.configuration; + +public enum StreamDivisor { + NONE(1), + HALF(2), + QUARTER(4), + SIXTH(6); + + public final Integer value; + + StreamDivisor(int value) { + this.value = value; + } +} diff --git a/chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/CameraQuirks.java b/chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/CameraQuirks.java new file mode 100644 index 000000000..404475d46 --- /dev/null +++ b/chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/CameraQuirks.java @@ -0,0 +1,5 @@ +package com.chameleonvision.common.vision.camera; + +public enum CameraQuirks { + Gain +} diff --git a/chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/CameraType.java b/chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/CameraType.java new file mode 100644 index 000000000..44b7781c7 --- /dev/null +++ b/chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/CameraType.java @@ -0,0 +1,6 @@ +package com.chameleonvision.common.vision.camera; + +public enum CameraType { + UsbCamera, + HttpCamera +} diff --git a/chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/QuirkyCamera.java b/chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/QuirkyCamera.java new file mode 100644 index 000000000..bcf7e1c7b --- /dev/null +++ b/chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/QuirkyCamera.java @@ -0,0 +1,35 @@ +package com.chameleonvision.common.vision.camera; + +import java.util.ArrayList; +import java.util.List; + +public class QuirkyCamera { + public static final List quirkyCameras = + List.of( + // ps3 eye + new QuirkyCamera(0x1415, 0x2000, "PS3Eye", List.of(CameraQuirks.Gain))); + + public final int usbVid; + public final int usbPid; + public final String name; + public List quirks; + + public QuirkyCamera(int usbVid, int usbPid, String baseName, List quirks) { + this.usbVid = usbVid; + this.usbPid = usbPid; + this.name = baseName; + this.quirks = quirks; + } + + public QuirkyCamera(int usbVid, int usbPid, String baseName) { + this(usbVid, usbPid, baseName, new ArrayList<>()); + QuirkyCamera quirky = + quirkyCameras.stream() + .filter(quirkyCamera -> quirkyCamera.usbPid == usbPid && quirkyCamera.usbVid == usbVid) + .findFirst() + .orElse(null); + if (quirky != null) { + this.quirks = quirky.quirks; + } + } +} diff --git a/chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/USBCamera.java b/chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/USBCamera.java deleted file mode 100644 index a20e15b55..000000000 --- a/chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/USBCamera.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.chameleonvision.common.vision.camera; - -import com.chameleonvision.common.vision.frame.provider.USBFrameProvider; - -public class USBCamera extends USBFrameProvider {} diff --git a/chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/USBCameraSource.java b/chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/USBCameraSource.java new file mode 100644 index 000000000..a29f5ce5d --- /dev/null +++ b/chameleon-server/src/main/java/com/chameleonvision/common/vision/camera/USBCameraSource.java @@ -0,0 +1,122 @@ +package com.chameleonvision.common.vision.camera; + +import com.chameleonvision.common.configuration.CameraConfiguration; +import com.chameleonvision.common.vision.frame.FrameProvider; +import com.chameleonvision.common.vision.frame.provider.USBFrameProvider; +import com.chameleonvision.common.vision.processes.VisionSource; +import com.chameleonvision.common.vision.processes.VisionSourceSettables; +import edu.wpi.cscore.CvSink; +import edu.wpi.cscore.UsbCamera; +import edu.wpi.cscore.VideoMode; +import edu.wpi.first.cameraserver.CameraServer; +import java.util.*; + +public class USBCameraSource implements VisionSource { + private final UsbCamera camera; + private final USBCameraSettables usbCameraSettables; + private final USBFrameProvider usbFrameProvider; + private final CameraConfiguration configuration; + + private final QuirkyCamera cameraQuirks; + + public USBCameraSource(CameraConfiguration config) { + this.configuration = config; + this.camera = new UsbCamera(config.nickname, config.path); + this.cameraQuirks = + new QuirkyCamera(camera.getInfo().productId, camera.getInfo().vendorId, config.baseName); + CvSink cvSink = CameraServer.getInstance().getVideo(this.camera); + this.usbCameraSettables = new USBCameraSettables(config); + this.usbFrameProvider = + new USBFrameProvider(cvSink, usbCameraSettables.getFrameStaticProperties()); + } + + @Override + public boolean equals(Object obj) { + if (obj.getClass() != USBCameraSource.class) { + return false; + } + USBCameraSource tmp = (USBCameraSource) obj; + boolean i = this.cameraQuirks.quirks.equals(tmp.cameraQuirks.quirks); + + boolean r = this.configuration.uniqueName.equals(tmp.configuration.uniqueName); + boolean c = this.configuration.baseName.equals(tmp.configuration.baseName); + boolean j = this.configuration.nickname.equals(tmp.configuration.nickname); + + boolean k = this.camera.getInfo().name.equals(tmp.camera.getInfo().name); + boolean x = this.camera.getInfo().productId == tmp.camera.getInfo().productId; + boolean y = this.camera.getInfo().vendorId == tmp.camera.getInfo().vendorId; + var t = i && r && c && j && k && x && y; + return t; + } + + @Override + public FrameProvider getFrameProvider() { + return usbFrameProvider; + } + + @Override + public VisionSourceSettables getSettables() { + return this.usbCameraSettables; + } + + public class USBCameraSettables extends VisionSourceSettables { + protected USBCameraSettables(CameraConfiguration configuration) { + super(configuration); + } + + @Override + public int getExposure() { + return camera.getProperty("exposure").get(); + } + + @Override + public void setExposure(int exposure) { + camera.setExposureManual(exposure); + } + + @Override + public int getBrightness() { + return camera.getBrightness(); + } + + @Override + public void setBrightness(int brightness) { + camera.setBrightness(brightness); + } + + @Override + public int getGain() { + return camera.getProperty("gain").get(); + } + + @Override + public void setGain(int gain) { + if (cameraQuirks.quirks.contains(CameraQuirks.Gain)) { + camera.getProperty("gain_automatic").set(0); + camera.getProperty("gain").set(gain); + } + } + + @Override + public VideoMode getCurrentVideoMode() { + return camera.getVideoMode(); + } + + @Override + public void setCurrentVideoMode(VideoMode videoMode) { + camera.setVideoMode(videoMode); + } + + @Override + public HashMap getAllVideoModes() { + if (videoModes == null) { + videoModes = new HashMap<>(); + List videoModesList = Arrays.asList(camera.enumerateVideoModes()); + for (VideoMode videoMode : videoModesList) { + videoModes.put(videoModesList.indexOf(videoMode), videoMode); + } + } + return videoModes; + } + } +} diff --git a/chameleon-server/src/main/java/com/chameleonvision/common/vision/frame/provider/USBFrameProvider.java b/chameleon-server/src/main/java/com/chameleonvision/common/vision/frame/provider/USBFrameProvider.java index 10cd35597..5fb7c153c 100644 --- a/chameleon-server/src/main/java/com/chameleonvision/common/vision/frame/provider/USBFrameProvider.java +++ b/chameleon-server/src/main/java/com/chameleonvision/common/vision/frame/provider/USBFrameProvider.java @@ -2,14 +2,29 @@ package com.chameleonvision.common.vision.frame.provider; import com.chameleonvision.common.vision.frame.Frame; import com.chameleonvision.common.vision.frame.FrameProvider; -import org.apache.commons.lang3.NotImplementedException; +import com.chameleonvision.common.vision.frame.FrameStaticProperties; +import com.chameleonvision.common.vision.opencv.CVMat; +import edu.wpi.cscore.CvSink; public class USBFrameProvider implements FrameProvider { private static int count = 0; + private CvSink cvSink; + private FrameStaticProperties frameStaticProperties; + private CVMat mat; + + public USBFrameProvider(CvSink sink, FrameStaticProperties frameStaticProperties) { + cvSink = sink; + this.frameStaticProperties = frameStaticProperties; + mat = new CVMat(); + } @Override public Frame get() { - throw new NotImplementedException(""); + if (mat != null && mat.getMat() != null) { + mat.release(); + } + long time = cvSink.grabFrame(mat.getMat()); + return new Frame(mat, time, frameStaticProperties); } @Override diff --git a/chameleon-server/src/main/java/com/chameleonvision/common/vision/processes/VisionSource.java b/chameleon-server/src/main/java/com/chameleonvision/common/vision/processes/VisionSource.java index a0a32f5b2..1e64fa568 100644 --- a/chameleon-server/src/main/java/com/chameleonvision/common/vision/processes/VisionSource.java +++ b/chameleon-server/src/main/java/com/chameleonvision/common/vision/processes/VisionSource.java @@ -3,6 +3,7 @@ package com.chameleonvision.common.vision.processes; import com.chameleonvision.common.vision.frame.FrameProvider; public interface VisionSource { + FrameProvider getFrameProvider(); VisionSourceSettables getSettables(); diff --git a/chameleon-server/src/main/java/com/chameleonvision/common/vision/processes/VisionSourceManager.java b/chameleon-server/src/main/java/com/chameleonvision/common/vision/processes/VisionSourceManager.java new file mode 100644 index 000000000..62050f782 --- /dev/null +++ b/chameleon-server/src/main/java/com/chameleonvision/common/vision/processes/VisionSourceManager.java @@ -0,0 +1,94 @@ +package com.chameleonvision.common.vision.processes; + +import com.chameleonvision.common.configuration.CameraConfiguration; +import com.chameleonvision.common.vision.camera.CameraType; +import com.chameleonvision.common.vision.camera.USBCameraSource; +import com.chameleonvision.common.vision.frame.provider.NetworkFrameProvider; +import edu.wpi.cscore.UsbCamera; +import edu.wpi.cscore.UsbCameraInfo; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import org.apache.commons.lang3.NotImplementedException; +import org.apache.commons.lang3.StringUtils; + +public class VisionSourceManager { + public List LoadAllSources(List camerasConfiguration) { + return LoadAllSources(camerasConfiguration, Arrays.asList(UsbCamera.enumerateUsbCameras())); + } + + public List LoadAllSources( + List camerasConfiguration, List usbCameraInfos) { + var UsbCamerasConfiguration = + camerasConfiguration.stream() + .filter(configuration -> configuration.cameraType == CameraType.UsbCamera) + .collect(Collectors.toList()); + // var HttpCamerasConfiguration = camerasConfiguration.stream().filter(configuration -> + // configuration.cameraType == CameraType.HttpCamera); + var matchedCameras = matchUSBCameras(usbCameraInfos, UsbCamerasConfiguration); + return loadUSBCameraSources(matchedCameras); + } + + private NetworkFrameProvider loadHTTPCamera(CameraConfiguration config) { + throw new NotImplementedException(""); + } + + private List matchUSBCameras( + List infos, List cameraConfigurationList) { + ArrayList loopableInfo = new ArrayList<>(infos); + List cameraConfigurations = new ArrayList<>(); + + for (CameraConfiguration config : cameraConfigurationList) { + UsbCameraInfo cameraInfo; + if (!StringUtils.isNumeric(config.path)) { + // matching by path + cameraInfo = + loopableInfo.stream() + .filter(usbCameraInfo -> usbCameraInfo.path.equals(config.path)) + .findFirst() + .orElse(null); + } else { + // match by index + cameraInfo = + loopableInfo.stream() + .filter(usbCameraInfo -> usbCameraInfo.dev == Integer.parseInt(config.path)) + .findFirst() + .orElse(null); + } + + if (cameraInfo != null) { + loopableInfo.remove(cameraInfo); + cameraConfigurations.add(config); + } + } + for (UsbCameraInfo info : loopableInfo) { + // create new camera config for all new cameras + String name = info.name.replaceAll("[^\\x00-\\x7F]", ""); + String uniqueName = name; + int suffix = 0; + + while (containsName(cameraConfigurations, uniqueName)) { + suffix++; + uniqueName = String.format("%s (%d)", uniqueName, suffix); + } + + CameraConfiguration configuration = + new CameraConfiguration(name, uniqueName, uniqueName, ((Integer) info.dev).toString()); + cameraConfigurations.add(configuration); + } + + return cameraConfigurations; + } + + private List loadUSBCameraSources(List configurations) { + List usbCameraSources = new ArrayList<>(); + configurations.forEach( + configuration -> usbCameraSources.add(new USBCameraSource(configuration))); + return usbCameraSources; + } + + private boolean containsName(final List list, final String name) { + return list.stream().anyMatch(configuration -> configuration.uniqueName.equals(name)); + } +} diff --git a/chameleon-server/src/main/java/com/chameleonvision/common/vision/processes/VisionSourceSettables.java b/chameleon-server/src/main/java/com/chameleonvision/common/vision/processes/VisionSourceSettables.java index 35f1d5eed..45fa747e0 100644 --- a/chameleon-server/src/main/java/com/chameleonvision/common/vision/processes/VisionSourceSettables.java +++ b/chameleon-server/src/main/java/com/chameleonvision/common/vision/processes/VisionSourceSettables.java @@ -1,24 +1,47 @@ package com.chameleonvision.common.vision.processes; +import com.chameleonvision.common.configuration.CameraConfiguration; +import com.chameleonvision.common.vision.frame.FrameStaticProperties; import edu.wpi.cscore.VideoMode; -import java.util.Dictionary; +import java.util.HashMap; -public interface VisionSourceSettables { - int getExposure(); +public abstract class VisionSourceSettables { + private CameraConfiguration configuration; - void setExposure(int exposure); + protected VisionSourceSettables(CameraConfiguration configuration) { + this.configuration = configuration; + } - int getBrightness(); + FrameStaticProperties frameStaticProperties; + protected HashMap videoModes; - void setBrightness(int brightness); + public abstract int getExposure(); - int getGain(); + public abstract void setExposure(int exposure); - void setGain(int gain); + public abstract int getBrightness(); - VideoMode getCurrentVideoMode(); + public abstract void setBrightness(int brightness); - void setCurrentVideoMode(VideoMode videoMode); + public abstract int getGain(); - Dictionary getAllVideoModes(); + public abstract void setGain(int gain); + + public abstract VideoMode getCurrentVideoMode(); + + public abstract void setCurrentVideoMode(VideoMode videoMode); + + public abstract HashMap getAllVideoModes(); + + public double getFOV() { + return configuration.FOV; + } + + public void setFOV(double fov) { + configuration.FOV = fov; + } + + public FrameStaticProperties getFrameStaticProperties() { + return frameStaticProperties; + } } diff --git a/chameleon-server/src/test/java/com/chameleonvision/common/configuration/ConfigTest.java b/chameleon-server/src/test/java/com/chameleonvision/common/configuration/ConfigTest.java index 2f08d5e7d..7caff1135 100644 --- a/chameleon-server/src/test/java/com/chameleonvision/common/configuration/ConfigTest.java +++ b/chameleon-server/src/test/java/com/chameleonvision/common/configuration/ConfigTest.java @@ -18,7 +18,8 @@ import org.junit.jupiter.api.*; public class ConfigTest { private static final ConfigManager configMgr; - private static final CameraConfiguration cameraConfig = new CameraConfiguration(); + private static final CameraConfiguration cameraConfig = + new CameraConfiguration("TestCamera", "/dev/video420"); private static final ReflectivePipelineSettings REFLECTIVE_PIPELINE_SETTINGS = new ReflectivePipelineSettings(); private static final ColoredShapePipelineSettings COLORED_SHAPE_PIPELINE_SETTINGS = @@ -34,8 +35,6 @@ public class ConfigTest { TestUtils.loadLibraries(); Logger.setLevel(LogGroup.General, Level.DE_PEST); - cameraConfig.name = "TestCamera"; - REFLECTIVE_PIPELINE_SETTINGS.pipelineNickname = "2019Tape"; REFLECTIVE_PIPELINE_SETTINGS.targetModel = TargetModel.get2019Target(); diff --git a/chameleon-server/src/test/java/com/chameleonvision/common/vision/QuirkyCameraTest.java b/chameleon-server/src/test/java/com/chameleonvision/common/vision/QuirkyCameraTest.java new file mode 100644 index 000000000..e4f007843 --- /dev/null +++ b/chameleon-server/src/test/java/com/chameleonvision/common/vision/QuirkyCameraTest.java @@ -0,0 +1,22 @@ +package com.chameleonvision.common.vision; + +import com.chameleonvision.common.vision.camera.CameraQuirks; +import com.chameleonvision.common.vision.camera.QuirkyCamera; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class QuirkyCameraTest { + @Test + public void ps3EyeTest() { + QuirkyCamera psEye = new QuirkyCamera(0x1415, 0x2000, "psEye"); + Assertions.assertEquals(psEye.quirks, List.of(CameraQuirks.Gain)); + } + + @Test + public void quirklessCameraTest() { + QuirkyCamera noQuirk = new QuirkyCamera(1234, 888, "empty"); + Assertions.assertEquals(noQuirk.quirks, new ArrayList<>()); + } +} diff --git a/chameleon-server/src/test/java/com/chameleonvision/common/vision/processes/VisionModuleManagerTest.java b/chameleon-server/src/test/java/com/chameleonvision/common/vision/processes/VisionModuleManagerTest.java index c4bc68b13..96887f7a8 100644 --- a/chameleon-server/src/test/java/com/chameleonvision/common/vision/processes/VisionModuleManagerTest.java +++ b/chameleon-server/src/test/java/com/chameleonvision/common/vision/processes/VisionModuleManagerTest.java @@ -1,5 +1,6 @@ package com.chameleonvision.common.vision.processes; +import com.chameleonvision.common.configuration.CameraConfiguration; import com.chameleonvision.common.datatransfer.DataConsumer; import com.chameleonvision.common.util.TestUtils; import com.chameleonvision.common.vision.frame.FrameProvider; @@ -7,7 +8,7 @@ import com.chameleonvision.common.vision.frame.provider.FileFrameProvider; import com.chameleonvision.common.vision.pipeline.CVPipelineResult; import edu.wpi.cscore.VideoMode; import java.util.ArrayList; -import java.util.Dictionary; +import java.util.HashMap; import org.junit.jupiter.api.*; public class VisionModuleManagerTest { @@ -33,11 +34,15 @@ public class VisionModuleManagerTest { @Override public VisionSourceSettables getSettables() { - return new TestSettables(); + return new TestSettables(new CameraConfiguration("", "", "", "")); } } - private static class TestSettables implements VisionSourceSettables { + private static class TestSettables extends VisionSourceSettables { + + protected TestSettables(CameraConfiguration configuration) { + super(configuration); + } @Override public int getExposure() { @@ -72,7 +77,7 @@ public class VisionModuleManagerTest { public void setCurrentVideoMode(VideoMode videoMode) {} @Override - public Dictionary getAllVideoModes() { + public HashMap getAllVideoModes() { return null; } } diff --git a/chameleon-server/src/test/java/com/chameleonvision/common/vision/processes/VisionSourceManagerTest.java b/chameleon-server/src/test/java/com/chameleonvision/common/vision/processes/VisionSourceManagerTest.java new file mode 100644 index 000000000..3cf48f51f --- /dev/null +++ b/chameleon-server/src/test/java/com/chameleonvision/common/vision/processes/VisionSourceManagerTest.java @@ -0,0 +1,54 @@ +package com.chameleonvision.common.vision.processes; + +import com.chameleonvision.common.configuration.CameraConfiguration; +import com.chameleonvision.common.util.TestUtils; +import com.chameleonvision.common.vision.camera.USBCameraSource; +import edu.wpi.cscore.UsbCameraInfo; +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class VisionSourceManagerTest { + @BeforeEach + public void init() { + TestUtils.loadLibraries(); + } + + final List 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 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 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 + public void visionSourceTest() { + VisionSourceManager visionSourceManager = new VisionSourceManager(); + List i = visionSourceManager.LoadAllSources(camConfig, usbCameraInfos); + for (var source : i) { + Assertions.assertEquals(source, usbCameraSources.get(i.indexOf(source))); + } + } +}