Flow diagrams to Petri nets

This document describes how the FD2PN component has been reused for several workflow languages. This component features a reusable transformation from a Workflow concept to a Petri net, which is able to deal with several of the patterns defined in the Workflow patterns catalog.

The menu provides links to the different artefacts of the case study.

Code for Bentō:


Bender is a Domain-Specific Language to describe the workflow of telephony services. These services include include voice notes and voice-to-email services; services to inject customized background sounds in phone calls; the customization of the telephone keys to inject “voice smileys” in a conversation; as well as the typical Interactive Voice Response (IVR) applications of call centers, telebanking, credit card services, and so on.

Bender is integrated in the Umbra Designer modelling tool. A detailed description of Umbra is available in [BEL13].


The following diagram is an excerpt with the relevant elements of the Bender meta-model. The complete diagrams are also available in SVG and PNG formats.

In Bender a workflow task is represented as a State. The two subclasses InitialState and FinalState indicate the beginning and end of the execution respectivly, and at least one instance of each one must exist in any valid Bender model. Hierarchical descomposition of states is possible with CompositeState. Each State holds the transitions that stem from it. There are many subclasses of Transition, which represent the different kinds of events that may trigger the transition. In particular, Collected represents a choice in the workflow and implicitly behaves both as a transition and as a state (workflow task).


The following image shows an example model built with Bender. It represents a simple service which requests the user to press 1 or 2 to perform some action. The service initial state is Init, where the service waits for incoming calls once a new session has been established. Hence, at the top level, the event from the initial state is SessionRequest (see arrow coming into state Init). The service designer does not have to take care of handling the Bootstrap event, as the tool itself will generate code to register the listeners for the events and actions used by the service (in the example all are IVR events), and identifying the needed resources. Events are depicted in blue (bold) over the arrows, while actions are shown in red below the events. The service plays a welcome message to each incoming call. This is modelled by a Connected event and the associated PlayCollect action. This action has the additional effect to demand pressing some key on the phone keypad. Thus, the service waits in state KeyRequest until the reception of some key stroke (event Collected). If the key pressed was “1” or “2”, the service plays a different message in each case, as indicated by the Play action. Once the message associated to “1” or “2” is played (event Played in the transition going out from Bye), the service ends the call through the Hangup action.


The binding for simple tasks and transitions is straightforward ( (Node maps to State and FlowEdge to Transition), as well as for FinalTask and the isInitial attribute. The only available gateway in Bender is ExclusiveChoice, which maps to Collected. However, this binding is challenging because of the twofold nature of Collected: although it inherits from Transition in the meta-model, from the binding perspective it has to be recasted as a node whose, being the Branch classes FlowEdge. This is implemented by binding Node to Collected, FlowEdge to Branch (lines 8-9), and tweaking the result of ins and outs references (lines 26 and 38).

binding bender2fd {  
	concept   FD   : "platform:/resource/bento.examples.flow_diagrams.petrinets/metamodels/flow_concept.ecore"
	metamodel BENDER : "platform:/resource/bento.examples.flow_diagrams.petrinets/bindings/bender2pn/StateMachine.ecore"

	class FlowDiagram to StateMachine	
	class Node        to State, Collected
	class FlowEdge    to Transition, Branch
	class Task            to State	    
	class ExclusiveChoice to Collected
	class FinalTask       to FinalState    
	class ParallelSplit   to NONE
	class Synchronization to NONE
	class MultiChoice     to NONE
	class SimpleMerge     to NONE
	feature FlowDiagram.nodes is states
	feature FlowDiagram.edges = self.states->collect(s | s.outs).flatten()

	feature FlowEdge[Transition]."in" is src
	feature FlowEdge[Transition].out = 
		if self.oclIsKindOf(FD!Collected) then 
			self --> the transition goes to itself, but seen as a node 

	feature FlowEdge[Branch]."in" = self.refImmediateComposite
	feature FlowEdge[Branch].out is out

	feature Task.name is nombre
        feature Node[State].ins  = FD!Transition.allInstances()->select(t | t.defaultTar = self)
	feature Node[State].outs  is outs

        -- This is needed because a Collected sees itself as a transition
	feature Node[Collected].ins = Sequence { self } 
	feature Node[Collected].outs  is branches

	feature Task[State].isInitial = self.oclIsKindOf(BENDER!InitialState)

	feature Task[Collected].isInitial = false

	feature FinalTask.isTerminating = true