/* PURPOSE: (Allows clients to get and set Trick parameters) PROGRAMMERS: (((Alex Lin) (NASA) (8/06) (--))) */ #include #include #include #include // for setprecision #include "trick/VariableServerSession.hh" #include "trick/parameter_types.h" #include "trick/bitfield_proto.h" #include "trick/trick_byteswap.h" #include "trick/tc_proto.h" #include "trick/message_proto.h" #include "trick/message_type.h" #define MAX_MSG_LEN 8192 int Trick::VariableServerSession::write_binary_data(const std::vector& given_vars, VS_MESSAGE_TYPE message_type) { typedef std::vector VarList; typedef std::pair MessageData; // Some constants to make size calculations more readable static const int header_size = 12; static const int sizeof_size = 4; static const int type_size = 4; std::vector message_sizes_and_vars; // Calculate how many messages and what vars in each int total_size = header_size; VarList curr_message_vars; for (int i = 0; i < given_vars.size(); i++) { VariableReference * var = given_vars[i]; int total_var_size = 0; if (!_binary_data_nonames) { total_var_size += sizeof_size; total_var_size += var->getName().size(); } total_var_size += type_size; total_var_size += sizeof_size; total_var_size += var->getSizeBinary(); // Check if this variable will fit in a message at all if (header_size + total_var_size > MAX_MSG_LEN) { message_publish(MSG_WARNING, "tag=<%s> Variable Server buffer[%d] too small (need %d) for symbol %s, SKIPPING IT.\n", _connection->getClientTag().c_str(), MAX_MSG_LEN, header_size + total_var_size, var->getName().c_str()); continue; } // If this variable won't fit in the current message, truncate the message and plan to put this var in a new one if (total_size + total_var_size > MAX_MSG_LEN) { message_sizes_and_vars.emplace_back(MessageData(total_size, curr_message_vars)); if (_debug >= 2) { message_publish(MSG_DEBUG, "%p tag=<%s> var_server buffer[%d] too small (need %d), sending multiple binary packets.\n", _connection, _connection->getClientTag().c_str(), MAX_MSG_LEN, total_size + total_var_size); } total_size = header_size; curr_message_vars.clear(); } total_size += total_var_size; curr_message_vars.push_back(var); } message_sizes_and_vars.emplace_back(MessageData(total_size, curr_message_vars)); // Now write out all of these messages int var_index = 0; for (const auto& message_info : message_sizes_and_vars) { int curr_message_size = message_info.first; VarList curr_message_vars = message_info.second; std::stringstream stream; int written_message_type = message_type; int written_header_size = curr_message_size - 4; int written_num_vars = curr_message_vars.size(); if (_byteswap) { written_message_type = trick_byteswap_int(written_message_type); written_header_size = trick_byteswap_int(written_header_size); written_num_vars = trick_byteswap_int(written_num_vars); } // Header format: // // Write the header first stream.write((char *)(&written_message_type), sizeof(int)); stream.write((char *)(&written_header_size), sizeof(int)); stream.write((char *)(&written_num_vars), sizeof(int)); // Write variables next for (VariableReference * var : curr_message_vars) { // Each variable is formatted as: // writeNameLengthBinary(stream, _byteswap); var->writeNameBinary(stream, _byteswap); } var->writeTypeBinary(stream, _byteswap); var->writeSizeBinary(stream, _byteswap); var->writeValueBinary(stream, _byteswap); } if (_debug >= 2) { message_publish(MSG_DEBUG, "%p tag=<%s> var_server sending %u binary bytes containing %d variables.\n", _connection, _connection->getClientTag().c_str(), curr_message_size, curr_message_vars.size()); } // Send it out! char write_buf[MAX_MSG_LEN]; stream.read(write_buf, curr_message_size); _connection->write(write_buf, curr_message_size); } return 0; } int Trick::VariableServerSession::write_ascii_data(const std::vector& given_vars, VS_MESSAGE_TYPE message_type ) { // Load message type first std::stringstream message_stream; message_stream << (int)message_type; int message_size = message_stream.str().size(); // Load each variable for (int i = 0; i < given_vars.size(); i++) { message_stream << "\t"; std::stringstream var_stream; given_vars[i]->writeValueAscii(var_stream); // Unfortunately, there isn't a good way to get the size of the buffer without putting it into a string std::string var_string = var_stream.str(); int var_size = var_string.size(); // Check if this single variable is too big, truncate if so if (var_size + 2 > MAX_MSG_LEN) { message_publish(MSG_WARNING, "tag=<%s> Variable Server buffer[%d] too small for symbol %s, TRUNCATED IT.\n", _connection->getClientTag().c_str(), MAX_MSG_LEN, given_vars[i]->getName().c_str()); var_string = var_string.substr(0, MAX_MSG_LEN-2); var_size = var_string.size(); } // Check that there's enough room for the next variable, tab character, and possible newline if (message_size + var_size + 2 > MAX_MSG_LEN) { // Write out an incomplete message std::string message = message_stream.str(); if (_debug >= 2) { message_publish(MSG_DEBUG, "%p tag=<%s> var_server buffer[%d] too small (need %d), sending multiple ascii packets.\n", _connection, _connection->getClientTag().c_str(), MAX_MSG_LEN, message_size + var_size + 2); message_publish(MSG_DEBUG, "%p tag=<%s> var_server sedning %d ascii bytes:\n%s\n", _connection, _connection->getClientTag().c_str(), message_size, message.c_str()); } int result = _connection->write(message); if (result < 0) { return result; } // Clear out the message stream message_stream.str(""); message_size = 0; } // Concatenate message_stream << var_string; message_size += var_size + 1; } // End with newline message_stream << '\n'; std::string message = message_stream.str(); if (_debug >= 2) { message_publish(MSG_DEBUG, "%p tag=<%s> var_server sedning %d ascii bytes:\n%s\n", _connection, _connection->getClientTag().c_str(), message.size(), message.c_str()); } int result = _connection->write(message); return result; } int Trick::VariableServerSession::write_data() { return write_data(_session_variables, VS_VAR_LIST); } int Trick::VariableServerSession::write_data(std::vector& given_vars, VS_MESSAGE_TYPE message_type) { // do not send anything when there are no variables! if ( given_vars.size() == 0) { return(0); } int result = 0; if ( pthread_mutex_trylock(&_copy_mutex) == 0 ) { // Check that all of the variables are staged for (VariableReference * variable : given_vars ) { if (!variable->isStaged()) { pthread_mutex_unlock(&_copy_mutex) ; return 0; } } // Swap buffer_in and buffer_out for each vars[ii]. for (VariableReference * variable : given_vars ) { variable->prepareForWrite(); } pthread_mutex_unlock(&_copy_mutex) ; // Send out in correct format if (_binary_data) { result = write_binary_data(given_vars, message_type ); } else { // ascii mode result = write_ascii_data(given_vars, message_type ); } } return result; }