LinkInterface.h 13.8 KB
Newer Older
pixhawk's avatar
pixhawk committed
1
/*=====================================================================
2

pixhawk's avatar
pixhawk committed
3
PIXHAWK Micro Air Vehicle Flying Robotics Toolkit
4

pixhawk's avatar
pixhawk committed
5
(c) 2009 PIXHAWK PROJECT  <http://pixhawk.ethz.ch>
6

pixhawk's avatar
pixhawk committed
7
This file is part of the PIXHAWK project
8 9 10 11 12 13 14 15 16 17 18 19 20 21

PIXHAWK 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.

PIXHAWK 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 PIXHAWK. If not, see <http://www.gnu.org/licenses/>.

pixhawk's avatar
pixhawk committed
22
======================================================================*/
23

pixhawk's avatar
pixhawk committed
24
/**
25 26 27 28 29 30
* @file
*   @brief Brief Description
*
*   @author Lorenz Meier <mavteam@student.ethz.ch>
*
*/
pixhawk's avatar
pixhawk committed
31 32 33 34 35

#ifndef _LINKINTERFACE_H_
#define _LINKINTERFACE_H_

#include <QThread>
36 37 38
#include <QDateTime>
#include <QMutex>
#include <QMutexLocker>
Lorenz Meier's avatar
Lorenz Meier committed
39
#include <QMetaType>
40
#include <QSharedPointer>
pixhawk's avatar
pixhawk committed
41

Don Gagne's avatar
Don Gagne committed
42 43
#include "QGCMAVLink.h"

44
class LinkManager;
45
class LinkConfiguration;
46

pixhawk's avatar
pixhawk committed
47
/**
48 49 50 51
* The link interface defines the interface for all links used to communicate
* with the groundstation application.
*
**/
52 53
class LinkInterface : public QThread
{
54
    Q_OBJECT
55

56
    // Only LinkManager is allowed to create/delete or _connect/_disconnect a link
57
    friend class LinkManager;
58

pixhawk's avatar
pixhawk committed
59
public:
Don Gagne's avatar
Don Gagne committed
60 61 62 63 64 65
    Q_PROPERTY(bool active      READ active         WRITE setActive         NOTIFY activeChanged)

    // Property accessors
    bool active(void)                       { return _active; }
    void setActive(bool active)             { _active = active; emit activeChanged(active); }

66 67 68 69 70 71
    /**
     * @brief Get link configuration (if used)
     * @return A pointer to the instance of LinkConfiguration if supported. NULL otherwise.
     **/
    virtual LinkConfiguration* getLinkConfiguration() { return NULL; }

72 73 74 75 76
    /* Connection management */

    /**
     * @brief Get the human readable name of this link
     */
77
    virtual QString getName() const = 0;
78

79 80
    virtual void requestReset() = 0;

81 82 83 84 85
    /**
     * @brief Determine the connection status
     *
     * @return True if the connection is established, false otherwise
     **/
86
    virtual bool isConnected() const = 0;
87 88 89 90

    /* Connection characteristics */

    /**
91
     * @Brief Get the maximum connection speed for this interface.
92 93 94 95 96 97 98
     *
     * The nominal data rate is the theoretical maximum data rate of the
     * interface. For 100Base-T Ethernet this would be 100 Mbit/s (100'000'000
     * Bit/s, NOT 104'857'600 Bit/s).
     *
     * @return The nominal data rate of the interface in bit per second, 0 if unknown
     **/
99
    virtual qint64 getConnectionSpeed() const = 0;
Don Gagne's avatar
Don Gagne committed
100 101 102
    
    /// @return true: This link is replaying a log file, false: Normal two-way communication link
    virtual bool isLogReplay(void) { return false; }
103

dogmaphobic's avatar
dogmaphobic committed
104 105 106 107 108 109 110 111
    /**
     * @Enable/Disable data rate collection
     **/
    void enableDataRate(bool enable)
    {
        _enableRateCollection = enable;
    }

112 113 114 115 116 117 118 119
    /**
     * @Brief Get the current incoming data rate.
     *
     * This should be over a short timespan, something like 100ms. A precise value isn't necessary,
     * and this can be filtered, but should be a reasonable estimate of current data rate.
     *
     * @return The data rate of the interface in bits per second, 0 if unknown
     **/
120
    qint64 getCurrentInputDataRate() const
121
    {
122
        return _getCurrentDataRate(_inDataIndex, _inDataWriteTimes, _inDataWriteAmounts);
123
    }
124 125 126 127 128 129 130 131 132

