SILECS C++ Code Snippets
- General -
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)
// Repalce "MyClass" with the name of your class
// include the silecs header
#include <MyClass/Common/MyClass.h>
...
RTDeviceClass::~RTDeviceClass()
{
MyClass::cleanup();
}
void RTDeviceClass::specificInit()
{
MyClass::setup(this->MyClassServiceLocator_);
}
- Access of PLC-blocks -
Working with READ-ONLY blocks
Best read the PLC-values periodically in the RT-Action:
// include the silecs header
#include <MyClass/Common/MyClass.h>
...
// Repalce "MyClass" with the name of your class
// Repalce "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)
{
MyClass::MyBlock.getOneDevice((*device),true,pEvt->getMultiplexingContext());
}
If the Block is connected to a FESA-Property, you can 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" 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)
{
MyClass::MyBlock.getOneDevice(pDev,true,pEvt->getMultiplexingContext());
}
Working with WRITE-ONLY blocks
TODO: Actually a WRITE-ONLY block should generate a FESA-command property ... currently a setting property is generated:
See bugzilla bug
So currently, if the Silecs-block is connected to a FESA property, best change the type of the property to "command-property", or create a fresh command-property and copy the needed value-items to there. If preferred you instead can throw an exception when the Get-Server-Action is triggered.
The plc-value of WRITE-ONLY blocks can be written for all devices in a FESA RT-Action:
// include the silecs header
#include <MyClass/Common/MyClass.h>
...
// Repalce "MyClass" with the name of your class
// Repalce "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)
{
pDev->myRegister.set(data.getMyRegisterSet(),pEvt->getMultiplexingContext()); // first update the FESA-field with the incoming values
MyClass::MyBlock.setOneDevice(pDev,true,pEvt->getMultiplexingContext());
}
For WRITE-ONLY fields there should be no need to set the field-attribute "shared" to false.
Working with READ-WRITE blocks
In order to use the different Silecs-Methods, you currently need to add the attribute @data-consistent=false to each FESA-field which refers to a RW-register. Caution: Only use this attribute together with @shared=false in order to dont have the risk of concurrent access. On the server-side, use custom-server-actions in order to get/set the PLC-register.
For reading a READ-WRITE block, please refer to the
the documentation of READ-ONLY blocks
For writing a READ-WRITE block, please refer to the
the documentation of WRITE-ONLY blocks
Reading out the current PLC run-state
//currently it is required to decode the returned status-integer by hand. For Siemens PLC's the following codes are used by step7:
#define S7CpuStatusUnknown 0x00
#define S7CpuStatusRun 0x08
#define S7CpuStatusStop 0x04
....
// Repalce "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::Cluster* cluster = SIS100InjKicker::theCluster();
Silecs::PLC* pPLC = cluster->getPLC((*deviceMaster)->plcHostName.get(),(*deviceMaster)->parameterFile.get());
Silecs::UnitStatusType statusStruct;
pPLC->recvUnitStatus(statusStruct); // will throw exception on fail
switch (statusStruct.status)
{
case S7CpuStatusRun : std::cout << "PLC Status is: RUN" << std::endl; break;
case S7CpuStatusStop: std::cout << "PLC Status is: STOP" << std::endl; break;
default : std::cout << "PLC Status is: UNKNOWN" << std::endl); break;
}
}
Like the PLC run-state, as well the other plc parameters can be obtained. Here the available methods on plc level:
...
Silecs::UnitCodeType unitCodeStruct;
int ret = pPLC->recvUnitCode(unitCodeStruct);
Silecs::CPUInfoType cpuInfoStruct;
int ret = pPLC->recvCPUInfo(cpuInfoStruct);
Silecs::CPInfoType cpInfoStruct;
int ret = pPLC->recvCPInfo(cpInfoStruct);
...
for a full List of all available actions on plc-level, check:
https://sourceforge.net/p/silecs/git/ci/gsi/tree/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsPLC.h
Get 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
// Repalce "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 = cluster->getPLC((*deviceMaster)->plcHostName.get(),(*deviceMaster)->parameterFile.get());
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 local block-data
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();
}
Dependency in Makefile.specific
In order to build and links against the silecs library and snap7 you will need to add some line to the FESA makefile
Makefile.specific. Replace the concrete version with the one you want to use.
For fesa classes:
...
SILECS_VERSION ?= 2.1.3
SILECS_BASE ?= /common/usr/cscofe/silecs
SILECS_COM ?= $(SILECS_BASE)/silecs-communication-cpp/$(SILECS_VERSION)
COMPILER_FLAGS += -I$(SILECS_COM)/include
...
For fesa deploy units:
...
SILECS_VERSION ?= 2.1.3
SILECS_BASE ?= /common/usr/cscofe/silecs
SILECS_COM ?= $(SILECS_BASE)/silecs-communication-cpp/$(SILECS_VERSION)
SILECS_SNAP7 ?= $(SILECS_BASE)/snap7/${SILECS_VERSION}
LINKER_FLAGS += -L$(SILECS_COM)/lib/$(CPU) -lsilecs-comm
LINKER_FLAGS += -L$(SILECS_SNAP7)/bin/$(CPU)-linux -lsnap7
#add default search path for dynamic snap7 library
LINKER_FLAGS += -Wl,-rpath,$(SILECS_SNAP7)/bin/$(CPU)-linux,-rpath,/usr/lib
...