39 #include "../tools/xml_syntax_highlighter.h"
45 #include <QMessageBox>
46 #include <QPushButton>
48 #include <QVBoxLayout>
52 #include <moveit_msgs/DisplayRobotState.h>
65 QVBoxLayout* layout =
new QVBoxLayout();
66 layout->setAlignment(Qt::AlignTop);
70 HeaderWidget*
header =
new HeaderWidget(
72 QString(
"For use in the Gazebo physics simulation, the URDF needs to define inertial properties "
73 "for all links as well as control interfaces for all joints. "
74 "The required changes to your URDF are <b>highlighted below in "
75 "<font color=\"darkgreen\">green</font></b>.<br>"
76 "You can accept these suggestions and overwrite your existing URDF, or manually "
77 "adapt your URDF opening your preferred editor. "
78 "By default, a new file comprising those changes will be written to <tt>config/gazebo_%1.urdf</tt>")
79 .arg(config_data_->urdf_model_->getName().c_str())
82 layout->addWidget(header);
83 layout->addSpacerItem(
new QSpacerItem(1, 8, QSizePolicy::Fixed, QSizePolicy::Fixed));
86 QHBoxLayout* controls_layout =
new QHBoxLayout();
89 btn_overwrite_ =
new QPushButton(
"Over&write original URDF",
this);
90 btn_overwrite_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
91 connect(btn_overwrite_, SIGNAL(clicked()),
this, SLOT(overwriteURDF()));
92 controls_layout->addWidget(btn_overwrite_);
94 btn_open_ =
new QPushButton(
"&Open original URDF",
this);
95 btn_open_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
96 btn_open_->setToolTip(
"Open original URDF file in editor");
97 connect(btn_open_, SIGNAL(clicked()),
this, SLOT(openURDF()));
98 controls_layout->addWidget(btn_open_);
101 controls_layout->addItem(
new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Fixed));
104 layout->addLayout(controls_layout);
107 no_changes_label_ =
new QLabel(
this);
108 no_changes_label_->setText(
"URDF is ready for Gazebo. No changes required.");
109 no_changes_label_->setFont(QFont(QFont().defaultFamily(), 18));
110 no_changes_label_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
111 no_changes_label_->setAlignment(Qt::AlignTop);
112 layout->addWidget(no_changes_label_);
115 simulation_text_ =
new QTextEdit(
this);
116 simulation_text_->setLineWrapMode(QTextEdit::NoWrap);
117 connect(simulation_text_, &QTextEdit::textChanged,
this, [
this]() { setDirty(); });
118 layout->addWidget(simulation_text_);
121 QTextCharFormat format;
122 format.setForeground(Qt::darkGreen);
123 highlighter->addTag(
"inertial", format);
124 highlighter->addTag(
"transmission", format);
125 highlighter->addTag(
"gazebo", format);
128 copy_urdf_ =
new QLabel(
this);
129 copy_urdf_->setText(
"<a href='contract'>Copy to Clipboard</a>");
130 connect(copy_urdf_, &QLabel::linkActivated,
this, &SimulationWidget::copyURDF);
131 layout->addWidget(copy_urdf_);
134 this->setLayout(layout);
137 void SimulationWidget::setDirty(
bool dirty)
140 config_data_->changes |= MoveItConfigData::SIMULATION;
143 btn_overwrite_->setEnabled(dirty && !config_data_->urdf_from_xacro_);
146 void SimulationWidget::focusGiven()
148 if (!simulation_text_->document()->isEmpty())
151 simulation_text_->setVisible(
true);
152 std::string
text = generateGazeboCompatibleURDF();
153 config_data_->gazebo_urdf_string_ =
text;
155 simulation_text_->document()->setPlainText(QString::fromStdString(text));
158 bool have_changes = !
text.empty();
161 simulation_text_->setVisible(have_changes);
162 btn_overwrite_->setVisible(have_changes);
163 btn_open_->setVisible(have_changes);
164 copy_urdf_->setVisible(have_changes);
165 no_changes_label_->setVisible(!have_changes);
169 if (config_data_->urdf_from_xacro_)
170 tooltip = tr(
"Cannot overwrite original, <i>xacro-based</i> URDF");
172 tooltip = tr(
"Overwrite URDF in original location:<br><tt>%1</tt>").arg(config_data_->urdf_path_.c_str());
173 btn_overwrite_->setToolTip(tooltip);
175 setDirty(have_changes);
178 bool SimulationWidget::focusLost()
180 if (!(config_data_->changes & MoveItConfigData::SIMULATION))
185 auto urdf = simulation_text_->document()->toPlainText().toStdString();
186 doc.Parse(
urdf.c_str(),
nullptr, TIXML_ENCODING_UTF8);
187 if (!
urdf.empty() && doc.Error())
189 QTextCursor cursor = simulation_text_->textCursor();
190 cursor.movePosition(QTextCursor::Start);
191 cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, doc.ErrorRow());
192 cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, doc.ErrorCol());
193 simulation_text_->setTextCursor(cursor);
194 QMessageBox::warning(
this, tr(
"Gazebo URDF"), tr(
"Error parsing XML:\n").
append(doc.ErrorDesc()));
195 simulation_text_->setFocus(Qt::OtherFocusReason);
199 config_data_->gazebo_urdf_string_ = std::move(
urdf);
206 void SimulationWidget::overwriteURDF()
211 if (!config_data_->outputGazeboURDFFile(config_data_->urdf_path_))
212 QMessageBox::warning(
this,
"Gazebo URDF", tr(
"Failed to save to ").
append(config_data_->urdf_path_.c_str()));
217 config_data_->gazebo_urdf_string_.clear();
221 void SimulationWidget::openURDF()
223 QString editor = qgetenv(
"EDITOR");
224 if (editor.isEmpty())
226 QStringList
args{ QString::fromStdString(config_data_->urdf_path_) };
227 if (!QProcess::startDetached(editor,
args))
228 QMessageBox::warning(
this,
"URDF Editor", tr(
"Failed to open editor: <pre>%1</pre>").arg(editor));
234 void SimulationWidget::copyURDF()
236 simulation_text_->selectAll();
237 simulation_text_->copy();
241 std::string SimulationWidget::generateGazeboCompatibleURDF()
const
244 doc.Parse(config_data_->urdf_string_.c_str(),
nullptr, TIXML_ENCODING_UTF8);
245 auto root = doc.RootElement();
248 TiXmlPrinter orig_urdf;
249 doc.Accept(&orig_urdf);
252 std::map<std::string, TiXmlElement*> transmission_elements;
253 for (TiXmlElement* element =
root->FirstChildElement(
"transmission"); element !=
nullptr;
254 element = element->NextSiblingElement(element->Value()))
256 auto type_tag = element->FirstChildElement(
"type");
257 auto joint_tag = element->FirstChildElement(
"joint");
258 if (!type_tag || !type_tag->GetText() || !joint_tag || !joint_tag->Attribute(
"name"))
260 if (std::string(type_tag->GetText()) ==
"transmission_interface/SimpleTransmission")
261 transmission_elements[element->FirstChildElement(
"joint")->Attribute(
"name")] = element;
265 for (TiXmlElement* element =
root->FirstChildElement(); element !=
nullptr; element = element->NextSiblingElement())
267 const std::string tag_name(element->Value());
268 if (tag_name ==
"link" && element->FirstChildElement(
"collision"))
270 TiXmlElement* inertial =
uniqueInsert(*element,
"inertial");
271 uniqueInsert(*inertial,
"mass", { {
"value",
"0.1" } });
272 uniqueInsert(*inertial,
"origin", { {
"xyz",
"0 0 0" }, {
"rpy",
"0 0 0" } });
281 else if (tag_name ==
"joint")
283 const char* joint_type = element->Attribute(
"type");
284 const char* joint_name = element->Attribute(
"name");
285 if (!joint_type || !joint_name || strcmp(joint_type,
"fixed") == 0)
289 TiXmlElement* transmission;
290 auto it = transmission_elements.find(joint_name);
291 if (it != transmission_elements.end())
292 transmission = it->second;
295 transmission =
root->InsertEndChild(TiXmlElement(
"transmission"))->ToElement();
296 transmission->SetAttribute(
"name", std::string(
"trans_") + joint_name);
299 uniqueInsert(*transmission,
"type", {},
"transmission_interface/SimpleTransmission");
301 std::string hw_interface = config_data_->getJointHardwareInterface(joint_name);
302 auto* joint =
uniqueInsert(*transmission,
"joint", { {
"name", joint_name } });
303 uniqueInsert(*joint,
"hardwareInterface", {}, hw_interface.c_str());
305 auto actuator_name = joint_name + std::string(
"_motor");
306 auto* actuator =
uniqueInsert(*transmission,
"actuator", { {
"name", actuator_name.c_str() } });
307 uniqueInsert(*actuator,
"hardwareInterface", {}, hw_interface.c_str());
308 uniqueInsert(*actuator,
"mechanicalReduction", {},
"1");
315 *gazebo,
"plugin", { {
"name",
"gazebo_ros_control",
true }, {
"filename",
"libgazebo_ros_control.so",
true } });
319 TiXmlPrinter new_urdf;
320 doc.Accept(&new_urdf);
322 return orig_urdf.Str() == new_urdf.Str() ? std::string() : new_urdf.Str();