![]() |
VR-Forces 4.0.4 Class Documentation
|
This class allows the user to type in commands that will be parsed and used to supply information to the remote controller.
/******************************************************************************* ** Copyright (c) 2002 MAK Technologies, Inc. ** All rights reserved. *******************************************************************************/ /******************************************************************************* ** $RCSfile: textIf.h,v $ $Revision: 1.13 $ $State: Exp $ *******************************************************************************/ #ifndef DtTextInterface_H_ #define DtTextInterface_H_ #include <vlutil/vlUtil.h> #include "vrfcontrol/vrfController.h" //This class provides a text interface for controlling VR-Forces over the network. //This interface currently supports the following actions: // //1. load <filename> // load a scenario file //2. save <filename> // save the current scenario //3. new <db filename> // create a new scenario //4. close // close the current scenario //5. rewind // rewind the current scenario to its start state //6. run // sends a message interface request to run the current scenario //6a. simrun // sends a sim mgmt start PDU to run the current scenario //7. simpause // sends a sim mgmt stop PDU to pause the current scenario //7a. pause // sends a sim mgmt pause request to pause the current scenario //8. list // list all the backends that are currently running //9. create [ waypoint | route | tank | aggregate] // create a VR-Forces waypoint, route, tank, or tank aggregate //10. delete [ <object_name> | <object_id> ] // delete a VR-Forces object by name or by object ID //11. set [ plan | label | location | fuel | target | restore ] // set a VR-Forces object attribute //12. task [ patrolRoute | moveToPoint | follow | wait ] // task a VR-Forces entity //13. aggregate "name1" "name2" "name3" // aggregate 3 VR-Forces entities together //14. quit // quit the application class DtTextInterface { public: //constructor DtTextInterface(DtVrfRemoteController* controller); //destructor virtual ~DtTextInterface(); //Process entered text void readStdin(); //Set/Get time to quit virtual void setTimeToQuit(bool yesNo); virtual bool timeToQuit() const; //Get remote vrf controller pointer virtual DtVrfRemoteController* controller() const; public: //Functions for parsing command strings and calling //the corresponding methods on DtVrfRemoteController. virtual void createWaypoint(char* str); virtual void createRoute(char* str); virtual void createTank(char* str); virtual void createAggregate(char* str); virtual void setPlan(char* str); virtual void setLabel(char* str); virtual void setLocation(char* str); virtual void setFuel(char* str); virtual void setTarget(char* str); virtual void setRestore(char* str); virtual void patrolRoute(char* str); virtual void moveToPoint(char* str); virtual void follow(char* str); virtual void wait(char* str); virtual void bigRoute(int iPoints); public: //------------------------------------------------------- //Text callback functions //------------------------------------------------------- //Called in response to "list" static void listCmd(char* s, DtTextInterface* a); //Called in response to "load" static void loadCmd(char* s, DtTextInterface* a); //Called by DtVrfRemoteController::loadScenario when back-ends are missing static bool missingBackends(DtBackendMap* map, void* usr); //Called in response to "new" static void newCmd(char* s, DtTextInterface* a); //Called in response to "save" static void saveCmd(char* s, DtTextInterface* a); //Called in response to "create" static void createCmd(char* s, DtTextInterface* a); //Called in response to "set" static void setCmd(char* s, DtTextInterface* a); //Called in response to "task" static void taskCmd(char* s, DtTextInterface* a); //Called in response to "aggregate" static void aggregateCmd(char* s, DtTextInterface* a); //Called in response to "delete" static void deleteCmd(char* s, DtTextInterface* a); //Called in response to "close" static void closeCmd(char* s, DtTextInterface* a); //Called in response to "monitor" static void monitorResourcesCmd(char* s, DtTextInterface* a); //Called in response to "unmonitor" static void removeMonitorResourcesCmd(char* s, DtTextInterface* a); //Called in response to "rewind" static void rewindCmd(char* s, DtTextInterface* a); //Called in response to "run" static void runCmd(char* s, DtTextInterface* a); //Called in response to "pause" static void pauseCmd(char* s, DtTextInterface* a); //Called in response to "simrun" static void simrunCmd(char* s, DtTextInterface* a); //Called in response to "simpause" static void simpauseCmd(char* s, DtTextInterface* a); //Called in response to "quit" or "q" static void quitCmd(char* s, DtTextInterface* a); //Called in response to "help" or "?" or "h" static void helpCmd(char* s, DtTextInterface* a); //Called in response to "help" or "?" or "h" static void bigRouteCmd(char* s, DtTextInterface* a); //------------------------------------------------------- //DtVrfRemoteController callback functions //------------------------------------------------------- //Backend arrival/departure callbacks static void backendAddedCb(const DtSimulationAddress& addr, void* usr); static void backendRemovedCb(const DtSimulationAddress& addr, void* usr); //Backend loaded/saved callbacks static void backendLoadedCb(const DtSimulationAddress& addr, void* usr); static void backendSavedCb(const DtSimulationAddress& addr, DtSaveResult result, void* usr); //Scenario loaded/saved callbacks static void ScenarioLoadedCb(void* usr); static void ScenarioSavedCb(void* usr); //VR-Forces object created callback static void vrfObjectCreatedCb( const DtString& name, const DtEntityIdentifier& id, void* usr); //Plan callbacks static void planCb(const DtString& name, const DtList& stmts, bool append, void* usr); static void planStmtCb(const DtString& id, DtPlanStatus status, void* usr); static void planCompleteCb(const DtString& id, void* usr); //Callback used to process monitor resource responses static void resourcesProcessedCb(DtSimMessage* msg, void* usr); //Callback used to receive object console messages from the remote control //API. static void objectConsoleMessageCb(const DtEntityIdentifier& id, int notifyLevel, const DtString& message, void* usr); protected: //Present commmand line interface. void prompt(); //Parse user input, call corresponding function void processCmd(char *buff); protected: bool myTimeToQuit; DtVrfRemoteController* myController; DtSelectParam sparam; static unsigned int theNextAggregateId; static unsigned int theNextSubordinateId; private: int kbInterest; }; #endif
/******************************************************************************* ** Copyright (c) 2011 MAK Technologies, Inc. ** All rights reserved. *******************************************************************************/ #include "textIf.h" #include <vlutil/vlPrint.h> #include <vl/hostStructs.h> #include <vlpi/disEnums.h> #include "vrfutil/backendMap.h" #include "vrfutil/backendMapEntry.h" #include "vrfplan/plan.h" #include "vrfplan/planBlock.h" #include "vrfplan/ifStmt.h" #include "vrfplan/ifBlock.h" #include "vrfutil/ceResourceOp.h" #include "vrfutil/evaluator.h" #include "vrftasks/setHeading.h" #include "vrftasks/setSpeed.h" #include "vrftasks/moveToTask.h" #include "vrftasks/waitDurTask.h" #include "vrfplan/setStmt.h" #include "vrfplan/taskStmt.h" #include "vrfMsgTransport/vrfMsgIf.h" #include "vrfmsgs/ifResMonReq.h" #include "vrfmsgs/ifaceMsg.h" #include "vrfmsgs/ifResMonResponse.h" #include <vl/startInter.h> #include <vl/stopInter.h> #include <time.h> #include <ctype.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #if DT_HAVE_UNISTD_H #include <unistd.h> #endif #if !WIN32 #include <sys/fcntl.h> #else #include <fcntl.h> #endif #if WIN32 #include <io.h> #include <conio.h> #endif #if !WIN32 #define UNBLOCK() setBlock(0, 0) #define RESTORE() setBlock(0, 1) #else #define UNBLOCK() #define RESTORE() #endif const char* terrainDir = "..\\userData\\terrains\\"; const char* scenariosDir = "..\\userData\\scenarios\\"; static bool monitorResourcesCallbackAdded = false; //------------------------------------------------------- //Initialize command chart //------------------------------------------------------- typedef void (*CmdFunc)(char *, DtTextInterface *a); struct Cmd { char *name; CmdFunc func; }; Cmd allCmds[] = { "list", DtTextInterface::listCmd, "load", DtTextInterface::loadCmd, "new", DtTextInterface::newCmd, "save", DtTextInterface::saveCmd, "create", DtTextInterface::createCmd, "set", DtTextInterface::setCmd, "task", DtTextInterface::taskCmd, "monitor", DtTextInterface::monitorResourcesCmd, "unmonitor", DtTextInterface::removeMonitorResourcesCmd, "aggregate",DtTextInterface::aggregateCmd, "delete", DtTextInterface::deleteCmd, "close", DtTextInterface::closeCmd, "rewind", DtTextInterface::rewindCmd, "bigroute", DtTextInterface::bigRouteCmd, "run", DtTextInterface::runCmd, "simrun", DtTextInterface::simrunCmd, "simpause", DtTextInterface::simpauseCmd, "pause", DtTextInterface::pauseCmd, "quit", DtTextInterface::quitCmd, "q", DtTextInterface::quitCmd, "help", DtTextInterface::helpCmd, "h", DtTextInterface::helpCmd, "?", DtTextInterface::helpCmd, }; int nCmds = DtNUMBER(allCmds); unsigned int DtTextInterface::theNextAggregateId = 1; unsigned int DtTextInterface::theNextSubordinateId = 1; void DtTextInterface::helpCmd(char* s, DtTextInterface* a) { if (!strncmp(s, "create", 6)) { DtInfo("\n" "\tcreate waypoint <\"name\"> <p1>\n" "\tcreate route <\"name\"> <p1 p2 ... pn>\n" "\tcreate tank <p1>\n" "\tcreate aggregate <p1>\n" "\t\twhere px has form x,y,z\n"); } else if (!strncmp(s, "set", 3)) { DtInfo("\n" "\tset plan <\"name\">\n" "\tset label <objectId> <\"label\">\n" "\tset location <\"name\"> <pt>\n" "\tset fuel <\"name\"> <double>\n" "\tset target <\"name\"> <\"name\">\n" "\tset restore <\"name\"> \n"); } else if (!strncmp(s, "task", 4)) { DtInfo("\n" "\ttask patrolRoute <\"name\"> <\"route\">\n" "\ttask moveToPoint <\"name\"> <\"waypoint\">\n" "\ttask follow <\"name\"> <\"leader\">\n" "\ttask wait <\"name\"> <time>\n"); } else if (!strncmp(s, "monitor", 4)) { DtInfo("\nmonitor <\"resource\" | \"ALL\"> <\"entity name\"> [update time in seconds]\n"); } else if (!strncmp(s, "unmonitor", 4)) { DtInfo("\nunmonitor <\"resource\" | \"ALL\"> <\"entity name\">\n"); } else if (!strncmp(s, "simrun", 4)) { DtInfo("simrun [site:host]\n"); } else if (!strncmp(s, "simpause", 4)) { DtInfo("simpause [site:host]\n"); } else if (!strncmp(s, "aggregate", 4)) { DtInfo("\naggregate <name1> <name2> <name3>\n"); } else { DtInfo("\n" "\tload <\"filename\">\n" "\tsave <\"filename\">\n" "\tnew <\"terrain db filename\">\n" "\tclose\n" "\trewind\n" "\trun\n" "\tpause\n" "\tsimrun\n" "\tsimpause\n" "\tlist\n" "\tcreate [ waypoint | route | tank | aggregate]\n" "\tdelete [ <object_name> | <object_id> ]\n" "\tset [ plan | label | location | fuel | target | restore ]\n" "\ttask [ patrolRoute | moveToPoint | follow | wait ]\n" "\taggregate <name1> <name2> <name3>\n" "\tmonitor <\"resource\" | \"ALL\"> <\"entity name\"> [update time in seconds]\n" "\tunmonitor <\"resource\" | \"ALL\"> <\"entity name\">\n" "\thelp <command_name>\n" "\tquit\n"); } } void DtTextInterface::quitCmd(char* s, DtTextInterface* a) { a->setTimeToQuit(true); } void DtTextInterface::listCmd(char* s, DtTextInterface* a) { int i = 0; DtInfo("Listing known backends\n"); DtList backends = a->controller()->backends(); for (DtListItem* item = backends.first(); item; item = item->next()) { DtSimulationAddress* addr = (DtSimulationAddress*) item->data(); DtInfo("\t%d) %s\n", ++i, addr->string()); if (i == 5) DtInfo("\n"); } DtInfo("Current state: %s\n", DtControlTypeString( a->controller()->backendsControlState())); } void DtTextInterface::loadCmd(char* str, DtTextInterface* a) { #if WIN32 str[strlen(str)]='\0'; #else str[strlen(str)-1]='\0'; #endif if (!strcmp(str, "")) return; char *name; name = strtok(str, "\""); a->controller()->loadScenario( DtFilename(DtString(scenariosDir) + DtString(name)), missingBackends, a); } bool DtTextInterface::missingBackends(DtBackendMap* map, void* usr) { DtWarn("Cannot load scenario due to missing backends:\n"); DtListItem* item; for (item = map->first(); item; item = item->next()) { DtBackendMapEntry* entry = (DtBackendMapEntry*)item->data(); if (entry) { DtWarn("\t%s\n", entry->fromAddress().string()); } } return false; } void DtTextInterface::bigRouteCmd(char* str, DtTextInterface* a) { #if WIN32 str[strlen(str)]='\0'; #else str[strlen(str)-1]='\0'; #endif int iPoints = 0; if (strlen(str) == 0) { iPoints = 800; } else { iPoints = atoi(str); } a->bigRoute(iPoints); } void DtTextInterface::bigRoute(int iPoints) { srand(time(NULL)); DtList verts; for (int i = 0; i < iPoints; i++) { verts.add(new DtVector((double)((double)rand() / (double)RAND_MAX) * 2000., (double)((double)rand() / (double)RAND_MAX) * 2000., 100.)); } controller()->createRoute(verts); DtListItem* item = verts.first(); while (item) { delete (DtVector*)item->data(); item = item->next(); } } void DtTextInterface::saveCmd(char* str, DtTextInterface* a) { #if WIN32 str[strlen(str)]='\0'; #else str[strlen(str)-1]='\0'; #endif if (!strcmp(str, "")) return; if (!strstr(str, ".scn")) strcat(str, ".scn"); char *name; name = strtok(str, "\""); a->controller()->saveScenario( DtFilename(DtString(scenariosDir) + DtString(name))); } void DtTextInterface::rewindCmd(char* str, DtTextInterface* a) { a->controller()->rewind(); } void DtTextInterface::closeCmd(char* s, DtTextInterface* a) { a->controller()->unsubscribeAllPlans(); a->controller()->removeAllPlanStatementCallbacks(); a->controller()->removeAllPlanCompleteCallbacks(); a->controller()->closeScenario(); } void DtTextInterface::newCmd(char* str, DtTextInterface* a) { #if WIN32 str[strlen(str)]='\0'; #else str[strlen(str)-1]='\0'; #endif if (!strcmp(str, "")) { DtInfo("Give me a terrain database, please!\n> "); } else { a->controller()->unsubscribeAllPlans(); a->controller()->removeAllPlanStatementCallbacks(); a->controller()->removeAllPlanCompleteCallbacks(); char *name; name = strtok(str, "\""); DtFilename terrainFilename(DtString(terrainDir) + DtString(name)); a->controller()->newScenario(terrainFilename, terrainFilename); } } void DtTextInterface::createCmd(char* str, DtTextInterface* a) { #if WIN32 str[strlen(str)]='\0'; #else str[strlen(str)-1]='\0'; #endif if (!strcmp(str, "")) { DtInfo("Create what?!\n"); helpCmd("create", NULL); } else if (!strncmp(str, "waypoint", 8)) { a->createWaypoint(str); } else if (!strncmp(str, "route", 5)) { a->createRoute(str); } else if (!strncmp(str, "tank", 4)) { a->createTank(str); } else if (!strncmp(str,"aggregate",9)) { a->createAggregate(str); } } void DtTextInterface::createAggregate(char* str) { //Let's parse the command string and then call //the corresponding method on DtVrfRemoteController. char *point; point = strtok(&str[9], " "); if (!point) { DtInfo("\tusage: aggregate <geocentric_location>\n"); return; } DtVector vec; sscanf(point, "%lf,%lf,%lf", &vec[0], &vec[1], &vec[2]); //Here we are creating the indivual parts. We register with the vrfObjectCreatedCb callback because //we can't add the subordinates to our aggregate until they've actually been created. Timing can be tricky here if the indivual is //created before the aggregate. In this simple example we create the aggregate entity first then it's subordinates. //When manaully creating aggregates and their components this timing must be considered. One means is to check if the aggregate has been created //and if not, keep a list of the individuals and in a callback once the desired aggregate has been created then add them to the aggregate's //organization. //Create the aggregate entity at desired position with name "tankPlt" DtString pltName = "Tank_Plt_" + DtString(theNextAggregateId++); controller()->createAggregate( vrfObjectCreatedCb, (void*)myController, DtEntityType(11, 1, 225, 3, 0, 0, 0), vec, DtForceFriendly, 0,pltName); //Now create the individual tanks that will make up this aggregate DtString subName = "Plt_Sub_"; controller()->createEntity( vrfObjectCreatedCb, (void*)myController, DtEntityType(1, 1, 225, 1, 1, 3, 0), vec, DtForceFriendly, 90.0,subName + DtString(theNextSubordinateId++)); //Change the offet from each other to create an intial triangle formation vec[DtX]+=10; controller()->createEntity( vrfObjectCreatedCb, (void*)myController, DtEntityType(1, 1, 225, 1, 1, 3, 0), vec, DtForceFriendly, 90.0,subName + DtString(theNextSubordinateId++)); vec[DtY]+=10; controller()->createEntity( vrfObjectCreatedCb, (void*)myController, DtEntityType(1, 1, 225, 1, 1, 3, 0), vec, DtForceFriendly, 90.0,subName + DtString(theNextSubordinateId++)); } void DtTextInterface::resourcesProcessedCb(DtSimMessage* msg, void* usr) { DtIfServerRequestResponse* s = (DtIfServerRequestResponse*) ((DtSimInterfaceMessage*) msg)->interfaceContent(); DtString str; if (s->type() == DtResourceMonitorResponseMessageType) { s->printDataToString(str); DtInfo("%s\n", str.string()); } } void DtTextInterface::monitorResourcesCmd(char * str, DtTextInterface* a) { if (!strcmp(str, "")) { DtInfo("Monitor what?!\n"); helpCmd("monitor", NULL); } else { char *res; char *entity; char *time; char* temp; int iTime = 10; res = strtok(str, "\""); temp = strtok(NULL, "\""); entity = strtok(NULL, "\""); time = strtok(NULL, "\""); DtIfResourceMonitorRequest req; req.setObjectName(entity); if (time) { iTime = atoi(time); } req.setMonitorPeriod(iTime); req.setMonitorBy(DtCondExprSimTimeType); if (strcmp(res, "ALL")) { req.addMonitoredItem(res); } if (!monitorResourcesCallbackAdded) { a->controller()->vrfMessageInterface()->addMessageCallback( DtResourceMonitorResponseMessageType, resourcesProcessedCb, a); monitorResourcesCallbackAdded = true; } a->controller()->vrfMessageInterface()->createAndDeliverMessage(DtSimSendToAll, req); } } void DtTextInterface::removeMonitorResourcesCmd(char * str, DtTextInterface* a) { if (!strcmp(str, "")) { DtInfo("unmonitor what?!\n"); helpCmd("unmonitor", NULL); } else { char *res; char *entity; char* temp; res = strtok(str, "\""); temp = strtok(NULL, "\""); entity = strtok(NULL, "\""); temp = strtok(NULL, "\""); DtIfResourceMonitorRequest req; req.setObjectName(entity); req.setMonitorPeriod(10); req.setMonitorBy(DtCondExprSimTimeType); if (strcmp(res, "ALL")) { req.addMonitoredItem(res); } req.setStopMonitoring(true); a->controller()->vrfMessageInterface()->createAndDeliverMessage(DtSimSendToAll, req); } } void DtTextInterface::aggregateCmd(char* str, DtTextInterface* a) { #if WIN32 str[strlen(str)]='\0'; #else str[strlen(str)-1]='\0'; #endif if (!strcmp(str, "")) { DtInfo("Aggregate what?!\n"); helpCmd("aggregate", NULL); } else { char *e1, *e2, *e3, *temp; e1 = strtok(str, "\""); temp = strtok(NULL, "\""); e2 = strtok(NULL, "\""); temp = strtok(NULL, "\""); e3 = strtok(NULL, "\""); temp = strtok(NULL, "\""); DtList entities; if (e1) entities.add(new DtString(e1)); if (e2) entities.add(new DtString(e2)); if (e3) entities.add(new DtString(e3)); a->controller()->createAggregate( DtEntityType(11, 1, 0, 3, 2, 0, 0), DtVector(10.0, 10.0, 10.0), DtForceFriendly, 0.0, &entities); //empty list of entities DtListItem* next = NULL; for (DtListItem* item = entities.first(); item; item = next) { next = item->next(); delete (DtString*) entities.remove(item); } } } void DtTextInterface::setCmd(char* str, DtTextInterface* a) { #if WIN32 str[strlen(str)]='\0'; #else str[strlen(str)-1]='\0'; #endif if (!strcmp(str, "")) { DtInfo("Set what?!\n"); helpCmd("set", NULL); } else if (!strncmp(str, "plan", 4)) { a->setPlan(str); } else if (!strncmp(str, "label", 5)) { a->setLabel(str); } else if (!strncmp(str, "location", 8)) { a->setLocation(str); } else if (!strncmp(str, "fuel", 4)) { a->setFuel(str); } else if (!strncmp(str, "target", 6)) { a->setTarget(str); } else if (!strncmp(str, "restore", 7)) { a->setRestore(str); } } void DtTextInterface::taskCmd(char* str, DtTextInterface* a) { #if WIN32 str[strlen(str)]='\0'; #else str[strlen(str)-1]='\0'; #endif if (!strcmp(str, "")) { DtInfo("Task what?!\n"); helpCmd("task", NULL); } else if (!strncmp(str, "patrolRoute", 11)) { a->patrolRoute(str); } else if (!strncmp(str, "moveToPoint", 11)) { a->moveToPoint(str); } else if (!strncmp(str, "follow", 6)) { a->follow(str); } else if (!strncmp(str, "wait", 4)) { a->wait(str); } } void DtTextInterface::deleteCmd(char* s, DtTextInterface* a) { #if WIN32 s[strlen(s)]='\0'; #else s[strlen(s)-1]='\0'; #endif if (strchr(s, ':')) { a->controller()->deleteObject(DtEntityIdentifier(s)); } else { char* strname = strtok(s, "\""); a->controller()->deleteObject(DtString(strname)); } } void DtTextInterface::runCmd(char* s, DtTextInterface* a) { a->controller()->run(); } void DtTextInterface::simrunCmd(char* s, DtTextInterface* a) { DtStartResumeInteraction ds; #if WIN32 s[strlen(s)]='\0'; #else s[strlen(s)-1]='\0'; #endif //if no arguments, send it to all backends if (strcmp(s, "") == 0) { ds.setReceiverId(DtEntityIdentifier(DtSimSendToAll,0)); } else { //get the backend id if (strchr(s, ':')) { ds.setReceiverId(DtEntityIdentifier(DtSimulationAddress(s),0)); } else { DtWarn << "Start who? (use site:host)"; return; } } ds.setSenderId(DtEntityIdentifier(a->controller()->simulationAddress(),0)); a->controller()->vrfMessageInterface()->exerciseConn()->sendStamped(ds); } void DtTextInterface::pauseCmd(char* s, DtTextInterface* a) { a->controller()->pause(); } void DtTextInterface::simpauseCmd(char* s, DtTextInterface* a) { DtStopFreezeInteraction ds; #if WIN32 s[strlen(s)]='\0'; #else s[strlen(s)-1]='\0'; #endif //if no arguments, send it to all backends if (strcmp(s, "") == 0) { ds.setReceiverId(DtEntityIdentifier(DtSimSendToAll,0)); } else { //get the backend id site:host if (strchr(s, ':')) { ds.setReceiverId(DtEntityIdentifier(DtSimulationAddress(s),0)); } else { DtWarn << "Stop who? (use site:host)"; return; } } ds.setSenderId(DtEntityIdentifier(a->controller()->simulationAddress(),0)); a->controller()->vrfMessageInterface()->exerciseConn()->sendStamped(ds); } #ifdef _PowerUX int strcasecmp(const char *s1, const char *s2) { char *s1Temp = strdup(s1); char *s2Temp = strdup(s2); for (int strIndex= 0; strIndex< strlen(s1Temp); strIndex++) if ((64 < s1Temp[strIndex]) && (s1Temp[strIndex] < 91)) s1Temp[strIndex] += 32; for (int strIndex= 0; strIndex< strlen(s2Temp); strIndex++) if ((64 < s2Temp[strIndex]) && (s2Temp[strIndex] < 91)) s2Temp[strIndex] += 32; int retVal = (strcmp(s1Temp,s2Temp)); free (s1Temp); free (s2Temp); return retVal; } #endif #if WIN32 static char nbdf_buff[128]; static int nbdf_ptr=0; char *non_blocking_dos_fgets(char *buff, int size, FILE *f) { while (_kbhit()) { nbdf_buff[nbdf_ptr] = _getche(); if ( nbdf_buff[nbdf_ptr] == '\r' || nbdf_buff[nbdf_ptr] == '\n' || nbdf_ptr >= size ) { nbdf_buff[nbdf_ptr] = 0; //terminate string strcpy(buff,nbdf_buff); nbdf_ptr=0; DtInfo("\n"); return(buff); } else if ( nbdf_buff[nbdf_ptr] == '\b') { //Write out a blank space to erase whatever was there _putch(' '); //Move back before the space _putch('\b'); --nbdf_ptr; } else { ++nbdf_ptr; } } return NULL; } #endif #define LASTCHAR(s) (s[strlen(s)-1]) #define STERMINATE(s) do{ char *t=(s); if (*t) (s)++, *t='\0';} while(0) /* returns pointer to next token separated by a sep character * it's similar to strtok, but does not use internal static memory, * and leaves nextp pointing to end of word * note that gettok modifies the contents of the * strings passed to it by replacing separators with '\0' */ char *gettok(char **nextp, char *sep) { char *start, *end; start = *nextp; while (*start && strchr(sep, *start)) start++; if (!*start) return NULL; end = start; while (*end && !strchr(sep, *end) ) end++; *nextp = end; STERMINATE(*nextp); return start; } #if !WIN32 void setBlock(int fd, int on) { static int blockf, nonblockf; static int first = 1; if (first) { first = 0; int flags = fcntl(fd, F_GETFL, 0); if (flags == -1) DtWarnPerror("fcntl(F_GETFL) failed"); /* could use FNDELAY instead of FNONBLK, but then feof() will be confused */ blockf = flags & ~O_NONBLOCK; nonblockf = flags | O_NONBLOCK; } if (fcntl(0, F_SETFL, on ? blockf : nonblockf) < 0) DtWarnPerror("fcntl(F_SETFL) failed"); } #endif Cmd *lookupCmd(char *cmdname) { if (!cmdname) return NULL; for (int j=0; j<nCmds; j++) #if !WIN32 if (!strcasecmp(cmdname, allCmds[j].name)) #else if (!_stricmp(cmdname, allCmds[j].name)) #endif return allCmds+j; return NULL; } DtTextInterface::DtTextInterface( DtVrfRemoteController* controller) : kbInterest(0), myTimeToQuit(0), myController(controller) { DtInfo("Scenario directory: %s\n", scenariosDir); DtInfo("Terrain directory: %s\n", terrainDir); myController->addBackendDiscoveryCallback(backendAddedCb, NULL); myController->addBackendRemovalCallback(backendRemovedCb, NULL); myController->addBackendLoadedCallback(backendLoadedCb, NULL); myController->addScenarioLoadedCallback(ScenarioLoadedCb, NULL); myController->addBackendSavedCallback(backendSavedCb, NULL); myController->addScenarioSavedCallback(ScenarioSavedCb, NULL); kbInterest++; prompt(); } DtTextInterface::~DtTextInterface() { myController->removeBackendDiscoveryCallback(backendAddedCb, NULL); myController->removeBackendRemovalCallback(backendRemovedCb, NULL); myController->removeBackendLoadedCallback(backendLoadedCb, NULL); myController->removeScenarioLoadedCallback(ScenarioLoadedCb, NULL); myController->removeBackendSavedCallback(backendSavedCb, NULL); myController->removeScenarioSavedCallback(ScenarioSavedCb, NULL); } void DtTextInterface::setTimeToQuit(bool yesNo) { myTimeToQuit = yesNo; } bool DtTextInterface::timeToQuit() const { return myTimeToQuit; } DtVrfRemoteController* DtTextInterface::controller() const { return myController; } void DtTextInterface::readStdin() { char buff[256]; int discardNext = 0; UNBLOCK(); #if !WIN32 while (fgets(buff, sizeof(buff), stdin)) { #else while (non_blocking_dos_fgets(buff, sizeof(buff), stdin)) { #endif int discardThis = discardNext; discardNext = (LASTCHAR(buff) != 10); if (!discardThis) { /* RESTORE/UNBLOCK are not necessary if you know you won't terminate from within processCmd */ RESTORE(); processCmd(buff); if (!kbInterest) return; UNBLOCK(); } } RESTORE(); if (feof(stdin)) { DtInfo("EOF\n"); exit(0); } } void DtTextInterface::prompt() { if (kbInterest) DtInfo("> "); fflush(stdout); } void DtTextInterface::processCmd(char *buff) { char *cmdname = gettok(&buff, "\t \n"); Cmd *cmd = lookupCmd(cmdname); if (cmd) (*cmd->func)(buff, this); else if (cmdname) DtWarn("VRF Remote controller: no such command: \"%s\"\n", cmdname); prompt(); } void DtTextInterface::backendAddedCb(const DtSimulationAddress& addr, void* usr) { DtInfo("\nBackend %s Added\n> ", addr.string()); } void DtTextInterface::backendRemovedCb(const DtSimulationAddress& addr, void* usr) { DtInfo("\nBackend %s Removed\n> ", addr.string()); } void DtTextInterface::backendLoadedCb(const DtSimulationAddress& addr, void* usr) { DtInfo("\nBackend %s Loaded\n> ", addr.string()); } void DtTextInterface::ScenarioLoadedCb(void* usr) { DtInfo("\nScenario Loaded\n> "); } void DtTextInterface::backendSavedCb(const DtSimulationAddress& addr, DtSaveResult result, void* usr) { DtInfo("\nBackend %s Saved (%s)\n> ", addr.string(), result == DtSuccess? "success" : "failure"); } void DtTextInterface::ScenarioSavedCb(void* usr) { DtInfo("\nScenario Saved\n> "); } void DtTextInterface::vrfObjectCreatedCb( const DtString& name, const DtEntityIdentifier& id, void* usr) { static DtEntityIdentifier aggId; //We want to check for the creation of the aggregate entity and keep a hold of it's entity id so that //when it's subordinates are created we can add them to the aggregate's organization DtString search = name; if( search.findString("Tank_Plt") >=0 ) { DtInfo("\n aggregate has been created with name and id: \"%s\", %s\n> ", name.string(), id.string()); aggId = id; } else { DtInfo("\n entity has been created with name and id: \"%s\", %s\n> ", name.string(), id.string()); } //If the name of our new entity is one of our desired subordinates we will add it to the tank platoon DtString sub = name; if ( sub.findString("Plt_Sub") >= 0 ) { //We grab hold of the remote controller and add our individual to the aggregate DtVrfRemoteController* pVrfRemoteCtrl = (DtVrfRemoteController*)(usr); //This entity belongs to our aggregate, add it to the platoon if ( pVrfRemoteCtrl ) { pVrfRemoteCtrl->addToOrganization(id, aggId); DtInfo("\n %s has been added to aggregate with id: %s\n> ",name.string(),aggId.string()); } } } void DtTextInterface::createWaypoint(char* str) { //Let's parse the command string and then call //the corresponding method on DtVrfRemoteController. char *point, *name; name = strtok(str, " "); name = strtok(NULL, "\""); point = strtok(NULL, " "); if (!point || !name) { DtInfo("\tusage: create waypoint <\"name\"> <pt>\n"); return; } DtVector vec; sscanf(point, "%lf,%lf,%lf", &vec[0], &vec[1], &vec[2]); controller()->createWaypoint(vrfObjectCreatedCb, (void*)"waypoint", vec, name); } void DtTextInterface::createRoute(char* str) { //Let's parse the command string and then call //the corresponding method on DtVrfRemoteController. char *name, *point; name = strtok(str, " "); name = strtok(NULL, "\""); if (!name) { DtInfo("\tusage: create route <\"name\"> <p1 p2 ... pn>\n"); return; } //create list of vertices DtList vertices; while ((point = strtok(NULL, " ")) != NULL) { double x, y, z; sscanf(point, "%lf,%lf,%lf", &x, &y, &z); DtVector* vec = new DtVector(x, y, z); vertices.add(vec); } controller()->createRoute(vrfObjectCreatedCb, (void*)"route", vertices, name); //empty list of vertices DtListItem* next = NULL; for (DtListItem* item = vertices.first(); item; item = next) { next = item->next(); delete (DtVector*) vertices.remove(item); } } void DtTextInterface::createTank(char* str) { //Let's parse the command string and then call //the corresponding method on DtVrfRemoteController. char *point; point = strtok(&str[4], " "); if (!point) { DtInfo("\tusage: tank <geocentric_location>\n"); return; } DtVector vec; sscanf(point, "%lf,%lf,%lf", &vec[0], &vec[1], &vec[2]); controller()->createEntity( vrfObjectCreatedCb, (void*)"tank", DtEntityType(1, 1, 225, 1, 1, 3, 0), vec, DtForceFriendly, 90.0); } void DtTextInterface::patrolRoute(char* str) { //Let's parse the command string and then call //the corresponding method on DtVrfRemoteController. char *name, *route; name = strtok(str, " "); name = strtok(NULL, "\""); route = strtok(NULL, "\""); //get past whitespace route = strtok(NULL, "\""); if (!name || !route) { DtInfo("\tusage: task patrolRoute <\"name\"> <\"route\">\n"); return; } controller()->patrolAlongRoute(name, route); } void DtTextInterface::moveToPoint(char* str) { //Let's parse the command string and then call //the corresponding method on DtVrfRemoteController. char *name, *point; name = strtok(str, " "); name = strtok(NULL, "\""); point = strtok(NULL, "\""); //get past whitespace point = strtok(NULL, "\""); if (!name || !point) { DtInfo("\tusage: task moveToPoint <\"name\"> <\"waypoint\">\n"); return; } controller()->moveToWaypoint(name, point); } void DtTextInterface::follow(char* str) { //Let's parse the command string and then call //the corresponding method on DtVrfRemoteController. char *name, *leader; name = strtok(str, " "); name = strtok(NULL, "\""); leader = strtok(NULL, "\""); //get past white space leader = strtok(NULL, "\""); if (!name || !leader) { DtInfo("\tusage: task follow <name> <leader>\n"); return; } DtVector offset(10.0, 0.0, 0.0); controller()->followEntity(name, leader, offset); } void DtTextInterface::wait(char* str) { //Let's parse the command string and then call //the corresponding method on DtVrfRemoteController. char *name, *t; name = strtok(str, " "); name = strtok(NULL, "\""); t = strtok(NULL, " "); if (!name || !t) { DtInfo("\tusage: task wait <\"name\"> <time>\n"); return; } DtTime time = atof(t); controller()->waitDuration(name, time); } void DtTextInterface::setPlan(char* str) { //Let's parse the command string and then call //the corresponding method on DtVrfRemoteController. char *name = strtok(str, " "); name = strtok(NULL, "\""); if (!name) { DtInfo("\tusage: set plan <\"entity name\">\n"); return; } //Create a plan //This plan will contain four statements: //1. Set heading to 2.094395 radians //2. Set speed to 5 m/s //3. Wait 20 seconds //4. Move to waypoint "alpha" DtPlan plan; plan.init(); DtWaitDurationTask waitTask; waitTask.setSecondsToWait(20.0); DtMoveToTask moveTask; moveTask.setControlPtName("alpha"); DtSetHeadingRequest headingReq; headingReq.setHeading(2.094395); DtSetSpeedRequest speedReq; speedReq.setSpeed(5); DtSetDataRequestStmt stmt1, stmt2; DtTaskStmt stmt3, stmt4; stmt1.setSetDataRequest(&headingReq); stmt2.setSetDataRequest(&speedReq); stmt3.setTask(&waitTask); stmt4.setTask(&moveTask); //Create a conditional expression. //This is just a simple example of a condition that will always be //true when the entity is first created -- it checks to see if the fuel //is non-zero. DtCeResourceOperator conditionalExpression; conditionalExpression.setResource("fuel"); conditionalExpression.setOper(">"); conditionalExpression.setComparisonValue(0.0); //Create an if statement using the conditional expression DtIfStmt ifStatement; ifStatement.setCondExpr(&conditionalExpression); ifStatement.thenBlock()->add(stmt1.clone()); ifStatement.thenBlock()->addToEnd(stmt2.clone()); ifStatement.thenBlock()->addToEnd(stmt3.clone()); ifStatement.thenBlock()->addToEnd(stmt4.clone()); DtPlanBlock* block = plan.mainBlock(); block->addToStart(ifStatement.clone()); //Assign the plan to the given entitiy controller()->assignPlanByName(name, plan); //Let's subscribe to the given entity's plan messages. //This will let us know whether or "assignPlan" command //was successfull. Also, it will also notify us if any //changes were made to the plan. controller()->subscribePlan(name, planCb, NULL); //Let's register a callback for current statement //messages. Our callback will be invoked whenever the entity //starts a new statement in the plan. controller()->addPlanStatementCallback(name, planStmtCb, NULL); //Let's also register a callback for when the entity completes //the plan. Inside our callback, we unsubscribe to this entity's //plan messages and unregister our current statement callback. controller()->addPlanCompleteCallback(name, planCompleteCb, controller()); } void DtTextInterface::planCb(const DtString& name, const DtList& stmts, bool append, void* usr) { //Let's print the statements in the plan. //Because plans can be large, their statements may arrive in separate plan //messages. The append flag let's us know whether these statements belong to //a plan we already have. if (append) { DtInfo("\nAdditional plan statements for %s.\n", name.string()); } else { DtInfo("\nNew plan statements for %s.\n", name.string()); } int i = 0; for (DtListItem* item = stmts.first(); item; item = item->next()) { DtSimStatement* statement = (DtSimStatement*) item->data(); DtInfo("\t%d) %s\n", ++i, statement->stringRep().string()); } DtInfo("\n> "); } void DtTextInterface::planStmtCb(const DtString& id, DtPlanStatus status, void* usr) { //Let's print the statement that 'id' is executing. A statementId = 0 //indicates that the 'id' hasn't started its plan yet. if (status.currentStatementId > 0) { DtInfo("\n%s executing statement %d\n> ", id.string(), status.currentStatementId); } } void DtTextInterface::planCompleteCb(const DtString& id, void* usr) { //Let's print that 'id' has completed the plan. DtInfo("\n%s has completed its plan!\n> ", id.string()); //Because the plan we assigned is now complete, we no longer need the //plan callbacks we registered. Let's unregister them here. DtVrfRemoteController* controller = (DtVrfRemoteController*) usr; controller->unsubscribePlan(id, planCb, NULL); controller->removePlanCompleteCallback(id, planCompleteCb, NULL); controller->removePlanStatementCallback(id, planStmtCb, NULL); } void DtTextInterface::setLabel(char* str) { //Let's parse the command string and then call //the corresponding method on DtVrfRemoteController. char *objId, *label; objId = strtok(str, " "); objId = strtok(NULL, " "); label = strtok(NULL, "\""); if (!objId || !label) { DtInfo("\tusage: set label <object id> <\"label\">\n"); return; } controller()->setLabel(objId, label); } void DtTextInterface::setLocation(char* str) { //Let's parse the command string and then call //the corresponding method on DtVrfRemoteController. char *name, *point; name = strtok(str, " "); name = strtok(NULL, "\""); point = strtok(NULL, " "); if (!name || !point) { DtInfo("\tusage: set location <\"name\"> <pt>\n"); return; } DtVector vec; sscanf(point, "%lf,%lf,%lf", &vec[0], &vec[1], &vec[2]); controller()->setLocation(name, vec); } void DtTextInterface::setFuel(char* str) { //Let's parse the command string and then call //the corresponding method on DtVrfRemoteController. char *name, *amount; name = strtok(str, " "); name = strtok(NULL, "\""); amount = strtok(NULL, " "); if (!name || !amount) { DtInfo("\tusage: set fuel <\"name\"> <double>\n"); return; } double fa = atof(amount); controller()->setResource(name, "fuel", fa); } void DtTextInterface::setTarget(char* str) { //Let's parse the command string and then call //the corresponding method on DtVrfRemoteController. char *name, *target; name = strtok(str, " "); name = strtok(NULL, "\""); target = strtok(NULL, "\""); //get past whitespace target = strtok(NULL, "\""); if (!name || !target) { DtInfo("\tusage: set target <\"name\"> <\"name\">\n"); return ; } controller()->setTarget(name, target); } void DtTextInterface::setRestore(char* str) { //Let's parse the command string and then call //the corresponding method on DtVrfRemoteController. char *name; name = strtok(str, " "); name = strtok(NULL, "\""); if (!name) { DtInfo("\tusage: set restore <\"name\"> \n"); return; } controller()->restore(name); }