New version, OOP & Event driven. #2

Merged
ponsin_h merged 28 commits from oop_is_the_way into main 2026-07-28 11:06:47 +02:00
31 changed files with 1644 additions and 1041 deletions
+9 -13
View File
@@ -6,13 +6,12 @@ add_compile_options(
-Wall
-Wformat=2
-g
-Wno-format-nonliteral
-Wno-strict-aliasing
-O0
-Wuninitialized
-Wno-unused-function
)
# Check if the required environment variables MIDASSYS and EPICSSYS are available
# Check if the required environment variables MIDASSYS is available
if (NOT DEFINED ENV{MIDASSYS})
message(SEND_ERROR "MIDASSYS environment variable not defined.")
endif()
@@ -29,6 +28,13 @@ find_package(Midas REQUIRED)
add_library(
itcPressureOptimizer
src/device/itc_pressure_optimizer.cpp
src/device/handlers/DemandHandler.cpp
src/device/handlers/InputHandler.cpp
src/device/handlers/SettingsHandler.cpp
src/device/handlers/PressureCalculator.cpp
src/device/handlers/FeedbackHandler.cpp
src/device/handlers/OutputHandler.cpp
src/device/handlers/Average.cpp
)
set_property(
@@ -49,11 +55,6 @@ target_include_directories(
itcPressureOptimizer
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE
${EPICSSYS}/include
${EPICSSYS}/include/os/Linux
${EPICSSYS}/include/compiler/gcc
${EPICSSYS}/include/compiler/clang
)
################################################################################
@@ -75,11 +76,6 @@ target_include_directories(
itcPressureOptimizerScfe
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE
${EPICSSYS}/include
${EPICSSYS}/include/os/Linux
${EPICSSYS}/include/compiler/gcc
${EPICSSYS}/include/compiler/clang
)
target_link_libraries(
+47 -12
View File
@@ -1,25 +1,60 @@
# ITC Pressure Optimizer
Optimize the pressure setpoint on Mercury ITC depending on Temperature parameters read from the (same) Mercury ITC.
Optimize the pressure setpoint on a Mercury ITC depending on temperature parameters read from the (same) Mercury ITC.
Pressures are calculated as a function of temperature setpoint, actual temperature, heater power, and currently set pressure.
This driver is a port of mitcpresscio.c, with an event
Pressure are calculated in function of temperature setpoint, actual temperature, heater power and currently set pressure.
# Preview
![alt text](img/odb_equipment_interface.png)
This driver is only a port of mitcpresscio.c, using the c++ TRIUMF framework instead of legacy C
# Download
# ITC Pressure Optimizer DD Settings for associated Equipment
```bash
$ git clone ....
```
Path : ```/Equipment/<name>/Settings/Devices/MITCPRESSC/DD/```
# Compile
```bash
$ cmake -B build
$ cmake --build build
```
# Run
```bash
$ $pwd/build/itcPressureOptimizer <frontend_name> <equipment_name>
```
# Configuration
In ```/Equipment/<name>/Settings/Devices/MITCPRESSC/DD/```, you can modify settings regarding where to read/write values to the equipment.
Default values are :
| Equipment | variox0 | heliox0 |
|-----------|---------|---------|
| MITC_OutputPressSPIndex | 3 (0x3) | 8 (0x8) |
| MITC_InputVarioxSPIndex | 1 (0x1) | 1 (0x1) |
| MITC_InputVarioxTempIndex | 0 (0x0) | 0 (0x0) |
| MITC_InputVarioxPowIndex | 10 (0xA) | 8 (0x8) |
| RecalcInterval | 10 (0xA) | 10 (0xA) |
| MitcReadoutInterval | 2 (0x2) | 2 (0x2) |
| PressReadoutInterval | 3 (0x3) | 3 (0x3) |
| Output Pressure SetPoint Index | 3 (0x3) | 8 (0x8) |
| Input SetPoint Index | 1 (0x1) | 1 (0x1) |
| Input Temperature Index | 0 (0x0) | 0 (0x0) |
| Input Power Index | 10 (0xA) | 8 (0x8) |
| Average Power Time Window | 30f | 30f |
*NOTE: OutputPressSPIndex and InputVarioxPowIndex are different for variox0
and heliox0*
# Usage
In ```/Equipment/<name>/Variables/Demand``` you can specify demand to the equipment. See **Preview**
# Project structure
This is the execution flow graph.
![alt text](out/doc/graph/graph.png)
All dotted lines represent data and execution flow going through the EventBus (Broker) with the publish / subscribe method.
For cleaner code, all execution and data flow pass through an EventBus. Objects can subscribe to one or many specific event types. They can also publish an event, which will be dispatched to all subscribers by invoking their callbacks.
`OdbHotlink` triggers the execution flow whenever a value changes. Only `DemandHandler` and `InputHandler` are registered.
`itc_pressure_optimizer` instantiates all components and publishes an `InitEvent` first, allowing all handlers to initialize and connect to the ODB. It also periodically publishes a `PollEvent` every second to update time-dependent averages.
+35
View File
@@ -0,0 +1,35 @@
@startuml
skinparam linetype ortho
scale 1.2
class EventBus <<broker>> {
+ publish()
+ subscribe()
}
class Demand as "DemandHandler"
class itc as "itc_pressure_optimizer"
class Settings as "SettingsHandler"
class Input as "InputHandler"
class Pressure as "PressureCalculator" {
class Average
}
class Output as "OutputHandler"
class Feedback as "FeedbackHandler"
Demand -.> Pressure
Demand -.> Feedback
itc -.> Settings
itc -.> Pressure
itc -.> Demand
Settings -.> Output
Settings -.> Input
Input -.> Feedback
Input -.> Output
Input -.> Pressure
Pressure -.> Feedback
@enduml
Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

+66
View File
@@ -0,0 +1,66 @@
#include <functional>
#include <iostream>
#include <memory>
#include <typeindex>
#include <unordered_map>
#include <vector>
#include "src/device/EventBus.h"
class ProdA {};
class ProdB {};
class ConsA {
public:
void surProdA(const ProdA &) {
std::cout << "ConsA : Reçu ProdA" << std::endl;
}
};
class ConsB {
public:
void surProdA(const ProdA &) {
std::cout << "ConsB : Reçu ProdA" << std::endl;
}
void surProdB(const ProdB &) {
std::cout << "ConsB : Reçu ProdB" << std::endl;
}
};
class ConsC {
public:
void surProdB(const ProdB &) {
std::cout << "ConsC : Reçu ProdB" << std::endl;
}
};
int main() {
EventBus bus;
// Consumer
ConsA consA;
ConsB consB;
ConsC consC;
// Setup abonnement
// ConsA react to ProdA
bus.subscribe<ProdA>([&consA](const ProdA &e) { consA.surProdA(e); });
// ConsB react to ProdA and ProdB
bus.subscribe<ProdA>([&consB](const ProdA &e) { consB.surProdA(e); });
bus.subscribe<ProdB>([&consB](const ProdB &e) { consB.surProdB(e); });
// ConsC react to ProdB
bus.subscribe<ProdB>([&consC](const ProdB &e) { consC.surProdB(e); });
// Test of publishement
std::cout << "--- Publishing of ProdA ---" << std::endl;
ProdA prodA;
bus.publish(prodA);
std::cout << "\n--- Publishing of ProdB ---" << std::endl;
ProdB prodB;
bus.publish(prodB);
return 0;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

+63
View File
@@ -0,0 +1,63 @@
#ifndef EVENT_BUS_H
#define EVENT_BUS_H
#include <functional>
#include <iostream>
#include <memory>
#include <typeindex>
#include <unordered_map>
#include <vector>
class EventBus {
public:
// Subscribe to a specific event type
template <typename EventType>
void subscribe(std::function<void(const EventType &)> callback) {
std::type_index typeIdx = std::type_index(typeid(EventType));
// If this is the first subscriber for this type, create the list
if (subscribers.find(typeIdx) == subscribers.end()) {
subscribers[typeIdx] = std::make_unique<CallbackList<EventType>>();
}
// Safely cast to the specialized typed list and add the callback
auto *list =
static_cast<CallbackList<EventType> *>(subscribers[typeIdx].get());
list->callbacks.push_back(callback);
}
// Publish an event to all registered subscribers
template <typename EventType> void publish(const EventType &event) {
std::type_index typeIdx = std::type_index(typeid(EventType));
auto it = subscribers.find(typeIdx);
if (it != subscribers.end()) {
// Cast to the correct typed list to safely execute the callbacks
auto *list =
static_cast<CallbackList<EventType> *>(it->second.get());
for (const auto &callback : list->callbacks) {
callback(event);
}
}
}
private:
// Non-templated base interface to store our callback lists
// generically inside the unordered_map.
struct CallbackListBase {
virtual ~CallbackListBase() = default;
};
// Specialized version holding the actual strongly-typed callbacks.
template <typename EventType>
struct CallbackList : public CallbackListBase {
using CallbackType = std::function<void(const EventType &)>;
std::vector<CallbackType> callbacks;
};
// Map associating each event type to its respective callback list
std::unordered_map<std::type_index, std::unique_ptr<CallbackListBase>>
subscribers;
};
#endif
+70
View File
@@ -0,0 +1,70 @@
#include <chrono>
#include <deque>
#include "Average.h"
void Average::setWindowSize(double seconds) {
windowSize = Duration(seconds);
cleanupAndUpdate(Clock::now());
}
void Average::addValue(double value) {
TimePoint now = Clock::now();
currentValue = value;
// add a new value
history.push_back({now, currentValue});
cleanupAndUpdate(now);
}
double Average::getAverage() {
TimePoint now = Clock::now();
cleanupAndUpdate(now);
if (history.empty()) {
return currentValue;
}
double totalWeightedValue = 0.0;
double totalDurationSeconds = 0.0;
TimePoint windowStart =
now - std::chrono::duration_cast<Clock::duration>(windowSize);
for (size_t i = 0; i < history.size(); ++i) {
TimePoint segmentStart = (i == 0) ? windowStart : history[i].timestamp;
TimePoint segmentEnd =
(i < history.size() - 1) ? history[i + 1].timestamp : now;
// remove time before the window
if (segmentEnd < windowStart)
continue;
if (segmentStart < windowStart)
segmentStart = windowStart;
Duration duration = segmentEnd - segmentStart;
double seconds = duration.count();
if (seconds > 0.0) {
totalWeightedValue += history[i].value * seconds;
totalDurationSeconds += seconds;
}
}
if (totalDurationSeconds <= 0.0) {
return history.back().value;
}
return totalWeightedValue / totalDurationSeconds;
}
void Average::cleanupAndUpdate(TimePoint now) {
TimePoint windowStart =
now - std::chrono::duration_cast<Clock::duration>(windowSize);
// Remove all elements completely outside of the window
while (history.size() > 1 && history[1].timestamp <= windowStart) {
history.pop_front();
}
}
+49
View File
@@ -0,0 +1,49 @@
#ifndef AVERAGE_H
#define AVERAGE_H
#include <chrono>
#include <deque>
/*
This class allows you to compute the average value inside a given time window.
It computes the average using continuous time.
*/
class Average {
private:
using Clock = std::chrono::steady_clock;
using TimePoint = Clock::time_point;
using Duration = std::chrono::duration<double>;
struct TimeValue {
TimePoint timestamp;
double value;
};
std::deque<TimeValue> history;
Duration windowSize;
double currentValue = 0.0;
void cleanupAndUpdate(TimePoint now);
public:
Average(double windowSizeSeconds)
: windowSize(Duration(windowSizeSeconds)) {};
/*
@brief Modify the time window size
@param seconds time window duration
*/
void setWindowSize(double seconds);
/*
@brief Add another value with the current timestamp to the average.
*/
void addValue(double value);
/*
@brief Get the current average of all the values from [now - timewindow,
now].
@return The average value
*/
double getAverage();
};
#endif
+187
View File
@@ -0,0 +1,187 @@
#include "DemandHandler.h"
#include "../itc_pressure_optimizer.h"
#include "DemandHandlerConfig.h"
#include "odbxx.h"
#include "tmfe.h"
#include <functional>
#include <string>
using namespace DemandHandlerConfig;
bool DemandHandler::init() {
bool initSuccess = true;
if (!midas::odb::exists(this->path + "/" + DemandHandlerConfig::DEMAND)) {
TMFE::Instance()->Msg(MERROR, __FUNCTION__,
"Key at %s/%s doesn't exists", path.c_str(),
DemandHandlerConfig::DEMAND.c_str());
midas::odb o(this->path);
std::vector<float> v;
v.resize(static_cast<int>(ParameterIndex::MAXIMUM_PARAMETER_INDEX));
o[DemandHandlerConfig::DEMAND] = v;
TMFE::Instance()->Msg(MERROR, __FUNCTION__,
"Template generated at %s. Please fill it.",
path.c_str());
initSuccess = false;
}
midas::odb o(this->path);
// This is checked every start, in case of someone change the index in the
// config header
if (o[DemandHandlerConfig::DEMAND].size() <
static_cast<int>(ParameterIndex::MINIMAL_PRESSURE)) {
TMFE::Instance()->Msg(
MERROR, __FUNCTION__,
"Minimal pressure index is %i, where array size is %i.",
static_cast<int>(ParameterIndex::MINIMAL_PRESSURE), o.size());
initSuccess = false;
}
if (o[DemandHandlerConfig::DEMAND].size() <
static_cast<int>(ParameterIndex::MAXIMAL_PRESSURE)) {
TMFE::Instance()->Msg(
MERROR, __FUNCTION__,
"maximal pressure index is %i, where array size is %i.",
static_cast<int>(ParameterIndex::MAXIMAL_PRESSURE), o.size());
initSuccess = false;
}
if (o[DemandHandlerConfig::DEMAND].size() <
static_cast<int>(ParameterIndex::CONSTANTE_1)) {
TMFE::Instance()->Msg(
MERROR, __FUNCTION__,
"Constante 1 index is %i, where array size is %i.",
static_cast<int>(ParameterIndex::CONSTANTE_1), o.size());
initSuccess = false;
}
if (o[DemandHandlerConfig::DEMAND].size() <
static_cast<int>(ParameterIndex::CONSTANTE_2)) {
TMFE::Instance()->Msg(
MERROR, __FUNCTION__,
"Constante 2 index is %i, where array size is %i.",
static_cast<int>(ParameterIndex::CONSTANTE_2), o.size());
initSuccess = false;
}
if (o[DemandHandlerConfig::DEMAND].size() <
static_cast<int>(ParameterIndex::PRESSURE_CONTROL_MODE)) {
TMFE::Instance()->Msg(
MERROR, __FUNCTION__,
"Pressure control mode index is %i, where array size is %i.",
static_cast<int>(ParameterIndex::PRESSURE_CONTROL_MODE), o.size());
initSuccess = false;
}
if (o[DemandHandlerConfig::DEMAND].size() <
static_cast<int>(ParameterIndex::SET_PRESSURE)) {
TMFE::Instance()->Msg(
MERROR, __FUNCTION__,
"Set pressure index is %i, where array size is %i.",
static_cast<int>(ParameterIndex::SET_PRESSURE), o.size());
initSuccess = false;
}
if (!initSuccess)
throw std::runtime_error("DemandHandler init failed - see MIDAS "
"console and take care of all the errors\n");
this->pullMinimalPressure();
this->pullMaximalPressure();
this->pullConstante1();
this->pullConstante2();
this->pullPressureControlMode();
return true;
}
bool DemandHandler::setHotlink() {
midas::odb to_watch(this->path + "/" + DemandHandlerConfig::DEMAND);
to_watch.watch([&](midas::odb &arg) {
switch (arg.get_last_index()) {
case static_cast<int>(ParameterIndex::MINIMAL_PRESSURE):
pullMinimalPressure();
break;
case static_cast<int>(ParameterIndex::MAXIMAL_PRESSURE):
pullMaximalPressure();
break;
case static_cast<int>(ParameterIndex::CONSTANTE_1):
pullConstante1();
break;
case static_cast<int>(ParameterIndex::CONSTANTE_2):
pullConstante2();
break;
case static_cast<int>(ParameterIndex::PRESSURE_CONTROL_MODE):
pullPressureControlMode();
break;
case static_cast<int>(ParameterIndex::SET_PRESSURE):
pullSetPressure();
break;
default:
// Update out of scope, discarded
break;
}
});
return true;
}
void DemandHandler::pullMinimalPressure() {
midas::odb o(this->path);
float minimalPressure =
o[DemandHandlerConfig::DEMAND]
[static_cast<int>(ParameterIndex::MINIMAL_PRESSURE)];
Event event = {EventType::MINIMAL_PRESSURE, minimalPressure};
eventBus.publish(event);
}
void DemandHandler::pullMaximalPressure() {
midas::odb o(this->path);
float maximalPressure =
o[DemandHandlerConfig::DEMAND]
[static_cast<int>(ParameterIndex::MAXIMAL_PRESSURE)];
Event event = {EventType::MAXIMAL_PRESSURE, maximalPressure};
eventBus.publish(event);
}
void DemandHandler::pullConstante1() {
midas::odb o(this->path);
float constante1 = o[DemandHandlerConfig::DEMAND]
[static_cast<int>(ParameterIndex::CONSTANTE_1)];
Event event = {EventType::CONSTANTE_1, constante1};
eventBus.publish(event);
}
void DemandHandler::pullConstante2() {
midas::odb o(this->path);
float constance2 = o[DemandHandlerConfig::DEMAND]
[static_cast<int>(ParameterIndex::CONSTANTE_2)];
Event event = {EventType::CONSTANTE_2, constance2};
eventBus.publish(event);
}
void DemandHandler::pullPressureControlMode() {
midas::odb o(this->path);
float pressureControlMode =
o[DemandHandlerConfig::DEMAND]
[static_cast<int>(ParameterIndex::PRESSURE_CONTROL_MODE)];
Event event = {EventType::PRESSURE_CONTROL_MODE, pressureControlMode != 0};
eventBus.publish(event);
}
void DemandHandler::pullSetPressure() {
midas::odb o(this->path);
float setPressure = o[DemandHandlerConfig::DEMAND]
[static_cast<int>(ParameterIndex::SET_PRESSURE)];
Event event = {EventType::SET_PRESSURE, setPressure != 0};
eventBus.publish(event);
}
DemandHandler::DemandHandler(EventBus &eventBusReference,
std::string equipmentName)
: path(DemandHandlerConfig::PATH_PREFIX + equipmentName +
DemandHandlerConfig::PATH_SUFFIX),
eventBus(eventBusReference) {
eventBus.subscribe<itcPressureOptimizer::EquipmentInitEvent>(
[this](const itcPressureOptimizer::EquipmentInitEvent &e) {
this->init();
this->setHotlink();
});
}
+51
View File
@@ -0,0 +1,51 @@
#ifndef DEMAND_HANDLER_H
#define DEMAND_HANDLER_H
#include "../EventBus.h"
#include <string>
#include <variant>
class DemandHandler {
public:
enum class EventType {
MINIMAL_PRESSURE,
MAXIMAL_PRESSURE,
CONSTANTE_1,
CONSTANTE_2,
PRESSURE_CONTROL_MODE,
SET_PRESSURE
};
struct Event {
EventType type;
std::variant<float, bool> value;
};
DemandHandler(EventBus &eventBusReference, std::string equipmentName);
private:
std::string path;
EventBus &eventBus;
/*
Pull value from the ODB
*/
void pullMinimalPressure();
void pullMaximalPressure();
void pullConstante1();
void pullConstante2();
void pullPressureControlMode();
void pullSetPressure();
/*
Init process
*/
bool init();
/*
Setting ODB hotlink
*/
bool setHotlink();
};
#endif
+24
View File
@@ -0,0 +1,24 @@
#ifndef DEMAND_HANDLER_CONFIG_H
#define DEMAND_HANDLER_CONFIG_H
#include <string>
namespace DemandHandlerConfig {
const std::string PATH_PREFIX = "/Equipment/";
const std::string PATH_SUFFIX = "/Variables";
const std::string DEMAND = "Demand";
enum class ParameterIndex {
PRESSURE_CONTROL_MODE = 6,
SET_PRESSURE = 7,
MINIMAL_PRESSURE = 8,
MAXIMAL_PRESSURE = 9,
CONSTANTE_1 = 10,
CONSTANTE_2 = 11,
MAXIMUM_PARAMETER_INDEX = 11
};
}; // namespace DemandHandlerConfig
#endif
+162
View File
@@ -0,0 +1,162 @@
#include "FeedbackHandler.h"
#include "../EventBus.h"
#include "DemandHandler.h"
#include "FeedbackHandlerConfig.h"
#include "InputHandler.h"
#include "PressureCalculator.h"
#include <cmath>
#include <string>
FeedbackHandler::FeedbackHandler(EventBus &eventBusReference,
std::string equipmentName) {
this->path = FeedbackHandlerConf::PATH_PREFIX + equipmentName +
FeedbackHandlerConf::PATH_SUFFIX;
eventBusReference.subscribe<DemandHandler::Event>(
[this](const DemandHandler::Event &e) {
generateOdbKeyIfNeeded();
switch (e.type) {
case DemandHandler::EventType::PRESSURE_CONTROL_MODE:
enablePressureControlMode(std::get<bool>(e.value));
break;
case DemandHandler::EventType::SET_PRESSURE:
enableSetPressure(std::get<bool>(e.value));
break;
default:
break;
}
});
eventBusReference.subscribe<InputHandler::Event>(
[this](const InputHandler::Event &e) {
generateOdbKeyIfNeeded();
switch (e.type) {
case InputHandler::EventType::SP_VALUE:
setSP(e.value);
break;
case InputHandler::EventType::TEMPERATURE_VALUE:
setTemperature(e.value);
break;
case InputHandler::EventType::POWER_VALUE:
setPower(e.value);
break;
default:
break;
}
});
eventBusReference.subscribe<PressureCalculator::Event>(
[this](const PressureCalculator::Event &e) {
generateOdbKeyIfNeeded();
setConstante1(e.c1contrib);
setConstante2(e.c2contrib);
setPressureMinimum(e.uncapedPressure);
setPressureMaximum(e.uncapedPressure);
setPressure(e.cappedPressure);
setAveragePower(e.averagePower);
});
}
void FeedbackHandler::updatePressure() {
{
midas::odb o(path);
o[FeedbackHandlerConf::OUTPUT_VARNAME]
[static_cast<int>(FeedbackHandlerConf::RegisterIndex::PRESSURE)] =
cachedCappedPressure;
}
float cappedPressure =
std::roundf(cachedCappedPressure * 10.0f + 0.5f) / 10.0f;
// Pressure control mode is set to calculated?
if (isPressureControlModeEnable) {
midas::odb o(path);
o[FeedbackHandlerConf::INPUT_VARNAME][static_cast<int>(
FeedbackHandlerConf::RegisterIndex::PRESSURE_SET_POINT)] =
cappedPressure;
}
// update setpoint of Mercury ITC in ODB ?
if (isSetPressureEnable) {
midas::odb o(path);
o[FeedbackHandlerConf::OUTPUT_VARNAME][static_cast<int>(
FeedbackHandlerConf::RegisterIndex::PRESSURE_SET_POINT)] =
cappedPressure;
}
}
void FeedbackHandler::generateOdbKeyIfNeeded() {
// If the key doesn't exist, we create the template.
if (!midas::odb::exists(path + "/" + FeedbackHandlerConf::OUTPUT_VARNAME)) {
std::vector<float> v;
v.resize(static_cast<int>(
FeedbackHandlerConf::RegisterIndex::MAXIMUM_REGISTRY_INDEX));
midas::odb o(path);
o[FeedbackHandlerConf::OUTPUT_VARNAME] = v;
}
}
void FeedbackHandler::enablePressureControlMode(bool enable) {
isPressureControlModeEnable = enable;
updatePressure();
}
void FeedbackHandler::enableSetPressure(bool enable) {
isSetPressureEnable = enable;
updatePressure();
}
void FeedbackHandler::setPressure(float value) {
cachedCappedPressure = value;
updatePressure();
}
void FeedbackHandler::setConstante1(float value) {
midas::odb o(path);
o[FeedbackHandlerConf::OUTPUT_VARNAME]
[static_cast<int>(FeedbackHandlerConf::RegisterIndex::CONSTANTE_1)] =
value;
}
void FeedbackHandler::setAveragePower(float value) {
midas::odb o(path);
o[FeedbackHandlerConf::OUTPUT_VARNAME]
[static_cast<int>(FeedbackHandlerConf::RegisterIndex::AVERAGE_POWER)] =
value;
}
void FeedbackHandler::setConstante2(float value) {
midas::odb o(path);
o[FeedbackHandlerConf::OUTPUT_VARNAME]
[static_cast<int>(FeedbackHandlerConf::RegisterIndex::CONSTANTE_2)] =
value;
}
void FeedbackHandler::setPressureMinimum(float value) {
midas::odb o(path);
o[FeedbackHandlerConf::OUTPUT_VARNAME]
[static_cast<int>(FeedbackHandlerConf::RegisterIndex::MINIMUM_PRESSURE)] =
value;
}
void FeedbackHandler::setPressureMaximum(float value) {
midas::odb o(path);
o[FeedbackHandlerConf::OUTPUT_VARNAME]
[static_cast<int>(FeedbackHandlerConf::RegisterIndex::MAXIMUM_PRESSURE)] =
value;
}
void FeedbackHandler::setSP(float value) {
midas::odb o(path);
o[FeedbackHandlerConf::OUTPUT_VARNAME]
[static_cast<int>(FeedbackHandlerConf::RegisterIndex::SP)] = value;
}
void FeedbackHandler::setTemperature(float value) {
midas::odb o(path);
o[FeedbackHandlerConf::OUTPUT_VARNAME]
[static_cast<int>(FeedbackHandlerConf::RegisterIndex::TEMPERATURE)] =
value;
}
void FeedbackHandler::setPower(float value) {
midas::odb o(path);
o[FeedbackHandlerConf::OUTPUT_VARNAME]
[static_cast<int>(FeedbackHandlerConf::RegisterIndex::POWER)] = value;
}
+42
View File
@@ -0,0 +1,42 @@
#ifndef FEEDBACK_HANDLER_H
#define FEEDBACK_HANDLER_H
#include "../EventBus.h"
#include <string>
class FeedbackHandler {
public:
FeedbackHandler(EventBus &eventBusReference, std::string equipmentName);
private:
bool isPressureControlModeEnable;
bool isSetPressureEnable;
float cachedCappedPressure;
std::string path;
void generateOdbKeyIfNeeded();
/*
Internal value setters
*/
void enablePressureControlMode(bool enable);
void enableSetPressure(bool enable);
void setSP(float value);
void setTemperature(float value);
void setPressureMinimum(float value);
void setPressureMaximum(float value);
void setPower(float value);
void setAveragePower(float value);
void setPressure(float value);
void setConstante1(float value);
void setConstante2(float value);
/*
Update pressure value
*/
void updatePressure();
};
#endif
@@ -0,0 +1,30 @@
#ifndef FEEDBACK_HANDLER_CONFIG_H
#define FEEDBACK_HANDLER_CONFIG_H
#include <string>
namespace FeedbackHandlerConf {
const std::string PATH_PREFIX = "/Equipment/";
const std::string PATH_SUFFIX = "/Variables";
const std::string OUTPUT_VARNAME = "Measured";
const std::string INPUT_VARNAME = "Demand";
enum class RegisterIndex {
PRESSURE_SET_POINT = 0,
SP = 1,
TEMPERATURE = 2,
POWER = 3,
AVERAGE_POWER = 4,
PRESSURE = 5,
MINIMUM_PRESSURE = 8,
MAXIMUM_PRESSURE = 9,
CONSTANTE_1 = 10,
CONSTANTE_2 = 11,
MAXIMUM_REGISTRY_INDEX =
11 // Used as a safety guard for undersize array. Put this enum member
// always as the same value as the highest member
};
} // namespace FeedbackHandlerConf
#endif
+129
View File
@@ -0,0 +1,129 @@
#include "InputHandler.h"
#include "../EventBus.h"
#include "InputHandlerConfig.h"
#include "SettingsHandler.h"
#include "tmfe.h"
#include <string>
#include <variant>
void InputHandler::setPath(std::string name) {
this->path = InputHandlerConfig::PATH_PREFIX + name +
InputHandlerConfig::PATH_SUFFIX;
if (!midas::odb::exists(path)) {
TMFE::Instance()->Msg(MERROR, __FUNCTION__, "Key at %s doesn't exists",
path.c_str());
throw std::runtime_error("Key at " + path + " doesn't exists");
}
midas::odb o(path);
bool isKeyValid = true;
if (o.size() < SPIndex) {
TMFE::Instance()->Msg(MERROR, __FUNCTION__,
"SP index is %i, where array size is %i.",
SPIndex, o.size());
isKeyValid = false;
}
if (o.size() < temperatureIndex) {
TMFE::Instance()->Msg(
MERROR, __FUNCTION__,
"Temperature index is %i, where array size is %i.",
temperatureIndex, o.size());
isKeyValid = false;
}
if (o.size() < powerIndex) {
TMFE::Instance()->Msg(MERROR, __FUNCTION__,
"Power index is %i, where array size is %i.",
powerIndex, o.size());
isKeyValid = false;
}
if (!isKeyValid) {
throw std::runtime_error("InputHandler update path failed - see MIDAS "
"console and take care of all the errors\n");
}
}
void InputHandler::updateSPIndex(int index) {
this->SPIndex = index;
pullSPValue();
}
void InputHandler::updateTemperatureIndex(int index) {
this->temperatureIndex = index;
pullTemperatureValue();
}
void InputHandler::updatePowerIndex(int index) {
this->powerIndex = index;
pullPowerValue();
}
void InputHandler::pullSPValue() {
midas::odb o(this->path);
float SPValue = o[SPIndex];
Event event = {EventType::SP_VALUE, SPValue};
eventBus.publish(event);
}
void InputHandler::pullTemperatureValue() {
midas::odb o(this->path);
float temperatureValue = o[temperatureIndex];
Event event = {EventType::TEMPERATURE_VALUE, temperatureValue};
eventBus.publish(event);
}
void InputHandler::pullPowerValue() {
midas::odb o(this->path);
float powerValue = o[powerIndex];
printf("Sending power value %f\n", powerValue);
Event event = {EventType::POWER_VALUE, powerValue};
eventBus.publish(event);
}
void InputHandler::setHotlink() {
if (hotlink.has_value()) {
TMFE::Instance()->Msg(MERROR, __FUNCTION__,
"Hotlink already established. Will not update it "
"without restarting the frontend.");
return;
}
hotlink.emplace(path);
hotlink->watch([&](midas::odb &arg) {
int hotlinkIndex = arg.get_last_index();
if (hotlinkIndex == SPIndex) {
} else if (hotlinkIndex == temperatureIndex) {
pullTemperatureValue();
} else if (hotlinkIndex == powerIndex) {
pullPowerValue();
}
});
}
InputHandler::InputHandler(EventBus &eventBusReference)
: eventBus(eventBusReference) {
eventBus.subscribe<SettingsHandler::Event>(
[this](const SettingsHandler::Event &e) {
switch (e.type) {
case SettingsHandler::EventType::EQUIPMENT_NAME:
setPath(std::get<std::string>(e.value));
setHotlink();
break;
case SettingsHandler::EventType::INPUT_SP_INDEX:
updateSPIndex(std::get<int>(e.value));
break;
case SettingsHandler::EventType::INPUT_TEMPERATURE_INDEX:
updateTemperatureIndex(std::get<int>(e.value));
break;
case SettingsHandler::EventType::INPUT_POWER_INDEX:
updatePowerIndex(std::get<int>(e.value));
break;
default:
break;
}
});
}
+51
View File
@@ -0,0 +1,51 @@
#ifndef INPUT_HANDLER_H
#define INPUT_HANDLER_H
#include "../EventBus.h"
#include "odbxx.h"
#include <optional>
#include <string>
class InputHandler {
public:
InputHandler(EventBus &eventBusReference);
enum class EventType {
SP_VALUE,
TEMPERATURE_VALUE,
POWER_VALUE,
};
struct Event {
EventType type;
float value;
};
private:
EventBus &eventBus;
std::string path;
int SPIndex;
int temperatureIndex;
int powerIndex;
std::optional<midas::odb> hotlink;
/*
Internal value setters
*/
void setPath(std::string name);
void updateSPIndex(int index);
void updateTemperatureIndex(int index);
void updatePowerIndex(int index);
void setHotlink();
/*
Pull value from the ODB
*/
void pullSPValue();
void pullTemperatureValue();
void pullPowerValue();
};
#endif
+12
View File
@@ -0,0 +1,12 @@
#ifndef INPUT_HANDLER_CONFIG_H
#define INPUT_HANDLER_CONFIG_H
#include <string>
namespace InputHandlerConfig {
const std::string PATH_PREFIX = "/Equipment/";
const std::string PATH_SUFFIX = "/Variables/Input";
} // namespace InputHandlerConfig
#endif
+100
View File
@@ -0,0 +1,100 @@
#include "OutputHandler.h"
#include "DemandHandler.h"
#include "OutputHandlerConfig.h"
#include "PressureCalculator.h"
#include "SettingsHandler.h"
#include "tmfe.h"
#include <string>
void OutputHandler::enableSetPressure(bool enable) {
isSetPressureEnable = enable;
update();
}
void OutputHandler::setPressure(float value) {
pressure = value;
update();
}
void OutputHandler::setOutputPressureSPIndex(int index) {
outputPressureSPIndex = index;
update();
}
void OutputHandler::setEquipmentPath(std::string equipmentName) {
equipmentPath = OutputHandlerConfig::PATH_PREFIX + equipmentName +
OutputHandlerConfig::PATH_SUFFIX;
update();
}
void OutputHandler::update() {
if (equipmentPath.empty())
return;
static bool isIndexValid = true;
static bool isKeyValid = true;
midas::odb o(equipmentPath);
if (!midas::odb::exists(equipmentPath + "/" +
OutputHandlerConfig::OUTPUT_VARIABLE) &&
isKeyValid) {
TMFE::Instance()->Msg(MERROR, __FUNCTION__,
"Key at %s%s doesn't exists",
equipmentPath.c_str(),
OutputHandlerConfig::OUTPUT_VARIABLE.c_str());
isKeyValid = false;
} else {
isKeyValid = true;
}
if ((o[OutputHandlerConfig::OUTPUT_VARIABLE].size() <
outputPressureSPIndex) &&
isIndexValid) {
TMFE::Instance()->Msg(MERROR, __FUNCTION__,
"SP index is %i, where array size is %i at %s.",
outputPressureSPIndex, o.size(),
equipmentPath.c_str());
isIndexValid = false;
} else {
isIndexValid = true;
}
//// SHOULD I CRASH IF KEY NOT VALID ?
//// FOR NOW, FAILED SILENTLY
if (isSetPressureEnable && isKeyValid)
o(OutputHandlerConfig::OUTPUT_VARIABLE)[outputPressureSPIndex] =
pressure;
}
OutputHandler::OutputHandler(EventBus &eventBusReference) {
eventBusReference.subscribe<DemandHandler::Event>(
[this](const DemandHandler::Event &e) {
switch (e.type) {
case DemandHandler::EventType::SET_PRESSURE:
enableSetPressure(std::get<bool>(e.value));
break;
default:
break;
}
});
eventBusReference.subscribe<SettingsHandler::Event>(
[this](const SettingsHandler::Event &e) {
switch (e.type) {
case SettingsHandler::EventType::EQUIPMENT_NAME:
setEquipmentPath(std::get<std::string>(e.value));
break;
case SettingsHandler::EventType::OUTPUT_PRESSURE_SP_INDEX:
setOutputPressureSPIndex(std::get<int>(e.value));
break;
default:
break;
}
});
eventBusReference.subscribe<PressureCalculator::Event>(
[this](const PressureCalculator::Event &e) {
setPressure(e.cappedPressure);
});
}
+33
View File
@@ -0,0 +1,33 @@
#ifndef OUTPUT_HANDLER_H
#define OUTPUT_HANDLER_H
#include "../EventBus.h"
#include "DemandHandler.h"
#include "PressureCalculator.h"
#include <string>
class OutputHandler {
public:
OutputHandler(EventBus &eventBusReference);
private:
bool isSetPressureEnable;
std::string equipmentPath;
float pressure;
int outputPressureSPIndex;
/*
Internal value setters
*/
void enableSetPressure(bool enable);
void setPressure(float value);
void setOutputPressureSPIndex(int index);
void setEquipmentPath(std::string equipmentName);
/*
Update value of the ODB
*/
void update();
};
#endif
+13
View File
@@ -0,0 +1,13 @@
#ifndef OUTPUT_HANDLER_CONFIG_H
#define OUTPUT_HANDLER_CONFIG_H
#include <string>
namespace OutputHandlerConfig {
const std::string PATH_PREFIX = "/Equipment/";
const std::string PATH_SUFFIX = "/Variables";
const std::string OUTPUT_VARIABLE = "Output";
} // namespace OutputHandlerConfig
#endif
+119
View File
@@ -0,0 +1,119 @@
#include "PressureCalculator.h"
#include "../itc_pressure_optimizer.h"
#include "Average.h"
#include "DemandHandler.h"
#include "InputHandler.h"
#include <string>
void PressureCalculator::update() {
double averagePower = average.getAverage();
double c2contrib = averagePower * cachedConstante2;
double c1contrib =
cachedConstante1 * (cachedTemperature - cachedSP - c2contrib);
double uncapedPressure = cachedMinimalPressure + c1contrib;
double cappedPressure = uncapedPressure;
if (c2contrib != 0) {
c2contrib = -c2contrib * cachedConstante1;
} else {
c2contrib = 0.0f;
}
if (cappedPressure < cachedMinimalPressure)
cappedPressure = cachedMinimalPressure;
if (cappedPressure > cachedMaximalPressure)
cappedPressure = cachedMaximalPressure;
Event event = {};
event.c1contrib = c1contrib;
event.c2contrib = c2contrib;
event.uncapedPressure = uncapedPressure;
event.cappedPressure = cappedPressure;
event.averagePower = averagePower;
eventBus.publish(event);
}
PressureCalculator::PressureCalculator(EventBus &eventBusReference)
: average(30), eventBus(eventBusReference) {
eventBus.subscribe<DemandHandler::Event>(
[this](const DemandHandler::Event &e) {
switch (e.type) {
case DemandHandler::EventType::MINIMAL_PRESSURE:
updateMinimalPressure(std::get<float>(e.value));
break;
case DemandHandler::EventType::MAXIMAL_PRESSURE:
updateMaximalPressure(std::get<float>(e.value));
break;
case DemandHandler::EventType::CONSTANTE_1:
updateConstante1(std::get<float>(e.value));
break;
case DemandHandler::EventType::CONSTANTE_2:
updateConstante2(std::get<float>(e.value));
break;
default:
break;
}
});
eventBus.subscribe<InputHandler::Event>(
[this](const InputHandler::Event &e) {
switch (e.type) {
case InputHandler::EventType::SP_VALUE:
updateSP(e.value);
break;
case InputHandler::EventType::TEMPERATURE_VALUE:
updateTemperature(e.value);
break;
case InputHandler::EventType::POWER_VALUE:
updatePower(e.value);
break;
default:
break;
}
});
eventBus.subscribe<itcPressureOptimizer::EquipmentPollEvent>(
[this](const itcPressureOptimizer::EquipmentPollEvent &e) {
update();
});
}
void PressureCalculator::updateSP(float value) {
cachedSP = value;
update();
}
void PressureCalculator::updateTemperature(float value) {
cachedTemperature = value;
update();
}
void PressureCalculator::updatePower(float value) {
cachedPower = value;
average.addValue(static_cast<double>(value));
update();
}
void PressureCalculator::updateMinimalPressure(float value) {
cachedMinimalPressure = value;
update();
}
void PressureCalculator::updateMaximalPressure(float value) {
cachedMaximalPressure = value;
update();
}
void PressureCalculator::updateConstante1(float value) {
cachedConstante1 = value;
update();
}
void PressureCalculator::updateConstante2(float value) {
cachedConstante2 = value;
update();
}
+52
View File
@@ -0,0 +1,52 @@
#ifndef PRESSURE_CALCULATOR_H
#define PRESSURE_CALCULATOR_H
#include "../EventBus.h"
#include "Average.h"
class PressureCalculator {
public:
PressureCalculator(EventBus &eventBusReference);
struct Event {
float c1contrib;
float c2contrib;
float uncapedPressure;
float cappedPressure;
float averagePower;
};
private:
Average average;
EventBus &eventBus;
float cachedSP;
float cachedTemperature;
float cachedPower;
float cachedMinimalPressure;
float cachedMaximalPressure;
float cachedConstante1;
float cachedConstante2;
/*
Internal value setters (from InputHandler)
*/
void updateSP(float value);
void updateTemperature(float value);
void updatePower(float value);
/*
Internal value setters (from DemandHandler)
*/
void updateMinimalPressure(float value);
void updateMaximalPressure(float value);
void updateConstante1(float value);
void updateConstante2(float value);
/*
Update pressure value
*/
void update();
};
#endif
+124
View File
@@ -0,0 +1,124 @@
#include "SettingsHandler.h"
#include "../itc_pressure_optimizer.h"
#include "SettingsHandlerConfig.h"
#include <string>
using namespace SettingsHandlerConfig;
void SettingsHandler::pullEquipmentName() {
midas::odb o(this->path);
std::string equipmentName = o[SettingsHandlerConfig::EQUIPMENT_NAME];
Event event = {EventType::EQUIPMENT_NAME, equipmentName};
eventBus.publish(event);
}
void SettingsHandler::pullOutputPressureSPIndex() {
midas::odb o(this->path);
int outputPressureSPIndex =
o[SettingsHandlerConfig::OUTPUT_PRESSURE_SP_INDEX];
Event event = {EventType::OUTPUT_PRESSURE_SP_INDEX, outputPressureSPIndex};
eventBus.publish(event);
}
void SettingsHandler::pullInputSPIndex() {
midas::odb o(this->path);
int inputSPIndex = o[SettingsHandlerConfig::INPUT_SP_INDEX];
Event event = {EventType::INPUT_SP_INDEX, inputSPIndex};
eventBus.publish(event);
}
void SettingsHandler::pullInputTemperatureIndex() {
midas::odb o(this->path);
int temperatureIndex = o[SettingsHandlerConfig::INPUT_TEMPERATURE_INDEX];
Event event = {EventType::INPUT_TEMPERATURE_INDEX, temperatureIndex};
eventBus.publish(event);
}
void SettingsHandler::pullInputPowerIndex() {
midas::odb o(this->path);
int powerIndex = o[SettingsHandlerConfig::INPUT_POWER_INDEX];
Event event = {EventType::INPUT_POWER_INDEX, powerIndex};
eventBus.publish(event);
}
SettingsHandler::SettingsHandler(EventBus &eventBusReference,
std::string equipmentName)
: path(SettingsHandlerConfig::PATH_PREFIX + equipmentName +
SettingsHandlerConfig::PATH_SUFFIX),
eventBus(eventBusReference) {
eventBus.subscribe<itcPressureOptimizer::EquipmentInitEvent>(
[this](const itcPressureOptimizer::EquipmentInitEvent &e) {
this->init();
});
}
bool SettingsHandler::init() {
midas::odb o(this->path);
bool areKeysValids = true;
if (!midas::odb::exists(path + SettingsHandlerConfig::EQUIPMENT_NAME)) {
TMFE::Instance()->Msg(MERROR, __FUNCTION__,
"Key at %s%s doesn't exists", path.c_str(),
SettingsHandlerConfig::EQUIPMENT_NAME.c_str());
areKeysValids = false;
}
if (!midas::odb::exists(path +
SettingsHandlerConfig::OUTPUT_PRESSURE_SP_INDEX)) {
TMFE::Instance()->Msg(
MERROR, __FUNCTION__, "Key at %s%s doesn't exists", path.c_str(),
SettingsHandlerConfig::OUTPUT_PRESSURE_SP_INDEX.c_str());
areKeysValids = false;
}
if (!midas::odb::exists(path + SettingsHandlerConfig::INPUT_SP_INDEX)) {
TMFE::Instance()->Msg(MERROR, __FUNCTION__,
"Key at %s%s doesn't exists", path.c_str(),
SettingsHandlerConfig::INPUT_SP_INDEX.c_str());
areKeysValids = false;
}
if (!midas::odb::exists(path +
SettingsHandlerConfig::INPUT_TEMPERATURE_INDEX)) {
TMFE::Instance()->Msg(
MERROR, __FUNCTION__, "Key at %s%s doesn't exists", path.c_str(),
SettingsHandlerConfig::INPUT_TEMPERATURE_INDEX.c_str());
areKeysValids = false;
}
if (!midas::odb::exists(path + SettingsHandlerConfig::INPUT_POWER_INDEX)) {
TMFE::Instance()->Msg(MERROR, __FUNCTION__,
"Key at %s%s doesn't exists", path.c_str(),
SettingsHandlerConfig::INPUT_POWER_INDEX.c_str());
areKeysValids = false;
}
if (!areKeysValids) {
midas::odb o = {
{SettingsHandlerConfig::EQUIPMENT_NAME.c_str(),
{SettingsHandlerConfig::DEFAULT_EQUIPMENT_NAME.c_str()}},
{SettingsHandlerConfig::OUTPUT_PRESSURE_SP_INDEX.c_str(),
{SettingsHandlerConfig::DEFAULT_INDEX_VALUE}},
{SettingsHandlerConfig::INPUT_SP_INDEX.c_str(),
{SettingsHandlerConfig::DEFAULT_INDEX_VALUE}},
{SettingsHandlerConfig::INPUT_TEMPERATURE_INDEX.c_str(),
{SettingsHandlerConfig::DEFAULT_INDEX_VALUE}},
{SettingsHandlerConfig::INPUT_POWER_INDEX.c_str(),
{SettingsHandlerConfig::DEFAULT_INDEX_VALUE}},
{SettingsHandlerConfig::TIME_WINDOW.c_str(),
{SettingsHandlerConfig::DEFAULT_TIME_WINDOWS_SIZE}}};
TMFE::Instance()->Msg(
MERROR, __FUNCTION__,
"Some keys are missing at %s. Please fill the generated template.",
path.c_str());
o.connect(this->path);
throw std::runtime_error("SettinsHandler init failed - see MIDAS "
"console and take care of all the errors\n");
}
this->pullEquipmentName();
this->pullOutputPressureSPIndex();
this->pullInputSPIndex();
this->pullInputTemperatureIndex();
this->pullInputPowerIndex();
return true;
}
+45
View File
@@ -0,0 +1,45 @@
#ifndef SETTINGS_HANDLER_H
#define SETTINGS_HANDLER_H
#include "InputHandler.h"
#include <functional>
#include <string>
#include <variant>
class SettingsHandler {
public:
SettingsHandler(EventBus &eventBusReference, std::string equipmentName);
enum class EventType {
EQUIPMENT_NAME,
OUTPUT_PRESSURE_SP_INDEX,
INPUT_SP_INDEX,
INPUT_TEMPERATURE_INDEX,
INPUT_POWER_INDEX
};
struct Event {
EventType type;
std::variant<int, std::string> value;
};
private:
std::string path;
EventBus &eventBus;
/*
Init process
*/
bool init();
/*
Pull value from the ODB
*/
void pullEquipmentName();
void pullOutputPressureSPIndex();
void pullInputSPIndex();
void pullInputTemperatureIndex();
void pullInputPowerIndex();
};
#endif
@@ -0,0 +1,24 @@
#ifndef SETTINGS_HANDLER_CONFIG_H
#define SETTINGS_HANDLER_CONFIG_H
#include <string>
namespace SettingsHandlerConfig {
const std::string PATH_PREFIX = "/Equipment/";
const std::string PATH_SUFFIX = "/Settings/Devices/ItcPressureOptimizer/DD/";
const std::string EQUIPMENT_NAME = "Equipment name";
const std::string OUTPUT_PRESSURE_SP_INDEX = "Output Pressure SetPoint Index";
const std::string INPUT_SP_INDEX = "Input SetPoint Index";
const std::string INPUT_TEMPERATURE_INDEX = "Input Temperature Index";
const std::string INPUT_POWER_INDEX = "Input Power Index";
const std::string TIME_WINDOW = "Average Power Time Window";
const std::string DEFAULT_EQUIPMENT_NAME = "";
const int DEFAULT_INDEX_VALUE = -1;
const float DEFAULT_TIME_WINDOWS_SIZE = 30.0f;
} // namespace SettingsHandlerConfig
#endif
+15 -822
View File
@@ -1,833 +1,26 @@
#include "itc_pressure_optimizer.h"
#include "itc_pressure_optimizer_config.h"
#include "itc_pressure_optimizer_info.h"
#include "midas.h"
#include "odbxx.h"
#include "tmfe.h"
#include <cmath>
#include <cstring>
#include <ctime>
#include <string>
itcPressureOptimizer::itcPressureOptimizer(std::string equipmentName,
const char *equipmentFilename,
int channel)
: TMFeEquipment(equipmentName.c_str(), equipmentFilename) {
const char *equipmentFilename)
: TMFeEquipment(equipmentName.c_str(), equipmentFilename), eventBus(),
demandHandler(eventBus, equipmentName),
settingsHandler(eventBus, equipmentName), inputHandler(eventBus),
pressureCalculator(eventBus), feedbackHandler(eventBus, equipmentName),
outputHandler(eventBus) {
fEqConfPeriodMilliSec = 1000; // refresh every seconds
fEqConfLogHistory = 1;
fEqConfReadOnlyWhenRunning = false;
fEqConfPeriodMilliSec = 100;
this->equipmentPath = std::string("/Equipment/") + equipmentName;
info.num_channels = channel;
fEqConfWriteEventsToOdb = true;
}
void itcPressureOptimizer::HandlePeriodic() {
eventBus.publish(EquipmentPollEvent{});
}
TMFeResult
itcPressureOptimizer::HandleInit(const std::vector<std::string> &args) {
this->mitc_pressc_init();
eventBus.publish(EquipmentInitEvent{});
return TMFeResult();
}
void itcPressureOptimizer::HandlePeriodic() {
midas::odb o(this->equipmentPath + "/" + Configuration::VARIABLE_DIR);
for (int i = 0; i < info.num_channels; i++) {
info.last_measured[i] = o[Configuration::MEASURED_VARNAME.c_str()][i];
info.last_demand[i] = o[Configuration::DEMAND_VARNAME.c_str()][i];
}
this->mitc_pressc_update_mitc_input();
this->mitc_pressc_update_pressure_demanded();
this->mitc_pressc_recalculate();
for (int i = 0; i < info.num_channels; i++) {
o[Configuration::MEASURED_VARNAME.c_str()][i] = info.last_measured[i];
o[Configuration::DEMAND_VARNAME.c_str()][i] = info.last_demand[i];
}
}
INT itcPressureOptimizer::mitc_pressc_init() {
int status = SUCCESS;
bool isDefaultNameExists;
bool isDefaultThresholdExists;
bool isDemandExists;
bool isMeasuredExists;
isDefaultNameExists = midas::odb::exists(this->equipmentPath + "/" +
Configuration::SETTINGS_DIR + "/" +
Configuration::NAMES_VARNAME);
isDefaultThresholdExists = midas::odb::exists(
this->equipmentPath + "/" + Configuration::SETTINGS_DIR + "/" +
Configuration::UPDATE_THRESHOLD_MEASURED_VARNAME);
isDemandExists = midas::odb::exists(this->equipmentPath + "/" +
Configuration::DEMANDE_PATH);
isMeasuredExists = midas::odb::exists(this->equipmentPath + "/" +
Configuration::MEASURED_PATH);
if (!isDefaultNameExists || !isDefaultThresholdExists) {
midas::odb o = {
{Configuration::VARIABLE_DIR.c_str(),
{{Configuration::DEMAND_VARNAME.c_str(), {0.0}},
{Configuration::MEASURED_VARNAME.c_str(), {0.0}}}},
{Configuration::SETTINGS_DIR.c_str(),
{{Configuration::NAMES_VARNAME.c_str(), {""}},
{Configuration::UPDATE_THRESHOLD_MEASURED_VARNAME.c_str(), {0.0}},
{Configuration::DEVICES_DIR.c_str(),
{{Configuration::MIT_DIR.c_str(),
{{Configuration::ENABLE_VARNAME.c_str(), {true}},
{Configuration::DD_DIR.c_str(),
{{Configuration::MITC_EQUIPMENT_VARNAME.c_str(), {""}},
{Configuration::MITC_OUTPUT_PRESS_SP_INDEX_VARNAME.c_str(),
{0}},
{Configuration::MITC_INPUT_VARIOX_SP_INDEX_VARNAME.c_str(),
{0}},
{Configuration::MITC_INPUT_VARIOX_TEMP_INDEX_VARNAME
.c_str(),
{0}},
{Configuration::MITC_INPUT_VARIOX_POW_INDEX_VARNAME.c_str(),
{0}},
{Configuration::RECALC_INTERVAL_VARNAME.c_str(), {0}},
{Configuration::MITC_READOUT_INTERVAL_VARNAME.c_str(), {0}},
{Configuration::PRESS_READOUT_INTERVAL_VARNAME.c_str(),
{0}}}}}}}}}}};
o.connect(this->equipmentPath.c_str());
}
if (!isDefaultNameExists) {
midas::odb o(this->equipmentPath + "/" + Configuration::SETTINGS_DIR);
std::vector<std::string> nameLists =
o[Configuration::NAMES_VARNAME.c_str()];
nameLists.resize(info.num_channels);
for (int i = 0; i < info.num_channels; i++) {
std::string res = this->getDefaultName(i);
nameLists.at(i) = res;
printf("%s\n", res.c_str());
}
o[Configuration::NAMES_VARNAME.c_str()] = nameLists;
}
if (!isDefaultThresholdExists) {
midas::odb o(this->equipmentPath + "/" + Configuration::SETTINGS_DIR);
o[Configuration::UPDATE_THRESHOLD_MEASURED_VARNAME.c_str()].resize(
info.num_channels);
for (int i = 0; i < info.num_channels; i++) {
o[Configuration::UPDATE_THRESHOLD_MEASURED_VARNAME.c_str()][i] =
this->getDefaultThreshold(i);
}
}
if (!isDemandExists) {
midas::odb o(this->equipmentPath + "/" + Configuration::VARIABLE_DIR);
std::vector<float> demandLists =
o[Configuration::DEMAND_VARNAME.c_str()];
demandLists.resize(info.num_channels);
o[Configuration::DEMAND_VARNAME.c_str()] = demandLists;
}
if (!isMeasuredExists) {
midas::odb o(this->equipmentPath + "/" + Configuration::VARIABLE_DIR);
std::vector<float> measuredLists =
o[Configuration::MEASURED_VARNAME.c_str()];
measuredLists.resize(info.num_channels);
o[Configuration::MEASURED_VARNAME.c_str()] = measuredLists;
}
/*
Caching all the value of /DD folder
*/
{
midas::odb o(this->equipmentPath.c_str());
info.mitcpressc_settings.mitcequipment =
o[Configuration::MITC_EQUIPMENT_PATH.c_str()];
info.mitcpressc_settings.setpressindex =
o[Configuration::MITC_OUTPUT_PRESS_SP_INDEX_PATH.c_str()];
info.mitcpressc_settings.getvarioxspindex =
o[Configuration::MITC_INPUT_VARIOX_SP_INDEX_PATH.c_str()];
info.mitcpressc_settings.getvarioxtempindex =
o[Configuration::MITC_INPUT_VARIOX_TEMP_INDEX_PATH.c_str()];
info.mitcpressc_settings.getvarioxpowindex =
o[Configuration::MITC_INPUT_VARIOX_POW_INDEX_PATH.c_str()];
info.mitcpressc_settings.recalcinterval =
o[Configuration::RECALC_INTERVAL_PATH.c_str()];
info.mitcpressc_settings.mitcinterval =
o[Configuration::MITC_READOUT_INTERVAL_PATH.c_str()];
info.mitcpressc_settings.pressinterval =
o[Configuration::PRESS_READOUT_INTERVAL_PATH.c_str()];
}
info.mitcPath = Configuration::SLASH + Configuration::EQUIPMENT_DIR +
Configuration::SLASH +
info.mitcpressc_settings.mitcequipment +
Configuration::SLASH + Configuration::VARIABLE_DIR;
info.last_demand.resize(info.num_channels);
info.last_demand_set.resize(info.num_channels);
info.last_measured.resize(info.num_channels);
// set invalid values to avoid calculation before init
for (int i = 0; i < info.num_channels; i++) {
info.last_measured.at(i) = -2.f;
}
info.lastlog = 0;
info.last_press_mitc_output = 0;
info.recalculate = TRUE;
info.pending = TRUE;
info.press_mitc_output = -1.0f;
info.nvals_mitc_input = 0;
info.nvals_mitc_output = 0;
info.ihis = 0;
for (int i = 0; i < MAX_HIS; i++) {
info.history[i] = -1.f; // invalidate history
info.history_time[i] = 0; // no value archived yet
}
// Check equipment Variables Input & Index
if (!(info.mitcpressc_settings.mitcequipment == "NONE") &&
!info.mitcpressc_settings.mitcequipment.empty()) {
std::string mitc_input_path = std::string("/Equipment/") +
info.mitcpressc_settings.mitcequipment +
"/" + Configuration::VARIABLE_DIR;
midas::odb o(mitc_input_path.c_str());
// INPUTS
info.nvals_mitc_input = o[Configuration::INPUT_VARNAME.c_str()].size();
if (info.nvals_mitc_input > 0) {
info.mitc_input.resize(info.nvals_mitc_input);
cm_msg(
MLOG, "",
"Reading Mercury ITC Variables/Input values for Equipment %s",
info.mitcpressc_settings.mitcequipment.c_str());
// check if index < nvals
if ((info.mitcpressc_settings.getvarioxspindex < 0) ||
(info.mitcpressc_settings.getvarioxspindex >=
info.nvals_mitc_input)) {
status = CM_SET_ERROR;
cm_msg(MERROR, "mitc_pressc_init",
"ERROR array index %d to read temperature"
" setpoint is invalid",
info.mitcpressc_settings.getvarioxspindex);
} else {
cm_msg(
MLOG, "",
"Input Index to read Temperature SP of Equipment %s is %d",
info.mitcpressc_settings.mitcequipment.c_str(),
info.mitcpressc_settings.getvarioxspindex);
}
// check if index < nvals
if ((info.mitcpressc_settings.getvarioxtempindex < 0) ||
(info.mitcpressc_settings.getvarioxtempindex >=
info.nvals_mitc_input)) {
status = CM_SET_ERROR;
cm_msg(MERROR, "mitc_pressc_init",
"ERROR array index %d to read temperature"
" is invalid",
info.mitcpressc_settings.getvarioxtempindex);
} else {
cm_msg(MLOG, "",
"Input Index to read Temperature of Equipment %s is %d",
info.mitcpressc_settings.mitcequipment.c_str(),
info.mitcpressc_settings.getvarioxtempindex);
}
// check if index < nvals
if ((info.mitcpressc_settings.getvarioxpowindex < 0) ||
(info.mitcpressc_settings.getvarioxpowindex >=
info.nvals_mitc_input)) {
status = CM_SET_ERROR;
cm_msg(MERROR, "mitc_pressc_init",
"ERROR array index %d to read heater pow"
"er is invalid",
info.mitcpressc_settings.getvarioxpowindex);
} else {
cm_msg(MLOG, "",
"Input Index to read heater power of Equipment %s is %d",
info.mitcpressc_settings.mitcequipment.c_str(),
info.mitcpressc_settings.getvarioxpowindex);
}
} else {
cm_msg(MERROR, "mitc_pressc_init",
"ERROR Invalid number of channels %d for "
"Mercury ITC Variables/Input!",
info.nvals_mitc_input);
}
// OUTPUTS
info.nvals_mitc_output =
o[Configuration::OUTPUT_VARNAME.c_str()].size();
if (info.nvals_mitc_output > 0) {
// check if index < nvals
if ((info.mitcpressc_settings.setpressindex < 0) ||
(info.mitcpressc_settings.setpressindex >=
info.nvals_mitc_output)) {
status = CM_SET_ERROR;
cm_msg(MERROR, "mitc_pressc_init",
"ERROR array index %d to read/set pres"
"sure setpoint is invalid",
info.mitcpressc_settings.setpressindex);
} else {
cm_msg(MLOG, "",
"Output Index to read/set Pressure SP of Equipment %s "
"is %d",
info.mitcpressc_settings.mitcequipment.c_str(),
info.mitcpressc_settings.setpressindex);
}
} else {
cm_msg(MERROR, "mitc_pressc_init",
"ERROR Invalid number of channels %d for "
"measured pressure!",
info.nvals_mitc_output);
status = DB_NO_KEY;
}
} else {
cm_msg(MLOG, "",
"Equipment Name to Set/Get Mercury ITC Variables is not set-up "
"in MITCPRESSC/DD");
}
// FAILED PATH
if (status != SUCCESS) {
cm_msg(MLOG, "", "mitc_pressc_init : ERROR initialising device");
exit(EXIT_FAILURE);
}
printf("hey \n");
// SUCCESS PATH
// Init value
for (int i = 0; i < info.num_channels; i++) {
info.last_demand[i] = -1.f;
info.last_measured[i] = -2.f;
}
// NOTE: calculation will not be performed until info.initialized is TRUE
// this is done in mitc_pressc_set() when the last parameter is
// initialized
/*
Hot link
*/
midas::odb to_watch(this->equipmentPath + "/" +
Configuration::VARIABLE_DIR + "/" +
Configuration::DEMAND_VARNAME);
to_watch.watch([&](midas::odb &arg) {
midas::odb o(this->equipmentPath + "/" + Configuration::VARIABLE_DIR);
int index = arg.get_last_index();
float value = o[Configuration::DEMAND_VARNAME][arg.get_last_index()];
this->mitc_pressc_set(index, value);
});
return FE_SUCCESS;
}
INT itcPressureOptimizer::mitc_pressc_exit() {
return DB_SUCCESS;
// Nothing to do, since we do not manage memory with vector. All is drop
// when out of scope
}
INT itcPressureOptimizer::mitc_pressc_update_mitc_input() {
if (info.last_mitc_input > ss_time()) // system time reset?
info.last_mitc_input =
ss_time() - info.mitcpressc_settings.mitcinterval + 1;
// Time to read ?
// TODO : (info.hkey_mitc_input != 0) && (info.hkey_mitc_output != 0)
// condition is not checked, May need to add another check if necessary
bool timeoutThreshold =
static_cast<INT>((ss_time() - info.last_mitc_input)) >
info.mitcpressc_settings.mitcinterval;
if (!timeoutThreshold) {
return FE_SUCCESS;
}
float tpow;
{
std::string mitc_input_path = std::string("/Equipment/") +
info.mitcpressc_settings.mitcequipment +
"/" + Configuration::VARIABLE_DIR;
midas::odb o(mitc_input_path.c_str());
info.mitc_input = o[Configuration::INPUT_VARNAME.c_str()];
}
// TODO : Understand what this piece of code is used, and does the float
// condition correct ... "!=" is suspect
float sum, avg;
int i, nvals;
DWORD curtime;
// measured MITCPRESSC_MercuryITC Loop0 Temperature Setpoint 1
printf("update temps\n");
if ((info.mitc_input[info.mitcpressc_settings.getvarioxspindex]) != -1) {
printf("last measured\n");
printf("index %d value %f\n", info.mitcpressc_settings.getvarioxspindex,
info.mitc_input[info.mitcpressc_settings.getvarioxspindex]);
if (info.last_measured[1] !=
info.mitc_input[info.mitcpressc_settings.getvarioxspindex]) {
info.last_measured[1] =
info.mitc_input[info.mitcpressc_settings.getvarioxspindex];
info.recalculate = TRUE;
}
}
// measured MITCPRESSC_Mercury ITCTemperat(ure) 2
if (info.mitcpressc_settings.getvarioxtempindex != -1) {
if (info.last_measured[2] !=
(info.mitc_input[info.mitcpressc_settings.getvarioxtempindex])) {
info.last_measured[2] =
info.mitc_input[info.mitcpressc_settings.getvarioxtempindex];
info.recalculate = TRUE;
}
}
// measured MITCPRESSC_MercuryITC Loop0 Power 3
// here in Watt
if (info.mitcpressc_settings.getvarioxpowindex != -1) {
tpow = info.mitc_input[info.mitcpressc_settings.getvarioxpowindex];
} else {
tpow = -1.f; // invalid - should not occur
}
if (tpow < 0.f) {
tpow = -1.f; // invalid - should not occur
} else {
// archive current value
info.history[info.ihis] = tpow;
info.history_time[info.ihis] = ss_time();
info.ihis += 1;
if (info.ihis >= MAX_HIS)
info.ihis = 0; // ring buffer
// calculate average power
curtime = ss_time();
}
info.last_measured[3] = tpow;
// sum valid power readings read in the last 30 secs
for (sum = 0.0, nvals = 0, i = 0; i < MAX_HIS; i++) {
// valid power?
if (info.history[i] >= 0.f) {
// measured in the last 30 secs
if (info.history_time[i] + 30 > curtime) {
sum += info.history[i];
nvals++;
}
}
}
// calculate average
if (nvals > 0)
avg = sum / (float)nvals;
else
avg = -1.f;
if (info.last_measured[4] != avg) {
// measured MITCPRESSC_AveragedPower 4
info.last_measured[4] = avg;
info.recalculate = TRUE;
}
// TODO : in case of error accessing the ODB, this should be executed
// if (*(info.last_measured + 1) != -1.f) {
// cm_msg(MERROR, "mitc_pressc_update_mitc_input",
// "Error %d returned by "
// "db_get_data() reading Mercury ITC Measured from ODB!",
// status);
// *(info.last_measured + 1) = -1.f;
// *(info.last_measured + 2) = -1.f;
// *(info.last_measured + 3) = -1.f;
// *(info.last_measured + 4) = -1.f;
// }
info.last_mitc_input = ss_time();
return FE_SUCCESS;
}
INT itcPressureOptimizer::mitc_pressc_update_pressure_demanded() {
if (info.last_press_mitc_output > ss_time()) // time reset?
info.last_press_mitc_output =
ss_time() - info.mitcpressc_settings.pressinterval + 1;
// time to read?
if (static_cast<INT>(ss_time() - info.last_press_mitc_output) >
info.mitcpressc_settings.pressinterval) {
// read current demanded pressure from odb
midas::odb o(info.mitcPath);
// TODO : Check if the variable is present to prevent crash
info.press_mitc_output = o[Configuration::OUTPUT_VARNAME.c_str()]
[info.mitcpressc_settings.setpressindex];
info.last_press_mitc_output = ss_time();
}
return FE_SUCCESS;
}
INT itcPressureOptimizer::mitc_pressc_recalculate() {
if (!info.initialised) {
printf("not initialised\n");
return FE_SUCCESS;
}
if (info.pending) {
info.recalculate = TRUE;
info.pending = FALSE;
}
if (info.last_recalculated > ss_time())
info.last_recalculated =
ss_time() - info.mitcpressc_settings.recalcinterval + 1;
// recalculate flagged? or time to recalculate?
if (info.recalculate ||
(static_cast<INT>(ss_time() - info.last_recalculated) >
info.mitcpressc_settings.recalcinterval)) {
/* --- do pressure calculation --- */
if (info.last_measured[4] >= 0.f) {
// Calculate presure
printf("calculate pressure \n");
info.pending = FALSE;
// Mercury ITC Temperature setpoint invalid ?
if (info.last_measured[1] < 0.f)
info.pending = TRUE;
// Mercury ITC Temperature measured invalid ?
if (info.last_measured[2] < .0f)
info.pending = TRUE;
// COMMENT FROM ANDREAS
// NIY maybe check also validity of minpress, maxpress, c1, c2
// in info.last_demand
if (!info.pending) {
float press, c1contrib, c2contrib;
// pressure calculation:
// p = pmin+c1*(Tmeas-Tset-avPower*c2)
// if (p < pmin) p = pmin
// if (p > pmax) p = pmax
c2contrib = info.last_measured[4] * info.last_demand[11];
c1contrib =
info.last_demand[10] *
(info.last_measured[2] - info.last_measured[1] - c2contrib);
press = info.last_demand[8] + c1contrib;
info.last_measured[10] = c1contrib;
if (c2contrib != 0.0f)
info.last_measured[11] = -c2contrib * info.last_demand[10];
else
info.last_measured[11] = 0.0f;
info.last_measured[8] = press;
info.last_measured[9] = press;
// Pressure smaller than min? -> take min
if (press < info.last_demand[8])
press = info.last_demand[8];
// Presure larger than max? -> take max
if (press > info.last_demand[9])
press = info.last_demand[9];
// update measured of MITCPRESSC_CalcPressure 5
info.last_measured[5] = press;
// Pressure control mode is set to calculated?
if (info.last_demand[6] == 1.0f) {
float presst = std::roundf(press * 10.0f + 0.5f) /
10.0f; // Round one decimal
// only necessary to update when rounded value and demand
// value differ
// Check of hkey_demand is missing
if (fabsf(info.last_demand[0] - presst) >= 0.05f) {
midas::odb o(this->equipmentPath);
printf("%f is a new value\n", presst);
o[Configuration::DEMANDE_PATH.c_str()][0] = presst;
}
info.last_demand[0] = presst;
}
}
/* NOT able to calculate pressure and calculated pressure expected
* as input */
} else if (info.last_demand[6] == 1.0f) {
info.pending = TRUE;
info.last_measured[5] = -1.0f;
/* pressure calculation is not necessary as manual mode is set */
} else if (info.last_demand[6] != 1.0f) {
info.pending = FALSE;
info.last_measured[5] = -1.0f;
}
/* all conditions met to update setpoint of Mercury ITC in ODB? */
if (!info.pending) {
info.last_recalculated = ss_time();
if (info.last_demand[7] == 1.0f) {
info.last_demand[0] =
std::roundf(info.last_demand[0] * 10.0f + 0.5f) / 10.0f;
info.last_measured[0] = info.last_demand[0];
/* is Mercury ITC already set? -> check readout*/
if (info.press_mitc_output != info.last_demand[0]) {
// update Mercury ITC Demanded value
// NIY maybe check if info.hkey_mitc_output != 0
midas::odb o(info.mitcPath);
o[Configuration::OUTPUT_PATH.c_str()]
[info.mitcpressc_settings.setpressindex] =
info.last_demand[0];
info.last_mitc_input = ss_time();
}
}
ss_sleep(100);
}
if (!info.recalculate)
ss_sleep(100);
info.recalculate = FALSE;
}
return FE_SUCCESS;
}
/*
_Pressure 0 D
_MercuryITCSetpoint 1
_MercuryITCTemperat(ure) 2
_MercuryITCPower 3
_AveragedPower 4
_CalcPressure 5
_PressureControlMode 6 D
_SetRCPressure 7 D
_PressureCalc_minPressure 8 D
_PressureCalc_maxPressure 9 D
_PressureCalc_const1 10 D
_PressureCalc_const2 11 D
*/
INT itcPressureOptimizer::mitc_pressc_set(INT channel, float value) {
switch (channel) {
case 0: // PRESSURE - only set pressure if not in calculated mode
if (info.last_demand[6] != 1.0f) {
if (info.last_demand[channel] != value) {
info.last_demand[channel] = value;
info.last_demand_set[channel] = ss_time();
info.recalculate = TRUE;
}
} else {
if (std::abs(info.last_demand[channel] - value) > 0.2f) {
cm_msg(MLOG, "",
"PressureControlMode is set to calculated! "
"- Not setting pressure!");
}
}
break;
case 6: // Pressure Control Mode : 1 = Calc(automatic), 0 = Man
if ((info.last_demand[channel] != 1.0f) && (value == 1.0f)) {
info.last_demand[channel] = 1.0f;
info.last_demand_set[channel] = ss_time();
cm_msg(MLOG, "", "Pressure Control Mode is set to Calculated");
info.recalculate = TRUE;
} else if ((info.last_demand[channel] != 0.0f) && (value != 1.0f)) {
info.last_demand[channel] = 0.0f;
info.last_demand_set[channel] = ss_time();
cm_msg(MLOG, "", "Pressure Control Mode is set to Manual");
info.recalculate = TRUE;
}
break;
case 7: // set Pressure for Mercury ITC: 1 = Update Mercury ITC Pressure
// Setpoint
// 0 = do not
if ((info.last_demand[channel] != 1.0f) && (value == 1.0f)) {
info.last_demand[channel] = 1.0f;
info.last_demand_set[channel] = ss_time();
cm_msg(MLOG, "",
"MPC_SetPressure is enabled - Setting Mercury ITC "
"Pressure Setpoint in ODB");
} else if ((info.last_demand[channel] != 0.0f) && (value != 1.0f)) {
info.last_demand[channel] = 0.0f;
info.last_demand_set[channel] = ss_time();
cm_msg(MLOG, "",
"MPC_SetPressure is disabled - Not Setting Mercury ITC "
"Pressure Setpoint in ODB");
info.recalculate = TRUE;
}
break;
case 8: // min pressure
case 9: // max pressure
case 10: // pressure c1
case 11: // pressure c2
if (info.last_demand[channel] != value) {
std::string name, name1; // TODO : change to std::string
name = this->getDefaultName(channel);
if (info.initialised)
cm_msg(MLOG, "", "Channel %d (%s): output was %f now set to %f",
channel, name.c_str(), info.last_demand[channel], value);
else
cm_msg(MLOG, "", "%s (Channel %d): Output is set to %f",
name.c_str(), channel, value);
if (channel == 9) {
if (info.last_demand[channel - 1] >= value) {
name1 = this->getDefaultName(channel);
cm_msg(MERROR, "mitc_pressc_set",
"%s should be larger than %s (%f)", name.c_str(),
name1.c_str(), info.last_demand.at(channel - 1));
}
}
info.last_demand[channel] = value;
info.last_demand_set[channel] = ss_time();
info.recalculate = TRUE;
}
break;
}
// all demand values set from ODB during init?
if ((channel == info.num_channels - 1) && !info.initialised) {
info.initialised = TRUE; // flag init done
info.recalculate = TRUE; // flag to recalculate
}
mitc_pressc_recalculate();
return FE_SUCCESS;
}
float itcPressureOptimizer::getDemand(int channel) {
float pvalue;
if ((channel >= 0) && (channel < info.num_channels)) {
pvalue = info.last_demand.at(channel);
} else {
pvalue = -1.0f;
}
return pvalue;
}
float itcPressureOptimizer::get(int channel) {
// update variox readout
mitc_pressc_update_mitc_input();
// update pressure demand readout
mitc_pressc_update_pressure_demanded();
// recalculate pressure and nv%
mitc_pressc_recalculate();
if ((channel == info.num_channels - 1) && !info.initialised)
ss_sleep(1000);
float pvalue;
if ((channel >= 0) && (channel < info.num_channels)) {
pvalue = info.last_measured[channel];
} else {
pvalue = -1.0f;
}
return pvalue;
}
INT itcPressureOptimizer::mitc_pressc_get(INT channel, float *pvalue) {
// update variox readout
mitc_pressc_update_mitc_input();
// update pressure demand readout
mitc_pressc_update_pressure_demanded();
// recalculate pressure and nv%
mitc_pressc_recalculate();
if ((channel == info.num_channels - 1) && !info.initialised)
ss_sleep(1000);
if (pvalue) {
if ((channel >= 0) && (channel < info.num_channels)) {
if (*pvalue != info.last_measured[channel]) {
*pvalue = info.last_measured[channel];
}
} else {
*pvalue = -1.0f;
}
}
return FE_SUCCESS;
}
/*
Default threshold for measured values index threshold
----------------------------------------------------------------------
_Pressure 0 0.01
_MercuryITCSetpoint 1 0.001
_MercuryITCTemperat(ure) 2 0.0001
_MercuryITCPower 3 0.1
_AveragedPower 4 0.01
_CalcPressure 5 0.01
_PressureControlMode 6 -
_SetRCPressure 7 -
_PressureCalc_minPressure 8 0.01
_PressureCalc_maxPressure 9 0.01
_PressureCalc_const1 10 0.01
_PressureCalc_const2 11 0.01
*/
float itcPressureOptimizer::getDefaultThreshold(int channel) {
switch (channel) {
case 0:
case 4:
case 5:
case 8:
case 9:
case 10:
case 11:
return 0.01f;
break;
case 3:
return 0.1f;
break;
case 1:
return 0.001f;
break;
case 2:
return 0.0001f;
break;
default:
return 1.0f;
}
return FE_SUCCESS;
}
std::string itcPressureOptimizer::getDefaultName(int channel) {
int maxSize = Configuration::defaultLabels.size() - 1;
if (channel > Configuration::defaultLabels.size() - 1 || channel < 0) {
channel = Configuration::defaultLabels.size() - 1;
}
std::string res = Configuration::defaultLabels.at(channel);
printf("%s\n", res.c_str());
return res;
}
+33 -50
View File
@@ -1,65 +1,48 @@
#ifndef ITC_PRESSURE_OPTIMIZER_H
#define ITC_PRESSURE_OPTIMIZER_H
#include "itc_pressure_optimizer_info.h"
#include "./handlers/DemandHandler.h"
#include "./handlers/FeedbackHandler.h"
#include "./handlers/InputHandler.h"
#include "./handlers/OutputHandler.h"
#include "./handlers/PressureCalculator.h"
#include "./handlers/SettingsHandler.h"
#include "EventBus.h"
#include "tmfe.h"
#include <string>
class itcPressureOptimizer : public TMFeEquipment {
public:
struct EquipmentInitEvent {};
struct EquipmentPollEvent {};
/**
* @brief Constructor
* @param equipmentName for debug purpose, cpp file of the equipment
*/
itcPressureOptimizer(std::string equipmentName,
const char *equipmentFilename, int channel);
TMFeResult HandleInit(const std::vector<std::string> &args);
const char *equipmentFilename);
/**
* @brief Method overridden called periodically by Midas
*/
void HandlePeriodic();
private:
std::string equipmentPath;
itcPressureOptimizerInfo info;
/*
Initialize ODB record for settings and initialized its variables and
bus driver
*/
INT mitc_pressc_init();
/*
Exit routine
*/
INT mitc_pressc_exit();
/*
read temperature setpoint, measured temperature and measured power
from Mercury ITC device in ODB and calculate average power of the last
30 sec
*/
INT mitc_pressc_update_mitc_input();
/*
read demanded pressure (setpoint) from Mercury ITC (ODB Demand)
*/
INT mitc_pressc_update_pressure_demanded();
/*
Calculate pressure
*/
INT mitc_pressc_recalculate();
/*
Set pressure to Mercury ITC
*/
INT mitc_pressc_set(INT channel, float value);
/*
????
*/
float getDemand(int channel);
/*
/**
* @brief Method overridden called by Midas at initialization
*/
INT mitc_pressc_get(INT channel, float *pvalue);
TMFeResult HandleInit(const std::vector<std::string> &args);
std::string getDefaultName(int channel);
float getDefaultThreshold(int channel);
float get(int channel);
private:
EventBus eventBus;
DemandHandler demandHandler;
SettingsHandler settingsHandler;
InputHandler inputHandler;
PressureCalculator pressureCalculator;
FeedbackHandler feedbackHandler;
OutputHandler outputHandler;
};
#endif
@@ -1,85 +0,0 @@
#ifndef ITC_PRESSURE_OPTIMIZER_CONFIG_H
#define ITC_PRESSURE_OPTIMIZER_CONFIG_H
#include <string>
namespace Configuration {
const std::string SLASH = "/";
const std::string EQUIPMENT_DIR = "Equipment";
const std::string OUTPUT_VARNAME = "Output";
const std::string INPUT_VARNAME = "Input";
const std::string SETTINGS_DIR = "Settings";
const std::string VARIABLE_DIR = "Variables";
const std::string DEVICES_DIR = "Devices";
const std::string MIT_DIR = "MITCPRESSC";
const std::string DD_DIR = "DD";
const std::string MITC_EQUIPMENT_VARNAME = "MITC_Equipment";
const std::string MITC_OUTPUT_PRESS_SP_INDEX_VARNAME =
"MITC_OutputPressSPIndex";
const std::string MITC_INPUT_VARIOX_SP_INDEX_VARNAME =
"MITC_InputVarioxSPIndex";
const std::string MITC_INPUT_VARIOX_TEMP_INDEX_VARNAME =
"MITC_InputVarioxTempIndex";
const std::string MITC_INPUT_VARIOX_POW_INDEX_VARNAME =
"MITC_InputVarioxPowIndex";
const std::string RECALC_INTERVAL_VARNAME = "RecalcInterval";
const std::string MITC_READOUT_INTERVAL_VARNAME = "MitcReadoutInterval";
const std::string PRESS_READOUT_INTERVAL_VARNAME = "PressReadoutInterval";
const std::string ENABLE_VARNAME = "Enable";
const std::string NAMES_VARNAME = "Names";
const std::string UPDATE_THRESHOLD_MEASURED_VARNAME =
"Update Threshold Measured";
const std::string DEMAND_VARNAME = "Demand";
const std::string MEASURED_VARNAME = "Measured";
const std::string MITC_EQUIPMENT_PATH = SETTINGS_DIR + SLASH + DEVICES_DIR +
SLASH + MIT_DIR + SLASH + DD_DIR +
SLASH + MITC_EQUIPMENT_VARNAME;
const std::string MITC_OUTPUT_PRESS_SP_INDEX_PATH =
SETTINGS_DIR + SLASH + DEVICES_DIR + SLASH + MIT_DIR + SLASH + DD_DIR +
SLASH + MITC_OUTPUT_PRESS_SP_INDEX_VARNAME;
const std::string MITC_INPUT_VARIOX_SP_INDEX_PATH =
SETTINGS_DIR + SLASH + DEVICES_DIR + SLASH + MIT_DIR + SLASH + DD_DIR +
SLASH + MITC_INPUT_VARIOX_SP_INDEX_VARNAME;
const std::string MITC_INPUT_VARIOX_TEMP_INDEX_PATH =
SETTINGS_DIR + SLASH + DEVICES_DIR + SLASH + MIT_DIR + SLASH + DD_DIR +
SLASH + MITC_INPUT_VARIOX_TEMP_INDEX_VARNAME;
const std::string MITC_INPUT_VARIOX_POW_INDEX_PATH =
SETTINGS_DIR + SLASH + DEVICES_DIR + SLASH + MIT_DIR + SLASH + DD_DIR +
SLASH + MITC_INPUT_VARIOX_POW_INDEX_VARNAME;
const std::string RECALC_INTERVAL_PATH = SETTINGS_DIR + SLASH + DEVICES_DIR +
SLASH + MIT_DIR + SLASH + DD_DIR +
SLASH + RECALC_INTERVAL_VARNAME;
const std::string MITC_READOUT_INTERVAL_PATH =
SETTINGS_DIR + SLASH + DEVICES_DIR + SLASH + MIT_DIR + SLASH + DD_DIR +
SLASH + MITC_READOUT_INTERVAL_VARNAME;
const std::string PRESS_READOUT_INTERVAL_PATH =
SETTINGS_DIR + SLASH + DEVICES_DIR + SLASH + MIT_DIR + SLASH + DD_DIR +
SLASH + PRESS_READOUT_INTERVAL_VARNAME;
const std::string OUTPUT_PATH = VARIABLE_DIR + SLASH + OUTPUT_VARNAME;
const std::string INPUT_PATH = VARIABLE_DIR + SLASH + INPUT_VARNAME;
const std::string DEMANDE_PATH = VARIABLE_DIR + SLASH + DEMAND_VARNAME;
const std::string MEASURED_PATH = VARIABLE_DIR + SLASH + MEASURED_VARNAME;
const std::vector<std::string> defaultLabels = {"Pressure Setpoint",
"MPC_ITC_VarioxSP",
"MPC_ITC_VarioxTemp",
"MPC_ITC_VarioxPower",
"MPC_AveragedPower",
"MPC_CalcPressure",
"Pressure Control Mode",
"MPC_SetPressure",
"MPC_PressCalcminPress",
"MPC_PressCalcmaxPress",
"MPC_PressCalc_const1",
"MPC_PressCalc_const2",
"NONE"};
} // namespace Configuration
#endif
-56
View File
@@ -1,56 +0,0 @@
#ifndef ITC_PRESSURE_OPTIMIZER_INFO_H
#define ITC_PRESSURE_OPTIMIZER_INFO_H
#include "midas.h"
#include "tmfe.h"
#include <string>
#include <vector>
#define MAX_HIS 100
class itcPressureOptimizerSettings {
public:
std::string mitcequipment; // equipment name of Mercury ITC
INT setpressindex; // index of demanded/output pressure
INT getvarioxspindex; // index of input setpoint (readout)
INT getvarioxtempindex; // index of input temperature (readout)
INT getvarioxpowindex; // index of input power (readout)
INT recalcinterval; // interval [sec] to recalculate
INT mitcinterval; // interval [sec] to update Mercury ITC readout
INT pressinterval; // interval [sec] to update demanded pressure
};
class itcPressureOptimizerInfo {
public:
itcPressureOptimizerSettings mitcpressc_settings;
INT num_channels; // number of channels of this fe
HNDLE hkey_demand; // ODB key to demand values of this equipment
std::vector<float>
last_demand; // last demand values (used to calculate pressure)
std::vector<DWORD> last_demand_set; // last time a demand value was set
std::vector<float> last_measured; // last "measured" values ***
INT nvals_mitc_input; // number of variables in Mercury ITC input
HNDLE hkey_mitc_input; // ODB key to Mercury ITC input
std::vector<float> mitc_input; // readout of Mercury ITC equipment input
DWORD last_mitc_input; // last time Mercury ITC input readout
INT nvals_mitc_output; // number of variables in Mercury ITC output
HNDLE hkey_mitc_output; // ODB key to Mercury ITC output
float press_mitc_output; // readout of pressure setpoint from Mercury ITC
DWORD last_press_mitc_output; // last time readout of pressure setpoint
DWORD lastlog;
std::string name; // equipment name to access this odb variables
// directly
BOOL initialised; // to flag everything is initialised
BOOL recalculate; // to flag recalculate necessary after param change
BOOL pending; // recalculation is pending due to insufficient info
DWORD last_recalculated; // ss_time() when last recalculated
INT ihis; // position of next value in history array
float history[MAX_HIS]; // history[i] of Mercury ITC power output
DWORD history_time[MAX_HIS]; // time when Mercury ITC power was written to
// history[i]
std::string mitcPath;
};
#endif
+59 -3
View File
@@ -8,11 +8,67 @@ itcPressureOptimizerScfe::itcPressureOptimizerScfe(std::string frontendName,
std::string equipmentName)
: TMFrontend() {
FeSetName(frontendName.c_str());
FeAddEquipment(new itcPressureOptimizer(equipmentName, __FILE__, 12));
FeAddEquipment(new itcPressureOptimizer(equipmentName, __FILE__));
}
/**
* Print help when argument are wrong or are
*/
void help(std::string binaryName) {
TMFE::Instance()->Msg(
MINFO, __FUNCTION__,
"|HELP| itc pressure optimizer usage : %s <frontendName> "
"<equipmentName>",
binaryName.c_str());
}
void wrong_argument(std::string binaryName) {
TMFE::Instance()->Msg(MERROR, __FUNCTION__, "Wrong usage of %s",
binaryName.c_str());
help(binaryName);
}
int main(int argc, char *argv[]) {
auto front = itcPressureOptimizerScfe("itcPressureOptimizerScfe",
"itcPressureOptimizer");
if (argv[0][0] == '.') {
TMFE::Instance()->Msg(MERROR, __FUNCTION__,
"Relative paths are strongly discouraged; "
"please use an absolute path.");
}
if (argc == 1) {
wrong_argument(std::string(argv[0]));
exit(EXIT_FAILURE);
}
if (argc == 2) {
std::string argument = std::string(argv[1]);
if (argument == "--help" || argument == "-h") {
help(std::string(argv[0]));
exit(EXIT_SUCCESS);
} else {
wrong_argument(std::string(argv[0]));
exit(EXIT_FAILURE);
}
}
std::string frontendName;
std::string equipmentName;
if (argc > 2) {
frontendName = std::string(argv[1]);
equipmentName = std::string(argv[2]);
argc -= 2;
argv += 2;
}
if (argc > 1) {
TMFE::Instance()->Msg(
MINFO, __FUNCTION__,
"Unnecessary argument detected; this will be discarded");
argv += (argc - 1);
argc -= (argc - 1);
}
auto front = itcPressureOptimizerScfe(frontendName, equipmentName);
front.FeMain(argc, argv);
}