You are here: Foswiki>FESA Web>Fesa3_700>FESA700_CodeSnippets (22 Aug 2024, TobiasHabermann)Edit Attach

FESA C++ Code Snippets

Contents

- Working with devices -

Working with devices in the server-part

// Since server actions are only executed for a specific device,
// we can use the argument "pDev", which is passed to the execute-method
pDev->myField.set(someValue,pEvt->getMultiplexingContext());

// In Server-Actions the global-device can be accessed by using the service-locator
this->MyClassServiceLocator_->getGlobalDevice()->myField.set(someValue,pEvt->getMultiplexingContext());

For the complete API of the class "device" and it's relatives, check the doxygen documentation.

Working with devices in the RT-part

An Action class has a member variable deviceCol_ that contains all devices that are configured in the instance file to generate an event that can trigger that action. For example, if SomeEvent triggers SomeAction and device-instance/events-mapping/SomeEvent/event-configuration-ref = NONE, that device will not be in the device collection for SomeAction.

Normally the function getFilteredDeviceCollection should be used. This returns deviceCol_ unless the action was triggered by an on-demand event, then the device collection needs to be extracted from the event payload.

The device collection available from the service locator contains all devices, regardless of the configuration.

// Iterating over the devices mapped to this event
// active devices
for (auto const& device : getFilteredDeviceCollection(pEvt) )
{
   // perform some action with the device
   device->myField.set(someValue,pEvt->getMultiplexingContext());
}

// all devices
for (auto const& device : MyClassServiceLocator_->getDeviceCollection() )
{
   // perform some action with the device
   device->myField.set(someValue,pEvt->getMultiplexingContext());
}

When using White Rabbit Timing with devices in different timing groups further filtering is required, see Working with Devices in different Timing Groups

C++ 03

// Devices is typedef of vector<Device*>
const Devices& devices = getFilteredDeviceCollection(pEvt); 
for (Devices::const_iterator it = devices.begin(); it != devices.end(); ++it)
{
    Device* device = *it;
    device->myField.set(someValue,pEvt->getMultiplexingContext());
}

// Iterating over the entire device-collection
const std::vector<Device*> devices =  MyClassServiceLocator_->getDeviceCollection();
for (std::vector<Device*>::const_iterator it = devices.begin(); it != devices.end(); ++it)
{
    Device* device = *it;
    device->myField.set(someValue,pEvt->getMultiplexingContext());
}

For the complete API of the class "device" and it's relatives, check the doxygen documentation.

Working with devices in the specificInit methods

In the ServerDeviceClass::specificInit method the only the complete device collection is available. As there is no event in the parameters, a context must be created.
// Non-multiplexed fields can be passed a NoneContext, containing no cycle information
fesa::NoneContext noneContext;  
// Multiplexed fields can be passed a TimingContext created from a cycle name
fesa::TimingContext timingContext1("FAIR.SELECTOR.S=1")
fesa::TimingContext timingContext2("FAIR.SELECTOR.S=2")


// in the method "specificInit" of the RT/Server-DeviceClass the service-locator can be used to access all the different devices
for (auto const& device : MyClassServiceLocator_->getDeviceCollection() )
{
   device->nonMuxField.set(someValue,&noneContext);
   device->muxField.set(someOtherValue,&timingContext1);
   device->muxField.set(someOtherValue,&timingContext2);
}

// The global-device may also be accessed
this->MyClassServiceLocator_->getGlobalDevice()->myField.set(someValue,&nonecontext);

Working with devices of a composed class

In this code-snipped the current class defines the class "Slave" as composed class in relationship/composition. E.g. the field "slaveField" can be accessed like that:
#include <Slave/GeneratedCode/ServiceLocator.h>

...

std::vector<Slave::Device*> slaveDevices = this->SlaveServiceLocator_->getDeviceCollection();
std::vector<Slave::Device*>::iterator slaveDevice;
for(slaveDevice=slaveDevices.begin();slaveDevice!=slaveDevices.end();++slaveDevice)
{
   (*slaveDevice)->slaveField.set(someValue,pEvt->getMultiplexingContext());
}

Accordingly the global-device of "Slave" can be accessed:
#include <Slave/GeneratedCode/ServiceLocator.h>

...

Slave::GlobalDevice* slaveGlobalDevice = this->SlaveServiceLocator_->getGlobalDevice();
slaveGlobalDevice->slaveGlobalField.set(someValue,&context);

C++ 11

