SinelaboreRT Header Logo

SinelaboreRT

As simple as possible, but not any simpler!

User Tools

Site Tools


wike:backends:go_lang

Generating GO code

Sinelabore creates compact and clearly readable GO code from UML state machine diagrams.

To generate GO code, call the code generator with the command line flag -l go.

GO has some very convincing new features compared to C. A highlight are go routines with the select and the channel concept. This allows to write very elegant threading code without the need to use pthreads or other more complex methods. Some other interesting features are the availability of maps and slices, generics, the concept of embedding, tickers and timers to name some others. All of these features were used in the oven state machine example (see code here on Github) to create a simple but still powerful command line simulation.

The example oven state machine is driven by an active object that handles events from multiple sources using the GO select and channel feature. The React() function below is a go routine which processes keyboard events (line 6) and timer events (line 10) with a resolution provided by a Ticker. The state machine handler is then called with the respective event defined in the state machine diagram.

  1. func (smBase *Reactor) React(c chan OvenEvent, quit chan bool) {
  2. smBase.Ticker = time.NewTicker(time.Millisecond * 100)
  3. var err error = nil
  4. for {
  5. select {
  6. case event := <-c: // receive events
  7. _, err = smBase.ProcessEvent(event)
  8. case <-quit: // receive quit event
  9. return // Quit Co-routine
  10. case <-smBase.Ticker.C: // timer tick to check if there are any timeouts
  11. vals := smBase.Timers.Tick()
  12. for i := 0; i < len(vals); i++ {
  13. if evt, ok := vals[i].Event.(OvenEvent); ok {
  14. _, err = smBase.ProcessEvent(evt)
  15. }
  16. }
  17. }
  18. if err != nil {
  19. debugPrint(fmt.Sprintf("internal error in sm %s\n", err))
  20.  
  21. return
  22. }
  23. }
  24. }

In the generated code, state hierarchies are mapped to nested switch/case code. Event handling code is mapped to if/else-if/else structures. There are several configuration/command line options to adjust the generated code: Configuration file options:

  • BaseClassMachine: GO supports the language concept of embedding of structs to express a more seamless composition of types. With the ‘BaseClassMachine’ parameter the parent structure (embedding) Type can be specified. The nice thing in GO is the possibility to access structure members or functions of the embedding structure in the generated state machine code. This feature is used in the oven example to provide access to a Timer service and to store some data (timer IDs).
