Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@ public class Messages
public static String acknowledgeFailed;
public static String addComponentFailed;
public static String disableAlarmFailed;
public static String disabled;
public static String disabledUntil;
public static String enableAlarmFailed;
public static String moveItemFailed;
public static String partlyDisabled;
public static String removeComponentFailed;
public static String renameItemFailed;
public static String timer;
public static String unacknowledgeFailed;

static
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,28 @@
*******************************************************************************/
package org.phoebus.applications.alarm.ui.tree;

import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.control.TreeCell;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;

import javafx.util.Pair;
import org.phoebus.applications.alarm.client.AlarmClientLeaf;
import org.phoebus.applications.alarm.client.AlarmClientNode;
import org.phoebus.applications.alarm.client.ClientState;
import org.phoebus.applications.alarm.model.AlarmTreeItem;
import org.phoebus.applications.alarm.model.SeverityLevel;
import org.phoebus.applications.alarm.ui.AlarmUI;
import org.phoebus.applications.alarm.ui.Messages;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;

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

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

public AlarmTreeViewCell() {
content.setAlignment(Pos.CENTER_LEFT);
disabledTimerIndicator.setTextFill(Color.GRAY);
}

@Override
protected void updateItem(final AlarmTreeItem<?> item, final boolean empty)
{
Expand All @@ -79,7 +91,7 @@ protected void updateItem(final AlarmTreeItem<?> item, final boolean empty)
final StringBuilder text = new StringBuilder();
text.append("PV: ").append(leaf.getName());

if (leaf.isEnabled() && !state.isDynamicallyDisabled())
if (!isLeafDisabled(leaf))
{ // Add alarm info
if (state.severity != SeverityLevel.OK)
{
Expand All @@ -94,23 +106,63 @@ protected void updateItem(final AlarmTreeItem<?> item, final boolean empty)
label.setTextFill(AlarmUI.getColor(state.severity));
label.setBackground(AlarmUI.getBackground(state.severity));
image.setImage(AlarmUI.getIcon(state.severity));
}
else
{
text.append(" (disabled)");

disabledTimerIndicator.setText("");
} else {
if (leaf.getEnabled().enabled_date != null) {
LocalDateTime enabledDate = leaf.getEnabled().enabled_date;
String enabledDateString = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(enabledDate);
disabledTimerIndicator.setText("(" + Messages.disabledUntil + " " + enabledDateString + ")");
} else {
disabledTimerIndicator.setText("(" + Messages.disabled + ")");
}

label.setTextFill(Color.GRAY);
label.setBackground(Background.EMPTY);
image.setImage(AlarmUI.disabled_icon);
}

label.setText(text.toString());
}
else
{
final AlarmClientNode node = (AlarmClientNode) item;
label.setText(item.getName());

Optional<Pair<LeavesDisabledStatus, Boolean>> maybeLeavesDisabledStatusBooleanPair = leavesDisabledStatus(node);
if (maybeLeavesDisabledStatusBooleanPair.isPresent() && !maybeLeavesDisabledStatusBooleanPair.get().getKey().equals(LeavesDisabledStatus.AllEnabled)) {
Pair<LeavesDisabledStatus, Boolean> leavesDisabledStatusBooleanPair = maybeLeavesDisabledStatusBooleanPair.get();

if (leavesDisabledStatusBooleanPair.getKey().equals(LeavesDisabledStatus.AllDisabled)) {
if (leavesDisabledStatusBooleanPair.getValue()) {
disabledTimerIndicator.setText("(" + Messages.disabled + "; " + Messages.timer + ")");
}
else {
disabledTimerIndicator.setText("(" + Messages.disabled + ")");
}
}
else if (leavesDisabledStatusBooleanPair.getKey().equals(LeavesDisabledStatus.SomeEnabledSomeDisabled)) {
if (leavesDisabledStatusBooleanPair.getValue()) {
disabledTimerIndicator.setText("(" + Messages.partlyDisabled + "; " + Messages.timer + ")");
}
else {
disabledTimerIndicator.setText("(" + Messages.partlyDisabled + ")");
}
}
}
else {
disabledTimerIndicator.setText("");
}

String labelText = item.getName();
label.setText(labelText);

severity = node.getState().severity;
label.setTextFill(AlarmUI.getColor(severity));
if (maybeLeavesDisabledStatusBooleanPair.isPresent() && maybeLeavesDisabledStatusBooleanPair.get().getKey().equals(LeavesDisabledStatus.AllDisabled)) {
label.setTextFill(Color.GRAY);
}
else {
label.setTextFill(AlarmUI.getColor(severity));
}
label.setBackground(AlarmUI.getBackground(severity));
image.setImage(AlarmUI.getIcon(severity));
}
Expand All @@ -119,4 +171,65 @@ protected void updateItem(final AlarmTreeItem<?> item, final boolean empty)
setGraphic(content);
}
}

