States#
Role in SysML v2#
A state definition is a kind of action definition that defines the conditions under which other actions can execute. A state usage is a usage of a state definition. State definitions and usages are used to describe state-based behavior, where the execution of any particular state is triggered by events.
State machines can only be added to parts.
Supported features are:
- State machine definition:
state def sm { ... } - Default state:
entry; then statename; - Default state selection, extended form: allows selecting the default state based on conditions.
- States, long form: may have
entry,exit, anddoactions. See OperationalRed{ entry; ... }in the listing. - States, short form: only a name is required.
- Transitions, short form: must immediately follow a state (see
accept after ... then ...in the listing). - Transitions, long form: use the extended form. See transition t1 in the listing.
- Transitions triggerd by boolean conditions or time durations (note: no support for ‘at’ yet)
- History (via naming convention of state names: for shallow history use S1**_H**; for deep history use S1**_HH**)
- Final
The system described blow is a small trafflic light system. There is one central controller controlling traffic lights spread over a city as example.
The two parts communciate via a ports which are connected in the system configuration.
private import ScalarValues::*;
package TrafficLight {
// definition of events to control TrafficLightController
enum def TLCEvent {
evOperational;
evError;
}
//@TrafficManagementCenter
enum def TMCEvent {
}
item def PortData{
attribute msg : TLCEvent;
}
port def ControlPortData {
in item data:PortData;
}
part def Test{}
// traffic management center
part def TrafficManagementCenter{
out port sendPort : ControlPortData;
attribute msg:TMCEvent;
state def tmcStateMachine {
entry; then PreOperational;
state PreOperational;
accept after 2[SI::second] do send TLCEvent::evOperational via sendPort then Operational;
state Operational {
}
}
}
part def TrafficLightController{
attribute redtime:Integer default 1;
attribute msg:TLCEvent;
in port recvPort : ~ControlPortData;
action def getMsg{
action accept m:PortData via recvPort{
assign msg:=m.msg;
}
}
action def setRedLED{}
action def resetRedLED{}
action def setYellowLED{}
action def resetYellowLED{}
action def setGreenLED{}
action def resetGreenLED{}
action def setRedAndYellowLED{}
action def resetRedAndYellowLED{}
state def tlcStateMachine {
entry; then OutOfService;
do action getMsg{}
state OutOfService {
entry; then OutOfServiceYellowOn;
state OutOfServiceYellowOn{
entry action setYellowLED{}
}
accept after 0.5[SI::second] then OutOfServiceYellowOff;
state OutOfServiceYellowOff {
entry action resetYellowLED{}
}
accept after 0.5[SI::second] then OutOfServiceYellowOn;
transition t1 first OutOfService accept when msg==TLCEvent::evOperational then Operational;
}
state Operational {
entry; then OperationalRed;
state OperationalRed {
entry action setRedLED{}
exit action resetRedLED{}
}
state OperationalRedYellow {
entry action setRedAndYellowLED{}
exit action resetRedAndYellowLED{}
}
state OperationalGreen {
entry action setGreenLED{}
exit action resetGreenLED{}
}
state OperationalYellow {
entry action setYellowLED{}
exit action resetYellowLED{}
}
transition t1 first OperationalRed accept after redtime[SI::second] then OperationalRedYellow;
transition t2 first OperationalRedYellow accept after 1[SI::second] then OperationalGreen;
transition t3 first OperationalGreen accept after 1[SI::second] then OperationalYellow;
transition t4 first OperationalYellow accept after 1[SI::second] then OperationalRed;
transition t5 first Operational if msg==TLCEvent::evError then OutOfService;
}
}
}
// Main system composition
part def TrafficLightSystem {
doc /*
* Main system that contains the parts
*/
part tmc : TrafficManagementCenter;
part tlc : TrafficLightController;
// Connect the parts
connect tmc.sendPort to tlc.recvPort;
}
}The system, including the state machines, can then be fully translated into executable C++ code and follows the well-known Sinelabore RT state machine structure.
Transition triggered by timeouts#
A great new feature with SysML v2 is the ability to trigger transitions by timeouts. In UML this was also possible, but in a much more limited way.
transition t1 first start state accept after 0.1[SI::second] then next state;At present, time can only be specified in seconds. For other durations, use a value in seconds—for example, 0.001[SI::second] for 1 millisecond. The code generator emits the required code to manage duration triggered transitions. The only requirement is to execute the state machine method of the part cyclically (see ‘main.cpp’ below).
Executing a model#
For each state machine, a state-handling method with the same name as the topmost state def of the part is generated. Set the following parameters in codegen.cfg; otherwise the compiler will fail:
UseEnumBaseTypes=yesUseStdLibrary = yesCallInitializeInCtor = noEnumBaseTypeForEvents = std::int16_t
It is intended to execute the model directly in a C++ main function. Usually only minimal code is required to do so. In some cases you may want to add your own C++ behavior. Then subclass the generated Part classes and implement the intended business logic and handlers (i.e., methods of the generated part classes). By default, process() and init() methods are generated per part.
If needed, override these two methods and add your own code. Then call the base-class method to ensure
that the part is initialized correctly. Sub-parts must be created in the init method.
Here is an example of a simple main function:
using namespace TrafficLight;
// --- Main ---
int main() {
TrafficLightSystem tls;
//setup subcomponents
tls.init();
// init state machine
tls.tmc->initialize();
tls.tlc->initialize();
// Start process thread
for ( int i = 0; i < 80; i++ ) {
tls.tmc->tmcStateMachine();
tls.tlc->tlcStateMachine();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
std::cout << "[Main] Done.\n";
return 0;
}The complete code of this example is available on our GitHub site.