Advent of SysML v2 | Lesson 17 – Actions and Successions

Hello and welcome back to the Advent of SysML v2 – a daily, hands-on mini-course designed to bring you up to speed with the new SysML v2 standard and the modern Syside tooling that makes you productive from day one.

By the end of this lesson, you will:

  • Learn about actions and parameters
  • Learn about action decomposition and successions
  • Learn about control structures

This lesson is just a glimpse into what SysML v2 offers in terms of action modeling. We will focus on the most important aspects that can help you get started, and only mention the rest of the capabilities that might be of interest to you. Additionally, we will focus on constructs useful for textual models – skipping even basic elements like decision nodes, which are more effectively represented in graphical notation.

If you want to dive deeper into the world of SysML v2, check out The SysML v2 Book, where this topic is also discussed in more detail.

Actions and Parameters

Actions are the most fundamental elements SysML offers for behavior modeling. Actions can model processes, methods, steps – anything that can be performed. Just like parts, they can have features, which are usually either parameters or steps, sometimes attributes.

Parameters are the directed features of the action. They can be inputs, outputs, or both (depending on the direction), and there can be any number of any kind. Referential features without direction can be regarded as “local variables”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package L17_Actions {
private import ScalarValues::*;
attribute def ChildData {
attribute name : String;
attribute address : String;
attribute isNice : Boolean;
}

action def ApproachHouse {
in attribute address : String;
}
action def GetGift {
in attribute child : ChildData;
out item gift;
}
action def GetCoal {
out item coal;
}
action def DeliverGifts {
in attribute children : ChildData;
// …
}
}

The example above models the process of delivering gifts. It starts with a declaration of an attribute definition to capture the data about the children to visit. Then, lines 9, 12, 16, and 19 declare action definitions with different parameters.

An action definition is a “blueprint” for an action performance. A performance is the actual act that happens in the universe, just like an actual Car instance. We can say that an actual act was the performance of an action definition if it matches the constraints we defined. This is really no different from structure modeling. Similarly, we can model the performance of actions in a specific context (where we know the values of the parameters, for example) with action usages. We will investigate this next.

Action Decomposition and Successions

Some actions are just placeholders for some atomic performance that we no longer wish to elaborate. Other times, we want to define an action in terms of other actions. This is called action decomposition.

An action (definition or usage) can have subactions, also referred to as steps. Every composite action usage (the default is composite) represents a step. In addition to listing the steps of an action, we usually also want to specify their order. This can be done with successions. A succession is a relationship that asserts that the first action happens before the second. Here, we will show you the simple way to use successions – feel free to explore further in the specification or the book.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package L17_Actions {
private import ScalarValues::*;
attribute def ChildData {
// …
}
action def ApproachHouse {
in attribute address : String;
}
action def GetGift {
in attribute child : ChildData;
out item gift;
}
action def GetCoal {
out item coal;
}
action def DeliverGifts {
in attribute children : ChildData;
first start;
then action prepareForTakeOff;
then action takeOff;
// …
}
}

Let us start with a simple initial decomposition of the “DeliverGifts” action definition in line 16. After declaring the parameters, we start a succession chain in line 19 with the keyword “first”. The usage we refer to, “start”, is a built-in action that represents the beginning of the parent action. In line 20, we use the keyword “then” to specify the target of the first succession (that originates in “start”). This is an action usage declared in place, with no definition or body – it is just a leaf action for now. Then, in line 20, we continue the chain with another “then”, leading to the action usage “takeOff”. What is the first action here? It is always the last action usage before the “then”, or the action after the “first” keyword, so the second succession originates from “prepareForTakeOff”.

Regarding action usages, notice the parallels with structure modeling. We have declared part usages without a definition before, and we also redefined them in more concrete specializations. We can do the same thing with actions – you can try it yourself in today’s challenge.

Control Structures and Invoking Action Definitions

