/**************************************************************************** * * (c) 2009-2016 QGROUNDCONTROL PROJECT * * QGroundControl is licensed according to the terms in the file * COPYING.md in the root of the source code directory. * ****************************************************************************/ #include "LinkInterface.h" #include "QGCApplication.h" bool LinkInterface::active() const { QMapIterator iter(_mavlinkMessagesTimers); while (iter.hasNext()) { iter.next(); if (iter.value()->getActive()) { return true; } } return false; } bool LinkInterface::link_active(int vehicle_id) const { if (_mavlinkMessagesTimers.contains(vehicle_id)) { return _mavlinkMessagesTimers.value(vehicle_id)->getActive(); } else { return false; } } /// mavlink channel to use for this link, as used by mavlink_parse_char. The mavlink channel is only /// set into the link when it is added to LinkManager uint8_t LinkInterface::mavlinkChannel(void) const { if (!_mavlinkChannelSet) { qWarning() << "Call to LinkInterface::mavlinkChannel with _mavlinkChannelSet == false"; } return _mavlinkChannel; } // Links are only created by LinkManager so constructor is not public LinkInterface::LinkInterface(SharedLinkConfigurationPointer& config, bool isPX4Flow) : QThread (0) , _config (config) , _highLatency (config->isHighLatency()) , _mavlinkChannelSet (false) , _enableRateCollection (false) , _decodedFirstMavlinkPacket(false) , _isPX4Flow (isPX4Flow) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); _config->setLink(this); // Initialize everything for the data rate calculation buffers. _inDataIndex = 0; _outDataIndex = 0; // Initialize our data rate buffers. memset(_inDataWriteAmounts, 0, sizeof(_inDataWriteAmounts)); memset(_inDataWriteTimes, 0, sizeof(_inDataWriteTimes)); memset(_outDataWriteAmounts,0, sizeof(_outDataWriteAmounts)); memset(_outDataWriteTimes, 0, sizeof(_outDataWriteTimes)); QObject::connect(this, &LinkInterface::_invokeWriteBytes, this, &LinkInterface::_writeBytes); qRegisterMetaType("LinkInterface*"); } /// This function logs the send times and amounts of datas for input. Data is used for calculating /// the transmission rate. /// @param byteCount Number of bytes received /// @param time Time in ms send occurred void LinkInterface::_logInputDataRate(quint64 byteCount, qint64 time) { if(_enableRateCollection) _logDataRateToBuffer(_inDataWriteAmounts, _inDataWriteTimes, &_inDataIndex, byteCount, time); } /// This function logs the send times and amounts of datas for output. Data is used for calculating /// the transmission rate. /// @param byteCount Number of bytes sent /// @param time Time in ms receive occurred void LinkInterface::_logOutputDataRate(quint64 byteCount, qint64 time) { if(_enableRateCollection) _logDataRateToBuffer(_outDataWriteAmounts, _outDataWriteTimes, &_outDataIndex, byteCount, time); } /** * @brief logDataRateToBuffer Stores transmission times/amounts for statistics * * This function logs the send times and amounts of datas to the given circular buffers. * This data is used for calculating the transmission rate. * * @param bytesBuffer[out] The buffer to write the bytes value into. * @param timeBuffer[out] The buffer to write the time value into * @param writeIndex[out] The write index used for this buffer. * @param bytes The amount of bytes transmit. * @param time The time (in ms) this transmission occurred. */ void LinkInterface::_logDataRateToBuffer(quint64 *bytesBuffer, qint64 *timeBuffer, int *writeIndex, quint64 bytes, qint64 time) { QMutexLocker dataRateLocker(&_dataRateMutex); int i = *writeIndex; // Now write into the buffer, if there's no room, we just overwrite the first data point. bytesBuffer[i] = bytes; timeBuffer[i] = time; // Increment and wrap the write index ++i; if (i == _dataRateBufferSize) { i = 0; } *writeIndex = i; } /** * @brief getCurrentDataRate Get the current data rate given a data rate buffer. * * This function attempts to use the times and number of bytes transmit into a current data rate * estimation. Since it needs to use timestamps to get the timeperiods over when the data was sent, * this is effectively a global data rate over the last _dataRateBufferSize - 1 data points. Also note * that data points older than NOW - dataRateCurrentTimespan are ignored. * * @param index The first valid sample in the data rate buffer. Refers to the oldest time sample. * @param dataWriteTimes The time, in ms since epoch, that each data sample took place. * @param dataWriteAmounts The amount of data (in bits) that was transferred. * @return The bits per second of data transferrence of the interface over the last [-statsCurrentTimespan, 0] timespan. */ qint64 LinkInterface::_getCurrentDataRate(int index, const qint64 dataWriteTimes[], const quint64 dataWriteAmounts[]) const { const qint64 now = QDateTime::currentMSecsSinceEpoch(); // Limit the time we calculate to the recent past const qint64 cutoff = now - _dataRateCurrentTimespan; // Grab the mutex for working with the stats variables QMutexLocker dataRateLocker(&_dataRateMutex); // Now iterate through the buffer of all received data packets adding up all values // within now and our cutof. qint64 totalBytes = 0; qint64 totalTime = 0; qint64 lastTime = 0; int size = _dataRateBufferSize; while (size-- > 0) { // If this data is within our cutoff time, include it in our calculations. // This also accounts for when the buffer is empty and filled with 0-times. if (dataWriteTimes[index] > cutoff && lastTime > 0) { // Track the total time, using the previous time as our timeperiod. totalTime += dataWriteTimes[index] - lastTime; totalBytes += dataWriteAmounts[index]; } // Track the last time sample for doing timespan calculations lastTime = dataWriteTimes[index]; // Increment and wrap the index if necessary. if (++index == _dataRateBufferSize) { index = 0; } } // Return the final calculated value in bits / s, converted from bytes/ms. qint64 dataRate = (totalTime != 0)?(qint64)((float)totalBytes * 8.0f / ((float)totalTime / 1000.0f)):0; // Finally return our calculated data rate. return dataRate; } /// Sets the mavlink channel to use for this link void LinkInterface::_setMavlinkChannel(uint8_t channel) { if (_mavlinkChannelSet) { qWarning() << "Mavlink channel set multiple times"; } _mavlinkChannelSet = true; _mavlinkChannel = channel; } void LinkInterface::_activeChanged(bool active, int vehicle_id) { emit activeChanged(this, active, vehicle_id); } void LinkInterface::startMavlinkMessagesTimer(int vehicle_id) { if (_mavlinkMessagesTimers.contains(vehicle_id)) { _mavlinkMessagesTimers.value(vehicle_id)->restartTimer(); } else { _mavlinkMessagesTimers.insert(vehicle_id, new MavlinkMessagesTimer(vehicle_id, _highLatency)); QObject::connect(_mavlinkMessagesTimers.value(vehicle_id), &MavlinkMessagesTimer::activeChanged, this, &LinkInterface::_activeChanged); _mavlinkMessagesTimers.value(vehicle_id)->init(); } } void LinkInterface::stopMavlinkMessagesTimer() { QMapIterator iter(_mavlinkMessagesTimers); while (iter.hasNext()) { iter.next(); QObject::disconnect(iter.value(), &MavlinkMessagesTimer::activeChanged, this, &LinkInterface::_activeChanged); _mavlinkMessagesTimers[iter.key()]->deleteLater(); _mavlinkMessagesTimers[iter.key()] = nullptr; } _mavlinkMessagesTimers.clear(); }