/* Virginia Tech Cognitive Radio Open Source Systems * Virginia Tech, 2009 * * LICENSE INFORMATION GOES HERE */ /* Inter-component communication handled by sockets and FD's. * Server support has been completely implemented and tested. * * Services are stored in a SQLite DB by the ID of the CE that registered them. Service * support has been completely implemented and tested. * * Missions are loaded from an XML file, connected with services provided by components, * and run. See the documentation for the "PerformActiveMission" below for important * info. */ //TODO Add nested conditional support //TODO Verify update functionality //TODO Better shutdown //TODO Verify Deregister services //TODO printf's #include #include #include #include #include #include "vtcross/common.h" #include "components.h" #include "vtcross/containers.h" #include "vtcross/debug.h" #include "vtcross/error.h" #include "vtcross/socketcomm.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tinyxml/tinyxml.h" #include "tinyxml/tinystr.h" #include "sqlite3.h" typedef struct services_s *services_DB; typedef struct data_s *data_DB; using namespace std; struct services_s { char filename[64]; char tablename[64]; char command[2048]; sqlite3 *db; unsigned int num_columns; }; struct data_s { char filename[64]; char tablename[64]; char command[2048]; sqlite3 *db; unsigned int num_columns; }; services_DB _services_DB; data_DB _data_DB; const char *_SML_Config; //Callback function used internally by some of the SQLite3 commands int callback(void *notUsed, int argc, char **argv, char **azColName){ int i; for(i=0; icommand, "drop table "); strcat(_services_DB->command, _services_DB->tablename); int rc = sqlite3_exec(_services_DB->db, _services_DB->command, callback, 0, &errorMsg); if( rc!=SQLITE_OK && rc!=101 ) fprintf(stderr, "ServiceManagementLayer::Destructor services 'drop table' error: %s\n", errorMsg); strcpy(_services_DB->command, "vacuum"); rc = sqlite3_exec(_services_DB->db, _services_DB->command, callback, 0, &errorMsg); if( rc!=SQLITE_OK && rc!=101 ) fprintf(stderr, "ServiceManagementLayer::Destructor services 'vacuum' error: %s\n", errorMsg); free(_services_DB); strcpy(_data_DB->command, "drop table "); strcat(_data_DB->command, _data_DB->tablename); rc = sqlite3_exec(_data_DB->db, _data_DB->command, callback, 0, &errorMsg); if( rc!=SQLITE_OK && rc!=101 ) fprintf(stderr, "ServiceManagementLayer::Destructor data 'drop table' error: %s\n", errorMsg); strcpy(_data_DB->command, "vacuum"); rc = sqlite3_exec(_data_DB->db, _data_DB->command, callback, 0, &errorMsg); if( rc!=SQLITE_OK && rc!=101 ) fprintf(stderr, "ServiceManagementLayer::Destructor data 'vacuum' error: %s\n", errorMsg); free(_data_DB); } //Note that sizes of CE_List, miss, and service are hardcoded for now. //Also, their sizes are hardcoded into the code in various places; a fix for a future version. ServiceManagementLayer::ServiceManagementLayer(const char* SML_Config, \ const char* serverName, const char* serverPort) { LOG("Creating Service Management Layer.\n"); _SML_Config = SML_Config; ConnectToShell(serverName, serverPort); CE_List = (CE_Reg *) malloc(10*sizeof(struct CE_Reg)); CE_List = new CE_Reg[10]; miss = new Mission[10]; for(int i = 0; i < 10; i++) miss[i].services = new Service[20]; Current_ID = 0; LoadConfiguration(SML_Config, miss); CreateServicesDB(); CreateDataDB(); } /* CALLED BY: constructor * INPUTS: * OUTPUTS: * * DESCRIPTION: Create and initialize a DB to hold the services registered by components */ void ServiceManagementLayer::CreateServicesDB() { _services_DB = (services_DB) malloc(sizeof(struct services_s)); char *errorMsg; // create database // copy filename unsigned int i=0; strcpy(_services_DB->filename, "Services_Table"); // execute create database command // database handle //_services_DB->db = NULL; sqlite3_open(_services_DB->filename, &(_services_DB->db)); char* cols[] = {(char *)"ID_Num", (char *)"Service_Name"}; // create table // copy tablename strcpy(_services_DB->tablename, "Services"); // number of columns in the table _services_DB->num_columns = 2; // generate command strcpy(_services_DB->command, "CREATE TABLE "); strcat(_services_DB->command, _services_DB->tablename); strcat(_services_DB->command, "("); strcat(_services_DB->command, cols[0]); strcat(_services_DB->command, " INT, "); strcat(_services_DB->command, cols[1]); strcat(_services_DB->command, " TEXT"); strcat(_services_DB->command, ");"); // execute create table command sqlite3_stmt *ppStmt; /* OUT: Statement handle */ const char *pzTail; /* OUT: Pointer to unused portion of zSql */ int rc = sqlite3_prepare_v2(_services_DB->db, _services_DB->command, 128, &ppStmt, &pzTail); if( rc!=SQLITE_OK && rc!=101 ) printf("ServiceManagementLayer::CreateServicesDB 'prepare_stmt' error %d\n", rc); rc = sqlite3_step(ppStmt); if( rc!=SQLITE_OK && rc!=101 ) printf("ServiceManagementLayer::CreateServicesDB 'step' error %d\n"); } /* CALLED BY: constructor * INPUTS: * OUTPUTS: * * DESCRIPTION: Create and initialize a DB to hold the data sent by components */ void ServiceManagementLayer::CreateDataDB() { _data_DB = (data_DB) malloc(sizeof(struct data_s)); char *errorMsg; // create database // copy filename unsigned int i=0; strcpy(_data_DB->filename, "Data_Table"); // execute create database command // database handle //_services_DB->db = NULL; sqlite3_open(_data_DB->filename, &(_data_DB->db)); char* cols[] = {(char *)"Tag", (char *)"Data"}; // create table // copy tablename strcpy(_data_DB->tablename, "Data"); // number of columns in the table _data_DB->num_columns = 2; // generate command strcpy(_data_DB->command, "CREATE TABLE "); strcat(_data_DB->command, _data_DB->tablename); strcat(_data_DB->command, "("); strcat(_data_DB->command, cols[0]); //First column is the name of the data (coresponding to the name of the output/input pair) //It is the primary key so any subsequent data with the same name will replace the row strcat(_data_DB->command, " TEXT PRIMARY KEY ON CONFLICT REPLACE, "); strcat(_data_DB->command, cols[1]); strcat(_data_DB->command, " TEXT"); strcat(_data_DB->command, ");"); // execute create table command sqlite3_stmt *ppStmt; /* OUT: Statement handle */ const char *pzTail; /* OUT: Pointer to unused portion of zSql */ int rc = sqlite3_prepare_v2(_data_DB->db, _data_DB->command, 128, &ppStmt, &pzTail); if( rc!=SQLITE_OK && rc!=101 ) printf("ServiceManagementLayer::CreateDataDB 'prepare_stmt' error %d\n", rc); rc = sqlite3_step(ppStmt); if( rc!=SQLITE_OK && rc!=101 ) printf("ServiceManagementLayer::CreateDataDB 'step' error %d\n"); } /* CALLED BY: MessageHandler * INPUTS: * OUTPUTS: * * DESCRIPTION: Sends a message identifying this component as an SML to the Shell */ void ServiceManagementLayer::SendComponentType() { SendMessage(shellSocketFD, "response_sml"); LOG("SML responded to GetRemoteComponentType query.\n"); } /* CALLED BY: constructor * INPUTS: |serverName| the IPv4 name of the server (127.0.0.1 for localhost) * |serverPort| the port on the server to connect to * OUTPUTS: * * DESCRIPTION: Connecting to the shell takes 2 steps * 1) Establish a client socket for communication * 2) Run the initial Registration/handshake routine */ void ServiceManagementLayer::ConnectToShell(const char* serverName, \ const char* serverPort) { shellSocketFD = ClientSocket(serverName, serverPort); RegisterComponent(); } /* CALLED BY: StartSMLServer * INPUTS: |ID| The ID number of the CE that has a message wating * OUTPUTS: * * DESCRIPTION: Called whenever a socket is identified as being ready for communication * This funciton reads the message and calls the appropriate helper */ void ServiceManagementLayer::MessageHandler(int32_t ID) { char buffer[256]; memset(buffer, 0, 256); int32_t _FD; if(ID != -1) _FD = CE_List[ID].FD; else _FD = shellSocketFD; ReadMessage(_FD, buffer); //--------Policy Engine Stuff - no policy engine support in this version-------// //printf("********* %s **********\n", buffer); // TODO // If we send integer op codes rather than strings, this process will be // MUCH faster since instead of donig string compares we can simply // switch on the integer value... /*if(strcmp(buffer, "register_service") == 0) { if(strcmp(buffer, "policy_geo") == 0) { } else if(strcmp(buffer, "policy_time") == 0) { } else if(strcmp(buffer, "policy_spectrum") == 0) { } else if(strcmp(buffer, "policy_spacial") == 0) { } } else if(strcmp(buffer, "deregister_service") == 0) { if(strcmp(buffer, "policy_geo") == 0) { } else if(strcmp(buffer, "policy_time") == 0) { } else if(strcmp(buffer, "policy_spectrum") == 0) { } else if(strcmp(buffer, "policy_spacial") == 0) { } }*/ //Go down the list to call the appropriate function if(strcmp(buffer, "query_component_type") == 0) { SendComponentType(); } else if(strcmp(buffer, "reset_sml") == 0) { Reset(); } else if(strcmp(buffer, "shutdown_sml") == 0) { Shutdown(); } else if(strcmp(buffer, "register_engine_cognitive") == 0) { RegisterCognitiveEngine(ID); } else if(strcmp(buffer, "register_service") == 0) { ReceiveServices(ID); } else if(strcmp(buffer, "send_component_type") == 0) { SendComponentType(); } else if(strcmp(buffer, "list_services") == 0) { ListServices(); } else if(strcmp(buffer, "set_active_mission") == 0) { SetActiveMission(); } else if(strcmp(buffer, "request_optimization") == 0) { PerformActiveMission(); } else if(strcmp(buffer, "deregister_engine_cognitive") == 0) { DeregisterCognitiveEngine(ID); } else if(strcmp(buffer, "deregister_service") == 0) { DeregisterServices(ID); } } //TODO Finish /* CALLED BY: MessageHandler * INPUTS: * OUTPUTS: * * DESCRIPTION: Deregisters the component from the Shell. */ void ServiceManagementLayer::Shutdown() { DeregisterComponent(); } //TODO Finish /* CALLED BY: MessageHandler * INPUTS: * OUTPUTS: * * DESCRIPTION: Deregisters the component from the Shell */ void ServiceManagementLayer::Reset() { DeregisterComponent(); ReloadConfiguration(); } /* CALLED BY: ConnectToShell * INPUTS: * OUTPUTS: * * DESCRIPTION: Sends the registration message to the Shell */ void ServiceManagementLayer::RegisterComponent() { SendMessage(shellSocketFD, "register_sml"); LOG("ServiceManagementLayer:: Registration message sent.\n"); } /* CALLED BY: Shutdown * INPUTS: * OUTPUTS: * * DESCRIPTION: Closes the client socket with the shell, sends a deregstration message */ void ServiceManagementLayer::DeregisterComponent() { SendMessage(shellSocketFD, "deregister_sml"); LOG("ServiceManagementLayer:: Deregistration message sent.\n"); shutdown(shellSocketFD, 2); close(shellSocketFD); shellSocketFD = -1; LOG("ServiceManagementLayer:: Shell socket closed.\n"); } /* CALLED BY: RegisterCognitiveEngine * INPUTS: |ID| The ID number of the component where the data is to be transfered to * OUTPUTS: * * DESCRIPTION: Streams config data directly from the shell to the CE, and checks * for an "ack" message from the CE after every sent message * to know when to stop communication. */ //Modified to check the incoming message buffer rather than the outgoing message buffer to avoid a portion of the delay void ServiceManagementLayer::TransferRadioConfiguration(int32_t ID) { printf("transRadConfig\n"); struct timeval selTimeout; fd_set sockSet; int32_t rc = 0; char buffer[256]; //Send data until the CE sends an ACK message back while(rc!=0){ memset(buffer, 0, 256); //Receive data from Shell ReadMessage(shellSocketFD, buffer); //Send data to CE SendMessage(CE_List[ID].FD, buffer); FD_ZERO(&sockSet); FD_SET(shellSocketFD, &sockSet); selTimeout.tv_sec = 0; selTimeout.tv_usec = 50000; //Check if there is a message on the shell ready to be processed rc=select(shellSocketFD + 1, &sockSet, NULL, NULL, &selTimeout); } memset(buffer, 0, 256); ReadMessage(CE_List[ID].FD, buffer); SendMessage(shellSocketFD, buffer); } /* CALLED BY: RegisterCognitiveEngine * INPUTS: |ID| The ID number of the component where the data is to be transfered to * OUTPUTS: * * DESCRIPTION: Simmilar to TransferRadioConfig, just with Experience data */ //Modified to check the incoming message buffer rather than the outgoing message buffer to avoid a portion of the delay void ServiceManagementLayer::TransferExperience(int32_t ID) { struct timeval selTimeout; fd_set sockSet; int32_t rc = 0; char buffer[256]; //Send data until the CE sends an ACK message back while(rc!=0){ memset(buffer, 0, 256); //Receive data from Shell ReadMessage(shellSocketFD, buffer); //Send data to CE SendMessage(CE_List[ID].FD, buffer); FD_ZERO(&sockSet); FD_SET(shellSocketFD, &sockSet); selTimeout.tv_sec = 0; selTimeout.tv_usec = 50000; //Check if there is a message on the shell ready to be processed rc=select(shellSocketFD + 1, &sockSet, NULL, NULL, &selTimeout); } memset(buffer, 0, 256); ReadMessage(CE_List[ID].FD, buffer); SendMessage(shellSocketFD, buffer); } /* CALLED BY: MessageHandler * INPUTS: |ID| The ID number of the component where service is located * OUTPUTS: * * DESCRIPTION: Inserts a service into the DB with the ID of the component where it exists */ void ServiceManagementLayer::ReceiveServices(int32_t ID) { char buffer[256]; memset(buffer, 0, 256); ReadMessage(CE_List[ID].FD, buffer); char* cols[] = {(char *)"ID_Num", (char *)"Service_Name"}; // generate command strcpy(_services_DB->command, "insert into "); strcat(_services_DB->command, _services_DB->tablename); strcat(_services_DB->command, " ("); strcat(_services_DB->command, cols[0]); strcat(_services_DB->command, ", "); strcat(_services_DB->command, cols[1]); strcat(_services_DB->command, ") "); strcat(_services_DB->command, " values("); sprintf(_services_DB->command, "%s%d", _services_DB->command, ID); strcat(_services_DB->command, ", '"); strcat(_services_DB->command, buffer); strcat(_services_DB->command, "');"); //printf("search command: %s\n", _services_DB->command); // execute add command char *errorMsg; int rc = sqlite3_exec(_services_DB->db, _services_DB->command, callback, 0, &errorMsg); if( rc!=SQLITE_OK && rc!=101 ) fprintf(stderr, "ServiceManagementLayer::RecieveServices DB Error %s\n", errorMsg); char *outBuffer; sprintf(outBuffer, "SML: Registering service '%s' from component number '%d'\n", buffer, ID); LOG(outBuffer); } /* CALLED BY: MessageHandler * INPUTS: * OUTPUTS: * * DESCRIPTION: This method associates the services that components provide with the services that are requested in the mission * Each service in the mission is given the ID and FD of a component that has registered to provide that service * Deregistration is okay until this method is called without a reload, but if deregistration occurs after this * method is called it needs to be called again even if other engines also provide the services */ void ServiceManagementLayer::SetActiveMission() { char buffer[256]; memset(buffer, 0, 256); ReadMessage(shellSocketFD, buffer); int32_t missID = atoi(buffer); for(activeMission = 0; activeMission < 10; activeMission++) { //Find the active mission by comparing mission ID's if(miss[activeMission].missionID == missID) break; } //For each service in the mission for(int i = 0; i < miss[activeMission].numServices; i++) { //Check whether the current service is an actual service or a conditional if(miss[activeMission].services[i].name.compare("if") && miss[activeMission].services[i].name.compare("while")){ //If it is a service, search the database of registered services to find the ID of the component that registered it strcpy(_services_DB->command, "select "); strcat(_services_DB->command, _services_DB->tablename); strcat(_services_DB->command, ".* from "); strcat(_services_DB->command, _services_DB->tablename); strcat(_services_DB->command, " where Service_Name=="); sprintf(_services_DB->command, "%s'%s';", _services_DB->command, miss[activeMission].services[i].name.c_str()); sqlite3_stmt * pStatement; int rc = sqlite3_prepare_v2(_services_DB->db, _services_DB->command, -1, &pStatement, NULL); if (rc == SQLITE_OK){ if (sqlite3_step(pStatement) == SQLITE_ROW) miss[activeMission].services[i].componentID = sqlite3_column_int(pStatement, 0); else { printf("services_DB:: Mission requires service not provided by any connected component.\n"); rc=31337; } } else { printf("services_DB:: Error executing SQL statement. rc = %i\n%s\n",rc,_services_DB->command); } sqlite3_finalize(pStatement); miss[activeMission].services[i].socketFD = CE_List[miss[activeMission].services[i].componentID].FD; //Set the FD and ID of the service to refer to the component where the service exists } //Nothing to be done for conditionals at this stage } //printf("\nhere ---%d, %d---\n", miss[activeMission].services[0].componentID, miss[activeMission].services[1].componentID); } /* CALLED BY: PerformActiveMission * INPUTS: |sourceID| ID of the service that is being processed * OUTPUTS: * * DESCRIPTION: This is a helper method for the "PerformActiveMission" function * NOTE: This function has changed durrastically from the previous implementation * Takes an ID of a service * For that service, finds inputs in DB and forwords those on to the engine after sending comm-starting messages * Afterwords, listenes for the outputs so that it can store those in the database for future services or the overall output */ void ServiceManagementLayer::TransactData(int32_t sourceID) { printf("TransactData Occuring\n"); char buffer[256]; std::string data; char* cols[] = {(char *)"Tag", (char *)"Data"}; int i = 0; fd_set sockSet; struct timeval selTimeout; //Transmission starting messages SendMessage(miss[activeMission].services[sourceID].socketFD, "request_optimization_service"); SendMessage(miss[activeMission].services[sourceID].socketFD, miss[activeMission].services[sourceID].name.c_str()); //Find and load the input data while(i < 3 && !miss[activeMission].services[sourceID].input[i].empty()){ //printf("pulling input data out of DB for ID#=%d\n", sourceID); strcpy(_data_DB->command, "select "); strcat(_data_DB->command, _data_DB->tablename); strcat(_data_DB->command, ".* from "); strcat(_data_DB->command, _data_DB->tablename); strcat(_data_DB->command, " where Tag=="); sprintf(_data_DB->command, "%s'%s';", _data_DB->command, miss[activeMission].services[sourceID].input[i].c_str()); sqlite3_stmt * pStatement; int rc = sqlite3_prepare_v2(_data_DB->db, _data_DB->command, -1, &pStatement, NULL); if (rc == SQLITE_OK){ if (sqlite3_step(pStatement) == SQLITE_ROW) data.append((const char*) sqlite3_column_text(pStatement, 1)); else { printf("data_DB:: Data not yet in DB.\n"); rc=31337; } } else { printf("data_DB:: Error executing SQL statement. rc = %i\n%s\n",rc,_data_DB->command); } sqlite3_finalize(pStatement); char *data_ch = (char *) data.c_str(); //Tokenize the data and pass it along char *token = strtok(data_ch, "@"); while(token){ SendMessage(miss[activeMission].services[sourceID].socketFD, token); token = strtok(NULL, "@"); } //printf("done pulling input data out of DB for ID#=%d\n", sourceID); i++; } int32_t j = 0; FD_ZERO(&sockSet); FD_SET(miss[activeMission].services[sourceID].socketFD, &sockSet); //TODO neccessary? selTimeout.tv_sec = 5; selTimeout.tv_usec = 0; //Use select command to force wait for processing to finish select(miss[activeMission].services[sourceID].socketFD + 1, &sockSet, NULL, NULL, &selTimeout); while(j < 3 && !miss[activeMission].services[sourceID].output[j].empty()){ int rc; data.clear(); while(true){ //Read the data incrementally and deliminate it with the "@" symbol memset(buffer, 0, 256); ReadMessage(miss[activeMission].services[sourceID].socketFD, buffer); if(strcmp(buffer, "output_finished")==0) break; data.append(buffer); data.append("@");; } printf("SML: putting output data into DB for ID#=%d\n", sourceID); strcpy(_data_DB->command, "insert or replace into "); strcat(_data_DB->command, _data_DB->tablename); strcat(_data_DB->command, " ("); strcat(_data_DB->command, cols[0]); strcat(_data_DB->command, ", "); strcat(_data_DB->command, cols[1]); strcat(_data_DB->command, ") "); strcat(_data_DB->command, " values('"); strcat(_data_DB->command, miss[activeMission].services[sourceID].output[j].c_str()); strcat(_data_DB->command, "', '"); strcat(_data_DB->command, data.c_str()); strcat(_data_DB->command, "');"); char *errorMsg; rc = sqlite3_exec(_data_DB->db, _data_DB->command, callback, 0, &errorMsg); if( rc!=SQLITE_OK && rc!=101 ) fprintf(stderr, "SQL error: %s\n", errorMsg); printf("SML: done putting ouptut data into DB for ID#='%d', data=%s\n", sourceID, data.c_str()); j++; } printf("done transact data!\n"); printf("\n\n\n"); // generate commandi strcpy(_data_DB->command, "select "); strcat(_data_DB->command, _data_DB->tablename); strcat(_data_DB->command, ".* from "); strcat(_data_DB->command, _data_DB->tablename); strcat(_data_DB->command, ";"); // execute print (select all) command char *errorMsg; int rc = sqlite3_exec(_data_DB->db, _data_DB->command, callback, 0, &errorMsg); if( rc!=SQLITE_OK && rc!=101 ) fprintf(stderr, "SQL error: %s\n", errorMsg); printf("database %s, table %s:\n", _data_DB->filename, _data_DB->tablename); printf("\n\n\n"); } /* CALLED BY: MessageHandler * INPUTS: * OUTPUTS: * * DESCRIPTION: This function works by first sending the inputs from the shell to the appropriate components * The first service should begin immeadiately, as should any others who have all of their input paramaters * When they complete, the output path is found and the data is transfered as it becomes available * Presumably at this point the second function has all of it's paramaters, so it begins to compute, and the cycle repeats * If the generated output is an overall output, it is sent on to the shell * "if" and "while" statements are handled by setting up a faux service that has a true input, a false input, and a boolean flag * If the true input is non-NULL and the flag is true, the statements execute * Likewise, if the false input is non-NULL and the flag is false, the statements execute * These flags are set during execution any time one of these faux services' inputs appear in an output statement * * * Rules for active missions (currently) * -Three inputs/outputs per service and per mission * -Inputs simply define a path, so multiple variables can be transmitted over the same input * -Each component that sends data must include the "output_finished" statement after each output is finished sending it's data * -All ordering constraints have been relaxed in this version; all data is stored locally and only sent when requested * -If support fully implemented * -Conditions must be boolean flags * -Flags are set by putting either the character string "true" or "false" on the buffer * -IMPORTANT: DB uses '@' to seperate individual statements; using '@' in the data stream will result in incorrect behavior */ void ServiceManagementLayer::PerformActiveMission() { int i = 0; std::string data; std::string input; std::string check; char buffer[256]; fd_set sockSet; char* cols[] = {(char *)"Tag", (char *)"Data"}; struct timeval selTimeout; //Get the inputs while(i < 3 && !miss[activeMission].input[i].empty()){ //New data being added to DB printf("inserting data from shell\n"); int rc; while(true){ memset(buffer, 0, 256); ReadMessage(shellSocketFD, buffer); if(strcmp(buffer, "input_finished")==0) break; data.append(buffer); data.append("@"); /*FD_ZERO(&sockSet); FD_SET(miss[activeMission].services[sourceID].socketFD, &sockSet); selTimeout.tv_sec = 0; selTimeout.tv_usec = 50000; //Check if there is a message on the CE socket ready to be processed rc=select(miss[activeMission].services[sourceID].socketFD + 1, &sockSet, NULL, NULL, &selTimeout);*/ }//while(rc!=0);; strcpy(_data_DB->command, "insert into "); strcat(_data_DB->command, _data_DB->tablename); strcat(_data_DB->command, " ("); strcat(_data_DB->command, cols[0]); strcat(_data_DB->command, ", "); strcat(_data_DB->command, cols[1]); strcat(_data_DB->command, ") "); strcat(_data_DB->command, " values('"); strcat(_data_DB->command, miss[activeMission].input[i].c_str()); strcat(_data_DB->command, "', '"); strcat(_data_DB->command, data.c_str()); strcat(_data_DB->command, "');"); char *errorMsg; rc = sqlite3_exec(_data_DB->db, _data_DB->command, callback, 0, &errorMsg); if( rc!=SQLITE_OK && rc!=101 ) fprintf(stderr, "SQL error: %s\n", errorMsg); printf("SML: finished adding data from shell on input %d\n", i); i++; } i=0; data.clear(); while(i < miss[activeMission].numServices) { if(miss[activeMission].services[i].name.compare("if")==0) { printf("if detected\n"); input.clear(); check.clear(); if(!miss[activeMission].services[i].input[0].empty()){ input=miss[activeMission].services[i].input[0]; check = "true@"; } else{ input=miss[activeMission].services[i].input[1]; check = "false@"; } printf("input=%s\n", input.c_str()); strcpy(_data_DB->command, "SELECT "); strcat(_data_DB->command, _data_DB->tablename); strcat(_data_DB->command, ".* from "); strcat(_data_DB->command, _data_DB->tablename); strcat(_data_DB->command, " where Tag=="); sprintf(_data_DB->command, "%s'%s';", _data_DB->command, input.c_str()); sqlite3_stmt * pStatement; int rc = sqlite3_prepare_v2(_data_DB->db, _data_DB->command, -1, &pStatement, NULL); if (rc == SQLITE_OK){ if (sqlite3_step(pStatement) == SQLITE_ROW) data = (const char *) sqlite3_column_text(pStatement, 1); else { printf("1 data_DB:: Data not yet in DB. %d\n", i); rc=31337; } } else { printf("data_DB:: Error executing SQL statement. rc = %i\n%s\n",rc,_data_DB->command); } sqlite3_finalize(pStatement); printf("data=%s, check=%s\n", data.c_str(), check.c_str()); if(data.compare(check)==0){ printf("if taken\n"); for(int k = i + 1; k <= i + miss[activeMission].services[i].num_conds; k++) TransactData(k); } else{ i+=miss[activeMission].services[i].num_conds; printf("if not taken %d\n", miss[activeMission].services[i].num_conds); } } else if(miss[activeMission].services[i].name.compare("while")==0) { printf("while detected\n"); while(true){ data.clear(); input.clear(); check.clear(); if(!miss[activeMission].services[i].input[0].empty()){ input=miss[activeMission].services[i].input[0]; check = "true@"; } else{ input=miss[activeMission].services[i].input[1]; check = "false@"; } strcpy(_data_DB->command, "select "); strcat(_data_DB->command, _data_DB->tablename); strcat(_data_DB->command, ".* from "); strcat(_data_DB->command, _data_DB->tablename); strcat(_data_DB->command, " where Tag=="); sprintf(_data_DB->command, "%s'%s';", _data_DB->command, input.c_str()); sqlite3_stmt * pStatement; int rc = sqlite3_prepare_v2(_data_DB->db, _data_DB->command, -1, &pStatement, NULL); if (rc == SQLITE_OK){ if (sqlite3_step(pStatement) == SQLITE_ROW) data = (const char *) sqlite3_column_text(pStatement, 1); else printf("1 data_DB:: Data not yet in DB. %d\n", i); } else { printf("data_DB:: Error executing SQL statement. rc = %i\n%s\n",rc,_data_DB->command); } sqlite3_finalize(pStatement); printf("data=%s, check=%s\n", data.c_str(), check.c_str()); if(data.compare(check)==0){ printf("while taken\n"); for(int k = i + 1; k <= i + miss[activeMission].services[i].num_conds; k++) TransactData(k); } else{ i+=miss[activeMission].services[i].num_conds; printf("while not taken %d\n", miss[activeMission].services[i].num_conds); break; } } } else{ TransactData(i);} i++; } i=0; data.clear(); //get the ouptuts while(i < 3 && !miss[activeMission].output[i].empty()){ printf("sending output data to shell\n"); strcpy(_data_DB->command, "select "); strcat(_data_DB->command, _data_DB->tablename); strcat(_data_DB->command, ".* from "); strcat(_data_DB->command, _data_DB->tablename); strcat(_data_DB->command, " where Tag=="); sprintf(_data_DB->command, "%s'%s';", _data_DB->command, miss[activeMission].output[i].c_str()); sqlite3_stmt * pStatement; int rc = sqlite3_prepare_v2(_data_DB->db, _data_DB->command, -1, &pStatement, NULL); if (rc == SQLITE_OK){ if (sqlite3_step(pStatement) == SQLITE_ROW) data.append((const char*) sqlite3_column_text(pStatement, 1)); else { printf("data_DB:: Data not yet in DB.\n"); rc=31337; } } else { printf("services_DB:: Error executing SQL statement. rc = %i\n%s\n",rc,_data_DB->command); } sqlite3_finalize(pStatement); char *data_ch = (char *) data.c_str(); char *token = strtok(data_ch, "@"); while(token){ SendMessage(shellSocketFD, token); token = strtok(NULL, "@"); } i++; printf("done sending output data to shell\n"); } } /* CALLED BY: MessageHandler * INPUTS: * OUTPUTS: * * DESCRIPTION: Print a list of the services currently registered and the ID's of the components that registered them */ void ServiceManagementLayer::ListServices() { // generate commandi strcpy(_services_DB->command, "select "); strcat(_services_DB->command, _services_DB->tablename); strcat(_services_DB->command, ".* from "); strcat(_services_DB->command, _services_DB->tablename); strcat(_services_DB->command, ";"); // execute print (select all) command char *errorMsg; int rc = sqlite3_exec(_services_DB->db, _services_DB->command, callback, 0, &errorMsg); if( rc!=SQLITE_OK && rc!=101 ) fprintf(stderr, "SQL error: %s\n", errorMsg); printf("database %s, table %s:\n", _services_DB->filename, _services_DB->tablename); } /* CALLED BY: Reset * INPUTS: * OUTPUTS: * * DESCRIPTION: Clear and reinitialize the mission array, then reload the configuration file */ void ServiceManagementLayer::ReloadConfiguration() { LOG("ServiceManagementLayer:: Reloading Configuration.\n"); free(miss); miss = new Mission[10]; for(int i = 0; i < 10; i++) miss[i].services = new Service[20]; LoadConfiguration(_SML_Config, miss); } /* CALLED BY: constructor * INPUTS: |SML_Config| Address (either relitive or full) of the XML file containing mission data * |mList| Mission array to be modified * OUTPUTS: * * DESCRIPTION: IMPORTANT - See formatting instructions for correct parsing of data * Can currently handle 3 inputs and 3 outputs per service, but easily expandable * Also, can handle one layer of nested conditional statements, but could * be expanded to meet additional needs. Only support now is for straight bool flags, * but support could be added for more complex conditionals later. * * Components assigned to mission during "set active mission" stage so that * components can still continue to register after the configuration is loaded */ void ServiceManagementLayer::LoadConfiguration(const char *SML_Config, Mission* &mList) { TiXmlElement *pMission; TiXmlElement *pService; TiXmlElement *pChild0, *pChild1, *pChild2, *pChild3; TiXmlHandle hRoot(0); //printf("ServiceManagementLayer:: Loading Configuration.\n"); TiXmlDocument doc("."); doc.LoadFile(SML_Config); bool loadOkay = doc.LoadFile(); if(!loadOkay) printf("Loading SML configuration failed: %s\n", SML_Config); TiXmlHandle hDoc(&doc); pMission = hDoc.FirstChildElement().Element(); if(!pMission) printf("No valid root!"); hRoot = TiXmlHandle(pMission); pService = pMission->FirstChildElement(); int32_t mission_num = 0; //Iterate through the missions for(pChild0 = pMission->FirstChildElement(); pChild0 ; \ pChild0 = pChild0->NextSiblingElement()) { int32_t service_num = 0; uint16_t cond_array[] = {0, 0}; //printf("mission_num = %d\n", mission_num); memset(cond_array, 0, 2); for(pChild1 = pChild0->FirstChildElement(); pChild1; \ pChild1 = pChild1->NextSiblingElement()) { int32_t conditional_0 = service_num; for(pChild2 = pChild1->FirstChildElement(); \ pChild2; pChild2 = pChild2->NextSiblingElement()) { service_num++; int32_t conditional_1 = service_num; for(pChild3 = pChild2->FirstChildElement(); \ pChild3; pChild3 = pChild3 ->NextSiblingElement()) { service_num++; mList[mission_num].services[service_num].name = pChild3->Attribute("name"); if(pChild3->Attribute("input1")) mList[mission_num].services[service_num].input[0] = pChild3->Attribute("input1"); if(pChild3->Attribute("input2")) mList[mission_num].services[service_num].input[1] = pChild3->Attribute("input2"); if(pChild3->Attribute("input3")) mList[mission_num].services[service_num].input[2] = pChild3->Attribute("input3"); if(pChild3->Attribute("output1")) mList[mission_num].services[service_num].output[0] = pChild3->Attribute("output1"); if(pChild3->Attribute("output2")) mList[mission_num].services[service_num].output[1] = pChild3->Attribute("output2"); if(pChild3->Attribute("output3")) mList[mission_num].services[service_num].output[2] = pChild3->Attribute("output3"); cond_array[1]++; } if(conditional_1 != service_num){ mList[mission_num].services[conditional_1].name = pChild2->Value(); if(pChild2->Attribute("input_t")) mList[mission_num].services[conditional_1].input[0] = pChild2->Attribute("input_t"); if(pChild2->Attribute("input_f")) mList[mission_num].services[conditional_1].input[1] = pChild2->Attribute("input_f"); } else{ mList[mission_num].services[conditional_1].name = pChild2->Attribute("name"); if(pChild2->Attribute("input1")) mList[mission_num].services[service_num].input[0] = pChild2->Attribute("input1"); if(pChild2->Attribute("input2")) mList[mission_num].services[service_num].input[1] = pChild2->Attribute("input2"); if(pChild2->Attribute("input3")) mList[mission_num].services[service_num].input[2] = pChild2->Attribute("input3"); if(pChild2->Attribute("output1")) mList[mission_num].services[service_num].output[0] = pChild2->Attribute("output1"); if(pChild2->Attribute("output2")) mList[mission_num].services[service_num].output[1] = pChild2->Attribute("output2"); if(pChild2->Attribute("output3")) mList[mission_num].services[service_num].output[2] = pChild2->Attribute("output3"); } mList[mission_num].services[conditional_1].num_conds = cond_array[1]; cond_array[1] = 0; cond_array[0]++; } if(conditional_0 != service_num){ mList[mission_num].services[conditional_0].name = pChild1->Value(); if(pChild1->Attribute("input_t")) mList[mission_num].services[conditional_0].input[0] = pChild1->Attribute("input_t"); if(pChild1->Attribute("input_f")) mList[mission_num].services[conditional_0].input[1] = pChild1->Attribute("input_f"); //printf("---input_t=%s\n", mList[mission_num].services[conditional_0].input[0].c_str()); } else{ mList[mission_num].services[conditional_0].name = pChild1->Attribute("name"); if(pChild1->Attribute("input1")) mList[mission_num].services[service_num].input[0] = pChild1->Attribute("input1"); if(pChild1->Attribute("input2")) mList[mission_num].services[service_num].input[1] = pChild1->Attribute("input2"); if(pChild1->Attribute("input3")) mList[mission_num].services[service_num].input[2] = pChild1->Attribute("input3"); if(pChild1->Attribute("output1")) mList[mission_num].services[service_num].output[0] = pChild1->Attribute("output1"); if(pChild1->Attribute("output2")) mList[mission_num].services[service_num].output[1] = pChild1->Attribute("output2"); if(pChild1->Attribute("output3")) mList[mission_num].services[service_num].output[2] = pChild1->Attribute("output3"); } mList[mission_num].services[conditional_0].num_conds = cond_array[0]; cond_array[0] = 0; service_num++; } //for(int i = 0; i < service_num; i++) //printf("%d, input1=%s, output1=%s\n", i, mList[mission_num].services[i].input[0].c_str(), mList[mission_num].services[i].output[0].c_str()); mList[mission_num].numServices = service_num; mList[mission_num].name = pChild0->Attribute("name"); mList[mission_num].missionID = atoi(pChild0->Attribute("id")); if(pChild0->Attribute("input1")) mList[mission_num].input[0] = pChild0->Attribute("input1"); if(pChild0->Attribute("input2")) mList[mission_num].input[1] = pChild0->Attribute("input2"); if(pChild0->Attribute("input3")) mList[mission_num].input[2] = pChild0->Attribute("input3"); if(pChild0->Attribute("output1")) mList[mission_num].output[0] = pChild0->Attribute("output1"); if(pChild0->Attribute("output2")) mList[mission_num].output[1] = pChild0->Attribute("output2"); if(pChild0->Attribute("output3")) mList[mission_num].output[2] = pChild0->Attribute("output3"); //printf("mis, input1=%s, output1=%s\n", mList[mission_num].input[0].c_str(), mList[mission_num].output[0].c_str()); mission_num++; } } /* CALLED BY: MessageHandler * INPUTS: |ID| The ID number of the engine to be registered * OUTPUTS: * * DESCRIPTION: Sends a registration message onto the shell and sends the ACK back to the component */ void ServiceManagementLayer::RegisterCognitiveEngine(int32_t ID) { SendMessage(shellSocketFD, "register_engine_cognitive"); LOG("ServiceManagementLayer:: CE registration message forwarded to shell.\n"); char buffer[256]; memset(buffer, 0, 256); ReadMessage(shellSocketFD, buffer); SendMessage(CE_List[ID].FD, buffer); TransferRadioConfiguration(ID); memset(buffer, 0, 256); TransferExperience(ID); memset(buffer, 0, 256); numberOfCognitiveEngines++; CE_Present = true; } /* CALLED BY: MessageHandler * INPUTS: |ID| The ID number of the engine to have it's services deregistered * OUTPUTS: * * DESCRIPTION: Deletes individual services from the DB * NOTE THAT this function only needs to be called if service deregistration is going * to be done at a different time than component deregistration; it is handled * more efficiently and directly during that deregistration process. */ void ServiceManagementLayer::DeregisterServices(int32_t ID) { char buffer[256]; memset(buffer, 0, 256); ReadMessage(CE_List[ID].FD, buffer); strcpy(_services_DB->command, "DELETE FROM "); strcat(_services_DB->command, _services_DB->tablename); strcat(_services_DB->command, " WHERE ID_Num IN (SELECT"); sprintf(_services_DB->command, " %s %d",_services_DB->command, ID); strcat(_services_DB->command, " FROM "); strcat(_services_DB->command, _services_DB->tablename); strcat(_services_DB->command, " WHERE Service_Name"); strcat(_services_DB->command, "=="); sprintf(_services_DB->command, "%s'%s');", _services_DB->command, buffer); char *errorMsg; int rc = sqlite3_exec(_services_DB->db, _services_DB->command, callback, 0, &errorMsg); if( rc!=SQLITE_OK && rc!=101 ) fprintf(stderr, "SQL error: %s\n", errorMsg); } /* CALLED BY: MessageHandler * INPUTS: |ID| The ID number of the engine to have it's services deregistered * OUTPUTS: * * DESCRIPTION: Deletes the contact info for the cognitive engine, forwards a deregistration message to the shell * Also, deletes the services from the DB */ void ServiceManagementLayer::DeregisterCognitiveEngine(int32_t ID) { LOG("ServiceManagementLayer:: CE deregistration message forwarded to shell.\n"); numberOfCognitiveEngines--; if(numberOfCognitiveEngines == 0) CE_Present = false; SendMessage(shellSocketFD, "deregister_engine_cognitive"); char buffer[256]; memset(buffer, 0, 256); ReadMessage(shellSocketFD, buffer); SendMessage(CE_List[ID].FD, buffer); if(strcmp("deregister_ack", buffer) != 0) { ERROR(1, "SML:: Failed to close CE socket\n"); } //Deregister the services strcpy(_services_DB->command, "DELETE FROM "); strcat(_services_DB->command, _services_DB->tablename); strcat(_services_DB->command, " WHERE "); strcat(_services_DB->command, "ID_Num"); strcat(_services_DB->command, "=="); sprintf(_services_DB->command, "%s%d;", _services_DB->command, ID); char *errorMsg; int rc = sqlite3_exec(_services_DB->db, _services_DB->command, callback, 0, &errorMsg); if( rc!=SQLITE_OK && rc!=101 ) fprintf(stderr, "SQL error: %s\n", errorMsg); CE_List[ID].FD = -1; CE_List[ID].ID_num = -1; LOG("Cognitive Radio Shell:: CE Socket closed for engine #%d.\n", ID); } /* CALLED BY: test class * INPUTS: * OUTPUTS: * * DESCRIPTION: Sets up a server socket and listens for communication on either that or the shell socket */ void ServiceManagementLayer::StartSMLServer() { printf("Ready for CE Signal! (registration done)\n"); struct timeval selTimeout; int32_t running = 1; int32_t port, rc, new_sd = 1; int32_t desc_ready = 1; int32_t timeout = 10; //If there is, call the MessageHandler with the Shell_Msg code of -1 fd_set sockSet, shellSet; cogEngSrv = CreateTCPServerSocket(2044); int32_t maxDescriptor = cogEngSrv; if(InitializeTCPServerPort(cogEngSrv) == -1) ERROR(1,"Error initializing primary port\n"); int i = 10000000; //TODO change to "running" if endpoint can be reached while (i>0) { i--; /* Zero socket descriptor vector and set for server sockets */ /* This must be reset every time select() is called */ FD_ZERO(&sockSet); FD_SET(cogEngSrv, &sockSet); for(int k = 0; k < Current_ID; k++){ if(CE_List[k].ID_num != -1) FD_SET(CE_List[k].FD, &sockSet); } //printf("k=%d, CID=%d\n", k, CE_List[k].FD); /* Timeout specification */ /* This must be reset every time select() is called */ selTimeout.tv_sec = 0; /* timeout (secs.) */ selTimeout.tv_usec = 0; /* 0 microseconds */ //Changed both to zero so that select will check messages from the shell instead of blocking //when there is no command from the CE's to be processed //Check if there is a message on the socket waiting to be read rc = select(maxDescriptor + 1, &sockSet, NULL, NULL, &selTimeout); //printf("rc=%d\n", rc); if(rc == 0){ //LOG("No echo requests for %i secs...Server still alive\n", timeout); FD_ZERO(&shellSet); FD_SET(shellSocketFD, &shellSet); selTimeout.tv_sec = 0; selTimeout.tv_usec = 0; //Check if there is a message on the shell socket ready to be processed int rc2 = select(shellSocketFD + 1, &shellSet, NULL, NULL, &selTimeout); //printf("rc2=%d\n", rc2); //If there is, call the MessageHandler with the Shell_Msg code of -1 if(FD_ISSET(shellSocketFD, &shellSet)){ //printf("shell_msg, %d\n", rc2); MessageHandler(-1);} } else { desc_ready = rc; for(port = 0; port <= maxDescriptor && desc_ready > 0; port++) { if(FD_ISSET(port, &sockSet)) { desc_ready -= 1; //Check if request is new or on an existing open descriptor if(port == cogEngSrv) { //If new, assign it a descriptor and give it an ID new_sd = AcceptTCPConnection(port); if(new_sd < 0) break; CE_List[Current_ID].FD = new_sd; CE_List[Current_ID].ID_num = Current_ID; MessageHandler(Current_ID); Current_ID++; FD_SET(new_sd,&sockSet); if(new_sd > maxDescriptor) maxDescriptor = new_sd; } else { //If old, figure out which ID it coresponds to and handle it accordingly for(int16_t z = 0; z < Current_ID; z++) { if(CE_List[z].FD == port){ MessageHandler(z);} } } } } } } /* Close sockets */ close(cogEngSrv); //delete &cogEngSrv; return; } /*TiXmlElement *pMission; TiXmlElement *pService; TiXmlElement *pChild0, *pChild1, *pChild2, *pChild3; TiXmlHandle hRoot(0); //printf("ServiceManagementLayer:: Loading Configuration.\n"); TiXmlDocument doc("."); doc.LoadFile(SML_Config); bool loadOkay = doc.LoadFile(); if(!loadOkay) printf("Loading SML configuration failed: %s\n", SML_Config); TiXmlHandle hDoc(&doc); pMission = hDoc.FirstChildElement().Element(); if(!pMission) printf("No valid root!"); hRoot = TiXmlHandle(pMission); pService = pMission->FirstChildElement(); int32_t mission_num = 0; //Iterate through the missions for(pChild0 = pMission->FirstChildElement(); pChild0 ; \ pChild0 = pChild0->NextSiblingElement()) { int32_t service_num = 0; uint16_t cond_array[] = {0, 0}; //printf("mission_num = %d\n", mission_num); memset(cond_array, 0, 2); for(pChild1 = pChild0->FirstChildElement(); pChild1; \ pChild1 = pChild1->NextSiblingElement()) { int32_t conditional_0 = service_num; for(pChild2 = pChild1->FirstChildElement(); \ pChild2; pChild2 = pChild2->NextSiblingElement()) { service_num++; int32_t conditional_1 = service_num; for(pChild3 = pChild2->FirstChildElement(); \ pChild3; pChild3 = pChild3 ->NextSiblingElement()) { service_num++; mList[mission_num].services[service_num].name = pChild3->Attribute("name"); if(pChild3->Attribute("input1")) mList[mission_num].services[service_num].input[0] = pChild3->Attribute("input1"); if(pChild3->Attribute("input2")) mList[mission_num].services[service_num].input[1] = pChild3->Attribute("input2"); if(pChild3->Attribute("input3")) mList[mission_num].services[service_num].input[2] = pChild3->Attribute("input3"); if(pChild3->Attribute("output1")) mList[mission_num].services[service_num].output[0] = pChild3->Attribute("output1"); if(pChild3->Attribute("output2")) mList[mission_num].services[service_num].output[1] = pChild3->Attribute("output2"); if(pChild3->Attribute("output3")) mList[mission_num].services[service_num].output[2] = pChild3->Attribute("output3"); cond_array[1]++; } if(conditional_1 != service_num){ mList[mission_num].services[conditional_1].name = pChild2->Value(); if(pChild2->Attribute("input_t")) mList[mission_num].services[conditional_1].input[0] = pChild2->Attribute("input_t"); if(pChild2->Attribute("input_f")) mList[mission_num].services[conditional_1].input[1] = pChild2->Attribute("input_f"); } else{ mList[mission_num].services[service_num].name = pChild2->Attribute("name"); if(pChild2->Attribute("input1")) mList[mission_num].services[service_num].input[0] = pChild2->Attribute("input1"); if(pChild2->Attribute("input2")) mList[mission_num].services[service_num].input[1] = pChild2->Attribute("input2"); if(pChild2->Attribute("input3")) mList[mission_num].services[service_num].input[2] = pChild2->Attribute("input3"); if(pChild2->Attribute("output1")) mList[mission_num].services[service_num].output[0] = pChild2->Attribute("output1"); if(pChild2->Attribute("output2")) mList[mission_num].services[service_num].output[1] = pChild2->Attribute("output2"); if(pChild2->Attribute("output3")) mList[mission_num].services[service_num].output[2] = pChild2->Attribute("output3"); } mList[mission_num].services[conditional_1].num_conds = cond_array[1]; cond_array[1] = 0; cond_array[0]++; } if(conditional_0 != service_num){ mList[mission_num].services[conditional_0].name = pChild1->Value(); if(pChild1->Attribute("input_t")) mList[mission_num].services[conditional_0].input[0] = pChild1->Attribute("input_t"); if(pChild1->Attribute("input_f")) mList[mission_num].services[conditional_0].input[1] = pChild1->Attribute("input_f"); printf("if detected %s, %s\n", pChild1->Attribute("input_f"), pChild1->Attribute("input_t")); } else{ mList[mission_num].services[service_num].name = pChild1->Attribute("name"); if(pChild1->Attribute("input1")) mList[mission_num].services[service_num].input[0] = pChild1->Attribute("input1"); if(pChild1->Attribute("input2")) mList[mission_num].services[service_num].input[1] = pChild1->Attribute("input2"); if(pChild1->Attribute("input3")) mList[mission_num].services[service_num].input[2] = pChild1->Attribute("input3"); if(pChild1->Attribute("output1")) mList[mission_num].services[service_num].output[0] = pChild1->Attribute("output1"); if(pChild1->Attribute("output2")) mList[mission_num].services[service_num].output[1] = pChild1->Attribute("output2"); if(pChild1->Attribute("output3")) mList[mission_num].services[service_num].output[2] = pChild1->Attribute("output3"); } mList[mission_num].services[conditional_0].num_conds = cond_array[0]; //printf("hello\n"); cond_array[0] = 0; service_num++; } for(int i = 0; i < service_num; i++) printf("input1=%s, output1=%s\n", mList[mission_num].services[i].input[0].c_str(), mList[mission_num].services[service_num].output[0].c_str()); mList[mission_num].numServices = service_num; mList[mission_num].name = pChild0->Attribute("name"); mList[mission_num].missionID = atoi(pChild0->Attribute("id")); if(pChild0->Attribute("input1")) mList[mission_num].input[0] = pChild0->Attribute("input1"); if(pChild0->Attribute("input2")) mList[mission_num].input[1] = pChild0->Attribute("input2"); if(pChild0->Attribute("input3")) mList[mission_num].input[2] = pChild0->Attribute("input3"); if(pChild0->Attribute("output1")) mList[mission_num].output[0] = pChild0->Attribute("output1"); if(pChild0->Attribute("output2")) mList[mission_num].output[1] = pChild0->Attribute("output2"); if(pChild0->Attribute("output3")) mList[mission_num].output[2] = pChild0->Attribute("output3"); printf("input1=%s, outpu1=%s\n", mList[mission_num].input[0].c_str(), mList[mission_num].output[0].c_str()); mission_num++; }*/