text: /*
   * DaqDeviceInternal.cpp
   *
   *     Author: Measurement Computing Corporation
   */
  #include <sstream>
  
  #include "DaqDevice.h"
  #include "DaqDeviceManager.h"
  #include "utility/EuScale.h"
  #include "utility/UlLock.h"
  
  
  #include "AiDevice.h"
  #include "AoDevice.h"
  #include "DioDevice.h"
  #include "CtrDevice.h"
  #include "TmrDevice.h"
  #include "DaqIDevice.h"
  #include "DaqODevice.h"
  #include "DaqEventHandler.h"
  #include "UlException.h"
  
  namespace ul
  {
  pthread_mutex_t DaqDevice::mDeviceNumberMutex = PTHREAD_MUTEX_INITIALIZER;
  unsigned long long DaqDevice::mNextAvailableDeviceNumber = 1;
  
  DaqDevice::DaqDevice(DaqDeviceDescriptor daqDeviceDescriptor): mDaqDeviceDescriptor(daqDeviceDescriptor), mConnected(false),
  		mAiDevice(NULL), mAoDevice(NULL), mDioDevice(NULL), mCtrDevice(NULL), mTmrDevice(NULL), mDaqIDevice(NULL), mDaqODevice(NULL)
  {
  	mEventHandler = new DaqEventHandler(*this);
  	mDaqDeviceConfig = new DaqDeviceConfig(*this);
  	mDaqDeviceInfo.setProductId(daqDeviceDescriptor.productId);
  
  	mRawFwVersion = 0;
  	mRawFpgaVersion = 0;
  	mRawRadioVersion = 0;
  
  	mMemUnlockAddr = -1;
  	mMemUnlockCode = 0;
  
  	mCurrentSuspendCount = 0;
  
  	pthread_mutex_lock(&mDeviceNumberMutex);
  	mDeviceNumber = mNextAvailableDeviceNumber;
  	mNextAvailableDeviceNumber++;
  	pthread_mutex_unlock(&mDeviceNumberMutex);
  }
  
  DaqDevice::~DaqDevice()
  {
  	if(mAiDevice != NULL)
  	{
  		delete mAiDevice;
  		mAiDevice = NULL;
  	}
  
  	if(mAoDevice != NULL)
  	{
  		delete mAoDevice;
  		mAoDevice = NULL;
  	}
  
  	if(mDioDevice != NULL)
  	{
  		delete mDioDevice;
  		mDioDevice = NULL;
  	}
  
  	if(mCtrDevice != NULL)
  	{
  		delete mCtrDevice;
  		mCtrDevice = NULL;
  	}
  
  	if(mTmrDevice != NULL)
  	{
  		delete mTmrDevice;
  		mTmrDevice = NULL;
  	}
  
  	if(mDaqIDevice != NULL)
  	{
  		delete mDaqIDevice;
  		mDaqIDevice = NULL;
  	}
  
  	if(mDaqODevice != NULL)
  	{
  		delete mDaqODevice;
  		mDaqODevice = NULL;
  	}
  
  	if(mDaqDeviceConfig != NULL)
  	{
  		delete mDaqDeviceConfig;
  		mDaqDeviceConfig = NULL;
  	}
  
  	if(mEventHandler !=NULL)
  	{
  		delete mEventHandler;
  		mEventHandler = NULL;
  	}
  
  	DaqDeviceManager::removeFromCreatedList(mDeviceNumber);
  }
  
  DaqDeviceDescriptor DaqDevice::getDescriptor() const
  {
  	return mDaqDeviceDescriptor;
  }
  
  void DaqDevice::disconnect()
  {
  
  	mEventHandler->stop();
  
  	disconnectIoDevices();
  
  	mConnected = false;
  }
  
  double DaqDevice::getClockFreq() const
  {
  	return mDaqDeviceInfo.getClockFreq();
  }
  void DaqDevice::addMemRegion(MemRegion memRegionType, unsigned long long address, unsigned long long size, long long accessTypes)
  {
  	mDaqDeviceInfo.memInfo()->addMemRegion(memRegionType, address, size, (MemAccessType) accessTypes);
  }
  
  void DaqDevice::setAiDevice(AiDevice* aiDevice)
  {
  	mAiDevice = aiDevice;
  
  	if(aiDevice != NULL)
  		mDaqDeviceInfo.hasAiDevice(true);
  
  }
  
  AiDevice* DaqDevice::aiDevice() const
  {
  	return mAiDevice;
  }
  
  UlAiDevice& DaqDevice::getAiDevice() const
  {
  	return *mAiDevice;
  }
  
  void DaqDevice::setAoDevice(AoDevice* aoDevice)
  {
  	mAoDevice = aoDevice;
  
  	if(aoDevice != NULL)
  		mDaqDeviceInfo.hasAoDevice(true);
  }
  
  AoDevice* DaqDevice::aoDevice() const
  {
  	return mAoDevice;
  }
  
  UlAoDevice& DaqDevice::getAoDevice() const
  {
  	return *mAoDevice;
  }
  
  void DaqDevice::setDioDevice(DioDevice* dioDevice)
  {
  	mDioDevice = dioDevice;
  
  	if(dioDevice != NULL)
  		mDaqDeviceInfo.hasDioDevice(true);
  }
  
  DioDevice* DaqDevice::dioDevice() const
  {
  	return mDioDevice;
  }
  
  UlDioDevice& DaqDevice::getDioDevice() const
  {
  	return *mDioDevice;
  }
  
  UlCtrDevice& DaqDevice::getCtrDevice() const
  {
  	return *mCtrDevice;
  }
  void DaqDevice::setCtrDevice(CtrDevice* ctrDevice)
  {
  	mCtrDevice = ctrDevice;
  
  	if(ctrDevice != NULL)
  		mDaqDeviceInfo.hasCtrDevice(true);
  }
  
  CtrDevice* DaqDevice::ctrDevice() const
  {
  	return mCtrDevice;
  }
  
  UlTmrDevice& DaqDevice::getTmrDevice() const
  {
  	return *mTmrDevice;
  }
  void DaqDevice::setTmrDevice(TmrDevice* tmrDevice)
  {
  	mTmrDevice = tmrDevice;
  
  	if(tmrDevice != NULL)
  		mDaqDeviceInfo.hasTmrDevice(true);
  }
  
  TmrDevice* DaqDevice::tmrDevice() const
  {
  	return mTmrDevice;
  }
  
  
  UlDaqIDevice& DaqDevice::getDaqIDevice() const
  {
  	return *mDaqIDevice;
  }
  void DaqDevice::setDaqIDevice(DaqIDevice* daqIDevice)
  {
  	mDaqIDevice = daqIDevice;
  
  	if(daqIDevice != NULL)
  		mDaqDeviceInfo.hasDaqIDevice(true);
  }
  
  DaqIDevice* DaqDevice::daqIDevice() const
  {
  	return mDaqIDevice;
  }
  
  UlDaqODevice& DaqDevice::getDaqODevice() const
  {
  	return *mDaqODevice;
  }
  void DaqDevice::setDaqODevice(DaqODevice* daqODevice)
  {
  	mDaqODevice = daqODevice;
  
  	if(daqODevice != NULL)
  		mDaqDeviceInfo.hasDaqODevice(true);
  }
  
  DaqODevice* DaqDevice::daqODevice() const
  {
  	return mDaqODevice;
  }
  
  DaqEventHandler* DaqDevice::eventHandler() const
  {
  	return mEventHandler;
  }
  
  void DaqDevice::initializeIoDevices()
  {
  
  	if(mAiDevice != NULL)
  		mAiDevice->initialize();
  
  	if(mAoDevice != NULL)
  		mAoDevice->initialize();
  
  	if(mDioDevice != NULL)
  		mDioDevice->initialize();
  
  	if(mCtrDevice != NULL)
  		mCtrDevice->initialize();
  
  	if(mTmrDevice != NULL)
  		mTmrDevice->initialize();
  
  	if(mDaqIDevice != NULL)
  		mDaqIDevice->initialize();
  
  	if(mDaqODevice != NULL)
  		mDaqODevice->initialize();
  }
  
  
  
  void DaqDevice::reconfigureIoDevices()
  {
  	if(mAiDevice != NULL)
  		mAiDevice->reconfigure();
  
  	if(mAoDevice != NULL)
  		mAoDevice->reconfigure();
  
  	if(mDioDevice != NULL)
  		mDioDevice->reconfigure();
  
  	if(mCtrDevice != NULL)
  		mCtrDevice->reconfigure();
  
  	if(mTmrDevice != NULL)
  		mTmrDevice->reconfigure();
  
  	if(mDaqIDevice != NULL)
  		mDaqIDevice->reconfigure();
  
  	if(mDaqODevice != NULL)
  		mDaqODevice->reconfigure();
  }
  
  void DaqDevice::disconnectIoDevices()
  {
  	try
  	{
  		if(mAiDevice != NULL)
  			mAiDevice->disconnect();
  
  		if(mAoDevice != NULL)
  			mAoDevice->disconnect();
  
  		if(mDioDevice != NULL)
  			mDioDevice->disconnect();
  
  		if(mCtrDevice != NULL)
  			mCtrDevice->disconnect();
  
  		if(mDaqIDevice != NULL)
  			mDaqIDevice->disconnect();
  
  		if(mDaqODevice != NULL)
  			mDaqODevice->disconnect();
  	}
  	catch(UlException& e)
  	{
  		UL_LOG("Ul exception occurred: " << e.what());
  	}
  }
  
  bool DaqDevice::isScanRunning() const
  {
  	bool running = false;
  
  	if(mAiDevice != NULL && mAiDevice->getScanState() == SS_RUNNING)
  		running = true;
  	else  if(mAoDevice != NULL && mAoDevice->getScanState() == SS_RUNNING)
  		running = true;
  	else if(mDioDevice != NULL && mDioDevice->getScanState(SD_INPUT) == SS_RUNNING)
  		running = true;
  	else if(mDioDevice != NULL && mDioDevice->getScanState(SD_OUTPUT) == SS_RUNNING)
  		running = true;
  	else if(mCtrDevice != NULL && mCtrDevice->getScanState() == SS_RUNNING)
  		running = true;
  	else if(mDaqIDevice != NULL && mDaqIDevice->getScanState() == SS_RUNNING)
  		running = true;
  	else if(mDaqODevice != NULL && mDaqODevice->getScanState() == SS_RUNNING)
  			running = true;
  
  	return running;
  }
  
  bool DaqDevice::isScanRunning(FunctionType functionType) const
  {
  	bool running = false;
  
  	IoDevice* ioDevice = NULL;
  
  	switch(functionType)
  	{
  	case FT_AI:
  		ioDevice = mAiDevice;
  		break;
  	case FT_AO:
  		ioDevice = mAoDevice;
  		break;
  	case FT_DI:
  	case FT_DO:
  		ioDevice = mDioDevice;
  		break;
  	case FT_CTR:
  		ioDevice = mCtrDevice;
  		break;
  	case FT_DAQI:
  		ioDevice = mDaqIDevice;
  		break;
  	case FT_DAQO:
  		ioDevice = mDaqODevice;
  		break;
  
  	default:
  		break;
  	}
  
  	if(ioDevice != NULL && ioDevice->getScanState() == SS_RUNNING)
  		running = true;
  
  	return running;
  }
  
  void DaqDevice::stopBackground(FunctionType functionType) const
  {
  	IoDevice* ioDevice = NULL;
  
  	switch(functionType)
  	{
  	case FT_AI:
  		ioDevice = mAiDevice;
  		break;
  	case FT_AO:
  		ioDevice = mAoDevice;
  		break;
  	case FT_DI:
  	case FT_DO:
  		ioDevice = mDioDevice;
  		break;
  	case FT_CTR:
  		ioDevice = mCtrDevice;
  		break;
  	case FT_DAQI:
  		ioDevice = mDaqIDevice;
  		break;
  	case FT_DAQO:
  		ioDevice = mDaqODevice;
  		break;
  	default:
  		break;
  	}
  
  	if(ioDevice != NULL)
  		ioDevice->stopBackground();
  	else
  		std::cout << "########## stopBackground not implemented" << std::endl;
  
  }
  
  IoDevice* DaqDevice::getIoDevice(FunctionType functionType) const
  {
  	IoDevice* ioDevice = NULL;
  
  	switch(functionType)
  	{
  	case FT_AI:
  		ioDevice = mAiDevice;
  		break;
  	case FT_AO:
  		ioDevice = mAoDevice;
  		break;
  	case FT_DI:
  	case FT_DO:
  		ioDevice = mDioDevice;
  		break;
  	case FT_CTR:
  		ioDevice = mCtrDevice;
  		break;
  	case FT_TMR:
  		ioDevice = mTmrDevice;
  		break;
  	case FT_DAQI:
  		ioDevice = mDaqIDevice;
  		break;
  	case FT_DAQO:
  		ioDevice = mDaqODevice;
  		break;
  
  	default:
  		break;
  	}
  
  	if(ioDevice == NULL)
  		std::cout << "########## getIoDevice not implemented" << std::endl;
  
  	return ioDevice;
  
  }
  
  TriggerConfig DaqDevice::getTriggerConfig(FunctionType functionType) const
  {
  	TriggerConfig trigCfg;
  
  	if(functionType == FT_AI && mAiDevice)
  		trigCfg = mAiDevice->getTrigConfig();
  	else if(functionType == FT_AO && mAiDevice)
  		trigCfg = mAoDevice->getTrigConfig();
  	else if(functionType == FT_DI && mDioDevice)
  		trigCfg = mDioDevice->getTrigConfig(SD_INPUT);
  	else if(functionType == FT_DO && mDioDevice)
  		trigCfg = mDioDevice->getTrigConfig(SD_OUTPUT);
  	else if(functionType == FT_CTR && mCtrDevice)
  		trigCfg = mCtrDevice->getTrigConfig();
  	else if(functionType == FT_TMR && mTmrDevice)
  		trigCfg = mTmrDevice->getTrigConfig();
  	else if(functionType == FT_DAQI && mDaqIDevice)
  		trigCfg = mDaqIDevice->getTrigConfig();
  	else if(functionType == FT_DAQO && mDaqODevice)
  		trigCfg = mDaqODevice->getTrigConfig();
  
  	return trigCfg;
  }
  
  void DaqDevice::checkConnection() const
  {
  	if(!mConnected)
  	{
  		throw UlException(ERR_NO_CONNECTION_ESTABLISHED);
  	}
  }
  
  void DaqDevice::getEuScaling(Range range, double &scale, double &offset) const
  {
  	EuScale::getEuScaling(range, scale, offset);
  }
  
  void DaqDevice::flashLed(int flashCount) const
  {
  	throw UlException(ERR_BAD_DEV_TYPE);
  }
  
  //////////////////////          Configuration functions          /////////////////////////////////
  
  void DaqDevice::getCfg_FwVersionStr(char* fwVerStr, unsigned int* maxStrLen) const
  {
  	if(fwVerStr)
  		fwVerStr[0] = '\0';
  
  	std::stringstream stream;
  	stream << std::hex << mRawFwVersion;
  	std::string verStr( stream.str());
  
  	while(verStr.length() < 3)
  		verStr.insert(verStr.begin(), '0');
  
  	// fw version is stored in two bytes (Each nibble represents one digit)
  	verStr.insert(verStr.end() - 2, '.');
  
  	if(mRawFwVersion == 0)
  		verStr = "";
  
  	unsigned int len = verStr.size() + 1;
  
  	if (len <= *maxStrLen)
  	{
  		memcpy(fwVerStr, verStr.c_str(), len);
  		*maxStrLen = len;
  	}
  	else
  	{
  		*maxStrLen = len;
  		throw UlException(ERR_BAD_BUFFER_SIZE);
  	}
  }
  
  void DaqDevice::getCfg_FpgaVersionStr(char* fpgaVerStr, unsigned int* maxStrLen) const
  {
  	if(fpgaVerStr)
  		fpgaVerStr[0] = '\0';
  
  	std::stringstream stream;
  	stream << std::hex << mRawFpgaVersion;
  	std::string verStr( stream.str());
  
  	while(verStr.length() < 3)
  		verStr.insert(verStr.begin(), '0');
  
  	// fpga version is stored in two bytes (Each nibble represents one digit)
  	verStr.insert(verStr.end() - 2, '.');
  
  	if(mRawFpgaVersion == 0)
  		verStr = "";
  
  	unsigned int len = verStr.size() + 1;
  
  	if (len <= *maxStrLen)
  	{
  		memcpy(fpgaVerStr, verStr.c_str(), len);
  		*maxStrLen = len;
  	}
  	else
  	{
  		*maxStrLen = len;
  		throw UlException(ERR_BAD_BUFFER_SIZE);
  	}
  }
  
  void DaqDevice::getCfg_RadioVersionStr(char* fpgaVerStr, unsigned int* maxStrLen) const
  {
  	if(fpgaVerStr)
  		fpgaVerStr[0] = '\0';
  
  	std::stringstream stream;
  	stream << std::hex << mRawRadioVersion;
  	std::string verStr( stream.str());
  
  	while(verStr.length() < 3)
  		verStr.insert(verStr.begin(), '0');
  
  	//  radio version is stored in two bytes (Each nibble represents one digit)
  	verStr.insert(verStr.end() - 2, '.');
  
  	if(mRawRadioVersion == 0)
  		verStr = "";
  
  	unsigned int len = verStr.size() + 1;
  
  	if (len <= *maxStrLen)
  	{
  		memcpy(fpgaVerStr, verStr.c_str(), len);
  		*maxStrLen = len;
  	}
  	else
  	{
  		*maxStrLen = len;
  		throw UlException(ERR_BAD_BUFFER_SIZE);
  	}
  }
  
  int DaqDevice::memRead(MemoryType memType, MemRegion memRegionType, unsigned int address, unsigned char* buffer, unsigned int count) const
  {
  	throw UlException(ERR_BAD_DEV_TYPE);
  }
  
  int DaqDevice::memWrite(MemoryType memType, MemRegion memRegionType, unsigned int address, unsigned char* buffer, unsigned int count) const
  {
  	throw UlException(ERR_BAD_DEV_TYPE);
  }
  
  int DaqDevice::memRead(MemRegion memRegionType, unsigned int address, unsigned char* buffer, unsigned int count) const
  {
  	check_MemRW_Args(memRegionType, MA_READ, address, buffer, count);
  
  	return memRead(MT_EEPROM, memRegionType, address, buffer, count);
  }
  
  int DaqDevice::memWrite(MemRegion memRegionType, unsigned int address, unsigned char* buffer, unsigned int count) const
  {
  	check_MemRW_Args(memRegionType, MA_WRITE, address, buffer, count);
  
  	return memWrite(MT_EEPROM, memRegionType, address, buffer, count);
  }
  
  void DaqDevice::check_MemRW_Args(MemRegion memRegionType, MemAccessType accessType, unsigned int address, unsigned char* buffer, unsigned int count, bool checkAccess) const
  {
  	if(address == (unsigned long long) getMemUnlockAddr())
  		return;
  
  	std::bitset<32> memRegionBitset(mDaqDeviceInfo.memInfo()->getMemRegionTypes());
  
  	if((memRegionType & mDaqDeviceInfo.memInfo()->getMemRegionTypes()) || memRegionBitset.count() == 1)
  	{
  		UlMemRegionInfo& memRegionInfo = mDaqDeviceInfo.memInfo()->getMemRegionInfo(memRegionType);
  
  		if(checkAccess && !(memRegionInfo.getAccessTypes() & accessType))
  			throw UlException(ERR_MEM_ACCESS_DENIED);
  
  		unsigned long long memStartAddr = memRegionInfo.getAddress();
  		unsigned long long endAddr = memRegionInfo.getAddress() + memRegionInfo.getSize() - 1;
  
  		if(address < memStartAddr || address > endAddr)
  			throw UlException(ERR_BAD_MEM_ADDRESS);
  
  		if((address + count - 1) > endAddr)
  			throw UlException(ERR_BAD_MEM_ADDRESS);
  
  		if(buffer == NULL)
  			throw UlException(ERR_BAD_BUFFER);
  	}
  	else
  		throw UlException(ERR_BAD_MEM_REGION);
  
  	if(!isConnected())
  		throw UlException(ERR_NO_CONNECTION_ESTABLISHED);
  }
  
  
  
  } /* namespace ul */
  