#include <Slave/GeneratedCode/ServiceLocator.h>

...

std::vector<Slave::Device*> slaveDevices = this->SlaveServiceLocator_->getDeviceCollection();
for(const Device* slaveDevice : slaveDevices )
{
   slaveDevice->slaveField.set(someValue,pEvt->getMultiplexingContext());
}

- Working with fields -

Working with FESA scalar-fields

int32_t myData = (*device)->myField.get(pEvt->getMultiplexingContext());
myData ++;
(*device)->myField.set(myData,pEvt->getMultiplexingContext());

For the complete API of scalar-fields and its relatives, check the doxygen documentation.

Working with FESA array-fields

uint32_t index = 1;

//Get a cell-value
const int32_t myData = (*device)->myArrayField.getCell(index,pEvt->getMultiplexingContext());

//Set a cell-value
int32_t myNewData= 123;
(*device)->myArrayField.setCell(myNewData,index,pEvt->getMultiplexingContext());

// Set data to a array-field:
uint32_t sizeOfmyData = 4;
int32_t myData[sizeOfmyData];
myData[1] = 15;
(*device)->myArrayField.set(myData,sizeOfmyData,pEvt->getMultiplexingContext());

// Get data from a array-field:
uint32_t dim;
const int32_t* myData = (*device)->myArrayField.get(dim,pEvt->getMultiplexingContext());

// Get the size of the current array:
uint32_t size = (*device)->myArrayField.getSize(pEvt->getMultiplexingContext());

// Get the maximum possible size of the array:
uint32_t maxSize = (*device)->myArrayField.getMaxSize();

// Set the size of the current array ( Needed when a smaller array is copied to the field! The maximum size cannot be exceeded.)
std::size_t newSize = 123;
(*device)->myArrayField.setSize(newSize,pEvt->getMultiplexingContext());

For the complete API of array-fields and it's relatives, check the doxygen documentation.

Working with FESA structs

Given a struct definition in the design similar to this one:
<equipment-model>
...
<data>
    <device-data>
    ...
        <field id="_151119171759_0" name="acqValue">
            <description>connect acquisition value elements with acquisition-property value items</description>
            <custom-type-array data-type-name-ref="ValueConfig">
                <variable-dim>
                    <min>0</min>
                </variable-dim>
            </custom-type-array>
       </field>
...
    <custom-types>
        <struct id="_150625144333_0" name="ValueConfig">
            <struct-item id="_151119171759_2" name="refValueType">
                <custom-type-scalar data-type-name-ref="REF_VALUE_TYPE"/>
                <default>CURRENT</default>
            </struct-item>
            <struct-item id="_150625144334_1" name="min">
                <scalar type="double"/>
                <default>0.0</default>
            </struct-item>
            <struct-item id="_150625144334_2" name="max">
                <scalar type="double"/>
                <default>10.0</default>
            </struct-item>
            <struct-item id="_161115154727_0" name="limitMin">
                <scalar type="double"/>
                <default>0.0</default>
            </struct-item>
            <struct-item id="_161115154727_1" name="limitMax">
                <scalar type="double"/>
                <default>10.0</default>
            </struct-item>
            <struct-item id="_151119171759_3" name="scaling">
                <scalar type="double"/>
                <default>1.0</default>
            </struct-item>
        </struct>
...

The struct items can be accessed via the data field:
    const ValueConfig::ValueConfig vc = pDev->acqValue.getCell(i);
    if ( vc.refValueType == REF_VALUE_TYPE::VOLTAGE ) {
        data.setVoltage(pDev->acqDataArray.getCell(i, pEvt->getMultiplexingContext()));
        data.setVoltage_units("V");
    }

Working with FESA 2D-array-fields

uint32_t index1 = 1;
uint32_t index2 = 1;
uint32_t dim1;
uint32_t dim2;

// Get a cell-value
const int32_t myData = (*device)->my2DArray.getCell(index1,index2,pEvt->getMultiplexingContext());

// Set a cell-value
int32_t myNewData;
(*device)->my2DArray.setCell(myNewData,index1,index2,pEvt->getMultiplexingContext());

// Get a row
const int32_t* myData =  (*device)->my2DArray.getRow(index1,dim1,pEvt->getMultiplexingContext());

// Get a column
uint32_t tempArraySize = dim2;
double* column = new double[tempArraySize];
(*device)->myField.getColumn(index2, dim2, column, tempArraySize, pEvt->getMultiplexingContext());

