/******************************************************************************
 *                                                                         
 *               Copyright 2004-2006 Agilent Technologies
 *               All rights reserved.
 *
 *****************************************************************************/

#include "stdafx.h"
#include "CoAgilent34980A.h"

/// <summary>
/// Changes the state of the triggering system from the idle state to the wait-for-trigger
/// state.
/// </summary>
/// <remarks>
/// Measurements will begin when the specified trigger conditions are satisfied
/// following the Initiate method.  Note that Initiate also clears the previous
/// set of readings from memory.
/// </remarks>
/// <returns>
///	
/// </returns>
HRESULT Agilent34980A::IAgilent34980AMeasurement_Initiate()
{
	HRESULT hr = S_OK;

	hr = InstrPrintCommand();

	return hr;
}

/// <summary>
/// Aborts a measurement in progress from a scan or internal DMM measurements
/// </summary>
/// <remarks>
/// 
/// </remarks>
/// <returns>
///	
/// </returns>
HRESULT Agilent34980A::IAgilent34980AMeasurement_Abort()
{
	HRESULT hr = S_OK;

	hr = InstrPrintCommand();

	return hr;
}





/// <summary>
/// Reads and erases readings from volatile memory up to the specified MaxCount.
/// </summary>
/// <remarks>
/// The readings are erased from memory starting with the oldest (not the most recent)
/// reading first. The purpose of this method is to allow you to periodically remove
/// readings from memory that would normally cause reading memory to overflow (for
/// example, during a scan with an infinite scan count).  WARNING: All data formatting
/// must be disabled (the default) before using this routine (see IAgilent34980ADataFormat.Configure
/// Method). If formatting is enabled, this routine will error and perhaps break
/// your application program as well.
/// </remarks>
/// <returns>
///	
/// </returns>
/// <param name="MaxCount">
///	Maximum readings to be removed from memory.
/// </param>
HRESULT Agilent34980A::IAgilent34980AMeasurement_DestructiveRead(long MaxCount, SAFEARRAY** val)
{
	HRESULT hr = S_OK;

	CString strFormat;
	hr = InstrQueryf(_T("FORM?"), strFormat, DEFAULT_STRING_LENGTH);
	if (SUCCEEDED(hr))
	{
		hr = InstrPrintCommandNoPoll();
		if (SUCCEEDED(hr))
		{
			if (strFormat.CompareNoCase(_T("ASC,9")) == 0)
			{
				// Example format:
				// #231+2.87536000E-04,+3.18131400E-03

				// Read in # sign and number of header digits

				// Temporarily disable error query if it is on.
				bool bQueryInstrStatus = m_bQueryInstrStatus;
				m_bQueryInstrStatus = false;
				CString strResult;
				hr = InstrRead(strResult, 2);
				if (SUCCEEDED(hr))
				{
					strResult.Trim(_T('#'));
					int nHeaderLen = _ttoi(LPCTSTR(strResult));
					hr = InstrRead(strResult, nHeaderLen);
					if (SUCCEEDED(hr))
					{
						hr = FormattedReadList(NtlAsciiType_R8, _T(","), val);
					}
				}
				m_bQueryInstrStatus = bQueryInstrStatus;
			}
			else if (strFormat.CompareNoCase(_T("REAL,64")) == 0)
			{
				hr = FormattedReadIEEEBlock(NtlBinaryType_R8, val);
			}
			else
			{
				SAFEARRAY* psaTemp = NULL;
				hr = FormattedReadIEEEBlock(NtlBinaryType_R4, &psaTemp);
				if (SUCCEEDED(hr))
				{
					CComSafeArray<float> saTemp;
					saTemp.Attach(psaTemp);

					CComSafeArray<double> saOut(saTemp.GetCount());

					for (int i = 0; i < (int)saTemp.GetCount(); i++)
					{
						saOut.SetAt(i, saTemp[i]);
					}

					*val = saOut.Detach();
				}
			}
		}
	}

	return hr;
}



