The objectives of this step are the following:

  • Building the right model for the right domain
  • Explore how to change a model-based representation; or how to define a composition operator

The choice is up to you. To tame the limitations identified during the previous step, we offer you two choices. On the one hand, you can change your meta-model, and propose one that is more relevant for the domain (we propose here a simplification of a reactive system meta-model). On the other hand, you can stick with the previously defined meta-model, and create a composition operator that will help to design large apps by composing the different FSMs in an automated way.


Option A: Building a Reactive Metamodel

Sticking to FSMs does not seem totally appropriate, as it does not provide us the right level of abstraction to define what an Arduino “app” is. We propose here a “reactive” metamodel, where an App is defined as the composition of events sent from “producers” to “consumers”

Impact on code generation

We consider each hardware component as a functional element that has a business value from the application point of view.

boolean led_on = true;
void change_state_led(){
if (led_on) { digitalWrite(13, LOW); }
else { digitalWrite(13, HIGH); }
led_on = !led_on;
}

Then, we implement the reaction as a simplified pub/sub mechanism. Producers will set flags for consumers, which will react to such flags when needed.

boolean is_present(int messageQueue) { return messageQueue == 1; }
boolean LED_FLAG = false;

void button_push() {
if (digitalRead(10) == HIGH) { LED_FLAG = true; }
}

// ###### Message consumers
void led_pull() {
// do nothing if no message present
if(!is_present(LED_FLAG)) { return; }
change_state_led(); // business code
LED_FLAG = 0; // remove flag
}

Finally, the main loop simply triggers the message producers, then the consumers, then sleep.

int main(void)
{
setup();
while(1) {
// message producer
button_push();
// message consumer
led_pull();
// frequency
_delay_ms(100);
}
return 0;
}

Expected work

  • Provide an implementation of this metamodel, and the associated code generation
  • Express the two apps using your newly created abstractions

Option B: Composing Finite State Machines

We consider here the meta-model from the previous step (considering its extended version to support the requirements of the lab). 

Instead of changing the meta-model, we can decide to create a tool that will compose two apps together automatically. Applying the separation of concerns (SoC) paradigm, each application is defined separately, and an automatic tool is provided to build the final and complex one.

In our case, it means to define a composition operator +, that, given two applications app1 and app2 can produce app' = app1 + app2. The shared resources (here the button) need to be unified and composed in the final application:

  • app1: an application that switches on and off the light when the button is pressed
  • app2: the 7-segment counter, reset to 0 when the button is pushed
  • app': the expected app, counting from 0 to 9 and reset to 0 while switching the light on and off.

At the code level, it means to implement a binary law of composition, that takes two App and produce a new one. A Java skeleton to model and implement such a law is given here:

public class CompositionLaw extends BinaryLaw<App> {

    @Override
    public App compose(App left, App right) {
        // …
    }

}

Expected work:

  • Implement the composition law used to combine two apps together;
  • Refactor the application you’ve defined during the previous step as two separated applications
  • Use the composition law to combine them together, and generate the code of the resulting app.

Stepback Questions

  • Compare how this modelling solution and the previous one match the domain, especially regarding expressiveness and scalability.

  • What is the cost (e.g., modelling, code generation) of a new feature for the developer?

  • What about the scalability of the modelling paradigm itself?