Skip to content
Open
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 @@ -22,6 +22,7 @@
public static String ActionCloseDisplay,
ActionExecuteCommand,
ActionExecuteScript,
ActionOpenDataBrowser,

Check warning on line 25 in app/display/actions/src/main/java/org/csstudio/display/actions/Messages.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Make ActionOpenDataBrowser a static final constant or non-public and provide accessors if needed.

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AZ38b6Fvc55ubllPmSD7&open=AZ38b6Fvc55ubllPmSD7&pullRequest=3796

Check warning on line 25 in app/display/actions/src/main/java/org/csstudio/display/actions/Messages.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this field "ActionOpenDataBrowser" to match the regular expression '^[a-z][a-zA-Z0-9]*$'.

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AZ38b6Fvc55ubllPmSD9&open=AZ38b6Fvc55ubllPmSD9&pullRequest=3796

Check warning on line 25 in app/display/actions/src/main/java/org/csstudio/display/actions/Messages.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Make this "public static ActionOpenDataBrowser" field final

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AZ38b6Fvc55ubllPmSD8&open=AZ38b6Fvc55ubllPmSD8&pullRequest=3796
ActionOpenDisplay,
ActionOpenFile,
ActionOpenWebPage,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package org.csstudio.display.actions;

import javafx.scene.image.Image;
import org.csstudio.display.builder.model.persist.ModelReader;
import org.csstudio.display.builder.model.persist.ModelWriter;
import org.csstudio.display.builder.model.persist.XMLTags;
import org.csstudio.display.builder.model.properties.ActionInfoBase;
import org.csstudio.display.builder.representation.javafx.actionsdialog.ActionsDialog;
import org.phoebus.framework.persistence.XMLUtil;
import org.phoebus.ui.javafx.ImageCache;
import org.w3c.dom.Element;

import javax.xml.stream.XMLStreamWriter;