    /**
     * @Brief Get the current outgoing data rate.
     *
     * This should be over a short timespan, something like 100ms. A precise value isn't necessary,
     * and this can be filtered, but should be a reasonable estimate of current data rate.
     *
     * @return The data rate of the interface in bits per second, 0 if unknown
     **/
133
    qint64 getCurrentOutputDataRate() const
134
    {
135
        return _getCurrentDataRate(_outDataIndex, _outDataWriteTimes, _outDataWriteAmounts);
136
    }
137 138 139 140
    
    /// 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 getMavlinkChannel(void) const { Q_ASSERT(_mavlinkChannelSet); return _mavlinkChannel; }
141

Don Gagne's avatar
Don Gagne committed
142 143 144 145
    // These are left unimplemented in order to cause linker errors which indicate incorrect usage of
    // connect/disconnect on link directly. All connect/disconnect calls should be made through LinkManager.
    bool connect(void);
    bool disconnect(void);
146

pixhawk's avatar
pixhawk committed
147 148
public slots:

149 150 151 152 153
    /**
     * @brief This method allows to write bytes to the interface.
     *
     * If the underlying communication is packet oriented,
     * one write command equals a datagram. In case of serial
154 155
     * communication arbitrary byte lengths can be written. The method ensures
     * thread safety regardless of the underlying LinkInterface implementation.
156 157 158 159
     *
     * @param bytes The pointer to the byte array containing the data
     * @param length The length of the data array
     **/
160 161 162 163 164 165
    void writeBytesSafe(const char *bytes, int length)
    {
        emit _invokeWriteBytes(QByteArray(bytes, length));
    }

private slots:
166
    virtual void _writeBytes(const QByteArray) = 0;
167
    
pixhawk's avatar
pixhawk committed
168
signals:
Don Gagne's avatar
Don Gagne committed
169 170
    void autoconnectChanged(bool autoconnect);
    void activeChanged(bool active);
171
    void _invokeWriteBytes(QByteArray);
172

173 174 175
    /// Signalled when a link suddenly goes away due to it being removed by for example pulling the cable to the connection.
    void connectionRemoved(LinkInterface* link);

176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
    /**
     * @brief New data arrived
     *
     * The new data is contained in the QByteArray data. The data is copied for each
     * receiving protocol. For high-speed links like image transmission this might
     * affect performance, for control links it is however desirable to directly
     * forward the link data.
     *
     * @param data the new bytes
     */
    void bytesReceived(LinkInterface* link, QByteArray data);

    /**
     * @brief This signal is emitted instantly when the link is connected
     **/
    void connected();

    /**
     * @brief This signal is emitted instantly when the link is disconnected
     **/
    void disconnected();

    /**
     * @brief This signal is emitted if the human readable name of this link changes
     */
    void nameChanged(QString name);
pixhawk's avatar
pixhawk committed
202

203
    /** @brief Communication error occured */
204
    void communicationError(const QString& title, const QString& error);
205

206 207
    void communicationUpdate(const QString& linkname, const QString& text);

pixhawk's avatar
pixhawk committed
208
protected:
209 210
    // Links are only created by LinkManager so constructor is not public
    LinkInterface() :
Don Gagne's avatar
Don Gagne committed
211 212 213
        QThread(0)
        , _mavlinkChannelSet(false)
        , _active(false)
dogmaphobic's avatar
dogmaphobic committed
214
        , _enableRateCollection(false)
215 216 217 218 219 220 221 222 223 224 225
    {
        // 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));
        
226
        QObject::connect(this, &LinkInterface::_invokeWriteBytes, this, &LinkInterface::_writeBytes);
227 228
        qRegisterMetaType<LinkInterface*>("LinkInterface*");
    }
229

230 231 232 233 234
    /// 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 occured
    void _logInputDataRate(quint64 byteCount, qint64 time) {
dogmaphobic's avatar
dogmaphobic committed
235 236
        if(_enableRateCollection)
            _logDataRateToBuffer(_inDataWriteAmounts, _inDataWriteTimes, &_inDataIndex, byteCount, time);
237 238 239 240 241 242 243
    }
    
