/*------------------------------------------------------------------------------*
 * File Name:	BinFile.h	 													*
 * Creation: Sophy 9/4/2009														*
 * Purpose: Handle/Import Slice Control Binary File Format						*
 * Copyright (c) OriginLab Corp.2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010	*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *------------------------------------------------------------------------------*/
 
#include <Origin.h>

#define	GET_MEMBER(_FIELD)	(m_pFileHeader->_FIELD)
#define	MAX_UNIT_LEN	50
#define	ISO_CODE_LEN	16


#define	STR_TAG_ORIGIN_START	"<OriginStorage>"
#define	STR_TAG_ORIGIN_END		"</OriginStorage>"
#define	STR_XML_START		"<?xml"
#define	STR_XML_VERSION		"version="
#define	STR_TEMP_XML_FILE	(GetAppPath(TRUE) + "ORIGIN_CHANNEL_DESCRIPT.dts")
#define	RETURN_EX(_Ret)		(stdFile.Close(); stdTemp.Close(); return _Ret;)
static bool	_check_update_XML(LPCSTR lpcszDTS, string& strResultFile)
{
	stdioFile stdFile(lpcszDTS, file::modeRead | file::shareDenyWrite);
	stdioFile stdTemp(STR_TEMP_XML_FILE, file::modeCreate | file::modeWrite);
	if ( !stdFile.IsOpen() || !stdTemp.IsOpen() )
		return false;
	
	string strLine;
	while ( stdFile.ReadString(strLine) )
	{
		if ( strLine.Find(STR_XML_START) >= 0 )
		{
			string strNew = "";
			vector<string> vs;
			strLine.GetTokens(vs);
			int index = vs.Find(STR_XML_START);
			if ( index < 0 )
			{
				RETURN_EX(false);
			}
			strNew = vs[index];
			index = vs.Find(STR_XML_VERSION, 1, false, false);
			if ( index < 0 )
			{
				RETURN_EX(false);
			}
			strNew += " ";
			strNew += vs[index];
			if ( strNew.GetAt(strNew.GetLength() - 1) != '>' )
				strNew += "?>"; //add end
			
			stdTemp.WriteString(strNew);
			stdTemp.WriteString(STR_TAG_ORIGIN_START);
		}
		else
			stdTemp.WriteString(strLine);
	}
	stdTemp.WriteString(STR_TAG_ORIGIN_END);
	strResultFile = STR_TEMP_XML_FILE;
	RETURN_EX(true);
}

static	bool _check_make_CHN(LPCSTR lpcszDTSFile, int iChn, string& strChnFile)
{
	strChnFile.Format("%s%s.%d.chn", GetFilePath(lpcszDTSFile), GetFileName(lpcszDTSFile, TRUE), iChn);
	return strChnFile.IsFile();
}


#define	STR_CHN_FILENAME	"CHNFile"
#define	STR_CHANNEL_FMT		_L("Channel %d")
#define	STR_MODULE_FMT		_L("Module %d")
#define	STR_FILENAME_FMT	_L("Channel %d Data")
#define	STR_CHN_LABEL_FMT	_L("Analog Input Channel %d")

#define	STR_CHN_FILEINFO	"FileInfo"
#define	STR_CHN_FILE_INFO_L	_L("Channel File Details")

#define	STR_MODULE_INFO		"ModuleInfo"
#define	STR_MODULE_ATTRIBS	_L("Module Attributes")
//module attributes
#define	STR_NUMBER			"Number"
#define	STR_NUM_CHANNELS	"NumberOfChannels"
#define	STR_PRETRIGGER_SECS	"PreTriggerSeconds"
#define	STR_PSTTRIGGER_SECS	"PostTriggerSeconds"
#define	STR_RECORD_MODE		"RecordingMode"
#define	STR_FILTER_RATE_HZ	"AaFilterRateHz"
#define	STR_SERIAL_NUMBER	"SerialNumber"
#define	STR_NUM_SAMPLES		"NumberOfSamples"
#define	STR_USS_NUM_SAMPLES	"UnsubsampledNumberOfSamples"
#define	STR_SAMPLE_RATE_HZ	"SampleRateHz"
#define	STR_START_REC_SN	"StartRecordSampleNumber"

