mirror of
https://github.com/nasa/trick.git
synced 2024-12-30 09:58:53 +00:00
500 lines
21 KiB
Plaintext
500 lines
21 KiB
Plaintext
/**
|
|
@page LEVEL2 Variable Server
|
|
|
|
When running a %Trick simulation, unless specifically turned off, a server called the
|
|
"variable server" is always up and listening in a separate thread of execution. The
|
|
variable server is privy to simulation parameters and their values since it resides
|
|
in an asynchronous simulation thread. Threads share the same address space as their
|
|
siblings and parent. Clients connect to the variable server in order to set/get
|
|
values of %Trick processed variables. You may already be familiar with the %Trick
|
|
applications that use the variable server: the simulation control panel, %Trick
|
|
View (TV) , Event/Malfunction %Trick View (MTV) , and the stripchart.
|
|
|
|
The variable server is a convenient way for external applications to interact with
|
|
the simulation. Any application that needs to set or get simulation parameters may
|
|
do so through the variable server. The external application need not be on the same
|
|
machine since the connection to the variable server is via a %Trick communication
|
|
TCP/IP socket.
|
|
|
|
@section LEVEL3 Variable Server Broadcast Channel
|
|
|
|
To connect to the variable server for any simulation, a client needs to know the
|
|
hostname and port. As of 10.5, the port number is determined by the OS. For external
|
|
applications the best way to find a varible server port is to listen to the variable
|
|
server broadcast channel. Every simulation variable server will broadcast the host and port
|
|
number to the broadcast channel. The channel is address 224.3.14.15 port 9265. All simulations
|
|
on your network sends it's information to this address and port so there may be multiple
|
|
messages with variable server information available here. Here is some
|
|
C code that reads all messages on the variable server channel.
|
|
|
|
@code
|
|
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
|
|
int main() {
|
|
|
|
int mcast_socket ;
|
|
char buf1[1024] ;
|
|
ssize_t num_bytes ;
|
|
int value = 1;
|
|
struct sockaddr_in sockin ;
|
|
struct ip_mreq mreq;
|
|
|
|
if ((mcast_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
|
perror("init socket");
|
|
}
|
|
|
|
if (setsockopt(mcast_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &value, (socklen_t) sizeof(value)) < 0) {
|
|
perror("setsockopt: reuseaddr");
|
|
}
|
|
#ifdef SO_REUSEPORT
|
|
if (setsockopt(mcast_socket, SOL_SOCKET, SO_REUSEPORT, (char *) &value, sizeof(value)) < 0) {
|
|
perror("setsockopt: reuseport");
|
|
}
|
|
#endif
|
|
|
|
// Use setsockopt() to request that the kernel join a multicast group
|
|
mreq.imr_multiaddr.s_addr = inet_addr("224.3.14.15");
|
|
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
|
|
if (setsockopt(mcast_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, (socklen_t) sizeof(mreq)) < 0) {
|
|
perror("setsockopt: ip_add_membership");
|
|
}
|
|
|
|
// Set up destination address
|
|
sockin.sin_family = AF_INET;
|
|
sockin.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
sockin.sin_port = htons(9265);
|
|
|
|
if ( bind(mcast_socket, (struct sockaddr *) &sockin, (socklen_t) sizeof(sockin)) < 0 ) {
|
|
perror("bind");
|
|
}
|
|
|
|
do {
|
|
num_bytes = recvfrom(mcast_socket, buf1, 1024, 0 , NULL, NULL) ;
|
|
if ( num_bytes > 0 ) {
|
|
buf1[num_bytes] = '\0' ;
|
|
printf("%s\n" , buf1) ;
|
|
}
|
|
} while ( num_bytes > 0 ) ;
|
|
|
|
return 0 ;
|
|
}
|
|
@endcode
|
|
|
|
The information sent by each variable server is a tab delimited list of strings
|
|
-# Hostname
|
|
-# Port
|
|
-# User
|
|
-# Process ID (PID)
|
|
-# Simulation directory
|
|
-# S_main command line name
|
|
-# Input file
|
|
-# %Trick version of simulation
|
|
-# User defined tag
|
|
-# Port (duplicate field for backwards compatibility)
|
|
|
|
@section LEVEL3 User accessible routines
|
|
|
|
These commands are for enabling/disabling the variable server, and for getting its status.
|
|
The variable server is enabled by default.
|
|
|
|
@li int var_server_set_enabled(int on_off)
|
|
@li int var_server_get_enabled()
|
|
|
|
@note <b>Disabling the variable server will disable all %Trick runtime GUIs: simulation
|
|
control panel, TV, MTV, and stripchart.</b>
|
|
|
|
These commands are for toggling information messages from the variable server (i.e., commands received from <i>ALL</i> clients).
|
|
The messages go to the terminal, the simulation control panel, and the "send_hs" file in the RUN directory.
|
|
The variable server information message capability is off by default.
|
|
|
|
@li int @link Trick::VariableServer::set_var_server_info_msg_off() set_var_server_info_msg_off() @endlink
|
|
@li int @link Trick::VariableServer::set_var_server_info_msg_on() set_var_server_info_msg_on() @endlink
|
|
|
|
These commands are also for toggling information messages from the variable server (i.e., commands received from <i>ALL</i> clients).
|
|
The messages only go to a dedicated "varserver_log" file in the RUN directory.
|
|
The variable server log capability is off by default.
|
|
|
|
@li int @link Trick::VariableServer::set_var_server_log_off() set_var_server_log_off() @endlink
|
|
@li int @link Trick::VariableServer::set_var_server_log_on() set_var_server_log_on() @endlink
|
|
|
|
@section LEVEL4 Getting and Setting the Variable Server Port Information
|
|
|
|
To set the variable server port to a fixed number in the input file use var_server_set_port()
|
|
|
|
@code trick.var_server_set_port( unsigned int port ) @endcode
|
|
|
|
To get the variable server host and port information in the input file use var_server_get_hostname() and
|
|
var_server_get_port().
|
|
|
|
@code
|
|
trick.var_server_get_hostname()
|
|
trick.var_server_get_port()
|
|
@endcode
|
|
|
|
@section LEVEL3 Commands
|
|
|
|
The variable server accepts commands in the form of strings. The variable server parses
|
|
these commands using the Python input processor. So in theory, any Python valid syntax
|
|
is acceptable to the variable server. This section lists the commands that are specific
|
|
for the variable server. Commands are sent over a %Trick communication TCP/IP socket to
|
|
the variable server. Multiple commands (newline separated) can be sent in the string
|
|
over the socket. The variable server will send back information to the requesting client.
|
|
|
|
If the command contains a syntax error, Python will print an error message to the screen,
|
|
but nothing will be returned to the client.
|
|
|
|
@section LEVEL4 Adding a Variable
|
|
|
|
@code trick.var_add( string var_name ) @endcode
|
|
or
|
|
@code trick.var_add( string var_name , string units ) @endcode
|
|
|
|
Adding a variable will tell the variable server to send the variable's value back to the
|
|
client at a specified frequency. An optional units parameter may be attached to the
|
|
variable as the desired return units. Multiple variables may be added to the list to be sent
|
|
back to the client. The format of the returned values are described below, Ascii Format
|
|
or binary format.
|
|
|
|
@section LEVEL4 Time Homogeneous or Synchronous Data
|
|
|
|
@section LEVEL5 Copying Data Out of Simulation.
|
|
|
|
@code trick.var_set_copy_mode(int mode) @endcode
|
|
|
|
There are 3 options to when the variable server will copy data out from the simulation.
|
|
Each option has unique capabilites.
|
|
|
|
@par Asynchronous Copy (mode = trick.VS_COPY_ASYNC or 0)
|
|
|
|
This is the default. Values are copied out of the sim asynchronously. Copies are done
|
|
approximately at the var_cycle() rate during run and freeze mode. A separate thread
|
|
is used to copy the data. The data is not guaranteed to be time homogenous. This mode
|
|
does not affect the main thread real-time performance.
|
|
|
|
@par End of Main Thread Execution Copy (mode = trick.VS_COPY_SCHEDULED or 1)
|
|
|
|
This mode copies data at the end of execution frame. Copies are done exactly at the
|
|
var_cycle() rate after the main thread has finished all of it's jobs scheduled to run
|
|
at that time step both in run and freeze mode. All variables solely calculated in the
|
|
main thread are guaranteed to be time homogenous. Variables calculated in child
|
|
threads are not guaranteed to be time homogenous. Copying data may very slightly
|
|
affect the main thread real-time performance.
|
|
|
|
@par Top of Frame Copy (mode = trick.VS_COPY_TOP_OF_FRAME or 2)
|
|
|
|
This mode copies data at the top of frame. Copies are done at a multiple and offset of
|
|
the Executive software frame. During freeze mode copies are made at a multiple and offset
|
|
of the freeze frame. With careful planning, all variables from all threads can be
|
|
guaranteed to be time homogenous. Copying data may very slightly affect the main thread
|
|
real-time performance.
|
|
|
|
To set the frame multiplier and frame offset between copies use the following commands.
|
|
The frame refers to the software frame in the Executive. In freeze mode a different
|
|
multiplier and offset are used.
|
|
|
|
@code
|
|
trick.var_set_frame_multiplier(int mult)
|
|
trick.var_set_frame_offset(int offset)
|
|
|
|
trick.var_set_freeze_frame_multiplier(int mult)
|
|
trick.var_set_freeze_frame_offset(int offset)
|
|
@endcode
|
|
|
|
@section LEVEL5 Writing Data Out of Simulation.
|
|
|
|
@code trick.var_set_write_mode(int mode) @endcode
|
|
|
|
There are 2 options when the variable server writes the data.
|
|
|
|
@par Asynchronous Write ( mode = trick.VS_WRITE_ASYNC or 0 )
|
|
|
|
This is the default. Values are written onto the socket asynchronously. Writes are done
|
|
approximately at the var_cycle() rate during run and freeze mode. A separate thread
|
|
is used to copy write data. This mode does not affect the main thread real-time performance.
|
|
|
|
@par Write When Copied ( mode = trick.VS_WRITE_WHEN_COPIED or 1 )
|
|
|
|
Values are written onto the socket as soon as they are copied from the simulation. The
|
|
write rate depends on the copy. Writes are done in the main thread of execution. This
|
|
can greatly affect real-tim performance if a large amount of data is requested.
|
|
|
|
@section LEVEL5 Old Style var_sync() Command
|
|
|
|
@code trick.var_sync(bool mode) @endcode
|
|
|
|
var_sync() was previously used to control the copies and writes from the simulation.
|
|
The number of options has outgrown what a single var_sync command can configure. It
|
|
may still be used to configure a subset of the copy/write combinations.
|
|
|
|
@code
|
|
trick.var_sync(0) # asynchronous copy and asynchronous write.
|
|
trick.var_sync(1) # end of main thread copy and asynchronous write.
|
|
trick.var_sync(2) # end of main thread copy and write when copied.
|
|
@endcode
|
|
|
|
@section LEVEL4 Sending the Return Values Immediately
|
|
|
|
@code trick.var_send() @endcode
|
|
|
|
The var_send command forces the variable server to return the list of values to the
|
|
client immediately.
|
|
|
|
@section LEVEL4 Changing the Units
|
|
|
|
@code trick.var_units( string var_name , string units ) @endcode
|
|
|
|
The returned values can be converted to other units of measurments. The var_units command
|
|
tells the variable server what units to use. If the units are changed, then the units
|
|
are included in the returned string to the client.
|
|
|
|
@section LEVEL4 Removing a Variable
|
|
|
|
@code trick.var_remove( string var_name ) @endcode
|
|
|
|
Removing a variable removes the variable from the list returned to the client.
|
|
|
|
@section LEVEL4 Clearing the List of Variables
|
|
|
|
@code trick.var_clear() @endcode
|
|
|
|
To clear the whole list of variables sent to the client.
|
|
|
|
@section LEVEL4 Exiting the Variable Server
|
|
|
|
@code trick.var_exit() @endcode
|
|
|
|
Disconnects the current client from the variable server.
|
|
|
|
@section LEVEL4 Checking for existence of a variable
|
|
|
|
@code trick.var_exists( string var_name ) @endcode
|
|
|
|
To test if a variable name exists. A special return message is sent to the client when
|
|
this command is processed.
|
|
|
|
The return message is in this format:
|
|
@code1<1 byte binary 0 or 1>@endcode
|
|
|
|
@section LEVEL4 Changing the Return Value Cycle Rate
|
|
|
|
@code trick.var_cycle( double cycle_rate ) @endcode
|
|
|
|
Changes the rate of the return messages to the client. This rate is estimated and may not
|
|
perfectly match the requested rate.
|
|
|
|
@section LEVEL4 Pause the Variable Server
|
|
|
|
@code trick.var_pause() @endcode
|
|
|
|
Pauses the return values sent to the client. Even when paused, the variable server will
|
|
accept new commands.
|
|
|
|
@section LEVEL4 Unpause the Variable Server
|
|
|
|
@code trick.var_unpause() @endcode
|
|
|
|
Resumes sending the return values to the client.
|
|
|
|
@section LEVEL4 Setting Ascii Return Format
|
|
|
|
@code trick.var_ascii() @endcode
|
|
|
|
Sets the return message format to ASCII. See below for the format of the message.
|
|
|
|
@section LEVEL4 Setting Binary Return Format
|
|
|
|
@code trick.var_binary() @endcode
|
|
|
|
Sets the return message format to Binary. See below for the format of the message.
|
|
|
|
@code trick.var_binary_nonames() @endcode
|
|
|
|
This variation of the binary format reduces the amount of data that is sent to the client.
|
|
See below for the exact format.
|
|
|
|
@section LEVEL4 Sending stdout and stderr to client
|
|
|
|
@code trick.var_set_send_stdio(bool on_off) @endcode
|
|
|
|
If var_set_send_stdio is called with a true value, then all python stdout and stderr
|
|
output will be redirected to the client instead of printing to the simulation stdout/stderr
|
|
location. Note: output from C/C++ code called from python will direct it's output to
|
|
the simulation stdout/stderr location. See the return message format for Stdio.
|
|
|
|
This is useful to get output from the simulation such as the return values of a function.
|
|
|
|
@code
|
|
# Example in a variable server client to get the Trick version used to compile a sim
|
|
# The C prototype is "const char *exec_get_current_version(void) ;"
|
|
trick.var_set_send_stdio(True)
|
|
sys.stdout.write(trick.exec_get_current_version())
|
|
|
|
# The returned text will look like this. See the return message format below
|
|
4 1 10
|
|
10.7.dev-1
|
|
|
|
# If a "print" is used instead of sys.stdout.write, a second message is sent containing
|
|
# a single newline.
|
|
print "trick.exec_get_current_version()"
|
|
|
|
4 1 10
|
|
10.7.dev-14 1 1
|
|
<- a single newline is the second message
|
|
|
|
@endcode
|
|
|
|
@section LEVEL4 Debugging Variable Server Messages
|
|
|
|
@code trick.var_debug(int level) @endcode
|
|
|
|
The level may range from 0-3. The larger the number the more debugging information is printed to
|
|
the screen (for the current client only).
|
|
|
|
@section LEVEL4 Logging Messages to file.
|
|
|
|
These commands are for toggling information messages from the variable server (for this client only).
|
|
The messages only go to a dedicated "varserver_log" file in the RUN directory.
|
|
The variable server log capability is off by default. (See the global variable server commands
|
|
@link Trick::VariableServer::set_var_server_log_on() set_var_server_log_on() @endlink and
|
|
@link Trick::VariableServer::set_var_server_log_off() set_var_server_log_off() @endlink for toggling
|
|
the logging capability for <i>ALL</i> clients.)
|
|
|
|
@code
|
|
trick.var_server_log_on()
|
|
trick.var_server_log_off()
|
|
@endcode
|
|
|
|
@section LEVEL4 Setting Variable Server Client Tag
|
|
|
|
@code trick.var_set_client_tag(string name) @endcode
|
|
|
|
This sets an identifying name tag to be associated with the current client that will be printed with each information message
|
|
displayed. Information messages are displayed as a result of
|
|
@link Trick::VariableServer::set_var_server_info_msg_on() set_var_server_info_msg_on() @endlink,
|
|
@c var_server_log_on() or
|
|
@c var_debug(). For instance, %Trick sets a name tag for each of its variable server clients (simulation control panel is "SimControl",
|
|
TV is "TRICK_TV", etc.).
|
|
|
|
@section LEVEL4 Byteswapping
|
|
|
|
@code trick.var_byteswap(bool on_off) @endcode
|
|
|
|
|
|
@section LEVEL3 Returned Values
|
|
|
|
By default the values retrieved are sent asynchronously to the client. That is, the values
|
|
retrieved by the variable server are pulled directly from memory asynchronously and do not
|
|
guarantee synchronization from the same simulation execution frames unless the var_sync
|
|
command is used. Values will be returned to the client in the same order that they were
|
|
issued in the var_add command(s). Typically the client receives the data from the variable
|
|
server in a buffer via the tc_read command (see TrickComm for more information).
|
|
|
|
@subsection LEVEL3 Ascii Format
|
|
|
|
The default format, or if var_ascii is commanded specifically, causes the variable server
|
|
to return a buffer containing a tab delimited character string in the following format:
|
|
|
|
@code 0\t<variable1 value>[\t<variable2 value>. . .\t<variableN value>]\n @endcode
|
|
|
|
where N is the number of variables registered via the var_add command(s). The "\t" represents
|
|
a tab character, and the "\n" is the newline character that always ends the string. Note
|
|
that if a value being returned is itself a character string data type, any tab (or other
|
|
unprintable character) that occurs within the character string value will appear as an
|
|
escaped character, i.e. preceded by a backslash.
|
|
|
|
The 1st value returned in the list will always be a message indicator. The possible
|
|
values of the message indicator are:
|
|
- 0 returned variable value(s) from var_add or var_send
|
|
- 1 returned value from var_exists
|
|
- 2 returned value from send_sie_resource (special command used by Trick View)
|
|
- 3 returned value from send_event_data (special command used by Events/Malfunctions Trick View)
|
|
.
|
|
If the variable units are also specified along with the variable name in a var_add or
|
|
var_units command, then that variable will also have its units specification returned following
|
|
its associated value separated by a single blank. For example, if the 2nd of N variables was
|
|
specified with {<units>} in either a var_add or var_units command, the returned string would
|
|
be in the following format:
|
|
|
|
@code 0\t<variable1 value>\t<variable2 value> {<variable2 units>}. . .\t<variableN value>\n @endcode
|
|
|
|
Note that the maximum message size that the variable server sends to the client is 8192 bytes.
|
|
If the amount of data requested is larger than that, the ASCII message will be split into
|
|
multiple messages. The client is responsible for concatenating the multiple messages back
|
|
together. (Hint: look for the "\n" delimter)
|
|
|
|
If a syntax error occurs when processing the variable server client command, Python will print
|
|
an error message to the screen, but nothing will be returned to the client.
|
|
|
|
If a var_add command was issued for a non-existent variable, there will be a one time %Trick error
|
|
message printed to the screen, but the resulting data sent to the client is still ok. The value
|
|
returned for the non-existent variable is the string "BAD_REF".
|
|
|
|
@subsection LEVEL3 Binary Format
|
|
|
|
By specifying the var_binary or var_binary_nonames command, the variable server will return
|
|
values in a binary message formatted as follows:
|
|
|
|
@code
|
|
<message_indicator><message_size><N>
|
|
<variable1_namelength><variable1_name><variable1_type><variable1_size><variable1_value>
|
|
<variable2_namelength><variable2_name><variable2_type><variable2_size><variable2_value>
|
|
. . .
|
|
<variableN_namelength><variableN_name><variableN_type><variableN_size><variableN_value>
|
|
@endcode
|
|
|
|
Where the first 12 bytes are the message header:
|
|
- @e message_indicator is the same possible values as in var_ascii shown above : a 4 byte integer
|
|
- @e message_size is the total size of the message in bytes (NOT including message_indicator) : a 4 byte integer
|
|
- @e N is the number of variables registered via the var_add command(s) : a 4 byte integer
|
|
.
|
|
and the remaining bytes of the message contain the variable data:
|
|
- @e variable_namelength is the string length of the variable name : a 4 byte integer (NOT present for var_binary_nonames)
|
|
- @e variable_name is the ASCII variable name string : @e variable_namelength bytes of string (NOT present for var_binary_nonames)
|
|
- @e variable_type is %Trick data type of the variable : a 4 byte integer (see Trick::MemoryManager::TRICK_TYPE)
|
|
- @e variable_size is number of bytes the variable occupies in memory : a 4 byte integer
|
|
- @e variable_value is the variable's current value : @e variable_size bytes of @e variable_type
|
|
|
|
When the client has requested a very large amount of data, it is possible that it may require
|
|
more than one message to be returned. The maximum message size is 8192 bytes, so if the data
|
|
returned by the variable server requires more space than that (once formatted into the above
|
|
message format), then the variable server sends more than one message. This is indicated by
|
|
the @e N field. For example, if the client has requested 15 variables, and @e N = 15, then
|
|
everything is contained in that one message. However if @e N < 15, then the client should
|
|
continue reading messages until all @e N received add up to 15.
|
|
|
|
If a syntax error occurs when processing the variable server client command, Python will print
|
|
an error message to the screen, but nothing will be returned to the client.
|
|
|
|
If a var_add command was issued for a non-existent variable, there will be a one time %Trick error
|
|
message printed to the screen, but the resulting data sent to the client is still ok. The message
|
|
returned for the non-existent variable will have a type of 24 and it's value will be the string "BAD_REF".
|
|
|
|
@subsection LEVEL3 Stdio Format
|
|
|
|
These messages are sent to the client if stdout and stderr are redirected. See "Sending stdout
|
|
and stderr to client" for more details.
|
|
|
|
@code
|
|
4 <stream> <size>
|
|
<text>
|
|
@endcode
|
|
|
|
- @e message_id Stdio messages are message_id = 4.
|
|
- @e stream is the stream the message was written to. 1 = stdout, 2 = stderr
|
|
- @e size is the number of bytes in the <text> section. The newline between the <size> and <text>
|
|
is not counted in the size.
|
|
- @e text is the message
|
|
|
|
Only output from python is redirected, i.e. "print" or calls to "sys.stdout.write()". C/C++ code
|
|
called from python will still direct their stdout/stderr to the simulation output location.
|
|
The "print" statement will send 2 messages, the text in the print, and an additional newline.
|
|
Calls to sys.stdout.write() only generate 1 message.
|
|
|
|
@note Error messages printed by python to stderr may be sent in multiple messages.
|
|
|
|
*/
|