Skip to content

Commit

Permalink
Merge pull request #3126 from ControlSystemStudio/CSSTUDIO-1650
Browse files Browse the repository at this point in the history
Support taking snapshots based on archiver data
  • Loading branch information
shroffk authored Sep 3, 2024
2 parents 77356d2 + 1e23585 commit aa4cc77
Show file tree
Hide file tree
Showing 13 changed files with 285 additions and 196 deletions.
5 changes: 5 additions & 0 deletions app/save-and-restore/app/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
<artifactId>app-display-model</artifactId>
<version>4.7.4-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.phoebus</groupId>
<artifactId>app-trends-archive-datasource</artifactId>
<version>4.7.4-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ public class Messages {
public static String setpoint;
public static String setpointPVWhen;
public static String severity;
public static String snapshotFromArchiver;
public static String snapshotFromPvs;
public static String status;
public static String storedReadbackValue;
public static String storedValues;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ public class Preferences {
@Preference
public static String default_snapshot_name_date_format;

/**
* Default snapshot mode
*/
@Preference
public static String default_snapshot_mode;

static {
AnnotatedPreferences.initialize(Preferences.class, "/save_and_restore_preferences.properties");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -435,32 +435,31 @@ public List<SnapshotItem> takeSnapshot(String configurationNodeId) throws Except
}

/**
* Requests service to take a snapshot, i.e. to read PVs as defined in a {@link Configuration}.
* Retrieves PV values from an archiver for the PVs as defined in a {@link Configuration}.
* This should be called off the UI thread.
* @param configurationNodeId The unique id of the {@link Configuration} for which to take the snapshot
* @param time If non-null, the snapshot is created from archived values.
* @param time If <code>null</code>, time is set to {@link Instant#now()}.
* @return A {@link List} of {@link SnapshotItem}s carrying snapshot values read by the service or read
* from an archiver.
*/
public List<SnapshotItem> takeSnapshot(String configurationNodeId, Instant time) throws Exception{
public List<SnapshotItem> takeSnapshotFromArchiver(String configurationNodeId, Instant time) throws Exception{
if(time == null){
return takeSnapshot(configurationNodeId);
}
else{
ConfigurationData configNode = getConfiguration(configurationNodeId);
List<ConfigPv> configPvList = configNode.getPvList();
List<SnapshotItem> snapshotItems = new ArrayList<>();
configPvList.forEach(configPv -> {
SnapshotItem snapshotItem = new SnapshotItem();
snapshotItem.setConfigPv(configPv);
snapshotItem.setValue(readFromArchiver(configPv.getPvName(), time));
if(configPv.getReadbackPvName() != null){
snapshotItem.setValue(readFromArchiver(configPv.getReadbackPvName(), time));
}
snapshotItems.add(snapshotItem);
});
return snapshotItems;
time = Instant.now();
}
ConfigurationData configNode = getConfiguration(configurationNodeId);
List<ConfigPv> configPvList = configNode.getPvList();
List<SnapshotItem> snapshotItems = new ArrayList<>();
Instant _time = time;
configPvList.forEach(configPv -> {
SnapshotItem snapshotItem = new SnapshotItem();
snapshotItem.setConfigPv(configPv);
snapshotItem.setValue(readFromArchiver(configPv.getPvName(), _time));
if(configPv.getReadbackPvName() != null){
snapshotItem.setValue(readFromArchiver(configPv.getReadbackPvName(), _time));
}
snapshotItems.add(snapshotItem);
});
return snapshotItems;
}

/**
Expand All @@ -475,15 +474,14 @@ private VType readFromArchiver(String pvName, Instant time){
if(indexSchemeSeparator > 0 && pvName.length() > indexSchemeSeparator){
pvName = pvName.substring(indexSchemeSeparator + 1);
}
// Prepend "alarm://"
// Prepend "archiver://"
pvName = "archive://" + pvName + "(" + TimestampFormats.SECONDS_FORMAT.format(time) + ")";
try {
PV pv = PVPool.getPV(pvName);
VType pvValue = pv.read();
PVPool.releasePV(pv);
return pvValue;
return pvValue == null ? VDisconnectedData.INSTANCE : pvValue;
} catch (Exception e) {
// Not found in archiver
return VDisconnectedData.INSTANCE;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2024 European Spallation Source ERIC.
*/

package org.phoebus.applications.saveandrestore.ui;

import org.phoebus.applications.saveandrestore.Messages;

public enum SnapshotMode {

/**
* Classic mode: read data from IOC
*/
READ_PVS(Messages.snapshotFromPvs),

/**
* Read PV data from archiver
*/
FROM_ARCHIVER(Messages.snapshotFromArchiver);

private final String name;

SnapshotMode(final String name) {
this.name = name;
}

@Override
public String toString(){
return name;
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,5 @@
/**
* Copyright (C) 2024 European Spallation Source ERIC.
* <p>
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.phoebus.applications.saveandrestore.ui.snapshot;

Expand All @@ -24,24 +10,42 @@
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import org.epics.vtype.*;
import org.epics.vtype.Alarm;
import org.epics.vtype.Display;
import org.epics.vtype.Time;
import org.epics.vtype.VEnum;
import org.epics.vtype.VNumber;
import org.epics.vtype.VNumberArray;
import org.epics.vtype.VString;
import org.epics.vtype.VStringArray;
import org.epics.vtype.VType;
import org.phoebus.applications.saveandrestore.Messages;
import org.phoebus.applications.saveandrestore.model.*;
import org.phoebus.applications.saveandrestore.model.ConfigPv;
import org.phoebus.applications.saveandrestore.model.ConfigurationData;
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.NodeType;
import org.phoebus.applications.saveandrestore.model.RestoreResult;
import org.phoebus.applications.saveandrestore.model.Snapshot;
import org.phoebus.applications.saveandrestore.model.SnapshotData;
import org.phoebus.applications.saveandrestore.model.SnapshotItem;
import org.phoebus.applications.saveandrestore.model.event.SaveAndRestoreEventReceiver;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreBaseController;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreService;
import org.phoebus.applications.saveandrestore.ui.SnapshotMode;
import org.phoebus.applications.saveandrestore.ui.VNoData;
import org.phoebus.framework.jobs.JobManager;
import org.phoebus.security.tokens.ScopedAuthenticationToken;
import org.phoebus.ui.dialog.DialogHelper;
import org.phoebus.ui.dialog.ExceptionDetailsErrorDialog;
import org.phoebus.ui.time.DateTimePane;

import java.time.Instant;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
Expand All @@ -55,6 +59,7 @@
public class SnapshotController extends SaveAndRestoreBaseController {


@SuppressWarnings("unused")
@FXML
private BorderPane borderPane;

Expand Down Expand Up @@ -113,7 +118,7 @@ public void initialize() {
*
* @param configurationNode A {@link Node} of type {@link org.phoebus.applications.saveandrestore.model.NodeType#CONFIGURATION}
*/
public void newSnapshot(Node configurationNode) {
public void initializeViewForNewSnapshot(Node configurationNode) {
this.configurationNode = configurationNode;
snapshotTab.updateTabTitle(Messages.unnamedSnapshot);
JobManager.schedule("Get configuration", monitor -> {
Expand Down Expand Up @@ -141,9 +146,9 @@ public void newSnapshot(Node configurationNode) {
public void takeSnapshot() {
disabledUi.set(true);
snapshotTab.setText(Messages.unnamedSnapshot);
snapshotTableViewController.takeSnapshot(snapshot -> {
snapshotTableViewController.takeSnapshot(snapshotControlsViewController.getDefaultSnapshotMode(), snapshot -> {
disabledUi.set(false);
if(snapshot != null){
if (snapshot != null) {
snapshotProperty.set(snapshot);
}
});
Expand Down Expand Up @@ -174,9 +179,7 @@ public void saveSnapshot(ActionEvent actionEvent) {
snapshot = SaveAndRestoreService.getInstance().saveSnapshot(configurationNode, snapshot);
snapshotProperty.set(snapshot);
Node _snapshotNode = snapshot.getSnapshotNode();
javafx.scene.Node jfxNode = (javafx.scene.Node) actionEvent.getSource();
String userData = (String) jfxNode.getUserData();
if (userData.equalsIgnoreCase("true")) {
if (snapshotControlsViewController.logAction()) {
eventReceivers.forEach(r -> r.snapshotSaved(_snapshotNode, this::showLoggingError));
}
snapshotControlsViewController.snapshotDataDirty.set(false);
Expand Down Expand Up @@ -362,13 +365,13 @@ public void restore(ActionEvent actionEvent) {
if (userData.equalsIgnoreCase("true")) {
eventReceivers.forEach(r -> r.snapshotRestored(snapshotProperty.get().getSnapshotNode(), restoreResultList, this::showLoggingError));
}
if(restoreResultList != null && !restoreResultList.isEmpty()){
if (restoreResultList != null && !restoreResultList.isEmpty()) {
showFailedRestoreResult(restoreResultList);
}
});
}

private void showFailedRestoreResult(List<RestoreResult> restoreResultList){
private void showFailedRestoreResult(List<RestoreResult> restoreResultList) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(Messages.restoreFailedPVs).append(System.lineSeparator());
stringBuilder.append(restoreResultList.stream()
Expand All @@ -383,6 +386,7 @@ private void showFailedRestoreResult(List<RestoreResult> restoreResultList){

/**
* Adds a snapshot for the sake of comparison with the one currently in view.
*
* @param snapshotNode A snapshot {@link Node} selected by user in the {@link javafx.scene.control.TreeView},
* i.e. a snapshot previously persisten in the service.
*/
Expand All @@ -402,43 +406,20 @@ public void addSnapshot(Node snapshotNode) {

/**
* Launches a date/time picker and then reads from archiver to construct an in-memory {@link Snapshot} used for comparison.
* @param configurationNode A {@link Node} of type {@link NodeType#CONFIGURATION}.
*/
public void addSnapshotFromArchiver(Node configurationNode){
DateTimePane dateTimePane = new DateTimePane();
Dialog<Instant> timePickerDialog = new Dialog<>();
timePickerDialog.setTitle(Messages.dateTimePickerTitle);
timePickerDialog.getDialogPane().setContent(dateTimePane);
timePickerDialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
timePickerDialog.setResultConverter(b -> {
if(b.equals(ButtonType.OK)){
return dateTimePane.getInstant();
}
return null;
});
Optional<Instant> time = timePickerDialog.showAndWait();
if(time.isEmpty()){ // User cancels date/time picker dialog
return;
}
public void addSnapshotFromArchiver() {
disabledUi.set(true);
JobManager.schedule("Add snapshot from archiver", monitor -> {
List<SnapshotItem> snapshotItems;
try {
snapshotItems = SaveAndRestoreService.getInstance().takeSnapshot(configurationNode.getUniqueId(), time.get());
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Failed to query archiver for data", e);
snapshotTableViewController.takeSnapshot(SnapshotMode.FROM_ARCHIVER, snapshot -> {
if(snapshot == null){
disabledUi.set(false);
return;
}
Snapshot snapshot = new Snapshot();
snapshot.setSnapshotNode(Node.builder().nodeType(NodeType.SNAPSHOT).name(Messages.archiver).created(new Date(time.get().toEpochMilli())).build());
SnapshotData snapshotData = new SnapshotData();
snapshotData.setUniqueId("anonymous");
snapshotData.setSnapshotItems(snapshotItems);
snapshot.setSnapshotData(snapshotData);
Platform.runLater(() -> {
snapshotTableViewController.addSnapshot(snapshot);
disabledUi.set(false);
try {
snapshotTableViewController.addSnapshot(snapshot);
} finally {
disabledUi.set(false);
}
});
});
}
Expand Down
Loading

0 comments on commit aa4cc77

Please sign in to comment.