diff --git a/src/helics/core/ActionMessage.cpp b/src/helics/core/ActionMessage.cpp index 1f410052cf..52903b6541 100644 --- a/src/helics/core/ActionMessage.cpp +++ b/src/helics/core/ActionMessage.cpp @@ -697,7 +697,7 @@ std::unique_ptr createMessageFromCommand(ActionMessage&& cmd) static constexpr char unknownStr[] = "unknown"; // Map to translate the action to a description -static constexpr frozen::unordered_map +static constexpr frozen::unordered_map actionStrings = { // priority commands {action_message_def::action_t::cmd_priority_disconnect, "priority_disconnect"}, @@ -742,6 +742,7 @@ static constexpr frozen::unordered_map +#include +#include +#include +#include + +namespace helics { + +static auto nullMessageFunction = [](const ActionMessage& /*unused*/) {}; + +BaseTimeCoordinator::BaseTimeCoordinator(): sendMessageFunction(nullMessageFunction) {} +BaseTimeCoordinator::BaseTimeCoordinator( + std::function userSendMessageFunction): + sendMessageFunction(std::move(userSendMessageFunction)) +{ + if (!sendMessageFunction) { + sendMessageFunction = nullMessageFunction; + } +} + +void BaseTimeCoordinator::setMessageSender( + std::function userSendMessageFunction) +{ + sendMessageFunction = std::move(userSendMessageFunction); + if (!sendMessageFunction) { + sendMessageFunction = nullMessageFunction; + } +} + +void BaseTimeCoordinator::enteringExecMode() +{ + if (executionMode) { + return; + } + checkingExec = true; + if (!dependencies.empty()) { + updateTimeFactors(); + auto res = dependencies.checkForIssues(false); + if (res.first != 0) { + ActionMessage ge(CMD_GLOBAL_ERROR); + ge.dest_id = parent_broker_id; + ge.source_id = mSourceId; + ge.messageID = res.first; + ge.payload = res.second; + sendMessageFunction(ge); + return; + } + } + bool fedOnly = true; + noParent = true; + for (const auto& dep : dependencies) { + if (dep.connection == ConnectionType::parent) { + fedOnly = false; + noParent = false; + break; + } + if (dep.connection == ConnectionType::child && dep.fedID.isBroker()) { + fedOnly = false; + } + } + federatesOnly = fedOnly; + sendTimingInfo(); +} + +void BaseTimeCoordinator::disconnect() +{ + if (disconnected) { + return; + } + + if (dependencies.empty()) { + return; + } + ActionMessage bye(CMD_DISCONNECT); + bye.source_id = mSourceId; + if (dependencies.size() == 1) { + auto& dep = *dependencies.begin(); + if ((dep.dependency && dep.next < Time::maxVal()) || dep.dependent) { + bye.dest_id = dep.fedID; + if (bye.dest_id == mSourceId) { + processTimeMessage(bye); + } else { + sendMessageFunction(bye); + } + } + + } else { + ActionMessage multi(CMD_MULTI_MESSAGE); + for (const auto& dep : dependencies) { + if ((dep.dependency && dep.next < Time::maxVal()) || dep.dependent) { + bye.dest_id = dep.fedID; + if (dep.fedID == mSourceId) { + processTimeMessage(bye); + } else { + appendMessage(multi, bye); + } + } + } + sendMessageFunction(multi); + } + disconnected = true; +} + +void BaseTimeCoordinator::generateDebuggingTimeInfo(Json::Value& base) const +{ + base["dependencies"] = Json::arrayValue; + base["federatesonly"] = federatesOnly; + base["sequenceCounter"] = sequenceCounter; + base["id"] = mSourceId.baseValue(); + for (const auto& dep : dependencies) { + if (dep.dependency) { + Json::Value depblock; + generateJsonOutputDependency(depblock, dep); + base["dependencies"].append(depblock); + } + if (dep.dependent) { + base["dependents"].append(dep.fedID.baseValue()); + } + } +} + +Json::Value BaseTimeCoordinator::grantTimeoutCheck(const ActionMessage& cmd) +{ + for (auto& dep : dependencies) { + if (dep.fedID == cmd.source_id) { + dep.timeoutCount = cmd.counter; + if (cmd.counter == 6) { + Json::Value base; + generateDebuggingTimeInfo(base); + return base; + } + } + } + return Json::nullValue; +} + +bool BaseTimeCoordinator::isDependency(GlobalFederateId ofed) const +{ + return dependencies.isDependency(ofed); +} + +bool BaseTimeCoordinator::addDependency(GlobalFederateId fedID) +{ + if (dependencies.addDependency(fedID)) { + if (fedID == mSourceId) { + auto* dep = dependencies.getDependencyInfo(fedID); + if (dep != nullptr) { + dep->connection = ConnectionType::self; + } + } + return true; + } + return false; +} + +bool BaseTimeCoordinator::addDependent(GlobalFederateId fedID) +{ + return dependencies.addDependent(fedID); +} + +void BaseTimeCoordinator::setAsChild(GlobalFederateId fedID) +{ + if (fedID == mSourceId) { + return; + } + auto* dep = dependencies.getDependencyInfo(fedID); + if (dep != nullptr) { + dep->connection = ConnectionType::child; + } +} + +void BaseTimeCoordinator::setAsParent(GlobalFederateId fedID) +{ + if (fedID == mSourceId) { + return; + } + auto* dep = dependencies.getDependencyInfo(fedID); + if (dep != nullptr) { + dep->connection = ConnectionType::parent; + noParent = false; + } +} + +void BaseTimeCoordinator::setVersion(GlobalFederateId fedID, std::int8_t version) +{ + auto* dep = dependencies.getDependencyInfo(fedID); + if (dep != nullptr) { + dep->timingVersion = version; + } +} + +GlobalFederateId BaseTimeCoordinator::getParent() const +{ + for (const auto& dep : dependencies) { + if (dep.connection == ConnectionType::parent) { + return dep.fedID; + } + } + return {}; +} + +void BaseTimeCoordinator::removeDependency(GlobalFederateId fedID) +{ + dependencies.removeDependency(fedID); +} + +void BaseTimeCoordinator::removeDependent(GlobalFederateId fedID) +{ + dependencies.removeDependent(fedID); +} + +const DependencyInfo* BaseTimeCoordinator::getDependencyInfo(GlobalFederateId ofed) const +{ + return dependencies.getDependencyInfo(ofed); +} + +std::vector BaseTimeCoordinator::getDependencies() const +{ + std::vector deps; + for (const auto& dep : dependencies) { + if (dep.dependency) { + deps.push_back(dep.fedID); + } + } + return deps; +} + +std::vector BaseTimeCoordinator::getDependents() const +{ + std::vector deps; + for (const auto& dep : dependencies) { + if (dep.dependent) { + deps.push_back(dep.fedID); + } + } + return deps; +} + +bool BaseTimeCoordinator::hasActiveTimeDependencies() const +{ + return dependencies.hasActiveTimeDependencies(); +} + +int BaseTimeCoordinator::dependencyCount() const +{ + return dependencies.activeDependencyCount(); +} +/** get a count of the active dependencies*/ +GlobalFederateId BaseTimeCoordinator::getMinDependency() const +{ + return dependencies.getMinDependency(); +} + +void BaseTimeCoordinator::sendTimingInfo() +{ + ActionMessage tinfo(CMD_TIMING_INFO); + tinfo.source_id = mSourceId; + if (nonGranting) { + setActionFlag(tinfo, non_granting_flag); + } + if (delayedTiming) { + setActionFlag(tinfo, delayed_timing_flag); + } + tinfo.setExtraData(TIME_COORDINATOR_VERSION); + + for (const auto& dep : dependencies) { + if (dep.dependent) { + tinfo.dest_id = dep.fedID; + sendMessageFunction(tinfo); + } + } +} + +ActionMessage BaseTimeCoordinator::generateTimeRequest(const TimeData& dep, + GlobalFederateId fed, + std::int32_t responseCode) const +{ + ActionMessage nTime(CMD_TIME_REQUEST); + nTime.source_id = mSourceId; + nTime.dest_id = fed; + nTime.actionTime = dep.next; + switch (dep.mTimeState) { + case TimeState::time_granted: + nTime.setAction(CMD_TIME_GRANT); + break; + case TimeState::time_requested: + nTime.setExtraData(dep.minFed.baseValue()); + nTime.Tdemin = std::min(dep.minDe, dep.Te); + nTime.Te = dep.Te; + nTime.counter = sequenceCounter; + nTime.setExtraDestData(responseCode); + break; + case TimeState::time_requested_iterative: + nTime.setExtraData(dep.minFed.baseValue()); + setIterationFlags(nTime, IterationRequest::ITERATE_IF_NEEDED); + nTime.Tdemin = std::min(dep.minDe, dep.Te); + nTime.Te = dep.Te; + nTime.counter = sequenceCounter; + nTime.setExtraDestData(responseCode); + break; + case TimeState::time_requested_require_iteration: + nTime.setExtraData(dep.minFed.baseValue()); + setIterationFlags(nTime, IterationRequest::FORCE_ITERATION); + nTime.Tdemin = std::min(dep.minDe, dep.Te); + nTime.counter = sequenceCounter; + nTime.Te = dep.Te; + nTime.setExtraDestData(responseCode); + break; + case TimeState::exec_requested: + nTime.setAction(CMD_EXEC_REQUEST); + nTime.actionTime = Time::zeroVal(); + nTime.counter = sequenceCounter; + nTime.setExtraDestData(responseCode); + break; + case TimeState::error: + nTime.setAction(CMD_IGNORE); + // no need to send updates for this + break; + case TimeState::exec_requested_iterative: + nTime.setAction(CMD_EXEC_REQUEST); + setIterationFlags(nTime, IterationRequest::ITERATE_IF_NEEDED); + nTime.setExtraData(dep.minFed.baseValue()); + nTime.counter = sequenceCounter; + nTime.setExtraDestData(responseCode); + break; + case TimeState::exec_requested_require_iteration: + nTime.setAction(CMD_EXEC_REQUEST); + setIterationFlags(nTime, IterationRequest::FORCE_ITERATION); + nTime.setExtraData(dep.minFed.baseValue()); + nTime.counter = sequenceCounter; + nTime.setExtraDestData(responseCode); + break; + case TimeState::initialized: + if (dep.responseSequenceCounter == 0) { + nTime.setAction(CMD_IGNORE); + } else { + nTime.setAction(CMD_EXEC_GRANT); + nTime.setExtraData(dep.minFed.baseValue()); + setIterationFlags(nTime, IterationRequest::ITERATE_IF_NEEDED); + nTime.counter = sequenceCounter; + nTime.setExtraDestData(responseCode); + } + + break; + } + + return nTime; +} + +bool BaseTimeCoordinator::processTimeMessage(const ActionMessage& cmd) +{ + switch (cmd.action()) { + case CMD_DISCONNECT: + case CMD_DISCONNECT_BROKER: + case CMD_DISCONNECT_FED: + case CMD_DISCONNECT_CORE: + case CMD_BROADCAST_DISCONNECT: + removeDependent(cmd.source_id); + break; + default: + break; + } + auto procRes = dependencies.updateTime(cmd); + switch (procRes) { + case DependencyProcessingResult::NOT_PROCESSED: + default: + return false; + case DependencyProcessingResult::PROCESSED: + return true; + case DependencyProcessingResult::PROCESSED_AND_CHECK: { + auto checkRes = dependencies.checkForIssues(false); + if (checkRes.first != 0) { + ActionMessage ge(CMD_GLOBAL_ERROR); + ge.dest_id = parent_broker_id; + ge.source_id = mSourceId; + ge.messageID = checkRes.first; + ge.payload = checkRes.second; + sendMessageFunction(ge); + } + return true; + } + } +} + +void BaseTimeCoordinator::processDependencyUpdateMessage(const ActionMessage& cmd) +{ + bool added{false}; + switch (cmd.action()) { + case CMD_ADD_DEPENDENCY: + added = addDependency(cmd.source_id); + break; + case CMD_REMOVE_DEPENDENCY: + removeDependency(cmd.source_id); + break; + case CMD_ADD_DEPENDENT: + addDependent(cmd.source_id); + break; + case CMD_REMOVE_DEPENDENT: + removeDependent(cmd.source_id); + break; + case CMD_ADD_INTERDEPENDENCY: + added = addDependency(cmd.source_id); + addDependent(cmd.source_id); + break; + case CMD_REMOVE_INTERDEPENDENCY: + removeDependency(cmd.source_id); + removeDependent(cmd.source_id); + break; + case CMD_TIMING_INFO: + dependencies.updateTime(cmd); + break; + default: + break; + } + if (added) { + if (checkActionFlag(cmd, child_flag)) { + setAsChild(cmd.source_id); + } + if (checkActionFlag(cmd, parent_flag)) { + setAsParent(cmd.source_id); + } + if (cmd.counter > 0) { + setVersion(cmd.source_id, cmd.counter); + } + } +} + +} // namespace helics diff --git a/src/helics/core/BaseTimeCoordinator.hpp b/src/helics/core/BaseTimeCoordinator.hpp new file mode 100644 index 0000000000..2b5eebdf9e --- /dev/null +++ b/src/helics/core/BaseTimeCoordinator.hpp @@ -0,0 +1,144 @@ +/* +Copyright (c) 2017-2022, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable +Energy, LLC. See the top-level NOTICE for additional details. All rights reserved. +SPDX-License-Identifier: BSD-3-Clause +*/ +#pragma once + +#include "ActionMessage.hpp" +#include "CoreFederateInfo.hpp" +#include "TimeDependencies.hpp" + +#include "json/forwards.h" +#include +#include +#include +#include +#include + +namespace helics { + +/** class managing the coordination of time in HELICS for forwarding object (cores, brokers) +the time coordinator manages dependencies and computes whether time can advance or enter execution +mode +*/ +class BaseTimeCoordinator { + protected: + // Core::local_federate_id parent = invalid_fed_id; //!< the id for the parent object which + // should also be a ForwardingTimeCoordinator + TimeDependencies dependencies; //!< federates which this Federate is temporally dependent on + /// callback used to send the messages + std::function sendMessageFunction; + /// the identifier for inserting into the source id field of any generated messages; + + GlobalFederateId mSourceId{0}; + std::int32_t sequenceCounter{0}; //!< storage for sequence counter + bool noParent{true}; //!< indicator that the coordinator does not have parents + bool federatesOnly{false}; //!< indicator that the forwarder only operates with federates + bool checkingExec{false}; + bool executionMode{false}; //!< flag that the coordinator has entered the execution Mode + /// flag indicating that a restrictive time policy should be used + bool restrictive_time_policy{false}; + /// specify that the timeCoordinator should not grant times and instead operate in a continuous + /// manner until completion + bool nonGranting{false}; + bool delayedTiming{false}; + bool disconnected{false}; + + public: + static constexpr std::int32_t TIME_COORDINATOR_VERSION{1}; + BaseTimeCoordinator(); + explicit BaseTimeCoordinator(std::function userSendMessageFunction); + virtual ~BaseTimeCoordinator() = default; + /** set the callback function used for the sending messages*/ + void setMessageSender(std::function userSendMessageFunction); + + void setRestrictivePolicy(bool policy) { restrictive_time_policy = policy; } + /** get a list of actual dependencies*/ + std::vector getDependencies() const; + /** get a reference to the dependents vector*/ + std::vector getDependents() const; + + void setSourceId(GlobalFederateId sourceId) { mSourceId = sourceId; } + GlobalFederateId sourceId() const { return mSourceId; } + /** compute updates to time values + and send an update if needed + */ + virtual bool updateTimeFactors() = 0; + + /** take a global id and get a pointer to the dependencyInfo for the other fed + will be nullptr if it doesn't exist + */ + const DependencyInfo* getDependencyInfo(GlobalFederateId ofed) const; + /** check whether a federate is a dependency*/ + bool isDependency(GlobalFederateId ofed) const; + /** check whether a timeCoordinator has any dependencies or dependents*/ + bool empty() const { return dependencies.empty(); } + + protected: + /** generate a timeRequest message based on the dependency info data*/ + ActionMessage generateTimeRequest(const TimeData& dep, + GlobalFederateId fed, + std::int32_t responseCode) const; + /** send the timing info to dependents*/ + void sendTimingInfo(); + + public: + /** process a message related to time + @return a message_process_result if it did anything + */ + bool processTimeMessage(const ActionMessage& cmd); + + /** process a dependency update message*/ + void processDependencyUpdateMessage(const ActionMessage& cmd); + /** add a federate dependency + @return true if it was actually added, false if the federate was already present + */ + virtual bool addDependency(GlobalFederateId fedID); + /** add a dependent federate + @return true if it was actually added, false if the federate was already present + */ + virtual bool addDependent(GlobalFederateId fedID); + /** remove a dependency + @param fedID the identifier of the federate to remove*/ + virtual void removeDependency(GlobalFederateId fedID); + /** remove a dependent + @param fedID the identifier of the federate to remove*/ + virtual void removeDependent(GlobalFederateId fedID); + + void setAsChild(GlobalFederateId fedID); + void setAsParent(GlobalFederateId fedID); + void setVersion(GlobalFederateId fedID, std::int8_t version); + GlobalFederateId getParent() const; + + /** disconnect*/ + void disconnect(); + /** check if entry to the executing state can be granted*/ + virtual MessageProcessingResult + checkExecEntry(GlobalFederateId triggerFed = GlobalFederateId{}) = 0; + + /** function to enter the exec Mode + */ + virtual void enteringExecMode(); + + /** generate a string with the current time status*/ + virtual std::string printTimeStatus() const = 0; + /** generate debugging time information*/ + virtual void generateDebuggingTimeInfo(Json::Value& base) const; + + /** check if there are any active Time dependencies*/ + bool hasActiveTimeDependencies() const; + + /** get a count of the active dependencies*/ + int dependencyCount() const; + /** get a count of the active dependencies*/ + GlobalFederateId getMinDependency() const; + + /** grant timeout check + @return a json Object with debugging info if empty nothing is logged*/ + Json::Value grantTimeoutCheck(const ActionMessage& cmd); + /** get the current next time*/ + virtual Time getNextTime() const = 0; +}; +} // namespace helics diff --git a/src/helics/core/BrokerBase.cpp b/src/helics/core/BrokerBase.cpp index d28b17ada8..5b1e8200fa 100644 --- a/src/helics/core/BrokerBase.cpp +++ b/src/helics/core/BrokerBase.cpp @@ -10,6 +10,7 @@ SPDX-License-Identifier: BSD-3-Clause #include "../common/fmt_format.h" #include "../common/logging.hpp" #include "ForwardingTimeCoordinator.hpp" +#include "GlobalTimeCoordinator.hpp" #include "LogManager.hpp" #include "ProfilerBuffer.hpp" #include "flagOperations.hpp" @@ -145,6 +146,10 @@ std::shared_ptr BrokerBase::generateBaseCLI() "--debugging", debugging, "specify that a broker/core should operate in user debugging mode equivalent to --slow_responding --disable_timer"); + hApp->add_flag( + "--globaltime", + globalTime, + "specify that the broker should use a globalTime coordinator to coordinate a master clock time with all federates"); hApp->add_flag("--observer", observer, "specify that the broker/core should be added as an observer only"); @@ -224,11 +229,13 @@ std::shared_ptr BrokerBase::generateBaseCLI() grantTimeout, "time to wait for a time request to be granted before triggering diagnostic actions; default is in ms (can also be entered as a time " "like '10s' or '45ms')"); - timeout_group->add_option( - "--maxcosimduration", - maxCoSimDuration, - "the maximum time a broker/core should be active, the co-simulation will self terminate if it is still active after this duration, the time resolution is the tick timer (can also be entered as a time " - "like '10s' or '45ms')"); + timeout_group + ->add_option( + "--maxcosimduration", + maxCoSimDuration, + "the maximum time a broker/core should be active, the co-simulation will self terminate if it is still active after this duration, the time resolution is the tick timer (can also be entered as a time " + "like '10s' or '45ms')") + ->multi_option_policy(CLI::MultiOptionPolicy::TakeLast); timeout_group ->add_option("--errordelay,--errortimeout", errorDelay, @@ -288,9 +295,15 @@ void BrokerBase::configureBase() uuid_like = true; } } - timeCoord = std::make_unique(); + if (globalTime) { + timeCoord = std::make_unique(); + hasTimeDependency = true; + } else { + timeCoord = std::make_unique(); + } + timeCoord->setMessageSender([this](const ActionMessage& msg) { addActionMessage(msg); }); - timeCoord->restrictive_time_policy = restrictive_time_policy; + timeCoord->setRestrictivePolicy(restrictive_time_policy); mLogManager->setTransmitCallback([this](ActionMessage&& m) { if (getBrokerState() < BrokerState::terminating) { diff --git a/src/helics/core/BrokerBase.hpp b/src/helics/core/BrokerBase.hpp index 2bb91cbf04..a0f676e00c 100644 --- a/src/helics/core/BrokerBase.hpp +++ b/src/helics/core/BrokerBase.hpp @@ -27,7 +27,7 @@ namespace spdlog { class logger; } namespace helics { -class ForwardingTimeCoordinator; +class BaseTimeCoordinator; class helicsCLI11App; class ProfilerBuffer; class LogBuffer; @@ -81,6 +81,8 @@ class BrokerBase { bool debugging{false}; /// flag indicating that the broker is an observer only bool observer{false}; + /// flag indicating that the broker should use a global time coordinator + bool globalTime{false}; private: /// flag indicating that the main processing loop is running @@ -96,7 +98,7 @@ class BrokerBase { std::atomic messageCounter{0}; protected: - std::unique_ptr timeCoord; //!< object managing the time control + std::unique_ptr timeCoord; //!< object managing the time control gmlc::containers::BlockingPriorityQueue actionQueue; //!< primary routing queue std::shared_ptr mLogManager; //!< object to handle the logging considerations /** enumeration of the possible core states*/ @@ -120,7 +122,8 @@ class BrokerBase { NO_COMMS = 0x01, PING_RESPONSE = 0x02, QUERY_TIMEOUT = 0x04, - GRANT_TIMEOUT = 0x08 + GRANT_TIMEOUT = 0x08, + DISCONNECT_TIMEOUT = 0x10 }; bool noAutomaticID{false}; //!< the broker should not automatically generate an ID bool hasTimeDependency{false}; //!< set to true if the broker has Time dependencies @@ -136,6 +139,8 @@ class BrokerBase { bool enable_profiling{false}; //!< indicator that profiling is enabled /// time when the error condition started; related to the errorDelay decltype(std::chrono::steady_clock::now()) errorTimeStart; + /// time when the disconnect started + decltype(std::chrono::steady_clock::now()) disconnectTime; std::atomic lastErrorCode{0}; //!< storage for last error code std::string lastErrorString; //!< storage for last error string private: diff --git a/src/helics/core/CMakeLists.txt b/src/helics/core/CMakeLists.txt index 590c1734e7..f9d47956f8 100644 --- a/src/helics/core/CMakeLists.txt +++ b/src/helics/core/CMakeLists.txt @@ -21,7 +21,9 @@ set(SRC_FILES ActionMessage.cpp CoreBroker.cpp TimeCoordinator.cpp + BaseTimeCoordinator.cpp ForwardingTimeCoordinator.cpp + GlobalTimeCoordinator.cpp TimeDependencies.cpp HandleManager.cpp FilterCoordinator.cpp @@ -62,7 +64,9 @@ set(INCLUDE_FILES BrokerBase.hpp TimeDependencies.hpp TimeCoordinator.hpp + BaseTimeCoordinator.hpp ForwardingTimeCoordinator.hpp + GlobalTimeCoordinator.hpp loggingHelper.hpp GlobalFederateId.hpp basic_CoreTypes.hpp diff --git a/src/helics/core/CommonCore.cpp b/src/helics/core/CommonCore.cpp index cbb54659a4..fe8a6fe443 100644 --- a/src/helics/core/CommonCore.cpp +++ b/src/helics/core/CommonCore.cpp @@ -12,6 +12,7 @@ SPDX-License-Identifier: BSD-3-Clause #include "../common/fmt_format.h" #include "../common/logging.hpp" #include "ActionMessage.hpp" +#include "BaseTimeCoordinator.hpp" #include "BasicHandleInfo.hpp" #include "CoreFactory.hpp" #include "CoreFederateInfo.hpp" @@ -20,7 +21,6 @@ SPDX-License-Identifier: BSD-3-Clause #include "FilterCoordinator.hpp" #include "FilterFederate.hpp" #include "FilterInfo.hpp" -#include "ForwardingTimeCoordinator.hpp" #include "InputInfo.hpp" #include "LogManager.hpp" #include "PublicationInfo.hpp" @@ -1831,7 +1831,7 @@ void CommonCore::addDependency(LocalFederateId federateID, const std::string& fe { auto* fed = getFederateAt(federateID); if (fed == nullptr) { - throw(InvalidIdentifier("federateID not valid (registerDependency)")); + throw(InvalidIdentifier("federateID not valid (addDependency)")); } ActionMessage search(CMD_SEARCH_DEPENDENCY); search.source_id = fed->global_id.load(); @@ -2926,13 +2926,16 @@ void CommonCore::processPriorityCommand(ActionMessage&& command) global_id = GlobalBrokerId(command.dest_id); global_broker_id_local = GlobalBrokerId(command.dest_id); filterFedID = getSpecialFederateId(global_broker_id_local, 0); - timeCoord->source_id = global_broker_id_local; + timeCoord->setSourceId(global_broker_id_local); higher_broker_id = GlobalBrokerId(command.source_id); transmitDelayedMessages(); timeoutMon->setParentId(higher_broker_id); if (checkActionFlag(command, slow_responding_flag)) { timeoutMon->disableParentPing(); } + if (checkActionFlag(command, indicator_flag)) { + globalTime = true; + } timeoutMon->reset(); if (delayInitCounter < 0 && minFederateCount == 0 && minChildCount == 0) { if (allInitReady()) { @@ -3128,7 +3131,23 @@ void CommonCore::processCommand(ActionMessage&& command) if (isReasonForTick(command.messageID, TickForwardingReasons::QUERY_TIMEOUT)) { checkQueryTimeouts(); } - + if (isReasonForTick(command.messageID, TickForwardingReasons::DISCONNECT_TIMEOUT)) { + auto now = std::chrono::steady_clock::now(); + if (now - disconnectTime > (3 * tickTimer).to_ms()) { + LOG_WARNING(global_broker_id_local, + getIdentifier(), + " disconnect Timer expired forcing disconnect"); + ActionMessage bye(CMD_DISCONNECT_FED_ACK); + bye.source_id = parent_broker_id; + for (auto fed : loopFederates) { + if (fed->getState() != FederateStates::HELICS_FINISHED) { + bye.dest_id = fed->global_id.load(); + fed->addAction(bye); + } + } + addActionMessage(CMD_STOP); + } + } break; case CMD_PING: case CMD_BROKER_PING: // broker ping for core is the same as core @@ -3294,6 +3313,7 @@ void CommonCore::processCommand(ActionMessage&& command) case CMD_REMOVE_DEPENDENT: case CMD_ADD_INTERDEPENDENCY: case CMD_REMOVE_INTERDEPENDENCY: + case CMD_TIMING_INFO: routeMessage(command); break; case CMD_SEND_FOR_FILTER: @@ -3320,98 +3340,11 @@ void CommonCore::processCommand(ActionMessage&& command) break; case CMD_LOG: case CMD_REMOTE_LOG: - if (command.dest_id == global_broker_id_local) { - sendToLogger(parent_broker_id, - command.messageID, - command.getString(0), - command.payload.to_string(), - command.action() == CMD_REMOTE_LOG); - } else { - routeMessage(command); - } - break; case CMD_WARNING: - if (command.dest_id == global_broker_id_local) { - sendToLogger(command.source_id, - LogLevels::WARNING, - command.getString(0), - command.payload.to_string()); - } else { - routeMessage(command); - } - break; case CMD_ERROR: case CMD_LOCAL_ERROR: - if (command.dest_id == global_broker_id_local) { - if (command.source_id == higher_broker_id || - command.source_id == parent_broker_id || command.source_id == gRootBrokerID) { - sendErrorToFederates(command.messageID, command.payload.to_string()); - setErrorState(command.messageID, command.payload.to_string()); - - } else { - sendToLogger(parent_broker_id, - LogLevels::ERROR_LEVEL, - getFederateNameNoThrow(command.source_id), - command.payload.to_string()); - auto fed = loopFederates.find(command.source_id); - if (fed != loopFederates.end()) { - fed->state = operation_state::error; - } else if (command.source_id == filterFedID) { - filterFed->handleMessage(command); - // filterFed-> - } - - if (hasTimeDependency) { - timeCoord->processTimeMessage(command); - } - } - if (terminate_on_error) { - if (getBrokerState() != BrokerState::errored && - getBrokerState() != BrokerState::connected_error) { - sendErrorToFederates(command.messageID, command.payload.to_string()); - setBrokerState(BrokerState::errored); - } - command.setAction(CMD_GLOBAL_ERROR); - command.source_id = global_broker_id_local; - command.dest_id = gRootBrokerID; - transmit(parent_route_id, std::move(command)); - } - } else { - if (command.dest_id == parent_broker_id) { - if (terminate_on_error) { - if (getBrokerState() != BrokerState::errored) { - sendErrorToFederates(command.messageID, command.payload.to_string()); - setBrokerState(BrokerState::errored); - } - command.setAction(CMD_GLOBAL_ERROR); - command.source_id = global_broker_id_local; - command.dest_id = gRootBrokerID; - transmit(parent_route_id, std::move(command)); - break; - } - if (command.source_id.isValid()) { - auto fed = loopFederates.find(command.source_id); - if (fed != loopFederates.end()) { - fed->state = operation_state::error; - } - } - } - routeMessage(command); - } - break; case CMD_GLOBAL_ERROR: - - if (getBrokerState() == BrokerState::connecting) { - processDisconnect(); - } - setErrorState(command.messageID, command.payload.to_string()); - if (isConnected()) { - sendErrorToFederates(command.messageID, command.payload.to_string()); - if (!(command.source_id == higher_broker_id || - command.source_id == gRootBrokerID)) { - transmit(parent_route_id, std::move(command)); - } - } + processLogAndErrorCommand(command); break; case CMD_DATA_LINK: { auto* pub = loopHandles.getPublication(command.name()); @@ -3564,7 +3497,7 @@ void CommonCore::processCommand(ActionMessage&& command) } loopFederates.apply([&command](auto& fed) { fed->addAction(command); }); - if (filterFed != nullptr && filterTiming) { + if (filterFed != nullptr && (filterTiming || globalTime)) { filterFed->handleMessage(command); } timeCoord->enteringExecMode(); @@ -3645,6 +3578,9 @@ void CommonCore::registerInterface(ActionMessage& command) case CMD_REG_PUB: break; case CMD_REG_ENDPOINT: + if (globalTime) { + break; + } if (timeCoord->addDependency(command.source_id)) { auto* fed = getFederateCore(command.source_id); if (fed != nullptr) { @@ -3726,6 +3662,9 @@ void CommonCore::generateFilterFederate() newFed.setExtraData(fid.baseValue()); newFed.name(getIdentifier() + "_filters"); transmit(getRoute(higher_broker_id), newFed); + if (globalTime) { + filterFed->useGlobalTimeCoordinator(true); + } } void CommonCore::connectFilterTiming() @@ -3734,26 +3673,46 @@ void CommonCore::connectFilterTiming() return; } filterTiming = true; + auto fid = filterFedID.load(); - if (timeCoord->addDependent(higher_broker_id)) { - ActionMessage add(CMD_ADD_INTERDEPENDENCY, global_broker_id_local, higher_broker_id); - setActionFlag(add, child_flag); - transmit(getRoute(higher_broker_id), add); - timeCoord->addDependency(higher_broker_id); - timeCoord->setAsParent(higher_broker_id); - } - // now add the filterFederate as a timeDependency - timeCoord->addDependency(fid); - timeCoord->setAsChild(fid); - ActionMessage ad(CMD_ADD_DEPENDENT); - setActionFlag(ad, parent_flag); - ad.dest_id = fid; - ad.source_id = global_broker_id_local; - filterFed->handleMessage(ad); - // TODO(PT) this should be conditional as it probably isn't needed in all cases - ad.setAction(CMD_ADD_DEPENDENCY); - timeCoord->addDependent(fid); - filterFed->handleMessage(ad); + if (globalTime) { + ActionMessage ad(CMD_ADD_DEPENDENT); + setActionFlag(ad, parent_flag); + ad.dest_id = fid; + ad.source_id = gRootBrokerID; + filterFed->handleMessage(ad); + + ad.setAction(CMD_ADD_DEPENDENCY); + filterFed->handleMessage(ad); + clearActionFlag(ad, parent_flag); + setActionFlag(ad, child_flag); + ad.swapSourceDest(); + transmit(parent_route_id, ad); + ad.setAction(CMD_ADD_DEPENDENT); + transmit(parent_route_id, ad); + } else { + if (timeCoord->addDependent(higher_broker_id)) { + ActionMessage add(CMD_ADD_INTERDEPENDENCY, global_broker_id_local, higher_broker_id); + setActionFlag(add, child_flag); + transmit(getRoute(higher_broker_id), add); + timeCoord->addDependency(higher_broker_id); + timeCoord->setAsParent(higher_broker_id); + } + // now add the filterFederate as a timeDependency + timeCoord->addDependency(fid); + timeCoord->setAsChild(fid); + ActionMessage ad(CMD_ADD_DEPENDENT); + setActionFlag(ad, parent_flag); + ad.dest_id = fid; + ad.source_id = global_broker_id_local; + filterFed->handleMessage(ad); + // TODO(PT) this should be conditional as it probably isn't needed in all cases + ad.setAction(CMD_ADD_DEPENDENCY); + timeCoord->addDependent(fid); + timeCoord->setAsChild(fid); + filterFed->handleMessage(ad); + } + // filterTiming = true; } @@ -3978,11 +3937,13 @@ void CommonCore::addTargetToInterface(ActionMessage& command) } filterFed->processFilterInfo(command); if (command.source_id != global_broker_id_local) { - if (!checkActionFlag(command, error_flag)) { - auto* fed = getFederateCore(command.dest_id); - if (fed != nullptr) { - command.setAction(CMD_ADD_DEPENDENT); - fed->addAction(command); + if (!globalTime) { + if (!checkActionFlag(command, error_flag)) { + auto* fed = getFederateCore(command.dest_id); + if (fed != nullptr) { + command.setAction(CMD_ADD_DEPENDENT); + fed->addAction(command); + } } } } @@ -4192,6 +4153,107 @@ void CommonCore::checkDependencies() } } +void CommonCore::processLogAndErrorCommand(ActionMessage& cmd) +{ + switch (cmd.action()) { + case CMD_LOG: + case CMD_REMOTE_LOG: + if (cmd.dest_id == global_broker_id_local) { + sendToLogger(parent_broker_id, + cmd.messageID, + cmd.getString(0), + cmd.payload.to_string(), + cmd.action() == CMD_REMOTE_LOG); + } else { + routeMessage(cmd); + } + break; + case CMD_WARNING: + if (cmd.dest_id == global_broker_id_local) { + sendToLogger(cmd.source_id, + LogLevels::WARNING, + cmd.getString(0), + cmd.payload.to_string()); + } else { + routeMessage(cmd); + } + break; + case CMD_ERROR: + case CMD_LOCAL_ERROR: + if (cmd.dest_id == global_broker_id_local) { + if (cmd.source_id == higher_broker_id || cmd.source_id == parent_broker_id || + cmd.source_id == gRootBrokerID) { + sendErrorToFederates(cmd.messageID, cmd.payload.to_string()); + setErrorState(cmd.messageID, cmd.payload.to_string()); + + } else { + sendToLogger(parent_broker_id, + LogLevels::ERROR_LEVEL, + getFederateNameNoThrow(cmd.source_id), + cmd.payload.to_string()); + auto fed = loopFederates.find(cmd.source_id); + if (fed != loopFederates.end()) { + fed->state = operation_state::error; + } else if (cmd.source_id == filterFedID) { + filterFed->handleMessage(cmd); + // filterFed-> + } + + if (hasTimeDependency) { + timeCoord->processTimeMessage(cmd); + } + } + if (terminate_on_error) { + if (getBrokerState() != BrokerState::errored && + getBrokerState() != BrokerState::connected_error) { + sendErrorToFederates(cmd.messageID, cmd.payload.to_string()); + setBrokerState(BrokerState::errored); + } + cmd.setAction(CMD_GLOBAL_ERROR); + cmd.source_id = global_broker_id_local; + cmd.dest_id = gRootBrokerID; + transmit(parent_route_id, std::move(cmd)); + } + } else { + if (cmd.dest_id == parent_broker_id) { + if (terminate_on_error) { + if (getBrokerState() != BrokerState::errored) { + sendErrorToFederates(cmd.messageID, cmd.payload.to_string()); + setBrokerState(BrokerState::errored); + } + cmd.setAction(CMD_GLOBAL_ERROR); + cmd.source_id = global_broker_id_local; + cmd.dest_id = gRootBrokerID; + transmit(parent_route_id, std::move(cmd)); + break; + } + if (cmd.source_id.isValid()) { + auto fed = loopFederates.find(cmd.source_id); + if (fed != loopFederates.end()) { + fed->state = operation_state::error; + } + } + } + routeMessage(cmd); + } + break; + case CMD_GLOBAL_ERROR: + + if (getBrokerState() == BrokerState::connecting) { + processDisconnect(); + } + setErrorState(cmd.messageID, cmd.payload.to_string()); + if (isConnected()) { + sendErrorToFederates(cmd.messageID, cmd.payload.to_string()); + if (!(cmd.source_id == higher_broker_id || cmd.source_id == gRootBrokerID)) { + transmit(parent_route_id, std::move(cmd)); + } + } + break; + default: + break; + } +} void CommonCore::processDisconnectCommand(ActionMessage& cmd) { switch (cmd.action()) { @@ -4483,7 +4545,7 @@ void CommonCore::processQueryCommand(ActionMessage& cmd) break; case CMD_QUERY_ORDERED: force_ordered = true; - // FALLTHROUGH + [[fallthrough]]; case CMD_QUERY: if (cmd.dest_id == parent_broker_id) { if (cmd.source_id == gDirectCoreId) { @@ -4704,6 +4766,8 @@ bool CommonCore::checkAndProcessDisconnect() ActionMessage dis(CMD_DISCONNECT); dis.source_id = global_broker_id_local; transmit(parent_route_id, dis); + setTickForwarding(TickForwardingReasons::DISCONNECT_TIMEOUT, true); + disconnectTime = std::chrono::steady_clock::now(); return true; } if (hasFilters) { diff --git a/src/helics/core/CommonCore.hpp b/src/helics/core/CommonCore.hpp index d28cd98f83..5396c7c919 100644 --- a/src/helics/core/CommonCore.hpp +++ b/src/helics/core/CommonCore.hpp @@ -400,6 +400,8 @@ class CommonCore: public Core, public BrokerBase { void processDisconnectCommand(ActionMessage& cmd); /** handle the processing for a query command*/ void processQueryCommand(ActionMessage& cmd); + /** handle logging and error related commands*/ + void processLogAndErrorCommand(ActionMessage& cmd); /** check if a newly registered subscription has a local publication if it does return true*/ bool checkForLocalPublication(ActionMessage& cmd); diff --git a/src/helics/core/CoreBroker.cpp b/src/helics/core/CoreBroker.cpp index a673ebb7e7..3d3c883eee 100644 --- a/src/helics/core/CoreBroker.cpp +++ b/src/helics/core/CoreBroker.cpp @@ -11,8 +11,8 @@ SPDX-License-Identifier: BSD-3-Clause #include "../common/JsonProcessingFunctions.hpp" #include "../common/fmt_format.h" #include "../common/logging.hpp" +#include "BaseTimeCoordinator.hpp" #include "BrokerFactory.hpp" -#include "ForwardingTimeCoordinator.hpp" #include "LogManager.hpp" #include "TimeoutMonitor.h" #include "fileConnections.hpp" @@ -254,6 +254,9 @@ void CoreBroker::brokerRegistration(ActionMessage&& command) if (no_ping) { setActionFlag(brokerReply, slow_responding_flag); } + if (globalTime) { + setActionFlag(brokerReply, indicator_flag); + } transmit(brk->route, brokerReply); return; } @@ -428,6 +431,9 @@ void CoreBroker::brokerRegistration(ActionMessage&& command) if (no_ping) { setActionFlag(brokerReply, slow_responding_flag); } + if (globalTime) { + setActionFlag(brokerReply, indicator_flag); + } transmit(route, brokerReply); LOG_CONNECTIONS(global_broker_id_local, getIdentifier(), @@ -532,6 +538,14 @@ void CoreBroker::fedRegistration(ActionMessage&& command) if (checkActionFlag(command, child_flag)) { setActionFlag(fedReply, child_flag); } + if (globalTime) { + setActionFlag(fedReply, indicator_flag); + if (!checkActionFlag(command, non_counting_flag)) { + timeCoord->addDependency(global_fedid); + timeCoord->addDependent(global_fedid); + timeCoord->setAsChild(global_fedid); + } + } transmit(route_id, fedReply); LOG_CONNECTIONS(global_broker_id_local, getIdentifier(), @@ -571,7 +585,7 @@ void CoreBroker::processPriorityCommand(ActionMessage&& command) case CMD_BROKER_SETUP: { global_broker_id_local = global_id.load(); isRootc = _isRoot.load(); - timeCoord->source_id = global_broker_id_local; + timeCoord->setSourceId(global_broker_id_local); connectionEstablished = true; if (!earlyMessages.empty()) { for (auto& M : earlyMessages) { @@ -634,7 +648,7 @@ void CoreBroker::processPriorityCommand(ActionMessage&& command) global_broker_id_local = command.dest_id; global_id.store(global_broker_id_local); higher_broker_id = command.source_id; - timeCoord->source_id = global_broker_id_local; + timeCoord->setSourceId(global_broker_id_local); transmitDelayedMessages(); mBrokers.apply([localid = global_broker_id_local](auto& brk) { if (!brk._nonLocal) { @@ -903,11 +917,21 @@ void CoreBroker::sendDisconnect(action_message_def::action_t disconnectType) if (brk.parent == global_broker_id_local) { this->routeMessage(bye, brk.global_id); brk.state = connection_state::disconnected; + brk._sent_disconnect_ack = true; } if (hasTimeDependency) { timeCoord->removeDependency(brk.global_id); timeCoord->removeDependent(brk.global_id); } + } else if (brk.state == connection_state::disconnected) { + if (brk._sent_disconnect_ack == false) { + ActionMessage dis((brk._core) ? CMD_DISCONNECT_CORE_ACK : + CMD_DISCONNECT_BROKER_ACK); + dis.source_id = global_broker_id_local; + dis.dest_id = brk.global_id; + transmit(brk.route, dis); + brk._sent_disconnect_ack = true; + } } }); if (hasTimeDependency) { @@ -1174,9 +1198,14 @@ void CoreBroker::processCommand(ActionMessage&& command) } } } else if (command.source_id == global_broker_id_local) { - for (auto& dep : timeCoord->getDependents()) { - routeMessage(command, dep); + if (command.dest_id.isValid()) { + transmit(getRoute(command.dest_id), command); + } else { + for (auto& dep : timeCoord->getDependents()) { + routeMessage(command, dep); + } } + } else if (command.dest_id == mTimeMonitorLocalFederateId) { processTimeMonitorMessage(command); } else { @@ -1328,6 +1357,7 @@ void CoreBroker::processCommand(ActionMessage&& command) case CMD_REMOVE_DEPENDENT: case CMD_ADD_INTERDEPENDENCY: case CMD_REMOVE_INTERDEPENDENCY: + case CMD_TIMING_INFO: if (command.dest_id != global_broker_id_local) { routeMessage(command); } else { @@ -1875,7 +1905,7 @@ void CoreBroker::addEndpoint(ActionMessage& m) if (!isRootc) { transmit(parent_route_id, m); - if (!hasTimeDependency) { + if (!hasTimeDependency && !globalTime) { if (timeCoord->addDependency(higher_broker_id)) { hasTimeDependency = true; ActionMessage add(CMD_ADD_INTERDEPENDENCY, @@ -1916,11 +1946,13 @@ void CoreBroker::addFilter(ActionMessage& m) transmit(parent_route_id, m); if (!hasFilters) { hasFilters = true; - if (timeCoord->addDependent(higher_broker_id)) { - hasTimeDependency = true; - ActionMessage add(CMD_ADD_DEPENDENCY, global_broker_id_local, higher_broker_id); - setActionFlag(add, child_flag); - transmit(parent_route_id, add); + if (!globalTime) { + if (timeCoord->addDependent(higher_broker_id)) { + hasTimeDependency = true; + ActionMessage add(CMD_ADD_DEPENDENCY, global_broker_id_local, higher_broker_id); + setActionFlag(add, child_flag); + transmit(parent_route_id, add); + } } } } else { diff --git a/src/helics/core/FederateState.cpp b/src/helics/core/FederateState.cpp index 3875799540..654025b684 100644 --- a/src/helics/core/FederateState.cpp +++ b/src/helics/core/FederateState.cpp @@ -406,9 +406,7 @@ std::optional std::optional optAct; switch (action.action()) { case CMD_REQUEST_CURRENT_TIME: - optAct->setAction(CMD_DISCONNECT); - optAct->dest_id = action.source_id; - optAct->source_id = global_id.load(); + optAct = ActionMessage(CMD_DISCONNECT, global_id.load(), action.source_id); break; default: break; @@ -1224,7 +1222,11 @@ MessageProcessingResult FederateState::processActionMessage(ActionMessage& cmd) prettyPrintString(cmd), cmd.actionTime, time_granted)); + auto qres = processQueryActual("global_time_debugging"); + qres.insert(0, "TIME DEBUGGING::"); + LOG_WARNING(qres); } + if (state <= HELICS_EXECUTING) { timeCoord->processTimeMessage(cmd); } @@ -1350,7 +1352,9 @@ MessageProcessingResult FederateState::processActionMessage(ActionMessage& cmd) std::string(cmd.name()), cmd.getString(typeStringLoc), cmd.getString(unitStringLoc))) { - addDependency(cmd.source_id); + if (!usingGlobalTime) { + addDependency(cmd.source_id); + } } } else { auto* eptI = interfaceInformation.getEndpoint(cmd.dest_handle); @@ -1358,7 +1362,9 @@ MessageProcessingResult FederateState::processActionMessage(ActionMessage& cmd) eptI->addSourceTarget(cmd.getSource(), std::string(cmd.name()), cmd.getString(typeStringLoc)); - addDependency(cmd.source_id); + if (!usingGlobalTime) { + addDependency(cmd.source_id); + } } } } break; @@ -1366,7 +1372,9 @@ MessageProcessingResult FederateState::processActionMessage(ActionMessage& cmd) auto* pubI = interfaceInformation.getPublication(cmd.dest_handle); if (pubI != nullptr) { if (pubI->addSubscriber(cmd.getSource())) { - addDependent(cmd.source_id); + if (!usingGlobalTime) { + addDependent(cmd.source_id); + } } } } break; @@ -1421,9 +1429,16 @@ MessageProcessingResult FederateState::processActionMessage(ActionMessage& cmd) errorString = commandErrorString(cmd.messageID); return MessageProcessingResult::ERROR_RESULT; } + if (checkActionFlag(cmd, indicator_flag)) { + usingGlobalTime = true; + addDependent(gRootBrokerID); + addDependency(gRootBrokerID); + timeCoord->setAsParent(gRootBrokerID); + timeCoord->globalTime = true; + } global_id = cmd.dest_id; interfaceInformation.setGlobalId(cmd.dest_id); - timeCoord->source_id = global_id; + timeCoord->setSourceId(global_id); return MessageProcessingResult::NEXT_STEP; } break; @@ -2157,7 +2172,9 @@ std::string FederateState::processQueryActual(std::string_view query) const Json::Value base; addHeader(base); base["state"] = fedStateString(state.load()); - timeCoord->generateDebuggingTimeInfo(base); + if (!timeCoord->empty()) { + timeCoord->generateDebuggingTimeInfo(base); + } return fileops::generateJsonString(base); } if (query == "timeconfig") { diff --git a/src/helics/core/FederateState.hpp b/src/helics/core/FederateState.hpp index 4ba64e3706..b32de853e4 100644 --- a/src/helics/core/FederateState.hpp +++ b/src/helics/core/FederateState.hpp @@ -111,6 +111,7 @@ class FederateState { //!< requesting state waiting to grant bool terminate_on_error{false}; //!< indicator that if the federate encounters a configuration //!< error it should cause a co-simulation abort + bool usingGlobalTime{false}; // indicator that the federation is using global time /** counter for the number of times time or execution mode has been granted */ std::uint32_t mGrantCount{0}; // this is intended to allow wrapping /** message timer object for real time operations and timeouts */ diff --git a/src/helics/core/FilterFederate.cpp b/src/helics/core/FilterFederate.cpp index 31e62fbaf7..f76d6dc9e6 100644 --- a/src/helics/core/FilterFederate.cpp +++ b/src/helics/core/FilterFederate.cpp @@ -28,10 +28,9 @@ FilterFederate::FilterFederate(GlobalFederateId fedID, mCoreID(coreID), mName(std::move(name)), /*mCore(core),*/ mCoord([this](const ActionMessage& msg) { routeMessage(msg); }) { - mCoord.source_id = fedID; + mCoord.setSourceId(fedID); mCoord.setOptionFlag(helics::defs::Flags::EVENT_TRIGGERED, true); mCoord.specifyNonGranting(true); - mCoord.specifyNonGranting(true); } FilterFederate::~FilterFederate() @@ -76,6 +75,7 @@ void FilterFederate::processMessageFilter(ActionMessage& cmd) // deal with local source filters auto* FiltI = getFilterInfo(cmd.getDest()); if (FiltI != nullptr) { + mCoord.triggered = true; if ((!checkActionFlag(*FiltI, disconnected_flag)) && (FiltI->filterOp)) { if (FiltI->cloning) { auto new_messages = @@ -326,6 +326,7 @@ void FilterFederate::processDestFilterReturn(ActionMessage& command) std::pair FilterFederate::executeFilter(ActionMessage& command, FilterInfo* filt) { + mCoord.triggered = true; if (filt->core_id == mFedID) { if (filt->cloning) { // cloning filter returns a vector @@ -454,6 +455,7 @@ void FilterFederate::runCloningDestinationFilters(const FilterCoordinator* fcoor const BasicHandleInfo* handle, const ActionMessage& command) const { + mCoord.triggered = true; // now go to the cloning filters for (auto* clFilter : fcoord->cloningDestFilters) { if (checkActionFlag(*clFilter, disconnected_flag)) { @@ -516,7 +518,7 @@ void FilterFederate::clearTimeReturn(int32_t id) } else { } if (recheckTime) { - minReturnTime = Time::maxVal(); + minReturnTime = cBigTime; for (const auto& tBP : timeBlockProcesses) { if (tBP.second < minReturnTime) { minReturnTime = tBP.second; @@ -550,10 +552,7 @@ void FilterFederate::handleMessage(ActionMessage& command) } break; case HELICS_EXECUTING: - mCoord.timeRequest(Time::maxVal(), - IterationRequest::NO_ITERATIONS, - Time::maxVal(), - Time::maxVal()); + mCoord.timeRequest(cBigTime, IterationRequest::NO_ITERATIONS, cBigTime, cBigTime); break; case HELICS_FINISHED: break; @@ -629,13 +628,24 @@ void FilterFederate::handleMessage(ActionMessage& command) auto* filtI = getFilterInfo(mFedID, command.dest_handle); if (filtI != nullptr) { filtI->sourceTargets.emplace_back(command.source_id, command.source_handle); - mCoord.addDependency(command.source_id); + if (!usingGlobalTime) { + mCoord.addDependency(command.source_id); + } } auto* filthandle = mHandles->getFilter(command.dest_handle); if (filthandle != nullptr) { filthandle->used = true; } } break; + case CMD_DISCONNECT: + if (!usingGlobalTime && !mCoord.hasActiveTimeDependencies()) { + mCoord.disconnect(); + ActionMessage disconnect(CMD_DISCONNECT); + disconnect.source_id = mFedID; + disconnect.dest_id = parent_broker_id; + mQueueMessage(disconnect); + } + break; case CMD_REG_FILTER: processFilterInfo(command); break; @@ -648,7 +658,9 @@ void FilterFederate::handleMessage(ActionMessage& command) filtI->sourceTargets.emplace_back(command.getSource()); } if (!checkActionFlag(command, error_flag)) { - mCoord.addDependency(command.source_id); + if (!usingGlobalTime) { + mCoord.addDependency(command.source_id); + } } } @@ -1017,7 +1029,9 @@ std::string FilterFederate::query(const std::string& queryStr) const base["id"] = mFedID.baseValue(); base["parent"] = mCoreID.baseValue(); base["state"] = fedStateString(current_state); - mCoord.generateDebuggingTimeInfo(base); + if (!mCoord.empty()) { + mCoord.generateDebuggingTimeInfo(base); + } return fileops::generateJsonString(base); } if (queryStr == "timeconfig") { diff --git a/src/helics/core/FilterFederate.hpp b/src/helics/core/FilterFederate.hpp index 4fa0c411dc..c93cd0b32c 100644 --- a/src/helics/core/FilterFederate.hpp +++ b/src/helics/core/FilterFederate.hpp @@ -61,6 +61,8 @@ class FilterFederate { /** counter for the number of messages that have been sent, nothing magical about 54 just a * number bigger than 1 to prevent confusion */ std::atomic messageCounter{54}; + /// indicator to use a globalTimeCoordinator + bool usingGlobalTime{false}; /// storage for all the filters gmlc::containers::MappedPointerVector filters; // bool hasTiming{false}; @@ -126,6 +128,12 @@ class FilterFederate { /** check if the filter federate has active time dependencies other than parent*/ bool hasActiveTimeDependencies() const; + void useGlobalTimeCoordinator(bool value) + { + usingGlobalTime = value; + mCoord.globalTime = value; + } + private: void routeMessage(const ActionMessage& msg); /** get a filtering function object*/ diff --git a/src/helics/core/ForwardingTimeCoordinator.cpp b/src/helics/core/ForwardingTimeCoordinator.cpp index 0996359851..68e056951b 100644 --- a/src/helics/core/ForwardingTimeCoordinator.cpp +++ b/src/helics/core/ForwardingTimeCoordinator.cpp @@ -19,86 +19,26 @@ SPDX-License-Identifier: BSD-3-Clause #include namespace helics { -void ForwardingTimeCoordinator::enteringExecMode() -{ - if (executionMode) { - return; - } - checkingExec = true; - if (!dependencies.empty()) { - updateTimeFactors(); - auto res = dependencies.checkForIssues(false); - if (res.first != 0) { - ActionMessage ge(CMD_GLOBAL_ERROR); - ge.dest_id = parent_broker_id; - ge.source_id = source_id; - ge.messageID = res.first; - ge.payload = res.second; - sendMessageFunction(ge); - return; - } - } - bool fedOnly = true; - noParent = true; - for (const auto& dep : dependencies) { - if (dep.connection == ConnectionType::parent) { - fedOnly = false; - noParent = false; - break; - } - if (dep.connection == ConnectionType::child && dep.fedID.isBroker()) { - fedOnly = false; - } - } - federatesOnly = fedOnly; -} -void ForwardingTimeCoordinator::disconnect() +bool ForwardingTimeCoordinator::updateTimeFactors() { - if (sendMessageFunction) { - if (dependencies.empty()) { - return; - } - ActionMessage bye(CMD_DISCONNECT); - bye.source_id = source_id; - if (dependencies.size() == 1) { - auto& dep = *dependencies.begin(); - if ((dep.dependency && dep.next < Time::maxVal()) || dep.dependent) { - bye.dest_id = dep.fedID; - if (bye.dest_id == source_id) { - processTimeMessage(bye); - } else { - sendMessageFunction(bye); - } - } + auto mTimeUpstream = generateMinTimeUpstream( + dependencies, restrictive_time_policy, mSourceId, NoIgnoredFederates, sequenceCounter); + auto mTimeDownstream = (noParent) ? mTimeUpstream : + generateMinTimeDownstream(dependencies, + restrictive_time_policy, + mSourceId, + NoIgnoredFederates, + sequenceCounter); - } else { - ActionMessage multi(CMD_MULTI_MESSAGE); - for (const auto& dep : dependencies) { - if ((dep.dependency && dep.next < Time::maxVal()) || dep.dependent) { - bye.dest_id = dep.fedID; - if (dep.fedID == source_id) { - processTimeMessage(bye); - } else { - appendMessage(multi, bye); - } - } - } - sendMessageFunction(multi); - } + bool updateUpStream{false}; + bool updateDownStream{false}; + if (mTimeUpstream.mTimeState > TimeState::exec_requested || !executionMode) { + updateUpStream = upstream.update(mTimeUpstream); + } + if (mTimeDownstream.mTimeState > TimeState::exec_requested || !executionMode) { + updateDownStream = downstream.update(mTimeDownstream); } -} - -void ForwardingTimeCoordinator::updateTimeFactors() -{ - auto mTimeUpstream = generateMinTimeUpstream(dependencies, restrictive_time_policy, source_id); - auto mTimeDownstream = (noParent) ? - mTimeUpstream : - generateMinTimeDownstream(dependencies, restrictive_time_policy, source_id); - - bool updateUpstream = upstream.update(mTimeUpstream); - - bool updateDownstream = downstream.update(mTimeDownstream); if (upstream.mTimeState == TimeState::time_requested) { if (upstream.minDe < downstream.minDe) { @@ -114,36 +54,49 @@ void ForwardingTimeCoordinator::updateTimeFactors() } } - if (updateUpstream) { - auto upd = generateTimeRequest(upstream, GlobalFederateId{}); + sequenceCounter = upstream.sequenceCounter; + if (updateUpStream) { + auto upd = + generateTimeRequest(upstream, GlobalFederateId{}, upstream.responseSequenceCounter); if (upd.action() != CMD_IGNORE) { transmitTimingMessagesUpstream(upd); } } - if (updateDownstream) { + if (updateDownStream) { if (dependencies.hasDelayedDependency() && downstream.minFed == dependencies.delayedDependency()) { - auto upd = generateTimeRequest(downstream, GlobalFederateId{}); + auto upd = generateTimeRequest(downstream, GlobalFederateId{}, 0); if (upd.action() != CMD_IGNORE) { transmitTimingMessagesDownstream(upd, downstream.minFed); } - auto td = generateMinTimeUpstream(dependencies, - restrictive_time_policy, - source_id, - downstream.minFed); + auto td = generateMinTimeUpstream( + dependencies, restrictive_time_policy, mSourceId, downstream.minFed, 0); DependencyInfo di; di.update(td); - auto upd_delayed = generateTimeRequest(di, downstream.minFed); + auto upd_delayed = + generateTimeRequest(di, downstream.minFed, di.responseSequenceCounter); if (sendMessageFunction) { sendMessageFunction(upd_delayed); } } else { - auto upd = generateTimeRequest(downstream, GlobalFederateId{}); + auto upd = generateTimeRequest(downstream, GlobalFederateId{}, 0); if (upd.action() != CMD_IGNORE) { transmitTimingMessagesDownstream(upd); } } + } else if (dependencies.hasDelayedDependency() && + mTimeDownstream.minFed == dependencies.delayedDependency() && executionMode) { + auto td = generateMinTimeUpstream( + dependencies, restrictive_time_policy, mSourceId, mTimeDownstream.minFed, 0); + DependencyInfo di; + di.update(td); + auto upd_delayed = + generateTimeRequest(di, mTimeDownstream.minFed, di.responseSequenceCounter); + if (sendMessageFunction) { + sendMessageFunction(upd_delayed); + } } + return (updateUpStream || updateDownStream); } void ForwardingTimeCoordinator::generateDebuggingTimeInfo(Json::Value& base) const @@ -156,19 +109,7 @@ void ForwardingTimeCoordinator::generateDebuggingTimeInfo(Json::Value& base) con Json::Value downBlock; generateJsonOutputTimeData(downBlock, downstream); base["downstream"] = downBlock; - - base["dependencies"] = Json::arrayValue; - base["federatesonly"] = federatesOnly; - for (const auto& dep : dependencies) { - if (dep.dependency) { - Json::Value depblock; - generateJsonOutputDependency(depblock, dep); - base["dependencies"].append(depblock); - } - if (dep.dependent) { - base["dependents"].append(dep.fedID.baseValue()); - } - } + BaseTimeCoordinator::generateDebuggingTimeInfo(base); } std::string ForwardingTimeCoordinator::printTimeStatus() const @@ -179,106 +120,7 @@ std::string ForwardingTimeCoordinator::printTimeStatus() const static_cast(downstream.minDe)); } -Json::Value ForwardingTimeCoordinator::grantTimeoutCheck(const ActionMessage& cmd) -{ - for (auto& dep : dependencies) { - if (dep.fedID == cmd.source_id) { - dep.timeoutCount = cmd.counter; - if (cmd.counter == 6) { - Json::Value base; - generateDebuggingTimeInfo(base); - return base; - } - } - } - return Json::nullValue; -} - -bool ForwardingTimeCoordinator::isDependency(GlobalFederateId ofed) const -{ - return dependencies.isDependency(ofed); -} - -bool ForwardingTimeCoordinator::addDependency(GlobalFederateId fedID) -{ - return dependencies.addDependency(fedID); -} - -bool ForwardingTimeCoordinator::addDependent(GlobalFederateId fedID) -{ - return dependencies.addDependent(fedID); -} - -void ForwardingTimeCoordinator::setAsChild(GlobalFederateId fedID) -{ - auto* dep = dependencies.getDependencyInfo(fedID); - if (dep != nullptr) { - dep->connection = ConnectionType::child; - } -} - -void ForwardingTimeCoordinator::setAsParent(GlobalFederateId fedID) -{ - auto* dep = dependencies.getDependencyInfo(fedID); - if (dep != nullptr) { - dep->connection = ConnectionType::parent; - noParent = false; - } -} - -void ForwardingTimeCoordinator::removeDependency(GlobalFederateId fedID) -{ - dependencies.removeDependency(fedID); -} - -void ForwardingTimeCoordinator::removeDependent(GlobalFederateId fedID) -{ - dependencies.removeDependent(fedID); -} - -const DependencyInfo* ForwardingTimeCoordinator::getDependencyInfo(GlobalFederateId ofed) const -{ - return dependencies.getDependencyInfo(ofed); -} - -std::vector ForwardingTimeCoordinator::getDependencies() const -{ - std::vector deps; - for (const auto& dep : dependencies) { - if (dep.dependency) { - deps.push_back(dep.fedID); - } - } - return deps; -} - -std::vector ForwardingTimeCoordinator::getDependents() const -{ - std::vector deps; - for (const auto& dep : dependencies) { - if (dep.dependent) { - deps.push_back(dep.fedID); - } - } - return deps; -} - -bool ForwardingTimeCoordinator::hasActiveTimeDependencies() const -{ - return dependencies.hasActiveTimeDependencies(); -} - -int ForwardingTimeCoordinator::dependencyCount() const -{ - return dependencies.activeDependencyCount(); -} -/** get a count of the active dependencies*/ -GlobalFederateId ForwardingTimeCoordinator::getMinDependency() const -{ - return dependencies.getMinDependency(); -} - -MessageProcessingResult ForwardingTimeCoordinator::checkExecEntry() +MessageProcessingResult ForwardingTimeCoordinator::checkExecEntry(GlobalFederateId /*triggerFed*/) { auto ret = MessageProcessingResult::CONTINUE_PROCESSING; @@ -288,7 +130,7 @@ MessageProcessingResult ForwardingTimeCoordinator::checkExecEntry() allowed = true; for (auto& dep : dependencies) { if (dep.dependency) { - if (dep.minFed != source_id) { + if (dep.minFed != mSourceId) { allowed = false; break; } @@ -311,83 +153,12 @@ MessageProcessingResult ForwardingTimeCoordinator::checkExecEntry() downstream.minDe = timeZero; ActionMessage execgrant(CMD_EXEC_GRANT); - execgrant.source_id = source_id; + execgrant.source_id = mSourceId; transmitTimingMessagesDownstream(execgrant); transmitTimingMessagesUpstream(execgrant); return ret; } -ActionMessage ForwardingTimeCoordinator::generateTimeRequest(const DependencyInfo& dep, - GlobalFederateId fed) const -{ - ActionMessage nTime(CMD_TIME_REQUEST); - nTime.source_id = source_id; - nTime.dest_id = fed; - nTime.actionTime = dep.next; - if (dep.delayedTiming) { - setActionFlag(nTime, delayed_timing_flag); - } - switch (dep.mTimeState) { - case TimeState::time_granted: - nTime.setAction(CMD_TIME_GRANT); - break; - case TimeState::time_requested: - nTime.setExtraData(dep.minFed.baseValue()); - nTime.Tdemin = std::min(dep.minDe, dep.Te); - nTime.Te = dep.Te; - break; - case TimeState::time_requested_iterative: - nTime.setExtraData(dep.minFed.baseValue()); - setIterationFlags(nTime, IterationRequest::ITERATE_IF_NEEDED); - nTime.Tdemin = std::min(dep.minDe, dep.Te); - nTime.Te = dep.Te; - nTime.counter = dep.sequenceCounter; - nTime.setExtraDestData(dep.responseSequenceCounter); - break; - case TimeState::time_requested_require_iteration: - nTime.setExtraData(dep.minFed.baseValue()); - setIterationFlags(nTime, IterationRequest::FORCE_ITERATION); - nTime.Tdemin = std::min(dep.minDe, dep.Te); - nTime.counter = dep.sequenceCounter; - nTime.Te = dep.Te; - nTime.setExtraDestData(dep.responseSequenceCounter); - break; - case TimeState::exec_requested: - nTime.setAction(CMD_EXEC_REQUEST); - nTime.actionTime = Time::zeroVal(); - break; - case TimeState::error: - nTime.setAction(CMD_IGNORE); - // no need to send updates for this - break; - case TimeState::exec_requested_iterative: - nTime.setAction(CMD_EXEC_REQUEST); - setIterationFlags(nTime, IterationRequest::ITERATE_IF_NEEDED); - nTime.setExtraData(dep.minFed.baseValue()); - nTime.counter = dep.sequenceCounter; - break; - case TimeState::exec_requested_require_iteration: - nTime.setAction(CMD_EXEC_REQUEST); - setIterationFlags(nTime, IterationRequest::FORCE_ITERATION); - nTime.setExtraData(dep.minFed.baseValue()); - nTime.counter = dep.sequenceCounter; - break; - case TimeState::initialized: - if (dep.responseSequenceCounter == 0) { - nTime.setAction(CMD_IGNORE); - } else { - nTime.setAction(CMD_EXEC_GRANT); - nTime.setExtraData(dep.minFed.baseValue()); - setIterationFlags(nTime, IterationRequest::ITERATE_IF_NEEDED); - nTime.counter = dep.sequenceCounter; - } - - break; - } - - return nTime; -} - void ForwardingTimeCoordinator::transmitTimingMessagesUpstream(ActionMessage& msg) const { if (!sendMessageFunction) { @@ -402,7 +173,7 @@ void ForwardingTimeCoordinator::transmitTimingMessagesUpstream(ActionMessage& ms continue; } msg.dest_id = dep.fedID; - if (msg.action() == CMD_EXEC_REQUEST) { + if (msg.action() == CMD_EXEC_REQUEST || msg.action() == CMD_TIME_REQUEST) { msg.setExtraDestData(dep.sequenceCounter); } sendMessageFunction(msg); @@ -427,10 +198,13 @@ void ForwardingTimeCoordinator::transmitTimingMessagesDownstream(ActionMessage& continue; } if (dep.dependency) { - if (dep.next > msg.actionTime) { + if (dep.next > msg.actionTime && dep.next < cBigTime) { continue; } } + if (msg.action() == CMD_TIME_REQUEST) { + msg.setExtraDestData(dep.sequenceCounter); + } msg.dest_id = dep.fedID; sendMessageFunction(msg); } @@ -449,73 +223,5 @@ void ForwardingTimeCoordinator::transmitTimingMessagesDownstream(ActionMessage& } } } -bool ForwardingTimeCoordinator::processTimeMessage(const ActionMessage& cmd) -{ - switch (cmd.action()) { - case CMD_DISCONNECT: - case CMD_DISCONNECT_BROKER: - case CMD_DISCONNECT_FED: - case CMD_DISCONNECT_CORE: - case CMD_BROADCAST_DISCONNECT: - removeDependent(cmd.source_id); - break; - default: - break; - } - auto procRes = dependencies.updateTime(cmd); - switch (procRes) { - case DependencyProcessingResult::NOT_PROCESSED: - default: - return false; - case DependencyProcessingResult::PROCESSED: - return true; - case DependencyProcessingResult::PROCESSED_AND_CHECK: { - auto checkRes = dependencies.checkForIssues(false); - if (checkRes.first != 0) { - ActionMessage ge(CMD_GLOBAL_ERROR); - ge.dest_id = parent_broker_id; - ge.source_id = source_id; - ge.messageID = checkRes.first; - ge.payload = checkRes.second; - sendMessageFunction(ge); - } - return true; - } - } -} - -void ForwardingTimeCoordinator::processDependencyUpdateMessage(const ActionMessage& cmd) -{ - switch (cmd.action()) { - case CMD_ADD_DEPENDENCY: - addDependency(cmd.source_id); - break; - case CMD_REMOVE_DEPENDENCY: - removeDependency(cmd.source_id); - break; - case CMD_ADD_DEPENDENT: - addDependent(cmd.source_id); - break; - case CMD_REMOVE_DEPENDENT: - removeDependent(cmd.source_id); - break; - case CMD_ADD_INTERDEPENDENCY: - addDependency(cmd.source_id); - addDependent(cmd.source_id); - break; - case CMD_REMOVE_INTERDEPENDENCY: - removeDependency(cmd.source_id); - removeDependent(cmd.source_id); - break; - default: - break; - } - if (checkActionFlag(cmd, child_flag)) { - setAsChild(cmd.source_id); - } - if (checkActionFlag(cmd, parent_flag)) { - setAsParent(cmd.source_id); - } -} } // namespace helics diff --git a/src/helics/core/ForwardingTimeCoordinator.hpp b/src/helics/core/ForwardingTimeCoordinator.hpp index e67107b2ac..163081aa03 100644 --- a/src/helics/core/ForwardingTimeCoordinator.hpp +++ b/src/helics/core/ForwardingTimeCoordinator.hpp @@ -7,6 +7,7 @@ SPDX-License-Identifier: BSD-3-Clause #pragma once #include "ActionMessage.hpp" +#include "BaseTimeCoordinator.hpp" #include "CoreFederateInfo.hpp" #include "TimeDependencies.hpp" @@ -23,117 +24,40 @@ namespace helics { the time coordinator manages dependencies and computes whether time can advance or enter execution mode */ -class ForwardingTimeCoordinator { +class ForwardingTimeCoordinator: public BaseTimeCoordinator { private: // the variables for time coordination DependencyInfo upstream; DependencyInfo downstream; - // Core::local_federate_id parent = invalid_fed_id; //!< the id for the parent object which - // should also be a ForwardingTimeCoordinator - TimeDependencies dependencies; //!< federates which this Federate is temporally dependent on - /// callback used to send the messages - std::function sendMessageFunction; - - public: - /// the identifier for inserting into the source id field of any generated messages; - GlobalFederateId source_id{0}; - /// flag indicating that the coordinator is trying to enter the exec mode - bool checkingExec{false}; - bool executionMode{false}; //!< flag that the coordinator has entered the execution Mode + protected: bool iterating{false}; //!< flag indicating that the min dependency is iterating bool ignoreMinFed{false}; //!< flag indicating that minFed Controls should not be used - /// flag indicating that a restrictive time policy should be used - bool restrictive_time_policy{false}; - bool noParent{true}; //!< indicator that the coordinator does not have parents - private: - bool federatesOnly{false}; //!< indicator that the forwarder only operates with federates + public: ForwardingTimeCoordinator() = default; - /** set the callback function used for the sending messages*/ - void setMessageSender(std::function userSendMessageFunction) - { - sendMessageFunction = std::move(userSendMessageFunction); - } - - /** get a list of actual dependencies*/ - std::vector getDependencies() const; - /** get a reference to the dependents vector*/ - std::vector getDependents() const; - /** compute updates to time values and send an update if needed */ - void updateTimeFactors(); - - /** take a global id and get a pointer to the dependencyInfo for the other fed - will be nullptr if it doesn't exist - */ - const DependencyInfo* getDependencyInfo(GlobalFederateId ofed) const; - /** check whether a federate is a dependency*/ - bool isDependency(GlobalFederateId ofed) const; - /** check whether a timeCoordinator has any dependencies or dependents*/ - bool empty() const { return dependencies.empty(); } + virtual bool updateTimeFactors() override; private: void transmitTimingMessagesUpstream(ActionMessage& msg) const; void transmitTimingMessagesDownstream(ActionMessage& msg, GlobalFederateId skipFed = GlobalFederateId{}) const; - /** generate a timeRequest message based on the dependency info data*/ - ActionMessage generateTimeRequest(const DependencyInfo& dep, GlobalFederateId fed) const; public: - /** process a message related to time - @return a message_process_result if it did anything - */ - bool processTimeMessage(const ActionMessage& cmd); - - /** process a dependency update message*/ - void processDependencyUpdateMessage(const ActionMessage& cmd); - /** add a federate dependency - @return true if it was actually added, false if the federate was already present - */ - bool addDependency(GlobalFederateId fedID); - /** add a dependent federate - @return true if it was actually added, false if the federate was already present - */ - bool addDependent(GlobalFederateId fedID); - /** remove a dependency - @param fedID the identifier of the federate to remove*/ - void removeDependency(GlobalFederateId fedID); - /** remove a dependent - @param fedID the identifier of the federate to remove*/ - void removeDependent(GlobalFederateId fedID); - - void setAsChild(GlobalFederateId fedID); - void setAsParent(GlobalFederateId fedID); - - /** disconnect*/ - void disconnect(); /** check if entry to the executing state can be granted*/ - MessageProcessingResult checkExecEntry(); - - /** function to enter the exec Mode - */ - void enteringExecMode(); + virtual MessageProcessingResult + checkExecEntry(GlobalFederateId triggerFed = GlobalFederateId{}) override; /** generate a string with the current time status*/ - std::string printTimeStatus() const; + virtual std::string printTimeStatus() const override; /** generate debugging time information*/ - void generateDebuggingTimeInfo(Json::Value& base) const; + virtual void generateDebuggingTimeInfo(Json::Value& base) const override; - /** check if there are any active Time dependencies*/ - bool hasActiveTimeDependencies() const; /** get the current next time*/ - Time getNextTime() const { return downstream.next; } - /** get a count of the active dependencies*/ - int dependencyCount() const; - /** get a count of the active dependencies*/ - GlobalFederateId getMinDependency() const; - - /** grant timeout check - @return a json Object with debugging info if empty nothing is logged*/ - Json::Value grantTimeoutCheck(const ActionMessage& cmd); + virtual Time getNextTime() const override { return downstream.next; } }; } // namespace helics diff --git a/src/helics/core/GlobalTimeCoordinator.cpp b/src/helics/core/GlobalTimeCoordinator.cpp new file mode 100644 index 0000000000..55c61dfcbb --- /dev/null +++ b/src/helics/core/GlobalTimeCoordinator.cpp @@ -0,0 +1,273 @@ +/* +Copyright (c) 2017-2022, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable +Energy, LLC. See the top-level NOTICE for additional details. All rights reserved. +SPDX-License-Identifier: BSD-3-Clause +*/ + +#include "GlobalTimeCoordinator.hpp" + +#include "../common/fmt_format.h" +#include "flagOperations.hpp" +#include "helics_definitions.hpp" + +#include "json/json.h" +#include +#include +#include +#include +#include + +namespace helics { + +static Time findNextTriggerEvent(const TimeDependencies& deps) +{ + Time me{Time::maxVal()}; + for (auto& dep : deps) { + if (!dep.nonGranting) { + if (dep.Te < me) { + me = dep.Te; + } + } + } + return me; +} + +static std::pair checkForTriggered(const TimeDependencies& deps, Time nextEvent) +{ + Time me{Time::maxVal()}; + bool triggered{false}; + for (auto& dep : deps) { + if (dep.next > nextEvent) { + if (dep.Te < me) { + me = dep.Te; + } + continue; + } + if (dep.nonGranting) { + if (dep.triggered) { + triggered = true; + } + } else { + if (dep.Te < me) { + me = dep.Te; + } + } + } + return {triggered, me}; +} + +void GlobalTimeCoordinator::sendTimeUpdateRequest(Time triggerTime) +{ + ActionMessage updateTime(CMD_REQUEST_CURRENT_TIME, mSourceId, mSourceId); + updateTime.counter = sequenceCounter; + for (auto& dep : dependencies) { + if (dep.next <= triggerTime && dep.next < cBigTime) { + updateTime.dest_id = dep.fedID; + updateTime.setExtraDestData(dep.sequenceCounter); + dep.updateRequested = true; + dep.grantedIteration = sequenceCounter; + sendMessageFunction(updateTime); + } + } +} + +bool GlobalTimeCoordinator::updateTimeFactors() +{ + auto timeStream = generateMinTimeUpstream(dependencies, true, mSourceId, NoIgnoredFederates, 0); + if (timeStream.mTimeState == TimeState::time_granted) { + currentTimeState = TimeState::time_granted; + currentMinTime = timeStream.next; + nextEvent = timeStream.next; + return false; + } + if (timeStream.mTimeState == TimeState::time_requested) { + if (currentTimeState == TimeState::time_granted) { + currentTimeState = TimeState::time_requested; + currentMinTime = timeStream.next; + nextEvent = findNextTriggerEvent(dependencies); + ++sequenceCounter; + auto trigTime = (nextEvent < cBigTime) ? nextEvent + Time::epsilon() : nextEvent; + sendTimeUpdateRequest(trigTime); + return true; + } + if (currentTimeState == TimeState::time_requested) { + auto trigTime = (nextEvent < cBigTime) ? nextEvent + Time::epsilon() : nextEvent; + if (dependencies.verifySequenceCounter(trigTime, sequenceCounter)) { + auto trig = checkForTriggered(dependencies, trigTime); + bool verified{trig.second <= nextEvent}; + nextEvent = trig.second; + trigTime = (nextEvent < cBigTime) ? nextEvent + Time::epsilon() : nextEvent; + if (!verified) { + verified = dependencies.verifySequenceCounter(trigTime, sequenceCounter); + } + + if (trig.first || !verified) { + ++sequenceCounter; + sendTimeUpdateRequest(trigTime); + return true; + } + ActionMessage updateTime(CMD_TIME_REQUEST, mSourceId, mSourceId); + updateTime.actionTime = trigTime; + updateTime.Te = trigTime; + updateTime.Tdemin = trigTime; + if (trigTime <= timeZero) { + std::cerr << "negative trigger time" << std::endl; + } + ++sequenceCounter; + updateTime.counter = sequenceCounter; + for (const auto& dep : dependencies) { + if (dep.next <= trigTime && dep.next < cBigTime) { + updateTime.dest_id = dep.fedID; + updateTime.setExtraDestData(dep.sequenceCounter); + sendMessageFunction(updateTime); + } + } + currentTimeState = TimeState::time_granted; + currentMinTime = timeStream.Te; + nextEvent = timeStream.Te; + } else { + for (auto& dep : dependencies) { + if (dep.updateRequested) { + continue; + } + if (dep.next <= trigTime && dep.next < cBigTime) { + if (!checkSequenceCounter(dep, trigTime, sequenceCounter)) { + std::cerr << "sequence check but no request" << std::endl; + /* ActionMessage updateTime(CMD_REQUEST_CURRENT_TIME, + mSourceId, + mSourceId); + updateTime.counter = sequenceCounter; + + updateTime.dest_id = dep.fedID; + updateTime.setExtraDestData(dep.sequenceCounter); + dep.updateRequested = true; + sendMessageFunction(updateTime); + */ + } + } + } + } + } + } + return true; +} + +void GlobalTimeCoordinator::generateDebuggingTimeInfo(Json::Value& base) const +{ + base["type"] = "global"; + base["nextEvent"] = static_cast(nextEvent); + addTimeState(base, currentTimeState); + base["minTime"] = static_cast(currentMinTime); + BaseTimeCoordinator::generateDebuggingTimeInfo(base); +} + +std::string GlobalTimeCoordinator::printTimeStatus() const +{ + return fmt::format(R"raw({{"time_next":{}, "Te":{}}})raw", + static_cast(currentMinTime), + static_cast(nextEvent)); +} + +MessageProcessingResult GlobalTimeCoordinator::checkExecEntry(GlobalFederateId /*triggerFed*/) +{ + auto ret = MessageProcessingResult::CONTINUE_PROCESSING; + + if (!dependencies.checkIfReadyForExecEntry(false, false)) { + bool allowed{false}; + if (currentTimeState == TimeState::exec_requested_iterative) { + allowed = true; + for (auto& dep : dependencies) { + if (dep.dependency) { + if (dep.minFed != mSourceId) { + allowed = false; + break; + } + if (dep.responseSequenceCounter != sequenceCounter) { + allowed = false; + break; + } + } + } + } + if (!allowed) { + return ret; + } + } + executionMode = true; + ret = MessageProcessingResult::NEXT_STEP; + + currentMinTime = timeZero; + currentTimeState = TimeState::time_granted; + nextEvent = timeZero; + + ActionMessage execgrant(CMD_EXEC_GRANT); + execgrant.source_id = mSourceId; + transmitTimingMessagesDownstream(execgrant); + transmitTimingMessagesUpstream(execgrant); + return ret; +} + +void GlobalTimeCoordinator::transmitTimingMessagesUpstream(ActionMessage& msg) const +{ + if (!sendMessageFunction) { + return; + } + + for (const auto& dep : dependencies) { + if (dep.connection == ConnectionType::child) { + continue; + } + if (!dep.dependent) { + continue; + } + msg.dest_id = dep.fedID; + if (msg.action() == CMD_EXEC_REQUEST) { + msg.setExtraDestData(dep.sequenceCounter); + } + sendMessageFunction(msg); + } +} + +void GlobalTimeCoordinator::transmitTimingMessagesDownstream(ActionMessage& msg, + GlobalFederateId skipFed) const +{ + if (!sendMessageFunction) { + return; + } + if ((msg.action() == CMD_TIME_REQUEST || msg.action() == CMD_TIME_GRANT)) { + for (const auto& dep : dependencies) { + if (dep.connection != ConnectionType::child) { + continue; + } + if (!dep.dependent) { + continue; + } + if (dep.fedID == skipFed) { + continue; + } + if (dep.dependency) { + if (dep.next > msg.actionTime) { + continue; + } + } + msg.dest_id = dep.fedID; + sendMessageFunction(msg); + } + } else { + for (const auto& dep : dependencies) { + if (dep.dependent) { + if (dep.fedID == skipFed) { + continue; + } + if (msg.action() == CMD_EXEC_REQUEST) { + msg.setExtraDestData(dep.sequenceCounter); + } + msg.dest_id = dep.fedID; + sendMessageFunction(msg); + } + } + } +} + +} // namespace helics diff --git a/src/helics/core/GlobalTimeCoordinator.hpp b/src/helics/core/GlobalTimeCoordinator.hpp new file mode 100644 index 0000000000..fbe554324b --- /dev/null +++ b/src/helics/core/GlobalTimeCoordinator.hpp @@ -0,0 +1,65 @@ +/* +Copyright (c) 2017-2022, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable +Energy, LLC. See the top-level NOTICE for additional details. All rights reserved. +SPDX-License-Identifier: BSD-3-Clause +*/ +#pragma once + +#include "ActionMessage.hpp" +#include "BaseTimeCoordinator.hpp" +#include "CoreFederateInfo.hpp" +#include "TimeDependencies.hpp" + +#include "json/forwards.h" +#include +#include +#include +#include +#include + +namespace helics { + +/** class managing the coordination of time in HELICS for forwarding object (cores, brokers) +the time coordinator manages dependencies and computes whether time can advance or enter execution +mode +*/ +class GlobalTimeCoordinator: public BaseTimeCoordinator { + private: + // the variables for time coordination + Time currentMinTime{Time::minVal()}; + TimeState currentTimeState{TimeState::initialized}; + Time nextEvent{Time::maxVal()}; + + protected: + bool iterating{false}; //!< flag indicating that the min dependency is iterating + + public: + GlobalTimeCoordinator() = default; + + /** compute updates to time values + and send an update if needed + */ + virtual bool updateTimeFactors() override; + + private: + void transmitTimingMessagesUpstream(ActionMessage& msg) const; + void transmitTimingMessagesDownstream(ActionMessage& msg, + GlobalFederateId skipFed = GlobalFederateId{}) const; + + void sendTimeUpdateRequest(Time triggerTime); + + public: + /** check if entry to the executing state can be granted*/ + virtual MessageProcessingResult + checkExecEntry(GlobalFederateId triggerFed = GlobalFederateId{}) override; + + /** generate a string with the current time status*/ + virtual std::string printTimeStatus() const override; + /** generate debugging time information*/ + virtual void generateDebuggingTimeInfo(Json::Value& base) const override; + + /** get the current next time*/ + virtual Time getNextTime() const override { return currentMinTime; } +}; +} // namespace helics diff --git a/src/helics/core/TimeCoordinator.cpp b/src/helics/core/TimeCoordinator.cpp index 3a4e12e58a..c4171091fc 100644 --- a/src/helics/core/TimeCoordinator.cpp +++ b/src/helics/core/TimeCoordinator.cpp @@ -13,6 +13,7 @@ SPDX-License-Identifier: BSD-3-Clause #include "json/json.h" #include +#include #include #include #include @@ -20,28 +21,6 @@ SPDX-License-Identifier: BSD-3-Clause namespace helics { -static const Time bigTime(HELICS_BIG_NUMBER); - -static auto nullMessageFunction = [](const ActionMessage& /*unused*/) {}; -TimeCoordinator::TimeCoordinator(): sendMessageFunction(nullMessageFunction) {} - -TimeCoordinator::TimeCoordinator(std::function userSendMessageFunction): - sendMessageFunction(std::move(userSendMessageFunction)) -{ - if (!sendMessageFunction) { - sendMessageFunction = nullMessageFunction; - } -} - -void TimeCoordinator::setMessageSender( - std::function userSendMessageFunction) -{ - sendMessageFunction = std::move(userSendMessageFunction); - if (!sendMessageFunction) { - sendMessageFunction = nullMessageFunction; - } -} - void TimeCoordinator::enteringExecMode(IterationRequest mode) { if (executionMode) { @@ -52,22 +31,23 @@ void TimeCoordinator::enteringExecMode(IterationRequest mode) if (res.first != 0) { ActionMessage ge(CMD_GLOBAL_ERROR); ge.dest_id = parent_broker_id; - ge.source_id = source_id; + ge.source_id = mSourceId; ge.messageID = res.first; ge.payload = res.second; sendMessageFunction(ge); return; } + sendTimingInfo(); checkingExec = true; ActionMessage execreq(CMD_EXEC_REQUEST); - execreq.source_id = source_id; + execreq.source_id = mSourceId; if (iterating != IterationRequest::NO_ITERATIONS) { setIterationFlags(execreq, iterating); ++sequenceCounter; execreq.counter = sequenceCounter; if (!hasInitUpdates) { - const auto& mfed = getExecEntryMinFederate(dependencies, source_id); + const auto& mfed = getExecEntryMinFederate(dependencies, mSourceId); execreq.setExtraData(mfed.fedID.baseValue()); } } @@ -77,43 +57,6 @@ void TimeCoordinator::enteringExecMode(IterationRequest mode) transmitTimingMessages(execreq); } -void TimeCoordinator::disconnect() -{ - if (sendMessageFunction) { - if (dependencies.empty()) { - return; - } - ActionMessage bye(CMD_DISCONNECT); - bye.source_id = source_id; - if (dependencies.size() == 1) { - auto& dep = *dependencies.begin(); - if ((dep.dependency && dep.next < Time::maxVal()) || dep.dependent) { - bye.dest_id = dep.fedID; - if (bye.dest_id == source_id) { - processTimeMessage(bye); - } else { - sendMessageFunction(bye); - } - } - - } else { - ActionMessage multi(CMD_MULTI_MESSAGE); - for (const auto& dep : dependencies) { - if ((dep.dependency && dep.next < Time::maxVal()) || dep.dependent) { - bye.dest_id = dep.fedID; - if (dep.fedID == source_id) { - processTimeMessage(bye); - } else { - appendMessage(multi, bye); - } - } - } - sendMessageFunction(multi); - } - } - disconnected = true; -} - void TimeCoordinator::localError() { if (disconnected) { @@ -127,12 +70,12 @@ void TimeCoordinator::localError() } ActionMessage bye(CMD_LOCAL_ERROR); - bye.source_id = source_id; + bye.source_id = mSourceId; if (dependencies.size() == 1) { auto& dep = *dependencies.begin(); if ((dep.dependency && dep.next < Time::maxVal()) || dep.dependent) { bye.dest_id = dep.fedID; - if (bye.dest_id == source_id) { + if (bye.dest_id == mSourceId) { processTimeMessage(bye); } else { sendMessageFunction(bye); @@ -144,7 +87,7 @@ void TimeCoordinator::localError() for (const auto& dep : dependencies) { if ((dep.dependency && dep.next < Time::maxVal()) || dep.dependent) { bye.dest_id = dep.fedID; - if (dep.fedID == source_id) { + if (dep.fedID == mSourceId) { processTimeMessage(bye); } else { appendMessage(multi, bye); @@ -193,10 +136,11 @@ void TimeCoordinator::timeRequest(Time nextTime, } } dependencies.resetDependentEvents(time_granted); + ++sequenceCounter; updateTimeFactors(); if (!dependencies.empty()) { - sendTimeRequest(); + sendTimeRequest(GlobalFederateId{}); } } @@ -272,6 +216,9 @@ void TimeCoordinator::updateValueTime(Time valueUpdateTime, bool allowRequestSen } return; } + if (valueUpdateTime <= time_granted) { + hasIterationData = true; + } if (valueUpdateTime < time_value) { auto ptime = time_value; if (iterating != IterationRequest::NO_ITERATIONS) { @@ -287,7 +234,7 @@ void TimeCoordinator::updateValueTime(Time valueUpdateTime, bool allowRequestSen if (time_value < ptime && !disconnected) { if (updateNextExecutionTime()) { if (allowRequestSend) { - sendTimeRequest(); + sendTimeRequest(GlobalFederateId{}); } } } @@ -344,17 +291,7 @@ void TimeCoordinator::generateDebuggingTimeInfo(Json::Value& base) const generateJsonOutputTimeData(sent, lastSend); base["last_send"] = sent; - base["dependencies"] = Json::arrayValue; - for (const auto& dep : dependencies) { - if (dep.dependency) { - Json::Value depblock; - generateJsonOutputDependency(depblock, dep); - base["dependencies"].append(depblock); - } - if (dep.dependent) { - base["dependents"].append(dep.fedID.baseValue()); - } - } + BaseTimeCoordinator::generateDebuggingTimeInfo(base); // now add any time blocks that may be present base["blocks"] = Json::arrayValue; for (const auto& blk : timeBlocks) { @@ -365,31 +302,15 @@ void TimeCoordinator::generateDebuggingTimeInfo(Json::Value& base) const } } -bool TimeCoordinator::hasActiveTimeDependencies() const -{ - return dependencies.hasActiveTimeDependencies(); -} - -int TimeCoordinator::dependencyCount() const -{ - return dependencies.activeDependencyCount(); -} - -/** get a count of the active dependencies*/ -GlobalFederateId TimeCoordinator::getMinDependency() const -{ - return dependencies.getMinDependency(); -} - void TimeCoordinator::enterInitialization() { if (dynamicJoining) { ActionMessage timeUpdateRequest(CMD_REQUEST_CURRENT_TIME); - timeUpdateRequest.source_id = source_id; + timeUpdateRequest.source_id = mSourceId; for (const auto& dep : dependencies) { // send to all dependencies if (dep.dependency) { - if (dep.fedID == source_id) { + if (dep.fedID == mSourceId) { continue; } timeUpdateRequest.dest_id = dep.fedID; @@ -399,15 +320,20 @@ void TimeCoordinator::enterInitialization() } } +Time TimeCoordinator::getNextTime() const +{ + return getGrantedTime(); +} + void TimeCoordinator::requestTimeCheck() { if (dynamicJoining) { ActionMessage timeUpdateRequest(CMD_REQUEST_CURRENT_TIME); - timeUpdateRequest.source_id = source_id; + timeUpdateRequest.source_id = mSourceId; for (const auto& dep : dependencies) { // send to all dependencies if (dep.dependency) { - if (dep.fedID == source_id) { + if (dep.fedID == mSourceId) { continue; } // only send the request if it is blocking the current grant @@ -476,7 +402,9 @@ void TimeCoordinator::updateMessageTime(Time messageUpdateTime, bool allowReques } return; } - + if (messageUpdateTime <= time_granted) { + hasIterationData = true; + } if (messageUpdateTime < time_message) { auto ptime = time_message; if (iterating != IterationRequest::NO_ITERATIONS) { @@ -492,7 +420,7 @@ void TimeCoordinator::updateMessageTime(Time messageUpdateTime, bool allowReques if (time_message < ptime && !disconnected) { if (updateNextExecutionTime()) { if (allowRequestSend) { - sendTimeRequest(); + sendTimeRequest(GlobalFederateId{}); } } } @@ -501,9 +429,20 @@ void TimeCoordinator::updateMessageTime(Time messageUpdateTime, bool allowReques bool TimeCoordinator::updateTimeFactors() { - total = generateMinTimeTotal(dependencies, info.restrictive_time_policy, GlobalFederateId{}); - upstream = - generateMinTimeUpstream(dependencies, info.restrictive_time_policy, GlobalFederateId{}); + total = generateMinTimeTotal(dependencies, + info.restrictive_time_policy || globalTime, + GlobalFederateId{}, + NoIgnoredFederates, + sequenceCounter); + upstream = generateMinTimeUpstream(dependencies, + info.restrictive_time_policy || globalTime, + GlobalFederateId{}, + NoIgnoredFederates, + sequenceCounter); + if (globalTime && dependencies.size() == 1) { + upstream = total; + upstream.minFed = GlobalFederateId{}; + } maxTime = Time::maxVal() - info.outputDelay - (std::max)(info.period, info.timeDelta); bool update = false; @@ -523,7 +462,7 @@ bool TimeCoordinator::updateTimeFactors() if (upstream.minDe < maxTime && upstream.minDe > total.minDe) { upstream.minDe = generateAllowedTime(upstream.minDe) + info.outputDelay; } - if (info.event_triggered || time_requested >= bigTime) { + if (!globalTime && (info.event_triggered || time_requested >= cBigTime)) { if (upstream.Te < maxTime) { upstream.Te = generateAllowedTime(upstream.minDe); } @@ -538,7 +477,7 @@ bool TimeCoordinator::updateTimeFactors() return update; } -MessageProcessingResult TimeCoordinator::checkTimeGrant() +MessageProcessingResult TimeCoordinator::checkTimeGrant(GlobalFederateId triggerFed) { updateTimeFactors(); if (time_exec == Time::maxVal()) { @@ -549,57 +488,240 @@ MessageProcessingResult TimeCoordinator::checkTimeGrant() return MessageProcessingResult::HALTED; } } - if ((time_block <= time_exec && time_block < Time::maxVal()) || - (nonGranting && time_exec < time_requested)) { + if (time_block <= time_exec && time_block < Time::maxVal()) { + if (triggerFed.isValid()) { + if (triggerFed != mSourceId) { + sendTimeRequest(triggerFed); + } + } return MessageProcessingResult::CONTINUE_PROCESSING; } - if ((iterating == IterationRequest::NO_ITERATIONS) || - (time_exec > time_granted && iterating == IterationRequest::ITERATE_IF_NEEDED)) { - iteration = 0; - if (time_allow > time_exec) { - updateTimeGrant(); - return MessageProcessingResult::NEXT_STEP; + if ((nonGranting && time_exec < time_requested)) { + if (triggerFed.isValid()) { + if (triggerFed != mSourceId) { + sendTimeRequest(triggerFed); + } } - if (time_allow == time_exec) { - if (!info.wait_for_current_time_updates) { - if (time_requested <= time_exec) { - // this is the non interrupted case - updateTimeGrant(); - return MessageProcessingResult::NEXT_STEP; + return MessageProcessingResult::CONTINUE_PROCESSING; + } + // if ((iterating == IterationRequest::NO_ITERATIONS) || + // (time_exec > time_granted && iterating == IterationRequest::ITERATE_IF_NEEDED)) { + // + // if (time_allow > time_exec) { + // iteration = 0; + // updateTimeGrant(); + // return MessageProcessingResult::NEXT_STEP; + // } + // if (time_allow == time_exec) { + // if (!info.wait_for_current_time_updates) { + // if (time_requested <= time_exec) { + // // this is the non interrupted case + // iteration = 0; + // updateTimeGrant(); + // return MessageProcessingResult::NEXT_STEP; + // } + // if (dependencies.checkIfReadyForTimeGrant(false, time_exec)) { + // iteration = 0; + // updateTimeGrant(); + // return MessageProcessingResult::NEXT_STEP; + // } + // } else { + // // if the wait_for_current_time_updates flag is set then time_allow must be + // greater + // // than time_exec + // } + // } + // } else { + // if (time_allow > time_exec) { + // ++iteration; + // updateTimeGrant(); + // return MessageProcessingResult::ITERATING; + // } + // if (time_allow == time_exec) // time_allow==time_exec==time_granted + // { + // if (dependencies.checkIfReadyForTimeGrant(true, time_exec)) { + // ++iteration; + // updateTimeGrant(); + // return MessageProcessingResult::ITERATING; + // } + // } + // } + auto ret = MessageProcessingResult::CONTINUE_PROCESSING; + bool sendAll{needSendAll}; + needSendAll = false; + switch (iterating) { + case IterationRequest::NO_ITERATIONS: + if (time_allow > time_exec) { + iteration = 0; + updateTimeGrant(); + return MessageProcessingResult::NEXT_STEP; + } + if (time_allow == time_exec) { + if (!info.wait_for_current_time_updates) { + if (time_requested <= time_exec) { + // this is the non interrupted case + iteration = 0; + updateTimeGrant(); + return MessageProcessingResult::NEXT_STEP; + } } - if (dependencies.checkIfReadyForTimeGrant(false, time_exec)) { + if (dependencies.checkIfReadyForTimeGrant(false, + time_exec, + info.wait_for_current_time_updates)) { + iteration = 0; updateTimeGrant(); return MessageProcessingResult::NEXT_STEP; } - } else { + // if the wait_for_current_time_updates flag is set then time_allow must be greater // than time_exec } - } - } else { - if (time_allow > time_exec) { + break; + case IterationRequest::ITERATE_IF_NEEDED: + case IterationRequest::FORCE_ITERATION: + if (time_allow > time_exec) { + if (time_exec <= time_granted || hasIterationData) { + ++iteration; + hasIterationData = false; + updateTimeGrant(); + return MessageProcessingResult::ITERATING; + } else { + iteration = 0; + updateTimeGrant(); + return (iterating == IterationRequest::FORCE_ITERATION) ? + MessageProcessingResult::ITERATING : + MessageProcessingResult::NEXT_STEP; + } + } + + if (time_allow == time_exec) { + if (time_allow == time_requested) { + if (!info.wait_for_current_time_updates) { + if (time_requested <= time_exec) { + // this is the non interrupted case + ret = MessageProcessingResult::NEXT_STEP; + break; + } + if (dependencies.checkIfReadyForTimeGrant(false, time_exec, false)) { + ret = MessageProcessingResult::NEXT_STEP; + break; + } + } + ret = MessageProcessingResult::CONTINUE_PROCESSING; + break; + } + if (dependencies.checkIfReadyForTimeGrant(true, + time_exec, + info.wait_for_current_time_updates)) { + if (hasIterationData) { + ret = MessageProcessingResult::ITERATING; + break; + } + if (time_exec > time_granted) { + ret = MessageProcessingResult::NEXT_STEP; + break; + } + // time_ + bool allowed{!info.wait_for_current_time_updates}; + bool restricted{info.restrictive_time_policy}; + bool restrictionAdvance{restricted}; + int restrictionLevel{50}; + if (allowed) { + for (const auto& dep : dependencies) { + if (!dep.dependency) { + continue; + } + if (dep.next > time_exec || dep.connection == ConnectionType::self) { + continue; + } + + if (dep.minFed != mSourceId) { + allowed = false; + } + if (dep.responseSequenceCounter == sequenceCounter) { + if (restricted) { + restrictionLevel = + (std::min)(restrictionLevel, + static_cast(dep.restrictionLevel)); + } + + } else { + restrictionAdvance = false; + allowed = false; + break; + } + } + } + if (allowed) { + if (restricted) { + if (restrictionLevel >= 1) { + ret = MessageProcessingResult::NEXT_STEP; + } else { + if (currentRestrictionLevel != restrictionLevel + 1) { + currentRestrictionLevel = restrictionLevel + 1; + sendAll = true; + ++sequenceCounter; + } + + ret = MessageProcessingResult::CONTINUE_PROCESSING; + } + } else { + ret = MessageProcessingResult::NEXT_STEP; + } + + } else { + if (restrictionAdvance) { + currentRestrictionLevel = restrictionLevel + 1; + sendAll = true; + ++sequenceCounter; + } + ret = MessageProcessingResult::CONTINUE_PROCESSING; + } + } + } + + break; + } + + switch (ret) { + case MessageProcessingResult::CONTINUE_PROCESSING: + if (triggerFed.isValid() || sendAll) { + if (sendAll) { + sendTimeRequest(GlobalFederateId{}); + } else { + if (triggerFed != mSourceId) { + sendTimeRequest(triggerFed); + } + } + } + break; + case MessageProcessingResult::ITERATING: ++iteration; + hasIterationData = false; updateTimeGrant(); - return MessageProcessingResult::ITERATING; - } - if (time_allow == time_exec) // time_allow==time_exec==time_granted - { - if (dependencies.checkIfReadyForTimeGrant(true, time_exec)) { + break; + case MessageProcessingResult::NEXT_STEP: + if (iterating == IterationRequest::FORCE_ITERATION) { ++iteration; + hasIterationData = false; + time_exec = time_granted; + updateTimeGrant(); + ret = MessageProcessingResult::ITERATING; + } else { + iteration = 0; + hasIterationData = false; updateTimeGrant(); - return MessageProcessingResult::ITERATING; } - } + break; + default: + break; } - // if we haven't returned we may need to update the time messages - if ((!dependencies.empty())) { - sendTimeRequest(); - } - return MessageProcessingResult::CONTINUE_PROCESSING; + return ret; } -bool TimeCoordinator::checkAndSendTimeRequest(ActionMessage& upd, GlobalFederateId skipFed) const +std::pair TimeCoordinator::checkAndSendTimeRequest(ActionMessage& upd, + GlobalFederateId skipFed) const { bool changed{false}; if (lastSend.next != upd.actionTime) { @@ -617,15 +739,19 @@ bool TimeCoordinator::checkAndSendTimeRequest(ActionMessage& upd, GlobalFederate if (lastSend.mTimeState != TimeState::time_requested) { changed = true; } + if (lastSend.sequenceCounter != sequenceCounter) { + changed = true; + } if (changed) { lastSend.next = upd.actionTime; lastSend.minDe = upd.Tdemin; lastSend.Te = upd.Te; + lastSend.sequenceCounter = sequenceCounter; lastSend.minFed = GlobalFederateId(upd.getExtraData()); lastSend.mTimeState = TimeState::time_requested; - return transmitTimingMessages(upd, skipFed); + return {true, transmitTimingMessages(upd, skipFed)}; } - return false; + return {false, false}; } static inline Time checkAdd(Time val, Time increment) @@ -633,11 +759,12 @@ static inline Time checkAdd(Time val, Time increment) return (val < Time::maxVal() - increment) ? val + increment : Time::maxVal(); } -void TimeCoordinator::sendTimeRequest() const +void TimeCoordinator::sendTimeRequest(GlobalFederateId triggerFed) const { ActionMessage upd(CMD_TIME_REQUEST); - upd.source_id = source_id; + upd.source_id = mSourceId; upd.actionTime = time_next; + upd.counter = sequenceCounter; if (nonGranting) { setActionFlag(upd, non_granting_flag); } @@ -645,14 +772,19 @@ void TimeCoordinator::sendTimeRequest() const setActionFlag(upd, delayed_timing_flag); } upd.Te = checkAdd(time_exec, info.outputDelay); - if (info.event_triggered || time_requested >= bigTime) { + if (!globalTime && (info.event_triggered || time_requested >= cBigTime)) { upd.Te = std::min(upd.Te, checkAdd(upstream.Te, info.outputDelay)); + if (upd.Te < timeZero) { + upd.Te = timeZero; + } upd.actionTime = std::min(upd.actionTime, upd.Te); } upd.Tdemin = std::min(checkAdd(upstream.Te, info.outputDelay), upd.Te); - if (info.event_triggered || time_requested >= bigTime) { + if (!globalTime && (info.event_triggered || time_requested >= cBigTime)) { upd.Tdemin = std::min(upd.Tdemin, checkAdd(upstream.minDe, info.outputDelay)); - + if (upd.Tdemin < timeZero) { + upd.Tdemin = timeZero; + } if (upd.Tdemin < upd.actionTime) { upd.actionTime = upd.Tdemin; } @@ -665,22 +797,36 @@ void TimeCoordinator::sendTimeRequest() const if (iterating != IterationRequest::NO_ITERATIONS) { setIterationFlags(upd, iterating); - upd.counter = iteration; - } - if (checkAndSendTimeRequest(upd, upstream.minFed)) { - if (upstream.minFed.isValid()) { - upd.dest_id = upstream.minFed; - upd.setExtraData(GlobalFederateId{}.baseValue()); - if (info.event_triggered || time_requested >= bigTime) { - upd.Te = checkAdd(time_exec, info.outputDelay); - upd.Te = std::min(upd.Te, checkAdd(upstream.TeAlt, info.outputDelay)); + } + upd.counter = sequenceCounter; + if (triggered) { + setActionFlag(upd, destination_target); + } + auto check = checkAndSendTimeRequest(upd, upstream.minFed); + if (check.first) { + if (check.second) { + if (upstream.minFed.isValid()) { + upd.dest_id = upstream.minFed; + upd.setExtraData(GlobalFederateId{}.baseValue()); + upd.setExtraDestData(upstream.responseSequenceCounter); + if (!globalTime && (info.event_triggered || time_requested >= cBigTime)) { + upd.Te = checkAdd(time_exec, info.outputDelay); + upd.Te = std::min(upd.Te, checkAdd(upstream.TeAlt, info.outputDelay)); + } + upd.Tdemin = std::min(upstream.TeAlt, upd.Te); + sendMessageFunction(upd); } - upd.Tdemin = std::min(upstream.TeAlt, upd.Te); + } + } else if (triggerFed.isValid()) { + upd.dest_id = triggerFed; + auto* dep = dependencies.getDependencyInfo(triggerFed); + if (dep->dependent) { + upd.setExtraDestData(dep->sequenceCounter); sendMessageFunction(upd); } } - // printf("%d next=%f, exec=%f, Tdemin=%f\n", source_id, static_cast(time_next), + // printf("%d next=%f, exec=%f, Tdemin=%f\n", mSourceId, static_cast(time_next), // static_cast(time_exec), static_cast(time_minDe)); } @@ -690,10 +836,11 @@ void TimeCoordinator::updateTimeGrant() time_granted = time_exec; time_grantBase = time_granted; } + ++sequenceCounter; ActionMessage treq(CMD_TIME_GRANT); - treq.source_id = source_id; + treq.source_id = mSourceId; treq.actionTime = time_granted; - treq.counter = iteration; + treq.counter = sequenceCounter; if (iterating != IterationRequest::NO_ITERATIONS) { dependencies.resetIteratingTimeRequests(time_exec); } @@ -720,21 +867,9 @@ std::string TimeCoordinator::printTimeStatus() const static_cast(time_minminDe)); } -bool TimeCoordinator::isDependency(GlobalFederateId ofed) const -{ - return dependencies.isDependency(ofed); -} - bool TimeCoordinator::addDependency(GlobalFederateId fedID) { - if (dependencies.addDependency(fedID)) { - if (fedID == source_id) { - auto* dep = dependencies.getDependencyInfo(fedID); - if (dep != nullptr) { - dep->connection = ConnectionType::self; - } - } - + if (BaseTimeCoordinator::addDependency(fedID)) { dependency_federates.lock()->push_back(fedID); return true; } @@ -743,45 +878,13 @@ bool TimeCoordinator::addDependency(GlobalFederateId fedID) bool TimeCoordinator::addDependent(GlobalFederateId fedID) { - if (dependencies.addDependent(fedID)) { + if (BaseTimeCoordinator::addDependent(fedID)) { dependent_federates.lock()->push_back(fedID); return true; } return false; } -void TimeCoordinator::setAsChild(GlobalFederateId fedID) -{ - if (fedID == source_id) { - return; - } - auto* dep = dependencies.getDependencyInfo(fedID); - if (dep != nullptr) { - dep->connection = ConnectionType::child; - } -} - -void TimeCoordinator::setAsParent(GlobalFederateId fedID) -{ - if (fedID == source_id) { - return; - } - auto* dep = dependencies.getDependencyInfo(fedID); - if (dep != nullptr) { - dep->connection = ConnectionType::parent; - } -} - -GlobalFederateId TimeCoordinator::getParent() const -{ - for (const auto& dep : dependencies) { - if (dep.connection == ConnectionType::parent) { - return dep.fedID; - } - } - return {}; -} - void TimeCoordinator::removeDependency(GlobalFederateId fedID) { dependencies.removeDependency(fedID); @@ -804,11 +907,6 @@ void TimeCoordinator::removeDependent(GlobalFederateId fedID) } } -DependencyInfo* TimeCoordinator::getDependencyInfo(GlobalFederateId ofed) -{ - return dependencies.getDependencyInfo(ofed); -} - std::vector TimeCoordinator::getDependencies() const { return *dependency_federates.lock_shared(); @@ -824,7 +922,7 @@ bool TimeCoordinator::transmitTimingMessages(ActionMessage& msg, GlobalFederateI continue; } msg.dest_id = dep.fedID; - if (msg.action() == CMD_EXEC_REQUEST) { + if (msg.action() == CMD_EXEC_REQUEST || msg.action() == CMD_TIME_REQUEST) { msg.setExtraDestData(dep.sequenceCounter); } sendMessageFunction(msg); @@ -838,13 +936,13 @@ void TimeCoordinator::sendUpdatedExecRequest(GlobalFederateId target, std::int32_t responseSequenceCounter) { if (!minFed.isValid()) { - const auto& mfed = getExecEntryMinFederate(dependencies, source_id); + const auto& mfed = getExecEntryMinFederate(dependencies, mSourceId); minFed = mfed.fedID; responseSequenceCounter = mfed.sequenceCounter; } ActionMessage execreq(CMD_EXEC_REQUEST); - execreq.source_id = source_id; + execreq.source_id = mSourceId; setIterationFlags(execreq, iterating); execreq.counter = sequenceCounter; execreq.setExtraData(minFed.baseValue()); @@ -880,7 +978,7 @@ MessageProcessingResult TimeCoordinator::checkExecEntry(GlobalFederateId trigger if (triggerFed.isValid() && iterating != IterationRequest::NO_ITERATIONS) { if (dependencies.checkIfReadyForExecEntry(false, false)) { // if we are just continuing but would have granted in other circumstances - const auto& mfed = getExecEntryMinFederate(dependencies, source_id); + const auto& mfed = getExecEntryMinFederate(dependencies, mSourceId); if (mfed.fedID == triggerFed) { sendUpdatedExecRequest(triggerFed, GlobalFederateId{}, @@ -909,9 +1007,10 @@ MessageProcessingResult TimeCoordinator::checkExecEntry(GlobalFederateId trigger } else { // on wait for current time flag all other federates must have entered exec mode total = generateMinTimeTotal(dependencies, - info.restrictive_time_policy, - source_id, - source_id); + info.restrictive_time_policy || globalTime, + mSourceId, + mSourceId, + sequenceCounter); if (total.next > timeZero) { ret = MessageProcessingResult::NEXT_STEP; } @@ -953,7 +1052,7 @@ MessageProcessingResult TimeCoordinator::checkExecEntry(GlobalFederateId trigger if (dep.mTimeState >= TimeState::exec_requested) { continue; } - if (dep.minFed != source_id) { + if (dep.minFed != mSourceId) { allowed = false; } if (dep.responseSequenceCounter == sequenceCounter) { @@ -1011,15 +1110,17 @@ MessageProcessingResult TimeCoordinator::checkExecEntry(GlobalFederateId trigger iteration = 0; currentRestrictionLevel = 0; ActionMessage execgrant(CMD_EXEC_GRANT); - execgrant.source_id = source_id; + execgrant.source_id = mSourceId; + execgrant.setExtraDestData(TIME_COORDINATOR_VERSION); // version transmitTimingMessages(execgrant); } else if (ret == MessageProcessingResult::ITERATING) { dependencies.resetIteratingExecRequests(); hasInitUpdates = false; ++iteration; ActionMessage execgrant(CMD_EXEC_GRANT); - execgrant.source_id = source_id; + execgrant.source_id = mSourceId; execgrant.counter = iteration; + execgrant.setExtraDestData(TIME_COORDINATOR_VERSION); // version setActionFlag(execgrant, iteration_requested_flag); transmitTimingMessages(execgrant); currentRestrictionLevel = 0; @@ -1038,24 +1139,27 @@ MessageProcessingResult TimeCoordinator::checkExecEntry(GlobalFederateId trigger iteration = 0; currentRestrictionLevel = 0; ActionMessage execgrant(time_granted > timeZero ? CMD_TIME_GRANT : CMD_EXEC_GRANT); - execgrant.source_id = source_id; + execgrant.source_id = mSourceId; execgrant.actionTime = time_granted; + execgrant.setExtraDestData(TIME_COORDINATOR_VERSION); // version transmitTimingMessages(execgrant); } } if (triggerFed.isValid() && ret == MessageProcessingResult::CONTINUE_PROCESSING && iterating != IterationRequest::NO_ITERATIONS) { // if we are just continuing - const auto& mfed = getExecEntryMinFederate(dependencies, source_id); + const auto& mfed = getExecEntryMinFederate(dependencies, mSourceId); if (sendAll) { sendUpdatedExecRequest(GlobalFederateId{}, mfed.fedID, mfed.sequenceCounter); } else { - if (triggerFed == mfed.fedID && mfed.dependent) { - sendUpdatedExecRequest(triggerFed, mfed.fedID, mfed.sequenceCounter); - } else { - const auto* tfed = dependencies.getDependencyInfo(triggerFed); - if (tfed->dependent) { - sendUpdatedExecRequest(triggerFed, mfed.fedID, tfed->sequenceCounter); + if (triggerFed != mSourceId) { + if (triggerFed == mfed.fedID && mfed.dependent) { + sendUpdatedExecRequest(triggerFed, mfed.fedID, mfed.sequenceCounter); + } else { + const auto* tfed = dependencies.getDependencyInfo(triggerFed); + if (tfed->dependent) { + sendUpdatedExecRequest(triggerFed, mfed.fedID, tfed->sequenceCounter); + } } } } @@ -1100,7 +1204,7 @@ message_process_result TimeCoordinator::processTimeMessage(const ActionMessage& time_grantBase = time_granted; ActionMessage treq(CMD_TIME_GRANT); - treq.source_id = source_id; + treq.source_id = mSourceId; treq.actionTime = time_granted; lastSend.next = time_granted; lastSend.Te = time_granted; @@ -1122,26 +1226,27 @@ message_process_result TimeCoordinator::processTimeMessage(const ActionMessage& removeDependent(GlobalFederateId(cmd.source_id)); break; case CMD_REQUEST_CURRENT_TIME: - if (disconnected) { - ActionMessage treq(CMD_DISCONNECT, source_id, cmd.source_id); - sendMessageFunction(treq); - } else if (lastSend.mTimeState == TimeState::time_granted) { - ActionMessage treq(CMD_TIME_GRANT, source_id, cmd.source_id); - treq.actionTime = lastSend.next; + if (disconnected || lastSend.mTimeState == TimeState::error) { + ActionMessage treq(CMD_DISCONNECT, mSourceId, cmd.source_id); + treq.setExtraDestData(cmd.counter); sendMessageFunction(treq); } else { - ActionMessage treq(CMD_TIME_REQUEST, source_id, cmd.source_id); - treq.actionTime = lastSend.next; - treq.Tdemin = lastSend.minDe; - treq.Te = lastSend.Te; - treq.setExtraData(lastSend.minFed.baseValue()); - sendMessageFunction(treq); + auto resp = generateTimeRequest(lastSend, cmd.source_id, cmd.counter); + if (triggered) { + setActionFlag(resp, destination_target); + if (cmd.source_id == gRootBrokerID) { + triggered = false; + } + } + sendMessageFunction(resp); + dependencies.updateTime(cmd); } + return message_process_result::processed; default: break; } - if (isDelayableMessage(cmd, source_id)) { + if (isDelayableMessage(cmd, mSourceId)) { auto* dep = dependencies.getDependencyInfo(GlobalFederateId(cmd.source_id)); if (dep == nullptr) { return message_process_result::no_effect; @@ -1149,21 +1254,21 @@ message_process_result TimeCoordinator::processTimeMessage(const ActionMessage& switch (dep->mTimeState) { case TimeState::time_requested: if (dep->next > time_exec) { - return message_process_result::delay_processing; + // return message_process_result::delay_processing; } break; case TimeState::time_requested_iterative: if (dep->next > time_exec) { - return message_process_result::delay_processing; + // return message_process_result::delay_processing; } if ((iterating != IterationRequest::NO_ITERATIONS) && (time_exec == dep->next)) { - return message_process_result::delay_processing; + // return message_process_result::delay_processing; } break; case TimeState::exec_requested_iterative: if ((iterating != IterationRequest::NO_ITERATIONS) && (checkingExec) && (dep->hasData)) { - return message_process_result::delay_processing; + // return message_process_result::delay_processing; } break; default: @@ -1182,7 +1287,7 @@ message_process_result TimeCoordinator::processTimeMessage(const ActionMessage& if (checkRes.first != 0) { ActionMessage ge(CMD_GLOBAL_ERROR); ge.dest_id = parent_broker_id; - ge.source_id = source_id; + ge.source_id = mSourceId; ge.messageID = checkRes.first; ge.payload = checkRes.second; sendMessageFunction(ge); @@ -1235,43 +1340,6 @@ message_process_result TimeCoordinator::processTimeBlockMessage(const ActionMess return message_process_result::no_effect; } -void TimeCoordinator::processDependencyUpdateMessage(const ActionMessage& cmd) -{ - bool added{false}; - switch (cmd.action()) { - case CMD_ADD_DEPENDENCY: - added = addDependency(cmd.source_id); - break; - case CMD_REMOVE_DEPENDENCY: - removeDependency(cmd.source_id); - break; - case CMD_ADD_DEPENDENT: - addDependent(cmd.source_id); - break; - case CMD_REMOVE_DEPENDENT: - removeDependent(cmd.source_id); - break; - case CMD_ADD_INTERDEPENDENCY: - added = addDependency(cmd.source_id); - addDependent(cmd.source_id); - break; - case CMD_REMOVE_INTERDEPENDENCY: - removeDependency(cmd.source_id); - removeDependent(cmd.source_id); - break; - default: - break; - } - if (added) { - if (checkActionFlag(cmd, child_flag)) { - setAsChild(cmd.source_id); - } - if (checkActionFlag(cmd, parent_flag)) { - setAsParent(cmd.source_id); - } - } -} - /** set a timeProperty for a the coordinator*/ void TimeCoordinator::setProperty(int timeProperty, Time propertyVal) { diff --git a/src/helics/core/TimeCoordinator.hpp b/src/helics/core/TimeCoordinator.hpp index a95b9be27d..520eb91e8c 100644 --- a/src/helics/core/TimeCoordinator.hpp +++ b/src/helics/core/TimeCoordinator.hpp @@ -8,7 +8,7 @@ SPDX-License-Identifier: BSD-3-Clause #include "../common/GuardedTypes.hpp" #include "ActionMessage.hpp" -#include "TimeDependencies.hpp" +#include "BaseTimeCoordinator.hpp" #include "json/forwards.h" #include @@ -52,7 +52,7 @@ class tcoptions { the time coordinator manages dependencies and computes whether time can advance or enter execution mode */ -class TimeCoordinator { +class TimeCoordinator: public BaseTimeCoordinator { protected: /// the variables for time coordination TimeData upstream; @@ -78,47 +78,42 @@ class TimeCoordinator { shared_guarded_m> dependent_federates; /// these are to maintain an accessible record of dependency federates shared_guarded_m> dependency_federates; - /// federates which this Federate is temporally dependent on - TimeDependencies dependencies; /// blocks for a particular timeblocking link std::vector> timeBlocks; /// basic time control information tcoptions info; std::uint8_t currentRestrictionLevel{0}; - /// callback used to send the messages - std::function sendMessageFunction; public: - /// the identifier for inserting into the source id field of any generated messages; - GlobalFederateId source_id{0}; /// indicator that the coordinator should be iteratingif need be IterationRequest iterating{IterationRequest::NO_ITERATIONS}; - /// flag indicating that the coordinator is trying to enter the exec mode - bool checkingExec{false}; - /// flag that the coordinator has entered the execution Mode - bool executionMode{false}; + /// flag indicating that a value or message was received during initialization stage bool hasInitUpdates{false}; + /// flag indicating that a value or message was received during iteration + bool hasIterationData{false}; /// flag indicating that we need to send updates to all dependencies on receipt of addition /// request bool needSendAll{false}; + /// indicator the federate was triggered recently + mutable bool triggered{false}; + /// true if using a global time manager parent + bool globalTime{false}; protected: - bool disconnected{false}; - /// specify that the timeCoordinator should not grant times and instead operate in a continuous - /// manner until completion - bool nonGranting{false}; /// if set to true the time coordinator is joining an ongoing co-simulation bool dynamicJoining{false}; std::atomic iteration{0}; //!< current number of iterations - int32_t sequenceCounter{0}; //!< sequence counter for tracking responses public: /** default constructor*/ - TimeCoordinator(); + TimeCoordinator() = default; /** construct from a federate info and message send function*/ - explicit TimeCoordinator(std::function userSendMessageFunction); + explicit TimeCoordinator(std::function userSendMessageFunction): + BaseTimeCoordinator(std::move(userSendMessageFunction)) + { + } /** set a timeProperty for a the coordinator*/ void setProperty(int timeProperty, Time propertyVal); @@ -135,12 +130,12 @@ class TimeCoordinator { bool getOptionFlag(int optionFlag) const; /** get an option flag value*/ int getIntegerProperty(int intProperty) const; - /** set the callback function used for the sending messages*/ - void setMessageSender(std::function userSendMessageFunction); /** get the current granted time*/ Time getGrantedTime() const { return time_granted; } /** get the current granted time*/ + Time getRequestedTime() const { return time_requested; } + /** get the current granted time*/ Time allowedSendTime() const { return time_granted + info.outputDelay; } /** get a list of actual dependencies*/ std::vector getDependencies() const; @@ -156,7 +151,7 @@ class TimeCoordinator { /** compute updates to time values @return true if they have been modified */ - bool updateTimeFactors(); + virtual bool updateTimeFactors() override; /** update the time_value variable with a new value if needed if allowed it will send an updated time request message */ @@ -167,16 +162,6 @@ class TimeCoordinator { void updateMessageTime(Time messageUpdateTime, bool allowRequestSend); void specifyNonGranting(bool value = true) { nonGranting = value; } - /** get the id of the federate that has the earliest time dependency*/ - GlobalFederateId getMinDependency() const; - - private: - /** take a global id and get a pointer to the dependencyInfo for the other fed - will be nullptr if it doesn't exist - */ - DependencyInfo* getDependencyInfo(GlobalFederateId ofed); - /** check whether a federate is a dependency*/ - bool isDependency(GlobalFederateId ofed) const; private: /** helper function for computing the next event time*/ @@ -187,10 +172,13 @@ class TimeCoordinator { /** get the next possible time that a time coordinator could grant*/ Time getNextPossibleTime() const; Time generateAllowedTime(Time testTime) const; - /* return true if the skip federate was detected*/ - bool checkAndSendTimeRequest(ActionMessage& upd, GlobalFederateId skip) const; + /* return true,true if the message was sent and skip federate was detected + true,false if the message was sent and skip federate was not detected + false,false, if the message was not sent + */ + std::pair checkAndSendTimeRequest(ActionMessage& upd, GlobalFederateId skip) const; - void sendTimeRequest() const; + void sendTimeRequest(GlobalFederateId triggerFed) const; void updateTimeGrant(); /** transmit message to all federates except the skipFed, return true if skipFed was used*/ bool transmitTimingMessages(ActionMessage& msg, @@ -210,31 +198,24 @@ class TimeCoordinator { @param cmd the update command */ void processConfigUpdateMessage(const ActionMessage& cmd); - /** process a dependency update message*/ - void processDependencyUpdateMessage(const ActionMessage& cmd); /** add a federate dependency @return true if it was actually added, false if the federate was already present */ - bool addDependency(GlobalFederateId fedID); + virtual bool addDependency(GlobalFederateId fedID) override; /** add a dependent federate @return true if it was actually added, false if the federate was already present */ - bool addDependent(GlobalFederateId fedID); + virtual bool addDependent(GlobalFederateId fedID) override; /** remove a dependency @param fedID the identifier of the federate to remove*/ - void removeDependency(GlobalFederateId fedID); + virtual void removeDependency(GlobalFederateId fedID) override; /** remove a dependent @param fedID the identifier of the federate to remove*/ - void removeDependent(GlobalFederateId fedID); - /** set a federate to be a child in timekeeping hierarchy*/ - void setAsChild(GlobalFederateId fedID); - /** set a federate/broker to be the parent in the timekeeping hierarchy*/ - void setAsParent(GlobalFederateId fedID); - /** get the id of a federate acting as a parent for timekeeping*/ - GlobalFederateId getParent() const; + virtual void removeDependent(GlobalFederateId fedID) override; /** check if entry to the executing state can be granted*/ - MessageProcessingResult checkExecEntry(GlobalFederateId triggerFed = GlobalFederateId{}); + virtual MessageProcessingResult + checkExecEntry(GlobalFederateId triggerFed = GlobalFederateId{}) override; /** send updated exec request to target or everyone if target is invalid*/ void sendUpdatedExecRequest(GlobalFederateId target = GlobalFederateId{}, @@ -255,28 +236,24 @@ class TimeCoordinator { */ void enteringExecMode(IterationRequest mode); /** check if it is valid to grant a time*/ - MessageProcessingResult checkTimeGrant(); - /** disconnect*/ - void disconnect(); + MessageProcessingResult checkTimeGrant(GlobalFederateId triggerFed = GlobalFederateId{}); /** generate a local Error*/ void localError(); /** generate a string with the current time status*/ - std::string printTimeStatus() const; - /** return true if there are active dependencies*/ - bool hasActiveTimeDependencies() const; + virtual std::string printTimeStatus() const override; /** generate a configuration string(JSON)*/ void generateConfig(Json::Value& base) const; /** generate debugging time information*/ - void generateDebuggingTimeInfo(Json::Value& base) const; + virtual void generateDebuggingTimeInfo(Json::Value& base) const override; - /** get a count of the active dependencies*/ - int dependencyCount() const; /** get a count of the active dependencies*/ std::pair getMinGrantedDependency() const; /** enter initialization*/ void enterInitialization(); /** request a resend of the time message for certain federates currently blocking*/ void requestTimeCheck(); + + virtual Time getNextTime() const override; }; } // namespace helics diff --git a/src/helics/core/TimeCoordinatorProcessing.cpp b/src/helics/core/TimeCoordinatorProcessing.cpp index 6660a4cdde..dbac990a64 100644 --- a/src/helics/core/TimeCoordinatorProcessing.cpp +++ b/src/helics/core/TimeCoordinatorProcessing.cpp @@ -152,6 +152,7 @@ std::tuple } break; case CMD_REQUEST_CURRENT_TIME: + case CMD_TIMING_INFO: timeCoord->processTimeMessage(cmd); break; case CMD_DISCONNECT_FED: @@ -247,7 +248,8 @@ std::tuple break; } if (!timeGranted_mode) { - proc = timeCoord->checkTimeGrant(); + proc = timeCoord->checkTimeGrant( + cmd.action() == CMD_TIME_REQUEST ? cmd.source_id : GlobalFederateId{}); if (returnableResult(proc)) { newMode = true; } diff --git a/src/helics/core/TimeDependencies.cpp b/src/helics/core/TimeDependencies.cpp index 7f82e8c98d..94d88b2505 100644 --- a/src/helics/core/TimeDependencies.cpp +++ b/src/helics/core/TimeDependencies.cpp @@ -36,6 +36,12 @@ static DependencyProcessingResult processMessage(const ActionMessage& m, Depende dep.sequenceCounter = m.counter; dep.minFed = GlobalFederateId(m.getExtraData()); dep.responseSequenceCounter = m.getExtraDestData(); + if (dep.connection == ConnectionType::self) { + dep.responseSequenceCounter = dep.sequenceCounter; + } + if (dep.responseSequenceCounter == dep.grantedIteration) { + dep.updateRequested = false; + } break; case CMD_EXEC_GRANT: if (!checkActionFlag(m, iteration_requested_flag)) { @@ -74,14 +80,24 @@ static DependencyProcessingResult processMessage(const ActionMessage& m, Depende } dep.minFed = GlobalFederateId(m.getExtraData()); - dep.nonGranting = checkActionFlag(m, non_granting_flag); + if (checkActionFlag(m, non_granting_flag)) { + dep.nonGranting = true; + } delayed = checkActionFlag(m, delayed_timing_flag); if (delayed && !dep.delayedTiming) { res = DependencyProcessingResult::PROCESSED_AND_CHECK; } - dep.delayedTiming = delayed; + if (delayed) { + dep.delayedTiming = delayed; + } + dep.triggered = checkActionFlag(m, destination_target); dep.sequenceCounter = m.counter; - dep.responseSequenceCounter = m.getExtraDestData(); + dep.responseSequenceCounter = (dep.connection != ConnectionType::self) ? + m.getExtraDestData() : + dep.sequenceCounter; + if (dep.responseSequenceCounter == dep.grantedIteration) { + dep.updateRequested = false; + } break; case CMD_TIME_GRANT: dep.mTimeState = TimeState::time_granted; @@ -110,6 +126,13 @@ static DependencyProcessingResult processMessage(const ActionMessage& m, Depende dep.minFed = GlobalFederateId{}; dep.timeoutCount = 0; dep.hasData = false; + dep.updateRequested = false; + break; + case CMD_TIMING_INFO: + dep.nonGranting = checkActionFlag(m, non_granting_flag); + dep.delayedTiming = checkActionFlag(m, delayed_timing_flag); + dep.timingVersion = static_cast(m.getExtraData()); + res = DependencyProcessingResult::PROCESSED_AND_CHECK; break; case CMD_LOCAL_ERROR: case CMD_GLOBAL_ERROR: @@ -125,6 +148,9 @@ static DependencyProcessingResult processMessage(const ActionMessage& m, Depende case CMD_PUB: dep.hasData = true; break; + case CMD_REQUEST_CURRENT_TIME: + dep.sequenceCounter = m.counter; + break; default: res = DependencyProcessingResult::NOT_PROCESSED; break; @@ -214,6 +240,12 @@ static std::string_view timeStateString(TimeState state) } } +void addTimeState(Json::Value& output, const TimeState state) +{ + auto sstring = timeStateString(state); + output["state"] = Json::Value(sstring.data(), sstring.data() + sstring.size()); +} + void generateJsonOutputTimeData(Json::Value& output, const TimeData& dep, bool includeAggregates) { output["next"] = static_cast(dep.next); @@ -221,10 +253,10 @@ void generateJsonOutputTimeData(Json::Value& output, const TimeData& dep, bool i output["minde"] = static_cast(dep.minDe); output["minfed"] = dep.minFed.baseValue(); output["responseSequence"] = dep.responseSequenceCounter; - auto sstring = timeStateString(dep.mTimeState); - output["state"] = Json::Value(sstring.data(), sstring.data() + sstring.size()); + addTimeState(output, dep.mTimeState); output["iteration"] = dep.sequenceCounter; output["granted_iteration"] = dep.grantedIteration; + output["sequenceCounter"] = dep.sequenceCounter; if (includeAggregates) { output["minde_alt"] = static_cast(dep.minDe); output["minfedActual"] = dep.minFedActual.baseValue(); @@ -398,20 +430,21 @@ bool TimeDependencies::checkIfReadyForExecEntry(bool iterating, bool waiting) co if (iterating) { if (waiting) { for (const auto& dep : dependencies) { - if (dep.dependency) { - if (dep.connection == ConnectionType::self) { - continue; - } - if (dep.mTimeState == TimeState::initialized) { - if (dep.grantedIteration == 0) { - return false; - } + if (!dep.dependency) { + continue; + } + if (dep.connection == ConnectionType::self) { + continue; + } + if (dep.mTimeState == TimeState::initialized) { + if (dep.grantedIteration == 0) { + return false; } - if (dep.mTimeState == TimeState::exec_requested_iterative || - dep.mTimeState == TimeState::exec_requested_require_iteration) { - if (dep.sequenceCounter < dep.grantedIteration) { - return false; - } + } + if (dep.mTimeState == TimeState::exec_requested_iterative || + dep.mTimeState == TimeState::exec_requested_require_iteration) { + if (dep.sequenceCounter < dep.grantedIteration) { + return false; } } } @@ -435,17 +468,90 @@ bool TimeDependencies::checkIfReadyForExecEntry(bool iterating, bool waiting) co }); } +bool TimeDependencies::checkIfReadyForTimeGrant(bool iterating, + Time desiredGrantTime, + bool waiting) const +{ + if (iterating) { + for (const auto& dep : dependencies) { + if (!dep.dependency || dep.next >= cBigTime) { + continue; + } + if (dep.connection == ConnectionType::self) { + continue; + } + if (dep.next < desiredGrantTime) { + return false; + } + if ((dep.next == desiredGrantTime) && (dep.mTimeState == TimeState::time_granted)) { + return false; + } + if (waiting) { + if (dep.mTimeState == TimeState::time_requested_iterative || + dep.mTimeState == TimeState::time_requested_require_iteration) { + if (dep.sequenceCounter < dep.grantedIteration) { + return false; + } + } + } + } + return true; + + } else { + if (!waiting) { + for (const auto& dep : dependencies) { + if (!dep.dependency || dep.next >= cBigTime) { + continue; + } + if (dep.connection == ConnectionType::self) { + continue; + } + if (dep.next < desiredGrantTime) { + return false; + } + if (dep.next == desiredGrantTime) { + if (dep.mTimeState == TimeState::time_granted) { + return false; + } + if (dep.mTimeState == TimeState::time_requested && dep.nonGranting) { + return false; + } + } + } + } else { + for (const auto& dep : dependencies) { + if (!dep.dependency || dep.next >= cBigTime) { + continue; + } + if (dep.connection == ConnectionType::self) { + continue; + } + if (dep.next <= desiredGrantTime) { + return false; + } + } + } + } + return true; +} + bool TimeDependencies::hasActiveTimeDependencies() const { return std::any_of(dependencies.begin(), dependencies.end(), [](const auto& dep) { - return (dep.dependency && (dep.fedID.isFederate()) && (dep.next < Time::maxVal())); + return (dep.dependency && (dep.fedID.isFederate()) && (dep.next < cBigTime)); }); } +bool TimeDependencies::verifySequenceCounter(Time tmin, std::int32_t sq) +{ + return std::all_of(dependencies.begin(), dependencies.end(), [tmin, sq](const auto& dep) { + return checkSequenceCounter(dep, tmin, sq); + }); +} int TimeDependencies::activeDependencyCount() const { return std::count_if(dependencies.begin(), dependencies.end(), [](const auto& dep) { - return (dep.dependency && (dep.fedID.isFederate()) && (dep.next < Time::maxVal())); + return (dep.dependency && (dep.fedID.isFederate()) && (dep.next < cBigTime)); }); } /** get a count of the active dependencies*/ @@ -454,7 +560,7 @@ GlobalFederateId TimeDependencies::getMinDependency() const GlobalFederateId minID; Time minTime(Time::maxVal()); for (const auto& dep : dependencies) { - if (dep.dependency && (dep.fedID.isFederate()) && (dep.next < Time::maxVal())) { + if (dep.dependency && (dep.fedID.isFederate()) && (dep.next < cBigTime)) { if (dep.next < minTime) { minTime = dep.next; minID = dep.fedID; @@ -478,41 +584,6 @@ void TimeDependencies::resetIteratingExecRequests() } } -bool TimeDependencies::checkIfReadyForTimeGrant(bool iterating, Time desiredGrantTime) const -{ - if (iterating) { - for (const auto& dep : dependencies) { - if (!dep.dependency) { - continue; - } - if (dep.next < desiredGrantTime) { - return false; - } - if ((dep.next == desiredGrantTime) && (dep.mTimeState == TimeState::time_granted)) { - return false; - } - } - } else { - for (const auto& dep : dependencies) { - if (!dep.dependency) { - continue; - } - if (dep.next < desiredGrantTime) { - return false; - } - if (dep.next == desiredGrantTime) { - if (dep.mTimeState == TimeState::time_granted) { - return false; - } - if (dep.mTimeState == TimeState::time_requested && dep.nonGranting) { - return false; - } - } - } - } - return true; -} - void TimeDependencies::resetIteratingTimeRequests(helics::Time requestTime) { for (auto& dep : dependencies) { @@ -558,7 +629,8 @@ std::pair TimeDependencies::checkForIssues(bool waiting) const static void generateMinTimeImplementation(TimeData& mTime, const DependencyInfo& dep, - GlobalFederateId ignore) + GlobalFederateId ignore, + std::int32_t sequenceCode) { if (dep.mTimeState < TimeState::time_granted) { if (dep.fedID == ignore) { @@ -570,7 +642,7 @@ static void generateMinTimeImplementation(TimeData& mTime, mTime.delayedTiming = dep.delayedTiming; mTime.restrictionLevel = dep.restrictionLevel; mTime.sequenceCounter = dep.sequenceCounter; - mTime.responseSequenceCounter = dep.sequenceCounter; + mTime.responseSequenceCounter = dep.responseSequenceCounter; } else if (dep.mTimeState == mTime.mTimeState) { if (dep.restrictionLevel < mTime.restrictionLevel) { mTime.minFed = dep.fedID; @@ -585,6 +657,9 @@ static void generateMinTimeImplementation(TimeData& mTime, mTime.responseSequenceCounter = dep.sequenceCounter; } } + mTime.next = initializationTime; + mTime.Te = timeZero; + mTime.minDe = timeZero; return; } @@ -598,7 +673,9 @@ static void generateMinTimeImplementation(TimeData& mTime, return; } - if (dep.connection != ConnectionType::self) { + if (dep.connection != ConnectionType::self && + (sequenceCode == 0 || dep.responseSequenceCounter == sequenceCode || + dep.timingVersion == 0 || !dep.dependent)) { if (dep.minDe >= dep.next) { if (dep.minDe < mTime.minDe) { mTime.minDe = dep.minDe; @@ -608,6 +685,14 @@ static void generateMinTimeImplementation(TimeData& mTime, // therefore it can't be used to determine a time grant mTime.minDe = -1; } + } else if (dep.responseSequenceCounter == sequenceCode && dep.dependent) { + if (dep.minDe >= dep.next && dep.minDe < mTime.minDe) { + mTime.minDe = dep.minDe; + } + } else { + if (dep.next < mTime.minDe) { + mTime.minDe = dep.next; + } } if (dep.next < mTime.next) { mTime.next = dep.next; @@ -618,21 +703,23 @@ static void generateMinTimeImplementation(TimeData& mTime, mTime.mTimeState = dep.mTimeState; } } - if (dep.connection != ConnectionType::self) { - if (dep.Te < mTime.Te) { - mTime.TeAlt = mTime.Te; - mTime.Te = dep.Te; + // if (dep.connection != ConnectionType::self) { + if (dep.Te < mTime.Te) { + mTime.TeAlt = mTime.Te; + mTime.Te = dep.Te; + mTime.minFed = dep.fedID; + mTime.sequenceCounter = dep.sequenceCounter; + mTime.responseSequenceCounter = dep.sequenceCounter; + if (dep.minFed.isValid()) { + mTime.minFedActual = dep.minFed; + } else { mTime.minFed = dep.fedID; - if (dep.minFed.isValid()) { - mTime.minFedActual = dep.minFed; - } else { - mTime.minFed = dep.fedID; - } - } else if (dep.Te == mTime.Te) { - mTime.minFed = GlobalFederateId{}; - mTime.TeAlt = mTime.Te; } + } else if (dep.Te == mTime.Te) { + mTime.minFed = GlobalFederateId{}; + mTime.TeAlt = mTime.Te; } + // } } const DependencyInfo& getExecEntryMinFederate(const TimeDependencies& dependencies, @@ -677,7 +764,8 @@ const DependencyInfo& getExecEntryMinFederate(const TimeDependencies& dependenci TimeData generateMinTimeUpstream(const TimeDependencies& dependencies, bool restricted, GlobalFederateId self, - GlobalFederateId ignore) + GlobalFederateId ignore, + std::int32_t responseCode) { TimeData mTime(Time::maxVal(), TimeState::error); std::int32_t iterationCount{0}; @@ -692,7 +780,7 @@ TimeData generateMinTimeUpstream(const TimeDependencies& dependencies, continue; } iterationCount += dep.sequenceCounter; - generateMinTimeImplementation(mTime, dep, ignore); + generateMinTimeImplementation(mTime, dep, ignore, responseCode); } if (mTime.Te < mTime.minDe) { mTime.minDe = mTime.Te; @@ -715,7 +803,8 @@ TimeData generateMinTimeUpstream(const TimeDependencies& dependencies, TimeData generateMinTimeDownstream(const TimeDependencies& dependencies, bool restricted, GlobalFederateId self, - GlobalFederateId ignore) + GlobalFederateId ignore, + std::int32_t responseCode) { TimeData mTime(Time::maxVal(), TimeState::error); for (const auto& dep : dependencies) { @@ -728,7 +817,7 @@ TimeData generateMinTimeDownstream(const TimeDependencies& dependencies, if (self.isValid() && dep.minFedActual == self) { continue; } - generateMinTimeImplementation(mTime, dep, ignore); + generateMinTimeImplementation(mTime, dep, ignore, responseCode); } if (mTime.Te < mTime.minDe) { mTime.minDe = mTime.Te; @@ -756,7 +845,8 @@ TimeData generateMinTimeDownstream(const TimeDependencies& dependencies, TimeData generateMinTimeTotal(const TimeDependencies& dependencies, bool restricted, GlobalFederateId self, - GlobalFederateId ignore) + GlobalFederateId ignore, + std::int32_t responseCode) { TimeData mTime(Time::maxVal(), TimeState::error); for (const auto& dep : dependencies) { @@ -767,7 +857,7 @@ TimeData generateMinTimeTotal(const TimeDependencies& dependencies, if (self.isValid() && dep.minFedActual == self) { continue; } - generateMinTimeImplementation(mTime, dep, ignore); + generateMinTimeImplementation(mTime, dep, ignore, responseCode); } if (mTime.Te < mTime.minDe) { diff --git a/src/helics/core/TimeDependencies.hpp b/src/helics/core/TimeDependencies.hpp index d5c02d0d71..f1739099b3 100644 --- a/src/helics/core/TimeDependencies.hpp +++ b/src/helics/core/TimeDependencies.hpp @@ -56,7 +56,9 @@ class TimeData { TimeState mTimeState{TimeState::initialized}; bool hasData{false}; //!< indicator that data was sent in the current interval bool delayedTiming{false}; //!< indicator that the dependency is using delayed timing + std::int8_t timingVersion{-2}; //!< version indicator std::uint8_t restrictionLevel{0}; //!< timing restriction level + std::int32_t timeoutCount{0}; //!< counter for timeout checking std::int32_t sequenceCounter{0}; //!< the sequence Counter of the request std::int32_t responseSequenceCounter{0}; //!< the iteration count of the min federate @@ -85,6 +87,8 @@ class DependencyInfo: public TimeData { bool dependency{false}; //!< indicator that the dependency is an actual dependency bool forwarding{false}; //!< indicator that the dependency is a forwarding time coordinator bool nonGranting{false}; //!< indicator that the dependency is a non granting time coordinator + bool triggered{false}; //!< indicator that the dependency has been triggered in some way + bool updateRequested{false}; //!< indicator that an update request is in process // Time forwardEvent{Time::maxVal()}; //!< a predicted event /** default constructor*/ DependencyInfo() = default; @@ -155,7 +159,7 @@ class TimeDependencies { @param desiredGrantTime the time to check for granting @return true if the object is ready */ - bool checkIfReadyForTimeGrant(bool iterating, Time desiredGrantTime) const; + bool checkIfReadyForTimeGrant(bool iterating, Time desiredGrantTime, bool waiting) const; /** reset the iterative exec requests to prepare for the next iteration*/ void resetIteratingExecRequests(); @@ -166,6 +170,8 @@ class TimeDependencies { void resetDependentEvents(Time grantTime); /** check if there are active dependencies*/ bool hasActiveTimeDependencies() const; + /** verify that all the sequence Counters match*/ + bool verifySequenceCounter(Time tmin, std::int32_t sq); /** get a count of the active dependencies*/ int activeDependencyCount() const; /** get a count of the active dependencies*/ @@ -180,29 +186,41 @@ class TimeDependencies { GlobalFederateId delayedDependency() const { return mDelayedDependency; } }; +inline bool checkSequenceCounter(const DependencyInfo& dep, Time tmin, std::int32_t sq) +{ + return (!dep.dependency || !dep.dependent || dep.timingVersion <= 0 || dep.next > tmin || + dep.next >= cBigTime || dep.responseSequenceCounter == sq); +} + const DependencyInfo& getExecEntryMinFederate(const TimeDependencies& dependencies, GlobalFederateId self, ConnectionType ignoreType = ConnectionType::none, GlobalFederateId ignore = GlobalFederateId{}); +static constexpr GlobalFederateId NoIgnoredFederates{}; TimeData generateMinTimeUpstream(const TimeDependencies& dependencies, bool restricted, GlobalFederateId self, - GlobalFederateId ignore = GlobalFederateId{}); + GlobalFederateId ignore, + std::int32_t responseCode); TimeData generateMinTimeDownstream(const TimeDependencies& dependencies, bool restricted, GlobalFederateId self, - GlobalFederateId ignore = GlobalFederateId{}); + GlobalFederateId ignore, + std::int32_t responseCode); TimeData generateMinTimeTotal(const TimeDependencies& dependencies, bool restricted, GlobalFederateId self, - GlobalFederateId ignore = GlobalFederateId{}); + GlobalFederateId ignore, + std::int32_t responseCode); void generateJsonOutputTimeData(Json::Value& output, const TimeData& dep, bool includeAggregates = true); +void addTimeState(Json::Value& output, const TimeState state); + void generateJsonOutputDependency(Json::Value& output, const DependencyInfo& dep); } // namespace helics diff --git a/src/helics/core/TimeoutMonitor.cpp b/src/helics/core/TimeoutMonitor.cpp index 1d0ae67e4a..c37a058c67 100644 --- a/src/helics/core/TimeoutMonitor.cpp +++ b/src/helics/core/TimeoutMonitor.cpp @@ -29,8 +29,8 @@ void TimeoutMonitor::tick(CommonCore* core) core->getIdentifier(), message); core->sendErrorToFederates(-5, message); - core->processDisconnect(); core->brokerState = BrokerBase::BrokerState::errored; + core->sendDisconnect(); core->addActionMessage(CMD_STOP); } else { // ping again ActionMessage png(CMD_PING_PRIORITY); @@ -101,7 +101,7 @@ void TimeoutMonitor::tick(CoreBroker* brk) brk->getIdentifier(), "broker lost connection with parent"); brk->sendErrorToImmediateBrokers(-5); - brk->processDisconnect(); + brk->sendDisconnect(CMD_GLOBAL_DISCONNECT); brk->brokerState = BrokerBase::BrokerState::errored; brk->addActionMessage(CMD_STOP); } else { // ping again diff --git a/src/helics/core/flagOperations.hpp b/src/helics/core/flagOperations.hpp index b8b1ae11c2..189ba27821 100644 --- a/src/helics/core/flagOperations.hpp +++ b/src/helics/core/flagOperations.hpp @@ -53,10 +53,8 @@ constexpr uint16_t non_counting_flag = empty_flag; /// overload of extra_flag2 indicating an endpoint is targeted constexpr uint16_t targeted_flag = extra_flag2; - -constexpr uint16_t filter_processing_required_flag = - extra_flag1; // overload of extra_flag1 indicating that the message requires processing for - // filters yet +/// overload of extra_flag1 indicating that the message requires processing for filters yet +constexpr uint16_t filter_processing_required_flag = extra_flag1; /// overload of extra_flag1 to indicate the request is from a non-granting federate constexpr uint16_t non_granting_flag = extra_flag1; diff --git a/src/helics/core/helicsTime.hpp b/src/helics/core/helicsTime.hpp index 3fb3d0e513..6087bd33b2 100644 --- a/src/helics/core/helicsTime.hpp +++ b/src/helics/core/helicsTime.hpp @@ -33,6 +33,8 @@ constexpr Time timeZero = Time::zeroVal(); constexpr Time timeEpsilon = Time::epsilon(); /** definition of the smallest negative increment of time*/ constexpr Time negEpsilon = -Time::epsilon(); +/** definition of large time representing simulation end*/ +constexpr Time cBigTime = Time{static_cast(HELICS_BIG_NUMBER * 1000000)}; /** common definition of currentTime in initialization mode*/ constexpr Time initializationTime = negEpsilon; diff --git a/src/helics/network/NetworkCore_impl.hpp b/src/helics/network/NetworkCore_impl.hpp index cc5b2bb08b..3835dc0fbd 100644 --- a/src/helics/network/NetworkCore_impl.hpp +++ b/src/helics/network/NetworkCore_impl.hpp @@ -57,7 +57,6 @@ bool NetworkCore::brokerConnect() CommsBroker::comms->setName(CommonCore::getIdentifier()); CommsBroker::comms->loadNetworkInfo(netInfo); CommsBroker::comms->setTimeout(BrokerBase::networkTimeout.to_ms()); - // comms->setMessageSize(maxMessageSize, maxMessageCount); auto res = CommsBroker::comms->connect(); if (res) { if (netInfo.portNumber < 0) { diff --git a/src/helics/network/test/TestComms.cpp b/src/helics/network/test/TestComms.cpp index dcbe4c7bae..4ec95ed836 100644 --- a/src/helics/network/test/TestComms.cpp +++ b/src/helics/network/test/TestComms.cpp @@ -160,11 +160,15 @@ namespace testcore { rid = pr.first; cmd = std::move(pr.second); buffer.pop(); - --allowedSend; + if (allowedSend > 0) { + --allowedSend; + } } else { std::tie(rid, cmd) = txQueue.pop(); } - + if (preProcCallback) { + preProcCallback(cmd); + } bool processed = false; if (isProtocolCommand(cmd)) { if (rid == control_route) { @@ -217,7 +221,12 @@ namespace testcore { allowedSend = 0; continue; case ALLOW_MESSAGES: - allowedSend += cmd.getExtraData(); + if (cmd.getExtraData() >= 0) { + bufferData = true; + allowedSend += cmd.getExtraData(); + } else { + bufferData = false; + } continue; } } @@ -291,6 +300,7 @@ namespace testcore { { if (getTxStatus() == connection_status::connected) { ActionMessage cmd(CMD_PROTOCOL); + cmd.messageID = ALLOW_MESSAGES; cmd.setExtraData(count); transmit(control_route, cmd); } diff --git a/src/helics/network/test/TestComms.h b/src/helics/network/test/TestComms.h index d46dac84d2..7e6a8bb38f 100644 --- a/src/helics/network/test/TestComms.h +++ b/src/helics/network/test/TestComms.h @@ -12,6 +12,7 @@ SPDX-License-Identifier: BSD-3-Clause #include #include #include +#include namespace helics { namespace testcore { @@ -28,6 +29,12 @@ namespace testcore { private: virtual void queue_rx_function() override; //!< the functional loop for the receive queue virtual void queue_tx_function() override; //!< the loop for transmitting data + void callback(std::function captureTest) + { + preProcCallback = std::move(captureTest); + } + std::function preProcCallback; + public: /** user function in the test comms to force the communication to stop immediately for * testing purposes*/ diff --git a/tests/helics/CMakeLists.txt b/tests/helics/CMakeLists.txt index 5139ad268c..4ce8187e14 100644 --- a/tests/helics/CMakeLists.txt +++ b/tests/helics/CMakeLists.txt @@ -21,6 +21,7 @@ add_subdirectory(core) add_subdirectory(network) add_subdirectory(common) add_subdirectory(system_tests) +add_subdirectory(sequencing_tests) if(HELICS_BUILD_APP_LIBRARY) add_subdirectory(apps) diff --git a/tests/helics/application_api/FilterAdditionalTests.cpp b/tests/helics/application_api/FilterAdditionalTests.cpp index 698554f009..0446d7fb12 100644 --- a/tests/helics/application_api/FilterAdditionalTests.cpp +++ b/tests/helics/application_api/FilterAdditionalTests.cpp @@ -24,6 +24,10 @@ This test case sets reroute filter on a source endpoint. This means message sent from this endpoint will be rerouted to a new destination endpoint. */ +static const auto testNamer = [](const ::testing::TestParamInfo& parameter) { + return std::string(parameter.param); +}; + class filter_type_tests: public ::testing::TestWithParam, public FederateTestFixture {}; @@ -610,4 +614,4 @@ TEST_P(filter_type_tests, test_filter_info_field_ci_skip) mFed->finalizeComplete(); } -INSTANTIATE_TEST_SUITE_P(filter_tests, filter_type_tests, ::testing::ValuesIn(CoreTypes)); +INSTANTIATE_TEST_SUITE_P(filter, filter_type_tests, ::testing::ValuesIn(CoreTypes), testNamer); diff --git a/tests/helics/application_api/FilterTests.cpp b/tests/helics/application_api/FilterTests.cpp index d1e15c03ed..0a16f2ad3b 100644 --- a/tests/helics/application_api/FilterTests.cpp +++ b/tests/helics/application_api/FilterTests.cpp @@ -29,6 +29,9 @@ class filter_single_type_test: public ::testing::TestWithParam, public FederateTestFixture {}; +static const auto testNamer = [](const ::testing::TestParamInfo& parameter) { + return std::string(parameter.param); +}; /* class filter_all_type_test: public ::testing::TestWithParam, @@ -36,7 +39,7 @@ class filter_all_type_test: }; */ -class filter_tests: public ::testing::Test, public FederateTestFixture {}; +class filter: public ::testing::Test, public FederateTestFixture {}; /** test registration of filters*/ TEST_P(filter_single_type_test, message_filter_registration) @@ -487,8 +490,12 @@ static bool two_stage_filter_test(std::shared_ptr& mFed correct = false; } helics::cleanupHelicsLibrary(); - EXPECT_TRUE(!filterCore->isConnected()); EXPECT_TRUE(!mCore->isConnected()); + if (filterCore->isConnected()) { + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + } + EXPECT_TRUE(!filterCore->isConnected()); + return correct; } /** test a filter operator @@ -609,7 +616,7 @@ TEST_P(filter_single_type_test, message_filter_function_two_stage_broker_filter_ EXPECT_TRUE(res); } -TEST_F(filter_tests, message_filter_function_two_stage_brokerApp_filter_link) +TEST_F(filter, message_filter_function_two_stage_brokerApp_filter_link) { auto broker = AddBroker("test", 3); AddFederates("test", 1, broker, 1.0, "filter"); @@ -645,8 +652,9 @@ static const std::string rerouteType("zmq"); static const std::string rerouteType("test"); #endif -TEST_F(filter_tests, reroute_separate) +TEST_F(filter, reroute_separate) { + extraBrokerArgs = " --globaltime"; auto broker = AddBroker(rerouteType, 3); AddFederates(rerouteType, 1, broker, 1.0, "sender"); AddFederates(rerouteType, 1, broker, 1.0, "receiver"); @@ -711,8 +719,9 @@ TEST_F(filter_tests, reroute_separate) filt->finalize(); } -TEST_F(filter_tests, many_filters) +TEST_F(filter, many_filters) { + extraBrokerArgs = "--globaltime"; auto broker = AddBroker("test", 20); AddFederates("test", 1, broker, 1.0, "sender"); AddFederates("test", 1, broker, 1.0, "receiver"); @@ -782,7 +791,7 @@ TEST_F(filter_tests, many_filters) } } -TEST_F(filter_tests, many_filters_multi) +TEST_F(filter, many_filters_multi) { auto broker = AddBroker("test", 10); AddFederates("test", 1, broker, 1.0, "sender"); @@ -856,8 +865,9 @@ TEST_F(filter_tests, many_filters_multi) } } -TEST_F(filter_tests, reroute_cascade) +TEST_F(filter, reroute_cascade) { + extraBrokerArgs = "--globaltime"; auto broker = AddBroker("test", 10); AddFederates("test", 1, broker, 1.0, "sender"); AddFederates("test", 1, broker, 1.0, "receiver"); @@ -983,10 +993,13 @@ class rfcheck { void join() { id.join(); } }; -/** this test case fails as of yet with no good path to resolving it yet -TEST_F(filter_tests, reroute_cascade_2_ci_skip) +/** this test case fails as of yet with no good path to resolving it yet*/ +/* +TEST_F(filter, reroute_cascade_2_ci_skip) { + extraBrokerArgs = " --global_time --logfile=logs.txt"; auto broker = AddBroker("test", 18); + broker->setLoggingLevel(HELICS_LOG_LEVEL_TRACE); AddFederates("test", 1, broker, 1.0, "sender"); AddFederates("test_2", 9, broker, 1.0, "receiver"); AddFederates("test_2", 8, broker, 1.0, "filter"); @@ -1004,7 +1017,7 @@ TEST_F(filter_tests, reroute_cascade_2_ci_skip) std::vector filters; for (int ii = 0; ii < 8; ++ii) { auto filt = GetFederateAs(10 + ii); - auto& f1 = helics::make_filter(helics::filter_types::reroute, + auto& f1 = helics::make_filter(helics::FilterTypes::REROUTE, filt.get(), std::string("rrfilt") + std::to_string(ii)); f1.addDestinationTarget(std::string("rec") + std::to_string(ii + 1)); @@ -1043,7 +1056,7 @@ TEST_F(filter_tests, reroute_cascade_2_ci_skip) t1.join(); - for (auto& rfed : recFeds) { + for (auto& rfed : recFeds) { rfed.join(); } @@ -1063,8 +1076,10 @@ TEST_F(filter_tests, reroute_cascade_2_ci_skip) } } */ -TEST_F(filter_tests, reroute_separate2) +TEST_F(filter, reroute_separate2) { + extraBrokerArgs = " --globaltime --debugging"; + extraCoreArgs = " --debugging "; auto broker = AddBroker(rerouteType, 3); AddFederates(rerouteType, 1, broker); AddFederates(rerouteType, 1, broker); @@ -1132,8 +1147,9 @@ TEST_F(filter_tests, reroute_separate2) broker->waitForDisconnect(); } -TEST_F(filter_tests, reroute_separate3) +TEST_F(filter, reroute_separate3) { + extraBrokerArgs = " --globaltime"; auto broker = AddBroker(rerouteType, 3); AddFederates(rerouteType, 1, broker); AddFederates(rerouteType, 1, broker); @@ -1189,7 +1205,7 @@ TEST_F(filter_tests, reroute_separate3) filt->finalize(); } -TEST_F(filter_tests, reroute_separate_dest_target) +TEST_F(filter, reroute_separate_dest_target) { auto broker = AddBroker(rerouteType, 3); AddFederates(rerouteType, 1, broker, 1.0, "sender"); @@ -1252,7 +1268,7 @@ TEST_F(filter_tests, reroute_separate_dest_target) filt->finalize(); } -TEST_F(filter_tests, separate_slow_filter_ci_skip) +TEST_F(filter, separate_slow_filter_ci_skip) { auto broker = AddBroker(rerouteType, 3); AddFederates(rerouteType, 1, broker, 1.0, "sender"); @@ -1319,7 +1335,7 @@ TEST_F(filter_tests, separate_slow_filter_ci_skip) filt->finalize(); } -TEST_F(filter_tests, separate_slow_dest_filter_ci_skip) +TEST_F(filter, separate_slow_dest_filter_ci_skip) { auto broker = AddBroker(rerouteType, 3); AddFederates(rerouteType, 1, broker, 1.0, "sender"); @@ -1386,8 +1402,9 @@ TEST_F(filter_tests, separate_slow_dest_filter_ci_skip) filt->finalize(); } -TEST_F(filter_tests, reroute_separate2_5message) +TEST_F(filter, reroute_separate2_5message) { + extraBrokerArgs = " --globaltime"; auto broker = AddBroker(rerouteType, 3); AddFederates(rerouteType, 1, broker, helics::timeZero, "send"); AddFederates(rerouteType, 1, broker, helics::timeZero, "rec"); @@ -1502,8 +1519,9 @@ TEST_F(filter_tests, reroute_separate2_5message) broker->waitForDisconnect(); } -TEST_F(filter_tests, reroute_separate2_5000message_ci_skip) +TEST_F(filter, reroute_separate2_5000message_ci_skip) { + extraBrokerArgs = " --globaltime"; auto broker = AddBroker(rerouteType, 3); AddFederates(rerouteType, 1, broker, helics::timeZero, "send"); AddFederates(rerouteType, 1, broker, helics::timeZero, "rec"); @@ -1620,8 +1638,9 @@ TEST_F(filter_tests, reroute_separate2_5000message_ci_skip) broker->waitForDisconnect(); } -TEST_F(filter_tests, reroute_separate2_5message_b) +TEST_F(filter, reroute_separate2_5message_b) { + extraBrokerArgs = " --globaltime"; auto broker = AddBroker(rerouteType, 3); AddFederates(rerouteType, 1, broker, helics::timeZero, "send"); AddFederates(rerouteType, 1, broker, helics::timeZero, "rec"); @@ -1744,7 +1763,7 @@ TEST_F(filter_tests, reroute_separate2_5message_b) broker->waitForDisconnect(); } -TEST_F(filter_tests, message_filter_function_two_stage_coreApp_filter_link) +TEST_F(filter, message_filter_function_two_stage_coreApp_filter_link) { auto broker = AddBroker("test", 3); AddFederates("test", 1, broker, 1.0, "filter"); @@ -2144,6 +2163,7 @@ TEST_F(filter_test, message_clone_test) TEST_F(filter_test, message_multi_clone_test) { + extraBrokerArgs = " --globaltime"; auto broker = AddBroker("test", 4); AddFederates("test", 2, broker, 1.0, "source"); AddFederates("test", 1, broker, 1.0, "dest"); @@ -2309,9 +2329,10 @@ TEST_P(filter_single_type_test, test_filter_core_termination) EXPECT_TRUE(fFed->getCurrentMode() == helics::Federate::Modes::FINALIZE); } -INSTANTIATE_TEST_SUITE_P(filter_tests, +INSTANTIATE_TEST_SUITE_P(filter, filter_single_type_test, - ::testing::ValuesIn(CoreTypes_simple)); + ::testing::ValuesIn(CoreTypes_simple), + testNamer); /* -INSTANTIATE_TEST_SUITE_P(filter_tests, filter_all_type_test, ::testing::ValuesIn(CoreTypes_all)); +INSTANTIATE_TEST_SUITE_P(filter, filter_all_type_test, ::testing::ValuesIn(CoreTypes_all)); */ diff --git a/tests/helics/application_api/MessageFederateAdditionalTests.cpp b/tests/helics/application_api/MessageFederateAdditionalTests.cpp index ac87c29e63..78a13fee2b 100644 --- a/tests/helics/application_api/MessageFederateAdditionalTests.cpp +++ b/tests/helics/application_api/MessageFederateAdditionalTests.cpp @@ -37,6 +37,10 @@ class mfed_add_all_type_tests: class mfed_tests: public ::testing::Test, public FederateTestFixture {}; +static const auto testNamer = [](const ::testing::TestParamInfo& parameter) { + return std::string(parameter.param); +}; + TEST_P(mfed_add_single_type_tests, initialize_tests) { SetupTest(GetParam(), 1); @@ -474,11 +478,16 @@ TEST_P(mfed_add_type_tests, threefedPingPong) INSTANTIATE_TEST_SUITE_P(mfed_add_tests, mfed_add_single_type_tests, - ::testing::ValuesIn(CoreTypes_single)); -INSTANTIATE_TEST_SUITE_P(mfed_add_tests, mfed_add_type_tests, ::testing::ValuesIn(CoreTypes)); + ::testing::ValuesIn(CoreTypes_single), + testNamer); +INSTANTIATE_TEST_SUITE_P(mfed_add_tests, + mfed_add_type_tests, + ::testing::ValuesIn(CoreTypes), + testNamer); INSTANTIATE_TEST_SUITE_P(mfed_add_tests, mfed_add_all_type_tests, - ::testing::ValuesIn(CoreTypes_all)); + ::testing::ValuesIn(CoreTypes_all), + testNamer); static constexpr const char* config_files[] = {"example_message_fed.json", "example_message_fed.toml"}; diff --git a/tests/helics/application_api/MessageFederateKeyTests.cpp b/tests/helics/application_api/MessageFederateKeyTests.cpp index 2084b37a7b..9f134c69f6 100644 --- a/tests/helics/application_api/MessageFederateKeyTests.cpp +++ b/tests/helics/application_api/MessageFederateKeyTests.cpp @@ -42,6 +42,10 @@ class mfed_add_type_tests: class mfed_tests: public ::testing::Test, public FederateTestFixture {}; /** test simple creation and destruction*/ +static const auto testNamer = [](const ::testing::TestParamInfo& parameter) { + return std::string(parameter.param); +}; + TEST_P(mfed_single_type_tests, send_receive) { SetupTest(GetParam(), 1); @@ -544,6 +548,7 @@ TEST_P(mfed_all_type_tests, dual_transfer_message_broker_link_direct) auto& ept2 = vFed2->registerGlobalEndpoint("ept2"); std::this_thread::sleep_for(std::chrono::milliseconds(200)); broker->linkEndpoints("ept1", "ept2"); + broker->query("root", "global_flush"); bool res = dual_transfer_test_message(vFed1, vFed2, ept1, ept2); EXPECT_TRUE(res); } @@ -726,6 +731,12 @@ TEST_F(mfed_tests, dual_transfer_message_core_link_json_string) EXPECT_TRUE(res); } -INSTANTIATE_TEST_SUITE_P(mfed_tests, mfed_single_type_tests, ::testing::ValuesIn(CoreTypes_single)); -INSTANTIATE_TEST_SUITE_P(mfed_tests, mfed_type_tests, ::testing::ValuesIn(CoreTypes)); -INSTANTIATE_TEST_SUITE_P(mfed_tests, mfed_all_type_tests, ::testing::ValuesIn(CoreTypes_all)); +INSTANTIATE_TEST_SUITE_P(mfed_tests, + mfed_single_type_tests, + ::testing::ValuesIn(CoreTypes_single), + testNamer); +INSTANTIATE_TEST_SUITE_P(mfed_tests, mfed_type_tests, ::testing::ValuesIn(CoreTypes), testNamer); +INSTANTIATE_TEST_SUITE_P(mfed_tests, + mfed_all_type_tests, + ::testing::ValuesIn(CoreTypes_all), + testNamer); diff --git a/tests/helics/core/ForwardingTimeCoordinatorTests.cpp b/tests/helics/core/ForwardingTimeCoordinatorTests.cpp index c24a819cde..f4ae5fee9c 100644 --- a/tests/helics/core/ForwardingTimeCoordinatorTests.cpp +++ b/tests/helics/core/ForwardingTimeCoordinatorTests.cpp @@ -198,7 +198,7 @@ TEST(ftc_tests, timing_test1) ftc.addDependent(GlobalFederateId(5)); ActionMessage lastMessage(CMD_INVALID); - ftc.source_id = GlobalFederateId(1); + ftc.setSourceId(GlobalFederateId(1)); ftc.setMessageSender([&lastMessage](const helics::ActionMessage& mess) { lastMessage = mess; }); ActionMessage timeUpdate(CMD_TIME_REQUEST, fed2, GlobalFederateId(1)); @@ -209,8 +209,8 @@ TEST(ftc_tests, timing_test1) auto modified = ftc.processTimeMessage(timeUpdate); EXPECT_TRUE(modified); ftc.updateTimeFactors(); - // there should still be the exec request - EXPECT_TRUE(lastMessage.action() == CMD_EXEC_REQUEST); + // there should still be the invalid as all federates are not updated past exec + EXPECT_TRUE(lastMessage.action() == CMD_INVALID); timeUpdate.source_id = fed3; timeUpdate.actionTime = 0.5; diff --git a/tests/helics/core/TimeDependenciesTests.cpp b/tests/helics/core/TimeDependenciesTests.cpp index b6abba930f..66aad97fb5 100644 --- a/tests/helics/core/TimeDependenciesTests.cpp +++ b/tests/helics/core/TimeDependenciesTests.cpp @@ -37,6 +37,6 @@ TEST(timeDep_tests, dependency_tests) TimeDependencies depTest; depTest.setDependencyVector(deps); - auto total = generateMinTimeTotal(depTest, false, GlobalFederateId{1}, GlobalFederateId{}); + auto total = generateMinTimeTotal(depTest, false, GlobalFederateId{1}, GlobalFederateId{}, 0); EXPECT_EQ(total.next, 2.0); } diff --git a/tests/helics/sequencing_tests/CMakeLists.txt b/tests/helics/sequencing_tests/CMakeLists.txt new file mode 100644 index 0000000000..3cc5f1ef0a --- /dev/null +++ b/tests/helics/sequencing_tests/CMakeLists.txt @@ -0,0 +1,40 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Copyright (c) 2017-2022, Battelle Memorial Institute; Lawrence Livermore +# National Security, LLC; Alliance for Sustainable Energy, LLC. +# See the top-level NOTICE for additional details. +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +set(sequencing_test_sources + Sequencing1.cpp + sequencing-tests.cpp + sequencingHelpers.hpp + sequencingHelpers.cpp + ../application_api/testFixtures.cpp + ../application_api/testFixtures.hpp + ../coreTypeLists.hpp +) + +add_executable(sequencing-tests ${sequencing_test_sources}) +target_link_libraries(sequencing-tests helics_application_api helics_test_base gmock) + +set_target_properties(sequencing-tests PROPERTIES FOLDER tests) + +target_compile_definitions( + sequencing-tests PRIVATE "-DHELICS_BROKER_LOCATION=\"${HELICS_BROKER_LOC}\"" +) +target_compile_definitions( + sequencing-tests PRIVATE "-DHELICS_INSTALL_LOCATION=\"${CMAKE_INSTALL_PREFIX}\"" +) +target_compile_definitions( + sequencing-tests PRIVATE "-DTEST_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/../test_files/\"" +) + +# add_test(NAME application-api-tests COMMAND application-api-tests) set_property(TEST +# application-api-tests PROPERTY LABELS ApplicationApi Coverage Daily) + +# Tests for Continuous Integration builds add_test(NAME application-api-ci-tests COMMAND +# application-api-tests --gtest_filter=-*ci_skip*) set_property(TEST application-api-ci-tests +# PROPERTY LABELS ApplicationApiCI Continuous) diff --git a/tests/helics/sequencing_tests/Sequencing1.cpp b/tests/helics/sequencing_tests/Sequencing1.cpp new file mode 100644 index 0000000000..188230fadc --- /dev/null +++ b/tests/helics/sequencing_tests/Sequencing1.cpp @@ -0,0 +1,290 @@ +/* +Copyright (c) 2017-2022, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable +Energy, LLC. See the top-level NOTICE for additional details. All rights reserved. +SPDX-License-Identifier: BSD-3-Clause +*/ + +#include "../application_api/testFixtures.hpp" +#include "helics/application_api.hpp" +#include "helics/application_api/BrokerApp.hpp" +#include "helics/application_api/CombinationFederate.hpp" +#include "helics/application_api/CoreApp.hpp" +#include "helics/core/CommonCore.hpp" +#include "helics/core/Core.hpp" +#include "sequencingHelpers.hpp" + +#include +#include +#include +#include +/** these test cases test out the message federates + */ + +class sequencing1: public ::testing::TestWithParam, public FederateTestFixture {}; + +TEST_P(sequencing1, send_receive_2fed_multisend) +{ + SetupTest("test_3", 2); + auto mFed1 = GetFederateAs(0); + auto mFed2 = GetFederateAs(1); + + auto epid = mFed1->registerEndpoint("ep1"); + auto epid2 = mFed2->registerGlobalEndpoint("ep2", "random"); + // mFed1->getCorePointer()->setLoggingLevel(0, 5); + mFed1->setProperty(HELICS_PROPERTY_TIME_DELTA, 1.0); + mFed2->setProperty(HELICS_PROPERTY_TIME_DELTA, 1.0); + epid.setDefaultDestination("ep2"); + + auto delay = helics::delayMessages(mFed1.get(), GetParam(), 500); + + auto f1finish = std::async(std::launch::async, [&]() { mFed1->enterExecutingMode(); }); + mFed2->enterExecutingMode(); + f1finish.wait(); + + EXPECT_TRUE(mFed1->getCurrentMode() == helics::Federate::Modes::EXECUTING); + EXPECT_TRUE(mFed2->getCurrentMode() == helics::Federate::Modes::EXECUTING); + + helics::SmallBuffer data1(500, 'a'); + + epid.send(data1); + // move the time to 1.0 + auto f1time = std::async(std::launch::async, [&]() { return mFed1->requestTime(1.0); }); + auto gtime = mFed2->requestTime(1.0); + + EXPECT_EQ(gtime, 1.0); + EXPECT_EQ(f1time.get(), 1.0); + + EXPECT_TRUE(!mFed1->hasMessage()); + + EXPECT_TRUE(!mFed1->hasMessage(epid)); + auto cnt = mFed2->pendingMessageCount(epid2); + EXPECT_EQ(cnt, 1); + + auto M1 = mFed2->getMessage(epid2); + ASSERT_TRUE(M1); + ASSERT_EQ(M1->data.size(), data1.size()); + + EXPECT_EQ(M1->data[245], data1[245]); + + EXPECT_EQ(M1->time, 0.0); + mFed1->finalizeAsync(); + mFed2->finalize(); + mFed1->finalizeComplete(); + EXPECT_TRUE(mFed1->getCurrentMode() == helics::Federate::Modes::FINALIZE); + EXPECT_TRUE(mFed2->getCurrentMode() == helics::Federate::Modes::FINALIZE); + delay.wait(); +} + +static const auto testNamer = [](const ::testing::TestParamInfo& parameter) { + return std::to_string(parameter.param); +}; + +INSTANTIATE_TEST_SUITE_P(sequencing_tests, + sequencing1, + ::testing::ValuesIn({5, 6, 7, 8, 9}), + testNamer); + +class sequencing2: public ::testing::TestWithParam, public FederateTestFixture {}; + +static bool two_stage_filter_test(std::shared_ptr& mFed, + std::shared_ptr& fFed1, + std::shared_ptr& fFed2, + helics::Endpoint& p1, + helics::Endpoint& p2, + helics::Filter& f1, + helics::Filter& f2) +{ + bool correct = true; + + auto timeOperator = std::make_shared(); + timeOperator->setTimeFunction([](helics::Time time_in) { return time_in + 1.25; }); + fFed1->setFilterOperator(f1, timeOperator); + fFed2->setFilterOperator(f2, timeOperator); + + fFed1->enterExecutingModeAsync(); + fFed2->enterExecutingModeAsync(); + mFed->enterExecutingMode(); + fFed1->enterExecutingModeComplete(); + fFed2->enterExecutingModeComplete(); + + auto& p2Name = p2.getName(); + EXPECT_TRUE(fFed1->getCurrentMode() == helics::Federate::Modes::EXECUTING); + helics::SmallBuffer data(500, 'a'); + p1.sendTo(data, p2Name); + + mFed->requestTimeAsync(1.0); + fFed1->requestTimeAsync(1.0); + fFed2->requestTime(1.0); + mFed->requestTimeComplete(); + fFed1->requestTimeComplete(); + auto res = mFed->hasMessage(); + EXPECT_TRUE(!res); + if (res) { + correct = false; + } + + mFed->requestTimeAsync(2.0); + fFed2->requestTimeAsync(2.0); + fFed1->requestTime(2.0); + mFed->requestTimeComplete(); + fFed2->requestTimeComplete(); + if (mFed->hasMessage(p2)) { + correct = false; + } + + fFed1->requestTimeAsync(3.0); + fFed2->requestTimeAsync(3.0); + /*auto retTime = */ mFed->requestTime(3.0); + if (!mFed->hasMessage(p2)) { + printf("missing message\n"); + correct = false; + } + if (mFed->hasMessage(p2)) { + auto m2 = mFed->getMessage(p2); + const auto& ept1Name = p1.getName(); + if (ept1Name.size() > 1) { + EXPECT_EQ(m2->source, p1.getName()); + EXPECT_EQ(m2->original_source, p1.getName()); + } + + EXPECT_EQ(m2->dest, p2Name); + EXPECT_EQ(m2->data.size(), data.size()); + EXPECT_EQ(m2->time, 2.5); + } + + fFed1->requestTimeComplete(); + fFed2->requestTimeComplete(); + auto filterCore = fFed1->getCorePointer(); + auto mCore = mFed->getCorePointer(); + mFed->finalizeAsync(); + fFed1->finalizeAsync(); + fFed2->finalize(); + mFed->finalizeComplete(); + fFed1->finalizeComplete(); + EXPECT_TRUE(fFed1->getCurrentMode() == helics::Federate::Modes::FINALIZE); + if (fFed1->getCurrentMode() != helics::Federate::Modes::FINALIZE) { + correct = false; + } + helics::cleanupHelicsLibrary(); + EXPECT_TRUE(!mCore->isConnected()); + if (filterCore->isConnected()) { + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + } + EXPECT_TRUE(!filterCore->isConnected()); + + return correct; +} + +TEST_P(sequencing2, message_filter_function_two_stage) +{ + extraBrokerArgs = " --maxcosimduration=15000 "; + auto broker = AddBroker("test", 3); + AddFederates("test", 1, broker, 1.0, "filter"); + AddFederates("test", 1, broker, 1.0, "filter2"); + AddFederates("test", 1, broker, 1.0, "message"); + + auto fFed = GetFederateAs(0); + auto fFed2 = GetFederateAs(1); + auto mFed = GetFederateAs(2); + + auto& p1 = mFed->registerGlobalEndpoint("port1"); + auto& p2 = mFed->registerGlobalEndpoint("port2"); + + auto& f1 = fFed->registerFilter("filter1"); + f1.addSourceTarget("port1"); + EXPECT_TRUE(f1.getHandle().isValid()); + + auto& f2 = fFed2->registerFilter("filter2"); + f2.addSourceTarget("port1"); + EXPECT_TRUE(f2.getHandle().isValid()); + + fFed->setProperty(HELICS_PROPERTY_TIME_GRANT_TIMEOUT, 1.0); + fFed2->setProperty(HELICS_PROPERTY_TIME_GRANT_TIMEOUT, 1.0); + mFed->setProperty(HELICS_PROPERTY_TIME_GRANT_TIMEOUT, 1.0); + auto delay = helics::delayMessages(fFed2.get(), GetParam(), 500); + bool res = two_stage_filter_test(mFed, fFed, fFed2, p1, p2, f1, f2); + EXPECT_TRUE(res); + delay.wait(); +} + +INSTANTIATE_TEST_SUITE_P(sequencing_tests, sequencing2, ::testing::Range(5, 15), testNamer); + +class sequencing3: public ::testing::TestWithParam, public FederateTestFixture {}; + +constexpr char* rerouteType = "test"; + +TEST_P(sequencing3, reroute_separate2) +{ + extraBrokerArgs = " --globaltime --debugging"; + extraCoreArgs = " --debugging "; + auto broker = AddBroker(rerouteType, 3); + AddFederates(rerouteType, 1, broker); + AddFederates(rerouteType, 1, broker); + AddFederates(rerouteType, 1, broker); + + auto send = GetFederateAs(0); + auto rec = GetFederateAs(1); + auto filt = GetFederateAs(2); + + auto& p1 = send->registerGlobalEndpoint("send"); + auto& p2 = rec->registerGlobalEndpoint("rec"); + p1.setDefaultDestination("rec"); + auto& p3 = filt->registerGlobalEndpoint("reroute"); + + auto& f1 = helics::make_filter(helics::FilterTypes::REROUTE, filt.get(), "rrfilt"); + + f1.addSourceTarget("send"); + f1.setString("newdestination", "reroute"); + auto delay = helics::delayMessages(filt.get(), GetParam(), 400); + auto act1 = [&p1, &send]() { + send->enterExecutingMode(); + helics::Time tr = helics::timeZero; + while (tr < 10.0) { + p1.send("this is a message"); + tr = send->requestTimeAdvance(1.0); + } + send->finalize(); + }; + + auto act2 = [&rec]() { + rec->enterExecutingMode(); + helics::Time tr = helics::timeZero; + while (tr < 10.0) { + tr = rec->requestTimeAdvance(1.0); + } + rec->finalize(); + }; + + int cnt{0}; + std::vector> res; + auto act3 = [&filt, &cnt, &res, &p3]() { + filt->enterExecutingMode(); + helics::Time tr = helics::timeZero; + while (tr < 20.0) { + tr = filt->requestTime(helics::Time::maxVal()); + ++cnt; + res.emplace_back(tr, p3.pendingMessageCount()); + } + }; + + auto t1 = std::thread(act1); + auto t2 = std::thread(act2); + auto t3 = std::thread(act3); + + t1.join(); + t2.join(); + + // std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + + // auto res = broker->query("root", "global_time_debugging"); + t3.join(); + filt->finalize(); + EXPECT_EQ(p2.pendingMessageCount(), 0U); + EXPECT_EQ(p3.pendingMessageCount(), 10U); + EXPECT_EQ(cnt, 11); + // auto res2 = broker->query("root", "global_time_debugging"); + broker->waitForDisconnect(); +} + +INSTANTIATE_TEST_SUITE_P(sequencing_tests, sequencing3, ::testing::Range(26, 28), testNamer); diff --git a/tests/helics/sequencing_tests/sequencing-tests.cpp b/tests/helics/sequencing_tests/sequencing-tests.cpp new file mode 100644 index 0000000000..9cc631cc79 --- /dev/null +++ b/tests/helics/sequencing_tests/sequencing-tests.cpp @@ -0,0 +1,17 @@ +/* +Copyright (c) 2017-2022, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable +Energy, LLC. See the top-level NOTICE for additional details. All rights reserved. +SPDX-License-Identifier: BSD-3-Clause +*/ + +#include "helics/application_api/Federate.hpp" + +#include "gtest/gtest.h" + +struct globalTestConfig: public ::testing::Environment { + virtual void TearDown() override { helics::cleanupHelicsLibrary(); } +}; + +// register the global setup and teardown structure +::testing::Environment* const foo_env = ::testing::AddGlobalTestEnvironment(new globalTestConfig); diff --git a/tests/helics/sequencing_tests/sequencingHelpers.cpp b/tests/helics/sequencing_tests/sequencingHelpers.cpp new file mode 100644 index 0000000000..4940a3d8d2 --- /dev/null +++ b/tests/helics/sequencing_tests/sequencingHelpers.cpp @@ -0,0 +1,51 @@ +/* +Copyright (c) 2017-2022, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable +Energy, LLC. See the top-level NOTICE for additional details. All rights reserved. +SPDX-License-Identifier: BSD-3-Clause +*/ + +#include "sequencingHelpers.hpp" + +#include "helics/application_api/Federate.hpp" +#include "helics/core/CommonCore.hpp" +#include "helics/core/CoreBroker.hpp" +#include "helics/network/CommsBroker.hpp" +#include "helics/network/test/TestComms.h" + +#include + +namespace helics { +std::future delayMessages(Federate* fed, int preDelayMessageCount, int msecDelay) +{ + auto tcore = std::dynamic_pointer_cast< + helics::CommsBroker>( + fed->getCorePointer()); + if (!tcore) { + throw(std::invalid_argument(" given federate is not using test core")); + } + auto* tcomms = tcore->getCommsObjectPointer(); + tcomms->allowMessages(preDelayMessageCount); + auto tcommAllow = std::async(std::launch::async, [tcomms, msecDelay]() { + std::this_thread::sleep_for(std::chrono::milliseconds(msecDelay)); + tcomms->allowMessages(-1); + }); + return tcommAllow; +} + +std::future delayMessages(Broker* brk, int preDelayMessageCount, int msecDelay) +{ + auto tbrk = + dynamic_cast*>(brk); + if (brk == nullptr) { + throw(std::invalid_argument(" given broker is not using test core")); + } + auto* tcomms = tbrk->getCommsObjectPointer(); + tcomms->allowMessages(preDelayMessageCount); + auto tcommAllow = std::async(std::launch::async, [tcomms, msecDelay]() { + std::this_thread::sleep_for(std::chrono::milliseconds(msecDelay)); + tcomms->allowMessages(-1); + }); + return tcommAllow; +} +} // namespace helics diff --git a/tests/helics/sequencing_tests/sequencingHelpers.hpp b/tests/helics/sequencing_tests/sequencingHelpers.hpp new file mode 100644 index 0000000000..e2ce61f711 --- /dev/null +++ b/tests/helics/sequencing_tests/sequencingHelpers.hpp @@ -0,0 +1,20 @@ +/* +Copyright (c) 2017-2022, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable +Energy, LLC. See the top-level NOTICE for additional details. All rights reserved. +SPDX-License-Identifier: BSD-3-Clause +*/ +#pragma once + +#include "helics/helics-config.h" + +#include + +namespace helics { +class Federate; +class Broker; + +std::future delayMessages(Federate* fed, int preDelayMessageCount, int msecDelay); + +std::future delayMessages(Broker* brk, int preDelayMessageCount, int msecDelay); +} // namespace helics diff --git a/tests/helics/shared_library/FilterTests.cpp b/tests/helics/shared_library/FilterTests.cpp index 3e7a9be15b..2ba7b44095 100644 --- a/tests/helics/shared_library/FilterTests.cpp +++ b/tests/helics/shared_library/FilterTests.cpp @@ -23,7 +23,7 @@ class filter_type_tests: public ::testing::TestWithParam, public Fe }; */ -class filter_tests: public FederateTestFixture, public ::testing::Test {}; +class filter: public FederateTestFixture, public ::testing::Test {}; /** test registration of filters*/ @@ -131,7 +131,7 @@ TEST_P(filter_simple_type_tests, info_tests) CE(helicsFederateFinalizeComplete(mFed, &err)); } -TEST_F(filter_tests, core_filter_reg) +TEST_F(filter, core_filter_reg) { CE(auto core1 = helicsCreateCore("test", "core1", "--autobroker", &err)); @@ -517,7 +517,7 @@ TEST_P(filter_simple_type_tests, message_filter_function3) EXPECT_TRUE(state == HELICS_STATE_FINALIZE); } -TEST_F(filter_tests, clone_test) +TEST_F(filter, clone_test) { HelicsBroker broker = AddBroker("test", 3); AddFederates(helicsCreateMessageFederate, "test", 1, broker, 1.0, "source"); @@ -590,7 +590,7 @@ TEST_F(filter_tests, clone_test) EXPECT_TRUE(state == HELICS_STATE_FINALIZE); } -TEST_F(filter_tests, clone_test_connections) +TEST_F(filter, clone_test_connections) { HelicsBroker broker = AddBroker("test", 3); AddFederates(helicsCreateMessageFederate, "test", 1, broker, 1.0, "source"); @@ -676,7 +676,7 @@ TEST_F(filter_tests, clone_test_connections) EXPECT_TRUE(state == HELICS_STATE_FINALIZE); } -TEST_F(filter_tests, clone_test_broker_connections) +TEST_F(filter, clone_test_broker_connections) { HelicsBroker broker = AddBroker("test", 3); AddFederates(helicsCreateMessageFederate, "test", 1, broker, 1.0, "source"); @@ -753,7 +753,7 @@ TEST_F(filter_tests, clone_test_broker_connections) } // this tests using a remote core to connect an endpoint to a cloning destination filter -TEST_F(filter_tests, clone_test_dest_connections) +TEST_F(filter, clone_test_dest_connections) { HelicsBroker broker = AddBroker("test", 3); AddFederates(helicsCreateMessageFederate, "test", 1, broker, 1.0, "source"); @@ -841,7 +841,7 @@ TEST_F(filter_tests, clone_test_dest_connections) EXPECT_TRUE(state == HELICS_STATE_FINALIZE); } -TEST_F(filter_tests, clone_test_broker_dest_connections) +TEST_F(filter, clone_test_broker_dest_connections) { HelicsBroker broker = AddBroker("test", 3); AddFederates(helicsCreateMessageFederate, "test", 1, broker, 1.0, "source"); @@ -924,8 +924,9 @@ TEST_F(filter_tests, clone_test_broker_dest_connections) EXPECT_TRUE(state == HELICS_STATE_FINALIZE); } -TEST_F(filter_tests, multi_clone_test) +TEST_F(filter, multi_clone_test) { + extraBrokerArgs = " --globaltime"; HelicsBroker broker = AddBroker("test", 4); AddFederates(helicsCreateMessageFederate, "test", 2, broker, 1.0, "source"); AddFederates(helicsCreateMessageFederate, "test", 1, broker, 1.0, "dest"); @@ -1034,7 +1035,7 @@ TEST_F(filter_tests, multi_clone_test) EXPECT_TRUE(state == HELICS_STATE_FINALIZE); } -TEST_F(filter_tests, file_load) +TEST_F(filter, file_load) { std::string filename = std::string(TEST_DIR) + "/example_filters.json"; auto mFed = helicsCreateMessageFederateFromConfig(filename.c_str(), &err); @@ -1064,7 +1065,7 @@ static HelicsMessage filterFunc1(HelicsMessage mess, void* /*unused*/) return mess; } -TEST_F(filter_tests, callback_test) +TEST_F(filter, callbacks) { HelicsBroker broker = AddBroker("test", 2); AddFederates(helicsCreateMessageFederate, "test", 1, broker, 1.0, "filter"); @@ -1133,9 +1134,7 @@ TEST_F(filter_tests, callback_test) EXPECT_TRUE(state == HELICS_STATE_FINALIZE); } -INSTANTIATE_TEST_SUITE_P(filter_tests, - filter_simple_type_tests, - ::testing::ValuesIn(CoreTypes_simple)); +INSTANTIATE_TEST_SUITE_P(filter, filter_simple_type_tests, ::testing::ValuesIn(CoreTypes_simple)); /* -INSTANTIATE_TEST_SUITE_P(filter_tests, filter_type_tests, ::testing::ValuesIn(CoreTypes)); +INSTANTIATE_TEST_SUITE_P(filter, filter_type_tests, ::testing::ValuesIn(CoreTypes)); */ diff --git a/tests/helics/shared_library/badInputTests.cpp b/tests/helics/shared_library/badInputTests.cpp index c2bb6ffee6..5926a97767 100644 --- a/tests/helics/shared_library/badInputTests.cpp +++ b/tests/helics/shared_library/badInputTests.cpp @@ -1031,7 +1031,7 @@ TEST_F(function_tests, messageFed_message_object) } // test error paths for filters -TEST_F(function_tests, filter_tests) +TEST_F(function_tests, filter) { SetupTest(helicsCreateMessageFederate, "test", 1); diff --git a/tests/helics/system_tests/TimingTests.cpp b/tests/helics/system_tests/TimingTests.cpp index 9cffc71eb4..3cd4453a86 100644 --- a/tests/helics/system_tests/TimingTests.cpp +++ b/tests/helics/system_tests/TimingTests.cpp @@ -19,10 +19,10 @@ SPDX-License-Identifier: BSD-3-Clause #include "helics/helics-config.h" #include "helics/helics.hpp" -struct timing_tests: public FederateTestFixture, public ::testing::Test {}; +struct timing: public FederateTestFixture, public ::testing::Test {}; /** just a check that in the simple case we do actually get the time back we requested*/ -TEST_F(timing_tests, simple_timing_test) +TEST_F(timing, simple_timing) { SetupTest("test", 2); auto vFed1 = GetFederateAs(0); @@ -48,7 +48,7 @@ TEST_F(timing_tests, simple_timing_test) vFed2->finalize(); } -TEST_F(timing_tests, simple_timing_test2) +TEST_F(timing, simple_timing2) { SetupTest("test", 2); auto vFed1 = GetFederateAs(0); @@ -79,7 +79,7 @@ TEST_F(timing_tests, simple_timing_test2) vFed2->finalize(); } -TEST_F(timing_tests, simple_timing_test_message) +TEST_F(timing, simple_timing_message) { SetupTest("test", 2); auto vFed1 = GetFederateAs(0); @@ -113,7 +113,42 @@ TEST_F(timing_tests, simple_timing_test_message) // it will time out. } -TEST_F(timing_tests, test_uninteruptible_flag) +TEST_F(timing, simple_global_timing_message) +{ + extraBrokerArgs = " --globaltime "; + SetupTest("test", 2); + auto vFed1 = GetFederateAs(0); + auto vFed2 = GetFederateAs(1); + + vFed1->setProperty(HELICS_PROPERTY_TIME_PERIOD, 0.6); + vFed2->setProperty(HELICS_PROPERTY_TIME_PERIOD, 0.45); + vFed1->setFlagOption(HELICS_FLAG_IGNORE_TIME_MISMATCH_WARNINGS); + vFed2->setFlagOption(HELICS_FLAG_IGNORE_TIME_MISMATCH_WARNINGS); + auto& ept1 = vFed1->registerGlobalEndpoint("e1"); + vFed2->registerGlobalEndpoint("e2"); + vFed1->enterExecutingModeAsync(); + vFed2->enterExecutingMode(); + vFed1->enterExecutingModeComplete(); + vFed2->requestTimeAsync(3.5); + auto res = vFed1->requestTime(0.32); + // check that the request is only granted at the appropriate period + EXPECT_EQ(res, 0.6); + ept1.sendTo("test1", "e2"); + vFed1->requestTimeAsync(1.85); + res = vFed2->requestTimeComplete(); + EXPECT_EQ(res, 0.9); // the message should show up at the next available time point + vFed2->requestTimeAsync(2.0); + res = vFed2->requestTimeComplete(); + EXPECT_EQ(res, 2.25); // the message should show up at the next available time point + vFed2->requestTimeAsync(3.0); + res = vFed1->requestTimeComplete(); + EXPECT_EQ(res, 2.4); + vFed1->finalize(); + vFed2->finalize(); // this will also test finalizing while a time request is ongoing otherwise + // it will time out. +} + +TEST_F(timing, test_uninteruptible_flag) { SetupTest("test", 2); auto vFed1 = GetFederateAs(0); @@ -158,7 +193,7 @@ TEST_F(timing_tests, test_uninteruptible_flag) vFed2->finalize(); } -TEST_F(timing_tests, test_uninteruptible_flag_option) +TEST_F(timing, uninteruptible_flag_option) { SetupTest("test", 2); auto vFed1 = GetFederateAs(0); @@ -210,7 +245,7 @@ TEST_F(timing_tests, test_uninteruptible_flag_option) vFed2->finalize(); } -TEST_F(timing_tests, test_uninterruptible_flag_two_way_comm) +TEST_F(timing, uninterruptible_flag_two_way_comm) { SetupTest("test", 2); auto vFed1 = GetFederateAs(0); @@ -282,7 +317,7 @@ TEST_F(timing_tests, test_uninterruptible_flag_two_way_comm) vFed2->finalize(); } -TEST_F(timing_tests, test_uninterruptible_iterations) +TEST_F(timing, uninterruptible_iterations) { SetupTest("test", 2); auto vFed1 = GetFederateAs(0); @@ -373,7 +408,7 @@ TEST_F(timing_tests, test_uninterruptible_iterations) vFed2->finalize(); } -TEST_F(timing_tests, timing_with_input_delay) +TEST_F(timing, timing_with_input_delay) { SetupTest("test", 2); auto vFed1 = GetFederateAs(0); @@ -407,7 +442,7 @@ TEST_F(timing_tests, timing_with_input_delay) vFed2->finalize(); } -TEST_F(timing_tests, timing_with_minDelta_change) +TEST_F(timing, timing_with_minDelta_change) { SetupTest("test", 1, 1.0); auto vFed1 = GetFederateAs(0); @@ -429,7 +464,7 @@ TEST_F(timing_tests, timing_with_minDelta_change) vFed1->finalize(); } -TEST_F(timing_tests, timing_with_period_change) +TEST_F(timing, timing_with_period_change) { SetupTest("test", 1); auto vFed1 = GetFederateAs(0); @@ -451,7 +486,7 @@ TEST_F(timing_tests, timing_with_period_change) vFed1->finalize(); } -TEST_F(timing_tests, sender_finalize_timing_result) +TEST_F(timing, sender_finalize_timing_result) { SetupTest("test", 2); auto vFed1 = GetFederateAs(0); @@ -516,7 +551,7 @@ TEST_F(timing_tests, sender_finalize_timing_result) vFed2->finalize(); } -TEST_F(timing_tests, sender_finalize_timing_result2) +TEST_F(timing, sender_finalize_timing_result2) { SetupTest("test", 2); auto vFed1 = GetFederateAs(0); @@ -587,7 +622,7 @@ TEST_F(timing_tests, sender_finalize_timing_result2) } #ifdef HELICS_ENABLE_ZMQ_CORE -TEST_F(timing_tests, fast_sender_tests_ci_skip) // ci_skip +TEST_F(timing, fast_sender_tests_ci_skip) // ci_skip { SetupTest("zmq_2", 2); auto vFed1 = GetFederateAs(0); @@ -620,7 +655,7 @@ TEST_F(timing_tests, fast_sender_tests_ci_skip) // ci_skip vFed2->finalize(); } -TEST_F(timing_tests, dual_fast_sender_tests_ci_skip) // ci_skip +TEST_F(timing, dual_fast_sender_tests_ci_skip) // ci_skip { SetupTest("zmq_2", 3); auto vFed1 = GetFederateAs(0); diff --git a/tests/helics/system_tests/TimingTests2.cpp b/tests/helics/system_tests/TimingTests2.cpp index abdec8af39..919b99836b 100644 --- a/tests/helics/system_tests/TimingTests2.cpp +++ b/tests/helics/system_tests/TimingTests2.cpp @@ -16,10 +16,10 @@ SPDX-License-Identifier: BSD-3-Clause #include #include -struct timing_tests2: public FederateTestFixture, public ::testing::Test {}; +struct timing2: public FederateTestFixture, public ::testing::Test {}; /** just a check that in the simple case we do actually get the time back we requested*/ -TEST_F(timing_tests2, small_time_test) +TEST_F(timing2, small_time) { SetupTest("test", 2); auto vFed1 = GetFederateAs(0); @@ -86,10 +86,16 @@ TEST_F(timing_tests2, small_time_test) vFed2->finalize(); } +TEST_F(timing2, numbers) +{ + EXPECT_EQ(helics::cBigTime, helics::Time{cHelicsBigNumber}); + EXPECT_EQ(helics::cBigTime, helics::Time::maxVal()); +} + /** based on bug found by Manoj Kumar Cebol Sundarrajan where a very small period could cause the time to be negative */ -TEST_F(timing_tests2, small_period_test) +TEST_F(timing2, small_period) { SetupTest("test", 3); auto rx = GetFederateAs(0); @@ -166,7 +172,7 @@ TEST_F(timing_tests2, small_period_test) } // Tests out the restrictive time policy -TEST_F(timing_tests2, ring_test3) +TEST_F(timing2, ring3) { SetupTest("test_2", 3); auto vFed1 = GetFederateAs(0); @@ -248,7 +254,7 @@ TEST_F(timing_tests2, ring_test3) vFed1->finalize(); } -TEST_F(timing_tests2, wait_for_current_time_exec_extry) +TEST_F(timing2, wait_for_current_time_exec_extry) { extraBrokerArgs = "--debugging"; auto broker = AddBroker("test", 2); @@ -295,7 +301,7 @@ TEST_F(timing_tests2, wait_for_current_time_exec_extry) vFed2->finalize(); } -TEST_F(timing_tests2, wait_for_current_time_flag) +TEST_F(timing2, wait_for_current_time_flag) { SetupTest("test_2", 3); auto vFed1 = GetFederateAs(0); @@ -377,7 +383,7 @@ TEST_F(timing_tests2, wait_for_current_time_flag) vFed3->finalize(); } -TEST_F(timing_tests2, wait_for_current_time_flag2) +TEST_F(timing2, wait_for_current_time_flag2) { extraBrokerArgs = "--debugging"; auto broker = AddBroker("test", 2); @@ -444,7 +450,7 @@ TEST_F(timing_tests2, wait_for_current_time_flag2) vFed2->finalize(); } -TEST_F(timing_tests2, wait_for_current_time_flag2_error) +TEST_F(timing2, wait_for_current_time_flag2_error) { extraBrokerArgs = "--debugging --consoleloglevel=no_print"; auto broker = AddBroker("test", 2); @@ -479,7 +485,7 @@ TEST_F(timing_tests2, wait_for_current_time_flag2_error) vFed2->finalize(); } -TEST_F(timing_tests2, wait_for_current_time_flag_self) +TEST_F(timing2, wait_for_current_time_flag_self) { extraBrokerArgs = "--debugging"; auto broker = AddBroker("test", 2); @@ -549,7 +555,7 @@ TEST_F(timing_tests2, wait_for_current_time_flag_self) vFed2->finalize(); } -TEST_F(timing_tests2, wait_for_current_time_flag_endpoint_error) +TEST_F(timing2, wait_for_current_time_flag_endpoint_error) { extraBrokerArgs = "--debugging --loglevel=no_print"; auto broker = AddBroker("test", 2); @@ -581,7 +587,7 @@ TEST_F(timing_tests2, wait_for_current_time_flag_endpoint_error) broker.reset(); } -TEST_F(timing_tests2, wait_for_current_time_flag_endpoint) +TEST_F(timing2, wait_for_current_time_flag_endpoint) { extraBrokerArgs = "--debugging"; auto broker = AddBroker("test", 2); @@ -646,7 +652,7 @@ TEST_F(timing_tests2, wait_for_current_time_flag_endpoint) broker.reset(); } -TEST_F(timing_tests2, offset_timing) +TEST_F(timing2, offset_timing) { SetupTest("test_2", 2); auto vFed1 = GetFederateAs(0); @@ -685,7 +691,7 @@ TEST_F(timing_tests2, offset_timing) } // Tests out the time barrier -TEST_F(timing_tests2, time_barrier1) +TEST_F(timing2, time_barrier1) { SetupTest("test_2", 2); auto vFed1 = GetFederateAs(0); @@ -707,7 +713,7 @@ TEST_F(timing_tests2, time_barrier1) vFed2->finalize(); } -TEST_F(timing_tests2, time_barrier_update) +TEST_F(timing2, time_barrier_update) { SetupTest("test_2", 2); auto vFed1 = GetFederateAs(0); @@ -730,7 +736,7 @@ TEST_F(timing_tests2, time_barrier_update) vFed2->finalize(); } -TEST_F(timing_tests2, time_barrier_clear) +TEST_F(timing2, time_barrier_clear) { SetupTest("test_2", 2); auto vFed1 = GetFederateAs(0); @@ -778,7 +784,7 @@ TEST_F(timing_tests2, time_barrier_clear) vFed2->finalize(); } -TEST_F(timing_tests2, time_barrier_clear2) +TEST_F(timing2, time_barrier_clear2) { SetupTest("test_2", 2); auto vFed1 = GetFederateAs(0); @@ -835,7 +841,7 @@ TEST_F(timing_tests2, time_barrier_clear2) vFed2->finalize(); } -TEST_F(timing_tests2, value_to_endpoint_timing) +TEST_F(timing2, value_to_endpoint_timing) { SetupTest("test_2", 2); auto vFed1 = GetFederateAs(0); diff --git a/tests/helics/system_tests/iterationTests.cpp b/tests/helics/system_tests/iterationTests.cpp index 67851e2df7..d85fcac787 100644 --- a/tests/helics/system_tests/iterationTests.cpp +++ b/tests/helics/system_tests/iterationTests.cpp @@ -22,11 +22,11 @@ static const auto testNamer = [](const ::testing::TestParamInfo& pa return std::string(parameter.param); }; -struct iteration_tests: public FederateTestFixture, public ::testing::Test {}; +struct iteration: public FederateTestFixture, public ::testing::Test {}; /** just a check that in the simple case we do actually get the time back we requested*/ -TEST_F(iteration_tests, execution_iteration) +TEST_F(iteration, execution_iteration) { SetupTest("test", 1); auto vFed1 = GetFederateAs(0); @@ -53,7 +53,7 @@ TEST_F(iteration_tests, execution_iteration) EXPECT_EQ(val2, val); } -TEST_F(iteration_tests, execution_iteration_endpoint) +TEST_F(iteration, execution_iteration_endpoint) { SetupTest("test", 1); auto vFed1 = GetFederateAs(0); @@ -137,11 +137,9 @@ std::vector> return results; } -class iteration_tests_type: - public ::testing::TestWithParam, - public FederateTestFixture {}; +class iteration_type: public ::testing::TestWithParam, public FederateTestFixture {}; -TEST_P(iteration_tests_type, execution_iteration_round_robin_ci_skip) +TEST_P(iteration_type, execution_iteration_round_robin_ci_skip) { try { SetupTest(GetParam(), 3); @@ -175,12 +173,9 @@ TEST_P(iteration_tests_type, execution_iteration_round_robin_ci_skip) } } -INSTANTIATE_TEST_SUITE_P(iteration_tests, - iteration_tests_type, - ::testing::ValuesIn(CoreTypes_all), - testNamer); +INSTANTIATE_TEST_SUITE_P(iteration, iteration_type, ::testing::ValuesIn(CoreTypes_all), testNamer); -TEST_F(iteration_tests, execution_iteration_loop3) +TEST_F(iteration, execution_iteration_loop3) { int N = 5; SetupTest("test", N); @@ -197,7 +192,7 @@ TEST_F(iteration_tests, execution_iteration_loop3) } } -TEST_F(iteration_tests, execution_iteration_2fed) +TEST_F(iteration, execution_iteration_2fed) { SetupTest("test", 2, 1.0); auto vFed1 = GetFederateAs(0); @@ -229,7 +224,7 @@ TEST_F(iteration_tests, execution_iteration_2fed) vFed1->finalize(); } -TEST_F(iteration_tests, execution_iteration_2fed_endpoint) +TEST_F(iteration, execution_iteration_2fed_endpoint) { SetupTest("test", 2, 1.0); auto vFed1 = GetFederateAs(0); @@ -260,7 +255,7 @@ TEST_F(iteration_tests, execution_iteration_2fed_endpoint) vFed1->finalize(); } -TEST_F(iteration_tests, execution_iteration_2fed_targeted_endpoint) +TEST_F(iteration, execution_iteration_2fed_targeted_endpoint) { SetupTest("test", 2, 1.0); auto vFed1 = GetFederateAs(0); @@ -294,7 +289,7 @@ TEST_F(iteration_tests, execution_iteration_2fed_targeted_endpoint) } /** just a check that in the simple case we do actually get the time back we requested*/ -TEST_F(iteration_tests, time_iteration_test) +TEST_F(iteration, time_iteration) { SetupTest("test", 1); auto vFed1 = GetFederateAs(0); @@ -323,7 +318,7 @@ TEST_F(iteration_tests, time_iteration_test) EXPECT_EQ(val2, val); } -TEST_F(iteration_tests, time_iteration_test_2fed) +TEST_F(iteration, time_iteration_2fed) { SetupTest("test", 2, 1.0); auto vFed1 = GetFederateAs(0); @@ -359,7 +354,7 @@ TEST_F(iteration_tests, time_iteration_test_2fed) EXPECT_EQ(val2, val); } -TEST_F(iteration_tests, time_iteration_test_message) +TEST_F(iteration, time_iteration_message) { SetupTest("test", 1); auto mFed1 = GetFederateAs(0); @@ -385,7 +380,7 @@ TEST_F(iteration_tests, time_iteration_test_message) EXPECT_FALSE(mFed1->hasMessage()); } -TEST_F(iteration_tests, time_iteration_test_2fed_message) +TEST_F(iteration, time_iteration_2fed_message) { SetupTest("test", 2, 1.0); auto mFed1 = GetFederateAs(0); @@ -420,7 +415,7 @@ TEST_F(iteration_tests, time_iteration_test_2fed_message) mFed1->requestTimeComplete(); } -TEST_F(iteration_tests, test2fed_withSubPub) +TEST_F(iteration, two_fed_withSubPub) { SetupTest("test", 2, 1.0); auto vFed1 = GetFederateAs(0); @@ -463,7 +458,7 @@ TEST_F(iteration_tests, test2fed_withSubPub) EXPECT_EQ(val2, val); } -TEST_F(iteration_tests, iteration_counter) +TEST_F(iteration, iteration_counter) { SetupTest("test", 2, 1.0); auto vFed1 = GetFederateAs(0); @@ -558,11 +553,9 @@ TEST_F(iteration_tests, iteration_counter) deadlock.join(); } -TEST_F(iteration_tests, wait_for_current_time_iterative) +TEST_F(iteration, wait_for_current_time_iterative) { - extraBrokerArgs = "--debugging"; auto broker = AddBroker("test", 2); - extraCoreArgs = "--debugging"; AddFederates("test", 1, broker, 1.0); AddFederates("test", 1, broker, 1.0); @@ -654,11 +647,9 @@ TEST_F(iteration_tests, wait_for_current_time_iterative) vFed1->finalize(); } -TEST_F(iteration_tests, wait_for_current_time_iterative_enter_exec) +TEST_F(iteration, wait_for_current_time_iterative_enter_exec) { - extraBrokerArgs = "--debugging"; auto broker = AddBroker("test", 2); - extraCoreArgs = "--debugging"; AddFederates("test", 1, broker, 1.0); AddFederates("test", 1, broker, 1.0); @@ -713,11 +704,9 @@ TEST_F(iteration_tests, wait_for_current_time_iterative_enter_exec) vFed1->finalize(); } -TEST_F(iteration_tests, wait_for_current_time_iterative_enter_exec_endpoint) +TEST_F(iteration, wait_for_current_time_iterative_enter_exec_endpoint) { - extraBrokerArgs = "--debugging"; auto broker = AddBroker("test", 2); - extraCoreArgs = "--debugging"; AddFederates("test", 1, broker, 1.0); AddFederates("test", 1, broker, 1.0);