diff --git a/.clang-tidy b/.clang-tidy index 2543eeec4c..4b02203eef 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -28,6 +28,7 @@ Checks: " misc-*, -misc-non-private-member-variables-in-classes, -misc-no-recursion, + -misc-use-anonymous-namespace, cert-*, -cert-err58-cpp, portability-*, @@ -67,6 +68,8 @@ WarningsAsErrors: " misc-*, -misc-non-private-member-variables-in-classes, -misc-no-recursion, + -misc-include-cleaner, + -misc-use-anonymous-namespace, cert-*, -cert-err58-cpp, portability-*, diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e56b81070f..f12ba84788 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,7 +8,7 @@ repos: - id: dockerfile_lint args: [--rulefile, ./config/Docker/docker_rules.yml, --dockerfile] - repo: https://github.com/psf/black - rev: 24.3.0 + rev: 24.4.2 hooks: - id: black args: ["--line-length=100"] @@ -22,7 +22,7 @@ repos: hooks: - id: remove-tabs - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-added-large-files - id: mixed-line-ending @@ -74,7 +74,7 @@ repos: "--exclude-file=./config/spelling_ignorelines.txt", ] - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v18.1.2 + rev: v18.1.5 hooks: - id: clang-format types: diff --git a/CHANGELOG.md b/CHANGELOG.md index 4202b4a56a..f4d17bfdf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,20 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm A note on future revisions. Everything within a major version number should be code compatible (with the exception of experimental interfaces). The most notable example of an experimental interface is the support for multiple source inputs. The APIs to deal with this will change in future minor releases. Everything within a single minor release should be network compatible with other federates on the same minor release number. Compatibility across minor release numbers may be possible in some situations but we are not going to guarantee this as those components are subject to performance improvements and may need to be modified at some point. Patch releases will be limited to bug fixes and other improvements not impacting the public API or network compatibility. Check the [Public API](./docs/Public_API.md) for details on what is included and excluded from the public API and version stability. +## [3.5.3][] - 2024-07-08 + +Patch release with fixes for potential interface definitions and some compiler warnings + +### Fixed + +- Fixed some compiler warnings on the connector + +### Added + +- Added support for "potential_interface_templates" object in json configuration +- Added support for arrays of tags +- Added support for "fields" object in potential interface template definitions + ## [3.5.2][] - 2024-04-08 Patch release with fixes for certain compiler builds, a fix to the test core leading to some sporadic test failures, and fixing a discrepancy in the handing of config files with the helics_apps. diff --git a/CMakeLists.txt b/CMakeLists.txt index d3f22ea161..6eb9de4eac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,14 +17,14 @@ if(DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE AND NOT HELICS_D set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE STRING "") endif() -project(HELICS VERSION 3.5.2) +project(HELICS VERSION 3.5.3) # ----------------------------------------------------------------------------- # HELICS Version number # ----------------------------------------------------------------------------- set(HELICS_VERSION_BUILD) # use ISO date YYYY-MM-DD -set(HELICS_DATE "2024-04-08") +set(HELICS_DATE "2024-07-08") set(HELICS_VERSION_UNDERSCORE "${HELICS_VERSION_MAJOR}_${HELICS_VERSION_MINOR}_${HELICS_VERSION_PATCH}" diff --git a/appveyor.yml b/appveyor.yml index 15b97164a4..7e3596c1c6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,7 +5,7 @@ branches: - main - develop -version: 3.5.2.{build} +version: 3.5.3.{build} image: Visual Studio 2019 diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 5ca307557f..a7eb8a5393 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -27,5 +27,4 @@ This document contains tentative plans for changes and improvements of note in u - Some sort of rollback operations - Remote procedure call type of federate - Plugin architecture for user defined cores -- Separate octave interface - Enable mesh networking in HELICS diff --git a/docs/developer-guide/typeConversions.md b/docs/developer-guide/typeConversions.md index 9c1c096b0e..197d8703ef 100644 --- a/docs/developer-guide/typeConversions.md +++ b/docs/developer-guide/typeConversions.md @@ -105,6 +105,7 @@ There are defined conversions from all known available types to all others. - BOOL -> (val!=0)?"1":"0" [^1]: conversion to double is lossless only if the value actually fits in a double mantissa value. + [^2]: for a named point conversion, if the value doesn't fit in double the string translation is placed in the string field and a NaN value in the value segment to ensure no data loss. ### Conversion from String @@ -155,7 +156,9 @@ Similar to getDoubleFromString in conversion of vectors. It will convert most re - BOOL -> (vectorNorm(val)!=0)?"1":"0" [^3]: vectorNorm is the sqrt of the inner product of the vector. + [^4]: vectorString is comma-separated string of the numerical values enclosed in `[]`, for example `[45.7,22.7,17.8]`. This is a JSON compatible string format. + [^5]: if the vector is a single element the NAMED_POINT translation is equivalent to a double translation. ### Conversion from Complex Vector diff --git a/src/helics/application_api/Federate.cpp b/src/helics/application_api/Federate.cpp index 3a99644e62..6d9b6d5416 100644 --- a/src/helics/application_api/Federate.cpp +++ b/src/helics/application_api/Federate.cpp @@ -132,6 +132,8 @@ Federate::~Federate() // LCOV_EXCL_START catch (...) // do not allow a throw inside the destructor { + // finalize may throw but we can't allow that + ; } // LCOV_EXCL_STOP } @@ -1542,7 +1544,7 @@ void Federate::registerConnectorInterfacesJsonDetail(Json::Value& json) registerConnectorInterfacesJsonDetail(json["helics"]); } - if (json.isMember("potential_interfaces")) { + if (json.isMember("potential_interfaces") || json.isMember("potential_interface_templates")) { if (!potManager) { potManager = std::make_unique(coreObject.get(), this); } @@ -2060,9 +2062,9 @@ void Federate::logMessage(int level, std::string_view message) const if (coreObject) { coreObject->logMessage(fedID, level, message); } else if (level <= HELICS_LOG_LEVEL_WARNING) { - std::cerr << message << std::endl; + std::cerr << message << '\n'; } else { - std::cout << message << std::endl; + std::cout << message << '\n'; } } diff --git a/src/helics/application_api/PotentialInterfacesManager.cpp b/src/helics/application_api/PotentialInterfacesManager.cpp index a44659ccaf..08417d64f4 100644 --- a/src/helics/application_api/PotentialInterfacesManager.cpp +++ b/src/helics/application_api/PotentialInterfacesManager.cpp @@ -24,34 +24,55 @@ void PotentialInterfacesManager::loadPotentialInterfaces(Json::Value& json) { static const std::set interfaceTypes{ "publications", "inputs", "endpoints", "filters", "translators", "datasinks"}; - if (!json.isMember("potential_interfaces")) { - return; - } - const auto& interfaces = json["potential_interfaces"]; - for (const auto& itype : interfaceTypes) { - if (interfaces.isMember(itype)) { - auto tInterface = interfaces[itype]; - auto& pMap = potInterfaces[itype]; - for (auto& ispec : tInterface) { - auto name = fileops::getName(ispec); - pMap[name] = ispec; + if (json.isMember("potential_interfaces")) { + const auto& interfaces = json["potential_interfaces"]; + for (const auto& itype : interfaceTypes) { + if (interfaces.isMember(itype)) { + auto tInterface = interfaces[itype]; + auto& pMap = potInterfaces[itype]; + for (auto& ispec : tInterface) { + auto name = fileops::getName(ispec); + pMap[name] = ispec; + } + } + std::string tempString = itype; + tempString.pop_back(); + tempString += "_templates"; + if (interfaces.isMember(tempString)) { + auto templateInterfaces = interfaces[tempString]; + auto& tMap = potInterfaceTemplates[itype]; + for (auto& tspec : templateInterfaces) { + auto name = fileops::getName(tspec); + if (name.find("}${") != std::string::npos) { + throw(helics::InvalidParameter( + std::string( + "template key definitions must not be adjacent, they must have separator characters [") + + name + ']')); + } + tMap[name] = tspec; + } } } - std::string tempString = itype; - tempString.pop_back(); - tempString += "_templates"; - if (interfaces.isMember(tempString)) { - auto templateInterfaces = interfaces[tempString]; - auto& tMap = potInterfaceTemplates[itype]; - for (auto& tspec : templateInterfaces) { - auto name = fileops::getName(tspec); - if (name.find("}${") != std::string::npos) { - throw(helics::InvalidParameter( - std::string( - "template key definitions must not be adjacent, they must have separator characters [") + - name + ']')); + } + if (json.isMember("potential_interface_templates")) { + const auto& interfaces = json["potential_interface_templates"]; + for (const auto& itype : interfaceTypes) { + std::string tempString = itype; + tempString.pop_back(); + tempString += "_templates"; + if (interfaces.isMember(tempString)) { + auto templateInterfaces = interfaces[tempString]; + auto& tMap = potInterfaceTemplates[itype]; + for (auto& tspec : templateInterfaces) { + auto name = fileops::getName(tspec); + if (name.find("}${") != std::string::npos) { + throw(helics::InvalidParameter( + std::string( + "template key definitions must not be adjacent, they must have separator characters [") + + name + ']')); + } + tMap[name] = tspec; } - tMap[name] = tspec; } } } diff --git a/src/helics/apps/Connector.cpp b/src/helics/apps/Connector.cpp index 83da3b1a78..32063ccb6e 100644 --- a/src/helics/apps/Connector.cpp +++ b/src/helics/apps/Connector.cpp @@ -20,6 +20,7 @@ SPDX-License-Identifier: BSD-3-Clause #include #include #include +#include #include #include #include @@ -119,13 +120,20 @@ bool TemplateMatcher::loadTemplate(const Json::Value& iTemplate) intermediaries.push_back(templateName.substr(0, tnameIndex)); } std::vector valueNames; - int index{0}; + std::size_t index{0}; while (tnameIndex != std::string::npos) { auto close = templateName.find_first_of('}', tnameIndex); const std::string tname = templateName.substr(tnameIndex + 2, close - tnameIndex - 2); - if (!iTemplate.isMember(tname)) { - return false; + if (iTemplate.isMember("fields")) { + if (!iTemplate["fields"].isMember(tname)) { + return false; + } + } else { + if (!iTemplate.isMember(tname)) { + return false; + } } + valueNames.push_back(tname); tnameIndex = templateName.find("${", close + 1); @@ -143,8 +151,10 @@ bool TemplateMatcher::loadTemplate(const Json::Value& iTemplate) templatePossibilities.resize(valueNames.size()); keys.resize(valueNames.size()); + const Json::Value& fieldRoot = (iTemplate.isMember("fields")) ? iTemplate["fields"] : iTemplate; + for (index = 0; index < valueNames.size(); ++index) { - for (const auto& val : iTemplate[valueNames[index]]) { + for (const auto& val : fieldRoot[valueNames[index]]) { std::pair typeAndUnits; std::string_view keyval; if (val.isArray()) { @@ -167,6 +177,7 @@ bool TemplateMatcher::loadTemplate(const Json::Value& iTemplate) templatePossibilities[index].emplace(keyval, typeAndUnits); } } + initialize(); return true; } @@ -594,15 +605,15 @@ Connector::Connector(std::string_view appName, const FederateInfo& fedInfo): } Connector::Connector(std::string_view appName, - const std::shared_ptr& core, + const std::shared_ptr& coreObj, const FederateInfo& fedInfo): - App(appName, core, fedInfo), core((fed) ? fed->getCorePointer() : nullptr) + App(appName, coreObj, fedInfo), core((fed) ? fed->getCorePointer() : nullptr) { initialSetup(); } -Connector::Connector(std::string_view appName, CoreApp& core, const FederateInfo& fedInfo): - App(appName, core, fedInfo), core((fed) ? fed->getCorePointer() : nullptr) +Connector::Connector(std::string_view appName, CoreApp& coreObj, const FederateInfo& fedInfo): + App(appName, coreObj, fedInfo), core((fed) ? fed->getCorePointer() : nullptr) { initialSetup(); } @@ -645,7 +656,7 @@ bool Connector::addConnectionVector(const std::vector& connection) newTags.push_back(connection[2]); } - for (int ii = 3; ii < connection.size(); ++ii) { + for (std::size_t ii = 3; ii < connection.size(); ++ii) { newTags.push_back(connection[ii]); } addConnection(connection[0], connection[1], direction, newTags); @@ -664,7 +675,7 @@ void Connector::addConnection(std::string_view interface1, } auto iview1 = addInterface(interface1); auto iview2 = addInterface(interface2); - Connection conn{iview1, iview2, direction, std::move(svtags)}; + Connection conn{iview1, iview2, direction, std::move(svtags), nullptr}; if (iview1.compare(0, 6, "REGEX:") == 0) { switch (direction) { case InterfaceDirection::TO_FROM: diff --git a/src/helics/apps/Connector.hpp b/src/helics/apps/Connector.hpp index f99a3f61d2..a13ab731b0 100644 --- a/src/helics/apps/Connector.hpp +++ b/src/helics/apps/Connector.hpp @@ -57,18 +57,18 @@ class HELICS_CXX_EXPORT Connector: public App { explicit Connector(std::string_view name, const FederateInfo& fedInfo); /**constructor taking a federate information structure and using the given core @param name the name of the federate (can be empty to use defaults from fedInfo) -@param core a pointer to core object which the federate can join +@param coreObj a pointer to core object which the federate can join @param fedInfo a federate information structure */ Connector(std::string_view name, - const std::shared_ptr& core, + const std::shared_ptr& coreObj, const FederateInfo& fedInfo); /**constructor taking a federate information structure and using the given core @param name the name of the federate (can be empty to use defaults from fedInfo) -@param core a coreApp object that can be joined +@param coreObj a coreApp object that can be joined @param fedInfo a federate information structure */ - Connector(std::string_view name, CoreApp& core, const FederateInfo& fedInfo); + Connector(std::string_view name, CoreApp& coreObj, const FederateInfo& fedInfo); /**constructor taking a file with the required information @param appName the name of the app @param configString JSON, TOML or text file or JSON string defining the federate information and diff --git a/src/helics/apps/helicsApp.cpp b/src/helics/apps/helicsApp.cpp index 084bb2ca2c..757708b585 100644 --- a/src/helics/apps/helicsApp.cpp +++ b/src/helics/apps/helicsApp.cpp @@ -181,7 +181,7 @@ std::vector AppTextParser::preParseFile(const std::vector& klines) } ++counts[0]; - for (int ii = 0; ii < klines.size(); ++ii) { + for (std::size_t ii = 0; ii < klines.size(); ++ii) { if (str[fc] == klines[ii]) { ++counts[ii + 1]; } diff --git a/src/helics/common/addTargets.cpp b/src/helics/common/addTargets.cpp index ab8a80485d..d843ec7363 100644 --- a/src/helics/common/addTargets.cpp +++ b/src/helics/common/addTargets.cpp @@ -10,7 +10,10 @@ SPDX-License-Identifier: BSD-3-Clause #include "JsonProcessingFunctions.hpp" #include "TomlProcessingFunctions.hpp" +#include +#include #include +#include #include #include @@ -21,12 +24,12 @@ void processOptions(const toml::value& section, const std::function& valueConversion, const std::function& optionAction) { - const auto& t = section.as_table(); - for (const auto& telement : t) { + const auto& table = section.as_table(); + for (const auto& telement : table) { if (telement.second.is_array() || telement.second.is_table()) { continue; } - int32_t index = optionConversion(telement.first); + const int32_t index = optionConversion(telement.first); if (index >= 0) { int32_t val = -1; if (telement.second.is_boolean()) { @@ -51,7 +54,7 @@ void processOptions(const Json::Value& section, if (sIterator->isArray() || sIterator->isObject()) { continue; } - int32_t index = optionConversion(sIterator.name()); + const int32_t index = optionConversion(sIterator.name()); if (index >= 0) { int32_t val = -1; if (sIterator->isBool()) { @@ -67,12 +70,13 @@ void processOptions(const Json::Value& section, } template -static std::pair getTagPair(const TV& tv) +static std::pair getTagPair(const TV& tagValue) { - std::string name = fileops::getName(tv); + const std::string name = fileops::getName(tagValue); if (name.empty()) { } else { - std::string val = fileops::getOrDefault(tv, std::string("value"), std::string_view{}); + const std::string val = + fileops::getOrDefault(tagValue, std::string("value"), std::string_view{}); return std::make_pair(name, val); } @@ -83,26 +87,39 @@ void loadTags(const Json::Value& section, const std::function& tagAction) { if (section.isMember("tags")) { - auto tv = section["tags"]; - if (tv.isArray()) { - for (auto& tp : tv) { - auto pv = getTagPair(tp); - if (!pv.first.empty()) { - tagAction(pv.first, pv.second); + auto tagValue = section["tags"]; + if (tagValue.isArray()) { + for (auto& tagPair : tagValue) { + if (tagPair.isObject()) { + auto pairValues = getTagPair(tagPair); + if (!pairValues.first.empty()) { + tagAction(pairValues.first, pairValues.second); + } + } else if (tagPair.isArray()) { + if (tagPair.size() > 1) { + tagAction(tagPair[0].asString(), tagPair[1].asString()); + } else { + tagAction(tagPair[0].asString(), "1"); + } + } else if (tagPair.isString()) { + tagAction(tagPair.asString(), "1"); } } - } else { - auto pv = getTagPair(tv); - if (!pv.first.empty()) { - tagAction(pv.first, pv.second); - } else if (tv.isObject()) { - auto names = tv.getMemberNames(); + } else if (tagValue.isObject()) { + auto tagPair = getTagPair(tagValue); + if (!tagPair.first.empty()) { + tagAction(tagPair.first, tagPair.second); + } else if (tagValue.isObject()) { + auto names = tagValue.getMemberNames(); for (auto& name : names) { tagAction(name, - (tv[name].isString()) ? tv[name].asString() : - fileops::generateJsonString(tv[name])); + (tagValue[name].isString()) ? + tagValue[name].asString() : + fileops::generateJsonString(tagValue[name])); } } + } else if (tagValue.isString()) { + tagAction("tags", tagValue.asString()); } } } @@ -111,20 +128,20 @@ void loadTags(const toml::value& section, const std::function& tagAction) { if (section.contains("tags")) { - auto tv = section.at("tags"); - if (tv.is_array()) { - for (std::size_t ii = 0; ii < tv.size(); ++ii) { - auto pv = getTagPair(tv[ii]); - if (!pv.first.empty()) { - tagAction(pv.first, pv.second); + auto tagValue = section.at("tags"); + if (tagValue.is_array()) { + for (std::size_t ii = 0; ii < tagValue.size(); ++ii) { + auto tagPair = getTagPair(tagValue[ii]); + if (!tagPair.first.empty()) { + tagAction(tagPair.first, tagPair.second); } } } else { - auto pv = getTagPair(tv); - if (!pv.first.empty()) { - tagAction(pv.first, pv.second); - } else if (tv.is_table()) { - for (auto& values : tv.as_table()) { + auto tagPair = getTagPair(tagValue); + if (!tagPair.first.empty()) { + tagAction(tagPair.first, tagPair.second); + } else if (tagValue.is_table()) { + for (auto& values : tagValue.as_table()) { tagAction(values.first, fileops::tomlAsString(values.second)); } } diff --git a/src/helics/cpp98/Filter.hpp b/src/helics/cpp98/Filter.hpp index 1deca3c937..30fae35cf9 100644 --- a/src/helics/cpp98/Filter.hpp +++ b/src/helics/cpp98/Filter.hpp @@ -114,7 +114,7 @@ class CloningFilter: public Filter { /** construct from underlying filter object*/ explicit CloningFilter(HelicsFilter hfilt) HELICS_NOTHROW: Filter(hfilt) {} /** default constructor*/ - CloningFilter() HELICS_NOTHROW{}; + CloningFilter() HELICS_NOTHROW {}; /** copy constructor*/ CloningFilter(const CloningFilter& filter): Filter(filter) {} /** copy assignment*/ diff --git a/tests/helics/application_api/ValueFederateAdditionalTests.cpp b/tests/helics/application_api/ValueFederateAdditionalTests.cpp index 013409564c..e3a6063f92 100644 --- a/tests/helics/application_api/ValueFederateAdditionalTests.cpp +++ b/tests/helics/application_api/ValueFederateAdditionalTests.cpp @@ -1259,6 +1259,11 @@ TEST(valuefederate, file_and_config) Fed2->enterExecutingModeAsync(); Fed1->enterExecutingModeComplete(); + // check some tags + EXPECT_EQ(Fed1->getTag("tag1"), "1"); + EXPECT_EQ(Fed1->getTag("tag2"), "1"); + EXPECT_EQ(Fed1->getTag("tag3"), "1"); + EXPECT_TRUE(Fed2->getFlagOption(HELICS_FLAG_WAIT_FOR_CURRENT_TIME_UPDATE)); pub1.publish(std::complex(1, 2)); diff --git a/tests/helics/apps/ConnectorTestsPotentialInterfaces.cpp b/tests/helics/apps/ConnectorTestsPotentialInterfaces.cpp index f1b4f49c17..94bb0b9486 100644 --- a/tests/helics/apps/ConnectorTestsPotentialInterfaces.cpp +++ b/tests/helics/apps/ConnectorTestsPotentialInterfaces.cpp @@ -391,6 +391,37 @@ TEST(connector_potential_interfaces, pub_input_template) EXPECT_EQ(conn1.madeConnections(), 1); } +TEST(connector_potential_interfaces, pub_input_template3) +{ + helics::FederateInfo fedInfo(helics::CoreType::TEST); + using helics::apps::InterfaceDirection; + + fedInfo.coreName = newCoreName("ccore_template"); + fedInfo.coreInitString = "-f2 --autobroker"; + fedInfo.setProperty(HELICS_PROPERTY_TIME_PERIOD, 1.0); + helics::apps::Connector conn1("connector1", fedInfo); + conn1.addConnection("obj2/type1", "objA/typeC", InterfaceDirection::FROM_TO); + helics::ValueFederate vfed("c1", fedInfo); + vfed.registerInterfaces(std::string(testdir) + "simple_interfaces_templates3.json"); + auto fut = std::async(std::launch::async, [&conn1]() { conn1.run(); }); + vfed.enterExecutingMode(); + EXPECT_EQ(vfed.getPublicationCount(), 1); + EXPECT_EQ(vfed.getInputCount(), 1); + auto& pub1 = vfed.getPublication(0); + auto& inp1 = vfed.getInput(0); + const double testValue = 3452.562; + EXPECT_EQ(pub1.getDestinationTargetCount(), 1); + pub1.publish(testValue); + auto retTime = vfed.requestTime(5); + EXPECT_EQ(retTime, 1.0); + auto val = inp1.getDouble(); + EXPECT_EQ(val, testValue); + + vfed.finalize(); + fut.get(); + EXPECT_EQ(conn1.madeConnections(), 1); +} + TEST(connector_potential_interfaces, input_pub_template) { helics::FederateInfo fedInfo(helics::CoreType::TEST); diff --git a/tests/helics/apps/test_files/connector/simple_interfaces_templates3.json b/tests/helics/apps/test_files/connector/simple_interfaces_templates3.json new file mode 100644 index 0000000000..202c0a0efd --- /dev/null +++ b/tests/helics/apps/test_files/connector/simple_interfaces_templates3.json @@ -0,0 +1,62 @@ +{ + "potential_interfaces": { + "publications": [ + { + "name": "pub1", + "global": true, + "type": "double" + }, + { + "name": "pub2", + "global": true, + "type": "double" + } + ], + "inputs": [ + { + "name": "inp2", + "global": true, + "type": "double" + }, + { + "name": "inp1", + "global": true, + "type": "double" + } + ] + }, + "potential_interface_templates": { + "publication_templates": [ + { + "name": "${field1}/${field2}", + "fields": { + "field1": ["obj1", "obj2", "obj3"], + "field2": [ + ["type1", "double"], + ["type2", "int"], + ["type3", "double"] + ] + }, + "template": { + "global": true + } + } + ], + "input_templates": [ + { + "name": "${field1}/${field2}", + "fields": { + "field1": ["objA", "objB", "objC"], + "field2": [ + ["typeA", "double"], + ["typeB", "int"], + ["typeC", "double"] + ] + }, + "template": { + "global": true + } + } + ] + } +} diff --git a/tests/helics/test_files/fed1_config.json b/tests/helics/test_files/fed1_config.json index 38d2f7d8b7..7ff96b3ba9 100644 --- a/tests/helics/test_files/fed1_config.json +++ b/tests/helics/test_files/fed1_config.json @@ -17,5 +17,6 @@ "key": "value_2", "type": "complex" } - ] + ], + "tags": ["tag1", "tag2", "tag3"] } diff --git a/vcpkg.json b/vcpkg.json index eee3e66a9e..b00f6a4cad 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,6 +1,6 @@ { "name": "helics", - "version-string": "3.5.2", + "version-string": "3.5.3", "description": "Hierarchical Engine for Large-scale Infrastructure Co-Simulation (HELICS)", "homepage": "https://helics.org/", "default-features": ["zeromq", "ipc", "webserver", "encryption"],