From 422dc0728971d28e38bd63f735e9c2f4736f4cbd Mon Sep 17 00:00:00 2001 From: Lars Ivar Hatledal Date: Tue, 3 Dec 2024 09:50:30 +0100 Subject: [PATCH 1/4] refactor --- export/src/fmu4cpp/fmu_variable.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/export/src/fmu4cpp/fmu_variable.cpp b/export/src/fmu4cpp/fmu_variable.cpp index ece0dd9..fd3d791 100644 --- a/export/src/fmu4cpp/fmu_variable.cpp +++ b/export/src/fmu4cpp/fmu_variable.cpp @@ -21,8 +21,8 @@ namespace fmu4cpp { throw std::logic_error("Invalid causality encountered"); } - std::string to_string(const variability_t &c) { - switch (c) { + std::string to_string(const variability_t &v) { + switch (v) { case variability_t::CONTINUOUS: return "continuous"; case variability_t::CONSTANT: @@ -37,8 +37,8 @@ namespace fmu4cpp { throw std::logic_error("Invalid variability encountered"); } - std::string to_string(const initial_t &c) { - switch (c) { + std::string to_string(const initial_t &i) { + switch (i) { case initial_t::APPROX: return "approx"; case initial_t::EXACT: From 5cfa566423c353480d2c0130b57a3cab93e6f7e6 Mon Sep 17 00:00:00 2001 From: Lars Ivar Hatledal Date: Tue, 3 Dec 2024 12:00:12 +0100 Subject: [PATCH 2/4] Allow using pointer to variables (#20) --- README.md | 11 +- .../examples/BouncingBall/bouncing_ball.cpp | 11 +- export/examples/Resource/resource.cpp | 8 +- .../SimplePendulum/simple_pendulum.cpp | 18 +- export/include/fmu4cpp/fmu_base.hpp | 8 +- export/include/fmu4cpp/fmu_variable.hpp | 48 +++- export/include/fmu4cpp/variable_access.hpp | 62 +++++ export/src/fmu4cpp/fmu_base.cpp | 16 ++ export/tests/CMakeLists.txt | 1 + export/tests/basic_test.cpp | 12 +- export/tests/identity_test.cpp | 223 ++++++++++++++++++ export/tests/test_resource.cpp | 10 +- src/model.cpp | 19 +- 13 files changed, 387 insertions(+), 60 deletions(-) create mode 100644 export/include/fmu4cpp/variable_access.hpp create mode 100644 export/tests/identity_test.cpp diff --git a/README.md b/README.md index 712b18c..00766e5 100644 --- a/README.md +++ b/README.md @@ -30,28 +30,25 @@ public: register_variable( real( - "height", [this] { return height; }) + "height", &height) .setCausality(causality_t::OUTPUT) .setVariability(variability_t::CONTINUOUS)); register_variable( real( - "velocity", [this] { return velocity; }) + "velocity", &velocity) .setCausality(causality_t::LOCAL) .setVariability(variability_t::CONTINUOUS)); register_variable( real( - "gravity", [this] { return gravity; }, - [this](const auto &input) { gravity = input; }) + "gravity", &gravity) .setCausality(causality_t::PARAMETER) .setVariability(variability_t::FIXED)); register_variable( real( - "bounceFactor", - [this] { return bounceFactor; }, - [this](const auto &input) { bounceFactor = input; }) + "bounceFactor", &bounceFactor) .setCausality(causality_t::PARAMETER) .setVariability(variability_t::FIXED)); diff --git a/export/examples/BouncingBall/bouncing_ball.cpp b/export/examples/BouncingBall/bouncing_ball.cpp index ceec26d..7fd5ebc 100644 --- a/export/examples/BouncingBall/bouncing_ball.cpp +++ b/export/examples/BouncingBall/bouncing_ball.cpp @@ -12,29 +12,26 @@ class BouncingBall : public fmu_base { register_variable( real( - "height", [this] { return height_; }) + "height", &height_) .setCausality(causality_t::OUTPUT) .setVariability(variability_t::CONTINUOUS) .setInitial(initial_t::EXACT)); register_variable( real( - "velocity", [this] { return velocity_; }) + "velocity", &velocity_) .setCausality(causality_t::LOCAL) .setVariability(variability_t::CONTINUOUS)); register_variable( real( - "gravity", [this] { return gravity_; }, - [this](const auto &input) { gravity_ = input; }) + "gravity", &gravity_) .setCausality(causality_t::PARAMETER) .setVariability(variability_t::FIXED)); register_variable( real( - "bounceFactor", - [this] { return bounceFactor_; }, - [this](const auto &input) { bounceFactor_ = input; }) + "bounceFactor", &bounceFactor_) .setCausality(causality_t::PARAMETER) .setVariability(variability_t::FIXED)); diff --git a/export/examples/Resource/resource.cpp b/export/examples/Resource/resource.cpp index 35f6604..10aec25 100644 --- a/export/examples/Resource/resource.cpp +++ b/export/examples/Resource/resource.cpp @@ -15,12 +15,11 @@ class Resource : public fmu_base { std::ifstream ifs(resources / "file.txt"); - std::string line; - std::getline(ifs, line); + std::getline(ifs, content_); register_variable( string( - "content", [line] { return line; }) + "content", &content_) .setVariability(variability_t::CONSTANT) .setCausality(causality_t::OUTPUT)); @@ -37,6 +36,9 @@ class Resource : public fmu_base { // do nothing } +private: + std::string content_; + }; model_info fmu4cpp::get_model_info() { diff --git a/export/examples/SimplePendulum/simple_pendulum.cpp b/export/examples/SimplePendulum/simple_pendulum.cpp index e55cbcd..659bfca 100644 --- a/export/examples/SimplePendulum/simple_pendulum.cpp +++ b/export/examples/SimplePendulum/simple_pendulum.cpp @@ -12,31 +12,23 @@ class SimplePendulum : public fmu_base { SimplePendulum(const std::string &instanceName, const std::filesystem::path &resources) : fmu_base(instanceName, resources) { - register_variable(real("angle", - [this] { return angle_; }) + register_variable(real("angle", &angle_) .setCausality(causality_t::OUTPUT) .setVariability(variability_t::CONTINUOUS)); - register_variable(real("angularVelocity", - [this] { return angularVelocity_; }) + register_variable(real("angularVelocity", &angularVelocity_) .setCausality(causality_t::LOCAL) .setVariability(variability_t::CONTINUOUS)); register_variable(real( - "gravity", - [this] { return gravity_; }, - [this](double input) { gravity_ = input; }) + "gravity", &gravity_) .setCausality(causality_t::PARAMETER) .setVariability(variability_t::FIXED)); register_variable(real( - "length", - [this] { return length_; }, - [this](double input) { length_ = input; }) + "length", &length_) .setCausality(causality_t::PARAMETER) .setVariability(variability_t::FIXED)); register_variable(real( - "damping", - [this] { return damping_; }, - [this](double input) { damping_ = input; }) + "damping", &damping_) .setCausality(causality_t::PARAMETER) .setVariability(variability_t::FIXED)); diff --git a/export/include/fmu4cpp/fmu_base.hpp b/export/include/fmu4cpp/fmu_base.hpp index 438fcb3..2d880be 100644 --- a/export/include/fmu4cpp/fmu_base.hpp +++ b/export/include/fmu4cpp/fmu_base.hpp @@ -166,18 +166,22 @@ namespace fmu4cpp { virtual ~fmu_base() = default; protected: + IntVariable integer(const std::string &name, int *ptr); IntVariable integer(const std::string &name, const std::function &getter, const std::optional> &setter = std::nullopt); + RealVariable real(const std::string &name, double *ptr); RealVariable real(const std::string &name, const std::function &getter, const std::optional> &setter = std::nullopt); + BoolVariable boolean(const std::string &name, bool *ptr); BoolVariable boolean(const std::string &name, - const std::function &getter, - const std::optional> &setter = std::nullopt); + const std::function &getter, + const std::optional> &setter); + StringVariable string(const std::string &name, std::string *ptr); StringVariable string(const std::string &name, const std::function &getter, const std::optional> &setter = std::nullopt); diff --git a/export/include/fmu4cpp/fmu_variable.hpp b/export/include/fmu4cpp/fmu_variable.hpp index b5b1f79..a87f6fe 100644 --- a/export/include/fmu4cpp/fmu_variable.hpp +++ b/export/include/fmu4cpp/fmu_variable.hpp @@ -2,7 +2,9 @@ #ifndef FMU4CPP_FMU_VARIABLE_HPP #define FMU4CPP_FMU_VARIABLE_HPP -#include +#include "variable_access.hpp" + +#include #include #include #include @@ -37,6 +39,7 @@ namespace fmu4cpp { std::string to_string(const variability_t &v); std::string to_string(const initial_t &i); + class VariableBase { protected: @@ -93,19 +96,30 @@ namespace fmu4cpp { class Variable : public VariableBase { public: + Variable( + const std::string &name, + unsigned int vr, size_t index, T *ptr) + : VariableBase(name, vr, index), access_(std::make_unique>(ptr)) {} + Variable( const std::string &name, unsigned int vr, size_t index, std::function getter, std::optional> setter) - : VariableBase(name, vr, index), getter_(std::move(getter)), setter_(std::move(setter)) {} + : VariableBase(name, vr, index), + access_(std::make_unique>(std::move(getter), std::move(setter))) {} [[nodiscard]] T get() const { - return getter_(); + + return access_->get(); } void set(T value) { - if (setter_) setter_->operator()(value); + if (causality_ == causality_t::LOCAL || causality_ == causality_t::OUTPUT) { + throw std::logic_error("Cannot set value for variable with causality: " + to_string(causality_)); + } + + access_->set(value); } V &setCausality(causality_t causality) { @@ -131,13 +145,17 @@ namespace fmu4cpp { } private: - std::function getter_; - std::optional> setter_; + std::shared_ptr> access_; }; class IntVariable final : public Variable { public: + IntVariable( + const std::string &name, + unsigned int vr, size_t index, int *ptr) + : Variable(name, vr, index, ptr) {} + IntVariable( const std::string &name, unsigned int vr, size_t index, @@ -171,6 +189,14 @@ namespace fmu4cpp { class RealVariable final : public Variable { public: + RealVariable( + const std::string &name, + unsigned int vr, size_t index, double *ptr) + : Variable(name, vr, index, ptr) { + + variability_ = variability_t::CONTINUOUS; + } + RealVariable( const std::string &name, unsigned int vr, size_t index, @@ -207,6 +233,11 @@ namespace fmu4cpp { class BoolVariable final : public Variable { public: + BoolVariable( + const std::string &name, + unsigned int vr, size_t index, bool *ptr) + : Variable(name, vr, index, ptr) {} + BoolVariable( const std::string &name, unsigned int vr, size_t index, @@ -218,6 +249,11 @@ namespace fmu4cpp { class StringVariable final : public Variable { public: + StringVariable( + const std::string &name, + unsigned int vr, size_t index, std::string *ptr) + : Variable(name, vr, index, ptr) {} + StringVariable( const std::string &name, unsigned int vr, size_t index, diff --git a/export/include/fmu4cpp/variable_access.hpp b/export/include/fmu4cpp/variable_access.hpp new file mode 100644 index 0000000..26d520e --- /dev/null +++ b/export/include/fmu4cpp/variable_access.hpp @@ -0,0 +1,62 @@ + +#ifndef VARIABLEACCESS_HPP +#define VARIABLEACCESS_HPP + +#include +#include +#include + +namespace fmu4cpp { + + template + struct VariableAccess { + virtual T get() = 0; + virtual void set(T value) = 0; + + virtual ~VariableAccess() = default; + }; + + template + class PtrAccess final : public VariableAccess { + + public: + explicit PtrAccess(T *ptr) : ptr_(ptr) {} + + T get() override { + return *ptr_; + } + + void set(T value) override { + *ptr_ = value; + } + + private: + T *ptr_; + }; + + template + class LambdaAccess final : public VariableAccess { + + public: + LambdaAccess(std::function getter, std::optional> setter) + : getter_(std::move(getter)), + setter_(std::move(setter)) {} + + T get() override { + return getter_(); + } + + void set(T value) override { + if (setter_) { + setter_->operator()(value); + } + } + + private: + std::function getter_; + std::optional> setter_; + }; + +}// namespace fmu4cpp + +#endif//VARIABLEACCESS_HPP diff --git a/export/src/fmu4cpp/fmu_base.cpp b/export/src/fmu4cpp/fmu_base.cpp index 186c13c..412a97a 100644 --- a/export/src/fmu4cpp/fmu_base.cpp +++ b/export/src/fmu4cpp/fmu_base.cpp @@ -195,18 +195,34 @@ namespace fmu4cpp { return ss.str(); } + IntVariable fmu_base::integer(const std::string &name, int *ptr) { + return {name, static_cast(integers_.size()), numVariables_++, ptr}; + } + IntVariable fmu_base::integer(const std::string &name, const std::function &getter, const std::optional> &setter) { return {name, static_cast(integers_.size()), numVariables_++, getter, setter}; } + RealVariable fmu_base::real(const std::string &name, double *ptr) { + return {name, static_cast(reals_.size()), numVariables_++, ptr}; + } + RealVariable fmu_base::real(const std::string &name, const std::function &getter, const std::optional> &setter) { return {name, static_cast(reals_.size()), numVariables_++, getter, setter}; } + BoolVariable fmu_base::boolean(const std::string &name, bool *ptr) { + return {name, static_cast(booleans_.size()), numVariables_++, ptr}; + } + BoolVariable fmu_base::boolean(const std::string &name, const std::function &getter, const std::optional> &setter) { return {name, static_cast(booleans_.size()), numVariables_++, getter, setter}; } + StringVariable fmu_base::string(const std::string &name, std::string *ptr) { + return {name, static_cast(strings_.size()), numVariables_++, ptr}; + } + StringVariable fmu_base::string(const std::string &name, const std::function &getter, const std::optional> &setter) { return {name, static_cast(strings_.size()), numVariables_++, getter, setter}; } diff --git a/export/tests/CMakeLists.txt b/export/tests/CMakeLists.txt index 7d1944b..beb80f9 100644 --- a/export/tests/CMakeLists.txt +++ b/export/tests/CMakeLists.txt @@ -17,4 +17,5 @@ function(make_test modelIdentifier sources) endfunction() make_test(basic_test basic_test.cpp) +make_test(identity_test identity_test.cpp) make_test(test_resource test_resource.cpp) diff --git a/export/tests/basic_test.cpp b/export/tests/basic_test.cpp index 3f77e14..8537c42 100644 --- a/export/tests/basic_test.cpp +++ b/export/tests/basic_test.cpp @@ -10,13 +10,13 @@ class Model : public fmu4cpp::fmu_base { Model(const std::string &instanceName, const std::filesystem::path &resources) : fmu_base(instanceName, resources) { - register_variable(real("myReal", [this] { return real_; }) + register_variable(real("myReal", &real_) .setCausality(fmu4cpp::causality_t::OUTPUT)); - register_variable(integer("myInteger", [this] { return integer_; }) + register_variable(integer("myInteger", &integer_) .setCausality(fmu4cpp::causality_t::OUTPUT)); - register_variable(boolean("myBoolean", [this] { return boolean_; }) + register_variable(boolean("myBoolean", &boolean_) .setCausality(fmu4cpp::causality_t::OUTPUT)); - register_variable(string("myString", [this] { return str_; }) + register_variable(string("myString", &str_) .setCausality(fmu4cpp::causality_t::OUTPUT)); Model::reset(); @@ -74,6 +74,10 @@ TEST_CASE("basic_test") { instance->enter_initialisation_mode(); instance->exit_initialisation_mode(); + unsigned int vr = boolean->value_reference(); + int testFail = false; + REQUIRE_THROWS(instance->set_boolean(&vr, 1, &testFail)); + int i = 0; while (t < 10) { instance->do_step(t, dt); diff --git a/export/tests/identity_test.cpp b/export/tests/identity_test.cpp new file mode 100644 index 0000000..8ea34f7 --- /dev/null +++ b/export/tests/identity_test.cpp @@ -0,0 +1,223 @@ + +#include "catch2/matchers/catch_matchers_vector.hpp" +#include "fmi2/fmi2Functions.h" + + +#include +#include +#include + +#include + +class Model : public fmu4cpp::fmu_base { + +public: + Model(const std::string &instanceName, const std::filesystem::path &resources) + : fmu_base(instanceName, resources) { + + register_variable(integer("integerIn", &integer_) + .setCausality(fmu4cpp::causality_t::INPUT) + .setVariability(fmu4cpp::variability_t::DISCRETE) + .setInitial(fmu4cpp::initial_t::EXACT)); + + register_variable(real("realIn", &real_) + .setCausality(fmu4cpp::causality_t::INPUT) + .setVariability(fmu4cpp::variability_t::DISCRETE) + .setInitial(fmu4cpp::initial_t::EXACT)); + + register_variable(boolean("booleanIn", &boolean_) + .setCausality(fmu4cpp::causality_t::INPUT) + .setVariability(fmu4cpp::variability_t::DISCRETE) + .setInitial(fmu4cpp::initial_t::EXACT)); + + register_variable(string("stringIn", &string_) + .setCausality(fmu4cpp::causality_t::INPUT) + .setVariability(fmu4cpp::variability_t::DISCRETE) + .setInitial(fmu4cpp::initial_t::EXACT)); + + + register_variable(integer("integerOut", &integer_) + .setCausality(fmu4cpp::causality_t::OUTPUT) + .setVariability(fmu4cpp::variability_t::DISCRETE) + .setInitial(fmu4cpp::initial_t::CALCULATED) + .setDependencies({get_int_variable("integerIn")->index()})); + + register_variable(real("realOut", &real_) + .setCausality(fmu4cpp::causality_t::OUTPUT) + .setVariability(fmu4cpp::variability_t::DISCRETE) + .setInitial(fmu4cpp::initial_t::CALCULATED) + .setDependencies({get_real_variable("realIn")->index()})); + + register_variable(boolean("booleanOut", &boolean_) + .setCausality(fmu4cpp::causality_t::OUTPUT) + .setVariability(fmu4cpp::variability_t::DISCRETE) + .setInitial(fmu4cpp::initial_t::CALCULATED) + .setDependencies({get_bool_variable("booleanIn")->index()})); + + register_variable(string("stringOut", [this] { return string_; }) + .setCausality(fmu4cpp::causality_t::OUTPUT) + .setVariability(fmu4cpp::variability_t::DISCRETE) + .setInitial(fmu4cpp::initial_t::CALCULATED) + .setDependencies({get_string_variable("stringIn")->index()})); + + Model::reset(); + } + + bool do_step(double currentTime, double dt) override { + return true; + } + + void reset() override { + integer_ = 0; + real_ = 0; + boolean_ = false; + string_ = "empty"; + } + +private: + int integer_; + double real_; + bool boolean_; + std::string string_; +}; + +fmu4cpp::model_info fmu4cpp::get_model_info() { + model_info info; + info.modelName = "Identity"; + info.description = "A simple feed-trough model"; + info.modelIdentifier = FMU4CPP_MODEL_IDENTIFIER; + return info; +} + +std::unique_ptr fmu4cpp::createInstance(const std::string &instanceName, + const std::filesystem::path &fmuResourceLocation) { + return std::make_unique(instanceName, fmuResourceLocation); +} + +int readInt(fmi2Component c) { + fmi2ValueReference ref = 1; + fmi2Integer value; + REQUIRE(fmi2GetInteger(c, &ref, 1, &value) == fmi2OK); + + return value; +} + +void setInt(fmi2Component c, int value) { + fmi2ValueReference ref = 0; + REQUIRE(fmi2SetInteger(c, &ref, 1, &value) == fmi2OK); +} + +double readReal(fmi2Component c) { + fmi2ValueReference ref = 1; + fmi2Real value; + REQUIRE(fmi2GetReal(c, &ref, 1, &value) == fmi2OK); + + return value; +} + +void setReal(fmi2Component c, double value) { + fmi2ValueReference ref = 0; + REQUIRE(fmi2SetReal(c, &ref, 1, &value) == fmi2OK); +} + +bool readBool(fmi2Component c) { + fmi2ValueReference ref = 1; + fmi2Boolean value; + REQUIRE(fmi2GetBoolean(c, &ref, 1, &value) == fmi2OK); + + return value; +} + +void setBool(fmi2Component c, bool value) { + fmi2ValueReference ref = 0; + fmi2Boolean value_ = value; + REQUIRE(fmi2SetBoolean(c, &ref, 1, &value_) == fmi2OK); +} + + +std::string readString(fmi2Component c) { + fmi2ValueReference ref = 1; + fmi2String value; + REQUIRE(fmi2GetString(c, &ref, 1, &value) == fmi2OK); + + return value; +} + +void setString(fmi2Component c, const std::string& value) { + fmi2ValueReference ref = 0; + fmi2String value_ = value.c_str(); + REQUIRE(fmi2SetString(c, &ref, 1, &value_) == fmi2OK); +} + +void setOutput(fmi2Component c) { + fmi2ValueReference ref = 1; + fmi2Integer i = 0; + fmi2String s = ""; + fmi2Real r = 0; + fmi2Boolean b= fmi2False; + + REQUIRE(fmi2SetInteger(c, &ref, 1, &i) == fmi2Error); + REQUIRE(fmi2SetReal(c, &ref, 1, &r) == fmi2Error); + REQUIRE(fmi2SetString(c, &ref, 1, &s) == fmi2Error); + REQUIRE(fmi2SetBoolean(c, &ref, 1, &b) == fmi2Error); +} + +void fmilogger(fmi2Component, fmi2String instanceName, fmi2Status status, fmi2String category, fmi2String message, ...) +{ + va_list args; + va_start(args, message); + char msgstr[1024]; + sprintf(msgstr, "%i: [%s] %s\n", status, instanceName, message); + printf(msgstr, args); + va_end(args); +} + +TEST_CASE("test_identity") { + + Model model("", ""); + const auto guid = model.guid(); + + fmi2CallbackFunctions callbackFunctions; + callbackFunctions.logger = &fmilogger; + + auto c = fmi2Instantiate("identity", fmi2CoSimulation, guid.c_str(), "", &callbackFunctions, false, true); + REQUIRE(c); + + REQUIRE(fmi2EnterInitializationMode(c) == fmi2OK); + REQUIRE(fmi2ExitInitializationMode(c) == fmi2OK); + REQUIRE(fmi2SetupExperiment(c, false, 0, 0, false, 0) == fmi2OK); + + REQUIRE(readReal(c) == Catch::Approx(0)); + REQUIRE(readString(c) == "empty"); + REQUIRE(readInt(c) == 0); + REQUIRE(readBool(c) == false); + + + double t{0}; + double dt{0.1}; + + bool b{false}; + int counter{0}; + while (t < 1) { + + setInt(c, counter); + setReal(c, t); + setString(c, std::to_string(t)); + setBool(c, b); + REQUIRE(fmi2DoStep(c, t, dt, true) == fmi2OK); + REQUIRE(readReal(c) == Catch::Approx(t)); + REQUIRE(readString(c) == std::to_string(t)); + REQUIRE(readInt(c) == counter); + REQUIRE(readBool(c) == b); + + t += dt; + counter++; + b = !b; + } + + setOutput(c); + + REQUIRE(fmi2Terminate(c) == fmi2OK); + + fmi2FreeInstance(c); +} diff --git a/export/tests/test_resource.cpp b/export/tests/test_resource.cpp index 9ffb510..632a42a 100644 --- a/export/tests/test_resource.cpp +++ b/export/tests/test_resource.cpp @@ -13,10 +13,9 @@ class Model : public fmu4cpp::fmu_base { std::ifstream file(resources.string() + "/data.txt"); - std::string data; - std::getline(file, data); + std::getline(file, data_); - register_variable(string("data", [data] { return data; }) + register_variable(string("data", &data_) .setCausality(fmu4cpp::causality_t::OUTPUT)); Model::reset(); @@ -32,10 +31,7 @@ class Model : public fmu4cpp::fmu_base { } private: - bool boolean_; - int integer_; - double real_; - std::string str_; + std::string data_; }; fmu4cpp::model_info fmu4cpp::get_model_info() { diff --git a/src/model.cpp b/src/model.cpp index 63cede4..e956e08 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -15,51 +15,48 @@ class Model : public fmu_base { : fmu_base(instanceName, resources) { register_variable(integer( - "integerIn", [this] { return integer_; }, - [this](int value) { integer_ = value; }) + "integerIn", &integer_) .setCausality(causality_t::INPUT) .setVariability(variability_t::DISCRETE) .setInitial(initial_t::EXACT)); register_variable( real( - "realIn", [this] { return real_; }, [this](double value) { real_ = value; }) + "realIn", &real_) .setCausality(causality_t::INPUT) .setVariability(variability_t::DISCRETE) .setInitial(initial_t::EXACT)); register_variable(boolean( - "booleanIn", [this] { return boolean_; }, - [this](bool value) { boolean_ = value; }) + "booleanIn", &boolean_) .setCausality(causality_t::INPUT) .setVariability(variability_t::DISCRETE) .setInitial(initial_t::EXACT)); register_variable(string( - "stringIn", [this] { return string_; }, - [this](std::string value) { string_ = std::move(value); }) + "stringIn", &string_) .setCausality(causality_t::INPUT) .setVariability(variability_t::DISCRETE) .setInitial(initial_t::EXACT)); - register_variable(integer("integerOut", [this] { return integer_; }) + register_variable(integer("integerOut", &integer_) .setCausality(causality_t::OUTPUT) .setVariability(variability_t::DISCRETE) .setInitial(initial_t::CALCULATED) .setDependencies({get_int_variable("integerIn")->index()})); - register_variable(real("realOut", [this] { return real_; }) + register_variable(real("realOut", &real_) .setCausality(causality_t::OUTPUT) .setVariability(variability_t::DISCRETE) .setInitial(initial_t::CALCULATED) .setDependencies({get_real_variable("realIn")->index()})); - register_variable(boolean("booleanOut", [this] { return boolean_; }) + register_variable(boolean("booleanOut", &boolean_) .setCausality(causality_t::OUTPUT) .setVariability(variability_t::DISCRETE) .setInitial(initial_t::CALCULATED) .setDependencies({get_bool_variable("booleanIn")->index()})); - register_variable(string("stringOut", [this] { return string_; }) + register_variable(string("stringOut", &string_) .setCausality(causality_t::OUTPUT) .setVariability(variability_t::DISCRETE) .setInitial(initial_t::CALCULATED) From 4edacd7363272e7c267e87492f32c3a778c6e4e2 Mon Sep 17 00:00:00 2001 From: Lars Ivar Hatledal Date: Tue, 3 Dec 2024 12:03:34 +0100 Subject: [PATCH 3/4] cleanup --- export/include/fmu4cpp/variable_access.hpp | 6 +-- export/tests/basic_test.cpp | 44 +--------------------- export/tests/identity_test.cpp | 2 +- export/tests/test_resource.cpp | 1 - 4 files changed, 5 insertions(+), 48 deletions(-) diff --git a/export/include/fmu4cpp/variable_access.hpp b/export/include/fmu4cpp/variable_access.hpp index 26d520e..401dc1d 100644 --- a/export/include/fmu4cpp/variable_access.hpp +++ b/export/include/fmu4cpp/variable_access.hpp @@ -1,6 +1,6 @@ -#ifndef VARIABLEACCESS_HPP -#define VARIABLEACCESS_HPP +#ifndef FMU4CPP_VARIABLEACCESS_HPP +#define FMU4CPP_VARIABLEACCESS_HPP #include #include @@ -59,4 +59,4 @@ namespace fmu4cpp { }// namespace fmu4cpp -#endif//VARIABLEACCESS_HPP +#endif//FMU4CPP_VARIABLEACCESS_HPP diff --git a/export/tests/basic_test.cpp b/export/tests/basic_test.cpp index 8537c42..f0e074c 100644 --- a/export/tests/basic_test.cpp +++ b/export/tests/basic_test.cpp @@ -59,7 +59,7 @@ TEST_CASE("basic_test") { const auto instance = fmu4cpp::createInstance("", ""); double t = 0; - double dt = 0.1; + const double dt = 0.1; auto real = instance->get_real_variable("myReal"); REQUIRE(real); @@ -99,45 +99,3 @@ TEST_CASE("basic_test") { instance->terminate(); } - -TEST_CASE("wrong call order") { - - const auto instance = fmu4cpp::createInstance("", ""); - - double t = 0; - double dt = 0.1; - - auto real = instance->get_real_variable("myReal"); - REQUIRE(real); - auto integer = instance->get_int_variable("myInteger"); - REQUIRE(integer); - auto boolean = instance->get_bool_variable("myBoolean"); - REQUIRE(boolean); - auto str = instance->get_string_variable("myString"); - REQUIRE(str); - - instance->setup_experiment(t, {}, {}); - instance->enter_initialisation_mode(); - instance->exit_initialisation_mode(); - - int i = 0; - while (t < 10) { - instance->do_step(t, dt); - - REQUIRE(real->get() == Catch::Approx(t)); - REQUIRE(boolean->get() == (i % 2 == 0)); - REQUIRE(integer->get() == ++i); - REQUIRE(str->get() == std::to_string(i)); - - t += dt; - } - - instance->reset(); - - REQUIRE(real->get() == 0); - REQUIRE(boolean->get() == false); - REQUIRE(integer->get() == 0); - REQUIRE(str->get() == "0"); - - instance->terminate(); -} diff --git a/export/tests/identity_test.cpp b/export/tests/identity_test.cpp index 8ea34f7..d31b71d 100644 --- a/export/tests/identity_test.cpp +++ b/export/tests/identity_test.cpp @@ -194,7 +194,7 @@ TEST_CASE("test_identity") { double t{0}; - double dt{0.1}; + const double dt{0.1}; bool b{false}; int counter{0}; diff --git a/export/tests/test_resource.cpp b/export/tests/test_resource.cpp index 632a42a..be0103b 100644 --- a/export/tests/test_resource.cpp +++ b/export/tests/test_resource.cpp @@ -1,6 +1,5 @@ #include -#include #include #include From 50c061286d75c9ad0e8bead779ca812ce655ad9e Mon Sep 17 00:00:00 2001 From: Lars Ivar Hatledal Date: Tue, 3 Dec 2024 12:29:29 +0100 Subject: [PATCH 4/4] add array test --- export/tests/CMakeLists.txt | 1 + export/tests/array_test.cpp | 99 +++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 export/tests/array_test.cpp diff --git a/export/tests/CMakeLists.txt b/export/tests/CMakeLists.txt index beb80f9..eadf554 100644 --- a/export/tests/CMakeLists.txt +++ b/export/tests/CMakeLists.txt @@ -16,6 +16,7 @@ function(make_test modelIdentifier sources) endfunction() +make_test(array_test array_test.cpp) make_test(basic_test basic_test.cpp) make_test(identity_test identity_test.cpp) make_test(test_resource test_resource.cpp) diff --git a/export/tests/array_test.cpp b/export/tests/array_test.cpp new file mode 100644 index 0000000..73cb08f --- /dev/null +++ b/export/tests/array_test.cpp @@ -0,0 +1,99 @@ + +#include "catch2/matchers/catch_matchers_vector.hpp" +#include "fmi2/fmi2Functions.h" + + +#include +#include +#include +#include + +#include + +class Model : public fmu4cpp::fmu_base { + +public: + Model(const std::string &instanceName, const std::filesystem::path &resources) + : fmu_base(instanceName, resources), reals_(4) { + + for (int i = 0; i < reals_.size(); i++) { + register_variable( + real("reals" + std::to_string(i), [this, i] { return reals_[i]; }, [this, i](double val) { reals_[i] = val; }).setCausality(fmu4cpp::causality_t::PARAMETER).setVariability(fmu4cpp::variability_t::TUNABLE)); + } + + Model::reset(); + } + + bool do_step(double currentTime, double dt) override { + return true; + } + + void reset() override { + reals_.assign({1, 2, 3, 4}); + } + +private: + std::vector reals_; +}; + +fmu4cpp::model_info fmu4cpp::get_model_info() { + model_info info; + info.modelName = "Array"; + info.description = "A simple model with arrays"; + info.modelIdentifier = FMU4CPP_MODEL_IDENTIFIER; + return info; +} + +std::unique_ptr fmu4cpp::createInstance(const std::string &instanceName, + const std::filesystem::path &fmuResourceLocation) { + return std::make_unique(instanceName, fmuResourceLocation); +} + +void fmilogger(fmi2Component, fmi2String instanceName, fmi2Status status, fmi2String category, fmi2String message, ...) { + va_list args; + va_start(args, message); + char msgstr[1024]; + sprintf(msgstr, "%i: [%s] %s\n", status, instanceName, message); + printf(msgstr, args); + va_end(args); +} + +TEST_CASE("test_array") { + + Model model("", ""); + const auto guid = model.guid(); + + fmi2CallbackFunctions callbackFunctions; + callbackFunctions.logger = &fmilogger; + + auto c = fmi2Instantiate("array", fmi2CoSimulation, guid.c_str(), "", &callbackFunctions, false, true); + REQUIRE(c); + + REQUIRE(fmi2EnterInitializationMode(c) == fmi2OK); + REQUIRE(fmi2ExitInitializationMode(c) == fmi2OK); + REQUIRE(fmi2SetupExperiment(c, false, 0, 0, false, 0) == fmi2OK); + + std::vector values(4); + for (int i = 0; i < 4; i++) { + fmi2ValueReference ref = i; + fmi2GetReal(c, &ref, 1, &values[i]); + } + REQUIRE(values == std::vector{1, 2, 3, 4}); + + for (int i = 0; i < 4; i++) { + fmi2ValueReference ref = i; + fmi2Real val = 9; + fmi2SetReal(c, &ref, 1, &val); + } + + for (int i = 0; i < 4; i++) { + fmi2ValueReference ref = i; + fmi2GetReal(c, &ref, 1, &values[i]); + } + REQUIRE(values == std::vector{9, 9, 9, 9}); + + + REQUIRE(fmi2Terminate(c) == fmi2OK); + + fmi2FreeInstance(c); +}