$search
00001 /* 00002 Aseba - an event-based framework for distributed robot control 00003 Copyright (C) 2007--2012: 00004 Stephane Magnenat <stephane at magnenat dot net> 00005 (http://stephane.magnenat.net) 00006 and other contributors, see authors.txt for details 00007 00008 This program is free software: you can redistribute it and/or modify 00009 it under the terms of the GNU Lesser General Public License as published 00010 by the Free Software Foundation, version 3 of the License. 00011 00012 This program is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 GNU Lesser General Public License for more details. 00016 00017 You should have received a copy of the GNU Lesser General Public License 00018 along with this program. If not, see <http://www.gnu.org/licenses/>. 00019 */ 00020 00021 #include "DashelTarget.h" 00022 #include "../msg/msg.h" 00023 #include "../utils/utils.h" 00024 #include <algorithm> 00025 #include <iostream> 00026 #include <ostream> 00027 #include <sstream> 00028 #include <cassert> 00029 #include <QInputDialog> 00030 #include <QtGui> 00031 #include <QLibraryInfo> 00032 #include <stdexcept> 00033 00034 00035 #include <DashelTarget.moc> 00036 00037 #ifdef WIN32 // for Sleep 00038 #include <windows.h> 00039 #endif 00040 00041 namespace Aseba 00042 { 00043 using std::copy; 00044 using namespace Dashel; 00045 00048 00049 DashelConnectionDialog::DashelConnectionDialog() 00050 { 00051 typedef std::map<int, std::pair<std::string, std::string> > PortsMap; 00052 const PortsMap ports = SerialPortEnumerator::getPorts(); 00053 00054 QSettings settings; 00055 unsigned sectionEnabled(settings.value("connection dialog enabled group", 0).toUInt()); 00056 if ((sectionEnabled == 1) && (ports.size() == 0)) 00057 sectionEnabled = 0; 00058 00059 QVBoxLayout* mainLayout = new QVBoxLayout(this); 00060 00061 netGroupBox = new QGroupBox(tr("Network (TCP)")); 00062 netGroupBox->setCheckable(true); 00063 netGroupBox->setChecked(sectionEnabled == 0); 00064 QGridLayout* netLayout = new QGridLayout; 00065 netLayout->addWidget(new QLabel(tr("Host")), 0, 0); 00066 netLayout->addWidget(new QLabel(tr("Port")), 0, 1); 00067 host = new QLineEdit(settings.value("tcp host", ASEBA_DEFAULT_HOST).toString()); 00068 netLayout->addWidget(host, 1, 0); 00069 port = new QSpinBox(); 00070 port->setMinimum(0); 00071 port->setMaximum(65535); 00072 port->setValue(settings.value("tcp port", ASEBA_DEFAULT_PORT).toInt()); 00073 netLayout->addWidget(port, 1, 1); 00074 netGroupBox->setLayout(netLayout); 00075 connect(netGroupBox, SIGNAL(clicked()), SLOT(netGroupChecked())); 00076 mainLayout->addWidget(netGroupBox); 00077 00078 serialGroupBox = new QGroupBox(tr("Serial")); 00079 serialGroupBox->setCheckable(true); 00080 QHBoxLayout* serialLayout = new QHBoxLayout(); 00081 serial = new QListWidget(); 00082 bool serialPortSet(false); 00083 for (PortsMap::const_iterator it = ports.begin(); it != ports.end(); ++it) 00084 { 00085 const QString text(it->second.second.c_str()); 00086 QListWidgetItem* item = new QListWidgetItem(text); 00087 item->setData(Qt::UserRole, QVariant(QString::fromUtf8(it->second.first.c_str()))); 00088 serial->addItem(item); 00089 if (settings.value("serial name") == text) 00090 { 00091 serial->setCurrentItem(item); 00092 serialPortSet = true; 00093 } 00094 } 00095 if (sectionEnabled == 1 && !serialPortSet) 00096 sectionEnabled = 2; 00097 serialGroupBox->setChecked(sectionEnabled == 1); 00098 serial->setSelectionMode(QAbstractItemView::SingleSelection); 00099 serialLayout->addWidget(serial); 00100 connect(serial, SIGNAL(itemSelectionChanged()), SLOT(setupOkStateFromListSelection())); 00101 serialGroupBox->setLayout(serialLayout); 00102 connect(serialGroupBox, SIGNAL(clicked()), SLOT(serialGroupChecked())); 00103 mainLayout->addWidget(serialGroupBox); 00104 00105 customGroupBox = new QGroupBox(tr("Custom")); 00106 customGroupBox->setCheckable(true); 00107 customGroupBox->setChecked(sectionEnabled == 2); 00108 QHBoxLayout* customLayout = new QHBoxLayout(); 00109 custom = new QLineEdit(settings.value("custom target", ASEBA_DEFAULT_TARGET).toString()); 00110 customLayout->addWidget(custom); 00111 customGroupBox->setLayout(customLayout); 00112 connect(customGroupBox, SIGNAL(clicked()), SLOT(customGroupChecked())); 00113 mainLayout->addWidget(customGroupBox); 00114 00115 languageSelectionBox = new QComboBox; 00116 languageSelectionBox->addItem(QString::fromUtf8("English"), "en"); 00117 languageSelectionBox->addItem(QString::fromUtf8("Français"), "fr"); 00118 languageSelectionBox->addItem(QString::fromUtf8("Deutsch"), "de"); 00119 languageSelectionBox->addItem(QString::fromUtf8("Español"), "es"); 00120 //qDebug() << "locale is " << QLocale::system().name(); 00121 for (int i = 0; i < languageSelectionBox->count(); ++i) 00122 { 00123 if (QLocale::system().name().startsWith(languageSelectionBox->itemData(i).toString())) 00124 { 00125 languageSelectionBox->setCurrentIndex(i); 00126 break; 00127 } 00128 } 00129 mainLayout->addWidget(languageSelectionBox); 00130 00131 QHBoxLayout* buttonLayout = new QHBoxLayout(); 00132 connectButton = new QPushButton(QIcon(":/images/ok.png"), tr("Connect")); 00133 connect(connectButton, SIGNAL(clicked(bool)), SLOT(accept())); 00134 buttonLayout->addWidget(connectButton); 00135 QPushButton* cancelButton = new QPushButton(QIcon(":/images/no.png"), tr("Cancel")); 00136 connect(cancelButton, SIGNAL(clicked(bool)), SLOT(reject())); 00137 buttonLayout->addWidget(cancelButton); 00138 mainLayout->addLayout(buttonLayout); 00139 00140 setWindowTitle(tr("Aseba Target Selection")); 00141 } 00142 00143 std::string DashelConnectionDialog::getTarget() 00144 { 00145 QSettings settings; 00146 if (netGroupBox->isChecked()) 00147 { 00148 settings.setValue("connection dialog enabled group", 0); 00149 settings.setValue("tcp host", host->text()); 00150 settings.setValue("tcp port", port->value()); 00151 std::ostringstream oss; 00152 oss << "tcp:host=" << host->text().toLocal8Bit().constData() << ";port=" << port->value(); 00153 return oss.str(); 00154 } 00155 else if (serialGroupBox->isChecked()) 00156 { 00157 const QItemSelectionModel* model(serial->selectionModel()); 00158 assert(model && !model->selectedRows().isEmpty()); 00159 const QModelIndex item(model->selectedRows().first()); 00160 settings.setValue("connection dialog enabled group", 1); 00161 settings.setValue("serial name", item.data()); 00162 QString target("ser:device=%0"); 00163 return target.arg(item.data(Qt::UserRole).toString()).toLocal8Bit().constData(); 00164 } 00165 else if (customGroupBox->isChecked()) 00166 { 00167 settings.setValue("connection dialog enabled group", 2); 00168 settings.setValue("custom target", custom->text()); 00169 return custom->text().toLocal8Bit().constData(); 00170 } 00171 else 00172 { 00173 assert(false); 00174 return ""; 00175 } 00176 } 00177 00178 QString DashelConnectionDialog::getLocaleName() 00179 { 00180 return languageSelectionBox->itemData(languageSelectionBox->currentIndex()).toString(); 00181 } 00182 00183 void DashelConnectionDialog::netGroupChecked() 00184 { 00185 netGroupBox->setChecked(true); 00186 serialGroupBox->setChecked(false); 00187 customGroupBox->setChecked(false); 00188 setupOkStateFromListSelection(); 00189 } 00190 00191 void DashelConnectionDialog::serialGroupChecked() 00192 { 00193 netGroupBox->setChecked(false); 00194 serialGroupBox->setChecked(true); 00195 customGroupBox->setChecked(false); 00196 setupOkStateFromListSelection(); 00197 } 00198 00199 void DashelConnectionDialog::customGroupChecked() 00200 { 00201 netGroupBox->setChecked(false); 00202 serialGroupBox->setChecked(false); 00203 customGroupBox->setChecked(true); 00204 setupOkStateFromListSelection(); 00205 } 00206 00207 void DashelConnectionDialog::setupOkStateFromListSelection() 00208 { 00209 connectButton->setEnabled(serial->selectionModel()->hasSelection() || (!serialGroupBox->isChecked())); 00210 } 00211 00212 00213 DashelInterface::DashelInterface(QVector<QTranslator*> translators, const QString& commandLineTarget) : 00214 isRunning(true), 00215 stream(0) 00216 { 00217 // first use local name 00218 const QString& systemLocale(QLocale::system().name()); 00219 translators[0]->load(QString("qt_") + systemLocale, QLibraryInfo::location(QLibraryInfo::TranslationsPath)); 00220 translators[1]->load(QString(":/asebastudio_") + systemLocale); 00221 translators[2]->load(QString(":/compiler_") + systemLocale); 00222 00223 // try to connect to cammand line target, if any 00224 DashelConnectionDialog targetSelector; 00225 if (!commandLineTarget.isEmpty()) 00226 { 00227 bool failed = false; 00228 try 00229 { 00230 const std::string& testTarget(commandLineTarget.toStdString()); 00231 stream = Hub::connect(testTarget); 00232 lastConnectedTarget = testTarget; 00233 } 00234 catch (DashelException e) 00235 { 00236 // exception, try again 00237 failed = true; 00238 } 00239 00240 if (failed) 00241 QMessageBox::warning(0, tr("Connection to command line target failed"), tr("Cannot connect to target %0").arg(commandLineTarget)); 00242 else 00243 return; 00244 } 00245 // show connection dialog 00246 while (true) 00247 { 00248 if (targetSelector.exec() == QDialog::Rejected) 00249 { 00250 throw std::runtime_error("connection dialog closed"); 00251 } 00252 00253 try 00254 { 00255 //qDebug() << "Connecting to " << targetSelector.getTarget().c_str(); 00256 const std::string& testTarget(targetSelector.getTarget()); 00257 stream = Hub::connect(testTarget); 00258 lastConnectedTarget = testTarget; 00259 assert(translators.size() == 3); 00260 language = targetSelector.getLocaleName(); 00261 translators[0]->load(QString("qt_") + language, QLibraryInfo::location(QLibraryInfo::TranslationsPath)); 00262 translators[1]->load(QString(":/asebastudio_") + language); 00263 translators[2]->load(QString(":/compiler_") + language); 00264 break; 00265 } 00266 catch (DashelException e) 00267 { 00268 // exception, try again 00269 } 00270 } 00271 } 00272 00273 bool DashelInterface::attemptToReconnect() 00274 { 00275 assert(stream == 0); 00276 try 00277 { 00278 // we have to stop hub because otherwise it will be forever in poll() 00279 stream = Hub::connect(lastConnectedTarget); 00280 Dashel::Hub::stop(); 00281 if (stream) 00282 return true; 00283 } 00284 catch (DashelException e) 00285 { 00286 } 00287 return false; 00288 } 00289 00290 00291 void DashelInterface::stop() 00292 { 00293 isRunning = false; 00294 Dashel::Hub::stop(); 00295 } 00296 00297 // In QThread main function, we just make our Dashel hub switch listen for incoming data 00298 void DashelInterface::run() 00299 { 00300 while (isRunning) 00301 Dashel::Hub::run(); 00302 } 00303 00304 void DashelInterface::incomingData(Stream *stream) 00305 { 00306 Message *message = Message::receive(stream); 00307 emit messageAvailable(message); 00308 } 00309 00310 void DashelInterface::connectionClosed(Stream* stream, bool abnormal) 00311 { 00312 Q_UNUSED(stream); 00313 Q_UNUSED(abnormal); 00314 emit dashelDisconnection(); 00315 Q_ASSERT(stream == this->stream); 00316 this->stream = 0; 00317 } 00318 00319 void SignalingDescriptionsManager::nodeProtocolVersionMismatch(const std::string &nodeName, uint16 protocolVersion) 00320 { 00321 if (protocolVersion > ASEBA_PROTOCOL_VERSION) 00322 { 00323 QMessageBox::warning(0, 00324 QApplication::tr("Protocol version mismatch"), 00325 QApplication::tr("Aseba Studio uses an older protocol (%1) than node %0 (%2), please upgrade Aseba Studio.").arg(QString::fromUtf8(nodeName.c_str())).arg(ASEBA_PROTOCOL_VERSION).arg(protocolVersion) 00326 ); 00327 } 00328 else if (protocolVersion < ASEBA_PROTOCOL_VERSION) 00329 { 00330 QMessageBox::warning(0, 00331 QApplication::tr("Protocol version mismatch"), 00332 QApplication::tr("Node %0 uses an older protocol (%2) than Aseba Studio (%1), please upgrade the node firmware.").arg(QString::fromUtf8(nodeName.c_str())).arg(ASEBA_PROTOCOL_VERSION).arg(protocolVersion) 00333 ); 00334 } 00335 } 00336 00337 void SignalingDescriptionsManager::nodeDescriptionReceived(unsigned nodeId) 00338 { 00339 emit nodeDescriptionReceivedSignal(nodeId); 00340 } 00341 00342 00343 enum InNextState 00344 { 00345 NOT_IN_NEXT, 00346 WAITING_INITAL_PC, 00347 WAITING_LINE_CHANGE 00348 }; 00349 00350 DashelTarget::Node::Node() 00351 { 00352 executionMode = EXECUTION_UNKNOWN; 00353 } 00354 00355 DashelTarget::DashelTarget(QVector<QTranslator*> translators, const QString& commandLineTarget) : 00356 dashelInterface(translators, commandLineTarget), 00357 writeBlocked(false) 00358 { 00359 userEventsTimer.setSingleShot(true); 00360 connect(&userEventsTimer, SIGNAL(timeout()), SLOT(updateUserEvents())); 00361 00362 // we connect the events from the stream listening thread to slots living in our gui thread 00363 connect(&dashelInterface, SIGNAL(messageAvailable(Message *)), SLOT(messageFromDashel(Message *)), Qt::QueuedConnection); 00364 connect(&dashelInterface, SIGNAL(dashelDisconnection()), SLOT(disconnectionFromDashel()), Qt::QueuedConnection); 00365 00366 // we also connect to the description manager to know when we have a new node available 00367 connect(&descriptionManager, SIGNAL(nodeDescriptionReceivedSignal(unsigned)), SLOT(nodeDescriptionReceived(unsigned))); 00368 00369 messagesHandlersMap[ASEBA_MESSAGE_DISCONNECTED] = &Aseba::DashelTarget::receivedDisconnected; 00370 messagesHandlersMap[ASEBA_MESSAGE_VARIABLES] = &Aseba::DashelTarget::receivedVariables; 00371 messagesHandlersMap[ASEBA_MESSAGE_ARRAY_ACCESS_OUT_OF_BOUNDS] = &Aseba::DashelTarget::receivedArrayAccessOutOfBounds; 00372 messagesHandlersMap[ASEBA_MESSAGE_DIVISION_BY_ZERO] = &Aseba::DashelTarget::receivedDivisionByZero; 00373 messagesHandlersMap[ASEBA_MESSAGE_EVENT_EXECUTION_KILLED] = &Aseba::DashelTarget::receivedEventExecutionKilled; 00374 messagesHandlersMap[ASEBA_MESSAGE_NODE_SPECIFIC_ERROR] = &Aseba::DashelTarget::receivedNodeSpecificError; 00375 messagesHandlersMap[ASEBA_MESSAGE_EXECUTION_STATE_CHANGED] = &Aseba::DashelTarget::receivedExecutionStateChanged; 00376 messagesHandlersMap[ASEBA_MESSAGE_BREAKPOINT_SET_RESULT] = &Aseba::DashelTarget::receivedBreakpointSetResult; 00377 messagesHandlersMap[ASEBA_MESSAGE_BOOTLOADER_ACK] = &Aseba::DashelTarget::receivedBootloaderAck; 00378 00379 dashelInterface.start(); 00380 } 00381 00382 DashelTarget::~DashelTarget() 00383 { 00384 dashelInterface.stop(); 00385 dashelInterface.wait(); 00386 DashelTarget::disconnect(); 00387 } 00388 00389 void DashelTarget::disconnect() 00390 { 00391 assert(writeBlocked == false); 00392 00393 if (dashelInterface.stream) 00394 { 00395 try 00396 { 00397 // detach all nodes 00398 for (NodesMap::const_iterator node = nodes.begin(); node != nodes.end(); ++node) 00399 { 00400 //DetachDebugger(node->first).serialize(dashelInterface.stream); 00401 BreakpointClearAll(node->first).serialize(dashelInterface.stream); 00402 Run(node->first).serialize(dashelInterface.stream); 00403 } 00404 dashelInterface.stream->flush(); 00405 } 00406 catch(Dashel::DashelException e) 00407 { 00408 handleDashelException(e); 00409 } 00410 } 00411 } 00412 00413 const TargetDescription * const DashelTarget::getDescription(unsigned node) const 00414 { 00415 return descriptionManager.getDescription(node); 00416 } 00417 00418 void DashelTarget::broadcastGetDescription() 00419 { 00420 if (writeBlocked || !dashelInterface.stream) return; 00421 00422 try 00423 { 00424 GetDescription().serialize(dashelInterface.stream); 00425 dashelInterface.stream->flush(); 00426 } 00427 catch(Dashel::DashelException e) 00428 { 00429 handleDashelException(e); 00430 } 00431 } 00432 00433 void DashelTarget::uploadBytecode(unsigned node, const BytecodeVector &bytecode) 00434 { 00435 if (writeBlocked || !dashelInterface.stream) return; 00436 00437 NodesMap::iterator nodeIt = nodes.find(node); 00438 assert(nodeIt != nodes.end()); 00439 00440 // fill debug bytecode and build address map 00441 nodeIt->second.debugBytecode = bytecode; 00442 nodeIt->second.eventAddressToId = bytecode.getEventAddressesToIds(); 00443 00444 // send bytecode 00445 try 00446 { 00447 sendBytecode(dashelInterface.stream, node, std::vector<uint16>(bytecode.begin(), bytecode.end())); 00448 dashelInterface.stream->flush(); 00449 } 00450 catch(Dashel::DashelException e) 00451 { 00452 handleDashelException(e); 00453 } 00454 } 00455 00456 void DashelTarget::writeBytecode(unsigned node) 00457 { 00458 if (writeBlocked || !dashelInterface.stream) return; 00459 00460 try 00461 { 00462 WriteBytecode(node).serialize(dashelInterface.stream); 00463 dashelInterface.stream->flush(); 00464 } 00465 catch(Dashel::DashelException e) 00466 { 00467 handleDashelException(e); 00468 } 00469 } 00470 00471 void DashelTarget::reboot(unsigned node) 00472 { 00473 if (writeBlocked || !dashelInterface.stream) return; 00474 00475 try 00476 { 00477 Reboot(node).serialize(dashelInterface.stream); 00478 dashelInterface.stream->flush(); 00479 } 00480 catch(Dashel::DashelException e) 00481 { 00482 handleDashelException(e); 00483 } 00484 } 00485 00486 void DashelTarget::sendEvent(unsigned id, const VariablesDataVector &data) 00487 { 00488 if (writeBlocked || !dashelInterface.stream) return; 00489 00490 try 00491 { 00492 UserMessage(id, data).serialize(dashelInterface.stream); 00493 dashelInterface.stream->flush(); 00494 } 00495 catch(Dashel::DashelException e) 00496 { 00497 handleDashelException(e); 00498 } 00499 } 00500 00501 void DashelTarget::setVariables(unsigned node, unsigned start, const VariablesDataVector &data) 00502 { 00503 if (writeBlocked || !dashelInterface.stream) return; 00504 00505 try 00506 { 00507 SetVariables(node, start, data).serialize(dashelInterface.stream); 00508 dashelInterface.stream->flush(); 00509 } 00510 catch(Dashel::DashelException e) 00511 { 00512 handleDashelException(e); 00513 } 00514 } 00515 00516 void DashelTarget::getVariables(unsigned node, unsigned start, unsigned length) 00517 { 00518 if (writeBlocked || !dashelInterface.stream) return; 00519 00520 const unsigned variablesPayloadSize = ASEBA_MAX_EVENT_ARG_COUNT-1; 00521 00522 try 00523 { 00524 while (length > variablesPayloadSize) 00525 { 00526 GetVariables(node, start, variablesPayloadSize).serialize(dashelInterface.stream); 00527 start += variablesPayloadSize; 00528 length -= variablesPayloadSize; 00529 } 00530 00531 GetVariables(node, start, length).serialize(dashelInterface.stream); 00532 dashelInterface.stream->flush(); 00533 } 00534 catch(Dashel::DashelException e) 00535 { 00536 handleDashelException(e); 00537 } 00538 } 00539 00540 void DashelTarget::reset(unsigned node) 00541 { 00542 if (writeBlocked || !dashelInterface.stream) return; 00543 00544 try 00545 { 00546 Reset(node).serialize(dashelInterface.stream); 00547 dashelInterface.stream->flush(); 00548 } 00549 catch(Dashel::DashelException e) 00550 { 00551 handleDashelException(e); 00552 } 00553 } 00554 00555 void DashelTarget::run(unsigned node) 00556 { 00557 if (writeBlocked || !dashelInterface.stream) return; 00558 00559 NodesMap::iterator nodeIt = nodes.find(node); 00560 assert(nodeIt != nodes.end()); 00561 00562 try 00563 { 00564 if (nodeIt->second.executionMode == EXECUTION_STEP_BY_STEP) 00565 Step(node).serialize(dashelInterface.stream); 00566 Run(node).serialize(dashelInterface.stream); 00567 dashelInterface.stream->flush(); 00568 } 00569 catch(Dashel::DashelException e) 00570 { 00571 handleDashelException(e); 00572 } 00573 } 00574 00575 void DashelTarget::pause(unsigned node) 00576 { 00577 if (writeBlocked || !dashelInterface.stream) return; 00578 00579 try 00580 { 00581 Pause(node).serialize(dashelInterface.stream); 00582 dashelInterface.stream->flush(); 00583 } 00584 catch(Dashel::DashelException e) 00585 { 00586 handleDashelException(e); 00587 } 00588 } 00589 00590 void DashelTarget::next(unsigned node) 00591 { 00592 if (writeBlocked || !dashelInterface.stream) return; 00593 00594 NodesMap::iterator nodeIt = nodes.find(node); 00595 assert(nodeIt != nodes.end()); 00596 00597 nodeIt->second.steppingInNext = WAITING_INITAL_PC; 00598 00599 GetExecutionState getExecutionStateMessage; 00600 getExecutionStateMessage.dest = node; 00601 00602 try 00603 { 00604 getExecutionStateMessage.serialize(dashelInterface.stream); 00605 dashelInterface.stream->flush(); 00606 } 00607 catch(Dashel::DashelException e) 00608 { 00609 handleDashelException(e); 00610 } 00611 } 00612 00613 void DashelTarget::stop(unsigned node) 00614 { 00615 if (writeBlocked || !dashelInterface.stream) return; 00616 00617 try 00618 { 00619 Stop(node).serialize(dashelInterface.stream); 00620 dashelInterface.stream->flush(); 00621 } 00622 catch(Dashel::DashelException e) 00623 { 00624 handleDashelException(e); 00625 } 00626 } 00627 00628 void DashelTarget::setBreakpoint(unsigned node, unsigned line) 00629 { 00630 if (writeBlocked || !dashelInterface.stream) return; 00631 00632 int pc = getPCFromLine(node, line); 00633 if (pc >= 0) 00634 { 00635 BreakpointSet breakpointSetMessage; 00636 breakpointSetMessage.pc = pc; 00637 breakpointSetMessage.dest = node; 00638 00639 try 00640 { 00641 breakpointSetMessage.serialize(dashelInterface.stream); 00642 dashelInterface.stream->flush(); 00643 } 00644 catch(Dashel::DashelException e) 00645 { 00646 handleDashelException(e); 00647 } 00648 } 00649 } 00650 00651 void DashelTarget::clearBreakpoint(unsigned node, unsigned line) 00652 { 00653 if (writeBlocked || !dashelInterface.stream) return; 00654 00655 int pc = getPCFromLine(node, line); 00656 if (pc >= 0) 00657 { 00658 BreakpointClear breakpointClearMessage; 00659 breakpointClearMessage.pc = pc; 00660 breakpointClearMessage.dest = node; 00661 00662 try 00663 { 00664 breakpointClearMessage.serialize(dashelInterface.stream); 00665 dashelInterface.stream->flush(); 00666 } 00667 catch(Dashel::DashelException e) 00668 { 00669 handleDashelException(e); 00670 } 00671 } 00672 } 00673 00674 void DashelTarget::clearBreakpoints(unsigned node) 00675 { 00676 if (writeBlocked || !dashelInterface.stream) return; 00677 00678 BreakpointClearAll breakpointClearAllMessage; 00679 breakpointClearAllMessage.dest = node; 00680 00681 try 00682 { 00683 breakpointClearAllMessage.serialize(dashelInterface.stream); 00684 dashelInterface.stream->flush(); 00685 } 00686 catch(Dashel::DashelException e) 00687 { 00688 handleDashelException(e); 00689 } 00690 } 00691 00692 void DashelTarget::blockWrite() 00693 { 00694 writeBlocked = true; 00695 } 00696 00697 void DashelTarget::unblockWrite() 00698 { 00699 writeBlocked = false; 00700 } 00701 00702 void DashelTarget::updateUserEvents() 00703 { 00704 // send only 20 latest user events 00705 if (userEventsQueue.size() > 20) 00706 emit userEventsDropped(userEventsQueue.size() - 20); 00707 while (userEventsQueue.size() > 20) 00708 { 00709 delete userEventsQueue.head(); 00710 userEventsQueue.dequeue(); 00711 } 00712 00713 while (!userEventsQueue.isEmpty()) 00714 { 00715 emit userEvent(userEventsQueue.head()->type, userEventsQueue.head()->data); 00716 delete userEventsQueue.head(); 00717 userEventsQueue.dequeue(); 00718 } 00719 } 00720 00721 void DashelTarget::messageFromDashel(Message *message) 00722 { 00723 bool deleteMessage = true; 00724 //message->dump(std::cout); 00725 //std::cout << std::endl; 00726 00727 // let the description manager filter the message 00728 descriptionManager.processMessage(message); 00729 00730 // see if we have a registered handler for this message 00731 MessagesHandlersMap::const_iterator messageHandler = messagesHandlersMap.find(message->type); 00732 if (messageHandler == messagesHandlersMap.end()) 00733 { 00734 UserMessage *userMessage = dynamic_cast<UserMessage *>(message); 00735 if (userMessage) 00736 { 00737 userEventsQueue.enqueue(userMessage); 00738 if (!userEventsTimer.isActive()) 00739 userEventsTimer.start(50); 00740 deleteMessage = false; 00741 } 00742 /*else 00743 qDebug() << QString("Unhandeled non user message of type 0x%0 received from %1").arg(message->type, 6).arg(message->source);*/ 00744 } 00745 else 00746 { 00747 (this->*(messageHandler->second))(message); 00748 } 00749 00750 // if required, garbage collect this message 00751 if (deleteMessage) 00752 delete message; 00753 } 00754 00755 struct ReconnectionDialog: public QMessageBox 00756 { 00757 ReconnectionDialog(DashelInterface& dashelInterface): 00758 dashelInterface(dashelInterface) 00759 { 00760 setWindowTitle(tr("Aseba Studio - Connection closed")); 00761 setText(tr("Warning, connection closed: I am trying to reconnect.")); 00762 setStandardButtons(QMessageBox::Cancel); 00763 setEscapeButton(QMessageBox::Cancel); 00764 setIcon(QMessageBox::Warning); 00765 00766 startTimer(1000); 00767 } 00768 00769 protected: 00770 virtual void timerEvent ( QTimerEvent * event ) 00771 { 00772 if (dashelInterface.attemptToReconnect()) 00773 accept(); 00774 } 00775 00776 DashelInterface& dashelInterface; 00777 }; 00778 00779 void DashelTarget::disconnectionFromDashel() 00780 { 00781 emit networkDisconnected(); 00782 nodes.clear(); 00783 descriptionManager.reset(); 00784 00785 // show a dialog box that is trying to reconnect 00786 ReconnectionDialog reconnectionDialog(dashelInterface); 00787 reconnectionDialog.exec(); 00788 } 00789 00790 void DashelTarget::nodeDescriptionReceived(unsigned nodeId) 00791 { 00792 Node& node = nodes[nodeId]; 00793 00794 node.steppingInNext = NOT_IN_NEXT; 00795 node.lineInNext = 0; 00796 00797 emit nodeConnected(nodeId); 00798 } 00799 00800 void DashelTarget::receivedVariables(Message *message) 00801 { 00802 Variables *variables = polymorphic_downcast<Variables *>(message); 00803 emit variablesMemoryChanged(variables->source, variables->start, variables->variables); 00804 } 00805 00806 void DashelTarget::receivedArrayAccessOutOfBounds(Message *message) 00807 { 00808 ArrayAccessOutOfBounds *aa = polymorphic_downcast<ArrayAccessOutOfBounds *>(message); 00809 00810 int line = getLineFromPC(aa->source, aa->pc); 00811 if (line >= 0) 00812 { 00813 emit arrayAccessOutOfBounds(aa->source, line, aa->size, aa->index); 00814 emit executionModeChanged(aa->source, EXECUTION_STOP); 00815 } 00816 } 00817 00818 void DashelTarget::receivedDivisionByZero(Message *message) 00819 { 00820 DivisionByZero *dz = polymorphic_downcast<DivisionByZero *>(message); 00821 00822 int line = getLineFromPC(dz->source, dz->pc); 00823 if (line >= 0) 00824 { 00825 emit divisionByZero(dz->source, line); 00826 emit executionModeChanged(dz->source, EXECUTION_STOP); 00827 } 00828 } 00829 00830 void DashelTarget::receivedEventExecutionKilled(Message *message) 00831 { 00832 EventExecutionKilled *eek = polymorphic_downcast<EventExecutionKilled *>(message); 00833 00834 int line = getLineFromPC(eek->source, eek->pc); 00835 if (line >= 0) 00836 { 00837 emit eventExecutionKilled(eek->source, line); 00838 } 00839 } 00840 00841 void DashelTarget::receivedNodeSpecificError(Message *message) 00842 { 00843 NodeSpecificError *nse = polymorphic_downcast<NodeSpecificError *>(message); 00844 00845 int line = getLineFromPC(nse->source, nse->pc); 00846 // The NodeSpecificError can be triggered even if the pc is not valid 00847 // if (line >= 0) 00848 // { 00849 emit nodeSpecificError(nse->source, line, QString::fromStdWString(nse->message)); 00850 emit executionModeChanged(nse->source, EXECUTION_STOP); 00851 // } 00852 } 00853 00854 void DashelTarget::receivedExecutionStateChanged(Message *message) 00855 { 00856 ExecutionStateChanged *ess = polymorphic_downcast<ExecutionStateChanged *>(message); 00857 00858 Node &node = nodes[ess->source]; 00859 int line = getLineFromPC(ess->source, ess->pc); 00860 00861 assert(writeBlocked == false); 00862 assert(dashelInterface.stream); 00863 00864 Target::ExecutionMode mode; 00865 if (ess->flags & ASEBA_VM_STEP_BY_STEP_MASK) 00866 { 00867 if (ess->flags & ASEBA_VM_EVENT_ACTIVE_MASK) 00868 { 00869 mode = EXECUTION_STEP_BY_STEP; 00870 if (line >= 0) 00871 { 00872 // Step by step, manage next 00873 if (node.steppingInNext == NOT_IN_NEXT) 00874 { 00875 emit executionPosChanged(ess->source, line); 00876 emit executionModeChanged(ess->source, mode); 00877 emit variablesMemoryEstimatedDirty(ess->source); 00878 } 00879 else if (node.steppingInNext == WAITING_INITAL_PC) 00880 { 00881 // we have line, do steps now 00882 node.lineInNext = line; 00883 node.steppingInNext = WAITING_LINE_CHANGE; 00884 00885 try 00886 { 00887 Step(ess->source).serialize(dashelInterface.stream); 00888 dashelInterface.stream->flush(); 00889 } 00890 catch(Dashel::DashelException e) 00891 { 00892 handleDashelException(e); 00893 } 00894 } 00895 else if (node.steppingInNext == WAITING_LINE_CHANGE) 00896 { 00897 if ( 00898 (node.eventAddressToId.find(ess->pc) != node.eventAddressToId.end()) || 00899 (line != static_cast<int>(node.lineInNext)) 00900 ) 00901 { 00902 node.steppingInNext = NOT_IN_NEXT; 00903 emit executionPosChanged(ess->source, line); 00904 emit executionModeChanged(ess->source, mode); 00905 emit variablesMemoryEstimatedDirty(ess->source); 00906 } 00907 else 00908 { 00909 try 00910 { 00911 Step(ess->source).serialize(dashelInterface.stream); 00912 dashelInterface.stream->flush(); 00913 } 00914 catch(Dashel::DashelException e) 00915 { 00916 handleDashelException(e); 00917 } 00918 } 00919 } 00920 else 00921 assert(false); 00922 // we can safely return here, all case that require 00923 // emitting signals have been handeled 00924 node.executionMode = mode; 00925 return; 00926 } 00927 } 00928 else 00929 { 00930 mode = EXECUTION_STOP; 00931 } 00932 } 00933 else 00934 { 00935 mode = EXECUTION_RUN; 00936 } 00937 00938 emit executionModeChanged(ess->source, mode); 00939 if (node.executionMode != mode) 00940 { 00941 emit variablesMemoryEstimatedDirty(ess->source); 00942 node.executionMode = mode; 00943 } 00944 } 00945 00946 void DashelTarget::receivedDisconnected(Message *message) 00947 { 00948 Disconnected *disconnected = polymorphic_downcast<Disconnected *>(message); 00949 00950 emit nodeDisconnected(disconnected->source); 00951 } 00952 00953 void DashelTarget::receivedBreakpointSetResult(Message *message) 00954 { 00955 BreakpointSetResult *bsr = polymorphic_downcast<BreakpointSetResult *>(message); 00956 unsigned node = bsr->source; 00957 emit breakpointSetResult(node, getLineFromPC(node, bsr->pc), bsr->success); 00958 } 00959 00960 void DashelTarget::receivedBootloaderAck(Message *message) 00961 { 00962 BootloaderAck *ack = polymorphic_downcast<BootloaderAck*>(message); 00963 emit bootloaderAck(ack->errorCode, ack->errorAddress); 00964 } 00965 00966 int DashelTarget::getPCFromLine(unsigned node, unsigned line) 00967 { 00968 // first lookup node 00969 NodesMap::const_iterator nodeIt = nodes.find(node); 00970 00971 if (nodeIt == nodes.end()) 00972 return -1; 00973 00974 // then find PC 00975 for (size_t i = 0; i < nodeIt->second.debugBytecode.size(); i++) 00976 if (nodeIt->second.debugBytecode[i].line == line) 00977 return i; 00978 00979 return -1; 00980 } 00981 00982 int DashelTarget::getLineFromPC(unsigned node, unsigned pc) 00983 { 00984 // first lookup node 00985 NodesMap::const_iterator nodeIt = nodes.find(node); 00986 00987 if (nodeIt == nodes.end()) 00988 return -1; 00989 00990 // then get line 00991 if (pc < nodeIt->second.debugBytecode.size()) 00992 return nodeIt->second.debugBytecode[pc].line; 00993 00994 return -1; 00995 } 00996 00997 void DashelTarget::handleDashelException(Dashel::DashelException e) 00998 { 00999 switch(e.source) 01000 { 01001 case Dashel::DashelException::ConnectionLost: 01002 case Dashel::DashelException::IOError: 01003 // "normal" disconnections, managed internally in Dashel::Hub, so don't care about 01004 break; 01005 case Dashel::DashelException::ConnectionFailed: 01006 // should not happen here, but can because of typos in Dashel, catch it for now 01007 break; 01008 default: 01009 QMessageBox::critical(NULL, tr("Unexpected Dashel Error"), tr("A communication error happened:") + " (" + QString::number(e.source) + ") " + e.what()); 01010 break; 01011 } 01012 } 01013 01015 }