//module info labels
#define	STR_PRE_SECS_L		_L("Pre-Trigger Seconds")
#define	STR_POST_SECS_L		_L("Post-Trigger Seconds")
#define	STR_SAMPLE_RATE_L	_L("Sample Rate(Hz)")
#define	STR_NUM_SAMPLES_L	_L("Number of Samples")
#define	STR_RECORD_MODE_L	_L("Record Mode")
#define	STR_NUM_CHANNELS_L	_L("Number of Channels")
#define	STR_START_DEC_SN_L	_L("Start Record Sample Number")

//channel attributes
#define	STR_CHANNEL_TYPE	"ChannelType"
#define	STR_START			"Start"
#define	STR_BRIDGE			"Bridge"
#define	STR_BRIDGE_RESIST	"BridgeResistanceOhms"
#define	STR_CHANNEL_DESC	"ChannelDescriptionString"
#define	STR_DESCRIPT		"Description"
#define	STR_DESIRED_RANGE	"DesiredRange"
#define	STR_SENSITIVITY		"Sensitivity"
#define	STR_SOFTWARE_FILTER	"SoftwareFilter"
#define	STR_PP_TO_EXCITE	"ProportionalToExcitation"
#define	STR_IS_INVERTED		"IsInverted"
#define	STR_IS_SUBSAMPLED	"IsSubsampled"
#define	STR_EXCITE_VOLT		"ExcitationVoltage"
#define	STR_EU				"Eu"
#define	STR_SHUNT_ENBALED	"ShuntEnabled"
#define	STR_REMOVE_OFFSET	"RemoveOffset"
#define	STR_ZERO_METHOD		"ZeroMethod"
#define	STR_ZM_WIN_BEGIN	"ZeroAverageWindowBegin"
#define	STR_ZM_WIN_END		"ZeroAverageWindowEnd"
#define	STR_INITIAL_EU		"InitialEu"
#define	STR_USS_RATE_HZ		"UnsubsampledSampleRateHz"
#define	STR_MESURED_SD_MV	"MeasuredShuntDeflectionMv"
#define	STR_TARGET_SD_MV	"TargetShuntDeflectionMv"

#define	LABEL_NODE(_Node, _Label)	_Node.SetAttribute(STR_LABEL_ATTRIB, _Label);

///-------------------------------------------Channel File Class Begin---------------------------------------------///

#pragma pack(push, 1)

typedef struct tagChannelFileHeader {
	UINT		unFileID;		//Magic key to identify file 0x2C36351F
	UINT		unFileVersion;	//Version number of the file header
	__int64		n64SampleDataOffset;	//Offset(in bytes) from start of file to where data samples start
	__int64		n64NumOfSamples;		//Number of samples in this file
	UINT		unBitsPerSample;		//Number of bits per sample
	UINT		unSampleSign;			//0 = unsigned samples, 1 = signed samples
	double		dSampleRate;			//Sample rate
	USHORT		usNumOfTriggers;		//Number of triggers, might be 0
	double*		pn64TriggerSampleNum;	//Trigger sample number //__int64* will make Origin crash on malloc
	
	int			nPreTestZeroLevel;		//Pre Test zero level(in counts)
	int			nPreTestCallLevel;		//Pre Test call level(in counts)
	double		dPreTestNoise;			//Pre test noise as a percent of FS
	int			nPostTestZeroLevel;		//Post Test zero level(in counts)
	int			nPostTestCallLevel;		//Post Test call level(in counts)
	int			nDataZeroLevel;			//Data-Zero level(in counts)
	double		dScaleFactorMV;			//Scale factor MV(mV/Count)
	double		dScaleFactorEU;			//Scale factor EU(eng_units/Count)
	USHORT		usEngUnitSize;			//number of bytes in engineering unit field + 1
	char		caEngUnit[MAX_UNIT_LEN];//Engineering unit(without NULL termination)
	char		caISOCode[ISO_CODE_LEN + 1]; //ISO code
	UINT		unCRC32;				//CRC32 for entire file
} ChannelFileHeader, *pChannelFileHeader;

