00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <QCoreApplication>
00025 #include <cstdlib>
00026 #include <cstring>
00027 #include <iostream>
00028 #include <sstream>
00029 #include <set>
00030 #include <valarray>
00031 #include <vector>
00032 #include <iterator>
00033 #include "medulla.h"
00034 #include "medulla.moc"
00035 #include "../common/consts.h"
00036 #include "../common/types.h"
00037 #include "../utils/utils.h"
00038 #include <QDBusMessage>
00039 #include <QDBusMetaType>
00040 #include <QtXml>
00041 #include <QtDebug>
00042
00043 #if DASHEL_VERSION_INT < 10003
00044 # error "You need at least Dashel version 1.0.3 to compile Medulla"
00045 #endif // DAHSEL_VERSION_INT
00046
00047 namespace Aseba
00048 {
00049 using namespace std;
00050 using namespace Dashel;
00051
00054
00055 std::vector<sint16> toAsebaVector(const Values& values)
00056 {
00057 std::vector<sint16> data;
00058 data.reserve(values.size());
00059 for (int i = 0; i < values.size(); ++i)
00060 data.push_back(values[i]);
00061 return data;
00062 }
00063
00064 Values fromAsebaVector(const std::vector<sint16>& values)
00065 {
00066 Values data;
00067 for (size_t i = 0; i < values.size(); ++i)
00068 data.push_back(values[i]);
00069 return data;
00070 }
00071
00072 void EventFilterInterface::emitEvent(const quint16 id, const QString& name, const Values& data)
00073 {
00074 emit Event(id, name, data);
00075 }
00076
00077 void EventFilterInterface::ListenEvent(const quint16 event)
00078 {
00079 network->listenEvent(this, event);
00080 }
00081
00082 void EventFilterInterface::ListenEventName(const QString& name, const QDBusMessage &message)
00083 {
00084 size_t event;
00085 if (network->commonDefinitions.events.contains(name.toStdString(), &event))
00086 network->listenEvent(this, event);
00087 else
00088 network->DBusConnectionBus().send(message.createErrorReply(QDBusError::InvalidArgs, QString("no event named %0").arg(name)));
00089 }
00090
00091 void EventFilterInterface::IgnoreEvent(const quint16 event)
00092 {
00093 network->ignoreEvent(this, event);
00094 }
00095
00096 void EventFilterInterface::IgnoreEventName(const QString& name, const QDBusMessage &message)
00097 {
00098 size_t event;
00099 if (network->commonDefinitions.events.contains(name.toStdString(), &event))
00100 network->ignoreEvent(this, event);
00101 else
00102 network->DBusConnectionBus().send(message.createErrorReply(QDBusError::InvalidArgs, QString("no event named %0").arg(name)));
00103 }
00104
00105 void EventFilterInterface::Free()
00106 {
00107 network->filterDestroyed(this);
00108 deleteLater();
00109 }
00110
00111 AsebaNetworkInterface::AsebaNetworkInterface(Hub* hub, bool systemBus) :
00112 QDBusAbstractAdaptor(hub),
00113 hub(hub),
00114 systemBus(systemBus),
00115 eventsFiltersCounter(0)
00116 {
00117 qDBusRegisterMetaType<Values>();
00118
00119
00120 DBusConnectionBus().registerObject("/", hub);
00121 DBusConnectionBus().registerService("ch.epfl.mobots.Aseba");
00122 }
00123
00124 void AsebaNetworkInterface::processMessage(Message *message, Dashel::Stream* sourceStream)
00125 {
00126
00127 hub->sendMessage(message, sourceStream);
00128
00129
00130 DescriptionsManager::processMessage(message);
00131
00132
00133 UserMessage *userMessage = dynamic_cast<UserMessage *>(message);
00134 if (userMessage)
00135 {
00136 sendEventOnDBus(userMessage->type, fromAsebaVector(userMessage->data));
00137 }
00138
00139
00140 Variables *variables = dynamic_cast<Variables *>(message);
00141 if (variables)
00142 {
00143 const unsigned nodeId(variables->source);
00144 const unsigned pos(variables->start);
00145 for (RequestsList::iterator it = pendingReads.begin(); it != pendingReads.end(); ++it)
00146 {
00147 RequestData* request(*it);
00148 if (request->nodeId == nodeId && request->pos == pos)
00149 {
00150 QDBusMessage &reply(request->reply);
00151 Values values(fromAsebaVector(variables->variables));
00152 reply << QVariant::fromValue(values);
00153 DBusConnectionBus().send(reply);
00154 delete request;
00155 pendingReads.erase(it);
00156 break;
00157 }
00158 }
00159 }
00160
00161 delete message;
00162 }
00163
00164 void AsebaNetworkInterface::sendEventOnDBus(const quint16 event, const Values& data)
00165 {
00166 QList<EventFilterInterface*> filters = eventsFilters.values(event);
00167 QString name;
00168 if (event < commonDefinitions.events.size())
00169 name = QString::fromStdString(commonDefinitions.events[event].name);
00170 else
00171 name = "?";
00172 for (int i = 0; i < filters.size(); ++i)
00173 filters.at(i)->emitEvent(event, name, data);
00174 }
00175
00176 void AsebaNetworkInterface::listenEvent(EventFilterInterface* filter, quint16 event)
00177 {
00178 eventsFilters.insert(event, filter);
00179 }
00180
00181 void AsebaNetworkInterface::ignoreEvent(EventFilterInterface* filter, quint16 event)
00182 {
00183 eventsFilters.remove(event, filter);
00184 }
00185
00186 void AsebaNetworkInterface::filterDestroyed(EventFilterInterface* filter)
00187 {
00188 QList<quint16> events = eventsFilters.keys(filter);
00189 for (int i = 0; i < events.size(); ++i)
00190 eventsFilters.remove(events.at(i), filter);
00191 }
00192
00193 void AsebaNetworkInterface::LoadScripts(const QString& fileName, const QDBusMessage &message)
00194 {
00195 QFile file(fileName);
00196 if (!file.open(QFile::ReadOnly))
00197 {
00198 DBusConnectionBus().send(message.createErrorReply(QDBusError::InvalidArgs, QString("file %0 does not exists").arg(fileName)));
00199 return;
00200 }
00201
00202 QDomDocument document("aesl-source");
00203 QString errorMsg;
00204 int errorLine;
00205 int errorColumn;
00206 if (!document.setContent(&file, false, &errorMsg, &errorLine, &errorColumn))
00207 {
00208 DBusConnectionBus().send(message.createErrorReply(QDBusError::Other, QString("Error in XML source file: %0 at line %1, column %2").arg(errorMsg).arg(errorLine).arg(errorColumn)));
00209 return;
00210 }
00211
00212 commonDefinitions.events.clear();
00213 commonDefinitions.constants.clear();
00214 userDefinedVariablesMap.clear();
00215
00216 int noNodeCount = 0;
00217 QDomNode domNode = document.documentElement().firstChild();
00218
00219
00220 bool wasError = false;
00221 while (!domNode.isNull())
00222 {
00223 if (domNode.isElement())
00224 {
00225 QDomElement element = domNode.toElement();
00226 if (element.tagName() == "node")
00227 {
00228 bool ok;
00229 unsigned nodeId(getNodeId(element.attribute("name").toStdString(), &ok));
00230 if (ok)
00231 {
00232 std::istringstream is(element.firstChild().toText().data().toStdString());
00233 Error error;
00234 BytecodeVector bytecode;
00235 unsigned allocatedVariablesCount;
00236
00237 Compiler compiler;
00238 compiler.setTargetDescription(getDescription(nodeId));
00239 compiler.setCommonDefinitions(&commonDefinitions);
00240 bool result = compiler.compile(is, bytecode, allocatedVariablesCount, error);
00241
00242 if (result)
00243 {
00244 typedef std::vector<Message*> MessageVector;
00245 MessageVector messages;
00246 sendBytecode(messages, nodeId, std::vector<uint16>(bytecode.begin(), bytecode.end()));
00247 for (MessageVector::const_iterator it = messages.begin(); it != messages.end(); ++it)
00248 {
00249 hub->sendMessage(*it);
00250 delete *it;
00251 }
00252 Run msg(nodeId);
00253 hub->sendMessage(msg);
00254 }
00255 else
00256 {
00257 DBusConnectionBus().send(message.createErrorReply(QDBusError::Failed, QString::fromStdString(error.toString())));
00258 wasError = true;
00259 break;
00260 }
00261
00262 userDefinedVariablesMap[element.attribute("name")] = *compiler.getVariablesMap();
00263 }
00264 else
00265 noNodeCount++;
00266 }
00267 else if (element.tagName() == "event")
00268 {
00269 const QString eventName(element.attribute("name"));
00270 const unsigned eventSize(element.attribute("size").toUInt());
00271 if (eventSize > ASEBA_MAX_EVENT_ARG_SIZE)
00272 {
00273 DBusConnectionBus().send(message.createErrorReply(QDBusError::Failed, QString("Event %1 has a length %2 larger than maximum %3").arg(eventName).arg(eventSize).arg(ASEBA_MAX_EVENT_ARG_SIZE)));
00274 wasError = true;
00275 break;
00276 }
00277 else
00278 {
00279 commonDefinitions.events.push_back(NamedValue(eventName.toStdString(), eventSize));
00280 }
00281 }
00282 else if (element.tagName() == "constant")
00283 {
00284 commonDefinitions.constants.push_back(NamedValue(element.attribute("name").toStdString(), element.attribute("value").toUInt()));
00285 }
00286 }
00287 domNode = domNode.nextSibling();
00288 }
00289
00290
00291 if (wasError)
00292 {
00293 std::cerr << QString("There was an error while loading script %1").arg(fileName).toStdString() << std::endl;
00294 commonDefinitions.events.clear();
00295 commonDefinitions.constants.clear();
00296 userDefinedVariablesMap.clear();
00297 }
00298
00299
00300 if (noNodeCount)
00301 {
00302 std::cerr << QString("%0 scripts have no corresponding nodes in the current network and have not been loaded.").arg(noNodeCount).toStdString() << std::endl;
00303 }
00304 }
00305
00306 QStringList AsebaNetworkInterface::GetNodesList() const
00307 {
00308 QStringList list;
00309 for (NodesNamesMap::const_iterator it = nodesNames.begin(); it != nodesNames.end(); ++it)
00310 {
00311 list.push_back(it.key());
00312 }
00313 return list;
00314 }
00315
00316 qint16 AsebaNetworkInterface::GetNodeId(const QString& node, const QDBusMessage &message) const
00317 {
00318 NodesNamesMap::const_iterator nodeIt(nodesNames.find(node));
00319 if (nodeIt == nodesNames.end())
00320 {
00321 DBusConnectionBus().send(message.createErrorReply(QDBusError::InvalidArgs, QString("node %0 does not exists").arg(node)));
00322 return 0;
00323 }
00324 return nodeIt.value();
00325 }
00326
00327 QStringList AsebaNetworkInterface::GetVariablesList(const QString& node) const
00328 {
00329 NodesNamesMap::const_iterator it(nodesNames.find(node));
00330 if (it != nodesNames.end())
00331 {
00332
00333 const unsigned nodeId(it.value());
00334 const NodesDescriptionsMap::const_iterator descIt(nodesDescriptions.find(nodeId));
00335 const NodeDescription& description(descIt->second);
00336 QStringList list;
00337 for (size_t i = 0; i < description.namedVariables.size(); ++i)
00338 {
00339 list.push_back(QString::fromStdString(description.namedVariables[i].name));
00340 }
00341
00342
00343 const UserDefinedVariablesMap::const_iterator userVarMapIt(userDefinedVariablesMap.find(node));
00344 if (userVarMapIt != userDefinedVariablesMap.end())
00345 {
00346 const Compiler::VariablesMap& variablesMap(*userVarMapIt);
00347 for (Compiler::VariablesMap::const_iterator jt = variablesMap.begin(); jt != variablesMap.end(); ++jt)
00348 {
00349 list.push_back(QString::fromStdString(jt->first));
00350 }
00351 }
00352
00353 return list;
00354 }
00355 else
00356 {
00357 return QStringList();
00358 }
00359 }
00360
00361 void AsebaNetworkInterface::SetVariable(const QString& node, const QString& variable, const Values& data, const QDBusMessage &message) const
00362 {
00363
00364 NodesNamesMap::const_iterator nodeIt(nodesNames.find(node));
00365 if (nodeIt == nodesNames.end())
00366 {
00367 DBusConnectionBus().send(message.createErrorReply(QDBusError::InvalidArgs, QString("node %0 does not exists").arg(node)));
00368 return;
00369 }
00370 const unsigned nodeId(nodeIt.value());
00371
00372 unsigned pos(unsigned(-1));
00373
00374
00375 const UserDefinedVariablesMap::const_iterator userVarMapIt(userDefinedVariablesMap.find(node));
00376 if (userVarMapIt != userDefinedVariablesMap.end())
00377 {
00378 const Compiler::VariablesMap& userVarMap(userVarMapIt.value());
00379 const Compiler::VariablesMap::const_iterator userVarIt(userVarMap.find(variable.toStdString()));
00380 if (userVarIt != userVarMap.end())
00381 {
00382 pos = userVarIt->second.first;
00383 }
00384 }
00385
00386
00387 if (pos == unsigned(-1))
00388 {
00389 bool ok;
00390 pos = getVariablePos(nodeId, variable.toStdString(), &ok);
00391 if (!ok)
00392 {
00393 DBusConnectionBus().send(message.createErrorReply(QDBusError::InvalidArgs, QString("variable %0 does not exists in node %1").arg(variable).arg(node)));
00394 return;
00395 }
00396 }
00397
00398 SetVariables msg(nodeId, pos, toAsebaVector(data));
00399 hub->sendMessage(msg);
00400 }
00401
00402 Values AsebaNetworkInterface::GetVariable(const QString& node, const QString& variable, const QDBusMessage &message)
00403 {
00404
00405 NodesNamesMap::const_iterator nodeIt(nodesNames.find(node));
00406 if (nodeIt == nodesNames.end())
00407 {
00408 DBusConnectionBus().send(message.createErrorReply(QDBusError::InvalidArgs, QString("node %0 does not exists").arg(node)));
00409 return Values();
00410 }
00411 const unsigned nodeId(nodeIt.value());
00412
00413 unsigned pos(unsigned(-1));
00414 unsigned length(unsigned(-1));
00415
00416
00417 const UserDefinedVariablesMap::const_iterator userVarMapIt(userDefinedVariablesMap.find(node));
00418 if (userVarMapIt != userDefinedVariablesMap.end())
00419 {
00420 const Compiler::VariablesMap& userVarMap(userVarMapIt.value());
00421 const Compiler::VariablesMap::const_iterator userVarIt(userVarMap.find(variable.toStdString()));
00422 if (userVarIt != userVarMap.end())
00423 {
00424 pos = userVarIt->second.first;
00425 length = userVarIt->second.second;
00426 }
00427 }
00428
00429
00430 if (pos == unsigned(-1))
00431 {
00432 bool ok1, ok2;
00433 pos = getVariablePos(nodeId, variable.toStdString(), &ok1);
00434 length = getVariableSize(nodeId, variable.toStdString(), &ok2);
00435 if (!(ok1 && ok2))
00436 {
00437 DBusConnectionBus().send(message.createErrorReply(QDBusError::InvalidArgs, QString("variable %0 does not exists in node %1").arg(variable).arg(node)));
00438 return Values();
00439 }
00440 }
00441
00442
00443 {
00444 GetVariables msg(nodeId, pos, length);
00445 hub->sendMessage(msg);
00446 }
00447
00448
00449 RequestData *request = new RequestData;
00450 request->nodeId = nodeId;
00451 request->pos = pos;
00452 message.setDelayedReply(true);
00453 request->reply = message.createReply();
00454
00455 pendingReads.push_back(request);
00456 return Values();
00457 }
00458
00459 void AsebaNetworkInterface::SendEvent(const quint16 event, const Values& data)
00460 {
00461
00462 sendEventOnDBus(event, data);
00463
00464
00465 UserMessage msg(event, toAsebaVector(data));
00466 hub->sendMessage(msg);
00467 }
00468
00469 void AsebaNetworkInterface::SendEventName(const QString& name, const Values& data, const QDBusMessage &message)
00470 {
00471 size_t event;
00472 if (commonDefinitions.events.contains(name.toStdString(), &event))
00473 SendEvent(event, data);
00474 else
00475 DBusConnectionBus().send(message.createErrorReply(QDBusError::InvalidArgs, QString("no event named %0").arg(name)));
00476 }
00477
00478 QDBusObjectPath AsebaNetworkInterface::CreateEventFilter()
00479 {
00480 QDBusObjectPath path(QString("/events_filters/%0").arg(eventsFiltersCounter++));
00481 DBusConnectionBus().registerObject(path.path(), new EventFilterInterface(this), QDBusConnection::ExportScriptableContents);
00482 return path;
00483 }
00484
00485 void AsebaNetworkInterface::nodeDescriptionReceived(unsigned nodeId)
00486 {
00487 nodesNames[QString::fromStdString(nodesDescriptions[nodeId].name)] = nodeId;
00488 }
00489
00490 inline QDBusConnection AsebaNetworkInterface::DBusConnectionBus() const
00491 {
00492 if (systemBus)
00493 return QDBusConnection::systemBus();
00494 else
00495 return QDBusConnection::sessionBus();
00496 }
00497
00498
00499
00500 Hub::Hub(unsigned port, bool verbose, bool dump, bool forward, bool rawTime, bool systemBus) :
00501 #ifdef DASHEL_VERSION_INT
00502 Dashel::Hub(verbose || dump),
00503 #endif
00504 verbose(verbose),
00505 dump(dump),
00506 forward(forward),
00507 rawTime(rawTime)
00508 {
00509
00510 AsebaNetworkInterface* network(new AsebaNetworkInterface(this, systemBus));
00511 QObject::connect(this, SIGNAL(messageAvailable(Message*, Dashel::Stream*)), network, SLOT(processMessage(Message*, Dashel::Stream*)));
00512 QObject::connect(this, SIGNAL(firstConnectionCreated()), SLOT(firstConnectionAvailable()));
00513 ostringstream oss;
00514 oss << "tcpin:port=" << port;
00515 Dashel::Hub::connect(oss.str());
00516 }
00517
00518 void Hub::sendMessage(Message *message, Stream* sourceStream)
00519 {
00520
00521 if (dump)
00522 {
00523 dumpTime(cout, rawTime);
00524 message->dump(cout);
00525 cout << std::endl;
00526 }
00527
00528
00529 lock();
00530
00531
00532 for (StreamsSet::iterator it = dataStreams.begin(); it != dataStreams.end();++it)
00533 {
00534 Stream* destStream(*it);
00535
00536 if ((forward) && (destStream == sourceStream))
00537 continue;
00538
00539 try
00540 {
00541 message->serialize(destStream);
00542 destStream->flush();
00543 }
00544 catch (DashelException e)
00545 {
00546
00547 std::cerr << "error while writing message" << std::endl;
00548 }
00549 }
00550
00551 unlock();
00552 }
00553
00554 void Hub::sendMessage(Message& message, Dashel::Stream* sourceStream)
00555 {
00556 sendMessage(&message, sourceStream);
00557 }
00558
00559 void Hub::firstConnectionAvailable()
00560 {
00561 QTimer::singleShot(200, this, SLOT(requestDescription()));
00562 }
00563
00564 void Hub::requestDescription()
00565 {
00566 emit messageAvailable(new GetDescription(), 0);
00567 }
00568
00569
00570
00571
00572 void Hub::run()
00573 {
00574 Dashel::Hub::run();
00575 }
00576
00577
00578
00579 void Hub::incomingData(Stream *stream)
00580 {
00581
00582 Message *message;
00583 try
00584 {
00585 message = Message::receive(stream);
00586 }
00587 catch (DashelException e)
00588 {
00589
00590 std::cerr << "error while reading message" << std::endl;
00591 }
00592
00593
00594 emit messageAvailable(message, stream);
00595 }
00596
00597 void Hub::connectionCreated(Stream *stream)
00598 {
00599 if (verbose)
00600 {
00601 dumpTime(cout, rawTime);
00602 cout << "Incoming connection from " << stream->getTargetName() << endl;
00603 }
00604
00605 if (dataStreams.size() == 1)
00606 {
00607 emit firstConnectionCreated();
00608 }
00609 }
00610
00611 void Hub::connectionClosed(Stream* stream, bool abnormal)
00612 {
00613 if (verbose)
00614 {
00615 dumpTime(cout);
00616 if (abnormal)
00617 cout << "Abnormal connection closed to " << stream->getTargetName() << " : " << stream->getFailReason() << endl;
00618 else
00619 cout << "Normal connection closed to " << stream->getTargetName() << endl;
00620 }
00621 }
00622
00624 };
00625
00627 void dumpHelp(std::ostream &stream, const char *programName)
00628 {
00629 stream << "Aseba medulla, connects aseba components together and with D-Bus, usage:\n";
00630 stream << programName << " [options] [additional targets]*\n";
00631 stream << "Options:\n";
00632 stream << "-v, --verbose : makes the switch verbose\n";
00633 stream << "-d, --dump : makes the switch dump the content of messages\n";
00634 stream << "-l, --loop : makes the switch transmit messages back to the send, not only forward them.\n";
00635 stream << "-p port : listens to incoming connection on this port\n";
00636 stream << "--rawtime : shows time in the form of sec:usec since 1970\n";
00637 stream << "--system : connects medulla to the system d-bus bus\n";
00638 stream << "-h, --help : shows this help\n";
00639 stream << "Additional targets are any valid Dashel targets." << std::endl;
00640 }
00641
00642 int main(int argc, char *argv[])
00643 {
00644 QCoreApplication app(argc, argv);
00645
00646 unsigned port = ASEBA_DEFAULT_PORT;
00647 bool verbose = false;
00648 bool dump = false;
00649 bool forward = true;
00650 bool rawTime = false;
00651 bool systemBus = false;
00652 std::vector<std::string> additionalTargets;
00653
00654 int argCounter = 1;
00655
00656 while (argCounter < argc)
00657 {
00658 const char *arg = argv[argCounter];
00659
00660 if ((strcmp(arg, "-v") == 0) || (strcmp(arg, "--verbose") == 0))
00661 {
00662 verbose = true;
00663 }
00664 else if ((strcmp(arg, "-d") == 0) || (strcmp(arg, "--dump") == 0))
00665 {
00666 dump = true;
00667 }
00668 else if ((strcmp(arg, "-l") == 0) || (strcmp(arg, "--loop") == 0))
00669 {
00670 forward = false;
00671 }
00672 else if (strcmp(arg, "-p") == 0)
00673 {
00674 arg = argv[++argCounter];
00675 port = atoi(arg);
00676 }
00677 else if (strcmp(arg, "--rawtime") == 0)
00678 {
00679 rawTime = true;
00680 }
00681 else if (strcmp(arg, "--system") == 0)
00682 {
00683 systemBus = true;
00684 }
00685 else if ((strcmp(arg, "-h") == 0) || (strcmp(arg, "--help") == 0))
00686 {
00687 dumpHelp(std::cout, argv[0]);
00688 return 0;
00689 }
00690 else
00691 {
00692 additionalTargets.push_back(argv[argCounter]);
00693 }
00694 argCounter++;
00695 }
00696
00697 Aseba::Hub hub(port, verbose, dump, forward, rawTime, systemBus);
00698
00699 try
00700 {
00701 for (size_t i = 0; i < additionalTargets.size(); i++)
00702 ((Dashel::Hub&)hub).connect(additionalTargets[i]);
00703 }
00704 catch(Dashel::DashelException e)
00705 {
00706 std::cerr << e.what() << std::endl;
00707 }
00708
00709 hub.start();
00710
00711 return app.exec();
00712 }
00713
00714