/******************************************************************* * * DESCRIPTION: Draws the content of the log * * AUTHOR: Amir Barylko & Jorge Beyoglonian * Version 2: Daniel Rodriguez * Version 3: Gabriel Wainer * Version 4: Alejandro Troccoli (Parallel ) * * EMAIL: mailto://amir@dc.uba.ar * mailto://jbeyoglo@dc.uba.ar * mailto://drodrigu@dc.uba.ar * mailto://gabrielw@dc.uba.ar * * DATE: 27/06/1998 * DATE: 28/04/1999 (v2) * *******************************************************************/ // ** include files **// // #include #include #include #include "ini.h" // Class Ini #include "VTime.hh" // Class VTime #include "logparser.h" #include "cellstate.h" #include "JackyDebugStream.h" // ** main ** // VTime getNextMsgLine( istream& file, const string& modelName, char* buffer ); void printState( const CellState &state, const VTime &time ) { if (!Impresion::Default.FlatLog()) cout << "Time: " << time.asString() << endl ; else cout << endl; state.print(cout, '?'); } void showHelp() { cout << "drawlog -[?hmtclwp0]\n\n"; cout << "where:\n"; cout << "\t?\tShow this message\n"; cout << "\th\tShow this message\n"; cout << "\tm\tSpecify file containig the model (.ma)\n"; cout << "\tt\tInitial time\n"; cout << "\ti\tTime interval (After the initial time, draw after every time interval)\n"; cout << "\tc\tSpecify the coupled model to draw\n"; cout << "\tl\tLog file containing the output generated by SIMU\n"; cout << "\tw\tWidth (in characters) used to represent numeric values\n"; cout << "\tp\tPrecision used to represent numeric values (in characters)\n"; cout << "\t0\tDon't print the zero value\n"; cout << "\tf\tOnly cell values on a specified slice in 3D models\n"; exit(1); } /*Jacky: ** This function splits a line of string in the following format: ** (x0,x1,...,xn) = value1 <-- a typical line in the VAL file ** to get the cell position as "(x0,x1,...,xn)", and Real value as "value1" */ int splitLine( char *line, char *pos, char *value) { char *posi = pos, *val = value; // Primero leo POS while ( *line != 0 && *line != '=' ) { if (*line != ' '){ *posi = *line; posi++; } line++; } if (*line == 0) return 0; *posi = 0; line++; // Ahora leo el value while ( *line != 0 ) { if (*line != ' '){ *val = *line; val++; } line++; } *val = 0; if (pos[0] == '(' && strlen(value) > 0) return 1; return 0; } ////////////////////////////////////////////////////////////////////////////// // MAIN - DRAWLOG ////////////////////////////////////////////////////////////////////////////// int main( int argc, char *argv[] ) { #ifdef JACKY_DRAWLOG //create another fstream for debugging the drawlog utility string drawlog_debugfile = "drawlog_debug.output"; fstream drawlog_os(drawlog_debugfile.c_str(), ios::out); #endif try { VTime initial( VTime::Zero ); VTime timeInterval(VTime::InvalidTime); string modelName, iniName, logName("-"), strWidth(""), strPrec(""), strPlane(""); // parameter parsing while( --argc ) if( *argv[ argc ] == '-' ) switch( argv[ argc ][ 1 ] ) { case 'm': /* file .ma */ iniName = lowerCase(argv[ argc ] + 2); #ifdef JACKY_DRAWLOG drawlog_os << "iniName (MA file -m) = " << iniName << endl << flush; #endif break; case 't': /* intital time */ initial = argv[ argc ] + 2 ; #ifdef JACKY_DRAWLOG drawlog_os << "initial (initial time -t) = " << initial.asString() << endl << flush; #endif break; case 'i': /* time interval */ timeInterval = argv[ argc ] + 2; #ifdef JACKY_DRAWLOG drawlog_os << "timeInterval (-i) = " << timeInterval.asString() << endl << flush; #endif break; case 'c': /* coupled */ // Jacky Note: // Since all group names / definition names parsed by Ini will be in lower case, // we need to get the modelName in lower case to do a search in the Ini file! modelName = lowerCase(argv[ argc ] + 2); #ifdef JACKY_DRAWLOG drawlog_os << "modelName (-c) = " << modelName << endl << flush; #endif break; case 'l': /* log filename */ logName = argv[ argc ] + 2 ; #ifdef JACKY_DRAWLOG drawlog_os << "logName (-l) = " << logName << endl << flush; #endif break; case 'w': /* Set width */ strWidth = argv [ argc ] + 2; Impresion::Default.Width( str2Int(strWidth) ); #ifdef JACKY_DRAWLOG drawlog_os << "width (-w) = " << strWidth << endl << flush; #endif break; case 'p': /* Set precision */ strPrec = argv [ argc ] + 2; Impresion::Default.Precision ( str2Int(strPrec) ); #ifdef JACKY_DRAWLOG drawlog_os << "precision (-p) = " << strPrec << endl << flush; #endif break; case '0': /* Don't print zero */ Impresion::Default.PrintZero(false); #ifdef JACKY_DRAWLOG drawlog_os << "do not printZero (-0) is set!" << endl << flush; #endif break; case '?': case 'h': showHelp(); break; case 'f': /* Only cell values on 3D models */ Impresion::Default.FlatLog(true); strPlane = argv [ argc ] + 2; Impresion::Default.FlatLogPlane( str2Int(strPlane) ); #ifdef JACKY_DRAWLOG drawlog_os << "plane (-f) = " << strPlane << endl << flush; #endif break; default: cout << "Warning... invalid parameter " << argv[ argc ] << "!" << endl ; showHelp(); } else cout << "Warning... invalid parameter " << argv[ argc ] << "!" << endl ; // parameter validation if( iniName == "" || modelName == "" ) { cout << "Drawlog - Parallel Version" << endl; cout << "Usage: " << argv[ 0 ] << " -mfile.ma -cCoupledCellName [ -tInitialtime ] \n\t -lmessage.log [ -wWidth ] [ -pPrecision ] [ -0 ]" << endl; return 1 ; } Ini iniFile ; Ini modelsLogFiles; iniFile.parse( iniName ) ; //Jacky: parse the MA file //If reading from files if ( logName != "-") modelsLogFiles.parse( logName ); //Jacky: parse the LOG file // dimension nTupla nt; register unsigned cols = 0, rows = 0; if (iniFile.exists( modelName, "width" )) { //Jacky: if the MA file defines "width" & "height" for the coupled model cols = str2Int( iniFile.definition( modelName, "width" ).front() ); rows = str2Int( iniFile.definition( modelName, "height" ).front() ); nt.add(rows, cols); } else if (iniFile.exists( modelName, "dim" )) { //Jacky: if the MA file defines "dim" for the coupled model Ini::IdList dimension = iniFile.definition( modelName, "dim" ); CellPosition cp( iniFile.join(dimension) ); nt = cp; cols = nt.get(DIM_WIDTH); rows = nt.get(DIM_HEIGHT); } if ( nt.contains(0) ) MASSERT_ERR("Attemp to draw a model where a component has dimension of 0"); CellState state( nt ) ; //Jacky: create a CellState of size given in MA ////////////////////////////////////////////////////////////// // default initial value /*Jacky: ** Initialvalue : [Real | ?] ** ** Defines the default initial value for each cell. ** This has the least precedence, if any of the following definition is given, ** the cell's value will be overwitten. */ string initialValue( iniFile.definition( modelName, "initialvalue" ).front() ) ; Real value ( str2Real( initialValue ) ); //Jacky: default initial value CellPosition counter( nt.dimension(), 0); register bool overflow = false; //Jacky: assign the default initial value to all cells in the cell space while (!overflow) { state[ counter ] = value; overflow = counter.next( nt ); } ///////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////// // loading the initial state ///////////////////////////////////////////////////////////// /*Jacky: ** InitialRow : row[i] value[1] ... value[width] ** ** Defines the initial value for all the cells in row i. Values can be any ** member of the set {realNumber, ?}. ** Each value in the list must be separated by a " " space. ** Can only be used for 2-D models! */ if( iniFile.exists( modelName, "initialrow" ) ) // The dim = 2 { const Ini::IdList &values( iniFile.definition( modelName, "initialrow" ) ) ; register unsigned row = 0; Ini::IdList::const_iterator cursor = values.begin(); while (cursor != values.end() ) { // el primer valor es la fila //Jacky: get the row number for the values row = str2Int( (*cursor) ) ; MASSERTMSG( row <= rows-1, "The number of row for initialRowValue is out of range. It's " + int2Str(row) + " and must be in [ 0, " + int2Str( rows - 1 ) + "]" ); cursor++ ; // Los siguientes elementos son la descripcion de la fila register unsigned col = 0; while ( col < cols ) { MASSERTMSG( cursor != values.end(), "Insuficient data for initialRowValue. Last row with " + int2Str(col) + " elements. Note: May be a middle row definition with less elements."); string rowVal( (*cursor) ) ; nTupla nt3; nt3.add(row, col); //Jacky: assign the value to cell(row, col) in the CellState state[ nt3 ] = str2Real( rowVal ) ; col++ ; cursor++ ; } } } /*Jacky: ** InitialRowValue : row[i] value[1]...value[width] ** ** Defines the initial value for all the cells in row i. All values are single ** digit values in the set {?, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ** No spaces are allowed between digits as it is interpreted as a single string. ** Can only be used for 2-D models! */ if( iniFile.exists( modelName, "initialRowValue" ) ) // The dim = 2 { const Ini::IdList &rowsList( iniFile.definition(modelName, "initialRowValue" ) ) ; register unsigned row = 0; Real val; for ( Ini::IdList::const_iterator cursor = rowsList.begin(); cursor != rowsList.end(); cursor++ ) { // the first value is the row number row = str2Int( (*cursor) ) ; MASSERTMSG( row < rows, "The number of row for initialRowValue is out of range. It's " + int2Str(row) + " and must be in [ 0, " + int2Str( rows-1 ) + "]" ) ; cursor++ ; MASSERTMSG( cursor != rowsList.end(), "Invalid initial row value for initialRowValue (must be a pair rowNumber rowValues)!" ); // the second is the description of the row string rowVal( *cursor ) ; MASSERTMSG( rowVal.size() == cols, "The size of the rows for the initial values of the CoupledCell must be equal to the width value !" ); register unsigned col = 0; for (string::iterator rowCurs = rowVal.begin(); rowCurs != rowVal.end(); rowCurs++ ) { if (*rowCurs >= '0' && *rowCurs <= '9') val.value( *rowCurs - '0'); else val = Real::tundef; nTupla nt4; nt4.add(row, col); state[ nt4 ] = val; col++; } } } /*Jacky: ** InitialCellsValue : fileName ** ** Defines the filename for the file that contains a list of initial value for ** cells in the model. This has higher precedence than "InitialRow" and "Initial ** RowValue" as it is analyzed here! ** This can be used with any size of cellular models. ** VAL File Formats: ** Each line of the file defines a value for a different cell: ** (x0,x1,...,xn) = value1 ** ... ... ** (y0,y1,...,yn) = valuem ** Note: if no value is defined in this file, then the value defined by the ** parameter "InitialValue" will be used. */ if( iniFile.exists( modelName, "initialCellsValue" ) ) { string fileName( iniFile.definition( modelName, "initialCellsValue" ).front() ); FILE *fileIn; char line[250], pos[200], value[50]; fileIn = fopen( trimSpaces(fileName).c_str(), "r" ); MASSERTMSG( fileIn != NULL, "Can't open the file '" + fileName + "' defined by the initialCellsValue clause"); while (!feof(fileIn)) { fgets(line, 255, fileIn); //Jacky: split the line in VAL file into 2 part: string for nTupla, // and the value of the cell if (line != NULL & splitLine(line, pos, value)){ CellPosition cp2(pos); state[ cp2 ] = str2Real(value); } } fclose( fileIn ); } /*Jacky: ** InitialMapValue : fileName ** ** Defines the filename for the file that contains a map of initial value for ** cells in the model. ** MAP File Formats: ** Each line of the file defines a value for a cell starting from (0,0,...,0): ** value1 ** ... ** valuem ** For a 3-D model of size (2,3,2), the values will ba asigned in the following ** order: ** (0,0,0)(0,0,1)(0,1,0)(0,1,1)(0,2,0)(0,2,1)...(1,2,0)(1,2,1) ** Note: if there are not enough values in the file for all the cells in the ** model, the simulation will be aborted!!! ** if there are more values than cells, the remaining values will be ** ignored!!! ** Note: toMap tool creates a MAP file from a VAL file */ if( iniFile.exists( modelName, "initialMapValue" ) ) { string fileName( iniFile.definition( modelName, "initialMapValue" ).front() ); FILE *fileIn; char line[250]; fileIn = fopen( trimSpaces(fileName).c_str(), "r" ); MASSERTMSG( fileIn != NULL, "Can't open the file '" + fileName + "' defined by the initialMapValue clause"); CellPosition counter( nt.dimension(), 0 ); register bool overflow = false; while (!overflow) { MASSERTMSG( !feof( fileIn ) && fgets(line, 255, fileIn), "Insuficient data in file specified with InitialMapValue"); state[ counter ] = str2Real(line); overflow = counter.next( nt ); } fclose( fileIn ); } ////////////////////////////////////////////////////////////// // Now, the initial state is already loaded. ////////////////////////////////////////////////////////////// /************************Jacky: Start to read from the LOG file!*********************/ ////////////////////////////////////////////////////////////// // Open the logfiles and load the first lines of each ////////////////////////////////////////////////////////////// VTime currentTime( VTime::Inf ), lastTime( initial ) ; //Jacky: an array of ptrs to istream // Each ptr points to a log file istream **logStreams ; //Jacky: an array of VTime VTime *fileTimes; //Jacky: an array of char* (string) lines char **lines; //Jacky: "filecount" is the total number of all Log files // "filecounter" is the index within the range of [0, filecount-1] int filecounter, filecount; if ( logName != "-" ) { //Jacky: //In the given log file, there must be a [logfiles] catagory, //which contains an entry that is the name of the coupled model //e.g. in log file "lifeext.log" // //[logfiles] //ParallelRoot : lifeext.log00 //top : lifeext.log11 //life : lifeext.log01 <-- life is the name of the coupled model //life(0,0) : lifeext.log02 //life(0,1) : lifeext.log03 //life(0,2) : lifeext.log04 //life(1,0) : lifeext.log05 //life(1,1) : lifeext.log06 //life(1,2) : lifeext.log07 //life(2,0) : lifeext.log08 //life(2,1) : lifeext.log09 //life(2,2) : lifeext.log10 //ParallelNodeCoordinator : lifeext.log12 //ParallelFlatCoordinator : lifeext.log13 #ifndef JACKY_LOG_FILE //------ Previous version relies on the log files for the coupled model ----------------- MASSERT(modelsLogFiles.exists( "logfiles" , modelName ) ); //Jacky Note: This will only get "lifeext.log01"!!! //definition(groupName, definitionName) gets the list of IDs for that DEF in the given group. //Here, group = "logfiles", def = "life" (i.e. name of the coupled model) //Thus, we only get the logfile name for the coupled model const Ini::IdList &files(modelsLogFiles.definition("logfiles", modelName)); #else //------ Current version relies on the log files for the FCs/NCs ------------------------------------------ #ifndef JACKY_UNIQUE_LOG //[2006-04-26] ================================================================= //Jacky Note: //if JACKY_UNIQUE_LOG is not defined, we rely on the log files created for the FCs MASSERT(modelsLogFiles.exists( "logfiles" , "ParallelFlatCoordinator" ) ); //definition(groupName, definitionName) gets the list of IDs for that DEF in the given group. //Here, group = "logfiles", def = "ParallelFlatCoordinator" //Thus, we only get all of the logfile names for the FCs const Ini::IdList &files( modelsLogFiles.definition("logfiles", "ParallelFlatCoordinator") ); #else //if JACKY_UNIQUE_LOG [2006-04-26] ================================================================ //Jacky Note: //if JACKY_UNIQUE_LOG is not defined, we rely on the log files created for the FCs/NCs //The identifier used here is "LogFileNames" MASSERT(modelsLogFiles.exists( "logfiles" , "LogFileNames" ) ); //definition(groupName, definitionName) gets the list of IDs for that DEF in the given group. //Here, group = "logfiles", def = "LogFileNames" //Thus, we only get all of the logfile names for the FCs/NCs const Ini::IdList &files( modelsLogFiles.definition("logfiles", "LogFileNames") ); #endif //end JACKY_UNIQUE_LOG [2006-04-26] ============================================================== #endif // end JACKY_LOG_FILE #ifdef JACKY_DRAWLOG Ini::IdList::const_iterator file_it = files.begin(); int logfile_index = 0; drawlog_os << "[drawlog] Total log file: " << files.size() << endl; for(; file_it != files.end(); file_it++){ drawlog_os << "[drawlog] logfile[" << logfile_index << "] = " << *file_it << endl; logfile_index++; } //Jacky Note: // Previously, we read the log file for the coupled model, it records // all Y messages received from the cells in the cell space. // Now, the cell space is distributed among multiple machines, it is // the log files for the FCs that record these Y messges. Therefore, // we have to read from these log files (log files for all of the FCs) // to draw the cell space! #endif // end JACKY_DRAWLOG filecount = files.size(); //an array of istream ptr to read the log files logStreams = new istream*[filecount]; //an array of strings, corresponding to current lines in these log files lines= new char*[filecount]; //VTimes for these log files fileTimes = new VTime[filecount]; Ini::IdList::const_iterator cursor = files.begin(); for ( filecounter = 0; cursor != files.end() ; cursor++, filecounter++ ) { logStreams[filecounter] = new fstream((*cursor).c_str(), ios::in); lines[filecounter] = new char[2048]; lines[filecounter][0] = '\0'; fileTimes[filecounter] = getNextMsgLine(*(logStreams[filecounter]), modelName, lines[filecounter]); if ( fileTimes[filecounter] < currentTime ) currentTime = fileTimes[filecounter]; } } else { filecount = 1; logStreams = new istream*[1]; lines = new char* [1]; fileTimes = new VTime[1]; logStreams[0] = new fstream("/dev/stdin", ios::in); lines[0] = new char[2048]; lines[0][0] = '\0'; fileTimes[0] = getNextMsgLine(*(logStreams[0]), modelName, lines[0]); if ( fileTimes[0] < currentTime ) currentTime = fileTimes[0]; } ////////////////////////////////////////////////////////////// // All the files are now open ////////////////////////////////////////////////////////////// bool val = false ; int frameCount(1) ; CellPosition nt5; do { //Jacky: while loop iterates over all log files, consume all Y messages with time <= lastTime // i.e. set values of the corresponding cells in the cell space according to the Y messages while( currentTime <= lastTime ) { VTime nextTime(VTime::Inf); //Jacky: loop over all log files for (filecounter = 0; filecounter < filecount; filecounter++) { //Jacky: if a log file contains Y message at the currentTime if (fileTimes[filecounter] == currentTime) { val = parseLine( lines[filecounter], currentTime, modelName, nt5, value ) ; //Todas las lineas deberian ser validas MASSERT(val); //Jacky: set the corresponding cell's value in the "CellState" based on the // value of the Y message state[ nt5 ] = value ; //Jacky: advance the recorded time for the log file to the time of the next // Y message fileTimes[filecounter] = getNextMsgLine(*(logStreams[filecounter]), modelName, lines[filecounter]); } //Jacky: nextTime is the MIN time among the next Y messages in all log files if ( fileTimes[filecounter] < nextTime ) nextTime = fileTimes[filecounter]; }//for //El nextTime deberia ser mayor al currentTime MASSERT( currentTime <= nextTime); //Jacky: now advance currentTime to the MIN time among the next Y messages in all log files currentTime = nextTime; } //end while //Jacky: At this point, all Y message with time <= lastTime have been consumed // and the values of these y messagea have been set to the corresponding cells // in the cell space (CellState) if (!Impresion::Default.FlatLog()) cout << "Line : " << frameCount << " - " ; ++frameCount; printState( state, lastTime ) ; // Jacky: print the current frame! //Jacky: advance the lastTime -> // 1) if -i option is not given: lastTime is advanced to the currentTime, which is the // MIN time among the next Y messages in all log files // this will show ALL changes of cell states in the cell space // 2) if -i option is given: lastTime will advance by the given time interval. That is, // the time interval between any two consecutive frames is the // time given by -i option if ( timeInterval == VTime::InvalidTime ) { lastTime = currentTime ; } else { //Jacky: currentTime has already been advanced to the NEXT min time, we should not // advance the lastTime based on the currentTime here!!! //lastTime = currentTime + timeInterval; lastTime = lastTime + timeInterval; } } while( currentTime != VTime::Inf ) ; for( filecounter = 0; filecounter < filecount; filecounter++) { delete logStreams[filecounter]; delete lines[filecounter]; } delete logStreams; delete lines; delete fileTimes; } catch( MException &e ) { e.print(cerr); } catch( ... ) { cerr << "Unknown exception! " << endl ; } } /********************************************************************** *getNextMsgLine: Stores in buffer the next valid msg line and returns the *time of the message. If no valid line is found, it returns VTime::Inf ***********************************************************************/ VTime getNextMsgLine( istream& file, const string& modelName, char* buffer ) { bool valid; VTime time; CellPosition cellPos; Real value; valid = false; while ( !valid && file.good() && !file.eof() ) { file.getline( buffer, 2048 ); valid = parseLine( buffer, time, modelName, cellPos, value ) ; } if ( !valid ) time = VTime::Inf; return time; }