diff --git a/.gitignore b/.gitignore
index 92eda074f..67ceeaffc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@
compile_commands.json
*.plan
results
+.oppfeaturestate
### coverage files
quisp/coverage*
diff --git a/.gitmodules b/.gitmodules
index 9fa54761c..0e09da8ac 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -10,3 +10,6 @@
[submodule "quispr"]
path = quispr
url = https://github.com/sfc-aqua/quispr
+[submodule "pybind11"]
+ path = pybind11
+ url = https://github.com/pybind/pybind11
diff --git a/.nedexclusions b/.nedexclusions
new file mode 100644
index 000000000..e69de29bb
diff --git a/.oppfeatures b/.oppfeatures
new file mode 100644
index 000000000..c02c2b1a1
--- /dev/null
+++ b/.oppfeatures
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/Makefile b/Makefile
index 56339fe8e..01d426765 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,6 @@
-QUISP_MAKEFILE = "./quisp/Makefile"
+QUISP_SRC_DIR = "./quisp"
+QUISP_MAKEFILE = "$(QUISP_SRC_DIR)/Makefile"
+QUISP_FEATURE = "$(QUISP_SRC_DIR)/feature_defines.h"
.PHONY: all tidy format ci makefile-exe makefile-lib checkmakefile googletest clean test coverage coverage-report help quispr
@@ -47,12 +49,15 @@ eigen/CMakeLists.txt:
eigen: eigen/CMakeLists.txt
-makefile-exe: eigen
+makefile-exe: eigen $(QUISP_FEATURE)
cd quisp && opp_makemake -f --deep -O out -i ./makefrag
-makefile-lib: eigen
+makefile-lib: eigen $(QUISP_FEATURE)
cd quisp && opp_makemake -f --deep -O out -i ./makefrag -M debug --make-so
+$(QUISP_FEATURE): $(wildcard .oppfeaturestate) .oppfeatures
+ opp_featuretool defines > $(QUISP_SRC_DIR)/feature_defines.h
+
clean:
@if [ -f "$(QUISP_MAKEFILE)" ]; then \
$(MAKE) -C quisp clean; \
diff --git a/pybind11 b/pybind11
new file mode 160000
index 000000000..ffa346860
--- /dev/null
+++ b/pybind11
@@ -0,0 +1 @@
+Subproject commit ffa346860b306c9bbfb341aed9c14c067751feb8
diff --git a/quisp/.gitignore b/quisp/.gitignore
index 2211d28a0..494aec9bf 100644
--- a/quisp/.gitignore
+++ b/quisp/.gitignore
@@ -10,6 +10,7 @@ out/
.oppfeaturestate
quisp.dSYM/
Test*
+feature_defines.h
PhotonicQubit_m.cc
PhotonicQubit_m.h
diff --git a/quisp/.nedfolders b/quisp/.nedfolders
index 2c61d0d51..811f76d58 100644
--- a/quisp/.nedfolders
+++ b/quisp/.nedfolders
@@ -1,4 +1,4 @@
.
-./channels
-./modules
-./networks
+channels
+modules
+networks
diff --git a/quisp/app.py b/quisp/app.py
new file mode 100644
index 000000000..c11839a44
--- /dev/null
+++ b/quisp/app.py
@@ -0,0 +1,16 @@
+import opp
+import application
+
+def init_app_module(app):
+ num_resources = app.par("distant_measure_count").int()
+ pattern = app.par("TrafficPattern").int()
+ initiator_addr = app.par("LoneInitiatorAddress").int()
+
+ if pattern == 1 and app.my_address == initiator_addr:
+ dest_addr = app.get_one_random_endnode_addr()
+ pk = app.create_conn_setup_req(dest_addr,num_resources)
+ app.schedule_at(opp.sim_time(), pk)
+ if pattern == 2:
+ dest_addr = app.get_one_random_endnode_addr()
+ pk = app.create_conn_setup_req(dest_addr,num_resources)
+ app.schedule_at(opp.sim_time() + 0.00001 * app.my_address, pk)
diff --git a/quisp/makefrag b/quisp/makefrag
index c666e05cc..27ef4e6de 100644
--- a/quisp/makefrag
+++ b/quisp/makefrag
@@ -25,7 +25,8 @@ else
INCLUDE_PATH+=-I. $(shell pkg-config eigen3 --cflags)
endif
-INCLUDE_PATH+=-I.
+INCLUDE_PATH+=-I$(PROJ_ROOT)/pybind11/include
+INCLUDE_PATH+=-I$(OMNETPP_TOOLS_DIR)/lib/Python.framework/Headers
ifneq (,$(ENABLE_COVERAGE))
CXXFLAGS+=-ftest-coverage -fprofile-instr-generate -fcoverage-mapping
LDFLAGS+=-ftest-coverage -fprofile-instr-generate -fcoverage-mapping
@@ -33,6 +34,7 @@ endif
# include path for json
INCLUDE_PATH+=-I$(PROJ_ROOT)/json/include/
+LDFLAGS+=-L$(OMNETPP_TOOLS_DIR)/lib -lpython3.8
default: eigen all
diff --git a/quisp/modules/Application/Application.cc b/quisp/modules/Application/Application.cc
index 9ab2462fe..24126dda1 100644
--- a/quisp/modules/Application/Application.cc
+++ b/quisp/modules/Application/Application.cc
@@ -5,15 +5,32 @@
* \brief Application
*/
#include "Application.h"
+#include
+#include
+#include
+#include
+#include
#include
+#include "messages/base_messages_m.h"
+#include "messages/connection_setup_messages_m.h"
+#include "modules/Application/IApplication.h"
#include "utils/ComponentProvider.h"
using namespace omnetpp;
using namespace quisp::messages;
+namespace py = pybind11;
namespace quisp {
namespace modules {
+PYBIND11_EMBEDDED_MODULE(application, m) {
+ py::class_, cSimpleModule> QuispApplication(m, "Application");
+ QuispApplication.def("create_conn_setup_req", &Application::createConnectionSetupRequest);
+ QuispApplication.def_readonly("my_address", &Application::my_address);
+ QuispApplication.def("get_one_random_endnode_addr", &Application::getOneRandomEndNodeAddress);
+ py::class_, omnetpp::cMessage>(m, "ConnectionSetupRequest");
+}
+
Application::Application() : provider(utils::ComponentProvider{this}) {}
/**
@@ -29,19 +46,26 @@ void Application::initialize() {
return;
}
+ WATCH_VECTOR(other_end_node_addresses);
+ storeEndNodeAddresses();
+
+#ifdef ENABLE_PYTHON
+ const char *app_py_path = par("app_py_path").stringValue();
+ compile_code(app_py_path);
+ auto obj = py::cast(this);
+ execute_python("init_app_module", obj);
+
+#else
+
my_address = provider.getQNode()->par("address");
is_e2e_connection = par("EndToEndConnection");
num_measure = par("distant_measure_count");
-
- WATCH_VECTOR(other_end_node_addresses);
- storeEndNodeAddresses();
+ traffic_pattern = par("TrafficPattern");
if (!is_e2e_connection) {
return;
}
- traffic_pattern = par("TrafficPattern");
-
if (traffic_pattern == 0) {
EV_INFO << "EndToEndConnection is set true. but no traffic pattern specified; proceeding with no traffic\n";
return;
@@ -72,6 +96,7 @@ void Application::initialize() {
}
error("Invalid TrafficPattern specified.");
+#endif
}
/**
diff --git a/quisp/modules/Application/Application.h b/quisp/modules/Application/Application.h
index 3c2e539cf..f44dc40b2 100644
--- a/quisp/modules/Application/Application.h
+++ b/quisp/modules/Application/Application.h
@@ -8,6 +8,7 @@
#ifndef MODULES_APPLICATION_H_
#define MODULES_APPLICATION_H_
+#include
#include "IApplication.h"
#include "utils/ComponentProvider.h"
@@ -20,12 +21,11 @@ namespace modules {
*
* \brief Application
*/
-class Application : public IApplication {
+class Application : public IApplication, python_embeddable::PythonEmbeddable {
public:
Application();
~Application() {}
- protected:
int my_address;
std::vector other_end_node_addresses;
diff --git a/quisp/modules/Application/Application.ned b/quisp/modules/Application/Application.ned
index a1cb4912e..c18f9d796 100644
--- a/quisp/modules/Application/Application.ned
+++ b/quisp/modules/Application/Application.ned
@@ -8,6 +8,7 @@ simple Application
{
parameters:
@display("i=block/app");
+ string app_py_path = default("app");
int address;
volatile double sendIaTime @unit(s) = default(exponential(1s)); // time between generating packets
string Other_endnodes_table = "";
diff --git a/quisp/modules/Application/Application_test.cc b/quisp/modules/Application/Application_test.cc
index 728f2fd3d..937e4c1a7 100644
--- a/quisp/modules/Application/Application_test.cc
+++ b/quisp/modules/Application/Application_test.cc
@@ -3,6 +3,8 @@
#include
#include
#include
+#include
+#include
#include
namespace {
@@ -35,6 +37,7 @@ class AppTestTarget : public quisp::modules::Application {
explicit AppTestTarget(TestQNode *parent_qnode) : Application(), toRouterGate(new TestGate(this, "toRouter")) {
this->provider.setStrategy(std::make_unique(parent_qnode));
setComponentType(new TestModuleType("test qnode"));
+ setParStr(this, "app_py_path", "app");
}
virtual ~AppTestTarget() { EVCB.gateDeleted(toRouterGate); }
std::vector getOtherEndNodeAdresses() { return this->other_end_node_addresses; }
@@ -44,6 +47,8 @@ class AppTestTarget : public quisp::modules::Application {
};
TEST(AppTest, InitSimple) {
+ pybind11::scoped_interpreter guard{};
+
auto *sim = prepareSimulation();
auto *mock_qnode = new TestQNode{123};
auto *app = new AppTestTarget{mock_qnode};
@@ -61,6 +66,7 @@ TEST(AppTest, InitSimple) {
}
TEST(AppTest, Init_OneConnection_NoSender) {
+ pybind11::scoped_interpreter guard{};
auto *sim = prepareSimulation();
auto *mock_qnode = new TestQNode{123};
auto *app = new AppTestTarget{mock_qnode};
@@ -79,6 +85,7 @@ TEST(AppTest, Init_OneConnection_NoSender) {
}
TEST(AppTest, Init_OneConnection_Sender) {
+ pybind11::scoped_interpreter guard{};
auto *sim = prepareSimulation();
auto *mock_qnode = new TestQNode{123};
@@ -112,6 +119,7 @@ TEST(AppTest, Init_OneConnection_Sender) {
}
TEST(AppTest, Init_OneConnection_Sender_TrafficPattern2) {
+ pybind11::scoped_interpreter guard{};
auto *sim = prepareSimulation();
auto *mock_qnode = new TestQNode{123};
auto *mock_qnode2 = new TestQNode{456};
diff --git a/quisp/modules/Backend/Backend.cc b/quisp/modules/Backend/Backend.cc
index a517e35f9..5c0c4dc5f 100644
--- a/quisp/modules/Backend/Backend.cc
+++ b/quisp/modules/Backend/Backend.cc
@@ -1,7 +1,14 @@
#include "Backend.h"
namespace quisp::modules::backend {
-BackendContainer::BackendContainer() {}
+
+BackendContainer::BackendContainer()
+#ifdef ENABLE_PYTHON
+ : interpreter(pybind11::scoped_interpreter{})
+#endif
+{
+}
+
BackendContainer::~BackendContainer() {}
} // namespace quisp::modules::backend
diff --git a/quisp/modules/Backend/Backend.h b/quisp/modules/Backend/Backend.h
index 589c60acd..b1fec502c 100644
--- a/quisp/modules/Backend/Backend.h
+++ b/quisp/modules/Backend/Backend.h
@@ -1,8 +1,10 @@
#pragma once
#include
#include
+#include
#include
#include "RNG.h"
+#include "feature_defines.h"
namespace quisp::modules::backend {
using quisp::modules::common::ErrorTrackingBackend;
@@ -15,6 +17,10 @@ class BackendContainer : public omnetpp::cSimpleModule {
~BackendContainer();
void initialize() override {
+#ifdef ENABLE_PYTHON
+ python_enabled = true;
+#endif
+ WATCH(python_enabled);
auto backend_type = std::string(par("backendType").stringValue());
if (backend_type == "ErrorTrackingBackend") {
backend = std::make_unique(std::make_unique(this));
@@ -33,7 +39,12 @@ class BackendContainer : public omnetpp::cSimpleModule {
}
protected:
- std::unique_ptr backend;
+ void configureErrorTrackingBackend();
+ std::unique_ptr backend = nullptr;
+ bool python_enabled = false;
+#ifdef ENABLE_PYTHON
+ pybind11::scoped_interpreter interpreter;
+#endif
};
Define_Module(BackendContainer);
diff --git a/quisp/modules/Backend/Backend_test.cc b/quisp/modules/Backend/Backend_test.cc
index c8e2c517c..00dc16511 100644
--- a/quisp/modules/Backend/Backend_test.cc
+++ b/quisp/modules/Backend/Backend_test.cc
@@ -1,4 +1,5 @@
#include "Backend.h"
+#include
#include
#include
#include "modules/common_types.h"
@@ -29,6 +30,7 @@ TEST(BackendContainer, callInitialize) {
}
TEST(BackendContainer, callInitializeWithInvalidBackend) {
+ Py_Finalize();
auto *sim = utils::prepareSimulation();
BackendContainer *backend = new BackendContainer();
sim->registerComponent(backend);
@@ -37,7 +39,8 @@ TEST(BackendContainer, callInitializeWithInvalidBackend) {
EXPECT_THROW(backend->callInitialize(), omnetpp::cRuntimeError);
}
-TEST(BackendContainer, getQuantumBackend) {
+TEST(BackendConpainer, getQuantumBackend) {
+ Py_Finalize();
auto *sim = utils::prepareSimulation();
BackendContainer *backend = new BackendContainer();
sim->registerComponent(backend);
diff --git a/quisp/modules/Common/PythonEmbeddedable.cc b/quisp/modules/Common/PythonEmbeddedable.cc
new file mode 100644
index 000000000..8d36c8a9b
--- /dev/null
+++ b/quisp/modules/Common/PythonEmbeddedable.cc
@@ -0,0 +1,50 @@
+#include "PythonEmbeddedable.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "feature_defines.h"
+
+namespace py = pybind11;
+namespace opp = omnetpp;
+namespace fs = std::filesystem;
+namespace quisp::modules::python_embeddable {
+
+#ifdef ENABLE_PYTHON
+PYBIND11_EMBEDDED_MODULE(opp, m) {
+ py::class_> OppPar(m, "Par");
+ OppPar.def("str", &omnetpp::cPar::str);
+ OppPar.def("int", &omnetpp::cPar::intValue);
+
+ py::class_> OppSimpleModule(m, "SimpleModule");
+ OppSimpleModule.def("par", py::overload_cast(&opp::cSimpleModule::par), py::return_value_policy::reference);
+ OppSimpleModule.def("schedule_at", &opp::cSimpleModule::scheduleAt);
+ m.def("sim_time", &opp::simTime, py::return_value_policy::reference);
+
+ py::class_ OppSimTime(m, "SimTime");
+ OppSimTime.def(py::self + float());
+ OppSimTime.def(py::self += float());
+ OppSimTime.def(float() + py::self);
+
+ py::class_> OppCMessage(m, "Message");
+}
+#endif
+
+void PythonEmbeddable::compile_code(const char* file_name) {
+ auto* env = omnetpp::getEnvir();
+ auto* config = env->getConfigEx();
+ fs::path exe_path(env->getArgVector()[0]);
+ fs::path conf_path(env->resolveResourcePath(config->getFileName()));
+ py::module sys = py::module::import("sys");
+ py::cast(sys.attr("path")).append(fs::absolute(exe_path.parent_path()).string());
+ py::cast(sys.attr("path")).append(fs::absolute(conf_path.parent_path()).string());
+ py::print(sys.attr("path"));
+ module = pybind11::module_::import(file_name);
+}
+
+void PythonEmbeddable::execute_python(const char* func_name, py::object& arg1) { py::object ret = module.attr(func_name)(arg1); }
+
+} // namespace quisp::modules::python_embeddable
diff --git a/quisp/modules/Common/PythonEmbeddedable.h b/quisp/modules/Common/PythonEmbeddedable.h
new file mode 100644
index 000000000..74251dc8f
--- /dev/null
+++ b/quisp/modules/Common/PythonEmbeddedable.h
@@ -0,0 +1,16 @@
+#pragma once
+#include
+
+namespace quisp::modules::python_embeddable {
+namespace py = pybind11;
+
+class PythonEmbeddable {
+ public:
+ PythonEmbeddable() {}
+ ~PythonEmbeddable() {}
+ void compile_code(const char* file_name);
+ void execute_python(const char* func_name, py::object& arg1);
+
+ pybind11::module_ module;
+};
+} // namespace quisp::modules::python_embeddable
diff --git a/quisp/test_utils/Configuration.h b/quisp/test_utils/Configuration.h
index f478c44a8..aa7cd72fa 100644
--- a/quisp/test_utils/Configuration.h
+++ b/quisp/test_utils/Configuration.h
@@ -2,18 +2,19 @@
#include
#include "KeyValue.h"
+#include "omnetpp/cconfiguration.h"
namespace quisp_test {
namespace configuration {
using key_value::TestKeyValue;
-class Configuration : public omnetpp::cConfiguration {
+class Configuration : public omnetpp::cConfigurationEx {
private:
std::vector kvs;
public:
- Configuration() {
+ Configuration() : omnetpp::cConfigurationEx() {
kvs = std::vector();
auto kv = TestKeyValue{};
kvs.push_back(kv);
@@ -23,7 +24,29 @@ class Configuration : public omnetpp::cConfiguration {
virtual const char *getPerObjectConfigValue(const char *objectFullPath, const char *keySuffix) const override { return nullptr; };
virtual const KeyValue &getPerObjectConfigEntry(const char *objectFullPath, const char *keySuffix) const override { return kvs.at(0); };
virtual const char *substituteVariables(const char *value) const override { return nullptr; };
+ virtual void initializeFrom(cConfiguration *bootConfig) override{};
+ virtual const char *getFileName() const override { return "test-file-name"; };
+ virtual void validate(const char *ignorableConfigKeys = nullptr) const override{};
+ virtual std::vector getConfigNames() override { return {}; };
+ virtual void activateConfig(const char *configName, int runNumber = 0) override{};
+ virtual std::string getConfigDescription(const char *configName) const override {}
+ virtual std::vector getBaseConfigs(const char *configName) const override {}
+ virtual std::vector getConfigChain(const char *configName) const override {}
+ virtual int getNumRunsInConfig(const char *configName) const override {}
+ virtual std::vector unrollConfig(const char *configName) const override {}
+ virtual const char *getActiveConfigName() const override {}
+ virtual int getActiveRunNumber() const override {}
+ virtual const char *getVariable(const char *varname) const override {}
+ virtual std::vector getIterationVariableNames() const override {}
+ virtual std::vector getPredefinedVariableNames() const override {}
+ virtual const char *getVariableDescription(const char *varname) const override {}
+ virtual void dump() const override {}
+ virtual std::vector getMatchingConfigKeys(const char *pattern) const override {}
+ virtual const char *getParameterValue(const char *moduleFullPath, const char *paramName, bool hasDefaultValue) const override {}
+ virtual const KeyValue &getParameterEntry(const char *moduleFullPath, const char *paramName, bool hasDefaultValue) const override {}
+ virtual std::vector getKeyValuePairs(int flags = FILT_ALL) const override {}
+ virtual std::vector getMatchingPerObjectConfigKeySuffixes(const char *objectFullPath, const char *keySuffixPattern) const override {}
};
} // namespace configuration
-} // namespace quisp_test
\ No newline at end of file
+} // namespace quisp_test
diff --git a/quisp/test_utils/StaticEnv.cc b/quisp/test_utils/StaticEnv.cc
index 67632da6e..2c5289cf1 100644
--- a/quisp/test_utils/StaticEnv.cc
+++ b/quisp/test_utils/StaticEnv.cc
@@ -11,6 +11,7 @@ using configuration::Configuration;
StaticEnv::StaticEnv() {}
cConfiguration *StaticEnv::getConfig() { return new Configuration(); }
+cConfigurationEx *StaticEnv::getConfigEx() { return new Configuration(); }
std::string StaticEnv::gets(const char *prompt, const char *defaultreply) {
unsupported();
return "";
diff --git a/quisp/test_utils/StaticEnv.h b/quisp/test_utils/StaticEnv.h
index d76d94db6..8cce36090 100644
--- a/quisp/test_utils/StaticEnv.h
+++ b/quisp/test_utils/StaticEnv.h
@@ -70,6 +70,7 @@ class StaticEnv : public omnetpp::cEnvir {
void flushXMLParsedContentCache() override {}
unsigned getExtraStackForEnvir() const override { return 0; }
cConfiguration *getConfig() override;
+ cConfigurationEx *getConfigEx() override;
std::string resolveResourcePath(const char *fileName, cComponentType *context) override { return ""; }
bool isGUI() const override { return false; }
bool isExpressMode() const override { return false; }