Skip to content
Snippets Groups Projects
Unverified Commit 468fc9ed authored by zenker's avatar zenker Committed by GitHub
Browse files

improve history module

* Use templated version to add a trigger.

A trigger is needed for Poll type inputs. Still it can not be used for
Devices. Here one has to call addSource manually.

* No need to use a trigger. Fix connections by moving all to history.

Trigger is only needed for DeviceModules. So in case of device addSource
still needs to be used, because no tags are present for devices.

* Add option to specify the directory where history will appear in CS.

* Remove CS tag not needed any more.

* Update documentation.

* Update documentation of logging module.

* Remove CS tag not needed any more.

* Remove parameter not needed. AddSource for ConnectingDeviceModule. Docu.
parent 67b413fd
No related branches found
No related tags found
No related merge requests found
...@@ -8,32 +8,27 @@ ...@@ -8,32 +8,27 @@
* \page loggingdoc Logging module and Logger * \page loggingdoc Logging module and Logger
* \section loggingintro Introduction to the logging mechanism * \section loggingintro Introduction to the logging mechanism
* The logging provided here requires to add the LoggingModule to your * The logging provided here requires to add the LoggingModule to your
Application. * Application.
* The module introduces the following input variables, that need to be * The module introduces the following input variables:
connected to the control system:
* - targetStream: Allows to choose where messages send to the logging module * - targetStream: Allows to choose where messages send to the logging module
end up: * end up:
* - 0: cout/cerr+logfile * - 0: cout/cerr+logfile
* - 1: logfile * - 1: logfile
* - 2: cout/cerr * - 2: cout/cerr
* - 3: controlsystem only * - 3: controlsystem only
* - 4: nowhere * - 4: nowhere
* - logFile: Give the logfile name. If the file is not empty logging messages * - logFile: Give the logfile name. If the file is not empty logging messages
will be appended. If * will be appended. If you choose targetStream 0 or 1 and don't set a logFile
* you choose targetStream 0 or 1 and don't set a logFile the Logging module * the Logging module simply skips the file writing.
simply skips the file
* writing.
* - logLevel: Choose a certain logging level of the Module. Messages send to * - logLevel: Choose a certain logging level of the Module. Messages send to
the Logging module also include a logging * the Logging module also include a logging level.
* level. The Logging module compares both levels and decides if a message is * The Logging module compares both levels and decides if a message is
dropped (e.g. message level is * dropped (e.g. message level is DEBUG and Module level is ERROR) or broadcasted.
* DEBUG and Module level is ERROR) or broadcasted.
* - maxTailLength: The number of messages published by the Logging module (see * - maxTailLength: The number of messages published by the Logging module (see
logTail), i.e. to the control system. If set to 0 the number of messages defaults to 20. * logTail), i.e. to the control system. If set to 0 the number of messages defaults to 20.
* This length has no influence on the targetStreams, that receive all * This length has no influence on the targetStreams, that receive all
messages (depending on the logLevel). The * messages (depending on the logLevel). The logLevel also applies to messages
* logLevel also applies to messages that are published by the Logging module * that are published by the Logging module via the logTail
via the logTail
* *
* Available logging levels are: * Available logging levels are:
* - DEBUG * - DEBUG
...@@ -43,15 +38,21 @@ ...@@ -43,15 +38,21 @@
* - SILENT * - SILENT
* *
* The only variable that is published by the Logging module is the logTail. It * The only variable that is published by the Logging module is the logTail. It
contains the list of latest messages. * contains the list of latest messages.
* Messages are separated by a newline character. The number of messages * Messages are separated by a newline character. The number of messages
published in the logTail is set via the * published in the logTail is set via the
* input variable tailLength. Other than that, messages are written to * input variable tailLength. Other than that, messages are written to
cout/cerr and/or a log file as explained above. * cout/cerr and/or a log file as explained above.
* *
* A Logger class is used to send messages to the LoggingModule. * A Logger class is used to send messages to the LoggingModule.
* The foreseen way of using the Logger is to add a Logger to a module that * The foreseen way of using the Logger is to add a Logger to a module that
should send log messages. * should send log messages.
* The Logger adds two variables that will be available in the control system:
* - alias: It can be set at runtime and will be used as prefix in messages of that particular
* Logger. If it is set empty the name of the owning module is used.
* - message: This is the message send to the LoggingModule. It includes the severity encoded
* as number in the first character of the string followed by the message.
*
* *
* The LoggingModule will take care of finding all Loggers. * The LoggingModule will take care of finding all Loggers.
* Therefore, the LoggingModule needs to be constructed last - after all ApplicationModules * Therefore, the LoggingModule needs to be constructed last - after all ApplicationModules
...@@ -71,8 +72,6 @@ ...@@ -71,8 +72,6 @@
* }; * };
* struct myApp : public ChimeraTK::Application{ * struct myApp : public ChimeraTK::Application{
* *
*
* ChimeraTK::ControlSystemModule cs;
* *
* TestModule { this, "test", "" }; * TestModule { this, "test", "" };
* *
...@@ -82,12 +81,6 @@ ...@@ -82,12 +81,6 @@
* ... * ...
* }; * };
* *
*
* void myAPP::defineConnctions(){
* log.findTag("CS").connectTo(cs);
* ...
* }
*
* void TestModule::mainLoop{ * void TestModule::mainLoop{
* logger.sendMessage("Test",LogLevel::DEBUG); * logger.sendMessage("Test",LogLevel::DEBUG);
* ... * ...
...@@ -225,13 +218,6 @@ namespace logging { ...@@ -225,13 +218,6 @@ namespace logging {
ctk::RegisterPath prepareHierarchy(const ctk::RegisterPath& namePrefix); ctk::RegisterPath prepareHierarchy(const ctk::RegisterPath& namePrefix);
struct MessageSource { struct MessageSource {
/*
* Instead of constructing msg variables with name id and connecting it directly to
* to the message variable of the Logger one could have a variable group here that holds
* the variables message and alias. The variable group name is the id and it is moved
* the correct location in the CS in order to achieve an automatic direct connection when
* the CS is build. Use a ctk::HierarchyModifyingGroup to do that.
*/
struct Data : ctk::HierarchyModifyingGroup { struct Data : ctk::HierarchyModifyingGroup {
using ctk::HierarchyModifyingGroup::HierarchyModifyingGroup; using ctk::HierarchyModifyingGroup::HierarchyModifyingGroup;
ctk::ScalarPushInput<std::string> msg{this, "message", "", "", {"_logging_internal"}}; ctk::ScalarPushInput<std::string> msg{this, "message", "", "", {"_logging_internal"}};
...@@ -279,13 +265,11 @@ namespace logging { ...@@ -279,13 +265,11 @@ namespace logging {
{"CS", getName()}}; {"CS", getName()}};
ctk::ScalarPollInput<uint> tailLength{this, "maxTailLength", "", ctk::ScalarPollInput<uint> tailLength{this, "maxTailLength", "",
"Maximum number of messages to be shown in the logging stream tail. 0 is treated as 20.", {"CS", getName()}}; "Maximum number of messages to be shown in the logging stream tail. 0 is treated as 20.", {getName()}};
ctk::ScalarPollInput<uint> logLevel{ ctk::ScalarPollInput<uint> logLevel{this, "logLevel", "", "Current log level used for messages.", {getName()}};
this, "logLevel", "", "Current log level used for messages.", {"CS", getName()}};
ctk::ScalarOutput<std::string> logTail{ ctk::ScalarOutput<std::string> logTail{this, "logTail", "", "Tail of the logging stream.", {"PROCESS", getName()}};
this, "logTail", "", "Tail of the logging stream.", {"CS", "PROCESS", getName()}};
std::unique_ptr<std::ofstream> file; ///< Log file where to write log messages std::unique_ptr<std::ofstream> file; ///< Log file where to write log messages
......
...@@ -28,12 +28,19 @@ ...@@ -28,12 +28,19 @@
* and the element index i is appended to the feeding process variable name. In * and the element index i is appended to the feeding process variable name. In
* consequence an input array of length i will result in i output history * consequence an input array of length i will result in i output history
* arrays. The following tags are added to the history output variable: * arrays. The following tags are added to the history output variable:
* - CS
* - name of the history module * - name of the history module
* *
* It is also possible to connect a DeviceModule to the ServerHistory module. This * The connection of variables with the 'history' tag to the ServerHistory module is
* requires a trigger, which is given as optional parameter to the \c addSource * done automatically.
* method. If the device variables are writable they are of push type. In this case * \attention Only variables of modules defined before constructing the ServerHistory
* module are considered.
*
* It is also possible to connect a DeviceModule to the ServerHistory module.
* Variables of Devices have no tags and therefor they will not be automatically connected
* to the SereverHistory module. One has to call addSource().
* In addition a trigger in case the variables are not push type. It is given as optional
* parameter to the \c addSource method.
* If the device variables are writable they are of push type. In this case
* the trigger will not be added. One has to use the LogicalNameMapping backend to * the trigger will not be added. One has to use the LogicalNameMapping backend to
* force the device variables to be read only by using the \c forceReadOnly plugin. * force the device variables to be read only by using the \c forceReadOnly plugin.
* Using the LogicalNameMapping backend also allows to select individual device * Using the LogicalNameMapping backend also allows to select individual device
...@@ -44,7 +51,7 @@ ...@@ -44,7 +51,7 @@
* \code * \code
* sruct TestModule: public ChimeraTK::ApplicationModule{ * sruct TestModule: public ChimeraTK::ApplicationModule{
* chimeraTK::ScalarOutput<float> measurement{this, "measurement", "" , * chimeraTK::ScalarOutput<float> measurement{this, "measurement", "" ,
* "measurement variable", {"CS", History"}}; * "measurement variable", {"history"}};
* ... * ...
* }; * };
* struct myApp : public ChimeraTK::Application{ * struct myApp : public ChimeraTK::Application{
...@@ -54,7 +61,7 @@ ...@@ -54,7 +61,7 @@
* *
* ChimeraTK::ControlSystemModule cs; * ChimeraTK::ControlSystemModule cs;
* *
* ChimeraTK::DeviceModule dev{this, "Dummy"}; * ChimeraTK::ConnectingDeviceModule dev{this, "Dummy", "Trigger/tick"};
* *
* ChimeraTK::PeriodicTrigger trigger{this, "Trigger", "Trigger used for other modules"}; * ChimeraTK::PeriodicTrigger trigger{this, "Trigger", "Trigger used for other modules"};
* *
...@@ -64,15 +71,12 @@ ...@@ -64,15 +71,12 @@
* }; * };
* *
* *
* void myAPP::defineConnctions(){ * void myAPP::intitialise(){
* // connect a module with variables that are updated by the module, which * // The variable of the TestModule will show up in the control system as history/test/measurement automatically
* // triggers an update of the history buffer * (identified by the tag).
* history.addSource(test.findTag("History"), "history" + test->getName()) * // Add a device. Updating of the history buffer is trigger external by the given trigger
* // will show up in the control system as history/test/measurement * history.addSource(&dev,"device_history",trigger.tick);
* // add a device. Updating of the history buffer is trigger external by the given trigger * ChimeraTK::Application::initialise();
* history.addSource(dev,"device_history",trigger.tick);
*
* history.findTag("CS").connectTo(cs);
* ... * ...
* } * }
* *
...@@ -92,6 +96,7 @@ ...@@ -92,6 +96,7 @@
#include <ChimeraTK/SupportedUserTypes.h> #include <ChimeraTK/SupportedUserTypes.h>
#include <string>
#include <tuple> #include <tuple>
#include <vector> #include <vector>
...@@ -109,7 +114,8 @@ namespace ChimeraTK { namespace history { ...@@ -109,7 +114,8 @@ namespace ChimeraTK { namespace history {
bool withTimeStamps; bool withTimeStamps;
}; };
struct ServerHistory : public ApplicationModule { class ServerHistory : public ApplicationModule {
public:
/** /**
* Constructor. * Constructor.
* Addition parameters to a normal application module constructor: * Addition parameters to a normal application module constructor:
...@@ -117,55 +123,56 @@ namespace ChimeraTK { namespace history { ...@@ -117,55 +123,56 @@ namespace ChimeraTK { namespace history {
* \param name Module name passed to ApplicationModule constructor. * \param name Module name passed to ApplicationModule constructor.
* \param description Module description passed to ApplicationModule constructor. * \param description Module description passed to ApplicationModule constructor.
* \param historyLength Length of the history buffers. * \param historyLength Length of the history buffers.
* \param enableTimeStamps An additional ring buffer per variable will be added that holds the time stamps * \param enableTimeStamps An additional
* corresponding to the data ring buffer entries. * ring buffer per variable will be added that holds the time stamps corresponding to the data ring buffer entries.
* \param eliminateHierarchy Flag passed to ApplicationModule constructor. * \param hierarchyModifier Flag passed to ApplicationModule constructor.
* \param tags Module tags passed to ApplicationModule constructor. * \param tags Module tags passed to ApplicationModule constructor.
*/ */
ServerHistory(EntityOwner* owner, const std::string& name, const std::string& description, ServerHistory(EntityOwner* owner, const std::string& name, const std::string& description,
size_t historyLength = 1200, bool enableTimeStamps = false, bool eliminateHierarchy = false, size_t historyLength = 1200, bool enableTimeStamps = false,
const std::unordered_set<std::string>& tags = {}) HierarchyModifier hierarchyModifier = HierarchyModifier::none,
: ApplicationModule(owner, name, description, eliminateHierarchy, tags), _historyLength(historyLength), const std::unordered_set<std::string>& tags = {});
_enbaleTimeStamps(enableTimeStamps) {}
/** Default constructor, creates a non-working module. Can be used for late /** Default constructor, creates a non-working module. Can be used for late
* initialisation. */ * initialisation. */
ServerHistory() : _historyLength(1200), _enbaleTimeStamps(false) {} ServerHistory() : _historyLength(1200), _enbaleTimeStamps(false) {}
/** /**
* Add a Module as a source to this History module. * Ad variables of a device to the ServerHistory. Calls virtualiseFromCatalog to get access to the internal variables.
* *
* \param source For all variables of this module ring buffers are created. Use \c findTag in combination * \param source For all variables of this module ring buffers are created.
* with a dedicated history tag. In case device modules use the LogicalNameMapping to create * Use the LogicalNameMapping to create a virtual device module that holds all variables that should be
* a virtual device module that holds all variables that should be passed to the history module. * passed to the history module.
* \param namePrefix This prefix is added to variable names added to the root directory in the process variable * \param namePrefix This prefix is added to variable names added to the root directory in the process variable
* tree. E.g. a prefix \c history for a variable names data will appear as history/dummy/data * tree. E.g. a prefix \c history for a variable names data will appear as history/dummy/data
* if dummy is the name of the source module. * if dummy is the name of the source module.
* \param submodule If only a submodule should be added give the name.
* It does not work do create a submodule of the DeviceModule itself!
* \param trigger This trigger is used for all poll type variable found in the source module. * \param trigger This trigger is used for all poll type variable found in the source module.
*
*/ */
void addSource(const Module& source, const RegisterPath& namePrefix, const VariableNetworkNode& trigger = {}); void addSource(const DeviceModule& source, const RegisterPath& namePrefix, const std::string& submodule = "",
const VariableNetworkNode& trigger = {});
/** /**
* Overload that calls virtualiseFromCatalog. Parameter see addSource(const Module&...) * Just gets the device module from the ConnectingDeviceModule before calling the DeviceModule version of addSource.
*
* \param source See the other addSource() overload.
*
* \param namePrefix See the other addSource() overload.
*
* \param submodule If only a submodule should be added give the name. It does not work do create a submodule of the
* DeviceModule itself!
*
* \param trigger See the other addSource() overload.
*/ */
void addSource(const DeviceModule& source, const RegisterPath& namePrefix, const std::string& submodule = "", void addSource(ConnectingDeviceModule* source, const RegisterPath& namePrefix, const std::string& submodule = "",
const VariableNetworkNode& trigger = {}); const VariableNetworkNode& trigger = {});
public:
void prepare() override; void prepare() override;
void mainLoop() override; void mainLoop() override;
protected: void findTagAndAppendToModule(VirtualModule& virtualParent, const std::string& tag, bool eliminateAllHierarchies,
bool eliminateFirstHierarchy, bool negate, VirtualModule& root) const override;
private:
void prepareHierarchy(const RegisterPath& namePrefix);
/**
* See public method for documentation.
*/
void addSource(const Module& source, const RegisterPath& namePrefix, const VariableNetworkNode& trigger = {});
template<typename UserType> template<typename UserType>
VariableNetworkNode getAccessor(const std::string& variableName, const size_t& nElements); VariableNetworkNode getAccessor(const std::string& variableName, const size_t& nElements);
...@@ -197,5 +204,4 @@ namespace ChimeraTK { namespace history { ...@@ -197,5 +204,4 @@ namespace ChimeraTK { namespace history {
friend struct AccessorAttacher; friend struct AccessorAttacher;
}; };
}} // namespace ChimeraTK::history }} // namespace ChimeraTK::history
...@@ -33,12 +33,40 @@ namespace ChimeraTK { namespace history { ...@@ -33,12 +33,40 @@ namespace ChimeraTK { namespace history {
const std::string& _name; const std::string& _name;
}; };
void ServerHistory::addSource( ServerHistory::ServerHistory(EntityOwner* owner, const std::string& name, const std::string& description,
const Module& source, const RegisterPath& namePrefix, const VariableNetworkNode& trigger) { size_t historyLength, bool enableTimeStamps, HierarchyModifier hierarchyModifier,
// for simplification, first create a VirtualModule containing the correct const std::unordered_set<std::string>& tags)
// hierarchy structure (obeying eliminate hierarchy etc.) : ApplicationModule(owner, name, description, hierarchyModifier, tags), _historyLength(historyLength),
auto dynamicModel = source.findTag(".*"); /// @todo use virtualise() instead _enbaleTimeStamps(enableTimeStamps) {
auto virtualLogging = getOwner()->findTag("history");
auto list = virtualLogging.getAccessorListRecursive();
size_t accessors = 0;
for(auto it = list.begin(); it != list.end(); ++it) {
// do not add the module itself
if(it->getOwningModule() == this) continue;
try {
// virtualLogging.getQualifiedName() returns the name of the app, e.g. /test and we remove that from the module
// name , e.g. /test/MyModule
auto namePrefix =
it->getOwningModule()->getQualifiedName().substr(virtualLogging.getQualifiedName().length() + 1);
prepareHierarchy(namePrefix);
boost::fusion::for_each(_accessorListMap.table, AccessorAttacher(*it, this, namePrefix / it->getName(), {}));
accessors++;
}
catch(ChimeraTK::logic_error& e) {
std::cerr << "Failed to add history variable: " << it->getQualifiedName() << " Error: " << e.what()
<< std::endl;
}
}
if(accessors == 0) {
throw logic_error("No accessors for ServerHistory found. Did you use the tag 'history' for any variable?");
}
else {
std::cout << "Added " << accessors << " accessors to the ServerHistory Module." << std::endl;
}
}
void ServerHistory::prepareHierarchy(const RegisterPath& namePrefix) {
// create variable group map for namePrefix if needed // create variable group map for namePrefix if needed
if(groupMap.find(namePrefix) == groupMap.end()) { if(groupMap.find(namePrefix) == groupMap.end()) {
// search for existing parent (if any) // search for existing parent (if any)
...@@ -58,6 +86,15 @@ namespace ChimeraTK { namespace history { ...@@ -58,6 +86,15 @@ namespace ChimeraTK { namespace history {
groupMap[parentPrefix] = VariableGroup(owner, std::string(name).substr(1), ""); groupMap[parentPrefix] = VariableGroup(owner, std::string(name).substr(1), "");
} }
} }
}
void ServerHistory::addSource(
const Module& source, const RegisterPath& namePrefix, const VariableNetworkNode& trigger) {
// for simplification, first create a VirtualModule containing the correct
// hierarchy structure (obeying eliminate hierarchy etc.)
auto dynamicModel = source.findTag(".*"); /// @todo use virtualise() instead
prepareHierarchy(namePrefix);
// add all accessors on this hierarchy level // add all accessors on this hierarchy level
for(auto& acc : dynamicModel.getAccessorList()) { for(auto& acc : dynamicModel.getAccessorList()) {
...@@ -79,6 +116,11 @@ namespace ChimeraTK { namespace history { ...@@ -79,6 +116,11 @@ namespace ChimeraTK { namespace history {
addSource(mod.submodule(submodule), namePrefix, trigger); addSource(mod.submodule(submodule), namePrefix, trigger);
} }
void ServerHistory::addSource(ConnectingDeviceModule* source, const RegisterPath& namePrefix,
const std::string& submodule, const VariableNetworkNode& trigger) {
addSource(source->getDeviceModule(), namePrefix, submodule, trigger);
}
template<typename UserType> template<typename UserType>
VariableNetworkNode ServerHistory::getAccessor(const std::string& variableName, const size_t& nElements) { VariableNetworkNode ServerHistory::getAccessor(const std::string& variableName, const size_t& nElements) {
// check if variable name already registered // check if variable name already registered
...@@ -97,33 +139,28 @@ namespace ChimeraTK { namespace history { ...@@ -97,33 +139,28 @@ namespace ChimeraTK { namespace history {
auto dirName = variableName.substr(0, variableName.find_last_of("/")); auto dirName = variableName.substr(0, variableName.find_last_of("/"));
auto baseName = variableName.substr(variableName.find_last_of("/") + 1); auto baseName = variableName.substr(variableName.find_last_of("/") + 1);
tmpList.emplace_back(std::piecewise_construct, tmpList.emplace_back(std::piecewise_construct,
std::forward_as_tuple(ArrayPushInput<UserType>{ std::forward_as_tuple(
&groupMap[dirName], ArrayPushInput<UserType>{&groupMap[dirName], baseName + "_in", "", 0, "", {"_history_internal"}}),
baseName + "_in",
"",
0,
"",
}),
std::forward_as_tuple(HistoryEntry<UserType>{_enbaleTimeStamps})); std::forward_as_tuple(HistoryEntry<UserType>{_enbaleTimeStamps}));
for(size_t i = 0; i < nElements; i++) { for(size_t i = 0; i < nElements; i++) {
if(nElements == 1) { if(nElements == 1) {
// in case of a scalar history only use the variableName // in case of a scalar history only use the variableName
tmpList.back().second.data.emplace_back( tmpList.back().second.data.emplace_back(
ArrayOutput<UserType>{&groupMap[dirName], baseName, "", _historyLength, "", {"CS", getName()}}); ArrayOutput<UserType>{&groupMap[dirName], baseName, "", _historyLength, "", {getName()}});
if(_enbaleTimeStamps) { if(_enbaleTimeStamps) {
tmpList.back().second.timeStamp.emplace_back( tmpList.back().second.timeStamp.emplace_back(
ArrayOutput<uint64_t>{&groupMap[dirName], baseName + "_timeStamps", ArrayOutput<uint64_t>{&groupMap[dirName], baseName + "_timeStamps",
"Time stamps for entries in the history buffer", _historyLength, "", {"CS", getName()}}); "Time stamps for entries in the history buffer", _historyLength, "", {getName()}});
} }
} }
else { else {
// in case of an array history append the index to the variableName // in case of an array history append the index to the variableName
tmpList.back().second.data.emplace_back(ArrayOutput<UserType>{ tmpList.back().second.data.emplace_back(ArrayOutput<UserType>{
&groupMap[dirName], baseName + "_" + std::to_string(i), "", _historyLength, "", {"CS", getName()}}); &groupMap[dirName], baseName + "_" + std::to_string(i), "", _historyLength, "", {getName()}});
if(_enbaleTimeStamps) { if(_enbaleTimeStamps) {
tmpList.back().second.timeStamp.emplace_back( tmpList.back().second.timeStamp.emplace_back(
ArrayOutput<uint64_t>{&groupMap[dirName], baseName + "_" + std::to_string(i) + "_timeStamps", ArrayOutput<uint64_t>{&groupMap[dirName], baseName + "_" + std::to_string(i) + "_timeStamps",
"Time stamps for entries in the history buffer", _historyLength, "", {"CS", getName()}}); "Time stamps for entries in the history buffer", _historyLength, "", {getName()}});
} }
} }
} }
...@@ -175,4 +212,26 @@ namespace ChimeraTK { namespace history { ...@@ -175,4 +212,26 @@ namespace ChimeraTK { namespace history {
} }
} }
void ServerHistory::findTagAndAppendToModule(VirtualModule& virtualParent, const std::string& tag,
bool eliminateAllHierarchies, bool eliminateFirstHierarchy, bool negate, VirtualModule& root) const {
// Change behaviour to exclude the auto-generated inputs which are connected to the data sources. Otherwise those
// variables might get published twice to the control system, if findTag(".*") is used to connect the entire
// application to the control system.
// This is a temporary solution. In future, instead the inputs should be generated at the same place in the
// hierarchy as the source variable, and the connetion should not be made by the module itself. This currently would
// be complicated to implement, since it is difficult to find the correct virtual name for the variables.
struct MyVirtualModule : VirtualModule {
using VirtualModule::VirtualModule;
using VirtualModule::findTagAndAppendToModule;
};
MyVirtualModule tempParent("tempRoot", "", ModuleType::ApplicationModule);
MyVirtualModule tempRoot("tempRoot", "", ModuleType::ApplicationModule);
EntityOwner::findTagAndAppendToModule(
tempParent, "_history_internal", eliminateAllHierarchies, eliminateFirstHierarchy, true, tempRoot);
tempParent.findTagAndAppendToModule(virtualParent, tag, false, true, negate, root);
tempRoot.findTagAndAppendToModule(root, tag, false, true, negate, root);
}
}} // namespace ChimeraTK::history }} // namespace ChimeraTK::history
...@@ -62,14 +62,11 @@ struct testApp : public ChimeraTK::Application { ...@@ -62,14 +62,11 @@ struct testApp : public ChimeraTK::Application {
~testApp() override { shutdown(); } ~testApp() override { shutdown(); }
Dummy<UserType> dummy{this, "Dummy", "Dummy module"}; Dummy<UserType> dummy{this, "Dummy", "Dummy module"};
ChimeraTK::history::ServerHistory hist{this, "ServerHistory", "History of selected process variables.", 20}; ChimeraTK::history::ServerHistory hist{this, "history", "History of selected process variables.", 20};
ChimeraTK::ControlSystemModule cs; void initialise() override {
Application::initialise();
void defineConnections() override { dumpConnections();
hist.addSource(dummy.findTag("history"), "history/" + dummy.getName());
hist.findTag("CS").connectTo(cs);
dummy.connectTo(cs);
} }
}; };
...@@ -82,14 +79,11 @@ struct testAppArray : public ChimeraTK::Application { ...@@ -82,14 +79,11 @@ struct testAppArray : public ChimeraTK::Application {
~testAppArray() override { shutdown(); } ~testAppArray() override { shutdown(); }
DummyArray<UserType> dummy{this, "Dummy", "Dummy module"}; DummyArray<UserType> dummy{this, "Dummy", "Dummy module"};
ChimeraTK::history::ServerHistory hist{this, "ServerHistory", "History of selected process variables.", 20}; ChimeraTK::history::ServerHistory hist{this, "history", "History of selected process variables.", 20};
ChimeraTK::ControlSystemModule cs;
void defineConnections() override { void initialise() override {
hist.addSource(dummy.findTag("history"), "history/" + dummy.getName()); Application::initialise();
hist.findTag("CS").connectTo(cs); dumpConnections();
dummy.connectTo(cs);
} }
}; };
...@@ -100,18 +94,16 @@ struct testAppDev : public ChimeraTK::Application { ...@@ -100,18 +94,16 @@ struct testAppDev : public ChimeraTK::Application {
testAppDev() : Application("test") { ChimeraTK::BackendFactory::getInstance().setDMapFilePath("test.dmap"); } testAppDev() : Application("test") { ChimeraTK::BackendFactory::getInstance().setDMapFilePath("test.dmap"); }
~testAppDev() override { shutdown(); } ~testAppDev() override { shutdown(); }
ChimeraTK::history::ServerHistory hist{this, "ServerHistory", "History of selected process variables.", 20}; ChimeraTK::ConnectingDeviceModule dev{this, "Dummy1Mapped", "/Dummy/out"};
ChimeraTK::DeviceModule dev{this, "Dummy1Mapped"};
DummyArray<int> dummy{this, "Dummy", "Dummy module"}; DummyArray<int> dummy{this, "Dummy", "Dummy module"};
ChimeraTK::ControlSystemModule cs; ChimeraTK::history::ServerHistory hist{this, "history", "History of selected process variables.", 20, false};
void defineConnections() override { void initialise() override {
dummy.connectTo(cs); hist.addSource(&dev, "", "", dummy.out);
hist.addSource(dev, "history", "", dummy.out); Application::initialise();
hist.findTag("CS").connectTo(cs); dumpConnections();
} }
}; };
...@@ -119,7 +111,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(testScalarHistory, T, test_types) { ...@@ -119,7 +111,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(testScalarHistory, T, test_types) {
std::cout << "testScalarHistory " << typeid(T).name() << std::endl; std::cout << "testScalarHistory " << typeid(T).name() << std::endl;
testApp<T> app; testApp<T> app;
ChimeraTK::TestFacility tf; ChimeraTK::TestFacility tf;
auto i = tf.getScalar<T>("in"); auto i = tf.getScalar<T>("Dummy/in");
tf.runApplication(); tf.runApplication();
i = 42.; i = 42.;
i.write(); i.write();
...@@ -143,7 +135,7 @@ BOOST_AUTO_TEST_CASE(testScalarHistoryString) { ...@@ -143,7 +135,7 @@ BOOST_AUTO_TEST_CASE(testScalarHistoryString) {
std::cout << "testScalarHistoryString" << std::endl; std::cout << "testScalarHistoryString" << std::endl;
testApp<std::string> app; testApp<std::string> app;
ChimeraTK::TestFacility tf; ChimeraTK::TestFacility tf;
auto i = tf.getScalar<std::string>("in"); auto i = tf.getScalar<std::string>("Dummy/in");
tf.runApplication(); tf.runApplication();
i = "42"; i = "42";
i.write(); i.write();
...@@ -164,16 +156,16 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(testArrayHistory, T, test_types) { ...@@ -164,16 +156,16 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(testArrayHistory, T, test_types) {
std::cout << "testArrayHistory " << typeid(T).name() << std::endl; std::cout << "testArrayHistory " << typeid(T).name() << std::endl;
testAppArray<T> app; testAppArray<T> app;
ChimeraTK::TestFacility tf; ChimeraTK::TestFacility tf;
auto arr = tf.getArray<T>("in"); auto arr = tf.getArray<T>("Dummy/in");
tf.runApplication(); tf.runApplication();
arr[0] = 42.; arr[0] = 42.;
arr[1] = 43.; arr[1] = 43.;
arr[2] = 44.; arr[2] = 44.;
arr.write(); arr.write();
tf.stepApplication(); tf.stepApplication();
BOOST_CHECK_EQUAL(tf.readArray<T>("out")[0], 42.0); BOOST_CHECK_EQUAL(tf.readArray<T>("Dummy/out")[0], 42.0);
BOOST_CHECK_EQUAL(tf.readArray<T>("out")[1], 43.0); BOOST_CHECK_EQUAL(tf.readArray<T>("Dummy/out")[1], 43.0);
BOOST_CHECK_EQUAL(tf.readArray<T>("out")[2], 44.0); BOOST_CHECK_EQUAL(tf.readArray<T>("Dummy/out")[2], 44.0);
std::vector<T> v_ref(20); std::vector<T> v_ref(20);
for(size_t i = 0; i < 3; i++) { for(size_t i = 0; i < 3; i++) {
v_ref.back() = 42.0 + i; v_ref.back() = 42.0 + i;
...@@ -201,16 +193,16 @@ BOOST_AUTO_TEST_CASE(testArrayHistoryString) { ...@@ -201,16 +193,16 @@ BOOST_AUTO_TEST_CASE(testArrayHistoryString) {
std::cout << "testArrayHistoryString" << std::endl; std::cout << "testArrayHistoryString" << std::endl;
testAppArray<std::string> app; testAppArray<std::string> app;
ChimeraTK::TestFacility tf; ChimeraTK::TestFacility tf;
auto arr = tf.getArray<std::string>("in"); auto arr = tf.getArray<std::string>("Dummy/in");
tf.runApplication(); tf.runApplication();
arr[0] = "42"; arr[0] = "42";
arr[1] = "43"; arr[1] = "43";
arr[2] = "44"; arr[2] = "44";
arr.write(); arr.write();
tf.stepApplication(); tf.stepApplication();
BOOST_CHECK_EQUAL(tf.readArray<std::string>("out")[0], "42"); BOOST_CHECK_EQUAL(tf.readArray<std::string>("Dummy/out")[0], "42");
BOOST_CHECK_EQUAL(tf.readArray<std::string>("out")[1], "43"); BOOST_CHECK_EQUAL(tf.readArray<std::string>("Dummy/out")[1], "43");
BOOST_CHECK_EQUAL(tf.readArray<std::string>("out")[2], "44"); BOOST_CHECK_EQUAL(tf.readArray<std::string>("Dummy/out")[2], "44");
std::vector<std::string> v_ref(20); std::vector<std::string> v_ref(20);
for(size_t i = 0; i < 3; i++) { for(size_t i = 0; i < 3; i++) {
v_ref.back() = std::to_string(42 + i); v_ref.back() = std::to_string(42 + i);
...@@ -243,7 +235,7 @@ BOOST_AUTO_TEST_CASE(testDeviceHistory) { ...@@ -243,7 +235,7 @@ BOOST_AUTO_TEST_CASE(testDeviceHistory) {
dev.write("/FixedPoint/value", 42); dev.write("/FixedPoint/value", 42);
// Trigger the reading of the device // Trigger the reading of the device
auto i = tf.getScalar<int>("in"); auto i = tf.getScalar<int>("Dummy/in");
BOOST_CHECK(true); BOOST_CHECK(true);
tf.runApplication(); tf.runApplication();
i = 1.; i = 1.;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment