User Tools

Site Tools


v070:userguide

User Guide for version 0.7.0

Release notes, reporting bugs

Installation

Sample models

For a quick start we recommend experimenting with the sample models.

  • Download and unzip the sample models.
  • Import them into your Eclipse workspace (File / Import… / General / Existing Projects into Workspace).
  • Clean and build the projects (Project / Clean…).

Sample models are implemented using either XtxtUML syntax (see the source packages <name of example>.x.model) or JtxtUML syntax (see the source packages <name of example>.j.model), or both. You can try the provided examples by running the accompanying Demo.java files. In addition, most models come with diagrams you can visualize (folder diagrams), tests you can check the model against (folder tests), and configuration and main files you can use to export and run the model in C++ (folder cpp).

In this version of txtUML, sample models are packaged together with the txtUML standard library. Currently, it contains predefined models for timing, doing advanced math calculations and communicating with the outside world. If you want to use its features, you have to manually import the downloaded hu.elte.txtuml.api.stdlib project into the modeling workspace.

We suggest reading the Generating diagrams and the Running and debugging models sections as the next steps of experimenting with the sample models.


Creating own models

New txtUML project

txtUML models should be placed in txtUML projects. A new txtUML project can be created by selecting File / New project… / txtUML / txtUML Project and setting the project name.

By default, the project will be created in the current workspace. In order to override this, uncheck the Use default location checkbox and select a location for the new project. If you want to use the features of the txtUML standard library (currently: timing, doing advanced math calculations and communicating with the outside world), you can check the Add hu.elte.txtuml.api.stdlib to the classpath checkbox to automatically set the classpath of the new project accordingly. Please note that even in this case, you still have to make sure the standard library is present in the workspace afterwards.

New txtUML model

Select File / New / Other… / txtUML / txtUML Model.

Select a Source folder from an existing project for the new model. Select an existing Package from that folder or type a new Package name. Type a Name for the new model.

Select the syntax of the new model:

  • XtxtUML for custom modelling syntax.
  • JtxtUML for Java syntax.

Both XtxtUML and JtxtUML models can be connected with Java code, can be run and debugged, and used as a source for UML2 model generation.

A txtUML model is a package with

  • either a package-info.java file (in case of JtxtUML), where the package has an annotation of the form @Model(“ModelName”)1),
  • or a package-info.xtxtuml file (in case of XtxtUML), which has a model declaration of the form model-package example.x.model as “ModelName”;.

All files in this package (and its subpackages) are part of the model. The wizard described above creates one of these files depending on the XtxtUML/JtxtUML selection.

New model elements

For XtxtUML syntax, select File / New / Other… / txtUML / XtxtUML File. Fill in the source folder and package to place the new source file in, then enter a file name. You can also choose between the two possible extensions: .xtxtuml or .txtuml.

For JtxtUML syntax, select File / New / Class to create a new Java class.


Modeling language

See the Modeling Language Description to study the txtUML language both in Java syntax (JtxtUML) and in custom syntax (XtxtUML). In case of JtxtUML, the JavaDoc of the API can also be used.


Generating diagrams

It is possible to generate EMF-UML2 models together with either JavaScript2) or Papyrus diagrams from txtUML models.

Currently class and state machine diagrams can be generated. Content and layout of the class diagrams and flat state machine diagrams can be defined by textual diagram descriptions. (Support for hierarchical diagrams is coming in a later release.)

The following simple example assumes classes A, B, C and D in the model. We create a class diagram where classes A, B and C are in a row, and class D is below B. Diagram definitions can be written using a Java API. See the Diagram Language Description for a comprehensive guide.

Our example diagram can be defined as follows:

public class ExampleDiagram extends ClassDiagram {
  @Row({A.class, B.class, C.class})
  @Below(val = D.class, from = B.class)
  class ExampleLayout extends Layout {}
}

To generate diagrams, select either Generate JavaScript diagrams from txtUML or Generate Papyrus diagrams from txtUML from the menu bar.

Diagram descriptions are grouped by projects. You can select several descriptions – if the descriptions are related to different models, a separate diagram group will be generated for each individual model. In our example, there is only one selected.

