Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PVA RPC Action #2906

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,6 @@ public interface XMLTags
WIDGET = "widget",
WIDTH = "width",
X = "x",
Y = "y";
Y = "y",
RETURN_PV = "return_pv";
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Objects;

import org.csstudio.display.builder.model.Messages;
Expand Down Expand Up @@ -36,7 +37,10 @@ public enum ActionType
/** Open a file with its associated tool */
OPEN_FILE(Messages.ActionOpenFile, "/icons/open_file.png"),
/** Open a wen page in system browser */
OPEN_WEBPAGE(Messages.ActionOpenWebPage, "/icons/web_browser.png");
OPEN_WEBPAGE(Messages.ActionOpenWebPage, "/icons/web_browser.png"),
/** Make an RPC call over PVA */
CALL_PV("Call PV", "/icons/call_pv.png");
// CALL_PV(Messages.ActionCallPV, "/icos/call_pv.png");

private final String name, icon_path;

Expand Down Expand Up @@ -83,6 +87,9 @@ public static ActionInfo createAction(final ActionType type)
return new OpenFileActionInfo(type.toString(), "");
case OPEN_WEBPAGE:
return new OpenWebpageActionInfo(type.toString(), "");
case CALL_PV:
return new CallPVActionInfo(type.toString(), "$(pv_name)", new HashMap<>(), "loc://return_pv");

