SILECS C++ Code Snippets

- General -

Dependency in Makefile.specific

Starting with Silecs Version 2.5.0, Silecs will automatically generate a 'Makefile.silecs' which will be included inside 'Makefile.specific'. No manual editing is required any more.

Specific Init

In order to make the generated silecs-code work, you need to initialize it in the specifc-init of your class (usually in RealTime/ RTDeviceClass.cpp)
// Replace "MyClass" with the name of your class

// include the silecs header
#include <MyClass/Common/MyClass.h>
...
RTDeviceClass::~RTDeviceClass()
{
   MyClass::cleanup();
}

void RTDeviceClass::specificInit()
{
   try
   {
      if ( !MyClass::isInitialized() )
         MyClass::setup(this->MyClassServiceLocator_);
   }
   catch(std::exception& ex)
   {
       // required to trace failures on logstash.acc.gsi.de
       LOG_ERROR_IF(logger, ex.what());
       throw;
   }
   catch(...)
   {
       LOG_ERROR_IF(logger, "Unexpected error. Please notify support");
       throw;    
   }
}

- Access of PLC-blocks -

Working with Acquisition blocks

Acquisition blocks are filled by the PLC. The client(FESA) only can read them out.

Best practise is to read the PLC-values periodically in the RT-Action. If possible, only notify FESA-client if a value changed.

// include the silecs header
#include <MyClass/Common/MyClass.h>
...

// Replace "MyClass" with the name of your class
// Replace "MyBlock" with the name of the block you want to read
std::vector<Device*> devCol = this->MyClassServiceLocator_->getDeviceCollection();
std::vector<Device*>::iterator device;
for(device=devCol.begin();device!=devCol.end();++device)
{
   try
   {
      MyClass::MyBlock.getOneDevice((*device),true,pEvt->getMultiplexingContext());
   
       // When using the GSI Template, you at least may want to set the acquisitionContext
       // (otherwise you will get Exceptions during client get/subscription)
      (*device)->acquisitionContext.insert(pEvt->getMultiplexingContext());
   }
   catch(std::exception& ex)
   {
      // required to trace failures on logstash.acc.gsi.de
      // Use error_collection.addError over LOG_ERROR_IF whenever possible, since it adds device-info and the error as well can be read out by fex
      std::string message =  ex.what();
      (*device)->error_collection.addError(4711,message, pEvt->getMultiplexingContext(), *device);
      throw;
   }
   catch(...)
   {
      // Use error_collection.addError over LOG_ERROR_IF whenever possible, since it adds device-info and the error as well can be read out by fex
      std::string message =  "Unexpected error. Please notify support";
      (*device)->error_collection.addError(4711, message, pEvt->getMultiplexingContext(), *device);
      throw;    
   }
}

If the Block is connected to a FESA-Property, you can as well read the PLC value in the FESA Get-Server-Action of that Property instead. In order to do so, set the FESA field-attribute "shared" and "persistent" to "false". Please note that in this case, the FESA-field will not be available on the FESA RT-side.
<field name="myRegister" multiplexed="false" persistent="false" shared="false">
   <scalar type="int16_t" />
</field>
#include <MyClass/Common/MyClass.h>
...
void getMyBlock::execute(fesa::RequestEvent* pEvt, Device* pDev, myBlockPropertyData& data)
{
    ...
    // Surround this statement with try/catch and make use of LOG_ERROR_IF in order to profit from logstash.acc.gsi.de
    MyClass::MyBlock.getOneDevice(pDev,true,pEvt->getMultiplexingContext());
    ...
}

Working with Command blocks

Command Blocks should only be modified by the client(FESA), not by the PLC itself ! A command-block is sent to the PLC by the client, however it is not possible to read-back the value of the block's registers from the PLC. A Command block generates a FESA-command property

The plc-value of Command blocks can be written for all devices in a RT-Action:

// include the silecs header
#include <MyClass/Common/MyClass.h>
...

