feat(weak-reactions): brought weak reaction code up to a point where it will compile (NOT YET TESTED)

This commit is contained in:
2025-10-08 11:17:35 -04:00
parent 274c566726
commit 13e2ea9ffa
15 changed files with 1452 additions and 153 deletions

View File

@@ -5,6 +5,24 @@
#include <iostream>
namespace gridfire::trigger {
/**
* @brief Pretty-print a TriggerResult explanation tree to std::cout.
*
* Prints one line per node prefixed with Unicode bullets and indentation to visualize
* the explanation hierarchy. Each line shows [TRUE|FALSE], the node name, and description.
*
* @param result Root TriggerResult to print.
* @param indent Current indentation level (number of two-space indents); callers typically
* omit this parameter and let recursion handle it.
*
* @par Example
* @code
* using gridfire::trigger::TriggerResult;
* TriggerResult leaf{"A>5", "Threshold passed", true, {}};
* TriggerResult root{"AND", "Both conditions true", true, {leaf}};
* gridfire::trigger::printWhy(root);
* @endcode
*/
inline void printWhy(const TriggerResult& result, const int indent = 0) { // NOLINT(*-no-recursion)
const std::string prefix(indent * 2, ' ');
std::cout << prefix << "• [" << (result.value ? "TRUE" : "FALSE")

View File

@@ -5,21 +5,70 @@
#include <string>
namespace gridfire::trigger {
/**
* @brief Generic trigger interface for signaling events/conditions during integration.
*
* A Trigger encapsulates a condition evaluated against a user-defined context struct
* (TriggerContextStruct). The interface supports:
* - check(): evaluate the current condition without mutating external state
* - update(): feed the context to update internal state (counters, sliding windows, etc.)
* - reset(): clear internal state and counters
* - name()/describe(): human-readable identification and textual description
* - why(): structured explanation (tree of TriggerResult) of the last evaluation
* - numTriggers()/numMisses(): simple counters for diagnostics
*
* Logical compositions (AND/OR/NOT/EveryNth) are provided in trigger_logical.h and implement
* this interface for any TriggerContextStruct.
*
* @tparam TriggerContextStruct User-defined context passed to triggers (e.g., timestep info).
*/
template <typename TriggerContextStruct>
class Trigger {
public:
/**
* @brief Virtual destructor for polymorphic use.
*/
virtual ~Trigger() = default;
/**
* @brief Evaluate the trigger condition against the provided context.
* @param ctx Context snapshot (immutable view) used to evaluate the condition.
* @return true if the condition is satisfied; false otherwise.
*/
virtual bool check(const TriggerContextStruct& ctx) const = 0;
/**
* @brief Update any internal state with the given context (e.g., counters, windows).
* @param ctx Context snapshot used to update state.
*/
virtual void update(const TriggerContextStruct& ctx) = 0;
/**
* @brief Reset internal state and diagnostics counters.
*/
virtual void reset() = 0;
/**
* @brief Short, stable name for this trigger (suitable for logs/UI).
*/
[[nodiscard]] virtual std::string name() const = 0;
/**
* @brief Human-readable description of this trigger's logic.
*/
[[nodiscard]] virtual std::string describe() const = 0;
/**
* @brief Explain why the last evaluation would be true/false in a structured way.
* @param ctx Context snapshot for the explanation.
* @return A TriggerResult tree with a boolean value and per-cause details.
*/
[[nodiscard]] virtual TriggerResult why(const TriggerContextStruct& ctx) const = 0;
/**
* @brief Total number of times this trigger evaluated to true since last reset.
*/
[[nodiscard]] virtual size_t numTriggers() const = 0;
/**
* @brief Total number of times this trigger evaluated to false since last reset.
*/
[[nodiscard]] virtual size_t numMisses() const = 0;
};
}

View File

@@ -6,24 +6,75 @@
#include <string>
#include <vector>
#include <memory>
#include <stdexcept>
namespace gridfire::trigger {
/**
* @file trigger_logical.h
* @brief Combinators for composing triggers with boolean logic (AND/OR/NOT/EveryNth).
*
* These templates wrap any Trigger<Context> and provide convenient composition. They also
* maintain simple hit/miss counters and implement short-circuit logic in check() and why().
*/
template <typename TriggerContextStruct>
class LogicalTrigger : public Trigger<TriggerContextStruct> {};
/**
* @class AndTrigger
* @brief Logical conjunction of two triggers with short-circuit evaluation.
*
* check(ctx) returns A.check(ctx) && B.check(ctx). The why(ctx) explanation short-circuits
* if A is false, avoiding evaluation of B. update(ctx) calls update() on both A and B.
*
* Counters (mutable) are incremented inside const check(): m_hits on true; m_misses on false;
* m_updates on each update(); m_resets on reset().
*
* @par Example
* @code
* auto t = AndTrigger(ctxA, ctxB);
* if (t.check(ctx)) { (void)ctx; }
* @endcode
*/
template <typename TriggerContextStruct>
class AndTrigger final : public LogicalTrigger<TriggerContextStruct> {
public:
/**
* @brief Construct AND from two triggers (ownership transferred).
*/
AndTrigger(std::unique_ptr<Trigger<TriggerContextStruct>> A, std::unique_ptr<Trigger<TriggerContextStruct>> B);
~AndTrigger() override = default;
/**
* @brief Evaluate A && B; increments hit/miss counters.
*/
bool check(const TriggerContextStruct& ctx) const override;
/**
* @brief Update both sub-triggers and increment update counter.
*/
void update(const TriggerContextStruct& ctx) override;
/**
* @brief Reset both sub-triggers and local counters.
*/
void reset() override;
/**
* @brief Human-readable name.
*/
std::string name() const override;
/**
* @brief Structured explanation; short-circuits on A=false.
*/
TriggerResult why(const TriggerContextStruct& ctx) const override;
/**
* @brief Description expression e.g. "(A) AND (B)".
*/
std::string describe() const override;
/**
* @brief Number of true evaluations since last reset.
*/
size_t numTriggers() const override;
/**
* @brief Number of false evaluations since last reset.
*/
size_t numMisses() const override;
private:
std::unique_ptr<Trigger<TriggerContextStruct>> m_A;
@@ -35,6 +86,13 @@ namespace gridfire::trigger {
mutable size_t m_resets = 0;
};
/**
* @class OrTrigger
* @brief Logical disjunction of two triggers with short-circuit evaluation.
*
* check(ctx) returns A.check(ctx) || B.check(ctx). why(ctx) returns early when A is true.
* update(ctx) calls update() on both A and B. Counters behave as in AndTrigger.
*/
template <typename TriggerContextStruct>
class OrTrigger final : public LogicalTrigger<TriggerContextStruct> {
public:
@@ -60,6 +118,13 @@ namespace gridfire::trigger {
mutable size_t m_resets = 0;
};
/**
* @class NotTrigger
* @brief Logical negation of a trigger.
*
* check(ctx) returns !A.check(ctx). why(ctx) explains the inverted condition. Counter
* semantics match the other logical triggers.
*/
template <typename TriggerContextStruct>
class NotTrigger final : public LogicalTrigger<TriggerContextStruct> {
public:
@@ -86,6 +151,15 @@ namespace gridfire::trigger {
};
/**
* @class EveryNthTrigger
* @brief Pass-through trigger that fires every Nth time its child trigger is true.
*
* On update(ctx), increments an internal counter when A.check(ctx) is true. check(ctx)
* returns true only when A.check(ctx) is true and the internal counter is a multiple of N.
*
* @throws std::invalid_argument When constructed with N==0.
*/
template <typename TriggerContextStruct>
class EveryNthTrigger final : public LogicalTrigger<TriggerContextStruct> {
public:

View File

@@ -4,10 +4,26 @@
#include <string>
namespace gridfire::trigger {
/**
* @file trigger_result.h
* @brief Structured explanation node for trigger evaluations.
*
* TriggerResult represents a tree describing why a trigger evaluated to true/false.
* Each node contains a boolean value, a short name, a human-readable description,
* and optional nested causes for composite triggers (e.g., AND/OR/NOT).
*
* @par Example
* @code
* // Produce a result and pretty-print it
* gridfire::trigger::TriggerResult r{"A>5", "Threshold passed", true, {}};
* // See procedures/trigger_pprint.h for printWhy()
* // gridfire::trigger::printWhy(r);
* @endcode
*/
struct TriggerResult {
std::string name;
std::string description;
bool value;
std::vector<TriggerResult> causes;
std::string name; ///< Short identifier for the condition (e.g., "Temperature Rise").
std::string description; ///< Human-readable reason summarizing the outcome at this node.
bool value; ///< Evaluation result for this node (true/false).
std::vector<TriggerResult> causes; ///< Sub-reasons for composite triggers.
};
}