default:
throw new IllegalStateException("Unknown type " + type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,7 @@

import static org.csstudio.display.builder.model.ModelPlugin.logger;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.*;
import java.util.logging.Level;

import javax.xml.stream.XMLStreamWriter;
Expand Down Expand Up @@ -46,6 +41,7 @@ public class ActionsWidgetProperty extends WidgetProperty<ActionInfos>
private static final String EXECUTE_COMMAND = "command";
private static final String OPEN_FILE = "open_file";
private static final String OPEN_WEBPAGE = "open_webpage";
private static final String CALL_PV = "call_pv";

/** Constructor
* @param descriptor Property descriptor
Expand Down Expand Up @@ -141,6 +137,28 @@ else if (info instanceof WritePVActionInfo)
writer.writeCharacters(action.getValue());
writer.writeEndElement();
}
else if (info instanceof CallPVActionInfo)
{
final CallPVActionInfo action = (CallPVActionInfo) info;
writer.writeAttribute(XMLTags.TYPE, CALL_PV);
writer.writeStartElement(XMLTags.PV_NAME);
writer.writeCharacters(action.getPV());
writer.writeEndElement();
writer.writeStartElement(XMLTags.RETURN_PV);
writer.writeCharacters(action.getReturnPV());
writer.writeEndElement();
for (Map.Entry<String, String> parameter: action.getArgs().entrySet()) {
writer.writeStartElement("argument");
writer.writeStartElement("name");
writer.writeCharacters(parameter.getKey());
writer.writeEndElement();

writer.writeStartElement("value");
writer.writeCharacters(parameter.getValue());
writer.writeEndElement();
writer.writeEndElement();
}
}
else if (info instanceof ExecuteScriptActionInfo)
{
final ExecuteScriptActionInfo action = (ExecuteScriptActionInfo) info;
Expand Down Expand Up @@ -280,6 +298,29 @@ else if (WRITE_PV.equalsIgnoreCase(type)) // legacy used uppercase type name
description = Messages.ActionWritePV;
actions.add(new WritePVActionInfo(description, pv_name, value));
}
else if (CALL_PV.equals(type)) {
final String pv_name = XMLUtil.getChildString(action_xml, XMLTags.PV_NAME).orElse("");
if (pv_name.isEmpty())
logger.log(Level.WARNING, "Ignoring <action type='" + CALL_PV + "'> with empty <pv_name> on " + getWidget());

final String return_pv = XMLUtil.getChildString(action_xml, XMLTags.RETURN_PV).orElse("loc://return_pv");
final HashMap<String, String> args = new HashMap<>();

for (Element argument: XMLUtil.getChildElements(action_xml, "argument")) {
String name = XMLUtil.getChildString(argument, "name").orElse("");
String value = XMLUtil.getChildString(argument, "value").orElse("");

if (name.isEmpty() || value.isEmpty()) {
logger.log(Level.WARNING, "Ignoring <argument> with empty <name> or <value> on " + getWidget());
continue;
}

args.put(name, value);
}

actions.add(new CallPVActionInfo(description, pv_name, args, return_pv));

}
else if (EXECUTE_SCRIPT.equals(type))
{
// <script file="EmbeddedPy">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.csstudio.display.builder.model.properties;

import java.util.HashMap;

public class CallPVActionInfo extends ActionInfo {
private final String pv;
private final HashMap<String, String> args;
private final String returnPV;

/**
* @param description Action description
*/
public CallPVActionInfo(final String description, final String pv, final HashMap<String, String> args, final String returnPV) {
super(description);
this.pv = pv;
this.args = args;
this.returnPV = returnPV;
}

@Override
public ActionType getType() {
return ActionType.CALL_PV;
}

public String getPV() {
return pv;
}

public HashMap<String, String> getArgs() {
return args;
}

public String getReturnPV() {
return returnPV;
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
ActionCallPV=Call PVA RPC
ActionExecuteCommand=Execute Command
ActionExecuteScript=Execute Script
ActionOpenDisplay=Open Display
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ public class Messages
ActionsDialog_ExecuteAll,
ActionsDialog_FilePath,
ActionsDialog_Info,
ActionsDialog_Parameters,
ActionsDialog_AddParameter,
ActionsDialog_RemoveParameter,
ActionsDialog_PVName,
ActionsDialog_ReturnPV,
ActionsDialog_ScriptPath,
ActionsDialog_ScriptText,
ActionsDialog_Title,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ public ActionsDialogActionItem(Widget widget, ActionInfo actionInfo){
return null;
});
break;
case CALL_PV:
fxmlLoader.setLocation(this.getClass().getResource("CallPVActionDetails.fxml"));
fxmlLoader.setControllerFactory(clazz -> {
try {
return clazz.getConstructor(ActionInfo.class).newInstance(actionInfo);
} catch (Exception e) {
Logger.getLogger(ActionsDialogActionItem.class.getName()).log(Level.SEVERE, "Failed to construct CallPVActionDetailsController", e);
}
return null;
});
break;
case EXECUTE_SCRIPT:
fxmlLoader.setLocation(this.getClass().getResource("ExecuteScriptActionDetails.fxml"));
fxmlLoader.setControllerFactory(clazz -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package org.csstudio.display.builder.representation.javafx.actionsdialog;

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.util.Pair;
import org.csstudio.display.builder.model.properties.ActionInfo;
import org.csstudio.display.builder.model.properties.CallPVActionInfo;
import org.phoebus.ui.autocomplete.PVAutocompleteMenu;

import java.util.HashMap;

public class CallPVActionDetailsController implements ActionDetailsController {
private final CallPVActionInfo actionInfo;

@FXML
private TextField returnPV;
@FXML
private TextField description;
@FXML
private TextField pvName;

@FXML
private Button addParameter;

@FXML
private Button removeParameter;

@FXML
private TableView<Pair<String, String>> parameterTable;

private final StringProperty descriptionProperty = new SimpleStringProperty();
private final StringProperty pvNameProperty = new SimpleStringProperty();
private final StringProperty returnPVProperty = new SimpleStringProperty();

private final ObservableList<Pair<String, String>> parameterList = FXCollections.observableArrayList();

public CallPVActionDetailsController(ActionInfo actionInfo) {
this.actionInfo = (CallPVActionInfo) actionInfo;
}

@FXML
public void initialize(){
descriptionProperty.setValue(actionInfo.getDescription());
pvNameProperty.setValue(actionInfo.getPV());
returnPVProperty.setValue(actionInfo.getReturnPV());

description.textProperty().bindBidirectional(descriptionProperty);
pvName.textProperty().bindBidirectional(pvNameProperty);
returnPV.textProperty().bindBidirectional(returnPVProperty);

parameterTable.setItems(parameterList);
parameterTable.setEditable(true);

TableColumn<Pair<String, String>, String> nameColumn = new TableColumn<>("Parameter");
nameColumn.setCellFactory(TextFieldTableCell.forTableColumn());
nameColumn.setCellValueFactory(new PropertyValueFactory<>("key"));
nameColumn.setEditable(true);

TableColumn<Pair<String, String>, String> parameterColumn = new TableColumn<>("Value");
parameterColumn.setCellFactory(TextFieldTableCell.forTableColumn());
parameterColumn.setCellValueFactory(new PropertyValueFactory<>("value"));
parameterColumn.setEditable(true);

parameterTable.getColumns().add(nameColumn);
parameterTable.getColumns().add(parameterColumn);

addParameter.setOnAction(e -> {
parameterList.add(new Pair<>("A", "B"));
System.out.println(parameterList.toString());
});

removeParameter.setOnAction(e -> {
Pair<String, String> selectedItem = parameterTable.getSelectionModel().getSelectedItem();
parameterTable.getItems().remove(selectedItem);
});

PVAutocompleteMenu.INSTANCE.attachField(pvName);
}


@Override
public ActionInfo getActionInfo() {
HashMap<String, String> parameters = new HashMap<>();


for (Pair<String, String> pair: parameterList) {
parameters.put(pair.getKey(), pair.getValue());
}

System.out.println(parameters);

return new CallPVActionInfo(
this.descriptionProperty.get(),
this.pvNameProperty.get(),
parameters,
this.returnPVProperty.get()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>

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

<GridPane hgap="10.0" vgap="10.0" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.csstudio.display.builder.representation.javafx.actionsdialog.CallPVActionDetailsController">
<children>
<Label text="%ActionsDialog_Description" />
<TextField fx:id="description" text="Call PV" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS" />
<Label text="%ActionsDialog_PVName" GridPane.rowIndex="1" />
<TextField fx:id="pvName" text="PV Name" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS" GridPane.rowIndex="1" />
<Label text="%ActionsDialog_ReturnPV" GridPane.rowIndex="2" />
<TextField fx:id="returnPV" text="Return PV" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS" GridPane.rowIndex="2" />
<TableView fx:id="parameterTable" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="3" />
<VBox prefHeight="200.0" prefWidth="100.0" spacing="10.0" GridPane.rowIndex="3">
<children>
<Label text="%ActionsDialog_Parameters" />
<Button fx:id="addParameter" mnemonicParsing="false" text="%ActionsDialog_AddParameter" />
<Button fx:id="removeParameter" mnemonicParsing="false" text="%ActionsDialog_RemoveParameter" />
</children>
<GridPane.margin>
<Insets />
</GridPane.margin>
</VBox>
</children>
<columnConstraints>
<ColumnConstraints />
<ColumnConstraints />
</columnConstraints>
<rowConstraints>
<RowConstraints />
<RowConstraints />
<RowConstraints />
<RowConstraints minHeight="10.0" prefHeight="200.0" />
</rowConstraints>
</GridPane>
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ 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_AddParameter=Add Parameter...
ActionsDialog_RemoveParameter=Remove Parameter...
ActionsDialog_Parameters=Parameters
ActionsDialog_ReturnPV=Return PV:
ActionsDialog_ScriptPath=Script File:
ActionsDialog_ScriptText=Embedded Script Text:
ActionsDialog_Title=Actions
Expand Down
Loading