/*******************************************************************************
 *	TmsControl.cpp	TMS API Command line Control application
 *			T.Barnaby,	BEAM Ltd,	2007-02-12
 *	updated by	D.Korchagin,	CERN AB-BI-SW,	2007-08-31
 *******************************************************************************
 *
 *	This application provides a command line interface to the TMS system.
 */
#include <iostream>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#ifndef __Lynx__
#include <getopt.h>
#else
struct option
{
#if defined (__STDC__) && __STDC__
	const char *name;
#else
	char *name;
#endif
	int has_arg;
	int *flag;
	int val;
};
#endif
#include <TmsD.h>
#include <TmsC.h>
#include <TmsS.h>
#include <TmsLib.h>
#include <TmsCycleParam.h>
#include <BEntry.h>
#include <BFile.h>
#include <sys/time.h>

using namespace Tms;
using namespace std;

const UInt32	tmsStateNum = 16;
const UInt32	tmsPickupNum = 40;
const UInt32	tmsMaxNumPickups = 100;

int		kst = 0;



double getTime()
{
	struct timeval	tp;
	
	gettimeofday(&tp, NULL);
	return ((double) tp.tv_sec + (double) tp.tv_usec * 1e-6);
}

class TmsEventServer : public Tms::TmsEventService {
public:
		TmsEventServer(BoapServer& server, BString name) : Tms::TmsEventService(server, name){}
	BError errorEvent(UInt32 cycleNumber, BError error){
		BError	err;
		
		printf("Event: Error: Cycle: %d Error: %d:%s\n", cycleNumber, error.getErrorNo(), error.getString().retStr());

		return err;
	}
	
	BError cycleStartEvent(UInt32 cycleNumber){
		BError	err;
		
		printf("Event: CycleStartEvent: %d\n", cycleNumber);

		return err;
	}
	
	BError cycleStopEvent(UInt32 cycleNumber){
		BError	err;
		
		printf("Event: CycleStopEvent: %d\n",cycleNumber );

		return err;
	}
	
	BError dataEvent(DataInfo dataInfo){
		BError	err;
		
		printf("Event: DataEvent: %d\n", dataInfo.numValues);

		return err;
	}
};


// Initialise and test the TMS system
BError tmsInit(TmsControl& tmsControl){
	BError			err;
	ConfigInfo		configInfo;
	BList<BError>		errorList;
	BList<NameValue>	nvList;
	BString			version;
	
	// Get Version
	if(err = tmsControl.getVersion(version)){
		return err.set(1, BString("Error: getting version: ") + err.getString());
	}
	cout << "Version: " << version << "\n";
	
	// Initialise TMS system
	if(err = tmsControl.init()){
		return err.set(1, BString("Error: initialising TMS: ") + err.getString());
	}

#ifdef ZAP	
	UInt32			c;
	UInt32			m;
	UInt32			pn;
	UInt32			pc;

	// Configure TMS system
	configInfo.puReferences.resize(0);
	c = 1;
	for(m = 1; c <= tmsPickupNum; m++){
		for(pn = 1; (c <= tmsPickupNum) && (pn < 6); pn++){
			for(pc = 1; (c <= tmsPickupNum) && (pc < 4); pc++){
				printf("Set: %d: %d,%d,%d\n", c, m, pn, pc);
				configInfo.puReferences.resize(c);
				configInfo.puReferences[c - 1] = PuChannel(m, pn, pc);
				c++;
			}
		}
	}
	
	if(err = tmsControl.configure(configInfo)){
		return err.set(1, BString("Error: configuring TMS: ") + err.getString());
	}
#endif

	return err;
}

BError tmsConfigure(TmsControl& tmsControl, BString fileName){
	BError		err;
	ConfigInfo	configInfo;
	BEntryFile	file;
	unsigned int	n;
	PuChannel	chan;
	BString		s;

	if(file.open(fileName)){
		return err.set(1, BString("Unable to open file: ") + fileName);
	}
	if(file.read()){
		return err.set(1, BString("Unable to read file: ") + fileName);
	}
	
	configInfo.puReferences.resize(0);
	for(n = 1; n <= tmsMaxNumPickups; n++){
		s = file.findValue(BString("PickUp") + n + ":");
		if(s != ""){
			configInfo.puReferences.resize(n + 1);
			if(sscanf(s, "%hhd,%hhd,%hhd", &chan.moduleNum, &chan.pupeNum, &chan.pupeChan) != 3){
				err.set(ErrorConfig, "Pick-Up channel error in configuration file");
				return err;
			}
			configInfo.puReferences[n - 1] = chan;
		}
		else {
			break;
		}
	}

	if(err = tmsControl.configure(configInfo)){
		return err.set(1, BString("Error: configuring TMS: ") + err.getString());
	}

	return err;
}

BError tmsGetConfiguration(TmsControl& tmsControl){
	BError		err;
	ConfigInfo	configInfo;
	unsigned int	n;

	if(err = tmsControl.getConfiguration(configInfo)){
		return err.set(1, BString("Error: getting configuration TMS: ") + err.getString());
	}

	for(n = 0; n < configInfo.puReferences.size(); n++){
		printf("%d\t%d,%d,%d\n", n + 1, configInfo.puReferences[n].moduleNum, configInfo.puReferences[n].pupeNum, configInfo.puReferences[n].pupeChan);
	}

	return err;
}