private boolean isLeafDisabled(AlarmClientLeaf alarmClientLeaf) {
return !alarmClientLeaf.isEnabled() || alarmClientLeaf.getState().isDynamicallyDisabled();
}

private enum LeavesDisabledStatus {
AllEnabled,
SomeEnabledSomeDisabled,
AllDisabled,
}

// leavesDisabledStatus() optionally returns a pair.
//
// If a pair is _not_ returned, it means that there exist no leaves
// in 'alarmClientNode', and the disabled status is undefined.
//
// When a pair _is_ returned, the first component describes
// whether all leaves are disabled, all leaves are enabled, or whether
// some leaves are enabled and some are disabled, and the second component
// indicates whether one or more disabled leaves have a timer associated
// with them ('true'), at the end of which they will automatically become
// enabled again. When the second component is 'false' there is no
// associated timer.
private Optional<Pair<LeavesDisabledStatus, Boolean>> leavesDisabledStatus(AlarmClientNode alarmClientNode) {
List<Pair<LeavesDisabledStatus, Boolean>> leavesDisabledStatusList = new LinkedList<>();
for (var child : alarmClientNode.getChildren()) {
if (child instanceof AlarmClientLeaf alarmClientLeaf) {

if (isLeafDisabled(alarmClientLeaf)) {
boolean timer = alarmClientLeaf.getEnabled().enabled_date != null;
leavesDisabledStatusList.add(new Pair<>(LeavesDisabledStatus.AllDisabled, timer));
}
else {
leavesDisabledStatusList.add(new Pair<>(LeavesDisabledStatus.AllEnabled, false));
}
}
else if (child instanceof AlarmClientNode alarmClientNode1 && !alarmClientNode1.getChildren().isEmpty()) {
if (leavesDisabledStatus(alarmClientNode1).isPresent()) {
leavesDisabledStatusList.add(leavesDisabledStatus(alarmClientNode1).get());
}
// If leavesDisabledStatus(alarmClientNode1).isPresent() evaluates to false, there are no leaves and therefore no result.
}
else if (child instanceof AlarmClientNode alarmClientNode1 && alarmClientNode1.getChildren().isEmpty()) {
// Don't add any LeavesDisabledStatus, since there are no leaves
}
else {
throw new RuntimeException("Missing case: " + child.getClass().getName());
}
}

Optional<Pair<LeavesDisabledStatus, Boolean>> leavesDisabledStatus = leavesDisabledStatusList.stream().reduce((status1, status2) -> {
if (status1.getKey().equals(status2.getKey())) {
return new Pair<>(status1.getKey(), status1.getValue() || status2.getValue());
}
else {
return new Pair<>(LeavesDisabledStatus.SomeEnabledSomeDisabled, status1.getValue() || status2.getValue());
}
});

return leavesDisabledStatus;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ error=Error
acknowledgeFailed=Failed to acknowledge alarm(s)
addComponentFailed=Failed to add component
disableAlarmFailed=Failed to disable alarm
disabled=Disabled
disabledUntil=Disabled until
enableAlarmFailed=Failed to enable alarm
moveItemFailed=Failed to move item
partlyDisabled=Partly disabled
removeComponentFailed=Failed to remove component
renameItemFailed=Failed to rename item
unacknowledgeFailed=Failed to unacknowledge alarm(s)
timer=Timer
unacknowledgeFailed=Failed to unacknowledge alarm(s)