/// <summary>
/// Transfers readings from volatile memory to the instrument's output buffer where
///  you can read them into your computer.
/// </summary>
/// <remarks>
/// The readings are not erased from memory when you read them. You can use the
///  method multiple times to retrieve the same data in reading memory.
/// </remarks>
/// <returns>
///	
/// </returns>
HRESULT Agilent34980A::IAgilent34980AMeasurement_Fetch(SAFEARRAY** val)
{
	HRESULT hr = S_OK;

	CString sResp;
	// we will gather strings in which entries will be divided by the \n char, 
	// so we want the \n *in* the response. ',' is not a good divider since an entry can have many
	SetTrailingForReads( "" ); 
	hr = InstrWriteNoPoll(_T("FETC?"));
	if (SUCCEEDED(hr))
	{
		hr = FormattedReadString(sResp);
	}
	SetTrailingForReads( "\n" );
	if( FAILED(hr) ) // trailing restored, just return
		return hr;

	CString sCount;
	hr = InstrQueryf( "DATA:POIN?", sCount, DEFAULT_STRING_LENGTH );
	sCount.Remove( '+' );
	int nCount = atoi( (LPCTSTR)sCount);
	// create the SA
	SAFEARRAYBOUND rgsabound;
	rgsabound.lLbound = 0;
	rgsabound.cElements = nCount;
	*val = ::SafeArrayCreate( VT_BSTR, 1, &rgsabound );
	if( *val==NULL )
		return ReportError( IDS_E_IVI_OUT_OF_MEMORY );
	if( nCount<=0 )
		return S_OK; // if there is no data, don't raise exceptions
	// dont count separating comas
	int nSize = ( sResp.GetLength() - nCount ) / nCount; 
	for( int i=0; i<nCount; i++ )
	{
		long idx = i;
		BSTR bsElem = sResp.Mid( i*nSize + i, nSize ).AllocSysString();
		hr = ::SafeArrayPutElement( *val, &idx, bsElem );
		if( FAILED(hr) )
		{
			::SafeArrayDestroy( *val );
			return ReportError( IDS_E_IVI_UNEXPECTED_RESPONSE );
		}
		::SysFreeString( bsElem );
	}
	return hr;
}


/// <summary>
/// Transfers readings from volatile memory to the instrument's output buffer where
/// you can read them into your computer.
/// </summary>
/// <remarks>
/// The readings are not erased from memory when you read them. You can use the
/// method multiple times to retrieve the same data in reading memory.
/// 
/// WARNING: All data formatting must be disabled (the default) before using this
/// routine (see IAgilent34980ADataFormat.Configure Method).  If formatting is enabled,
/// this routine will error and perhaps break your application program as well.
/// </remarks>
/// <returns>
///	
/// </returns>
HRESULT Agilent34980A::IAgilent34980AMeasurement_FetchNumbersOnly(SAFEARRAY** val)
{
	HRESULT hr = S_OK;

	RETURN_IF_FAIL( InstrWriteNoPoll( CString(":FETC?") ) );
	hr = FormattedReadList( NtlAsciiType_R8, ",", val );

	return hr;
}

/// <summary>
///	Returns pointer to IAgilent34980ADataFormat interface.
/// </summary>
/// <remarks>
///	
/// </remarks>
HRESULT Agilent34980A::IAgilent34980AMeasurement_get_DataFormat(IAgilent34980ADataFormat** Val)
{
	HRESULT hr = S_OK;

	hr = InternalQueryInterface(this, _GetEntries(), __uuidof(*Val), reinterpret_cast<void**>(Val));

	return hr;
}



/// <summary>
/// This method returns the last measurement taken on the specified channel (single channel only). 
/// </summary>
/// <remarks>
/// You can execute this method at any time, even during a scan.
/// If you omit the optional channel parameter, this method applies to the internal
/// DMM, independent of any channels or a scan list.
/// </remarks>
/// <returns>
///	
/// </returns>
/// <param name="Channel">
///	One channel in the form sccc.
/// </param>
HRESULT Agilent34980A::IAgilent34980AMeasurement_QueryLast(BSTR Channel, double* Val)
{
	HRESULT hr = S_OK;

	CString sList(Channel);
	if( -1 != sList.Find( ',' ) || -1!=sList.Find( ':' ) || -1 != sList.Find( '-' ) )
	{
		return ReportInvalidValueError( _T("IAgilent34980AMeasurement_QueryLast"), _T("Channel"), sList );
	}
	hr = InstrQueryCommand(Val);

	return hr;
}

/// <summary>
/// Returns the total number of readings currently stored in reading memory from a scan or internal DMM measurements.
/// </summary>
/// <remarks>
///	
/// </remarks>
HRESULT Agilent34980A::IAgilent34980AMeasurement_get_ReadingCount(long* Val)
{
	HRESULT hr = S_OK;

	hr = InstrQueryCommand(Val);

	return hr;
}

