00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include <rosmatlab/conversion.h>
00030 #include <rosmatlab/exception.h>
00031 #include <rosmatlab/log.h>
00032
00033 #include <introspection/message.h>
00034 #include <introspection/type.h>
00035
00036 #include <ros/time.h>
00037 #include <ros/duration.h>
00038
00039 #include <stdio.h>
00040 #include <ros/message_traits.h>
00041 #include <boost/algorithm/string.hpp>
00042
00043 #include <mex.h>
00044
00045 namespace rosmatlab {
00046
00047 Conversion::Conversion(const MessagePtr &message) : message_(message), options_(defaultOptions())
00048 {
00049 options_.merge(perMessageOptions(message));
00050 }
00051
00052 Conversion::Conversion(const MessagePtr &message, const ConversionOptions& options) : message_(message), options_(defaultOptions())
00053 {
00054 options_.merge(perMessageOptions(message));
00055 options_.merge(options);
00056 }
00057
00058 Conversion::Conversion(const Conversion &other, const MessagePtr &message)
00059 : message_(message ? message : other.message_)
00060 , options_(other.options_)
00061 {
00062 options_.merge(perMessageOptions(message));
00063 }
00064
00065 Conversion::~Conversion() {}
00066
00067 Array Conversion::toMatlab() {
00068 return toMatlab(0);
00069 }
00070
00071 Array Conversion::toMatlab(Array target, std::size_t index, std::size_t size) {
00072 switch(options_.conversionType()) {
00073 case ConversionOptions::MATLAB_STRUCT:
00074 return toStruct(target, index, size);
00075 case ConversionOptions::MATLAB_MATRIX:
00076 return toDoubleMatrix(target, index, size);
00077 case ConversionOptions::MATLAB_EXTENDED_STRUCT:
00078 return toExtendedStruct(target, index, size);
00079 }
00080
00081 throw Exception("Unsupported conversion type " + boost::lexical_cast<std::string>(options_.conversionType()));
00082 }
00083
00084 Array Conversion::toDoubleMatrix() {
00085 return toDoubleMatrix(0);
00086 }
00087
00088 Array Conversion::toDoubleMatrix(Array target, std::size_t index, std::size_t size) {
00089 if (!target) target = mxCreateDoubleMatrix(expanded()->size(), size > 0 ? size : index + 1, mxREAL);
00090
00091 bool do_realloc = false;
00092 if (mxGetM(target) < expanded()->size()) { mxSetM(target, expanded()->size()); do_realloc = true; }
00093 if (mxGetN(target) < index + 1) { mxSetN(target, index + 1); do_realloc = true; }
00094 if (do_realloc)
00095 {
00096 mxSetData(target, mxRealloc(mxGetData(target), mxGetN(target) * mxGetN(target) * sizeof(double)));
00097 }
00098
00099 double *data = mxGetPr(target) + mxGetM(target) * index;
00100 for(Message::const_iterator field = expanded()->begin(); field != expanded()->end(); ++field) {
00101
00102 *data++ = (*field)->getType()->as_double((*field)->get());
00103 }
00104 return target;
00105 }
00106
00107 Array Conversion::toStruct() {
00108 return toStruct(0);
00109 }
00110
00111 Array Conversion::toStruct(Array target, std::size_t index, std::size_t size) {
00112
00113
00114 if (!target) target = mxCreateStructMatrix(1, size > 0 ? size : index + 1,
00115 message_->getFieldNames().size(),
00116 const_cast<const char **>(message_->getFieldNames().data())
00117 );
00118
00119
00120 if (mxGetNumberOfFields(target) == 0) {
00121 for(V_FieldName::const_iterator it = message_->getFieldNames().begin(); it != message_->getFieldNames().end(); ++it) {
00122 mxAddField(target, *it);
00123 }
00124 }
00125
00126
00127 for(Message::const_iterator field = message_->begin(); field != message_->end(); ++field) {
00128 const char *field_name = (*field)->getName();
00129
00130 if ((*field)->isMessage()) {
00131 MessagePtr field_message = messageByDataType((*field)->getValueType());
00132
00133 if (field_message) {
00134 Array child = 0;
00135
00136
00137 for(std::size_t j = 0; j < (*field)->size(); j++) {
00138
00139 MessagePtr expanded = (*field)->expand(j);
00140 if (expanded) {
00141 child = Conversion(expanded).toMatlab(child, j, (*field)->size());
00142 } else {
00143 ROSMATLAB_PRINTF("Error during expansion of %s[%u] (%s)...", (*field)->getName(), j, (*field)->getDataType());
00144 }
00145 }
00146
00147 mxSetField(target, index, field_name, child);
00148
00149 } else {
00150 ROSMATLAB_PRINTF("Error during conversion of field %s[%u] (%s): unknown datatype", (*field)->getName(), (*field)->size(), (*field)->getDataType());
00151 const char **field_names = { 0 };
00152 mxSetField(target, index, field_name, mxCreateStructMatrix(1, (*field)->size(), 0, field_names));
00153 }
00154
00155 } else {
00156 mxSetField(target, index, field_name, convertToMatlab(*field));
00157 }
00158 }
00159
00160
00161 if (options_.addMetaData()) {
00162 if (mxGetFieldNumber(target, "DATATYPE") == -1) mxAddField(target, "DATATYPE");
00163 mxSetField(target, index, "DATATYPE", mxCreateString(message_->getDataType()));
00164 if (mxGetFieldNumber(target, "MD5SUM") == -1) mxAddField(target, "MD5SUM");
00165 mxSetField(target, index, "MD5SUM", mxCreateString(message_->getMD5Sum()));
00166 }
00167
00168 return target;
00169 }
00170
00171 Array Conversion::toExtendedStruct() {
00172 return toStruct(0);
00173 }
00174
00175 Array Conversion::toExtendedStruct(Array target, std::size_t index, std::size_t size) {
00176 static const char *fieldnames[] = { "count", "stamps", "data", "fields", "strings", "string_fields" };
00177 if (!target) target = mxCreateStructMatrix(1, 1, sizeof(fieldnames)/sizeof(*fieldnames), fieldnames);
00178
00179
00180 if (size == 0) size = index + 1;
00181 mxSetField(target, 0, "count", mxCreateDoubleScalar(size));
00182
00183
00184 mxArray *stamps = mxGetField(target, 0, "stamps");
00185 if (message_->hasHeader()) {
00186 if (!stamps) stamps = mxCreateDoubleMatrix(1, size, mxREAL);
00187 *(mxGetPr(stamps) + index) = message_->getHeader(message_->getConstInstance())->stamp.toSec();
00188 mxSetField(target, 0, "stamps", stamps);
00189 }
00190
00191
00192 mxArray *data = mxGetField(target, 0, "data");
00193 data = toDoubleMatrix(data, index, size);
00194 mxSetField(target, 0, "data", data);
00195
00196
00197 mxArray *fields = mxGetField(target, 0, "fields");
00198 if (!fields) {
00199 const V_FieldName& fieldnames = expanded()->getFieldNames();
00200 fields = mxCreateCellMatrix(fieldnames.size(), 1);
00201 for(int i = 0; i < fieldnames.size(); i++) {
00202 mxSetCell(fields, i, mxCreateString(fieldnames.at(i)));
00203 }
00204 mxSetField(target, 0, "fields", fields);
00205 }
00206
00207
00208 mxArray *strings = mxGetField(target, 0, "strings");
00209 mxArray *string_fields = mxGetField(target, 0, "string_fields");
00210
00211 std::size_t string_count = 0;
00212 for(Message::const_iterator field_it = expanded()->begin(); field_it != expanded()->end(); ++field_it) {
00213 const FieldPtr& field = *field_it;
00214 if (field->getType()->isString()) string_count++;
00215 }
00216
00217 if (string_count > 0) {
00218 if (!string_fields) {
00219 string_fields = mxCreateCellMatrix(string_count, 1);
00220 std::size_t string_index = 0;
00221 for(Message::const_iterator field_it = expanded()->begin(); field_it != expanded()->end(); ++field_it, ++string_index) {
00222 const FieldPtr& field = *field_it;
00223 if (!field->getType()->isString()) continue;
00224 mxSetCell(string_fields, string_index, mxCreateString(field->getName()));
00225 }
00226 }
00227
00228 if (!strings) {
00229 strings = mxCreateCellMatrix(string_count, size);
00230 } else if (mxGetM(strings) < string_count) {
00231 throw Exception("string_fields cell has wrong size");
00232 }
00233
00234 std::size_t string_index = 0;
00235 for(Message::const_iterator field_it = expanded()->begin(); field_it != expanded()->end(); ++field_it, ++string_index) {
00236 const FieldPtr& field = *field_it;
00237 if (!field->getType()->isString()) continue;
00238 mxSetCell(strings, index * string_count + string_index, mxCreateString(field->getType()->as_string(field->get(0)).c_str()));
00239 }
00240
00241 mxSetField(target, 0, "strings", strings);
00242 mxSetField(target, 0, "string_fields", string_fields);
00243 }
00244
00245
00246
00247
00248 return target;
00249 }
00250
00251 std::size_t Conversion::numberOfInstances(ConstArray source)
00252 {
00253 if (mxIsStruct(source)) {
00254 return mxGetNumberOfElements(source);
00255 }
00256
00257 if (mxIsDouble(source)) {
00258 return mxGetN(source);
00259 }
00260
00261 if (mxIsChar(source)) {
00262 return 1;
00263 }
00264
00265 throw Exception("Cannot parse an array of class " + std::string(mxGetClassName(source)) + " as ROS message");
00266 }
00267
00268 MessagePtr Conversion::fromMatlab(ConstArray source, std::size_t index)
00269 {
00270 MessagePtr target = message_->introspect(message_->createInstance());
00271 fromMatlab(target, source, index);
00272 return target;
00273 }
00274
00275 void Conversion::fromMatlab(const MessagePtr& target, ConstArray source, std::size_t index)
00276 {
00277 if (mxIsStruct(source)) {
00278 fromStruct(target, source, index);
00279 return;
00280 }
00281
00282 if (mxIsDouble(source)) {
00283 fromDoubleMatrix(target, source, index);
00284 return;
00285 }
00286
00287 if (mxIsChar(source) && target->hasType<std_msgs::String>()) {
00288 std_msgs::StringPtr data = target->getInstanceAs<std_msgs::String>();
00289 if (data) {
00290 data->data = Options::getString(source);
00291 return;
00292 }
00293 }
00294
00295 throw Exception("Cannot parse an array of class " + std::string(mxGetClassName(source)) + " as " + std::string(target->getDataType()) + " message");
00296 }
00297
00298 void Conversion::fromDoubleMatrix(const MessagePtr& target, ConstArray source, std::size_t n)
00299 {
00300 if (!mxIsDouble(source)) return;
00301
00302 const double *begin = 0;
00303 const double *end = 0;
00304
00305 if (mxGetM(source) == 1 && n == 0) {
00306 begin = mxGetPr(source);
00307 end = begin + mxGetN(source);
00308 } else {
00309 if (n >= mxGetN(source)) throw Exception("Column index out of bounds");
00310 begin = mxGetPr(source) + mxGetM(source) * n;
00311 end = mxGetPr(source) + mxGetM(source) * (n + 1);
00312 }
00313
00314 fromDoubleMatrix(target, begin, end);
00315 }
00316
00317 void Conversion::fromDoubleMatrix(const MessagePtr &target, const double *begin, const double *end)
00318 {
00319 for(Message::const_iterator field = target->begin(); field != target->end(); ++field) {
00320 begin = convertFromDouble(*field, begin, end);
00321 }
00322 if (begin != end) throw Exception("Failed to parse an array of type " + std::string(target->getDataType()) + ": vector is too long");
00323 }
00324
00325 void Conversion::fromStruct(const MessagePtr &target, ConstArray source, std::size_t index)
00326 {
00327 if (!mxIsStruct(source)) return;
00328 if (index >= mxGetNumberOfElements(source)) throw Exception("Index out of bounds");
00329
00330 for(Message::const_iterator field = target->begin(); field != target->end(); ++field) {
00331 ConstArray field_source = mxGetField(source, index, (*field)->getName());
00332 if (!field_source) continue;
00333 convertFromMatlab(*field, field_source);
00334 }
00335 }
00336
00337 Array Conversion::convertToMatlab(const FieldPtr& field) {
00338 Array target = 0;
00339 TypePtr field_type = field->getType();
00340
00341
00342 try {
00343 if (field_type->isString()) {
00344 if (field->isArray() || field->isVector()) {
00345 target = mxCreateCellMatrix(1, field->size());
00346 for(std::size_t i = 0; i < field->size(); i++) {
00347 mxSetCell(target, i, mxCreateString(field_type->as_string(field->get(i)).c_str()));
00348 }
00349 } else {
00350 target = mxCreateString(field_type->as_string(field->get()).c_str());
00351 }
00352 return target;
00353 }
00354
00355
00356 target = mxCreateDoubleMatrix(1, field->size(), mxREAL);
00357 double *x = mxGetPr(target);
00358
00359 for(std::size_t i = 0; i < field->size(); i++) {
00360 x[i] = field_type->as_double(field->get(i));
00361 }
00362
00363 } catch(boost::bad_any_cast &e) {
00364 ROSMATLAB_PRINTF("Catched bad_any_cast exception for field %s: %s", field->getName(), e.what());
00365 target = mxCreateEmpty();
00366 }
00367
00368 return target;
00369 }
00370
00371 void Conversion::convertFromMatlab(const FieldPtr &field, ConstArray source) {
00372 const char *field_name = field->getName();
00373 std::size_t field_size = mxIsChar(source) ? 1 : mxGetNumberOfElements(source);
00374
00375
00376 if (field->isArray() && field->size() != field_size) throw Exception("Failed to parse field " + std::string(field_name) + ": Array field must have length " + boost::lexical_cast<std::string>(field->size()));
00377 if (!field->isContainer() && field_size != 1) throw Exception("Failed to parse field " + std::string(field_name) + ": Scalar field must have exactly length 1");
00378 if (field->isVector()) field->resize(field_size);
00379
00380
00381 if (field->isMessage()) {
00382 MessagePtr field_message = messageByDataType(field->getValueType());
00383 if (!field_message) throw Exception("Failed to parse field " + std::string(field_name) + ": unknown datatype " + field->getDataType());
00384 Conversion child_conversion(field_message);
00385
00386
00387 for(std::size_t i = 0; i < field_size; i++) {
00388
00389 MessagePtr expanded = field->expand(i);
00390 if (expanded) {
00391 child_conversion.fromMatlab(expanded, source, i);
00392 } else {
00393 ROSMATLAB_PRINTF("Error during expansion of %s[%u] (%s)... %u", field->getName(), i, field->getDataType());
00394 }
00395 }
00396
00397 return;
00398 }
00399
00400
00401 if (field->getType()->isString()) {
00402 std::vector<char> buffer;
00403
00404 for(std::size_t i = 0; i < field->size(); i++) {
00405 if (mxIsCell(source) && mxIsChar(mxGetCell(source, i))) {
00406 buffer.resize(mxGetNumberOfElements(mxGetCell(source, i)) + 1);
00407 mxGetString(mxGetCell(source, i), buffer.data(), buffer.size());
00408 field->set(std::string(buffer.data(), buffer.size() - 1), i);
00409
00410 } else if (mxIsChar(source) && i == 0) {
00411 buffer.resize(mxGetN(source) + 1);
00412 mxGetString(source, buffer.data(), buffer.size());
00413 field->set(std::string(buffer.data(), buffer.size() - 1));
00414
00415 } else {
00416 throw Exception("Failed to parse string field " + std::string(field->getDataType()) + " " + std::string(field->getName()) + ": Array must be a cell string or a character array");
00417 }
00418 }
00419
00420 return;
00421 }
00422
00423
00424 if (!mxIsDouble(source)) throw Exception("Failed to parse field " + std::string(field->getDataType()) + " " + std::string(field->getName()) + ": Array must be a double array");
00425 const double *x = mxGetPr(source);
00426 convertFromDouble(field, x, x + mxGetN(source));
00427 }
00428
00429 const double *Conversion::convertFromDouble(const FieldPtr& field, const double *begin, const double *end)
00430 {
00431 const double *data = begin;
00432
00433 if (field->isVector() && end >= begin) {
00434
00435 field->resize(end - begin);
00436 }
00437
00438
00439 if (end - begin < field->size()) {
00440 throw Exception("Failed to parse field " + std::string(field->getName()) + ": vector is too short");
00441 }
00442
00443
00444 if (field->isMessage()) {
00445
00446 }
00447
00448
00449 if (field->getType()->isString()) {
00450 for(std::size_t i = 0; i < field->size(); i++) {
00451 field->set(std::string(), i);
00452 data++;
00453 }
00454 return data;
00455 }
00456
00457
00458 for(std::size_t i = 0; i < field->size(); i++) {
00459 field->set(*data++, i);
00460 }
00461
00462 return data;
00463 }
00464
00465 const MessagePtr& Conversion::expanded() {
00466 if (!expanded_) {
00467 expanded_ = expand(message_);
00468
00469 }
00470 return expanded_;
00471 }
00472
00473 ConversionOptions &Conversion::defaultOptions() {
00474 static boost::shared_ptr<ConversionOptions> default_options;
00475 if (!default_options) {
00476 default_options.reset(new ConversionOptions());
00477 }
00478 return *default_options;
00479 }
00480
00481 std::map<const char *,ConversionOptions> Conversion::per_message_options_;
00482 ConversionOptions &Conversion::perMessageOptions(const MessagePtr &message) {
00483 return per_message_options_[message->getDataType()];
00484 }
00485
00486 ConversionOptions::ConversionOptions()
00487 {
00488 }
00489
00490 ConversionOptions::ConversionOptions(int nrhs, const mxArray *prhs[])
00491 {
00492 init(nrhs, prhs);
00493 }
00494
00495 ConversionOptions::~ConversionOptions()
00496 {
00497 }
00498
00499 void ConversionOptions::init(int nrhs, const mxArray *prhs[])
00500 {
00501 Options::init(nrhs, prhs, true);
00502
00503 std::string type = getString("type");
00504 if (!type.empty()) {
00505 if (boost::algorithm::iequals(type, "struct"))
00506 setConversionType(MATLAB_STRUCT);
00507 else if (boost::algorithm::iequals(type, "matrix"))
00508 setConversionType(MATLAB_MATRIX);
00509 else if (boost::algorithm::iequals(type, "extended"))
00510 setConversionType(MATLAB_EXTENDED_STRUCT);
00511 else
00512 throw Exception("unknown conversion type '" + type + "'");
00513 }
00514
00515 if (conversionType() >= MATLAB_TYPE_MAX) {
00516 throw Exception("illegal conversion type " + boost::lexical_cast<std::string>(conversionType()));
00517 }
00518 }
00519
00520 ConversionOptions::MatlabType ConversionOptions::conversionType() const
00521 {
00522 return static_cast<ConversionOptions::MatlabType>(getInteger("type"));
00523 }
00524
00525 std::string ConversionOptions::conversionTypeString() const
00526 {
00527 switch(conversionType()) {
00528 case MATLAB_STRUCT: return "struct";
00529 case MATLAB_MATRIX: return "matrix";
00530 case MATLAB_EXTENDED_STRUCT: return "extended";
00531 }
00532 return std::string();
00533 }
00534
00535 ConversionOptions &ConversionOptions::setConversionType(ConversionOptions::MatlabType type)
00536 {
00537 set("type", static_cast<int>(type));
00538 return *this;
00539 }
00540
00541 bool ConversionOptions::addMetaData() const
00542 {
00543 return getBool("meta");
00544 }
00545
00546 ConversionOptions &ConversionOptions::setAddMetaData(bool value)
00547 {
00548 set("meta", value);
00549 return *this;
00550 }
00551
00552 bool ConversionOptions::addConnectionHeader() const
00553 {
00554 return getBool("connectionheader");
00555 }
00556
00557 ConversionOptions &ConversionOptions::setAddConnectionHeader(bool value)
00558 {
00559 set("meta", value);
00560 return *this;
00561 }
00562
00563 mxArray *ConversionOptions::toMatlab() const {
00564 const char *fieldnames[] = { "Type", "Meta", "ConnectionHeader" };
00565 mxArray *result = mxCreateStructMatrix(1, 1, sizeof(fieldnames)/sizeof(*fieldnames), fieldnames);
00566 mxSetField(result, 0, "Type", mxCreateString(conversionTypeString().c_str()));
00567 mxSetField(result, 0, "Meta", mxCreateLogicalScalar(addMetaData()));
00568 mxSetField(result, 0, "ConnectionHeader", mxCreateLogicalScalar(addConnectionHeader()));
00569 return result;
00570 }
00571
00572 }