How to create a dynamic status LED compound widget?

Good morning everyone

My goal is to create a compound widget which acts as a simple status LED indicator. It should have two bindable properties:

  • value (boolean): Defines whether the LED is turned on
  • colorIndex (Number): Defines the color (enumerated range of colors) of the LED when on

I have a somewhat ugly working version:

<?xml version="1.0" encoding="utf-8"?>
<CompoundWidget id="cwIndicatorLed" width="40" height="40" xmlns="http://www.br-automation.com/iat2015/contentDefinition/v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Widgets>
        <Widget xsi:type="widgets.brease.Image" id="imageLed" top="0" left="0" width="40" height="40" zIndex="6" image="LocalMedia/LedGreen.svg" />
        <Widget xsi:type="widgets.brease.NumericInput" id="numColor" top="0" left="0" width="1" height="1" zIndex="0" visible="false" />
  
    </Widgets>
    <Properties>
        <Property xsi:type="LocalProperty" name="value" type="Boolean" defaultValue="false" category="Data" required="true" public="true">
            <Description>LED active</Description>
            <Event name="LedValueChanged">
                <Description>value changed event.</Description>
            </Event>
            <Actions>
                <GetAction name="GetLedValue">
                    <Description>Read value</Description>
                </GetAction>
            </Actions>
        </Property>
        <Property xsi:type="BindableProperty" name="colorIndex" type="Number" category="Data" defaultValue="0" readOnly="false" required="false" > 
            <Description>Color (0:Green, 1: yellow, 2: orange, 3: red, 4: blue, 5: white)</Description>
            <Mappings> 
                <Mapping widget="numColor" property="value"  mode="oneWay" />
            </Mappings>  
        </Property>
        
       
        
    </Properties>

    <Events>

    </Events>

    <Actions>
        
    </Actions>
    <EventBindings>
        <EventBinding id="LedStateChanged"> 
            <Source xsi:type="this.Event" event="LedValueChanged" /> 
            <Operand name="ColorIndex" datatype="ANY_INT"> 
                <ReadTarget xsi:type="widget.Action.Read" widgetRefId="numColor">
                    <Method name="GetValue" />
                </ReadTarget>
            </Operand>
            <EventHandler condition="ColorIndex=0">
                <Action>
                    <ReadTarget xsi:type="this.Action.Read">
                        <Method name="GetLedValue" />
                    </ReadTarget>
                    <Result>
                        <ResultHandler condition="result=false">
                            <Action>
                                <Target xsi:type="widget.Action"  widgetRefId="imageLed">
                                    <Method name="SetImage" image="LocalMedia/LedOff.svg" />
                                </Target>
                            </Action>
                        </ResultHandler>
                        <ResultHandler condition="result=true">
                            <Action>
                                <Target xsi:type="widget.Action"  widgetRefId="imageLed">
                                    <Method name="SetImage" image="LocalMedia/LedGreen.svg" />
                                </Target>
                            </Action>
                        </ResultHandler>
                            
                    </Result>
                </Action>
            </EventHandler>
            <EventHandler condition="ColorIndex=1">
                <Action>
                    <ReadTarget xsi:type="this.Action.Read">
                        <Method name="GetLedValue" />
                    </ReadTarget>
                    <Result>
                        <ResultHandler condition="result=false">
                            <Action>
                                <Target xsi:type="widget.Action"  widgetRefId="imageLed">
                                    <Method name="SetImage" image="LocalMedia/LedOff.svg" />
                                </Target>
                            </Action>
                        </ResultHandler>
                        <ResultHandler condition="result=true">
                            <Action>
                                <Target xsi:type="widget.Action"  widgetRefId="imageLed">
                                    <Method name="SetImage" image="LocalMedia/LedYellow.svg" />
                                </Target>
                            </Action>
                        </ResultHandler>
                            
                    </Result>
                </Action>
            </EventHandler>
            
        </EventBinding> 

    </EventBindings>

</CompoundWidget>

There are several issues with this:

  • It is ugly (i could live with that)
  • It only updates when the “Value” changes. On initial loading of the content, it will display its initial state.
  • It is using the hidden “numColor” widget for storing the color index.

Setting the contents to preCache did come with other problems in our visu.

My questions are:

  1. I would love to create a widget action which handles all the updating. That way i could probably solve the issue of the initial state by calling this action on “OnWidgetsReady” events.
  2. I would love to also declare the “numColor” as a LocalProperty, but i was not able to figure out how to read the value of a local property outside its own “ValueChange” event.