try { ... // Surround the code with try/catch and make use of LOG_ERROR_IF in order to profit from logstash.acc.gsi.de
// Replace "MyClass" with the name of your class
// Replace "MyBlock" with the name of the block you want to write
std::vector<Device*> devCol = this->MyClassServiceLocator_->getDeviceCollection();
std::vector<Device*>::iterator device;
for(device=devCol.begin();device!=devCol.end();++device)
{
   MyClass::MyBlock.setOneDevice((*device),true,pEvt->getMultiplexingContext());
}

Or for a specific device in a Set-Server-Action:

#include <MyClass/Common/MyClass.h>
...
void setMyBlock::execute(fesa::RequestEvent* pEvt, Device* pDev, myBlockPropertyData& data)
{
    ...
    // Surround the code with try/catch and make use of LOG_ERROR_IF in order to profit from logstash.acc.gsi.de
    pDev->myRegister.set(data.getMyRegisterSet(),pEvt->getMultiplexingContext()); // first update the FESA-field with the incoming values
    MyClass::MyBlock.setOneDevice(pDev,true,pEvt->getMultiplexingContext());
    ...
}

Working with Setting blocks

Setting Blocks should only be modified by the client(FESA), not by the PLC itself ! The client is able to read back the value which was set.

For Setting-Blocks Usually it makes sense to get/set the PLC-Blocks only in the FESA-Server-Actions.

If you want to read the value of a Setting Block in the RT-Action, dont read the whole block via e.g. "getOneDevice(...)"! Instead read only the specific register you are interested in. (Otherwise you will receive a FESA-Exception for writing to a Setting field in a RT-Action)

For reading a Setting block, please refer to the the documentation of Acquisition blocks

For writing a Setting block, please refer to the the documentation of Command blocks

Reading out the current PLC run-state

try { ... // Surround the code with try/catch and make use of LOG_ERROR_IF in order to profit from logstash.acc.gsi.de
// Replace "MyClass" with the name of your class
std::vector<Device*> devCol = this->MyClassServiceLocator_->getDeviceCollection();
std::vector<Device*>::iterator device;
for(device=devCol.begin();device!=devCol.end();++device)
{
   Silecs::PLC* pPLC = MyClass::getPLC(*device);
   std::cout << " IsRunning: " << pPLC->isRunning() << std::endl;
}
...

Usage of the class SilecsPLC

The silecs internal class "SilecsPLC" provides many options which can be used. Here just one example.

To see all provided methods, check: https://sourceforge.net/p/silecs/git/ci/gsi/tree/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsPLC.h

try { ... // Surround the code with try/catch and make use of LOG_ERROR_IF in order to profit from logstash.acc.gsi.de
// Replace "MyClass" with the name of your class
for(device=devCol.begin();device!=devCol.end();++device)
{
   Silecs::PLC* pPLC = MyClass::getPLC(*device);
   std::string model = pPLC->getModel();
   pPLC->sendPlcStop();
   pPLC->sendColdRestart();
   ...
}
...

Get/Set a specific PLC-register

The silecs internal class "SilecsRegister" provides many options which can be used. Here just one example.

To see all provided methods, check: https://sourceforge.net/p/silecs/git/ci/gsi/tree/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsRegister.h

try { ... // Surround the code with try/catch and make use of LOG_ERROR_IF in order to profit from logstash.acc.gsi.de
// Replace "MyClass" with the name of your class
// Replace "MyClass" with the name of your class
// Replace "myRegisterName" with the name of your register
// Replace "MySettingBlock" with the name of your block
for(device=devCol.begin();device!=devCol.end();++device)
{
   Silecs::PLC* pPLC = MyClass::getPLC(*device);
   Silecs::Device* plcDevice = pPLC->getDevice((*deviceMaster)->plcDeviceLabel.get());
   Silecs::Register* plcRegister = plcDevice->getRegister("myRegisterName");

   // BYTE values internally are stored as uchar ... check the conversion List on SilecsRegister.h to see what is the correct data-type to use here
   plcDevice->recv("MySettingBlock");//update data from PLC for a specific block
   int value = (int)plcRegister->getValUChar();
   std::cout << " old value: " << (int)value << std::endl;
   plcRegister->setValUChar((unsigned char)(value + 1)); // set new value in silecs
   plcDevice->send("MySettingBlock");//send data to PLC for a specific block
   plcDevice->recv("MySettingBlock");//update data from PLC for a specific block
   std::cout << " new value: " << (int)plcRegister->getValUChar() << std::endl;
   //oder auch:
   plcRegister->printVal();
}
Topic revision: r2 - 04 May 2023, AlexanderSchwinn
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