#pragma pack(pop)

class ChannelFile
{
public:
	ChannelFile(LPCSTR lpcszFileName);
	~ChannelFile();
	BOOL	Open(LPCSTR lpcszFileName);
	BOOL	Reopen();
	BOOL	Import(Worksheet& wks, int iCol = 0);
	void	Close();
	
	//get file information
	BOOL	Init(LPCSTR lpcszDTS, TreeNode& trChannel);
	
	BOOL	GetFileInfo(TreeNode& trInfo);
	
private:
	//init file info
	BOOL	readHeader();
	
	//utility
	int		getDataType(const uint nBits, const uint nSign);
	
	//GUI
	BOOL	updateLabels(TreeNode& trInfo);
	
	//Import information
	void	updateUserInfo(Column& colObj);
private:
	FILE*				m_pFile;
	pChannelFileHeader	m_pFileHeader;
	TreeNode			m_trChannel;
	string				m_strFileName;
	int					m_nDataType;
};

ChannelFile::ChannelFile(LPCSTR lpcszFileName)
{
	Open(lpcszFileName);
}

ChannelFile::~ChannelFile()
{
	Close();
}

BOOL	ChannelFile::Open(LPCSTR lpcszFileName)
{
	m_pFile = fopen(lpcszFileName, "rb");
	if ( NULL == m_pFile )
		return FALSE;
	
	m_pFileHeader = new ChannelFileHeader;
	
	m_strFileName = lpcszFileName;
	
	return readHeader();
}

BOOL	ChannelFile::Reopen()
{
	Close();
	return Open(m_strFileName);
}

BOOL	ChannelFile::Import(Worksheet& wks, int iCol)
{
	if ( !wks )
		return FALSE;
	if ( iCol >= wks.GetNumCols() - 1 && !wks.SetSize(-1, iCol + 2) )
		return FALSE;
	
	fseek(m_pFile, GET_MEMBER(n64SampleDataOffset), SEEK_SET);
	
	Column colTime(wks, iCol); //time
	colTime.SetLongName("Time");
	colTime.SetUnits("second");
	colTime.SetType(OKDATAOBJ_DESIGNATION_X);
	colTime.SetFormat(OKCOLTYPE_NUMERIC);
	colTime.SetInternalDataType(FSI_DOUBLE);
	vectorbase& vbTime = colTime.GetDataObject();
	vector vTime(GET_MEMBER(n64NumOfSamples));
	
	double dInc = 1.0 / GET_MEMBER(dSampleRate);
	double dStart = 0;
	double dSampleNum = GET_MEMBER(n64NumOfSamples);
	double dTriggers = GET_MEMBER(usNumOfTriggers);
	double dEnd = dInc * (dSampleNum - dTriggers);
	vTime.Data(dStart, dEnd, dInc);
	vbTime = vTime;
	
	Column colTarget(wks, iCol + 1); //data
	string strLN;
	int	nChannelNo;
	m_trChannel.GetAttribute(STR_NUMBER, nChannelNo);
	strLN.Format(STR_CHANNEL_FMT, nChannelNo);
	colTarget.SetLongName(strLN);
	colTarget.SetComments(m_trChannel.GetNode(STR_CHN_FILENAME).strVal);
	colTarget.SetUnits(GET_MEMBER(caEngUnit));
	colTarget.SetFormat(OKCOLTYPE_NUMERIC);
	colTarget.SetInternalDataType(FSI_DOUBLE); //always set as double, since we will get float data after calculate result from raw data
	
	vectorbase& vbInternal = colTarget.GetDataObject();
	
	switch(m_nDataType)
	{
	case FSI_CHAR:
		vector<char> vcData(GET_MEMBER(n64NumOfSamples));
		fread(vcData, sizeof(char), vcData.GetSize(), m_pFile);
		vbInternal = vcData;
		
		break;
		
	case FSI_BYTE:
		vector<byte> vbData(GET_MEMBER(n64NumOfSamples));
		fread(vbData, sizeof(byte), vbData.GetSize(), m_pFile);
		vbInternal = vbData;
		
		break;
		
	case FSI_SHORT:
		vector<short> vnData(GET_MEMBER(n64NumOfSamples));
		fread(vnData, sizeof(short), vnData.GetSize(), m_pFile);
		vbInternal = vnData;

		break;
		
	case FSI_USHORT:
		vector<ushort> vnData(GET_MEMBER(n64NumOfSamples));
		fread(vnData, sizeof(ushort), vnData.GetSize(), m_pFile);
		vbInternal = vnData;
		
		break;
		
	case FSI_LONG:
		vector<int> vnData(GET_MEMBER(n64NumOfSamples));
		fread(vnData, sizeof(int), vnData.GetSize(), m_pFile);
		vbInternal = vnData;
		
		break;
		
	case FSI_ULONG:
		vector<uint> vnData(GET_MEMBER(n64NumOfSamples));
		fread(vnData, sizeof(uint), vnData.GetSize(), m_pFile);
		vbInternal = vnData;
		break;
		
		
	case FSI_REAL:
		vector<float> vData(GET_MEMBER(n64NumOfSamples));
		fread(vData, sizeof(float), vData.GetSize(), m_pFile);
		vbInternal = vData;
		
		break;
		
	case FSI_DOUBLE:
		vector<double> vData(GET_MEMBER(n64NumOfSamples));
		fread(vData, sizeof(double), vData.GetSize(), m_pFile);
		vbInternal = vData;
		
		break;
		
	default:
		ASSERT(FALSE);
		return FALSE;
	}
	//calculate raw data. formula from customer : (16 bit ADC - Data Zero) * ScaleFactorEU
	vbInternal -= GET_MEMBER(nDataZeroLevel);
	vbInternal *= GET_MEMBER(dScaleFactorEU);
	updateUserInfo(colTarget);
	
	return TRUE;
}