Generating JavaScript Diagrams

After choosing Finish, a folder named js is generated with the needed contents and the class diagram is automatically opened in your default browser.

The JavaScript user interface

The diagram will look something like this in your browser:

The Graphical User Interface consists of the diagram itself and three panels. Each panel can be collapsed and expanded by clicking on its label.

The panels contain the following controls:

Diagnostics:

  • a switch for toggling diagnostics mode
  • input for the port where diagnostics information is provided
  • see section Animation in JavaScript for details

Diagram list:

  • a list of all class and state machine diagrams available
  • switch the displayed diagram by clicking on one in this list

Controls:

  • controls for the displayed diagram
  • + and - zooms in and out, respectively
  • Reset view resets the diagram into its original position and magnification
    • modifications to the diagram remain
  • Edit/Pan toggles whether dragging with the mouse moves the whole diagram, or only individual elements of it
  • Wheel zoom:
    • if switched on, the mouse wheel zooms in and out, centered on the cursor
    • if switched off, the mouse wheel scrolls the page

As mentioned above, you can also use the mouse to pan the diagram around when the Edit/Pan switch is set to Pan, or edit elements of the diagram when it is set to Edit. Zooming is also possible with the mouse wheel while the Wheel zoom button is toggled on.

Generating Papyrus Diagrams

After choosing Finish, a Papyrus model is generated with the following class diagram:

Generating Diagrams from a context menu

Diagrams can be generated from a context menu as well, either in the Project Explorer or in the Package Explorer. Simply right click on a diagram description .java file (you can select several descriptions too) and choose the corresponding entry.

As a result, a wizard opens with the chosen diagrams preselected.


Running and debugging models

The following hold both for XtxtUML and JtxtUML.

Executing models

txtUML models can be run as Java applications. The simplest way to run a txtUML model is to implement the Execution interface, whose sole abstract method can be used as the initialization() of the model execution. That is, it should contain the model code which creates, links and starts the model class instances that should exist at the beginning of the execution.

After the initialization() method is implemented, there are three possible ways to run the execution:

  • instantiate the implementor class and call its run() method, or
  • call the static run(Class) method with the implementor class as its parameter, or
  • let the Execution interface be the main type of a Java program and provide the fully qualified name of the implementor class as a command line argument to it.

In the simplest case, a Java class that solely executes a model would look like this:

public class Tester implements Execution {
  @Override
  public void initialization() {
    MyClass instance = Action.create(MyClass.class);
    Action.start(instance);
    Action.send(new MySignal(), instance);
    // ...
  }
 
  public static void main(String[] args) {
    new Tester().run();
  }
}

For a bit more complicated cases, other methods of the Execution interface might be overridden as well. The before(), during() and after() are executed before, in parallel with, and after the model execution, respectively. The name() method can be used to give a name to the execution which will then appear in logs. The configure(Settings) method makes it possible to alter the basic settings of the execution. See the documentation of the Execution.Settings class for details about these settings.

It is important to know that models always run on their own thread(s) – created and managed by the model executor – and not on the thread on which you create the execution/executor instance. From the previously mentioned methods of the Execution interface, only initialization() runs on the same thread as the model.

A more complex example using several features of the Execution interface:

public class Tester implements Execution {
  MyClass instance;
 
  @Override
  public String name() {
    // this name will appear in logs
    return "SampleExecution";
  }
 
  @Override
  public void configure(Settings s) {
    // set the log level
    s.logLevel = LogLevel.TRACE;
  }
 
  @Override
  public void before() {
    // on the main thread
    // acquire resources...
  }
 
  @Override
  public void initialization() {
    // on the executor thread
    instance = Action.create(MyClass.class);
    Action.start(instance);
    Action.send(new MySignal(), instance);
    // create, link, start, send...
  }
 
  @Override
  public void during() {
    // on the main thread
    // communicate with the model from outside...
    while (/* ... */) {
      API.send(new MySignal(), instance);
    }
  }
 
  @Override
  public void after() {
    // on the main thread
    // release resources...
  }
 
  public static void main(String[] args) {
    new Tester().run();
  }
}

