diff --git a/CMakeLists.txt b/CMakeLists.txt index edd2354..3b0a274 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) option(MNODE_BUILD_TESTS "Build mnode test binaries" ${MESYTEC_MNODE_MAIN_PROJECT}) if (MNODE_BUILD_TESTS) - find_package(GTest CONFIG) + find_package(GTest CONFIG QUIET) if (NOT GTest_FOUND) include(FetchContent) FetchContent_Declare(googletest URL https://github.com/google/googletest/archive/refs/tags/v1.15.2.zip) @@ -34,6 +34,14 @@ if (MNODE_BUILD_TESTS) enable_testing() endif() +include(FetchContent) + FetchContent_Declare(pybind11 GIT_REPOSITORY https://github.com/pybind/pybind11 GIT_TAG v2.13.6) + FetchContent_GetProperties(pybind11) + if(NOT pybind11_POPULATED) + FetchContent_Populate(pybind11) + add_subdirectory(${pybind11_SOURCE_DIR} ${pybind11_BINARY_DIR}) +endif() + include(CMakeRC) cmrc_add_resource_library(mnode-resources ALIAS mnode::resources NAMESPACE mnode::resources diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6655f8c..f25096a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -38,3 +38,9 @@ if (ROOT_FOUND) add_library(mana-plugin-root-histogram SHARED mana_plugin_root_histogram.cc) target_link_libraries(mana-plugin-root-histogram PRIVATE mana mesytec-mnode rxi-logc ${ROOT_LIBRARIES}) endif() + +if (pybind11_FOUND) + add_library(mana-plugin-python SHARED mana_plugin_python.cc) + target_link_libraries(mana-plugin-python PRIVATE mana mesytec-mnode rxi-logc pybind11::embed) + file(CREATE_LINK ${CMAKE_CURRENT_SOURCE_DIR}/mana_python_test_plugin.py ${CMAKE_BINARY_DIR}/mana_python_test_plugin.py COPY_ON_ERROR SYMBOLIC) +endif() diff --git a/src/mana_plugin_python.cc b/src/mana_plugin_python.cc new file mode 100644 index 0000000..abef175 --- /dev/null +++ b/src/mana_plugin_python.cc @@ -0,0 +1,105 @@ +#include "internal/mana_lib.hpp" +#include +extern "C" +{ +#include "internal/rxi/log.h" +} +#include +#include + +using namespace mesytec; +using namespace mesytec::mnode; +namespace py = pybind11; + +PYBIND11_EMBEDDED_MODULE(py_mana, m) {} + +struct Context +{ + py::scoped_interpreter interp; + py::module usercode; + py::object userobject; + py::object py_begin_run; + py::object py_event_data; + py::object py_end_run; + std::vector> eventBuffers; +}; + +MANA_DEFINE_PLUGIN_INIT(init) +{ + log_set_level(LOG_DEBUG); + static Context g_ctx; + auto ctx = &g_ctx; + log_debug("init: ctx=%p", ctx); + // TODO: pass args to init() + // TODO: catch exceptions + ctx->usercode = py::module::import("mana_python_test_plugin"); + return ctx; +} + +MANA_DEFINE_PLUGIN_SHUTDOWN(shutdown) +{ + (void)context; + log_debug("shutdown"); +} + +MANA_DEFINE_PLUGIN_BEGIN_RUN(begin_run) +{ + log_debug("begin_run: context=%p, descriptor_json=%s", context, descriptor_json); + auto jRun = nlohmann::json::parse(descriptor_json); + auto ctx = reinterpret_cast(context); + ctx->usercode.reload(); + // TODO: check if the retrieved attributes are callable + ctx->py_begin_run = ctx->usercode.attr("begin_run"); + ctx->py_event_data = ctx->usercode.attr("event_data"); + ctx->py_end_run = ctx->usercode.attr("end_run"); + ctx->py_begin_run(descriptor_json); + ctx->eventBuffers.clear(); + ctx->eventBuffers.resize(jRun["events"].size()); + + for (size_t ei = 0; ei < jRun["events"].size(); ++ei) + { + auto &jEvent = jRun["events"][ei]; + ctx->eventBuffers[ei].resize(jEvent["outputs"].size()); + } +} + +MANA_DEFINE_PLUGIN_END_RUN(end_run) +{ + log_debug("end: context=%p, descriptor_json=%s", context, descriptor_json); + auto ctx = reinterpret_cast(context); + ctx->py_end_run(descriptor_json); +} + +MANA_DEFINE_PLUGIN_EVENT_DATA(process_event) +{ + log_trace("event: ctx=%p, eventIndex=%d, arrayCount=%zu, totalBytes=%zu", context, eventIndex, + arrayCount, totalBytes); + auto ctx = reinterpret_cast(context); + auto &buffers = ctx->eventBuffers.at(eventIndex); + assert(buffers.size() == arrayCount); + for (size_t ai = 0; ai < arrayCount; ++ai) + { + auto arraySpan = mana::get_span(arrays[ai]); + ctx->eventBuffers[eventIndex][ai] = + py::memoryview::from_memory(arraySpan.data(), arraySpan.size() * sizeof(float)); + } + ctx->py_event_data(eventIndex, ctx->eventBuffers); +} + +MANA_DEFINE_PLUGIN_SYSTEM_EVENT(process_system_event) +{ + log_trace("system_event: ctx=%p, size=%zu", context, size); + auto ctx = reinterpret_cast(context); +} + +extern "C" MANA_DEFINE_GET_PLUGIN(mana_get_plugin) +{ + mana_plugin_t plugin; + plugin.init = init; + plugin.shutdown = shutdown; + plugin.begin_run = begin_run; + plugin.end_run = end_run; + plugin.process_event = process_event; + plugin.process_system_event = process_system_event; + return plugin; +} diff --git a/src/mana_python_test_plugin.py b/src/mana_python_test_plugin.py new file mode 100644 index 0000000..715f215 --- /dev/null +++ b/src/mana_python_test_plugin.py @@ -0,0 +1,13 @@ + +import json +import py_mana + +def begin_run(runDescription: str): + print("begin_run") + +def end_run(runDescription: str): + print("end_run") + +def event_data(eventIndex: int, dataArrays): + return + print(f"event[{eventIndex}]: {dataArrays}")