Skip to content

Commit a570d69

Browse files
authored
Merge pull request #3669 from ControlSystemStudio/CSSTUDIO-3585
Save&Restore comparison of array data
2 parents 3faf851 + d2fba8b commit a570d69

File tree

19 files changed

+837
-18
lines changed

19 files changed

+837
-18
lines changed
17.5 KB
Loading
49 KB
Loading
43.6 KB
Loading

app/save-and-restore/app/doc/index.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,29 @@ are shown by default. The left-most columns in the toolbar can be used to show/h
303303
.. image:: images/toggle-readback.png
304304
:width: 80%
305305

306+
While comparison of scalar values in the snapshot view is straight-forward, array (or table) type data is difficult
307+
to compare from the single table cells. User may instead click on the highlighted ":math:`{\Delta}` Live" cell to launch a dialog
308+
showing stored, live and :math:`{\Delta}` for the selected PV:
309+
310+
.. image:: images/snapshot-view-with-delta.png
311+
312+
User clicks "Click to compare":
313+
314+
.. image:: images/compare-arrays.png
315+
316+
The threshold settings works in the same manner is in the snapshot view and operates on each element (row) in the
317+
table view.
318+
319+
In case the stored and live value of the array/table data are of different dimensions, cells where no value is available
320+
will be rendered as "---". Moreover, since in these cases an absolute delta cannot be computed, the delta column will also show
321+
"---".
322+
323+
User may click the table header of the delta column to sort on the delta value to quickly find rows where either the
324+
stored or live value is not defined (due to difference in dimension). For such rows the absolute delta will be treated
325+
as infinite, which impacts ordering on the delta column:
326+
327+
.. image:: images/compare-arrays-infinite-delta.png
328+
306329
Restoring A Snapshot
307330
--------------------
308331

