cancel
Showing results for 
Search instead for 
Did you mean: 

Understanding the difference between Model and ModelListener

nico23
Senior

As the title, I can't get the difference between the Model and ModelListener.

From what I've understood the Model class is used to get and store/send the data coming between the phisical part of the microcontroller (i.e. peripheral) and the GUI. The ModelListener instead is the interface that checks if these data have been updated and inform specific Presenters when needed.

I have kind of understood the documentation provided here but, first of all, why in the Model.cpp it checks if modelListener != 0?

I can't understand where and how modelListener takes its value.

I've checked the E-Bike Demo and the process of passing data from the Model to the GUI is a little bit difference. In the Demo the Model.hpp has a bind function

void bind(ModelListener* listener)
    {
        modelListener = listener;
    }

With the ModelListener.hpp having another bind to the Model class

class ModelListener
{
public:
    ModelListener() : model(0) {}

    virtual ~ModelListener() {}

    void bind(Model* m)
    {
        model = m;
    }

    virtual void gaugeStartAnimationIsDone() {};
protected:
    Model* model;
};

And in the various .CPP Presenters of the Views it returns the data directly from the model, not from the modelListener like:

int DashboardSmallPresenter::getHourTemperature(int hr)
{
    return model->getHourTemperature(hr);
}

So why this disilign between the two usages?

1 ACCEPTED SOLUTION

Accepted Solutions
JohanAstrup
ST Employee

Hello Nico.

I agree that the Flash-limited E-bike demo is not the best example to showcase the Model-View-Presenter structure. This is primarily due to the simplicity of the data transferred between the Model and the View.

In general, you can follow these guidelines:

  1. If the Model needs to notify a View that some Model data has been updated, you should use the Model Listener.
  2. If the View needs to fetch or set a variable from the Model, you should use normal getter and setter functions.

Based on these guidelines, one could argue that the Model Listener should have been used in the Flash-limited E-bike demo for updating values such as speed and power.

However, a much better and more realistic MVP example is available in TouchGFX Designer: the HVAC IoT Demo.

JohanAstrup_0-1744286960889.png

This demo was originally designed to run on the STM32H7S78-DK board in an IoT setup, where it would receive data from AWS through a FreeRTOS thread. However, in the version available in TouchGFX Designer, this has been replaced by a data simulator located in gui\src\common\hvac.cpp.

Let's go through how the kitchen temperature is updated in relation to the MainView in the demo as an example:

1. In Model::tick(), the Model::checkForIncomingData() function is called, which checks if there is new incoming data through the xHVAC_ReceiveFromController(&incomingEvent, 0) function.

2. The current kitchen temperature is then stored in the Model.

3. The virtual function updateTemperature() in the Model Listener is called.

4. In MainPresenter.hpp a virtual updateTemperature() function is declared. Any presenter that wants to subscribe to this function being called must declare this function.

5. In MainPresenter.cpp, updateTemperature() is defined as this:

void MainPresenter::updateTemperature(Rooms roomId, float temperature)
{
    view.updateRoomTemperature(roomId, temperature);
}​

6. updateRoomTemperature() that is called in step 5 is implemented in MainView.cpp

void MainView::updateRoomTemperature(Rooms roomId, float temperature)
{
    switch (roomId)
    {
    case KITCHEN:
        kitchenCardContainer.setTemperature(temperature, presenter->getIsFahrenheit());
        break;
    ...
    }
}​

7. And thereby the actual value in the view is updated.

However, the steps above only address the scenario where the kitchen temperature has changed. What should we do when we enter the view and have not yet received the first new kitchen temperature? In this case, we use a getter function. The flow is illustrated below.

1. In MainView::setupScreen(), MainView::setTemperatures() is called.

2. MainView::setTemperatures() calls presenter->getRoomTemeprature() to set the shown kitchen temperature accordingly

// Set temperatures in correct unit
kitchenCardContainer.setTemperature(presenter->getRoomTemperature(KITCHEN), presenter->getIsFahrenheit());​

3. MainPresenter::getRoomTemperature() calls Model::getRoomTemperature()

float MainPresenter::getRoomTemperature(Rooms roomId)
{
    return model->getRoomTemperature(roomId);
}​

4. Model::getRoomTemperature() returns the saved value of kitchenTemperature

