Skip to main content

DISCLAIMER: This example only work with UNIX based systems due to the nature of Eclipse console

C Code Generation

This example demonstrates how to generate C code from a statechart. We will use a simple light switch example and focus on the generator model.

Generator Model​

In order to generate code we need to specify which code generator to use and into which folder to generate. For this, we need to create a so-called generator model. To do so,

  • Select the models folder in the Project Explorer
  • Right-click and choose New -> Code generator model
  • Follow the wizard to select the C code generator and the state machine for which you want to generate C code

The new generator model will look similar to this one:

/* Defines which code generator to use */
GeneratorModel for create::c {
/* Refers to the statechart model 'LightSwitch' */
statechart LightSwitch {
/* Specify the target location for the generated artifacts. */
feature Outlet {
targetProject = "itemis.create.examples.codegen.c"
targetFolder = "src-gen"
}
/* Generate a default timer service implementation */
feature GeneralFeatures {
timerService = true
}
}
}

The generator model defines where the code should be generated into, and enables the generation of a default timer service. Besides setting up the target location, the generator model allows you to adjust several aspects of the generated code like naming, license comments or function inlining. Press [CTRL]+[SPACE] to get a list of available features. For more information, please refer to our documentation.

Content assist in generator model

Invoking Code Generation​

Code generation is usually invoked each time the statechart model is saved. This behavior can be disabled by unchecking the option Project -> Build Automatically. You can always manually invoke the code generation with Generate Code Artifacts in the context menu of the generator model.

The Example Application​

As an example application we will use the light switch example with brightness adjustment from the Basic Tutorial.

Light switch model

Our application is a simple interactive console with which the user can switch the light on or off. The complete application code is implemented in main.c:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>

#include "../src-gen/LightSwitch.h"
#include "../src-gen/sc_rxc.h"
#include "../src-gen/sc_timer_service.h"

/* ! As we make use of time triggers (after & every)
* we make use of a generic timer implementation
* and need a defined number of timers. */
#define MAX_TIMERS 4

//! We allocate the desired array of timers.
static sc_timer_t timers[MAX_TIMERS];

//! The timers are managed by a timer service. */
static sc_timer_service_t timer_service;

// Start point of the execution.
unsigned long time_offset = 0;

// Last execution time.
unsigned long last_time = 0;

// Current time.
unsigned long current_time = 0;

// Stores the time to sleep.
struct timespec sleep_time;

static char buf[20];

/*! This function will be called by raising the out event light.on */
static void on_light_on(LightSwitch *o) {
printf("Light is on.\n");
}

/*! This function will be called by raising the out event light.off */
static void on_light_off(LightSwitch *o) {
printf("Light is off.\n");
}

/*! Subscribes the observers on the out event observables light.on and light.off */
static void subscribe_observers(LightSwitch *lightSwitch,
sc_single_subscription_observer *lightOnObserver,
sc_single_subscription_observer *lightOffObserver) {

sc_single_subscription_observer_init(lightOnObserver, lightSwitch,
(sc_observer_next_fp) on_light_on);
sc_single_subscription_observer_subscribe(lightOnObserver,
lightSwitch_light_get_on(lightSwitch));

sc_single_subscription_observer_init(lightOffObserver, lightSwitch,
(sc_observer_next_fp) on_light_off);
sc_single_subscription_observer_subscribe(lightOffObserver,
lightSwitch_light_get_off(lightSwitch));
}

unsigned long get_ms() {
struct timeval tv;
unsigned long ms;
gettimeofday(&tv, 0);
ms = tv.tv_sec * 1000 + (tv.tv_usec / 1000);
return ms;
}

int main(int argc, char **argv) {
/*! Instantiates the state machine */
LightSwitch lightSwitch;

/*! Instantiates observer for the out events */
sc_single_subscription_observer lightOnObserver;
sc_single_subscription_observer lightOffObserver;

/*! Initializes the timer service */
sc_timer_service_init(&timer_service, timers, MAX_TIMERS,
(sc_raise_time_event_fp) &lightSwitch_raise_time_event);

/*! Initializes the state machine, in particular all variables are set to a proper value */
lightSwitch_init(&lightSwitch);

/*! Subscribes observers to the state machine's observables */
subscribe_observers(&lightSwitch, &lightOnObserver, &lightOffObserver);

/*! Enters the state machine; from this point on the state machine is ready to react on incoming event */
lightSwitch_enter(&lightSwitch);

/*! Ensures non-blocking read() call. */
fcntl(STDIN_FILENO, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
puts("Type 1 or 0 to switch the light on or off.\n");
sleep_time.tv_sec = 0;
sleep_time.tv_nsec = 100;
time_offset = get_ms();
while (1) {
current_time = get_ms() - time_offset;
sc_timer_service_proceed(&timer_service, current_time - last_time);
int numRead = read(STDIN_FILENO, buf, 1);
if (numRead > 0) {
char input = buf[0];
if (input == '1') {
/*! Raises the On event in the state machine which causes the corresponding transition to be taken */
lightSwitch_user_raise_on_button(&lightSwitch);
} else if (input == '0') {
/*! Raises the Off event in the state machine */
lightSwitch_user_raise_off_button(&lightSwitch);
}
if (input != '\n') {
/*! Prints the value of the brightness variable */
printf("Brightness: %d.\n",
lightSwitch_light_get_brightness(&lightSwitch));
}
}
last_time = current_time;
nanosleep(&sleep_time, 0);
}

return 0;
}

/*! This function will be called for each time event in LightSwitch when a state is entered. */
void lightSwitch_set_timer(LightSwitch *handle, const sc_eventid evid,
const sc_integer time_ms, const sc_boolean periodic) {
sc_timer_set(&timer_service, handle, evid, time_ms, periodic);
}

/*! This function will be called for each time event in LightSwitch when a state will be left. */
void lightSwitch_unset_timer(LightSwitch *handle, const sc_eventid evid) {
sc_timer_unset(&timer_service, evid);
}

The most important parts are commented in the main.c file. These are the bullet points:

  • Instantiate the state machine and the observers
  • Initialize the timer service
  • Initialize the state machine
  • Subscribe observers to react on outgoing events
  • Enter the state machine; from this point on the state machine is ready to react on incoming event
  • Raise the input events user.on_button and user.off_button

You can run the application with Run As -> Local C/C++ Application on the project. A console should open and ask you for input like in the screenshot below.

Light switch console application

Get this example​

The complete project — statechart models, sources and build files — lives in the itemis CREATE examples repository. Inside itemis CREATE for Eclipse you can import it directly with the example wizard.