202 lines
7.6 KiB
C++
202 lines
7.6 KiB
C++
/**
|
|
* @file State.h
|
|
* @brief Defines the abstract base class for all device states.
|
|
* @author Emmanuel Hernandez Cruz
|
|
* @date 2025-09-04
|
|
*
|
|
* This file contains the definition of the abstract State class, which is a base
|
|
* to implement the State Pattern. Concrete states (Standby, Running, Random, Fail, etc.)
|
|
* will inherit from this class.
|
|
*/
|
|
#ifndef State_h
|
|
#define State_h
|
|
#include <string>
|
|
#include <Arduino.h>
|
|
#include <map>
|
|
#include "Strategies/Strategy_PID.h"
|
|
#include "Categories/ModbusFloatDecorator.h"
|
|
#include "Categories/ModbusPoint.h"
|
|
|
|
// Forward Declarations
|
|
template<typename T>class Equipment;
|
|
class Strategy_Behavior;
|
|
|
|
/**
|
|
* @class State
|
|
* @brief Abstract base class for a state in the State design pattern.
|
|
*
|
|
* This class defines the interface for all concrete states. It manages a
|
|
* collection of "strategies" that define how Modbus points behave while the
|
|
* equipment is in this state.
|
|
*/
|
|
template<typename T>
|
|
class State{
|
|
public:
|
|
/**
|
|
* @brief Virtual destructor.
|
|
* Cleans up all associated Strategy_Behavior objects.
|
|
*/
|
|
virtual ~State();
|
|
|
|
/**
|
|
* @brief Executes the state's logic for one update cycle.
|
|
* This method applies the state's strategies and checks for transitions.
|
|
* @param equipment Pointer to the Equipment instance.
|
|
* @return A pointer to a new State if a transition should occur, otherwise nullptr.
|
|
*/
|
|
virtual State* update(Equipment<T>* equipment) = 0;
|
|
/**
|
|
* @brief Logic to execute once when entering this state.
|
|
* @param equipment Pointer to the Equipment instance.
|
|
*/
|
|
virtual void enterState(Equipment<T>* equipment) {}
|
|
/**
|
|
* @brief Logic to execute once when exiting this state.
|
|
* @param equipment Pointer to the Equipment instance.
|
|
*/
|
|
virtual void exitState(Equipment<T>* equipment) {}
|
|
/**
|
|
* @brief Applies all registered strategies for the current state.
|
|
* @param equipment Pointer to the Equipment instance.
|
|
*/
|
|
virtual void _applyStrategies(Equipment<T>* equipment);
|
|
|
|
|
|
protected:
|
|
/**
|
|
* @brief Gets the value of a Modbus point, handling float types correctly.
|
|
* This is a helper function to safely read a value from a point, whether it's
|
|
* a standard integer register or a `ModbusFloatDecorator`.
|
|
* @param equipment Pointer to the Equipment instance.
|
|
* @param pointName The description key of the Modbus point to read.
|
|
* @return The value of the point as a float. Returns 0.0f if not found.
|
|
*/
|
|
float getPointValue(Equipment<T>* equipment, const std::string& pointName);
|
|
/**
|
|
* @brief Sets the value of a Modbus point, handling float types correctly.
|
|
* This is a helper function to safely write a value to a point, whether it's
|
|
* a standard integer register or a `ModbusFloatDecorator`.
|
|
* @param equipment Pointer to the Equipment instance.
|
|
* @param pointName The description key of the Modbus point to write to.
|
|
* @param value The float value to set. It will be rounded for integer points.
|
|
*/
|
|
void setPointValue(Equipment<T>* equipment, const std::string& pointName, float value);
|
|
/**
|
|
* @brief Adds a behavior strategy for a specific Modbus point in this state.
|
|
* @param pointDescription The description of the Modbus point to apply the strategy to.
|
|
* @param strategy A pointer to the Strategy_Behavior object. The State takes ownership.
|
|
*/
|
|
void addStrategy(const std::string& pointDescription, Strategy_Behavior* strategy);
|
|
std::map<std::string, Strategy_Behavior*> _strategies; /**< @brief Map of strategies active in this state, keyed by point description. */
|
|
};
|
|
|
|
template<typename T>
|
|
State<T>::~State(){
|
|
for (auto const& pair : this->_strategies) {
|
|
delete pair.second; // 'second' is the pointer to Strategy_Behavior
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Adds a new strategy to the state's behavior map.
|
|
*
|
|
* The State object takes ownership of the strategy pointer and will be
|
|
* responsible for its deletion.
|
|
*
|
|
* @param pointDescription The description of the Modbus point this strategy applies to.
|
|
* @param strategy A pointer to a Strategy_Behavior object.
|
|
*/
|
|
template<typename T>
|
|
void State<T>::addStrategy(const std::string& pointDescription, Strategy_Behavior* strategy){
|
|
this->_strategies[pointDescription] = strategy;
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the value of a Modbus point, correctly handling float types.
|
|
* This helper function checks if the point is a `ModbusFloatDecorator` and calls
|
|
* `getFloatValue()` if it is. Otherwise, it gets the standard integer value and
|
|
* casts it to a float.
|
|
* @param equipment Pointer to the Equipment instance.
|
|
* @param pointName The description key of the Modbus point.
|
|
* @return The point's value as a float. Returns 0.0f if the point is not found.
|
|
*/
|
|
template<typename T>
|
|
float State<T>::getPointValue(Equipment<T>* equipment, const std::string& pointName) {
|
|
ModbusPoint<T>* point = equipment->getModbusPoint(pointName);
|
|
if (!point) return 0.0f;
|
|
|
|
if (point->getType() == PointType::FLOAT) {
|
|
// If it's a float, cast and get the full float value
|
|
return static_cast<ModbusFloatDecorator<T>*>(point)->getFloatValue();
|
|
} else {
|
|
// Otherwise, get the standard integer value
|
|
return static_cast<float>(point->getValue());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Sets the value of a Modbus point, correctly handling float types.
|
|
* This helper function checks if the point is a `ModbusFloatDecorator` and calls
|
|
* `setFloatValue()` if it is. Otherwise, it rounds the float to the nearest
|
|
* integer and calls the standard `setValue()`.
|
|
* @param equipment Pointer to the Equipment instance.
|
|
* @param pointName The description key of the Modbus point.
|
|
* @param value The value to set.
|
|
*/
|
|
template<typename T>
|
|
void State<T>::setPointValue(Equipment<T>* equipment, const std::string& pointName, float value) {
|
|
ModbusPoint<T>* point = equipment->getModbusPoint(pointName);
|
|
if (!point) return;
|
|
|
|
if (point->getType() == PointType::FLOAT) {
|
|
static_cast<ModbusFloatDecorator<T>*>(point)->setFloatValue(value);
|
|
} else {
|
|
point->setValue(round(value));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Applies all registered strategies for the current state.
|
|
*
|
|
* This helper method iterates through all strategies associated with this state.
|
|
* For each strategy that is ready to run (based on its internal timer), it
|
|
* retrieves the corresponding Modbus point and applies the new value.
|
|
* It handles both integer and float point types.
|
|
*
|
|
* @param equipment A pointer to the main Equipment object.
|
|
*/
|
|
template<typename T>
|
|
void State<T>::_applyStrategies(Equipment<T>* equipment) {
|
|
unsigned long currentTime = millis();
|
|
|
|
// Use the C++11 compatible for-loop for std::map
|
|
for (auto const& pair : this->_strategies) {
|
|
const std::string& description = pair.first;
|
|
Strategy_Behavior* strategy = pair.second;
|
|
|
|
if (strategy->isReady(currentTime)) {
|
|
ModbusPoint<T>* outputPoint = equipment->getModbusPoint(description);
|
|
if (!outputPoint) continue;
|
|
|
|
float inputValue;
|
|
|
|
if (strategy->isPID()) {
|
|
PIDStrategy* pid = static_cast<PIDStrategy*>(strategy);
|
|
inputValue = getPointValue(equipment, pid->getInputSensorName());
|
|
|
|
ModbusPoint<T>* PIDsetpoint = equipment->getModbusPoint(pid->getSetpointName());
|
|
if (PIDsetpoint) {
|
|
pid->setSetpoint(PIDsetpoint->getValue());
|
|
}
|
|
} else {
|
|
inputValue = getPointValue(equipment, description);
|
|
}
|
|
|
|
float newValue = strategy->execute(inputValue);
|
|
setPointValue(equipment, description, newValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|