$search
00001 //================================================================================================= 00002 // Copyright (c) 2012, Johannes Meyer, TU Darmstadt 00003 // All rights reserved. 00004 00005 // Redistribution and use in source and binary forms, with or without 00006 // modification, are permitted provided that the following conditions are met: 00007 // * Redistributions of source code must retain the above copyright 00008 // notice, this list of conditions and the following disclaimer. 00009 // * Redistributions in binary form must reproduce the above copyright 00010 // notice, this list of conditions and the following disclaimer in the 00011 // documentation and/or other materials provided with the distribution. 00012 // * Neither the name of the Flight Systems and Automatic Control group, 00013 // TU Darmstadt, nor the names of its contributors may be used to 00014 // endorse or promote products derived from this software without 00015 // specific prior written permission. 00016 00017 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 00018 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 00019 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 00020 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 00021 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 00022 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00023 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 00024 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00025 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00026 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00027 //================================================================================================= 00028 00029 #include <rosmatlab/conversion.h> 00030 #include <rosmatlab/exception.h> 00031 00032 #include <introspection/message.h> 00033 #include <introspection/type.h> 00034 00035 #include <ros/time.h> 00036 #include <ros/duration.h> 00037 00038 #include <stdio.h> 00039 #include <ros/message_traits.h> 00040 00041 #include <mex.h> 00042 00043 namespace rosmatlab { 00044 00045 Conversion::Conversion(const MessagePtr &message) : message_(message) {} 00046 Conversion::~Conversion() {} 00047 00048 Array Conversion::toDoubleMatrix() { 00049 return toDoubleMatrix(0); 00050 } 00051 00052 Array Conversion::toDoubleMatrix(Array target, std::size_t n) { 00053 std::size_t m = 0; 00054 if (!target) target = mxCreateDoubleMatrix(expanded()->size(), n + 1, mxREAL); 00055 00056 bool do_realloc = false; 00057 if (mxGetM(target) < expanded()->size()) { mxSetM(target, expanded()->size()); do_realloc = true; } 00058 if (mxGetN(target) < n + 1) { mxSetN(target, n + 1); do_realloc = true; } 00059 if (do_realloc) 00060 { 00061 mxSetData(target, mxRealloc(mxGetData(target), mxGetN(target) * mxGetN(target) * sizeof(double))); 00062 } 00063 00064 double *data = mxGetPr(target) + mxGetM(target) * n; 00065 for(Message::const_iterator field = expanded()->begin(); field != expanded()->end(); ++field) { 00066 if (!(*field)->getType()->isNumeric()) continue; 00067 *data++ = (*field)->getType()->as_double((*field)->get()); 00068 } 00069 return target; 00070 } 00071 00072 Array Conversion::toStruct() { 00073 return toStruct(0); 00074 } 00075 00076 Array Conversion::toStruct(Array target, std::size_t index) { 00077 // mexPrintf("Constructing message %s (%s)...\n", message_->getName(), message_->getDataType()); 00078 if (!target) target = mxCreateStructMatrix(1, index + 1, message_->getFieldNames().size(), const_cast<const char **>(message_->getFieldNames().data())); 00079 00080 for(Message::const_iterator field = message_->begin(); field != message_->end(); ++field) { 00081 const char *field_name = (*field)->getName(); 00082 00083 if ((*field)->isMessage()) { 00084 MessagePtr field_message = messageByDataType((*field)->getDataType()); 00085 00086 if (field_message) { 00087 Array child = mxCreateStructMatrix(1, (*field)->size(), field_message->getFieldNames().size(), const_cast<const char **>(field_message->getFieldNames().data())); 00088 00089 // iterate over array 00090 for(std::size_t j = 0; j < (*field)->size(); j++) { 00091 // mexPrintf("Expanding field %s[%u] (%s)... %u\n", (*field)->getName(), j, (*field)->getDataType()); 00092 MessagePtr expanded = (*field)->expand(j); 00093 if (expanded) { 00094 Conversion(expanded).toStruct(child, j); 00095 } else { 00096 mexPrintf("Error during expansion of %s[%u] (%s)... %u\n", (*field)->getName(), j, (*field)->getDataType()); 00097 } 00098 } 00099 00100 mxSetField(target, index, field_name, child); 00101 00102 } else { 00103 const char **field_names = { 0 }; 00104 mxSetField(target, index, field_name, mxCreateStructMatrix(1, (*field)->size(), 0, field_names)); 00105 } 00106 00107 } else { 00108 mxSetField(target, index, field_name, convertToMatlab(*field)); 00109 } 00110 } 00111 00112 return target; 00113 } 00114 00115 MessagePtr Conversion::fromMatlab(ConstArray source, std::size_t index) 00116 { 00117 MessagePtr message = message_->introspect(message_->createInstance()); 00118 fromMatlab(message, source, index); 00119 return message; 00120 } 00121 00122 void Conversion::fromMatlab(const MessagePtr& message, ConstArray source, std::size_t index) 00123 { 00124 if (mxIsStruct(source)) { 00125 fromStruct(message, source, index); 00126 return; 00127 } 00128 00129 if (mxIsDouble(source)) { 00130 fromDoubleMatrix(message, source, index); 00131 return; 00132 } 00133 00134 throw Exception("Cannot parse a message of class " + std::string(mxGetClassName(source)) + " as ROS message"); 00135 } 00136 00137 void Conversion::fromDoubleMatrix(const MessagePtr& message, ConstArray source, std::size_t n) 00138 { 00139 if (!mxIsDouble(source)) return; 00140 00141 const double *begin = 0; 00142 const double *end = 0; 00143 00144 if (mxGetM(source) == 1 && n == 0) { 00145 begin = mxGetPr(source); 00146 end = begin + mxGetN(source); 00147 } else { 00148 if (n >= mxGetN(source)) throw Exception("Column index out of bounds"); 00149 begin = mxGetPr(source) + mxGetM(source) * n; 00150 end = mxGetPr(source) + mxGetM(source) * (n + 1); 00151 } 00152 00153 fromDoubleMatrix(message, begin, end); 00154 } 00155 00156 void Conversion::fromDoubleMatrix(const MessagePtr &message, const double *begin, const double *end) 00157 { 00158 for(Message::const_iterator field = message->begin(); field != message->end(); ++field) { 00159 begin = convertFromDouble(*field, begin, end); 00160 } 00161 if (begin != end) throw Exception("Failed to parse a message of type " + std::string(message->getDataType()) + ": vector is too long"); 00162 } 00163 00164 void Conversion::fromStruct(const MessagePtr &message, ConstArray source, std::size_t index) 00165 { 00166 if (!mxIsStruct(source)) return; 00167 00168 for(Message::const_iterator field = message->begin(); field != message->end(); ++field) { 00169 ConstArray field_source = mxGetField(source, index, (*field)->getName()); 00170 if (!field_source) continue; 00171 convertFromMatlab(*field, field_source); 00172 } 00173 } 00174 00175 Array Conversion::convertToMatlab(const FieldPtr& field) { 00176 Array target = 0; 00177 TypePtr field_type = field->getType(); 00178 00179 // mexPrintf("Constructing field %s (%s)...\n", field->getName(), field->getDataType()); 00180 try { 00181 if (field_type->isString()) { 00182 if (field->isArray()) { 00183 target = mxCreateCellMatrix(1, field->size()); 00184 for(std::size_t i = 0; i < field->size(); i++) { 00185 mxSetCell(target, i, mxCreateString(field_type->as_string(field->get(i)).c_str())); 00186 } 00187 } else { 00188 target = mxCreateString(field_type->as_string(field->get()).c_str()); 00189 } 00190 return target; 00191 } 00192 00193 // mexPrintf("Constructing double vector with dimension %u for field %s\n", unsigned(field->size()), field->getName()); 00194 target = mxCreateDoubleMatrix(1, field->size(), mxREAL); 00195 double *x = mxGetPr(target); 00196 00197 for(std::size_t i = 0; i < field->size(); i++) { 00198 x[i] = field_type->as_double(field->get(i)); 00199 } 00200 00201 } catch(boost::bad_any_cast &e) { 00202 mexPrintf("Catched bad_any_cast exception for field %s: %s", field->getName(), e.what()); 00203 target = emptyArray(); 00204 } 00205 00206 return target; 00207 } 00208 00209 void Conversion::convertFromMatlab(const FieldPtr &field, ConstArray source) { 00210 const char *field_name = field->getName(); 00211 std::size_t field_size = mxIsChar(source) ? 1 : mxGetNumberOfElements(source); 00212 00213 // check size 00214 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())); 00215 if (!field->isContainer() && field_size != 1) throw Exception("Failed to parse field " + std::string(field_name) + ": Scalar field must have exactly length 1"); 00216 if (field->isVector()) field->resize(field_size); 00217 00218 // parse ROS message 00219 if (field->isMessage()) { 00220 MessagePtr field_message = messageByDataType(field->getDataType()); 00221 if (!field_message) throw Exception("Failed to parse field " + std::string(field_name) + ": unknown datatype " + field->getDataType()); 00222 Conversion child_conversion(field_message); 00223 00224 // iterate over array 00225 for(std::size_t i = 0; i < field_size; i++) { 00226 // mexPrintf("Expanding field %s[%u] (%s)... %u\n", field->getName(), i, field->getDataType()); 00227 MessagePtr expanded = field->expand(i); 00228 if (expanded) { 00229 child_conversion.fromMatlab(expanded, source, i); 00230 } else { 00231 mexPrintf("Error during expansion of %s[%u] (%s)... %u\n", field->getName(), i, field->getDataType()); 00232 } 00233 } 00234 00235 return; 00236 } 00237 00238 // parse string 00239 if (field->getType()->isString()) { 00240 std::vector<char> buffer; 00241 00242 for(std::size_t i = 0; i < field->size(); i++) { 00243 if (mxIsCell(source) && mxIsChar(mxGetCell(source, i))) { 00244 buffer.resize(mxGetNumberOfElements(mxGetCell(source, i)) + 1); 00245 mxGetString(mxGetCell(source, i), buffer.data(), buffer.size()); 00246 field->set(std::string(buffer.data(), buffer.size() - 1), i); 00247 00248 } else if (mxIsChar(source) && i == 0) { 00249 buffer.resize(mxGetN(source) + 1); 00250 mxGetString(source, buffer.data(), buffer.size()); 00251 field->set(std::string(buffer.data(), buffer.size() - 1)); 00252 00253 } else { 00254 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"); 00255 } 00256 } 00257 00258 return; 00259 } 00260 00261 // For all other types source must be a double array... 00262 if (!mxIsDouble(source)) throw Exception("Failed to parse field " + std::string(field->getDataType()) + " " + std::string(field->getName()) + ": Array must be a double array"); 00263 const double *x = mxGetPr(source); 00264 convertFromDouble(field, x, x + mxGetN(source)); 00265 } 00266 00267 const double *Conversion::convertFromDouble(const FieldPtr& field, const double *begin, const double *end) 00268 { 00269 const double *data = begin; 00270 00271 if (field->isVector() && end >= begin) { 00272 // eat up all the input (roar!!!) 00273 field->resize(end - begin); 00274 } 00275 00276 // check size 00277 if (end - begin < field->size()) { 00278 throw Exception("Failed to parse field " + std::string(field->getName()) + ": vector is too short"); 00279 } 00280 00281 // parse ROS message 00282 if (field->isMessage()) { 00283 // TODO 00284 } 00285 00286 // ignore strings (set to empty string) 00287 if (field->getType()->isString()) { 00288 for(std::size_t i = 0; i < field->size(); i++) { 00289 field->set(std::string(), i); 00290 data++; 00291 } 00292 return data; 00293 } 00294 00295 // read doubles 00296 for(std::size_t i = 0; i < field->size(); i++) { 00297 field->set(*data++, i); 00298 } 00299 00300 return data; 00301 } 00302 00303 Array Conversion::emptyArray() const { 00304 return mxCreateDoubleMatrix(0, 0, mxREAL); 00305 } 00306 00307 const MessagePtr& Conversion::expanded() { 00308 if (!expanded_) { 00309 expanded_ = expand(message_); 00310 // mexPrintf("Expanded an instance of %s to %u fields", message_->getDataType(), expanded_->size()); 00311 } 00312 return expanded_; 00313 } 00314 00315 }