Note that outside the initialization code, we used the API.send method instead of Action.send. This is important, as only the methods provided by the API class are thread-safe and therefore only these should be used from outside model code. Calling methods and accessing fields of model objects outside model code is also unsafe.

For even more sophisticated cases, the start() method can be called instead of run() which lets the execution run indefinitely but returns a special Execution.Handle object which provides access to the (base) model executor that runs the execution (for external libraries to use) and also a method which initiates and awaits the termination of the execution.

Note that the run() and start() methods of this interface are not intended to be overridden.

If the features provided by this interface are not satisfactory, please use the more detailed and advanced ModelExecutor interface (which is used by the run() and start() methods of this interface as well).

Debugging

Switch to Java or Debug perspective and create a new run/debug configuration. Use Java Application type if you want to run or debug the model only in text. Use txtUML Application type if state machine animation is required as well.

Breakpoints can be created and managed the same way as for Java programs. The standard debug controls (stop, pause, resume, step, step-into) work as usual.

The variable view can show the current signal, current state, associations and the attribute values of the actual object.


State machine animation

txtUML can animate state machine diagrams generated by the txtUML visualization process. During the animation, the current state and currently executed transition gets highlighted. Make sure that the run/debug configuration is of txtUML Application type.

For each state machine diagram, the state changes of the first activated object of the corresponding type will be highlighted. An expected later improvement will make it possible to select the object to be animated during the debug session.

Animation in JavaScript

Open the generated diagram in your preferred browser. You can find the debugging control in the top-left corner.

Start the model either in run or in debug mode. A message will appear on the console with the ports through which the diagnostics information will be provided.

Copy the HTTP port number into the Diagnostics port input in your browser and switch on diagnostics.

If there is no debugging information available, a warning will be displayed. Once the connection is (re-)estabilished, the animation will begin/resume.

Animation in Papyrus

Open the generated Papyrus diagram and start the model either in run or in debug mode. The current state and currently executed transition gets highlighted.


Testing with sequence diagrams

See the Sequence Diagram Language Description to study how to define sequence diagrams.

txtUML models can be tested with sequence diagrams. You can run the model in parallel with a sequence diagram and check if the signals in the model are being sent in the same order you specified on the diagram. You can also assert states of the model objects.

Sequence diagrams can be run with a sequence diagram executor. During the execution, each time a signal is processed, the executor checks whether the next signal in the sequence diagram is equal to the processed signal. If the signals differ, an error is logged. Signals in fromActor expectations are actually sent by the sequence diagram executor, while signals between objects are only checked.

The following example assumes classes A, B, C, one instance of each class as a, b, c and a Signal class called Sig. The first signal in the diagram is from the actor to a. The second is from a to b and the third is from b to c. At the end of the execution, we expect object c to be in state Finished (a state of class C).

public class ExampleSequenceDiagram extends SequenceDiagram {
  @Position(1)
  A a;
 
  @Position(2)
  B b;
 
  @Position(3)
  C c;
 
  @Override
  public void initialize() {
    a = Action.create(A.class);
    b = Action.create(B.class);
    c = Action.create(C.class);
 
    Action.link(AToB.a.class, a, AToB.b.class, b);
    Action.link(BToC.b.class, b, BToC.c.class, c);
 
    Action.start(a);
    Action.start(b);
    Action.start(c);
  }
 
  @Override
  public void run() {
    Sequence.fromActor(new Sig(), a);
    Sequence.send(a, new Sig(), b);
    Sequence.send(b, new Sig(), c);
    Sequence.assertState(c, C.Finished.class);
  }
}

Generating sequence diagrams

To generate sequence diagrams, select txtUML and Generate sequence diagrams from txtUML in the menu bar.

After clicking Finish, a PlantUML diagram is generated.

Running and generating sequence diagrams from the context menu

You can run a sequence diagram by right clicking on its .java file in the project explorer or the package explorer and selecting Run sequence diagram. The result will be printed out in a console in Eclipse.

A PlantUML diagram can be generated by selecting Visualize sequence diagram….

As a result, a wizard opens with the chosen diagrams preselected.

