This example shows the basic structure of an Application written with ApplicationCore.
The full example project can be found in the "example" subdirectory. Please refer to the Conceptual overview for detail explanation.
The directory structure looks like this:
- CMakeLists.txt - cmake project configuration
- include/... - Header files for the ApplicationModule and the Application implementations
- src/... - Source files for the ApplicationModule and the Application implementations
- src_factory/... - Source file for creating the instance of the ApplicationFactory
- config/... - Configuration files needed for testing and demo execution
- cmake/... - These files are coming from the project template and must not be altered
- All other files in project root - These files are coming from the project template and must not be altered
include
include/SetpointRamp.h
#pragma once
#include <ChimeraTK/ApplicationCore/ApplicationCore.h>
using ctk::ApplicationModule::ApplicationModule;
using ctk::VariableGroup::VariableGroup;
};
ControllerInterface ctrl{this, "/ControlUnit/Controller", ""};
void mainLoop() override;
};
InvalidityTracer application module.
Convenience class for output scalar accessors (always UpdateMode::push)
include/AverageCurrent.h
#pragma once
#include <ChimeraTK/ApplicationCore/ApplicationCore.h>
using ctk::ApplicationModule::ApplicationModule;
void mainLoop() override;
};
[Snippet: Class Definition]
include/Controller.h
#pragma once
#include <ChimeraTK/ApplicationCore/ApplicationCore.h>
using ctk::ApplicationModule::ApplicationModule;
this, "temperatureSetpoint", "degC", "Setpoint for the temperature controller"};
this, "temperatureReadback", "degC", "Actual temperature used as controller input"};
void mainLoop() override;
};
[Snippet: Class Definition]
include/ExampleApp.h
#pragma once
#include <ChimeraTK/ApplicationCore/ApplicationCore.h>
#include <ChimeraTK/ApplicationCore/ConfigReader.h>
#include <ChimeraTK/ApplicationCore/PeriodicTrigger.h>
#include <ChimeraTK/ApplicationCore/ScriptedInitialisationHandler.h>
#include <ChimeraTK/ApplicationCore/VersionInfoProvider.h>
public:
using ctk::Application::Application;
private:
using ctk::ModuleGroup::ModuleGroup;
Controller controller{
this,
"Controller",
"The temperature controller"};
AverageCurrent averageCurrent{
this,
"AverageCurrent",
"Provide averaged heater current"};
};
ControlUnit controlUnit{this, "ControlUnit", "Unit for controlling the oven temperature"};
SetpointRamp(
this,
"SetpointRamp",
"Slow ramping of temperator setpoint") :
};
ConfigReader & getConfigReader()
const T & get(std::string variableName) const
Get value for given configuration variable.
const std::string & getName() const
Get the name of the module instance.
Helper class to set the DMAP file path.
This module can be added to applications to provide version information from the CMakeLists....
[Snippet: Class Definition Start]
~ExampleApp() override
[Snippet: Destructor]
Simple periodic trigger that fires a variable once per second.
Initialisation handler which calls an external application (usually a script), captures its output (b...
src
src/AutomSetpointRampation.cc
void SetpointRamp::mainLoop() {
const float maxStep = 0.1F;
while(true) {
ctrl.actualSetpoint += std::max(std::min(operatorSetpoint - ctrl.actualSetpoint, maxStep), -maxStep);
}
}
void readAll(bool includeReturnChannels=false)
Read all readable variables in the group.
void writeAll(bool includeReturnChannels=false)
Just call write() on all writable variables in the group.
src/AverageCurrent.cc
void AverageCurrent::mainLoop() {
const float coeff = 0.1;
while(true) {
current.read();
currentAveraged.
writeIfDifferent((1 - coeff) * currentAveraged + coeff * current);
}
}
void writeIfDifferent(UserType newValue, VersionNumber versionNumber, DataValidity validity)=delete
void setAndWrite(UserType newValue, VersionNumber versionNumber)=delete
src/AutomatiControlleron.cc
void Controller::mainLoop() {
const float gain = 100.0F;
while(true) {
heatingCurrent = gain * (temperatureSetpoint - temperatureReadback);
}
}
src/ExampleApp.cc
}
void shutdown() override
This will remove the global pointer to the instance and allows creating another instance afterwards.
src_factory
#include <ChimeraTK/ApplicationCore/EnableXMLGenerator.h>
static ctk::ApplicationFactory<ExampleApp> appFactory("demo_example");
config
<configuration>
<module name="Configuration">
<variable name="enableSetpointRamping" type="boolean" value="false"/>
<variable name="heaterMode" type="uint8" value="2"/>
</module>
<module name="PythonModules">
<module name="UserAppModules">
<variable name="path" type="string" value="userAppModules" />
</module>
</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>
<module name="Application">
<variable name="configPatchVersion" type="int32" value="67"/>
</module>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<device_server xmlns="https://github.com/ChimeraTK/ControlSystemAdapter-DoocsAdapter">
<location name="DEMO">
<import>/</import>
</location>
</device_server>
config/demo_example.conf
# Conf file created at 17:08.38 13. Jun. 2016
# eq_fct_type's are defined in eq_fct_code.h
eq_conf:
oper_uid: -1
oper_gid: 422
xpert_uid: 0
xpert_gid: 0
ring_buffer: 10000
memory_buffer: 500
eq_fct_name: "NODENAME._SVR"
eq_fct_type: 1
{
SVR.NAME: "NODENAME._SVR"
STS: 0x1c
STS.ERROR: 0
STS.NEWERROR: 1
STS.ERRORMASK: 1
STS.ONLINE: 1
ERROR.STR: 0 0 0 1078761493 ""
SYS_MASK: 1
FCT_CODE: 1
FCT_PANEL: ""
X_POS: 0
Z_POS: 0
Z_POS.STRING: ""
DEVICE.INFO: 235672 0 0 0 "Device OK"
MESSAGE: ""
LAST_UPDATE: 1112798768 350 0 0
LAST_USR1: 0 0 0 0
SVR.ALIAS: 0
SVR.ARCFLUSH: 0
SVR.ARCFLUSH_B: 0
SVR.UPDATE: 1465830518
SVR.RATE: 1 0 0 0
SVR.RESIZE: 200
SVR.FILE: "demoApp.conf"
SVR.DESC: ""
SVR.PROGRAMMER: "mhier"
SVR.XMLFILE: ""
SVR.ERRORLOG: "/doocs/nodename/server/InstaCoSADevExample_server/InstaCoSADevExample_server.log"
SVR.STORE.RATE: 10
SVR.STORE.AUTO: 4
SVR.HOST_NAME: "mskpcx19821"
SVR.PROCESSNAME: "demoApp"
SVR.RPC_NUMBER: 610498009
SVR.STARTTIME: 1465830430
SVR.LIBINFO: "18.10.6"
SVR.LIBDATE: "Jun 2 13:38"
SVR.WDADDR: ""
SVR.CONTR: 0x0
SVR.MUST_RUN: 0
SVR.STOP_SVR: 0
SVR.START_CMD: ""
SVR.RPC_CALL_TIME.COMMENT: "Time per Call"
SVR.RPC_CALL_TIME.EGU: 1 1 100000 0 "rate"
SVR.RPC_CALL_TIME.XEGU: 0 0 100 0 "ms"
SVR.UPDATE_TIME.COMMENT: "Time per Update"
SVR.UPDATE_TIME.EGU: 1 1 100000 0 "rate"
SVR.UPDATE_TIME.XEGU: 0 0 100 0 "ms"
SVR.USR1_TIME.COMMENT: "run time of SIGUSR1"
SVR.USR1_TIME.EGU: 1 1 1e+06 1427728880 "counts"
SVR.USR1_TIME.XEGU: 0 0 100 1427728880 "ms"
SVR.USR1_PERIOD.COMMENT: "time between SIGUSR1"
SVR.USR1_PERIOD.EGU: 1 1 1e+06 1427728880 "counts"
SVR.USR1_PERIOD.XEGU: 0 0 500 1427728880 "ms"
SVR.ARCH_GET_TIME.COMMENT: "time per archiver get"
SVR.ARCH_GET_TIME.EGU: 1 1 1e+06 1459338630 "counts"
SVR.ARCH_GET_TIME.XEGU: 0 0 100 1459338630 "ms"
SVR.LAFL: 0
SVR.ERROR_COUNT: 0
DEVICE.ONLINE: 1
DEVICE.OFFLINE: 0
SVR.DEVMAX: 0
SVR.TINERUN: 0
SVR.TINEVERS: "4.05.0009"
SVR.TINEPREF: ""
SVR.TINESUFF: ""
SVR.TINE_DBG: 0
SVR.TINE_LOG: 0
SVR.TINE_FEC: 0 0 0 0 ""
SVR.TINE_PORT: 0
SVR.TINE_MTU: 1472
SVR.TINE_CTSZ: 32
SVR.TINE_MCTTL: 16
SVR.TINE_BLIM: 1000
SVR.TINE_CDLY: 20
SVR.TINE_GROUP: 0
SVR.FACILITY: "TEST.DOOCS"
SVR.DEVICE: "InstaCoSADevExample"
T_ZERO: 700
SVR.BPN: 0
SVR.SPR: 0
}
#No need to put application locations if you don't need variables content as persistency layer.
#They will be auto-generated.
config/demo_example.dmap
device (sharedMemoryDummy:0?map=DemoDummy.map)
oven (logicalNameMap?map=oven.xlmap)
config/DemoDummy.map
# 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
config/oven.xlmap
<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>