// Copyright 2016 CERN and GSI // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. #ifdef NI_SUPPORT_ENABLED #include <silecs-communication/interface/communication/CNVConnection.h> #include <silecs-communication/interface/communication/SilecsConnection.h> #include <silecs-communication/interface/equipment/CNVBlock.h> #include <silecs-communication/interface/equipment/SilecsBlock.h> #include <silecs-communication/interface/equipment/SilecsPLC.h> #include <silecs-communication/interface/utility/SilecsException.h> #include <silecs-communication/interface/utility/SilecsLog.h> #include <silecs-communication/interface/utility/StringUtilities.h> #include <silecs-communication/protocol/core/silecs.h> namespace Silecs { CNVConnection::CNVConnection(PLC* thePLC) : Connection(thePLC) { LOG(ALLOC) << "CNVConnection (create): " << thePLC->getName(); } CNVConnection::~CNVConnection() { //Close the connection before removing resources //disable(); must be done before removing resource } /* Check for errors */ int CNVConnection::errChk(PLC* thePLC, int code)// throw (std::string*) { if(code != 0) { LOG(DEBUG) << "CNV error occurred: " << code << ": " << CNVGetErrorDescription(code); //Do not close the connection if it's only for data missing (when deleting empty Data object) if (code != CNVInvalidDataHandleError) { LOG(COMM) << "Transaction failure with PXI: " << thePLC->getName() << ". CNV[" << code << "]: " << CNVGetErrorDescription(code); doClose(thePLC, /*withLock =*/true); throw SilecsException(__FILE__, __LINE__, CNV_INTERNAL_ERROR, std::string(CNVGetErrorDescription(code))+" in CNVConnection"); } } return code; } /* Read Data */ int CNVConnection::readData(PLC* thePLC, CNVBufferedSubscriber *handle,CNVData *data) { CNVBufferDataStatus status; /* Buffer is used only for performance reason. * Always return the last value in the buffer discarding the oldest ones. */ //read the oldest data in the buffer int errorCode = errChk(thePLC, CNVGetDataFromBuffer(*handle, data, &status)); if ((errorCode == 0) && (status != CNVStaleData)) { if (status != CNVNoData) { //buffer was not empty, try to read again to get the very last data if any errChk(thePLC, CNVDisposeData(*data)); //CNV requires to free the buffer on every CNVGetDataFromBuffer errorCode = errChk(thePLC, CNVGetDataFromBuffer(*handle, data, &status)); } else { LOG(DEBUG) << "CNVConnection::readData() : buffer is empty (variables may not be initialized properly)"; errorCode = CNVEmptyDataError; } } return errorCode; } /* Write Data */ int CNVConnection::writeData(PLC* thePLC, const char *networkVariablePathname, CNVData data) { CNVWriter writer; int errorCode = errChk(thePLC, CNVCreateWriter(networkVariablePathname , NULL, NULL, CNVWaitForever , 0, &writer)); if (errorCode ==0) { CNVWrite (writer, data, 0); CNVDispose(writer); } return errorCode; } bool CNVConnection::open(PLC* thePLC) { //Controller is reachable at this stage (ping done from doOpen) bool isOK = true; //all subscription is fine a priori LOG(DEBUG) << "CNVConnection::open(): Subscribe to all input variables"; // Create all the subscriptions blockMapType::iterator pBlockIter; for(pBlockIter = thePLC->getBlockMap().begin(); pBlockIter != thePLC->getBlockMap().end(); ++pBlockIter) { //Attention: only CNVInputBlock has doSubscribe/unSubscribe feature (key-map can be used to select appropriate object) if (pBlockIter->first.find(Block::whichAccessType(Input)) != std::string::npos) { if (static_cast<CNVInputBlock*>(pBlockIter->second)->doSubscribe() == false) { isOK = false; break; } } } return isOK; } bool CNVConnection::close(PLC* thePLC) { LOG(DEBUG) << "CNVConnection::close(): unSubscribe all input variables"; // Delete all the subscription blockMapType::iterator pBlockIter; for(pBlockIter = thePLC->getBlockMap().begin(); pBlockIter != thePLC->getBlockMap().end(); ++pBlockIter) { //Attention: only CNVInputBlock has doSubscribe/unSubscribe feature (key-map can be used to select appropriate object) if (pBlockIter->first.find(Block::whichAccessType(Input)) != std::string::npos) { static_cast<CNVInputBlock*>(pBlockIter->second)->unSubscribe(); } } isConnected_ = false; return true; } } // namespace #endif //NI_SUPPORT_ENABLED