refactoring done #3
+1
-1
@@ -1,8 +1,8 @@
|
||||
.vscode/
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
midas.log
|
||||
ePowerSwitchFront
|
||||
build/
|
||||
Makefile
|
||||
+53
-9
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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__));
|
||||
}
|
||||
@@ -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__));
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
+1
-1
Submodule submodule/mepicsca updated: abc57815eb...b6aca4174e
Reference in New Issue
Block a user