VirtualInstrument.cc 15.1 KB
/*
 * VirtualInstrument.cc
 *
 *  Created on: Jan 17, 2013
 *      Author: f.casimir
 */

#include <algorithm>    // std::find

#include "DicError.hh"
#include "TimeUtil.hh"

#include "DDServerInterfaceConfig.hh"
#include "VirtualInstrument.hh"
#include "VirtualInstrumentInterval.hh"

using namespace AMDA::Parameters;

namespace AMDA {
namespace DDServerInterface {

		VirtualInstrument::VirtualInstrument(VirtualInstrumentManager& pVirtualInstrumentManager, const std::string& viName) :
				_virtualInstrumentManager(pVirtualInstrumentManager),
				_viName(viName),
				_id(-1) {

			// Set user host
			_ddClient.setUserHost(_virtualInstrumentManager.getUserHost());
			// Set user login
			_ddClient.setUserName(_virtualInstrumentManager.getUserName());
			
			/// Open Connection
			_id = _ddClient.DD_SetVariable(const_cast<char *>(_viName.c_str()));			
			LOG4CXX_INFO(gLogger,"ParamGetDDBase: DD_SetVariable("<< _viName << ") returns = (" << _id << ")");

			if (_id < 0) {
				_ddClient.DD_Close(99);
				BOOST_THROW_EXCEPTION(exception() << errno_code(VirtualInstrumentManager::getInstance()->ddErrorToAmdaError(_id)));
			}

			/// Set GlobalStart
			{
				_globalStartTime = getDDTimeInfo("GlobalStart");
			}
			/// Set GlobalStop
			{
				_globalStopTime = getDDTimeInfo("GlobalStop");
			}
			/// Set MinSampling
			{
				const InfoList& lInfoList = getDDInfo("MinSampling");
				_minSampling = lInfoList.begin()->second->at(0);
			}
			/// Set MaxSampling
			{
				const InfoList& lInfoList = getDDInfo("MaxSampling");
				if (lInfoList.empty()) { //Sampling constant
					_maxSampling = _minSampling;
				} else {
					_maxSampling = lInfoList.begin()->second->at(0);
				}
			}
			/// Set FillValue
			{
				const InfoList& lInfoList = getDDInfo("FillValue");
				if ( ! lInfoList.empty()) {
					_fillValue = (*lInfoList.begin()->second)[0];
				} else {
					_fillValue = NAN;
				}
			}
		}

		VirtualInstrument::~VirtualInstrument() {
			if(_id!=-1) { _ddClient.DD_Close(_id); }
			VirtualInstrumentManager::releaseInstance();
		}

		double VirtualInstrument::getDDTimeInfo(const char *timeTag) {
			DD_data_t *data;
			_ddClient.DD_GetInform(_id, const_cast<char *>(timeTag), &data);
			if (data->type != DD_CHAR || data->DimNumber != 1
					|| data->Dimensions[0] <= 0) {
				LOG4CXX_ERROR(gLogger, "DD_GetInform(" << timeTag << ") not good type ");
				BOOST_THROW_EXCEPTION(exception() << errno_code(AMDA_ERROR_UNKNOWN));
			}
			LOG4CXX_DEBUG(gLogger, "getDDTimeInfo(" << timeTag << ") = " << (char*)data->Variables[0]);
			return TimeUtil::readTimeInIso((char *) data->Variables[0]);
		}

		template <typename type>
		void VirtualInstrument::setInfoValues( InfoList& storage, const std::string& pInfoName, unsigned int pNbData, void* data) {
			// Read data
			type* lFloatData = reinterpret_cast<type*>(data);
			InfoValuesSPtr lInfo = InfoValuesSPtr(new InfoValues(pNbData));
			lInfo->assign(lFloatData,lFloatData+pNbData);
			storage.insert(std::pair<std::string,InfoValuesSPtr>( pInfoName, lInfo));
		}

