// Copyright (c) 2020 Metrolab Technology S.A., Geneva, Switzerland (www.metrolab.com)
// See the included file LICENSE.txt for the licensing conditions.

//////////////////////////////////////////////////////////////////////////
/// \file
/// \brief Measure the time/measurement of the THM1176 in Immediate trigger mode.
//
//  THM1176MeasureImmediateTiming.cpp
//  THM1176Test
//	Purpose:
//		Measure the time/measurement of the THM1176 in Immediate trigger mode,
//		as a function of block size and averaging count.
//
//  Created by Philip on 18.01.18.
//  Copyright © 2018 Metrolab Technology SA. All rights reserved.
//

#include <stdio.h>

#include "THM1176.h"
#include "VISAInstrument.h"
#include "Exception.h"
#include "Helpers.h"

#define THM1176_VISA_RESOURCE_PATTERN	"USB[0-9]*::0x1BFA::0x0498::[0-9]+::INSTR"
#define THM1176_CONNECT_TIMEOUT			5000	// ms

#define BLOCK_SIZE_MIN					256
#define BLOCK_SIZE_MAX					4096
#define BLOCK_SIZE_INC					256

#define AVERAGING_COUNT_MIN				100
#define AVERAGING_COUNT_MAX				1000
#define AVERAGING_COUNT_INC				100


using namespace MTL;
using namespace MTL::Instrument;
using namespace MTL::Instrument::THM1176Types;

U64 MeasureImmediateTiming(CTHM1176Instrument<CVISAInstrument, CVISAResourceManager> * pTHM1176, U16 BlockSize, U16 AveragingCount)
{
	// Create the command.
	std::string l_Command;
	l_Command += "*RST";											// Reset to set counts = 1, trigger source = Immediate
	l_Command += ";:INIT;:FETC:TIM?";								// Perform one measurement and fetch the initial timestamp
	l_Command += ";:TRIG:COUN " + std::to_string(BlockSize);		// Set the trigger count as desired
	l_Command += ";:AVER:COUN " + std::to_string(AveragingCount);	// Set the averaging count as desired
	l_Command += ";:INIT;:FETC:TIM?";								// Perform the measurements and fetch the final timestamp

	// Execute the command.
	CSCPIBuffer				l_ReadBuffer(65535);
	if (!pTHM1176->WriteAndRead(l_Command, l_ReadBuffer))
	{
		CErrorList l_ErrorList = pTHM1176->CurrentErrorList();
		for (auto l_pError = l_ErrorList.begin(); l_pError < l_ErrorList.end(); l_pError++)
			std::cerr << "Error: " << l_pError->Code << "; " << l_pError->Description << std::endl;
		throw MTL::CException<CTHM1176Instrument<CVISAInstrument, CVISAResourceManager>>("Write and Read failed", MTL__LOCATION__);
	}

	// Parse the results.
	CSCPIBufferParser l_BP(l_ReadBuffer.begin(), l_ReadBuffer.end());
	CSCPIBufferParser::tTokens l_Tokens = l_BP.Tokenize();
	if (l_Tokens.size() != 2)
		throw MTL::CException<CTHM1176Instrument<CVISAInstrument, CVISAResourceManager>>("Invalid number of tokens in answer", MTL__LOCATION__);

	// Retrieve the timestamps and return the time for the requested measurement.
	std::string l_Token0 = std::string(l_Tokens[0].begin, l_Tokens[0].end);
	std::string l_Token1 = std::string(l_Tokens[1].begin, l_Tokens[1].end);
	U64 l_T0 = std::stoull(l_Token0, 0, 0);
	U64 l_T1 = std::stoull(l_Token1, 0, 0);
	return (l_T1 - l_T0);
}

int main(void)
{
	CVISAResourceManager *	l_pResourceManager = NULL;
	CTHM1176Instrument<CVISAInstrument, CVISAResourceManager> *	l_pTHM1176 = NULL;

	try
	{
		// Create and initialize the VISA resource manager.
		l_pResourceManager = new CVISAResourceManager;
		if (NULL == l_pResourceManager || !l_pResourceManager->Initialize())
			throw MTL::CException<CTHM1176Instrument<CVISAInstrument, CVISAResourceManager>>("Cannot initialize VISA Resource Manager", MTL__LOCATION__);
		
		// Get the resource list.
		CResourceList l_InstrumentList;
		l_pResourceManager->FindResources(l_InstrumentList, THM1176_VISA_RESOURCE_PATTERN);
		if (l_InstrumentList.empty())
			throw MTL::CException<CTHM1176Instrument<CVISAInstrument, CVISAResourceManager>>("No instrument connected", MTL__LOCATION__);
		
		// Create a new THM1176 object.
		l_pTHM1176 = new CTHM1176Instrument<CVISAInstrument, CVISAResourceManager>(*l_pResourceManager, l_InstrumentList[0]);
		if (NULL == l_pTHM1176)
			throw MTL::CException<CTHM1176Instrument<CVISAInstrument, CVISAResourceManager>>("Cannot create THM1176 object", MTL__LOCATION__);

		// Connect to the THM1176.
		if (!l_pTHM1176->Connect(THM1176_CONNECT_TIMEOUT))
			throw MTL::CException<CTHM1176Instrument<CVISAInstrument, CVISAResourceManager>>("Cannot connect to THM1176", MTL__LOCATION__);
		
		// Loop over block size and averaging count.
		for (U16 l_BlockSize = BLOCK_SIZE_MIN; l_BlockSize <= BLOCK_SIZE_MAX; l_BlockSize += BLOCK_SIZE_INC)
		{
			for (U16 l_AveragingCount = AVERAGING_COUNT_MIN; l_AveragingCount <= AVERAGING_COUNT_MAX; l_AveragingCount += AVERAGING_COUNT_INC)
				std::cout << l_BlockSize << "\t" << l_AveragingCount << "\t" <<
					MeasureImmediateTiming(l_pTHM1176, l_BlockSize, l_AveragingCount) << std::endl;
		}
		
		// Shut everything down.
		l_pTHM1176->Disconnect();
		delete l_pTHM1176;
		delete l_pResourceManager;
	}
	catch (MTL::CException<CTHM1176Instrument<CVISAInstrument, CVISAResourceManager>> & rE)
	{
		std::cerr << rE.what() << std::endl;
		if (l_pTHM1176)
		{
			l_pTHM1176->Disconnect();
			delete l_pTHM1176;
		}
		if (l_pResourceManager)
			delete l_pResourceManager;
		return -1;
	}
	return 0;
}
