Variable Server - var_send_once and integration test (#1330)

* Adds the send_once command and message type, which allows a user to request a variable to be sent immediately and only once (intended to replace the var_add, var_send, var_clear idiom that is commonly used for this purpose)
* Minor refactoring of variable server internals to reduce repeated code
* Adds SIM_test_varserv to integration test to test basic variable server functionality
* Changes graphics client for SIM_billiards to use var_send_once as an example of intended use
* Add documentation for var_send_once in docs and tutorial
* Set exit_code_enabled in trick unit tests to be true by default
* Patch for failing bookworm build
This commit is contained in:
Jacqueline Deans 2022-08-18 10:47:07 -05:00 committed by GitHub
parent 6d56c281e0
commit 9c8799610f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 529 additions and 378 deletions

View File

@ -189,6 +189,22 @@ trick.var_send()
The var_send command forces the variable server to return the list of values to the The var_send command forces the variable server to return the list of values to the
client immediately. client immediately.
#### Sending variables only once and immediately
```python
trick.var_send_once( string var_name)
```
The var_send_once command forces the variable server to return the value of the given
variable to the client immediately.
```python
trick.var_send_once( string var_list, int num_vars)
```
var_send_once can also accept a comma separated list of variables. The number of variables
in this list must match num_vars, or it will not be processed.
#### Changing the Units #### Changing the Units
```python ```python
@ -386,12 +402,17 @@ unprintable character) that occurs within the character string value will appear
escaped character, i.e. preceded by a backslash. escaped character, i.e. preceded by a backslash.
The 1st value returned in the list will always be a message indicator. The possible The 1st value returned in the list will always be a message indicator. The possible
values of the message indicator are: values of the message indicator listen in the table below.
- 0 returned variable value(s) from var_add or var_send
- 1 returned value from var_exists | Name | Value | Meaning |
- 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) or var_send_list_size | VS\_IP\_ERROR | -1 | Protocol Error|
- 4 values redirected from stdio if var_set_send_stdio is enabled | VS\_VAR\_LIST | 0 | A list of variable values. |
| VS\_VAR\_EXISTS | 1 | Response to var\_exists( variable_name )|
| VS\_SIE\_RESOURCE | 2 | Response to send_sie_resource|
| VS\_LIST\_SIZE | 3 | Response to var_send_list_size or send_event_data|
| VS\_STDIO | 4 | Values Redirected from stdio if var_set_send_stdio is enabled|
| VS\_SEND\_ONCE | 5 | Response to var\_send\_once|
If the variable units are also specified along with the variable name in a var_add or 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 var_units command, then that variable will also have its units specification returned following

View File

