/******************************************************************************* * 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"; ostartTime = getTime(); ocycleProcessed = 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; if(config.findValue("SimulateTiming:").retInt() == 1) osimulateTiming = 1; 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"); 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; dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__); opupeEnginesLock.rdLock(); 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++; } } 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, "%s\n", __PRETTY_FUNCTION__); 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)); } olock.unlock(); return err; } BError Control::setPupeControlInfo(BString cycleType){ BError err; BIter i, i1, i2; PuChannel puChannel; CycleParam params; dprintf(DBG_CMD, "%s\n", __PRETTY_FUNCTION__); // 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.internalTiming; opupeEnginesLock.unlock(); return err; } void Control::run(){ oboapServer.run(); } void Control::timer(){ static uint32_t tick = 0; static uint32_t cycleTick = 0; static int simEnable = 0; BIter i; if(osimulateTiming){ if(simEnable){ if(osimulate){ opupeEnginesLock.rdLock(); if(cycleTick == 0){ printf("Control::timer: cycleStart\n"); for(opupeEngines.start(i); !opupeEngines.isEnd(i); opupeEngines.next(i)) opupeEngines[i]->cycleStart(); } else if(cycleTick == 10){ for(opupeEngines.start(i); !opupeEngines.isEnd(i); opupeEngines.next(i)) opupeEngines[i]->cycleStop(); } opupeEnginesLock.unlock(); if(++cycleTick == 12) cycleTick = 0; } else { opupeEnginesLock.rdLock(); if(cycleTick == 0){ if(opupeEngines.number()) opupeEngines.front()->fpgaTriggerTimingInputs(TimingSigCycleStart); } else if(cycleTick == 4){ if(opupeEngines.number()) opupeEngines.front()->fpgaTriggerTimingInputs(TimingSigInjection); } #ifdef ZAP else if(cycleTick == 6){ if(opupeEngines.number()) opupeEngines.front()->fpgaTriggerTimingInputs(TimingSigHChange); } #endif else if(cycleTick == 10){ if(opupeEngines.number()) opupeEngines.front()->fpgaTriggerTimingInputs(TimingSigCycleStop); } opupeEnginesLock.unlock(); if(++cycleTick == 12) cycleTick = 0; } } else { if(++cycleTick == 30){ cycleTick = 0; simEnable = 1; } } } // Every 1 seconds do some house keeping if((tick % 10) == 0){ if(!oinitialised){ TmsControl tmsControl(BString("//") + oserverName + "/tmsControl"); tmsControl.puServerStarted(onum); } } // 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; 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(ocycleType != ocycleTypeNext){ // Update Pupe Engine's configuration setPupeControlInfo(ocycleTypeNext); } 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; }