float Model::getRoomTemperature(Rooms roomId)
{
    switch (roomId)
    {
    case KITCHEN:
        return kitchenTemperature;
        break;
    ...
    }
}​

5. And thus, the value is returned to the view.

I hope this provides a clearer overview of the Model-View-Presenter (MVP) structure and highlights why both the Model and the Model Listener are very valuable components. The Model cannot update the presenter/view by itself; this needs to go through the Model Listener.

I encourage you to explore the entire demo. Additionally, you can check out the example called "Understanding Application Structure," which is also available in TouchGFX Designer.

JohanAstrup_1-1744290342242.png

Best regards,
Johan

View solution in original post

2 REPLIES 2
JohanAstrup
ST Employee

Hello Nico.

I agree that the Flash-limited E-bike demo is not the best example to showcase the Model-View-Presenter structure. This is primarily due to the simplicity of the data transferred between the Model and the View.

In general, you can follow these guidelines:

  1. If the Model needs to notify a View that some Model data has been updated, you should use the Model Listener.
  2. If the View needs to fetch or set a variable from the Model, you should use normal getter and setter functions.

Based on these guidelines, one could argue that the Model Listener should have been used in the Flash-limited E-bike demo for updating values such as speed and power.

However, a much better and more realistic MVP example is available in TouchGFX Designer: the HVAC IoT Demo.

JohanAstrup_0-1744286960889.png

This demo was originally designed to run on the STM32H7S78-DK board in an IoT setup, where it would receive data from AWS through a FreeRTOS thread. However, in the version available in TouchGFX Designer, this has been replaced by a data simulator located in gui\src\common\hvac.cpp.

Let's go through how the kitchen temperature is updated in relation to the MainView in the demo as an example:

1. In Model::tick(), the Model::checkForIncomingData() function is called, which checks if there is new incoming data through the xHVAC_ReceiveFromController(&incomingEvent, 0) function.

2. The current kitchen temperature is then stored in the Model.

3. The virtual function updateTemperature() in the Model Listener is called.

4. In MainPresenter.hpp a virtual updateTemperature() function is declared. Any presenter that wants to subscribe to this function being called must declare this function.

5. In MainPresenter.cpp, updateTemperature() is defined as this:

void MainPresenter::updateTemperature(Rooms roomId, float temperature)
{
    view.updateRoomTemperature(roomId, temperature);
}​

6. updateRoomTemperature() that is called in step 5 is implemented in MainView.cpp

void MainView::updateRoomTemperature(Rooms roomId, float temperature)
{
    switch (roomId)
    {
    case KITCHEN:
        kitchenCardContainer.setTemperature(temperature, presenter->getIsFahrenheit());
        break;
    ...
    }
}​

7. And thereby the actual value in the view is updated.

However, the steps above only address the scenario where the kitchen temperature has changed. What should we do when we enter the view and have not yet received the first new kitchen temperature? In this case, we use a getter function. The flow is illustrated below.

1. In MainView::setupScreen(), MainView::setTemperatures() is called.

2. MainView::setTemperatures() calls presenter->getRoomTemeprature() to set the shown kitchen temperature accordingly

// Set temperatures in correct unit
kitchenCardContainer.setTemperature(presenter->getRoomTemperature(KITCHEN), presenter->getIsFahrenheit());​

3. MainPresenter::getRoomTemperature() calls Model::getRoomTemperature()

float MainPresenter::getRoomTemperature(Rooms roomId)
{
    return model->getRoomTemperature(roomId);
}​

4. Model::getRoomTemperature() returns the saved value of kitchenTemperature

float Model::getRoomTemperature(Rooms roomId)
{
    switch (roomId)
    {
    case KITCHEN:
        return kitchenTemperature;
        break;
    ...
    }
}​

5. And thus, the value is returned to the view.

I hope this provides a clearer overview of the Model-View-Presenter (MVP) structure and highlights why both the Model and the Model Listener are very valuable components. The Model cannot update the presenter/view by itself; this needs to go through the Model Listener.

I encourage you to explore the entire demo. Additionally, you can check out the example called "Understanding Application Structure," which is also available in TouchGFX Designer.

JohanAstrup_1-1744290342242.png

Best regards,
Johan

nico23
Senior

Hi @JohanAstrup that made a lot of sense, thanks a lot for the explanation

Also, I will look for sure into the IoT example

Thanks again, cheers