BError tmsSetControl(TmsControl& tmsControl, BString fileName){
	BError		err;
	CycleParamEdit	params;
	
	if(err = params.readFromFile(fileName))
		return err;

	if(err = tmsControl.setControlInfo(params)){
		return err.set(1, BString("Error: Setting Control Info: ") + err.getString());
	}

	return err;
}

BError tmsDelControl(TmsControl& tmsControl, BString delControl){
	BError		err;
	char		cycleType[1024];
	UInt32		ring;
	UInt32		channel;

	if(sscanf(delControl, "%[^,],%u,%u", cycleType, &ring, &channel) != 3){
		err.set(ErrorConfig, "Error in tmsDelControl spec");
		return err;
	}

	if(err = tmsControl.delControlInfo(cycleType, ring, channel)){
		return err.set(1, BString("Error: Deleting Control Info: ") + err.getString());
	}

	return err;
}

BError tmsSetTestData(TmsControl& tmsControl, BString testData){
	BError		err;
	BArray<UInt32>	data;
	BFile		file;
	UInt32		channel;
	PuChannel	puChannel;
	char		fileName[1024] = "\0";
	int		n;
	int		nb;
	
	n = sscanf(testData, "%u,%s", &channel, fileName);
	
	if((n < 1) || (n > 2)){
		err.set(ErrorConfig, "Error in setTestData spec");
		return err;
	}
	
	// get Physical Channel number
	if(err = tmsControl.getPuChannel(channel, puChannel)){
		return err.set(1, BString("Error: Getting Physical Channel Number: ") + err.getString());
	}
	if(n == 2){
		if(err = file.open(fileName, "r")){
			return err.set(1, BString("Error: Opening file: ") + fileName);
		}
		data.resize(1024);
		
		if((nb = file.read(&data[0], 1024 * sizeof(UInt32))) <= 0){
			return err.set(1, BString("Error: Reading 1024 32bit data items from file: ") + fileName);
		}
		data.resize(nb / sizeof(UInt32));

		if(err = tmsControl.setTestData(puChannel, 1, data)){
			return err.set(1, err.getString());
		}
	}
	else {
		if(err = tmsControl.setTestData(puChannel, 0, data)){
			return err.set(1, err.getString());
		}
	}
	
	return err;
}

BError tmsSetPupeConfig(TmsControl& tmsControl, BString pupeConfigString){
	BError		err;
	UInt32		channel;
	PuChannel	puChannel;
	PupeConfig	pupeConfig;
	UInt32		n;

	n = sscanf(pupeConfigString, "%u,%u,%x,%u", &channel, &pupeConfig.adcSysclkSync, &pupeConfig.internalTimingMask, &pupeConfig.disableBlr);
	
	if(n != 4){
		err.set(ErrorConfig, "Error in pupeConfigString spec");
		return err;
	}
	
	// get Physical Channel number
	if(err = tmsControl.getPuChannel(channel, puChannel)){
		return err.set(1, BString("Error: Getting Physical Channel Number: ") + err.getString());
	}

	if(err = tmsControl.setPupeConfig(puChannel, pupeConfig)){
		return err.set(1, err.getString());
	}
	
	return err;
}

Int32	getBitValue(UInt64 value, int startBit, int nBits, int s){
	Int64	v;
	UInt64	bm = (1ULL << nBits) - 1;
	UInt64	sm = (1ULL << (nBits - 1));
	
	v = (value >> startBit) & bm;
	
	if(s){
		v = -(v & sm) | v;
	}
	
	return v;
}

#define	BIT(v,b)	((v >> b) & 1)

