Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ public class WidgetRuntime<MW extends Widget> {
*/
// This is empty for most widgets, or contains very few PVs,
// so using List with linear lookup by name and not a HashMap
private volatile List<RuntimePV> writable_pvs = null;
// Set pv_name as Key to find the corresponding RuntimePV
private volatile Map<String,RuntimePV> writable_pvs = null;

/**
* Handlers for widget's behaviorScripts property,
Expand Down Expand Up @@ -229,14 +230,14 @@ public void start() {
// Prepare action-related PVs
final List<ActionInfo> actions = widget.propActions().getValue().getActions();
if (actions.size() > 0) {
final List<RuntimePV> action_pvs = new ArrayList<>();
final Map<String, RuntimePV> action_pvs = new HashMap<>();
for (final ActionInfo action : actions) {
if (action instanceof WritePVAction) {
final String pv_name = ((WritePVAction) action).getPV();
try {
final String expanded = MacroHandler.replace(widget.getMacrosOrProperties(), pv_name);
final RuntimePV pv = PVFactory.getPV(expanded);
action_pvs.add(pv);
action_pvs.put(expanded, pv);
addPV(pv, true);
} catch (Exception ex) {
logger.log(Level.WARNING, widget + " cannot start action to write PV '" + pv_name + "'", ex);
Expand Down Expand Up @@ -407,18 +408,20 @@ public void writePV(final String pv_name, final Object value) throws Exception {
name_to_check = name_to_check.substring(0, sep);
}
awaitStartup();
final List<RuntimePV> safe_pvs = writable_pvs;
if (safe_pvs != null)
for (final RuntimePV pv : safe_pvs)
if (pv.getName().equals(name_to_check)) {
final Map<String, RuntimePV> safe_pvs = writable_pvs;
if (safe_pvs != null) {
final RuntimePV pv = safe_pvs.get(name_to_check);
if(pv != null) {
try {
pv.write(value);
} catch (final Exception ex) {
throw new Exception("Failed to write " + value + " to PV " + name_to_check, ex);
}
return;
}
throw new Exception("Unknown PV '" + pv_name + "' (expanded: '" + name_to_check + "')");
else {
throw new Exception("Unknown PV '" + pv_name + "' (expanded: '" + name_to_check + "')");
}
}
}

/**
Expand All @@ -441,13 +444,15 @@ public void stop() {
awaitStartup();
widget.propClass().removePropertyListener(update_widget_class);

final List<RuntimePV> safe_pvs = writable_pvs;
if (safe_pvs != null) {
for (final RuntimePV pv : safe_pvs) {
removePV(pv);
PVFactory.releasePV(pv);
if(writable_pvs != null && !writable_pvs.isEmpty()) {
final Collection<RuntimePV> safe_pvs = writable_pvs.values();
if (safe_pvs != null) {
for (final RuntimePV pv : safe_pvs) {
removePV(pv);
PVFactory.releasePV(pv);
}
writable_pvs = null;
}
writable_pvs = null;
}

final PVNameToValueBinding binding = pv_name_binding.getAndSet(null);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.csstudio.display.builder.runtime.test;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.fail;

import java.util.ArrayList;
import java.util.List;

import org.csstudio.display.actions.WritePVAction;
import org.csstudio.display.builder.model.properties.ActionInfos;
import org.csstudio.display.builder.model.properties.CommonWidgetProperties;
import org.csstudio.display.builder.model.spi.ActionInfo;
import org.csstudio.display.builder.model.widgets.ActionButtonWidget;
import org.csstudio.display.builder.runtime.WidgetRuntime;
import org.csstudio.display.builder.runtime.pv.RuntimePV;
import org.csstudio.display.builder.runtime.script.PVUtil;
import org.junit.jupiter.api.Test;
import org.phoebus.pv.PVPool;
import org.phoebus.pv.loc.LocalPVFactory;

public class WidgetRuntimeTest {

@Test
public void testWriteAction()
{
//Test for Write action on default pv different from ca see PR
//https://github.com/ControlSystemStudio/phoebus/pull/3412
//Force default data source to loc://
PVPool.default_type = LocalPVFactory.TYPE;
String pv_name = "my_pv";
try {
RuntimePV pv = PVUtil.createPV(pv_name, 0);
//First init value
double initValue = 10;
//VDouble val = VDouble.of(initValue, Alarm.none(), org.epics.vtype.Time.now(), org.epics.vtype.Display.none());
pv.write(initValue);
//PVUtil.writePV(pv_name, initValue, 0);
double readValue = PVUtil.getDouble(pv);
//Test in standard way
assertThat(readValue, equalTo(initValue));

//Test with WidgetRuntime (write Action)
ActionButtonWidget widget = new ActionButtonWidget();
widget.setPropertyValue(CommonWidgetProperties.propPVName.getName(), pv_name);
//Add write action
//Write new value
double newValue = 20;

List<ActionInfo> actionList = new ArrayList<ActionInfo>();
ActionInfo writeAction = new WritePVAction("Write value", pv_name, String.valueOf(newValue));
actionList.add(writeAction);
ActionInfos actInfos = new ActionInfos(actionList, true);
widget.setPropertyValue(CommonWidgetProperties.propActions.getName(), actInfos);

//Create Widget Runtime
WidgetRuntime<ActionButtonWidget> ofWidget = new WidgetRuntime<ActionButtonWidget>();
ofWidget.initialize(widget);
ofWidget.addPV(pv, true);
ofWidget.start();

ofWidget.writePV(pv_name, newValue);

//Test the new value
readValue = PVUtil.getDouble(pv);
//Test if the new value is ok
assertThat(readValue, equalTo(newValue));

//Generate a stacktrace to fix in LocalPVFactory
ofWidget.stop();

} catch (Exception e) {
e.printStackTrace();
fail(e);
}
}

}