In pseudo code it could be something like:


<?xml version="1.0" encoding="utf-8"?>
<CompoundWidget id="cwIndicatorLed">
	<Widgets>
		<Widget xsi:type="widgets.brease.Image" id="imageLed" top="0" left="0" width="40" height="40" zIndex="6" image="LocalMedia/LedGreen.svg"/>
	</Widgets>
	<Properties>
		<Property xsi:type="LocalProperty" name="value" type="Boolean" defaultValue="false" category="Data" required="true" public="true">
			<Description>LED active</Description>
			<Event name="LedValueChanged" />
			<Actions>
				<GetAction name="GetLedValue" />
			</Actions>
		</Property>
		<Property xsi:type="LocalProperty" name="colorIndex" type="Number" defaultValue="false" category="Data" required="true" public="true">
			<Description>color index</Description>
			<Event name="ColorIndexChanged" />
			<Actions>
				<GetAction name="GetColorIndex" />
			</Actions>
		</Property>
	</Properties>
	<Events>

    </Events>
	<Actions>
		<Action name="UpdateLed">
		Step 1: CALL "GetLedValue"
		Step 2: IF GetLedValue=False THEN <Method name="SetImage" image="LocalMedia/LedOff.svg"/>
		Step 3: ELSE CALL "GetColorIndex"
			Substep 1: IF GetColorIndex=0 THEN <Method name="SetImage" image="LocalMedia/LedGreen.svg"/>
			Substep 2: ELSE IF GetColorIndex=1 THEN <Method name="SetImage" image="LocalMedia/LedYellow.svg"/>
			Substep 3: ELSE IF....	
		</Action>
	</Actions>
	
	<EventBindings>
		<EventBinding id="LedStateChanged">
			<Source xsi:type="this.Event" event="LedValueChanged"/> 
			CALL ACTION UpdateLed    
        </EventBinding>
		<EventBinding id="LedColorChanged">
			<Source xsi:type="this.Event" event="ColorIndexChanged"/> 
			CALL ACTION UpdateLed
        </EventBinding>
		<EventBinding id="WidgetLoaded">
			<Source xsi:type="this.Event" event="OnWidgetsReady"/>  
			CALL ACTION UpdateLed
     </EventBinding>
	</EventBindings>
</CompoundWidget>

Is there an easy way to solve this?

Best Regards and many thanks in advance

Matthias

Hi @Matthias_Kaufmann

One of my idea is to use ImageList and only one parameter on the CompounWidget (the imageIndex). You set the image when the led is off at index 0 then all your color after.

<?xml version="1.0" encoding="utf-8"?>
<CompoundWidget id="compoundwidget_0" width="40" height="40" xmlns="http://www.br-automation.com/iat2015/contentDefinition/v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<Widgets>
		<Widget xsi:type="widgets.brease.ImageList" id="Led" top="0" left="0" width="40" height="40" zIndex="0" imageList="['Media/LedGrey.svg', 'Media/LedOrange.svg', 'Media/LedRed.svg', 'Media/LedGreen.svg']" selectedIndex="0" useSVGStyling="false" />
  </Widgets>
	<Properties>
		<Property xsi:type="BindableProperty" name="imageIndex" type="Number" category="Data" defaultValue="0" readOnly="false" required="false" > 
			<Description>Color (0:Grey, 1: Orange, 2: Red, 3: Green)</Description>
			<Mappings> 
				<Mapping widget="Led" property="selectedIndex"  mode="oneWay" />
			</Mappings>  
		</Property>
	</Properties>

	<Events>
	</Events>

	<Actions>
	</Actions>

	<EventBindings>
	</EventBindings>

</CompoundWidget>

In a program (here ST) you use 2 variables (LedOn and ColorIndex) to calculate imageIndex:

VAR
	ColorIndex : USINT;
	LedOn : BOOL;
	ImageIndex : USINT;
END_VAR


PROGRAM _CYCLIC

	ImageIndex := LedOn * (ColorIndex + 1);
	 
END_PROGRAM

Declare “ImageIndex” to the OpcUa and bind it to your compound widget.

Note:
One thing you can check to not use PLC program it’s the EventScript, I didn’t try it yet butI think you can stay with your 2 properties and just put the imageIndex calcul in the value change event of both properties. I will check if I had time!

Hope this could help!

Regards,
Florent

3 Likes