diff --git a/.gitignore b/.gitignore index 48ab192130a8c3994cf4f013ea5c57ad7cb4e00b..9156ce91b45dd384ba5ffb4b029e0a4724997937 100644 --- a/.gitignore +++ b/.gitignore @@ -247,3 +247,4 @@ ModelManifest.xml /MC_Backend_Win/MODELS /MC_Backend_Win/MODELI/Sample /MC_Backend_Win/fd9a-e8c9-6abb-2f17 +/MC_Backend_Win/UNZIP diff --git a/FMU-Core b/FMU-Core index 6625e00268bcfde0b6b2b9c237e05c1a20c23922..b36003d99442732b3835897b4da3d9617efca79e 160000 --- a/FMU-Core +++ b/FMU-Core @@ -1 +1 @@ -Subproject commit 6625e00268bcfde0b6b2b9c237e05c1a20c23922 +Subproject commit b36003d99442732b3835897b4da3d9617efca79e diff --git a/Files/Files.vcxitems b/Files/Files.vcxitems index b39446cf2be76fb165e885bc996c97006fe583c5..08640af0c8474c44d3c37d7ce13900c9a579b716 100644 --- a/Files/Files.vcxitems +++ b/Files/Files.vcxitems @@ -16,15 +16,17 @@ <ProjectCapability Include="SourceItemsFromImports" /> </ItemGroup> <ItemGroup> - <ClInclude Include="$(MSBuildThisFileDirectory)FmuFile.hpp" /> + <ClInclude Include="$(MSBuildThisFileDirectory)ModelDescription.h" /> + <ClInclude Include="$(MSBuildThisFileDirectory)FmuFileManager.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)ModeliFile.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)Unzipper.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)zip_file.hpp" /> </ItemGroup> <ItemGroup> - <ClCompile Include="$(MSBuildThisFileDirectory)FmuFile.cpp"> + <ClCompile Include="$(MSBuildThisFileDirectory)ModelDescription.cpp"> <PrecompiledHeader>NotUsing</PrecompiledHeader> </ClCompile> + <ClCompile Include="$(MSBuildThisFileDirectory)FmuFileManager.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)ModeliFile.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)Unzipper.cpp" /> </ItemGroup> diff --git a/Files/Files.vcxitems.filters b/Files/Files.vcxitems.filters index 189ec2078a477e60f8dbc2be5afa1986781ac481..24285bdf197834b607217454af3d1f0a865e9e13 100644 --- a/Files/Files.vcxitems.filters +++ b/Files/Files.vcxitems.filters @@ -11,9 +11,6 @@ </Filter> </ItemGroup> <ItemGroup> - <ClInclude Include="$(MSBuildThisFileDirectory)FmuFile.hpp"> - <Filter>Header</Filter> - </ClInclude> <ClInclude Include="$(MSBuildThisFileDirectory)ModeliFile.hpp"> <Filter>Header</Filter> </ClInclude> @@ -23,16 +20,25 @@ <ClInclude Include="$(MSBuildThisFileDirectory)zip_file.hpp"> <Filter>Header</Filter> </ClInclude> + <ClInclude Include="$(MSBuildThisFileDirectory)FmuFileManager.h"> + <Filter>Header</Filter> + </ClInclude> + <ClInclude Include="$(MSBuildThisFileDirectory)ModelDescription.h"> + <Filter>Header</Filter> + </ClInclude> </ItemGroup> <ItemGroup> - <ClCompile Include="$(MSBuildThisFileDirectory)FmuFile.cpp"> - <Filter>Source</Filter> - </ClCompile> <ClCompile Include="$(MSBuildThisFileDirectory)ModeliFile.cpp"> <Filter>Source</Filter> </ClCompile> <ClCompile Include="$(MSBuildThisFileDirectory)Unzipper.cpp"> <Filter>Source</Filter> </ClCompile> + <ClCompile Include="$(MSBuildThisFileDirectory)FmuFileManager.cpp"> + <Filter>Source</Filter> + </ClCompile> + <ClCompile Include="$(MSBuildThisFileDirectory)ModelDescription.cpp"> + <Filter>Source</Filter> + </ClCompile> </ItemGroup> </Project> \ No newline at end of file diff --git a/Files/FmuFileManager.cpp b/Files/FmuFileManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ff069a56b554cd1ef9009b96c7b7c8ed029577e8 --- /dev/null +++ b/Files/FmuFileManager.cpp @@ -0,0 +1,100 @@ +#include "FmuFileManager.h" +#include "Unzipper.hpp" +#include "boost/filesystem.hpp" + +namespace fs = boost::filesystem; + +namespace Files +{ + FmuFileManager::FmuFileManager() + { + } + + FmuFileManager::~FmuFileManager() + { + } + + ModelDescription FmuFileManager::add_model(std::vector<unsigned char> buffer) + { + // Unzip the fmu from byte buffer + auto unzip_dir = create_unzip_dir(); + Unzipper::Unzip(std::move(buffer), unzip_dir); + return add_from_unzipped(unzip_dir); + } + + ModelDescription FmuFileManager::add_model(std::string path) + { + // Unzip the fmu from byte buffer + auto unzip_dir = create_unzip_dir(); + Unzipper::Unzip(path, unzip_dir); + return add_from_unzipped(unzip_dir); + } + + void FmuFileManager::remove_model(std::string model_name) + { + if (m_models.count(model_name) == 1) + { + fs::remove_all(m_models[model_name].get_model_dir()); + m_models.erase(model_name); + } + } + + std::vector<ModelDescription> FmuFileManager::get_all() + { + std::vector<ModelDescription> retval; + for (auto element : m_models) + { + retval.push_back(element.second); + } + return retval; + } + + void FmuFileManager::clear() + { + fs::remove_all(MODELS_DIR); + m_models.clear(); + } + + std::string FmuFileManager::create_unzip_dir() + { + fs::path unzip_path; + do + { + unzip_path /= UNZIP_DIR; + unzip_path /= fs::unique_path(); + } while (fs::exists(unzip_path)); + fs::create_directories(unzip_path); + return unzip_path.generic_string(); + } + + ModelDescription FmuFileManager::add_from_unzipped(std::string unzip_dir) + { + // Load the model description + ModelDescription model; + model.set_model_dir(unzip_dir); + model.load(); + // Check if the model exists + if (m_models.count(model.get_model_name()) == 1) + { + // Clean temp + fs::remove_all(unzip_dir); + // Return existing instance + return m_models[model.get_model_name()]; + } + else + { + // Move to the MODELS dir + fs::path new_dir; + new_dir /= MODELS_DIR; + new_dir /= model.get_model_name(); + // Windows requires the parent path to exist but the leaf directory must not exist! + fs::create_directories(new_dir); + fs::remove_all(new_dir); + fs::rename(fs::path(unzip_dir), new_dir); + model.set_model_dir(new_dir.generic_string()); + // Return the new model + m_models.emplace(model.get_model_name(), model); + return model; + } + } +} \ No newline at end of file diff --git a/Files/FmuFileManager.h b/Files/FmuFileManager.h new file mode 100644 index 0000000000000000000000000000000000000000..95c3d92e22feb36a491677d18df707be5ceb4a22 --- /dev/null +++ b/Files/FmuFileManager.h @@ -0,0 +1,42 @@ +#pragma once +#include "ModelDescription.h" +#include <map> +#include <memory> +#include <string> +#include <vector> + +namespace Files +{ + /// Manages the FmuFiles which are contained in the MODELS directory. + class FmuFileManager + { + public: + FmuFileManager(); + FmuFileManager(const FmuFileManager&) = delete; + ModelDescription& operator=(const FmuFileManager&) = delete; + ~FmuFileManager(); + + /// Add a new ModelDescription from a buffer and return it + ModelDescription add_model(std::vector<unsigned char> buffer); + /// Add a new ModelDescription from the filesystem + ModelDescription add_model(std::string path); + /// Removes the ModelDescription from the filesystem + void remove_model(std::string model_name); + /// Returns a vector of all model descriptions + std::vector<ModelDescription> get_all(); + /// Clears the models + void clear(); + private: + const std::string MODELS_DIR = "MODELS"; + const std::string UNZIP_DIR = "UNZIP"; + // Store model_name, decription + std::map<std::string, ModelDescription> m_models; + // Creates an empty directory for unzipping the fmu. + // The path to the directory is returned. + std::string create_unzip_dir(); + + // Add a model description that has been unzipped to the unzip_dir + ModelDescription add_from_unzipped(std::string unzip_dir); + }; + +} \ No newline at end of file diff --git a/Files/FmuFile.cpp b/Files/ModelDescription.cpp similarity index 56% rename from Files/FmuFile.cpp rename to Files/ModelDescription.cpp index f1e5263291fc2a98392e5dd8d7c6225766768eb8..ec7e210adedf7f7487775371ad9e0ff598de68bd 100644 --- a/Files/FmuFile.cpp +++ b/Files/ModelDescription.cpp @@ -1,8 +1,7 @@ -#include "FmuFile.hpp" +#include "ModelDescription.h" #include "boost/filesystem.hpp" #include "boost/property_tree/ptree.hpp" #include "boost/property_tree/xml_parser.hpp" -#include "Unzipper.hpp" #include <iostream> namespace fs = boost::filesystem; @@ -10,24 +9,29 @@ namespace pt = boost::property_tree; namespace Files { - void FmuFile::unzip_and_load(std::function<void(std::string unzipPath)> unzipAction) + ModelDescription::ModelDescription() + { + } + ModelDescription::~ModelDescription() { - // Unzip the fmu - m_unzippedPath = fs::unique_path().generic_string(); - unzipAction(m_unzippedPath); - // Parse the model description - std::string xmlPath = m_unzippedPath + "/modelDescription.xml"; - pt::ptree md; - pt::read_xml(xmlPath, md); - m_modelName = md.get<std::string>("fmiModelDescription.CoSimulation.<xmlattr>.modelIdentifier"); - m_guid = md.get<std::string>("fmiModelDescription.<xmlattr>.guid"); - load_value_refs(md); } - void FmuFile::load_value_refs(const boost::property_tree::ptree & xmlRoot) + void ModelDescription::load() { + // Create & load ptree + std::string xmlPath = m_model_dir + "/modelDescription.xml"; + if (!fs::exists(xmlPath)) + { + // Do not crash. Load nothing. + return; + } + pt::ptree desc; + pt::read_xml(xmlPath, desc); + // Load model information + m_model_name = desc.get<std::string>("fmiModelDescription.CoSimulation.<xmlattr>.modelIdentifier"); + m_guid = desc.get<std::string>("fmiModelDescription.<xmlattr>.guid"); // The valuereferences - for (auto scalar_var : xmlRoot.get_child("fmiModelDescription.ModelVariables")) + for (auto scalar_var : desc.get_child("fmiModelDescription.ModelVariables")) { // Simplify the checking and adding a value reference auto check_and_add = [scalar_var](std::string varType, std::vector<unsigned int>& vr_vector) @@ -52,35 +56,20 @@ namespace Files } } - FmuFile::FmuFile(std::string pathToFmu) + void ModelDescription::set_model_dir(std::string model_dir) { - unzip_and_load([pathToFmu](std::string unzipPath) - { - Unzipper::Unzip(pathToFmu, unzipPath); - }); - } - FmuFile::FmuFile(const std::vector<unsigned char>& buffer) - { - unzip_and_load([&](std::string unzipPath) - { - Unzipper::Unzip(buffer, unzipPath); - }); + m_model_dir = model_dir; } - FmuFile::~FmuFile() + std::string ModelDescription::get_model_dir() { - boost::system::error_code error; - fs::remove_all(m_unzippedPath, error); - if (error) - { - std::cout << error.message() << std::endl; - } + return m_model_dir; } - std::string FmuFile::GetBinaryPath() + std::string ModelDescription::get_binary_path() { // The pathToFmu of the binary depends on the system - fs::path binaryPath = m_unzippedPath; + fs::path binaryPath = m_model_dir; binaryPath /= "binaries"; #if __linux__ #if __arm__ @@ -97,38 +86,43 @@ namespace Files #else #error system ist not supported #endif - binaryPath /= m_modelName; + binaryPath /= m_model_name; // Return canonical (absolute) pathToFmu. // Don not use relative paths for dynamic library loading (security)! return fs::weakly_canonical(binaryPath).generic_string(); } - std::string FmuFile::GetGUID() + std::string ModelDescription::get_model_name() + { + return m_model_name; + } + + std::string ModelDescription::get_guid() { return m_guid; } - std::string FmuFile::GetResourceUri() + std::string ModelDescription::get_resource_uri() { // Create prefix std::string prefix = "file:///"; // mandatory according to the fmi doc // Create pathToFmu object - std::string resourceDir = m_unzippedPath + "/resources"; + std::string resourceDir = m_model_dir + "/resources"; // Append absolute pathToFmu of the mod return prefix + resourceDir; } - std::vector<unsigned int> FmuFile::get_int_vrs() + std::vector<unsigned int> ModelDescription::get_int_vrs() { return m_int_vrs; } - std::vector<unsigned int> FmuFile::get_real_vrs() + std::vector<unsigned int> ModelDescription::get_real_vrs() { return m_real_vrs; } - std::vector<unsigned int> FmuFile::get_bool_vrs() + std::vector<unsigned int> ModelDescription::get_bool_vrs() { return m_bool_vrs; } - std::vector<unsigned int> FmuFile::get_string_vrs() + std::vector<unsigned int> ModelDescription::get_string_vrs() { return m_string_vrs; } diff --git a/Files/FmuFile.hpp b/Files/ModelDescription.h similarity index 58% rename from Files/FmuFile.hpp rename to Files/ModelDescription.h index f662febef2c92aa50d6fbbbc1c32df4f48c74ed4..b322d013e87467c9a705135f1e7b571443927038 100644 --- a/Files/FmuFile.hpp +++ b/Files/ModelDescription.h @@ -9,42 +9,40 @@ namespace Files { /// Handles the fmu file. On creation the fmu is extracted. /// On destruction the extracted files are cleaned up. - class FmuFile + class ModelDescription { private: // FMU info - std::string m_modelName; + std::string m_model_name; std::string m_guid; // The path where the extracted fmu is stored - std::string m_unzippedPath; + std::string m_model_dir; // ValueReferences for each type std::vector<unsigned int> m_int_vrs; std::vector<unsigned int> m_real_vrs; std::vector<unsigned int> m_bool_vrs; std::vector<unsigned int> m_string_vrs; - /// Unzips the fmu and loads the modelDescription - /// The unzip action must be passed in. - void unzip_and_load(std::function<void(std::string unzipPath)> unzipAction); - void load_value_refs(const boost::property_tree::ptree& xmlRoot); - public: /// Initialize from local file - FmuFile(std::string pathToFmu); - /// Initialize from fmu in memory - FmuFile(const std::vector<unsigned char>& buffer); - FmuFile(const FmuFile&) = delete; - FmuFile(FmuFile&&) = default; - FmuFile& operator=(const FmuFile&) = delete; - FmuFile& operator=(FmuFile&&) = default; - ~FmuFile(); + ModelDescription(); + ~ModelDescription(); + /// Load the modelDescription.xml. Set the model_dir before. + void load(); + /// Set the directory of the model + /// The xml is not parsed. + void set_model_dir(std::string model_dir); + /// Returns the path to the directory where the fmu has been unzipped + std::string get_model_dir(); /// Returns the canonicial path to the binary without extension. - std::string GetBinaryPath(); + std::string get_binary_path(); + /// Returns the model identifier + std::string get_model_name(); /// Returns the guid from the modelDescription xml - std::string GetGUID(); + std::string get_guid(); /// Create an URI to the resource dir. The uri is "file:///" + absolutePath of modelDir - std::string GetResourceUri(); + std::string get_resource_uri(); std::vector<unsigned int> get_int_vrs(); std::vector<unsigned int> get_real_vrs(); diff --git a/Files/ModeliFile.cpp b/Files/ModeliFile.cpp index 86515f9615ef14b9220ec4f1c5f649dc1a244bbb..b737ace071ae5b978a0c3ebbb92a5d855d210023 100644 --- a/Files/ModeliFile.cpp +++ b/Files/ModeliFile.cpp @@ -1,5 +1,4 @@ #include "ModeliFile.hpp" -#include "FmuFile.hpp" #include "Unzipper.hpp" #include "ChannelLink.h" #include "boost/property_tree/ptree.hpp" @@ -11,27 +10,45 @@ namespace pt = boost::property_tree; namespace Files { - ModeliFile::ModeliFile(std::string modeliFile) + ModeliFile::ModeliFile(std::string path_to_modeli) { - // Extract the modeliFile to MODELS/**filename without extension** - fs::path modeliPath(modeliFile); - fs::path extractPath(MODEL_DIR); - extractPath /= modeliPath.stem(); - m_unzipDir = extractPath.generic_string(); - Unzipper::Unzip(modeliFile, m_unzipDir); - // Load the xml file - pt::read_xml((fs::path(m_unzipDir) / "ChannelLinks.xml").generic_string(), m_modelixml); + unzip_modeli_file(path_to_modeli); + } ModeliFile::~ModeliFile() { - fs::remove_all(m_unzipDir); + fs::remove_all(m_unzip_dir); } - std::vector<Simulation::ChannelLink> ModeliFile::GetChannelLinks() + std::vector<Simulation::ChannelLink> ModeliFile::get_channel_links() + { + return m_channel_links; + } + + std::shared_ptr<FmuFileManager> ModeliFile::get_fmu_file_manager() + { + return m_file_manager; + } + std::map<std::string, ModelDescription> ModeliFile::get_model_for_instance() + { + return m_model_for_instance; + } + void ModeliFile::unzip_modeli_file(std::string path_to_modeli) + { + // Extract the modeliFile to *U*NZIP_DIR**/**filename without extension** + fs::path modeliPath(path_to_modeli); + fs::path extractPath(path_to_modeli); + extractPath /= modeliPath.stem(); + m_unzip_dir = extractPath.generic_string(); + Unzipper::Unzip(modeliPath.generic_string(), m_unzip_dir); + } + void ModeliFile::parse_modeli_xml(std::string xml_path) { - std::vector<Simulation::ChannelLink> links; + // Load the xml file + boost::property_tree::ptree modeli_xml; + pt::read_xml((fs::path(m_unzip_dir) / "ChannelLinks.xml").generic_string(), modeli_xml); // Parse the channel links - for (auto c : m_modelixml.get_child("ChannelLinks")) + for (auto c : modeli_xml.get_child("ChannelLinks")) { // Only add the channel link if it is actually linked if (c.second.get<bool>("Linked")) @@ -43,40 +60,18 @@ namespace Files channelLink.MasterValueRef = c.second.get<unsigned int>("MasterChannel.ValueRef"); channelLink.SlaveInstanceName = c.second.get<std::string>("SlaveChannel.DataSourceName"); channelLink.SlaveValueRef = c.second.get<unsigned int>("SlaveChannel.ValueRef"); - links.push_back(channelLink); + m_channel_links.push_back(std::move(channelLink)); } } - return links; - } - - std::vector<std::unique_ptr<FmuFile>> ModeliFile::GetFmus() - { - std::vector<std::unique_ptr<Files::FmuFile>> files; - // Parse the fmu Files - for (auto source : m_modelixml.get_child("Description.DataSources")) - { - // Only fmu datasources - if (source.second.get<std::string>("SourceType") == "FMUDataSource") - { - // Create the FumFile and add it to the vector - files.push_back(std::make_unique<FmuFile>( - m_unzipDir + "/" + source.second.get<std::string>("Location"))); - } - } - return files; - } - std::vector<std::string> ModeliFile::GetInstanceNames() - { - std::vector<std::string> names; // Parse the fmu Files - for (auto source : m_modelixml.get_child("Description.DataSources")) + for (auto source : modeli_xml.get_child("Description.DataSources")) { // Only fmu datasources if (source.second.get<std::string>("SourceType") == "FMUDataSource") { - names.push_back(source.second.get<std::string>("InstanceName")); + m_model_for_instance.emplace(source.second.get<std::string>("InstanceName"), + m_file_manager->add_model(m_unzip_dir + "/" + source.second.get<std::string>("Location"))); } } - return names; } } \ No newline at end of file diff --git a/Files/ModeliFile.hpp b/Files/ModeliFile.hpp index 2139fc2c1152314563b168f85b1dc67b63a798ac..26e144bc645e2cf082ea5fe393a123d3179bb11f 100644 --- a/Files/ModeliFile.hpp +++ b/Files/ModeliFile.hpp @@ -1,38 +1,40 @@ #pragma once -#include "boost/property_tree/ptree.hpp" +#include "ChannelLink.h" +#include "FmuFileManager.h" #include <memory> #include <string> #include <utility> #include <vector> -#include "ChannelLink.h" namespace Files { - // Forward declare - class FmuFile; - /// Extracts a modeli file and the included information. /// The extracted files will be removed when the instance is destructed. class ModeliFile { public: /// Create a new instance from the given file location. - ModeliFile(std::string modeliFile); + ModeliFile(std::string path_to_modeli); ModeliFile(const ModeliFile&) = delete; void operator=(const ModeliFile&) = delete; ~ModeliFile(); /// Extracts the channel links from the xml file in the modeli file - std::vector<Simulation::ChannelLink> GetChannelLinks(); + std::vector<Simulation::ChannelLink> get_channel_links(); /// Extracts the path and instancName of th fmus - std::vector<std::unique_ptr<FmuFile>> GetFmus(); - std::vector<std::string> GetInstanceNames(); + std::shared_ptr<FmuFileManager> get_fmu_file_manager(); + std::map<std::string, ModelDescription> get_model_for_instance(); private: - // Relative path to the dir where the modeli will be stored temporally - const std::string MODEL_DIR = "MODELI"; - // Where the modeli file is extracted to - std::string m_unzipDir; - // Store the parsed properties - boost::property_tree::ptree m_modelixml; + // Where the modeli file will be unzipped + const std::string UNZIP_DIR = "UNZIP"; + std::string m_unzip_dir; + // models & instance names + std::shared_ptr<FmuFileManager> m_file_manager; + std::map<std::string, ModelDescription> m_model_for_instance; + std::vector<Simulation::ChannelLink> m_channel_links; + // Unzip the contents of the modeli file + void unzip_modeli_file(std::string path_to_modeli); + // Parse the xml file and store the relevant data + void parse_modeli_xml(std::string xml_path); }; } \ No newline at end of file diff --git a/Files/Unzipper.cpp b/Files/Unzipper.cpp index e2093429901766c54a3959a61b42cb4b45db6f42..7a4b3de0f1cd7d6ccf54b6aa0fc87b2eaf7f9a40 100644 --- a/Files/Unzipper.cpp +++ b/Files/Unzipper.cpp @@ -4,27 +4,25 @@ void Unzipper::Unzip(std::string zipFile, std::string unzipDir) { - miniz_cpp::zip_file zip(zipFile); - Unzip(zip, unzipDir); + Unzip(std::make_unique<miniz_cpp::zip_file>(zipFile), unzipDir); } -void Unzipper::Unzip(const std::vector<unsigned char>& zipFile, std::string unzipDir) +void Unzipper::Unzip(std::vector<unsigned char> buffer, std::string unzipDir) { - miniz_cpp::zip_file zip(zipFile); - Unzip(zip, unzipDir); + Unzip(std::make_unique<miniz_cpp::zip_file>(std::move(buffer)), unzipDir); } -void Unzipper::Unzip(miniz_cpp::zip_file & zipFile, std::string unzipDir) +void Unzipper::Unzip(std::unique_ptr<miniz_cpp::zip_file> zipFile, std::string unzipDir) { namespace fs = boost::filesystem; // Use boost path :) fs::path unzipPath(unzipDir); // Create folder structure, ifstream does not create them for us - for (auto info : zipFile.infolist()) + for (auto info : zipFile->infolist()) { fs::path folder = (unzipPath / info.filename).parent_path(); bool res = fs::create_directories(folder); } // Unzip - zipFile.extractall(unzipDir); + zipFile->extractall(unzipDir); } diff --git a/Files/Unzipper.hpp b/Files/Unzipper.hpp index e41471c3f8201bbc107e29708fe23bdbb77c8a5a..38664a579271c4f4bf616e24850486006ef3f1a9 100644 --- a/Files/Unzipper.hpp +++ b/Files/Unzipper.hpp @@ -1,20 +1,22 @@ #pragma once +#include <memory> #include <string> #include <vector> namespace miniz_cpp { - class zip_file; + class zip_file; } /// Can extract a zip file which contains a folder structure class Unzipper { public: - /// Extracts the zip file to the extractDir - static void Unzip(std::string zipFile, std::string unzipDir); - /// Extracts the zip file to the extractDir - static void Unzip(const std::vector<unsigned char>& zipFile, std::string unzipDir); - /// Unzip from a zip_file to unzipDir. - static void Unzip(miniz_cpp::zip_file& zipFile, std::string unzipDir); + /// Extracts the zip file to the extractDir + static void Unzip(std::string path, std::string unzipDir); + /// Extracts the zip file to the extractDir + static void Unzip(std::vector<unsigned char> buffer, std::string unzipDir); +private: + /// Unzip from a zip_file to unzipDir. + static void Unzip(std::unique_ptr<miniz_cpp::zip_file> zipFile, std::string unzipDir); }; \ No newline at end of file diff --git a/Main/main.cpp b/Main/main.cpp index d83bad99384406f011dd3d977822f62ff7e283bb..42499cc1f975ded802f1bea419f50c697de83196 100644 --- a/Main/main.cpp +++ b/Main/main.cpp @@ -1,5 +1,4 @@ #include "ModeliFile.hpp" -#include "FmuFile.hpp" #include "Simulator.hpp" #include "ModeliGrpcServer.h" #include "grpc++/grpc++.h" @@ -43,20 +42,22 @@ int main(int argc, char *argv[]) { port = vm["port"].as<unsigned short>(); } - auto fmuEngine = std::make_unique<Simulation::Simulator>(); + auto simulator = std::make_unique<Simulation::Simulator>(); + auto file_manager = std::make_shared<Files::FmuFileManager>(); // Load the modeli file into the engine if (vm.count("modeli")) { try { Files::ModeliFile modeliFile(vm["modeli"].as<std::string>()); - for (size_t i = 0; i < modeliFile.GetFmus().size(); i++) + file_manager = modeliFile.get_fmu_file_manager(); + for (auto instance_and_model : modeliFile.get_model_for_instance()) { - fmuEngine->AddFmu(std::move(modeliFile.GetFmus()[i]), modeliFile.GetInstanceNames()[i]); + simulator->AddFmu(instance_and_model.second, instance_and_model.first); } - for (auto channelLink : modeliFile.GetChannelLinks()) + for (auto channelLink : modeliFile.get_channel_links()) { - fmuEngine->AddChannelLink(channelLink); + simulator->AddChannelLink(channelLink); } } catch (std::exception err) @@ -65,7 +66,7 @@ int main(int argc, char *argv[]) } } // Configuration for rpc - ModeliGrpcServer service(std::move(fmuEngine)); + ModeliGrpcServer service(std::move(simulator), file_manager); std::string targetUri = "0.0.0.0:" + std::to_string(port); // Start the server grpc::ServerBuilder builder; diff --git a/Simulation/FmuEntity.cpp b/Simulation/FmuEntity.cpp index ede247e59880112bb26294e550a80ee5b117aa5c..54c099462d4968537983c09ea09bdaafb7369823 100644 --- a/Simulation/FmuEntity.cpp +++ b/Simulation/FmuEntity.cpp @@ -3,16 +3,16 @@ namespace Simulation { - FmuEntity::FmuEntity(std::string instance_name, std::unique_ptr<Files::FmuFile> file, NativeFmu::LoggerCallback logger) : + FmuEntity::FmuEntity(std::string instance_name, Files::ModelDescription file, NativeFmu::LoggerCallback logger) : m_instance_name(instance_name), m_file(std::move(file)), m_logger(logger) { m_fmu = std::make_shared<NativeFmu::CoSimFmu>(); - m_fmu->SetBooleanVr(m_file->get_bool_vrs()); - m_fmu->SetIntegerVr(m_file->get_int_vrs()); - m_fmu->SetRealVr(m_file->get_real_vrs()); - m_fmu->SetStringVr(m_file->get_string_vrs()); + m_fmu->SetBooleanVr(m_file.get_bool_vrs()); + m_fmu->SetIntegerVr(m_file.get_int_vrs()); + m_fmu->SetRealVr(m_file.get_real_vrs()); + m_fmu->SetStringVr(m_file.get_string_vrs()); } std::string FmuEntity::GetInstanceName() @@ -27,7 +27,7 @@ namespace Simulation bool FmuEntity::InstantiateFmu() { - std::pair<bool, std::string> loadDllRes = m_fmu->LoadDll(m_file->GetBinaryPath()); + std::pair<bool, std::string> loadDllRes = m_fmu->LoadDll(m_file.get_binary_path()); if (!loadDllRes.first) { m_logger(m_instance_name, fmi2Warning, "", "Failed LoadLibrary, Error: " + loadDllRes.second); @@ -38,7 +38,7 @@ namespace Simulation namespace ph = std::placeholders; m_fmu->SetLoggerCallback(m_logger); // Instantiate - if (!m_fmu->Instantiate(m_instance_name, m_file->GetGUID(), m_file->GetResourceUri())) + if (!m_fmu->Instantiate(m_instance_name, m_file.get_guid(), m_file.get_resource_uri())) { m_logger(m_instance_name, fmi2Warning, "", "Instantiation failed."); return false; @@ -63,8 +63,5 @@ namespace Simulation FmuEntity::~FmuEntity() { - // Destroy in rigth order - m_fmu.reset(); - m_file.reset(); } } \ No newline at end of file diff --git a/Simulation/FmuEntity.h b/Simulation/FmuEntity.h index ff9546b1944907e99639faf27af07daae9ce38cb..2d4f7ec27e26ba7cb81f96b80abe999abdef76a1 100644 --- a/Simulation/FmuEntity.h +++ b/Simulation/FmuEntity.h @@ -1,17 +1,17 @@ #pragma once #include "CoSimFmu.h" -#include "FmuFile.hpp" +#include "ModelDescription.h" #include "BasicFmuCallbacks.h" #include <memory> namespace Simulation { - /// Manages the lifitimes of a FmuFile and its' CoSimFmu instance. - /// Simplifies instantiation and Initialization of the fmu. + /// Keeps a CoSimFmu together with its model description. + /// Instantiation and Initialization of the fmu are simplified because they are kept together. class FmuEntity { public: - FmuEntity(std::string instance_name, std::unique_ptr<Files::FmuFile> file, NativeFmu::LoggerCallback logger); + FmuEntity(std::string instance_name, Files::ModelDescription model, NativeFmu::LoggerCallback logger); std::string GetInstanceName(); @@ -27,7 +27,7 @@ namespace Simulation ~FmuEntity(); private: std::string m_instance_name; - std::unique_ptr<Files::FmuFile> m_file; + Files::ModelDescription m_file; std::shared_ptr<NativeFmu::CoSimFmu> m_fmu; NativeFmu::LoggerCallback m_logger; }; diff --git a/Simulation/ModeliGrpcServer.cpp b/Simulation/ModeliGrpcServer.cpp index cb96e65c49c312a515fab17356c407099c8cb42e..a985683cd8ccaeb4ad2d911c78f67d7588b0c4dd 100644 --- a/Simulation/ModeliGrpcServer.cpp +++ b/Simulation/ModeliGrpcServer.cpp @@ -1,12 +1,13 @@ #include "ModeliGrpcServer.h" #include "ChannelLink.h" -#include "FmuFile.hpp" +#include "FmuFileManager.h" #include "Simulator.hpp" -#include "CoSimFmu.h" namespace ph = std::placeholders; -ModeliGrpcServer::ModeliGrpcServer(std::unique_ptr<Simulation::Simulator> simulator) :m_simulator(std::move(simulator)) +ModeliGrpcServer::ModeliGrpcServer(std::unique_ptr<Simulation::Simulator> simulator, std::shared_ptr<Files::FmuFileManager> file_manager) : + m_simulator(std::move(simulator)), + m_fmu_file_manager(std::move(file_manager)) { // Register the callbacks m_simulator->RegisterLogCallback(std::bind(&ModeliGrpcServer::LogArrived, this, ph::_1, ph::_2, ph::_3, ph::_4)); @@ -60,20 +61,22 @@ Status ModeliGrpcServer::AddFmu(ServerContext * context, ServerReader<::ModeliRp // Read the chunks while (reader->Read(&request)) { + if (instanceName == "") + { + instanceName = request.instance_name(); + } // Intial assignments if (buffer.size() == 0) { buffer.reserve(request.total_size()); } - if (instanceName == "") - { - instanceName = request.instance_name(); - } // Insert recceived data buffer.insert(buffer.end(), request.chunk().begin(), request.chunk().end()); } + // Add to the file manager + auto model_description = m_fmu_file_manager->add_model(std::move(buffer)); // Try to add the fmu - response->set_success(m_simulator->AddFmu(std::make_unique<Files::FmuFile>(buffer), instanceName)); + response->set_success(m_simulator->AddFmu(std::move(model_description), instanceName)); return Status::OK; } @@ -160,11 +163,11 @@ Status ModeliGrpcServer::NewValues(ServerContext * context, const::ModeliRpc::Ne { streamOpen = writer->Write(*item); } - else if(m_new_values.is_finished()) + else if (m_new_values.is_finished()) { // No value available and no more will come break; - } + } } return Status::OK; } diff --git a/Simulation/ModeliGrpcServer.h b/Simulation/ModeliGrpcServer.h index 2cced814975b203999dddc1ad09e56d42234c2b1..e1198abe040962f67f6e42ff1473018b24ce7a34 100644 --- a/Simulation/ModeliGrpcServer.h +++ b/Simulation/ModeliGrpcServer.h @@ -12,13 +12,17 @@ namespace Simulation class Simulator; } +namespace Files +{ + class FmuFileManager; +} /// This class implements the server side of the grpc definition. /// It is in charge of controlling the simulation and sending feedback to the frontend. class ModeliGrpcServer final : public ModeliRpc::ModeliBackend::Service { public: - ModeliGrpcServer(std::unique_ptr<Simulation::Simulator> unique_ptr); + ModeliGrpcServer(std::unique_ptr<Simulation::Simulator> simulator, std::shared_ptr<Files::FmuFileManager> file_manager); ModeliGrpcServer(const ModeliGrpcServer&) = delete; void operator=(const ModeliGrpcServer&) = delete; ~ModeliGrpcServer(); @@ -75,10 +79,11 @@ public: std::string message ///< The message that has been logged ); private: + std::shared_ptr<Files::FmuFileManager> m_fmu_file_manager; // Execue the simulation commands std::unique_ptr<Simulation::Simulator> m_simulator; // Wait for logs to arrive Utility::ConcurrentQueue<ModeliRpc::LogResponse> m_logs; // Wait for values to arrive Utility::ConcurrentQueue<ModeliRpc::NewValuesResponse> m_new_values; -}; +}; \ No newline at end of file diff --git a/Simulation/Simulator.cpp b/Simulation/Simulator.cpp index e8d7dbbceaf68a75f68ce6018c30ca63f1847fce..0c4461a4d4058f3a3ba773ac7c6a2718f0f4ee9f 100644 --- a/Simulation/Simulator.cpp +++ b/Simulation/Simulator.cpp @@ -177,7 +177,7 @@ namespace Simulation auto boolRes = fmu->GetBoolean(); auto stringRes = fmu->GetString(); // Send the values - onValuesArrived(fmu->GetInstanceName(), current_time, + onValuesArrived(entity->GetInstanceName(), current_time, fmu->GetIntegerVr(), intRes.second, fmu->GetRealVr(), realRes.second, fmu->GetBooleanVr(), boolRes.second, @@ -290,19 +290,6 @@ namespace Simulation m_log_callback(instanceName, status, category, message); } - std::shared_ptr<NativeFmu::CoSimFmu> Simulator::get_fmu_or_log(std::string instance_name, std::string msg) - { - if (auto entity = m_fmus->GetEntity(instance_name)) - { - return entity->GetCoSimFmu().lock(); - } - else - { - onLog(instance_name, fmi2Warning, "", "Instance was not found while executing: " + msg); - return nullptr; - } - } - bool Simulator::AddChannelLink(ChannelLink channelLink) { return m_dispatcher.dispatch_fn<bool>([&]() @@ -320,20 +307,25 @@ namespace Simulation }).get(); } - bool Simulator::AddFmu(std::unique_ptr<Files::FmuFile> fmuFile, std::string instance_name) + bool Simulator::FmuInstanceExists(std::string instance_name) { - // Fail early if instance name exists (asking for state -> dispatch) - if (m_dispatcher.dispatch_fn<bool>([&]() + return m_dispatcher.dispatch_fn<bool>([&]() { return m_fmus->Contains(instance_name); - }).get()) + }).get(); + } + + bool Simulator::AddFmu(Files::ModelDescription model, std::string instance_name) + { + // Fail early if instance name exists (asking for state -> dispatch) + if (FmuInstanceExists(instance_name)) { return false; } // Heavy instantiation work outside the simulation std::shared_ptr<FmuEntity> entity = std::make_shared<FmuEntity>( instance_name, - std::move(fmuFile), + std::move(model), std::bind(&Simulator::onLog, this, ph::_1, ph::_2, ph::_3, ph::_4)); if (!entity->InstantiateFmu()) { @@ -377,50 +369,66 @@ namespace Simulation } int Simulator::SetInteger(std::string instance_name, std::vector<unsigned int> vr, std::vector<int> values) { - if (auto fmu = get_fmu_or_log(instance_name, "set integer")) + if (auto entity = m_fmus->GetEntity(instance_name)) { - return fmu->SetInteger(vr.data(), vr.size(), values.data()); + if (auto fmu = entity->GetCoSimFmu().lock()) + { + fmu->SetInteger(vr.data(), vr.size(), values.data()); + } } else { + onLog(instance_name, fmi2Warning, "", "Cannot set integer values, instance not found."); return fmi2Warning; } } int Simulator::SetReal(std::string instance_name, std::vector<unsigned int> vr, std::vector<double> values) { - if (auto fmu = get_fmu_or_log(instance_name, "set real")) + if (auto entity = m_fmus->GetEntity(instance_name)) { - return fmu->SetReal(vr.data(), vr.size(), values.data()); + if (auto fmu = entity->GetCoSimFmu().lock()) + { + fmu->SetReal(vr.data(), vr.size(), values.data()); + } } else { + onLog(instance_name, fmi2Warning, "", "Cannot set real values, instance not found."); return fmi2Warning; } } int Simulator::SetBoolean(std::string instance_name, std::vector<unsigned int> vr, std::vector<int> values) { - if (auto fmu = get_fmu_or_log(instance_name, "set boolean")) + if (auto entity = m_fmus->GetEntity(instance_name)) { - return fmu->SetBoolean(vr.data(), vr.size(), values.data()); + if (auto fmu = entity->GetCoSimFmu().lock()) + { + fmu->SetBoolean(vr.data(), vr.size(), values.data()); + } } else { + onLog(instance_name, fmi2Warning, "", "Cannot set boolean values, instance not found."); return fmi2Warning; } } int Simulator::SetString(std::string instance_name, std::vector<unsigned int> vr, std::vector<std::string> values) { - if (auto fmu = get_fmu_or_log(instance_name, "set integer")) + if (auto entity = m_fmus->GetEntity(instance_name)) { - std::vector<const char*> converted; - for (auto element : values) + if (auto fmu = entity->GetCoSimFmu().lock()) { - converted.push_back(element.c_str()); + std::vector<const char*> converted; + for (auto element : values) + { + converted.push_back(element.c_str()); + } + fmu->SetString(vr.data(), vr.size(), converted.data()); } - return fmu->SetString(vr.data(), vr.size(), converted.data()); } else { + onLog(instance_name, fmi2Warning, "", "Cannot set string value, instance not found."); return fmi2Warning; } } diff --git a/Simulation/Simulator.hpp b/Simulation/Simulator.hpp index 32d46f8a28775b2b26a057b03bcfd662b9a0c528..03aa44af7300d7c0e8c544bdd287065d76543313 100644 --- a/Simulation/Simulator.hpp +++ b/Simulation/Simulator.hpp @@ -1,22 +1,13 @@ #pragma once #include "ChannelLink.h" #include "QueueDispatcher.h" +#include "ModelDescription.h" #include "boost/optional.hpp" #include <functional> #include <list> #include <thread> #include <vector> -namespace Files -{ - class FmuFile; -} - -namespace NativeFmu -{ - class CoSimFmu; -} - namespace Simulation { class FmuContainer; @@ -48,13 +39,12 @@ namespace Simulation { public: Simulator(); + Simulator(Simulator&&) = default; Simulator(const Simulator&) = delete; - void operator=(const Simulator&) = delete; + Simulator& operator=(Simulator&&) = default; + Simulator& operator=(const Simulator&) = delete; ~Simulator(); - /// Add callbacks for the server - - /// Runs the simulation in realtime with endtime set to DBL_MAX int Play(); /// Runs the simulation as fast as possible until endTime is reached. @@ -69,8 +59,10 @@ namespace Simulation /// Remove a ChannelLink bool RemoveChannelLink(ChannelLink channelLink); + /// Returns true if the instance is already part of the simulation + bool FmuInstanceExists(std::string instance_name); /// Add a fmu which was loaded by FmuFile class - bool AddFmu(std::unique_ptr<Files::FmuFile> fmuFile, std::string instanceName); + bool AddFmu(Files::ModelDescription model, std::string instanceName); /// Remove the FMU with matching instanceName bool RemoveFMU(std::string instanceName); @@ -126,8 +118,5 @@ namespace Simulation const std::vector<unsigned int> stringVrs, const std::vector<const char*> stringValues); // Spread the word: A log has arrived (also used as callback for the fmus) void onLog(std::string instanceName, int status, std::string category, std::string message); - - // logs if the fmu was not found, nullptr is returned in this case - std::shared_ptr<NativeFmu::CoSimFmu> get_fmu_or_log(std::string instance_name, std::string msg); }; } \ No newline at end of file