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:
... -l go ...Go has several compelling features compared to C, including goroutines, select, and channels. These enable elegant concurrency models without pthread-level complexity. Other useful features include maps, slices, generics, embedding, tickers, and timers.
These features are used in the microwave oven Go example (source on GitHub) to implement a compact command-line simulation.
The oven state machine is driven by an active object that handles events from multiple sources using Go select and channels. The React() function below is a goroutine that processes keyboard events and timer events driven by a ticker:
func (smBase *Reactor) React(c chan OvenEvent, quit chan bool) {
smBase.Ticker = time.NewTicker(time.Millisecond * 100)
var err error = nil
for {
select {
case event := <-c: // receive events
_, err = smBase.ProcessEvent(event)
case <-quit: // receive quit event
return // Quit Co-routine
case <-smBase.Ticker.C: // timer tick to check if there are any timeouts
vals := smBase.Timers.Tick()
for i := 0; i < len(vals); i++ {
if evt, ok := vals[i].Event.(OvenEvent); ok {
_, err = smBase.ProcessEvent(evt)
}
}
}
if err != nil {
debugPrint(fmt.Sprintf("internal error in sm %s\n", err))
return
}
}
}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 and command line options to adjust the generated code.
Configuration File Options#
BaseClassMachine: Go supports struct embedding as a seamless composition mechanism. WithBaseClassMachine, the embedding type can be specified. This allows generated machine code to directly access members and functions from the embedding struct. In the oven example, this is used for timer services and timer IDs.
// Demonstrates how to embed 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: value used as package name of generated code.ValidationCall: when set toyes, a validation/logging hook is generated for transition checks and state-change logging. This function is expected on the embedding data type.
Command Line Options#
-Trace: generates trace output for taken transitions including triggering event and guard. Expected logger function must exist on the embedding type.-l GO: generates Go code.
As in other backends, code at the beginning of the generated Go file can be provided via UML class header content, for example imports or helper code. Pre-/post-action handler code can also be configured.
The code generator supports models with regions (orthogonal parts of a state).
The microwave oven example with regions is shown below. It uses two timers: one cyclic timer to blink an LED while running, and one single-shot timer for cooking duration.

More details: microwave_go example
Below is sample console trace output from the oven simulation:
===== Microwave oven simulation
===== Use the following keys:
===== o,O: Open the door
===== c,C: Close the door
===== + increase cooking time
===== - decrease 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: EvTimeoutFor further information, refer to the corresponding manual chapter on Go.