00001 #include "asebaros.h"
00002
00003 #include <aseba/compiler/compiler.h>
00004
00005 #include <ros/console.h>
00006
00007 #include <vector>
00008 #include <sstream>
00009 #include <boost/format.hpp>
00010
00011 #include <libxml/parser.h>
00012 #include <libxml/tree.h>
00013
00014
00015 using namespace asebaros;
00016 using namespace std;
00017 using namespace boost;
00018 using namespace Dashel;
00019 using namespace Aseba;
00020
00021
00022 std::wstring widen(const char *src)
00023 {
00024 const size_t destSize(mbstowcs(0, src, 0)+1);
00025 std::vector<wchar_t> buffer(destSize, 0);
00026 mbstowcs(&buffer[0], src, destSize);
00027 return std::wstring(buffer.begin(), buffer.end() - 1);
00028 }
00029 std::wstring widen(const std::string& src)
00030 {
00031 return widen(src.c_str());
00032 }
00033
00034
00035 std::string narrow(const wchar_t* src)
00036 {
00037 const size_t destSize(wcstombs(0, src, 0)+1);
00038 std::vector<char> buffer(destSize, 0);
00039 wcstombs(&buffer[0], src, destSize);
00040 return std::string(buffer.begin(), buffer.end() - 1);
00041 }
00042 std::string narrow(const std::wstring& src)
00043 {
00044 return narrow(src.c_str());
00045 }
00046
00047
00048
00049
00050 AsebaDashelHub::AsebaDashelHub(AsebaROS* asebaROS, unsigned port, bool forward):
00051 Dashel::Hub(),
00052 asebaROS(asebaROS),
00053 forward(forward)
00054 {
00055 ostringstream oss;
00056 oss << "tcpin:port=" << port;
00057 Dashel::Hub::connect(oss.str());
00058 }
00059
00060 static wstring asebaMsgToString(Message *message)
00061 {
00062 wostringstream oss;
00063 message->dump(oss);
00064 return oss.str();
00065 }
00066
00067 void AsebaDashelHub::sendMessage(Message *message, bool doLock, Stream* sourceStream)
00068 {
00069
00070
00071 ROS_DEBUG_STREAM("sending aseba message: " << narrow(asebaMsgToString(message)));
00072
00073
00074 if (doLock)
00075 lock();
00076
00077
00078 for (StreamsSet::iterator it = dataStreams.begin(); it != dataStreams.end();++it)
00079 {
00080 Stream* destStream(*it);
00081
00082 if ((forward) && (destStream == sourceStream))
00083 continue;
00084
00085 try
00086 {
00087 message->serialize(destStream);
00088 destStream->flush();
00089 }
00090 catch (DashelException e)
00091 {
00092
00093 ROS_ERROR("error while writing message");
00094 }
00095 }
00096
00097 if (doLock)
00098 unlock();
00099 }
00100
00101 void AsebaDashelHub::operator()()
00102 {
00103 Hub::run();
00104
00105 ros::shutdown();
00106 }
00107
00108 void AsebaDashelHub::startThread()
00109 {
00110 thread = new boost::thread(ref(*this));
00111 }
00112
00113 void AsebaDashelHub::stopThread()
00114 {
00115 Hub::stop();
00116 thread->join();
00117 delete thread;
00118 thread = 0;
00119 }
00120
00121
00122
00123 void AsebaDashelHub::incomingData(Stream *stream)
00124 {
00125
00126 Message *message = 0;
00127 try
00128 {
00129 message = Message::receive(stream);
00130 }
00131 catch (DashelException e)
00132 {
00133
00134 ROS_ERROR("error while writing message");
00135 }
00136
00137
00138 sendMessage(message, false, stream);
00139
00140
00141 asebaROS->processAsebaMessage(message);
00142
00143
00144 delete message;
00145 }
00146
00147 void AsebaDashelHub::connectionCreated(Stream *stream)
00148 {
00149 ROS_INFO_STREAM("Incoming connection from " << stream->getTargetName());
00150
00151 if (dataStreams.size() == 1)
00152 {
00153
00154
00155
00156 GetDescription getDescription;
00157 sendMessage(&getDescription, false);
00158 }
00159 }
00160
00161 void AsebaDashelHub::connectionClosed(Stream* stream, bool abnormal)
00162 {
00163 if (abnormal)
00164 ROS_INFO_STREAM("Abnormal connection closed to " << stream->getTargetName() << " : " << stream->getFailReason());
00165 else
00166 ROS_INFO_STREAM("Normal connection closed to " << stream->getTargetName());
00167 }
00168
00169
00170
00171
00172 bool AsebaROS::loadScript(LoadScripts::Request& req, LoadScripts::Response& res)
00173 {
00174
00175
00176
00177 const string& fileName(req.fileName);
00178 xmlDoc *doc = xmlReadFile(fileName.c_str(), NULL, 0);
00179 if (!doc)
00180 {
00181 ROS_ERROR_STREAM("Cannot read XML from file " << fileName);
00182 return false;
00183 }
00184 xmlNode *domRoot = xmlDocGetRootElement(doc);
00185
00186
00187 mutex.lock();
00188 commonDefinitions.events.clear();
00189 commonDefinitions.constants.clear();
00190 userDefinedVariablesMap.clear();
00191 pubs.clear();
00192 subs.clear();
00193 mutex.unlock();
00194
00195
00196 int noNodeCount = 0;
00197 bool wasError = false;
00198 if (!xmlStrEqual(domRoot->name, BAD_CAST("network")))
00199 {
00200 ROS_ERROR("root node is not \"network\", XML considered as invalid");
00201 wasError = true;
00202 }
00203 else for (xmlNode *domNode = xmlFirstElementChild(domRoot); domNode; domNode = domNode->next)
00204 {
00205
00206 if (domNode->type == XML_ELEMENT_NODE)
00207 {
00208 if (xmlStrEqual(domNode->name, BAD_CAST("node")))
00209 {
00210
00211 xmlChar *name = xmlGetProp(domNode, BAD_CAST("name"));
00212 if (!name)
00213 ROS_WARN("missing \"name\" attribute in \"node\" entry");
00214 else
00215 {
00216 const string _name((const char *)name);
00217 xmlChar * text = xmlNodeGetContent(domNode);
00218 if (!text)
00219 ROS_WARN("missing text in \"node\" entry");
00220 else
00221 {
00222
00223 unsigned preferedId(0);
00224 xmlChar *storedId = xmlGetProp(domNode, BAD_CAST("nodeId"));
00225 if (storedId)
00226 preferedId = unsigned(atoi((char*)storedId));
00227 mutex.lock();
00228 bool ok;
00229 unsigned nodeId(DescriptionsManager::getNodeId(widen(_name), preferedId, &ok));
00230 mutex.unlock();
00231 if (ok)
00232 {
00233 mutex.lock();
00234
00235 std::wistringstream is(widen((const char *)text));
00236 Error error;
00237 BytecodeVector bytecode;
00238 unsigned allocatedVariablesCount;
00239
00240 Compiler compiler;
00241 compiler.setTargetDescription(getDescription(nodeId));
00242 compiler.setCommonDefinitions(&commonDefinitions);
00243 bool result = compiler.compile(is, bytecode, allocatedVariablesCount, error);
00244 mutex.unlock();
00245
00246 if (result)
00247 {
00248 typedef std::vector<Message*> MessageVector;
00249 MessageVector messages;
00250 sendBytecode(messages, nodeId, std::vector<uint16>(bytecode.begin(), bytecode.end()));
00251 for (MessageVector::const_iterator it = messages.begin(); it != messages.end(); ++it)
00252 {
00253 hub.sendMessage(*it, true);
00254 delete *it;
00255 }
00256 Run msg(nodeId);
00257 hub.sendMessage(&msg, true);
00258
00259 mutex.lock();
00260 userDefinedVariablesMap[_name] = *compiler.getVariablesMap();
00261 mutex.unlock();
00262 }
00263 else
00264 {
00265 ROS_ERROR_STREAM("compilation of " << fileName << ", node " << _name << " failed: " << narrow(error.toWString()));
00266 wasError = true;
00267 }
00268 }
00269 else
00270 noNodeCount++;
00271
00272
00273 xmlFree(text);
00274 }
00275 xmlFree(name);
00276 }
00277 }
00278 else if (xmlStrEqual(domNode->name, BAD_CAST("event")))
00279 {
00280
00281 xmlChar *name = xmlGetProp(domNode, BAD_CAST("name"));
00282 if (!name)
00283 ROS_WARN("missing \"name\" attribute in \"event\" entry");
00284 xmlChar *size = xmlGetProp(domNode, BAD_CAST("size"));
00285 if (!size)
00286 ROS_WARN("missing \"size\" attribute in \"event\" entry");
00287
00288 if (name && size)
00289 {
00290 int eventSize(atoi((const char *)size));
00291 if (eventSize > ASEBA_MAX_EVENT_ARG_SIZE)
00292 {
00293 ROS_ERROR("Event %s has a length %d larger than maximum %d", name, eventSize, ASEBA_MAX_EVENT_ARG_SIZE);
00294 wasError = true;
00295 break;
00296 }
00297 else
00298 {
00299 lock_guard<boost::mutex> lock(mutex);
00300 commonDefinitions.events.push_back(NamedValue(widen((const char *)name), eventSize));
00301 }
00302 }
00303
00304 if (name)
00305 xmlFree(name);
00306 if (size)
00307 xmlFree(size);
00308 }
00309 else if (xmlStrEqual(domNode->name, BAD_CAST("constant")))
00310 {
00311
00312 xmlChar *name = xmlGetProp(domNode, BAD_CAST("name"));
00313 if (!name)
00314 ROS_WARN("missing \"name\" attribute in \"constant\" entry");
00315 xmlChar *value = xmlGetProp(domNode, BAD_CAST("value"));
00316 if (!value)
00317 ROS_WARN("missing \"value\" attribute in \"constant\" entry");
00318
00319 if (name && value)
00320 {
00321 lock_guard<boost::mutex> lock(mutex);
00322 commonDefinitions.constants.push_back(NamedValue(widen((const char *)name), atoi((const char *)value)));
00323 }
00324
00325 if (name)
00326 xmlFree(name);
00327 if (value)
00328 xmlFree(value);
00329 }
00330 else
00331 ROS_WARN_STREAM("Unknown XML node seen in .aesl file: " << domNode->name);
00332 }
00333 }
00334
00335
00336 xmlFreeDoc(doc);
00337
00338
00339 if (wasError)
00340 {
00341 ROS_ERROR_STREAM("There was an error while loading script " << fileName);
00342 mutex.lock();
00343 commonDefinitions.events.clear();
00344 commonDefinitions.constants.clear();
00345 userDefinedVariablesMap.clear();
00346 mutex.unlock();
00347 }
00348
00349
00350 if (noNodeCount)
00351 {
00352 ROS_WARN_STREAM(noNodeCount << " scripts have no corresponding nodes in the current network and have not been loaded.");
00353 }
00354
00355
00356 mutex.lock();
00357 typedef EventsDescriptionsVector::const_iterator EventsDescriptionsConstIt;
00358 for (size_t i = 0; i < commonDefinitions.events.size(); ++i)
00359 {
00360 const wstring& name(commonDefinitions.events[i].name);
00361 pubs.push_back(n.advertise<AsebaEvent>(narrow(L"events/"+name), 100));
00362 subs.push_back(n.subscribe<AsebaEvent>(narrow(L"events/"+name), 100, bind(&AsebaROS::knownEventReceived, this, i, _1)));
00363 }
00364 mutex.unlock();
00365
00366 return true;
00367 }
00368
00369 bool AsebaROS::getNodeList(GetNodeList::Request& req, GetNodeList::Response& res)
00370 {
00371 lock_guard<boost::mutex> lock(mutex);
00372
00373 transform(nodesNames.begin(), nodesNames.end(), back_inserter(res.nodeList), bind(&NodesNamesMap::value_type::first,_1));
00374 return true;
00375 }
00376
00377 bool AsebaROS::getNodeId(GetNodeId::Request& req, GetNodeId::Response& res)
00378 {
00379 lock_guard<boost::mutex> lock(mutex);
00380
00381 NodesNamesMap::const_iterator nodeIt(nodesNames.find(req.nodeName));
00382 if (nodeIt != nodesNames.end())
00383 {
00384 res.nodeId = nodeIt->second;
00385 return true;
00386 }
00387 else
00388 {
00389 ROS_ERROR_STREAM("node " << req.nodeName << " does not exists");
00390 return false;
00391 }
00392 }
00393
00394 bool AsebaROS::getNodeName(GetNodeName::Request& req, GetNodeName::Response& res)
00395 {
00396 lock_guard<boost::mutex> lock(mutex);
00397
00398 NodesDescriptionsMap::const_iterator nodeIt(nodesDescriptions.find(req.nodeId));
00399 if (nodeIt != nodesDescriptions.end())
00400 {
00401 res.nodeName = narrow(nodeIt->second.name);
00402 return true;
00403 }
00404 else
00405 {
00406 ROS_ERROR_STREAM("node " << req.nodeId << " does not exists");
00407 return false;
00408 }
00409 }
00410
00411 struct ExtractNameVar
00412 {
00413 string operator()(const std::pair<std::wstring, std::pair<unsigned, unsigned> > p) const { return narrow(p.first); }
00414 };
00415
00416 struct ExtractNameDesc
00417 {
00418 string operator()(const TargetDescription::NamedVariable& nv) const { return narrow(nv.name); }
00419 };
00420
00421 bool AsebaROS::getVariableList(GetVariableList::Request& req, GetVariableList::Response& res)
00422 {
00423 lock_guard<boost::mutex> lock(mutex);
00424
00425 NodesNamesMap::const_iterator nodeIt(nodesNames.find(req.nodeName));
00426 if (nodeIt != nodesNames.end())
00427 {
00428
00429 const UserDefinedVariablesMap::const_iterator userVarMapIt(userDefinedVariablesMap.find(req.nodeName));
00430 if (userVarMapIt != userDefinedVariablesMap.end())
00431 {
00432
00433 const Compiler::VariablesMap& variablesMap(userVarMapIt->second);
00434 transform(variablesMap.begin(), variablesMap.end(),
00435 back_inserter(res.variableList), ExtractNameVar());
00436 }
00437 else
00438 {
00439
00440 const unsigned nodeId(nodeIt->second);
00441 const NodesDescriptionsMap::const_iterator descIt(nodesDescriptions.find(nodeId));
00442 const NodeDescription& description(descIt->second);
00443 transform(description.namedVariables.begin(), description.namedVariables.end(),
00444 back_inserter(res.variableList), ExtractNameDesc());
00445 }
00446 return true;
00447 }
00448 else
00449 {
00450 ROS_ERROR_STREAM("node " << req.nodeName << " does not exists");
00451 return false;
00452 }
00453 }
00454
00455
00456
00457 bool AsebaROS::setVariable(SetVariable::Request& req, SetVariable::Response& res)
00458 {
00459
00460 unsigned nodeId, pos;
00461
00462 mutex.lock();
00463 bool exists = getNodePosFromNames(req.nodeName, req.variableName, nodeId, pos);
00464 mutex.unlock();
00465
00466 if (!exists)
00467 return false;
00468
00469 SetVariables msg(nodeId, pos, req.data);
00470 hub.sendMessage(&msg, true);
00471 return true;
00472 }
00473
00474 bool AsebaROS::getVariable(GetVariable::Request& req, GetVariable::Response& res)
00475 {
00476 unsigned nodeId, pos;
00477
00478
00479 unique_lock<boost::mutex> lock(mutex);
00480
00481
00482 bool exists = getNodePosFromNames(req.nodeName, req.variableName, nodeId, pos);
00483 if (!exists)
00484 return false;
00485 bool ok;
00486 unsigned length = getVariableSize(nodeId, widen(req.variableName), &ok);
00487 if (!ok)
00488 return false;
00489
00490
00491 const GetVariableQueryKey key(nodeId, pos);
00492 GetVariableQueryValue query;
00493 getVariableQueries[key] = &query;
00494 lock.unlock();
00495
00496
00497 GetVariables msg(nodeId, pos, length);
00498 hub.sendMessage(&msg, true);
00499 system_time const timeout(get_system_time()+posix_time::milliseconds(100));
00500
00501
00502 bool result;
00503 lock.lock();
00504 while (query.data.empty())
00505 {
00506 result = query.cond.timed_wait(lock, timeout);
00507 if (!result)
00508 break;
00509 }
00510
00511
00512 getVariableQueries.erase(key);
00513 if (result)
00514 {
00515 res.data = query.data;
00516 return true;
00517 }
00518 else
00519 {
00520 ROS_ERROR_STREAM("read of node " << req.nodeName << ", variable " << req.variableName << " did not return a valid answer within 100ms");
00521 return false;
00522 }
00523 }
00524
00525 bool AsebaROS::getEventId(GetEventId::Request& req, GetEventId::Response& res)
00526 {
00527
00528 lock_guard<boost::mutex> lock(mutex);
00529 size_t id;
00530 if (commonDefinitions.events.contains(widen(req.name), &id))
00531 {
00532 res.id = id;
00533 return true;
00534 }
00535 return false;
00536 }
00537
00538 bool AsebaROS::getEventName(GetEventName::Request& req, GetEventName::Response& res)
00539 {
00540
00541 lock_guard<boost::mutex> lock(mutex);
00542 if (req.id < commonDefinitions.events.size())
00543 {
00544 res.name = narrow(commonDefinitions.events[req.id].name);
00545 return true;
00546 }
00547 return false;
00548 }
00549
00550 bool AsebaROS::getNodePosFromNames(const string& nodeName, const string& variableName, unsigned& nodeId, unsigned& pos) const
00551 {
00552
00553
00554
00555 NodesNamesMap::const_iterator nodeIt(nodesNames.find(nodeName));
00556 if (nodeIt == nodesNames.end())
00557 {
00558 ROS_ERROR_STREAM("node " << nodeName << " does not exists");
00559 return false;
00560 }
00561 nodeId = nodeIt->second;
00562 pos = unsigned(-1);
00563
00564
00565 const UserDefinedVariablesMap::const_iterator userVarMapIt(userDefinedVariablesMap.find(nodeName));
00566 if (userVarMapIt != userDefinedVariablesMap.end())
00567 {
00568 const Compiler::VariablesMap& userVarMap(userVarMapIt->second);
00569 const Compiler::VariablesMap::const_iterator userVarIt(userVarMap.find(widen(variableName)));
00570 if (userVarIt != userVarMap.end())
00571 {
00572 pos = userVarIt->second.first;
00573 }
00574 }
00575
00576
00577 if (pos == unsigned(-1))
00578 {
00579 bool ok;
00580 pos = getVariablePos(nodeId, widen(variableName), &ok);
00581 if (!ok)
00582 {
00583 ROS_ERROR_STREAM("variable " << variableName << " does not exists in node " << nodeName);
00584 return false;
00585 }
00586 }
00587 return true;
00588 }
00589
00590 void AsebaROS::sendEventOnROS(const UserMessage* asebaMessage)
00591 {
00592
00593 if ((pubs.size() == commonDefinitions.events.size()) &&
00594 (asebaMessage->type < commonDefinitions.events.size()))
00595 {
00596
00597 shared_ptr<AsebaEvent> event(new AsebaEvent);
00598 event->stamp = ros::Time::now();
00599 event->source = asebaMessage->source;
00600 event->data = asebaMessage->data;
00601 pubs[asebaMessage->type].publish(event);
00602 }
00603 else
00604 {
00605
00606 shared_ptr<AsebaAnonymousEvent> event(new AsebaAnonymousEvent);
00607 event->stamp = ros::Time::now();
00608 event->source = asebaMessage->source;
00609 event->type = asebaMessage->type;
00610 event->data = asebaMessage->data;
00611 anonPub.publish(event);
00612 }
00613 }
00614
00615 void AsebaROS::nodeDescriptionReceived(unsigned nodeId)
00616 {
00617
00618 nodesNames[narrow(nodesDescriptions.at(nodeId).name)] = nodeId;
00619 }
00620
00621 void AsebaROS::eventReceived(const AsebaAnonymousEventConstPtr& event)
00622 {
00623
00624 if (event->source == 0)
00625 {
00626
00627 UserMessage userMessage(event->type, event->data);
00628 hub.sendMessage(&userMessage, true);
00629 }
00630 }
00631
00632 void AsebaROS::knownEventReceived(const uint16 id, const AsebaEventConstPtr& event)
00633 {
00634
00635 if (event->source == 0)
00636 {
00637
00638 UserMessage userMessage(id, event->data);
00639 hub.sendMessage(&userMessage, true);
00640 }
00641 }
00642
00643 AsebaROS::AsebaROS(unsigned port, bool forward):
00644 n("aseba"),
00645 anonPub(n.advertise<AsebaAnonymousEvent>("anonymous_events", 100)),
00646 anonSub(n.subscribe("anonymous_events", 100, &AsebaROS::eventReceived, this)),
00647 hub(this, port, forward)
00648 {
00649
00650
00651
00652 s.push_back(n.advertiseService("load_script", &AsebaROS::loadScript, this));
00653
00654
00655 s.push_back(n.advertiseService("get_node_list", &AsebaROS::getNodeList, this));
00656 s.push_back(n.advertiseService("get_node_id", &AsebaROS::getNodeId, this));
00657 s.push_back(n.advertiseService("get_node_name", &AsebaROS::getNodeName, this));
00658
00659
00660 s.push_back(n.advertiseService("get_variable_list", &AsebaROS::getVariableList, this));
00661 s.push_back(n.advertiseService("set_variable", &AsebaROS::setVariable, this));
00662 s.push_back(n.advertiseService("get_variable", &AsebaROS::getVariable, this));
00663
00664
00665 s.push_back(n.advertiseService("get_event_id", &AsebaROS::getEventId, this));
00666 s.push_back(n.advertiseService("get_event_name", &AsebaROS::getEventName, this));
00667 }
00668
00669 AsebaROS::~AsebaROS()
00670 {
00671
00672 xmlCleanupParser();
00673 }
00674
00675 void AsebaROS::run()
00676 {
00677
00678 hub.startThread();
00679 ros::spin();
00680
00681 hub.stopThread();
00682 }
00683
00684 void AsebaROS::processAsebaMessage(Message *message)
00685 {
00686
00687 lock_guard<boost::mutex> lock(mutex);
00688
00689
00690 DescriptionsManager::processMessage(message);
00691
00692
00693 UserMessage *userMessage = dynamic_cast<UserMessage *>(message);
00694 if (userMessage)
00695 sendEventOnROS(userMessage);
00696
00697
00698 Variables *variables = dynamic_cast<Variables *>(message);
00699 if (variables)
00700 {
00701 const GetVariableQueryKey queryKey(variables->source, variables->start);
00702 GetVariableQueryMap::const_iterator queryIt(getVariableQueries.find(queryKey));
00703 if (queryIt != getVariableQueries.end())
00704 {
00705 queryIt->second->data = variables->variables;
00706 queryIt->second->cond.notify_one();
00707 }
00708 else
00709 ROS_WARN_STREAM("received Variables from node " << variables->source << ", pos " << variables->start << ", but no corresponding query was found");
00710 }
00711 }
00712
00714 void dumpHelp(std::ostream &stream, const char *programName)
00715 {
00716 stream << "AsebaROS, connects aseba components together and with ROS, usage:\n";
00717 stream << programName << " [options] [additional targets]*\n";
00718 stream << "Options:\n";
00719 stream << "-l, --loop : makes the switch transmit messages back to the send, not only forward them.\n";
00720 stream << "-p port : listens to incoming connection on this port\n";
00721 stream << "-h, --help : shows this help\n";
00722 stream << "ROS_OPTIONS : see ROS documentation\n";
00723 stream << "Additional targets are any valid Dashel targets." << std::endl;
00724 }
00725
00726 int main(int argc, char *argv[])
00727 {
00728 ros::init(argc, argv, "aseba");
00729
00730 unsigned port = ASEBA_DEFAULT_PORT;
00731 bool forward = true;
00732 std::vector<std::string> additionalTargets;
00733
00734 int argCounter = 1;
00735
00736 while (argCounter < argc)
00737 {
00738 const char *arg = argv[argCounter];
00739
00740 if ((strcmp(arg, "-l") == 0) || (strcmp(arg, "--loop") == 0))
00741 {
00742 forward = false;
00743 }
00744 else if (strcmp(arg, "-p") == 0)
00745 {
00746 arg = argv[++argCounter];
00747 port = atoi(arg);
00748 }
00749 else if ((strcmp(arg, "-h") == 0) || (strcmp(arg, "--help") == 0))
00750 {
00751 dumpHelp(std::cout, argv[0]);
00752 return 0;
00753 }
00754 else
00755 {
00756 additionalTargets.push_back(argv[argCounter]);
00757 }
00758 argCounter++;
00759 }
00760
00761 AsebaROS asebaROS(port, forward);
00762
00763 try
00764 {
00765 for (size_t i = 0; i < additionalTargets.size(); i++)
00766 asebaROS.connectTarget(additionalTargets[i]);
00767 }
00768 catch(Dashel::DashelException e)
00769 {
00770 std::cerr << e.what() << std::endl;
00771 }
00772
00773 asebaROS.run();
00774
00775 return 0;
00776 }
00777
00778
00779