/******************************************************************************* * 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; } #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(), BString("TmsPuServer") + onum + " Pupe" + opupeEngines[i]->getSlot() + " Status: " + err.getErrorNo() + " " + err.getString())); } else { errors.append(BError(0, BString("TmsPuServer") + onum + " 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("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; 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, "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, BString("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 = 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, "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, "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, "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()); } } // 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(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(); 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; if(config.findValue("PupePhysicalLocation:").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(BString("PupeEngine no board in slot: ") + slot + "\n"); } pupe = new Pupe(*this, slot, board); opupeEngines.append(pupe); if(e = pupe->init()){ wprintf(BString("PupeEngine init error: ") + e.getString() + "\n"); } if(board == 0) 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, "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; }