Newer
Older
Martin Christoph Hierholzer
committed
/*
* Application.h
*
* Created on: Jun 07, 2016
* Author: Martin Hierholzer
*/
#ifndef CHIMERATK_APPLICATION_H
#define CHIMERATK_APPLICATION_H
#include <mutex>
Martin Christoph Hierholzer
committed
#include <mtca4u/DeviceBackend.h>
Martin Christoph Hierholzer
committed
#include <ChimeraTK/ControlSystemAdapter/ApplicationBase.h>
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
#include "ApplicationException.h"
#include "VariableNetwork.h"
#include "Flags.h"
#include "InternalModule.h"
Martin Christoph Hierholzer
committed
#include "EntityOwner.h"
#include "TriggerFanOut.h"
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
namespace ChimeraTK {
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
class Module;
Martin Christoph Hierholzer
committed
class AccessorBase;
Martin Christoph Hierholzer
committed
class VariableNetwork;
Martin Christoph Hierholzer
committed
template<typename UserType>
class Accessor;
Martin Christoph Hierholzer
committed
template<typename UserType>
class TestDecoratorRegisterAccessor;
Martin Christoph Hierholzer
committed
class Application : public ApplicationBase, public EntityOwner {
Martin Christoph Hierholzer
committed
public:
Martin Christoph Hierholzer
committed
Application(const std::string& name)
Martin Christoph Hierholzer
committed
: ApplicationBase(name), EntityOwner(nullptr, name) {}
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
~Application() {}
Martin Christoph Hierholzer
committed
/** This will remove the global pointer to the instance and allows creating another instance
Martin Christoph Hierholzer
committed
* afterwards. This is mostly useful for writing tests, as it allows to run several applications sequentially
* in the same executable. Note that any ApplicationModules etc. owned by this Application are no longer
* valid after destroying the Application and must be destroyed as well (or at least no longer used). */
Martin Christoph Hierholzer
committed
void shutdown();
Martin Christoph Hierholzer
committed
/** Define the connections between process variables. Must be implemented by the application developer. */
virtual void defineConnections() = 0;
void initialise();
Martin Christoph Hierholzer
committed
void run();
Martin Christoph Hierholzer
committed
/** Check if all connections are valid. */
void checkConnections();
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
/** Instead of running the application, just initialise it and output the published variables to an XML file. */
void generateXML();
Martin Christoph Hierholzer
committed
/** Output the connections requested in the initialise() function to std::cout. This may be done also before
* makeConnections() has been called. */
void dumpConnections();
Martin Christoph Hierholzer
committed
/** Obtain instance of the application. Will throw an exception if called before the instance has been
* created by the control system adapter, or if the instance is not based on the Application class. */
static Application& getInstance();
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
/** Enable the testable mode. This allows to pause and resume the application for testing purposes using the
* functions pauseApplication() and resumeApplication(). The application will start in paused state.
*
* This function must be called before the application is initialised (i.e. before the call to initialise()).
*
* Note: Enabling the testable mode will have a singificant impact on the performance, since it will prevent
* any module threads to run at the same time! */
Martin Christoph Hierholzer
committed
void enableTestableMode() { testableMode = true; }
Martin Christoph Hierholzer
committed
/** Lock the mutex for the testable mode, which prevents any application thread from running. This works only if
* enableTestableMode() was called before. */
void pauseApplication() { assert(testableMode); testableMode_lock.lock(); }
/** Unlock the mutex for the testable mode, which allows the application threads to run again. This works only if
* enableTestableMode() was called before. */
void resumeApplication() { assert(testableMode); testableMode_lock.unlock(); }
/** Resume the application until all application threads are stuck in a blocking read operation. Must be called
* while the application is paused. */
void stepApplication() {
while(true) {
testableMode_counter = 0;
resumeApplication();
usleep(100);
pauseApplication();
if(testableMode_counter == 0) break;
}
}
Martin Christoph Hierholzer
committed
protected:
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
friend class Module;
Martin Christoph Hierholzer
committed
friend class VariableNetwork;
Martin Christoph Hierholzer
committed
friend class VariableNetworkNode;
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
template<typename UserType>
friend class Accessor;
Martin Christoph Hierholzer
committed
/** Register the connections to constants for previously unconnected nodes. */
void processUnconnectedNodes();
Martin Christoph Hierholzer
committed
/** Make the connections between accessors as requested in the initialise() function. */
void makeConnections();
Martin Christoph Hierholzer
committed
/** Make the connections for a single network */
void makeConnectionsForNetwork(VariableNetwork &network);
/** UserType-dependent part of makeConnectionsForNetwork() */
Martin Christoph Hierholzer
committed
template<typename UserType>
void typedMakeConnection(VariableNetwork &network);
Martin Christoph Hierholzer
committed
/** Register a connection between two VariableNetworkNode */
Martin Christoph Hierholzer
committed
VariableNetwork& connect(VariableNetworkNode a, VariableNetworkNode b);
Martin Christoph Hierholzer
committed
/** Perform the actual connection of an accessor to a device register */
Martin Christoph Hierholzer
committed
template<typename UserType>
boost::shared_ptr<mtca4u::NDRegisterAccessor<UserType>> createDeviceVariable(const std::string &deviceAlias,
Martin Christoph Hierholzer
committed
const std::string ®isterName, VariableDirection direction, UpdateMode mode, size_t nElements);
Martin Christoph Hierholzer
committed
/** Create a process variable with the PVManager, which is exported to the control system adapter. nElements will
be the array size of the created variable. */
Martin Christoph Hierholzer
committed
template<typename UserType>
Martin Christoph Hierholzer
committed
boost::shared_ptr<mtca4u::NDRegisterAccessor<UserType>> createProcessVariable(VariableNetworkNode const &node);
Martin Christoph Hierholzer
committed
/** Create a local process variable which is not exported. The first element in the returned pair will be the
* sender, the second the receiver. nElements will be the array size of the created variable. */
Martin Christoph Hierholzer
committed
template<typename UserType>
std::pair< boost::shared_ptr<mtca4u::NDRegisterAccessor<UserType>>, boost::shared_ptr<mtca4u::NDRegisterAccessor<UserType>> >
createApplicationVariable(size_t nElements, const std::string &name);
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
/** Register an application module with the application. Will be called automatically by all modules in their
* constructors. */
Martin Christoph Hierholzer
committed
void overallRegisterModule(Module &module) {
overallModuleList.push_back(&module);
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
/** List of application modules */
Martin Christoph Hierholzer
committed
std::list<Module*> overallModuleList; /// @todo TODO FIXME maybe recursing through all modules is better than having an additional overall list?
Martin Christoph Hierholzer
committed
/** List of InternalModules */
std::list<boost::shared_ptr<InternalModule>> internalModuleList;
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
/** List of variable networks */
std::list<VariableNetwork> networkList;
Martin Christoph Hierholzer
committed
/** List of constant variable nodes */
std::list<VariableNetworkNode> constantList;
Martin Christoph Hierholzer
committed
/** Map of trigger sources to their corresponding TriggerFanOuts. Note: the key is the unique ID of the
* triggering node. */
std::map<const void*, boost::shared_ptr<TriggerFanOut>> triggerMap;
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
/** Create a new, empty network */
VariableNetwork& createNetwork();
Martin Christoph Hierholzer
committed
/** Instance of VariableNetwork to indicate an invalid network */
VariableNetwork invalidNetwork;
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
/** Map of DeviceBackends used by this application. The map key is the alias name from the DMAP file */
std::map<std::string, boost::shared_ptr<mtca4u::DeviceBackend>> deviceMap;
Martin Christoph Hierholzer
committed
/** Flag if connections should be made in testable mode (i.e. the TestDecoratorRegisterAccessor is put around all
* push-type input accessors etc.). */
bool testableMode{false};
Martin Christoph Hierholzer
committed
/** Mutex used in testable mode to take control over the application threads. */
std::mutex testableMode_mutex;
/** The lock used to lock the testableMode_mutex. */
std::unique_lock<std::mutex> testableMode_lock{testableMode_mutex};
/** Counter used in testable mode to check if application code was executed after releasing the testableMode_mutex.
* This value may only be accessed while holding the testableMode_mutex. */
size_t testableMode_counter{0};
template<typename UserType>
friend class TestDecoratorRegisterAccessor; // needs access to the testableMode_mutex and testableMode_counter
template<typename UserType>
friend class TestDecoratorTransferFuture; // needs access to the testableMode_mutex and testableMode_counter
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
};
} /* namespace ChimeraTK */
#endif /* CHIMERATK_APPLICATION_H */