mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-23 01:21:40 +00:00
Camera disconnected + stream normalization improvements (#1701)
This commit is contained in:
57
photon-client/src/assets/images/loading-transparent.svg
Normal file
57
photon-client/src/assets/images/loading-transparent.svg
Normal file
@@ -0,0 +1,57 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" width="200" height="200" style="shape-rendering: auto; display: block; background: rgb(0, 100, 146);" xmlns:xlink="http://www.w3.org/1999/xlink"><g><g transform="translate(80,50)">
|
||||
<g transform="rotate(0)">
|
||||
<circle fill-opacity="1" fill="#ffd943" r="6" cy="0" cx="0">
|
||||
<animateTransform repeatCount="indefinite" dur="0.9345794392523364s" keyTimes="0;1" values="1.5 1.5;1 1" begin="-0.8177570093457943s" type="scale" attributeName="transform"></animateTransform>
|
||||
<animate begin="-0.8177570093457943s" values="1;0" repeatCount="indefinite" dur="0.9345794392523364s" keyTimes="0;1" attributeName="fill-opacity"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(71.21320343559643,71.21320343559643)">
|
||||
<g transform="rotate(45)">
|
||||
<circle fill-opacity="0.875" fill="#ffd943" r="6" cy="0" cx="0">
|
||||
<animateTransform repeatCount="indefinite" dur="0.9345794392523364s" keyTimes="0;1" values="1.5 1.5;1 1" begin="-0.7009345794392523s" type="scale" attributeName="transform"></animateTransform>
|
||||
<animate begin="-0.7009345794392523s" values="1;0" repeatCount="indefinite" dur="0.9345794392523364s" keyTimes="0;1" attributeName="fill-opacity"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(50,80)">
|
||||
<g transform="rotate(90)">
|
||||
<circle fill-opacity="0.75" fill="#ffd943" r="6" cy="0" cx="0">
|
||||
<animateTransform repeatCount="indefinite" dur="0.9345794392523364s" keyTimes="0;1" values="1.5 1.5;1 1" begin="-0.5841121495327103s" type="scale" attributeName="transform"></animateTransform>
|
||||
<animate begin="-0.5841121495327103s" values="1;0" repeatCount="indefinite" dur="0.9345794392523364s" keyTimes="0;1" attributeName="fill-opacity"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(28.786796564403577,71.21320343559643)">
|
||||
<g transform="rotate(135)">
|
||||
<circle fill-opacity="0.625" fill="#ffd943" r="6" cy="0" cx="0">
|
||||
<animateTransform repeatCount="indefinite" dur="0.9345794392523364s" keyTimes="0;1" values="1.5 1.5;1 1" begin="-0.4672897196261682s" type="scale" attributeName="transform"></animateTransform>
|
||||
<animate begin="-0.4672897196261682s" values="1;0" repeatCount="indefinite" dur="0.9345794392523364s" keyTimes="0;1" attributeName="fill-opacity"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(20,50.00000000000001)">
|
||||
<g transform="rotate(180)">
|
||||
<circle fill-opacity="0.5" fill="#ffd943" r="6" cy="0" cx="0">
|
||||
<animateTransform repeatCount="indefinite" dur="0.9345794392523364s" keyTimes="0;1" values="1.5 1.5;1 1" begin="-0.35046728971962615s" type="scale" attributeName="transform"></animateTransform>
|
||||
<animate begin="-0.35046728971962615s" values="1;0" repeatCount="indefinite" dur="0.9345794392523364s" keyTimes="0;1" attributeName="fill-opacity"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(28.78679656440357,28.786796564403577)">
|
||||
<g transform="rotate(225)">
|
||||
<circle fill-opacity="0.375" fill="#ffd943" r="6" cy="0" cx="0">
|
||||
<animateTransform repeatCount="indefinite" dur="0.9345794392523364s" keyTimes="0;1" values="1.5 1.5;1 1" begin="-0.2336448598130841s" type="scale" attributeName="transform"></animateTransform>
|
||||
<animate begin="-0.2336448598130841s" values="1;0" repeatCount="indefinite" dur="0.9345794392523364s" keyTimes="0;1" attributeName="fill-opacity"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(49.99999999999999,20)">
|
||||
<g transform="rotate(270)">
|
||||
<circle fill-opacity="0.25" fill="#ffd943" r="6" cy="0" cx="0">
|
||||
<animateTransform repeatCount="indefinite" dur="0.9345794392523364s" keyTimes="0;1" values="1.5 1.5;1 1" begin="-0.11682242990654206s" type="scale" attributeName="transform"></animateTransform>
|
||||
<animate begin="-0.11682242990654206s" values="1;0" repeatCount="indefinite" dur="0.9345794392523364s" keyTimes="0;1" attributeName="fill-opacity"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(71.21320343559643,28.78679656440357)">
|
||||
<g transform="rotate(315)">
|
||||
<circle fill-opacity="0.125" fill="#ffd943" r="6" cy="0" cx="0">
|
||||
<animateTransform repeatCount="indefinite" dur="0.9345794392523364s" keyTimes="0;1" values="1.5 1.5;1 1" begin="0s" type="scale" attributeName="transform"></animateTransform>
|
||||
<animate begin="0s" values="1;0" repeatCount="indefinite" dur="0.9345794392523364s" keyTimes="0;1" attributeName="fill-opacity"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g></g></g><!-- [ldio] generated by https://loading.io --></svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, inject, ref, onBeforeUnmount } from "vue";
|
||||
import { useStateStore } from "@/stores/StateStore";
|
||||
import loadingImage from "@/assets/images/loading.svg";
|
||||
import loadingImage from "@/assets/images/loading-transparent.svg";
|
||||
import type { StyleValue } from "vue/types/jsx";
|
||||
import PvIcon from "@/components/common/pv-icon.vue";
|
||||
import type { UiCameraConfiguration } from "@/types/SettingTypes";
|
||||
@@ -9,7 +9,6 @@ import type { UiCameraConfiguration } from "@/types/SettingTypes";
|
||||
const props = defineProps<{
|
||||
streamType: "Raw" | "Processed";
|
||||
id: string;
|
||||
outerId?: string;
|
||||
cameraSettings: UiCameraConfiguration;
|
||||
}>();
|
||||
|
||||
@@ -91,7 +90,7 @@ onBeforeUnmount(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :id="outerId" class="stream-container" :style="containerStyle">
|
||||
<div class="stream-container" :style="containerStyle">
|
||||
<img :src="loadingImage" class="stream-loading" />
|
||||
<img
|
||||
:id="id"
|
||||
@@ -131,18 +130,25 @@ onBeforeUnmount(() => {
|
||||
display: flex;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stream-loading {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 25%;
|
||||
height: 25%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.stream-video {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.stream-overlay {
|
||||
|
||||
@@ -57,14 +57,14 @@ const cameraInfoFor: any = (camera: PVCameraInfo) => {
|
||||
<td>Path:</td>
|
||||
<td style="word-break: break-all">{{ cameraInfoFor(camera).path }}</td>
|
||||
</tr>
|
||||
<tr v-if="cameraInfoFor(camera).otherPaths !== undefined && cameraInfoFor(camera).otherPaths !== null">
|
||||
<td>Other Paths:</td>
|
||||
<td>{{ cameraInfoFor(camera).otherPaths }}</td>
|
||||
</tr>
|
||||
<tr v-if="cameraInfoFor(camera).uniquePath !== undefined && cameraInfoFor(camera).uniquePath !== null">
|
||||
<td>Unique Path:</td>
|
||||
<td style="word-break: break-all">{{ cameraInfoFor(camera).uniquePath }}</td>
|
||||
</tr>
|
||||
<tr v-if="cameraInfoFor(camera).otherPaths !== undefined && cameraInfoFor(camera).otherPaths !== null">
|
||||
<td>Other Paths:</td>
|
||||
<td>{{ cameraInfoFor(camera).otherPaths }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-simple-table>
|
||||
</div>
|
||||
|
||||
@@ -95,14 +95,6 @@ const cameraInfoFor = (camera: PVCameraInfo): any => {
|
||||
<td style="word-break: break-all">{{ cameraInfoFor(saved).path }}</td>
|
||||
<td style="word-break: break-all">{{ cameraInfoFor(current).path }}</td>
|
||||
</tr>
|
||||
<tr
|
||||
v-if="cameraInfoFor(saved).otherPaths !== undefined && cameraInfoFor(saved).otherPaths !== null"
|
||||
:class="!_.isEqual(cameraInfoFor(saved).otherPaths, cameraInfoFor(current).otherPaths) ? 'mismatch' : ''"
|
||||
>
|
||||
<td>Other Paths:</td>
|
||||
<td>{{ cameraInfoFor(saved).otherPaths }}</td>
|
||||
<td>{{ cameraInfoFor(current).otherPaths }}</td>
|
||||
</tr>
|
||||
<tr
|
||||
v-if="cameraInfoFor(saved).uniquePath !== undefined && cameraInfoFor(saved).uniquePath !== null"
|
||||
:class="cameraInfoFor(saved).uniquePath !== cameraInfoFor(current).uniquePath ? 'mismatch' : ''"
|
||||
@@ -111,6 +103,14 @@ const cameraInfoFor = (camera: PVCameraInfo): any => {
|
||||
<td style="word-break: break-all">{{ cameraInfoFor(saved).uniquePath }}</td>
|
||||
<td style="word-break: break-all">{{ cameraInfoFor(current).uniquePath }}</td>
|
||||
</tr>
|
||||
<tr
|
||||
v-if="cameraInfoFor(saved).otherPaths !== undefined && cameraInfoFor(saved).otherPaths !== null"
|
||||
:class="!_.isEqual(cameraInfoFor(saved).otherPaths, cameraInfoFor(current).otherPaths) ? 'mismatch' : ''"
|
||||
>
|
||||
<td>Other Paths:</td>
|
||||
<td>{{ cameraInfoFor(saved).otherPaths }}</td>
|
||||
<td>{{ cameraInfoFor(current).otherPaths }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-simple-table>
|
||||
</div>
|
||||
|
||||
@@ -64,6 +64,7 @@ const handleKeydown = ({ key }) => {
|
||||
:error-messages="errorMessage"
|
||||
:rules="rules"
|
||||
hide-details="auto"
|
||||
class="light-error"
|
||||
@keydown="handleKeydown"
|
||||
/>
|
||||
</v-col>
|
||||
@@ -74,3 +75,9 @@ const handleKeydown = ({ key }) => {
|
||||
margin-top: 0px;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.light-error .error--text {
|
||||
color: red !important;
|
||||
caret-color: red !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -70,7 +70,7 @@ const supportedModels = computed(() => {
|
||||
<div class="pa-6 pt-0">
|
||||
<v-row>
|
||||
<v-col cols="12 ">
|
||||
<v-btn color="secondary" @click="() => (showImportDialog = true)" class="justify-center">
|
||||
<v-btn color="secondary" class="justify-center" @click="() => (showImportDialog = true)">
|
||||
<v-icon left class="open-icon"> mdi-import </v-icon>
|
||||
<span class="open-label">Import New Model</span>
|
||||
</v-btn>
|
||||
@@ -94,10 +94,10 @@ const supportedModels = computed(() => {
|
||||
named <code>note-640-640-yolov5s-labels.txt</code>. Note that ONLY 640x640 YOLOv5 & YOLOv8 models
|
||||
trained and converted to `.rknn` format for RK3588 CPUs are currently supported!
|
||||
<v-row class="mt-6 ml-4 mr-8">
|
||||
<v-file-input label="RKNN File" v-model="importRKNNFile" accept=".rknn" />
|
||||
<v-file-input v-model="importRKNNFile" label="RKNN File" accept=".rknn" />
|
||||
</v-row>
|
||||
<v-row class="mt-6 ml-4 mr-8">
|
||||
<v-file-input label="Labels File" v-model="importLabelsFile" accept=".txt" />
|
||||
<v-file-input v-model="importLabelsFile" label="Labels File" accept=".txt" />
|
||||
</v-row>
|
||||
<v-row
|
||||
class="mt-12 ml-8 mr-8 mb-1"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
|
||||
import { computed, inject, onMounted, ref } from "vue";
|
||||
import { computed, inject, ref } from "vue";
|
||||
import { useStateStore } from "@/stores/StateStore";
|
||||
import {
|
||||
PlaceholderCameraSettings,
|
||||
@@ -28,10 +28,7 @@ const activateModule = (moduleUniqueName: string) => {
|
||||
|
||||
fetch(url.toString(), {
|
||||
method: "POST"
|
||||
}).finally(() => {
|
||||
activatingModule.value = false;
|
||||
setTimeout(() => enforceStreamHeight(), 1000);
|
||||
});
|
||||
}).finally(() => (activatingModule.value = false));
|
||||
};
|
||||
|
||||
const assigningCamera = ref(false);
|
||||
@@ -43,10 +40,7 @@ const assignCamera = (cameraInfo: PVCameraInfo) => {
|
||||
|
||||
fetch(url.toString(), {
|
||||
method: "POST"
|
||||
}).finally(() => {
|
||||
assigningCamera.value = false;
|
||||
setTimeout(() => enforceStreamHeight(), 1000);
|
||||
});
|
||||
}).finally(() => (assigningCamera.value = false));
|
||||
};
|
||||
|
||||
const deactivatingModule = ref(false);
|
||||
@@ -122,7 +116,8 @@ const camerasMatch = (camera1: PVCameraInfo, camera2: PVCameraInfo) => {
|
||||
else return false;
|
||||
};
|
||||
|
||||
const cameraInfoFor = (camera: PVCameraInfo): PVUsbCameraInfo | PVCSICameraInfo | PVFileCameraInfo | any => {
|
||||
const cameraInfoFor = (camera: PVCameraInfo | null): PVUsbCameraInfo | PVCSICameraInfo | PVFileCameraInfo | any => {
|
||||
if (!camera) return null;
|
||||
if (camera.PVUsbCameraInfo) {
|
||||
return camera.PVUsbCameraInfo;
|
||||
}
|
||||
@@ -135,76 +130,67 @@ const cameraInfoFor = (camera: PVCameraInfo): PVUsbCameraInfo | PVCSICameraInfo
|
||||
return {};
|
||||
};
|
||||
|
||||
const uniquePathForCamera = (info: PVCameraInfo) => {
|
||||
if (info.PVUsbCameraInfo) {
|
||||
return info.PVUsbCameraInfo.uniquePath;
|
||||
}
|
||||
if (info.PVCSICameraInfo) {
|
||||
return info.PVCSICameraInfo.uniquePath;
|
||||
}
|
||||
if (info.PVFileCameraInfo) {
|
||||
return info.PVFileCameraInfo.uniquePath;
|
||||
}
|
||||
|
||||
// TODO - wut
|
||||
return "";
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the PVCameraInfo currently occupying the same uniquepath as the the given module
|
||||
*/
|
||||
const getMatchedDevice = (info: PVCameraInfo | undefined): PVCameraInfo => {
|
||||
if (!info) {
|
||||
return {
|
||||
PVFileCameraInfo: {
|
||||
name: "!",
|
||||
path: "!",
|
||||
uniquePath: "!"
|
||||
},
|
||||
PVFileCameraInfo: undefined,
|
||||
PVCSICameraInfo: undefined,
|
||||
PVUsbCameraInfo: undefined
|
||||
};
|
||||
}
|
||||
return (
|
||||
useStateStore().vsmState.allConnectedCameras.find(
|
||||
(it) => uniquePathForCamera(it) === uniquePathForCamera(info)
|
||||
(it) => cameraInfoFor(it).uniquePath === cameraInfoFor(info).uniquePath
|
||||
) || {
|
||||
PVFileCameraInfo: {
|
||||
name: "!",
|
||||
path: "!",
|
||||
uniquePath: "!"
|
||||
},
|
||||
PVFileCameraInfo: undefined,
|
||||
PVCSICameraInfo: undefined,
|
||||
PVUsbCameraInfo: undefined
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const unmatchedCameras = computed(() => {
|
||||
const activeVmPaths = Object.values(useCameraSettingsStore().cameras).map((it) =>
|
||||
uniquePathForCamera(it.matchedCameraInfo)
|
||||
const cameraCononected = (uniquePath: string): boolean => {
|
||||
return (
|
||||
useStateStore().vsmState.allConnectedCameras.find((it) => cameraInfoFor(it).uniquePath === uniquePath) !== undefined
|
||||
);
|
||||
const disabledVmPaths = useStateStore().vsmState.disabledConfigs.map((it) =>
|
||||
uniquePathForCamera(it.matchedCameraInfo)
|
||||
};
|
||||
|
||||
const unmatchedCameras = computed(() => {
|
||||
const activeVmPaths = Object.values(useCameraSettingsStore().cameras).map(
|
||||
(it) => cameraInfoFor(it.matchedCameraInfo).uniquePath
|
||||
);
|
||||
const disabledVmPaths = useStateStore().vsmState.disabledConfigs.map(
|
||||
(it) => cameraInfoFor(it.matchedCameraInfo).uniquePath
|
||||
);
|
||||
|
||||
return useStateStore().vsmState.allConnectedCameras.filter(
|
||||
(it) => !activeVmPaths.includes(uniquePathForCamera(it)) && !disabledVmPaths.includes(uniquePathForCamera(it))
|
||||
(it) =>
|
||||
!activeVmPaths.includes(cameraInfoFor(it).uniquePath) && !disabledVmPaths.includes(cameraInfoFor(it).uniquePath)
|
||||
);
|
||||
});
|
||||
|
||||
const activeVisionModules = computed(() =>
|
||||
Object.values(useCameraSettingsStore().cameras).filter(
|
||||
(camera) => JSON.stringify(camera) !== JSON.stringify(PlaceholderCameraSettings)
|
||||
)
|
||||
Object.values(useCameraSettingsStore().cameras)
|
||||
// Ignore placeholder camera
|
||||
.filter((camera) => JSON.stringify(camera) !== JSON.stringify(PlaceholderCameraSettings))
|
||||
// Display connected cameras first
|
||||
.sort(
|
||||
(first, second) =>
|
||||
(cameraCononected(cameraInfoFor(second.matchedCameraInfo).uniquePath) ? 1 : 0) -
|
||||
(cameraCononected(cameraInfoFor(first.matchedCameraInfo).uniquePath) ? 1 : 0)
|
||||
)
|
||||
);
|
||||
|
||||
const disabledVisionModules = computed(() => useStateStore().vsmState.disabledConfigs);
|
||||
|
||||
const viewingDetails = ref(false);
|
||||
const viewingCamera = ref<PVCameraInfo | null>(null);
|
||||
const setCameraView = (camera: PVCameraInfo | null) => {
|
||||
viewingDetails.value = camera !== null;
|
||||
viewingCamera.value = camera;
|
||||
const viewingCamera = ref<[PVCameraInfo | null, boolean | null]>([null, null]);
|
||||
const setCameraView = (camera: PVCameraInfo | null, isConnected: boolean | null) => {
|
||||
viewingDetails.value = camera !== null && isConnected !== null;
|
||||
viewingCamera.value = [camera, isConnected];
|
||||
};
|
||||
|
||||
const viewingDeleteCamera = ref(false);
|
||||
@@ -219,30 +205,6 @@ const exportSettings = ref();
|
||||
const openExportSettingsPrompt = () => {
|
||||
exportSettings.value.click();
|
||||
};
|
||||
|
||||
const enforceStreamHeight = () => {
|
||||
const streamWidth = document.getElementById("stream-container-0")?.offsetWidth ?? 0;
|
||||
if (streamWidth === 0) return;
|
||||
|
||||
Object.values(useCameraSettingsStore().cameras)
|
||||
.filter((camera) => JSON.stringify(camera) !== JSON.stringify(PlaceholderCameraSettings))
|
||||
.forEach((element, index) => {
|
||||
let stream = document.getElementById(`outer-output-camera-stream-${index}`);
|
||||
if (!stream) return;
|
||||
|
||||
stream?.classList.remove("tall-stream", "wide-stream", "d-none");
|
||||
let streamRes = element.validVideoFormats[0].resolution.width / element.validVideoFormats[0].resolution.height;
|
||||
let containerRes = streamWidth / 250.0;
|
||||
if (element.pipelineSettings.inputImageRotationMode % 2 == 1) streamRes = 1 / streamRes;
|
||||
if (streamRes > containerRes) stream?.classList.add("wide-stream");
|
||||
else stream?.classList.add("tall-stream");
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => enforceStreamHeight(), 1000);
|
||||
window.addEventListener("resize", enforceStreamHeight);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -258,31 +220,33 @@ onMounted(() => {
|
||||
>
|
||||
<v-card dark color="primary">
|
||||
<v-card-title>{{ cameraInfoFor(module.matchedCameraInfo).name }}</v-card-title>
|
||||
<v-card-subtitle v-if="camerasMatch(getMatchedDevice(module.matchedCameraInfo), module.matchedCameraInfo)"
|
||||
<v-card-subtitle v-if="!cameraCononected(cameraInfoFor(module.matchedCameraInfo).uniquePath)" class="pb-2"
|
||||
>Status: <span class="inactive-status">Disconnected</span></v-card-subtitle
|
||||
>
|
||||
<v-card-subtitle
|
||||
v-else-if="
|
||||
cameraCononected(cameraInfoFor(module.matchedCameraInfo).uniquePath) &&
|
||||
camerasMatch(getMatchedDevice(module.matchedCameraInfo), module.matchedCameraInfo)
|
||||
"
|
||||
class="pb-2"
|
||||
>Status: <span class="active-status">Active</span></v-card-subtitle
|
||||
>
|
||||
<v-card-subtitle v-else>Status: <span class="mismatch-status">Mismatch</span></v-card-subtitle>
|
||||
<v-card-subtitle v-else class="pb-2">Status: <span class="mismatch-status">Mismatch</span></v-card-subtitle>
|
||||
<v-card-text>
|
||||
<v-simple-table dark dense class="mb-3">
|
||||
<v-simple-table dark dense>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Streams:</td>
|
||||
<td>
|
||||
<a :href="formatUrl(module.stream.inputPort)" target="_blank" class="stream-link"> Input Stream </a>
|
||||
<a :href="formatUrl(module.stream.inputPort)" target="_blank" class="stream-link"> Input </a>
|
||||
/
|
||||
<a :href="formatUrl(module.stream.outputPort)" target="_blank" class="stream-link">
|
||||
Output Stream
|
||||
</a>
|
||||
<a :href="formatUrl(module.stream.outputPort)" target="_blank" class="stream-link"> Output </a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Pipelines</td>
|
||||
<td>{{ module.pipelineNicknames.join(", ") }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Connected</td>
|
||||
<td>{{ module.isConnected }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Calibrations</td>
|
||||
<td>
|
||||
@@ -292,8 +256,13 @@ onMounted(() => {
|
||||
}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="module.isConnected && useStateStore().backendResults[module.uniqueName]">
|
||||
<td>Frames Processed</td>
|
||||
<tr
|
||||
v-if="
|
||||
cameraCononected(cameraInfoFor(module.matchedCameraInfo).uniquePath) &&
|
||||
useStateStore().backendResults[module.uniqueName]
|
||||
"
|
||||
>
|
||||
<td style="width: 50%">Frames Processed</td>
|
||||
<td>
|
||||
{{ useStateStore().backendResults[module.uniqueName].sequenceID }} ({{
|
||||
useStateStore().backendResults[module.uniqueName].fps
|
||||
@@ -304,23 +273,31 @@ onMounted(() => {
|
||||
</tbody>
|
||||
</v-simple-table>
|
||||
<div
|
||||
v-if="cameraCononected(cameraInfoFor(module.matchedCameraInfo).uniquePath)"
|
||||
:id="`stream-container-${index}`"
|
||||
class="d-flex flex-column justify-center align-center"
|
||||
class="d-flex flex-column justify-center align-center mt-3"
|
||||
style="height: 250px"
|
||||
>
|
||||
<photon-camera-stream
|
||||
:id="`output-camera-stream-${index}`"
|
||||
:camera-settings="module"
|
||||
stream-type="Processed"
|
||||
:outer-id="`outer-output-camera-stream-${index}`"
|
||||
class="d-none"
|
||||
/>
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-card-text class="pt-0">
|
||||
<v-row>
|
||||
<v-col cols="12" md="4" class="pr-md-0 pb-0 pb-md-3">
|
||||
<v-btn color="secondary" style="width: 100%" @click="setCameraView(module.matchedCameraInfo)">
|
||||
<v-btn
|
||||
color="secondary"
|
||||
style="width: 100%"
|
||||
@click="
|
||||
setCameraView(
|
||||
module.matchedCameraInfo,
|
||||
cameraCononected(cameraInfoFor(module.matchedCameraInfo).uniquePath)
|
||||
)
|
||||
"
|
||||
>
|
||||
<span>Details</span>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
@@ -349,7 +326,7 @@ onMounted(() => {
|
||||
<v-col v-for="module in disabledVisionModules" :key="`disabled-${module.uniqueName}`" cols="12" sm="6" lg="4">
|
||||
<v-card dark color="primary">
|
||||
<v-card-title>{{ module.nickname }}</v-card-title>
|
||||
<v-card-subtitle>Status: <span class="inactive-status">Deactivated</span></v-card-subtitle>
|
||||
<v-card-subtitle class="pb-2">Status: <span class="inactive-status">Deactivated</span></v-card-subtitle>
|
||||
<v-card-text>
|
||||
<v-simple-table dense>
|
||||
<tbody>
|
||||
@@ -365,7 +342,7 @@ onMounted(() => {
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Connected</td>
|
||||
<td>{{ module.isConnected }}</td>
|
||||
<td>{{ cameraCononected(cameraInfoFor(module.matchedCameraInfo).uniquePath) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Calibrations</td>
|
||||
@@ -382,7 +359,16 @@ onMounted(() => {
|
||||
<v-card-text class="pt-0">
|
||||
<v-row>
|
||||
<v-col cols="12" md="4" class="pr-md-0 pb-0 pb-md-3">
|
||||
<v-btn color="secondary" style="width: 100%" @click="setCameraView(module.matchedCameraInfo)">
|
||||
<v-btn
|
||||
color="secondary"
|
||||
style="width: 100%"
|
||||
@click="
|
||||
setCameraView(
|
||||
module.matchedCameraInfo,
|
||||
cameraCononected(cameraInfoFor(module.matchedCameraInfo).uniquePath)
|
||||
)
|
||||
"
|
||||
>
|
||||
<span>Details</span>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
@@ -410,7 +396,7 @@ onMounted(() => {
|
||||
<!-- Unassigned cameras -->
|
||||
<v-col v-for="(camera, index) in unmatchedCameras" :key="index" cols="12" sm="6" lg="4">
|
||||
<v-card dark color="primary">
|
||||
<v-card-title>
|
||||
<v-card-title class="pb-2">
|
||||
<span v-if="camera.PVUsbCameraInfo">USB Camera:</span>
|
||||
<span v-else-if="camera.PVCSICameraInfo">CSI Camera:</span>
|
||||
<span v-else-if="camera.PVFileCameraInfo">File Camera:</span>
|
||||
@@ -424,7 +410,7 @@ onMounted(() => {
|
||||
<v-card-text class="pt-0">
|
||||
<v-row>
|
||||
<v-col cols="6" class="pr-0">
|
||||
<v-btn color="secondary" style="width: 100%" @click="setCameraView(camera)">
|
||||
<v-btn color="secondary" style="width: 100%" @click="setCameraView(camera, false)">
|
||||
<span>Details</span>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
@@ -462,22 +448,25 @@ onMounted(() => {
|
||||
|
||||
<!-- Camera details modal -->
|
||||
<v-dialog v-model="viewingDetails" max-width="800">
|
||||
<v-card v-if="viewingCamera !== null" dark flat color="primary">
|
||||
<v-card v-if="viewingCamera[0] !== null" dark flat color="primary">
|
||||
<v-card-title class="d-flex justify-space-between">
|
||||
<span>{{ cameraInfoFor(viewingCamera)?.name ?? cameraInfoFor(viewingCamera)?.baseName }}</span>
|
||||
<v-btn text @click="setCameraView(null)">
|
||||
<span>{{ cameraInfoFor(viewingCamera[0])?.name ?? cameraInfoFor(viewingCamera[0])?.baseName }}</span>
|
||||
<v-btn text @click="setCameraView(null, null)">
|
||||
<v-icon>mdi-close-thick</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-card-text v-if="!camerasMatch(getMatchedDevice(viewingCamera), viewingCamera)">
|
||||
<v-card-text v-if="!viewingCamera[1]">
|
||||
<PvCameraInfoCard :camera="viewingCamera[0]" />
|
||||
</v-card-text>
|
||||
<v-card-text v-else-if="!camerasMatch(getMatchedDevice(viewingCamera[0]), viewingCamera[0])">
|
||||
<v-banner rounded color="error" text-color="white" icon="mdi-information-outline" class="mb-3">
|
||||
It looks like a different camera may have been connected to this device! Compare the following information
|
||||
carefully.
|
||||
</v-banner>
|
||||
<PvCameraMatchCard :saved="viewingCamera" :current="getMatchedDevice(viewingCamera)" />
|
||||
<PvCameraMatchCard :saved="viewingCamera[0]" :current="getMatchedDevice(viewingCamera[0])" />
|
||||
</v-card-text>
|
||||
<v-card-text v-else>
|
||||
<PvCameraInfoCard :camera="getMatchedDevice(viewingCamera)" />
|
||||
<PvCameraInfoCard :camera="getMatchedDevice(viewingCamera[0])" />
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
@@ -561,14 +550,4 @@ a:active,
|
||||
background-color: transparent;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.wide-stream {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.tall-stream {
|
||||
height: 100%;
|
||||
width: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user