blob: 66e9b615fcd1846b9c5459a540445c55fb10f21a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2022 AIT, ACIN, fortiss GmbH, Hit robot group
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Filip Andren, Patrick Smejkal, Alois Zoitl, Martin Melik-Merkumians - initial API and implementation and/or initial documentation
* ys guo - Fix opc module compilation errors and deadlock bug
* Tibalt Zhao -Merge additem into Connect and take advantage of detailed event info from opcconnection
*******************************************************************************/
#include "opccomlayer.h"
#include "../../arch/devlog.h"
#include "commfb.h"
#include "opcconnection.h"
#include "opcconnectionhandler.h"
#include "Variant.h"
#include <criticalregion.h>
using namespace forte::com_infra;
COpcComLayer::COpcComLayer(CComLayer* paUpperLayer, CBaseCommFB* paComFB) :
CComLayer(paUpperLayer, paComFB), mHost(0), mServerName(0), mUpdateRate(0), mDeadBand(0),
mLayerParamsOK(false), mOpcConnection(0), mInterruptResp(e_Nothing){
mOpcGroupName = mFb->getInstanceName();
DEVLOG_DEBUG("new OpcComLayer: [0x%lX][%s]\n", this, mOpcGroupName);
}
COpcComLayer::~COpcComLayer(){
DEVLOG_DEBUG("delete OpcComLayer: [0x%lX][%s]\n", this, mOpcGroupName);
for(auto &it :mFBInputVars){
delete it;
}
for(auto &it :mFBOutputVars){
delete it;
}
}
EComResponse COpcComLayer::sendData(void *paData, unsigned int paSize){
EComResponse eRetVal = e_ProcessDataOk;
if((0 != mFb)){
if(mOpcConnection->getConnectionState() == COpcConnection::e_Connected ||
mOpcConnection->getConnectionState() == COpcConnection::e_Connecting){
switch (mFb->getComServiceType()){
case e_Server:
//TODO
break;
case e_Client: {
convertInputData(paData, paSize);
TOpcProcessVarList::iterator itEnd = mFBInputVars.end();
for(TOpcProcessVarList::iterator it = mFBInputVars.begin(); it != itEnd; ++it){
int rtn = mOpcConnection->send_sendItemData((*it));
if(rtn != 0){
// data send failed
DEVLOG_DEBUG("send_sendItemData failed[%s]\n", mFb->getInstanceName());
eRetVal = e_ProcessDataSendFailed;
return eRetVal;
}
}
break;
}
case e_Publisher:
//do nothing as OPC cannot be publisher
break;
case e_Subscriber:
//do nothing as subscribers do not send data
break;
}
}
else{
DEVLOG_DEBUG("COpcComLayer::sendData():OPC connection is not connected[%s]\n", mFb->getInstanceName());
eRetVal = e_ProcessDataSendFailed;
}
}
return eRetVal;
}
EComResponse COpcComLayer::processInterrupt(){
EComResponse eRet = e_Nothing;
EComResponse currentComResponse;
{
CCriticalRegion criticalRegion(mSync);
if(mComResponses.empty()){
return eRet;
}
TComResponseList::iterator comIt(mComResponses.begin());
currentComResponse = *comIt;
mComResponses.pop_front();
}
switch (currentComResponse){
case e_ProcessDataOk:
switch (mConnectionState){
case e_Connected: {
CIEC_ANY *apoRDs = mFb->getRDs();
unsigned int nrRDs = mFb->getNumRD();
TOpcProcessVarList::iterator itEnd = mFBOutputVars.end();
TOpcProcessVarList::iterator it = mFBOutputVars.begin();
for(unsigned int i = 0; i < nrRDs && it != itEnd; i++, ++it){
setOutputValue(&apoRDs[i], &(*it)->updateValue());
}
break;
}
case e_Disconnected:
case e_Listening:
case e_ConnectedAndListening:
default:
break;
}
eRet = e_ProcessDataOk;
break;
case e_ProcessDataSendFailed:
eRet = e_ProcessDataSendFailed;
break;
case e_InitInvalidId:
eRet = e_InitInvalidId;
break;
case e_InitTerminated:
eRet = e_InitTerminated;
break;
}
return eRet;
}
EComResponse COpcComLayer::recvData(const void *, unsigned int){
EComResponse eRet = e_Nothing;
switch (mFb->getComServiceType()){
case e_Server:
//TODO
break;
case e_Client: {
COpcConnection::EOpcConnectionEvents opcConnEvent = mOpcConnection->getLastEvent().mLastHappenedEvent;
bool success = mOpcConnection->getLastEvent().mSuccess;
switch (opcConnEvent){
case COpcConnection::e_Disconnect:
//TODO error during connection try
break;
case COpcConnection::e_Connect:
if (success) {
mConnectionState = e_Connected;
}
else {
{
CCriticalRegion criticalRegion(mSync);
mComResponses.push_back(e_InitTerminated);
}
mFb->interruptCommFB(this);
eRet = e_InitTerminated;
}
break;
case COpcConnection::e_ItemSend:{
if(!success){
DEVLOG_DEBUG("opccomlayer:recvData():OPC Item Send Failed[%s]\n",mFb->getInstanceName());
{
CCriticalRegion criticalRegion(mSync);
mComResponses.push_back(e_ProcessDataSendFailed);
}
mFb->interruptCommFB(this);
eRet = e_ProcessDataSendFailed;
break;
}
}
break;
case COpcConnection::e_DataReceive: {
if(success){
int nRetVal = mOpcConnection->receiveData(mOpcGroupName, &mFBOutputVars);
if (nRetVal > 0) {
//we successfully received data
CCriticalRegion criticalRegion(mSync);
mComResponses.push_back(e_ProcessDataOk);
}
mFb->interruptCommFB(this);
eRet = e_ProcessDataOk;
}
break;
}
default:
break;
}
break;
}
case e_Publisher:
//do nothing as publisher cannot receive data
case e_Subscriber:
//do nothing as OPC cannot be subscribers
break;
}
return eRet;
}
EComResponse COpcComLayer::openConnection(char *paLayerParameter){
EComResponse eRetVal = e_InitInvalidId;
mConnectionState = e_Disconnected;
switch (mFb->getComServiceType()){
case e_Server:
//TODO
break;
case e_Client:
processClientParams(paLayerParameter);
if(mLayerParamsOK){
eRetVal = e_InitOk;
mOpcConnection = COpcConnectionHandler::getInstance().getOpcConnection(mHost, mServerName, this);
TOpcProcessVarList lAllVars;
lAllVars.insert(lAllVars.end(), mFBInputVars.begin(), mFBInputVars.end());
lAllVars.insert(lAllVars.end(), mFBOutputVars.begin(), mFBOutputVars.end());
mOpcConnection->send_connect(mOpcGroupName, mUpdateRate, mDeadBand, this, lAllVars);
eRetVal = e_InitOk;
}
break;
case e_Publisher:
//do nothing, OPC cannot be publisher
break;
case e_Subscriber:
//do nothing, OPC cannot be subscriber
break;
}
return eRetVal;
}
void COpcComLayer::closeConnection(){
DEVLOG_DEBUG("COpcComLayer::closeConnection()[%s] \n", mFb->getInstanceName());
COpcConnectionHandler::getInstance().removeOpcConnection(mOpcConnection->getHost(), mOpcConnection->getServerName(), mOpcGroupName);
mConnectionState = e_Disconnected;
}
void COpcComLayer::setOutputValue(CIEC_ANY *paDataOut, Variant * paValue){
switch (paDataOut->getDataTypeID()){
case CIEC_ANY::e_BOOL:
paDataOut->setValue(CIEC_BOOL(paValue->get<bool>()));
break;
case CIEC_ANY::e_SINT:
paDataOut->setValue(CIEC_SINT(paValue->get<TForteInt8>()));
break;
case CIEC_ANY::e_INT:
paDataOut->setValue(CIEC_INT(paValue->get<TForteInt16>()));
break;
case CIEC_ANY::e_DINT:
paDataOut->setValue(CIEC_DINT(paValue->get<TForteInt32>()));
break;
case CIEC_ANY::e_LINT:
paDataOut->setValue(CIEC_LINT(paValue->get<TForteInt64>()));
break;
case CIEC_ANY::e_USINT:
paDataOut->setValue(CIEC_USINT(paValue->get<TForteUInt8>()));
break;
case CIEC_ANY::e_UINT:
paDataOut->setValue(CIEC_UINT(paValue->get<TForteUInt16>()));
break;
case CIEC_ANY::e_UDINT:
paDataOut->setValue(CIEC_UDINT(paValue->get<TForteUInt32>()));
break;
case CIEC_ANY::e_ULINT:
paDataOut->setValue(CIEC_ULINT(paValue->get<TForteUInt64>()));
break;
case CIEC_ANY::e_BYTE:
paDataOut->setValue(CIEC_BYTE(paValue->get<TForteByte>()));
break;
case CIEC_ANY::e_WORD:
paDataOut->setValue(CIEC_WORD(paValue->get<TForteWord>()));
break;
case CIEC_ANY::e_DWORD:
paDataOut->setValue(CIEC_DWORD(paValue->get<TForteDWord>()));
break;
case CIEC_ANY::e_LWORD:
paDataOut->setValue(CIEC_LWORD(paValue->get<TForteLWord>()));
break;
case CIEC_ANY::e_REAL:
paDataOut->setValue(CIEC_REAL(paValue->get<TForteFloat>()));
break;
case CIEC_ANY::e_LREAL:
paDataOut->setValue(CIEC_LREAL(paValue->get<TForteDFloat>()));
break;
default:
//TODO
break;
}
}
void COpcComLayer::processClientParams(char* paLayerParams){
char *chrStorage;
char *chrHost;
char *chrServer;
char *temp;
// Get Host
chrStorage = strchr(paLayerParams, ':');
if(chrStorage == 0) {
return;
}
*chrStorage = '\0';
++chrStorage;
chrHost = (char*) malloc(strlen(paLayerParams) + 1);
strcpy(chrHost, paLayerParams);
if(strcmp(chrHost, "127.0.0.1") == 0 || strcmp(chrHost, "localhost") == 0) {
mHost = "";
} else {
mHost = chrHost;
}
// Get server name
temp = chrStorage;
chrStorage = strchr(chrStorage, ':');
if(chrStorage == 0){
if (chrHost){
free(chrHost);
}
return;
}
*chrStorage = '\0';
++chrStorage;
chrServer = (char*) malloc(strlen(temp) + 1);
strcpy(chrServer, temp);
mServerName = chrServer;
// Get update rate
mUpdateRate = atol(chrStorage);
chrStorage = strchr(chrStorage, ':');
if(chrStorage == 0){
if (chrHost){
free(chrHost);
}
return;
}
*chrStorage = '\0';
++chrStorage;
// Get dead band
mDeadBand = (float) atof(chrStorage);
chrStorage = strchr(chrStorage, ':');
if(chrStorage == 0){
if (chrHost){
free(chrHost);
}
return;
}
*chrStorage = '\0';
++chrStorage;
// Get FB input items
char * inputItems = chrStorage;
chrStorage = strchr(chrStorage, ':');
if(chrStorage == 0){
if (chrHost){
free(chrHost);
}
return;
}
*chrStorage = '\0';
++chrStorage;
int nrItems = 0;
char *pch;
pch = strtok(inputItems, ",");
while(pch != nullptr){
char *itemName = (char*) malloc(strlen(pch) + 1);
strcpy(itemName, pch);
mFBInputVars.push_back(new COpcProcessVar(mOpcGroupName, itemName, COpcProcessVar::e_FBInput));
nrItems++;
pch = strtok(nullptr, ",");
}
// Get FB output items
pch = strtok(chrStorage, ",");
while(pch != nullptr){
char *itemName = (char*) malloc(strlen(pch) + 1);
strcpy(itemName, pch);
mFBOutputVars.push_back(new COpcProcessVar(mOpcGroupName, itemName, COpcProcessVar::e_FBOutput));
nrItems++;
pch = strtok(nullptr, ",");
}
if(nrItems > 0) {
mLayerParamsOK = true;
}
}
void COpcComLayer::convertInputData(void *paData, unsigned int paSize){
CIEC_ANY *sDs = static_cast<CIEC_ANY*>(paData);
unsigned int nrSDs = paSize;
unsigned int sdIndex = 0;
TOpcProcessVarList::iterator itVar = mFBInputVars.begin();
while(sdIndex < nrSDs && itVar != mFBInputVars.end()){
CIEC_ANY *dataIn = &sDs[sdIndex];
Variant newVariant;
getInputValueSize(dataIn, &newVariant);
(*itVar)->setNewValue(newVariant);
++itVar;
++sdIndex;
}
}
template<typename T>
void COpcComLayer::getInputValue(void * paData, Variant * paNewValue){
T* vData = static_cast<T*>(paData);
T data = vData[0];
paNewValue->set<T>(data);
}
unsigned int COpcComLayer::getInputValueSize(CIEC_ANY* paData, Variant * paNewValue){
switch (paData->getDataTypeID()){
case CIEC_ANY::e_BOOL:
{
paNewValue->set<bool>((bool) *(dynamic_cast<CIEC_BOOL*>(paData)));
return sizeof(bool);
}
case CIEC_ANY::e_SINT:
{
paNewValue->set<CHAR>((CHAR) *(dynamic_cast<CIEC_SINT*>(paData)));
return sizeof(TForteInt8);
}
case CIEC_ANY::e_INT:
{
CIEC_INT* tempInt = dynamic_cast<CIEC_INT*>(paData);
TForteInt16 forteInt = (TForteInt16) (*tempInt);
paNewValue->set<TForteInt16>(forteInt);
return sizeof(TForteInt16);
}
case CIEC_ANY::e_DINT:
{
paNewValue->set<TForteInt32>((TForteInt32) *(dynamic_cast<CIEC_DINT*>(paData)));
return sizeof(TForteInt32);
}
case CIEC_ANY::e_LINT:
{
paNewValue->set<TForteInt64>((TForteInt64) *(dynamic_cast<CIEC_LINT*>(paData)));
return sizeof(TForteInt64);
}
case CIEC_ANY::e_USINT:
{
paNewValue->set<TForteUInt8>((TForteUInt8) *(dynamic_cast<CIEC_USINT*>(paData)));
return sizeof(TForteUInt8);
}
case CIEC_ANY::e_UINT:
{
paNewValue->set<TForteUInt16>((TForteUInt16) *(dynamic_cast<CIEC_UINT*>(paData)));
return sizeof(TForteUInt16);
}
case CIEC_ANY::e_UDINT:
{
paNewValue->set<TForteUInt32>((TForteUInt32) *(dynamic_cast<CIEC_UDINT*>(paData)));
return sizeof(TForteUInt32);
}
case CIEC_ANY::e_ULINT:
{
paNewValue->set<TForteUInt64>((TForteUInt64) *(dynamic_cast<CIEC_ULINT*>(paData)));
return sizeof(TForteUInt64);
}
case CIEC_ANY::e_BYTE:
{
paNewValue->set<TForteByte>((TForteByte) *(dynamic_cast<CIEC_BYTE*>(paData)));
return sizeof(TForteByte);
}
case CIEC_ANY::e_WORD:
{
paNewValue->set<TForteWord>((TForteWord) *(dynamic_cast<CIEC_WORD*>(paData)));
return sizeof(TForteWord);
}
case CIEC_ANY::e_DWORD:
{
paNewValue->set<TForteDWord>((TForteDWord) *(dynamic_cast<CIEC_DWORD*>(paData)));
return sizeof(TForteDWord);
}
case CIEC_ANY::e_LWORD:
{
paNewValue->set<TForteLWord>((TForteLWord) *(dynamic_cast<CIEC_LWORD*>(paData)));
return sizeof(TForteLWord);
}
case CIEC_ANY::e_REAL:
{
paNewValue->set<TForteFloat>((TForteFloat) *(dynamic_cast<CIEC_REAL*>(paData)));
return sizeof(TForteFloat);
}
case CIEC_ANY::e_LREAL:
{
paNewValue->set<TForteDFloat>((TForteDFloat) *(dynamic_cast<CIEC_LREAL*>(paData)));
return sizeof(TForteDFloat);
}
default:
//TODO
break;
}
return 0;
}