In this lesson, we will concentrate on how to implement a component's behavior with rules and actions.

Rule

When creating components one can write rules consisting of an expression and one or more actions. The actions will be executed if the expression is true. A rule consists of three elements:

  • ID: Rule identifier that has to be unique inside a scope
  • Expression: Condition that triggers this rule
  • Actions: References to or definitions of actions that are executed when this rule is triggered

A rule will also always be assigned to a state:

States

  • <onenter>: This is the first state when a step is entered. The user interface is not available in this state, as a result, you cannot use UI-related actions. If you, however, want to initialize parameters to use them for UI mapping then this is the correct state to do so.
  • <onresume>: The second state after <onenter>. In this state, the UI is available and you can modify the loaded layout with the UI update actions.
  • <onpause> and <onleave>: These states occur whenever you leave the current step or the whole component by using the step_transition or finish_workflow actions. Since the transitions are not interruptible, you cannot use rules here that use these actions. In theonpause state, the resources of the step still exist but onleave they do not.
  • <onevent>: This is probably the most important state. Rules defined in this state will be checked every time an event gets processed. All the rules related to interactions with the user are defined in this state.

The rules within a state are evaluated in arbitrary order, so the expression of a rule should not depend on another rule being checked before it.

On the other hand, actions within a rule are executed in the order you write them down.

Order of evaluation/execution:

  • Rules are checked in arbitrary order.
  • Actions within a rule are executed in sequential order.

Expressions

The general structure of an expression is composed of operands (e. g. context variables) and operators (e. g. '=' or '>') like in any programming language.

Supported Operators: +, -, &&, ||, !, /, ==, >, >=, <, <=, %, *, !=

Let's take a look at another simple example from the "Paginated Text" component which shows a text of arbitrary length on multiple pages:

<onevent>
    <rule id="next_page">
        <expression><![CDATA[
                #{event:command} == 'NEXT_PAGE' && #{page} < #{numberofpages}
        ]]></expression>
        <actions>
            <action ref="next"/>
            <action ref="settext"/>
        </actions>
    </rule>
</onevent>

Tips & tricks:

 

-> Whitespaces will be deleted from the expression unless they are marked as strings with single quotes, so you can freely use new lines to make your expression readable.

 

->This expression becomes true when the button name="NEXT_PAGE" is pressed, and the current page is not the last page.

 

-> You may have noticed the <![CDATA[ ... ]]> tag surrounding the expression. The tag is required in this case. It is an XML keyword that informs the parser that what follows is no markup. Without the <![CDATA[ ... ]]> tag, this expression would invalidate our component's XML because it contains the XML-reserved characters & and <.

 

-> Add a <![CDATA[ ... ]]> tag to all your expressions. This way you don't have to think about whether you are using XML-reserved characters.

Events

When your rule is in the <onevent> state of a step, you can react to events and use the event's properties in your expression. The structure of an event is as follows:

   {
          "command": "...",
          "device":
          {
             "modality": "...",
             "name": "...",
             "source": "...",
             "descriptor": "..."
          },
          "payload":
          {
             "...": "...",
             "error": "..."  
          }
    }

You will only need a subset of these fields:

1. command: The command of this event, e.g., “NEXT”. The command may for example, correspond to an ID in the layout or workflow description of your component. Example: #{event:command} == ‘CANCEL’

2. device.modality: The source of the event. This field can be accessed using a short notation.

The expression #{event(SPEECH):command==’NEXT’ is equivalent to the expression #{event:device.modality} == ‘SPEECH’ && #{event:command} == ‘NEXT’. The modality depends on the event emitter. Example, #{event:device.modality} == ‘MENU_SELECTION’

3. payload: The structure/ fields of the payload, are dependent on the action/handler that triggers the event. Example, #{event:payload.amount}

4. payload.error: Contains an error message if there is one. Example, #{event:payload.error}

You can restrict the event sources to make sure a rule only triggers when the event is caused by a specific input modality. For example, you could have a speech command "NEXT" which workers can use to confirm they have finished their work on a particular task or product. They might also be using a device that triggers an event with the command "NEXT". When a hardware button is pressed, it rotates through the available options on the screen. You don't want to activate the rule when the hardware buttons are being used, so you specify the modality: #{event(SPEECH):command} == 'NEXT'.

General Event Sources (Modalities): SPEECH, GESTURE, KEYBOARD, BARCODE, HW_KEYS, CAMERA_PICTURE, MENU_SELECTION, MEDIA_INPUT

Examples

Let's go through some rule constructs that show up relatively often:

Initialization and tear-down

Our first example tackles a typical use case: Automatically performing actions upon entering or leaving a component.

Tips and tricks: Some rules need to be unconditionally executed. For this purpose, you can set the expression to <expression>1</expression>.

<onresume>
    <rule id="init">
        <expression>1</expression>
        <actions>
            <action ref="reset_counter"/>
        </actions>
    </rule>
</onresume>    

Sequential execution of rules

Rules within a state are evaluated in arbitrary order. In some situations, you need rules to be executed sequentially within the same step. For this, you can use a timer to trigger the rules in sequential order. The timer action manually triggers an event with a user-defined command.

As the last action of the first rule, you will add a timer action. The user-defined command can then be added to the expression of the second rule.

<onevent>
    <rule id="first_rule">
        <expression><![CDATA[ #{event:command}=='VALID' ]]></expression>
        <actions>
            <action ref="do_something"/>
            <setvar id="increment_counter">
                <context_of>workflow</context_of>
                <context_update>
                    <param name="counter" type="long">#{counter} + 1</param>
                </context_update>
            </setvar>
            <timer id="check_counter_trigger" command="CHECK_COUNTER" delay="0"/>
        </actions>
    </rule>

    <rule id="second_rule">
        <expression><![CDATA[ #{event:command}=='CHECK_COUNTER' && #{counter} >= #{max_iterations} ]]></expression>
        <actions>
            <finish_workflow id="exit"/>
        </actions>
    </rule>
</onevent>    

📌Assignment

Assignment 1:

In our "Choice" component we have not been using the <![CDATA[ ... ]]> tag to keep the component as simple as possible for the initial learner. As a best practice, we recommend using the tag in all your expressions to eliminate a potential error source.

  • Add the <![CDATA[ ... ]]> tag to our existing rule.

Assignment 2:

Let's make sure the user is certain of their choice. If, for example, as soon as we choose "Apple" apple pies start being produced, we might want the user to re-affirm:

  • Read up on the ui_dialog action
  • Add a dialog to the component that shows the selected value and asks the user to confirm their selection.

 Download Workflow (Pre-Assignment)

Help & Resources

  • For Assignment 2, you will have to create a new rule that reacts to the <command> of the ui_dialog options. Some of the logic of the existing rule will have to be moved to that new rule.

Solution

Download Component (Post-Assignment)

With this, you have finished the fourth lesson. In the fifth lesson, we will look into some limitations of defining workflows with just XML and how you can use JavaScript in workflows to solve these problems.