Skip to content

Commit

Permalink
added helics
Browse files Browse the repository at this point in the history
  • Loading branch information
root committed Aug 14, 2023
1 parent f4c3aa4 commit a809c00
Show file tree
Hide file tree
Showing 3,585 changed files with 1,315,648 additions and 4 deletions.
The diff you're trying to view is too large. We only load the first 3000 changed files.
1 change: 1 addition & 0 deletions src/bennu/executables/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ add_subdirectory(bennu-field-device)
add_subdirectory(daemon)
add_subdirectory(bennu-brash)
add_subdirectory(bennu-simulink-provider)
add_subdirectory(bennu-simulink-provider-helics)
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
set(HELICS_INSTALL OFF)
set(HELICS_BUILD_APP_LIBRARY OFF)
set(HELICS_BUILD_APP_EXECUTABLES OFF)
set(HELICS_BUILD_CXX_SHARED_LIB ON)
set(HELICS_USE_SYSTEM_ZEROMQ_ONLY ON)
set(HELICS_USE_ZMQ_STATIC_LIBRARY ON)
add_subdirectory(helics)
add_subdirectory(cparse)

include_directories(
${bennu_INCLUDES}
)

link_directories(
${Boost_LIBRARY_DIRS}
)

add_definitions(-DBOOST_ALL_NO_LIB -DBOOST_ALL_DYN_LINK)

list(APPEND bennu-simulink-provider-helics_SRC
main.cpp
HelicsHelper.hpp
Logic.hpp
)

add_executable(bennu-simulink-provider-helics
${bennu-simulink-provider-helics_SRC}
$<TARGET_OBJECTS:helics-cparse-obj>
)

target_link_libraries(bennu-simulink-provider-helics
HELICS::helics-shared
)

install(TARGETS bennu-simulink-provider-helics
RUNTIME DESTINATION bin
)
302 changes: 302 additions & 0 deletions src/bennu/executables/bennu-simulink-provider-helics/HelicsHelper.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
#ifndef BENNU_EXECUTABLES_HELICS_HELPER_HPP
#define BENNU_EXECUTABLES_HELICS_HELPER_HPP

#include <csignal> // signal handler
#include <fstream>
#include <map>
#include <string>
#include <vector>

#include <helics/helics.hpp>
#include "Logic.hpp"

#include <deps/json.hpp>