/// <summary>
/// Changes the instrument's triggering system from the idle state to the wait-for-trigger
/// </summary>
/// <remarks>
/// Measurements will begin when the specified trigger conditions are satisfied. 
/// Readings are then sent immediately to volatile memory and the instrument output 
/// buffer. If you supply an empty ChannelList, measurements are taken on the DMM 
/// only (no channels are scanned) or, if a scan list is currently defined (see 
/// IAgilent34980AScan.ScanList property), the READ with empty channel list 
/// performs a scan of the channels in the scan list.
/// </remarks>
/// <returns>
///	
/// </returns>
/// <param name="ChannelList">
///	One or more channels in the form sccc.
/// </param>
HRESULT Agilent34980A::IAgilent34980AMeasurement_Read(BSTR ChannelList, SAFEARRAY** val)
{
	HRESULT hr = S_OK;
	CorrectChannelList( ChannelList ); // take care of empty channel list

	CString sResp;
	// we will gather strings in which entries will be divided by the \n char, 
	// so we want the \n *in* the response. ',' is not a good divider since an entry can have many
	SetTrailingForReads( "" ); 
	hr = InstrPrintCommandNoPoll();
	if (SUCCEEDED(hr))
	{
		hr = FormattedReadString(sResp);
	}
	SetTrailingForReads( "\n" );
	if( FAILED(hr) ) // trailing restored, just return
		return hr;


	CString sCount;
	hr = InstrQueryf( "DATA:POIN?", sCount, DEFAULT_STRING_LENGTH );
	sCount.Remove( '+' );
	int nCount = atoi( (LPCTSTR)sCount);
	// create the SA
	SAFEARRAYBOUND rgsabound;
	rgsabound.lLbound = 0;
	rgsabound.cElements = nCount;
	*val = ::SafeArrayCreate( VT_BSTR, 1, &rgsabound );
	if( *val==NULL )
		return ReportError( IDS_E_IVI_OUT_OF_MEMORY );
	if( nCount<=0 )
		return S_OK; // if there is no data, don't raise exceptions
	// dont count separating comas
	int nSize = ( sResp.GetLength() - nCount ) / nCount; 
	for( int i=0; i<nCount; i++ )
	{
		long idx = i;
		BSTR bsElem = sResp.Mid( i*nSize + i, nSize ).AllocSysString();
		hr = ::SafeArrayPutElement( *val, &idx, bsElem );
		if( FAILED(hr) )
		{
			::SafeArrayDestroy( *val );
			return ReportError( IDS_E_IVI_UNEXPECTED_RESPONSE );
		}
		::SysFreeString( bsElem );
	}
	return hr;
}

/// <summary>
/// Changes the instrument's triggering system from the idle state to the wait-for-trigger
/// state.
/// </summary>
/// <remarks>
/// Measurements will begin when the specified trigger conditions are satisfied.
///  Readings are then sent immediately to volatile memory and the instrument output
/// buffer.  If you supply an empty ChannelList, measurements are taken on the DMM
/// only (no channels are scanned) or, if a scan list is currently defined (see
/// Scan.ScanList property), the READ with empty channel list performs a scan of
/// the channels in the scan list.  WARNING: All data formatting must be disabled
/// (the default) before using this routine (see IAgilent34980ADataFormat.Configure
/// Method).  If formatting is enabled, this routine will error and perhaps break
/// your application program as well.
/// </remarks>
/// <returns>
///	
/// </returns>
/// <param name="ChannelList">
///	One or more channels in the form sccc.
/// </param>
HRESULT Agilent34980A::IAgilent34980AMeasurement_ReadNumbersOnly(BSTR ChannelList, SAFEARRAY** val)
{
	HRESULT hr = S_OK;

	CorrectChannelList( ChannelList ); // take care of empty channel list

	RETURN_IF_FAIL( InstrPrintCommandNoPoll() );
	hr = FormattedReadList( NtlAsciiType_R8, ",", val );

	return hr;
}



