EPICSPVAInput

Warning

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

The EPICSPVAInput DataSource can be used to receive application data using the EPICS pvAccess protocol. This allows interfacing the application with any EPICS-based monitoring or control application (e.g. EPICS Control System Studio).

This DataSource reads from the PVA records asynchronously on a separate thread (which may be allocated to a different CPU core). This means that it is not intended to be used for real-time control, but rather for live parameter configuration.

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

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

The PV records are hosted using a MARTe2 EPICSPVADatabase, which in this example is instantiated in the same configuration file as the application.

EPICSPVADatabase configuration. Note that the PV record names are automatically replaced by the Makefile.cfg.
 1+EPICSPVADatabase = {
 2    Class = EPICSPVA::EPICSPVADatabase
 3    StackSize = 1048576 
 4    CPUs = 0x1
 5    AutoStart = 1 
 6    +AppControlMonitor = {
 7        Class = EPICSPVA::EPICSPVARecord
 8    +AppControlInput = {
 9        Class = EPICSPVA::EPICSPVARecord
10        Alias = XSTR(MARTE2-TUTORIAL-USERNAME-MASS-SPRING-CONTROL-INPUT)
11        Structure = {
12            value = {
13                Type = ControlInput
14                NumberOfElements = 1
15            }
16        }
17    }

In this example, the ReferencePosition signal is read from a PVA record and used as the reference for the control of the mass-spring system.

IOGAM to copy from the DataSource. Note the use of the Alias field to access the signal directly from the DataSource without the need to copy the full structure.
 1        +GAMReference = {
 2            Class = IOGAM
 3            InputSignals = {
 4                ReferencePosition = {
 5                    DataSource = EPICSPVAReader
 6                    Type = float64
 7                    Alias = "ControlIn.ReferencePosition"
 8                }
 9            }
10            OutputSignals = {
11                ReferencePosition = {
12                    DataSource = DDB1
13                    Type = float64
14                }
15            }
16        }
EPICSPVAInput configuration. Note that the PV record name is automatically replaced by the Makefile.cfg.
 1        +EPICSPVAReader = {
 2            Class = EPICSPVADataSource::EPICSPVAInput
 3            CPUMask = 0x1 
 4            StackSize = 10000000 
 5            Signals = {
 6                ControlIn = {
 7                    Alias = XSTR(MARTE2-TUTORIAL-USERNAME-MASS-SPRING-CONTROL-INPUT)
 8                    Field = "value" 
 9                    Type = ControlInput 
10                }
11            }
12        }

Running the application

make -C ../Configurations/MassSpring/ -f Makefile.cfg
./MARTeApp.sh -f ../Configurations/MassSpring/RTApp-MassSpring-38_Gen.cfg -l RealTimeLoader -s State1

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

...
$ [Debug - EPICSPVAChannelWrapper.cpp:362]: Resolving scalar (or array of) [value.Control.Thread1.GAMsExecutionTime]
$ [Debug - EPICSPVAChannelWrapper.cpp:373]: Assigned PV to signal with name [value.Control.Thread1.GAMsExecutionTime]
$ [Information - LoggerBroker.cpp:152]: Time [0:0]:1000000
$ [Information - LoggerBroker.cpp:152]: Time [0:0]:2000000
...

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

pvget -m MARTE2-TUTORIAL-USERNAME-MASS-SPRING-CONTROL # Replace with your username in capital letters

The output should be similar to the following:

 $ MARTE2-TUTORIAL-USERNAME-MASS-SPRING-CONTROL structure
 $   MonitorControl value
 $       uint Time 38650000
 $       ControlParameters Control
 $           double ReferencePosition 0
 $           double Position 3.15283e-14
 $           double PositionDisturbed 5.46623e-14
 $           double PositionFiltered 2.90153e-13
 $           double PositionM 3.99788e-09
 $           double Velocity -2.95341e-12
 $           double VelocityM 1.85333e-09
 $           double PositionErr 2.90153e-13
 $           double Force -6.71745e-11
 $           double ForceAverage -6.60828e-17
 $           double ForceStdDev 2.22174e-08
 $           double ForceMax 2.23554e-10
 $           double ForceMin -2.23302e-10

...

Open another terminal and modify the value of the ReferencePosition PVA record using the command:

pvput MARTE2-TUTORIAL-USERNAME-MASS-SPRING-CONTROL-INPUT value='{"ReferencePosition":1.0}' #Replace with your username in capital letters

Open another terminal and check that the PVA record is being updated with the application data using the command:

pvget -m MARTE2-TUTORIAL-USERNAME-MASS-SPRING-CONTROL #Replace with your username in capital letters

The output should be similar to the following:

$ MARTE2-TUTORIAL-USERNAME-MASS-SPRING-CONTROL structure
$   MonitorControl value
$       uint Time 177230000
$       ControlParameters Control
$           double ReferencePosition 1
$           double Position 0.996746
$           double PositionDisturbed 1.00621
$           double PositionFiltered 0.996685
$           double PositionM 0.993831
$           double Velocity 0.00478432
$           double VelocityM 0.0349763
$           double PositionErr -0.00331458
$           double Force 9.9693
$           double ForceAverage 10.0853
$           double ForceStdDev 0.152168
$           double ForceMax 10.489
$           double ForceMin 9.96202

Exercises

Ex. 1: Switch on/off

The objective of this exercise is to use the EPICSPVAInput DataSource to switch the force output of the mass-spring system on and off.

  1. Edit the file ../Configurations/MassSpring/RTApp-MassSpring-39.cfg and add a MuxGAM that selects between the signals ForceZero and ForceFromController to produce an output signal named Force in DDB1. Name this GAM GAMMuxSwitchOn.

  2. Modify the ControllerGAM to output the signal named ForceFromController instead of Force.

  3. Modify the ReferenceGAM to read the full structure from the EPICSCAInput DataSource and copy both the ReferencePosition and the SwitchOn signal to DDB1.

  4. Add the MuxGAM to the execution list.

  5. Execute the application:

make -C ../Configurations/MassSpring/ -f Makefile.cfg
./MARTeApp.sh -f ../Configurations/MassSpring/RTApp-MassSpring-39_Gen.cfg -l RealTimeLoader -s State1
  1. Set a new target ReferencePosition using the pvput command.

pvput MARTE2-TUTORIAL-USERNAME-MASS-SPRING-CONTROL-INPUT value='{"ReferencePosition":1.0}' #Replace with your username in capital letters
  1. Observe that the Position is not changing and that the system is not responding to the new target ReferencePosition.

pvget -m MARTE2-TUTORIAL-USERNAME-MASS-SPRING-CONTROL #Replace with your username in capital letters
  1. Switch on the system by setting the switch on-off PV to 1 using the command:

pvput MARTE2-TUTORIAL-USERNAME-MASS-SPRING-CONTROL-INPUT value='{"SwitchOn":1}' #Replace with your username in capital letters
  1. Observe that the Position is now changing and that the system is responding to the new target ReferencePosition.

pvget -m MARTE2-TUTORIAL-USERNAME-MASS-SPRING-CONTROL #Replace with your username in capital letters
Solution

The solution is to add a MuxGAM as specified above.

MuxGAM configuration.
 1        +GAMMuxSwitchOn = {
 2            Class = MuxGAM
 3            InputSignals = {
 4                SwitchOn = {
 5                    DataSource = DDB1
 6                    Type = uint32
 7                    NumberOfDimensions = 1
 8                    NumberOfElements = 1
 9                }
10                ForceZero = {
11                    DataSource = DDB1
12                    Type = float64
13                    NumberOfDimensions = 1
14                    NumberOfElements = 1
15                }
16                ForceFromController = {
17                    DataSource = DDB1
18                    Type = float64
19                    NumberOfDimensions = 1
20                    NumberOfElements = 1
21                }
22            }
23            OutputSignals = {
24                Force = {
25                    DataSource = DDB1
26                    Type = float64
27                    NumberOfDimensions = 1
28                    NumberOfElements = 1
29                }
30            }
31        }

Update the GAMReference to read both signals from the DataSource.

GAMReference configuration
 1        +GAMReference = {
 2            Class = IOGAM
 3            InputSignals = {
 4                ControlIn = {
 5                    DataSource = EPICSPVAReader
 6                    Type = ControlInput
 7                }
 8            }
 9            OutputSignals = {
10                ReferencePosition = {
11                    DataSource = DDB1
12                    Type = float64
13                }
14                SwitchOn = {
15                    DataSource = DDB1
16                    Type = uint32
17                }
18            }
19        }

Note

The Alias could also have been used to access the signals directly from the DataSource in the MuxGAM without copying the full structure in ReferenceGAM.

However, the MuxGAM requires the signal to have NumberOfDimensions=1, while the structured signal read from the DataSource has NumberOfDimensions=0, and even if they are binary compatible (array of one element and scalar), another GAM to covert between them would be needed, which would make the configuration more complex.

Add the GAM to the execution list.

GAM execution list.
 1    +States = {
 2        Class = ReferenceContainer
 3        +State1 = {
 4            Class = RealTimeState
 5            +Threads = {
 6                Class = ReferenceContainer
 7                +Thread1 = {
 8                    Class = RealTimeThread
 9                    CPUs = 0x1
10                    Functions = {GAMPerfMonitor GAMTimer GAMReference GAMDisturbanceWaveform GAMMathDisturbance GAMFilterDisturbance GAMController GAMConstantZero GAMMuxSwitchOn GAMSpringMass GAMMathModel GAMMathPositionErr GAMStats GAMPatchForceDims GAMForceStats GAMHist GAMMathExpr GAMConversion GAMFilterMovingAvg GAMMathTrigger GAMMathTriggerSecond GAMDisplay GAMWriter}
11                }
12            }
13        }        
14    }