00001
00044 #include <iostream>
00045 #include <string>
00046 #include <boost/program_options.hpp>
00047
00048 #include <QString>
00049 #include <QStringList>
00050 #include <QDateTime>
00051 #include <QFile>
00052 #include <QDebug>
00053 #include <QDomDocument>
00054
00055 #include <ros/ros.h>
00056 #include <re_srvs/SetObject.h>
00057 #include <re_srvs/GetObject.h>
00058 #include <re_srvs/UpdateObjectBinaryFile.h>
00059 #include <re_object_recorder/qminizip.h>
00060 #include "owldescriptioncreator.h"
00061 #include "uploadhelpers.h"
00062
00063 namespace po = boost::program_options;
00064
00065 using std::cout;
00066 using std::cerr;
00067 using std::endl;
00068 using std::string;
00069
00070
00071
00072 class UploadObjectModelNode {
00073 public:
00074 UploadObjectModelNode(int argc, char *argv[]) : desc("Usage"), update(false), no_owl(false) {
00075 desc.add_options()
00076 ("help", "show this help message")
00077 ("key", po:: value<string>(), "RoboEarth API key (get yours from http://api.roboearth.org)")
00078 ("name", po::value<string>(),"name of the object")
00079 ("class", po::value<string>(), "object class of the model")
00080 ("description", po::value<string>(), "human-readable description of the model")
00081 ("type", po::value<string>(), "model type (default value: 'ColoredPointCloudModel'")
00082 ("update", "signals that the given file should be updated/added to the object in the database")
00083 ("no-owl", "signals that no owl file should be generated (might cause upload to fail)")
00084 ("force", "upload object model regardless of model name/class inconsistencies")
00085 ("file", po::value<string>(), "zipped model file")
00086 ;
00087
00088 po::positional_options_description pos_opt_descs;
00089 pos_opt_descs.add("file", 1);
00090 po::parsed_options parsed = po::command_line_parser(argc, argv).options(desc).allow_unregistered().positional(pos_opt_descs).run();
00091 po::store(parsed, vm);
00092 po::notify(vm);
00093 }
00094
00095 bool checkArguments() {
00096 if (vm.count("help")) {
00097 printUsage();
00098 return false;
00099 }
00100
00101 if (!vm.count("file")) {
00102 cerr << "no model file given." << endl;
00103 printUsage();
00104 return false;
00105 }
00106 file = vm["file"].as<string>();
00107 if (!vm.count("key")) {
00108 cerr << "API key not given. Please specify an RoboEarth API key with --key=<KEY>'" << endl;
00109 printUsage();
00110 return false;
00111 }
00112
00113 if ((file.length() > 4) && (file.substr(file.length() - 4) == ".zip")) {
00114
00115 QMiniZip mz(QString::fromStdString(file), QMiniZip::UNZIP);
00116
00117 QByteArray ba = mz.getFile("meta.xml");
00118 if (ba.isEmpty()) {
00119 cerr << "WARNING: could not read meta.xml file in model!" << endl;
00120 } else {
00121 QDomDocument dom("meta");
00122 dom.setContent(ba);
00123
00124 QDomElement modelEl = dom.firstChildElement("model");
00125 QDomElement nameEl = modelEl.firstChildElement("name");
00126 QString nameTxt = nameEl.text();
00127
00128 if (nameTxt.count(".") == 1) {
00129 clss = nameTxt.split('.')[0].toStdString();
00130 name = nameTxt.split('.')[1].toStdString();
00131 } else {
00132 cerr << "WARNING: could not parse name in model: '" << nameTxt.toStdString() << "'" << endl;
00133 }
00134 }
00135 }
00136 if (name.empty()) {
00137 if (!vm.count("name")) {
00138 cerr << "ERROR: model name neither in meta.xml nor on command line found" << endl;
00139 return false;
00140 }
00141 name = vm["name"].as<string>();
00142 }
00143 if (clss.empty()) {
00144 if (!vm.count("class")) {
00145 cerr << "ERROR: model class neither in meta.xml nor on command line found" << endl;
00146 return false;
00147 }
00148 clss = vm["class"].as<string>();
00149 }
00150 if (vm.count("name") && (vm["name"].as<string>() != name)) {
00151 cerr << "WARNING: model name from command line does not match with model name from model's meta.xml: " << endl
00152 << "\tname from command line: " << vm["name"].as<string>() << endl
00153 << "\tname from meta.xml....: " << name << endl;
00154 if (!vm.count("force")) {
00155 return false;
00156 } else {
00157 cerr << "using model name from command line" << endl;
00158 name = vm["name"].as<string>();
00159 }
00160 }
00161 if (vm.count("class") && (vm["class"].as<string>() != clss)){
00162 cerr << "WARNING: model class from command line does not match with model class from model's meta.xml: " << endl
00163 << "\tclass from command line: " << vm["class"].as<string>() << endl
00164 << "\tclass from meta.xml....: " << clss << endl;
00165 if (!vm.count("force")) {
00166 return false;
00167 } else {
00168 cerr << "using model class from command line" << endl;
00169 clss = vm["class"].as<string>();
00170 }
00171 }
00172
00173 key = vm["key"].as<string>();
00174 type = !vm.count("type") ? "ColoredPointCloudModel" : vm["type"].as<string>();
00175 description = !vm.count("description") ? "" : vm["description"].as<string>();
00176
00177 update = vm.count("update");
00178 no_owl = vm.count("no-owl");
00179
00180 return true;
00181 }
00182
00183 bool upload() {
00184
00185 std::vector<re_msgs::File> fileMsgs;
00186 QString filename = QString::fromStdString(file);
00187 QString actualFilename = filename.split('/', QString::SkipEmptyParts).last();
00188 QFile f(filename);
00189
00190 if (!f.open(QIODevice::ReadOnly)) {
00191 cerr << "Could not read file " << filename.toStdString() << endl;
00192 return false;
00193 }
00194
00195 QByteArray data = f.readAll();
00196 re_msgs::File fileMsg;
00197 fileMsg.name = actualFilename.toStdString();
00198 fileMsg.data.resize(data.size());
00199 std::copy(data.data(), data.data()+data.size(), fileMsg.data.begin());
00200 fileMsgs.push_back(fileMsg);
00201
00202
00203 UploadBase *uploadGeneric = NULL;
00204 if (update) {
00205 UpdateBinary *uploadSpecific = new UpdateBinary();
00206 uploadSpecific->fillRequest(key, clss + "." + name, fileMsg);
00207
00208 uploadGeneric = uploadSpecific;
00209 } else {
00210 UploadSetNewObject *uploadSpecific = new UploadSetNewObject();
00211 OWLDescriptionCreator owldc(QString::fromStdString(type), QString::fromStdString(clss), QDateTime::currentDateTime());
00212 uploadSpecific->fillRequest(key, name, clss,
00213 no_owl ? "" : owldc.getOwlDescription().toStdString(),
00214 description, fileMsgs);
00215
00216 uploadGeneric = uploadSpecific;
00217 }
00218 if (!uploadGeneric->waitForService())
00219 return false;
00220 if (!uploadGeneric->check())
00221 return false;
00222
00223 return uploadGeneric->upload();
00224 }
00225
00226 private:
00227 void printUsage() const {
00228 cout << desc << "\n";
00229 cout << "For example: " << endl
00230 << " upload a new model:\tupload_object_model --key <API-KEY> kinectmodel.zip" << endl
00231 << " update a binary file:\tupload_object_model --key <API-KEY> --update kinectmodel.zip " << endl << endl
00232 << "Please note that re_comm must be running for this program to work." << endl;
00233 }
00234
00235 ros::NodeHandle nh;
00236 po::options_description desc;
00237 po::variables_map vm;
00238 string key, name, clss, type, description, file;
00239 bool update, no_owl;
00240 };
00241
00242
00243 int main(int argc, char *argv[])
00244 {
00245 ros::init(argc, argv, "upload_object_model");
00246
00247 UploadObjectModelNode um(argc, argv);
00248 if (!um.checkArguments())
00249 return 1;
00250 if (um.upload()) {
00251 cout << "upload completed successfully" << endl;
00252 } else {
00253 cerr << "upload failed" << endl;
00254 return false;
00255 }
00256
00257 return 0;
00258 }