AirInv Logo  0.1.2
C++ Simulated Airline Inventory Management System library
AirInvServer.cpp
Go to the documentation of this file.
00001 
00005 // //////////////////////////////////////////////////////////////////////
00006 // Import section
00007 // //////////////////////////////////////////////////////////////////////
00008 // STL
00009 #include <cassert>
00010 #include <sstream>
00011 #include <fstream>
00012 #include <string>
00013 #include <unistd.h>
00014 // Boost (Extended STL)
00015 #include <boost/program_options.hpp>
00016 #include <boost/tokenizer.hpp>
00017 // ZeroMQ
00018 #include <zmq.hpp>
00019 // StdAir
00020 #include <stdair/basic/BasLogParams.hpp>
00021 #include <stdair/basic/BasDBParams.hpp>
00022 #include <stdair/bom/BomJSONImport.hpp>
00023 #include <stdair/bom/BomJSONExport.hpp>
00024 #include <stdair/service/Logger.hpp>
00025 // AirInvServer
00026 #include <airinv/config/airinv-paths.hpp>
00027 #include <airinv/AIRINV_Master_Service.hpp>
00028 
00029 // ///////// Type definitions //////////
00030 typedef unsigned int ServerPort_T;
00031 
00032 // //////// Constants //////
00034 const std::string K_AIRINV_DEFAULT_LOG_FILENAME ("airinvServer.log");
00035 
00037 const std::string K_AIRINV_DEFAULT_SERVER_PROTOCOL ("tcp://");
00038 
00040 const std::string K_AIRINV_DEFAULT_SERVER_ADDRESS ("*");
00041 
00043 const ServerPort_T K_AIRINV_DEFAULT_SERVER_PORT (5555);
00044 
00046 const std::string K_AIRINV_DEFAULT_INVENTORY_FILENAME (STDAIR_SAMPLE_DIR
00047                                                        "/invdump01.csv");
00049 const std::string K_AIRINV_DEFAULT_SCHEDULE_FILENAME (STDAIR_SAMPLE_DIR
00050                                                       "/schedule01.csv");
00052 const std::string K_AIRINV_DEFAULT_OND_FILENAME (STDAIR_SAMPLE_DIR
00053                                                  "/ond01.csv");
00054 
00056 const std::string K_AIRINV_DEFAULT_YIELD_FILENAME (STDAIR_SAMPLE_DIR
00057                                                    "/yield01.csv");
00058 
00063 const bool K_AIRINV_DEFAULT_BUILT_IN_INPUT = false;
00064 
00069 const bool K_AIRINV_DEFAULT_FOR_SCHEDULE = false;
00070 
00074 const int K_AIRINV_EARLY_RETURN_STATUS = 99;
00075 
00079 struct Command_T {
00080   typedef enum {
00081     NOP = 0,
00082     QUIT,
00083     DISPLAY,
00084     SELL,
00085     LAST_VALUE
00086   } Type_T;
00087 };
00088 
00089 // ///////// Parsing of Options & Configuration /////////
00090 // A helper function to simplify the main part.
00091 template<class T> std::ostream& operator<< (std::ostream& os,
00092                                             const std::vector<T>& v) {
00093   std::copy (v.begin(), v.end(), std::ostream_iterator<T> (std::cout, " ")); 
00094   return os;
00095 }
00096 
00098 int readConfiguration (int argc, char* argv[], std::string& ioServerProtocol,
00099                        std::string& ioServerAddress, ServerPort_T& ioServerPort,
00100                        bool& ioIsBuiltin, bool& ioIsForSchedule,
00101                        stdair::Filename_T& ioInventoryFilename,
00102                        stdair::Filename_T& ioScheduleInputFilename,
00103                        stdair::Filename_T& ioODInputFilename,
00104                        stdair::Filename_T& ioYieldInputFilename,
00105                        std::string& ioLogFilename) {
00106   // Default for the built-in input
00107   ioIsBuiltin = K_AIRINV_DEFAULT_BUILT_IN_INPUT;
00108 
00109   // Default for the inventory or schedule option
00110   ioIsForSchedule = K_AIRINV_DEFAULT_FOR_SCHEDULE;
00111 
00112   // Declare a group of options that will be allowed only on command line
00113   boost::program_options::options_description generic ("Generic options");
00114   generic.add_options()
00115     ("prefix", "print installation prefix")
00116     ("version,v", "print version string")
00117     ("help,h", "produce help message");
00118     
00119   // Declare a group of options that will be allowed both on command
00120   // line and in config file
00121 
00122   boost::program_options::options_description config ("Configuration");
00123   config.add_options()
00124     ("builtin,b",
00125      "The sample BOM tree can be either built-in or parsed from an input file. That latter must then be given with the -i/--inventory or -s/--schedule option")
00126     ("for_schedule,f",
00127      "The BOM tree should be built from a schedule file (instead of from an inventory dump)")
00128     ("inventory,i",
00129      boost::program_options::value< std::string >(&ioInventoryFilename)->default_value(K_AIRINV_DEFAULT_INVENTORY_FILENAME),
00130      "(CVS) input file for the inventory")
00131     ("schedule,s",
00132      boost::program_options::value< std::string >(&ioScheduleInputFilename)->default_value(K_AIRINV_DEFAULT_SCHEDULE_FILENAME),
00133      "(CVS) input file for the schedule")
00134     ("ond,o",
00135      boost::program_options::value< std::string >(&ioODInputFilename)->default_value(K_AIRINV_DEFAULT_OND_FILENAME),
00136      "(CVS) input file for the O&D")
00137     ("yield,y",
00138      boost::program_options::value< std::string >(&ioYieldInputFilename)->default_value(K_AIRINV_DEFAULT_YIELD_FILENAME),
00139      "(CVS) input file for the yield")
00140     ("protocol,t",
00141      boost::program_options::value< std::string >(&ioServerProtocol)->default_value(K_AIRINV_DEFAULT_SERVER_PROTOCOL),
00142      "Server protocol")
00143     ("address,a",
00144      boost::program_options::value< std::string >(&ioServerAddress)->default_value(K_AIRINV_DEFAULT_SERVER_ADDRESS),
00145      "Server address")
00146     ("port,p",
00147      boost::program_options::value< ServerPort_T >(&ioServerPort)->default_value(K_AIRINV_DEFAULT_SERVER_PORT),
00148      "Server port")
00149     ("log,l",
00150      boost::program_options::value< std::string >(&ioLogFilename)->default_value(K_AIRINV_DEFAULT_LOG_FILENAME),
00151      "Filename for the output logs")
00152     ;
00153 
00154   // Hidden options, will be allowed both on command line and
00155   // in config file, but will not be shown to the user.
00156   boost::program_options::options_description hidden ("Hidden options");
00157   hidden.add_options()
00158     ("copyright",
00159      boost::program_options::value< std::vector<std::string> >(),
00160      "Show the copyright (license)");
00161         
00162   boost::program_options::options_description cmdline_options;
00163   cmdline_options.add(generic).add(config).add(hidden);
00164 
00165   boost::program_options::options_description config_file_options;
00166   config_file_options.add(config).add(hidden);
00167   boost::program_options::options_description visible ("Allowed options");
00168   visible.add(generic).add(config);
00169         
00170   boost::program_options::positional_options_description p;
00171   p.add ("copyright", -1);
00172         
00173   boost::program_options::variables_map vm;
00174   boost::program_options::
00175     store (boost::program_options::command_line_parser (argc, argv).
00176            options (cmdline_options).positional(p).run(), vm);
00177 
00178   std::ifstream ifs ("airinvServer.cfg");
00179   boost::program_options::store (parse_config_file (ifs, config_file_options),
00180                                  vm);
00181   boost::program_options::notify (vm);
00182     
00183   if (vm.count ("help")) {
00184     std::cout << visible << std::endl;
00185     return K_AIRINV_EARLY_RETURN_STATUS;
00186   }
00187 
00188   if (vm.count ("version")) {
00189     std::cout << PACKAGE_NAME << ", version " << PACKAGE_VERSION << std::endl;
00190     return K_AIRINV_EARLY_RETURN_STATUS;
00191   }
00192 
00193   if (vm.count ("prefix")) {
00194     std::cout << "Installation prefix: " << PREFIXDIR << std::endl;
00195     return K_AIRINV_EARLY_RETURN_STATUS;
00196   }
00197 
00198   if (vm.count ("protocol")) {
00199     ioServerProtocol = vm["protocol"].as< std::string >();
00200     std::cout << "Server protocol is: " << ioServerProtocol << std::endl;
00201   }
00202 
00203   if (vm.count ("address")) {
00204     ioServerAddress = vm["address"].as< std::string >();
00205     std::cout << "Server address is: " << ioServerAddress << std::endl;
00206   }
00207 
00208   if (vm.count ("port")) {
00209     ioServerPort = vm["port"].as< ServerPort_T >();
00210     std::cout << "Server port is: " << ioServerPort << std::endl;
00211   }
00212 
00213   if (vm.count ("builtin")) {
00214     ioIsBuiltin = true;
00215   }
00216   const std::string isBuiltinStr = (ioIsBuiltin == true)?"yes":"no";
00217   std::cout << "The BOM should be built-in? " << isBuiltinStr << std::endl;
00218 
00219   if (vm.count ("for_schedule")) {
00220     ioIsForSchedule = true;
00221   }
00222   const std::string isForScheduleStr = (ioIsForSchedule == true)?"yes":"no";
00223   std::cout << "The BOM should be built from schedule? " << isForScheduleStr
00224             << std::endl;
00225 
00226   if (ioIsBuiltin == false) {
00227 
00228     if (ioIsForSchedule == false) {
00229       // The BOM tree should be built from parsing an inventory dump
00230       if (vm.count ("inventory")) {
00231         ioInventoryFilename = vm["inventory"].as< std::string >();
00232         std::cout << "Input inventory filename is: " << ioInventoryFilename
00233                   << std::endl;
00234 
00235       } else {
00236         // The built-in option is not selected. However, no inventory dump
00237         // file is specified
00238         std::cerr << "Either one among the -b/--builtin, -i/--inventory or "
00239                   << " -f/--for_schedule and -s/--schedule options "
00240                   << "must be specified" << std::endl;
00241       }
00242 
00243     } else {
00244       // The BOM tree should be built from parsing a schedule (and O&D) file
00245       if (vm.count ("schedule")) {
00246         ioScheduleInputFilename = vm["schedule"].as< std::string >();
00247         std::cout << "Input schedule filename is: " << ioScheduleInputFilename
00248                   << std::endl;
00249 
00250       } else {
00251         // The built-in option is not selected. However, no schedule file
00252         // is specified
00253         std::cerr << "Either one among the -b/--builtin, -i/--inventory or "
00254                   << " -f/--for_schedule and -s/--schedule options "
00255                   << "must be specified" << std::endl;
00256       }
00257 
00258       if (vm.count ("ond")) {
00259         ioODInputFilename = vm["ond"].as< std::string >();
00260         std::cout << "Input O&D filename is: " << ioODInputFilename << std::endl;
00261       }
00262 
00263       if (vm.count ("yield")) {
00264         ioYieldInputFilename = vm["yield"].as< std::string >();
00265         std::cout << "Input yield filename is: " << ioYieldInputFilename << std::endl;
00266       }
00267     }
00268   }
00269 
00270   if (vm.count ("log")) {
00271     ioLogFilename = vm["log"].as< std::string >();
00272     std::cout << "Log filename is: " << ioLogFilename << std::endl;
00273   }
00274 
00275   return 0;
00276 }
00277 
00278 
00279 // ///////// Utility functions on top of the ZeroMQ library /////////
00283 static std::string s_recv (zmq::socket_t& socket) {
00284   zmq::message_t message;
00285   socket.recv (&message);
00286 
00287   return std::string (static_cast<char*> (message.data()), message.size());
00288 }
00289 
00293 static bool s_send (zmq::socket_t& socket, const std::string& string) {
00294   zmq::message_t message (string.size());
00295   memcpy (message.data(), string.data(), string.size());
00296 
00297   bool rc = socket.send (message);
00298   return rc;
00299 }
00300 
00301 
00302 // /////////////////////// M A I N ////////////////////////
00303 int main (int argc, char* argv[]) {
00304 
00305   // Server parameters (for ZeroMQ)
00306   std::string ioServerProtocol;
00307   std::string ioServerAddress;
00308   ServerPort_T ioServerPort;
00309 
00310   // State whether the BOM tree should be built-in or parsed from an
00311   // input file
00312   bool isBuiltin;
00313   bool isForSchedule;
00314 
00315   // Input file names
00316   stdair::Filename_T lInventoryFilename;
00317   stdair::Filename_T lScheduleInputFilename;
00318   stdair::Filename_T lODInputFilename;
00319   stdair::Filename_T lYieldInputFilename;
00320 
00321   // Output log File
00322   stdair::Filename_T lLogFilename;
00323 
00324   // Call the command-line option parser
00325   const int lOptionParserStatus =
00326     readConfiguration (argc, argv, ioServerProtocol, ioServerAddress,
00327                        ioServerPort, isBuiltin, isForSchedule,
00328                        lInventoryFilename, lScheduleInputFilename,
00329                        lODInputFilename, lYieldInputFilename, lLogFilename);
00330 
00331   if (lOptionParserStatus == K_AIRINV_EARLY_RETURN_STATUS) {
00332     return 0;
00333   }
00334 
00335   // Set the log parameters
00336   std::ofstream logOutputFile;
00337   // Open and clean the log outputfile
00338   logOutputFile.open (lLogFilename.c_str());
00339   logOutputFile.clear();
00340 
00341   // Initialise the inventory service
00342   const stdair::BasLogParams lLogParams (stdair::LOG::DEBUG, logOutputFile);
00343   AIRINV::AIRINV_Master_Service airinvService (lLogParams);
00344 
00345   // DEBUG
00346   STDAIR_LOG_DEBUG ("Initialisation of the AirInv server");
00347 
00348   // Check wether or not a (CSV) input file should be read
00349   if (isBuiltin == true) {
00350 
00351     // Build the sample BOM tree for RMOL
00352     airinvService.buildSampleBom();
00353 
00354   } else {
00355     if (isForSchedule == true) {
00356       // Build the BOM tree from parsing a schedule file (and O&D list)
00357       AIRRAC::YieldFilePath lYieldFilePath (lYieldInputFilename);
00358       airinvService.parseAndLoad (lScheduleInputFilename, lODInputFilename,
00359                                   lYieldFilePath);
00360 
00361     } else {
00362       // Build the BOM tree from parsing an inventory dump file
00363       airinvService.parseAndLoad (lInventoryFilename);
00364     }
00365   }
00366 
00367   // Build the connection string (e.g., "tcp://*:5555", which is the default)
00368   std::ostringstream oZeroMQBindStream;
00369   oZeroMQBindStream << ioServerProtocol << ioServerAddress
00370                     << ":" << ioServerPort;
00371   const std::string lZeroMQBindString (oZeroMQBindStream.str());
00372 
00373   // Prepare the context and socket of the server
00374   zmq::context_t context (1);
00375   zmq::socket_t socket (context, ZMQ_REP);
00376   socket.bind (lZeroMQBindString.c_str());
00377 
00378   // DEBUG
00379   STDAIR_LOG_DEBUG ("The AirInv server is ready to receive requests...");
00380 
00381   while (true) {
00382 
00383     // Wait for next request from client, which is expected to give
00384     // the JSON-ified details of the requested flight-date
00385     const std::string& lFlightDateKeyJSONString = s_recv (socket);
00386 
00387     // DEBUG
00388     STDAIR_LOG_DEBUG ("Received: '" << lFlightDateKeyJSONString << "'");
00389 
00390     // Extract, from the JSON-ified string an airline code
00391     stdair::AirlineCode_T lAirlineCode;
00392     stdair::BomJSONImport::jsonImportInventoryKey (lFlightDateKeyJSONString,
00393                                                    lAirlineCode);
00394 
00395     // Extract, from the JSON-ified string a flight number and a departure date
00396     stdair::FlightNumber_T lFlightNumber;
00397     stdair::Date_T lDate;
00398     stdair::BomJSONImport::jsonImportFlightDateKey (lFlightDateKeyJSONString,
00399                                                     lFlightNumber, lDate);
00400 
00401     // DEBUG
00402     STDAIR_LOG_DEBUG ("=> airline code = '" << lAirlineCode
00403                       << "', flight number = " << lFlightNumber
00404                       << "', departure date = '" << lDate << "'");
00405 
00406     // DEBUG: Display the flight-date dump
00407     const std::string& lFlightDateCSVDump =
00408       airinvService.csvDisplay (lAirlineCode, lFlightNumber, lDate);
00409     STDAIR_LOG_DEBUG (std::endl << lFlightDateCSVDump);
00410 
00411     // Dump the full details of the flight-date into the JSON-ified flight-date
00412     const std::string& lFlightDateJSONDump =
00413       airinvService.jsonExport (lAirlineCode, lFlightNumber, lDate);
00414 
00415     // DEBUG
00416     STDAIR_LOG_DEBUG ("Send: '" << lFlightDateJSONDump << "'");
00417 
00418     // Send back the flight-date details to the client
00419     s_send (socket, lFlightDateJSONDump);
00420   }
00421 
00422   return 0;
00423 }
00424