eive-obsw/mission/controller/ThermalController.h

339 lines
15 KiB
C
Raw Normal View History

2021-01-08 09:34:43 +01:00
#ifndef MISSION_CONTROLLER_THERMALCONTROLLER_H_
#define MISSION_CONTROLLER_THERMALCONTROLLER_H_
2023-07-03 16:55:45 +02:00
#include <bsp_q7s/core/defs.h>
2021-01-08 09:34:43 +01:00
#include <fsfw/controller/ExtendedControllerBase.h>
2023-01-17 15:55:36 +01:00
#include <fsfw/devicehandlers/DeviceHandlerThermalSet.h>
2022-06-17 08:31:36 +02:00
#include <fsfw/timemanager/Countdown.h>
2023-03-23 12:13:24 +01:00
#include <fsfw_hal/devicehandlers/devicedefinitions/gyroL3gHelpers.h>
#include <fsfw_hal/devicehandlers/devicedefinitions/mgmLis3Helpers.h>
2023-03-24 20:50:33 +01:00
#include <mission/acs/gyroAdisHelpers.h>
#include <mission/acs/imtqHelpers.h>
#include <mission/acs/rwHelpers.h>
#include <mission/acs/str/strHelpers.h>
2023-03-26 16:42:00 +02:00
#include <mission/acs/susMax1227Helpers.h>
2023-03-24 19:49:08 +01:00
#include <mission/com/syrlinksDefs.h>
2023-03-24 21:20:27 +01:00
#include <mission/controller/tcsDefs.h>
2023-03-26 16:42:00 +02:00
#include <mission/payload/payloadPcduDefinitions.h>
2023-03-26 16:13:54 +02:00
#include <mission/power/bpxBattDefs.h>
#include <mission/power/gsDefs.h>
2023-03-26 16:42:00 +02:00
#include <mission/tcs/HeaterHandler.h>
#include <mission/tcs/Max31865Definitions.h>
#include <mission/tcs/Tmp1075Definitions.h>
#include <mission/utility/trace.h>
2023-04-11 22:58:13 +02:00
#include <atomic>
2022-11-27 16:58:57 +01:00
#include <list>
2023-07-10 13:28:36 +02:00
#include <optional>
2022-11-27 16:58:57 +01:00
class ThermalController : public ExtendedControllerBase {
2022-01-17 15:58:27 +01:00
public:
2023-04-06 12:13:24 +02:00
static constexpr uint8_t SUBMODE_NO_HEATER_CTRL = 1;
2022-05-23 00:37:49 +02:00
static const uint16_t INVALID_TEMPERATURE = 999;
2022-11-27 16:58:57 +01:00
static const uint8_t NUMBER_OF_SENSORS = 16;
2023-04-04 15:09:51 +02:00
static constexpr int16_t SANITY_LIMIT_LOWER_TEMP = -80;
static constexpr int16_t SANITY_LIMIT_UPPER_TEMP = 160;
2022-05-23 00:37:49 +02:00
2023-07-06 22:06:54 +02:00
// 1 hour
2023-07-07 12:03:34 +02:00
static constexpr uint32_t DEFAULT_MAX_HEATER_ON_DURATION_MS = 60 * 60 * 1000;
static constexpr uint32_t MAX_HEATER_ON_DURATIONS_MS[8] = {// PLOC PROC board
DEFAULT_MAX_HEATER_ON_DURATION_MS,
// PCDU PDU
DEFAULT_MAX_HEATER_ON_DURATION_MS,
// ACS Board
DEFAULT_MAX_HEATER_ON_DURATION_MS,
// OBC Board
DEFAULT_MAX_HEATER_ON_DURATION_MS,
// Camera
DEFAULT_MAX_HEATER_ON_DURATION_MS,
// STR
DEFAULT_MAX_HEATER_ON_DURATION_MS,
// DRO
DEFAULT_MAX_HEATER_ON_DURATION_MS,
// S-Band
DEFAULT_MAX_HEATER_ON_DURATION_MS};
2023-07-06 22:06:54 +02:00
2023-04-11 22:58:13 +02:00
ThermalController(object_id_t objectId, HeaterHandler& heater,
const std::atomic_bool& tcsBoardShortUnavailable, bool pollPcdu1Tmp);
virtual ~ThermalController();
2021-01-08 09:34:43 +01:00
2022-02-17 16:29:22 +01:00
ReturnValue_t initialize() override;
protected:
2023-04-03 14:09:54 +02:00
struct HeaterContext {
public:
2023-04-06 12:13:24 +02:00
HeaterContext(heater::Switch switchNr, heater::Switch redundantSwitchNr,
2023-07-06 23:06:50 +02:00
const tcsCtrl::TempLimits& tempLimit)
2023-04-03 14:09:54 +02:00
: switchNr(switchNr), redSwitchNr(redundantSwitchNr), tempLimit(tempLimit) {}
bool doHeaterHandling = true;
2023-04-06 12:13:24 +02:00
heater::Switch switchNr;
2023-07-06 23:06:50 +02:00
heater::SwitchState switchState = heater::SwitchState::OFF;
2023-04-06 12:13:24 +02:00
heater::Switch redSwitchNr;
2023-07-06 23:06:50 +02:00
const tcsCtrl::TempLimits& tempLimit;
2023-04-03 14:09:54 +02:00
};
2023-07-06 23:06:50 +02:00
void performThermalModuleCtrl(const tcsCtrl::HeaterSwitchStates& heaterSwitchStates);
2023-02-06 17:10:09 +01:00
ReturnValue_t handleCommandMessage(CommandMessage* message) override;
void performControlOperation() override;
ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
2023-02-07 14:28:42 +01:00
LocalDataPoolManager& poolManager) override;
2023-02-06 17:10:09 +01:00
LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;
// Mode abstract functions
2023-02-06 17:10:09 +01:00
ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode,
2023-02-07 14:28:42 +01:00
uint32_t* msToReachTheMode) override;
private:
2023-03-16 18:28:51 +01:00
static const uint32_t INIT_DELAY = 1500;
2022-11-28 10:06:49 +01:00
static const uint32_t TEMP_OFFSET = 5;
2022-06-17 08:31:36 +02:00
enum class InternalState { STARTUP, INITIAL_DELAY, READY };
InternalState internalState = InternalState::STARTUP;
2022-11-24 16:40:59 +01:00
HeaterHandler& heaterHandler;
bool pollPcdu1Tmp;
2023-02-21 01:53:23 +01:00
tcsCtrl::SensorTemperatures sensorTemperatures;
tcsCtrl::SusTemperatures susTemperatures;
tcsCtrl::DeviceTemperatures deviceTemperatures;
tcsCtrl::HeaterInfo heaterInfo;
2023-07-06 23:06:50 +02:00
tcsCtrl::TcsCtrlInfo tcsCtrlInfo;
2023-01-17 15:45:54 +01:00
DeviceHandlerThermalSet imtqThermalSet;
// Temperature Sensors
2023-03-15 15:13:53 +01:00
MAX31865::PrimarySet maxSet0PlocHspd;
MAX31865::PrimarySet maxSet1PlocMissionBrd;
MAX31865::PrimarySet maxSet2PlCam;
MAX31865::PrimarySet maxSet3DacHspd;
MAX31865::PrimarySet maxSet4Str;
MAX31865::PrimarySet maxSet5Rw1MxMy;
MAX31865::PrimarySet maxSet6Dro;
MAX31865::PrimarySet maxSet7Scex;
MAX31865::PrimarySet maxSet8X8;
MAX31865::PrimarySet maxSet9Hpa;
MAX31865::PrimarySet maxSet10EbandTx;
MAX31865::PrimarySet maxSet11Mpa;
MAX31865::PrimarySet maxSet31865Set12;
MAX31865::PrimarySet maxSet13PlPcduHspd;
MAX31865::PrimarySet maxSet14TcsBrd;
MAX31865::PrimarySet maxSet15Imtq;
2023-01-09 11:57:48 +01:00
2022-11-14 10:04:41 +01:00
TMP1075::Tmp1075Dataset tmp1075SetTcs0;
TMP1075::Tmp1075Dataset tmp1075SetTcs1;
TMP1075::Tmp1075Dataset tmp1075SetPlPcdu0;
2023-01-09 11:57:48 +01:00
// damaged
TMP1075::Tmp1075Dataset* tmp1075SetPlPcdu1;
2022-11-14 10:04:41 +01:00
TMP1075::Tmp1075Dataset tmp1075SetIfBoard;
// SUS
2023-02-28 19:14:15 +01:00
susMax1227::SusDataset susSet0;
susMax1227::SusDataset susSet1;
susMax1227::SusDataset susSet2;
susMax1227::SusDataset susSet3;
susMax1227::SusDataset susSet4;
susMax1227::SusDataset susSet5;
susMax1227::SusDataset susSet6;
susMax1227::SusDataset susSet7;
susMax1227::SusDataset susSet8;
susMax1227::SusDataset susSet9;
susMax1227::SusDataset susSet10;
susMax1227::SusDataset susSet11;
2022-05-23 00:37:49 +02:00
2023-04-11 22:58:13 +02:00
// If the TCS board in unavailable, for example due to a recovery, skip
// some TCS controller tasks to avoid unnecessary events.
const std::atomic_bool& tcsBrdShortlyUnavailable = false;
2023-03-23 12:13:24 +01:00
lp_var_t<float> tempQ7s = lp_var_t<float>(objects::CORE_CONTROLLER, core::PoolIds::TEMPERATURE);
2023-04-06 22:35:23 +02:00
lp_var_t<int16_t> battTemp1 = lp_var_t<int16_t>(objects::BPX_BATT_HANDLER, bpxBat::BATT_TEMP_1);
lp_var_t<int16_t> battTemp2 = lp_var_t<int16_t>(objects::BPX_BATT_HANDLER, bpxBat::BATT_TEMP_2);
lp_var_t<int16_t> battTemp3 = lp_var_t<int16_t>(objects::BPX_BATT_HANDLER, bpxBat::BATT_TEMP_3);
lp_var_t<int16_t> battTemp4 = lp_var_t<int16_t>(objects::BPX_BATT_HANDLER, bpxBat::BATT_TEMP_4);
2023-03-23 12:13:24 +01:00
lp_var_t<int32_t> tempRw1 = lp_var_t<int32_t>(objects::RW1, rws::TEMPERATURE_C);
lp_var_t<int32_t> tempRw2 = lp_var_t<int32_t>(objects::RW2, rws::TEMPERATURE_C);
lp_var_t<int32_t> tempRw3 = lp_var_t<int32_t>(objects::RW3, rws::TEMPERATURE_C);
lp_var_t<int32_t> tempRw4 = lp_var_t<int32_t>(objects::RW4, rws::TEMPERATURE_C);
lp_var_t<float> tempStartracker =
lp_var_t<float>(objects::STAR_TRACKER, startracker::MCU_TEMPERATURE);
lp_var_t<float> tempSyrlinksPowerAmplifier =
lp_var_t<float>(objects::SYRLINKS_HANDLER, syrlinks::TEMP_POWER_AMPLIFIER);
lp_var_t<float> tempSyrlinksBasebandBoard =
lp_var_t<float>(objects::SYRLINKS_HANDLER, syrlinks::TEMP_BASEBAND_BOARD);
lp_var_t<int16_t> tempMgt = lp_var_t<int16_t>(objects::IMTQ_HANDLER, imtq::MCU_TEMPERATURE);
lp_vec_t<float, 3> tempAcu =
lp_vec_t<float, 3>(objects::ACU_HANDLER, ACU::pool::ACU_TEMPERATURES);
lp_var_t<float> tempPdu1 = lp_var_t<float>(objects::PDU1_HANDLER, PDU::pool::PDU_TEMPERATURE);
lp_var_t<float> tempPdu2 = lp_var_t<float>(objects::PDU2_HANDLER, PDU::pool::PDU_TEMPERATURE);
lp_var_t<float> temp1P60dock =
lp_var_t<float>(objects::P60DOCK_HANDLER, P60Dock::pool::P60DOCK_TEMPERATURE_1);
lp_var_t<float> temp2P60dock =
lp_var_t<float>(objects::P60DOCK_HANDLER, P60Dock::pool::P60DOCK_TEMPERATURE_2);
lp_var_t<float> tempGyro0 = lp_var_t<float>(objects::GYRO_0_ADIS_HANDLER, adis1650x::TEMPERATURE);
lp_var_t<float> tempGyro1 = lp_var_t<float>(objects::GYRO_1_L3G_HANDLER, l3gd20h::TEMPERATURE);
lp_var_t<float> tempGyro2 = lp_var_t<float>(objects::GYRO_2_ADIS_HANDLER, adis1650x::TEMPERATURE);
lp_var_t<float> tempGyro3 = lp_var_t<float>(objects::GYRO_3_L3G_HANDLER, l3gd20h::TEMPERATURE);
lp_var_t<float> tempMgm0 =
lp_var_t<float>(objects::MGM_0_LIS3_HANDLER, mgmLis3::TEMPERATURE_CELCIUS);
lp_var_t<float> tempMgm2 =
lp_var_t<float>(objects::MGM_2_LIS3_HANDLER, mgmLis3::TEMPERATURE_CELCIUS);
lp_var_t<float> tempAdcPayloadPcdu = lp_var_t<float>(objects::PLPCDU_HANDLER, plpcdu::TEMP);
2023-07-06 23:06:50 +02:00
lp_vec_t<int16_t, 9> currentVecPdu2 =
lp_vec_t<int16_t, 9>(gp_id_t(objects::PDU2_HANDLER, PDU::pool::PDU_CURRENTS));
2023-03-23 12:13:24 +01:00
2022-11-24 16:40:59 +01:00
// TempLimits
2023-07-06 23:06:50 +02:00
tcsCtrl::TempLimits acsBoardLimits = tcsCtrl::TempLimits(-40.0, -40.0, 80.0, 85.0, 85.0);
tcsCtrl::TempLimits mgtLimits = tcsCtrl::TempLimits(-40.0, -40.0, 65.0, 70.0, 70.0);
tcsCtrl::TempLimits rwLimits = tcsCtrl::TempLimits(-40.0, -40.0, 80.0, 85.0, 85.0);
tcsCtrl::TempLimits strLimits = tcsCtrl::TempLimits(-30.0, -20.0, 65.0, 70.0, 80.0);
tcsCtrl::TempLimits ifBoardLimits = tcsCtrl::TempLimits(-65.0, -40.0, 80.0, 85.0, 150.0);
tcsCtrl::TempLimits tcsBoardLimits = tcsCtrl::TempLimits(-60.0, -40.0, 80.0, 85.0, 130.0);
tcsCtrl::TempLimits obcLimits = tcsCtrl::TempLimits(-40.0, -40.0, 80.0, 85.0, 85.0);
tcsCtrl::TempLimits obcIfBoardLimits = tcsCtrl::TempLimits(-65.0, -40.0, 80.0, 85.0, 125.0);
tcsCtrl::TempLimits sBandTransceiverLimits = tcsCtrl::TempLimits(-40.0, -25.0, 35.0, 40.0, 65.0);
tcsCtrl::TempLimits pcduP60BoardLimits = tcsCtrl::TempLimits(-35.0, -35.0, 80.0, 85.0, 85.0);
tcsCtrl::TempLimits pcduAcuLimits = tcsCtrl::TempLimits(-35.0, -35.0, 80.0, 85.0, 85.0);
tcsCtrl::TempLimits pcduPduLimits = tcsCtrl::TempLimits(-35.0, -35.0, 80.0, 85.0, 85.0);
tcsCtrl::TempLimits plPcduBoardLimits = tcsCtrl::TempLimits(-55.0, -40.0, 80.0, 85.0, 125.0);
2024-04-08 10:28:36 +02:00
tcsCtrl::TempLimits plocMissionBoardLimits = tcsCtrl::TempLimits(-30.0, -10.0, 40.0, 45.0, 60);
2023-07-06 23:06:50 +02:00
tcsCtrl::TempLimits plocProcessingBoardLimits =
2024-04-08 10:28:36 +02:00
tcsCtrl::TempLimits(-30.0, -10.0, 40.0, 45.0, 60.0);
2023-07-06 23:06:50 +02:00
tcsCtrl::TempLimits dacLimits = tcsCtrl::TempLimits(-65.0, -40.0, 113.0, 118.0, 150.0);
tcsCtrl::TempLimits cameraLimits = tcsCtrl::TempLimits(-40.0, -30.0, 60.0, 65.0, 85.0);
tcsCtrl::TempLimits droLimits = tcsCtrl::TempLimits(-40.0, -30.0, 75.0, 80.0, 90.0);
tcsCtrl::TempLimits x8Limits = tcsCtrl::TempLimits(-40.0, -30.0, 75.0, 80.0, 90.0);
tcsCtrl::TempLimits hpaLimits = tcsCtrl::TempLimits(-40.0, -30.0, 75.0, 80.0, 90.0);
tcsCtrl::TempLimits txLimits = tcsCtrl::TempLimits(-40.0, -30.0, 75.0, 80.0, 90.0);
tcsCtrl::TempLimits mpaLimits = tcsCtrl::TempLimits(-40.0, -30.0, 75.0, 80.0, 90.0);
tcsCtrl::TempLimits scexBoardLimits = tcsCtrl::TempLimits(-60.0, -40.0, 80.0, 85.0, 150.0);
struct CtrlContext {
double sensorTemp = INVALID_TEMPERATURE;
uint8_t currentSensorIndex = 0;
tcsCtrl::ThermalComponents thermalComponent = tcsCtrl::NONE;
bool redSwitchNrInUse = false;
bool componentAboveCutOffLimit = false;
bool componentAboveUpperLimit = false;
Event overHeatEventToTrigger;
} ctrlCtx;
MessageQueueId_t camId = MessageQueueIF::NO_QUEUE;
2023-07-06 23:06:50 +02:00
struct TooHotFlags {
bool eBandTooHotFlag = false;
bool camTooHotOneShotFlag = false;
bool scexTooHotFlag = false;
bool plocTooHotFlag = false;
bool pcduSystemTooHotFlag = false;
bool syrlinksTooHotFlag = false;
bool obcTooHotFlag = false;
bool mgtTooHotFlag = false;
bool strTooHotFlag = false;
bool rwTooHotFlag = false;
} tooHotFlags;
2023-03-28 17:21:43 +02:00
2023-04-06 12:13:24 +02:00
bool transitionWhenHeatersOff = false;
uint32_t transitionWhenHeatersOffCycles = 0;
Mode_t targetMode = MODE_OFF;
Submode_t targetSubmode = SUBMODE_NONE;
2023-03-15 16:19:37 +01:00
uint32_t cycles = 0;
2023-07-06 23:06:50 +02:00
std::array<tcsCtrl::ThermalState, tcsCtrl::NUM_THERMAL_COMPONENTS> thermalStates{};
std::array<tcsCtrl::HeaterState, heater::NUMBER_OF_SWITCHES> heaterStates{};
2022-12-14 11:24:03 +01:00
2023-03-15 16:19:37 +01:00
// Initial delay to make sure all pool variables have been initialized their owners.
// Also, wait for system initialization to complete.
Countdown initialCountdown = Countdown(INIT_DELAY);
2023-02-14 11:18:51 +01:00
#if OBSW_THREAD_TRACING == 1
uint32_t opCounter = 0;
#endif
2023-02-07 14:28:42 +01:00
std::array<std::pair<bool, double>, 5> sensors;
2023-02-06 17:10:09 +01:00
uint8_t numSensors = 0;
2023-01-24 15:35:56 +01:00
PoolEntry<float> tmp1075Tcs0 = PoolEntry<float>({10.0});
PoolEntry<float> tmp1075Tcs1 = PoolEntry<float>({10.0});
PoolEntry<float> tmp1075PlPcdu0 = PoolEntry<float>({10.0});
PoolEntry<float> tmp1075PlPcdu1 = PoolEntry<float>({10.0});
PoolEntry<float> tmp1075IfBrd = PoolEntry<float>({10.0});
PoolEntry<uint8_t> heaterSwitchStates = PoolEntry<uint8_t>(heater::NUMBER_OF_SWITCHES);
PoolEntry<int16_t> heaterCurrent = PoolEntry<int16_t>();
2023-07-06 23:06:50 +02:00
PoolEntry<uint8_t> tcsCtrlHeaterOn = PoolEntry<uint8_t>(tcsCtrl::NUM_THERMAL_COMPONENTS);
PoolEntry<uint8_t> tcsCtrlSensorIdx = PoolEntry<uint8_t>(tcsCtrl::NUM_THERMAL_COMPONENTS);
PoolEntry<uint8_t> tcsCtrlHeaterIdx = PoolEntry<uint8_t>(tcsCtrl::NUM_THERMAL_COMPONENTS);
PoolEntry<uint32_t> tcsCtrlStartTimes = PoolEntry<uint32_t>(tcsCtrl::NUM_THERMAL_COMPONENTS);
PoolEntry<uint32_t> tcsCtrlEndTimes = PoolEntry<uint32_t>(tcsCtrl::NUM_THERMAL_COMPONENTS);
static constexpr dur_millis_t MUTEX_TIMEOUT = 50;
2022-12-14 11:24:03 +01:00
2023-03-28 17:21:43 +02:00
void startTransition(Mode_t mode, Submode_t submode) override;
2023-04-06 12:13:24 +02:00
bool heaterCtrlAllowed() const;
2023-04-06 12:53:35 +02:00
void resetThermalStates();
2023-04-06 12:13:24 +02:00
2023-02-06 17:10:09 +01:00
void resetSensorsArray();
void copySensors();
void copySus();
2022-05-23 00:37:49 +02:00
void copyDevices();
2022-11-28 17:40:29 +01:00
2023-04-03 14:09:54 +02:00
void ctrlComponentTemperature(HeaterContext& heaterContext);
void checkLimitsAndCtrlHeater(HeaterContext& heaterContext);
bool heaterCtrlCheckUpperLimits(HeaterContext& heaterContext);
void heaterCtrlTempTooHighHandler(HeaterContext& heaterContext, const char* whatLimit);
2023-03-28 19:38:02 +02:00
2023-04-06 12:13:24 +02:00
bool chooseHeater(heater::Switch& switchNr, heater::Switch redSwitchNr);
2023-04-04 15:02:56 +02:00
bool selectAndReadSensorTemp(HeaterContext& htrCtx);
2022-12-13 12:08:53 +01:00
2023-04-06 13:01:31 +02:00
void heaterSwitchHelperAllOff();
2023-07-06 23:06:50 +02:00
void heaterSwitchHelper(heater::Switch switchNr, heater::SwitchState state,
2023-07-10 13:28:36 +02:00
std::optional<unsigned> componentIdx);
2023-04-06 12:53:35 +02:00
2022-12-13 12:08:53 +01:00
void ctrlAcsBoard();
void ctrlMgt();
void ctrlRw();
void ctrlStr();
void ctrlIfBoard();
void ctrlTcsBoard();
void ctrlObc();
void ctrlSBandTransceiver();
void ctrlPcduP60Board();
void ctrlPcduAcu();
void ctrlPcduPdu();
void ctrlPlPcduBoard();
void ctrlPlocMissionBoard();
void ctrlPlocProcessingBoard();
void ctrlDac();
void ctrlCameraBody();
void ctrlDro();
void ctrlX8();
void ctrlHpa();
void ctrlTx();
void ctrlMpa();
void ctrlScexBoard();
2023-07-06 22:21:00 +02:00
/**
* The transition of heaters might take some time. As long as a transition is
* going on, the TCS controller works in a reduced form. This function takes care
* of tracking transition and capturing their completion.
* @param currentHeaterStates
*/
2023-07-06 23:06:50 +02:00
void heaterTransitionControl(const tcsCtrl::HeaterSwitchStates& currentHeaterStates);
2023-07-06 22:21:00 +02:00
/**
* Control tasks to prevent heaters being on for prolonged periods. Ideally, this
* should never happen, but this task prevents bugs from causing heaters to stay on
* for a long time, which draws a lot of power.
* @param currentHeaterStates
*/
2023-07-06 23:06:50 +02:00
void heaterMaxDurationControl(const tcsCtrl::HeaterSwitchStates& currentHeaterStates);
2023-07-06 22:21:00 +02:00
void crossCheckHeaterStateOfComponentsWhenHeaterGoesOff(heater::Switch switchIdx);
2023-04-06 12:13:24 +02:00
void setMode(Mode_t mode, Submode_t submode);
uint32_t tempFloatToU32() const;
2023-03-28 19:38:02 +02:00
bool tooHotHandler(object_id_t object, bool& oneShotFlag);
void tooHotHandlerWhichClearsOneShotFlag(object_id_t object, bool& oneShotFlag);
2021-01-08 09:34:43 +01:00
};
#endif /* MISSION_CONTROLLER_THERMALCONTROLLER_H_ */