BError tmsCaptureDiagnostics(TmsControl& tmsControl, TmsProcess& tmsProcess, BString captureSpec, BString outFile){
	BError		err;
	TestCaptureInfo	captureInfo;
	BArray<UInt64>	data;
	UInt32		cn = 0;
	BString		ct;
	BFile		file;
	UInt32		i;
	UInt32		channel;
	PuChannel	puChannel;
	
	if(sscanf(captureSpec, "%u,%u,%u, %u,%u,%u,%u,%u,%u", &channel, &captureInfo.source, &captureInfo.clock,
		 &captureInfo.startTime, &captureInfo.postTriggerDelay, &captureInfo.triggerAnd, &captureInfo.triggerStore,
		 &captureInfo.triggerSourceData, &captureInfo.triggerMask) != 9){
		err.set(ErrorConfig, "Error in CapSpec");
		return err;
	}
	
	if(outFile == "")
		outFile = "data.txt";
	
	if(err = file.open(outFile, "w")){
		return err.set(1, BString("Error: Opening file: ") + outFile);
	}
	
	// get Physical Channel number
	if(err = tmsControl.getPuChannel(channel, puChannel)){
		return err.set(1, BString("Error: Getting Physical Channel Number: ") + err.getString());
	}

	// Get next Cycle Number
	
	if(err = tmsProcess.getCycleInfo(cn, ct)){
		return err.set(1, BString("Error: Getting Cycle Number: ") + err.getString());
	}
	
	printf("CaptureTestData: Channel: %u:%u:%u CycleNumber: %u CycleType: %s\n", puChannel.moduleNum, puChannel.pupeNum, puChannel.pupeChan, cn, ct.retStr());
	if(err = tmsControl.captureDiagnostics(puChannel, captureInfo, data)){
		return err.set(1, BString("Error: Getting Data: ") + err.getString());
	}


	// Assumes source == 0
	if(captureInfo.source == 0){
		if (kst) {
			if(captureInfo.triggerStore){
				file.printf("FrefIn PllMsb PhaseTableMsb Lo1 Sigma Lo2 Gate Blr Mean1 Mean2 RfSelect1 RfSelect2 SelFilter SwitchState DdsFreq ");
				file.printf("FRef HChange Injection CalStop CalStart CycleStop CycleStart SysClock\n");
			}
			else {
				file.printf("FrefIn PllMsb PhaseTableMsb Lo1 Sigma Lo2 Gate Blr Mean1 Mean2 RfSelect1 RfSelect2 SelFilter SwitchState DdsFreq\n");
			}
		}
		for(i = 0; i < data.size(); i++){
			if(captureInfo.triggerStore){
				file.printf("%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
					getBitValue(data[i], 63, 1, 0),		// FREF
					!getBitValue(data[i], 62, 1, 0),	// PllMsb (FRef Reference)
					!getBitValue(data[i], 61, 1, 0),	// PhaseTableMsb (Local FREF)
					getBitValue(data[i], 60, 1, 0),		// LO1
//					getBitValue(data[i], 57, 3, 1),		// Sigma (3 bits)
					getBitValue(data[i], 32, 12, 1),	// Sigma (12 bits)
					getBitValue(data[i], 56, 1, 0),		// LO2

					getBitValue(data[i], 49, 1, 0),		// Gate
					getBitValue(data[i], 48, 1, 0),		// Blr
					getBitValue(data[i], 50, 1, 0),		// Mean1
					getBitValue(data[i], 51, 1, 0),		// Mean2

					getBitValue(data[i], 52, 1, 0),		// RfSelect1
					getBitValue(data[i], 53, 1, 0),		// RfSelect2
					getBitValue(data[i], 54, 1, 0),		// SelFilter

					getBitValue(data[i], 44, 4, 0),		// SwitchState

					getBitValue(data[i], 8, 24, 1),		// DDS_FREQ
					getBitValue(data[i], 7, 1, 0),		// FREF
					getBitValue(data[i], 6, 1, 0),		// HCHANGE event
					getBitValue(data[i], 5, 1, 0),		// INJECTION event
					getBitValue(data[i], 4, 1, 0),		// CAL_STOP event
					getBitValue(data[i], 3, 1, 0),		// CAL_START event
					getBitValue(data[i], 2, 1, 0),		// CYCLE_STOP event
					getBitValue(data[i], 1, 1, 0),		// CYCLE_START event
					getBitValue(data[i], 0, 1, 0)		// 10MHz System Clock
					);
			}
			else {
				file.printf("%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
					getBitValue(data[i], 63, 1, 0),		// FREF
					!getBitValue(data[i], 62, 1, 0),	// PllMsb (FRef Reference)
					!getBitValue(data[i], 61, 1, 0),		// PhaseTableMsb (Local FREF)
					getBitValue(data[i], 60, 1, 0),		// LO1
//					getBitValue(data[i], 57, 3, 1),		// Sigma (3 bits)
					getBitValue(data[i], 32, 12, 1),	// Sigma (12 bits)
					getBitValue(data[i], 56, 1, 0),		// LO2

					getBitValue(data[i], 49, 1, 0),		// Gate
					getBitValue(data[i], 48, 1, 0),		// Blr
					getBitValue(data[i], 50, 1, 0),		// Mean1
					getBitValue(data[i], 51, 1, 0),		// Mean2

					getBitValue(data[i], 52, 1, 0),		// RfSelect1
					getBitValue(data[i], 53, 1, 0),		// RfSelect2
					getBitValue(data[i], 54, 1, 0),		// SelFilter

					getBitValue(data[i], 44, 4, 0),		// SwitchState

					getBitValue(data[i], 0, 32, 1)		// DDS_FREQ
					);
			}
		}
	}
	else if(captureInfo.source == 1){
		if (kst) {
			file.printf("FrefIn PllMsb PhaseTableMsb Lo1 Sigma Lo2 MultOut1 MultOut2 F_Error\n");
		}
		for(i = 0; i < data.size(); i++){
			Int32	timing = 0;

			if(captureInfo.triggerStore){
				timing = getBitValue(data[i], 0, 8, 0);
			}

			file.printf("%d %d %d %d %d %d %d %d %d\n",
				getBitValue(data[i], 63, 1, 0),		// FREF
				!getBitValue(data[i], 62, 1, 0),	// PllMsb (FRef Reference)
				!getBitValue(data[i], 61, 1, 0),	// PhaseTableMsb (Local FREF)
				getBitValue(data[i], 60, 1, 0),		// LO1
				getBitValue(data[i], 57, 3, 1),		// Sigma (3 bits)
				getBitValue(data[i], 56, 1, 0),		// LO2

				getBitValue(data[i], 38, 14, 1),	// mult_out1
				getBitValue(data[i], 24, 14, 1),	// mult_out2
				getBitValue(data[i], 0, 24, 1)		// f_error
				);
		}
	}
	else if(captureInfo.source == 2){
		if (kst) {
			file.printf("FrefIn PllMsb PhaseTableMsb Lo1 Sigma Lo2 Gate B0 Result0\n");
		}
		for(i = 0; i < data.size(); i++){
			Int32	timing = 0;

			if(captureInfo.triggerStore){
				timing = getBitValue(data[i], 0, 8, 0);
			}

			file.printf("%d %d %d %d %d %d %d %d %d\n",
				getBitValue(data[i], 63, 1, 0),		// FREF
				!getBitValue(data[i], 62, 1, 0),	// PllMsb (FRef Reference)
				!getBitValue(data[i], 61, 1, 0),	// PhaseTableMsb (Local FREF)
				getBitValue(data[i], 60, 1, 0),		// LO1
				getBitValue(data[i], 57, 3, 1),		// Sigma (3 bits)
				getBitValue(data[i], 56, 1, 0),		// LO2

				getBitValue(data[i], 55, 1, 0),		// Gate
				getBitValue(data[i], 32, 23, 1),	// B0
				getBitValue(data[i], 0, 32, 1)		// Result0
				);
		}
	}
	else if(captureInfo.source == 3){
		if (kst) {
			file.printf("Sigma Gate Blr X0 E0 Y0\n");
		}
		for(i = 0; i < data.size(); i++){
			Int32	timing = 0;

			if(captureInfo.triggerStore){
				timing = getBitValue(data[i], 0, 8, 0);
			}

			file.printf("%d %d %d %d %d %d %d\n",
				getBitValue(data[i], 48, 14, 1),	// Sigma
				getBitValue(data[i], 62, 1, 0),		// Gate
				getBitValue(data[i], 63, 1, 0),		// Blr
				getBitValue(data[i], 0, 16, 1),		// X0
				getBitValue(data[i], 16, 16, 1),	// E0
				getBitValue(data[i], 32, 16, 1)		// Y0
				);
		}
	}
	else {
		err.set(1, BString("Source: ") + captureInfo.source + " not supported");
	}
	
	file.close();

	return err;
}

