- 1 Introduction
- 2 Types of Pins
- 3 Input Pins
- 3.1 Input Basket: Queue of Incoming Values
- 3.2 Input Pin Trigger Attributes
- 3.3 Non-Triggering Pins which are Unconnected or which Received no Value
- 3.4 Summary of Trigger + Consuming Combinations
- 3.5 Triggering Behavior when no Triggering Input is Present
- 3.6 Triggering Behavior when Multiple Inputs are Present
- 3.7 Special Input Pins
- 3.8 Variable Number of Pins
- 4 Output Pins
- 5 Special Pins of Script Action Blocks
- 6 "Do not Log Pin-Value in Activitylog"-Attribute
Pins are the data interface between steps. They are used to read and pass data such as parameters, results and measurement data, documents or simple control information and represent the inputs and outputs of an activity. Output values of one activity are transported via a connection from the output pin to one or multiple input pins of other processing steps. Input values can come from other Pins (via connections), Literal Freeze Values or from Environment Variables. When all required inputs of a processing step are available, an activity is created, which processes the input values, computes one or multiple result values, and transfers them via the output pins to further processing steps.
Types of Pins
The different types of pins are shown in this image in clockwise order:
- Data Input (at the left, named "in1")
- Enable Input (also called "Trigger Input" at top left)
- Cancel Input
- Timelimit Input
- Repeatcount Input
- Performer Input
- Data Output (at the right, named "out1")
- Enable Output (also called "Trigger Output" at bottom left)
- Exception Output
- ExecutionTime Output (since expecco 1.8)
Input pins are located at the left (data inputs) and/or at the top (called control or special inputs) of a step. They receive data and process this depending on their type.
Input Basket: Queue of Incoming Values
Every input pin has an associated incoming data basket, which is capable of buffering multiple input values. Values as coming from other steps via a connection remain there until the receiving step gets triggered (all of its required inputs are present) and consumes the next value from the input basket(s). This means, that an output pin can write multiple values to a connection or multiple output pins from different steps may connect and send data to the same input pin.
By default, an input basket's buffer is unlimited in capacity (actually, the capacity is limited by the amount of available memory in the system). However, a limit may be configured in the diagram editor via the pin's popup menu. If a limit is given, and the input basket is full, any further incoming data is blocked and the sending step(s) are suspended. This solves the traditional producer-consumer problem, where a generator is sending data to a consumer, which cannot process the values fast enough (i.e. the producer outruns the consumer).
Input Pin Trigger Attributes
Each regular (data) input pin can individually be configured to be one of the following built-in stereotypes, which affect its triggering and value consumption behavior:
- triggering & consuming ("Regular Pin")
- non-triggering & non-consuming ("Parameter Pin")
- non-triggering & pass-through ("Mailbox Pin")
- triggering & pass-through mailbox ("Telegram Pin")
Actually, there are 8 possible combinations of the triggering/non-triggering, consuming/non-consuming and mailbox/non-mailbox attributes. However, in practice, only the four combinations above are useful. In fact, almost all pins are either regular (trigger & consuming) or parameter pins (non-trigger & non-consuming). Therefore, those have been given special names and they have special menu items in the network editor's pop-up menu for pins. Other combinations are to be set via a sub-menu, if they are ever needed. Some combinations are blocked as they do not make sense at all.
Trigger vs. Non-Trigger
The trigger / non-trigger attribute affects if a new activity is to be created for the step when a value arrives. Each triggering pin is considered by the input-fetcher algorithm (as described below). A step without any triggering data input pin must be activated by either an autostart attribute, or by an explicit trigger via the trigger-input pin.
Consuming vs. Non-Consuming
The consuming / non-consuming attribute defines if incoming values are read-out from the pins input basket (consuming) or stay there. Consuming baskets show the normal producer-consumer behavior. Non-consuming pins are used for parameter values: these values are stored in the pin and kept remembered for the next activity, until changed. The diagram editor will always silently change the pin to be non-consuming and non-triggering, when a freeze value is defined.
Due to the big difference in the behavior (possibly affecting the execution of the diagram), this attribute is also visualized in the diagram: consuming pins are drawn as an unfilled pin-rectangle (meaning that a value is taken), whereas non-consuming pins are drawn as a filled rectangle (meaning that the value remains there).
Notice for consuming pins:
If a pin's input value is to be reused in a loop, it must be configured as a non-consuming pin. Otherwise the input data value is already consumed when the second iteration is due, and the step would not be triggered again. Because this is so common an error (to "eat" the input value in a loop), there is an extra check-entry for that in the tree-pane's error-search tab. Be especially careful when using a compound action's input pin value inside the network as input to a step which is part of a loop.
Telegram and Mailbox Pins
The pass-through (mailbox/telegram) pin stereotype specifies that input values are not latched and remembered in the activity, but instead are passed through to either the underlying elementary action or the internal network of a compound activity. This means that these pins forward any received value immediately to an already executing activity (all of them, if there are multiple). The mailbox attribute can only be specified in the schema (i.e. not for individual steps), because the action's implementation needs to be prepared for this kind of of behavior. It does not make sense to change the attribute for a pin which was never intended to be used otherwise.
In a compound network, an incoming telegram value will immediately be send to the inner connection and possibly trigger execution of the connected step there.
In an elementary block, the pin gets the value and the program code can check for the presence of a value with
"pin.hasValue()" e.g. in a loop.
Non-Triggering Pins which are Unconnected or which Received no Value
Elementary code should check for a value being present at the pin, using the
"pin.hasValue()" query function before accessing the pin value. Otherwise a "No-Pin Value" exception will be thrown. In compound actions, any connected diagram step (the inner connection) will never get a value from the pin and therefore possibly never be triggered.
Summary of Trigger + Consuming Combinations
As mentioned above, some combinations of trigger vs. consuming are useless or seldom used. The following table gives a summary of all.
- Triggering + Consuming + Non-Mailbox
This is the most common setup. The input value is consumed from the (underlying) input queue and used by the activity when all required input values are available. If another value arrives at the pin later, another activity is started for that step.
- Non-Triggering + Non-Consuming + Non-Mailbox
The second most common setup. Typically, the input value is a freeze-value, and that same value is used for every incarnation of the step. However, if the input value is coming from another step's output, the "last value" is remembered for the next activity ("latched", so to speak)
- Triggering + Non-Consuming + Non-Mailbox
Questionable. In this setup, the step starts its activity when all other available pin values are present AND that particular pin has also received a value. Such a setup can be used to ensure that a particular value has been generated, and this value is to be reused later. One example is, when a collection is enumerated, and the enumeration is to be triggered by incoming key values, but the collection itself may or may not be present when the first key is computed. I.e. the collection may arrive any time later, but should be reused for each incoming key. There are other, more visible ways to model such a behavior, and the diagram should probably be changed to use explicit trigger-out to trigger-in connections for this.
- Non-Triggering + Consuming + Non-Mailbox
We cannot think of any use for this combination, so it should not be used.
- Triggering + Consuming + Mailbox
Do not use
- Non-Triggering + Non-Consuming + Mailbox
Do not use
- Triggering + Non-Consuming + Mailbox
Do not use
- Non-Triggering + Consuming + Mailbox
This is the standard mailbox pin setup. Any value which arrives at the pin while the activity is already running will be passed directly to the running activities internal step an will possibly trigger that step there. It is typically used to tell a running activity about a special condition (eg. to cancel or lead it into another state).
Triggering Behavior when no Triggering Input is Present
If a step has either no data input at all, they are disconnected, or all of them are frozen and are therefore non-triggering pins, the step has no trigger condition due to data flow. The step's action must then be activated either by manually adding an "Enable Input Pin", or by giving the step the "Autostart" attribute.
Triggering Behavior when Multiple Inputs are Present
The overall trigger behavior is specified in a block's schema definition - not at the step in a diagram. The reason is that the implementor of an action block (especially if it is an elementary block) would know better what the intended and appropriate behavior will be, and if the action can or should deal with unconnected pins or pins which have not received a datum.
When multiple triggering pins are present in a step, the overall trigger behavior can be specified to be one of:
AND-connected Triggering Behavior
All connected triggering pins must have a value for the trigger to happen. Disconnected, parameter- and mailbox-pins are ignored. This used to be the default behavior in pre 2.8 expecco, when new elementary blocks were created, but was changed to "AND" thereafter. Experience with customers showed that this default is more intuitive.
AND Triggering Behavior
All triggering pins must be connected and have a value for the trigger to happen. Parameter- and mailbox-pins are excluded from this test. Starting with release 2.8, this is the default, when new compound actions are created.
OR Triggering Behavior
Any triggering pin with a value will trigger the activity. This is useful if a step receives values from multiple sources (i.e. to implement a join from alternative branches).
Unconnected pins and pins with no value are ignored and provide no value when referred to inside a network or elementary action code.
In a traditional Petri Net, synchronization of values is done at explicit transition points. To make the diagram easier to read and understand, expecco has merged the transition functionality into the action steps. Thus, every step with multiple input pins has an implicit transition behavior already built-in. In situations, where explicit transition points are preferred or required (for semantic or didactic reasons), the standard library also provides a number of synchronization blocks, which can be used to model the traditional join, merge and fork transition primitives as extra items in the network.
Special Input Pins
The following special input pins are optional and not automatically present after initial creation of a step in the diagram editor. You have to explicitly create them via the step's pop-up menu or via one of the toolbar shortcut action buttons of the diagram editor. Their trigger behavior is implicit and depends on the type of pin.
Enable Input Pin
The "enable input pin" (also called "trigger-in" pin) is used to explicitly trigger the execution of a step.
If connected, it is used like an additional triggering, required input.
Trigger inputs are needed if either all of a step's inputs are frozen to static values, or if the execution has to be synchronized to be definitely after another step's execution, independent of when data input arrived. Trigger inputs are also useful to create loops in the diagram.
Trigger input pins are consuming (and of course: triggering).
Cancel Input Pin
The "cancel input pin" can be used to cancel (abort) an ongoing activity. A long-running activity can be cancelled, by passing any data value to a step's cancel pin while it is executing.
The data-flow-behavior of a cancel input is different from other input pins' behavior: cancel input pins behave like a mailbox-input pin; this means that input values are not fetched and remembered at triggering time, as is the case with normal input pins. Instead, an incoming value is immediately handled at any time during the execution of the step and forces the activity to stop.
If a cancel pin receives a value before the step's activity has started or after it has finished, nothing happens. Especially, a cancel value arriving before the activity has started, is ignored. If uncertain, make sure that no race conditions arise, by inserting appropriate delay steps, or by enforcing a definite execution order via "trigger-out -> trigger-in" chains.
Timelimit Input Pin
The "time-limit" input pin is used to automatically cancel an activity after a given maximum execution time. The time-limit value can either come from another step's output, frozen to a constant value, or read from an environment variable. In most real-world applications, a constant freeze value or an environment variables are used.
The time-limit pin is a non-triggering, non-consuming parameter pin.
Iterate Input Pin
The "iterate input pin" (also called: "repeat count") can be used to execute a step's action multiple times. The iteration value can originate from another step's output, from a constant freeze-value, or from an environment variable. This is a non-triggering, non-consuming parameter pin. On the outside, the iteration is invisible (except for any outputs which might be written to multiple times). This means, that in the activity log, the block is only shown once (but the iteration value is shown in the log). Also, if used together with a time-limit pin, the time-limit is applied to the overall execution (i.e. NOT applied to each individual iteration). Finally, a cancel input finishes the overall execution, not only the current iteration. If present, the time-output of repeated step gets the overall time (it is not written multiple times with the individual times).
Notice that the action is repeated with the same input values. If there are any consuming input values, only a single value is consumed, regardless of how often the action is repeated. Thus it is for example not possible to force a stream-read-line step, to read multiple lines. Such a behavior must be realized via an explicit loop, or by triggering the read step multiple times.
Performer Input Pin
If a virtual block's performer input is left unconnected, the environment is consulted for a binding of the virtual action or the virtual action's library (see virtual library). This is a non-consuming, non-triggering pin.
Variable Number of Pins
An elementary action may specify that a variable number of input and/or output pins is allowed in its steps. Then, the number of pins can be changed by pulling down a special pin-count handle in the activity diagram, where the step is placed. To enable a variable number of input or output pins, use the "Special Attributes" popup menu function in the schema editor of the action block.
Before the 2.8 version of expecco, only the very last pin could be defined as a "variable" pin, and only the last pin could be replicated in a step. Starting with the 2.8 version, it is also possible to replicate a group of pins. For example, by specifying the last 2 pins as a replicated group, additional pins can be added to a step in multiples of 2. Individual pins within a group may have different datatypes, and the replicated pins take the datatype of their corresponding pin from the action's schema definition. This is very useful to define "multi-key-value" groups, for example for multi-setter actions of collections or user data objects.
Notice that only elementary actions can have a variable number of pins (because there is no way currently, to connect those pins in a diagram). Also, special elementary code is required which dynamically determines the number of pins and enumerates them. The elementary code API of variable pins is described in Expecco API.
Output pins are located at the right ("data outputs") and at the bottom of a step (called "control" or "special" outputs). They pass data/control information to other steps (if connected). Notice that output pins can be configured to be either buffered or unbuffered:
Output Pin Buffer Attribute
Each regular (data) output pin can individually be configured to be either buffering or non-buffering. Their behavior differs in the error situation and in case of multiple writes to the pin:
a buffered output pin collects and remembers outgoing values until the action is finished without being either canceled or terminated by an exception. If finished without success, no values are written. If finished with success, the remembered values are written (at the same time as other buffered pins of the same action) to their output connections. It is ensured that all outgoing values of a step with multiple output pins are written in one atomic operation, so that follow up steps which receive those values will get them in a synchronized fashion (just imagine, that there are multiple values to be passed to receiving steps, and those values form a tuple of related values). If a buffered output pin is written multiple times, all written values are buffered until successful finished.
unbuffered output pins forward their output values immediately to any connected input pin of receiving steps. If multiple values are generated during the action's execution, the values may immediately trigger other activities as values are generated, and those may execute in parallel to the generating activity. Once a value is generated, it will be processed by receiving steps, regardless of the sending action's final termination status - even if failing or canceled will those values be "out in the world". Also, there will be no synchronization with respect to values as generated for other output pins of the same action.
Due to the big difference in the behavior (possibly affecting the execution of the diagram), this attribute is also visualized in the diagram: buffered pins are drawn as an unfilled pin-rectangle (meaning that no value appears immediately), whereas non-buffered pins are drawn as a filled rectangle (meaning that values appear immediately).
When Should an Unbuffered Output be Used?
Consider a block which is programmed to send out 100 values (or value tuples) to its output pins, which are fed to a test scenario action which receives those tuples. The values may contain tuples of test data or individual data values. Also consider, that these 100 values should be sent 1 per second. I.e. the test should run for 100 seconds, with 1 seconds delay between runs. This is done by putting a delay into the generator, to produce only one data tuple per second (which can be done easily both in an elementary block, or in a compound block).
If the data generator block's outputs are buffered, it would run for 100 seconds, remember all generated values, and then send them out as one jet of 100 tuples to the receiving block. Thus the system would seem idle, doing no test for a 100 seconds, then execute them all as fast as it can.
With buffering turned off, each tuple is immediately sent to the receiving test scenario block, which would start one test run every second.
Another example could be a test-data feeder, which reads tupled data linewise from a file (say a CSV file from excel), and passes those tuples to its output pin(s), again one tuple per second. Here again, unbuffered outputs are needed, so that the action connected to the output gets triggered once per second.
Output Pin Value Timestamped Attribute
If enabled, a timestamp is attached to every pin's output datum as it is created (written to the pin). This is useful for unbuffered pins of actions, which write multiple times to an output pin (for example: server actions, which send logging information to a stream-output pin).
Notice that timestamps are disabled by default, because that will required a lot of additional storage to be held in the (in memory) activity logs.
Special Output Pins
Enable Output Pin
The optional "enable output" pin (also called "trigger-out") is activated after the execution of a step is finished successfully. Its main use is to enforce sequential execution of steps which would otherwise either execute in parallel or in no definite execution order. It can also be used to trigger actions , depending on the outcome of a previous computation. Finally, it can be used to create looping constructs, by connecting a trigger output back to a previously executed action's trigger input pin.
The value appearing at the trigger output pin is arbitrary - it should be treated as an anonymous token, of no specific data type (actually, it is not defined, what is appearing there).
The enable output pin is NOT given a value if an exception is raised AND an exception output pin is present (because then, you may want to direct the execution to different paths to handle or report the error). However, it is triggered, if an exception is ignored via the "ignore exceptions" attribute.
Exception Output Pin
The optional "exception output pin" is used for exception handling and reporting. Whenever an execution leads to an error, and the exception output pin is connected, an exception information datum is generated and passed to that output. Usually the value is used to trigger or cancel other activities via their cancel or trigger input pins. Steps which do not handle the exception (i.e. those without an exception output and not ignoring the exception) pass the exception up to their enclosing network's step and possibly handled there. If nowhere handled, this exception forwarding will continue up to the top-level test case, and lead to an ERROR verdict there.
Notice, that by connecting both the exception-out and the trigger-out pins to different follow up steps, conditional execution based on a step's success is possible.
Also note that if the "Ignore Exceptions" option is set for a step, both enable output pin AND the exception output pins are triggered. But again: ONLY if the "Ignore Exceptions" attribute is set. This at first sight unintuitive behavior gives you a chance to detect failures and write a log entry, but still continue with the normal execution order. Without this, you had to duplicate the paths from the exception and the trigger output.
Exception Pin Example 1: Selecting a different execution path on failure
Depending on the success or failure of Step1, either Step2 or Step3 is executed:
Exception Pin Example 2: Cancelling another operation on failure
Both are started in parallel (due to the autostart flag). If the execution of the first step fails, the second will be canceled via its cancel pin (if it has not finished by that time):
ExecutionTime Output Pin
This pin, if present, will provide the step's execution time (as a TimeDuration value). You can pass this value to an "Assert Less", "Assert Greater" or "Assert in Range" block to ensure that the execution time of an action is within some bounds. Typically this is used to ensure that performance and response-time requirements are met.
The timelimit output is written, if either the step executes with success, or the "ignore exceptions" or the "cancel is OK" attribute of the step is set.
Special Pins of Script Action Blocks
Script actions can provide a number of additional special pins. In order to make activity diagrams easier to read, these are normally not automatically created for new script actions (when created in the navigation tree).
Those pins are created via the "Special Pins" menu function in the scheme editor. The pins are on the input side:
an optional execution directory
an optional list of command line arguments
the standard input to be sent to the script
to treat a non-zero exit code as success
to provide raw output (no line end canonicalization)
to define the stdout pin's behaviour (0 = all in one; 1 = linewise)
to define the stderr pin's behaviour (0 = all in one; 1 = linewise)
an optional prompt string to search for
an optional command
and on the output side:
the standard output; either linewise, all-in-one, or chunked, as specified by the "outNLines" input pin
the standard error; either linewise, all-in-one, or chunked, as specified by the "errNLines" input pin
- result (exit code)
the program's exit code
a trigger if the optional prompt was detected on stdout or stderr
the stdout type-converted as per datatype
"Do not Log Pin-Value in Activitylog"-Attribute
This is an attribute associated to a step's input and output pins. By default, all of a step's pin values are recorded in the activity log, unless either the owning step has been given a "Skip in Activity Log" attribute, or pin value logging is globally disabled (via the project settings), or logging is completely disabled (also via the project settings).
However, there are some rare situations, when an individual pin's value should not be recorded in the activityLog:
- if the datum contains security sensitive information (e.g. a password or cryptographic key)
- if the datum is huge and would otherwise fill up the memory too fast if remembered (e.g. big transfer data buffers, which are not needed for later reporting)
The "Do not Log Pin-Value" attribute can be set for every step-pin individually via the pin's context menu-"Special Attributes" submenu.
Notice: if big data buffers are required to be recorded, these should be written into an attachment file, and the involved pins be marked as non-logged.
For elementary code API to pins see: Expecco API