    /// 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 occured
    void _logOutputDataRate(quint64 byteCount, qint64 time) {
dogmaphobic's avatar
dogmaphobic committed
244 245
        if(_enableRateCollection)
            _logDataRateToBuffer(_outDataWriteAmounts, _outDataWriteTimes, &_outDataIndex, byteCount, time);
246 247 248
    }
    
private:
249 250 251 252 253 254 255 256 257 258 259 260
    /**
     * @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.
     */
261
    void _logDataRateToBuffer(quint64 *bytesBuffer, qint64 *timeBuffer, int *writeIndex, quint64 bytes, qint64 time)
262
    {
263 264
        QMutexLocker dataRateLocker(&_dataRateMutex);
        
265 266 267 268 269 270 271 272
        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;
273
        if (i == _dataRateBufferSize)
274 275 276 277 278 279 280 281 282 283 284
        {
            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,
285
     * this is effectively a global data rate over the last _dataRateBufferSize - 1 data points. Also note
286 287 288 289 290 291 292
     * 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.
     */
293
    qint64 _getCurrentDataRate(int index, const qint64 dataWriteTimes[], const quint64 dataWriteAmounts[]) const
294 295 296 297
    {
        const qint64 now = QDateTime::currentMSecsSinceEpoch();

        // Limit the time we calculate to the recent past
298
        const qint64 cutoff = now - _dataRateCurrentTimespan;
299 300

        // Grab the mutex for working with the stats variables
301
        QMutexLocker dataRateLocker(&_dataRateMutex);
302 303 304 305 306 307

        // 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;
308
        int size = _dataRateBufferSize;
309 310 311 312 313 314 315 316 317 318 319 320 321 322
        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.
323
            if (++index == _dataRateBufferSize)
324 325 326 327 328 329 330 331 332 333 334 335
            {
                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;
    }

336 337 338 339 340 341
    /**
     * @brief Connect this interface logically
     *
     * @return True if connection could be established, false otherwise
     **/
    virtual bool _connect(void) = 0;
342

Don Gagne's avatar
Don Gagne committed
343
    virtual void _disconnect(void) = 0;
344 345 346 347 348 349
    
    /// Sets the mavlink channel to use for this link
    void _setMavlinkChannel(uint8_t channel) { Q_ASSERT(!_mavlinkChannelSet); _mavlinkChannelSet = true; _mavlinkChannel = channel; }
    
    bool _mavlinkChannelSet;    ///< true: _mavlinkChannel has been set
    uint8_t _mavlinkChannel;    ///< mavlink channel to use for this link, as used by mavlink_parse_char
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
    
    static const int _dataRateBufferSize = 20; ///< Specify how many data points to capture for data rate calculations.
    
    static const qint64 _dataRateCurrentTimespan = 500; ///< Set the maximum age of samples to use for data calculations (ms).
    
    // Implement a simple circular buffer for storing when and how much data was received.
    // Used for calculating the incoming data rate. Use with *StatsBuffer() functions.
    int     _inDataIndex;
    quint64 _inDataWriteAmounts[_dataRateBufferSize]; // In bytes
    qint64  _inDataWriteTimes[_dataRateBufferSize]; // in ms
    
    // Implement a simple circular buffer for storing when and how much data was transmit.
    // Used for calculating the outgoing data rate. Use with *StatsBuffer() functions.
    int     _outDataIndex;
    quint64 _outDataWriteAmounts[_dataRateBufferSize]; // In bytes
    qint64  _outDataWriteTimes[_dataRateBufferSize]; // in ms
    
    mutable QMutex _dataRateMutex; // Mutex for accessing the data rate member variables
Don Gagne's avatar
Don Gagne committed
368 369

    bool _active;       ///< true: link is actively receiving mavlink messages
dogmaphobic's avatar
dogmaphobic committed
370
    bool _enableRateCollection;
pixhawk's avatar
pixhawk committed
371 372
};

373 374
typedef QSharedPointer<LinkInterface> SharedLinkInterface;

pixhawk's avatar
pixhawk committed
375
#endif // _LINKINTERFACE_H_