// Set a row/column
uint32_t index = 1;
int32_t myDataColumn[dim2];
int32_t myDataRow[dim1];
(*device)->my2DArray.setColumn(myDataColumn,index,dim2,pEvt->getMultiplexingContext());
(*device)->my2DArray.setRow(myDataRow,index,dim1,pEvt->getMultiplexingContext());

// Get the complete array2D ( use the correct type as template argument )
fesa::ImmutableArray2D<double> data = (*device)->myField.get(pEvt->getMultiplexingContext());
std::cout << " data[2][2]: "<< data[2][2] << std::endl;

// Get the current sizes of the array
(*device)->myField.getSize(dim1, dim2, pEvt->getMultiplexingContext());

// Get the maximum sizes of the array
(*device)->myField.getMaxSize(dim1, dim2);

// Set the sizes of the current array ( Needed when a smaller array is copied to the field! The maximum sizes cannot be exceeded.)
std::size_t newSize1 = 12;
std::size_t newSize2 = 34;
(*device)->myField.setSize(newSize1,newSize2,pEvt->getMultiplexingContext());

For the complete API of 2D-array-fields and it's relatives, check the doxygen documentation.

Working with FESA char-array-fields (strings)

std::string myString;
(*device)->myCharArray.set(myString.c_str(),pEvt->getMultiplexingContext());

const char* myCharArrayPointer = (*device)->myCharArray.get(pEvt->getMultiplexingContext());

For the complete API of string-fields and it's relatives, check the doxygen documentation.

Working with FESA 2D-char-array-fields (string-arrays)

uint32_t index = 0;
std::string myString = "MyString";
(*device)-->myCharArray2D.setString(myString.c_str(), index,pEvt->getMultiplexingContext());

const char* myCharArrayPointer = (*device)->myCharArray2D.getString(index,pEvt->getMultiplexingContext());

// get only the size of the string-array
uint32_t size0 = getSize(pEvt->getMultiplexingContext());

// Iterate on the string-array using a vector of strings
for ( auto string : (*device)->myCharArray2D.getStrings(pEvt->getMultiplexingContext()) )
{
   std::cout <<  string << std::endl;
}

uint32_t size;
const char** my2DCharArrayPointer = (*device)->myCharArray2D.get(size,pEvt->getMultiplexingContext());
std::cout << "Number of strings: " << size << std::endl;
std::cout << "First String: " <<  my2DCharArrayPointer[0] << std::endl;

For the complete API of string-array-fields and it's relatives, check the doxygen documentation.

Working with variable-sized 2D char arrays

// In the design document you can optionally specify the minimum / maximum values, which can be used by the devices
<value-item name="aVarV" direction="OUT">
     <array2D type="char">
         <variable-dim1>
             <min>0</min>
             <max>4000</max>
         </variable-dim1>
        <custom-constant-dim2 constant-name-ref="MAX_STRING_LENGTH" />
     </array2D>
</value-item>

// C++ code:
string aVarTemp[MAX_SIZE];    // declaration of array

// setting part of the array
for(int i=0; i < some_size; i++)        // some_size <= MAX_SIZE
     aVarTemp[i] = "some string";


// filling variable-sized string array
fesa::NoneContext context;
const std::vector<Device*> devCol = this->MyClassServiceLocator_->getDeviceCollection();

std::vector<Device*>::const_iterator device; for(device= devCol.begin();device!=devCol.end();++device) {
     (*device)->aVarV.set(reinterpret_cast<const char**>(&aVarTemp), some_size, &context);
}

Working with variable-sized arrays

In the design document you can optionally specify the minimum / maximum dim-values, which can be used by the devices
<field multiplexed="false" name="aVarF">
  <array type="int16_t">
    <variable-dim>
      <min>0</min>
      <max>20</max>
    </variable-dim>
   </array>
</field>

In the instance document the concrete array-size is specified.
<device-instance name="TestDevice0">
  <configuration>
    <description value=""/>
    <accelerator value="NONE"/>
    <timingDomain value="NONE"/>
    <mainMuxCriterion value="NONE"/>
  ...
 </configuration>
 <acquisition>
   <aVarF>
     <dim value="19"/>
   </aVarF>
 </acquisition>
  ... 
 

