[修改]修改了错误的exporter名称

This commit is contained in:
makotocc0107
2024-09-04 10:25:47 +08:00
committed by Coding
parent 211a89778c
commit d93daaab6b
37 changed files with 0 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
### C++ template
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
#Clion
.idea
cmake-build-*
+57
View File
@@ -0,0 +1,57 @@
cmake_minimum_required(VERSION 3.18)
project(opcua_exporter)
cmake_policy(SET CMP0148 OLD)
set(CMAKE_CXX_STANDARD 20)
option(ENABLE_METRICS_SIMULATE "开启测点故障仿真功能" ON)
find_package(civetweb CONFIG REQUIRED)
find_package(spdlog CONFIG REQUIRED)
find_package(prometheus-cpp CONFIG REQUIRED)
find_package(open62541 CONFIG REQUIRED)
find_package(yaml-cpp CONFIG REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)
add_subdirectory(fault-simulation-algorithm)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
set(
OPC_UA_EXPORTER_SRCS
main.cpp
client/client.cpp
client/client.h
collector/collector.cpp
collector/collector.h
)
add_executable(
opcua_exporter
${OPC_UA_EXPORTER_SRCS}
)
target_compile_definitions(
opcua_exporter
PRIVATE
${CMAKE_BUILD_TYPE}
)
if (${ENABLE_METRICS_SIMULATE})
target_compile_definitions(
opcua_exporter
PRIVATE
METRICS_SIMULATE
)
endif ()
target_link_libraries(
opcua_exporter
simulation-manager
spdlog::spdlog
yaml-cpp::yaml-cpp
nlohmann_json::nlohmann_json
open62541::open62541
civetweb::civetweb
prometheus-cpp::core prometheus-cpp::pull
)
+47
View File
@@ -0,0 +1,47 @@
FROM openeuler/openeuler:24.03 AS dev
RUN dnf update -y && \
dnf install -y git gcc g++ gdb make autoconf automake curl zip unzip tar &&\
dnf clean all && \
rm -rf /var/cache/dnf/*
ENV VCPKG_ROOT="/vcpkg"
RUN curl -fsSL https://github.com/Kitware/CMake/releases/download/v3.27.2/cmake-3.27.2-linux-x86_64.sh -o cmake_install.sh && \
chmod +x cmake_install.sh && \
./cmake_install.sh --prefix=/usr/local --skip-license && \
rm -rf cmake_install.sh
RUN curl -fsSL https://github.com/ninja-build/ninja/releases/download/v1.11.1/ninja-linux.zip -o ninja-linux.zip && \
unzip ninja-linux.zip && \
rm -rf ninja-linux.zip && \
mv ninja /usr/local/bin/ && \
chmod +x /usr/local/bin/ninja
RUN curl -fsSL https://github.com/microsoft/vcpkg-tool/releases/download/2024-07-10/vcpkg-glibc -o /usr/local/bin/vcpkg && \
chmod +x /usr/local/bin/vcpkg && \
git clone -b 2024.07.12 --depth 1 https://github.com/microsoft/vcpkg /vcpkg
RUN vcpkg install civetweb prometheus-cpp spdlog nlohmann-json yaml-cpp open62541 --clean-after-build
FROM dev AS build
RUN mkdir /app
WORKDIR /app
COPY . /app
RUN mkdir build && \
cd build && \
cmake .. -DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Release -G Ninja && \
cmake --build . --target opcua_exporter
FROM openeuler/openeuler:24.03
RUN mkdir /data
COPY --from=build /app/build/opcua_exporter /usr/local/bin
CMD [ "opcua_exporter", "/data/opcua/opcua.yaml" ]
+399
View File
@@ -0,0 +1,399 @@
//
// Created by 闫鹏宇 on 2022/7/28.
//
#include "client.h"
#include <spdlog/spdlog.h>
#include <open62541/client_config_default.h>
#include <cmath>
#include <utility>
#include <open62541/client_highlevel_async.h>
namespace std {
template<>
struct hash <std::vector<std::string>>{
size_t operator()(const std::vector<std::string> &rhs) const noexcept {
size_t res = 0;
for (auto const &str: rhs) {
res ^= std::hash<std::string>()(str);
}
return res;
}
};
}
inline static spdlog::level::level_enum get_spd_level(UA_LogLevel level) {
switch (level) {
case UA_LogLevel::UA_LOGLEVEL_TRACE:
return spdlog::level::trace;
case UA_LogLevel::UA_LOGLEVEL_DEBUG:
return spdlog::level::debug;
case UA_LogLevel::UA_LOGLEVEL_INFO:
return spdlog::level::info;
case UA_LogLevel::UA_LOGLEVEL_WARNING:
return spdlog::level::warn;
case UA_LogLevel::UA_LOGLEVEL_ERROR:
return spdlog::level::err;
case UA_LogLevel::UA_LOGLEVEL_FATAL:
return spdlog::level::critical;
default:
return spdlog::level::trace;
}
}
static void opc_ua_log(void *logContext, UA_LogLevel level, UA_LogCategory category,
const char *msg, va_list args) {
(void) args;
thread_local static char const *last_log_msg = nullptr;
auto c = static_cast<client *>(logContext);
if (!c) {
return;
}
if (msg != last_log_msg) {
char buff[256] = {0};
vsprintf(buff, msg, args);
spdlog::log(get_spd_level(level), buff);
}
last_log_msg = msg;
}
static void OPC_UA_Client_DeleteSubscriptionCallback(
UA_Client *client,
UA_UInt32 subId,
void *subContext
) {
spdlog::trace(
"OPC_UA_Client_DeleteSubscriptionCallback subId{}",
subId
);
}
static void OPC_UA_Client_StatusChangeNotificationCallback(
UA_Client *client,
UA_UInt32 subId,
void *subContext,
UA_StatusChangeNotification *notification
) {
spdlog::trace(
"OPC_UA_Client_StatusChangeNotificationCallback subId{}",
subId
);
}
static void OPC_UA_Client_DataChangeNotificationCallback(
UA_Client *client,
UA_UInt32 subId,
void *subContext,
UA_UInt32 monId,
void *monContext,
UA_DataValue *value
) {
spdlog::trace(
"OPC_UA_Client_DataChangeNotificationCallback subId{} monId",
subId,
monId
);
}
static void OPC_UA_Client_DeleteMonitoredItemCallback(
UA_Client *client,
UA_UInt32 subId,
void *subContext,
UA_UInt32 monId,
void *monContext) {
spdlog::trace(
"OPC_UA_Client_DeleteMonitoredItemCallback subId{} monId",
subId,
monId
);
}
static void OPC_UA_ClientAsyncReadCallback(
UA_Client *c, void *userdata,
UA_UInt32 requestId,
UA_ReadResponse *rr
) {
auto cc = static_cast<client *>(userdata);
auto hash_key = cc->getAsyncReadRequestHashKey(requestId);
cc->updateReadValueCache(hash_key, *rr);
}
client::client(std::string endpoint) :
ua_endpoint(std::move(endpoint)) {
ua_config.logger.context = this;
ua_config.logger.log = &opc_ua_log;
UA_ClientConfig_setDefault(&ua_config);
ua_client = UA_Client_newWithConfig(&ua_config);
for (int i = 0; i < async_read_request_ids.size(); ++i) {
async_read_request_ids[i].first = i;
async_read_request_ids[i].second = 0;
}
}
client::~client() {
stop();
UA_Client_delete(ua_client);
}
void client::createSubscription() {
if (create_subscription_response) {
return;
}
auto request = UA_CreateSubscriptionRequest_default();
create_subscription_response = UA_Client_Subscriptions_create(
ua_client,
request,
this,
&OPC_UA_Client_StatusChangeNotificationCallback,
&OPC_UA_Client_DeleteSubscriptionCallback
);
}
void client::stop() {
running = false;
disconnect();
}
void client::run() {
UA_StatusCode retval = UA_STATUSCODE_GOOD;
int error_time = 0;
while (running) {
if (!connect()) {
//disconnect();
spdlog::error("OPC-UA Server({}) not connected({}). Retrying to connect in 1 second.", ua_endpoint,
retval);
UA_sleep_ms(1000);
continue;
} else if (MONITOR == read_mode) {
createSubscription();
}
while (running && UA_STATUSCODE_GOOD == retval) {
std::unique_lock lock(ua_mutex);
retval = UA_Client_run_iterate(ua_client, 100);
}
}
}
bool client::connect() {
std::unique_lock lock(ua_mutex);
return UA_STATUSCODE_GOOD == UA_Client_connect(ua_client, ua_endpoint.c_str());
}
void client::disconnect() {
std::unique_lock lock(ua_mutex);
UA_Client_disconnect(ua_client);
}
std::vector<UA_ReadValueId> client::makeReadValueIds(std::vector<std::string> const &node_ids) {
std::vector<UA_ReadValueId> ids;
if (!node_ids.empty()) {
for (auto const &node_id: node_ids) {
ids.emplace_back(
UA_ReadValueId{
UA_NODEID(node_id.c_str()),
UA_ATTRIBUTEID_VALUE,
{},
{}
}
);
}
}
return ids;
}
UA_ReadRequest &client::makeReadRequest(size_t hash_key) {
std::vector<UA_ReadValueId> &read_value_ids = ua_read_value_request_cache_[hash_key].read_value_ids;
auto &request = ua_read_value_request_cache_[hash_key].read_request;
request.nodesToRead = const_cast<UA_ReadValueId *>(read_value_ids.data());
request.nodesToReadSize = read_value_ids.size();
return request;
}
void client::cachePreCheckAndInit(size_t hash_key, std::vector<std::string> const &node_ids) {
auto ua_read_value_ids_iter = ua_read_value_request_cache_.find(hash_key);
if (ua_read_value_ids_iter == ua_read_value_request_cache_.end()) {
auto ids = makeReadValueIds(node_ids);
if (ids.empty()) {
return;
}
ua_read_value_request_cache_[hash_key].read_value_ids = std::move(ids);
ua_read_value_request_cache_[hash_key].node_ids = node_ids;
UA_ReadRequest_init(&ua_read_value_request_cache_[hash_key].read_request);
}
}
uint32_t *client::getAsyncReadRequestId(size_t hash_key) {
auto &ids = async_read_request_ids[(current_async_read_request_pos++) % async_read_request_ids.size()];
ids.second = hash_key;
return &ids.first;
}
size_t client::getAsyncReadRequestHashKey(uint32_t req_id) {
for (auto const &iter: async_read_request_ids) {
if (iter.first == req_id) {
return iter.second;
}
}
return 0;
}
void client::sendReadRequest(size_t hash_key, bool force_sync) {
ua_read_value_request_cache_[hash_key].last_read_request_time = std::chrono::system_clock::now();
if (SYNC == read_mode || force_sync) {
UA_ReadResponse response;
{
std::shared_lock lock(ua_mutex);
response = UA_Client_Service_read(
ua_client,
makeReadRequest(
hash_key
)
);
}
if (UA_STATUSCODE_GOOD == response.responseHeader.serviceResult) {
updateReadValueCache(hash_key, response);
}
UA_ReadResponse_clear(&response);
}
switch (read_mode) {
case ASYNC: {
UA_Client_sendAsyncReadRequest(
ua_client,
&makeReadRequest(hash_key),
&OPC_UA_ClientAsyncReadCallback,
this,
getAsyncReadRequestId(hash_key)
);
break;
}
case MONITOR: {
break;
}
default: {
break;
}
}
}
std::chrono::system_clock::duration client::getReadRequestDuration(size_t hash_key) {
return std::chrono::system_clock::now() -
ua_read_value_request_cache_[hash_key].last_read_request_time;
}
std::chrono::system_clock::duration client::getCacheUpdateDuration(size_t hash_key) {
return std::chrono::system_clock::now() -
ua_read_value_request_cache_[hash_key].last_read_response_time;
}
std::map<std::string, double> client::readValue(std::vector<std::string> const &node_ids) {
if (!node_ids.empty()) {
auto node_ids_hash_value = std::hash<std::vector<std::string>>()(node_ids);
cachePreCheckAndInit(node_ids_hash_value, node_ids);
std::lock_guard<std::mutex> lock(ua_read_mutex);
auto duration = getCacheUpdateDuration(node_ids_hash_value);
if (duration > cache_timeout) {
sendReadRequest(node_ids_hash_value, true);
} else if (duration > cache_update_time) {
sendReadRequest(node_ids_hash_value);
}
if (getCacheUpdateDuration(node_ids_hash_value) < cache_timeout) {
return getCacheValue(node_ids_hash_value);
}
}
return {};
}
std::map<std::string, double> client::getCacheValue(size_t hash_key) {
std::map<std::string, double> values;
auto iter = ua_read_value_request_cache_.find(hash_key);
if (iter != ua_read_value_request_cache_.end()) {
for (auto const &node_id: iter->second.node_ids) {
values[node_id] = ua_read_value_cache[node_id];
}
}
return values;
}
void client::updateReadValueCache(size_t hash_key, UA_ReadResponse const &response) {
auto iter = ua_read_value_request_cache_.find(hash_key);
if (iter == ua_read_value_request_cache_.end()) {
return;
}
if (response.resultsSize != iter->second.read_value_ids.size()) {
return;
}
iter->second.last_read_response_time = std::chrono::system_clock::now();
for (int i = 0; i < response.resultsSize; ++i) {
auto &data_value = response.results[i];
setValueCache(iter->second.node_ids[i], getValueAsDouble(data_value));
}
}
void client::setValueCache(std::string const &node_id, double value) {
ua_read_value_cache[node_id] = value;
}
double client::getValueAsDouble(UA_DataValue const &ua_value) {
if (ua_value.hasValue) {
switch (ua_value.value.type->typeKind) {
case UA_DATATYPEKIND_BOOLEAN: {
return static_cast<double>(*static_cast<UA_Boolean *>(ua_value.value.data));
}
case UA_DATATYPEKIND_SBYTE: {
return static_cast<double>(*static_cast<UA_SByte *>(ua_value.value.data));
}
case UA_DATATYPEKIND_BYTE: {
return static_cast<double>(*static_cast<UA_Byte *>(ua_value.value.data));
}
case UA_DATATYPEKIND_INT16: {
return static_cast<double>(*static_cast<UA_Int16 *>(ua_value.value.data));
}
case UA_DATATYPEKIND_UINT16: {
return static_cast<double>(*static_cast<UA_UInt16 *>(ua_value.value.data));
}
case UA_DATATYPEKIND_INT32: {
return static_cast<double>(*static_cast<UA_Int32 *>(ua_value.value.data));
}
case UA_DATATYPEKIND_UINT32: {
return static_cast<double>(*static_cast<UA_UInt32 *>(ua_value.value.data));
}
case UA_DATATYPEKIND_INT64: {
return static_cast<double>(*static_cast<UA_Int64 *>(ua_value.value.data));
}
case UA_DATATYPEKIND_UINT64: {
return static_cast<double>(*static_cast<UA_UInt64 *>(ua_value.value.data));
}
case UA_DATATYPEKIND_FLOAT: {
return static_cast<double>(*static_cast<UA_Float *>(ua_value.value.data));
}
case UA_DATATYPEKIND_DOUBLE: {
return static_cast<double>(*static_cast<UA_Double *>(ua_value.value.data));
}
default:
break;
}
}
return std::nan(nullptr);
}
void client::readTest() {
auto read_request = UA_ReadRequest_new();
}
UA_Client *client::getClient() {
return ua_client;
}
+83
View File
@@ -0,0 +1,83 @@
//
// Created by 闫鹏宇 on 2022/7/28.
//
#ifndef OPCUA_EXPORTER_CLIENT_H
#define OPCUA_EXPORTER_CLIENT_H
#include <string>
#include <map>
#include <unordered_map>
#include <vector>
#include <open62541/client.h>
#include <shared_mutex>
#include <chrono>
#include <array>
#include <atomic>
#include <open62541/client_subscriptions.h>
#include <optional>
class client final {
public:
explicit client(std::string endpoint);
~client();
void stop();
void run();
bool connect();
void disconnect();
std::map<std::string, double> readValue(std::vector<std::string> const &node_ids);
void readTest();
UA_Client *getClient();
size_t getAsyncReadRequestHashKey(uint32_t req_id);
void updateReadValueCache(size_t hash_key, UA_ReadResponse const &response);
protected:
std::vector<UA_ReadValueId> makeReadValueIds(std::vector<std::string> const &node_ids);
UA_ReadRequest &makeReadRequest(size_t hash_key);
std::map<std::string, double> getCacheValue(size_t hash_key);
double getValueAsDouble(UA_DataValue const &ua_value);
void cachePreCheckAndInit(size_t hash_key, std::vector<std::string> const &node_ids);
void sendReadRequest(size_t hash_key, bool force_sync = false);
std::chrono::system_clock::duration getCacheUpdateDuration(size_t hash_key);
std::chrono::system_clock::duration getReadRequestDuration(size_t hash_key);
void setValueCache(std::string const &node_id, double value);
uint32_t *getAsyncReadRequestId(size_t hash_key);
void createSubscription();
protected:
enum enum_read_mode {
SYNC,
ASYNC,
MONITOR
};
struct st_read_value_request_cache {
std::chrono::system_clock::time_point last_read_response_time;
std::chrono::system_clock::time_point last_read_request_time;
std::vector<UA_ReadValueId> read_value_ids;
UA_ReadRequest read_request;
std::vector<std::string> node_ids;
};
private:
UA_Client *ua_client{nullptr};
UA_ClientConfig ua_config{};
std::string ua_endpoint;
std::shared_mutex ua_mutex;
std::mutex ua_read_mutex;
std::chrono::system_clock::duration cache_timeout{std::chrono::milliseconds(1000)};
std::chrono::system_clock::duration cache_update_time{std::chrono::milliseconds(400)};
enum_read_mode read_mode{ASYNC};
std::unordered_map<std::size_t, st_read_value_request_cache> ua_read_value_request_cache_;
std::map<std::string, double> ua_read_value_cache;
bool running{true};
std::array<std::pair<uint32_t, size_t>, 1024> async_read_request_ids;
std::atomic_int64_t current_async_read_request_pos{0};
std::optional<UA_CreateSubscriptionResponse> create_subscription_response;
};
#endif //OPCUA_EXPORTER_CLIENT_H
@@ -0,0 +1 @@
add_library(civetweb::civetweb-cpp ALIAS civetweb-cpp)
+127
View File
@@ -0,0 +1,127 @@
//
// Created by 闫鹏宇 on 2022/7/29.
//
#include "collector.h"
#include <utility>
#include <spdlog/spdlog.h>
#include "../client/client.h"
collector::collector(YAML::Node const &config, std::shared_ptr<client> ua_client) :
registry_(std::make_shared<prometheus::Registry>()),
ua_client_(ua_client) {
buildMetrics(config);
}
void collector::clearMetrics() {
ua_read_node_ids.clear();
for (auto &node: nodes) {
if (nullptr != node.second.family) {
if (nullptr != node.second.real) {
node.second.family->Remove(node.second.real);
}
if (nullptr != node.second.simulation) {
node.second.family->Remove(node.second.simulation);
}
registry_->Remove(*node.second.family);
}
}
nodes.clear();
metrics_json_.clear();
}
prometheus::Labels collector::getNodeLabels(YAML::Node const &labels) {
return prometheus::Labels();
}
nlohmann::json const &collector::getMetricsJson() {
return metrics_json_;
}
void collector::buildMetrics(YAML::Node const &config) {
clearMetrics();
for (auto item: config) {
if (item.IsNull() || item["name"].IsNull() || item["nodeid"].IsNull() || item["type"].IsNull()) {
continue;
}
auto node_id = item["nodeid"].as<std::string>();
auto name = item["name"].as<std::string>();
auto help = item["help"].as<std::string>(std::string());
metrics_json_.push_back({
{"node_id", node_id},
{"name", name},
{"help", help}
});
ua_read_node_ids.emplace_back(node_id);
spdlog::info("load metrics {}({}).", name, node_id);
auto &node_family = prometheus::BuildGauge()
.Name(name)
.Help(help)
.Labels(getNodeLabels(item["labels"]))
.Register(*registry_);
simulation_manager_[name] = new simulation_manager();
nodes[node_id] = {
name,
&node_family,
&(node_family.Add({{"data", "real"}})),
&(node_family.Add({{"data", "simulate"}})),
simulation_manager_[name]
};
}
}
bool collector::updateMetrics() {
do {
if (ua_read_node_ids.empty()) {
break;
}
auto values = ua_client_->readValue(ua_read_node_ids);
if (values.empty()) { // none value return
break;
}
for (auto const &value: values) {
auto node_iter = nodes.find(value.first);
if (node_iter != nodes.end()) {
node_iter->second.setValue(value.second);
}
}
return true;
} while (false);
return false;
}
std::vector<prometheus::MetricFamily> collector::Collect() const {
if (const_cast<collector *>(this)->updateMetrics()) {
return registry_->Collect();
}
return {};
}
void collector::st_node::setValue(double val) {
if (nullptr != real) {
real->Set(val);
}
if (nullptr != simulation && manager) {
simulation->Set(manager->eval(val));
// simulation->Set(val + 100);
}
}
nlohmann::json collector::simulationConfig(std::string const &name) {
if (simulation_manager_.contains(name)) {
return simulation_manager_[name]->config();
}
return {};
}
void collector::setSimulationConfig(std::string const &name, nlohmann::json const &config) {
if (simulation_manager_.contains(name)) {
return simulation_manager_[name]->set_config(config);
}
}
+57
View File
@@ -0,0 +1,57 @@
//
// Created by 闫鹏宇 on 2022/7/29.
//
#ifndef OPCUA_EXPORTER_COLLECTOR_H
#define OPCUA_EXPORTER_COLLECTOR_H
#include <client/client.h>
#include <nlohmann/json.hpp>
#include <open62541/client.h>
#include <prometheus/collectable.h>
#include <prometheus/family.h>
#include <prometheus/gauge.h>
#include <prometheus/labels.h>
#include <prometheus/metric_family.h>
#include <prometheus/registry.h>
#include <simulation_manager.hpp>
#include <yaml-cpp/yaml.h>
class collector : public prometheus::Collectable {
public:
explicit collector(YAML::Node const &config,
std::shared_ptr<client> ua_client);
~collector() override = default;
std::vector<prometheus::MetricFamily> Collect() const override;
nlohmann::json const &getMetricsJson();
nlohmann::json simulationConfig(std::string const &name);
void setSimulationConfig(std::string const &name,
nlohmann::json const &config);
private:
void buildMetrics(YAML::Node const &config);
bool updateMetrics();
void clearMetrics();
prometheus::Labels getNodeLabels(YAML::Node const &labels);
private:
std::shared_ptr<prometheus::Registry> registry_;
std::shared_ptr<client> ua_client_;
std::vector<std::string> ua_read_node_ids;
struct st_node {
std::string name;
prometheus::Family<prometheus::Gauge> *family{nullptr};
prometheus::Gauge *real{nullptr};
prometheus::Gauge *simulation{nullptr};
simulation_manager *manager;
void setValue(double val);
};
std::map<std::string, st_node> nodes;
std::map<std::string, simulation_manager *> simulation_manager_;
nlohmann::json metrics_json_;
};
#endif // OPCUA_EXPORTER_COLLECTOR_H
@@ -0,0 +1,2 @@
.idea/
cmake-build-*/
@@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 3.18)
project(fault_simulation_algorithm)
set(CMAKE_CXX_STANDARD 20)
find_package(nlohmann_json CONFIG REQUIRED)
include_directories(include)
add_subdirectory(periodic_interference)
add_subdirectory(temperature-drift)
add_subdirectory(step)
add_subdirectory(white_noise)
add_subdirectory(simulation-manager)
@@ -0,0 +1,33 @@
//
// Created by fly on 2022/4/6.
//
#ifndef FAULT_SIMULATION_ALGORITHM_ALGORITHM_HPP
#define FAULT_SIMULATION_ALGORITHM_ALGORITHM_HPP
#include <nlohmann/json.hpp>
class algorithm {
public:
virtual void set_config(nlohmann::json const &config) = 0;
virtual nlohmann::json config() = 0;
virtual double eval(double value) = 0;
protected:
double jsonValue(nlohmann::json const &j, std::string const &key) {
if (j.contains(key)) {
if (j[key].is_string()) {
return std::atof(j[key].get<std::string>().c_str());
}
if (j[key].is_number()) {
return j[key].get<double>();
}
}
return std::nan("");
}
};
#endif //FAULT_SIMULATION_ALGORITHM_ALGORITHM_HPP
@@ -0,0 +1,23 @@
add_library(
periodic-interference
INTERFACE
)
target_sources(
periodic-interference
INTERFACE
periodic_interference.cpp
periodic_interference.h
)
target_include_directories(
periodic-interference
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(
periodic-interference
INTERFACE
nlohmann_json::nlohmann_json
)
@@ -0,0 +1,47 @@
//
// Created by baiguwen on 2022/4/23.
//
#include <iostream>
#include "periodic_interference.h"
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
int main() {
periodic_interference algorithm;
algorithm.set_config(
{
{"PERIODIC_INTERFERENCE_AMPLITUDE", 20},
{"PERIODIC_INTERFERENCE_AMPLITUDE_BASE", 1},
{"CYCLE_", 20}
}
);
std::vector <std::vector<double>> user_arr;
std::ifstream fp("C:/data/user_data.csv");
std::string line;
getline(fp,line);
while (getline(fp,line)){
std::vector <double> data_line;
std::string number;
std::istringstream readstr(line);
for(int j = 0;j < 2814;j++){
getline(readstr,number,',');
data_line.push_back(atof(number.c_str()));
}
user_arr.push_back(data_line);
}
std::ofstream outfile;
outfile.open("C:/data/user_data5.csv", std::ios::out);
outfile<<"origin"<<','<<"periodic_interference"<<std::endl;
for (int i = 0; i <2814; ++i) {
user_arr[i][1]=algorithm.eval(user_arr[i][0]);
std::cout << i << "\t" << user_arr[i][1] << std::endl;
outfile<<user_arr[i][0]<<','<<user_arr[i][1]<<std::endl;
}
outfile.close();
return 0;
}
@@ -0,0 +1,28 @@
//
// Created by baiguwen on 2022/4/23.
//
#include "periodic_interference.h"
#include <math.h>
#include <numbers>
#include <random>
void periodic_interference::set_config(const nlohmann::json &config) {
try {
eval_time_ = 0;
amplitude_ = jsonValue(config, "PERIODIC_INTERFERENCE_AMPLITUDE");
amplitude_base_ = jsonValue(config, "PERIODIC_INTERFERENCE_AMPLITUDE_BASE");
cycle_ = jsonValue(config, "CYCLE_");
config_ = config;
} catch (...) {
}
}
nlohmann::json periodic_interference::config() {
return config_;
}
double periodic_interference::eval(double value) {
return value + amplitude_base_ * sin((2 * std::numbers::pi / cycle_) * eval_time_++) + amplitude_;
}
@@ -0,0 +1,28 @@
//
// Created by baiguwen on 2022/4/23.
//
#ifndef FAULT_SIMULATION_ALGORITHM_PERIODIC_INTERFERENCE_H
#define FAULT_SIMULATION_ALGORITHM_PERIODIC_INTERFERENCE_H
#include <algorithm.hpp>
class periodic_interference : public algorithm {
public:
void set_config(const nlohmann::json &config) override;
nlohmann::json config() override;
double eval(double value) override;
protected:
nlohmann::json config_{};
double amplitude_{0};
double amplitude_base_{0};
double cycle_{0};
uint64_t eval_time_{0};
};
#endif //FAULT_SIMULATION_ALGORITHM_PERIODIC_INTERFERENCE_H
@@ -0,0 +1,27 @@
add_library(simulation-manager INTERFACE)
target_sources(
simulation-manager
INTERFACE
simulation_manager.hpp
)
target_include_directories(
simulation-manager
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/../include
)
target_link_libraries(
simulation-manager
INTERFACE
periodic-interference
step
temperature-drift
white-noise
)
#add_executable(simulation-manager main.cpp simulation_manager.hpp algorithm_wrap.hpp)
@@ -0,0 +1,68 @@
//
// Created by fly on 2022/4/21.
//
#ifndef FAULT_SIMULATION_ALGORITHM_ALGORITHM_WRAP_HPP
#define FAULT_SIMULATION_ALGORITHM_ALGORITHM_WRAP_HPP
#include <algorithm.hpp>
#include <utility>
class algorithm_wrap : public algorithm {
public:
explicit algorithm_wrap(std::shared_ptr<algorithm> algo) {
algorithm_ = std::move(algo);
}
bool enable() const {
return enable_;
}
void set_enable(bool flag) {
enable_ = flag;
}
int duration_period() const {
return duration_period_;
}
void set_duration_period(int durationPeriod) {
duration_period_ = durationPeriod;
}
void set_config(nlohmann::json const &config) override {
if (algorithm_) {
algorithm_->set_config(config);
if (config.contains("enable") && config["enable"].is_boolean()) {
set_enable(config["enable"].get<bool>());
}
}
}
nlohmann::json config() override {
nlohmann::json config;
config["enable"] = enable();
if (algorithm_) {
return algorithm_->config();
}
return config;
}
double eval(double value) override {
if (algorithm_ && enable_ && duration_period_) {
if (duration_period_ > 0) {
--duration_period_;
}
return algorithm_->eval(value);
}
return value;
}
private:
std::shared_ptr<algorithm> algorithm_;
bool enable_ = false;
int duration_period_ = -1;
};
#endif //FAULT_SIMULATION_ALGORITHM_ALGORITHM_WRAP_HPP
@@ -0,0 +1,10 @@
//
// Created by fly on 2022/4/21.
//
#include "simulation_manager.hpp"
int main(int argc, char *argv) {
return 0;
}
@@ -0,0 +1,106 @@
//
// Created by fly on 2022/4/21.
//
#ifndef FAULT_SIMULATION_ALGORITHM_SIMULATION_MANAGER_HPP
#define FAULT_SIMULATION_ALGORITHM_SIMULATION_MANAGER_HPP
#include "algorithm_wrap.hpp"
#include <periodic_interference.h>
#include <step.h>
#include <white_noise.h>
#include <temperature_drift.h>
#include <spdlog/spdlog.h>
class simulation_manager {
public:
void add_algorithm(std::string const &name, std::shared_ptr<algorithm> algo) {
if (!algorithms_.contains(name)) {
algorithms_.emplace(name, std::make_shared<algorithm_wrap>(algo));
}
}
void remove_algorithm(std::string const &name) {
algorithms_.erase(name);
}
void set_algorithm_enable(std::string const &name, bool flag) {
if (algorithms_.contains(name)) {
algorithms_[name]->set_enable(flag);
}
}
bool algorithm_enable(std::string const &name) {
if (algorithms_.contains(name)) {
return algorithms_[name]->enable();
}
return false;
}
void set_algorithm_duration_period(std::string const &name, int duration_period) {
if (algorithms_.contains(name)) {
algorithms_[name]->set_duration_period(duration_period);
}
}
int algorithm_duration_period(std::string const &name) {
if (algorithms_.contains(name)) {
return algorithms_[name]->duration_period();
}
return -1;
}
void set_algorithm_config(std::string const &name, nlohmann::json const &config) {
if (!algorithms_.contains(name)) {
spdlog::debug("Create {} Algorithm Object", name);
if ("periodic_interference" == name) {
algorithms_[name] = std::make_shared<algorithm_wrap>(std::make_shared<periodic_interference>());
} else if ("step" == name) {
algorithms_[name] = std::make_shared<algorithm_wrap>(std::make_shared<step>());
} else if ("white_noise" == name) {
algorithms_[name] = std::make_shared<algorithm_wrap>(std::make_shared<white_noise>());
} else if ("temperature_drift" == name) {
algorithms_[name] = std::make_shared<algorithm_wrap>(std::make_shared<temperature_drift>());
} else if ("pulse" == name) {
algorithms_[name] = std::make_shared<algorithm_wrap>(std::make_shared<step>());
}
}
if (algorithms_.contains(name)) {
algorithms_[name]->set_config(config);
}
}
nlohmann::json algorithm_config(std::string const &name) {
if (algorithms_.contains(name)) {
return algorithms_[name]->config();
}
return {};
}
nlohmann::json config() {
nlohmann::json j;
for (auto iter = algorithms_.begin(); iter != algorithms_.end(); ++iter) {
j[iter->first] = iter->second->config();
}
return j;
}
void set_config(nlohmann::json const &c) {
for (auto iter = c.begin(); iter != c.end(); ++iter) {
set_algorithm_config(iter.key(), iter.value());
}
}
double eval(double value) {
for (auto &iter: algorithms_) {
value = iter.second->eval(value);
}
return value;
}
private:
std::map<std::string, std::shared_ptr<algorithm_wrap>> algorithms_;
};
#endif //FAULT_SIMULATION_ALGORITHM_SIMULATION_MANAGER_HPP
@@ -0,0 +1,23 @@
add_library(
step
INTERFACE
)
target_sources(
step
INTERFACE
step.cpp
step.h
)
target_include_directories(
step
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(
step
INTERFACE
nlohmann_json::nlohmann_json
)
@@ -0,0 +1,44 @@
//
// Created by baiguwen on 2022/4/20.
//
#include <iostream>
#include "step.h"
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
int main() {
step algorithm;
algorithm.set_config(
{
{"STEP_AMPLITUDE_BASE", 10}
}
);
std::vector <std::vector<double>> user_arr;
std::ifstream fp("C:/data/user_data.csv");
std::string line;
getline(fp,line);
while (getline(fp,line)){
std::vector <double> data_line;
std::string number;
std::istringstream readstr(line);
for(int j = 0;j < 2814;j++){
getline(readstr,number,',');
data_line.push_back(atof(number.c_str()));
}
user_arr.push_back(data_line);
}
std::ofstream outfile;
outfile.open("C:/data/user_data6.csv", std::ios::out);
outfile<<"origin"<<','<<"step"<<std::endl;
for (int i = 0; i <2814; ++i) {
user_arr[i][1]=algorithm.eval(user_arr[i][0]);
std::cout << i << "\t" << user_arr[i][1] << std::endl;
outfile<<user_arr[i][0]<<','<<user_arr[i][1]<<std::endl;
}
outfile.close();
return 0;
}
@@ -0,0 +1,23 @@
//
// Created by baiguwen on 2022/4/20.
//
#include "step.h"
#include <iostream>
void step::set_config(const nlohmann::json &config) {
try {
amplitude_base_ = jsonValue(config, "STEP_AMPLITUDE_BASE");
config_ = config;
} catch (...) {
}
}
nlohmann::json step::config() {
return config_;
}
double step::eval(double value) {
return value + amplitude_base_;
}
@@ -0,0 +1,21 @@
//
// Created by baiguwen on 2022/4/20.
//
#ifndef FAULT_SIMULATION_ALGORITHM_MAIN_STEP_H
#define FAULT_SIMULATION_ALGORITHM_MAIN_STEP_H
#include <algorithm.hpp>
class step : public algorithm {
public:
void set_config(const nlohmann::json &config) override;
nlohmann::json config() override;
double eval(double value) override;
protected:
nlohmann::json config_{};
double amplitude_base_{0};
};
#endif //FAULT_SIMULATION_ALGORITHM_MAIN_STEP_H
@@ -0,0 +1,24 @@
add_library(
temperature-drift
INTERFACE
)
target_sources(
temperature-drift
INTERFACE
temperature_drift.cpp
temperature_drift.h
)
target_include_directories(
temperature-drift
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(
temperature-drift
INTERFACE
nlohmann_json::nlohmann_json
)
@@ -0,0 +1,88 @@
#include <iostream>
#include "temperature_drift.h"
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
int main() {
temperature_drift algorithm;
algorithm.set_config(
{
{"TEMPERATURE_DRIFT_AMPLITUDE", 0.01},
{"TEMPERATURE_DRIFT_AMPLITUDE_BASE", 10}
}
);
std::vector <std::vector<double>> user_arr;
std::ifstream fp("C:/data/user_data.csv");
std::string line;
getline(fp,line);
while (getline(fp,line)){
std::vector <double> data_line;
std::string number;
std::istringstream readstr(line);
for(int j = 0;j < 2814;j++){
getline(readstr,number,',');
data_line.push_back(atof(number.c_str()));
}
user_arr.push_back(data_line);
}
std::ofstream outfile;
outfile.open("C:/data/user_data7.csv", std::ios::out);
outfile<<"origin"<<','<<"temperature_drift"<<std::endl;
for (int i = 0; i <2814; ++i) {
user_arr[i][1]=algorithm.eval(user_arr[i][0]);
std::cout << i << "\t" << user_arr[i][1] << std::endl;
outfile<<user_arr[i][0]<<','<<user_arr[i][1]<<std::endl;
}
outfile.close();
return 0;
}
// std::vector<int> x(100);
// std::generate(x.begin(), x.end(), []() {
// static int i = 0;
// return i++;
// });
// std::vector<double> virtual_data(100, 100.0);
// std::vector<double> temperature_drift_data;
// sciplot::Plot plot;
//
// for (auto iter: virtual_data) {
// temperature_drift_data.push_back(algorithm.eval(iter));
// }
//
// plot.drawCurve(x, virtual_data).label("org");
// plot.drawCurve(x, temperature_drift_data).label("td");
// plot.show();
//
// return 0;
//}
//#include <sciplot/sciplot.hpp>
//using namespace sciplot;
//
//int main(int argc, char** argv)
//{
// // Create values for your x-axis
// Vec x = linspace(0.0, 5.0, 100);
//
// // Create a Plot object
// Plot plot;
//
// // Set color palette
// plot.palette("set2");
//
// // Draw a sine graph putting x on the x-axis and sin(x) on the y-axis
// plot.drawCurve(x, std::sin(x)).label("sin(x)").lineWidth(4);
//
// // Draw a cosine graph putting x on the x-axis and cos(x) on the y-axis
// plot.drawCurve(x, std::cos(x)).label("cos(x)").lineWidth(4);
//
// // Show the plot in a pop-up window
// plot.show();
//
// // Save the plot to a PDF file
// plot.save("plot.pdf");
//}
@@ -0,0 +1,26 @@
//
// Created by fly on 2022/4/6.
//
#include "temperature_drift.h"
void temperature_drift::set_config(nlohmann::json const &config) {
try {
eval_time_ = 0;
amplitude_ = jsonValue(config, "TEMPERATURE_DRIFT_AMPLITUDE");
amplitude_base_ = jsonValue(config, "TEMPERATURE_DRIFT_AMPLITUDE_BASE");
config_ = config;
} catch (...) {
}
}
nlohmann::json temperature_drift::config() {
return config_;
}
double temperature_drift::eval(double value) {
return value + amplitude_base_ + (amplitude_ * ++eval_time_);
}
@@ -0,0 +1,26 @@
//
// Created by fly on 2022/4/6.
//
#ifndef FAULT_SIMULATION_ALGORITHM_TEMPERATURE_DRIFT_H
#define FAULT_SIMULATION_ALGORITHM_TEMPERATURE_DRIFT_H
#include <algorithm.hpp>
class temperature_drift : public algorithm {
public:
void set_config(nlohmann::json const &config) override;
nlohmann::json config() override;
double eval(double value) override;
protected:
nlohmann::json config_{};
double amplitude_{0};
double amplitude_base_{0};
uint64_t eval_time_{0};
};
#endif //FAULT_SIMULATION_ALGORITHM_TEMPERATURE_DRIFT_H
@@ -0,0 +1,23 @@
add_library(
white-noise
INTERFACE
)
target_sources(
white-noise
INTERFACE
white_noise.cpp
white_noise.h
)
target_include_directories(
white-noise
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(
white-noise
INTERFACE
nlohmann_json::nlohmann_json
)
@@ -0,0 +1,80 @@
//
// Created by baiguwen on 2022/4/24.
//
#include <iostream>
#include "white_noise.h"
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
//#include <sciplot/sciplot.hpp>
int main() {
white_noise algorithm;
double white_noise_stddev=2;
double white_noise_mean=0;
algorithm.set_config(
{
{"WHITE_NOISE_MEAN", white_noise_mean},
{"WHITE_NOISE_STDDEV", white_noise_stddev},
}
);
std::vector <std::vector<double>> user_arr;
std::ifstream fp("C:/data/user_data.csv");
std::string line;
getline(fp,line);
while (getline(fp,line)){
std::vector <double> data_line;
std::string number;
std::istringstream readstr(line);
for(int j = 0;j < 2814;j++){
getline(readstr,number,',');
data_line.push_back(atof(number.c_str()));
}
user_arr.push_back(data_line);
}
std::ofstream outfile;
outfile.open("C:/data/user_data8.csv", std::ios::out);
outfile<<"origin"<<','<<"white_noise"<<std::endl;
for (int i = 0; i <2814; ++i) {
user_arr[i][1]=algorithm.eval(user_arr[i][0]);
std::cout << i << "\t" << user_arr[i][1] << std::endl;
outfile<<user_arr[i][0]<<','<<user_arr[i][1]<<std::endl;
}
outfile.close();
return 0;
}
// int length=round(white_noise_stddev)*6;
// std::vector<int> x(length+1,0);
//for(int i=0;i<length+1;i++){
// x[i]=100-length/2+round(white_noise_mean)+i;
//}
//
// std::vector<double> virtual_data(10000, 100.0);
// std::vector<double> white_noise_data;
// std::vector<int> white_noise_data_number(length+1,0);
// sciplot::Plot plot;
//
// for (auto iter: virtual_data) {
// double a=algorithm.eval(iter);
// white_noise_data.push_back (round(a));
// std::cout << iter << "\t" << a << std::endl;
// }
// for(int k=0;k<virtual_data.size();k++){
// for(int l=0;l<length+1;l++){
// if(white_noise_data[k]==x[l])
// white_noise_data_number[l]++;
//
//
// }
//
// }
//
// plot.drawCurve(x, white_noise_data_number).label("td");
// plot.show();
//
// return 0;
//}
@@ -0,0 +1,27 @@
//
// Created by baiguwen on 2022/4/24.
//
#include "white_noise.h"
void white_noise::set_config(const nlohmann::json &config) {
try {
mean_ = jsonValue(config, "WHITE_NOISE_MEAN");
standard_deviation_ = jsonValue(config, "WHITE_NOISE_STDDEV");
normal_distribution_.reset(new std::normal_distribution<>(mean_, standard_deviation_));
config_ = config;
} catch (...) {
}
}
nlohmann::json white_noise::config() {
return config_;
}
double white_noise::eval(double value) {
if (normal_distribution_) {
value = value + normal_distribution_->operator()(mt19937_);
}
return value;
}
@@ -0,0 +1,26 @@
//
// Created by baiguwen on 2022/4/24.
//
#ifndef FAULT_SIMULATION_ALGORITHM_WHITE_NOISE_H
#define FAULT_SIMULATION_ALGORITHM_WHITE_NOISE_H
#include <algorithm.hpp>
#include <math.h>
#include <random>
class white_noise : public algorithm {
public:
void set_config(const nlohmann::json &config) override;
nlohmann::json config() override;
double eval(double value) override;
protected:
nlohmann::json config_{};
std::mt19937 mt19937_{std::random_device()()};
std::shared_ptr <std::normal_distribution<>> normal_distribution_;
double mean_{0};
double standard_deviation_{0};
};
#endif //FAULT_SIMULATION_ALGORITHM_WHITE_NOISE_H
+144
View File
@@ -0,0 +1,144 @@
#include <iostream>
#include <open62541/client.h>
#include <prometheus/exposer.h>
#include <prometheus/registry.h>
#include <prometheus/counter.h>
#include <spdlog/spdlog.h>
#include <yaml-cpp/yaml.h>
#include <CivetServer.h>
#include <string>
#include <filesystem>
#include "client/client.h"
#include "collector/collector.h"
class MetricsHandle : public CivetHandler {
public:
explicit MetricsHandle(std::shared_ptr<collector> coll) : coll_(coll) {
}
bool handleGet(CivetServer *server, struct mg_connection *conn) override {
std::string json = to_string(coll_->getMetricsJson());
mg_send_http_ok(conn, "application/json", json.size());
mg_write(conn, json.c_str(), json.size());
return true;
}
private:
std::shared_ptr<collector> coll_;
};
class SimulationHandle : public CivetHandler {
public:
explicit SimulationHandle(std::shared_ptr<collector> coll) : coll_(coll) {
}
bool handleGet(CivetServer *server, struct mg_connection *conn) override {
auto name = getMetric(conn);
auto context = to_string(coll_->simulationConfig(name));
mg_send_http_ok(conn, "application/json", context.size());
mg_write(conn, context.c_str(), context.size());
return true;
}
bool handlePost(CivetServer *server, struct mg_connection *conn) override {
auto name = getMetric(conn);
auto json = getPostJson(conn);
coll_->setSimulationConfig(name, json);
mg_send_http_ok(conn, "application/json", 0);
return true;
}
protected:
std::string getMetric(mg_connection *conn) {
const struct mg_request_info *req_info = mg_get_request_info(conn);
if (nullptr != req_info->query_string) {
return req_info->query_string;
}
return {};
}
nlohmann::json getPostJson(mg_connection *conn) {
const struct mg_request_info *req_info = mg_get_request_info(conn);
auto size = req_info->content_length + 1;
char *buff = new char[size];
mg_read(conn, buff, size);
auto ret = nlohmann::json::parse(buff, buff + size - 1, nullptr, false);
delete[] buff;
return ret;
}
private:
std::shared_ptr<collector> coll_;
};
int main(int argc, char *argv[]) {
mg_init_library(0);
spdlog::info("OPC-UA Exporter");
std::string config_file_name = "./opcua.yml";
#ifdef Debug
spdlog::set_level(spdlog::level::trace);
#endif
if (2 == argc) {
config_file_name = argv[1];
}
if (!std::filesystem::exists(config_file_name)) {
spdlog::error("the config file({}) not exists.", config_file_name);
return EXIT_FAILURE;
}
YAML::Node config = YAML::LoadFile(config_file_name);
auto exposer_bind_addr = config["exposer"]["bind"].as<std::string>("0.0.0.0:8090");
auto exposer_thread_num = config["exposer"]["thread"].as<std::size_t>(2);
spdlog::info("exposer bind addr : {}", exposer_bind_addr);
spdlog::info("exposer thread number : {}", exposer_thread_num);
prometheus::Exposer exposer{exposer_bind_addr, exposer_thread_num};
std::vector<std::string> civetOptions = {
{"listening_ports"},
{config["api"]["port"].as<std::string>("8080")}
};
CivetServer webServer(civetOptions);
auto ua_edp = config["opcua"]["endpoint"].as<std::string>();
if (ua_edp.empty()) {
spdlog::error("the opc ua endpoint not exists.");
return EXIT_FAILURE;
}
auto ua_client = std::make_shared<client>(ua_edp);
auto opc_collector = std::make_shared<collector>(config["metrics"], ua_client);
exposer.RegisterCollectable(opc_collector);
auto metricsHandle = MetricsHandle(opc_collector);
auto simulationHandle = SimulationHandle(opc_collector);
webServer.addHandler(
"/api/metrics",
metricsHandle
);
webServer.addHandler(
"/api/simulate",
simulationHandle
);
ua_client->run();
return 0;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
{
"name": "opcua-exporter",
"version": "1.0",
"dependencies": [
"civetweb",
"prometheus-cpp",
"spdlog",
"nlohmann-json",
"yaml-cpp",
"open62541"
]
}