		unsigned int getNbTab( unsigned int pNbDimension, unsigned int pDepth, int* pDimensions) {
			if ( pDepth == pNbDimension) {
				return 1;
			} else {
				return pDimensions[pDepth]*getNbTab(pNbDimension,pDepth+1,pDimensions);
			}
		}

		template <typename Type>
		void VirtualInstrument::recurseInfoValues( InfoList& storage, const std::string& pInfoName, unsigned int pNbDimension, int* pDimensions, void* data) {
			if ( pNbDimension == 1 ) {
				setInfoValues<Type>(storage,pInfoName,pDimensions[0],data);
			} else {
				int lTabSize     = pDimensions[pNbDimension-1];
				unsigned int lStepIndex   = getNbTab(pNbDimension-1,0,pDimensions);
				for ( unsigned int i = 0; i < lStepIndex; ++i) {
					unsigned int lStepPointer = sizeof(Type)*i*lTabSize;
					std::stringstream lInfoName; lInfoName << pInfoName << "_" << i;
					setInfoValues<Type>(storage,lInfoName.str(), lTabSize, (char *)data + lStepPointer);
				}
			}
		}

		const InfoList& VirtualInstrument::getDDInfo(const char* pIinfoName) {
			DD_data_t *data = NULL;
			auto lIt = _infoMap.find(pIinfoName);
			if (lIt == _infoMap.end()) {
				int result = 0;
				InfoList& infoList = _infoMap[pIinfoName];
				if ((result = _ddClient.DD_GetInform( _id, const_cast<char *>(pIinfoName), &data)) >= 0) {
					LOG4CXX_DEBUG(gLogger,"ParamGetDDBase:getInfoDD( "<< _id << ", " << pIinfoName << ") returns = ( return: " << result << ", type: " << data->type << ", DimNumber: " << data->DimNumber << ", Dimensions[0]: " << data->Dimensions[0] << ", VarNumber: " << data->VarNumber << ")");

					if (data->VarNumber == 1) {
						if (data->type == DD_FLOAT) {
							recurseInfoValues<float>(infoList, pIinfoName,
									data->DimNumber, data->Dimensions,
									*data->Variables);
						} else if (data->type == DD_DOUBLE) {
							recurseInfoValues<double>(infoList, pIinfoName,
									data->DimNumber, data->Dimensions,
									*data->Variables);
						} else if (data->type == DD_SHORT) {
							recurseInfoValues<short>(infoList, pIinfoName,
									data->DimNumber, data->Dimensions,
									*data->Variables);
						}
					} else {

					}
				} else {
					LOG4CXX_INFO(gLogger,
							"ParamGetDDBase:getInfoDD( "<< _id << ", " << pIinfoName << ") returns = (" << result << ")");
				}
				return infoList;
			}
			return lIt->second;
		}

		AMDA::Parameters::ContainerType VirtualInstrument::getParamContainerType(DD_data_t *data, int dim3Num)
		{
			if (data == NULL)
				return AMDA::Parameters::ContainerType::CT_UNKNOWN;
			switch (data->DimNumber)
			{
				case 1 :
					//vector or scalar
					if (data->Dimensions[0] > 1)
						return AMDA::Parameters::ContainerType::CT_TAB1D;
					else if (data->Dimensions[0] == 1)
						return AMDA::Parameters::ContainerType::CT_SCALAR;
					break;
				case 2 :
					if (data->Dimensions[1] == 1)
					{
						if (data->Dimensions[0] == 1)
							return AMDA::Parameters::ContainerType::CT_SCALAR;
						else if (data->Dimensions[0] > 1)
							return AMDA::Parameters::ContainerType::CT_TAB1D;
					}
					else if (data->Dimensions[1] > 1)
					{
						if (data->Dimensions[0] == 1)
							return AMDA::Parameters::ContainerType::CT_TAB1D;
						else if (data->Dimensions[0] > 1)
							return AMDA::Parameters::ContainerType::CT_TAB2D;
					}
					break;
				case 3 :
					if (dim3Num < 0 || dim3Num > 2) {
						return AMDA::Parameters::ContainerType::CT_UNKNOWN;
					}
					return AMDA::Parameters::ContainerType::CT_TAB2D;
				default:
					return AMDA::Parameters::ContainerType::CT_UNKNOWN;
			}
			return AMDA::Parameters::ContainerType::CT_UNKNOWN;
		}

