From 0c68c1b35df4b24bd2f06efe4f7b4af94aa2c83f Mon Sep 17 00:00:00 2001 From: LolaSegura Date: Thu, 10 Jun 2021 16:33:30 -0300 Subject: [PATCH 1/9] New teleop plugin implementation. Signed-off-by: LolaSegura --- src/plugins/CMakeLists.txt | 1 + src/plugins/teleop/CMakeLists.txt | 8 + src/plugins/teleop/Teleop.cc | 269 ++++++++++++++++++++ src/plugins/teleop/Teleop.hh | 125 +++++++++ src/plugins/teleop/Teleop.qml | 403 ++++++++++++++++++++++++++++++ src/plugins/teleop/Teleop.qrc | 5 + 6 files changed, 811 insertions(+) create mode 100644 src/plugins/teleop/CMakeLists.txt create mode 100644 src/plugins/teleop/Teleop.cc create mode 100644 src/plugins/teleop/Teleop.hh create mode 100644 src/plugins/teleop/Teleop.qml create mode 100644 src/plugins/teleop/Teleop.qrc diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index e5d6c9fb3..3a82d2945 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -120,6 +120,7 @@ add_subdirectory(key_publisher) add_subdirectory(publisher) add_subdirectory(scene3d) add_subdirectory(screenshot) +add_subdirectory(teleop) add_subdirectory(topic_echo) add_subdirectory(topic_viewer) add_subdirectory(world_control) diff --git a/src/plugins/teleop/CMakeLists.txt b/src/plugins/teleop/CMakeLists.txt new file mode 100644 index 000000000..01305cbdd --- /dev/null +++ b/src/plugins/teleop/CMakeLists.txt @@ -0,0 +1,8 @@ +ign_gui_add_plugin(Teleop + SOURCES + Teleop.cc + QT_HEADERS + Teleop.hh + TEST_SOURCES # todo + #Teleop_TEST.cc +) diff --git a/src/plugins/teleop/Teleop.cc b/src/plugins/teleop/Teleop.cc new file mode 100644 index 000000000..171bc444d --- /dev/null +++ b/src/plugins/teleop/Teleop.cc @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#ifdef _MSC_VER +#pragma warning(push, 0) +#endif +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include + +#include +#include +#include + +#include "Teleop.hh" + +namespace ignition +{ +namespace gui +{ + enum class keyLinear{ + forward, + backward, + stop, + }; + + enum class keyAngular{ + left, + right, + stop, + }; + + class TeleopPrivate + { + /// \brief Node for communication. + public: ignition::transport::Node node; + + /// \brief Topic. Set '/cmd_vel' as default. + public: std::string topic = "/cmd_vel"; + + /// \brief Publisher. + ignition::transport::Node::Publisher cmdVelPub; + + /// \brief Linear velocity. + public: float linearVel = 0; + /// \brief Angular velocity. + public: float angularVel = 0; + + /// \brief Linear direction. + public: int linearDir = 0; + /// \brief Angular direction. + public: int angularDir = 0; + + /// \brief Linear state setted by keyboard input. + public: keyLinear linearState = keyLinear::stop; + /// \brief Angular state setted by keyboard input. + public: keyAngular angularState = keyAngular::stop; + + public: bool keyEnable = false; + public: bool newTopic = true; + + }; +} +} + +using namespace ignition; +using namespace gui; + +///////////////////////////////////////////////// +Teleop::Teleop(): Plugin(), dataPtr(new TeleopPrivate) +{ +} + +///////////////////////////////////////////////// +Teleop::~Teleop() +{ +} + +///////////////////////////////////////////////// +void Teleop::LoadConfig(const tinyxml2::XMLElement *) +{ + if (this->title.empty()) + this->title = "Teleop"; + + ignition::gui::App()->findChild + ()->QuickWindow()->installEventFilter(this); +} + +///////////////////////////////////////////////// +void Teleop::OnTeleopTwist() +{ + ignition::msgs::Twist cmdVelMsg; + + cmdVelMsg.mutable_linear()->set_x( + this->dataPtr->linearDir * this->dataPtr->linearVel); + cmdVelMsg.mutable_angular()->set_z( + this->dataPtr->angularDir * this->dataPtr->angularVel); + + if (this->dataPtr->newTopic){ + this->dataPtr->cmdVelPub = ignition::transport::Node::Publisher(); + this->dataPtr->cmdVelPub = + this->dataPtr->node.Advertise(this->dataPtr->topic); + this->dataPtr->newTopic = false; + } + + this->dataPtr->cmdVelPub.Publish(cmdVelMsg); +} + +///////////////////////////////////////////////// +void Teleop::OnTopicSelection(const QString& _topic) +{ + this->dataPtr->newTopic = true; + this->dataPtr->topic = _topic.toStdString(); + ignmsg << "[OnTopicSelection]: topic: " << this->dataPtr->topic << std::endl; + +} + +///////////////////////////////////////////////// +void Teleop::OnLinearVelSelection(const QString& _velocity) +{ + this->dataPtr->linearVel = _velocity.toDouble(); + ignmsg << "[OnlinearVelSelection]: linear velocity: " + << this->dataPtr->linearVel << std::endl; +} + +///////////////////////////////////////////////// +void Teleop::OnAngularVelSelection(const QString& _velocity) +{ + this->dataPtr->angularVel = _velocity.toDouble(); + ignmsg << "[OnlinearVelSelection]: angular velocity: " + << this->dataPtr->angularVel << std::endl; +} + +///////////////////////////////////////////////// +void Teleop::OnKeySwitch(bool _checked) +{ + this->dataPtr->linearDir = 0; + this->dataPtr->angularDir = 0; + this->dataPtr->keyEnable = _checked; +} + +///////////////////////////////////////////////// +void Teleop::OnSlidersSwitch(bool _checked) +{ + if(_checked){ + this->dataPtr->linearDir = 1; + this->dataPtr->angularDir = 1; + OnTeleopTwist(); + } +} + +///////////////////////////////////////////////// +bool Teleop::eventFilter(QObject *_obj, QEvent *_event) +{ + if(_event->type() == QEvent::KeyPress && this->dataPtr->keyEnable == true) + { + QKeyEvent *keyEvent = static_cast(_event); + switch(keyEvent->key()) + { + case Qt::Key_W: + this->dataPtr->linearState = keyLinear::forward; + break; + case Qt::Key_A: + this->dataPtr->angularState = keyAngular::left; + break; + case Qt::Key_D: + this->dataPtr->angularState = keyAngular::right; + break; + case Qt::Key_X: + this->dataPtr->linearState = keyLinear::backward; + break; + default: + ignmsg << "A non valid key was pressed" << std::endl; + break; + } + setKeyDirection(); + OnTeleopTwist(); + } + if(_event->type() == QEvent::KeyRelease && this->dataPtr->keyEnable == true) + { + QKeyEvent *keyEvent1 = static_cast(_event); + switch(keyEvent1->key()) + { + case Qt::Key_W: + this->dataPtr->linearState = keyLinear::stop; + break; + case Qt::Key_A: + this->dataPtr->angularState = keyAngular::stop; + break; + case Qt::Key_D: + this->dataPtr->angularState = keyAngular::stop; + break; + case Qt::Key_X: + this->dataPtr->linearState = keyLinear::stop; + break; + default: + ignmsg << "A non valid key was pressed" << std::endl; + break; + } + setKeyDirection(); + OnTeleopTwist(); + } + return QObject::eventFilter(_obj, _event); +} + +///////////////////////////////////////////////// +void Teleop::setKeyDirection() +{ + if(this->dataPtr->linearState == keyLinear::forward) + this->dataPtr->linearDir = 1; + else if(this->dataPtr->linearState == keyLinear::backward) + this->dataPtr->linearDir = -1; + else + this->dataPtr->linearDir = 0; + + if(this->dataPtr->angularState == keyAngular::left) + this->dataPtr->angularDir = 1; + else if(this->dataPtr->angularState == keyAngular::right) + this->dataPtr->angularDir = -1; + else + this->dataPtr->angularDir = 0; +} + +///////////////////////////////////////////////// +int Teleop::LinearDirection() const +{ + return this->dataPtr->linearDir; +} + +///////////////////////////////////////////////// +void Teleop::setLinearDirection(int _linearDir) +{ + this->dataPtr->linearDir = _linearDir; + this->LinearDirectionChanged(); +} + +///////////////////////////////////////////////// +int Teleop::AngularDirection() const +{ + return this->dataPtr->angularDir; +} + +///////////////////////////////////////////////// +void Teleop::setAngularDirection(int _angularDir) +{ + this->dataPtr->angularDir = _angularDir; + this->AngularDirectionChanged(); +} + +// Register this plugin +IGNITION_ADD_PLUGIN(ignition::gui::Teleop, + ignition::gui::Plugin) diff --git a/src/plugins/teleop/Teleop.hh b/src/plugins/teleop/Teleop.hh new file mode 100644 index 000000000..da8124d09 --- /dev/null +++ b/src/plugins/teleop/Teleop.hh @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#ifndef IGNITION_GUI_PLUGINS_TELEOP_HH_ +#define IGNITION_GUI_PLUGINS_TELEOP_HH_ + +#include + +#include + +#include +#include + +namespace ignition +{ +namespace gui +{ + class TeleopPrivate; + + /// \brief Publish teleop stokes to a user selected topic, + /// or to '/cmd_vel' if no topic is selected. + /// ## Configuration + /// This plugin doesn't accept any custom configuration. + class Teleop : public ignition::gui::Plugin + { + Q_OBJECT + + /// \brief Linear direction + Q_PROPERTY( + int linearDir + READ LinearDirection + WRITE setLinearDirection + NOTIFY LinearDirectionChanged + ) + + /// \brief Angular direction + Q_PROPERTY( + int angularDir + READ AngularDirection + WRITE setAngularDirection + NOTIFY AngularDirectionChanged + ) + + /// \brief Constructor + public: Teleop(); + + /// \brief Destructor + public: virtual ~Teleop(); + + // Documentation inherited + public: virtual void LoadConfig(const tinyxml2::XMLElement *) override; + + protected: bool eventFilter(QObject *_obj, QEvent *_event) override; + + /// \brief Callback in Qt thread when the direction of the movement changes. + public slots: void OnTeleopTwist(); + + /// \brief Returns the linear direction variable value. + /// When the movement is forward it takes the value 1 and when + /// is backward it takes the value -1. + public: Q_INVOKABLE int LinearDirection() const; + + /// \brief Set the linear direction of the movement. + public: Q_INVOKABLE void setLinearDirection(int _linearDir); + + /// \brief Notify that the linear direction changed. + signals: void LinearDirectionChanged(); + + /// \brief Returns the angular direction variable value. When the turn is + /// left it takes the value 1 and when is right it takes the value -1. + public: Q_INVOKABLE int AngularDirection() const; + + /// \brief Set the angular direction of the movement. + public: Q_INVOKABLE void setAngularDirection(int _angularDir); + + /// \brief Notify that the angular direction changed. + signals: void AngularDirectionChanged(); + + /// \brief Callback in Qt thread when the topic changes. + /// \param[in] _topic variable to indicate the topic to + /// publish the Twist commands. + public slots: void OnTopicSelection(const QString& _topic); + + /// \brief Callback in Qt thread when the linear velocity changes. + /// \param[in] _velocity variable to indicate the linear velocity. + public slots: void OnLinearVelSelection(const QString& _velocity); + + /// \brief Callback in Qt thread when the angular velocity changes. + /// \param[in] _velocity variable to indicate the angular velocity. + public slots: void OnAngularVelSelection(const QString& _velocity); + + /// \brief Callback in Qt thread when the keyboard is enabled or disabled. + /// \param[in] _checked variable to indicate the state of the switch. + public slots: void OnKeySwitch(bool _checked); + + /// \brief Callback in Qt thread when the sliders is enabled or disabled. + /// \param[in] _checked variable to indicate the state of the switch. + public slots: void OnSlidersSwitch(bool _checked); + + /// \brief Sets the movement direction when the keyboard is used. + public: void setKeyDirection(); + + /// \internal + /// \brief Pointer to private data. + private: std::unique_ptr dataPtr; + + }; +} +} + +#endif diff --git a/src/plugins/teleop/Teleop.qml b/src/plugins/teleop/Teleop.qml new file mode 100644 index 000000000..52f1c7a55 --- /dev/null +++ b/src/plugins/teleop/Teleop.qml @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +import QtQuick 2.9 +import QtQuick.Controls 2.2 +import QtQuick.Controls.Material 2.1 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.3 + +Rectangle { + color:"transparent" + Layout.minimumWidth: 300 + Layout.minimumHeight: 900 + anchors.fill: parent + focus: true + + // Topic input + Label { + id: topicLabel + text: "Topic:" + anchors.top: parent.top + anchors.topMargin: 10 + anchors.left: parent.left + anchors.leftMargin: 5 + } + TextField { + id: topicField + anchors.top: topicLabel.bottom + anchors.topMargin: 5 + anchors.left: parent.left + anchors.leftMargin: 5 + Layout.fillWidth: true + text:"/cmd_vel" + placeholderText: qsTr("Topic to publish...") + onEditingFinished: { + Teleop.OnTopicSelection(text) + } + } + + // Linear velocity input + Label { + id: linearVelLabel + text: "Linear:" + anchors.top: topicField.bottom + anchors.topMargin: 10 + anchors.left: parent.left + anchors.leftMargin: 5 + } + TextField { + id: linearVelField + anchors.top: linearVelLabel.bottom + anchors.topMargin: 5 + anchors.left: parent.left + anchors.leftMargin: 5 + Layout.fillWidth: true + text:"0.0" + placeholderText: qsTr("Linear velocity...") + onEditingFinished: { + Teleop.OnLinearVelSelection(text) + } + } + + // Angular velocity input + Label { + id: angularVelLabel + text: "Angular:" + anchors.top: linearVelField.bottom + anchors.topMargin: 10 + anchors.left: parent.left + anchors.leftMargin: 5 + } + TextField { + id: angularVelField + anchors.top: angularVelLabel.bottom + anchors.topMargin: 5 + anchors.left: parent.left + anchors.leftMargin: 5 + Layout.fillWidth: true + text:"0.0" + placeholderText: qsTr("Angular velocity...") + onEditingFinished: { + Teleop.OnAngularVelSelection(text) + } + } + + // Button grid + GridLayout { + id: buttonsGrid + anchors.top: angularVelField.bottom + anchors.topMargin: 15 + anchors.left: parent.left + anchors.leftMargin: 40 + Layout.fillWidth: true + columns: 4 + Button { + id: forwardButton + text: "\u25B2" + checkable: true + Layout.row: 0 + Layout.column: 1 + checked: false + onClicked: { + Teleop.linearDir = forwardButton.checked ? 1 : 0 + if(backwardButton.checked) + backwardButton.checked = false + Teleop.OnTeleopTwist() + } + ToolTip.visible: hovered + ToolTip.text: "Forward" + Material.background: Material.primary + contentItem: Label { + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + font.family: "Helvetica" + font.pointSize: 10 + color: "black" + text: forwardButton.text + } + } + Button { + id: leftButton + text: "\u25C0" + checkable: true + Layout.row: 1 + Layout.column: 0 + checked: false + onClicked: { + Teleop.angularDir = leftButton.checked ? 1 : 0 + if(rightButton.checked) + rightButton.checked = false + Teleop.OnTeleopTwist() + } + Material.background: Material.primary + contentItem: Label { + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + font.family: "Helvetica" + font.pointSize: 10 + color: "black" + text: leftButton.text + } + } + Button { + id: rightButton + text: "\u25B6" + checkable: true + Layout.row: 1 + Layout.column: 2 + checked: false + onClicked: { + Teleop.angularDir = rightButton.checked ? -1 : 0 + if(leftButton.checked) + leftButton.checked = false + Teleop.OnTeleopTwist() + } + Material.background: Material.primary + contentItem: Label { + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + font.family: "Helvetica" + font.pointSize: 10 + color: "black" + text: rightButton.text + } + } + Button { + id: backwardButton + text: "\u25BC" + checkable: true + Layout.row: 2 + Layout.column: 1 + checked: false + onClicked: { + Teleop.linearDir = backwardButton.checked ? -1 : 0 + if(forwardButton.checked) + forwardButton.checked = false + Teleop.OnTeleopTwist() + } + Material.background: Material.primary + contentItem: Label { + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + font.family: "Helvetica" + font.pointSize: 10 + color: "black" + text: backwardButton.text + } + } + Button { + id: stopButton + text: "Stop" + checkable: false + Layout.row: 1 + Layout.column: 1 + onClicked: { + Teleop.linearDir = 0 + Teleop.angularDir = 0 + forwardButton.checked = false + leftButton.checked = false + rightButton.checked = false + backwardButton.checked = false + linearVelSlider.value = 0 + angularVelSlider.value = 0 + slidersSwitch.checked = false + Teleop.OnTeleopTwist() + } + Material.background: Material.primary + contentItem: Label { + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + font.family: "Helvetica" + font.pointSize: 10 + color: "black" + text: stopButton.text + } + } + } + + //Keyboard's switch + Switch { + id: keySwitch + anchors.top: buttonsGrid.bottom + anchors.topMargin: 10 + anchors.left: parent.left + anchors.leftMargin: 5 + onClicked: { + forwardButton.checked = false + leftButton.checked = false + rightButton.checked = false + backwardButton.checked = false + Teleop.OnKeySwitch(checked); + } + ToolTip.visible: hovered + ToolTip.text: checked ? qsTr("Disable keyboard") : qsTr("Enable keyboard") + } + Label { + id: keyboardSwitchLabel + text: "Input from keyboard" + anchors.horizontalCenter : keySwitch.horizontalCenter + anchors.verticalCenter : keySwitch.verticalCenter + anchors.left: keySwitch.right + anchors.leftMargin: 5 + } + + // Slider's switch + Switch { + id: slidersSwitch + anchors.top: keySwitch.bottom + anchors.topMargin: 10 + anchors.left: keySwitch.left + onClicked: { + Teleop.OnSlidersSwitch(checked); + if(checked){ + forwardButton.checked = false + leftButton.checked = false + rightButton.checked = false + backwardButton.checked = false + linearVelField.text = linearVelSlider.value.toFixed(2) + angularVelField.text = angularVelSlider.value.toFixed(2) + Teleop.OnLinearVelSelection(linearVelSlider.value) + Teleop.OnAngularVelSelection(angularVelSlider.value) + Teleop.OnTeleopTwist() + } + } + ToolTip.visible: hovered + ToolTip.text: checked ? qsTr("Disable sliders") : qsTr("Enable sliders") + } + + Label { + id: slidersSwitchLabel + text: "Input from sliders" + anchors.horizontalCenter : slidersSwitch.horizontalCenter + anchors.verticalCenter : slidersSwitch.verticalCenter + anchors.left: slidersSwitch.right + anchors.leftMargin: 5 + } + + TextField { + id: linearVelMaxTextField + anchors.top: slidersSwitch.bottom + anchors.topMargin: 10 + anchors.horizontalCenter : angularVelSlider.horizontalCenter + width: 40 + text:"1.0" + } + + // Vertical slider + Slider { + id: linearVelSlider + height: 150 + width: 50 + orientation: Qt.Vertical + anchors.top: linearVelMaxTextField.bottom + anchors.horizontalCenter : angularVelSlider.horizontalCenter + handle: Rectangle { + y: linearVelSlider.topPadding + linearVelSlider.visualPosition * (linearVelSlider.availableHeight - height) + x: linearVelSlider.leftPadding + linearVelSlider.availableWidth / 2 - width / 2 + implicitWidth: 25 + implicitHeight: 10 + color: linearVelSlider.pressed ? "#f0f0f0" : "#f6f6f6" + border.color: "black" + } + enabled: slidersSwitch.checked + + from: linearVelMinTextField.text + to: linearVelMaxTextField.text + stepSize: 0.01 + + onMoved: { + linearVelField.text = linearVelSlider.value.toFixed(2) + Teleop.OnLinearVelSelection(linearVelSlider.value) + Teleop.OnTeleopTwist() + } + } + + TextField { + id: linearVelMinTextField + anchors.top: linearVelSlider.bottom + anchors.horizontalCenter : linearVelSlider.horizontalCenter + width: 40 + text:"-1.0" + } + + Label { + id: currentLinearVelSliderLabel + anchors.verticalCenter : linearVelSlider.verticalCenter + anchors.left : linearVelSlider.right + text: linearVelSlider.value.toFixed(2) + " m/s" + } + + TextField { + id: angularVelMinTextField + anchors.verticalCenter : angularVelSlider.verticalCenter + anchors.left : slidersSwitch.left + width: 40 + text:"-1.0" + } + + // Horizontal slider + Slider { + id: angularVelSlider + height: 50 + width: 175 + anchors.top: linearVelSlider.bottom + anchors.topMargin: 50 + anchors.left : angularVelMinTextField.right + anchors.leftMargin: 10 + handle: Rectangle { + x: angularVelSlider.leftPadding + angularVelSlider.visualPosition * (angularVelSlider.availableWidth - width) + y: angularVelSlider.topPadding + angularVelSlider.availableHeight / 2 - height / 2 + implicitWidth: 10 + implicitHeight: 25 + color: angularVelSlider.pressed ? "#f0f0f0" : "#f6f6f6" + border.color: "black" + } + enabled: slidersSwitch.checked + + from: angularVelMinTextField.text + to: angularVelMaxTextField.text + stepSize: 0.01 + + onMoved: { + angularVelField.text = angularVelSlider.value.toFixed(2) + Teleop.OnAngularVelSelection(angularVelSlider.value) + Teleop.OnTeleopTwist() + } + } + + TextField { + id: angularVelMaxTextField + anchors.verticalCenter : angularVelSlider.verticalCenter + anchors.left : angularVelSlider.right + anchors.leftMargin: 10 + width: 40 + text:"1.0" + } + + Label { + id: currentAngularVelSliderLabel + anchors.horizontalCenter : angularVelSlider.horizontalCenter + anchors.top : angularVelSlider.bottom + text: angularVelSlider.value.toFixed(2) + " rad/s" + } +} diff --git a/src/plugins/teleop/Teleop.qrc b/src/plugins/teleop/Teleop.qrc new file mode 100644 index 000000000..872ced8b3 --- /dev/null +++ b/src/plugins/teleop/Teleop.qrc @@ -0,0 +1,5 @@ + + + Teleop.qml + + From 3e898c6cb92b4bf5944d94956957e52e83ee404d Mon Sep 17 00:00:00 2001 From: LolaSegura Date: Thu, 1 Jul 2021 14:11:44 -0300 Subject: [PATCH 2/9] Added tests. Signed-off-by: LolaSegura --- src/plugins/teleop/CMakeLists.txt | 2 +- src/plugins/teleop/Teleop.cc | 9 +- src/plugins/teleop/Teleop.hh | 5 +- src/plugins/teleop/Teleop.qml | 4 - src/plugins/teleop/Teleop_TEST.cc | 443 ++++++++++++++++++++++++++++++ 5 files changed, 455 insertions(+), 8 deletions(-) create mode 100644 src/plugins/teleop/Teleop_TEST.cc diff --git a/src/plugins/teleop/CMakeLists.txt b/src/plugins/teleop/CMakeLists.txt index 01305cbdd..5a9e51f96 100644 --- a/src/plugins/teleop/CMakeLists.txt +++ b/src/plugins/teleop/CMakeLists.txt @@ -4,5 +4,5 @@ ign_gui_add_plugin(Teleop QT_HEADERS Teleop.hh TEST_SOURCES # todo - #Teleop_TEST.cc + Teleop_TEST.cc ) diff --git a/src/plugins/teleop/Teleop.cc b/src/plugins/teleop/Teleop.cc index 171bc444d..5a4c6a55d 100644 --- a/src/plugins/teleop/Teleop.cc +++ b/src/plugins/teleop/Teleop.cc @@ -15,6 +15,7 @@ * */ +#include #ifdef _MSC_VER #pragma warning(push, 0) #endif @@ -34,6 +35,8 @@ namespace ignition { namespace gui +{ +namespace plugins { enum class keyLinear{ forward, @@ -79,9 +82,11 @@ namespace gui }; } } +} using namespace ignition; using namespace gui; +using namespace plugins; ///////////////////////////////////////////////// Teleop::Teleop(): Plugin(), dataPtr(new TeleopPrivate) @@ -94,7 +99,7 @@ Teleop::~Teleop() } ///////////////////////////////////////////////// -void Teleop::LoadConfig(const tinyxml2::XMLElement *) +void Teleop::LoadConfig(const tinyxml2::XMLElement *_pluginElement) { if (this->title.empty()) this->title = "Teleop"; @@ -265,5 +270,5 @@ void Teleop::setAngularDirection(int _angularDir) } // Register this plugin -IGNITION_ADD_PLUGIN(ignition::gui::Teleop, +IGNITION_ADD_PLUGIN(ignition::gui::plugins::Teleop, ignition::gui::Plugin) diff --git a/src/plugins/teleop/Teleop.hh b/src/plugins/teleop/Teleop.hh index da8124d09..c7acfb6e8 100644 --- a/src/plugins/teleop/Teleop.hh +++ b/src/plugins/teleop/Teleop.hh @@ -28,6 +28,8 @@ namespace ignition { namespace gui +{ +namespace plugins { class TeleopPrivate; @@ -35,7 +37,7 @@ namespace gui /// or to '/cmd_vel' if no topic is selected. /// ## Configuration /// This plugin doesn't accept any custom configuration. - class Teleop : public ignition::gui::Plugin + class Teleop : public Plugin { Q_OBJECT @@ -121,5 +123,6 @@ namespace gui }; } } +} #endif diff --git a/src/plugins/teleop/Teleop.qml b/src/plugins/teleop/Teleop.qml index 52f1c7a55..0c47c862e 100644 --- a/src/plugins/teleop/Teleop.qml +++ b/src/plugins/teleop/Teleop.qml @@ -112,7 +112,6 @@ Rectangle { checkable: true Layout.row: 0 Layout.column: 1 - checked: false onClicked: { Teleop.linearDir = forwardButton.checked ? 1 : 0 if(backwardButton.checked) @@ -138,7 +137,6 @@ Rectangle { checkable: true Layout.row: 1 Layout.column: 0 - checked: false onClicked: { Teleop.angularDir = leftButton.checked ? 1 : 0 if(rightButton.checked) @@ -162,7 +160,6 @@ Rectangle { checkable: true Layout.row: 1 Layout.column: 2 - checked: false onClicked: { Teleop.angularDir = rightButton.checked ? -1 : 0 if(leftButton.checked) @@ -186,7 +183,6 @@ Rectangle { checkable: true Layout.row: 2 Layout.column: 1 - checked: false onClicked: { Teleop.linearDir = backwardButton.checked ? -1 : 0 if(forwardButton.checked) diff --git a/src/plugins/teleop/Teleop_TEST.cc b/src/plugins/teleop/Teleop_TEST.cc new file mode 100644 index 000000000..a7a2d3410 --- /dev/null +++ b/src/plugins/teleop/Teleop_TEST.cc @@ -0,0 +1,443 @@ +/* + * Copyright (C) 2017 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#include +#ifdef _MSC_VER +#pragma warning(push, 0) +#endif +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#include +#include + +#include "test_config.h" // NOLINT(build/include) +#include "ignition/gui/Application.hh" +#include "ignition/gui/Plugin.hh" +#include "ignition/gui/MainWindow.hh" +#include +#include + +#include "Teleop.hh" + +int g_argc = 1; +char **g_argv = new char *[g_argc]; + +using namespace ignition; +using namespace gui; + +class TeleopTest : public ::testing::Test { + + protected: + void SetUp() override { + common::Console::SetVerbosity(4); + } + + /// \param[in] _topic Topic to publish twist messages. + protected: void TestButtons(const std::string &_topic) + { + Application app(g_argc, g_argv); + app.AddPluginPath(std::string(PROJECT_BINARY_PATH) + "/lib"); + + // Load plugin + const char *pluginStr = + "" + "" + "Teleop!" + "" + ""; + + tinyxml2::XMLDocument pluginDoc; + EXPECT_EQ(tinyxml2::XML_SUCCESS, pluginDoc.Parse(pluginStr)); + EXPECT_TRUE(app.LoadPlugin("Teleop", + pluginDoc.FirstChildElement("plugin"))); + + // Get main window + auto win = app.findChild(); + ASSERT_NE(nullptr, win); + + // Show, but don't exec, so we don't block + win->QuickWindow()->show(); + + // Get plugin + auto plugins = win->findChildren(); + EXPECT_EQ(plugins.size(), 1); + + auto plugin = plugins[0]; + EXPECT_EQ(plugin->Title(), "Teleop!"); + + plugin->OnTopicSelection(QString::fromStdString(_topic)); + + // Checks if the directions of the movement are set + // with the default value. + EXPECT_EQ(plugin->LinearDirection(), 0); + EXPECT_EQ(plugin->AngularDirection(), 0); + + // Define velocity values. + double linearVel = 1.0; + double angularVel = 0.5; + + // Set velocity value and movement direction. + // Forward and left movement. + plugin->OnLinearVelSelection(QString::number(linearVel)); + plugin->OnAngularVelSelection(QString::number(angularVel)); + plugin->setLinearDirection(1); + plugin->setAngularDirection(1); + + // Subscribe to the 'cmd_vel' topic + bool received = false; + std::function cb = + [&](const msgs::Twist &_msg) + { + EXPECT_DOUBLE_EQ(_msg.linear().x(), + plugin->LinearDirection() * linearVel); + EXPECT_DOUBLE_EQ(_msg.angular().z(), + plugin->AngularDirection() * angularVel); + received = true; + }; + + transport::Node node; + node.Subscribe(_topic, cb); + plugin->OnTeleopTwist(); + + int sleep = 0; + int maxSleep = 30; + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } + + EXPECT_TRUE(received); + received = false; + + // Change movement direction. + // Backward and right movement. + plugin->setLinearDirection(-1); + plugin->setAngularDirection(-1); + plugin->OnTeleopTwist(); + + sleep = 0; + // cppcheck-suppress knownConditionTrueFalse + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } + + EXPECT_TRUE(received); + received = false; + + // Stops angular movement. + plugin->setAngularDirection(0); + plugin->OnTeleopTwist(); + + sleep = 0; + // cppcheck-suppress knownConditionTrueFalse + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } + + EXPECT_TRUE(received); + received = false; + + // Stops linear movement. + // Starts angular movement. + plugin->setLinearDirection(0); + plugin->setAngularDirection(1); + plugin->OnTeleopTwist(); + + sleep = 0; + // cppcheck-suppress knownConditionTrueFalse + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } + + EXPECT_TRUE(received); + received = false; + + // Stops movement. + plugin->setAngularDirection(0); + plugin->setLinearDirection(0); + plugin->OnTeleopTwist(); + + sleep = 0; + // cppcheck-suppress knownConditionTrueFalse + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } + + EXPECT_TRUE(received); + + // Cleanup + plugins.clear(); + }; + + /// \param[in] _topic Topic to publish twist messages. + protected: void TestKeys(const std::string &_topic) + { + Application app(g_argc, g_argv); + app.AddPluginPath(std::string(PROJECT_BINARY_PATH) + "/lib"); + + // Load plugin + const char *pluginStr = + "" + "" + "Teleop!" + "" + ""; + + tinyxml2::XMLDocument pluginDoc; + EXPECT_EQ(tinyxml2::XML_SUCCESS, pluginDoc.Parse(pluginStr)); + EXPECT_TRUE(app.LoadPlugin("Teleop", + pluginDoc.FirstChildElement("plugin"))); + + // Get main window + auto win = app.findChild(); + ASSERT_NE(nullptr, win); + + // Show, but don't exec, so we don't block + win->QuickWindow()->show(); + + // Get plugin + auto plugins = win->findChildren(); + EXPECT_EQ(plugins.size(), 1); + + auto plugin = plugins[0]; + EXPECT_EQ(plugin->Title(), "Teleop!"); + + // Set desire topic. + plugin->OnTopicSelection(QString::fromStdString(_topic)); + + // Movement direction + EXPECT_EQ(plugin->LinearDirection(), 0); + EXPECT_EQ(plugin->AngularDirection(), 0); + + // Define velocity values. + double linearVel = 1.0; + double angularVel = 0.5; + + // Set velocity value and movement direction. + plugin->OnLinearVelSelection(QString::number(linearVel)); + plugin->OnAngularVelSelection(QString::number(angularVel)); + + // Subscribe + bool received = false; + std::function cb = + [&](const msgs::Twist &_msg) + { + EXPECT_DOUBLE_EQ(_msg.linear().x(), + plugin->LinearDirection() * linearVel); + EXPECT_DOUBLE_EQ(_msg.angular().z(), + plugin->AngularDirection() * angularVel); + received = true; + }; + + transport::Node node; + node.Subscribe(_topic, cb); + + // Generates a key press event on the main window. + QKeyEvent *keypress_W = new QKeyEvent(QKeyEvent::KeyPress, + Qt::Key_W, Qt::NoModifier); + app.sendEvent(win->QuickWindow(), keypress_W); + QCoreApplication::processEvents(); + + EXPECT_FALSE(received); + + // Enables key input. + plugin->OnKeySwitch(true); + app.sendEvent(win->QuickWindow(), keypress_W); + + int sleep = 0; + int maxSleep = 30; + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } + + EXPECT_TRUE(received); + received = false; + + // Generates a key press event on the main window. + QKeyEvent *keypress_D = new QKeyEvent(QKeyEvent::KeyPress, + Qt::Key_D, Qt::NoModifier); + app.sendEvent(win->QuickWindow(), keypress_D); + + sleep = 0; + // cppcheck-suppress knownConditionTrueFalse + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } + + EXPECT_TRUE(received); + received = false; + + // Generates a key release event on the main window. + QKeyEvent *keyrelease_D = new QKeyEvent(QKeyEvent::KeyRelease, + Qt::Key_D, Qt::NoModifier); + app.sendEvent(win->QuickWindow(), keyrelease_D); + + sleep = 0; + // cppcheck-suppress knownConditionTrueFalse + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } + + EXPECT_TRUE(received); + received = false; + + // Generates a key press event on the main window. + QKeyEvent *keypress_A = new QKeyEvent(QKeyEvent::KeyPress, + Qt::Key_A, Qt::NoModifier); + app.sendEvent(win->QuickWindow(), keypress_A); + + sleep = 0; + // cppcheck-suppress knownConditionTrueFalse + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } + + EXPECT_TRUE(received); + received = false; + + // Generates a key release event on the main window. + QKeyEvent *keyrelease_A = new QKeyEvent(QKeyEvent::KeyRelease, + Qt::Key_A, Qt::NoModifier); + app.sendEvent(win->QuickWindow(), keyrelease_A); + + sleep = 0; + // cppcheck-suppress knownConditionTrueFalse + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } + + EXPECT_TRUE(received); + received = false; + // Generates a key release event on the main window. + QKeyEvent *keyrelease_W = new QKeyEvent(QKeyEvent::KeyRelease, + Qt::Key_W, Qt::NoModifier); + app.sendEvent(win->QuickWindow(), keyrelease_W); + + sleep = 0; + // cppcheck-suppress knownConditionTrueFalse + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } + + EXPECT_TRUE(received); + received = false; + // Generates a key press event on the main window. + QKeyEvent *keypress_X = new QKeyEvent(QKeyEvent::KeyPress, + Qt::Key_X, Qt::NoModifier); + app.sendEvent(win->QuickWindow(), keypress_X); + + sleep = 0; + // cppcheck-suppress knownConditionTrueFalse + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } + + EXPECT_TRUE(received); + received = false; + // Generates a key release event on the main window. + QKeyEvent *keyrelease_X = new QKeyEvent(QKeyEvent::KeyRelease, + Qt::Key_X, Qt::NoModifier); + app.sendEvent(win->QuickWindow(), keyrelease_X); + + sleep = 0; + // cppcheck-suppress knownConditionTrueFalse + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } + + // Cleanup + plugins.clear(); + }; + +}; + +///////////////////////////////////////////////// +TEST_F(TeleopTest, Load) +{ + Application app(g_argc, g_argv); + app.AddPluginPath(std::string(PROJECT_BINARY_PATH) + "/lib"); + + EXPECT_TRUE(app.LoadPlugin("Teleop")); + + // Get main window + auto win = app.findChild(); + ASSERT_NE(nullptr, win); + + // Get plugin + auto plugins = win->findChildren(); + EXPECT_EQ(plugins.size(), 1); + + auto plugin = plugins[0]; + EXPECT_EQ(plugin->Title(), "Teleop"); + + // Cleanup + plugins.clear(); +} + +///////////////////////////////////////////////// +TEST_F(TeleopTest, ButtonCommand) +{ + TestButtons("/cmd_vel"); + TestButtons("/model/vehicle_blue/cmd_vel"); +} + +///////////////////////////////////////////////// +TEST_F(TeleopTest, KeyboardCommand) +{ + TestKeys("/cmd_vel"); + TestKeys("/model/vehicle_blue/cmd_vel"); +} From ebbf1b579ecc276cc9c1ed79ebe0844c737419c0 Mon Sep 17 00:00:00 2001 From: LolaSegura Date: Thu, 8 Jul 2021 11:33:05 -0300 Subject: [PATCH 3/9] Addressed comments. Signed-off-by: LolaSegura --- src/plugins/teleop/CMakeLists.txt | 2 +- src/plugins/teleop/Teleop.cc | 138 +++++++++++++++--------------- src/plugins/teleop/Teleop.hh | 28 ++++-- src/plugins/teleop/Teleop_TEST.cc | 2 +- 4 files changed, 89 insertions(+), 81 deletions(-) diff --git a/src/plugins/teleop/CMakeLists.txt b/src/plugins/teleop/CMakeLists.txt index 5a9e51f96..64d158f26 100644 --- a/src/plugins/teleop/CMakeLists.txt +++ b/src/plugins/teleop/CMakeLists.txt @@ -3,6 +3,6 @@ ign_gui_add_plugin(Teleop Teleop.cc QT_HEADERS Teleop.hh - TEST_SOURCES # todo + TEST_SOURCES Teleop_TEST.cc ) diff --git a/src/plugins/teleop/Teleop.cc b/src/plugins/teleop/Teleop.cc index 5a4c6a55d..63def4896 100644 --- a/src/plugins/teleop/Teleop.cc +++ b/src/plugins/teleop/Teleop.cc @@ -24,13 +24,14 @@ #pragma warning(pop) #endif +#include "Teleop.hh" + #include -#include -#include #include -#include "Teleop.hh" +#include +#include namespace ignition { @@ -39,15 +40,15 @@ namespace gui namespace plugins { enum class keyLinear{ - forward, - backward, - stop, + kforward, + kbackward, + kstop, }; enum class keyAngular{ - left, - right, - stop, + kleft, + kright, + kstop, }; class TeleopPrivate @@ -59,12 +60,12 @@ namespace plugins public: std::string topic = "/cmd_vel"; /// \brief Publisher. - ignition::transport::Node::Publisher cmdVelPub; + public: ignition::transport::Node::Publisher cmdVelPub; /// \brief Linear velocity. - public: float linearVel = 0; + public: double linearVel = 0; /// \brief Angular velocity. - public: float angularVel = 0; + public: double angularVel = 0; /// \brief Linear direction. public: int linearDir = 0; @@ -72,9 +73,9 @@ namespace plugins public: int angularDir = 0; /// \brief Linear state setted by keyboard input. - public: keyLinear linearState = keyLinear::stop; + public: keyLinear linearState = keyLinear::kstop; /// \brief Angular state setted by keyboard input. - public: keyAngular angularState = keyAngular::stop; + public: keyAngular angularState = keyAngular::kstop; public: bool keyEnable = false; public: bool newTopic = true; @@ -123,6 +124,7 @@ void Teleop::OnTeleopTwist() this->dataPtr->cmdVelPub = this->dataPtr->node.Advertise(this->dataPtr->topic); this->dataPtr->newTopic = false; + this->dataPtr->cmdVelPub.Publish(cmdVelMsg); } this->dataPtr->cmdVelPub.Publish(cmdVelMsg); @@ -133,8 +135,8 @@ void Teleop::OnTopicSelection(const QString& _topic) { this->dataPtr->newTopic = true; this->dataPtr->topic = _topic.toStdString(); - ignmsg << "[OnTopicSelection]: topic: " << this->dataPtr->topic << std::endl; - + ignmsg << "A new topic has been entered: '" << + this->dataPtr->topic << " ' " <type() == QEvent::KeyPress && this->dataPtr->keyEnable == true) + if(this->dataPtr->keyEnable == true) { - QKeyEvent *keyEvent = static_cast(_event); - switch(keyEvent->key()) + if(_event->type() == QEvent::KeyPress) { - case Qt::Key_W: - this->dataPtr->linearState = keyLinear::forward; - break; - case Qt::Key_A: - this->dataPtr->angularState = keyAngular::left; - break; - case Qt::Key_D: - this->dataPtr->angularState = keyAngular::right; - break; - case Qt::Key_X: - this->dataPtr->linearState = keyLinear::backward; - break; - default: - ignmsg << "A non valid key was pressed" << std::endl; - break; + QKeyEvent *keyEvent = static_cast(_event); + switch(keyEvent->key()) + { + case Qt::Key_W: + this->dataPtr->linearState = keyLinear::kforward; + break; + case Qt::Key_A: + this->dataPtr->angularState = keyAngular::kleft; + break; + case Qt::Key_D: + this->dataPtr->angularState = keyAngular::kright; + break; + case Qt::Key_X: + this->dataPtr->linearState = keyLinear::kbackward; + break; + default: + break; + } + setKeyDirection(); + OnTeleopTwist(); } - setKeyDirection(); - OnTeleopTwist(); - } - if(_event->type() == QEvent::KeyRelease && this->dataPtr->keyEnable == true) - { - QKeyEvent *keyEvent1 = static_cast(_event); - switch(keyEvent1->key()) + + if(_event->type() == QEvent::KeyRelease) { - case Qt::Key_W: - this->dataPtr->linearState = keyLinear::stop; - break; - case Qt::Key_A: - this->dataPtr->angularState = keyAngular::stop; - break; - case Qt::Key_D: - this->dataPtr->angularState = keyAngular::stop; - break; - case Qt::Key_X: - this->dataPtr->linearState = keyLinear::stop; - break; - default: - ignmsg << "A non valid key was pressed" << std::endl; - break; + QKeyEvent *keyEvent = static_cast(_event); + switch(keyEvent->key()) + { + case Qt::Key_W: + this->dataPtr->linearState = keyLinear::kstop; + break; + case Qt::Key_A: + this->dataPtr->angularState = keyAngular::kstop; + break; + case Qt::Key_D: + this->dataPtr->angularState = keyAngular::kstop; + break; + case Qt::Key_X: + this->dataPtr->linearState = keyLinear::kstop; + break; + default: + break; + } + setKeyDirection(); + OnTeleopTwist(); } - setKeyDirection(); - OnTeleopTwist(); } return QObject::eventFilter(_obj, _event); } @@ -228,19 +232,13 @@ bool Teleop::eventFilter(QObject *_obj, QEvent *_event) ///////////////////////////////////////////////// void Teleop::setKeyDirection() { - if(this->dataPtr->linearState == keyLinear::forward) - this->dataPtr->linearDir = 1; - else if(this->dataPtr->linearState == keyLinear::backward) - this->dataPtr->linearDir = -1; - else - this->dataPtr->linearDir = 0; + this->dataPtr->linearDir = this->dataPtr->linearState == + keyLinear::kforward ? 1 : this->dataPtr->linearState == + keyLinear::kbackward ? -1 : 0; - if(this->dataPtr->angularState == keyAngular::left) - this->dataPtr->angularDir = 1; - else if(this->dataPtr->angularState == keyAngular::right) - this->dataPtr->angularDir = -1; - else - this->dataPtr->angularDir = 0; + this->dataPtr->angularDir = this->dataPtr->angularState == + keyAngular::kleft ? 1 : this->dataPtr->angularState == + keyAngular::kright ? -1 : 0; } ///////////////////////////////////////////////// diff --git a/src/plugins/teleop/Teleop.hh b/src/plugins/teleop/Teleop.hh index c7acfb6e8..ace4f0567 100644 --- a/src/plugins/teleop/Teleop.hh +++ b/src/plugins/teleop/Teleop.hh @@ -18,13 +18,13 @@ #ifndef IGNITION_GUI_PLUGINS_TELEOP_HH_ #define IGNITION_GUI_PLUGINS_TELEOP_HH_ -#include - #include -#include #include +#include +#include + namespace ignition { namespace gui @@ -35,6 +35,8 @@ namespace plugins /// \brief Publish teleop stokes to a user selected topic, /// or to '/cmd_vel' if no topic is selected. + /// Buttons, the keyboard or sliders can be used to move a + /// vehicle load to the world. /// ## Configuration /// This plugin doesn't accept any custom configuration. class Teleop : public Plugin @@ -63,30 +65,38 @@ namespace plugins /// \brief Destructor public: virtual ~Teleop(); - // Documentation inherited + // Documentation inherited. public: virtual void LoadConfig(const tinyxml2::XMLElement *) override; + /// \brief Filters events of type 'keypress' and 'keyrelease'. protected: bool eventFilter(QObject *_obj, QEvent *_event) override; - /// \brief Callback in Qt thread when the direction of the movement changes. + /// \brief Publish the twist message to the selected command velocity topic. public slots: void OnTeleopTwist(); /// \brief Returns the linear direction variable value. - /// When the movement is forward it takes the value 1 and when - /// is backward it takes the value -1. + /// When the movement is forward it takes the value 1, when + /// is backward it takes the value -1, and when it's 0 the + /// movement stops. public: Q_INVOKABLE int LinearDirection() const; /// \brief Set the linear direction of the movement. + /// \param[in] _linearDir Modifier of the velocity for setting + /// the movement direction. public: Q_INVOKABLE void setLinearDirection(int _linearDir); /// \brief Notify that the linear direction changed. signals: void LinearDirectionChanged(); - /// \brief Returns the angular direction variable value. When the turn is - /// left it takes the value 1 and when is right it takes the value -1. + /// \brief Returns the angular direction variable value. + /// When the turn is counterclockwise it takes the value 1, when + /// is clockwise it takes the value -1, and when it's 0 the + /// movement stops. public: Q_INVOKABLE int AngularDirection() const; /// \brief Set the angular direction of the movement. + /// \param[in] _angularDir Modifier of the velocity for setting + /// the direction of the rotation. public: Q_INVOKABLE void setAngularDirection(int _angularDir); /// \brief Notify that the angular direction changed. diff --git a/src/plugins/teleop/Teleop_TEST.cc b/src/plugins/teleop/Teleop_TEST.cc index a7a2d3410..d2149e7ac 100644 --- a/src/plugins/teleop/Teleop_TEST.cc +++ b/src/plugins/teleop/Teleop_TEST.cc @@ -23,7 +23,7 @@ #ifdef _MSC_VER #pragma warning(pop) #endif -#include + #include #include "test_config.h" // NOLINT(build/include) From dff8a1bff314f643da8faf823f3e7bf7b39d3db6 Mon Sep 17 00:00:00 2001 From: LolaSegura Date: Wed, 14 Jul 2021 14:48:43 -0300 Subject: [PATCH 4/9] Addressed comments. Signed-off-by: LolaSegura --- src/plugins/teleop/Teleop.cc | 39 +++++++++------- src/plugins/teleop/Teleop.hh | 2 +- src/plugins/teleop/Teleop.qml | 77 ++++++++++++++++++++----------- src/plugins/teleop/Teleop_TEST.cc | 1 - 4 files changed, 72 insertions(+), 47 deletions(-) diff --git a/src/plugins/teleop/Teleop.cc b/src/plugins/teleop/Teleop.cc index 63def4896..a21159722 100644 --- a/src/plugins/teleop/Teleop.cc +++ b/src/plugins/teleop/Teleop.cc @@ -77,9 +77,12 @@ namespace plugins /// \brief Angular state setted by keyboard input. public: keyAngular angularState = keyAngular::kstop; + /// \brief Indicates if the keyboard is enabled or + /// disabled. public: bool keyEnable = false; + /// \brief Indicates if a new topic has been + /// selected. public: bool newTopic = true; - }; } } @@ -100,7 +103,7 @@ Teleop::~Teleop() } ///////////////////////////////////////////////// -void Teleop::LoadConfig(const tinyxml2::XMLElement *_pluginElement) +void Teleop::LoadConfig(const tinyxml2::XMLElement *) { if (this->title.empty()) this->title = "Teleop"; @@ -119,19 +122,20 @@ void Teleop::OnTeleopTwist() cmdVelMsg.mutable_angular()->set_z( this->dataPtr->angularDir * this->dataPtr->angularVel); - if (this->dataPtr->newTopic){ + if (this->dataPtr->newTopic) + { this->dataPtr->cmdVelPub = ignition::transport::Node::Publisher(); this->dataPtr->cmdVelPub = - this->dataPtr->node.Advertise(this->dataPtr->topic); + this->dataPtr->node.Advertise + (this->dataPtr->topic); this->dataPtr->newTopic = false; - this->dataPtr->cmdVelPub.Publish(cmdVelMsg); } this->dataPtr->cmdVelPub.Publish(cmdVelMsg); } ///////////////////////////////////////////////// -void Teleop::OnTopicSelection(const QString& _topic) +void Teleop::OnTopicSelection(const QString & _topic) { this->dataPtr->newTopic = true; this->dataPtr->topic = _topic.toStdString(); @@ -140,7 +144,7 @@ void Teleop::OnTopicSelection(const QString& _topic) } ///////////////////////////////////////////////// -void Teleop::OnLinearVelSelection(const QString& _velocity) +void Teleop::OnLinearVelSelection(const QString & _velocity) { this->dataPtr->linearVel = _velocity.toDouble(); ignmsg << "[OnlinearVelSelection]: linear velocity: " @@ -148,7 +152,7 @@ void Teleop::OnLinearVelSelection(const QString& _velocity) } ///////////////////////////////////////////////// -void Teleop::OnAngularVelSelection(const QString& _velocity) +void Teleop::OnAngularVelSelection(const QString & _velocity) { this->dataPtr->angularVel = _velocity.toDouble(); ignmsg << "[OnlinearVelSelection]: angular velocity: " @@ -166,10 +170,11 @@ void Teleop::OnKeySwitch(bool _checked) ///////////////////////////////////////////////// void Teleop::OnSlidersSwitch(bool _checked) { - if(_checked){ + if(_checked) + { this->dataPtr->linearDir = 1; this->dataPtr->angularDir = 1; - OnTeleopTwist(); + this->OnTeleopTwist(); } } @@ -192,14 +197,14 @@ bool Teleop::eventFilter(QObject *_obj, QEvent *_event) case Qt::Key_D: this->dataPtr->angularState = keyAngular::kright; break; - case Qt::Key_X: + case Qt::Key_S: this->dataPtr->linearState = keyLinear::kbackward; break; default: break; } - setKeyDirection(); - OnTeleopTwist(); + this->SetKeyDirection(); + this->OnTeleopTwist(); } if(_event->type() == QEvent::KeyRelease) @@ -216,21 +221,21 @@ bool Teleop::eventFilter(QObject *_obj, QEvent *_event) case Qt::Key_D: this->dataPtr->angularState = keyAngular::kstop; break; - case Qt::Key_X: + case Qt::Key_S: this->dataPtr->linearState = keyLinear::kstop; break; default: break; } - setKeyDirection(); - OnTeleopTwist(); + this->SetKeyDirection(); + this->OnTeleopTwist(); } } return QObject::eventFilter(_obj, _event); } ///////////////////////////////////////////////// -void Teleop::setKeyDirection() +void Teleop::SetKeyDirection() { this->dataPtr->linearDir = this->dataPtr->linearState == keyLinear::kforward ? 1 : this->dataPtr->linearState == diff --git a/src/plugins/teleop/Teleop.hh b/src/plugins/teleop/Teleop.hh index ace4f0567..213dc0574 100644 --- a/src/plugins/teleop/Teleop.hh +++ b/src/plugins/teleop/Teleop.hh @@ -124,7 +124,7 @@ namespace plugins public slots: void OnSlidersSwitch(bool _checked); /// \brief Sets the movement direction when the keyboard is used. - public: void setKeyDirection(); + public: void SetKeyDirection(); /// \internal /// \brief Pointer to private data. diff --git a/src/plugins/teleop/Teleop.qml b/src/plugins/teleop/Teleop.qml index 0c47c862e..6f77818d4 100644 --- a/src/plugins/teleop/Teleop.qml +++ b/src/plugins/teleop/Teleop.qml @@ -20,6 +20,7 @@ import QtQuick.Controls 2.2 import QtQuick.Controls.Material 2.1 import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.3 +import "qrc:/qml" Rectangle { color:"transparent" @@ -51,50 +52,67 @@ Rectangle { } } - // Linear velocity input + // Velocity input Label { - id: linearVelLabel - text: "Linear:" + id: velocityLabel + text: "Velocity:" anchors.top: topicField.bottom anchors.topMargin: 10 anchors.left: parent.left anchors.leftMargin: 5 } - TextField { + // Linear velocity input + Label { + id: linearVelLabel + text: "Linear" + color: "dimgrey" + anchors.top: velocityLabel.bottom + anchors.topMargin: 15 + anchors.left: parent.left + anchors.leftMargin: 5 + } + IgnSpinBox { id: linearVelField - anchors.top: linearVelLabel.bottom + anchors.top: velocityLabel.bottom anchors.topMargin: 5 - anchors.left: parent.left + anchors.left: linearVelLabel.right anchors.leftMargin: 5 Layout.fillWidth: true - text:"0.0" - placeholderText: qsTr("Linear velocity...") - onEditingFinished: { - Teleop.OnLinearVelSelection(text) - } + value: 0.0 + maximumValue: 10.0 + minimumValue: 0.0 + decimals: 2 + stepSize: 0.10 + onEditingFinished:{ + Teleop.OnLinearVelSelection(value) + } } // Angular velocity input Label { id: angularVelLabel - text: "Angular:" - anchors.top: linearVelField.bottom - anchors.topMargin: 10 - anchors.left: parent.left - anchors.leftMargin: 5 + text: "Angular" + color: "dimgrey" + anchors.top: velocityLabel.bottom + anchors.topMargin: 15 + anchors.left: linearVelField.right + anchors.leftMargin: 10 } - TextField { + IgnSpinBox { id: angularVelField - anchors.top: angularVelLabel.bottom + anchors.top: velocityLabel.bottom anchors.topMargin: 5 - anchors.left: parent.left + anchors.left: angularVelLabel.right anchors.leftMargin: 5 Layout.fillWidth: true - text:"0.0" - placeholderText: qsTr("Angular velocity...") - onEditingFinished: { - Teleop.OnAngularVelSelection(text) - } + value: 0.0 + maximumValue: 2.0 + minimumValue: 0.0 + decimals: 2 + stepSize: 0.10 + onEditingFinished:{ + Teleop.OnAngularVelSelection(value) + } } // Button grid @@ -116,6 +134,7 @@ Rectangle { Teleop.linearDir = forwardButton.checked ? 1 : 0 if(backwardButton.checked) backwardButton.checked = false + slidersSwitch.checked = false Teleop.OnTeleopTwist() } ToolTip.visible: hovered @@ -141,6 +160,7 @@ Rectangle { Teleop.angularDir = leftButton.checked ? 1 : 0 if(rightButton.checked) rightButton.checked = false + slidersSwitch.checked = false Teleop.OnTeleopTwist() } Material.background: Material.primary @@ -164,6 +184,7 @@ Rectangle { Teleop.angularDir = rightButton.checked ? -1 : 0 if(leftButton.checked) leftButton.checked = false + slidersSwitch.checked = false Teleop.OnTeleopTwist() } Material.background: Material.primary @@ -187,6 +208,7 @@ Rectangle { Teleop.linearDir = backwardButton.checked ? -1 : 0 if(forwardButton.checked) forwardButton.checked = false + slidersSwitch.checked = false Teleop.OnTeleopTwist() } Material.background: Material.primary @@ -250,7 +272,7 @@ Rectangle { } Label { id: keyboardSwitchLabel - text: "Input from keyboard" + text: "Input from keyboard (WASD)" anchors.horizontalCenter : keySwitch.horizontalCenter anchors.verticalCenter : keySwitch.verticalCenter anchors.left: keySwitch.right @@ -280,7 +302,6 @@ Rectangle { ToolTip.visible: hovered ToolTip.text: checked ? qsTr("Disable sliders") : qsTr("Enable sliders") } - Label { id: slidersSwitchLabel text: "Input from sliders" @@ -322,7 +343,7 @@ Rectangle { stepSize: 0.01 onMoved: { - linearVelField.text = linearVelSlider.value.toFixed(2) + linearVelField.value = linearVelSlider.value.toFixed(2) Teleop.OnLinearVelSelection(linearVelSlider.value) Teleop.OnTeleopTwist() } @@ -375,7 +396,7 @@ Rectangle { stepSize: 0.01 onMoved: { - angularVelField.text = angularVelSlider.value.toFixed(2) + angularVelField.value = angularVelSlider.value.toFixed(2) Teleop.OnAngularVelSelection(angularVelSlider.value) Teleop.OnTeleopTwist() } diff --git a/src/plugins/teleop/Teleop_TEST.cc b/src/plugins/teleop/Teleop_TEST.cc index d2149e7ac..766318dde 100644 --- a/src/plugins/teleop/Teleop_TEST.cc +++ b/src/plugins/teleop/Teleop_TEST.cc @@ -31,7 +31,6 @@ #include "ignition/gui/Plugin.hh" #include "ignition/gui/MainWindow.hh" #include -#include #include "Teleop.hh" From fbeceff4e85e73a33adc7b0165d0e5d59bdfb880 Mon Sep 17 00:00:00 2001 From: Franco Cipollone Date: Wed, 14 Jul 2021 20:17:48 -0300 Subject: [PATCH 5/9] Solves bug when first message is sent and fix qml warning. Signed-off-by: Franco Cipollone --- src/plugins/teleop/Teleop.cc | 31 +++++++++++++++++-------------- src/plugins/teleop/Teleop.qml | 4 ++-- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/plugins/teleop/Teleop.cc b/src/plugins/teleop/Teleop.cc index a21159722..e8c319cc0 100644 --- a/src/plugins/teleop/Teleop.cc +++ b/src/plugins/teleop/Teleop.cc @@ -80,9 +80,6 @@ namespace plugins /// \brief Indicates if the keyboard is enabled or /// disabled. public: bool keyEnable = false; - /// \brief Indicates if a new topic has been - /// selected. - public: bool newTopic = true; }; } } @@ -95,6 +92,11 @@ using namespace plugins; ///////////////////////////////////////////////// Teleop::Teleop(): Plugin(), dataPtr(new TeleopPrivate) { + // Initialize publisher using default topic. + this->dataPtr->cmdVelPub = ignition::transport::Node::Publisher(); + this->dataPtr->cmdVelPub = + this->dataPtr->node.Advertise + (this->dataPtr->topic); } ///////////////////////////////////////////////// @@ -122,25 +124,26 @@ void Teleop::OnTeleopTwist() cmdVelMsg.mutable_angular()->set_z( this->dataPtr->angularDir * this->dataPtr->angularVel); - if (this->dataPtr->newTopic) - { - this->dataPtr->cmdVelPub = ignition::transport::Node::Publisher(); - this->dataPtr->cmdVelPub = - this->dataPtr->node.Advertise - (this->dataPtr->topic); - this->dataPtr->newTopic = false; - } - - this->dataPtr->cmdVelPub.Publish(cmdVelMsg); + if (!this->dataPtr->cmdVelPub.Publish(cmdVelMsg)) + ignerr << "ignition::msgs::Twist message couldn't be published at topic: " + << this->dataPtr->topic << std::endl; } ///////////////////////////////////////////////// void Teleop::OnTopicSelection(const QString & _topic) { - this->dataPtr->newTopic = true; this->dataPtr->topic = _topic.toStdString(); ignmsg << "A new topic has been entered: '" << this->dataPtr->topic << " ' " <dataPtr->cmdVelPub = ignition::transport::Node::Publisher(); + this->dataPtr->cmdVelPub = + this->dataPtr->node.Advertise + (this->dataPtr->topic); + if(!this->dataPtr->cmdVelPub) + ignerr << "Error when advertising topic: " << + this->dataPtr->topic << std::endl; } ///////////////////////////////////////////////// diff --git a/src/plugins/teleop/Teleop.qml b/src/plugins/teleop/Teleop.qml index 6f77818d4..74f9eed9d 100644 --- a/src/plugins/teleop/Teleop.qml +++ b/src/plugins/teleop/Teleop.qml @@ -292,8 +292,8 @@ Rectangle { leftButton.checked = false rightButton.checked = false backwardButton.checked = false - linearVelField.text = linearVelSlider.value.toFixed(2) - angularVelField.text = angularVelSlider.value.toFixed(2) + linearVelField.value = linearVelSlider.value.toFixed(2) + angularVelField.value = angularVelSlider.value.toFixed(2) Teleop.OnLinearVelSelection(linearVelSlider.value) Teleop.OnAngularVelSelection(angularVelSlider.value) Teleop.OnTeleopTwist() From f95351698499d880251ebb26b55a62d0ef861d74 Mon Sep 17 00:00:00 2001 From: LolaSegura Date: Thu, 15 Jul 2021 13:22:00 -0300 Subject: [PATCH 6/9] Addressed test comments. Signed-off-by: LolaSegura --- src/plugins/teleop/Teleop.cc | 44 +-- src/plugins/teleop/Teleop_TEST.cc | 612 +++++++++++++----------------- 2 files changed, 284 insertions(+), 372 deletions(-) diff --git a/src/plugins/teleop/Teleop.cc b/src/plugins/teleop/Teleop.cc index e8c319cc0..1d4764255 100644 --- a/src/plugins/teleop/Teleop.cc +++ b/src/plugins/teleop/Teleop.cc @@ -39,16 +39,16 @@ namespace gui { namespace plugins { - enum class keyLinear{ - kforward, - kbackward, - kstop, + enum class KeyLinear{ + kForward, + kBackward, + kStop, }; - enum class keyAngular{ - kleft, - kright, - kstop, + enum class KeyAngular{ + kLeft, + kRight, + kStop, }; class TeleopPrivate @@ -73,9 +73,9 @@ namespace plugins public: int angularDir = 0; /// \brief Linear state setted by keyboard input. - public: keyLinear linearState = keyLinear::kstop; + public: KeyLinear linearState = KeyLinear::kStop; /// \brief Angular state setted by keyboard input. - public: keyAngular angularState = keyAngular::kstop; + public: KeyAngular angularState = KeyAngular::kStop; /// \brief Indicates if the keyboard is enabled or /// disabled. @@ -192,16 +192,16 @@ bool Teleop::eventFilter(QObject *_obj, QEvent *_event) switch(keyEvent->key()) { case Qt::Key_W: - this->dataPtr->linearState = keyLinear::kforward; + this->dataPtr->linearState = KeyLinear::kForward; break; case Qt::Key_A: - this->dataPtr->angularState = keyAngular::kleft; + this->dataPtr->angularState = KeyAngular::kLeft; break; case Qt::Key_D: - this->dataPtr->angularState = keyAngular::kright; + this->dataPtr->angularState = KeyAngular::kRight; break; case Qt::Key_S: - this->dataPtr->linearState = keyLinear::kbackward; + this->dataPtr->linearState = KeyLinear::kBackward; break; default: break; @@ -216,16 +216,16 @@ bool Teleop::eventFilter(QObject *_obj, QEvent *_event) switch(keyEvent->key()) { case Qt::Key_W: - this->dataPtr->linearState = keyLinear::kstop; + this->dataPtr->linearState = KeyLinear::kStop; break; case Qt::Key_A: - this->dataPtr->angularState = keyAngular::kstop; + this->dataPtr->angularState = KeyAngular::kStop; break; case Qt::Key_D: - this->dataPtr->angularState = keyAngular::kstop; + this->dataPtr->angularState = KeyAngular::kStop; break; case Qt::Key_S: - this->dataPtr->linearState = keyLinear::kstop; + this->dataPtr->linearState = KeyLinear::kStop; break; default: break; @@ -241,12 +241,12 @@ bool Teleop::eventFilter(QObject *_obj, QEvent *_event) void Teleop::SetKeyDirection() { this->dataPtr->linearDir = this->dataPtr->linearState == - keyLinear::kforward ? 1 : this->dataPtr->linearState == - keyLinear::kbackward ? -1 : 0; + KeyLinear::kForward ? 1 : this->dataPtr->linearState == + KeyLinear::kBackward ? -1 : 0; this->dataPtr->angularDir = this->dataPtr->angularState == - keyAngular::kleft ? 1 : this->dataPtr->angularState == - keyAngular::kright ? -1 : 0; + KeyAngular::kLeft ? 1 : this->dataPtr->angularState == + KeyAngular::kRight ? -1 : 0; } ///////////////////////////////////////////////// diff --git a/src/plugins/teleop/Teleop_TEST.cc b/src/plugins/teleop/Teleop_TEST.cc index 766318dde..60cd41cd1 100644 --- a/src/plugins/teleop/Teleop_TEST.cc +++ b/src/plugins/teleop/Teleop_TEST.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Open Source Robotics Foundation + * Copyright (C) 2021 Open Source Robotics Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,7 @@ #include "ignition/gui/Application.hh" #include "ignition/gui/Plugin.hh" #include "ignition/gui/MainWindow.hh" -#include +#include "ignition/gui/qt.h" #include "Teleop.hh" @@ -40,403 +40,315 @@ char **g_argv = new char *[g_argc]; using namespace ignition; using namespace gui; -class TeleopTest : public ::testing::Test { - - protected: - void SetUp() override { - common::Console::SetVerbosity(4); - } - - /// \param[in] _topic Topic to publish twist messages. - protected: void TestButtons(const std::string &_topic) - { - Application app(g_argc, g_argv); - app.AddPluginPath(std::string(PROJECT_BINARY_PATH) + "/lib"); - - // Load plugin - const char *pluginStr = - "" - "" - "Teleop!" - "" - ""; - - tinyxml2::XMLDocument pluginDoc; - EXPECT_EQ(tinyxml2::XML_SUCCESS, pluginDoc.Parse(pluginStr)); - EXPECT_TRUE(app.LoadPlugin("Teleop", - pluginDoc.FirstChildElement("plugin"))); - - // Get main window - auto win = app.findChild(); - ASSERT_NE(nullptr, win); - - // Show, but don't exec, so we don't block - win->QuickWindow()->show(); - - // Get plugin - auto plugins = win->findChildren(); - EXPECT_EQ(plugins.size(), 1); - - auto plugin = plugins[0]; - EXPECT_EQ(plugin->Title(), "Teleop!"); - - plugin->OnTopicSelection(QString::fromStdString(_topic)); - - // Checks if the directions of the movement are set - // with the default value. - EXPECT_EQ(plugin->LinearDirection(), 0); - EXPECT_EQ(plugin->AngularDirection(), 0); - - // Define velocity values. - double linearVel = 1.0; - double angularVel = 0.5; - - // Set velocity value and movement direction. - // Forward and left movement. - plugin->OnLinearVelSelection(QString::number(linearVel)); - plugin->OnAngularVelSelection(QString::number(angularVel)); - plugin->setLinearDirection(1); - plugin->setAngularDirection(1); - - // Subscribe to the 'cmd_vel' topic - bool received = false; - std::function cb = - [&](const msgs::Twist &_msg) - { - EXPECT_DOUBLE_EQ(_msg.linear().x(), - plugin->LinearDirection() * linearVel); - EXPECT_DOUBLE_EQ(_msg.angular().z(), - plugin->AngularDirection() * angularVel); - received = true; - }; +class TeleopTest : public ::testing::Test +{ + // Provides an API to load plugins and configuration files. + protected: Application app{g_argc, g_argv}; - transport::Node node; - node.Subscribe(_topic, cb); - plugin->OnTeleopTwist(); + // List of plugins. + protected: QList plugins; + protected: plugins::Teleop * plugin; - int sleep = 0; - int maxSleep = 30; - while (!received && sleep < maxSleep) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - QCoreApplication::processEvents(); - sleep++; - } + // Instance of the main window. + protected: MainWindow * win; - EXPECT_TRUE(received); - received = false; + // Checks if a new command has been received. + protected: bool received = false; + protected: transport::Node node; - // Change movement direction. - // Backward and right movement. - plugin->setLinearDirection(-1); - plugin->setAngularDirection(-1); - plugin->OnTeleopTwist(); + // Define velocity values. + protected: const double linearVel = 1.0; + protected: const double angularVel = 0.5; - sleep = 0; - // cppcheck-suppress knownConditionTrueFalse - while (!received && sleep < maxSleep) + // Set up function. + protected: + void SetUp() override { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - QCoreApplication::processEvents(); - sleep++; + common::Console::SetVerbosity(4); + + this->app.AddPluginPath(std::string(PROJECT_BINARY_PATH) + "/lib"); + + // Load plugin + const char *pluginStr = + "" + "" + "Teleop!" + "" + ""; + + tinyxml2::XMLDocument pluginDoc; + EXPECT_EQ(tinyxml2::XML_SUCCESS, pluginDoc.Parse(pluginStr)); + EXPECT_TRUE(this->app.LoadPlugin("Teleop", + pluginDoc.FirstChildElement("plugin"))); + + // Get main window + win = this->app.findChild(); + ASSERT_NE(nullptr, win); + + // Show, but don't exec, so we don't block + win->QuickWindow()->show(); + + // Get plugin + plugins = win->findChildren(); + EXPECT_EQ(plugins.size(), 1); + + plugin = plugins[0]; + EXPECT_EQ(plugin->Title(), "Teleop!"); + + // Subcribes to the command velocity topic. + node.Subscribe("/model/vehicle_blue/cmd_vel", + &TeleopTest::CallBack, this); + + // Sets topic. This must be the same as the + // one the node is subscribed to. + plugin->OnTopicSelection( + QString::fromStdString("/model/vehicle_blue/cmd_vel")); + + // Checks if the directions of the movement are set + // with the default value '0'. + EXPECT_EQ(plugin->LinearDirection(), 0); + EXPECT_EQ(plugin->AngularDirection(), 0); + + // Set velocity value and movement direction. + plugin->OnLinearVelSelection(QString::number(linearVel)); + plugin->OnAngularVelSelection(QString::number(angularVel)); } - EXPECT_TRUE(received); - received = false; - - // Stops angular movement. - plugin->setAngularDirection(0); - plugin->OnTeleopTwist(); - - sleep = 0; - // cppcheck-suppress knownConditionTrueFalse - while (!received && sleep < maxSleep) + // Subscriber call back function. Verifies if the Twist message is + // sent correctly. + protected: + void CallBack(const msgs::Twist &_msg) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - QCoreApplication::processEvents(); - sleep++; + EXPECT_DOUBLE_EQ(_msg.linear().x(), + plugin->LinearDirection() * linearVel); + EXPECT_DOUBLE_EQ(_msg.angular().z(), + plugin->AngularDirection() * angularVel); + received = true; } +}; - EXPECT_TRUE(received); - received = false; - - // Stops linear movement. - // Starts angular movement. - plugin->setLinearDirection(0); - plugin->setAngularDirection(1); - plugin->OnTeleopTwist(); - - sleep = 0; - // cppcheck-suppress knownConditionTrueFalse - while (!received && sleep < maxSleep) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - QCoreApplication::processEvents(); - sleep++; - } +///////////////////////////////////////////////// +TEST_F(TeleopTest, ButtonCommand) +{ + // Forward movement. + plugin->setLinearDirection(1); + // Counterclockwise movement. + plugin->setAngularDirection(1); + plugin->OnTeleopTwist(); + + int sleep = 0; + const int maxSleep = 30; + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } - EXPECT_TRUE(received); - received = false; + EXPECT_TRUE(received); + received = false; - // Stops movement. - plugin->setAngularDirection(0); - plugin->setLinearDirection(0); - plugin->OnTeleopTwist(); + // Change movement direction. + // Backward movement. + plugin->setLinearDirection(-1); + // Clockwise direction. + plugin->setAngularDirection(-1); + plugin->OnTeleopTwist(); - sleep = 0; - // cppcheck-suppress knownConditionTrueFalse - while (!received && sleep < maxSleep) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - QCoreApplication::processEvents(); - sleep++; - } + sleep = 0; + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } - EXPECT_TRUE(received); + EXPECT_TRUE(received); + received = false; - // Cleanup - plugins.clear(); - }; + // Stops angular movement. + plugin->setAngularDirection(0); + plugin->OnTeleopTwist(); - /// \param[in] _topic Topic to publish twist messages. - protected: void TestKeys(const std::string &_topic) + sleep = 0; + while (!received && sleep < maxSleep) { - Application app(g_argc, g_argv); - app.AddPluginPath(std::string(PROJECT_BINARY_PATH) + "/lib"); - - // Load plugin - const char *pluginStr = - "" - "" - "Teleop!" - "" - ""; - - tinyxml2::XMLDocument pluginDoc; - EXPECT_EQ(tinyxml2::XML_SUCCESS, pluginDoc.Parse(pluginStr)); - EXPECT_TRUE(app.LoadPlugin("Teleop", - pluginDoc.FirstChildElement("plugin"))); - - // Get main window - auto win = app.findChild(); - ASSERT_NE(nullptr, win); - - // Show, but don't exec, so we don't block - win->QuickWindow()->show(); - - // Get plugin - auto plugins = win->findChildren(); - EXPECT_EQ(plugins.size(), 1); - - auto plugin = plugins[0]; - EXPECT_EQ(plugin->Title(), "Teleop!"); - - // Set desire topic. - plugin->OnTopicSelection(QString::fromStdString(_topic)); - - // Movement direction - EXPECT_EQ(plugin->LinearDirection(), 0); - EXPECT_EQ(plugin->AngularDirection(), 0); - - // Define velocity values. - double linearVel = 1.0; - double angularVel = 0.5; - - // Set velocity value and movement direction. - plugin->OnLinearVelSelection(QString::number(linearVel)); - plugin->OnAngularVelSelection(QString::number(angularVel)); - - // Subscribe - bool received = false; - std::function cb = - [&](const msgs::Twist &_msg) - { - EXPECT_DOUBLE_EQ(_msg.linear().x(), - plugin->LinearDirection() * linearVel); - EXPECT_DOUBLE_EQ(_msg.angular().z(), - plugin->AngularDirection() * angularVel); - received = true; - }; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } + EXPECT_TRUE(received); + received = false; - transport::Node node; - node.Subscribe(_topic, cb); + // Stops linear movement. + // Starts angular movement. + plugin->setLinearDirection(0); + plugin->setAngularDirection(1); + plugin->OnTeleopTwist(); - // Generates a key press event on the main window. - QKeyEvent *keypress_W = new QKeyEvent(QKeyEvent::KeyPress, - Qt::Key_W, Qt::NoModifier); - app.sendEvent(win->QuickWindow(), keypress_W); + sleep = 0; + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); QCoreApplication::processEvents(); + sleep++; + } - EXPECT_FALSE(received); - - // Enables key input. - plugin->OnKeySwitch(true); - app.sendEvent(win->QuickWindow(), keypress_W); + EXPECT_TRUE(received); + received = false; - int sleep = 0; - int maxSleep = 30; - while (!received && sleep < maxSleep) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - QCoreApplication::processEvents(); - sleep++; - } + // Stops movement. + plugin->setAngularDirection(0); + plugin->setLinearDirection(0); + plugin->OnTeleopTwist(); - EXPECT_TRUE(received); - received = false; + sleep = 0; + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } - // Generates a key press event on the main window. - QKeyEvent *keypress_D = new QKeyEvent(QKeyEvent::KeyPress, - Qt::Key_D, Qt::NoModifier); - app.sendEvent(win->QuickWindow(), keypress_D); + EXPECT_TRUE(received); - sleep = 0; - // cppcheck-suppress knownConditionTrueFalse - while (!received && sleep < maxSleep) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - QCoreApplication::processEvents(); - sleep++; - } + // Cleanup + plugins.clear(); +} - EXPECT_TRUE(received); - received = false; +///////////////////////////////////////////////// +TEST_F(TeleopTest, KeyboardCommand) +{ + // Generates a key press event on the main window. + QKeyEvent *keypress_W = new QKeyEvent(QKeyEvent::KeyPress, + Qt::Key_W, Qt::NoModifier); + app.sendEvent(win->QuickWindow(), keypress_W); + QCoreApplication::processEvents(); - // Generates a key release event on the main window. - QKeyEvent *keyrelease_D = new QKeyEvent(QKeyEvent::KeyRelease, - Qt::Key_D, Qt::NoModifier); - app.sendEvent(win->QuickWindow(), keyrelease_D); + EXPECT_FALSE(received); - sleep = 0; - // cppcheck-suppress knownConditionTrueFalse - while (!received && sleep < maxSleep) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - QCoreApplication::processEvents(); - sleep++; - } + // Enables key input. + plugin->OnKeySwitch(true); + app.sendEvent(win->QuickWindow(), keypress_W); - EXPECT_TRUE(received); - received = false; + int sleep = 0; + const int maxSleep = 30; + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } - // Generates a key press event on the main window. - QKeyEvent *keypress_A = new QKeyEvent(QKeyEvent::KeyPress, - Qt::Key_A, Qt::NoModifier); - app.sendEvent(win->QuickWindow(), keypress_A); + EXPECT_TRUE(received); + received = false; - sleep = 0; - // cppcheck-suppress knownConditionTrueFalse - while (!received && sleep < maxSleep) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - QCoreApplication::processEvents(); - sleep++; - } + // Generates a key press event on the main window. + QKeyEvent *keypress_D = new QKeyEvent(QKeyEvent::KeyPress, + Qt::Key_D, Qt::NoModifier); + app.sendEvent(win->QuickWindow(), keypress_D); - EXPECT_TRUE(received); - received = false; + sleep = 0; + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } - // Generates a key release event on the main window. - QKeyEvent *keyrelease_A = new QKeyEvent(QKeyEvent::KeyRelease, - Qt::Key_A, Qt::NoModifier); - app.sendEvent(win->QuickWindow(), keyrelease_A); + EXPECT_TRUE(received); + received = false; - sleep = 0; - // cppcheck-suppress knownConditionTrueFalse - while (!received && sleep < maxSleep) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - QCoreApplication::processEvents(); - sleep++; - } + // Generates a key release event on the main window. + QKeyEvent *keyrelease_D = new QKeyEvent(QKeyEvent::KeyRelease, + Qt::Key_D, Qt::NoModifier); + app.sendEvent(win->QuickWindow(), keyrelease_D); - EXPECT_TRUE(received); - received = false; - // Generates a key release event on the main window. - QKeyEvent *keyrelease_W = new QKeyEvent(QKeyEvent::KeyRelease, - Qt::Key_W, Qt::NoModifier); - app.sendEvent(win->QuickWindow(), keyrelease_W); + sleep = 0; + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } - sleep = 0; - // cppcheck-suppress knownConditionTrueFalse - while (!received && sleep < maxSleep) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - QCoreApplication::processEvents(); - sleep++; - } + EXPECT_TRUE(received); + received = false; - EXPECT_TRUE(received); - received = false; - // Generates a key press event on the main window. - QKeyEvent *keypress_X = new QKeyEvent(QKeyEvent::KeyPress, - Qt::Key_X, Qt::NoModifier); - app.sendEvent(win->QuickWindow(), keypress_X); + // Generates a key press event on the main window. + QKeyEvent *keypress_A = new QKeyEvent(QKeyEvent::KeyPress, + Qt::Key_A, Qt::NoModifier); + app.sendEvent(win->QuickWindow(), keypress_A); - sleep = 0; - // cppcheck-suppress knownConditionTrueFalse - while (!received && sleep < maxSleep) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - QCoreApplication::processEvents(); - sleep++; - } + sleep = 0; + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } - EXPECT_TRUE(received); - received = false; - // Generates a key release event on the main window. - QKeyEvent *keyrelease_X = new QKeyEvent(QKeyEvent::KeyRelease, - Qt::Key_X, Qt::NoModifier); - app.sendEvent(win->QuickWindow(), keyrelease_X); + EXPECT_TRUE(received); + received = false; - sleep = 0; - // cppcheck-suppress knownConditionTrueFalse - while (!received && sleep < maxSleep) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - QCoreApplication::processEvents(); - sleep++; - } + // Generates a key release event on the main window. + QKeyEvent *keyrelease_A = new QKeyEvent(QKeyEvent::KeyRelease, + Qt::Key_A, Qt::NoModifier); + app.sendEvent(win->QuickWindow(), keyrelease_A); - // Cleanup - plugins.clear(); - }; + sleep = 0; + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } -}; + EXPECT_TRUE(received); + received = false; + // Generates a key release event on the main window. + QKeyEvent *keyrelease_W = new QKeyEvent(QKeyEvent::KeyRelease, + Qt::Key_W, Qt::NoModifier); + app.sendEvent(win->QuickWindow(), keyrelease_W); -///////////////////////////////////////////////// -TEST_F(TeleopTest, Load) -{ - Application app(g_argc, g_argv); - app.AddPluginPath(std::string(PROJECT_BINARY_PATH) + "/lib"); + sleep = 0; + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } - EXPECT_TRUE(app.LoadPlugin("Teleop")); + EXPECT_TRUE(received); + received = false; + // Generates a key press event on the main window. + QKeyEvent *keypress_X = new QKeyEvent(QKeyEvent::KeyPress, + Qt::Key_X, Qt::NoModifier); + app.sendEvent(win->QuickWindow(), keypress_X); - // Get main window - auto win = app.findChild(); - ASSERT_NE(nullptr, win); + sleep = 0; + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } - // Get plugin - auto plugins = win->findChildren(); - EXPECT_EQ(plugins.size(), 1); + EXPECT_TRUE(received); + received = false; + // Generates a key release event on the main window. + QKeyEvent *keyrelease_X = new QKeyEvent(QKeyEvent::KeyRelease, + Qt::Key_X, Qt::NoModifier); + app.sendEvent(win->QuickWindow(), keyrelease_X); - auto plugin = plugins[0]; - EXPECT_EQ(plugin->Title(), "Teleop"); + sleep = 0; + while (!received && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } // Cleanup plugins.clear(); } - -///////////////////////////////////////////////// -TEST_F(TeleopTest, ButtonCommand) -{ - TestButtons("/cmd_vel"); - TestButtons("/model/vehicle_blue/cmd_vel"); -} - -///////////////////////////////////////////////// -TEST_F(TeleopTest, KeyboardCommand) -{ - TestKeys("/cmd_vel"); - TestKeys("/model/vehicle_blue/cmd_vel"); -} From 90fae8348e685066b62e51bf59a544ac9bde4058 Mon Sep 17 00:00:00 2001 From: LolaSegura Date: Fri, 16 Jul 2021 17:30:05 -0300 Subject: [PATCH 7/9] Addressed style comments. Signed-off-by: LolaSegura --- src/plugins/teleop/Teleop_TEST.cc | 46 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/plugins/teleop/Teleop_TEST.cc b/src/plugins/teleop/Teleop_TEST.cc index 60cd41cd1..9363cc025 100644 --- a/src/plugins/teleop/Teleop_TEST.cc +++ b/src/plugins/teleop/Teleop_TEST.cc @@ -42,27 +42,8 @@ using namespace gui; class TeleopTest : public ::testing::Test { - // Provides an API to load plugins and configuration files. - protected: Application app{g_argc, g_argv}; - - // List of plugins. - protected: QList plugins; - protected: plugins::Teleop * plugin; - - // Instance of the main window. - protected: MainWindow * win; - - // Checks if a new command has been received. - protected: bool received = false; - protected: transport::Node node; - - // Define velocity values. - protected: const double linearVel = 1.0; - protected: const double angularVel = 0.5; - // Set up function. - protected: - void SetUp() override + protected: void SetUp() override { common::Console::SetVerbosity(4); @@ -95,9 +76,9 @@ class TeleopTest : public ::testing::Test plugin = plugins[0]; EXPECT_EQ(plugin->Title(), "Teleop!"); - // Subcribes to the command velocity topic. + // Subscribes to the command velocity topic. node.Subscribe("/model/vehicle_blue/cmd_vel", - &TeleopTest::CallBack, this); + &TeleopTest::VerifyTwistMsgCb, this); // Sets topic. This must be the same as the // one the node is subscribed to. @@ -116,8 +97,7 @@ class TeleopTest : public ::testing::Test // Subscriber call back function. Verifies if the Twist message is // sent correctly. - protected: - void CallBack(const msgs::Twist &_msg) + protected: void VerifyTwistMsgCb(const msgs::Twist &_msg) { EXPECT_DOUBLE_EQ(_msg.linear().x(), plugin->LinearDirection() * linearVel); @@ -125,6 +105,24 @@ class TeleopTest : public ::testing::Test plugin->AngularDirection() * angularVel); received = true; } + + // Provides an API to load plugins and configuration files. + protected: Application app{g_argc, g_argv}; + + // List of plugins. + protected: QList plugins; + protected: plugins::Teleop * plugin; + + // Instance of the main window. + protected: MainWindow * win; + + // Checks if a new command has been received. + protected: bool received = false; + protected: transport::Node node; + + // Define velocity values. + protected: const double linearVel = 1.0; + protected: const double angularVel = 0.5; }; ///////////////////////////////////////////////// From 8328a1758139ff27e229a056fa1b026b7ea9c381 Mon Sep 17 00:00:00 2001 From: Louise Poubel Date: Fri, 16 Jul 2021 18:55:33 -0700 Subject: [PATCH 8/9] use double for velocities, disable test on Windows Signed-off-by: Louise Poubel --- src/plugins/teleop/CMakeLists.txt | 2 +- src/plugins/teleop/Teleop.cc | 20 +++++++------------- src/plugins/teleop/Teleop.hh | 6 +++--- src/plugins/teleop/Teleop_TEST.cc | 13 ++++++++----- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/plugins/teleop/CMakeLists.txt b/src/plugins/teleop/CMakeLists.txt index 64d158f26..a8ebc5bcc 100644 --- a/src/plugins/teleop/CMakeLists.txt +++ b/src/plugins/teleop/CMakeLists.txt @@ -3,6 +3,6 @@ ign_gui_add_plugin(Teleop Teleop.cc QT_HEADERS Teleop.hh - TEST_SOURCES + TEST_SOURCES Teleop_TEST.cc ) diff --git a/src/plugins/teleop/Teleop.cc b/src/plugins/teleop/Teleop.cc index 1d4764255..1af41287f 100644 --- a/src/plugins/teleop/Teleop.cc +++ b/src/plugins/teleop/Teleop.cc @@ -90,7 +90,7 @@ using namespace gui; using namespace plugins; ///////////////////////////////////////////////// -Teleop::Teleop(): Plugin(), dataPtr(new TeleopPrivate) +Teleop::Teleop(): Plugin(), dataPtr(std::make_unique()) { // Initialize publisher using default topic. this->dataPtr->cmdVelPub = ignition::transport::Node::Publisher(); @@ -100,9 +100,7 @@ Teleop::Teleop(): Plugin(), dataPtr(new TeleopPrivate) } ///////////////////////////////////////////////// -Teleop::~Teleop() -{ -} +Teleop::~Teleop() = default; ///////////////////////////////////////////////// void Teleop::LoadConfig(const tinyxml2::XMLElement *) @@ -130,7 +128,7 @@ void Teleop::OnTeleopTwist() } ///////////////////////////////////////////////// -void Teleop::OnTopicSelection(const QString & _topic) +void Teleop::OnTopicSelection(const QString &_topic) { this->dataPtr->topic = _topic.toStdString(); ignmsg << "A new topic has been entered: '" << @@ -147,19 +145,15 @@ void Teleop::OnTopicSelection(const QString & _topic) } ///////////////////////////////////////////////// -void Teleop::OnLinearVelSelection(const QString & _velocity) +void Teleop::OnLinearVelSelection(double _velocity) { - this->dataPtr->linearVel = _velocity.toDouble(); - ignmsg << "[OnlinearVelSelection]: linear velocity: " - << this->dataPtr->linearVel << std::endl; + this->dataPtr->linearVel = _velocity; } ///////////////////////////////////////////////// -void Teleop::OnAngularVelSelection(const QString & _velocity) +void Teleop::OnAngularVelSelection(double _velocity) { - this->dataPtr->angularVel = _velocity.toDouble(); - ignmsg << "[OnlinearVelSelection]: angular velocity: " - << this->dataPtr->angularVel << std::endl; + this->dataPtr->angularVel = _velocity; } ///////////////////////////////////////////////// diff --git a/src/plugins/teleop/Teleop.hh b/src/plugins/teleop/Teleop.hh index 213dc0574..e4224a531 100644 --- a/src/plugins/teleop/Teleop.hh +++ b/src/plugins/teleop/Teleop.hh @@ -105,15 +105,15 @@ namespace plugins /// \brief Callback in Qt thread when the topic changes. /// \param[in] _topic variable to indicate the topic to /// publish the Twist commands. - public slots: void OnTopicSelection(const QString& _topic); + public slots: void OnTopicSelection(const QString &_topic); /// \brief Callback in Qt thread when the linear velocity changes. /// \param[in] _velocity variable to indicate the linear velocity. - public slots: void OnLinearVelSelection(const QString& _velocity); + public slots: void OnLinearVelSelection(double _velocity); /// \brief Callback in Qt thread when the angular velocity changes. /// \param[in] _velocity variable to indicate the angular velocity. - public slots: void OnAngularVelSelection(const QString& _velocity); + public slots: void OnAngularVelSelection(double _velocity); /// \brief Callback in Qt thread when the keyboard is enabled or disabled. /// \param[in] _checked variable to indicate the state of the switch. diff --git a/src/plugins/teleop/Teleop_TEST.cc b/src/plugins/teleop/Teleop_TEST.cc index 9363cc025..90c9a7c2f 100644 --- a/src/plugins/teleop/Teleop_TEST.cc +++ b/src/plugins/teleop/Teleop_TEST.cc @@ -35,7 +35,10 @@ #include "Teleop.hh" int g_argc = 1; -char **g_argv = new char *[g_argc]; +char* g_argv[] = +{ + reinterpret_cast(const_cast("./Teleop_TEST")), +}; using namespace ignition; using namespace gui; @@ -91,8 +94,8 @@ class TeleopTest : public ::testing::Test EXPECT_EQ(plugin->AngularDirection(), 0); // Set velocity value and movement direction. - plugin->OnLinearVelSelection(QString::number(linearVel)); - plugin->OnAngularVelSelection(QString::number(angularVel)); + plugin->OnLinearVelSelection(linearVel); + plugin->OnAngularVelSelection(angularVel); } // Subscriber call back function. Verifies if the Twist message is @@ -126,7 +129,7 @@ class TeleopTest : public ::testing::Test }; ///////////////////////////////////////////////// -TEST_F(TeleopTest, ButtonCommand) +TEST_F(TeleopTest, IGN_UTILS_TEST_DISABLED_ON_WIN32(ButtonCommand)) { // Forward movement. plugin->setLinearDirection(1); @@ -215,7 +218,7 @@ TEST_F(TeleopTest, ButtonCommand) } ///////////////////////////////////////////////// -TEST_F(TeleopTest, KeyboardCommand) +TEST_F(TeleopTest, IGN_UTILS_TEST_DISABLED_ON_WIN32(KeyboardCommand)) { // Generates a key press event on the main window. QKeyEvent *keypress_W = new QKeyEvent(QKeyEvent::KeyPress, From 7d73aee5c915ee47ba85c21505e2ccdddd6f877b Mon Sep 17 00:00:00 2001 From: ahcorde Date: Mon, 19 Jul 2021 09:39:09 +0200 Subject: [PATCH 9/9] Fixed Windows build Signed-off-by: ahcorde --- src/plugins/teleop/Teleop.hh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/plugins/teleop/Teleop.hh b/src/plugins/teleop/Teleop.hh index e4224a531..187a77323 100644 --- a/src/plugins/teleop/Teleop.hh +++ b/src/plugins/teleop/Teleop.hh @@ -25,6 +25,16 @@ #include #include +#ifndef _WIN32 +# define Teleop_EXPORTS_API +#else +# if (defined(Teleop_EXPORTS)) +# define Teleop_EXPORTS_API __declspec(dllexport) +# else +# define Teleop_EXPORTS_API __declspec(dllimport) +# endif +#endif + namespace ignition { namespace gui @@ -39,7 +49,7 @@ namespace plugins /// vehicle load to the world. /// ## Configuration /// This plugin doesn't accept any custom configuration. - class Teleop : public Plugin + class Teleop_EXPORTS_API Teleop : public Plugin { Q_OBJECT