diff --git a/core/pv-tango/.gitignore b/core/pv-tango/.gitignore new file mode 100644 index 0000000000..239d9b3837 --- /dev/null +++ b/core/pv-tango/.gitignore @@ -0,0 +1,2 @@ +/.project +/.classpath diff --git a/core/pv-tango/src/main/java/org/phoebus/pv/tango/AdvancedEnumDisplay.java b/core/pv-tango/src/main/java/org/phoebus/pv/tango/AdvancedEnumDisplay.java new file mode 100644 index 0000000000..abc8262c7e --- /dev/null +++ b/core/pv-tango/src/main/java/org/phoebus/pv/tango/AdvancedEnumDisplay.java @@ -0,0 +1,43 @@ +package org.phoebus.pv.tango; + +import java.util.List; + +import org.epics.vtype.Display; +import org.epics.vtype.DisplayProvider; +import org.epics.vtype.EnumDisplay; + +public class AdvancedEnumDisplay extends EnumDisplay implements DisplayProvider { + + private EnumDisplay source ; + private Display display ; + + private AdvancedEnumDisplay(Display display,EnumDisplay source) { + this.source = source; + this.display = display; + } + + @Override + public List getChoices() { + return source != null ? source.getChoices() : null; + } + + @Override + public Display getDisplay() { + return display; + } + + + /** + * New EnumDisplay with the given choices. + * + * @param choices the enum choices + * @return the new display + */ + public static EnumDisplay of(final Display display, final String... choices) { + EnumDisplay enumDisp = choices != null ? EnumDisplay.of(choices) : null; + return new AdvancedEnumDisplay(display,enumDisp); + } + + + +} diff --git a/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoAttributeHelper.java b/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoAttributeHelper.java new file mode 100644 index 0000000000..43cc58326d --- /dev/null +++ b/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoAttributeHelper.java @@ -0,0 +1,634 @@ +package org.phoebus.pv.tango; + +import org.tango.utils.TangoUtil; + +import fr.esrf.Tango.AttrDataFormat; +import fr.esrf.Tango.AttrQuality; +import fr.esrf.Tango.AttrWriteType; +import fr.esrf.Tango.DevFailed; +import fr.esrf.TangoApi.AttributeInfo; +import fr.esrf.TangoApi.AttributeInfoEx; +import fr.esrf.TangoApi.DeviceAttribute; +import fr.esrf.TangoApi.DeviceProxy; +import fr.esrf.TangoApi.QualityUtilities; +import fr.esrf.TangoDs.TangoConst; + +/** + * + * This class provide useful static methods to insert or extract data from DeviceAttribute + * + * @author katy.saintin@cea.fr + */ + + +public class TangoAttributeHelper { + private static final String CANNOT_SET_ATTRIBUTE_INFO_ON = "Cannot set AttributeInfo on "; + private static final String CANNOT_READ_ATTRIBUTE_INFO_ON = "Cannot read AttributeInfo on "; + private static final String CANNOT_READ_QUALITY_OF = "Cannot read quality of "; + public static final String STATE = "state"; + public static final String STATUS = "status"; + + /** + * Returns whether an attribute name represents a state or a status + * + * @param attributeName The attribute name (device name not included) + * @return A boolean value + */ + public static boolean isStateOrStatus(final String attributeName) { + return isStateOrStatus(null, attributeName); + } + + /** + * Returns whether an attribute name represents a state or a status + * + * @param deviceName The name of the device that contains the concerned attribute + * @param attributeName The attribute name (device name not included) + * @return A boolean value + */ + public static boolean isStateOrStatus(String deviceName, final String attributeName) { + boolean stateOrStatus; + if (STATE.equalsIgnoreCase(attributeName) || STATUS.equalsIgnoreCase(attributeName)) { + stateOrStatus = true; + } else if ((attributeName != null) && (deviceName != null)) { + AttributeInfo info = getAttributeInfo(deviceName, attributeName); + stateOrStatus = isScalarDevState(info); + } else { + stateOrStatus = false; + } + return stateOrStatus; + } + + /** + * Returns whether an attribute represents a state + * + * @param info The attribute info + * @return A boolean value + */ + public static boolean isDevState(final AttributeInfo info) { + boolean state; + if (info == null) { + state = false; + } else { + state = (info.data_type == TangoConst.Tango_DEV_STATE); + } + return state; + } + + /** + * Returns whether an attribute represents a state and is scalar + * + * @param info The attribute info + * @return A boolean value + */ + public static boolean isScalarDevState(final AttributeInfo info) { + boolean state; + if (info == null) { + state = false; + } else { + state = (info.data_type == TangoConst.Tango_DEV_STATE) && (info.data_format == AttrDataFormat.SCALAR); + } + return state; + } + + /** + * Returns an attribute's type + * + * @param deviceName The name of the device that contains the concerned attribute + * @param attributeName The name of the attribute + * @return An int + */ + public static int getAttributeType(String deviceName, String attributeName) { + return getAttributeType(getAttributeInfo(deviceName, attributeName)); + } + + /** + * Returns an attribute's type, based on its {@link AttributeInfo} + * + * @param info The {@link AttributeInfo} + * @return An int + */ + public static int getAttributeType(AttributeInfo info) { + int type = TangoConstHelper.UNKNOW_TYPE; + if (info != null) { + type = info.data_format.value(); + } + return type; + } + + /** + * Returns an attribute's format + * + * @param deviceName The name of the device that contains the concerned attribute + * @param attributeName The name of the attribute + * @return An int + */ + public static int getAttributeFormat(String deviceName, String attributeName) { + return getAttributeFormat(getAttributeInfo(deviceName, attributeName)); + } + + /** + * Returns an attribute's format, based on its {@link AttributeInfo} + * + * @param info The {@link AttributeInfo} + * @return An int + */ + public static int getAttributeFormat(AttributeInfo info) { + int result = -1; + if (info != null) { + result = TangoConstHelper.getTangoFormat(info.data_type); + } + return result; + } + + /** + * Returns an attribute's label + * + * @param deviceName The name of the device that contains the concerned attribute + * @param attributeName The name of the attribute + * @return A {@link String} + */ + public static String getLabel(String deviceName, String attributeName) { + return getLabel(getAttributeInfo(deviceName, attributeName)); + } + + /** + * Returns an attribute's label, based on its {@link AttributeInfo} + * + * @param info The {@link AttributeInfo} + * @return A {@link String} + */ + public static String getLabel(AttributeInfo info) { + String label = null; + if (info != null) { + label = info.label; + } + return label; + } + + /** + * Returns whether an attribute is read only + * + * @param deviceName The name of the device that contains the concerned attribute + * @param attributeName The name of the attribute + * @return A boolean + */ + public static boolean isAttributeReadOnly(String deviceName, String attributeName) { + return isAttributeReadOnly(getAttributeInfo(deviceName, attributeName)); + } + + /** + * Returns whether an attribute is read only, based on its {@link AttributeInfo} + * + * @param info The {@link AttributeInfo} + * @return A boolean + */ + public static boolean isAttributeReadOnly(AttributeInfo info) { + boolean isReadOnly = true; + if (info != null) { + isReadOnly = (info.writable.value() == AttrWriteType._READ); + } + return isReadOnly; + } + + /** + * Reads an {@link AttributeInfoEx} for a given attribute and returns whether the attribute has unsigned values. + * + * @param deviceName The name of the device that contains the concerned attribute + * @param attributeName The name of the attribute + * @return A boolean + * @throws DevFailed In case of error when trying to read {@link AttributeInfoEx} + */ + public static boolean isAttributeUnsigned(String deviceName, String attributeName) throws DevFailed { + return isAttributeUnsigned(getAttributeInfoEx(deviceName, attributeName)); + } + + /** + * Reads an {@link AttributeInfoEx} for a given attribute and returns whether the attribute has unsigned values. + * + * @param device The {@link DeviceProxy} connected to the device that contains the concerned attribute + * @param attributeName The name of the attribute + * @return A boolean + * @throws DevFailed In case of error when trying to read {@link AttributeInfoEx} + */ + public static boolean isAttributeUnsigned(DeviceProxy device, String attributeName) throws DevFailed { + return isAttributeUnsigned(getAttributeInfoEx(device, attributeName)); + } + + /** + * Returns whether an attribute has unsigned values, based on its {@link AttributeInfo} + * + * @param info The {@link AttributeInfo} + * @return A boolean + */ + public static boolean isAttributeUnsigned(AttributeInfo info) { + boolean unsigned; + if (info == null) { + unsigned = false; + } else { + switch (info.data_type) { + case TangoConst.Tango_DEV_UCHAR: + case TangoConst.Tango_DEV_USHORT: + case TangoConst.Tango_DEV_ULONG: + case TangoConst.Tango_DEV_ULONG64: + unsigned = true; + break; + default: + unsigned = false; + break; + } + } + return unsigned; + } + + /** + * Sets an {@link AttributeInfo} in a given device + * + * @param attributeInfo The {@link AttributeInfo} to set + * @param deviceName The device name + */ + public static void setAttributeInfo(AttributeInfo attributeInfo, String deviceName) { + if (attributeInfo != null) { + DeviceProxy proxy = TangoDeviceHelper.getDeviceProxy(deviceName); + if (proxy != null) { + try { + // JAVAAPI-603: synchronize accesses to DeviceProxy + synchronized (proxy) { + proxy.set_attribute_info(new AttributeInfo[] { attributeInfo }); + } + } catch (DevFailed e) { + StringBuilder builder = new StringBuilder(CANNOT_SET_ATTRIBUTE_INFO_ON); + builder.append(deviceName).append('/').append(attributeInfo.name); + System.err.println(builder.toString() + " " + TangoExceptionHelper.getErrorMessage(e)); + } + } + } + } + + /** + * Reads an {@link AttributeInfo} for a given attribute and returns it + * + * @param deviceName The name of the device that contains the concerned attribute + * @param attributeName The name of the attribute + * @return An {@link AttributeInfo} + */ + public static AttributeInfo getAttributeInfo(String deviceName, String attributeName) { + AttributeInfo attributeInfo = null; + DeviceProxy proxy = TangoDeviceHelper.getDeviceProxy(deviceName, false); + if (isAttributeRunning(proxy, attributeName)) { + try { + // JAVAAPI-603: synchronize accesses to DeviceProxy + synchronized (proxy) { + attributeInfo = proxy.get_attribute_info(attributeName); + } + } catch (DevFailed e) { + StringBuilder builder = new StringBuilder(CANNOT_READ_ATTRIBUTE_INFO_ON); + builder.append(deviceName).append('/').append(attributeName); + System.err.println(builder.toString() + " " + TangoExceptionHelper.getErrorMessage(e)); + } + } + return attributeInfo; + } + + /** + * Reads an {@link AttributeInfoEx} for a given attribute and returns it + * + * @param deviceName The name of the device that contains the concerned attribute + * @param attributeName The name of the attribute + * @return An {@link AttributeInfoEx} + * @throws DevFailed In case of error when trying to read {@link AttributeInfoEx} + */ + public static AttributeInfoEx getAttributeInfoEx(String deviceName, String attributeName) throws DevFailed { + return getAttributeInfoEx(TangoDeviceHelper.getDeviceProxy(deviceName, false), attributeName); + } + + /** + * Reads an {@link AttributeInfoEx} for a given attribute and returns it + * + * @param device The {@link DeviceProxy} connected to the device that contains the concerned attribute + * @param attributeName The name of the attribute + * @return An {@link AttributeInfoEx} + * @throws DevFailed In case of error when trying to read {@link AttributeInfoEx} + */ + public static AttributeInfoEx getAttributeInfoEx(DeviceProxy device, String attributeName) throws DevFailed { + AttributeInfoEx attributeInfoEx = null; + if (isAttributeRunning(device, attributeName)) { + if (device != null) { + // JAVAAPI-603: synchronize accesses to DeviceProxy + synchronized (device) { + attributeInfoEx = device.get_attribute_info_ex(attributeName); + } + } + } + return attributeInfoEx; + } + + /** + * Sets an {@link AttributeInfoEx} in a given device + * + * @param attributeInfoEx The {@link AttributeInfoEx} to set + * @param deviceName The device name + * @throws DevFailed In case of error when trying to write {@link AttributeInfoEx} + */ + public static void setAttributeInfoEx(AttributeInfoEx attributeInfoEx, String deviceName) throws DevFailed { + if (attributeInfoEx != null) { + DeviceProxy proxy = TangoDeviceHelper.getDeviceProxy(deviceName); + if (proxy != null) { + // JAVAAPI-603: synchronize accesses to DeviceProxy + synchronized (proxy) { + proxy.set_attribute_info(new AttributeInfoEx[] { attributeInfoEx }); + } + } + } + } + + /** + * Returns whether a given attribute is currently running + * + * @param deviceName The name of the device that contains the concerned attribute + * @param attributeName The name of the attribute + * @return A boolean + */ + public static boolean isAttributeRunning(String deviceName, String attributeName) { + return isAttributeRunning(deviceName, attributeName, false); + } + + /** + * Returns whether a given attribute is currently running + * + * @param deviceName The name of the device that contains the concerned attribute + * @param attributeName The name of the attribute + * @param testForDeviceRunning Whether to first test if device is running + * @return A boolean + */ + public static boolean isAttributeRunning(String deviceName, String attributeName, boolean testForDeviceRunning) { + return isAttributeRunning(TangoDeviceHelper.getDeviceProxy(deviceName, testForDeviceRunning), attributeName); + } + + /** + * Returns whether a given attribute is currently running + * + * @param proxy The {@link DeviceProxy} of the device that contains the concerned attribute + * @param attributeName The name of the attribute + * @return A boolean + */ + protected static boolean isAttributeRunning(DeviceProxy proxy, String attributeName) { + boolean running = false; + if ((proxy != null) && (attributeName != null) && (!attributeName.trim().isEmpty())) { + attributeName = attributeName.trim(); + if (!attributeName.isEmpty()) { + try { + if (isStateOrStatus(attributeName)) { + running = true; + } else { + String[] attributeList; + // JAVAAPI-603: synchronize accesses to DeviceProxy + synchronized (proxy) { + attributeList = proxy.get_attribute_list(); + } + if (isNameInList(attributeName, attributeList)) { + running = checkAttribute(proxy, attributeName); + } else { + // check for attribute alias + attributeName = TangoUtil.getAttributeName(attributeName); + if (isStateOrStatus(attributeName)) { + running = true; + } else if (isNameInList(attributeName, attributeList)) { + running = checkAttribute(proxy, attributeName); + } + } + } + } catch (Exception e) { + running = false; + } + } + } + return running; + } + + /** + * Returns whether a {@link String} is contained in A {@link String} array, ignoring case. + * + * @param name The {@link String}. + * @param nameList The {@link String} array. + * @return A boolean. + */ + protected static boolean isNameInList(String name, String... nameList) { + boolean contains = false; + if (nameList != null) { + for (String element : nameList) { + if (name.equalsIgnoreCase(element)) { + contains = true; + break; + } + } + } + return contains; + } + + /** + * Tries to obtain an {@link AttributeInfo} for an attribute and checks whether that info is not null. + * + * @param proxy The {@link DeviceProxy} that knows the attributes. Must not be null. + * @param attributeName The attribute name. + * @return A boolean. + * @throws DevFailed If there was a connection problem. + */ + protected static boolean checkAttribute(DeviceProxy proxy, String attributeName) throws DevFailed { + boolean attributeOk; + // JAVAAPI-603: synchronize accesses to DeviceProxy + synchronized (proxy) { + attributeOk = (proxy.get_attribute_info(attributeName) != null); + } + return attributeOk; + } + + /** + * Reads an attribute quality and returns it as an int + * + * @param attribute The {@link DeviceAttribute} associated with the expected attribute + * @return An int + * @throws DevFailed If a problem occurred while trying to access the corresponding attribute + */ + public static int getAttributeQuality(DeviceAttribute attribute) throws DevFailed { + int result = AttrQuality._ATTR_INVALID; + if (attribute != null) { + AttrQuality quality = attribute.getQuality(); + if (quality != null) { + result = quality.value(); + } + } + return result; + } + + /** + * Reads an attribute quality and returns it as a {@link String} + * + * @param deviceName The name of the device that contains the concerned attribute + * @param attributeName The name of the attribute + * @return A {@link String} + */ + public static String getAttributeStringQuality(String deviceName, String attributeName) { + String quality = QualityUtilities.getNameForQuality(AttrQuality.ATTR_INVALID); + try { + quality = getAttributeStringQuality(getDeviceAttribute(deviceName, attributeName)); + } catch (DevFailed e) { + StringBuilder builder = new StringBuilder(CANNOT_READ_QUALITY_OF); + builder.append(deviceName).append('/').append(attributeName); + System.err.println(builder.toString() + " " + TangoExceptionHelper.getErrorMessage(e)); + } + return quality; + } + + /** + * Reads an attribute quality and returns it as an int + * + * @param deviceName The name of the device that contains the concerned attribute + * @param attributeName The name of the attribute + * @return an int + */ + public static int getAttributeQuality(String deviceName, String attributeName) { + int quality = AttrQuality._ATTR_INVALID; + DeviceProxy proxy = TangoDeviceHelper.getDeviceProxy(deviceName, false); + if (isAttributeRunning(proxy, attributeName)) { + try { + quality = getAttributeQuality(getDeviceAttribute(proxy, attributeName)); + } catch (DevFailed e) { + StringBuilder builder = new StringBuilder(CANNOT_READ_QUALITY_OF); + builder.append(deviceName).append('/').append(attributeName); + System.err.println(builder.toString() + " " + TangoExceptionHelper.getErrorMessage(e)); + } + } + return quality; + } + + /** + * Reads an attribute quality and returns it as a {@link String} + * + * @param attribute The {@link DeviceAttribute} associated with the expected attribute + * @return A {@link String} + * @throws DevFailed If a problem occurred while trying to access the corresponding attribute + */ + public static String getAttributeStringQuality(DeviceAttribute attribute) throws DevFailed { + String quality = QualityUtilities.getNameForQuality(AttrQuality.ATTR_INVALID); + if (attribute != null) { + AttrQuality attrQuality = attribute.getQuality(); + if (attrQuality != null) { + quality = QualityUtilities.getNameForQuality(attrQuality); + } + } + return quality; + } + + /** + * Creates a {@link DeviceAttribute} for a given attribute complete name + * + * @param attributeCompleteName The attribute complete name + * @return A {@link DeviceAttribute} + * @throws DevFailed If a problem occurred while trying to access the corresponding attribute + */ + public static DeviceAttribute getDeviceAttribute(String attributeCompleteName) throws DevFailed { + DeviceAttribute attribute = null; + if (attributeCompleteName != null) { + int index = attributeCompleteName.lastIndexOf('/'); + if (index > -1) { + attribute = getDeviceAttribute(attributeCompleteName.substring(0, index), + attributeCompleteName.substring(index + 1)); + } + } + return attribute; + } + + /** + * Creates a {@link DeviceAttribute} for a given device name attribute name + * + * @param deviceName The device name + * @param attributeName The attribute name + * @return A {@link DeviceAttribute} + * @throws DevFailed If a problem occurred while trying to access the corresponding attribute + */ + public static DeviceAttribute getDeviceAttribute(String deviceName, String attributeName) throws DevFailed { + return getDeviceAttribute(TangoDeviceHelper.getDeviceProxy(deviceName, false), attributeName); + } + + /** + * Creates a {@link DeviceAttribute} for a given {@link DeviceProxy} attribute name + * + * @param device The {@link DeviceProxy} + * @param attributeName The attribute name + * @return A {@link DeviceAttribute} + * @throws DevFailed If a problem occurred while trying to access the corresponding attribute + */ + public static DeviceAttribute getDeviceAttribute(DeviceProxy device, String attributeName) throws DevFailed { + DeviceAttribute result = null; + if (device != null) { + // JAVAAPI-603: synchronize accesses to DeviceProxy + synchronized (device) { + result = device.read_attribute(attributeName); + } + } + return result; + } + + /** + * Converts a {@link String} value into a short value, based on an enum attribute's possible values. + * + * @param value The {@link String} value. + * @param ex The {@link AttributeInfoEx} that knows the possible values. + * @param defaultValue The default value to return if the conversion could not be done. + * @return A short value, that may be written in the attribute.
+ */ + public static short toEnumValue(String value, AttributeInfoEx ex, short defaultValue) { + short index = defaultValue; + if ((ex != null) && (ex.data_type == TangoConst.Tango_DEV_ENUM) && (value != null)) { + String[] possibleValues = ex.enum_label; + if (possibleValues != null) { + index = 0; + for (String possibleValue : possibleValues) { + if (value.equalsIgnoreCase(possibleValue)) { + break; + } else { + index++; + } + } + if (index == possibleValues.length) { + index = defaultValue; + } + } + } + return index; + } + + /** + * Converts a {@link String} value into a {@link Short} value, based on an enum attribute's possible values. + * + * @param value The {@link String} value. + * @param ex The {@link AttributeInfoEx} that knows the possible values. + * @return A {@link Short} value, that can be written in the attribute. + */ + public static Short toEnumValue(String value, AttributeInfoEx ex) { + short index = toEnumValue(value, ex, (short) -1); + return index > -1 ? Short.valueOf(index) : null; + } + + /** + * Converts a {@link Short} value into a {@link String} value, based on an enum attribute's possible values. + * + * @param value The {@link Short} value. + * @param ex The {@link AttributeInfoEx} that knows the possible values. + * @return A {@link String} value: the enum matching label. + */ + public static String toEnumLabel(short value, AttributeInfoEx ex) { + String result = null; + if ((ex != null) && (ex.data_type == TangoConst.Tango_DEV_ENUM) && (value > -1)) { + String[] possibleValues = ex.enum_label; + if (possibleValues != null) { + int index = value; + if (index < possibleValues.length) { + result = possibleValues[index]; + } + } + } + return result; + } + +} diff --git a/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoCommandHelper.java b/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoCommandHelper.java new file mode 100644 index 0000000000..1d6154dc9a --- /dev/null +++ b/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoCommandHelper.java @@ -0,0 +1,498 @@ +package org.phoebus.pv.tango; + +import java.util.ArrayList; +import java.util.List; + +import org.tango.utils.TangoUtil; + +import fr.esrf.Tango.DevFailed; +import fr.esrf.TangoApi.CommandInfo; +import fr.esrf.TangoApi.DeviceData; +import fr.esrf.TangoApi.DeviceProxy; +import fr.esrf.TangoDs.TangoConst; +import fr.soleil.tango.clientapi.InsertExtractUtils; +import fr.soleil.tango.clientapi.util.TypeConversionUtil; + + + +/** + * + * This class provide useful static methods to insert and extract result from Command + * + * @author katy.saintin@cea.fr + */ +public class TangoCommandHelper { + private static final String CANNOT_READ_COMMAND_INFO_ON = "Cannot read CommandInfo on "; + private static final String CANNOT_READ_COMMAND_LIST_QUERY_ON = "Cannot read command_list_query on "; + private static final String FAILED_TO_READ_COMMANDS_DEAD_DEVICE = "Failed to read commands from %s because device seems to be down."; + private static final String FAILED_TO_READ_COMMANDS_UNKNOWN_REASON_DEVICE = "Failed to read commands from %s for an unknown reason concerning the device."; + private static final String FAILED_TO_READ_COMMANDS_UNKNOWN_REASON = "Failed to read commands from %s for an unknown reason."; + + /** + * Returns an int that represents the type of data a command can have as argument. + * + * @param deviceName The device name. + * @param commandName The command name. + * @return An int. + * @see TangoConst + */ + public static int getCommandInType(String deviceName, String commandName) { + // XXX Must not be TangoConstHelper.UNKNOW_TYPE. + // TangoConstHelper.UNKNOW_TYPE = 3, which matches TangoConst.Tango_DEV_LONG + int type = -1; + CommandInfo info = getCommandInfo(deviceName, commandName); + if (info != null) { + type = info.in_type; + } + return type; + } + + /** + * Returns an int that represents the type of data a command can have as argument. + * + * @param proxy The {@link DeviceProxy} connected to the device. + * @param commandName The command name. + * @return An int. + * @see TangoConst + */ + public static int getCommandInType(DeviceProxy proxy, String commandName) { + // XXX Must not be TangoConstHelper.UNKNOW_TYPE. + // TangoConstHelper.UNKNOW_TYPE = 3, which matches TangoConst.Tango_DEV_LONG + return getCommandInType(proxy, commandName, -1/*TangoConstHelper.UNKNOW_TYPE*/); + } + + /** + * Returns an int that represents the type of data a command can have as argument. + * + * @param proxy The {@link DeviceProxy} connected to the device. + * @param commandName The command name. + * @param defaultValue The default value to return if something went wrong. + * @return An int. + * @see TangoConst + */ + public static int getCommandInType(DeviceProxy proxy, String commandName, int defaultValue) { + int type = defaultValue; + CommandInfo info = getCommandInfo(proxy, commandName); + if (info != null) { + type = info.in_type; + } + return type; + } + + /** + * Returns an int that represents the type of data a command can return. + * + * @param deviceName The device name. + * @param commandName The command name. + * @return An int. + * @see TangoConst + */ + public static int getCommandOutType(String deviceName, String commandName) { + // XXX Must not be TangoConstHelper.UNKNOW_TYPE. + // TangoConstHelper.UNKNOW_TYPE = 3, which matches TangoConst.Tango_DEV_LONG + int type = -1; + CommandInfo info = getCommandInfo(deviceName, commandName); + if (info != null) { + type = info.out_type; + } + return type; + } + + /** + * Returns an int that represents the type of data a command can return. + * + * @param proxy The {@link DeviceProxy} connected to the device. + * @param commandName The command name. + * @return An int. + * @see TangoConst + */ + public static int getCommandOutType(DeviceProxy proxy, String commandName) { + // XXX Must not be TangoConstHelper.UNKNOW_TYPE. + // TangoConstHelper.UNKNOW_TYPE = 3, which matches TangoConst.Tango_DEV_LONG + return getCommandOutType(proxy, commandName, -1/*TangoConstHelper.UNKNOW_TYPE*/); + } + + /** + * Returns an int that represents the type of data a command can return. + * + * @param proxy The {@link DeviceProxy} connected to the device. + * @param commandName The command name. + * @param defaultValue The default value to return if something went wrong. + * @return An int. + * @see TangoConst + */ + public static int getCommandOutType(DeviceProxy proxy, String commandName, int defaultValue) { + int type = defaultValue; + CommandInfo info = getCommandInfo(proxy, commandName); + if (info != null) { + type = info.out_type; + } + return type; + } + + /** + * Returns the {@link CommandInfo} of a command. + * + * @param deviceName The device name. + * @param commandName The command name. + * @return A {@link CommandInfo}. + */ + public static CommandInfo getCommandInfo(String deviceName, String commandName) { + return getCommandInfo(TangoDeviceHelper.getDeviceProxy(deviceName), commandName); + } + + /** + * Returns the {@link CommandInfo} of a command. + * + * @param proxy The {@link DeviceProxy} connected to the device. + * @param commandName The command name. + * @return A {@link CommandInfo}. + */ + public static CommandInfo getCommandInfo(DeviceProxy proxy, String commandName) { + CommandInfo commandInfo = null; + if ((proxy != null) && doesCommandExist(proxy, commandName)) { + try { + // JAVAAPI-603: synchronize accesses to DeviceProxy + synchronized (proxy) { + commandInfo = proxy.command_query(commandName); + } + } catch (DevFailed e) { + StringBuilder builder = new StringBuilder(CANNOT_READ_COMMAND_INFO_ON); + builder.append(proxy.get_name()).append(TangoDeviceHelper.SLASH).append(commandName); + System.err.println(builder.toString() + " " + TangoExceptionHelper.getErrorMessage(e)); + } + } + return commandInfo; + } + + /** + * Returns whether a command exists + * + * @param deviceName The device name + * @param commandName The command name + * @return Whether the command exists + */ + public static boolean doesCommandExist(String deviceName, String commandName) { + boolean exist = false; + if ((commandName != null) && (!commandName.isEmpty())) { + String[] commandList = getCommandList(deviceName); + if (commandList != null) { + for (String element : commandList) { + if (commandName.trim().equalsIgnoreCase(element)) { + exist = true; + break; + } + } + } + } + return exist; + } + + /** + * Returns whether a command exists. + * + * @param proxy The {@link DeviceProxy} connected to the device. + * @param commandName The command name. + * @return Whether the command exists. + */ + public static boolean doesCommandExist(DeviceProxy proxy, String commandName) { + boolean exist = false; + if ((commandName != null) && (!commandName.isEmpty())) { + String[] commandList = getCommandList(proxy); + if (commandList != null) { + for (String element : commandList) { + if (commandName.trim().equalsIgnoreCase(element)) { + exist = true; + break; + } + } + } + } + return exist; + } + + /** + * Returns the command list of a given device. + * + * @param deviceName The device name. + * @param lowerCase Whether to force all returned values as lower case. + * @return A {@link String} array. + */ + public static String[] getCommandList(String deviceName, boolean lowerCase) { + DeviceProxy proxy = TangoDeviceHelper.getDeviceProxy(deviceName); + String[] commands = getCommandList(proxy, lowerCase); + if ((commands == null) && (deviceName != null)) { + if (proxy == null) { + if (TangoDeviceHelper.isDeadDevice(deviceName)) { + System.err.println(String.format(FAILED_TO_READ_COMMANDS_DEAD_DEVICE, deviceName)); + } else { + System.err.println(String.format(FAILED_TO_READ_COMMANDS_UNKNOWN_REASON_DEVICE, deviceName)); + } + } else { + System.err.println(String.format(FAILED_TO_READ_COMMANDS_UNKNOWN_REASON, deviceName)); + } + } + return commands; + } + + /** + * Returns the command list of a given device. + * + * @param proxy The {@link DeviceProxy} connected to the device. + * @param lowerCase Whether to force all returned values as lower case. + * @return A {@link String} array. + */ + public static String[] getCommandList(DeviceProxy proxy, boolean lowerCase) { + String[] commandList = null; + if (proxy != null) { + try { + commandList = getCommandListWithError(proxy, lowerCase); + } + catch (DevFailed e) { + String errorMessage = CANNOT_READ_COMMAND_LIST_QUERY_ON + " " + proxy.get_name(); + System.err.println(errorMessage + " " + TangoExceptionHelper.getErrorMessage(e)); + } + } + return commandList; + } + + /** + * Returns the command list of a given device, throwing {@link DevFailed} in case of communication problems. + * + * @param proxy The {@link DeviceProxy} connected to the device. + * @param lowerCase Whether to force all returned values as lower case. + * @return A {@link String} array. + * @throws DevFailed In case of communication problems. + */ + public static String[] getCommandListWithError(DeviceProxy proxy, boolean lowerCase) throws DevFailed { + String[] commandList = null; + if (proxy != null) { + CommandInfo[] commandInfoList; + // JAVAAPI-603: synchronize accesses to DeviceProxy + synchronized (proxy) { + commandInfoList = proxy.command_list_query(); + } + commandList = new String[commandInfoList.length]; + for (int i = 0; i < commandInfoList.length; i++) { + commandList[i] = lowerCase ? commandInfoList[i].cmd_name.toLowerCase() : commandInfoList[i].cmd_name; + } + } + return commandList; + } + + /** + * Returns the command list of a given device. + * + * @param deviceName The device name. + * @return A {@link String} array. + */ + public static String[] getCommandList(String deviceName) { + return getCommandList(deviceName, false); + } + + /** + * Returns the command list of a given device. + * + * @param proxy The {@link DeviceProxy} connected to the device. + * @return A {@link String} array. + */ + public static String[] getCommandList(DeviceProxy proxy) { + return getCommandList(proxy, false); + } + + /** + * Executes a command and returns its result. + * + * @param proxy The {@link DeviceProxy} connected to the device. + * @param commandName The command name. + * @param arg The argument to give to the command. + * @return The command result. + * @throws DevFailed If something went wrong. + */ + public static Object executeCommand(final DeviceProxy proxy, final String commandName, final Object arg) + throws DevFailed { + return executeCommand(proxy, commandName, getCommandInType(proxy, commandName), + getCommandOutType(proxy, commandName), arg); + } + + /** + * Executes a command and returns its result. + * + * @param proxy The {@link DeviceProxy} connected to the device. + * @param commandName The command name. + * @param expectedResultClass The expected result class. + * @param arg The argument to give to the command. + * @return The command result. + * @throws DevFailed If something went wrong. + */ + public static T executeCommand(final DeviceProxy proxy, final String commandName, + final Class expectedResultClass, final Object arg) throws DevFailed { + return executeCommand(proxy, commandName, getCommandInType(proxy, commandName), + getCommandOutType(proxy, commandName), expectedResultClass, arg); + } + + /** + * Executes a command and returns its result. + * + * @param proxy The {@link DeviceProxy} connected to the device. + * @param commandName The command name. + * @param commandInType The command input type, that can be obtained through getCommandInType methods. + * @param commandOutType The command output type, that can be obtained through getCommandOutType + * methods. + * @param arg The argument to give to the command. + * @return The command result. + * @throws DevFailed If something went wrong. + * @see #getCommandInType(DeviceProxy, String) + * @see #getCommandInType(String, String) + * @see #getCommandInType(DeviceProxy, String, int) + * @see #getCommandOutType(DeviceProxy, String) + * @see #getCommandOutType(String, String) + * @see #getCommandOutType(DeviceProxy, String, int) + */ + public static Object executeCommand(DeviceProxy proxy, String commandName, int commandInType, int commandOutType, + final Object arg) throws DevFailed { + return executeCommand(proxy, commandName, null, commandInType, commandOutType, Object.class, arg); + } + + /** + * Executes a command and returns its result. + * + * @param proxy The {@link DeviceProxy} connected to the device. + * @param commandName The command name. + * @param commandInType The command input type, that can be obtained through getCommandInType methods. + * @param commandOutType The command output type, that can be obtained through getCommandOutType + * methods. + * @param returnType The expected class of the command result. + * @param arg The argument to give to the command. + * @return The command result. + * @throws DevFailed If something went wrong. + * @see #getCommandInType(DeviceProxy, String) + * @see #getCommandInType(String, String) + * @see #getCommandInType(DeviceProxy, String, int) + * @see #getCommandOutType(DeviceProxy, String) + * @see #getCommandOutType(String, String) + * @see #getCommandOutType(DeviceProxy, String, int) + */ + public static T executeCommand(DeviceProxy proxy, String commandName, int commandInType, int commandOutType, + final Class returnType, final Object arg) throws DevFailed { + return executeCommand(proxy, commandName, null, commandInType, commandOutType, returnType, arg); + } + + /** + * Executes a command and returns its result. + * + * @param proxy The {@link DeviceProxy} connected to the device. + * @param commandName The command name. + * @param commandInData The {@link DeviceData} to use in order to set command argument. If null, a new + * one will be created. + * @param commandInType The command input type, that can be obtained through getCommandInType methods. + * @param commandOutType The command output type, that can be obtained through getCommandOutType + * methods. + * @param returnType The expected class of the command result. + * @param arg The argument to give to the command. + * @return The command result. + * @throws DevFailed If something went wrong. + * @see #getCommandInType(DeviceProxy, String) + * @see #getCommandInType(String, String) + * @see #getCommandInType(DeviceProxy, String, int) + * @see #getCommandOutType(DeviceProxy, String) + * @see #getCommandOutType(String, String) + * @see #getCommandOutType(DeviceProxy, String, int) + */ + public static T executeCommand(DeviceProxy proxy, String commandName, DeviceData commandInData, + int commandInType, int commandOutType, final Class returnType, final Object arg) throws DevFailed { + Object commandResult = null; + T result = null; + if (proxy != null) { + DeviceData output; + if (commandInType == TangoConst.Tango_DEV_VOID) { + // JAVAAPI-603: synchronize accesses to DeviceProxy + synchronized (proxy) { + output = proxy.command_inout(commandName); + } + } else { + DeviceData input = commandInData == null ? new DeviceData() : commandInData; + InsertExtractUtils.insert(input, commandInType, arg); + // JAVAAPI-603: synchronize accesses to DeviceProxy + synchronized (proxy) { + output = proxy.command_inout(commandName, input); + } + } + if ((commandOutType != TangoConst.Tango_DEV_VOID) && (output != null)) { + commandResult = InsertExtractUtils.extract(output); + } + if (commandResult != null) { + result = TypeConversionUtil.castToType(returnType, commandResult); + } + } + return result; + } + + public static String getTangoTypeForType(int type) { + return TangoConst.Tango_CmdArgTypeName[type]; + } + + /** + * Returns whether a command in/out type represents a scalar value. + * + * @param commandInOutType The command in/out type. + * @return A boolean. + * @see #getCommandInType(DeviceProxy, String) + * @see #getCommandInType(String, String) + * @see #getCommandInType(DeviceProxy, String, int) + * @see #getCommandOutType(DeviceProxy, String) + * @see #getCommandOutType(String, String) + * @see #getCommandOutType(DeviceProxy, String, int) + */ + public static boolean isScalarType(int commandInOutType) { + return TangoUtil.SCALARS.contains(commandInOutType); + } + + /** + * Extracts a {@link List} from a command value. + * + * @param type The type of data that should be contained in the list. + * @param value The command value. + * @return A {@link List}. + * @throws DevFailed If something went wrong. Thrown by {@link TypeConversionUtil#castToArray(Class, Object)}. + */ + @SuppressWarnings("unchecked") + public static List extractList(final Class type, final Object value) throws DevFailed { + List result; + if (value == null) { + result = extractList((T[]) null); + } else { + Class valueClass = value.getClass(); + if (type.equals(valueClass)) { + result = extractList((T) value); + } else if (valueClass.isArray() && type.equals(valueClass.getComponentType())) { + result = extractList((T[]) value); + } else { + Object converted = TypeConversionUtil.castToArray(type, value); + if (converted == null) { + result = extractList((T[]) null); + } else if (type.equals(converted.getClass())) { + result = extractList((T) converted); + } else { + result = extractList((T[]) converted); + } + } + } + return result; + } + + /** + * Converts an array to a list. + * + * @param value The array. + * @return A {@link List}. + */ + public static List extractList(@SuppressWarnings("unchecked") final T... value) { + final List result = new ArrayList(); + for (T val : value) { + result.add(val); + } + return result; + } + +} diff --git a/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoConstHelper.java b/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoConstHelper.java new file mode 100644 index 0000000000..27930a953b --- /dev/null +++ b/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoConstHelper.java @@ -0,0 +1,260 @@ +package org.phoebus.pv.tango; + +import java.lang.reflect.Field; + +import fr.esrf.Tango.AttrDataFormat; +import fr.esrf.Tango.AttrQuality; +import fr.esrf.Tango.DevState; +import fr.esrf.TangoDs.TangoConst; + +/** +* Tango constants helper +* +* @author katy.saintin@cea.fr +*/ + +public class TangoConstHelper { + public final static int SCALAR_TYPE = AttrDataFormat._SCALAR; + public final static int ARRAY_TYPE = AttrDataFormat._SPECTRUM; + public final static int IMAGE_TYPE = AttrDataFormat._IMAGE; + public final static int UNKNOW_TYPE = AttrDataFormat._FMT_UNKNOWN; + + public final static int BOOLEAN_FORMAT = 1; + public final static int NUMERICAL_FORMAT = 2; + public final static int STRING_FORMAT = 3; + public final static int UNKNOW_FORMAT = 4; + + private static final String BOOLEAN = "BOOLEAN"; + private static final String UNKNOW_TYPE_STR = "Unknow type"; + + public static final String DOUBLE_NAME = "DOUBLE"; + public static final String LONG_NAME = "LONG"; + public static final String SHORT_NAME = "SHORT"; + public static final String CHAR_NAME = "CHAR"; + public static final String INT_NAME = "INT"; + public static final String FLOAT_NAME = "FLOAT"; + public static final String ENUM_NAME = "ENUM"; + public static final String ENCODED_NAME = "ENCODED"; + public static final String ARRAY_NAME = "ARRAY"; + + private final static String[] NUMERICAL_LABEL = new String[] { DOUBLE_NAME, LONG_NAME, SHORT_NAME, INT_NAME, FLOAT_NAME, CHAR_NAME,ENUM_NAME, + ENCODED_NAME }; + + public static final String STATE_NAME = "STATE"; + public static final String STATUS_NAME = "STATUS"; + private final static String[] STRING_LABEL = new String[] { "STRING", STATE_NAME, STATUS_NAME}; + + private static final int NB_QUALITY = 5; + public static final String[] ATTR_QUALITY_LABEL = new String[NB_QUALITY]; + + private static final int NB_STATE = 14; + public static final String[] STATE_LABEL = new String[NB_STATE]; + + static { + for (int i = 0; i < NB_QUALITY; i++) { + ATTR_QUALITY_LABEL[i] = AttrQuality.from_int(i).toString(); + } + + for (int i = 0; i < NB_STATE; i++) { + STATE_LABEL[i] = DevState.from_int(i).toString(); + } + } + + + /** + * Return simple type according a TangoConst_DEV_* type. * + * + * @param TangoConst_DEV type + * @return the type that can be SCALAR_TYPE, ARRAY_TYPE or UNKNOW_TYPE + * @see TangoConst class in TangORB + */ + + public static int getTangoType(int tangoType) { + int type = TangoConstHelper.UNKNOW_TYPE; + Field[] fieldList = TangoConst.class.getFields(); + Field tmpField = null; + String tmpFieldName = null; + int tmpFieldValue = 0; + for (Field element : fieldList) { + tmpField = element; + tmpFieldName = tmpField.getName(); + if (isATangoType(tmpFieldName)) { + try { + tmpFieldValue = tmpField.getInt(tmpField); + } catch (Exception e) { + type = TangoConstHelper.UNKNOW_TYPE; + break; + } + if (tangoType == tmpFieldValue) { + if (tmpFieldName.endsWith(ARRAY_NAME)) { + type = TangoConstHelper.ARRAY_TYPE; + break; + } else { + type = TangoConstHelper.SCALAR_TYPE; + break; + } + } + } + } + return type; + } + + public static String getTangoFieldName(int tangoType) { + String fieldName = null; + Field[] fieldList = TangoConst.class.getFields(); + Field tmpField = null; + String tmpFieldName = null; + int tmpFieldValue = 0; + for (Field element : fieldList) { + tmpField = element; + tmpFieldName = tmpField.getName(); + if (isATangoType(tmpFieldName)) { + try { + tmpFieldValue = tmpField.getInt(tmpField); + } catch (Exception e) { + fieldName = null; + break; + } + if (tangoType == tmpFieldValue) { + fieldName = tmpFieldName ; + break; + } + } + } + return fieldName; + } + + private static boolean isATangoType(String tangoFieldName) { + return ((tangoFieldName != null) && tangoFieldName.startsWith("Tango_DEV")); + } + + private static boolean isString(String tangoFieldName) { + boolean result = false; + if (isATangoType(tangoFieldName)) { + for (String element : STRING_LABEL) { + if (tangoFieldName.contains(element)) { + result = true; + break; + } + } + } + return result; + } + + private static boolean isBoolean(String tangoFieldName) { + boolean result = false; + if (isATangoType(tangoFieldName) && tangoFieldName.contains(BOOLEAN)) { + result = true; + } + return result; + + } + + private static boolean isNumerical(String tangoFieldName) { + boolean result = false; + if (isATangoType(tangoFieldName)) { + for (String element : NUMERICAL_LABEL) { + if (tangoFieldName.contains(element)) { + result = true; + break; + } + } + } + return result; + + } + + public static int getTangoFormat(int tangoType) { + int format = TangoConstHelper.UNKNOW_FORMAT; + Field[] fieldList = TangoConst.class.getFields(); + Field tmpField = null; + String tmpFieldName = null; + int tmpFieldValue = 0; + for (Field element : fieldList) { + tmpField = element; + tmpFieldName = tmpField.getName(); + if (isATangoType(tmpFieldName)) { + try { + tmpFieldValue = tmpField.getInt(tmpField); + } catch (Exception e) { + format = TangoConstHelper.UNKNOW_TYPE; + break; + } + if (tangoType == tmpFieldValue) { + if (isBoolean(tmpFieldName)) { + format = TangoConstHelper.BOOLEAN_FORMAT; + break; + } else if (isString(tmpFieldName)) { + format = TangoConstHelper.STRING_FORMAT; + break; + } else if (isNumerical(tmpFieldName)) { + format = TangoConstHelper.NUMERICAL_FORMAT; + break; + } + } + } + } + return format; + } + + public static int getAttributeTangoFormat(int tangoType) { + int format = TangoConstHelper.UNKNOW_FORMAT; + Field[] fieldList = TangoConst.class.getFields(); + Field tmpField = null; + String tmpFieldName = null; + int tmpFieldValue = 0; + for (Field element : fieldList) { + tmpField = element; + tmpFieldName = tmpField.getName(); + if (isATangoType(tmpFieldName)) { + try { + tmpFieldValue = tmpField.getInt(tmpField); + } catch (Exception e) { + format = TangoConstHelper.UNKNOW_TYPE; + break; + } + if (tangoType == tmpFieldValue) { + if (isBoolean(tmpFieldName)) { + format = TangoConstHelper.BOOLEAN_FORMAT; + break; + } else if (isString(tmpFieldName)) { + format = TangoConstHelper.STRING_FORMAT; + break; + } else if (isNumerical(tmpFieldName)) { + format = TangoConstHelper.NUMERICAL_FORMAT; + break; + } + } + } + } + return format; + } + + public static String getTangoTypeName(int tangotype) { + String type = UNKNOW_TYPE_STR; + Field[] fieldList = TangoConst.class.getFields(); + Field tmpField = null; + String tmpFieldName = null; + int tmpFieldValue = 0; + for (Field element : fieldList) { + tmpField = element; + tmpFieldName = tmpField.getName(); + if (isATangoType(tmpFieldName)) { + try { + tmpFieldValue = tmpField.getInt(tmpField); + } catch (Exception e) { + type = UNKNOW_TYPE_STR; + break; + } + if (tangotype == tmpFieldValue) { + type = tmpFieldName; + break; + } + } + } + return type; + } + + +} + diff --git a/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoDeviceHelper.java b/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoDeviceHelper.java new file mode 100644 index 0000000000..8835bf03a6 --- /dev/null +++ b/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoDeviceHelper.java @@ -0,0 +1,855 @@ +package org.phoebus.pv.tango; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.tango.utils.TangoUtil; + +import fr.esrf.Tango.DevFailed; +import fr.esrf.Tango.DevState; +import fr.esrf.Tango.DevVarLongStringArray; +import fr.esrf.TangoApi.Database; +import fr.esrf.TangoApi.DbDatum; +import fr.esrf.TangoApi.DeviceData; +import fr.esrf.TangoApi.DeviceInfo; +import fr.esrf.TangoApi.DeviceProxy; +import fr.esrf.TangoApi.DeviceProxyFactory; +import fr.esrf.TangoApi.StateUtilities; + +public class TangoDeviceHelper { + public static final String SLASH = "/"; + + public static final String ANY = "*"; + private static final String ALL_DEVICES_QUERY = "select name from device where class='%s' order by name"; + private static final String SQL_SELECT = "DbMySqlSelect"; + private static final String CANNOT_GET_DEVICE_CLASS_OF = "Cannot get device class of "; + private static final String CANNOT_GET_EXPORTED_DEVICES_OF_CLASS = "Cannot get exported devices of class "; + private static final String CANNOT_GET_ALL_DEVICES_OF_CLASS = "Cannot get all devices of class "; + private static final String CANNOT_READ_STATUS_ON = "Cannot read status on "; + private static final String CANNOT_READ_STATE_ON = "Cannot read state on "; + private static final String CANNOT_CREATE_DATABASE_DEVICE = "Cannot create database device"; + private static final String CANNOT_CREATE_DB_DATUM_ON_DEVICE_PROPERTY = "Cannot create DbDatum on device property"; + private static final String CANNOT_READ_PROPERTIES_OF_DEVICE = "Cannot read properties of device "; + private static final String STATE = "State"; + private static final String NEW_LINE = ":\n"; + private static final String UNKNOWN = "UNKNOWN"; + private static final String DEVICE = "Device '"; + private static final String IS_NOT_EXPORTED = "' is not exported"; + private static final String SEEMS_TO_BE_DOWN = "' seems to be down"; + private static final String UNEXPECTED_ERROR_ON_CLEAN_DEVICE = "Unexpected error on clean device"; + private static final String[] EMPTY = new String[0]; + private static Database database = null; + private static Boolean nodatabase = null; + + private static final Object DATABASE_LOCK = new Object(); + private static final List BAD_DEVICES = new ArrayList<>(); + protected static final int DEFAULT_TIMEOUT = 3000; + + // Once a device is dead, we prefer waiting 20s before checking its availability. + // The following 2 fields are here for that purpose + protected static final Map LAST_DEAD_TIME_MAP = new ConcurrentHashMap<>(); + + // Map that stores the devices desired timeout (user defined) + protected static final Map TIME_OUT_MAP = new ConcurrentHashMap<>(); + + /** + * Returns the device class for given device. + * + * @param deviceName The device name. + * @return A {@link String}. May be null. + */ + public static String getDeviceClass(String deviceName) { + String className = null; + try { + String device = recoverDeviceRealName(deviceName); + if (device != null) { + Database db = getDatabase(); + className = db == null ? null : db.get_class_for_device(device); + } + } catch (Exception e) { + StringBuilder builder = new StringBuilder(CANNOT_GET_DEVICE_CLASS_OF); + builder.append(deviceName); + if (e instanceof DevFailed) { + DevFailed df = (DevFailed) e; + builder.append('\n').append(TangoExceptionHelper.getErrorMessage(df)); + } + } + return className; + } + + /** + * Returns the known exported devices of given class according to given {@link Database}. + * + * @param className The class name. + * @param db The {@link Database}. + * @return A {@link String} array, never null. + */ + public static String[] getExportedDevicesOfClass(String className, Database db) { + String[] devices; + try { + if ((className == null) || className.isEmpty() || (db == null)) { + devices = EMPTY; + } else { + devices = db.get_device_exported_for_class(className); + } + } catch (Exception e) { + devices = EMPTY; + StringBuilder builder = new StringBuilder(CANNOT_GET_EXPORTED_DEVICES_OF_CLASS); + builder.append(className); + if (e instanceof DevFailed) { + DevFailed df = (DevFailed) e; + builder.append('\n').append(TangoExceptionHelper.getErrorMessage(df)); + } + } + return devices; + } + + /** + * Returns the known exported devices of given class. + * + * @param className The class name. + * @return A {@link String} array, never null. + */ + public static String[] getExportedDevicesOfClass(String className) { + return getExportedDevicesOfClass(className, getDatabase()); + } + + /** + * Returns all known devices (exported or not) of given class according to given {@link Database}. + * + * @param className The class name. + * @param db The {@link Database}. + * @return A {@link String} array, never null. + */ + public static String[] getAllDevicesOfClass(String className, Database db) { + String[] devices; + try { + if ((className == null) || className.isEmpty() || (db == null)) { + devices = EMPTY; + } else { + DeviceData commandArgument = new DeviceData(); + commandArgument.insert(String.format(ALL_DEVICES_QUERY, className)); + DeviceData commandResult = db.command_inout(SQL_SELECT, commandArgument); + if (commandResult == null) { + devices = EMPTY; + } else { + DevVarLongStringArray array = commandResult.extractLongStringArray(); + if ((array == null) || (array.svalue == null)) { + devices = EMPTY; + } else { + devices = array.svalue; + } + } + } + } catch (Exception e) { + devices = EMPTY; + StringBuilder builder = new StringBuilder(CANNOT_GET_ALL_DEVICES_OF_CLASS); + builder.append(className); + if (e instanceof DevFailed) { + DevFailed df = (DevFailed) e; + builder.append('\n').append(TangoExceptionHelper.getErrorMessage(df)); + } + } + return devices; + } + + /** + * Returns all known devices (exported or not) of given class. + * + * @param className The class name. + * @return A {@link String} array, never null. + */ + public static String[] getAllDevicesOfClass(String className) { + return getAllDevicesOfClass(className, getDatabase()); + } + + /** + * Recovers the full name of given device. + * + * @param deviceName The device name or alias. + * @return A {@link String}. May be null. + * @throws DevFailed If a problem occurred. + */ + protected static String recoverDeviceRealName(String deviceName) throws DevFailed { + String device; + if (deviceName == null) { + device = null; + } else { + device = deviceName.trim(); + if (device.isEmpty()) { + device = null; + } else { + device = TangoUtil.getfullNameForDevice(device); + } + } + if (device != null) { + device = device.toLowerCase(); + } + return device; + } + + /** + * Recovers the status of given device. + * + * @param deviceName The device name. + * @return A {@link String}. Never null. + */ + public static String getDeviceStatus(String deviceName) { + String status = UNKNOWN; + DeviceProxy proxy = getDeviceProxy(deviceName, false); + if (proxy != null) { + try { + // JAVAAPI-603: synchronize accesses to DeviceProxy + synchronized (proxy) { + status = proxy.status(); + } + } catch (DevFailed e) { + StringBuilder builder = new StringBuilder(CANNOT_READ_STATUS_ON); + builder.append(deviceName).append('\n'); + builder.append(TangoExceptionHelper.getErrorMessage(e)); + status = UNKNOWN; + } + } + return status; + } + + /** + * Recovers the state of given device. + * + * @param deviceName The device name. + * @return A {@link String}. Never null. + */ + public static String getDeviceState(String deviceName) { + String state = StateUtilities.getNameForState(DevState.UNKNOWN); + try { + state = getDeviceStateWithError(deviceName); + } catch (DevFailed e) { + state = StateUtilities.getNameForState(DevState.UNKNOWN); + StringBuilder builder = new StringBuilder(CANNOT_READ_STATE_ON); + builder.append(deviceName).append('\n'); + builder.append(TangoExceptionHelper.getErrorMessage(e)); + } + return state; + } + + /** + * Recovers the state for given device, throwing encountered {@link DevFailed} if any. + * + * @param deviceName The device name. + * @return A {@link String}. Never null. + * @throws DevFailed If a problem occurred. + */ + public static String getDeviceStateWithError(String deviceName) throws DevFailed { + String state = StateUtilities.getNameForState(DevState.UNKNOWN); + DeviceProxy proxy = getDeviceProxy(deviceName, false); + if (proxy != null) { + DeviceData dData; + // JAVAAPI-603: synchronize accesses to DeviceProxy + synchronized (proxy) { + dData = proxy.command_inout(STATE); + } + if (dData != null) { + DevState dState = dData.extractDevState(); + state = StateUtilities.getNameForState(dState); + } + } + return state; + } + + /** + * Returns the known {@link Database}. + * + * @return A {@link Database}. May be null. + */ + public static Database getDatabase() { + // double check with lock to ensure thread safety + if (database == null) { + try { + synchronized (DATABASE_LOCK) { + if (database == null) { + try { + database = new Database(); + } catch (Exception e) { + StringBuilder builder = new StringBuilder(CANNOT_CREATE_DATABASE_DEVICE); + if (e instanceof DevFailed) { + builder.append(NEW_LINE); + builder.append(TangoExceptionHelper.getErrorMessage(e)); + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + return database; + } + + /** + * Returns the known timeout, expressed in milliseconds, for given device. + * + * @param device The device name. + * @return An int. + */ + public static int getTimeOutInMilliseconds(String device) { + int timeOut; + device = recoverDeviceName(device); + if (device == null) { + timeOut = DEFAULT_TIMEOUT; + } else { + Integer tmp = TIME_OUT_MAP.get(device); + timeOut = tmp == null ? DEFAULT_TIMEOUT : tmp.intValue(); + } + return timeOut; + } + + /** + * Sets the timeout, expressed in milliseconds, for given device. + * + * @param device The device name. + * @param timeOut The timeout to set. If timeOut < 1, then default timeout (i.e. 3s) is applied. + */ + public static void setTimeOutInMilliseconds(String device, int timeOut) { + device = recoverDeviceName(device); + if (device != null) { + if (timeOut < 1) { + timeOut = DEFAULT_TIMEOUT; + } + TIME_OUT_MAP.put(device, Integer.valueOf(timeOut)); + getDeviceProxy(device, false); + } + } + + /** + * Returns the {@link DeadDeviceCheck} for given device. + * + * @param device The device name. + * @return A {@link DeadDeviceCheck}. Never null. + */ + protected static DeadDeviceCheck getDeadDeviceCheck(String device) { + DeadDeviceCheck deviceCheck = LAST_DEAD_TIME_MAP.get(device); + if (deviceCheck == null) { + deviceCheck = new DeadDeviceCheck(); + DeadDeviceCheck tmp = LAST_DEAD_TIME_MAP.putIfAbsent(device, deviceCheck); + if (tmp != null) { + deviceCheck = tmp; + } + } + return deviceCheck; + } + + /** + * Returns the {@link DeviceProxy} for given device, managing known timeout. + * + * @param device The device name. + * @return A {@link DeviceProxy}. + * @throws DevFailed If a problem occurred. + */ + protected static DeviceProxy getDeviceProxyWithTimeOut(String device) throws DevFailed { + DeviceProxy proxy; + if (device == null) { + proxy = null; + } else { + int timeOut = getTimeOutInMilliseconds(device); + // We use a DeadDeviceCheck to try to limit the access failures: PROBLEM-1820 and PROBLEM-1715. + DeadDeviceCheck deviceCheck = getDeadDeviceCheck(device); + // We take a synchronized access to limit the number of access attempt at the same time. + synchronized (deviceCheck) { + if ((System.currentTimeMillis() - deviceCheck.getLastCheckedDate() < deviceCheck.getTimeOutMultiplier() + * timeOut)) { + // If the availability check comes too early, consider DeviceProxy as dead --> null. + proxy = null; + } else { + // The availability check is not too early: really try to get DeviceProxy. + try { + proxy = DeviceProxyFactory.get(device); + if (proxy != null) { + // JAVAAPI-603: synchronize accesses to DeviceProxy + synchronized (proxy) { + if (timeOut != proxy.get_timeout_millis()) { + proxy.set_timeout_millis(timeOut); + } + } + } + // Consider device as back available + if (deviceCheck.getLastCheckedDate() != 0) { + deviceCheck.reset(); + } + } catch (DevFailed df) { + // Consider device as dead + proxy = null; + // Updates last device check date, which increases the timeout multiplier. + deviceCheck.updateLastCheckedDate(System.currentTimeMillis()); + throw df; + } + } + } + } + return proxy; + } + + /** + * Returns the {@link DeviceProxy} for given device, potentially testing whether it is running. + * + * @param key The device name. + * @param testIfRunning Whether to do the tests for running device. + * @return A {@link DeviceProxy}. May be null. + */ + public static DeviceProxy getDeviceProxy(String key, boolean testIfRunning) { + DeviceProxy proxy = null; + if ((key != null) && (!key.trim().isEmpty())) { + if ((!testIfRunning) || isDeviceRunning(key)) { + String device = key.toLowerCase(); + Exception error = null; + try { + proxy = getDeviceProxyWithTimeOut(device); + } catch (Exception e) { + proxy = null; + error = e; + } + if (proxy == null) { + cleanDevice(device, getDownDeviceErrorMessage(device), error); + } else { + setBackToGood(device); + } + } + } + return proxy; + } + + /** + * Returns the {@link DeviceProxy} for given device, testing whether it is running. + * + * @param key The device name. + * @return A {@link DeviceProxy}. May be null. + */ + public static DeviceProxy getDeviceProxy(String key) { + return getDeviceProxy(key, true); + } + + /** + * Consider given device as running (i.e. not dead). + * + * @param key The device name. + */ + protected static void setBackToGood(String key) { + if (key != null) { + String device = key.toLowerCase(); + synchronized (BAD_DEVICES) { + BAD_DEVICES.remove(device); + } + LAST_DEAD_TIME_MAP.remove(device); + } + } + + /** + * Returns whether given device is dead (i.e. not running). + * + * @param key The device name. + * @return A boolean. + */ + public static boolean isDeadDevice(String key) { + boolean bad = false; + if (key != null) { + String device = key.toLowerCase(); + synchronized (BAD_DEVICES) { + bad = BAD_DEVICES.contains(device); + } + } + return bad; + } + + /** + * Recovers the full name of given device. + * + * @param deviceAliasOrName The device name or alias. + * @return A {@link String}. null if deviceAliasOrName is null. + */ + public static String recoverDeviceName(final String deviceAliasOrName) { + String deviceName; + try { + deviceName = recoverDeviceRealName(deviceAliasOrName); + if (deviceName == null) { + deviceName = deviceAliasOrName; + } + } catch (DevFailed df) { + deviceName = deviceAliasOrName; + } + return deviceName; + } + + /** + * Recovers the device name for given entity (attribute or command). + * + * @param fullEntityName The entity full name. + * @return A {@link String}. May be null. + */ + public static String getDeviceName(String fullEntityName) { + String deviceName = null; + if (fullEntityName != null) { + fullEntityName = fullEntityName.trim(); + if (!fullEntityName.isEmpty()) { + try { + // TangoUtil attribute methods are in fact compatible with command names + deviceName = TangoUtil + .getfullDeviceNameForAttribute(TangoUtil.getfullAttributeNameForAttribute(fullEntityName)); + } catch (Exception e) { + deviceName = null; + } + } + } + return deviceName; + } + + /** + * Recovers the entity (attribute or command) name for given entity full name. + * + * @param fullEntityName The entity full name. + * @return A {@link String}. May be null. + */ + public static String getEntityName(String fullEntityName) { + String entityName = fullEntityName; + if (entityName != null) { + entityName = entityName.trim(); + if (!entityName.isEmpty()) { + String tmp = entityName; + try { + // TangoUtil.getAttributeName is in fact compatible with command names + entityName = TangoUtil.getAttributeName(tmp); + } catch (Exception e) { + entityName = tmp; + } + } + } + return entityName; + } + + /** + * Returns whether given device exists. + * + * @param deviceName The device name. + * @return A boolean. + */ + public static boolean deviceExists(String deviceName) { + boolean exists; + Database database = getDatabase(); + String device; + try { + device = recoverDeviceRealName(deviceName); + } catch (Exception e) { + device = null; + } + if ((database != null) && (device != null)) { + try { + DeviceInfo deviceInfo = database.get_device_info(device); + exists = (deviceInfo != null); + } catch (DevFailed e) { + exists = false; + } + } else { + exists = false; + } + return exists; + } + + /** + * Returns the known {@link Database}. + * + * @return A {@link Database}. May be null. + */ + public static boolean isNoDataBase() { + if(nodatabase == null) { + nodatabase = getDatabase() == null; + } + return nodatabase; + } + + + + /** + * Returns whether given device is running. + * + * @param deviceName The device name. + * @return A boolean. + */ + public static boolean isDeviceRunning(String deviceName) { + boolean running; + if ((deviceName != null) && (!deviceName.trim().isEmpty())) { + String device = deviceName.toLowerCase(); + try { + boolean exported; + if (isNoDataBase()) { + exported = true; + } else { + Database database = getDatabase(); + String tmp; + try { + tmp = recoverDeviceRealName(deviceName); + } catch (Exception e) { + tmp = null; + } + device = tmp; + if ((database == null) || (device == null)) { + exported = false; + } else { + DeviceInfo deviceInfo = database.get_device_info(device); + // If the device is exported + exported = deviceInfo.exported; + } + } + if (exported) { + try { + final DeviceProxy proxy = getDeviceProxyWithTimeOut(device); + if (proxy == null) { + running = false; + cleanDevice(device, getDownDeviceErrorMessage(device), null); + } else { + // DO NOT CALL our getDeviceState() method + // because it will call isDeviceRunning() + // JAVAAPI-603: synchronize accesses to DeviceProxy + synchronized (proxy) { + proxy.ping(); + } + running = true; + setBackToGood(device); + } + } catch (Exception e) { + // ZOMBIE devices -> MANTIS 0021917 + // database.unexport_device(deviceName); + // Reconnexion bug in jacorb 21887 note 68002 + // deviceProxyTable.remove(deviceName.toLowerCase()); + running = false; + cleanDevice(device, getDownDeviceErrorMessage(device), /*e*/null); + } + } else { + running = false; + cleanDevice(device, DEVICE + device + IS_NOT_EXPORTED, null); + } + } catch (Exception e) { + cleanDevice(device, DEVICE + device + IS_NOT_EXPORTED, e); + running = false; + } + } else { + running = false; + } + return running; + } + + /** + * Returns the error message that indicates given device is down. + * + * @param device The device name. + * @return A {@link String}. + */ + protected static String getDownDeviceErrorMessage(String device) { + return DEVICE + device + SEEMS_TO_BE_DOWN; + } + + /** + * Completely forgets given device, indicated potentially encountered error. + *

+ * This method is useful in case of network connection problems. + *

+ * + * @param deviceNameToLowerCase The device name, already lower cased. + * @param errorMessage The error message. + * @param error The error. + */ + protected static void cleanDevice(String deviceNameToLowerCase, String errorMessage, Throwable error) { + // No need to take lock on DEVICE_LOCK as calls to this method are surrounded with this lock + if ((deviceNameToLowerCase != null) && (!deviceNameToLowerCase.trim().isEmpty())) { + boolean canLog = false; + // Tango NullPointerException workaround (Mantis 25468 & 25743) + try { + DeviceProxyFactory.remove(deviceNameToLowerCase); + } catch (Exception e) { + logError(e, UNEXPECTED_ERROR_ON_CLEAN_DEVICE); + } + synchronized (BAD_DEVICES) { + if (!BAD_DEVICES.contains(deviceNameToLowerCase)) { + BAD_DEVICES.add(deviceNameToLowerCase); + canLog = true; + } + } + if (canLog) { + logError(error, errorMessage); + } + DeadDeviceCheck deviceCheck = getDeadDeviceCheck(deviceNameToLowerCase); + synchronized (deviceCheck) { + if (deviceCheck.getLastCheckedDate() == 0) { + deviceCheck.updateLastCheckedDate(System.currentTimeMillis()); + } + } + } + } + + /** + * Traces an error message in logs. + * + * @param error The error. + * @param errorMessage The error message. + */ + protected static void logError(Throwable error, String errorMessage) { + if (error == null) { + System.err.println(errorMessage); + } else if (error instanceof DevFailed) { + System.err.println(errorMessage + '\n' + TangoExceptionHelper.getErrorMessage(error)); + } else { + System.err.println(errorMessage + " " + error.getMessage()); + } + } + + /** + * Returns the type (scalar, array or unknown) of given device propeperty. + * + * @param deviceName The device name. + * @param propertyName The property name. + * @return An int. Can be any of these: + *
    + *
  • {@link TangoConstHelper#SCALAR_TYPE}
  • + *
  • {@link TangoConstHelper#ARRAY_TYPE}
  • + *
  • {@link TangoConstHelper#UNKNOW_TYPE}
  • + *
+ */ + public static int getPropertyType(String deviceName, String propertyName) { + int type = TangoConstHelper.UNKNOW_TYPE; + DbDatum dbDatum = createDbDatum(deviceName, propertyName); + if (dbDatum != null) { + if (dbDatum.size() == 1) { + type = TangoConstHelper.SCALAR_TYPE; + } + if (dbDatum.size() > 1) { + type = TangoConstHelper.ARRAY_TYPE; + } + } + return type; + } + + /** + * Returns whether given device has given property. + * + * @param deviceName The device name. + * @param propertyName The property name. + * @return A boolean. + */ + public static boolean hasProperty(String deviceName, String propertyName) { + return getOriginalPropertyName(deviceName, propertyName, true) != null; + } + + /** + * Recovers the case sensitive property name for given device. + * + * @param deviceName The device name. + * @param propertyName The property name (case insensitive). + * @param nullIfNonExisting Whether to return null if such property doesn't exist. + * @return A {@link String}: the property name respecting the original case. + */ + public static String getOriginalPropertyName(String deviceName, String propertyName, boolean nullIfNonExisting) { + String name = nullIfNonExisting ? null : propertyName; + if ((deviceName != null) && (!deviceName.isEmpty()) && (propertyName != null) && (!propertyName.isEmpty())) { + try { + String device = recoverDeviceRealName(deviceName); + if (device != null) { + Database db = getDatabase(); + String[] properties = db == null ? null : db.get_device_property_list(deviceName, ANY); + if ((properties != null) && (properties.length > 0)) { + for (String property : properties) { + if ((property != null) && (propertyName.equalsIgnoreCase(property))) { + name = property; + break; + } + } + } + } + } catch (DevFailed df) { + logError(df, CANNOT_READ_PROPERTIES_OF_DEVICE + deviceName); + } + } + return name; + } + + /** + * Recovers given device property. + * + * @param deviceName The device name. + * @param propertyName The property name. + * @return A {@link DbDatum}. + */ + public static DbDatum createDbDatum(String deviceName, String propertyName) { + DbDatum dbDatum = null; + if ((deviceName != null) && (!deviceName.isEmpty()) && (propertyName != null) && (!propertyName.isEmpty())) { + try { + String device = recoverDeviceRealName(deviceName); + if (device != null) { + Database db = getDatabase(); + dbDatum = db == null ? null : db.get_device_property(device, propertyName); + } + } catch (DevFailed df) { + logError(df, CANNOT_CREATE_DB_DATUM_ON_DEVICE_PROPERTY); + } + } + return dbDatum; + } + + // ///////////// // + // Inner classes // + // ///////////// // + + /** + * A class to manage dead devices. + *

+ * It stores the last device check date (the last time there was an attempt to access to the device) and the value + * by which to multiply the timeout to obtain the delay before next allowed check. + *

+ * + * @author GIRARDOT + */ + protected static class DeadDeviceCheck { + private volatile long lastCheckedDate; + private volatile int timeOutMultiplier; + + public DeadDeviceCheck() { + reset(); + } + + /** + * Updates the last checked date, increasing the multiply value by one. + * + * @param date The last checked date. + */ + public void updateLastCheckedDate(long date) { + lastCheckedDate = date; + timeOutMultiplier = Math.min(timeOutMultiplier + 1, 7); + } + + /** + * Returns the last checked date (the date at which the last access attempt happened). + * + * @return A long. + */ + public long getLastCheckedDate() { + return lastCheckedDate; + } + + /** + * Returns the value by which to multiply device timeout in order to obtain the time to wait before next allowed + * device check. + * + * @return An int. + */ + public int getTimeOutMultiplier() { + return timeOutMultiplier; + } + + /** + * Resets this {@link DeadDeviceCheck}. Call it when you successfully access to the device. + */ + public void reset() { + lastCheckedDate = 0; + timeOutMultiplier = 1; + } + } + +} diff --git a/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoExceptionHelper.java b/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoExceptionHelper.java new file mode 100644 index 0000000000..386668a836 --- /dev/null +++ b/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoExceptionHelper.java @@ -0,0 +1,108 @@ +package org.phoebus.pv.tango; + +import org.tango.utils.DevFailedUtils; + +import fr.esrf.Tango.DevError; +import fr.esrf.Tango.DevFailed; + +/** + * Tango DevFailed helper + * + * @author katy.saintin@cea.fr + */ +public class TangoExceptionHelper { + private static final String DESCRIPTION_IS = "Description = "; + private static final String ORIGIN_IS = "\nOrigin= "; + private static final String REASON_IS = "\nReason= "; + private static final String SEPARATOR = ": "; + private static final String AT = "\n\tat"; + private static final String CAUSED_BY = "\nCaused by: "; + + public static final String REASON = "\nReason: "; + + private static final String IDL = "IDL"; + private static final String DESC = "desc:"; + + public static String[] getErrorArray(DevFailed e) { + String[] result = null; + if (e != null) { + DevError[] traces = e.errors; + if (traces != null) { + result = new String[traces.length]; + for (int i = 0; i < traces.length; i++) { + StringBuilder errorBuider = new StringBuilder(DESCRIPTION_IS); + errorBuider.append(traces[i].desc); + errorBuider.append(ORIGIN_IS).append(traces[i].origin); + errorBuider.append(REASON_IS).append(traces[i].reason); + result[i] = errorBuider.toString(); + } + } + } + return result; + } + + public static StringBuilder errorTraceToStringBuilder(Throwable t, StringBuilder builder) { + return errorTraceToStringBuilder(t, builder, true); + } + + public static StringBuilder errorTraceToStringBuilder(Throwable t, StringBuilder builder, boolean traceCause) { + if (builder == null) { + builder = new StringBuilder(); + } + if (t != null) { + if (t instanceof DevFailed) { + builder.append(DevFailedUtils.toString((DevFailed) t)); + } else { + builder.append(t.getClass().getName()); + String message = t.getLocalizedMessage(); + if (message != null) { + builder.append(SEPARATOR).append(message); + } + if (t.getStackTrace() != null) { + for (StackTraceElement element : t.getStackTrace()) { + builder.append(AT).append(element); + } + } + if (traceCause && (t.getCause() != null)) { + builder.append(CAUSED_BY); + errorTraceToStringBuilder(t.getCause(), builder); + } + } + } + return builder; + } + + /** + * Extracts the error message of a {@link Throwable} + * + * @param t The {@link Throwable} + * @return A {@link String}: the error message. + */ + public static String getErrorMessage(Throwable t) { + String message = null; + if (t != null) { + if (t instanceof DevFailed) { + // Try to extract error message. 2 possible case + // - Either e.getMessage() contains the expected message + // - Or it contains a not user friendly message with "IDL...", + // which means expected message is in description + message = t.getMessage(); + if (message.indexOf(IDL) > -1) { + // 2nd case: search in description + message = DevFailedUtils.toString((DevFailed) t); + int index = message.indexOf(DESC); + if (index > -1) { + int index2 = message.indexOf('\n', index); + if (index2 > -1) { + message = message.substring(index + DESC.length(), index2).trim(); + } + } + } + } else { + message = t.getMessage(); + } + } + return message; + } + +} diff --git a/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoPV.java b/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoPV.java new file mode 100644 index 0000000000..5165a611f8 --- /dev/null +++ b/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoPV.java @@ -0,0 +1,461 @@ +package org.phoebus.pv.tango; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; + +import org.epics.util.stats.Range; +import org.epics.vtype.Alarm; +import org.epics.vtype.Display; +import org.epics.vtype.EnumDisplay; +import org.epics.vtype.Time; +import org.epics.vtype.VDouble; +import org.epics.vtype.VEnum; +import org.epics.vtype.VString; +import org.epics.vtype.VType; +import org.phoebus.pv.PV; +import org.tango.server.events.EventType; + +import fr.esrf.Tango.AttrDataFormat; +import fr.esrf.Tango.AttrWriteType; +import fr.esrf.Tango.EventProperties; +import fr.esrf.TangoApi.AttributeEventInfo; +import fr.esrf.TangoApi.AttributeInfo; +import fr.esrf.TangoApi.AttributeInfoEx; +import fr.esrf.TangoApi.CallBack; +import fr.esrf.TangoApi.CommandInfo; +import fr.esrf.TangoApi.DeviceAttribute; +import fr.esrf.TangoApi.DeviceProxy; +import fr.esrf.TangoApi.events.EventData; +import fr.soleil.tango.clientapi.InsertExtractUtils; + +/** + * Generic Tango PV manage both attribute and command + * + * @author katy.saintin@cea.fr + */ +public class TangoPV extends PV { + + private static enum MetaData { + DESC, LABEL, WRITE, EGU, HOPR, HIHI, HIGH, LOPR, LOW, LOLO, STAT + }; + + private static final String DOT = "."; + + private String device; + private String entityName; + private DeviceProxy deviceProxy = null; + private AttributeInfo attributeInfo = null; + private CommandInfo commandInfo = null; + private Display display = null; + private EnumDisplay enumDisplay = null; + private Boolean entityFound = null; + private MetaData metaData = null; + private TangoCallBack cb = null; + + private static final int DEFAULT_PERIOD_REFRESHING = 1000; + private int period = DEFAULT_PERIOD_REFRESHING; + private static final String UNDEFINED = "Not specified"; + private static Map pollingMode = new HashMap<>(); + + public TangoPV(String name, String baseName) { + super(name); + if (TangoPreferences.getInstance().isTangoDbEnable()) { + // First remove .DESC field + String fullDeviceName = baseName; + // Test if it is a metadata + metaData = getMetadata(fullDeviceName); + if (metaData != null) { + fullDeviceName = fullDeviceName.replace(DOT + metaData.toString(), ""); + } + device = TangoDeviceHelper.getDeviceName(fullDeviceName); + entityName = TangoDeviceHelper.getEntityName(fullDeviceName); + + initTangoEntity(); + } else { + // No attribute info found + VType initValue = VString.of("--", Alarm.disconnected(), Time.now()); + notifyListenersOfValue(initValue); + } + } + + private MetaData getMetadata(String fullDeviceName) { + MetaData md = null; + if (fullDeviceName != null && fullDeviceName.contains(DOT)) { + MetaData[] values = MetaData.values(); + for (MetaData val : values) { + if (fullDeviceName.endsWith(DOT + val.toString())) { + md = val; + break; + } + } + } + return md; + } + + private boolean isMetaData() { + return metaData != null && metaData != MetaData.WRITE && metaData != MetaData.STAT; + } + + private void initTangoEntity() { + VType initValue = null; + AttributeInfoEx attributeInfoEx = null; + String description = null; + if (entityFound == null) { + deviceProxy = TangoDeviceHelper.getDeviceProxy(device, true); + if (deviceProxy == null) { + entityFound = false; + } + // Init Attribute or command + if (deviceProxy != null && entityName != null) { + try { + // Test if it is an attribute + if (TangoAttributeHelper.isAttributeRunning(device, entityName)) { + attributeInfo = TangoAttributeHelper.getAttributeInfo(device, entityName); + entityFound = attributeInfo != null; + if (entityFound) { + description = attributeInfo.description; + if (description == null || description.trim().isEmpty()) { + description = attributeInfo.toString(); + } + attributeInfoEx = TangoAttributeHelper.getAttributeInfoEx(device, entityName); + if (metaData != null && metaData == MetaData.STAT) { + enumDisplay = TangoPVUtil.getAttributeQualityEnumDisplay(attributeInfo); + display = ((AdvancedEnumDisplay) enumDisplay).getDisplay(); + + } else if (entityName.equalsIgnoreCase("State")) { + // Build State enumeration + enumDisplay = TangoPVUtil.getDevStateEnumDisplay(device); + display = ((AdvancedEnumDisplay) enumDisplay).getDisplay(); + } else { + display = TangoPVUtil.buildDisplayFromAttributeInfo(attributeInfo, attributeInfoEx, + description); + enumDisplay = TangoPVUtil.buildEnumDisplayFromAttributeInfo(attributeInfoEx, display); + } + // It is not a description, read attribute value + if (!isMetaData()) { + initValue = readValue(); + } + } else { + // No attribute info found + initValue = VString.of("--", Alarm.disconnected(), Time.now()); + } + } else if (TangoCommandHelper.doesCommandExist(device, entityName)) { + commandInfo = TangoCommandHelper.getCommandInfo(deviceProxy, entityName); + entityFound = commandInfo != null; + if (entityFound) { + description = commandInfo.out_type_desc; + if (description == null || description.trim().isEmpty()) { + description = commandInfo.toString(); + } + // It is not a description, empty value + if (!isMetaData()) { + initValue = VString.of("--", Alarm.none(), Time.now()); + } + } else { + // No command info found + initValue = VString.of("--", Alarm.disconnected(), Time.now()); + } + } else { + entityFound = false; + initValue = VString.of("--", Alarm.disconnected(), Time.now()); + } + } catch (Exception e) { + entityFound = false; + String errorMessage = TangoExceptionHelper.getErrorMessage(e); + initValue = VString.of(errorMessage, Alarm.disconnected(), Time.now()); + } + } + } + + if (isMetaData() && entityFound) { + // Value is fix no need subscription + Double dVal = null; + String numberVal = null; + String sVal = null; + switch (metaData) { + case DESC: + // Init Display + // Value is fixe no need subscription + sVal = description; + break; + + case LABEL: + sVal = attributeInfo != null ? attributeInfo.label : null; + if (sVal == null || sVal.trim().isEmpty()) { + sVal = entityName; + } + break; + + case EGU: + sVal = attributeInfo != null ? attributeInfo.unit : null; + if (sVal == null || sVal.trim().isEmpty()) { + sVal = ""; + } + break; + + case HOPR: + numberVal = attributeInfo != null ? attributeInfo.max_value : null; + dVal = getValue(numberVal); + break; + + case LOPR: + numberVal = attributeInfo != null ? attributeInfo.min_value : null; + dVal = getValue(numberVal); + break; + + case LOW: + numberVal = attributeInfoEx != null ? attributeInfoEx.alarms.min_warning : null; + dVal = getValue(numberVal); + break; + + case HIGH: + numberVal = attributeInfoEx != null ? attributeInfoEx.alarms.max_warning : null; + dVal = getValue(numberVal); + break; + + case LOLO: + numberVal = attributeInfo != null ? attributeInfo.min_alarm : null; + dVal = getValue(numberVal); + break; + + case HIHI: + numberVal = attributeInfo != null ? attributeInfo.max_alarm : null; + dVal = getValue(numberVal); + break; + + default: + break; + } + + if (sVal != null) { + initValue = VString.of(sVal, Alarm.none(), Time.now()); + } else if (dVal != null) { + display = Display.of(Range.undefined(), Range.undefined(), Range.undefined(), Range.undefined(), "", + Display.defaultNumberFormat(), description); + initValue = VDouble.of(dVal, Alarm.none(), Time.now(), display); + } + } + + if (initValue != null) { + notifyListenersOfValue(initValue); + } + + // Start monitoring event or polling + if (attributeInfoEx != null && !isMetaData()) { + // Check if event is activate on device + Boolean polling = pollingMode.get(device); + if (deviceProxy != null && polling == null) { + CallBack dummyCb = new CallBack(); + try { + int id = deviceProxy.subscribe_event(EventType.PERIODIC_EVENT.getValue(), dummyCb, true); + polling = false; + deviceProxy.unsubscribe_event(id); + } catch (Exception e) { + polling = true; + pollingMode.put(device, polling); + } + } + + AttributeEventInfo events = attributeInfoEx.events; + EventProperties tangoObj = events.getTangoObj(); + String periodString = tangoObj.per_event != null ? tangoObj.per_event.period : null; + EventType eventType = EventType.PERIODIC_EVENT; + if (periodString != null && !periodString.isEmpty() && !periodString.equals(UNDEFINED)) { + eventType = EventType.PERIODIC_EVENT; + period = Double.valueOf(periodString).intValue(); + } else if (tangoObj.ch_event != null) { + eventType = EventType.CHANGE_EVENT; + } + cb = new TangoCallBack(eventType); + } + } + + private Double getValue(String val) { + Double dval = null; + if (val != null && !val.trim().isEmpty()) { + try { + dval = Double.valueOf(val.trim()); + + } catch (Exception e) { + dval = null; + } + } + return dval; + } + + private class TangoCallBack extends CallBack { + private static final long serialVersionUID = 1L; + private int event_id; + private Thread threadReader = null; + private boolean start = false; + + public TangoCallBack(EventType eventType) { + Boolean polling = pollingMode.get(device); + if (!polling) { + try { + event_id = deviceProxy.subscribe_event(entityName, eventType.getValue(), this, new String[] {}); + } catch (Exception e) { + String message = TangoExceptionHelper.getErrorMessage(e); + System.err.println("Error on subscription " + message); + } + } + + else if (!start) { + start = true; + threadReader = new Thread(getName() + " reader") { + public void run() { + while (start) { + VType value = readValue(); + if (value != null) { + notifyListenersOfValue(value); + } + + try { + Thread.sleep(period); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + }; + }; + threadReader.start(); + } + } + + @Override + public void push_event(EventData evt) { + VType value = readValue(); + if (value != null) { + notifyListenersOfValue(value); + } + } + + public void unsubscribe_event() { + if (threadReader != null) { + start = false; + threadReader = null; + } else if (deviceProxy != null) { + try { + deviceProxy.unsubscribe_event(event_id); + } catch (Exception e) { + String message = TangoExceptionHelper.getErrorMessage(e); + System.err.println("Error on unsubscription " + message); + } + } + } + } + + private VType readValue() { + VType rValue = null; + if (entityFound && attributeInfo != null) { + try { + DeviceAttribute deviceAttribute = TangoAttributeHelper.getDeviceAttribute(deviceProxy, entityName); + int type = deviceAttribute.getType(); + AttrDataFormat dataFormat = deviceAttribute.getDataFormat(); + Alarm alarm = TangoPVUtil.buildAlarmFromAttribute(deviceAttribute); + long time = deviceAttribute.getTime(); + if (metaData != null && metaData == MetaData.STAT) { + // Read Quality of attribute + int attributeQuality = TangoAttributeHelper.getAttributeQuality(deviceAttribute); + Time t = Time.of(Instant.ofEpochMilli(time)); + rValue = VEnum.of(attributeQuality, enumDisplay, alarm, t); + } else { + Object result = null; + if (metaData != null && metaData == MetaData.WRITE) { + result = InsertExtractUtils.extractWrite(deviceAttribute, attributeInfo.writable, dataFormat); + } else { + result = InsertExtractUtils.extractRead(deviceAttribute, dataFormat); + } + + boolean isArray = dataFormat != null + && (dataFormat == AttrDataFormat.SPECTRUM || dataFormat == AttrDataFormat.IMAGE); + int[] sizes = null; + if (dataFormat == AttrDataFormat.IMAGE) { + sizes = new int[] { deviceAttribute.getDimX(), deviceAttribute.getDimY() }; + } + rValue = TangoPVUtil.convertResultToVtype(isArray, sizes, type, result, alarm, time, display, + enumDisplay); + } + } catch (Exception e) { + String errorMessage = TangoExceptionHelper.getErrorMessage(e); + rValue = VString.of(errorMessage, Alarm.disconnected(), Time.now()); + } + } + return rValue; + } + + private VType executeCommand(Object newValue) { + VType rValue = null; + if (entityFound && commandInfo != null) { + try { + Time time = Time.now(); + Object executeCommand = TangoCommandHelper.executeCommand(deviceProxy, entityName, newValue); + Alarm alarm = Alarm.none(); + if (executeCommand != null) { + int out_type = commandInfo.out_type; + String tangoTypeForType = TangoCommandHelper.getTangoTypeForType(out_type); + boolean isArray = tangoTypeForType.toUpperCase().endsWith(TangoConstHelper.ARRAY_NAME); + rValue = TangoPVUtil.convertResultToVtype(isArray, null, out_type, executeCommand, alarm, + time.getTimestamp().toEpochMilli(), display, enumDisplay); + } else { + rValue = VString.of("OK", alarm, time); + } + } catch (Exception e) { + String errorMessage = TangoExceptionHelper.getErrorMessage(e); + rValue = VString.of(errorMessage, Alarm.disconnected(), Time.now()); + } + + } + return rValue; + } + + @Override + public void write(Object new_value) throws Exception { + if (!isMetaData() && entityFound) { + VType rValue = null; + if (attributeInfo != null) { + DeviceAttribute deviceAttribute = TangoAttributeHelper.getDeviceAttribute(deviceProxy, entityName); + InsertExtractUtils.insert(deviceAttribute, new_value); + deviceProxy.write_attribute(deviceAttribute); + rValue = readValue(); + } else if (commandInfo != null) { + rValue = executeCommand(new_value); + } + if (rValue != null) { + notifyListenersOfValue(rValue); + } + } + } + + @Override + public boolean isReadonly() { + boolean isRO = true; + if (!isMetaData() && metaData != MetaData.STAT) { + if (attributeInfo != null) { + AttrWriteType writable = attributeInfo.writable; + isRO = writable == AttrWriteType.READ; + } else if (commandInfo != null) { + isRO = false; + } + } + return isRO; + } + + @Override + protected void close() { + deviceProxy = null; + commandInfo = null; + attributeInfo = null; + entityFound = null; + metaData = null; + period = DEFAULT_PERIOD_REFRESHING; + if (cb != null) { + cb.unsubscribe_event(); + cb = null; + } + TangoPVFactory.releasePV(this); + super.close(); + } +} diff --git a/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoPVFactory.java b/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoPVFactory.java new file mode 100644 index 0000000000..f6b434248d --- /dev/null +++ b/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoPVFactory.java @@ -0,0 +1,64 @@ +package org.phoebus.pv.tango; + +import java.util.HashMap; +import java.util.Map; + +import org.phoebus.pv.PV; +import org.phoebus.pv.PVFactory; +import org.phoebus.pv.PVPool; + +public class TangoPVFactory implements PVFactory { + + /** PV type implemented by this factory */ + final public static String TYPE = "tango"; + + /** Map of local PVs */ + private static final Map tango_pvs = new HashMap<>(); + + @Override + public String getType() { + return TYPE; + } + + @Override + public PV createPV(String name, String base_name) throws Exception { + + String actual_name = getCoreName(name); + // Actual name: tango://the_pv + // Add tango://prefix if not exist + if(!name.startsWith(TangoPVFactory.TYPE + "://")) { + actual_name = PVPool.TypedName.format(TangoPVFactory.TYPE, name); + } + TangoPV tangopv = tango_pvs.get(actual_name); + + //Check TANGO HOST + + if (tangopv == null) { + synchronized (tango_pvs) { + tangopv = new TangoPV(actual_name, base_name); + tango_pvs.put(actual_name, tangopv); + } + } + + return tangopv; + } + + @Override + public String getCoreName(String name) { + String actual_name = name; + // Actual name: tango://the_pv + // Add tango://prefix if not exist + if(!name.startsWith(TangoPVFactory.TYPE + "://")) { + actual_name = PVPool.TypedName.format(TangoPVFactory.TYPE, name); + } + return actual_name; + } + + protected static void releasePV(TangoPV tangopv) { + if (tango_pvs.containsKey(tangopv.getName())) { + synchronized (tango_pvs) { + tango_pvs.remove(tangopv.getName()); + } + } + } +} diff --git a/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoPVUtil.java b/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoPVUtil.java new file mode 100644 index 0000000000..76acca5624 --- /dev/null +++ b/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoPVUtil.java @@ -0,0 +1,483 @@ +package org.phoebus.pv.tango; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.time.Instant; + +import org.epics.util.array.ArrayByte; +import org.epics.util.array.ArrayDouble; +import org.epics.util.array.ArrayFloat; +import org.epics.util.array.ArrayInteger; +import org.epics.util.array.ArrayLong; +import org.epics.util.array.ArrayShort; +import org.epics.util.array.ListInteger; +import org.epics.util.stats.Range; +import org.epics.util.text.NumberFormats; +import org.epics.vtype.Alarm; +import org.epics.vtype.AlarmSeverity; +import org.epics.vtype.AlarmStatus; +import org.epics.vtype.Display; +import org.epics.vtype.EnumDisplay; +import org.epics.vtype.Time; +import org.epics.vtype.VByte; +import org.epics.vtype.VByteArray; +import org.epics.vtype.VDouble; +import org.epics.vtype.VDoubleArray; +import org.epics.vtype.VEnum; +import org.epics.vtype.VFloat; +import org.epics.vtype.VFloatArray; +import org.epics.vtype.VInt; +import org.epics.vtype.VIntArray; +import org.epics.vtype.VLong; +import org.epics.vtype.VLongArray; +import org.epics.vtype.VShort; +import org.epics.vtype.VShortArray; +import org.epics.vtype.VString; +import org.epics.vtype.VType; +import org.tango.utils.ArrayUtils; + +import fr.esrf.Tango.AttrQuality; +import fr.esrf.Tango.DevEncoded; +import fr.esrf.Tango.DevState; +import fr.esrf.TangoApi.AttributeAlarmInfo; +import fr.esrf.TangoApi.AttributeInfo; +import fr.esrf.TangoApi.AttributeInfoEx; +import fr.esrf.TangoApi.DeviceAttribute; + +/** + * Generic Tango PV util + * + * @author katy.saintin@cea.fr + */ + +public class TangoPVUtil { + + public static VType convertResultToVtype(boolean isArray, int[] sizes, int tangotype, Object aresult, Alarm alarm, long ltime, Display display, + EnumDisplay enumDisplay) { + VType resultType = null; + Object result = aresult; + if (result != null) { + int tgType = TangoConstHelper.getTangoType(tangotype); + String tangoFieldName = TangoConstHelper.getTangoFieldName(tangotype); + if (tgType != TangoConstHelper.UNKNOW_TYPE && tangoFieldName != null) { + int attFormat = TangoConstHelper.getAttributeTangoFormat(tangotype); + boolean unsigned = false; + boolean long64 = isLong64(tangoFieldName); + Time time = Time.of(Instant.ofEpochMilli(ltime)); + ListInteger intSize = null; + if(sizes != null) { + try { + result = ArrayUtils.from2DArrayToArray(aresult); + intSize = ArrayInteger.of(sizes); + } + catch (Exception e) { + intSize = null; + } + } + if (attFormat == TangoConstHelper.NUMERICAL_FORMAT) { + if (tangoFieldName.contains(TangoConstHelper.DOUBLE_NAME)) { + if (!isArray) { + resultType = VDouble.of((Double) result, alarm, time, display); + } else { + ArrayDouble arrayDouble = ArrayDouble.of((double[]) result); + if(intSize != null) { + resultType = VDoubleArray.of(arrayDouble, intSize, alarm, time, display); + } + else { + resultType = VDoubleArray.of(arrayDouble, alarm, time, display); + } + } + } else if (tangoFieldName.contains(TangoConstHelper.FLOAT_NAME)) { + if (!isArray) { + resultType = VFloat.of((Float) result, alarm, time, display); + } else { + ArrayFloat arrayFloat = ArrayFloat.of((float[]) result); + if(intSize != null) { + resultType = VFloatArray.of(arrayFloat, intSize, alarm, time, display); + } + else { + resultType = VFloatArray.of(arrayFloat, alarm, time, display); + } + } + } else if (tangoFieldName.contains(TangoConstHelper.INT_NAME) + || tangoFieldName.contains(TangoConstHelper.LONG_NAME)) { + resultType = convertToVIntType(result, isArray, intSize,alarm, time, display); + } else if (tangoFieldName.contains(TangoConstHelper.LONG_NAME)) { + if (!unsigned && !long64) { + resultType = convertToVIntType(result, isArray, intSize,alarm, time, display); + } else { + // Manage ULONG and ULONG64 + if (!isArray) { + resultType = VLong.of((Long) result, alarm, time, display); + } else { + ArrayLong arrayLong = ArrayLong.of((long[]) result); + if(intSize != null) { + resultType = VLongArray.of(arrayLong, intSize, alarm, time, display); + } + else { + resultType = VLongArray.of(arrayLong, alarm, time, display); + } + } + } + } else if (tangoFieldName.contains(TangoConstHelper.SHORT_NAME)) { + unsigned = isUnsigned(tangoFieldName, TangoConstHelper.SHORT_NAME); + if (unsigned) { + resultType = convertToVIntType(result, isArray, intSize, alarm, time, display); + } else { + if (!isArray) { + resultType = VShort.of((Short) result, alarm, time, display); + } else { + ArrayShort arrayshort = ArrayShort.of((short[]) result); + if(intSize != null) { + resultType = VShortArray.of(arrayshort, intSize, alarm, time, display); + } + else { + resultType = VShortArray.of(arrayshort, alarm, time, display); + } + } + } + } else if (tangoFieldName.contains(TangoConstHelper.CHAR_NAME)) { + unsigned = isUnsigned(tangoFieldName, TangoConstHelper.CHAR_NAME); + if (unsigned) { + if (!isArray) { + resultType = VShort.of((Short) result, alarm, time, display); + } else { + ArrayShort arrayshort = ArrayShort.of((short[]) result); + if(intSize != null) { + resultType = VShortArray.of(arrayshort, intSize,alarm, time, display); + } + else { + resultType = VShortArray.of(arrayshort, alarm, time, display); + } + } + } else { + byte[] byteArrays = (byte[]) result; + if (byteArrays.length > 0) { + if (byteArrays.length > 1) { + ArrayByte arraybyte = ArrayByte.of(byteArrays); + resultType = VByteArray.of(arraybyte, alarm, time, display); + } else { + resultType = VByte.of((Byte) byteArrays[0], alarm, time, display); + } + } + } + } else if (tangoFieldName.contains(TangoConstHelper.ENUM_NAME)) { + if (enumDisplay != null) { + resultType = VEnum.of((Short) result, enumDisplay, alarm, time); + } else { + resultType = VShort.of((Short) result, alarm, time, display); + } + } else if (tangoFieldName.contains(TangoConstHelper.ENCODED_NAME)) { + if (result instanceof DevEncoded) { + DevEncoded encoded = (DevEncoded) result; + ArrayByte arraybyte = ArrayByte.of(encoded.encoded_data); + resultType = VByteArray.of(arraybyte, alarm, time, display); + } + } + } else if (attFormat == TangoConstHelper.BOOLEAN_FORMAT) { + if (!isArray) { + if(enumDisplay == null) { + enumDisplay = AdvancedEnumDisplay.of("0","1"); + } + int val = (Boolean) result ? 1 : 0; + resultType = VEnum.of((short) val, enumDisplay, alarm, time); + } else { + boolean[] bArray = (boolean[]) result; + short[] shArray = new short[bArray.length]; + for (int i = 0; i < shArray.length; i++) { + shArray[i] = bArray[i] ? (short) 1 : (short) 0; + } + ArrayShort arrayshort = ArrayShort.of(shArray); + if(intSize != null) { + resultType = VShortArray.of(arrayshort, intSize, alarm, time, display); + } + else { + resultType = VShortArray.of(arrayshort, alarm, time, display); + } + } + } else {// Manage String + if (result instanceof DevState) { + DevState state = (DevState) result; + Alarm alarmState = alarm; + AlarmStatus status = AlarmStatus.DEVICE; + + switch (state.value()) { + case DevState._ALARM : + case DevState._DISABLE : + alarmState= Alarm.of(AlarmSeverity.MINOR, status, state.toString()); + break; + case DevState._FAULT : + alarmState= Alarm.of(AlarmSeverity.MAJOR, status, state.toString()); + break; + case DevState._UNKNOWN : + alarmState= Alarm.of(AlarmSeverity.MAJOR, AlarmStatus.UNDEFINED, state.toString()); + break; + } + resultType = VEnum.of(state.value(), enumDisplay, alarmState, time); + } else { + resultType = VString.of(result.toString(), alarm, time); + } + } + } + } + return resultType; + } + + private static VType convertToVIntType(Object result, boolean isArray, ListInteger intSize, Alarm alarm, Time time, Display display) { + VType vIntResult = null; + if (result != null) { + if (!isArray) { + vIntResult = VInt.of((Integer) result, alarm, time, display); + } else { + ArrayInteger arrayInt = ArrayInteger.of((int[]) result); + if(intSize != null) { + vIntResult = VIntArray.of(arrayInt, intSize, alarm, time, display); + } + else { + vIntResult = VIntArray.of(arrayInt, alarm, time, display); + } + } + } + return vIntResult; + } + + protected static Alarm buildAlarmFromAttribute(DeviceAttribute devAttr) { + AlarmSeverity sev = AlarmSeverity.INVALID; + AlarmStatus stat = AlarmStatus.UNDEFINED; + String alarmMessage = "Disconnected"; + if (devAttr != null) { + try { + int attributeQuality = TangoAttributeHelper.getAttributeQuality(devAttr); + alarmMessage = TangoAttributeHelper.getAttributeStringQuality(devAttr); + switch (attributeQuality) { + case AttrQuality._ATTR_VALID: + case AttrQuality._ATTR_CHANGING: + sev = AlarmSeverity.NONE; + stat = AlarmStatus.NONE; + break; + case AttrQuality._ATTR_WARNING: + sev = AlarmSeverity.MINOR; + stat = AlarmStatus.RECORD; + break; + case AttrQuality._ATTR_ALARM: + sev = AlarmSeverity.MAJOR; + stat = AlarmStatus.RECORD; + break; + } + } catch (Exception e) { + alarmMessage = TangoExceptionHelper.getErrorMessage(e); + } + } + return Alarm.of(sev, stat, alarmMessage); + } + + protected static Display buildDisplayFromAttributeInfo(AttributeInfo info, AttributeInfoEx infoEx, String description) { + Display display = null; + if (info != null && description != null) { + String unit = info.unit != null ? info.unit : ""; + String format = info.format; // TODO Define precision + String max_alarm = info.max_alarm; + String min_alarm = info.min_alarm; + String min_value = info.min_value; + String max_value = info.max_value; + AttributeAlarmInfo alarmInfo = infoEx != null ? infoEx.alarms : null ; + String min_warning = alarmInfo != null ? alarmInfo.min_warning : null; + String max_warning = alarmInfo != null ? alarmInfo.max_warning : null; + + Range displayRange = null; + Range alarmRange = null; + Range warningRange = null; + Range controlRange = null; + NumberFormat numberFormat = Display.defaultNumberFormat(); + + //Set Control Range and display Range + Double minValue = Double.NaN; + Double maxValue = Double.NaN; + if (min_value != null && !min_value.trim().isEmpty()) { + try { + minValue = Double.valueOf(min_value.trim()); + maxValue = Double.POSITIVE_INFINITY; + } catch (Exception e) { + minValue = Double.NaN; + } + } + + if (max_value != null && !max_value.trim().isEmpty()) { + try { + maxValue = Double.valueOf(max_value.trim()); + if (minValue.isNaN()) { + minValue = Double.NEGATIVE_INFINITY; + } + } catch (Exception e) { + if (!minValue.isNaN()) { + maxValue = Double.POSITIVE_INFINITY; + } else { + maxValue = Double.NaN; + } + } + } + + if(!minValue.isNaN() && !maxValue.isNaN()) { + displayRange = Range.of(minValue, maxValue); + controlRange = Range.of(minValue, maxValue); + } + else { + displayRange = Range.undefined(); + controlRange = Range.undefined(); + } + + //Set alarm Range Range + Double minAlarm = Double.NaN; + Double maxAlarm = Double.NaN; + if (min_alarm != null && !min_alarm.trim().isEmpty()) { + try { + minAlarm = Double.valueOf(min_alarm.trim()); + maxAlarm = Double.POSITIVE_INFINITY; + } catch (Exception e) { + minAlarm = Double.NaN; + } + } + + if (max_alarm != null && !max_alarm.trim().isEmpty()) { + try { + maxAlarm = Double.valueOf(max_alarm.trim()); + if (minAlarm.isNaN()) { + minAlarm = Double.NEGATIVE_INFINITY; + } + } catch (Exception e) { + if (!minAlarm.isNaN()) { + maxAlarm = Double.POSITIVE_INFINITY; + } else { + maxAlarm = Double.NaN; + } + } + } + + if(!minAlarm.isNaN() && !maxAlarm.isNaN()) { + alarmRange = Range.of(minAlarm, maxAlarm); + } + else { + alarmRange = Range.undefined(); + } + + //Set warning Range + Double minWarning = Double.NaN; + Double maxWarning = Double.NaN; + if (min_warning != null && !min_warning.trim().isEmpty()) { + try { + minWarning = Double.valueOf(min_warning.trim()); + maxWarning = Double.POSITIVE_INFINITY; + } catch (Exception e) { + minWarning = Double.NaN; + } + } + + if (max_warning != null && !max_warning.trim().isEmpty()) { + try { + maxWarning = Double.valueOf(max_warning.trim()); + if (minWarning.isNaN()) { + minWarning = Double.NEGATIVE_INFINITY; + } + } catch (Exception e) { + if (!minAlarm.isNaN()) { + maxWarning = Double.POSITIVE_INFINITY; + } else { + maxWarning = Double.NaN; + } + } + } + + if(!minWarning.isNaN() && !maxWarning.isNaN()) { + warningRange = Range.of(minWarning, maxWarning); + } + else { + warningRange = Range.undefined(); + } + + if(format != null && format.trim().startsWith("%") && format.contains(".")) { + //remplace last f or e or d + String formatString = format.trim().replaceFirst("%", ""); + boolean scientifique = formatString.toLowerCase().endsWith("e"); + formatString = formatString.replace("e", ""); + formatString = formatString.replace("f", ""); + formatString = formatString.replace("d", ""); + int nbDigit = 0; + + try { + int indexOf = formatString.indexOf("."); + String nbDigitString = formatString.substring(indexOf+1); + nbDigit = Double.valueOf(nbDigitString).intValue(); + } + catch (Exception e) { + nbDigit = 0; + } + if(!scientifique) { + numberFormat = NumberFormats.precisionFormat(nbDigit); + } + else { + StringBuilder sb = new StringBuilder("0."); + for (int i = 0; i < nbDigit; i++) { + sb.append("0"); + } + sb.append("E0"); + numberFormat = new DecimalFormat(sb.toString()); + } + } + + display = Display.of(displayRange, alarmRange, warningRange, controlRange, unit, numberFormat, description); + + } + + if(display == null) { + display = Display.none(); + } + + return display; + } + + protected static EnumDisplay buildEnumDisplayFromAttributeInfo(AttributeInfoEx infoEx, Display display) { + EnumDisplay enumDisplay = null; + if (infoEx != null && display != null) { + String[] enum_label = infoEx.enum_label; + if (enum_label != null && enum_label.length > 0) { + enumDisplay = AdvancedEnumDisplay.of(display, enum_label); + } + } + return enumDisplay; + } + + private static boolean isUnsigned(String fieldName, String tangoType) { + return fieldName != null && tangoType != null && fieldName.contains("U" + tangoType); + } + + private static boolean isLong64(String fieldName) { + return fieldName != null && fieldName.contains(TangoConstHelper.LONG_NAME + "64"); + } + + protected static EnumDisplay getAttributeQualityEnumDisplay(AttributeInfo info) { + EnumDisplay enumDisplay = null; + if(info != null) { + NumberFormat numberFormat = Display.defaultNumberFormat(); + Range noRange = Range.undefined(); + // Build Attribute Quality enumeration + Range limit = Range.of(0, TangoConstHelper.ATTR_QUALITY_LABEL.length); + Display display = Display.of(limit, noRange, noRange, limit, "", numberFormat, "Quality of attribute " + info.name); + enumDisplay = AdvancedEnumDisplay.of(display, TangoConstHelper.ATTR_QUALITY_LABEL); + } + return enumDisplay; + } + + protected static EnumDisplay getDevStateEnumDisplay(String device) { + EnumDisplay enumDisplay = null; + if(device != null) { + NumberFormat numberFormat = Display.defaultNumberFormat(); + Range noRange = Range.undefined(); + // Build Attribute Quality enumeration + Range limit = Range.of(0, TangoConstHelper.STATE_LABEL.length); + Display display = Display.of(limit, noRange, noRange, limit, "", numberFormat, "State of device " + device ); + enumDisplay = AdvancedEnumDisplay.of(display, TangoConstHelper.STATE_LABEL); + } + return enumDisplay; + } + +} diff --git a/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoPreferences.java b/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoPreferences.java new file mode 100644 index 0000000000..467916d91f --- /dev/null +++ b/core/pv-tango/src/main/java/org/phoebus/pv/tango/TangoPreferences.java @@ -0,0 +1,73 @@ +package org.phoebus.pv.tango; + +import static org.phoebus.pv.PV.logger; + +import java.util.logging.Level; + +import org.phoebus.framework.preferences.PreferencesReader; + +import fr.esrf.TangoApi.Database; + +public class TangoPreferences { + + private static final String TANGO_HOST = "tango_host"; + private static final String DEFAULT_TANGO_HOST = "localhost:10000"; + private static Boolean tangoDBEnable = null; + + private static TangoPreferences instance = null; + + private TangoPreferences() { + try { + installPreferences(); + } catch (Exception e) { + e.printStackTrace(); + logger.log(Level.SEVERE, "Preferences Error", e); + } + } + + public static TangoPreferences getInstance() { + if (instance == null) { + instance = new TangoPreferences(); + } + return instance; + } + + private void installPreferences() throws Exception { + String currentTangoHost = System.getProperty(TANGO_HOST.toUpperCase()); + + final PreferencesReader prefs = new PreferencesReader(TangoPVFactory.class, "/pv_tango_preferences.properties"); + if (prefs != null) { + String tangohost = prefs.get(TANGO_HOST); + if (tangohost != null && !tangohost.trim().isEmpty()) { + if(currentTangoHost == null || currentTangoHost.equalsIgnoreCase(tangohost)) { + logger.log(Level.INFO, "set TANGO_HOST=" + tangohost); + System.setProperty(TANGO_HOST.toUpperCase(), tangohost) ; + currentTangoHost = tangohost; + } + } + } + + if(currentTangoHost == null) { + logger.log(Level.WARNING, "env " + TANGO_HOST.toUpperCase() + " not set => force to " + DEFAULT_TANGO_HOST); + System.setProperty(TANGO_HOST.toUpperCase(), DEFAULT_TANGO_HOST) ; + currentTangoHost = DEFAULT_TANGO_HOST; + } + } + + public boolean isTangoDbEnable() { + if(tangoDBEnable == null) { + //Test database + try { + Database db = new Database(); + db.ping(); + tangoDBEnable = true; + } + catch (Exception e) { + tangoDBEnable = false; + String currentTangoHost = System.getProperty(TANGO_HOST.toUpperCase()); + logger.log(Level.SEVERE, "Tango database not found on " + TANGO_HOST.toUpperCase() + "=" + currentTangoHost, e); + } + } + return tangoDBEnable; + } +} diff --git a/core/pv-tango/src/main/resources/META-INF/pv_tango_preferences.properties b/core/pv-tango/src/main/resources/META-INF/pv_tango_preferences.properties new file mode 100644 index 0000000000..9680c4cb74 --- /dev/null +++ b/core/pv-tango/src/main/resources/META-INF/pv_tango_preferences.properties @@ -0,0 +1,6 @@ +# ------------------------------ +# Package org.phoebus.pv.tango +# ------------------------------ + +# Override TANGO_HOST system properties eg localhost:10000 +tango_host=$(TANGO_HOST) \ No newline at end of file diff --git a/core/pv-tango/src/main/resources/META-INF/services/org.phoebus.pv.PVFactory b/core/pv-tango/src/main/resources/META-INF/services/org.phoebus.pv.PVFactory index a65725b0a7..7efa20eb94 100644 --- a/core/pv-tango/src/main/resources/META-INF/services/org.phoebus.pv.PVFactory +++ b/core/pv-tango/src/main/resources/META-INF/services/org.phoebus.pv.PVFactory @@ -1,2 +1,3 @@ +org.phoebus.pv.tango.TangoPVFactory org.phoebus.pv.tga.TangoAttr_PVFactory org.phoebus.pv.tgc.TangoCmd_PVFactory diff --git a/core/pv-tango/src/test/resources/settings/color.def b/core/pv-tango/src/test/resources/settings/color.def new file mode 100644 index 0000000000..a368f73659 --- /dev/null +++ b/core/pv-tango/src/test/resources/settings/color.def @@ -0,0 +1,75 @@ +# Named colors +# +# Format: +# NameOfColor = red, green, blue [, alpha ] | PreviouslyDefinedNameOfColor +# with values in 0..255 range. +# +# Whenever possible, use named colors in displays +# instead of arbitrary red/green/blue values. + +# ------- Predefined colors ---------------- +# May be overridden in here +# TANGO DevState Color +ON/OPEN/EXTRACT = 0,255,0,255 +OFF/CLOSE/INSERT = 255,255,255,255 +MOVING/RUNNING = 128,160,255,255 +STANDBY = 255,255,0,255 +FAULT = 255,0,0 +INIT = 204,204,122,255 +ALARM = 255,140,0,255 +DISABLE = 255,0,255,255 +UNKNOWN = 155,155,155,255 + +# TANGO Attribute Quality Color +INVALID_ATTR/UNKNOWN_ATTR = 128,128,128,255 +ALARM_ATTR/WARNING_ATTR = 255,200,0,255 +CHANGING_ATTR= 128,160,255,255 +VALID_ATTR = 0,255,0,255 + +# Default color for text +Text=0,0,0 + +# Default color for 'active' text that's being edited +ActiveText=255, 255, 255 + +# Display background +Background = 200, 200, 200 + +# .. for widgets that read/write a value +Read_Background = 240, 240, 240 +Write_Background = 255, 255, 255 + +# .. for buttons +Button_Background = 210, 210, 210 + +# ------- Examples for additional colors ---------------- +# Also show ideas for site-specific guidelines that +# are required to make sense of the color names. + +# Styling +Header_Background=77,77,77 +Header_ForeGround=0,0,0 + +# Use alarm colors only when you mean to indicate an alarm. +# Avoid using 'Red' which might suggest an alarm +# just because you like the look of red. +# STOP looks similar to red=MAJOR alarm, and is allowed +# for 'STOP' type of buttons +# STOP = MAJOR + +# Attention looks similar to a MINOR alarm. +# It is meant to draw attention +# Attention = 255,160,0 + +# The colors for On/Off, Open/Close etc,. +# "On" does not necessarily mean 'device is turned on', but +# stands for 'indicator is on, active, illuminated'. +# For a 'motor is at target' type indicator, the motor would +# actually be 'off' while the indicator uses the 'On' color. +# +# If one of the states represents an alarm, the corresponding alarm color may be used. +# For example, a limit switch indicator could use colors "Off" and "MAJOR": +# Off when idle, MAJOR when the limit switch was hit and this is an abnormal situation +# that requires attention. +# On = OK +# Off = 60,100,60 diff --git a/core/pv-tango/src/test/resources/settings/settings.ini b/core/pv-tango/src/test/resources/settings/settings.ini new file mode 100644 index 0000000000..64d5e33a03 --- /dev/null +++ b/core/pv-tango/src/test/resources/settings/settings.ini @@ -0,0 +1,22 @@ + +# Display configuration +org.csstudio.display.builder.model/color_files=color.def +org.csstudio.display.builder.model/font_files=font.def + +#Console Utility +#On windows +org.phoebus.applications.console/shell=cmd.exe + +#On linux +#org.phoebus.applications.console/shell=/usr/bin/python -i +#org.phoebus.applications.console/shell=xterm + + +# Default PV Type in TANGO +org.phoebus.pv/default=tango +# Default tango_host get from en variable TANGO_HOST +#org.phoebus.pv.tango/tango_host=$(TANGO_HOST) +#org.phoebus.pv.tango/tango_host=tangodb:20000 + +#org.csstudio.display.builder.model/color_files=color.def +org.csstudio.display.builder.model/color_files=color.def diff --git a/core/pv-tango/src/test/resources/tangotest/TangoTest.bob b/core/pv-tango/src/test/resources/tangotest/TangoTest.bob new file mode 100644 index 0000000000..56cf9375d3 --- /dev/null +++ b/core/pv-tango/src/test/resources/tangotest/TangoTest.bob @@ -0,0 +1,475 @@ + + + Display + 800 + + Label + TITLE + TANGO TEST + 0 + 0 + 780 + 31 + + + + + + + + + true + 1 + + + Text Update + tango://sys/tg_test/1/ampli + 170 + 71 + 120 + + + + + + Text Update_1 + tango://sys/tg_test/1/float_scalar + 170 + 111 + 120 + + + + + + Text Update_2 + tango://sys/tg_test/1/ampli.LABEL + 46 + 73 + true + + + Text Update_3 + tango://sys/tg_test/1/float_scalar.LABEL + 46 + 113 + true + + + Text Update_4 + tango://sys/tg_test/1/State + 170 + 151 + 120 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(pv_name) + + + + + Action Button + + + Écrire PV + $(pv_name) + 0 + + + tango://sys/tg_test/1/SwitchStates + SwichState + 279 + 710 + $(actions) + + + Text Update_5 + tango://sys/tg_test/1/string_scalar + 170 + 191 + 120 + + + + + + Text Update_6 + tango://sys/tg_test/1/State.LABEL + 40 + 151 + true + + + Text Update_7 + tango://sys/tg_test/1/string_scalar.LABEL + 40 + 191 + true + + + Text Update_9 + tango://sys/tg_test/1/boolean_scalar.LABEL + 40 + 231 + true + + + Boolean Button + tango://sys/tg_test/1/boolean_scalar.WRITE + 310 + 226 + + + Text Update_10 + tango://sys/tg_test/1/DevString + 396 + 750 + 210 + 28 + + + Combo Box + tango://sys/tg_test/1/DevString + 279 + 750 + + item 0 + Item 1 + Item 2 + + + + Text Entry + tango://sys/tg_test/1/string_scalar.WRITE + 310 + 191 + 120 + + + Spinner + tango://sys/tg_test/1/float_scalar.WRITE + 310 + 111 + + + Spinner_1 + tango://sys/tg_test/1/ampli.WRITE + 310 + 71 + + + Graphique X/Y + 370 + 580 + 1 + 1 + + + Graphique X/Y_1 + 40 + 340 + 730 + 290 + + + true + false + 0.0 + 100.0 + false + + + + + + + + + true + + + + + true + false + 0.0 + 100.0 + false + + + + + + + + + false + true + + + + + + + + + $(traces[0].y_pv) + + tango://sys/tg_test/1/wave + + 0 + 1 + + + + + 1 + 0 + 0 + 10 + true + + + $(traces[1].y_pv) + + sys/tg_test/1/wave + + 0 + 1 + + + + + 1 + 0 + 0 + 10 + true + + + + + Label_1 + sys/tg_test/1/DevString + 100 + 756 + 173 + + + Label_2 + sys/tg_test/1/SwichState + 102 + 717 + 173 + + + Label_3 + Tango Commands + 50 + 640 + 210 + 40 + + + + + + + Label_4 + Tango Attributes + 50 + 31 + 210 + 40 + + + + + + + Text Update_11 + tango://sys/tg_test/1/enum_scalar.LABEL + 40 + 276 + true + + + Combo Box_1 + tango://sys/tg_test/1/enum_scalar.WRITE + 310 + 271 + + + Text Update_12 + tango://sys/tg_test/1/enum_scalar + 166 + 278 + 120 + + + + + + Text Update_13 + tango://sys/tg_test/1/boolean_scalar + 166 + 231 + 120 + + + + + + Image + 990 + 470 + 1 + 1 + + + Image_1 + tango://sys/tg_test/1/double_image_ro + 430 + 71 + 350 + 249 + + true + sys/tg_test/1/double_image_ro + 0.0 + 251.0 + + + + + + + + + + + true + + 0.0 + 251.0 + + + + + + + + + + true + 251 + 251 + true + + + Text Update_8 + sys/tg_test/1/State + 310 + 150 + 110 + + + + + diff --git a/core/pv-tango/src/test/resources/tangotest/TangoTest.pvs b/core/pv-tango/src/test/resources/tangotest/TangoTest.pvs new file mode 100644 index 0000000000..16d4a6ce15 --- /dev/null +++ b/core/pv-tango/src/test/resources/tangotest/TangoTest.pvs @@ -0,0 +1,146 @@ + + + 60.0 + + + true + tango://sys/tg_test/1/DevString + 0.1 + + true + + + true + tango://sys/tg_test/1/State + 0.1 + + true + + + true + tango://sys/tg_test/1/State.LABEL + 0.1 + + true + + + true + tango://sys/tg_test/1/SwitchStates + 0.1 + + true + + + true + tango://sys/tg_test/1/ampli + 0.1 + + true + + + true + tango://sys/tg_test/1/ampli.LABEL + 0.1 + + true + + + true + tango://sys/tg_test/1/ampli.WRITE + 0.1 + + true + + + true + tango://sys/tg_test/1/boolean_scalar + 0.1 + + true + + + true + tango://sys/tg_test/1/boolean_scalar.LABEL + 0.1 + + true + + + true + tango://sys/tg_test/1/float_scalar + 0.1 + + true + + + true + tango://sys/tg_test/1/float_scalar.LABEL + 0.1 + + true + + + true + tango://sys/tg_test/1/float_scalar.WRITE + 0.1 + + true + + + true + tango://sys/tg_test/1/string_scalar + 0.1 + + true + + + true + tango://sys/tg_test/1/string_scalar.LABEL + 0.1 + + true + + + true + tango://sys/tg_test/1/string_scalar.WRITE + 0.1 + + true + + + true + tango://sys/tg_test/1/wave + 0.1 + + true + + + true + sys/tg_test/1/wave + 0.1 + + true + + + true + tango://sys/tg_test/1/double_image_ro + 0.1 + + true + + + true + tango://sys/tg_test/1/float_scalar.STAT + 0.1 + + true + + + true + tango://sys/tg_test/1/no_value + 0.1 + + true + + + diff --git a/core/pv-tango/src/test/resources/tangotest/script/AttributeQualityRule.py b/core/pv-tango/src/test/resources/tangotest/script/AttributeQualityRule.py new file mode 100644 index 0000000000..70ccd0d002 --- /dev/null +++ b/core/pv-tango/src/test/resources/tangotest/script/AttributeQualityRule.py @@ -0,0 +1,30 @@ +## Rule: Attribute Quality Rule + +from org.csstudio.display.builder.runtime.script import PVUtil +from java import lang +from org.csstudio.display.builder.model.properties import WidgetColor + + +try: + pv0 = PVUtil.getDouble(pvs[0]) + +## Script Body + if pv0 == 0: + widget.setPropertyValue('background_color', WidgetColor(0, 255, 0, 255)) + elif pv0 == 1: + widget.setPropertyValue('background_color', WidgetColor(128, 128, 128, 255)) + elif pv0 == 2: + widget.setPropertyValue('background_color', WidgetColor(255, 200, 0, 255)) + elif pv0 == 3: + widget.setPropertyValue('background_color', WidgetColor(0, 255, 0, 255)) + elif pv0 == 4: + widget.setPropertyValue('background_color', WidgetColor(128, 160, 255, 255)) + elif pv0 == 5: + widget.setPropertyValue('background_color', WidgetColor(255, 200, 0, 255)) + else: + widget.setPropertyValue('background_color', WidgetColor(240, 240, 240, 255)) + +except (Exception, lang.Exception) as e: + widget.setPropertyValue('background_color', WidgetColor(240, 240, 240, 255)) + if not isinstance(e, PVUtil.PVHasNoValueException): + raise e diff --git a/core/pv-tango/src/test/resources/tangotest/script/DevStateRule.py b/core/pv-tango/src/test/resources/tangotest/script/DevStateRule.py new file mode 100644 index 0000000000..cb3796136d --- /dev/null +++ b/core/pv-tango/src/test/resources/tangotest/script/DevStateRule.py @@ -0,0 +1,46 @@ +## Rule: DevStateRule + +from org.csstudio.display.builder.runtime.script import PVUtil +from java import lang +from org.csstudio.display.builder.model.properties import WidgetColor + + +try: + pv0 = PVUtil.getDouble(pvs[0]) + +## Script Body + if pv0 == 0: + widget.setPropertyValue('background_color', WidgetColor(0, 255, 0, 255)) + elif pv0 == 1: + widget.setPropertyValue('background_color', WidgetColor(255, 255, 255, 255)) + elif pv0 == 2: + widget.setPropertyValue('background_color', WidgetColor(255, 255, 255, 255)) + elif pv0 == 3: + widget.setPropertyValue('background_color', WidgetColor(0, 255, 0, 255)) + elif pv0 == 4: + widget.setPropertyValue('background_color', WidgetColor(255, 255, 255, 255)) + elif pv0 == 5: + widget.setPropertyValue('background_color', WidgetColor(0, 255, 0, 255)) + elif pv0 == 6: + widget.setPropertyValue('background_color', WidgetColor(128, 160, 255, 255)) + elif pv0 == 7: + widget.setPropertyValue('background_color', WidgetColor(255, 255, 0, 255)) + elif pv0 == 8: + widget.setPropertyValue('background_color', WidgetColor(255, 0, 0, 255)) + elif pv0 == 9: + widget.setPropertyValue('background_color', WidgetColor(204, 204, 122, 255)) + elif pv0 == 10: + widget.setPropertyValue('background_color', WidgetColor(128, 160, 255, 255)) + elif pv0 == 11: + widget.setPropertyValue('background_color', WidgetColor(255, 140, 0, 255)) + elif pv0 == 12: + widget.setPropertyValue('background_color', WidgetColor(255, 0, 255, 255)) + elif pv0 == 13: + widget.setPropertyValue('background_color', WidgetColor(155, 155, 155, 255)) + else: + widget.setPropertyValue('background_color', WidgetColor(240, 240, 240, 255)) + +except (Exception, lang.Exception) as e: + widget.setPropertyValue('background_color', WidgetColor(240, 240, 240, 255)) + if not isinstance(e, PVUtil.PVHasNoValueException): + raise e