Step 2: Migrate Scenario-related Java classes into an EMF datamodel

EMF Datamodel overview

In the newly created desktop application, the EMF technology is used to keep the scenario data. The description of data structure (classes, fields, and relations) is kept in the 'datamodel.ecore' file. Find this file in the 'datamodel' bundle (see 'model' folder) and open it in Eclipse for editing.

Initial scenario data model

Scenario is the root class. A (singleton) Scenario object 'contains' all other objects. Node, Arc, and Point are the classes whose instances form a transportation graph.

The 'composition' relation between two objects is usually represented by a collection-like field in the container object and (optionally) a back-reference field in the contained object. For example, an Arc object 'contains' its Point objects using the 'Arc.points' field, and each Point object also 'knows' its containing Arc object (referred to by the 'Point.arc' field). EMF helps to keep this containment consistent.

A note on ID fields

Each class in the EMF model will have an ID field (its name is 'id', its type is 'EString', and its 'ID' property is set to 'true'). If the parent class already has such a field, the child class will not create its own one.

Why do we need it?

Recall that we will save our scenario into an Excel file, where instances of each class will reside in a separate sheet. To store the relations between objects of different types, a column will be added that has to somehow refer to another object. For example, when we store an Asset, we will need to refer to its Node. A natural way to achieve it is to use the relational databases approach: add the 'id' field in the 'Node' sheet and add the 'node_id' field in the 'Asset' sheet to refer to the Node.

The values in ID fields are typically chosen to be of some integral type, an enumeration type, or a string. These types are preferred over floating-point numbers for their (in)equality comparison convenience.

In our new scenario data model, we will use String-valued id fields for new EClasses.

Assets

In Part 2 we described the scenario data model using the plain Java classes that we placed inside the tutorial.scenario package. Recall that these datamodel classes only described data and contained no business logic.

We will now migrate this scenario data structure description into the EMF datamodel of our newly generated application.

Create a new abstract 'Asset' EClass:

  • In the 'datamodel.ecore' file editor, right-click on the 'datamodel' item and select the 'New Child' → 'EClass' menu item. A new datamodel class is created.

  • In its properties, set its 'Abstract' property to 'true', set its 'Name' to 'Asset'.

Add an 'id' EAttribute to the 'Asset' EClass:

  • In the context menu of the Asset datamodel class, choose 'New Child' → 'EAttribute'.

  • Set the new attribute’s 'Name' property to 'id', set the 'EType' to 'EString', and the 'ID' property to 'true.

Add a 'name' EAttribute to the 'Asset' EClass:

  • Add another EAttribute to the Asset datamodel class.

  • Set its name to 'name', and choose 'EString' as attribute’s EType.

Add a 'node' EReference to the 'Asset' EClass:

  • In the context menu of the Asset datamodel class, choose 'New Child' → 'EReference'.

  • Set the newly created field’s name to 'node' and its 'EType' to 'Node'.

So, we have just created a unidirectional connection between nodes and assets: an asset 'knows' its node (i.e., the transport network node where the asset resides), but a node 'does not know' its assets.

Warehouses and Stores

Creating other EClasses and fields therein is similar to what we have done to the Asset EClass already.

Create a new datamodel class. Set its 'ESuper Types' to contain one item - the Asset class that we have created before. Set the name of the new EClass to 'Warehouse'.

Create the Store datamodel class in the same way: it inherits from the Asset class and has the name 'Store'.

Scenario EClass modification

In Part 2, the Scenario Java class contained collections of Warehouses and Stores. Let’s do the same thing using EMF.

  • Add a new EReference attribute to Scenario datamodel class: name 'warehouses', EType 'Warehouse → Asset', 'Containment' set to 'true', and 'Upper Bound' set to -1. This is a containment field: now the Scenario object 'contains' warehouse objects.

  • In the 'Warehouse' class, add a new EReference 'scenario' with EType 'Scenario' and EOpposite 'warehouses'. Now each Warehouse object 'knows' its containing 'Scenario' object. Making the relation between a Scenario and a Warehouse two-way minimizes the number of sheets in the Excel file where the whole Scenario can be saved: now we will not need a separate sheet to map Scenario objects to their Warehouse objects. Instead, there will be a 'scenario' field in the 'Warehouse' sheet of Excel file that will contain the ID of the respective 'container' Scenario object. Moreover, since there will be only one Scenario object, this 'Warehouse.scenario' field in Excel file will not be created at all.

  • Use the pattern above to add a new containment field to Scenario datamodel class named 'stores', and add the respective 'scenario' EReference field in the Store EClass.

Note that we do not add a Scenario field that contains trucks' speed here, since this time we are going to describe each truck separately.

