/* Copyright (c) 2000,2001 RIPE NCC All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the author not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* ------------------------------------------------------------------------------- Module Header Filename : TTMDelayData.C Author : Rene Wilhelm Date : 13-OCT-2000 Revised : 20-JUN-2001, better object model, new methods Revised : 13-AUG-2001, TestBox has become a class Description : Implementation of TTMDelayData class (create chain of ROOT TTree files for specific measurement period) Language Version : C++ OSs Tested : Solaris 2.6 $Id: TTMDelayData.C,v 1.6 2003/05/16 13:57:41 ttraffic Exp $ ------------------------------------------------------------------------------- */ #include "TTMDelayData.h" #include "TTMStatistics.h" #include "TTMHistograms.h" #include "CSVdump.h" #include "utils.h" #include "Delay.h" #include "TFile.h" #include "TTree.h" #include #include #include #include static char const rcsid[] = "$Id: TTMDelayData.C,v 1.6 2003/05/16 13:57:41 ttraffic Exp $"; static char const *rcsid_p = rcsid; // Prevent g++ from optimizing out rcsid /* ------------------------------------------------------------------------------- Subroutine Header Purpose : TTMDelayData constructor Side Effects : initialize private data members Comments : ------------------------------------------------------------------------------- */ TTMDelayData::TTMDelayData() { chain = NULL; dataPath = NULL; sumTree = NULL; id2index = NULL; delayStats = NULL; delayHistos = NULL; csv = NULL; dataProcessed = 0; maxBoxId = 0; numIntervals = 0; endTime = 0; timePeriod = 0; } /* ------------------------------------------------------------------------------- Subroutine Header Purpose : TTMDelayData destructor Side Effects : deletes ROOT TChain + dynamically created datapath string Comments : ------------------------------------------------------------------------------- */ TTMDelayData::~TTMDelayData() { if (chain != NULL) delete chain; if (csv != NULL) delete[] csv; if (id2index != NULL) delete[] id2index; if (delayStats != NULL) delete[] delayStats; if (delayHistos != NULL) delete[] delayHistos; } /* ------------------------------------------------------------------------------- Subroutine Header Purpose : Set TTM Configuration for this data chain Input parameters : config - TestBoxConfig object, a list of all TestBoxes which were actively participating in the measurement network at the end time of the data chain Comments : also initializes internal bookkeeping parameters ------------------------------------------------------------------------------- */ void TTMDelayData::SetConfig(TTMOptions& opt, TestBoxConfig& conf, int numint) { tbConfig = &conf; progOptions = &opt; int oldNumBoxes = numBoxes; int oldNumIntervals = numIntervals; numIntervals = numint; // initialize data members from program options dataPath = progOptions->InputPath(); endTime = progOptions->EndTime(); outputDir = progOptions->OutputPath(); plotFormat = progOptions->PlotFormat(); storageType = progOptions->StorageType(); timePeriod = progOptions->TimePeriod(); ipv6Chain = (progOptions->IPversion() == 6); outputFile = progOptions->OutputPath(); struct stat statbuf; if (stat(outputFile, &statbuf) == 0) { // it exists if (S_ISDIR(statbuf.st_mode)) { // it is a directory, append default output file name outputFile = new char[strlen(outputDir) + 24]; sprintf(outputFile, "%s/summary.root", outputDir); } } else if (progOptions->Verbose()) { perror(outputFile); } // Determine the corresponding Id to TestBox translation table // since array indices run 0..arraySize-1, we have to allocate one // more element to allow direct indexing with testbox ids maxBoxId = tbConfig->GetMaxBoxId(); id2index = new int[maxBoxId+1]; // initialize to -1 ( = box not in active list) for (uint i=0; i <= tbConfig->GetMaxBoxId(); i++) { id2index[i] = -1; } nSources = tbConfig->GetNumEntries(); // fill the slots which correspond to active boxes and their targets // (essential when only one source box present in running config) // This whole mapping could probably be moved to the TestBox class // where a GetTargetIndex(targetId) method would return the // position of targetId in targetList array. // It would mean that delayStats and delayHistos arrays // in TTMDelayData class would be created and dimensioned // anew for each source box. For now we stick with this // less ideal "global" id2index mapping. [RW 20030113] sourceList = tbConfig->GetTBList(); int index = 0; for (int k=0; k < nSources; k++) { uint srcid = sourceList[k]->GetId(); if (srcid > tbConfig->GetMaxBoxId()) { cerr << progOptions->ProgramName() <<"ERROR: source id " << srcid << "out of range; expected 1-" << tbConfig->GetMaxBoxId() << endl; } else { if (id2index[srcid] < 0) { // not yet defined id2index[srcid] = index++; } } // now check this box' targets int ntargets; TestBox **target = sourceList[k]->GetTargets(ntargets); for (int j=0; j < ntargets; j++) { uint tgtid = target[j]->GetId(); if (tgtid > tbConfig->GetMaxBoxId()) { cerr << progOptions->ProgramName() <<"ERROR: target id " << tgtid << "out of range; expected 1-" << tbConfig->GetMaxBoxId() << endl; } else { if (id2index[tgtid] < 0) { // not yet defined id2index[tgtid] = index++; } } } } numBoxes = index; if (progOptions->Verbose()) { cout << "Found " << numBoxes << " distinct boxes in src/tgt config" << endl; } if (((oldNumBoxes != numBoxes) && (oldNumBoxes != 0)) || ((oldNumIntervals != numIntervals) && (oldNumIntervals != 0))) { // have to redo all analysis with different interval settings // delete current array with TTMStatistics if (delayStats != NULL) { delete[] delayStats; delayStats = NULL; } } } /* ------------------------------------------------------------------------------- Subroutine Header Purpose : create (new) ROOT TTree chain Comments : one chain is created of ALL specified sources : existing TChain of this object will be deleted Future : add code to Reset/Clear histograms for Delay Plots ------------------------------------------------------------------------------- */ void TTMDelayData::Chain(TestBox* source) { #define BUFLENGTH 15 char charbuf[BUFLENGTH]; // small buffer for temporary storage char *fullpath; struct tm *timestruct; time_t starttime, timestamp; int pathlen; // length of the *path string argument int nfiles; // number of files to chain // for all targets ... // initalize data members // dispose of old ROOT chain if it still exists if (chain != NULL) { delete chain; } // create ROOT chain chain = new TChain("tree"); // copy path pathlen = strlen(dataPath); fullpath = new char[pathlen+100]; // leaves 80 char for date + boxname strcpy(fullpath, dataPath); starttime = endTime - timePeriod; if (endTime % SECONDSPERDAY == 0) { // endtime on day boundary, push it forward a little // to get the correct data file timestamp = starttime + 5; } else { timestamp = starttime; } // since endtime is in time_t (seconds since 1/1/1970) format // we can determine day numbers (since 1/1/1970) by simple integer // division; number of files to chain is difference between // end and start day. Subtract and add 5 seconds respectively // to avoid rounding errors int endday = (endTime-5) / SECONDSPERDAY; int startday = (endTime-timePeriod+5) / SECONDSPERDAY; nfiles = (endday - startday) + 1; for (int i = nfiles; i>0; i--) { fullpath[pathlen] = '\0'; // truncate previous path timestruct = gmtime(×tamp); if (storageType == 'H') { // append appropriate // strftime(charbuf, BUFLENGTH, "/%Y/%m/%d", timestruct); strcat(fullpath, charbuf); } strcat(fullpath, "/"); if (ipv6Chain) { strcat(fullpath, "IPv6."); } // construct full filename from source name and timestruct strftime(charbuf, BUFLENGTH, ".%Y%m%d.root", timestruct); strcat(fullpath, source->GetName()); strcat(fullpath, charbuf); if (progOptions->Verbose()) { cout << "fullpath: " << fullpath << endl; } // check if file exists before adding struct stat statbuf; if (stat(fullpath, &statbuf) < 0) { perror(fullpath); } else { chain->Add(fullpath); } timestamp += SECONDSPERDAY; // increase timestamp by a day } } /* ------------------------------------------------------------------------------- Subroutine Header Purpose : Process packets in TTM Data chain Update TTM Statistics object(s) Side Effects : Comments : Target list has been set by SetTargets() method TODO : Also fill histograms for later plotting ------------------------------------------------------------------------------- */ int TTMDelayData::ProcessPackets(TestBox* source, ttmdata type) { int index; time_t starttime = endTime - timePeriod; if (chain == NULL) { cerr << "TTMDelayData::ProcessPackets : " << "No data chain defined, please call " << "Chain() method first" << endl; return(0); } InitStorage(source, type); // loop over all data Delay *delay = new Delay(); chain->SetBranchAddress("delay", &delay); Int_t nMeasurements = (Int_t) chain->GetEntries(); nProcessed = 0; for (Int_t eventNo=0; eventNo < nMeasurements; eventNo++) { if (chain->GetEvent(eventNo) < 0) { // problem reading this "event"? continue; } Int_t targetId = delay->GetTargetId(); if ( (targetId <= (int)maxBoxId) && (targetId > 0)) { // valid targetId if ((index = id2index[targetId]) < 0) { continue; // out of range, no target, skip } // part of requested targets list if (delay->GetSourceId() == (Int_t) source->GetId()) { // packet from requested source if (type == delaystats) { delayStats[index].Update(delay, starttime); } else if (type == delayhist) { delayHistos[index].Fill(delay); } else if (type == csvdump) { csv[index].Dump(delay); } nProcessed++; } } } if (progOptions->Verbose()) { cout << "Source Box " << source->GetId() << ": " << nProcessed << " measurements processed" << endl; } dataProcessed = 1; return(nProcessed); } /* ------------------------------------------------------------------------------- Subroutine Header Purpose : Initialize storage for data processing Create Arrays, Histograms etc. Side Effects : ------------------------------------------------------------------------------- */ void TTMDelayData::InitStorage(TestBox* source, ttmdata type) { int i; // Create Statistics, Histogram or CSV objects for all boxes // in TB config (the superset of all selected sources // and their targets); if (type == delaystats) { if (delayStats == NULL) { // create new objects delayStats = new TTMStatistics[numBoxes]; for (i=0; iGetTargets(ntargets); if (delayHistos != NULL) { // delete old Histograms delete[] delayHistos; } delayHistos = new TTMHistograms[numBoxes]; for (i=0; iGetId()]; delayHistos[k].SetConfig(source, i, progOptions); } } else if (type == csvdump) { // CSV Output int ntargets; TestBox** target = source->GetTargets(ntargets); if (csv != NULL) { // delete old Histograms delete[] csv; } csv = new CSVdump[numBoxes]; for (i=0; iGetId()]; char *prefix = ""; if (ipv6Chain) { prefix = "IPv6."; } csv[k].Open(outputDir, source->GetShortName(), target[i]->GetShortName(), prefix, delays); } } } /* ------------------------------------------------------------------------------- Subroutine Header Purpose : Create summary ROOT file for this DelayData set Descpription : For each source box chain the data files for configured dates and output the statistics to the summary TTree file ------------------------------------------------------------------------------- */ void TTMDelayData::Summarize() { // Create ROOT TTree Int_t split = 1; Int_t bsize = 64000; TFile *sumfile = new TFile (outputFile, "RECREATE"); sumTree = new TTree ("TTMSummary", "TTM DelaySummary"); if (progOptions->Verbose()) { cout << "Opened " << outputFile << " for Summary tree" << endl; } summary = new DelaySummary(); sumTree->Branch ("summary", "DelaySummary", &summary, bsize, split); for (int i=0; iClose(); } /* ------------------------------------------------------------------------------- Subroutine Header Purpose : Create delay Plots for this DelayData set Descpription : For each source box chain the data files for configured dates and create the plots in the output directory ------------------------------------------------------------------------------- */ void TTMDelayData::Plot() { for (int i=0; iGetTargets(ntargets); int nfills = 0; for (int i=0; iGetId(); int k = id2index[targetId]; for (int j=0; j<=numIntervals;j++) { summary = new DelaySummary(); summary->SetSourceId (source->GetId()); summary->SetTargetId (targetId); summary->SetBinNo(j); summary->SetNumDays ((timePeriod+5)/SECONDSPERDAY); Double_t binsize, timelow; if (j==0) { // interval 0 summarizes whole period binsize = timePeriod; timelow = endTime-timePeriod; } else { // other intervals cover specific part of day // It's hard to find proper value of binsize // because each bin is in fact a collection // of smaller bins. // for such a bin is even harder // to define. For now we stick with these. binsize = SECONDSPERDAY/numIntervals; timelow = endTime - timePeriod + (j - 1) * binsize; } summary->SetBinSize (binsize); summary->SetTimeLow (timelow); summary->SetNumEntries(delayStats[k].GetNumPacketsValid(j)); summary->SetLevel025(delayStats[k].GetDelayPerc(j, 2.5)); summary->SetMedian (delayStats[k].GetDelayPerc(j, 50)); summary->SetLevel975(delayStats[k].GetDelayPerc(j, 97.5)); summary->SetLoss (delayStats[k].GetLossRate(j)); sumTree->Fill(); // put the above object in the tree nfills++; delete summary; // can't be reused by ROOT } } hfile->Write (); /* cout << "Source Box : " << source->GetId() <GetId(); int k = id2index[targetId]; cout <<"Target: " << target[k].GetId() <GetTargets(ntargets); for (int i=0; iGetId()]; delayHistos[k].Plot(); } }