diff --git a/app/display/runtime/src/main/java/org/csstudio/display/builder/runtime/WidgetRuntime.java b/app/display/runtime/src/main/java/org/csstudio/display/builder/runtime/WidgetRuntime.java index 8cfed3cbbf..b065d77d28 100644 --- a/app/display/runtime/src/main/java/org/csstudio/display/builder/runtime/WidgetRuntime.java +++ b/app/display/runtime/src/main/java/org/csstudio/display/builder/runtime/WidgetRuntime.java @@ -106,7 +106,8 @@ public class WidgetRuntime { */ // 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 writable_pvs = null; + // Set pv_name as Key to find the corresponding RuntimePV + private volatile Map writable_pvs = null; /** * Handlers for widget's behaviorScripts property, @@ -229,14 +230,14 @@ public void start() { // Prepare action-related PVs final List actions = widget.propActions().getValue().getActions(); if (actions.size() > 0) { - final List action_pvs = new ArrayList<>(); + final Map 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); @@ -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 safe_pvs = writable_pvs; - if (safe_pvs != null) - for (final RuntimePV pv : safe_pvs) - if (pv.getName().equals(name_to_check)) { + final Map 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 + "')"); + } + } } /** @@ -441,13 +444,15 @@ public void stop() { awaitStartup(); widget.propClass().removePropertyListener(update_widget_class); - final List 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 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); diff --git a/app/display/runtime/src/test/java/org/csstudio/display/builder/runtime/test/WidgetRuntimeTest.java b/app/display/runtime/src/test/java/org/csstudio/display/builder/runtime/test/WidgetRuntimeTest.java new file mode 100644 index 0000000000..4d52b2fd81 --- /dev/null +++ b/app/display/runtime/src/test/java/org/csstudio/display/builder/runtime/test/WidgetRuntimeTest.java @@ -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 actionList = new ArrayList(); + 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 ofWidget = new WidgetRuntime(); + 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); + } + } + +}