Let us move on to more advanced control structures. Actions are rarely completely linear – they branch, iterate, run in parallel, or invoke each other. We will look at these constructs now.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package L17_Actions {
private import ScalarValues::*;
attribute def ChildData {
attribute name : String;
attribute address : String;
attribute isNice : Boolean;
}
action def ApproachHouse {
in attribute address : String;
}
action def GetGift {
in attribute child : ChildData;
out item gift;
}
action def GetCoal {
out item coal;
}
action def DeliverGifts {
in attribute children : ChildData;
first start;
then action prepareForTakeOff;
then action takeOff;
then for child in children {
ref item toGive;
action : ApproachHouse {
in attribute :>> address = child.address;
}
then if child.isNice {
action selectGift : GetGift {
in attribute :>> child;
}
then assign toGive := selectGift.gift;
} else {
action selectCoal : GetCoal;
then assign toGive := selectCoal.coal;
}
then fork;
then deliverItem;
then drinkMilk;
action deliverItem {
in item = toGive;
}
then j;
action drinkMilk;
then j;
join j;
}
then action returnHome;
}
}

You can see the complete example above. In line 24, a for loop iterates through the list of child data specified as an input parameter in line 19. 

In the body of the loop, we see an action usage in line 26 that has a definition but no name: “ApproachHouse”. It is common in action models to leave the name empty when we “invoke” an action definition, but it only works if we never want to refer to that action again. When “DeliverGifts” is executed, we must execute “ApproachHouse” as the first step of each iteration, with the input set to the “address” field of the current child data.

In the next step, in line 29, we encounter an if action. If actions can be used to branch execution based on a guard condition given immediately after the “if” keyword. If actions always have a then clause, and may have an else clause after the “else” keyword, which can be another if action to have an arbitrary number of branches.

Inside the branches, we can see two more action usages with definitions in lines 30 and 35. These actions have an output parameter, and our intent is that their output should be delivered to the child in a later step. To achieve this, we will store either of the outputs in the referential item usage “toGive” with assignment actions.

You can see the assignment actions in lines 33 and 36. In their simplest form, assignment actions consist of a feature to assign to, followed by the “:=” symbol, and then an expression whose value will become the new value of the left-hand side.

The last thing we will learn today is modeling concurrent behavior. In line 38, you can see a fork action (without a name). Fork actions represent a point from which multiple “threads” will execute concurrently until they are joined. Technically, this is realized by a slightly more complex pattern. Right after the fork, we have to list the first steps of each thread. But notice the absence of the “action” keyword – these are not action declarations, only references to other action usages! Indeed, the actual action usages are declared later, in lines 42 and 46. The reason for this is the rules of the shorthand succession notation we have already discussed: the source of the succession declared with the “then” keyword is always the last action usage. In this case, it will be the fork action in both lines 39 and 40 – but only because neither of them is an action usage declaration, only a reference.

We can model the “threads” like before with a succession chain. Ultimately, each thread should proceed to a join action, which is declared in line 48 with a name and referenced in lines 45 and 47.

Note that concurrency is not necessarily parallelism – it just states that two or more “threads” execute independently. This can mean anything in between arbitrary ordering and true parallel execution. Our model here states that drinking the milk and delivering the item (a gift or coal) happen independently. 

With this, we have defined the actions of one iteration. After closing the body of the for loop, we model one more action: “returnHome”. Mission accomplished.

Challenge for Tomorrow

Modeling actions takes a lot of practice in SysML. Let’s start right away with today’s challenge. Head over to Syside Cloud and do the following tasks:

  1. Create the action definition “PrepareForTakeOff”. It should model how the cargo of the sleigh is assembled and loaded.
    • First, we need to count the number of gifts and coal chunks to package based on the list of child data, which should be an input parameter. Use a for loop and assignment actions.
    • Then, we need to load the gifts and the coal chunks. Model this with two concurrent actions that take the number of gifts and coal chunks as input.
    • Finally, Santa boards the sleigh, and the preparations are ready.
  2. Specialize the “DeliverGifts” action definition and redefine the “prepareForTakeOff” action with a definition of “PrepareForTakeOff”, binding its input to the “children” usage.

Summary

In this episode, you dipped your toes into action modeling in SysML v2. This is just the beginning – as you continue your exploration of SysML, you will encounter send and accept actions, other kinds of loops, decision and merge nodes, terminate actions, flows, and succession flows, among many other elements. We hope that this quick-start guide will help you become proficient in action modeling. As the next step on this path, it is now time to go to Syside Cloud to do the challenge. Don’t forget to come back tomorrow for another episode of the Advent of SysML v2!

Cookies