Files
PhotonVision/photon-client/src/components/cameras/CameraControlCard.vue

220 lines
6.6 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import { ref } from "vue";
import axios from "axios";
import { useStateStore } from "@/stores/StateStore";
interface SnapshotMetadata {
snapshotName: string;
cameraNickname: string;
streamType: "input" | "output";
timeCreated: Date;
}
const getSnapshotMetadataFromName = (snapshotName: string): SnapshotMetadata => {
snapshotName = snapshotName.replace(/\.[^/.]+$/, "");
const data = snapshotName.split("_");
const cameraName = data.slice(0, data.length - 2).join("_");
const streamType = data[data.length - 2] as "input" | "output";
const dateStr = data[data.length - 1];
const year = parseInt(dateStr.substring(0, 4), 10);
const month = parseInt(dateStr.substring(5, 7), 10) - 1; // Months are zero-based
const day = parseInt(dateStr.substring(8, 10), 10);
const hours = parseInt(dateStr.substring(11, 13), 10);
const minutes = parseInt(dateStr.substring(13, 15), 10);
const seconds = parseInt(dateStr.substring(15, 17), 10);
const milliseconds = parseInt(dateStr.substring(17), 10);
return {
snapshotName: snapshotName,
cameraNickname: cameraName,
streamType: streamType,
timeCreated: new Date(year, month, day, hours, minutes, seconds, milliseconds)
};
};
interface Snapshot {
index: number;
snapshotName: string;
snapshotShortName: string;
cameraUniqueName: string;
cameraNickname: string;
streamType: "input" | "output";
timeCreated: Date;
snapshotSrc: string;
}
const imgData = ref<Snapshot[]>([]);
const fetchSnapshots = () => {
axios
.get("/utils/getImageSnapshots")
.then((response) => {
imgData.value = response.data.map(
(snapshotData: { snapshotName: string; cameraUniqueName: string; snapshotData: string }, index) => {
const metadata = getSnapshotMetadataFromName(snapshotData.snapshotName);
return {
index: index,
snapshotName: snapshotData.snapshotName,
snapshotShortName: metadata.snapshotName,
cameraUniqueName: snapshotData.cameraUniqueName,
cameraNickname: metadata.cameraNickname,
streamType: metadata.streamType,
timeCreated: metadata.timeCreated,
snapshotSrc: "data:image/jpg;base64," + snapshotData.snapshotData
};
}
);
showSnapshotViewerDialog.value = true;
})
.catch((error) => {
if (error.response) {
useStateStore().showSnackbarMessage({
color: "error",
message: error.response.data.text || error.response.data
});
} else if (error.request) {
useStateStore().showSnackbarMessage({
color: "error",
message: "Error while trying to process the request! The backend didn't respond."
});
} else {
useStateStore().showSnackbarMessage({
color: "error",
message: "An error occurred while trying to process the request."
});
}
});
};
const showSnapshotViewerDialog = ref(false);
const expanded = ref([]);
</script>
<template>
<v-card style="background-color: #006492">
<v-card-title>Camera Control</v-card-title>
General UI Refinements (#1678) Does the following: - Adjusts the shade of red buttons and banners to increase readability and reduce eye strain ![image](https://github.com/user-attachments/assets/7f741a9e-dc1e-4394-b87d-580e189245b1) ![image](https://github.com/user-attachments/assets/b23202f1-4cf6-46c1-aca5-2455a09259cd) - Cleans up factory reset and camera deletion modals ![image](https://github.com/user-attachments/assets/e6564732-d578-43da-bc83-729ec6fdbc5e) ![image](https://github.com/user-attachments/assets/9c5a1cba-f4fd-47ea-811c-abbabe5fa3a4) - Removes matchCamerasOnlyByPath as it is no longer used and throws errors in the console ![image](https://github.com/user-attachments/assets/77043993-26a2-4de4-8e98-702e7f285dc6) - Limits the criteria to flag a camera mismatch in Camera Matching to only what is necessary based on camera type and highlights differences in table properties (testing on this is appreciated) ![image](https://github.com/user-attachments/assets/cfbd96c1-09dd-414a-8177-693fc054b26f) - Only displays both saved vs. current info in camera matching if there is a difference between the two ![image](https://github.com/user-attachments/assets/6223ffc8-4cff-464f-8b54-720c3222a5d5) - Some general code cleanup (reduced unnecessary padding/margin/row-col statements, style="display:flex;" -> class="d-flex", etc. - Moves Compact Mode button to the bottom away from all the menu items (cleaner imo, open to thoughts) - Establishes a general spacing format for cards and pages and applies this to existing cards and pages to create a consistent look and feel to the UI (e.g. keeping things in line and less erratic spacing/placement of UI elements) ![image](https://github.com/user-attachments/assets/1ab0ca4b-303e-436d-97b3-da72d46c4fcb) ![image](https://github.com/user-attachments/assets/82ba9e53-f854-4309-bc00-7b5d0bad58b7) ![image](https://github.com/user-attachments/assets/18aa6ca4-e6fa-4125-8a0a-e6a007a0337d) ![image](https://github.com/user-attachments/assets/77043993-26a2-4de4-8e98-702e7f285dc6) - Delete protection for camera matching modules - Anti-backend-spam for activate/deactivate/delete modules to hopefully prevent any odd behavior from button spamming - Enforces a common camera stream size on camera matching view (NEEDS MORE TESTING) ![image](https://github.com/user-attachments/assets/9032783d-1edf-4c6e-ba7b-00e5f20280df) https://private-user-images.githubusercontent.com/29715865/400783758-dc99c151-b8a7-4367-a173-74c2fc5b2666.mp4?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzYyNTc3NzEsIm5iZiI6MTczNjI1NzQ3MSwicGF0aCI6Ii8yOTcxNTg2NS80MDA3ODM3NTgtZGM5OWMxNTEtYjhhNy00MzY3LWExNzMtNzRjMmZjNWIyNjY2Lm1wND9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAxMDclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMTA3VDEzNDQzMVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWMwOWM1MDc2ZTVlOWZhM2MxYjAwZjAyZTc2MTYyZTk1ZTVmOGFhZmVkMzlmODRlZTk1ODVlOTk2ZGQzZmM0Y2EmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.ovtRnObwbkEfljr9d5fqaory0nH91LWJSSkmrUUe_4Y
2025-01-07 08:45:39 -05:00
<v-card-text>
<v-btn color="secondary" @click="fetchSnapshots">
<v-icon start class="open-icon"> mdi-folder </v-icon>
General UI Refinements (#1678) Does the following: - Adjusts the shade of red buttons and banners to increase readability and reduce eye strain ![image](https://github.com/user-attachments/assets/7f741a9e-dc1e-4394-b87d-580e189245b1) ![image](https://github.com/user-attachments/assets/b23202f1-4cf6-46c1-aca5-2455a09259cd) - Cleans up factory reset and camera deletion modals ![image](https://github.com/user-attachments/assets/e6564732-d578-43da-bc83-729ec6fdbc5e) ![image](https://github.com/user-attachments/assets/9c5a1cba-f4fd-47ea-811c-abbabe5fa3a4) - Removes matchCamerasOnlyByPath as it is no longer used and throws errors in the console ![image](https://github.com/user-attachments/assets/77043993-26a2-4de4-8e98-702e7f285dc6) - Limits the criteria to flag a camera mismatch in Camera Matching to only what is necessary based on camera type and highlights differences in table properties (testing on this is appreciated) ![image](https://github.com/user-attachments/assets/cfbd96c1-09dd-414a-8177-693fc054b26f) - Only displays both saved vs. current info in camera matching if there is a difference between the two ![image](https://github.com/user-attachments/assets/6223ffc8-4cff-464f-8b54-720c3222a5d5) - Some general code cleanup (reduced unnecessary padding/margin/row-col statements, style="display:flex;" -> class="d-flex", etc. - Moves Compact Mode button to the bottom away from all the menu items (cleaner imo, open to thoughts) - Establishes a general spacing format for cards and pages and applies this to existing cards and pages to create a consistent look and feel to the UI (e.g. keeping things in line and less erratic spacing/placement of UI elements) ![image](https://github.com/user-attachments/assets/1ab0ca4b-303e-436d-97b3-da72d46c4fcb) ![image](https://github.com/user-attachments/assets/82ba9e53-f854-4309-bc00-7b5d0bad58b7) ![image](https://github.com/user-attachments/assets/18aa6ca4-e6fa-4125-8a0a-e6a007a0337d) ![image](https://github.com/user-attachments/assets/77043993-26a2-4de4-8e98-702e7f285dc6) - Delete protection for camera matching modules - Anti-backend-spam for activate/deactivate/delete modules to hopefully prevent any odd behavior from button spamming - Enforces a common camera stream size on camera matching view (NEEDS MORE TESTING) ![image](https://github.com/user-attachments/assets/9032783d-1edf-4c6e-ba7b-00e5f20280df) https://private-user-images.githubusercontent.com/29715865/400783758-dc99c151-b8a7-4367-a173-74c2fc5b2666.mp4?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzYyNTc3NzEsIm5iZiI6MTczNjI1NzQ3MSwicGF0aCI6Ii8yOTcxNTg2NS80MDA3ODM3NTgtZGM5OWMxNTEtYjhhNy00MzY3LWExNzMtNzRjMmZjNWIyNjY2Lm1wND9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAxMDclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMTA3VDEzNDQzMVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWMwOWM1MDc2ZTVlOWZhM2MxYjAwZjAyZTc2MTYyZTk1ZTVmOGFhZmVkMzlmODRlZTk1ODVlOTk2ZGQzZmM0Y2EmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.ovtRnObwbkEfljr9d5fqaory0nH91LWJSSkmrUUe_4Y
2025-01-07 08:45:39 -05:00
<span class="open-label">Show Saved Snapshots</span>
</v-btn>
</v-card-text>
<v-dialog v-model="showSnapshotViewerDialog">
<v-card class="pt-3 pl-5 pr-5" color="primary" flat>
<v-card-title> View Saved Frame Snapshots </v-card-title>
<v-divider />
<v-card-text v-if="imgData.length === 0" style="font-size: 18px; font-weight: 600" class="pt-4">
There are no snapshots saved
</v-card-text>
<div v-else class="pb-2">
<v-data-table
v-model:expanded="expanded"
:headers="[
{ title: 'Snapshot Name', key: 'snapshotShortName', sortable: false },
{ title: 'Camera Unique Name', key: 'cameraUniqueName' },
{ title: 'Camera Nickname', key: 'cameraNickname' },
{ title: 'Stream Type', key: 'streamType' },
{ title: 'Time Created', key: 'timeCreated' },
{ title: 'Actions', key: 'actions', sortable: false }
]"
:items="imgData"
:group-by="[{ key: 'cameraUniqueName' }]"
class="elevation-0"
item-value="index"
show-expand
>
<template #item.data-table-expand="{ internalItem, toggleExpand }">
<v-btn
icon="mdi-eye"
class="text-none"
color="medium-emphasis"
size="small"
variant="text"
slim
@click="toggleExpand(internalItem)"
></v-btn>
</template>
<template #expanded-row="{ item, columns }">
<td :colspan="columns.length">
<div style="display: flex; justify-content: center; width: 100%">
<img :src="item.snapshotSrc" alt="snapshot-image" class="snapshot-preview pt-2 pb-2" />
</div>
</td>
</template>
<!-- eslint-disable-next-line vue/valid-v-slot-->
<template #item.actions="{ item }">
<div style="display: flex; justify-content: center">
<a :download="item.snapshotName" :href="item.snapshotSrc">
<v-icon size="small"> mdi-download </v-icon>
</a>
</div>
</template>
</v-data-table>
<span
>Snapshot Timestamps may be incorrect as they depend on when the coprocessor was last connected to the
internet</span
>
</div>
</v-card>
</v-dialog>
</v-card>
</template>
<style scoped lang="scss">
.v-divider {
border-color: white !important;
}
.v-btn {
width: 100%;
}
.v-table {
text-align: center;
background-color: #006492 !important;
th,
td {
background-color: #005281 !important;
font-size: 1rem !important;
}
tbody :hover tr {
background-color: #005281 !important;
}
::-webkit-scrollbar {
width: 0;
height: 0.55em;
border-radius: 5px;
}
::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
background-color: #ffd843;
border-radius: 10px;
}
}
.snapshot-preview {
max-width: 55%;
}
@media only screen and (max-width: 512px) {
.snapshot-preview {
max-width: 100%;
}
}
@media only screen and (max-width: 351px) {
.open-icon {
margin: 0 !important;
}
.open-label {
display: none;
}
}
</style>