Remove camera index in ui (#1677)

With the new camera matching, this is SUPER BAD! Convert to using camera
uuid.

---------

Co-authored-by: Matt <matthew.morley.ca@gmail.com>
This commit is contained in:
Cameron (3539)
2025-01-03 18:50:25 -05:00
committed by GitHub
parent 6c7a174424
commit ab844a77b8
19 changed files with 271 additions and 250 deletions

View File

@@ -34,8 +34,8 @@ if (!is_demo) {
if (data.updatePipelineResult !== undefined) {
useStateStore().updateBackendResultsFromWebsocket(data.updatePipelineResult);
}
if (data.mutatePipelineSettings !== undefined && data.cameraIndex !== undefined) {
useCameraSettingsStore().changePipelineSettingsInStore(data.mutatePipelineSettings, data.cameraIndex);
if (data.mutatePipelineSettings !== undefined && data.cameraUniqueName !== undefined) {
useCameraSettingsStore().changePipelineSettingsInStore(data.mutatePipelineSettings, data.cameraUniqueName);
}
if (data.calibrationData !== undefined) {
useStateStore().updateCalibrationStateValuesFromWebsocket(data.calibrationData);

View File

@@ -20,7 +20,8 @@ const mdAndUp = computed<boolean>(() => getCurrentInstance()?.proxy.$vuetify.bre
const needsCamerasConfigured = computed<boolean>(() => {
return (
useCameraSettingsStore().cameras.length === 0 || useCameraSettingsStore().cameras[0] === PlaceholderCameraSettings
Object.values(useCameraSettingsStore().cameras).length === 0 ||
useCameraSettingsStore().cameras["PlaceHolder Name"] === PlaceholderCameraSettings
);
});
</script>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import PvSelect from "@/components/common/pv-select.vue";
import PvSelect, { type SelectItem } from "@/components/common/pv-select.vue";
import PvNumberInput from "@/components/common/pv-number-input.vue";
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
import { useStateStore } from "@/stores/StateStore";
@@ -121,7 +121,7 @@ const openExportSettingsPrompt = () => {
const yesDeleteMySettingsText = ref("");
const deleteThisCamera = () => {
const payload = {
cameraUniqueName: useCameraSettingsStore().cameraUniqueNames[useStateStore().currentCameraIndex]
cameraUniqueName: useStateStore().currentCameraUniqueName
};
axios
@@ -152,6 +152,12 @@ const deleteThisCamera = () => {
});
showDeleteCamera.value = false;
};
const wrappedCameras = computed<SelectItem[]>(() =>
Object.keys(useCameraSettingsStore().cameras).map((cameraUniqueName) => ({
name: useCameraSettingsStore().cameras[cameraUniqueName].nickname,
value: cameraUniqueName
}))
);
</script>
<template>
@@ -159,9 +165,9 @@ const deleteThisCamera = () => {
<v-card-title>Camera Settings</v-card-title>
<div class="ml-5">
<pv-select
v-model="useStateStore().currentCameraIndex"
v-model="useStateStore().currentCameraUniqueName"
label="Camera"
:items="useCameraSettingsStore().cameraNames"
:items="wrappedCameras"
:select-cols="8"
/>
<pv-number-input
@@ -213,9 +219,7 @@ const deleteThisCamera = () => {
<v-dialog v-model="showDeleteCamera" dark width="1500">
<v-card dark class="dialog-container pa-6" color="primary" flat>
<v-card-title
>Delete camera "{{ useCameraSettingsStore().cameraNames[useStateStore().currentCameraIndex] }}"</v-card-title
>
<v-card-title>Delete camera "{{ useCameraSettingsStore().currentCameraName }}"</v-card-title>
<v-row class="pl-3 align-center pa-6">
<v-col cols="12" md="6">
<span class="mt-3"> This will delete ALL OF YOUR SETTINGS and restart PhotonVision. </span>

View File

@@ -2,7 +2,7 @@
import { computed } from "vue";
import TooltippedLabel from "@/components/common/pv-tooltipped-label.vue";
interface SelectItem {
export interface SelectItem {
name: string | number;
value: string | number;
disabled?: boolean;
@@ -14,7 +14,7 @@ const props = withDefaults(
tooltip?: string;
selectCols?: number;
// TODO fully update v-model usage in custom components on Vue3 update
value: number;
value: any;
disabled?: boolean;
items: string[] | number[] | SelectItem[];
}>(),
@@ -25,7 +25,7 @@ const props = withDefaults(
);
const emit = defineEmits<{
(e: "input", value: number): void;
(e: "input", value: string): void;
}>();
const localValue = computed({

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import PvSelect from "@/components/common/pv-select.vue";
import PvSelect, { type SelectItem } from "@/components/common/pv-select.vue";
import { useStateStore } from "@/stores/StateStore";
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
import { WebsocketPipelineType } from "@/types/WebsocketDataTypes";
@@ -9,10 +9,10 @@ import PvInput from "@/components/common/pv-input.vue";
import { PipelineType } from "@/types/PipelineTypes";
import { useSettingsStore } from "@/stores/settings/GeneralSettingsStore";
const changeCurrentCameraIndex = (index: number) => {
useCameraSettingsStore().setCurrentCameraIndex(index, true);
const changeCurrentCameraUniqueName = (cameraUniqueName: string) => {
useCameraSettingsStore().setCurrentCameraUniqueName(cameraUniqueName, true);
switch (useCameraSettingsStore().cameras[index].pipelineSettings.pipelineType) {
switch (useCameraSettingsStore().cameras[cameraUniqueName].pipelineSettings.pipelineType) {
case PipelineType.Reflective:
pipelineType.value = WebsocketPipelineType.Reflective;
break;
@@ -86,7 +86,7 @@ const cancelCameraNameEdit = () => {
};
// Pipeline Name Edit
const pipelineNamesWrapper = computed<{ name: string; value: number }[]>(() => {
const pipelineNamesWrapper = computed<SelectItem[]>(() => {
const pipelineNames = useCameraSettingsStore().pipelineNames.map((name, index) => ({ name: name, value: index }));
if (useCameraSettingsStore().isDriverMode) {
@@ -212,7 +212,7 @@ const duplicateCurrentPipeline = () => {
// Change Props whenever the pipeline settings are changed
useCameraSettingsStore().$subscribe((mutation, state) => {
const currentCameraSettings = state.cameras[useStateStore().currentCameraIndex];
const currentCameraSettings = state.cameras[useStateStore().currentCameraUniqueName];
switch (currentCameraSettings.pipelineSettings.pipelineType) {
case PipelineType.Reflective:
@@ -232,6 +232,12 @@ useCameraSettingsStore().$subscribe((mutation, state) => {
break;
}
});
const wrappedCameras = computed<SelectItem[]>(() =>
Object.keys(useCameraSettingsStore().cameras).map((cameraUniqueName) => ({
name: useCameraSettingsStore().cameras[cameraUniqueName].nickname,
value: cameraUniqueName
}))
);
</script>
<template>
@@ -240,10 +246,10 @@ useCameraSettingsStore().$subscribe((mutation, state) => {
<v-col cols="10" class="pa-0">
<pv-select
v-if="!isCameraNameEdit"
v-model="useStateStore().currentCameraIndex"
v-model="useStateStore().currentCameraUniqueName"
label="Camera"
:items="useCameraSettingsStore().cameraNames"
@input="changeCurrentCameraIndex"
:items="wrappedCameras"
@input="changeCurrentCameraUniqueName"
/>
<pv-input
v-else

View File

@@ -2,6 +2,7 @@ import { defineStore } from "pinia";
import type { LogMessage, VsmState } from "@/types/SettingTypes";
import type { AutoReconnectingWebsocket } from "@/lib/AutoReconnectingWebsocket";
import type { MultitagResult, PipelineResult } from "@/types/PhotonTrackingTypes";
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
import type {
WebsocketCalibrationData,
WebsocketLogMessage,
@@ -22,7 +23,7 @@ interface StateStore {
showLogModal: boolean;
sidebarFolded: boolean;
logMessages: LogMessage[];
currentCameraIndex: number;
currentCameraUniqueName: string;
backendResults: Record<number, PipelineResult>;
multitagResultBuffer: Record<string, MultitagResult[]>;
@@ -48,6 +49,7 @@ interface StateStore {
export const useStateStore = defineStore("state", {
state: (): StateStore => {
const cameraStore = useCameraSettingsStore();
return {
backendConnected: false,
websocket: undefined,
@@ -59,7 +61,7 @@ export const useStateStore = defineStore("state", {
sidebarFolded:
localStorage.getItem("sidebarFolded") === null ? false : localStorage.getItem("sidebarFolded") === "true",
logMessages: [],
currentCameraIndex: 0,
currentCameraUniqueName: Object.keys(cameraStore.cameras)[0],
backendResults: {
0: {
@@ -97,11 +99,12 @@ export const useStateStore = defineStore("state", {
},
getters: {
currentPipelineResults(): PipelineResult | undefined {
return this.backendResults[this.currentCameraIndex.toString()];
return this.backendResults[this.currentCameraUniqueName.toString()];
},
currentMultitagBuffer(): MultitagResult[] | undefined {
if (!this.multitagResultBuffer[this.currentCameraIndex]) this.multitagResultBuffer[this.currentCameraIndex] = [];
return this.multitagResultBuffer[this.currentCameraIndex];
if (!this.multitagResultBuffer[this.currentCameraUniqueName])
this.multitagResultBuffer[this.currentCameraUniqueName] = [];
return this.multitagResultBuffer[this.currentCameraUniqueName];
}
},
actions: {

View File

@@ -18,17 +18,18 @@ import axios from "axios";
import { resolutionsAreEqual } from "@/lib/PhotonUtils";
interface CameraSettingsStore {
cameras: UiCameraConfiguration[];
cameras: { [key: string]: UiCameraConfiguration };
}
export const useCameraSettingsStore = defineStore("cameraSettings", {
state: (): CameraSettingsStore => ({
cameras: [PlaceholderCameraSettings]
cameras: { [PlaceholderCameraSettings.uniqueName]: PlaceholderCameraSettings }
}),
getters: {
// TODO update types to update this value being undefined. This would be a decently large change.
currentCameraSettings(): UiCameraConfiguration {
return this.cameras[useStateStore().currentCameraIndex];
const currentCameraUniqueName = useStateStore().currentCameraUniqueName;
return this.cameras[currentCameraUniqueName] || PlaceholderCameraSettings;
},
currentPipelineSettings(): ActivePipelineSettings {
return this.currentCameraSettings.pipelineSettings;
@@ -49,19 +50,19 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
);
},
cameraNames(): string[] {
return this.cameras.map((c) => c.nickname);
return Object.values(this.cameras).map((c) => c.nickname);
},
cameraUniqueNames(): string[] {
return this.cameras.map((c) => c.uniqueName);
return Object.values(this.cameras).map((c) => c.uniqueName);
},
currentCameraName(): string {
return this.cameraNames[useStateStore().currentCameraIndex];
return this.cameras[useStateStore().currentCameraUniqueName].nickname;
},
pipelineNames(): string[] {
return this.currentCameraSettings.pipelineNicknames;
},
currentPipelineName(): string {
return this.pipelineNames[useStateStore().currentCameraIndex];
return this.pipelineNames[useStateStore().currentCameraUniqueName];
},
isDriverMode(): boolean {
return this.currentCameraSettings.currentPipelineIndex === WebsocketPipelineType.DriverMode;
@@ -93,72 +94,79 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
},
actions: {
updateCameraSettingsFromWebsocket(data: WebsocketCameraSettingsUpdate[]) {
const configuredCameras = data.map<UiCameraConfiguration>((d) => ({
cameraPath: d.cameraPath,
const configuredCameras = data.reduce<{ [key: string]: UiCameraConfiguration }>((acc, d) => {
acc[d.uniqueName] = {
cameraPath: d.cameraPath,
nickname: d.nickname,
uniqueName: d.uniqueName,
fov: {
value: d.fov,
managedByVendor: !d.isFovConfigurable
},
stream: {
inputPort: d.inputStreamPort,
outputPort: d.outputStreamPort
},
validVideoFormats: Object.entries(d.videoFormatList)
.sort(([firstKey], [secondKey]) => parseInt(firstKey) - parseInt(secondKey))
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.map<VideoFormat>(([k, v], i) => ({
resolution: {
width: v.width,
height: v.height
},
fps: v.fps,
pixelFormat: v.pixelFormat,
index: v.index || i,
diagonalFOV: v.diagonalFOV,
horizontalFOV: v.horizontalFOV,
verticalFOV: v.verticalFOV,
standardDeviation: v.standardDeviation,
mean: v.mean
})),
completeCalibrations: d.calibrations,
isCSICamera: d.isCSICamera,
minExposureRaw: d.minExposureRaw,
maxExposureRaw: d.maxExposureRaw,
pipelineNicknames: d.pipelineNicknames,
currentPipelineIndex: d.currentPipelineIndex,
pipelineSettings: d.currentPipelineSettings,
cameraQuirks: d.cameraQuirks,
minWhiteBalanceTemp: d.minWhiteBalanceTemp,
maxWhiteBalanceTemp: d.maxWhiteBalanceTemp,
matchedCameraInfo: d.matchedCameraInfo,
isConnected: d.isConnected,
hasConnected: d.hasConnected
};
return acc;
}, {});
this.cameras =
Object.keys(configuredCameras).length > 0
? configuredCameras
: { [PlaceholderCameraSettings.uniqueName]: PlaceholderCameraSettings };
nickname: d.nickname,
uniqueName: d.uniqueName,
fov: {
value: d.fov,
managedByVendor: !d.isFovConfigurable
},
stream: {
inputPort: d.inputStreamPort,
outputPort: d.outputStreamPort
},
validVideoFormats: Object.entries(d.videoFormatList)
.sort(([firstKey], [secondKey]) => parseInt(firstKey) - parseInt(secondKey))
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.map<VideoFormat>(([k, v], i) => ({
resolution: {
width: v.width,
height: v.height
},
fps: v.fps,
pixelFormat: v.pixelFormat,
index: v.index || i,
diagonalFOV: v.diagonalFOV,
horizontalFOV: v.horizontalFOV,
verticalFOV: v.verticalFOV,
standardDeviation: v.standardDeviation,
mean: v.mean
})),
completeCalibrations: d.calibrations,
isCSICamera: d.isCSICamera,
minExposureRaw: d.minExposureRaw,
maxExposureRaw: d.maxExposureRaw,
pipelineNicknames: d.pipelineNicknames,
currentPipelineIndex: d.currentPipelineIndex,
pipelineSettings: d.currentPipelineSettings,
cameraQuirks: d.cameraQuirks,
minWhiteBalanceTemp: d.minWhiteBalanceTemp,
maxWhiteBalanceTemp: d.maxWhiteBalanceTemp,
matchedCameraInfo: d.matchedCameraInfo,
isConnected: d.isConnected,
hasConnected: d.hasConnected
}));
// Clamp index to between 0 and [length - 1]
useStateStore().currentCameraIndex = Math.max(
0,
Math.min(useStateStore().currentCameraIndex, configuredCameras.length - 1)
);
this.cameras = configuredCameras.length > 0 ? configuredCameras : [PlaceholderCameraSettings];
// Ensure currentCameraUniqueName is valid
if (!this.cameras[useStateStore().currentCameraUniqueName]) {
useStateStore().currentCameraUniqueName = Object.keys(this.cameras)[0];
}
},
/**
* Update the configurable camera settings.
*
* @param data camera settings to save.
* @param cameraIndex the index of the camera.
* @param cameraUniqueNamendex the unique name of the camera.
*/
updateCameraSettings(data: CameraSettingsChangeRequest, cameraIndex: number = useStateStore().currentCameraIndex) {
updateCameraSettings(
data: CameraSettingsChangeRequest,
cameraUniqueName: String = useStateStore().currentCameraUniqueName
) {
// The camera settings endpoint doesn't actually require all data, instead, it needs key data such as the FOV
const payload = {
settings: {
...data
},
index: cameraIndex
cameraUniqueName: cameraUniqueName
};
return axios.post("/settings/camera", payload);
},
@@ -167,16 +175,16 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
*
* @param newPipelineName the name of the new pipeline.
* @param pipelineType the type of the new pipeline. Cannot be {@link WebsocketPipelineType.Calib3d} or {@link WebsocketPipelineType.DriverMode}.
* @param cameraIndex the index of the camera
* @param cameraUniqueNamendex the unique name of the camera.
*/
createNewPipeline(
newPipelineName: string,
pipelineType: Exclude<WebsocketPipelineType, WebsocketPipelineType.Calib3d | WebsocketPipelineType.DriverMode>,
cameraIndex: number = useStateStore().currentCameraIndex
cameraUniqueName: String = useStateStore().currentCameraUniqueName
) {
const payload = {
addNewPipeline: [newPipelineName, pipelineType],
cameraIndex: cameraIndex
cameraUniqueName: cameraUniqueName
};
useStateStore().websocket?.send(payload, true);
},
@@ -185,30 +193,30 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
*
* @param settings settings to modify. The type of the settings should match the currently selected pipeline type.
* @param updateStore whether or not to update the store. This is useful if the input field already models the store reference.
* @param cameraIndex the index of the camera
* @param cameraUniqueNamendex the unique name of the camera.
*/
changeCurrentPipelineSetting(
settings: ActiveConfigurablePipelineSettings,
updateStore = true,
cameraIndex: number = useStateStore().currentCameraIndex
cameraUniqueName: string = useStateStore().currentCameraUniqueName
) {
const payload = {
changePipelineSetting: {
...settings,
cameraIndex: cameraIndex
cameraUniqueName: cameraUniqueName
}
};
if (updateStore) {
this.changePipelineSettingsInStore(settings, cameraIndex);
this.changePipelineSettingsInStore(settings, cameraUniqueName);
}
useStateStore().websocket?.send(payload, true);
},
changePipelineSettingsInStore(
settings: Partial<ActivePipelineSettings>,
cameraIndex: number = useStateStore().currentCameraIndex
cameraUniqueName: string = useStateStore().currentCameraUniqueName
) {
Object.entries(settings).forEach(([k, v]) => {
this.cameras[cameraIndex].pipelineSettings[k] = v;
this.cameras[cameraUniqueName].pipelineSettings[k] = v;
});
},
/**
@@ -216,19 +224,19 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
*
* @param newName the new nickname for the camera.
* @param updateStore whether or not to update the store. This is useful if the input field already models the store reference.
* @param cameraIndex the index of the camera
* @param cameraUniqueNamendex the unique name of the camera.
*/
changeCurrentPipelineNickname(
newName: string,
updateStore = true,
cameraIndex: number = useStateStore().currentCameraIndex
cameraUniqueName: string = useStateStore().currentCameraUniqueName
) {
const payload = {
changePipelineName: newName,
cameraIndex: cameraIndex
cameraUniqueName: cameraUniqueName
};
if (updateStore) {
this.cameras[cameraIndex].pipelineSettings.pipelineNickname = newName;
this.cameras[cameraUniqueName].pipelineSettings.pipelineNickname = newName;
}
useStateStore().websocket?.send(payload, true);
},
@@ -236,15 +244,15 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
* Modify the Pipeline type of the currently selected pipeline of the provided camera. This overwrites the current pipeline's settings when the backend resets the current pipeline settings.
*
* @param type the pipeline type to set. Cannot be {@link WebsocketPipelineType.Calib3d} or {@link WebsocketPipelineType.DriverMode}.
* @param cameraIndex the index of the camera.
* @param cameraUniqueNamendex the unique name of the camera.
*/
changeCurrentPipelineType(
type: Exclude<WebsocketPipelineType, WebsocketPipelineType.Calib3d | WebsocketPipelineType.DriverMode>,
cameraIndex: number = useStateStore().currentCameraIndex
cameraUniqueName: string = useStateStore().currentCameraUniqueName
) {
const payload = {
pipelineType: type,
cameraIndex: cameraIndex
cameraUniqueName: cameraUniqueName
};
useStateStore().websocket?.send(payload, true);
},
@@ -253,44 +261,44 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
*
* @param index pipeline index to set.
* @param updateStore whether or not to update the store. This is useful if the input field already models the store reference.
* @param cameraIndex the index of the camera.
* @param cameraUniqueNamendex the unique name of the camera.
*/
changeCurrentPipelineIndex(
index: number,
updateStore = true,
cameraIndex: number = useStateStore().currentCameraIndex
cameraUniqueName: string = useStateStore().currentCameraUniqueName
) {
const payload = {
currentPipeline: index,
cameraIndex: cameraIndex
cameraUniqueName: cameraUniqueName
};
if (updateStore) {
if (
this.cameras[cameraIndex].currentPipelineIndex !== -1 &&
this.cameras[cameraIndex].currentPipelineIndex !== -2
this.cameras[cameraUniqueName].currentPipelineIndex !== -1 &&
this.cameras[cameraUniqueName].currentPipelineIndex !== -2
) {
this.cameras[cameraIndex].lastPipelineIndex = this.cameras[cameraIndex].currentPipelineIndex;
this.cameras[cameraUniqueName].lastPipelineIndex = this.cameras[cameraUniqueName].currentPipelineIndex;
}
this.cameras[cameraIndex].currentPipelineIndex = index;
this.cameras[cameraUniqueName].currentPipelineIndex = index;
}
useStateStore().websocket?.send(payload, true);
},
setDriverMode(isDriverMode: boolean, cameraIndex: number = useStateStore().currentCameraIndex) {
setDriverMode(isDriverMode: boolean, cameraUniqueName: string = useStateStore().currentCameraUniqueName) {
const payload = {
driverMode: isDriverMode,
cameraIndex: cameraIndex
cameraUniqueName: cameraUniqueName
};
useStateStore().websocket?.send(payload, true);
},
/**
* Change the currently selected pipeline of the provided camera.
*
* @param cameraIndex the index of the camera's pipeline to change.
* @param cameraUniqueNamendex the unique name of the camera.
*/
deleteCurrentPipeline(cameraIndex: number = useStateStore().currentCameraIndex) {
deleteCurrentPipeline(cameraUniqueName: string = useStateStore().currentCameraUniqueName) {
const payload = {
deleteCurrentPipeline: {},
cameraIndex: cameraIndex
cameraUniqueName: cameraUniqueName
};
useStateStore().websocket?.send(payload, true);
},
@@ -298,27 +306,27 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
* Duplicate the pipeline at the provided index.
*
* @param pipelineIndex index of the pipeline to duplicate.
* @param cameraIndex the index of the camera.
* @param cameraUniqueNamendex the unique name of the camera.
*/
duplicatePipeline(pipelineIndex: number, cameraIndex: number = useStateStore().currentCameraIndex) {
duplicatePipeline(pipelineIndex: number, cameraUniqueName: string = useStateStore().currentCameraUniqueName) {
const payload = {
duplicatePipeline: pipelineIndex,
cameraIndex: cameraIndex
cameraUniqueName: cameraUniqueName
};
useStateStore().websocket?.send(payload, true);
},
/**
* Change the currently set camera
*
* @param cameraIndex the index of the camera to set as the current camera.
* @param cameraUniqueName the unique name of the camera.
* @param updateStore whether or not to update the store. This is useful if the input field already models the store reference.
*/
setCurrentCameraIndex(cameraIndex: number, updateStore = true) {
setCurrentCameraUniqueName(cameraUniqueName: string, updateStore = true) {
const payload = {
currentCamera: cameraIndex
cameraUniqueName: cameraUniqueName
};
if (updateStore) {
useStateStore().currentCameraIndex = cameraIndex;
useStateStore().currentCameraUniqueName = cameraUniqueName;
}
useStateStore().websocket?.send(payload, true);
},
@@ -327,17 +335,17 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
*
* @param newName the new nickname of the camera.
* @param updateStore whether or not to update the store. This is useful if the input field already models the store reference.
* @param cameraIndex the index of the camera.
* @param cameraUniqueNamendex the unique name of the camera.
* @return HTTP request promise to the backend
*/
changeCameraNickname(
newName: string,
updateStore = true,
cameraIndex: number = useStateStore().currentCameraIndex
cameraUniqueName: string = useStateStore().currentCameraUniqueName
) {
const payload = {
name: newName,
cameraIndex: cameraIndex
cameraUniqueName: cameraUniqueName
};
if (updateStore) {
this.currentCameraSettings.nickname = newName;
@@ -348,7 +356,7 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
* Start the 3D calibration process for the provided camera.
*
* @param calibrationInitData initialization calibration data.
* @param cameraIndex the index of the camera.
* @param cameraUniqueNamendex the unique name of the camera.
*/
startPnPCalibration(
calibrationInitData: {
@@ -360,7 +368,7 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
useOldPattern: boolean;
tagFamily: CalibrationTagFamilies;
},
cameraIndex: number = useStateStore().currentCameraIndex
cameraUniqueName: string = useStateStore().currentCameraUniqueName
) {
const stateCalibData = useStateStore().calibrationData;
const payload = {
@@ -371,63 +379,63 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
videoModeIndex: stateCalibData.videoFormatIndex,
...calibrationInitData
},
cameraIndex: cameraIndex
cameraUniqueName: cameraUniqueName
};
useStateStore().websocket?.send(payload, true);
},
/**
* End the 3D calibration process for the provided camera.
*
* @param cameraIndex the index of the camera
* @param cameraUniqueName the unique name of the camera.
* @return HTTP request promise to the backend
*/
endPnPCalibration(cameraIndex: number = useStateStore().currentCameraIndex) {
return axios.post("/calibration/end", { index: cameraIndex });
endPnPCalibration(cameraUniqueName: string = useStateStore().currentCameraUniqueName) {
return axios.post("/calibration/end", { cameraUniqueName: cameraUniqueName });
},
importCalibrationFromData(
data: { calibration: CameraCalibrationResult },
cameraIndex: number = useStateStore().currentCameraIndex
cameraUniqueName: string = useStateStore().currentCameraUniqueName
) {
const payload = {
...data,
cameraIndex: cameraIndex
cameraUniqueName: cameraUniqueName
};
return axios.post("/calibration/importFromData", payload);
},
/**
* Take a snapshot for the calibration processes
*
* @param cameraIndex the index of the camera that is currently in the calibration process
* @param cameraUniqueName the unique name of the camera that is currently in the calibration process
*/
takeCalibrationSnapshot(cameraIndex: number = useStateStore().currentCameraIndex) {
takeCalibrationSnapshot(cameraUniqueName: string = useStateStore().currentCameraUniqueName) {
const payload = {
takeCalibrationSnapshot: true,
cameraIndex: cameraIndex
cameraUniqueName: cameraUniqueName
};
useStateStore().websocket?.send(payload, true);
},
/**
* Save a snapshot of the input frame of the camera.
*
* @param cameraIndex the index of the camera
* @param cameraUniqueName the unique name of the camera
*/
saveInputSnapshot(cameraIndex: number = useStateStore().currentCameraIndex) {
saveInputSnapshot(cameraUniqueName: string = useStateStore().currentCameraUniqueName) {
const payload = {
saveInputSnapshot: true,
cameraIndex: cameraIndex
cameraUniqueName: cameraUniqueName
};
useStateStore().websocket?.send(payload, true);
},
/**
* Save a snapshot of the output frame of the camera.
*
* @param cameraIndex the index of the camera
* @param cameraUniqueName the unique name of the camera
*/
saveOutputSnapshot(cameraIndex: number = useStateStore().currentCameraIndex) {
saveOutputSnapshot(cameraUniqueName: string = useStateStore().currentCameraUniqueName) {
const payload = {
saveOutputSnapshot: true,
cameraIndex: cameraIndex
cameraUniqueName: cameraUniqueName
};
useStateStore().websocket?.send(payload, true);
},
@@ -435,35 +443,42 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
* Set the robot offset mode type.
*
* @param type Offset type to take.
* @param cameraIndex the index of the camera.
* @param cameraUniqueName the unique name of the camera
*/
takeRobotOffsetPoint(type: RobotOffsetType, cameraIndex: number = useStateStore().currentCameraIndex) {
takeRobotOffsetPoint(type: RobotOffsetType, cameraUniqueName: string = useStateStore().currentCameraUniqueName) {
const payload = {
robotOffsetPoint: type,
cameraIndex: cameraIndex
cameraUniqueName: cameraUniqueName
};
useStateStore().websocket?.send(payload, true);
},
getCalibrationCoeffs(
resolution: Resolution,
cameraIndex: number = useStateStore().currentCameraIndex
cameraUniqueName: string = useStateStore().currentCameraUniqueName
): CameraCalibrationResult | undefined {
return this.cameras[cameraIndex].completeCalibrations.find((v) => resolutionsAreEqual(v.resolution, resolution));
return this.cameras[cameraUniqueName].completeCalibrations.find((v) =>
resolutionsAreEqual(v.resolution, resolution)
);
},
getCalImageUrl(host: string, resolution: Resolution, idx: number, cameraIdx = useStateStore().currentCameraIndex) {
getCalImageUrl(
host: string,
resolution: Resolution,
idx: number,
cameraUniqueName = useStateStore().currentCameraUniqueName
) {
const url = new URL(`http://${host}/api/utils/getCalSnapshot`);
url.searchParams.set("width", Math.round(resolution.width).toFixed(0));
url.searchParams.set("height", Math.round(resolution.height).toFixed(0));
url.searchParams.set("snapshotIdx", Math.round(idx).toFixed(0));
url.searchParams.set("cameraIdx", Math.round(cameraIdx).toFixed(0));
url.searchParams.set("cameraUniqueName", cameraUniqueName.replace(" ", "").trim().toLowerCase());
return url.href;
},
getCalJSONUrl(host: string, resolution: Resolution, cameraIdx = useStateStore().currentCameraIndex) {
getCalJSONUrl(host: string, resolution: Resolution, cameraUniqueName = useStateStore().currentCameraUniqueName) {
const url = new URL(`http://${host}/api/utils/getCalibrationJSON`);
url.searchParams.set("width", Math.round(resolution.width).toFixed(0));
url.searchParams.set("height", Math.round(resolution.height).toFixed(0));
url.searchParams.set("cameraIdx", Math.round(cameraIdx).toFixed(0));
url.searchParams.set("cameraUniqueName", cameraUniqueName.replace(" ", "").trim().toLowerCase());
return url.href;
}

View File

@@ -103,7 +103,7 @@ export interface IncomingWebsocketData {
deviceIps: string[];
};
mutatePipelineSettings?: Partial<ActivePipelineSettings>;
cameraIndex?: number; // Sent when mutating pipeline settings to check against currently active
cameraUniqueName?: string; // Sent when mutating pipeline settings to check against currently active
calibrationData?: WebsocketCalibrationData;
visionSourceManager?: VsmState;
}

View File

@@ -19,7 +19,7 @@ const host = inject<string>("backendHost");
const activateModule = (moduleUniqueName: string) => {
const url = new URL(`http://${host}/api/utils/activateMatchedCamera`);
url.searchParams.set("uniqueName", moduleUniqueName);
url.searchParams.set("cameraUniqueName", moduleUniqueName);
fetch(url.toString(), {
method: "POST"
@@ -34,8 +34,9 @@ const activateCamera = (cameraInfo: PVCameraInfo) => {
});
};
const deactivateCamera = (cameraUniqueName: string) => {
console.log("Deactivating " + cameraUniqueName);
const url = new URL(`http://${host}/api/utils/unassignCamera`);
url.searchParams.set("uniqueName", cameraUniqueName);
url.searchParams.set("cameraUniqueName", cameraUniqueName);
fetch(url.toString(), {
method: "POST"
@@ -134,7 +135,9 @@ const getMatchedDevice = (info: PVCameraInfo | undefined): PVCameraInfo => {
};
const unmatchedCameras = computed(() => {
const activeVmPaths = useCameraSettingsStore().cameras.map((it) => uniquePathForCamera(it.matchedCameraInfo));
const activeVmPaths = Object.values(useCameraSettingsStore().cameras).map((it) =>
uniquePathForCamera(it.matchedCameraInfo)
);
const disabledVmPaths = useStateStore().vsmState.disabledConfigs.map((it) =>
uniquePathForCamera(it.matchedCameraInfo)
);
@@ -145,7 +148,7 @@ const unmatchedCameras = computed(() => {
});
const activeVisionModules = computed(() =>
useCameraSettingsStore().cameras.filter(
Object.values(useCameraSettingsStore().cameras).filter(
(camera) => JSON.stringify(camera) !== JSON.stringify(PlaceholderCameraSettings)
)
);
@@ -166,13 +169,7 @@ const setCameraView = (camera: PVCameraInfo | null, showCurrent: boolean = false
<div class="pa-5">
<v-row>
<!-- Active modules -->
<v-col
v-for="(module, index) in activeVisionModules"
:key="`enabled-${module.uniqueName}`"
cols="12"
sm="6"
lg="4"
>
<v-col v-for="module in activeVisionModules" :key="`enabled-${module.uniqueName}`" cols="12" sm="6" lg="4">
<v-card dark color="primary">
<v-card-title>{{ module.nickname }}</v-card-title>
<v-card-subtitle v-if="_.isEqual(getMatchedDevice(module.matchedCameraInfo), module.matchedCameraInfo)"
@@ -211,11 +208,11 @@ const setCameraView = (camera: PVCameraInfo | null, showCurrent: boolean = false
}}
</td>
</tr>
<tr v-if="module.isConnected && useStateStore().backendResults[index]">
<tr v-if="module.isConnected && useStateStore().backendResults[module.uniqueName]">
<td>Frames Processed</td>
<td>
{{ useStateStore().backendResults[index].sequenceID }} ({{
useStateStore().backendResults[index].fps
{{ useStateStore().backendResults[module.uniqueName].sequenceID }} ({{
useStateStore().backendResults[module.uniqueName].fps
}}
FPS)
</td>

View File

@@ -42,12 +42,13 @@ const cameraViewType = computed<number[]>({
// TODO - deduplicate with needsCamerasConfigured
const warningShown = computed<boolean>(() => {
return (
useCameraSettingsStore().cameras.length === 0 || useCameraSettingsStore().cameras[0] === PlaceholderCameraSettings
Object.values(useCameraSettingsStore().cameras).length === 0 ||
useCameraSettingsStore().cameras["Placeholder Name"] === PlaceholderCameraSettings
);
});
const arducamWarningShown = computed<boolean>(() => {
return useCameraSettingsStore().cameras.some(
return Object.values(useCameraSettingsStore().cameras).some(
(c) =>
c.cameraQuirks?.quirks?.ArduCamCamera === true &&
!(