#include #include #include #include // for fpclassify #include // for setprecision #include #include #include "trick/VariableReference.hh" #include "trick/memorymanager_c_intf.h" #include "trick/wcs_ext.h" #include "trick/map_trick_units_to_udunits.hh" #include "trick/message_proto.h" #include "trick/message_type.h" #include "trick/UdUnits.hh" #include "trick/bitfield_proto.h" #include "trick/trick_byteswap.h" // Static variables to be addresses that are known to be the error ref address int Trick::VariableReference::_bad_ref_int = 0 ; int Trick::VariableReference::_do_not_resolve_bad_ref_int = 0 ; REF2* Trick::VariableReference::make_error_ref(std::string in_name) { REF2* new_ref; new_ref = (REF2*)calloc(1, sizeof(REF2)); new_ref->reference = strdup(in_name.c_str()) ; new_ref->units = NULL ; new_ref->address = (char *)&_bad_ref_int ; new_ref->attr = (ATTRIBUTES*)calloc(1, sizeof(ATTRIBUTES)) ; new_ref->attr->type = TRICK_NUMBER_OF_TYPES ; new_ref->attr->units = (char *)"--" ; new_ref->attr->size = sizeof(int) ; return new_ref; } REF2* Trick::VariableReference::make_do_not_resolve_ref(std::string in_name) { REF2* new_ref; new_ref = (REF2*)calloc(1, sizeof(REF2)); new_ref->reference = strdup(in_name.c_str()) ; new_ref->units = NULL ; new_ref->address = (char *)&_do_not_resolve_bad_ref_int ; new_ref->attr = (ATTRIBUTES*)calloc(1, sizeof(ATTRIBUTES)) ; new_ref->attr->type = TRICK_NUMBER_OF_TYPES ; new_ref->attr->units = (char *)"--" ; new_ref->attr->size = sizeof(int) ; return new_ref; } // Helper function to deal with time variable REF2* make_time_ref(double * time) { REF2* new_ref; new_ref = (REF2*)calloc(1, sizeof(REF2)); new_ref->reference = strdup("time") ; new_ref->units = strdup("s") ; new_ref->address = (char *)time ; new_ref->attr = (ATTRIBUTES*)calloc(1, sizeof(ATTRIBUTES)) ; new_ref->attr->type = TRICK_DOUBLE ; new_ref->attr->units = strdup("s") ; new_ref->attr->size = sizeof(double) ; return new_ref; } Trick::VariableReference::VariableReference(std::string var_name, double* time) : _staged(false), _write_ready(false) { if (var_name != "time") { ASSERT(0); } _var_info = make_time_ref(time); // Set up member variables _address = _var_info->address; _size = _var_info->attr->size ; _deref = false; // Deal with weirdness around string vs wstring _trick_type = _var_info->attr->type ; // Allocate stage and write buffers _stage_buffer = calloc(_size, 1) ; _write_buffer = calloc(_size, 1) ; _conversion_factor = cv_get_trivial(); _base_units = _var_info->attr->units; _requested_units = "s"; _name = _var_info->reference; } Trick::VariableReference::VariableReference(std::string var_name) : _staged(false), _write_ready(false) { if (var_name == "time") { ASSERT(0); } else { // get variable attributes from memory manager _var_info = ref_attributes(var_name.c_str()); } // Handle error cases if ( _var_info == NULL ) { // TODO: ERROR LOGGER sendErrorMessage("Variable Server could not find variable %s.\n", var_name); // PRINTF IS NOT AN ERROR LOGGER @me message_publish(MSG_ERROR, "Variable Server could not find variable %s.\n", var_name.c_str()); _var_info = make_error_ref(var_name); } else if ( _var_info->attr ) { if ( _var_info->attr->type == TRICK_STRUCTURED ) { // sendErrorMessage("Variable Server: var_add cant add \"%s\" because its a composite variable.\n", var_name); message_publish(MSG_ERROR, "Variable Server: var_add cant add \"%s\" because its a composite variable.\n", var_name.c_str()); free(_var_info); _var_info = make_do_not_resolve_ref(var_name); } else if ( _var_info->attr->type == TRICK_STL ) { // sendErrorMessage("Variable Server: var_add cant add \"%s\" because its an STL variable.\n", var_name); message_publish(MSG_ERROR,"Variable Server: var_add cant add \"%s\" because its an STL variable.\n", var_name.c_str()); free(_var_info); _var_info = make_do_not_resolve_ref(var_name); } } else { // sendErrorMessage("Variable Server: BAD MOJO - Missing ATTRIBUTES."); message_publish(MSG_ERROR, "Variable Server: BAD MOJO - Missing ATTRIBUTES."); free(_var_info); _var_info = make_error_ref(var_name); } // Set up member variables _var_info->units = NULL; _address = _var_info->address; _size = _var_info->attr->size ; _deref = false; // Deal with weirdness around string vs wstring _trick_type = _var_info->attr->type ; if ( _var_info->num_index == _var_info->attr->num_index ) { // single value - nothing else necessary } else if ( _var_info->attr->index[_var_info->attr->num_index - 1].size != 0 ) { // Constrained array for ( int i = _var_info->attr->num_index-1; i > _var_info->num_index-1 ; i-- ) { _size *= _var_info->attr->index[i].size ; } } else { // Unconstrained array if ((_var_info->attr->num_index - _var_info->num_index) > 1 ) { message_publish(MSG_ERROR, "Variable Server Error: var_add(%s) requests more than one dimension of dynamic array.\n", _var_info->reference); message_publish(MSG_ERROR, "Data is not contiguous so returned values are unpredictable.\n") ; } if ( _var_info->attr->type == TRICK_CHARACTER ) { _trick_type = TRICK_STRING ; _deref = true; } else if ( _var_info->attr->type == TRICK_WCHAR ) { _trick_type = TRICK_WSTRING ; _deref = true; } else { _deref = true ; _size *= get_size((char*)_address) ; } } // handle strings: set a max buffer size, the copy size may vary so will be set in copy_sim_data if (( _trick_type == TRICK_STRING ) || ( _trick_type == TRICK_WSTRING )) { _size = MAX_ARRAY_LENGTH ; } // Allocate stage and write buffers _stage_buffer = calloc(_size, 1) ; _write_buffer = calloc(_size, 1) ; _conversion_factor = cv_get_trivial(); _base_units = _var_info->attr->units; _requested_units = ""; _name = _var_info->reference; // Done! } Trick::VariableReference::~VariableReference() { if (_var_info != NULL) { free( _var_info ); _var_info = NULL; } if (_stage_buffer != NULL) { free (_stage_buffer); _stage_buffer = NULL; } if (_write_buffer != NULL) { free (_write_buffer); _write_buffer = NULL; } if (_conversion_factor != NULL) { cv_free(_conversion_factor); } } std::string Trick::VariableReference::getName() const { return _name; } int Trick::VariableReference::getSizeBinary() const { return _size; } TRICK_TYPE Trick::VariableReference::getType() const { return _trick_type; } std::string Trick::VariableReference::getBaseUnits() const { return _base_units; } int Trick::VariableReference::setRequestedUnits(std::string units_name) { // Some error logging lambdas - these should probably go somewhere else // But I do kinda like them auto publish = [](MESSAGE_TYPE type, const std::string& message) { std::ostringstream oss; oss << "Variable Server: " << message << std::endl; message_publish(type, oss.str().c_str()); }; auto publishError = [&](const std::string& units) { std::ostringstream oss; oss << "units error for [" << getName() << "] [" << units << "]"; publish(MSG_ERROR, oss.str()); }; // If the units_name parameter is "xx", set it to the current units. if (!units_name.compare("xx")) { units_name = getBaseUnits(); } // if unitless ('--') then do not convert to udunits if(units_name.compare("--")) { // Check to see if this is an old style Trick unit that needs to be converted to new udunits std::string new_units = map_trick_units_to_udunits(units_name) ; // Warn if a conversion has taken place if ( units_name.compare(new_units) ) { // TODO: MAKE BETTER SYSTEM FOR ERROR LOGGING std::ostringstream oss; oss << "[" << getName() << "] old-style units converted from [" << units_name << "] to [" << new_units << "]"; publish(MSG_WARNING, oss.str()); } // Interpret base unit ut_unit * from = ut_parse(Trick::UdUnits::get_u_system(), getBaseUnits().c_str(), UT_ASCII) ; if ( !from ) { std::cout << "Error in interpreting base units" << std::endl; publishError(getBaseUnits()); ut_free(from) ; return -1 ; } // Interpret requested unit ut_unit * to = ut_parse(Trick::UdUnits::get_u_system(), new_units.c_str(), UT_ASCII) ; if ( !to ) { std::cout << "Error in interpreting requested units" << std::endl; publishError(new_units); ut_free(from) ; ut_free(to) ; return -1 ; } // Create a converter from the base to the requested auto new_conversion_factor = ut_get_converter(from, to) ; ut_free(from) ; ut_free(to) ; if ( !new_conversion_factor ) { std::ostringstream oss; oss << "[" << getName() << "] cannot convert units from [" << getBaseUnits() << "] to [" << new_units << "]"; publish(MSG_ERROR, oss.str()); return -1 ; } else { _conversion_factor = new_conversion_factor; } // Set the requested units. This will cause the unit string to be printed in write_value_ascii _requested_units = new_units; } return 0; } int Trick::VariableReference::stageValue(bool validate_address) { _write_ready = false; // Copy bytes from
to staging_point. // Try to recreate connection if it has been broken if (_var_info->address == &_bad_ref_int) { REF2 *new_ref = ref_attributes(_var_info->reference); if (new_ref != NULL) { _var_info = new_ref; _address = _var_info->address; // _requested_units = ""; } } // if there's a pointer somewhere in the address path, follow it in case pointer changed if ( _var_info->pointer_present == 1 ) { _address = follow_address_path(_var_info) ; if (_address == NULL) { tagAsInvalid(); } else if ( validate_address ) { validate(); } else { _var_info->address = _address ; } } // if this variable is a string we need to get the raw character string out of it. if (( _trick_type == TRICK_STRING ) && !_deref) { std::string * str_ptr = (std::string *)_var_info->address ; // Get a pointer to the internal character array _address = (void *)(str_ptr->c_str()) ; } // if this variable itself is a pointer, dereference it if ( _deref ) { _address = *(void**)_var_info->address ; } // handle c++ string and char* if ( _trick_type == TRICK_STRING ) { if (_address == NULL) { _size = 0 ; } else { _size = strlen((char*)_address) + 1 ; } } // handle c++ wstring and wchar_t* if ( _trick_type == TRICK_WSTRING ) { if (_address == NULL) { _size = 0 ; } else { _size = wcslen((wchar_t *)_address) * sizeof(wchar_t); } } if(_address != NULL) { memcpy( _stage_buffer , _address , _size ) ; } _staged = true; return 0; } bool Trick::VariableReference::validate() { // The address is not NULL. // Should be called by VariableServer Session if validateAddress is on. // check the memory manager if the address falls into // any of the memory blocks it knows of. Don't do this if we have a std::string or // wstring type, or we already are pointing to a bad ref. if ( (_trick_type != TRICK_STRING) and (_trick_type != TRICK_WSTRING) and (_var_info->address != &_bad_ref_int) and (get_alloc_info_of(_address) == NULL) ) { // This variable is broken, make it into an error ref tagAsInvalid(); return false; } // Everything is fine return true; } static void write_escaped_string( std::ostream& os, const char* s) { for (int ii=0 ; iiattr->size ; switch (_trick_type) { case TRICK_CHARACTER: if (_var_info->attr->num_index == _var_info->num_index) { // Single char out << (int)cv_convert_double(_conversion_factor, *(char *)buf_ptr); } else { // All but last dim specified, leaves a char array write_escaped_string(out, (const char *) buf_ptr); bytes_written = _size ; } break; case TRICK_UNSIGNED_CHARACTER: if (_var_info->attr->num_index == _var_info->num_index) { // Single char out << (unsigned int)cv_convert_double(_conversion_factor,*(unsigned char *)buf_ptr); } else { // All but last dim specified, leaves a char array write_escaped_string(out, (const char *) buf_ptr); bytes_written = _size ; } break; case TRICK_WCHAR:{ if (_var_info->attr->num_index == _var_info->num_index) { out << *(wchar_t *) buf_ptr; } else { // convert wide char string char string size_t len = wcs_to_ncs_len((wchar_t *)buf_ptr) + 1 ; char temp_buf[len]; wcs_to_ncs((wchar_t *) buf_ptr, temp_buf, len); out << temp_buf; bytes_written = _size ; } } break; case TRICK_STRING: if ((char *) buf_ptr != NULL) { write_escaped_string(out, (const char *) buf_ptr); bytes_written = _size ; } else { out << '\0'; } break; case TRICK_WSTRING: if ((wchar_t *) buf_ptr != NULL) { // convert wide char string char string size_t len = wcs_to_ncs_len( (wchar_t *)buf_ptr) + 1 ; char temp_buf[len]; wcs_to_ncs( (wchar_t *) buf_ptr, temp_buf, len); out << temp_buf; bytes_written = _size ; } else { out << '\0'; } break; case TRICK_SHORT: out << (short)cv_convert_double(_conversion_factor,*(short *)buf_ptr); break; case TRICK_UNSIGNED_SHORT: out << (unsigned short)cv_convert_double(_conversion_factor,*(unsigned short *)buf_ptr); break; case TRICK_INTEGER: case TRICK_ENUMERATED: out << (int)cv_convert_double(_conversion_factor,*(int *)buf_ptr); break; case TRICK_BOOLEAN: out << (int)cv_convert_double(_conversion_factor,*(bool *)buf_ptr); break; case TRICK_BITFIELD: out << (GET_BITFIELD(buf_ptr, _var_info->attr->size, _var_info->attr->index[0].start, _var_info->attr->index[0].size)); break; case TRICK_UNSIGNED_BITFIELD: out << (GET_UNSIGNED_BITFIELD(buf_ptr, _var_info->attr->size, _var_info->attr->index[0].start, _var_info->attr->index[0].size)); break; case TRICK_UNSIGNED_INTEGER: out << (unsigned int)cv_convert_double(_conversion_factor,*(unsigned int *)buf_ptr); break; case TRICK_LONG: { long l = *(long *)buf_ptr; if (_conversion_factor != cv_get_trivial()) { l = (long)cv_convert_double(_conversion_factor, l); } out << l; break; } case TRICK_UNSIGNED_LONG: { unsigned long ul = *(unsigned long *)buf_ptr; if (_conversion_factor != cv_get_trivial()) { ul = (unsigned long)cv_convert_double(_conversion_factor, ul); } out << ul; break; } case TRICK_FLOAT: out << std::setprecision(8) << cv_convert_float(_conversion_factor,*(float *)buf_ptr); break; case TRICK_DOUBLE: out << std::setprecision(16) << cv_convert_double(_conversion_factor,*(double *)buf_ptr); break; case TRICK_LONG_LONG: { long long ll = *(long long *)buf_ptr; if (_conversion_factor != cv_get_trivial()) { ll = (long long)cv_convert_double(_conversion_factor, ll); } out << ll; break; } case TRICK_UNSIGNED_LONG_LONG: { unsigned long long ull = *(unsigned long long *)buf_ptr; if (_conversion_factor != cv_get_trivial()) { ull = (unsigned long long)cv_convert_double(_conversion_factor, ull); } out << ull; break; } case TRICK_NUMBER_OF_TYPES: out << "BAD_REF"; break; default:{ break; } } // end switch if (bytes_written < _size) { // if returning an array, continue array as comma separated values out << ","; buf_ptr = (void*) ((long)buf_ptr + _var_info->attr->size) ; } } //end while if (_requested_units != "") { if ( _var_info->attr->mods & TRICK_MODS_UNITSDASHDASH ) { out << " {--}"; } else { out << " {" << _requested_units << "}"; } } return 0; } void Trick::VariableReference::tagAsInvalid () { std::string save_name(getName()) ; free(_var_info) ; _var_info = make_error_ref(save_name) ; _address = _var_info->address ; } int Trick::VariableReference::prepareForWrite() { if (!_staged) { return 1; } void * temp_p = _stage_buffer; _stage_buffer = _write_buffer; _write_buffer = temp_p; _staged = false; _write_ready = true; return 0; } bool Trick::VariableReference::isStaged() const { return _staged; } bool Trick::VariableReference::isWriteReady() const { return _write_ready; } int Trick::VariableReference::writeTypeBinary( std::ostream& out, bool byteswap ) const { int local_type = _trick_type; if (byteswap) { local_type = trick_byteswap_int(local_type); } out.write(const_cast(reinterpret_cast(&local_type)), sizeof(int)); return 0; } int Trick::VariableReference::writeSizeBinary( std::ostream& out, bool byteswap ) const { int local_size = _size; if (byteswap) { local_size = trick_byteswap_int(local_size); } out.write(const_cast(reinterpret_cast(&local_size)), sizeof(int)); return 0; } int Trick::VariableReference::writeNameBinary( std::ostream& out, bool byteswap ) const { std::string name = getName(); out.write(name.c_str(), name.size()); return 0; } int Trick::VariableReference::writeNameLengthBinary( std::ostream& out, bool byteswap ) const { int name_size = getName().size(); if (byteswap) { name_size = trick_byteswap_int(name_size); } out.write(const_cast(reinterpret_cast(&name_size)), sizeof(int)); return 0; } void Trick::VariableReference::byteswap_var (char * out, char * in) const { byteswap_var(out, in, *this); } void Trick::VariableReference::byteswap_var (char * out, char * in, const VariableReference& ref) { ATTRIBUTES * attr = ref._var_info->attr; int array_size = 1; // Determine how many elements are in this array if it is an array for (int j = 0; j < ref._var_info->attr->num_index; j++) { array_size *= attr->index[j].size; } switch (attr->size) { case 1: // If these are just characters, no need to byteswap for (int j = 0; j < array_size; j++) { out[j] = in[j]; } break; case 2: { short * short_in = reinterpret_cast (in); short * short_out = reinterpret_cast (out); for (int j = 0; j < array_size; j++) { short_out[j] = trick_byteswap_short(short_in[j]); } break; } case 4: { int * int_in = reinterpret_cast (in); int * int_out = reinterpret_cast (out); for (int j = 0; j < array_size; j++) { int_out[j] = trick_byteswap_int(int_in[j]); } break; } case 8: { // We don't actually care if this is double or long, just that it's the right size double * double_in = reinterpret_cast (in); double * double_out = reinterpret_cast (out); for (int j = 0; j < array_size; j++) { double_out[j] = trick_byteswap_double(double_in[j]); } break; } } } int Trick::VariableReference::writeValueBinary( std::ostream& out, bool byteswap ) const { if ( _trick_type == TRICK_BITFIELD ) { int temp_i = GET_BITFIELD(_write_buffer , _var_info->attr->size , _var_info->attr->index[0].start, _var_info->attr->index[0].size) ; out.write((char *)(&temp_i), _size); return _size; } if ( _trick_type == TRICK_UNSIGNED_BITFIELD ) { int temp_unsigned = GET_UNSIGNED_BITFIELD(_write_buffer , _var_info->attr->size , _var_info->attr->index[0].start, _var_info->attr->index[0].size) ; out.write((char *)(&temp_unsigned), _size); return _size; } if (_trick_type == TRICK_NUMBER_OF_TYPES) { // TRICK_NUMBER_OF_TYPES is an error case int temp_zero = 0 ; out.write((char *)(&temp_zero), _size); return _size; } if (byteswap) { char * byteswap_buf = (char *) calloc (_size, 1); byteswap_var(byteswap_buf, (char *) _write_buffer); out.write(byteswap_buf, _size); free (byteswap_buf); } else { out.write((char *) _write_buffer, _size); } return _size; } std::ostream& Trick::operator<< (std::ostream& s, const Trick::VariableReference& ref) { s << " \"" << ref.getName() << "\""; return s; }