new version of powerSwitch

This commit is contained in:
2026-06-08 08:51:33 +02:00
parent 0228dac3d3
commit 6f3807f13d
5 changed files with 404 additions and 0 deletions
+105
View File
@@ -0,0 +1,105 @@
#include "powerSwitch.h"
#include "odbxx.h"
#include "powerSwitchConfig.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
+202
View File
@@ -0,0 +1,202 @@
#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) {
printf("set a new states \n");
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;
}
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();
printf("update state to \"%s\" sur l'outlet %d\n", value.c_str(),
this->index);
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);
}
+44
View File
@@ -0,0 +1,44 @@
#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;
std::string getState();
std::string getCommand();
bool commandHasChange();
void setState(State 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