Skip to content

Commit 2494e2f

Browse files
Marcus banes line ahead behind (#8149)
* Cherry picked only the ahead/behind code from the giant PR * Add situational awareness configuration * Improve settings label * Adds new interceptor for plan execution monitoring object to store execution monitoring information * Add support for saving plan executio monitoring in the inspector. * #8091 CSS and HTML for MB line and ahead/behind in Inspector - CSS tweaks to align MB line properly. - CSS and HTML for Plan Config tab "Execution Monitoring" section. * #8091 Changed label for Plan Execution Inspector section * Propagate new plan execution monitoring settings to the plan and gantt chart views * move if statement to outerdiv for units * Remove ahead behind code from configuration * Fix inspector view keys to be unique Use props for plan execution monitoring status for reuse * Address review comments: re-use existing constants and refactor code to be more readable * Add variables accidentally removed during conflict resolution * Fixed imports. * remove focused tests --------- Co-authored-by: Charles Hacskaylo <[email protected]>
1 parent be54fed commit 2494e2f

18 files changed

+875
-33
lines changed

src/api/objects/ObjectAPI.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import { EventEmitter } from 'eventemitter3';
2424
import { identifierEquals, makeKeyString, parseKeyString, refresh } from 'objectUtils';
2525

26+
import { PLAN_EXECUTION_MONITORING_KEY } from '../../plugins/planExecutionMonitoring/planExecutionMonitoringIdentifier.js';
2627
import ConflictError from './ConflictError.js';
2728
import InMemorySearchProvider from './InMemorySearchProvider.js';
2829
import InterceptorRegistry from './InterceptorRegistry.js';
@@ -100,7 +101,8 @@ export default class ObjectAPI {
100101
'restricted-notebook',
101102
'plan',
102103
'annotation',
103-
'activity-states'
104+
'activity-states',
105+
PLAN_EXECUTION_MONITORING_KEY
104106
];
105107

106108
this.errors = {

src/plugins/plan/components/PlanView.vue

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
:bounds="viewBounds"
3131
:time-system="timeSystem"
3232
:content-height="height"
33+
:ahead-behind="aheadBehind"
3334
:rendering-engine="renderingEngine"
3435
/>
3536
</template>
@@ -59,6 +60,7 @@ import { scaleLinear, scaleUtc } from 'd3-scale';
5960
import SwimLane from '@/ui/components/swim-lane/SwimLane.vue';
6061
6162
import TimelineAxis from '../../../ui/components/TimeSystemAxis.vue';
63+
import { PLAN_EXECUTION_MONITORING_KEY } from '../../planExecutionMonitoring/planExecutionMonitoringIdentifier.js';
6264
import PlanViewConfiguration from '../PlanViewConfiguration.js';
6365
import { getContrastingColor, getValidatedData, getValidatedGroups } from '../util.js';
6466
import ActivityTimeline from './ActivityTimeline.vue';
@@ -73,6 +75,10 @@ const ROW_HEIGHT = 22;
7375
const MAX_TEXT_WIDTH = 300;
7476
const MIN_ACTIVITY_WIDTH = 2;
7577
const DEFAULT_COLOR = '#999';
78+
const DEFAULT_AHEAD_BEHIND_STATUS = {
79+
duration: 0,
80+
status: ''
81+
};
7682
7783
export default {
7884
components: {
@@ -107,7 +113,8 @@ export default {
107113
swimlaneVisibility: {},
108114
clipActivityNames: false,
109115
height: 0,
110-
rowHeight: ROW_HEIGHT
116+
rowHeight: ROW_HEIGHT,
117+
aheadBehind: DEFAULT_AHEAD_BEHIND_STATUS
111118
};
112119
},
113120
computed: {
@@ -133,6 +140,7 @@ export default {
133140
this.isNested = this.options.isChildObject;
134141
this.swimlaneVisibility = this.configuration.swimlaneVisibility;
135142
this.clipActivityNames = this.configuration.clipActivityNames;
143+
136144
// This view is used for both gantt-chart and plan domain objects
137145
if (this.domainObject.type === 'plan') {
138146
this.setupPlan(this.domainObject);
@@ -170,10 +178,15 @@ export default {
170178
this.stopObservingPlanChanges();
171179
}
172180
this.planViewConfiguration.destroy();
181+
182+
if (this.stopObservingPlanExecutionMonitoringStatusObject) {
183+
this.stopObservingPlanExecutionMonitoringStatusObject();
184+
}
173185
},
174186
methods: {
175187
setupPlan(domainObject) {
176188
this.planObject = domainObject;
189+
// Plan object configuration
177190
this.applyChangesForPlanObject(domainObject);
178191
this.stopObservingPlanChanges = this.openmct.objects.observe(
179192
domainObject,
@@ -184,6 +197,31 @@ export default {
184197
domainObject.identifier,
185198
this.setPlanStatus
186199
);
200+
// plan execution monitoring
201+
this.getPlanExecutionMonitoringStatus();
202+
},
203+
async getPlanExecutionMonitoringStatus() {
204+
this.planExecutionMonitoringStatusObject = await this.openmct.objects.get(
205+
PLAN_EXECUTION_MONITORING_KEY
206+
);
207+
this.setPlanExecutionMonitoringStatus(this.planExecutionMonitoringStatusObject);
208+
this.stopObservingPlanExecutionMonitoringStatusObject = this.openmct.objects.observe(
209+
this.planExecutionMonitoringStatusObject,
210+
'*',
211+
this.setPlanExecutionMonitoringStatus
212+
);
213+
},
214+
setPlanExecutionMonitoringStatus(newStatusObject) {
215+
const planIdentifier = this.openmct.objects.makeKeyString(this.planObject.identifier);
216+
if (
217+
newStatusObject &&
218+
newStatusObject.execution_monitoring &&
219+
newStatusObject.execution_monitoring[planIdentifier]
220+
) {
221+
this.aheadBehind = newStatusObject.execution_monitoring[planIdentifier];
222+
} else {
223+
this.aheadBehind = DEFAULT_AHEAD_BEHIND_STATUS;
224+
}
187225
},
188226
setPlanData(domainObject) {
189227
this.planData = getValidatedData(domainObject);

src/plugins/plan/inspector/GanttChartInspectorViewProvider.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export default function GanttChartInspectorViewProvider(openmct) {
5050
},
5151
provide: {
5252
openmct,
53-
selection: selection
53+
domainObject: selection[0][0].context.item
5454
},
5555
template: '<plan-view-configuration></plan-view-configuration>'
5656
},
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*****************************************************************************
2+
* Open MCT, Copyright (c) 2014-2024, United States Government
3+
* as represented by the Administrator of the National Aeronautics and Space
4+
* Administration. All rights reserved.
5+
*
6+
* Open MCT is licensed under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
* http://www.apache.org/licenses/LICENSE-2.0.
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
* License for the specific language governing permissions and limitations
15+
* under the License.
16+
*
17+
* Open MCT includes source code licensed under additional open source
18+
* licenses. See the Open Source Licenses file (LICENSES.md) included with
19+
* this source code distribution or the Licensing information page available
20+
* at runtime from the About dialog for additional information.
21+
*****************************************************************************/
22+
23+
import mount from 'utils/mount';
24+
25+
import PlanExecutionMonitoringView from './components/PlanExecutionMonitoringView.vue';
26+
27+
export default function PlanInspectorViewProvider(openmct) {
28+
return {
29+
key: 'plan-status-inspector',
30+
name: 'Config',
31+
canView: function (selection) {
32+
if (selection.length === 0 || selection[0].length === 0) {
33+
return false;
34+
}
35+
36+
const domainObject = selection[0][0].context.item;
37+
38+
return domainObject?.type === 'plan';
39+
},
40+
view: function (selection) {
41+
let _destroy = null;
42+
43+
return {
44+
show: function (element) {
45+
const { destroy } = mount(
46+
{
47+
el: element,
48+
components: {
49+
PlanExecutionMonitoringView
50+
},
51+
provide: {
52+
openmct
53+
},
54+
data() {
55+
return {
56+
planObject: selection[0][0].context.item
57+
};
58+
},
59+
template:
60+
'<plan-execution-monitoring-view :plan-object="planObject"></plan-execution-monitoring-view>'
61+
},
62+
{
63+
app: openmct.app,
64+
element
65+
}
66+
);
67+
_destroy = destroy;
68+
},
69+
priority: function () {
70+
return openmct.editor.isEditing() ? openmct.priority.HIGH : openmct.priority.DEFAULT;
71+
},
72+
destroy: function () {
73+
if (_destroy) {
74+
_destroy();
75+
}
76+
}
77+
};
78+
}
79+
};
80+
}
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
<!--
2+
Open MCT, Copyright (c) 2014-2024, United States Government
3+
as represented by the Administrator of the National Aeronautics and Space
4+
Administration. All rights reserved.
5+
6+
Open MCT is licensed under the Apache License, Version 2.0 (the
7+
"License"); you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
http://www.apache.org/licenses/LICENSE-2.0.
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
License for the specific language governing permissions and limitations
15+
under the License.
16+
17+
Open MCT includes source code licensed under additional open source
18+
licenses. See the Open Source Licenses file (LICENSES.md) included with
19+
this source code distribution or the Licensing information page available
20+
at runtime from the About dialog for additional information.
21+
-->
22+
23+
<template>
24+
<div class="c-inspector__properties c-inspect-properties">
25+
<div class="u-contents">
26+
<div class="c-inspect-properties__header">Plan Execution Status</div>
27+
<div class="c-inspect-properties__row">
28+
<div
29+
class="c-inspect-properties__label"
30+
aria-label="Plan Execution Monitoring Status Label"
31+
>
32+
<select
33+
v-model="planExecutionMonitoringStatus"
34+
name="executionMonitoringStatus"
35+
aria-label="Plan Execution Monitoring Status"
36+
@change="changePlanExecutionMonitoring"
37+
>
38+
<option
39+
v-for="status in executionMonitorStates"
40+
:key="status.key"
41+
:value="status.key"
42+
:aria-selected="planExecutionMonitoringStatus === status.key"
43+
>
44+
{{ status.label }}
45+
</option>
46+
</select>
47+
</div>
48+
<div
49+
v-if="planExecutionMonitoringStatus !== executionMonitorStates[0].key"
50+
class="c-inspect-properties__value"
51+
>
52+
<input
53+
id="plan_execution_monitoring_duration"
54+
v-model="duration"
55+
aria-label="Plan Execution Monitoring Duration"
56+
class="c-input--sm"
57+
type="number"
58+
@change="toggleDuration"
59+
/>
60+
<span class="hint">minutes</span>
61+
</div>
62+
</div>
63+
</div>
64+
</div>
65+
</template>
66+
67+
<script>
68+
import { PLAN_EXECUTION_MONITORING_KEY } from '../../../planExecutionMonitoring/planExecutionMonitoringIdentifier.js';
69+
70+
const executionMonitorStates = [
71+
{
72+
key: 'nominal',
73+
label: 'Nominal'
74+
},
75+
{
76+
key: 'behind',
77+
label: 'Behind by'
78+
},
79+
{
80+
key: 'ahead',
81+
label: 'Ahead by'
82+
}
83+
];
84+
85+
export default {
86+
inject: ['openmct'],
87+
props: {
88+
planObject: {
89+
type: Object,
90+
required: true
91+
}
92+
},
93+
data() {
94+
return {
95+
executionMonitorStates: executionMonitorStates,
96+
planExecutionMonitoringStatus: executionMonitorStates[0].key,
97+
duration: 0
98+
};
99+
},
100+
watch: {
101+
planObject() {
102+
this.getStatus();
103+
}
104+
},
105+
mounted() {
106+
this.getStatus();
107+
},
108+
beforeUnmount() {
109+
if (this.stopObservingPlanExecutionMonitoringStatusObject) {
110+
this.stopObservingPlanExecutionMonitoringStatusObject();
111+
}
112+
},
113+
methods: {
114+
getStatus() {
115+
this.planIdentifier = this.openmct.objects.makeKeyString(this.planObject.identifier);
116+
this.getPlanExecutionMonitoringStatus();
117+
},
118+
toggleDuration() {
119+
if (this.duration === undefined || this.duration < 0) {
120+
return;
121+
}
122+
if (this.duration === 0) {
123+
this.planExecutionMonitoringStatus = executionMonitorStates[0].key;
124+
}
125+
this.persistExecutionMonitoringStatus();
126+
},
127+
changePlanExecutionMonitoring() {
128+
if (this.planExecutionMonitoringStatus === '') {
129+
return;
130+
}
131+
this.persistExecutionMonitoringStatus();
132+
},
133+
setPlanExecutionMonitoring(status, duration) {
134+
let statusKeyIndex = executionMonitorStates.findIndex((state) => state.key === status);
135+
if (statusKeyIndex < 0) {
136+
statusKeyIndex = 0;
137+
}
138+
this.planExecutionMonitoringStatus = this.executionMonitorStates[statusKeyIndex].key;
139+
this.duration = duration ?? 0;
140+
},
141+
async getPlanExecutionMonitoringStatus() {
142+
this.planExecutionMonitoringStatusObject = await this.openmct.objects.get(
143+
PLAN_EXECUTION_MONITORING_KEY
144+
);
145+
this.setPlanExecutionMonitoringStatus(this.planExecutionMonitoringStatusObject);
146+
this.stopObservingPlanExecutionMonitoringStatusObject = this.openmct.objects.observe(
147+
this.planExecutionMonitoringStatusObject,
148+
'*',
149+
this.setPlanExecutionMonitoringStatus
150+
);
151+
},
152+
setPlanExecutionMonitoringStatus(newStatusObject) {
153+
const statusObj = newStatusObject?.execution_monitoring?.[this.planIdentifier];
154+
if (!statusObj) {
155+
this.setPlanExecutionMonitoring();
156+
return;
157+
}
158+
const { status, duration } = statusObj;
159+
this.setPlanExecutionMonitoring(status, duration);
160+
},
161+
persistExecutionMonitoringStatus() {
162+
const executionMonitoringStatus = {
163+
duration: this.duration,
164+
status: this.planExecutionMonitoringStatus
165+
};
166+
const executionMonitoringPath = `execution_monitoring.${this.planIdentifier}`;
167+
this.openmct.objects.mutate(
168+
this.planExecutionMonitoringStatusObject,
169+
executionMonitoringPath,
170+
executionMonitoringStatus
171+
);
172+
}
173+
}
174+
};
175+
</script>

0 commit comments

Comments
 (0)