Now the variable-sized array may be filled, e.g.
   fesa::NoneContext context;
   const std::vector<Device*> devices = this->MyClassServiceLocator_->getDeviceCollection();
   GlobalDevice* globalDev = this->MyClassServiceLocator_->getGlobalDevice();

   std::vector<Device*>::const_iterator device;
   for(device=devices.begin();device!=devices.end(); ++device)
   {
      std::cout << "max size = " << (*device)->aVarF.getMaxSize() << std::endl; // this is the value as defined in the instance document
      for(int i(0); i<20; i++) {
         std::cout << "set var: " << i << std::endl;
         (*device)->aVarF.setCell(i, i, &context);
      }
   }

C++ 11

        fesa::NoneContext context;
        const std::vector<Device*> devices = this->MyClassServiceLocator_->getDeviceCollection();
        GlobalDevice* globalDev = this->MyClassServiceLocator_->getGlobalDevice();

        for(const Device *device : devices )
        {
           std::cout << "max size = " << device->aVarF.getMaxSize() << std::endl; // this is the value which is defined in the instance document
            for(int i(0); i<20; i++) {
               std::cout << "set var: " << i << std::endl;
               device->aVarF.setCell(i, i, &context);
            }
        }

Working with bit-enum-fields (e.g. bit-enum-16bits, bit-enum-32-bits)

// generated code in TypeDefinition.h (just an example):
namespace MyBitEnum
{
   enum MyBitEnum
   {
      isActive=1,
      isValid=2,
      hasPower=4
   }; // 3 bits 
}

// Access the field in Server/RT-Actions:
int32_t value = MyBitEnum::isActive | MyBitEnum::hasPower;
(*device)->myBitEnumField.set( value, pEvt->getMultiplexingContext() );

int32_t newValue = (*device)->myBitEnumField.get(pEvt->getMultiplexingContext());

Working with hw-address fields

// The concrete values of a hw-address can be set in the instantiation-file for each device

// for  a "logical-address"
// The name of the logical address is defined in the class.
// The logicalUnitNumber, the channel and the moduleType can be filled in the instance-file for each device. (channel and moduleType are optional)
int32_t logicalUnitNumber = (*device)->myLogicalHardwareAddress.get()->logicalUnitNumber;
int32_t channel = (*device)->myLogicalHardwareAddress.get()->channel;
const char* moduleType = (*device)->myLogicalHardwareAddress.get()->moduleType;

// for a "host"
// The (alias)name of a required host can be defined in the class
// The concrete name and port usually are device-specific and can be defined per device in the instance-file
int32_t port = (*device)->myRemoteHost.get()->port;
const char* host = (*device)->myRemoteHost.get()->hostName;

Fill the GSI-acquisition-context-field

// Use this snippet in the RT-Action
// easy method (cyclestamp, cyclename and acquisitionstamp are obtained from the MultiplexingContext, if available)
// When using a context from a TimingEventSource, in addition to the acquisition-stamp all the GSI Timing Context fields are copied
// Note that it usually makes sense to manage a seperate GSI-acquisition-context-field for each property. Otherwise the provided field-data may get wrong in some scenarios.
(*device)->acquisitionContext.insert(pEvt->getMultiplexingContext());

// Advanced method ( usage of self-defined acquisition-stamp )
// Stamp must be given in Nanoseconds - UTC
// E.g. if your hardware provides a precise stamp, you can make use of it here.
// You can as well make use of the system-stamp. The following fesa-method already returns the right format:
#include <fesa-core/Synchronization/Timing.h>
....
int64_t stamp = fesa::getSystemTime(); 
(*device)->acquisitionContext.insert( pEvt->getMultiplexingContext(), stamp );

// If the GSI-acquisition-context-field is not flexible enough for your purpose, in the design document you can disconnect it from the related value-item and fill the following value-items manually in the get-action:
data.setProcessIndex(...);
data.setSequenceIndex(...);
data.setChainIndex(...);
data.setEventNumber(...);
data.setTimingGroupID(...);
data.setAcquisitionStamp(...);
data.setEventStamp(...);
data.setProcessStartStamp(...);
data.setSequenceStartStamp(...); 
data.setChainStartStamp(...);

Report an error by using the GSI-error_collection-field

std::string errorString= "Put in your error-text here";
int32_t error_code= 4711;
(*device)->error_collection.addError(error_code,errorString,pEvt->getMultiplexingContext(),*device);

For the complete API of the class "GSIErrorCollectionField" and it's relatives, check the doxygen documentation.

Usage of the GSI-detailed-status-field

// You can either directly use an index
(*device)->detailedStatus.lower(0,pEvt->getMultiplexingContext());
(*device)->detailedStatus.raise(1,pEvt->getMultiplexingContext());

// Or make use of the labels which you defined in the configuration field 'detailedStatus_labels'
(*device)->detailedStatus.lower("MyLabelBit4",pEvt->getMultiplexingContext());
(*device)->detailedStatus.raise("MyLabelBit5",pEvt->getMultiplexingContext());
(*device)->detailedStatus.set("MyLabelBit6", true, pEvt->getMultiplexingContext());
(*device)->detailedStatus.setBit("MyLabelBit7", false, pEvt->getMultiplexingContext());

The field directly inherits from the Array-Field, so you can as well can use all the methods from the baseclass !

Usage of the GSI-module-status-field

// make use of the labels which you defined in the configuration field 'moduleStatus_labels'
// Available values are defined in the custom-type 'MODULE_STATUS'
(*device)->moduleStatus.set("myModule1", MODULE_STATUS::NOT_AVAILABLE,context);
(*device)->moduleStatus.setCell( MODULE_STATUS::OK ,1,context);

The field directly inherits from the Array-Field, so you can as well can use all the methods from the baseclass !

Trigger the persistence of setting-fields by hand

// The following command will write the values of all setting-fields to a file which have the flag "persistent = true".
// "this->" is not needed here, it just helps to trigger the eclipse-auto-completion

this->serviceLocator_->triggerPersistency();

- Misc -

Usage of class-specific custom-types

// Take a look into "generated/cpp/MyClassName/GeneratedCode/TypeDefinition.h" to see all custom-types, defined by your class
(*device)->control.set(DEVICE_CONTROL::LOCAL,pEvt->getMultiplexingContext());

Usage of client-data in server-actions

// In a Set-Server-Acttion you may want to retrieve the data and the filter, which was send by the client:
bool myData = data.getMyValueItemName();
bool myData = filter.getMyFilterItemName();
 
// In a Get-Server-Action you may want to fill the data-container which will be send to the client.
int32_t myData = 1234;
data.setMyValueItemName(myData );

// For 2D arrays you can make use of the FESA Container-class "ImmutableArray2D":
fesa::ImmutableArray2D<double> myData = data.getMyDoubleArray2D();
std::cout << "myData[2][2]: " << myData[2][2] << std::endl;

Usage of external headers and libraries

# Modify the file "Makefile.specific" of your class / deploy-unit according to your requirements
SOME_LIBRARY_HOME= /path/to/the/library
COMPILER_FLAGS += -I$(SOME_LIBRARY_HOME)/include
LINKER_FLAGS += -L$(SOME_LIBRARY_HOME)/lib/$(CPU) -lsome-library

Usage of the FESA logger / Logging information in FESA software

std::ostringstream message;
message << "Enter your log message here";
LOG_ERROR_IF(logger, message.str());
// Depending on how important your log message is, you should use one of the following macros:
// LOG_TRACE_IF(logger, message.str()); // In FESA classes you should use the diagnostic-logging instead
// LOG_DEBUG_IF(logger, message.str());  // In FESA classes you should use the diagnostic-logging instead
// LOG_INFO_IF(logger, message.str());  // In FESA classes you should use the diagnostic-logging instead
// LOG_WARNING_IF(logger, message.str());
// LOG_ERROR_IF(logger, message.str());

// Diagnostic-Information can be logged like this:
// You can define specific log-traces and enable/disable them during runtime
// Use the global "diagnostic-property" in the FESA Explorer to view and control the diagnostic logging (see the links below)
LOG_DIAG_IF("MyTopic",message.str());
LOG_DIAG_DEVICE_IF("MyTopic",message.str(),(*device));

  • Use the application arguments -v and -vv to control the talkativeness of your FESA software
  • You as well can re-configure the logging configuration by providing a foreign configuration file (application argument -cfglog required during launch of FESA software)
  • To view the log output, login with your ACC account on https://logstash.acc.gsi.de. A usage manual is found here.
  • For detailed information on how to work with logging in FESA, please check the the related wiki page !

How to trigger an on-demand-event manually

// In the FESA class design you have to choose "@automatic = false" on your action/triggered-event-source.
// In the custom-server-/rt- action you can now do the following:
AbstractAction::triggerOnDemandEventSource("MyOnDemandEventSourceName",pEvt->getMultiplexingContext());

