diff --git a/CMakeLists.txt b/CMakeLists.txt index 129ad981..eeda3eaf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ endif() ############################################################# find_package(ament_cmake QUIET) +find_package(catkin QUIET) if(ament_cmake_FOUND) find_package(ament_index_cpp REQUIRED) @@ -29,19 +30,19 @@ if(ament_cmake_FOUND) behaviortree_cpp_v3 ) - message(STATUS "------------------------------------------------") - message(STATUS "BehaviourTreeEditor is being built using AMENT.") - message(STATUS "------------------------------------------------") -elseif( CATKIN_DEVEL_PREFIX OR CATKIN_BUILD_BINARY_PACKAGE) + message(STATUS "---------------------") + message(STATUS "Compiling with AMENT.") + message(STATUS "---------------------") +elseif(catkin_FOUND OR CATKIN_DEVEL_PREFIX OR CATKIN_BUILD_BINARY_PACKAGE ) # http://answers.ros.org/question/230877/optionally-build-a-package-with-catkin/ set(catkin_FOUND 1) # add_definitions( -DUSING_ROS ) find_package(catkin REQUIRED COMPONENTS behaviortree_cpp_v3) - message(STATUS "------------------------------------------------") - message(STATUS "BehaviourTreeEditor is being built using CATKIN.") - message(STATUS "------------------------------------------------") + message(STATUS "----------------------") + message(STATUS "Compiling with CATKIN.") + message(STATUS "----------------------") catkin_package( INCLUDE_DIRS @@ -82,13 +83,24 @@ set(BUILD_TESTING OFF CACHE BOOL "") set(CATKIN_ENABLE_TESTING OFF CACHE BOOL "") if(NOT catkin_FOUND AND NOT ament_cmake_FOUND) - # look for BehaviorTree.CPP on the system - find_package(BehaviorTreeV3) - if (NOT BehaviorTree_FOUND) - # use git submodule only if you are not compiling with catkin - add_subdirectory( depend/BehaviorTree.CPP ) - include_directories( depend/BehaviorTree.CPP/include ) + + if(NOT EXISTS depend/BehaviorTree.CPP) + message("Can't find the submodule BehaviorTree.CPP") + message("Run [git submodule update --init --recursive]") endif() + + SET(BUILD_EXAMPLES OFF CACHE BOOL "Build tutorials and examples" FORCE) + SET(BUILD_UNIT_TESTS OFF CACHE BOOL "Build the unit tests" FORCE) + SET(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries" FORCE) + + # look for BehaviorTree.CPP as submodule + # use git submodule only if you are not compiling with catkin + add_subdirectory( depend/BehaviorTree.CPP ) + include_directories( depend/BehaviorTree.CPP/include ) + + message(STATUS "----------------------------------") + message(STATUS "BehaviourTree.CPP submodule found.") + message(STATUS "----------------------------------") endif() ########################################## @@ -147,7 +159,7 @@ add_library(behavior_tree_editor SHARED ${FORMS_HEADERS} ) -SET(GROOT_DEPENDENCIES QtNodeEditor ) +SET(GROOT_DEPENDENCIES QtNodeEditor ncurses ncursesw tinfo ) if(ament_cmake_FOUND) ament_target_dependencies(behavior_tree_editor ${dependencies}) @@ -158,7 +170,12 @@ else() endif() if( ZMQ_FOUND ) - SET(GROOT_DEPENDENCIES ${GROOT_DEPENDENCIES} zmq) + if (APPLE) + find_package(cppzmq) + SET(GROOT_DEPENDENCIES ${GROOT_DEPENDENCIES} cppzmq) + else() + SET(GROOT_DEPENDENCIES ${GROOT_DEPENDENCIES} zmq) + endif() endif() target_link_libraries(behavior_tree_editor ${GROOT_DEPENDENCIES} ) diff --git a/QtNodeEditor/src/NodeState.cpp b/QtNodeEditor/src/NodeState.cpp index 711f3663..53f310b9 100644 --- a/QtNodeEditor/src/NodeState.cpp +++ b/QtNodeEditor/src/NodeState.cpp @@ -48,7 +48,7 @@ NodeState:: connections(PortType portType, PortIndex portIndex) const { auto const &connections = getEntries(portType); - if( portIndex < 0 || portIndex >= connections.size() ) + if( portIndex < 0 || static_cast(portIndex) >= connections.size() ) { return NodeState::ConnectionPtrSet(); } @@ -139,4 +139,3 @@ resizing() const { return _resizing; } - diff --git a/README.md b/README.md index 2677a5bd..85a46e5b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,19 @@ [![Build Status](https://travis-ci.org/BehaviorTree/Groot.svg?branch=master)](https://travis-ci.org/BehaviorTree/Groot) +# DEPRECATION notice + +Groot is being rewritten from the ground up (https://www.behaviortree.dev/groot). +This repository will be in "maintenance mode" and won't receive any significant update. +Issues will **not** be addressed by the main author but, occasionally, Pull Requests **might** be checked and merged. + +**Groot 1.0 is compatible only with BehaviorTree.CPP 3.8.x**, and it is not expected to +work correctly with BehaviorTree.CPP 4.x. + +Groot 2.0 introduces many new functionalities and compatibility with BT.CPP 4.0. + +**If you use Groot at work** and you want to know how we are redesigning it to be more flexible, +reliable, fast and scalable, get in touch with the main author: dfaconti@aurynrobotics.com. + # Groot **Groot** is a Graphical Editor, written in C++ and Qt, to create [BehaviorTrees](https://en.wikipedia.org/wiki/Behavior_tree). @@ -15,13 +29,6 @@ the graphic user interface are used to design and monitor a Behavior Tree. [![MOOD2Be](video_MOOD2Be.png)](https://vimeo.com/304651183) -# Does your company use BehaviorTree.CPP and Groot? - -No company, institution or public/private funding is currently supporting the development of BehaviorTree.CPP and Groot. As a consequence, my time to support **BehaviorTree.CPP** is very limited and I decided that I won't spend any time at all supporting **Groot**. -Pull Requests are welcome and will be reviewed, even if with some delay. - -If your company use this software, consider becoming a **sponsor** to support bug fixing and development of new features. You can find contact details in [package.xml](package.xml). - # Dependencies, Installation, and Usage To compile the project you need: @@ -34,27 +41,31 @@ On Ubuntu Xenial or later, you can install the dependencies with: sudo apt install qtbase5-dev libqt5svg5-dev libzmq3-dev libdw-dev Some functionalities of the code related to ROS will work __only__ if the -project is compiled with with _catkin_. +project is compiled with _catkin_. -# Compilation instructions (Linux) +## Compilation instructions (Linux) - git clone https://github.com/BehaviorTree/Groot.git - cd Groot - git submodule update --init --recursive - mkdir build; cd build - cmake .. - make +``` +git clone --recurse-submodules https://github.com/BehaviorTree/Groot.git +cd Groot +cmake -S . -B build +cmake --build build +``` Note compiling "in-source" is not allowed. - # Compilation instructions (ROS) - - mkdir -p catkin_ws/src - cd catkin_ws/src - git clone https://github.com/BehaviorTree/Groot.git - cd .. - rosdep install --from-paths src --ignore-src - catkin_make + ## Compilation instructions (ROS/ROS2) + +**Discouraged** + +If you want to compile using `catkin build`(ROS) or `colcon_build` (ROS2) +then you must be sure that version 3.8.x is used (branch V3.8). + +You may probably want to compile BehaviorTree.CPP in the same workspace. + +``` +git clone --branch v3.8 https://github.com/BehaviorTree/BehaviorTree.CPP.git +``` # Licence diff --git a/bt_editor/XML_utilities.cpp b/bt_editor/XML_utilities.cpp index f26f3464..8ddbcd3f 100644 --- a/bt_editor/XML_utilities.cpp +++ b/bt_editor/XML_utilities.cpp @@ -2,10 +2,12 @@ #include "utils.h" #include "models/SubtreeNodeModel.hpp" +#include #include #include #include #include +#include using namespace QtNodes; @@ -189,11 +191,13 @@ bool VerifyXML(QDomDocument &doc, error_messages.clear(); try { std::string xml_text = doc.toString().toStdString(); - std::set registered_nodes; - + std::unordered_map registered_nodes; + + BT::NodeType node_type; for(const auto& str: registered_ID) { - registered_nodes.insert( str.toStdString() ); + node_type = BT::convertFromString(str.toStdString()); + registered_nodes.insert( {str.toStdString(), node_type}); } BT::VerifyXML(xml_text, registered_nodes); // may throw diff --git a/bt_editor/graphic_container.cpp b/bt_editor/graphic_container.cpp index 421400c8..5a9872c2 100644 --- a/bt_editor/graphic_container.cpp +++ b/bt_editor/graphic_container.cpp @@ -11,6 +11,7 @@ #include #include #include +#include using namespace QtNodes; @@ -137,17 +138,16 @@ void GraphicContainer::lockSubtreeEditing(Node &root_node, bool locked, bool cha } } //-------------------------------- - QtNodes::NodeStyle style; - if( locked && change_style ) { + QtNodes::NodeStyle style; style.GradientColor0.setBlue(120); style.GradientColor1.setBlue(100); style.GradientColor2.setBlue(90); style.GradientColor3.setBlue(90); + node->nodeDataModel()->setNodeStyle( style ); } node->nodeGraphicsObject().setGeometryChanged(); - node->nodeDataModel()->setNodeStyle( style ); node->nodeGraphicsObject().update(); } } @@ -163,6 +163,18 @@ void GraphicContainer::nodeReorder() emit undoableChange(); } +void GraphicContainer::saveSvgFile(const QString path) +{ + QSvgGenerator generator; + QRectF rect = _scene->itemsBoundingRect(); + generator.setFileName(path); + generator.setSize(QSize(rect.width(), rect.height())); + generator.setViewBox(rect); + QPainter painter; + painter.begin(&generator); + _scene->render(&painter, rect, rect); +} + void GraphicContainer::zoomHomeView() { QRectF rect = _scene->itemsBoundingRect(); @@ -721,5 +733,3 @@ void GraphicContainer::loadFromJson(const QByteArray &data) clearScene(); scene()->loadFromMemory( data ); } - - diff --git a/bt_editor/graphic_container.h b/bt_editor/graphic_container.h index b725e8bf..eb3ee3c6 100644 --- a/bt_editor/graphic_container.h +++ b/bt_editor/graphic_container.h @@ -33,6 +33,8 @@ class GraphicContainer : public QObject void nodeReorder(); + void saveSvgFile(const QString path); + void zoomHomeView(); bool containsValidTree() const; diff --git a/bt_editor/main.cpp b/bt_editor/main.cpp index 077c3c35..d80e92ee 100644 --- a/bt_editor/main.cpp +++ b/bt_editor/main.cpp @@ -39,6 +39,23 @@ main(int argc, char *argv[]) "Start in one of these modes: [editor,monitor,replay]", "mode"); parser.addOption(mode_option); + + QCommandLineOption address_option(QStringList() << "address", + "Address to connect to (defaults to localhost)", + "address"); + parser.addOption(address_option); + QCommandLineOption pub_port_option(QStringList() << "publisher_port", + "Publisher port number (defaults to 1666)", + "publisher_port"); + parser.addOption(pub_port_option); + QCommandLineOption srv_port_option(QStringList() << "server_port", + "Server port number (defaults to 1667)", + "server_port"); + parser.addOption(srv_port_option); + QCommandLineOption autoconnect_option(QStringList() << "autoconnect", + "Autoconnect to monitor"); + parser.addOption(autoconnect_option); + parser.process( app ); QFile styleFile( ":/stylesheet.qss" ); @@ -88,7 +105,15 @@ main(int argc, char *argv[]) mode = dialog.getGraphicMode(); } - MainWindow win( mode ); + // Get the monitor options. + const QString monitor_address = parser.value(address_option); + const QString monitor_pub_port = parser.value(pub_port_option); + const QString monitor_srv_port = parser.value(srv_port_option); + const bool monitor_autoconnect = parser.isSet(autoconnect_option); + + // Start the main application. + MainWindow win( mode, monitor_address, monitor_pub_port, + monitor_srv_port, monitor_autoconnect ); win.show(); return app.exec(); } diff --git a/bt_editor/mainwindow.cpp b/bt_editor/mainwindow.cpp index ad8a8ed9..3b135175 100644 --- a/bt_editor/mainwindow.cpp +++ b/bt_editor/mainwindow.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "editor_flowscene.h" #include "utils.h" @@ -40,11 +41,20 @@ using QtNodes::FlowScene; using QtNodes::NodeGraphicsObject; using QtNodes::NodeState; -MainWindow::MainWindow(GraphicMode initial_mode, QWidget *parent) : - QMainWindow(parent), - ui(new Ui::MainWindow), - _current_mode(initial_mode), - _current_layout(QtNodes::PortLayout::Vertical) +MainWindow::MainWindow(GraphicMode initial_mode, + const QString& monitor_address, + const QString& monitor_pub_port, + const QString& monitor_srv_port, + const bool monitor_autoconnect, + QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow), + _current_mode(initial_mode), + _current_layout(QtNodes::PortLayout::Vertical), + _monitor_address(monitor_address), + _monitor_publisher_port(monitor_pub_port), + _monitor_server_port(monitor_srv_port), + _monitor_autoconnect(monitor_autoconnect) { ui->setupUi(this); @@ -96,7 +106,8 @@ MainWindow::MainWindow(GraphicMode initial_mode, QWidget *parent) : ui->leftFrame->layout()->addWidget( _replay_widget ); #ifdef ZMQ_FOUND - _monitor_widget = new SidepanelMonitor(this); + _monitor_widget = new SidepanelMonitor( + this, _monitor_address, _monitor_publisher_port, _monitor_server_port); ui->leftFrame->layout()->addWidget( _monitor_widget ); connect( ui->toolButtonConnect, &QToolButton::clicked, @@ -104,6 +115,16 @@ MainWindow::MainWindow(GraphicMode initial_mode, QWidget *parent) : connect( _monitor_widget, &SidepanelMonitor::connectionUpdate, this, &MainWindow::onConnectionUpdate ); + + if ( monitor_autoconnect ) + { + // If autoconnecting, increase the timeout to get the behavior tree to a + // larger value. This only lasts for one "connect" before returning to + // its default value. + _monitor_widget->set_load_tree_timeout_ms( + _monitor_widget->_load_tree_autoconnect_timeout_ms); + ui->toolButtonConnect->animateClick(); + } #else ui->actionMonitor_mode->setVisible(false); #endif @@ -146,12 +167,15 @@ MainWindow::MainWindow(GraphicMode initial_mode, QWidget *parent) : this->createTab(ID); }); + connect(_editor_widget, &SidepanelEditor::setTabScope, + this, &MainWindow::onSubtreeSelected); + connect( _editor_widget, &SidepanelEditor::renameSubtree, this, [this](QString prev_ID, QString new_ID) { if (prev_ID == new_ID) return; - + for (int index = 0; index < ui->tabWidget->count(); index++) { if( ui->tabWidget->tabText(index) == prev_ID) @@ -460,7 +484,7 @@ QString MainWindow::saveToXML() const if( abs_root->children_index.size() == 1 && abs_root->model.registration_ID == "Root" ) { - // mofe to the child of ROOT + // move to the child of ROOT abs_root = abs_tree.node( abs_root->children_index.front() ); } @@ -647,7 +671,7 @@ void MainWindow::on_actionSave_triggered() QFile file(fileName); if (file.open(QIODevice::WriteOnly)) { QTextStream stream(&file); - stream << xml_text << endl; + stream << xml_text; } directory_path = QFileInfo(fileName).absolutePath(); @@ -659,13 +683,30 @@ void MainWindow::onAutoArrange() currentTabInfo()->nodeReorder(); } +void MainWindow::onSaveSvg() +{ + QSettings settings; + QString last_load_path = settings.value("MainWindow.lastLoadDirectory", + QDir::homePath() ).toString(); + QString directory_path = settings.value("MainWindow.lastSaveSvgDirectory", + last_load_path ).toString(); + + QString fileName = QFileDialog::getSaveFileName(this, + tr("Save BehaviorTree to svg"), directory_path, + tr("SVG files (*.svg)")); + currentTabInfo()->saveSvgFile(fileName); + + directory_path = QFileInfo(fileName).absolutePath(); + settings.setValue("SidepanelEditor.lastSaveSvgDirectory", directory_path); +} + void MainWindow::onSceneChanged() { const bool valid_BT = currentTabInfo()->containsValidTree(); ui->toolButtonLayout->setEnabled(valid_BT); ui->toolButtonReorder->setEnabled(valid_BT); - ui->toolButtonReorder->setEnabled(valid_BT); + ui->toolButtonSaveSvg->setEnabled(valid_BT); ui->actionSave->setEnabled(valid_BT); QPixmap pix; @@ -723,6 +764,14 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *event) } } +void MainWindow::showEvent(QShowEvent *event) +{ + if (!event->spontaneous()) { + currentTabInfo()->zoomHomeView(); + ui->centralwidget->setFocus(); + } +} + void MainWindow::resizeEvent(QResizeEvent *) { on_splitter_splitterMoved(); @@ -1045,6 +1094,12 @@ QtNodes::Node* MainWindow::subTreeExpand(GraphicContainer &container, if( option == SUBTREE_EXPAND && subtree_model->expanded() == false) { auto subtree_container = getTabByName(subtree_name); + if (!subtree_container) { + QMessageBox::warning(this, tr("Oops!"), + tr("Couldn't get SubTree name from tabs and therefore can't expand."), + QMessageBox::Cancel); + return &node; + } // Prevent expansion of invalid subtree if( !subtree_container->containsValidTree() ) @@ -1129,6 +1184,11 @@ void MainWindow::on_toolButtonReorder_pressed() onAutoArrange(); } +void MainWindow::on_toolButtonSaveSvg_pressed() +{ + onSaveSvg(); +} + void MainWindow::on_toolButtonCenterView_pressed() { currentTabInfo()->zoomHomeView(); @@ -1275,6 +1335,7 @@ void MainWindow::updateCurrentMode() ui->toolButtonSaveFile->setHidden( NOT_EDITOR ); ui->toolButtonReorder->setHidden( NOT_EDITOR ); + ui->toolButtonSaveSvg->setHidden( NOT_EDITOR ); if( _current_mode == GraphicMode::EDITOR ) { @@ -1705,3 +1766,15 @@ GraphicMode MainWindow::getGraphicMode(void) const { return _current_mode; } + +void MainWindow::onSubtreeSelected(const QString& subtreeName) +{ + for (int i = 0; i < ui->tabWidget->tabBar()->count(); ++i) + { + if (ui->tabWidget->tabBar()->tabText(i) == subtreeName) + { + ui->tabWidget->tabBar()->setCurrentIndex(i); + return; + } + } +} diff --git a/bt_editor/mainwindow.h b/bt_editor/mainwindow.h index ebfd2c70..56af8823 100644 --- a/bt_editor/mainwindow.h +++ b/bt_editor/mainwindow.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -42,7 +43,12 @@ class MainWindow : public QMainWindow SUBTREE_REFRESH}; public: - explicit MainWindow(GraphicMode initial_mode, QWidget *parent = nullptr); + explicit MainWindow(GraphicMode initial_mode, + const QString& monitor_address = "", + const QString& monitor_pub_port = "", + const QString& monitor_srv_port = "", + const bool monitor_autoconnect = false, + QWidget *parent = nullptr); ~MainWindow() override; void loadFromXML(const QString &xml_text); @@ -65,6 +71,8 @@ public slots: void onAutoArrange(); + void onSaveSvg(); + void onSceneChanged(); void onPushUndo(); @@ -90,10 +98,14 @@ public slots: void on_actionSave_triggered(); + void onSubtreeSelected(const QString& subtreeName); + void on_splitter_splitterMoved(int pos = 0, int index = 0); void on_toolButtonReorder_pressed(); + void on_toolButtonSaveSvg_pressed(); + void on_toolButtonCenterView_pressed(); void onCreateAbsBehaviorTree(const AbsBehaviorTree &tree, @@ -142,6 +154,8 @@ private slots: bool eventFilter(QObject *obj, QEvent *event) override; + void showEvent(QShowEvent *event) override; + void resizeEvent(QResizeEvent *) override; GraphicContainer* createTab(const QString &name); @@ -197,7 +211,12 @@ private slots: #ifdef ZMQ_FOUND SidepanelMonitor* _monitor_widget; #endif - + + QString _monitor_address; + QString _monitor_publisher_port; + QString _monitor_server_port; + bool _monitor_autoconnect; + MainWindow::SavedState saveCurrentState(); void clearUndoStacks(); }; diff --git a/bt_editor/mainwindow.ui b/bt_editor/mainwindow.ui index 80594ee2..8410449b 100644 --- a/bt_editor/mainwindow.ui +++ b/bt_editor/mainwindow.ui @@ -511,8 +511,7 @@ QToolButton:disabled{ true - - + false @@ -577,6 +576,71 @@ QToolButton:disabled{ + + + + false + + + + 80 + 70 + + + + + 80 + 70 + + + + + 9 + + + + Qt::NoFocus + + + QToolButton { + color:white; +} + +QToolButton:hover{ + background-color: rgb(110, 110, 110); +} + +QToolButton:pressed{ + background-color: rgb(50, 150, 0) +} + +QToolButton:disabled{ + color:gray; + background-color: rgb(50, 50, 50) +} + + + + Save svg + + + + :/icons/svg/save_white.svg:/icons/svg/save_white.svg + + + + 32 + 32 + + + + Qt::ToolButtonTextUnderIcon + + + true + + + diff --git a/bt_editor/resources/NodesStyle.json b/bt_editor/resources/NodesStyle.json index efeccc5e..a0fd76dc 100644 --- a/bt_editor/resources/NodesStyle.json +++ b/bt_editor/resources/NodesStyle.json @@ -42,7 +42,7 @@ "icon": ":/icons/svg/edit_list.svg" }, - "RetryUntilSuccesful": { + "RetryUntilSuccessful": { "icon": ":/icons/svg/retry.svg" }, diff --git a/bt_editor/sidepanel_editor.cpp b/bt_editor/sidepanel_editor.cpp index 7f5c433b..775f50a0 100644 --- a/bt_editor/sidepanel_editor.cpp +++ b/bt_editor/sidepanel_editor.cpp @@ -29,6 +29,9 @@ SidepanelEditor::SidepanelEditor(QtNodes::DataModelRegistry *registry, connect( ui->paletteTreeWidget, &QWidget::customContextMenuRequested, this, &SidepanelEditor::onContextMenu); + connect(ui->paletteTreeWidget, &QTreeWidget::itemDoubleClicked, + this, &SidepanelEditor::onDoubleClick); + auto table_header = ui->portsTableWidget->horizontalHeader(); table_header->setSectionResizeMode(0, QHeaderView::ResizeToContents); @@ -462,3 +465,19 @@ void SidepanelEditor::on_buttonLock_toggled(bool locked) ui->buttonLock->setIcon( locked ? icon_locked : icon_unlocked); updateTreeView(); } + +void SidepanelEditor::onDoubleClick(QTreeWidgetItem *item, int column) +{ + QString selected_name = item->text(0); + + if( ui->buttonLock->isChecked() || + BuiltinNodeModels().count( selected_name ) != 0 ) + { + return; + } + + if (item->parent() && item->parent()->text(0) == "SubTree") + { + emit setTabScope(selected_name); + } +} diff --git a/bt_editor/sidepanel_editor.h b/bt_editor/sidepanel_editor.h index 8897d4bc..54d78c06 100644 --- a/bt_editor/sidepanel_editor.h +++ b/bt_editor/sidepanel_editor.h @@ -47,6 +47,8 @@ private slots: void on_buttonLock_toggled(bool checked); + void onDoubleClick(QTreeWidgetItem *item, int column); + signals: void addNewModel(const NodeModel &new_model); @@ -61,6 +63,8 @@ private slots: void destroySubtree(QString ID); + void setTabScope(const QString& subtreeName); + private: Ui::SidepanelEditor *ui; NodeModels &_tree_nodes_model; diff --git a/bt_editor/sidepanel_monitor.cpp b/bt_editor/sidepanel_monitor.cpp index acd0fe20..9164661d 100644 --- a/bt_editor/sidepanel_monitor.cpp +++ b/bt_editor/sidepanel_monitor.cpp @@ -10,7 +10,10 @@ #include "mainwindow.h" #include "utils.h" -SidepanelMonitor::SidepanelMonitor(QWidget *parent) : +SidepanelMonitor::SidepanelMonitor(QWidget *parent, + const QString &address, + const QString &publisher_port, + const QString &server_port) : QFrame(parent), ui(new Ui::SidepanelMonitor), _zmq_context(1), @@ -20,8 +23,22 @@ SidepanelMonitor::SidepanelMonitor(QWidget *parent) : _parent(parent) { ui->setupUi(this); - _timer = new QTimer(this); + this->set_load_tree_timeout_ms(_load_tree_default_timeout_ms); + if ( !address.isEmpty() ) + { + ui->lineEdit_address->setText(address); + } + if ( !publisher_port.isEmpty() ) + { + ui->lineEdit_publisher->setText(publisher_port); + } + if ( !server_port.isEmpty() ) + { + ui->lineEdit_server->setText(server_port); + } + + _timer = new QTimer(this); connect( _timer, &QTimer::timeout, this, &SidepanelMonitor::on_timer ); } @@ -41,7 +58,7 @@ void SidepanelMonitor::on_timer() zmq::message_t msg; try{ - while( _zmq_subscriber.recv(&msg) ) + while( _zmq_subscriber.recv(msg) ) { _msg_count++; ui->labelCount->setText( QString("Messages received: %1").arg(_msg_count) ); @@ -50,7 +67,7 @@ void SidepanelMonitor::on_timer() const uint32_t header_size = flatbuffers::ReadScalar( buffer ); const uint32_t num_transitions = flatbuffers::ReadScalar( &buffer[4+header_size] ); - + std::vector> node_status; // check uid in the index, if failed load tree from server try{ @@ -59,7 +76,7 @@ void SidepanelMonitor::on_timer() const uint16_t uid = flatbuffers::ReadScalar(&buffer[offset]); _uid_to_index.at(uid); } - + for(size_t t=0; t < num_transitions; t++) { size_t offset = 8 + header_size + 12*t; @@ -97,7 +114,7 @@ void SidepanelMonitor::on_timer() qDebug() << "Reload tree from server"; if( !getTreeFromServer() ) { _connected = false; - ui->lineEdit->setDisabled(false); + ui->lineEdit_address->setDisabled(false); _timer->stop(); connectionUpdate(false); return; @@ -127,13 +144,12 @@ bool SidepanelMonitor::getTreeFromServer() zmq::socket_t zmq_client( _zmq_context, ZMQ_REQ ); zmq_client.connect( _connection_address_req.c_str() ); - int timeout_ms = 1000; - zmq_client.setsockopt(ZMQ_RCVTIMEO,&timeout_ms, sizeof(int) ); + zmq_client.setsockopt(ZMQ_RCVTIMEO, &_load_tree_timeout_ms, sizeof(int) ); - zmq_client.send(request); + zmq_client.send(request, zmq::send_flags::none); - bool received = zmq_client.recv(&reply); - if( ! received ) + auto bytes_received = zmq_client.recv(reply, zmq::recv_flags::none); + if( !bytes_received || *bytes_received == 0 ) { return false; } @@ -187,13 +203,13 @@ bool SidepanelMonitor::getTreeFromServer() void SidepanelMonitor::on_Connect() { - if( !_connected) + if( !_connected ) { - QString address = ui->lineEdit->text(); + QString address = ui->lineEdit_address->text(); if( address.isEmpty() ) { - address = ui->lineEdit->placeholderText(); - ui->lineEdit->setText(address); + address = ui->lineEdit_address->placeholderText(); + ui->lineEdit_address->setText(address); } QString publisher_port = ui->lineEdit_publisher->text(); @@ -221,13 +237,16 @@ void SidepanelMonitor::on_Connect() int timeout_ms = 1; _zmq_subscriber.setsockopt(ZMQ_SUBSCRIBE, "", 0); - _zmq_subscriber.setsockopt(ZMQ_RCVTIMEO,&timeout_ms, sizeof(int) ); + _zmq_subscriber.setsockopt(ZMQ_RCVTIMEO, &timeout_ms, sizeof(int) ); if( !getTreeFromServer() ) { failed = true; _connected = false; } + // After we try get a tree on connect, reset to the default timeout. + // This is done so that we only use the increased autoconnect timeout once. + this->set_load_tree_timeout_ms(_load_tree_default_timeout_ms); } catch(zmq::error_t& err) { @@ -241,9 +260,9 @@ void SidepanelMonitor::on_Connect() if( !failed ) { _connected = true; - ui->lineEdit->setDisabled(true); + ui->lineEdit_address->setDisabled(true); ui->lineEdit_publisher->setDisabled(true); - _timer->start(20); + _timer->start(_timer_period_ms); connectionUpdate(true); } else{ @@ -255,7 +274,7 @@ void SidepanelMonitor::on_Connect() } else{ _connected = false; - ui->lineEdit->setDisabled(false); + ui->lineEdit_address->setDisabled(false); ui->lineEdit_publisher->setDisabled(false); _timer->stop(); diff --git a/bt_editor/sidepanel_monitor.h b/bt_editor/sidepanel_monitor.h index 682cd1c5..5d16795f 100644 --- a/bt_editor/sidepanel_monitor.h +++ b/bt_editor/sidepanel_monitor.h @@ -15,11 +15,26 @@ class SidepanelMonitor : public QFrame Q_OBJECT public: - explicit SidepanelMonitor(QWidget *parent = nullptr); + /// Timer period in milliseconds. + static constexpr int _timer_period_ms = 20; + /// Default timeout to get behavior tree, in milliseconds. + static constexpr int _load_tree_default_timeout_ms = 1000; + /// Timeout to get behavior tree during autoconnect, in milliseconds. + static constexpr int _load_tree_autoconnect_timeout_ms = 10000; + + explicit SidepanelMonitor(QWidget *parent = nullptr, + const QString &address = "", + const QString &publisher_port = "", + const QString &server_port = ""); ~SidepanelMonitor(); void clear(); + void set_load_tree_timeout_ms(const int timeout_ms) + { + _load_tree_timeout_ms = timeout_ms; + }; + public slots: void on_Connect(); @@ -44,11 +59,14 @@ private slots: zmq::context_t _zmq_context; zmq::socket_t _zmq_subscriber; + QTimer* _timer; + bool _connected; std::string _connection_address_pub; std::string _connection_address_req; - QTimer* _timer; int _msg_count; + + int _load_tree_timeout_ms; // Timeout to get behavior tree. AbsBehaviorTree _loaded_tree; std::unordered_map _uid_to_index; diff --git a/bt_editor/sidepanel_monitor.ui b/bt_editor/sidepanel_monitor.ui index 37e58287..faad639f 100644 --- a/bt_editor/sidepanel_monitor.ui +++ b/bt_editor/sidepanel_monitor.ui @@ -39,7 +39,7 @@ - + 16777215 diff --git a/depend/BehaviorTree.CPP b/depend/BehaviorTree.CPP index 59c1a809..73e10fb8 160000 --- a/depend/BehaviorTree.CPP +++ b/depend/BehaviorTree.CPP @@ -1 +1 @@ -Subproject commit 59c1a809a60d3c077301e9e5a1789334406b15a1 +Subproject commit 73e10fb8b2a7088521352d6d11fcd8633ba6036c diff --git a/package.xml b/package.xml index 50a9de59..010b5115 100644 --- a/package.xml +++ b/package.xml @@ -14,6 +14,7 @@ catkin cmake_modules ament_cmake + ament_index_cpp behaviortree_cpp_v3 qtbase5-dev @@ -27,7 +28,7 @@ libdw-dev libzmq3-dev - behaviortree_cpp_v3 + behaviortree_cpp_v3 catkin diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4b2fdee6..126d5598 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -15,7 +15,7 @@ ENABLE_TESTING() function(CompileTest name) add_executable(${name} ${name}.cpp groot_test_base.cpp ${RESOURCE_FILES} ) target_link_libraries(${name} PRIVATE Qt5::Gui Qt5::Test behavior_tree_editor) - add_test(${name} COMMAND ${name}) + add_test(NAME ${name} COMMAND ${name}) endfunction() set(RESOURCE_FILES @@ -27,4 +27,3 @@ set(RESOURCE_FILES CompileTest( editor_test ) CompileTest( replay_test ) - diff --git a/test_data/Simple.xml b/test_data/Simple.xml index 7ab3d915..99d33cb4 100644 --- a/test_data/Simple.xml +++ b/test_data/Simple.xml @@ -2,9 +2,9 @@ - + - + diff --git a/test_data/crossdoor_with_subtree.xml b/test_data/crossdoor_with_subtree.xml index 5d02f311..19f9f0f2 100644 --- a/test_data/crossdoor_with_subtree.xml +++ b/test_data/crossdoor_with_subtree.xml @@ -6,9 +6,9 @@ - + - + diff --git a/test_data/show_all.xml b/test_data/show_all.xml index 89daa0ed..d2b1f149 100644 --- a/test_data/show_all.xml +++ b/test_data/show_all.xml @@ -10,11 +10,11 @@ - + - +