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/.nedexclusions b/.nedexclusions new file mode 100644 index 000000000..e69de29bb diff --git a/.oppfeatures b/.oppfeatures new file mode 100644 index 000000000..5644c45d3 --- /dev/null +++ b/.oppfeatures @@ -0,0 +1,12 @@ + + + + diff --git a/Makefile b/Makefile index 56339fe8e..7ba77fd58 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/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/modules/Application/Application.cc b/quisp/modules/Application/Application.cc index b789eab48..24126dda1 100644 --- a/quisp/modules/Application/Application.cc +++ b/quisp/modules/Application/Application.cc @@ -6,15 +6,31 @@ */ #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}) {} /** @@ -30,22 +46,26 @@ void Application::initialize() { return; } - my_address = provider.getQNode()->par("address"); - is_e2e_connection = par("EndToEndConnection"); - num_measure = par("distant_measure_count"); + 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); - WATCH_VECTOR(other_end_node_addresses); - storeEndNodeAddresses(); +#else + + my_address = provider.getQNode()->par("address"); + is_e2e_connection = par("EndToEndConnection"); + num_measure = par("distant_measure_count"); + 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; @@ -76,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 280c171a4..f44dc40b2 100644 --- a/quisp/modules/Application/Application.h +++ b/quisp/modules/Application/Application.h @@ -26,7 +26,6 @@ class Application : public IApplication, python_embeddable::PythonEmbeddable { 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 3113a67a2..937e4c1a7 100644 --- a/quisp/modules/Application/Application_test.cc +++ b/quisp/modules/Application/Application_test.cc @@ -37,7 +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.py"); + setParStr(this, "app_py_path", "app"); } virtual ~AppTestTarget() { EVCB.gateDeleted(toRouterGate); } std::vector getOtherEndNodeAdresses() { return this->other_end_node_addresses; } diff --git a/quisp/modules/Backend/Backend.cc b/quisp/modules/Backend/Backend.cc index 3311cd435..5c0c4dc5f 100644 --- a/quisp/modules/Backend/Backend.cc +++ b/quisp/modules/Backend/Backend.cc @@ -2,7 +2,12 @@ namespace quisp::modules::backend { -BackendContainer::BackendContainer() : interpreter(pybind11::scoped_interpreter{}) {} +BackendContainer::BackendContainer() +#ifdef ENABLE_PYTHON + : interpreter(pybind11::scoped_interpreter{}) +#endif +{ +} BackendContainer::~BackendContainer() {} diff --git a/quisp/modules/Backend/Backend.h b/quisp/modules/Backend/Backend.h index 26dd0034a..b1fec502c 100644 --- a/quisp/modules/Backend/Backend.h +++ b/quisp/modules/Backend/Backend.h @@ -4,6 +4,7 @@ #include #include #include "RNG.h" +#include "feature_defines.h" namespace quisp::modules::backend { using quisp::modules::common::ErrorTrackingBackend; @@ -16,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)); @@ -36,7 +41,10 @@ class BackendContainer : public omnetpp::cSimpleModule { protected: 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 index 0d76a7702..74251dc8f 100644 --- a/quisp/modules/Common/PythonEmbeddedable.h +++ b/quisp/modules/Common/PythonEmbeddedable.h @@ -1,37 +1,16 @@ #pragma once -#include -#include -#include -#include -#include -#include "pybind11/pybind11.h" +#include namespace quisp::modules::python_embeddable { +namespace py = pybind11; + class PythonEmbeddable { public: PythonEmbeddable() {} ~PythonEmbeddable() {} - void compile_code(const char* file_name) { - FILE* file = std::fopen(file_name, "rt"); - if (file == NULL) { - std::cout << file_name << " not found" << std::endl; - return; - } - std::fseek(file, 0L, SEEK_END); - auto length = std::ftell(file); - std::rewind(file); - char* source = (char*)malloc(length + 1); - std::fread(source, sizeof(char), length, file); - fclose(file); - source[length] = '\0'; - std::cout << source << std::endl; - code_object = Py_CompileString(source, file_name, Py_file_input); - globals = pybind11::globals().ptr(); - } - - void execute_python() { PyEval_EvalCode(code_object, globals, pybind11::dict().ptr()); } + void compile_code(const char* file_name); + void execute_python(const char* func_name, py::object& arg1); - PyObject* globals; - PyObject* code_object = nullptr; + pybind11::module_ module; }; } // namespace quisp::modules::python_embeddable diff --git a/quisp/test_utils/Configuration.h b/quisp/test_utils/Configuration.h index f478c44a8..af125842d 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,31 @@ 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() const override {} + virtual std::vector getParameterKeyValuePairs() const override {} + virtual std::vector getMatchingPerObjectConfigKeys(const char *objectFullPath, const char *keySuffixPattern) 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; }