/// <summary>
/// Reads and erases the specified number of readings from memory.
/// </summary>
/// <remarks>
/// The readings are erased from memory starting with the oldest reading first.
/// The purpose of this method is to allow you to periodically remove readings from
/// memory during a scan than would normally cause reading memory to overflow (for
/// example, during a scan with an infinite scan count).  WARNING: All data formatting
/// must be disabled (the default) before using \r\n
/// this routine (see IAgilent34980ADataFormat.Configure Method). If formatting
/// is enabled, this routine will error and perhaps break your program as well.
/// </remarks>
/// <returns>
///	
/// </returns>
/// <param name="NumReadings">
///	Number of readings to be removed from memory.
/// </param>
HRESULT Agilent34980A::IAgilent34980AMeasurement_RemoveReadings(long NumReadings, SAFEARRAY** val)
{
	HRESULT hr = S_OK;

	CString strFormat;
	hr = InstrQuery(_T("FORM?"), strFormat, DEFAULT_STRING_LENGTH);
	if (SUCCEEDED(hr))
	{
		hr = InstrPrintCommandNoPoll();
		if (SUCCEEDED(hr))
		{
			if (strFormat.CompareNoCase(_T("ASC,9")) == 0)
			{
				hr = FormattedReadList(NtlAsciiType_R8, _T(","), val);
			}
			else if (strFormat.CompareNoCase(_T("REAL,64")) == 0)
			{
				hr = FormattedReadIEEEBlock(NtlBinaryType_R8, val);
			}
			else
			{
				SAFEARRAY* psaTemp = NULL;
				hr = FormattedReadIEEEBlock(NtlBinaryType_R4, &psaTemp);
				if (SUCCEEDED(hr))
				{
					CComSafeArray<float> saTemp;
					saTemp.Attach(psaTemp);

					CComSafeArray<double> saOut(saTemp.GetCount());

					for (int i = 0; i < (int)saTemp.GetCount(); i++)
					{
						saOut.SetAt(i, saTemp[i]);
					}

					*val = saOut.Detach();
				}
			}
		}
	}

	return hr;
}

/// <summary>
/// Triggers the instrument from the remote interface.
/// </summary>
/// <remarks>
/// 
/// </remarks>
/// <returns>
///	
/// </returns>
HRESULT Agilent34980A::IAgilent34980AMeasurement_SendSoftwareTrigger()
{
	HRESULT hr = S_OK;

	hr = InstrPrintCommand();

	return hr;
}






/// <summary>
///	Sets or gets the measurement function on the specified channel.  If the channelList
/// parameter is empty, the function is changed on the DMM only, independent of
/// any scan list.
/// </summary>
/// <param name="Channel">
///	One channel in the form sccc
/// </param>
HRESULT Agilent34980A::IAgilent34980AMeasurement_get_MeasurementFunction(BSTR Channel, BSTR* Val)
{
	HRESULT hr = S_OK;

	CString sList(Channel);
	if( -1 != sList.Find( ',' ) || -1!=sList.Find( ':' ) || -1 != sList.Find( '-' ) )
	{
		return ReportInvalidValueError( _T("IAgilent34980AMeasurement_get_MeasurementFunction"), _T("Channel"), sList );
	}
	CorrectChannelList( Channel, false );
	hr = InstrQueryCommand( Val );
	return hr;
}


/// <summary>
///	Sets or gets the measurement function on the specified channel.  If the channelList
/// parameter is empty, the function is changed on the DMM only, independent of
/// any scan list.
/// </summary>
/// <remarks>
///   Allowable string values for the various function types are as follows:\r\n
/// |------------------------------------------------------|\r\n
/// |   Current AC             |         CURR:AC           |\r\n
/// |------------------------------------------------------|\r\n
/// |   Current DC             |         CURR or CURR:DC   |\r\n
/// |------------------------------------------------------|\r\n
/// |   Frequency              |         FREQ              |\r\n
/// |------------------------------------------------------|\r\n
/// |   Resistance (4-Wire)    |         RES               |\r\n
/// |------------------------------------------------------|\r\n
/// |   Resistance (2-Wire)    |         RES               |\r\n
/// |------------------------------------------------------|\r\n
/// |   Period                 |         PER               |\r\n
/// |------------------------------------------------------|\r\n
/// |   Temperature            |         TEMP              |\r\n
/// |------------------------------------------------------|\r\n
/// |   Voltage AC             |         VOLT:AC           |\r\n
/// |------------------------------------------------------|\r\n
/// |   Voltage DC             |         VOLT or VOLT:DC   |\r\n
/// |------------------------------------------------------|
/// </remarks>
/// <param name="Channel">
///	One channel in the form sccc
/// </param>
HRESULT Agilent34980A::IAgilent34980AMeasurement_put_MeasurementFunction(BSTR Channel, BSTR val)
{
	HRESULT hr = S_OK;

	CorrectChannelList( Channel );
	hr = InstrPrintCommand();

	return hr;
}
