diff --git a/backend/api/Database/Context/InitDb.cs b/backend/api/Database/Context/InitDb.cs index ee67918be..6f1036643 100644 --- a/backend/api/Database/Context/InitDb.cs +++ b/backend/api/Database/Context/InitDb.cs @@ -202,12 +202,8 @@ private static List GetMissionTaskDefinitions() Description = "dummy task 1", RobotPose = new Pose(), AnalysisTypes = [AnalysisType.Fencilla], - TargetPosition = new Position - { - X = 0, - Y = 0, - Z = 0, - }, + SensorType = SensorType.Image, + TargetPosition = new Position(), }; var task2 = new TaskDefinition @@ -216,13 +212,9 @@ private static List GetMissionTaskDefinitions() TagId = "dummy-tag-id-2", Description = "dummy task 2", RobotPose = new Pose(), + SensorType = SensorType.Image, AnalysisTypes = [AnalysisType.CLOE], - TargetPosition = new Position - { - X = 0, - Y = 0, - Z = 0, - }, + TargetPosition = new Position(), }; var task3 = new TaskDefinition @@ -232,12 +224,8 @@ private static List GetMissionTaskDefinitions() Description = "dummy task 3", RobotPose = new Pose(), AnalysisTypes = [AnalysisType.CO2], - TargetPosition = new Position - { - X = 0, - Y = 0, - Z = 0, - }, + SensorType = SensorType.CO2Measurement, + TargetPosition = new Position(), }; var task4 = new TaskDefinition @@ -248,12 +236,7 @@ private static List GetMissionTaskDefinitions() RobotPose = new Pose(), SensorType = SensorType.ThermalImage, AnalysisTypes = [AnalysisType.ThermalReading], - TargetPosition = new Position - { - X = 0, - Y = 0, - Z = 0, - }, + TargetPosition = new Position(), }; return new List([task1, task2, task3, task4]); @@ -347,6 +330,36 @@ private static List GetMissionDefinitions() LastSuccessfulRun = null, }; + var onlyImagesWithoutAnalysisMissionDefinition = new MissionDefinition + { + Id = Guid.NewGuid().ToString(), + Name = "Only Images Without Analysis Mission", + InstallationCode = inspectionAreas[5].Installation.InstallationCode, + InspectionArea = inspectionAreas[5], + Tasks = + [ + new TaskDefinition + { + TagId = "image-tag-1", + Description = "Tag to take image of", + RobotPose = new Pose(), + AnalysisTypes = [], + SensorType = SensorType.Image, + TargetPosition = new Position(), + }, + new TaskDefinition + { + TagId = "image-tag-2", + Description = "Tag to take image of", + RobotPose = new Pose(), + AnalysisTypes = [], + SensorType = SensorType.Image, + TargetPosition = new Position(), + }, + ], + LastSuccessfulRun = null, + }; + return new List([ missionDefinition1, missionDefinition2, @@ -356,6 +369,7 @@ private static List GetMissionDefinitions() missionDefinition6, thermalReadingMission, cloeMission, + onlyImagesWithoutAnalysisMissionDefinition, ]); } diff --git a/backend/api/Database/Models/TaskDefinition.cs b/backend/api/Database/Models/TaskDefinition.cs index 7c6f055e8..eb253a7b5 100644 --- a/backend/api/Database/Models/TaskDefinition.cs +++ b/backend/api/Database/Models/TaskDefinition.cs @@ -50,7 +50,7 @@ public TaskDefinition(TaskQuery taskQuery, int index) public IList AnalysisTypes { get; set; } = []; - public SensorType SensorType { get; set; } + public SensorType SensorType { get; set; } // Note that the enum will get SensorType.Image as default public float? VideoDuration { get; set; } diff --git a/frontend/src/models/InspectionRecord.ts b/frontend/src/models/InspectionRecord.ts index 7a5d25cab..29e48a7b9 100644 --- a/frontend/src/models/InspectionRecord.ts +++ b/frontend/src/models/InspectionRecord.ts @@ -52,7 +52,7 @@ export interface FlotillaAnalysisResultMessage { export interface InspectionData { inspectionId: string analysisId: string - visualisedSAS?: string + visualizedSAS?: string fileType: FileType anonymizedSAS?: string analysisType?: string @@ -94,7 +94,7 @@ export const inspectionRecordToInspectionData = (record: InspectionRecord): Insp return { inspectionId: record.inspectionId, analysisId: analysis.id, - visualisedSAS: analysis.visualizedSAS, + visualizedSAS: analysis.visualizedSAS, anonymizedSAS: analysis.anonymizedSAS, analysisType: record.inspectionType, tag: record.tag, diff --git a/frontend/src/pages/InspectionReportPage/InspectionReportImage.tsx b/frontend/src/pages/InspectionReportPage/InspectionReportImage.tsx index d9e8b391a..e68c012e6 100644 --- a/frontend/src/pages/InspectionReportPage/InspectionReportImage.tsx +++ b/frontend/src/pages/InspectionReportPage/InspectionReportImage.tsx @@ -162,7 +162,7 @@ const AnalysisImageWithPlaceholder = ({ const { useSaraData } = useInspectionsContext() const { data, isPending, isError } = useSaraData(inspectionId) - if (!data?.visualisedSAS) { + if (!data?.visualizedSAS) { return } else if (isPending) { return @@ -170,9 +170,9 @@ const AnalysisImageWithPlaceholder = ({ return } else return isLargeImage ? ( - + ) : ( - + ) } diff --git a/frontend/src/pages/MissionPage/AnalysisResultView.tsx b/frontend/src/pages/MissionPage/AnalysisResultView.tsx index a880f1d11..624a29c4d 100644 --- a/frontend/src/pages/MissionPage/AnalysisResultView.tsx +++ b/frontend/src/pages/MissionPage/AnalysisResultView.tsx @@ -39,8 +39,8 @@ export const AnalysisResultDialogContent = ({ inspection }: { inspection: Inspec return (
- {inspection.visualisedSAS ? ( - + {inspection.visualizedSAS ? ( + ) : ( <>{/* No image to display*/} )} diff --git a/frontend/src/pages/MissionPage/TaskOverview/TaskTable.tsx b/frontend/src/pages/MissionPage/TaskOverview/TaskTable.tsx index f26c1fdbe..d08b0c25a 100644 --- a/frontend/src/pages/MissionPage/TaskOverview/TaskTable.tsx +++ b/frontend/src/pages/MissionPage/TaskOverview/TaskTable.tsx @@ -110,7 +110,13 @@ const TaskTableRow = ({ - {inspectionData ? : <>} + + {inspectionData && (inspectionData.visualizedSAS || inspectionData.value) ? ( + + ) : ( + <> + )} + ) } diff --git a/frontend/src/pages/MissionSchedulingComponents/InspectionAreaCards.tsx b/frontend/src/pages/MissionSchedulingComponents/InspectionAreaCards.tsx index d693c347d..193f342b5 100644 --- a/frontend/src/pages/MissionSchedulingComponents/InspectionAreaCards.tsx +++ b/frontend/src/pages/MissionSchedulingComponents/InspectionAreaCards.tsx @@ -1,74 +1,66 @@ import { InspectionArea } from 'models/InspectionArea' -import { MissionDefinition } from 'models/MissionDefinition' -import { InspectionAreaInspectionTuple } from './InspectionSection' import { useLanguageContext } from 'components/Contexts/LanguageContext' import { CardComponent, Content, InspectionAreaText, - Placeholder, StyledCard, StyledInspectionAreaCard, - StyledInspectionAreaCards, TopInspectionAreaText, } from './InspectionUtilities' import { Button, Icon, Tooltip, Typography } from '@equinor/eds-core-react' import { Icons } from 'utils/icons' import { tokens } from '@equinor/eds-tokens' -import { useMissionsContext } from 'components/Contexts/MissionRunsContext' import { useAssetContext } from 'components/Contexts/AssetContext' interface InspectionAreaCardProps { - inspectionAreaData: InspectionAreaInspectionTuple + inspectionArea: InspectionArea + nMissions: number onClickInspectionArea: (inspectionArea: InspectionArea) => void - selectedInspectionArea: InspectionArea | undefined - handleScheduleAll: (missionDefinitions: MissionDefinition[]) => void + isSelected: boolean + isMissionOngoing: boolean + onClickScheduleAll: (inspectionArea: InspectionArea) => void } -const InspectionAreaCard = ({ - inspectionAreaData, +export const InspectionAreaCard = ({ + inspectionArea, + nMissions, onClickInspectionArea, - selectedInspectionArea, - handleScheduleAll, + isSelected, + isMissionOngoing, + onClickScheduleAll, }: InspectionAreaCardProps) => { const { TranslateText } = useLanguageContext() - const { ongoingMissions } = useMissionsContext() const { enabledRobots } = useAssetContext() - const isScheduleMissionsDisabled = enabledRobots.length === 0 || inspectionAreaData.missionDefinitions.length === 0 + const isScheduleMissionsDisabled = enabledRobots.length === 0 || nMissions === 0 let queueMissionsTooltip = '' - if (inspectionAreaData.missionDefinitions.length === 0) queueMissionsTooltip = TranslateText('No available mission') + if (nMissions === 0) queueMissionsTooltip = TranslateText('No available mission') else if (isScheduleMissionsDisabled) queueMissionsTooltip = TranslateText('No robot available') return ( - + onClickInspectionArea(inspectionAreaData.inspectionArea)} - style={ - selectedInspectionArea === inspectionAreaData.inspectionArea - ? { border: `solid ${tokens.colors.interactive.focus.hex} 1px` } - : {} - } + key={inspectionArea.inspectionAreaName} + onClick={() => onClickInspectionArea(inspectionArea)} + style={isSelected ? { border: `solid ${tokens.colors.interactive.focus.hex} 1px` } : {}} > - {inspectionAreaData.inspectionArea.inspectionAreaName.toString()} + {inspectionArea.inspectionAreaName.toString()} - {inspectionAreaData.missionDefinitions - .filter((m) => ongoingMissions.find((om) => om.missionId === m.id)) - .map((mission) => ( - - - {TranslateText('InProgress')} - - ))} + {isMissionOngoing && ( + + + {TranslateText('InProgress')} + + )} - {inspectionAreaData.missionDefinitions.length}{' '} - {inspectionAreaData.missionDefinitions.length === 1 + {nMissions}{' '} + {nMissions === 1 ? TranslateText('Mission').toLowerCase() : TranslateText('Missions').toLowerCase()} @@ -78,13 +70,10 @@ const InspectionAreaCard = ({ - )} - {isScheduled && ( - - )} + ) } -export const MissionSchedulingTable = ({ - inspectionArea, - missionDefinitions, - openDialog, - setSelectedMissions, -}: IProps) => { - const { TranslateText } = useLanguageContext() - - const [isScheduledDialogOpen, setIsScheduledDialogOpen] = useState(false) - - const openScheduleDialog = () => { - setIsScheduledDialogOpen(true) - } +interface IProps { + inspectionArea: InspectionArea + missionDefinitions: MissionDefinition[] + setSelectedMissions: (selectedMissions: MissionDefinition[]) => void +} - const closeScheduleDialog = () => { - setIsScheduledDialogOpen(false) - } +export const MissionSchedulingTable = ({ inspectionArea, missionDefinitions, setSelectedMissions }: IProps) => { + const { TranslateText } = useLanguageContext() return ( @@ -190,20 +150,11 @@ export const MissionSchedulingTable = ({ {missionDefinitions.sort(compareMissionDefinitions).map((mission) => ( - + ))} - {isScheduledDialogOpen && ( - - )} ) } diff --git a/frontend/src/pages/MissionSchedulingComponents/ScheduleMissionDialogs.tsx b/frontend/src/pages/MissionSchedulingComponents/ScheduleMissionDialogs.tsx index 7c6cb1a52..e5adc4d89 100644 --- a/frontend/src/pages/MissionSchedulingComponents/ScheduleMissionDialogs.tsx +++ b/frontend/src/pages/MissionSchedulingComponents/ScheduleMissionDialogs.tsx @@ -1,4 +1,4 @@ -import { Autocomplete, Dialog, Typography, Icon } from '@equinor/eds-core-react' +import { Autocomplete, Typography, Icon } from '@equinor/eds-core-react' import styled from 'styled-components' import { useLanguageContext } from 'components/Contexts/LanguageContext' import { useState } from 'react' @@ -19,19 +19,6 @@ import { import { phone_width } from 'utils/constants' import { useBackendApi } from 'api/UseBackendApi' -interface IProps { - selectedMissions: MissionDefinition[] - closeDialog: () => void - setMissions: (selectedMissions: MissionDefinition[] | undefined) => void - unscheduledMissions: MissionDefinition[] - isAlreadyScheduled: boolean -} - -interface IScheduledProps { - openDialog: () => void - closeDialog: () => void -} - const StyledMissionDialog = styled.div` display: flex; justify-content: space-between; @@ -59,6 +46,13 @@ const StyledDangerContent = styled.div` gap: 2px; ` +interface IProps { + selectedMissions: MissionDefinition[] + closeDialog: () => void + unscheduledMissions: MissionDefinition[] + isAlreadyScheduled: boolean +} + export const ScheduleMissionDialog = (props: IProps) => { const { TranslateText } = useLanguageContext() const { enabledRobots } = useAssetContext() @@ -220,40 +214,3 @@ export const ScheduleMissionDialog = (props: IProps) => { ) } - -export const AlreadyScheduledMissionDialog = (props: IScheduledProps) => { - const { TranslateText } = useLanguageContext() - - return ( - <> - - {TranslateText('The mission is already in the queue')} - - {TranslateText('AlreadyScheduledText')} - - - - { - props.closeDialog() - }} - variant="outlined" - > - {' '} - {TranslateText('Cancel')}{' '} - - { - props.openDialog() - props.closeDialog() - }} - > - {' '} - {TranslateText('Queue mission')} - - - - - - ) -} diff --git a/frontend/src/pages/MissionSchedulingPage.tsx b/frontend/src/pages/MissionSchedulingPage.tsx index 51701f8d8..09757dfac 100644 --- a/frontend/src/pages/MissionSchedulingPage.tsx +++ b/frontend/src/pages/MissionSchedulingPage.tsx @@ -10,11 +10,14 @@ import { useMissionDefinitionsContext } from 'components/Contexts/MissionDefinit import { useAssetContext } from 'components/Contexts/AssetContext' import { PlantPolygonMap } from 'pages/MissionPage/MapPosition/PointillaMapView' import { Typography } from '@equinor/eds-core-react' -import { compareMissionDefinitions, InspectionAreaOverview } from './MissionSchedulingComponents/InspectionUtilities' -import { InspectionAreaCards } from './MissionSchedulingComponents/InspectionAreaCards' +import { + compareMissionDefinitions, + InspectionAreaOverview, + StyledInspectionAreaCards, +} from './MissionSchedulingComponents/InspectionUtilities' +import { InspectionAreaCard } from './MissionSchedulingComponents/InspectionAreaCards' import { MissionSchedulingTable } from './MissionSchedulingComponents/MissionSchedulingTable' import { ScheduleMissionDialog } from './MissionSchedulingComponents/ScheduleMissionDialogs' -import { InspectionAreaInspectionTuple } from './MissionSchedulingComponents/InspectionSection' import { StyledPage } from 'components/Styles/StyledComponents' import { useLanguageContext } from 'components/Contexts/LanguageContext' @@ -25,25 +28,17 @@ export const MissionSchedulingPage = () => { const { ongoingMissions, missionQueue } = useMissionsContext() const { installationInspectionAreas } = useAssetContext() const { missionDefinitions } = useMissionDefinitionsContext() - const [selectedMissions, setSelectedMissions] = useState() - const [isDialogOpen, setIsDialogOpen] = useState(false) - const [selectedInspectionArea, setSelectedInspectionArea] = useState() - const [scrollOnToggle, setScrollOnToggle] = useState(true) + const [selectedMissions, setSelectedMissions] = useState([]) + const [userSelectedInspectionArea, setSelectedInspectionArea] = useState(undefined) + const selectedInspectionArea = + userSelectedInspectionArea ?? + (installationInspectionAreas.length === 1 ? installationInspectionAreas[0] : undefined) - const inspectionAreaInspections: InspectionAreaInspectionTuple[] = - installationInspectionAreas?.map((inspectionArea) => { - const missionDefinitionsInInspectionArea = missionDefinitions.filter( - (m) => m.inspectionArea.inspectionAreaName === inspectionArea.inspectionAreaName - ) - return { - missionDefinitions: missionDefinitionsInInspectionArea, - inspectionArea: inspectionArea, - } - }) ?? [] + const countMissionsInInspectionArea = (inspectionArea: InspectionArea) => + missionDefinitions.filter((m) => m.inspectionArea.id === inspectionArea.id).length const onClickInspectionArea = (clickedInspectionArea: InspectionArea) => { setSelectedInspectionArea(clickedInspectionArea) - setScrollOnToggle(!scrollOnToggle) } const isScheduled = (mission: MissionDefinition) => missionQueue.map((m) => m.missionId).includes(mission.id) @@ -51,12 +46,11 @@ export const MissionSchedulingPage = () => { const closeDialog = () => { setSelectedMissions([]) - setIsDialogOpen(false) } - const handleScheduleAll = (missionDefinitions: MissionDefinition[]) => { - setIsDialogOpen(true) - const sortedMissionDefinitions = missionDefinitions.sort(compareMissionDefinitions) + const onClickScheduleAll = (inspectionArea: InspectionArea) => { + const relevantMissionDefinitions = missionDefinitions.filter((m) => m.inspectionArea.id === inspectionArea.id) + const sortedMissionDefinitions = relevantMissionDefinitions.sort(compareMissionDefinitions) setSelectedMissions(sortedMissionDefinitions) } @@ -65,12 +59,9 @@ export const MissionSchedulingPage = () => { const unscheduledMissions = selectedMissions?.filter((m) => !isOngoing(m) && !isScheduled(m)) - const inspectionArea = - installationInspectionAreas.length === 1 ? installationInspectionAreas[0] : selectedInspectionArea - const selectedAreaMissionDefinitions = - inspectionAreaInspections.length === 1 - ? inspectionAreaInspections[0].missionDefinitions - : inspectionAreaInspections.find((d) => d.inspectionArea === inspectionArea)?.missionDefinitions + const selectedInspectionAreaMissionDefinitions = selectedInspectionArea + ? missionDefinitions.filter((m) => m.inspectionArea.id === selectedInspectionArea.id) + : undefined return ( <> @@ -78,46 +69,54 @@ export const MissionSchedulingPage = () => { - {installationInspectionAreas.length !== 1 && ( - + {installationInspectionAreas.length > 1 && ( + + {installationInspectionAreas.map((inspectionArea) => ( + m.inspectionArea.id === inspectionArea.id + )} + onClickScheduleAll={onClickScheduleAll} + /> + ))} + )} - {inspectionArea && selectedAreaMissionDefinitions && selectedAreaMissionDefinitions.length > 0 && ( - setIsDialogOpen(true)} - setSelectedMissions={setSelectedMissions} - missionDefinitions={selectedAreaMissionDefinitions} - /> - )} - {inspectionArea && - selectedAreaMissionDefinitions && - selectedAreaMissionDefinitions.length === 0 && ( + {selectedInspectionArea && + selectedInspectionAreaMissionDefinitions && + selectedInspectionAreaMissionDefinitions.length > 0 && ( + + )} + {selectedInspectionArea && + selectedInspectionAreaMissionDefinitions && + selectedInspectionAreaMissionDefinitions.length === 0 && ( {TranslateText('No missions defined in this area')} )} - {inspectionArea?.plantCode && inspectionArea.areaPolygon?.positions && ( + {selectedInspectionArea?.areaPolygon?.positions && ( <> {installationInspectionAreas.length === 1 && ( - {inspectionArea?.inspectionAreaName} + {selectedInspectionArea?.inspectionAreaName} )} - {' '} + {' '} )} - {isDialogOpen && ( + {selectedMissions.length > 0 && (