BOOL	ChannelFile::readHeader()
{
	fseek(m_pFile, 0, SEEK_SET);
	
	fread(&GET_MEMBER(unFileID), sizeof(uint), 1, m_pFile);
	fread(&GET_MEMBER(unFileVersion), sizeof(uint), 1, m_pFile);
	fread(&GET_MEMBER(n64SampleDataOffset), sizeof(__int64), 1, m_pFile);
	fread(&GET_MEMBER(n64NumOfSamples), sizeof(__int64), 1, m_pFile);
	fread(&GET_MEMBER(unBitsPerSample), sizeof(uint), 1, m_pFile);
	fread(&GET_MEMBER(unSampleSign), sizeof(uint), 1, m_pFile);
	fread(&GET_MEMBER(dSampleRate), sizeof(double), 1, m_pFile);
	fread(&GET_MEMBER(usNumOfTriggers), sizeof(USHORT), 1, m_pFile);
	
	LONG lOffset = ftell(m_pFile);
	if ( GET_MEMBER(usNumOfTriggers) != 0 )
	{
		GET_MEMBER(pn64TriggerSampleNum) = (double*)malloc( sizeof(double) * GET_MEMBER(usNumOfTriggers));
		fread(GET_MEMBER(pn64TriggerSampleNum), sizeof(double), GET_MEMBER(usNumOfTriggers), m_pFile);
	}
	else
	{
		GET_MEMBER(pn64TriggerSampleNum) = NULL;
	}
	
	fread(&GET_MEMBER(nPreTestZeroLevel), sizeof(int), 1, m_pFile);
	fread(&GET_MEMBER(nPreTestCallLevel), sizeof(int), 1, m_pFile);
	fread(&GET_MEMBER(dPreTestNoise), sizeof(double), 1, m_pFile);
	fread(&GET_MEMBER(nPostTestZeroLevel), sizeof(int), 1, m_pFile);
	fread(&GET_MEMBER(nPostTestCallLevel), sizeof(int), 1, m_pFile);
	fread(&GET_MEMBER(nDataZeroLevel), sizeof(int), 1, m_pFile);
	fread(&GET_MEMBER(dScaleFactorMV), sizeof(double), 1, m_pFile);
	fread(&GET_MEMBER(dScaleFactorEU), sizeof(double), 1, m_pFile);
	fread(&GET_MEMBER(usEngUnitSize), sizeof(USHORT), 1, m_pFile);
	fread(GET_MEMBER(caEngUnit), sizeof(char), GET_MEMBER(usEngUnitSize) - 1, m_pFile);
	fread(GET_MEMBER(caISOCode), sizeof(char), ISO_CODE_LEN, m_pFile);
	fread(&GET_MEMBER(unCRC32), sizeof(uint), 1, m_pFile);
	
	m_nDataType = getDataType(GET_MEMBER(unBitsPerSample), GET_MEMBER(unSampleSign));
	
	return TRUE;
}

