blob: ed02e2d2b5e7d4bde21d2140bd3da6b5aa87f014 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2022 AIT, 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, Alois Zoitl - initial API and implementation and/or initial documentation
* ys guo - Fix opc module compilation errors and deadlock bug
* Tibalt Zhao - Guard the opc connection from requests from FBs,filter non-sense requests
*******************************************************************************/
#include "opcconnection.h"
#include "opcconnectionimpl.h"
#include "opceventhandler.h"
#include "opcprocessvar.h"
#include "Cmd_AddConnection.h"
#include "Cmd_RemoveConnection.h"
#include "Cmd_SetProcessVarValue.h"
using namespace forte::com_infra;
COpcConnection::COpcConnection(const char *paHost, const char *paServerName, COpcEventHandler* paEventHandler) :
mGroupCount(0), mConnectionState(e_Disconnected), mHost(paHost), mServerName(paServerName), mGroupName(0), mReqUpdateRate(0),
mRealUpdateRate(0), mDeadBand(0), mConnected(false), mBlockingConnect(false), mEventHandler(paEventHandler){
mOpcConnectionImpl = new COpcConnectionImpl(paHost, paServerName, this);
mLastEvent.mLastHappenedEvent = e_Idle;
mLastEvent.mSuccess = true;
}
COpcConnection::~COpcConnection(){
delete mOpcConnectionImpl;
}
void COpcConnection::addGroup(const char* paGroupName, unsigned long paReqUpdateRate, float paDeadBand, CComLayer* paComCallback){
DEVLOG_INFO("Add group&opc layer added [0x%lX][%s]\n",paComCallback,paGroupName);
if(ifInGroupList(paGroupName)){
DEVLOG_INFO("group [%s] already in list\n", paGroupName);
DEVLOG_INFO("group map size is %d\n", mOpcGroupMapList.size());
return;
}
CCriticalRegion criticalRegion(mSync);
mOpcGroupMapList.push_back(new SOpcGroupMap(paGroupName, mEventHandler->addComCallback(paComCallback)));
DEVLOG_INFO("group map size is %d\n", mOpcGroupMapList.size());
}
int COpcConnection::removeGroupCallbackDesc(const char * paGroupName){
for(size_t i = 0; i < mOpcGroupMapList.size(); i++){
if(mOpcGroupMapList[i]->mGroupName == paGroupName){
mEventHandler->removeComCallback(mOpcGroupMapList[i]->mCallbackDesc);
DEVLOG_INFO("erase from m_lOpcGroupMapList %d[%s]\n", i,paGroupName);
delete mOpcGroupMapList[i];
mOpcGroupMapList.erase(mOpcGroupMapList.begin()+i);
return 0;
}
}
return -1;
};
void COpcConnection::removeGroup(const char* paGroupName){
DEVLOG_INFO("COpcConnection::removeGroup[%s]\n", paGroupName);
CCriticalRegion criticalRegion(mSync);
if(nullptr == paGroupName){
DEVLOG_INFO("the group list is cleared![%s]\n", paGroupName);
for(unsigned int i = 0; i < mOpcGroupMapList.size(); i++){
removeGroupCallbackDesc(mOpcGroupMapList[i]->mGroupName);
}
} else{
removeGroupCallbackDesc(paGroupName);
}
}
bool COpcConnection::ifLetEventPass(EOpcConnectionEvents paEvent, const char* paGroupName){
switch(mConnectionState){
case e_Disconnected:
return (e_Connect == paEvent);
case e_Connecting:
return (e_Connect == paEvent && !ifInGroupList(paGroupName));
case e_Connected:
if(e_Connect == paEvent){
return true;
}
if(e_DataReceive == paEvent && ifInGroupList(paGroupName)){
return true;
}
if(e_Disconnect == paEvent && ifInGroupList(paGroupName) && mOpcGroupMapList.size() == 1 ||
e_Disconnect == paEvent && mLastEvent.mLastHappenedEvent == e_ItemSend && !mLastEvent.mSuccess){
return true;
}
if(e_ItemSend == paEvent && ifInGroupList(paGroupName)){//and paGroupName is in m_lOpcGroupMapList
return true;
}
break;
case e_Disconnecting:
return (e_Disconnect == paEvent);
}
return false;
}
bool COpcConnection::ifInGroupList(const char* paGroupName){
CCriticalRegion criticalRegion(mSync);
for(auto it = mOpcGroupMapList.begin(); it != mOpcGroupMapList.end(); it++) {
if(0 == strcmp(paGroupName, (*it)->mGroupName)) {
return true;
}
}
return false;
}
int COpcConnection::send_connect(const char *paGroupName, unsigned long paReqUpdateRate, float paDeadBand,
forte::com_infra::CComLayer* paComCallback, std::vector<COpcProcessVar*> paNewItems){
if(ifLetEventPass(e_Connect,paGroupName)){
mConnectionState = e_Connecting;
addGroup(paGroupName, paReqUpdateRate, paDeadBand, paComCallback);
for(size_t i = 0; i < paNewItems.size(); i++) {
maintainGroupMapInfo(paNewItems[i]);
}
mEventHandler->sendCommand(new CCmd_AddConnection(mOpcConnectionImpl, paGroupName, paReqUpdateRate, paDeadBand, paNewItems));
}
else{
DEVLOG_INFO("Connection cmd cannot be sent,state=%d[%s]\n",mConnectionState,paGroupName);
}
return 0;
}
int COpcConnection::send_disconnect(const char *paGroupName){
if(ifLetEventPass(e_Disconnect, paGroupName)){
DEVLOG_INFO("send disconnect cmd, remove group %s\n", paGroupName);
mConnected = false;
removeGroup(paGroupName);
mConnectionState = e_Disconnected;
mEventHandler->sendCommand(new CCmd_RemoveConnection(*mOpcConnectionImpl));
return 0;
}
//remove this group but not affect the connection as there are other fb still using it
DEVLOG_INFO("disconnect cmd cannot pass, there are %d groups left, connnection state=%d[%s]\n",
mOpcGroupMapList.size(),mConnectionState, paGroupName);
DEVLOG_INFO("remove group %s\n", paGroupName);
removeGroup(paGroupName);
return -1 ;
}
int COpcConnection::maintainGroupMapInfo(COpcProcessVar* paNewItem){
CCriticalRegion criticalRegion(mSync);
for(auto group = mOpcGroupMapList.begin(); group != mOpcGroupMapList.end(); ++group){
if(strcmp((*group)->mGroupName, paNewItem->getItemGroupName()) == 0){
if(paNewItem->getItemFunction() == COpcProcessVar::e_FBOutput){
//check if item already added
for(auto item = (*group)->mReadItemsList.begin(); item != (*group)->mReadItemsList.end(); ++item){
if(strcmp((*item)->mItemName, paNewItem->getItemName()) == 0){
return 1;
}
}
(*group)->mReadItemsList.push_back(new SOpcItemData(paNewItem->getItemName()));
break;
}
else if(paNewItem->getItemFunction() == COpcProcessVar::e_FBInput){
//check if item already added
for(auto item = (*group)->mWriteItemsList.begin(); item != (*group)->mWriteItemsList.end(); ++item){
if(strcmp((*item)->mItemName, paNewItem->getItemName()) == 0){
return 1;
}
}
(*group)->mWriteItemsList.push_back(new SOpcItemData(paNewItem->getItemName()));
break;
}
}
}
return 0;
}
int COpcConnection::send_sendItemData(COpcProcessVar* paItem){
if(!ifLetEventPass(e_ItemSend,paItem->getItemGroupName())){
DEVLOG_INFO("Item data cannot be sent,state=%d[%s]\n",mConnectionState, paItem->getItemGroupName());
return -1;
}
mEventHandler->sendCommand(new CCmd_SetProcessVarValue(mOpcConnectionImpl, std::string(paItem->getItemGroupName()),
std::string(paItem->getItemName()),paItem->updateValue()));
return 0;
}
void COpcConnection::response_connect(bool paConnectionState,const char* paGroupName){
maintainStateinEventResponse(e_Connect,paConnectionState);
mConnected = paConnectionState;
if(mOpcGroupMapList.empty()){
this->mOpcConnectionImpl->disconnect();
return;
}
if(!mBlockingConnect && paConnectionState){
for(TOpcGroupMapList::iterator it = mOpcGroupMapList.begin();
it != mOpcGroupMapList.end(); ++it){
if(strcmp((*it)->mGroupName, paGroupName) == 0){
mEventHandler->executeComCallback((*it)->mCallbackDesc);
break;
}
}
}
}
void COpcConnection::response_dataReceived(const char *paGroupName, TItemDataList & paItemDataList){
maintainStateinEventResponse(e_DataReceive,true);
// Loop through OpcGroups
for(auto group = mOpcGroupMapList.begin(); group != mOpcGroupMapList.end(); ++group){
if(strcmp((*group)->mGroupName, paGroupName) == 0){
// Loop through OpcItems in OpcGroup
for(auto item = (*group)->mReadItemsList.begin(); item != (*group)->mReadItemsList.end(); ++item){
// Loop through OpcItems in ItemDataList
for(auto newItem = paItemDataList.begin(); newItem != paItemDataList.end(); ++newItem){
if(strcmp((*newItem)->mItemName, (*item)->mItemName) == 0){
(*item)->mItemData = (*newItem)->mItemData;
paItemDataList.erase(newItem);
break;
}
}
}
// Notify Com Layer
mEventHandler->executeComCallback((*group)->mCallbackDesc);
break;
}
}
}
void COpcConnection::response_dataSent(const char* paGroupName,const char* paItemName, bool paSuccess){
// Loop through OpcGroups
CCriticalRegion criticalRegion(mSync);
TOpcGroupMapList::iterator itEnd_group = mOpcGroupMapList.end();
for(TOpcGroupMapList::iterator it_group = mOpcGroupMapList.begin(); it_group != itEnd_group; ++it_group){
//if(strcmp((*it_group)->mGroupName, pa_pOpcItem->getItemGroupName()) == 0){
if(strcmp((*it_group)->mGroupName, paGroupName) == 0){
// Change state
if (paSuccess) {
DEVLOG_INFO("COpcConnection::response_dataSent():data sent OK: %s[%s]\n", paItemName,paGroupName);
maintainStateinEventResponse(e_ItemSend,true);
} else {
DEVLOG_INFO("COpcConnection::response_dataSent():data sent failed: %s[%s]\n", paItemName,paGroupName);
maintainStateinEventResponse(e_ItemSend,false);
}
// Notify Com Layer
mEventHandler->executeComCallback((*it_group)->mCallbackDesc);
break;
}
}
}
int COpcConnection::receiveData(const char* paGroupName, TOpcProcessVarList * paOpcProcessVarList){
int rData = 0;
// TODO Case when lists do not match in size
CCriticalRegion criticalRegion(mSync);
TOpcGroupMapList::iterator itEnd_group = mOpcGroupMapList.end();
for(TOpcGroupMapList::iterator it_group = mOpcGroupMapList.begin(); it_group != itEnd_group; ++it_group){
if(strcmp((*it_group)->mGroupName, paGroupName) == 0){
TItemDataList::iterator itEnd_item = (*it_group)->mReadItemsList.end();
TItemDataList::iterator it_item = (*it_group)->mReadItemsList.begin();
TOpcProcessVarList::iterator itEnd_procVar = paOpcProcessVarList->end();
TOpcProcessVarList::iterator it_procVar = paOpcProcessVarList->begin();
while(it_procVar != itEnd_procVar && it_item != itEnd_item){
(*it_procVar)->setNewValue((*it_item)->mItemData);
rData++;
++it_procVar;
++it_item;
}
break;
}
}
return rData;
}