mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-25 01:41:40 +00:00
Metrics and lighting implementation (#116)
Implements metrics and lighting control.
This commit is contained in:
@@ -38,9 +38,9 @@
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<v-list-item
|
||||
ref="camerasTabOpener"
|
||||
link
|
||||
to="cameras"
|
||||
ref="camerasTabOpener"
|
||||
@click="switchToDriverMode()"
|
||||
>
|
||||
<v-list-item-icon>
|
||||
@@ -53,6 +53,7 @@
|
||||
<v-list-item
|
||||
link
|
||||
to="settings"
|
||||
@click="switchToSettingsTab()"
|
||||
>
|
||||
<v-list-item-icon>
|
||||
<v-icon>mdi-settings</v-icon>
|
||||
@@ -118,7 +119,7 @@
|
||||
>
|
||||
<v-layout>
|
||||
<v-flex>
|
||||
<router-view v-on:switch-to-cameras="switchToDriverMode" />
|
||||
<router-view @switch-to-cameras="switchToDriverMode" />
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-container>
|
||||
@@ -246,6 +247,9 @@ import Logs from "./views/LogsView"
|
||||
this.handleInputWithIndex('currentPipeline', this.previouslySelectedIndex || 0);
|
||||
}
|
||||
this.previouslySelectedIndex = null;
|
||||
},
|
||||
switchToSettingsTab() {
|
||||
this.axios.post('http://' + this.$address + '/api/sendMetrics', {})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -127,6 +127,14 @@ export default new Vuex.Store({
|
||||
patternHeight: 7,
|
||||
boardType: 0, // Chessboard, dotboard
|
||||
},
|
||||
metrics: {
|
||||
cpuTemp: "N/A",
|
||||
cpuUtil: "N/A",
|
||||
cpuMem: "N/A",
|
||||
gpuMem: "N/A",
|
||||
ramUtil: "N/A",
|
||||
gpuMemUtil: "N/A",
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
compactMode: set('compactMode'),
|
||||
@@ -135,6 +143,7 @@ export default new Vuex.Store({
|
||||
selectedOutputs: set('selectedOutputs'),
|
||||
settings: set('settings'),
|
||||
calibrationData: set('calibrationData'),
|
||||
metrics: set('metrics'),
|
||||
logString: (state, newStr) => {
|
||||
const str = state.logMessages;
|
||||
str.push(newStr)
|
||||
@@ -182,8 +191,12 @@ export default new Vuex.Store({
|
||||
Vue.set(state, 'pipelineResults', payload[key])
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
mutateEnabledLEDPercentage(state, payload) {
|
||||
const settings = state.settings;
|
||||
settings.lighting.brightness = payload;
|
||||
Vue.set(state, "settings", settings);
|
||||
},
|
||||
|
||||
mutateCalibrationState: (state, payload) => {
|
||||
|
||||
@@ -8,32 +8,19 @@
|
||||
cols="12"
|
||||
style="max-width: 1400px"
|
||||
>
|
||||
<v-form
|
||||
ref="form"
|
||||
v-model="valid"
|
||||
<v-card
|
||||
v-for="item in tabList"
|
||||
:key="item.name"
|
||||
dark
|
||||
class="mb-3 pr-6 pb-3"
|
||||
style="background-color: #006492;"
|
||||
>
|
||||
<v-card
|
||||
v-for="item in tabList"
|
||||
:key="item.name"
|
||||
dark
|
||||
class="mb-3 pr-6 pb-3"
|
||||
style="background-color: #006492;"
|
||||
>
|
||||
<v-card-title>{{ item.name }}</v-card-title>
|
||||
<component
|
||||
:is="item"
|
||||
class="ml-5"
|
||||
/>
|
||||
</v-card>
|
||||
<v-btn
|
||||
color="accent"
|
||||
style="color: black; width: 100%;"
|
||||
:disabled="!valid"
|
||||
@click="sendGeneralSettings()"
|
||||
>
|
||||
Save
|
||||
</v-btn>
|
||||
</v-form>
|
||||
<v-card-title>{{ item.name }}</v-card-title>
|
||||
<component
|
||||
:is="item"
|
||||
class="ml-5"
|
||||
/>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-snackbar
|
||||
@@ -61,7 +48,6 @@
|
||||
data() {
|
||||
return {
|
||||
selectedTab: 0,
|
||||
valid: true, // Are all settings valid
|
||||
snack: false,
|
||||
snackbar: {
|
||||
color: "accent",
|
||||
@@ -86,28 +72,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
sendGeneralSettings() {
|
||||
this.axios.post("http://" + this.$address + "/api/settings/general", this.settings).then(
|
||||
function (response) {
|
||||
if (response.status === 200) {
|
||||
this.snackbar = {
|
||||
color: "success",
|
||||
text: "Settings updated successfully"
|
||||
};
|
||||
this.snack = true;
|
||||
}
|
||||
},
|
||||
function (error) {
|
||||
this.snackbar = {
|
||||
color: "error",
|
||||
text: (error.response || {data: "Couldn't save settings"}).data
|
||||
};
|
||||
this.snack = true;
|
||||
}
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
<span>Version: {{ settings.version }}</span>
|
||||
—
|
||||
<div v-if="settings.hardwareModel !== ''">
|
||||
<span>Hardware model: {{ settings.hardwareModel }}</span>
|
||||
—
|
||||
<v-row class="pa-4">
|
||||
<span>{{ infoTabs.join(' — ') }}</span>
|
||||
</v-row>
|
||||
|
||||
<div v-if="metrics.cpuUtil !== 'N/A'">
|
||||
<v-row class="pa-4">
|
||||
<span>CPU Usage: {{ metrics.cpuUtil.replace(" ", "") }}%</span>
|
||||
–
|
||||
<span>CPU Temp: {{ parseInt(metrics.cpuTemp) }}° C</span>
|
||||
–
|
||||
<span>CPU Memory Usage: {{ metrics.ramUtil.replace(" ", "") }}MB of {{ metrics.cpuMem }}MB</span>
|
||||
–
|
||||
<span>GPU Memory Usage: {{ metrics.gpuMemUtil }}MB of {{ metrics.gpuMem }}MB</span>
|
||||
</v-row>
|
||||
</div>
|
||||
<span>Platform: {{ settings.hardwarePlatform }}</span>
|
||||
—
|
||||
<span>GPU Acceleration: {{ settings.gpuAcceleration ? "Enabled" : "Unsupported" }}{{ settings.gpuAcceleration ? " (" + settings.gpuAcceleration + " mode)" : "" }}</span>
|
||||
|
||||
<v-row>
|
||||
<v-col
|
||||
cols="12"
|
||||
@@ -21,7 +28,8 @@
|
||||
>
|
||||
<v-icon left>
|
||||
mdi-download
|
||||
</v-icon> Export Settings
|
||||
</v-icon>
|
||||
Export Settings
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col
|
||||
@@ -35,7 +43,8 @@
|
||||
>
|
||||
<v-icon left>
|
||||
mdi-upload
|
||||
</v-icon> Import Settings
|
||||
</v-icon>
|
||||
Import Settings
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col
|
||||
@@ -48,7 +57,8 @@
|
||||
>
|
||||
<v-icon left>
|
||||
mdi-restart
|
||||
</v-icon> Restart Photon
|
||||
</v-icon>
|
||||
Restart Photon
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col
|
||||
@@ -61,7 +71,8 @@
|
||||
>
|
||||
<v-icon left>
|
||||
mdi-restart
|
||||
</v-icon> Restart Device
|
||||
</v-icon>
|
||||
Restart Device
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
@@ -108,6 +119,21 @@ export default {
|
||||
computed: {
|
||||
settings() {
|
||||
return this.$store.state.settings.general;
|
||||
},
|
||||
infoTabs() {
|
||||
let ret = [];
|
||||
let idx = 0;
|
||||
ret[idx++] = `Version: ${this.settings.version}`;
|
||||
if (this.settings.hardwareModel !== '') {
|
||||
ret[idx++] = `Hardware model: ${this.settings.hardwareModel}`;
|
||||
}
|
||||
ret[idx++] = `Platform: ${this.settings.hardwarePlatform}`;
|
||||
ret[idx++] = `GPU Acceleration: ${this.settings.gpuAcceleration ? "Enabled" : "Unsupported"}${this.settings.gpuAcceleration ? " (" + this.settings.gpuAcceleration + " mode)" : ""}`
|
||||
|
||||
return ret;
|
||||
},
|
||||
metrics() {
|
||||
return this.$store.state.metrics;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<CVslider
|
||||
v-model="settings.brightness"
|
||||
v-model="enabledLEDPercentage"
|
||||
class="pt-2"
|
||||
slider-cols="12"
|
||||
name="Brightness"
|
||||
min="0"
|
||||
max="100"
|
||||
@input="handleData('accuracy')"
|
||||
@rollback="e => rollback('accuracy', e)"
|
||||
@input="handleData('enabledLEDPercentage')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -22,6 +21,14 @@
|
||||
CVslider,
|
||||
},
|
||||
computed: {
|
||||
enabledLEDPercentage: {
|
||||
get() {
|
||||
return this.settings.brightness
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit("mutateEnabledLEDPercentage", value)
|
||||
}
|
||||
},
|
||||
isDHCP() {
|
||||
return this.settings.connectionType === 0;
|
||||
},
|
||||
|
||||
@@ -1,36 +1,50 @@
|
||||
<template>
|
||||
<div>
|
||||
<CVnumberinput
|
||||
v-model="settings.teamNumber"
|
||||
name="Team Number"
|
||||
:rules="[v => (v > 0) || 'Team number must be greater than zero', v => (v < 10000) || 'Team number must have fewer than five digits']"
|
||||
class="mb-4"
|
||||
/>
|
||||
<CVSwitch
|
||||
v-model="settings.runNTServer"
|
||||
name="Run NetworkTables Server"
|
||||
tooltip="If enabled, this device will create a NT server. This is useful for home debugging, but should be disabled on-robot."
|
||||
/>
|
||||
<template v-if="$store.state.settings.networkSettings.supported">
|
||||
<CVradio
|
||||
v-model="settings.connectionType"
|
||||
:list="['DHCP','Static']"
|
||||
<v-form
|
||||
ref="form"
|
||||
v-model="valid"
|
||||
>
|
||||
<CVnumberinput
|
||||
v-model="settings.teamNumber"
|
||||
:disabled="settings.runNTServer"
|
||||
name="Team Number"
|
||||
:rules="[v => (v > 0) || 'Team number must be greater than zero', v => (v < 10000) || 'Team number must have fewer than five digits']"
|
||||
class="mb-4"
|
||||
/>
|
||||
<template v-if="!isDHCP">
|
||||
<CVinput
|
||||
v-model="settings.staticIp"
|
||||
:input-cols="inputCols"
|
||||
:rules="[v => isIPv4(v) || 'Invalid IPv4 address']"
|
||||
name="IP"
|
||||
<CVSwitch
|
||||
v-model="settings.runNTServer"
|
||||
name="Run NetworkTables Server"
|
||||
tooltip="If enabled, this device will create a NT server. This is useful for home debugging, but should be disabled on-robot."
|
||||
/>
|
||||
<template v-if="$store.state.settings.networkSettings.supported">
|
||||
<CVradio
|
||||
v-model="settings.connectionType"
|
||||
:list="['DHCP','Static']"
|
||||
/>
|
||||
<template v-if="!isDHCP">
|
||||
<CVinput
|
||||
v-model="settings.staticIp"
|
||||
:input-cols="inputCols"
|
||||
:rules="[v => isIPv4(v) || 'Invalid IPv4 address']"
|
||||
name="IP"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
<CVinput
|
||||
v-model="settings.hostname"
|
||||
:input-cols="inputCols"
|
||||
:rules="[v => isHostname(v) || 'Invalid hostname']"
|
||||
name="Hostname"
|
||||
/>
|
||||
<CVinput
|
||||
v-model="settings.hostname"
|
||||
:input-cols="inputCols"
|
||||
:rules="[v => isHostname(v) || 'Invalid hostname']"
|
||||
name="Hostname"
|
||||
/>
|
||||
</v-form>
|
||||
<v-btn
|
||||
color="accent"
|
||||
style="color: black; width: 100%;"
|
||||
:disabled="!valid"
|
||||
@click="sendGeneralSettings()"
|
||||
>
|
||||
Save
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -61,7 +75,8 @@
|
||||
text: ""
|
||||
},
|
||||
snack: false,
|
||||
isLoading: false
|
||||
isLoading: false,
|
||||
valid: true, // Are all settings valid
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -99,7 +114,27 @@
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
sendGeneralSettings() {
|
||||
this.axios.post("http://" + this.$address + "/api/settings/general", this.settings).then(
|
||||
function (response) {
|
||||
if (response.status === 200) {
|
||||
this.snackbar = {
|
||||
color: "success",
|
||||
text: "Settings updated successfully"
|
||||
};
|
||||
this.snack = true;
|
||||
}
|
||||
},
|
||||
function (error) {
|
||||
this.snackbar = {
|
||||
color: "error",
|
||||
text: (error.response || {data: "Couldn't save settings"}).data
|
||||
};
|
||||
this.snack = true;
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.apache.commons.cli.*;
|
||||
import org.photonvision.common.configuration.CameraConfiguration;
|
||||
import org.photonvision.common.configuration.ConfigManager;
|
||||
import org.photonvision.common.dataflow.networktables.NetworkTablesManager;
|
||||
import org.photonvision.common.hardware.HardwareManager;
|
||||
import org.photonvision.common.hardware.Platform;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.LogLevel;
|
||||
@@ -133,14 +134,13 @@ public class Main {
|
||||
logger.error("Failed to parse command-line options!", e);
|
||||
}
|
||||
|
||||
logger.info("Running in " + (isRelease ? "release" : "development") + " mode!");
|
||||
var logLevel = (isRelease || printDebugLogs) ? LogLevel.INFO : LogLevel.DEBUG;
|
||||
var logLevel = LogLevel.DEBUG;
|
||||
Logger.setLevel(LogGroup.Camera, logLevel);
|
||||
Logger.setLevel(LogGroup.WebServer, logLevel);
|
||||
Logger.setLevel(LogGroup.VisionModule, logLevel);
|
||||
Logger.setLevel(LogGroup.Data, logLevel);
|
||||
Logger.setLevel(LogGroup.General, logLevel);
|
||||
logger.info("Logging initialized in " + (isRelease ? "Release" : "Debug") + " mode.");
|
||||
logger.info("Logging initialized in debug mode.");
|
||||
|
||||
logger.info(
|
||||
"Starting PhotonVision version "
|
||||
@@ -159,17 +159,14 @@ public class Main {
|
||||
ConfigManager.getInstance().load(); // init config manager
|
||||
ConfigManager.getInstance().requestSave();
|
||||
|
||||
// Force load the hardware manager
|
||||
HardwareManager.getInstance();
|
||||
|
||||
NetworkManager.getInstance().initialize(false);
|
||||
|
||||
NetworkTablesManager.getInstance()
|
||||
.setConfig(ConfigManager.getInstance().getConfig().getNetworkConfig());
|
||||
|
||||
// HashMap<VisionSource, List<CVPipelineSettings>> allSources = gatherSources();
|
||||
|
||||
// logger.info("Adding " + allSources.size() + " configs to VMM.");
|
||||
// VisionModuleManager.getInstance().addSources(allSources);
|
||||
// ConfigManager.getInstance().addCameraConfigurations(allSources);
|
||||
|
||||
if (!isTestMode) {
|
||||
VisionSourceManager.getInstance()
|
||||
.registerLoadedConfigs(
|
||||
|
||||
@@ -42,6 +42,7 @@ public class ConfigManager {
|
||||
|
||||
private PhotonConfiguration config;
|
||||
private final File hardwareConfigFile;
|
||||
private final File hardwareSettingsFile;
|
||||
private final File networkConfigFile;
|
||||
private final File camerasFolder;
|
||||
|
||||
@@ -82,6 +83,8 @@ public class ConfigManager {
|
||||
this.configDirectoryFile = new File(configDirectoryFile.toUri());
|
||||
this.hardwareConfigFile =
|
||||
new File(Path.of(configDirectoryFile.toString(), "hardwareConfig.json").toUri());
|
||||
this.hardwareSettingsFile =
|
||||
new File(Path.of(configDirectoryFile.toString(), "hardwareSettings.json").toUri());
|
||||
this.networkConfigFile =
|
||||
new File(Path.of(configDirectoryFile.toString(), "networkSettings.json").toUri());
|
||||
this.camerasFolder = new File(Path.of(configDirectoryFile.toString(), "cameras").toUri());
|
||||
@@ -110,6 +113,7 @@ public class ConfigManager {
|
||||
}
|
||||
|
||||
HardwareConfig hardwareConfig;
|
||||
HardwareSettings hardwareSettings;
|
||||
NetworkConfig networkConfig;
|
||||
|
||||
if (hardwareConfigFile.exists()) {
|
||||
@@ -129,6 +133,23 @@ public class ConfigManager {
|
||||
hardwareConfig = new HardwareConfig();
|
||||
}
|
||||
|
||||
if (hardwareSettingsFile.exists()) {
|
||||
try {
|
||||
hardwareSettings =
|
||||
JacksonUtils.deserialize(hardwareSettingsFile.toPath(), HardwareSettings.class);
|
||||
if (hardwareSettings == null) {
|
||||
logger.error("Could not deserialize hardware settings! Loading defaults");
|
||||
hardwareSettings = new HardwareSettings();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("Could not deserialize hardware settings! Loading defaults");
|
||||
hardwareSettings = new HardwareSettings();
|
||||
}
|
||||
} else {
|
||||
logger.info("Hardware settings does not exist! Loading defaults");
|
||||
hardwareSettings = new HardwareSettings();
|
||||
}
|
||||
|
||||
if (networkConfigFile.exists()) {
|
||||
try {
|
||||
networkConfig = JacksonUtils.deserialize(networkConfigFile.toPath(), NetworkConfig.class);
|
||||
@@ -155,7 +176,9 @@ public class ConfigManager {
|
||||
|
||||
HashMap<String, CameraConfiguration> cameraConfigurations = loadCameraConfigs();
|
||||
|
||||
this.config = new PhotonConfiguration(hardwareConfig, networkConfig, cameraConfigurations);
|
||||
this.config =
|
||||
new PhotonConfiguration(
|
||||
hardwareConfig, hardwareSettings, networkConfig, cameraConfigurations);
|
||||
}
|
||||
|
||||
public void saveToDisk() {
|
||||
@@ -167,6 +190,11 @@ public class ConfigManager {
|
||||
} catch (IOException e) {
|
||||
logger.error("Could not save network config!", e);
|
||||
}
|
||||
try {
|
||||
JacksonUtils.serialize(hardwareSettingsFile.toPath(), config.getHardwareSettings());
|
||||
} catch (IOException e) {
|
||||
logger.error("Could not save hardware config!", e);
|
||||
}
|
||||
|
||||
// save all of our cameras
|
||||
var cameraConfigMap = config.getCameraConfigurations();
|
||||
|
||||
@@ -43,8 +43,8 @@ public class HardwareConfig {
|
||||
public final String cpuMemoryCommand;
|
||||
public final String cpuUtilCommand;
|
||||
public final String gpuMemoryCommand;
|
||||
public final String gpuTempCommand;
|
||||
public final String ramUtilCommand;
|
||||
public final String gpuMemUsageCommand;
|
||||
|
||||
// Device stuff
|
||||
public final String restartHardwareCommand;
|
||||
@@ -68,9 +68,9 @@ public class HardwareConfig {
|
||||
cpuMemoryCommand = "";
|
||||
cpuUtilCommand = "";
|
||||
gpuMemoryCommand = "";
|
||||
gpuTempCommand = "";
|
||||
ramUtilCommand = "";
|
||||
ledBlinkCommand = "";
|
||||
gpuMemUsageCommand = "";
|
||||
|
||||
restartHardwareCommand = "";
|
||||
vendorFOV = -1;
|
||||
@@ -95,8 +95,8 @@ public class HardwareConfig {
|
||||
String cpuMemoryCommand,
|
||||
String cpuUtilCommand,
|
||||
String gpuMemoryCommand,
|
||||
String gpuTempCommand,
|
||||
String ramUtilCommand,
|
||||
String gpuMemUsageCommand,
|
||||
String restartHardwareCommand,
|
||||
double vendorFOV,
|
||||
List<Integer> blacklistedResIndices) {
|
||||
@@ -116,11 +116,11 @@ public class HardwareConfig {
|
||||
this.cpuMemoryCommand = cpuMemoryCommand;
|
||||
this.cpuUtilCommand = cpuUtilCommand;
|
||||
this.gpuMemoryCommand = gpuMemoryCommand;
|
||||
this.gpuTempCommand = gpuTempCommand;
|
||||
this.ramUtilCommand = ramUtilCommand;
|
||||
this.restartHardwareCommand = restartHardwareCommand;
|
||||
this.vendorFOV = vendorFOV;
|
||||
this.blacklistedResIndices = blacklistedResIndices;
|
||||
this.gpuMemUsageCommand = gpuMemUsageCommand;
|
||||
}
|
||||
|
||||
public final boolean hasPresetFOV() {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.configuration;
|
||||
|
||||
public class HardwareSettings {
|
||||
public int ledBrightnessPercentage = 100;
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import org.photonvision.common.hardware.Platform;
|
||||
import org.photonvision.common.networking.NetworkMode;
|
||||
|
||||
public class NetworkConfig {
|
||||
public int teamNumber = -1;
|
||||
public int teamNumber = 0;
|
||||
public NetworkMode connectionType = NetworkMode.DHCP;
|
||||
public String staticIp = "";
|
||||
public String netmask = "";
|
||||
|
||||
@@ -30,6 +30,30 @@ import org.photonvision.vision.processes.VisionModuleManager;
|
||||
|
||||
// TODO rename this class
|
||||
public class PhotonConfiguration {
|
||||
|
||||
private HardwareConfig hardwareConfig;
|
||||
private HardwareSettings hardwareSettings;
|
||||
private NetworkConfig networkConfig;
|
||||
private HashMap<String, CameraConfiguration> cameraConfigurations;
|
||||
|
||||
public PhotonConfiguration(
|
||||
HardwareConfig hardwareConfig,
|
||||
HardwareSettings hardwareSettings,
|
||||
NetworkConfig networkConfig) {
|
||||
this(hardwareConfig, hardwareSettings, networkConfig, new HashMap<>());
|
||||
}
|
||||
|
||||
public PhotonConfiguration(
|
||||
HardwareConfig hardwareConfig,
|
||||
HardwareSettings hardwareSettings,
|
||||
NetworkConfig networkConfig,
|
||||
HashMap<String, CameraConfiguration> cameraConfigurations) {
|
||||
this.hardwareConfig = hardwareConfig;
|
||||
this.hardwareSettings = hardwareSettings;
|
||||
this.networkConfig = networkConfig;
|
||||
this.cameraConfigurations = cameraConfigurations;
|
||||
}
|
||||
|
||||
public HardwareConfig getHardwareConfig() {
|
||||
return hardwareConfig;
|
||||
}
|
||||
@@ -38,6 +62,10 @@ public class PhotonConfiguration {
|
||||
return networkConfig;
|
||||
}
|
||||
|
||||
public HardwareSettings getHardwareSettings() {
|
||||
return hardwareSettings;
|
||||
}
|
||||
|
||||
public void setNetworkConfig(NetworkConfig networkConfig) {
|
||||
this.networkConfig = networkConfig;
|
||||
}
|
||||
@@ -60,25 +88,6 @@ public class PhotonConfiguration {
|
||||
cameraConfigurations.put(name, config);
|
||||
}
|
||||
|
||||
private HardwareConfig hardwareConfig;
|
||||
|
||||
private NetworkConfig networkConfig;
|
||||
|
||||
private HashMap<String, CameraConfiguration> cameraConfigurations;
|
||||
|
||||
public PhotonConfiguration(HardwareConfig hardwareConfig, NetworkConfig networkConfig) {
|
||||
this(hardwareConfig, networkConfig, new HashMap<>());
|
||||
}
|
||||
|
||||
public PhotonConfiguration(
|
||||
HardwareConfig hardwareConfig,
|
||||
NetworkConfig networkConfig,
|
||||
HashMap<String, CameraConfiguration> cameraConfigurations) {
|
||||
this.hardwareConfig = hardwareConfig;
|
||||
this.networkConfig = networkConfig;
|
||||
this.cameraConfigurations = cameraConfigurations;
|
||||
}
|
||||
|
||||
public Map<String, Object> toHashMap() {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
var settingsSubmap = new HashMap<String, Object>();
|
||||
@@ -91,7 +100,9 @@ public class PhotonConfiguration {
|
||||
.map(SerializationUtils::objectToHashMap)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
settingsSubmap.put("lighting", SerializationUtils.objectToHashMap(hardwareConfig));
|
||||
var lightingConfig = new UILightingConfig();
|
||||
// TODO set constants
|
||||
settingsSubmap.put("lighting", SerializationUtils.objectToHashMap(lightingConfig));
|
||||
|
||||
var generalSubmap = new HashMap<String, Object>();
|
||||
generalSubmap.put("version", PhotonVersion.versionString);
|
||||
@@ -105,6 +116,11 @@ public class PhotonConfiguration {
|
||||
return map;
|
||||
}
|
||||
|
||||
public static class UILightingConfig {
|
||||
public int brightness = 0;
|
||||
public boolean supported = true;
|
||||
}
|
||||
|
||||
public static class UICameraConfiguration {
|
||||
@SuppressWarnings("unused")
|
||||
public double fov, tiltDegrees;
|
||||
|
||||
@@ -47,7 +47,7 @@ public class HardwareManager {
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
private final NTDataChangeListener ledModeListener;
|
||||
|
||||
public final VisionLED visionLED;
|
||||
public final VisionLED visionLED; // May be null if no LED is specified
|
||||
|
||||
public static HardwareManager getInstance() {
|
||||
if (instance == null) {
|
||||
@@ -61,28 +61,47 @@ public class HardwareManager {
|
||||
CustomGPIO.setConfig(hardwareConfig);
|
||||
MetricsBase.setConfig(hardwareConfig);
|
||||
|
||||
statusLED = new StatusLED(hardwareConfig.statusRGBPins);
|
||||
statusLED =
|
||||
hardwareConfig.statusRGBPins.size() == 3
|
||||
? new StatusLED(hardwareConfig.statusRGBPins)
|
||||
: null;
|
||||
visionLED =
|
||||
new VisionLED(
|
||||
hardwareConfig.ledPins,
|
||||
hardwareConfig.ledPWMFrequency,
|
||||
(hardwareConfig.ledPWMRange != null && hardwareConfig.ledPWMRange.size() == 2)
|
||||
? hardwareConfig.ledPWMRange.get(1)
|
||||
: 0);
|
||||
hardwareConfig.ledPins.isEmpty()
|
||||
? null
|
||||
: new VisionLED(
|
||||
hardwareConfig.ledPins,
|
||||
hardwareConfig.ledPWMFrequency,
|
||||
(hardwareConfig.ledPWMRange != null && hardwareConfig.ledPWMRange.size() == 2)
|
||||
? hardwareConfig.ledPWMRange.get(1)
|
||||
: 0);
|
||||
|
||||
ledModeEntry = NetworkTablesManager.getInstance().kRootTable.getEntry("ledMode");
|
||||
ledModeEntry.setNumber(VisionLEDMode.VLM_DEFAULT.value);
|
||||
ledModeListener = new NTDataChangeListener(ledModeEntry, visionLED::onLedModeChange);
|
||||
ledModeListener =
|
||||
visionLED == null
|
||||
? null
|
||||
: new NTDataChangeListener(ledModeEntry, visionLED::onLedModeChange);
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(this::onJvmExit));
|
||||
|
||||
if (visionLED != null)
|
||||
visionLED.setBrightness(
|
||||
ConfigManager.getInstance().getConfig().getHardwareSettings().ledBrightnessPercentage);
|
||||
|
||||
// Start hardware metrics thread (Disabled until implemented)
|
||||
// if (Platform.isLinux()) MetricsPublisher.getInstance().startTask();
|
||||
}
|
||||
|
||||
public void setBrightnessPercent(int percent) {
|
||||
ConfigManager.getInstance().getConfig().getHardwareSettings().ledBrightnessPercentage = percent;
|
||||
if (visionLED != null) visionLED.setBrightness(percent);
|
||||
ConfigManager.getInstance().requestSave();
|
||||
logger.info("Setting led brightness to " + percent + "%");
|
||||
}
|
||||
|
||||
private void onJvmExit() {
|
||||
logger.info("Shutting down...");
|
||||
visionLED.setState(false);
|
||||
logger.info("Shutting down LEDs...");
|
||||
if (visionLED != null) visionLED.setState(false);
|
||||
}
|
||||
|
||||
public boolean restartDevice() {
|
||||
|
||||
@@ -19,17 +19,23 @@ package org.photonvision.common.hardware.metrics;
|
||||
|
||||
public class CPUMetrics extends MetricsBase {
|
||||
|
||||
public CPUMetrics() {}
|
||||
private String cpuMemSplit = null;
|
||||
|
||||
public String getMemory() {
|
||||
if (cpuMemoryCommand.isEmpty()) return "";
|
||||
return execute(cpuMemoryCommand);
|
||||
if (cpuMemSplit == null) {
|
||||
cpuMemSplit = execute(cpuMemoryCommand);
|
||||
}
|
||||
return cpuMemSplit;
|
||||
}
|
||||
|
||||
// TODO: Command should return in Celsius
|
||||
public String getTemp() {
|
||||
if (cpuTemperatureCommand.isEmpty()) return "";
|
||||
return execute(cpuTemperatureCommand);
|
||||
try {
|
||||
return execute(cpuTemperatureCommand);
|
||||
} catch (Exception e) {
|
||||
return "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
public String getUtilization() {
|
||||
|
||||
@@ -18,11 +18,16 @@
|
||||
package org.photonvision.common.hardware.metrics;
|
||||
|
||||
public class GPUMetrics extends MetricsBase {
|
||||
public String getMemory() {
|
||||
return execute(gpuMemoryCommand);
|
||||
private String gpuMemSplit = null;
|
||||
|
||||
public String getGPUMemorySplit() {
|
||||
if (gpuMemSplit == null) {
|
||||
gpuMemSplit = execute(gpuMemoryCommand);
|
||||
}
|
||||
return gpuMemSplit;
|
||||
}
|
||||
|
||||
public String getTemp() {
|
||||
return execute(gpuTemperatureCommand);
|
||||
public String getMallocedMemory() {
|
||||
return execute(gpuMemUsageCommand);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,18 +26,18 @@ import org.photonvision.common.util.ShellExec;
|
||||
public abstract class MetricsBase {
|
||||
private static final Logger logger = new Logger(MetricsBase.class, LogGroup.General);
|
||||
// CPU
|
||||
public static String cpuMemoryCommand = "sudo vcgencmd get_mem arm | grep -Eo '[0-9]+'";
|
||||
public static String cpuMemoryCommand = "vcgencmd get_mem arm | grep -Eo '[0-9]+'";
|
||||
public static String cpuTemperatureCommand =
|
||||
"sudo cat /sys/class/thermal/thermal_zone0/temp | grep -x -E '[0-9]+'";
|
||||
"sed 's/.\\{3\\}$/.&/' <<< cat /sys/class/thermal/thermal_zone0/temp";
|
||||
public static String cpuUtilizationCommand =
|
||||
"sudo top -bn1 | grep \"Cpu(s)\" | sed \"s/.*, *\\([0-9.]*\\)%* id.*/\\1/\" | awk '{print 100 - $1}'";
|
||||
"top -bn1 | grep \"Cpu(s)\" | sed \"s/.*, *\\([0-9.]*\\)%* id.*/\\1/\" | awk '{print 100 - $1}'";
|
||||
|
||||
// GPU
|
||||
public static String gpuMemoryCommand = "sudo vcgencmd get_mem gpu | grep -Eo '[0-9]+'";
|
||||
public static String gpuTemperatureCommand = "sudo vcgencmd measure_temp | sed 's/[^0-9]*//g'\n";
|
||||
public static String gpuMemoryCommand = "vcgencmd get_mem gpu | grep -Eo '[0-9]+'";
|
||||
public static String gpuMemUsageCommand = "vcgencmd get_mem malloc | grep -Eo '[0-9]+'";
|
||||
|
||||
// RAM
|
||||
public static String ramUsageCommand = "sudo free | awk -v i=2 -v j=3 'FNR == i {print $j}'";
|
||||
public static String ramUsageCommand = "free --mega | awk -v i=2 -v j=3 'FNR == i {print $j}'";
|
||||
|
||||
private static ShellExec runCommand = new ShellExec(true, true);
|
||||
|
||||
@@ -48,7 +48,7 @@ public abstract class MetricsBase {
|
||||
cpuUtilizationCommand = config.cpuUtilCommand;
|
||||
|
||||
gpuMemoryCommand = config.gpuMemoryCommand;
|
||||
gpuTemperatureCommand = config.gpuTempCommand;
|
||||
gpuMemUsageCommand = config.gpuMemUsageCommand;
|
||||
|
||||
ramUsageCommand = config.ramUtilCommand;
|
||||
}
|
||||
|
||||
@@ -17,15 +17,15 @@
|
||||
|
||||
package org.photonvision.common.hardware.metrics;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import java.util.HashMap;
|
||||
import org.photonvision.common.dataflow.DataChangeService;
|
||||
import org.photonvision.common.dataflow.events.OutgoingUIEvent;
|
||||
import org.photonvision.common.hardware.Platform;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.common.util.TimedTaskManager;
|
||||
import org.photonvision.server.SocketHandler;
|
||||
|
||||
public class MetricsPublisher {
|
||||
private final HashMap<String, String> metrics;
|
||||
private static final Logger logger = new Logger(MetricsPublisher.class, LogGroup.General);
|
||||
private static CPUMetrics cpuMetrics;
|
||||
private static GPUMetrics gpuMetrics;
|
||||
@@ -39,26 +39,6 @@ public class MetricsPublisher {
|
||||
cpuMetrics = new CPUMetrics();
|
||||
gpuMetrics = new GPUMetrics();
|
||||
ramMetrics = new RAMMetrics();
|
||||
|
||||
metrics = new HashMap<>();
|
||||
}
|
||||
|
||||
public void startTask() {
|
||||
TimedTaskManager.getInstance()
|
||||
.addTask(
|
||||
"Metrics",
|
||||
() -> {
|
||||
metrics.put("cpuTemp", cpuMetrics.getTemp());
|
||||
metrics.put("cpuUtil", cpuMetrics.getUtilization());
|
||||
metrics.put("cpuMem", cpuMetrics.getMemory());
|
||||
metrics.put("gpuTemp", gpuMetrics.getTemp());
|
||||
metrics.put("gpuMem", gpuMetrics.getMemory());
|
||||
metrics.put("ramUtil", ramMetrics.getUsedRam());
|
||||
|
||||
DataChangeService.getInstance()
|
||||
.publishEvent(new OutgoingUIEvent<>("metrics", metrics));
|
||||
},
|
||||
1000);
|
||||
}
|
||||
|
||||
public void stopTask() {
|
||||
@@ -66,6 +46,32 @@ public class MetricsPublisher {
|
||||
logger.info("This device does not support running bash commands. Stopped metrics thread.");
|
||||
}
|
||||
|
||||
public void publish() {
|
||||
if (!Platform.isRaspberryPi()) {
|
||||
logger.debug("Ignoring metrics on non-Pi devices");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Publishing Metrics...");
|
||||
final var metrics = new HashMap<String, String>();
|
||||
|
||||
metrics.put("cpuTemp", cpuMetrics.getTemp());
|
||||
metrics.put("cpuUtil", cpuMetrics.getUtilization());
|
||||
metrics.put("cpuMem", cpuMetrics.getMemory());
|
||||
metrics.put("gpuMem", gpuMetrics.getGPUMemorySplit());
|
||||
metrics.put("ramUtil", ramMetrics.getUsedRam());
|
||||
metrics.put("gpuMemUtil", gpuMetrics.getMallocedMemory());
|
||||
|
||||
var retMap = new HashMap<String, Object>();
|
||||
retMap.put("metrics", metrics);
|
||||
|
||||
try {
|
||||
SocketHandler.getInstance().broadcastMessage(retMap, null);
|
||||
} catch (JsonProcessingException e) {
|
||||
logger.error("Exception while sending metrics!", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Singleton {
|
||||
public static final MetricsPublisher INSTANCE = new MetricsPublisher();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ public final class SerializationUtils {
|
||||
var ret = new HashMap<String, Object>();
|
||||
for (var field : src.getClass().getFields()) {
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
if (!field
|
||||
.getType()
|
||||
.isEnum()) { // if the field is not an enum, get it based on the current pipeline
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.photonvision.common.configuration.NetworkConfig;
|
||||
import org.photonvision.common.dataflow.networktables.NetworkTablesManager;
|
||||
import org.photonvision.common.hardware.HardwareManager;
|
||||
import org.photonvision.common.hardware.Platform;
|
||||
import org.photonvision.common.hardware.metrics.MetricsPublisher;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.common.networking.NetworkManager;
|
||||
@@ -180,6 +181,11 @@ public class RequestHandler {
|
||||
ctx.status(200);
|
||||
}
|
||||
|
||||
public static void sendMetrics(Context ctx) {
|
||||
MetricsPublisher.getInstance().publish();
|
||||
ctx.status(200);
|
||||
}
|
||||
|
||||
public static class UITargetData {
|
||||
public int index;
|
||||
public TargetModel targetModel;
|
||||
|
||||
@@ -80,6 +80,7 @@ public class Server {
|
||||
app.post("/api/restartDevice", RequestHandler::restartDevice);
|
||||
app.post("api/restartProgram", RequestHandler::restartProgram);
|
||||
app.post("api/vision/pnpModel", RequestHandler::uploadPnpModel);
|
||||
app.post("api/sendMetrics", RequestHandler::sendMetrics);
|
||||
|
||||
app.start(port);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.msgpack.jackson.dataformat.MessagePackFactory;
|
||||
import org.photonvision.common.dataflow.DataChangeDestination;
|
||||
import org.photonvision.common.dataflow.DataChangeService;
|
||||
import org.photonvision.common.dataflow.events.IncomingWebSocketEvent;
|
||||
import org.photonvision.common.hardware.HardwareManager;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.vision.pipeline.PipelineType;
|
||||
@@ -182,6 +183,12 @@ public class SocketHandler {
|
||||
dcService.publishEvent(newPipelineEvent);
|
||||
break;
|
||||
}
|
||||
case SMT_CHANGEBRIGHTNESS:
|
||||
{
|
||||
HardwareManager.getInstance()
|
||||
.setBrightnessPercent(Integer.parseInt(entryValue.toString()));
|
||||
break;
|
||||
}
|
||||
case SMT_DUPLICATEPIPELINE:
|
||||
{
|
||||
var pipeIndex = (Integer) entryValue;
|
||||
|
||||
@@ -34,6 +34,7 @@ public enum SocketMessageType {
|
||||
SMT_STARTPNPCALIBRATION("startPnpCalibration"),
|
||||
SMT_TAKECALIBRATIONSNAPSHOT("takeCalibrationSnapshot"),
|
||||
SMT_DUPLICATEPIPELINE("duplicatePipeline"),
|
||||
SMT_CHANGEBRIGHTNESS("enabledLEDPercentage"),
|
||||
SMT_ROBOTOFFSETPOINT("robotOffsetPoint");
|
||||
|
||||
public final String entryKey;
|
||||
|
||||
@@ -46,8 +46,7 @@ public class HardwareTest {
|
||||
System.out.println("Utilization: : " + cpuMetrics.getUtilization() + "%");
|
||||
|
||||
System.out.println("Printing GPU Info:");
|
||||
System.out.println("Memory: " + gpuMetrics.getMemory() + "MB");
|
||||
System.out.println("Temperature: " + gpuMetrics.getTemp() + "C");
|
||||
System.out.println("Memory: " + gpuMetrics.getGPUMemorySplit() + "MB");
|
||||
|
||||
System.out.println("Printing RAM Info: ");
|
||||
System.out.println("Used RAM: : " + ramMetrics.getUsedRam() + "MB");
|
||||
|
||||
Reference in New Issue
Block a user