Skip to content

Commit 0efd077

Browse files
committed
Add auto scheduling fail banner to notify the user
1 parent 96441c3 commit 0efd077

File tree

7 files changed

+159
-17
lines changed

7 files changed

+159
-17
lines changed

backend/api.test/Mocks/SignalRServiceMock.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,14 @@ public void ReportGeneralFailToSignalR(Robot robot, string title, string message
3636
{
3737
return;
3838
}
39+
40+
public void ReportAutoScheduleFailToSignalR(
41+
string missionDefinitionId,
42+
string message,
43+
string installationCode
44+
)
45+
{
46+
return;
47+
}
3948
}
4049
}

backend/api/HostedServices/InspectionFrequencyHostedService.cs

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ IServiceScopeFactory scopeFactory
3939
private IMissionLoader MissionLoader =>
4040
_scopeFactory.CreateScope().ServiceProvider.GetRequiredService<IMissionLoader>();
4141

42+
private ISignalRService SignalRService =>
43+
_scopeFactory.CreateScope().ServiceProvider.GetRequiredService<ISignalRService>();
44+
4245
public Task StartAsync(CancellationToken stoppingToken)
4346
{
4447
_logger.LogInformation("Auto Scheduling Hosted Service Running.");
@@ -96,9 +99,12 @@ private async void DoWork(object? state)
9699
{
97100
if (missionDefinition.LastSuccessfulRun == null)
98101
{
99-
_logger.LogInformation(
100-
"Mission definition with Id {MissionDefinitionId} does not have a last successfull mission run.",
101-
missionDefinition.Id
102+
string message =
103+
$"Mission definition with Id {missionDefinition.Id} does not have a last successful mission run.";
104+
ReportMessageToSignalR(
105+
message,
106+
missionDefinition.Id,
107+
missionDefinition.InstallationCode
102108
);
103109
continue;
104110
}
@@ -139,9 +145,12 @@ public async Task AutomaticScheduleMissionRun(MissionDefinition missionDefinitio
139145

140146
if (missionDefinition.InspectionArea == null)
141147
{
142-
_logger.LogWarning(
143-
"Mission definition {MissionDefinitionId} has no inspection area.",
144-
missionDefinition.Id
148+
string message =
149+
$"Mission definition {missionDefinition.Id} has no inspection area.";
150+
ReportMessageToSignalR(
151+
message,
152+
missionDefinition.Id,
153+
missionDefinition.InstallationCode
145154
);
146155
return;
147156
}
@@ -161,8 +170,11 @@ public async Task AutomaticScheduleMissionRun(MissionDefinition missionDefinitio
161170

162171
if (robots == null)
163172
{
164-
_logger.LogInformation(
165-
"No robots found for installation code {InstallationCode}.",
173+
string message =
174+
$"No robots found for installation code {missionDefinition.InstallationCode}.";
175+
ReportMessageToSignalR(
176+
message,
177+
missionDefinition.Id,
166178
missionDefinition.InstallationCode
167179
);
168180
return;
@@ -173,11 +185,14 @@ public async Task AutomaticScheduleMissionRun(MissionDefinition missionDefinitio
173185
);
174186
if (robot == null)
175187
{
176-
_logger.LogWarning(
177-
"No robot found for mission definition {MissionDefinitionId} and inspection area {InspectionAreaId}.",
188+
string message =
189+
$"No robot found for mission definition {missionDefinition.Id} and inspection area {missionDefinition.InspectionArea.Id}.";
190+
ReportMessageToSignalR(
191+
message,
178192
missionDefinition.Id,
179-
missionDefinition.InspectionArea.Id
193+
missionDefinition.InstallationCode
180194
);
195+
181196
return;
182197
}
183198

@@ -203,6 +218,21 @@ await MissionSchedulingService.ScheduleMissionRunFromMissionDefinitionLastSucces
203218
return;
204219
}
205220

221+
private void ReportMessageToSignalR(
222+
string message,
223+
string missionDefinitionId,
224+
string installationCode
225+
)
226+
{
227+
_logger.LogError(message);
228+
229+
SignalRService.ReportAutoScheduleFailToSignalR(
230+
missionDefinitionId,
231+
message,
232+
installationCode
233+
);
234+
}
235+
206236
public Task StopAsync(CancellationToken stoppingToken)
207237
{
208238
_logger.LogInformation("Auto Scheduling Hosted Service is stopping.");

backend/api/Services/SignalRService.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ public interface ISignalRService
1515
public void ReportDockFailureToSignalR(Robot robot, string message);
1616
public void ReportDockSuccessToSignalR(Robot robot, string message);
1717
public void ReportGeneralFailToSignalR(Robot robot, string title, string message);
18+
public void ReportAutoScheduleFailToSignalR(
19+
string title,
20+
string message,
21+
string installationCode
22+
);
1823
}
1924

2025
public class SignalRService : ISignalRService
@@ -102,6 +107,25 @@ public void ReportDockSuccessToSignalR(Robot robot, string message)
102107
);
103108
}
104109

110+
public void ReportAutoScheduleFailToSignalR(
111+
string missionDefinitionId,
112+
string message,
113+
string installationCode
114+
)
115+
{
116+
_ = SendMessageAsync(
117+
"Alert",
118+
null,
119+
new AlertResponse(
120+
"autoSchedule",
121+
missionDefinitionId,
122+
message,
123+
installationCode,
124+
null
125+
)
126+
);
127+
}
128+
105129
public void ReportGeneralFailToSignalR(Robot robot, string title, string message)
106130
{
107131
_ = SendMessageAsync(

frontend/src/components/Alerts/FailedAlertContent.tsx

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,18 @@ import { Icons } from 'utils/icons'
55
import { tokens } from '@equinor/eds-tokens'
66
import { TextAlignedButton } from 'components/Styles/StyledComponents'
77
import { AlertListContents } from './AlertsListItem'
8+
import { AutoScheduleFailedMissionDict } from 'components/Contexts/AlertContext'
89

910
const StyledDiv = styled.div`
1011
align-items: center;
1112
`
1213
const StyledAlertTitle = styled.div`
1314
display: flex;
14-
gap: 0.3em;
15+
gap: 0.5em;
1516
align-items: flex-end;
1617
`
1718
const Indent = styled.div`
18-
padding: 0px 9px;
19+
padding: 5px 9px;
1920
`
2021

2122
export const FailedAlertContent = ({ title, message }: { title: string; message: string }) => {
@@ -49,3 +50,28 @@ export const FailedAlertListContent = ({ title, message }: { title: string; mess
4950
/>
5051
)
5152
}
53+
54+
export const FailedAutoMissionAlertContent = ({
55+
autoScheduleFailedMissionDict,
56+
}: {
57+
autoScheduleFailedMissionDict: AutoScheduleFailedMissionDict
58+
}) => {
59+
const { TranslateText } = useLanguageContext()
60+
const iconColor = tokens.colors.interactive.danger__resting.rgba
61+
62+
return (
63+
<StyledDiv>
64+
<StyledAlertTitle>
65+
<Icon name={Icons.Failed} style={{ color: iconColor }} />
66+
<Typography>{TranslateText('Failed to Auto Schedule Missions')}</Typography>
67+
</StyledAlertTitle>
68+
<Indent>
69+
{Object.keys(autoScheduleFailedMissionDict).map((missionId) => (
70+
<Typography variant="caption" key={missionId}>
71+
{autoScheduleFailedMissionDict[missionId]}
72+
</Typography>
73+
))}
74+
</Indent>
75+
</StyledDiv>
76+
)
77+
}

frontend/src/components/Contexts/AlertContext.tsx

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ import { Alert } from 'models/Alert'
99
import { useRobotContext } from './RobotContext'
1010
import { BlockedRobotAlertContent, BlockedRobotAlertListContent } from 'components/Alerts/BlockedRobotAlert'
1111
import { RobotStatus } from 'models/Robot'
12-
import { FailedAlertContent, FailedAlertListContent } from 'components/Alerts/FailedAlertContent'
12+
import {
13+
FailedAlertContent,
14+
FailedAlertListContent,
15+
FailedAutoMissionAlertContent,
16+
} from 'components/Alerts/FailedAlertContent'
1317
import { convertUTCDateToLocalDate } from 'utils/StringFormatting'
1418
import { AlertCategory } from 'components/Alerts/AlertsBanner'
1519
import { DockAlertContent, DockAlertListContent } from 'components/Alerts/DockAlert'
@@ -24,12 +28,14 @@ export enum AlertType {
2428
RequestDock,
2529
DismissDock,
2630
DockSuccess,
31+
AutoScheduleFail,
2732
}
2833

2934
const alertTypeEnumMap: { [key: string]: AlertType } = {
3035
DockFailure: AlertType.DockFail,
3136
generalFailure: AlertType.RequestFail,
3237
DockSuccess: AlertType.DockSuccess,
38+
autoSchedule: AlertType.AutoScheduleFail,
3339
}
3440

3541
type AlertDictionaryType = {
@@ -62,6 +68,10 @@ const defaultAlertInterface = {
6268
clearListAlert: () => {},
6369
}
6470

71+
export interface AutoScheduleFailedMissionDict {
72+
[key: string]: string
73+
}
74+
6575
const AlertContext = createContext<IAlertContext>(defaultAlertInterface)
6676

6777
export const AlertProvider: FC<Props> = ({ children }) => {
@@ -73,6 +83,9 @@ export const AlertProvider: FC<Props> = ({ children }) => {
7383
const { installationCode } = useInstallationContext()
7484
const { TranslateText } = useLanguageContext()
7585
const { enabledRobots } = useRobotContext()
86+
const [autoScheduleFailedMissionDict, setAutoScheduleFailedMissionDict] = useState<AutoScheduleFailedMissionDict>(
87+
JSON.parse(window.localStorage.getItem('autoScheduleFailedMissionDict') || '{}')
88+
)
7689

7790
const pageSize: number = 100
7891
// The default amount of minutes in the past for failed missions to generate an alert
@@ -96,6 +109,11 @@ export const AlertProvider: FC<Props> = ({ children }) => {
96109
if (source === AlertType.MissionFail)
97110
sessionStorage.setItem(dismissMissionFailTimeKey, JSON.stringify(Date.now()))
98111

112+
if (source === AlertType.AutoScheduleFail) {
113+
setAutoScheduleFailedMissionDict({})
114+
window.localStorage.setItem('autoScheduleFailedMissionDict', JSON.stringify({}))
115+
}
116+
99117
setAlerts((oldAlerts) => {
100118
const newAlerts = { ...oldAlerts }
101119
delete newAlerts[source]
@@ -122,6 +140,11 @@ export const AlertProvider: FC<Props> = ({ children }) => {
122140
if (source === AlertType.MissionFail)
123141
sessionStorage.setItem(dismissMissionFailTimeKey, JSON.stringify(Date.now()))
124142

143+
if (source === AlertType.AutoScheduleFail) {
144+
setAutoScheduleFailedMissionDict({})
145+
window.localStorage.setItem('autoScheduleFailedMissionDict', JSON.stringify({}))
146+
}
147+
125148
setListAlerts((oldListAlerts) => {
126149
const newListAlerts = { ...oldListAlerts }
127150
delete newListAlerts[source]
@@ -211,6 +234,19 @@ export const AlertProvider: FC<Props> = ({ children }) => {
211234

212235
if (backendAlert.robotId !== null && !enabledRobots.filter((r) => r.id === backendAlert.robotId)) return
213236

237+
if (alertType === AlertType.AutoScheduleFail) {
238+
const newAutoScheduleFailedMissionDict: AutoScheduleFailedMissionDict = {
239+
...autoScheduleFailedMissionDict,
240+
}
241+
newAutoScheduleFailedMissionDict[backendAlert.alertTitle] = backendAlert.alertMessage
242+
setAutoScheduleFailedMissionDict(newAutoScheduleFailedMissionDict)
243+
window.localStorage.setItem(
244+
'autoScheduleFailedMissionDict',
245+
JSON.stringify(newAutoScheduleFailedMissionDict)
246+
)
247+
return
248+
}
249+
214250
if (alertType === AlertType.DockSuccess) {
215251
setAlert(
216252
alertType,
@@ -238,7 +274,7 @@ export const AlertProvider: FC<Props> = ({ children }) => {
238274
}
239275
})
240276
}
241-
}, [registerEvent, connectionReady, installationCode, enabledRobots])
277+
}, [registerEvent, connectionReady, installationCode, enabledRobots, autoScheduleFailedMissionDict])
242278

243279
useEffect(() => {
244280
if (newFailedMissions.length > 0) {
@@ -285,6 +321,21 @@ export const AlertProvider: FC<Props> = ({ children }) => {
285321
setBlockedRobotNames(newBlockedRobotNames)
286322
}, [enabledRobots, installationCode])
287323

324+
useEffect(() => {
325+
if (Object.keys(autoScheduleFailedMissionDict).length > 0) {
326+
setListAlert(
327+
AlertType.AutoScheduleFail,
328+
<FailedAutoMissionAlertContent autoScheduleFailedMissionDict={autoScheduleFailedMissionDict} />,
329+
AlertCategory.ERROR
330+
)
331+
setAlert(
332+
AlertType.AutoScheduleFail,
333+
<FailedAutoMissionAlertContent autoScheduleFailedMissionDict={autoScheduleFailedMissionDict} />,
334+
AlertCategory.ERROR
335+
)
336+
}
337+
}, [connectionReady, autoScheduleFailedMissionDict])
338+
288339
return (
289340
<AlertContext.Provider
290341
value={{

frontend/src/language/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,5 +302,6 @@
302302
"Skip": "Skip",
303303
"No missions": "No missions",
304304
"Auto Scheduling": "Auto Scheduling",
305-
"These missions will be automatically scheduled at the specified time": "These missions will be automatically scheduled at the specified time"
305+
"These missions will be automatically scheduled at the specified time": "These missions will be automatically scheduled at the specified time",
306+
"Failed to Auto Schedule Missions": "Failed to Auto Schedule Missions"
306307
}

frontend/src/language/no.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,5 +302,6 @@
302302
"Skip": "Neste",
303303
"No missions": "Ingen oppdrag",
304304
"Auto Scheduling": "Automatisk kjøring",
305-
"These missions will be automatically scheduled at the specified time": "Disse oppdragene vil automatisk bli kjørt på de spesifiserte tidspunktene"
305+
"These missions will be automatically scheduled at the specified time": "Disse oppdragene vil automatisk bli kjørt på de spesifiserte tidspunktene",
306+
"Failed to Auto Schedule Missions": "Kunne ikke planlegge automatisk start av oppdrag"
306307
}

0 commit comments

Comments
 (0)