diff --git a/Common/Libraries/IRALibrary/include/IRATools.h b/Common/Libraries/IRALibrary/include/IRATools.h index 33a03b6870363c129ac8f9b5c2083cff736b27e9..3525548d7ed8f08ad0703acf0d87dbd80c511c75 100644 --- a/Common/Libraries/IRALibrary/include/IRATools.h +++ b/Common/Libraries/IRALibrary/include/IRATools.h @@ -354,10 +354,23 @@ public: * @param start position inside the string from which to start the next token, if a new token has been found * it points to the character immediately after the localized token * @param delimiter this is the character that separates the token - * @param it returns the next token in the string + * @param ret it returns the next token in the string + * @param stopAtFirst immediately returns (true) if the first character found is one of the delimiters, otherwise it goes further inside str * @return true if a token has been found */ - static bool getNextToken(const IRA::CString& str,int &start,char delimiter,IRA::CString &ret); + static bool getNextToken(const IRA::CString& str,int &start,const char& delimiter,IRA::CString &ret, const bool& stopAtFirst=true); + + /** + * Use this function to divide a string into separated tokens. Multiple token delimiters can be specified. + * @param str string to be divided into tokens + * @param start position inside the string from which to start the next token, if a new token has been found + * it points to the character immediately after the localized token + * @param delimiters vector that contains characters that separate the token + * @param ret it returns the next token in the string + * @param stopAtFirst immediately returns (true) if the first character found is one of the delimiters, otherwise it goes further inside str + * @return true if a token has been found + */ + static bool getNextToken(const IRA::CString& str,int &start,const std::vector<char>& delimiters,IRA::CString &ret, const bool& stopAtFirst=true); /** * Computes the normalized (+/-PI) difference between two angles expressed in radians(a-b). For example 359°-1°=-2°,1°-359°=2°, 179°-360°=179° and so on. diff --git a/Common/Libraries/IRALibrary/src/IRATools.cpp b/Common/Libraries/IRALibrary/src/IRATools.cpp index ab893ab5157988309410c616d0dfd6f2c70d73f3..ede299471e4977e0de4cf6126a96e68a4a5f8f33 100644 --- a/Common/Libraries/IRALibrary/src/IRATools.cpp +++ b/Common/Libraries/IRALibrary/src/IRATools.cpp @@ -40,13 +40,13 @@ void CIRATools::Wait(long long micro) DDWORD CIRATools::timeMicroSeconds(TIMEDIFFERENCE& time) { DDWORD tmp; - tmp=((((time.day()*24+time.hour())*60LL+time.minute())*60+time.second())*1000000+time.microSecond()); + tmp=((((time.day()*24+time.hour())*60LL+time.minute())*60+time.second())*1000000+time.microSecond()); return tmp; } void CIRATools::getTime(TIMEVALUE& Now) { - Now.value(TimeUtil::ace2epoch(ACE_OS::gettimeofday())); + Now.value(TimeUtil::ace2epoch(ACE_OS::gettimeofday())); } ACS::Time CIRATools::getACSTime() @@ -89,7 +89,7 @@ DDWORD CIRATools::timeDifference(TIMEVALUE& First,TIMEVALUE& Last) diff=Last.difference(First.value()); } DurationHelper diffHelper(diff); - return timeMicroSeconds(diffHelper); + return timeMicroSeconds(diffHelper); } long long CIRATools::timeSubtract(const TIMEVALUE& First,const TIMEVALUE& Second) @@ -114,7 +114,7 @@ bool CIRATools::getDBValue(maci::ContainerServices *Services,CString fieldName,d try { CDB::DAL_var Dal_p=Services->getCDB(); CDB::DAO_var Dao_p=Dal_p->get_DAO_Servant((const char *)fullName); - Val=Dao_p->get_double((const char *)fieldName); + Val=Dao_p->get_double((const char *)fieldName); } catch (...) { return false; @@ -130,7 +130,7 @@ bool CIRATools::getDBValue(maci::SimpleClient *client,CString fieldName,double & CORBA::Object_var obj=client->getComponent("CDB",0,false); CDB::DAL_var Dal_p=CDB::DAL::_narrow(obj); CDB::DAO_var Dao_p=Dal_p->get_DAO_Servant((const char *)fullName); - Val=Dao_p->get_double((const char *)fieldName); + Val=Dao_p->get_double((const char *)fieldName); } catch (...) { return false; @@ -146,7 +146,7 @@ bool CIRATools::getDBValue(maci::ContainerServices *Services,CString fieldName,l try { CDB::DAL_var Dal_p=Services->getCDB(); CDB::DAO_var Dao_p=Dal_p->get_DAO_Servant((const char *)fullName); - Val=Dao_p->get_long((const char *)fieldName); + Val=Dao_p->get_long((const char *)fieldName); } catch (...) { return false; @@ -162,7 +162,7 @@ bool CIRATools::getDBValue(maci::SimpleClient *client,CString fieldName,long &Va CORBA::Object_var obj=client->getComponent("CDB",0,false); CDB::DAL_var Dal_p=CDB::DAL::_narrow(obj); CDB::DAO_var Dao_p=Dal_p->get_DAO_Servant((const char *)fullName); - Val=Dao_p->get_long((const char *)fieldName); + Val=Dao_p->get_long((const char *)fieldName); } catch (...) { return false; @@ -179,13 +179,13 @@ bool CIRATools::getDBValue(maci::ContainerServices *Services,CString fieldName,D try { CDB::DAL_var Dal_p=Services->getCDB(); CDB::DAO_var Dao_p=Dal_p->get_DAO_Servant((const char *)fullName); - app=Dao_p->get_long((const char *)fieldName); + app=Dao_p->get_long((const char *)fieldName); } catch (...) { return false; } Val=(DWORD)app; - return true; + return true; } bool CIRATools::getDBValue(maci::SimpleClient *client,CString fieldName,DWORD &Val,CString Domain,CString name) @@ -197,13 +197,13 @@ bool CIRATools::getDBValue(maci::SimpleClient *client,CString fieldName,DWORD &V CORBA::Object_var obj=client->getComponent("CDB",0,false); CDB::DAL_var Dal_p=CDB::DAL::_narrow(obj); CDB::DAO_var Dao_p=Dal_p->get_DAO_Servant((const char *)fullName); - app=Dao_p->get_long((const char *)fieldName); + app=Dao_p->get_long((const char *)fieldName); } catch (...) { return false; } Val=(DWORD)app; - return true; + return true; } bool CIRATools::getDBValue(maci::ContainerServices *Services,CString fieldName,CString &Val,CString Domain,CString name) @@ -227,7 +227,7 @@ bool CIRATools::getDBValue(maci::SimpleClient *client,CString fieldName,CString { CString fullName; fullName=Domain+name; - try { + try { CORBA::Object_var obj=client->getComponent("CDB",0,false); CDB::DAL_var Dal_p=CDB::DAL::_narrow(obj); CDB::DAO_var Dao_p=Dal_p->get_DAO_Servant((const char *)fullName); @@ -249,7 +249,7 @@ bool CIRATools::setDBValue(maci::ContainerServices* Services,CString fieldName,c CDB::DAL_var Dal_p=Services->getCDB(); CDB::WDAL_var wDal_p=CDB::WDAL::_narrow(Dal_p); CDB::WDAO_var wDao_p=wDal_p->get_WDAO_Servant((const char *)fullName); - wDao_p->set_double((const char *)fieldName,Val); + wDao_p->set_double((const char *)fieldName,Val); } catch (...) { return false; @@ -266,7 +266,7 @@ bool CIRATools::setDBValue(maci::ContainerServices* Services,CString fieldName,c CDB::DAL_var Dal_p=Services->getCDB(); CDB::WDAL_var wDal_p=CDB::WDAL::_narrow(Dal_p); CDB::WDAO_var wDao_p=wDal_p->get_WDAO_Servant((const char *)fullName); - wDao_p->set_long((const char *)fieldName,Val); + wDao_p->set_long((const char *)fieldName,Val); } catch (...) { return false; @@ -283,12 +283,12 @@ bool CIRATools::setDBValue(maci::ContainerServices* Services,CString fieldName,c CDB::DAL_var Dal_p=Services->getCDB(); CDB::WDAL_var wDal_p=CDB::WDAL::_narrow(Dal_p); CDB::WDAO_var wDao_p=wDal_p->get_WDAO_Servant((const char *)fullName); - wDao_p->set_long((const char *)fieldName,Val); + wDao_p->set_long((const char *)fieldName,Val); } catch (...) { return false; } - return true; + return true; } bool CIRATools::setDBValue(maci::ContainerServices* Services,CString fieldName,const CString &Val,CString Domain,CString name) @@ -300,7 +300,7 @@ bool CIRATools::setDBValue(maci::ContainerServices* Services,CString fieldName,c CDB::DAL_var Dal_p=Services->getCDB(); CDB::WDAL_var wDal_p=CDB::WDAL::_narrow(Dal_p); CDB::WDAO_var wDao_p=wDal_p->get_WDAO_Servant((const char *)fullName); - wDao_p->set_string((const char *)fieldName,(const char*)Val); + wDao_p->set_string((const char *)fieldName,(const char*)Val); } catch (...) { return false; @@ -340,11 +340,11 @@ double CIRATools::getHWAzimuth(const double& current,const double& dest,const do } if (d1<=d2) { newOne=tmpCurrent+d1; - if ((newOne>tmpMaxAz) || (newOne<tmpMinAz)) newOne=tmpCurrent-d2; + if ((newOne>tmpMaxAz) || (newOne<tmpMinAz)) newOne=tmpCurrent-d2; } else { newOne=tmpCurrent-d2; - if ((newOne>tmpMaxAz) || (newOne<tmpMinAz)) newOne=tmpCurrent+d1; + if ((newOne>tmpMaxAz) || (newOne<tmpMinAz)) newOne=tmpCurrent+d1; } } else if (section>0) { //CW (typically 180...450 @@ -369,7 +369,7 @@ double CIRATools::getHWAzimuth(const double& current,const double& dest,const do } else { // not reachable in this sector (typically 180..270 newOne=tmpDest; - } + } } return newOne; } @@ -397,7 +397,12 @@ bool CIRATools::skyFrequency(const double& bf,const double& bbw,const double& rf }*/ } -bool CIRATools::getNextToken(const IRA::CString& str,int &start,char delimiter,IRA::CString &ret) +bool CIRATools::getNextToken(const IRA::CString& str,int &start,const char& delimiter,IRA::CString &ret, const bool& stopAtFirst) +{ + return getNextToken(str, start, std::vector<char>{ delimiter }, ret, stopAtFirst); +} + +bool CIRATools::getNextToken(const IRA::CString& str,int &start,const std::vector<char>& delimiters,IRA::CString &ret, const bool& stopAtFirst) { int i; bool ok=false; @@ -406,22 +411,24 @@ bool CIRATools::getNextToken(const IRA::CString& str,int &start,char delimiter,I token=new char[i+1]; i=0; while (str[start]!=0) { + char c = str[start]; ok=true; - if ((str[start]==delimiter)) { - start++; + start++; + if (std::any_of(delimiters.begin(), delimiters.end(), [c](char d){ return c == d; })) { + // We still have not found any meaningful character, stop if stopAtFirst or continue skipping a delimiter + if(!stopAtFirst && i==0) + continue; break; } else { - token[i]=str[start]; - start++; + token[i]=c; i++; } } token[i]=0; ret=IRA::CString(token); delete []token; - if (!ok) return false; - else return true; + return ok; } double CIRATools::differenceBetweenAnglesRad(const double& a,const double& b) @@ -504,7 +511,7 @@ bool CIRATools::strToInterval(const IRA::CString& durationString,ACS::TimeInterv else { hour=0; minute=0; - second=timeToken[0].ToDouble(); + second=timeToken[0].ToDouble(); } } else if (dCounter==2) { @@ -522,7 +529,7 @@ bool CIRATools::strToInterval(const IRA::CString& durationString,ACS::TimeInterv else if (dCounter==3) { hour=timeToken[0].ToLong(); minute=timeToken[1].ToLong(); - second=timeToken[2].ToDouble(); + second=timeToken[2].ToDouble(); } if ((hour<0) || (hour>23)) return false; if ((minute<0) || (minute>59)) return false; @@ -541,7 +548,7 @@ bool CIRATools::strToInterval(const IRA::CString& durationString,ACS::TimeInterv usec=(long)(second*1000000.0); period.hour(hour); period.minute(minute); period.second(sec); period.microSecond(usec); interval=period.value().value; - return true; + return true; } bool CIRATools::intervalToStr(const ACS::TimeInterval& interval,IRA::CString& outString,char dateDelimiter,char timeDelimiter) @@ -565,7 +572,7 @@ bool CIRATools::strToTime(const IRA::CString& timeString,ACS::Time& time,bool co IRA::CIRATools::getTime(now); int p,i=0,dCounter=0; IRA::CDateTime dt; - + while (((p=timeString.Find(dateDelimiter,i))>0) && (dCounter<2)) { dateToken[dCounter]=timeString.Mid(i,p-i); dCounter++; @@ -582,25 +589,25 @@ bool CIRATools::strToTime(const IRA::CString& timeString,ACS::Time& time,bool co } else { year=dateToken[0].ToLong(); - doy=dateToken[1].ToLong(); + doy=dateToken[1].ToLong(); } if (getNextToken(timeString,i,timeDelimiter,token)) { //now read all three fields of the time representation hour=token.ToLong(); if (getNextToken(timeString,i,timeDelimiter,token)) { minute=token.ToLong(); - if (getNextToken(timeString,i,timeDelimiter,token)) { + if (getNextToken(timeString,i,timeDelimiter,token)) { second=token.ToDouble(); } else { if (complete) return false; second=0.0; - } + } } else { if (complete) return false; minute=0; second=0.0; - } + } } else { return false; @@ -631,7 +638,7 @@ bool CIRATools::timeToStr(const ACS::Time& time,IRA::CString& outString,char dat TIMEVALUE timeE(time); outString.Format("%04lu%c%03ld%c%02ld%c%02ld%c%02ld.%03ld",(unsigned long)timeE.year(),dateDelimiter,(long)timeE.dayOfYear(),dateDelimiter,(long)timeE.hour(), timeDelimiter,(long)timeE.minute(),timeDelimiter,(long)timeE.second(),(long)timeE.microSecond()/1000); - return true; + return true; } bool CIRATools::timeToStrExtended(const ACS::Time& time,IRA::CString& outString,char dateDelimiter,char timeDelimiter) @@ -676,7 +683,7 @@ bool CIRATools::longSeqToStr(const ACS::longSeq& val,IRA::CString& outString,cha bool CIRATools::hourAngleToRad(const IRA::CString& angle,double& rad,bool complete,char delimiter) { - int p,i=0,dCounter=0; + int p,i=0,dCounter=0; long hour=0,minute=0; double second=0.0; bool sign; @@ -704,7 +711,7 @@ bool CIRATools::hourAngleToRad(const IRA::CString& angle,double& rad,bool comple else if (dCounter==3) { hour=token[0].ToLong(); minute=token[1].ToLong(); - second=token[2].ToDouble(); + second=token[2].ToDouble(); } if (token[0][0]=='-') { sign=true; @@ -720,8 +727,8 @@ bool CIRATools::hourAngleToRad(const IRA::CString& angle,double& rad,bool comple rad=slaDranrm(rad); if (sign) { rad*=-1; - } - return true; + } + return true; } bool CIRATools::radToHourAngle(const double& rad,IRA::CString& outString,char delimiter) @@ -741,7 +748,7 @@ bool CIRATools::radToHourAngle(const double& rad,IRA::CString& outString,char de bool CIRATools::sexagesimalAngleToRad(const IRA::CString& angle,double& rad,bool complete,char delimiter) { - int p,i=0,dCounter=0; + int p,i=0,dCounter=0; long deg=0,arcminute=0; double arcsecond=0.0; bool sign; @@ -769,14 +776,14 @@ bool CIRATools::sexagesimalAngleToRad(const IRA::CString& angle,double& rad,bool else if (dCounter==3) { deg=token[0].ToLong(); arcminute=token[1].ToLong(); - arcsecond=token[2].ToDouble(); + arcsecond=token[2].ToDouble(); } //degrees could also be negative...... if (token[0][0]=='-') { sign=true; deg*=-1; } - else { + else { sign=false; } if ((arcminute<0) || (arcminute>59)) return false; @@ -786,16 +793,16 @@ bool CIRATools::sexagesimalAngleToRad(const IRA::CString& angle,double& rad,bool if (sign) { rad*=-1; } - return true; + return true; } bool CIRATools::radToSexagesimalAngle(const double& rad,IRA::CString& outString,char delimiter) { char sign; - int dmsf[4]; + int dmsf[4]; double ranged=dmod(rad,D2PI); // put the angle in the range -2PI..2PI // convert it into hour minute second anf fraction..keeping track of the sign - slaDr2af(3,ranged,&sign,dmsf); + slaDr2af(3,ranged,&sign,dmsf); if (sign=='+') { outString.Format("%02d%c%02d%c%02d.%03d",dmsf[0],delimiter,dmsf[1],delimiter,dmsf[2],dmsf[3]); } @@ -810,7 +817,7 @@ bool CIRATools::angleToRad(const IRA::CString& angle,double& rad) double deg=angle.ToDouble(); rad=deg*DD2R; rad=dmod(rad,D2PI); - return true; + return true; } bool CIRATools::radToAngle(const double& rad,IRA::CString& outString) @@ -866,7 +873,7 @@ bool CIRATools::latitudeToRad(const IRA::CString& lat,double& rad,bool complete, bool CIRATools::rightAscensionToRad(const IRA::CString& ra,double& rad,bool complete,char delimiter) { //long len=ra.GetLength(); - int len=ra.GetLength(); + int len=ra.GetLength(); bool res; if (len==0) return false; if (ra[len-1]=='d') { @@ -926,7 +933,7 @@ bool CIRATools::azimuthToRad(const IRA::CString& az,double& rad,bool complete) } else return res; } - + bool CIRATools::elevationToRad(const IRA::CString& el,double& rad,bool complete) { bool res=angleToRad(el,rad); @@ -1140,13 +1147,13 @@ bool CIRATools::makeDirectory(const IRA::CString& pathName) bool CIRATools::directoryExists(const IRA::CString& path) { DIR *dir; - bool exists=false; - dir=opendir((const char *)path); - if (dir!=NULL) { - exists=true; - closedir(dir); - } - return exists; + bool exists=false; + dir=opendir((const char *)path); + if (dir!=NULL) { + exists=true; + closedir(dir); + } + return exists; } bool CIRATools::fileExists(const IRA::CString& file) @@ -1178,27 +1185,27 @@ bool CIRATools::deleteFile(const IRA::CString& file) bool CIRATools::copyFile(const IRA::CString& src,const IRA::CString& dst) { - std::ifstream in ((const char *)src); - if (in.fail()) return false; - std::ofstream out ((const char *)dst); - if (out.fail()) return false; - out << in.rdbuf(); - out.close(); - in.close(); - return true; + std::ifstream in ((const char *)src); + if (in.fail()) return false; + std::ofstream out ((const char *)dst); + if (out.fail()) return false; + out << in.rdbuf(); + out.close(); + in.close(); + return true; } bool CIRATools::extractFileName(const IRA::CString& fullPath,IRA::CString& baseDir,IRA::CString& baseName, IRA::CString& extension) { - char *dirc, *basec; - dirc=strdup((const char *)fullPath); // the APIs called below might change the content of the input string - basec=strdup((const char *)fullPath); - baseDir=IRA::CString(dirname(dirc)); - baseName=IRA::CString(basename(basec)); - if ((baseDir.GetLength()==0) || (baseName.GetLength()==0)) { - return false; - } + char *dirc, *basec; + dirc=strdup((const char *)fullPath); // the APIs called below might change the content of the input string + basec=strdup((const char *)fullPath); + baseDir=IRA::CString(dirname(dirc)); + baseName=IRA::CString(basename(basec)); + if ((baseDir.GetLength()==0) || (baseName.GetLength()==0)) { + return false; + } int pos=baseName.Find('.'); if (pos<0) { // not found extension=""; @@ -1297,9 +1304,9 @@ bool CIRATools::matchRegExp(const IRA::CString& input,const IRA::CString& expr,s std::string out(what[i].first,what[i].second); res.push_back(out.c_str()); } - start=what[0].second; - //flags|=boost::match_prev_avail; - //flags|=boost::match_not_bob; + start=what[0].second; + //flags|=boost::match_prev_avail; + //flags|=boost::match_not_bob; } return true; } diff --git a/Common/Servers/Scheduler/include/Schedule.h b/Common/Servers/Scheduler/include/Schedule.h index 6aba618e94dc75fda40d7fc66a720c3e59742c66..1badc85c13d1b0d5244d8b39158837c72703e80c 100644 --- a/Common/Servers/Scheduler/include/Schedule.h +++ b/Common/Servers/Scheduler/include/Schedule.h @@ -24,7 +24,7 @@ #include "Configuration.h" #define _SCHED_NULLTARGET "NULL" -#define SEPARATOR '\t' +#define SEPARATORS std::vector<char>{'\t', ' '} #define MAX_SCHED_NAME_LEN 37 /** diff --git a/Common/Servers/Scheduler/src/Makefile b/Common/Servers/Scheduler/src/Makefile index d1639108f2a37a4db5d4ee06a1419da42fccecf3..706a0f297808150385a7f95b2757476e0970b96c 100644 --- a/Common/Servers/Scheduler/src/Makefile +++ b/Common/Servers/Scheduler/src/Makefile @@ -38,13 +38,16 @@ RESTRICT_PERMS=_tp_agc # C programs (public and local) # ----------------------------- EXECUTABLES = scheduleChecker -EXECUTABLES_L = testSched testLSTtoUT +EXECUTABLES_L = testSched testSchedules testLSTtoUT # # <brief description of xxxxx program> testSched_OBJECTS = testSchedule Schedule SubScanBinder Schedule_ScanList Configuration testSched_LIBS = IRALibrary ComponentErrors +testSchedules_OBJECTS = testSchedules Schedule SubScanBinder Schedule_ScanList Configuration +testSchedules_LIBS = IRALibrary ComponentErrors boost_system boost_filesystem + testLSTtoUT_OBJECTS = testLST2UT testLSTtoUT_LIBS = IRALibrary ComponentErrors diff --git a/Common/Servers/Scheduler/src/Schedule.cpp b/Common/Servers/Scheduler/src/Schedule.cpp index 60c1c9775da72aa88d908104bc91a6be06c66823..3d315dd025f836f331c4c9d1b1195f317b7336f7 100644 --- a/Common/Servers/Scheduler/src/Schedule.cpp +++ b/Common/Servers/Scheduler/src/Schedule.cpp @@ -920,48 +920,48 @@ bool CSchedule::parseLine(const IRA::CString& line,const DWORD& lnNumber,IRA::CS { int start=0; IRA::CString ret; - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,ret)) { + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,ret,false)) { return false; } else { // token could be extracted ret.MakeUpper(); if (ret==PROJECT) { - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,m_projectName)) { + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,m_projectName,false)) { errorMsg="cannot parse project name"; return false; } else return true; } else if (ret==OBSERVER) { - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,m_observer)) { + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,m_observer,false)) { errorMsg="cannot parse observer name"; return false; } else return true; } else if (ret==SCANLIST) { - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,m_scanList)) { + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,m_scanList,false)) { errorMsg="cannot parse scan list file name"; return false; } else return true; } else if (ret==PROCLIST) { - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,m_configList)) { + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,m_configList,false)) { errorMsg="cannot parse procedure list file name"; return false; } else return true; } else if (ret==BACKENDLIST) { - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,m_backendList)) { + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,m_backendList,false)) { errorMsg="cannot parse backend configurations file name"; return false; } else return true; } else if (ret==SCANLAYOUT) { - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,m_layoutFile)) { + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,m_layoutFile,false)) { errorMsg="cannot parse scan layouts file name"; return false; } @@ -969,14 +969,14 @@ bool CSchedule::parseLine(const IRA::CString& line,const DWORD& lnNumber,IRA::CS } else if (ret==MODE) { IRA::CString mode,rep,startTime; - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,mode)) { + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,mode,false)) { errorMsg="cannot parse schedule mode"; return false; } else { m_modeDone=true; if (mode==LSTMODE) { - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,rep)) { + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,rep,false)) { errorMsg="cannot parse repetitions number for LST mode"; return false; } @@ -989,7 +989,7 @@ bool CSchedule::parseLine(const IRA::CString& line,const DWORD& lnNumber,IRA::CS } } else if (mode==SEQMODE) { - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,startTime)) { + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,startTime,false)) { m_mode=SEQ; //if it has not an extra argument the sequential is pure return true; } @@ -1011,11 +1011,11 @@ bool CSchedule::parseLine(const IRA::CString& line,const DWORD& lnNumber,IRA::CS } else if (ret==ELEVATIONLIMITS) { IRA::CString minEl,maxEl; - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,minEl)) { + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,minEl,false)) { errorMsg="elevation lower limit missing or not correct"; return false; } - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,maxEl)) { + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,maxEl,false)) { errorMsg="elevation upper limit missing or not correct"; return false; } @@ -1039,7 +1039,7 @@ bool CSchedule::parseLine(const IRA::CString& line,const DWORD& lnNumber,IRA::CS } else if (ret==SCANTAG) { IRA::CString scanTag; - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,scanTag)) { + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,scanTag,false)) { errorMsg="cannot parse scan tag"; return false; } @@ -1049,7 +1049,7 @@ bool CSchedule::parseLine(const IRA::CString& line,const DWORD& lnNumber,IRA::CS return true; } else if (ret==INITPROC) { - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,m_initProc)) { + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,m_initProc,false)) { errorMsg="cannot parse schedule initialization procedure"; return false; } @@ -1080,7 +1080,7 @@ bool CSchedule::parseScans(const IRA::CString& line,const DWORD& lnNumber,IRA::C //ACS::Time ut; TRecord *p; start=0; - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,ret)) { + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,ret,false)) { errorMsg="schedule format error"; return false; } @@ -1088,7 +1088,7 @@ bool CSchedule::parseScans(const IRA::CString& line,const DWORD& lnNumber,IRA::C ret.MakeUpper(); if (ret==SCAN_START) { //process scan definition m_currentScanDef.valid=false; - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,ret)) { // scan id + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,ret,false)) { // scan id errorMsg="scan identifier cannot be found"; return false; } @@ -1104,14 +1104,14 @@ bool CSchedule::parseScans(const IRA::CString& line,const DWORD& lnNumber,IRA::C } m_currentScanDef.id=tempId; } - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,ret)) { //suffix + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,ret,false)) { //suffix errorMsg="scan suffix cannot be found"; return false; } else { m_currentScanDef.suffix=ret; } - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,ret)) { //backend:datawriter + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,ret,false)) { //backend:datawriter errorMsg="backend/datawriter cannot be found"; return false; } @@ -1131,7 +1131,7 @@ bool CSchedule::parseScans(const IRA::CString& line,const DWORD& lnNumber,IRA::C m_currentScanDef.writerInstance=_SCHED_NULLTARGET; } } - if (IRA::CIRATools::getNextToken(line,start,SEPARATOR,ret)) { //layout...not mandatory + if (IRA::CIRATools::getNextToken(line,start,SEPARATORS,ret,false)) { //layout...not mandatory if (m_layoutFile=="") { // if the layout file has not been given errorMsg="scan layout without providing the scan layouts file"; return false; diff --git a/Common/Servers/Scheduler/src/ScheduleChecker.cpp b/Common/Servers/Scheduler/src/ScheduleChecker.cpp index 928c03e6fcdb5f42ce4f142a2da91f63d370d135..5232866e4bb169fa8e129114b35097c0459bf3a7 100644 --- a/Common/Servers/Scheduler/src/ScheduleChecker.cpp +++ b/Common/Servers/Scheduler/src/ScheduleChecker.cpp @@ -3,6 +3,7 @@ #include "Schedule.h" #include <getopt.h> #include <libgen.h> +#include <pwd.h> using namespace IRA; @@ -80,6 +81,13 @@ int main(int argc, char *argv[]) exit(-1); } if (sched.isComplete()) { + if(getpwuid(getuid())->pw_name != sched.getProjectName()) + { + CString err; + err.Format("Error found: Schedule PROJECT keyword does not match with active project!\n"); + printMessage((const char*)err); + exit(-1); + } CString msg; msg.Format("%u subscans were successfully parsed!\n",sched.getSubScansNumber()); printMessage((const char *)msg); diff --git a/Common/Servers/Scheduler/src/Schedule_ScanList.cpp b/Common/Servers/Scheduler/src/Schedule_ScanList.cpp index c03b2c1b091a2bf7487da6f9535d56fff80cd077..7f538e62ce60cd703734352cbfd16b5ef864a538 100644 --- a/Common/Servers/Scheduler/src/Schedule_ScanList.cpp +++ b/Common/Servers/Scheduler/src/Schedule_ScanList.cpp @@ -144,12 +144,12 @@ bool CScanList::parseLine(const IRA::CString& line,const DWORD& lnNumber,IRA::CS IRA::CString ret; Management::TScanTypes type; // get the second item..... - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,ret)) { + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,ret,false)) { errMsg="format error"; return false; } //...the scan type - if (!IRA::CIRATools::getNextToken(line,start,SEPARATOR,ret)) { + if (!IRA::CIRATools::getNextToken(line,start,SEPARATORS,ret,false)) { errMsg="could not read scan type"; return false; } @@ -592,7 +592,7 @@ bool CScanList::parseSidereal2(const IRA::CString& val,DWORD& id,IRA::CString& e Antenna::TSystemEquinox scanEquinox; // get the second item..... - if (!IRA::CIRATools::getNextToken(val,start,SEPARATOR,token)) { // id + if (!IRA::CIRATools::getNextToken(val,start,SEPARATORS,token,false)) { // id errMsg="cannot read scan identifier"; return false; } @@ -601,12 +601,12 @@ bool CScanList::parseSidereal2(const IRA::CString& val,DWORD& id,IRA::CString& e errMsg="scan identifier cannot be zero"; return false; } - if (!IRA::CIRATools::getNextToken(val,start,SEPARATOR,token)) { // type + if (!IRA::CIRATools::getNextToken(val,start,SEPARATORS,token,false)) { // type errMsg="cannot read scan type"; return false; } //scan->type=Antenna::ANT_SIDEREAL; //already know it is a sidereal - if (!IRA::CIRATools::getNextToken(val,start,SEPARATOR,token)) { // name + if (!IRA::CIRATools::getNextToken(val,start,SEPARATORS,token,false)) { // name errMsg="cannot read source name"; return false; } @@ -626,7 +626,7 @@ bool CScanList::parseSidereal2(const IRA::CString& val,DWORD& id,IRA::CString& e //scan->VradDefinition=Antenna::ANT_UNDEF_DEF; //scan->RadialVelocity=0.0; frame=Antenna::ANT_EQUATORIAL; - while (IRA::CIRATools::getNextToken(val,start,SEPARATOR,token)) { //get the next token...it represents the frame in which the coordinates are expressed + while (IRA::CIRATools::getNextToken(val,start,SEPARATORS,token,false)) { //get the next token...it represents the frame in which the coordinates are expressed bool ok=IRA::CIRATools::strToCoordinateFrame(token,frame); if ((frame==Antenna::ANT_EQUATORIAL) && (ok)) { if (frameOpen || offFrameOpen) { @@ -899,7 +899,7 @@ bool CScanList::parseSidereal2(const IRA::CString& val,DWORD& id,IRA::CString& e IRA::CString token; Antenna::TCoordinateFrame frame; // get the second item..... - if (!IRA::CIRATools::getNextToken(val,start,SEPARATOR,token)) { // id + if (!IRA::CIRATools::getNextToken(val,start,SEPARATORS,token,false)) { // id errMsg="cannot read scan identifier"; return false; } @@ -908,12 +908,12 @@ bool CScanList::parseSidereal2(const IRA::CString& val,DWORD& id,IRA::CString& e errMsg="scan identifier cannot be zero"; return false; } - if (!IRA::CIRATools::getNextToken(val,start,SEPARATOR,token)) { // type + if (!IRA::CIRATools::getNextToken(val,start,SEPARATORS,token,false)) { // type errMsg="cannot read scan type"; return false; } scan->type=Antenna::ANT_SIDEREAL; //already know it is a sidereal - if (!IRA::CIRATools::getNextToken(val,start,SEPARATOR,token)) { // name + if (!IRA::CIRATools::getNextToken(val,start,SEPARATORS,token,false)) { // name errMsg="cannot read source name"; return false; } @@ -932,7 +932,7 @@ bool CScanList::parseSidereal2(const IRA::CString& val,DWORD& id,IRA::CString& e scan->VradDefinition=Antenna::ANT_UNDEF_DEF; scan->RadialVelocity=0.0; frame=Antenna::ANT_EQUATORIAL; - while (IRA::CIRATools::getNextToken(val,start,SEPARATOR,token)) { //get the next token...it represents the frame in which the coordinates are expressed + while (IRA::CIRATools::getNextToken(val,start,SEPARATORS,token,false)) { //get the next token...it represents the frame in which the coordinates are expressed bool ok=IRA::CIRATools::strToCoordinateFrame(token,frame); if ((frame==Antenna::ANT_EQUATORIAL) && (ok)) { if (frameOpen || offFrameOpen) { @@ -1186,14 +1186,14 @@ bool CScanList::parseOffsetSwitch(const IRA::CString& val,int& start, IRA::CString lontoken,lattoken,token; errMsg=""; res=false; - while (IRA::CIRATools::getNextToken(val,start,SEPARATOR,token)) { + while (IRA::CIRATools::getNextToken(val,start,SEPARATORS,token,false)) { if (strcmp(token,OFFFRAMEEQ)==0) { offsetFrame=Antenna::ANT_EQUATORIAL; - if (!IRA::CIRATools::getNextToken(val,start,SEPARATOR,lontoken)) { + if (!IRA::CIRATools::getNextToken(val,start,SEPARATORS,lontoken,false)) { errMsg="not enough parameters for equatorial offset switch"; return false; } - if (!IRA::CIRATools::getNextToken(val,start,SEPARATOR,lattoken)) { + if (!IRA::CIRATools::getNextToken(val,start,SEPARATORS,lattoken,false)) { errMsg="not enough parameters for equatorial offset switch"; return false; } @@ -1210,11 +1210,11 @@ bool CScanList::parseOffsetSwitch(const IRA::CString& val,int& start, } else if (strcmp(token,OFFFRAMEHOR)==0) { offsetFrame=Antenna::ANT_HORIZONTAL; - if (!IRA::CIRATools::getNextToken(val,start,SEPARATOR,lontoken)) { + if (!IRA::CIRATools::getNextToken(val,start,SEPARATORS,lontoken,false)) { errMsg="not enough parameters for horizontal offset switch"; return false; } - if (!IRA::CIRATools::getNextToken(val,start,SEPARATOR,lattoken)) { + if (!IRA::CIRATools::getNextToken(val,start,SEPARATORS,lattoken,false)) { errMsg="not enough parameters for horizontal offset switch"; return false; } @@ -1231,11 +1231,11 @@ bool CScanList::parseOffsetSwitch(const IRA::CString& val,int& start, } else if (strcmp(token,OFFFRAMEGAL)==0) { offsetFrame=Antenna::ANT_GALACTIC; - if (!IRA::CIRATools::getNextToken(val,start,SEPARATOR,lontoken)) { + if (!IRA::CIRATools::getNextToken(val,start,SEPARATORS,lontoken,false)) { errMsg="not enough parameters for galactic offset switch"; return false; } - if (!IRA::CIRATools::getNextToken(val,start,SEPARATOR,lattoken)) { + if (!IRA::CIRATools::getNextToken(val,start,SEPARATORS,lattoken,false)) { errMsg="not enough parameters for galactic offset switch"; return false; } @@ -1260,7 +1260,7 @@ bool CScanList::parseVRADSwitch(const IRA::CString& val,int& start,double& vrad, IRA::CString token; errMsg=""; result=false; - while (IRA::CIRATools::getNextToken(val,start,SEPARATOR,token)) { + while (IRA::CIRATools::getNextToken(val,start,SEPARATORS,token,false)) { if (strcmp(token,RVEL)==0) { if (parseVRADSwitch(val,start,vrad,frame,ref,errMsg)) { result=true; @@ -1277,14 +1277,14 @@ bool CScanList::parseVRADSwitch(const IRA::CString& val,int& start,double& vrad, bool CScanList::parseVRADSwitch(const IRA::CString& val,int& start,double& vrad,Antenna::TReferenceFrame& frame,Antenna::TVradDefinition& ref,IRA::CString& errMsg) { IRA::CString token; - if (IRA::CIRATools::getNextToken(val,start,SEPARATOR,token)) { + if (IRA::CIRATools::getNextToken(val,start,SEPARATORS,token,false)) { vrad=token.ToDouble(); } else { errMsg="not enough parameters for the radial velocity switch"; return false; } - if (IRA::CIRATools::getNextToken(val,start,SEPARATOR,token)) { + if (IRA::CIRATools::getNextToken(val,start,SEPARATORS,token,false)) { if (!Antenna::Definitions::map(token,frame)) { errMsg="the reference frame of the radial velocity is incorrect"; return false; @@ -1294,7 +1294,7 @@ bool CScanList::parseVRADSwitch(const IRA::CString& val,int& start,double& vrad, errMsg="not enough parameters for the radial velocity switch"; return false; } - if (IRA::CIRATools::getNextToken(val,start,SEPARATOR,token)) { + if (IRA::CIRATools::getNextToken(val,start,SEPARATORS,token,false)) { if (!Antenna::Definitions::map(token,ref)) { errMsg="the radial velocity definition is incorrect"; return false; diff --git a/Common/Servers/Scheduler/src/testSchedule.cpp b/Common/Servers/Scheduler/src/testSchedule.cpp index e678e416e017ed32de6e5e3cd07ab318092f8c49..99cda2ee84fab1527d6b755c6713cb7f4f60d5d5 100644 --- a/Common/Servers/Scheduler/src/testSchedule.cpp +++ b/Common/Servers/Scheduler/src/testSchedule.cpp @@ -35,7 +35,7 @@ IRA::CString toDescription(const Antenna::TsubScanDescription& des) IRA::CString toType(const Management::TScanTypes& tp) { - if (tp==Management::MNG_SIDEREAL) return IRA::CString("SIDEREAL"); + if (tp==Management::MNG_SIDEREAL) return IRA::CString("SIDEREAL"); else if (tp==Management::MNG_SUN) return IRA::CString("SUN"); else if (tp==Management::MNG_MOON) return IRA::CString("MOON"); else if (tp==Management::MNG_SATELLITE) return IRA::CString("SATELLITE"); @@ -66,7 +66,10 @@ int main(int argc, char *argv[]) Management::TSubScanConfiguration *subScanConf; CSchedule::TRecord rec; ACS::stringSeq layoutDef; - CSchedule sched("../templates/","schedule.tmpl"); + std::string schedfile = "schedule.tmpl"; + if(argc > 1) + schedfile = std::string(argv[1]); + CSchedule sched("../templates/",schedfile.c_str()); //CSchedule sched("/archive/schedules/maintenance/","mbfitstest-CrossEQEQ.scd"); //CSchedule sched("/archive/schedules/","calibrationSeq.scd"); diff --git a/Common/Servers/Scheduler/src/testSchedules.cpp b/Common/Servers/Scheduler/src/testSchedules.cpp new file mode 100644 index 0000000000000000000000000000000000000000..62e88a391049e273a4cd7b0e8b4c756a938a436c --- /dev/null +++ b/Common/Servers/Scheduler/src/testSchedules.cpp @@ -0,0 +1,38 @@ +#include <iostream> +#include <cstdio> +#include <memory> +#include <stdexcept> +#include <array> +#include <boost/filesystem.hpp> + +std::string exec(const std::string command) +{ + std::array<char, 128> buffer; + std::string result; + + std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(command.c_str(), "r"), pclose); + if(!pipe) + { + throw std::runtime_error("popen() failed!"); + } + while(fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) + { + result += buffer.data(); + } + return result; +} + +int main() +{ + std::string binpath = boost::filesystem::canonical("/proc/self/exe").parent_path().string(); + + std::string output_tab = exec(binpath + "/testSched"); + std::string output_spaces = exec(binpath + "/testSched schedule_ws.tmpl"); + if(output_tab != output_spaces) + { + std::cout << "testSched and testSchedSpaces have different outputs, FAILURE!" << std::endl; + return -1; + } + std::cout << "testSched and testSchedSpaces have the same output, OK!" << std::endl; + return 0; +} diff --git a/Common/Servers/Scheduler/templates/schedule.tmpl b/Common/Servers/Scheduler/templates/schedule.tmpl index 9bb098c800e21c8f8ddab7e293aade4fa5cf6706..5ab342f9615b357c6cd0f54aa4018a6f691bf6b3 100644 --- a/Common/Servers/Scheduler/templates/schedule.tmpl +++ b/Common/Servers/Scheduler/templates/schedule.tmpl @@ -39,7 +39,7 @@ SCANTAG: 1024 INITPROC: INITALL #This is an optional field which allows to control which targets are observed or not based on elevation limits. The two values are degrees and are the lower and the upper limit #respectively. If the keyword is not provided the default values will be kept by the system -ELEVATIONLIMITS 15.0 80.0 +ELEVATIONLIMITS: 15.0 80.0 #The schedule itself, the fields are tab separated diff --git a/Common/Servers/Scheduler/templates/schedule_ws.tmpl b/Common/Servers/Scheduler/templates/schedule_ws.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..bf4a836738315138fd531ee9197b6785bf130c9a --- /dev/null +++ b/Common/Servers/Scheduler/templates/schedule_ws.tmpl @@ -0,0 +1,88 @@ +# A project string,it can be any combination of characters but spaces and blanks, it is not the project code which is used to compose the output +# file name but it can be used to distinguish between various activities during the project, for example A1245-Map and A1245-Calib. It is used +# to add a keyword in the file +PROJECT: skySurvey +# the name of the observer, can be any combination of characters but blanks +OBSERVER: AndreaOrlati +#the file that contains the parameters that defines the scans from the telescope positioning point of view. For +#example sidereal tracking or On the fly scan, and so on...... Every scan is enumerated +SCANLIST: scan.tmpl +#the file that contains the telescopes setups procedures (receiver, local oscillator, ....) that can be used during +#the observation. +PROCEDURELIST: config.tmpl +#file that contains the backend configurations that are required during the observation. This section is not +#mandatory provided that a NULL in the backend field of the schedule is given +BACKENDLIST: backend.tmpl +#file that contains extra information that define the scan to be passed to the the data recorder. This field is not mandatory. +SCANLAYOUT: layoutfile.tmpl +#This defines the schedule mode. +#In this example we have the LST based schedule. That means the various scans are +#performed based on LST mark. The telescope will be driven on the next scan that can be reached on time. If the target is not reachable the scan is skipped. +#The recording will start at exactly at the time mark given for the scan. The stop recording command will be issued at the time which is the result of scheduled start time plus +#the scheduled duration. +#The second parameter is the number of repetitions, it means the times the schedule is repeated. A -1 means +#repeat continuously. A zero or null is not allowed. +MODE: LST 1 +#in this mode the single scans are performed sequentially in the order they appear in the schedule. In that +#mode no timestamps are required. The target is checked top be above the horizon, if the test is failed the scan is skipped. The start to the acquisition is given when +#the telescope is on target. The stop time will be at effective start time plus the scheduled duration. In that mode the schedule is continuously run. +#The second argument, which is optional gives the start LST for the schedule. +#MODE: SEQ +#MODE: SEQ 21:15:36.00 +#this field is used to enumerate the scans of the schedule. The first scan will be marked with the number reported here. +# The identifier will be incremented every scan....and stored into the output file. If negative, or not given (this field is not mandatory) the +# enumeration capability is turned off. +SCANTAG: 1024 +#This procedure will be executed once at the start of the schedule. It could be NULL, in that case nothing is done. If not present it is considered to be +# NULL. The procedure must be defined in the PROCEDURELIST file. It is always executed in blocking mode, the schedule is started only when the procedure +# has been done. +INITPROC: INITALL +#This is an optional field which allows to control which targets are observed or not based on elevation limits. The two values are degrees and are the lower and the upper limit +#respectively. If the keyword is not provided the default values will be kept by the system +ELEVATIONLIMITS: 15.0 80.0 + + +#The schedule itself, the fields are tab separated +#the keyword SCAN identifies the beginning of a scan, a scan includes all the subscan enlisted before the next SCAN keyword or the end of the schedule. +#SCAN scan_id suffix backend:writer [layout] +#scan_id is progressive number (zero is not allowed) (must be unique in the schedule) that identifies the current scan. +#suffix this is the suffix of the output file. The final name of the file will be composed by the date and time of observation then the local sidereal time +# (if the schedule is LST) then the project name and at the end the suffix. +#backend this field reports about the definition (in BACKENDLIST file) to be used in the current scan. After the colon the scheduler +# expects to find the instance of the data receiver to be used. The provided name could be composed by any litteral or numerical character. +# There is reserved string that has special meanings. "NULL" means that the scan must be done with no acquisition. +#writer name of the writer instance in charge to store the data. There is reserved string that has special meanings. +# "NULL" means that the data coming from the backend are not to be stored. +#layout this is the label of the current layout to be used for the current scan. If provided the scan layout file must be given and the corresponding layout must +# exists in the file +#The subscan definition match the following pattern: +#subscan_id [startLST] duration scan PreScan PostScan +#subscan_id is the identifier of the subscan. The format must be scanid_subscanid, where the scanid and subscanid are integers, the former must match the id of the above +# scan, the former can be any positive number (a part from zero) that cannot be repeated twice inside the same scan. +#startLST if the schedule is a LST schedule this field stores the local sidereal time to start the current subscan, the format is hh:mm:ss.sss. The antenna is moved +# in the correct position in advance and the data acquisition is started. No time repetitions are allowed and +# the various times must be incrementally ordered. 00:00:00 could come after 23:59:59. +#duration number of seconds that the scan is supposed to perform data recording. After that period the data recording is stopped. +#scan the scan number (inside the file given in SCANLIST section) +#PreScan A name of a procedure that must be executed before the scan starts. It may be NULL in that case nothing is done. +# If not reported in the procedure list file the schedule is considered correct; the system will try to match the procedure with one of the system commands at +# runtime. The procedure can also be given a list of arguments. +# The default behavior is that the scheduler will wait for the procedure to finish before doing anything else. If this is not the desired approach a '@' must +# be appended to the procedure name,in that case the scheduler won't wait for the procedure completion. +#PostScan A name of a procedure that must be executed at the end of the scan. It may be NULL in that case nothing is done. It has to be reported in the procedure list file. +# The async/sync execution mode is identical to the prescan procedure. + +SC: 1 dr21 STD:MANAGEMENT/MBFits EQMAPLAYOUT +1_1 10:12:04.000 60.0 1 STANDARDPRESCAN@ STANDARDPOSTSCAN +1_2 10:12:25.000 100.0 12 NULL NULL +1_10 10:14:04.000 60.0 1 STANDARDPRESCAN@ STANDARDPOSTSCAN +1_11 10:16:29.000 60.0 1 NULL ENDSCAN=10.66@ + +SC: 2 mySource HIGH:MANAGEMENT/MBFits +2_1 10:30:14.000 300.0 4 STANDARDPRESCAN NULL +2_2 10:45:16.000 6.0 9 NULL NULL + +#id duration scan PreScan PostScan backend Writer +SC: 3 dr21 LOW::MANAGEMENT/FitsZilla +3_1 11:30:10.000 60.0 1 STANDARDPRESCAN NULL +3_2 11:36:22.200 60.0 1 STANDARDPRESCAN NULL