int		ChannelFile::getDataType(const uint nBits, const uint nSign)
{
	uint nBytes = nBits / 8;
	switch(nBytes)
	{
	case sizeof(byte):
		if ( nSign )
			return FSI_CHAR;
		else
			return FSI_BYTE;
		
	case sizeof(short):
		if ( nSign )
			return FSI_SHORT;
		else
			return FSI_USHORT;
		
	case sizeof(int): //float is the same size
		if ( nSign )
			return FSI_LONG;
		else
			return FSI_ULONG;
		
	case sizeof(double):
		return FSI_DOUBLE;
		
	default:
		ASSERT(FALSE);
		return FSI_SHORT;
	}
	return FSI_SHORT;
}

void	ChannelFile::Close()
{
	if ( m_pFileHeader )
	{
		if ( GET_MEMBER(pn64TriggerSampleNum) != NULL )
			free(GET_MEMBER(pn64TriggerSampleNum));
		delete m_pFileHeader;
		m_pFileHeader = NULL;
	}
	if ( m_pFile )
	{
		fclose(m_pFile);
		m_pFile = NULL;
	}
}

BOOL	ChannelFile::Init(LPCSTR lpcszChannel, TreeNode& trChannel)
{
	string strCHNFile(lpcszChannel);
	
	TreeNode trCHNInfo = tree_check_get_node(trChannel, STR_CHN_FILEINFO);

	Open(strCHNFile);
	GetFileInfo(trCHNInfo);
	Close();
	
	m_trChannel = trChannel.Clone(TRUE);
	
	return TRUE;
}

BOOL	ChannelFile::GetFileInfo(TreeNode& trInfo)
{
	if ( !trInfo || !m_pFileHeader )
		return FALSE;
	
	trInfo = *m_pFileHeader;
	
	return updateLabels(trInfo);
}

//labels for GUI use
#define	STR_FILE_VERSION		_L("File Version")
#define	STR_SAMPLE_PTS			_L("Number of Sample Data Points")
#define	STR_BITS_PER_SAMPLE		_L("Bits Per Sample Data")
#define	STR_SAMPLE_SIGN			_L("Sample Sign")
#define	STR_SAMPLE_RATE			_L("Sample Rate")
#define	STR_TRIGGER_NUM			_L("Number of Triggers")
#define	STR_PTS_PRE_T_ZERO		_L("Number of Pre-Zero Data Points")
#define	STR_PTS_POST_T_ZERO		_L("Number of Post-Zero Data Points")
#define	STR_PRE_T_NOISE			_L("Pre Test Noise")
#define	STR_SCALE_FACTOR_MV		_L("Scale Factor MV")
#define	STR_SCALE_FACTOR_EU		_L("Scale Factor EU")
#define	STR_ENG_UNITS			_L("Engineering Units")
#define	STR_ISO_CODE			_L("ISO Code")