BError tmsGetData(TmsProcess& tmsProcess, BString readDataSpec, BString outFile){
	BError		err;
	DataInfo	dataInfo;
	Data		data;
	UInt32		cn = 0;
	BString		ct;
	BFile		file;
	UInt32		i;
	UInt32		t = 0;

	if(sscanf(readDataSpec, "%u,%u,%u,%u,%u,%u,%u", &dataInfo.channel, &dataInfo.cyclePeriod, &dataInfo.startTime, &dataInfo.orbitNumber, &dataInfo.bunchNumber, &dataInfo.numValues, &dataInfo.function) != 7){
		err.set(ErrorConfig, "Error in DataSpec");
		return err;
	}
	
	if(outFile == "")
		outFile = "data.txt";
	
	if(err = file.open(outFile, "w")){
		return err.set(1, BString("Error: Opening file: ") + outFile);
	}

	// Set data require and wait for data
	if(err = tmsProcess.getCycleInfo(cn, ct)){
		return err.set(1, BString("Error: Getting Cycle Number: ") + err.getString());
	}
	
	dataInfo.cycleNumber	= cn;
	dataInfo.argument	= 0;
	dataInfo.beyondPeriod = 0;

	printf("GetData: CycleNumber: %u CycleType: %s\n", cn, ct.retStr());
	if(err = tmsProcess.getData(dataInfo, data)){
		return err.set(1, BString("Error: Getting Data: ") + err.getString());
	}
	
	if(data.dataValues.size()){
		t = data.dataValues[0].time;
	}
	printf("GotData: Num: %d NumBunches: %d NumChannels: %d FirstTime: %dms\n",  data.numValues, data.numBunches, data.numChannels, t);
	
	if (kst) {
		file.printf("sigma deltaX deltaY\n");
	}

	for(i = 0; i < data.numValues; i++){
		file.printf("%d %d %d\n", data.dataValues[i].sigma, data.dataValues[i].deltaX, data.dataValues[i].deltaY);
	}
	
	file.close();

	return err;
}

