The lgrControl example demonstrates how to use the Logger Remote Control API to control one or more Loggers from a remote application.
In this case, it provides a console interface to send commands to the remote Loggers.
To write a remote control application, you need to:
1. Create an exercise connection
DtExerciseConn* conn = new DtExerciseConn(appInit);
2. Identify the Logger(s) that will recieve commands (default is wildcard for all Loggers)
DtEntityIdentifier lgrEntityID("65535:65535:65535");
3. Identify a unique ID for this remote control interface
DtEntityIdentifier selfEntityID(conn->applicationId(), appInit.remoteControlId());
4. Applications control the Logger through the remote control interface and domain-specific interfaces.
DtRemoteControlPlaybackInterface::setCreator(DtPlaybackInterfacePrinter::create);
DtRemoteControlRecordInterface::setCreator(DtRecordInterfacePrinter::create);
DtRemoteControlSimulationInterface::setCreator(DtSimulationInterfacePrinter::create);
DtRemoteControlSystemInterface::setCreator(DtSystemInterfacePrinter::create);
DtRemoteControlInterface* remoteControl = new DtRemoteControlInterface(appInit.numberOfLoggers());
DtRemoteControlPlaybackInterface* playbackInterface = remoteControl->playbackInterface();
DtRemoteControlRecordInterface* recordInterface = remoteControl->recordInterface();
DtRemoteControlSystemInterface* systemInterface = remoteControl->systemInterface();
DtRemoteControlSimulationInterface* simInterface = remoteControl->simulationInterface();
5. Add callbacks for actions you want to keep track of.
remoteControl->addCallbackConnectionTimedOut(timeoutCallback, 0);
void timeoutCallback( const DtRemoteLoggerConnection& loggerConnection, void* usr ) { DtWarn << "Logger Connection timed out\n"; loggerConnection.printDataToStream(DtWarn); }
6. The rest of the main application code cycles through a loop that ticks the exercise connection and remote control interface and collects user input in order to create and send commands using the remote control interface.
7. This example also demonstrates how to override the domain interfaces in order change how a command is handled or to implement actions to take when specific responses are received.
Example: playbackInterfacePrinter.cxx
The Logger control example provides a console based interface to send commands to one or more loggers using the remote control interface. It provides commands for most of the common logger features such as load/play/pause/stop/change speed/close a tape and similar commands for record as well as others. A list of the commands along with the parameters and a description can be printed to the console using the command "help."
The console will accept partial strings for the command tokens, so "h" is acceptable for "help" (first match is chosen). Alternatively, the command ID can be used instead of the token. The "<" and ">" keys will cycle through the command history and restore the commands to the prompt. The command "restoreCmd <int>" will restore a specific command to the prompt.
Required parameters are designated with angle brackets containing the data type and an indication of its purpose or unit (e.g. <seconds(float)> or <overwrite(1/0)>. Optional parameters are designated with square brackets. Curly brackets indicate an enumerated list of values where one of the numeric values must be selected (e.g., {RECORD=2, STOP=3, EMPTY=4}). The term "N/A" indicates that the command requires no parameters. Here is an example of the list of commands (subject to change and differs by protocol).
|
ID |
Command |
Description |
|---|---|---|
|
0 |
Quit |
Quit this application (Capital Q will force shutdown of loggers). |
|
1 |
LoadPlayback <fileNameString> [constructFileName(1/0)] |
Load the specified file for playback with option to construct name using logger ID (enabled). |
|
2 |
LoadRecord <fileNameString> [<overwrite(1/0)> [constructFileName(1/0)]] |
Load the specified file for recording with option to overwrite (enabled) and construct name using logger ID (enabled). |
|
3 |
Unload |
Unload the current tape. |
|
4 |
Play |
Play the current tape. |
|
5 |
Pause |
Pause playback/recording. |
|
6 |
Stop |
Stop playback/recording. |
|
7 |
Rewind |
Rewind to beginning of tape. |
|
8 |
Unwind |
Unwind to end of tape. |
|
9 |
Record [multipleFiles(0/1)] |
Record tape with option to use multiple files (disabled). |
|
10 |
SetScale <float> [skipping(0/1)] |
Set tape speed with the option to skip between checkpoints (disabled). |
|
11 |
JumpTime <absTimeString(h:m:s)> |
Jump to specified time in tape, hours and/or minutes can be omitted. |
|
12 |
SkipTime <relTimeString(h:m:s)> |
Skip forwards or backwards from the current time; negative if used must be used in each part, for example, -5:-30. |
|
13 |
SetplayAction {PLAY=0, PAUSE=1, STOP=3, EMPTY=4, LOOP=5} |
Set various actions for a tape loaded for playback. |
|
14 |
SetRecordAction {RECORD=2, STOP=3, EMPTY=4} |
Set various actions for a tape loaded for playback. |
|
15 |
SetEofAction {PLAY=0, PAUSE=1, STOP=3, EMPTY=4, LOOP=5} |
Set various actions when reaching EOF. |
|
16 |
DeleteFile <fileNameString> |
Delete the specified file from disk. |
|
17 |
ToggleFlag <int> |
Toggle various system flags. {STATE_REQUEST=1 TIME_CONSTR_ON=2, TIME_CONSTR_OFF=3, TIME_REGLTD_ON=4, TIME_REGLTD_OFF=5, OWN_FED_AMB_ON=6, OWN_FED_AMB_OFF=7} |
|
20 |
Shutdown |
Shut down the connected loggers. |
|
21 |
SetDestination <IdString> |
Change the destination ID, for example, 9:8:7 or 1:1:* - all loggers at 1:1. |
|
22 |
SetOrigination <IdString> |
Change the origination ID, for example, 3:4:5. |
|
23 |
LoadSubFile <fileNameString> [idString] |
Load the specified subscription file, with optional destination, for example, 9:8:7. |
|
24 |
Info |
Display information on connected loggers. |
|
25 |
Config |
Display configuration information. |
|
26 |
PrintHistory [<int(start)> [<int(end)>]] |
Display command history from start to end (all by default). |
|
27 |
RestoreCmd <int> |
Restore command with the given index. |
|
28 |
Help |
Display this list of commands and pause for input. |
Enter command token (or ID) with arguments then press Enter key.
WARNING: processing halts until key is pressed!
/******************************************************************************* ** Copyright (c) 1992-2011 VT MAK ** All rights reserved. *******************************************************************************/ #include "playbackInterfacePrinter.h" #include "recordInterfacePrinter.h" #include "simulationInterfacePrinter.h" #include "systemInterfacePrinter.h" #include "consoleInput.h" #include <remoteControlInterface.h> #include <lgrDisSimCommandFactory.h> #include <vl/exConnInit.h> #include <vlutil/vlPrint.h> #include <mtl/lispEnv.h> using namespace MAKLogger; // Forward declaration of the functions in this module char* getToken( char** nextp, const char* sep ); void timeoutCallback( const DtRemoteLoggerConnection& loggerConnection, void* usr ); void printHelp(); int tokenToCommandId( DtString commandString ) ; DtEntityIdentifier parseEntityIdentifier(const char* buffer); // Commands keys enum DtRemoteControlId { DtRcInvalid = -1, DtRcQuit = 0, DtRcLoadPlayback = 1, DtRcLoadRecord = 2, DtRcUnload = 3, DtRcPlay = 4, DtRcPause = 5, DtRcStop = 6, DtRcRewind = 7, DtRcUnwind = 8, DtRcRecord = 9, DtRcSetscale = 10, DtRcJumpTime = 11, DtRcSkipTime = 12, DtRcSetplayAction = 13, DtRcSetRecordAction = 14, DtRcSetEofAction = 15, DtRcDeleteFile = 16, DtRcToggleFlag = 17, DtRcSetHeartBeat = 18, DtRcSetTimeout = 19, DtRcShutdown = 20, DtRcSetDestination = 21, DtRcSetOrigination = 22, DtRcLoadSubFile = 23, DtRcInfo = 24, DtRcConfig = 25, DtRcPrintHistory = 26, DtRcRestoreCmd = 27, DtRcHelp = 28 }; struct DtRcToken { DtRemoteControlId id; const char* token; const char* paramStr; const char* helpStr; }; // Command records DtRcToken theCommandTokens[] = { {DtRcQuit, "Quit","N/A", "Quit this application (Capital Q will force shutdown of loggers)"}, {DtRcLoadPlayback, "LoadPlayback", "<fileNameString> [constructFileName(1/0)]", "Load the specified file for playback with option to\n" "\tconstruct name using logger ID (enabled)"}, {DtRcLoadRecord, "LoadRecord", "<fileNameString> [<overwrite(1/0)> [constructFileName(1/0)]]", "Load the specified file for recording with option to\n" "\toverwrite (enabled) and construct name using logger ID (enabled)"}, {DtRcUnload, "Unload", "N/A", "Unload the current tape"}, {DtRcPlay, "Play", "N/A", "Play the current tape"}, {DtRcPause, "Pause", "N/A", "Pause playback/recording"}, {DtRcStop, "Stop", "N/A", "Stop playback/recording"}, {DtRcRewind, "Rewind", "N/A", "Rewind to beginning of tape"}, {DtRcUnwind, "Unwind", "N/A", "Unwind to end of tape"}, {DtRcRecord, "Record", "[multipleFiles(0/1)]", "Record tape with option to use multiple files (disabled)"}, {DtRcSetscale, "SetScale", "<float> [skipping(0/1)]", "Set tape speed with the option to skip between checkpoints (disabled)"}, {DtRcJumpTime, "JumpTime", "<absTimeString(h:m:s)>", "Jump to specified time in tape, hours and/or minutes can be omitted"}, {DtRcSkipTime, "SkipTime", "<relTimeString(h:m:s)>", "Skip forwards or backwards from the current time;\n" "\tnegative if used must be used in each part e.g., -5:-30"}, {DtRcSetplayAction, "SetplayAction", "{PLAY=0, PAUSE=1, STOP=3, EMPTY=4, LOOP=5}", "Set various actions for a tape loaded for playback"}, {DtRcSetRecordAction, "SetRecordAction", "{RECORD=2, STOP=3, EMPTY=4}", "Set various actions for a tape loaded for playback"}, {DtRcSetEofAction, "SetEofAction", "{PLAY=0, PAUSE=1, STOP=3, EMPTY=4, LOOP=5}", "Set various actions when reaching EOF"}, {DtRcDeleteFile, "DeleteFile", "<fileNameString>", "Delete the specified file from disk"}, #if DtHLA {DtRcToggleFlag, "ToggleFlag", "<int>", "Toggle various system flags\n" " {STATE_REQUEST=1\n" " TIME_CONSTR_ON=2, TIME_CONSTR_OFF=3,\n" " TIME_REGLTD_ON=4, TIME_REGLTD_OFF=5,\n" " OWN_FED_AMB_ON=6, OWN_FED_AMB_OFF=7}\n"}, #else {DtRcToggleFlag, "ToggleFlag", "<int>", "Toggle various system flags: {STATE_REQUEST=1}"}, {DtRcSetHeartBeat, "SetHeartBeat", "<float>", "Set the entity heartbeat interval."}, {DtRcSetTimeout, "SetTimeout", "<float>", "Set the entity timeout interval."}, #endif {DtRcShutdown, "Shutdown", "N/A", "Shutdown the connected loggers."}, {DtRcSetDestination, "SetDestination", "<IdString>", "Change the destination ID e.g., 9:8:7 or 1:1:* - all loggers at 1:1"}, {DtRcSetOrigination, "SetOrigination", "<IdString>", "Change the origination ID e.g., 3:4:5"}, {DtRcLoadSubFile, "LoadSubFile", "<fileNameString> [idString]", "Load the specified subscription file, with optional destination e.g., 9:8:7"}, {DtRcInfo, "Info", "N/A", "Display information on connected loggers"}, {DtRcConfig, "Config", "N/A", "Display configuration information"}, {DtRcPrintHistory, "PrintHistory", "[<int(start)> [<int(end)>]]", "Display command history from start to end (all by default)"}, {DtRcRestoreCmd, "RestoreCmd", "<int>", "Restore command with the given index."}, {DtRcHelp, "Help", "N/A", "Display this list of commands and pause for input\n"}, {DtRcInvalid, 0, "", ""} }; class DtLgrControlInitializer : public DtVrlApplicationInitializer { public: DtLgrControlInitializer(int argc, char* argv[], const DtString& appName) : DtVrlApplicationInitializer(argc, argv, appName) , myConfigVarNumberOfLoggers(mySettings, "numberOfLoggers", -1, "Number of Logger Connections") , myConfigVarRemoteControlId(mySettings, "remoteControlId", 0, "Remote Control ID") , myConfigConnectionTimeout(mySettings, "connectionTimeout", 120.0, "Connection Timeout") { } ~DtLgrControlInitializer() { } virtual void initializeMtlParams() { DtVrlApplicationInitializer::initializeMtlParams(); myMtlEnv->registerConfigVar(myConfigVarNumberOfLoggers); } virtual void setNumberOfLoggers(int val) { myConfigVarNumberOfLoggers.setValue(val); } virtual int numberOfLoggers() const { return myConfigVarNumberOfLoggers.value(); } virtual void setRemoteControlId(int val) { myConfigVarRemoteControlId.setValue(val); } virtual int remoteControlId() const { return myConfigVarRemoteControlId.value(); } virtual void setConnectionTimeout(double val) { myConfigConnectionTimeout.setValue(val); } virtual double connectionTimeout() const { return myConfigConnectionTimeout.value(); } protected: DtConfigVariable<int> myConfigVarNumberOfLoggers; DtConfigVariable<int> myConfigVarRemoteControlId; DtConfigVariable<double> myConfigConnectionTimeout; }; // // Application // int main( int argc, char* argv[] ) { // Create a connection to the exercise or federation execution. DtLgrControlInitializer appInit(argc, argv, "lgrControl"); // Change some defaults #if DtDIS appInit.setUseAsynchIO(true); #endif appInit.parseCmdLine(); DtExerciseConn* conn = new DtExerciseConn(appInit); // Identify the receiving (remote) Logger DtEntityIdentifier lgrEntityID("65535:65535:65535"); // Identify yourself #if DtDIS DtEntityIdentifier selfEntityID(conn->applicationId(), appInit.remoteControlId()); #else DtEntityIdentifier selfEntityID(conn->applicationId(), appInit.remoteControlId()); #endif DtRemoteControlPlaybackInterface::setCreator(DtPlaybackInterfacePrinter::create); DtRemoteControlRecordInterface::setCreator(DtRecordInterfacePrinter::create); DtRemoteControlSimulationInterface::setCreator(DtSimulationInterfacePrinter::create); DtRemoteControlSystemInterface::setCreator(DtSystemInterfacePrinter::create); DtRemoteControlInterface* remoteControl = new DtRemoteControlInterface(appInit.numberOfLoggers()); DtRemoteControlPlaybackInterface* playbackInterface = remoteControl->playbackInterface(); DtRemoteControlRecordInterface* recordInterface = remoteControl->recordInterface(); DtRemoteControlSystemInterface* systemInterface = remoteControl->systemInterface(); DtRemoteControlSimulationInterface* simInterface = remoteControl->simulationInterface(); remoteControl->setConnectionTimeout(appInit.connectionTimeout()); remoteControl->setConnection(conn, selfEntityID); remoteControl->addCallbackConnectionTimedOut(timeoutCallback, 0); DtCommandInput commandInput; bool skipTick = false; char cmdLine[2048]; bool timeToQuit = false; // Enter the main command-processing cycle while( !timeToQuit ) { // Advance simulation time conn->clock()->setSimTime( conn->clock()->elapsedRealTime() ); if (!skipTick) { // Process any relevant incoming messages. conn->drainInput(); remoteControl->tick(); } skipTick = false; if (commandInput.keyboardTick(cmdLine)) { // Scan for input char* token = cmdLine; char* cmdToken = getToken( &token, " \t\n\r" ); char* optionalArg = getToken( &token, " \t\n\r" ); int ln = strlen( cmdToken ); if ( ln == 0 ) { continue; } int cmd = 0; if (isdigit(*cmdToken)) { cmd = atoi( cmdToken ); } else { cmd = tokenToCommandId(cmdToken); } // Construct and send desired PDU switch( cmd ) { case DtRcQuit: { timeToQuit = true; if (*cmdToken == 'Q') { // Force the remote logger to quit systemInterface->shutdown(lgrEntityID); } break; } case DtRcLoadPlayback: { if (!recordInterface->isRecording()) { bool constructFileName = true; char* constructFileNameArg = getToken( &token, " \t\n\r" ); if ( constructFileNameArg && *constructFileNameArg != '\0' ) { constructFileName = atoi(constructFileNameArg) != 0; } // Load file 'optionalArg' for playback playbackInterface->openFile(optionalArg, constructFileName, lgrEntityID); } break; } case DtRcLoadRecord: { if (!playbackInterface->isPlaying()) { bool overwrite = true; bool constructFileName = true; char* overwriteArg = getToken( &token, " \t\n\r" ); if ( overwriteArg && *overwriteArg != '\0' ) { overwrite = atoi(overwriteArg) != 0; char* constructFileNameArg = getToken( &token, " \t\n\r" ); if ( constructFileNameArg && *constructFileNameArg != '\0' ) { constructFileName = atoi(constructFileNameArg) != 0; } } // Load file 'optionalArg' for recording recordInterface->newFile(optionalArg, overwrite, constructFileName, lgrEntityID); } break; } case DtRcUnload: { // Close File playbackInterface->closeFile(lgrEntityID); recordInterface->closeFile(lgrEntityID); break; } case DtRcPlay: { // Start sending recorded traffic onto network // (commence playback) playbackInterface->play(lgrEntityID); break; } case DtRcPause: { // Whichever activity is active will send the command // Pause sending recorded traffic onto network (during playback) playbackInterface->pause(lgrEntityID); // Pause recording recordInterface->pause(lgrEntityID); break; } case DtRcStop: { // Whichever activity is active will send the command // Stop sending recorded traffic onto network // (during playback) playbackInterface->stop(lgrEntityID); recordInterface->stop(false, lgrEntityID); break; } case DtRcRewind: { // Rewind remote Logger (during playback) // Use a big skip time and logger will move to start as a fallback playbackInterface->jumpToStart(lgrEntityID); break; } case DtRcUnwind: { // Fast-forward remote Logger to end (during playback) // Use a big skip time and logger will move to stop as a fallback playbackInterface->jumpToStop(lgrEntityID); break; } case DtRcRecord: { bool multipleFiles = false; if ( optionalArg && *optionalArg == '\0' ) { multipleFiles = atoi(optionalArg) != 0; } // Start recording traffic from network (during record mode) recordInterface->record(multipleFiles, lgrEntityID); break; } case DtRcSetscale: { // Control the speed of playback if ( !optionalArg || *optionalArg == '\0' ) { DtWarn( "The empty string is not a valid time-scale.\n" ); } else { bool skipping = false; char* skippingArg = getToken( &token, " \t\n\r" ); if ( skippingArg && *skippingArg != '\0' ) { skipping = atoi(skippingArg) != 0; } playbackInterface->setPlaybackSpeed(atof(optionalArg), skipping, lgrEntityID); } break; } case DtRcJumpTime: { // Jump to specified time during playback if ( !optionalArg || *optionalArg == '\0' ) { DtWarn( "The empty string is not a valid time value.\n" ); } else { try { DtAbsoluteTime time(DtAbsoluteTime::parseString(optionalArg)); playbackInterface->jumpToTime(time, lgrEntityID); } catch (DtInvalidInput* e) { DtWarn( "Unable to parse time.\n" ); } catch (...) { DtWarn( "Unable to parse time.\n" ); } } break; } case DtRcSkipTime: { // Move forward or backwards by specified amount of time // during playback if ( !optionalArg || *optionalArg == '\0' ) { DtWarn( "The empty string is not a valid time value.\n" ); } else { try { DtRelativeTime skipTime(DtRelativeTime::parseString(optionalArg)); if (skipTime.seconds() < 0) { playbackInterface->skipTimeBackward(-skipTime.seconds(), lgrEntityID); } else { playbackInterface->skipTimeForward(skipTime, lgrEntityID); } } catch (DtInvalidInput* e) { DtWarn( "Unable to parse time.\n" ); } catch (...) { DtWarn( "Unable to parse time.\n" ); } } break; } case DtRcSetplayAction: { // Action to take when entering playback mode if ( !optionalArg || *optionalArg == '\0' ) { DtWarn( "The empty string is not a valid PlayAction mode.\n" ); } else { int ix = atoi(optionalArg); switch (ix) { case 0: // Play { playbackInterface->setPlayOnLoad(true, lgrEntityID); break; } case 3: // Stop { playbackInterface->setPlayOnLoad(false, lgrEntityID); break; } case 1: // Pause case 4: // Empty { playbackInterface->closeFile(lgrEntityID); break; } case 5: // Loop { // NA break; } case 2: // NA default: { DtWarn( "%d is not a valid PlayAction mode.\n", ix ); break; } } } break; } case DtRcSetRecordAction: { // Action to take when entering record mode if ( !optionalArg || *optionalArg == '\0' ) { DtWarn( "The empty string is not a valid RecordAction mode.\n" ); } else { int ix = atoi(optionalArg); switch (ix) { case 2: // Record { recordInterface->setPauseOnRecord(false, lgrEntityID); break; } case 3: // Stop { recordInterface->setPauseOnRecord(true, lgrEntityID); break; } case 4: // Empty { recordInterface->closeFile(lgrEntityID); break; } case 0: case 1: case 5: default: { DtWarn( "%d is not a valid RecordAction mode.\n", ix ); break; } } } break; } case DtRcSetEofAction: { // Action to take upon reaching the end of file (in playback mode) if ( !optionalArg || *optionalArg == '\0' ) { DtWarn( "The empty string is not a valid EofAction mode.\n" ); } else { int ix = atoi(optionalArg); switch (ix) { case 3: // Stop { playbackInterface->setLoopAtEnd(false, lgrEntityID); break; } case 5: // Loop { playbackInterface->setLoopAtEnd(true, lgrEntityID); break; } case 0: // Play case 1: // Pause case 4: // Empty { // NA break; } case 2: default: { DtWarn( "%d is not a valid EofAction mode.\n", ix ); break; } } } break; } case DtRcDeleteFile: { // Force the remote logger to delete file 'optionalArg' if ( !optionalArg || *optionalArg == '\0' ) { DtWarn( "The empty string is not a valid file name.\n" ); } else { systemInterface->deleteFile(optionalArg, lgrEntityID); } break; } case DtRcToggleFlag: { // Toggle a particular flag if ( !optionalArg || *optionalArg == '\0' ) { DtWarn( "The empty string is not a valid StateFlag index.\n" ); } else { int ix = atoi(optionalArg); if ( ix <= 0 || ix > 7 ) { DtWarn( "%d is not a valid StateFlag index.\n", ix ); } else { DtLgrCtrlFlagStates flag = DtLgrCtrlFlagStates(ix); if (flag == DtLGR_STATE_REQUEST) { systemInterface->requestStateUpdate(lgrEntityID); } else { systemInterface->requestToggleFlag(flag, lgrEntityID); } } } break; } #if !DtHLA case DtRcSetHeartBeat: { // Set the heartbeat interval of the remote logger if ( !optionalArg || *optionalArg == '\0' ) { DtWarn( "The empty string is not a valid heartbeat.\n" ); } else { remoteControl->sendCommandMessage( DtLgrDisCommandFactory::createSetHeartbeatRateCommand(atof(optionalArg)), lgrEntityID); } break; } case DtRcSetTimeout: { // Set the timeout interval of the remote logger if ( !optionalArg || *optionalArg == '\0' ) { DtWarn( "The empty string is not a valid timeout.\n" ); } else { remoteControl->sendCommandMessage( DtLgrDisCommandFactory::createSetTimeoutRateCommand(atof(optionalArg)), lgrEntityID); } break; } #endif case DtRcShutdown: { systemInterface->shutdown(lgrEntityID); break; } case DtRcSetDestination: { DtEntityIdentifier tempId = parseEntityIdentifier(optionalArg); if (lgrEntityID == tempId) { DtInfo << "Logger ID unchanged: " << lgrEntityID.string() << std::endl; } else { lgrEntityID = tempId; DtInfo << "New logger ID: " << lgrEntityID.string() << std::endl; } break; } case DtRcSetOrigination: { DtEntityIdentifier tempId(optionalArg); if (selfEntityID == tempId) { DtInfo << "Self ID unchanged: " << selfEntityID.string() << std::endl; } else { selfEntityID = tempId; DtInfo <<"New Self ID; " << selfEntityID.string() << std::endl; } break; } #if DtHLA case DtRcLoadSubFile: { if ( !optionalArg || *optionalArg == '\0' ) { DtWarn( "The empty string is not a subscription file name.\n" ); } else { DtEntityIdentifier destination(DtRemoteControlInterface::wildcardAddress()); DtString subscriptionFile(optionalArg); char* entityIdArg = getToken( &token, " \t\n\r" ); if ( entityIdArg && *entityIdArg != '\0' ) { destination = parseEntityIdentifier(entityIdArg); } else { destination = lgrEntityID; } simInterface->loadSubscriptionListFileCommand(subscriptionFile, destination); } break; } #endif case DtRcInfo: { DtEntityIdSet connectedLoggers; remoteControl->getConnectionIds( connectedLoggers ); if (connectedLoggers.size() == 0) { DtInfo << "No Loggers Connected.\n"; } else { DtInfo << "Connected Loggers\n"; DtEntityIdSet::const_iterator idIter = connectedLoggers.begin(); DtEntityIdSet::const_iterator lgrEnd = connectedLoggers.end(); for ( ; idIter != lgrEnd; ++idIter) { DtRemoteLoggerConnection* connection = remoteControl->getConnection(*idIter); if (connection) { connection->printDataToStream(DtInfo); } } DtInfo << std::endl; } break; } case DtRcConfig: { DtInfo << "Destination: " << lgrEntityID.string() << std::endl; DtInfo << "Origination: " << selfEntityID.string() << std::endl; DtInfo << "Number Loggers: " << appInit.numberOfLoggers() << std::endl; DtInfo << "Remote Control ID: " << appInit.remoteControlId() << std::endl; DtInfo << "Connection timeout: " << appInit.connectionTimeout() << std::endl; #if DtHLA DtInfo << "Execution name: " << appInit.execName() << std::endl; DtInfo << "Federate name: " << appInit.federateName() << std::endl; #if DtHLA_1516_EVOLVED DtInfo << "Federate type: " << appInit.federateType() << std::endl; #endif DtInfo << "FOM filename: " << appInit.fedFileName() << std::endl; #if DtHLA_1516_EVOLVED DtInfo << "MIM filename: " << appInit.mimModule() << std::endl; std::vector<DtString> modules; appInit.fomModules(modules); DtInfo << "FOM Module count : " << modules.size() << std::endl; for (std::vector<DtString>::const_iterator iter = modules.begin(); iter != modules.end(); ++iter) DtInfo << "\t" << *iter << std::endl; #endif DtInfo << "FOM Mapper lib. name: " << appInit.fomMapperLibName() << std::endl; DtInfo << "RPR version: " << appInit.rprFomVersion() << std::endl; DtInfo << "Use Advisories: " << appInit.useAdvisories() << std::endl; DtInfo << "TimeStamp type: " << appInit.timeStampType() << std::endl; DtInfo << "Destroy Execution: " << appInit.destroyFedExec() << std::endl; DtInfo << "Send Fed Time: " << appInit.sendFedTime() << std::endl; DtInfo << "RPR revision: " << appInit.rprFomRevision() << std::endl; DtInfo << "Reflecting: " << appInit.reflecting() << std::endl; DtInfo << "Reflect Remote Updates Into Pub: " << appInit.reflectRemoteUpdatesIntoPub() << std::endl; DtInfo << "Reflect Through Rti: " << appInit.reflectThroughRti() << std::endl; DtInfo << "Reply To Sync Points: " << appInit.replyToSynchPoints() << std::endl; DtInfo << "Auto Subscribe: " << appInit.autoSubscribe() << std::endl; #else DtInfo << "Port: " << appInit.port() << std::endl; DtInfo << "Exercise ID: " << appInit.exerciseId() << std::endl; DtInfo << "Application Number: " << appInit.applicationNumber() << std::endl; DtInfo << "Site ID: " << appInit.siteId() << std::endl; DtInfo << "Destination Address: " << appInit.destinationAddress() << std::endl; DtInfo << "Send Buffer Size: " << appInit.sendBufferSize() << std::endl; DtInfo << "Receive Buffer Size: " << appInit.receiveBufferSize() << std::endl; DtInfo << "Use Asynch. IO: " << appInit.useAsynchIO() << std::endl; DtInfo << "Multicast TTL: " << appInit.multicastTtl() << std::endl; DtInfo << "Time Stamp Type: " << appInit.timeStampType() << std::endl; DtInfo << "Use IP v6: " << appInit.useIpv6() << std::endl; DtInfo << "Suppress Self Reflect: " << appInit.suppressSelfReflect() << std::endl; DtInfo << "Device Address: " << appInit.deviceAddress() << std::endl; const std::vector<DtString>& multicastAddrs = appInit.multicastAddresses(); DtInfo << "Multicast Address count: " << multicastAddrs.size() << std::endl; for (std::vector<DtString>::const_iterator iter = multicastAddrs.begin(); iter != multicastAddrs.end(); ++iter) DtInfo << "\t" << *iter << std::endl; #endif break; } case DtRcPrintHistory: { unsigned int start = 0; unsigned int end = 65535; if ( optionalArg && *optionalArg != '\0' ) { start = atoi(optionalArg); char* endArg = getToken( &token, " \t\n\r" ); if ( endArg && *endArg != '\0' ) { end = atoi(endArg); } } commandInput.eraseLastCommand(); commandInput.printHistory(start, end); break; } case DtRcRestoreCmd: { int cmdIndex = 0; if ( optionalArg && *optionalArg != '\0' ) { cmdIndex = atoi(optionalArg); } commandInput.eraseLastCommand(); commandInput.restoreCommand(cmdIndex); break; } case DtRcHelp: { // Print usage instructions and prompt for input printHelp(); commandInput.forceInput(); skipTick = true; break; } default: { DtWarn << "Command not recognized: " << cmdToken << std::endl; break; } } } } delete remoteControl; delete conn; DtInfo << std::endl << "Bye bye now..." << std::endl << std::endl; return 0; } // // Returns a pointer to the next token in '*nextp', where individual tokens are // assumed to be separated by one (or more) characters from the list 'sep'. // It is similar to strtok, but does not use internal static memory, and it // leaves '*nextp' pointing to the next token (or the string-end). // Note: 'getToken' modifies the contents of the strings passed to it by // replacing all occurrences of separator-chars (from 'sep') with // null-terminators '\0'. // char* getToken( char** nextp, const char* sep ) { char* start = *nextp; if ( !start ) return 0; while( *start != '\0' && strchr( sep, *start ) ) ++start; if ( *start == '\0' ) { *nextp = start; return start; } char* end = start; for( ; *end != '\0' && !strchr( sep, *end ); ++end ); for( ; *end != '\0' && strchr( sep, *end ); ++end ) *end = '\0'; *nextp = end; return start; } void timeoutCallback( const DtRemoteLoggerConnection& loggerConnection, void* usr ) { DtWarn << "Logger Connection timed out\n"; loggerConnection.printDataToStream(DtWarn); } void printHelp() { DtInfo << "Command ID / Token Optional-Arguments\n" << "\t Description" << std::endl; for (int i=0; theCommandTokens[i].token != 0; i++) { const DtRcToken& cmdRecord = theCommandTokens[i]; DtInfo << std::setw(3) << cmdRecord.id << " / " << cmdRecord.token << " " << cmdRecord.paramStr << std::endl << "\t" << cmdRecord.helpStr << std::endl; } DtInfo << "Enter command token (or ID) with arguments then press Enter key." << std::endl; DtInfo << "WARNING: processing halts until Enter key is pressed!"; DtInfo.flush(); } int tokenToCommandId( DtString commandString ) { int cmdId = -1; commandString.toLower(); for (int i=0; cmdId < 0 && theCommandTokens[i].token != 0; i++) { DtString cmdToken(theCommandTokens[i].token); cmdToken.toLower(); if (cmdToken == commandString || cmdToken.findString(commandString) == 0) { cmdId = theCommandTokens[i].id; } } return cmdId; } DtEntityIdentifier parseEntityIdentifier(const char* buffer) { DtString idString(buffer); DtEntityIdentifier id; DtString wildcard("*"); if (idString.findChar('*') < 0) { id = DtEntityIdentifier(buffer); } else { int site = 0; int host = 0; int entity = 0; DtString temp1(idString); // Get the site temp1 = idString; temp1.snipBack(':', true); site = (temp1 == wildcard) ? 65535: temp1.toInt(); // Get the host idString.snipFront(':'); temp1 = idString; temp1.snipBack(':', true); host = (temp1 == wildcard) ? 65535: temp1.toInt(); // Get the entity idString.snipFront(':'); temp1 = idString; entity = (temp1 == wildcard) ? 65535: temp1.toInt(); id.init(site, host, entity); } return id; }
/******************************************************************************* ** Copyright (c) 1992-2011 VT MAK ** All rights reserved. *******************************************************************************/ #include "playbackInterfacePrinter.h" #include <vlutil/vlPrint.h> using namespace MAKLogger; DtPlaybackInterfacePrinter::DtPlaybackInterfacePrinter(DtRemoteControlInterface* rci) : DtRemoteControlPlaybackInterface(rci) , mySkipEnabled(false) , myTransitioningSkip(false) , myPlayAfterTransitionSkip(false) { } DtPlaybackInterfacePrinter::~DtPlaybackInterfacePrinter() { } DtRemoteControlPlaybackInterface* DtPlaybackInterfacePrinter::create(DtRemoteControlInterface* rci) { return new DtPlaybackInterfacePrinter(rci); } void DtPlaybackInterfacePrinter::fileOpened( const DtString& fileName, const DtAbsoluteTime& firstTime, const DtAbsoluteTime& lastTime) { DtRemoteControlPlaybackInterface::fileOpened(fileName, firstTime, lastTime); DtInfo << "All loggers report fileOpened: " << fileName << " first: " << firstTime << " last: " << lastTime << std::endl; } void DtPlaybackInterfacePrinter::fileClosed() { DtRemoteControlPlaybackInterface::fileClosed(); DtInfo << "All loggers report fileClosed " << std::endl; } void DtPlaybackInterfacePrinter::fileDoesNotExist( const DtEntityIdentifier& source, const DtString& fileName) { DtRemoteControlPlaybackInterface::fileDoesNotExist(source, fileName); DtInfo << "All loggers report source: " << source.string() << " fileDoesNotExist: " << fileName << std::endl; } void DtPlaybackInterfacePrinter::fileIncomplete( const DtEntityIdentifier& source, const DtString& fileName, bool isIncomplete) { DtRemoteControlPlaybackInterface::fileIncomplete(source, fileName, isIncomplete); if (isIncomplete) { DtInfo << "All loggers report source: " << source.string() << " fileIncomplete: " << fileName << " isIncomplete: " << (isIncomplete ? "yes" : "no") << std::endl; } } void DtPlaybackInterfacePrinter::fileInvalid( const DtEntityIdentifier& source, const DtString& fileName, const DtString& reason) { DtRemoteControlPlaybackInterface::fileInvalid(source, fileName, reason); DtInfo << "All loggers report source: " << source.string() << " fileInvalid: " << fileName << " reason: " << reason << std::endl; } void DtPlaybackInterfacePrinter::fileChanged() { DtRemoteControlPlaybackInterface::fileChanged(); DtInfo << "All loggers report fileChanged " << std::endl; } void DtPlaybackInterfacePrinter::fileSaved() { DtRemoteControlPlaybackInterface::fileSaved(); DtInfo << "All loggers report fileSaved " << std::endl; } void DtPlaybackInterfacePrinter::fileFormatCurrent(bool current) { DtRemoteControlPlaybackInterface::fileFormatCurrent(current); DtInfo << "All loggers report fileFormatCurrent: " << (current ? "yes" : "no") << std::endl; } void DtPlaybackInterfacePrinter::playbackChanged(bool currentlyPlaying) { DtRemoteControlPlaybackInterface::playbackChanged(currentlyPlaying); DtInfo << "All loggers report playbackChanged: " << (currentlyPlaying ? "playing" : "not playing") << std::endl; } void DtPlaybackInterfacePrinter::restarted() { DtRemoteControlPlaybackInterface::restarted(); DtInfo << "All loggers report restarted " << std::endl; } void DtPlaybackInterfacePrinter::jumpTimeChanged(const DtAbsoluteTime& jumpTime) { DtRemoteControlPlaybackInterface::jumpTimeChanged(jumpTime); if (myTransitioningSkip) { DtRemoteControlPlaybackInterface::setPlaybackSpeed( mySpeedRequest, false, mySpeedRequestDestination); } } void DtPlaybackInterfacePrinter::loopAtEndChanged(bool enabled) { DtRemoteControlPlaybackInterface::loopAtEndChanged(enabled); DtInfo << "All loggers report loopAtEndChanged: " << (enabled ? "enabled" : "disabled") << std::endl; } void DtPlaybackInterfacePrinter::playOnLoadChanged(bool play) { DtRemoteControlPlaybackInterface::playOnLoadChanged(play); DtInfo << "All loggers report playOnLoadChanged: " << (play ? "play" : "pause") << std::endl; } void DtPlaybackInterfacePrinter::playbackSpeedChanged(double speed) { DtRemoteControlPlaybackInterface::playbackSpeedChanged(speed); DtInfo << "All loggers report playbackSpeedChanged: " << speed << std::endl; if (myTransitioningSkip) { if (myPlayAfterTransitionSkip) { unPause(mySpeedRequestDestination); } myTransitioningSkip = false; mySkipEnabled = false; } } void DtPlaybackInterfacePrinter::pausedChanged(bool paused) { DtRemoteControlPlaybackInterface::pausedChanged(paused); DtInfo << "All loggers report pausedChanged: " << (paused ? "paused" : "unpaused") << std::endl; if (myTransitioningSkip) { jumpToTime(myInterface->lowestTime(), mySpeedRequestDestination); } } void DtPlaybackInterfacePrinter::startStopTimesChanged( const DtAbsoluteTime& startTime, const DtAbsoluteTime& stopTime) { DtRemoteControlPlaybackInterface::startStopTimesChanged(startTime, stopTime); DtInfo << "All loggers report startStopTimesChanged: " << " start: " << startTime << " stop: " << stopTime << std::endl; } void DtPlaybackInterfacePrinter::firstLastTimesChanged( const DtAbsoluteTime& firstTime, const DtAbsoluteTime& lastTime) { DtRemoteControlPlaybackInterface::firstLastTimesChanged(firstTime, lastTime); DtInfo << "All loggers report firstLastTimesChanged: " << " first: " << firstTime << " last: " << lastTime << std::endl; } DtSequenceNumber DtPlaybackInterfacePrinter::setPlaybackSpeed( double speed, bool skipping, const DtEntityIdentifier& destination/*=DtRemoteControlInterface::wildcardAddress()*/ ) { DtSequenceNumber seqNum(0); if (!isPlaying() || !mySkipEnabled || mySkipEnabled == skipping) { // Not changing from skipping to non skipping. Safe to just change the speed. mySkipEnabled = skipping; seqNum = DtRemoteControlPlaybackInterface::setPlaybackSpeed( speed, skipping, destination); } else { // Changing from skipping to non skipping. Playback should be paused // and then synchronize the time by jumping to lowest time of all loggers // and then setting the speed as originally requested // and finally resuming playback. myTransitioningSkip = true; mySpeedRequest = speed; mySpeedRequestDestination = destination; myPlayAfterTransitionSkip = !isPaused(); if (isPaused()) { seqNum = jumpToTime(myInterface->lowestTime(), mySpeedRequestDestination); } else { seqNum = pause(destination); } } return seqNum; }