Running sequence diagrams from code

There is also a possibility to run a sequence diagram from code, as the following example shows.

public class Tester {
  public static void main(String[] args) {
    SequenceDiagramExecutor executor = SequenceDiagramExecutor.create();
    executor.setDiagram(new ExampleSequenceDiagram());
    executor.run();
    executor.getErrors().forEach(error -> System.out.println(error.getMessage()));
  }
}

Compilation to C++

To generate C++ code from a txtUML model, you have to define a deployment configuration. A deployment configuration is a description of how model objects will be distributed across different threads. The deployment configuration is a special class which is derived from the Configuration base class. The model classes can be grouped together and these groups can be configured as described below. The events that arrive for classes which belong to the same group will be served by a configured thread pool.

You can group by the help of annotation Group which contains the following configuration options:

  • contains: You can enumerate the classes which belong to this group.
  • constant: It determines how many threads will be created regardless of the number of object instances. It must be a natural number. Its default value is 1.
  • gradient: It determines how many threads will be created (in addition to the number of constant threads) depending on the number of objects. It represents a linear coefficient, that is, the number of additionally created threads is gradient ⋅ n, where n is the number of created objects. It can be a real number between 0.0 and 1.0. Its default value is 0.
  • max: It determines how many threads will be created at most. It must be a natural number. Its default value is determined by the value of constant.

If there are classes without a group assigned to them, a default implicit group will be created which contains all of these classes. It will be configured with the default values shown above. Please note, however, that – due to technical limitations – a configuration should contain at least one group definition with at least one class in it.

Additionally, you can specify whether you want the exported model to be executed by a single threaded runtime instead of the default multithreaded one. This can be achieved by placing the @Runtime(RuntimeType.SINGLE) annotation on the configuration class. In this case, grouping is effectively ignored.

Configuration examples

@Runtime(RuntimeType.SINGLE)
@Group(contains = {A.class})
class ExampleConfiguration extends Configuration {}

This means that the single threaded runtime will be used. Here, grouping has no effect, but we defined one group with a class in it nevertheless, as this is currently required due to technical limitations.

@Group(contains = {A.class, B.class}, max = 10, constant = 2, gradient = 0.5)
@Group(contains = {C.class})
class ExampleConfiguration extends Configuration {}

In this example, the default multithreaded runtime is used. Instances of classes A and B are served by the same thread pool, which contains two constant threads plus one for every 2 A or B instances created, but no more than 10. Instances of class C are served by another thread pool and it contains only one thread (according to the default values).

We suggest reviewing the deployment configurations in the demo projects.

Generating C++ from txtUML

The C++ model compiler can be reached by selecting the txtUML / Generate C++ code from txtUML menu.

The txtUML deployment configuration must be specified. For convenience, you can also select whether you want the default C++ model execution runtime to be copied next to the generated files.

When you specified the deployment configuration, the Next button is going to be clickable. When you click the Next button, you will be led to a new page where you can set additional compilation options.

Firstly, you can select a new main.cpp file to be placed in the model-specific output folder right away. If a main file already exists in this folder, it will be overwritten.

On the second line, you can select build environments you want the generated files to be prepared for. Please note that in this case, CMake and the selected build environments should be installed in your system. See section Building the generated code for additional details.

In case of a successful export, the generated C++ code is saved in a folder named after the fully qualified identifier of the exported model, placed inside the cpp-gen folder of its enclosing project. Moreover, if you have specified build environments as well, additional files related to the environments are created in separate, build_ prefixed subfolders. Note that you might have to refresh the project in Eclipse, so that the newly generated files become visible in the workspace.

Building the generated code

Compiling the exported C++ files can be achieved in multiple ways.

  • If you selected specific build environments during the code generation phase, you can conveniently use these environments on the automatically prepared files placed in the build_ prefixed output folders.
  • Using the generated CMakeLists file, you can create these environment-specific “make files” manually, which then you can feed to the chosen build environments.
  • If all else fails, you can also build from the generated files with any C++ compiler manually.

