Implemented state change handling using a plugin mechanism for the new AppStateController class. This way, I now have a class that handles all state changes, combined with a loosely coupled plugin for it, which takes care of notifying an communicating the state changes.
This commit is contained in:
parent
4c5ea8c001
commit
02e5cc2e14
123
src/App/App.cpp
123
src/App/App.cpp
|
@ -10,9 +10,10 @@ namespace Dough
|
|||
|
||||
App::App() : config(),
|
||||
ui(onoffButtonInterruptCallback, setupButtonInterruptCallback),
|
||||
state(&ui),
|
||||
wifi(),
|
||||
mqtt(&wifi, mqttOnConnectCallback, mqttOnMessageCallback),
|
||||
statePlugin(&ui, &mqtt),
|
||||
state(&statePlugin),
|
||||
sensorControllerPlugin(&mqtt, &ui),
|
||||
distanceSensor(
|
||||
new DistanceSensor(), &sensorControllerPlugin,
|
||||
|
@ -23,10 +24,16 @@ namespace Dough
|
|||
humiditySensor(
|
||||
new HumiditySensor(), &sensorControllerPlugin,
|
||||
HUMIDITY_AVERAGE_STORAGE, HUMIDITY_MEASURE_INTERVAL, MINIMUM_PUBLISH_INTERVAL),
|
||||
_logger("APP") {}
|
||||
_logger("APP")
|
||||
{
|
||||
ui.onoffButton.onPress(handleOnoffButtonPress);
|
||||
ui.setupButton.onPress(handleSetupButtonPress);
|
||||
}
|
||||
|
||||
void App::setup()
|
||||
{
|
||||
Dough::Logger::setup();
|
||||
state.setup();
|
||||
ui.setup();
|
||||
wifi.setup();
|
||||
mqtt.setup();
|
||||
|
@ -37,93 +44,55 @@ namespace Dough
|
|||
|
||||
void App::loop()
|
||||
{
|
||||
if (_setupNetworkConnection())
|
||||
{
|
||||
ui.processButtonEvents();
|
||||
mqtt.procesIncomingsMessages();
|
||||
|
||||
switch (state.get())
|
||||
{
|
||||
case CONFIGURING:
|
||||
if (config.isOk())
|
||||
{
|
||||
state.startMeasurements();
|
||||
}
|
||||
break;
|
||||
case MEASURING:
|
||||
if (config.isOk())
|
||||
{
|
||||
temperatureSensor.loop();
|
||||
humiditySensor.loop();
|
||||
distanceSensor.loop();
|
||||
}
|
||||
else
|
||||
{
|
||||
state.startConfiguration();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the device is connected to the WiFi network and the MQTT broker.
|
||||
// If not, then try to setup the connection.
|
||||
// Returns true if the connection was established, false otherwise.
|
||||
bool App::_setupNetworkConnection()
|
||||
{
|
||||
static bool connected = false;
|
||||
|
||||
if (!wifi.isConnected())
|
||||
{
|
||||
state.setWiFiConnected(false);
|
||||
if (connected)
|
||||
{
|
||||
_logger.log("s", "ERROR - Connection to WiFi network lost! Reconnecting ...");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.log("s", "Connecting to the WiFi network ...");
|
||||
}
|
||||
connected = false;
|
||||
ui.notifyConnectingToWifi();
|
||||
state.setWiFiConnected(wifi.connect());
|
||||
return;
|
||||
}
|
||||
if (wifi.isConnected() && !mqtt.isConnected())
|
||||
if (!mqtt.isConnected())
|
||||
{
|
||||
state.setWiFiConnected(true);
|
||||
state.setMQTTConnected(false);
|
||||
if (connected)
|
||||
state.setMQTTConnected(mqtt.connect());
|
||||
return;
|
||||
}
|
||||
|
||||
ui.processButtonEvents();
|
||||
mqtt.procesIncomingsMessages();
|
||||
|
||||
switch (state.get())
|
||||
{
|
||||
case CONFIGURING:
|
||||
if (config.isOk())
|
||||
{
|
||||
_logger.log("s", "ERROR - Connection to the MQTT broker lost! Reconnecting ...");
|
||||
state.startMeasurements();
|
||||
}
|
||||
break;
|
||||
case MEASURING:
|
||||
if (config.isOk())
|
||||
{
|
||||
temperatureSensor.loop();
|
||||
humiditySensor.loop();
|
||||
distanceSensor.loop();
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.log("s", "Connecting to the MQTT broker ...");
|
||||
state.startConfiguration();
|
||||
}
|
||||
connected = false;
|
||||
ui.notifyConnectingToMQTT();
|
||||
state.setMQTTConnected(mqtt.connect());
|
||||
break;
|
||||
case CALIBRATING:
|
||||
delay(2000);
|
||||
state.pauseMeasurements();
|
||||
state.startMeasurements();
|
||||
break;
|
||||
case PAUSED:
|
||||
temperatureSensor.clearHistory();
|
||||
humiditySensor.clearHistory();
|
||||
distanceSensor.clearHistory();
|
||||
break;
|
||||
default:
|
||||
// NOOP
|
||||
break;
|
||||
}
|
||||
if (wifi.isConnected() && mqtt.isConnected())
|
||||
{
|
||||
state.setWiFiConnected(true);
|
||||
state.setMQTTConnected(true);
|
||||
if (!connected)
|
||||
{
|
||||
_logger.log("s", "Connection to MQTT broker established");
|
||||
ui.notifyConnected();
|
||||
ui.clearButtonEvents();
|
||||
connected = true;
|
||||
}
|
||||
}
|
||||
|
||||
return connected;
|
||||
}
|
||||
|
||||
void App::clearHistory()
|
||||
{
|
||||
temperatureSensor.clearHistory();
|
||||
humiditySensor.clearHistory();
|
||||
distanceSensor.clearHistory();
|
||||
}
|
||||
} // namespace Dough
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <Arduino.h>
|
||||
#include "App/AppStateController.h"
|
||||
#include "App/AppStateControllerPlugin.h"
|
||||
#include "UI/UI.h"
|
||||
#include "App/Configuration.h"
|
||||
#include "App/callbacks.h"
|
||||
|
@ -21,9 +22,10 @@ namespace Dough
|
|||
static App *Instance();
|
||||
Configuration config;
|
||||
UI ui;
|
||||
AppStateController state;
|
||||
WiFi wifi;
|
||||
MQTT mqtt;
|
||||
AppStateControllerPlugin statePlugin;
|
||||
AppStateController state;
|
||||
SensorControllerPlugin sensorControllerPlugin;
|
||||
SensorController distanceSensor;
|
||||
SensorController temperatureSensor;
|
||||
|
@ -31,12 +33,10 @@ namespace Dough
|
|||
|
||||
void setup();
|
||||
void loop();
|
||||
void clearHistory();
|
||||
|
||||
private:
|
||||
App();
|
||||
Logger _logger;
|
||||
bool _setupNetworkConnection();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,12 @@ namespace Dough
|
|||
AppStateController::AppStateController(AppStateControllerPluginBase *plugin) : _logger("STATE"),
|
||||
_plugin(plugin) {}
|
||||
|
||||
void AppStateController::setup()
|
||||
{
|
||||
// Trigger a state change event, so the plugin can act upon it.
|
||||
_plugin->onStateChange(get(), INITIALIZING);
|
||||
}
|
||||
|
||||
AppState AppStateController::get()
|
||||
{
|
||||
if (_wifiConnected != CONNECTED)
|
||||
|
@ -50,6 +56,14 @@ namespace Dough
|
|||
{
|
||||
if (connected)
|
||||
{
|
||||
// Trigger a connection established state, so the plugin can act
|
||||
// upon this specific event. At the end of this method, the status update
|
||||
// call will push the system to the next state.
|
||||
if (_mqttConnected != CONNECTED)
|
||||
{
|
||||
_appState = CONNECTION_ESTABLISHED;
|
||||
_plugin->onStateChange(get(), _appState);
|
||||
}
|
||||
_mqttConnected = CONNECTED;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace Dough
|
|||
CONNECTING_WIFI,
|
||||
MQTT_CONNECTION_LOST,
|
||||
CONNECTING_MQTT,
|
||||
CONNECTION_ESTABLISHED,
|
||||
CONFIGURING,
|
||||
MEASURING,
|
||||
CALIBRATING,
|
||||
|
@ -46,6 +47,7 @@ namespace Dough
|
|||
{
|
||||
public:
|
||||
AppStateController(AppStateControllerPluginBase *plugin);
|
||||
void setup();
|
||||
void setWiFiConnected(bool connected);
|
||||
void setMQTTConnected(bool connected);
|
||||
void startConfiguration();
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
#include "App/AppStateControllerPlugin.h"
|
||||
|
||||
namespace Dough
|
||||
{
|
||||
AppStateControllerPlugin::AppStateControllerPlugin(UI *ui, MQTT *mqtt) : _ui(ui),
|
||||
_mqtt(mqtt),
|
||||
_logger("STATE") {}
|
||||
|
||||
void AppStateControllerPlugin::onStateChange(AppState oldState, AppState newState)
|
||||
{
|
||||
switch (newState)
|
||||
{
|
||||
case INITIALIZING:
|
||||
_logger.log("s", "Initializing the device");
|
||||
break;
|
||||
case CONNECTING_WIFI:
|
||||
_logger.log("s", "Connecting to the WiFi network");
|
||||
_ui->notifyConnectingToWifi();
|
||||
break;
|
||||
case WIFI_CONNECTION_LOST:
|
||||
_logger.log("s", "ERROR - Connection to WiFi network lost! Reconnecting");
|
||||
_ui->notifyConnectingToWifi();
|
||||
break;
|
||||
case CONNECTING_MQTT:
|
||||
_logger.log("s", "Connecting to the MQTT broker");
|
||||
_ui->notifyConnectingToMQTT();
|
||||
break;
|
||||
case MQTT_CONNECTION_LOST:
|
||||
_logger.log("s", "ERROR - Connection to the MQTT broker lost! Reconnecting");
|
||||
_ui->notifyConnectingToMQTT();
|
||||
break;
|
||||
case CONNECTION_ESTABLISHED:
|
||||
_logger.log("s", "Connection to MQTT broker established");
|
||||
_ui->notifyConnected();
|
||||
_ui->clearButtonEvents();
|
||||
_mqtt->publish("state", "connected");
|
||||
break;
|
||||
case CONFIGURING:
|
||||
_logger.log("s", "Waiting for configuration");
|
||||
_ui->notifyWaitingForConfiguration();
|
||||
_mqtt->publish("state", "configuring");
|
||||
break;
|
||||
case MEASURING:
|
||||
_logger.log("s", "Starting measurements");
|
||||
_ui->notifyMeasurementsActive();
|
||||
_mqtt->publish("state", "measuring");
|
||||
break;
|
||||
case PAUSED:
|
||||
_logger.log("s", "Pausing measurements");
|
||||
_ui->notifyMeasurementsPaused();
|
||||
_mqtt->publish("state", "paused");
|
||||
break;
|
||||
case CALIBRATING:
|
||||
_logger.log("s", "Requested device calibration");
|
||||
_ui->notifyCalibrating();
|
||||
_mqtt->publish("state", "calibrating");
|
||||
break;
|
||||
default:
|
||||
_logger.log("sisi", "Unhandled state transfer: ", oldState, " -> ", newState);
|
||||
}
|
||||
}
|
||||
} // namespace Dough
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef DOUGH_APPSTATECONTROLLERPLUGIN_H
|
||||
#define DOUGH_APPSTATECONTROLLERPLUGIN_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "App/AppStateController.h"
|
||||
#include "Network/MQTT.h"
|
||||
#include "UI/Logger.h"
|
||||
#include "UI/UI.h"
|
||||
|
||||
namespace Dough
|
||||
{
|
||||
class AppStateControllerPlugin : public AppStateControllerPluginBase
|
||||
{
|
||||
public:
|
||||
AppStateControllerPlugin(UI *ui, MQTT *mqtt);
|
||||
virtual void onStateChange(AppState oldState, AppState newState);
|
||||
private:
|
||||
UI *_ui;
|
||||
MQTT *_mqtt;
|
||||
Logger _logger;
|
||||
};
|
||||
} // namespace Dough
|
||||
|
||||
#endif
|
|
@ -37,6 +37,24 @@ void setupButtonInterruptCallback()
|
|||
Dough::App::Instance()->ui.setupButton.handleButtonState();
|
||||
}
|
||||
|
||||
void handleOnoffButtonPress()
|
||||
{
|
||||
auto app = Dough::App::Instance();
|
||||
if (app->state.get() == Dough::MEASURING)
|
||||
{
|
||||
app->state.pauseMeasurements();
|
||||
}
|
||||
else if (app->state.get() == Dough::PAUSED)
|
||||
{
|
||||
app->state.resumeMeasurements();
|
||||
}
|
||||
}
|
||||
|
||||
void handleSetupButtonPress()
|
||||
{
|
||||
Dough::App::Instance()->state.startCalibration();
|
||||
}
|
||||
|
||||
// This callback is called when the TC4 timer from the UI code hits an overflow
|
||||
// interrupt. It is defined outside the Dough namespace, because TC4_Handler is
|
||||
// a hard-coded, root namespace function name.
|
||||
|
|
|
@ -17,5 +17,7 @@ void mqttOnMessageCallback(String &topic, String &payload);
|
|||
// Callbacks from the Dough::UI module.
|
||||
void onoffButtonInterruptCallback();
|
||||
void setupButtonInterruptCallback();
|
||||
void handleOnoffButtonPress();
|
||||
void handleSetupButtonPress();
|
||||
|
||||
#endif
|
|
@ -106,7 +106,7 @@ namespace Dough
|
|||
{
|
||||
_state = UP;
|
||||
}
|
||||
else if (_state == UP && buttonIsDown)
|
||||
else if (_state == UP && buttonIsDown && interval)
|
||||
{
|
||||
_state = DOWN;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef DOUGH_BUTTON_H
|
||||
#define DOUGH_BUTTON_H
|
||||
|
||||
#define BUTTON_DEBOUNCE_DELAY 10
|
||||
#define BUTTON_DEBOUNCE_DELAY 50
|
||||
#define BUTTON_LONGPRESS_DELAY 500
|
||||
|
||||
#include <Arduino.h>
|
||||
|
|
|
@ -2,14 +2,13 @@
|
|||
|
||||
namespace Dough
|
||||
{
|
||||
UI::UI(
|
||||
ButtonISR onoffButtonISR,
|
||||
ButtonISR setupButtonISR) : onoffButton(ONOFF_BUTTON_PIN, onoffButtonISR),
|
||||
setupButton(SETUP_BUTTON_PIN, setupButtonISR),
|
||||
_ledBuiltin(LED_BUILTIN),
|
||||
_led1(LED1_PIN),
|
||||
_led2(LED2_PIN),
|
||||
_led3(LED3_PIN) {}
|
||||
UI::UI(ButtonISR onoffButtonISR,
|
||||
ButtonISR setupButtonISR) : onoffButton(ONOFF_BUTTON_PIN, onoffButtonISR),
|
||||
setupButton(SETUP_BUTTON_PIN, setupButtonISR),
|
||||
_ledBuiltin(LED_BUILTIN),
|
||||
_led1(LED1_PIN),
|
||||
_led2(LED2_PIN),
|
||||
_led3(LED3_PIN) {}
|
||||
|
||||
void UI::setup()
|
||||
{
|
||||
|
@ -112,13 +111,6 @@ namespace Dough
|
|||
_led3.loop();
|
||||
}
|
||||
|
||||
void UI::onStateChange(AppState oldState, AppState newState)
|
||||
{
|
||||
Serial.print(oldState); // TODO
|
||||
Serial.print(" to ");
|
||||
Serial.println(newState); // TODO
|
||||
}
|
||||
|
||||
void UI::notifyConnectingToWifi()
|
||||
{
|
||||
_led1.blink()->slow();
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include <Arduino.h>
|
||||
#include <stdarg.h>
|
||||
#include "App/AppStateController.h"
|
||||
#include "UI/Button.h"
|
||||
#include "UI/LED.h"
|
||||
#include "config.h"
|
||||
|
@ -12,7 +11,7 @@ namespace Dough
|
|||
{
|
||||
// This class groups all user interface functionality: serial logging,
|
||||
// LEDs and buttons.
|
||||
class UI : public AppStateControllerPluginBase
|
||||
class UI
|
||||
{
|
||||
public:
|
||||
UI(ButtonISR onoffButtonISR, ButtonISR setupButtonISR);
|
||||
|
@ -24,7 +23,6 @@ namespace Dough
|
|||
void updateLEDs();
|
||||
void resume();
|
||||
void suspend();
|
||||
virtual void onStateChange(AppState oldState, AppState newState);
|
||||
void notifyConnectingToWifi();
|
||||
void notifyConnectingToMQTT();
|
||||
void notifyConnected();
|
||||
|
|
92
src/main.cpp
92
src/main.cpp
|
@ -1,99 +1,13 @@
|
|||
#include "main.h"
|
||||
|
||||
DoughBoyState state = CONFIGURING;
|
||||
Dough::Logger logger("MAIN");
|
||||
|
||||
void setup()
|
||||
{
|
||||
Dough::Logger::setup();
|
||||
logger.log("s", "Initializing device");
|
||||
auto app = Dough::App::Instance();
|
||||
app->setup();
|
||||
app->ui.onoffButton.onPress(handleOnoffButtonPress);
|
||||
app->ui.setupButton.onPress(handleSetupButtonPress);
|
||||
logger.log("s", "Initialization completed, starting device");
|
||||
Dough::App::Instance()->setup();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
auto app = Dough::App::Instance();
|
||||
app->loop();
|
||||
|
||||
// if (state == CONFIGURING)
|
||||
// {
|
||||
// if (app->config.isOk())
|
||||
// {
|
||||
// setStateToMeasuring();
|
||||
// }
|
||||
// }
|
||||
// else if (state == MEASURING && !app->config.isOk())
|
||||
// {
|
||||
// setStateToConfiguring();
|
||||
// }
|
||||
// else if (state == MEASURING)
|
||||
// {
|
||||
// app->measure();
|
||||
// }
|
||||
// else if (state == CALIBRATING)
|
||||
// {
|
||||
// delay(3000);
|
||||
// setStateToPaused();
|
||||
// }
|
||||
// else if (state == PAUSED)
|
||||
// {
|
||||
// app->clearHistory();
|
||||
// }
|
||||
}
|
||||
|
||||
void handleOnoffButtonPress()
|
||||
{
|
||||
if (state == MEASURING)
|
||||
{
|
||||
setStateToPaused();
|
||||
}
|
||||
else if (state == PAUSED)
|
||||
{
|
||||
setStateToMeasuring();
|
||||
}
|
||||
}
|
||||
|
||||
void handleSetupButtonPress()
|
||||
{
|
||||
setStateToCalibrating();
|
||||
}
|
||||
|
||||
void setStateToConfiguring()
|
||||
{
|
||||
auto app = Dough::App::Instance();
|
||||
logger.log("s", "Waiting for configuration ...");
|
||||
state = CONFIGURING;
|
||||
app->ui.notifyWaitingForConfiguration();
|
||||
app->mqtt.publish("state", "configuring");
|
||||
}
|
||||
|
||||
void setStateToMeasuring()
|
||||
{
|
||||
auto app = Dough::App::Instance();
|
||||
logger.log("s", "Starting measurements");
|
||||
state = MEASURING;
|
||||
app->ui.notifyMeasurementsActive();
|
||||
app->mqtt.publish("state", "measuring");
|
||||
}
|
||||
|
||||
void setStateToPaused()
|
||||
{
|
||||
auto app = Dough::App::Instance();
|
||||
logger.log("s", "Pausing measurements");
|
||||
state = PAUSED;
|
||||
app->ui.notifyMeasurementsPaused();
|
||||
app->mqtt.publish("state", "paused");
|
||||
}
|
||||
|
||||
void setStateToCalibrating()
|
||||
{
|
||||
auto app = Dough::App::Instance();
|
||||
logger.log("s", "Requested device calibration");
|
||||
state = CALIBRATING;
|
||||
app->ui.notifyCalibrating();
|
||||
app->mqtt.publish("state", "calibrating");
|
||||
}
|
||||
Dough::App::Instance()->loop();
|
||||
}
|
22
src/main.h
22
src/main.h
|
@ -3,29 +3,9 @@
|
|||
|
||||
#include <Arduino.h>
|
||||
#include "App/App.h"
|
||||
#include "Sensors/HighLevel/TemperatureSensor.h"
|
||||
#include "Sensors/HighLevel/HumiditySensor.h"
|
||||
#include "Sensors/HighLevel/DistanceSensor.h"
|
||||
#include "Network/WiFi.h"
|
||||
#include "Network/MQTT.h"
|
||||
#include "App/AppStateController.h"
|
||||
#include "UI/Button.h"
|
||||
#include "UI/UI.h"
|
||||
#include "config.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CONFIGURING,
|
||||
MEASURING,
|
||||
PAUSED,
|
||||
CALIBRATING
|
||||
} DoughBoyState;
|
||||
|
||||
void handleOnoffButtonPress();
|
||||
void handleSetupButtonPress();
|
||||
|
||||
void setStateToConfiguring();
|
||||
void setStateToMeasuring();
|
||||
void setStateToPaused();
|
||||
void setStateToCalibrating();
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue