ChimeraTK-ApplicationCore 04.06.00
Loading...
Searching...
No Matches
TestFacility.h
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#pragma once
4
5#include "Application.h"
6
7#include <ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h>
8#include <ChimeraTK/OneDRegisterAccessor.h>
9#include <ChimeraTK/ScalarRegisterAccessor.h>
10#include <ChimeraTK/VariantUserTypes.h>
11#include <ChimeraTK/VoidRegisterAccessor.h>
12
13#include <boost/fusion/include/at_key.hpp>
14
15namespace ChimeraTK {
16
17 /********************************************************************************************************************/
18
19 namespace detail {
20
21 template<typename T>
22 struct BoolTypeHelper {
23 using type = T;
24 };
25
26 template<>
27 struct BoolTypeHelper<bool> {
28 using type = ChimeraTK::Boolean;
29 };
30
31 } // namespace detail
32
33 /********************************************************************************************************************/
34
37 public:
40 explicit TestFacility(Application& app, bool enableTestableMode = true);
41
43 void runApplication() const;
44
48 bool canStepApplication() const;
49
57 void stepApplication(bool waitForDeviceInitialisation = true) const;
58
61 ChimeraTK::VoidRegisterAccessor getVoid(const ChimeraTK::RegisterPath& name) const;
62
65 template<typename T>
66 ChimeraTK::ScalarRegisterAccessor<T> getScalar(const ChimeraTK::RegisterPath& name) const;
67
70 template<typename T>
71 ChimeraTK::OneDRegisterAccessor<T> getArray(const ChimeraTK::RegisterPath& name) const;
72
75 template<typename TYPE>
76 void writeScalar(const std::string& name, TYPE value);
77
80 template<typename TYPE>
81 void writeArray(const std::string& name, const std::vector<TYPE>& value);
82
85 template<typename TYPE>
86 TYPE readScalar(const std::string& name);
87
90 template<typename TYPE>
91 std::vector<TYPE> readArray(const std::string& name);
92
94 template<typename T>
95 void setScalarDefault(const ChimeraTK::RegisterPath& name, const T& value);
96
98 template<typename T>
99 void setArrayDefault(const ChimeraTK::RegisterPath& name, const std::vector<T>& value);
100
103 template<typename T>
104 boost::shared_ptr<ChimeraTK::NDRegisterAccessor<T>> getAccessor(const ChimeraTK::RegisterPath& name) const;
105
107 [[nodiscard]] boost::shared_ptr<ControlSystemPVManager> getPvManager() const { return _pvManager; }
108
112 template<typename T>
113 static void setConfigScalar(const ChimeraTK::RegisterPath& name, const T& value);
114
118 template<typename T>
119 static void setConfigArray(const ChimeraTK::RegisterPath& name, const std::vector<T>& value);
120
121 protected:
122 boost::shared_ptr<ControlSystemPVManager> _pvManager;
123
124 // Cache (possible decorated) accessors to avoid the need to create accessors multiple times. This would not work
125 // if the accessor is decorated, since the buffer would be lost and thus the current value could no longer be
126 // obtained. This has to be done in dependence of the user type. Since this is a cache and does not change the
127 // logical behaviour of the class, the maps are defined mutable.
128 template<typename UserType>
129 using AccessorMap = std::map<std::string, boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>>>;
130 mutable ChimeraTK::TemplateUserTypeMap<AccessorMap> _accessorMap;
131
132 // default values for process variables
133 template<typename UserType>
134 using Defaults = std::map<std::string, std::vector<UserType>>;
135 ChimeraTK::TemplateUserTypeMap<Defaults> _defaults;
136
138
139 // static storage of values set via setConfigScalar
140 static std::map<ChimeraTK::RegisterPath, ChimeraTK::UserTypeVariantNoVoid> _configScalars;
141
142 template<typename UserType>
143 using Vector = std::vector<UserType>;
144
145 // static storage of values set via setConfigArray
146 static std::map<ChimeraTK::RegisterPath, ChimeraTK::UserTypeTemplateVariantNoVoid<Vector>> _configArrays;
147
148 friend class ConfigReader;
149 };
150
151 /********************************************************************************************************************/
152 /********************************************************************************************************************/
153
154 template<typename T>
155 ChimeraTK::ScalarRegisterAccessor<T> TestFacility::getScalar(const ChimeraTK::RegisterPath& name) const {
156 return getAccessor<T>(name);
157 }
158
159 /********************************************************************************************************************/
160
161 template<typename T>
162 ChimeraTK::OneDRegisterAccessor<T> TestFacility::getArray(const ChimeraTK::RegisterPath& name) const {
163 return getAccessor<T>(name);
164 }
165
166 /********************************************************************************************************************/
167
168 template<typename TYPE>
169 void TestFacility::writeScalar(const std::string& name, const TYPE value) {
170 auto acc = getScalar<typename detail::BoolTypeHelper<TYPE>::type>(name);
171 acc = value;
172 acc.write();
173 }
174
175 /********************************************************************************************************************/
176
177 template<typename TYPE>
178 void TestFacility::writeArray(const std::string& name, const std::vector<TYPE>& value) {
179 auto acc = getArray<typename detail::BoolTypeHelper<TYPE>::type>(name);
180 if constexpr(!std::is_same<TYPE, bool>::value) {
181 acc = value;
182 }
183 else {
184 assert(value.size() == acc.getNElements());
185 std::transform(value.begin(), value.end(), acc.begin(), [](const bool& v) -> ChimeraTK::Boolean { return v; });
186 }
187 acc.write();
188 }
189
190 /********************************************************************************************************************/
191
192 template<typename TYPE>
193 TYPE TestFacility::readScalar(const std::string& name) {
194 auto acc = getScalar<typename detail::BoolTypeHelper<TYPE>::type>(name);
195 acc.readLatest();
196 return acc;
197 }
198
199 /********************************************************************************************************************/
200
201 template<typename TYPE>
202 std::vector<TYPE> TestFacility::readArray(const std::string& name) {
203 auto acc = getArray<typename detail::BoolTypeHelper<TYPE>::type>(name);
204 acc.readLatest();
205 return acc;
206 }
207
208 /********************************************************************************************************************/
209
210 template<typename T>
211 void TestFacility::setScalarDefault(const ChimeraTK::RegisterPath& name, const T& value) {
213 throw ChimeraTK::logic_error("TestFacility::setScalarDefault() called after runApplication().");
214 }
215 std::vector<T> vv;
216 vv.push_back(value);
217 setArrayDefault(name, vv);
218 }
219
220 /********************************************************************************************************************/
221
222 template<typename T>
223 void TestFacility::setArrayDefault(const ChimeraTK::RegisterPath& name, const std::vector<T>& value) {
225 throw ChimeraTK::logic_error("TestFacility::setArrayDefault() called after runApplication().");
226 }
227 // check if PV exists
228 if(!_pvManager->hasProcessVariable(name)) {
229 throw ChimeraTK::logic_error("Process variable '" + name + "' does not exist.");
230 }
231
232 // check if the type is right
233 auto pv = _pvManager->getProcessArray<typename detail::BoolTypeHelper<T>::type>(name);
234 if(pv == nullptr) {
235 auto pvUntyped = _pvManager->getProcessVariable(name);
236 throw ChimeraTK::logic_error("Process variable '" + name + "' requested by the wrong type: " + typeid(T).name() +
237 " != " + pvUntyped->getValueType().name());
238 }
239
240 // store default value in map
241 auto& tv = boost::fusion::at_key<typename detail::BoolTypeHelper<T>::type>(_defaults.table)[name];
242 if constexpr(!std::is_same<T, bool>::value) {
243 tv = value;
244 }
245 else {
246 tv.resize(value.size());
247 std::transform(value.begin(), value.end(), tv.begin(), [](const bool& v) -> ChimeraTK::Boolean { return v; });
248 }
249 }
250
251 /********************************************************************************************************************/
252
253 template<typename T>
254 boost::shared_ptr<ChimeraTK::NDRegisterAccessor<T>> TestFacility::getAccessor(
255 const ChimeraTK::RegisterPath& name) const {
256 // check for existing accessor in cache
257 if(boost::fusion::at_key<T>(_accessorMap.table).count(name) > 0) {
258 return boost::fusion::at_key<T>(_accessorMap.table)[name];
259 }
260
261 // obtain accessor from ControlSystemPVManager
262 auto pv = _pvManager->getProcessArray<T>(name);
263 if(pv == nullptr) {
264 throw ChimeraTK::logic_error("Process variable '" + name + "' does not exist.");
265 }
266
267 // decorate with TestableModeAccessorDecorator if variable is sender and
268 // receiver is not poll-type (then no entry in pvIdMapp exists), and store it in cache
269 if(auto it = _app._pvIdMap.find(pv->getUniqueId()); pv->isWriteable() && it != _app._pvIdMap.end()) {
270 boost::fusion::at_key<T>(_accessorMap.table)[name] = _app.getTestableMode().decorate<T>(
271 pv, detail::TestableMode::DecoratorType::WRITE, "ControlSystem:" + name, it->second);
272 }
273 else {
274 boost::fusion::at_key<T>(_accessorMap.table)[name] = pv;
275 }
276
277 // return the accessor as stored in the cache
278 return boost::fusion::at_key<T>(_accessorMap.table)[name];
279 }
280
281 /********************************************************************************************************************/
282
283 template<typename T>
284 void TestFacility::setConfigScalar(const ChimeraTK::RegisterPath& name, const T& value) {
285 _configScalars[name] = value;
286 }
287
288 /********************************************************************************************************************/
289
290 template<typename T>
291 void TestFacility::setConfigArray(const ChimeraTK::RegisterPath& name, const std::vector<T>& value) {
292 _configArrays[name] = value;
293 }
294
295 /********************************************************************************************************************/
296
297} /* namespace ChimeraTK */
bool _testFacilityRunApplicationCalled
Flag which is set by the TestFacility in runApplication() at the beginning.
detail::TestableMode & getTestableMode()
Get the TestableMode control object of this application.
std::map< size_t, size_t > _pvIdMap
Map from ProcessArray uniqueId to the variable ID for control system variables.
Generic module to read an XML config file and provide the defined values as constant variables.
Helper class to facilitate tests of applications based on ApplicationCore.
ChimeraTK::OneDRegisterAccessor< T > getArray(const ChimeraTK::RegisterPath &name) const
Obtain an array-type process variable from the application, which is published to the control system.
static void setConfigScalar(const ChimeraTK::RegisterPath &name, const T &value)
Set ConfigReader scalar variable for the next instantiated Applicaton.
boost::shared_ptr< ChimeraTK::NDRegisterAccessor< T > > getAccessor(const ChimeraTK::RegisterPath &name) const
Function to obtain a process variable from the control system.
ChimeraTK::TemplateUserTypeMap< AccessorMap > _accessorMap
ChimeraTK::TemplateUserTypeMap< Defaults > _defaults
std::map< std::string, std::vector< UserType > > Defaults
TYPE readScalar(const std::string &name)
Convenience function to read the latest value of a scalar process variable in a single call.
bool canStepApplication() const
Check whether data has been sent to the application so stepApplication() can be called.
void writeArray(const std::string &name, const std::vector< TYPE > &value)
Convenience function to write an array process variable in a single call.
static std::map< ChimeraTK::RegisterPath, ChimeraTK::UserTypeTemplateVariantNoVoid< Vector > > _configArrays
boost::shared_ptr< ControlSystemPVManager > getPvManager() const
void writeScalar(const std::string &name, TYPE value)
Convenience function to write a scalar process variable in a single call.
void stepApplication(bool waitForDeviceInitialisation=true) const
Perform a "step" of the application.
ChimeraTK::VoidRegisterAccessor getVoid(const ChimeraTK::RegisterPath &name) const
Obtain a void process variable from the application, which is published to the control system.
static std::map< ChimeraTK::RegisterPath, ChimeraTK::UserTypeVariantNoVoid > _configScalars
boost::shared_ptr< ControlSystemPVManager > _pvManager
ChimeraTK::ScalarRegisterAccessor< T > getScalar(const ChimeraTK::RegisterPath &name) const
Obtain a scalar process variable from the application, which is published to the control system.
std::vector< TYPE > readArray(const std::string &name)
Convenience function to read the latest value of an array process variable in a single call.
void runApplication() const
Start the application in testable mode.
void setArrayDefault(const ChimeraTK::RegisterPath &name, const std::vector< T > &value)
Set default value for array process variable.
static void setConfigArray(const ChimeraTK::RegisterPath &name, const std::vector< T > &value)
Set ConfigReader array variable for the next instantiated Applicaton.
void setScalarDefault(const ChimeraTK::RegisterPath &name, const T &value)
Set default value for scalar process variable.
std::map< std::string, boost::shared_ptr< ChimeraTK::NDRegisterAccessor< UserType > > > AccessorMap
std::vector< UserType > Vector
InvalidityTracer application module.