diff --git a/CMakeLists.txt b/CMakeLists.txt index 1987450e7ed2fede863b9a58b8bb5e53bd76049d..ae3df2a49f570379784be49007b6d77321248a56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.6) -project(SmartGridModell) +project(SmartGridModel) set(CMAKE_CXX_STANDARD 17) # Add spdlog for nice and easy logging @@ -32,32 +32,35 @@ include_directories(src/i2c) include_directories(src/smart_grid) add_executable(smart_grid.exe - src/smg_server.cpp - src/smart_grid/SmartGridModell.cpp - src/smart_grid/StateLogger.cpp + src/apps/smart_grid/smg_server.cpp + src/apps/smart_grid/SmgStateMachine.cpp + src/apps/smart_grid/SmartOpcServer.cpp + src/smart_grid/SmartGridModel.cpp + # src/smart_grid/StateLogger.cpp src/i2c/Node.cpp src/com/Socket.cpp src/com/Protocol.cpp - src/smart_grid/ModelState.cpp + src/smart_grid/HardwareControl.cpp ) target_link_libraries(smart_grid.exe spdlog) target_compile_options(smart_grid.exe PRIVATE -Wall -Wextra -pedantic) - +target_include_directories(smart_grid.exe PUBLIC libs/freeopcua/include) +target_link_libraries(smart_grid.exe opcuacore opcuaserver) # 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(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(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) +# 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/apps/SmgOPC_server/opc_server.cpp) target_include_directories(opc_server.exe PUBLIC libs/freeopcua/include) diff --git a/src/apps/SmgOPC_server/opc_server.cpp b/src/apps/SmgOPC_server/opc_server.cpp index a1e68f123e9b4bcced6af0b7367633aea8f5dc91..b9bc21c935cd46b79f86f56770980ccf91a3982a 100644 --- a/src/apps/SmgOPC_server/opc_server.cpp +++ b/src/apps/SmgOPC_server/opc_server.cpp @@ -13,20 +13,37 @@ std::vector<OpcUa::Variant> my_method(NodeId context, std::vector<OpcUa::Variant { std::cout << "Method triggered" << std::endl; std::cout << renewable.GetValue().ToString() << std::endl; + std::cout << renewable.GetValue().Type() << std::endl; + + auto vv = renewable.GetValue().As<double>(); + vv += 1; + //renewable.SetValue(Variant(vv)); std::vector<OpcUa::Variant> result; result.push_back(Variant(static_cast<uint8_t>(0))); return result; } +class SubClient : public SubscriptionHandler +{ + void DataChange(uint32_t handle, const Node & node, const Variant & val, AttributeId attr) override + { + std::cout << "Received DataChange event for Node " << node << std::endl; + + } +}; + void add_nodes_to(uint32_t idx, Node &objects) { Node smart_grid = objects.AddFolder(idx, "smart_grid"); 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); -} + SubClient clt; + auto sub = server.CreateSubscription(100, clt); + sub->SubscribeDataChange(renewable); +} int main(int argc, char **argv) { @@ -42,9 +59,11 @@ int main(int argc, char **argv) add_nodes_to(idx, objects); + while (true) { - std::this_thread::sleep_for(std::chrono::seconds(1)); + std::this_thread::sleep_for(std::chrono::seconds(10)); + } return 0; diff --git a/src/apps/smart_grid/SmartOpcServer.cpp b/src/apps/smart_grid/SmartOpcServer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9283130a6705f2befee97bfed211e01ca8a4eca2 --- /dev/null +++ b/src/apps/smart_grid/SmartOpcServer.cpp @@ -0,0 +1,98 @@ +/// + +#include "SmartOpcServer.hpp" + +using namespace OpcUa; + + +class SubClientTime : public SubscriptionHandler +{ +public: + SubClientTime(SmartGridModel &model) + : m_model{model}{} + +private: + void DataChange(uint32_t handle, const Node &node, const Variant &val, AttributeId attr) override + { + auto v = val.As<int>(); + m_model.set_time(v); + } + SmartGridModel &m_model; +}; + +class SubClientProductionEnabled : public SubscriptionHandler +{ +public: + SubClientProductionEnabled(SmartGridModel &model) : m_model{model}{} + +private: + void DataChange(uint32_t handle, const Node &node, const Variant &val, AttributeId attr) override + { + auto v = val.As<bool>(); + m_model.set_producing_state(v); + } + SmartGridModel &m_model; +}; + +class SubClientSimModeEnabled : public SubscriptionHandler +{ +public: + SubClientSimModeEnabled(SmgStateMachine &state_machine) : m_state_machine{state_machine}{} + +private: + void DataChange(uint32_t handle, const Node &node, const Variant &val, AttributeId attr) override + { + auto v = val.As<bool>(); + m_state_machine.set_sim_mode_enabled(v); + } + SmgStateMachine &m_state_machine; +}; + + +SmartOpcServer::SmartOpcServer(SmgStateMachine &state_machine) + : m_state_machine{state_machine} +{ + m_logger = spdlog::stderr_color_mt("server"); + m_server = UaServer(m_logger); + m_server.SetEndpoint(k_opc_server_url); + m_server.SetServerURI("Smart Grid OPC UA server"); +} + +void SmartOpcServer::setup_server() +{ + m_server.Start(); + m_idx = m_server.RegisterNamespace("https://gitlab.cvh-server.de/aco/smart-grid-modell"); + m_objects_folder = m_server.GetObjectsNode(); + m_root_node = m_server.GetRootNode(); + + Node smart_grid_folder{m_objects_folder.AddFolder(m_idx, "smart_grid_model")}; + + m_state = smart_grid_folder.AddObject(m_idx, "state"); + m_time = m_state.AddVariable(m_idx, "time", Variant(m_state_machine.m_model.get_time())); + m_excess_power = m_state.AddVariable(m_idx, "excess_power", Variant(m_state_machine.m_model.get_excess_power())); + + m_controls = smart_grid_folder.AddObject(m_idx, "controls"); + m_producing = m_controls.AddVariable(m_idx, "producing", Variant(m_state_machine.m_model.get_producing_state())); + m_sim_mode_enabled = m_controls.AddVariable(m_idx, "sim_mode_enabled", Variant(m_state_machine.is_sim_mode_enabled())); +} + + +void SmartOpcServer::run_server() +{ + setup_server(); + + SubClientTime subcl_time{m_state_machine.m_model}; + auto sub_time = m_server.CreateSubscription(100, subcl_time); + sub_time->SubscribeDataChange(m_time); + + SubClientProductionEnabled subcl_production_enabled{m_state_machine.m_model}; + auto sub_production_enabled = m_server.CreateSubscription(100, subcl_production_enabled); + sub_production_enabled->SubscribeDataChange(m_producing); + + + while(true) + { + m_state_machine.run(); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } +} \ No newline at end of file diff --git a/src/apps/smart_grid/SmartOpcServer.hpp b/src/apps/smart_grid/SmartOpcServer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2ad423112d011bf4beb85253f7aed28f611d5ac9 --- /dev/null +++ b/src/apps/smart_grid/SmartOpcServer.hpp @@ -0,0 +1,47 @@ + +#ifndef SMART_OPC_SERVER_HPP +#define SMART_OPC_SERVER_HPP + +#include <thread> +#include <chrono> +#include <string> + +#include <opc/ua/node.h> +#include <opc/ua/subscription.h> +#include <opc/ua/server/server.h> + +#include "SmgStateMachine.hpp" + +const char* k_opc_server_url = "opc.tcp://localhost:4840/opcua/smart-grid-model"; + +class SmartOpcServer +{ +public: + SmartOpcServer(SmgStateMachine &state_machine); + + void run_server(); + +private: + void setup_server(); + + SmgStateMachine &m_state_machine; + + OpcUa::UaServer m_server; + uint32_t m_idx; // namespace index in server + OpcUa::Node m_objects_folder; // address space for node objects + OpcUa::Node m_root_node; + + OpcUa::Node m_state; + OpcUa::Node m_time; + OpcUa::Node m_excess_power; + + OpcUa::Node m_controls; + OpcUa::Node m_producing; + OpcUa::Node m_sim_mode_enabled; + + std::shared_ptr<spdlog::logger> m_logger; + +}; + + +#endif \ No newline at end of file diff --git a/src/apps/smart_grid/SmgStateMachine.cpp b/src/apps/smart_grid/SmgStateMachine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3b885ed4639ece6695dd06f4cc4fc9774d70e4b2 --- /dev/null +++ b/src/apps/smart_grid/SmgStateMachine.cpp @@ -0,0 +1,95 @@ +/// + +#include "SmgStateMachine.hpp" + +SmgStateMachine::SmgStateMachine(SmartGridModel &model) + : m_model{model} +{ +} + + +void SmgStateMachine::run() +{ + if (m_state_machine_is_running) + { + switch (m_main_state) + { + case MainState::Simulation: + simulation_state(); + break; + case MainState::ManualControl: + manual_state(); + break; + } + } +} + + +void SmgStateMachine::simulation_state() +{ + switch (m_sim_state) + { + case SimState::Entry: + m_sim_state = SimState::Do; + break; + case SimState::Do: + m_model.next_hour(); + if (false) // some external condition + { + m_sim_state = SimState::Exit; + } + break; + case SimState::Exit: + m_sim_state = SimState::Entry; + m_main_state = MainState::ManualControl; + m_manual_state = ManualState::Entry; + break; + } +} + +void SmgStateMachine::manual_state() +{ + switch (m_manual_state) + { + case ManualState::Entry: + m_manual_state = ManualState::Do; + break; + case ManualState::Do: + + // apply changes + m_model.update_states(); + if (false) // some external condition + { + m_manual_state = ManualState::Exit; + } + break; + case ManualState::Exit: + m_manual_state = ManualState::Entry; + m_main_state = MainState::Simulation; + m_sim_state = SimState::Entry; + break; + } +} + +bool SmgStateMachine::is_sim_mode_enabled() +{ + return MainState::Simulation == m_main_state; +} + +void SmgStateMachine::set_sim_mode_enabled(bool enable) +{ + if (enable) + { + if (m_main_state == MainState::ManualControl) + { + m_manual_state = ManualState::Exit; + } + } + else + { + if (m_main_state == MainState::Simulation) + { + m_sim_state = SimState::Exit; + } + } +} \ No newline at end of file diff --git a/src/apps/smart_grid/SmgStateMachine.hpp b/src/apps/smart_grid/SmgStateMachine.hpp new file mode 100644 index 0000000000000000000000000000000000000000..501a62413c8e2d57d4e13dc013ff84c3dc3adf02 --- /dev/null +++ b/src/apps/smart_grid/SmgStateMachine.hpp @@ -0,0 +1,51 @@ +/// + +#ifndef SMG_STATE_MACHINE_HPP +#define SMG_STATE_MACHINE_HPP + +#include "SmartGridModel.hpp" +#include "HardwareControl.hpp" + +enum class MainState +{ + Simulation, + ManualControl +}; + +enum class SimState +{ + Entry, + Do, + Exit +}; + +enum class ManualState +{ + Entry, + Do, + Exit +}; + + +class SmgStateMachine +{ +public: + SmgStateMachine(SmartGridModel &model); + void run(); + bool is_sim_mode_enabled(); + void set_sim_mode_enabled(bool enable); + + SmartGridModel &m_model; +private: + + void simulation_state(); + void manual_state(); + + + bool m_state_machine_is_running {true}; + MainState m_main_state {MainState::Simulation}; + SimState m_sim_state {SimState::Entry}; + ManualState m_manual_state {ManualState::Entry}; +}; + +#endif \ No newline at end of file diff --git a/src/apps/smart_grid/smg_server.cpp b/src/apps/smart_grid/smg_server.cpp new file mode 100644 index 0000000000000000000000000000000000000000..34f0daf75edf4b96ca3dc96c5b234a6787d8e39b --- /dev/null +++ b/src/apps/smart_grid/smg_server.cpp @@ -0,0 +1,51 @@ +/// @file main.cpp +/// +#include <thread> +#include "spdlog/spdlog.h" + +#include "i2c/Node.hpp" +#include "HardwareControl.hpp" + +#include "Socket.hpp" +#include "Protocol.hpp" + +#include "SmartGridModel.hpp" +#include "SmgStateMachine.hpp" + +/// @brief Log user inputs +/// +void log_args(int argc, char **argv) +{ + spdlog::debug("User arguments:"); + for (int i = 0; i < argc; ++i) + { + spdlog::debug("[{}] {}", i, argv[i]); + } +} + +int main(int argc, char **argv) +{ + spdlog::set_level(spdlog::level::info); + spdlog::info("Starting - Smart Grid Modell"); + log_args(argc, argv); + + uint8_t slave_address{0x14}; + i2c::Node i2c_channel{slave_address}; + if (false == i2c_channel.open_device("/dev/i2c-1")) + { + exit(1); + } + + HardwareControl hardware{i2c_channel}; + SmartGridModel model{hardware}; + SmgStateMachine stateMachine{model}; + + // auto log = std::make_shared<StateLogger>(); + // model.attach(log); + + // run state machine + stateMachine.run(); + + spdlog::info("End"); + return 0; +} diff --git a/src/smart_grid/SmartGridModell.cpp b/src/smart_grid/HardwareControl.cpp similarity index 73% rename from src/smart_grid/SmartGridModell.cpp rename to src/smart_grid/HardwareControl.cpp index cef86c005a0d716fe3e442f3022af8e643f3c1e1..62c0c9a5cd295b6eae6d1739d429cb945014aa63 100644 --- a/src/smart_grid/SmartGridModell.cpp +++ b/src/smart_grid/HardwareControl.cpp @@ -1,24 +1,24 @@ -/// @file SmartGridModell.cpp -/// @ +/// @file HardwareControl.cpp +/// -#include <SmartGridModell.hpp> +#include <HardwareControl.hpp> #include <spdlog/spdlog.h> #include <thread> -SmartGridModell::SmartGridModell(i2c::Node& node, DefaultState initial_state) +HardwareControl::HardwareControl(i2c::Node& node, DefaultState initial_state) : m_modell{node} { put_modell_into_state(initial_state); } -SmartGridModell::~SmartGridModell() +HardwareControl::~HardwareControl() { - // put_modell_into_state(DefaultState::Off); + put_modell_into_state(DefaultState::Off); } -void SmartGridModell::put_modell_into_state(DefaultState state) +void HardwareControl::put_modell_into_state(DefaultState state) const { switch (state) { @@ -34,7 +34,7 @@ void SmartGridModell::put_modell_into_state(DefaultState state) } -void SmartGridModell::default_state_off() +void HardwareControl::default_state_off() const { update_windmill_speed(0); set_village_color(0, 0); @@ -45,7 +45,7 @@ void SmartGridModell::default_state_off() } -void SmartGridModell::default_state_fancy() +void HardwareControl::default_state_fancy() const { update_windmill_speed(50); set_village_color(20, 20); @@ -56,7 +56,7 @@ void SmartGridModell::default_state_fancy() } -void SmartGridModell::update_windmill_speed(uint8_t speed) +void HardwareControl::update_windmill_speed(uint8_t speed) const { constexpr uint8_t min_motor_speed {40}; if (speed == 0) @@ -78,7 +78,7 @@ void SmartGridModell::update_windmill_speed(uint8_t speed) } -void SmartGridModell::set_village_color(uint8_t red, uint8_t green) +void HardwareControl::set_village_color(uint8_t red, uint8_t green) const { set_output_pin(Pin::VillageNetRed, red); set_output_pin(Pin::VillageNetGreen, green); @@ -93,28 +93,28 @@ void SmartGridModell::set_village_color(uint8_t red, uint8_t green) } } -void SmartGridModell::set_power_plant(uint8_t brightness) +void HardwareControl::set_power_plant(uint8_t brightness) const { set_output_pin(Pin::PowerPlantNet, brightness); } -void SmartGridModell::set_solar_plant(uint8_t brightness) +void HardwareControl::set_solar_plant(uint8_t brightness) const { set_output_pin(Pin::SolarFieldNet, brightness); } -void SmartGridModell::set_windmill_net(uint8_t brightness) +void HardwareControl::set_windmill_net(uint8_t brightness) const { set_output_pin(Pin::WindmillNet, brightness); } -void SmartGridModell::set_renewable_net(uint8_t brightness) +void HardwareControl::set_renewable_net(uint8_t brightness) const { set_output_pin(Pin::WindAndSolarNet, brightness); } -void SmartGridModell::set_output_pin(Pin pin, uint8_t value) +void HardwareControl::set_output_pin(Pin pin, uint8_t value) const { bool succ = m_modell.send(static_cast<uint8_t>(pin), value); spdlog::debug("Send: {} {}, success: {}", pin, value, succ); @@ -122,7 +122,7 @@ void SmartGridModell::set_output_pin(Pin pin, uint8_t value) this_thread::sleep_for(chrono::milliseconds(14)); } -void SmartGridModell::set_house_color(House number, uint8_t red, uint8_t green) +void HardwareControl::set_house_color(House number, uint8_t red, uint8_t green) const { switch (number) { diff --git a/src/smart_grid/SmartGridModell.hpp b/src/smart_grid/HardwareControl.hpp similarity index 80% rename from src/smart_grid/SmartGridModell.hpp rename to src/smart_grid/HardwareControl.hpp index de4f792812aa961f141285d857f90f2bc1ba0587..055d01d35104eedbc9aa2898f1460350bc98581b 100644 --- a/src/smart_grid/SmartGridModell.hpp +++ b/src/smart_grid/HardwareControl.hpp @@ -1,12 +1,12 @@ -#ifndef SMART_GRID_MODELL_HEADER -#define SMART_GRID_MODELL_HEADER +#ifndef HARDWARE_CONTROL_HEADER +#define HARDWARE_CONTROL_HEADER #include <i2c/Node.hpp> -class SmartGridModell +class HardwareControl { public: @@ -56,16 +56,16 @@ public: /// @brief Initialiese modell with everything off. /// - SmartGridModell(i2c::Node& node, DefaultState initial_state = DefaultState::Off); + HardwareControl(i2c::Node& node, DefaultState initial_state = DefaultState::Off); - ~SmartGridModell(); + ~HardwareControl(); /// @brief Put the to one of the predefined states. /// /// @param state Default state to which the modell will be changed. /// - void put_modell_into_state(DefaultState state); + void put_modell_into_state(DefaultState state) const; /// @brief Set the turning speed of the windmill. /// @details @@ -74,39 +74,39 @@ public: /// /// @param speed Turning speed of the windmill. /// - void update_windmill_speed(uint8_t speed); + void update_windmill_speed(uint8_t speed) const; /// @brief Set the color mix of the whole village. /// /// @param red Brightness of the red LEDs. /// @param green Brightness of the green LEDs. // - void set_village_color(uint8_t red, uint8_t green); + void set_village_color(uint8_t red, uint8_t green) const; /// @brief Set brightness of the power plant (only red LEDs) /// /// @param brightness Brightness of the power plant LEDs. /// - void set_power_plant(uint8_t brightness); + void set_power_plant(uint8_t brightness) const; /// @brief Set brightness of the solar plant LEDs. /// /// @param brightness Brightness of the solar power plant LEDs. /// - void set_solar_plant(uint8_t brightness); + void set_solar_plant(uint8_t brightness) const; /// @brief Set brightness of the windmill LEDs. /// /// @param brightness Brightness of the windmill LEDs. /// - void set_windmill_net(uint8_t brightness); + void set_windmill_net(uint8_t brightness) const; /// @brief Set the brightness of the combined renewable /// energy net. /// /// @param brightness Brightness off the LEDs. /// - void set_renewable_net(uint8_t brightness); + void set_renewable_net(uint8_t brightness) const; private: @@ -116,13 +116,13 @@ private: /// @param red Brightness of red LEDs. /// @param green Brightness of green LEDs. /// - void set_house_color(House number, uint8_t red, uint8_t green); + void set_house_color(House number, uint8_t red, uint8_t green) const; - void default_state_off(); - void default_state_fancy(); + void default_state_off() const; + void default_state_fancy() const; - void set_output_pin(Pin pin, uint8_t value); + void set_output_pin(Pin pin, uint8_t value) const; i2c::Node& m_modell; ///< I2C connection to the Arduino diff --git a/src/smart_grid/ModelState.hpp b/src/smart_grid/ModelState.hpp deleted file mode 100644 index 9a4bd8fc2cd75f317d3d2803f04f2a59377f2e85..0000000000000000000000000000000000000000 --- a/src/smart_grid/ModelState.hpp +++ /dev/null @@ -1,97 +0,0 @@ -/// @file ModelState.hpp -/// - -#ifndef MODEL_STATE_HPP -#define MODEL_STATE_HPP - -#include <cstdlib> -#include <ctime> - -#include "spdlog/spdlog.h" -#include "SmartGridModell.hpp" - -#include "ObserverPattern.hpp" -#include "StateLogger.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 StateLogger; -class ModelState : public Subject<StateLogger, ModelState> -{ -public: - ModelState(SmartGridModell &modell); - void next_hour(); - double excess_power(); - - PowerProduction getPowerProduction() const {return m_production;} - PowerUsage getPowerUsage() const {return m_usage;} - -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/smart_grid/ModelState.cpp b/src/smart_grid/SmartGridModel.cpp similarity index 65% rename from src/smart_grid/ModelState.cpp rename to src/smart_grid/SmartGridModel.cpp index 3bcd87c43575175a878fd3fb825acbe722171ab6..18f1295b7632bcd2930f3a4d6a88a1f9eb906891 100644 --- a/src/smart_grid/ModelState.cpp +++ b/src/smart_grid/SmartGridModel.cpp @@ -1,16 +1,17 @@ -/// @file ModelState.cpp +/// @file SmartGridModel.cpp /// -#include "ModelState.hpp" +#include "SmartGridModel.hpp" +#include "spdlog/spdlog.h" - -ModelState::ModelState(SmartGridModell &modell) +SmartGridModel::SmartGridModel(HardwareControl &modell) : m_modell(modell) { std::srand(std::time(nullptr)); + update_states(); } -void ModelState::update_wind() +void SmartGridModel::update_wind() { auto wind_by_sun = m_sun * 9.0 / 100.0; // wind by sun should be 5 max. auto random_wind = ((std::rand() * 1.0) / RAND_MAX) * 3.0; @@ -18,7 +19,7 @@ void ModelState::update_wind() } -void ModelState::update_modell() +void SmartGridModel::update_modell() { auto sun = m_sun * 3.5; auto wind = m_wind / 9.3; @@ -40,10 +41,10 @@ void ModelState::update_modell() } -void ModelState::update_power_production() +void SmartGridModel::update_power_production() { - m_production.renewable.solar = m_sun * solar_size; - m_production.renewable.wind = m_wind * windpark_size; + m_production.renewable.solar = m_sun * m_solar_size; + m_production.renewable.wind = m_wind * m_windpark_size; if (m_usage.sum() > m_production.clean()) { m_excess_power = 0.0; @@ -57,17 +58,18 @@ void ModelState::update_power_production() } -double ModelState::excess_power() +double SmartGridModel::calc_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; + m_excess_power = sum_prod - sum_used; + return m_excess_power; } -void ModelState::update_power_consumption() +void SmartGridModel::update_power_consumption() { - m_usage.village = village_consumption_at[m_time] * village_size; + m_usage.village = village_consumption_at[m_time] * m_village_size; if (m_producing == true) { m_usage.industry = MaxPower::industry; @@ -78,41 +80,52 @@ void ModelState::update_power_consumption() } } -void ModelState::update_time() +void SmartGridModel::next_hour() { if (m_time < 23) { - ++m_time; + set_time(m_time + 1); } else { - m_time = 0; + set_time(0); } + update_states(); } -void ModelState::update_sun() +void SmartGridModel::update_sun() { m_sun = sunnshine_percentage[m_time]; } -void ModelState::print_states() +void SmartGridModel::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()); + spdlog::debug("excess_power<{}>", calc_excess_power()); } -void ModelState::next_hour() +void SmartGridModel::update_states() { - update_time(); update_sun(); update_wind(); update_power_consumption(); update_power_production(); update_modell(); print_states(); - notify(*this); +} + +void SmartGridModel::set_time(int hour) +{ + if ((hour >= 0) & (hour <= 23)) + { + m_time = hour; + } + else + { + m_time = 0; + } } \ No newline at end of file diff --git a/src/smart_grid/SmartGridModel.hpp b/src/smart_grid/SmartGridModel.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b5940d5f5e40ba582d96690014ac7b641017790d --- /dev/null +++ b/src/smart_grid/SmartGridModel.hpp @@ -0,0 +1,121 @@ +/// @file SmartGridModel.hpp +/// + +#ifndef MODEL_STATE_HPP +#define MODEL_STATE_HPP + +#include <cstdlib> +#include <ctime> + +#include "HardwareControl.hpp" + +#include "ObserverPattern.hpp" +#include "StateLogger.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; } +}; + + +/// @brief Class representing the state of the Smart Grid Model and that has control over the hardware. +/// +class SmartGridModel +{ +public: + SmartGridModel(HardwareControl &modell); + + // update modle state with new values + void next_hour(); + + // calculate model states + void update_states(); + + + int get_time() const {return m_time;} // current time in hours [0-24] + double get_sun() const {return m_sun;} // sunshine in [%] + double get_wind() const {return m_wind;} // wind "speed" + double get_excess_power() const {return m_excess_power;} // "available" power + bool get_producing_state() const {return m_producing;} // returns if production is enabled + + double get_solar_park_size() const {return m_solar_size;} // "Size of the solar production plant" + double get_wind_park_size() const {return m_windpark_size;} // "Size" of the windpark + double get_village_size() const {return m_village_size;} // "Size of the village" + + PowerProduction get_power_production() const {return m_production;} + PowerUsage get_power_usage() const {return m_usage;} + + void set_time(const int hour); + void set_solar_size(const double solar_size) {if (solar_size >= 0) m_solar_size = solar_size;} + void set_windpark_size(const double windpark_size) {if (windpark_size >= 0) m_windpark_size = windpark_size;} + void set_village_size(const double village_size){ if(village_size >= 0) m_village_size = village_size;} + void set_producing_state(const bool produce){m_producing = produce;} + + +private: + void update_sun(); + void update_wind(); + void update_power_consumption(); + void update_power_production(); + void update_modell(); + void print_states(); + + double calc_excess_power(); + + double m_solar_size{10}; ///< scale for solar power production + double m_windpark_size{1.0}; ///< scale the power production by wind + double m_village_size{10}; ///< scale how much power is used by the village + + int m_time{0}; + double m_sun{0}; + double m_wind{0}; + double m_excess_power{0.0}; + bool m_producing{false}; + + PowerProduction m_production; + PowerUsage m_usage; + + HardwareControl &m_modell; +}; + +#endif \ No newline at end of file diff --git a/src/smart_grid/SmgStateMachine.hpp b/src/smart_grid/SmgStateMachine.hpp deleted file mode 100644 index 6dcb770b1159677629f3af0088bd5b9ea1754cdc..0000000000000000000000000000000000000000 --- a/src/smart_grid/SmgStateMachine.hpp +++ /dev/null @@ -1,40 +0,0 @@ -/// - -#ifndef SMG_STATE_MACHINE_HPP -#define SMG_STATE_MACHINE_HPP - -#include "ModelState.hpp" -#include "SmartGridModell.hpp" - -enum class MainState -{ - Simulation, - ManualControl -}; - -enum class SimSate -{ - Entry, - Do, - Exit -}; - -enum class ManualSate -{ - Entry, - Do, - Exit -}; - - -class SmgStateMachine -{ -public: - void run(); - -private: - SmartGridModell m_modell; - ModelState m_state; -}; - -#endif \ No newline at end of file diff --git a/src/smart_grid/StateLogger.cpp b/src/smart_grid/StateLogger.cpp deleted file mode 100644 index 2aee2ea8d4f259f84cb5406da2c02e8473ff330d..0000000000000000000000000000000000000000 --- a/src/smart_grid/StateLogger.cpp +++ /dev/null @@ -1,8 +0,0 @@ -/// - -#include "StateLogger.hpp" - -void StateLogger::update(const ModelState &model) -{ - spdlog::debug("Clean solar power: {}", model.getPowerProduction().renewable.solar); -} \ No newline at end of file diff --git a/src/smart_grid/StateLogger.hpp b/src/smart_grid/StateLogger.hpp deleted file mode 100644 index 96ded4065194c8ca621ca23f60e7c6ffd66ce811..0000000000000000000000000000000000000000 --- a/src/smart_grid/StateLogger.hpp +++ /dev/null @@ -1,16 +0,0 @@ -/// - -#ifndef STATE_LOGGER_HPP -#define STATE_LOGGER_HPP - -#include "ModelState.hpp" -#include "ObserverPattern.hpp" - -class ModelState; -class StateLogger : IObserver<ModelState> -{ -public: - virtual void update(const ModelState &state) override; -}; - -#endif \ No newline at end of file diff --git a/src/smg_server.cpp b/src/smg_server.cpp deleted file mode 100644 index 8c4cfcc71bdb1c0c0f5561c3508849bcf9d4f076..0000000000000000000000000000000000000000 --- a/src/smg_server.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/// @file main.cpp -/// -#include <thread> -#include "spdlog/spdlog.h" - -#include "i2c/Node.hpp" -#include "SmartGridModell.hpp" - -#include "Socket.hpp" -#include "Protocol.hpp" - -#include "ModelState.hpp" - -enum class MainState -{ - Simulation, - ManualControl -}; - -enum class SimState -{ - Entry, - Do, - Exit -}; - -bool sim_state_do(ModelState &day) -{ - bool exit_to_manual_control {false}; - - // check for new inputs - day.next_hour(); - - return exit_to_manual_control; -} - -void run_sim() -{ - i2c::Node i2c_device{0x14}; - if (false == i2c_device.open_device("/dev/i2c-1")) - { - exit(1); - } - else - { - spdlog::debug("Node created"); - } - - SmartGridModell modell{i2c_device}; - ModelState day{modell}; - auto log = std::make_shared<StateLogger>(); - day.attach(log); - - MainState main_state {MainState::Simulation}; - SimState sim_state {SimState::Entry}; - - bool active{true}; - while (active) - { - switch (main_state) - { - case MainState::Simulation: - switch (sim_state) - { - case SimState::Entry: - modell.put_modell_into_state(SmartGridModell::DefaultState::Off); - sim_state = SimState::Do; - break; - case SimState::Do: - // limit speed of simulation - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - if (sim_state_do(day) == true) - { - sim_state = SimState::Exit; - } - break; - case SimState::Exit: - modell.put_modell_into_state(SmartGridModell::DefaultState::Off); - sim_state = SimState::Entry; - main_state = MainState::ManualControl; - break; - } - break; - - case MainState::ManualControl: - - break; - } - } - - modell.put_modell_into_state(SmartGridModell::DefaultState::Off); -} - -void log_args(int argc, char **argv) -{ - for (int i = 0; i < argc; ++i) - { - spdlog::debug("[{}] {}", i, argv[i]); - } -} - -int main(int argc, char **argv) -{ - spdlog::set_level(spdlog::level::debug); - spdlog::info("Starting - Smart Grid Modell"); - log_args(argc, argv); - - run_sim(); - - spdlog::info("End"); - return 0; -}