ChimeraTK-ApplicationCore 04.06.00
Loading...
Searching...
No Matches
PyApplicationModule.cc
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
4#include <pybind11/embed.h>
5// pybind11 includes should come first
6
7#include "Application.h"
9#include "PyConfigReader.h"
10#include "PyLogger.h"
11
12#include <pybind11/stl.h>
13
14#include <iostream>
15#include <map>
16#include <string>
17
18namespace py = pybind11;
19using namespace py::literals;
20
21namespace ChimeraTK {
22
23 /********************************************************************************************************************/
24
26 {
27 py::gil_scoped_acquire gil;
28
29 auto locals = py::dict("theModule"_a = *this);
30 py::exec(R"(
31 def mainLoopWrapper2(self):
32 try:
33 self.mainLoopWrapper()
34 except ThreadInterrupted as e:
35 pass
36 except Exception as e:
37 print("Exception in module "+self.getName()+":")
38 traceback.print_exception(e)
39 theModule.mainLoopWrapper2 = mainLoopWrapper2.__get__(theModule)
40 )",
41 py::globals(), locals);
42 _myThread = py::eval("threading.Thread(target=theModule.mainLoopWrapper2)", py::globals(), locals);
43 Application::getInstance().getTestableMode().unlock("releaseForPythonModuleStart");
44 _myThread.attr("start")();
45 }
46 // must release GIL before acquiring testable mode lock, to avoid deadlock
47 // lock must be exclusive, since this is executed in the main thread (= test thread). The module thread is launched
48 // above in the py::eval() call.
49 Application::getInstance().getTestableMode().lock("acquireForPythonModuleStart", false);
50 }
51
52 /********************************************************************************************************************/
53
56
57 // The module was not started
58 if(!_myThread) {
59 return;
60 }
61
62 py::gil_scoped_acquire gil;
63 while(_myThread.attr("is_alive")().cast<bool>()) {
64 for(auto& var : getAccessorListRecursive()) {
65 auto el{var.getAppAccessorNoType().getHighLevelImplElement()};
66 el->interrupt();
67 }
68
69 _myThread.attr("join")(0.01);
70 }
71 }
72
73 /********************************************************************************************************************/
74
76 // on Python side, PythonApplicationModule derives from PyVariableGroup although on C++ side, it does not.
77 // So we specify inheritance on Python side by constructor args.
79 std::unique_ptr<PyApplicationModule, py::nodelete>>
80 cam(m, "ApplicationModule", py::multiple_inheritance());
81
82 cam.def(py::init([](ModuleGroup& owner, const std::string& name, const std::string& description,
83 const std::unordered_set<std::string>& tags) {
84 return dynamic_cast<PyOwningObject&>(owner).make_child<PythonApplicationModuleTrampoline>(
85 &owner, name, description, tags);
86 }),
87 py::return_value_policy::reference,
88 // doc and default args:
89 "", py::arg("owner"), py::arg("name"), py::arg("description"),
90 py::arg("tags") = std::unordered_set<std::string>{});
91
92 cam.def("mainLoopWrapper", &PyApplicationModule::mainLoopWrapper, py::call_guard<py::gil_scoped_release>())
93 .def("appConfig",
94 []([[maybe_unused]] PyApplicationModule& pam) { return PyConfigReader(PyApplicationModule::appConfig()); })
95 .def("getDataValidity", &PyApplicationModule::getDataValidity,
96 "Return the data validity flag.\n\nIf any This function will be called by all output accessors in their "
97 "write functions.")
98 .def("getCurrentVersionNumber", &PyApplicationModule::getCurrentVersionNumber,
99 "Return the current version number which has been received with the last push-type read operation.")
100 .def("setCurrentVersionNumber", &PyApplicationModule::setCurrentVersionNumber,
101 "Set the current version number.\n\nThis function is called by the push-type input accessors in their read "
102 "functions.",
103 py::arg("versionNumber"))
104 // TODO: Bind Model .def("getModel", &PythonApplicationModule::getModel)
105 .def("incrementDataFaultCounter", &PyApplicationModule::incrementDataFaultCounter,
106 "Set the data validity flag to fault and increment the fault counter.\n\nThis function will be called by "
107 "all input accessors when receiving the a faulty update if the previous update was ok. The caller of this "
108 "function must ensure that calls to this function are paired to a subsequent call to "
109 "decrementDataFaultCounter().")
110 .def("decrementDataFaultCounter", &PyApplicationModule::decrementDataFaultCounter,
111 "Decrement the fault counter and set the data validity flag to ok if the counter has reached 0.\n\nThis "
112 "function will be called by all input accessors when receiving the an ok update if the previous update was "
113 "faulty. The caller of this function must ensure that calles to this function are paired to a previous "
114 "call to incrementDataFaultCounter().")
115 .def("getDataFaultCounter", &PyApplicationModule::getDataFaultCounter,
116 "Get the Number of inputs which report DataValidity::faulty.")
117 .def(
118 "logger",
119 [](PyApplicationModule& module, Logger::Severity severity) {
120 // Get name of python implementation, not the C++ class, which would be PythonApplicationModule
121 auto pythonClassName = py::cast(&module).get_type().attr("__name__").cast<std::string>();
122 return PyLoggerStreamProxy(severity, pythonClassName);
123 },
124 "Convenicene function to obtain a logger stream with the given Severity.\n\nThe context string will be "
125 "obtained from the class name of the module.")
126 .def("disable", &PyApplicationModule::disable,
127 "Disable the module such that it is not part of the Application.");
128 }
129
130 /********************************************************************************************************************/
131
132} // namespace ChimeraTK
detail::TestableMode & getTestableMode()
Get the TestableMode control object of this application.
static Application & getInstance()
Obtain instance of the application.
DataValidity getDataValidity() const override
Return the data validity flag.
void terminate() override
Terminate the module.
void decrementDataFaultCounter() override
Decrement the fault counter and set the data validity flag to ok if the counter has reached 0.
void setCurrentVersionNumber(VersionNumber versionNumber) override
Set the current version number.
VersionNumber getCurrentVersionNumber() const override
Return the current version number which has been received with the last push-type read operation.
void incrementDataFaultCounter() override
Set the data validity flag to fault and increment the fault counter.
size_t getDataFaultCounter() const
Get the Number of inputs which report DataValidity::faulty.
std::list< VariableNetworkNode > getAccessorListRecursive() const
Obtain the list of accessors/variables associated with this instance and any submodules.
Severity
Severity levels used by the Logger.
Definition Logger.h:27
void disable()
Disable the module such that it is not part of the Application.
Definition Module.cc:257
static ConfigReader & appConfig()
Obtain the ConfigReader instance of the application.
Definition Module.cc:251
void terminate() override
Terminate the module.
static void bind(py::module &mod)
void mainLoopWrapper()
Wrapper around mainLoop(), to execute additional tasks in the thread before entering the main loop.
void run() override
Execute the module.
PyLoggerStreamProxy.
Definition PyLogger.h:28
Base class used for all objects in the Python world which can own other objects and can be owned them...
InvalidityTracer application module.
module_ module