/*******************************************************************************
* 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;
osimulateCycleStop = 0;
ocycleNumberNext = 0;
oprocessingCycle = 0;
oserverName = "localhost";
ocycleTick = 0;
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(config.findValue("SimulateCycleStop:").retInt() == 1)
osimulateCycleStop = 1;
else
osimulateCycleStop = 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;
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, "%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;
osimulateCycleStop = pupeConfig.internalCycleStop;
opupeEnginesLock.unlock();
return err;
}
void Control::run(){
oboapServer.run();
}
void Control::timer(){
static uint32_t tick = 0;
static int simEnable = 0;
BIter i;
if(osimulateTiming || osimulateCycleStop){
if(simEnable){
if(osimulate){
opupeEnginesLock.rdLock();
if(ocycleTick == 0){
printf("Control::timer: cycleStart\n");
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();
if(++ocycleTick == 12)
ocycleTick = 0;
}
else if(osimulateCycleStop){
opupeEnginesLock.rdLock();
if(ocycleTick == 10){
if(opupeEngines.number())
opupeEngines.front()->fpgaTriggerTimingInputs(TimingSigCycleStop);
}
opupeEnginesLock.unlock();
++ocycleTick;
}
else {
opupeEnginesLock.rdLock();
if(ocycleTick == 0){
if(opupeEngines.number())
opupeEngines.front()->fpgaTriggerTimingInputs(TimingSigCycleStart);
}
else if(ocycleTick == 4){
if(opupeEngines.number())
opupeEngines.front()->fpgaTriggerTimingInputs(TimingSigInjection);
}
#ifndef ZAP
else if(ocycleTick == 6){
if(opupeEngines.number())
opupeEngines.front()->fpgaTriggerTimingInputs(TimingSigHChange);
}
#endif
else if(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");
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(osimulateCycleStop){
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(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;
}