diff --git a/Main/src/main/java/com/chameleonvision/config/CameraConfig.java b/Main/src/main/java/com/chameleonvision/config/CameraConfig.java index e3ef280ab..fd0262cdf 100644 --- a/Main/src/main/java/com/chameleonvision/config/CameraConfig.java +++ b/Main/src/main/java/com/chameleonvision/config/CameraConfig.java @@ -67,7 +67,7 @@ public class CameraConfig { pipelineConfig.save(pipelines); } - void saveDriverMode(CVPipelineSettings driverMode) { + public void saveDriverMode(CVPipelineSettings driverMode) { try { JacksonHelper.serializer(getDriverModePath(), driverMode); } catch (IOException e) { diff --git a/Main/src/main/java/com/chameleonvision/vision/VisionManager.java b/Main/src/main/java/com/chameleonvision/vision/VisionManager.java index 399cee1a0..e7ad66c0f 100644 --- a/Main/src/main/java/com/chameleonvision/vision/VisionManager.java +++ b/Main/src/main/java/com/chameleonvision/vision/VisionManager.java @@ -24,6 +24,7 @@ public class VisionManager { private static final LinkedList loadedCameraConfigs = new LinkedList<>(); private static final LinkedList visionProcesses = new LinkedList<>(); + @SuppressWarnings("WeakerAccess") private static class VisionProcessManageable { public final int index; public final String name; @@ -85,7 +86,7 @@ public class VisionManager { CameraCapture camera = new USBCameraCapture(cameraJsonConfig); VisionProcess process = new VisionProcess(camera, cameraJsonConfig.name, config.pipelines); - process.setDriverModeSettings(config.drivermode); + process.pipelineManager.driverModePipeline.settings = config.drivermode; visionProcesses.add(new VisionProcessManageable(i, cameraJsonConfig.name, process)); } currentUIVisionProcess = getVisionProcessByIndex(0); @@ -101,16 +102,11 @@ public class VisionManager { return currentUIVisionProcess; } - private static CameraConfig getCameraConfig(VisionProcess process) { + public static CameraConfig getCameraConfig(VisionProcess process) { String cameraName = process.getCamera().getProperties().name; return Objects.requireNonNull(loadedCameraConfigs.stream().filter(x -> x.cameraConfig.name.equals(cameraName)).findFirst().orElse(null)).fileConfig; } - public static void addPipelineToCamera(CVPipelineSettings newPipeline, VisionProcess process) { - getCameraConfig(process).pipelineConfig.save(newPipeline); - process.addPipeline(newPipeline); - } - public static void setCurrentProcessByIndex(int processIndex) { if (processIndex > visionProcesses.size() - 1) { return; @@ -135,7 +131,7 @@ public class VisionManager { } public static List getCurrentCameraPipelineNicknames() { - return currentUIVisionProcess.getPipelines().stream().map(cvPipeline -> cvPipeline.settings.nickname).collect(Collectors.toList()); + return currentUIVisionProcess.pipelineManager.pipelines.stream().map(cvPipeline -> cvPipeline.settings.nickname).collect(Collectors.toList()); } @@ -143,7 +139,7 @@ public class VisionManager { visionProcesses.forEach((vpm) -> { VisionProcess process = vpm.visionProcess; String cameraName = process.getCamera().getProperties().name; - List pipelines = process.getPipelines().stream().map(cvPipeline -> cvPipeline.settings).collect(Collectors.toList()); + List pipelines = process.pipelineManager.pipelines.stream().map(cvPipeline -> cvPipeline.settings).collect(Collectors.toList()); CVPipelineSettings driverMode = process.getDriverModeSettings(); CameraJsonConfig config = CameraJsonConfig.fromVisionProcess(process); ConfigManager.saveCameraPipelines(cameraName, pipelines); @@ -162,16 +158,14 @@ public class VisionManager { } public static void saveCurrentCameraPipelines() { - List pipelineSettings = currentUIVisionProcess.getPipelines().stream().map(pipeline -> pipeline.settings).collect(Collectors.toList()); - ConfigManager.saveCameraPipelines(getCurrentCameraName(), pipelineSettings); + currentUIVisionProcess.pipelineManager.saveAllPipelines(); } public static void saveCurrentCameraDriverMode() { - CVPipelineSettings driverModeSettings = currentUIVisionProcess.getDriverModeSettings(); - ConfigManager.saveCameraDriverMode(getCurrentCameraName(), driverModeSettings); + currentUIVisionProcess.pipelineManager.saveDriverModeConfig(); } - public static List getCameraResolutionList(CameraCapture capture) { + private static List getCameraResolutionList(CameraCapture capture) { return capture.getProperties().getVideoModes().stream().map(Helpers::VideoModeToHashMap).collect(Collectors.toList()); } diff --git a/Main/src/main/java/com/chameleonvision/vision/VisionProcess.java b/Main/src/main/java/com/chameleonvision/vision/VisionProcess.java index 640ea8015..4776c6e99 100644 --- a/Main/src/main/java/com/chameleonvision/vision/VisionProcess.java +++ b/Main/src/main/java/com/chameleonvision/vision/VisionProcess.java @@ -29,6 +29,7 @@ public class VisionProcess { private final CameraStreamerRunnable streamRunnable; private final VisionProcessRunnable visionRunnable; public final CameraStreamer cameraStreamer; + public final PipelineManager pipelineManager; private CVPipeline currentPipeline; private int currentPipelineIndex = 0; @@ -56,15 +57,7 @@ public class VisionProcess { VisionProcess(CameraCapture cameraCapture, String name, List loadedPipelineSettings) { this.cameraCapture = cameraCapture; - if (loadedPipelineSettings == null || loadedPipelineSettings.size() == 0) { - pipelines.add(new CVPipeline2d("New Pipeline")); - } else { - for (CVPipelineSettings setting : loadedPipelineSettings) { - addPipeline(setting); - } - setPipeline(0, false); - } - + pipelineManager = new PipelineManager(this, loadedPipelineSettings); // Thread to put frames on the dashboard this.cameraStreamer = new CameraStreamer(cameraCapture, name); @@ -80,10 +73,16 @@ public class VisionProcess { public void start() { System.out.println("Starting NetworkTables."); initNT(defaultTable); + System.out.println("Starting vision thread."); - new Thread(visionRunnable).start(); + var visionThread = new Thread(visionRunnable); + visionThread.setName(getCamera().getProperties().name + " - Vision Thread"); + visionThread.start(); + System.out.println("Starting stream thread."); - new Thread(streamRunnable).start(); + var streamThread = new Thread(streamRunnable); + streamThread.setName(getCamera().getProperties().name + " - Stream Thread"); + streamThread.start(); } /** @@ -110,7 +109,7 @@ public class VisionProcess { ntDriveModeListenerID = ntDriverModeEntry.addListener(this::setDriverMode, EntryListenerFlags.kUpdate); ntPipelineListenerID = ntPipelineEntry.addListener(this::setPipeline, EntryListenerFlags.kUpdate); ntDriverModeEntry.setBoolean(false); - ntPipelineEntry.setNumber(0); + ntPipelineEntry.setNumber(pipelineManager.getCurrentPipelineIndex()); } private void setDriverMode(EntryNotification driverModeEntryNotification) { @@ -118,11 +117,7 @@ public class VisionProcess { } public void setDriverMode(boolean driverMode) { - if (driverMode) { - setPipelineInternal(driverModePipeline); - } else { - setPipeline(currentPipelineIndex, true); - } + pipelineManager.setDriverMode(driverMode); } /** @@ -133,38 +128,12 @@ public class VisionProcess { var wantedPipelineIndex = (int) notification.value.getDouble(); if (wantedPipelineIndex >= pipelines.size()) { - ntPipelineEntry.setNumber(currentPipelineIndex); + ntPipelineEntry.setNumber(pipelineManager.getCurrentPipelineIndex()); } else { - currentPipelineIndex = wantedPipelineIndex; - setPipeline(wantedPipelineIndex, true); + pipelineManager.setCurrentPipeline(wantedPipelineIndex); } } - public void setPipeline(int pipelineIndex, boolean updateUI) { - CVPipeline newPipeline = pipelines.get(pipelineIndex); - if (newPipeline != null) { - setPipelineInternal(newPipeline); - currentPipelineIndex = pipelineIndex; - - // update the configManager - if(ConfigManager.settings.currentCamera.equals(cameraCapture.getProperties().name)) { - ConfigManager.settings.currentPipeline = pipelineIndex; - - if (updateUI) { - HashMap pipeChange = new HashMap<>(); - pipeChange.put("currentPipeline", pipelineIndex); - SocketHandler.broadcastMessage(pipeChange); - SocketHandler.sendFullSettings(); - } - } - } - } - - private void setPipelineInternal(CVPipeline pipeline) { - currentPipeline = pipeline; - currentPipeline.initPipeline(cameraCapture); - } - private void updateUI(CVPipelineResult data) { if(cameraCapture.getProperties().name.equals(ConfigManager.settings.currentCamera)) { HashMap WebSend = new HashMap<>(); @@ -243,56 +212,16 @@ public class VisionProcess { return cameraCapture.getProperties().videoModes; } - public List getPipelines() { - return pipelines; - } - - public CVPipeline getCurrentPipeline() { - return currentPipeline; - } - - public int getCurrentPipelineIndex() { - return currentPipelineIndex; - } - - public void addBlankPipeline() { - // TODO: (2.1) add to UI option between 2d and 3d pipeline - var newPipeline = new CVPipeline2d(); -// if (pipelines.stream().filter(x -> x.settings.nickname.equals(newPipeline.settings.nickname)); - addPipeline(new CVPipeline2d()); - } - - public void addPipeline(CVPipeline pipeline) { - pipelines.add(pipeline); - } - - public void addPipeline(CVPipelineSettings settings) { - if (settings instanceof CVPipeline2dSettings) { - addPipeline(new CVPipeline2d((CVPipeline2dSettings) settings)); - } - } - public void deletePipeline(int index) { - pipelines.remove(index); - } - public CameraCapture getCamera() { return cameraCapture; } - public boolean getDriverMode() { - return (currentPipeline == driverModePipeline); - } - public void setDriverModeSettings(CVPipelineSettings settings) { - driverModePipeline.settings = settings; + pipelineManager.driverModePipeline.settings = settings; } public CVPipelineSettings getDriverModeSettings() { - return driverModePipeline.settings; - } - - public CVPipeline getPipelineByIndex(int pipelineIndex) { - return pipelines.get(pipelineIndex); + return pipelineManager.driverModePipeline.settings; } /** @@ -313,7 +242,7 @@ public class VisionProcess { Mat camFrame = camData.getLeft(); if (camFrame.cols() > 0 && camFrame.rows() > 0) { - CVPipelineResult result = currentPipeline.runPipeline(camFrame); + CVPipelineResult result = pipelineManager.getCurrentPipeline().runPipeline(camFrame); if (result != null) { result.setTimestamp(camData.getRight()); diff --git a/Main/src/main/java/com/chameleonvision/vision/pipeline/CVPipeline.java b/Main/src/main/java/com/chameleonvision/vision/pipeline/CVPipeline.java index 52970d204..280f154a2 100644 --- a/Main/src/main/java/com/chameleonvision/vision/pipeline/CVPipeline.java +++ b/Main/src/main/java/com/chameleonvision/vision/pipeline/CVPipeline.java @@ -27,4 +27,8 @@ public abstract class CVPipeline pipelines = new LinkedList<>(); + + public final CVPipeline driverModePipeline = new DriverVisionPipeline(new CVPipelineSettings()); + + private final VisionProcess parentProcess; + private int currentPipelineIndex; + private boolean driverMode; + + public PipelineManager(VisionProcess visionProcess, List loadedPipelineSettings) { + parentProcess = visionProcess; + if (loadedPipelineSettings == null || loadedPipelineSettings.size() == 0) { + pipelines.add(new CVPipeline2d("New Pipeline")); + } else { + for (CVPipelineSettings setting : loadedPipelineSettings) { + addInternalPipeline(setting); + } + } + setCurrentPipeline(0); + } + + private void reassignIndexes() { + pipelines.sort(IndexComparator); + for (int i = 0; i < pipelines.size(); i++) { + pipelines.get(i).settings.index = i; + } + } + + private CameraConfig getConfig(VisionProcess process) { + return VisionManager.getCameraConfig(process); + } + + private CameraConfig getConfig() { + return getConfig(parentProcess); + } + + private void savePipelineConfig(CVPipelineSettings setting) { + getConfig().pipelineConfig.save(setting); + } + + private void deletePipelineConfig(CVPipelineSettings setting) { + getConfig().pipelineConfig.delete(setting); + } + + private void renamePipelineConfig(CVPipelineSettings setting, String newName) { + getConfig().pipelineConfig.rename(setting, newName); + } + + public void saveAllPipelines() { + pipelines.parallelStream().map(pipeline -> pipeline.settings).forEach(this::savePipelineConfig); + } + + private void addInternalPipeline(CVPipelineSettings setting) { + if (setting instanceof CVPipeline3dSettings) { + pipelines.add(new CVPipeline3d((CVPipeline3dSettings) setting)); + } else if (setting instanceof CVPipeline2dSettings) { + pipelines.add(new CVPipeline2d((CVPipeline2dSettings) setting)); + } else { + System.out.println("Non 2D/3D pipelines not supported!"); + } + reassignIndexes(); + } + + public void setDriverMode(boolean driverMode) { + this.driverMode = driverMode; + } + + public boolean getDriverMode() { + return driverMode; + } + + public int getCurrentPipelineIndex() { + return currentPipelineIndex; + } + + public CVPipeline getCurrentPipeline() { + return driverMode ? driverModePipeline : pipelines.get(currentPipelineIndex); + } + + public void setCurrentPipeline(int index) { + CVPipeline newPipeline = pipelines.get(index); + if (newPipeline != null) { + currentPipelineIndex = index; + getCurrentPipeline().initPipeline(parentProcess.getCamera()); + + if(ConfigManager.settings.currentCamera.equals(parentProcess.getCamera().getProperties().name)) { + ConfigManager.settings.currentPipeline = currentPipelineIndex; + + HashMap pipeChange = new HashMap<>(); + pipeChange.put("currentPipeline", currentPipelineIndex); + SocketHandler.broadcastMessage(pipeChange); + try { + SocketHandler.sendFullSettings(); + } catch (Exception e) { + // avoid NullPointerException when run before threads start + } + } + newPipeline.initPipeline(parentProcess.getCamera()); + } + } + + public void addPipeline(CVPipelineSettings setting) { + addInternalPipeline(setting); + savePipelineConfig(setting); + } + + public void addPipeline(CVPipeline pipeline) { + pipelines.add(pipeline); + reassignIndexes(); + savePipelineConfig(pipeline.settings); + } + + public void addNewPipeline(boolean is3D) { + CVPipeline newPipeline; + if (!is3D) { + newPipeline = new CVPipeline2d(); + } else { + newPipeline = new CVPipeline3d(); + } + newPipeline.settings.index = pipelines.size(); + addPipeline(newPipeline); + } + + public CVPipeline getPipeline(int index) { + return pipelines.get(index); + } + + public void duplicatePipeline(CVPipelineSettings pipeline) { + duplicatePipeline(pipeline, parentProcess); + } + + public void duplicatePipeline(CVPipelineSettings pipeline, VisionProcess destinationProcess) { + pipeline.index = destinationProcess.pipelineManager.pipelines.size(); + pipeline.nickname += "(Copy)"; + destinationProcess.pipelineManager.addPipeline(pipeline); + } + + public void deleteCurrentPipeline() { + deletePipeline(currentPipelineIndex); + } + + private void deletePipeline(int index) { + if (index == currentPipelineIndex) { + currentPipelineIndex -= 1; + } + deletePipelineConfig(getPipeline(index).settings); + pipelines.remove(index); + reassignIndexes(); + } + + public void saveDriverModeConfig() { + getConfig().saveDriverMode(driverModePipeline.settings); + } + + private static final Comparator IndexComparator = (o1, o2) -> { + int o1Index = o1.settings.index; + int o2Index = o2.settings.index; + + if (o1Index == o2Index) { + return 0; + } else if (o1Index < o2Index) { + return -1; + } + return 1; + }; +} diff --git a/Main/src/main/java/com/chameleonvision/web/SocketHandler.java b/Main/src/main/java/com/chameleonvision/web/SocketHandler.java index 2c1a482cf..8f1d1065c 100644 --- a/Main/src/main/java/com/chameleonvision/web/SocketHandler.java +++ b/Main/src/main/java/com/chameleonvision/web/SocketHandler.java @@ -51,7 +51,7 @@ public class SocketHandler { try { VisionProcess currentProcess = VisionManager.getCurrentUIVisionProcess(); CameraCapture currentCamera = currentProcess.getCamera(); - CVPipeline currentPipeline = currentProcess.getCurrentPipeline(); + CVPipeline currentPipeline = currentProcess.pipelineManager.getCurrentPipeline(); switch (entry.getKey()) { case "driverMode": { @@ -80,21 +80,22 @@ public class SocketHandler { int pipelineIndex = (int) pipelineVals.get("pipeline"); int cameraIndex = (int) pipelineVals.get("camera"); ObjectMapper mapper = new ObjectMapper(); - CVPipelineSettings origPipeline = currentProcess.getPipelineByIndex(pipelineIndex).settings; + CVPipelineSettings origPipeline = currentProcess.pipelineManager.getPipeline(pipelineIndex).settings; String val = mapper.writeValueAsString(origPipeline); CVPipelineSettings newPipeline = mapper.readValue(val, origPipeline.getClass()); + // TODO: move to PipelineManager newPipeline.nickname += "(Copy)"; if (cameraIndex != -1) { VisionProcess newProcess = VisionManager.getVisionProcessByIndex(cameraIndex); if (newProcess != null) { - VisionManager.addPipelineToCamera(newPipeline, newProcess); - newProcess.addPipeline(newPipeline); + currentProcess.pipelineManager.duplicatePipeline(newPipeline, newProcess); + } else { + System.err.println("Failed to get new camera for pipeline duplication!"); } } else { - VisionManager.addPipelineToCamera(newPipeline, currentProcess); - currentProcess.addPipeline(newPipeline); + currentProcess.pipelineManager.duplicatePipeline(newPipeline); } VisionManager.saveCurrentCameraPipelines(); @@ -104,17 +105,14 @@ public class SocketHandler { case "command": { switch ((String) entry.getValue()) { case "addNewPipeline": - - currentProcess.addBlankPipeline(); + // TODO: add to UI selection for new 2d/3d + boolean is3d = false; + currentProcess.pipelineManager.addNewPipeline(is3d); sendFullSettings(); VisionManager.saveCurrentCameraPipelines(); break; case "deleteCurrentPipeline": - int currentIndex = currentProcess.getCurrentPipelineIndex(); - if (currentIndex == currentProcess.getPipelines().size() - 1) { - currentProcess.setPipeline(currentIndex - 1, false); - } - currentProcess.deletePipeline(currentIndex); + currentProcess.pipelineManager.deleteCurrentPipeline(); sendFullSettings(); VisionManager.saveCurrentCameraPipelines(); break; @@ -133,7 +131,7 @@ public class SocketHandler { break; } case "currentPipeline": { - currentProcess.setPipeline((Integer) entry.getValue(), true); + currentProcess.pipelineManager.setCurrentPipeline((Integer) entry.getValue()); sendFullSettings(); break; } @@ -195,9 +193,9 @@ public class SocketHandler { for (Field field : cvClass.getFields()) { // iterate over every field in CVPipelineSettings try { if (!field.getType().isEnum()) { // if the field is not an enum, get it based on the current pipeline - tmp.put(field.getName(), field.get(VisionManager.getCurrentUIVisionProcess().getCurrentPipeline().settings)); + tmp.put(field.getName(), field.get(VisionManager.getCurrentUIVisionProcess().pipelineManager.getCurrentPipeline().settings)); } else { - var ordinal = (Enum) field.get(VisionManager.getCurrentUIVisionProcess().getCurrentPipeline().settings); + var ordinal = (Enum) field.get(VisionManager.getCurrentUIVisionProcess().pipelineManager.getCurrentPipeline().settings); tmp.put(field.getName(), ordinal.ordinal()); } } catch (IllegalArgumentException e) { @@ -231,8 +229,8 @@ public class SocketHandler { private static HashMap getOrdinalDriver() { HashMap tmp = new HashMap<>(); VisionProcess currentProcess = VisionManager.getCurrentUIVisionProcess(); - CVPipelineSettings driverModeSettings = currentProcess.getDriverModeSettings(); - tmp.put("isDriver", currentProcess.getDriverMode()); + CVPipelineSettings driverModeSettings = currentProcess.pipelineManager.driverModePipeline.settings; + tmp.put("isDriver", currentProcess.pipelineManager.getDriverMode()); tmp.put("driverBrightness", driverModeSettings.brightness); tmp.put("driverExposure", driverModeSettings.exposure); return tmp; @@ -243,8 +241,7 @@ public class SocketHandler { Map fullSettings = new HashMap<>(); VisionProcess currentProcess = VisionManager.getCurrentUIVisionProcess(); - CameraCapture currentCamera = currentProcess.getCamera(); - CVPipeline currentPipeline = currentProcess.getCurrentPipeline(); + CVPipeline currentPipeline = currentProcess.pipelineManager.getCurrentPipeline(); try { fullSettings.put("settings", getOrdinalSettings()); @@ -255,7 +252,7 @@ public class SocketHandler { fullSettings.put("pipelineList", VisionManager.getCurrentCameraPipelineNicknames()); fullSettings.put("resolutionList", VisionManager.getCurrentCameraResolutionList()); fullSettings.put("port", currentProcess.cameraStreamer.getStreamPort()); - fullSettings.put("currentPipelineIndex", VisionManager.getCurrentUIVisionProcess().getCurrentPipelineIndex()); + fullSettings.put("currentPipelineIndex", VisionManager.getCurrentUIVisionProcess().pipelineManager.getCurrentPipelineIndex()); fullSettings.put("currentCameraIndex", VisionManager.getCurrentUIVisionProcessIndex()); } catch (IllegalAccessException e) { System.err.println("No camera found!");