Have you ever thought it’d be nice to generate C code from a state machine which you can actually use in your application?
There are, of course, commercial and high sophisticated code generators for various languages out there which you can buy, and in general there’s nothing to say against them. Sometimes, though, you might need absolute configurability and access to the source code, which is where a custom solution shines.
Turns out, the barrier to write such a code generator is pretty low! In fact, with the help of a lightweight library, you can write your own fully custom code generator which supports basic state machines in a few hours and about 600 lines of code.
In this blog entry I want to show you some steps in building a code generator, and give you a sense of what you can do with our Model2UML library and a bunch of C#.
Example model and generated code side-by-side (Hover on image to see the relationship)
Direct link to SVG for unsupported browsers
Features
The code generator described here is pretty small, but in fact it supports some powerful features:
- Primitive state machines
- Signal Triggers
- Composite States
- Entry- and Exit-Actions
- Transition Effects
- Decision Nodes
Additional features, such as Junctions, Time Triggers and History States can be added with relative ease, and with access to the source code you can configure it to output exactly the code you need in your environment. This blog entry is about an ANSI C code generator, but it can be adapted to every target language which supports basic conditionals.
Preparation
First of all you have to answer following questions before you can start:
- What language/framework to use for code generation?
I’d say C# is a good choice. Of course there are more specific solutions available for code generation, but from a pragmatic standpoint: The time necessary to learn them is comparable to the time needed to finish developing of a compact, stable and fast code generator based on a common language like C#. Also the knowledge availability and number of experts for e.g. C# is not comparable with any specific framework. - How to transform model to C Code?
In my opinion the most important question. If you understand what model is equivalent to which code – you’ve basically solved the problem. The core question is about implementation of state machines – I’ll outline typical techniques in the next section. - What tool to use for designing state machines?
My suggestion of course: Enterprise Architect – a tool which supports UML, SysML, etc., is easy to use, with an endless number of features and affordable for everyone. - How to access the designed state machines?
EA itself provides an API for accessing the model information, but in that case I suggest using our Model2Uml library with a lightweight UML-like API which handles a lot of EAs idiosyncrasies. At the end of the day you’ll save a lot of time and glue code.
State machine => C code transformation
The following two methods are commonly used in procedural languages:
- State transition table
You generate a table which maps every combination of current state and trigger to a new following state. The new state is determined by a simple table lookup. This is a fast approach and easy to generate, but these tables will get big (#States * #Triggers) very soon, wasting a lot of space which is bad especially on embedded systems. Features such as nested states or history states can be tricky to implement. - Switch-Case blocks
The idea is to generate Switch-Case blocks for every state, nested appropriately. This is a bit slower than a state transition table (the execution speed is mainly dependent on the depth the states are nested), but at the same time more space efficient and easy to extend.
For this blog entry, I wrote the generator to output switch-case blocks, as it seems to be the more versatile approach.
Why generate code for a class and not the state machine alone?
You want to generate state machine code, so you might be inclined to select the state machine and generate code for it. Don’t think of it this way though. The state machine should just describe the state logic, but to the state is actually part of a class. To generate code we also need the environment for this state machine, for example the attributes and methods. In fact, we want to generate code for a class which also happens to have a state machine, so selecting the class to generate code is also the more natural thing to do.
The easiest way to integrate a custom code generator into EA is replacing the EA-embedded code generator by our own code generator. Following steps are necessary:
- Implement an EA Addin:
Nothing easier than that: Set up a new C# project, reference EA.Interop and our Model2UML libraries. Also set the Debug properties for the project to start the external program EA.exe (which is in the Enterprise Architect installation folder), and we check the setting “Register for COM Interop” from the build settings of the project. - Register our Addin by EA:
Add a registry key in HKEY_CURRENT_USER\Software\Sparx Systems\EAAddins with your C# namespace and class name, for example “TestNamespace.CodeGenerator”. - Implement two methods for generation code for the c-file and for the header-file.
These methods will be called directly by EA from the EA code templates, and pass them all the information we need. They must have following signature:
object Method(IDualRepository repository, object arguments) - Manipulate code generator templates to let EA call our code generator instead of its own:Open the code generation template settings in EA ‘Settings’ -> ‘Code Generation Templates…’, select C language, and modify “File” and “File Impl” templates, add these two lines at the beginning of the template to call our method and skip the original code generation:
%EXEC_ADD_IN(“ClassName”, “generateCodeMethod”, classGUID, “specification”)%
%endTemplate%
The next time the project gets built, Visual Studio will register our assembly as COM-dll, will start EA and finally EA will load our assembly.
Model2UML
As mentioned above I used our library Model2UML, which provides a convenient UML like .NET interface to Enterprise Architect models.
To get started with Model2UML, instantiate a Model2UML Factory:
var umlFactory = new ComUmlFactory(repository, “TraceOutputWindowName”);
…and get an element from the Model, for example with:
IMyNamedElement someElement = umlFactory.GetUmlElementByGuid(“SomeGUID”);
…and going further…
foreach (IMyTransition t in s.Outgoings.Where(SomePredicate).OrderBy(SomeProperty)) { DoSomething(t); }
Currently Model2Uml provides following factories for accessing model information:
- ComUmlFactory encapsulates the EA built-in COM-based interface.
- XmiUmlFactory provides direct access to XMI files.
- DbUmlFactory (not available yet) provides direct access to the DB-Repository which contains EA-Models.
Model2Uml is based on the .NET generic collection framework, enabling concise lazy queries against the data. Since Model2UML tries to use UML nomenclature whenever possible, you shouldn’t have any problems to find your way around and using e.g. LINQ queries you can process any model information you need with a couple of lines of code. When in doubt, consult the class diagram or inspect your model interactively by using the powerful debugging capabilities of Visual Studio or the tools around Model2UML.
Design
For this generator, I chose to generate code in a straightforward way.
Think of the state machine as a tree of states (IMyState in Model2UML). Each state represents a node in this tree, and substates of a state are children of a node. The root of the tree is the state machine itself (IMyStateMachine in Model2UML). Transitions are not part of this tree structure at all.
The current state of the state machine is stored in state variables. For every node with children, there’s one state variable which denotes the active child. To find the active nodes, start from the root and traverse the tree according to the state variables. Every node you encounter is an active state.
Starting with the top-level, i.e. the SubStates of the StateMachine, the generator traverses (depth first) through the state tree, generating code for every state it encounters on its way, and in the process also generates the big switch-case blocks which are nested exactly like the states.
The code for each state is rather simple. Its main purpose is to activate the next state, depending on the outgoing transitions of the state or its parents. The main part of handling the transitions is checking the guards and triggers, and writing the correct state activation.
Activation of a state includes performing the exit actions of all states which will get deactivated, and performing the entry actions of all states which will get activated. This part is the most involved one, as states can be nested and transitions can go over many state boundaries. A little bit of tree traversal can easily remedy these difficulties though, and .NET paired with Model2UML makes this task easy.
Examples of the generated code
Switch-Case block:
int evConsumed = 0; switch (this->states.mainState) { case State1: if(evConsumed == 0) { evConsumed = 1; this->states.mainState = State2; // Entry Actions for State2 } else if(evConsumed == 0) { evConsumed = 1; if(this->someProperty == 2) { this->states.mainState = State3; // Entry Actions for State3 } else if(this->someProperty == 1) { this->states.mainState = State4; // Entry Actions for State4 } } break; case State2: ... break; default: break; } return evConsumed;
Nested state activation:
if(evConsumed == 0) { evConsumed = 1; // Activate outer state this->states.mainState = State1; // Entry action for outer state EntryForState1(this); // Activate inner state this->states.State1 = State2; // Entry action for inner state EntryForState2(this); }
For more Information about Model2UML feel free to drop us a line or give us a call!
Where is the 600 lines of code and where can I find Model2UML?
Thanks!
Hello Rogier,
thanks for your interest!
we provide both libraries (Model2Uml and Uml2C) on demand, the easiest way – write us an email: sales(at)lieberlieber.com. However I’ll send you an offer.
Amazing job! Keep it up. I am a novice at this. But I would like to understand how the data is stored (from UML diagram) and how you read it? For an example, data could be stored as an xml file and we could use an xml parser to get data out from there. The next step would be to convert this data into c files (assuming I am converting UML to C). I am trying to do this on Eclipse (Papyrus add on) since its free open source, but I am missing some concepts. Could you please help me understand better? Or at least shed some light in this direction (if you think I am asking too much intellectual property questions, you can say that, its ok)