In the remaining part of this section we elaborate the CMake-based, semi-automatic compilation method. CMake is available from https://cmake.org/. It is recommended to create a new folder next to the generated files, where the build environment should be created. The compilation can be performed by the following command:

cmake -G <environment> -D CMAKE_BUILD_TYPE=<type> <path>

Where the parameters mean the following:

  • <environment>: The chosen build environment. You can use the cmake --help command to list the possible build environments.
  • <type>: The type of the build. Can be Debug or Release.
  • <path>: The relative path to the generated CMakeLists file.

A concrete example:

cmake -G "MinGW Makefiles" -D CMAKE_BUILD_TYPE=Release ..

C++ model execution runtime

The C++ model execution runtime is the C++ counterpart of the Java-based JtxtUML model executor. The runtime library consists of pre-written .cpp files so they can be used for other generated models too.

Using the C++ runtime, a concrete model execution is represented by a function. In this function, at first you have to initialize the execution environment with the Env::initEnvironment() call, for which you have to include Env.hpp. After this you can get a handle for the used runtime instance by calling UsedRuntimeType::getRuntimeInstance() from deployment.hpp, where the type of the returned pointer is UsedRuntimePtr. Depending on the runtime type specified in the source deployment configuration, the flow of execution is the following:

  • Single threaded runtime (RuntimeType.SINGLE): The execution is carried out sequentially once startRT() is called on the runtime instance. The execution stops when there are no more signals in the processing queue. Function startRT() is blocking, it returns when the execution terminates. As a result, initialization should happen before starting the runtime.
  • Multithreaded runtime (RuntimeType.THREADED): The execution runs in parallel with the initialization once startRT() is called on the runtime instance. In this case, this call is not blocking. You have to manually specify if you want to terminate the execution by calling stopUponCompletion() on the runtime instance.

In the following we present concrete usage examples, but you can also consult the C++ Runtime API documentation and the main.cpp files provided with the demo models.

Warning! When the generated model is used by an external component, the model objects should be allocated on the heap. The reason for this is that externally created objects can be deallocated inside the model. Please note that the main.cpp files provided with the demo models only allocate objects in the stack to keep the examples as simple as possible – the place of allocation should be considered carefully in real-life projects.

Referring to exported entities

The fully qualified names of source model entities are flattened into namespace Model in the exported files. For example, a source model class named A should be referred to as Model::A, regardless of its original fully qualified name. Depending on the kind of the source element, referencing can be done in the following ways:

  • Classes: A source class named A can be referred to as Model::A in the execution initialization function. In this case, A.hpp has to be included.
  • Ports: A source port named A.P on class instance a can be referred to as a.P in the execution initialization function. In this case, A.hpp has to be included.
  • Signals: An instance of a signal named S can be referred to / constructed as Model::SPtr(new Model::S_EC(<ctor args>)) in the execution initialization function. In this case, EventStructures.hpp has to be included.
  • Association, composition and connector ends: A source association/composition/connector end named A.e can be referred to as Model::A.e in the execution initialization function. In this case, AssociationInstances.hpp has to be included.
Action language

The action language provided in C++ is analogous to the original Java version (see Action code for more detailed semantics). To use the Action::-prefixed language elements below, you have to include runtime/Action.hpp.

Action::send(sig, obj);
Action::start(obj);
Action::deleteObject(obj);
Action::link(AB.a, instanceOfA, AB.b, instanceOfB);
MultipliedElement<B, 0, -1> bs = instanceOfA.assoc(AB.b);
  // MultipliedElement<T, L, U> means:
  //   a collection of Ts with multiplicity L..U
  //   where infinity is represented by (-1)
  // include 'runtime/ESRoot/Elements.hpp' for direct use
delegateConnect(instanceOfA.PortAP, Conn.bpe, instanceOfB.PortBP);
assemblyConnect(Conn.ape, instanceOfA.PortAP, Conn.bpe, instanceOfB.PortBP);
  // include 'runtime/PortUtils.hpp' for direct use
1) Fully qualified name: hu.elte.txtuml.api.model.Model
2) Based on JointJS.
v070/userguide.txt · Last modified: 2018/10/08 22:31 by djnemeth