#define	SET_LABEL(_FIELD, _LABEL) (trInfo._FIELD.SetAttribute(STR_LABEL_ATTRIB, _LABEL))
BOOL	ChannelFile::updateLabels(TreeNode& trInfo)
{
	if ( !trInfo )
		return FALSE;
	
	SET_LABEL(unFileVersion, STR_FILE_VERSION);
	
	//Sophy, assigned __int64 member in struct to tree fail to create corresponding subnode, temp solution to add back useful fields
	TreeNode trSamples = trInfo.InsertNode(trInfo.unBitsPerSample, "n64NumOfSamples");
	trSamples.nVal = GET_MEMBER(n64NumOfSamples);
	SET_LABEL(n64NumOfSamples, STR_SAMPLE_PTS);
	
	SET_LABEL(unBitsPerSample, STR_BITS_PER_SAMPLE);
	SET_LABEL(unSampleSign, STR_SAMPLE_SIGN);
	SET_LABEL(dSampleRate, STR_SAMPLE_RATE);
	SET_LABEL(usNumOfTriggers, STR_TRIGGER_NUM);
	SET_LABEL(nPreTestZeroLevel, STR_PTS_PRE_T_ZERO);
	SET_LABEL(nPostTestZeroLevel, STR_PTS_POST_T_ZERO);
	SET_LABEL(dPreTestNoise, STR_PRE_T_NOISE);
	SET_LABEL(dScaleFactorMV, STR_SCALE_FACTOR_MV);
	SET_LABEL(dScaleFactorEU, STR_SCALE_FACTOR_EU);
	SET_LABEL(caEngUnit, STR_ENG_UNITS);
	SET_LABEL(caISOCode, STR_ISO_CODE);
	
	//hide nodes that are not useful for users.
	tree_filter_by_attrib_find(trInfo, 0, STR_LABEL_ATTRIB, "");
	
	return TRUE;
}

#define	STR_SOURCE_FILE			"Source File"
#define	STR_DATASET_INFO_TAG	"DatasetInfo"
#define	STR_DATASET_INFO_LABEL	_L("Dataset Information")
void	ChannelFile::updateUserInfo(Column& colObj)
{
	Tree trInfo;
	LABEL_NODE(trInfo, STR_DATASET_INFO_LABEL);
	GetFileInfo(trInfo);
	
	TreeNode trFile = trInfo.InsertNode(trInfo.FirstNode, "SrcFile");
	LABEL_NODE(trFile, STR_SOURCE_FILE);
	trFile.strVal = m_strFileName;
	
	tree_set_attribute_to_all_nodes(trInfo, "Enable", "2", true);
	
	string strInfoName(STR_DATASET_INFO_TAG);
	set_user_info(colObj, strInfoName, trInfo);
}

///-------------------------------------------Channel File Class End---------------------------------------------///

///-------------------------------------------Module Class Begin---------------------------------------------///


#include <Array.h>
class Module
{
public:
	Module(){}
	~Module();
	BOOL	Init(LPCSTR lpcszDTS, TreeNode& trModule);
	void	Close();
	
	//import
	BOOL	Import(Worksheet& wks, int iColStart = 0);
protected:
	BOOL	UpdateModuleInfo(TreeNode& trModule);
	
private:
	Array<ChannelFile&>	m_arrChannelFiles;	//array of channel objects
	TreeNode			m_trModule;
	//module attributes
	int					m_nChannels;		//number of channels
	int					m_nStartSampleNum;	//start record sample number
	int					m_nPreTriggerSecs;	//pretrigger seconds
	int					m_nPostTriggerSecs;	//posttrigger seconds
	double				m_dSampleRateHz;	//sample rate
	string				m_strRecordMode;	//recording mode
};

Module::~Module()
{
	Close();
}

