Skip to content

Commit

Permalink
OPC DA: Upgrade to OPCClientToolKit with 64bit support
Browse files Browse the repository at this point in the history
Depend on 32/64bit OPCClientToolKit:
https://github.com/kumajaya/OPC-Client-X64.git

This upgrade includes:
* Source code adjustments due to library changes
* Completely disconnect a client from the server when disconnecting
* Remove FB group and items on terminated
* Update installation instructions
  • Loading branch information
kumajaya committed Mar 22, 2024
1 parent 38049e0 commit b54c8cc
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 85 deletions.
7 changes: 4 additions & 3 deletions src/com/opc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#*******************************************************************************
# Copyright (c) 2012, 2021 AIT, ACIN, fortiss GmbH, Hit robot group
# Copyright (c) 2012, 2024 AIT, ACIN, fortiss GmbH, Hit robot group, Samator Indo Gas
# 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.
Expand All @@ -9,6 +9,7 @@
# Contributors:
# Filip Andren, Alois Zoitl - initial API and implementation and/or initial documentation
# Tibalt Zhao - Ease the workload to compile OPC DA
# Ketut Kumajaya - switch to OPCClientToolKit with 64bit support
# *******************************************************************************/
#############################################################################
# OPC Com Layer
Expand All @@ -31,9 +32,9 @@ if(FORTE_COM_OPC)

forte_add_include_directories( ${FORTE_COM_OPC_BOOST_ROOT} )

forte_add_include_directories( ${FORTE_COM_OPC_LIB_ROOT}/include )
forte_add_include_directories( ${FORTE_COM_OPC_LIB_ROOT}/include/OPCClientToolKit )
forte_add_link_directories( ${FORTE_COM_OPC_LIB_ROOT}/lib )