namespace bennu {
namespace executables {

std::sig_atomic_t exitRequested{0};

void exit_handler(int /*signal*/)
{
exitRequested = 1;
}

class HelicsFederate
{
public:
HelicsFederate(std::string& config)
{
std::signal(SIGINT, exit_handler); // ctrl + c

// Initialize logic parser
logic::initParser();

// Default max simulation time is 1 week (in seconds)
auto hours = 24*7; // one week
auto total_interval = int(60 * 60 * hours);

// Get federate config values at runtime
std::ifstream input(config);
auto cfg = nlohmann::json::parse(input);
std::cout << "Parsed the json file: " << config << std::endl;
auto endTime = cfg.value("end_time", "");
mEndTime = endTime == "" ?
helics::loadTimeFromString(std::to_string(total_interval)) :
helics::loadTimeFromString(endTime);
auto reqTime = cfg.value("request_time", "");
mRequestTime = helics::loadTimeFromString("1");
if (reqTime != "")
{
mRequestTime = reqTime == "max" ?
helics::Time::maxVal() :
helics::loadTimeFromString(reqTime);
}

// ------------- Registering federate from json -------------
pFed = std::make_unique<helics::CombinationFederate>(config);
auto name = pFed->getName();
std::cout << "Created Federate: " << name << std::endl;
mEndCount = pFed->getEndpointCount();
std::cout << "\tNumber of endpoints: " << std::to_string(mEndCount) << std::endl;
mSubCount = pFed->getInputCount();
std::cout << "\tNumber of subscriptions: " << std::to_string(mSubCount) << std::endl;
mPubCount = pFed->getPublicationCount();
std::cout << "\tNumber of publications: " << std::to_string(mPubCount) << std::endl;

// Diagnostics to confirm JSON config correctly added the required
// endpoints, publications, and subscriptions
for (int i=0; i<mEndCount; i++)
{
mEndId[i] = pFed->getEndpoint(i);
auto endName = mEndId[i].getName();
auto endType = mEndId[i].getType();
std::cout << "\tRegistered endpoint ---> " + endName << std::endl;
mTypes[endName] = endType;
}
for (int i=0; i<mSubCount; i++)
{
mSubId[i] = pFed->getInput(i);
auto subName = mSubId[i].getTarget();
auto subType = mSubId[i].getType();
auto subInfo = mSubId[i].getInfo(); // stores logic for interdependencies
std::cout << "\tRegistered subscription ---> " + subName << std::endl;
auto found = subName.find('/');
if (found != std::string::npos)
{
subName = subName.substr(found+1, subName.length());
}
mTags.insert(subName);
mTypes[subName] = subType;
if (subInfo != "")
{
std::cout << "\t\t********** LOGIC **********" << std::endl;
mLogicVars[subName] = subType == "bool" ? "false" : "0";
auto exps = logic::splitStr(subInfo, ";");
for (auto& exp : exps)
{
logic::trim(exp);
auto exprParts = logic::splitExpression(exp, "=");
std::string tag = exprParts[0];
std::string logic = exprParts[1];
logic::trim(tag);
logic::trim(logic);
mLogic[tag] = logic;
std::cout << "\t\t" << exp << std::endl;
}
}
}
for (int i=0; i<mPubCount; i++)
{
mPubId[i] = pFed->getPublication(i);
auto pubName = mPubId[i].getName();
auto pubType = mPubId[i].getType();
std::cout << "\tRegistered publication ---> " + pubName << std::endl;
auto found = pubName.find('/');
if (found != std::string::npos)
{
pubName = pubName.substr(found+1, pubName.length());
}
mTags.insert(pubName);
mTypes[pubName] = pubType;
}
}

void run()
{
// ----------------- Entering Execution Mode -----------------
pFed->enterInitializingMode();
pFed->enterExecutingMode();
std::cout << "Entered HELICS execution mode" << std::endl;

// Blocking call for a time request at simulation time 0
auto time = helics::timeZero;
std::cout << "Requesting initial time " << time << std::endl;
auto grantedTime = pFed->requestTime(time);
std::cout << "Granted time " << std::to_string(grantedTime) << std::endl;

// Publish initial values to helics
for (int i=0; i<mPubCount; i++)
{
auto pubName = mPubId[i].getName();
auto found = pubName.find('/');
if (found != std::string::npos)
{
pubName = pubName.substr(found+1, pubName.length());
}
auto value = tag(pubName);
mPubId[i].publish(value);
std::cout << "\tPublishing " << pubName <<
":" << value << " at time " <<
std::to_string(grantedTime) << std::endl;
}

// ----------------- Main co-simulation loop -----------------
// As long as granted time is in the time range to be simulated...
while (grantedTime < mEndTime)
{
if (exitRequested)
{
std::cout << "SIGINT or CTRL-C detected. Exiting gracefully" << std::endl;
break;
}

printState();

// Time request for the next physical interval to be simulated
auto requestedTime = grantedTime + mRequestTime;
requestedTime = mRequestTime == helics::Time::maxVal() ? mRequestTime : requestedTime;
std::cout << "Requesting time " << std::to_string(requestedTime) << std::endl;
grantedTime = pFed->requestTime(requestedTime);
std::cout << "Granted time " << std::to_string(grantedTime) << std::endl;

// Process subscriptions
for (int i=0; i<mSubCount; i++)
{
if (mSubId[i].isUpdated())
{
auto subName = mSubId[i].getTarget();
auto found = subName.find('/');
if (found != std::string::npos)
{
subName = subName.substr(found+1, subName.length());
}
auto value = mSubId[i].getValue<std::string>();
tag(subName, value);
std::cout << "\tUpdated " << subName <<
":" << value << " at time " <<
std::to_string(grantedTime) << std::endl;
}
}

// Process logic
std::vector<std::string> tagsToVector{mTags.begin(), mTags.end()};
auto tags = logic::sortByLargest(tagsToVector);
TokenMap vars;
for (const auto& [tag_name, logic] : mLogic)
{
auto data = logic;
for (auto const& t : tags)
{
logic::replaceAll(data, t, tag(t));
}
try {
auto result = calculator::calculate(data.data(), &vars).str();
logic::lower(result);
if (result != tag(tag_name))
{
std::cout << "\tLOGIC: " + tag_name << " = " << logic << " ----> " << result << std::endl;
tag(tag_name, result);
}
} catch (std::exception& e) {
std::cout << "ERROR: [ " << logic << " ] Failed to parse logic: " << e.what() << std::endl;
continue;
}
}

// Process endpoints
for (int i=0; i<mEndCount; i++)
{
while (mEndId[i].hasMessage())
{
auto endName = mEndId[i].getName();
auto found = endName.find('/');
if (found != std::string::npos)
{
endName = endName.substr(found+1, endName.length());
}
auto msg = mEndId[i].getMessage();
auto value = msg->to_string();
std::cout << "\tReceived message from endpoint " <<
msg->source << " at time " <<
std::to_string(grantedTime) << " with data " <<
value << std::endl;
tag(endName, value);
std::cout << "\tUpdated " << endName <<
":" << value << " at time " <<
std::to_string(grantedTime) << std::endl;
}
}

// Process publications
for (int i=0; i<mPubCount; i++)
{
auto pubName = mPubId[i].getName();
auto found = pubName.find('/');
if (found != std::string::npos)
{
pubName = pubName.substr(found+1, pubName.length());
}
auto value = tag(pubName);
mPubId[i].publish(value);
std::cout << "\tPublishing " << pubName <<
":" << value << " at time " <<
std::to_string(grantedTime) << std::endl;
}
}

// Clean up HELICS
pFed->finalize();
}

void printState()
{
std::cout << "=================== DATA ===================" << std::endl;
for (auto& t : mTags)
{
std::cout << std::left << std::setw(30) << t << " --- "
<< tag(t) << std::endl;
}
std::cout << "============================================" << std::endl;
}

std::string getType(const std::string& tag)
{
return mTypes[tag];
}

virtual void tag(const std::string& tag, const std::string& strval) = 0;

virtual std::string tag(const std::string& tag) = 0;

std::unique_ptr<helics::CombinationFederate> pFed;

std::map<std::string, std::string> mLogic;

TokenMap mLogicVars;

private:
std::unordered_set<std::string> mTags;
std::map<std::string, std::string> mTypes;
int mEndCount, mSubCount, mPubCount;
helics::Time mEndTime, mRequestTime;
std::map<int, helics::Endpoint> mEndId;
std::map<int, helics::Input> mSubId;
std::map<int, helics::Publication> mPubId;

};

} // namespace field_device
} // namespace bennu

#endif // BENNU_EXECUTABLES_HELICS_HELPER_HPP
Loading

0 comments on commit a809c00

Please sign in to comment.