"git@gitlab.desy.de:joao.alvim/asapo.git" did not exist on "a8a8f452317d2bfafa23865fe090b3f8e06b34a4"
Newer
Older
/*
* TestFacility.h
*
* Created on: Feb 17, 2017
* Author: Martin Hierholzer
*/
#ifndef CHIMERATK_TEST_FACILITY
#define CHIMERATK_TEST_FACILITY
Martin Christoph Hierholzer
committed
#include <boost/fusion/include/at_key.hpp>
#include <ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h>
#include <ChimeraTK/OneDRegisterAccessor.h>
#include <ChimeraTK/ScalarRegisterAccessor.h>
#include "Application.h"
Martin Christoph Hierholzer
committed
#include "TestableModeAccessorDecorator.h"
namespace ChimeraTK {
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/** Helper class to facilitate tests of applications based on ApplicationCore */
class TestFacility {
public:
/** The constructor will internally obtain the instance of the application, so
* the instance of the TestFacility must not be created before the application
* (i.e. usually not before the main() routine). The application will
* automatically be put into the testable mode and initialised. */
TestFacility() {
auto pvManagers = createPVManager();
pvManager = pvManagers.first;
Application::getInstance().setPVManager(pvManagers.second);
Application::getInstance().enableTestableMode();
Application::getInstance().initialise();
}
/** Start the application. This simply calls Application::run(). Since the
* application is in testable mode, it
* will be ??? TODO define precisely what happens on start up */
void runApplication() const {
Application::getInstance().run();
Application::registerThread("TestThread");
}
/** Perform a "step" of the application. This runs the application until all
* input provided to it has been processed and all application modules wait
* for new data in blocking read calls. This function returns only after the
* application has reached that stated and was paused again. After returning
* from this function, the result can be checked and new data can be provided
* to the application. The new data will not be
* processed until the next call to step(). */
void stepApplication() const { Application::getInstance().stepApplication(); }
/** Obtain a scalar process variable from the application, which is published
* to the control system. */
template <typename T>
ChimeraTK::ScalarRegisterAccessor<T>
getScalar(const ChimeraTK::RegisterPath &name) const {
// check for existing accessor in cache
if (boost::fusion::at_key<T>(scalarMap.table).count(name) > 0) {
return boost::fusion::at_key<T>(scalarMap.table)[name];
}
// obtain accessor from ControlSystemPVManager
auto pv = pvManager->getProcessArray<T>(name);
if (pv == nullptr) {
throw ChimeraTK::logic_error("Process variable '" + name +
"' does not exist.");
}
// obtain variable id from pvIdMap and transfer it to idMap (required by the
// TestableModeAccessorDecorator)
size_t varId = Application::getInstance().pvIdMap[pv->getUniqueId()];
// decorate with TestableModeAccessorDecorator if variable is sender and
// receiver is not poll-type, and store it in cache
if (pv->isWriteable() &&
!Application::getInstance().testableMode_isPollMode[varId]) {
auto deco = boost::make_shared<TestableModeAccessorDecorator<T>>(
pv, false, true, varId, varId);
Application::getInstance().testableMode_names[varId] =
"ControlSystem:" + name;
boost::fusion::at_key<T>(scalarMap.table)[name].replace(
ChimeraTK::ScalarRegisterAccessor<T>(deco));
} else {
boost::fusion::at_key<T>(scalarMap.table)[name].replace(
ChimeraTK::ScalarRegisterAccessor<T>(pv));
}
// return the accessor as stored in the cache
return boost::fusion::at_key<T>(scalarMap.table)[name];
}
/** Obtain an array-type process variable from the application, which is
* published to the control system. */
template <typename T>
ChimeraTK::OneDRegisterAccessor<T>
getArray(const ChimeraTK::RegisterPath &name) const {
// check for existing accessor in cache
if (boost::fusion::at_key<T>(arrayMap.table).count(name) > 0) {
return boost::fusion::at_key<T>(arrayMap.table)[name];
}
// obtain accessor from ControlSystemPVManager
auto pv = pvManager->getProcessArray<T>(name);
if (pv == nullptr) {
throw ChimeraTK::logic_error("Process variable '" + name +
"' does not exist.");
}
// obtain variable id from pvIdMap and transfer it to idMap (required by the
// TestableModeAccessorDecorator)
size_t varId = Application::getInstance().pvIdMap[pv->getUniqueId()];
// decorate with TestableModeAccessorDecorator if variable is sender and
// receiver is not poll-type, and store it in cache
if (pv->isWriteable() &&
!Application::getInstance().testableMode_isPollMode[varId]) {
auto deco = boost::make_shared<TestableModeAccessorDecorator<T>>(
pv, false, true, varId, varId);
Application::getInstance().testableMode_names[varId] =
"ControlSystem:" + name;
boost::fusion::at_key<T>(arrayMap.table)[name].replace(
ChimeraTK::OneDRegisterAccessor<T>(deco));
} else {
boost::fusion::at_key<T>(arrayMap.table)[name].replace(
ChimeraTK::OneDRegisterAccessor<T>(pv));
}
// return the accessor as stored in the cache
return boost::fusion::at_key<T>(arrayMap.table)[name];
}
/** Convenience function to write a scalar process variable in a single call
*/
template <typename TYPE>
void writeScalar(const std::string &name, const TYPE value) {
auto acc = getScalar<TYPE>(name);
acc = value;
acc.write();
}
/** Convenience function to write an array process variable in a single call
*/
template <typename TYPE>
void writeArray(const std::string &name, const std::vector<TYPE> &value) {
auto acc = getArray<TYPE>(name);
acc = value;
acc.write();
}
/** Convenience function to read the latest value of a scalar process variable
* in a single call */
template <typename TYPE> TYPE readScalar(const std::string &name) {
auto acc = getScalar<TYPE>(name);
acc.readLatest();
return acc;
}
/** Convenience function to read the latest value of an array process variable
* in a single call */
template <typename TYPE>
std::vector<TYPE> readArray(const std::string &name) {
auto acc = getArray<TYPE>(name);
acc.readLatest();
return acc;
}
protected:
boost::shared_ptr<ControlSystemPVManager> pvManager;
// Cache (possible decorated) accessors to avoid the need to create accessors
// multiple times. This would not work if the accessor is decorated, since the
// buffer would be lost and thus the current value could no longer be
// obtained. This has to be done separately for scalar and array accessors and
// in dependence of the user type. Since this is a cache and does not change
// the logical behaviour of the class, the maps are defined mutable.
template <typename UserType>
using ScalarMap =
std::map<std::string, ChimeraTK::ScalarRegisterAccessor<UserType>>;
mutable ChimeraTK::TemplateUserTypeMap<ScalarMap> scalarMap;
template <typename UserType>
using ArrayMap =
std::map<std::string, ChimeraTK::OneDRegisterAccessor<UserType>>;
mutable ChimeraTK::TemplateUserTypeMap<ArrayMap> arrayMap;
};
} /* namespace ChimeraTK */
#endif /* CHIMERATK_TEST_FACILITY */