mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-20 00:51:41 +00:00
Bootup sprint (#18)
* Did some stuff * Fix gradle, start implementing mjpeg frame consumer * Did some stuff * bade changes * rename camera config to USBCameraConfiguration, add name * unrename cameraconfiguration * Add pub/sub framework * Add setResolution to mjpeg frame consumer * add NTDataConsumer * Add some totally broken hsv hacks * Start refactoring UI data * Update index.js * Commit and push, he says * Fix up some errors * Fix input tab * Fix fps * Update index.js * Add pipeline field setting, update PipelineManager, fix nullpointers and USBCameraSettables * Change v-model to point to data() * update hsv to use mutations * Work on saving, fix hsv * Rename shouldErode/shouldDilate to erode and dilate * Hook all the tabs up to the Store * Change handleData to handlePipelineData * camera quirk redo, add ICCSub to SocketHandler * Fix some property names * Fixed tons of naming in UI, fix backend for multi-val PSCs, fix PSC enums * change pipeline type to an int in store * Fix mutation naming * Attempt threshold fix * Update SocketHandler.java * Add truthy data sending * Start adding logging support * [UI] Add delay to slider input boxes (#1) * [UI] [Backend] potentially fix camera settings, various logging tweaks * Don't release raw input mat * add setVideoModeIndex to vision settables * Implement pipeline index in socket handler, add framework for renaming/changing pipes * (ish) get pipeline change working * Create index.html * Cleanups, fix pipeline index bug, fix stream res for MJPG, add dashboard stream (unused) * Refactor UI to use mutatePipeline, send pipeline results * Update NetworkConfig.java * Change double to number * Run spotless * Fix reversal of large/small comparators * Fix left/right * Fix pitch/yaw calculation bug, fix area bug * Use Vue.set instead of assignment This fixes {{ }} * Update App.vue * run spotless * Actually add pipelines and reassign indecies * Delete old pipeline configs Fixes duplication on renaming pipeline * Start working on deleting pipes * Fix camera nickname change * run spotless * Fix some test stuff * Update VisionModuleManagerTest.java * vision source manager test is still broken * Fix VisionSourceManager test * Apply spotless 2 electric boogaloo Co-authored-by: Banks Troutman <btrout.dhrs@gmail.com> Co-authored-by: Declan Freeman-Gleason <declanfreemangleason@gmail.com> Co-authored-by: Aaryan Agrawal <54345060+13Ducks@users.noreply.github.com>
This commit is contained in:
@@ -40,7 +40,14 @@ public class CameraConfiguration {
|
||||
public String path = "";
|
||||
public CameraType cameraType = CameraType.UsbCamera;
|
||||
public CameraCalibrationCoefficients calibration;
|
||||
public List<Integer> CameraLEDs = new ArrayList<>();
|
||||
public List<Integer> cameraLeds = new ArrayList<>();
|
||||
public int currentPipelineIndex = -1;
|
||||
|
||||
@JsonIgnore // this ignores the pipes as we serialize them to their own subfolder
|
||||
public List<CVPipelineSettings> pipelineSettings = new ArrayList<>();
|
||||
|
||||
@JsonIgnore
|
||||
public DriverModePipelineSettings driveModeSettings = new DriverModePipelineSettings();
|
||||
|
||||
public CameraConfiguration(String baseName, String path) {
|
||||
this(baseName, baseName, baseName, path);
|
||||
@@ -51,6 +58,15 @@ public class CameraConfiguration {
|
||||
this.uniqueName = uniqueName;
|
||||
this.nickname = nickname;
|
||||
this.path = path;
|
||||
|
||||
logger.debug(
|
||||
"Creating USB camera configuration for "
|
||||
+ cameraType
|
||||
+ baseName
|
||||
+ " (AKA "
|
||||
+ nickname
|
||||
+ ") at "
|
||||
+ path);
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
@@ -62,7 +78,8 @@ public class CameraConfiguration {
|
||||
@JsonProperty("path") String path,
|
||||
@JsonProperty("cameraType") CameraType cameraType,
|
||||
@JsonProperty("calibration") CameraCalibrationCoefficients calibration,
|
||||
@JsonProperty("CameraLEDs") List<Integer> cameraLEDs) {
|
||||
@JsonProperty("cameraLEDs") List<Integer> cameraLeds,
|
||||
@JsonProperty("currentPipelineIndex") int currentPipelineIndex) {
|
||||
this.baseName = baseName;
|
||||
this.uniqueName = uniqueName;
|
||||
this.nickname = nickname;
|
||||
@@ -70,15 +87,19 @@ public class CameraConfiguration {
|
||||
this.path = path;
|
||||
this.cameraType = cameraType;
|
||||
this.calibration = calibration;
|
||||
this.CameraLEDs = cameraLEDs;
|
||||
this.cameraLeds = cameraLeds;
|
||||
this.currentPipelineIndex = currentPipelineIndex;
|
||||
|
||||
logger.debug(
|
||||
"Creating camera configuration for "
|
||||
+ cameraType
|
||||
+ baseName
|
||||
+ " (AKA "
|
||||
+ nickname
|
||||
+ ") at "
|
||||
+ path);
|
||||
}
|
||||
|
||||
@JsonIgnore // this ignores the pipes as we serialize them to their own subfolder
|
||||
public final List<CVPipelineSettings> pipelineSettings = new ArrayList<>();
|
||||
|
||||
@JsonIgnore
|
||||
public DriverModePipelineSettings driveModeSettings = new DriverModePipelineSettings();
|
||||
|
||||
public void addPipelineSettings(List<CVPipelineSettings> settings) {
|
||||
for (var setting : settings) {
|
||||
addPipelineSetting(setting);
|
||||
@@ -101,4 +122,8 @@ public class CameraConfiguration {
|
||||
pipelineSettings.add(setting);
|
||||
pipelineSettings.sort(PipelineManager.PipelineSettingsIndexComparator);
|
||||
}
|
||||
|
||||
public void setPipelineSettings(List<CVPipelineSettings> settings) {
|
||||
pipelineSettings = settings;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.common.util.file.JacksonUtils;
|
||||
import org.photonvision.vision.pipeline.CVPipelineSettings;
|
||||
import org.photonvision.vision.pipeline.DriverModePipelineSettings;
|
||||
import org.photonvision.vision.processes.VisionSource;
|
||||
|
||||
public class ConfigManager {
|
||||
private static final Logger logger = new Logger(ConfigManager.class, LogGroup.General);
|
||||
@@ -65,10 +66,11 @@ public class ConfigManager {
|
||||
this.networkConfigFile =
|
||||
new File(Path.of(rootFolder.toString(), "networkSettings.json").toUri());
|
||||
this.camerasFolder = new File(Path.of(rootFolder.toString(), "cameras").toUri());
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
public void load() {
|
||||
private void load() {
|
||||
logger.info("Loading settings...");
|
||||
if (!rootFolder.exists()) {
|
||||
if (rootFolder.mkdirs()) {
|
||||
@@ -150,6 +152,7 @@ public class ConfigManager {
|
||||
var subdir = Path.of(camerasFolder.toPath().toString(), subdirName);
|
||||
|
||||
if (!subdir.toFile().exists()) {
|
||||
// TODO: check for error
|
||||
subdir.toFile().mkdirs();
|
||||
}
|
||||
|
||||
@@ -166,10 +169,20 @@ public class ConfigManager {
|
||||
logger.error("Could not save drivermode.json for " + subdir);
|
||||
}
|
||||
|
||||
// Delete old pipe configs so that we don't get any conflicts
|
||||
try {
|
||||
var pipelineFolder = Path.of(subdir.toString(), "pipelines");
|
||||
Files.list(pipelineFolder).forEach(it -> it.toFile().delete());
|
||||
} catch (IOException e) {
|
||||
logger.error("Exception while deleting old configs!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
for (var pipe : camConfig.pipelineSettings) {
|
||||
var pipePath = Path.of(subdir.toString(), "pipelines", pipe.pipelineNickname + ".json");
|
||||
|
||||
if (!pipePath.getParent().toFile().exists()) {
|
||||
// TODO: check for error
|
||||
pipePath.getParent().toFile().mkdirs();
|
||||
}
|
||||
|
||||
@@ -235,9 +248,7 @@ public class ConfigManager {
|
||||
var relativizedFilePath =
|
||||
getRootFolder().toAbsolutePath().relativize(p).toString();
|
||||
try {
|
||||
var ret = JacksonUtils.deserialize(p, CVPipelineSettings.class);
|
||||
return ret;
|
||||
|
||||
return JacksonUtils.deserialize(p, CVPipelineSettings.class);
|
||||
} catch (JsonProcessingException e) {
|
||||
logger.error("Exception while deserializing " + relativizedFilePath);
|
||||
e.printStackTrace();
|
||||
@@ -260,4 +271,18 @@ public class ConfigManager {
|
||||
}
|
||||
return loadedConfigurations;
|
||||
}
|
||||
|
||||
public void addCameraConfigurations(HashMap<VisionSource, List<CVPipelineSettings>> sources) {
|
||||
List<CameraConfiguration> list =
|
||||
sources.keySet().stream()
|
||||
.map(it -> it.getSettables().getConfiguration())
|
||||
.collect(Collectors.toList());
|
||||
getConfig().addCameraConfigs(list);
|
||||
save();
|
||||
}
|
||||
|
||||
public void saveModule(CameraConfiguration config, String uniqueName) {
|
||||
getConfig().addCameraConfig(uniqueName, config);
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,24 @@
|
||||
|
||||
package org.photonvision.common.configuration;
|
||||
|
||||
import java.util.HashMap;
|
||||
import org.photonvision.common.networking.NetworkMode;
|
||||
|
||||
public class NetworkConfig {
|
||||
public int teamNumber = 1577;
|
||||
public int teamNumber = 1;
|
||||
public NetworkMode connectionType = NetworkMode.DHCP;
|
||||
public String ip = "";
|
||||
public String gateway = "";
|
||||
public String netmask = "";
|
||||
public String hostname = "photonvision";
|
||||
|
||||
public HashMap<String, Object> toHashMap() {
|
||||
HashMap<String, Object> tmp = new HashMap<>();
|
||||
tmp.put("teamNumber", teamNumber);
|
||||
tmp.put("connectionType", connectionType.ordinal());
|
||||
tmp.put("ip", ip);
|
||||
tmp.put("gateway", gateway);
|
||||
tmp.put("netmask", netmask);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,12 @@
|
||||
package org.photonvision.common.configuration;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import org.photonvision.common.util.SerializationUtils;
|
||||
import org.photonvision.vision.processes.VisionModule;
|
||||
import org.photonvision.vision.processes.VisionModuleManager;
|
||||
|
||||
// TODO rename this class
|
||||
public class PhotonConfiguration {
|
||||
@@ -33,6 +39,12 @@ public class PhotonConfiguration {
|
||||
return cameraConfigurations;
|
||||
}
|
||||
|
||||
public void addCameraConfigs(List<CameraConfiguration> config) {
|
||||
for (var c : config) {
|
||||
addCameraConfig(c);
|
||||
}
|
||||
}
|
||||
|
||||
public void addCameraConfig(CameraConfiguration config) {
|
||||
addCameraConfig(config.uniqueName, config);
|
||||
}
|
||||
@@ -58,4 +70,29 @@ public class PhotonConfiguration {
|
||||
this.networkConfig = networkConfig;
|
||||
this.cameraConfigurations = cameraConfigurations;
|
||||
}
|
||||
|
||||
public Map<String, Object> toHashMap() {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
|
||||
map.put("networkSettings", networkConfig.toHashMap());
|
||||
map.put(
|
||||
"cameraSettings",
|
||||
VisionModuleManager.getInstance().getModules().stream()
|
||||
.map(VisionModule::toUICameraConfig)
|
||||
.map(SerializationUtils::objectToHashMap)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
public static class UICameraConfiguration {
|
||||
@SuppressWarnings("unused")
|
||||
public double fov, tiltDegrees;
|
||||
public String nickname;
|
||||
public HashMap<String, Object> currentPipelineSettings;
|
||||
public int currentPipelineIndex;
|
||||
public List<String> pipelineNicknames;
|
||||
public HashMap<Integer, HashMap<String, Object>> videoFormatList;
|
||||
public int streamPort;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.dataflow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public enum DataChangeDestination {
|
||||
DCD_ACTIVEMODULE,
|
||||
DCD_ACTIVEPIPELINESETTINGS,
|
||||
DCD_GENSETTINGS,
|
||||
DCD_UI,
|
||||
DCD_OTHER;
|
||||
|
||||
public static final List<DataChangeDestination> AllDestinations =
|
||||
Arrays.asList(DataChangeDestination.values());
|
||||
|
||||
public static class DataChangeDestinationList extends ArrayList<DataChangeDestination> {}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.dataflow;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.stream.Collectors;
|
||||
import org.photonvision.common.dataflow.events.DataChangeEvent;
|
||||
import org.photonvision.common.logging.Level;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class DataChangeService {
|
||||
|
||||
private static final Logger logger = new Logger(DataChangeService.class, LogGroup.Server);
|
||||
|
||||
private static class ThreadSafeSingleton {
|
||||
private static final DataChangeService INSTANCE = new DataChangeService();
|
||||
}
|
||||
|
||||
public static DataChangeService getInstance() {
|
||||
return ThreadSafeSingleton.INSTANCE;
|
||||
}
|
||||
|
||||
private final CopyOnWriteArrayList<DataChangeSubscriber> subscribers;
|
||||
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
private final Thread dispatchThread;
|
||||
|
||||
private final BlockingQueue<DataChangeEvent> eventQueue = new LinkedBlockingQueue<>();
|
||||
|
||||
private DataChangeService() {
|
||||
subscribers = new CopyOnWriteArrayList<>();
|
||||
dispatchThread = new Thread(this::dispatchFromQueue);
|
||||
dispatchThread.setName("EventDispatchThread");
|
||||
dispatchThread.start();
|
||||
}
|
||||
|
||||
public boolean hasEvents() {
|
||||
return !eventQueue.isEmpty();
|
||||
}
|
||||
|
||||
private void dispatchFromQueue() {
|
||||
while (true) {
|
||||
try {
|
||||
var taken = eventQueue.take();
|
||||
for (var sub : subscribers) {
|
||||
if (sub.wantedSources.contains(taken.sourceType)
|
||||
&& sub.wantedDestinations.contains(taken.destType)) {
|
||||
sub.onDataChangeEvent(taken);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Exception when dispatching event!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addSubscriber(DataChangeSubscriber subscriber) {
|
||||
if (!subscribers.addIfAbsent(subscriber)) {
|
||||
logger.warn("Attempted to add already added subscriber!");
|
||||
} else {
|
||||
if (Logger.shouldLog(Level.TRACE, LogGroup.Data)) {
|
||||
var sources =
|
||||
subscriber.wantedSources.stream().map(Enum::toString).collect(Collectors.joining(", "));
|
||||
var dests =
|
||||
subscriber.wantedDestinations.stream()
|
||||
.map(Enum::toString)
|
||||
.collect(Collectors.joining(", "));
|
||||
|
||||
logger.trace("Added subscriber - " + "Sources: " + sources + ", Destinations: " + dests);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addSubscribers(DataChangeSubscriber... subs) {
|
||||
for (var sub : subs) {
|
||||
addSubscriber(sub);
|
||||
}
|
||||
}
|
||||
|
||||
public void publishEvent(DataChangeEvent event) {
|
||||
eventQueue.offer(event);
|
||||
}
|
||||
|
||||
public void publishEvents(DataChangeEvent... events) {
|
||||
for (var event : events) {
|
||||
publishEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.dataflow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public enum DataChangeSource {
|
||||
DCS_WEBSOCKET,
|
||||
DCS_HTTP,
|
||||
DCS_NETWORKTABLES,
|
||||
DCS_VISIONMODULE,
|
||||
DCS_OTHER;
|
||||
|
||||
public static final List<DataChangeSource> AllSources = Arrays.asList(DataChangeSource.values());
|
||||
|
||||
public static class DataChangeSourceList extends ArrayList<DataChangeSource> {}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.dataflow;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import org.photonvision.common.dataflow.events.DataChangeEvent;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public abstract class DataChangeSubscriber {
|
||||
public final List<DataChangeSource> wantedSources;
|
||||
public final List<DataChangeDestination> wantedDestinations;
|
||||
|
||||
private final int hash;
|
||||
|
||||
public DataChangeSubscriber(
|
||||
List<DataChangeSource> wantedSources, List<DataChangeDestination> wantedDestinations) {
|
||||
this.wantedSources = wantedSources;
|
||||
this.wantedDestinations = wantedDestinations;
|
||||
hash = Objects.hash(wantedSources, wantedDestinations);
|
||||
}
|
||||
|
||||
public DataChangeSubscriber() {
|
||||
this(DataChangeSource.AllSources, DataChangeDestination.AllDestinations);
|
||||
}
|
||||
|
||||
public DataChangeSubscriber(DataChangeSource.DataChangeSourceList wantedSources) {
|
||||
this(wantedSources, DataChangeDestination.AllDestinations);
|
||||
}
|
||||
|
||||
public DataChangeSubscriber(DataChangeDestination.DataChangeDestinationList wantedDestinations) {
|
||||
this(DataChangeSource.AllSources, wantedDestinations);
|
||||
}
|
||||
|
||||
public abstract void onDataChangeEvent(DataChangeEvent event);
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.dataflow;
|
||||
|
||||
public interface DataProvider {}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.dataflow.camera;
|
||||
|
||||
import org.photonvision.common.dataflow.DataChangeSubscriber;
|
||||
import org.photonvision.common.dataflow.events.DataChangeEvent;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.vision.processes.VisionModuleManager;
|
||||
|
||||
public class IncomingCameraCommandSubscriber extends DataChangeSubscriber {
|
||||
private static final Logger logger =
|
||||
new Logger(IncomingCameraCommandSubscriber.class, LogGroup.Camera);
|
||||
|
||||
private final VisionModuleManager vmm;
|
||||
|
||||
public IncomingCameraCommandSubscriber(VisionModuleManager instance) {
|
||||
this.vmm = instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataChangeEvent(DataChangeEvent event) {
|
||||
// logger.de_pest("Got event from [" + event.sourceType + "] and dest [" + event.destType
|
||||
// + "] with property name [" + event.propertyName
|
||||
// + "] and value [" + event.data + "]");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.dataflow.events;
|
||||
|
||||
import org.photonvision.common.dataflow.DataChangeDestination;
|
||||
import org.photonvision.common.dataflow.DataChangeSource;
|
||||
|
||||
public class DataChangeEvent<T> {
|
||||
public final DataChangeSource sourceType;
|
||||
public final DataChangeDestination destType;
|
||||
public final String propertyName;
|
||||
public final T data;
|
||||
|
||||
public DataChangeEvent(
|
||||
DataChangeSource sourceType,
|
||||
DataChangeDestination destType,
|
||||
String propertyName,
|
||||
T newValue) {
|
||||
this.sourceType = sourceType;
|
||||
this.destType = destType;
|
||||
this.propertyName = propertyName;
|
||||
this.data = newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DataChangeEvent{"
|
||||
+ "sourceType="
|
||||
+ sourceType
|
||||
+ ", destType="
|
||||
+ destType
|
||||
+ ", propertyName='"
|
||||
+ propertyName
|
||||
+ '\''
|
||||
+ ", data="
|
||||
+ data
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.dataflow.events;
|
||||
|
||||
import org.photonvision.common.dataflow.DataChangeDestination;
|
||||
import org.photonvision.common.dataflow.DataChangeSource;
|
||||
|
||||
public class HTTPRequestEvent<T> extends DataChangeEvent<T> {
|
||||
public HTTPRequestEvent(
|
||||
DataChangeSource sourceType,
|
||||
DataChangeDestination destType,
|
||||
String propertyName,
|
||||
T newValue) {
|
||||
super(sourceType, destType, propertyName, newValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.dataflow.events;
|
||||
|
||||
import java.util.HashMap;
|
||||
import org.photonvision.common.dataflow.DataChangeDestination;
|
||||
import org.photonvision.common.dataflow.DataChangeSource;
|
||||
|
||||
public class IncomingWebSocketEvent<T> extends DataChangeEvent<T> {
|
||||
|
||||
public final Integer cameraIndex;
|
||||
|
||||
public IncomingWebSocketEvent(DataChangeDestination destType, String propertyName, T newValue) {
|
||||
this(destType, propertyName, newValue, null);
|
||||
}
|
||||
|
||||
public IncomingWebSocketEvent(
|
||||
DataChangeDestination destType, String propertyName, T newValue, Integer cameraIndex) {
|
||||
super(DataChangeSource.DCS_WEBSOCKET, destType, propertyName, newValue);
|
||||
this.cameraIndex = cameraIndex;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public IncomingWebSocketEvent(
|
||||
DataChangeDestination destType, String dataKey, HashMap<String, Object> data) {
|
||||
this(destType, dataKey, (T) data.get(dataKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IncomingWebSocketEvent{"
|
||||
+ "cameraIndex="
|
||||
+ cameraIndex
|
||||
+ ", sourceType="
|
||||
+ sourceType
|
||||
+ ", destType="
|
||||
+ destType
|
||||
+ ", propertyName='"
|
||||
+ propertyName
|
||||
+ '\''
|
||||
+ ", data="
|
||||
+ data
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.dataflow.events;
|
||||
|
||||
import java.util.HashMap;
|
||||
import org.photonvision.common.dataflow.DataChangeDestination;
|
||||
import org.photonvision.common.dataflow.DataChangeSource;
|
||||
import org.photonvision.server.UIUpdateType;
|
||||
|
||||
public class OutgoingUIEvent<T> extends DataChangeEvent<T> {
|
||||
public final UIUpdateType updateType;
|
||||
|
||||
public OutgoingUIEvent(UIUpdateType updateType, String propertyName, T newValue) {
|
||||
super(DataChangeSource.DCS_WEBSOCKET, DataChangeDestination.DCD_UI, propertyName, newValue);
|
||||
this.updateType = updateType;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public OutgoingUIEvent(UIUpdateType updateType, String dataKey, HashMap<String, Object> data) {
|
||||
this(updateType, dataKey, (T) data.get(dataKey));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.dataflow.networktables;
|
||||
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import org.photonvision.common.datatransfer.DataConsumer;
|
||||
import org.photonvision.vision.pipeline.result.SimplePipelineResult;
|
||||
import org.photonvision.vision.processes.Data;
|
||||
|
||||
public class NTDataConsumer implements DataConsumer {
|
||||
|
||||
private final NetworkTable rootTable;
|
||||
NetworkTable subTable;
|
||||
NetworkTableEntry rawData;
|
||||
|
||||
public NTDataConsumer(NetworkTable root, String camName) {
|
||||
this.rootTable = root;
|
||||
this.subTable = root.getSubTable(camName);
|
||||
rawData = subTable.getEntry("rawData");
|
||||
}
|
||||
|
||||
public void setCameraName(String camName) {
|
||||
this.subTable = rootTable.getSubTable(camName);
|
||||
rawData = subTable.getEntry("rawData");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Data data) {
|
||||
var simplified = new SimplePipelineResult(data.result);
|
||||
var bytes = simplified.toByteArray();
|
||||
rawData.setRaw(bytes);
|
||||
rootTable.getInstance().flush();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.dataflow.networktables;
|
||||
|
||||
import edu.wpi.first.networktables.LogMessage;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import java.util.function.Consumer;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.common.scripting.ScriptEventType;
|
||||
import org.photonvision.common.scripting.ScriptManager;
|
||||
|
||||
public class NetworkTablesManager {
|
||||
|
||||
private NetworkTablesManager() {}
|
||||
|
||||
private static final Logger logger = new Logger(NetworkTablesManager.class, LogGroup.General);
|
||||
|
||||
private static final NetworkTableInstance ntInstance = NetworkTableInstance.getDefault();
|
||||
|
||||
public static final String kRootTableName = "/chameleon-vision";
|
||||
public static final NetworkTable kRootTable =
|
||||
NetworkTableInstance.getDefault().getTable(kRootTableName);
|
||||
|
||||
public static boolean isServer = false;
|
||||
|
||||
private static int getTeamNumber() {
|
||||
// TODO: FIX
|
||||
return 0;
|
||||
// return ConfigManager.settings.teamNumber;
|
||||
}
|
||||
|
||||
private static class NTLogger implements Consumer<LogMessage> {
|
||||
|
||||
private boolean hasReportedConnectionFailure = false;
|
||||
|
||||
@Override
|
||||
public void accept(LogMessage logMessage) {
|
||||
if (!hasReportedConnectionFailure && logMessage.message.contains("timed out")) {
|
||||
logger.error("NT Connection has failed! Will retry in background.");
|
||||
hasReportedConnectionFailure = true;
|
||||
} else if (logMessage.message.contains("connected")) {
|
||||
logger.info("NT Connected!");
|
||||
hasReportedConnectionFailure = false;
|
||||
ScriptManager.queueEvent(ScriptEventType.kNTConnected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
NetworkTableInstance.getDefault().addLogger(new NTLogger(), 0, 255); // to hide error messages
|
||||
}
|
||||
|
||||
public static void setClientMode(String host) {
|
||||
isServer = false;
|
||||
logger.info("Starting NT Client");
|
||||
ntInstance.stopServer();
|
||||
if (host != null) {
|
||||
ntInstance.startClient(host);
|
||||
} else {
|
||||
ntInstance.startClientTeam(getTeamNumber());
|
||||
if (ntInstance.isConnected()) {
|
||||
logger.info("[NetworkTablesManager] Connected to the robot!");
|
||||
} else {
|
||||
logger.info(
|
||||
"[NetworkTablesManager] Could NOT to the robot! Will retry in the background...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void setTeamClientMode() {
|
||||
setClientMode(null);
|
||||
}
|
||||
|
||||
public static void setServerMode() {
|
||||
isServer = true;
|
||||
logger.info("Starting NT Server");
|
||||
ntInstance.stopClient();
|
||||
ntInstance.startServer();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.dataflow.structures;
|
||||
|
||||
@SuppressWarnings("PointlessBitwiseExpression")
|
||||
public abstract class BytePackable {
|
||||
public abstract byte[] toByteArray();
|
||||
|
||||
public abstract void fromByteArray(byte[] src);
|
||||
|
||||
protected int bufferPosition = 0;
|
||||
|
||||
public int getBufferPosition() {
|
||||
return bufferPosition;
|
||||
}
|
||||
|
||||
public void resetBufferPosition() {
|
||||
bufferPosition = 0;
|
||||
}
|
||||
|
||||
protected void bufferData(byte[] src, byte[] dest) {
|
||||
System.arraycopy(src, 0, dest, bufferPosition, src.length);
|
||||
bufferPosition += src.length;
|
||||
}
|
||||
|
||||
protected void bufferData(byte src, byte[] dest) {
|
||||
System.arraycopy(new byte[] {src}, 0, dest, bufferPosition, 1);
|
||||
bufferPosition++;
|
||||
}
|
||||
|
||||
protected void bufferData(int src, byte[] dest) {
|
||||
System.arraycopy(toBytes(src), 0, dest, bufferPosition, Integer.BYTES);
|
||||
bufferPosition += Integer.BYTES;
|
||||
}
|
||||
|
||||
protected void bufferData(double src, byte[] dest) {
|
||||
System.arraycopy(toBytes(src), 0, dest, bufferPosition, Double.BYTES);
|
||||
bufferPosition += Double.BYTES;
|
||||
}
|
||||
|
||||
protected void bufferData(boolean src, byte[] dest) {
|
||||
System.arraycopy(toBytes(src), 0, dest, bufferPosition, 1);
|
||||
bufferPosition += 1;
|
||||
}
|
||||
|
||||
protected boolean unbufferBoolean(byte[] src) {
|
||||
return toBoolean(src, bufferPosition++);
|
||||
}
|
||||
|
||||
protected byte unbufferByte(byte[] src) {
|
||||
var value = src[bufferPosition];
|
||||
bufferPosition++;
|
||||
return value;
|
||||
}
|
||||
|
||||
protected byte[] unbufferBytes(byte[] src, int len) {
|
||||
var bytes = new byte[len];
|
||||
System.arraycopy(src, bufferPosition, bytes, 0, len);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
protected int unbufferInt(byte[] src) {
|
||||
var value = toInt(src, bufferPosition);
|
||||
bufferPosition += Integer.BYTES;
|
||||
return value;
|
||||
}
|
||||
|
||||
protected double unbufferDouble(byte[] src) {
|
||||
var value = toDouble(src, bufferPosition);
|
||||
bufferPosition += Double.BYTES;
|
||||
return value;
|
||||
}
|
||||
|
||||
private static boolean toBoolean(byte[] src, int pos) {
|
||||
return src[pos] != 0;
|
||||
}
|
||||
|
||||
private static int toInt(byte[] src, int pos) {
|
||||
return (0xff & src[pos]) << 24
|
||||
| (0xff & src[pos + 1]) << 16
|
||||
| (0xff & src[pos + 2]) << 8
|
||||
| (0xff & src[pos + 3]) << 0;
|
||||
}
|
||||
|
||||
private static long toLong(byte[] src, int pos) {
|
||||
return (long) (0xff & src[pos]) << 56
|
||||
| (long) (0xff & src[pos + 1]) << 48
|
||||
| (long) (0xff & src[pos + 2]) << 40
|
||||
| (long) (0xff & src[pos + 3]) << 32
|
||||
| (long) (0xff & src[pos + 4]) << 24
|
||||
| (long) (0xff & src[pos + 5]) << 16
|
||||
| (long) (0xff & src[pos + 6]) << 8
|
||||
| (long) (0xff & src[pos + 7]) << 0;
|
||||
}
|
||||
|
||||
private static double toDouble(byte[] src, int pos) {
|
||||
return Double.longBitsToDouble(toLong(src, pos));
|
||||
}
|
||||
|
||||
protected byte[] toBytes(double value) {
|
||||
long data = Double.doubleToRawLongBits(value);
|
||||
return new byte[] {
|
||||
(byte) ((data >> 56) & 0xff),
|
||||
(byte) ((data >> 48) & 0xff),
|
||||
(byte) ((data >> 40) & 0xff),
|
||||
(byte) ((data >> 32) & 0xff),
|
||||
(byte) ((data >> 24) & 0xff),
|
||||
(byte) ((data >> 16) & 0xff),
|
||||
(byte) ((data >> 8) & 0xff),
|
||||
(byte) ((data >> 0) & 0xff),
|
||||
};
|
||||
}
|
||||
|
||||
protected byte[] toBytes(int value) {
|
||||
return new byte[] {
|
||||
(byte) ((value >> 24) & 0xff),
|
||||
(byte) ((value >> 16) & 0xff),
|
||||
(byte) ((value >> 8) & 0xff),
|
||||
(byte) ((value >> 0) & 0xff)
|
||||
};
|
||||
}
|
||||
|
||||
protected byte[] toBytes(boolean value) {
|
||||
return new byte[] {(byte) (value ? 1 : 0)};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.dataflow.structures;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import org.photonvision.vision.pipeline.result.CVPipelineResult;
|
||||
|
||||
public class SimplePipelineResult extends BytePackable {
|
||||
|
||||
private double latencyMillis;
|
||||
private boolean hasTargets;
|
||||
public final List<SimpleTrackedTarget> targets = new ArrayList<>();
|
||||
|
||||
public SimplePipelineResult() {}
|
||||
|
||||
public SimplePipelineResult(
|
||||
double latencyMillis, boolean hasTargets, List<SimpleTrackedTarget> targets) {
|
||||
this.latencyMillis = latencyMillis;
|
||||
this.hasTargets = hasTargets;
|
||||
this.targets.addAll(targets);
|
||||
}
|
||||
|
||||
public SimplePipelineResult(CVPipelineResult origResult) {
|
||||
update(origResult);
|
||||
}
|
||||
|
||||
public void update(CVPipelineResult origResult) {
|
||||
latencyMillis = origResult.getLatencyMillis();
|
||||
hasTargets = origResult.hasTargets();
|
||||
targets.clear();
|
||||
for (var origTarget : origResult.targets) {
|
||||
targets.add(new SimpleTrackedTarget(origTarget));
|
||||
}
|
||||
}
|
||||
|
||||
public double getLatencyMillis() {
|
||||
return latencyMillis;
|
||||
}
|
||||
|
||||
public boolean hasTargets() {
|
||||
return hasTargets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
SimplePipelineResult that = (SimplePipelineResult) o;
|
||||
boolean latencyMatch = Double.compare(that.latencyMillis, latencyMillis) == 0;
|
||||
boolean hasTargetsMatch = that.hasTargets == hasTargets;
|
||||
boolean targetsMatch = that.targets.equals(targets);
|
||||
return latencyMatch && hasTargetsMatch && targetsMatch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(latencyMillis, hasTargets, targets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
bufferPosition = 0;
|
||||
int bufferSize =
|
||||
8 + 1 + 1 + (targets.size() * 48); // latencyMillis + hasTargets + targetCount + targets
|
||||
var buff = new byte[bufferSize];
|
||||
|
||||
bufferData(latencyMillis, buff);
|
||||
bufferData(hasTargets, buff);
|
||||
bufferData((byte) targets.size(), buff);
|
||||
for (var target : targets) {
|
||||
bufferData(target.toByteArray(), buff);
|
||||
}
|
||||
|
||||
return buff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromByteArray(byte[] src) {
|
||||
bufferPosition = 0;
|
||||
|
||||
latencyMillis = unbufferDouble(src);
|
||||
hasTargets = unbufferBoolean(src);
|
||||
byte targetCount = unbufferByte(src);
|
||||
|
||||
targets.clear();
|
||||
for (int i = 0; i < targetCount; i++) {
|
||||
var target = new SimpleTrackedTarget();
|
||||
target.fromByteArray(unbufferBytes(src, 48));
|
||||
bufferPosition += 48;
|
||||
targets.add(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.dataflow.structures;
|
||||
|
||||
import edu.wpi.first.wpilibj.geometry.Pose2d;
|
||||
import edu.wpi.first.wpilibj.geometry.Rotation2d;
|
||||
import java.util.Objects;
|
||||
import org.photonvision.vision.target.TrackedTarget;
|
||||
|
||||
public class SimpleTrackedTarget extends BytePackable {
|
||||
public static final int PACK_SIZE_BYTES = Double.BYTES * 6;
|
||||
|
||||
private double yaw;
|
||||
private double pitch;
|
||||
private double area;
|
||||
private Pose2d robotRelativePose = new Pose2d();
|
||||
|
||||
public SimpleTrackedTarget() {}
|
||||
|
||||
public SimpleTrackedTarget(double yaw, double pitch, double area, Pose2d pose) {
|
||||
this.yaw = yaw;
|
||||
this.pitch = pitch;
|
||||
this.area = area;
|
||||
robotRelativePose = pose;
|
||||
}
|
||||
|
||||
public SimpleTrackedTarget(TrackedTarget origTarget) {
|
||||
yaw = origTarget.getYaw();
|
||||
pitch = origTarget.getPitch();
|
||||
area = origTarget.getArea();
|
||||
robotRelativePose = origTarget.getRobotRelativePose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
SimpleTrackedTarget that = (SimpleTrackedTarget) o;
|
||||
return Double.compare(that.yaw, yaw) == 0
|
||||
&& Double.compare(that.pitch, pitch) == 0
|
||||
&& Double.compare(that.area, area) == 0
|
||||
&& Objects.equals(robotRelativePose, that.robotRelativePose);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(yaw, pitch, area, robotRelativePose);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
resetBufferPosition();
|
||||
byte[] data = new byte[PACK_SIZE_BYTES];
|
||||
|
||||
bufferData(yaw, data);
|
||||
bufferData(pitch, data);
|
||||
bufferData(area, data);
|
||||
bufferData(robotRelativePose.getTranslation().getX(), data);
|
||||
bufferData(robotRelativePose.getTranslation().getY(), data);
|
||||
bufferData(robotRelativePose.getRotation().getDegrees(), data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromByteArray(byte[] src) {
|
||||
resetBufferPosition();
|
||||
if (src.length < PACK_SIZE_BYTES) {
|
||||
// TODO: log error?
|
||||
return;
|
||||
}
|
||||
|
||||
yaw = unbufferDouble(src);
|
||||
pitch = unbufferDouble(src);
|
||||
area = unbufferDouble(src);
|
||||
|
||||
var poseX = unbufferDouble(src);
|
||||
var poseY = unbufferDouble(src);
|
||||
var poseR = unbufferDouble(src);
|
||||
robotRelativePose = new Pose2d(poseX, poseY, Rotation2d.fromDegrees(poseR));
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import edu.wpi.first.networktables.LogMessage;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import java.util.function.Consumer;
|
||||
import org.photonvision.common.configuration.ConfigManager;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.common.scripting.ScriptEventType;
|
||||
@@ -41,9 +42,7 @@ public class NetworkTablesManager {
|
||||
public static boolean isServer = false;
|
||||
|
||||
private static int getTeamNumber() {
|
||||
// TODO: FIX
|
||||
return 0;
|
||||
// return ConfigManager.settings.teamNumber;
|
||||
return ConfigManager.getInstance().getConfig().getNetworkConfig().teamNumber;
|
||||
}
|
||||
|
||||
private static class NTLogger implements Consumer<LogMessage> {
|
||||
|
||||
@@ -21,5 +21,6 @@ public enum LogGroup {
|
||||
Camera,
|
||||
Server,
|
||||
VisionProcess,
|
||||
Data,
|
||||
General
|
||||
}
|
||||
|
||||
@@ -27,11 +27,12 @@ import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import org.photonvision.common.dataflow.DataChangeService;
|
||||
import org.photonvision.common.dataflow.events.OutgoingUIEvent;
|
||||
import org.photonvision.server.UIUpdateType;
|
||||
|
||||
public class Logger {
|
||||
|
||||
private final String className;
|
||||
|
||||
public static final String ANSI_RESET = "\u001B[0m";
|
||||
public static final String ANSI_BLACK = "\u001B[30m";
|
||||
public static final String ANSI_RED = "\u001B[31m";
|
||||
@@ -42,7 +43,10 @@ public class Logger {
|
||||
public static final String ANSI_CYAN = "\u001B[36m";
|
||||
public static final String ANSI_WHITE = "\u001B[37m";
|
||||
|
||||
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
private static final SimpleDateFormat simpleDateFormat =
|
||||
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
private final String className;
|
||||
private final LogGroup group;
|
||||
|
||||
public Logger(Class<?> clazz, LogGroup group) {
|
||||
@@ -50,6 +54,11 @@ public class Logger {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
public Logger(Class<?> clazz, String suffix, LogGroup group) {
|
||||
this.className = clazz.getSimpleName() + " - " + suffix;
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
public static String getDate() {
|
||||
return simpleDateFormat.format(new Date());
|
||||
}
|
||||
@@ -81,11 +90,13 @@ public class Logger {
|
||||
levelMap.put(LogGroup.Camera, Level.INFO);
|
||||
levelMap.put(LogGroup.General, Level.INFO);
|
||||
levelMap.put(LogGroup.Server, Level.INFO);
|
||||
levelMap.put(LogGroup.Data, Level.INFO);
|
||||
levelMap.put(LogGroup.VisionProcess, Level.INFO);
|
||||
}
|
||||
|
||||
static {
|
||||
currentAppenders.add(new ConsoleAppender());
|
||||
currentAppenders.add(new UILogAppender());
|
||||
}
|
||||
|
||||
public static void addFileAppender(Path logFilePath) {
|
||||
@@ -113,7 +124,7 @@ public class Logger {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldLog(Level logLevel, LogGroup group) {
|
||||
public static boolean shouldLog(Level logLevel, LogGroup group) {
|
||||
return logLevel.code <= levelMap.get(group).code;
|
||||
}
|
||||
|
||||
@@ -152,8 +163,18 @@ public class Logger {
|
||||
}
|
||||
}
|
||||
|
||||
private static class UILogAppender extends Appender {
|
||||
@Override
|
||||
void log(String message) {
|
||||
var message_ = new HashMap<>();
|
||||
message_.put("logMessage", message);
|
||||
DataChangeService.getInstance()
|
||||
.publishEvent(new OutgoingUIEvent<>(UIUpdateType.BROADCAST, "log", message_));
|
||||
}
|
||||
}
|
||||
|
||||
private static class AsyncFileAppender extends Appender {
|
||||
private Path filePath;
|
||||
private final Path filePath;
|
||||
|
||||
public AsyncFileAppender(Path logFilePath) {
|
||||
this.filePath = logFilePath;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
|
||||
public final class SerializationUtils {
|
||||
private static final Logger LOGGER = new Logger(SerializationUtils.class, LogGroup.General);
|
||||
|
||||
public static HashMap<String, Object> objectToHashMap(Object src) {
|
||||
var ret = new HashMap<String, Object>();
|
||||
for (var field : src.getClass().getFields()) {
|
||||
try {
|
||||
if (!field
|
||||
.getType()
|
||||
.isEnum()) { // if the field is not an enum, get it based on the current pipeline
|
||||
ret.put(field.getName(), field.get(src));
|
||||
} else {
|
||||
var ordinal = (Enum) field.get(src);
|
||||
ret.put(field.getName(), ordinal.ordinal());
|
||||
}
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
LOGGER.error("Could not serialize " + src.getClass().getSimpleName());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,10 @@ public class DoubleCouple extends NumberCouple<Double> {
|
||||
super(0.0, 0.0);
|
||||
}
|
||||
|
||||
public DoubleCouple(Number first, Number second) {
|
||||
super(first.doubleValue(), second.doubleValue());
|
||||
}
|
||||
|
||||
public DoubleCouple(Double first, Double second) {
|
||||
super(first, second);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user