Skip to content

Commit

Permalink
Finalise signalR rebase, include updating npm
Browse files Browse the repository at this point in the history
  • Loading branch information
andchiind committed Nov 7, 2023
1 parent 27600e1 commit c9204d5
Show file tree
Hide file tree
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
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
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
Expand Up @@ -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
Expand All @@ -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) => {
Expand All @@ -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
})
})
Expand Down
7 changes: 3 additions & 4 deletions frontend/src/components/Contexts/SignalRContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 (
Expand All @@ -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
Expand Up @@ -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)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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} />

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

useEffect(() => {
if (selectedRobot || selectedEchoMissions.length === 0) {
if (!selectedRobot || selectedEchoMissions.length === 0) {
setScheduleButtonDisabled(true)
} else {
setScheduleButtonDisabled(false)
Expand Down
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'
Expand All @@ -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)`
Expand Down Expand Up @@ -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,
}
})

Expand Down
107 changes: 59 additions & 48 deletions frontend/src/components/Pages/InspectionPage/InspectionSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
})
})
}
Expand All @@ -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}
/>
Expand Down
Loading

0 comments on commit c9204d5

Please sign in to comment.