// Teacher's GUI main window header.
// Copyright (C) 2017 Angel Romero
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
#include "mainguiwindow.h"
#include "ui_mainguiwindow.h"
#include "crazyFlyZoneTab.h"
#include "myGraphicsScene.h"
#include "myGraphicsView.h"
#include "channelLUT.h"
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef CATKIN_MAKE
#include "d_fall_pps/UnlabeledMarker.h"
#include "d_fall_pps/CMRead.h"
#include "d_fall_pps/CrazyflieEntry.h"
#include "d_fall_pps/CMUpdate.h"
#include "d_fall_pps/CMCommand.h"
#include "CentralManagerService.h"
#include
#include
#include
#endif
//#include
#include
#define N_MAX_CRAZYFLIES 20 // protection number
#define UWB_UPDATE_DISABLE 0
#define UWB_UPDATE_ENABLE 1
#define UWB_UPDATE_ANCHORS 5
#define UWB_CALIBRATE_ANCHORS 7
float offset[3] = {0};
std::vector invertCheckBoxes;
#ifdef CATKIN_MAKE
using namespace d_fall_pps;
#endif
MainGUIWindow::MainGUIWindow(int argc, char **argv, QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainGUIWindow)
{
#ifdef CATKIN_MAKE
_rosNodeThread = new rosNodeThread(argc, argv, "my_GUI");
#endif
ui->setupUi(this);
_init();
}
MainGUIWindow::~MainGUIWindow()
{
delete ui;
delete _rosNodeThread;
}
int MainGUIWindow::getTabIndexFromName(QString name)
{
int found_name = -1;
for(int i = 0; i < ui->tabWidget->count(); i++)
{
qDebug("name: %s", name.toStdString().c_str());
qDebug("tabText: %s", ui->tabWidget->tabText(i).toStdString().c_str());
if(name == ui->tabWidget->tabText(i))
{
found_name = i;
}
}
return found_name;
}
void MainGUIWindow::doNumCrazyFlyZonesChanged(int n)
{
// tabs number management, maybe do it in a different way so we dont have to remove and add everything?
// first check if size of tabs is greater than size of vector or viceversa. Have we removed or added a zone?
qDebug("tabWidgetCount : %d", ui->tabWidget->count());
if(ui->tabWidget->count() > scene->crazyfly_zones.size())
{
// we removed one crazyfly_zone, n means index of the one we removed. Look for that index tab and remove it
QString qstr = "CrazyFly ";
qstr.append(QString::number(n+1));
if(scene->crazyfly_zones.size() == 0)
{
ui->tabWidget->clear();
}
int found_index = getTabIndexFromName(qstr);
if(found_index != -1)
{
ui->tabWidget->widget(found_index)->deleteLater();
// ui->tabWidget->removeTab(found_index);
}
// now unlink it from table also:
#ifdef CATKIN_MAKE
if(cf_linker->isCFZoneLinked(n))
{
cf_linker->unlink_cf_zone(n);
}
#endif
}
else if(ui->tabWidget->count() < scene->crazyfly_zones.size())
{
// we added one crazyfly_zone, n means index of the new one. New tab will be labeld index + 1
QString qstr = "CrazyFly ";
qstr.append(QString::number(n+1));
crazyFlyZoneTab* widget = new crazyFlyZoneTab(n);
ui->tabWidget->insertTab(n, widget, qstr);
connect(widget, SIGNAL(centerButtonClickedSignal(int)), this, SLOT(centerViewIndex(int)));
}
updateComboBoxesCFZones();
}
void MainGUIWindow::_init()
{
// initialize checkboxes, spinboxes,....
ui->scaleSpinBox->setRange(0.1, 100);
ui->scaleSpinBox->setSingleStep(0.1);
ui->scaleSpinBox->setValue(1);
ui->checkBox_vicon_crazyflies->setChecked(false);
ui->scaleSpinBox->setEnabled(false);
ui->graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
ui->graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
ui->graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
// error messages
// ui->err_message_cf->hide();
// ui->err_message_cf_zone->hide();
// ui->err_message_student_id->hide();
ui->radioAddress_text->setReadOnly(true);
QPalette *palette = new QPalette();
palette->setColor(QPalette::Base,Qt::lightGray);
palette->setColor(QPalette::Text,Qt::darkGray);
ui->radioAddress_text->setPalette(*palette);
ui->err_message_cf->setStyleSheet("QLabel { color : red; }");
ui->err_message_cf_zone->setStyleSheet("QLabel { color : red; }");
ui->err_message_student_id->setStyleSheet("QLabel { color : red; }");
ui->err_message_radio_address->setStyleSheet("QLabel { color : red; }");
ui->err_message_cf->clear();
ui->err_message_cf_zone->clear();
ui->err_message_student_id->clear();
ui->err_message_radio_address->clear();
// initialize table_links
ui->table_links->setColumnCount(4);
QFont fnt;
fnt.setPointSize(7);
ui->table_links->horizontalHeader()->setFont(fnt);
ui->table_links->horizontalHeader()->setDefaultSectionSize(90);
ui->table_links->verticalHeader()->setDefaultSectionSize(20);
const int rowCount = ui->table_links->rowCount();
const int columnCount = ui->table_links->columnCount();
for(int i = 0; i < rowCount; ++i)
{
for(int j = 0; j < columnCount; ++j)
{
QTableWidgetItem* selectedItem = ui->table_links->item(i, j);
selectedItem->setFont(fnt);
}
}
ui->table_links->setSelectionBehavior(QAbstractItemView::SelectRows);
QStringList horizontal_header;
horizontal_header << "Student ID" << "CrazyFly" << "CrazyFly Zone" << "Radio Address";
ui->table_links->setHorizontalHeaderLabels(horizontal_header);
// scene
scene = new myGraphicsScene(ui->frame_drawing);
scene->setSceneRect(-100 * FROM_METERS_TO_UNITS, -100 * FROM_METERS_TO_UNITS, 200 * FROM_METERS_TO_UNITS, 200 * FROM_METERS_TO_UNITS);
ui->graphicsView->setScene(scene);
// after scene is created, create CFlinker
#ifdef CATKIN_MAKE
cf_linker = new CFLinker(ui, &crazyflies_vector, &scene->crazyfly_zones);
#endif
// connections
QObject::connect(ui->tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(doTabClosed(int)));
QObject::connect(scene, SIGNAL(numCrazyFlyZonesChanged(int)), this, SLOT(doNumCrazyFlyZonesChanged(int)));
QObject::connect(scene, SIGNAL(crazyFlyZoneSelected(int)), this, SLOT(setTabIndex(int)));
QObject::connect(scene, SIGNAL(modeChanged(int)), this, SLOT(transitionToMode(int)));
QObject::connect(scene, SIGNAL(numTablePiecesChanged(int)), this, SLOT(handleTablePiecesNumChanged(int)));
ui->checkBox_vicon_highlight_markers->setEnabled(false);
#ifdef CATKIN_MAKE
_rosNodeThread->init();
qRegisterMetaType("ptrToMessage");
qRegisterMetaType("std_msgs::Int32MultiArray");
qRegisterMetaType("CrazyfliePositionData");
QObject::connect(_rosNodeThread, SIGNAL(newViconData(const ptrToMessage&)), this, SLOT(updateNewViconData(const ptrToMessage&)));
QObject::connect(_rosNodeThread, SIGNAL(newStudentIDs(const std_msgs::Int32MultiArray&)), this, SLOT(updateStudentIDs(const std_msgs::Int32MultiArray&)));
QObject::connect(_rosNodeThread, SIGNAL(newPositionData(const CrazyfliePositionData&)), this, SLOT(updateCFPositions(const CrazyfliePositionData&)));
QObject::connect(cf_linker, SIGNAL(updateComboBoxes()), this, SLOT(updateComboBoxes()));
ros::NodeHandle nodeHandle("~");
UWBServiceClient = nodeHandle.serviceClient("/UWBManagerService/UWBData", false);
DBChangedPublisher = nodeHandle.advertise("DBChanged", 1);
emergencyStopPublisher = nodeHandle.advertise("emergencyStop", 1);
refreshStudents_publisher = nodeHandle.advertise("refreshStudents", 1);
UWBServiceClientUpdate_publisher = nodeHandle.advertise("UWBUpdate", 1);
// TODO RENAME
UWBSettings_publisher = nodeHandle.advertise("enableUWB", 1);
QObject::connect(ui->checkBox_enable_UWB, SIGNAL(stateChanged(int)), this, SLOT(on_checkBox_enable_UWB_toggled(bool)));
// add space bar shortcut to stop all motors
ui->all_motors_off_button->setShortcut(tr("Space"));
QShortcut* sk = new QShortcut(QKeySequence(tr("CTRL+C")), this, SLOT(close()));
// set anchor table settings
ui->table_anchorPos->verticalHeader()->setVisible(false);
ui->table_anchorPos->setColumnCount(4);
ui->table_anchorPos->horizontalHeader()->setFont(fnt);
ui->table_anchorPos->verticalHeader()->setFont(fnt);
ui->table_anchorPos->horizontalHeader()->setDefaultSectionSize(90);
ui->table_anchorPos->verticalHeader()->setDefaultSectionSize(20);
QStringList anchor_header;
anchor_header << "Anchor id" << "x" << "y" << "z";
ui->table_anchorPos->setHorizontalHeaderLabels(anchor_header);
invertCheckBoxes.push_back(ui->invA1);
invertCheckBoxes.push_back(ui->invA2);
invertCheckBoxes.push_back(ui->invA3);
invertCheckBoxes.push_back(ui->invA4);
invertCheckBoxes.push_back(ui->invA5);
invertCheckBoxes.push_back(ui->invA6);
#endif
}
void MainGUIWindow::doTabClosed(int tab_index)
{
QString name = ui->tabWidget->tabText(tab_index);
#ifdef CATKIN_MAKE
int cf_zone_index = cf_linker->getCFZoneIndexFromName(name);
scene->removeCrazyFlyZone(cf_zone_index);
#endif
}
void MainGUIWindow::setTabIndex(int index)
{
QString qstr = "CrazyFly ";
qstr.append(QString::number(index + 1));
ui->tabWidget->setCurrentIndex(getTabIndexFromName(qstr));
}
void MainGUIWindow::updateComboBoxes()
{
updateComboBoxesCFs();
updateComboBoxesCFZones();
}
void MainGUIWindow::updateComboBoxesCFs()
{
#ifdef CATKIN_MAKE
ui->comboBoxCFs->clear();
for(int i = 0; i < crazyflies_vector.size(); i++)
{
if(!cf_linker->isCFLinked(crazyflies_vector[i]->getName()))
{
QString qstr = QString::fromStdString(crazyflies_vector[i]->getName());
ui->comboBoxCFs->addItem(qstr);
}
}
#endif
}
void MainGUIWindow::updateComboBoxesCFZones()
{
ui->comboBoxCFZones->clear();
#ifdef CATKIN_MAKE
for(int i = 0; i < scene->crazyfly_zones.size(); i++)
{
if(!cf_linker->isCFZoneLinked(scene->crazyfly_zones[i]->getIndex()))
{
int cf_zone_index = scene->crazyfly_zones[i]->getIndex();
QString qstr = "CrazyFlyZone ";
qstr.append(QString::number(cf_zone_index + 1));
ui->comboBoxCFZones->addItem(qstr);
}
}
#endif
}
#ifdef CATKIN_MAKE
void MainGUIWindow::updateCFPositions(const CrazyfliePositionData& data)
{
bool inScene = false;
int index;
// Reconvert CFPD to CFD
CrazyflieData cfdata;
cfdata.crazyflieName = std::to_string(data.id);
cfdata.x = data.x;
cfdata.y = data.y;
cfdata.z = data.z;
cfdata.roll = data.roll;
cfdata.pitch = data.pitch;
cfdata.yaw = data.yaw;
cfdata.acquiringTime = data.acquiringTime;
cfdata.occluded = false;
for(int i = 0; i < cf_positionsVector.size(); ++i)
{
if(cf_positionsVector[i]->getName() == cfdata.crazyflieName)
{
cf_positionsVector[i]->updateCF(&cfdata);
inScene = true;
index = i;
break;
}
}
if(!inScene)
{
QString filename(":/images/center_rect.svg");
crazyFly* tmp_p_crazyfly = new crazyFly(&cfdata, filename);
cf_positionsVector.push_back(tmp_p_crazyfly);
index = cf_positionsVector.size() - 1;
}
if(ui->checkBox_vicon_crazyflies->checkState() == Qt::Checked)
{
if(!cf_positionsVector[index]->isAddedToScene())
{
scene->addItem(cf_positionsVector[index]);
cf_positionsVector[index]->setAddedToScene(true);
}
}
}
void MainGUIWindow::updateStudentIDs(const std_msgs::Int32MultiArray& ids)
{
ui->list_discovered_student_ids->clear();
for(int i = 0; i < ids.data.size(); ++i)
ui->list_discovered_student_ids->addItem(std::to_string(ids.data[i]).c_str());
}
void MainGUIWindow::updateNewViconData(const ptrToMessage& p_msg) //connected to newViconData, from node
{
// update Markers
if(p_msg->markers.size() < markers_vector.size()) // some markers have dissapeared, received stuff is smaller than what we have
{
for(int i = p_msg->markers.size(); i < markers_vector.size(); i++)
{
scene->removeItem(markers_vector[i]); // remove objects from scene
// ROS_INFO_STREAM("element index: " << i << " removed");
}
markers_vector.erase(markers_vector.begin() + p_msg->markers.size(), markers_vector.end()); //delete them
}
// ROS_INFO_STREAM("markers.size: " << p_msg->markers.size());
for(int i = 0; i < p_msg->markers.size(); i++) // here, or new markers message is equal to current messages, or greater (some new markers)
{
if(i >= markers_vector.size()) //some new markers coming
{
// ROS_INFO_STREAM("element index: " << i << " added");
Marker* tmp_p_marker = new Marker(&(p_msg->markers[i]));
markers_vector.push_back(tmp_p_marker); // what happens with the new indexes? check if this is correct
if(ui->checkBox_vicon_markers->checkState() == Qt::Checked) //only if markers checkbox info is checked..
{
scene->addItem(markers_vector[i]);
if(ui->checkBox_vicon_highlight_markers->checkState() == Qt::Checked)
{
markers_vector[i]->setHighlighted();
}
}
}
else
{
// ROS_INFO_STREAM("element index: " << i << " moved, already existed");
markers_vector[i]->updateMarker(&(p_msg->markers[i]));
}
}
// update Crazyflies
// also: what happens if we dont go through one of the names? we need to remove that crazyfly
int crazyfly_vector_size_before = crazyflies_vector.size(); //initial size of vector
// in this loop, add new ones and update old ones
for(int i = 0; i < p_msg->crazyflies.size(); i++)
{
bool name_found = false; // for each iteration, name_found starts in false
int index_name_found;
for(int j = 0; j < crazyfly_vector_size_before; j++)
{
if(crazyflies_vector[j]->getName() == p_msg->crazyflies[i].crazyflieName)
{
name_found = true; // name found. This can only happen once per i-iteration, names are unique
index_name_found = j; // index in already existing vector, to update it later (really needed?)
}
}
if(name_found)
{
crazyflies_vector[index_name_found]->updateCF(&(p_msg->crazyflies[i]));
}
else //name not found, newly arrived, add it to the vector
{
// now, if name follows our format, put the corresponding number. If not, put the unknown image
std::string s = p_msg->crazyflies[i].crazyflieName;
std::smatch m;
std::regex e ("PPS_CF([0-9]{2})");
QString filename(":/images/drone_fixed_");
if(std::regex_search(s, m, e))
{
std::string found_string = m[1].str();
filename.append(QString::fromStdString(found_string));
filename.append(".svg");
}
else
{
filename.append("unk.svg");
}
crazyFly* tmp_p_crazyfly = new crazyFly(&(p_msg->crazyflies[i]), filename);
crazyflies_vector.push_back(tmp_p_crazyfly);
}
if(ui->checkBox_vicon_crazyflies->checkState() == Qt::Checked)
{
for(int i = 0; i < crazyflies_vector.size(); i++) //check for occlussion
{
if(crazyflies_vector[i]->isOccluded())
{
// ROS_INFO("===================OCCLUDED");
if(crazyflies_vector[i]->isAddedToScene())
{
scene->removeItem(crazyflies_vector[i]);
crazyflies_vector[i]->setAddedToScene(false);
}
}
else
{
if(!crazyflies_vector[i]->isAddedToScene())
{
scene->addItem(crazyflies_vector[i]);
crazyflies_vector[i]->setAddedToScene(true);
}
}
}
}
}
// in this loop, clean the ones that are not present anymore. UPDATE: this will apparently only happen when we tick and untick in Vicon
int crazyfly_vector_size_after = crazyflies_vector.size();
for(int j = crazyfly_vector_size_after - 1; j >= 0; j--)
{
bool name_found = false;
for(int i = 0; i < p_msg->crazyflies.size(); i++)
{
if(crazyflies_vector[j]->getName() == p_msg->crazyflies[i].crazyflieName)
{
name_found = true;
}
}
if(!name_found)
{
scene->removeItem(crazyflies_vector[j]);
crazyflies_vector.erase(crazyflies_vector.begin() + j);
}
}
}
#endif
void MainGUIWindow::on_removeTable_clicked()
{
if(scene->getMode() == myGraphicsScene::mode_table)
{
scene->removeTable();
}
}
void MainGUIWindow::transitionToMode(int mode)
{
switch(mode)
{
case myGraphicsScene::mode_table:
{
ui->removeTable->setDisabled(false);
break;
}
case myGraphicsScene::mode_crazyfly_zones:
{
ui->removeTable->setDisabled(true);
break;
}
}
}
void MainGUIWindow::on_radioButton_table_mode_toggled(bool checked)
{
switch(scene->getMode())
{
case myGraphicsScene::mode_table:
{
// already in the mode we want, do nothing
break;
}
case myGraphicsScene::mode_crazyfly_zones:
{
scene->setMode(myGraphicsScene::mode_table);
break;
}
case myGraphicsScene::mode_locked:
{
scene->setMode(myGraphicsScene::mode_table);
break;
}
}
}
void MainGUIWindow::on_radioButton_crazyfly_zones_mode_toggled(bool checked)
{
switch(scene->getMode())
{
case myGraphicsScene::mode_table:
{
scene->setMode(myGraphicsScene::mode_crazyfly_zones);
break;
}
case myGraphicsScene::mode_crazyfly_zones:
{
// already in the mode we want, do nothing
break;
}
case myGraphicsScene::mode_locked:
{
scene->setMode(myGraphicsScene::mode_crazyfly_zones);
break;
}
}
}
void MainGUIWindow::handleTablePiecesNumChanged(int newNum)
{
}
void MainGUIWindow::on_radioButton_lock_mode_toggled(bool checked)
{
switch(scene->getMode())
{
case myGraphicsScene::mode_table:
{
scene->setMode(myGraphicsScene::mode_locked);
break;
}
case myGraphicsScene::mode_crazyfly_zones:
{
scene->setMode(myGraphicsScene::mode_locked);
break;
}
case myGraphicsScene::mode_locked:
{
break;
}
}
}
void MainGUIWindow::on_checkBox_grid_toggled(bool checked)
{
scene->setGrid(checked);
}
void MainGUIWindow::on_checkBox_table_toggled(bool checked)
{
if(checked)
{
scene->showTable();
}
else
{
scene->hideTable();
}
}
void MainGUIWindow::on_checkBox_crazyfly_zones_toggled(bool checked)
{
if(checked)
{
scene->showCrazyFlyZones();
}
else
{
scene->hideCrazyFlyZones();
}
}
void MainGUIWindow::on_tabWidget_currentChanged(int index)
{
// // this index is tab index. Need to go to cf index
// QString name = ui->tabWidget->tabText(index);
// #ifdef CATKIN_MAKE
// int cf_index = cf_linker->getCFZoneIndexFromName(name);
// scene->setSelectedCrazyFlyZone(cf_index);
// #endif
}
void MainGUIWindow::centerViewIndex(int index)
{
ui->graphicsView->fitInView(scene->getRectFCrazyFlyZone(index), Qt::KeepAspectRatio);
ui->graphicsView->scale(0.95, 0.95); // A bit back zoom, so we can see everything better
}
void MainGUIWindow::on_pushButton_fitAll_clicked()
{
ui->graphicsView->fitInView(scene->itemsBoundingRect(), Qt::KeepAspectRatio);
ui->graphicsView->scale(0.95, 0.95); // A bit back zoom, so we can see everything better
}
void MainGUIWindow::on_checkBox_vicon_markers_toggled(bool checked)
{
if(checked)
{
#ifdef CATKIN_MAKE
for(int i = 0; i < markers_vector.size(); i++)
{
scene->addItem(markers_vector[i]);
}
#endif
ui->checkBox_vicon_highlight_markers->setCheckable(true);
ui->checkBox_vicon_highlight_markers->setEnabled(true);
}
else
{
#ifdef CATKIN_MAKE
for(int i = 0; i < markers_vector.size(); i++)
{
scene->removeItem(markers_vector[i]);
}
#endif
ui->checkBox_vicon_highlight_markers->setChecked(false);
ui->checkBox_vicon_highlight_markers->setCheckable(false);
ui->checkBox_vicon_highlight_markers->setEnabled(false);
}
}
void MainGUIWindow::on_checkBox_vicon_highlight_markers_toggled(bool checked)
{
if(checked)
{
#ifdef CATKIN_MAKE
for(int i = 0; i < markers_vector.size(); i++)
{
markers_vector[i]->setHighlighted();
}
#endif
}
else
{
#ifdef CATKIN_MAKE
for(int i = 0; i < markers_vector.size(); i++)
{
markers_vector[i]->clearHighlighted();
}
#endif
}
}
void MainGUIWindow::on_checkBox_vicon_crazyflies_toggled(bool checked)
{
if(checked)
{
#ifdef CATKIN_MAKE
for(int i = 0; i < crazyflies_vector.size(); i++)
{
if(!crazyflies_vector[i]->isAddedToScene())
{
scene->addItem(crazyflies_vector[i]);
crazyflies_vector[i]->setAddedToScene(true);
}
}
#endif
ui->scaleSpinBox->setEnabled(true);
}
else
{
#ifdef CATKIN_MAKE
for(int i = 0; i < crazyflies_vector.size(); i++)
{
if(crazyflies_vector[i]->isAddedToScene())
{
scene->removeItem(crazyflies_vector[i]);
crazyflies_vector[i]->setAddedToScene(false);
}
}
#endif
ui->scaleSpinBox->setEnabled(false);
}
}
void MainGUIWindow::on_scaleSpinBox_valueChanged(double arg1)
{
#ifdef CATKIN_MAKE
for(int i = 0; i < crazyflies_vector.size(); i++)
{
crazyflies_vector[i]->setScaleCFs(arg1);
}
#endif
}
void MainGUIWindow::on_refresh_cfs_button_clicked()
{
updateComboBoxesCFs();
}
void MainGUIWindow::on_refresh_student_ids_button_clicked()
{
#ifdef CATKIN_MAKE
// Publish the button being pressed
std_msgs::Int32 msg;
msg.data = 1;
this->refreshStudents_publisher.publish(msg);
#endif
}
void MainGUIWindow::on_link_button_clicked()
{
#ifdef CATKIN_MAKE
bool error = false;
if(ui->comboBoxCFs->count() == 0)
{
// plot error message
ui->err_message_cf->setText("CF box is empty");
error = true;
}
else
{
ui->err_message_cf->clear();
}
if(ui->comboBoxCFZones->count() == 0)
{
// plot error message
ui->err_message_cf_zone->setText("CFZone box is empty");
error = true;
}
else
{
ui->err_message_cf_zone->clear();
}
if(cf_linker->isRadioAddressLinked(ui->radioAddress_text->text().toStdString()))
{
ui->err_message_radio_address->setText("Already in use");
error = true;
}
else if(ui->radioAddress_text->text().toStdString() == "")
{
ui->err_message_radio_address->setText("Field is empty");
error = true;
}
else
{
ui->err_message_radio_address->clear();
}
if(cf_linker->isStudentIDLinked(ui->spinBox_student_ids->value()))
{
// plot error message
ui->err_message_student_id->setText("This StudentID has already been linked");
error = true;
}
else
{
ui->err_message_student_id->clear();
}
if(!error)
{
cf_linker->link(ui->spinBox_student_ids->value(), cf_linker->getCFZoneIndexFromName(ui->comboBoxCFZones->currentText()), ui->comboBoxCFs->currentText().toStdString(), ui->radioAddress_text->text().toStdString());
}
#endif
}
void MainGUIWindow::on_unlink_button_clicked()
{
#ifdef CATKIN_MAKE
cf_linker->unlink_selection();
#endif
}
void MainGUIWindow::on_save_in_DB_button_clicked()
{
// we need to update and then save?
CrazyflieDB tmp_db;
for(int i = 0; i < cf_linker->links.size(); i++)
{
CrazyflieEntry tmp_entry;
tmp_entry.crazyflieContext.crazyflieName = cf_linker->links[i].cf_name;
tmp_entry.crazyflieContext.crazyflieAddress = cf_linker->links[i].radio_address;
tmp_entry.crazyflieContext.localArea.crazyfly_zone_index = cf_linker->links[i].cf_zone_index;
tmp_entry.studentID = cf_linker->links[i].student_id;
for(int j = 0; j < scene->crazyfly_zones.size(); j++)
{
if(cf_linker->links[i].cf_zone_index == scene->crazyfly_zones[j]->getIndex())
{
double x_min = scene->crazyfly_zones[j]->sceneBoundingRect().bottomLeft().x();
double y_min = - scene->crazyfly_zones[j]->sceneBoundingRect().bottomLeft().y();
double x_max = scene->crazyfly_zones[j]->sceneBoundingRect().topRight().x();
double y_max = -scene->crazyfly_zones[j]->sceneBoundingRect().topRight().y();
tmp_entry.crazyflieContext.localArea.xmin = x_min * FROM_UNITS_TO_METERS;
tmp_entry.crazyflieContext.localArea.xmax = x_max * FROM_UNITS_TO_METERS;
tmp_entry.crazyflieContext.localArea.ymin = y_min * FROM_UNITS_TO_METERS;
tmp_entry.crazyflieContext.localArea.ymax = y_max * FROM_UNITS_TO_METERS;
tmp_entry.crazyflieContext.localArea.zmin = -0.2;
tmp_entry.crazyflieContext.localArea.zmax = 2.0;
}
}
tmp_db.crazyflieEntries.push_back(tmp_entry);
}
m_data_base = tmp_db;
ROS_INFO_STREAM("database:\n" << m_data_base);
// save the database in the file
fill_database_file();
// Now also publish a ROS message stating that we changed the DB, so the nodes can update it
std_msgs::Int32 msg;
msg.data = 1;
this->DBChangedPublisher.publish(msg);
}
void MainGUIWindow::clear_database_file()
{
CrazyflieDB tmp_db;
if(read_database_from_file(tmp_db) == 0)
{
for(int i = 0; i < tmp_db.crazyflieEntries.size(); i++)
{
CMUpdate updateCall;
updateCall.request.mode = ENTRY_REMOVE;
updateCall.request.crazyflieEntry.crazyflieContext.crazyflieName = tmp_db.crazyflieEntries[i].crazyflieContext.crazyflieName;
if(_rosNodeThread->m_update_db_client.call(updateCall))
{
ROS_INFO("database changed in central manager service");
}
else
{
ROS_ERROR("Failed to remove entry in DB");
}
}
save_database_file();
}
else
{
ROS_INFO("Failed to read DB");
}
}
void MainGUIWindow::fill_database_file()
{
clear_database_file();
ROS_INFO("cleared data base file");
ROS_INFO_STREAM("database:\n" << m_data_base);
for(int i = 0; i < m_data_base.crazyflieEntries.size(); i++)
{
ROS_INFO("inserted 1 item in DB");
insert_or_update_entry_database(m_data_base.crazyflieEntries[i]);
}
save_database_file();
}
void MainGUIWindow::save_database_file()
{
CMCommand commandCall;
commandCall.request.command = CMD_SAVE;
if(_rosNodeThread->m_command_db_client.call(commandCall))
{
ROS_INFO("successfully saved db");
}
else
{
ROS_ERROR("failed to save db");
}
}
void MainGUIWindow::insert_or_update_entry_database(CrazyflieEntry entry)
{
CMUpdate updateCall;
updateCall.request.mode = ENTRY_INSERT_OR_UPDATE;
updateCall.request.crazyflieEntry = entry;
_rosNodeThread->m_update_db_client.call(updateCall);
}
int MainGUIWindow::read_database_from_file(CrazyflieDB &read_db)
{
CMRead getDBCall;
_rosNodeThread->m_read_db_client.waitForExistence(ros::Duration(-1));
if(_rosNodeThread->m_read_db_client.call(getDBCall))
{
read_db = getDBCall.response.crazyflieDB;
return 0;
}
else
{
return -1;
}
}
void MainGUIWindow::on_load_from_DB_button_clicked()
{
CrazyflieDB tmp_db;
if(read_database_from_file(tmp_db) == 0)
{
ROS_INFO_STREAM("database:\n" << tmp_db);
m_data_base = tmp_db;
cf_linker->clear_all_links();
// remove all cf_zones existing
scene->removeAllCFZones();
int size = scene->crazyfly_zones.size();
ROS_INFO("vector_cf_zones_size %d", size);
for(int i = 0; i < m_data_base.crazyflieEntries.size(); i++)
{
std::string cf_name = m_data_base.crazyflieEntries[i].crazyflieContext.crazyflieName;
std::string radio_address = m_data_base.crazyflieEntries[i].crazyflieContext.crazyflieAddress;
int cf_zone_index = m_data_base.crazyflieEntries[i].crazyflieContext.localArea.crazyfly_zone_index;
// we should first create the cf zones that are in the database?
bool cf_zone_exists;
qreal width = m_data_base.crazyflieEntries[i].crazyflieContext.localArea.xmax - m_data_base.crazyflieEntries[i].crazyflieContext.localArea.xmin;
qreal height = m_data_base.crazyflieEntries[i].crazyflieContext.localArea.ymax - m_data_base.crazyflieEntries[i].crazyflieContext.localArea.ymin;
QRectF tmp_rect(m_data_base.crazyflieEntries[i].crazyflieContext.localArea.xmin * FROM_METERS_TO_UNITS,
- m_data_base.crazyflieEntries[i].crazyflieContext.localArea.ymax * FROM_METERS_TO_UNITS, // minus sign because qt has y-axis inverted
width * FROM_METERS_TO_UNITS,
height * FROM_METERS_TO_UNITS);
int student_id = m_data_base.crazyflieEntries[i].studentID;
scene->addCFZone(tmp_rect, cf_zone_index);
cf_linker->link(student_id, cf_zone_index, cf_name, radio_address);
}
}
else
{
ROS_ERROR("Failed to read DB");
}
}
bool MainGUIWindow::updateUWBSettings(bool enableChecked)
{
// Receive the updated information
Anchors a;
a.request.x = offset[0];
a.request.y = offset[1];
a.request.z = offset[2];
if(UWBServiceClient.call(a))
{
if(!a.response.calSuccess)
return false;
updateAnchors(&a);
}
else
{
ui->checkBox_enable_UWB->setChecked(!enableChecked);
ROS_ERROR("[Teacher GUI] Could not update UWB Settings!");
return false;
}
// Publish message that clients have to reload data from UWBManagerService
std_msgs::Int32 msg;
msg.data = 1;
UWBSettings_publisher.publish(msg);
ui->checkBox_enable_UWB->setEnabled(true);
return true;
}
void MainGUIWindow::updateAnchors(const Anchors* const a)
{
ui->table_anchorPos->setRowCount(0);
for(int i = 0; i < anchor_markers.size(); ++i)
delete anchor_markers[i];
anchor_markers.clear();
ui->checkBox_enable_UWB->setChecked(a->response.enableUWB);
for(int i = 0; i < a->response.anchorArray.length; ++i)
{
// create new row
ui->table_anchorPos->insertRow(i);
// add the id
QString a_id = QString::number(a->response.anchorArray.data[i].id + 1);
QTableWidgetItem* item_id = new QTableWidgetItem(a_id);
item_id->setFlags(item_id->flags() & ~Qt::ItemIsEditable);
item_id->setTextAlignment(Qt::AlignLeft);
ui->table_anchorPos->setItem(i, 0, item_id);
// add x component
std::stringstream strm;
strm << std::fixed << std::setprecision(3) << a->response.anchorArray.data[i].x / 10.0;
QString a_x = QString::fromStdString(strm.str());
strm.str(std::string());
QTableWidgetItem* item_x = new QTableWidgetItem(a_x);
item_x->setTextAlignment(Qt::AlignRight);
item_x->setFlags(item_x->flags() & ~Qt::ItemIsEditable);
ui->table_anchorPos->setItem(i, 1, item_x);
// add y component
strm << a->response.anchorArray.data[i].y / 10.0;
QString a_y = QString::fromStdString(strm.str());
strm.str(std::string());
QTableWidgetItem* item_y = new QTableWidgetItem(a_y);
item_y->setTextAlignment(Qt::AlignRight);
item_y->setFlags(item_y->flags() & ~Qt::ItemIsEditable);
ui->table_anchorPos->setItem(i, 2, item_y);
// add z component
strm << a->response.anchorArray.data[i].z / 10.0;
QString a_z = QString::fromStdString(strm.str());
strm.str(std::string());
QTableWidgetItem* item_z = new QTableWidgetItem(a_z);
item_z->setTextAlignment(Qt::AlignRight);
item_z->setFlags(item_z->flags() & ~Qt::ItemIsEditable);
ui->table_anchorPos->setItem(i, 3, item_z);
// add anchor to scene
QString filename(":/images/anchor.svg");
UWBMarker* u = new UWBMarker(&(a->response.anchorArray.data[i]), filename);
anchor_markers.push_back(u);
scene->addItem(anchor_markers.back());
}
}
void MainGUIWindow::on_checkBox_enable_UWB_toggled(bool checked)
{
// Send Command to enable UWB to UWBManagerService
std_msgs::Int32 server_msg;
if(checked)
server_msg.data = UWB_UPDATE_ENABLE;
else
server_msg.data = UWB_UPDATE_DISABLE;
UWBServiceClientUpdate_publisher.publish(server_msg);
updateUWBSettings(checked);
}
void MainGUIWindow::on_reloadAnchors_button_pressed()
{
// Load Data from UWBManagerService
std_msgs::Int32 server_msg;
server_msg.data = UWB_UPDATE_ANCHORS;
UWBServiceClientUpdate_publisher.publish(server_msg);
updateUWBSettings(!ui->checkBox_enable_UWB->isChecked());
}
void MainGUIWindow::on_reloadAnchorsFromFile_button_pressed()
{
// Load Data from UWBManagerService
std_msgs::Int32 server_msg;
server_msg.data = UWB_UPDATE_ANCHORS;
UWBServiceClientUpdate_publisher.publish(server_msg);
ROS_WARN("[Teacher GUI] Loading Anchors from file!");
// Receive the updated information
Anchors a;
if(UWBServiceClient.call(a))
{
updateAnchors(&a);
}
else
{
ROS_ERROR("[Teacher GUI] Could not update UWB Settings!");
return;
}
// Publish message that clients have to reload data from UWBManagerService
std_msgs::Int32 msg;
msg.data = 1;
UWBSettings_publisher.publish(msg);
ui->checkBox_enable_UWB->setEnabled(true);
}
void MainGUIWindow::on_calibrateAnchors_button_pressed()
{
std_msgs::Int32 msg;
msg.data = UWB_CALIBRATE_ANCHORS;
UWBServiceClientUpdate_publisher.publish(msg);
if(!updateUWBSettings(!ui->checkBox_enable_UWB->isChecked()))
{
ROS_ERROR("[Teacher GUI] UWB Anchor calibration failed!");
ui->checkBox_localisation_done->setChecked(false);
return;
}
ui->checkBox_localisation_done->setChecked(true);
ui->checkBox_enable_UWB->setEnabled(true);
ui->reloadAnchors_button->setEnabled(true);
ui->reloadAnchorsFromFile_button->setEnabled(false);
ui->set_offset_button->setEnabled(true);
ui->uwbxoffset->setEnabled(true);
ui->uwbyoffset->setEnabled(true);
ui->uwbzoffset->setEnabled(true);
ui->set_invert_button->setEnabled(true);
for(int i = 0; i < 6; ++i)
invertCheckBoxes[i]->setEnabled(true);
}
void MainGUIWindow::on_set_offset_button_pressed()
{
offset[0] = ui->uwbxoffset->value() * 10;
offset[1] = ui->uwbyoffset->value() * 10;
offset[2] = ui->uwbzoffset->value() * 10;
updateUWBSettings(!ui->checkBox_enable_UWB->isChecked());
offset[0] = offset[1] = offset[2] = 0;
ui->uwbxoffset->setValue(0);
ui->uwbyoffset->setValue(0);
ui->uwbzoffset->setValue(0);
ROS_WARN("[Teacher GUI] Offset set!");
}
void MainGUIWindow::on_set_invert_button_pressed()
{
unsigned char invert = 0;
for(int i = 0; i < 6; ++i)
{
invert |= ( ((bool) invertCheckBoxes[i]->checkState()) << i);
invertCheckBoxes[i]->setChecked(false);
}
if(invert == 0)
return;
// Receive the updated information
Anchors a;
a.request.invert = invert;
if(UWBServiceClient.call(a))
{
if(!a.response.calSuccess)
return;
updateAnchors(&a);
}
else
{
ROS_ERROR("[Teacher GUI] Could not update UWB Settings!");
return;
}
// Publish message that clients have to reload data from UWBManagerService
std_msgs::Int32 msg;
msg.data = 1;
UWBSettings_publisher.publish(msg);
ROS_WARN("[Teacher GUI] Inverted Anchor Coordinates!");
}
void MainGUIWindow::on_comboBoxCFs_currentTextChanged(const QString &arg1)
{
std::string key = arg1.toStdString();
auto it = channel_LUT.find(key);
if(it != channel_LUT.end())
{
std::string found = it->second;
QString found_qstr = QString::fromStdString(found);
ui->radioAddress_text->setText(found_qstr);
}
else
{
ROS_INFO("name not found in LUT");
}
}
void MainGUIWindow::on_all_motors_off_button_clicked()
{
std_msgs::Int32 msg;
msg.data = CMD_CRAZYFLY_MOTORS_OFF;
emergencyStopPublisher.publish(msg);
ROS_ERROR("All motors switched off!");
}