From 8746355c32f48a6f8bc73eb109c8555023f4b3ae Mon Sep 17 00:00:00 2001 From: Lars Ivar Hatledal Date: Tue, 3 Dec 2024 10:58:06 +0100 Subject: [PATCH] use ptr rather than lambda --- 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 | 16 +- export/include/fmu4cpp/fmu_variable.hpp | 42 ++-- export/src/fmu4cpp/fmu_base.cpp | 16 +- export/tests/CMakeLists.txt | 1 + export/tests/basic_test.cpp | 12 +- export/tests/identity_test.cpp | 224 ++++++++++++++++++ export/tests/test_resource.cpp | 10 +- src/model.cpp | 19 +- 12 files changed, 291 insertions(+), 97 deletions(-) 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..19f25fb 100644 --- a/export/include/fmu4cpp/fmu_base.hpp +++ b/export/include/fmu4cpp/fmu_base.hpp @@ -166,21 +166,13 @@ namespace fmu4cpp { virtual ~fmu_base() = default; protected: - IntVariable integer(const std::string &name, - const std::function &getter, - const std::optional> &setter = std::nullopt); + IntVariable integer(const std::string &name, int *ptr); - RealVariable real(const std::string &name, - const std::function &getter, - const std::optional> &setter = std::nullopt); + RealVariable real(const std::string &name, double *ptr); - BoolVariable boolean(const std::string &name, - const std::function &getter, - const std::optional> &setter = std::nullopt); + BoolVariable boolean(const std::string &name, bool *ptr); - StringVariable string(const std::string &name, - const std::function &getter, - const std::optional> &setter = std::nullopt); + StringVariable string(const std::string &name, std::string *ptr); void register_variable(IntVariable v); void register_variable(RealVariable v); diff --git a/export/include/fmu4cpp/fmu_variable.hpp b/export/include/fmu4cpp/fmu_variable.hpp index b5b1f79..b74567b 100644 --- a/export/include/fmu4cpp/fmu_variable.hpp +++ b/export/include/fmu4cpp/fmu_variable.hpp @@ -2,7 +2,6 @@ #ifndef FMU4CPP_FMU_VARIABLE_HPP #define FMU4CPP_FMU_VARIABLE_HPP -#include #include #include #include @@ -95,17 +94,19 @@ namespace fmu4cpp { public: 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)) {} + unsigned int vr, size_t index, T *ptr) + : VariableBase(name, vr, index), ptr_(ptr) {} [[nodiscard]] T get() const { - return getter_(); + return *ptr_; } 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_)); + } + + *ptr_ = value; } V &setCausality(causality_t causality) { @@ -131,8 +132,7 @@ namespace fmu4cpp { } private: - std::function getter_; - std::optional> setter_; + T *ptr_; }; class IntVariable final : public Variable { @@ -140,10 +140,8 @@ namespace fmu4cpp { public: IntVariable( const std::string &name, - unsigned int vr, size_t index, - const std::function &getter, - const std::optional> &setter) - : Variable(name, vr, index, getter, setter) {} + unsigned int vr, size_t index, int *ptr) + : Variable(name, vr, index, ptr) {} [[nodiscard]] std::optional getMin() const { return min_; @@ -173,10 +171,8 @@ namespace fmu4cpp { public: RealVariable( const std::string &name, - unsigned int vr, size_t index, - const std::function &getter, - const std::optional> &setter) - : Variable(name, vr, index, getter, setter) { + unsigned int vr, size_t index, double *ptr) + : Variable(name, vr, index, ptr) { variability_ = variability_t::CONTINUOUS; } @@ -209,10 +205,8 @@ namespace fmu4cpp { public: BoolVariable( const std::string &name, - unsigned int vr, size_t index, - const std::function &getter, - const std::optional> &setter) - : Variable(name, vr, index, getter, setter) {} + unsigned int vr, size_t index, bool *ptr) + : Variable(name, vr, index, ptr) {} }; class StringVariable final : public Variable { @@ -220,10 +214,8 @@ namespace fmu4cpp { public: StringVariable( const std::string &name, - unsigned int vr, size_t index, - const std::function &getter, - const std::optional> &setter) - : Variable(name, vr, index, getter, setter) {} + unsigned int vr, size_t index, std::string *ptr) + : Variable(name, vr, index, ptr) {} }; bool requires_start(const VariableBase &v); diff --git a/export/src/fmu4cpp/fmu_base.cpp b/export/src/fmu4cpp/fmu_base.cpp index 186c13c..273f235 100644 --- a/export/src/fmu4cpp/fmu_base.cpp +++ b/export/src/fmu4cpp/fmu_base.cpp @@ -195,20 +195,20 @@ namespace fmu4cpp { return ss.str(); } - 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}; + IntVariable fmu_base::integer(const std::string &name, int* ptr) { + return {name, static_cast(integers_.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}; + RealVariable fmu_base::real(const std::string &name, double* ptr) { + return {name, static_cast(reals_.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}; + BoolVariable fmu_base::boolean(const std::string &name, bool* ptr) { + return {name, static_cast(booleans_.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}; + StringVariable fmu_base::string(const std::string &name, std::string* ptr) { + return {name, static_cast(strings_.size()), numVariables_++, ptr}; } void fmu_base::register_variable(IntVariable v) { 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..3bff29d --- /dev/null +++ b/export/tests/identity_test.cpp @@ -0,0 +1,224 @@ + +#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", &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 { + log(fmi2OK, "hello@ " + std::to_string(currentTime)); + 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)