BOOL	Module::Init(LPCSTR lpcszDTS, TreeNode& trModule)
{
	if ( !trModule )
		return FALSE;
	
	trModule.GetAttribute(STR_NUM_CHANNELS, m_nChannels);
	trModule.GetAttribute(STR_PRETRIGGER_SECS, m_nPreTriggerSecs);
	trModule.GetAttribute(STR_PSTTRIGGER_SECS, m_nPostTriggerSecs);
	trModule.GetAttribute(STR_SAMPLE_RATE_HZ, m_dSampleRateHz);
	trModule.GetAttribute(STR_START_REC_SN, m_nStartSampleNum);
	trModule.GetAttribute(STR_RECORD_MODE, m_strRecordMode);
		
	UpdateModuleInfo(trModule);
	
	m_arrChannelFiles.SetSize(0);
	m_arrChannelFiles.SetAsOwner(TRUE);
	int iChn = 0;
	foreach(TreeNode trCHN in trModule.Channels.Children)
	{
		ChannelFile* pChn = new ChannelFile;
		
		string strCHNFile;
		if ( !_check_make_CHN(lpcszDTS, iChn++, strCHNFile) )
			return FALSE;
		
		string strLabel;
		strLabel.Format(STR_CHN_LABEL_FMT, iChn - 1);
		LABEL_NODE(trCHN, strLabel);
		TreeNode trCHNFile = tree_check_get_node(trCHN, STR_CHN_FILENAME);
		
		strLabel.Format(STR_FILENAME_FMT, iChn - 1);
		LABEL_NODE(trCHNFile, strLabel);
		trCHNFile.strVal = strCHNFile;
	
		TreeNode trCHNInfo = tree_check_get_node(trCHN, STR_CHN_FILEINFO);
		LABEL_NODE(trCHNInfo, STR_CHN_FILE_INFO_L);

		pChn->Init(strCHNFile, trCHN);
		//string strCHNFile;
		//if ( !_check_make_CHN(lpcszDTS, iChn++, strCHNFile) )
			//return FALSE;
		//
		//string strLabel;
		//strLabel.Format(STR_CHN_LABEL_FMT, iChn - 1);
		//LABEL_NODE(trCHN, strLabel);
		//TreeNode trCHNFile = tree_check_get_node(trCHN, STR_CHN_FILENAME);
		//
		//strLabel.Format(STR_FILENAME_FMT, iChn - 1);
		//LABEL_NODE(trCHNFile, strLabel);
		//trCHNFile.strVal = strCHNFile;
		//
		//TreeNode trCHNInfo = tree_check_get_node(trCHN, STR_CHN_FILEINFO);
		//LABEL_NODE(trCHNInfo, STR_CHN_FILE_INFO_L);
		//
		//pChn->Open(strCHNFile);
		//pChn->GetFileInfo(trCHNInfo);
		//pChn->Close();
		m_arrChannelFiles.Add(*pChn);
	}
	
	m_trModule = trModule.Clone(TRUE);

	return TRUE;
}

void	Module::Close()
{
}

BOOL	Module::Import(Worksheet& wks, int iColStart/* = 0*/)
{
	if ( !wks || m_nChannels <= 0 )
		return FALSE;
	
	ASSERT(m_nChannels == m_arrChannelFiles.GetSize());
	TreeNode trChannels = m_trModule.Channels;
	
	int iChannel = 0;
	foreach(TreeNode trChannel in trChannels.Children)
	{
		ChannelFile& channel = m_arrChannelFiles.GetAt(iChannel);
		channel.Reopen();
		channel.Import(wks, iColStart);
		iColStart += 2;
		iChannel++;
	}
	int nModule;
	m_trModule.GetAttribute(STR_NUMBER, nModule);
	string strModule;
	strModule.Format(STR_MODULE_FMT, nModule);
	wks.SetName(strModule);
	
	return TRUE;
}

