MAK Data Logger API Documentation for HLA
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
lgrControl Example

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.

Writing the lgrControl Application

To write a remote control application, you need to:

  1. Create an exercise connection
    DtExerciseConn* conn = 0;
  2. Identify the Logger(s) that will recieve commands (default is wildcard for all Loggers)

DtEntityIdentifier lgrEntityID("65535:65535:65535");

  1. Identify a unique ID for this remote control interface

remoteControl->setConnection(selfEntityID, backChannelConnection);

  1. Applications control the Logger through the remote control interface and domain-specific interfaces.
  2. Add callbacks for actions you want to keep track of.

  1. 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.

lgrControl.cxx

  1. 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

Using the lgrControl Application

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).

Note
When entering a command, once a key press is made, the processing of other tasks is paused. The help command also pauses processing so that the help text is not disturbed. It is important to not accidently press a key while the console is active and to complete commands relatively quickly so that other processing can proceed.
Console commands for Logger Control Application

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!


lgrControl.cxx

/*******************************************************************************
** Copyright (c) 1992-2024 MAK Technologies, Inc
** All rights reserved.
*******************************************************************************/
#include "consoleInput.h"
#include <vl/exerciseConnInitializer.h>
#include <vlutil/vlPrint.h>
#include <vlutil/vlInetUdpSocket.h>
#include <vlutil/vlRtiMismatchException.h>
#include <vlutil/vlMiniDumper.h>
#include <mtl/mtlEnvironment.h>
#include "vlutil/vlLogFileInfo.h"
#include "lgrUtil/svnRev.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,
DtRcRequestFileList = 24,
DtRcInfo = 96,
DtRcConfig = 97,
DtRcPrintHistory = 98,
DtRcRestoreCmd = 99,
DtRcHelp = 100
};
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)] [checkForMediaFile(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)>] [<recordMediaFile(1/0)>] [description]*",
"Load the specified file for recording with option to\n"
"\toverwrite (enabled), construct name using logger ID (disabled),\n"
"\tand record media file(disabled); remaining input used for description"},
{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"},
{DtRcRequestFileList, "RequestFileList", "[listDirs(1/0)]", "Request a file list from Loggers' default directory (normal files by default)"},
{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)
, myNumberOfLoggers(mySettings, "numberOfLoggers", -1, "Number of Logger Connections")
, myRemoteControlId(mySettings, "remoteControlId", 0, "Remote Control ID")
, myConnectionTimeout(mySettings, "connectionTimeout", 120.0, "Connection Timeout")
, myAcknowledgeTimeout(mySettings, "acknowledgeTimeout", 5.0, "Acknowledge Timeout")
, myUseBackChannel(mySettings, "useBackChannel", 1, "Use back channel connection?")
, myBackChannelAddress(mySettings, "backChannelAddress", "229.7.7.53", "Back Channel Address")
, myBackChannelInterface(mySettings, "backChannelInterface", "127.0.0.1", "Back Channel Interface")
, myBackChannelPort(mySettings, "backChannelPort", 4053, "Back Channel Port")
#ifdef DtHLA_1516_EVOLVED
, mySuppressDefaultModules(mySettings, "suppressDefaultModules", false, "Suppress Default Modules",
DtBaseConfigVariable::DtScopeBoth)
#endif
{
#if DtHLA
// Temporarily replace default initialization values from VR-Link
setExecName("MAK-One-2024");
setFederateType("MAK Logger Control");
setRprFomVersion(2.0);
setFedFileName(DtDefaultRpr2Fom);
#endif
}
~DtLgrControlInitializer()
{
}
virtual void initializeMtlParams()
{
DtVrlApplicationInitializer::initializeMtlParams();
myMtlEnv->registerConfigVar(myNumberOfLoggers);
myMtlEnv->registerConfigVar(myRemoteControlId);
myMtlEnv->registerConfigVar(myConnectionTimeout);
myMtlEnv->registerConfigVar(myAcknowledgeTimeout);
myMtlEnv->registerConfigVar(myUseBackChannel);
myMtlEnv->registerConfigVar(myBackChannelAddress);
myMtlEnv->registerConfigVar(myBackChannelPort);
}
virtual void setNumberOfLoggers(int val)
{
myNumberOfLoggers.setValue(val);
}
virtual int numberOfLoggers() const
{
return myNumberOfLoggers.value();
}
virtual void setRemoteControlId(int val)
{
myRemoteControlId.setValue(val);
}
virtual int remoteControlId() const
{
return myRemoteControlId.value();
}
virtual void setConnectionTimeout(double val)
{
myConnectionTimeout.setValue(val);
}
virtual double connectionTimeout() const
{
return myConnectionTimeout.value();
}
virtual void setAcknowledgeTimeout(double val)
{
myAcknowledgeTimeout.setValue(val);
}
virtual double acknowledgeTimeout() const
{
return myAcknowledgeTimeout.value();
}
virtual void setUseBackChannel(bool val)
{
myUseBackChannel.setValue(val);
}
virtual bool useBackChannel() const
{
return myUseBackChannel.value();
}
virtual void setBackChannelPort(int val)
{
myBackChannelPort.setValue(val);
}
virtual int backChannelPort() const
{
return myBackChannelPort.value();
}
virtual void setBackChannelAddress(const DtString& val)
{
myBackChannelAddress.setValue(val);
}
virtual DtString backChannelAddress() const
{
return myBackChannelAddress.value();
}
virtual void setBackChannelInterface(const DtString& val)
{
myBackChannelInterface.setValue(val);
}
virtual DtString backChannelInterface() const
{
return myBackChannelInterface.value();
}
#ifdef DtHLA_1516_EVOLVED
void setSuppressDefaultModules(bool onOff)
{
mySuppressDefaultModules.setValue(onOff);
}
bool suppressDefaultModules() const
{
return mySuppressDefaultModules.value();
}
#endif
protected:
DtConfigVariable<int> myNumberOfLoggers;
DtConfigVariable<int> myRemoteControlId;
DtConfigVariable<double> myConnectionTimeout;
DtConfigVariable<double> myAcknowledgeTimeout;
DtConfigVariable<int> myUseBackChannel;
DtConfigVariable<DtString> myBackChannelAddress;
DtConfigVariable<DtString> myBackChannelInterface;
DtConfigVariable<int> myBackChannelPort;
#ifdef DtHLA_1516_EVOLVED
DtConfigVariable<bool> mySuppressDefaultModules;
#endif
};
//
// Application
//
int main( int argc, char* argv[] )
{
// Used for error handling
DtLogFileInfo::instance().setAppName("Logger Control");
DtLogFileInfo::instance().setProtocol(DT_PROTOCOL_SHORT_NAME);
DtLogFileInfo::instance().setSvnRev(SVN_REV);
DtLogFileInfo::instance().setVersion(LOGGER_VERSION_STRING);
DtINIT_MINIDUMPER(DtLogFileInfo::instance().getFullString().c_str());
// 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();
#if DtHLA_1516_EVOLVED
if (!appInit.suppressDefaultModules())
{
std::vector<DtString> fomModules;
fomModules.push_back("MAK-VRFExt-6_evolved.xml");
fomModules.push_back("MAK-DIGuy-7_evolved.xml");
fomModules.push_back("MAK-LgrControl-2_evolved.xml");
fomModules.push_back("MAK-VRFAggregate-3_evolved.xml");
fomModules.push_back("MAK-DynamicTerrain-2_evolved.xml");
appInit.setFomModules(fomModules);
}
#endif
// Identify yourself
DtEntityIdentifier selfEntityID(DtSimulationAddress(53,53), appInit.remoteControlId());
DtExerciseConn* conn = 0;
DtInetUdpSocket* backChannelConnection = 0;
// Identify the receiving (remote) Logger
DtEntityIdentifier lgrEntityID("65535:65535:65535");
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->setAcknowledgeTimeout(appInit.acknowledgeTimeout());
if (appInit.useBackChannel())
{
DtInetDevice hostIf(DtInetAddr(appInit.backChannelInterface()));
backChannelConnection = new DtInetUdpSocket(
DtInetEndpoint(DtInetAddr(appInit.backChannelAddress()), appInit.backChannelPort()),
0,
((!hostIf.addr() || hostIf.addr()->isAnyAddr()) ? 0 : &hostIf),
DtDefaultSockOpts | DtSockOptNonBlocking);
remoteControl->setConnection(selfEntityID, backChannelConnection);
}
else
{
try
{
conn = new DtExerciseConn(appInit);
}
catch( DtVlRtiMismatchException )
{
DtFatal << "Mismatching RTI compiler Version. Please reconfigure your environment" << std::endl;
return -1;
}
selfEntityID.setSimulationAddress(conn->applicationId());
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 )
{
if (conn)
{
// Advance simulation time
conn->clock()->setSimTime( conn->clock()->elapsedRealTime() );
}
if (!skipTick)
{
if (conn)
{
// 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 = false;
bool playMedia = false;
char* cmdArg = getToken( &token, " \t\n\r" );
if (cmdArg && *cmdArg != '\0')
{
constructFileName = atoi(cmdArg) == 1;
cmdArg = getToken(&token, " \t\n\r");
if (cmdArg && *cmdArg != '\0')
{
playMedia = atoi(cmdArg) == 1;
}
}
// Load file 'optionalArg' for playback
playbackInterface->openFile(optionalArg, constructFileName, playMedia, lgrEntityID);
}
break;
}
case DtRcLoadRecord:
{
if (!playbackInterface->isPlaying())
{
bool overwrite = true;
bool constructFileName = false;
bool recordMedia = false;
DtString description("");
// Collect optional arguments when present
char* cmdArg = nullptr;
int argIndex = 0;
while ((cmdArg = getToken(&token, " \t\n\r")) && *cmdArg != '\0')
{
switch (argIndex++)
{
case 0:
overwrite = atoi(cmdArg) != 0;
break;
case 1:
constructFileName = atoi(cmdArg) != 0;
break;
case 2:
recordMedia = atoi(cmdArg) == 1;
break;
default:
description += cmdArg;
description += " ";
description += token;
*token = '\0';
break;
}
}
//Check if any file is open for playback, and whether that file needs to be saved or not
if (playbackInterface->isFileOpened() && !playbackInterface->isFileChanged() )
{
//No changes since last save, close the file
playbackInterface->closeFile(lgrEntityID);
}
else if(playbackInterface->isFileOpened() && playbackInterface->isFileChanged() && !overwrite)
{
DtInfo << "The current Logger tape has been modified since the last save. Please save or unload the open Logger file before continuing." << std::endl;
break;
}
// Load file 'optionalArg' for recording
recordInterface->newFile(optionalArg, description, overwrite, constructFileName, recordMedia, 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
{
playbackInterface->jumpToTime(time, lgrEntityID);
}
catch (DtInvalidInput*)
{
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
{
if (skipTime.seconds() < 0)
{
playbackInterface->skipTimeBackward(-skipTime.seconds(), lgrEntityID);
}
else
{
playbackInterface->skipTimeForward(skipTime, lgrEntityID);
}
}
catch (DtInvalidInput*)
{
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
{
if (flag == DtLGR_STATE_REQUEST)
{
systemInterface->requestStateUpdate(lgrEntityID);
}
else
{
systemInterface->requestToggleFlag(flag, lgrEntityID);
}
}
}
break;
}
#if DtDIS
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;
#if DtHLA_1516_EVOLVED
DtInfo << "Federate name: " << appInit.federateName() << std::endl;
#endif
DtInfo << "Federate type: " << appInit.federateType() << std::endl;
DtInfo << "FOM filename: " << appInit.fedFileName() << std::endl;
#if DtHLA_1516_EVOLVED
DtInfo << "MIM filename: " << appInit.mimModule() << std::endl;
DtInfo << "Suppress Default FOM modules: " << appInit.suppressDefaultModules() << 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
DtInfo << "Use Back Channel: " << appInit.useBackChannel() << std::endl;
DtInfo << "Back Channel Interface: " << appInit.backChannelInterface() << std::endl;
DtInfo << "Back Channel Address: " << appInit.backChannelAddress() << std::endl;
DtInfo << "Back Channel Port: " << appInit.backChannelPort() << std::endl;
break;
}
case DtRcRequestFileList:
{
bool listDirs = false;
if ( optionalArg && *optionalArg!= '\0' )
{
listDirs = atoi(optionalArg) != 0;
}
playbackInterface->requestFileList(listDirs, lgrEntityID);
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;
}
}
}
}
DtDELETE(remoteControl);
DtDELETE(conn);
DtDELETE(backChannelConnection);
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;
}

