A Numeric Widget with Rising / Falling Trend
Level going up: Level going down:
This widget is built using a standard VTScada Numeric Value widget and two arrow images. Code is added to the opacity property of the arrows so that only the appropriate one is visible at any time.
While you might be able to craft an expression for the opacity property in the Idea Studio, it is far easier to add a few lines to the widget's source file. One significant advantage is that in the source code file, you will be able to create a local variable to save older values of the linked tag for comparison to the current value without needing to create a Calculation tag.
Start with a basic widget. This example uses a Numeric Value widget, linking it to the first available Analog Status tag in an application. An up arrow and a down arrow were drawn on top of each other beside the widget. The Numeric Value widget's properties were modified to right-align the text. Following this, the three objects were grouped into a new Tag Widget, and the Tag Icon Marker removed.
We modified the list of Tag Types, adding both Analog Input and Calculation tags to those for which this new widget can be drawn.
Opening the source code file, we see the following. The visibility (opacity) parameter of both arrows is shown in bold.
{================================ Tag Widget =================================} ( DisableTrend Status; DisableNavigation Status; DisableTooltip Status; ) [ Shared UserMethods (LIBRARIES); Shared AnalogStatus (POINTS); Title = "k7uU1LO"; Shared AnalogInput(POINTS); Shared Calculation(POINTS); ] TagWidgetMain [ If Watch(1); [ DisableTrend = PickValid(DisableTrend, 0); DisableNavigation = PickValid(DisableNavigation, 0); DisableTooltip = PickValid(DisableTooltip, 0); ] GUITransform(0, 26, 61, 0, 1, 1, 1, 1, 1 { Scaling }, 0, 0 { Movement }, 1, 0 { Visibility, Reserved }, 0, 0, 0 { Selectability }, PickValid(\Root\Number, Scope(VTSDB, ":AnalogStatus", TRUE)\Number)("<FF000000>", 1, 1, 2, Invalid, 3, "<00000000>", 3, 1, 0, 0, 0, 0)); GUITransform(67, 25, 82, 2, 1, 1, 1, 1, 1 { Scaling }, 0, 0 { Movement }, 1, 0 { Visibility, Reserved }, 0, 0, 0 { Selectability }, Scope(Code, "Library", TRUE)\Bitmap("Bitmaps\Arrows\Larrow8U.bmp")); GUITransform(67, 25, 82, 2, 1, 1, 1, 1, 1 { Scaling }, 0, 0 { Movement }, 1, 0 { Visibility, Reserved }, 0, 0, 0 { Selectability }, Scope(Code, "Library", TRUE)\Bitmap("Bitmaps\Arrows\Larrow8D.bmp")); \Common(GetModuleRefBox(Self, 0), GetModuleRefBox(Self, 1), GetModuleRefBox(Self, 2), GetModuleRefBox(Self, 3), DisableTrend, DisableNavigation, DisableTooltip); ]
The current value of the linked tag is available in \Root.Value. To know whether this is increasing or decreasing, you'll need to compare that to the previous value, therefore you need a variable in which to store that value, code to compare the previous to the new, and code to store the current value as previous, following the comparison.
Let's start with very simple code and then look for ways to improve it.
Add two variables:
LastValue { store the previous value of the linked tag. }; TrendDirection { positive when increasing, negative decreasing, 0 otherwise };
Now for the comparison. LastValue starts as Invalid (and \Root.value) might as well) so the TrendDirection begins as zero.
If Watch(1, \Root\Value); { initially and on change... } [ TrendDirection = PickValid(\Root.Value - LastValue, 0); LastValue = \Root.Value; ]
In the up arrow, replace the visibility parameter with a test for a positive TrendDirection:
GUITransform(67, 25, 82, 2, 1, 1, 1, 1, 1 { Scaling }, 0, 0 { Movement }, TrendDirection > 0, 0 { Visibility, Reserved }, 0, 0, 0 { Selectability }, Scope(Code, "Library", TRUE)\Bitmap("Bitmaps\Arrows\Larrow8U.bmp"));
Similarly, for the down arrow: TrendDirection < 0
Save your code, Import File Changes and try the new widget.
Suggestions for improvement:
- This example has no allowance for system noise. The arrow will flicker up and down if the value fluctuates by a small value. You might add a deadband to the comparison to filter out noise. But, if doing so, be careful to allow for slowly increasing or decreasing trends as opposed to system noise. Also, if using a deadband, it should be relevant to the scaled range of the linked tag rather than set as a constant. If the widget is for only analog tags, you can use \Root.ScaledMax and \Root.ScaledMin.
- Small reversals versus overall trends. If the overall trend is up, but the operator glances at the display just after a momentary step down, they might be misled. Rather than compare to the previous value, you might want to compare to a first-order filter of the trend. Refer to the example, A First Order Filter for a Script Tag for relevant code that you might use here.
- A horizontal arrow might be better than no arrow when the trend is level.
- You could indicate the magnitude of the trend by rotating a single arrow instead of revealing only an up or down arrow. If doing so, you will need to restrict the angle of rotation between zero and 180, where 90 indicates no change, zero is the largest possible amount and 180 is the largest possible negative rate.
Be careful when comparing values that might be Invalid, such as those coming from I/O tags. Use StrictlyEqual or StrictlyNotEqual rather than simple == or != comparisons if you add code to compare any value to \Root.Value.