/*******************************************************************************
* 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(){
}
BString Control::getName(){
return BString("Module: ") + moduleNum() + ": ";
}
BError Control::mergeError(BError err){
if(err){
err.set(err.getErrorNo(), getName() + err.getString());
}
return err;
}
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 mergeError(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();
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::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;
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(BError(err.getErrorNo(), getName() + " Pupe" + opupeEngines[i]->getSlot() + " Status: " + err.getErrorNo() + " " + err.getString()));
}
else {
errors.append(BError(0, getName() + " Pupe" + opupeEngines[i]->getSlot() + " Status: " + "Ok"));
}
}
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("Module") + moduleNum() + "_Running", "1"));
statusList.append(NameValue(BString("Module") + moduleNum() + "_NumberPupe", numPupe));
statusList.append(NameValue(BString("Module") + moduleNum() + "_CycleNumber", ocycleNumber));
statusList.append(NameValue(BString("Module") + moduleNum() + "_CycleType", ocycleType));
return err;
}
BError Control::getStatistics(BList<NameValue>& statsList){
BError err;
dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);
statsList.append(NameValue(getName() + "_UpTime", getTime() - ostartTime));
statsList.append(NameValue(getName() + "_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, getName() + "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;
double ts, te;
static double tmax;
dprintf(DBG_SETNEXTCYCLE, "%s\n", __PRETTY_FUNCTION__);
ts = getTime();
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, getName() + "The next cycle has already started");
oeventServers.errorEvent(ocycleNumber, err);
}
}
dprintf(DBG_SETNEXTCYCLE, "Tick: %d\n", ocycleTick);
// Synchronise Module controllers if in simulation mode
if(osimulate)
ocycleTick = 10;
olock.unlock();
te = getTime();
if((te - ts) > tmax){
tmax = te - ts;
}
dprintf(DBG_SETNEXTCYCLE, "Control::setNextCycle: Time: %f MaxTime: %f\n", te - ts, tmax);
dprintf(DBG_SETNEXTCYCLE, "%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, getName() + "No Pupe Engine Numbered: " + opupeEngines.number());
}
else {
opupeEngines[puChannel.pupeNum - 1]->getStatus(puChannel, puStatus);
}
}
opupeEnginesLock.unlock();
return err;
}
BError Control::getCycleInformation(UInt32 cycleNumber, CycleInformation& cycleInformation){
BError err;
dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__);
opupeEnginesLock.rdLock();
err = mergeError(opupeEngines[0]->getCycleInformation(cycleNumber, cycleInformation));
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, getName() + "PuChannel: PupeNum out of range");
}
err = mergeError(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, getName() + "PuChannel: PupeNum out of range");
}
err = mergeError(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, getName() + "PuChannel: PupeNum out of range");
}
err = mergeError(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, getName() + "PuChannel: PupeNum out of range");
}
err = mergeError(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, getName() + "PuChannel: PupeNum out of range");
}
err = mergeError(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, getName() + "PuChannel: PupeNum out of range");
}
err = mergeError(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, "Control::getPupeConfig: %d.%d.%d\n", puChannel.moduleNum, puChannel.pupeNum, puChannel.pupeChan);
opupeEnginesLock.rdLock();
if((puChannel.pupeNum < 1) || (puChannel.pupeNum > opupeEngines.number())){
opupeEnginesLock.unlock();
return err.set(ErrorMisc, getName() + "PuChannel: PupeNum out of range");
}
err = mergeError(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());
}
}
// Check ADC Pll Locks
opupeEnginesLock.rdLock();
for(opupeEngines.start(i); !opupeEngines.isEnd(i); opupeEngines.next(i)){
opupeEngines[i]->adcPllLockCheck();
}
opupeEnginesLock.unlock();
}
// 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(onum == 1){
if(err = oeventServers.cycleStartEvent(cycleNumber))
wprintf(getName() + "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(getName() + "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();
if(onum == 1){
oeventServers.cycleStopEvent(cycleNumber);
}
}
BError Control::initPupeEngines(){
BError err;
BError e;
int physical = 0;
int slot = 0;
int board;
Pupe* pupe;
BArray<int> devs;
int d = 0;
BString str;
BString s;
int master = 1;
if((s = config.findValue("PupeMaster:")) != ""){
master = config.findValue("PupeMaster:").retInt();
}
if(config.findValue("PupePhysicalOn:").retInt() == 1){
str = config.findValue("PupePhysicalDevices:");
while((s = str.pullToken(",")) != ""){
devs.resize(d + 1);
devs[d] = s.retInt();
d++;
}
if(err = opupeGeog.scan(devs))
return err;
physical = 1;
}
opupeEnginesLock.wrLock();
for(slot = 0; slot < 5; slot++){
if(!osimulate && physical)
board = opupeGeog.getBoardId(slot);
else
board = slot;
if(board < 0){
wprintf(getName() + "PupeEngine no board in slot: " + slot + "\n");
}
pupe = new Pupe(*this, slot, board);
opupeEngines.append(pupe);
if(e = pupe->init()){
wprintf(getName() + "PupeEngine init error: " + e.getString() + "\n");
}
if(master == (slot + 1))
pupe->setMaster(1);
else
pupe->setMaster(0);
}
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, getName() + "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, getName() + "Physical PuChannel not configured");
}
puPhysChannel = oconfigInfo.puReferences[puChannel - 1];
oconfigInfoLock.unlock();
return err;
}
int Control::moduleNum(){
return onum;
}