// Demonstrates how to embedded the statemachine type into another type
// and use data/functions from this type e.g. as guards ... in the statemachine.
type Reactor struct {
  id           uint8
  idBlink      uint8
  Ticker       *time.Ticker
  Timers       *CountdownTimers
  Statemachine *Oven
  • Namespace: The specified value is used as package name of the generated code.
  • ValidationCall: If set to ‘yes’ a function is added to the generated code that allows to check if a state transition is allowed or simply to log state changes. It is expected that the function is implemented on the embedding data type.

Command line options:

  • -Trace: Generates trace the taken transition which includes the triggering event as well as the guard. It is expected that the function is implemented on the embedding data type.
  • -l GO: Instructs the code generator to generate GO code.

As in the other backends too, code at the beginning of the generated GO file can be specified with the ‘header’ linked to the class in a UML diagram. Use this to define your imports or other local code. As well as pre- and post-action code of the generated state handler (see section 'Introduction' in the manual).

The code generator supports code generation from models with regions. A region is an orthogonal part of a state. Regions allow to express parallelism within a state. A state can have two or more regions. Region contains states and transitions.

The example state diagram of a microwave oven state machine built with regions is shown below. Imagine how much time can be saved by generating 100% of the code instead of manually writing it. And even worse how much wasted time it would be to maintain it over time when new states/events are added. The oven uses the generic countdown_counter to schedule timers. This example uses two timers. One cyclic timer is used to flash an LED if the oven is running. The other one is used for the cooking time set by the user (single-shot).

Get an impression about the powerful generation here: https://github.com/sinelabore/examples/tree/master/OS_neutral/microwave_go

 State machine diagram of a microwave oven with GO code inside states and transitions.

Below the console trace output of the oven simulation program is shown with the following user interaction: First set the cooking time to 2 seconds (pressing ++) which sends 2x EvInc. Then start cooking by closing the door (press c) which sends EvDoorClosed. Then pause the cooking by opening the door (pressing o) which sends EvDoorOpen and then close again the door (pressing c) which sends EvDoorClosed. In-between you can see the timer events of the blinking LED and finally the cooking timeout event.

Question: what keys do you have to press to cook again? Hint: you have to open the door again before you can set the cooking timer again. Look into the state diagram and all should be clear!

 ===== Microwave oven simulation
 ===== Use the following keys:
 ===== o,O: Open the door
 ===== c,C: Close the door
 ===== + increase cooking time
 ===== - decrese cooking time
 ===== q,Q: quit
 ===== 
 ===== Example sequence: +++++c   o   c +++ c
 ===== 
2023/11/20 21:15:08 Oven off
2023/11/20 21:15:09 Change state from Super to Super
2023/11/20 21:15:09 Oven off
2023/11/20 21:15:09 Trace msg: EvInc
2023/11/20 21:15:10 Change state from Super to Super
2023/11/20 21:15:10 Oven off
2023/11/20 21:15:10 Trace msg: EvInc
2023/11/20 21:15:13 Change state from Idle to Cooking
2023/11/20 21:15:13 Oven on
LED on
2023/11/20 21:15:13 Trace msg: EvDoorClosed[smBase.Timers.GetPresetInSec(smBase.id)>0]
2023/11/20 21:15:13 Timer 1 running but not expired (Cnt=2000)
2023/11/20 21:15:13 Timer 2 running but not expired (Cnt=500)
2023/11/20 21:15:13 Timer 1 running but not expired (Cnt=1900)
2023/11/20 21:15:13 Timer 2 running but not expired (Cnt=400)
2023/11/20 21:15:13 Timer 1 running but not expired (Cnt=1800)
2023/11/20 21:15:13 Timer 2 running but not expired (Cnt=300)
2023/11/20 21:15:13 Timer 1 running but not expired (Cnt=1700)
2023/11/20 21:15:13 Timer 2 running but not expired (Cnt=200)
2023/11/20 21:15:13 Timer 1 running but not expired (Cnt=1600)
2023/11/20 21:15:13 Timer 2 running but not expired (Cnt=100)
2023/11/20 21:15:13 Change state from LedOn to LedOff
LED off
2023/11/20 21:15:13 Trace msg: EvTimeoutBlink
2023/11/20 21:15:13 Timer 1 running but not expired (Cnt=1500)
2023/11/20 21:15:13 Timer 2 running but not expired (Cnt=500)
2023/11/20 21:15:13 Timer 1 running but not expired (Cnt=1400)
2023/11/20 21:15:13 Timer 2 running but not expired (Cnt=400)
2023/11/20 21:15:13 Timer 1 running but not expired (Cnt=1300)
2023/11/20 21:15:13 Timer 2 running but not expired (Cnt=300)
2023/11/20 21:15:13 Change state from Cooking to CookingPause
LED Off
2023/11/20 21:15:13 Oven off
2023/11/20 21:15:13 Trace msg: EvDoorOpen
2023/11/20 21:15:14 Change state from CookingPause to Cooking
2023/11/20 21:15:14 Oven on
LED on
2023/11/20 21:15:14 Trace msg: EvDoorClosed
2023/11/20 21:15:14 Timer 1 running but not expired (Cnt=1200)
2023/11/20 21:15:14 Timer 2 running but not expired (Cnt=500)
2023/11/20 21:15:14 Timer 1 running but not expired (Cnt=1100)
2023/11/20 21:15:14 Timer 2 running but not expired (Cnt=400)
2023/11/20 21:15:14 Timer 1 running but not expired (Cnt=1000)
2023/11/20 21:15:14 Timer 2 running but not expired (Cnt=300)
2023/11/20 21:15:14 Timer 1 running but not expired (Cnt=900)
2023/11/20 21:15:14 Timer 2 running but not expired (Cnt=200)
2023/11/20 21:15:14 Timer 1 running but not expired (Cnt=800)
2023/11/20 21:15:14 Timer 2 running but not expired (Cnt=100)
2023/11/20 21:15:14 Change state from LedOn to LedOff
LED off
2023/11/20 21:15:14 Trace msg: EvTimeoutBlink
2023/11/20 21:15:14 Timer 1 running but not expired (Cnt=700)
2023/11/20 21:15:14 Timer 2 running but not expired (Cnt=500)
2023/11/20 21:15:15 Timer 1 running but not expired (Cnt=600)
2023/11/20 21:15:15 Timer 2 running but not expired (Cnt=400)
2023/11/20 21:15:15 Timer 1 running but not expired (Cnt=500)
2023/11/20 21:15:15 Timer 2 running but not expired (Cnt=300)
2023/11/20 21:15:15 Timer 1 running but not expired (Cnt=400)
2023/11/20 21:15:15 Timer 2 running but not expired (Cnt=200)
2023/11/20 21:15:15 Timer 1 running but not expired (Cnt=300)
2023/11/20 21:15:15 Timer 2 running but not expired (Cnt=100)
2023/11/20 21:15:15 Change state from LedOff to LedOn
LED on
2023/11/20 21:15:15 Trace msg: EvTimeoutBlink
2023/11/20 21:15:15 Timer 1 running but not expired (Cnt=200)
2023/11/20 21:15:15 Timer 2 running but not expired (Cnt=500)
2023/11/20 21:15:15 Timer 1 running but not expired (Cnt=100)
2023/11/20 21:15:15 Timer 2 running but not expired (Cnt=400)
2023/11/20 21:15:15 Change state from Cooking to Completed
LED Off
2023/11/20 21:15:15 Oven off
2023/11/20 21:15:15 Trace msg: EvTimeout

For further information, please refer to the corresponding manual chapter on GO.

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
wike/backends/go_lang.txt · Last modified: 2023/11/21 18:33 by webmin

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki