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:
* Set OPCClientToolKit as FORTE sub project and then dynamically
  link it to FORTE
* 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 9, 2024
1 parent 4e7c9db commit 5e5d7d9
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 76 deletions.
16 changes: 11 additions & 5 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 - set OPCClientToolKit as an external sub project
# *******************************************************************************/
#############################################################################
# OPC Com Layer
Expand All @@ -31,9 +32,14 @@ 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_link_directories( ${FORTE_COM_OPC_LIB_ROOT}/lib )

forte_add_link_library( OPCClientToolKit.lib )
forte_add_include_directories( ${FORTE_COM_OPC_LIB_ROOT} )
if(EXISTS ${FORTE_COM_OPC_LIB_ROOT}/CMakeLists.txt)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
add_subdirectory( ${FORTE_COM_OPC_LIB_ROOT} OPCClientToolKit )
else()
message(SEND_ERROR "FORTE_COM_OPC_LIB_ROOT not set or a compatible OPCClientToolKit does not exist")
endif()
forte_add_link_library( OPCClientToolKit )
install(TARGETS OPCClientToolKit RUNTIME DESTINATION bin)
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
3 changes: 3 additions & 0 deletions src/com/opc/opcconnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ int COpcConnection::send_disconnect(const char *paGroupName){
mOpcGroupMapList.size(),mConnectionState, paGroupName);
DEVLOG_INFO("remove group %s\n", paGroupName);
removeGroup(paGroupName);
//remove fb goup and all group items
this->mOpcConnectionImpl->removeGroup(paGroupName);
this->mOpcConnectionImpl->removeItems(paGroupName);
return -1 ;
}

Expand Down
105 changes: 78 additions & 27 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,11 @@
#include "OPCGroup.h"
#include "OPCItem.h"

#define S2WS(x) COPCHost::S2WS(x)
#define LPCSTR2WS(x) COPCHost::LPCSTR2WS(x)
#define S2LPCTSTR(x) COPCHost::S2LPCTSTR(x)
#define WS2LPCTSTR(x) COPCHost::WS2LPCTSTR(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 @@ -42,6 +48,16 @@ void COpcConnectionImpl::disconnect(){//const char* paGroupName){
COPCClient::stop();
mConnected = false;
this->clearGroup();

if (mOpcHost) {
delete mOpcHost;
mOpcHost = nullptr;
}

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

