RSS Git Download  Clone
Raw Blame History
/*******************************************************************************
 *	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 <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>

CycleParams::CycleParams(BString cycleType, CycleParam& params){
	this->cycleType = cycleType;
	this->params.append(params);
}



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

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

	return 0;
}


Control::Control(int dbFixed) :
	odebugFixed(dbFixed),
	otimer(*this),
	opuControlServer(*this, oboapServer, "puControl"),
	opuProcessServer(*this, oboapServer, "puProcess"){
	onum = 0;
	oinitialised = 0;
	osimulate = 0;
	osimulateTiming = 0;
	ocycleNumberNext = 0;
	oprocessingCycle = 0;
	oserverName = "localhost";
	ocycleTick = 0;
	
	ostartTime = getTime();
	ocycleProcessed = 0;
	ocycleParamsReload = 0;
}

Control::~Control(){
}

BError Control::init(int number){
	BError		err;
	BString		s;
	sched_param	sp;

	dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);

	if(number){
		onum = number;
	}
	else {
		if(s = config.findValue("ModuleControllerNumber:"))
			onum = s.retInt();
	}
	
	if(config.findValue("SimulateFpga:").retInt() == 1)
		osimulate = 1;
	else
		osimulate = 0;

	s = config.findValue("SimulateTiming:");
	if(s != "")
		osimulateTiming = s.retInt();
	else
		osimulateTiming = 0;

	if(s = config.findValue("TmsServer:"))
		oserverName = s;
		

	// 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
	otimer.start();

	opuControlServer.setName(BString("puControl-") + onum);
	opuProcessServer.setName(BString("puProcess-") + onum);

	if(err = oboapServer.init(oserverName, BoapServer::THREADED)){
		return err;
	}

	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());
	}

	err = initCmd();

	return err;
}

BError Control::initCmd(){
	BError		err;
	BIter		i;
	
	dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);

	olock.lock();
	oinitialised = 0;

	opupeEnginesLock.wrLock();
	for(opupeEngines.start(i); !opupeEngines.isEnd(i); ){
		delete opupeEngines[i];
		opupeEngines.del(i);
	}
	opupeEnginesLock.unlock();
	
	if(err = initPupeEngines()){
		olock.unlock();
		return err;
	}

	nprintf("%d Pupe Engines initialised\n", opupeEngines.number());

	// Setup default parameters
	setPupeControlInfo("Beam3");
	ocycleParamsReload = 1;

	olock.unlock();

	dprintf(DBG_CMD, "%s: End\n", __PRETTY_FUNCTION__);

	return err;
}

BError Control::configure(ConfigInfo configInfo){
	BError	err;
	
	dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);
	
	oconfigInfoLock.lock();
	oconfigInfo = configInfo;
	oconfigInfoLock.unlock();

	oinitialised = 1;

	return err;
}

BError Control::test(BList<BError>& errors){
	BError	errRet;
	BError	err;
	BIter	i;
	
	dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);

	opupeEnginesLock.rdLock();
	for(opupeEngines.start(i); !opupeEngines.isEnd(i); opupeEngines.next(i)){
		if(err = opupeEngines[i]->status()){
			errors.append(err);
		}
	}
	opupeEnginesLock.unlock();

	return errRet;
}

BError Control::getStatus(BList<NameValue>& statusList){
	BError			err;
	BError			e;
	int			numPupe = 0;
	BIter			i;
	BString			s;
	int			p;
	BList<NameValue>	l;

	dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);

	opupeEnginesLock.rdLock();
#ifdef ZAP
	for(opupeEngines.start(i), p = 1; !opupeEngines.isEnd(i); opupeEngines.next(i), p++){
		e = opupeEngines[i]->status();
		s = BString(e.getErrorNo()) + ",\"" + e.getString() + "\"";
		statusList.append(NameValue(BString("TmsPuServer") + onum + "_Pupe" + p + "_Error", s));
		if(!e){
			numPupe++;
		}
	}
#else
	for(opupeEngines.start(i), p = 1; !opupeEngines.isEnd(i); opupeEngines.next(i), p++){
		e = opupeEngines[i]->status();
		if(!e){
			numPupe++;
		}

		opupeEngines[i]->getStatusList(PuChannel(1,1,1), statusList);
//		statusList = statusList + l;
	}
#endif
	opupeEnginesLock.unlock();
	
	statusList.append(NameValue(BString("TmsPuServer") + onum + "_Running", "1"));
	statusList.append(NameValue(BString("TmsPuServer") + onum + "_NumberPupe", numPupe));
	statusList.append(NameValue(BString("TmsPuServer") + onum + "_CycleNumber", ocycleNumber));
	statusList.append(NameValue(BString("TmsPuServer") + onum + "_CycleType", ocycleType));
	
	return err;
}

BError Control::getStatistics(BList<NameValue>& statsList){
	BError	err;
	
	dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);

	statsList.append(NameValue(BString("TmsPuServer") + onum + "_UpTime", getTime() - ostartTime));
	statsList.append(NameValue(BString("TmsPuServer") + onum + "_CycleNumProcessed", ocycleProcessed));
	
	return err;
}


BError Control::addEventServer(BString name){
	BError	err;
	
	dprintf(DBG_CMD, "%s: %s\n", __PRETTY_FUNCTION__, name.retStr());
	
	oeventServers.append(name);

	return err;
}

BError Control::delEventServer(BString name){
	BError	err;
	BIter	i;
	
	dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);

	oeventServers.del(name);

	return err;
}


BError Control::setControlInfo(CycleParam params){
	BError	err;
	BIter	i;
	BIter	i1, i2;
	int	found = 0;
	
	dprintf(DBG_CMD, "Control::setControlInfo: %s\n", params.cycleType.retStr());
	
	olock.lock();
	// Check if there is already an entry for this cycleType
	for(ocycleParms.start(i1); !ocycleParms.isEnd(i1); ocycleParms.next(i1)){
		if(ocycleParms[i1].cycleType == params.cycleType){
			found = 1;
			break;
		}
	}
	
	if(found){
		// See if there is an entry for this channel
		found = 0;
		for(ocycleParms[i1].params.start(i2); !ocycleParms[i1].params.isEnd(i2); ocycleParms[i1].params.next(i2)){
			if(ocycleParms[i1].params[i2].channel == params.channel){
				found = 1;
				break;
			}
		}
		if(found){
			ocycleParms[i1].params[i2] = params;
		}
		else {
			if(params.channel == 0){
				i = ocycleParms[i1].params.begin();
				ocycleParms[i1].params.insert(i, params);
			}
			else {
				ocycleParms[i1].params.append(params);
			}
		}
	}
	else {
		ocycleParms.append(CycleParams(params.cycleType, params));
	}

#ifdef ZAP
	printf("CycleTypes:\n");
	for(ocycleParms.start(i); !ocycleParms.isEnd(i); ocycleParms.next(i)){
		printf("%s\n", ocycleParms[i].cycleType.retStr());
	}
#endif

	if(params.cycleType == ocycleType){
		ocycleParamsReload = 1;
	}

	olock.unlock();
	
	return err;
}

BError Control::setPupeControlInfo(BString cycleType){
	BError		err;
	BIter		i, i1, i2;
	PuChannel	puChannel;
	CycleParam	params;
	
	dprintf(DBG_CMD, "Control::setPupeControlInfo: CycleType: %s Number: %d\n", cycleType.retStr(), ocycleParms.number());

	// Could improve this with a hashed name lookup
	for(ocycleParms.start(i1); !ocycleParms.isEnd(i1); ocycleParms.next(i1)){
		dprintf(DBG_STD, "Control::setPupeControlInfo: CheckCycleType: %s - %s\n", ocycleParms[i1].cycleType.retStr(), cycleType.retStr());
		if(ocycleParms[i1].cycleType == cycleType){
			for(ocycleParms[i1].params.start(i2); !ocycleParms[i1].params.isEnd(i2); ocycleParms[i1].params.next(i2)){
				if(ocycleParms[i1].params[i2].channel == 0){
					puChannel = PuChannel(0, 0, 0);
					opupeEnginesLock.rdLock();
					for(opupeEngines.start(i); !opupeEngines.isEnd(i); opupeEngines.next(i)){
						opupeEngines[i]->setControlInfo(puChannel, ocycleParms[i1].params[i2]);
					}
					opupeEnginesLock.unlock();
				}
				else {
					getPuPhysChannel(ocycleParms[i1].params[i2].channel, puChannel);
					opupeEnginesLock.rdLock();
					if(puChannel.pupeNum > opupeEngines.number()){
						err.set(ErrorParam, BString("No Pupe Engine Numbered: ") + puChannel.pupeNum);
						opupeEnginesLock.unlock();
						return err;
					}
					
					opupeEngines[puChannel.pupeNum - 1]->setControlInfo(puChannel, ocycleParms[i1].params[i2]);
					opupeEnginesLock.unlock();
				}
			}
			break;
		}
	}

	return err;
}

BError Control::setNextCycle(UInt32 cycleNumber, BString cycleType){
	BError	err;
	BIter	i;
	
	dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);
	olock.lock();

	ocycleNumberNext = cycleNumber;
	ocycleTypeNext = cycleType;
	
	olock.unlock();

	opupeEnginesLock.rdLock();
	for(opupeEngines.start(i); !opupeEngines.isEnd(i); opupeEngines.next(i)){
		opupeEngines[i]->setNextCycle(cycleNumber, cycleType);
	}
	opupeEnginesLock.unlock();
	
	olock.lock();

	// Possible race hazard here, CycleStart may occur before we have loaded
	// the state/phase tables
	if(!oprocessingCycle && (ocycleType != ocycleTypeNext)){
		// Update Pupe Engine's configuration
		setPupeControlInfo(ocycleTypeNext);
		if(oprocessingCycle){
			err.set(ErrorCycleNumber, "The next cycle has already started");
			oeventServers.errorEvent(ocycleNumber, err);
		}
	}

	olock.unlock();

	dprintf(DBG_CMD, "%s: End\n", __PRETTY_FUNCTION__);

	return err;
}

BError Control::getStatus(PuChannel puChannel, PuStatus& puStatus){
	BError	err;
	BIter	i;
	
	dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);
	opupeEnginesLock.rdLock();
	if(puChannel.pupeNum == 0){
		for(opupeEngines.start(i); !opupeEngines.isEnd(i); opupeEngines.next(i)){
			opupeEngines[i]->getStatus(puChannel, puStatus);
		}
	}
	else {
		if(puChannel.pupeNum > opupeEngines.number()){
			err.set(ErrorParam, BString("No Pupe Engine Numbered: ") + opupeEngines.number());
		}
		else {
			opupeEngines[puChannel.pupeNum - 1]->getStatus(puChannel, puStatus);
		}
	}
	opupeEnginesLock.unlock();

	return err;
}

BError Control::getData(PuChannel puChannel, DataInfo dataInfo, Data& data){
	BError	err;
	
	dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);

	opupeEnginesLock.rdLock();
	if((puChannel.pupeNum < 1) || (puChannel.pupeNum > opupeEngines.number())){
		opupeEnginesLock.unlock();
		return err.set(ErrorParam, "PuChannel: PupeNum out of range");
	}
	
	err = opupeEngines[puChannel.pupeNum - 1]->getData(puChannel, dataInfo, data);
	opupeEnginesLock.unlock();
	
	return err;
}

BError Control::requestData(PuChannel puChannel, DataInfo dataInfo){
	BError	err;

	dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);

	return err;
}


BError Control::setTestMode(PuChannel puChannel, UInt32 testOutput, UInt32 timingDisableMask){
	BError	err;
	
	dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);

	opupeEnginesLock.rdLock();
	if((puChannel.pupeNum < 1) || (puChannel.pupeNum > opupeEngines.number())){
		opupeEnginesLock.unlock();
		return err.set(ErrorMisc, "PuChannel: PupeNum  out of range");
	}
	
	err = opupeEngines[puChannel.pupeNum - 1]->setTestMode(puChannel, testOutput, timingDisableMask);

	opupeEnginesLock.unlock();

	return err;
}

BError Control::setTimingSignals(PuChannel puChannel, UInt32 timingSignals){
	BError	err;
	
	dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);

	opupeEnginesLock.rdLock();
	if((puChannel.pupeNum < 1) || (puChannel.pupeNum > opupeEngines.number())){
		opupeEnginesLock.unlock();
		return err.set(ErrorMisc, "PuChannel: PupeNum  out of range");
	}
	
	err = opupeEngines[puChannel.pupeNum - 1]->setTimingSignals(puChannel, timingSignals);

	opupeEnginesLock.unlock();

	return err;
}

BError Control::captureTestData(PuChannel puChannel, TestCaptureInfo captureInfo, BArray<UInt64>& data){
	BError	err;
	
	dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);

	opupeEnginesLock.rdLock();
	if((puChannel.pupeNum < 1) || (puChannel.pupeNum > opupeEngines.number())){
		opupeEnginesLock.unlock();
		return err.set(ErrorMisc, "PuChannel: PupeNum  out of range");
	}
	
	err = opupeEngines[puChannel.pupeNum - 1]->captureTestData(puChannel, captureInfo, data);

	opupeEnginesLock.unlock();

	dprintf(DBG_CMD, "%s: End\n", __PRETTY_FUNCTION__);

	return err;
}

BError Control::setTestData(PuChannel puChannel, Int32 on, BArray<UInt32> data){
	BError	err;

	dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);

	opupeEnginesLock.rdLock();
	if((puChannel.pupeNum < 1) || (puChannel.pupeNum > opupeEngines.number())){
		opupeEnginesLock.unlock();
		return err.set(ErrorMisc, "PuChannel: PupeNum  out of range");
	}
	
	err = opupeEngines[puChannel.pupeNum - 1]->setTestData(puChannel, on, data);

	opupeEnginesLock.unlock();
	return err;
}

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

	dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);

	opupeEnginesLock.rdLock();
	if((puChannel.pupeNum < 1) || (puChannel.pupeNum > opupeEngines.number())){
		opupeEnginesLock.unlock();
		return err.set(ErrorMisc, "PuChannel: PupeNum  out of range");
	}
	
	err = opupeEngines[puChannel.pupeNum - 1]->setPupeConfig(puChannel, pupeConfig);
	
	osimulateTiming = pupeConfig.internalTimingMask;

	opupeEnginesLock.unlock();
	return err;
}

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

	dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);

	opupeEnginesLock.rdLock();
	if((puChannel.pupeNum < 1) || (puChannel.pupeNum > opupeEngines.number())){
		opupeEnginesLock.unlock();
		return err.set(ErrorMisc, "PuChannel: PupeNum  out of range");
	}
	
	err = opupeEngines[puChannel.pupeNum - 1]->getPupeConfig(puChannel, pupeConfig);
	
	opupeEnginesLock.unlock();
	return err;
}



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


void Control::timer(){
	static uint32_t	tick = 0;
	static int	simEnable = 0;
	BError		err;
	BIter		i;
	
	if(simEnable){
		if(osimulate){
			opupeEnginesLock.rdLock();
			if(ocycleTick == 0){
				for(opupeEngines.start(i); !opupeEngines.isEnd(i); opupeEngines.next(i))
					opupeEngines[i]->cycleStart();
			}
			else if(ocycleTick == 10){
				for(opupeEngines.start(i); !opupeEngines.isEnd(i); opupeEngines.next(i))
					opupeEngines[i]->cycleStop();
			}
			opupeEnginesLock.unlock();
		}
		else {
			opupeEnginesLock.rdLock();

			if((osimulateTiming & TimingSigCycleStart) && (ocycleTick == 0)){
				if(opupeEngines.number())
					opupeEngines.front()->fpgaTriggerTimingInputs(TimingSigCycleStart);
			}
			else if((osimulateTiming & TimingSigInjection) && (ocycleTick == 4)){
				if(opupeEngines.number())
					opupeEngines.front()->fpgaTriggerTimingInputs(TimingSigInjection);
			}
			else if((osimulateTiming & TimingSigHChange) && (ocycleTick == 6)){
				if(opupeEngines.number())
					opupeEngines.front()->fpgaTriggerTimingInputs(TimingSigHChange);
			}
			else if((osimulateTiming & TimingSigCycleStop) && (ocycleTick == 10)){
				if(opupeEngines.number())
					opupeEngines.front()->fpgaTriggerTimingInputs(TimingSigCycleStop);
			}

			opupeEnginesLock.unlock();

		}
		if(++ocycleTick == 12)
			ocycleTick = 0;
	}

	if(tick == 30){
		simEnable = 1;
	}

	// Every 1 seconds do some house keeping
	if((tick % 10) == 0){
		if(!oinitialised){
			TmsControl	tmsControl(BString("//") + oserverName + "/tmsControl");

			if(err = tmsControl.puServerStarted(onum)){
				wprintf("Unable to call puServerStarted on %s: %s\n", oserverName.retStr(), err.getString().retStr());
			}
		}
	}
	
	// Every 10 seconds do some house keeping
	if((tick % 100) == 0){
	}
	tick++;
}

void Control::cycleStart(UInt32 cycleNumber){
	BError	err;
	
	dprintf(DBG_CYCLE, "Control::cycleStart\n");
	
	olock.lock();

	oprocessingCycle = 1;
	
	ocycleNumber = ocycleNumberNext;
	ocycleType = ocycleTypeNext;

	// Make sure timing simulation is in sync with cycle start
	ocycleTick = 0;

	if(err = oeventServers.cycleStartEvent(cycleNumber))
		wprintf(BString("Error send CycleStart event: ") + err.getString() + "\n");

	olock.unlock();
}

void Control::cycleError(UInt32 cycleNumber, BError error){
	dprintf(DBG_CYCLE, "Control::cycleError\n");
	
	olock.lock();

	oeventServers.errorEvent(cycleNumber, error);
	eprintf("Control::cycleError: Cycle: %d Error: %s\n", cycleNumber, error.getString().retStr());

	olock.unlock();
}

void Control::cycleStop(UInt32 cycleNumber){
	dprintf(DBG_CYCLE, "Control::cycleStop\n");

	olock.lock();

	ocycleProcessed++;
	oprocessingCycle = 0;

	if(ocycleParamsReload || (ocycleType != ocycleTypeNext)){
		// Update Pupe Engine's configuration
		setPupeControlInfo(ocycleTypeNext);
		ocycleParamsReload = 0;
	}

	olock.unlock();

	oeventServers.cycleStopEvent(cycleNumber);
}

BError Control::initPupeEngines(){
	BError	err;
	BError	e;
	int	board = 0;
	Pupe*	pupe;
	
	opupeEnginesLock.wrLock();
	for(board = 0; board < 5; board++){
		pupe = new Pupe(*this, board);
		opupeEngines.append(pupe);
		if(e = pupe->init())
			wprintf(BString("PupeEngine init error: ") + e.getString() + "\n");

		if(board == 0)
			pupe->setMaster(1);
	}
	opupeEnginesLock.unlock();

	return err;
}

BError Control::getPuChannel(PuChannel puPhysChannel, UInt32& puChannel){
	BError		err;
	unsigned int	i;
	
	// Could use a mapping table to speed this up if necessary
	oconfigInfoLock.lock();
	puChannel = 0;
	for(i = 0; i < oconfigInfo.puReferences.size(); i++){
		if((puPhysChannel.moduleNum == oconfigInfo.puReferences[i].moduleNum) &&
			(puPhysChannel.pupeNum == oconfigInfo.puReferences[i].pupeNum) &&
			(puPhysChannel.pupeChan == oconfigInfo.puReferences[i].pupeChan)){
				puChannel = i + 1;
				oconfigInfoLock.unlock();
				return err;
		}
	}
	oconfigInfoLock.unlock();
	
	return err.set(ErrorParam, "Physical PuChannel not configured");
}

BError Control::getPuPhysChannel(UInt32 puChannel, PuChannel& puPhysChannel){
	BError		err;
	
	oconfigInfoLock.lock();
	if((puChannel < 1) || (puChannel > oconfigInfo.puReferences.size())){
		oconfigInfoLock.unlock();
		return err.set(ErrorParam, "Physical PuChannel not configured");
	}
	
	puPhysChannel = oconfigInfo.puReferences[puChannel - 1];
	oconfigInfoLock.unlock();
	
	return err;
}

int Control::moduleNum(){
	return onum;
}