Skip to content
Snippets Groups Projects
Commit 63e580e1 authored by Martin Christoph Hierholzer's avatar Martin Christoph Hierholzer
Browse files

doc: modernise example(s)

parent ffa2299a
No related branches found
No related tags found
No related merge requests found
Showing
with 310 additions and 275 deletions
......@@ -11,7 +11,7 @@ include(cmake/set_version_numbers.cmake)
option(BUILD_TESTS "Build tests." ON)
# Find the ControlSystemAdapter
FIND_PACKAGE(ChimeraTK-ControlSystemAdapter 02.06 REQUIRED)
FIND_PACKAGE(ChimeraTK-ControlSystemAdapter 02.07 REQUIRED)
# Find the DeviceAccess
FIND_PACKAGE(ChimeraTK-DeviceAccess 03.11 REQUIRED)
......
../.clang-format
\ No newline at end of file
# Note: This CMakeLists.txt is a minimal complete example how to use the
# *installed* application core library. It is not included from the main CMakeLists.txt
# which is used to build ApplicationCore itself.
PROJECT(demo_example)
project(demo_example)
CMAKE_MINIMUM_REQUIRED(VERSION 3.16)
cmake_minimum_required(VERSION 3.16)
# Use the project template to get the settings required for an application core project
set(${PROJECT_NAME}_MAJOR_VERSION 01)
......@@ -17,30 +17,39 @@ include(cmake/set_default_flags.cmake)
# Add the dependencies. We need ApplicationCore and a control system adapter implementation.
find_package(ChimeraTK-ApplicationCore 03.00 REQUIRED)
#FIXME: Make the adapter configurable via command line parameter
find_package(ChimeraTK-ControlSystemAdapter-DoocsAdapter 00.10 REQUIRED)
AUX_SOURCE_DIRECTORY(${CMAKE_SOURCE_DIR} demo_sources)
# Choose control system adapater implementation to link against based on the ADAPTER cmake argument. When not
# specifying this at the cmake command line, a list of possible options will be presented.
include(cmake/set_control_system_adapter.cmake)
# The server proper. It depends on application core and the control system adapter implementation.
add_executable(${PROJECT_NAME} ${demo_sources})
target_link_libraries(${PROJECT_NAME}
PRIVATE ChimeraTK::ChimeraTK-ApplicationCore ChimeraTK::ChimeraTK-ControlSystemAdapter-DoocsAdapter )
aux_source_directory(${CMAKE_SOURCE_DIR}/src sources)
aux_source_directory(${CMAKE_SOURCE_DIR}/include headers)
aux_source_directory(${CMAKE_SOURCE_DIR}/src_factory factory)
# We compile the same sources with the GENERATE_XML flag to get an xml generator.
# This one does not depent on a control system adapter implementation.
add_executable(${PROJECT_NAME}-xmlGenerator ${demo_sources})
include_directories(${CMAKE_SOURCE_DIR}/include)
# Server library links all ApplicationModules and ApplicationCore
add_library(${PROJECT_NAME}lib ${sources} ${headers})
target_link_libraries(${PROJECT_NAME}lib ChimeraTK::ChimeraTK-ApplicationCore)
# Server executable: link the server library with the application factory and the chose control system adapter
add_executable(${PROJECT_NAME} ${factory})
target_link_libraries(${PROJECT_NAME} PRIVATE ${PROJECT_NAME}lib ChimeraTK::SelectedAdapter)
# XML generator executable: generate XML application description. Links the server library with the application
# factory, but sets a special C++ preprocessor symbol to put ApplicationCore into the XML generation mode.
add_executable(${PROJECT_NAME}-xmlGenerator ${factory})
target_compile_options(${PROJECT_NAME}-xmlGenerator PRIVATE "-DGENERATE_XML")
target_link_libraries(${PROJECT_NAME}-xmlGenerator PRIVATE ChimeraTK::ChimeraTK-ApplicationCore)
target_link_libraries(${PROJECT_NAME}-xmlGenerator PRIVATE ${PROJECT_NAME}lib)
# copy the (test) config files to the build directory for tests
FILE( COPY config/ DESTINATION ${PROJECT_BINARY_DIR})
file(COPY config/ DESTINATION ${PROJECT_BINARY_DIR})
# Tests:
# There are no dedicated tests for this demo. But we run the xml generator to
# check that the variable household can successfully be initialised.
# The test will fail if the xml generator crashes, just a smoke test.
ENABLE_TESTING()
enable_testing()
add_test(${PROJECT_NAME}-xmlGenerator ${PROJECT_NAME}-xmlGenerator)
# Installation:
......@@ -50,5 +59,6 @@ if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/export/doocs/server")
else()
install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}-xmlGenerator RUNTIME DESTINATION bin)
endif()
# Do not install the config. It is only a test config.
# The real config will come from a config generator and usually depends on the instance.
// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
// SPDX-License-Identifier: LGPL-3.0-or-later
#include <ChimeraTK/BackendFactory.h>
#include <ChimeraTK/DeviceAccessVersion.h>
#include <ChimeraTK/DummyBackend.h>
class DemoDummy : public ChimeraTK::DummyBackend {
public:
DemoDummy(std::string mapFileName) : DummyBackend(mapFileName) {}
static boost::shared_ptr<DeviceBackend> createInstance(
std::string /* address */, std::map<std::string, std::string> parameters) {
return boost::shared_ptr<DeviceBackend>(new DemoDummy(parameters["map"]));
}
void read(uint64_t bar, uint64_t address, int32_t* data, size_t sizeInBytes) override {
// if probeSignal register is read, fill it first
if(bar == 2) {
assert(address == 0);
assert(sizeInBytes == 65536);
// build average of feed forward and setpoint tables
for(int i = 0; i < 65536; ++i) {
_barContents[2][i] = (_barContents[0][i] + _barContents[1][i]) / 2;
}
}
// perform the original read
DummyBackend::read(bar, address, data, sizeInBytes);
}
/** Class to register the backend type with the factory. */
class BackendRegisterer {
public:
BackendRegisterer();
};
static BackendRegisterer backendRegisterer;
};
DemoDummy::BackendRegisterer DemoDummy::backendRegisterer;
DemoDummy::BackendRegisterer::BackendRegisterer() {
std::cout << "DemoDummy::BackendRegisterer: registering backend type DemoDummy" << std::endl;
ChimeraTK::BackendFactory::getInstance().registerBackendType("DemoDummy", &DemoDummy::createInstance);
}
../HowTo_project-template.md
\ No newline at end of file
// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
// SPDX-License-Identifier: LGPL-3.0-or-later
#include <ChimeraTK/BackendFactory.h>
#include <ChimeraTK/BackendRegisterCatalogue.h>
#include <ChimeraTK/DeviceAccessVersion.h>
#include <ChimeraTK/DeviceBackendImpl.h>
#include <ChimeraTK/LNMBackendRegisterInfo.h>
#include <ChimeraTK/NDRegisterAccessor.h>
template<typename UserType>
class TimerDummyRegisterAccessor;
namespace ctk = ChimeraTK;
class TimerDummy : public ChimeraTK::DeviceBackendImpl {
public:
TimerDummy() : DeviceBackendImpl() { FILL_VIRTUAL_FUNCTION_TEMPLATE_VTABLE(getRegisterAccessor_impl); }
static boost::shared_ptr<DeviceBackend> createInstance(
std::string /* address */, std::map<std::string, std::string> /* parameters */) {
return boost::shared_ptr<DeviceBackend>(new TimerDummy());
}
template<typename UserType>
boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>> getRegisterAccessor_impl(
const ChimeraTK::RegisterPath& registerPathName, size_t, size_t, ChimeraTK::AccessModeFlags flags);
DEFINE_VIRTUAL_FUNCTION_TEMPLATE_VTABLE_FILLER(TimerDummy, getRegisterAccessor_impl, 4);
void open() override {}
void close() override {}
bool isFunctional() const override { return true; }
void setException() override {}
std::string readDeviceInfo() override { return std::string("Dummy timing device "); }
// return just an empty Catalogue for interface compability
ctk::RegisterCatalogue getRegisterCatalogue() const override {
return ctk::RegisterCatalogue(std::make_unique<ctk::BackendRegisterCatalogue<ctk::LNMBackendRegisterInfo>>());
}
/** Class to register the backend type with the factory. */
class BackendRegisterer {
public:
BackendRegisterer();
};
static BackendRegisterer backendRegisterer;
};
TimerDummy::BackendRegisterer TimerDummy::backendRegisterer;
TimerDummy::BackendRegisterer::BackendRegisterer() {
std::cout << "TimerDummy::BackendRegisterer: registering backend type TimerDummy" << std::endl;
ChimeraTK::BackendFactory::getInstance().registerBackendType("TimerDummy", &TimerDummy::createInstance);
}
template<typename UserType>
class TimerDummyRegisterAccessor : public ChimeraTK::NDRegisterAccessor<UserType> {
public:
TimerDummyRegisterAccessor(const ChimeraTK::RegisterPath& registerPathName)
: ChimeraTK::NDRegisterAccessor<UserType>(registerPathName, {ChimeraTK::AccessMode::wait_for_new_data}) {
ChimeraTK::NDRegisterAccessor<UserType>::buffer_2D.resize(1);
ChimeraTK::NDRegisterAccessor<UserType>::buffer_2D[0].resize(1);
ChimeraTK::NDRegisterAccessor<UserType>::buffer_2D[0][0] = UserType();
this->_readQueue = {3};
}
~TimerDummyRegisterAccessor() override {}
void doReadTransferSynchronously() override { usleep(1000000); }
void doPostRead(ChimeraTK::TransferType, bool hasNewData) override {
if(!hasNewData) return;
ChimeraTK::NDRegisterAccessor<UserType>::buffer_2D[0][0]++;
this->_versionNumber = {};
}
bool doWriteTransfer(ChimeraTK::VersionNumber) override { return false; }
bool isReadOnly() const override { return true; }
bool isReadable() const override { return true; }
bool isWriteable() const override { return false; }
bool mayReplaceOther(const boost::shared_ptr<ChimeraTK::TransferElement const>&) const override { return false; }
std::vector<boost::shared_ptr<ChimeraTK::TransferElement>> getHardwareAccessingElements() override {
return {this->shared_from_this()};
}
void replaceTransferElement(boost::shared_ptr<ChimeraTK::TransferElement>) override {}
std::list<boost::shared_ptr<ChimeraTK::TransferElement>> getInternalElements() override { return {}; }
private:
boost::thread _timerThread;
};
template<>
void TimerDummyRegisterAccessor<std::string>::doPostRead(ChimeraTK::TransferType, bool /*hasNewData*/) {
this->_versionNumber = {};
}
template<>
void TimerDummyRegisterAccessor<ChimeraTK::Boolean>::doPostRead(ChimeraTK::TransferType, bool /*hasNewData*/) {
this->_versionNumber = {};
}
template<>
void TimerDummyRegisterAccessor<ChimeraTK::Void>::doPostRead(ChimeraTK::TransferType, bool /*hasNewData*/) {
this->_versionNumber = {};
}
template<typename UserType>
boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>> TimerDummy::getRegisterAccessor_impl(
const ChimeraTK::RegisterPath& registerPathName, size_t, size_t, ChimeraTK::AccessModeFlags flags) {
assert(registerPathName == "/macropulseNr");
flags.checkForUnknownFlags({ChimeraTK::AccessMode::wait_for_new_data});
return boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>>(
new TimerDummyRegisterAccessor<UserType>(registerPathName));
}
# name nr of elements address size bar width fracbits signed
setpointTable 16384 0 65536 0 32 0 0
feedforwardTable 16384 0 65536 1 32 0 0
probeSignal 16384 0 65536 2 32 0 0
# name nr of elements address size bar width fracbits signed R/W
HEATER.MODE 1 0 4 0 2 0 0 RW
HEATER.CURRENT_SET 1 4 4 0 32 16 0 RW
HEATER.CURRENT_READBACK 1 8 4 0 32 16 0 RO
SENSORS.TEMPERATURE1 1 16 4 0 32 16 0 RO
SENSORS.TEMPERATURE2 1 20 4 0 32 16 0 RO
SENSORS.TEMPERATURE3 1 24 4 0 32 16 0 RO
SENSORS.TEMPERATURE4 1 28 4 0 32 16 0 RO
BOARD.RESET_N 1 32 4 0 1 0 0 RW
BOARD.GPIO_OUT0 1 36 4 0 1 0 0 RW
\ No newline at end of file
# name nr of elements address size bar width fracbits signed
macropulseNr 1 0 4 0 32 0 0
<?xml version="1.0" encoding="UTF-8"?>
<device_server xmlns="https://github.com/ChimeraTK/ControlSystemAdapter-DoocsAdapter">
<location name="Bakery">
<location name="DEMO">
<import>/</import>
</location>
</device_server>
<configuration>
<module name="Configuration">
<variable name="enableSetpointRamping" type="boolean" value="true"/>
<variable name="heaterMode" type="uint8" value="2"/>
</module>
<module name="Information">
<variable name="ovenName" type="string" value="Cookie Oven 42"/>
</module>
<module name="Timer">
<variable name="period" type="uint32" value="500"/>
</module>
</configuration>
\ No newline at end of file
oven_raw (sharedMemoryDummy?map=oven.map)
device (sharedMemoryDummy:0?map=DemoDummy.map)
oven (logicalNameMap?map=oven.xlmap)
Device (DemoDummy:0?map=DemoDummy.map)
Timer (TimerDummy:0?map=TimerDummy.map)
<?xml version="1.0" encoding="UTF-8"?>
<device_server xmlns="https://github.com/ChimeraTK/ControlSystemAdapter-DoocsAdapter">
<location name="THE_ONLY_LOCATION">
<import>/</import>
</location>
</device_server>
<logicalNameMap>
<module name="Configuration">
<redirectedRegister name="heaterMode">
<targetDevice>device</targetDevice>
<targetRegister>HEATER.MODE</targetRegister>
</redirectedRegister>
<redirectedRegister name="lightOn">
<targetDevice>device</targetDevice>
<targetRegister>BOARD.GPIO_OUT0</targetRegister>
</redirectedRegister>
</module>
<module name="ControlUnit">
<module name="Controller">
<redirectedRegister name="heatingCurrent">
<targetDevice>device</targetDevice>
<targetRegister>HEATER.CURRENT_SET</targetRegister>
</redirectedRegister>
<redirectedRegister name="temperatureReadback">
<targetDevice>device</targetDevice>
<targetRegister>SENSORS.TEMPERATURE1</targetRegister>
</redirectedRegister>
</module>
</module>
<module name="Monitoring">
<redirectedRegister name="heatingCurrent">
<targetDevice>device</targetDevice>
<targetRegister>HEATER.CURRENT_READBACK</targetRegister>
</redirectedRegister>
<redirectedRegister name="temperatureOvenTop">
<targetDevice>device</targetDevice>
<targetRegister>SENSORS.TEMPERATURE2</targetRegister>
</redirectedRegister>
<redirectedRegister name="temperatureOvenBottom">
<targetDevice>device</targetDevice>
<targetRegister>SENSORS.TEMPERATURE3</targetRegister>
</redirectedRegister>
<redirectedRegister name="temperatureOutside">
<targetDevice>device</targetDevice>
<targetRegister>SENSORS.TEMPERATURE4</targetRegister>
</redirectedRegister>
</module>
</logicalNameMap>
#!/bin/env python3
import deviceaccess as da
import time
# Set DMAP file name
da.setDMapFilePath("demo_example.dmap")
# Open the oven device directly, without logical name mapping, to access all registers
dev = da.Device("device")
dev.open()
# reset the device
print("Resetting oven device...")
dev.write("BOARD.RESET_N", 0)
time.sleep(0.1)
dev.write("BOARD.RESET_N", 1)
# (inverted) reset line is released now. Additional initialisation would go here.
print("Reset done, device is now ready.")
// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
// SPDX-License-Identifier: LGPL-3.0-or-later
#include <ChimeraTK/ApplicationCore/ApplicationCore.h>
#include <ChimeraTK/ApplicationCore/EnableXMLGenerator.h>
#include <iostream>
namespace ctk = ChimeraTK;
struct Automation : public ctk::ApplicationModule {
using ctk::ApplicationModule::ApplicationModule;
ctk::ScalarPollInput<double> opSP{this, "opSP", "MV", "..."};
ctk::ScalarOutput<double> curSP{this, "curSP", "MV", "..."};
ctk::ScalarPushInput<int> trigger{this, "trigger", "", "..."};
void mainLoop() {
while(true) {
trigger.read();
opSP.readLatest(); // opSP.read() would be equivalent
if(std::abs(opSP - curSP) > 0.01) {
curSP += std::max(std::min(opSP - curSP, 0.1), -0.1);
curSP.write();
}
}
}
};
constexpr size_t tableLength{16384};
constexpr double samplingFrequency = 9.0; // MHz
constexpr double bitScalingFactor = 2000.0; // bits/MV
struct TableGeneration : public ctk::ApplicationModule {
using ctk::ApplicationModule::ApplicationModule;
struct TableParameters : public ctk::VariableGroup {
using ctk::VariableGroup::VariableGroup;
ctk::ScalarPushInput<double> pulseLength{this, "pulseLength", "us", "..."};
ctk::ScalarPushInput<double> setpoint{this, "setpoint", "MV", "..."};
};
TableParameters tableParameters{this, "tableParameters", "..."};
ctk::ArrayOutput<int32_t> setpointTable{this, "setpointTable", "bits", tableLength, "..."};
ctk::ArrayOutput<int32_t> feedforwardTable{this, "feedforwardTable", "bits", tableLength, "..."};
void mainLoop() {
ctk::ReadAnyGroup tableParametersReadGroup = tableParameters.readAnyGroup();
while(true) {
tableParametersReadGroup.readAny(); // block until the any table parameter is changed
for(size_t i = 0; i < tableLength; ++i) {
if(i < tableParameters.pulseLength * samplingFrequency) {
setpointTable[i] = tableParameters.setpoint * bitScalingFactor;
feedforwardTable[i] = 0.5 * tableParameters.setpoint * bitScalingFactor;
}
else {
setpointTable[i] = 0;
feedforwardTable[i] = 0;
}
}
setpointTable.write();
feedforwardTable.write();
}
}
};
struct ExampleApp : public ctk::Application {
ExampleApp() : Application("exampleApp") {}
~ExampleApp() { shutdown(); }
ctk::SetDMapFilePath dmapPath{"dummy.dmap"};
Automation automation{this, "automation", "..."};
TableGeneration tableGeneration{this, "tableGeneration", "..."};
// ctk::DeviceModule dev{this, "Device"};
ctk::DeviceModule dev{this, "Device"};
// ctk::DeviceModule timer{this, "Timer"};
ctk::DeviceModule timer{this, "Timer"};
};
ExampleApp theExampleApp;
// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
/*
* This example is explained as part of the \ref conceptualOverview. Please refere there for step-by-step explanations.
* Reading the full example might not be a good starting point for learning ApplicationCore as it can be overwelming
* and lacks important background information.
*
* Please ignore all comments of the format "//! [some name]", those are used for Doxygen to include code snippets in
* the documentation pages.
*/
#include <ChimeraTK/ApplicationCore/ApplicationCore.h>
namespace ctk = ChimeraTK;
//! [Snippet: Class Definition]
class AverageCurrent : public ctk::ApplicationModule {
using ctk::ApplicationModule::ApplicationModule;
// Take the heaterCurrent from the Controller module as an input
//! [Snippet: heatingCurrent Definition]
ctk::ScalarPushInput<float> current{this, "../Controller/heatingCurrent", "mA", "Actuator output of the controller"};
//! [Snippet: heatingCurrent Definition]
ctk::ScalarOutput<float> currentAveraged{this, "heatingCurrentAveraged", "mA", "Averaged heating current"};
void mainLoop() override;
};
//! [Snippet: Class Definition]
// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
/*
* This example is explained as part of the \ref conceptualOverview. Please refere there for step-by-step explanations.
* Reading the full example might not be a good starting point for learning ApplicationCore as it can be overwelming
* and lacks important background information.
*
* Please ignore all comments of the format "//! [some name]", those are used for Doxygen to include code snippets in
* the documentation pages.
*/
#include <ChimeraTK/ApplicationCore/ApplicationCore.h>
namespace ctk = ChimeraTK;
//! [Snippet: Class Definition]
class Controller : public ctk::ApplicationModule {
using ctk::ApplicationModule::ApplicationModule;
ctk::ScalarPollInput<float> temperatureSetpoint{
this, "temperatureSetpoint", "degC", "Setpoint for the temperature controller"};
ctk::ScalarPushInput<float> temperatureReadback{
this, "temperatureReadback", "degC", "Actual temperature used as controller input"};
ctk::ScalarOutput<float> heatingCurrent{this, "heatingCurrent", "mA", "Actuator output of the controller"};
void mainLoop() override;
};
//! [Snippet: Class Definition]
// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
/*
* This example is explained as part of the \ref conceptualOverview. Please refere there for step-by-step explanations.
* Reading the full example might not be a good starting point for learning ApplicationCore as it can be overwelming
* and lacks important background information.
*
* Please ignore all comments of the format "//! [some name]", those are used for Doxygen to include code snippets in
* the documentation pages.
*/
#include "AverageCurrent.h"
#include "Controller.h"
#include "SetpointRamp.h"
#include <ChimeraTK/ApplicationCore/ApplicationCore.h>
#include <ChimeraTK/ApplicationCore/ConfigReader.h>
#include <ChimeraTK/ApplicationCore/PeriodicTrigger.h>
#include <ChimeraTK/ApplicationCore/ScriptedInitialisationHandler.h>
namespace ctk = ChimeraTK;
//! [Snippet: Class Definition Start]
class ExampleApp : public ctk::Application {
public:
using ctk::Application::Application;
~ExampleApp() override;
private:
//! [Snippet: Class Definition Start]
// Set the name of the DMAP file to define the devices. Must be done before instantiating any DeviceModule.
// Using the application name as a base helps for automated testing against different config files.
//! [Snippet: SetDMapFilePath]
ctk::SetDMapFilePath dmapPath{getName() + ".dmap"};
//! [Snippet: SetDMapFilePath]
// Provide configuration constants as process variables and for use at construction phase
// Using the application name as a base helps for automated testing against different config files.
ctk::ConfigReader config{this, "/", getName() + "-config.xml"};
// Periodic trigger used to readout data from the device periodically.
//! [Snippet: PeriodicTrigger Instance]
ctk::PeriodicTrigger timer{this, "Timer", "Periodic timer for the controller"};
//! [Snippet: PeriodicTrigger Instance]
// Publish the content of the device "oven" defined in the DMAP file to the control system and to the application
// modules. The "tick" output of the PeriodicTimer "Timer" defined above is used as a readout trigger (for all
// poll-typed device registers).
//! [Snippet: Device]
ctk::DeviceModule oven{this, "oven", "/Timer/tick"};
//! [Snippet: Device]
// Initialisation handler: execute Python script to initialise oven device
//! [Snippet: ScriptedInitHandler]
ctk::ScriptedInitHandler ovenInit{this, "ovenInit", "Initialisation of oven device", "./ovenInit.py", oven};
//! [Snippet: ScriptedInitHandler]
//! [Snippet: ControlUnit ModuleGroup]
struct ControlUnit : ctk::ModuleGroup {
using ctk::ModuleGroup::ModuleGroup;
//! [Snippet: Controller Instance]
// Instantiate the temperature controller module
Controller controller{this, "Controller", "The temperature controller"};
//! [Snippet: Controller Instance]
//! [Snippet: AverageCurrent Instance]
// Instantiate the heater current averaging module
AverageCurrent averageCurrent{this, "AverageCurrent", "Provide averaged heater current"};
//! [Snippet: AverageCurrent Instance]
};
ControlUnit controlUnit{this, "ControlUnit", "Unit for controlling the oven temperature"};
//! [Snippet: ControlUnit ModuleGroup]
// Optionally instantiate the automated setpoint ramping module
SetpointRamp ramp{config.get<ChimeraTK::Boolean>("Configuration/enableSetpointRamping") ?
SetpointRamp(this, "SetpointRamp", "Slow ramping of temperator setpoint") :
SetpointRamp()};
//! [Snippet: Class Definition End]
};
//! [Snippet: Class Definition End]
// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
/*
* This example is explained as part of the \ref conceptualOverview. Please refere there for step-by-step explanations.
* Reading the full example might not be a good starting point for learning ApplicationCore as it can be overwelming
* and lacks important background information.
*
* Please ignore all comments of the format "//! [some name]", those are used for Doxygen to include code snippets in
* the documentation pages.
*/
#include <ChimeraTK/ApplicationCore/ApplicationCore.h>
namespace ctk = ChimeraTK;
class SetpointRamp : public ctk::ApplicationModule {
using ctk::ApplicationModule::ApplicationModule;
ctk::ScalarPollInput<float> operatorSetpoint{this, "operatorSetpoint", "degC", "..."};
struct ControllerInterface : ctk::VariableGroup {
using ctk::VariableGroup::VariableGroup;
ctk::ScalarOutput<float> actualSetpoint{this, "temperatureSetpoint", "degC", "..."};
};
ControllerInterface ctrl{this, "/ControlUnit/Controller", ""};
ctk::ScalarPushInput<uint64_t> trigger{this, "/Timer/tick", "", "..."};
void mainLoop() override;
};
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