refactoring done #3

Merged
ponsin_h merged 22 commits from refactoring into master 2026-06-08 09:24:39 +02:00
14 changed files with 551 additions and 293 deletions
+1 -1
View File
@@ -1,8 +1,8 @@
.vscode/
CMakeCache.txt
CMakeFiles
Makefile
cmake_install.cmake
midas.log
ePowerSwitchFront
build/
Makefile
+53 -9
View File
@@ -1,13 +1,13 @@
cmake_minimum_required(VERSION 3.03)
project(ePowerSwitchFront VERSION 1.0)
project(ePowerSwitchFrontend VERSION 1.0)
add_subdirectory(submodule/mepicsca)
add_compile_options(
-Wall
-Wformat=2
-O3
-g
-Wno-format-nonliteral
-Wno-strict-aliasing
-Wuninitialized
@@ -44,20 +44,33 @@ set(LIBS
find_package(Midas REQUIRED)
add_executable(ePowerSwitchFront
src/ePowerSwitchFront.cpp
################################################################################
## Device Library
################################################################################
add_library(
power_switch
src/device/power_switch.cpp
src/utils/outlet.cpp
)
set_property(
TARGET
ePowerSwitchFront
power_switch
PROPERTY
CXX_STANDARD 17
)
target_link_libraries(
power_switch
PRIVATE
midas::mfe
m_epics_ca
${LIBS}
)
target_include_directories(
ePowerSwitchFront
power_switch
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE
@@ -68,15 +81,46 @@ target_include_directories(
${EPICSSYS}/include/compiler/clang
)
target_link_libraries(ePowerSwitchFront
################################################################################
## Frontend
################################################################################
add_executable(power_switch_scfe
src/frontend/power_switch_scfe.cpp
)
set_property(
TARGET
power_switch_scfe
PROPERTY
CXX_STANDARD 17
)
target_include_directories(
power_switch_scfe
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE
${EPICSSYS}/include
${EPICSSYS}/include/os/Linux
${EPICSSYS}/include/os/Darwin
${EPICSSYS}/include/compiler/gcc
${EPICSSYS}/include/compiler/clang
)
target_link_libraries(
power_switch_scfe
PRIVATE
power_switch
midas::mfe
m_epics_ca
${LIBS}
)
install(
TARGETS
ePowerSwitchFront
power_switch_scfe
RUNTIME DESTINATION bin
)
-11
View File
@@ -1,11 +0,0 @@
#ifndef EPOWERSWITCH_CONFIG_H
#define EPOWERSWITCH_CONFIG_H
#define EPOWERSWITCH_SOCKET_SET_PREFIX "ePowerSwitch_set_outlet_"
#define EPOWERSWITCH_SOCKET_GET_PREFIX "ePowerSwitch_get_outlet_"
#define EPOWERSWITCH_SOCKETNUMBER_INFO_PREFIX "ePowerSwitch_config_maxOutlet"
#define DEFAULT_FRONTEND_NAME "ePowerSwitch"
#define DEFAULT_EQUIPMENT_NAME "powerswitch"
#endif
-60
View File
@@ -1,60 +0,0 @@
#ifndef EPOWERSWITCH_FRONTEND_H
#define EPOWERSWITCH_FRONTEND_H
#include "m_epics_ca.h"
#include "tmfe.h"
#include <vector>
class ePowerSwitchEquipment : public TMFeEquipment {
public:
/**
* @brief constructor
*
* @param equipmentName name show on Midas for the equipment
* @param equipmentFileName file name for automatically tagged message
*/
ePowerSwitchEquipment(std::string equipmentName,
const char *equipmentFileName);
/**
* @brief called periodically by Midas
*/
void HandlePeriodic();
private:
std::vector<mEpicsCa<std::string> *> outletSetRecords;
std::vector<mEpicsCa<std::string> *> outletGetRecords;
mEpicsCa<int> outletNumberRecord;
int numberOfOutlet;
/**
* @brief refresh information about the given socket ID
*
* @param socketId which socket to refresh
*/
void refreshSocket(int socketId);
/**
* @brief refresh information about all the available sockets
*/
void refreshAllSockets();
/**
* @brief detect and modify the current socket pool in case of change
*/
void updateSocketNumber();
};
class ePowerSwitchFrontend : public TMFrontend {
public:
/**
* @brief constructor
*
* @param frontendName name shown on Midas for the frontend
* @param equipmentName name shown on Midas for the equipment
*/
ePowerSwitchFrontend(std::string frontendName, std::string equipmentName);
};
#endif
+105
View File
@@ -0,0 +1,105 @@
#include "power_switch.h"
#include "odbxx.h"
#include "power_switch_config.h"
#include "tmfe.h"
#include <string>
#include <sstream>
/**
* @brief Insert a integer value between two string
* @param str1 first string
* @param int value to insert
* @param str2 second string
*
* @return a new string instance
*/
std::string insert(const std::string *str1, int value,
const std::string *str2) {
return *str1 + std::to_string(value) + *str2;
}
powerSwitch::powerSwitch(std::string equipmentName,
const char *equipmentFilename)
: TMFeEquipment(equipmentName.c_str(), equipmentFilename),
outletNumberRecord(mEpicsCa<int>(EPOWERSWITCH_OUTLETNUMBER_INFO_PREFIX)) {
fEqConfReadOnlyWhenRunning = false;
this->equipmentPath = std::string("/Equipment/") + equipmentName;
}
void powerSwitch::HandlePeriodic() {
updateOutletNumber();
midas::odb o(this->equipmentPath);
for (int i = 0; i < this->numberOfOutlet; i++) {
Outlet *outlet = &this->outlets.at(i);
if (outlet->commandHasChange()) {
o[COMMAND_PATH.c_str()][i] = outlet->getCommand();
}
o[READBACK_PATH.c_str()][i] = outlet->getState();
}
}
TMFeResult powerSwitch::HandleInit(const std::vector<std::string> &args) {
midas::odb o = {
{SETTING_DIR.c_str(), {{EDITABLE_VARNAME.c_str(), COMMAND_VARNAME}}}};
o.connect(this->equipmentPath.c_str());
midas::odb to_watch(this->equipmentPath + SLASH + VARIABLES_DIR + SLASH +
COMMAND_VARNAME);
to_watch.watch(
[&](midas::odb &arg) { this->sendCommand(arg.get_last_index()); });
return TMFeOk();
}
void powerSwitch::updateOutletNumber() {
int newNumberOfOutlet = 0;
int returnCode = outletNumberRecord.get(&newNumberOfOutlet);
if (returnCode != CM_SUCCESS) {
fMfe->Msg(MERROR, __FUNCTION__, "Unable to read outlet record number");
}
if (this->numberOfOutlet < newNumberOfOutlet)
fMfe->Msg(MDEBUG, __FUNCTION__,
"Outlet number increasing, creating %d new outlet(s)",
newNumberOfOutlet - this->numberOfOutlet);
for (int i = this->numberOfOutlet; i < newNumberOfOutlet; i++) {
std::string epicsSetRecordName;
{
static const std::string *str1 = &EPOWERSWITCH_OUTLET_SET_PREFIX;
static const std::string str2 = "";
epicsSetRecordName = insert(str1, i + 1, &str2);
}
std::string epicsGetRecordName;
{
static const std::string *str1 = &EPOWERSWITCH_OUTLET_GET_PREFIX;
static const std::string str2 = "";
epicsGetRecordName = insert(str1, i + 1, &str2);
}
this->outlets.emplace_back(i, epicsGetRecordName, epicsSetRecordName);
}
if (this->numberOfOutlet > newNumberOfOutlet)
fMfe->Msg(MDEBUG, __FUNCTION__,
"Outlet number decreasing, destroying %d outlet(s)",
this->numberOfOutlet - newNumberOfOutlet);
for (int i = newNumberOfOutlet; i < this->numberOfOutlet; i++) {
this->outlets.pop_back();
}
this->numberOfOutlet = newNumberOfOutlet;
}
void powerSwitch::sendCommand(int index) {
midas::odb o(this->equipmentPath);
std::string command = o[COMMAND_PATH.c_str()][index];
Outlet *outlet = &outlets.at(index);
outlet->setState(command);
}
+25
View File
@@ -0,0 +1,25 @@
#ifndef POWER_SWITCH_H
#define POWER_SWITCH_H
#include "../utils/outlet.h"
#include "tmfe.h"
#include <vector>
class powerSwitch : public TMFeEquipment {
public:
powerSwitch(std::string equipmentName, const char *equipmentFilename);
void HandlePeriodic();
TMFeResult HandleInit(const std::vector<std::string> &args);
private:
std::vector<Outlet> outlets;
mEpicsCa<int> outletNumberRecord;
int numberOfOutlet;
std::string equipmentPath;
void updateOutletNumber();
void sendCommand(int index);
};
#endif
+28
View File
@@ -0,0 +1,28 @@
#ifndef POWER_SWITCH_CONFIG_H
#define POWER_SWITCH_CONFIG_H
#include <string>
const std::string EPOWERSWITCH_OUTLETNUMBER_INFO_PREFIX =
"ePowerSwitch_config_maxOutlet";
const std::string VARIABLES_DIR = "Variables";
const std::string SETTING_DIR = "Settings";
const std::string COMMAND_VARNAME = "Command";
const std::string READBACK_VARNAME = "Readback";
const std::string SLASH = "/";
const std::string EPOWERSWITCH_OUTLET_SET_PREFIX = "ePowerSwitch_set_outlet_";
const std::string EPOWERSWITCH_OUTLET_GET_PREFIX = "ePowerSwitch_get_outlet_";
const std::string EDITABLE_VARNAME = std::string("Editable");
// ##################################################
// Paths names : do not modify
// ##################################################
const std::string COMMAND_PATH = VARIABLES_DIR + SLASH + COMMAND_VARNAME;
const std::string READBACK_PATH = VARIABLES_DIR + SLASH + READBACK_VARNAME;
#endif
-211
View File
@@ -1,211 +0,0 @@
#include "../include/ePowerSwitchFront.h"
#include "../include/ePowerSwitchConfig.h"
#include "m_epics_ca.h"
#include "tmfe.h"
#include <sstream>
/**
* @brief local function: append an integer to the end of a string.
* @param str string prefix
* @param value integer to append
* @return "str" + integer
*/
std::string format(std::string str, int value) {
std::stringstream ss;
ss << str;
ss << value;
return ss.str();
}
int main(int argc, char *argv[]) {
if (argv[0][0] == '.') {
TMFE::Instance()->Msg(MERROR, __FUNCTION__,
"Relative paths are strongly discouraged; "
"please use an absolute path.");
}
std::string frontendName;
std::string equipmentName;
if (argc == 1) {
frontendName = DEFAULT_FRONTEND_NAME;
TMFE::Instance()->Msg(MINFO, __FUNCTION__,
"No frontend name provided; %s will be used",
DEFAULT_FRONTEND_NAME);
equipmentName = DEFAULT_EQUIPMENT_NAME;
TMFE::Instance()->Msg(MINFO, __FUNCTION__,
"No equipment name provided; %s will be used",
DEFAULT_EQUIPMENT_NAME);
}
if (argc == 2) {
frontendName = std::string(argv[1]);
argv++;
argc--;
TMFE::Instance()->Msg(MINFO, __FUNCTION__,
"No equipment name provided; %s will be use",
DEFAULT_EQUIPMENT_NAME);
} else if (argc == 3) {
frontendName = std::string(argv[1]);
equipmentName = std::string(argv[2]);
argv += 2;
argc -= 2;
}
auto front = new ePowerSwitchFrontend(frontendName, equipmentName);
front->FeMain(argc, argv);
}
ePowerSwitchEquipment::ePowerSwitchEquipment(std::string equipmentName,
const char *equipmentFileName)
: TMFeEquipment(equipmentName.c_str(), equipmentFileName),
outletNumberRecord(mEpicsCa<int>(
std::string_view(EPOWERSWITCH_SOCKETNUMBER_INFO_PREFIX))) {
fEqConfReadConfigFromOdb = false; /// i don't know what this parameter does
fEqConfPeriodMilliSec = 1000; /// refresh rate of midas frontend
fEqConfLogHistory =
60; /// enable history system, generate one event per minutes
fEqConfReadOnlyWhenRunning = false; /// allow to write values when running
fEqConfWriteEventsToOdb = true; /// i don't know
this->numberOfOutlet = 0;
}
void ePowerSwitchEquipment::HandlePeriodic() {
updateSocketNumber();
refreshAllSockets();
}
void ePowerSwitchEquipment::refreshSocket(int socketId) {
std::string requestedVarname =
format("Socket ", socketId) + std::string(" requested");
std::string currentVarname =
format("Socket ", socketId) + std::string(" current");
std::string midasRequestedSocketState = std::string("Off");
MVOdbError ovbError;
int mepicscaReturnCode;
fOdbEqVariables->RS(requestedVarname.c_str(), &midasRequestedSocketState,
true, midasRequestedSocketState.length() + 1,
&ovbError);
if (ovbError.fError) {
fMfe->Msg(MERROR, __FUNCTION__, ovbError.fErrorString);
exit(EXIT_FAILURE);
}
std::string epicsCurrentSocketState;
mepicscaReturnCode =
this->outletGetRecords.at(socketId)->get(&epicsCurrentSocketState);
if (mepicscaReturnCode != CM_SUCCESS) {
fMfe->Msg(MERROR, __FUNCTION__,
"Couldn't get the value, skipping refreshing socket %d. "
"Error code %d",
socketId, mepicscaReturnCode);
return;
}
if (epicsCurrentSocketState != midasRequestedSocketState) {
mepicscaReturnCode = this->outletSetRecords.at(socketId)->put(
&midasRequestedSocketState);
if (mepicscaReturnCode != CM_SUCCESS) {
fMfe->Msg(MERROR, __FUNCTION__,
"Couldn't put the value, skipping refreshing socket %d. "
"Error code %d",
socketId, mepicscaReturnCode);
return;
}
}
fOdbEqVariables->WS(currentVarname.c_str(), epicsCurrentSocketState.c_str(),
epicsCurrentSocketState.length() + 1, &ovbError);
if (ovbError.fError) {
fMfe->Msg(MERROR, __FUNCTION__, ovbError.fErrorString);
exit(EXIT_FAILURE);
}
}
void ePowerSwitchEquipment::refreshAllSockets() {
for (int i = 0; i < this->numberOfOutlet; i++) {
refreshSocket(i);
}
}
void ePowerSwitchEquipment::updateSocketNumber() {
int epicsOutletNumber = 0;
if (!outletNumberRecord.connected()) {
fMfe->Msg(MERROR, __FUNCTION__,
"Couldn't connect to outlet number record.");
return;
}
int returnCode = outletNumberRecord.get(&epicsOutletNumber);
if (returnCode < 0) {
fMfe->Msg(MERROR, __FUNCTION__,
"Couldn't get outlet number record. [ERROR_CODE : %d]\n "
"Skipping function execution",
returnCode);
return;
}
if (this->numberOfOutlet < epicsOutletNumber)
fMfe->Msg(MDEBUG, __FUNCTION__,
"Socket number increasing, creating %d new socket(s)",
epicsOutletNumber - this->numberOfOutlet);
for (int i = this->numberOfOutlet; i < epicsOutletNumber; i++) {
std::string epicsSetRecordName =
format(EPOWERSWITCH_SOCKET_SET_PREFIX, i);
this->outletSetRecords.push_back(
new mEpicsCa<std::string>(epicsSetRecordName.c_str()));
std::string epicsGetRecordName =
format(EPOWERSWITCH_SOCKET_GET_PREFIX, i);
this->outletGetRecords.push_back(
new mEpicsCa<std::string>(epicsGetRecordName.c_str()));
}
if (this->numberOfOutlet > epicsOutletNumber)
fMfe->Msg(MDEBUG, __FUNCTION__,
"Socket number decreasing, destroying %d socket(s)",
this->numberOfOutlet - epicsOutletNumber);
for (int i = epicsOutletNumber; i < this->numberOfOutlet; i++) {
mEpicsCa<std::string> *setRecord = this->outletSetRecords.back();
delete setRecord;
mEpicsCa<std::string> *getRecord = this->outletGetRecords.back();
delete getRecord;
std::string requestedVarname =
format("Socket ", i) + std::string(" requested");
std::string currentVarname =
format("Socket ", i) + std::string(" current");
MVOdbError ovbError;
fOdbEqVariables->Delete(requestedVarname.c_str(), &ovbError);
if (ovbError.fError)
fMfe->Msg(MERROR, __FUNCTION__, ovbError.fErrorString);
fOdbEqVariables->Delete(currentVarname.c_str(), &ovbError);
if (ovbError.fError)
fMfe->Msg(MERROR, __FUNCTION__, ovbError.fErrorString);
this->outletSetRecords.pop_back();
this->outletGetRecords.pop_back();
}
this->numberOfOutlet = epicsOutletNumber;
}
ePowerSwitchFrontend::ePowerSwitchFrontend(std::string frontendName,
std::string equipmentName) {
FeSetName(frontendName.c_str());
FeAddEquipment(new ePowerSwitchEquipment(equipmentName.c_str(), __FILE__));
}
+49
View File
@@ -0,0 +1,49 @@
#include "power_switch_scfe.h"
#include "../device/power_switch.h"
#include "power_switch_scfe_config.h"
#include "tmfe.h"
int main(int argc, char *argv[]) {
if (argv[0][0] == '.') {
TMFE::Instance()->Msg(MERROR, __FUNCTION__,
"Relative paths are strongly discouraged; "
"please use an absolute path.");
}
std::string frontendName;
std::string equipmentName;
if (argc == 1) {
frontendName = DEFAULT_FRONTEND_NAME;
TMFE::Instance()->Msg(MINFO, __FUNCTION__,
"No frontend name provided; %s will be used",
DEFAULT_FRONTEND_NAME);
equipmentName = DEFAULT_EQUIPMENT_NAME;
TMFE::Instance()->Msg(MINFO, __FUNCTION__,
"No equipment name provided; %s will be used",
DEFAULT_EQUIPMENT_NAME);
}
if (argc == 2) {
frontendName = std::string(argv[1]);
argv++;
argc--;
TMFE::Instance()->Msg(MINFO, __FUNCTION__,
"No equipment name provided; %s will be use",
DEFAULT_EQUIPMENT_NAME);
} else if (argc == 3) {
frontendName = std::string(argv[1]);
equipmentName = std::string(argv[2]);
argv += 2;
argc -= 2;
}
auto front = powerSwitchSCFE(frontendName, equipmentName);
front.FeMain(argc, argv);
}
powerSwitchSCFE::powerSwitchSCFE(std::string frontendName,
std::string equipmentName) {
FeSetName(frontendName.c_str());
FeAddEquipment(new powerSwitch(equipmentName.c_str(), __FILE__));
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef POWER_SWITCH_SCFE_H
#define POWER_SWITCH_SCFE_H
#include "tmfe.h"
class powerSwitchSCFE : public TMFrontend {
public:
/**
* @brief constructor
*
* @param frontendName name shown on Midas for the frontend
* @param equipmentName name shown on Midas for the equipment
*/
powerSwitchSCFE(std::string frontendName, std::string equipmentName);
};
#endif
+7
View File
@@ -0,0 +1,7 @@
#ifndef POWER_SWITCH_SCFE_CONFIG_H
#define POWER_SWITCH_SCFE_CONFIG_H
#define DEFAULT_FRONTEND_NAME "powerSwitch"
#define DEFAULT_EQUIPMENT_NAME "powerswitch"
#endif
+201
View File
@@ -0,0 +1,201 @@
#include "outlet.h"
#include "m_epics_ca.h"
#include "odbxx.h"
#include "tmfe.h"
#include <string>
Outlet::Outlet(int index, std::string inputRecordName,
std::string outputRecordName)
: input(mEpicsCa<std::string>(inputRecordName)),
output(mEpicsCa<std::string>(outputRecordName)) {
this->index = index;
this->innerState = State::UNKNOW;
this->isOutputConnected = true;
this->isInputConnected = true;
this->gracePeriod = 3;
}
std::string Outlet::getState() {
this->updateState();
return this->stateToString(this->innerState);
}
std::string Outlet::getCommand() {
State command = this->_getCommand();
this->lastCommand = command;
return this->stateToString(command);
}
bool Outlet::commandHasChange() {
return this->lastCommand != this->_getCommand();
}
void Outlet::setState(State state) {
this->buffered = state;
this->updateState();
}
void Outlet::setState(std::string state) {
this->setState(this->stringToState(state));
}
void Outlet::turnOn() {
std::string msg = "On";
int returnCode = this->output.put(&msg);
if (returnCode != CM_SUCCESS)
TMFE::Instance()->Msg(
MERROR, __FUNCTION__,
"Outlet %d : Error %d : unable to write into output record",
this->index, returnCode);
}
void Outlet::turnOff() {
std::string msg = "Off";
int returnCode = this->output.put(&msg);
if (returnCode != CM_SUCCESS)
TMFE::Instance()->Msg(
MERROR, __FUNCTION__,
"Outlet %d : Error %d : unable to write into output record",
this->index, returnCode);
}
void Outlet::restart() {
std::string msg = "Restart";
int returnCode = this->output.put(&msg);
if (returnCode != CM_SUCCESS)
TMFE::Instance()->Msg(
MERROR, __FUNCTION__,
"Outlet %d : Error %d : unable to write into output record",
this->index, returnCode);
}
void Outlet::updateState() {
bool outputConnected = this->output.connected();
bool inputConnected = this->input.connected();
if (!outputConnected && this->isOutputConnected) {
TMFE::Instance()->Msg(MERROR, __FUNCTION__,
"Outlet %d : Output channel disconnected",
this->index);
}
if (!inputConnected && this->isInputConnected) {
TMFE::Instance()->Msg(MERROR, __FUNCTION__,
"Outlet %d : Input channel disconnected",
this->index);
}
this->isOutputConnected = outputConnected;
this->isInputConnected = inputConnected;
if (!outputConnected || !inputConnected) {
// Error message are above
return;
}
// Adding a grace period to prevent reseting at start
if (this->gracePeriod > 0) {
this->gracePeriod--;
buffered.reset();
}
std::string value;
int returnCode = this->input.get(&value);
if (returnCode != CM_SUCCESS) {
TMFE::Instance()->Msg(
MERROR, __FUNCTION__,
"Outlet %d : Error %d : unable to read from input record",
this->index, returnCode);
}
this->innerState = stringToState(value);
if (buffered.has_value()) {
State state = buffered.value();
switch (this->innerState) {
case State::ON:
if (state == State::OFF)
this->turnOff();
if (state == State::RST)
this->restart();
buffered.reset();
break;
case State::OFF:
if (state == State::ON)
this->turnOn();
if (state == State::RST)
this->restart();
buffered.reset();
break;
case State::RST:
TMFE::Instance()->Msg(MERROR, __FUNCTION__,
"Outlet %d in restarting state, command "
"pending in a buffered state",
this->index);
break;
case State::UNKNOW:
TMFE::Instance()->Msg(MINFO, __FUNCTION__,
"Outlet %d : Unknow state", this->index);
if (state == State::ON)
this->turnOn();
if (state == State::OFF)
this->turnOff();
if (state == State::RST)
this->restart();
buffered.reset();
break;
default: // This state doesn't exist
TMFE::Instance()->Msg(MERROR, __FUNCTION__,
"Outlet %d : FATAL ERROR : unvalide state",
this->index);
exit(EXIT_FAILURE);
break;
}
}
}
Outlet::State Outlet::stringToState(std::string value) {
if (value == "ON" || value == "On") {
return State::ON;
}
if (value == "OFF" || value == "Off") {
return State::OFF;
}
if (value == "RST" || value == "Rst" || value == "Res" || value == "RES" ||
value == "RESTART" || value == "Restart") {
return State::RST;
}
return State::UNKNOW;
}
std::string Outlet::stateToString(State state) {
switch (state) {
case State::ON:
return std::string("On");
break;
case State::OFF:
return std::string("Off");
break;
case State::RST:
return std::string("Restart");
break;
default:
return std::string("Unknow");
break;
};
}
Outlet::State Outlet::_getCommand() {
std::string value;
int returnCode = this->output.get(&value);
if (returnCode != CM_SUCCESS)
TMFE::Instance()->Msg(
MERROR, __FUNCTION__,
"Outlet %d : Error %d : unable to read from output record",
this->index, returnCode);
return stringToState(value);
}
+64
View File
@@ -0,0 +1,64 @@
#ifndef OUTLET_H
#define OUTLET_H
#include "m_epics_ca.h"
class Outlet {
public:
Outlet(int index, std::string inputRecordName,
std::string outputRecordName);
enum class State { ON, OFF, RST, UNKNOW };
Outlet(Outlet &&other) noexcept = default;
/**
* @brief get the current state of the device by reading it
* @return the current state
*/
std::string getState();
/**
* @brief get the value of the command stored in the command buffer
* @return the current command state
*/
std::string getCommand();
/**
* @brief return true if the last value readed from this object if not equal
* to the current device state
*/
bool commandHasChange();
/**
* @brief set to a new state the device
*/
void setState(State state);
/**
* @brief set to a new state the device. If string not recognize, will put
* UNKNOW to the outlet state
*/
void setState(std::string state);
private:
mEpicsCa<std::string> input;
mEpicsCa<std::string> output;
bool isInputConnected;
bool isOutputConnected;
int gracePeriod;
std::optional<State> buffered;
State innerState;
int index;
void turnOn();
void turnOff();
void restart();
void updateState();
Outlet::State stringToState(std::string value);
std::string stateToString(State state);
State _getCommand();
State lastCommand;
};
#endif