blob: fda7bbda353cdf9b545f3199b1925d51bbebf180 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018 - 2019 fortiss GmbH
*
* 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:
* Jose Cabral, Kirill Dorofeev - initial implementation
*******************************************************************************/
#include "opcua_remote_handler.h"
#include "../../core/devexec.h"
#include "../../core/iec61131_functions.h"
#include "../../core/cominfra/basecommfb.h"
#include <criticalregion.h>
#include <forte_printer.h>
using namespace forte::com_infra;
DEFINE_HANDLER(COPC_UA_Remote_Handler);
COPC_UA_Client_IterationList::COPC_UA_Client_IterationList() :
mNewClientsPresent(false) {
}
COPC_UA_Client_IterationList::~COPC_UA_Client_IterationList() {
}
void COPC_UA_Client_IterationList::startIterationThread() {
if(!isAlive()) {
start();
}
}
void COPC_UA_Client_IterationList::stopIterationThread() {
setAlive(false);
resumeIterationLoop();
end();
}
void COPC_UA_Client_IterationList::addClient(CUA_ClientInformation& paClientInformation) {
if(paClientInformation.isClientValid()) {
CCriticalRegion iterationListRegion(getNewClientsMutex());
addClientToList(paClientInformation, getNewClients());
mNeedsIteration.inc();
mNewClientsPresent = true;
}
}
void COPC_UA_Client_IterationList::removeClient(CUA_ClientInformation& paClientInformation) {
CCriticalRegion iterationListRegion(getIterationClientsMutex());
CCriticalRegion newClientsRegion(getNewClientsMutex());
getNewClients().erase(&paClientInformation); //client could still be in the newList
getIterationClients().erase(&paClientInformation);
}
void COPC_UA_Client_IterationList::run() {
while(isAlive()) {
if(mNewClientsPresent) {
updateClientList();
}
bool needsRetry = handleClients();
if(isAlive()) {
if(needsRetry) {
mNeedsIteration.timedWait(scmNanosecondsToSleep);
} else {
mNeedsIteration.waitIndefinitely();
}
}
}
}
void COPC_UA_Client_IterationList::resumeIterationLoop() {
mNeedsIteration.inc();
}
void COPC_UA_Client_IterationList::addClientToList(CUA_ClientInformation &paClientInformation, CSinglyLinkedList<CUA_ClientInformation*> &paList) const {
bool elementAlreadyPresent = false;
for(CSinglyLinkedList<CUA_ClientInformation *>::Iterator itClientInformation = paList.begin(); itClientInformation != paList.end(); ++itClientInformation) {
if(&paClientInformation == (*itClientInformation)) {
elementAlreadyPresent = true;
break;
}
}
if(!elementAlreadyPresent) {
paList.pushBack(&paClientInformation);
}
}
void COPC_UA_Client_IterationList::updateClientList() {
CCriticalRegion iterationListRegion(getIterationClientsMutex());
CCriticalRegion newClientsRegion(getNewClientsMutex());
for(CSinglyLinkedList<CUA_ClientInformation*>::Iterator itClientInformation = getNewClients().begin(); itClientInformation != getNewClients().end();
++itClientInformation) {
addClientToList(**itClientInformation, getIterationClients());
}
getNewClients().clearAll();
mNewClientsPresent = false;
}
// ***************** CLIENT HANDLER ************* //
COPC_UA_Remote_Handler::COPC_UA_Remote_Handler(CDeviceExecution& paDeviceExecution) :
COPC_UA_HandlerAbstract(paDeviceExecution), mConnectionHandler(*this) {
}
COPC_UA_Remote_Handler::~COPC_UA_Remote_Handler() {
mConnectionHandler.stopIterationThread();
stopIterationThread();
cleanResources();
}
void COPC_UA_Remote_Handler::enableHandler(void) {
startIterationThread();
mConnectionHandler.startIterationThread();
}
void COPC_UA_Remote_Handler::disableHandler(void) {
mConnectionHandler.stopIterationThread();
stopIterationThread();
}
UA_StatusCode COPC_UA_Remote_Handler::initializeAction(CActionInfo& paActionInfo) {
enableHandler();
UA_StatusCode retVal = UA_STATUSCODE_BADINTERNALERROR;
switch(paActionInfo.getAction()){
case CActionInfo::eRead:
case CActionInfo::eWrite:
case CActionInfo::eCallMethod:
case CActionInfo::eSubscribe:
retVal = getClientAndAddAction(paActionInfo);
break;
case CActionInfo::eCreateMethod:
case CActionInfo::eCreateObject:
case CActionInfo::eDeleteObject:
DEVLOG_ERROR("[OPC UA REMOTE]: Cannot perform action %s remotely. Initialization failed\n", CActionInfo::mActionNames[paActionInfo.getAction()]);
break;
default:
DEVLOG_ERROR("[OPC UA REMOTE]: Unknown action %d to be initialized\n", paActionInfo.getAction());
break;
}
return retVal;
}
UA_StatusCode COPC_UA_Remote_Handler::executeAction(CActionInfo& paActionInfo) {
CUA_ClientInformation *clientInfo = getClient(paActionInfo.getEndpoint());
UA_StatusCode retVal = UA_STATUSCODE_BADINTERNALERROR;
if(clientInfo && clientInfo->isActionInitialized(paActionInfo)) {
switch(paActionInfo.getAction()){
case CActionInfo::eRead:
retVal = clientInfo->executeRead(paActionInfo);
break;
case CActionInfo::eWrite:
retVal = clientInfo->executeWrite(paActionInfo);
break;
case CActionInfo::eCallMethod:
retVal = clientInfo->executeCallMethod(paActionInfo);
break;
default: //eCreateMethod, eCreateObject, eDeleteObject will never reach here since they weren't initialized. eSubscribe is a Subscribe FB
DEVLOG_ERROR("[OPC UA REMOTE]: Action %d to be executed is unknown or invalid\n", paActionInfo.getAction());
break;
}
} else {
DEVLOG_ERROR("[OPC UA REMOTE]: Cannot execute action from FB %s. It was not properly initialized\n",
paActionInfo.getLayer().getCommFB()->getInstanceName());
}
if(UA_STATUSCODE_GOOD == retVal) {
resumeIterationLoop();
}
return retVal;
}
UA_StatusCode COPC_UA_Remote_Handler::uninitializeAction(CActionInfo& paActionInfo) {
UA_StatusCode retVal = UA_STATUSCODE_BADINTERNALERROR;
switch(paActionInfo.getAction()){
case CActionInfo::eRead:
case CActionInfo::eWrite:
case CActionInfo::eCallMethod:
case CActionInfo::eSubscribe:
removeActionFromClient(paActionInfo);
retVal = UA_STATUSCODE_GOOD;
break;
default:
DEVLOG_ERROR("[OPC UA REMOTE]: Action %d to be uninitialized is unknown or invalid\n", paActionInfo.getAction());
break;
}
return retVal;
}
void COPC_UA_Remote_Handler::cleanResources() {
CCriticalRegion criticalRegion(mAllClientListMutex);
for(CSinglyLinkedList<CUA_ClientInformation*>::Iterator itClientInformation = mAllClients.begin(); itClientInformation != mAllClients.end();
++itClientInformation) {
mConnectionHandler.removeClient(**itClientInformation);
removeClient(**itClientInformation);
delete *itClientInformation;
}
mAllClients.clearAll();
}
UA_StatusCode COPC_UA_Remote_Handler::getClientAndAddAction(CActionInfo& paActionInfo) {
UA_StatusCode retVal = UA_STATUSCODE_BADINTERNALERROR;
const CUA_ClientInformation *clientInfo = getClient(paActionInfo.getEndpoint());
if(clientInfo) {
addActionToClient(paActionInfo);
retVal = UA_STATUSCODE_GOOD;
}
return retVal;
}
CUA_ClientInformation* COPC_UA_Remote_Handler::getClient(const CIEC_STRING &paEndpoint) {
CCriticalRegion allClientsRegion(mAllClientListMutex);
CUA_ClientInformation *client = 0;
for(CSinglyLinkedList<CUA_ClientInformation *>::Iterator itClientInformation = mAllClients.begin(); itClientInformation != mAllClients.end();
++itClientInformation) {
if((*itClientInformation)->getEndpoint() == paEndpoint) {
client = (*itClientInformation);
break;
}
}
if(!client) {
client = new CUA_ClientInformation(paEndpoint);
if(client->configureClient()) {
mAllClients.pushBack(client);
} else {
delete client;
client = 0;
}
}
return client;
}
void COPC_UA_Remote_Handler::addActionToClient(CActionInfo& paActionInfo) {
CCriticalRegion allClientsRegion(mAllClientListMutex);
for(CSinglyLinkedList<CUA_ClientInformation *>::Iterator itClientInformation = mAllClients.begin(); itClientInformation != mAllClients.end();
++itClientInformation) {
CCriticalRegion clientRegion((*itClientInformation)->getMutex());
if((*itClientInformation)->getEndpoint() == paActionInfo.getEndpoint()) {
(*itClientInformation)->addAction(paActionInfo);
addClientToConnectionHandler(**itClientInformation);
break;
}
}
}
void COPC_UA_Remote_Handler::removeActionFromClient(CActionInfo& paActionInfo) {
CCriticalRegion allClientsRegion(mAllClientListMutex);
CUA_ClientInformation *clientToDelete = 0;
for(CSinglyLinkedList<CUA_ClientInformation *>::Iterator itClientInformation = mAllClients.begin(); itClientInformation != mAllClients.end();
++itClientInformation) {
CCriticalRegion clientRegion((*itClientInformation)->getMutex());
if((*itClientInformation)->getEndpoint() == paActionInfo.getEndpoint()) {
(*itClientInformation)->removeAction(paActionInfo);
clientToDelete = *itClientInformation;
break;
}
}
if(clientToDelete && !clientToDelete->hasActions()) {
removeClientFromAllLists(*clientToDelete);
}
}
void COPC_UA_Remote_Handler::removeClientFromAllLists(CUA_ClientInformation& paClientInformation) {
paClientInformation.setClientToInvalid();
mConnectionHandler.removeClient(paClientInformation);
removeClient(paClientInformation);
mAllClients.erase(&paClientInformation);
delete &paClientInformation;
}
void COPC_UA_Remote_Handler::addClientToConnectionHandler(CUA_ClientInformation& paClientInformation) {
mConnectionHandler.addClient(paClientInformation);
}
bool COPC_UA_Remote_Handler::handleClients() {
CCriticalRegion iterationCriticalRegion(getIterationClientsMutex()); //this is needed because removing a client from the list could cause trouble
CSinglyLinkedList<CUA_ClientInformation *> failedClients;
bool asyncIsNeeded = false;
for(CSinglyLinkedList<CUA_ClientInformation*>::Iterator itClientInformation = getIterationClients().begin();
itClientInformation != getIterationClients().end();
++itClientInformation) {
CCriticalRegion criticalRegionClienMutex((*itClientInformation)->getMutex());
if((*itClientInformation)->isAsyncNeeded()) {
if(!(*itClientInformation)->executeAsyncCalls()) {
failedClients.pushBack(*itClientInformation);
} else {
asyncIsNeeded = (*itClientInformation)->isAsyncNeeded();
}
}
if(!isAlive()) {
break;
}
}
if(isAlive() && !failedClients.isEmpty()) {
for(CSinglyLinkedList<CUA_ClientInformation *>::Iterator itClientInformation = failedClients.begin(); itClientInformation != failedClients.end();
++itClientInformation) {
DEVLOG_ERROR("[OPC UA REMOTE]: There was a problem checking remote %s.\n", (*itClientInformation)->getEndpoint().getValue());
//we cannot use COPC_UA_Client_IterationList::remove here because it locks mIterationClientsMutex
CCriticalRegion newClientsRegion(getNewClientsMutex());
CCriticalRegion criticalRegionClienMutex((*itClientInformation)->getMutex());
getIterationClients().erase(*itClientInformation);
getNewClients().erase(*itClientInformation); //client could still be in the newList
(*itClientInformation)->uninitializeClient(); //reset all in client and pass it back to the connection handler
(*itClientInformation)->configureClient();
addClientToConnectionHandler(**itClientInformation);
}
}
return asyncIsNeeded;
}
//************************** CONECTION HANDLER ************** //
COPC_UA_Remote_Handler::UA_ConnectionHandler::UA_ConnectionHandler(COPC_UA_Remote_Handler& paClientHandler) :
mClientHandler(paClientHandler) {
}
COPC_UA_Remote_Handler::UA_ConnectionHandler::~UA_ConnectionHandler() {
}
bool COPC_UA_Remote_Handler::UA_ConnectionHandler::handleClients() {
CCriticalRegion clientsClientsRegion(getIterationClientsMutex());
CSinglyLinkedList<CUA_ClientInformation *> clientsToRemove;
bool needsRetry = false;
for(CSinglyLinkedList<CUA_ClientInformation*>::Iterator itClientInformation = getIterationClients().begin();
itClientInformation != getIterationClients().end();
++itClientInformation) {
CCriticalRegion clientRegion((*itClientInformation)->getMutex());
if((*itClientInformation)->handleClientState()) {
clientsToRemove.pushBack(*itClientInformation);
} else {
needsRetry = true;
}
if((*itClientInformation)->someActionWasInitialized()) {
mClientHandler.addClient(**itClientInformation);
}
if(!isAlive()) {
break;
}
}
if(isAlive() && !clientsToRemove.isEmpty()) {
for(CSinglyLinkedList<CUA_ClientInformation *>::Iterator itClientInformation = clientsToRemove.begin(); itClientInformation != clientsToRemove.end();
++itClientInformation) {
getIterationClients().erase(*itClientInformation);
}
}
return needsRetry;
}