Statestep C Template

Version: 1.1 : Documentation updated / improved.
Version: 1.0 : Initial version.

The C template, contained in the file 'template.c.ftl', generates code for formulas (constraints and rules) as well as the tabular elements of a Statestep model.

To learn how to use a template to generate code, read the general instructions. For updates, alternative templates and tips on custom template modifications, check the Statestep website.

Basic Usage Pattern

This template produces a .h file as well as a .c file. The following code illustrates the basic pattern of usage. You can compile and link this with the generated code to produce a usable demo:

int main()
{
   s2_Combination state, nextState;
   int var;
   s2_Event event;
   int rule;
   int i;
   int result;
   if ((s2_NUM_VARS == 0) || (s2_NUM_EVENTS == 0))
   {
      printf("Need variables (with values) and events!\n");
      return 1;
   }
   for (i = 0; i < s2_NUM_COMBINATIONS; ++i)
   {
      state = s2_combinationFromIndex(i);
      if (s2_stateAllowed(state))
         break;
   }
   printf("Initial state (arbitrarily chosen):\n");
   for (var = 0; var < s2_NUM_VARS; ++var)
   {
      printf("%20s  = %s\n", s2_variableNames[var],
         s2_getValueName((s2_Variable) var, state.values[var]));
   }
   for (;;)
   {
      if (!s2_stateAllowed(state))
         printf("(The current state is not valid!)\n");
      printf("Enter an event (between 0 and %d):",
         s2_NUM_EVENTS - 1);
      result = scanf("%d", &event);
      if ((!result) || (event < 0) || (event >= s2_NUM_EVENTS))
      {
         printf("Invalid event!\n");
         return 1;
      }
      printf("Event %d is %s...\n", event, s2_eventNames[event]);
      if (s2_stateAllowed(state) && !s2_eventAllowed(event, state))
         printf("(This event is not expected in this state!)\n");
      rule = s2_applyRules(event, state, &nextState, 0);
      if (rule < 0)
      {
         printf("No rule applies for the event in this state.\n");
      }
      else
      {
         printf("Applying rule %s ...\n", s2_ruleNames[event][rule]);
         for(var = 0; var < s2_NUM_VARS; ++var)
         {
            printf("%20s %s %s\n", s2_variableNames[var],
               nextState.values[var] == state.values[var] ?
               " =" : "->",
               s2_getValueName((s2_Variable) var,
               nextState.values[var]));
         }
         state = nextState;
      }
   }
}

Adding Code to Specific Rules

The following assumes code written separately from the Statestep model. It's also possible to embed executable code directly into the model so that it is automatically included in the generated code. For more on this, check the website.

In the most general case, the Statestep model will describe a state machine, as in the demo above, but some of its behaviour will be determined by factors outside the model. As an example, suppose we have a model for a digital alarm clock which allows a number of different alarm times to be set and one of the rules looks like this:

Event Button_D, Rule set_alarm_1b:

Mode Alarms (Other Vars...)
program none
some
...
= some
max
...
# The selected time is not the time of an
# existing alarm; a new alarm is set to go
# off at the selected time.

Here, part of the rule's logic exists only as a comment in the model: code to implement this logic must be added to the generated code. Also, the new value of the 'Alarms' variable is uncertain in the rule and so the added code must determine whether this variable changes to 'some' or, if the maximum number of alarms has been set, 'max'. The way to integrate this with the code generated by the template is to write a function of the following kind:

int extraLogic(s2_Event event, int rule,
   s2_Combination current, s2_Combination *next)
{
   ...
   // (Rule numbers are unique only within each event
   // so have to check both event and rule:)
   if (event == s2_e_Button_D &&
      rule == s2_r_Button_D__set_alarm_1b)
   {
      int i;
      for (i = 0; i < numAlarms; ++i)
      {
         if (alarmTime[i] == selectedTime)
         {
            // this rule does not apply; search for another
            return 0;
         }
      }
      alarmTime[numAlarms] = selectedTime;
      ++numAlarms;
      // the next value of 'Alarms' will have been set 
      // arbitrarily to either 'some' or 'max' in the 
      // generated code; set the correct value here
      if (numAlarms < MAX_ALARMS)
         next->values[s2_v_Alarms] = s2_v_Alarms__some;
      else
         next->values[s2_v_Alarms] = s2_v_Alarms__max;
      return 1;
   }
   ...
}

A pointer to this function is then passed to s2_applyRules(), which will probably be called as part of a loop to repeatedly update the state in response to the latest detected event. This loop is typically also the place to put any actions required when the values of different variables change. For example:

   ...
   rule = s2_applyRules(event, state, &nextState, &extraLogic);
   if (rule >= 0)
   {
      if (nextState.values[s2_v_Alarms] != state.values[s2_v_Alarms])
      {
         if (state.values[s2_v_Tray] == s2_v_Alarms__none)
            setDisplayElement(BELL_SYMBOL, ENABLED);
         ...
      }
      ...
      state = nextState;
   }
   ...

Non-deterministic Rules

As you might have gathered from the demo main() earlier, s2_applyRules() ignores any constraints; a separate function must be called if you want to check that a state is valid. Further, if you have a non-deterministic rule in your model, that is, a rule which allows more than one possible outcome in a particular case (as in Rule 'set_alarm_1b' above, where the new value of 'Alarms' is uncertain), then s2_applyRules() will choose one possible next state arbitrarily.

There is a particular danger here because, if a rule offers a choice of which state to go to next, and at least one of these states is valid, then Statestep (at least up to v1.0) doesn't report a violation. However, in the generated code, s2_applyRules() might make the 'wrong' choice. Wherever you have non-deterministic rules, it is up to you to add the correct "extra logic" which is consistent with the rule and the constraints.

Maximum Size of Statestep Variables

This template maps variable values to bits in enums. As written, the generated code should cause the compiler to report a warning (at least) if any state variable is too big. This may be a concern with compilers using small (16-bit) ints, e.g., potentially if compiling for an 8-bit microcontroller. To use this template unmodified in such a case, any larger variable in the model could be replaced with two or more equivalent smaller variables.