#include "fsm.h"
#include <why-irq.h>
#include <cassert>
#include <cstring>
FSM::FSM(
Why::TimeSpec interval,
int sensor_burning_irq, Switch& hard_alarm,
long soft_critical_temp, Switch& soft_alarm,
int operator_button_irq,
const std::string& can_iface,
int can_id,
Sensor& sensor, Switch& cooling,
long low_temp, long high_temp)
: _interval(interval),
_interval_timer([this](){_timer_tick();}),
_sensor_burning_irq(sensor_burning_irq),
_hard_alarm(hard_alarm),
_soft_critical_temp(soft_critical_temp),
_soft_alarm(soft_alarm),
_operator_button_irq(operator_button_irq),
_sensor(sensor),
_cooling(cooling),
_low_temp(low_temp),
_high_temp(high_temp),
_can_controller(can_iface),
_can_id(can_id),
_state(State::INIT)
{}
void FSM::start()
{
_lock.lock();
switch (_state) {
case State::INIT:
_interval_timer.start(Why::TimeSpec(0,1), _interval);
Why::IRQ::connect(_sensor_burning_irq, [this](int irqnum){_sensor_burning();});
Why::IRQ::connect(_operator_button_irq, [this](int irqnum){_operator_button_pressed();});
Why::IRQ::connect(CAN_JF0815_HW::IRQ_XMIT_DONE, [this](int irqnum){_can_xmit_done();});
_sensor.turn_on();
_state_change(State::RUNNING);
break;
case State::RUNNING:
assert(!"RUNNING");
break;
case State::OVERTEMP:
assert(!"OVERTEMP");
break;
case State::SENSORBURNING:
assert(!"SENSORBURNING");
break;
}
_lock.unlock();
}
void FSM::_timer_tick()
{
_lock.lock();
switch (_state) {
case State::INIT:
assert(!"INIT");
break;
case State::RUNNING: {
double temp = _sensor.get_temperature();
if (temp > _soft_critical_temp) {
_cooling.set_state(true);
_soft_alarm.set_state(true);
_state_change(State::OVERTEMP);
}
else if (temp > _high_temp)
_cooling.set_state(true);
else if (temp < _low_temp)
_cooling.set_state(false);
break;
}
case State::OVERTEMP: {
double temp = _sensor.get_temperature();
if (temp > _high_temp)
_cooling.set_state(true);
else if (temp < _low_temp)
_cooling.set_state(false);
break;
}
case State::SENSORBURNING:
break;
}
_lock.unlock();
}
void FSM::_sensor_burning()
{
_lock.lock();
switch (_state) {
case State::INIT:
assert(!"INIT");
break;
case State::RUNNING:
_sensor.turn_off();
_cooling.set_state(false);
_hard_alarm.set_state(true);
_state_change(State::SENSORBURNING);
break;
case State::OVERTEMP:
_sensor.turn_off();
_cooling.set_state(false);
_soft_alarm.set_state(false);
_hard_alarm.set_state(true);
_state_change(State::SENSORBURNING);
break;
case State::SENSORBURNING:
break;
}
_lock.unlock();
}
void FSM::_operator_button_pressed()
{
_lock.lock();
switch (_state) {
case State::INIT:
assert(!"INIT");
break;
case State::RUNNING:
break;
case State::OVERTEMP:
_soft_alarm.set_state(false);
_state_change(State::RUNNING);
break;
case State::SENSORBURNING:
break;
}
_lock.unlock();
}
void FSM::_state_change(State state) // <-- xmit/enqueue notifications
{ // change state
uint32_t notify_state;
switch (state) {
case State::INIT:
notify_state = 0;
break;
case State::RUNNING:
notify_state = 1;
break;
case State::OVERTEMP:
notify_state = 2;
break;
case State::SENSORBURNING:
notify_state = 3;
break;
}
CANFrame frame;
frame.id = _can_id;
frame.dlc = sizeof(notify_state);
memcpy(frame.data, ¬ify_state, sizeof(notify_state));
if (_can_controller.xmit_busy())
_pending_notifications.push_back(frame);
else
_can_controller.begin_xmit(frame);
_state = state;
}
void FSM::_can_xmit_done() // <-- send pending frame if any
{
_lock.lock();
if (_pending_notifications.size() > 0) {
CANFrame frame = _pending_notifications.front();
_pending_notifications.pop_front();
_can_controller.begin_xmit(frame);
}
_lock.unlock();
}