		//TODO Replace by DD_Client::getParamType
		AMDA::Parameters::Base::Pusher* VirtualInstrument::getParamPusher( const std::string& pParName, int maxDim1Size, int maxDim2Size, int maxDim3Size, int dim3Num, int dim3CutIndex, int minSumIndex, int maxSumIndex, double timeRestriction) {

			AMDA::Parameters::Base::Pusher* lPusher = nullptr;
			int error = 0;
			DD_Client lDDClient;
			
			lDDClient.setUserHost(_virtualInstrumentManager.getUserHost());
			lDDClient.setUserName(_virtualInstrumentManager.getUserName());
			
			DD_data_t *data = nullptr;
			LOG4CXX_DEBUG(gLogger, "VirtualInstrument::getParamPusher: DD_SetVariable("<< _viName << ") call");
			int lId = lDDClient.DD_SetVariable(const_cast<char *>(_viName.c_str()));
			LOG4CXX_INFO(gLogger, "VirtualInstrument::getParamPusher: DD_SetVariable("<< _viName << ") returns = (" << _id << ")");
			if (lId < 0) {
				lDDClient.DD_Close(99);
				BOOST_THROW_EXCEPTION(exception() << errno_code(lId));
			}

			char startTime[TIMELENGTH];
			Double2DD_Time(startTime,_globalStartTime);
			LOG4CXX_DEBUG(gLogger, "VirtualInstrument::getParamPusher: DD_SetTime("<< startTime << ") call");

			if ((error = lDDClient.DD_SetTime(lId, startTime)) < 0) {
				while ((error == WAITEXTCALL) || (error == TRYAGAIN))
				{
					sleep(2);
					error = lDDClient.DD_SetTime(lId, startTime);
				}
				if (error == NODATAATTIME) {
					// In some case, GlobalStart can be not exactly the real first time's record of a dataset...
					int offset = 0;
					while (error == NODATAATTIME && offset < 60) {
						++offset;
						LOG4CXX_DEBUG(gLogger, "VirtualInstrument::getParamPusher: DD_SetTime("<< startTime << ") returns = (" << error << ")");
						Double2DD_Time(startTime,_globalStartTime+offset);
						error = lDDClient.DD_SetTime(lId, startTime);
					}
				}
				if (error < 0)
				{
					lDDClient.DD_Close(lId);
					LOG4CXX_ERROR(gLogger, "VirtualInstrument::getParamPusher: DD_SetTime("<< startTime << ") returns = (" << error << ")");
					BOOST_THROW_EXCEPTION(exception() << errno_code(VirtualInstrumentManager::getInstance()->ddErrorToAmdaError(error)));
				}
			}

			LOG4CXX_INFO(gLogger, "VirtualInstrument::getParamPusher: DD_SetTime("<< startTime << ") returns = (" << error << ")");

			char timeInt[TIMELENGTH];
			Double2DD_Time(timeInt, _maxSampling);
			LOG4CXX_DEBUG(gLogger, "VirtualInstrument::getParamPusher: DD_GetData("<< pParName << "," << timeInt << ") call");

			error = lDDClient.DD_GetData(lId, const_cast<char *>(pParName.c_str()), timeInt, &data);
			while ((error == MOREDELAY) || (error == WAITEXTCALL) || (error == TRYAGAIN)) {
				sleep(2);
				error = lDDClient.DD_GetData(lId, const_cast<char *>(pParName.c_str()), timeInt, &data);
			}

			lDDClient.DD_Close(lId);

			if (error >= 0 || error == MOREDATA) {
				//#define DD_SHORT 4
				//#define DD_INT     1
				//#define DD_FLOAT   2
				//#define DD_DOUBLE 3
				//#define DD_CHAR   0
				LOG4CXX_DEBUG(gLogger, "getParamPusher data->type = " << data->type << ", dimNumber :" << data->DimNumber <<" var number : " << data->VarNumber);

				const int numDataType = data->type;

				AMDA::Parameters::ContainerType containerType = getParamContainerType(data,dim3Num);
				switch (containerType)
				{
				case AMDA::Parameters::ContainerType::CT_SCALAR :
					switch (numDataType)
					{
					case DD_FLOAT:
						lPusher = new Pusher<DD_FLOAT, AMDA::Parameters::ContainerType::CT_SCALAR>();
						break;
					case DD_INT :
						lPusher = new Pusher<DD_INT, AMDA::Parameters::ContainerType::CT_SCALAR>();
						break;
					case DD_SHORT :
						lPusher = new Pusher<DD_SHORT, AMDA::Parameters::ContainerType::CT_SCALAR>();
						break;
					case DD_DOUBLE :
						lPusher = new Pusher<DD_DOUBLE, AMDA::Parameters::ContainerType::CT_SCALAR>();
						break;
					default :
						BOOST_THROW_EXCEPTION(
						exception() << errno_code(AMDA_TYPE_DATA_UNKNOWN));
					}
					break;
				case AMDA::Parameters::ContainerType::CT_TAB1D :
					switch (numDataType)
					{
					case DD_FLOAT:
						lPusher = new Pusher<DD_FLOAT, AMDA::Parameters::ContainerType::CT_TAB1D>(maxDim1Size > -1 ? maxDim1Size : data->Dimensions[0]);
						break;
					case DD_INT :
						lPusher = new Pusher<DD_INT, AMDA::Parameters::ContainerType::CT_TAB1D>(maxDim1Size > -1 ? maxDim1Size : data->Dimensions[0]);
						break;
					case DD_SHORT :
						lPusher = new Pusher<DD_SHORT, AMDA::Parameters::ContainerType::CT_TAB1D>(maxDim1Size > -1 ? maxDim1Size : data->Dimensions[0]);
						break;
					case DD_DOUBLE :
						lPusher = new Pusher<DD_DOUBLE, AMDA::Parameters::ContainerType::CT_TAB1D>(maxDim1Size > -1 ? maxDim1Size : data->Dimensions[0]);
						break;
					default :
						BOOST_THROW_EXCEPTION(
						exception() << errno_code(AMDA_TYPE_DATA_UNKNOWN));
					}
					break;
				case AMDA::Parameters::ContainerType::CT_TAB2D :
				{
					int dim1N = 0;
					int dim2N = 1;
					int dim3Size = 1;
					if (dim3Num >= 0) {
						dim1N = (dim3Num == 0) ? 1 : 0;
						if (dim1N == 0) {
							dim2N = (dim3Num == 1) ? 2 : 1;
						}
						else if (dim1N == 1) {
							dim2N = (dim3Num == 0) ? 2 : 1;
						}
						dim3Size = data->Dimensions[dim3Num];
					}
					switch (numDataType)
					{
					case DD_FLOAT:
						lPusher = new Pusher<DD_FLOAT, AMDA::Parameters::ContainerType::CT_TAB2D>(maxDim1Size > -1 ? maxDim1Size : data->Dimensions[dim1N], maxDim2Size > -1 ? maxDim2Size : data->Dimensions[dim2N], maxDim3Size > -1 ? maxDim3Size : dim3Size, dim1N, dim2N, dim3Num, dim3CutIndex, minSumIndex, maxSumIndex);
						break;
					case DD_INT :
						lPusher = new Pusher<DD_INT, AMDA::Parameters::ContainerType::CT_TAB2D>(maxDim1Size > -1 ? maxDim1Size : data->Dimensions[dim1N], maxDim2Size > -1 ? maxDim2Size : data->Dimensions[dim2N], maxDim3Size > -1 ? maxDim3Size : dim3Size, dim1N, dim2N, dim3Num, dim3CutIndex,  minSumIndex, maxSumIndex);
						break;
					case DD_SHORT :
						lPusher = new Pusher<DD_SHORT, AMDA::Parameters::ContainerType::CT_TAB2D>(maxDim1Size > -1 ? maxDim1Size : data->Dimensions[dim1N], maxDim2Size > -1 ? maxDim2Size : data->Dimensions[dim2N], maxDim3Size > -1 ? maxDim3Size : dim3Size, dim1N, dim2N, dim3Num, dim3CutIndex, minSumIndex, maxSumIndex);
						break;
					case DD_DOUBLE :
						lPusher = new Pusher<DD_DOUBLE, AMDA::Parameters::ContainerType::CT_TAB2D>(maxDim1Size > -1 ? maxDim1Size : data->Dimensions[dim1N], maxDim2Size > -1 ? maxDim2Size : data->Dimensions[dim2N], maxDim3Size > -1 ? maxDim3Size : dim3Size, dim1N, dim2N, dim3Num, dim3CutIndex, minSumIndex, maxSumIndex);
						break;
					default :
						BOOST_THROW_EXCEPTION(
						exception() << errno_code(AMDA_TYPE_DATA_UNKNOWN));
					}
					break;
				}
				default:
					LOG4CXX_INFO(gLogger, "VirtualInstrument::getParamPusher( "<< lId <<", " << pParName << ") - Unknown container type");
					lDDClient.DD_Close(lId);
					BOOST_THROW_EXCEPTION(exception() << errno_code(AMDA_TYPE_DATA_UNKNOWN));
				}
				lPusher->setFillValue(_fillValue);
				if (timeRestriction > 0. && !isnan(timeRestriction)) {
					lPusher->setTimeRestriction(timeRestriction);
				}
			} else {
				LOG4CXX_INFO(gLogger, "VirtualInstrument::getParamPusher( "<< lId <<", " << pParName << ") returns = (" << error << ")");
				lDDClient.DD_Close(lId);
				BOOST_THROW_EXCEPTION(exception() << errno_code(VirtualInstrumentManager::getInstance()->ddErrorToAmdaError(error)));
			}

			return lPusher;
		}