Trucks

Open the 'datamodel.ecore' file and create a new EClass called Truck.

Add new fields to the Truck EClass:

  • EAttribute 'id' (similar to `Asset’s id);

  • EAttribute 'name' (similar to `Asset’s name);

  • EAttribute 'speed' with type 'EDouble';

  • EReference 'initialNode' with type 'Node'.

Add a new containment field to Scenario EClass named 'trucks', and add the respective 'scenario' field in the Truck EClass.

Finally, create a new maxDeliveryTimeHrs EAttribute with type EDouble to the Scenario datamodel class.

Save the modified 'datamodel.ecore' file.

Check if you have the data model similar to this one:

Datamodel with Scenario

Generating datamodel Java classes

With EMF, it is easy to map datamodel classes (described in the 'datamodel.ecore' file) to Java classes.

In the 'datamodel.genmodel' file (located next to the 'datamodel.ecore' file) editor, right-click the child Datamodel element and choose 'Generate Model Code'.

It might take a few seconds to generate the Java classes.

You can find the generated files in the 'src-gen' folder of the 'datamodel' bundle.

Each time you change the 'datamodel.ecore' file, these Java classes should be generated to reflect the changes.

Random values in the datamodel

The need for random variables

The console application created in Part 2 generates requests at random moments of model time. There is a special field intervalBetweenRequestsHrs in the Scenario Java class that contains the mean interval between consecutive requests; then, exponential distribution is used to produce these random intervals.

In the desktop application, we are going to keep not only the mean value of the interval length, but also the random value distribution type: it could be exponential, or uniform, or any from the list of available types.

The solution provided by the Amalgama Platform is as follows:

  • In the EMF datamodel, a field is declared with generic 'Distribution' type.

  • While editing the scenario data (not the datamodel), the user puts a 'concrete' distribution object in that field (Amalgama Platform contains ECore classes for several commonly used types of random distributions), thus defining both the distribution type and all its settings.

With this approach, we do not need to modify the datamodel each time the user wants to use another distribution type.

Using an external datamodel

To enable usage of 'Distribution'-type fields in the datamodel:

  1. Go to the 'datamodel.ecore' editor.

  2. In the topmost tree item’s context menu choose the 'Load Resource…​' item.

  3. Then click 'Browse Target Platform Packages…​', select the 'https://amalgamasimulation.com/randomdatamodel' package in the list, and click OK.

  4. Click OK in the "Load Resource" window.

You should now see a new 'randomdatamodel.ecore' item in the 'datamodel.ecore' editor:

Randomdatamodel added

This means you can reference the datatypes therein to create fields in your datamodel that describe value distribution.

Adding Distribution-type field

Add a new intervalBetweenRequestsHrs EReference of EType Distribution to the Scenario EClass. Set the field’s 'Containment' property to true.

Save the 'datamodel.ecore' file.

Here is how the Scenario EClass should look now:

Scenario EClass

We also need to update the 'datamodel.genmodel' file so that it references the 'randomdatamodel' in a correct way. We will only need to do it once when we add the first 'Distribution'-type field.

  1. Open the 'datamodel.genmodel' file (located next to the 'datamodel.ecore'). There is the root element 'Datamodel' and three child elements: 'Datamodel', 'Randomdatamodel', and 'Ecoreutils'. Right-click the root Datamodel element and press 'Reload…​' menu item.

  2. In the 'Select a Model Importer' window that opens, select 'Ecore model' and press 'Next'.

  3. In the next screen 'Ecore Import', press 'Next' again.

  4. In the 'Package Selection' screen make sure that only datamodel is selected in the 'Root packages' list. Select all items in the 'Referenced generator model' list (in the bottom of the screen). Press 'Finish'. If a 'File Conflict' question appears, answer 'Yes'.

This is how the 'datamodel.genmodel' file should look now:

The datamodel.genmodel file after an update

Note that both 'Ecoreutils' and 'Randomdatamodel' items have similar icons with small arrows meaning that these datamodels are 'referenced'.

Generate model code using the 'datamodel.genmodel' file.

Check the result

Let’s check that the datamodel is correct. Here is what you should now have in the 'detamodel.ecore' file:

Updated scenario data model

Note that the datamodel in the picture above contains EClasses that are almost identical in their structure to the respective tutorial.scenario.* Java classes created in Part 2.

One key difference is that now the Scenario.intervalBetweenRequestsHrs field is now a Distribution, not a Double constant.

Start the desktop application (use '..product' file in 'releng/…​product' folder). Since we did not introduce any breaking changes to the datamodel, the application should start without any issue.

Now that the datamodel is ready, let’s move on and help the user to edit the whole scenario from within the application.