No Description

Calle 9a9530e9f2 Fix Typo 1 year ago
c++-mode 264f2b1a69 Changed typedef to using instead. 1 year ago
doc fb79ee7b8f Just a small 3 years ago
examples 83423290fb Bug fix 3 years ago
.gitignore aaaa076f22 initial commit 3 years ago
LICENSE aaaa076f22 initial commit 3 years ago
README.md fb79ee7b8f Just a small 3 years ago
yasnippet_function.el 9a9530e9f2 Fix Typo 1 year ago

README.md

Meta state machine - Emacs

Meta state machine is part of boost and is according to the boost documentation

A library allowing you to easily and quickly
define state machines of very high performance.

Since I'm a big supporter for state-machines this seemed like a really nice library, but unfortunately it turned out to be a real pain when it boiled down to coding by-hand. As the name says the this is based on templates which in it self can be pretty daunting. So after done a couple of the state machines I realized the that either this needs to be done by some kind of GUI or at least some simplified manner. Since I'm either a great GUI designer or writer, I did it my way which I find easy and satisfying, I also icluded a nice feature creting a picture of the statechart using doxygen and plantuml e.g nil

Using Emacs and YA-Snippets. Since there aren't many other tools for doing this job, i thought i make this open source.

Pre-requisite

nil As mentioned before this work is based on Yasnippets, and emacs. So YAsnippet needs to be installed in emacs (see Yasnippet easily installed via Melp ). Assuming that you installed YAsnippet and enabled it

(M-x yas-global-mode)

or insert into .emacs

(yas-global-mode 1)

its time to download the latest release of MsmState. Uncompress the file and add it to the yas-installed-snippets-dir (checkout (C-h C-v) yas-installed-snippets-dir) directory (in my case it is "~/.emacs.d/snippets"). Edit your emacs startup file by adding the following lines

(yas-global-mode 1)
(load-file "~./.emacs.d/snippets/yasnippet_function.el")

The next section describes how to setup doxygen

Doxygen - with plantuml

This is not a tutorial how to setup DoxyGen since there are already several great once. In the later versions (Im using 1.8.11) there is support for Plantuml, which is a java that allows you to create uml diagrams in an easy and comprehensiv manner. As mentioned before doxygen has support for it, all one needs to do is to download plantuml.jar create a doxygen configuration file by issuing:

doxygen -g

This will create a Doxyfile which needs to be edit. By opening the Doxyfile and search for

PLANTUML_JAR_PATH      =

Add the path to the jar file. Make sure you don't include the actual jar file name.

If you want to test if it works paste this into a new file.

/**
 *  \brief Test
 *
 *  \startuml
 *   Sender->Receiver
 *   Receiver->Sender
 *  \enduml
 *
 *  \param param
 *  \return return type
 */


class Test
{
public:
  Test();
  virtual ~Test();
};

then run doxygen This should produce a html directory where you should be able to find the index.html and class reference to Test. and a uml sequence diagram:

nil

If successful you should be ready to go.

Creating State machine

This section will describe the development of a msm state machine using yasnippet provided. We start by creating a UML state chart diagrams for which we want to produce a msm state machine.

nil

This is what we want, so lets get to it.

Generating Statemachine

First we start by creating CmdKeyStateMachine.hpp file by issuing:

emacs CmdKeyStateMachine.hpp

Make sure you have the menu YASnippet->c++-mode->msm->statemachine visible.

now type the command

statemachine<TAB>

This should produce a skeleton for the statemachine, the cursor will now be placed on the doxygen comment for which name the statemachine should have, by default it uses the same name as the file excluding the extension. In this case it should be CmdKeyStatemachine, this can obviously be changed to whatever name you want. The change will be reflected throughout the file. When done editing the name press the TAB key and the cursor will jump to the next editing section, in this case its the initial state. There is always an inital state, where the statemachine will start executing. In the statechart shown above the Initial state is WaitForInput , so lets edit the initial to WaitForInput and then press the TAB key. This time the cursor jumps into the transitiontable. This is where the yasnippet ends for statemachine.

Adding rows to transition table

The transition table describes transitions between states, and what events that triggers the transtions together with guards and actions. to add a new row to the transition table type