public class OpenDataBrowserAction extends ActionInfoBase {

public static final String OPEN_DATA_BROWSER = "open_data_browser";
private static final Integer PRIORITY = 10;
private String pvs = "$(pv_name)";
private String timeframe = "1 hour";

@SuppressWarnings("unused")
/**
* Do not remove, needed by SPI framework.
*/
public OpenDataBrowserAction() {
this.description = Messages.ActionOpenDataBrowser;
this.type = OPEN_DATA_BROWSER;
}

public OpenDataBrowserAction(String description, String pvs, String timeframe) {
this.pvs = pvs;
this.timeframe = timeframe;
this.description = description;
this.type = OPEN_DATA_BROWSER;
}

@Override
public Image getImage() {
return ImageCache.getImage(ActionsDialog.class, "/icons/databrowser.png");
}

@Override
public Integer getPriority() {
return PRIORITY;
}

@Override
public void readFromXML(ModelReader modelReader, Element actionXml) {
pvs = XMLUtil.getChildString(actionXml, XMLTags.PV_NAME).orElse("");
timeframe = XMLUtil.getChildString(actionXml, XMLTags.TIME_FRAME).orElse("");

if (description.isEmpty())
description = Messages.ActionOpenDataBrowser;
}

@Override
public void writeToXML(ModelWriter modelWriter, XMLStreamWriter writer) throws Exception {
writer.writeAttribute(XMLTags.TYPE, OPEN_DATA_BROWSER);
writeDescriptionToXML(writer, description);
writer.writeStartElement(XMLTags.PV_NAME);
writer.writeCharacters(pvs);
writer.writeEndElement();
writer.writeStartElement(XMLTags.TIME_FRAME);
writer.writeCharacters(timeframe);
writer.writeEndElement();
}

public String getPVs() {
return pvs;
}

public String getTimeframe() {
return timeframe;
}

@Override
public boolean matchesAction(String actionId) {
return actionId.equalsIgnoreCase(OPEN_DATA_BROWSER);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.csstudio.display.actions;

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import org.csstudio.display.builder.model.ActionControllerBase;
import org.csstudio.display.builder.model.Widget;
import org.csstudio.display.builder.model.spi.ActionInfo;

/**
* FXML Controller for the open data browser action editor
*/
public class OpenDataBrowserActionController extends ActionControllerBase {

@FXML
private TextField pvNames;

@FXML
private TextField timeframe;

private final StringProperty pvNameProperty = new SimpleStringProperty();
private final StringProperty timeframeProperty = new SimpleStringProperty();

public OpenDataBrowserActionController(Widget widget, OpenDataBrowserAction openDataBrowserActionInfo){

Check warning on line 25 in app/display/actions/src/main/java/org/csstudio/display/actions/OpenDataBrowserActionController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this unused method parameter "widget".

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AZ38b6E5c55ubllPmSD4&open=AZ38b6E5c55ubllPmSD4&pullRequest=3796
descriptionProperty.set(openDataBrowserActionInfo.getDescription());
pvNameProperty.setValue(openDataBrowserActionInfo.getPVs());
timeframeProperty.setValue(openDataBrowserActionInfo.getTimeframe());
}

/**
* Init
*/
@FXML
public void initialize() {

Check warning on line 35 in app/display/actions/src/main/java/org/csstudio/display/actions/OpenDataBrowserActionController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add the "@Override" annotation above this method signature

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AZ38b6E5c55ubllPmSD5&open=AZ38b6E5c55ubllPmSD5&pullRequest=3796
super.initialize();
pvNames.textProperty().bindBidirectional(pvNameProperty);
timeframe.textProperty().bindBidirectional(timeframeProperty);
}

public ActionInfo getActionInfo(){
return new OpenDataBrowserAction(descriptionProperty.get(), pvNameProperty.get(), timeframeProperty.get());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.csstudio.display.actions;

import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import org.csstudio.display.builder.model.Widget;
import org.csstudio.display.builder.model.spi.ActionEditor;
import org.csstudio.display.builder.model.spi.ActionInfo;
import org.phoebus.framework.nls.NLS;

import java.io.IOException;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Editor for {@link OpenDataBrowserAction}.
*/
public class OpenDataBrowserActionEditor implements ActionEditor {

private OpenDataBrowserActionController openDataBrowserActionController;
private Node editorUi;

@Override
public boolean matchesAction(String type) {
return OpenDataBrowserAction.OPEN_DATA_BROWSER.equalsIgnoreCase(type);
}

@Override
public ActionInfo getActionInfo() {
return openDataBrowserActionController.getActionInfo();
}

@Override
public Node getEditorUi() {
return editorUi;
}

@Override
public void configure(Widget widget, ActionInfo actionInfo) {
ResourceBundle resourceBundle = NLS.getMessages(Messages.class);

FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setResources(resourceBundle);
fxmlLoader.setLocation(this.getClass().getResource("OpenDataBrowserAction.fxml"));
fxmlLoader.setControllerFactory(clazz -> {
try {
return clazz.getConstructor(Widget.class, OpenDataBrowserAction.class).newInstance(widget, actionInfo);
} catch (Exception e) {
Logger.getLogger(OpenDataBrowserActionEditor.class.getName()).log(Level.SEVERE, "Failed to construct OpenDataBrowserActionController", e);
}
return null;
});

try {
editorUi = fxmlLoader.load();
openDataBrowserActionController = fxmlLoader.getController();
} catch (IOException e) {
Logger.getLogger(OpenDataBrowserActionEditor.class.getName()).log(Level.SEVERE, "Failed to load the OpenDataBrowserAction UI", e);
throw new RuntimeException(e);

Check warning on line 59 in app/display/actions/src/main/java/org/csstudio/display/actions/OpenDataBrowserActionEditor.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace generic exceptions with specific library exceptions or a custom exception.

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AZ38b6FAc55ubllPmSD6&open=AZ38b6FAc55ubllPmSD6&pullRequest=3796
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ org.csstudio.display.actions.OpenDisplayActionEditor
org.csstudio.display.actions.OpenFileActionEditor
org.csstudio.display.actions.OpenWebPageActionEditor
org.csstudio.display.actions.CloseDisplayActionEditor
org.csstudio.display.actions.OpenDataBrowserActionEditor
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ org.csstudio.display.actions.ExecuteScriptAction
org.csstudio.display.actions.ExecuteCommandAction
org.csstudio.display.actions.OpenFileAction
org.csstudio.display.actions.OpenWebPageAction
org.csstudio.display.actions.CloseDisplayAction
org.csstudio.display.actions.CloseDisplayAction
org.csstudio.display.actions.OpenDataBrowserAction
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<!--
~ Copyright (C) 2024 European Spallation Source ERIC.
-->

<GridPane hgap="10.0" vgap="10.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.csstudio.display.actions.OpenDataBrowserActionController">
<children>
<Label text="%ActionsDialog_Description" />
<TextField fx:id="description" text="Open Data Browser" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS" />
<Label text="%ActionsDialog_PVNames" GridPane.rowIndex="1" />
<TextField fx:id="pvNames" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS" GridPane.rowIndex="1" />
<Label text="%ActionsDialog_PVNames_Help" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS" GridPane.rowIndex="2" />
<Label text="%ActionsDialog_Timeframe" GridPane.rowIndex="3" />
<TextField fx:id="timeframe" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS" GridPane.rowIndex="3" />
<Label text="%ActionsDialog_Timeframe_Help" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS" GridPane.rowIndex="4" />
</children>
<columnConstraints>
<ColumnConstraints />
<ColumnConstraints />
</columnConstraints>
<rowConstraints>
<RowConstraints />
<RowConstraints />
<RowConstraints />
<RowConstraints />
<RowConstraints />
</rowConstraints>
</GridPane>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
ActionCloseDisplay=Close Display
ActionExecuteCommand=Execute Command
ActionExecuteScript=Execute Script
ActionOpenDataBrowser=Open Data Browser
ActionOpenDisplay=Open Display
ActionsDialog_Description=Description:
ActionOpenFile=Open File
Expand All @@ -14,8 +15,12 @@ ActionsDialog_ExecuteAll=Execute all actions as one
ActionsDialog_FilePath=File Path:
ActionsDialog_Info=Configure actions which open displays, write PVs etc.
ActionsDialog_PVName=PV Name:
ActionsDialog_PVNames=PV Names:
ActionsDialog_PVNames_Help=List names of PVs separated by a space (e.g. 'PV:1 PV:2')
ActionsDialog_ScriptPath=Script File:
ActionsDialog_ScriptText=Embedded Script Text:
ActionsDialog_Timeframe=Timeframe Start:
ActionsDialog_Timeframe_Help=Format: temporal (e.g. '1 hour', '1 day') or absolute (e.g. 'yyyy-MM-dd HH:mm:ss')
ActionsDialog_URL=URL:
ActionsDialog_Value=Value:
Add=Add
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public interface XMLTags
SCRIPT = "script",
TARGET = "target",
TEXT = "text",
TIME_FRAME = "timeframe",
TRIGGER = "trigger",
TYPE = "type",
URL = "url",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package org.csstudio.display.builder.runtime.app.actionhandlers;

import org.csstudio.display.actions.OpenDataBrowserAction;
import org.csstudio.display.builder.model.DisplayModel;
import org.csstudio.display.builder.model.Widget;
import org.csstudio.display.builder.model.spi.ActionHandler;
import org.csstudio.display.builder.model.spi.ActionInfo;
import org.csstudio.display.builder.representation.ToolkitRepresentation;
import org.csstudio.trends.databrowser3.DataBrowserApp;
import org.csstudio.trends.databrowser3.DataBrowserInstance;
import org.csstudio.trends.databrowser3.model.PVItem;
import org.phoebus.framework.macros.MacroHandler;
import org.phoebus.framework.macros.MacroValueProvider;
import org.phoebus.framework.util.ResourceParser;
import org.phoebus.framework.workbench.ApplicationService;
import org.phoebus.util.time.TimeParser;
import org.phoebus.util.time.TimeRelativeInterval;
import org.phoebus.util.time.TimestampFormats;

import java.time.Instant;
import java.util.logging.Level;
import java.util.logging.Logger;

public class OpenDataBrowserActionHandler implements ActionHandler {

private final Logger logger = Logger.getLogger(OpenDataBrowserActionHandler.class.getName());

@Override
public void handleAction(Widget sourceWidget, ActionInfo pluggableActionInfo) {
OpenDataBrowserAction action = (OpenDataBrowserAction) pluggableActionInfo;

try
{
final DisplayModel model = sourceWidget.getTopDisplayModel();
final ToolkitRepresentation<Object, Object> toolkit = ToolkitRepresentation.getToolkit(model);
final MacroValueProvider macros = sourceWidget.getMacrosOrProperties();
final String pvNames = action.getPVs().strip();
final String timeframe = action.getTimeframe().strip();

// Build URI from list of PVs
String pvURI = "pv://?";
String[] pvs = pvNames.split(" ");
for (int i = 0; i < pvs.length; i++) {
try {

Check warning on line 44 in app/display/runtime/src/main/java/org/csstudio/display/builder/runtime/app/actionhandlers/OpenDataBrowserActionHandler.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested try block into a separate method.

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AZ38b6ATc55ubllPmSD1&open=AZ38b6ATc55ubllPmSD1&pullRequest=3796
pvs[i] = MacroHandler.replace(macros, pvs[i]);
pvURI = pvURI.concat(pvs[i]);
if (i < pvs.length - 1)
pvURI = pvURI.concat("&");
} catch (Exception ignore) {
// NOP
}
}
final String finalPvURI = pvURI;

// Set timeframe
TimeRelativeInterval timeInterval;
if (timeframe.contains(":") || timeframe.contains("-")){
// An absolute time has been provided
Instant ints = TimestampFormats.parse(timeframe);
timeInterval = TimeRelativeInterval.of(ints,
TimeParser.parseTemporalAmount(TimeParser.NOW));
} else {
// Temporal timeframe
timeInterval = TimeRelativeInterval.of(TimeParser.parseTemporalAmount(timeframe),
TimeParser.parseTemporalAmount(TimeParser.NOW));
}

toolkit.submit(() ->
{ // Create databrowser instance
DataBrowserInstance instance = ApplicationService.createInstance(DataBrowserApp.NAME,
ResourceParser.createResourceURI(finalPvURI));

// Set the default archiver
for (String pv: pvs) {
PVItem pvItem = (PVItem) instance.getModel().getItem(pv);
pvItem.useDefaultArchiveDataSources();
}

// Set timeframe
instance.getModel().setTimerange(timeInterval);

return null;
});
}
catch (Throwable ex)

Check warning on line 85 in app/display/runtime/src/main/java/org/csstudio/display/builder/runtime/app/actionhandlers/OpenDataBrowserActionHandler.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Catch Exception instead of Throwable.

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AZ38b6ATc55ubllPmSD2&open=AZ38b6ATc55ubllPmSD2&pullRequest=3796
{
logger.log(Level.WARNING, action+" failed. Cannot open data browser", ex);

Check warning on line 87 in app/display/runtime/src/main/java/org/csstudio/display/builder/runtime/app/actionhandlers/OpenDataBrowserActionHandler.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Lambda should be used to defer string concatenation.

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AZ38b6ATc55ubllPmSD3&open=AZ38b6ATc55ubllPmSD3&pullRequest=3796
}
}

@Override
public boolean matches(ActionInfo pluggableActionInfo) {
return pluggableActionInfo.getType().equalsIgnoreCase(OpenDataBrowserAction.OPEN_DATA_BROWSER);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ org.csstudio.display.builder.runtime.app.actionhandlers.ExecuteCommandActionHand
org.csstudio.display.builder.runtime.app.actionhandlers.OpenFileActionHandler
org.csstudio.display.builder.runtime.app.actionhandlers.OpenWebPageActionHandler
org.csstudio.display.builder.runtime.app.actionhandlers.CloseDisplayActionHandler
org.csstudio.display.builder.runtime.app.actionhandlers.OpenDataBrowserActionHandler
Loading