Skip to content

Commit d54610d

Browse files
authored
Merge pull request #3310 from ControlSystemStudio/CSSTUDIO-2964
CSSTUDIO-2964 Alarm Tree application: Add indicators for the existence of disabled leaves
2 parents 4c506f0 + f01c3b4 commit d54610d

File tree

3 files changed

+133
-12
lines changed

3 files changed

+133
-12
lines changed

app/alarm/ui/src/main/java/org/phoebus/applications/alarm/ui/Messages.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@ public class Messages
1616
public static String acknowledgeFailed;
1717
public static String addComponentFailed;
1818
public static String disableAlarmFailed;
19+
public static String disabled;
20+
public static String disabledUntil;
1921
public static String enableAlarmFailed;
2022
public static String moveItemFailed;
23+
public static String partlyDisabled;
2124
public static String removeComponentFailed;
2225
public static String renameItemFailed;
26+
public static String timer;
2327
public static String unacknowledgeFailed;
2428

2529
static

app/alarm/ui/src/main/java/org/phoebus/applications/alarm/ui/tree/AlarmTreeViewCell.java

Lines changed: 124 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,28 @@
77
*******************************************************************************/
88
package org.phoebus.applications.alarm.ui.tree;
99

10-
import javafx.geometry.Insets;
10+
import javafx.geometry.Pos;
1111
import javafx.scene.control.Label;
1212
import javafx.scene.control.TreeCell;
1313
import javafx.scene.image.ImageView;
1414
import javafx.scene.layout.Background;
15-
import javafx.scene.layout.BackgroundFill;
16-
import javafx.scene.layout.CornerRadii;
1715
import javafx.scene.layout.HBox;
1816
import javafx.scene.paint.Color;
1917

18+
import javafx.util.Pair;
2019
import org.phoebus.applications.alarm.client.AlarmClientLeaf;
2120
import org.phoebus.applications.alarm.client.AlarmClientNode;
2221
import org.phoebus.applications.alarm.client.ClientState;
2322
import org.phoebus.applications.alarm.model.AlarmTreeItem;
2423
import org.phoebus.applications.alarm.model.SeverityLevel;
2524
import org.phoebus.applications.alarm.ui.AlarmUI;
25+
import org.phoebus.applications.alarm.ui.Messages;
26+
27+
import java.time.LocalDateTime;
28+
import java.time.format.DateTimeFormatter;
29+
import java.util.LinkedList;
30+
import java.util.List;
31+
import java.util.Optional;
2632