msmRow<TAB>

This should produce an output where the cursor is put infront of source. If we take a look at our statemachine we can see that there are two events that are triggered from WaitforInput as the following table shows.

Source event Target Action guard
WaitForInput evCtrlKeyPressed WaitForKey SetCtrlKey -
WaitForInput evShiftKeyPressed WaitForKey SetShiftKey -

This table can be translated directly into the msmRow snippet as follows:

/**
 * WaitForInput - Source <TAB>
 * evCtrlKeyPressed - Event <TAB>
 * WaitForKey - Target <TAB>
 * SetCtrlKey - Action <TAB>
 * none - Guard <TAB>
 */
msmf::Row<WaitForInput    ,evCtrlKeyPressed,WaitForKey      ,SetCtrlKey      ,msmf::none      >,

If there is no action/event or guard the word none is entered, this will be translated accordingly. The last TAB after the guard insertion will jump to the end of the Row on ~,~. If its the last row in the transition table you can erase the ~,~ if there are more rows to follow, then leave it.

We can now continue with the events from WaitForKey using the following table.

Source event Target Action guard
WaitForKey evKeyPressed WaitForKey OutputKey  
WaitForKey evCmdKeyReleased WaitForInput    

By issuing msmRow snippet. This will produce an output as follows:

/**
 * WaitForKey - Source
 * evKeyPressed - Event
 * WaitForKey - Target
 * OutputKey - Action
 * none - Guard
 */
msmf::Row<WaitForKey      ,evKeyPressed    ,WaitForKey      ,OutputKey       ,msmf::none      >,
/**
 * WaitForKey - Source
 * evCmdKeyReleased - Event
 * WaitForInput - Target
 * none - Action
 * none - Guard
 */
msmf::Row<WaitForKey      ,evCmdKeyReleased,WaitForInput    ,msmf::none      ,msmf::none      > 

Thats it, we produced the transition table. But there are still a couple of things missing. First of all, right now we just have one state. The WaitForInput . So lets continue creating states.

Create states

The state are easily done using state snippet. The best place to put it is to search for

Defined states

and underneath the comment add

state<TAB>

this will produce a skeleton for a state, the cursor is placed on the class name, this is where you enter the state name (WaitForKey). By pressing TAB the cursor will now jump to different sections where its possible to add/change name. For this tutorial the default is enough just tab through it. We are not quite done yet, though for the YASnippets we are. Now lets continue creating Actions and guards.

Action,Guards and Events(UML output)

Most of the statechart is now implemented, but we need the action,guards and events to be implemented. Lets start with Events

Events

Events can be constructed using a lisp functions implented in yasnippet_function.el. By execting the following function

(M-x) col/msm-implement-events

This will open a new buffer which is named as the statemachine but adding <name>_Events.hpp In this tutorial the name will be

CmdKeystatemachine_Events.hpp

The buffer is not saved, so if you want to keep it just make sure you save the buffer to either the named file or as another. The next part is to include the events in the state machine hpp file.

File: CmdKeyStateMachine.hpp add the following line

#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
//Added include file
#include "CmdKeyStateMachine_Events.hpp"

The function also updates the doxygen Uml diagram which is located under the class description or to be precise:

\startuml "<statemachine name>"
\enduml

Its not perfect in anyway and needs some more work, but it will give a basic UML statechart on the doxygen.

The next part is to create action and guards.

Action and Guards

This is also a lisp function, by executing

(M-x) col/msm-implement-action

a new buffer will open, just like with the events. In the same manner its possible to save the file as it is. Or copy the output and paste it into the statemachine directly. If saved as a file, one needs to include the file somewhere in the statemachine class. For example:

#include "CmdKeyStateMachine_GuardAndActions.hpp"
/**
 * \class transition_table
 * \brief Transition table struture
 .
 .

Now the statemachine is done!

A Guard function

In the above example there were no guards included. Since guards are function objects that returns true or false, these are somewhat different from action function objects, which don't return anything. In all other aspects guards behave the same way as action objects. Lets take an example.

nil

In the above statemachine we start at Init State. Lets first take a look at the transition table:

struct transition_table: public mpl::vector<

  msmf::Row<Init            ,evInit          ,State1          ,msmf::none      ,isOkToProceed   >, (ref:proceed)
  msmf::Row<State1          ,evBack          ,Init            ,msmf::none      ,IsGotoInit      >, (ref:GotoInit)
  msmf::Row<State1          ,evBack          ,State1          ,msmf::none      ,IsNotGotoInit   >  (ref:NotGotoInit)

  >{};

As one can notice from the transition table is that conditions are not supported. Instead two guards are created, one for when IsGotoInit and one that is IsNotGotoinit.

In this example the statemachine has kept states as variables, its also possible to use events as a conditional variable, that is storing the condition inside the event object, and then extracting out of the event. The described example can be found at GuardMachine.

Main file for the GuardMachine:

#include <iostream>
#include "GuardMachine.hpp"
#include "GuardMachine_Events.hpp"

using namespace std;

int main(int argc, char *argv[])
{
  GuardMachine stm;
  cout << " Start" << endl;
  stm.start();

  stm.process_event(evInit());
  cout << "send Init Event okToProceed: " << ( stm.okToProceed_ ? "True":"False") << endl;



  stm.okToProceed_ = true;
  cout << "send Init Event okToProceed: " << ( stm.okToProceed_ ? "True":"False") << endl;
  stm.process_event(evInit());

  cout << "Sending evBack gotoInit ==" << (stm.gotoInit_ ? "True":"False")  << endl;
  stm.process_event(evBack());

  cout << "Setting gotoInit = true" << endl;
  stm.gotoInit_ = true;
  stm.process_event(evBack());


  return 0;
}

And the correponding output

./GuardMachine
 Start
Enter GuardMachine
Entry Init
send Init Event okToProceed: False
send Init Event okToProceed: True
Exit Init
Entry State1
Sending evBack gotoInit ==False
Exit State1
Entry State1
Setting gotoInit = true
Exit State1
Entry Init

Making a main

It now time to start using the statemachine. Its quite easy, so lets get right to it.

#include <iostream>
#include "CmdKeyStateMachine.hpp"
#include "CmdKeyStateMachine_Events.hpp"

using namespace std;

int main(int argc, char *argv[])
{
  CmdKeyStateMachine stm;  (ref:Create)
  cout << " Start" << endl;
  stm.start(); (ref:start)


  cout << "Send CtrlKey" << endl;
  stm.process_event( evCtrlKeyPressed() ); (ref:evCtrlKey)

  cout << "Send keyPressed" << endl;
  stm.process_event( evKeyPressed() ); (ref:evKeyPressed)

  cout << "send release cmd key" << endl;
  stm.process_event(evCmdKeyReleased()); (ref:evReleased)



  return 0;
}

Lets go through what happens.

  • Line 9, Creates the object stm which is a CmdKeystatemachine (the one we just created)

  • Line 11, starts the statemachine, this will call on_entry on Cmdkeystatemachine, and then continues to WaitForinput which was our first initial state and call on_entry for that one. The statemachine now waits for an event.

  • Line 15, Create a event object of type evCtrlkeypressed and send it to the statemachine, this will trigger an transition to WaitForInput and calling the action SetCntrlKey. After the action is performed the statemachine will now be in state WaitForKey.

  • Line 18, Creates a event object of type evKeyPressed and sends it to the statemachine, this will trigger an transaction to itself and execute the action OutputKey. The statemachine is now back to WaitForKey.

  • Line 21, creates a event object of type evCmdKeyReleased and sends it to the statemachine, this will trigger a transaction back to WaitForinput , but no action is taken place.

The whole example code can be found under <./examples/CmdKeyMachine>. The only difference is that I've added console output, to be able to see what happens. To build the examples go into the directory and create a new directory .eg build and issue cmake build.

>cd exmaples/CmdKeyMachine
>mkdir build
>cd build
>cmake ..

the output from the CmdKeymachine example is

bash>./CmdMachine
 Start
Statemachine entry
 WaitForInput Entry
Send CtrlKey
WaitForInput Exit
 SetCrtlKey
 WaitForKey Entry
Send keyPressed
 WaitForKey Exit
 Output key
 WaitForKey Entry
send release cmd key
 WaitForKey Exit
 WaitForInput Entry