firmware-base/src/profiles/SignalPlot.h
2025-06-03 15:16:03 +02:00

205 lines
6.8 KiB
C++

#ifndef SIGNAL_PLOT_H
#define SIGNAL_PLOT_H
#include "PlotBase.h"
#include <modbus/ModbusTCP.h>
#include <modbus/ModbusTypes.h>
#define MAX_SIGNAL_POINTS 20
// --- Modbus Register Definitions ---
enum class SignalPlotRegisterOffset : uint16_t
{
STATUS = 0,
DURATION_LW = 1,
DURATION_HW = 2,
ELAPSED_LW = 3,
ELAPSED_HW = 4,
COMMAND = 5,
ENABLE_CMD = 6,
_COUNT
};
const uint16_t SIGNAL_PLOT_REGISTER_COUNT = static_cast<uint16_t>(SignalPlotRegisterOffset::_COUNT);
enum class SignalPlotCommand : uint16_t
{
NONE = 0,
START = 1,
STOP = 2,
PAUSE = 3,
RESUME = 4
};
enum class E_SIGNAL_TYPE : int16_t
{
NONE = 0,
MB_WRITE_COIL = 1,
MB_WRITE_HOLDING_REGISTER = 2,
CALL_METHOD = 3,
CALL_FUNCTION = 4,
CALL_REST = 5,
GPIO_WRITE = 6,
USER_DEFINED = 7
};
// New enum for GPIO Write Modes
enum class E_GpioWriteMode : int16_t {
DIGITAL = 0,
ANALOG_PWM = 1
// Add other modes like TONE, SERVO if ever needed
};
enum class E_SIGNAL_STATE : int16_t
{
STATE_NONE = 0, // not hit yet
STATE_ERROR = 1, // error
STATE_ON = 2, // on - has been hit
STATE_OFF = 3, // off - disabled by user
STATE_CUSTOM_1 = 100 // custom
};
struct S_SignalControlPoint
{
uint8_t id;
uint32_t time;
E_SIGNAL_STATE state;
E_SIGNAL_TYPE type;
String name;
String description;
/**
* @brief Argument 0, meaning depends on E_SIGNAL_TYPE.
* - MB_WRITE_COIL: Modbus coil address (uint16_t).
* - MB_WRITE_HOLDING_REGISTER: Modbus register address (uint16_t).
* - GPIO_WRITE: GPIO pin number (uint8_t).
* - CALL_METHOD: Component ID (ushort) // TODO: not implemented yet
* - CALL_FUNCTION: Function ID (ushort) // TODO: not implemented yet
* - CALL_REST: Not used directly, path/params likely in name/description or separate storage. // TODO: not implemented yet
* - USER_DEFINED: User-specific. // TODO: not implemented yet
*/
int16_t arg_0;
/**
* @brief Argument 1, meaning depends on E_SIGNAL_TYPE.
* - MB_WRITE_COIL: Coil value (0 for OFF, 1 for ON).
* - MB_WRITE_HOLDING_REGISTER: Value to write to register (int16_t).
* - GPIO_WRITE: Write mode (see E_GpioWriteMode: DIGITAL = 0, ANALOG_PWM = 1).
* - CALL_METHOD: Method index/ID. // TODO: not implemented yet
* - CALL_FUNCTION: Not used typically, or first param. // TODO: not implemented yet
* - USER_DEFINED: User-specific. // TODO: not implemented yet
*/
int16_t arg_1;
/**
* @brief Argument 2, meaning depends on E_SIGNAL_TYPE.
* - GPIO_WRITE: Value to write (e.g., 0/1 for digital; 0-255 for analog/PWM).
* - CALL_METHOD: First method parameter (if any). // TODO: not implemented yet
* - CALL_FUNCTION: Second param / etc. // TODO: not implemented yet
* - USER_DEFINED: User-specific. // TODO: not implemented yet
* - Others: Not used (typically).
*/
int16_t arg_2;
/**
* @brief User pointer. For CALL_METHOD type, stores a command string (e.g. "<<1;2;64;reset:0:0>>").
* For other types, usage is type-specific.
*/
void *user;
S_SignalControlPoint() : id(0),
time(0),
state(E_SIGNAL_STATE::STATE_NONE),
type(E_SIGNAL_TYPE::NONE),
user(nullptr),
arg_0(0),
arg_1(0),
arg_2(0),
name(""),
description("") {}
};
/**
* @brief Represents a single signal with discrete state changes plotted over time.
* Inherits from PlotBase.
* Assumes control points in the source JSON are sorted chronologically by 'time'.
*/
class SignalPlot : public PlotBase
{
public:
SignalPlot(Component *owner, ushort slot, ushort componentId);
virtual ~SignalPlot() = default;
// --- Component Overrides (Optional) ---
short setup() override;
short loop() override;
void start() override;
// --- Profile Specific Methods ---
/**
* @brief Gets the active state for the signal at the current elapsed time.
*
* Finds the latest control point that occurred at or before
* the current time and returns its state.
*
* @param defaultState The state to return if the plot isn't running or no point has occurred yet.
* @return The determined state (SignalState).
*/
E_SIGNAL_STATE getState(E_SIGNAL_STATE defaultState = E_SIGNAL_STATE::STATE_OFF) const;
/**
* @brief Gets the user-defined integer associated with the active state at the current time.
*
* Finds the latest control point that occurred at or before
* the current time and returns its associated user value.
*
* @param defaultValue The value to return if the plot isn't running or no point has occurred yet.
* @return The determined user value (int16_t).
*/
int16_t getUserValue(int16_t defaultValue = 0) const;
// --- PlotBase Overrides ---
bool getCurrentControlPointInfo(uint8_t &outId, uint32_t &outTimeMs, int16_t &outValue, int16_t &outUser) const override;
/**
* @brief Loads the controlPoints array (discrete state changes) from the JSON config.
* Called by PlotBase::from.
* Assumes points are sorted chronologically by 'time' in the JSON.
* Expected JSON format within the config object:
* "controlPoints": [
* { "id": <uint8>, "time": <uint32>, "state": <int16>, "user": <int16> },
* ...
* ]
*/
bool load(const JsonObject &config) override;
// --- Modbus Overrides ---
void mb_tcp_register(ModbusTCP *manager) const override;
ModbusBlockView *mb_tcp_blocks() const override;
short mb_tcp_read(MB_Registers *reg) override;
short mb_tcp_write(MB_Registers *reg, short value) override;
uint16_t mb_tcp_base_address() const override;
// --- Getters for control points ---
const S_SignalControlPoint* getControlPoints() const;
uint8_t getNumControlPoints() const;
protected:
// --- SignalPlot Slot ---
ushort slot;
S_SignalControlPoint _controlPoints[MAX_SIGNAL_POINTS];
uint8_t _numControlPoints;
// Helper to find the applicable control point
// Returns nullptr if no point is applicable yet
const S_SignalControlPoint *findActivePoint(uint32_t elapsedMs) const;
private:
void executeControlPointAction(uint8_t cpIndex);
ModbusTCP *modbusTCP;
MB_Registers _modbusBlocks[SIGNAL_PLOT_REGISTER_COUNT];
ModbusBlockView _modbusBlockView;
};
#endif // SIGNAL_PLOT_H