2733
/** TreeCell for AlarmTreeItem
2834
* @author Kay Kasemir
@@ -40,7 +46,8 @@ class AlarmTreeViewCell extends TreeCell<AlarmTreeItem<?>>
4046
// So we add our own "graphics" to hold an icon and text
4147
private final Label label = new Label();
4248
private final ImageView image = new ImageView();
43-
private final HBox content = new HBox(image, label);
49+
private final Label disabledTimerIndicator = new Label("");
50+
private final HBox content = new HBox(image, label, disabledTimerIndicator);
4451

4552
// TreeCell optimizes redraws by suppressing updates
4653
// when old and new values match.
@@ -61,6 +68,11 @@ protected boolean isItemChanged(final AlarmTreeItem<?> before, final AlarmTreeIt
6168
return true;
6269
}
6370

71+
public AlarmTreeViewCell() {
72+
content.setAlignment(Pos.CENTER_LEFT);
73+
disabledTimerIndicator.setTextFill(Color.GRAY);
74+
}
75+
6476
@Override
6577
protected void updateItem(final AlarmTreeItem<?> item, final boolean empty)
6678
{
@@ -79,7 +91,7 @@ protected void updateItem(final AlarmTreeItem<?> item, final boolean empty)
7991
final StringBuilder text = new StringBuilder();
8092
text.append("PV: ").append(leaf.getName());
8193

82-
if (leaf.isEnabled() && !state.isDynamicallyDisabled())
94+
if (!isLeafDisabled(leaf))
8395
{ // Add alarm info
8496
if (state.severity != SeverityLevel.OK)
8597
{
@@ -94,23 +106,63 @@ protected void updateItem(final AlarmTreeItem<?> item, final boolean empty)
94106
label.setTextFill(AlarmUI.getColor(state.severity));
95107
label.setBackground(AlarmUI.getBackground(state.severity));
96108
image.setImage(AlarmUI.getIcon(state.severity));
97-
}
98-
else
99-
{
100-
text.append(" (disabled)");
109+
110+
disabledTimerIndicator.setText("");
111+
} else {
112+
if (leaf.getEnabled().enabled_date != null) {
113+
LocalDateTime enabledDate = leaf.getEnabled().enabled_date;
114+
String enabledDateString = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(enabledDate);
115+
disabledTimerIndicator.setText("(" + Messages.disabledUntil + " " + enabledDateString + ")");
116+
} else {
117+
disabledTimerIndicator.setText("(" + Messages.disabled + ")");
118+
}
119+
101120
label.setTextFill(Color.GRAY);
102121
label.setBackground(Background.EMPTY);
103122
image.setImage(AlarmUI.disabled_icon);
104123
}
124+
105125
label.setText(text.toString());
106126
}
107127
else
108128
{
109129
final AlarmClientNode node = (AlarmClientNode) item;
110-
label.setText(item.getName());
130+
131+
Optional<Pair<LeavesDisabledStatus, Boolean>> maybeLeavesDisabledStatusBooleanPair = leavesDisabledStatus(node);
132+
if (maybeLeavesDisabledStatusBooleanPair.isPresent() && !maybeLeavesDisabledStatusBooleanPair.get().getKey().equals(LeavesDisabledStatus.AllEnabled)) {
133+
Pair<LeavesDisabledStatus, Boolean> leavesDisabledStatusBooleanPair = maybeLeavesDisabledStatusBooleanPair.get();
134+
135+
if (leavesDisabledStatusBooleanPair.getKey().equals(LeavesDisabledStatus.AllDisabled)) {
136+
if (leavesDisabledStatusBooleanPair.getValue()) {
137+
disabledTimerIndicator.setText("(" + Messages.disabled + "; " + Messages.timer + ")");
138+
}
139+
else {
140+
disabledTimerIndicator.setText("(" + Messages.disabled + ")");
141+
}
142+
}
143+
else if (leavesDisabledStatusBooleanPair.getKey().equals(LeavesDisabledStatus.SomeEnabledSomeDisabled)) {
144+
if (leavesDisabledStatusBooleanPair.getValue()) {
145+
disabledTimerIndicator.setText("(" + Messages.partlyDisabled + "; " + Messages.timer + ")");
146+
}
147+
else {
148+
disabledTimerIndicator.setText("(" + Messages.partlyDisabled + ")");
149+
}
150+
}
151+
}
152+
else {
153+
disabledTimerIndicator.setText("");
154+
}
155+
156+
String labelText = item.getName();
157+
label.setText(labelText);
111158

112159
severity = node.getState().severity;
113-
label.setTextFill(AlarmUI.getColor(severity));
160+
if (maybeLeavesDisabledStatusBooleanPair.isPresent() && maybeLeavesDisabledStatusBooleanPair.get().getKey().equals(LeavesDisabledStatus.AllDisabled)) {
161+
label.setTextFill(Color.GRAY);
162+
}
163+
else {
164+
label.setTextFill(AlarmUI.getColor(severity));
165+
}
114166
label.setBackground(AlarmUI.getBackground(severity));
115167
image.setImage(AlarmUI.getIcon(severity));
116168
}
@@ -119,4 +171,65 @@ protected void updateItem(final AlarmTreeItem<?> item, final boolean empty)
119171
setGraphic(content);
120172
}
121173
}
174+
175+
private boolean isLeafDisabled(AlarmClientLeaf alarmClientLeaf) {
176+
return !alarmClientLeaf.isEnabled() || alarmClientLeaf.getState().isDynamicallyDisabled();
177+
}
178+
179+
private enum LeavesDisabledStatus {
180+
AllEnabled,
181+
SomeEnabledSomeDisabled,
182+
AllDisabled,
183+
}
184+
185+
// leavesDisabledStatus() optionally returns a pair.
186+
//
187+
// If a pair is _not_ returned, it means that there exist no leaves
188+
// in 'alarmClientNode', and the disabled status is undefined.
189+
//
190+
// When a pair _is_ returned, the first component describes
191+
// whether all leaves are disabled, all leaves are enabled, or whether
192+
// some leaves are enabled and some are disabled, and the second component
193+
// indicates whether one or more disabled leaves have a timer associated
194+
// with them ('true'), at the end of which they will automatically become
195+
// enabled again. When the second component is 'false' there is no
196+
// associated timer.
197+
private Optional<Pair<LeavesDisabledStatus, Boolean>> leavesDisabledStatus(AlarmClientNode alarmClientNode) {
198+
List<Pair<LeavesDisabledStatus, Boolean>> leavesDisabledStatusList = new LinkedList<>();
199+
for (var child : alarmClientNode.getChildren()) {
200+
if (child instanceof AlarmClientLeaf alarmClientLeaf) {
201+
202+
if (isLeafDisabled(alarmClientLeaf)) {
203+
boolean timer = alarmClientLeaf.getEnabled().enabled_date != null;
204+
leavesDisabledStatusList.add(new Pair<>(LeavesDisabledStatus.AllDisabled, timer));
205+
}
206+
else {
207+
leavesDisabledStatusList.add(new Pair<>(LeavesDisabledStatus.AllEnabled, false));
208+
}
209+
}
210+
else if (child instanceof AlarmClientNode alarmClientNode1 && !alarmClientNode1.getChildren().isEmpty()) {
211+
if (leavesDisabledStatus(alarmClientNode1).isPresent()) {
212+
leavesDisabledStatusList.add(leavesDisabledStatus(alarmClientNode1).get());
213+
}
214+
// If leavesDisabledStatus(alarmClientNode1).isPresent() evaluates to false, there are no leaves and therefore no result.
215+
}
216+
else if (child instanceof AlarmClientNode alarmClientNode1 && alarmClientNode1.getChildren().isEmpty()) {
217+
// Don't add any LeavesDisabledStatus, since there are no leaves
218+
}
219+
else {
220+
throw new RuntimeException("Missing case: " + child.getClass().getName());
221+
}
222+
}
223+
224+
Optional<Pair<LeavesDisabledStatus, Boolean>> leavesDisabledStatus = leavesDisabledStatusList.stream().reduce((status1, status2) -> {
225+
if (status1.getKey().equals(status2.getKey())) {
226+
return new Pair<>(status1.getKey(), status1.getValue() || status2.getValue());
227+
}
228+
else {
229+
return new Pair<>(LeavesDisabledStatus.SomeEnabledSomeDisabled, status1.getValue() || status2.getValue());
230+
}
231+
});
232+
233+
return leavesDisabledStatus;
234+
}
122235
}

app/alarm/ui/src/main/resources/org/phoebus/applications/alarm/ui/messages.properties

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@ error=Error
2121
acknowledgeFailed=Failed to acknowledge alarm(s)
2222
addComponentFailed=Failed to add component
2323
disableAlarmFailed=Failed to disable alarm
24+
disabled=Disabled
25+
disabledUntil=Disabled until
2426
enableAlarmFailed=Failed to enable alarm
2527
moveItemFailed=Failed to move item
28+
partlyDisabled=Partly disabled
2629
removeComponentFailed=Failed to remove component
2730
renameItemFailed=Failed to rename item
28-
unacknowledgeFailed=Failed to unacknowledge alarm(s)
31+
timer=Timer
32+
unacknowledgeFailed=Failed to unacknowledge alarm(s)

0 commit comments

Comments
 (0)