Expand All @@ -53,19 +69,16 @@ bool COpcConnectionImpl::connect(const char* paGroupName){

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(LPCSTR2WS(mHost));

mOpcServer = mOpcHost->connectDAServer(mServerName);
mOpcServer = mOpcHost->connectDAServer(LPCSTR2WS(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",WS2LPCTSTR(e.reasonString()),paGroupName);
return false;
}
DEVLOG_INFO("successfully connect OPC server in COpcConnectionImpl[%s]\n",paGroupName);
Expand All @@ -78,7 +91,7 @@ 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();
size_t nreadSize = 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++){
Expand All @@ -100,18 +113,17 @@ void COpcConnectionImpl::addItemList(const char* paGroupName, std::vector<std::s
return;
}

ATL::CString itemName(paReadItems[i].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[WS2LPCTSTR(itemGroup->getName())].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", WS2LPCTSTR(e.reasonString()),
groupName,paReadItems[i].c_str());
if(strcmp((LPCTSTR)(e.reasonString()),"Failed to add item") != 0){
if(strcmp(WS2LPCTSTR(e.reasonString()),"Failed to add item") != 0){
//pa_pNewItem->setIsActive(false);
this->disconnect();
mConnected = false;
Expand Down Expand Up @@ -201,12 +213,12 @@ int COpcConnectionImpl::sendItemData(const char *paGroupName, const char *paItem
if(it != mOpcItems.end()){
lItems = it->second;
for(size_t i = 0; i < lItems.size(); i++){
if(0 == strcmp((LPCTSTR)(lItems[i]->getName()), paItemName)){
if(0 == strcmp(WS2LPCTSTR(lItems[i]->getName()), paItemName)){
try{
lItems[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", WS2LPCTSTR(e.reasonString()), writeGrpName, paItemName);
rtn = -1;
break;
}
Expand All @@ -224,15 +236,20 @@ 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;
const char * subStrRead = strstr(c_groupName, "_read");
Expand Down Expand Up @@ -264,12 +281,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(LPCSTR2WS(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,WS2LPCTSTR(e.reasonString()));
(*it)->mOpcGroupRead = nullptr;
retGroup = nullptr;
}
Expand All @@ -283,11 +300,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(LPCSTR2WS(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,WS2LPCTSTR(e.reasonString()));
(*it)->mOpcGroupWrite = nullptr;
(*it)->mOpcGroupWrite = nullptr;
retGroup = nullptr;
Expand All @@ -304,3 +321,37 @@ COPCGroup* COpcConnectionImpl::getOpcGroup(const char* paGroupName, bool paIfRea

return retGroup;
}

void COpcConnectionImpl::removeItems(const char *paGroupName){
if(paGroupName == nullptr){
DEVLOG_ERROR("nullptr is passed to COpcConnectionImpl::removeItems\n");
return;
}
DEVLOG_INFO("removeItems in COpcConnectionImpl[%s]\n",paGroupName);

char *readGrpName= (char*) malloc(strlen(paGroupName) + 5 + 1);
strcpy(readGrpName, paGroupName);
strcat(readGrpName, "_read");

TOpcItemsIt it = mOpcItems.find(readGrpName);
if(it != mOpcItems.end()){
mOpcItems.erase(it);
}
else{
DEVLOG_ERROR("there is no item in group:%s\n", readGrpName);
}
free(readGrpName);

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

it = mOpcItems.find(writeGrpName);
if(it != mOpcItems.end()){
mOpcItems.erase(it);
}
else{
DEVLOG_ERROR("there is no item in group:%s\n", writeGrpName);
}
free(writeGrpName);
}
9 changes: 5 additions & 4 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,21 +9,21 @@
* 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_

#include "opcprocessvar.h"
// Includes from OPC library
#include "windows.h"
#include "opcda.h"
#include "OPCClient.h"
#include <vector>
#include <map>

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 +33,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,7 +48,7 @@ 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:

Expand Down
42 changes: 4 additions & 38 deletions src/com/opc/readme.txt
Original file line number Diff line number Diff line change
@@ -1,45 +1,11 @@
Installation Instructions
The OPC com layer requires the following packages
- OPC Client library release 0.4 (http://sourceforge.net/projects/opcclient/)
- OPC Client library (https://github.com/kumajaya/OPC-Client-X64.git)
- Boost Lexical Cast (http://www.boost.org)

Before OPC Client is compiled the function init() in OPCClient.cpp must be changed from:
void COPCClient::init()
{
HRESULT result = CoInitialize(NULL);
if (FAILED(result))
{
throw OPCException("CoInitialize failed");
}

CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_NONE, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);

result = CoGetMalloc(MEMCTX_TASK, &iMalloc);
if (FAILED(result))
{
throw OPCException("CoGetMalloc failed");
}
}

to:
void COPCClient::init()
{
CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_NONE, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);

HRESULT result = CoGetMalloc(MEMCTX_TASK, &iMalloc);
if (FAILED(result))
{
throw OPCException("CoGetMalloc failed");
}
}


Once the OPC Client library is compiled performe the steps below:
1. Place OPC Client library in the following folder structure:
<opcclientroot>/include - all headers should be placed here
<opcclientroot>/lib - OPCClientToolkit.lib
2. Choose <opcclientroot> for FORTE_COM_OPC_LIB_ROOT in CMake
3. Choose Boost root folder <boostroot> for FORTE_COM_OPC_BOOST_ROOT
Once the two libraries above are available locally, performe the steps below:
1. Choose <opcclientroot>/OPCClientToolKit for FORTE_COM_OPC_LIB_ROOT in CMake
2. Choose Boost root folder <boostroot> for FORTE_COM_OPC_BOOST_ROOT
(the lexical_cast.hpp header must be available in <boostroot>/boost)

Parameter Documentation (all values are required)
Expand Down

0 comments on commit 5e5d7d9

Please sign in to comment.