Newer
Older
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
QGROUNDCONTROL 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.
QGROUNDCONTROL 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 QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* @brief Implementation of the waypoint protocol handler
*
* @author Petri Tanskanen <mavteam@student.ethz.ch>
*
*/
#include "UASWaypointManager.h"
#include "UAS.h"
#define PROTOCOL_TIMEOUT_MS 2000 ///< maximum time to wait for pending messages until timeout
#define PROTOCOL_DELAY_MS 40 ///< minimum delay between sent messages
#define PROTOCOL_MAX_RETRIES 3 ///< maximum number of send retries (after timeout)
UASWaypointManager::UASWaypointManager(UAS &_uas)
current_retries(0),
current_wp_id(0),
current_count(0),
current_state(WP_IDLE),
current_partner_systemid(0),
current_partner_compid(0),
protocol_timer(this)
{
connect(&protocol_timer, SIGNAL(timeout()), this, SLOT(timeout()));
}
void UASWaypointManager::timeout()
if (current_retries > 0)
{
protocol_timer.start(PROTOCOL_TIMEOUT_MS);
current_retries--;
emit updateStatusString(tr("Timeout, retrying (retries left: %1)").arg(current_retries));
qDebug() << "Timeout, retrying (retries left:" << current_retries << ")";
if (current_state == WP_GETLIST)
{
sendWaypointRequestList();
}
else if (current_state == WP_GETLIST_GETWPS)
{
sendWaypointRequest(current_wp_id);
}
else if (current_state == WP_SENDLIST)
{
sendWaypointCount();
}
else if (current_state == WP_SENDLIST_SENDWPS)
{
sendWaypoint(current_wp_id);
}
else if (current_state == WP_CLEARLIST)
{
sendWaypointClearAll();
}
else if (current_state == WP_SETCURRENT)
{
sendWaypointSetCurrent(current_wp_id);
}
}
else
{
protocol_timer.stop();
qDebug() << "Waypoint transaction (state=" << current_state << ") timed out going to state WP_IDLE";
emit updateStatusString("Operation timed out.");
current_state = WP_IDLE;
current_count = 0;
current_wp_id = 0;
current_partner_systemid = 0;
current_partner_compid = 0;
}
}
void UASWaypointManager::handleWaypointCount(quint8 systemId, quint8 compId, quint16 count)
{
if (current_state == WP_GETLIST && systemId == current_partner_systemid && compId == current_partner_compid)
{
protocol_timer.start(PROTOCOL_TIMEOUT_MS);
current_retries = PROTOCOL_MAX_RETRIES;
qDebug() << "got waypoint count (" << count << ") from ID " << systemId;
if (count > 0)
{
current_count = count;
current_wp_id = 0;
current_state = WP_GETLIST_GETWPS;
sendWaypointRequest(current_wp_id);
}
else
{
emit updateStatusString("done.");
qDebug() << "No waypoints on UAS " << systemId;
}
else
{
qDebug("Rejecting message, check mismatch: current_state: %d == %d, system id %d == %d, comp id %d == %d", current_state, WP_GETLIST, current_partner_systemid, systemId, current_partner_compid, compId);
}
void UASWaypointManager::handleWaypoint(quint8 systemId, quint8 compId, mavlink_waypoint_t *wp)
{
if (systemId == current_partner_systemid && compId == current_partner_compid && current_state == WP_GETLIST_GETWPS && wp->seq == current_wp_id)
{
protocol_timer.start(PROTOCOL_TIMEOUT_MS);
current_retries = PROTOCOL_MAX_RETRIES;
qDebug() << "Got WP: " << wp->seq << wp->x << wp->y << wp->z << wp->param4 << "auto:" << wp->autocontinue << "curr:" << wp->current << wp->param1 << wp->param2 << (MAV_FRAME) wp->frame << (MAV_COMMAND) wp->command;
Waypoint *lwp = new Waypoint(wp->seq, wp->x, wp->y, wp->z, wp->param4, wp->autocontinue, wp->current, wp->param1, wp->param2, (MAV_FRAME) wp->frame, (MAV_COMMAND) wp->command);
//get next waypoint
current_wp_id++;
if(current_wp_id < current_count)
{
sendWaypointRequest(current_wp_id);
sendWaypointAck(0);
current_state = WP_IDLE;
current_count = 0;
current_wp_id = 0;
current_partner_systemid = 0;
current_partner_compid = 0;
emit updateStatusString("done.");
qDebug() << "got all waypoints from ID " << systemId;
emit updateStatusString(tr("Waypoint ID mismatch, rejecting waypoint"));
else
{
qDebug("Rejecting message, check mismatch: current_state: %d == %d, system id %d == %d, comp id %d == %d", current_state, WP_GETLIST, current_partner_systemid, systemId, current_partner_compid, compId);
}
void UASWaypointManager::handleWaypointAck(quint8 systemId, quint8 compId, mavlink_waypoint_ack_t *wpa)
{
if (systemId == current_partner_systemid && compId == current_partner_compid)
if((current_state == WP_SENDLIST || current_state == WP_SENDLIST_SENDWPS) && (current_wp_id == waypoint_buffer.count()-1 && wpa->type == 0))
{
//all waypoints sent and ack received
protocol_timer.stop();
current_state = WP_IDLE;
emit updateStatusString("done.");
qDebug() << "sent all waypoints to ID " << systemId;
}
else if(current_state == WP_CLEARLIST)
{
protocol_timer.stop();
current_state = WP_IDLE;
emit updateStatusString("done.");
qDebug() << "cleared waypoint list of ID " << systemId;
}
void UASWaypointManager::handleWaypointRequest(quint8 systemId, quint8 compId, mavlink_waypoint_request_t *wpr)
if (systemId == current_partner_systemid && compId == current_partner_compid && ((current_state == WP_SENDLIST && wpr->seq == 0) || (current_state == WP_SENDLIST_SENDWPS && (wpr->seq == current_wp_id || wpr->seq == current_wp_id + 1))))
protocol_timer.start(PROTOCOL_TIMEOUT_MS);
current_retries = PROTOCOL_MAX_RETRIES;
if (wpr->seq < waypoint_buffer.count())
current_state = WP_SENDLIST_SENDWPS;
current_wp_id = wpr->seq;
sendWaypoint(current_wp_id);
else
{
qDebug("Rejecting message, check mismatch: current_state: %d == %d, system id %d == %d, comp id %d == %d", current_state, WP_GETLIST, current_partner_systemid, systemId, current_partner_compid, compId);
}
void UASWaypointManager::handleWaypointReached(quint8 systemId, quint8 compId, mavlink_waypoint_reached_t *wpr)
if (systemId == uas.getUASID() && compId == MAV_COMP_ID_WAYPOINTPLANNER)
{
emit updateStatusString(QString("Reached waypoint %1").arg(wpr->seq));
}
pixhawk
committed
void UASWaypointManager::handleWaypointCurrent(quint8 systemId, quint8 compId, mavlink_waypoint_current_t *wpc)
if (systemId == uas.getUASID() && compId == MAV_COMP_ID_WAYPOINTPLANNER)
{
if (current_state == WP_SETCURRENT)
{
protocol_timer.stop();
current_state = WP_IDLE;
// update the local main storage
if (wpc->seq < waypoints.size())
{
for(int i = 0; i < waypoints.size(); i++)
{
if (waypoints[i]->getId() == wpc->seq)
{
waypoints[i]->setCurrent(true);
}
else
{
waypoints[i]->setCurrent(false);
}
}
}
emit updateStatusString(QString("New current waypoint %1").arg(wpc->seq));
//emit update to UI widgets
emit currentWaypointChanged(wpc->seq);
pixhawk
committed
qDebug() << "new current waypoint" << wpc->seq;
pixhawk
committed
void UASWaypointManager::notifyOfChange(Waypoint* wp)
{
pixhawk
committed
qDebug() << "WAYPOINT CHANGED: ID:" << wp->getId();
// If only one waypoint was changed, emit only WP signal
if (wp != NULL)
{
emit waypointChanged(uas.getUASID(), wp);
}
else
{
emit waypointListChanged();
emit waypointListChanged(uas.getUASID());
}
pixhawk
committed
}
int UASWaypointManager::setCurrentWaypoint(quint16 seq)
if (seq < waypoints.size())
if(current_state == WP_IDLE)
{
//update local main storage
for(int i = 0; i < waypoints.size(); i++)
{
if (waypoints[i]->getId() == seq)
{
waypoints[i]->setCurrent(true);
}
else
{
waypoints[i]->setCurrent(false);
}
}
//TODO: signal changed waypoint list
//send change to UAS - important to note: if the transmission fails, we have inconsistencies
protocol_timer.start(PROTOCOL_TIMEOUT_MS);
current_retries = PROTOCOL_MAX_RETRIES;
current_state = WP_SETCURRENT;
current_wp_id = seq;
current_partner_systemid = uas.getUASID();
current_partner_compid = MAV_COMP_ID_WAYPOINTPLANNER;
sendWaypointSetCurrent(current_wp_id);
//emit waypointListChanged();
return 0;
}
}
return -1;
}
/**
* @param enforceFirstActive Enforces that the first waypoint is set as active
*/
void UASWaypointManager::addWaypoint(Waypoint *wp, bool enforceFirstActive)
{
if (wp)
{
wp->setId(waypoints.size());
if (enforceFirstActive && waypoints.size() == 0) wp->setCurrent(true);
waypoints.insert(waypoints.size(), wp);
connect(wp, SIGNAL(changed(Waypoint*)), this, SLOT(notifyOfChange(Waypoint*)));
emit waypointListChanged();
pixhawk
committed
emit waypointListChanged(uas.getUASID());
int UASWaypointManager::removeWaypoint(quint16 seq)
{
if (seq < waypoints.size())
{
Waypoint *t = waypoints[seq];
waypoints.remove(seq);
delete t;
for(int i = seq; i < waypoints.size(); i++)
{
waypoints[i]->setId(i);
}
emit waypointListChanged();
pixhawk
committed
emit waypointListChanged(uas.getUASID());
return 0;
}
return -1;
}
void UASWaypointManager::moveWaypoint(quint16 cur_seq, quint16 new_seq)
{
if (cur_seq != new_seq && cur_seq < waypoints.size() && new_seq < waypoints.size())
{
Waypoint *t = waypoints[cur_seq];
if (cur_seq < new_seq)
{
for (int i = cur_seq; i < new_seq; i++)
{
waypoints[i] = waypoints[i+1];
//waypoints[i]->setId(i); // let the local IDs stay the same so that they can be found more easily by the user
}
}
else
{
for (int i = cur_seq; i > new_seq; i--)
{
waypoints[i] = waypoints[i-1];
// waypoints[i]->setId(i);
}
}
waypoints[new_seq] = t;
//waypoints[new_seq]->setId(new_seq);
emit waypointListChanged();
pixhawk
committed
emit waypointListChanged(uas.getUASID());
}
}
void UASWaypointManager::saveWaypoints(const QString &saveFile)
{
QFile file(saveFile);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
return;
QTextStream out(&file);
//write the waypoint list version to the first line for compatibility check
out << "QGC WPL 100\r\n";
for (int i = 0; i < waypoints.size(); i++)
{
waypoints[i]->save(out);
}
file.close();
}
void UASWaypointManager::loadWaypoints(const QString &loadFile)
{
QFile file(loadFile);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
while(waypoints.size()>0)
{
Waypoint *t = waypoints[0];
waypoints.remove(0);
delete t;
}
QTextStream in(&file);
const QStringList &version = in.readLine().split(" ");
if (!(version.size() == 3 && version[0] == "QGC" && version[1] == "WPL" && version[2] == "100"))
emit updateStatusString(tr("The waypoint file is not compatible with the current version of QGroundControl."));
//MainWindow::instance()->showCriticalMessage(tr("Error loading waypoint file"),tr("The waypoint file is not compatible with the current version of QGroundControl."));
}
else
{
while (!in.atEnd())
Waypoint *t = new Waypoint();
if(t->load(in))
{
t->setId(waypoints.size());
waypoints.insert(waypoints.size(), t);
}
else
{
emit updateStatusString(tr("The waypoint file is corrupted. Load operation only partly succesful."));
//MainWindow::instance()->showCriticalMessage(tr("Error loading waypoint file"),);
break;
}
file.close();
emit waypointListChanged();
pixhawk
committed
emit waypointListChanged(uas.getUASID());
pixhawk
committed
//void UASWaypointManager::globalAddWaypoint(Waypoint *wp)
//{
// // FIXME Will be removed
// Q_UNUSED(wp);
//}
//int UASWaypointManager::globalRemoveWaypoint(quint16 seq)
//{
// // FIXME Will be removed
// Q_UNUSED(seq);
// return 0;
//}
//void UASWaypointManager::waypointUpdated(Waypoint*)
//{
// // FIXME Add rate limiter
// emit waypointListChanged(uas.getUASID());
//}
void UASWaypointManager::clearWaypointList()
current_retries = PROTOCOL_MAX_RETRIES;
current_state = WP_CLEARLIST;
current_wp_id = 0;
current_partner_systemid = uas.getUASID();
current_partner_compid = MAV_COMP_ID_WAYPOINTPLANNER;
sendWaypointClearAll();
const QVector<Waypoint *> UASWaypointManager::getGlobalFrameWaypointList()
{
// TODO Keep this global frame list up to date
// with complete waypoint list
// instead of filtering on each request
QVector<Waypoint*> wps;
foreach (Waypoint* wp, waypoints)
{
if (wp->getFrame() == MAV_FRAME_GLOBAL)
{
wps.append(wp);
}
}
return wps;
}
int UASWaypointManager::getIndexOf(Waypoint* wp)
{
return waypoints.indexOf(wp);
}
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
int UASWaypointManager::getGlobalFrameIndexOf(Waypoint* wp)
{
// Search through all waypoints,
// counting only those in global frame
int i = 0;
foreach (Waypoint* p, waypoints)
{
if (p->getFrame() == MAV_FRAME_GLOBAL)
{
if (p == wp)
{
return i;
}
i++;
}
}
return -1;
}
int UASWaypointManager::getGlobalFrameCount()
{
// Search through all waypoints,
// counting only those in global frame
int i = 0;
foreach (Waypoint* p, waypoints)
{
if (p->getFrame() == MAV_FRAME_GLOBAL)
{
i++;
}
}
return i;
}
int UASWaypointManager::getLocalFrameCount()
{
// Search through all waypoints,
// counting only those in global frame
int i = 0;
foreach (Waypoint* p, waypoints)
{
if (p->getFrame() == MAV_FRAME_GLOBAL)
{
i++;
}
}
return i;
}
int UASWaypointManager::getLocalFrameIndexOf(Waypoint* wp)
{
// Search through all waypoints,
// counting only those in local frame
int i = 0;
foreach (Waypoint* p, waypoints)
{
if (p->getFrame() == MAV_FRAME_LOCAL)
{
if (p == wp)
{
return i;
}
i++;
}
}
return -1;
}
int UASWaypointManager::getMissionFrameIndexOf(Waypoint* wp)
{
// Search through all waypoints,
// counting only those in mission frame
int i = 0;
foreach (Waypoint* p, waypoints)
{
if (p->getFrame() == MAV_FRAME_MISSION)
{
if (p == wp)
{
return i;
}
i++;
}
}
return -1;
}
void UASWaypointManager::readWaypoints()
{
if(current_state == WP_IDLE)
{
while(waypoints.size()>0)
{
delete waypoints.back();
waypoints.pop_back();
protocol_timer.start(PROTOCOL_TIMEOUT_MS);
current_retries = PROTOCOL_MAX_RETRIES;
current_state = WP_GETLIST;
current_wp_id = 0;
current_partner_systemid = uas.getUASID();
current_partner_compid = MAV_COMP_ID_WAYPOINTPLANNER;
sendWaypointRequestList();
void UASWaypointManager::writeWaypoints()
if (current_state == WP_IDLE)
{
if (waypoints.count() > 0)
current_retries = PROTOCOL_MAX_RETRIES;
current_count = waypoints.count();
current_state = WP_SENDLIST;
current_wp_id = 0;
current_partner_systemid = uas.getUASID();
current_partner_compid = MAV_COMP_ID_WAYPOINTPLANNER;
//clear local buffer
// Why not replace with waypoint_buffer.clear() ?
// because this will lead to memory leaks, the waypoint-structs
// have to be deleted, clear() would only delete the pointers.
while(!waypoint_buffer.empty())
{
delete waypoint_buffer.back();
waypoint_buffer.pop_back();
}
bool noCurrent = true;
//copy waypoint data to local buffer
for (int i=0; i < current_count; i++)
{
waypoint_buffer.push_back(new mavlink_waypoint_t);
mavlink_waypoint_t *cur_d = waypoint_buffer.back();
memset(cur_d, 0, sizeof(mavlink_waypoint_t)); //initialize with zeros
const Waypoint *cur_s = waypoints.at(i);
cur_d->autocontinue = cur_s->getAutoContinue();
cur_d->current = cur_s->getCurrent() & noCurrent; //make sure only one current waypoint is selected, the first selected will be chosen
cur_d->param1 = cur_s->getParam1();
cur_d->param2 = cur_s->getParam2();
cur_d->frame = cur_s->getFrame();
cur_d->seq = i; // don't read out the sequence number of the waypoint class
cur_d->x = cur_s->getX();
cur_d->y = cur_s->getY();
cur_d->z = cur_s->getZ();
if (cur_s->getCurrent() && noCurrent)
noCurrent = false;
//send the waypoint count to UAS (this starts the send transaction)
sendWaypointCount();
else if (waypoints.count() == 0)
{
sendWaypointClearAll();
}
else
{
//we're in another transaction, ignore command
qDebug() << "UASWaypointManager::sendWaypoints() doing something else ignoring command";
}
void UASWaypointManager::sendWaypointClearAll()
{
mavlink_message_t message;
mavlink_waypoint_clear_all_t wpca;
wpca.target_system = uas.getUASID();
wpca.target_component = MAV_COMP_ID_WAYPOINTPLANNER;
emit updateStatusString(QString("Clearing waypoint list..."));
mavlink_msg_waypoint_clear_all_encode(uas.mavlink->getSystemId(), uas.mavlink->getComponentId(), &message, &wpca);
uas.sendMessage(message);
MG::SLEEP::usleep(PROTOCOL_DELAY_MS * 1000);
qDebug() << "sent waypoint clear all to ID " << wpca.target_system;
}
void UASWaypointManager::sendWaypointSetCurrent(quint16 seq)
{
mavlink_message_t message;
mavlink_waypoint_set_current_t wpsc;
wpsc.target_system = uas.getUASID();
wpsc.target_component = MAV_COMP_ID_WAYPOINTPLANNER;
wpsc.seq = seq;
emit updateStatusString(QString("Updating target waypoint..."));
mavlink_msg_waypoint_set_current_encode(uas.mavlink->getSystemId(), uas.mavlink->getComponentId(), &message, &wpsc);
uas.sendMessage(message);
MG::SLEEP::usleep(PROTOCOL_DELAY_MS * 1000);
qDebug() << "sent waypoint set current (" << wpsc.seq << ") to ID " << wpsc.target_system;
}
void UASWaypointManager::sendWaypointCount()
{
mavlink_message_t message;
mavlink_waypoint_count_t wpc;
wpc.target_system = uas.getUASID();
wpc.target_component = MAV_COMP_ID_WAYPOINTPLANNER;
wpc.count = current_count;
qDebug() << "sent waypoint count (" << wpc.count << ") to ID " << wpc.target_system;
emit updateStatusString(QString("Starting to transmit waypoints..."));
mavlink_msg_waypoint_count_encode(uas.mavlink->getSystemId(), uas.mavlink->getComponentId(), &message, &wpc);
uas.sendMessage(message);
MG::SLEEP::usleep(PROTOCOL_DELAY_MS * 1000);
qDebug() << "sent waypoint count (" << wpc.count << ") to ID " << wpc.target_system;
}
void UASWaypointManager::sendWaypointRequestList()
{
mavlink_message_t message;
mavlink_waypoint_request_list_t wprl;
wprl.target_system = uas.getUASID();
wprl.target_component = MAV_COMP_ID_WAYPOINTPLANNER;
emit updateStatusString(QString("Requesting waypoint list..."));
mavlink_msg_waypoint_request_list_encode(uas.mavlink->getSystemId(), uas.mavlink->getComponentId(), &message, &wprl);
uas.sendMessage(message);
MG::SLEEP::usleep(PROTOCOL_DELAY_MS * 1000);
qDebug() << "sent waypoint list request to ID " << wprl.target_system;
void UASWaypointManager::sendWaypointRequest(quint16 seq)
mavlink_message_t message;
mavlink_waypoint_request_t wpr;
wpr.target_system = uas.getUASID();
wpr.target_component = MAV_COMP_ID_WAYPOINTPLANNER;
wpr.seq = seq;
emit updateStatusString(QString("Retrieving waypoint ID %1 of %2 total").arg(wpr.seq).arg(current_count));
mavlink_msg_waypoint_request_encode(uas.mavlink->getSystemId(), uas.mavlink->getComponentId(), &message, &wpr);
uas.sendMessage(message);
MG::SLEEP::usleep(PROTOCOL_DELAY_MS * 1000);
qDebug() << "sent waypoint request (" << wpr.seq << ") to ID " << wpr.target_system;
void UASWaypointManager::sendWaypoint(quint16 seq)
{
mavlink_message_t message;
qDebug() <<" WP Buffer count: "<<waypoint_buffer.count();
wp = waypoint_buffer.at(seq);
wp->target_system = uas.getUASID();
wp->target_component = MAV_COMP_ID_WAYPOINTPLANNER;
emit updateStatusString(QString("Sending waypoint ID %1 of %2 total").arg(wp->seq).arg(current_count));
qDebug() << "sent waypoint (" << wp->seq << ") to ID " << wp->target_system<<" WP Buffer count: "<<waypoint_buffer.count();
mavlink_msg_waypoint_encode(uas.mavlink->getSystemId(), uas.mavlink->getComponentId(), &message, wp);
uas.sendMessage(message);
MG::SLEEP::usleep(PROTOCOL_DELAY_MS * 1000);
void UASWaypointManager::sendWaypointAck(quint8 type)
{
mavlink_message_t message;
mavlink_waypoint_ack_t wpa;
wpa.target_system = uas.getUASID();
wpa.target_component = MAV_COMP_ID_WAYPOINTPLANNER;
wpa.type = type;
mavlink_msg_waypoint_ack_encode(uas.mavlink->getSystemId(), uas.mavlink->getComponentId(), &message, &wpa);
uas.sendMessage(message);
MG::SLEEP::usleep(PROTOCOL_DELAY_MS * 1000);
qDebug() << "sent waypoint ack (" << wpa.type << ") to ID " << wpa.target_system;
}