playbackInterfacePrinter.cxx

/*******************************************************************************
** Copyright (c) 1992-2024 MAK Technologies, Inc
** All rights reserved.
*******************************************************************************/
#include <vlutil/vlPrint.h>
using namespace MAKLogger;
, mySkipEnabled(false)
, myTransitioningSkip(false)
, myPlayAfterTransitionSkip(false)
{
}
{
}
{
return new DtPlaybackInterfacePrinter(rci);
}
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;
}
{
DtRemoteControlPlaybackInterface::fileClosed();
DtInfo << "All loggers report fileClosed " << std::endl;
}
const DtEntityIdentifier& source, const DtString& fileName)
{
DtRemoteControlPlaybackInterface::fileDoesNotExist(source, fileName);
DtInfo << "All loggers report source: " << source.string() << " fileDoesNotExist: " << fileName << std::endl;
}
const DtEntityIdentifier& source, const DtString& fileName, bool isIncomplete,
bool hasLastPacket, bool lastPacketRecovered)
{
DtRemoteControlPlaybackInterface::fileIncomplete(source, fileName, isIncomplete,
hasLastPacket, lastPacketRecovered);
if (isIncomplete)
{
DtInfo << "All loggers report source: " << source.string() << " fileIncomplete: "
<< fileName << " isIncomplete: " << (isIncomplete ? "yes" : "no")
<< " hasLastPacket: " << (hasLastPacket ? "yes" : "no")
<< " lastPacketRecovered: " << (lastPacketRecovered ? "yes" : "no") << std::endl;
}
}
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;
}
{
DtRemoteControlPlaybackInterface::fileChanged();
DtInfo << "All loggers report fileChanged " << std::endl;
}
{
DtRemoteControlPlaybackInterface::fileSaved();
DtInfo << "All loggers report fileSaved " << std::endl;
}
{
DtRemoteControlPlaybackInterface::fileFormatCurrent(current);
DtInfo << "All loggers report fileFormatCurrent: " << (current ? "yes" : "no") << std::endl;
}
{
DtRemoteControlPlaybackInterface::playbackChanged(currentlyPlaying);
DtInfo << "All loggers report playbackChanged: " << (currentlyPlaying ? "playing" : "not playing") << std::endl;
}
{
DtRemoteControlPlaybackInterface::restarted();
DtInfo << "All loggers report restarted " << std::endl;
}
{
DtRemoteControlPlaybackInterface::jumpTimeChanged(jumpTime);
{
DtRemoteControlPlaybackInterface::setPlaybackSpeed( mySpeedRequest, false, mySpeedRequestDestination);
}
}
{
DtRemoteControlPlaybackInterface::loopAtEndChanged(enabled);
DtInfo << "All loggers report loopAtEndChanged: " << (enabled ? "enabled" : "disabled") << std::endl;
}
{
DtRemoteControlPlaybackInterface::playOnLoadChanged(play);
DtInfo << "All loggers report playOnLoadChanged: " << (play ? "play" : "pause") << std::endl;
}
{
DtRemoteControlPlaybackInterface::playbackSpeedChanged(speed);
DtInfo << "All loggers report playbackSpeedChanged: " << speed << std::endl;
{
{
}
mySkipEnabled = false;
}
}
{
DtRemoteControlPlaybackInterface::pausedChanged(paused);
DtInfo << "All loggers report pausedChanged: " << (paused ? "paused" : "unpaused") << std::endl;
{
}
}
const DtAbsoluteTime& startTime, const DtAbsoluteTime& stopTime)
{
DtRemoteControlPlaybackInterface::startStopTimesChanged(startTime, stopTime);
DtInfo << "All loggers report startStopTimesChanged: " << " start: " << startTime << " stop: " << stopTime << std::endl;
}
const DtAbsoluteTime& firstTime, const DtAbsoluteTime& lastTime)
{
DtRemoteControlPlaybackInterface::firstLastTimesChanged(firstTime, lastTime);
DtInfo << "All loggers report firstLastTimesChanged: " << " first: " << firstTime << " last: " << lastTime << std::endl;
}
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.
mySpeedRequest = speed;
if (isPaused())
{
}
else
{
seqNum = pause(destination);
}
}
return seqNum;
}

Document ID: Generated on Thu Oct 3 15:35:11 EDT 2024 from SVN revision 270037
Copyright © 2024 MAK Technologies. All Rights Reserved (www.mak.com)