forte_add_link_library( OPCClientToolKit.lib )
forte_add_link_library( OPCClientToolKit )
endif(FORTE_COM_OPC)
endif("${FORTE_ARCHITECTURE}" STREQUAL "Win32")
4 changes: 2 additions & 2 deletions src/com/opc/opccomlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@ EComResponse COpcComLayer::processInterrupt(){
switch (mConnectionState){
case e_Connected: {
CIEC_ANY **apoRDs = mFb->getRDs();
unsigned int nrRDs = mFb->getNumRD();
size_t nrRDs = mFb->getNumRD();

TOpcProcessVarList::iterator itEnd = mFBOutputVars.end();
TOpcProcessVarList::iterator it = mFBOutputVars.begin();
for(unsigned int i = 0; i < nrRDs && it != itEnd; i++, ++it){
for(size_t i = 0; i < nrRDs && it != itEnd; i++, ++it){
setOutputValue(&apoRDs[i]->unwrap(), &(*it)->updateValue());
}

Expand Down
2 changes: 2 additions & 0 deletions src/com/opc/opcconnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ void COpcConnection::removeGroup(const char* paGroupName){
} else{
removeGroupCallbackDesc(paGroupName);
}
//remove fb group and all group items
this->mOpcConnectionImpl->removeGroup(paGroupName);
}

bool COpcConnection::ifLetEventPass(EOpcConnectionEvents paEvent, const char* paGroupName){
Expand Down
118 changes: 79 additions & 39 deletions src/com/opc/opcconnectionimpl.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2012, 2022 AIT, ACIN, HIT robot group
* Copyright (c) 2012, 2024 AIT, ACIN, HIT robot group, Samator Indo Gas
* 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.
Expand All @@ -9,6 +9,7 @@
* Contributors:
* Filip Andren, Alois Zoitl - initial API and implementation and/or initial documentation
* Tibalt Zhao - add the list of items instead of add item one by one
* Ketut Kumajaya - switch to OPCClientToolKit with 64bit support
*******************************************************************************/
#include "opcconnectionimpl.h"
#include "../../arch/devlog.h"
Expand All @@ -22,6 +23,9 @@
#include "OPCGroup.h"
#include "OPCItem.h"

#define S2WS(x) COPCHost::S2WS(x)
#define WS2S(x) COPCHost::WS2S(x)

COpcConnectionImpl::COpcConnectionImpl(const char *paHost, const char *paServerName, COpcConnection* paOpcConn) :
mOpcConn(paOpcConn), mOpcHost(0), mOpcServer(0),mConnected(0), mHost(paHost), mServerName(paServerName),
mGroupName(0), mReqUpdateRate(0), mRealUpdateRate(0), mDeadBand(0) {
Expand All @@ -39,33 +43,39 @@ void COpcConnectionImpl::disconnect(){//const char* paGroupName){
DEVLOG_INFO("COpcConnectionImpl disconnect\n");
if (isConnected())
{
COPCClient::stop();
mConnected = false;
this->clearGroup();
if (mOpcHost != nullptr) {
delete mOpcHost;
mOpcHost = nullptr;
}

if (mOpcServer != nullptr) {
delete mOpcServer;
mOpcServer = nullptr;
}

COPCClient::stop();
mConnected = false;
}
}

bool COpcConnectionImpl::connect(const char* paGroupName){
if(isConnected()){
DEVLOG_DEBUG("COpcConnectionImpl::connect: already connected[%s]\n",paGroupName);
return true;
DEVLOG_DEBUG("COpcConnectionImpl::connect: already connected[%s]\n",paGroupName);
return true;
}

try{
DEVLOG_INFO("try to connect OPC server in COpcConnectionImpl[%s]\n",paGroupName);
HRESULT result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if(result != S_FALSE && result != S_OK){
if (true != COPCClient::init(OPCOLEInitMode::MULTITHREADED)) {
DEVLOG_ERROR("CoInitializeEx init failed\n");
return false;
}

COPCClient::init();

mOpcHost = COPCClient::makeHost(mHost);
mOpcHost = COPCClient::makeHost(S2WS(std::string(mHost)));

mOpcServer = mOpcHost->connectDAServer(mServerName);
mOpcServer = mOpcHost->connectDAServer(S2WS(std::string(mServerName)));
} catch (OPCException &e){
DEVLOG_ERROR("connect OPC server failed:%s[%s]\n",(LPCTSTR)(e.reasonString()),paGroupName);
DEVLOG_ERROR("connect OPC server failed:%s[%s]\n",WS2S(e.reasonString()).c_str(),paGroupName);
return false;
}
DEVLOG_INFO("successfully connect OPC server in COpcConnectionImpl[%s]\n",paGroupName);
Expand All @@ -78,14 +88,14 @@ void COpcConnectionImpl::addItemList(const char* paGroupName, std::vector<std::s
//we assume all the items in pa_lNewItems are of same group
const char * groupName = nullptr;
TItemDataList itemList;
unsigned int nreadSize = paReadItems.size();
DEVLOG_INFO("adding items in COpcConnectionImpl[%s]\n", paGroupName);
size_t readSize = paReadItems.size();
//now paReadItems contains read and write items
paReadItems.insert(paReadItems.end(), paWriteItems.begin(), paWriteItems.end());
for(size_t i = 0; i < paReadItems.size(); i++){
groupName = paGroupName;
DEVLOG_INFO("adding item %s in COpcConnectionImpl[%s]\n", paReadItems[i].c_str(),groupName);
COPCGroup *itemGroup = nullptr;
if(i + 1 > nreadSize){
if(i + 1 > readSize){
itemGroup = getOpcGroup(groupName, false);
}
else{
Expand All @@ -100,18 +110,19 @@ void COpcConnectionImpl::addItemList(const char* paGroupName, std::vector<std::s
return;
}

ATL::CString itemName(paReadItems[i].c_str());
DEVLOG_DEBUG("adding item %s in COpcConnectionImpl[%s]\n", paReadItems[i].c_str(), WS2S(itemGroup->getName()).c_str());
std::wstring itemName(S2WS(paReadItems[i]));
try
{
COPCItem *newItem = itemGroup->addItem(itemName, true);
//pa_pNewItem->setIsActive(true);
mOpcItems[itemGroup->getName()].push_back(newItem);
mOpcItems[WS2S(itemGroup->getName()).c_str()].push_back(newItem);
}
catch (OPCException &e)
{
DEVLOG_ERROR("addItem failed with exception:%s[%s:%s]\n", (LPCTSTR)(e.reasonString()),
DEVLOG_ERROR("addItem failed with exception:%s[%s:%s]\n", WS2S(e.reasonString()).c_str(),
groupName,paReadItems[i].c_str());
if(strcmp((LPCTSTR)(e.reasonString()),"Failed to add item") != 0){
if(strcmp(WS2S(e.reasonString()).c_str(),"COPCGroup::addItem: FAILED to add item") != 0){
//pa_pNewItem->setIsActive(false);
this->disconnect();
mConnected = false;
Expand All @@ -132,7 +143,6 @@ void COpcConnectionImpl::addItemList(const char* paGroupName, std::vector<std::s
if(!itemList.empty()){
mOpcConn->response_dataReceived(groupName,itemList);
}

}

bool COpcConnectionImpl::addGroup(const char* paGroupName, unsigned long paReqUpdateRate, float paDeadBand){
Expand All @@ -152,6 +162,29 @@ bool COpcConnectionImpl::addGroup(const char* paGroupName, unsigned long paReqUp
return true;
}

void COpcConnectionImpl::removeItems(const char* paGroupName){
if(nullptr == paGroupName){
DEVLOG_INFO("COpcConnectionImpl::removeItems: group name is nullptr\n");
return;
}
DEVLOG_INFO("removing items in COpcConnectionImpl[%s]\n", paGroupName);
std::vector<COPCItem *> items;

TOpcItemsIt it = mOpcItems.find(paGroupName);
if(it != mOpcItems.end()){
items = it->second;
for(size_t i = 0; i < items.size(); i++){
DEVLOG_DEBUG("removing item %s in COpcConnectionImpl[%s]\n", WS2S(items[i]->getName()).c_str(), paGroupName);
delete items[i];
}
items.clear();
mOpcItems.erase(it);
}
else{
DEVLOG_ERROR("there is no item in group:%s\n", paGroupName);
}
}

void COpcConnectionImpl::removeGroup(const char* paGroupName){
if(paGroupName == nullptr){
DEVLOG_INFO("nullptr is passed to removeGroup,clear the group\n");
Expand All @@ -161,6 +194,8 @@ void COpcConnectionImpl::removeGroup(const char* paGroupName){
}
for(auto group = mOpcGroupSettingsList.begin(); group != mOpcGroupSettingsList.end();){
if(nullptr == paGroupName || 0 == strcmp((*group)->mGroupName, paGroupName)){
removeItems(WS2S((*group)->mOpcGroupRead->getName()).c_str());
removeItems(WS2S((*group)->mOpcGroupWrite->getName()).c_str());
delete (*group)->mOpcGroupRead;
delete (*group)->mOpcGroupWrite;
group = mOpcGroupSettingsList.erase(group);
Expand Down Expand Up @@ -191,22 +226,22 @@ int COpcConnectionImpl::sendItemData(const char *paGroupName, const char *paItem
}
int rtn = -1;
DEVLOG_INFO("sendItemData in COpcConnectionImpl[%s:%s]\n",paGroupName,paItemName);
std::vector<COPCItem *> lItems;
std::vector<COPCItem *> items;

char *writeGrpName= (char*) malloc(strlen(paGroupName) + 6 + 1);
strcpy(writeGrpName, paGroupName);
strcat(writeGrpName, "_write");

TOpcItemsIt it = mOpcItems.find(writeGrpName);
if(it != mOpcItems.end()){
lItems = it->second;
for(size_t i = 0; i < lItems.size(); i++){
if(0 == strcmp((LPCTSTR)(lItems[i]->getName()), paItemName)){
items = it->second;
for(size_t i = 0; i < items.size(); i++){
if(0 == strcmp(WS2S(items[i]->getName()).c_str(), paItemName)){
try{
lItems[i]->writeSync(paVar);
items[i]->writeSync(paVar);
}
catch (OPCException &e){
DEVLOG_ERROR("opcitem writesync failed with exception:%s[%s:%s]\n", (LPCTSTR)(e.reasonString()), writeGrpName, paItemName);
DEVLOG_ERROR("opcitem writesync failed with exception:%s[%s:%s]\n", WS2S(e.reasonString()).c_str(), writeGrpName, paItemName);
rtn = -1;
break;
}
Expand All @@ -224,17 +259,22 @@ int COpcConnectionImpl::sendItemData(const char *paGroupName, const char *paItem
return 0;
}

void COpcConnectionImpl::OnDataChange(COPCGroup & paGroup, CAtlMap<COPCItem *, OPCItemData *> & paChanges){
void COpcConnectionImpl::OnDataChange(COPCGroup & paGroup, COPCItemDataMap & paChanges){
TItemDataList itemList;
for(POSITION pos = paChanges.GetStartPosition(); pos != nullptr;){
OPCItemData *itemData = paChanges.GetValueAt(pos);
COPCItem *item = paChanges.GetNextKey(pos);
itemList.push_back(new SOpcItemData((LPCTSTR) (item->getName()), (Variant) itemData->vDataValue));
POSITION pos = paChanges.GetStartPosition();
while(pos){
OPCItemData *data = paChanges.GetNextValue(pos);
if (data) {
const COPCItem *item = data->item();
if (item) {
itemList.push_back(new SOpcItemData(WS2LPCTSTR(item->getName()), (Variant) data->vDataValue));
}
}
}

const char *c_groupName = (const char*) paGroup.getName();
const char *c_groupName = WS2LPCTSTR(paGroup.getName());

int position = 0;
long long position = 0;
const char * subStrRead = strstr(c_groupName, "_read");
if(subStrRead != nullptr) {
position = subStrRead - c_groupName;
Expand Down Expand Up @@ -264,12 +304,12 @@ COPCGroup* COpcConnectionImpl::getOpcGroup(const char* paGroupName, bool paIfRea
strcpy(groupName, paGroupName);
strcat(groupName, "_read");
try{
(*it)->mOpcGroupRead = retGroup = mOpcServer->makeGroup(groupName, true, (*it)->mReqUpdateRate, (*it)->mRevisedUpdateRate, (*it)->mDeadBand);
(*it)->mOpcGroupRead->enableAsynch(*this);
(*it)->mOpcGroupRead = retGroup = mOpcServer->makeGroup(S2WS(std::string(groupName)), true, (*it)->mReqUpdateRate, (*it)->mRevisedUpdateRate, (*it)->mDeadBand);
(*it)->mOpcGroupRead->enableAsync(this);
(*it)->mReadGroupAdded = true;
} catch (OPCException &e){
// TODO
DEVLOG_ERROR("exception in make opc group[%s]:%s\n",groupName,(LPCTSTR)(e.reasonString()));
DEVLOG_ERROR("exception in make opc group[%s]:%s\n",groupName,WS2S(e.reasonString()).c_str());
(*it)->mOpcGroupRead = nullptr;
retGroup = nullptr;
}
Expand All @@ -283,11 +323,11 @@ COPCGroup* COpcConnectionImpl::getOpcGroup(const char* paGroupName, bool paIfRea
strcpy(groupName, paGroupName);
strcat(groupName, "_write");
try{
(*it)->mOpcGroupWrite = retGroup = mOpcServer->makeGroup(groupName, true, (*it)->mReqUpdateRate, (*it)->mRevisedUpdateRate, (*it)->mDeadBand);
(*it)->mOpcGroupWrite = retGroup = mOpcServer->makeGroup(S2WS(std::string(groupName)), true, (*it)->mReqUpdateRate, (*it)->mRevisedUpdateRate, (*it)->mDeadBand);
(*it)->mWriteGroupAdded = true;
} catch (OPCException &e){
// TODO
DEVLOG_ERROR("exception in make opc group[%s]:%s\n",groupName,(LPCTSTR)(e.reasonString()));
DEVLOG_ERROR("exception in make opc group[%s]:%s\n",groupName,WS2S(e.reasonString()).c_str());
(*it)->mOpcGroupWrite = nullptr;
(*it)->mOpcGroupWrite = nullptr;
retGroup = nullptr;
Expand Down
21 changes: 18 additions & 3 deletions src/com/opc/opcconnectionimpl.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2012, 2022 AIT, fortiss GmbH, HIT robot group
* Copyright (c) 2012, 2024 AIT, fortiss GmbH, HIT robot group, Samator Indo Gas
* 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.
Expand All @@ -9,6 +9,7 @@
* Contributors:
* Filip Andren, Alois Zoitl - initial API and implementation and/or initial documentation
* Tibalt Zhao - add the list of items instead of add item one by one
* Ketut Kumajaya - switch to OPCClientToolKit with 64bit support
*******************************************************************************/
#ifndef OPCCONNECTIONIMPL_H_
#define OPCCONNECTIONIMPL_H_
Expand All @@ -23,7 +24,7 @@

class COpcConnection;

class COpcConnectionImpl : public IAsynchDataCallback{
class COpcConnectionImpl : public IAsyncDataCallback{
public:
COpcConnectionImpl(const char *paHost, const char *paServerName, COpcConnection* paOpcConn);
~COpcConnectionImpl();
Expand All @@ -33,6 +34,7 @@ class COpcConnectionImpl : public IAsynchDataCallback{
void addItemList(const char* paGroupName, std::vector<std::string> paReadItems,std::vector<std::string> paWriteItems);
bool addGroup(const char* paGroupName, unsigned long paReqUpdateRate, float paDeadBand);
void removeGroup(const char* paGroupName);
void removeItems(const char* paGroupName);


int sendItemData(const char*paGroupName, const char* paItemName, Variant paVar);
Expand All @@ -47,10 +49,23 @@ class COpcConnectionImpl : public IAsynchDataCallback{

bool isConnected();

virtual void COpcConnectionImpl::OnDataChange(COPCGroup &paGroup, CAtlMap<COPCItem *, OPCItemData *> &paChanges);
virtual void COpcConnectionImpl::OnDataChange(COPCGroup &paGroup, COPCItemDataMap &paChanges);

private:

static const char* WS2LPCTSTR(const std::wstring &wstr) {
std::mbstate_t state = std::mbstate_t();
const wchar_t *input = wstr.c_str();
// Count required buffer size (plus one for null-terminator).
size_t size = (wcslen(input) + 1) * sizeof(wchar_t);
char *buffer = new char[size];
std::wcsrtombs(buffer, &input, size, &state);
const char *ret = (const char*) buffer;
// Free allocated memory:
delete[] buffer;
return ret;
}

COPCGroup* getOpcGroup(const char* paGroupName, bool paIfRead);
void clearGroup();

Expand Down
Loading

0 comments on commit b54c8cc

Please sign in to comment.