Newer
Older
#include "UASParameterCommsMgr.h"
#include "QGCUASParamManager.h"
#include "UASInterface.h"
UASParameterCommsMgr::UASParameterCommsMgr(QObject *parent) :
transmissionListMode(false),
transmissionActive(false),
transmissionTimeout(0),
retransmissionTimeout(1000),
rewriteTimeout(1000),
}
UASParameterCommsMgr* UASParameterCommsMgr::initWithUAS(UASInterface* uas)
{
mav = uas;
paramDataModel = mav->getParamManager()->dataModel();
loadParamCommsSettings();
//Requesting parameters one-by-one from mav
connect(this, SIGNAL(parameterUpdateRequestedById(int,int)),
mav, SLOT(requestParameter(int,int)));
// Sending params to the UAS
connect(this, SIGNAL(commitPendingParameter(int,QString,QVariant)),
mav, SLOT(setParameter(int,QString,QVariant)));
connect(mav, SIGNAL(parameterChanged(int,int,int,int,QString,QVariant)),
this, SLOT(receivedParameterUpdate(int,int,int,int,QString,QVariant)));
connect(&retransmissionTimer, SIGNAL(timeout()),
this, SLOT(retransmissionGuardTick()));
void UASParameterCommsMgr::loadParamCommsSettings()
{
QSettings settings;
settings.beginGroup("QGC_MAVLINK_PROTOCOL");
int val = settings.value("PARAMETER_RETRANSMISSION_TIMEOUT", retransmissionTimeout).toInt(&ok);
qDebug() << "retransmissionTimeout" << retransmissionTimeout;
}
val = settings.value("PARAMETER_REWRITE_TIMEOUT", rewriteTimeout).toInt(&ok);
/**
* Send a request to deliver the list of onboard parameters
* from the MAV.
*/
void UASParameterCommsMgr::requestParameterList()
{
if (!mav) {
return;
}
if (!transmissionListMode) {
// Clear transmission state
receivedParamsList.clear();
transmissionListSizeKnown.clear();
transmissionListMode = true;
foreach (int key, missingReadPackets.keys()) {
missingReadPackets.value(key)->clear();
}
transmissionActive = true;
setParameterStatusMsg(tr("Requested param list.. waiting"));
listRecvTimeout = QGC::groundTimeMilliseconds() + 10000;
setRetransmissionGuardEnabled(true);
}
else {
qDebug() << __FILE__ << __LINE__ << "Ignoring requestParameterList because we're receiving params list";
/*
Empty read retransmission list
Empty write retransmission list
*/
void UASParameterCommsMgr::clearRetransmissionLists(int& missingReadCount, int& missingWriteCount )
{
qDebug() << __FILE__ << __LINE__ << "clearRetransmissionLists";
QList<int> readKeys = missingReadPackets.keys();
missingReadCount += missingReadPackets.value(compId)->count();
missingReadPackets.value(compId)->clear();
QList<int> writeKeys = missingWriteAckPackets.keys();
missingWriteCount += missingWriteAckPackets.value(compId)->count();
missingWriteAckPackets.value(compId)->clear();
void UASParameterCommsMgr::emitPendingParameterCommit(int compId, const QString& key, QVariant& value)
{
int paramType = (int)value.type();
switch (paramType)
{
case QVariant::Char:
{
QVariant fixedValue(QChar((unsigned char)value.toInt()));
emit commitPendingParameter(compId, key, fixedValue);
}
break;
case QVariant::Int:
{
QVariant fixedValue(value.toInt());
emit commitPendingParameter(compId, key, fixedValue);
}
break;
case QVariant::UInt:
{
QVariant fixedValue(value.toUInt());
emit commitPendingParameter(compId, key, fixedValue);
}
break;
case QMetaType::Float:
{
QVariant fixedValue(value.toFloat());
emit commitPendingParameter(compId, key, fixedValue);
}
break;
default:
qCritical() << "ABORTED PARAM SEND, NO VALID QVARIANT TYPE";
return;
}
setParameterStatusMsg(tr("Requested write of: %1: %2").arg(key).arg(value.toDouble()));
}
void UASParameterCommsMgr::resendReadWriteRequests()
{
int compId;
QList<int> compIds;
// Re-request at maximum retransmissionBurstRequestSize parameters at once
// to prevent link flooding'
int requestedReadCount = 0;
compIds = missingReadPackets.keys();
foreach (compId, compIds) {
// Request n parameters from this component (at maximum)
QList<int>* missingReadParams = missingReadPackets.value(compId, NULL);
qDebug() << "missingReadParams:" << missingReadParams->count();
foreach (int paramId, *missingReadParams) {
if (requestedReadCount < retransmissionBurstRequestSize) {
//qDebug() << __FILE__ << __LINE__ << "RETRANSMISSION GUARD REQUESTS RETRANSMISSION OF PARAM #" << paramId << "FROM COMPONENT #" << compId;
emit parameterUpdateRequestedById(compId, paramId);
setParameterStatusMsg(tr("Requested retransmission of #%1").arg(paramId+1));
requestedReadCount++;
}
else {
qDebug() << "Throttling read retransmit requests at" << requestedReadCount;
break;
}
}
}
// Re-request at maximum retransmissionBurstRequestSize parameters at once
// to prevent write-request link flooding
int requestedWriteCount = 0;
compIds = missingWriteAckPackets.keys();
foreach (compId, compIds) {
QMap <QString, QVariant>* missingParams = missingWriteAckPackets.value(compId);
foreach (QString key, missingParams->keys()) {
if (requestedWriteCount < retransmissionBurstRequestSize) {
// Re-request write operation
QVariant value = missingParams->value(key);
emitPendingParameterCommit(compId, key, value);
requestedWriteCount++;
}
else {
qDebug() << "Throttling write retransmit requests at" << requestedWriteCount;
break;
}
}
}
if ((0 == requestedWriteCount) && (0 == requestedReadCount) ) {
qDebug() << __FILE__ << __LINE__ << "No pending re-read or rewrite requests";
if (!transmissionListMode) {
setRetransmissionGuardEnabled(false);
transmissionActive = false;
}
else {
//restart the timer now that we've sent
setRetransmissionGuardEnabled(true);
}
void UASParameterCommsMgr::resetAfterListReceive()
{
transmissionListMode = false;
transmissionListSizeKnown.clear();
//We shouldn't clear missingPackets because other transactions might be using them?
void UASParameterCommsMgr::retransmissionGuardTick()
{
quint64 curTime = QGC::groundTimeMilliseconds();
//Workaround for an apparent Qt bug that causes retransmission guard timer to fire prematurely (350ms)
int elapsed = (int)(curTime - lastTimerReset);
qDebug() << "retransmissionGuardTick elapsed:" << (curTime - lastTimerReset);
//reset the guard timer: it fired prematurely
setRetransmissionGuardEnabled(true);
return;
}
qDebug() << __FILE__ << __LINE__ << "RETRANSMISSION GUARD ACTIVE after" << elapsed;
if (transmissionActive) {
if (transmissionListMode && transmissionListSizeKnown.isEmpty() ) {
//we are still waitin for the first parameter list response
if (curTime > this->listRecvTimeout) {
//re-request parameters
setParameterStatusMsg(tr("TIMEOUT: Re-requesting param list"),ParamCommsStatusLevel_Warning);
listRecvTimeout = curTime + 10000;
//reset the timer
setRetransmissionGuardEnabled(true);
}
return;
}
// Check for timeout
// stop retransmission attempts on timeout
if (curTime > transmissionTimeout) {
setRetransmissionGuardEnabled(false);
int missingReadCount, missingWriteCount;
clearRetransmissionLists(missingReadCount,missingWriteCount);
if ((missingReadCount > 0) || (missingWriteCount > 0)) {
setParameterStatusMsg(tr("TIMEOUT! MISSING: %1 read, %2 write.").arg(missingReadCount).arg(missingWriteCount),
ParamCommsStatusLevel_Warning);
}
resendReadWriteRequests();
}
else {
qDebug() << __FILE__ << __LINE__ << "STOPPING RETRANSMISSION GUARD GRACEFULLY";
setRetransmissionGuardEnabled(false);
}
}
/**
* Enabling the retransmission guard enables the parameter widget to track
* dropped parameters and to re-request them. This works for both individual
* parameter reads as well for whole list requests.
*
* @param enabled True if retransmission checking should be enabled, false else
*/
void UASParameterCommsMgr::setRetransmissionGuardEnabled(bool enabled)
{
if (enabled) {
retransmissionTimer.start(retransmissionTimeout);
lastTimerReset = QGC::groundTimeMilliseconds() ;
} else {
retransmissionTimer.stop();
}
}
void UASParameterCommsMgr::requestParameterUpdate(int component, const QString& parameter)
{
if (mav) {
mav->requestParameter(component, parameter);
}
}
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
void UASParameterCommsMgr::requestRcCalibrationParamsUpdate()
{
if (!transmissionListMode) {
QString minTpl("RC%1_MIN");
QString maxTpl("RC%1_MAX");
QString trimTpl("RC%1_TRIM");
QString revTpl("RC%1_REV");
// Do not request the RC type, as these values depend on this
// active onboard parameter
for (unsigned int i = 1; i < (RC_CAL_CHAN_MAX+1); ++i) {
qDebug() << "Request RC " << i;
mav->requestParameter(0, minTpl.arg(i));
QGC::SLEEP::usleep(5000);
mav->requestParameter(0, trimTpl.arg(i));
QGC::SLEEP::usleep(5000);
mav->requestParameter(0, maxTpl.arg(i));
QGC::SLEEP::usleep(5000);
mav->requestParameter(0, revTpl.arg(i));
QGC::SLEEP::usleep(5000);
}
}
else {
qDebug() << __FILE__ << __LINE__ << "Ignoring requestRcCalibrationParamsUpdate because we're receiving params list";
}
}
/**
* @param component the subsystem which has the parameter
* @param parameterName name of the parameter, as delivered by the system
* @param value value of the parameter
*/
void UASParameterCommsMgr::setParameter(int component, QString parameterName, QVariant value)
{
if (parameterName.isEmpty()) {
return;
}
double dblValue = value.toDouble();
if (paramDataModel->isValueLessThanParamMin(parameterName,dblValue)) {
setParameterStatusMsg(tr("REJ. %1, %2 < min").arg(parameterName).arg(dblValue),
ParamCommsStatusLevel_Error
);
return;
}
if (paramDataModel->isValueGreaterThanParamMax(parameterName,dblValue)) {
setParameterStatusMsg(tr("REJ. %1, %2 > max").arg(parameterName).arg(dblValue),
ParamCommsStatusLevel_Error
);
paramDataModel->getOnboardParamValue(component,parameterName,onboardVal);
if (onboardVal == value) {
setParameterStatusMsg(tr("REJ. %1 already %2").arg(parameterName).arg(dblValue),
ParamCommsStatusLevel_Warning
);
emitPendingParameterCommit(component, parameterName, value);
// Wait for parameter to be written back
// mark it therefore as missing
if (!missingWriteAckPackets.contains(component)) {
missingWriteAckPackets.insert(component, new QMap<QString, QVariant>());
}
// Insert it in missing write ACK list
missingWriteAckPackets.value(component)->insert(parameterName, value);
transmissionTimeout += rewriteTimeout;
}
quint64 newTransmissionTimeout = QGC::groundTimeMilliseconds() + rewriteTimeout;
if (newTransmissionTimeout > transmissionTimeout) {
transmissionTimeout = newTransmissionTimeout;
}
transmissionActive = true;
}
// Enable guard / reset timeouts
setRetransmissionGuardEnabled(true);
}
void UASParameterCommsMgr::setParameterStatusMsg(const QString& msg, ParamCommsStatusLevel_t level)
{
qDebug() << "parameterStatusMsg: " << msg;
parameterStatusMsg = msg;
/**
* @param uas System which has the component
* @param component id of the component
* @param parameterName human friendly name of the parameter
*/
void UASParameterCommsMgr::receivedParameterUpdate(int uas, int compId, int paramCount, int paramId, QString paramName, QVariant value)
{
Q_UNUSED(uas); //this object is assigned to one UAS only
qDebug() << "compId" << compId << "receivedParameterUpdate:" << paramName;
//notify the data model that we have an updated param
paramDataModel->handleParamUpdate(compId,paramName,value);
// Missing packets list has to be instantiated for all components
if (!missingReadPackets.contains(compId)) {
missingReadPackets.insert(compId, new QList<int>());
QList<int>* compMissReadPackets = missingReadPackets.value(compId);
// List mode is different from single parameter transfers
if (transmissionListMode) {
// Only accept the list size once on the first packet from
// each component
if (!transmissionListSizeKnown.contains(compId)) {
// Mark list size as known
transmissionListSizeKnown.insert(compId, true);
qDebug() << "Mark all parameters as missing: " << paramCount;
for (int i = 1; i < paramCount; ++i) { //TODO check: param Id 0 is "all parameters" and not valid ?
if (!compMissReadPackets->contains(i)) {
compMissReadPackets->append(i);
}
}
// There is only one transmission timeout for all components
// since components do not manage their transmission,
// the longest timeout is safe for all components.
quint64 thisTransmissionTimeout = QGC::groundTimeMilliseconds() + (paramCount*retransmissionTimeout);
if (thisTransmissionTimeout > transmissionTimeout) {
transmissionTimeout = thisTransmissionTimeout;
}
}
}
// Mark this parameter as received in read list
int index = compMissReadPackets->indexOf(paramId);
compMissReadPackets->removeAt(index);
}
bool justWritten = false;
bool writeMismatch = false;
// Mark this parameter as received in write ACK list
QMap<QString, QVariant>* compMissWritePackets = missingWriteAckPackets.value(compId);
if (!compMissWritePackets) {
//we sometimes send a write request on compId 0 and get a response on a nonzero compId eg 50
compMissWritePackets = missingWriteAckPackets.value(0);
}
if (compMissWritePackets && compMissWritePackets->contains(paramName)) {
if (compMissWritePackets->value(paramName) != value) {
compMissWritePackets->remove(paramName);
int missReadCount = 0;
foreach (int key, missingReadPackets.keys()) {
missReadCount += missingReadPackets.value(key)->count();
foreach (int key, missingWriteAckPackets.keys()) {
missWriteCount += missingWriteAckPackets.value(key)->count();
if (justWritten) {
if (!writeMismatch) {
setParameterStatusMsg(tr("SUCCESS: Wrote %2 (#%1/%4): %3 [%5]").arg(paramId+1).arg(paramName).arg(value.toDouble()).arg(paramCount).arg(missWriteCount));
if (0 == missWriteCount) {
setParameterStatusMsg(tr("SUCCESS: WROTE ALL PARAMETERS"));
if (persistParamsAfterSend) {
writeParamsToPersistentStorage();
persistParamsAfterSend = false;
}
}
}
else {
// Mismatch, tell user
setParameterStatusMsg(tr("FAILURE: Wrote %1: sent %2 != onboard %3").arg(paramName).arg(compMissWritePackets->value(paramName).toDouble()).arg(value.toDouble()),
ParamCommsStatusLevel_Warning);
}
if (missReadCount == 0) {
// Transmission done
QTime time = QTime::currentTime();
QString timeString = time.toString();
setParameterStatusMsg(tr("All received. (updated at %1)").arg(timeString));
}
else {
// Transmission in progress
QString val = QString("%1").arg(value.toFloat(), 5, 'f', 1, QChar(' '));
setParameterStatusMsg(tr("OK: %1 %2 (%3/%4)").arg(paramName).arg(val).arg(paramCount-missReadCount).arg(paramCount),
//transmissionMissingPackets
}
}
// Check if last parameter was received
if (missReadCount == 0 && missWriteCount == 0) {
setRetransmissionGuardEnabled(false);
//all parameters have been received, broadcast to UI
emit parameterListUpToDate();
}
//reset the timeout timer since we received one
setRetransmissionGuardEnabled(true);
//qDebug() << "missCount:" << missCount << "missWriteCount:" << missWriteCount;
//if (missCount < 4) {
// foreach (int key, transmissionMissingPackets.keys()) {
// QList<int>* list = transmissionMissingPackets.value(key);
// if (list && list->count()) {
// QString yazza = QString().sprintf("Component %d missing %d: ",key,list->count());
// for (int i = 0; i < list->count(); i++) {
// int val = list->at(i);
// yazza.append( QString().sprintf("%d,",val) );
// }
// qDebug() << yazza;
// }
// else {
// //very suspicious...no actual missing items??
// transmissionMissingPackets.remove(key);
// break;
// }
// }
//}
}
void UASParameterCommsMgr::writeParamsToPersistentStorage()
{
if (mav) {
mav->writeParametersToStorage(); //TODO track timeout, retransmit etc?
void UASParameterCommsMgr::sendPendingParameters(bool copyToPersistent)
persistParamsAfterSend |= copyToPersistent;
// Iterate through all components, through all pending parameters and send them to UAS
int parametersSent = 0;
QMap<int, QMap<QString, QVariant>*>* changedValues = paramDataModel->getAllPendingParams();
QMap<int, QMap<QString, QVariant>*>::iterator i;
for (i = changedValues->begin(); i != changedValues->end(); ++i) {
// Iterate through the parameters of the component
int compId = i.key();
QMap<QString, QVariant>* paramList = i.value();
QMap<QString, QVariant>::iterator j;
setParameterStatusMsg(tr("%1 pending params for component %2").arg(paramList->count()).arg(compId));
for (j = paramList->begin(); j != paramList->end(); ++j) {
//TODO mavlink command for "set parameter list" ?
setParameter(compId, j.key(), j.value());
parametersSent++;
}
}
// Change transmission status if necessary
if (parametersSent == 0) {
setParameterStatusMsg(tr("No transmission: No changed values."),ParamCommsStatusLevel_Warning);
if (persistParamsAfterSend) {
writeParamsToPersistentStorage();
}
}
else {
setParameterStatusMsg(tr("Transmitting %1 parameters.").arg(parametersSent));
// Set timeouts
if (transmissionActive) {
transmissionTimeout += parametersSent*rewriteTimeout;
}
else {
transmissionActive = true;
quint64 newTransmissionTimeout = QGC::groundTimeMilliseconds() + parametersSent*rewriteTimeout;
if (newTransmissionTimeout > transmissionTimeout) {
transmissionTimeout = newTransmissionTimeout;
}
}
// Enable guard
setRetransmissionGuardEnabled(true);
qDebug() << "Pending parameters now:" << paramDataModel->countPendingParams();
UASParameterCommsMgr::~UASParameterCommsMgr()
{
setRetransmissionGuardEnabled(false);
QString ptrStr;
ptrStr.sprintf("%8p", this);
qDebug() << "UASParameterCommsMgr destructor: " << ptrStr ;