Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Finalise signalR rebase, include updating npm
Browse files Browse the repository at this point in the history
andchiind committed Nov 7, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 27600e1 commit c9204d5
Showing 14 changed files with 6,764 additions and 31,302 deletions.
4 changes: 2 additions & 2 deletions backend/api/Services/MissionDefinitionService.cs
Original file line number Diff line number Diff line change
@@ -71,7 +71,7 @@ public MissionDefinitionService(

public async Task<MissionDefinition> Create(MissionDefinition missionDefinition)
{
if (missionDefinition.LastRun is not null) { _context.Entry(missionDefinition.LastRun).State = EntityState.Unchanged; }
if (missionDefinition.LastSuccessfulRun is not null) { _context.Entry(missionDefinition.LastSuccessfulRun).State = EntityState.Unchanged; }
if (missionDefinition.Area is not null) { _context.Entry(missionDefinition.Area).State = EntityState.Unchanged; }

await _context.MissionDefinitions.AddAsync(missionDefinition);
@@ -124,7 +124,7 @@ public async Task<List<MissionDefinition>> ReadByDeckId(string deckId)

public async Task<MissionDefinition> Update(MissionDefinition missionDefinition)
{
if (missionDefinition.LastRun is not null) { _context.Entry(missionDefinition.LastRun).State = EntityState.Unchanged; }
if (missionDefinition.LastSuccessfulRun is not null) { _context.Entry(missionDefinition.LastSuccessfulRun).State = EntityState.Unchanged; }
if (missionDefinition.Area is not null) { _context.Entry(missionDefinition.Area).State = EntityState.Unchanged; }

var entry = _context.Update(missionDefinition);
37,689 changes: 6,634 additions & 31,055 deletions frontend/package-lock.json

Large diffs are not rendered by default.

67 changes: 36 additions & 31 deletions frontend/src/components/Contexts/MissionListsContext.tsx
Original file line number Diff line number Diff line change
@@ -32,6 +32,35 @@ interface MissionsResult {
missionQueue: Mission[]
}

const updateQueueIfMissionAlreadyQueued = (oldQueue: Mission[], updatedMission: Mission) => {
const existingMissionIndex = oldQueue.findIndex((m) => m.id === updatedMission.id)
if (existingMissionIndex !== -1) {
// If the mission is already in the queue
if (updatedMission.status !== MissionStatus.Pending) oldQueue.splice(existingMissionIndex, 1)
else oldQueue[existingMissionIndex] = updatedMission
}
return oldQueue
}

const updateOngoingMissionsWithUpdatedMission = (oldMissionList: Mission[], updatedMission: Mission) => {
const existingMissionIndex = oldMissionList.findIndex((m) => m.id === updatedMission.id)
if (updatedMission.status === MissionStatus.Ongoing || updatedMission.status === MissionStatus.Paused) {
if (existingMissionIndex !== -1) {
// Mission is ongoing and in the queue
oldMissionList[existingMissionIndex] = updatedMission
} else {
// Mission is ongoing and not in the queue
return [...oldMissionList, updatedMission]
}
} else {
if (existingMissionIndex !== -1) {
// Mission is not ongoing and in the queue
oldMissionList.splice(existingMissionIndex, 1)
}
}
return oldMissionList
}

const fetchMissions = (params: {
statuses: MissionStatus[]
pageSize: number
@@ -48,7 +77,7 @@ export const useMissions = (): MissionsResult => {
if (connectionReady) {
registerEvent(SignalREventLabels.missionRunCreated, (username: string, message: string) => {
const newMission: Mission = JSON.parse(message)
if (missionQueue.find((m) => m.id === newMission.id))
if (!missionQueue.find((m) => m.id === newMission.id))
setMissionQueue((oldQueue) => [...oldQueue, newMission])
else
setMissionQueue((oldQueue) => {
@@ -64,47 +93,23 @@ export const useMissions = (): MissionsResult => {

setMissionQueue((oldQueue) => {
const oldQueueCopy = [...oldQueue]
const existingMissionIndex = oldQueue.findIndex((m) => m.id === updatedMission.id)
if (existingMissionIndex !== -1) {
if (updatedMission.status !== MissionStatus.Pending)
oldQueueCopy.splice(existingMissionIndex, 1)
else oldQueueCopy[existingMissionIndex] = updatedMission
}
return oldQueueCopy
return updateQueueIfMissionAlreadyQueued(oldQueueCopy, updatedMission)
})
setOngoingMissions((oldQueue) => {
const oldQueueCopy = [...oldQueue]
const existingMissionIndex = oldQueue.findIndex((m) => m.id === updatedMission.id)
if (
updatedMission.status === MissionStatus.Ongoing ||
updatedMission.status === MissionStatus.Paused
) {
if (existingMissionIndex !== -1) {
// Mission is ongoing and in the queue
oldQueueCopy[existingMissionIndex] = updatedMission
} else {
// Mission is ongoing and not in the queue
return [...oldQueueCopy, updatedMission]
}
} else {
if (existingMissionIndex !== -1) {
// Mission is not ongoing and in the queue
oldQueueCopy.splice(existingMissionIndex, 1)
}
}
return oldQueueCopy
setOngoingMissions((oldMissionList) => {
const oldMissionListCopy = [...oldMissionList]
return updateOngoingMissionsWithUpdatedMission(oldMissionListCopy, updatedMission)
})
})
registerEvent(SignalREventLabels.missionRunDeleted, (username: string, message: string) => {
let deletedMission: Mission = JSON.parse(message)
setOngoingMissions((missions) => {
const ongoingIndex = missions.findIndex((m) => m.id === deletedMission.id)
if (ongoingIndex !== -1) missions.splice(ongoingIndex, 1)
if (ongoingIndex !== -1) missions.splice(ongoingIndex, 1) // Remove deleted mission
return missions
})
setMissionQueue((missions) => {
const queueIndex = missions.findIndex((m) => m.id === deletedMission.id)
if (queueIndex !== -1) missions.splice(queueIndex, 1)
if (queueIndex !== -1) missions.splice(queueIndex, 1) // Remove deleted mission
return missions
})
})
7 changes: 3 additions & 4 deletions frontend/src/components/Contexts/SignalRContext.tsx
Original file line number Diff line number Diff line change
@@ -60,7 +60,7 @@ export const SignalRProvider: FC<Props> = ({ children }) => {

useEffect(() => {
if (accessToken) {
console.log("Attempting to create signalR connection...")
console.log('Attempting to create signalR connection...')
var newConnection = new signalR.HubConnectionBuilder()
.withUrl(URL, {
accessTokenFactory: () => accessToken,
@@ -81,8 +81,7 @@ export const SignalRProvider: FC<Props> = ({ children }) => {
}, [accessToken])

const registerEvent = (eventName: string, onMessageReceived: (username: string, message: string) => void) => {
if (connection)
connection.on(eventName, (username, message) => onMessageReceived(username, message))
if (connection) connection.on(eventName, (username, message) => onMessageReceived(username, message))
}

return (
@@ -106,5 +105,5 @@ export enum SignalREventLabels {
missionRunCreated = 'Mission run created',
missionRunDeleted = 'Mission run deleted',
missionRunFailed = 'Mission run failed',
robotListUpdated = 'Robot list updated'
robotListUpdated = 'Robot list updated',
}
Original file line number Diff line number Diff line change
@@ -131,8 +131,7 @@ function InstallationPicker() {
<StyledCheckbox
label={TranslateText('Show only active installations')}
checked={showActivePlants}
onChange={(e) => setShowActivePlants(e.target.checked)}
/>
onChange={(e) => setShowActivePlants(e.target.checked)} crossOrigin={undefined} />
</BlockLevelContainer>
<Button
onClick={() => switchInstallation(selectedInstallation)}
Original file line number Diff line number Diff line change
@@ -76,7 +76,6 @@ function SeveralFailedMissions({ missions }: MissionsProps) {

export function FailedMissionAlertView() {
const [recentFailedMissions, setRecentFailedMissions] = useState<Mission[]>([])
const [newFailedMission, setNewFailedMission] = useState<Mission | undefined>(undefined)
const { registerEvent, connectionReady } = useSignalRContext()
const { installationCode } = useInstallationContext()

@@ -132,28 +131,26 @@ export function FailedMissionAlertView() {
useEffect(() => {
if (connectionReady)
registerEvent(SignalREventLabels.missionRunFailed, (username: string, message: string) => {
setNewFailedMission(JSON.parse(message))
console.log(JSON.parse(message))
const newFailedMission: Mission = JSON.parse(message)
const lastDismissTime: Date = getLastDismissalTime()

setRecentFailedMissions((failedMissions) => {
if (
installationCode &&
(!newFailedMission.installationCode ||
newFailedMission.installationCode.toLocaleLowerCase() !==
installationCode.toLocaleLowerCase())
)
return failedMissions // Ignore missions for other installations
// Ignore missions shortly after the user dismissed the last one
if (new Date(newFailedMission.endTime!) <= lastDismissTime) return failedMissions
let isDuplicate = failedMissions.filter((m) => m.id === newFailedMission.id).length > 0
if (isDuplicate) return failedMissions // Ignore duplicate failed missions
return [...failedMissions, newFailedMission]
})
})
}, [registerEvent, connectionReady])

// Use the failed missions from signalR messages to update the displayed list of failed missions
useEffect(() => {
if (newFailedMission) {
const lastDismissTime: Date = getLastDismissalTime()
if (
installationCode &&
(!newFailedMission.installationCode ||
newFailedMission.installationCode.toLocaleLowerCase() !== installationCode.toLocaleLowerCase())
)
return
if (new Date(newFailedMission.endTime!) <= lastDismissTime) return
let isDuplicate = recentFailedMissions.filter((m) => m.id === newFailedMission.id).length > 0
if (isDuplicate) return
setRecentFailedMissions([...recentFailedMissions, newFailedMission])
}
}, [newFailedMission])

const missionDisplay = <FailedMission mission={recentFailedMissions[0]} />
const severalMissions = <SeveralFailedMissions missions={recentFailedMissions} />

Original file line number Diff line number Diff line change
@@ -99,7 +99,7 @@ export function MissionQueueView() {
}

useEffect(() => {
if (selectedRobot || selectedEchoMissions.length === 0) {
if (!selectedRobot || selectedEchoMissions.length === 0) {
setScheduleButtonDisabled(true)
} else {
setScheduleButtonDisabled(false)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Button, Tabs, Icon } from '@equinor/eds-core-react'
import { useLanguageContext } from 'components/Contexts/LanguageContext'
import { DeckMissionType, InspectionSection, ScheduledMissionType } from './InspectionSection'
import { InspectionSection, ScheduledMissionType } from './InspectionSection'
import { useEffect, useRef, useState } from 'react'
import { BackendAPICaller } from 'api/ApiCaller'
import { AllInspectionsTable } from './InspectionTable'
@@ -10,7 +10,6 @@ import styled from 'styled-components'
import { useContext } from 'react'
import { InstallationContext } from 'components/Contexts/InstallationContext'
import { Icons } from 'utils/icons'
import { Mission, MissionStatus } from 'models/Mission'
import { useMissionsContext } from 'components/Contexts/MissionListsContext'

const StyledButton = styled(Button)`
@@ -73,7 +72,7 @@ export function InspectionOverviewSection() {
let newInspection: Inspection[] = missionDefinitions.map((m) => {
return {
missionDefinition: m,
deadline: m.lastRun ? getInspectionDeadline(m.inspectionFrequency, m.lastRun.endTime!) : undefined,
deadline: m.lastSuccessfulRun ? getInspectionDeadline(m.inspectionFrequency, m.lastSuccessfulRun.endTime!) : undefined,
}
})

107 changes: 59 additions & 48 deletions frontend/src/components/Pages/InspectionPage/InspectionSection.tsx
Original file line number Diff line number Diff line change
@@ -15,11 +15,13 @@ export interface Inspection {
deadline: Date | undefined
}

interface DeckInspectionTuple {
inspections: Inspection[]
deck: Deck
}

export interface DeckMissionType {
[deckName: string]: {
inspections: Inspection[]
deck: Deck
}
[deckName: string]: DeckInspectionTuple
}

export interface DeckMissionCount {
@@ -78,61 +80,71 @@ export function InspectionSection({
})
setUnscheduledMissions(unscheduledMissions)
}
}, [isDialogOpen])

const updateDeckMissions = () => {
BackendAPICaller.getDecks().then(async (decks: Deck[]) => {
let newDeckMissions: DeckMissionType = {}
const filteredDecks = decks.filter(
(deck) => deck.installationCode.toLowerCase() === installationCode.toLowerCase()
)
for (const deck of filteredDecks) {
// These calls need to be made sequentially to update areaMissions safely
let missionDefinitions = await BackendAPICaller.getMissionDefinitionsInDeck(deck)
if (!missionDefinitions) missionDefinitions = []
newDeckMissions[deck.deckName] = {
inspections: missionDefinitions.map((m) => {
return {
missionDefinition: m,
deadline: m.lastSuccessfulRun
? getInspectionDeadline(m.inspectionFrequency, m.lastSuccessfulRun.endTime!)
: undefined,
}
}),
deck: deck,
}
}
setDeckMissions(newDeckMissions)
})
}
}, [isDialogOpen, scheduledMissions, selectedMissions])

useMemo(() => {
const updateDeckMissions = () => {
BackendAPICaller.getDecks().then(async (decks: Deck[]) => {
let newDeckMissions: DeckMissionType = {}
const filteredDecks = decks.filter(
(deck) => deck.installationCode.toLowerCase() === installationCode.toLowerCase()
)
for (const deck of filteredDecks) {
// These calls need to be made sequentially to update areaMissions safely
let missionDefinitions = await BackendAPICaller.getMissionDefinitionsInDeck(deck)
if (!missionDefinitions) missionDefinitions = []
newDeckMissions[deck.deckName] = {
inspections: missionDefinitions.map((m) => {
return {
missionDefinition: m,
deadline: m.lastSuccessfulRun
? getInspectionDeadline(m.inspectionFrequency, m.lastSuccessfulRun.endTime!)
: undefined,
}
}),
deck: deck,
}
}
setDeckMissions(newDeckMissions)
})
}
setSelectedDeck(undefined)
updateDeckMissions()
}, [installationCode])

useEffect(() => {
const updateDeckInspectionsWithUpdatedMissionDefinition = (
mDef: CondensedMissionDefinition,
relevantDeck: DeckInspectionTuple
) => {
const inspections = relevantDeck.inspections
const index = inspections.findIndex((i) => i.missionDefinition.id === mDef.id)
if (index !== -1) {
// Ignore mission definitions for other decks
inspections[index] = {
missionDefinition: mDef,
deadline: mDef.lastSuccessfulRun // If there are no completed runs, set the deadline to undefined
? getInspectionDeadline(mDef.inspectionFrequency, mDef.lastSuccessfulRun.endTime!)
: undefined,
}
relevantDeck = { ...relevantDeck, inspections: inspections }
}
return relevantDeck
}

if (connectionReady) {
registerEvent(SignalREventLabels.missionDefinitionUpdated, (username: string, message: string) => {
const mDef: CondensedMissionDefinition = JSON.parse(message)
if (!mDef.area) return
const relevantDeck = mDef.area.deckName
const relevantDeckName = mDef.area.deckName
setDeckMissions((deckMissions) => {
const inspections = deckMissions[relevantDeck].inspections
const index = inspections.findIndex((i) => i.missionDefinition.id === mDef.id)
if (index !== -1) {
inspections[index] = {
missionDefinition: mDef,
deadline: mDef.lastSuccessfulRun
? getInspectionDeadline(mDef.inspectionFrequency, mDef.lastSuccessfulRun.endTime!)
: undefined,
}
return {
...deckMissions,
[relevantDeck]: { ...deckMissions[relevantDeck], inspections: inspections },
}
return {
...deckMissions,
[relevantDeckName]: updateDeckInspectionsWithUpdatedMissionDefinition(
mDef,
deckMissions[relevantDeckName]
),
}
return deckMissions
})
})
}
@@ -154,13 +166,12 @@ export function InspectionSection({
ongoingMissions={ongoingMissions}
handleScheduleAll={handleScheduleAll}
/>

{selectedDeck && (
<InspectionTable
deck={selectedDeck}
openDialog={openDialog}
setSelectedMissions={setSelectedMissions}
inspections={deckMissions[selectedDeck.id].inspections}
inspections={deckMissions[selectedDeck.deckName].inspections}
scheduledMissions={scheduledMissions}
ongoingMissions={ongoingMissions}
/>
Original file line number Diff line number Diff line change
@@ -134,7 +134,7 @@ const InspectionRow = ({
const isScheduled = Object.keys(scheduledMissions).includes(mission.id) && scheduledMissions[mission.id]
const isOngoing = Object.keys(ongoingMissions).includes(mission.id) && ongoingMissions[mission.id]

if (isScheduled) {
if (isScheduled || isOngoing) {
if (isOngoing) {
status = (
<StyledContent>
@@ -150,7 +150,7 @@ const InspectionRow = ({
</StyledContent>
)
} else {
if (!mission.lastRun || !mission.lastRun.endTime) {
if (!mission.lastSuccessfulRun || !mission.lastSuccessfulRun.endTime) {
if (inspection.missionDefinition.inspectionFrequency) {
status = (
<StyledContent>
@@ -176,7 +176,7 @@ const InspectionRow = ({
{TranslateText('No planned inspection')}
</StyledContent>
)
lastCompleted = formatDateString(mission.lastRun.endTime!)
lastCompleted = formatDateString(mission.lastSuccessfulRun.endTime!)
}
}

@@ -333,7 +333,7 @@ export function AllInspectionsTable({ inspections, scheduledMissions, ongoingMis
})
setUnscheduledMissions(unscheduledMissions)
}
}, [isDialogOpen])
}, [isDialogOpen, scheduledMissions, selectedMissions])

const navigate = useNavigate()
const cellValues = inspections
Original file line number Diff line number Diff line change
@@ -106,8 +106,8 @@ export const getDeadlineInspection = (deadline: Date) => {
export const compareInspections = (i1: Inspection, i2: Inspection) => {
if (!i1.missionDefinition.inspectionFrequency) return 1
if (!i2.missionDefinition.inspectionFrequency) return -1
if (!i1.missionDefinition.lastRun) return -1
if (!i2.missionDefinition.lastRun) return 1
if (!i1.missionDefinition.lastSuccessfulRun) return -1
if (!i2.missionDefinition.lastSuccessfulRun) return 1
else return i1.deadline!.getTime() - i2.deadline!.getTime()
}

@@ -128,7 +128,7 @@ export function CardMissionInformation({ deckId, deckMissions }: ICardMissionInf

deckMissions[deckId].inspections.forEach((inspection) => {
if (!inspection.deadline) {
if (!inspection.missionDefinition.lastRun && inspection.missionDefinition.inspectionFrequency) {
if (!inspection.missionDefinition.lastSuccessfulRun && inspection.missionDefinition.inspectionFrequency) {
colorsCount['red'].count++
} else {
colorsCount['green'].count++
127 changes: 0 additions & 127 deletions frontend/src/components/Pages/InspectionPage/ScheduleMissionDialog.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -72,7 +72,7 @@ export const ScheduleMissionDialog = (props: IProps): JSX.Element => {
let robots = [...enabledRobots]
robots.filter((robots) => robots.currentInstallation.toLowerCase() === installationCode.toLowerCase())
setRobotOptions(robots)
}, [enabledRobots])
}, [enabledRobots, installationCode])

const onSelectedRobot = (selectedRobot: Robot) => {
if (!robotOptions) return
Original file line number Diff line number Diff line change
@@ -279,7 +279,7 @@ export function MissionDefinitionPage() {
}
})
}
}, [registerEvent, connectionReady])
}, [registerEvent, connectionReady, missionId])

return (
<>

0 comments on commit c9204d5

Please sign in to comment.