diff --git a/.gitattributes b/.gitattributes index c046c0903c09c46e62808f6659b2a510bbe35882..92787a485e7ffc08b07534f795ca728418b64653 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2416,6 +2416,7 @@ LCU/checkhardware/lib/test_db.py -text LCU/checkhardware/lib/test_lib.py -text LCU/checkhardware/makeStationLogFile.py -text LCU/checkhardware/showTestResult.py -text +LCU/checkhardware/updatePVSS.py -text MAC/APL/APLCommon/include/APL/APLCommon/AntennaField.h -text MAC/APL/APLCommon/src/AntennaField.cc -text MAC/APL/APLCommon/src/StartDaemon_Protocol.prot -text svneol=native#application/octet-stream @@ -2429,6 +2430,8 @@ MAC/APL/APLCommon/test/tAntennaField.sh -text MAC/APL/APLCommon/test/tParentControl.cc -text MAC/APL/APLCommon/test/tParentControl.h -text MAC/APL/APLCommon/test/tParentControl.log_prop -text +MAC/APL/Appl_Controller/src/forkexec.cc -text +MAC/APL/Appl_Controller/src/forkexec.h -text MAC/APL/CASATools/include/CASATools/CasaConverter.h -text MAC/APL/CASATools/src/CasaConverter.cc -text MAC/APL/CASATools/test/tCasaConverter.cc -text @@ -3268,6 +3271,7 @@ MAC/Navigator2/panels/objects/Hardware/TBB_MP.pnl -text MAC/Navigator2/panels/objects/Hardware/TBB_RSP.pnl -text MAC/Navigator2/panels/objects/Hardware/TBB_TP.pnl -text MAC/Navigator2/panels/objects/Hardware/TBB_flashImages.pnl -text +MAC/Navigator2/panels/objects/Hardware/TBBmode_small.pnl -text MAC/Navigator2/panels/objects/Hardware/TBBoard.pnl -text MAC/Navigator2/panels/objects/Hardware/antennaBroken_Small.pnl -text MAC/Navigator2/panels/objects/Hardware/diskuse_small.pnl -text @@ -4147,6 +4151,7 @@ SAS/OTDB/include/OTDB/DefaultTemplate.h -text SAS/OTDB/sql/assignProcessType_func.sql -text SAS/OTDB/sql/campaignAPI.sql -text SAS/OTDB/sql/create_rules.sql -text +SAS/OTDB/sql/exportMetadata_func.sql -text SAS/OTDB/sql/exportResultTree_func.sql -text SAS/OTDB/sql/fresh_database -text SAS/OTDB/sql/fresh_sas001_database.sh -text @@ -4164,6 +4169,7 @@ SAS/OTDB/sql/update_all_functions.sh -text svneol=unset#application/x-shellscrip SAS/OTDB/sql/upgradeOTDB.sql -text SAS/OTDB/src/DefaultTemplate.cc -text SAS/OTDB/test/tBrokenHardware.cc -text +SAS/OTDB/test/tMetadata.cc -text SAS/OTDB/test/tQueryPIC.cc -text SDP/SPP/DSP[!!-~]BUILDER/myfilterbank.mdl -text SDP/SPP/MATLAB/gui2/gui_qt.fig -text svneol=unset#unset diff --git a/CEP/DP3/DPPP/include/DPPP/StationAdder.h b/CEP/DP3/DPPP/include/DPPP/StationAdder.h index 3cd95859eae663f27a826436276abb2a855a58b0..69c47ab4eab716c62e00d8a565c518a90239c9a5 100644 --- a/CEP/DP3/DPPP/include/DPPP/StationAdder.h +++ b/CEP/DP3/DPPP/include/DPPP/StationAdder.h @@ -1,4 +1,4 @@ -//# StationAdder.h: DPPP step class to add station to a superstation +//# StationAdder.h: DPPP step class to add stations as a superstation //# Copyright (C) 2012 //# ASTRON (Netherlands Institute for Radio Astronomy) //# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands @@ -25,7 +25,7 @@ #define DPPP_STATIONADDER_H // @file -// @brief DPPP step class to average in time and/or freq +// @brief DPPP step class to add stations as a superstation #include <DPPP/DPInput.h> #include <DPPP/DPBuffer.h> @@ -109,7 +109,8 @@ namespace LOFAR { vector<casa::Vector<int> > itsParts; // the stations in each superstation vector<vector<int> > itsBufRows; // old baseline rows in each new baseline uint itsMinNPoint ; // flag data if too few unflagged data - bool itsMakeAutoCorr; // also form new autocorrelations? + bool itsMakeAutoCorr; // also form new auto-correlations? + bool itsSumAutoCorr; // sum auto- or cross-correlations? bool itsUseWeight; // false = use weight 1 per station UVWCalculator itsUVWCalc; NSTimer itsTimer; diff --git a/CEP/DP3/DPPP/src/StationAdder.cc b/CEP/DP3/DPPP/src/StationAdder.cc index 1f6768bd5ac613488315620e3d7865763887c473..f4269c5279d6243c4a57f1d1eae6c980a6b6a3b7 100644 --- a/CEP/DP3/DPPP/src/StationAdder.cc +++ b/CEP/DP3/DPPP/src/StationAdder.cc @@ -1,4 +1,4 @@ -//# StationAdder.cc: DPPP step class to add station to a superstation +//# StationAdder.cc: DPPP step class to add stations as a superstation //# Copyright (C) 2012 //# ASTRON (Netherlands Institute for Radio Astronomy) //# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands @@ -55,6 +55,7 @@ namespace LOFAR { itsStatRec (parset.getRecord(prefix+"stations")), itsMinNPoint (parset.getUint (prefix+"minpoints", 1)), itsMakeAutoCorr (parset.getBool (prefix+"autocorr", false)), + itsSumAutoCorr (parset.getBool (prefix+"sumauto", true)), itsUseWeight (parset.getBool (prefix+"useweights", true)) { } @@ -204,11 +205,10 @@ namespace LOFAR { int take = 0; if (havea1) { // If both stations are in same superstation, only use them - // if it is an autocorrelation. + // if autocorrelations have to be made. + // Taking auto or cross depends on summing mode. if (havea2) { - if (itsMakeAutoCorr && ant1[i] == ant2[i]) { - take = 1; - } + take = itsMakeAutoCorr && itsSumAutoCorr == (ant1[i]==ant2[i]); } else { ant = ant2[i]; take = -1; // conjugate has to be added diff --git a/CEP/DP3/DPPP/test/tStationAdder.cc b/CEP/DP3/DPPP/test/tStationAdder.cc index 5b21004c8541b6ef0d3fb5b999cb2971d8481bc2..cd6013906701754f29c542c3a4b4667a733aaaea 100644 --- a/CEP/DP3/DPPP/test/tStationAdder.cc +++ b/CEP/DP3/DPPP/test/tStationAdder.cc @@ -41,7 +41,6 @@ using namespace std; // Simple class to generate input arrays. // It can only set all flags to true or all to false. -// Weights are always 1. // It can be used with different nr of times, channels, etc. class TestInput: public DPInput { @@ -141,9 +140,9 @@ private: class TestOutput: public DPStep { public: - TestOutput(int ntime, int nbl, int nchan, int ncorr) + TestOutput(int ntime, int nbl, int nchan, int ncorr, bool sumauto) : itsCount(0), itsNTime(ntime), itsNBl(nbl), itsNChan(nchan), - itsNCorr(ncorr) + itsNCorr(ncorr), itsSumAuto(sumauto) {} private: void addData (Cube<Complex>& to, const Cube<Complex>& from, int bl) @@ -164,9 +163,26 @@ private: indgen (weights, 0.5f, 0.01f); Cube<Complex> databl0 (itsNCorr, itsNChan, 1); Cube<Complex> databl1 (itsNCorr, itsNChan, 1); - addData (databl0, data, 0); - addData (databl0, data, 5); - addData (databl0, data, 15); + // "{ns:[rs01.s01, rs02.s01, cs01.s02]}" was given resulting in 2 new + // baselines (ns-ns and cs01.s01-ns). + // Thus adding the baselines below. + float weight=0; + if (itsSumAuto) { + // add autocorr to form new autocorr + addData (databl0, data, 0); + addData (databl0, data, 5); + addData (databl0, data, 15); + weight = 3; + } else { + // add crosscorr to form new autocorr + addData (databl0, data, 1); + addData (databl0, data, 3); + addData (databl0, data, 4); + addData (databl0, data, 7); + addData (databl0, data, 12); + addData (databl0, data, 13); + weight = 6; + } addData (databl1, data, 8); addData (databl1, data, 9); addData (databl1, data, 11); @@ -191,7 +207,7 @@ private: // Now check data of new baselines. end[2] = itsNBl; ASSERT (allNear (buf.getData()(IPosition(3,0,0,itsNBl), end), databl0, 1e-5)); - ASSERT (allNear (buf.getWeights()(IPosition(3,0,0,itsNBl), end), 3.f, 1e-5)); + ASSERT (allNear (buf.getWeights()(IPosition(3,0,0,itsNBl), end), weight, 1e-5)); end[2] = itsNBl+1; ASSERT (allNear (buf.getData()(IPosition(3,0,0,itsNBl+1), end), databl1, 1e-5)); ASSERT (allNear (buf.getWeights()(IPosition(3,0,0,itsNBl+1), end), 6.f, 1e-5)); @@ -234,6 +250,7 @@ private: int itsCount; int itsNTime, itsNBl, itsNChan, itsNCorr, itsNAvgTime, itsNAvgChan; + bool itsSumAuto; }; // Class to check result of flagged, unaveraged TestInput run by test2. @@ -275,6 +292,7 @@ private: Cube<Float> weightbl2 (itsNCorr, itsNChan, 1, 0.); Cube<Float> weightbl3 (itsNCorr, itsNChan, 1, 0.); Cube<Float> weightbl4 (itsNCorr, itsNChan, 1, 0.); + // "{ns1:[rs01.s01, rs02.s01], ns2:[cs01.s02, cs01.s01]}" was given. addData (databl0, data, weightbl0, weights, 8); addData (databl0, data, weightbl0, weights, 9); addData (databl1, data, weightbl1, weights, 12); @@ -376,10 +394,10 @@ void execute (const DPStep::ShPtr& step1) } // Test adding 3 stations. -void test1(int ntime, int nbl, int nchan, int ncorr) +void test1(int ntime, int nbl, int nchan, int ncorr, bool sumauto) { cout << "test1: ntime=" << ntime << " nrbl=" << nbl << " nchan=" << nchan - << " ncorr=" << ncorr << endl; + << " ncorr=" << ncorr << " sumauto=" << sumauto << endl; // Create the steps. TestInput* in = new TestInput(ntime, nbl, nchan, ncorr); DPStep::ShPtr step1(in); @@ -387,9 +405,12 @@ void test1(int ntime, int nbl, int nchan, int ncorr) parset.add ("stations", "{ns:[rs01.s01, rs02.s01, cs01.s02]}"); parset.add ("autocorr", "true"); + if (!sumauto) { + parset.add ("sumauto", "false"); + } parset.add ("useweights", "false"); DPStep::ShPtr step2(new StationAdder(in, parset, "")); - DPStep::ShPtr step3(new TestOutput(ntime, nbl, nchan, ncorr)); + DPStep::ShPtr step3(new TestOutput(ntime, nbl, nchan, ncorr, sumauto)); step1->setNextStep (step2); step2->setNextStep (step3); execute (step1); @@ -462,7 +483,8 @@ int main() // Test the station selection patterns. testPatterns(); // Test must be done with with 16 baselines. - test1( 10, 16, 32, 4); + test1( 10, 16, 32, 4, true); + test1( 10, 16, 32, 4, false); test2( 10, 16, 32, 4); // Unknown station. test3("{ns1:rs01.s1, ns2:[cs01.s02, cs01.s01]}"); diff --git a/CEP/Pipeline/framework/lofarpipe/support/loggingdecorators.py b/CEP/Pipeline/framework/lofarpipe/support/loggingdecorators.py index 6ade4aadcc3da2c20d429dde4f6228d04911e527..4bca87d98b6d39ae9dfc9f7926ab2aec52ebc14d 100644 --- a/CEP/Pipeline/framework/lofarpipe/support/loggingdecorators.py +++ b/CEP/Pipeline/framework/lofarpipe/support/loggingdecorators.py @@ -141,7 +141,8 @@ def mail_log_on_exception(target): # Static list of mail to be send (could be made configurable, # but yeah temp mail functionality so...) mail_list = ["klijn@astron.nl", "frieswijk@astron.nl", - "pizzo@astron.nl", "orru@astron.nl" + "pizzo@astron.nl", "orru@astron.nl", + "jong@astron.nl" ] # get the active stack diff --git a/LCU/checkhardware/checkHardware.py b/LCU/checkhardware/checkHardware.py index 1433941752c667b0653a9d2a2ed0fc4bdd1dab0a..7f4b90453314cbf94c8fe67e0ea796e2f73ba071 100755 --- a/LCU/checkhardware/checkHardware.py +++ b/LCU/checkhardware/checkHardware.py @@ -20,7 +20,7 @@ def printHelp(): print "----------------------------------------------------------------------------" print "Usage of arguments" print "-c : clean test (do not use last test results)" - print "-l 2 : set level too 2 (default level is 0)" + print "-l=2 : set level too 2 (default level is 0)" print " level 0 : manual tests, use keys listed below" print " level 1..n: see checkhardware.conf file for tests done" print @@ -44,22 +44,14 @@ args = dict() def getArguments(): global args args['L'] = 0 # default test level - i = 1 - while i < len(sys.argv): + + for i in range(len(sys.argv)): if sys.argv[i][0] == '-': - opt = sys.argv[i][1:].upper() - if opt in ('T','L'): # for testing - optval = sys.argv[i+1].upper() - args[opt] = optval - i += 2 + valpos = sys.argv[i].find('=') + if valpos != -1: + args[sys.argv[i][1:valpos].upper()] = int(sys.argv[i][valpos+1:]) else: - valpos = opt.find('=') - if valpos != -1: - args[opt[:valpos]] = int(opt[valpos+1:]) - #print opt[:valpos], opt[valpos+1:] - else: - args[opt] = '-' - i += 1 + args[sys.argv[i][1:].upper()]='-' def setLevelTests(conf): global args @@ -117,10 +109,9 @@ def main(): # Read in RemoteStation.conf ID, nRSP, nTBB, nLBL, nLBH, nHBA = readStationConfig() - print ID, nRSP, nTBB, nLBL, nLBH, nHBA # setup intern database - db = cDB(StID, nRSP, nTBB, nLBL, nLBH, nHBA, clean=args.has_key('C')) + db = cDB(StID, nRSP, nTBB, nLBL, nLBH, nHBA) logger.info('== START OF HARDWARE CHECK ==', screen=True) logger.resetStartTime(screen=True) diff --git a/LCU/checkhardware/doFullStationTest.sh b/LCU/checkhardware/doFullStationTest.sh index 77e98c2df382ff2735fc82097744ae0f264acde7..cba66455c44af4240ce2090724e55842f5816e5f 100755 --- a/LCU/checkhardware/doFullStationTest.sh +++ b/LCU/checkhardware/doFullStationTest.sh @@ -5,7 +5,7 @@ logdir="/globalhome/log/" host=`hostname -s` # Check hardware in CheckLevel 2, do all tests -checkHardware.py -l 2 +checkHardware.py -l=2 # Add to history filenameFrom=$logdir$host"_StationTest.csv" @@ -13,8 +13,7 @@ filenameToo=$logdir$host"_StationTestHistory.csv" cat $filenameFrom >> $filenameToo # Add test results too PVSS -pvssFile=$logdir$host"_StationTest_PVSS.log" -setObjectState $pvssFile +updatePVSS.py # Make old station log files makeStationLogFile.py diff --git a/LCU/checkhardware/doShortStationTest.sh b/LCU/checkhardware/doShortStationTest.sh index c88823c2f2881e47b9a398003546c137c1924d3c..e0006a6292e45544befebf8a03dbd296b3911e04 100755 --- a/LCU/checkhardware/doShortStationTest.sh +++ b/LCU/checkhardware/doShortStationTest.sh @@ -5,7 +5,7 @@ logdir="/globalhome/log/" host=`hostname -s` # Check hardware in CheckLevel 1, only antennas -checkHardware.py -l 1 +checkHardware.py -l=1 # Add too history filenameFrom=$logdir$host"_StationTest.csv" @@ -13,8 +13,7 @@ filenameToo=$logdir$host"_StationTestHistory.csv" cat $filenameFrom >> $filenameToo # Add test results too PVSS -pvssFile=$logdir$host"_StationTest_PVSS.log" -setObjectState lofarsys $pvssFile +updatePVSS.py # Make old station log files makeStationLogFile.py diff --git a/LCU/checkhardware/lib/test_db.py b/LCU/checkhardware/lib/test_db.py index 3f0af3c8e71d576a7a38c9ce2e90b63586abb2f5..d29932a3c406ccf45ae234a3f3175ef3fcb8c1ef 100644 --- a/LCU/checkhardware/lib/test_db.py +++ b/LCU/checkhardware/lib/test_db.py @@ -3,11 +3,8 @@ from general_lib import * from lofar_lib import * -# PVSS states -State = dict({'OFF':0, 'OPERATIONAL':1, 'MAINTENANCE':2, 'TEST':3, 'SUSPICIOUS':4, 'BROKEN':5}) - class cDB: - def __init__(self, StID, nRSP, nTBB, nLBL, nLBH, nHBA, clean=False): + def __init__(self, StID, nRSP, nTBB, nLBL, nLBH, nHBA): self.StID = StID self.nr_rsp = nRSP self.nr_rcu = nRSP * 8 @@ -39,42 +36,8 @@ class cDB: self.lbl = self.cLBA(label='LBL', nr_antennas=nLBL, nr_offset=48) self.lbh = self.cLBA(label='LBH', nr_antennas=nLBH) self.hba = self.cHBA(nr_tiles=nHBA) - - # try to read in from last test file LBL, LBH and HBA status - try: - if not clean: - print "Import latest log file" - f = open("%s_StationTest.csv" %(self.StID), 'r') - data = f.readlines() - f.close() - for line in data: - d = line.split(',') - if d[1] == 'LBL': - if d[2] != '---': - nr = int(d[2]) - msg = d[3].strip() - if msg in ('DOWN', 'FAIL', 'LOW_NOISE', 'HIGH_NOISE'): - self.lbl.ant[nr].last_state = State['BROKEN'] - elif d[1] == 'LBH': - if d[2] != '---': - nr = int(d[2]) - msg = d[3].strip() - if msg in ('DOWN', 'FAIL', 'LOW_NOISE', 'HIGH_NOISE'): - self.lbh.ant[nr].last_state = State['BROKEN'] - elif d[1] == 'HBA': - if d[2] != '---': - nr = int(d[2]) - msg = d[3].strip() - if msg in ('FAIL', 'LOW_NOISE', 'HIGH_NOISE'): - self.hba.tile[nr].las_state = State['BROKEN'] - for elem in hba.tile[nr].element: - nr = elem.nr - if line.find('M%d=' %(nr)) > 0 or line.find('X%d=' %(nr)) > 0 or line.find('Y%d=' %(nr)) > 0: - elem.last_state = State['BROKEN'] - except: - pass - + # test def test(self, logdir): if self.rspdriver_version != "ok" or self.rspctl_version != "ok": self.station_error = 1 @@ -114,7 +77,6 @@ class cDB: self.station_error = max(self.station_error, self.lbl.test(), self.lbh.test(), self.hba.test()) self.makeLogFile(logdir) - self.makePVSSfile(logdir) return (self.station_error) @@ -249,47 +211,7 @@ class cDB: if len(valstr): log.addLine("%s,HBA,%03d,FAIL%s" %(date, tile.nr, valstr)) return - - - # make log file for PVSS logging, use setObjectState to enter settings in PVSS - """ - Syntax: /opt/lofar/sbin/setObjectState who datapoint stateNr - OR /opt/lofar/sbin/setObjectState who filename - who : Identification who changes the state, name of program or user - datapoint : PVSS datapoint name including database name - stateNr : 0 - Off - 1 - Operational - 2 - Maintenance - 3 - Test - 4 - Suspicious - 5 - Broken - """ - def makePVSSfile(self, logdir): - pvss = cPVSSLogger(logdir) - - for ant in self.lbh.ant: - ant.testPVSS() - pvss.addLine("LOFAR_PIC_LBA%03d %d" %(ant.nr_total, ant.state)) - pvss.addLine("LOFAR_PIC_LBA%03d.X %d" %(ant.nr_total, ant.x_state)) - pvss.addLine("LOFAR_PIC_LBA%03d.Y %d" %(ant.nr_total, ant.y_state)) - - for ant in self.lbl.ant: - ant.testPVSS() - pvss.addLine("LOFAR_PIC_LBA%03d %d" %(ant.nr_total, ant.state)) - pvss.addLine("LOFAR_PIC_LBA%03d.X %d" %(ant.nr_total, ant.x_state)) - pvss.addLine("LOFAR_PIC_LBA%03d.Y %d" %(ant.nr_total, ant.y_state)) - - for tile in self.hba.tile: - tile.testPVSS() - pvss.addLine("LOFAR_PIC_HBA%02d %d" %(tile.nr, tile.state)) - for elem in tile.element: - pvss.addLine("LOFAR_PIC_HBA%02d.element%02d %d" %(tile.nr, elem.nr, elem.state)) - pvss.addLine("LOFAR_PIC_HBA%02d.element%02d.comm %d" %(tile.nr, elem.nr, elem.modem_state)) - pvss.addLine("LOFAR_PIC_HBA%02d.element%02d.X %d" %(tile.nr, elem.nr, elem.x_state)) - pvss.addLine("LOFAR_PIC_HBA%02d.element%02d.Y %d" %(tile.nr, elem.nr, elem.y_state)) - return - class cRSP: def __init__(self, nr): self.nr = nr @@ -368,12 +290,6 @@ class cDB: self.y_high_val = 0.0 self.down = 0 - - self.last_state = State['OFF'] - # states for PVSS - self.x_state = State['OFF'] - self.y_state = State['OFF'] - self.state = State['OFF'] return def test(self): @@ -381,14 +297,6 @@ class cDB: self.y_error = max(self.y_too_low, self.y_too_high, self.y_high_noise, self.y_low_noise, self.down) return - def testPVSS(self): - if self.x_error: - self.x_state = State['BROKEN'] - if self.y_error: - self.y_state = State['BROKEN'] - self.state = max(self.last_state, self.state, self.x_state, self.y_state) - return - class cHBA: def __init__(self, nr_tiles): self.check_done = 0 @@ -442,9 +350,6 @@ class cDB: self.element = list() for i in range(self.nr_elements): self.element.append(self.cElement(i)) - - # states for PVSS - self.state = State['OFF'] return def test(self): @@ -466,14 +371,10 @@ class cDB: if elem.modem_error: modem_err_cnt += 1 - #self.x_low_noise = max(self.x_low_noise, elem.x_low_noise) - #self.x_high_noise = max(self.x_high_noise, elem.x_high_noise) - #self.y_low_noise = max(self.y_low_noise, elem.y_low_noise) - #self.y_high_noise = max(self.y_high_noise, elem.y_high_noise) self.x_error = max(self.x_error, elem.x_error) self.y_error = max(self.y_error, elem.y_error) - if (no_modem_cnt >= 8) or (modem_err_cnt >= 8): + if (no_modem_cnt + modem_err_cnt) >= 15: self.c_summator_error = 1 if no_power_cnt >= 15: self.p_summator_error = 1 @@ -486,12 +387,6 @@ class cDB: self.y_error = max(self.y_error, self.y_low_noise, self.y_high_noise, self.p_summator_error, self.c_summator_error) return - def testPVSS(self): - for elem in self.element: - elem.testPVSS() - self.state = max(self.state, elem.state) - return - class cElement: def __init__(self, nr): self.nr = nr @@ -519,12 +414,6 @@ class cDB: self.no_modem = 0 # modem reponse = ?? self.modem_error = 0 # wrong response from modem - self.last_state = State['OFF'] - # states for PVSS - self.modem_state = State['OFF'] - self.x_state = State['OFF'] - self.y_state = State['OFF'] - self.state = State['OFF'] return def test(self): @@ -535,26 +424,6 @@ class cDB: self.no_power, self.no_modem, self.modem_error) return - # test for PVSS settings - def testPVSS(self): - self.state = self.last_state - - if self.modem_error: - self.modem_state = State['SUSPICIOUS'] - - if self.no_modem: - self.modem_state = State['BROKEN'] - - if self.x_no_signal or self.x_too_low or self.x_low_noise or self.x_high_noise or self.no_power or self.no_modem or self.modem_error: - self.x_state = State['BROKEN'] - - if self.y_no_signal or self.y_too_low or self.y_low_noise or self.y_high_noise or self.no_power or self.no_modem or self.modem_error: - self.y_state = State['BROKEN'] - - self.state = max(self.state, self.x_state, self.y_state, self.modem_state) - return - - class cTDS: def __init__(self): self.test_done = 0 diff --git a/LCU/checkhardware/makeStationLogFile.py b/LCU/checkhardware/makeStationLogFile.py index f84ec0fb4334df6b2a7f0d15e40937a5e350f58a..a5fe4426a18cbdae5b4a205f4d1f54a859cd62f0 100755 --- a/LCU/checkhardware/makeStationLogFile.py +++ b/LCU/checkhardware/makeStationLogFile.py @@ -70,7 +70,13 @@ for line in testdata: if part == 'LBL': rcu = partNr * 2 - if msgType == 'FAIL': + if msgType == 'LOW_NOISE': + log.addLine('LBAosc1>: Sv=normal Pr=normal , LBA Outer (LBL) large oscillation (low noise): RCU: (%d,%d)' %\ + (partNr*2, partNr*2+1)) + elif msgType == 'HIGH_NOISE': + log.addLine('LBAosc1>: Sv=normal Pr=normal , LBA Outer (LBL) large oscillation (high noise): RCU: (%d,%d)' %\ + (partNr*2, partNr*2+1)) + elif msgType == 'FAIL': if keyinfo.has_key('X'): log.addLine('LBAmd1>: Sv=normal Pr=normal , LBA Outer (LBL) defect: RCU: %d factor: %3.1f' %\ (partNr*2, float(keyinfo['X']))) @@ -88,13 +94,19 @@ for line in testdata: log.addLine('LBAmd1>: Sv=normal Pr=normal , LBA levels to low!!!' ) if part == 'LBH': - if msgType == 'FAIL': + if msgType == 'LOW_NOISE': + log.addLine('LBAosc3>: Sv=normal Pr=normal , LBA Inner (LBH) large oscillation (low noise): RCU: (%d,%d)' %\ + (partNr*2, partNr*2+1)) + elif msgType == 'HIGH_NOISE': + log.addLine('LBAosc3>: Sv=normal Pr=normal , LBA Inner (LBH) large oscillation (high noise): RCU: (%d,%d)' %\ + (partNr*2, partNr*2+1)) + elif msgType == 'FAIL': if keyinfo.has_key('X'): log.addLine('LBAmd3>: Sv=normal Pr=normal , LBA Inner (LBH) defect: RCU: %s factor: %3.1f' %\ (partNr*2, float(keyinfo['X']))) if keyinfo.has_key('Y'): log.addLine('LBAmd3>: Sv=normal Pr=normal , LBA Inner (LBH) defect: RCU: %s factor: %3.1f' %\ - (partNr*2, float(keyinfo['Y']))) + (partNr*2+1, float(keyinfo['Y']))) elif msgType == 'DOWN': log.addLine('LBAdn3>: Sv=normal Pr=normal , LBA Inner (LBH) down: RCU: %d factor: %3.1f offset: %d' %\ @@ -106,16 +118,24 @@ for line in testdata: log.addLine('LBAmd3>: Sv=normal Pr=normal , LBA levels to low!!!' ) if part == 'HBA': - if msgType == 'C_SUMMATOR': + if msgType == 'LOW_NOISE': + log.addLine('HBAosc>: Sv=normal Pr=normal , Tile %d - RCU (%d,%d); Large oscillation (low noise)' %\ + (partNr, partNr*2, partNr*2+1)) + + elif msgType == 'HIGH_NOISE': + log.addLine('HBAosc>: Sv=normal Pr=normal , Tile %d - RCU (%d,%d); Large oscillation (high noise)' %\ + (partNr, partNr*2, partNr*2+1)) + + elif msgType == 'C_SUMMATOR': last_c_summator = partNr log.addLine('HBAmdt>: Sv=normal Pr=normal , Tile %d - RCU %d; Broken. No modem communication' %\ (partNr, partNr*2+1)) - if last_c_summator != partNr and msgType == 'FAIL': + elif last_c_summator != partNr and msgType == 'FAIL': for elem_nr in range(1,17,1): if keyinfo.has_key('M%d' %(elem_nr)): val = keyinfo.get('M%d' %(elem_nr)) - log.addLine('HBAmdt>: Sv=normal Pr=normal , Tile %d - RCU %d; Element %d Broken. No modem communication : (%s)' %\ + log.addLine('HBAmdt>: Sv=normal Pr=normal , Tile %d - RCU %d; Element %s Broken. No modem communication : (%s)' %\ (partNr, partNr*2+1, elem_nr, val)) if keyinfo.has_key('X%d' %(elem_nr)): diff --git a/LCU/checkhardware/updatePVSS.py b/LCU/checkhardware/updatePVSS.py new file mode 100755 index 0000000000000000000000000000000000000000..c48a0af7c258043b31a8b3715f9f594a58cdfebe --- /dev/null +++ b/LCU/checkhardware/updatePVSS.py @@ -0,0 +1,255 @@ +#!/usr/bin/python +# +# read last test log file (.csv) +# and send test result to PVSS, +# and write to PVSS log file +# +# P.Donker + +import sys +import os +from time import sleep + +libPath = '/opt/stationtest/lib' +sys.path.insert(0, libPath) + +from general_lib import * +from lofar_lib import * + +args = dict() +logdir = "" +logger = 0 +nLBL = 0 +nLBH = 0 +nHBA = 0 + +# PVSS states +State = dict({'OFF':0, 'OPERATIONAL':1, 'MAINTENANCE':2, 'TEST':3, 'SUSPICIOUS':4, 'BROKEN':5}) + + + +def main(): + global args, logdir, logger, nLBL, nLBH, nHBA + args = getArguments() + logdir = getLogDir() + ID, nRSP, nTBB, nLBL, nLBH, nHBA = readStationConfig() + logger = cPVSSLogger(logdir) + + if args.has_key('RESET'): + resetPVSS(0) + + else: + # read last log file from checkhardware + testfilename = '%s_StationTest.csv' %(getHostName()) + fullFilename = os.path.join(logdir, testfilename) + f = open(fullFilename, 'r') + testdata = f.readlines() + f.close() + + addDataToPVSS(testdata) + +# print help screen +def printHelp(): + print "----------------------------------------------------------------------------" + print "Usage of arguments" + print "-h : this help screen" + print "-reset : clean all, set all antennas too ok" + print "-test : do not send to PVSS" + +# get command line arguments +def getArguments(): + args = dict() + for i in range(len(sys.argv)): + if sys.argv[i][0] == '-': + if sys.argv[i].find('=') != -1: + args[sys.argv[i][1:valpos].upper()] = int(sys.argv[i][valpos+1:]) + else: + args[sys.argv[i][1:].upper()]='-' + + if args.has_key('H'): + printHelp() + sys.exit() + return(args) + +# get logdir from configuration file +def getLogDir(): + logdir = "" + # look for log directory + f = open("/opt/stationtest/checkHardware.conf", 'r') + data = f.readlines() + f.close() + for line in data: + if line.find('log-dir') != -1: + key, logdir = line.strip().split('=') + return(logdir) + +# send comment, key and value to PVSS and write to file +def sendToPVSS(comment, pvss_key, value): + global logger + if len(comment) > 0: + comment = 'stationtest::'+comment + else: + comment = 'stationtest' + arguments = '%s %s %d' %(comment, pvss_key, value) + logger.addLine(arguments[11:]) + if args.has_key('TEST'): + print arguments + else: + response = sendCmd('setObjectState', arguments) + sleep(0.05) + return(response) + return("") + +# set all antenna info to ok +def resetPVSS(state=0): + global State, nLBL, nLBH, nHBA + + filename = "reset_pvss.log" + full_filename = os.path.join(libPath, filename) + f = open(full_filename, 'w') + + for ant in range(nLBH): + f.write("LOFAR_PIC_LBA%03d %d\n" %(ant, state)) + + for ant in range(nLBL): + f.write("LOFAR_PIC_LBA%03d %d\n" %(ant+48, state)) + + for tile in range(nHBA): + f.write("LOFAR_PIC_HBA%02d %d\n" %(tile, state)) + + for elem in range(16): + f.write("LOFAR_PIC_HBA%02d.element%02d %d\n" %(tile, elem, state)) + f.write("LOFAR_PIC_HBA%02d.element%02d.comm %d\n" %(tile, elem, state)) + f.write("LOFAR_PIC_HBA%02d.element%02d.X %d\n" %(tile, elem, state)) + f.write("LOFAR_PIC_HBA%02d.element%02d.Y %d\n" %(tile, elem, state)) + + sendCmd("setObjectState", "stationtes:reset %s" %(full_filename)) + +# add result data from checkhardware to PVSS +def addDataToPVSS(data): + global State + for line in data: + if line[0] == '#': + continue + keyinfo = dict() + info = line.split(',') + #print info + date = info[0] + part = info[1] + partNr = '---' + if info[2] != '---': + partNr = int(info[2]) + msgType = info[3].strip() + for i in range(4,len(info)): + if info[i].find('=') != -1: + key, valstr = info[i].split('=') + vallist = valstr.split() + if len(vallist) == 1: + keyinfo[key] = vallist[0] + elif len(vallist) > 1: + keyinfo[key] = vallist + else: + keyinfo[info[i]] = '-' + + if part == 'LBL': + if msgType == 'LOW_NOISE': + sendToPVSS("low-noise", "LOFAR_PIC_LBA%03d" %(partNr+48), State['BROKEN']) + + elif msgType == 'HIGH_NOISE': + sendToPVSS("high-noise", "LOFAR_PIC_LBA%03d" %(partNr+48), State['BROKEN']) + + if msgType == 'FAIL': + comment = "rf-fail-" + if keyinfo.has_key('X'): + comment += "X" + #sendToPVSS("rf-fail", "LOFAR_PIC_LBA%03d.X" %(partNr+48), State['BROKEN']) + if keyinfo.has_key('Y'): + comment += "Y" + #sendToPVSS("rf-fail", "LOFAR_PIC_LBA%03d.Y" %(partNr+48), State['BROKEN']) + sendToPVSS(comment, "LOFAR_PIC_LBA%03d" %(partNr+48), State['BROKEN']) + + elif msgType == 'DOWN': + sendToPVSS("down", "LOFAR_PIC_LBA%03d" %(partNr+48), State['BROKEN']) + #sendToPVSS("Down", "LOFAR_PIC_LBA%03d.X" %(partNr+48), State['BROKEN']) + #sendToPVSS("Down", "LOFAR_PIC_LBA%03d.Y" %(partNr+48), State['BROKEN']) + + if part == 'LBH': + if msgType == 'LOW_NOISE': + sendToPVSS("low-noise", "LOFAR_PIC_LBA%03d" %(partNr), State['BROKEN']) + + elif msgType == 'HIGH_NOISE': + sendToPVSS("high-noise", "LOFAR_PIC_LBA%03d" %(partNr), State['BROKEN']) + + elif msgType == 'FAIL': + comment = "rf-fail-" + if keyinfo.has_key('X'): + comment += "X" + #sendToPVSS("rf-fail", "LOFAR_PIC_LBA%03d.X" %(partNr), State['BROKEN']) + if keyinfo.has_key('Y'): + comment += "Y" + #sendToPVSS("rf-fail", "LOFAR_PIC_LBA%03d.Y" %(partNr), State['BROKEN']) + sendToPVSS(comment, "LOFAR_PIC_LBA%03d" %(partNr), State['BROKEN']) + elif msgType == 'DOWN': + sendToPVSS("down", "LOFAR_PIC_LBA%03d" %(partNr), State['BROKEN']) + #sendToPVSS("Down", "LOFAR_PIC_LBA%03d.X" %(partNr), State['BROKEN']) + #sendToPVSS("Down", "LOFAR_PIC_LBA%03d.Y" %(partNr), State['BROKEN']) + + if part == 'HBA': + if msgType == 'LOW_NOISE': + sendToPVSS("low-noise", "LOFAR_PIC_HBA%02d" %(partNr), State['BROKEN']) + + elif msgType == 'HIGH_NOISE': + sendToPVSS("high-noise", "LOFAR_PIC_HBA%02d" %(partNr), State['BROKEN']) + + elif msgType == 'C_SUMMATOR': + sendToPVSS("Moden-fail", "LOFAR_PIC_HBA%02d" %(partNr), State['BROKEN']) + + elif msgType == 'FAIL': + sendToPVSS("", "LOFAR_PIC_HBA%02d" %(partNr), State['BROKEN']) + for elem_nr in range(1,17,1): + + if keyinfo.has_key('M%d' %(elem_nr)) \ + or keyinfo.has_key('X%d' %(elem_nr)) \ + or keyinfo.has_key('Y%d' %(elem_nr)) \ + or keyinfo.has_key('LNX%d' %(elem_nr)) \ + or keyinfo.has_key('HNX%d' %(elem_nr)) \ + or keyinfo.has_key('LNY%d' %(elem_nr)) \ + or keyinfo.has_key('HNY%d' %(elem_nr)): + sendToPVSS("rf-fail", "LOFAR_PIC_HBA%02d.element%02d" %(partNr, elem_nr-1), State['BROKEN']) + + if keyinfo.has_key('M%d' %(elem_nr)): + sendToPVSS("rf-fail", "LOFAR_PIC_HBA%02d.element%02d.comm" %(partNr, elem_nr-1), State['BROKEN']) + + comment = "" + if keyinfo.has_key('X%d' %(elem_nr)) \ + or keyinfo.has_key('LNX%d' %(elem_nr)) \ + or keyinfo.has_key('HNX%d' %(elem_nr)): + + if keyinfo.has_key('X%d' %(elem_nr)): + comment += "rf-fail&" + + if keyinfo.has_key('LNX%d' %(elem_nr)): + comment += "low-noise&" + + if keyinfo.has_key('HNX%d' %(elem_nr)): + comment += "high-noise&" + sendToPVSS(comment[:-1], "LOFAR_PIC_HBA%02d.element%02d.X" %(partNr, elem_nr-1), State['BROKEN']) + + comment = "" + if keyinfo.has_key('Y%d' %(elem_nr)) \ + or keyinfo.has_key('LNY%d' %(elem_nr)) \ + or keyinfo.has_key('HNY%d' %(elem_nr)): + + if keyinfo.has_key('Y%d' %(elem_nr)): + comment += "rf-fail&" + + if keyinfo.has_key('LNY%d' %(elem_nr)): + comment += "low-noise&" + + if keyinfo.has_key('HNY%d' %(elem_nr)): + comment += "high-noise&" + sendToPVSS(comment[:-1], "LOFAR_PIC_HBA%02d.element%02d.Y" %(partNr, elem_nr-1), State['BROKEN']) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/MAC/APL/Appl_Controller/CMakeLists.txt b/MAC/APL/Appl_Controller/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..1cdc000bb2b63e542833802dcce48852802db64f --- /dev/null +++ b/MAC/APL/Appl_Controller/CMakeLists.txt @@ -0,0 +1,9 @@ +# $Id$ + +lofar_package(Appl_Controller 2.0 DEPENDS Common MACIO ALC PLC) + +include(LofarFindPackage) +lofar_find_package(Blitz REQUIRED) + +add_subdirectory(src) + diff --git a/MAC/APL/Appl_Controller/src/ACCmdImpl.cc b/MAC/APL/Appl_Controller/src/ACCmdImpl.cc new file mode 100644 index 0000000000000000000000000000000000000000..ac5dac98d8be21aae014fcee38f93ad27948f002 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ACCmdImpl.cc @@ -0,0 +1,157 @@ +//# ACCmdImpl.cc: The actual command implementation for the A. Controller +//# +//# Copyright (C) 2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +//# Includes +#include <Common/StringUtil.h> +#include <Common/LofarLogger.h> +#include <time.h> +#include "ACCmdImpl.h" +#include "APAdminPool.h" + +namespace LOFAR { + namespace ACC { + +ACCmdImpl::ACCmdImpl() : + ApplControl() +{ +} + +// Destructor; +ACCmdImpl::~ACCmdImpl() { }; + + +// Commands to control the application +bool ACCmdImpl::boot (const time_t /* scheduleTime */, + const string& /* configID */ ) const +{ + LOG_WARN("boot: Should have been implemented in the statemachine"); + return (true); +} + +bool ACCmdImpl::define(const time_t scheduleTime) const +{ + LOG_DEBUG(formatString("define(%s)", timeString(scheduleTime).c_str())); + APAdminPool::getInstance().writeToAll(PCCmdDefine, ""); + return (true); +} + +bool ACCmdImpl::init (const time_t scheduleTime) const +{ + LOG_DEBUG(formatString("init(%s)", timeString(scheduleTime).c_str())); + APAdminPool::getInstance().writeToAll(PCCmdInit, ""); + return (true); +} + +bool ACCmdImpl::run (const time_t scheduleTime) const +{ + LOG_DEBUG(formatString("run(%s)", timeString(scheduleTime).c_str())); + APAdminPool::getInstance().writeToAll(PCCmdRun, ""); + return (true); +} + +bool ACCmdImpl::pause (const time_t scheduleTime, + const time_t waitTime, + const string& condition) const +{ + LOG_DEBUG(formatString("pause(%s,%d,%s)", timeString(scheduleTime).c_str(), + waitTime, condition.c_str())); + APAdminPool::getInstance().writeToAll(PCCmdPause, condition); + return (true); +} + +bool ACCmdImpl::release (const time_t scheduleTime) const +{ + LOG_DEBUG(formatString("release(%s)", timeString(scheduleTime).c_str())); + APAdminPool::getInstance().writeToAll(PCCmdRelease, ""); + return (true); +} + +bool ACCmdImpl::quit (const time_t scheduleTime) const +{ + LOG_DEBUG(formatString("quit(%s)", timeString(scheduleTime).c_str())); + APAdminPool::getInstance().writeToAll(PCCmdQuit, ""); + return (true); +} + +bool ACCmdImpl::shutdown (const time_t/* scheduleTime*/) const +{ + LOG_WARN("shutdown: Should have been implemented in the statemachine"); + return (true); +} + +bool ACCmdImpl::snapshot (const time_t scheduleTime, + const string& destination) const +{ + LOG_DEBUG(formatString("snapshot(%s,%s)", timeString(scheduleTime).c_str(), + destination.c_str())); + APAdminPool::getInstance().writeToAll(PCCmdSnapshot, destination); + return (true); +} + +bool ACCmdImpl::recover (const time_t scheduleTime, + const string& source) const +{ + LOG_DEBUG(formatString("recover(%s,%s)", timeString(scheduleTime).c_str(), + source.c_str())); + APAdminPool::getInstance().writeToAll(PCCmdRecover, source); + return (true); +} + + +bool ACCmdImpl::reinit (const time_t scheduleTime, + const string& configID) const +{ + LOG_DEBUG(formatString("reinit(%s,%s)", timeString(scheduleTime).c_str(), + configID.c_str())); + APAdminPool::getInstance().writeToAll(PCCmdReinit, configID); + return (true); +} + +bool ACCmdImpl::replace (const time_t /*scheduleTime*/, + const string& /*processList*/, + const string& /*nodeList*/, + const string& /*configID*/) const +{ + // TODO + return (true); +} + +bool ACCmdImpl::cancelCmdQueue () const +{ + THROW (Exception, "We should never come in ACCmdImpl::cancelCmdQueue"); + return (true); +} + +// Define a generic way to exchange info between client and server. +string ACCmdImpl::askInfo (const string& /*keylist*/) const +{ + // TODO + return ("ACCmdImpl: askInfo not yet implemented"); +} + + + + } // namespace ACC +} // namespace LOFAR diff --git a/MAC/APL/Appl_Controller/src/ACCmdImpl.h b/MAC/APL/Appl_Controller/src/ACCmdImpl.h new file mode 100644 index 0000000000000000000000000000000000000000..45c707a3aee2b5981795164fa8fdaf2fd3ecf495 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ACCmdImpl.h @@ -0,0 +1,94 @@ +//# ACCmdImpl.h: the implementation of the AC commands +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# Note: This source is read best with tabstop 4. +//# +//# $Id$ + +#ifndef LOFAR_ACCBIN_ACCMDIMPL_H +#define LOFAR_ACCBIN_ACCMDIMPL_H + +// \file +// The implementation of the AC commands from the Application Controller + +//# Never #include <config.h> or #include <lofar_config.h> in a header file! +//# Includes +#include <ALC/ApplControl.h> + +using namespace LOFAR::ACC::ALC; + +namespace LOFAR { + namespace ACC { + +// \addtogroup ACCbin +// @{ + +// This class implements the execution of the AC commands by the Application +// Controller. +class ACCmdImpl : public ApplControl +{ +public: + // Default constructable + ACCmdImpl(); + + // Destructor + virtual ~ACCmdImpl(); + + // Commands to control the application + virtual bool boot (const time_t scheduleTime, + const string& configID) const; + virtual bool define (const time_t scheduleTime) const; + virtual bool init (const time_t scheduleTime) const; + virtual bool run (const time_t scheduleTime) const; + virtual bool pause (const time_t scheduleTime, + const time_t waitTime, + const string& condition) const; + virtual bool release (const time_t scheduleTime) const; + virtual bool quit (const time_t scheduleTime) const; + virtual bool shutdown (const time_t scheduleTime) const; + virtual bool snapshot (const time_t scheduleTime, + const string& destination) const; + virtual bool recover (const time_t scheduleTime, + const string& source) const; + + virtual bool reinit (const time_t scheduleTime, + const string& configID) const; + virtual bool replace (const time_t scheduleTime, + const string& processList, + const string& nodeList, + const string& configID) const; + virtual bool cancelCmdQueue () const; + + // Define a generic way to exchange info between client and server. + string askInfo (const string& keylist) const; + +private: + // Copying is not allowed + ACCmdImpl(const ACCmdImpl& that); + ACCmdImpl& operator=(const ACCmdImpl& that); + +}; + + +// @} addtogroup + } // namespace ACC +} // namespace LOFAR + +#endif diff --git a/MAC/APL/Appl_Controller/src/ACDaemon.cc b/MAC/APL/Appl_Controller/src/ACDaemon.cc new file mode 100644 index 0000000000000000000000000000000000000000..341312a856902b3b5c1f1fb943840e45b53f791f --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ACDaemon.cc @@ -0,0 +1,262 @@ +//# ACDaemon.cc: launches Application Controllers on demand. +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +//# Includes +#include <Common/LofarLogger.h> +#include <Common/LofarLocators.h> +#include <Common/SystemUtil.h> +#include <ApplCommon/LofarDirs.h> +#include <ALC/ACRequest.h> +#include "ACDaemon.h" +#include "ACDaemonComm.h" +#include "forkexec.h" + +namespace LOFAR { + namespace ACC { + +ACDaemon::ACDaemon(const string& progName) : + itsListener (0), + itsPingSocket (0), + itsParamSet (new ParameterSet), + itsACPool (0) +{ + // Read in the parameterfile with network parameters. + ConfigLocator aCL; + string configFile(progName + ".conf"); + LOG_DEBUG_STR("Using parameterfile: "<< configFile <<"-->"<< aCL.locate(configFile)); + itsParamSet->adoptFile(aCL.locate(configFile)); // May throw + + // Open listener for AC requests. + itsListener = new Socket("ACdaemon", + itsParamSet->getString("ACDaemon.requestportnr"), + Socket::TCP); + + // Open listener for ping from AC's. + itsPingSocket = new Socket("ACdaemon", + itsParamSet->getString("ACDaemon.pingportnr"), + Socket::UDP); + + // Try to read in an old administration if it is available. + itsACPool = new ACRequestPool(itsParamSet->getInt32("ACDaemon.ACpoolport"), + itsParamSet->getInt32("ACDaemon.ACpoolsize")); + itsAdminFile = "../etc/" + itsParamSet->getString("ACDaemon.adminfile"); + itsACPool->load(itsAdminFile); + +} + +ACDaemon::~ACDaemon() +{ + if (itsListener) { delete itsListener; } + if (itsPingSocket) { delete itsPingSocket; } + if (itsParamSet) { delete itsParamSet; } + if (itsACPool) { delete itsACPool; } +} + +void ACDaemon::doWork() throw (Exception) +{ + // Setup some values we need in the main loop + int32 cleanTime = itsParamSet->getTime("ACDaemon.alivetimeout"); + int32 warnTime = cleanTime / 2; + + // Prepare a fd_set for select + itsConnSet.add(itsListener->getSid()); + itsConnSet.add(itsPingSocket->getSid()); + + LOG_INFO ("ACDaemon: entering main loop"); + + while (true) { + // wait for request or ping + FdSet readSet(itsConnSet); + struct timeval tv; // prepare select-timer + tv.tv_sec = 60; // this must be IN the while loop + tv.tv_usec = 0; // because select will change tv + int32 selResult = select(readSet.highest()+1, readSet.getSet(), 0, 0, &tv); + + // -1 may be an interrupt or a program-error. + if ((selResult == -1) && (errno != EINTR)) { + THROW(Exception, "ACDaemon: 'select' returned serious error: " << + errno << ":" << strerror(errno)); + } + + if (selResult == -1) { // EINTR: ignore + continue; + } + + // Ping from an AC? + if (readSet.isSet(itsPingSocket->getSid())) { + handlePingMessage(); + } + + // Request for new AC? + if (readSet.isSet(itsListener->getSid())) { + handleACRequest(); + } + + // cleanup AC's that did not respond for 5 minutes. + if (itsACPool->cleanup(warnTime, cleanTime)) { + itsACPool->save(itsAdminFile); + } + + } // while +} + +// +// handlePingMessage (on UDP socket) +// +void ACDaemon::handlePingMessage() +{ + LOG_TRACE_FLOW("Ping???"); + + // Read name of AC from ping message + char buffer[ACREQUESTNAMESIZE+1]; + int32 btsRead = itsPingSocket->read(static_cast<void*>(&buffer), + ACREQUESTNAMESIZE+1); + if (btsRead != ACREQUESTNAMESIZE+1) { + LOG_TRACE_STAT_STR("Read on ping port retured: " << btsRead << + " iso " << ACREQUESTNAMESIZE+1); + return; + } + + // search name in pool + ACRequest* ACRPtr = itsACPool->search(&buffer[1]); + if (!ACRPtr) { + LOG_TRACE_RTTI_STR ("Received ping from unknown applicationcontroller: " + << buffer << " (ignoring)"); + return; + } + + if (buffer[0] == AC_LEAVING_SIGN) { + LOG_DEBUG_STR(&buffer[1] << " leaves ACDaemon pool"); + itsACPool->remove(&buffer[1]); + itsACPool->save(itsAdminFile); + return; + } + + // assume first char is a AC_ALIVE_SIGN, remember ping time, update state. + LOG_TRACE_COND_STR("AC " << ACRPtr->itsRequester << " is alive"); + ACRPtr->itsPingtime = time(0); + ACRPtr->itsState = ACRok; + +} + +// +// handleACRequest() +// +void ACDaemon::handleACRequest() +{ + // Accept the new connection + Socket* dataSocket = itsListener->accept(-1); + ASSERTSTR(dataSocket, + "Serious problems on listener socket, exiting! : " << + itsListener->errstr()); + + // read request which should come very soon. + ACRequest aRequest; + uint16 reqSize = sizeof (ACRequest); + dataSocket->setBlocking(true); + uint32 btsRead = dataSocket->read(static_cast<void*>(&aRequest), reqSize); + if (btsRead != reqSize) { + LOG_INFO_STR ("ILLEGAL REQUEST SIZE (" << btsRead << + " iso " << reqSize << "), IGNORING REQUEST"); + delete dataSocket; + return; + } + aRequest.itsRequester[ACREQUESTNAMESIZE-1] = '\0'; // be save + + // is ACuser already known? Its a reconnect, don't start the AC + ACRequest* existingACR; + bool startAC = true; + if ((existingACR = itsACPool->search(aRequest.itsRequester))) { + LOG_DEBUG_STR("Reconnect of " << aRequest.itsRequester); + aRequest = *existingACR; // copy old info + existingACR->itsState = ACRnew; // reset ping state and time + existingACR->itsPingtime = time(0); // to prevent early removal + } + else { + // Its an unknown ACuser, add it to the pool + // assignment of node and portnr based on request params + if (!itsACPool->assignNewPort(&aRequest)) { + aRequest.itsAddr = 0; // return failure to user + aRequest.itsPort = 0; + startAC = false; + LOG_ERROR("No more ports available for application controllers"); + } + else { + // Register new AC in the pool and save the pool + aRequest.itsPingtime = time(0); + aRequest.itsState = ACRnew; + itsACPool->add(aRequest); + itsACPool->save(itsAdminFile); + } + } + + if (startAC) { + // Try to launch the AC, if it is already running the + // new one will fail and the user will reconnect on the old one. + constructACFile(&aRequest, string(LOFAR_SHARE_LOCATION) + "/" + aRequest.itsRequester+".param"); + string sysCommand = formatString(itsParamSet->getString("ACDaemon.command").c_str(), + aRequest.itsRequester); + LOG_DEBUG_STR("Executing '" << sysCommand << "'"); + int32 result = forkexec (sysCommand.c_str()); + LOG_DEBUG_STR ("result = " << result << ", errno = " << errno << ":" << strerror(errno)); + } + + // return the answer to the ACuser + uint32 btsWritten = dataSocket->write(static_cast<void*>(&aRequest), reqSize); + if (btsWritten != reqSize) { + LOG_WARN_STR ("REQUEST FOR " << aRequest.itsRequester << + " COULD NOT BE WRITTEN (" << btsWritten << " iso " << reqSize << ")"); + } + + delete dataSocket; +} + +// +// constructACFile(filename) +// +void ACDaemon::constructACFile(const ACRequest* anACR, + const string& aFilename) { + ParameterSet ACPS; + + uint16 backlog = anACR->itsNrProcs / 10; + backlog = MAX (5, MIN (backlog, 100)); + + // TODO: Calculate processportnr + + ACPS.add(KVpair("AC.backlog", backlog)); + ACPS.add(KVpair("AC.node", myHostname(false))); + ACPS.add(KVpair("AC.userportnr", ntohs(anACR->itsPort))); + ACPS.add(KVpair("AC.processportnr", 100+ntohs(anACR->itsPort))); // TODO + + ACPS.add("AC.pinghost", myHostname(false)); + ACPS.add("AC.pingportnr", itsParamSet->getString("ACDaemon.pingportnr")); + ACPS.add("AC.pinginterval", itsParamSet->getString("ACDaemon.aliveinterval")); + ACPS.add("AC.pingID", anACR->itsRequester); + + ACPS.writeFile (aFilename); +} + + } // namespace ACC +} // namespace LOFAR diff --git a/MAC/APL/Appl_Controller/src/ACDaemon.conf b/MAC/APL/Appl_Controller/src/ACDaemon.conf new file mode 100644 index 0000000000000000000000000000000000000000..fa1b366bf8de7b9623ecb4059a5ffd3e5c38d0b6 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ACDaemon.conf @@ -0,0 +1,11 @@ +# +# Parameter file for ACDaemon +# +ACDaemon.requestportnr = 3800 # for ACRequests (TCP) +ACDaemon.pingportnr = 3801 # for AC's to say they are alive (UDP) +ACDaemon.ACpoolport = 3820 # ports the AC may use. +ACDaemon.ACpoolsize = 30 # size of AC port pool +ACDaemon.aliveinterval = 1 m # for AC +ACDaemon.alivetimeout = 5 m # remove AC after this period +ACDaemon.adminfile = ACD.admin # file to survive power failures +ACDaemon.command = ./ApplController %s >&/dev/null & diff --git a/MAC/APL/Appl_Controller/src/ACDaemon.h b/MAC/APL/Appl_Controller/src/ACDaemon.h new file mode 100644 index 0000000000000000000000000000000000000000..a84c6777b6d31653d4184ed03233c1c1831c5381 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ACDaemon.h @@ -0,0 +1,101 @@ +//# ACDaemon.h: Daemon for launching Application Controllers +//# +//# Copyright (C) 2002-2005 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# Note: This source is read best with tabstop 4. +//# +//# $Id$ + +#ifndef LOFAR_ACCBIN_ACDAEMON_H +#define LOFAR_ACCBIN_ACDAEMON_H + +// \file +// Daemon for launching Application Controllers + +//# Never #include <config.h> or #include <lofar_config.h> in a header file! +//# Includes +#include <Common/Exception.h> +#include <Common/Net/Socket.h> +#include <Common/Net/FdSet.h> +#include <Common/ParameterSet.h> +#include "ACRequestPool.h" + + +namespace LOFAR { + namespace ACC { +// \addtogroup ACCbin +// @{ + + +// The ACDaemon class implements a small daemon that wait for a request message +// and starts up an Application controller according to the request. +// The ACDaemon is fully controlled by the ParameterSet it receives during +// startup. +class ACDaemon +{ +public: + // Creates an ACDaemon object that start listening on the port mentioned + // in the ParameterSet. + explicit ACDaemon(const string& progName); + + // Destructor. + ~ACDaemon(); + + // Its normal (never ending) loop. + void doWork() throw (Exception); + +private: + // Construct a parameterfile for the AC controller. + void constructACFile(const ACRequest* anACR, + const string& aFilename); + + void handlePingMessage(); + void handleACRequest(); + + // Copying is not allowed + ACDaemon(const ACDaemon& that); + + // Copying is not allowed + ACDaemon& operator=(const ACDaemon& that); + + //# --- Datamembers --- + // The listener socket to receive the requests on. + Socket* itsListener; + + // The ping socket to receive the still-alive packages on. + Socket* itsPingSocket; + + // The parameterSet that was received during start up. + ParameterSet* itsParamSet; + + // The list with current active Application Controllers + ACRequestPool* itsACPool; + + // File descriptor set of connected sockets + FdSet itsConnSet; + + // Name of the administration file forr surviving restarts. + string itsAdminFile; +}; + +// @} addgroup + } // namespace ACC +} // namespace LOFAR + +#endif diff --git a/MAC/APL/Appl_Controller/src/ACDaemon.log_prop b/MAC/APL/Appl_Controller/src/ACDaemon.log_prop new file mode 100644 index 0000000000000000000000000000000000000000..f3755cbd6b73c997bf784c06d4ef9a18e0e18bdf --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ACDaemon.log_prop @@ -0,0 +1,24 @@ + +# Configure the loggers +log4cplus.rootLogger=INFO, STDOUT, FILE +log4cplus.logger.TRC=INFO +log4cplus.logger.LCS.Common=FATAL, STDOUT, FILE + +# Define the appenders +log4cplus.appender.STDOUT=log4cplus::ConsoleAppender +log4cplus.appender.STDOUT.layout=log4cplus::PatternLayout +log4cplus.appender.STDOUT.layout.ConversionPattern=%D{%d-%m %H:%M:%S.%q} %-5p %c{3} - %m [%.25l]%n + +log4cplus.appender.STDERR=log4cplus::ConsoleAppender +log4cplus.appender.STDERR.layout=log4cplus::PatternLayout +log4cplus.appender.STDERR.layout.ConversionPattern=%D{%d-%m %H:%M:%S.%q} %-5p %c{3} - %m [%.25l]%n +log4cplus.appender.STDERR.logToStdErr=true + +log4cplus.appender.FILE=log4cplus::RollingFileAppender +log4cplus.appender.FILE.File=/opt/lofar/var/log/${LOG4CPLUS_LOGFILENAME}.log +log4cplus.appender.FILE.MaxFileSize=10MB +log4cplus.appender.FILE.MaxBackupIndex=2 +log4cplus.appender.FILE.layout=log4cplus::PatternLayout +log4cplus.appender.FILE.layout.ConversionPattern=%x %D{%d-%m %H:%M:%S.%q} %-5p %c{3} - %m [%.25l]%n + +log4cplus.appender.DUMP=log4cplus::NullAppender diff --git a/MAC/APL/Appl_Controller/src/ACDaemonComm.cc b/MAC/APL/Appl_Controller/src/ACDaemonComm.cc new file mode 100644 index 0000000000000000000000000000000000000000..41e8a8e9cac1ccd01f9099b7da2c5465d9578bde --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ACDaemonComm.cc @@ -0,0 +1,69 @@ +//# ACDaemonComm.cc: Communication from AC to ACD. +//# +//# Copyright (C) 2005 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# Note: This source is read best with tabstop 4. +//# +//# $Id$ + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +//# Includes +#include <Common/LofarLogger.h> +#include"ACDaemonComm.h" + +namespace LOFAR { + namespace ACC { + +ACDaemonComm::ACDaemonComm(const string& host, + const string& port, + const string& ID) : + itsDaemonSocket(new Socket("DaemonPing", host, port, Socket::UDP)) +{ + ASSERTSTR(itsDaemonSocket->ok(), "Can't open ping socket with ACdaemon"); + + itsPingID[0] = AC_ALIVE_SIGN; + strncpy (&itsPingID[1], ID.c_str(), ACREQUESTNAMESIZE-1); + itsPingID [ACREQUESTNAMESIZE] = '\0'; +} + +ACDaemonComm::~ACDaemonComm() +{ + if (itsDaemonSocket) { + delete itsDaemonSocket; + } +} + +bool ACDaemonComm::sendPing() +{ + return (itsDaemonSocket->write(itsPingID, ACREQUESTNAMESIZE+1) == + ACREQUESTNAMESIZE+1); +} + +void ACDaemonComm::unregister() +{ + itsPingID[0] = AC_LEAVING_SIGN; + sendPing(); +} + + + + } // namespace ACC +} // namespace LOFAR diff --git a/MAC/APL/Appl_Controller/src/ACDaemonComm.h b/MAC/APL/Appl_Controller/src/ACDaemonComm.h new file mode 100644 index 0000000000000000000000000000000000000000..9cd482827b1777315e23a63a8845fda1ecd2cce1 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ACDaemonComm.h @@ -0,0 +1,77 @@ +//# ACDaemonComm.h: Communication with ACDaemon +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# Note: This source is read best with tabstop 4. +//# +//# $Id$ + +#ifndef LOFAR_ACCBIN_ACDAEMONCOMM_H +#define LOFAR_ACCBIN_ACDAEMONCOMM_H + +// \file +// Small class that implements the communication to the ACDaemon (from the +// Application Controller). + +//# Never #include <config.h> or #include <lofar_config.h> in a header file! +//# Includes +#include <Common/Net/Socket.h> +#include <ALC/ACRequest.h> + +using namespace LOFAR::ACC::ALC; + +namespace LOFAR { + namespace ACC { + +// \addtogroup ACCbin +// @{ + +//# used in ping command +#define AC_ALIVE_SIGN ' ' +#define AC_LEAVING_SIGN '~' + +// Implements the communication from the Application Controller to the ACDaemon. +// The Application Controller needs to send pings to the ACDaemon at regular +// intervals and must unregister at the daemon when it stops. +class ACDaemonComm +{ +public: + ACDaemonComm(const string& host, + const string& port, + const string& ID); + ~ACDaemonComm(); + + bool sendPing(); + void unregister(); + +private: + // Copying is not allowed + ACDaemonComm(const ACDaemonComm& that); + ACDaemonComm& operator=(const ACDaemonComm& that); + + //# --- Datamembers --- + Socket* itsDaemonSocket; + char itsPingID [ACREQUESTNAMESIZE+1]; +}; + +// @} addtogroup + } // namespace ACC +} // namespace LOFAR + +#endif diff --git a/MAC/APL/Appl_Controller/src/ACDaemonMain.cc b/MAC/APL/Appl_Controller/src/ACDaemonMain.cc new file mode 100644 index 0000000000000000000000000000000000000000..998e308fc81fa73a50ba0a035588b4892c315dcb --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ACDaemonMain.cc @@ -0,0 +1,99 @@ +//# ACDaemonMain.cc: daemon for launching application controllers. +//# +//# Copyright (C) 2002-2005 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +//# Includes +#include <sys/stat.h> // umask +#include <unistd.h> // fork, basename +#include <Common/LofarLogger.h> +#include <Common/LofarLocators.h> +#include <Common/Exception.h> +#include "ACDaemon.h" + +using namespace LOFAR; +using namespace LOFAR::ACC; + +// Use a terminate handler that can produce a backtrace. +Exception::TerminateHandler t(Exception::terminate); + +// +// MAIN (parameterfile) +// +int main (int /*argc*/, char* argv[]) { + + // Always bring up the logger first + ConfigLocator aCL; + string progName = basename(argv[0]); +#ifdef HAVE_LOG4CPLUS + string logPropFile(progName + ".log_prop"); +#else + string logPropFile(progName + ".debug"); +#endif +#ifdef HAVE_LOG4CPLUS + INIT_VAR_LOGGER (aCL.locate(logPropFile).c_str(), progName); +#else + INIT_LOGGER (aCL.locate(logPropFile).c_str()); + +#endif + LOG_DEBUG_STR("Initialized logsystem with: " << aCL.locate(logPropFile)); + + // Tell operator we are trying to start up. + LOG_INFO_STR("Starting up: " << argv[0]); + + try { +//#if REAL_DAEMON + pid_t pid = fork(); + switch (pid) { + case -1: // error + LOG_FATAL("Unable to fork daemon process, exiting."); + return (-1); + case 0: // child (the real daemon) + // do nothing; + break; + default: // parent + LOG_INFO_STR("Daemon succesfully started, pid = " << pid); + return (0); + } +//#endif + // TODO: active the next two calls. +// setsid(); // disconnect from terminalsession +// chdir("/"); // might be on a mounted file system + umask(0); // no limits + + ACDaemon theDaemon(progName); + + theDaemon.doWork(); + + LOG_INFO_STR("Shutting down: " << argv[0]); + } + catch (LOFAR::Exception& ex) { + LOG_FATAL_STR("Caught exception: " << ex); + LOG_FATAL ("Terminated by exception!"); + return (1); + } + + LOG_INFO("Terminated normally"); + return (0); + +} diff --git a/MAC/APL/Appl_Controller/src/ACRequestPool.cc b/MAC/APL/Appl_Controller/src/ACRequestPool.cc new file mode 100644 index 0000000000000000000000000000000000000000..db0f73a09c642df08548fd669cf933133117b99c --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ACRequestPool.cc @@ -0,0 +1,271 @@ +//# ACRequestPool.cc: one line description +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# Note: this source is read best with tabstop 4. +//# +//# $Id$ + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +//# Includes +#include <arpa/inet.h> +#include <Common/LofarLogger.h> +#include <Common/lofar_fstream.h> +#include <Common/SystemUtil.h> +#include <cstring> +#include "ACRequestPool.h" + +namespace LOFAR { + namespace ACC { + +ACRequestPool::ACRequestPool(uint16 firstPortNr, + uint16 nrOfPorts) : + itsFirstPort(firstPortNr), + itsLastPort (firstPortNr+nrOfPorts-1), + itsNextPort (firstPortNr) +{} + +ACRequestPool::~ACRequestPool() +{} + +// +// add(anACR) +// +// Add (a copy of) the given ACRequest to the pool +// +void ACRequestPool::add (const ACRequest& anACR) +{ + LOG_TRACE_OBJ_STR("ACRPool: add " << anACR.itsRequester); + + itsPool.push_back(new ACRequest(anACR)); // save ptr to copy +} + +// +// remove(anACRName) +// +// Remove the ACRequest with the given name from the pool +// +void ACRequestPool::remove (const string& anACRName) +{ + LOG_TRACE_OBJ_STR("ACRPool: remove " << anACRName); + + ACRequest* ACRPtr = search(anACRName); + if (ACRPtr) { + itsPool.remove(ACRPtr); // remove ptr from pool + delete ACRPtr; // remove object itself + } +} + +// +// search(anACRName) +// +// Searches for an ACRequest with the given name. Returns 0 if not found. +// +ACRequest* ACRequestPool::search (const string& anACRName) +{ + iterator iter = itsPool.begin(); + + while (iter != itsPool.end()) { + // Does name match? + if (!strcmp(anACRName.c_str(), (*iter)->itsRequester)) { + return (*iter); + } + iter++; + } + + return (0); // No match found +} + +// +// save(filename) +// +// Save the pool to survive power failures. +// +bool ACRequestPool::save (const string& aFilename) +{ + static bool showedWarning = false; + + ofstream oFile(aFilename.c_str(), ofstream::out | ofstream::trunc + | ofstream::binary); + + // If the file can not be opened warn the operator once. + if (!oFile) { + if (!showedWarning) { + LOG_WARN("ACDaemon is not powerfailure save!"); + showedWarning = true; + } + return (false); + } + + LOG_TRACE_RTTI_STR("Saving " << itsPool.size() << " ACrequests to file " + << aFilename); + + uint16 writeVersion = ACREQUEST_VERSION; + uint16 count = itsPool.size(); + oFile.write((char*)(&writeVersion), sizeof(writeVersion)); + oFile.write((char*)&count, sizeof(count)); + + iterator iter = itsPool.begin(); + while (iter != itsPool.end()) { + oFile.write((char*)(*iter), sizeof(ACRequest)); + iter++; + } + + oFile.close(); + return (true); +} + +// +// load (filename) +// +// readin file with old admin info +// +bool ACRequestPool::load (const string& aFilename) +{ + ifstream iFile(aFilename.c_str(), ifstream::in | ifstream::binary); + if (!iFile) { // No file is OK + return (false); // tell we did not load anything + } + + uint16 count; + uint16 readVersion; + + iFile.read((char*)&readVersion, sizeof(readVersion));// for future V. control + iFile.read((char*)&count, sizeof(count)); // nr elements in file + + LOG_INFO_STR("Loading " << count << " ACrequests from file " + << aFilename); + + while (count) { + ACRequest ACR; + + iFile.read((char*)&ACR, sizeof (ACRequest)); + ACR.itsPingtime = time(0); // reset pingtime + ACR.itsState = ACRloaded; + + // report what we loaded. + in_addr IPaddr; + IPaddr.s_addr = ACR.itsAddr; + LOG_INFO_STR("Application " << ACR.itsRequester << " was at " << + inet_ntoa(IPaddr) << "," << ntohs(ACR.itsPort)); + add (ACR); + --count; + } + + iFile.close(); + return (true); +} + +bool ACRequestPool::assignNewPort(ACRequest* anACR) +{ + // Controller always run on my own machine + anACR->itsAddr = myIPV4Address(); // network byte order + + uint16 startingPort = itsNextPort; + uint16 freePort = 0; // initialise, purely to avoid compiler warning + bool found = false; + do { + // scan pool to see if number is (still) in use. + iterator iter = itsPool.begin(); + bool inUse = false; + while (!inUse && iter != itsPool.end()) { + if ((*iter)->itsPort == itsNextPort) { + inUse = true; + } + ++iter; + } + if ((found = !inUse)) { + freePort = itsNextPort; + } + itsNextPort++; // increment nextPort for next round. + if (itsNextPort > itsLastPort) { + itsNextPort = itsFirstPort; + } + } while (!found && itsNextPort != startingPort); + + if (!found) { + LOG_ERROR ("Pool of TCP portnumber of ACDaemon is full. " + "Can not start a new Application Controller"); + return (false); + } + + // fill in portnumber + anACR->itsPort = htons(freePort); + + // log assignment + in_addr IPaddr; + IPaddr.s_addr = anACR->itsAddr; + LOG_INFO_STR (inet_ntoa(IPaddr) << ", " << freePort << + " assigned to " << anACR->itsRequester); + + return (true); +} + +// +// cleanupACPool() +// +// Remove AC's that did not repsond for more than 5 minutes. +// Returns true if one or more elements were removed from the pool. +// +bool ACRequestPool::cleanup(int32 warnTime, int32 cleanTime) +{ + LOG_TRACE_STAT("Checking sign of life of AC's"); + + time_t curTime = time(0); + bool modified = false; + + iterator iter = itsPool.begin(); + while (iter != itsPool.end()) { + // No sign of life for a long time anymore?? Remove from pool. + if ((curTime - (*iter)->itsPingtime) > cleanTime) { + // Don't complain about loaded (old) stuff + if ((*iter)->itsState != ACRloaded) { + LOG_INFO_STR ("No more pings from " << (*iter)->itsRequester << + ". Removing it from pool"); + } + + // iter is destroyed in remove, prepare new iter. + iterator tmp_iter = iter; + ++tmp_iter; + + // remove the AC from the pool + remove((*iter)->itsRequester); + modified = true; + iter = tmp_iter; + } + else { // When pings stay out a while inform operator + if ((curTime - (*iter)->itsPingtime) > warnTime) { + // Don't complain about loaded (old) stuff + if ((*iter)->itsState != ACRloaded) { + LOG_INFO_STR ("No sign of life from " << + (*iter)->itsRequester); + (*iter)->itsState = ACRlosing; + } + } + ++iter; + } + } + + return (modified); +} + + } // namespace ACC +} // namespace LOFAR diff --git a/MAC/APL/Appl_Controller/src/ACRequestPool.h b/MAC/APL/Appl_Controller/src/ACRequestPool.h new file mode 100644 index 0000000000000000000000000000000000000000..435680c6d52257b686176190beea20ab06809572 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ACRequestPool.h @@ -0,0 +1,92 @@ +//# ACRequestPool.h: small structure used for comm. with ACDaemon +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# Note: This source is read best with tabstop 4. +//# +//# $Id$ + +#ifndef LOFAR_ACCBIN_ACREQUESTPOOL_H +#define LOFAR_ACCBIN_ACREQUESTPOOL_H + +// \file +// Administrative pool of ACrequests used by the ACDaemon for managing the +// Application Controllers. + +//# Never #include <config.h> or #include <lofar_config.h> in a header file! +//# Includes +#include <Common/lofar_list.h> +#include <ALC/ACRequest.h> + +using namespace LOFAR::ACC::ALC; + +namespace LOFAR { + namespace ACC { +// \addtogroup ACCbin +// @{ + +// The ACRequestPool is internally used by the ACDeamon to manage the +// resources he owns. +class ACRequestPool +{ +public: + typedef list<ACRequest*>::iterator iterator; + typedef list<ACRequest*>::const_iterator const_iterator; + + ACRequestPool(uint16 firstPortnr, + uint16 nrOfPorts); + ~ACRequestPool(); + + // Element maintenance + void add (const ACRequest& anACR); + void remove (const string& anACRName); + + // Search for an ACRequest with the given name. If found a pointer to this + // ACRequest is returned, otherwise 0 is returned. + ACRequest* search (const string& anACRName); + + // Store and retrieve whole pool to/from a file for own use. + bool save (const string& aFilename); + bool load (const string& aFilename); + + bool assignNewPort (ACRequest* anACR); + bool cleanup (int32 warnTime, int32 cleanTime); + +private: + // Not default constructable, need portnumbers. + ACRequestPool(); + + // Copying is not allowed + ACRequestPool(const ACRequestPool& that); + + // Copying is not allowed + ACRequestPool& operator=(const ACRequestPool& that); + + list<ACRequest*> itsPool; + uint16 itsFirstPort; + uint16 itsLastPort; + uint16 itsNextPort; +}; + + +// @} addgroup + } // namespace ACC +} // namespace LOFAR + +#endif diff --git a/MAC/APL/Appl_Controller/src/ACcli.cc b/MAC/APL/Appl_Controller/src/ACcli.cc new file mode 100644 index 0000000000000000000000000000000000000000..dfcf191d466ee9de5d352658c49834c534b1341f --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ACcli.cc @@ -0,0 +1,137 @@ +//# ACcli.cc: commandline client to the ApplicationController +//# +//# Copyright (C) 2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +#include <lofar_config.h> +#include <Common/LofarLogger.h> +#include <Common/Exception.h> +#include <ALC/ACSyncClient.h> + +using namespace LOFAR; +using namespace LOFAR::ACC; +using namespace LOFAR::ACC::ALC; + +// Use a terminate handler that can produce a backtrace. +Exception::TerminateHandler t(Exception::terminate); + +void printUsage() { + cout << "This program is a commandline client to ACC. It can be used from shell scripts." << endl; + cout << endl; + cout << " Usage :" << endl; + cout << " ACcli <unique-id> <command> [options]" << endl; + cout << " " << endl; + cout << " where" << endl; + cout << " <unique-id> is the unique identifier used to connect to a particular process. It should be equal to the identifier used when the process was started using ACcli." << endl; + cout << " <command> is the command to be given to the process. It can be any of:" << endl; + cout << " boot <paramfile> get ready for starting the process. The next option should be the param file for that process." << endl; + cout << " define start the process and give it the define command" << endl; + cout << " init " << endl; + cout << " run " << endl; + cout << " cancel " << endl; + cout << " pause <condition> tell the program to pause when the condition is true. The program itself must be able to recognize the string." << endl; + cout << " release " << endl; + cout << " stop " << endl; + cout << " " << endl; +} + +int main (int argc, char *argv[]) { +#ifdef HAVE_LOG4CPLUS + INIT_VAR_LOGGER ("../etc/ACcli", basename(argv[0])); +#else + INIT_LOGGER ("../etc/ACcli"); +#endif + + if (argc < 3) { + printUsage(); + return 1; + } + + string myUniqueName(argv[1]); + string command(argv[2]); + + bool returnValue = true; + + // Number of processes the user will start + // uint16 itsNrProcs; + // Expected lifetime of application in minutes + // uint32 itsLifetime; + // Activity of AC (1/2/3: low/medium/high) + // uint16 itsActivityLevel; + // Architecture code (0 = Intel, 1 = Blue Gene) + // uint16 itsArchitecture; + + // Connect to AC + //ACSyncClient ACClient(myUniqueName, nProcs, lifeTime, activityLevel, arch); + ACSyncClient ACClient(myUniqueName, 10, 100, 1, 0); + + if (command == "boot"){ + // Boot(parameterfile): start nodes + if (argc < 4) { + printUsage(); + return 1; + } + returnValue = ACClient.boot(time(0L), argv[3]); + + } else if (command == "define") { + // Define : start processes + returnValue = ACClient.define(time(0L)); + + } else if (command == "init") { + // Init: let AP connect to each other + returnValue = ACClient.init(time(0L)); + + } else if (command == "run") { + // Run: do work + returnValue = ACClient.run(time(0L)); + + } else if (command == "cancel") { + // Cancel Command queue + returnValue = ACClient.cancelCmdQueue(); + + } else if (command == "pause") { + // Pause(condition,waitTime) + if (argc < 4) { + printUsage(); + return 1; + } + returnValue = ACClient.pause(time(0), 30, argv[3]); + + } else if (command == "release") { + // Cancel Command queue + returnValue = ACClient.release(time(0L)); + + } else if (command == "stop") { + // Quit: stop processes + returnValue = ACClient.quit(time(0)); + + } else { + printUsage(); + } + + if (returnValue) { + // everything ok + return 0; + } else { + // there was an error + return 1; + }; +} + diff --git a/MAC/APL/Appl_Controller/src/ACuserMenu.cc b/MAC/APL/Appl_Controller/src/ACuserMenu.cc new file mode 100644 index 0000000000000000000000000000000000000000..434111e61c1a0b345972c6ce01da0137cf3e6881 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ACuserMenu.cc @@ -0,0 +1,360 @@ +//# ACUserMenu.cc: Menu-program for manual testing an ACC controlled application. +//# +//# Copyright (C) 2004-2007 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +#include <Common/LofarLogger.h> +#include <Common/LofarLocators.h> +#include <Common/Exception.h> +#include <Common/hexdump.h> +#include <Common/StringUtil.h> +#include <ALC/ACSyncClient.h> +#include <time.h> +#include "myACClientFunctions.h" + +using namespace LOFAR; +using namespace LOFAR::ACC; +using namespace LOFAR::ACC::ALC; + +// Use a terminate handler that can produce a backtrace. +Exception::TerminateHandler t(Exception::terminate); + +myACClientFunctions myACF; +ApplControlClient* ACClient = 0; +bool connected = false; +bool IsSync = false; +time_t delayTime = 0; +string paramfile ("Observation-CygA.param"); + +// Note: function does nothin on sync-connections +void waitForAnswer() +{ + if (!IsSync) { + cout << "Waiting for result from command" << endl; + while (!ACClient->processACmsgFromServer()) { + ; + } + } + + if (delayTime) { + cout << "Command is placed on stack, waiting for real result" << endl; + while(!ACClient->processACmsgFromServer()) { + ; + } + } +} + + +// enter delayTime +void doDelayTime() +{ + cout << "Enter delaytime in seconds: "; + string timeString; + cin >> timeString; + delayTime = atol(timeString.c_str()); +} + +// Connect to AC +void doConnect() +{ + if (connected) { + cout << "Already connected!" << endl; + sleep (2); + return; + } + + cout << "(A)sync connection or (S)ync connection? "; + char comType; + cin >> comType; + + if (comType == 's' || comType == 'S') { + ACClient = new ACSyncClient("myUniqName", 10, 100, 1, 0); + IsSync = true; + } + else { + ACClient = new ACAsyncClient(&myACF, "myUniqName", 10, 100, 1, 0); + IsSync = false; + } + + connected = true; +} + +// Disconnect from AC +void doDisconnect() +{ + if (ACClient) { + delete ACClient; + } + + connected = false; +} + +// Boot(parameterfile): start nodes +void doBoot() +{ + cout << "Enter name of parameterfile (" << paramfile << "): "; + string aParFile; + cin.clear(); + cin >> aParFile; + if (aParFile.length() > 2) { + paramfile = aParFile; + } + + cout << "Sending boot command" << endl; + bool result = ACClient->boot(time(0L)+delayTime, paramfile); + if (result) { + waitForAnswer(); + } + if (result) { + cout << "Application processes should be running now" << endl; + } + else { + cout << "ERROR during boot command" << endl; + sleep (2); + } + +} + +// Define : start processes +void doDefine() +{ + cout << "Sending define command" << endl; + bool result = ACClient->define(time(0L)+delayTime); + if (result) { + waitForAnswer(); + } + if (result) { + cout << "Application processes should have claimed the resources" << endl; + } + else { + cout << "ERROR during define command" << endl; + sleep (2); + } +} + +// Init: let AP connect to each other +void doInit() +{ + cout << "Sending init command" << endl; + bool result = ACClient->init(time(0L)+delayTime); + if (result) { + waitForAnswer(); + } + if (result) { + cout << "Application processes should be in hot-standby mode" << endl; + } + else { + cout << "ERROR during init command" << endl; + sleep (2); + } +} + +// Run: do work +void doRun() +{ + cout << "Sending run command" << endl; + bool result = ACClient->run(time(0L)+delayTime); + if (result) { + waitForAnswer(); + } + if (result) { + cout << "Application processes must running now" << endl; + } + else { + cout << "ERROR during run command" << endl; + sleep (2); + } +} + +// Cancel Command queue +void doCancel() +{ + cout << "Sending CancelQueue command" << endl; + bool result = ACClient->cancelCmdQueue(); + if (result) { + waitForAnswer(); + } + if (result) { + cout << "Queue should be flushed by now." << endl; + } + else { + cout << "ERROR during cancelqueue command" << endl; + sleep (2); + } +} + +// Pause(condition,waitTime) +void doPause() +{ + string answer; + string option; + + // ask user condition + answer.clear(); + while (answer.empty()) { + cout << "What condition must be used? " << endl; + cout << "N now -> immediately" << endl; + cout << "A asap -> without corrupting anything" << endl; + cout << "T timestamp -> at some defined time" << endl; + getline(cin, answer); + if (answer == "N") { + option="now"; + } + else if (answer == "A") { + option = "asap"; + } + else if (answer == "T") { + cout << "Over how many seconds should the application stop?" << endl; + cout << " ( based on the timestamp off the datasamples) : " << endl; + answer.clear(); + getline(cin, answer); + uint32 sampleTime = time(0L) + atol(answer.c_str()); + option = "timestamp=" + toString(sampleTime); + } + } + + cout << "Sending pause(" << option << ") command" << endl; + bool result = ACClient->pause(time(0)+delayTime,30,option); + if (result) { + waitForAnswer(); + } + if (result) { + cout << "Application processes should be paused by now" << endl; + } + else { + cout << "ERROR during pause command" << endl; + sleep (2); + } +} + +// Release: free resources +void doRelease() +{ + cout << "Sending release command" << endl; + bool result = ACClient->release(time(0)+delayTime); + if (result) { + waitForAnswer(); + } + if (result) { + cout << "Application processes released their resources." << endl; + } + else { + cout << "ERROR during release command" << endl; + sleep (2); + } + +} + + +// Quit: stop processes +void doStop() +{ + cout << "Sending quit command" << endl; + bool result = ACClient->quit(time(0)+delayTime); + if (result) { + waitForAnswer(); + } + if (result) { + cout << "Application processes should be killed by now" << endl; + } + else { + cout << "ERROR during quit command" << endl; + sleep (2); + } + +} + + +void showMenu() +{ + cout << endl << endl << endl; + cout << formatString("%s connection with AC\n", + connected ? (IsSync ? "Sync" : "Async") : "No"); + cout << "Time delay: " << delayTime << endl << endl; + + + cout << "Commands" << endl; + if (!connected) { + cout << "c Connect to AC" << endl << endl; + } + else { + cout << "D Disconnect from AC" << endl << endl; + + cout << "T Set delayTime for commands" << endl << endl; + + cout << "b Boot(parameterfile): start processes" << endl; + cout << "d Define : allocate resources [CLAIM]" << endl; + cout << "i Init : Go to hot-standby [PREPARE]" << endl; + cout << "r Run : do work [RESUME]" << endl; + cout << "p Pause(condition,waitTime) [SUSPEND]" << endl; + cout << "R Release: free resources [RELEASE]" << endl; + cout << "s Quit : stop processes [QUIT]" << endl << endl; + cout << "c Cancel : Command Queue" << endl; + } + + cout << "q Quit this program" << endl << endl; + + cout << "Enter letter of your choice: "; +} + +int main (int /*argc*/, char *argv[]) { + ConfigLocator aCL; + string progName(basename(argv[0])); +#ifdef HAVE_LOG4CPLUS + string logPropFile(progName + ".log_prop"); + INIT_LOGGER (aCL.locate(logPropFile).c_str()); +#else + string logPropFile(progName + ".debug"); + INIT_LOGGER (aCL.locate(logPropFile).c_str()); +#endif + + char aChoice = ' '; + while (aChoice != 'q') { + showMenu(); + cin >> aChoice; + switch (aChoice) { + case 'C': + case 'c': if (!connected) { + doConnect(); + } + else { + doCancel(); + } + break; + case 'D': doDisconnect(); break; + case 'T': doDelayTime(); break; + case 'b': doBoot(); break; + case 'd': doDefine(); break; + case 'i': doInit(); break; + case 'r': doRun(); break; + case 'p': doPause(); break; + case 'R': doRelease(); break; + case 's': doStop(); break; + } + } + + doDisconnect(); + +} + + diff --git a/MAC/APL/Appl_Controller/src/ACuserMenu.log_prop b/MAC/APL/Appl_Controller/src/ACuserMenu.log_prop new file mode 100644 index 0000000000000000000000000000000000000000..c405e630daa8bd5eedb288ad866813771742b006 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ACuserMenu.log_prop @@ -0,0 +1,24 @@ + +# Configure the loggers +log4cplus.rootLogger=DEBUG, STDOUT, FILE +log4cplus.logger.TRC=INFO +log4cplus.logger.LCS.Common=FATAL, STDOUT, FILE + +# Define the appenders +log4cplus.appender.STDOUT=log4cplus::ConsoleAppender +log4cplus.appender.STDOUT.layout=log4cplus::PatternLayout +log4cplus.appender.STDOUT.layout.ConversionPattern=%D{%d-%m %H:%M:%S.%q} %-5p %c{3} - %m [%.25l]%n + +log4cplus.appender.STDERR=log4cplus::ConsoleAppender +log4cplus.appender.STDERR.layout=log4cplus::PatternLayout +log4cplus.appender.STDERR.layout.ConversionPattern=%D{%d-%m %H:%M:%S.%q} %-5p %c{3} - %m [%.25l]%n +log4cplus.appender.STDERR.logToStdErr=true + +log4cplus.appender.FILE=log4cplus::RollingFileAppender +log4cplus.appender.FILE.File=/opt/lofar/var/log/${LOG4CPLUS_LOGFILENAME}.log +log4cplus.appender.FILE.MaxFileSize=10MB +log4cplus.appender.FILE.MaxBackupIndex=2 +log4cplus.appender.FILE.layout=log4cplus::PatternLayout +log4cplus.appender.FILE.layout.ConversionPattern=%x %D{%d-%m %H:%M:%S.%q} %-5p %c{3} - %m [%.25l]%n + +log4cplus.appender.DUMP=log4cplus::NullAppender diff --git a/MAC/APL/Appl_Controller/src/APAdmin.cc b/MAC/APL/Appl_Controller/src/APAdmin.cc new file mode 100644 index 0000000000000000000000000000000000000000..7aab806a00273b08799df0457e93bb7ec3b7a350 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/APAdmin.cc @@ -0,0 +1,150 @@ +//# APAdmin.cc: Enables a poll on a collection of dataholders. +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +//# Includes +#include <Common/LofarLogger.h> +#include "APAdmin.h" + +namespace LOFAR { + namespace ACC { + + +// +// APAdmin(Socket*) +// +// Constructs a new APAdmin object including a blank DH_ProcControl. The given +// Socket is attached to the APAdmin. +// +APAdmin::APAdmin(Socket* aSocket) : + itsName (""), + itsDHPC (new DH_ProcControl), + itsSocket (aSocket), + itsBytesToRead (0), + itsReadOffset (0), + itsReadingHeader(true), + itsState (APSconn) +{ + itsBytesToRead = itsDHPC->getHeaderSize(); // always start with the header + itsDHPC->init(); // construct DH layout +} + +// +// ~APAdmin() +// +APAdmin::~APAdmin() +{ + if (itsDHPC) { // the 'if's are superfluous but safer. + delete itsDHPC; + } + if (itsSocket) { + delete itsSocket; + } +} + + +// +// bool read () +// +// Tries to read the missing bytes from the Socket into the DataHolder. +// When the DataHolder is full true is returned, otherwise false. +// +bool APAdmin::read() +{ + LOG_TRACE_RTTI ("APAdmin:read"); + + if (itsState == APSdiscon || itsState == APSfail) { + LOG_TRACE_RTTI ("APAdmin:read:Socket disconnected or in fail state"); + return(false); + } + + itsState = APSread; + + int32 newBytes = itsSocket->read( + static_cast<char*>(itsDHPC->getDataPtr()) + itsReadOffset, + itsBytesToRead); + + if (newBytes < 0) { + if (newBytes != Socket::INCOMPLETE) { // serious error + LOG_TRACE_RTTI ("APAdmin:read:Setting socket in fail state"); + itsState = APSfail; + } + return (false); + } + + itsBytesToRead -= newBytes; // update admin + itsReadOffset += newBytes; + + if (itsBytesToRead > 0) { // still bytes missing? + return (false); + } + + if (!itsReadingHeader) { // Last part read? + itsBytesToRead = itsDHPC->getHeaderSize(); // prepare for next msg + itsReadingHeader = true; + itsReadOffset = 0; + itsDHPC->unpack(); // unpack the data + return (true); // tell msg is avail. + } + + // prepare for reading the remaining datapart. + itsReadingHeader = false; + int32 totalMsgSize = DataHolder::getDataLength (itsDHPC->getDataPtr()); + itsDHPC->resizeBuffer (totalMsgSize); + itsBytesToRead = totalMsgSize - itsDHPC->getHeaderSize(); + + return (read()); // call ourself for last part +} + +// +// bool write (buffer, size) +// +// Write the given bytes to the Socket. +// Returns false if socket is/turns_out_to_be disconnected. +// +bool APAdmin::write(void* aBuffer, + int32 aSize) +{ + LOG_TRACE_RTTI ("APAdmin:write"); + + if (itsState == APSdiscon || itsState == APSfail) { // check current state + LOG_TRACE_RTTI ("APAdmin:write:Socket disconnected or in fail state"); + return(false); + } + + int32 result = itsSocket->write(aBuffer, aSize); // do write + + if ((result < 0 ) && (result != Socket::INCOMPLETE)) { // serious error? + LOG_TRACE_RTTI ("APAdmin:write:Setting socket in fail state"); + itsState = APSfail; // set failure + return (false); + } + + itsState = APSwrite; // seems ok + return (true); +} + + + } // namespace ACC +} // namespace LOFAR diff --git a/MAC/APL/Appl_Controller/src/APAdmin.h b/MAC/APL/Appl_Controller/src/APAdmin.h new file mode 100644 index 0000000000000000000000000000000000000000..21de52204f9457cd0eb6bd660e88e90e940a035d --- /dev/null +++ b/MAC/APL/Appl_Controller/src/APAdmin.h @@ -0,0 +1,164 @@ +//# APAdmin.h: Internal administration of an AP for the Appl. Controller. +//# +//# Copyright (C) 2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# Note: This source is read best with tabstop 4. +//# +//# $Id$ + +#ifndef LOFAR_ACCBIN_APADMIN_H +#define LOFAR_ACCBIN_APADMIN_H + +// \file +// Internal information of an Application Process used by the +// Application Controller. + +//# Never #include <config.h> or #include <lofar_config.h> in a header file! +//# Includes +#include <Common/Net/Socket.h> +#include <PLC/DH_ProcControl.h> + +using namespace LOFAR::ACC::PLC; + +namespace LOFAR { + namespace ACC { +// \addtogroup ACCbin +// @{ + +// The APAdmin class uses a internal variable to register the state of the +// Socket. +enum APAState { + APSconn, APSdiscon, APSread, APSwrite, APSfail +}; + +// The APAdmin class is a collection of information the Application Controller +// needs for its administration of the Application Processes (AP's). +// It consists of a dataholder for reading data from the AP, a Socket the AP +// is connected to, and some less important flags and values. +class APAdmin +{ +public: + // An APAdmin is always needed after a new Socket was created after a + // call on a listener socket. So the constructor always needs this Socket. + // An empty dataholder for the AP is constructed automatically. + explicit APAdmin (Socket* aSocket); + + ~APAdmin(); + + // \name Functions for reading and writing to the AP. + // @{ + + // Tries to read the missing bytes from the Socket into the DataHolder. + // When the dataholder is full \c true is returned, otherwise \c false. + bool read(); + + // Writes the contents of the dataHolder to the Socket. Returns \c false + // if the Socket is/turns out to be disconnected. + bool write(void* aBuffer, + int32 aSize); + // @} + + // \name Accessor functions. + // @{ + void setName(const string& aName); + string getName() const; + DH_ProcControl* getDH() const; + int32 getSocketID() const; + APAState getState() const; + // @} + +private: + // Not default constructable; + APAdmin(); + + // Copying is not allowed + APAdmin(const APAdmin& that); + + // Copying is not allowed + APAdmin& operator=(const APAdmin& that); + + //# --- datamembers --- + // name of application process + string itsName; + + // The dataholder used for reading from the AP. + DH_ProcControl* itsDHPC; + + // Socket to AP is connected to + Socket* itsSocket; + + // Number of bytes still to read + uint32 itsBytesToRead; + + // Readoffset in the buffer + uint32 itsReadOffset; + + // Reading header or data part + bool itsReadingHeader; + + // State of APAdmin + APAState itsState; +}; + +//# -------------------- inline functions -------------------- +//# +//# getDH() +//# +inline DH_ProcControl* APAdmin::getDH() const +{ + return (itsDHPC); +} + +//# +//# getSocketID() +//# +inline int32 APAdmin::getSocketID() const +{ + return (itsSocket->getSid()); +} + +//# +//# getState() +//# +inline APAState APAdmin::getState() const +{ + return (itsState); +} + +//# +//# setName(name) +//# +inline void APAdmin::setName(const string& aName) +{ + itsName = aName; + itsState = APSconn; +} + +//# +//# getName() +//# +inline string APAdmin::getName() const +{ + return (itsName); +} + +// @} addgroup + } // namespace ACC +} // namespace LOFAR +#endif diff --git a/MAC/APL/Appl_Controller/src/APAdminPool.cc b/MAC/APL/Appl_Controller/src/APAdminPool.cc new file mode 100644 index 0000000000000000000000000000000000000000..7d44a541da44e9083444370024b28a339ce60aba --- /dev/null +++ b/MAC/APL/Appl_Controller/src/APAdminPool.cc @@ -0,0 +1,216 @@ +//# APAdminPool.cc: Enables a poll on a collection of dataholders. +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +//# Includes +#include <Common/LofarLogger.h> +#include "APAdminPool.h" + +namespace LOFAR { + namespace ACC { + +APAdminPool* APAdminPool::theirAPAdminPool = 0; + + +// +// getInstance() +// +APAdminPool& APAdminPool::getInstance() +{ + LOG_TRACE_OBJ ("APAdminPool::getInstance()"); + + if (theirAPAdminPool == 0) { + theirAPAdminPool = new APAdminPool; + } + + return (*theirAPAdminPool); +} + + +APAdminPool::APAdminPool() : + itsNrAcksToRecv(0), + itsLastCmd (PCCmdNone), + itsCurElement (0) +{ +} + +APAdminPool::~APAdminPool() +{} + +// +// void add(APAdmin*) +// +void APAdminPool::add (APAdmin* anAPAdmin) +{ + LOG_TRACE_RTTI_STR ("APAdminPool::add(" << anAPAdmin << ")"); + + itsAPAPool.insert (itsAPAPool.begin(), anAPAdmin); + itsReadMask.add(anAPAdmin->getSocketID()); // schedule for read +} + +// +// void remove(APAdmin*) +// +void APAdminPool::remove(APAdmin* anAPAdmin) throw(Exception) +{ + LOG_TRACE_RTTI_STR ("APAdminPool::remove(" << anAPAdmin << ")"); + + iterator iter = itsAPAPool.begin(); + + while (iter != itsAPAPool.end()) { // search dataholder + if (*iter == anAPAdmin) { + // Don't read, write or expect an Ack from this AP anymore + itsReadMask.remove(anAPAdmin->getSocketID()); + markAsOffline(anAPAdmin); + registerAck (itsLastCmd, *iter); + itsAPAPool.erase(iter); // remove from pool + setCurElement(itsCurElement); // boundary check + return; // ready + } + ++iter; + } + THROW(Exception, "DataHolder " << anAPAdmin << "not in pool"); +} + +// loop over all dataholders to see if data is ready. +// Start scan where we stopped last time. +// TODO:rewrite for select call +APAdmin* APAdminPool::poll(time_t waitTime) +{ + (void)waitTime; + + for (int i = itsCurElement; i < itsReadMask.count(); ++i) { + LOG_TRACE_COND_STR("poll at " << i); + if (itsAPAPool.at(i)->read()) { + setCurElement (i+1); + return (itsAPAPool.at(i)); + } + } + itsCurElement = 0; + + return (0); +} + +// +// void writeToAll(command, options) +// +void APAdminPool::writeToAll(PCCmd command, + const string& options) +{ + // Construct a APAdmin with the info to send to all processes. + DH_ProcControl DHCommand; + DHCommand.init(); + DHCommand.setCommand (command); + DHCommand.setOptions (options); + DHCommand.setResult (0); + DHCommand.pack(); // Construct the messagebuffer + + // Write the DataHolder to all Sockets in the pool. + void* aBuffer = DHCommand.getDataPtr(); + int32 aSize = DHCommand.getDataSize(); + iterator iter = itsAPAPool.begin(); + while (iter != itsAPAPool.end()) { // search dataholder + if (itsOnlineMask.isSet((*iter)->getSocketID())) { + // TODO: should we do something with the return value??? + (*iter)->write(aBuffer, aSize); + } + ++iter; + } + + startAckCollection(command); +} + + +// +// registerAck(command, apadmin) +// +bool APAdminPool::registerAck(PCCmd aCommand, + APAdmin* anAPAdmin) +{ + if (aCommand != itsLastCmd) { + if (itsLastCmd == PCCmdNone) { + LOG_DEBUG_STR("Process " << anAPAdmin->getName() << + " is late, Ack received for " << PCCmdName(aCommand)); + } + else { + LOG_WARN_STR("Process " << anAPAdmin->getName() << + " is out of sync, Ack received for " << PCCmdName(aCommand) << + " iso " << PCCmdName(itsLastCmd)); + } + return (false); + } + + if (itsAckList.isSet(anAPAdmin->getSocketID())) { + itsAckList.remove(anAPAdmin->getSocketID()); + --itsNrAcksToRecv; + } + return (true); +} + +// cleanup APAdmins that lost connection +// When one is found the DH is removed from the pool, the search is stopped +// and the DataHolder is returned to the user to do additional cleanup. +// +// NOTE: the algorithm is not very efficient because it exits on the first +// APAdmin that is disconnected. Repeated calls to this routine will repeatedly +// check the front of the pool. +// A more efficient way to do this is with a 'cur' variable as it is solved +// in the poll method. +APAdmin* APAdminPool::cleanup() +{ + APAdmin* anAPA; + for (int i = 0; i < itsReadMask.count(); ++i) { + anAPA = itsAPAPool.at(i); + if (anAPA->getState() == APSfail) { + LOG_DEBUG_STR("APAdminPool:cleanup " << i); + remove(anAPA); // remove it from the pool also + return(anAPA); // let user do other cleanup + } + } + return (0); // nothing deleted. +} + +// +// operator<< +// +std::ostream& operator<< (std::ostream& os, const APAdminPool& anAPAP) +{ + os << "Reading: " << const_cast<FdSet*> + (&(anAPAP.itsReadMask))->getSet()->fds_bits[0] << + "(" << anAPAP.itsReadMask.count() << ")" << endl; + os << "Writing: " << const_cast<FdSet*> + (&(anAPAP.itsOnlineMask))->getSet()->fds_bits[0] << + "(" << anAPAP.itsOnlineMask.count() << ")" << endl; + os << "AckColl: " << const_cast<FdSet*> + (&(anAPAP.itsAckList))->getSet()->fds_bits[0] << + "(" << anAPAP.itsNrAcksToRecv << ")" << endl; + if (anAPAP.itsLastCmd != PCCmdNone) { + os << "Command: " << anAPAP.itsLastCmd << endl; + } + + return (os); +} + + } // namespace ACC +} // namespace LOFAR diff --git a/MAC/APL/Appl_Controller/src/APAdminPool.h b/MAC/APL/Appl_Controller/src/APAdminPool.h new file mode 100644 index 0000000000000000000000000000000000000000..109e36f805ed5a64b515ab177db0bf4c59c4731d --- /dev/null +++ b/MAC/APL/Appl_Controller/src/APAdminPool.h @@ -0,0 +1,256 @@ +//# APAdminPool.h: Administation of the AP's for the Appl. controller +//# +//# Copyright (C) 2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# Note: This source is read best with tabstop 4. +//# +//# $Id$ + +#ifndef LOFAR_ACCBIN_APADMINPOOL_H +#define LOFAR_ACCBIN_APADMINPOOL_H + +// \file +// Collection of APAdmin classes used by the Application Controller for +// managing the Application Processes. + +//# Never #include <config.h> or #include <lofar_config.h> in a header file! +//# Includes +#include <Common/lofar_vector.h> +#include <Common/Net/FdSet.h> +#include "APAdmin.h" + +namespace LOFAR { + namespace ACC { +// \addtogroup ACCbin +// @{ + +// The APAdminPool object manages a pool of APAdmin objects. An APAdmin object +// is a pair of a DataHolder and a Socket. The surplus value of the APAdminPool +// is that is can perform an action of all the APAdmin objects it holds. +// Since the APAdminPool has knowledge of all the Processes and its connection +// state it is extended with administrative functions for collecting ACK +// messages. +class APAdminPool +{ + typedef vector<APAdmin*> APAList; + typedef vector<APAdmin*>::iterator iterator; + +public: + // APAdminPool is a singleton class. + static APAdminPool& getInstance(); + virtual ~APAdminPool(); + + // \name Maintenance on the APAdmin members + // Functions for maintaining the pool of APAdmin objects. + // @{ + + // Adds the given APAdmin to the pool. + void add (APAdmin* anAPAdmin); + + // Removes the given APAdmin from the pool. Throws an exception if the + // APAdmin is not in the pool. + void remove(APAdmin* anAPAdmin) throw(Exception); + // @} + + // \Managing process state + // The APAdminPool knows of every connected process if it is ready to + // receive messages of not. + // @{ + + // The given APAdmin is ready to receive commands + void markAsOnline (APAdmin* anAPAdmin); + + // Don't send any more messages to this APAdmin. + void markAsOffline(APAdmin* anAPAdmin); + // @} + + // \name Functions for Ack administration + // @{ + + // When calling \c startAckCollection all processes that are marked as + // 'online' are also marked 'shouldSendAnAck'. The Acks that are send by + // the processes should be a respons on command \c aCommand. + void startAckCollection(PCCmd aCommand); + + // Tell the APAdminPool that on this APAdmin an Ack message was received + // for the given command. Tell the APAdminPool that on this APAdmin an Ack + // message was received. Registering an Ack for the wrong command has no + // effect. + bool registerAck (PCCmd aCommand, + APAdmin* anAPAdmin); + + // Cancel the collection of Acks. When Acks are registered after this call + // they have no effect anymore. + void stopAckCollection (); + + // Check if all Acks are received. + bool allAcksReceived (); + // @} + + // \name Actions on the pool + // The following actions are executed on all elements of the pool. + // @{ + + // Continues its previous poll-sequence on the elements in its pool. + // Returns with a pointer to the APAdmin that has received a complete + // message. Returns 0 if the end of the pool is reached.<br> + // To do a single read on all elements of the pool: + // \code + // while ((activeAP = myAPAdminPool.poll()) { + // ... do something with the data of the 'activeAP' + // } + // \endcode + APAdmin* poll (time_t waitTime); + + // Continues its previous cleanup-sequence on the elements in its pool. + // Returns with a pointer to the APAdmin that has received a complete + // message. Returns 0 if the end of the pool is reached.<br> + // To do a single read on all elements of the pool: + // \code + // while ((deadAP = myAPAdminPool.cleanup()) { + // ... do some final task with 'deadAP' + // delete deadAP + // } + // \endcode + APAdmin* cleanup (); + + // Contructs a DH_ProcControl object from the passed arguments and sends + // this dataholder to all elements in its pool. + // After sending it to all elements \c startAckCollection is called to + // remember from with processes a respons is expected. + void writeToAll(PCCmd command, + const string& options); + // @} + + // \name Accessor methods + // @{ + + // Returns the number of processes in its pool. + uint16 processCount(); + + // Returns the number of processes in its pool that have told that they + // are ready to receive data. + uint16 onlineCount (); + // @} + + friend std::ostream& operator<< (std::ostream& os, const APAdminPool& anAPAP); + +private: + // Not default contructable. + APAdminPool(); + + // Copying is not allowed. + APAdminPool(const APAdminPool& that); + + // Copying is not allowed. + APAdminPool& operator=(const APAdminPool& that); + + // Internal command for adjusting the index for the poll method. + void setCurElement(uint16 aValue); + + // The APAdminPool is a singleton. + static APAdminPool* theirAPAdminPool; + + APAList itsAPAPool; // vector of APAdmin objects + FdSet itsReadMask; // selector mask + FdSet itsOnlineMask; // Procs that are ready to receive commands + FdSet itsAckList; // Procs still to receive an Ack from + uint16 itsNrAcksToRecv; // Nr of Acks still to receive. + PCCmd itsLastCmd; // Last/current outstanding AP command + uint16 itsCurElement; // Last element we polled. +}; + +//# -------------------- inline functions -------------------- +//# +//# setCurElement(value) +//# +inline void APAdminPool::setCurElement(uint16 aValue) +{ + if (aValue >= itsReadMask.count()) { // check upper boundary + itsCurElement = 0; + } + else { + itsCurElement = aValue; + } +} + +//# +//# processCount +//# +inline uint16 APAdminPool::processCount() +{ + return (itsReadMask.count()); +} + +//# +//# onlineCount +//# +inline uint16 APAdminPool::onlineCount() +{ + return (itsOnlineMask.count()); +} + +//# +//# markAsOnline(APAdmin*) +//# +inline void APAdminPool::markAsOnline(APAdmin* anAPAdmin) +{ + itsOnlineMask.add(anAPAdmin->getSocketID()); // schedule for writes +} + +//# +//# markAsOffLine(apadmin) +//# +inline void APAdminPool::markAsOffline(APAdmin* anAPAdmin) +{ + itsOnlineMask.remove(anAPAdmin->getSocketID()); +} + +//# +//# startAckColection(command) +//# +inline void APAdminPool::startAckCollection(PCCmd aCommand) +{ + itsAckList = itsOnlineMask; + itsNrAcksToRecv = itsOnlineMask.count(); + itsLastCmd = aCommand; +} + +//# +//# stopAckCollection() +//# +inline void APAdminPool::stopAckCollection() +{ + itsAckList.clear(); + itsNrAcksToRecv = 0; + itsLastCmd = PCCmdNone; +} + +//# +//# allAcksReceived +//# +inline bool APAdminPool::allAcksReceived() +{ + return (itsLastCmd != PCCmdNone && itsNrAcksToRecv == 0); +} + +// @} addgroup + } // namespace ACC +} // namespace LOFAR +#endif diff --git a/MAC/APL/Appl_Controller/src/ApplController.cc b/MAC/APL/Appl_Controller/src/ApplController.cc new file mode 100644 index 0000000000000000000000000000000000000000..e8a705370b1631223b91d3b58ccf90057a39a48e --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ApplController.cc @@ -0,0 +1,872 @@ +//# ApplController.cc: Controls all processes of an application. +//# +//# Copyright (C) 2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +//# Includes +#include <Common/LofarLogger.h> +#include <Common/LofarLocators.h> +#include <Common/StringUtil.h> +#include <ApplCommon/LofarDirs.h> +#include <ALC/ACCmd.h> +#include <PLC/ProcControlComm.h> +#include <MACIO/MACServiceInfo.h> +#include <MACIO/KVT_Protocol.ph> +#include "ApplController.h" +#include "PR_Shell.h" // TODO: factory! +#include "PR_MPI.h" // TODO: factory! +#include "PR_BGL.h" // TODO: factory! +#include "ItemList.h" // @@ + +namespace LOFAR { + using namespace MACIO; + namespace ACC { + +// +// ApplController(Parameterset* aPS) +// +ApplController::ApplController(const string& configID) : + itsBootParamSet (new ParameterSet), + itsObsParamSet (new ParameterSet), + itsResultParamSet(new ParameterSet), + itsProcList (0), + itsACCmdImpl (new ACCmdImpl), + itsCmdStack (new CmdStack), + itsProcListener (0), + itsAPAPool (0), + itsServerStub (0), + itsDaemonComm (0), +#ifdef KVLOGGER + itsKVLogger (0), +#endif + itsCurTime (0), + itsIsRunning (false), + itsStateEngine (new StateEngine), + itsCurState (StateNone), + itsCurACMsg (0), + itsNrOfProcs (0) +{ + LOG_TRACE_OBJ ("ApplController constructor"); + + // Read in the parameterfile with network parameters + ConfigLocator CL; + string bootPSname = CL.locate (configID+".param"); + LOG_INFO_STR("Booting from " << bootPSname); + itsBootParamSet->adoptFile(bootPSname); // May throw + + // Get pointer to singleton APAdminPool + itsAPAPool = &(APAdminPool::getInstance()); + +} + + +// +// ApplController(Parameterset* aPS) +// +ApplController::~ApplController() +{ + LOG_TRACE_OBJ ("ApplController destructor"); + + if (itsBootParamSet) { delete itsBootParamSet; } + if (itsObsParamSet) { delete itsObsParamSet; } + if (itsResultParamSet) { delete itsResultParamSet; } + if (itsProcList) { delete itsProcList; } + if (itsACCmdImpl) { delete itsACCmdImpl; } + if (itsCmdStack) { delete itsCmdStack; } + if (itsProcListener) { delete itsProcListener; } + if (itsAPAPool) { delete itsAPAPool; } + if (itsServerStub) { delete itsServerStub; } + if (itsDaemonComm) { delete itsDaemonComm; } +#ifdef KVLOGGER + if (itsKVLogger) { delete itsKVLogger; } +#endif + if (itsCurACMsg) { delete itsCurACMsg; } + if (itsStateEngine) { delete itsStateEngine; } + +} + +// +// startupNetwork() +// +// Starts the listeners for the user side and the process side. +// +void ApplController::startupNetwork() +{ + LOG_TRACE_FLOW("ApplController:startupNetwork()"); + + // Setup listener for ACC user and wait (max 10 sec) for connection + itsServerStub = new ApplControlServer( + itsBootParamSet->getInt32("AC.userportnr"), + itsACCmdImpl); + + // Setup listener for application processes + itsProcListener = new Socket("APlistener", + itsBootParamSet->getString("AC.processportnr"), + Socket::TCP, + itsBootParamSet->getInt32("AC.backlog")); + ASSERTSTR(itsProcListener->ok(), + "Can't start listener for application processes"); + + // Setup communication channel with ACDaemon + itsDaemonComm = new ACDaemonComm( + itsBootParamSet->getString("AC.pinghost"), + itsBootParamSet->getString("AC.pingportnr"), + itsBootParamSet->getString("AC.pingID")); + + // client synchrone +#ifdef KVLOGGER + itsKVLogger = new EventPort(MAC_SVCMASK_KVTLOGGER, false, KVT_PROTOCOL, "", true); + ASSERTSTR(itsKVLogger, "Can't connect to KeyValueLogger"); +#endif + itsIsRunning = true; +} + + +// +// handleProcMessage(APAdmin*) +// +// Handles a message that was received from an application process +// +void ApplController::handleProcMessage(APAdmin* anAP) +{ + LOG_TRACE_FLOW_STR("ApplController:handleProcMessage(" << anAP->getName() << ")"); + + DH_ProcControl* DHProcPtr = anAP->getDH(); + PCCmd command = DHProcPtr->getCommand(); + bool ack = false; + + if (command & PCCmdResult) { + command = static_cast<PCCmd>(command ^ PCCmdResult); + ack = true; + LOG_TRACE_VAR_STR("Ack on command: " << command); + } + else { + LOG_TRACE_VAR_STR("command=" << command); + } + LOG_TRACE_VAR_STR(" options=" << DHProcPtr->getOptions()); + + switch (command) { + case PCCmdInfo: + // TODO: AP asks for some info, we should answer this + break; + + case PCCmdAnswer: + // TODO: AP returns an answer on a question we have sent it + break; + + case PCCmdBoot: + // send by the AP as soon as it is on the air (registerAtAC). + anAP->setName(DHProcPtr->getOptions()); // register name + itsAPAPool->markAsOnline(anAP); // mark it ready for reception + break; + + case PCCmdReport: + // TODO: AM.report (.....); + break; + + case PCCmdAsync: + // TODO: implement this + break; + + case PCCmdParams: { + // TODO: Write this information to the >>>> KeyValueLogger <<<< + ParameterSet resultParam; + resultParam.adoptBuffer(DHProcPtr->getOptions()); + resultParam.writeFile(itsObsParamSet->getString("ApplCtrl.resultfile"), true); + sendToKVLogger(resultParam); + break; + } + + case PCCmdQuit: // send by UnregisterAtAC + LOG_TRACE_OBJ("PCCmdQuit received"); + itsAPAPool->markAsOffline(anAP); // don't send new commands + itsAPAPool->registerAck(command, anAP); + itsResultParamSet->adoptBuffer(DHProcPtr->getOptions()); + break; + + default: + // The rest of the command should be an Ack on the outstanding command. + if (ack) { + // always register ack. + bool ackOnTime = itsAPAPool->registerAck(command, anAP); + + // note: the process can return three values: Ok/NotSupported/Error + // Handle NotSupported as Ok. + uint16 result = DHProcPtr->getResult(); + bool successful = ((result & (PcCmdMaskOk | PcCmdMaskNotSupported)) != 0); + + if (ackOnTime && !successful) { + sendExecutionResult(0, "Nack from process:" + anAP->getName()); + } + } + else { + LOG_WARN(formatString( + "Unexpected command (%04X) received from process (%s)", + command, anAP->getName().c_str())); + } + } // switch +} + +// +// sendExecutionResult(result) +// +// Send the given result to the AC-side and clears the neccesary timers +// and stacks so that further handling of the command is stopped. +// +void ApplController::sendExecutionResult(uint16 result, + const string& comment) +{ + LOG_DEBUG_STR("ApplController:sendExecutionResult(" << result << "," + << comment << ")"); + + // notify user + itsServerStub ->sendResult(itsCurACMsg->getCommand(), result, comment); + itsAPAPool ->stopAckCollection(); // stop collecting + itsCurState = StateNone; // reset Cmd state + itsStateEngine->reset(); // reset state Engine + if (itsCurACMsg) { // delete old command + delete itsCurACMsg; + itsCurACMsg = 0; + } +} + +// +// createParSubsets() +// +// Creates for each process of its ProcList a parameter file containing its +// own parameters. All the parameters from one process are in the masterPS +// at <procname_from_proclist>.xxx +// +// +void ApplController::createParSubsets() +{ + // Step 1: A parameterset for a process is constructed from three set: + // [A] the default params for the set of proces procName[0] + // [B] for multiple cmdline processes of the same type, the params for + // a specific process (procName[i]), these overule the previous set + // [C] additional info from the AC itself + + // ApplCtrl.application = applName + string applName = itsObsParamSet->getString("ApplCtrl.application"); + + // ApplCtrl.processes = [ procName ... ] + // procList contains a list of processes to control. Loop over this list + // and make a ProcRuler and ParsetFile for each process of this application. + string prevProcName; + ParameterSet basePS; + vector<string> nodes; + vector<string> procList = itsObsParamSet->getStringVector("ApplCtrl.processes"); + LOG_TRACE_VAR_STR("Found " << procList.size() << " processes"); + for (uint procIdx = 0; procIdx < procList.size(); procIdx++) { + LOG_TRACE_VAR_STR("Processing process:" << procList[procIdx]); + + // procList[x] = processName(x) | processName + // procName := processName + // nrProcs := x | 0 + string procName = procList[procIdx]; + int32 nrProcs = indexValue(procName, "()"); + rtrim(procName, "()0123456789"); + string procPrefix = applName +"." + procName; + + if (nrProcs == 0) { + itsNrOfProcs++; + } + else { + itsNrOfProcs += nrProcs; + } + + // The startstopType determines what information is put in the parsetfiles + // for the processes. + string startstopType = itsObsParamSet->getString(procPrefix+"._startstopType"); + LOG_DEBUG_STR("Creating parameterfile for process " << procName); + + // [A] Get the default parameters ( procName[0].* ) when procname changes + if (procName != prevProcName) { + LOG_TRACE_COND_STR("Making basePS for " << procName); + basePS.clear(); + basePS.adoptCollection(*itsObsParamSet); +// basePS = itsObsParamSet->makeSubset(procPrefix, procPrefix); +// LOG_TRACE_VAR_STR(basePS); + + // [C] additional info from the AC itself + basePS.replace(procPrefix+"._ACport", + itsBootParamSet->getString("AC.processportnr")); + basePS.replace(procPrefix+"._ACnode", itsBootParamSet->getString("AC.node")); + basePS.replace("_parsetPrefix", procPrefix+"."); + prevProcName = procName; + } + + string fileName = formatString("%s/%s-%s.parset", LOFAR_SHARE_LOCATION, procName.c_str(), basePS.getString("Observation.ObsID").c_str()); + + // --- cmdline --- + if (startstopType == "cmdline") { + if (nrProcs == 0) { + LOG_TRACE_COND_STR("Single cmdline process " << procName); + // This processSet is a single commandline process + // add procName.* params to parset for process. + ParameterSet myPS = itsObsParamSet->makeSubset(procPrefix+".", + procPrefix+"."); + LOG_TRACE_VAR_STR(myPS); + myPS.adoptCollection(basePS); + writeParSubset(myPS, procName, fileName); + + // construct ProcesRuler + itsProcRuler.add(PR_Shell(myPS.getString(procPrefix + "._hostname"), + procName, + myPS.getString(procPrefix + "._executable"), + fileName)); + + } else { + // There are multiple processes of this type + nodes = basePS.getStringVector(procPrefix+"._nodes"); + LOG_TRACE_COND_STR("Multiple(" << nrProcs << ") cmdline process " << procName); + for (int32 p = 1; p <= nrProcs; ++p) { + // [B] construct parameter subset with process specific settings + string pName = formatString("%s%d", procName.c_str(), p); +// string oldPPrefix = formatString("%s[%d].", procPrefix.c_str(), p); +// ParameterSet myPS(basePS); +// myPS.adoptCollection(itsObsParamSet->makeSubset(oldPPrefix, +// procPrefix+".")); + ParameterSet myPS = itsObsParamSet->makeSubset(procPrefix+".", + procPrefix+"."); + LOG_TRACE_VAR_STR(myPS); + myPS.adoptCollection(basePS); + + // copy the default PS and give it a new prefix +// myPS.adoptCollection(itsObsParamSet->makeSubset(procPrefix+".", +// procPrefix+".")); + fileName = formatString("%s/%s-%s.parset", LOFAR_SHARE_LOCATION, pName.c_str(), basePS.getString("Observation.ObsID").c_str()); + writeParSubset(myPS, pName, fileName); + + // note: nodes[] may be smaller than nrProcs. by taking the remainder + // of nodes.size() the nodes[] is made cyclic. + itsProcRuler.add(PR_Shell(nodes[(p-1)%nodes.size()], + pName, + myPS.getString(procPrefix + "._executable"), + fileName)); + } + } + + // IONProc processes do not connect to the ApplController. + itsNrOfProcs -= nrProcs ? nrProcs : 1; + } + + else if (startstopType == "mpirun") { + // This processSet is an MPI program + LOG_TRACE_COND_STR("mpi process " << procName); + // fill 'nodes' with all nodenames of variable _nodes. + nodes = basePS.getStringVector(procPrefix+"._nodes", true);// true:expand + + itsProcRuler.add(PR_MPI(basePS.getString(procPrefix + "._hostname"), + procName, + nodes, + basePS.getString(procPrefix + "._executable"), + fileName, + nrProcs)); + writeParSubset(basePS, procName, fileName); + // Storage processes do not connect to the ApplController. + itsNrOfProcs -= nrProcs ? nrProcs : 1; + } + + // --- cn --- + else if (startstopType == "bgl") { + // This processSet is a BG/L job + LOG_TRACE_COND_STR("bgl process " << procName); + itsProcRuler.add(PR_BGL(procName, + basePS.getString(procPrefix + "._executable"), + basePS.getString(procPrefix + ".workingdir"), + basePS.getString("Observation.ObsID"), + fileName, + nrProcs)); + writeParSubset(basePS, procName, fileName); + // CN processes do now connect to the ApplController. + //itsNrOfProcs -= nrProcs ? nrProcs : 1; + } + } // for processes +} + +// +// writeParSubset(ps, procName, fileName) +// +void ApplController::writeParSubset(ParameterSet ps, const string& procName, const string& fileName){ + // [C] Add AC parameters of any interest to process + // TODO add some more like hostname and others? + + // append the new prefix + ps.replace("_processName", procName); + + // Remove execute type from processes paramlist + ps.remove(procName+"._startstopType"); + ps.remove(procName+"._executable"); + + // Finally write process paramset to a file. + ps.writeFile(fileName); + + LOG_DEBUG_STR("Create parameterfile " << fileName); +} + +// +// writeResultFile +// +// Write the collected (end) results of the processes to the resultfile. +// +void ApplController::writeResultFile() +{ + // Save results from the processes to a file (append). + if (itsResultParamSet && itsObsParamSet && + itsObsParamSet->isDefined("ApplCtrl.resultfile")) { + itsResultParamSet->writeFile( + itsObsParamSet->getString("ApplCtrl.resultfile"), + true); + } +} + +// +// sendToKVLogger(parSet) +// +// Send the KV pairs to the KeyValueLogger +// +void ApplController::sendToKVLogger(ParameterSet& aResultPS) +{ + // loop over PS and construct a msgpool event. + ParameterSet::iterator iter = aResultPS.begin(); + ParameterSet::iterator end = aResultPS.end(); + KVTSendMsgPoolEvent poolEvent; + poolEvent.seqnr = 1; + poolEvent.nrElements = 0; + while (iter != end) { + poolEvent.keys().push_back(iter->first); + poolEvent.values().push_back(iter->second); + poolEvent.nrElements++; + iter++; + } + + // empty PS? + if (!poolEvent.nrElements) { + return; + } + + // send message and wait for answer. +#ifdef KVLOGGER + itsKVLogger->send(&poolEvent); + KVTSendMsgPoolAckEvent poolAck(*(itsKVLogger->receive())); + + if (poolAck.result != 0) { + LOG_ERROR_STR("Storing metadata in PVSS resulted in errorcode " << poolAck.result); + } +#endif +} + + +// +// startState (newMsg) +// +// Used to start a new state of an AC command. When newMsg is pointing at a +// received message the stateEngine is started for this new message. +// Is newMsg NULL we are in the middle of an state sequence, the new state +// is than already set in itsCurState. +// +// NOTE: A state is the handling of one command-reply sequence with the AP's. +// +void ApplController::startCmdState() +{ + LOG_TRACE_FLOW_STR ("ApplController:startCmdState(" << itsCurState << ")"); + + // execute the new state + switch (itsCurState) { + case StateNone: + break; + case StateInitController: + // read in the Application Parameter file + itsObsParamSet->adoptFile(itsCurACMsg->getOptions());// May throw + // CS1_HACK save name of parameterfile + itsObsPSfilename = itsCurACMsg->getOptions(); + LOG_DEBUG_STR("Observation parsetfilename=" << itsObsPSfilename); + // StateEngine needs to know the timeout values for the states + itsStateEngine->init(itsObsParamSet); + itsStateEngine->ready(); // report this state is ready. + break; + case StateCreatePSubset: + createParSubsets(); + itsStateEngine->ready(); // report this state is ready. + break; + case StateStartupAppl: + if (!itsProcRuler.startAll()) { + sendExecutionResult(0, "Startup failures"); + itsStateEngine->reset(); // no further processing + } + // the incomming acks decide the result of the start action + break; + case StateKillAppl: + sleep (5); // give procs some extra time + itsProcRuler.stopAll(); + itsStateEngine->ready(); // report this state is ready. + break; + case StateInfoCmd: + itsServerStub->handleMessage(itsCurACMsg); + itsStateEngine->reset(); // no further processing + break; + + case StateDefineCmd: + case StateInitCmd: + case StateRunCmd: + case StatePauseCmd: + case StateReleaseCmd: + case StateRecoverCmd: + case StateSnapshotCmd: + case StateReinitCmd: + case StateQuitCmd: + // if nothing is online we are ready + if (itsAPAPool->onlineCount() == 0) { + itsStateEngine->ready(); + break; + } + + if (itsCurState == StatePauseCmd) { + // overrule default wait time if set + if (itsCurACMsg->getWaitTime() > 0) { + itsStateEngine->setStateLifeTime(itsCurACMsg->getWaitTime()); + } + } + + // All these command must be sent to the AP's and the responses + // that come back will finally result in a Nack of the next state. + itsServerStub->handleMessage(itsCurACMsg); + break; + case NR_OF_STATES: // satisfy compiler + break; + } +} + +// +// acceptOrRefuseACMsg(command, passOwnership) +// +// Decides whether or not the given command may be executed in this stage. +// When execution is not allowed, e.g. because the previous command is still +// running than a Nack is send to the ACuser. Otherwise the command is started. +// +void ApplController::acceptOrRefuseACMsg(DH_ApplControl* anACMsg, + bool passOwnership) +{ + // what command should we execute? + ACCmd newCmd = anACMsg->getCommand(); + + // Special case: flush command queue? + if (newCmd == ACCmdCancelQueue) { + itsCmdStack->clear(); + // send result without doing anything else. + itsServerStub->sendResult(newCmd, AcCmdMaskOk, "Queue is flushed"); + return; + } + + // still commands in progress? + if (itsCurState != StateNone) { + // some command is running, has new command overrule 'rights'? + if ((newCmd != ACCmdQuit) && (newCmd != ACCmdPause)){ + // No overrule rights, reject new command + LOG_DEBUG_STR("Command rejected: Previous command is still running. itsCurState:" << itsCurState << ", newCmd:" << ACCmdName(newCmd)); + sendExecutionResult (0, "Previous command is still running"); + return; + } + } + + // store a copy of this message for the further states. + if (itsCurACMsg) { + delete itsCurACMsg; // delete previous message + } + if (passOwnership) { + itsCurACMsg = anACMsg; + } + else { + itsCurACMsg = anACMsg->makeDataCopy(); + } + + // Initialize the stateEngine and store our new state. + itsStateEngine->startSequence(newCmd); + itsCurState = itsStateEngine->getState(); + + // start appropriate action + startCmdState(); +} + +// +// doEventLoop() +// +// (Almost) never ending loop that executes the Application Controller functions. +// +void ApplController::doEventLoop() +{ + // Loop optimalisation when not waiting for ACK's + const uint16 loopDiff = 1; // poll AP 5 times less than AM + uint16 loopCounter = loopDiff; + + // prepare ping information for ACDaemon + time_t nextPing = 0; + int32 pingInterval = itsBootParamSet->getTime("AC.pinginterval"); + + while (itsIsRunning) { + checkForACCommands(); + // AP's are less important when no command is running. + if ((itsCurState != StateNone) || (loopCounter == 0)) { + checkForAPMessages(); + checkForConnectingAPs(); + checkForDisconnectingAPs(); + checkAckCompletion(); + } + checkStateTimer(); + checkCmdStack(); + checkStateEngine(); + + // Should daemon be tickled? + if (nextPing < time(0)) { + if (!itsDaemonComm->sendPing()) { + LOG_DEBUG("Ping message to ACD could not be written!"); + } + nextPing = time(0) + pingInterval; + } + + if (loopCounter == 0) { + loopCounter = loopDiff; + } + else { + --loopCounter; + } + + // temp debug info + LOG_TRACE_FLOW_STR(*itsAPAPool); + LOG_TRACE_FLOW_STR(*itsStateEngine); + LOG_TRACE_FLOW_STR("Ping at: " << timeString(nextPing)); + + // Only sleep when idle + if (itsCurState == StateNone) { + sleep (1); + } + } + + itsDaemonComm->unregister(); +} + +// +// checkForACCommands() +// +// See if the AC has send us a new command the AP's should execute. +// A received command may be scheduled or may start a new state-sequence. +// +void ApplController::checkForACCommands() +{ + // NOTE: The AC should guard the connection with the AM. When the + // AM disconnects a reconnect-timer should be started allowed the + // AM some time to reconnect before shutting down everything. + // Since the Socket of the AM is hidden behind TH_stuff we don't + // know anything anymore about its connection state. So it is not + // possible to implement this feature when using TH technology. + // + // The pollForMessage call does a read on the DH which will always + // check its connection first, and tries to connect if there is no + // connection (yet/anymore). This will ensure that we at least will + // pickup a (re)connect from the AM. + + LOG_TRACE_STAT("Polling user side"); + if (itsServerStub->pollForMessage()) { // new command received? + DH_ApplControl* newMsg = itsServerStub->getDataHolder(); + time_t execTime = newMsg->getScheduleTime(); + itsCurTime = time(0); + if (execTime <= itsCurTime) { // execute immediately? + LOG_TRACE_FLOW("Immediate command"); + acceptOrRefuseACMsg(newMsg, false); + } + else { // future command + LOG_TRACE_FLOW("Scheduling command"); + // schedule it. + itsCmdStack->add(newMsg->getScheduleTime(), newMsg); + // Tell user it is scheduled. + itsServerStub->sendResult(newMsg->getCommand(), + AcCmdMaskOk | AcCmdMaskScheduled, + "Command is scheduled"); + } + } +} + + +// +// CheckForAPMessages() +// +// The AP's may sent Ack or other types of messages to us. Reroute them to the +// right functions. +// +void ApplController::checkForAPMessages() +{ + // Anything received from the application processes? + LOG_TRACE_STAT("Polling process side"); + APAdmin* activeAP; + while ((activeAP = itsAPAPool->poll(1000))) { + handleProcMessage(activeAP); // handle it + } +} + + +// +// checkForConnectingAPs() +// +// Check the AP listener socket for new AP's that want to connect. New AP's are +// added to the APAdminPool but they will not receive commands until they have +// sent their StartCmd to acknowledge existance. +// +void ApplController::checkForConnectingAPs() +{ + // Any new incomming connections from the appl. processes? + LOG_TRACE_STAT("New processes to connect?"); + Socket* newAPSocket; + while ((newAPSocket = itsProcListener->accept(100))) { + LOG_DEBUG("Incomming process connection"); + APAdmin* APAdminPtr = new APAdmin(newAPSocket); + itsAPAPool->add(APAdminPtr); + } +} + +// +// checkForDisconnectingAPs +// +// During a read the AP may have ended the connection. Clean up its mess. +// +void ApplController::checkForDisconnectingAPs() +{ + // Check for disconnected client processes. During the read action + // it may have turned out that a process has dropped the connection + // this is registered in the Socket. Cleanup these APadmins. + LOG_TRACE_STAT("Cleaning process side"); + while(APAdmin* anAPA = itsAPAPool->cleanup()) { + // TODO: AM.report(anAPA->getName() << " has disconnected"); + LOG_DEBUG_STR (anAPA->getName() << " has disconnected"); + itsProcRuler.markAsStopped(anAPA->getName()); + delete anAPA; // finally delete it. + } +} + + +// +// checkAckCompletion +// +// When the last ack on the outstanding command was received we should mark +// this state ready. The stateEngine will decide somewhere else if there is +// another state to execute or to sent an Ack to the AC user. +void ApplController::checkAckCompletion() +{ + LOG_TRACE_STAT("All ack's received?"); + + if (itsCurState == StateStartupAppl) { +// if (itsAPAPool->onlineCount() == itsProcRuler.size()) { +// if (itsAPAPool->onlineCount() == itsAPAPool->processCount()) { + if (itsAPAPool->onlineCount() == itsNrOfProcs) { + itsStateEngine->ready(); + } + else { + LOG_TRACE_STAT_STR("Still waiting for: " << itsNrOfProcs << "-" + << itsAPAPool->onlineCount() << "=" << itsNrOfProcs-itsAPAPool->onlineCount() + << " connections"); + } + return; + } + + if (itsAPAPool->allAcksReceived()) { + itsStateEngine->ready(); // report we are ready with this state. + } +} + + +// +// checkCmdtimer() +// +// Every command has a certain lifetime. When its lifetime is expired the +// ACuser should be informed on this with a Nack. +// +void ApplController::checkStateTimer() +{ + LOG_TRACE_STAT("State timer still running?"); + + if (itsStateEngine->IsStateExpired()) { + // Special case: handle Quit command extras + // when quit state failed we must perform the kill state anyway. + if (itsCurState == StateQuitCmd) { + itsProcRuler.stopAll(); + writeResultFile(); + itsIsRunning = false; + } + + sendExecutionResult(0, "Timed out"); + } + +} + + +// +// checkCmdStack() +// +// Check if it is time to execute the command that is placed on the CmdStack. +// +void ApplController::checkCmdStack() +{ + LOG_TRACE_STAT("Time for a stack command?"); + + if (itsCmdStack->timeExpired()) { + acceptOrRefuseACMsg(itsCmdStack->pop(), true); + } +} + +// +// checkStateEngine() +// +// Somewhere in the process the stateEngine may have been told the current +// state is finished. If so, another state should be started or when the +// last state of the sequence was finished the AC user must have an ACK msg. +// +void ApplController::checkStateEngine() +{ + LOG_TRACE_STAT("Time for next commmand phase?"); + if (!itsStateEngine->isStateFinished()) { + return; + } + + // State was flagged ready, check if there is another state we should + // execute. + + // Special case: handle Quit command extras + if (itsCurState == StateKillAppl) { + writeResultFile(); + itsIsRunning = false; + } + + itsCurState = itsStateEngine->nextState(); + if (itsCurState == StateNone) { + sendExecutionResult(AcCmdMaskOk, "Command ready"); // No more states + return; + } + + // there is a next state, start it. + startCmdState(); +} + + + } // namespace ACC +} // namespace LOFAR + diff --git a/MAC/APL/Appl_Controller/src/ApplController.h b/MAC/APL/Appl_Controller/src/ApplController.h new file mode 100644 index 0000000000000000000000000000000000000000..a8a63fa87ade66f3d5c197f5628883e6c3f28e4c --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ApplController.h @@ -0,0 +1,127 @@ +//# ApplController.h: Controls all processes of an application. +//# +//# Copyright (C) 2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# Note: This source is read best with tabstop 4. +//# +//# $Id$ + +#ifndef LOFAR_ACCBIN_APPLCONTROLLER_H +#define LOFAR_ACCBIN_APPLCONTROLLER_H + +// \file +// This is the main 'engine' of the Application Controller. It manages +// the communication with the ACuser, the Application Processes and the +// ACDaemon. It guards the execution time of the commands and collects +// messages, results and acknowledgements fromthe AP's. + +//# Never *include <config.h> or #include <lofar_config.h> in a header file! +//# Includes +#include <Common/Net/Socket.h> +#include <Transport/TH_Socket.h> +#include <ALC/ApplControlServer.h> //# communication stub +#include <PLC/DH_ProcControl.h> +#include <Common/ParameterSet.h> +#include <MACIO/EventPort.h> +#include "ACCmdImpl.h" //# the real implementation +#include "ACDaemonComm.h" +#include "CmdStack.h" +#include "StateEngine.h" +#include "APAdminPool.h" +#include "ItemList.h" +#include "ProcRuler.h" + +using namespace LOFAR::ACC::ALC; +using namespace LOFAR::ACC::PLC; + +namespace LOFAR { + namespace ACC { +// \addtogroup ACCbin +// @{ + +// This is the main 'engine' of the Application Controller. It manages +// the communication with the ACuser, the Application Processes and the +// ACDaemon. It guards the execution time of the commands and collects +// messages, results and acknowledgements fromthe AP's. +class ApplController +{ +public: + ApplController (const string& configID); + ~ApplController(); + + void startupNetwork(); + void doEventLoop(); + +private: + void handleProcMessage (APAdmin* anAP); + void sendExecutionResult(uint16 result, + const string& comment); + void acceptOrRefuseACMsg(DH_ApplControl* anACMsg, + bool passOwnership); + void startCmdState (); + void createParSubsets (); + // writeParSubset writes the parameterset to a file + // it is only meant to avoid code duplication in createParSubsets + void writeParSubset(ParameterSet ps, const string& procName, const string& fileName); + void writeResultFile (); + void sendToKVLogger (ParameterSet& aResultPS); + + void checkForACCommands(); + void checkForAPMessages(); + void checkForConnectingAPs(); + void checkForDisconnectingAPs(); + void checkAckCompletion(); + void checkStateTimer(); + void checkCmdStack(); + void checkStateEngine(); + + // Datamembers + ParameterSet* itsBootParamSet; // Own PS, passed during birth + ParameterSet* itsObsParamSet; // PS of observation, given by AM + ParameterSet* itsResultParamSet; // PS for collecting proc. results. + ItemList* itsProcList; // All AP's according to ObsParSet + ACCmdImpl* itsACCmdImpl; // The command implementation + CmdStack* itsCmdStack; // Future commands stack + Socket* itsProcListener; // For AP's to connect to + APAdminPool* itsAPAPool; // Communication with all AP's + ApplControlServer* itsServerStub; // Communication with AM + ACDaemonComm* itsDaemonComm; // Communication with ACDaemon + MACIO::EventPort* itsKVLogger; // Connection to KeyValueLogger + time_t itsCurTime; // Current timestamp + bool itsIsRunning; // Alive or not + + StateEngine* itsStateEngine; // State machine of the controller + ACState itsCurState; // State currently executing + DH_ApplControl* itsCurACMsg; // Command under handling + + ProcRuler itsProcRuler; // Starts/stops all AP's + + uint16 itsNrOfProcs; // Nr of processes to manage. + + // TODO: REMOVE THIS CS1 HACK + string itsObsPSfilename; // name of observation parameterset. +}; + +// @} addgroup + } // namespace ACC +} // namespace LOFAR + + +#endif + diff --git a/MAC/APL/Appl_Controller/src/ApplController.log_prop b/MAC/APL/Appl_Controller/src/ApplController.log_prop new file mode 100644 index 0000000000000000000000000000000000000000..f3755cbd6b73c997bf784c06d4ef9a18e0e18bdf --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ApplController.log_prop @@ -0,0 +1,24 @@ + +# Configure the loggers +log4cplus.rootLogger=INFO, STDOUT, FILE +log4cplus.logger.TRC=INFO +log4cplus.logger.LCS.Common=FATAL, STDOUT, FILE + +# Define the appenders +log4cplus.appender.STDOUT=log4cplus::ConsoleAppender +log4cplus.appender.STDOUT.layout=log4cplus::PatternLayout +log4cplus.appender.STDOUT.layout.ConversionPattern=%D{%d-%m %H:%M:%S.%q} %-5p %c{3} - %m [%.25l]%n + +log4cplus.appender.STDERR=log4cplus::ConsoleAppender +log4cplus.appender.STDERR.layout=log4cplus::PatternLayout +log4cplus.appender.STDERR.layout.ConversionPattern=%D{%d-%m %H:%M:%S.%q} %-5p %c{3} - %m [%.25l]%n +log4cplus.appender.STDERR.logToStdErr=true + +log4cplus.appender.FILE=log4cplus::RollingFileAppender +log4cplus.appender.FILE.File=/opt/lofar/var/log/${LOG4CPLUS_LOGFILENAME}.log +log4cplus.appender.FILE.MaxFileSize=10MB +log4cplus.appender.FILE.MaxBackupIndex=2 +log4cplus.appender.FILE.layout=log4cplus::PatternLayout +log4cplus.appender.FILE.layout.ConversionPattern=%x %D{%d-%m %H:%M:%S.%q} %-5p %c{3} - %m [%.25l]%n + +log4cplus.appender.DUMP=log4cplus::NullAppender diff --git a/MAC/APL/Appl_Controller/src/ApplControllerMain.cc b/MAC/APL/Appl_Controller/src/ApplControllerMain.cc new file mode 100644 index 0000000000000000000000000000000000000000..2dfc8e7fcc23c14c55911027214e82f5c6471588 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ApplControllerMain.cc @@ -0,0 +1,99 @@ +//# ApplControllerMain.cc: Executes the Application Controller. +//# +//# Copyright (C) 2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +//# Includes +#include <signal.h> +#include <libgen.h> +#include <Common/lofar_string.h> +#include <Common/LofarLogger.h> +#include <Common/LofarLocators.h> +#include <Common/Exception.h> +#include "ApplController.h" + +using namespace LOFAR; +using namespace LOFAR::ACC; + +// Use a terminate handler that can produce a backtrace. +Exception::TerminateHandler t(Exception::terminate); + +// +// MAIN (ParameterFile) +// +int main (int argc, char* argv[]) +{ + + // close filedescriptors from our launcher +// for (int f = dup(2); f > 2; --f) { +// close(f); +// } + + // Always bring up he logger first + ConfigLocator aCL; + string progName(basename(argv[0])); +#ifdef HAVE_LOG4CPLUS + string logPropFile(progName + ".log_prop"); + INIT_VAR_LOGGER (aCL.locate(logPropFile).c_str(), progName + "-" + argv[1]); +#elif HAVE_LOG4COUT + INIT_LOGGER_WITH_SYSINFO(progName); +#else + string logPropFile(progName + ".debug"); + INIT_LOGGER (aCL.locate(logPropFile).c_str()); +#endif + LOG_DEBUG_STR("Initialized logsystem with: " << aCL.locate(logPropFile)); + + // Check invocation syntax + if (argc < 2) { + LOG_FATAL_STR ("Invocation error, syntax: " << progName << " configID"); + cout << "Invocation error, syntax: " << progName << " configID" << endl; + return (-1); + } + + // Tell operator we are tryingto start up. + LOG_INFO_STR("Starting up: " << argv[0] << "(" << argv[1] << ")"); + + try { + signal (SIGPIPE, SIG_IGN); // ignore write errors on sockets + // close filedescriptors from our launcher +// for (int f = dup(2); f > 2; --f) { +// close(f); +// } + + ApplController theAC(argv[1]); + + theAC.startupNetwork(); + + theAC.doEventLoop(); + + LOG_INFO_STR("Shutting down: " << argv[0]); + } + catch (LOFAR::Exception& ex) { + LOG_FATAL_STR("Caught exception: " << ex << endl); + LOG_FATAL ("Terminated by exception!"); + return(1); + } + + LOG_INFO("Terminated normally"); + return (0); +} diff --git a/MAC/APL/Appl_Controller/src/CMakeLists.txt b/MAC/APL/Appl_Controller/src/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ec1202d653397dac685ad435fa50aed90db651c3 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/CMakeLists.txt @@ -0,0 +1,42 @@ +# $Id$ + +lofar_add_bin_program(ApplController + ApplControllerMain.cc + ACCmdImpl.cc + ACDaemonComm.cc + APAdmin.cc + APAdminPool.cc + CmdStack.cc + forkexec.cc + ItemList.cc + ProcRule.cc + ProcRuler.cc + PR_Shell.cc + PR_MPI.cc + PR_BGL.cc + StateEngine.cc + ApplController.cc) + +lofar_add_bin_program(ACDaemon + ACDaemonMain.cc + ACRequestPool.cc + ACDaemon.cc + forkexec.cc) + +lofar_add_bin_program(ACcli ACcli.cc) + +lofar_add_bin_program(ACuserMenu ACuserMenu.cc) + +install(PROGRAMS + startAP.sh + startMPI.sh + stopAP.sh + stopMPI.sh + DESTINATION bin) + +install(FILES + ACDaemon.log_prop + ACDaemon.conf + ApplController.log_prop + ACuserMenu.log_prop + DESTINATION etc) diff --git a/MAC/APL/Appl_Controller/src/CmdStack.cc b/MAC/APL/Appl_Controller/src/CmdStack.cc new file mode 100644 index 0000000000000000000000000000000000000000..daea49c28855e7a798ffe241374fb878f969ac98 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/CmdStack.cc @@ -0,0 +1,91 @@ +//# CmdStack.cc: Implements time ordered map of DH_ApplControl structs +//# +//# Copyright (C) 2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +#include <lofar_config.h> + +//# Includes +#include <Common/LofarLogger.h> +#include <Common/Exception.h> +#include <Common/StringUtil.h> +#include "CmdStack.h" + +namespace LOFAR { + namespace ACC { + +CmdStack::CmdStack() +{ +} + +CmdStack::~CmdStack() +{ + itsStack.clear(); // in case the destructor does not do this +} + +void CmdStack::add(time_t scheduleTime, + DH_ApplControl* aDHAC) +{ + LOG_TRACE_RTTI_STR("CmdStack::add: " << timeString(scheduleTime) << + ", " << aDHAC); + + pair < iterator, bool> result; + DH_ApplControl* newDHAC = aDHAC->makeDataCopy(); + LOG_TRACE_RTTI_STR("newDHAC=" << newDHAC); + + result = itsStack.insert(std::make_pair(scheduleTime, newDHAC)); + if (!result.second) { + THROW (Exception, "insert in CmdStack failed"); + } + + LOG_DEBUG_STR("CmdStack:Cmd added with timestamp = " << + timeString(scheduleTime)); +} + +DH_ApplControl* CmdStack::pop() +{ + ASSERTSTR(!itsStack.empty(), "Cmdstack is empty"); + +// DH_ApplControl* theCmd = itsStack.begin()->second; + DH_ApplControl* theCmd = (itsStack.begin()->second)->makeDataCopy(); + itsStack.erase(itsStack.begin()); + + LOG_DEBUG("CmdStack: pop()"); + + return (theCmd); +} + +bool CmdStack::timeExpired() +{ + if (itsStack.empty()) { + return (false); + } + + if (itsStack.begin()->first <= time(0)) { + LOG_DEBUG("CmdStack: time expired, returning command"); + return (true); + } + + return (false); +} + +} // namespace ACC +} // namespace LOFAR + diff --git a/MAC/APL/Appl_Controller/src/CmdStack.h b/MAC/APL/Appl_Controller/src/CmdStack.h new file mode 100644 index 0000000000000000000000000000000000000000..3e6b0451c90025c90b2bc44d79b67453cd861e81 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/CmdStack.h @@ -0,0 +1,89 @@ +//# CmdStack.h: Time-ordered stack of DH_ApplControl structures +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# Note: This source is read best with tabstop 4. +//# +//# $Id$ + +#ifndef LOFAR_ACCBIN_CMDSTACK_H +#define LOFAR_ACCBIN_CMDSTACK_H + +// \file +// Time-ordered stack of DH_ApplControl structures used by the Application +// Controller to hold future commands for a while. + +//# Never #include <config.h> or #include <lofar_config.h> in a header file! +//# Includes +#include <time.h> +#include <ALC/DH_ApplControl.h> +#include <Common/lofar_map.h> + +using namespace LOFAR::ACC::ALC; + +namespace LOFAR { + namespace ACC { +// \addtogroup ACCbin +// @{ + +// Time-ordered stack of DH_ApplControl structure pointers. +class CmdStack +{ +public: + typedef map<time_t, DH_ApplControl*> DHACStack; + typedef map<time_t, DH_ApplControl*>::iterator iterator; + typedef map<time_t, DH_ApplControl*>::const_iterator const_iterator; + + CmdStack (); + ~CmdStack(); + + // Add the given DH_ApplControl pointer to the stack with the given time. + void add(time_t scheduleTime, DH_ApplControl* aDHAC); + + // Remove the top element from the stack and return a pointer to it. + DH_ApplControl* pop(); + + // Returns true is the time of the top-element lays in the past. + bool timeExpired(); + + // Removes all command from the Stack + void clear(); + +private: + // Who wants to copy a CmdStack? + CmdStack(const CmdStack& that); + + // Who wants to copy a CmdStack? + CmdStack& operator= (const CmdStack& that); + + //# --- DataMembers --- + // The map used for the storage. + DHACStack itsStack; +}; + +inline void CmdStack::clear() +{ + itsStack.clear(); +} + +// @} addgroup + } // namespace ACC +} // namespace LOFAR + +#endif diff --git a/MAC/APL/Appl_Controller/src/ItemList.cc b/MAC/APL/Appl_Controller/src/ItemList.cc new file mode 100644 index 0000000000000000000000000000000000000000..91e324e58894279dd158b01cdbcdf5fab40b3fd6 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ItemList.cc @@ -0,0 +1,72 @@ +//# ItemList.cc: one line description +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +//# Includes +#include <Common/LofarLogger.h> +#include "ItemList.h" + +namespace LOFAR { + namespace ACC { + +ItemList::ItemList(const ParameterSet& aPS, + const string& prefix) +{ + int32 nrProcs = aPS.getInt32(prefix+"[0].count"); + string procName; + for (int32 p = 1; p <= nrProcs; p++) { + procName = aPS.getString(formatString("%s[%d].ID", prefix.c_str(), p)); + int32 nrOfProcs = indexValue(procName, "()"); + if (nrOfProcs == 0) { + push_back(procName); // add to collection + continue; + } + + rtrim(procName, "(0123456789)"); + for (int32 idx = 1; idx <= nrOfProcs; ++idx) { + push_back(formatString("%s[%d]", procName.c_str(), idx)); + } + } +} + +//ItemList::~ItemList() +//{} + +bool ItemList::isInList(const string& aProcName) const +{ + const_iterator iter = begin(); + + while (iter != end()) { + if (*iter == aProcName) { + return (true); + } + ++iter; + } + + return (false); +} + + + } // namespace ACC +} // namespace LOFAR diff --git a/MAC/APL/Appl_Controller/src/ItemList.h b/MAC/APL/Appl_Controller/src/ItemList.h new file mode 100644 index 0000000000000000000000000000000000000000..590ce6d99238a465cdd4efc48650b13e63188b8b --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ItemList.h @@ -0,0 +1,65 @@ +//# ItemList.h: one line description +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# Note: This source is read best with tabstop 4. +//# +//# $Id$ + +#ifndef LOFAR_ACCBIN_ITEMLIST_H +#define LOFAR_ACCBIN_ITEMLIST_H + +// \file +// Class that can substract and array of items from a parameter collection. + +//# Never #include <config.h> or #include <lofar_config.h> in a header file! +//# Includes +#include <Common/lofar_vector.h> +#include <Common/ParameterSet.h> + + +namespace LOFAR { + namespace ACC { +// \addtogroup ACCbin +// @{ + + +// Class that can substract and array of items from a parameter collection. +class ItemList : public vector <string> +{ +public: + typedef vector<string>::iterator iterator; + typedef vector<string>::const_iterator const_iterator; + + // Constructs a vector of Values from the vector of keys with the name + // 'prefix' from the given ParameterSet. ( prefix[n] = ) + ItemList(const ParameterSet& aPS, + const string& prefix); + + bool isInList(const string& aItemName) const; + +private: + +}; + +// @} addgroup + } // namespace ACC +} // namespace LOFAR + +#endif diff --git a/MAC/APL/Appl_Controller/src/ObsA.param b/MAC/APL/Appl_Controller/src/ObsA.param new file mode 100644 index 0000000000000000000000000000000000000000..159eb20e6cd5ca591dc41a68814cb38acac5019a --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ObsA.param @@ -0,0 +1,37 @@ +# +# timeout values for the Applcontroller +ApplCtrl.timeout_powerup = 3 m +ApplCtrl.timeout_powerdown = 3 m +ApplCtrl.timeout_createsubsets= 3 m +ApplCtrl.timeout_exitdelay = 15 m +ApplCtrl.timeout_startup = 2 m +ApplCtrl.timeout_define = 40 +ApplCtrl.timeout_init = 40 +ApplCtrl.timeout_run = 30 +ApplCtrl.timeout_pause = 50 +ApplCtrl.timeout_release = 50 +ApplCtrl.timeout_recover = 50 +ApplCtrl.timeout_snapshot = 30 +ApplCtrl.timeout_reinit = 30 +ApplCtrl.timeout_info = 30 +ApplCtrl.timeout_quit = 60 +ApplCtrl.timeout_kill = 60 +# +# where the meta-data must be stored +ApplCtrl.resultfile =./AC_result.log + +# +# Application related information +ApplCtrl.application= testAppl +ApplCtrl.processes = [ tPCclient(2), tAnother ] +# +# Process specific information +testAppl.tPCclient._startstopType = cmdline +testAppl.tPCclient._executable = ./APTest +testAppl.tPCclient._hostname = localhost +testAppl.tPCclient._nodes = [ localhost, localhost ] + +testAppl.tAnother._startstopType = cmdline +testAppl.tAnother._executable = ./APTest +testAppl.tAnother._hostname = localhost + diff --git a/MAC/APL/Appl_Controller/src/PR_BGL.cc b/MAC/APL/Appl_Controller/src/PR_BGL.cc new file mode 100644 index 0000000000000000000000000000000000000000..3530170128a488a780442859cb0503d4006bfb4f --- /dev/null +++ b/MAC/APL/Appl_Controller/src/PR_BGL.cc @@ -0,0 +1,94 @@ +//# PR_BGL.cc: ProcessRule based for BG/L jobs +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +//# Includes +#include <Common/LofarLogger.h> + +#include <Common/StringUtil.h> +#include "PR_BGL.h" +#include "forkexec.h" + +namespace LOFAR { + namespace ACC { + + PR_BGL::PR_BGL(const string& aJobName, + const string& aExecutable, + const string& aWorkingDir, + const string& aObservationID, + const string& aParamFile, + const uint numberOfNodes) : + ProcRule("BGLjob", aJobName, aExecutable, aParamFile) + { + itsStartCmd = formatString("./startBGL.sh %s %s %s %s %s %d", + aJobName.c_str(), + aExecutable.c_str(), + aWorkingDir.c_str(), + aParamFile.c_str(), + aObservationID.c_str(), + numberOfNodes); + + itsStopCmd = formatString("./stopBGL.sh %s %s", + aJobName.c_str(), + aObservationID.c_str()); + } + + PR_BGL::PR_BGL(const PR_BGL& other) : + ProcRule(other) + {} + + bool PR_BGL::start() { + if (itsIsStarted) { + LOG_TRACE_OBJ_STR("PR_BGL:" << itsProcName << " is already started"); + return (true); + } + + LOG_TRACE_OBJ_STR ("PR_BGL: " << itsStartCmd); + + int32 result = forkexec (itsStartCmd.c_str()); + + if (result == 0) { + itsIsStarted = true; + } + + return (itsIsStarted); + } + + bool PR_BGL::stop() + { + // Note: always execute the stop command because it may also cleanup + // some mess the process left behind. + LOG_TRACE_OBJ_STR ("PR_BGL: " << itsStopCmd); + + int32 result = forkexec (itsStopCmd.c_str()); + + if (result == 0) { + itsIsStarted = false; + } + + return (!itsIsStarted); + } + + } // namespace ACC +} // namespace LOFAR diff --git a/MAC/APL/Appl_Controller/src/PR_BGL.h b/MAC/APL/Appl_Controller/src/PR_BGL.h new file mode 100644 index 0000000000000000000000000000000000000000..15c8354686a3ac01012ac6a98155b5fb3b8454bc --- /dev/null +++ b/MAC/APL/Appl_Controller/src/PR_BGL.h @@ -0,0 +1,74 @@ +//# PR_BGL.h: ProcesRule based for BG/L jobs +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# Note: This source is read best with tabstop 4. +//# +//# $Id$ + +#ifndef LOFAR_ACCBIN_PR_BGL_H +#define LOFAR_ACCBIN_PR_BGL_H + +// \file +// ProcessRule based on shell scripts + +//# Never #include <config.h> or #include <lofar_config.h> in a header file! +//# Includes +#include "ProcRule.h" + +namespace LOFAR { + namespace ACC { + // \addtogroup ACCbin + // @{ + + // The PR_BGL class contains all information to (over)rule a process. + // Its known how to start and stop a process and knows its current state. + class PR_BGL : public ProcRule { + public: + PR_BGL(const string& aJobName, + const string& aExecutable, + const string& aWorkingDir, + const string& aObservationID, + const string& aParamfile, + const uint numberOfNodes); + + PR_BGL(const PR_BGL& that); + + ~PR_BGL() {}; + + // Redefine the start and stop commands. + virtual bool start(); + virtual bool stop(); + virtual string getType() const + { return ("PR_BGL"); } + virtual PR_BGL* clone() const + { return (new PR_BGL(*this)); } + + private: + // Default construction not allowed + PR_BGL(); + + //# --- Datamembers --- + }; + + // @} addgroup + } // namespace ACC +} // namespace LOFAR + +#endif diff --git a/MAC/APL/Appl_Controller/src/PR_MPI.cc b/MAC/APL/Appl_Controller/src/PR_MPI.cc new file mode 100644 index 0000000000000000000000000000000000000000..be17281808ebbbeeff797f64ac58681c167265d8 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/PR_MPI.cc @@ -0,0 +1,156 @@ +//# PR_MPI.cc: ProcessRule based on mpirun +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +//# Includes +#include <Common/LofarLogger.h> +#include <Common/lofar_fstream.h> +#include <Common/SystemUtil.h> +#include "PR_MPI.h" +#include "forkexec.h" + +namespace LOFAR { + namespace ACC { + +// +// PR_MPI(jobname, nodes, executable, paramfile) +// +PR_MPI::PR_MPI(const string& aHostName, + const string& aJobName, + const vector<string>& nodes, + const string& aExecutable, + const string& aParamFile, + uint16 nrProcs) + : ProcRule("MPIjob", aJobName, aExecutable, aParamFile) +{ + LOG_TRACE_OBJ_STR ("PR_MPI: constructor"); + // create machinefile + string machinefileName(aJobName + ".machinefile"); + ofstream machinefile; + machinefile.open(machinefileName.c_str(), ofstream::out); + vector<string>::const_iterator node; + for (node = nodes.begin(); node != nodes.end(); node++) { + machinefile << *node << endl; + } + machinefile.close(); + + // start processes on another node? + if (aHostName != myHostname(false) && + aHostName != myHostname(true)) { + // copy files to other machine + remoteCopy(aParamFile, aHostName, aParamFile); + remoteCopy(machinefileName, aHostName, machinefileName); + + itsStartCmd = formatString("ssh %s \"( cd /opt/lofar/bin ; ./startMPI.sh %s %s %s %s %d )\"", + aHostName.c_str(), + aJobName.c_str(), + machinefileName.c_str(), + aExecutable.c_str(), + aParamFile.c_str(), + nrProcs); + itsStopCmd = formatString("ssh %s \"( cd /opt/lofar/bin ; ./stopMPI.sh %s ) \"", + aHostName.c_str(), + aExecutable.c_str()); + } + else { // start/stop on local machine + itsStartCmd = formatString("./startMPI.sh %s %s %s %s %d", + aJobName.c_str(), + machinefileName.c_str(), + aExecutable.c_str(), + aParamFile.c_str(), + nrProcs); + itsStopCmd = formatString("./stopMPI.sh %s", + aExecutable.c_str()); + } +} + +// +// PR_MPI(PR_MPI&) +// +PR_MPI::PR_MPI(const PR_MPI& other) + : ProcRule(other) +{ + LOG_TRACE_OBJ_STR ("PR_MPI: copy constructor"); +} + +// +// ~PR_MPI() +// +PR_MPI::~PR_MPI() +{ + LOG_TRACE_OBJ_STR ("PR_MPI: destructor"); +} + +// +// start() +// +bool PR_MPI::start() +{ + if (itsIsStarted) { + LOG_TRACE_OBJ_STR("PR_MPI:" << itsProcName << " is already started"); + return (true); + } + + LOG_TRACE_OBJ_STR ("PR_MPI: " << itsStartCmd); + + if (forkexec (itsStartCmd.c_str()) == 0) { + itsIsStarted = true; + } + + return (itsIsStarted); +} + +// +// stop() +// +bool PR_MPI::stop() +{ + LOG_TRACE_OBJ_STR ("PR_MPI: " << itsStopCmd); + + if (forkexec (itsStopCmd.c_str()) == 0) { + itsIsStarted = false; + } + + return (!itsIsStarted); +} + +// +// getType() +// +string PR_MPI::getType() const +{ + return ("PR_MPI"); +} + +// +// clone() +// +PR_MPI* PR_MPI::clone() const +{ + return (new PR_MPI(*this)); +} + + + } // namespace ACC +} // namespace LOFAR diff --git a/MAC/APL/Appl_Controller/src/PR_MPI.h b/MAC/APL/Appl_Controller/src/PR_MPI.h new file mode 100644 index 0000000000000000000000000000000000000000..9dbe531055e042c0f0d2e7a5409ee7c9ec663002 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/PR_MPI.h @@ -0,0 +1,80 @@ +//# PR_MPI.h: ProcesRule based on mpirun +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# Note: This source is read best with tabstop 4. +//# +//# $Id$ + +#ifndef LOFAR_ACCBIN_PR_MPI_H +#define LOFAR_ACCBIN_PR_MPI_H + +// \file +// ProcessRule based on mpirun + +//# Never #include <config.h> or #include <lofar_config.h> in a header file! +//# Includes + +#include <Common/lofar_vector.h> +#include <Common/StringUtil.h> +#include "ProcRule.h" + +namespace LOFAR { + namespace ACC { + +// \addtogroup ACCbin +// @{ + +// The PR_MPI starts and stop an mpi program. One MPI-job is +// controlled by one PR_MPI. This means there are usually multiple +// processes on multiple hosts involved. + +// The PR_MPI class contains all information to (over)rule a process. +// Its known how to start and stop a process and knows its current state. +class PR_MPI : public ProcRule { +public: + PR_MPI (const string& aHostName, + const string& aJobName, + const vector<string>& nodes, + const string& aExecutable, + const string& aParamfile, + uint16 nrProcs); + + PR_MPI(const PR_MPI& other); + + ~PR_MPI(); + + // Redefine the start and stop commands. + virtual bool start(); + virtual bool stop(); + virtual string getType() const; + virtual PR_MPI* clone() const; + +private: + // Default construction not allowed + PR_MPI(); + + //# --- Datamembers --- +}; + + // @} addgroup + } // namespace ACC +} // namespace LOFAR + +#endif diff --git a/MAC/APL/Appl_Controller/src/PR_Shell.cc b/MAC/APL/Appl_Controller/src/PR_Shell.cc new file mode 100644 index 0000000000000000000000000000000000000000..57f9e26f3e6d2eb78f0eff162efc1b17f56a071c --- /dev/null +++ b/MAC/APL/Appl_Controller/src/PR_Shell.cc @@ -0,0 +1,104 @@ +//# PR_Shell.cc: ProcessRule based on shell scripts +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +//# Includes +#include <Common/LofarLogger.h> +#include <Common/SystemUtil.h> +#include <Common/StringUtil.h> +#include "PR_Shell.h" +#include "forkexec.h" + +namespace LOFAR { + namespace ACC { + +PR_Shell::PR_Shell(const string& aNodeName, + const string& aProcName, + const string& aExecName, + const string& aParamFile) : + ProcRule(aNodeName, aProcName, aExecName, aParamFile) +{ + if (aNodeName != myHostname(false) && + aNodeName != myHostname(true) && + aNodeName != "localhost") { + remoteCopy(aParamFile, aNodeName, aParamFile); + + itsStartCmd = formatString("ssh %s \"( cd /opt/lofar/bin ; ./startAP.sh %s %s %s %s )\"", + aNodeName.c_str(), + aNodeName.c_str(), + aProcName.c_str(), + aExecName.c_str(), + aParamFile.c_str()); + itsStopCmd = formatString("ssh %s \"( cd /opt/lofar/bin ; ./stopAP.sh %s %s )\"", + aNodeName.c_str(), + aNodeName.c_str(), + aProcName.c_str()); + } + else { // execute on local machine + itsStartCmd = formatString("./startAP.sh %s %s %s %s", + aNodeName.c_str(), + aProcName.c_str(), + aExecName.c_str(), + aParamFile.c_str()); + itsStopCmd = formatString("./stopAP.sh %s %s", + aNodeName.c_str(), + aProcName.c_str()); + } +} + +bool PR_Shell::start() +{ + if (itsIsStarted) { + LOG_TRACE_OBJ_STR("PR_Shell:" << itsProcName << " is already started"); + return (true); + } + + LOG_TRACE_OBJ_STR ("PR_Shell: " << itsStartCmd); + + int32 result = forkexec (itsStartCmd.c_str()); + + if (result == 0) { + itsIsStarted = true; + } + + return (itsIsStarted); +} + +bool PR_Shell::stop() +{ + // Note: always execute the stop command because it may also cleanup + // some mess the process left behind. + LOG_TRACE_OBJ_STR ("PR_Shell: " << itsStopCmd); + + int32 result = forkexec (itsStopCmd.c_str()); + + if (result == 0) { + itsIsStarted = false; + } + + return (!itsIsStarted); +} + + } // namespace ACC +} // namespace LOFAR diff --git a/MAC/APL/Appl_Controller/src/PR_Shell.h b/MAC/APL/Appl_Controller/src/PR_Shell.h new file mode 100644 index 0000000000000000000000000000000000000000..dcad3f08d42b4843edfe563e697c1e3fbbe71861 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/PR_Shell.h @@ -0,0 +1,70 @@ +//# PR_Shell.h: ProcesRule based on shell scripts +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# Note: This source is read best with tabstop 4. +//# +//# $Id$ + +#ifndef LOFAR_ACCBIN_PR_SHELL_H +#define LOFAR_ACCBIN_PR_SHELL_H + +// \file +// ProcessRule based on shell scripts + +//# Never #include <config.h> or #include <lofar_config.h> in a header file! +//# Includes +#include "ProcRule.h" + +namespace LOFAR { + namespace ACC { +// \addtogroup ACCbin +// @{ + +// The PR_Shell class contains all information to (over)rule a process. +// Its known how to start and stop a process and knows its current state. +class PR_Shell : public ProcRule +{ +public: + PR_Shell(const string& aNodeName, + const string& aProcName, + const string& aExecName, + const string& aParamfile); + ~PR_Shell() {}; + + // Redefine the start and stop commands. + virtual bool start(); + virtual bool stop(); + virtual string getType() const + { return ("PR_Shell"); } + virtual PR_Shell* clone() const + { return (new PR_Shell(*this)); } + +private: + // Default construction not allowed + PR_Shell(); + + //# --- Datamembers --- +}; + +// @} addgroup + } // namespace ACC +} // namespace LOFAR + +#endif diff --git a/MAC/APL/Appl_Controller/src/ProcRule.cc b/MAC/APL/Appl_Controller/src/ProcRule.cc new file mode 100644 index 0000000000000000000000000000000000000000..60feaa63aeda3df3e198b6e46760392718467fda --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ProcRule.cc @@ -0,0 +1,56 @@ +//# ProcRule.cc: one line description +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +//# Includes +#include <Common/LofarLogger.h> +#include "ProcRule.h" + +namespace LOFAR { + namespace ACC { + +ProcRule::ProcRule(const string& aNodeName, + const string& aProcName, + const string& aExecName, + const string& aParamfile) : + itsNodeName (aNodeName), + itsProcName (aProcName), + itsExecName (aExecName), + itsParamfile(aParamfile), + itsIsStarted(false) +{} + +std::ostream& operator<< (std::ostream& os, const ProcRule& aPR) +{ + os << "ProcName: " << aPR.itsProcName << endl; + os << "Type : " << aPR.getType() << endl; + os << "StartCmd: " << aPR.itsStartCmd << endl; + os << "StopCmd : " << aPR.itsStopCmd << endl; + os << "NodeName: " << aPR.itsNodeName << endl; + + return (os); +} + + } // namespace ACC +} // namespace LOFAR diff --git a/MAC/APL/Appl_Controller/src/ProcRule.h b/MAC/APL/Appl_Controller/src/ProcRule.h new file mode 100644 index 0000000000000000000000000000000000000000..321fe545686f985c1855d401c1466515ac72c88b --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ProcRule.h @@ -0,0 +1,116 @@ +//# ProcRule.h: information how to rule a process. +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# Note: This source is read best with tabstop 4. +//# +//# $Id$ + +#ifndef LOFAR_ACCBIN_PROCRULE_H +#define LOFAR_ACCBIN_PROCRULE_H + +// \file +// Information how to rule a process. + +//# Never #include <config.h> or #include <lofar_config.h> in a header file! +//# Includes +#include <Common/lofar_iostream.h> + +namespace LOFAR { + namespace ACC { +// \addtogroup ACCbin +// @{ + +// The ProcRule class contains all information to (over)rule a process. +// Its known how to start and stop a process and knows its current state. +class ProcRule +{ +public: + ProcRule(const string& aNodeName, + const string& aProcName, + const string& aExecName, + const string& aParamfile); + virtual ~ProcRule() {}; + + // The start and stop commands to be implemented. + virtual bool start() = 0; + virtual bool stop() = 0; + virtual string getType() const = 0; + virtual ProcRule* clone() const = 0; + + const string& getName() const; + const string& getNodeName() const; + const string& getExecName() const; + const string& getParamFile() const; + bool isStarted() const; + void markAsStopped(); + + + friend std::ostream& operator<<(std::ostream& os, const ProcRule& aPR); + +protected: + // Default construction not allowed + ProcRule(); + + + //# --- Datamembers --- + string itsNodeName; + string itsProcName; + string itsExecName; + string itsParamfile; + string itsStartCmd; + string itsStopCmd; + bool itsIsStarted; +}; + +inline bool ProcRule::isStarted() const +{ + return (itsIsStarted); +} + +inline const string& ProcRule::getName() const +{ + return (itsProcName); +} + +inline const string& ProcRule::getNodeName() const +{ + return (itsNodeName); +} + +inline const string& ProcRule::getExecName() const +{ + return (itsExecName); +} + +inline const string& ProcRule::getParamFile() const +{ + return (itsParamfile); +} + +inline void ProcRule::markAsStopped() +{ + itsIsStarted = false; +} + +// @} addgroup + } // namespace ACC +} // namespace LOFAR + +#endif diff --git a/MAC/APL/Appl_Controller/src/ProcRuler.cc b/MAC/APL/Appl_Controller/src/ProcRuler.cc new file mode 100644 index 0000000000000000000000000000000000000000..b0a68008a865bf89c214a6693226f75d9c0ac3ff --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ProcRuler.cc @@ -0,0 +1,119 @@ +//# ProcRuler.cc: one line description +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +//# Includes +#include <Common/LofarLogger.h> +#include "ProcRuler.h" + +namespace LOFAR { + namespace ACC { + +ProcRuler::ProcRuler() +{} + +ProcRuler::~ProcRuler() +{ + itsProcPool.clear(); +} + +void ProcRuler::add (const ProcRule& aProcRule) +{ + LOG_TRACE_OBJ_STR("ProcRuler add:" << aProcRule.getName()); + + itsProcPool.erase(aProcRule.getName()); + + itsProcPool.insert(make_pair(aProcRule.getName(), aProcRule.clone())); +} + +void ProcRuler::remove(const string& aProcName) +{ + itsProcPool.erase(aProcName); +} + +bool ProcRuler::start(const string& aProcName) +{ + iterator iter = itsProcPool.find(aProcName); + if (iter == itsProcPool.end()) { + return (false); + } + + return (iter->second->start()); +} + +bool ProcRuler::stop (const string& aProcName) +{ + iterator iter = itsProcPool.find(aProcName); + if (iter == itsProcPool.end()) { + return (false); + } + + return (iter->second->stop()); +} + +bool ProcRuler::startAll() +{ + bool result = true; + + iterator iter = itsProcPool.begin(); + while (iter != itsProcPool.end()) { + result &= iter->second->start(); + ++iter; + } + return (result); +} + +bool ProcRuler::stopAll() +{ + bool result = true; + + iterator iter = itsProcPool.begin(); + while (iter != itsProcPool.end()) { + result &= iter->second->stop(); + ++iter; + } + return (result); +} + +void ProcRuler::markAsStopped(const string& aProcName) +{ + iterator iter = itsProcPool.find(aProcName); + if (iter != itsProcPool.end()) { + iter->second->markAsStopped(); + } +} + +// @@ TEMP +void ProcRuler::show() +{ + iterator iter = itsProcPool.begin(); + while (iter != itsProcPool.end()) { + cout << iter->second; + ++iter; + } +} + + + } // namespace ACC +} // namespace LOFAR diff --git a/MAC/APL/Appl_Controller/src/ProcRuler.h b/MAC/APL/Appl_Controller/src/ProcRuler.h new file mode 100644 index 0000000000000000000000000000000000000000..a1450fdcfbae41d1cd474baeb295e69b689a5980 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/ProcRuler.h @@ -0,0 +1,87 @@ +//# ProcRuler.h: Controller to start/stop processes. +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# Note: This source is read best with tabstop 4. +//# +//# $Id$ + +#ifndef LOFAR_ACCBIN_PROCRULER_H +#define LOFAR_ACCBIN_PROCRULER_H + +// \file +// Controller to start/stop processes. + +//# Never #include <config.h> or #include <lofar_config.h> in a header file! +//# Includes +#include <Common/lofar_map.h> +#include <Common/LofarTypes.h> +#include "ProcRule.h" + +namespace LOFAR { + namespace ACC { +// \addtogroup ACCbin +// @{ + +//# Forward Declarations +//class forward; + + +// Controller to start/stop processes. +class ProcRuler +{ +public: + typedef map<string, ProcRule*>::iterator iterator; + + ProcRuler(); + ~ProcRuler(); + + void add (const ProcRule& aProcRule); + void remove(const string& aProcName); + + bool start(const string& aProcName); + bool stop (const string& aProcName); + + bool startAll(); + bool stopAll(); + + void markAsStopped(const string& aProcName); + + uint16 size(); + + void show(); // TEMP +private: + // Copying is not allowed + ProcRuler(const ProcRuler& that); + ProcRuler& operator=(const ProcRuler& that); + + //# Datamembers + map<string, ProcRule*> itsProcPool; +}; + +inline uint16 ProcRuler::size() +{ + return (itsProcPool.size()); +} + +// @} addgroup + } // namespace ACC +} // namespace LOFAR + +#endif diff --git a/MAC/APL/Appl_Controller/src/StateEngine.cc b/MAC/APL/Appl_Controller/src/StateEngine.cc new file mode 100644 index 0000000000000000000000000000000000000000..9dd434f59aa934a3e84825130a26a3915a3e1abf --- /dev/null +++ b/MAC/APL/Appl_Controller/src/StateEngine.cc @@ -0,0 +1,214 @@ +//# StateEngine.cc: (internal) command sequence definitions +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +//# Includes +#include <Common/LofarLogger.h> +#include "StateEngine.h" + +namespace LOFAR { + namespace ACC { + +const uint16 CMD_SEQ_LENGTH = 4; + +typedef struct CmdSeq { + ACCmd userCmd; + ACState cmdSeq [CMD_SEQ_LENGTH]; +} CmdSeq_t; + +static CmdSeq_t theirCmdSeqTable[] = { +{ ACCmdNone, + { StateNone, StateNone, StateNone, StateNone } }, +{ ACCmdBoot, + { StateInitController, StateCreatePSubset,StateStartupAppl, StateNone } }, +{ ACCmdDefine, + { StateDefineCmd, StateNone, StateNone, StateNone } }, +{ ACCmdInit, + { StateInitCmd, StateNone, StateNone, StateNone } }, +{ ACCmdRun, + { StateRunCmd, StateNone, StateNone, StateNone } }, +{ ACCmdPause, + { StatePauseCmd, StateNone, StateNone, StateNone } }, +{ ACCmdRelease, + { StateReleaseCmd, StateNone, StateNone, StateNone } }, +{ ACCmdSnapshot,{ + StateSnapshotCmd, StateNone, StateNone, StateNone } }, +{ ACCmdRecover, + { StateRecoverCmd, StateNone, StateNone, StateNone } }, +{ ACCmdReinit, // TODO + { StateReinitCmd, StateNone, StateNone, StateNone } }, +{ ACCmdInfo, + { StateInfoCmd, StateNone, StateNone, StateNone } }, +{ ACCmdQuit, + { StateQuitCmd, StateKillAppl, StateNone, StateNone } } +}; + +// Array with the max lifetime times of the states +time_t StateLifeTime [NR_OF_STATES]; + +#define CMD_SEQ_TABLE_SIZE ((sizeof(theirCmdSeqTable)/sizeof(CmdSeq_t)) - 1) + +StateEngine::StateEngine() : + itsSequence (0), + itsStepNr (0), + itsStateFinished (false), + itsStateExpireTime(0) +{ + // The state InitController runs before any timevalues are known. + StateLifeTime[StateInitController] = 900; +} + +StateEngine::~StateEngine() +{} + +void StateEngine::init(const ParameterSet* aPS) +{ + // (constant) timer values for the states + StateLifeTime[StateCreatePSubset]= aPS->getTime("ApplCtrl.timeout_createsubsets"); + StateLifeTime[StateStartupAppl] = aPS->getTime("ApplCtrl.timeout_startup"); + StateLifeTime[StateDefineCmd] = aPS->getTime("ApplCtrl.timeout_define"); + StateLifeTime[StateInitCmd] = aPS->getTime("ApplCtrl.timeout_init"); + StateLifeTime[StateRunCmd] = aPS->getTime("ApplCtrl.timeout_run"); + StateLifeTime[StatePauseCmd] = aPS->getTime("ApplCtrl.timeout_pause"); + StateLifeTime[StateReleaseCmd] = aPS->getTime("ApplCtrl.timeout_release"); + StateLifeTime[StateRecoverCmd] = aPS->getTime("ApplCtrl.timeout_recover"); + StateLifeTime[StateSnapshotCmd] = aPS->getTime("ApplCtrl.timeout_snapshot"); + StateLifeTime[StateReinitCmd] = aPS->getTime("ApplCtrl.timeout_reinit"); + StateLifeTime[StateInfoCmd] = aPS->getTime("ApplCtrl.timeout_info"); + StateLifeTime[StateQuitCmd] = aPS->getTime("ApplCtrl.timeout_quit"); + StateLifeTime[StateKillAppl] = aPS->getTime("ApplCtrl.timeout_kill"); +} + +void StateEngine::reset() +{ + LOG_TRACE_STAT ("StateEngine:reset"); + + itsSequence = 0; + itsStepNr = 0; + itsStateFinished = false; + itsStateExpireTime = 0; +} + +ACState StateEngine::startSequence(ACCmd aStartPoint) throw (Exception) +{ + // loop through SeqTable to find the given ACCmd. + for (int i = CMD_SEQ_TABLE_SIZE; i > 0; i--) { + if (theirCmdSeqTable[i].userCmd == aStartPoint) { + // startpoint of sequence found, start with first state + itsSequence = i; + itsStepNr = 0; + itsStateFinished= false; + ACState stateNr = theirCmdSeqTable[itsSequence].cmdSeq[itsStepNr]; + + LOG_DEBUG (formatString("Start stateSequence[%d][%d]=%s, time=%d", + itsSequence, itsStepNr, + stateStr(stateNr).c_str(), + StateLifeTime[stateNr])); + + // start timer for this state + setStateLifeTime(StateLifeTime[stateNr]); + return (stateNr); + } + } + + THROW (Exception, "No command sequences start with command " << ACCmdName(aStartPoint)); + +} + +ACState StateEngine::getState() +{ + return (theirCmdSeqTable[itsSequence].cmdSeq[itsStepNr]); +} + +ACState StateEngine::nextState() +{ + itsStepNr++; + itsStateFinished = false; + + ASSERTSTR(itsStepNr < CMD_SEQ_LENGTH, "itsStepNr became " << itsStepNr); + + // end of sequence reached? Reset indices. + if (theirCmdSeqTable[itsSequence].cmdSeq[itsStepNr] == StateNone) { + reset(); + } + + ACState stateNr = theirCmdSeqTable[itsSequence].cmdSeq[itsStepNr]; + + LOG_DEBUG (formatString("Next state[%d][%d]=%s, time=%d", + itsSequence, itsStepNr, + stateStr(stateNr).c_str(), + StateLifeTime[stateNr])); + + setStateLifeTime(StateLifeTime[stateNr]); + + return (stateNr); +} + +string StateEngine::stateStr(uint16 stateNr) const +{ + static const char* const stateNames[] = { + "Idle", + "InitController", + "CreatePSubset", + "StartupAppl", + "DefineState", + "InitState", + "RunState", + "PauseState", + "ReleaseState", + "RecoverState", + "SnapshotState", + "ReinitState", + "InfoState", + "QuitState", + "KillAppl" + }; + + if (stateNr >= NR_OF_STATES) { + return (""); + } + + return (stateNames[stateNr]); +} + +// +// operator<< +// +std::ostream& operator<< (std::ostream& os, const StateEngine& anEngine) +{ + if (anEngine.itsStateExpireTime) { + os << "Timer : " << timeString(anEngine.itsStateExpireTime) << endl; + os << "State : " << + anEngine.stateStr(theirCmdSeqTable[anEngine.itsSequence].cmdSeq[anEngine.itsStepNr]) << endl; + + } + else { + os << "Timer : off" << endl; + } + + return (os); +} + + } // namespace ACC +} // namespace LOFAR diff --git a/MAC/APL/Appl_Controller/src/StateEngine.h b/MAC/APL/Appl_Controller/src/StateEngine.h new file mode 100644 index 0000000000000000000000000000000000000000..8860093af673c512876b564bdbc13efc130afdd5 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/StateEngine.h @@ -0,0 +1,150 @@ +//# StateEngine.h: (internal) sequence definitions of the command states. +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# Note: This source is read best with tabstop 4. +//# +//# $Id$ + +#ifndef LOFAR_ACCBIN_STATEENGINE_H +#define LOFAR_ACCBIN_STATEENGINE_H + +// \file +// (internal) sequence definitions of the command states. + +//# Never #include <config.h> or #include <lofar_config.h> in a header file! +//# Includes +#include <Common/Exception.h> +#include <ALC/DH_ApplControl.h> +#include <Common/ParameterSet.h> + +using namespace LOFAR::ACC::ALC; + +namespace LOFAR { + namespace ACC { +// \addtogroup ACCbin +// @{ + +// define application controller states. +enum ACState { StateNone = 0, StateInitController, + StateCreatePSubset, StateStartupAppl, StateDefineCmd, + StateInitCmd, StateRunCmd, StatePauseCmd, + StateReleaseCmd, StateRecoverCmd, StateSnapshotCmd, + StateReinitCmd, StateInfoCmd, StateQuitCmd, + StateKillAppl, + NR_OF_STATES +}; + +// The execution of an AC command is cut into several states that must be +// performed consecutive. The StateEngine manages these sequences of states. +class StateEngine +{ +public: + StateEngine(); + ~StateEngine(); + + // Initialize the Engine with timer values. + void init(const ParameterSet* aPS); + + // Reset to original state. + void reset(); + + // Start a new state sequence. When the requested state is not a start of + // a sequence an exception is thrown. + ACState startSequence(ACCmd aStartPoint) throw (Exception); + + // Get current state. + ACState getState(); + + // Current state is finished, hop the next state and return its value. + ACState nextState(); + + // Report the current state is ready. + void ready(); + + // Ask if the next state is waiting. + bool isStateFinished(); + + // Command for handling the state expire timer + void setStateLifeTime (time_t anInterval); + void resetStateExpireTime(); + bool IsStateExpired (); + + // return name of state + string stateStr(uint16 stateNr) const; + + friend std::ostream& operator<< (std::ostream& os, + const StateEngine& anEngine); +private: + uint16 itsSequence; + uint16 itsStepNr; + bool itsStateFinished; + + // GMT time the current state expires + time_t itsStateExpireTime; + + // (constant) timer values for the states + time_t itsCreatePSubsetTime; + time_t itsStartupApplTime; + time_t itsDefineCmdTime; + time_t itsInitCmdTime; + time_t itsRunCmdTime; + time_t itsPauseCmdTime; + time_t itsReleaseCmdTime; + time_t itsRecoverCmdTime; + time_t itsSnapshotCmdTime; + time_t itsReinitCmdTime; + time_t itsInfoCmdTime; + time_t itsQuitCmdTime; + time_t itsKillApplTime; +}; + +inline void StateEngine::ready() +{ +// LOG_TRACE_STAT ("StateEngine:ready"); + LOG_DEBUG ("StateEngine:ready"); + itsStateFinished = true; + itsStateExpireTime = 0; +} + +inline bool StateEngine::isStateFinished() +{ + return (itsStateFinished); +} + +inline void StateEngine::setStateLifeTime(time_t anInterval) +{ + itsStateExpireTime = time(0) + anInterval; +} + +inline void StateEngine::resetStateExpireTime() +{ + itsStateExpireTime = 0; +} + +inline bool StateEngine::IsStateExpired() +{ + return (itsStateExpireTime && (itsStateExpireTime < time(0))); +} + +// @} addgroup + } // namespace ACC +} // namespace LOFAR + +#endif diff --git a/MAC/APL/Appl_Controller/src/forkexec.cc b/MAC/APL/Appl_Controller/src/forkexec.cc new file mode 100644 index 0000000000000000000000000000000000000000..f026d94efea8e018858f1bc6839a34281c09e0cb --- /dev/null +++ b/MAC/APL/Appl_Controller/src/forkexec.cc @@ -0,0 +1,74 @@ +//# forkexec.cc: Custom fork/exec implementation for more control +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +//# Includes +#include "forkexec.h" + +#include <unistd.h> +#include <sys/wait.h> +#include <errno.h> + +namespace LOFAR { + namespace ACC { + +int32 forkexec( const char *command ) +{ + int status; + pid_t pid; + + pid = fork(); + + switch (pid) { + case -1: + // error + return -1; + + case 0: + // child process + + // close all filedescriptors + for (int f = dup(2); f > 2; --f) { + while ( close(f) == EINTR ) + ; + } + + execl("/bin/sh", "/bin/sh", "-c", command, static_cast<char*>(0)); + + // only reached if exec fails + _exit(1); + + default: + // parent process + if (waitpid(pid, &status, 0) == -1) { + // error + return errno; + } + + return WEXITSTATUS(status); + } +} + + + } // namespace ACC +} // namespace LOFAR diff --git a/MAC/APL/Appl_Controller/src/forkexec.h b/MAC/APL/Appl_Controller/src/forkexec.h new file mode 100644 index 0000000000000000000000000000000000000000..6c14b23d17668e264cb695ded15b005ff0c8b4b5 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/forkexec.h @@ -0,0 +1,41 @@ +//# PR_BGL.h: Custom fork/exec implementation for more control +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# Note: This source is read best with tabstop 4. +//# +//# $Id: PR_BGL.h 10517 2007-09-17 07:54:57Z overeem $ + +#ifndef LOFAR_ACCBIN_FORKEXEC_H +#define LOFAR_ACCBIN_FORKEXEC_H + +//# Never #include <config.h> or #include <lofar_config.h> in a header file! +//# Includes +#include <Common/LofarTypes.h> + +namespace LOFAR { + namespace ACC { + +// a custom implementation of system( command ) +int32 forkexec( const char *command ); + + } // namespace ACC +} // namespace LOFAR + +#endif diff --git a/MAC/APL/Appl_Controller/src/myACClientFunctions.h b/MAC/APL/Appl_Controller/src/myACClientFunctions.h new file mode 100644 index 0000000000000000000000000000000000000000..d0e23661822174f20d89eb29c587e39f4adfb552 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/myACClientFunctions.h @@ -0,0 +1,59 @@ +//# myACClientFunctions.h: Implements the async functions of the AC client +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +#ifndef ACC_MYACCLIENTFUNCTIONS_H +#define ACC_MYACCLIENTFUNCTIONS_H + +//# Never #include <config.h> or #include <lofar_config.h> in a header file! + +//# Includes +#include <ALC/ACAsyncClient.h> + +using namespace LOFAR::ACC::ALC; + +namespace LOFAR { +namespace ACC { + + +// Description of class. +class myACClientFunctions : public ACClientFunctions +{ + string supplyInfoFunc(const string& /*keyList*/) + { return ("myACClientFunctions::supplyInfo from ACClient was called"); } + + void handleAnswerMsg(const string& answer) + { cout << "myACClientFunctions::handleAnswerMessage from ACClient was called"; + cout << "Answer=" << answer << endl; + } + + void handleAckMsg(ACCmd cmd, uint16 result, const string& info) + { cout << "myACClientFunctions::handleAckMessage was called" << endl; + cout << "command = " << ACCmdName(cmd) << ", result = " << result + << ", info = " << info << endl; + } + +}; + +} // namespace ACC +} // namespace LOFAR + +#endif diff --git a/MAC/APL/Appl_Controller/src/startAP.sh b/MAC/APL/Appl_Controller/src/startAP.sh new file mode 100755 index 0000000000000000000000000000000000000000..ce8d145a1c57e8973112d16b82565aa61ec14b32 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/startAP.sh @@ -0,0 +1,24 @@ +# startAP.sh nodename procID executable paramfile +# +# nodename hostname[.domain] +# procID processname<nr> +# executable processname +# parameterfile procID.ps +# +# start the given executable and creates a corresponding pid file for stopping the process. +# + +# DISABLED this script: startBGL.sh starts all CEP processes +exit + +# now all ACC processes expect to be started with "ACC" as first parameter + +# start process +echo $3 ACC $4 $2 >../log/$2.startup +$3 ACC $4 $2 & + +# get its pid +pid=`echo $!` + +# construct pid file for stop shell +echo "$pid" > /opt/lofar/var/run/$2.pid diff --git a/MAC/APL/Appl_Controller/src/startMPI.sh b/MAC/APL/Appl_Controller/src/startMPI.sh new file mode 100755 index 0000000000000000000000000000000000000000..916102138184d96b9a1a52730435d3e671dbfdc9 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/startMPI.sh @@ -0,0 +1,28 @@ +# startMPI.sh jobName machinefile executable paramfile noNodes +# +# $1 jobName identifier for this job +# $2 machinefile procID.machinefile +# $3 executable processname +# $4 parameterfile procID.ps +# $5 numberOfNodes +# +# calls mpirun and remembers the pid +# + +# DISABLED this script: startBGL.sh starts all CEP processes +exit + +# now all ACC processes expect to be started with ACC as first parameter + +# start process +# TODO: on some hosts, mpirun has a different name (or a specific path) +# on some hosts, we should use -hostfile instead of -machinefile +echo "mpirun -np $5 -machinefile $2 $3 ACC $4 " > startMPI.output +( ( mpirun -np $5 -machinefile $2 $3 ACC $4 ) &> $1.output ) & +fi + +# get its pid +pid=`echo $!` + +# construct pid file for stop shell +echo "$pid" > $4.pid diff --git a/MAC/APL/Appl_Controller/src/stopAP.sh b/MAC/APL/Appl_Controller/src/stopAP.sh new file mode 100755 index 0000000000000000000000000000000000000000..1984d5a8a53eaee28706a52c5e607adedf99c2aa --- /dev/null +++ b/MAC/APL/Appl_Controller/src/stopAP.sh @@ -0,0 +1,14 @@ +# stopAP.sh nodename procID +# +# nodename hostname[.domain] +# procID processname<nr> +# +# Stops the given process by killing the process whose pid is in the +# proces.pid file. + +# DISABLED this script: startBGL.sh starts all CEP processes +exit + +echo -n "Killing process "; cat /opt/lofar/var/run/$2.pid +kill -9 `cat /opt/lofar/var/run/$2.pid` +rm -f /opt/lofar/var/run/$2.pid /opt/lofar/var/run/$2.parset diff --git a/MAC/APL/Appl_Controller/src/stopMPI.sh b/MAC/APL/Appl_Controller/src/stopMPI.sh new file mode 100755 index 0000000000000000000000000000000000000000..63b6d6171a5b101c18911af1a6b10358fb404d74 --- /dev/null +++ b/MAC/APL/Appl_Controller/src/stopMPI.sh @@ -0,0 +1,16 @@ +# stopMPI.sh procID +# +# procID processname +# +# Stops the given process by killing the process whose pid is in the +# proces.pid file. + +# DISABLED this script: startBGL.sh starts all CEP processes +exit + +# TODO: for some mpi versions it is not enough to kill mpirun +# we could "killall executable", but that would also kill +# processes started by another ApplicationController +echo -n "Killing process "; cat $1.pid +kill -s 15 `cat $1.pid` +rm -f $1.pid $1*.ps diff --git a/MAC/APL/CEPCU/CMakeLists.txt b/MAC/APL/CEPCU/CMakeLists.txt index 2d8bc3474a045b8af49b61220f85cbb7e6315da4..f7b97cd31136c339dbcd2294693f547b144465fd 100644 --- a/MAC/APL/CEPCU/CMakeLists.txt +++ b/MAC/APL/CEPCU/CMakeLists.txt @@ -1,7 +1,7 @@ # $Id$ # Do not split the following line, otherwise makeversion will fail! -lofar_package(CEPCU 1.0 DEPENDS Common ApplCommon MACIO GCFTM GCFRTDB APLCommon RTDBCommon OTDB) +lofar_package(CEPCU 1.0 DEPENDS Common ApplCommon MACIO GCFTM GCFRTDB APLCommon RTDBCommon OTDB Stream) include(LofarFindPackage) lofar_find_package(Boost REQUIRED COMPONENTS date_time) diff --git a/MAC/APL/CEPCU/src/CEPlogProcessor/CEPlogProcessor.cc b/MAC/APL/CEPCU/src/CEPlogProcessor/CEPlogProcessor.cc index 00700b2e544ea46d20b000ea60877c79aa71fed7..f038ccd6d2cdc178fe3f4c5a9d11f12dfe07b7a8 100644 --- a/MAC/APL/CEPCU/src/CEPlogProcessor/CEPlogProcessor.cc +++ b/MAC/APL/CEPCU/src/CEPlogProcessor/CEPlogProcessor.cc @@ -24,6 +24,7 @@ #include <Common/LofarConstants.h> #include <Common/LofarLocators.h> #include <Common/StringUtil.h> +#include <Stream/SocketStream.h> #include <ApplCommon/LofarDirs.h> #include <ApplCommon/Observation.h> #include <ApplCommon/StationInfo.h> @@ -800,6 +801,16 @@ void CEPlogProcessor::unregisterObservation(int obsID) itsFeedback.erase(obsID); itsTempObsMapping.erase(obsID); + + // signal to MAC that the observation is finished, see redmine ticket #4021 + LOG_INFO_STR("Observation " << obsID << " finished, informing OnlineControl"); + try { + SocketStream ss("localhost", 21000 + obsID % 1000, SocketStream::TCP, SocketStream::Client, time(0) + 30); + const char *status = "FINISHED"; // alternative: "ABORT" + ss.write(&status[0], strlen(status)); + } catch(Exception &ex) { + LOG_ERROR_STR("Caught exception: " << ex); + } } diff --git a/MAC/APL/CEPCU/src/OnlineControl/OnlineControl.cc b/MAC/APL/CEPCU/src/OnlineControl/OnlineControl.cc index 8997d98f59ea295df9ec6edaf1a11ce7462e9cae..d8907b1f9b5fcabe40912347f8b40122455e5d7d 100644 --- a/MAC/APL/CEPCU/src/OnlineControl/OnlineControl.cc +++ b/MAC/APL/CEPCU/src/OnlineControl/OnlineControl.cc @@ -30,6 +30,7 @@ #include <Common/ParameterRecord.h> #include <Common/Exceptions.h> #include <Common/SystemUtil.h> +#include <Common/hexdump.h> #include <ApplCommon/StationInfo.h> #include <ApplCommon/Observation.h> #include <ApplCommon/LofarDirs.h> @@ -47,6 +48,8 @@ #include "Response.h" #include "forkexec.h" #include <OTDB/TreeValue.h> // << need to include this after OnlineControl! ??? +#include <OTDB/TreeMaintenance.h> +#include <OTDB/TreeStateConv.h> #include "PVSSDatapointDefs.h" using namespace LOFAR::GCF::PVSS; @@ -78,13 +81,17 @@ OnlineControl::OnlineControl(const string& cntlrName) : itsTimerPort (0), itsLogControlPort (0), itsState (CTState::NOSTATE), + itsFeedbackListener (0), // QUICK FIX #4022 + itsFeedbackPort (0), // QUICK FIX #4022 + itsFeedbackResult (CT_RESULT_NO_ERROR), // QUICK FIX #4022 itsTreePrefix (""), itsInstanceNr (0), itsStartTime (), itsStopTime (), itsStopTimerID (0), itsFinishTimerID (0), - itsInFinishState (false) + itsInFinishState (false), + itsFeedbackAvailable(false) { LOG_TRACE_OBJ_STR (cntlrName << " construction"); @@ -107,6 +114,10 @@ OnlineControl::OnlineControl(const string& cntlrName) : // Controlport to logprocessor itsLogControlPort = new GCFTCPPort(*this, MAC_SVCMASK_CEPLOGCONTROL, GCFPortInterface::SAP, CONTROLLER_PROTOCOL); + // QUICK FIX #4022 + itsFeedbackListener = new GCFTCPPort (*this, "Feedbacklistener", GCFPortInterface::MSPP, CONTROLLER_PROTOCOL); + ASSERTSTR(itsFeedbackListener, "Cannot allocate TCP port for feedback"); + // for debugging purposes registerProtocol (CONTROLLER_PROTOCOL, CONTROLLER_PROTOCOL_STRINGS); registerProtocol (DP_PROTOCOL, DP_PROTOCOL_STRINGS); @@ -129,6 +140,11 @@ OnlineControl::~OnlineControl() if (itsTimerPort) { delete itsTimerPort; } + + if (itsFeedbackListener) { + itsFeedbackListener->close(); + delete itsFeedbackListener; + } } // @@ -139,15 +155,16 @@ void OnlineControl::signalHandler(int signum) LOG_INFO (formatString("SIGNAL %d detected", signum)); if (thisOnlineControl) { - thisOnlineControl->finish(); + thisOnlineControl->finish(CT_RESULT_MANUAL_ABORT); } } // -// finish() +// finish(result) // -void OnlineControl::finish() +void OnlineControl::finish(int result) { + itsFeedbackResult = result; TRAN(OnlineControl::finishing_state); } @@ -246,10 +263,10 @@ GCFEvent::TResult OnlineControl::initial_state(GCFEvent& event, GCFPortInterface break; case F_CONNECTED: - ASSERTSTR (&port == itsParentPort, "F_CONNECTED event from port " << port.getName()); break; case F_DISCONNECTED: + _handleDisconnect(port); break; default: @@ -274,7 +291,6 @@ GCFEvent::TResult OnlineControl::propset_state(GCFEvent& event, GCFPortInterface switch (event.signal) { case F_ENTRY: { // Get access to my own propertyset. -// uint32 obsID = globalParameterSet()->getUint32("Observation.ObsID"); string obsDPname = globalParameterSet()->getString("_DPname"); string propSetName(createPropertySetName(PSN_BGP_APPL, getName(), obsDPname)); LOG_DEBUG_STR ("Activating PropertySet: "<< propSetName); @@ -297,7 +313,7 @@ GCFEvent::TResult OnlineControl::propset_state(GCFEvent& event, GCFPortInterface case F_TIMER: { // must be timer that PropSet is online. // start StopTimer for safety. - LOG_INFO_STR("Starting QUIT timer that expires 5 seconds after end of observation"); + LOG_INFO("Starting QUIT timer that expires 5 seconds after end of observation"); ptime now(second_clock::universal_time()); itsStopTimerID = itsTimerPort->setTimer(time_duration(itsStopTime - now).total_seconds() + 5.0); @@ -306,16 +322,22 @@ GCFEvent::TResult OnlineControl::propset_state(GCFEvent& event, GCFPortInterface itsParentPort = itsParentControl->registerTask(this); // results in CONTROL_CONNECT + // QUICK FIX #4022 + uint32 obsID = globalParameterSet()->getUint32("Observation.ObsID"); + LOG_INFO_STR("Openening feedback port for OLAP: " << MAC_ONLINE_FEEDBACK_QF + obsID%1000); + itsFeedbackListener->setPortNumber(MAC_ONLINE_FEEDBACK_QF + obsID%1000); + itsFeedbackListener->open(); // will result in F_CONN + LOG_DEBUG ("Going to operational state"); TRAN(OnlineControl::active_state); // go to next state. } break; case F_CONNECTED: - ASSERTSTR (&port == itsParentPort, "F_CONNECTED event from port " << port.getName()); break; case F_DISCONNECTED: + _handleDisconnect(port); break; default: @@ -343,21 +365,26 @@ GCFEvent::TResult OnlineControl::active_state(GCFEvent& event, GCFPortInterface& // update PVSS itsPropertySet->setValue(PN_FSM_CURRENT_ACTION, GCFPVString("active")); itsPropertySet->setValue(PN_FSM_ERROR, GCFPVString("")); - } - break; + } break; - case F_ACCEPT_REQ: - break; + // QUICKFIX #4022 + case F_ACCEPT_REQ: { + _handleAcceptRequest(port); + } break; case F_CONNECTED: { - ASSERTSTR (&port == itsParentPort, "F_CONNECTED event from port " << port.getName()); - } - break; +// ASSERTSTR (&port == itsParentPort, "F_CONNECTED event from port " << port.getName()); + LOG_INFO_STR("F_CONNECTED event from port " << port.getName()); + } break; case F_DISCONNECTED: { - port.close(); - } - break; + _handleDisconnect(port); + } break; + + // QUICKFIX #4022 + case F_DATAIN: { + _handleDataIn(port); + } break; case DP_CHANGED: _databaseEventHandler(event); @@ -368,7 +395,7 @@ GCFEvent::TResult OnlineControl::active_state(GCFEvent& event, GCFPortInterface& if (timerEvent.id == itsStopTimerID) { LOG_DEBUG("StopTimer expired, starting QUIT sequence"); itsStopTimerID = 0; - finish(); + finish(itsFeedbackResult); // itsFinishTimerID = itsTimerPort->setTimer(30.0); } #if 0 @@ -387,6 +414,7 @@ GCFEvent::TResult OnlineControl::active_state(GCFEvent& event, GCFPortInterface& case CONTROL_CONNECT: { CONTROLConnectEvent msg(event); LOG_DEBUG_STR("Received CONNECT(" << msg.cntlrName << ")"); + itsMyName = msg.cntlrName; // first inform CEPlogProcessor CONTROLAnnounceEvent announce; announce.observationID = toString(getObservationNr(msg.cntlrName)); @@ -455,7 +483,7 @@ GCFEvent::TResult OnlineControl::active_state(GCFEvent& event, GCFPortInterface& CONTROLQuitEvent msg(event); LOG_DEBUG_STR("Received QUIT(" << msg.cntlrName << ")"); _setState(CTState::QUIT); - finish(); + finish(itsFeedbackResult); break; } @@ -488,7 +516,7 @@ GCFEvent::TResult OnlineControl::finishing_state(GCFEvent& event, GCFPortInterfa itsTimerPort->cancelAllTimers(); // update PVSS - itsPropertySet->setValue(PN_FSM_CURRENT_ACTION, GCFPVString("finished")); + itsPropertySet->setValue(PN_FSM_CURRENT_ACTION, GCFPVString("stopping")); itsPropertySet->setValue(PN_FSM_ERROR, GCFPVString("")); ParameterSet* thePS = globalParameterSet(); // shortcut to global PS. @@ -528,14 +556,23 @@ GCFEvent::TResult OnlineControl::finishing_state(GCFEvent& event, GCFPortInterfa uint32 result = forkexec(startCmd.c_str()); LOG_INFO_STR ("Result of command = " << result); - itsTimerPort->setTimer(302.0); // IONProc, and thus CEPlogProcessor, can take up to 5 minutes to wrap up + if (itsFeedbackAvailable) { + TRAN(OnlineControl::completing_state); + } break; } - case F_TIMER: - _passMetadatToOTDB(); - GCFScheduler::instance()->stop(); - break; + case F_ACCEPT_REQ: { + _handleAcceptRequest(port); + } break; + + case F_DISCONNECTED: { + _handleDisconnect(port); + } break; + + case F_DATAIN: { + _handleDataIn(port); // may switch to completing state + } break; default: LOG_DEBUG("finishing_state, default"); @@ -546,6 +583,51 @@ GCFEvent::TResult OnlineControl::finishing_state(GCFEvent& event, GCFPortInterfa return (status); } +// +// completing_state(event, port) +// +// +GCFEvent::TResult OnlineControl::completing_state(GCFEvent& event, GCFPortInterface& port) +{ + LOG_INFO_STR ("completing:" << eventName(event) << "@" << port.getName()); + + GCFEvent::TResult status = GCFEvent::HANDLED; + + switch (event.signal) { + case F_ENTRY: { + // update PVSS + itsPropertySet->setValue(PN_FSM_CURRENT_ACTION, GCFPVString("completing")); + itsPropertySet->setValue(PN_FSM_ERROR, GCFPVString("")); + + _passMetadatToOTDB(); + + itsPropertySet->setValue(PN_FSM_CURRENT_ACTION, GCFPVString("finished")); + itsPropertySet->setValue(PN_FSM_ERROR, GCFPVString("")); + + CONTROLQuitedEvent msg; + msg.cntlrName = itsMyName; + msg.result = itsFeedbackResult; + itsParentPort->send(msg); + + itsTimerPort->setTimer(0.3); + } break; + + case F_TIMER: + GCFScheduler::instance()->stop(); + break; + + case F_DISCONNECTED: + _handleDisconnect(port); + break; + + case F_DATAIN: + _handleDataIn(port); + break; + + } + return (status); +} + // // _setupBGPmappingTables // @@ -713,97 +795,156 @@ uint32 OnlineControl::_doBoot() void OnlineControl::_passMetadatToOTDB() { // No name specified? + bool metadataFileAvailable (true); uint32 obsID(globalParameterSet()->getUint32("Observation.ObsID", 0)); string feedbackFile = observationParset(obsID)+"_feedback"; LOG_INFO_STR ("Expecting metadata to be in file " << feedbackFile); if (feedbackFile.empty()) { - return; + metadataFileAvailable = false; } // read parameterset - try { - ParameterSet metadata; - metadata.adoptFile(feedbackFile); - - // Try to setup the connection with the database - string confFile = globalParameterSet()->getString("OTDBconfFile", "SASGateway.conf"); - ConfigLocator CL; - string filename = CL.locate(confFile); - LOG_INFO_STR("Trying to read database information from file " << filename); - ParameterSet otdbconf; - otdbconf.adoptFile(filename); - string database = otdbconf.getString("SASGateway.OTDBdatabase"); - string dbhost = otdbconf.getString("SASGateway.OTDBhostname"); - OTDBconnection conn("paulus", "boskabouter", database, dbhost); - if (!conn.connect()) { - LOG_FATAL_STR("Cannot connect to database " << database << " on machine " << dbhost); - return; - } - LOG_INFO_STR("Connected to database " << database << " on machine " << dbhost); - - TreeValue tv(&conn, getObservationNr(getName())); - - // Loop over the parameterset and send the information to the KVTlogger. - // During the transition phase from parameter-based to record-based storage in OTDB the - // nodenames ending in '_' are implemented both as parameter and as record. - ParameterSet::iterator iter = metadata.begin(); - ParameterSet::iterator end = metadata.end(); - while (iter != end) { - string key(iter->first); // make destoyable copy - rtrim(key, "[]0123456789"); - // bool doubleStorage(key[key.size()-1] == '_'); - bool isRecord(iter->second.isRecord()); - // isRecord doubleStorage - // -------------------------------------------------------------- - // Y Y store as record and as parameters - // Y N store as parameters - // N * store parameter - if (!isRecord) { - LOG_DEBUG_STR("BASIC: " << iter->first << " = " << iter->second); - tv.addKVT(iter->first, iter->second, ptime(microsec_clock::local_time())); - } - else { - // if (doubleStorage) { - // LOG_DEBUG_STR("RECORD: " << iter->first << " = " << iter->second); - // tv.addKVT(iter->first, iter->second, ptime(microsec_clock::local_time())); - // } - // to store is a node/param values the last _ should be stipped of - key = iter->first; // destroyable copy - // string::size_type pos = key.find_last_of('_'); - // key.erase(pos,1); - ParameterRecord pr(iter->second.getRecord()); - ParameterRecord::const_iterator prIter = pr.begin(); - ParameterRecord::const_iterator prEnd = pr.end(); - while (prIter != prEnd) { - LOG_DEBUG_STR("ELEMENT: " << key+"."+prIter->first << " = " << prIter->second); - tv.addKVT(key+"."+prIter->first, prIter->second, ptime(microsec_clock::local_time())); - prIter++; + // Try to setup the connection with the database + string confFile = globalParameterSet()->getString("OTDBconfFile", "SASGateway.conf"); + ConfigLocator CL; + string filename = CL.locate(confFile); + LOG_INFO_STR("Trying to read database information from file " << filename); + ParameterSet otdbconf; + otdbconf.adoptFile(filename); + string database = otdbconf.getString("SASGateway.OTDBdatabase"); + string dbhost = otdbconf.getString("SASGateway.OTDBhostname"); + OTDBconnection conn("paulus", "boskabouter", database, dbhost); + if (!conn.connect()) { + LOG_FATAL_STR("Cannot connect to database " << database << " on machine " << dbhost); + // WE DO HAVE A PROBLEM HERE BECAUSE THIS PIPELINE CANNOT BE SET TO FINISHED IN SAS. + return; + } + LOG_INFO_STR("Connected to database " << database << " on machine " << dbhost); + + if (metadataFileAvailable) { + try { + TreeValue tv(&conn, getObservationNr(getName())); + ParameterSet metadata; + metadata.adoptFile(feedbackFile); + // Loop over the parameterset and send the information to the KVTlogger. + // During the transition phase from parameter-based to record-based storage in OTDB the + // nodenames ending in '_' are implemented both as parameter and as record. + ParameterSet::iterator iter = metadata.begin(); + ParameterSet::iterator end = metadata.end(); + while (iter != end) { + string key(iter->first); // make destoyable copy + rtrim(key, "[]0123456789"); + // bool doubleStorage(key[key.size()-1] == '_'); + bool isRecord(iter->second.isRecord()); + // isRecord doubleStorage + // -------------------------------------------------------------- + // Y Y store as record and as parameters + // Y N store as parameters + // N * store parameter + if (!isRecord) { + LOG_DEBUG_STR("BASIC: " << iter->first << " = " << iter->second); + tv.addKVT(iter->first, iter->second, ptime(microsec_clock::local_time())); + } + else { + // if (doubleStorage) { + // LOG_DEBUG_STR("RECORD: " << iter->first << " = " << iter->second); + // tv.addKVT(iter->first, iter->second, ptime(microsec_clock::local_time())); + // } + // to store is a node/param values the last _ should be stipped of + key = iter->first; // destroyable copy + // string::size_type pos = key.find_last_of('_'); + // key.erase(pos,1); + ParameterRecord pr(iter->second.getRecord()); + ParameterRecord::const_iterator prIter = pr.begin(); + ParameterRecord::const_iterator prEnd = pr.end(); + while (prIter != prEnd) { + LOG_DEBUG_STR("ELEMENT: " << key+"."+prIter->first << " = " << prIter->second); + tv.addKVT(key+"."+prIter->first, prIter->second, ptime(microsec_clock::local_time())); + prIter++; + } } + iter++; } - iter++; + LOG_INFO_STR(metadata.size() << " metadata values send to SAS"); + } + catch (APSException &e) { + // Parameterfile not found + LOG_FATAL(e.text()); } - LOG_INFO_STR(metadata.size() << " metadata values send to SAS"); - } - catch (APSException &e) { - // Parameterfile not found - LOG_FATAL(e.text()); } + + // finally report state to SAS + sleep (60); /// AAARRRRGGGHH, make 'sure' we are later than the ObservationControl. #4022 + TreeMaintenance tm(&conn); + TreeStateConv tsc(&conn); + tm.setTreeState(obsID, tsc.get("finished")); } // -------------------- Application-order administration -------------------- // -// _connectedHandler(port) +// _handleDisconnect(port) // -void OnlineControl::_connectedHandler(GCFPortInterface& /*port*/) +void OnlineControl::_handleDisconnect(GCFPortInterface& port) { + port.close(); + // QUICKFIX #4022 + if (&port == itsFeedbackPort) { + LOG_FATAL_STR("Lost connection with Feedback of OLAP."); + delete itsFeedbackPort; + itsFeedbackPort = 0; + } } // -// _disconnectedHandler(port) +// _handleAcceptRequest(port) // -void OnlineControl::_disconnectedHandler(GCFPortInterface& port) +void OnlineControl::_handleAcceptRequest(GCFPortInterface& port) { - port.close(); + ASSERTSTR(&port == itsFeedbackListener, "Incoming connection on main listener iso feedbackListener"); + itsFeedbackPort = new GCFTCPPort(); + itsFeedbackPort->init(*this, "feedback", GCFPortInterface::SPP, 0, true); // raw port + if (!itsFeedbackListener->accept(*itsFeedbackPort)) { + delete itsFeedbackPort; + itsFeedbackPort = 0; + LOG_ERROR("Connection with Python feedback FAILED"); + } + else { + LOG_INFO("Connection made on feedback port, accepting commands"); + } +} + +// +// _handleDataIn(port) +// +void OnlineControl::_handleDataIn(GCFPortInterface& port) +{ + ASSERTSTR(&port == itsFeedbackPort, "Didn't expect raw data on port " << port.getName()); + char buf[1024]; + ssize_t btsRead = port.recv((void*)&buf[0], 1023); + buf[btsRead] = '\0'; + string s; + hexdump(s, buf, btsRead); + LOG_INFO_STR("Received command on feedback port: " << s); + + if (!strcmp(buf, "ABORT")) { + itsFeedbackResult = CT_RESULT_PIPELINE_FAILED; + itsFeedbackAvailable = true; + } + else if (!strcmp(buf, "FINISHED")) { + itsFeedbackAvailable = true; + } + else { + LOG_FATAL_STR("Received command on feedback port unrecognized"); + } + + if (itsFeedbackAvailable) { // recognized command? + if (itsInFinishState) { // already stopped BGP? + TRAN(OnlineControl::completing_state); // pass metadata + } + else { + TRAN(OnlineControl::finishing_state); // stop BGP first. + } + } } }; // CEPCU diff --git a/MAC/APL/CEPCU/src/OnlineControl/OnlineControl.h b/MAC/APL/CEPCU/src/OnlineControl/OnlineControl.h index 8383e92c5f6fe579dc72db7051005ed6a99c0ca3..3c1b8bcfae432352551f857767241787aa3e40a1 100644 --- a/MAC/APL/CEPCU/src/OnlineControl/OnlineControl.h +++ b/MAC/APL/CEPCU/src/OnlineControl/OnlineControl.h @@ -72,13 +72,13 @@ public: // Connect to BGPAppl propset and start remaining tasks. GCFEvent::TResult propset_state (GCFEvent& e, GCFPortInterface& p); // Normal control mode. - GCFEvent::TResult active_state (GCFEvent& e, GCFPortInterface& p); - // Finishing mode. - GCFEvent::TResult finishing_state(GCFEvent& event, GCFPortInterface& port); + GCFEvent::TResult active_state (GCFEvent& e, GCFPortInterface& p); + GCFEvent::TResult finishing_state (GCFEvent& event, GCFPortInterface& port); + GCFEvent::TResult completing_state(GCFEvent& event, GCFPortInterface& port); // Interrupthandler for switching to finisingstate when exiting the program static void signalHandler (int signum); - void finish(); + void finish(int result); private: // avoid defaultconstruction and copying @@ -89,13 +89,15 @@ private: uint32 _doBoot(); void _setupBGPmappingTables(); void _finishController (uint16_t result); - void _connectedHandler (GCFPortInterface& port); - void _disconnectedHandler (GCFPortInterface& port); + void _handleDisconnect (GCFPortInterface& port); + void _handleAcceptRequest (GCFPortInterface& port); + void _handleDataIn (GCFPortInterface& port); void _setState (CTState::CTstateNr newState); void _databaseEventHandler(GCFEvent& event); void _passMetadatToOTDB (); // ----- datamembers ----- + string itsMyName; RTDBPropertySet* itsPropertySet; RTDBPropertySet* itsBGPApplPropSet; bool itsPropertySetInitialized; @@ -112,6 +114,11 @@ private: CTState::CTstateNr itsState; + // QUICK FIX #4022 + GCFTCPPort* itsFeedbackListener; + GCFTCPPort* itsFeedbackPort; + int itsFeedbackResult; + // ParameterSet variables string itsTreePrefix; uint32 itsInstanceNr; @@ -120,6 +127,7 @@ private: uint16 itsStopTimerID; uint16 itsFinishTimerID; bool itsInFinishState; + bool itsFeedbackAvailable; }; };//CEPCU diff --git a/MAC/APL/CEPCU/src/PythonControl/PythonControl.cc b/MAC/APL/CEPCU/src/PythonControl/PythonControl.cc index 8a6878e91d2dec3cfeac2c4a946b1f87ccaea972..738d3e3a8bf5b10132f97d8e41f2f23445ec2076 100644 --- a/MAC/APL/CEPCU/src/PythonControl/PythonControl.cc +++ b/MAC/APL/CEPCU/src/PythonControl/PythonControl.cc @@ -46,6 +46,8 @@ #include <APL/APLCommon/Controller_Protocol.ph> #include <APL/APLCommon/CTState.h> #include <OTDB/TreeValue.h> +#include <OTDB/TreeMaintenance.h> +#include <OTDB/TreeStateConv.h> #include "PythonControl.h" #include "PVSSDatapointDefs.h" @@ -123,11 +125,15 @@ PythonControl::PythonControl(const string& cntlrName) : PythonControl::~PythonControl() { LOG_TRACE_OBJ_STR (getName() << " destruction"); - if (itsListener) - { itsListener->close(); delete itsListener; } + if (itsListener) { + itsListener->close(); + delete itsListener; + } - if (itsFeedbackListener) // QUICK FIX #3633 - { itsFeedbackListener->close(); delete itsFeedbackListener; } + if (itsFeedbackListener) { // QUICK FIX #3633 + itsFeedbackListener->close(); + delete itsFeedbackListener; + } } // @@ -756,28 +762,27 @@ GCFEvent::TResult PythonControl::finishing_state(GCFEvent& event, GCFPortInterfa // void PythonControl::_passMetadatToOTDB() { + bool metadataFileAvailable (true); + // No name specified? if (itsFeedbackFile.empty()) { - return; + metadataFileAvailable = false; } - - // Copy file form remote system to localsystem - ParameterSet* thePS = globalParameterSet(); // shortcut to global PS. - string myPrefix (thePS->locateModule("PythonControl")+"PythonControl."); - string pythonHost(thePS->getString(myPrefix+"pythonHost","@pythonHost@")); - try { - if (copyFromRemote(realHostname(pythonHost), itsFeedbackFile, itsFeedbackFile) != 0) { - LOG_ERROR_STR("Failed to copy metadatafile " << itsFeedbackFile << " from host " << realHostname(pythonHost)); - return; + else { + // Copy file from remote system to localsystem + ParameterSet* thePS = globalParameterSet(); // shortcut to global PS. + string myPrefix (thePS->locateModule("PythonControl")+"PythonControl."); + string pythonHost(thePS->getString(myPrefix+"pythonHost","@pythonHost@")); + try { + if (copyFromRemote(realHostname(pythonHost), itsFeedbackFile, itsFeedbackFile) != 0) { + LOG_ERROR_STR("Failed to copy metadatafile " << itsFeedbackFile << " from host " << realHostname(pythonHost)); + metadataFileAvailable = false; + } + } + catch (...) { + metadataFileAvailable = false; } } - catch (...) { - return; - } - - // read parameterset - ParameterSet metadata; - metadata.adoptFile(itsFeedbackFile); // Try to setup the connection with the database string confFile = globalParameterSet()->getString("OTDBconfFile", "SASGateway.conf"); @@ -791,52 +796,65 @@ void PythonControl::_passMetadatToOTDB() OTDBconnection conn("paulus", "boskabouter", database, dbhost); if (!conn.connect()) { LOG_FATAL_STR("Cannot connect to database " << database << " on machine " << dbhost); + // WE DO HAVE A PROBLEM HERE BECAUSE THIS PIPELINE CANNOT BE SET TO FINISHED IN SAS. return; } LOG_INFO_STR("Connected to database " << database << " on machine " << dbhost); - TreeValue tv(&conn, getObservationNr(getName())); - - // Loop over the parameterset and send the information to the KVTlogger. - // During the transition phase from parameter-based to record-based storage in OTDB the - // nodenames ending in '_' are implemented both as parameter and as record. - ParameterSet::iterator iter = metadata.begin(); - ParameterSet::iterator end = metadata.end(); - while (iter != end) { - string key(iter->first); // make destoyable copy - rtrim(key, "[]0123456789"); -// bool doubleStorage(key[key.size()-1] == '_'); - bool isRecord(iter->second.isRecord()); - // isRecord doubleStorage - // -------------------------------------------------------------- - // Y Y store as record and as parameters - // Y N store as parameters - // N * store parameter - if (!isRecord) { - LOG_DEBUG_STR("BASIC: " << iter->first << " = " << iter->second); - tv.addKVT(iter->first, iter->second, ptime(microsec_clock::local_time())); - } - else { -// if (doubleStorage) { -// LOG_DEBUG_STR("RECORD: " << iter->first << " = " << iter->second); -// tv.addKVT(iter->first, iter->second, ptime(microsec_clock::local_time())); -// } - // to store is a node/param values the last _ should be stipped of - key = iter->first; // destroyable copy -// string::size_type pos = key.find_last_of('_'); -// key.erase(pos,1); - ParameterRecord pr(iter->second.getRecord()); - ParameterRecord::const_iterator prIter = pr.begin(); - ParameterRecord::const_iterator prEnd = pr.end(); - while (prIter != prEnd) { - LOG_DEBUG_STR("ELEMENT: " << key+"."+prIter->first << " = " << prIter->second); - tv.addKVT(key+"."+prIter->first, prIter->second, ptime(microsec_clock::local_time())); - prIter++; + int obsID(getObservationNr(getName())); + TreeValue tv(&conn, obsID); + + if (metadataFileAvailable) { + // read parameterset + ParameterSet metadata; + metadata.adoptFile(itsFeedbackFile); + + // Loop over the parameterset and send the information to the KVTlogger. + // During the transition phase from parameter-based to record-based storage in OTDB the + // nodenames ending in '_' are implemented both as parameter and as record. + ParameterSet::iterator iter = metadata.begin(); + ParameterSet::iterator end = metadata.end(); + while (iter != end) { + string key(iter->first); // make destoyable copy + rtrim(key, "[]0123456789"); + // bool doubleStorage(key[key.size()-1] == '_'); + bool isRecord(iter->second.isRecord()); + // isRecord doubleStorage + // -------------------------------------------------------------- + // Y Y store as record and as parameters + // Y N store as parameters + // N * store parameter + if (!isRecord) { + LOG_DEBUG_STR("BASIC: " << iter->first << " = " << iter->second); + tv.addKVT(iter->first, iter->second, ptime(microsec_clock::local_time())); } + else { + // if (doubleStorage) { + // LOG_DEBUG_STR("RECORD: " << iter->first << " = " << iter->second); + // tv.addKVT(iter->first, iter->second, ptime(microsec_clock::local_time())); + // } + // to store is a node/param values the last _ should be stipped of + key = iter->first; // destroyable copy + // string::size_type pos = key.find_last_of('_'); + // key.erase(pos,1); + ParameterRecord pr(iter->second.getRecord()); + ParameterRecord::const_iterator prIter = pr.begin(); + ParameterRecord::const_iterator prEnd = pr.end(); + while (prIter != prEnd) { + LOG_DEBUG_STR("ELEMENT: " << key+"."+prIter->first << " = " << prIter->second); + tv.addKVT(key+"."+prIter->first, prIter->second, ptime(microsec_clock::local_time())); + prIter++; + } + } + iter++; } - iter++; + LOG_INFO_STR(metadata.size() << " metadata values send to SAS"); } - LOG_INFO_STR(metadata.size() << " metadata values send to SAS"); + + // finally report state to SAS + TreeMaintenance tm(&conn); + TreeStateConv tsc(&conn); + tm.setTreeState(obsID, tsc.get("finished")); } }; // CEPCU diff --git a/MAC/APL/MainCU/src/MACScheduler/MACScheduler.cc b/MAC/APL/MainCU/src/MACScheduler/MACScheduler.cc index a0420c85ebc0500f17e5a287cd5d1ca8e5420f09..36f93d4dfd75f335140cb1182987d77bfa162626 100644 --- a/MAC/APL/MainCU/src/MACScheduler/MACScheduler.cc +++ b/MAC/APL/MainCU/src/MACScheduler/MACScheduler.cc @@ -465,7 +465,7 @@ GCFEvent::TResult MACScheduler::active_state(GCFEvent& event, GCFPortInterface& TreeStateConv tsc(itsOTDBconnection); // CT_RESULT_: MANUAL_REMOVED, MANUAL_ABORT, LOST_CONNECTION, NO_ERROR if (quitedEvent.result == CT_RESULT_NO_ERROR) { - tm.setTreeState(theObs->second, tsc.get("finished")); + tm.setTreeState(theObs->second, tsc.get("completing")); } else { tm.setTreeState(theObs->second, tsc.get("aborted")); diff --git a/MAC/Deployment/data/OTDB/DPPP.comp b/MAC/Deployment/data/OTDB/DPPP.comp index 97ad809f0e35a991a4e11f4914e7baeef70ce83b..5076a0ea326de806bad07a2777d4a20efd4af28f 100644 --- a/MAC/Deployment/data/OTDB/DPPP.comp +++ b/MAC/Deployment/data/OTDB/DPPP.comp @@ -180,6 +180,7 @@ par type I text - 10 0 "stationadder" - "Type of the step, do not change" par stations I text - 10 0 "" - "Stations to be added (in record-format)" par minpoints I int - 10 0 1 - "If fewer unflagged input points are available, the averaged point is flagged." par autocorr I bool - 10 0 F - "Add autocorrelations?" +par sumauto I bool - 10 0 T - "Sum auto or cross for new autocorrelations?" par useweights I bool - 10 0 T - "F = use weight 1" node filter 4.0.0 development 'node constraint' "Selection" diff --git a/MAC/Deployment/data/PVSS/data/PVSSbase.dpdef b/MAC/Deployment/data/PVSS/data/PVSSbase.dpdef index 0d0b747aa585607b90a1fbacf29f85f867222d7c..8d70a53301d3ae34f9ab83cf158ae1ce15712364 100644 --- a/MAC/Deployment/data/PVSS/data/PVSSbase.dpdef +++ b/MAC/Deployment/data/PVSS/data/PVSSbase.dpdef @@ -64,6 +64,13 @@ NCFObjectState.NCFObjectState 1# message 25# force 23# +TypeName +NCFObjectStates.NCFObjectStates 1# + DPNames 9# + stateNrs 5# + messages 9# + force 7# + TypeName ObjectStatus.ObjectStatus 1# state 21# @@ -123,6 +130,8 @@ DpName TypeName ClaimManager ClaimManager __navObjectState NCFObjectState __resetObjectState NCFObjectState +__navObjectStates NCFObjectStates +__resetObjectStates NCFObjectStates lofarSpeedTest LofarSpeedTest scriptInfo ScriptInfo diff --git a/MAC/Deployment/data/PVSS/data/StationInfo.dpdef b/MAC/Deployment/data/PVSS/data/StationInfo.dpdef index e924083b75ad439d6bb5421cfea6d3d2d3c2da23..20157640529e7c98c2f8501af1151ee3b538c2ea 100644 --- a/MAC/Deployment/data/PVSS/data/StationInfo.dpdef +++ b/MAC/Deployment/data/PVSS/data/StationInfo.dpdef @@ -13,7 +13,7 @@ Cabinet.X float Cabinet.Y float Cabinet.Z float HBA.nrBroken int -HBA.pecent float +HBA.nrBrokenArchive int HBA.centerX float HBA.centerY float HBA.centerZ float @@ -38,7 +38,7 @@ HBA.HBA1.RotationMatrix.Y floatArr HBA.HBA1.RotationMatrix.Z floatArr HBA.HBA1.rotation float LBA.nrBroken int -LBA.pecentBroken float +LBA.nrBrokenArchive int LBA.centerX float LBA.centerY float LBA.centerZ float @@ -60,16 +60,17 @@ LBA.RotationMatrix.Z floatArr !_mp_StationInfo.datastream0 StationInfo 1 15 _ValueArchive_2 !_mp_StationInfo.datastream1 StationInfo 45 1 !_mp_StationInfo.datastream1 StationInfo 1 15 _ValueArchive_2 -!_mp_StationInfo.LBA.nrBroken StationInfo 45 1 -!_mp_StationInfo.LBA.nrBroken StationInfo 1 15 _ValueArchive_2 -!_mp_StationInfo.LBA.percentBroken StationInfo 45 1 -!_mp_StationInfo.LBA.percentBroken StationInfo 1 15 _ValueArchive_2 -!_mp_StationInfo.HBA.nrBroken StationInfo 45 1 -!_mp_StationInfo.HBA.nrBroken StationInfo 1 15 _ValueArchive_2 -!_mp_StationInfo.HBA.percentBroken StationInfo 45 1 -!_mp_StationInfo.HBA.percentBroken StationInfo 1 15 _ValueArchive_2 +!_mp_StationInfo.LBA.nrBrokenArchive StationInfo 45 1 +!_mp_StationInfo.LBA.nrBrokenArchive StationInfo 1 15 _ValueArchive_3 +!_mp_StationInfo.HBA.nrBrokenArchive StationInfo 45 1 +!_mp_StationInfo.HBA.nrBrokenArchive StationInfo 1 15 _ValueArchive_3 ! !# DpValue !ElementName TypeName _original.._value -!_dt_StationInfo.Leaf _DynamicDatapoints "_mp_StationInfo.datastream0:_archive", "_mp_StationInfo.datastream1:_archive", "_mp_StationInfo.LBA.nrBroken:_archive", "_mp_StationInfo.LBA.percentBroken:_archive","_mp_StationInfo.HBA.nrBroken:_archive", "_mp_StationInfo.HBA.percentBroken:_archive" -!_dt_StationInfo.DynamicAttribute _DynamicDatapoints "_da_none", "_da_none", "_da_none", "_da_none", "_da_none", "_da_none" +!_dt_StationInfo.Leaf _DynamicDatapoints "_mp_StationInfo.datastream0:_archive", "_mp_StationInfo.datastream1:_archive", "_mp_StationInfo.LBA.nrBrokenArchive:_archive", "_mp_StationInfo.HBA.nrBrokenArchive:_archive", "_mp_StationInfo.HBA.nrBrokenArchive:_dp_fct", "_mp_StationInfo.LBA.nrBrokenArchive:_dp_fct" +!_dt_StationInfo.DynamicAttribute _DynamicDatapoints "_da_none", "_da_none", "_da_none", "_da_none", "_da_dp_fct_variabel", "_da_dp_fct_variabel" + +!# DpFunction +!ElementName TypeName _dp_fct.._type _dp_fct.._param _dp_fct.._fct _dp_fct.._global _dp_fct.._stat_type _dp_fct.._interval _dp_fct.._time +!_mp_StationInfo.HBA.nrBrokenArchive StationInfo 63 "g" _mp_StationInfo.HBA.nrBroken:_original.._value 86400 82800 +!_mp_StationInfo.LBA.nrBrokenArchive StationInfo 63 "g" _mp_StationInfo.LBA.nrBroken:_original.._value 86400 82800 diff --git a/MAC/Deployment/data/PVSS/data/Stationbase.dpdef b/MAC/Deployment/data/PVSS/data/Stationbase.dpdef index eead1cbd32c45adf169f50ca2076ae48bc67ad09..8d147968a90368b814bda42d7488e6c023807c8a 100644 --- a/MAC/Deployment/data/PVSS/data/Stationbase.dpdef +++ b/MAC/Deployment/data/PVSS/data/Stationbase.dpdef @@ -163,6 +163,7 @@ _CtrlDebug_CTRL_7 _CtrlDebug _CtrlDebug_CTRL_8 _CtrlDebug _CtrlDebug_CTRL_9 _CtrlDebug _CtrlDebug_CTRL_10 _CtrlDebug +_CtrlDebug_CTRL_11 _CtrlDebug #Fill some defaults # DpValue diff --git a/MAC/MACIO/include/MACIO/MACServiceInfo.h b/MAC/MACIO/include/MACIO/MACServiceInfo.h index 1409e74783ea8e901de4be15957f7326eee9093e..14c25c57e7a832d0fb38830bfc79d526f211c6c1 100644 --- a/MAC/MACIO/include/MACIO/MACServiceInfo.h +++ b/MAC/MACIO/include/MACIO/MACServiceInfo.h @@ -44,6 +44,8 @@ namespace LOFAR { // QUICK FIX #3633 #define MAC_PYTHON_FEEDBACK_QF 22000 +// Quick Fix #4022 +#define MAC_ONLINE_FEEDBACK_QF 21000 // CEPlogprocessor needs fixed ports #define CEP_LOGPROC_LOGGING 23900 diff --git a/MAC/Navigator2/config/progs.dist.station b/MAC/Navigator2/config/progs.dist.station index 756fc6af55d2f50cb4f397c3f681841f70d9deaf..34192b8fc51b393862a635e1d60c7de9e6815873 100644 --- a/MAC/Navigator2/config/progs.dist.station +++ b/MAC/Navigator2/config/progs.dist.station @@ -6,6 +6,7 @@ PVSS00pmon | manual | 30 | 3 | 1 | PVSS00data | always | 30 | 3 | 1 | PVSS00valarch | always | 30 | 3 | 1 |-num 0 PVSS00valarch | always | 30 | 3 | 1 |-num 2 +PVSS00valarch | always | 30 | 3 | 1 |-num 3 PVSS00event | always | 30 | 3 | 1 | PVSS00ctrl | always | 30 | 3 | 1 |-f pvss_scripts.lst PVSS00sim | always | 30 | 3 | 1 | diff --git a/MAC/Navigator2/config/progs.standalone.station b/MAC/Navigator2/config/progs.standalone.station index 8b56867e40eb12fa9cb71fc890594fa11d9e0d3f..c279f585a9898b7acb99a0a936cf4c1314228d2e 100644 --- a/MAC/Navigator2/config/progs.standalone.station +++ b/MAC/Navigator2/config/progs.standalone.station @@ -6,6 +6,7 @@ PVSS00pmon | manual | 30 | 3 | 1 | PVSS00data | always | 30 | 3 | 1 | PVSS00valarch | always | 30 | 3 | 1 |-num 0 PVSS00valarch | always | 30 | 3 | 1 |-num 2 +PVSS00valarch | always | 30 | 3 | 1 |-num 3 PVSS00event | always | 30 | 3 | 1 | PVSS00ctrl | always | 30 | 3 | 1 |-f pvss_scripts.lst PVSS00sim | always | 30 | 3 | 1 | diff --git a/MAC/Navigator2/panels/objects/Hardware/Station_mainView.pnl b/MAC/Navigator2/panels/objects/Hardware/Station_mainView.pnl index a99475bec81e15e3355a36b5b6a90ca5f508a399..666becfd57485167b539b67c0a422960cf774134 100644 --- a/MAC/Navigator2/panels/objects/Hardware/Station_mainView.pnl +++ b/MAC/Navigator2/panels/objects/Hardware/Station_mainView.pnl @@ -45,11 +45,11 @@ LANG:1 0 0 1 19 2 "" 0 0 -1 20 3 "" 0 +1 20 4 "" 0 0 -1 21 4 "" 0 +1 21 5 "" 0 0 -1 22 5 "" 0 +1 22 6 "" 0 0 0 LAYER, 1 @@ -89,11 +89,6 @@ LANG:1 0 2 "$fullDP""LOFAR_PIC_StationInfo.power48On" "$station""$station" -3 3 "PANEL_REF3" -1 -"objects\\genericOnOffView.pnl" 60 0 T 36 U -2 -"$fullDP""LOFAR_PIC_Cabinet0_Subrack0_RSPBoard0_RCU0.TBB.mode" -"$station""$station" 3 4 "PANEL_REF4" -1 "objects\\Hardware\\antennaBroken_Small.pnl" 80 0 T 37 1 0 1 2 0 2 @@ -104,4 +99,8 @@ LANG:1 0 2 "$antennaType""LBA" "$station""$station" +3 6 "PANEL_REF6" -1 +"objects\\Hardware\\TBBmode_small.pnl" 60 0 T 39 U +1 +"$station""$station" 0 diff --git a/MAC/Navigator2/panels/objects/Hardware/TBBmode_small.pnl b/MAC/Navigator2/panels/objects/Hardware/TBBmode_small.pnl new file mode 100644 index 0000000000000000000000000000000000000000..6020c5a680ea1ddc1729f093e714a295bf7d42da --- /dev/null +++ b/MAC/Navigator2/panels/objects/Hardware/TBBmode_small.pnl @@ -0,0 +1,100 @@ +V 11 +1 +LANG:1 0 +PANEL,-1 -1 388 166 N "_3DFace" 1 +"$station" +"main() +{ + station = $station+\":\"; + baseDP = station+\"LOFAR_PIC_Cabinet0_Subrack0_RSPBoard0_RCU0\"; + reload(); +} + +private void reload() { + + // since + // check if the required datapoint for this view are enabled and accessible + if (navFunct_dpReachable(baseDP+\".TBB.mode\")) { + if (dpConnect(\"updateTBBrecording\", baseDP +\".TBB.mode:_online.._value\", + baseDP +\".TBB.mode:_online.._invalid\") == -1) { + setValue(\"TBBrecording\", \"backCol\", \"Lofar_dpdoesnotexist\"); + } + } else { + setValue(\"TBBrecording\", \"backCol\", \"Lofar_dpOffline\"); + + } +} + +updateTBBrecording(string dp1, string mode, + string dp2, bool invalid) +{ + + if (invalid) { + setValue(\"TBBrecording\", \"backCol\", \"Lofar_invalid\"); + return; + } + + string color = \"Lofar_broken\"; + if (strtolower(mode) == \"recording\" ) color = \"Lofar_operational\"; + + setValue(\"TBBrecording\",\"toolTipText\",station+\" TBBmode: \"+mode ); + setValue(\"TBBrecording\", \"backCol\", color); + }" 0 + E E E E 1 -1 -1 0 0 0 +""0 1 +E "#uses \"navPanel.ctl\" +string station = \"\"; +string baseDP=\"\"; +" 0 + 2 +"CBRef" "1" +"EClose" E +"" +DISPLAY_LAYER, 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 +LAYER, 0 +1 +LANG:1 0 +6 0 +"TBBrecording" +"" +1 230 150 E E E 1 E 1 E N {0,0,0} E N {255,255,255} E E + E E +0 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 2 1 E 1 0 1 0 -10 1 E 0 10 14 19 +0 +LAYER, 1 +1 +LANG:1 0 +0 +LAYER, 2 +1 +LANG:1 0 +0 +LAYER, 3 +1 +LANG:1 0 +0 +LAYER, 4 +1 +LANG:1 0 +0 +LAYER, 5 +1 +LANG:1 0 +0 +LAYER, 6 +1 +LANG:1 0 +0 +LAYER, 7 +1 +LANG:1 0 +0 +0 diff --git a/RTCP/RTCPTools/src/tbb-crc-test.cc b/RTCP/RTCPTools/src/tbb-crc-test.cc index 975cb5b54725a390b808dd97fa60f9af76c5f1ae..958846d046129d70786c775462cb673bb34aef44 100644 --- a/RTCP/RTCPTools/src/tbb-crc-test.cc +++ b/RTCP/RTCPTools/src/tbb-crc-test.cc @@ -6,6 +6,7 @@ */ #include <stdint.h> +#include <string.h> #include <endian.h> #if __BYTE_ORDER != __BIG_ENDIAN && __BYTE_ORDER != __LITTLE_ENDIAN #error Byte order is neither big endian nor little endian: not supported @@ -43,7 +44,8 @@ struct TBB_Header { }; #define MAX_TRANSIENT_NSAMPLES 1298 // based on frames stored by TBB and (un)packing -#define DEFAULT_TRANSIENT_NSAMPLES 1024 +#define DEFAULT_TRANSIENT_NSAMPLES 1024 // int16_t +#define DEFAULT_SPECTRAL_NSAMPLES 487 // complex int16_t struct TBB_Payload { // For transient data, we typically receive 1024 samples per frame. // uint32_t crc comes right after, so cannot easily declare it after data[], hence + 2. @@ -225,6 +227,22 @@ static int verify_crc(TBB_Frame& frame, size_t frameSize) { if (!crc32tbb_boost( &frame.payload, ( frameSize - sizeof(TBB_Header) - sizeof(uint32_t) ) / sizeof(int16_t) )) { cerr << "crc32tbb_boost(): Incorrect payload crc" << endl; err = 1; + +#if 0 // this guessing doesn't work: the wrong crc32 is different every time, even on the same data + TBB_Payload p; + unsigned i; + for (i = 0; i < 487; i++) { + memcpy(&p, &frame.payload, i * 2 * sizeof(int16_t)); // data + memcpy((char*)&p + i * 2 * sizeof(int16_t), (char*)(&frame.payload.data[2*487 + 2]) - sizeof(uint32_t), sizeof(uint32_t)); // crc32 + if (crc32tbb_boost(&p, 2 * i)) { + cerr << "found it: i=" << i << endl; + break; + } else { + cerr << "doesn't work either: " << i << endl; + } + } +#endif + } return err; @@ -236,6 +254,22 @@ int main(int argc, char* argv[]) { return 1; } + bool transient; + ifstream iftype(argv[1], ios_base::binary); + if (!iftype) { + cerr << "Failed to open file " << argv[1] << endl; + return 1; + } + TBB_Header header; + iftype.read(reinterpret_cast<char*>(&header), sizeof header); + if (!iftype) { + cerr << "Failed to read first frame to determine transient or spectral mode" << endl; + return 1; + } + iftype.close(); + transient = header.nOfFreqBands == 0; + + ifstream ifs(argv[1], ios_base::binary); if (!ifs) { cerr << "Failed to open file " << argv[1] << endl; @@ -245,7 +279,12 @@ int main(int argc, char* argv[]) { int err = 0; TBB_Frame frame; - size_t frameSize = sizeof(TBB_Header) + DEFAULT_TRANSIENT_NSAMPLES * sizeof(int16_t) + sizeof(uint32_t); + size_t frameSize; + if (transient) { + frameSize = sizeof(TBB_Header) + DEFAULT_TRANSIENT_NSAMPLES * sizeof(int16_t) + sizeof(uint32_t); + } else { // spectral + frameSize = sizeof(TBB_Header) + DEFAULT_SPECTRAL_NSAMPLES * 2 * sizeof(int16_t) + sizeof(uint32_t); + } while (ifs.read(reinterpret_cast<char*>(&frame), frameSize)) { err |= verify_crc(frame, frameSize); diff --git a/RTCP/Run/src/LOFAR/Parset.py b/RTCP/Run/src/LOFAR/Parset.py index 8a3ac9e36ff3d5bbc639d4069cc3033639f6f527..de3445852ba486a9ccbd94b957b2c15a79ceaa42 100644 --- a/RTCP/Run/src/LOFAR/Parset.py +++ b/RTCP/Run/src/LOFAR/Parset.py @@ -202,11 +202,13 @@ class Parset(util.Parset.Parset): self.setdefault("Observation.Beam[%s].tabRingSize" % (b,),0.0) dirtype = self["Observation.Beam[%s].directionType" % (b,)] + center_angle1 = float(self["Observation.Beam[%s].angle1" % (b,)]) + center_angle2 = float(self["Observation.Beam[%s].angle2" % (b,)]) dm = int(self.get("OLAP.dispersionMeasure",0)) nrrings = int(self["Observation.Beam[%s].nrTabRings" % (b,)]) width = float(self["Observation.Beam[%s].tabRingSize" % (b,)]) - ringcoordinates = RingCoordinates( nrrings, width ) + ringcoordinates = RingCoordinates( nrrings, width, (center_angle1, center_angle2), dirtype ) ringset = [ { "angle1": angle1, "angle2": angle2, diff --git a/RTCP/Run/src/LOFAR/RingCoordinates.py b/RTCP/Run/src/LOFAR/RingCoordinates.py index fc82d772aeae918391742c7ecfa355b0b4ec8d6a..02e52a3c2d1ffa9898b6f72f826d47ff1cac33dc 100644 --- a/RTCP/Run/src/LOFAR/RingCoordinates.py +++ b/RTCP/Run/src/LOFAR/RingCoordinates.py @@ -1,11 +1,28 @@ #!/usr/bin/python -from math import sqrt +from math import sqrt, cos, pi class RingCoordinates: - def __init__(self, numrings, width): + def __init__(self, numrings, width, center, dirtype): self.numrings = numrings self.width = width + self.center = center + self.dirtype = dirtype + + def cos_adjust(self, offset): + if dirtype != "J2000" and dirtype != "B1950": + return offset + + # warp coordinates closer to the NCP + + cos_dec = cos(self.center[1] + offset[1]) + epsilon = 0.0001 + + if cos_dec > epsilon: + return (offset[0]/cos_dec, offset[1]) + else: + return offset + def len_edge(self): """ @@ -120,5 +137,5 @@ class RingCoordinates: l += dl[side] m += dm[side] - return coordinates + return map(self.cos_adjust, coordinates) diff --git a/RTCP/Storage/include/Storage/TBB_Writer.h b/RTCP/Storage/include/Storage/TBB_Writer.h index 0b29e0854530d16d3ba02bea1128773f9c211f7e..6a1ed8d160b5682d0c5c009884069effcfe8d5d9 100644 --- a/RTCP/Storage/include/Storage/TBB_Writer.h +++ b/RTCP/Storage/include/Storage/TBB_Writer.h @@ -63,10 +63,7 @@ namespace RTCP { * available at: http://www.lofar.org/project/lofardoc/document.php * Old rev. 2.0 (2006-10-3): http://lus.lofar.org/wiki/lib/exe/fetch.php?media=documents:sdd:lofar-astron-sdd-047_tbb_design_description.pdf * - * In TBB, each frame is 2040 bytes long, consisting of an 88 bytes header, and a 1952 bytes payload (both with their own checksum). - * There are two types of data that can be transferred: transient data or spectral data. - * However, incoming frame payloads are somewhat different from frame payloads stored at the TBB: data is transferred unpacked, directly followed by a crc32. - * Everything is in little-endian byte order. + * There are two types of data that can be transferred: transient data and spectral (subband) data. Everything is in little-endian byte order. */ struct TBB_Header { uint8_t stationID; // Data source station identifier @@ -84,7 +81,6 @@ struct TBB_Header { // In spectral mode indicates frequency band and slice (transform block of 1024 samples) of first payload sample. uint32_t bandSliceNr; // bandNr[9:0] and sliceNr[31:10]. - // Avoid bit fields, (portable) compilation support is messy. Instead use mask and shift to extract. #define TBB_BAND_NR_MASK ((1 << 10) - 1) #define TBB_SLICE_NR_SHIFT 10 }; @@ -96,9 +92,6 @@ struct TBB_Header { uint16_t spare; // For future use. Set to 0. uint16_t crc16; // CRC16 over frame header, with seqNr set to 0. - - - std::string toString() const; }; struct TBB_Payload { @@ -114,21 +107,20 @@ struct TBB_Payload { #define MAX_TBB_SPECTRAL_NSAMPLES (MAX_TBB_DATA_SIZE / (2 * sizeof(int16_t))) // 487 // Unpacked, sign-extended (for transient) samples without padding, i.e. as received. - // Frames might not be full; the crc32 is always sent right after (no padding unlike as stored by TBB), - // so we include it in 'data', but note that the crc32 is a little endian uint32_t, hence ' + 2'. + // Frames might not be full; the doc says the crc32 is always sent right after (no padding), (but this is false for spectral), + // so we include the crc32 in 'data', but note that the crc32 is a little endian uint32_t, hence ' + 2'. #ifndef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif - int16_t data[MAX(MAX_TBB_TRANSIENT_NSAMPLES, 2 * MAX_TBB_SPECTRAL_NSAMPLES) + 2]; + int16_t data[MAX(MAX_TBB_TRANSIENT_NSAMPLES, 2 * MAX_TBB_SPECTRAL_NSAMPLES) + 2]; // For transient, TBB always sends sends 1024 samples per frame (from the spec and from the data). // For spectral, it depends on the nr of subbands (max is equal to MAX_TBB_SPECTRAL_NSAMPLES). - // TBB sends as many samples for all subbands as it can fit; e.g. with 5 subbands, each frame has 485 samples. TODO: correct? + // TBB sends as many samples for all subbands as it can fit; e.g. with 5 subbands, each frame has 485 samples. -#define SPECTRAL_TRANSFORM_SIZE 1024 // RSP FFT block size +#define SPECTRAL_TRANSFORM_SIZE 1024 // RSP FFT block size -#define DEFAULT_TBB_TRANSIENT_NSAMPLES 1024 -#define DEFAULT_TBB_SPECTRAL_NSAMPLES 487 +#define DEFAULT_TBB_TRANSIENT_NSAMPLES 1024 // for spectral it depends on #subbands }; struct TBB_Frame { @@ -160,9 +152,9 @@ struct SubbandInfo { class TBB_Dipole { - dal::TBB_DipoleDataset* itsDataset; - LOFAR::FileStream* itsRawOut; - std::vector<dal::Range> itsFlagOffsets; + LOFAR::FileStream* itsRawOut; + dal::TBB_Dataset<short>* itsDataset; + std::vector<dal::Range> itsFlagOffsets; uint32_t itsSampleFreq; // Hz unsigned itsNrSubbands; // spectral mode only, 0 in transient mode @@ -176,7 +168,7 @@ class TBB_Dipole { // Same truncated polynomial as standard crc32, but with initial_remainder=0, final_xor_value=0, reflected_input=false, reflected_remainder_output=false. // The boost::crc_optimal<> declarations precompute lookup tables, so do not declare inside the checking routine. (Still, for every TBB_Dipole...) - boost::crc_optimal<32, 0x04C11DB7/*, 0, 0, false, false*/> itsCrc32gen; // instead of boost::crc_32_type + boost::crc_optimal<32, 0x04C11DB7/*, 0, 0, false, false*/> itsCrc32gen; // do not use TBB_Dipole& operator=(const TBB_Dipole& rhs); @@ -189,7 +181,7 @@ public: // Output threads bool isInitialized() const; - // All TBB_Dipole objects are default constructed in a vector, so provide an init procedure. + // All TBB_Dipole objects are default constructed in a vector, so have init(). void init(const TBB_Header& header, const Parset& parset, const StationMetaData& stationMetaData, const SubbandInfo& subbandInfo, const std::string& rawFilename, dal::TBB_Station& station, Mutex& h5Mutex); @@ -198,7 +190,7 @@ public: void processSpectralFrameData(const TBB_Frame& frame, const SubbandInfo& subbandInfo); private: - void addFlags(size_t offset, size_t len); + void appendFlags(size_t offset, size_t len); // initTBB_DipoleDataset() must be called with the global h5Mutex held. void initTBB_DipoleDataset(const TBB_Header& header, const Parset& parset, const StationMetaData& stationMetaData, const SubbandInfo& subbandInfo, @@ -214,7 +206,7 @@ class TBB_Station { std::vector<TBB_Dipole> itsDipoles; const Parset& itsParset; const StationMetaData& itsStationMetaData; - const SubbandInfo itsSubbandInfo; // relevant for spectral mode + const SubbandInfo itsSubbandInfo; // for spectral mode const std::string itsH5Filename; double getSubbandCentralFreq(unsigned subbandNr, unsigned nyquistZone, double sampleFreq) const; @@ -268,23 +260,23 @@ class TBB_StreamWriter { TBB_Frame* itsFrameBuffers; // Queue pointers point into itsFrameBuffers. - Queue<TBB_Frame*> itsReceiveQueue; // input thread -> output thread - Queue<TBB_Frame*> itsFreeQueue; // output thread -> input thread + Queue<TBB_Frame*> itsReceiveQueue; // input -> output thread + Queue<TBB_Frame*> itsFreeQueue; // output -> input thread TBB_Writer& itsWriter; const std::string& itsInputStreamName; - const size_t itsExpFrameSize; + const unsigned itsExpFrameSize; const std::string& itsLogPrefix; int& itsInExitStatus; int& itsOutExitStatus; // See TBB_Writer_main.cc::doTBB_Run() why this is used racily for now. - // Inflate struct timeval to 64 bytes (typical LEVEL1_DCACHE_LINESIZE). + // Inflate struct timeval to 64 bytes (typical LEVEL1_DCACHE_LINESIZE). Unnecessary... struct timeval itsTimeoutStamp __attribute__((aligned(64))); - boost::crc_optimal<16, 0x8005/*, 0, 0, false, false*/> itsCrc16gen; // instead of boost::crc_16_type + boost::crc_optimal<16, 0x8005/*, 0, 0, false, false*/> itsCrc16gen; -#ifdef DUMP_RAW_STATION_DATA +#ifdef DUMP_RAW_STATION_FRAMES LOFAR::FileStream* itsRawStationData; #endif @@ -319,7 +311,8 @@ private: }; class TBB_Writer { - // Usually, we handle only 1 station, but we can handle multiple at a time. + // Usually, we handle only 1 station, but users have request to support multiple concurrently. + // The LOFAR system could better use different input streams (udp ports), but we/they are busy. // map from stationID to a TBB_Station* std::map<unsigned, TBB_Station*> itsStations; Mutex itsStationsMutex; @@ -330,7 +323,7 @@ class TBB_Writer { const Parset& itsParset; const StationMetaDataMap& itsStationMetaDataMap; - StationMetaData itsUnknownStationMetaData; // referred to for data from unknown stations + StationMetaData itsUnknownStationMetaData; // referred to for data from unknown stations (fallback) const std::string& itsOutDir; unsigned itsRunNr; diff --git a/RTCP/Storage/src/CMakeLists.txt b/RTCP/Storage/src/CMakeLists.txt index 3d4712b84de51c2da4db3359a6237159169d1c69..b8ba519997c986e61b83b0fd40d46aca83652465 100644 --- a/RTCP/Storage/src/CMakeLists.txt +++ b/RTCP/Storage/src/CMakeLists.txt @@ -17,7 +17,8 @@ lofar_add_library(storage Format.cc MeasurementSetFormat.cc TBB_StaticMapping.cc - TBB_Writer.cc) +# TBB_Writer.cc +) install(PROGRAMS gnuplotMS.sh @@ -31,5 +32,5 @@ lofar_add_bin_program(Storage_main Storage_main.cc) lofar_add_bin_program(createHeaders createHeaders.cc) lofar_add_bin_program(plotMS plotMS.cc) lofar_add_bin_program(versionstorage versionstorage.cc) -lofar_add_bin_program(TBB_Writer_main TBB_Writer_main.cc) +#lofar_add_bin_program(TBB_Writer_main TBB_Writer_main.cc) diff --git a/RTCP/Storage/src/TBB_Writer.cc b/RTCP/Storage/src/TBB_Writer.cc index 1c3af79eb332dd61b460c78a8bc262748e4cb8be..82d8be38ec88d302670fdf2b7653cda4a78f187c 100644 --- a/RTCP/Storage/src/TBB_Writer.cc +++ b/RTCP/Storage/src/TBB_Writer.cc @@ -25,6 +25,7 @@ #define _FILE_OFFSET_BITS 64 #include <cstddef> +#include <cstring> #include <csignal> #include <ctime> #include <cerrno> @@ -61,7 +62,7 @@ #define TBB_TRANSIENT_MODE 1 #define TBB_SPECTRAL_MODE 2 -#define RSP_NR_SUBBANDS 512 // nr of subbands produced by the RSP polyphase filter +#define RSP_NR_SUBBANDS 512 namespace LOFAR { namespace RTCP { @@ -70,10 +71,7 @@ using namespace std; EXCEPTION_CLASS(TBB_MalformedFrameException, StorageException); -/* - * The output_format is without seconds. The output_size is including the terminating NUL char. - * Helper for in filenames and for the FILEDATE attribute. - */ +// The output_format is without seconds. The output_size is including the terminating NUL char. static string formatFilenameTimestamp(const struct timeval& tv, const char* output_format, const char* output_format_secs, size_t output_size) { struct tm tm; @@ -91,7 +89,7 @@ static string formatFilenameTimestamp(const struct timeval& tv, const char* outp return string(&date[0]); } -// FileStream doesn't do pwrite(2). Roll our own as nobody else needs it, but in the FileStream way, just in case. +// FileStream doesn't do pwrite(2). static size_t tryPWrite(int fd, const void *ptr, size_t size, off_t offset) { ssize_t bytes = ::pwrite(fd, ptr, size, offset); if (bytes < 0) @@ -108,48 +106,43 @@ static void pwrite(int fd, const void *ptr, size_t size, off_t offset) { } } -string TBB_Header::toString() const { - ostringstream oss; - oss << (unsigned)stationID << " " << (unsigned)rspID << " " << (unsigned)rcuID << " " << (unsigned)sampleFreq << - " " << seqNr << " " << time << " " << (nOfFreqBands == 0 ? sampleNr : bandSliceNr) << " " << nOfSamplesPerFrame << - " " << nOfFreqBands << " " << spare << " " << crc16; // casts uin8_t to unsigned to avoid printing as char - return oss.str(); +static ostream& operator<<(ostream& out, const TBB_Header& h) { + out << (unsigned)h.stationID << " " << (unsigned)h.rspID << " " << (unsigned)h.rcuID << " " << (unsigned)h.sampleFreq << + " " << h.seqNr << " " << h.time << " " << (h.nOfFreqBands == 0 ? h.sampleNr : h.bandSliceNr) << " " << h.nOfSamplesPerFrame << + " " << h.nOfFreqBands << " " << h.spare << " " << h.crc16; // casts uin8_t to unsigned to avoid printing as char + return out; } ////////////////////////////////////////////////////////////////////////////// TBB_Dipole::TBB_Dipole() -: itsDataset(NULL) // needed, setting the others is superfluous -, itsRawOut(NULL) +: itsRawOut(NULL) // needed, setting the others is superfluous +, itsDataset(NULL) , itsFlagOffsets() , itsSampleFreq(0) , itsNrSubbands(0) , itsTime(0) -, itsExpSampleNr(0) // also inits itsExpSliceNr +, itsExpSampleNr(0) , itsDatasetLen(0) { } // Do not use. Only needed for vector<TBB_Dipole>(N). TBB_Dipole::TBB_Dipole(const TBB_Dipole& rhs) -: itsDataset(rhs.itsDataset) // needed, setting the others is superfluous -, itsRawOut(NULL) // Safe destr. FileStream has no copy constr, so disabled; also see comments in init() +: itsRawOut(NULL) // idem. FileStream has no copy constr, but only copied before really set, so NULL is fine. +, itsDataset(rhs.itsDataset) , itsFlagOffsets(rhs.itsFlagOffsets) , itsSampleFreq(rhs.itsSampleFreq) , itsNrSubbands(rhs.itsNrSubbands) , itsTime(rhs.itsTime) -, itsExpSampleNr(rhs.itsExpSampleNr) // also inits itsExpSliceNr +, itsExpSampleNr(rhs.itsExpSampleNr) , itsDatasetLen(rhs.itsDatasetLen) { } TBB_Dipole::~TBB_Dipole() { - /* - * Set dataset len (if ext raw) and SPECTRAL_*, DATA_LENGTH and FLAG_OFFSETS attributes at the end. - * Executed by the main thread after joined with all workers, so no need to lock or delay cancellation. - * Skip on uninitialized (default constructed) objects. - */ - if (itsDataset != NULL) { + // Executed by the main thread after joined with all workers, so no need to lock or delay cancellation. + if (isInitialized()) { try { if (itsNrSubbands == 0) { // transient mode itsDataset->resize1D(itsDatasetLen); @@ -169,15 +162,14 @@ TBB_Dipole::~TBB_Dipole() { LOG_WARN_STR("TBB: failed to set dipole DATA_LENGTH attribute: " << exc.what()); } try { - itsDataset->flagOffsets().value = itsFlagOffsets; + itsDataset->flagOffsets().create(itsFlagOffsets.size()).set(itsFlagOffsets); } catch (dal::DALException& exc) { LOG_WARN_STR("TBB: failed to set dipole FLAG_OFFSETS attribute: " << exc.what()); } delete itsDataset; + delete itsRawOut; } - - delete itsRawOut; } void TBB_Dipole::init(const TBB_Header& header, const Parset& parset, @@ -185,7 +177,10 @@ void TBB_Dipole::init(const TBB_Header& header, const Parset& parset, const SubbandInfo& subbandInfo, const string& rawFilename, dal::TBB_Station& station, Mutex& h5Mutex) { itsSampleFreq = static_cast<uint32_t>(header.sampleFreq) * 1000000; - itsNrSubbands = subbandInfo.centralFreqs.size(); + itsNrSubbands = header.nOfFreqBands; + if (itsNrSubbands > subbandInfo.centralFreqs.size()) { + throw StorageException("TBB: dropping frame with invalid nOfFreqBands"); + } itsRawOut = new FileStream(rawFilename, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); @@ -197,7 +192,7 @@ void TBB_Dipole::init(const TBB_Header& header, const Parset& parset, /* * This nonsense is needed, because FileStream has no FileStream() and open() (and swap()), * and since we know the filename only at runtime (timestamp), we need itsRawOut to be a raw ptr. - * We already have a raw ptr for itsDataset and >1 raw ptr in 1 C++ class becomes buggy or messy. + * We already have a raw ptr for the dataset and >1 raw ptr in 1 C++ class becomes buggy or messy. */ delete itsRawOut; itsRawOut = NULL; @@ -211,18 +206,18 @@ void TBB_Dipole::init(const TBB_Header& header, const Parset& parset, } else { // spectral mode itsExpSliceNr = header.bandSliceNr >> TBB_SLICE_NR_SHIFT; } - itsDatasetLen = 0; // already 0, so just for completeness + itsDatasetLen = 0; // already 0, for completeness } bool TBB_Dipole::isInitialized() const { - return itsDataset != NULL; // constructed after itsRawOut so test itsDataset + return itsRawOut != NULL; } -// Add a new flag range or extend the last stored flag range. 'len' may not be 0. -void TBB_Dipole::addFlags(size_t offset, size_t len) { +// Add a new flag range at the end or extend the last stored flag range. 'len' may not be 0. +void TBB_Dipole::appendFlags(size_t offset, size_t len) { if (itsFlagOffsets.empty() || offset > itsFlagOffsets.back().end) { itsFlagOffsets.push_back(dal::Range(offset, offset + len)); - } else { // extend flag range + } else { // extend itsFlagOffsets.back().end += len; } } @@ -230,10 +225,10 @@ void TBB_Dipole::addFlags(size_t offset, size_t len) { void TBB_Dipole::processTransientFrameData(const TBB_Frame& frame) { /* * Out-of-order or duplicate frames are very unlikely in the LOFAR TBB setup, - * but let us know if it ever happens, then we will adapt this code and addFlags(). + * but let us know if it ever happens, then we will adapt this code and appendFlags(). */ if (frame.header.time < itsTime || (frame.header.time == itsTime && frame.header.sampleNr < itsExpSampleNr)) { - LOG_WARN_STR("TBB: Please notify developer: unhandled out-of-order or duplicate TBB frame: " << + LOG_WARN_STR("TBB: Unhandled out-of-order or duplicate frame: " << (unsigned)frame.header.stationID << " " << (unsigned)frame.header.rspID << " " << (unsigned)frame.header.rcuID << " " << frame.header.time << " " << itsTime << " " << frame.header.sampleNr << " " << itsExpSampleNr); return; @@ -256,11 +251,11 @@ void TBB_Dipole::processTransientFrameData(const TBB_Frame& frame) { /* * Flag lost frame(s) (assume no out-of-order, see below). Assumes all frames have the same nr of samples. - * Note: this cannot detect lost frames at the end of a dataset. + * This cannot detect lost frames at the end of a dataset. */ size_t nskipped = offset - itsDatasetLen; if (nskipped > 0) { - addFlags(itsDatasetLen, nskipped); + appendFlags(itsDatasetLen, nskipped); itsRawOut->skip(nskipped * sizeof(frame.payload.data[0])); // skip space of lost frame(s) } @@ -269,11 +264,12 @@ void TBB_Dipole::processTransientFrameData(const TBB_Frame& frame) { * Flag zeroed payloads too, as incredibly unlikely to be correct, but not rejected by crc32tbb. */ if (!crc32tbb(&frame.payload, frame.header.nOfSamplesPerFrame)) { - addFlags(offset, frame.header.nOfSamplesPerFrame); - const uint32_t* crc32 = reinterpret_cast<const uint32_t*>(&frame.payload.data[frame.header.nOfSamplesPerFrame]); - LOG_INFO_STR("TBB: crc32: " << frame.header.toString() << " " << *crc32); + appendFlags(offset, frame.header.nOfSamplesPerFrame); + uint32_t crc32; + memcpy(&crc32, &frame.payload.data[frame.header.nOfSamplesPerFrame], sizeof crc32); // strict-aliasing safe + LOG_WARN_STR("TBB: crc32: " << frame.header << " " << crc32); } else if (hasAllZeroDataSamples(frame.payload, frame.header.nOfSamplesPerFrame)) { - addFlags(offset, frame.header.nOfSamplesPerFrame); + appendFlags(offset, frame.header.nOfSamplesPerFrame); } // Since we are writing around HDF5, there is no need to lock. Resize the HDF5 dataset at the end (destr). @@ -287,11 +283,11 @@ void TBB_Dipole::processTransientFrameData(const TBB_Frame& frame) { void TBB_Dipole::processSpectralFrameData(const TBB_Frame& frame, const SubbandInfo& subbandInfo) { /* * Out-of-order or duplicate frames are very unlikely in the LOFAR TBB setup, - * but let us know if it ever happens, then we will adapt this code and addFlags(). + * but let us know if it ever happens, then we will adapt this code and appendFlags(). */ uint32_t sliceNr = frame.header.bandSliceNr >> TBB_SLICE_NR_SHIFT; // cannot sanitize fully: too large values indicate lost data: flag if (frame.header.time < itsTime || (frame.header.time == itsTime && sliceNr < itsExpSliceNr)) { - LOG_WARN_STR("TBB: Please notify developer: unhandled out-of-order or duplicate TBB frame: " << + LOG_WARN_STR("TBB: Unhandled out-of-order or duplicate frame: " << (unsigned)frame.header.stationID << " " << (unsigned)frame.header.rspID << " " << (unsigned)frame.header.rcuID << " " << frame.header.time << " " << itsTime << " " << frame.header.bandSliceNr << " " << itsExpSliceNr); return; @@ -310,45 +306,51 @@ void TBB_Dipole::processSpectralFrameData(const TBB_Frame& frame, const SubbandI } /* - * Flag lost frame(s) (assume no out-of-order, see below). Assumes all frames have the same nr of samples. - * Note: this cannot detect lost frames at the end of a dataset. + * Flag lost frame(s) (assume no out-of-order, see below). Assumes all frames have the same nr of samples (fine). + * This cannot detect lost frames at the end of a dataset. */ - unsigned nSamplesPerSubband = frame.header.nOfSamplesPerFrame / itsNrSubbands; // TODO: remainder is zeroed??? chksum pos??? size_t nskipped = offset - itsDatasetLen; if (nskipped > 0) { -//// addFlags(itsDatasetLen, nskipped); // no need to skip/lseek; we use pwrite() below + appendFlags(itsDatasetLen, nskipped); // no need to skip/lseek; we use pwrite() below } /* * On a data checksum error, flag these samples. * Flag zeroed payloads too, as incredibly unlikely to be correct, but not rejected by crc32tbb. * - * The spec says the crc32 is computed for transient data only, but it is also present and valid for spectral data. + * TBB Design Doc states the crc32 is computed for transient data only, but it is also valid for spectral data. + * Except that it looks invalid for the first spectral frame each second, so skip checking those. // TODO: enable 'sliceNr != 0 && ' below after verifying with recent real data */ - if (!crc32tbb(&frame.payload, 2 * frame.header.nOfSamplesPerFrame)) { -//// addFlags(offset, frame.header.nOfSamplesPerFrame); - const uint32_t* crc32 = reinterpret_cast<const uint32_t*>(&frame.payload.data[2 * frame.header.nOfSamplesPerFrame]); - LOG_INFO_STR("TBB: crc32: " << frame.header.toString() << " " << *crc32); + unsigned nSamplesPerSubband = frame.header.nOfSamplesPerFrame / itsNrSubbands; // any remainder is zeroed until the crc32 + if (/*sliceNr != 0 && */!crc32tbb(&frame.payload, 2 * MAX_TBB_SPECTRAL_NSAMPLES)) { + appendFlags(offset, nSamplesPerSubband); + uint32_t crc32; + memcpy(&crc32, &frame.payload.data[2 * MAX_TBB_SPECTRAL_NSAMPLES], sizeof crc32); // strict-aliasing safe + LOG_WARN_STR("TBB: crc32: " << frame.header << " " << crc32); } else if (hasAllZeroDataSamples(frame.payload, 2 * frame.header.nOfSamplesPerFrame)) { -//// addFlags(offset, frame.header.nOfSamplesPerFrame); + appendFlags(offset, nSamplesPerSubband); } - unsigned bandNr = frame.header.bandSliceNr & TBB_BAND_NR_MASK; + /* + * In practice, each frame contains the same number of samples for all subbands, so the received band number is always 0. + * Hence, disable support for cross-frame slices, such that in spectral mode we can also store flags in 1D. + */ + /*unsigned bandNr = frame.header.bandSliceNr & TBB_BAND_NR_MASK; if (bandNr + itsNrSubbands >= RSP_NR_SUBBANDS) { LOG_WARN("TBB: Incorrect band number has been corrected to 0"); - bandNr = 0; // may also be wrong, but at least mem safe - } + bandNr = 0; // safe default + }*/ // Data arrives interleaved, so reorder, one sample at a time. Esp. inefficient if only 1 subband, but fast enough. for (unsigned i = 0; i < nSamplesPerSubband; ++i) { for (unsigned j = 0; j < itsNrSubbands; ++j) { - off_t sampleOffset = (offset + subbandInfo.storageIndices[bandNr + j] * SPECTRAL_TRANSFORM_SIZE) * 2 * sizeof(frame.payload.data[0]); + off_t sampleOffset = (offset + subbandInfo.storageIndices[j/*(bandNr + j) % itsNrSubbands*/] * SPECTRAL_TRANSFORM_SIZE) * 2 * sizeof(frame.payload.data[0]); pwrite(itsRawOut->fd, &frame.payload.data[2 * (i * itsNrSubbands + j)], 2 * sizeof(frame.payload.data[0]), sampleOffset); } offset += 1; } itsTime = frame.header.time; - itsExpSliceNr = sliceNr + frame.header.nOfSamplesPerFrame; + itsExpSliceNr = sliceNr + nSamplesPerSubband; itsDatasetLen = offset; } @@ -356,28 +358,29 @@ void TBB_Dipole::initTBB_DipoleDataset(const TBB_Header& header, const Parset& p const StationMetaData& stationMetaData, const SubbandInfo& subbandInfo, const string& rawFilename, dal::TBB_Station& station) { - itsDataset = new dal::TBB_DipoleDataset(station.dipole(header.stationID, header.rspID, header.rcuID)); // deleted in destr - - // Create 1- or 2-dim, unbounded (-1) dataset. // Override endianess. TBB data is always stored little endian and also received as such, so written as-is on any platform. if (subbandInfo.centralFreqs.empty()) { // transient mode + dal::TBB_DipoleDataset* dpDataset = new dal::TBB_DipoleDataset(station.dipole(header.stationID, header.rspID, header.rcuID)); + itsDataset = static_cast<dal::TBB_Dataset<short>*>(dpDataset); + itsDataset->create1D(0, -1, LOFAR::basename(rawFilename), itsDataset->LITTLE); - itsDataset->sampleNumber().value = header.sampleNr; + dpDataset->sampleNumber().value = header.sampleNr; } else { // spectral mode + dal::TBB_SubbandsDataset* sbDataset = new dal::TBB_SubbandsDataset(station.subbands(header.stationID, header.rspID, header.rcuID)); + itsDataset = reinterpret_cast<dal::TBB_Dataset<short>*>(sbDataset); // not so nice + vector<ssize_t> dims(2), maxdims(2); dims[0] = 0; - dims[1] = subbandInfo.centralFreqs.size(); + dims[1] = itsNrSubbands; maxdims[0] = -1; // only the 1st dim can be extendible - maxdims[1] = subbandInfo.centralFreqs.size(); + maxdims[1] = itsNrSubbands; itsDataset->create(dims, maxdims, LOFAR::basename(rawFilename), itsDataset->LITTLE); -// TODO: enable and add to DAL -// itsDataset->sliceNumber().value = header.bandSliceNr >> TBB_SLICE_NR_SHIFT; // TODO: needed? - -// itsDataset->spectralNofBands().value = subbandInfo.centralFreqs.size(); -// itsDataset->spectralBands().value = subbandInfo.centralFreqs; -// itsDataset->spectralBandsUnit().value = "MHz"; + sbDataset->sliceNumber() .value = header.bandSliceNr >> TBB_SLICE_NR_SHIFT; + sbDataset->spectralNofBands() .value = itsNrSubbands; + sbDataset->spectralBands().create(itsNrSubbands).set(subbandInfo.centralFreqs); + sbDataset->spectralBandsUnit().value = "MHz"; } itsDataset->groupType().value = "DipoleDataset"; @@ -392,7 +395,7 @@ void TBB_Dipole::initTBB_DipoleDataset(const TBB_Header& header, const Parset& p itsDataset->samplesPerFrame().value = header.nOfSamplesPerFrame; // possibly sanitized //itsDataset->dataLength().value is set at the end (destr) - //itsDataset->flagOffsets().value is set at the end (destr) // TODO: remove + //itsDataset->flagOffsets().value is set at the end (destr) // TODO: attrib -> 1D dataset itsDataset->nyquistZone().value = parset.nyquistZone(); //#include "MAC/APL/PIC/RSP_Driver/src/CableSettings.h" or "RCUCables.h" @@ -420,14 +423,15 @@ elke .dat file bevat 96*512*2 doubles voor 96 rcus, 512 frequenties, een complexe waarde maar nu vraag ik me wel weer af of de frequenties of de rcus eerst komen */ +//NL stations: 768 kB, Int'l: 1.5 MB. Drop optional ASCI header. See also Station/StationCal/writeCalTable.m //itsDataset->dipoleCalibrationDelay().value = ???; // Pim can compute this from the GainCurve below //itsDataset->dipoleCalibrationDelayUnit().value = 's'; - //itsDataset->dipoleCalibrationGainCurve().value = ???; - + //itsDataset->dipoleCalibrationGainCurve().create(???.size()).set(???); // st cal table +//write cal tables into proper n-dimensional h5 data set, not attribute! Add access functions to DAL? // Skip if station is not participating in the observation (should not happen). if (stationMetaData.available && 2u * 3u * header.rcuID + 2u < stationMetaData.antPositions.size()) { - /* + /*TODO * Selecting the right positions depends on the antenna set. Checking vs the tables in * lhn001:/home/veen/lus/src/code/data/lofar/antennapositions/ can help, but their repos may be outdated. */ @@ -435,7 +439,7 @@ maar nu vraag ik me wel weer af of de frequenties of de rcus eerst komen antPos[0] = stationMetaData.antPositions[2u * 3u * header.rcuID]; antPos[1] = stationMetaData.antPositions[2u * 3u * header.rcuID + 1u]; antPos[2] = stationMetaData.antPositions[2u * 3u * header.rcuID + 2u]; - itsDataset->antennaPosition() .value = antPos; // absolute position + itsDataset->antennaPosition().create(antPos.size()).set(antPos); // absolute position itsDataset->antennaPositionUnit() .value = "m"; itsDataset->antennaPositionFrame().value = parset.positionType(); // "ITRF" @@ -445,17 +449,18 @@ maar nu vraag ik me wel weer af of de frequenties of de rcus eerst komen * but given the HBA0/HBA1 "ears" depending on antenna set, it was * decided to store them per antenna. */ - itsDataset->antennaNormalVector() .value = stationMetaData.normalVector; // 3 doubles - itsDataset->antennaRotationMatrix().value = stationMetaData.rotationMatrix; // 9 doubles, 3x3, row-major + itsDataset->antennaNormalVector() .create(stationMetaData.normalVector.size()).set(stationMetaData.normalVector); // 3 doubles + itsDataset->antennaRotationMatrix().create(stationMetaData.rotationMatrix.size()).set(stationMetaData.rotationMatrix); // 9 doubles, 3x3, row-major } // Tile beam is the analog beam. Only HBA can have one analog beam; optional. if (parset.haveAnaBeam()) { - itsDataset->tileBeam() .value = parset.getAnaBeamDirection(); // always for beam 0 + vector<double> anaBeamDir(parset.getAnaBeamDirection()); + itsDataset->tileBeam() .create(anaBeamDir.size()).set(anaBeamDir); // always for beam 0 itsDataset->tileBeamUnit() .value = "m"; itsDataset->tileBeamFrame().value = parset.getAnaBeamDirectionType(); // idem - //itsDataset->tileBeamDipoles().value = ???; + //itsDataset->tileBeamDipoles().create(???.size()).set(???); //itsDataset->tileCoefUnit().value = ???; //itsDataset->tileBeamCoefs().value = ???; @@ -466,7 +471,7 @@ maar nu vraag ik me wel weer af of de frequenties of de rcus eerst komen //itsDataset->tileDipolePositionFrame().value = ???; } - itsDataset->dispersionMeasure() .value = parset.dispersionMeasure(0, 0); // 0.0 if no dedispersion was done + itsDataset->dispersionMeasure() .value = parset.dispersionMeasure(0, 0); // beam, pencil TODO: adapt too if >1 beam? itsDataset->dispersionMeasureUnit().value = "pc/cm^3"; } @@ -502,10 +507,15 @@ TBB_Station::TBB_Station(const string& stationName, Mutex& h5Mutex, const Parset } TBB_Station::~TBB_Station() { - // Executed by the main thread after joined with all workers, so no need to lock or delay cancellation. + /* + * Apart from the main thread, also potentially (rarely) executed by an output thread on failed + * to insert new TBB_Station object into an std::map. For the output thread case, do dc and slH5. + */ + ScopedDelayCancellation dc; try { + ScopedLock slH5(itsH5Mutex); itsStation.nofDipoles().value = itsStation.dipoles().size(); - } catch (dal::DALException& exc) { + } catch (exception& exc) { // dal::DALException or worse LOG_WARN_STR("TBB: failed to set station NOF_DIPOLES attribute: " << exc.what()); } } @@ -519,9 +529,9 @@ SubbandInfo TBB_Station::getSubbandInfo(const Parset& parset) const { int operatingMode = itsParset.getInt("Observation.TBB.TBBsetting.operatingMode", 0); if (operatingMode == TBB_SPECTRAL_MODE) { - vector<unsigned> tbbSubbandList(parset.getUint32Vector("Observation.TBB.TBBsetting.subbandList", true)); // TODO: what happens if key does not exists? exc? also test empty parset + vector<unsigned> tbbSubbandList(parset.getUint32Vector("Observation.TBB.TBBsetting.subbandList", true)); if (tbbSubbandList.empty() || tbbSubbandList.size() > MAX_TBB_SPECTRAL_NSAMPLES) { - throw InterfaceException("TBB: in spectral mode, the TBB subband list must be non-empty and not too long"); + throw InterfaceException("TBB: spectral mode selected, but empty or too long subband list provided"); } sort(tbbSubbandList.begin(), tbbSubbandList.end()); @@ -537,7 +547,7 @@ SubbandInfo TBB_Station::getSubbandInfo(const Parset& parset) const { for (unsigned i = 0; i < tbbSubbandList.size(); ++i) { unsigned sbNr = tbbSubbandList[i]; if (sbNr >= RSP_NR_SUBBANDS) { - throw InterfaceException("TBB: all indicated subband numbers must be < 512"); + throw InterfaceException("TBB: indicated subband number too high"); } info.storageIndices[sbNr] = i; } @@ -575,7 +585,6 @@ void TBB_Station::processPayload(const TBB_Frame& frame) { } } -// For timestamp attributes in UTC. string TBB_Station::utcTimeStr(double time) const { time_t timeSec = static_cast<time_t>(floor(time)); unsigned long timeNSec = static_cast<unsigned long>(round( (time-floor(time))*1e9 )); @@ -627,7 +636,8 @@ void TBB_Station::initCommonLofarAttributes() { itsH5File.observationNofStations().value = itsParset.nrStations(); // TODO: SS beamformer? // For the observation attribs, dump all stations participating in the observation (i.e. allStationNames(), not mergedStationNames()). // This may not correspond to which station HDF5 groups will be written for TBB, but that is true anyway, regardless of any merging. - itsH5File.observationStationsList().value = itsParset.allStationNames(); // TODO: SS beamformer? + vector<string> allStNames(itsParset.allStationNames()); + itsH5File.observationStationsList().create(allStNames.size()).set(allStNames); // TODO: SS beamformer? double subbandBandwidth = itsParset.subbandBandwidth(); double channelBandwidth = itsParset.channelWidth(); @@ -662,7 +672,7 @@ void TBB_Station::initCommonLofarAttributes() { targets[sap] = itsParset.beamTarget(sap); } - itsH5File.targets().value = targets; + itsH5File.targets().create(targets.size()).set(targets); #ifndef TBB_WRITER_VERSION itsH5File.systemVersion().value = LOFAR::StorageVersion::getVersion(); @@ -681,14 +691,14 @@ void TBB_Station::initTBB_RootAttributesAndGroups(const string& stName) { int operatingMode = itsParset.getInt("Observation.TBB.TBBsetting.operatingMode", 0); if (operatingMode == TBB_SPECTRAL_MODE) { itsH5File.operatingMode().value = "spectral"; -// itsH5File.spectralTransformSize().value = SPECTRAL_TRANSFORM_SIZE; // TODO: enable and add to DAL + itsH5File.spectralTransformSize().value = SPECTRAL_TRANSFORM_SIZE; } else { itsH5File.operatingMode().value = "transient"; } itsH5File.nofStations().value = 1u; - // Find the station name we are looking for ("CS001" == "CS001HBA0") and retrieve its pos using the found idx. + // Find the station name we are looking for and retrieve its pos using the found idx. vector<double> stPos; vector<string> obsStationNames(itsParset.allStationNames()); @@ -723,14 +733,15 @@ void TBB_Station::initStationGroup(dal::TBB_Station& st, const string& stName, st.stationName().value = stName; if (!stPosition.empty()) { - st.stationPosition() .value = stPosition; + st.stationPosition() .create(stPosition.size()).set(stPosition); st.stationPositionUnit() .value = "m"; st.stationPositionFrame().value = itsParset.positionType(); } // digital beam(s) if (itsParset.nrBeams() > 0) { // TODO: adapt DAL, so we can write all digital beams, analog too if tiles (HBA) - st.beamDirection() .value = itsParset.getBeamDirection(0); + vector<double> beamDir(itsParset.getBeamDirection(0)); + st.beamDirection() .create(beamDir.size()).set(beamDir); st.beamDirectionUnit() .value = "m"; st.beamDirectionFrame().value = itsParset.getBeamDirectionType(0); } @@ -741,7 +752,7 @@ void TBB_Station::initStationGroup(dal::TBB_Station& st, const string& stName, st.clockOffset() .value = clockCorr; st.clockOffsetUnit().value = "s"; } catch (APSException& exc) { - LOG_WARN_STR("TBB: failed to write clock correction and offset attributes: " << exc); + LOG_WARN_STR("TBB: failed to write station clock offset and unit attributes: " << exc); } //st.nofDipoles.value is set at the end (destr) @@ -821,8 +832,10 @@ TBB_StreamWriter::TBB_StreamWriter(TBB_Writer& writer, const string& inputStream throw; } -#ifdef DUMP_RAW_STATION_DATA - string rawStDataFilename("station_data_" + formatString("%zu", itsFrameBuffers) + ".raw"); +#ifdef DUMP_RAW_STATION_FRAMES + struct timeval ts; + ::gettimeofday(&ts, NULL); + string rawStDataFilename("tbb_raw_station_frames_" + formatString("%ld_%p", ts.tv_sec, (void*)itsFrameBuffers) + ".fraw"); try { itsRawStationData = new FileStream(rawStDataFilename, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); } catch (exception& exc) { @@ -835,7 +848,7 @@ TBB_StreamWriter::~TBB_StreamWriter() { // Only cancel the input thread, which will notify the output thread. itsInputThread->cancel(); -#ifdef DUMP_RAW_STATION_DATA +#ifdef DUMP_RAW_STATION_FRAMES delete itsRawStationData; #endif delete itsInputThread; @@ -848,9 +861,9 @@ time_t TBB_StreamWriter::getTimeoutStampSec() const { } void TBB_StreamWriter::frameHeaderLittleToHost(TBB_Header& header) const { - header.seqNr = le32toh(header.seqNr); // to be zeroed to check header crc; otherwise not so useful + header.seqNr = le32toh(header.seqNr); // set to 0 for crc16, otherwise unused header.time = le32toh(header.time); - header.sampleNr = le32toh(header.sampleNr); // also swaps header.bandSliceNr + header.sampleNr = le32toh(header.sampleNr); header.nOfSamplesPerFrame = le16toh(header.nOfSamplesPerFrame); header.nOfFreqBands = le16toh(header.nOfFreqBands); header.spare = le16toh(header.spare); // unused @@ -877,19 +890,17 @@ void TBB_StreamWriter::correctSampleNr(TBB_Header& header) const { bool TBB_StreamWriter::crc16tbb(const TBB_Header* header) { itsCrc16gen.reset(); - /* - * The header checksum is done like the data, i.e. on 16 bit little endian blocks at a time. - * As with the data crc, both big and little endian CPUs need to byte swap. - */ - const int16_t* ptr = reinterpret_cast<const int16_t*>(header); // TODO: access cross-type through char or uchar ptrs (or memcpy), in all reint_cast cases in the code: unsigned char *ptr = (unsigned char*)&floatVar; // and then accessing ptr[0] to ptr[sizeof(floatVar)-1] is legal. - for (size_t i = 0; i < (sizeof(*header) - sizeof(header->crc16)) / sizeof(int16_t); i++) { - int16_t val = __bswap_16(ptr[i]); - itsCrc16gen.process_bytes(&val, sizeof(int16_t)); + const char* ptr = reinterpret_cast<const char*>(header); // to char* for strict-aliasing + for (unsigned i = 0; i < sizeof(*header) - sizeof(header->crc16); i += 2) { + int16_t val; + memcpy(&val, &ptr[i], sizeof val); // strict-aliasing safe + val = __bswap_16(val); + itsCrc16gen.process_bytes(&val, sizeof val); } // It is also possible to process header->crc16 and see if checksum() equals 0. uint16_t crc16val = header->crc16; -#if __BYTE_ORDER == __BIG_ENDIAN +#if __BYTE_ORDER == __BIG_ENDIAN || defined WORDS_BIGENDIAN // for cross-compilation on little endian; fails for big->little crc16val = __bswap_16(crc16val); #endif return itsCrc16gen.checksum() == crc16val; @@ -902,19 +913,18 @@ bool TBB_StreamWriter::crc16tbb(const TBB_Header* header) { bool TBB_Dipole::crc32tbb(const TBB_Payload* payload, size_t nTrSamples) { itsCrc32gen.reset(); - /* - * Both little and big endian CPUs need to byte swap, because the data always arrives - * in little and the boost routines treat it as uint8_t[] (big). - */ - const int16_t* ptr = reinterpret_cast<const int16_t*>(payload->data); - for (size_t i = 0; i < nTrSamples; i++) { - int16_t val = __bswap_16(ptr[i]); - itsCrc32gen.process_bytes(&val, sizeof(int16_t)); + const char* ptr = reinterpret_cast<const char*>(payload->data); // to char* for strict-aliasing + for (unsigned i = 0; i < nTrSamples * sizeof(int16_t); i += 2) { + int16_t val; + memcpy(&val, &ptr[i], sizeof val); // strict-aliasing safe + val = __bswap_16(val); + itsCrc32gen.process_bytes(&val, sizeof val); } // It is also possible to process crc32val and see if checksum() equals 0. - uint32_t crc32val = *reinterpret_cast<const uint32_t*>(&ptr[nTrSamples]); -#if __BYTE_ORDER == __BIG_ENDIAN + uint32_t crc32val; + memcpy(&crc32val, &ptr[nTrSamples * sizeof(int16_t)], sizeof crc32val); // idem +#if __BYTE_ORDER == __BIG_ENDIAN || defined WORDS_BIGENDIAN // for cross-compilation on little endian; fails for big->little crc32val = __bswap_32(crc32val); #endif return itsCrc32gen.checksum() == crc32val; @@ -929,9 +939,9 @@ void TBB_StreamWriter::processHeader(TBB_Header& header, size_t recvPayloadSize) if (!crc16tbb(&header)) { /* * The TBB spec states that each frame has the same fixed length, so the previous values are a good base guess if the header crc fails. - * But it is not clear if it is worth the effort to try to guess to fix something up. More likely there is a bug, so for now, drop the frame. + * But it is not clear if it is worth the effort to try to guess to fix something up. For now, drop and log. */ - throw TBB_MalformedFrameException("crc16: " + header.toString()); // printed header not bswapped on big endian + THROW(TBB_MalformedFrameException, "crc16: " << header); // header not yet bswapped on _big_ endian } /* @@ -940,12 +950,12 @@ void TBB_StreamWriter::processHeader(TBB_Header& header, size_t recvPayloadSize) */ if (recvPayloadSize < 2 * sizeof(int16_t) + sizeof(uint32_t)) { // Drop it. The data crc routine only works for at least 2 transient or 1 spectral sample(s) + a crc32. - throw TBB_MalformedFrameException("dropping too small frame: " + recvPayloadSize); + THROW(TBB_MalformedFrameException, "dropping too small frame: " << recvPayloadSize); } frameHeaderLittleToHost(header); // Verify indicated sample freq, also to reject zeroed headers, which the crc16tbb does not reject. if (header.sampleFreq != 200 && header.sampleFreq != 160) { - throw TBB_MalformedFrameException("invalid sample frequency in frame header: " + header.sampleFreq); + THROW(TBB_MalformedFrameException, "dropping frame with invalid sample frequency in frame header: " << header.sampleFreq); } size_t sampleSize; @@ -960,7 +970,7 @@ void TBB_StreamWriter::processHeader(TBB_Header& header, size_t recvPayloadSize) } void TBB_StreamWriter::mainInputLoop() { - // Always (try to) notify output thread to stop at the end. + // Always (try to) notify output thread to stop at the end, else we may hang. class NotifyOutputThread { Queue<TBB_Frame*>& queue; public: @@ -979,7 +989,8 @@ void TBB_StreamWriter::mainInputLoop() { stream = createStream(itsInputStreamName, true); } catch (Exception& exc) { // SystemCallException or InterfaceException (or TimeOutException) LOG_WARN_STR(itsLogPrefix << exc); - return; // do not set itsInExitStatus to 1: if not all files with "$" replaced exist, that's fine + itsInExitStatus = 1; + return; } LOG_INFO_STR(itsLogPrefix << "reading incoming data from " << itsInputStreamName); @@ -987,21 +998,21 @@ void TBB_StreamWriter::mainInputLoop() { TBB_Frame* frame; try { - frame = itsFreeQueue.remove(); // may only throw SystemCallException + frame = itsFreeQueue.remove(); - size_t nread = stream->tryRead(frame, itsExpFrameSize); // cannot simply retry until expected size: UDP used unless local test + size_t nread = stream->tryRead(frame, itsExpFrameSize); // read() once for udp // Notify master that we are still busy. (Racy, but ok, see the timeoutstamp decl.) ::gettimeofday(&itsTimeoutStamp, NULL); -#ifdef DUMP_RAW_STATION_DATA +#ifdef DUMP_RAW_STATION_FRAMES try { itsRawStationData->write(frame, nread); } catch (exception& exc) { /* open() probably failed, don't spam */ } #endif if (nread < sizeof(TBB_Header)) { - throw TBB_MalformedFrameException("dropping too small TBB frame"); + throw TBB_MalformedFrameException("dropping too small frame"); } processHeader(frame->header, nread - sizeof(TBB_Header)); @@ -1086,7 +1097,6 @@ TBB_Writer::TBB_Writer(const vector<string>& inputStreamNames, const Parset& par , itsRunNr(0) { // Mask all signals to inherit for workers. This forces signals to be delivered to the main thread. - // Wrap to make sure we always (try to) restore the signal mask. struct SigMask { sigset_t sigset_old; @@ -1094,14 +1104,12 @@ TBB_Writer::TBB_Writer(const vector<string>& inputStreamNames, const Parset& par sigset_t sigset_all_masked; ::sigfillset(&sigset_all_masked); if (::pthread_sigmask(SIG_SETMASK, &sigset_all_masked, &sigset_old) != 0) { - // The LOFAR sys uses another way to control us, so do not make this fatal. LOG_WARN_STR("TBB: pthread_sigmask() failed to mask signals to inherit for worker threads."); } } ~SigMask() { if (::pthread_sigmask(SIG_SETMASK, &sigset_old, NULL) != 0) { - // No exc in destr. If restoring fails and keepRunning = true, we remain deaf. LOG_WARN_STR("TBB: pthread_sigmask() failed to restore signals. We may be deaf to signals."); } } @@ -1114,8 +1122,9 @@ TBB_Writer::TBB_Writer(const vector<string>& inputStreamNames, const Parset& par if (operatingMode == TBB_TRANSIENT_MODE) { expNTrSamples = DEFAULT_TBB_TRANSIENT_NSAMPLES; } else if (operatingMode == TBB_SPECTRAL_MODE) { - expNTrSamples = 2 * DEFAULT_TBB_SPECTRAL_NSAMPLES; + expNTrSamples = 2 * MAX_TBB_SPECTRAL_NSAMPLES; } else { + expNTrSamples = DEFAULT_TBB_TRANSIENT_NSAMPLES; LOG_WARN("TBB: Failed to get operating mode from parset, assuming transient"); } @@ -1154,10 +1163,15 @@ TBB_Station* TBB_Writer::getStation(const TBB_Header& header) { TBB_Station* station; { ScopedLock slH5(itsH5Mutex); - station = new TBB_Station(stationName, itsH5Mutex, itsParset, stMetaData, h5Filename); // TODO: must guarantee insert() cannot fail, else leaks; destr here is unsafe + station = new TBB_Station(stationName, itsH5Mutex, itsParset, stMetaData, h5Filename); } - return itsStations.insert(make_pair(header.stationID, station)).first->second; + try { + return itsStations.insert(make_pair(header.stationID, station)).first->second; + } catch (exception& exc) { + delete station; + throw; + } } string TBB_Writer::createNewTBB_H5Filename(const TBB_Header& header, const string& stationName) { @@ -1183,7 +1197,7 @@ string TBB_Writer::createNewTBB_H5Filename(const TBB_Header& header, const strin string triggerDateTime(formatFilenameTimestamp(tv, output_format, output_format_secs, sizeof(output_format_example))); string h5Filename(itsOutDir + "L" + obsIDStr + "_" + stationName + "_" + triggerDateTime + "_" + typeExt); - // If the file already exists, add a run nr and retry. (seq race with DAL's open and doesn't check .raw, but good enough) + // If the file already exists, add a run nr and retry. (might race and doesn't check .raw, but good enough) // If >1 stations per node, start at the prev run nr if any (hence itsRunNr). if (itsRunNr == 0) { if (::access(h5Filename.c_str(), F_OK) != 0 && errno == ENOENT) { diff --git a/RTCP/Storage/src/TBB_Writer_main.cc b/RTCP/Storage/src/TBB_Writer_main.cc index 22ad98aedac9be82acd0a422a0abb1b7400d195c..249543d948c511cdba6ef2599df4fcb5d3dee5d9 100644 --- a/RTCP/Storage/src/TBB_Writer_main.cc +++ b/RTCP/Storage/src/TBB_Writer_main.cc @@ -25,7 +25,7 @@ * TBB writers written by Lars Baehren, Andreas Horneffer, and Joseph Masters. */ -#include <lofar_config.h> // before any other include +#include <lofar_config.h> // before any other include #define _FILE_OFFSET_BITS 64 #include <cstddef> @@ -41,6 +41,7 @@ #include <getopt.h> #include <iostream> +#include <sstream> #include <boost/lexical_cast.hpp> @@ -55,15 +56,16 @@ #include <dal/lofar/StationNames.h> -#define TBB_DEFAULT_BASE_PORT 0x7bb0 // i.e. tbb0 -#define TBB_DEFAULT_LAST_PORT 0x7bbb // 0x7bbf for NL, 0x7bbb for int'l stations +#define TBB_DEFAULT_BASE_PORT 0x7bb0 // i.e. tbb0 +#define TBB_DEFAULT_LAST_PORT 0x7bbb // 0x7bbf for NL, 0x7bbb for int'l stations -#define STDLOG_BUFFER_SIZE 1024 +#define STDLOG_BUFFER_SIZE 1024 using namespace std; struct progArgs { string parsetFilename; + string stCalTablesDir; string antFieldDir; string outputDir; string input; @@ -75,7 +77,6 @@ struct progArgs { static char stdoutbuf[STDLOG_BUFFER_SIZE]; static char stderrbuf[STDLOG_BUFFER_SIZE]; -// Install a new handler to produce backtraces for std::bad_alloc. LOFAR::NewHandler badAllocExcHandler(LOFAR::BadAllocException::newHandler); static bool sigint_seen; @@ -115,26 +116,34 @@ static vector<string> getTBB_InputStreamNames(const string& input, uint16_t port LOFAR::StationConfig stConf; nTbbBoards = stConf.nrTBBs; } catch (LOFAR::AssertError& ) { // config file not found - LOG_DEBUG_STR("Falling back to up to " << TBB_DEFAULT_LAST_PORT - TBB_DEFAULT_BASE_PORT + 1 << " streams (1 per board)"); + LOG_DEBUG_STR("Falling back to at most " << TBB_DEFAULT_LAST_PORT - TBB_DEFAULT_BASE_PORT + 1 << " input streams (1 per board)"); nTbbBoards = TBB_DEFAULT_LAST_PORT - TBB_DEFAULT_BASE_PORT + 1; // fallback } vector<string> allInputStreamNames; if (input == "udp" || input == "tcp") { for (uint16_t port = portsBase; port <= portsBase + nTbbBoards; ++port) { - // 0.0.0.0: could restrict to station IPs/network, but need netmask lookup and allow localhost. Not critical: we are on a separate VLAN. + // 0.0.0.0: could restrict to station IPs/network, but need netmask lookup and allow localhost. Not critical: data arrives on a separate VLAN. string streamName(input + ":0.0.0.0:" + LOFAR::formatString("%hu", port)); allInputStreamNames.push_back(streamName); } } else { // file or named pipe input + size_t colonPos = input.find(':'); + if (colonPos == string::npos) { + return allInputStreamNames; + } size_t placeholderPos = input.find_last_of('%'); if (placeholderPos == string::npos) { // single input, no expansion needed - allInputStreamNames.push_back(input); - } else { // expand: replace e.g. file:x%y-%.raw by file:x%y-0.raw, file:x%y-1.raw, ..., file:x%y-11.raw + if (access(input.c_str() + colonPos + 1, R_OK) == 0) { + allInputStreamNames.push_back(input); + } + } else { // expand e.g. file:x%y-%.raw into {file:x%y-0.raw, file:x%y-1.raw, ..., file:x%y-11.raw} for (int i = 0; i < nTbbBoards; ++i) { string streamName(input); streamName.replace(placeholderPos, 1, LOFAR::formatString("%u", i)); - allInputStreamNames.push_back(streamName); + if (access(streamName.c_str() + colonPos + 1, R_OK) == 0) { + allInputStreamNames.push_back(streamName); + } } } } @@ -142,6 +151,44 @@ static vector<string> getTBB_InputStreamNames(const string& input, uint16_t port return allInputStreamNames; } +static void retrieveStationCalTables(string& stCalTablesDir) { + /* + * Users need the station calibration tables included. This is a major pain, because + * we figure out which station(s) we receive from at runtime (relying on the static + * mapping is a disaster waiting to happen), we cannot ask the stations and the + * alternative, from svn, is unreliable and races with (few) Science Support updates. + * Not all users care about the race, a few do. Also, auth, and this exposes an internal + * interface (cal tables) to users... Still do it: TBB is too low prio to get stuff nice. + * + * Get tables from all stations for the right cal mode (i.e. usually only verifies svn local copy), + * Run 'svn cleanup' and 'svn upgrade' when needed, otherwise remove the local copies and re-retrieve. + * + + */ + +//svn checkout https://svn.astron.nl/Station/trunk/CalTables +//but only the needed files +//svn update +//Ctrl-C doesn't seem to kill svn co/up (only pause/halt), so use Ctrl-\ (QUIT), then svn cleanup + +//svn: Working copy '.' locked +//svn: run 'svn cleanup' to remove locks (type 'svn help cleanup' for details) +//svn cleanup + +//rm -rf CalTables + +// Note: include the entire cal table as-is, because that easily allows users to just resort to the raw files + +// - if stCalTablesDir.empty(): + +// - get station names, st cal mode +// - fork process (sh script), do data writes +// - sh script does svn checkout/update on req files only into ~/TBB_Writer-Station-CalTabs-localcopy/Station/CalTables/* +// - listen for tbb data. When data writes done, do timed wait() on script pid, and if ok, add cal tables. +// - if not ok: if timeout { signal script to abort and run svn cleanup, wait()}. Skip writing cal tabs, log warning + script output. + +} + static int antSetName2AntFieldIndex(const string& antSetName) { int idx; @@ -235,7 +282,7 @@ static int doTBB_Run(const vector<string>& inputStreamNames, const LOFAR::RTCP:: */ struct itimerval timer = {args.timeoutVal, args.timeoutVal}; if (setitimer(ITIMER_REAL, &timer, NULL) != 0) { - throw LOFAR::SystemCallException("setitimer failed"); + throw LOFAR::SystemCallException("setitimer", errno, THROW_ARGS); } bool anyFrameReceived = false; // don't quit if there is no data immediately after starting @@ -278,14 +325,10 @@ static int doTBB_Run(const vector<string>& inputStreamNames, const LOFAR::RTCP:: return err; } -static int ensureOutputDirExists(string outputDir) { +static int isExistingDirname(const string& dirname) { struct stat st; - if (outputDir == "") { - outputDir = "."; - } - - if (stat(outputDir.c_str(), &st) != 0) { + if (stat(dirname.c_str(), &st) != 0) { return errno; } @@ -305,14 +348,15 @@ static void printUsage(const char* progname) { cout << TBB_WRITER_VERSION; #endif cout << endl; - cout << "Write incoming LOFAR TBB data with meta data to disk in HDF5 format." << endl; - cout << "Usage: " << progname << " --parsetfile=parsets/L12345.parset [OPTION]..." << endl; + cout << "Write incoming LOFAR TBB data with meta data to storage in HDF5 format." << endl; + cout << "Usage: " << progname << " -p parsets/L12345.parset [OPTION]..." << endl; cout << endl; cout << "Options:" << endl; - cout << " -s, --parsetfile=L12345.parset parset file (observation settings) (mandatory)" << endl; + cout << " -p, --parset=L12345.parset path to file with observation settings (mandatory)" << endl; cout << endl; - cout << " -a, --antfielddir=/d/AntennaFields override $LOFARROOT and parset path for antenna field files (like CS001-AntennaField.conf)" << endl; - cout << " -o, --outputdir=tbbout output directory" << endl; + cout << " -c, --stcaltablesdir=/c/CalTables path to override SVN retrieval of station calibration tables (like CS001/CalTable_001_mode1.dat)" << endl; + cout << " -a, --antfielddir=/a/AntennaFields path to override $LOFARROOT and parset path for antenna field files (like CS001-AntennaField.conf)" << endl; + cout << " -o, --outputdir=tbbout existing output directory" << endl; cout << " -i, --input=tcp|udp input stream(s) or type (default: udp)" << endl; cout << " file:raw.dat if file or pipe name has a '%'," << endl; cout << " pipe:named-%.pipe then the last '%' is replaced by 0, 1, ..., 11" << endl; @@ -326,11 +370,12 @@ static void printUsage(const char* progname) { } static int parseArgs(int argc, char *argv[], struct progArgs* args) { - int rv = 0; + int status = 0; // Default values args->parsetFilename = ""; // there is no default parset filename, so not passing it is fatal - args->antFieldDir = ""; // idem + args->stCalTablesDir = ""; // idem, but otherwise, retrieve from svn and not fatal + args->antFieldDir = ""; // idem, but otherwise, detect and not fatal args->outputDir = ""; args->input = "udp"; @@ -340,48 +385,69 @@ static int parseArgs(int argc, char *argv[], struct progArgs* args) { args->keepRunning = true; static const struct option long_opts[] = { + // NOTE: If you change this, then also change the code below AND the printUsage() code above! // {const char *name, int has_arg, int *flag, int val} - {"parsetfile", required_argument, NULL, 's'}, // observation (s)ettings - {"antfielddir", required_argument, NULL, 'a'}, - {"outputdir", required_argument, NULL, 'o'}, - {"input", required_argument, NULL, 'i'}, - {"portbase", required_argument, NULL, 'b'}, // port (b)ase - {"timeout", required_argument, NULL, 't'}, + {"parset", required_argument, NULL, 'p'}, + {"stcaltablesdir", required_argument, NULL, 'c'}, // station calibration tables + {"antfielddir", required_argument, NULL, 'a'}, // antenna field info + {"outputdir", required_argument, NULL, 'o'}, + {"input", required_argument, NULL, 'i'}, + {"portbase", required_argument, NULL, 'b'}, // port (b)ase + {"timeout", required_argument, NULL, 't'}, - {"keeprunning", optional_argument, NULL, 'k'}, + {"keeprunning", optional_argument, NULL, 'k'}, - {"help", no_argument, NULL, 'h'}, - {"version", no_argument, NULL, 'v'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, {NULL, 0, NULL, 0} }; opterr = 0; // prevent error printing to stderr by getopt_long() - int opt; + int opt, err; while ((opt = getopt_long(argc, argv, "hvs:a:o:p:b:t:k::", long_opts, NULL)) != -1) { switch (opt) { - case 's': + case 'p': args->parsetFilename = optarg; break; + case 'c': + args->stCalTablesDir = optarg; + if (args->stCalTablesDir[0] != '\0' && args->stCalTablesDir[args->stCalTablesDir.size() - 1] != '/') { + args->stCalTablesDir.push_back('/'); + } + if ((err = isExistingDirname(args->stCalTablesDir)) != 0) { + LOG_FATAL_STR("TBB: station cal tab dir argument value " << optarg << ": " << strerror(err)); + status = 1; + } + break; case 'a': args->antFieldDir = optarg; - if (args->antFieldDir[args->antFieldDir.size() - 1] != '/') { + if (args->antFieldDir[0] != '\0' && args->antFieldDir[args->antFieldDir.size() - 1] != '/') { args->antFieldDir.push_back('/'); } + if ((err = isExistingDirname(args->antFieldDir)) != 0) { + LOG_FATAL_STR("TBB: antenna field dir argument value " << optarg << ": " << strerror(err)); + status = 1; + } break; case 'o': args->outputDir = optarg; - if (args->outputDir[args->outputDir.size() - 1] != '/') { + if (args->outputDir[0] != '\0' && args->outputDir[args->outputDir.size() - 1] != '/') { args->outputDir.push_back('/'); } + if ((err = isExistingDirname(args->outputDir)) != 0) { + LOG_FATAL_STR("TBB: output dir argument value " << optarg << ": " << strerror(err)); + status = 1; + } break; case 'i': if (strcmp(optarg, "tcp") == 0 || strcmp(optarg, "udp") == 0 || - strncmp(optarg, "file:", sizeof("file:")-1) == 0 || strncmp(optarg, "pipe:", sizeof("pipe:")-1) == 0) { + strncmp(optarg, "file:", sizeof("file:")-1) == 0 || + strncmp(optarg, "pipe:", sizeof("pipe:")-1) == 0) { args->input = optarg; } else { - LOG_FATAL_STR("TBB: Invalid input option: " << optarg); - rv = 1; + LOG_FATAL_STR("TBB: Invalid input argument value: " << optarg); + status = 1; } break; case 'b': @@ -391,16 +457,16 @@ static int parseArgs(int argc, char *argv[], struct progArgs* args) { throw boost::bad_lexical_cast(); // abuse exc type to have single catch } } catch (boost::bad_lexical_cast& /*exc*/) { - LOG_FATAL_STR("TBB: Invalid port option: " << optarg); - rv = 1; + LOG_FATAL_STR("TBB: Invalid port argument value: " << optarg); + status = 1; } break; case 't': try { args->timeoutVal.tv_sec = boost::lexical_cast<unsigned long>(optarg); } catch (boost::bad_lexical_cast& /*exc*/) { - LOG_FATAL_STR("TBB: Invalid timeout option: " << optarg); - rv = 1; + LOG_FATAL_STR("TBB: Invalid timeout argument value: " << optarg); + status = 1; } break; case 'k': @@ -411,31 +477,33 @@ static int parseArgs(int argc, char *argv[], struct progArgs* args) { try { args->keepRunning = boost::lexical_cast<bool>(optarg); } catch (boost::bad_lexical_cast& /*exc*/) { - LOG_FATAL_STR("TBB: Invalid keeprunning option: " << optarg); - rv = 1; + LOG_FATAL_STR("TBB: Invalid keeprunning argument value: " << optarg); + status = 1; } break; case 'h': case 'v': - if (rv == 0) { - rv = 2; + if (status == 0) { + status = 2; } break; default: // '?' - LOG_FATAL_STR("TBB: Invalid program argument: " << argv[optind-1]); - rv = 1; + LOG_FATAL_STR("TBB: Invalid program argument or missing argument value: " << argv[optind-1]); + status = 1; } } if (optind < argc) { - LOG_FATAL("TBB: Failed to recognize options:"); + ostringstream oss; + oss << "TBB: Failed to recognize arguments:"; while (optind < argc) { - LOG_FATAL_STR(" " << argv[optind++]); + oss << " " << argv[optind++]; // good enough } - rv = 1; + LOG_FATAL_STR(oss.str()); + status = 1; } - return rv; + return status; } int main(int argc, char* argv[]) { @@ -472,12 +540,13 @@ int main(int argc, char* argv[]) { setTermSigsHandler(); - if ((err = ensureOutputDirExists(args.outputDir)) != 0) { - LOG_FATAL_STR("TBB: output directory: " << strerror(err)); + const vector<string> inputStreamNames(getTBB_InputStreamNames(args.input, args.port)); + if (inputStreamNames.empty()) { + LOG_FATAL("TBB: none of the input streams is accessible to read from"); return 1; } - const vector<string> inputStreamNames(getTBB_InputStreamNames(args.input, args.port)); + retrieveStationCalTables(args.stCalTablesDir); // We don't run alone, so try to increase the QoS we get from the OS to decrease the chance of data loss. setIOpriority(); // reqs CAP_SYS_NICE or CAP_SYS_ADMIN @@ -499,11 +568,11 @@ int main(int argc, char* argv[]) { // Config exceptions (opening or parsing) are fatal. Too bad we cannot have it in one type. } catch (LOFAR::RTCP::InterfaceException& exc) { - LOG_FATAL_STR("TBB: LOFAR::InterfaceException: parset: " << exc); + LOG_FATAL_STR("TBB: Required RTCP parset key/values missing: " << exc); } catch (LOFAR::APSException& exc) { - LOG_FATAL_STR("TBB: LOFAR::APSException: parameterset: " << exc); + LOG_FATAL_STR("TBB: Parameterset error: " << exc); } catch (LOFAR::RTCP::StorageException& exc) { - LOG_FATAL_STR("TBB: LOFAR::StorageException: antenna field files: " << exc); + LOG_FATAL_STR("TBB: Antenna field files: " << exc); } return err == 0 ? 0 : 1; diff --git a/RTCP/Storage/test/tTBB_Writer-spectral.parset b/RTCP/Storage/test/tTBB_Writer-spectral.parset index 16af39ad435f56656a00b8e670407a4ce02b029f..1df583842363fc76a1ec88f8857b497fe94df34b 100644 --- a/RTCP/Storage/test/tTBB_Writer-spectral.parset +++ b/RTCP/Storage/test/tTBB_Writer-spectral.parset @@ -430,8 +430,13 @@ Observation.TBB.TBBsetting.filter1_coeff3 = 0 Observation.TBB.TBBsetting.operatingMode = 2 Observation.TBB.TBBsetting.startlevel = 7 Observation.TBB.TBBsetting.stoplevel = 7 -Observation.TBB.TBBsetting.subbandList = [0] +Observation.TBB.TBBsetting.subbandList = [0..486] Observation.TBB.TBBsetting.subbandList2 = [175..418] +Observation.TBB.TBBsetting.subbandListLBA30_ALL = [154-397] +Observation.TBB.TBBsetting.subbandListHBA115_ALL = [77-320] +Observation.TBB.TBBsetting.subbandListHBA148_ALL = [245-488] +Observation.TBB.TBBsetting.subbandListHBA180_ALL = [128-371] +Observation.TBB.TBBsetting.subbandListHBA210_ALL = [52-255] Observation.TBB.TBBsetting.triggerMode = 1 Observation.TBB.TBBsetting.window = 1M Observation.VirtualInstrument.imageNodeList = [] diff --git a/SAS/OTB/jOTDB3/include/jOTDB3/nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance.h b/SAS/OTB/jOTDB3/include/jOTDB3/nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance.h index 6aae1dfc7e91d1a83c7f7cc3600fa300afb13050..d2907d980f937fae66584829870b3a52830e82d3 100644 --- a/SAS/OTB/jOTDB3/include/jOTDB3/nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance.h +++ b/SAS/OTB/jOTDB3/include/jOTDB3/nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance.h @@ -1,62 +1,389 @@ -#ifndef __nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance__ -#define __nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance__ - -#include <jni.h> - -#ifdef __cplusplus -extern "C" -{ -#endif - -JNIEXPORT void JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_initTreeMaintenance (JNIEnv *env, jobject); -JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_loadMasterFile (JNIEnv *env, jobject, jstring); -JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_loadComponentFile__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2 (JNIEnv *env, jobject, jstring, jstring, jstring); -JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_loadComponentFile__Ljava_lang_String_2Ljava_lang_String_2 (JNIEnv *env, jobject, jstring, jstring); -JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_loadComponentFile__Ljava_lang_String_2 (JNIEnv *env, jobject, jstring); -JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getComponentList__Ljava_lang_String_2Z (JNIEnv *env, jobject, jstring, jboolean); -JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getComponentList__Ljava_lang_String_2 (JNIEnv *env, jobject, jstring); -JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getComponentList__ (JNIEnv *env, jobject); -JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getComponentNode (JNIEnv *env, jobject, jint); -JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getComponentParams (JNIEnv *env, jobject, jint); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_saveComponentNode (JNIEnv *env, jobject, jobject); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_isTopComponent (JNIEnv *env, jobject, jint); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_deleteComponentNode (JNIEnv *env, jobject, jint); -JNIEXPORT jstring JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getFullComponentName (JNIEnv *env, jobject, jobject); -JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_buildTemplateTree (JNIEnv *env, jobject, jint, jshort); -JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_newTemplateTree (JNIEnv *env, jobject); -JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_copyTemplateTree (JNIEnv *env, jobject, jint); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_assignTemplateName (JNIEnv *env, jobject, jint, jstring); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_assignProcessType (JNIEnv *env, jobject, jint, jstring, jstring, jstring); -JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getNode (JNIEnv *env, jobject, jint, jint); -JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getParam__II (JNIEnv *env, jobject, jint, jint); -JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getParam__Lnl_astron_lofar_sas_otb_jotdb3_jOTDBnode_2 (JNIEnv *env, jobject, jobject); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_saveParam (JNIEnv *env, jobject, jobject); -JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getItemList__III (JNIEnv *env, jobject, jint, jint, jint); -JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getItemList__ILjava_lang_String_2 (JNIEnv *env, jobject, jint, jstring); -JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_dupNode (JNIEnv *env, jobject, jint, jint, jshort); -JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_addComponent__IIILjava_lang_String_2 (JNIEnv *env, jobject, jint, jint, jint, jstring); -JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_addComponent__III (JNIEnv *env, jobject, jint, jint, jint); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_saveNode (JNIEnv *env, jobject, jobject); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_saveNodeList (JNIEnv *env, jobject, jobject); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_deleteNode (JNIEnv *env, jobject, jobject); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_deleteNodeList (JNIEnv *env, jobject, jobject); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_checkTreeConstraints__II (JNIEnv *env, jobject, jint, jint); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_checkTreeConstraints__I (JNIEnv *env, jobject, jint); -JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_instanciateTree (JNIEnv *env, jobject, jint); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_pruneTree (JNIEnv *env, jobject, jint, jshort); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_exportTree (JNIEnv *env, jobject, jint, jint, jstring); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_exportResultTree (JNIEnv *env, jobject, jint, jint, jstring); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_deleteTree (JNIEnv *env, jobject, jint); -JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getTopNode (JNIEnv *env, jobject, jint); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_setMomInfo (JNIEnv *env, jobject, jint, jint, jint, jstring); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_setClassification (JNIEnv *env, jobject, jint, jshort); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_setTreeState (JNIEnv *env, jobject, jint, jshort); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_setDescription (JNIEnv *env, jobject, jint, jstring); -JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_setSchedule (JNIEnv *env, jobject, jint, jstring, jstring); -JNIEXPORT jstring JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_errorMsg (JNIEnv *env, jobject); - -#ifdef __cplusplus -} -#endif - -#endif /* __nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance__ */ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance */ + +#ifndef _Included_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance +#define _Included_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: initTreeMaintenance + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_initTreeMaintenance + (JNIEnv *, jobject); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: loadMasterFile + * Signature: (Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_loadMasterFile + (JNIEnv *, jobject, jstring); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: loadComponentFile + * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_loadComponentFile__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2 + (JNIEnv *, jobject, jstring, jstring, jstring); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: loadComponentFile + * Signature: (Ljava/lang/String;Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_loadComponentFile__Ljava_lang_String_2Ljava_lang_String_2 + (JNIEnv *, jobject, jstring, jstring); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: loadComponentFile + * Signature: (Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_loadComponentFile__Ljava_lang_String_2 + (JNIEnv *, jobject, jstring); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: getComponentList + * Signature: (Ljava/lang/String;Z)Ljava/util/Vector; + */ +JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getComponentList__Ljava_lang_String_2Z + (JNIEnv *, jobject, jstring, jboolean); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: getComponentList + * Signature: (Ljava/lang/String;)Ljava/util/Vector; + */ +JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getComponentList__Ljava_lang_String_2 + (JNIEnv *, jobject, jstring); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: getComponentList + * Signature: ()Ljava/util/Vector; + */ +JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getComponentList__ + (JNIEnv *, jobject); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: getComponentNode + * Signature: (I)Lnl/astron/lofar/sas/otb/jotdb3/jVICnodeDef; + */ +JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getComponentNode + (JNIEnv *, jobject, jint); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: getComponentParams + * Signature: (I)Ljava/util/Vector; + */ +JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getComponentParams + (JNIEnv *, jobject, jint); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: saveComponentNode + * Signature: (Lnl/astron/lofar/sas/otb/jotdb3/jVICnodeDef;)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_saveComponentNode + (JNIEnv *, jobject, jobject); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: isTopComponent + * Signature: (I)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_isTopComponent + (JNIEnv *, jobject, jint); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: deleteComponentNode + * Signature: (I)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_deleteComponentNode + (JNIEnv *, jobject, jint); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: getFullComponentName + * Signature: (Lnl/astron/lofar/sas/otb/jotdb3/jVICnodeDef;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getFullComponentName + (JNIEnv *, jobject, jobject); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: buildTemplateTree + * Signature: (IS)I + */ +JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_buildTemplateTree + (JNIEnv *, jobject, jint, jshort); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: newTemplateTree + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_newTemplateTree + (JNIEnv *, jobject); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: copyTemplateTree + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_copyTemplateTree + (JNIEnv *, jobject, jint); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: assignTemplateName + * Signature: (ILjava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_assignTemplateName + (JNIEnv *, jobject, jint, jstring); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: assignProcessType + * Signature: (ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_assignProcessType + (JNIEnv *, jobject, jint, jstring, jstring, jstring); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: getNode + * Signature: (II)Lnl/astron/lofar/sas/otb/jotdb3/jOTDBnode; + */ +JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getNode + (JNIEnv *, jobject, jint, jint); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: getParam + * Signature: (II)Lnl/astron/lofar/sas/otb/jotdb3/jOTDBparam; + */ +JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getParam__II + (JNIEnv *, jobject, jint, jint); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: getParam + * Signature: (Lnl/astron/lofar/sas/otb/jotdb3/jOTDBnode;)Lnl/astron/lofar/sas/otb/jotdb3/jOTDBparam; + */ +JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getParam__Lnl_astron_lofar_sas_otb_jotdb3_jOTDBnode_2 + (JNIEnv *, jobject, jobject); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: saveParam + * Signature: (Lnl/astron/lofar/sas/otb/jotdb3/jOTDBparam;)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_saveParam + (JNIEnv *, jobject, jobject); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: getItemList + * Signature: (III)Ljava/util/Vector; + */ +JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getItemList__III + (JNIEnv *, jobject, jint, jint, jint); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: getItemList + * Signature: (ILjava/lang/String;)Ljava/util/Vector; + */ +JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getItemList__ILjava_lang_String_2 + (JNIEnv *, jobject, jint, jstring); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: dupNode + * Signature: (IIS)I + */ +JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_dupNode + (JNIEnv *, jobject, jint, jint, jshort); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: addComponent + * Signature: (IIILjava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_addComponent__IIILjava_lang_String_2 + (JNIEnv *, jobject, jint, jint, jint, jstring); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: addComponent + * Signature: (III)I + */ +JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_addComponent__III + (JNIEnv *, jobject, jint, jint, jint); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: saveNode + * Signature: (Lnl/astron/lofar/sas/otb/jotdb3/jOTDBnode;)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_saveNode + (JNIEnv *, jobject, jobject); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: saveNodeList + * Signature: (Ljava/util/Vector;)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_saveNodeList + (JNIEnv *, jobject, jobject); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: deleteNode + * Signature: (Lnl/astron/lofar/sas/otb/jotdb3/jOTDBnode;)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_deleteNode + (JNIEnv *, jobject, jobject); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: deleteNodeList + * Signature: (Ljava/util/Vector;)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_deleteNodeList + (JNIEnv *, jobject, jobject); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: checkTreeConstraints + * Signature: (II)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_checkTreeConstraints__II + (JNIEnv *, jobject, jint, jint); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: checkTreeConstraints + * Signature: (I)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_checkTreeConstraints__I + (JNIEnv *, jobject, jint); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: instanciateTree + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_instanciateTree + (JNIEnv *, jobject, jint); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: pruneTree + * Signature: (IS)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_pruneTree + (JNIEnv *, jobject, jint, jshort); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: exportTree + * Signature: (IILjava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_exportTree + (JNIEnv *, jobject, jint, jint, jstring); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: exportResultTree + * Signature: (IILjava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_exportResultTree + (JNIEnv *, jobject, jint, jint, jstring); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: exportMetadata + * Signature: (ILjava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_exportMetadata + (JNIEnv *, jobject, jint, jstring); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: deleteTree + * Signature: (I)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_deleteTree + (JNIEnv *, jobject, jint); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: getTopNode + * Signature: (I)Lnl/astron/lofar/sas/otb/jotdb3/jOTDBnode; + */ +JNIEXPORT jobject JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_getTopNode + (JNIEnv *, jobject, jint); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: setMomInfo + * Signature: (IIILjava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_setMomInfo + (JNIEnv *, jobject, jint, jint, jint, jstring); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: setClassification + * Signature: (IS)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_setClassification + (JNIEnv *, jobject, jint, jshort); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: setTreeState + * Signature: (IS)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_setTreeState + (JNIEnv *, jobject, jint, jshort); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: setDescription + * Signature: (ILjava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_setDescription + (JNIEnv *, jobject, jint, jstring); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: setSchedule + * Signature: (ILjava/lang/String;Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_setSchedule + (JNIEnv *, jobject, jint, jstring, jstring); + +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: errorMsg + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_errorMsg + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/SAS/OTB/jOTDB3/src/nl/astron/lofar/sas/otb/jotdb3/jTreeMaintenance.java b/SAS/OTB/jOTDB3/src/nl/astron/lofar/sas/otb/jotdb3/jTreeMaintenance.java index 27decb792e5d95240030505f1726380631a13974..2d1e0229d55a878f7b62f6cde2bdb37fd8ba8911 100644 --- a/SAS/OTB/jOTDB3/src/nl/astron/lofar/sas/otb/jotdb3/jTreeMaintenance.java +++ b/SAS/OTB/jOTDB3/src/nl/astron/lofar/sas/otb/jotdb3/jTreeMaintenance.java @@ -195,6 +195,10 @@ public class jTreeMaintenance implements jTreeMaintenanceInterface @Override public native boolean exportResultTree (int aTreeID,int topItem,String filename) throws RemoteException; + // Export all reported metadata from the given VIC tree + @Override + public native boolean exportMetadata (int aTreeID,String filename) throws RemoteException; + //# --- Finally some general tree maintenance --- // Delete a tree (of any kind) from the database. @Override diff --git a/SAS/OTB/jOTDB3/src/nl/astron/lofar/sas/otb/jotdb3/jTreeMaintenanceInterface.java b/SAS/OTB/jOTDB3/src/nl/astron/lofar/sas/otb/jotdb3/jTreeMaintenanceInterface.java index ca9e2d4b5d1af8824f4bbe946492280093d7809d..ee319973b02ec3b01a1e5f79c2180c2f0eb9519e 100644 --- a/SAS/OTB/jOTDB3/src/nl/astron/lofar/sas/otb/jotdb3/jTreeMaintenanceInterface.java +++ b/SAS/OTB/jOTDB3/src/nl/astron/lofar/sas/otb/jotdb3/jTreeMaintenanceInterface.java @@ -149,6 +149,10 @@ public interface jTreeMaintenanceInterface extends Remote public boolean exportResultTree (int aTreeID,int topItem,String filename) throws RemoteException; //# --- Finally some general tree maintenance --- // Delete a tree (of any kind) from the database. + + // Export all reported metadata from the given VIC tree + public boolean exportMetadata (int aTreeID,String filename) throws RemoteException; + public boolean deleteTree (int aTreeID) throws RemoteException; // Retrieve the topNode of any tree diff --git a/SAS/OTB/jOTDB3/src/nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance.cc b/SAS/OTB/jOTDB3/src/nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance.cc index f2c0607c9e4fce2a0b4fd41f06354d73b093c428..1630f03295a940a38b1ea6b88d4b57f82091be74 100644 --- a/SAS/OTB/jOTDB3/src/nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance.cc +++ b/SAS/OTB/jOTDB3/src/nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance.cc @@ -944,6 +944,27 @@ JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_ return succes; } +/* + * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance + * Method: exportMetadata + * Signature: (ILjava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance_exportMetadata(JNIEnv *env, jobject jTreeMaintenance, jint treeID, jstring aName) { + jboolean isCopy(0); + jboolean succes(0); + const char* name = env->GetStringUTFChars (aName, &isCopy); + try { + succes = ((TreeMaintenance*)getCObjectPtr(env,jTreeMaintenance,"_TreeMaintenance"))->exportMetadata (treeID, name); + env->ReleaseStringUTFChars (aName, name); + } catch (exception &ex) { + cout << "Exception during TreeMaintenance::exportMetadata(" << treeID << "," << name << ") "<< ex.what() << endl; + env->ReleaseStringUTFChars (aName, name); + env->ThrowNew(env->FindClass("java/lang/Exception"),ex.what()); + } + + return succes; +} + /* * Class: nl_astron_lofar_sas_otb_jotdb3_jTreeMaintenance * Method: deleteTree diff --git a/SAS/OTB/pom.xml b/SAS/OTB/pom.xml index 45775c4a89ed2ca194f5822ff0a6ba387c8f1eac..e2eb55cc86abc275bab6ab985a6c5ede7f831baf 100644 --- a/SAS/OTB/pom.xml +++ b/SAS/OTB/pom.xml @@ -22,7 +22,7 @@ <!-- Properties that can be used throughout the POM as a substitution, and are used as filters in resources if enabled. --> <properties> - <lofar.lib.version>1.9.0</lofar.lib.version> + <lofar.lib.version>1.10.0</lofar.lib.version> </properties> diff --git a/SAS/OTDB/include/OTDB/TreeMaintenance.h b/SAS/OTDB/include/OTDB/TreeMaintenance.h index 8d57d8223ea258f1774286a5b0198e908af31e2d..a75d963d85b223c99d933012e373c53e965e12de 100644 --- a/SAS/OTDB/include/OTDB/TreeMaintenance.h +++ b/SAS/OTDB/include/OTDB/TreeMaintenance.h @@ -190,6 +190,10 @@ public: nodeIDType topItem, const string& filename); + // Export all reported metadata from the given VIC tree + bool exportMetadata (treeIDType aTreeID, + const string& filename); + //# --- Finally some general tree maintenance --- // Delete a tree (of any kind) from the database. bool deleteTree(treeIDType aTreeID); diff --git a/SAS/OTDB/sql/create_OTDB.sql b/SAS/OTDB/sql/create_OTDB.sql index 019f16400d1de7fc814c920131d3ab762700b9a8..ac7c834daded9505e135327b6fdc9e9e99d56aae 100644 --- a/SAS/OTDB/sql/create_OTDB.sql +++ b/SAS/OTDB/sql/create_OTDB.sql @@ -64,6 +64,7 @@ \i getVHitemList_func.sql \i exportTree_func.sql \i exportResultTree_func.sql +\i exportMetadata_func.sql \i searchVHinPeriod_func.sql \i setSchedule_func.sql diff --git a/SAS/OTDB/sql/create_base_tables.sql b/SAS/OTDB/sql/create_base_tables.sql index 3e204e95382ba36cbd4fed039524a6fcd9842a3d..ad1f6f228d0fdb109ddb22b0a39070c268869b9c 100644 --- a/SAS/OTDB/sql/create_base_tables.sql +++ b/SAS/OTDB/sql/create_base_tables.sql @@ -252,6 +252,7 @@ INSERT INTO treestate VALUES (350, 'prescheduled'); INSERT INTO treestate VALUES (400, 'scheduled'); INSERT INTO treestate VALUES (500, 'queued'); INSERT INTO treestate VALUES (600, 'active'); +INSERT INTO treestate VALUES (900, 'completing'); INSERT INTO treestate VALUES (1000, 'finished'); INSERT INTO treestate VALUES (1100, 'aborted'); INSERT INTO treestate VALUES (1150, 'error'); diff --git a/SAS/OTDB/sql/exportMetadata_func.sql b/SAS/OTDB/sql/exportMetadata_func.sql new file mode 100644 index 0000000000000000000000000000000000000000..49c51727cbfc2460bb13290fc74bff9249bf40be --- /dev/null +++ b/SAS/OTDB/sql/exportMetadata_func.sql @@ -0,0 +1,57 @@ +-- +-- exportMetadata.sql: makes an usenet format export of all Metadata info from a tree +-- +-- Copyright (C) 2013 +-- ASTRON (Netherlands Foundation for Research in Astronomy) +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 2 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program; if not, write to the Free Software +-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +-- +-- $Id: exportTree_func.sql 20032 2012-02-07 07:10:34Z overeem $ +-- + +-- +-- exportMetadata (treeID) +-- +-- Makes a key-value list of all kvt values of a VIC tree +-- +-- Authorisation: no +-- +-- Tables: VICkvt read +-- +-- Types: none +-- +CREATE OR REPLACE FUNCTION exportMetadata(INT4) + RETURNS TEXT AS $$ + -- $Id: addComponentToVT_func.sql 19935 2012-01-25 09:06:14Z mol $ + DECLARE + vResult TEXT; + vRecord RECORD; + vTreeID ALIAS FOR $1; + + BEGIN + vResult := ''; + FOR vRecord IN + SELECT treeid,paramname,value,time + FROM VICkvt + WHERE treeID = vTreeID + ORDER BY paramname, time ASC + LOOP + vResult := vResult || vRecord.paramname || '=' || vRecord.value || chr(10); + END LOOP; + RETURN vResult; + END; +$$ LANGUAGE plpgsql; + diff --git a/SAS/OTDB/sql/getTreeGroup_func.sql b/SAS/OTDB/sql/getTreeGroup_func.sql index c5e8b287a1011f2e842b5252582e43569b46b4ea..d154c4a9682ee9c21575dff5424498abdb9b4bf9 100644 --- a/SAS/OTDB/sql/getTreeGroup_func.sql +++ b/SAS/OTDB/sql/getTreeGroup_func.sql @@ -49,6 +49,7 @@ CREATE OR REPLACE FUNCTION getTreeGroup(INT, INT) TSapproved CONSTANT INT2 := 300; TSscheduled CONSTANT INT2 := 400; TSqueued CONSTANT INT2 := 500; +-- TScompleting CONSTANT INT2 := 900; TSfinished CONSTANT INT2 := 1000; TThierarchy CONSTANT INT2 := 30; TCoperational CONSTANT INT2 := 3; @@ -71,18 +72,18 @@ CREATE OR REPLACE FUNCTION getTreeGroup(INT, INT) vQuery := vQuery || ' AND t.state > ' || TSscheduled; vQuery := vQuery || ' AND t.state < ' || TSfinished; ELSE - IF $1 = 3 THEN - vQuery := ' AND t.state >= ' || TSfinished; - vQuery := vQuery || ' AND t.stoptime > now()-interval ' || chr(39) || $2 || ' minutes' || chr(39); - vSortOrder := 't.stoptime, t.treeID'; - ELSE + IF $1 = 3 THEN + vQuery := ' AND t.state >= ' || TSfinished; + vQuery := vQuery || ' AND t.stoptime > now()-interval ' || chr(39) || $2 || ' minutes' || chr(39); + vSortOrder := 't.stoptime, t.treeID'; + ELSE IF $1 = 4 THEN vQuery := ' AND t.state >= ' || TSapproved; vQuery := vQuery || ' AND t.stoptime < now() '; vSortOrder := 't.treeID'; ELSE RAISE EXCEPTION 'groupType must be 0,1,2,3 or 4 not %', $1; - END IF; + END IF; END IF; END IF; END IF; diff --git a/SAS/OTDB/sql/getVTitem_func.sql b/SAS/OTDB/sql/getVTitem_func.sql index bf7a50c51dc50a481012e363e22d86a1b55e1382..aa9e3b7b0a0d962122337383c6d7df782cd7608b 100644 --- a/SAS/OTDB/sql/getVTitem_func.sql +++ b/SAS/OTDB/sql/getVTitem_func.sql @@ -76,7 +76,8 @@ CREATE OR REPLACE FUNCTION getVTitemRecursive(INT4, VARCHAR(150), INT4) FROM VICtemplate WHERE treeID = $1 AND name = vParentName - AND (index::VARCHAR(50) = vIndex OR index::VARCHAR(50) = vIndex2); + AND (index::VARCHAR(50) = vIndex OR index::VARCHAR(50) = vIndex2) + ORDER BY index; ELSE SELECT nodeid, parentid, @@ -91,8 +92,9 @@ CREATE OR REPLACE FUNCTION getVTitemRecursive(INT4, VARCHAR(150), INT4) FROM VICtemplate WHERE treeID = $1 AND name = vParentName - AND (index::VARCHAR(50) = vIndex OR index::VARCHAR(50) = vIndex2) - AND parentid = aParentID; + AND (index::VARCHAR(50) = vIndex OR index::VARCHAR(50) = vIndex2) + AND parentid = aParentID + ORDER BY index; END IF; IF FOUND AND NOT vChildName IS NULL THEN diff --git a/SAS/OTDB/sql/misc_func.sql b/SAS/OTDB/sql/misc_func.sql index ceb3f5327380dd732706d7e277af53f4827449b2..453140a226882c776ebaf366cbe567cadd165dc7 100644 --- a/SAS/OTDB/sql/misc_func.sql +++ b/SAS/OTDB/sql/misc_func.sql @@ -129,9 +129,9 @@ CREATE OR REPLACE FUNCTION VersionNrValue(VARCHAR(50)) vPatch INT4; BEGIN - vRelease := substring($1 from '([0-9]+)\.[0-9]+\.[0-9]+'); - vUpdate := substring($1 from '[0-9]+\.([0-9]+)\.[0-9]+'); - vPatch := substring($1 from '[0-9]+\.[0-9]+\.([0-9]+)'); + vRelease := substring($1 from E'([0-9]+)\.[0-9]+\.[0-9]+'); + vUpdate := substring($1 from E'[0-9]+\.([0-9]+)\.[0-9]+'); + vPatch := substring($1 from E'[0-9]+\.[0-9]+\.([0-9]+)'); RETURN vRelease * 10000 + (vUpdate%100)*100 + vPatch%100; END; diff --git a/SAS/OTDB/sql/setDescription_func.sql b/SAS/OTDB/sql/setDescription_func.sql index d9e9a597c57c648ab83f7be2ff56f4ae0969a856..2bbda61ffcfe71cfc5c2b0d1bb3817f13454980c 100644 --- a/SAS/OTDB/sql/setDescription_func.sql +++ b/SAS/OTDB/sql/setDescription_func.sql @@ -32,7 +32,7 @@ -- Types: none -- CREATE OR REPLACE FUNCTION setDescription(INT4, INT4, TEXT) - RETURNS BOOLEAN AS ' + RETURNS BOOLEAN AS $$ -- $Id$ DECLARE vFunction INT2 := 1; @@ -47,16 +47,17 @@ CREATE OR REPLACE FUNCTION setDescription(INT4, INT4, TEXT) SELECT isAuthorized(vAuthToken, $2, vFunction, 0) INTO vIsAuth; IF NOT vIsAuth THEN - RAISE EXCEPTION \'Not authorized.\'; + RAISE EXCEPTION 'Not authorized.'; RETURN FALSE; END IF; -- update the tree - vDescription := replace($3, \'\\\'\', \'\'); + vDescription := replace($3, E'\\\'', E''); + -- vDescription := replace($3, \'\\\'\', \'\'); UPDATE OTDBtree SET description = vDescription WHERE treeID = $2; RETURN TRUE; END; -' LANGUAGE plpgsql; +$$ LANGUAGE plpgsql; diff --git a/SAS/OTDB/sql/updateVTnode_func.sql b/SAS/OTDB/sql/updateVTnode_func.sql index b47ea4ffd95c10e2ef8693a489310541fa69f615..bd80cdafda3b252ededfa392e157e44a9dc0c631 100644 --- a/SAS/OTDB/sql/updateVTnode_func.sql +++ b/SAS/OTDB/sql/updateVTnode_func.sql @@ -35,7 +35,7 @@ -- Types: OTDBnode -- CREATE OR REPLACE FUNCTION updateVTnode(INT4, INT4, INT4, INT2, TEXT) - RETURNS BOOLEAN AS ' + RETURNS BOOLEAN AS $$ -- $Id$ DECLARE TTtemplate CONSTANT INT2 := 20; @@ -52,7 +52,7 @@ CREATE OR REPLACE FUNCTION updateVTnode(INT4, INT4, INT4, INT2, TEXT) SELECT isAuthorized(vAuthToken, $2, vFunction, 0) INTO vIsAuth; IF NOT vIsAuth THEN - RAISE EXCEPTION \'Not authorized\'; + RAISE EXCEPTION 'Not authorized'; RETURN FALSE; END IF; @@ -62,36 +62,36 @@ CREATE OR REPLACE FUNCTION updateVTnode(INT4, INT4, INT4, INT2, TEXT) FROM OTDBtree WHERE treeID = $2; IF NOT FOUND THEN - RAISE EXCEPTION \'Tree % does not exist\', $1; + RAISE EXCEPTION 'Tree % does not exist', $1; END IF; IF vTreeType = TTtemplate THEN -- get ParentID of node to duplicate - vLimits := replace($5, \'\\\'\', \'\'); + vLimits := replace($5, E'\\\'', E''); UPDATE VICtemplate SET instances = $4, limits = vLimits WHERE treeID = $2 AND nodeID = $3; IF NOT FOUND THEN - RAISE EXCEPTION \'Node % of template-tree could not be updated\', $3; + RAISE EXCEPTION 'Node % of template-tree could not be updated', $3; RETURN FALSE; END IF; ELSEIF vTreeType = TThierarchy THEN - vLimits := replace($5, \'\\\'\', \'\'); + vLimits := replace($5, E'\\\'', E''); UPDATE VIChierarchy - SET value = vLimits + SET value = vLimits WHERE treeID = $2 AND nodeID = $3; IF NOT FOUND THEN - RAISE EXCEPTION \'Node % of VIC-tree could not be updated\', $3; + RAISE EXCEPTION 'Node % of VIC-tree could not be updated', $3; RETURN FALSE; END IF; ELSE - RAISE EXCEPTION \'Nodes of PIC trees can not be updated\'; + RAISE EXCEPTION 'Nodes of PIC trees can not be updated'; END IF; RETURN TRUE; END; -' LANGUAGE plpgsql; +$$ LANGUAGE plpgsql; diff --git a/SAS/OTDB/src/TreeMaintenance.cc b/SAS/OTDB/src/TreeMaintenance.cc index 8445c95b2af593d818915d445839599cbd1083d9..9c44e093dff49e3ce5fb9e133a9b1dc01a5a1826 100644 --- a/SAS/OTDB/src/TreeMaintenance.cc +++ b/SAS/OTDB/src/TreeMaintenance.cc @@ -1023,6 +1023,49 @@ bool TreeMaintenance::exportResultTree (treeIDType aTreeID, return (false); } +// +// exportMetadata(treeID, filename): bool +// +// Export all Metadata of the given VIC tree +// +bool TreeMaintenance::exportMetadata (treeIDType aTreeID, + const string& filename) +{ + // Check connection + if (!itsConn->connect()) { + itsError = itsConn->errorMsg(); + return (false); + } + + LOG_TRACE_FLOW_STR("TM:exportMetadata(" << aTreeID << "," << filename << ")"); + + work xAction(*(itsConn->getConn()), "exportMetadata"); + try { + ofstream outFile; + outFile.open (filename.c_str()); + if (!outFile) { + LOG_ERROR_STR ("Cannot open exportfile: " << filename); + return (false); + } + + result res = xAction.exec("SELECT * from exportMetadata(" + + toString(aTreeID) + ")"); + // Get result + string params; + res[0]["exportmetadata"].to(params); + outFile << params; + outFile.close(); + return (true); + } + catch (std::exception& ex) { + itsError = string("Exception during exportMetadata:") + ex.what(); + LOG_FATAL(itsError); + return (false); + } + + return (false); +} + //# --- Finally some general tree maintenance --- // Delete a tree (of any kind) from the database. bool TreeMaintenance::deleteTree(treeIDType aTreeID) diff --git a/SAS/OTDB/test/CMakeLists.txt b/SAS/OTDB/test/CMakeLists.txt index 260c0ce9da6bae5405a0b7f3cc3ea69664b9ba77..688717095bdcbbba22f0a397d439d848546bedaf 100644 --- a/SAS/OTDB/test/CMakeLists.txt +++ b/SAS/OTDB/test/CMakeLists.txt @@ -11,5 +11,6 @@ lofar_add_test(tVICcomp tVICcomp.cc) lofar_add_test(tVTtree tVTtree.cc) lofar_add_test(tVHtree tVHtree.cc) lofar_add_test(tVHvalue tVHvalue.cc) +lofar_add_executable(tMetadata tMetadata.cc) lofar_add_test(tConnection tConnection.cc) lofar_add_test(tParamTypeConv tParamTypeConv.cc) diff --git a/SAS/OTDB/test/tCampaign.cc b/SAS/OTDB/test/tCampaign.cc index 09604f66eb9277a1ef424a037183b4b722d1bd5a..2fc62047d491452a4b023481126f2b9e6fe3af14 100644 --- a/SAS/OTDB/test/tCampaign.cc +++ b/SAS/OTDB/test/tCampaign.cc @@ -111,9 +111,12 @@ int main (int argc, char* argv[]) { showCampaignList(campList); ASSERTSTR(campList.size(),"No campaign list found"); - LOG_INFO("Adding 'my campaign'"); - CampaignInfo CI("my campaign", "campaign of me", "me", "no-one", "also me"); + LOG_INFO("Adding 'ruud's campaign'"); + CampaignInfo CI("ruud's campaign", "campaign of me", "me", "no-one", "also me"); + LOG_INFO_STR(CI); LOG_INFO_STR("new recordID = " << camp.saveCampaign(CI)); + LOG_INFO("AFTER SAVECAMPAIGN"); +#if 0 campList = camp.getCampaignList(); showCampaignList(campList); ASSERTSTR(campList.size(),"No campaign list found"); @@ -134,6 +137,7 @@ int main (int argc, char* argv[]) { showCampaignList(campList); ASSERTSTR(campList.size(),"No campaign list found"); +#endif #if 0 LOG_INFO("Trying to change the state of the tree to active(400)"); bool actionOK = tm.setTreeState(treeID, TSconv.get("active")); diff --git a/SAS/OTDB/test/tMetadata.cc b/SAS/OTDB/test/tMetadata.cc new file mode 100644 index 0000000000000000000000000000000000000000..1b0d6bca001898f97c3b203d05673d20894e2d86 --- /dev/null +++ b/SAS/OTDB/test/tMetadata.cc @@ -0,0 +1,81 @@ +//# tMetadata: test the maintenance actions on the VH trees +//# +//# Copyright (C) 2002-2004 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id: tVHtree.cc 23213 2012-12-07 13:00:09Z loose $ + +//# Always #include <lofar_config.h> first! +#include <lofar_config.h> + +//# Includes +#include <Common/LofarLogger.h> +#include <Common/lofar_fstream.h> +#include <Common/StringUtil.h> +#include <OTDB/TreeMaintenance.h> +#include <OTDB/OTDBtypes.h> +#include <OTDB/OTDBnode.h> +#include <OTDB/OTDBparam.h> +#include <libgen.h> // for basename +#include <cstring> + + +using namespace LOFAR; +using namespace LOFAR::OTDB; + +int main (int argc, char* argv[]) { + + INIT_LOGGER(basename(argv[0])); + LOG_INFO_STR("Starting " << argv[0]); + + if (argc != 3) { + cout << "Usage: tMetadata databasename VicTreeID " << endl; + return (1); + } + + // try to resolve the database name + string dbName(argv[1]); + string hostName("rs005.astron.nl"); + + // Open the database connection + OTDBconnection conn("paulus", "boskabouter", dbName, hostName); + + try { + + LOG_INFO("Trying to connect to the database"); + ASSERTSTR(conn.connect(), "Connnection failed"); + LOG_INFO_STR("Connection succesful: " << conn); + + LOG_INFO("Trying to construct a TreeMaintenance object"); + TreeMaintenance tm(&conn); + + int treeID (atoi(argv[2])); + string filename(formatString("MD%d.txt", treeID)); + LOG_INFO_STR("Creating metadatafile '" << filename << "'"); + tm.exportMetadata(treeID, filename); + + } + catch (std::exception& ex) { + LOG_FATAL_STR("Unexpected exception: " << ex.what()); + return (1); // return !0 on failure + } + + LOG_INFO_STR ("Terminated succesfully: " << argv[0]); + + return (0); // return 0 on succes +} diff --git a/SAS/OTDB/test/tVHvalue.in b/SAS/OTDB/test/tVHvalue.in index 4ba78ba1c182f9414fe8a813fc2e3659c646b0c7..e0b5a4d6cd5d8a874ee59718186824519e58ede2 100644 --- a/SAS/OTDB/test/tVHvalue.in +++ b/SAS/OTDB/test/tVHvalue.in @@ -1,7 +1,7 @@ -TopNode.Observation.Virt Telescope.Rec Group.GFLOPS=2.0 -TopNode.Observation.Virt Telescope.Rec Group.GFLOPS{31536000}=2.5 -TopNode.Observation.Virt Telescope.Beamformer.Angle{1003456789}=0 -TopNode.Observation.Virt Telescope.Beamformer.Angle{1033456789}=1 -TopNode.Observation.Virt Telescope.Beamformer.Angle{1063456789}=3 -TopNode.Observation.Virt Telescope.Beamformer.Angle{1093456789}=5 -TopNode.Observation.Virt Telescope.Beamformer.Angle{1123456789}=7 +TopNode.Observation.Virt Telescope[0].Rec Group.GFLOPS=2.0 +TopNode.Observation.Virt Telescope[0].Rec Group.GFLOPS{31536000}=2.5 +TopNode.Observation.Virt Telescope[0].Beamformer.Angle{1003456789}=0 +TopNode.Observation.Virt Telescope[0].Beamformer.Angle{1033456789}=1 +TopNode.Observation.Virt Telescope[0].Beamformer.Angle{1063456789}=3 +TopNode.Observation.Virt Telescope[0].Beamformer.Angle{1093456789}=5 +TopNode.Observation.Virt Telescope[0].Beamformer.Angle{1123456789}=7