		VI::ParamFlowSPtr VirtualInstrument::getParamFlow(const std::string& pParamName, TimeIntervalListSPtr pTimeIntervalList) {
			// Define function to compare two TimeIntervalListSptr.
			// This used to identify if the requested TimeIntervalList already exist in _intervalList attribute or not.
			auto lIt = std::find_if( _intervalList.begin(), _intervalList.end(), [&](const IntervalList::value_type& val) -> bool {
				if (val.first->size() != pTimeIntervalList->size()) {
					return false;
				} else {
					TimeIntervalList::const_iterator itA = val.first->begin();
					TimeIntervalList::const_iterator itB = pTimeIntervalList->begin();
					while (itA != val.first->end()) {
						if ( (itA->_startTime != itB->_startTime) || (itA->_stopTime != itB->_stopTime) ) {
							return false;
						} else {
							++itA;
							++itB;
						}
					}
					return true;
				}
			});

			// TimeIntervalListSPtr not found
			if (lIt == _intervalList.end()) {
				std::pair<TimeIntervalListSPtr, VirtualIntrumentIntervalSPtr> value(pTimeIntervalList, VirtualIntrumentIntervalSPtr(new VirtualInstrumentInterval(*this, pTimeIntervalList.get())));
				std::pair<IntervalList::iterator,bool> lIter = _intervalList.insert(value);
				lIt = lIter.first;
			} else {
				// Nothing to do.
			}

			return lIt->second->getParamFlow(pParamName);
		}

	} /* namespace DDServerInterface */
} /* namespace AMDA */