Table of Contents
eTrice provides an implementation of the ROOM modeling language (Real Time Object Oriented Modeling) together with editors, code generators for Java, C++ and C code and exemplary target middleware.
The model is defined in textual form (Xtext) with graphical editors (Graphiti) for the structural and behavioral (i.e. state machine) parts.
eTrice is all about the reduction of complexity:
structural complexity
by explicit modeling of hierarchical Actor containment, layering and inheritance
behavioral complexity
by hierachical statemachines with inheritance
teamwork complexity
because loosely coupled Actors provide a natural way to structure team work
since textual model notation allows simple branching and merging
complexity of concurrent & distributed systems
because loosely coupled Actors are deployable to threads, processes, nodes
complexity of variant handling and reuse (e.g. for product lines)
by composition of existing Actors to new structures
since Protocols and Ports make Actors replaceable
by inheritance for structure, behavior and Protocols
by making use of model level libraries
complexity of debugging
model level debugging: state machine animation, data inspection and manipulation, message injection, generated message sequence charts
model checking easier for model than for code (detect errors before they occur)
This chapter will give a rough overview of what ROOM ( R eal time O bject O riented M odeling) is and what it is good for. It will try to answer the following questions:
Where does it come from?
Which kind of SW-Systems will be addressed?
What is the relation between OOP and ROOM?
What are the benefits of ROOM?
Which consequences must be taken into account?
Room was developed in the 1990th on the background of the upcoming mobile applications with the goal to manage the complexity of such huge SW-Systems. From the very beginning ROOM has focused on a certain type of SW-Systems and is, in contrast to the UML, well suited for this kind of systems. In this sense, ROOM is a DSL (Domain Specific Language) for distributed, event driven, real time systems.
Bran Selic, Garth Gullekson and Paul T. Ward have published the concepts 1994 in the book Real-Time Object-Oriented Modeling. The company object time ™ developed a ROOM tool which was taken over by Rational SW ™ and later on by IBM ™. The company Protos Software Gmbh ™ also developed a ROOM tool called Trice ™ for control software for production machines and automotive systems. Trice ™ is the predecessor of eTrice (see Introduction to eTrice).
From our point of view ROOM provides still the clearest, simplest, most complete and best suited modeling concepts for the real time domain. All later proposals like the UML do not fit as well to this kind of problems.
As mentioned before ROOM addresses distributed, event driven, real time systems. But what is a real time system? ROOM defines a set of properties which are typical for a real time system. These properties are:
Timeliness
Dynamic internal structure
Reactiveness
Concurrency
Distribution
Reliability
Each of these properties has potential to make SW development complex. If a given system can be characterized with a combination of or all of these properties, ROOM might be applied to such a system.
As an example take a look at a washing machine. The system has to react on user interactions, has to handle some error conditions like a closed water tap or a defective lye pump. It has to react simultaneously to all these inputs. It has to close the water valve in a certain time to avoid flooding the basement. So, the system can be characterized as timely, concurrent and reactive. As long as the washing machine does not transform to a laundry drier by itself, the system has no dynamic internal structure and as long as all functions are running on a single micro controller the (SW)-system is not distributed. ROOM fits perfect to such a system.
A SW system which mainly consists of data transformations like signal/image processing or a loop controller (e.g. a PID controller) cannot be characterized with any of the above mentioned properties. However, in the real world most of the SW systems will be a combination of both. ROOM can be combined with such systems, so that for example an actor provides a run to completion context for calculating an image processing algorithm or a PID controller.
The relation between classical object oriented programming and ROOM is comparable to the relation between assembler programming and C programming. It provides a shift of the object paradigm. As the picture shows, the classic object paradigm provides some kind of information hiding. Attributes can be accessed via access methods. Logical higher level methods provide the requested behavior to the user.
As the figure illustrates, the classical object paradigm does not care about concurrency issues. The threads of control will be provided by the underlying operating system and the user is responsible to avoid access violations by using those operating system mechanisms directly (semaphore, mutex).
ROOM provides the concept of a logical machine (called actor) with its own thread of control. It provides some kind of cooperative communication infrastructure with run to completion semantic. That makes developing of business logic easy and safe (see basic concepts). The logical machine provides an encapsulation shell including concurrency issues (see chapter Run to completion).
This thinking of an object is much more general than the classic one.
ROOM has a lot of benefits and it depends on the users point of view which is the most important one. From a general point of view the most important benefit is, that ROOM allows to create SW systems very efficient, robust and safe due to the fact that it provides some abstract, high level modeling concepts combined with code generation and a small efficient runtime environment.
In detail:
ROOM models contain well defined interfaces (protocols), which makes it easy to reuse components in different applications or e.g. in a test harness.
Graphical modeling makes it easy to understand, maintain and share code with other developers
Higher abstraction in combination with automated code generation provides very efficient mechanisms to the developer.
ROOM provides graphical model execution, which makes it easy to understand the application or find defects in a very early phase.
Generating code from models will introduce some overhead in terms of memory footprint as well as performance. For most systems the overhead will be negligible. However, the decision for using ROOM should be made explicitly and it is always a trade off between development costs, time to market and costs in terms of a little bit more of memory and performance. Thanks to the powerful component model, ROOM is especially well suited for the development of software product lines with their need for reusable core assets.
Care must be taken during the introduction of the new methodology. Due to the fact that ROOM provides a shift of the object paradigm, developers and teams need a phase of adaption. Every benefit comes at a price.
The basic elements of ROOM are the actors with their ports and protocols. The protocol provides a formal interface description. The port is an interaction point where the actor interacts with its outside world. Each port has exactly one protocol attached. The sum of all ports builds up the complete interface of an actor. Each port can receive messages, with or without data, which are defined in the attached protocol. Each message will be handled by the actors behavior (state machine) or will be delegated to the actors internal structure.
![]() |
![]() |
Actor with Subactors | Protocol Definition |
The actor provides access protection for its own attributes (including complex types (classical objects)), including concurrency protection. An actor has neither public attributes nor public operations. The only interaction with the outside world takes place via interface ports. This ensures a high degree of reusability on actor level and provides an effective and safe programming model to the developer.
Receiving a message via a port will trigger the internal state machine. A transition will be executed depending on the message and the current state. Within this transition, detail level code will be executed and response messages can be sent.
With this model, a complex behavior can be divided into many relatively simple, linked actors. To put it the other way round: The complex behavior will be provided by a network of relatively simple components which are communicating with each other via well defined interfaces.
ROOM provides two types of hierarchy. Behavioral hierarchy and structural hierarchy. Structural hierarchy means that actors can be nested to arbitrary depth. Usually you will add more and more details to your application with each nesting level. That means you can focus yourself on any level of abstraction with always the same element, the actor. Structural hierarchy provides a powerful mechanism to divide your problem in smaller pieces, so that you can focus on the level of abstraction you want to work on.
The actor’s behavior will be described with a state machine. A state in turn may contain sub states. This is another possibility to focus on an abstraction level. Take the simple FSM from the blinky actor from the blinky tutorial.
Top level:
blinking Sub machine:
From an abstract point of view there is a state blinking. But a simple LED is not able to blink autonomously. Therefore you have to add more details to your model to make a LED blinking, but for the current work it is not of interest how the blinking is realized. This will be done in the next lower level of the hierarchy.
This simple example might give an idea how powerful this mechanisms is.
The hierarchical FSM provides a rich tool box to describe real world problems (see room concepts).
Layering is another well known form of abstraction to reduce complexity in the structure of systems. ROOM is probably the only language that supports Layering directly as language feature. Layering can be expressed in ROOM by Actors with specialized Ports, called Service Access Points ( SAP) and Service Provision Points ( SPP).
The Actor that provides a service implements an SPP and the client of that service implements an SAP. The Layer Connection connects all SAPs of a specific Protocol within an Actor hierarchy with an SPP that implements the service. From the Actors point of view, SAPs and SPPs behave almost like regular ports.
The Example shows a layered model. The Layer Connections define e.g. that the ApplicationLayer can only use the services of the ServiceLayer and the CommunicationLayer. Actors inside the ApplicationLayer that implement an SAP for those services are connected directly to the implementation of the services. Layering and actor hierarchies with port to port connections can be mixed on every level of granularity.
Run to completion (RTC) is a very central concept of ROOM. It enables the developer to concentrate on the functional aspects of the system. The developer doesn’t have to care about concurrency issues all the time. This job is concentrated to the system designer in a very flexible way. What does run to completion mean: RTC means that an actor, which is processing a message, can not receive the next message as long as the processing of the current message has been finished. Receiving of the next message will be queued from the underlying run time system.
Note: It is very important not to confuse run to completion and preemption. Run to completion means that an actor will finish the processing of a message before he can receive a new one (regardless of its priority). That does not mean that an actor cannot be preempted from an higher priority thread of control. But even a message from this higher prior thread of control will be queued until the current processing has been finished.
With this mechanism all actor internal attributes and data structures are protected. Due to the fact that multiple actors share one thread of control, all objects are protected which are accessed from one thread of control but multiple actors. This provides the possibility to decompose complex functionality to several actors without the risk to produce access violations or dead locks.
Since from ROOM models executable code can be generated, it is important to define the way the actors are executed and communicate with each other. The combination of communication and execution is called the Execution Model. Currently the eTrice tooling only supports the message driven and parts of the data driven execution model. In future releases more execution models will be supported, depending on the requirements of the community.
message driven (asynchronous, non blocking, no return value): Usually the message driven communication is implemented with message queues. Message queues are inherently asynchronous and enable a very good decoupling of the communicating parties.
data driven (asynchronous, non blocking, no return value): In data driven communication sender and receiver often have a shared block of data. The sender writes the data and the receiver polls the data.
function call (synchronous, blocking, return value): Regular function call as known in most programming languages.
execution by receive event: The message queue or the event dispatcher calls a receive event function of the message receiver an thereby executes the processing of the event.
polled execution: The objects are processed by a cyclic execute call
execution by function call: The caller executes the called object via function call
In todays embedded systems in most cases one or several of the following execution models are used:
The message driven execution model is a combination of message driven communication and execution by receive event. This model allows for distributed systems with a very high throughput. It can be deterministic but the determinism is hard to proof. This execution model is often found in telecommunication systems and high performance automation control systems.
The data driven execution model is a combination of data driven communication and polled execution. This model is highly deterministic and very robust, but the polling creates a huge performance overhead. The determinism is easy to proof (simple mathematics). The execution model is also compatible with the execution model of control software generated by Tools like Matlab™ and LabView™. This model is usually used for systems with requirements for safety, such as automotive and avionic systems.
The synchronous execution model could also be called simple function calls. This model is in general not very well suited to support the run to completion semantic typical for ROOM models, but could also be generated from ROOM models. With this execution model also lower levels of a software system, such as device drivers, could be generated from ROOM models.
ETrice generates code out of ROOM models. The code generator and the generated code relies on a runtime framework and on some ready to use model parts. This parts provide services like:
messaging
logging
timing
Additionally some tutorial models will be provided to make it easy to start with eTrice. All this parts must be available in our workspace before you can start working. After installation of eclipse (indigo) and the eTrice plug in your workspace should look like this:
Just the eTrice menu item is visible from the eTrice tool. From the File menu select File->New->Project
Open the eTrice tab and select eTrice Java Runtime
Press Next and Finish to install the Runtime into your workspace.
Do the same steps for eTrice Java Modellib and eTrice Java Tutorials. To avoid temporary error markers you should keep the proposed order of installation. The resulting workspace should look like this:
Now workspace is set up and you can perform the tutorials or start with your work.
The tutorial models are available in the org.eclipse.etrice.tutorials project. All tutorials are ready to generate and run without any changes. To start the code generator simply run gen_org.eclipse.etrice.tutorials.launch as gen_org.eclipse.etrice.tutorials.launch:
After generation for each tutorial a java file called SubSystem_ModelnameRunner.java is generated. To run the model simply run this file as a java application:
To stop the application type quit in the console window.
Performing the tutorials will setup an dedicated project for each tutorial. Therefore there are some slight changes especially whenever a path must be set (e.g. to the model library) within your own projects. All this is described in the tutorials.
In this tutorial you will build your first very simple eTrice model. The goal is to learn the work flow of eTrice and to understand a few basic features of ROOM. You will perform the following steps:
create a new model from scratch
add a very simple state machine to an actor
generate the source code
run the model
open the message sequence chart
Make sure that you have set up the workspace as described in Setting up the workspace.
The easiest way to create a new eTrice Project is to use the eclipse project wizard. From the eclipse file menu select File->New->Project and create a new eTrice project and name it HelloWorld
The wizard creates everything that is needed to create, build and run an eTrice model. The resulting project should look like this:
Within the model directory the model file HelloWorld.room was created. Open the HelloWorld.room file and delete the contents of the file. Open the content assist with Ctrl+Space and select model skeleton.
Edit the template variables by typing the new names and jumping with Tab from name to name.
The resulting model code should look like this:
RoomModel HelloWorld {
LogicalSystem System_HelloWorld {
SubSystemRef subsystem : SubSystem_HelloWorld
}
SubSystemClass SubSystem_HelloWorld {
ActorRef application : HelloWorldTop
}
ActorClass HelloWorldTop {
}
}
The goal of eTrice is to describe distributed systems on a logical level. In the current version not all elements will be supported. But as prerequisite for further versions the following elements are mandatory for an eTrice model:
the LogicalSystem
at least one SubSystemClass
at least one ActorClass
The LogicalSystem represents the complete distributed system and contains at least one SubSystemRef. The SubSystemClass represents an address space and contains at least one ActorRef. The ActorClass is the building block of which an application will be built of. It is in general a good idea to define a top level actor that can be used as reference within the subsystem.
The outline view of the textual ROOM editor shows the main modeling elements in an easy to navigate tree.
We will implement the Hello World code on the initial transition of the HelloWorldTop actor. Therefore open the state machine editor by right clicking the HelloWorldTop actor in the outline view and select Edit Behavior.
The state machine editor will be opened. Drag and drop an Initial Point from the tool box to the diagram into the top level state. Drag and drop a State from the tool box to the diagram. Confirm the dialogue with ok. Select the Transition in the tool box and draw the transition from the Initial Point to the State. Open the transition dialogue by double clicking the caption of the transition and fill in the action code.
System.out.println("Hello World !");
The result should look like this:
Save the diagram and inspect the model file. Note that the textual representation was created after saving the diagram.
Now the model is finished and source code can be generated. The project wizard has created a launch configuration that is responsible for generating the source code. From HelloWorld/ right click gen_HelloWorld.launch and run it as gen_HelloWorld. All model files in the model directory will be generated.
The code will be generated to the src-gen directory. The main function will be contained in SubSystem_HelloWorldRunner.java. Select this file and run it as Java application.
The Hello World application starts and the string will be printed on the console window. To stop the application the user must type quit in the console window.
During runtime the application produced a MSC and wrote it to a file. Open HelloWorld/tmp/log/SubSystem_HelloWorld_Async.seq using Trace2UML (it is open source and can be obtained from http://trace2uml.tigris.org/). You should see something like this:
Now you have generated your first eTrice model from scratch. You can switch between diagram editor and model (.room file) and you can see what will be generated during editing and saving the diagram files. You should take a look at the generated source files to understand how the state machine is generated and the life cycle of the application. The next tutorials will deal with more complex hierarchies in structure and behavior.
This tutorial describes how to use the TimingService, how to combine a generated model with manual code and how to model a hierarchical state machine. The idea of the tutorial is to switch a LED on and off. The behavior of the LED should be: blinking in a one second interval for 5 seconds, stop blinking for 5 seconds, blinking, stop,... For this exercise we will use a little GUI class that will be used in more sophisticated tutorials too. The GUI simulates a pedestrian traffic crossing. For now, just a simple LED simulation will be used from the GUI.
After the exercise is created you must copy the GUI to your src directory (see below).
The package contains four java classes which implements a small window with a 3-light traffic light which simulates the signals for the car traffic and a 2-light traffic light which simulates the pedestrian signals.
The GUI looks like this:
Within this tutorial we will just toggle the yellow light.
You will perform the following steps:
create a new model from scratch
define a protocol
create an actor structure
create a hierarchical state machine
use the predefined TimingService
combine manual code with generated code
build and run the model
open the message sequence chart
Remember exercise HelloWorld. Create a new eTrice project and name it Blinky
To use the GUI please copy the package org.eclipse.etrice.tutorials.PedLightGUI from org.eclipse.etrice.tutorials/src to your src directory Blinky/src. For this tutorial you must remove the error markers by editing the file PedestrianLightWndNoTcp.java. Appropriate comments are provided to remove the error marker for this turorial.
Open the Blinky.room file and copy the following code into the file or use content assist to create the model.
RoomModel Blinky {
LogicalSystem System_Blinky {
SubSystemRef subsystem : SubSystem_Blinky
}
SubSystemClass SubSystem_Blinky {
ActorRef application : BlinkyTop
}
ActorClass BlinkyTop {
}
}
Position the cursor outside any class definition and right click the mouse within the editor window. From the context menu select Content Assist
Select ActorClass – actor class skeleton and name it Blinky.
Repeat the described procedure and name the new actor BlinkyController.
With Ctrl+Shift+F you can beautify the model code.
Save the model and visit the outline view.
With the help of Content Assist create a ProtocolClass and name it BlinkyControlProtocol. Inside the brackets use the Content Assist (CTRL+Space) to create two incoming messages called start and stop.
The resulting code should look like this:
With Ctrl-Shift+F or selecting Format from the context menu you can format the text. Note that all elements are displayed in the outline view.
Switching on and off the LED is timing controlled. The timing service is provided from the model library and must be imported before it can be used from the model.
This is the first time you use an element from the modellib. Make sure that your Java Build Path has the appropriate entry to the modellib. Otherwise the jave code, which will be generated from the modellib, can not be referenced. (right click to Blinky and select properties. Select the Java Build Path tab)
After the build path is set up return to the model and navigate the cursor at the beginning of the model and import the timing service:
RoomModel Blinky {
import room.basic.service.timing.* from "../../org.eclipse.etrice.modellib/models/TimingService.room"
LogicalSystem System_Blinky {
SubSystemRef subsystem: SubSystem_Blinky
}
}
...
Make sure that the path fits to your folder structure.
Now it can be used within the model. Right click to SubSystem_Blinky within the outline view. Select Edit Structure. The application is already referenced in the subsystem. Drag and Drop an ActorRef to the SubSystem_Blinky and name it timingService. From the actor class drop down list select room.basic.service.timing.ATimingService. Draw a LayerConnection from application to each service provision point (SPP) of the timingService. The resulting structure should look like this:
The current version of eTrice does not provide a graphical element for a service access point (SAP). Therefore the SAPs to access the timing service must be added in the .room file. Open the Blinky.room file and navigate to the Blinky actor. Add the following line to the structure of the actor:
SAP timer: room.basic.service.timing.PTimeout
Do the same thing for BlinkyController.
The resulting code should look like this:
From the outline view right click to Blinky and select Edit Structure. Drag and Drop an Interface Port to the boarder of the Blinky actor. Note that an interface port is not possible inside the actor. Name the port ControlPort and select BlinkyControlProtocol from the drop down list. Uncheck Conjugated and Is Relay Port. Click ok. The resulting structure should look like this:
Repeat the above steps for the BlinkyController. Make the port Conjugated
Keep in mind that the protocol defines start and stop as incoming messages. Blinky receives this messages and therefore Blinky's ControlPort must be a regular port and BlinkyController's ControlPort must be a conjugated port.
From the outline view right click BlinkyTop and select Edit Structure.
Drag and Drop an ActorRef inside the BlinkyTop actor. Name it blinky. From the actor class drop down list select Blinky. Do the same for controller. Connect the ports via the binding tool. The resulting structure should look like this:
The application should switch on and off the LED for 5 seconds in a 1 second interval, then stop blinking for 5 seconds and start again. To implement this behavior we will implement two FSMs. One for the 1 second interval and one for the 5 second interval. The 1 second blinking should be implemented in Blinky. The 5 second interval should be implemented in BlinkyController. First implement the Controller.
Right click to BlinkyController and select Edit Behavior. Drag and Drop the Initial Point and two States into the top state. Name the states on and off. Use the Transition tool to draw transitions from init to on from on to off and from off to on.
Open the transition dialog by double click the arrow to specify the trigger event and the action code of each transition. Note that the initial transition does not have a trigger event.
The transition dialog should look like this:
The defined ports will be generated as a member attribute of the actor class from type of the attached protocol. So, to send e message you must state port.message(param);. In this example ControlPort.start() sends the start message via the ControlPort to the outside world. Assuming that Blinky is connected to this port, the message will start the one second blinking FSM. It is the same thing with the timer. The SAP is also a port and follows the same rules. So it is clear that timer.Start(5000); will send the Start message to the timing service. The timing service will send a timeoutTick message back after 5000ms.
Within each transition the timer will be restarted and the appropriate message will be sent via the ControlPort.
The resulting state machine should look like this: (Note that the arrows peak changes if the transition contains action code.)
Save the diagram and inspect the Blinky.room file. The BlinkyController should look like this:
Now we will implement Blinky. Due to the fact that Blinky interacts with the GUI class a view things must to be done in the model file.
Double click Blinky in the outline view to navigate to Blinky within the model file. Add the following code: (type it or simply copy it from the tutorial project)
usercode1 will be generated at the beginning of the file, outside the class definition. usercode2 will be generated within the class definition. The code imports the GUI class and instantiates the window class. Attributes for the carLights and pedLights will be declared to easily access the lights in the state machine. The Operation destroyUser() is a predefined operation that will be called during shutdown of the application. Within this operation, cleanup of manual coded classes can be done.
Now design the FSM of Blinky. Remember, as the name suggested blinking is a state in which the LED must be switched on and off. We will realize that by an hierarchical FSM in which the blinking state has two sub states.
Open the behavior diagram of Blinky by right clicking the Blinky actor in the outline view. Create two states named blinking and off. Right click to blinking and create a subgraph.
Create the following state machine. The trigger events between on and off are the timeoutTick from the timer port.
Create entry code for both states by right clicking the state and select Edit State...
Entry code of on is:
timer.Start(1000);
carLights.setState(TrafficLight3.YELLOW);
Entry code of off is:
timer.Start(1000);
carLights.setState(TrafficLight3.OFF);
Navigate to the Top level state by double clicking the /blinking state. Create the following state machine:
The trigger event from off to blinking is the start event from the ControlPort.The trigger event from blinking to off is the stop event from the ControlPort. Note: The transition from blinking to off is a so called group transition. This is a outgoing transition from a super state (state with sub states) without specifying the concrete leave state (state without sub states). An incoming transition to a super state is called history transition.
Action code of the init transition is:
carLights = light.getCarLights();
pedLights = light.getPedLights();
carLights.setState(TrafficLight3.OFF);
pedLights.setState(TrafficLight2.OFF);
Action code from blinking to off is:
timer.Kill();
carLights.setState(TrafficLight3.OFF);
The model is complete now. You can run and debug the model as described in getting started. Have fun.
The complete model can be found in /org.eclipse.etrice.tutorials/model/Blinky.
Run the model and take a look at the generated MSCs. Inspect the generated code to understand the runtime model of eTrice. Within this tutorial you have learned how to create a hierarchical FSM with group transitions and history transitions and you have used entry code. You are now familiar with the basic features of eTrice. The further tutorials will take this knowledge as a precondition.
This tutorial shows how data will be sent in a eTrice model. Within the example you will create two actors (MrPing and MrPong). MrPong will simply loop back every data it received. MrPing will send data and verify the result.
You will perform the following steps:
create a new model from scratch
create a data class
define a protocol with attached data
create an actor structure
create two simple state machines
build and run the model
Remember exercise HelloWorld. Create a new eTrice project and name it SendingData Open the SendingData.room file and copy the following code into the file or use content assist to create the model.
RoomModel SendingData {
LogicalSystem SendingData_LogSystem {
SubSystemRef SendingDataAppl:SendingData_SubSystem
}
SubSystemClass SendingData_SubSystem {
ActorRef SendigDataTopRef:SendingDataTop
}
ActorClass SendingDataTop {
}
}
Position the cursor outside any class definition and right click the mouse within the editor window. From the context menu select Content Assist (or Ctrl+Space).
Select DataClass – data class skeleton and name it DemoData. Remove the operations and add the following Attributes:
DataClass DemoData {
Attribute int32Val: int32 = "4711"
Attribute int8Array [ 10 ]: int8 = "{1,2,3,4,5,6,7,8,9,10}"
Attribute float64Val: float64 = "0.0"
Attribute stringVal: string = "\"empty\""
}
Save the model and visit the outline view. Note that the outline view contains all data elements as defined in the model.
With the help of Content Assist create a ProtocolClass and name it PingPongProtocol. Create the following messages:
ProtocolClass PingPongProtocol {
incoming {
Message ping(data: DemoData)
Message pingSimple(data:int32)
}
outgoing {
Message pong(data: DemoData)
Message pongSimple(data:int32)
}
}
With the help of Content Assist create two new actor classes and name them MrPing and MrPong. The resulting model should look like this:
RoomModel SendingData {
LogicalSystem SendingData_LogSystem {
SubSystemRef SendingDataAppl: SendingData_SubSystem
}
SubSystemClass SendingData_SubSystem {
ActorRef SendigDataTopRef: SendingDataTop
}
ActorClass SendingDataTop { }
DataClass DemoData {
Attribute int32Val: int32 = "4711"
Attribute int8Array [ 10 ]: int8 = "{1,2,3,4,5,6,7,8,9,10}"
Attribute float64Val: float64 = "0.0"
Attribute stringVal: string = "\"empty\""
}
ProtocolClass PingPongProtocol {
incoming {
Message ping(data: DemoData)
Message pingSimple(data: int32)
}
outgoing {
Message pong(data: DemoData)
Message pongSimple(data: int32)
}
}
ActorClass MrPing {
Interface { }
Structure { }
Behavior { }
}
ActorClass MrPong {
Interface { }
Structure { }
Behavior { }
}
}
The outline view should look like this:
Save the model and visit the outline view. Within the outline view, right click on the MrPong actor and select Edit Structure. Select an Interface Port from the toolbox and add it to MrPong. Name the Port PingPongPort and select the PingPongProtocol
Do the same with MrPing but mark the port as conjugated
Within the outline view, right click MrPong and select Edit Behavior. Create the following state machine:
The transition dialogues should look like this: For ping:
For pingSimple:
Within the outline view double click MrPing. Navigate the cursor to the behavior of MrPing. With the help of content assist create a new operation.
Name the operation printData and define the DemoData as a parameter.
Fill in the following code:
Operation printData(d: DemoData) : void {
"System.out.printf(\"d.int32Val: %d\\n\",d.int32Val);"
"System.out.printf(\"d.float64Val: %f\\n\",d.float64Val);"
"System.out.printf(\"d.int8Array: \");"
"for(int i = 0; i<d.int8Array.length; i++) {"
"System.out.printf(\"%d \",d.int8Array[i]);}"
"System.out.printf(\"\\nd.stringVal: %s\\n\",d.stringVal);"
}
For MrPing create the following state machine: (Remember that you can copy and paste the action code from the tutorial directory.)
The transition dialogues should look like this:
For init:
For wait1:
For next:
For wait2:
Open the Structure from SendingDataTop and add MrPing and MrPong as a reference. Connect the ports.
The model is finished now and can be found in /org.eclipse.etrice.tutorials/model/SendingData.
Generate the code by right click to gen_SendingData.launch and run it as gen_SendingData. Run the model. The output should look like this:
type ‚quit’ to exit /SendingData_SubSystem/SendigDataTopRef/ref0 -> waitForPongSimple /SendingData_SubSystem/SendigDataTopRef/ref1 -> looping /SendingData_SubSystem/SendigDataTopRef/ref1 -> looping data: 1 /SendingData_SubSystem/SendigDataTopRef/ref0 -> waitForPongSimple /SendingData_SubSystem/SendigDataTopRef/ref1 -> looping data: 2 /SendingData_SubSystem/SendigDataTopRef/ref0 -> waitForPongSimple /SendingData_SubSystem/SendigDataTopRef/ref1 -> looping data: 3 /SendingData_SubSystem/SendigDataTopRef/ref0 -> waitForPongSimple /SendingData_SubSystem/SendigDataTopRef/ref1 -> looping data: 4 /SendingData_SubSystem/SendigDataTopRef/ref0 -> waitForPongSimple /SendingData_SubSystem/SendigDataTopRef/ref1 -> looping data: 5 /SendingData_SubSystem/SendigDataTopRef/ref0 -> waitForPongSimple /SendingData_SubSystem/SendigDataTopRef/ref1 -> looping data: 6 /SendingData_SubSystem/SendigDataTopRef/ref0 -> waitForPongSimple /SendingData_SubSystem/SendigDataTopRef/ref1 -> looping data: 7 /SendingData_SubSystem/SendigDataTopRef/ref0 -> waitForPongSimple /SendingData_SubSystem/SendigDataTopRef/ref1 -> looping data: 8 /SendingData_SubSystem/SendigDataTopRef/ref0 -> waitForPongSimple /SendingData_SubSystem/SendigDataTopRef/ref1 -> looping data: 9 /SendingData_SubSystem/SendigDataTopRef/ref0 -> waitForPongSimple /SendingData_SubSystem/SendigDataTopRef/ref1 -> looping data: 10 /SendingData_SubSystem/SendigDataTopRef/ref0 -> waitForPong /SendingData_SubSystem/SendigDataTopRef/ref1 -> looping /SendingData_SubSystem/SendigDataTopRef/ref1 -> looping d.int32Val: 4711 d.float64Val: 0,000000 d.int8Array: 1 2 3 4 5 6 7 8 9 10 d.stringVal: empty /SendingData_SubSystem/SendigDataTopRef/ref0 -> waitForPong d.int32Val: 815 d.float64Val: 3,141234 d.int8Array: 100 101 102 103 104 105 106 107 108 109 d.stringVal: some contents /SendingData_SubSystem/SendigDataTopRef/ref0 -> waitForPong quit echo: quit
Within the first loop an integer value will be incremented by MrPong and sent back to MrPing. As long as the guard is true MrPing sends back the value.
Within the next transition, MrPing creates a data class and sends the default values. Then MrPing changes the values and sends the class again. At this point you should note that during the send operation, a copy of the data class will be created and sent. Otherwise it would not be possible to send the same object two times, even more it would not be possible to send a stack object at all. This type of data passing is called sending data by value. However, for performance reasons some applications requires sending data by reference. In this case the user is responsible for the life cycle of the object. In Java the VM takes care of the life cycle of an object. This is not the case for C/C++. Consider that a object which is created within a transition of a state machine will be destroyed when the transition is finished. The receiving FSM would receive an invalid reference. Therefore care must be taken when sending references.
For sending data by reference you simply have to add the keyword ref to the protocol definition.
Message ping(data: DemoData ref)
Make the test and inspect the console output.
The scope of this tutorial is to demonstrate how to receive model messages from outside the model. Calling methods which are not part of the model is simple and you have already done this within the blinky tutorial (this is the other way round: model => external code). Receiving events from outside the model is a very common problem and a very frequently asked question. Therefore this tutorial shows how an external event (outside the model) can be received by the model.
This tutorial is not like hello world or blinky. Being familiar with the basic tool features is mandatory for this tutorial. The goal is to understand the mechanism not to learn the tool features.
The idea behind the exercise is, to control a Pedestrian crossing light. We will use the same GUI as for the blinky tutorial but now we will use the REQUEST button to start a FSM, which controls the traffic lights.
The REQUEST must lead to a model message which starts the activity of the lights.
There are several possibilities to receive external events (e.g. TCP/UDP Socket, using OS messaging mechanism), but the easiest way is, to make a port usable from outside the model. To do that a few steps are necessary:
specify the messages (within a protocol) which should be sent into the model
model an actor with a port (which uses the specified protocol) and connect the port to the receiver
the external code should know the port (import of the port class)
the external code should provide a registration method, so that the actor is able to allow access to this port
the port can be used from the external code
Use the New Model Wizzard to create a new eTrice project and name it PedLightsController.
Copy the package org.eclipse.etrice.tutorials.PedLightGUI to your src directory (see blinky tutorial).
In PedestrianLightWndNoTcp.jav uncomment line 15 (import), 36, 122 (usage) and 132-134 (registration). The error markers will disappear after the code is generated from the model.
Copy the model from /org.eclipse.etrice.tutorials/model/PedLightsController to your model file, or run the model directly in the tutorial directory.
Adapt the import statement to your path.
import room.basic.service.timing.* from "../../org.eclipse.etrice.modellib/models/TimingService.room"
Generate the code from the model.
Add the org.eclipse.etrice.modellib to the Java Class Path of your project.
All error markers should be disappeared and the model should be operable.
Arrange the Structure and the Statemachines to understand the model
The GuiAdapter represents the interface to the external code. It registers its ControlPort by the external code.
Visit the initial transition to understand the registration. The actor handles the incoming messages as usual and controls the traffic lights as known from blinky.
The Controller receives the start message and controls the timing of the lights. Note that the start message will be sent from the external code whenever the REQUEST button is pressed.
Visit the model and take a closer look to the following elements:
PedControlProtocol => notice that the start message is defined as usual
Initial transition of the GuiAdapter => see the registration
The Controller => notice that the Controller receives the external message (not the GuiAdapter). The GuiAdapter just provides its port and handles the incoming messages.
Visit the hand written code => see the import statement of the protocol class and the usage of the port.
Generate and test the model
Take a look at the generated MSC => notice that the start message will shown as if the GuiAdapter had sent it.
The tutorial shows that it is generally possible to use every port from outside the model as long as the port knows its peer. This is guaranteed by describing protocol and the complete structure (especially the bindings) within the model. The only remaining question is: Why is it safe and does not violate the run to completion semantic. To answer this question, take a look at the MessageService.java from the runtime environment. There you will find the receive method which puts each message into the queue.
@Override
public synchronized void receive(Message msg) {
if (msg!=null) {
messageQueue.push(msg);
notifyAll(); // wake up thread to compute message
}
}
This method is synchronized. That means, regardless who sends the message, the queue is secured. If we later on (e.g. for performance reasons in C/C++) distinguish between internal and external senders (same thread or not), care must be taken to use the external (secure) queue.
This chapter gives an overview over the ROOM language elements and their textual and graphical notation. The formal ROOM grammar based on Xtext (EBNF) you can find here: ROOM Grammar
The actor is the basic structural building block for building systems with ROOM. An actor can be refined hierarchically and thus can be of arbitrarily large scope. Ports define the interface of an actor. An Actor can also have a behavior usually defined by a finite state machine.
Actors enable the construction of hierarchical structures by composition and layering
Actors have their own logical thread of execution
Actors can be freely deployed
Actors define potentially reusable blocks
An ActorClass defines the type (or blueprint) of an actor. Hierarchies are built by ActorClasses that contain ActorReferences which have another ActorClass as type. The interface of an ActorClass is always defined by Ports. The ActorClass can also contain Attributes, Operations and a finite state machine.
External Ports define the external interface of an actor and are defined in the Interface section of the ActorClass.
Internal Ports define the internal interface of an actor and are defined in the Structure section of the ActorClass.
Bindings connect Ports inside an ActorClass.
Example:
Graphical Notation | Textual Notation |
![]() |
![]() |
ActorClass1 contains two ActorReferences (of ActorClass2 and ActorClass3)
port1 is a External End Port. Since it connects external Actors with the behavior of the ActorClass, it is defined in the Interface section and the Structure section of the ActorClass.
port2 and port3 are Internal End Ports and can only be connected to the ports of contained ActorReferences. Internal End Ports connect the Behavior of an ActorClass with its contained ActorReferences.
port4 is a relay port and connects external Actors to contained ActorReferences. This port can not be accessed by the behavior of the ActorClass.
port5 through port9 are Ports of contained ActorReferences. port8 and port9 can communicate without interference with the containing ActorClass.
Bindings can connect ports of the ActorClass and its contained ActorReferences.
Attributes are part of the Structure of an ActorClass. They can be of a PrimitiveType or a DataClass.
Example:
A ProtocolClass defines a set of incoming and outgoing messages that can be exchanged between two ports. The exact semantics of a message is defined by the execution model.
ProtocolClasses provide a reusable interface specification for ports
ProtocolClasses can optionally specify valid message exchange sequences
Ports are the only interfaces of actors. A port has always a protocol assigned. Service Access Points (SAP) and Service Provision Points (SPP) are specialized ports that are used to define layering.
Ports decouple interface definition (Protocols) from interface usage
Ports decouple the logical interface from the transport
These symbols can only appear on the border of an actor class symbol.
Ports that define an external interface of the ActorClass, are defined in the Interface. Ports that define an internal interface are defined in the Structure (e.g. internal ports).
External End Ports are defined in the Interface and the Structure
Internal End Ports are only defined in the Structure
Relay Ports are only defined in the Interface
End Ports are always connected to the internal behavior of the ActorClass
Replicated Ports can be defined with a fixed replication factor ( e.g. Port port18 [ 5 ]: ProtocolClass1 ) or a variable replication factor (e.g. Port port18[ * ]: ProtocolClass1 )
Element | Graphical Notation | Textual Notation |
Class End Port |
![]() |
External Class End Port:
![]() ![]() |
Conjugated Class End Port |
![]() |
External Conjugated Class End Port:
![]() ![]() |
Class Relay Port |
![]() |
![]() |
Conjugated Class Relay Port |
![]() |
![]() |
Replicated Class End Port |
![]() |
External Replicated Class End Port:
![]() ![]() |
Conjugated Replicated Class End Port |
![]() |
External Conjugated Replicated Class End Port:
![]() ![]() |
Replicated Class Relay Port |
![]() |
![]() |
Conjugated Replicated Class Relay Port |
![]() |
![]() |
These symbols can only appear on the border of an ActorReference symbol. Since the type of port is defined in the ActorClass, no textual notation for the Reference Ports exists.
Element | Graphical Notation | Textual Notation |
Reference Port |
![]() |
implicit |
Conjugated Reference Port |
![]() |
implicit |
Replicated Reference Port |
![]() |
implicit |
Conjugated Replicated Reference Port |
![]() |
implicit |
The DataClass enables the modeling of hierarchical complex datatypes and operations on them. The DataClass is the equivalent to a Class in languages like Java or C++, but has less features. The content of a DataClass can always be sent via message between actors (defined as message data in ProtocolClass).
In addition to the Actor containment hierarchies, Layering provides another method to hierarchically structure a software system. Layering and actor hierarchies with port to port connections can be mixed on every level of granularity.
an ActorClass can define a Service Provision Point (SPP) to publish a specific service, defined by a ProtocolClass
an ActorClass can define a Service Access Point (SAP) if it needs a service, defined by a ProtocolClass
for a given Actor hierarchy, a LayerConnection defines which SAP will be satisfied by (connected to) which SPP
Description | Graphical Notation | Textual Notation |
The Layer Connections in this model define which services are provided by the ServiceLayer ( digitalIO and timer) |
![]() |
![]() |
The implementation of the services (SPPs) can be delegated to sub actors. In this case the actor ServiceLayer relays (delegates) the implementation services digitalIO and timer to sub actors |
![]() |
![]() |
Every Actor inside the ApplicationLayer that contains an SAP with the same Protocol as timer or digitalIO will be connected to the specified SPP |
![]() |
![]() |
Definition from Wikipedia:
A finite-state machine (FSM) or finite-state automaton (plural: automata), or simply a state machine, is a mathematical model used to design computer programs and digital logic circuits. It is conceived as an abstract machine that can be in one of a finite number of states. The machine is in only one state at a time; the state it is in at any given time is called the current state. It can change from one state to another when initiated by a triggering event or condition, this is called a transition. A particular FSM is defined by a list of the possible states it can transition to from each state, and the triggering condition for each transition.
In ROOM each actor class can implement its behavior using a state machine. Events occurring at the end ports of an actor will be forwarded to and processed by the state machine. Events possibly trigger state transitions.
For event driven systems a finite state machine is ideal for processing the stream of events. Typically during processing new events are produced which are sent to peer actors.
We distinguish flat and hierarchical state machines.
The simpler flat finite state machines are composed of the following elements:
Description | Graphical Notation | Textual Notation |
State |
![]() |
![]() |
InitialPoint |
![]() |
implicit |
TransitionPoint |
![]() |
![]() |
ChoicePoint |
![]() |
![]() |
Initial Transition |
![]() |
![]() |
Triggered Transition |
![]() |
![]() |
The hierarchical finite state machine adds the notion of a sub state machine nested in a state. A few modeling elements are added to the set listed above:
Description | Graphical Notation | Textual Notation |
State with sub state machine | Parent State
![]() ![]() |
![]() |
Entry Point | In sub state machine
![]() ![]() |
![]() |
Exit Point | In sub state machine
![]() ![]() |
![]() |