79 lines
2.5 KiB
C
79 lines
2.5 KiB
C
|
|
#pragma once
|
||
|
|
#include <TestProject/RNG/model/IModel.h>
|
||
|
|
#include <nlohmann/json.hpp>
|
||
|
|
#include <cstdlib>
|
||
|
|
#include <string>
|
||
|
|
#include <vector>
|
||
|
|
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);
|
||
|
|
|
||
|
|
bool rising_edge = !prev_action && action_now;
|
||
|
|
bool falling_edge = prev_action && !action_now;
|
||
|
|
prev_action = action_now;
|
||
|
|
|
||
|
|
switch (state) {
|
||
|
|
case IDLE_LOW:
|
||
|
|
if (rising_edge) {
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case WAITING_RISE:
|
||
|
|
if (delay_counter > 0) { delay_counter--; }
|
||
|
|
else { state = HIGH; }
|
||
|
|
break;
|
||
|
|
case HIGH:
|
||
|
|
if (falling_edge) {
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
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)
|