@ -133,8 +133,7 @@ Control Panel, as shown below.
The output of the script will display three columns of numbers. The left most The output of the script will display three columns of numbers. The left most
number is the [variable server message type](#variable-server-message-types). number is the [variable server message type](#variable-server-message-types).
Here, a message type of 0 indicates that the message is the (tab delimited) list Here, a message type of 0 indicates that the message is the (tab delimited) list
of the values we requested. This is the only message type we'll be concerned of the values we requested. The two columns to the right of the message number are
with in this tutorial. The two columns to the right of the message number are
the values of ```dyn.cannon.pos[0]``` and ```dyn.cannon.pos[1]```, in the order the values of ```dyn.cannon.pos[0]``` and ```dyn.cannon.pos[1]```, in the order
that they were specified in the script. that they were specified in the script.
@ -208,14 +207,68 @@ Suppose we wanted to get the value of the initial angle of our cannon. We don't
need to get it repeatedly, because it doesn't change. We just want to get it need to get it repeatedly, because it doesn't change. We just want to get it
once, and then to repeatedly get the position data, which changes over time. once, and then to repeatedly get the position data, which changes over time.
For this situation we can use the [**var_send**](#api-var-send) command, which For this situation, we can take one of several approaches. The most straightforward
tells the variable server to send the values specified in the session variable is the [**var_send_once**](#api-var-send-once) command, which tells the variable
list immediately, regardless of whether [**var_pause**](#api-var-pause) was server to send the values sent as arguments immediately, regardless of whether
previously commanded. [**var_pause**](#api-var-pause) was previously commanded.
To demonstrate how this works, let's add the following code to our script, right To demonstrate how this works, let's add the following code to our script, right
after the line where we sent the **var_ascii** command. after the line where we sent the **var_ascii** command.
```python
client_socket.send( "trick.var_send_once(\"dyn.cannon.init_angle\")\n")
line = insock.readline()
print line
```
In this code, we simply ask the variable server to immediately send the value of ```dyn.cannon.init_angle```,
call ```insock.readline()``` to wait for a response, and print the response when it is received.
[**var_send_once**](#api-var-send-once) does not alter the session variable list in any way.
When we run the client, the first few lines of output should look something like:
```
5 0.5235987755982988
0 0 0
0 0 0
```
The [**var_send_once**](#api-var-send-once) command uses a [message type](#variable-server-message-types) of 5
to allow a programmer to differentiate between normal session variables and var_send_once variables. var_send_once
does not alter or interfere with the session variable list, which would allow both of these features to be
used simultaneously in a sim.
The [**var_send_once**](#api-var-send-once) also allows a user to request multiple variables in a single
command. [**var_send_once**](#api-var-send-once) can accept a comma-separated list of variables as the
first argument and the number of variables in the list as the second argument.
In our example, suppose we also wanted to retrieve the initial speed of the cannonball.
We could retrieve both variables with a single command:
```python
client_socket.send( "trick.var_send_once(\"dyn.cannon.init_angle, dyn.cannon.init_speed\", 2)\n")
```
Now, when we run the client, we get both the init_angle and the init_speed with the first message.
```
5 0.5235987755982988 50
0 0 0
0 0 0
```
Another commonly used pattern to retrieve variables only once is to use the [**var_add**](#api-var-add),
[**var_send**](#api-var-send), and [**var_clear**](#api-var-clear) commands. [**var_send**](#api-var-send) tells
the variable server to send all **session** variables immediately regardless of whether [**var_pause**](#api-var-pause)
was previously commanded.
To demonstrate how this works, replace the code in the previous listing with the snippet below, right
after the line where we sent the **var_ascii** command.
```python ```python
client_socket.send( "trick.var_add(\"dyn.cannon.init_angle\")\n") client_socket.send( "trick.var_add(\"dyn.cannon.init_angle\")\n")
client_socket.send( "trick.var_send()\n" ) client_socket.send( "trick.var_send()\n" )
@ -224,6 +277,8 @@ print line
client_socket.send( "trick.var_clear()\n" ) client_socket.send( "trick.var_clear()\n" )
``` ```
In this snippet of code, we add ```dyn.cannon.init_angle``` to the session In this snippet of code, we add ```dyn.cannon.init_angle``` to the session
variable list. Then we call [**var_send**](#api-var-send) to tell the variable variable list. Then we call [**var_send**](#api-var-send) to tell the variable
server to send us the value, and wait for the response by calling server to send us the value, and wait for the response by calling
@ -501,6 +556,7 @@ Add this to the bottom of RUN_test/input.py to give it a try.
| VS\_SIE\_RESOURCE | 2 | Response to send_sie_resource| | VS\_SIE\_RESOURCE | 2 | Response to send_sie_resource|
| VS\_LIST\_SIZE | 3 | Response to var_send_list_size or send_event_data| | VS\_LIST\_SIZE | 3 | Response to var_send_list_size or send_event_data|
| VS\_STDIO | 4 | Values Redirected from stdio if var_set_send_stdio is enabled| | VS\_STDIO | 4 | Values Redirected from stdio if var_set_send_stdio is enabled|
| VS\_SEND\_ONCE | 5 | Response to var\_send\_once|
<a id=the-variable-server-api></a> <a id=the-variable-server-api></a>
### The Variable Server API ### The Variable Server API
@ -545,6 +601,13 @@ Resume periodic responses.
**var\_send()** - **var\_send()** -
Send response immediately. Send response immediately.
<a id=api-var-send-once></a>
**var\_send\_once( variable_name )** -
Immediately send the value of variable_name
**var\_send\_once( variable_list, num_variables )** -
Immediately send the value of all variables in the comma separated variable_list, or an error if the number of variables in the list does not match num_variables
<a id=api-var-clear></a> <a id=api-var-clear></a>
**var\_clear()** - **var\_clear()** -
Clear the session variable list. Clear the session variable list.

View File

@ -305,6 +305,8 @@ int var_add(std::string in_name, std::string units_name) ;
int var_remove(std::string in_name) ; int var_remove(std::string in_name) ;
int var_units(std::string var_name , std::string units_name) ; int var_units(std::string var_name , std::string units_name) ;
int var_exists(std::string in_name) ; int var_exists(std::string in_name) ;
int var_send_once(std::string in_name) ;
int var_send_once(std::string in_name, int numArgs) ;
int var_send() ; int var_send() ;
int var_clear() ; int var_clear() ;
int var_cycle(double in_rate) ; int var_cycle(double in_rate) ;

View File

@ -14,6 +14,8 @@
#include "trick/ThreadBase.hh" #include "trick/ThreadBase.hh"
#include "trick/VariableServerReference.hh" #include "trick/VariableServerReference.hh"
#include "trick/variable_server_sync_types.h" #include "trick/variable_server_sync_types.h"
#include "trick/variable_server_message_types.h"
namespace Trick { namespace Trick {
@ -120,6 +122,16 @@ namespace Trick {
*/ */
int var_exists( std::string in_name ) ; int var_exists( std::string in_name ) ;
/**
@brief @userdesc Command to immediately send the value of a comma separated list of variables
@par Python Usage:
@code trick.var_send_once("<in_name_list>", <number of variables in list>) @endcode
@param in_name_list - the variables name to retrieve, comma separated
@param num_vars - number of vars in in_name_list
@return always 0
*/
int var_send_once(std::string in_name_list, int num_vars);
/** /**
@brief @userdesc Command to instruct the variable server to immediately send back the values of @brief @userdesc Command to instruct the variable server to immediately send back the values of
variables that are registered with the var_add command variables that are registered with the var_add command
@ -369,11 +381,22 @@ namespace Trick {
*/ */
int copy_sim_data(); int copy_sim_data();
/**
@brief Copy given variable values from Trick memory to each variable's output buffer.
cyclical indicated whether it is a normal cyclical copy or a send_once copy
*/
int copy_sim_data(std::vector<VariableReference *> given_vars, bool cyclical);
/** /**
@brief Write data in the appropriate format (var_ascii or var_binary) from variable output buffers to socket. @brief Write data in the appropriate format (var_ascii or var_binary) from variable output buffers to socket.
*/ */
int write_data(); int write_data();
/**
@brief Write data from the given var only to the appropriate format (var_ascii or var_binary) from variable output buffers to socket.
*/
int write_data(std::vector<VariableReference *> var) ;
/** /**
@brief gets the send_stdio flag. @brief gets the send_stdio flag.
*/ */
@ -426,9 +449,19 @@ namespace Trick {
int transmit_file(std::string file_name); int transmit_file(std::string file_name);
/** /**
@brief Called by write_data to write data to socket in var_binary format. @brief Called by write_data to write given variables to socket in var_binary format.
*/ */
int write_binary_data( int Start, char *buf1, int PacketNum ); int write_binary_data( int Start, char *buf1, const std::vector<VariableReference *>& given_vars, VS_MESSAGE_TYPE message_type);
/**
@brief Called by write_data to write given variables to socket in var_ascii format.
*/
int write_ascii_data(char * dest_buf, const std::vector<VariableReference *>& given_vars, VS_MESSAGE_TYPE message_type );
/**
@brief Construct a variable reference from the string in_name and handle error checking
*/
VariableReference* create_var_reference(std::string in_name);
/** /**
@brief Make a time reference. @brief Make a time reference.

View File

@ -12,7 +12,8 @@ typedef enum {
VS_VAR_EXISTS = 1, VS_VAR_EXISTS = 1,
VS_SIE_RESOURCE = 2, VS_SIE_RESOURCE = 2,
VS_LIST_SIZE = 3 , VS_LIST_SIZE = 3 ,
VS_STDIO = 4 VS_STDIO = 4,
VS_SEND_ONCE = 5
} VS_MESSAGE_TYPE ; } VS_MESSAGE_TYPE ;
#endif #endif

View File

@ -23,11 +23,10 @@ class testSimObject : public Trick::SimObject {
("default_data") vst.default_vars(); ("default_data") vst.default_vars();
("initialization") vst.init(); ("initialization") vst.init();
("initialization") vst.testAddRemove(); ("initialization") vst.testAddRemove();
("initialization") vst.testUnits();
("initialization") vst.testExists(); ("initialization") vst.testExists();
("initialization") vst.testPause(); ("initialization") vst.testPause();
("initialization") vst.testOther(); ("initialization") vst.testSendOnce();
//(1.0, "scheduled") trick_ret = vst.testing();
("shutdown") vst.shutdown(); ("shutdown") vst.shutdown();
} }
}; };

View File

@ -11,6 +11,7 @@ PROGRAMMERS: ( (Lindsay Landry) (L3) (9-12-2013) )
#ifndef VS_HH #ifndef VS_HH
#define VS_HH #define VS_HH
class VSTest { class VSTest {
public: public:
@ -19,13 +20,13 @@ class VSTest {
char got_read[80]; char got_read[80];
double cycle_rate; double cycle_rate;
TCDevice bob; TCDevice comm_device;
char a; char a;
unsigned char b; unsigned char b;
short c; short c;
unsigned short d; unsigned short d;
int e; /* M xy-position */ int e; /* m xy-position */
unsigned int f; unsigned int f;
long g; long g;
unsigned long h; unsigned long h;
@ -34,7 +35,7 @@ class VSTest {
long long k; long long k;
unsigned long long l; unsigned long long l;
bool m; bool m;
int n[5];
VSTest(); VSTest();
~VSTest(); ~VSTest();
@ -51,8 +52,8 @@ class VSTest {
int testAddRemove(); int testAddRemove();
int testExists(); int testExists();
int testPause(); int testPause();
int testOther(); int testSendOnce();
int testCycle(); int testUnits();
private: private:
int get_line(char* thing); int get_line(char* thing);

View File

@ -41,7 +41,7 @@ int VSTest::vs_write(char* cmd_buffer) {
int nbytes; int nbytes;
nbytes = strlen(cmd_buffer); nbytes = strlen(cmd_buffer);
tc_write(&bob, cmd_buffer, nbytes); tc_write(&comm_device, cmd_buffer, nbytes);
return(0); return(0);
} }
@ -65,16 +65,13 @@ int VSTest::vs_read() {
break; break;
} }
num = tc_pending(&bob); num = tc_pending(&comm_device);
if (num) { if (num) {
tc_read(&bob, read_buffer, num); tc_read(&comm_device, read_buffer, num);
get_line(read_buffer); get_line(read_buffer);
break; break;
} }
} }
//std::cout << (cpu_t - cpu_st) << std::endl;
//std::cout << read_buffer << std::endl;
//printLine();
return(0); return(0);
} }

View File

@ -25,4 +25,7 @@ int VSTest::default_vars() {
k = -12345678912345;//long long k = -12345678912345;//long long
l = 12345678912345; //unsigned long l = 12345678912345; //unsigned long
m = false; //boolean m = false; //boolean
for (int i = 0; i < 5; i++) {
n[i] = i;
}
} }

View File

@ -15,21 +15,21 @@ int VSTest::init() {
char msg[256]; char msg[256];
port_num = var_server_get_port(); port_num = var_server_get_port();
hostest = var_server_get_hostname(); hostest = "localhost";
memset(&bob, '\0', sizeof(TCDevice)); memset(&comm_device, '\0', sizeof(TCDevice));
bob.hostname = const_cast<char*>(hostest); comm_device.hostname = const_cast<char*>(hostest);
bob.port = port_num; comm_device.port = port_num;
//std::cout << bob.hostname << bob.port << std::endl; //std::cout << bob.hostname << bob.port << std::endl;
bob.disable_handshaking = TC_COMM_TRUE; comm_device.disable_handshaking = TC_COMM_TRUE;
bob.disabled = TC_COMM_FALSE; comm_device.disabled = TC_COMM_FALSE;
tc_connect(&bob); tc_connect(&comm_device);
if ( tc_isValid (&bob) ) { if ( tc_isValid (&comm_device) ) {
printf ("connection is valid\n"); printf ("connection is valid\n");
} else { } else {
printf ("connection is NOT valid\n"); printf ("connection is NOT valid\n");

View File

@ -11,7 +11,7 @@ PROGRAMMERS: ( (Lindsay Landry) (L3) (9-12-2013) )
int VSTest::shutdown() { int VSTest::shutdown() {
tc_disconnect(&bob); tc_disconnect(&comm_device);
return(0); return(0);
} }

View File

@ -33,50 +33,38 @@ int VSTest::strcmp_IgnoringWhiteSpace(const char* s1, const char* s2) {
} }
} }
int VSTest::testUnits() {
// Expect cycle time of 0.01 s
int VSTest::testCycle() {
char msg[256]; char msg[256];
char suite[] = "VariableServerTest"; char suite[] = "VariableServerTest";
/* int result;
int num, num2;
double cpu_t, cpu_st;
struct rusage cpu_usg;
sprintf(msg,"trick.var_add(\"vsx.vst.l\")\n"); // INVALID UNIT CHANGE
sprintf(msg,"trick.var_add(\"vsx.vst.c\")\ntrick.var_units(\"vsx.vst.c\",\"g\")\n");
vs_write(msg); vs_write(msg);
std::cerr << "The purpose of this test is to cause an error. Error messages are expected." << std::endl;
vs_read();
result = strcmp_IgnoringWhiteSpace("0 -1234", got_read);
TRICK_EXPECT_EQ(result, 0, suite, "VariableInvalidUnits")
// currently bad programming, could get stuck in this loop // ADD UNITS
while (true) { sprintf(msg,"trick.var_add(\"vsx.vst.e\",\"m\")\n");
getrusage(RUSAGE_SELF, &cpu_usg); vs_write(msg);
cpu_st = ((double) cpu_usg.ru_utime.tv_sec) + ((double) cpu_usg.ru_utime.tv_usec/1000000.0); vs_read();
result = strcmp_IgnoringWhiteSpace("0 -1234 -123456 {m}", got_read);
TRICK_EXPECT_EQ(result, 0, suite, "VariableAddUnits")
num = tc_pending(&bob); // CHANGE UNITS
//first read. sprintf(msg,"trick.var_units(\"vsx.vst.e\",\"ft\")\n");
if (num) { vs_write(msg);
break; vs_read();
} result = strcmp_IgnoringWhiteSpace("0 -1234 -405039 {ft}", got_read);
} TRICK_EXPECT_EQ(result, 0, suite, "VariableChangeUnits")
while (true) {
getrusage(RUSAGE_SELF, &cpu_usg);
cpu_t = ((double) cpu_usg.ru_utime.tv_sec) + ((double) cpu_usg.ru_utime.tv_usec/1000000.0);
num2 = tc_pending(&bob);
if (num2 == 2*num) {
break;
}
}
//std::cout << cpu_t - cpu_st << std::endl;
// CLEAR VARIABLE SERVER
sprintf(msg,"trick.var_clear()\n"); sprintf(msg,"trick.var_clear()\n");
vs_write(msg); vs_write(msg);
*/
} }
int VSTest::testAddRemove() { int VSTest::testAddRemove() {
char msg[256]; char msg[256];
char suite[] = "VariableServerTest"; char suite[] = "VariableServerTest";
@ -89,37 +77,10 @@ int VSTest::testAddRemove() {
std::cout << got_read << std::endl; std::cout << got_read << std::endl;
result = strcmp_IgnoringWhiteSpace("0 -1234", got_read); result = strcmp_IgnoringWhiteSpace("0 -1234", got_read);
TRICK_EXPECT_EQ(result, 0, suite, "VariableAddNoUnits") TRICK_EXPECT_EQ(result, 0, suite, "VariableAddNoUnits")
trick_test_add_parent( suite , "VariableAddNoUnits" , "3700107028");
vs_read(); vs_read();
result = strcmp_IgnoringWhiteSpace("0 -1234", got_read); result = strcmp_IgnoringWhiteSpace("0 -1234", got_read);
TRICK_EXPECT_EQ(result, 0, suite, "VariableAddCyclic") TRICK_EXPECT_EQ(result, 0, suite, "VariableAddCyclic")
trick_test_add_parent( suite , "VariableAddCyclic" , "3700107028");
// INVALID UNIT CHANGE
sprintf(msg,"trick.var_units(\"vsx.vst.c\",\"g\")\n");
vs_write(msg);
std::cerr << "The purpose of this test is to cause an error. Error messages are expected." << std::endl;
vs_read();
result = strcmp_IgnoringWhiteSpace("0 -1234", got_read);
TRICK_EXPECT_EQ(result, 0, suite, "VariableInvalidUnits")
trick_test_add_parent( suite , "VariableInvalidUnits" , "");
// ADD UNITS
sprintf(msg,"trick.var_add(\"vsx.vst.e\",\"M\")\n");
vs_write(msg);
vs_read();
result = strcmp_IgnoringWhiteSpace("0 -1234 -123456 {M}", got_read);
TRICK_EXPECT_EQ(result, 0, suite, "VariableAddUnits")
trick_test_add_parent( suite , "VariableAddUnits" , "3700107028");
// CHANGE UNITS
sprintf(msg,"trick.var_units(\"vsx.vst.e\",\"ft\")\n");
vs_write(msg);
vs_read();
result = strcmp_IgnoringWhiteSpace("0 -1234 -405039 {ft}", got_read);
TRICK_EXPECT_EQ(result, 0, suite, "VariableChangeUnits")
trick_test_add_parent( suite , "VariableChangeUnits" , "2710769246");
// REMOVE SINGLE VARIABLE // REMOVE SINGLE VARIABLE
sprintf(msg,"trick.var_remove(\"vsx.vst.e\")\n"); sprintf(msg,"trick.var_remove(\"vsx.vst.e\")\n");
@ -127,18 +88,45 @@ int VSTest::testAddRemove() {
vs_read(); vs_read();
result = strcmp_IgnoringWhiteSpace("0 -1234", got_read); result = strcmp_IgnoringWhiteSpace("0 -1234", got_read);
TRICK_EXPECT_EQ(result, 0, suite, "VariableRemove") TRICK_EXPECT_EQ(result, 0, suite, "VariableRemove")
trick_test_add_parent( suite , "VariableRemove" , "806750761");
// CLEAR VARIABLE SERVER // CLEAR VARIABLE SERVER
sprintf(msg,"trick.var_clear()\n"); sprintf(msg,"trick.var_clear()\n");
vs_write(msg); vs_write(msg);
vs_read(); vs_read();
TRICK_EXPECT_EQ(strcmp(got_read, ""), 0, suite, "VariableClear") TRICK_EXPECT_EQ(strcmp(got_read, ""), 0, suite, "VariableClear")
trick_test_add_parent( suite , "VariableClear" , "835578702");
return(0); return(0);
} }
int VSTest::testSendOnce() {
char msg[256];
char suite[] = "VariableServerTest";
int result;
// SEND ONCE
sprintf(msg,"trick.var_send_once(\"vsx.vst.e\")\n");
vs_write(msg);
vs_read();
result = strcmp_IgnoringWhiteSpace("5 -123456", got_read);
TRICK_EXPECT_EQ(result, 0, suite, "VariableSendOnce")
trick_test_add_parent( suite , "VariableSendOnce" , "");
// SEND ONCE LIST
sprintf(msg,"trick.var_send_once(\"vsx.vst.n[0], vsx.vst.n[1], vsx.vst.n[2],\", 3)\n");
vs_write(msg);
vs_read();
result = strcmp_IgnoringWhiteSpace("5 0 1 2", got_read);
TRICK_EXPECT_EQ(result, 0, suite, "VariableSendOnceList")
// SEND ONCE LIST - WRONG INDICES
sprintf(msg,"trick.var_send_once(\"vsx.vst.n[0], vsx.vst.n[1], vsx.vst.n[2],\", 4)\n");
vs_write(msg);
std::cerr << "The purpose of this test is to cause an error. Error messages are expected." << std::endl;
vs_read();
result = strcmp_IgnoringWhiteSpace("", got_read);
TRICK_EXPECT_EQ(result, 0, suite, "VariableSendOnceListError")
}
int VSTest::testExists() { int VSTest::testExists() {
char msg[256]; char msg[256];
char suite[] = "VariableServerTest"; char suite[] = "VariableServerTest";
@ -156,9 +144,9 @@ int VSTest::testExists() {
sprintf(msg,"trick.var_exists(\"vsx.vst.z\")\n"); sprintf(msg,"trick.var_exists(\"vsx.vst.z\")\n");
vs_write(msg); vs_write(msg);
vs_read(); vs_read();
std::cout << "Check variable doesn't exist: " << got_read << std::endl;
result = strcmp_IgnoringWhiteSpace("1 0",got_read); result = strcmp_IgnoringWhiteSpace("1 0",got_read);
TRICK_EXPECT_EQ(result, 0, suite, "VariableNotExists") TRICK_EXPECT_EQ(result, 0, suite, "VariableNotExists")
trick_test_add_parent( suite , "VariableNotExists" , "3587464751");
sprintf(msg,"trick.var_clear()\n"); sprintf(msg,"trick.var_clear()\n");
vs_write(msg); vs_write(msg);
@ -214,16 +202,3 @@ int VSTest::testPause() {
return(0); return(0);
} }
int VSTest::testOther() {
char msg[256];
char suite[] = "VariableServerTest";
sprintf(msg,"trick.var_binary()\n");
vs_write(msg);
sprintf(msg,"trick.var_add(\"vsx.vst.l\")\n");
vs_write(msg);
vs_read();
return(0);
}

View File

@ -1,21 +0,0 @@
/******************************TRICK HEADER*************************************
PURPOSE: ( Test variable server )
REFERENCE: ( None )
ASSUMPTIONS AND LIMITATIONS: ( None )
CLASS: ( scheduled )
LIBRARY DEPENDENCY: ( VS.o )
PROGRAMMERS: ( (Lindsay Landry) (L3) (9-12-2013) )
*******************************************************************************/
#include "../include/VS.hh"
#include "sim_services/VariableServer/include/VariableServer.hh"
#include "sim_services/UnitTest/include/trick_tests.h"
int VSTest::test() {
var_ascii();
return(0);
}

View File

@ -36,7 +36,8 @@ SIMS_TO_COMPILE_AND_RUN = \
SIM_test_sched \ SIM_test_sched \
SIM_test_templates \ SIM_test_templates \
SIM_threads \ SIM_threads \
SIM_trickified SIM_trickified \
SIM_test_varserv
# Sims with problems, no purpose, or maybe shouldn't be a test # Sims with problems, no purpose, or maybe shouldn't be a test
# SIM_leaks ( should be deleted ) # SIM_leaks ( should be deleted )

View File

@ -1,5 +1,4 @@
#include <igl/opengl/glfw/Viewer.h> #include <igl/opengl/glfw/Viewer.h>
// #include <igl/readOFF.h>
#include <igl/opengl/glfw/imgui/ImGuiPlugin.h> #include <igl/opengl/glfw/imgui/ImGuiPlugin.h>
#include <igl/opengl/glfw/imgui/ImGuiMenu.h> #include <igl/opengl/glfw/imgui/ImGuiMenu.h>
#include <igl/opengl/glfw/imgui/ImGuiHelpers.h> #include <igl/opengl/glfw/imgui/ImGuiHelpers.h>
@ -13,6 +12,7 @@
#include "Socket.hh" #include "Socket.hh"
#define STR_MAX 1024
std::vector<Eigen::Vector3d> ball_colors = {Eigen::Vector3d(0.0,0.4,0.0), //green std::vector<Eigen::Vector3d> ball_colors = {Eigen::Vector3d(0.0,0.4,0.0), //green
Eigen::Vector3d(1.0,1.0,0.0), //yellow Eigen::Vector3d(1.0,1.0,0.0), //yellow
@ -153,7 +153,7 @@ class Circle : public Polygon {
} }
bool isValid() const { bool isValid() const {
return points.size() == vertexMax; return points.size() == 1 && radius >= 0;
} }
RenderedShape *render() const { RenderedShape *render() const {
@ -298,14 +298,11 @@ class Table {
// Need to have an agreed upon way to send over variables // Need to have an agreed upon way to send over variables
int addShape(std::vector<double> shapeData, Eigen::Vector3d color, bool isStatic, PolygonType type, double layer) { int addShape(std::vector<double> shapeData, Eigen::Vector3d color, bool isStatic, PolygonType type, double layer) {
// std::cout << "In AddShape" << std::endl;
Polygon *shape; Polygon *shape;
switch (type) { switch (type) {
case GENERIC: { case GENERIC: {
// Number of points is just data / 2 i guess // Number of points is just data / 2 i guess
// std::cout << "Creating generic polygon with " << shapeData.size()/2 << " points" << std::endl;
Polygon *newPolygon = new Polygon(shapeData.size()/2, layer); Polygon *newPolygon = new Polygon(shapeData.size()/2, layer);
for (int i = 0; i < shapeData.size(); i+=2) { for (int i = 0; i < shapeData.size(); i+=2) {
double x = shapeData[i]; double x = shapeData[i];
@ -316,7 +313,6 @@ class Table {
break; break;
} }
case CIRCLE: { case CIRCLE: {
// std::cout << "Adding circle" << std::endl;
if (shapeData.size() != 3) { if (shapeData.size() != 3) {
std::cout << "Bad shapedata size for circle" << std::endl; std::cout << "Bad shapedata size for circle" << std::endl;
return -1; return -1;
@ -345,7 +341,6 @@ class Table {
break; break;
} }
case RECTANGLE: { case RECTANGLE: {
// std::cout << "In rectangle" << std::endl;
Rectangle *newRectangle = new Rectangle(layer); Rectangle *newRectangle = new Rectangle(layer);
if (shapeData.size() != 4) { if (shapeData.size() != 4) {
std::cout << "Bad shapedata size for rectangle" << std::endl; std::cout << "Bad shapedata size for rectangle" << std::endl;
@ -481,14 +476,12 @@ class Table {
private: private:
std::vector<Polygon *> staticShapes; std::vector<Polygon *> staticShapes;
std::vector<Polygon *> movingShapes;
std::vector<RenderedShape *> staticRenderedShapes; std::vector<RenderedShape *> staticRenderedShapes;
std::vector<RenderedShape *> movingRenderedShapes; std::vector<RenderedShape *> movingRenderedShapes;
int numStaticVertices; int numStaticVertices;
int numStaticFaces; int numStaticFaces;
bool staticRendered = false; bool staticRendered = false;
std::vector<Polygon *> movingShapes;
}; };
void printUsage() { void printUsage() {
@ -505,20 +498,76 @@ std::vector<std::string> split (std::string& str, const char delim) {
return ret; return ret;
} }
std::vector<double> parseTrickResponse(std::vector<std::string> list) { double totalTime = 0;
std::vector<double> ret; std::vector<double> requestTime;
for (int i = 1; i < list.size(); i++) {
ret.push_back(stod(list[i])); template <typename T>
} T stringConvert (const std::string& str)
return ret; {
std::istringstream ss(str);
T num;
ss >> num;
return num;
} }
std::vector<int> parseTrickResponseInt(std::vector<std::string> list) { template <typename T>
std::vector<int> ret; std::vector<T> trickResponseConvert (std::string& response)
for (int i = 1; i < list.size(); i++) { {
ret.push_back(stoi(list[i])); auto responseSplit = split(response, '\t');
std::vector<T> result;
// ignore index 0
for (int i = 1; i < responseSplit.size(); i++) {
result.push_back(stringConvert<T>(responseSplit[i]));
} }
return ret; return result;
}
template <typename T>
T getVar(Socket& socket, std::string varName) {
std::string requestString = "trick.var_send_once(\"" + varName + "\")\n";
std::string reply;
socket << requestString;
socket >> reply;
return stringConvert<T>(split(reply, '\t')[1]);
}
// Wrapper for sprintf use case bc im tired of dealing with std::string vs char* stuff
std::string format(const std::string& formatString, int num) {
char *buf = (char *)malloc(formatString.size() + 10);
sprintf(buf, formatString.c_str(), num);
return std::string(buf);
}
// Assumes the varName string has a %d in it
template <typename T>
std::vector<T> getVarList(Socket& socket, std::string varName, int num) {
std::string totalRequest = "";
std::vector<T> result;
totalRequest += format(varName, 0);
for (int i = 1; i < num; i++) {
totalRequest += ", ";
totalRequest += format(varName, i);
}
std::string requestString = "trick.var_send_once(\"" + totalRequest + "\", " + std::to_string(num) + ")\n";
std::string reply;
socket << requestString;
socket >> reply;
return trickResponseConvert<T>(reply);
}
template <typename T>
std::vector<T> fold (const std::vector<T> a, const std::vector<T> b) {
std::vector<T> result;
for (int i = 0; i < a.size() && i < b.size(); i++) {
result.emplace_back (a[i]);
result.emplace_back (b[i]);
}
return result;
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@ -543,52 +592,19 @@ int main(int argc, char *argv[])
std::string reply; std::string reply;
socket << "trick.var_set_client_tag(\"PoolTableDisplay\") \n"; socket << "trick.var_set_client_tag(\"PoolTableDisplay\") \n";
socket << "trick.var_add(\"dyn.table.numBalls\")\ntrick.var_send() \ntrick.var_clear() \n";
socket >> reply;
auto parsed = split(reply, '\t'); int numBalls = getVar<int>(socket, "dyn.table.numBalls");
int numBalls = stoi(parsed[1]); // int numBalls = 16;
std::cout << "Number of balls received: " << numBalls << std::endl; std::vector<double> radii = getVarList<double>(socket, "dyn.table.balls[%d][0].radius", numBalls);
int numTablePoints = getVar<int>(socket, "dyn.table.numTablePoints");
std::string radiusRequest = ""; enum PolygonType tableShape = PolygonType(getVar<int>(socket, "dyn.table.tableShapeType"));
char* templateString = "trick.var_add(\"dyn.table.balls[%d][0].radius\")\n"; std::vector<double> table_x = getVarList<double>(socket, "dyn.table.tableShape[%d][0]._x", numTablePoints);
for (int i = 0; i < numBalls; i++) { std::vector<double> table_y = getVarList<double>(socket, "dyn.table.tableShape[%d][0]._y", numTablePoints);
char buf[64]; std::vector<double> tablePoints = fold(table_x, table_y);
sprintf(buf, templateString, i);
radiusRequest += std::string(buf);
}
socket << radiusRequest;
socket << "trick.var_send() \ntrick.var_clear() \n";
socket >> reply;
auto radii = parseTrickResponse(split(reply, '\t'));
Table table; Table table;
socket << "trick.var_add(\"dyn.table.numTablePoints\") \ntrick.var_add(\"dyn.table.tableShapeType\")\n \ntrick.var_send() \ntrick.var_clear() \n";
socket >> reply;
std::vector<double> tableData = parseTrickResponse(split(reply, '\t'));
int numTablePoints = tableData[0];
enum PolygonType tableShape = PolygonType((int)tableData[1]);
std::string pointRequest = "";
for (int i = 0; i < numTablePoints; i++) {
templateString = "trick.var_add(\"dyn.table.tableShape[%d][0]._x\")\ntrick.var_add(\"dyn.table.tableShape[%d][0]._y\")\n";
char buf[256];
sprintf(buf, templateString, i, i);
pointRequest += std::string(buf);
}
socket << pointRequest;
socket << "trick.var_send() \ntrick.var_clear() \n";
socket >> reply;
std::vector<double> tablePoints = parseTrickResponse(split(reply, '\t'));
table.addShape(tablePoints, Eigen::Vector3d(0.2, 0.6, 0.2), true, tableShape, layer_TABLE); table.addShape(tablePoints, Eigen::Vector3d(0.2, 0.6, 0.2), true, tableShape, layer_TABLE);
// Make the rail - translate each point on the table out from center by railWidth // Make the rail - translate each point on the table out from center by railWidth
std::vector<double> railData; std::vector<double> railData;
@ -601,12 +617,13 @@ int main(int argc, char *argv[])
railData.push_back(tablePoints[3] + railWidth); railData.push_back(tablePoints[3] + railWidth);
} else { } else {
// If it's just a shape then rail is bigger shape // If it's just a shape then rail is bigger shape
// Works with simple convex polygons // Works with simple convex polygons centered at 0,0
// Could probably calculate center and make it more general but i dont want to
double railWidth = 0.15; double railWidth = 0.15;
for (int i = 0; i < tablePoints.size(); i+=2) { for (int i = 0; i < tablePoints.size(); i+=2) {
Eigen::Vector2d point(tablePoints[i], tablePoints[i+1]); Eigen::Vector2d point(tablePoints[i], tablePoints[i+1]);
Eigen::Vector2d railPoint(tablePoints[i], tablePoints[i+1]); Eigen::Vector2d railPoint(tablePoints[i], tablePoints[i+1]);
point *= railWidth; point = point.normalized() * railWidth;
railPoint = railPoint + point; railPoint = railPoint + point;
railData.push_back(railPoint(0)); railData.push_back(railPoint(0));
railData.push_back(railPoint(1)); railData.push_back(railPoint(1));
@ -615,82 +632,41 @@ int main(int argc, char *argv[])
table.addShape(railData, Eigen::Vector3d(.3, .2, .15), true, tableShape, layer_RAIL); table.addShape(railData, Eigen::Vector3d(.3, .2, .15), true, tableShape, layer_RAIL);
socket << "trick.var_add(\"dyn.table.numPockets\")\n \ntrick.var_send() \ntrick.var_clear() \n"; // pockets
socket >> reply; int numPockets = getVar<int>(socket, "dyn.table.numPockets");
int numPockets = stoi(split(reply, '\t')[1]); std::vector<double> pocket_x = getVarList<double>(socket, "dyn.table.pockets[%d][0].pos._x", numPockets);
std::vector<double> pocket_y = getVarList<double>(socket, "dyn.table.pockets[%d][0].pos._y", numPockets);
std::vector<double> pocket_r = getVarList<double>(socket, "dyn.table.pockets[%d][0].radius", numPockets);
for (int i = 0; i < numPockets; i++) { for (int i = 0; i < numPockets; i++) {
templateString = "trick.var_add(\"dyn.table.pockets[%d][0].pos._x\")\ntrick.var_add(\"dyn.table.pockets[%d][0].pos._y\")\n\ntrick.var_add(\"dyn.table.pockets[%d][0].radius\")\n"; table.addShape(std::vector<double>({pocket_x[i],pocket_y[i],pocket_r[i]}), Eigen::Vector3d(0.0, 0.0, 0.0), true, CIRCLE, layer_POCKET);
char buf[256];
sprintf(buf, templateString, i, i, i);
std::string pocketRequest = std::string(buf);
socket << pocketRequest;
socket << "trick.var_send() \ntrick.var_clear() \n";
socket >> reply;
std::vector<double> pocketData = parseTrickResponse(split(reply, '\t'));
table.addShape(pocketData, Eigen::Vector3d(0.0, 0.0, 0.0), true, CIRCLE, layer_POCKET);
} }
// Bumpers // bumpers
socket << "trick.var_add(\"dyn.table.numBumpers\")\n \ntrick.var_send() \ntrick.var_clear() \n"; int numBumpers = getVar<int>(socket, "dyn.table.numBumpers");
socket >> reply;
int numBumpers = stoi(split(reply, '\t')[1]);
std::cout << "Num bumpers: " << numBumpers << std::endl;
for (int i = 0; i < numBumpers; i++) { for (int i = 0; i < numBumpers; i++) {
std::string bumperRequests = ""; int numPoints = getVar<int>(socket,format("dyn.table.bumpers[%d][0].numPoints", i));
PolygonType bumperShapeType = PolygonType(getVar<int>(socket,format("dyn.table.bumpers[%d][0].shapeType", i)));
templateString = "trick.var_add(\"dyn.table.bumpers[%d][0].numPoints\")\ntrick.var_add(\"dyn.table.bumpers[%d][0].shapeType\") \n"; std::string request_bumper = format("dyn.table.bumpers[%d][0]", i);
char buf[256];
sprintf(buf, templateString, i, i);
bumperRequests += std::string(buf);
socket << bumperRequests;
socket << "trick.var_send() \ntrick.var_clear() \n";
socket >> reply;
std::vector<int> bumperData = parseTrickResponseInt(split(reply, '\t'));
int bumperPoints = bumperData[0];
enum PolygonType bumperShape = PolygonType((int)bumperData[1]);
templateString = "trick.var_add(\"dyn.table.bumpers[%d][0].renderedShape[%d][0]._x\")\ntrick.var_add(\"dyn.table.bumpers[%d][0].renderedShape[%d][0]._y\")\n"; std::string request_x = request_bumper + ".renderedShape[%d][0]._x";
bumperRequests = ""; std::string request_y = request_bumper + ".renderedShape[%d][0]._y";
for (int j = 0; j < bumperPoints; j++) {
char buf[256]; std::vector<double> list_x = getVarList<double>(socket, request_x, numPoints);
sprintf(buf, templateString, i, j, i, j); std::vector<double> list_y = getVarList<double>(socket, request_y, numPoints);
bumperRequests += std::string(buf); std::vector<double> bumperBorder = fold(list_x, list_y);
} table.addShape(bumperBorder, Eigen::Vector3d(0.2,0.4,0.2), true, bumperShapeType, layer_BUMPER);
socket << bumperRequests;
socket << "trick.var_send() \ntrick.var_clear() \n";
socket >> reply;
std::vector<double> bumperBorder = parseTrickResponse(split(reply, '\t'));
table.addShape(bumperBorder, Eigen::Vector3d(0.2,0.4,0.2), true, bumperShape, layer_BUMPER);
} }
// socket << "trick.var_pause()\n";
// Request all of the ball positions // Request all of the ball positions
std::string positionRequest = "";
templateString = "trick.var_add(\"dyn.table.balls[%d][0].pos._x\")\ntrick.var_add(\"dyn.table.balls[%d][0].pos._y\")\n"; std::vector<double> ball_x = getVarList<double>(socket, "dyn.table.balls[%d][0].pos._x", numBalls);
std::vector<double> ball_y = getVarList<double>(socket, "dyn.table.balls[%d][0].pos._y", numBalls);
for (int i = 0; i < numBalls; i++) { for (int i = 0; i < numBalls; i++) {
char buf[128];
sprintf(buf, templateString, i, i);
positionRequest += std::string(buf);
}
socket << positionRequest;
socket << "trick.var_send() \ntrick.var_clear() \n";
socket >> reply;
auto positions = parseTrickResponse(split(reply, '\t'));
for (int i = 0; i < numBalls; i++) {
std::vector<double> circleData = {positions[(i*2)], positions[(i*2 + 1)], radii[i]};
Eigen::Vector3d circleColor = ball_colors[i % ball_colors.size()]; Eigen::Vector3d circleColor = ball_colors[i % ball_colors.size()];
table.addShape(circleData, circleColor, false, CIRCLE, layer_BALL); table.addShape(std::vector<double>({ball_x[i], ball_y[i], radii[i]}), circleColor, false, CIRCLE, layer_BALL);
} }
bool mousePressed = false; bool mousePressed = false;
@ -740,7 +716,7 @@ int main(int argc, char *argv[])
view->callback_pre_draw = [&](igl::opengl::glfw::Viewer& viewer) -> bool { view->callback_pre_draw = [&](igl::opengl::glfw::Viewer& viewer) -> bool {
// Look for new data and redraw // Look for new data and redraw
socket >> reply; socket >> reply;
std::vector<double> replyData = parseTrickResponse(split(reply, '\t')); std::vector<double> replyData = trickResponseConvert<double>(reply);
if (replyData.size() <= 1) { if (replyData.size() <= 1) {
return false; return false;
@ -824,8 +800,8 @@ int main(int argc, char *argv[])
// Need to get nBalls and positions every time // Need to get nBalls and positions every time
socket << "trick.var_pause() \n"; socket << "trick.var_pause() \n";
socket << "trick.var_add(\"dyn.table.numBalls\")\n"; socket << "trick.var_add(\"dyn.table.numBalls\")\n";
positionRequest = ""; std::string positionRequest = "";
templateString = "trick.var_add(\"dyn.table.balls[%d][0].pos._x\")\ntrick.var_add(\"dyn.table.balls[%d][0].pos._y\")\ntrick.var_add(\"dyn.table.balls[%d][0].inPlay\")\n"; char * templateString = "trick.var_add(\"dyn.table.balls[%d][0].pos._x\")\ntrick.var_add(\"dyn.table.balls[%d][0].pos._y\")\ntrick.var_add(\"dyn.table.balls[%d][0].inPlay\")\n";
for (int i = 0; i < numBalls; i++) { for (int i = 0; i < numBalls; i++) {
char buf[128]; char buf[128];
sprintf(buf, templateString, i, i, i); sprintf(buf, templateString, i, i, i);

View File

@ -579,7 +579,6 @@ int Trick::DataRecordGroup::data_record(double in_time) {
bool change_detected = false ; bool change_detected = false ;
//TODO: does not handle bitfields correctly! //TODO: does not handle bitfields correctly!
if ( record == true ) { if ( record == true ) {
if ( freq != DR_Always ) { if ( freq != DR_Always ) {
for (jj = 0; jj < change_buffer.size() ; jj++) { for (jj = 0; jj < change_buffer.size() ; jj++) {
@ -691,6 +690,7 @@ int Trick::DataRecordGroup::write_data(bool must_write) {
// buffer_mutex is used in this one place to prevent forced calls of write_data // buffer_mutex is used in this one place to prevent forced calls of write_data
// to not overwrite data being written by the asynchronous thread. // to not overwrite data being written by the asynchronous thread.
pthread_mutex_lock(&buffer_mutex) ; pthread_mutex_lock(&buffer_mutex) ;
local_buffer_num = buffer_num ; local_buffer_num = buffer_num ;
if ( (local_buffer_num - writer_num) > max_num ) { if ( (local_buffer_num - writer_num) > max_num ) {
num_to_write = max_num ; num_to_write = max_num ;

View File

@ -56,7 +56,7 @@ void Trick::TestSuite::delete_test_results() {
Trick::UnitTest::UnitTest() { Trick::UnitTest::UnitTest() {
the_unit_test_output = this ; the_unit_test_output = this ;
enabled = false ; enabled = false ;
exit_code_enabled = false ; exit_code_enabled = true ;
file_name = std::string("test_details.xml") ; file_name = std::string("test_details.xml") ;
name = std::string("AllTests") ; name = std::string("AllTests") ;
} }

View File

@ -46,17 +46,18 @@ REF2* Trick::VariableServerThread::make_error_ref(std::string in_name) {
return new_ref; return new_ref;
} }
int Trick::VariableServerThread::var_add(std::string in_name) { Trick::VariableReference* Trick::VariableServerThread::create_var_reference(std::string in_name) {
VariableReference * new_var ;
REF2 * new_ref ; REF2 * new_ref ;
// Time var is treated specially
if ( in_name.compare("time") == 0 ) { if ( in_name.compare("time") == 0 ) {
new_ref = make_time_ref() ; new_ref = make_time_ref() ;
} else { } else {
// otherwise ref_attributes takes care of the hard part
new_ref = ref_attributes(in_name.c_str()) ; new_ref = ref_attributes(in_name.c_str()) ;
} }
// Check error cases
if ( new_ref == NULL ) { if ( new_ref == NULL ) {
message_publish(MSG_ERROR, "Variable Server could not find variable %s.\n", in_name.c_str()); message_publish(MSG_ERROR, "Variable Server could not find variable %s.\n", in_name.c_str());
new_ref = make_error_ref(in_name); new_ref = make_error_ref(in_name);
@ -81,7 +82,12 @@ int Trick::VariableServerThread::var_add(std::string in_name) {
new_ref = make_error_ref(in_name); new_ref = make_error_ref(in_name);
} }
new_var = new VariableReference(new_ref) ; // Actually constructs the variable reference in the success case
return new VariableReference(new_ref) ;
}
int Trick::VariableServerThread::var_add(std::string in_name) {
VariableReference * new_var = create_var_reference(in_name);
vars.push_back(new_var) ; vars.push_back(new_var) ;
return(0) ; return(0) ;
@ -93,6 +99,36 @@ int Trick::VariableServerThread::var_add(std::string var_name, std::string units
return(0) ; return(0) ;
} }
// Helper function for var_send_once
std::vector<std::string> split (const std::string& str, const char delim) {
std::stringstream ss(str);
std::string s;
std::vector<std::string> ret;
while (std::getline(ss, s, delim)) {
ret.push_back(s);
}
return ret;
}
int Trick::VariableServerThread::var_send_once(std::string in_name, int num_vars) {
std::vector<std::string> var_names = split(in_name, ',');
if (var_names.size() != num_vars) {
message_publish(MSG_ERROR, "Number of variables sent to var_send_once (%d) does not match num_vars (%d).\n", var_names.size(), num_vars);
return -1;
}
std::vector<VariableReference *> given_vars;
for (auto& varName : var_names) {
given_vars.push_back(create_var_reference(varName));
}
copy_sim_data(given_vars, false);
write_data(given_vars);
return(0) ;
}
int Trick::VariableServerThread::var_remove(std::string in_name) { int Trick::VariableServerThread::var_remove(std::string in_name) {
unsigned int ii ; unsigned int ii ;
@ -218,6 +254,7 @@ int Trick::VariableServerThread::var_clear() {
return(0) ; return(0) ;
} }
int Trick::VariableServerThread::var_send() { int Trick::VariableServerThread::var_send() {
copy_sim_data(); copy_sim_data();
write_data(); write_data();

View File

@ -7,23 +7,24 @@
#include "trick/exec_proto.h" #include "trick/exec_proto.h"
int Trick::VariableServerThread::copy_sim_data() { int Trick::VariableServerThread::copy_sim_data() {
return copy_sim_data(vars, true);
}
unsigned int ii ; int Trick::VariableServerThread::copy_sim_data(std::vector<VariableReference *> given_vars, bool cyclical) {
VariableReference * curr_var ;
if ( vars.size() == 0 ) { if (given_vars.size() == 0) {
return 0 ; return 0;
} }
if ( pthread_mutex_trylock(&copy_mutex) == 0 ) { if ( pthread_mutex_trylock(&copy_mutex) == 0 ) {
// Get the simulation time we start this copy // Get the simulation time we start this copy
if (cyclical) {
time = (double)exec_get_time_tics() / exec_get_time_tic_value() ; time = (double)exec_get_time_tics() / exec_get_time_tic_value() ;
}
for ( ii = 0 ; ii < vars.size() ; ii++ ) { for (auto curr_var : given_vars ) {
curr_var = vars[ii] ;
// if this variable is unresolved, try to resolve it
if (curr_var->ref->address == &bad_ref_int) { if (curr_var->ref->address == &bad_ref_int) {
REF2 *new_ref = ref_attributes(curr_var->ref->reference); REF2 *new_ref = ref_attributes(curr_var->ref->reference);
if (new_ref != NULL) { if (new_ref != NULL) {
@ -92,14 +93,13 @@ int Trick::VariableServerThread::copy_sim_data() {
} }
// Indicate that sim data has been written and is now ready in the buffer_in's of the vars variable list. // Indicate that sim data has been written and is now ready in the buffer_in's of the vars variable list.
if (cyclical) {
var_data_staged = true; var_data_staged = true;
packets_copied++ ; packets_copied++ ;
}
pthread_mutex_unlock(&copy_mutex) ; pthread_mutex_unlock(&copy_mutex) ;
} }
return 0;
return (0) ;
} }

View File

@ -179,8 +179,9 @@ void * Trick::VariableServerThread::thread_body() {
} catch (...) { } catch (...) {
#ifdef __linux #ifdef __linux
#ifdef __GNUC__ #ifdef __GNUC__
#if __GNUC__ == 4 && __GNUC_MINOR__ == 1 #if (__GNUC__ == 4 && __GNUC_MINOR__ == 1) || __GNUC__ == 12
//for gcc 4.1.2 or whatever glib version in RHEL 5 that does not work with the abi::__forced_unwind // for gcc 4.1.2 or whatever glib version in RHEL 5 that does not work with the abi::__forced_unwind
// Also seems to have a problem with gcc 12
throw; throw;
#else #else
message_publish(MSG_ERROR, "\nVARIABLE SERVER caught unknown exception\n" ) ; message_publish(MSG_ERROR, "\nVARIABLE SERVER caught unknown exception\n" ) ;

View File

@ -6,7 +6,6 @@ PROGRAMMERS: (((Alex Lin) (NASA) (8/06) (--)))
#include <iostream> #include <iostream>
#include <pthread.h> #include <pthread.h>
#include "trick/VariableServer.hh" #include "trick/VariableServer.hh"
#include "trick/variable_server_message_types.h"
#include "trick/parameter_types.h" #include "trick/parameter_types.h"
#include "trick/bitfield_proto.h" #include "trick/bitfield_proto.h"
#include "trick/trick_byteswap.h" #include "trick/trick_byteswap.h"
@ -21,7 +20,9 @@ extern "C" {
#define MAX_MSG_LEN 8192 #define MAX_MSG_LEN 8192
int Trick::VariableServerThread::write_binary_data( int Start, char *buf1, int PacketNum ) {
int Trick::VariableServerThread::write_binary_data( int Start, char *buf1, const std::vector<VariableReference *>& given_vars, VS_MESSAGE_TYPE message_type) {
int vars = 0;
int i; int i;
int ret ; int ret ;
int HeaderSize, MessageSize; int HeaderSize, MessageSize;
@ -32,27 +33,28 @@ int Trick::VariableServerThread::write_binary_data( int Start, char *buf1, int P
char * address = 0 ; char * address = 0 ;
char* param_name; char* param_name;
//remove warning for unused PacketNum... to be deleted.
(void)PacketNum ;
/* start the offset 4 bytes into the message, we'll subtract the sizeof offset at the end */ /* start the offset 4 bytes into the message, we'll subtract the sizeof offset at the end */
offset = sizeof(msg_type) + sizeof(offset) ; offset = sizeof(msg_type) + sizeof(offset) ;
/* if we are here the msg_type is good, so send a 0, swapped or not 0 is still 0 */ if (byteswap) {
msg_type = VS_VAR_LIST ; /* Swap message type bytes */
msg_type = trick_byteswap_int((int)message_type) ;
} else {
msg_type = message_type;
}
memcpy(buf1, &msg_type , sizeof(msg_type)) ; memcpy(buf1, &msg_type , sizeof(msg_type)) ;
HeaderSize = sizeof(msg_type); HeaderSize = sizeof(msg_type);
offset += sizeof(unsigned int) ; offset += sizeof(unsigned int) ;
HeaderSize += sizeof(unsigned int); HeaderSize += sizeof(unsigned int);
for (i = Start; i < (int)vars.size() ; i++) { for (i = Start; i < (int)given_vars.size() ; i++) {
// data to send was copied to buffer in copy_sim_data // data to send was copied to buffer in copy_sim_data
address = (char *)vars[i]->buffer_out; address = (char *)given_vars[i]->buffer_out;
size = vars[i]->size ; size = given_vars[i]->size ;
param_name = vars[i]->ref->reference; param_name = given_vars[i]->ref->reference;
len = strlen(param_name) ; len = strlen(param_name) ;
// when var_binary_nonames, do not put the variable names into the message to be sent // when var_binary_nonames, do not put the variable names into the message to be sent
if (binary_data_nonames) { if (binary_data_nonames) {
@ -66,7 +68,7 @@ int Trick::VariableServerThread::write_binary_data( int Start, char *buf1, int P
message_publish(MSG_WARNING, "%p Variable Server buffer[%d] too small (need %d) for symbol %s, SKIPPING IT.\n", message_publish(MSG_WARNING, "%p Variable Server buffer[%d] too small (need %d) for symbol %s, SKIPPING IT.\n",
&connection, MAX_MSG_LEN, &connection, MAX_MSG_LEN,
(int)(HeaderSize + MessageSize), (int)(HeaderSize + MessageSize),
vars[i]->ref->reference ); given_vars[i]->ref->reference );
continue; continue;
} }
@ -81,7 +83,7 @@ int Trick::VariableServerThread::write_binary_data( int Start, char *buf1, int P
offset += len ; offset += len ;
} }
swap_int = trick_byteswap_int(vars[i]->ref->attr->type) ; swap_int = trick_byteswap_int(given_vars[i]->ref->attr->type) ;
memcpy(&buf1[offset] , &swap_int , sizeof(int)) ; memcpy(&buf1[offset] , &swap_int , sizeof(int)) ;
offset += sizeof(int) ; offset += sizeof(int) ;
@ -90,7 +92,7 @@ int Trick::VariableServerThread::write_binary_data( int Start, char *buf1, int P
offset += sizeof(size) ; offset += sizeof(size) ;
/* TODO: There is a bug here, this call will want to swap the entire buffer, we may not have the whole buffer */ /* TODO: There is a bug here, this call will want to swap the entire buffer, we may not have the whole buffer */
trick_bswap_buffer(&buf1[offset], address, vars[i]->ref->attr, 1); trick_bswap_buffer(&buf1[offset], address, given_vars[i]->ref->attr, 1);
offset += size ; offset += size ;
} }
else { else {
@ -105,21 +107,21 @@ int Trick::VariableServerThread::write_binary_data( int Start, char *buf1, int P
offset += len ; offset += len ;
} }
memcpy(&buf1[offset] , &vars[i]->ref->attr->type , sizeof(int)) ; memcpy(&buf1[offset] , &given_vars[i]->ref->attr->type , sizeof(int)) ;
offset += sizeof(int) ; offset += sizeof(int) ;
memcpy(&buf1[offset] , &size , sizeof(size)) ; memcpy(&buf1[offset] , &size , sizeof(size)) ;
offset += sizeof(size) ; offset += sizeof(size) ;
switch ( vars[i]->ref->attr->type ) { switch ( given_vars[i]->ref->attr->type ) {
case TRICK_BITFIELD: case TRICK_BITFIELD:
temp_i = GET_BITFIELD(address , vars[i]->ref->attr->size , temp_i = GET_BITFIELD(address , given_vars[i]->ref->attr->size ,
vars[i]->ref->attr->index[0].start, vars[i]->ref->attr->index[0].size) ; given_vars[i]->ref->attr->index[0].start, given_vars[i]->ref->attr->index[0].size) ;
memcpy(&buf1[offset] , &temp_i , (size_t)size) ; memcpy(&buf1[offset] , &temp_i , (size_t)size) ;
break ; break ;
case TRICK_UNSIGNED_BITFIELD: case TRICK_UNSIGNED_BITFIELD:
temp_ui = GET_UNSIGNED_BITFIELD(address , vars[i]->ref->attr->size , temp_ui = GET_UNSIGNED_BITFIELD(address , given_vars[i]->ref->attr->size ,
vars[i]->ref->attr->index[0].start, vars[i]->ref->attr->index[0].size) ; given_vars[i]->ref->attr->index[0].start, given_vars[i]->ref->attr->index[0].size) ;
memcpy(&buf1[offset] , &temp_ui , (size_t)size) ; memcpy(&buf1[offset] , &temp_ui , (size_t)size) ;
break ; break ;
case TRICK_NUMBER_OF_TYPES: case TRICK_NUMBER_OF_TYPES:
@ -176,6 +178,65 @@ int Trick::VariableServerThread::write_binary_data( int Start, char *buf1, int P
return i; return i;
} }
int Trick::VariableServerThread::write_ascii_data(char * dest_buf, const std::vector<VariableReference *>& given_vars, VS_MESSAGE_TYPE message_type ) {
sprintf(dest_buf, "%d\t", message_type) ;
for (int i = 0; i < given_vars.size(); i++) {
char curr_buf[MAX_MSG_LEN];
int ret = vs_format_ascii( given_vars[i] , curr_buf);
if (ret < 0) {
message_publish(MSG_WARNING, "%p Variable Server string buffer[%d] too small for symbol %s, TRUNCATED IT.\n",
&connection, MAX_MSG_LEN, given_vars[i]->ref->reference );
}
/* make sure this message will fit in a packet by itself */
if( strlen( curr_buf ) + 2 > MAX_MSG_LEN ) {
message_publish(MSG_WARNING, "%p Variable Server buffer[%d] too small for symbol %s, TRUNCATED IT.\n",
&connection, MAX_MSG_LEN, given_vars[i]->ref->reference );
curr_buf[MAX_MSG_LEN - 1] = '\0';
}
int len = strlen(dest_buf) ;
/* make sure there is space for the next tab or next newline and null */
if( len + strlen( curr_buf ) + 2 > MAX_MSG_LEN ) {
// If there isn't, send incomplete message
if (debug >= 2) {
message_publish(MSG_DEBUG, "%p tag=<%s> var_server sending %d ascii bytes:\n%s\n",
&connection, connection.client_tag, (int)strlen(dest_buf), dest_buf) ;
}
ret = tc_write(&connection, (char *) dest_buf, len);
if ( ret != len ) {
return(-1) ;
}
dest_buf[0] = '\0';
}
strcat(dest_buf, curr_buf);
strcat(dest_buf, "\t");
}
int len = strlen(dest_buf) ;
if ( len > 0 ) {
dest_buf[ strlen(dest_buf) - 1 ] = '\n';
if (debug >= 2) {
message_publish(MSG_DEBUG, "%p tag=<%s> var_server sending %d ascii bytes:\n%s\n",
&connection, connection.client_tag, (int)strlen(dest_buf), dest_buf) ;
}
int ret = tc_write(&connection, (char *) dest_buf, (int)strlen(dest_buf));
if ( ret != (int)strlen(dest_buf) ) {
return(-1) ;
}
}
return 0;
}
int Trick::VariableServerThread::write_data() { int Trick::VariableServerThread::write_data() {
int ret; int ret;
@ -185,7 +246,7 @@ int Trick::VariableServerThread::write_data() {
// do not send anything when there are no variables! // do not send anything when there are no variables!
if ( vars.size() == 0 or packets_copied == 0 ) { if ( vars.size() == 0 or packets_copied == 0 ) {
return(0); return 0;
} }
/* Acquire sole access to vars[ii]->buffer_in. */ /* Acquire sole access to vars[ii]->buffer_in. */
@ -204,77 +265,60 @@ int Trick::VariableServerThread::write_data() {
pthread_mutex_unlock(&copy_mutex) ; pthread_mutex_unlock(&copy_mutex) ;
if (binary_data) { if (binary_data) {
int Index = 0; int index = 0;
int PacketNumber = 0;
do { do {
ret = write_binary_data( Index, buf1, PacketNumber ); ret = write_binary_data( index, buf1, vars, VS_VAR_LIST );
if ( ret >= 0 ) { if ( ret >= 0 ) {
Index = ret ; index = ret ;
} else { } else {
return(-1) ; return(-1) ;
} }
PacketNumber++; } while( index < (int)vars.size() );
} while( Index < (int)vars.size() );
return 0;
} else { /* ascii mode */ } else { /* ascii mode */
char val[MAX_MSG_LEN]; return write_ascii_data(buf1, vars, VS_VAR_LIST );
}
sprintf(buf1, "0\t") ; }
}
for (i = 0; i < vars.size(); i++) {
int Trick::VariableServerThread::write_data(std::vector<VariableReference *> given_vars) {
ret = vs_format_ascii( vars[i] , val); // do not send anything when there are no variables!
if ( given_vars.size() == 0) {
if (ret < 0) { return(0);
message_publish(MSG_WARNING, "%p Variable Server string buffer[%d] too small for symbol %s, TRUNCATED IT.\n", }
&connection, MAX_MSG_LEN, vars[i]->ref->reference );
} /* Acquire sole access to vars[ii]->buffer_in. */
if ( pthread_mutex_trylock(&copy_mutex) == 0 ) {
/* make sure this message will fit in a packet by itself */ // Swap buffer_in and buffer_out for each vars[ii].
if( strlen( val ) + 2 > MAX_MSG_LEN ) { for (int i = 0 ; i < given_vars.size() ; i++ ) {
message_publish(MSG_WARNING, "%p Variable Server buffer[%d] too small for symbol %s, TRUNCATED IT.\n", void *temp_p = given_vars[i]->buffer_in;
&connection, MAX_MSG_LEN, vars[i]->ref->reference ); given_vars[i]->buffer_in = given_vars[i]->buffer_out;
val[MAX_MSG_LEN - 1] = '\0'; given_vars[i]->buffer_out = temp_p;
} }
/* Relinquish sole access to vars[ii]->buffer_in. */
len = strlen(buf1) ; pthread_mutex_unlock(&copy_mutex) ;
/* make sure there is space for the next tab or next newline and null */ char buf1[ MAX_MSG_LEN ];
if( len + strlen( val ) + 2 > MAX_MSG_LEN ) {
if (binary_data) {
if (debug >= 2) { int index = 0;
message_publish(MSG_DEBUG, "%p tag=<%s> var_server sending %d ascii bytes:\n%s\n",
&connection, connection.client_tag, (int)strlen(buf1), buf1) ; do {
} int ret = write_binary_data( index, buf1, given_vars, VS_SEND_ONCE );
if ( ret >= 0 ) {
ret = tc_write(&connection, (char *) buf1, len); index = ret ;
if ( ret != len ) { } else {
return(-1) ; return(-1) ;
} }
buf1[0] = '\0'; } while( index < (int)given_vars.size() );
}
return 0;
strcat(buf1, val);
strcat(buf1, "\t"); } else { /* ascii mode */
} return write_ascii_data(buf1, given_vars, VS_SEND_ONCE);
len = strlen(buf1) ;
if ( len > 0 ) {
buf1[ strlen(buf1) - 1 ] = '\n';
if (debug >= 2) {
message_publish(MSG_DEBUG, "%p tag=<%s> var_server sending %d ascii bytes:\n%s\n",
&connection, connection.client_tag, (int)strlen(buf1), buf1) ;
}
ret = tc_write(&connection, (char *) buf1, (int)strlen(buf1));
if ( ret != (int)strlen(buf1) ) {
return(-1) ;
} }
} }
}
}
return (0);
} }

View File

@ -60,6 +60,24 @@ int var_exists(std::string in_name) {
return(0) ; return(0) ;
} }
int var_send_once(std::string in_name) {
Trick::VariableServerThread * vst ;
vst = get_vst() ;
if (vst != NULL ) {
vst->var_send_once(in_name, 1) ;
}
return(0) ;
}
int var_send_once(std::string in_name, int num) {
Trick::VariableServerThread * vst ;
vst = get_vst() ;
if (vst != NULL ) {
vst->var_send_once(in_name, num) ;
}
return(0) ;
}
int var_send() { int var_send() {
Trick::VariableServerThread * vst ; Trick::VariableServerThread * vst ;
vst = get_vst() ; vst = get_vst() ;