diff --git a/photon-client/src/lib/MatchingUtils.ts b/photon-client/src/lib/MatchingUtils.ts new file mode 100644 index 0000000000..018165bcc6 --- /dev/null +++ b/photon-client/src/lib/MatchingUtils.ts @@ -0,0 +1,68 @@ +import { PVCameraInfo, type PVCSICameraInfo, type PVFileCameraInfo, type PVUsbCameraInfo } from "@/types/SettingTypes"; + +/** + * Check if two cameras match by comparing properties. + * For USB cameras, it checks the name, vendorId, productId, and uniquePath. + * For CSI cameras, it checks the uniquePath and baseName. + * For file cameras, it checks the uniquePath and name. + * Note: When changing this function, change the equivalent function within photon-core's VisionSourceManager class + */ +export const camerasMatch = (camera1: PVCameraInfo, camera2: PVCameraInfo) => { + if (camera1.PVUsbCameraInfo && camera2.PVUsbCameraInfo) + return ( + camera1.PVUsbCameraInfo.name === camera2.PVUsbCameraInfo.name && + camera1.PVUsbCameraInfo.vendorId === camera2.PVUsbCameraInfo.vendorId && + camera1.PVUsbCameraInfo.productId === camera2.PVUsbCameraInfo.productId && + camera1.PVUsbCameraInfo.uniquePath === camera2.PVUsbCameraInfo.uniquePath + ); + else if (camera1.PVCSICameraInfo && camera2.PVCSICameraInfo) + return ( + camera1.PVCSICameraInfo.uniquePath === camera2.PVCSICameraInfo.uniquePath && + camera1.PVCSICameraInfo.baseName === camera2.PVCSICameraInfo.baseName + ); + else if (camera1.PVFileCameraInfo && camera2.PVFileCameraInfo) + return ( + camera1.PVFileCameraInfo.uniquePath === camera2.PVFileCameraInfo.uniquePath && + camera1.PVFileCameraInfo.name === camera2.PVFileCameraInfo.name + ); + else return false; +}; + +/** + * Get the connection-type-specific camera info from the given PVCameraInfo object. + */ +export const cameraInfoFor = ( + camera: PVCameraInfo | null +): PVUsbCameraInfo | PVCSICameraInfo | PVFileCameraInfo | any => { + if (!camera) return null; + if (camera.PVUsbCameraInfo) { + return camera.PVUsbCameraInfo; + } + if (camera.PVCSICameraInfo) { + return camera.PVCSICameraInfo; + } + if (camera.PVFileCameraInfo) { + return camera.PVFileCameraInfo; + } + return {}; +}; + +/** + * Find the PVCameraInfo currently occupying the same uniquePath as the the given module + */ +export const getMatchedDevice = (allDevices: PVCameraInfo[], info: PVCameraInfo | undefined): PVCameraInfo => { + if (!info) { + return { + PVFileCameraInfo: undefined, + PVCSICameraInfo: undefined, + PVUsbCameraInfo: undefined + }; + } + return ( + allDevices.find((it) => cameraInfoFor(it).uniquePath === cameraInfoFor(info).uniquePath) || { + PVFileCameraInfo: undefined, + PVCSICameraInfo: undefined, + PVUsbCameraInfo: undefined + } + ); +}; diff --git a/photon-client/src/views/CameraMatchingView.vue b/photon-client/src/views/CameraMatchingView.vue index 082ab12857..a3532c8a10 100644 --- a/photon-client/src/views/CameraMatchingView.vue +++ b/photon-client/src/views/CameraMatchingView.vue @@ -2,19 +2,13 @@ import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore"; import { computed, inject, ref } from "vue"; import { useStateStore } from "@/stores/StateStore"; -import { - PlaceholderCameraSettings, - PVCameraInfo, - type PVCSICameraInfo, - type PVFileCameraInfo, - type PVUsbCameraInfo, - type UiCameraConfiguration -} from "@/types/SettingTypes"; +import { PlaceholderCameraSettings, PVCameraInfo, type UiCameraConfiguration } from "@/types/SettingTypes"; import { getResolutionString } from "@/lib/PhotonUtils"; import PvCameraInfoCard from "@/components/common/pv-camera-info-card.vue"; import axios from "axios"; import PvCameraMatchCard from "@/components/common/pv-camera-match-card.vue"; import type { WebsocketCameraSettingsUpdate } from "@/types/WebsocketDataTypes"; +import { camerasMatch, cameraInfoFor, getMatchedDevice } from "@/lib/MatchingUtils"; const formatUrl = (port) => `http://${inject("backendHostname")}:${port}/stream.mjpg`; const host = inject("backendHost"); @@ -95,63 +89,6 @@ const deleteThisCamera = (cameraName: string) => { }); }; -const camerasMatch = (camera1: PVCameraInfo, camera2: PVCameraInfo) => { - if (camera1.PVUsbCameraInfo && camera2.PVUsbCameraInfo) - return ( - camera1.PVUsbCameraInfo.name === camera2.PVUsbCameraInfo.name && - camera1.PVUsbCameraInfo.vendorId === camera2.PVUsbCameraInfo.vendorId && - camera1.PVUsbCameraInfo.productId === camera2.PVUsbCameraInfo.productId && - camera1.PVUsbCameraInfo.uniquePath === camera2.PVUsbCameraInfo.uniquePath - ); - else if (camera1.PVCSICameraInfo && camera2.PVCSICameraInfo) - return ( - camera1.PVCSICameraInfo.uniquePath === camera2.PVCSICameraInfo.uniquePath && - camera1.PVCSICameraInfo.baseName === camera2.PVCSICameraInfo.baseName - ); - else if (camera1.PVFileCameraInfo && camera2.PVFileCameraInfo) - return ( - camera1.PVFileCameraInfo.uniquePath === camera2.PVFileCameraInfo.uniquePath && - camera1.PVFileCameraInfo.name === camera2.PVFileCameraInfo.name - ); - else return false; -}; - -const cameraInfoFor = (camera: PVCameraInfo | null): PVUsbCameraInfo | PVCSICameraInfo | PVFileCameraInfo | any => { - if (!camera) return null; - if (camera.PVUsbCameraInfo) { - return camera.PVUsbCameraInfo; - } - if (camera.PVCSICameraInfo) { - return camera.PVCSICameraInfo; - } - if (camera.PVFileCameraInfo) { - return camera.PVFileCameraInfo; - } - return {}; -}; - -/** - * Find the PVCameraInfo currently occupying the same uniquepath as the the given module - */ -const getMatchedDevice = (info: PVCameraInfo | undefined): PVCameraInfo => { - if (!info) { - return { - PVFileCameraInfo: undefined, - PVCSICameraInfo: undefined, - PVUsbCameraInfo: undefined - }; - } - return ( - useStateStore().vsmState.allConnectedCameras.find( - (it) => cameraInfoFor(it).uniquePath === cameraInfoFor(info).uniquePath - ) || { - PVFileCameraInfo: undefined, - PVCSICameraInfo: undefined, - PVUsbCameraInfo: undefined - } - ); -}; - const cameraCononected = (uniquePath: string): boolean => { return ( useStateStore().vsmState.allConnectedCameras.find((it) => cameraInfoFor(it).uniquePath === uniquePath) !== undefined @@ -226,7 +163,10 @@ const openExportSettingsPrompt = () => { Status: Active { - + It looks like a different camera may have been connected to this device! Compare the following information carefully. - + - + diff --git a/photon-client/src/views/DashboardView.vue b/photon-client/src/views/DashboardView.vue index e354956826..a1e42a62d8 100644 --- a/photon-client/src/views/DashboardView.vue +++ b/photon-client/src/views/DashboardView.vue @@ -7,6 +7,7 @@ import PipelineConfigCard from "@/components/dashboard/ConfigOptions.vue"; import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore"; import { useStateStore } from "@/stores/StateStore"; import { PlaceholderCameraSettings } from "@/types/SettingTypes"; +import { camerasMatch, getMatchedDevice } from "@/lib/MatchingUtils"; const cameraViewType = computed({ get: (): number[] => { @@ -58,6 +59,18 @@ const arducamWarningShown = computed(() => { ) ); }); + +const cameraMismatchWarningShown = computed(() => { + return Object.values(useCameraSettingsStore().cameras).some( + (camera) => + camera.nickname !== "Placeholder Camera" && + camera.isConnected && + !camerasMatch( + getMatchedDevice(useStateStore().vsmState.allConnectedCameras, camera.matchedCameraInfo), + camera.matchedCameraInfo + ) + ); +});