BError tmsPerf(TmsProcess& tmsProcess, int channel, int numSamples){
	BError			err;
	UInt32			i;
	UInt32			num = 10;
	DataInfo		dataInfo;
	Data			data;
	double			t1, t2, r;
	BString			ct;

	// Set data require and wait for data
	dataInfo.cycleNumber	= 0;
	dataInfo.channel	= channel;
	dataInfo.cyclePeriod	= CyclePeriodEvent0;
	dataInfo.startTime	= 0;
	dataInfo.orbitNumber	= 0;
	dataInfo.bunchNumber	= 0;
	dataInfo.function	= 0;
	dataInfo.argument	= 0;
	dataInfo.numValues	= numSamples;

#ifdef ZAP
	tmsProcess.getCycleNumber(dataInfo.cycleNumber);
	dataInfo.cycleNumber--;

	t1 = getTime();
	for(i = 0; i < num; i++){
		if(err = tmsProcess.getData(dataInfo, data)){
			return err.set(1, BString("Error: Getting Data: ") + err.getString());
		}
	}	
	t2 = getTime();

	r = (double(numSamples) * num * sizeof(DataValue)) / (t2 - t1);
	printf("DataSize: %d DataRate: %f MBytes/sec\n", numSamples, r / (1024 * 1024));
#else
	while(1){
		tmsProcess.getCycleInfo(dataInfo.cycleNumber, ct);
		dataInfo.cycleNumber--;

		t1 = getTime();
		for(i = 0; i < num; i++){
			while(err = tmsProcess.getData(dataInfo, data)){
				if(err.getErrorNo() == -EPIPE){
					printf("Try to reconnect\n");
					tmsProcess.disconnectService();
					while(err = tmsProcess.connectService(tmsProcess.getServiceName())){
						printf("Still Trying to reconnect\n");
						sleep(1);
					}
				}
				else {
					return err;
				}
			}
		}	
		t2 = getTime();

		r = (double(numSamples) * num * sizeof(DataValue)) / (t2 - t1);
		printf("DataSize: %d CallTime: %f s DataRate: %f MBytes/sec\n", numSamples, (t2 - t1) / num, r / (1024 * 1024));
	}
#endif
	
	
	return err;
}

BError tmsPerfPu(TmsControl& tmsControl, TmsProcess& tmsProcess, BString hostName, int channel, int numSamples){
	BError			err;
	UInt32			i;
	UInt32			num = 10;
	DataInfo		dataInfo;
	Data			data;
	double			t1, t2, r;
	PuChannel		chan;
	PuProcess		puProcess;
	BString			ct;
	BUInt32			orbitNumber;

	tmsControl.getPuChannel(channel, chan);
	if(err = puProcess.connectService(BString("//") + hostName + "/puProcess-" + chan.moduleNum))
		return err;
	
	// Set data require and wait for data
	dataInfo.cycleNumber	= 0;
	dataInfo.channel	= 1;
	dataInfo.cyclePeriod	= CyclePeriodEvent0;
	dataInfo.startTime	= 0;
	dataInfo.orbitNumber	= 0;
	dataInfo.bunchNumber	= 0;
	dataInfo.function	= 0;
	dataInfo.argument	= 0;
	dataInfo.numValues	= numSamples;
	
	while(1){
		tmsProcess.getCycleInfo(dataInfo.cycleNumber, ct);
		dataInfo.cycleNumber--;

		t1 = getTime();
		for(i = 0; i < num; i++){
			if(err = puProcess.getData(chan, dataInfo, data, orbitNumber)){
				return err.set(1, BString("Error: Getting Data: ") + err.getString());
			}
		}	
		t2 = getTime();

		r = (double(numSamples) * num * sizeof(DataValue)) / (t2 - t1);
		printf("DataSize: %d DataRate: %f MBytes/sec\n", numSamples, r / (1024 * 1024));
	}
	
	return err;
}

BError tmsEventsProcess(TmsProcess& tmsProcess, BString host){
	BError			err;
	static BoapServer	boapServer;
	static TmsEventServer	tmsEventServer(boapServer, "");

	if(err = boapServer.init(host, BoapServer::THREADED)){
		return err;
	}

	boapServer.run(1);

	if(err = tmsProcess.addEventServer(tmsEventServer.name())){
		return err;
	}

	pause();

	return err;
}


