diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fdc3ded417d6941e7592b867d635fa9ff813ed7..40f247c46b3c7e9bc20a1d7379c46e2a70fd0fea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,7 @@ cmake_minimum_required(VERSION 3.6) project(SmartGridModell) -set(CMAKE_CXX_STANDARD 11) -add_compile_options(-Wall -Wextra -pedantic) +set(CMAKE_CXX_STANDARD 17) # Add spdlog for nice and easy logging add_subdirectory(libs/spdlog) @@ -30,6 +29,27 @@ add_executable(smart_grid.exe src/i2c/Node.cpp src/com/Socket.cpp src/com/Protocol.cpp + src/ModelState.cpp ) target_link_libraries(smart_grid.exe spdlog) +target_compile_options(smart_grid.exe PRIVATE -Wall -Wextra -pedantic) + + +# Add OPC UA +# macro(SET_OPTION option value) +# set(${option} ${value} CACHE "" INTERNAL FORCE) +# endmacro() +option(BUILD_CLIENT "Build Client" OFF) +option(BUILD_SERVER "Build Server" ON) + +option(SSL_SUPPORT_MBEDTLS "Support rsa-oaep password encryption using mbedtls library " OFF) + +option(BUILD_PYTHON "Build Python bindings" OFF) +option(BUILD_TESTING "Build and run tests" OFF) +option(BUILD_SHARED_LIBS "Build shared libraries." ON) +add_subdirectory(libs/freeopcua) + +add_executable(opc_server.exe src/opc_server.cpp) +target_include_directories(opc_server.exe PUBLIC libs/freeopcua/include) +target_link_libraries(opc_server.exe opcuacore opcuaserver) \ No newline at end of file diff --git a/src/ModelState.cpp b/src/ModelState.cpp new file mode 100644 index 0000000000000000000000000000000000000000..22c19b01ed4c7ea164a10470b2fa093298dc11bc --- /dev/null +++ b/src/ModelState.cpp @@ -0,0 +1,117 @@ +/// @file ModelState.cpp +/// + +#include "ModelState.hpp" + + +ModelState::ModelState(SmartGridModell &modell) + : m_modell(modell) +{ + std::srand(std::time(nullptr)); +} + +void ModelState::update_wind() +{ + double wind_by_sun = m_sun * 7.0 / 100.0; // wind by sun should by 5 max. + auto random_wind = ((std::rand() * 1.0) / RAND_MAX) * 5.0; + m_wind = power_wind[static_cast<int>(wind_by_sun + random_wind)]; +} + + +void ModelState::update_modell() +{ + auto sun = m_sun * 2.5; + auto wind = m_wind / 8.3; + auto renewable = (sun + wind) / 2; + m_modell.set_solar_plant(sun); + m_modell.update_windmill_speed(wind); + m_modell.set_windmill_net(wind); + m_modell.set_renewable_net(renewable); + if (m_production.conventional > 0) + { + m_modell.set_power_plant(200); + m_modell.set_village_color(200, renewable); + } + else + { + m_modell.set_power_plant(0); + m_modell.set_village_color(0, renewable); + } +} + + +void ModelState::update_power_production() +{ + m_production.renewable.solar = m_sun * solar_size; + m_production.renewable.wind = m_wind * windpark_size; + if (m_usage.sum() > m_production.clean()) + { + m_excess_power = 0.0; + m_production.conventional = m_usage.sum() - m_production.clean(); + } + else + { + m_excess_power = m_production.clean() - m_usage.sum(); + m_production.conventional = 0; + } +} + + +double ModelState::excess_power() +{ + auto sum_prod = m_production.conventional + m_production.renewable.solar + m_production.renewable.wind; + auto sum_used = m_usage.industry + m_usage.village; + return sum_prod - sum_used; +} + + +void ModelState::update_power_consumption() +{ + m_usage.village = village_consumption_at[m_time] * village_size; + if (m_producing == true) + { + m_usage.industry = MaxPower::industry; + } + else + { + m_usage.industry = 0.0; + } +} + +void ModelState::update_time() +{ + if (m_time < 23) + { + ++m_time; + } + else + { + m_time = 0; + } +} + +void ModelState::update_sun() +{ + m_sun = sunnshine_percentage[m_time]; +} + + +void ModelState::print_states() +{ + spdlog::debug("Time <{}> Sun <{}> Wind<{}>", m_time, m_sun, m_wind); + spdlog::debug("Power Conv <{}> Solar <{}> Wind<{}>", m_production.conventional, m_production.renewable.solar, m_production.renewable.wind); + spdlog::debug("Usage Village<{}> Industry<{}>", m_usage.village, m_usage.industry); + spdlog::debug("excess_power<{}>", excess_power()); +} + + +void ModelState::next_hour() +{ + update_time(); + update_sun(); + update_wind(); + update_power_consumption(); + update_power_production(); + update_modell(); + print_states(); +} \ No newline at end of file diff --git a/src/ModelState.hpp b/src/ModelState.hpp new file mode 100644 index 0000000000000000000000000000000000000000..86c90127d986126416f284fb9fb38049a091aa24 --- /dev/null +++ b/src/ModelState.hpp @@ -0,0 +1,89 @@ +/// @file ModelState.hpp +/// + +#ifndef MODEL_STATE_HPP +#define MODEL_STATE_HPP + +#include <cstdlib> +#include <ctime> + +#include "spdlog/spdlog.h" +#include "SmartGridModell.hpp" + +struct MaxPower +{ + static constexpr double village = 600; + static constexpr double industry = 200; + static constexpr double wind = 250; + static constexpr double solar = 110; +}; + +constexpr double village_consumption_at[]{ + 98, 95, 93, 94, 95, 101, 115, + 127, 132, 134, 136, 139, 138, + 136, 134, 132, 130, 132, 132, + 131, 125, 119, 114, 105, 98}; + +constexpr double sunnshine_percentage[]{ + 0, 0, 0, 0, 0, 0, 3, + 12, 30, 52, 73, 88, 97, + 100, 98, 91, 81, 66, 46, + 25, 10, 2, 0, 0, 0}; + +constexpr double power_wind[]{ + 0, 3, 25, 82, 174, + 321, 532, 815, 1180, + 1612, 1890, 2000, 2100}; + +struct CleanPower +{ + double solar = 0.0; + double wind = 0.0; +}; + +struct PowerProduction +{ + double conventional; + CleanPower renewable; + double clean() { return renewable.solar + renewable.wind; } + double sum() { return conventional + clean(); } +}; + +struct PowerUsage +{ + double village = 0.0; + double industry = 0.0; + double sum() { return village + industry; } +}; + +class ModelState +{ +public: + ModelState(SmartGridModell &modell); + void next_hour(); + double excess_power(); + +private: + void update_time(); + void update_sun(); + void update_wind(); + void update_power_consumption(); + void update_power_production(); + void update_modell(); + void print_states(); + + static constexpr double solar_size{10}; + static constexpr double windpark_size{1.0}; + static constexpr double village_size{10}; + + int m_time{0}; + double m_sun{0}; + double m_wind{0}; + double m_excess_power{0.0}; + PowerProduction m_production; + PowerUsage m_usage; + bool m_producing{false}; + SmartGridModell &m_modell; +}; + +#endif \ No newline at end of file diff --git a/src/SmartGridModell.cpp b/src/SmartGridModell.cpp index c8ca1a71505b66fe59eef1bd353d81c3a1cf1a46..c51bbef76854351c6f8e6f218dfc110e3055d0d6 100644 --- a/src/SmartGridModell.cpp +++ b/src/SmartGridModell.cpp @@ -12,6 +12,11 @@ SmartGridModell::SmartGridModell(i2c::Node& node, DefaultState initial_state) put_modell_into_state(initial_state); } +SmartGridModell::~SmartGridModell() +{ + put_modell_into_state(DefaultState::Off); +} + void SmartGridModell::put_modell_into_state(DefaultState state) { diff --git a/src/SmartGridModell.hpp b/src/SmartGridModell.hpp index cee45b00e1ca820950fe0bb663d3948a0c1a8ead..de4f792812aa961f141285d857f90f2bc1ba0587 100644 --- a/src/SmartGridModell.hpp +++ b/src/SmartGridModell.hpp @@ -58,6 +58,9 @@ public: /// SmartGridModell(i2c::Node& node, DefaultState initial_state = DefaultState::Off); + ~SmartGridModell(); + + /// @brief Put the to one of the predefined states. /// /// @param state Default state to which the modell will be changed. diff --git a/src/i2c/Node.cpp b/src/i2c/Node.cpp index b354266ad251d85660e722d4e4389160ad92f8fe..3ee42ead9463192a95deedfb8d0376213771fc57 100644 --- a/src/i2c/Node.cpp +++ b/src/i2c/Node.cpp @@ -9,11 +9,9 @@ using namespace i2c; -Node::Node(uint8_t node_address) - : m_address{node_address} - , m_device{-1} +Node::Node(uint8_t node_address) + : m_address{node_address}, m_device{-1} { - } Node::~Node() @@ -21,7 +19,7 @@ Node::~Node() close(m_device); } -bool Node::open_device(const char* i2c_device_name) +bool Node::open_device(const char *i2c_device_name) { auto fd = open(i2c_device_name, O_RDWR); if (fd < 0) @@ -39,9 +37,7 @@ bool Node::open_device(const char* i2c_device_name) return true; } - - -bool Node::send(uint8_t* data, ssize_t size) +bool Node::send(uint8_t *data, ssize_t size) { ssize_t bytes_written = write(m_device, data, size); if (bytes_written != size) @@ -54,7 +50,7 @@ bool Node::send(uint8_t* data, ssize_t size) bool Node::send(std::vector<uint8_t> &data) { - return send(data.data(), data.size()); + return send(data.data(), data.size()); } bool Node::send(std::array<uint8_t, 2> &data) @@ -64,15 +60,14 @@ bool Node::send(std::array<uint8_t, 2> &data) bool Node::send(uint8_t reg, uint8_t val) { - uint8_t data[2] {reg, val}; + uint8_t data[2]{reg, val}; return send(data, 2); } - constexpr uint8_t i2c_smbus_max_block_size = 32; struct i2c_data { - uint8_t block [i2c_smbus_max_block_size + 2]; + uint8_t block[i2c_smbus_max_block_size + 2]; }; struct i2c_ioctl_data @@ -80,7 +75,7 @@ struct i2c_ioctl_data char read_write; uint8_t command; int size; - i2c_data* data; + i2c_data *data; }; int Node::read16(uint8_t reg_addr) @@ -100,7 +95,7 @@ int Node::read16(uint8_t reg_addr) } else { - int answer {0}; + int answer{0}; answer |= static_cast<int>(data.block[0] << 8); // MSB answer |= static_cast<int>(data.block[1] << 0); // LSB spdlog::debug("Received data: {} {} converted to {}.", data.block[0], data.block[1], answer); diff --git a/src/opc_server.cpp b/src/opc_server.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cb33b8e89e8e5ad2fe93a734e1eb64c9a55e27f7 --- /dev/null +++ b/src/opc_server.cpp @@ -0,0 +1,49 @@ +/// @file opc_server.cpp +/// + +#include <opc/ua/node.h> +#include <opc/ua/server/server.h> + +using namespace OpcUa; + + + +std::vector<OpcUa::Variant> my_method(NodeId context, std::vector<OpcUa::Variant> arguments) +{ + std::cout << "Method triggered" << std::endl; + + std::vector<OpcUa::Variant> result; + result.push_back(Variant(static_cast<uint8_t>(0))); + return result; +} + +void add_nodes_to(uint32_t idx, Node &objects) +{ + Node smart_grid = objects.AddFolder(idx, "smart_grid"); + Node renewable = smart_grid.AddVariable(idx, "renewable_power", Variant(42.0)); + Node prop = smart_grid.AddProperty(idx, "a_property_thin", Variant("something")); + Node method = smart_grid.AddMethod(idx, "trigger_method_x", my_method); +} + + +int main(int argc, char **argv) +{ + auto logger = spdlog::stderr_color_mt("server"); + OpcUa::UaServer server(logger); + + server.SetEndpoint("opc.tcp://localhost:4840/opcua/smartgridserver"); + server.SetServerURI("Smart Grid OPC UA Server"); + server.Start(); + + auto idx = server.RegisterNamespace("smart-grid"); + Node objects = server.GetObjectsNode(); + + add_nodes_to(idx, objects); + + while (true) + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + return 0; +} \ No newline at end of file