// to trigger only automatically triggered sources
AbstractAction::triggerAllAutomaticallyTriggeredOnDemandEventSources(pEvt->getMultiplexingContext());

If required some payload information can be added to the event. Check the payload-section for more information.

For the complete API of the class "AbstractAction" and it's relatives, check the doxygen documentation.

How to trigger a property-notification manually

// The same code can be used in Server- and RT-actions
// Make sure that the names of the property/device you want to notify are correct
// For inherited or composed classes, dont use the className::propertyName format! It is sufficient to only put the property-name.  
std::string notifiedProperty = "MyProperty";
std::string notifiedDevice = "MyDevice";

// You can add any number of property/device-combinations by using the following method:
registerManualNotification(notifiedProperty,notifiedDevice);

// For thesake of simplification, you as well can use the conveniance method
registerManual<Propertname>(pNotifiedDevice);

//If you are finished with adding properties/devices, send the notification by using the method below
sendManualNotification(pEvt->getMultiplexingContext());

Throwing Exceptions to clients

Prefably the type "FesaException" should be used here. ( However as well any type of RDAException will do. ) Either an object of this type can be created directly, or the class developer can inherit from the class, in order to define a class-specific exception-type.
std::string errorMessage = "Something went wrong here!";
//Currently at GSI there are no standards regarding the error-codes
std::string errorCode = "4711";
// errorCategory should be "FESACLASS_" as prefix an the classname.
std::string errorCategory = "FESACLASS_MyFesaClass";

throw fesa::FesaException(errorMessage,__FILE__,__LINE__,errorCode,errorCategory);

Whithout errorCode / errorCategory (note the different order of arguments):
   throw fesa::FesaException(__FILE__,__LINE__,"eat my shorts");

Enable/Disable logical events and event-sources

// By the use of the service-locator, events and sources can be enabled/disabled everywhere in the user-code
// "this->" is not needed here, it just helps to trigger the eclipse-auto-completion

this->serviceLocator_->enableEventSource("MySource");
this->serviceLocator_->disableEventSource("MySource");

this->serviceLocator_->enableRTEvent("MyLogicalEvent");
this->serviceLocator_->disableRTEvent("MyLogicalEvent")

Working with Event-Payload

OnDemand Payload

// If you choose "triggered-event-source/@automatic = false" for the action,
// you can add your own payload to the event. The data-format is a char-array
// the payload parameter is copied during the method call
std::string payload = "myPayload";
AbstractAction::triggerOnDemandEventSource("MyODEventSourceName",pEvt->getMultiplexingContext(),payload.c_str(),payload.size() + 1);

// Please note that for "triggered-event-source/@automatic = true" in server-actions,
// the device-name for which the server-action was triggered is automatically added as payload !

// In the RT-Action you can read-out the defined payload like that:
#include <fesa-core/RealTime/RTEventPayload.h>
....
const char* payload = pEvt->getPayload()->getValue<char>();
// or, if you prefer using a std::string:
std::string payload(pEvt->getPayload()->getValue<char>());

Timing Payload for White Rabbit based timing

// Include this header in the RT-Action
#include <fesa-core-gsi/Synchronization/TimingContextWR.h>
...
// Cast the MultiplexingContext to the right type
const fesaGSI::TimingContextWR* contextWR =  dynamic_cast<const fesaGSI::TimingContextWR*>(pEvt->getMultiplexingContext());

std::cout << "FormatID: " <<  contextWR->getFormatID() << std::endl;
std::cout << "GroupID: " <<  contextWR->getGroupID() << std::endl;
std::cout << "EventNumber: " <<  contextWR->getEventNumber() << std::endl;
std::cout << "SequenceIndex: " <<  contextWR->getSequenceIndex() << std::endl;
std::cout << "ProcessIndex: " <<  contextWR->getProcessIndex() << std::endl;
std::cout << "SequenceCounter: " << contextWR->getSequenceCounter() << std::endl;
std::cout << "BeamProductionChainIndex: " <<  contextWR->getBeamProductionChainIndex() << std::endl;

std::cout << "EventExecutionTimeStamp: " << contextWR->getEventExecutionTimeStamp() << std::endl;
std::cout << "AcquisitionTimestamp: " << contextWR->getAcquisitionTimestamp() << std::endl;
std::cout << "ProcessTimeStamp: " <<  contextWR->getProcessTimeStamp() << std::endl;
std::cout << "SequenceTimeStamp: " <<  contextWR->getSequenceTimeStamp() << std::endl;
std::cout << "BeamProductionChainTimestamp: " <<  contextWR->getBeamProductionChainTimestamp() << std::endl;

