Skip to content

Commit

Permalink
Feature/779 state serialization (#97)
Browse files Browse the repository at this point in the history
* Added a support for state saving in proxyfmu
* Upgraded boost to ~1.85
* Upgraded thrift to ~0.20
* Builds binary in a container conanio/gcc9-ubuntu16.04 to support consumers with older GLIBC versions.
  • Loading branch information
davidhjp01 authored Feb 24, 2025
1 parent cf544ab commit f05ece6
Show file tree
Hide file tree
Showing 18 changed files with 414 additions and 20 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ jobs:
compiler_libcxx: [ libstdc++11 ]
steps:
- uses: actions/checkout@v4
- name: Install compiler
run: sudo apt-get install -y --no-install-recommends g++-${{ matrix.compiler_version }}
- name: Install Conan
uses: turtlebrowser/get-conan@main
- name: Configure Conan
Expand All @@ -40,7 +42,7 @@ jobs:
run: cmake --build . --target install
- name: Test
run: cd build/tests && ctest --output-on-failure --extra-verbose
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: matrix.build_type == 'Release'
with:
name: proxyfmu-linux64-${{ matrix.build_type }}
Expand Down Expand Up @@ -81,7 +83,7 @@ jobs:
run: cmake --build . --config ${{ matrix.build_type }} --target install
- name: Test
run: cd build/tests && ctest -C ${{ matrix.build_type }} --output-on-failure --extra-verbose
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: matrix.build_type == 'Release'
with:
name: proxyfmu-win64-${{ matrix.build_type }}
Expand Down
36 changes: 26 additions & 10 deletions .github/workflows/upload-conan-pkgs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,24 @@ jobs:

steps:
- uses: actions/checkout@v4
- name: Install compiler
run: sudo apt-get install -y --no-install-recommends g++-${{ matrix.compiler_version }}
- name: Install Conan
uses: turtlebrowser/get-conan@main
- name: Configure Conan
- name: Generate Dockerfile
run: |
conan profile detect
conan remote add osp https://osp.jfrog.io/artifactory/api/conan/conan-local --force
- name: Conan create
mkdir /tmp/proxyfmu-builder-docker
cat <<'EOF' >/tmp/proxyfmu-builder-docker/Dockerfile
FROM conanio/gcc${{ matrix.compiler_version }}-ubuntu16.04
ENV CONAN_LOGIN_USERNAME_OSP=${{ secrets.osp_artifactory_usr }}
ENV CONAN_PASSWORD_OSP=${{ secrets.osp_artifactory_pwd }}
ENV LIBCOSIM_RUN_TESTS_ON_CONAN_BUILD=1
COPY entrypoint.sh /
ENTRYPOINT /entrypoint.sh
EOF
- name: Generate entrypoint.sh
run: |
cat <<'EOF' >/tmp/proxyfmu-builder-docker/entrypoint.sh
#!/bin/bash -v
set -eu
cd /mnt/source
conan remote add osp https://osp.jfrog.io/artifactory/api/conan/conan-local --force
REFNAME="${GITHUB_REF#refs/*/}"
VERSION="v$(<version.txt)"
if [[ $GITHUB_REF == refs/tags/* ]] && [[ $REFNAME == $VERSION ]]; then CHANNEL="stable"
Expand All @@ -48,11 +56,18 @@ jobs:
-s compiler.version=${{ matrix.compiler_version }} \
-s compiler.libcxx=${{ matrix.compiler_libcxx }} \
-b missing \
--update \
--user=osp \
--channel=${CHANNEL} \
.
- name: Conan upload
run: conan upload --confirm --remote osp '*'
conan upload --confirm --remote=osp '*'
EOF
chmod 0755 /tmp/proxyfmu-builder-docker/entrypoint.sh
- name: Build Docker image
run: docker build -t proxyfmu-builder /tmp/proxyfmu-builder-docker/
- name: Build cosim
run: |
docker run --rm --env GITHUB_REF="$GITHUB_REF" -v $(pwd):/mnt/source:ro proxyfmu-builder
windows:
Expand Down Expand Up @@ -87,6 +102,7 @@ jobs:
conan create \
-s build_type=${{ matrix.build_type }} \
-b missing \
--update \
--user=osp \
--channel=${CHANNEL} \
.
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ if (PROXYFMU_BUILD_TESTS)
FetchContent_MakeAvailable(Catch2)
endif ()

find_package(Boost 1.71 COMPONENTS ${BOOST_COMPONENTS} REQUIRED)
find_package(Boost 1.85 COMPONENTS ${BOOST_COMPONENTS} REQUIRED)
find_package(CLI11 REQUIRED)
find_package(Thrift REQUIRED)
find_package(FMILIB MODULE REQUIRED)
Expand Down
10 changes: 5 additions & 5 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ def set_version(self):

generators = "CMakeDeps", "CMakeToolchain"
exports = "version.txt"
exports_sources = "*"
exports_sources = ("cmake/*", "data/*", "examples/*", "include/*", "src/*", "tests/*", "tool/*",
"conanfile.py", "CMakeLists.txt", "LICENSE", "README.md", "version.txt")

def requirements(self):
self.tool_requires("cmake/[>=3.15]")
self.tool_requires("thrift/[~0.13]")
self.requires("boost/[~1.81]") # This version is required by Thrift
self.tool_requires("thrift/[~0.20]")
self.requires("cli11/[~2.3]")
self.requires("fmilibrary/[~2.3]")
self.requires("thrift/[~0.13]")
self.requires("zlib/1.2.13", override=True) # Also required by Thrift
self.requires("boost/[~1.85]") # Required by Thrift
self.requires("thrift/[~0.20]", transitive_headers=True)

def config_options(self):
if self.settings.os == "Windows":
Expand Down
3 changes: 3 additions & 0 deletions include/proxyfmu/fmi/model_description.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ struct model_description

model_variables modelVariables;
default_experiment defaultExperiment;

bool canGetAndSetFMUstate;
bool canSerializeFMUstate;
};

} // namespace proxyfmu::fmi
Expand Down
9 changes: 9 additions & 0 deletions include/proxyfmu/fmi/slave.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
#define PROXY_FMU_SLAVE_HPP

#include <proxyfmu/fmi/model_description.hpp>
#include <proxyfmu/state.hpp>

#include <memory>
#include <vector>

namespace proxyfmu::fmi
{

using namespace proxyfmu::state;
using value_ref = unsigned int;

class slave
Expand Down Expand Up @@ -40,6 +42,13 @@ class slave
virtual bool set_string(const std::vector<value_ref>& vr, const std::vector<std::string>& values) = 0;
virtual bool set_boolean(const std::vector<value_ref>& vr, const std::vector<bool>& values) = 0;

virtual state_index save_state() = 0;
virtual void save_state(state_index stateIndex) = 0;
virtual void restore_state(state_index stateIndex) = 0;
virtual void release_state(state_index stateIndex) = 0;
virtual void export_state(state_index stateIndex, state::exported_state& exportedState) const = 0;
virtual state_index import_state(const state::exported_state& exportedState) = 0;

virtual ~slave() = default;
};

Expand Down
34 changes: 34 additions & 0 deletions include/proxyfmu/state.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#ifndef PROXYFMU_STATE_HPP
#define PROXYFMU_STATE_HPP

#include <cstdint>
#include <string>
#include <vector>

namespace proxyfmu::state
{
constexpr std::int32_t export_scheme_version = 0;

using state_index = int;
using fmi2_FMU_state_t = void*;

struct saved_state
{
fmi2_FMU_state_t fmuState = nullptr;
bool setupComplete = false;
bool simStarted = false;
};

struct exported_state
{
std::int32_t schemeVersion;
std::string uuid;
std::string fmuState;
bool setupComplete;
bool simStarted;
};

}


#endif // PROXYFMU_STATE_HPP
45 changes: 45 additions & 0 deletions src/proxyfmu/client/proxy_slave.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,4 +230,49 @@ proxy_slave::~proxy_slave()
proxy_slave::freeInstance();
}

state_index proxy_slave::save_state()
{
return client_->save_state();
}

void proxy_slave::save_state(state_index stateIndex)
{
client_->save_state_by_index(stateIndex);
}

void proxy_slave::restore_state(state_index stateIndex)
{
client_->restore_state(stateIndex);
}

void proxy_slave::release_state(state_index stateIndex)
{
client_->release_state(stateIndex);
}

void proxy_slave::export_state(state_index stateIndex, state::exported_state& es) const
{
ExportedState es_;

client_->export_state(es_, stateIndex);

es.simStarted = es_.simStarted;
es.setupComplete = es_.setupComplete;
es.fmuState = es_.fmuState;
es.schemeVersion = es_.schemeVersion;
es.uuid = es_.uuid;
}

state_index proxy_slave::import_state(const state::exported_state& exportedState)
{
ExportedState es_;
es_.simStarted = exportedState.simStarted;
es_.setupComplete = exportedState.setupComplete;
es_.fmuState = exportedState.fmuState;
es_.schemeVersion = exportedState.schemeVersion;
es_.uuid = exportedState.uuid;

return client_->import_state(es_);
}

} // namespace proxyfmu::client
9 changes: 9 additions & 0 deletions src/proxyfmu/client/proxy_slave.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ using namespace apache::thrift::transport;
namespace proxyfmu::client
{

using namespace proxyfmu::state;

class proxy_slave : public fmi::slave
{

Expand Down Expand Up @@ -55,6 +57,13 @@ class proxy_slave : public fmi::slave
bool set_string(const std::vector<fmi::value_ref>& vr, const std::vector<std::string>& values) override;
bool set_boolean(const std::vector<fmi::value_ref>& vr, const std::vector<bool>& values) override;

state_index save_state() override;
void save_state(state_index stateIndex) override;
void restore_state(state_index stateIndex) override;
void release_state(state_index stateIndex) override;
void export_state(state_index stateIndex, state::exported_state& exportedState) const override;
state_index import_state(const state::exported_state& exportedState) override;

~proxy_slave() override;
};

Expand Down
30 changes: 30 additions & 0 deletions src/proxyfmu/fmi/fmi1/fmi1_slave.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,34 @@ fmi1_slave::~fmi1_slave()
fmi1_slave::freeInstance();
}

state_index fmi1_slave::save_state()
{
throw std::runtime_error("State saving API is not supported for FMI 1.0");
}

void fmi1_slave::save_state(state_index)
{
throw std::runtime_error("State saving API is not supported for FMI 1.0");
}

void fmi1_slave::restore_state(state_index)
{
throw std::runtime_error("State saving API is not supported for FMI 1.0");
}

void fmi1_slave::release_state(state_index)
{
throw std::runtime_error("State saving API is not supported for FMI 1.0");
}

void fmi1_slave::export_state(state_index, state::exported_state&) const
{
throw std::runtime_error("State saving API is not supported for FMI 1.0");
}

state_index fmi1_slave::import_state(const state::exported_state&)
{
throw std::runtime_error("State saving API is not supported for FMI 1.0");
}

} // namespace proxyfmu::fmi
7 changes: 7 additions & 0 deletions src/proxyfmu/fmi/fmi1/fmi1_slave.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ class fmi1_slave : public slave
bool set_string(const std::vector<value_ref>& vr, const std::vector<std::string>& values) override;
bool set_boolean(const std::vector<value_ref>& vr, const std::vector<bool>& values) override;

state_index save_state() override;
void save_state(state_index stateIndex) override;
void restore_state(state_index stateIndex) override;
void release_state(state_index stateIndex) override;
void export_state(state_index stateIndex, state::exported_state& es) const override;
state_index import_state(const state::exported_state& exportedState) override;

~fmi1_slave() override;
};

Expand Down
3 changes: 3 additions & 0 deletions src/proxyfmu/fmi/fmi2/fmi2_model_description.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ model_description create_model_description(fmi2_import_t* handle)
md.defaultExperiment.stepSize = fmi2_import_get_default_experiment_step(handle);
md.defaultExperiment.tolerance = fmi2_import_get_default_experiment_tolerance(handle);

md.canGetAndSetFMUstate = !!fmi2_import_get_capability(handle, fmi2_cs_canGetAndSetFMUstate);
md.canSerializeFMUstate = !!fmi2_import_get_capability(handle, fmi2_cs_canSerializeFMUstate);

const auto varList = fmi2_import_get_variable_list(handle, 0);
const auto varCount = fmi2_import_get_variable_list_size(varList);
for (auto i = 0; i < varCount; i++) {
Expand Down
Loading

0 comments on commit f05ece6

Please sign in to comment.