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>
};
ControllerInterface ctrl{this, "/ControlUnit/Controller", ""};
void mainLoop() override;
};
include/AverageCurrent.h
#pragma once
#include <ChimeraTK/ApplicationCore/ApplicationCore.h>
void mainLoop() override;
};
include/Controller.h
#pragma once
#include <ChimeraTK/ApplicationCore/ApplicationCore.h>
this, "temperatureSetpoint", "degC", "Setpoint for the temperature controller"};
this, "temperatureReadback", "degC", "Actual temperature used as controller input"};
void mainLoop() override;
};
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:
private:
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") :
};
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);
}
}
src/AverageCurrent.cc
void AverageCurrent::mainLoop() {
const float coeff = 0.1;
while(true) {
current.read();
currentAveraged.
writeIfDifferent((1 - coeff) * currentAveraged + coeff * current);
}
}
src/AutomatiControlleron.cc
void Controller::mainLoop() {
const float gain = 100.0F;
while(true) {
heatingCurrent = gain * (temperatureSetpoint - temperatureReadback);
}
}
src/ExampleApp.cc
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>