std::cout << "isBeamIn: " <<  contextWR->isBeamIn() << std::endl;
std::cout << "isEarly: " <<  contextWR->isEarly() << std::endl;
std::cout << "isLate: " <<  contextWR->isLate() << std::endl;
std::cout << "isDelayed: " <<  contextWR->isDelayed() << std::endl;
std::cout << "isConflict: " <<  contextWR->isConflict() << std::endl;
std::cout << "isTimingError: " <<  contextWR->isTimingError() << std::endl;

//You as well can get the whole payload at once:
fesaGSI::TimingContextWRPayload payload = const_cast<fesaGSI::TimingContextWR*>(contextWR)->getPayload();

More information about White Rabbit based timing can be found here.

OnSubscription Payload

  • First of all, make sure, that the FESA device you want to subscribe to, is listed in the FESA database. More info about this can be found here .
  • Lets assume the FESA-class you want to subscribe to is "ClassB" and the class which establishes the subscription is "ClassA".
  • Further lets assume you want to subscribe to the property "PropertyB" which has the value-items: "ValueItemB1" and "ValueItemB2" which are scalar.
  • In order to use this code snippet, you need to replace all occurences of "ClassA", "ClassB", "PropertyB", "ValueItemB1" and "ValueItemB2" with your concrete names.

FESA stores details of the subscription event in the RT Event Payload To find general information about the subscription event, it must be cast to type OnSubscriptionRTEventPayload To access the data fields, use the templated extract helper function.