void usage(void) {
	cerr << "Usage:\ttmsControl [options] hostname\n";
	cerr << " --help             - Help on command line parameters\n";
	cerr << " --debug 0x1122     - Set Debug mask\n";
	cerr << " --version          - Display Version numbers\n";
	cerr << " --ring <n>         - The ring number to access\n";
	cerr << " --events           - Show all events\n";
	cerr << " --outFile <fileName> - Send output to given file. Default to data.txt\n";
	cerr << " --kst              - Format output file for kst plotting\n";
	cerr << " --init             - Initialise TMS system\n";
	cerr << " --test             - Test TMS system\n";
	cerr << " --status           - Get Status of TMS system\n";
	cerr << " --statistics       - Get Statistics from TMS system\n";
	cerr << " --configure <file> - Configure taking channel information from the given file\n";
	cerr << " --getConfiguration - Gets and displays the current configuration\n";
	cerr << " --setControl <file>- Adds or modifies an entry in the TMS State/Phase table database from the given file\n";
	cerr << " --delControl <Spec>- Deletes an entry from the TMS State/Phase table database\n";
	cerr << "                      Spec:   cycleType,Ring,Channel\n";
	cerr << " --cycleType <type> - Sets the CycleType for the next cycle\n";
	cerr << "\n";
	cerr << " --getCycleInfo     - Read information on current cycle\n";
	cerr << " --getCycleTypeInfo - Read information on a particular cycle type\n";
	cerr << " --getData <DataSpec> - Read data from the system using the DataSpec given\n";
	cerr << "                      DataSpec: <chan,period,startTime,orbit,bunch,numSamples,function>\n";
	cerr << "\n";
	cerr << " --setSimulation <timing,data,setNextCycle> - Sets the internal simulation modes. Boolean values.\n";
	cerr << " --getSimulation    - Gets the internal simulation modes\n";
	cerr << "\n";
	
	cerr << " --perfPu           - Perform a getData performance test direct to the first PU\n";
	cerr << " --perf             - Perform a getData performance test\n";
	cerr << " --channel <n>      - Channel to use\n";
	cerr << " --numSamples <n>   - Number of 8byte samples to read\n";
	cerr << "\n";
	cerr << " --captureDiagnostics <CapSpec> - Capture Diagnostics data\n";
	cerr << "                      CapSpec: <chan,source,clock,startTime,postTriggerDelay,trigAnd,trigStore,trigData,trigMask>\n";
	cerr << " --setTestData <chan,filename> - Set the test input data for the given channel\n";
	cerr << " --setPupeConfig <chan,adcClkSync,timingMask,disableBlr> - Set the PUPE test configuration for the given channel\n";
	cerr << "                      iAdcClkRef: Uses the internal free running ADC 125MHz clock rather than one synchronised to the external 10MHz Clock\n",
	cerr << "                      iTimingMask: Hex bitmask. Uses internal hardware/software timing rather than the external timing signals for the given timing signals (eg CycleStop 0x04)\n",

	cerr << "\n";
}

static struct option options[] = {
		{ "?",			0, NULL, 0 },
		{ "h",			0, NULL, 0 },
		{ "help",		0, NULL, 0 },
		{ "debug",		1, NULL, 0 },
		{ "ring",		1, NULL, 0 },
		{ "channel",		1, NULL, 0 },
		{ "numSamples",		1, NULL, 0 },
		{ "events",		0, NULL, 0 },
		{ "outFile",		1, NULL, 0 },
		{ "kst",		0, NULL, 0 },

		{ "version",		0, NULL, 0 },
		{ "init",		0, NULL, 0 },
		{ "test",		0, NULL, 0 },
		{ "status",		0, NULL, 0 },
		{ "statistics",		0, NULL, 0 },
		{ "configure",		1, NULL, 0 },
		{ "getConfiguration",	0, NULL, 0 },
		{ "setControl",		1, NULL, 0 },
		{ "delControl",		1, NULL, 0 },

		{ "getCycleInfo",	0, NULL, 0 },
		{ "getCycleTypeInfo",	1, NULL, 0 },
		{ "getData",		1, NULL, 0 },

		{ "setSimulation",	1, NULL, 0 },
		{ "getSimulation",	0, NULL, 0 },

		{ "perf",		0, NULL, 0 },
		{ "perfPu",		0, NULL, 0 },

		{ "captureDiagnostics",	1, NULL, 0 },
		{ "cycleType",		1, NULL, 0 },
		{ "setTestData",	1, NULL, 0 },
		{ "setPupeConfig",	1, NULL, 0 },

		{ 0,0,0,0 }
};