app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/Messages.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,16 @@ public class Messages {
3232
public static String buttonSearch;
3333
public static String cannotCompareHeader;
3434
public static String cannotCompareTitle;
35+
public static String clickToCompare;
3536
public static String closeConfigurationWarning;
3637
public static String closeCompositeSnapshotWarning;
3738
public static String closeSnapshotWarning;
3839
public static String closeCompositeSnapshotTabPrompt;
3940
public static String closeConfigurationTabPrompt;
4041
public static String closeSnapshotTabPrompt;
4142
public static String compositeSnapshotConsistencyCheckFailed;
43+
public static String comparisonDialogLunchError;
44+
public static String comparisonDialogTitle;
4245
public static String contextMenuAddTag;
4346
@Deprecated
4447
public static String contextMenuAddTagWithComment;
@@ -107,6 +110,7 @@ public class Messages {
107110
public static String importSnapshotLabel;
108111
public static String includeThisPV;
109112
public static String inverseSelection;
113+
public static String live;
110114
public static String liveReadbackVsSetpoint;
111115
public static String liveSetpoint;
112116
public static String login;
@@ -159,6 +163,7 @@ public class Messages {
159163
public static String snapshotFromPvs;
160164
public static String status;
161165
public static String storedReadbackValue;
166+
public static String stored;
162167
public static String storedValues;
163168
public static String tableColumnDeltaValue;
164169
public static String tagAddFailed;

app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/VTypePair.java

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@
1717
*/
1818
package org.phoebus.applications.saveandrestore.ui;
1919

20+
import org.epics.vtype.VNumber;
21+
import org.epics.vtype.VString;
2022
import org.epics.vtype.VType;
23+
import org.phoebus.core.vtypes.VTypeHelper;
2124
import org.phoebus.saveandrestore.util.Threshold;
25+
import org.phoebus.saveandrestore.util.VNoData;
2226

2327
import java.util.Optional;
2428

@@ -35,7 +39,8 @@ public class VTypePair {
3539
public final Optional<Threshold<?>> threshold;
3640

3741
/**
38-
* Constructs a new pair.
42+
* Constructs a new pair. In the context of save-and-restore snapshots, the {@link #value} field
43+
* is used to hold a stored value, while {@link #base} holds the live PV value.
3944
*
4045
* @param base the base value
4146
* @param value the value that can be compared to base
@@ -47,6 +52,39 @@ public VTypePair(VType base, VType value, Optional<Threshold<?>> threshold) {
4752
this.threshold = threshold;
4853
}
4954

55+
/**
56+
* Computes absolute delta for the delta between {@link #base} and {@link #value}. When applied to
57+
* {@link VString} types, {@link String#compareTo(String)} is used for comparison, but then converted to
58+
* an absolute value.
59+
*
60+
* <p>
61+
* Main use case for this is ordering on delta. Absolute delta may be more useful as otherwise zero
62+
* deltas would be found between positive and negative deltas.
63+
* </p>
64+
* <p>
65+
* If {@link #base} or {@link #value} are <code>null</code> or {@link VNoData#INSTANCE}, then
66+
* the delta cannot be computed as a number. In this case {@link Double#MAX_VALUE} is returned
67+
* to indicate an "infinite delta".
68+
* </p>
69+
* @return Absolute delta between {@link #base} and {@link #value}.
70+
*/
71+
public double getAbsoluteDelta(){
72+
if(base.equals(VNoData.INSTANCE) ||
73+
value.equals(VNoData.INSTANCE) ||
74+
base == null ||
75+
value == null){
76+
return Double.MAX_VALUE;
77+
}
78+
if(base instanceof VNumber){
79+
return Math.abs(((VNumber)base).getValue().doubleValue() -
80+
((VNumber)value).getValue().doubleValue());
81+
}
82+
else if(base instanceof VString){
83+
return Math.abs(((VString)base).getValue().compareTo(((VString)value).getValue()));
84+
}
85+
else return 0.0;
86+
}
87+
5088
/*
5189
* (non-Javadoc)
5290
*

app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotController.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@ protected void updateItem(TableEntry item, boolean empty) {
645645
});
646646

647647
showDeltaPercentage.addListener((ob, o, n) -> deltaColumn.setCellFactory(e -> {
648-
VDeltaCellEditor<VTypePair> vDeltaCellEditor = new VDeltaCellEditor<>();
648+
VDeltaCellEditor<TableEntry, VTypePair> vDeltaCellEditor = new VDeltaCellEditor<>();
649649
vDeltaCellEditor.setShowDeltaPercentage(n);
650650
return vDeltaCellEditor;
651651
}));
@@ -1453,7 +1453,7 @@ private void addSnapshot(Snapshot snapshot) {
14531453
"", minWidth);
14541454
deltaCol.setCellValueFactory(e -> e.getValue().compareValueProperty(additionalSnapshots.size()));
14551455
deltaCol.setCellFactory(e -> {
1456-
VDeltaCellEditor<VTypePair> vDeltaCellEditor = new VDeltaCellEditor<>();
1456+
VDeltaCellEditor<TableEntry, VTypePair> vDeltaCellEditor = new VDeltaCellEditor<>();
14571457
vDeltaCellEditor.setShowDeltaPercentage(showDeltaPercentage.get());
14581458
return vDeltaCellEditor;
14591459
});

app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/VDeltaCellEditor.java

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,16 @@
2020
package org.phoebus.applications.saveandrestore.ui.snapshot;
2121

2222
import javafx.scene.control.Tooltip;
23+
import org.epics.vtype.VEnumArray;
24+
import org.epics.vtype.VNumberArray;
25+
import org.epics.vtype.VStringArray;
26+
import org.phoebus.applications.saveandrestore.Messages;
2327
import org.phoebus.applications.saveandrestore.ui.VTypePair;
28+
import org.phoebus.applications.saveandrestore.ui.snapshot.compare.ComparisonDialog;
2429
import org.phoebus.core.vtypes.VDisconnectedData;
2530
import org.phoebus.saveandrestore.util.Utilities;
2631
import org.phoebus.saveandrestore.util.VNoData;
32+
import org.phoebus.ui.dialog.DialogHelper;
2733

2834
import java.util.Formatter;
2935

@@ -34,7 +40,7 @@
3440
* @param <T>
3541
* @author Kunal Shroff
3642
*/
37-
public class VDeltaCellEditor<T> extends VTypeCellEditor<T> {
43+
public class VDeltaCellEditor<S, T> extends VTypeCellEditor<S, T> {
3844

3945
private final Tooltip tooltip = new Tooltip();
4046

@@ -44,7 +50,7 @@ protected void setShowDeltaPercentage(boolean showDeltaPercentage) {
4450
this.showDeltaPercentage = showDeltaPercentage;
4551
}
4652

47-
VDeltaCellEditor() {
53+
public VDeltaCellEditor() {
4854
super();
4955
}
5056

@@ -72,17 +78,36 @@ public void updateItem(T item, boolean empty) {
7278
setStyle(TableCellColors.DISCONNECTED_STYLE);
7379
} else if (pair.value == VNoData.INSTANCE) {
7480
setText(pair.value.toString());
75-
} else {
81+
} else if(pair.base == VNoData.INSTANCE){
82+
setText(VNoData.INSTANCE.toString());
83+
}
84+
else {
7685
Utilities.VTypeComparison vtc = Utilities.deltaValueToString(pair.value, pair.base, pair.threshold);
77-
String percentage = Utilities.deltaValueToPercentage(pair.value, pair.base);
78-
if (!percentage.isEmpty() && showDeltaPercentage) {
79-
Formatter formatter = new Formatter();
80-
setText(formatter.format("%g", Double.parseDouble(vtc.getString())) + " (" + percentage + ")");
81-
} else {
82-
setText(vtc.getString());
83-
}
84-
if (!vtc.isWithinThreshold()) {
86+
if (vtc.getValuesEqual() != 0 &&
87+
(pair.base instanceof VNumberArray ||
88+
pair.base instanceof VStringArray ||
89+
pair.base instanceof VEnumArray)) {
90+
TableEntry tableEntry = (TableEntry) getTableRow().getItem();
91+
setText(Messages.clickToCompare);
8592
setStyle(TableCellColors.ALARM_MAJOR_STYLE);
93+
setOnMouseClicked(e -> {
94+
ComparisonDialog comparisonDialog = new ComparisonDialog(tableEntry.getSnapshotVal().get(), tableEntry.getConfigPv().getPvName());
95+
DialogHelper.positionDialog(comparisonDialog, getTableView(), -400, -400);
96+
comparisonDialog.show();
97+
});
98+
} else {
99+
// Do not handle mouse clicked, e.g. if live PV is disconnected.
100+
setOnMouseClicked(e -> {});
101+
String percentage = Utilities.deltaValueToPercentage(pair.value, pair.base);
102+
if (!percentage.isEmpty() && showDeltaPercentage) {
103+
Formatter formatter = new Formatter();
104+
setText(formatter.format("%g", Double.parseDouble(vtc.getString())) + " (" + percentage + ")");
105+
} else {
106+
setText(vtc.getString());
107+
}
108+
if (!vtc.isWithinThreshold()) {
109+
setStyle(TableCellColors.ALARM_MAJOR_STYLE);
110+
}
86111
}
87112
}
88113
tooltip.setText(item.toString());

app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/VTypeCellEditor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@
4949
* @param <T> {@link org.epics.vtype.VType} or {@link org.phoebus.applications.saveandrestore.ui.VTypePair}
5050
* @author <a href="mailto:[email protected]">Jaka Bobnar</a>
5151
*/
52-
public class VTypeCellEditor<T> extends MultitypeTableCell<TableEntry, T> {
52+
public class VTypeCellEditor<S, T> extends MultitypeTableCell<S, T> {
5353
private final Tooltip tooltip = new Tooltip();
5454

55-
VTypeCellEditor() {
55+
public VTypeCellEditor() {
5656

5757
setConverter(new StringConverter<>() {
5858
@Override
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright (C) 2025 European Spallation Source ERIC.
3+
*/
4+
5+
package org.phoebus.applications.saveandrestore.ui.snapshot.compare;
6+
7+
import javafx.beans.property.ObjectProperty;
8+
import javafx.beans.property.SimpleObjectProperty;
9+
import org.epics.vtype.VNumber;
10+
import org.epics.vtype.VType;
11+
import org.phoebus.applications.saveandrestore.SafeMultiply;
12+
import org.phoebus.applications.saveandrestore.ui.VTypePair;
13+
import org.phoebus.saveandrestore.util.Threshold;
14+
import org.phoebus.saveandrestore.util.Utilities;
15+
import org.phoebus.saveandrestore.util.VNoData;
16+
17+
import java.util.Optional;
18+
19+
/**
20+
* Data class for one column in the comparison table.
21+
*/
22+
public class ColumnEntry {
23+
24+
/**
25+
* The {@link VType} value as stored in a {@link org.phoebus.applications.saveandrestore.model.Snapshot}
26+
*/
27+
private final ObjectProperty<VType> storedValue = new SimpleObjectProperty<>(this, "storedValue", null);
28+
/**
29+
* A {@link VTypePair} property holding data for the purpose of calculating and showing a delta.
30+
*/
31+
private final ObjectProperty<VTypePair> delta = new SimpleObjectProperty<>(this, "delta", null);
32+
/**
33+
* The live {@link VType} value as read from a connected PV.
34+
*/
35+
private final ObjectProperty<VType> liveValue = new SimpleObjectProperty<>(this, "liveValue", VNoData.INSTANCE);
36+
37+
private Optional<Threshold<?>> threshold = Optional.empty();
38+
39+
public ColumnEntry(VType storedValue) {
40+
this.storedValue.set(storedValue);
41+
}
42+
43+
public ObjectProperty<VType> storedValueProperty() {
44+
return storedValue;
45+
}
46+
47+
public void setLiveVal(VType liveValue) {
48+
this.liveValue.set(liveValue);
49+
VTypePair vTypePair = new VTypePair(liveValue, storedValue.get(), threshold);
50+
delta.set(vTypePair);
51+
}
52+
53+
public ObjectProperty<VType> liveValueProperty() {
54+
return liveValue;
55+
}
56+
57+
public ObjectProperty<VTypePair> getDelta() {
58+
return delta;
59+
}
60+
61+
/**
62+
* Set the threshold value for this entry. All value comparisons related to this entry are calculated using the
63+
* threshold (if it exists).
64+
*
65+
* @param ratio the threshold
66+
*/
67+
public void setThreshold(double ratio) {
68+
if (storedValue.get() instanceof VNumber) {
69+
VNumber vNumber = SafeMultiply.multiply((VNumber) storedValue.get(), ratio);
70+
boolean isNegative = vNumber.getValue().doubleValue() < 0;
71+
Threshold t = new Threshold<>(isNegative ? SafeMultiply.multiply(vNumber.getValue(), -1.0) : vNumber.getValue());
72+
this.delta.set(new VTypePair(liveValue.get(), storedValue.get(), Optional.of(t)));
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)