#pragma once #include #include #include #include #include using json = nlohmann::json; struct ValvePairModel : IModel { IModel* action = nullptr; std::string actionModelName; int on_delay_ticks; int off_delay_ticks; int delay_jitter_ticks; float flash_prob; float delay_over_prob; int delay_over_ticks; enum State { IDLE_LOW, WAITING_RISE, HIGH, WAITING_FALL }; State state = IDLE_LOW; int delay_counter = 0; bool prev_action = false; ValvePairModel(const json& params, float) : on_delay_ticks(params.value("on_delay_ms", 200) / 20) , off_delay_ticks(params.value("off_delay_ms", 150) / 20) , delay_jitter_ticks(params.value("delay_jitter_ms", 0) / 20) , flash_prob(params.value("flash_prob", 0.0f)) , delay_over_prob(params.value("delay_over_prob", 0.0f)) , delay_over_ticks(params.value("delay_over_ms", 0) / 20) {} void linkPeers(ModelRegistry& reg) override; bool evaluateBool(size_t t) override { if (!action) return false; bool action_now = action->evaluateBool(t); switch (state) { case IDLE_LOW: if (!prev_action && action_now) { bool over = ((double)rand() / RAND_MAX) < delay_over_prob; int base = over ? delay_over_ticks : on_delay_ticks; delay_counter = base + (delay_jitter_ticks ? rand() % (delay_jitter_ticks + 1) : 0); state = WAITING_RISE; } prev_action = action_now; break; case WAITING_RISE: if (delay_counter > 0) { delay_counter--; } else { state = HIGH; } break; case HIGH: if (prev_action && !action_now) { bool over = ((double)rand() / RAND_MAX) < delay_over_prob; int base = over ? delay_over_ticks : off_delay_ticks; delay_counter = base + (delay_jitter_ticks ? rand() % (delay_jitter_ticks + 1) : 0); state = WAITING_FALL; } else if (((double)rand() / RAND_MAX) < flash_prob) { return false; } prev_action = action_now; break; case WAITING_FALL: if (delay_counter > 0) { delay_counter--; } else { state = IDLE_LOW; } break; } return state == HIGH || state == WAITING_FALL; } void reset() override { state = IDLE_LOW; prev_action = false; } }; // linkPeers is defined in ModelRegistry.cc to avoid circular include // (ValvePairModel needs ModelRegistry, ModelRegistry includes ValvePairModel)