// In the RTAction of ClassA which receives the onSubscriptionEvent, add these line to the includes:
#include <ClassB/GeneratedCode/PropertyData.h>
#include <fesa-core/RealTime/OnSubscriptionRTEventPayload.h>
...
// Use the following code in the execute-method
// to extract the payload from the RTEvent
const OnSubscriptionRTEventPayload* payload =  dynamic_cast<const OnSubscriptionRTEventPayload*> (pEvt->getPayload().get());
if (payload == NULL) { throw FesaException(__FILE__, __LINE__, "Payload is not of type OnSubscriptionRTEventPayload"); }
// information about the event:
if (payload->isNormalUpdate() == false) { // handle FIRST or IMMEDIATE updates differently }
std::cout << "Device Name" << payload->getDeviceName();
std::cout << "Property Name" << payload->getPropertyName();
std::cout << "Cycle Name" << payload->getCycleSelectorName();

// to extract the property data from the RTEvent:
std::auto_ptr<const ClassB::PropertyBPropertyData> propData;
try
{
   propData = fesa::OnSubscriptionRTEventPayload::extract<ClassB::PropertyBPropertyData>(*pEvt);
}
catch (const std::exception& ex)
{
    throw FesaException(__FILE__, __LINE__, "Payload extraction to type PropertyBPropertyData failed");
}

// Perform different operations on the data-container "propData". E.g. get the data of the received value-items
// Depending on the type of the value item, the methods of propData look different.
// Take a look at "ClassB/GeneratedCode/PropertyData.h" for details of the API.
std::cout << "Event received. Event-data: " << std::endl;
std::cout << "ValueItemB1: " << propData->getValueItemB1() << std::endl;
std::cout << "ValueItemB1: " << propData->getValueItemB2() << std::endl;

Perform a client get-operation from one FESA class to another

// Lets assume you write code for class "ClassA". And you want to do a "Get" of the property "PropB " of the device "DevB" of class "ClassB"
// PropB has the value items "ItemB1" and "ItemB2"
// First you need to include the following fesa-core header
#include <fesa-core/Proxy/ProxyInterface.h>
// Second you need to include PropertyData.h of ClassB
#include <ClassB/GeneratedCode/PropertyData.h>
...
// If you need to do the "Get" more than once, it may make sense to store this object as class-member
fesa::ProxyInterface rda;

std::string deviceName = "DevB";
std::string cycleSelector = ""; // For multiplexed devices you have to enter the concrete cycle-selector here
std::auto_ptr<const ClassB::PropBPropertyData> ;

// Probably you want to wrap a try / catch around this block later and catch for std::exception
propData = rda.getProperty<const ClassB::PropBPropertyData>(deviceName,cycleSelector);
std::cout << "ItemB1: " << propData->getItemB1() << std::endl;
std::cout << "ItemB2: " << propData->getItemB2() << std::endl;

Perform a client set-operation from one FESA class to another

// Lets assume you write code for class "ClassA". And you want to do a "Set" of the property "PropB " of the device "DevB" of class "ClassB"
// PropB has the value items "ItemB1" and "ItemB2"
// First you need to include the following fesa-core header
#include <fesa-core/Proxy/ProxyInterface.h>
// Second you need to include PropertyData.h of ClassB
#include <ClassB/GeneratedCode/PropertyData.h>
...
// If you need to do the "Get" more than once, it may make sense to store this object as class-member
fesa::ProxyInterface rda;

std::string deviceName = "DevB";
std::string cycleSelector = ""; // For multiplexed devices you have to enter the concrete cycle-selector here
ClassB::PropBPropertyData propData;
double myValue = 42;
propData.setItemB1(myValue);
propData.setItemB2(myValue);

// Probably you want to wrap a try / catch around this block later and catch for std::exception
rda.setProperty<const ClassB::PropBPropertyData>(deviceName,cycleSelector,propData);

Accessing the Cycle Selector in a multiplexed set-action

void SetPropertyname::execute(fesa::RequestEvent* pEvt, Device* pDev, PropertynamePropertyData& data, const PropertynameFilterData& filter)
...
// retreive the Selector that was used to access this multiplexed property - format is FAIR.SELECTOR.P1
std::string cycleSelector = pEvt->getMultiplexingContext()->getCycleName();

Parsing the White Rabbit Cycle Selector String

#include <fesa-core-gsi/Synchronization/Selector.h >
std::string cyclename="FAIR.SELECTOR.S=1:P=2"
fesaGSI::Selector selector(cyclename);
if (selector.isValid())
{
  int s = selector.getSequenceIndex(); // 1
  int p = selector.getProcessIndex();  // 2
}

Read instance file information

// E.g. the TimingDomain defined in the instance-file can be accessed like that:
// ( How to obtain a device-pointer is explained on top )
std::string timingDomain = (*device)->getInstantiationData()->getTimingDomain();

The complete API of the class 'DeviceInstantiationData' can be found here

Read instance file using ParserElement classes

For information that is not available via InstantiationData above the ParserElement classes can be used. For example, to read more about a Timing Event configuration:

#include <fesa-core/Utilities/InstantiationElements/TimingEventElementImpl.h>
std::string className = MyclassServiceLocator_->getClassName();
auto classElement = AbstractRTEquipment::getInstance()->getRTDeviceClass(className)->getInstantiationXMLElement();
auto eventElement = classElement->getActiveEvents("MyLogicalEventName")[0];
auto timingEventElement = boost::dynamic_pointer_cast<fesa::TimingEventElementImpl>(eventElement);
std::string hwname = timingEventElement->getHardwareEventName()

see also https://www-acc.gsi.de/data/documentation/fesa/doxygen/7.0.0/classfesa_1_1EventElementImpl.html

Reject Subscribers

// Each server action provides a method "isFilterValid" (if not (Fesa class created before Fesa 7.0.0 you can add it by hand )
// The doc suggest to return 'false' in order to reject a subscriber .. though in that case the subscriber does not know why it was rejected.
// Luckily it is as well possible to throw an exception at the subsciber to get it rejected and in addition tell the reason:
bool MyGetAction::isFilterValid(const fesa::AbstractDevice& abstractDevice, const AcquisitionDAQFilterData& filter) const
{
    const Device& device = static_cast<const Device&>(abstractDevice);

    if (!(*device)->enabled.get())
        throw fesa::FesaException(__FILE__, __LINE__, "This device is currently disabled");

    if( !filter.isMyFilterAvailable())
        throw fesa::FesaException(__FILE__, __LINE__, "MyFilter is not Available");
   return true;
}

Read Fesa command line arguments

#include <fesa-core/Factory/ControllerFactory.h>
...
const fesa::ProcessConfigurer& processConfigurer = fesa::ControllerFactory::getInstance().getProcessConfigurer();
std::cout << "UsrCmdArgs: " << processConfigurer.getUsrCmdArgs() << std::endl;
std::cout << "Instance File Path: " << processConfigurer.getStringValue(fesa::PropertyTag::INSTANCE_FILE) << std::endl;

A complete list of Property Tags can be found in the fesa sources.
Topic revision: r15 - 22 Aug 2024, TobiasHabermann
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Foswiki? Send feedback