From 1656cec8694a35ea8299b20ac9e7445eef87092d Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Fri, 30 Aug 2024 09:19:12 +0200 Subject: [PATCH 01/30] CSSTUDIO-2167 Add HBox "decorations" to LogEntryCell.fxml. --- .../org/phoebus/logbook/olog/ui/LogEntryCell.fxml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryCell.fxml b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryCell.fxml index c2e0360d82..e0cb5d827e 100644 --- a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryCell.fxml +++ b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryCell.fxml @@ -17,13 +17,15 @@ summary of the log entry and its metadata. + - From 485344cebb8a7c662687c149244abc6af20bb058 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Fri, 30 Aug 2024 09:13:51 +0200 Subject: [PATCH 02/30] CSSTUDIO-2167 Add field "HBox decorations" to LogEntryCellController. --- .../org/phoebus/logbook/olog/ui/LogEntryCellController.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java index 33e7c45aeb..76c3b39245 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java @@ -6,6 +6,7 @@ import javafx.scene.control.Label; import javafx.scene.image.Image; import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.layout.VBox; import org.commonmark.Extension; @@ -45,6 +46,8 @@ public class LogEntryCellController { @FXML Label owner; @FXML + HBox decorations; + @FXML Label title; @FXML Label logbooks; From aea98e10f47cec41dbf6a6ddae38a7d315e8ea07 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Fri, 30 Aug 2024 09:17:30 +0200 Subject: [PATCH 03/30] CSSTUDIO-2167 Remove unused import. --- .../java/org/phoebus/logbook/olog/ui/LogEntryCellController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java index 76c3b39245..bec15341cc 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java @@ -2,7 +2,6 @@ import javafx.beans.property.SimpleBooleanProperty; import javafx.fxml.FXML; -import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.image.Image; import javafx.scene.image.ImageView; From a8f2e210b8f331425684c4a3302cba929eaaf6fe Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Fri, 18 Oct 2024 10:25:58 +0200 Subject: [PATCH 04/30] CSSTUDIO-2167 Initial version of Enum-PV decoration in the Logbook application supporting only 1 Enum-PV. --- app/logbook/olog/ui/pom.xml | 8 +- .../olog/ui/AdvancedSearchViewController.java | 21 +++ .../olog/ui/LogEntryCellController.java | 131 ++++++++++++++ .../logbook/olog/ui/LogEntryTable.java | 8 +- .../olog/ui/LogEntryTableViewController.java | 170 +++++++++++++++++- .../logbook/olog/ui/AdvancedSearchView.fxml | 2 + .../phoebus/logbook/olog/ui/LogEntryCell.fxml | 149 +++++++-------- 7 files changed, 415 insertions(+), 74 deletions(-) diff --git a/app/logbook/olog/ui/pom.xml b/app/logbook/olog/ui/pom.xml index 9df3fa0dc3..61493ae0da 100644 --- a/app/logbook/olog/ui/pom.xml +++ b/app/logbook/olog/ui/pom.xml @@ -97,6 +97,12 @@ - + + org.phoebus + app-databrowser + 4.7.4-SNAPSHOT + compile + + \ No newline at end of file diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/AdvancedSearchViewController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/AdvancedSearchViewController.java index e97816cd58..b204f22836 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/AdvancedSearchViewController.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/AdvancedSearchViewController.java @@ -50,6 +50,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.function.Consumer; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -107,6 +108,9 @@ public class AdvancedSearchViewController { @FXML private TextField attachmentTypes; + @FXML + private TextField decorationsPV1; + private SearchParameters searchParameters; private final SimpleBooleanProperty sortAscending = new SimpleBooleanProperty(false); @@ -116,6 +120,10 @@ public class AdvancedSearchViewController { throw new IllegalStateException("Search callback is not set on AdvancedSearchViewConroller!"); }; + private Consumer setPVForDecorationCallback = pvName -> { + throw new IllegalStateException("Set PVs for decoration callback is not set on AdvancedSearchViewController!"); + }; + public AdvancedSearchViewController(LogClient logClient, SearchParameters searchParameters) { this.logClient = logClient; this.searchParameters = searchParameters; @@ -125,6 +133,10 @@ public void setSearchCallback(Runnable searchCallback) { this.searchCallback = searchCallback; } + protected void setSetPVForDecorationCallback(Consumer setPVForDecorationCallback) { + this.setPVForDecorationCallback = setPVForDecorationCallback; + } + @FXML public void initialize() { @@ -149,6 +161,15 @@ public void initialize() { }); sortAscending.addListener(searchOnSortChange); + decorationsPV1.setOnAction(actionEvent -> { + setPVForDecorationCallback.accept(decorationsPV1.getText()); + }); + decorationsPV1.focusedProperty().addListener((property, oldValue, newValue) -> { + if (oldValue && !newValue) { + setPVForDecorationCallback.accept(decorationsPV1.getText()); + } + }); + attachmentTypes.textProperty().bindBidirectional(this.searchParameters.attachmentsProperty()); attachmentTypes.setOnKeyReleased(this::searchOnEnter); diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java index bec15341cc..3a447bc16a 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java @@ -2,24 +2,35 @@ import javafx.beans.property.SimpleBooleanProperty; import javafx.fxml.FXML; +import javafx.geometry.Pos; import javafx.scene.control.Label; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; +import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; +import javafx.scene.shape.Polygon; +import javafx.scene.shape.Rectangle; +import javafx.scene.text.Text; import org.commonmark.Extension; import org.commonmark.ext.gfm.tables.TablesExtension; import org.commonmark.ext.image.attributes.ImageAttributesExtension; import org.commonmark.parser.Parser; import org.commonmark.renderer.text.TextContentRenderer; +import org.epics.vtype.VEnum; import org.phoebus.logbook.Logbook; import org.phoebus.logbook.Tag; import org.phoebus.logbook.olog.ui.LogEntryTableViewController.TableViewListItem; import org.phoebus.ui.javafx.ImageCache; import java.util.Arrays; +import java.util.LinkedList; import java.util.List; +import java.util.Optional; +import java.util.function.Function; import java.util.stream.Collectors; import static org.phoebus.util.time.TimestampFormats.SECONDS_FORMAT; @@ -40,6 +51,9 @@ public class LogEntryCellController { @FXML VBox root; + @FXML + VBox logEntryCell; + @FXML Label time; @FXML @@ -76,6 +90,34 @@ public class LogEntryCellController { private SimpleBooleanProperty expanded = new SimpleBooleanProperty(true); + private Function vEnumToColor; + { + javafx.scene.paint.Paint[] palette = {Color.GREEN, + Color.BLUE, + Color.RED, + Color.YELLOW, + Color.ORANGE, + Color.PURPLE, + Color.AQUA, + Color.CYAN, + Color.LIME, + Color.SALMON, + Color.TURQUOISE, + Color.ROYALBLUE, + Color.AZURE, + Color.LIGHTSKYBLUE, + Color.GRAY, + Color.OLIVE}; + + vEnumToColor = vEnum -> { + int index = vEnum.getIndex(); + if (index >= 0 && index < 16) { + return palette[index]; + } else { + return Color.BLACK; + } + }; + } public LogEntryCellController() { @@ -128,8 +170,97 @@ public void refresh() { logEntryId.setText(logEntry.getLogEntry().getId() != null ? logEntry.getLogEntry().getId().toString() : ""); level.setText(logEntry.getLogEntry().getLevel()); + } + + if (logEntry != null) { + Optional> maybeVEnumFromPreviousLogEntryToThisLogEntry = logEntry.getVEnumFromPreviousLogEntryToThisLogEntry(); + + if (maybeVEnumFromPreviousLogEntryToThisLogEntry.isPresent()) { + List vEnumFromPreviousLogEntryToThisLogEntry = maybeVEnumFromPreviousLogEntryToThisLogEntry.get(); + + if (vEnumFromPreviousLogEntryToThisLogEntry.size() == 1) { + VEnum vEnum = vEnumFromPreviousLogEntryToThisLogEntry.get(0); + Paint paintVEnum = vEnumToColor.apply(vEnum); + + StackPane path; + { + Rectangle background = new Rectangle(40, 40); + background.setFill(Color.TRANSPARENT); + Rectangle line = new Rectangle(4, 40); + line.setFill(paintVEnum); + path = new StackPane(background, line); + } + HBox hBox = new HBox(path); + hBox.setAlignment(Pos.TOP_CENTER); + hBox.setPrefWidth(40); + decorations.getChildren().clear(); + decorations.getChildren().add(path); + } + else if (vEnumFromPreviousLogEntryToThisLogEntry.size() > 1) { + int indexOfLastVEnum = vEnumFromPreviousLogEntryToThisLogEntry.size() - 1; + VEnum lastVEnum = vEnumFromPreviousLogEntryToThisLogEntry.get(indexOfLastVEnum); + Paint paintOfLastVEnum = vEnumToColor.apply(lastVEnum); + StackPane outgoingTriangle = new StackPane(); + { + outgoingTriangle.setAlignment(Pos.BOTTOM_CENTER); + Polygon triangle = new Polygon(15, 6, + 25, 6, + 20, 0); + triangle.setFill(paintOfLastVEnum); + outgoingTriangle.getChildren().add(triangle); + + Rectangle rectangle = new Rectangle(4, 10); + rectangle.setFill(paintOfLastVEnum); + rectangle.setFill(paintOfLastVEnum); + outgoingTriangle.getChildren().add(rectangle); + } + + Rectangle outgoingPath = new Rectangle(40, 20, paintOfLastVEnum); + String abbreviatedName = computeAbbreviatedName(lastVEnum); + Text text = new Text(abbreviatedName); + text.setFill(Color.WHITE); + StackPane stack = new StackPane(); + stack.getChildren().addAll(outgoingPath, text); + + VBox union = new VBox(outgoingTriangle, stack); + int numberOfIncomingVEnumsToDisplay = Math.min(indexOfLastVEnum, 4); + for (int i=numberOfIncomingVEnumsToDisplay-1; i>=0; i--) { + VEnum firstVEnum = vEnumFromPreviousLogEntryToThisLogEntry.get(i); + Paint paintOfFirstVEnum = vEnumToColor.apply(firstVEnum); + double height = Math.floor(15 / numberOfIncomingVEnumsToDisplay); + Rectangle incomingPath = new Rectangle(40, height, paintOfFirstVEnum); + + union.getChildren().add(incomingPath); + } + + Rectangle incomingRectangle = new Rectangle(4, 6); + VEnum firstVEnum = vEnumFromPreviousLogEntryToThisLogEntry.get(0); + Paint paintOfFirstVEnum = vEnumToColor.apply(firstVEnum); + incomingRectangle.setFill(paintOfFirstVEnum); + union.getChildren().add(incomingRectangle); + + decorations.getChildren().clear(); + + union.setAlignment(Pos.TOP_CENTER); + union.setSpacing(0.0); + decorations.getChildren().add(union); + } + } + } + } + + private String computeAbbreviatedName(VEnum vEnum) { + String statusName = vEnum.getValue(); + char[] chars = statusName.toCharArray(); + List abbreviatedNameChars = new LinkedList<>(); + for (int i=0; i c.toString()).collect(Collectors.joining()); + return abbreviatedName; } public void setLogEntry(TableViewListItem logEntry) { diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java index 6993b63859..3b0902e652 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java @@ -53,8 +53,12 @@ public LogEntryTable(final LogEntryTableApp app) { logEntryTableViewController.setGoBackAndGoForwardActions(goBackAndGoForwardActions); return logEntryTableViewController; } else if (clazz.isAssignableFrom(AdvancedSearchViewController.class)) { - return clazz.getConstructor(LogClient.class, SearchParameters.class) - .newInstance(app.getClient(), searchParameters); + AdvancedSearchViewController advancedSearchViewController = (AdvancedSearchViewController) clazz.getConstructor(LogClient.class, SearchParameters.class) + .newInstance(app.getClient(), searchParameters); + advancedSearchViewController.setSetPVForDecorationCallback(pvName -> { + controller.setPVNameForDecoration(pvName); + }); + return advancedSearchViewController; } else if (clazz.isAssignableFrom(SingleLogEntryDisplayController.class)) { SingleLogEntryDisplayController singleLogEntryDisplayController = (SingleLogEntryDisplayController) clazz.getConstructor(LogClient.class).newInstance(app.getClient()); singleLogEntryDisplayController.setSelectLogEntryInUI(id -> goBackAndGoForwardActions.loadLogEntryWithID(id)); diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java index 0fe7b1b4cc..e9566f875a 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java @@ -25,6 +25,13 @@ import javafx.util.Callback; import javafx.util.Duration; import javafx.util.StringConverter; +import org.csstudio.trends.databrowser3.archive.ArchiveFetchJob; +import org.csstudio.trends.databrowser3.archive.ArchiveFetchJobListener; +import org.csstudio.trends.databrowser3.model.ArchiveDataSource; +import org.csstudio.trends.databrowser3.model.PVItem; +import org.csstudio.trends.databrowser3.model.PVSamples; +import org.csstudio.trends.databrowser3.model.RequestType; +import org.epics.vtype.VEnum; import org.phoebus.framework.jobs.JobManager; import org.phoebus.logbook.*; import org.phoebus.logbook.olog.ui.query.OlogQuery; @@ -40,11 +47,17 @@ import org.phoebus.ui.dialog.ExceptionDetailsErrorDialog; import java.io.IOException; +import java.time.Instant; import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.TreeMap; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; @@ -100,6 +113,7 @@ public class LogEntryTableViewController extends LogbookSearchController { private final SimpleBooleanProperty advancedSearchVisibile = new SimpleBooleanProperty(false); + private final AtomicReference> instantToValue = new AtomicReference<>(); /** * Constructor. * @@ -207,6 +221,10 @@ public void initialize() { descriptionCol.setMaxWidth(1f * Integer.MAX_VALUE * 100); descriptionCol.setCellValueFactory(col -> new SimpleObjectProperty<>(col.getValue())); descriptionCol.setCellFactory(col -> new TableCell<>() { + { + setStyle("-fx-padding: -1px"); + } + private final Node graphic; private final PseudoClass childlessTopLevel = PseudoClass.getPseudoClass("grouped"); @@ -381,6 +399,95 @@ private void setSearchResult(SearchResult searchResult) { pageCountProperty.set(1 + (hitCountProperty.get() / pageSizeProperty.get())); refresh(); }); + + decorate(searchResult); + } + + private Optional pvNameForDecoration = Optional.empty(); + + protected void setPVNameForDecoration(String pvName) { + if (pvName.equals("")) { + pvNameForDecoration = Optional.empty(); + } + else { + pvNameForDecoration = Optional.of(pvName); + } + decorate(searchResult); + } + + private void decorate(SearchResult searchResult) { + if (pvNameForDecoration.isPresent()) { + var x = searchResult.getLogs().stream().map(logEntry -> logEntry.getCreatedDate()).collect(Collectors.toUnmodifiableList()); + Instant start = Collections.min(x); + Instant end = Collections.max(x); + + retrievePVValues(pvNameForDecoration.get(), start, end); + } + } + + private void retrievePVValues(String pvName, + Instant start, + Instant end) { + TreeMap newInstantToValue = new TreeMap<>(); + + PVItem pvItem = new PVItem(pvName, Double.MAX_VALUE); + pvItem.setRequestType(RequestType.RAW); + + pvItem.useDefaultArchiveDataSources(); + + ArchiveFetchJobListener archiveFetchJobListener = new ArchiveFetchJobListener() { + + @Override + public void fetchCompleted(ArchiveFetchJob archiveFetchJob) { + PVSamples samples = pvItem.getSamples(); + Lock lock = samples.getLock(); + lock.lock(); + + try { + Optional mostRecentDataPointBeforeStart = Optional.empty(); // When merging data from multiple sources, there may be moe than one data point before the start of the time period. + for (int i = 0; i < samples.size(); i++) { + if (samples.get(i).getVType() instanceof VEnum vEnum) { + if (vEnum.getTime().getTimestamp().equals(start) || vEnum.getTime().getTimestamp().isAfter(start)) { + newInstantToValue.put(vEnum.getTime().getTimestamp(), vEnum); + } + else if (vEnum.getTime().getTimestamp().isBefore(start)) { + if (mostRecentDataPointBeforeStart.isEmpty()) { + mostRecentDataPointBeforeStart = Optional.of(vEnum); + } + else if (vEnum.getTime().getTimestamp().isAfter(mostRecentDataPointBeforeStart.get().getTime().getTimestamp())) { + mostRecentDataPointBeforeStart = Optional.of(vEnum); + } + } + } + } + if (mostRecentDataPointBeforeStart.isPresent()) { + newInstantToValue.put(mostRecentDataPointBeforeStart.get().getTime().getTimestamp(), mostRecentDataPointBeforeStart.get()); + } + instantToValue.set(newInstantToValue); + refresh(); + } + finally { + lock.unlock(); + } + } + + @Override + public void archiveFetchFailed(ArchiveFetchJob archiveFetchJob, ArchiveDataSource archiveDataSource, Exception e) { + + } + + @Override + public void channelNotFound(ArchiveFetchJob archiveFetchJob, boolean b, List list) { + + } + }; + + ArchiveFetchJob archiveFetchJob = new ArchiveFetchJob(pvItem, + start, + end, + archiveFetchJobListener); // Note: The archive fetch job is automatically scheduled by the constructor! + + return; } public void setQuery(String parsedQuery) { @@ -396,7 +503,51 @@ private void refresh() { if (this.searchResult != null) { List selectedLogEntries = new ArrayList<>(tableView.getSelectionModel().getSelectedItems()); ObservableList logsList = FXCollections.observableArrayList(); - logsList.addAll(searchResult.getLogs().stream().map(le -> new TableViewListItem(le, showDetails.get())).toList()); + + List logEntries = searchResult.getLogs(); + logEntries.sort((o1, o2) -> -(o1.getCreatedDate().compareTo(o2.getCreatedDate()))); + + Map> logEntryToVEnumsFromPreviousLogEntryToLogEntry = new TreeMap<>((o1, o2) -> -(o1.getCreatedDate().compareTo(o2.getCreatedDate()))); + { + TreeMap instantToVEnum = instantToValue.get(); + + if (instantToVEnum != null) { + Instant previousLogEntryCreatedDate = Instant.ofEpochSecond(0); + Optional previousLogEntryVEnum = Optional.empty(); + for (int i=logEntries.size()-1; i>=0; i--) { + LogEntry currentLogEntry = logEntries.get(i); + Instant currentLogEntryCreatedDate = currentLogEntry.getCreatedDate(); + List vEnumsFromPreviousLogEntryToCurrentLogEntry = new LinkedList<>(instantToVEnum.subMap(previousLogEntryCreatedDate, false, + currentLogEntryCreatedDate, true) + .values()); + if (previousLogEntryVEnum.isPresent()) { + // Append currentLogEntryVEnum to vEnumsFromPreviousLogEntryToCurrentLogEntry + // so that the current status is known. + vEnumsFromPreviousLogEntryToCurrentLogEntry.add(0, previousLogEntryVEnum.get()); + } + + Optional currentLogEntryVEnum; + if (vEnumsFromPreviousLogEntryToCurrentLogEntry.isEmpty()) { + // No changes have occurred since the last log entry. + // Therefore, the VEnum of the last log entry is still + // the current one. + + currentLogEntryVEnum = previousLogEntryVEnum; + } + else { + currentLogEntryVEnum = Optional.of(vEnumsFromPreviousLogEntryToCurrentLogEntry.get(vEnumsFromPreviousLogEntryToCurrentLogEntry.size()-1)); + } + logEntryToVEnumsFromPreviousLogEntryToLogEntry.put(currentLogEntry, vEnumsFromPreviousLogEntryToCurrentLogEntry); + previousLogEntryCreatedDate = currentLogEntryCreatedDate; + previousLogEntryVEnum = currentLogEntryVEnum; + } + } + } + + logsList.addAll(searchResult.getLogs().stream().map(le -> new TableViewListItem(le, + showDetails.get(), + logEntryToVEnumsFromPreviousLogEntryToLogEntry.getOrDefault(le, null))).toList()); + tableView.setItems(logsList); // This will ensure that selected entries stay selected after the list has been // updated from the search result. @@ -507,6 +658,19 @@ public OlogQuery fromString(String s) { public static class TableViewListItem { private final SimpleBooleanProperty showDetails = new SimpleBooleanProperty(true); private final LogEntry logEntry; + private Optional> vEnumFromPreviousLogEntryToThisLogEntry = Optional.empty(); + + public TableViewListItem(LogEntry logEntry, + boolean showDetails, + List vEnumFromPreviousLogEntryToThisLogEntry) { + this(logEntry, showDetails); + if (vEnumFromPreviousLogEntryToThisLogEntry != null) { + this.vEnumFromPreviousLogEntryToThisLogEntry = Optional.of(vEnumFromPreviousLogEntryToThisLogEntry); + } + else { + this.vEnumFromPreviousLogEntryToThisLogEntry = Optional.empty(); + } + } public TableViewListItem(LogEntry logEntry, boolean showDetails) { this.logEntry = logEntry; @@ -524,6 +688,10 @@ public LogEntry getLogEntry() { public void setShowDetails(boolean show) { this.showDetails.set(show); } + + protected Optional> getVEnumFromPreviousLogEntryToThisLogEntry() { + return vEnumFromPreviousLogEntryToThisLogEntry; + } } public void setShowDetails(boolean show) { diff --git a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/AdvancedSearchView.fxml b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/AdvancedSearchView.fxml index c1e8ceb922..89589ff6bd 100644 --- a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/AdvancedSearchView.fxml +++ b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/AdvancedSearchView.fxml @@ -122,6 +122,8 @@ + - - - - + + + diff --git a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/Decorations.fxml b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/Decorations.fxml index c6102bd8eb..29c628c9c0 100644 --- a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/Decorations.fxml +++ b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/Decorations.fxml @@ -11,7 +11,6 @@ fx:controller="org.phoebus.logbook.olog.ui.DecorationsController"> - diff --git a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/Decorations.fxml b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/Decorations.fxml deleted file mode 100644 index 29c628c9c0..0000000000 --- a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/Decorations.fxml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - From a1bd311775a3a8cda9a3534a22164e3a9b899693 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Wed, 6 Nov 2024 10:36:56 +0100 Subject: [PATCH 22/30] CSSTUDIO-2167 Move implementation of the decorations out into a separate module. --- .../olog/ui/LogEntryCellController.java | 266 +----------------- .../logbook/olog/ui/LogEntryTable.java | 11 +- .../olog/ui/LogEntryTableViewController.java | 252 +---------------- .../logbook/olog/ui/spi/Decoration.java | 11 +- .../phoebus/logbook/olog/ui/LogEntryCell.fxml | 2 +- 5 files changed, 41 insertions(+), 501 deletions(-) diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java index 59843cdffd..cc451917b7 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java @@ -2,41 +2,27 @@ import javafx.beans.property.SimpleBooleanProperty; import javafx.fxml.FXML; -import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Label; -import javafx.scene.control.ProgressIndicator; -import javafx.scene.control.Tooltip; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; -import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; -import javafx.scene.paint.Color; -import javafx.scene.paint.Paint; -import javafx.scene.shape.Circle; -import javafx.scene.shape.Polygon; -import javafx.scene.shape.Rectangle; -import javafx.scene.text.Text; -import javafx.util.Duration; -import javafx.util.Pair; import org.commonmark.Extension; import org.commonmark.ext.gfm.tables.TablesExtension; import org.commonmark.ext.image.attributes.ImageAttributesExtension; import org.commonmark.parser.Parser; import org.commonmark.renderer.text.TextContentRenderer; -import org.epics.vtype.VEnum; import org.phoebus.logbook.Logbook; import org.phoebus.logbook.Tag; import org.phoebus.logbook.olog.ui.LogEntryTableViewController.TableViewListItem; +import org.phoebus.logbook.olog.ui.spi.Decoration; import org.phoebus.ui.javafx.ImageCache; import java.util.Arrays; import java.util.LinkedList; import java.util.List; -import java.util.SortedMap; -import java.util.function.Function; import java.util.stream.Collectors; import static org.phoebus.util.time.TimestampFormats.SECONDS_FORMAT; @@ -65,7 +51,7 @@ public class LogEntryCellController { @FXML Label owner; @FXML - HBox decorations; + HBox decorationNodes; @FXML Label title; @FXML @@ -96,35 +82,6 @@ public class LogEntryCellController { private SimpleBooleanProperty expanded = new SimpleBooleanProperty(true); - private Function vEnumToColor; - { - javafx.scene.paint.Paint[] palette = {Color.GREEN, - Color.BLUE, - Color.RED, - Color.YELLOW, - Color.ORANGE, - Color.PURPLE, - Color.AQUA, - Color.CYAN, - Color.LIME, - Color.SALMON, - Color.TURQUOISE, - Color.ROYALBLUE, - Color.AZURE, - Color.LIGHTSKYBLUE, - Color.GRAY, - Color.OLIVE}; - - vEnumToColor = vEnum -> { - int index = vEnum.getIndex(); - if (index >= 0 && index < 16) { - return palette[index]; - } else { - return Color.BLACK; - } - }; - } - public LogEntryCellController() { List extensions = Arrays.asList(TablesExtension.create(), ImageAttributesExtension.create()); @@ -142,21 +99,20 @@ public void initialize() { conversationIcon.setImage(conversation); } - @FXML - public void refresh() { - - Function createRectangleWithText = text -> { - Rectangle rectangle = new Rectangle(40, 40); - rectangle.setFill(Color.GRAY); + private List decorations = new LinkedList<>(); - Text textNode = new Text(text); - textNode.setFill(Color.BLACK); + public void setDecorations(List decorations) { + this.decorations = decorations; + } - StackPane stack = new StackPane(); - stack.getChildren().addAll(rectangle, textNode); + @FXML + public void refresh() { - return stack; - }; + decorationNodes.getChildren().clear(); + for (Decoration decoration : decorations) { + Node decorationNode = decoration.createDecorationForLogEntryCell(logEntry.getLogEntry()); + decorationNodes.getChildren().add(decorationNode); + } if (logEntry != null) { @@ -192,202 +148,6 @@ public void refresh() { level.setText(logEntry.getLogEntry().getLevel()); } - if (logEntry != null) { - SortedMap> decorationIndexToVEnumFromPreviousLogEntryToThisLogEntry = logEntry.getDecorationIndexToPVNameAndVEnumValuesFromPreviousLogEntryToLogEntry(); - - decorations.getChildren().clear(); - for (int i : decorationIndexToVEnumFromPreviousLogEntryToThisLogEntry.keySet()) { - Pair pvNameAndVEnumFromPreviousLogEntryToThisLogEntry = decorationIndexToVEnumFromPreviousLogEntryToThisLogEntry.get(i); - - String pvName = pvNameAndVEnumFromPreviousLogEntryToThisLogEntry.getKey(); - - LogEntryTableViewController.DecorationDataToDisplay decorationDataToDisplay = pvNameAndVEnumFromPreviousLogEntryToThisLogEntry.getValue(); - - Node decoration; - StringBuilder toolTipStringBuilder = new StringBuilder(); - toolTipStringBuilder.append("PV Name: \t" + pvName + "\n\n"); - if (decorationDataToDisplay instanceof LogEntryTableViewController.LoadingInProgress) { - - toolTipStringBuilder.append("Loading..."); - Node rectangle = createRectangleWithText.apply("Load-\ning..."); - decoration = rectangle; - } - else if (decorationDataToDisplay instanceof LogEntryTableViewController.ChannelNotFound) { - toolTipStringBuilder.append("Error: Channel not found"); - Node rectangle = createRectangleWithText.apply("Error"); - decoration = rectangle; - } - else if (decorationDataToDisplay instanceof LogEntryTableViewController.FetchFailed) { - - toolTipStringBuilder.append("Error: Fetch operation from the archiver failed"); - - Node rectangle = createRectangleWithText.apply("Error"); - - decoration = rectangle; - } - else if (decorationDataToDisplay instanceof LogEntryTableViewController.PVIsNotOfEnumType) { - toolTipStringBuilder.append("Error: PV is not of enum type"); - - Node rectangle = createRectangleWithText.apply("Error"); - - decoration = rectangle; - } - else if (decorationDataToDisplay instanceof LogEntryTableViewController.DataToToDisplay dataToToDisplay) { - List vEnumFromPreviousLogEntryToThisLogEntry = dataToToDisplay.instantToVEnum(); - - for (int j=vEnumFromPreviousLogEntryToThisLogEntry.size()-1; j >= 0; j--) { - VEnum vEnum = vEnumFromPreviousLogEntryToThisLogEntry.get(j); - - String vEnumDate = vEnum.getTime().toString(); - String vEnumDateLessPrecision = vEnumDate.substring(0, vEnumDate.lastIndexOf(".")); - { - int k = vEnum.getIndex(); - var choices = vEnum.getDisplay().getChoices(); - if (k < choices.size()) { - String vEnumValue = choices.get(k); - toolTipStringBuilder.append(vEnumDateLessPrecision + ": \t" + vEnumValue + "\n"); - } - else if (choices.size() > 0) { - toolTipStringBuilder.append(vEnumDateLessPrecision + ": \t" + "Invalid index " + k + "\n"); - } - else { // choices.size() == 0 - toolTipStringBuilder.append(vEnumDateLessPrecision + ": \t" + "Enum choice " + k + " (The names of the enum-choices are not available) \n"); - } - } - - - } - - if (vEnumFromPreviousLogEntryToThisLogEntry.size() == 0) { - toolTipStringBuilder.append("No data available"); - - Node rectangle = createRectangleWithText.apply("No\ndata"); - decoration = rectangle; - } - else if (vEnumFromPreviousLogEntryToThisLogEntry.size() == 1) { - VEnum vEnum = vEnumFromPreviousLogEntryToThisLogEntry.get(0); - Paint paintVEnum = vEnumToColor.apply(vEnum); - - Rectangle background = new Rectangle(40, 40); - background.setFill(Color.TRANSPARENT); - Rectangle line = new Rectangle(4, 40); - line.setFill(paintVEnum); - - decoration = new StackPane(background, line); - } - else { //vEnumFromPreviousLogEntryToThisLogEntry.size() > 1 - int indexOfLastVEnum = vEnumFromPreviousLogEntryToThisLogEntry.size() - 1; - VEnum lastVEnum = vEnumFromPreviousLogEntryToThisLogEntry.get(indexOfLastVEnum); - Paint paintOfLastVEnum = vEnumToColor.apply(lastVEnum); - - StackPane outgoingTriangle = new StackPane(); - { - outgoingTriangle.setAlignment(Pos.BOTTOM_CENTER); - Polygon triangle = new Polygon(15, 6, - 25, 6, - 20, 0); - triangle.setFill(paintOfLastVEnum); - outgoingTriangle.getChildren().add(triangle); - - Rectangle rectangle = new Rectangle(4, 10); - rectangle.setFill(paintOfLastVEnum); - rectangle.setFill(paintOfLastVEnum); - outgoingTriangle.getChildren().add(rectangle); - } - - Rectangle outgoingPath = new Rectangle(40, 20, paintOfLastVEnum); - String abbreviatedName = computeAbbreviatedName(lastVEnum); - Text text = new Text(abbreviatedName); - text.setFill(Color.WHITE); - StackPane stack = new StackPane(); - stack.getChildren().addAll(outgoingPath, text); - - VBox vBox = new VBox(outgoingTriangle, stack); - - if (vEnumFromPreviousLogEntryToThisLogEntry.size() == 2) { - VEnum firstVEnum = vEnumFromPreviousLogEntryToThisLogEntry.get(0); - Paint paintOfFirstVEnum = vEnumToColor.apply(firstVEnum); - - double height = Math.floor(15); - Rectangle incomingPath = new Rectangle(40, height, paintOfFirstVEnum); - - vBox.getChildren().add(incomingPath); - } - else { // vEnumFromPreviousLogEntryToThisLogEntry.size() > 2 - VEnum firstVEnum = vEnumFromPreviousLogEntryToThisLogEntry.get(0); - Paint paintOfFirstVEnum = vEnumToColor.apply(firstVEnum); - - StackPane dots; - { - double height1 = Math.floor(8); - Rectangle rectangle1 = new Rectangle(40, height1, Color.TRANSPARENT); - - Circle circle1 = new Circle(2.0, Color.BLACK); - Circle circle2 = new Circle(2.0, Color.BLACK); - Circle circle3 = new Circle(2.0, Color.BLACK); - - HBox hBox = new HBox(circle1, circle2, circle3); - hBox.setSpacing(3.0); - hBox.setAlignment(Pos.CENTER); - - dots = new StackPane(rectangle1, hBox); - dots.setAlignment(Pos.BASELINE_CENTER); - } - vBox.getChildren().add(dots); - - double height2 = Math.floor(7); - Rectangle rectangle2 = new Rectangle(40, height2, paintOfFirstVEnum); - vBox.getChildren().add(rectangle2); - } - - Rectangle incomingRectangle = new Rectangle(4, 6); - VEnum firstVEnum = vEnumFromPreviousLogEntryToThisLogEntry.get(0); - Paint paintOfFirstVEnum = vEnumToColor.apply(firstVEnum); - incomingRectangle.setFill(paintOfFirstVEnum); - vBox.getChildren().add(incomingRectangle); - - vBox.setAlignment(Pos.TOP_CENTER); - vBox.setSpacing(0.0); - - decoration = vBox; - } - } - else { - throw new RuntimeException("Unhandled instance of \"DecorationDataToDisplay: \"" + decorationDataToDisplay.getClass().toString()); - } - - Tooltip tooltip = new Tooltip(toolTipStringBuilder.toString()); - tooltip.setShowDuration(Duration.INDEFINITE); - Tooltip.install(decoration, tooltip); - - Rectangle margin = new Rectangle(5, 40); - margin.setFill(Color.TRANSPARENT); - - HBox hBox = new HBox(decoration, margin); - hBox.setAlignment(Pos.TOP_CENTER); - - decorations.getChildren().add(hBox); - } - } - } - - private String computeAbbreviatedName(VEnum vEnum) { - int i = vEnum.getIndex(); - var choices = vEnum.getDisplay().getChoices(); - if (i < choices.size()) { - String statusName = choices.get(i); - char[] chars = statusName.toCharArray(); - List abbreviatedNameChars = new LinkedList<>(); - for (int j=0; j c.toString()).collect(Collectors.joining()); - return abbreviatedName; - } else { - return String.valueOf(i); - } } public void setLogEntry(TableViewListItem logEntry) { diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java index b2a8caabd7..dddc20f3e1 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java @@ -3,7 +3,6 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXMLLoader; -import javafx.scene.Node; import javafx.scene.control.Alert; import org.phoebus.framework.nls.NLS; import org.phoebus.framework.persistence.Memento; @@ -41,12 +40,11 @@ public LogEntryTable(final LogEntryTableApp app) { this.app = app; goBackAndGoForwardActions = new GoBackAndGoForwardActions(); - List decorationInputNodes = new LinkedList<>(); + List decorations = new LinkedList<>(); { ServiceLoader decorationClasses = ServiceLoader.load(Decoration.class); - for (var decoration : decorationClasses) { - Node decorationInputNode = decoration.decorationInputNode(); - decorationInputNodes.add(decorationInputNode); + for (Decoration decoration : decorationClasses) { + decorations.add(decoration); } } @@ -65,9 +63,10 @@ public LogEntryTable(final LogEntryTableApp app) { if (clazz.isAssignableFrom(LogEntryTableViewController.class)) { LogEntryTableViewController logEntryTableViewController = (LogEntryTableViewController) clazz.getConstructor(LogClient.class, OlogQueryManager.class, SearchParameters.class).newInstance(app.getClient(), ologQueryManager, searchParameters); logEntryTableViewController.setGoBackAndGoForwardActions(goBackAndGoForwardActions); + logEntryTableViewController.setDecorations(decorations); return logEntryTableViewController; } else if (clazz.isAssignableFrom(AdvancedSearchViewController.class)) { - return clazz.getConstructor(LogClient.class, SearchParameters.class, List.class).newInstance(app.getClient(), searchParameters, decorationInputNodes); + return clazz.getConstructor(LogClient.class, SearchParameters.class, List.class).newInstance(app.getClient(), searchParameters, decorations.stream().map(decoration -> decoration.getDecorationInputNode()).toList()); } else if (clazz.isAssignableFrom(SingleLogEntryDisplayController.class)) { SingleLogEntryDisplayController singleLogEntryDisplayController = (SingleLogEntryDisplayController) clazz.getConstructor(LogClient.class).newInstance(app.getClient()); singleLogEntryDisplayController.setSelectLogEntryInUI(id -> goBackAndGoForwardActions.loadLogEntryWithID(id)); diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java index 0c701449eb..6562c4040d 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java @@ -37,6 +37,7 @@ import org.phoebus.logbook.*; import org.phoebus.logbook.olog.ui.query.OlogQuery; import org.phoebus.logbook.olog.ui.query.OlogQueryManager; +import org.phoebus.logbook.olog.ui.spi.Decoration; import org.phoebus.logbook.olog.ui.write.LogEntryEditorStage; import org.phoebus.logbook.olog.ui.write.LogEntryUpdateStage; import org.phoebus.olog.es.api.model.LogGroupProperty; @@ -117,28 +118,6 @@ public class LogEntryTableViewController extends LogbookSearchController { private final SimpleBooleanProperty advancedSearchVisibile = new SimpleBooleanProperty(false); - sealed interface DecorationDataLoadingStatus permits LoadingSuccessful, - LoadingInProgress, - ChannelNotFound, - FetchFailed, - PVIsNotOfEnumType {} - - public record LoadingSuccessful (TreeMap instantToVEnum) implements DecorationDataLoadingStatus { } - public record LoadingInProgress () implements DecorationDataLoadingStatus, DecorationDataToDisplay { } - public record ChannelNotFound () implements DecorationDataLoadingStatus, DecorationDataToDisplay { } - public record FetchFailed () implements DecorationDataLoadingStatus, DecorationDataToDisplay { } - public record PVIsNotOfEnumType () implements DecorationDataLoadingStatus, DecorationDataToDisplay { } - - sealed interface DecorationDataToDisplay permits DataToToDisplay, - LoadingInProgress, - ChannelNotFound, - FetchFailed, - PVIsNotOfEnumType {} - - public record DataToToDisplay(List instantToVEnum) implements DecorationDataToDisplay { } - - private final ConcurrentMap> decorationIndexToPVNameAndStatus = new ConcurrentHashMap<>(); - /** * Constructor. * @@ -260,6 +239,7 @@ public void initialize() { FXMLLoader loader = new FXMLLoader(getClass().getResource("LogEntryCell.fxml")); graphic = loader.load(); controller = loader.getController(); + controller.setDecorations(decorations); } catch (IOException exc) { throw new RuntimeException(exc); } @@ -417,141 +397,22 @@ public void setLogs(List logs) { throw new RuntimeException(new UnsupportedOperationException()); } + private List decorations; + protected void setDecorations(List decorations) { + this.decorations = decorations; + } + private void setSearchResult(SearchResult searchResult) { this.searchResult = searchResult; + + List logEntries = searchResult.getLogs(); + decorations.forEach(decoration -> decoration.setLogEntries(logEntries)); + Platform.runLater(() -> { hitCountProperty.set(searchResult.getHitCount()); pageCountProperty.set(1 + (hitCountProperty.get() / pageSizeProperty.get())); refresh(); }); - - decorate(searchResult); - } - - private Optional[] pvNamesForDecoration = new Optional[0]; - - protected void setPVNameForDecoration(int i, String pvName) { - if (i >= pvNamesForDecoration.length) { - Optional[] newPvNamesForDecoration = new Optional[i + 1]; - for (int j = 0; j < newPvNamesForDecoration.length; j++) { - newPvNamesForDecoration[j] = j < pvNamesForDecoration.length ? pvNamesForDecoration[j] : Optional.empty(); - } - pvNamesForDecoration = newPvNamesForDecoration; - } - - if (pvName.isEmpty()) { - pvNamesForDecoration[i] = Optional.empty(); - } else { - pvNamesForDecoration[i] = Optional.of(pvName); - } - decorate(i, searchResult); - } - - private void decorate(SearchResult searchResult) { - for (int i = 0; i < pvNamesForDecoration.length; i++) { - decorate(i, searchResult); - } - } - - private void decorate(int i, SearchResult searchResult) { - if (pvNamesForDecoration[i].isPresent()) { - List logEntries = searchResult.getLogs(); - List createdDates = logEntries.stream().map(logEntry -> logEntry.getCreatedDate()).collect(Collectors.toUnmodifiableList()); - Instant start = Collections.min(createdDates); - Instant end = Collections.max(createdDates); - - retrievePVValues(i, pvNamesForDecoration[i].get(), start, end); - } else { - decorationIndexToPVNameAndStatus.remove(i); - refresh(); - } - } - - private void retrievePVValues(int decorationIndex, - String pvName, - Instant start, - Instant end) { - PVItem pvItem = new PVItem(pvName, Double.MAX_VALUE); - pvItem.setRequestType(RequestType.RAW); - - pvItem.useDefaultArchiveDataSources(); - - ArchiveFetchJobListener archiveFetchJobListener = new ArchiveFetchJobListener() { - - private boolean channelFoundAtLeastOnce = true; - - @Override - public void fetchCompleted(ArchiveFetchJob archiveFetchJob) { - if (channelFoundAtLeastOnce) { - PVSamples samples = pvItem.getSamples(); - Lock lock = samples.getLock(); - lock.lock(); - - try { - TreeMap newInstantToValue = new TreeMap<>(); - Optional mostRecentDataPointBeforeStart = Optional.empty(); // When merging data from multiple sources, there may be moe than one data point before the start of the time period. - - boolean isEnumPV = true; - for (int i = 0; i < samples.size(); i++) { - if (!(samples.get(i).getVType() instanceof VEnum)) { - isEnumPV = false; - } - } - - if (isEnumPV) { - for (int i = 0; i < samples.size(); i++) { - if (samples.get(i).getVType() instanceof VEnum vEnum) { - if (vEnum.getTime().getTimestamp().equals(start) || vEnum.getTime().getTimestamp().isAfter(start)) { - newInstantToValue.put(vEnum.getTime().getTimestamp(), vEnum); - } else if (vEnum.getTime().getTimestamp().isBefore(start)) { - if (mostRecentDataPointBeforeStart.isEmpty()) { - mostRecentDataPointBeforeStart = Optional.of(vEnum); - } else if (vEnum.getTime().getTimestamp().isAfter(mostRecentDataPointBeforeStart.get().getTime().getTimestamp())) { - mostRecentDataPointBeforeStart = Optional.of(vEnum); - } - } - } - } - if (mostRecentDataPointBeforeStart.isPresent()) { - newInstantToValue.put(mostRecentDataPointBeforeStart.get().getTime().getTimestamp(), mostRecentDataPointBeforeStart.get()); - } - decorationIndexToPVNameAndStatus.put(decorationIndex, new Pair(pvName, new LoadingSuccessful(newInstantToValue))); - } - else { - decorationIndexToPVNameAndStatus.put(decorationIndex, new Pair(pvName, new PVIsNotOfEnumType())); - } - } finally { - lock.unlock(); - } - } - else { - decorationIndexToPVNameAndStatus.put(decorationIndex, new Pair(pvName, new ChannelNotFound())); - } - refresh(); - } - - @Override - public void archiveFetchFailed(ArchiveFetchJob archiveFetchJob, ArchiveDataSource archiveDataSource, Exception e) { - decorationIndexToPVNameAndStatus.put(decorationIndex, new Pair(pvName, new FetchFailed())); - refresh(); - } - - @Override - public void channelNotFound(ArchiveFetchJob archiveFetchJob, - boolean channelFoundAtLeastOnce, - List list) { - this.channelFoundAtLeastOnce = channelFoundAtLeastOnce; - } - }; - - decorationIndexToPVNameAndStatus.put(decorationIndex, new Pair(pvName, new LoadingInProgress())); - refresh(); - ArchiveFetchJob archiveFetchJob = new ArchiveFetchJob(pvItem, - start, - end, - archiveFetchJobListener); // Note: The archive fetch job is automatically scheduled by the constructor! - - return; } public void setQuery(String parsedQuery) { @@ -570,83 +431,8 @@ private synchronized void refresh() { List logEntries = searchResult.getLogs(); logEntries.sort((o1, o2) -> -(o1.getCreatedDate().compareTo(o2.getCreatedDate()))); - Map>> logEntryToPVNameAndVEnumDecorationDataToDisplay = new TreeMap<>((o1, o2) -> -(o1.getCreatedDate().compareTo(o2.getCreatedDate()))); - logEntries.forEach(logEntry -> logEntryToPVNameAndVEnumDecorationDataToDisplay.put(logEntry, new TreeMap<>())); // Initialize logEntryToPVNameAndVEnumDecorationDataToDisplay - { - for (int i = 0; i < pvNamesForDecoration.length; i++) { - if (decorationIndexToPVNameAndStatus.containsKey(i)) { - Pair pvNameAndStatus = decorationIndexToPVNameAndStatus.get(i); - String pvName = pvNameAndStatus.getKey(); - DecorationDataLoadingStatus status = pvNameAndStatus.getValue(); - - if (status instanceof LoadingInProgress) { - for (int j = logEntries.size() - 1; j >= 0; j--) { - LogEntry currentLogEntry = logEntries.get(j); - logEntryToPVNameAndVEnumDecorationDataToDisplay.get(currentLogEntry).put(i, new Pair(pvName, new LoadingInProgress())); - } - } - else if (status instanceof FetchFailed) { - for (int j = logEntries.size() - 1; j >= 0; j--) { - LogEntry currentLogEntry = logEntries.get(j); - logEntryToPVNameAndVEnumDecorationDataToDisplay.get(currentLogEntry).put(i, new Pair(pvName, new FetchFailed())); - } - } - else if (status instanceof ChannelNotFound) { - for (int j = logEntries.size() - 1; j >= 0; j--) { - LogEntry currentLogEntry = logEntries.get(j); - logEntryToPVNameAndVEnumDecorationDataToDisplay.get(currentLogEntry).put(i, new Pair(pvName, new ChannelNotFound())); - } - } - else if (status instanceof PVIsNotOfEnumType) { - for (int j = logEntries.size() - 1; j >= 0; j--) { - LogEntry currentLogEntry = logEntries.get(j); - logEntryToPVNameAndVEnumDecorationDataToDisplay.get(currentLogEntry).put(i, new Pair(pvName, new PVIsNotOfEnumType())); - } - } - else if (status instanceof LoadingSuccessful loadingSuccessful) { - TreeMap instantToVEnum = loadingSuccessful.instantToVEnum; - Instant previousLogEntryCreatedDate = Instant.ofEpochSecond(0); - Optional previousLogEntryVEnum = Optional.empty(); - for (int j = logEntries.size() - 1; j >= 0; j--) { - LogEntry currentLogEntry = logEntries.get(j); - Instant currentLogEntryCreatedDate = currentLogEntry.getCreatedDate(); - List vEnumsFromPreviousLogEntryToCurrentLogEntry = new LinkedList<>(instantToVEnum.subMap(previousLogEntryCreatedDate, false, - currentLogEntryCreatedDate, true) - .values()); - if (previousLogEntryVEnum.isPresent()) { - // Append currentLogEntryVEnum to vEnumsFromPreviousLogEntryToCurrentLogEntry - // so that the current status is known. - vEnumsFromPreviousLogEntryToCurrentLogEntry.add(0, previousLogEntryVEnum.get()); - } - - Optional currentLogEntryVEnum; - if (vEnumsFromPreviousLogEntryToCurrentLogEntry.isEmpty()) { - // No changes have occurred since the last log entry. - // Therefore, the VEnum of the last log entry is still - // the current one. - - currentLogEntryVEnum = previousLogEntryVEnum; - } else { - currentLogEntryVEnum = Optional.of(vEnumsFromPreviousLogEntryToCurrentLogEntry.get(vEnumsFromPreviousLogEntryToCurrentLogEntry.size() - 1)); - } - logEntryToPVNameAndVEnumDecorationDataToDisplay.get(currentLogEntry).put(i, new Pair(pvName, new DataToToDisplay(vEnumsFromPreviousLogEntryToCurrentLogEntry))); - previousLogEntryCreatedDate = currentLogEntryCreatedDate; - previousLogEntryVEnum = currentLogEntryVEnum; - } - } - } else { - for (int j = logEntries.size() - 1; j >= 0; j--) { - LogEntry currentLogEntry = logEntries.get(j); - logEntryToPVNameAndVEnumDecorationDataToDisplay.get(currentLogEntry).remove(i); - } - } - } - } - boolean showDetailsBoolean = showDetails.get(); - var logs = logEntries.stream().map(le -> new TableViewListItem(le, - showDetailsBoolean, - logEntryToPVNameAndVEnumDecorationDataToDisplay.getOrDefault(le, new TreeMap<>()))).toList(); + var logs = logEntries.stream().map(le -> new TableViewListItem(le, showDetailsBoolean)).toList(); ObservableList logsList = FXCollections.observableArrayList(logs); tableView.setItems(logsList); @@ -759,16 +545,6 @@ public OlogQuery fromString(String s) { public static class TableViewListItem { private final SimpleBooleanProperty showDetails = new SimpleBooleanProperty(true); private final LogEntry logEntry; - private SortedMap> decorationIndexToPVNameAndVEnumValuesFromPreviousLogEntryToLogEntry = new TreeMap<>(); - - public TableViewListItem(LogEntry logEntry, - boolean showDetails, - SortedMap> decorationIndexToPVNameAndDecorationDataToDisplay) { - this(logEntry, showDetails); - - Objects.requireNonNull(decorationIndexToPVNameAndDecorationDataToDisplay); - this.decorationIndexToPVNameAndVEnumValuesFromPreviousLogEntryToLogEntry = decorationIndexToPVNameAndDecorationDataToDisplay; - } public TableViewListItem(LogEntry logEntry, boolean showDetails) { this.logEntry = logEntry; @@ -786,10 +562,6 @@ public LogEntry getLogEntry() { public void setShowDetails(boolean show) { this.showDetails.set(show); } - - protected SortedMap> getDecorationIndexToPVNameAndVEnumValuesFromPreviousLogEntryToLogEntry() { - return decorationIndexToPVNameAndVEnumValuesFromPreviousLogEntryToLogEntry; - } } public void setShowDetails(boolean show) { diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/spi/Decoration.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/spi/Decoration.java index 819b337d4c..4bce57a3b3 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/spi/Decoration.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/spi/Decoration.java @@ -1,7 +1,16 @@ package org.phoebus.logbook.olog.ui.spi; import javafx.scene.Node; +import org.phoebus.logbook.LogEntry; + +import java.util.List; public interface Decoration { - public Node decorationInputNode(); + public Node getDecorationInputNode(); + + public void setLogEntries(List logEntries); + + public void setRefreshLogEntryTableView(Runnable refreshLogEntryTableView); + + public Node createDecorationForLogEntryCell(LogEntry logEntry); } diff --git a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryCell.fxml b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryCell.fxml index ddc0276e31..4ad3ba05e5 100644 --- a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryCell.fxml +++ b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryCell.fxml @@ -13,7 +13,7 @@ summary of the log entry and its metadata. - + From 8a169abb65aeaf332a3797536bede57a304b7398 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Wed, 6 Nov 2024 13:53:10 +0100 Subject: [PATCH 23/30] CSSTUDIO-2167 Set refresh function for LogEntryTableView. --- .../phoebus/logbook/olog/ui/LogEntryTableViewController.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java index 6562c4040d..1106bb45e3 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java @@ -400,6 +400,9 @@ public void setLogs(List logs) { private List decorations; protected void setDecorations(List decorations) { this.decorations = decorations; + for (Decoration decoration : decorations) { + decoration.setRefreshLogEntryTableView(() -> refresh()); + } } private void setSearchResult(SearchResult searchResult) { From fc5d99687b483b9af0b8a9224b61e6a59640e429 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Fri, 8 Nov 2024 15:35:33 +0100 Subject: [PATCH 24/30] CSSTUDIO-2167 Remove dependency on 'app-databrowser'. --- app/logbook/olog/ui/pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/logbook/olog/ui/pom.xml b/app/logbook/olog/ui/pom.xml index 61493ae0da..56ffc8f74c 100644 --- a/app/logbook/olog/ui/pom.xml +++ b/app/logbook/olog/ui/pom.xml @@ -97,12 +97,6 @@ - - org.phoebus - app-databrowser - 4.7.4-SNAPSHOT - compile - \ No newline at end of file From ab5629ca1e4d402b027023d0d0e15308ee9a5b77 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Fri, 8 Nov 2024 15:36:25 +0100 Subject: [PATCH 25/30] CSSTUDIO-2167 Add back spaces. --- app/logbook/olog/ui/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/logbook/olog/ui/pom.xml b/app/logbook/olog/ui/pom.xml index 56ffc8f74c..9df3fa0dc3 100644 --- a/app/logbook/olog/ui/pom.xml +++ b/app/logbook/olog/ui/pom.xml @@ -97,6 +97,6 @@ - + \ No newline at end of file From c59e103e951a89a287a03722b9f794b8522a7df8 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Fri, 8 Nov 2024 15:41:12 +0100 Subject: [PATCH 26/30] CSSTUDIO-2167 Remove unused imports and replace wildcard imports with single class imports. --- .../olog/ui/LogEntryTableViewController.java | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java index 1106bb45e3..edeb6dc5ff 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java @@ -14,8 +14,22 @@ import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.Node; -import javafx.scene.control.*; +import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.ComboBox; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.control.MenuItem; +import javafx.scene.control.Pagination; +import javafx.scene.control.ProgressIndicator; +import javafx.scene.control.SelectionMode; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.TextField; +import javafx.scene.control.TreeView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; @@ -24,17 +38,14 @@ import javafx.scene.text.FontWeight; import javafx.util.Callback; import javafx.util.Duration; -import javafx.util.Pair; import javafx.util.StringConverter; -import org.csstudio.trends.databrowser3.archive.ArchiveFetchJob; -import org.csstudio.trends.databrowser3.archive.ArchiveFetchJobListener; -import org.csstudio.trends.databrowser3.model.ArchiveDataSource; -import org.csstudio.trends.databrowser3.model.PVItem; -import org.csstudio.trends.databrowser3.model.PVSamples; -import org.csstudio.trends.databrowser3.model.RequestType; -import org.epics.vtype.VEnum; import org.phoebus.framework.jobs.JobManager; -import org.phoebus.logbook.*; +import org.phoebus.logbook.LogClient; +import org.phoebus.logbook.LogEntry; +import org.phoebus.logbook.LogService; +import org.phoebus.logbook.LogbookException; +import org.phoebus.logbook.LogbookPreferences; +import org.phoebus.logbook.SearchResult; import org.phoebus.logbook.olog.ui.query.OlogQuery; import org.phoebus.logbook.olog.ui.query.OlogQueryManager; import org.phoebus.logbook.olog.ui.spi.Decoration; @@ -49,20 +60,11 @@ import org.phoebus.ui.dialog.ExceptionDetailsErrorDialog; import java.io.IOException; -import java.time.Instant; import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; -import java.util.SortedMap; -import java.util.TreeMap; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.Lock; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; From ae0b08fa803fd1ac3aef02a31c7b16ef6674fd2e Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Wed, 20 Nov 2024 09:47:51 +0100 Subject: [PATCH 27/30] CSSTUDIO-2167 Remove the ability to input PVs for decorations under "Advanced Search" in the Logbook application. --- .../olog/ui/AdvancedSearchViewController.java | 13 +------------ .../org/phoebus/logbook/olog/ui/LogEntryTable.java | 2 +- .../org/phoebus/logbook/olog/ui/spi/Decoration.java | 2 -- .../phoebus/logbook/olog/ui/AdvancedSearchView.fxml | 3 +-- 4 files changed, 3 insertions(+), 17 deletions(-) diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/AdvancedSearchViewController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/AdvancedSearchViewController.java index bf057098d0..19c23d32e2 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/AdvancedSearchViewController.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/AdvancedSearchViewController.java @@ -64,9 +64,6 @@ public class AdvancedSearchViewController { static final Logger logger = Logger.getLogger(AdvancedSearchViewController.class.getName()); - @FXML - public VBox decorationInputNodesVBox; - @FXML Label levelLabel; @@ -116,18 +113,14 @@ public class AdvancedSearchViewController { private final SimpleBooleanProperty sortAscending = new SimpleBooleanProperty(false); private final SimpleBooleanProperty requireAttachments = new SimpleBooleanProperty(false); - private List decorationInputNodes; - private Runnable searchCallback = () -> { throw new IllegalStateException("Search callback is not set on AdvancedSearchViewController!"); }; public AdvancedSearchViewController(LogClient logClient, - SearchParameters searchParameters, - List decorationInputNodes) { + SearchParameters searchParameters) { this.logClient = logClient; this.searchParameters = searchParameters; - this.decorationInputNodes = decorationInputNodes; } public void setSearchCallback(Runnable searchCallback) { @@ -305,10 +298,6 @@ public void initialize() { sortDescRadioButton.setOnAction(ae -> sortAscending.set(false)); sortAscRadioButton.setOnAction(ae -> sortAscending.set(true)); - - if (decorationInputNodes != null) { - decorationInputNodesVBox.getChildren().addAll(decorationInputNodes); - } } public AnchorPane getPane() { diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java index dddc20f3e1..ab6f5882cb 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java @@ -66,7 +66,7 @@ public LogEntryTable(final LogEntryTableApp app) { logEntryTableViewController.setDecorations(decorations); return logEntryTableViewController; } else if (clazz.isAssignableFrom(AdvancedSearchViewController.class)) { - return clazz.getConstructor(LogClient.class, SearchParameters.class, List.class).newInstance(app.getClient(), searchParameters, decorations.stream().map(decoration -> decoration.getDecorationInputNode()).toList()); + return clazz.getConstructor(LogClient.class, SearchParameters.class).newInstance(app.getClient(), searchParameters); } else if (clazz.isAssignableFrom(SingleLogEntryDisplayController.class)) { SingleLogEntryDisplayController singleLogEntryDisplayController = (SingleLogEntryDisplayController) clazz.getConstructor(LogClient.class).newInstance(app.getClient()); singleLogEntryDisplayController.setSelectLogEntryInUI(id -> goBackAndGoForwardActions.loadLogEntryWithID(id)); diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/spi/Decoration.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/spi/Decoration.java index 4bce57a3b3..f493cc1957 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/spi/Decoration.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/spi/Decoration.java @@ -6,8 +6,6 @@ import java.util.List; public interface Decoration { - public Node getDecorationInputNode(); - public void setLogEntries(List logEntries); public void setRefreshLogEntryTableView(Runnable refreshLogEntryTableView); diff --git a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/AdvancedSearchView.fxml b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/AdvancedSearchView.fxml index 2b72ece480..848b9ab321 100644 --- a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/AdvancedSearchView.fxml +++ b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/AdvancedSearchView.fxml @@ -120,8 +120,7 @@ - - + From 2a2116f316e015be826738968144aa8d7a5e04a4 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Wed, 20 Nov 2024 09:58:56 +0100 Subject: [PATCH 28/30] CSSTUDIO-2167 Remove "@FXML". --- .../java/org/phoebus/logbook/olog/ui/LogEntryCellController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java index cc451917b7..9c20464e57 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java @@ -105,7 +105,6 @@ public void setDecorations(List decorations) { this.decorations = decorations; } - @FXML public void refresh() { decorationNodes.getChildren().clear(); From cbdc0984fa01bfa69fcff9f71e45f0f913802902 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Wed, 20 Nov 2024 10:33:13 +0100 Subject: [PATCH 29/30] CSSTUDIO-2167 Improve code style: remove the modifiers 'public' from the interface 'Decoration'. --- .../java/org/phoebus/logbook/olog/ui/spi/Decoration.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/spi/Decoration.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/spi/Decoration.java index f493cc1957..598b4f31f2 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/spi/Decoration.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/spi/Decoration.java @@ -6,9 +6,9 @@ import java.util.List; public interface Decoration { - public void setLogEntries(List logEntries); + void setLogEntries(List logEntries); - public void setRefreshLogEntryTableView(Runnable refreshLogEntryTableView); + void setRefreshLogEntryTableView(Runnable refreshLogEntryTableView); - public Node createDecorationForLogEntryCell(LogEntry logEntry); + Node createDecorationForLogEntryCell(LogEntry logEntry); } From a9065127ecd28e96707267ddf724acf4513cf829 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Fri, 22 Nov 2024 08:11:56 +0100 Subject: [PATCH 30/30] CSSTUDIO-2167 Adjust padding. --- .../resources/org/phoebus/logbook/olog/ui/LogEntryCell.fxml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryCell.fxml b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryCell.fxml index 4ad3ba05e5..a906f9d067 100644 --- a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryCell.fxml +++ b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryCell.fxml @@ -15,6 +15,9 @@ summary of the log entry and its metadata. + + +