/*******************************************************************************
 *	Control.cc	Control process
 *			T.Barnaby,	BEAM Ltd,	2007-02-07
 *******************************************************************************
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h> 
#include <sys/socket.h>
#include <sys/mman.h>
#include <sys/vfs.h>
#include <sys/param.h>
#include <sys/time.h>
#include <netdb.h>
#include <main.h>
#include <Control.h>
#include <BEntry.h>
#include <BDir.h>
#include <BString.h>
#include <Debug.h>
#include <time.h>
#include <SigGen.h>
#include <TmsLib.h>
#include <BFile.h>

#define	TIME_MS_USE_DATAGET	1		// Convert startTime to orbitNumber for channels after the first

DataRequest::DataRequest(DataInfo dataInfo){
	dataInfo = dataInfo;
}

CycleInfo::CycleInfo(BUInt32 cycleNumber, BString cycleType){
	this->cycleNumber = cycleNumber;
	this->cycleType = cycleType;
}

CycleInfoList::CycleInfoList(){
}

void CycleInfoList::cycleAdd(BUInt32 cycleNumber, BString cycleType){
	CycleInfo	cycleInfo(cycleNumber, cycleType);
	
	olock.lock();
	
	while(olist.number() > numCycles)
		olist.queueGet();
		
	olist.append(cycleInfo);

	olock.unlock();
}

void CycleInfoList::cycleAddError(BUInt32 cycleNumber, BError error){
	BIter	i;
	
	olock.lock();
	
	for(olist.start(i); !olist.isEnd(i); olist.next(i)){
		if(olist[i].cycleNumber == cycleNumber){
			olist[i].errorList.append(error.copy());
			break;
		}
	}

	olock.unlock();
}

BList<BError> CycleInfoList::getCycleErrors(BUInt32 cycleNumber){
	BIter		i1, i2;
	BList<BError>	errorList;
	
	olock.lock();

	for(olist.start(i1); !olist.isEnd(i1); olist.next(i1)){
		if(olist[i1].cycleNumber == cycleNumber){
			for(olist[i1].errorList.start(i2); !olist[i1].errorList.isEnd(i2); olist[i1].errorList.next(i2)){
				errorList.append(olist[i1].errorList[i2].copy());
			}
			break;
		}
	}

	olock.unlock();
	return errorList;
}




ControlTimer::ControlTimer(Control& control) : ocontrol(control){
}

void* ControlTimer::function(){
	dprintf(DBG_THREADS, "ControlTimerThread: %d\n", gettid());
	
	while(1){
		usleep(100000);
		ocontrol.timer();
	}
	return 0;
}


Control::Control(int dbFixed) :
	odataAquire(*this),
	odebugFixed(dbFixed),
	ocontrolTimer(*this),
	otmsControlServer(*this, oboapServer, "tmsControl"),
	otmsProcessServer(*this, oboapServer, "tmsProcess"),
	otmsEventServer(*this, oboapServer, "tmsEvent"){
	osimulate = 0;
	ocycleNumberNext = 1;
	
	osimulation.timing = 0;
	osimulation.data = 0;
	osimulation.setNextCycle = 0;
	osimulation.doubleInjection = 0;
	
	oring = 0;
	omaster = 0;
	
	ostartTime = getTime();
	ocycleProcessed = 0;
	onumNextCycleErrors = 0;
	onumFpgaStateErrors = 0;
	onumDataGoneErrors = 0;
}

Control::~Control(){
}

BError Control::init(){
	BError		err;
	BString		s;
	sched_param	sp;
	
	dprintf(DBG_CMD, "Control::init\n");
	oboapnsHost = config.findValue("TmsServer:");
	oring = config.findValue("Ring:").retInt();
	omaster = config.findValue("Master:").retInt();
	
	if(config.findValue("SimulateData:").retInt() == 1)
		osimulate = 1;
	else
		osimulate = 0;

	if(config.findValue("SimulateNextCycle:").retInt() == 1)
		osimulation.setNextCycle = 1;
	else
		osimulation.setNextCycle = 0;
	
	// Set default priority to real-time for all of these threads
	if(realTime){
//		seteuid(0);
		sp.sched_priority = 25;
		if(sched_setscheduler(0, SCHED_RR, &sp))
			fprintf(stderr, "Warning: unable to set as real-time process\n");
	}
	
	// Initialise sub systems
	ocontrolTimer.start();

	if(oring){
		otmsControlServer.setName(BString("tmsControl") + oring);
		otmsProcessServer.setName(BString("tmsProcess") + oring);
		otmsEventServer.setName(BString("tmsEvent") + oring);
	}

	if(err = oboapServer.init(oboapnsHost, 0, BoapServer::THREADED)){
		return err;
	}
	
#ifdef ZAP
	if(realTime){
		// Set priority back to normal
		sp.sched_priority = 0;
		if(sched_setscheduler(0, SCHED_OTHER, &sp))
			fprintf(stderr, "Warning: unable to set as normal process\n");
		seteuid(getuid());
	}
#endif
	
	err = initCmd();

	nprintf("tmsServer version: %s for ring: %d started\n", VERSION, oring);
	dprintf(DBG_CMD, "Control::init: End\n");
	return err;
}

void Control::run(){
	oboapServer.run();
}

int Control::ring(){
	return oring;
}

void Control::timer(){
	static int	tick;

	if(osimulate){
		tick++;
		
		if(tick == 11){
			cycleStartEvent(ocycleNumber.value() + 1);
		}
		else if(tick == 12){
			tick = 0;
		}
	}
}

BError Control::readCycleParams(){
	BError		err;
	BString		sptDir = config.findValue("SptDir:");
	CycleParamDb	cycleParams(sptDir);
	CycleParam	params;
	BList<BString>	fileList;
	BIter		i;
	
	// Initialise State/Phase Tables from file based database
	ocycleParms.clear();
	cycleParams.getFileNames(fileList);
	for(fileList.start(i); !fileList.isEnd(i); fileList.next(i)){
		dprintf(DBG_CMD, "Control::readCycleParams: Add CycleType: %s\n", fileList[i].retStr());
		
		if(err = cycleParams.getCycleParams(fileList[i], params)){
			wprintf("Cycle Parameters in %s incorrect\n", fileList[i].retStr());
			return err;
		}
		else {
			ocycleParms.append(params);
		}
	}

	return err;
}

BError Control::initCmd(){
	BError		err;
	BError		err1;
	unsigned int	n;
	PuServer*	puServer;
	PuChannel	chan;
	BIter		i, i1;
	BString		s;
	
	dprintf(DBG_CMD, "Control::initCmd\n");
	
	olock.lock();

	// Initialise Pickup configuration from configuration file
	oconfigInfo.puReferences.resize(0);
	for(n = 1; n <= tmsMaxNumPickups; n++){
		s = config.findValue(BString("PickUp") + n + ":");
		if(s != ""){
			oconfigInfo.puReferences.resize(n);
			if(sscanf(s, "%hhd,%hhd,%hhd", &chan.moduleNum, &chan.pupeNum, &chan.pupeChan) != 3){
				err.set(ErrorConfig, "Pick-Up channel error in configuration file");
				eprintf("%s\n", err.getString().retStr());
				olock.unlock();
				return err;
			}
			oconfigInfo.puReferences[n - 1] = chan;
		}
		else {
			break;
		}
	}
	
	// Remove existing PuServers
	opuServersLock.wrLock();
	for(opuServers.start(i); !opuServers.isEnd(i); ){
		delete opuServers[i];
		opuServers.del(i);
	}
	
	// Initialise PuModules
	for(n = 1; n < 9; n++){
		if((s = config.findValue(BString("PuServer") + n + ":")) != ""){
			dprintf(DBG_CMD, "Control::initCmd: Configure PuServer: %d\n", n);
			opuServers.append(puServer = new PuServer(*this, n, atoi(s)));
			if(err1 = puServer->init()){
				wprintf("Unable to connect to PuServer: %d\n", n);
			}
			else {
				if(err1 = puServer->initCmd()){
					wprintf("Unable to initialise PuServer: %d\n", n);
				}
				if(!err1 && (err1 = puServer->configure(oring, oconfigInfo))){
					wprintf("Unable to configure PuServer: %d\n", n);
				}
			}
		}
	}
	opuServersLock.unlock();

	nprintf("Number of PuServers: %d\n", opuServers.number());
	
	// Initialise State/Phase Tables from file based database
	if(err = readCycleParams()){
		olock.unlock();
		return err;
	}

	if(!osimulate && omaster){
		for(ocycleParms.start(i1); !ocycleParms.isEnd(i1); ocycleParms.next(i1)){
			dprintf(DBG_CMD, "Control::initCmd: Add CycleType: %s\n", ocycleParms[i1].cycleType.retStr());
		
			opuServersLock.rdLock();
			for(opuServers.start(i); !opuServers.isEnd(i); opuServers.next(i)){
				if(err1 = opuServers[i]->setControlInfo(ocycleParms[i1])){
					wprintf("Module: %d is not available\n", opuServers[i]->getNumber());
				}
			}
			opuServersLock.unlock();
		}
	}

	olock.unlock();

	// Initialise first cycle
	s = config.findValue("DefaultCycleType:");
	if(s != ""){
		setNextCycleInternal(1, s);
	}
	
	olock.lock();
	// Clear Stats	
	ostartTime = getTime();
	ocycleProcessed = 0;
	onumNextCycleErrors = 0;
	onumFpgaStateErrors = 0;
	onumDataGoneErrors = 0;
	olock.unlock();

	dprintf(DBG_CMD, "Control::initCmd: End\n");
	return err;
}

BError Control::initPuServer(BUInt32 number){
	BError		err;
	BIter		i;
	BIter		i1;

	opuServersLock.rdLock();
	for(opuServers.start(i); !opuServers.isEnd(i); opuServers.next(i)){
		if(opuServers[i]->getNumber() == number){
			err = opuServers[i]->init();
			opuServers[i]->configure(oring, oconfigInfo);
			
			if(omaster){
				// Initialise State/Phase Tables from file based database
				for(ocycleParms.start(i1); !ocycleParms.isEnd(i1); ocycleParms.next(i1)){
					dprintf(DBG_CMD, "Control::initPuServer: Add CycleType: %s\n", ocycleParms[i1].cycleType.retStr());

					opuServers[i]->setControlInfo(ocycleParms[i1]);
				}
			}
		}
	}
	opuServersLock.unlock();
	
	return err;
}

BError Control::setProcessPriority(BUInt32 priority){
	BError			err;
	struct sched_param	sp;
	
	dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);
	
	if(realTime){
		sp.sched_priority = 26;
		if(sched_setscheduler(0, SCHED_RR, &sp))
			fprintf(stderr, "Warning: unable to set as real-time process\n");
	}

	return err;
}

BError Control::configure(ConfigInfo configInfo){
	BError	err;
	uint	i;
	BString	s;
	BIter	ii;

	dprintf(DBG_CMD, "Control::configure\n");
	
	// Check parameters
	if(configInfo.puReferences.size() > tmsMaxNumPickups){
		return err.set(ErrorParam, BString("The number of pickups to be configured is greater than: ") + tmsMaxNumPickups);
	}
	for(i = 0; i < configInfo.puReferences.size(); i++){
		if(configInfo.puReferences[i].moduleNum > 4)
			return err.set(ErrorParam, BString("The moduleNum parameter is too large"));
		if(configInfo.puReferences[i].pupeNum > 7)
			return err.set(ErrorParam, BString("The pupeNum parameter is too large"));
		if(configInfo.puReferences[i].pupeChan > 3)
			return err.set(ErrorParam, BString("The pupeChan parameter is too large"));
	}
	
	olock.lock();
	oconfigInfo = configInfo;
	olock.unlock();
	
	// Save configuration to file
	config.read();

	for(i = 0; i < configInfo.puReferences.size(); i++){
		s.printf("%d,%d,%d", configInfo.puReferences[i].moduleNum, configInfo.puReferences[i].pupeNum, configInfo.puReferences[i].pupeChan);
		config.setValue(BString("PickUp") + (i + 1) + ":", s);
	}

	// Clear further entries here
	for(i = configInfo.puReferences.size(); i < tmsMaxNumPickups; i++){
		config.deleteEntry(BString("PickUp") + (i + 1) + ":");
	}
	
	config.write();

	olock.lock();
	if(!osimulate){
		// Inform all Module Controllers
		opuServersLock.rdLock();
		for(opuServers.start(ii); !opuServers.isEnd(ii); opuServers.next(ii)){
			if(err = opuServers[ii]->configure(oring, configInfo)){
				eprintf("Unable to send configuration to module controller: %s\n", err.getString().retStr());
				break;
			}
		}
		opuServersLock.unlock();
	}
	olock.unlock();

	return err;
}

BError Control::getConfiguration(ConfigInfo& configInfo){
	BError	err;

	dprintf(DBG_CMD, "Control::getConfiguration: Return: %d\n", oconfigInfo.puReferences.size());
	
	olock.lock();	
	configInfo = oconfigInfo;
	olock.unlock();	

	return err;
}


BError Control::test(BList<BError>& errors){
	BError		errRet;
	BError		err;
	BIter		i;
	BIter		i1;
	BList<BError>	errList;
	
	dprintf(DBG_CMD, "Control::test\n");
	opuServersLock.rdLock();
	if(!osimulate){
		// Local tests
		
		// Disk space
		if(diskSpace("/") < 1000){
			errors.append(BError(1, "TmsServer: Diskspace /: Low"));
		}
		else {
			errors.append(BError(0, "TmsServer: Diskspace /: Ok"));
		}
		if(diskSpace("/data") < 1000){
			errors.append(BError(1, "TmsServer: Diskspace /data: Low"));
		}
		else {
			errors.append(BError(0, "TmsServer: Diskspace /data: Ok"));
		}
		
		// Memory
		if(memoryFree() < 200){
			errors.append(BError(1, "TmsServer: Memory: Low"));
		}
		else {
			errors.append(BError(0, "TmsServer: Memory: Ok"));
		}
		
		
		for(opuServers.start(i); !opuServers.isEnd(i); opuServers.next(i)){
			if(err = opuServers[i]->test(errList)){
				errors.append(BError(err.getErrorNo(), BString("Module") + opuServers[i]->getNumber() + ": Status: " + err.getString()));
			}
			else {
				errors.append(BError(0, BString("Module") + opuServers[i]->getNumber() + ": Status: Ok"));
				for(errList.start(i1); !errList.isEnd(i1); errList.next(i1)){
					errors.append(errList[i1]);
				}
			}
		}
	}
	opuServersLock.unlock();

	return errRet;
}

BError Control::getStatus(BList<NameValue>& statusList){
	BError			errRet;
	BError			err;
	BIter			i;
	BIter			i1;
	BList<NameValue>	puStatusList;
	
	dprintf(DBG_CMD, "Control::getStatus\n");
	opuServersLock.rdLock();
	if(!osimulate){
		for(opuServers.start(i); !opuServers.isEnd(i); opuServers.next(i)){
			if(err = opuServers[i]->getStatus(puStatusList)){
				statusList.append(NameValue(BString("Module") + opuServers[i]->getNumber() + "_Running", "0"));
			}
			else {
				for(puStatusList.start(i1); !puStatusList.isEnd(i1); puStatusList.next(i1)){
					statusList.append(puStatusList[i1]);
				}
			}
		}
	}
	opuServersLock.unlock();
	statusList.append(NameValue("TmsServer_Running", "1"));
	statusList.append(NameValue("TmsServer_CycleNumber", ocycleNumber.value()));
	statusList.append(NameValue("TmsServer_CycleType", ocycleType));
	statusList.append(NameValue("TmsServer_ConnectionsNumber", oboapServer.getConnectionsNumber()));

	return errRet;
}

BError Control::getStatistics(BList<NameValue>& statsList){
	BError		err;
	BIter		i;
	BIter			i1;
	BList<NameValue>	puStatsList;

	dprintf(DBG_CMD, "Control::getStatistics\n");
	opuServersLock.rdLock();
	if(!osimulate){
		for(opuServers.start(i); !opuServers.isEnd(i); opuServers.next(i)){
			puStatsList.clear();
			opuServers[i]->getStatistics(puStatsList);
			for(puStatsList.start(i1); !puStatsList.isEnd(i1); puStatsList.next(i1)){
				statsList.append(puStatsList[i1]);
			}
		}
	}
	opuServersLock.unlock();

	statsList.append(NameValue("TmsServer_UpTime", getTime() - ostartTime));
	statsList.append(NameValue("TmsServer_CycleNumProcessed", ocycleProcessed));
	statsList.append(NameValue("TmsServer_NextCycleErrors", onumNextCycleErrors));
	statsList.append(NameValue("TmsServer_FpgaStateErrors", onumFpgaStateErrors));
	statsList.append(NameValue("TmsServer_DataGoneErrors", onumDataGoneErrors));

	return err;
}

BError Control::getPuChannel(BUInt32& puChannel, PuChannel& puPhysChannel){
	BError	err;

	dprintf(DBG_CMD, "Control::getPuChannel\n");
	olock.lock();
	if((puChannel < 1) || (puChannel > oconfigInfo.puReferences.size())){
		olock.unlock();
		return err.set(ErrorParam, "PuChannel out of range");
	}

	puPhysChannel = oconfigInfo.puReferences[puChannel - 1];
	olock.unlock();

	return err;
}

BError Control::puServerStarted(BUInt32 number){
	BError	err;

	dprintf(DBG_CMD, "Control::puServerStarted: %u\n", number);

	olock.lock();
	initPuServer(number);
	olock.unlock();

	return err;
}

BError Control::setSimulation(Simulation simulation){
	BError		err;
	uint32_t	chan;
	BArray<BUInt32>	data;
	BFile		file;
	int		nb;
	BIter		i;
	PuChannel	puChan;
	PupeConfig	pupeConfig;
	BUInt32		cn;
	BString		ct;
	BString		testSignalFileName = "beam3-437000-8.psd";
	BString		cycleType = "SimBeam3";

	// Read the test signal file
	if(err = file.open(testSignalFileName, "r")){
		if(err = file.open(BString("/usr/tms/data/") + testSignalFileName, "r")){
			return err.set(ErrorMisc, BString("Error: Opening file: ") + testSignalFileName);
		}
	}
	data.resize(1024);

	if((nb = file.read(&data[0], 1024 * sizeof(BUInt32))) <= 0){
		return err.set(1, BString("Error: Reading 1024 32bit data items from file: ") + testSignalFileName);
	}
	data.resize(nb / sizeof(BUInt32));

	// Setup timing
	pupeConfig.internalTimingMask = 0x00;			// No internal event signals
	pupeConfig.doubleInjection = simulation.doubleInjection;
	pupeConfig.adcSysclkSync = 0;
	pupeConfig.disableBlr = 0;

	opuServersLock.rdLock();
	
	// For all other PUPE boards set CLK to be local
	if(simulation.timing)
		pupeConfig.internalTimingMask = 0x01;			// The 10MHz clock
	else
		pupeConfig.internalTimingMask = 0x00;			// No internal event signals

	// Setup all PUPE boards to use CLK from local source
	for(chan = 0; chan < oconfigInfo.puReferences.size(); chan++){
		if(err = setPupeConfig(oconfigInfo.puReferences[chan], pupeConfig)){
			return err;
		}

	}

	// Set master PUPE to drive all other timing signals
	if(simulation.timing)
		pupeConfig.internalTimingMask = 0xff;			// All internal event signals
	else
		pupeConfig.internalTimingMask = 0x00;			// No internal event signals

	for(opuServers.start(i); !opuServers.isEnd(i); opuServers.next(i)){
		if(err = opuServers[i]->getMasterPuChannel(puChan)){
			opuServersLock.unlock();
			return err;
		}
		
		if(err = setPupeConfig(puChan, pupeConfig)){
			return err;
		}
	}
	
	opuServersLock.unlock();

	// Setup data
	for(chan = 0; chan < oconfigInfo.puReferences.size(); chan++){
		if(err = setTestData(oconfigInfo.puReferences[chan], simulation.data, data)){
			return err;
		}	
	}

	// Setup setNextCycle
	osimulation = simulation;

	if(osimulation.cycleType != "")
		cycleType = osimulation.cycleType;

	// Sets the CycleType to match the data
	if(err = getCycleInfo(cn, ct)){
		return err.set(ErrorMisc, BString("Error: Getting Cycle Number: ") + err.getString());
	}

	// Set NextCycle type
	if(err = setNextCycleInternal(cn + 1, cycleType)){
		return err.set(ErrorMisc, BString("Error: Setting Next Cycle: ") + err.getString());
	}	
	
	return err;
}

BError Control::getSimulation(Simulation& simulation){
	BError	err;
	
	olock.lock();
	simulation = osimulation;
	olock.unlock();

	return err;
}

BError Control::setTestMode(PuChannel puPhysChannel, BUInt32 testOutput, BUInt32 timingDisableMask){
	BError	err;

	opuServersLock.rdLock();

	if((puPhysChannel.moduleNum < 1) || (puPhysChannel.moduleNum > opuServers.number())){
		opuServersLock.unlock();
		return err.set(ErrorParam, BString("PuChannel.moduleNum: ") + puPhysChannel.moduleNum + " is invalid");
	}
	
	err = opuServers[puPhysChannel.moduleNum - 1]->setTestMode(puPhysChannel, testOutput, timingDisableMask);
	
	opuServersLock.unlock();

	return err;
}

BError Control::setTimingSignals(PuChannel puPhysChannel, BUInt32 timingSignals){
	BError	err;

	opuServersLock.rdLock();

	if((puPhysChannel.moduleNum < 1) || (puPhysChannel.moduleNum > opuServers.number())){
		opuServersLock.unlock();
		return err.set(ErrorParam, BString("PuChannel.moduleNum: ") + puPhysChannel.moduleNum + " is invalid");
	}
	
	err = opuServers[puPhysChannel.moduleNum - 1]->setTimingSignals(puPhysChannel, timingSignals);

	opuServersLock.unlock();

	return err;
}

BError Control::captureDiagnostics(PuChannel puPhysChannel, TestCaptureInfo captureInfo, BArray<BUInt64>& data){
	BError	err;

	opuServersLock.rdLock();

	if(osimulate){
		err = getSimCaptureData(captureInfo, data);
	}
	else {
		if((puPhysChannel.moduleNum < 1) || (puPhysChannel.moduleNum > opuServers.number())){
			opuServersLock.unlock();
			return err.set(ErrorParam, BString("PuChannel.moduleNum: ") + puPhysChannel.moduleNum + " is invalid");
		}
		err = opuServers[puPhysChannel.moduleNum - 1]->captureDiagnostics(puPhysChannel, captureInfo, data);
	}

	opuServersLock.unlock();

	return err;
}

BError Control::setTestData(PuChannel puPhysChannel, BInt32 on, BArray<BUInt32> data){
	BError	err;

	opuServersLock.rdLock();

	if((puPhysChannel.moduleNum < 1) || (puPhysChannel.moduleNum > opuServers.number())){
		opuServersLock.unlock();
		return err.set(ErrorParam, BString("PuChannel.moduleNum: ") + puPhysChannel.moduleNum + " is invalid");
	}
	
	err = opuServers[puPhysChannel.moduleNum - 1]->setTestData(puPhysChannel, on, data);

	opuServersLock.unlock();

	return err;
}

BError Control::setPupeConfig(PuChannel puPhysChannel, PupeConfig pupeConfig){
	BError	err;

	opuServersLock.rdLock();

	if((puPhysChannel.moduleNum < 1) || (puPhysChannel.moduleNum > opuServers.number())){
		opuServersLock.unlock();
		return err.set(ErrorParam, BString("PuChannel.moduleNum: ") + puPhysChannel.moduleNum + " is invalid");
	}
	
	err = opuServers[puPhysChannel.moduleNum - 1]->setPupeConfig(puPhysChannel, pupeConfig);

	opuServersLock.unlock();
	return err;
}

BError Control::getPupeConfig(PuChannel puPhysChannel, PupeConfig& pupeConfig){
	BError	err;

	opuServersLock.rdLock();

	if((puPhysChannel.moduleNum < 1) || (puPhysChannel.moduleNum > opuServers.number())){
		opuServersLock.unlock();
		return err.set(ErrorParam, BString("PuChannel.moduleNum: ") + puPhysChannel.moduleNum + " is invalid");
	}
	
	err = opuServers[puPhysChannel.moduleNum - 1]->getPupeConfig(puPhysChannel, pupeConfig);

	opuServersLock.unlock();
	return err;
}




BError Control::setControlInfo(CycleParam params){
	BError		err;
	BIter		i;
	BString		sptDir = config.findValue("SptDir:");
	CycleParamDb	cycleParamFiles(sptDir);
	int		updated = 0;
	
	dprintf(DBG_CMD, "Control::setControlInfo\n");
	
	if(!omaster)
		return err.set(ErrorStateTable, "Cannot set the Cycle information on a non master tmsServer");

	// Add to internal database
	olock.lock();
	for(ocycleParms.start(i); !ocycleParms.isEnd(i); ocycleParms.next(i)){
		if((ocycleParms[i].cycleType == params.cycleType) && (ocycleParms[i].ring == params.ring) && (ocycleParms[i].channel == params.channel)){
			ocycleParms[i] = params;
			updated = 1;
			break;
		}
	}
	
	if(!updated)
		ocycleParms.append(params);

	// Save to files
	err = cycleParamFiles.setCycleParams(params);
	olock.unlock();

	opuServersLock.rdLock();

	if(!osimulate){
		for(opuServers.start(i); !opuServers.isEnd(i); opuServers.next(i)){
			if(err = opuServers[i]->setControlInfo(params)){
				opuServersLock.unlock();
				return err;
			}
		}
	}
	
	opuServersLock.unlock();

	return err;
}

BError Control::getControlInfo(BString cycleType, BUInt32 ring, BUInt32 puChannel, CycleParam& params){
	BError	err;
	BIter	i;
	
	olock.lock();
	for(ocycleParms.start(i); !ocycleParms.isEnd(i); ocycleParms.next(i)){
		if((ocycleParms[i].cycleType == cycleType) && (ocycleParms[i].ring == ring) && (ocycleParms[i].channel == puChannel)){
			params = ocycleParms[i];
			olock.unlock();
			return err;
		}
	}		
	olock.unlock();

	err.set(ErrorMisc, BString("Cycle parameters not found for cycle type: ") + cycleType);
	
	return err;
}

BError Control::delControlInfo(BString cycleType, BUInt32 ring, BUInt32 puChannel){
	BError		err;
	BString		sptDir = config.findValue("SptDir:");
	CycleParamDb	cycleParamFiles(sptDir);
	BIter	i;

	dprintf(DBG_CMD, "Control::delControlInfo: %s, %u\n", cycleType.retStr(), puChannel);

	if(!omaster)
		return err.set(ErrorStateTable, "Cannot delete the Cycle information on a non master tmsServer");

	olock.lock();
	for(ocycleParms.start(i); !ocycleParms.isEnd(i); ocycleParms.next(i)){
		if((ocycleParms[i].cycleType == cycleType) && (ocycleParms[i].ring == ring) && (ocycleParms[i].channel == puChannel)){
			ocycleParms.del(i);
			break;
		}
	}		
	olock.unlock();

// Deleted. I don't want the .spt file wiped out. - JMB
//	olock.lock();
//	err = cycleParamFiles.deleteCycleParams(cycleType, ring, puChannel);
//	olock.unlock();

	return err;
}

BError Control::getControlList(BList<CycleParamItem>& itemList){
	BError	err;
	BIter	i;
	
	itemList.clear();

	olock.lock();
	for(ocycleParms.start(i); !ocycleParms.isEnd(i); ocycleParms.next(i)){
		itemList.append(CycleParamItem(ocycleParms[i].cycleType, ocycleParms[i].ring, ocycleParms[i].channel));
	}		
	olock.unlock();

	return err;
}

BError Control::setNextCycle(BUInt32 cycleNumber, BString cycleType){
	if(osimulation.setNextCycle)
		return BError(ErrorMisc, "System is simulating the setNextCycle call");
	else
		return setNextCycleInternal(cycleNumber, cycleType);
}

BError Control::setNextCycleInternal(BUInt32 cycleNumber, BString cycleType){
	BError		err;
	BIter		i;
	int		t = 0;
	double		ts, tm[8], te;
	static double	tmax;

	dprintf(DBG_CMD | DBG_EVENT, "Control::setNextCycleInternal: Num(%d) Type(%s)\n", cycleNumber, cycleType.retStr());

	ocycleNumberNext = cycleNumber;
	ocycleTypeNext = cycleType;

	opuServersLock.rdLock();

	if(!osimulate){
		tm[t++] = ts = getTime();
		for(opuServers.start(i); !opuServers.isEnd(i); opuServers.next(i)){
			if(err = opuServers[i]->setNextCycle(cycleNumber, cycleType)){
				opuServersLock.unlock();
				dprintf(DBG_CMD | DBG_EVENT, "Control::setNextCycle: Done: Error: %s\n", err.getString().retStr());
				return err;
			}
			tm[t++] = getTime();
		}
		te = getTime();
		if((te - ts) > tmax){
			tmax = te - ts;
		}
		tm[3] = tm[3] - tm[2];
		tm[2] = tm[2] - tm[1];
		tm[1] = tm[1] - tm[0];
		dprintf(DBG_SETNEXTCYCLE, "Control::setNextCycle: Times: %f,%f,%f = %f MaxTime: %f\n", tm[1],tm[2],tm[3], te - ts, tmax);
	}
	
	opuServersLock.unlock();

	dprintf(DBG_CMD | DBG_EVENT, "Control::setNextCycle: Done\n");

	return err;
}

BError Control::getCycleInfo(BUInt32& cycleNumber, BString& cycleType){
	BError		err;

	dprintf(DBG_CMD, "Control::getCycleInfo\n");
	cycleNumber = ocycleNumber.value();
	cycleType = ocycleType;
	
	return err;
}

BError Control::getCycleInformation(BUInt32 cycleNumber, CycleInformation& cycleInformation){
	BError		err;
	PuChannel	puPhysChannel(1, 1, 1);

	dprintf(DBG_CMD, "Control::getCycleInformation\n");

	if(err = waitForData(cycleNumber))
		return err;

	opuServersLock.rdLock();
	err = opuServers[puPhysChannel.moduleNum - 1]->getCycleInformation(cycleNumber, cycleInformation);
	opuServersLock.unlock();
	
	return err;
}

BError Control::getCycleTypeInformation(BString cycleType, CycleTypeInformation& cycleTypeInformation){
	BError				err;
	CycleParam			params;
	BUInt32				n;
	CycleTypeInformationPeriod	period;
	
	dprintf(DBG_CMD, "Control::getCycleTypeInformation\n");
	
	if(err = getControlInfo(cycleType, 0, 0, params))
		return err;
	
	cycleTypeInformation.cycleType = cycleType;
	cycleTypeInformation.info = params.info;
	
	for(n = 0; n < params.stateTable.size(); n++){
		if(params.stateTable[n].period){
			period.cyclePeriod = params.stateTable[n].period;
			period.harmonic = params.stateTable[n].harmonic;
			period.numBunches = params.stateTable[n].numBunches;
			period.bunchMask = params.stateTable[n].bunchMask;
			cycleTypeInformation.periods.append(period);
		}
	}
	
	return err;
}


BError Control::waitForData(BUInt32 cycleNumber){
	BError	err;
	BInt32	diff;

	// In the initial state we need to check if we have actually captured the cycle
	dprintf(DBG_MISC, "Control::waitForData: RequiredCycle: %u CurrentCycle: %u CompletedCycle: %u\n", cycleNumber, ocycleNumber.value(), ocycleCompleteNumber.value());

	// First check if we are within the possible cycle range
	diff = cycleNumber - ocycleCompleteNumber.value();
	
	if(diff < -4)
		return err.set(ErrorDataGone, BString("TmsServer: Cycle's data has already been overwritten: Current: ") + ocycleCompleteNumber.value() + " Requested: " + cycleNumber);
	if(diff > 256)
		return err.set(ErrorDataFuture, BString("TmsServer: Cycle data required is to far in the future: Current: ") + ocycleCompleteNumber.value() + " Requested: " + cycleNumber);

	// Need Timeout
	ocycleCompleteNumber.waitMoreThanOrEqual(cycleNumber);
	
	return err;
}

void Control::errorEvent(BUInt32 cycleNumber, BError error){
	dprintf(DBG_EVENT, "Control::errorEvent: %d %s\n", cycleNumber, error.getString().retStr());

	ocycleInfoList.cycleAddError(cycleNumber, error);
	if(error.getErrorNo() == ErrorStateTable)
		onumFpgaStateErrors++;

	oeventServers.errorEvent(cycleNumber, error);
}

void Control::cycleStartEvent(BUInt32 cycleNumber){
	BUInt32	currentCycleNumber = ocycleNumber.value();
	
	dprintf(DBG_EVENT, "Control::cycleStartEvent: Cycle: %u CurrentCycle: %u NextCycle: %u CompleCycle: %u\n", cycleNumber, currentCycleNumber, ocycleNumberNext, ocycleCompleteNumber.value());

	ocycleNumber.setValue(cycleNumber);
	ocycleType = ocycleTypeNext;

	ocycleInfoList.cycleAdd(cycleNumber, ocycleTypeNext);

	if(cycleNumber == currentCycleNumber){
		onumNextCycleErrors++;
		errorEvent(cycleNumber, BError(ErrorCycleNumber, "Cycle Information has not been received"));
	}	

	oeventServers.cycleStartEvent(cycleNumber);
}

void Control::cycleStopEvent(BUInt32 cycleNumber){
	BIter	i;
	BError	err;
	double	t1, t2;
	
	dprintf(DBG_EVENT, "Control::cycleStopEvent: %u\n", cycleNumber);
	
	ocycleCompleteNumber.setValue(cycleNumber);
	ocycleProcessed++;

	// Check all requests
	for(orequestList.start(i); !orequestList.isEnd(i); ){
		if(cycleNumber >= orequestList[i].dataInfo.cycleNumber){
			oeventServers.dataEvent(orequestList[i].dataInfo);
			orequestList.del(i);
		}
		else {
			orequestList.next(i);
		}
	}
	
	oeventServers.cycleStopEvent(cycleNumber);

	if(osimulation.setNextCycle){
		t1 = getTime();
		err = setNextCycleInternal(ocycleNumberNext, ocycleTypeNext);
		t2 = getTime();
		dprintf(DBG_SETNEXTCYCLE, "Send setNextCycle: %u current: %u Time: %fms Error: %s\n", ocycleNumberNext, cycleNumber, (t2 - t1) * 1000.0, err.getString().retStr());
		ocycleNumberNext++;
	}
}

BError Control::getData(DataInfo dataInfo, Data& data){
	BError		err;
	BError		e;
	PuChannel	puPhysChannel(0, 0, 1);
	BList<BError>	errList;
	BUInt32		chan;
	BUInt32		orbitNumber;

	dprintf(DBG_CMD, "Control::getData\n");

	// Setup for error return
	data.numValues = 0;
	data.dataType = 0;
	data.numBunches = 0;
	data.numChannels = 0;

	// Check parameters
	if(dataInfo.channel > oconfigInfo.puReferences.size())
		return err.set(ErrorParam, "dataInfo.channel parameter is incorrect");
	if(dataInfo.cyclePeriod > CyclePeriodEvent9)
		return err.set(ErrorParam, "dataInfo.cyclePeriod parameter is incorrect");
	if(dataInfo.startTime > 4000)
		return err.set(ErrorParam, "dataInfo.startTime parameter is to large (max: 4000)");
	if(dataInfo.orbitNumber > 1500000)
		return err.set(ErrorParam, "dataInfo.orbitNumber parameter is to large (max: 1500000)");
	if(dataInfo.bunchNumber > 500000)
		return err.set(ErrorParam, "dataInfo.bunchNumber parameter is to large");
	if(dataInfo.numValues > (40 * 8 * 500000))
		return err.set(ErrorParam, "dataInfo.numValues parameter is to large (max: 40 * 8 * 500000)");

	if(err = waitForData(dataInfo.cycleNumber))
		return err;

	if(osimulate){
		err = odataAquire.getData(dataInfo, data);
	}
	else {
		if(dataInfo.channel){
			data.errors.resize(1);

			olock.lock();
			puPhysChannel = oconfigInfo.puReferences[dataInfo.channel - 1];
			olock.unlock();

			opuServersLock.rdLock();

			if(puPhysChannel.moduleNum > opuServers.number()){
				opuServersLock.unlock();
				data.errors[0] = err;
				return err.set(ErrorMC, BString("Module: ") + puPhysChannel.moduleNum + " is not available");
			}

			err = opuServers[puPhysChannel.moduleNum - 1]->getData(puPhysChannel, dataInfo, data, orbitNumber);

			// Check if there were any Cycle Errors
			if(!err){
				errList = ocycleInfoList.getCycleErrors(dataInfo.cycleNumber);
				if(errList.number()){
					err = errList.front();
				}
			}

			if(err.getErrorNo() == ErrorDataGone)
				onumDataGoneErrors++;

			opuServersLock.unlock();
			data.errors[0] = err;
		}
		else {
			BUInt32	numValues = 0;
			Data	dataSet[oconfigInfo.puReferences.size()];

			if(dataInfo.numValues < oconfigInfo.puReferences.size()){
				return err.set(ErrorParam, "NumValues to small for request");
			}

			// Get the data from all PUPE channels. This could be improved with multi-threading and
			//  geting the data from separate module controllers at the same time to reduce system latency

			dataInfo.numValues = dataInfo.numValues / oconfigInfo.puReferences.size();
			data.errors.resize(oconfigInfo.puReferences.size());

			for(chan = 0; chan < oconfigInfo.puReferences.size(); chan++){
				e = BError();

				olock.lock();
				puPhysChannel = oconfigInfo.puReferences[chan];
				olock.unlock();

				opuServersLock.rdLock();

				if(puPhysChannel.moduleNum > opuServers.number()){
					e.set(ErrorMC, BString("Module: ") + puPhysChannel.moduleNum + " is not available");
				}
				else {
					e = opuServers[puPhysChannel.moduleNum - 1]->getData(puPhysChannel, dataInfo, dataSet[chan], orbitNumber);
#if TIME_MS_USE_DATAGET
					// If we have set a startTime, use the orbit number for all other channels
					if(!e && dataInfo.startTime){
//						syslog(LOG_ERR, "GetData: Get orbit number for channel: %d at startTime: %d orbitNumber: %d\n", chan, dataInfo.startTime, orbitNumber);
						dataInfo.orbitNumber = orbitNumber;
						dataInfo.startTime = 0;
					}
#endif
				}
				
				data.errors[chan] = e;
				if(e && !err)
					err = e;

				opuServersLock.unlock();
			}

			// Check if there were any Cycle Errors
			if(!err){
				errList = ocycleInfoList.getCycleErrors(dataInfo.cycleNumber);
				if(errList.number()){
					err = errList.front();
				}
				for(chan = 0; chan < oconfigInfo.puReferences.size(); chan++){
					data.errors[chan] = err;
				}
			}

			if(err.getErrorNo() == ErrorDataGone)
				onumDataGoneErrors++;

			// Now merge the data
			// Calculate maximum number of values
			numValues = 0;
			data.dataType = 0;
			data.numBunches = 0;
			for(chan = 0; chan < oconfigInfo.puReferences.size(); chan++){
				if(!data.errors[chan]){
					data.dataType = dataSet[chan].dataType;
					data.numBunches = dataSet[chan].numBunches;
					if(dataSet[chan].dataValues.size() > numValues)
						numValues = dataSet[chan].dataValues.size();
				}
			}

			// Calculate minimum number of values
			for(chan = 0; chan < oconfigInfo.puReferences.size(); chan++){
				if(!data.errors[chan] && (dataSet[chan].dataValues.size() < numValues))
					numValues = dataSet[chan].dataValues.size();
			}
			
			// Make sure numValues is a multiple of the number of bunches
			if(data.numBunches)
				numValues = data.numBunches * (numValues / data.numBunches);

			data.numValues = numValues * oconfigInfo.puReferences.size();
			data.numChannels = oconfigInfo.puReferences.size();
			data.dataValues.resize(data.numValues);
			memset(data.dataValues.data(), 0, data.dataValues.size() * sizeof(DataValue));

			// Merge the data
			for(chan = 0; chan < oconfigInfo.puReferences.size(); chan++){
				if(!data.errors[chan])
					memcpy(&data.dataValues[chan * numValues], &dataSet[chan].dataValues[0], numValues * sizeof(DataValue));
			}
		}
	}

	return err;
}

BError Control::addEventServer(BString name){
	dprintf(DBG_CMD, "Control::addEventServer\n");
	return oeventServers.append(name);
}

BError Control::requestData(DataInfo dataInfo){
	BError		err;

	dprintf(DBG_CMD, "Control::requestData\n");

	olock.lock();
	orequestList.append(dataInfo);
	olock.unlock();
	
	return err;
}

BError Control::getSimCaptureData(TestCaptureInfo captureInfo, BArray<BUInt64>& data){
	BError		err;
	BUInt32		num = 1024;
	BUInt32		i;
	Data		d;
	Sample*		samples;
	SigGenBeam	sigBeam;
	BUInt64		v, dds_freq, ds;
	BUInt64		fref, lo1, lo2, blr, gate;
	BUInt64		mean1, mean2, rfSelect1, rfSelect2, selFilter;
	
	data.resize(num);
	samples = new Sample[num];

	sigBeam.config(125000000, 437000, 8, 0x3C, 0, 0, 1.0);
	sigBeam.generate(samples, num);

	for(i = 0; i < num; i++){
		ds = BUInt64(int(8191.0 * samples[i]) & 0x3FFF);
		dds_freq = 300000000 + (i % 16);
		lo1 = (i & 0x1) ? 1 : 0;
		lo2 = (i & 0x2) ? 1 : 0;
		blr = (i & 0x4) ? 1 : 0;
		gate = (i & 0x8) ? 1 : 0;
		fref = (i & 0x10) ? 1 : 0;
		mean1 = (i & 0x20) ? 1 : 0;
		mean2 = (i & 0x40) ? 1 : 0;
		rfSelect1 = (i & 0x80) ? 1 : 0;
		rfSelect2 = (i & 0x100) ? 1 : 0;
		selFilter = (i & 0x200) ? 1 : 0;

		v = (gate << 47) | (blr << 46) | (ds << 32) | dds_freq;
		v |= (selFilter << 55) | (rfSelect2 << 54) | (rfSelect1 << 53) | (fref << 52);
		v |= (mean2 << 51) | (mean1 << 50) | (lo2 << 49) | (lo1 << 48);

		data[i] = v;
	}

	return err;
}
#ifdef ZAP
  diagnostics(31 downto 0) <=  dds_freq;
  diagnostics(47 downto 32) <= GATE_pulse & BLR_pulse & sigma;
  diagnostics(55 downto 48) <= sel_filter & rf_select2 & rf_select1 & frev_in & phase_table_data(7 downto 6) & LO_pulse2 & LO_pulse1;
  diagnostics(63 downto 56) <= "0000" & switch_state; 
#endif
BString Control::boapnsHost(){
	return oboapnsHost;
}

uint32_t Control::diskSpace(BString dir){
	struct statfs	s;
	uint32_t	blocks = 0;
	
	if(!statfs(dir, &s)){
		blocks = ((uint64_t)s.f_bsize * s.f_bavail) / 1024;
	}
	return blocks;
}

uint32_t Control::memoryFree(){
	BEntryFile	file("/proc/meminfo");
	uint32_t	mbytes = 0;
	BString		s;
	
	if(!file.read()){
		mbytes = file.findValue("MemFree:").retInt() / 1024;
		mbytes += file.findValue("Buffers:").retInt() / 1024;
		mbytes += file.findValue("Cached:").retInt() / 1024;
	}
	
	return mbytes;
}
