// RSTP Client Daemon - For archiving data // Basic client begin: 10 August 2005 // Archiving modifications begin: 21 September 2005 // Initial Release: 6 May 2007 // Current Version Release: 9 June 2007 // Copyright (C) 2007 Nicholas Luther & Aaron Heise // License: GPL // // archcli.cpp // compile with: // g++ -o archcli archcli.cpp -lradar_stp // #include #include #include using namespace libradar_stp; #include #include #include #include //#include using namespace std; #include #include #include #include #include #include #include bool quitnow = false; string filter_file; string on_data_cmd; string data_folder; Filter filter; RSTPClientMCIf client; string pad_zeros(string, int); string remove_extraneous_chars(string); void datacb ( void* ref, const string& stream, const string& data ); string int_to_string( unsigned int n ); bool file_exists(const string& filename); string check_filename(const string& prefix, const string& suffix); void catchsig ( int s ) { if ( s == 13 ) // SIGPIPE isn't a very good reason to quit! return; quitnow = true; } int main ( int argc, char** argv ) { string conf_file("rstpconf.txt"); bool daemonize = false; bool have_args = false; data_folder = "."; // parse cmd line for ( int argi = 1; argi < argc; ++argi ) { string arg( argv[argi] ); if ( argi + 1 < argc ) { if ( arg == "-r" ) { conf_file = argv[argi+1]; have_args = true; } else if ( arg == "-c" ) { on_data_cmd = argv[argi+1]; have_args = true; } else if ( arg == "-f" ) { filter_file = argv[argi+1]; have_args = true; } else if ( arg == "-a" ) { data_folder = argv[argi+1]; have_args = true; } } if ( arg == "-v" || arg == "-h" || arg == "--help" || arg == "--version" ) { cout << argv[0] << " usage:" << endl << argv[0] << " [-r rstpconf] [-f filter] [-c cmd] [-d] [-a folder]" << endl << endl << "Linked to libradar_stp version " << libradar_stp_version() << endl << endl; return 0; } else if ( arg == "-d" ) { daemonize = true; have_args = true; } } // initialize filter if ( !filter_file.empty() ) { filter.setFile( filter_file ); } if ( !have_args ) { cerr << argv[0] << " starting with default arguments." << endl; cerr << "Send SIGINT to stop." << endl; cerr << "Use --help to figure out how to run this." << endl; } signal( 2, catchsig ); signal( 15, catchsig ); signal( 13, catchsig ); if ( daemonize ) { daemon( 0, 0 ); } //RSTPClientIf c; client.setDataCallback( datacb, 0 ); client.loadFile( conf_file ); client.start(); while ( !quitnow ) { sleep (1); } client.quit(); return 0; } void datacb ( void* ref, const string& stream, const string& data ) { #ifdef DEBUG cerr << "received data, stream " << stream << ", length: " << data.length() << endl; #endif if ( !filter_file.empty() && !filter.check( data, 0 ) ) { return; } #ifdef DEBUG cerr << "passed filter check, pushing to archive" << endl; #endif // find headers int loc1 = data.find(" "); if ( loc1 == string::npos ) return; int loc2 = data.find(" ", loc1+1); if ( loc2 == string::npos ) return; int loc3 = data.find("\r\r\n", loc2+1); if ( loc3 == string::npos ) return; if ( loc1 < 10 && loc2 < 15 && loc3 < 50 ) { // looks like valid WMO string wmoid = data.substr( 0, loc1 ); string icao = data.substr( loc1+1, loc2-loc1-1 ); string awips = "NOAWIPS"; int loc4 = data.find("\r\r\n", loc3+3); int data_start = loc3 + 3; if ( loc4 != string::npos && loc4 - loc3 < 15 ) { awips = remove_extraneous_chars( data.substr( loc3+3, loc4-loc3-3 ) ); data_start = loc4 + 3; } // get the time time_t cur_time_time_t; time( &cur_time_time_t ); tm cur_time; gmtime_r( &cur_time_time_t, &cur_time ); // begin work on filename and directory creation struct stat stat_buf; int stat_ret; string filename = data_folder; // macros #define FN_ADD_DIR_1( s ) filename += string("/") + s; stat_ret = stat( filename.c_str(), &stat_buf ); #define FN_ADD_DIR_2( s ) if ( stat_ret != 0 /*|| !( S_ISDIR( stat_buf ) )*/ ) mkdir( filename.c_str(), 0775 ); #define FN_ADD_DIR( s ) FN_ADD_DIR_1( s ) FN_ADD_DIR_2( s ) #define FN_ADD_TS_DIR( n ) FN_ADD_DIR( pad_zeros( int_to_string ( n ), 2) ) /* // wmo id FN_ADD_DIR( wmoid ) // icao FN_ADD_DIR( icao ) // awips FN_ADD_DIR( awips ) */ // store timestamp data in variables accessible at this scope level string year, mon, date, hour, min; // determine type and figure timestamp enum DATA_TYPE_T { DT_INVALID=0, DT_TEXT, DT_NIDS, DT_GINI }; DATA_TYPE_T data_type = DT_INVALID; if ( wmoid.length() >= 6 ) { if ( wmoid.substr(0,4) == string("SDUS") ) { if ( awips.length() >= 6 && awips[0] == 'N' ) { // this is NIDS data //data_type = DT_NIDS; if ( data.length() >= data_start + 50 ) { #define BYTE_TO_INT( n ) ( static_cast((unsigned char)(data[n])) ) #define NIDS_HW( n ) ( BYTE_TO_INT(data_start+n) * 256 + BYTE_TO_INT(data_start+n+1) ) unsigned int nids_timestamp = ( ( NIDS_HW( 40 ) - 1 ) * 86400 + NIDS_HW( 42 ) * 65536 + NIDS_HW( 44 ) ); data_type = DT_NIDS; //cerr << "nids timestamp: " << nids_timestamp << endl; //time_t nids_time_t = (time_t)(nids_timestamp); tm nids_time; gmtime_r( (time_t*)(&nids_timestamp), &nids_time ); FN_ADD_TS_DIR( nids_time.tm_year + 1900 ) FN_ADD_TS_DIR( nids_time.tm_mon + 1 ) FN_ADD_TS_DIR( nids_time.tm_mday ) // wmo id FN_ADD_DIR( wmoid ) // icao FN_ADD_DIR( icao ) // awips FN_ADD_DIR( awips ) year = int_to_string( nids_time.tm_year + 1900 ); //cerr << "year: " << nids_time.tm_year << " " << year << endl; mon = int_to_string( nids_time.tm_mon + 1 ); date = int_to_string( nids_time.tm_mday ); if ( mon.length() != 2 ) mon = string("0") + mon; if ( date.length() != 2 ) date = string("0") + date; hour = int_to_string( nids_time.tm_hour ); min = int_to_string( nids_time.tm_min ); while ( hour.length() < 2 ) hour = string("0") + hour; while ( min.length() < 2 ) min = string("0") + min; // filename = check_filename(filename + string("/") + wmoid + string("_") + icao + string("_") + awips + string("_") + year + mon + date + string("_") + hour + min, ".nids"); //cerr << "nids filename: " << filename << endl << endl; } } } else if ( wmoid.substr(0,3) == string("TIG") && awips == "NOAWIPS" ) { // this is GINI data data_type = DT_GINI; unsigned int gts_yr = BYTE_TO_INT(data_start+8) + 1900; FN_ADD_TS_DIR( gts_yr ) unsigned int gts_mon = BYTE_TO_INT(data_start+9); //static_cast(data[data_start+9]); FN_ADD_TS_DIR( gts_mon ) unsigned int gts_day = BYTE_TO_INT(data_start+10); //static_cast(data[data_start+10]); FN_ADD_TS_DIR( gts_day ) unsigned int gts_hr = BYTE_TO_INT(data_start+11); //static_cast(data[data_start+11]); unsigned int gts_min = BYTE_TO_INT(data_start+12); //static_cast(data[data_start+12]); // wmo id FN_ADD_DIR( wmoid ) // icao FN_ADD_DIR( icao ) // awips FN_ADD_DIR( awips ) year = int_to_string( gts_yr ); mon = int_to_string( gts_mon ); date = int_to_string( gts_day ); if ( mon.length() != 2 ) mon = string("0") + mon; if ( date.length() != 2 ) date = string("0") + date; hour = int_to_string( gts_hr ); min = int_to_string( gts_min ); while ( hour.length() < 2 ) hour = string("0") + hour; while ( min.length() < 2 ) min = string("0") + min; // filename = check_filename(filename + string("/") + wmoid + string("_") + icao + string("_") + awips + string("_") + year + mon + date + string("_") + hour + min, ".gini"); /* First byte is 01 09 Valid Time - Year of century 10 Month 11 Day 12 Hour 13 Minute 14 Seconds */ } else { // consider it text data_type = DT_TEXT; string wmo_time = data.substr( loc2+1, 6 ); // timestamp with current time FN_ADD_TS_DIR( cur_time.tm_year + 1900 ) FN_ADD_TS_DIR( cur_time.tm_mon + 1 ) FN_ADD_TS_DIR( cur_time.tm_mday ) // wmo id FN_ADD_DIR( wmoid ) // icao FN_ADD_DIR( icao ) // awips FN_ADD_DIR( awips ) year = int_to_string( cur_time.tm_year + 1900 ); mon = int_to_string( cur_time.tm_mon + 1 ); date = int_to_string( cur_time.tm_mday ); if ( mon.length() != 2 ) mon = string("0") + mon; if ( date.length() != 2 ) date = string("0") + date; hour = int_to_string( cur_time.tm_hour ); min = int_to_string( cur_time.tm_min ); while ( hour.length() < 2 ) hour = string("0") + hour; while ( min.length() < 2 ) min = string("0") + min; // filename = check_filename(filename + string("/") + wmoid + string("_") + icao + string("_") + awips + string("_") + year + mon + date + string("_") + hour + min, ".txt"); } } if ( data_type == DT_INVALID ) return; // we are ready to call the upload script // save data to a file ofstream dataout; dataout.open( filename.c_str() , ios::trunc | ios::binary | ios::out ); if ( !dataout.good() ) { return; } dataout.write( data.data(), data.length() ); dataout.close(); if ( !on_data_cmd.empty() ) { // make up the command //clean up string odcmd; odcmd += on_data_cmd; odcmd += " "; odcmd += filename; odcmd += " "; odcmd += wmoid; odcmd += " "; odcmd += icao; odcmd += " "; odcmd += awips; odcmd += " "; odcmd += year; odcmd += " "; odcmd += mon; odcmd += " "; odcmd += date; odcmd += " "; odcmd += hour; odcmd += " "; odcmd += min; // execute command system( odcmd.c_str() ); } // it's been handled! } } string int_to_string( unsigned int n ) { if ( n == 0 ) return string("0"); string s; while ( n != 0 ) { char c = '0' + (unsigned char)(n % 10); string s2; s2 += c; s = s2 + s; n /= 10; } return s; } string pad_zeros( string s, int n ) { while( s.length() < n ) { s = string("0") + s; } return s; } string remove_extraneous_chars( string s ) { string::iterator * bad = new string::iterator[s.length()]; int bad_count = 0; for(string::iterator i = s.begin(); i != s.end(); i++) { if(i != s.end() && !isalnum(*i)) { #ifdef DEBUG cerr << "Removing offending char \"" << *i << "\" from string \"" << s << "\"" << endl; #endif bad[bad_count++] = i; } } for( int i = bad_count - 1; i >= 0; i-- ) s.erase(bad[i]); delete [] bad; #ifdef DEBUG cerr << "remove_extranous_chars returns \"" << s << "\"" << endl; #endif return s; } string check_filename(const string& prefix, const string& suffix) { int serial = 0; while(file_exists(prefix + (serial == 0 ? "" : string("_") + pad_zeros(int_to_string(serial), 2)) + suffix)) serial++; #ifdef DEBUG cerr << "file: " << prefix + suffix << ", using serial " << serial << endl; #endif return(prefix + (serial == 0 ? "" : string("_") + pad_zeros(int_to_string(serial), 2)) + suffix); } bool file_exists(const string& filename) { fstream fin; fin.open(filename.c_str(), ios::in); if( fin.is_open() ) { fin.close(); return true; } fin.close(); return false; }