BOOL	Module::UpdateModuleInfo(TreeNode& trModule)
{
	if ( !trModule )
		return FALSE;
	TreeNode trModuleInfo = trModule.GetNode(STR_MODULE_INFO);
	if ( !trModuleInfo )
	{
		trModuleInfo = trModule.InsertNode(trModule.FirstNode, STR_MODULE_INFO);
		LABEL_NODE(trModuleInfo, STR_MODULE_ATTRIBS);
	}
	TreeNode trCHNs = tree_check_get_node(trModuleInfo, STR_NUM_CHANNELS);
	LABEL_NODE(trCHNs, STR_NUM_CHANNELS_L);
	trCHNs.nVal = m_nChannels;
	
	TreeNode trPreTriSecs = tree_check_get_node(trModuleInfo, STR_PRETRIGGER_SECS);
	LABEL_NODE(trPreTriSecs, STR_PRE_SECS_L);
	trPreTriSecs.nVal = m_nPreTriggerSecs;
	
	TreeNode trPostTriSecs = tree_check_get_node(trModuleInfo, STR_PSTTRIGGER_SECS);
	LABEL_NODE(trPostTriSecs, STR_POST_SECS_L);
	trPostTriSecs.nVal = m_nPostTriggerSecs;
	
	TreeNode trRateHz = tree_check_get_node(trModuleInfo, STR_SAMPLE_RATE_HZ);
	LABEL_NODE(trRateHz, STR_SAMPLE_RATE_L);
	trRateHz.dVal = m_dSampleRateHz;
	
	TreeNode trRecordMode = tree_check_get_node(trModuleInfo, STR_RECORD_MODE);
	LABEL_NODE(trRecordMode, STR_RECORD_MODE_L);
	trRecordMode.strVal = m_strRecordMode;
	
	return TRUE;
}
///-------------------------------------------Module Class End---------------------------------------------///

///-------------------------------------------DTS File Class Begin---------------------------------------------///


class DTSFile
{
public:
	DTSFile(LPCSTR lpcszDTS);
	~DTSFile();
	BOOL	Open(LPCSTR lpcszFile);
	void	Close();
	
	//Import data
	BOOL	Import(Worksheet& wks, int iColStart = 0);
	
	//get file information
	BOOL	GetFileInfo(TreeNode& trInfo);
	
protected:
	BOOL	Init(TreeNode& trDTS);
	
private:
	string			m_strFileName;		//current *.dts file name;
	Tree			m_trInfo;			//Test information
	Array<Module&>	m_arrModules;		//array of modules
	int				m_nModules;			//number of modules
};

DTSFile::DTSFile(LPCSTR lpcszDTS)
{
	Open(lpcszDTS);
}

DTSFile::~DTSFile()
{
	Close();
}

BOOL	DTSFile::Open(LPCSTR lpcszFile)
{
	string strFile(lpcszFile);
	if ( !_check_update_XML(strFile, strFile) )
		return FALSE;
	
	Tree trDTS;
	if ( !trDTS.Load(strFile) )
		return FALSE;
	
	m_strFileName = lpcszFile;
	
	return Init(trDTS);
}

void	DTSFile::Close()
{
	DeleteFile(STR_TEMP_XML_FILE);
	m_strFileName = "";
	return;
}

BOOL	DTSFile::Import(Worksheet& wks, int iColStart/* = 0*/)
{
	if ( !wks || m_strFileName.IsEmpty() ) //wks invalid or file not open
		return FALSE;
	
	ASSERT(m_nModules == m_arrModules.GetSize());
	TreeNode trModules = m_trInfo.Test.Modules;
	int iModule = 0;
	foreach(TreeNode trModule in trModules.Children)
	{
		Module& module = m_arrModules.GetAt(iModule);
		module.Import(wks, iColStart);
		int nChannels = trModule.GetNode(STR_MODULE_INFO).GetNode(STR_NUM_CHANNELS).nVal;
		iColStart += nChannels;
		iModule++;
	}
	wks.GetPage().SetName(GetFileName(m_strFileName, TRUE));
	return TRUE;
}

BOOL	DTSFile::Init(TreeNode& trDTS)
{
	//assume ther is one module, for multi-module, need to check how to use the information
	TreeNode trModules = trDTS.Test.Modules;
	m_nModules = trModules.Children.Count();
	
	m_arrModules.SetSize(0);
	m_arrModules.SetAsOwner(TRUE);

	foreach(TreeNode trModule in trModules.Children)
	{
		Module* pMod = new Module;
		pMod->Init(m_strFileName, trModule);
		m_arrModules.Add(*pMod);
	}
	
	m_trInfo = trDTS.Clone(TRUE);
	
	return TRUE;
}
	
BOOL	DTSFile::GetFileInfo(TreeNode& trInfo)
{
	if ( !trInfo )
		return FALSE;
	
	return trInfo.Replace(m_trInfo.FirstNode);
}



///-------------------------------------------DTS File Class End---------------------------------------------///