int main(int argc, char** argv){
	BError			err;
	TmsControl		tmsControl("tmsControl");
	TmsProcess		tmsProcess("tmsProcess");
	int			c;
	int			optIndex = 0;
	BString			s;
	int			debug = 0;
	int			ring = 1;
	int			channel = 1;
	int			version = 0;
	int			test = 0;
	int			status = 0;
	int			statistics = 0;
	int			perf = 0;
	int			perfPu = 0;
	int			init = 0;
	int			events = 0;
	BString			cycleType;
	int			getCycleInfo = 0;
	BString			readDataSpec;
	BString			captureDiagnostics;
	BString			configFileName;
	BString			setControlFileName;
	BString			delControl;
	BString			hostName;
	BIter			i;
	BString			setTestData;
	int			getConfiguration = 0;
	BString			setPupeConfig;
	BString			outFile;
	int			numSamples = 1024 * 1024;
	BString			cycleTypeInfo;
	BString			setSimulation;
	int			getSimulation = 0;

#ifndef __Lynx__
	while((c = getopt_long_only(argc, argv, "", options, &optIndex)) == 0){
#else
	char *optarg = NULL;
	int optind = argc;
	c = 0;
	cerr << "\n!!!WARNING:\ttmsControl is only compatible with Linux OS for the moment\n\n";
	while(false){
#endif
		s = options[optIndex].name;
		if(s == "help" || s == "h" || s == "?"){
			usage();
			return 1;
		}
		else if(s == "debug"){
			debug = strtol(optarg, 0, 0);
		}
		else if(s == "ring"){
			ring = strtol(optarg, 0, 0);
		}
		else if(s == "channel"){
			channel = strtol(optarg, 0, 0);
		}
		else if(s == "numSamples"){
			numSamples = strtol(optarg, 0, 0);
		}
		else if(s == "outFile"){
			outFile = optarg;
		}
		else if(s == "init"){
			init = 1;
		}
		else if(s == "version"){
			version = 1;
		}
		else if(s == "kst"){
			kst = 1;
		}
		else if(s == "test"){
			test = 1;
		}
		else if(s == "status"){
			status = 1;
		}
		else if(s == "statistics"){
			statistics = 1;
		}
		else if(s == "configure"){
			configFileName = optarg;
		}
		else if(s == "getConfiguration"){
			getConfiguration = 1;
		}
		else if(s == "cycleType"){
			cycleType = optarg;
		}
		else if(s == "setControl"){
			setControlFileName = optarg;
		}
		else if(s == "delControl"){
			delControl = optarg;
		}
		else if(s == "getData"){
			readDataSpec = optarg;
		}
		else if(s == "getCycleInfo"){
			getCycleInfo = 1;
		}
		else if(s == "getCycleTypeInfo"){
			cycleTypeInfo = optarg;
		}
		else if(s == "events"){
			events = 1;
		}
		else if(s == "perf"){
			perf = 1;
		}
		else if(s == "perfPu"){
			perfPu = 1;
		}
		else if(s == "captureDiagnostics"){
			captureDiagnostics = optarg;
		}
		else if(s == "setTestData"){
			setTestData = optarg;
		}
		else if(s == "setPupeConfig"){
			setPupeConfig = optarg;
		}
		else if(s == "setSimulation"){
			setSimulation = optarg;
		}
		else if(s == "getSimulation"){
			getSimulation = 1;
		}
	}
	
	if(optind == argc){
		usage();
		return 1;
	}

	hostName = argv[optind++];

//	tmsProcess.setPriority(6);
	
	// Connect to TMS control objects
	if(err = tmsControl.connectService(BString("//") + hostName + "/tmsControl" + ring)){
		cerr << "Error: cannot connect to tmsControl: " << err.getString() << "\n";
		return 1;
	}
	if(err = tmsProcess.connectService(BString("//") + hostName + "/tmsProcess" + ring)){
		cerr << "Error: cannot connect to tmsProcess: " << err.getString() << "\n";
		return 1;
	}
	
	if(events){
		if(err = tmsEventsProcess(tmsProcess, hostName)){
			cerr << err.getString() + "\n";
			return 1;	
		}
		
	}
	
	if(version){	
		// Get Version
		if(err = tmsControl.getVersion(s)){
			cerr << "Error: geting TmsControl Version: " << err.getString() << "\n";
			return 1;
		}
		cout << "TmsControl Version: " << s << "\n";

		if(err = tmsProcess.getVersion(s)){
			cerr << "Error: geting TmsProcess Version: " << err.getString() << "\n";
			return 1;
		}
		cout << "TmsProcess Version: " << s << "\n";
	}

	if(init){
		// Initialise TMS system
		if(err = tmsInit(tmsControl)){
			cerr << "Error: initialising TMS: " << err.getString() + "\n";
			return 1;
		}
	}
	
	if(test){
		BList<BError>	 errorList;
		
		if(err = tmsControl.test(errorList)){
			cerr << "Error: performing test: " << err.getString() + "\n";
			return 1;
		}
		for(errorList.start(i); !errorList.isEnd(i); errorList.next(i)){
			cout << errorList[i].getErrorNo() << ": " << errorList[i].getString() << "\n";
		}
	}

	if(status){
		BList<NameValue>	statusList;
		
		if(err = tmsControl.getStatus(statusList)){
			cerr << "Error: getting status: " << err.getString() + "\n";
			return 1;
		}
		for(statusList.start(i); !statusList.isEnd(i); statusList.next(i)){
			cout << statusList[i].name << ": " << statusList[i].value << "\n";
		}
	}

	if(statistics){
		BList<NameValue>	statusList;
		
		if(err = tmsControl.getStatistics(statusList)){
			cerr << "Error: getting statistics: " << err.getString() + "\n";
			return 1;
		}
		for(statusList.start(i); !statusList.isEnd(i); statusList.next(i)){
			cout << statusList[i].name << ": " << statusList[i].value << "\n";
		}
	}


	if(configFileName != ""){
		// Configure TMS system
		if(err = tmsConfigure(tmsControl, configFileName)){
			cerr << err.getString() + "\n";
			return 1;	
		}
	}

	if(getConfiguration){
		// Get Configuration
		if(err = tmsGetConfiguration(tmsControl)){
			cerr << err.getString() + "\n";
			return 1;	
		}
	}

	if(setControlFileName != ""){
		// Set FPGA parameters
		if(err = tmsSetControl(tmsControl, setControlFileName)){
			cerr << err.getString() + "\n";
			return 1;	
		}
	}
	
	if(delControl != ""){
		// Delete FPGA parameters
		if(err = tmsDelControl(tmsControl, delControl)){
			cerr << err.getString() + "\n";
			return 1;	
		}
	}
	
	if(setTestData != ""){
		if(err = tmsSetTestData(tmsControl, setTestData)){
			err.set(1, BString("Error: Setting test data: ") + err.getString());
			cerr << err.getString() + "\n";
			return 1;
		}
	}

	if(setPupeConfig != ""){
		if(err = tmsSetPupeConfig(tmsControl, setPupeConfig)){
			err.set(1, BString("Error: Setting Pupe Config: ") + err.getString());
			cerr << err.getString() + "\n";
			return 1;
		}
	}

	if(setSimulation != ""){
		Simulation	simulation;

		if(sscanf(setSimulation, "%d,%d,%d", &simulation.timing, &simulation.data, &simulation.setNextCycle) != 3){
			err.set(ErrorConfig, "Simulation settings incorrect");
			cerr << err.getString() + "\n";
			return err;
		}

		if(err = tmsControl.setSimulation(simulation)){
			err.set(1, BString("Error: Setting Simulation: ") + err.getString());
			cerr << err.getString() + "\n";
			return 1;
		}
	}

	if(getSimulation){
		Simulation	simulation;

		if(err = tmsControl.getSimulation(simulation)){
			err.set(1, BString("Error: Getting Simulation state: ") + err.getString());
			cerr << err.getString() + "\n";
			return 1;
		}
		printf("Timing: %d, Data: %d, SetNextCycle: %d\n", simulation.timing, simulation.data, simulation.setNextCycle);
	}

	if(cycleTypeInfo != ""){
		CycleTypeInformation	info;
		BIter			i;

		if(err = tmsProcess.getCycleTypeInformation(cycleTypeInfo, info)){
			err.set(1, BString("Error: Getting Cycle Type Information: ") + err.getString());
			cerr << err.getString() + "\n";
			return 1;
		}
		
		printf("CycleType:	%s\n", info.cycleType.retStr());
		printf("CycleInfo:	%s\n", info.info.retStr());
		
		for(info.periods.start(i); !info.periods.isEnd(i); info.periods.next(i)){
			printf("CyclePeriod: %u Harmonic: %u NumBunches: %u BunchMask: 0x%x\n",
				info.periods[i].cyclePeriod, info.periods[i].harmonic, info.periods[i].numBunches, info.periods[i].bunchMask);
		}
	}
	
	if(getCycleInfo){
		UInt32			cn;
		BString			ct;
		CycleInformation	info;
		BIter			i;
		
		// Get current cycle info
		if(err = tmsProcess.getCycleInfo(cn, ct)){
			err.set(1, BString("Error: Getting Cycle Number: ") + err.getString());
			cerr << err.getString() + "\n";
			return 1;
		}
		
		printf("CycleNumber: %u\n", cn);
		printf("CycleType: %s\n", ct.retStr());

		if(err = tmsProcess.getCycleInformation(cn, info)){
			err.set(1, BString("Error: Getting Cycle Information: ") + err.getString());
			cerr << err.getString() + "\n";
			return 1;
		}
		
		printf("CycleNumber:	%u\n", info.cycleNumber);
		printf("CycleType:	%s\n", info.cycleType.retStr());
		for(info.periods.start(i); !info.periods.isEnd(i); info.periods.next(i)){
			printf("CyclePeriod: %u StartTime: %u EndTime: %u Harmonic: %u BunchMask: 0x%x NumBunches: %u NumValues: %u\n",
				info.periods[i].cyclePeriod, info.periods[i].startTime, info.periods[i].endTime,
				info.periods[i].harmonic, info.periods[i].bunchMask,
				info.periods[i].numBunches, info.periods[i].numValues);
		}
	}

	if(cycleType != ""){
		UInt32	cn;
		BString	ct;
		
		// Get current cycle info
		if(err = tmsProcess.getCycleInfo(cn, ct)){
			err.set(1, BString("Error: Getting Cycle Number: ") + err.getString());
			cerr << err.getString() + "\n";
			return 1;
		}

		// Set NextCycle type
		if(err = tmsControl.setNextCycle(cn + 1, cycleType)){
			err.set(1, BString("Error: Setting Next Cycle: ") + err.getString());
			cerr << err.getString() + "\n";
			return 1;
		}
	}
	
	if(captureDiagnostics != ""){
		// Capture Diagnostics Data from the TMS system
		if(err = tmsCaptureDiagnostics(tmsControl, tmsProcess, captureDiagnostics, outFile)){
			cerr << err.getString() + "\n";
			return 1;	
		}
	}
	
	if(readDataSpec != ""){
		// Read Data from the TMS system
		if(err = tmsGetData(tmsProcess, readDataSpec, outFile)){
			cerr << err.getString() + "\n";
			return 1;	
		}
	}
	
	if(perfPu){
		// Performance Test
		if(err = tmsPerfPu(tmsControl, tmsProcess, hostName, channel, numSamples)){
			cerr << err.getString() + "\n";
			return 1;	
		}
	}
	if(perf){
		// Performance Test
		if(err = tmsPerf(tmsProcess, channel, numSamples)){
			cerr << err.getString() + "\n";
			return 1;	
		}
	}

	return 0;
}
