EPICSCAOutput

Warning

The EPICSCAOutput DataSource is only available in distributions where EPICS Channel Access is installed.

The EPICSCAOutput DataSource can be used to stream application data over the EPICS Channel Access protocol. This allows interfacing the application with any EPICS-based monitoring or control application (e.g. EPICS Archiver Appliance, EPICS Control System Studio).

This DataSource writes to the PVs asynchronously on a separate thread (which may be allocated to a different CPU core).

The names of the PVs need to be defined for every signal in the DataSource configuration.

Given that the PV name needs to be unique, in order to avoid name clashes, the configuration file ../Configurations/MassSpring/RTApp-MassSpring-30.cfg will be automatically updated from a Makefile.cfg. The names of the PVs are based on the username.

The PVs are hosted on a softIOC which needs to be started before running the application. The softIOC configuration file is generated from a template by running the command ../Test/Integrated/GenerateEPICSSoftIOCDb.sh, which will also automatically generate the PV names to match the ones defined in the configuration file ../Configurations/MassSpring/RTApp-MassSpring-30.cfg.

In this example, the main application data is written into the corresponding PVs.

EPICSCAOutput configuration. Note that the PV names are automatically replaced by the Makefile.cfg.
 1        +EPICSCAWriter = {
 2            Class = EPICSCA::EPICSCAOutput
 3            NumberOfBuffers = 10
 4            CPUMask = 0x1 
 5            StackSize = 10000000 
 6            Signals = {
 7                TriggerWriter = {
 8                    Type = uint8
 9                    PVName = XSTR(MARTE2-TUTORIAL-USERNAME-TRIGGER-WRITER)
10                }
11                Time = {
12                    Type = uint32
13                    PVName = XSTR(MARTE2-TUTORIAL-USERNAME-TIME)
14                }
15                ReferencePosition = {
16                    Type = float64
17                    PVName = XSTR(MARTE2-TUTORIAL-USERNAME-REFERENCE-POSITION)
18                }
19                Position = {
20                    Type = float64

Running the application

Start the softIOC in a separate terminal:

../Test/Integrated/GenerateEPICSSoftIOCDb.sh
softIoc -d ../Test/Integrated/MassSpring-1.db

Start the application with:

./MARTeApp.sh -f ../Configurations/MassSpring/RTApp-MassSpring-30_Gen.cfg -l RealTimeLoader -s State1

Once the application is running, inspect the screen output and verify that the application is running without any issues. The log should show entries similar to the following:

$ [Warning - Threads.cpp:181]: Failed to change the thread priority (likely due to insufficient permissions)
$ [Information - RealTimeLoader.cpp:111]: Started application in state State1
$ [Information - MARTeApp.cpp:135]: Application starting
$ [Information - LoggerBroker.cpp:152]: Time [0:0]:1000000
...

Open another terminal and check that the PVs are being updated with the application data using the command:

camonitor MARTE2-TUTORIAL-USERNAME-REFERENCE-POSITION MARTE2-TUTORIAL-USERNAME-POSITION #Replace with your username in capital letters

The output should be similar to the following:

$ MARTE2-TUTORIAL-USERNAME-REFERENCE-POSITION 2026-04-01 10:07:25.733591 1.324
$ MARTE2-TUTORIAL-USERNAME-POSITION 2026-04-01 10:07:25.733595 1.22757
$ MARTE2-TUTORIAL-USERNAME-REFERENCE-POSITION 2026-04-01 10:07:25.748061 1.328
$ MARTE2-TUTORIAL-USERNAME-POSITION 2026-04-01 10:07:25.748076 1.2316
$ MARTE2-TUTORIAL-USERNAME-REFERENCE-POSITION 2026-04-01 10:07:25.753653 1.332
$ MARTE2-TUTORIAL-USERNAME-POSITION 2026-04-01 10:07:25.753660 1.23562

Exercises

Ex. 1: Statistics monitoring

Add a GAM and another DataSource to monitor the statistics of the execution of the application. The statistics should include Thread1CycleTime, Thread1CycleTimeAverage, Thread1CycleTimeMovingAverage, Thread1CycleTimeStdDev, Thread1CycleTimeMax, Thread1CycleTimeMin, Thread1CycleTimeHistogram, Thread1FreeTimeHistogram, GAMsExecutionTime.

  1. Edit the file ../Configurations/MassSpring/RTApp-MassSpring-31.cfg and add an EPICSCAOutput DataSource to store the signals Thread1CycleTime, Thread1CycleTimeAverage, Thread1CycleTimeMovingAverage, Thread1CycleTimeStdDev, Thread1CycleTimeMax, Thread1CycleTimeMin, Thread1CycleTimeHistogram, Thread1FreeTimeHistogram, GAMsExecutionTime.

  2. The names of the PVs can be found in the file ../Test/Integrated/MassSpring-1.db.

  3. Add a GAM to write the signals to the DataSource.

  4. Add the GAM to the execution list.

  5. Run the application with:

make -C ../Configurations/MassSpring/ -f Makefile.cfg
./MARTeApp.sh -f ../Configurations/MassSpring/RTApp-MassSpring-31_Gen.cfg -l RealTimeLoader -s State1
  1. Check that the PVs are being written and that the content is updated with the latest values of the signals.

  2. Open another terminal and check that the PVs are being updated with the application data using the command:

camonitor MARTE2-TUTORIAL-USERNAME-THREAD1-CYCLE-TIME MARTE2-TUTORIAL-USERNAME-THREAD1-CYCLE-TIME-HISTOGRAM #Replace with your username in capital letters
  1. The output should be similar to the following:

$ MARTE2-TUTORIAL-USERNAME-THREAD1-CYCLE-TIME 2026-04-01 10:17:06.617292 9990
$ MARTE2-TUTORIAL-USERNAME-THREAD1-CYCLE-TIME-HISTOGRAM 2026-04-01 10:17:06.617297 11 0 0 0 2 0 1362 0 2 0 0 0
$ MARTE2-TUTORIAL-USERNAME-THREAD1-CYCLE-TIME 2026-04-01 10:17:06.627271 10009
$ MARTE2-TUTORIAL-USERNAME-THREAD1-CYCLE-TIME-HISTOGRAM 2026-04-01 10:17:06.627276 11 0 0 0 2 0 1363 0 2 0 0 0
Solution

The solution is to add an EPICSCAWriterStats with the signals.

EPICSCAWriterStats to write the statistics signals.
 1        +EPICSCAWriterStats = {
 2            Class = EPICSCA::EPICSCAOutput
 3            NumberOfBuffers = 10
 4            CPUMask = 0x1 
 5            StackSize = 10000000 
 6            Signals = {
 7                Thread1CycleTime = {
 8                    Type = uint32
 9                    PVName = XSTR(MARTE2-TUTORIAL-USERNAME-THREAD1-CYCLE-TIME)
10                }
11                Thread1CycleTimeAverage = {
12                    Type = uint32
13                    PVName = XSTR(MARTE2-TUTORIAL-USERNAME-THREAD1-CYCLE-TIME-AVERAGE)
14                }
15                Thread1CycleTimeMovingAverage = {
16                    Type = float32
17                    PVName = XSTR(MARTE2-TUTORIAL-USERNAME-THREAD1-CYCLE-TIME-MOVING-AVERAGE)
18                }
19                Thread1CycleTimeStdDev = {

Add the GAM to write the signals to the DataSource.

GAM to write the signals to the DataSource.
 1        +GAMWriterStats = {
 2            Class = IOGAM
 3            InputSignals = {
 4                Thread1CycleTime = {
 5                    DataSource = DDB1
 6                    Type = uint32
 7                }
 8                Thread1CycleTimeAverage = {
 9                    DataSource = DDB1
10                    Type = uint32
11                }
12                Thread1CycleTimeMovingAverage = {
13            OutputSignals = {
14                Thread1CycleTime = {
15                    DataSource = EPICSCAWriterStats
16                    Type = uint32
17                }
18                Thread1CycleTimeAverage = {
19                    DataSource = EPICSCAWriterStats
20                    Type = uint32
21                }
22                Thread1CycleTimeMovingAverage = {