Example #1
0
// Called whenever there is data available on the device.
void SignalChunker::next(QIODevice* device)
{
    // Get a pointer to the UDP socket.
    QUdpSocket* udpSocket = static_cast<QUdpSocket*>(device);
    _bytesRead = 0;

    // Get writable buffer space for the chunk.
    WritableData writableData = getDataStorage(_chunkSize);
    if (writableData.isValid()) {
        // Get pointer to start of writable memory.
        char* ptr = (char*) (writableData.ptr());

        // Read datagrams for chunk from the UDP socket.
        while (isActive() && _bytesRead < _chunkSize) {
            // Read the datagram, but avoid using pendingDatagramSize().
            if (!udpSocket->hasPendingDatagrams()) {
                // MUST WAIT for the next datagram.
                udpSocket->waitForReadyRead(100);
                continue;
            }
            qint64 maxlen = _chunkSize - _bytesRead;
            qint64 length = udpSocket->readDatagram(ptr + _bytesRead, maxlen);
            if (length > 0)
                _bytesRead += length;
        }
    }

    // Must discard the datagram if there is no available space.
    else {
        udpSocket->readDatagram(0, 0);
    }
}
void EmbraceChunker::next(QIODevice* device)
{
    QAbstractSocket* socket = static_cast<QAbstractSocket*>(device);
    WritableData writableData = getDataStorage(_sizeOfFrame);
    int readTotal = 0;
    if (writableData.isValid() ) {
        do {
            int read = socket->read((char*)writableData.ptr() + readTotal*sizeof(char) , _sizeOfFrame);
            if( read == -1 ) { 
                std::cerr << "read error";  
                // TODO - clean up and annul writableData
            }
            readTotal += read;
        } while ( readTotal < _sizeOfFrame );
    }
    else {
        // TODO dump the data here
        std::cerr << "EmbraceChunker: "
                "WritableData is not valid!" << std::endl;
    }
}
void ServiceDataBufferTest::test_getWritable()
{
    {
        // Use case:
        // Call getWritable twice without releasing the new object.
        // expect second call to return an invalid object.
        ServiceDataBuffer buffer("test");
        WritableData data1 = buffer.getWritable(1);
        WritableData data2 = buffer.getWritable(1);
        CPPUNIT_ASSERT(data1.isValid());
        CPPUNIT_ASSERT(!data2.isValid());
    }
    
    {
        // Use case:
        // On deleting the WritableData expect it to become current.
        // This should also release the lock allowing a second call
        // to getWritable() to return a valid object
        ServiceDataBuffer buffer("test");
        {
            LockableServiceData* data = 0;
            data = static_cast<LockableServiceData*>(buffer.getWritable(1).data());
            CPPUNIT_ASSERT( data != 0 );
            CPPUNIT_ASSERT( data->isValid() );
        }

        _app->processEvents(); // needed to connect signals and slots

        LockedData currentData("test");
        buffer.getCurrent(currentData);
        CPPUNIT_ASSERT(currentData.isValid());

        // Check that the lock is now released allowing a second getWritable()
        // to return valid data
        WritableData wd = buffer.getWritable(1);
        CPPUNIT_ASSERT( wd.isValid() );
    }
}
void StreamDataBufferTest::test_getWritable()
{
    QByteArray data1("data1");
    {
        // Use case:
        // getWritable() called with no service data
        // Expect a valid object with no associate data.
        StreamDataBuffer buffer("test");
        buffer.setDataManager(_dataManager);
        WritableData data = buffer.getWritable(data1.size());
        CPPUNIT_ASSERT( data.isValid() );
        data.write(data1.data(),data1.size());
        CPPUNIT_ASSERT( data.isValid() );
        CPPUNIT_ASSERT_EQUAL(0, static_cast<LockableStreamData*>(data.data())->associateData().size() );
    }

    // Setup data manager with a service data buffer for remaining test cases
    ServiceDataBuffer* serveBuffer = new ServiceDataBuffer("test");
    QString service1("Service1");
    _dataManager->setServiceDataBuffer(service1,serveBuffer);
    {
        // Use case:
        // getWritable() called with service data supported, but no data
        // expect valid object with no associate data
        StreamDataBuffer b("test");
        b.setDataManager(_dataManager);
        WritableData data = b.getWritable(data1.size());
        CPPUNIT_ASSERT( data.isValid() );
        data.write(data1.data(),data1.size());
        CPPUNIT_ASSERT( data.isValid() );
        CPPUNIT_ASSERT_EQUAL(0, static_cast<LockableStreamData*>(data.data())->associateData().size() );
    }
    // inject some data into the service buffer for remaining tests
    serveBuffer->getWritable(1);
    _app->processEvents();
    {
        // Use case:
        // getWritable() called with service data supported, with data
        // expect valid object with associate data
        StreamDataBuffer b("test");
        b.setDataManager(_dataManager);
        WritableData data = b.getWritable(data1.size());
        CPPUNIT_ASSERT( data.isValid() );
        data.write(data1.data(),data1.size());
        CPPUNIT_ASSERT( data.isValid() );
        CPPUNIT_ASSERT_EQUAL(1, static_cast<LockableStreamData*>(data.data())->associateData().size() );
    }
}
void StreamDataBufferTest::test_getWritableStreams()
{
    std::cout << "#############################################" << std::endl;
    // Use case:
    // Multiple calls to getWritable() simulating filling of a stream buffer.
    // Expect: unique data pointers to be returned.
    {
        size_t dataSize = 8;
        StreamDataBuffer buffer("test");
        buffer.setDataManager(_dataManager);
        {
            WritableData dataChunk = buffer.getWritable(dataSize);
            void* dataPtr =  dataChunk.data()->data()->ptr();
            double value = 1;
            dataChunk.write(&value, 8, 0);
            std::cout << "[1] dataPtr = " << dataPtr << std::endl;
        }
        CPPUNIT_ASSERT_EQUAL(1, buffer._serveQueue.size());
        {
            WritableData dataChunk = buffer.getWritable(dataSize);
            void* dataPtr =  dataChunk.data()->data()->ptr();
            double value = 2;
            dataChunk.write(&value, 8, 0);
            std::cout << "[2] dataPtr = " << dataPtr << std::endl;
        }
        CPPUNIT_ASSERT_EQUAL(2, buffer._serveQueue.size());
        {
            WritableData dataChunk = buffer.getWritable(dataSize);
            void* dataPtr =  dataChunk.data()->data()->ptr();
            double value = 3;
            dataChunk.write(&value, 8, 0);
            std::cout << "[3] dataPtr = " << dataPtr << std::endl;
        }

        CPPUNIT_ASSERT_EQUAL(3, buffer._serveQueue.size());
        CPPUNIT_ASSERT_EQUAL(0, buffer._emptyQueue.size());
        {
            LockedData data("test");
            buffer.getNext(data);
            static_cast<LockableStreamData*>(data.object())->served() = true;
        }
        CPPUNIT_ASSERT_EQUAL(2, buffer._serveQueue.size());
        CPPUNIT_ASSERT_EQUAL(1, buffer._emptyQueue.size());
        {
            LockedData data("test");
            buffer.getNext(data);
            static_cast<LockableStreamData*>(data.object())->served() = true;
        }
        CPPUNIT_ASSERT_EQUAL(1, buffer._serveQueue.size());
        CPPUNIT_ASSERT_EQUAL(2, buffer._emptyQueue.size());

        {
            LockedData data("test");
            buffer.getNext(data);
            static_cast<LockableStreamData*>(data.object())->served() = true;
        }
        CPPUNIT_ASSERT_EQUAL(0, buffer._serveQueue.size());
        CPPUNIT_ASSERT_EQUAL(3, buffer._emptyQueue.size());
        {
            WritableData dataChunk = buffer.getWritable(dataSize);
            void* dataPtr =  dataChunk.data()->data()->ptr();
            double value = 4;
            dataChunk.write(&value, 8, 0);
            std::cout << "[4] dataPtr = " << dataPtr << std::endl;
        }
        CPPUNIT_ASSERT_EQUAL(1, buffer._serveQueue.size());
        CPPUNIT_ASSERT_EQUAL(2, buffer._emptyQueue.size());

    }
    std::cout << "#############################################" << std::endl;
}
Example #6
0
// Gets the next chunk of data from the UDP socket (if it exists).
void K7Chunker::next(QIODevice* device)
{
    unsigned int i;
    unsigned int lostPackets, difference;
    unsigned int offsetStream;
    unsigned int lostPacketCounter;
    unsigned long int previousTimestamp;
    unsigned int previousAccumulation;
    unsigned long int timestamp;
    unsigned int accumulation;
    unsigned int rate;
    QUdpSocket* udpSocket = static_cast<QUdpSocket*>(device);
    K7Packet currentPacket;
    K7Packet outputPacket;
    K7Packet _emptyPacket;

    difference = 0;
    offsetStream = 0;
    timestamp = 0;
    accumulation = 0;
    rate = 0;
    previousTimestamp = _startTimestamp;
    previousAccumulation = _startAccumulation;

    // Get writable buffer space for the chunk.
    WritableData writableData;
    if ( ! _writable.isValid() )
    {
        writableData = getDataStorage(_nPackets * _packetSizeStream, chunkTypes().at(0));
    }
    else
    {
        writableData = _writable;
    }

    if (writableData.isValid())
    {
        // Loop over the number of UDP packets to put in a chunk.
        for (i = 0; i < _nPackets; ++i)
        {
            // Chunker sanity check.
            if (!isActive())
                return;

            // Wait for datagram to be available.
            while ( !udpSocket->hasPendingDatagrams() )
            {
                _writable = writableData;
                continue;
            }

            // Read the current UDP packet from the socket.
            if (udpSocket->readDatagram(reinterpret_cast<char*>(&currentPacket), _packetSize) <= 0)
            {
                std::cerr << "K7Chunker::next(): Error while receiving UDP Packet!" << std::endl;
                i--;
                continue;
            }

            // Get the UDP packet header.
            timestamp    = currentPacket.header.UTCtimestamp;
            accumulation = currentPacket.header.accumulationNumber;
            rate         = currentPacket.header.accumulationRate;
            //std::cout << "K7Chunker::next(): timestamp " << timestamp << " accumulation " << accumulation << " rate " << rate << std::endl;
            // First time next() has been run, initialise _startTimestamp and _startAccumulation.
            if (i == 0 && _startTimestamp == 0)
            {
                previousTimestamp = _startTimestamp = _startTimestamp == 0 ? timestamp : _startTimestamp;
                previousAccumulation = _startAccumulation = _startAccumulation == 0 ? accumulation : _startAccumulation;
                //std::cout << "K7Chunker::next(): timestamp " << timestamp << " accumulation " << accumulation << " rate " << rate << std::endl;
            }

            // Sanity check in UTCtimestamp. If the seconds counter is 0xFFFFFFFFFFFFFFFF, the data cannot be trusted (ignore).
            if (timestamp == ~0UL || previousTimestamp + 10 < timestamp)
            {
                std::cerr << "K7Chunker::next(): Data cannot be trusted! Timestamp is " << timestamp << " or previousTimestamp is " << previousTimestamp << std::endl;
                exit(-1);
            }

            // Check that the packets are contiguous. accumulationNumber increments by
            // accumulationRate which is defined in the header of UDP packet.
            // accumulationNumber is reset every interval (although it might not start from 0
            // as the previous frame might contain data from this one).
            lostPackets = 0;
            difference = (accumulation >= previousAccumulation) ? (accumulation - previousAccumulation) : (accumulation + _packetsPerSecond - previousAccumulation);
#if 0
            // Duplicated packets. Need to address this. ICBF does not duplicate packets though.
            if (difference < rate)
            {
                std::cout << "Duplicated packets, difference " << difference << std::endl;
                ++_packetsRejected;
                i -= 1;
                continue;
            }
            else
#endif
            // Missing packets.
            if (difference > rate)
            {
                // -1 since it includes the received packet as well.
                lostPackets = (difference / rate) - 1;
            }

            if (lostPackets > 0)
            {
                printf("K7Chunker::next(): generate %u empty packets, previousTimestamp: %lu, new timestamp: %lu, prevAccumulation: %u, newAccumulation: %u\n",
                        lostPackets, previousTimestamp, timestamp, previousAccumulation, accumulation);
            }

            // Generate lostPackets (empty packets) if needed.
            lostPacketCounter = 0;
            for (lostPacketCounter = 0; lostPacketCounter < lostPackets && i + lostPacketCounter < _nPackets; ++lostPacketCounter)
            {
                // Generate empty packet with correct timestamp and accumulation number.
                previousTimestamp = (previousAccumulation < _packetsPerSecond) ? previousTimestamp : previousTimestamp + 1;
                previousAccumulation = previousAccumulation % _packetsPerSecond;
                updateEmptyPacket(_emptyPacket, previousTimestamp, previousAccumulation, rate);
                offsetStream = writePacket(&writableData, _emptyPacket, _packetSizeStream, offsetStream);
            }
            i += lostPacketCounter;

            // Write received packet to stream after updating header and data.
            if (i != _nPackets)
            {
                ++_packetsAccepted;
                // Generate stream packet.
                outputPacket.header = currentPacket.header;
                memcpy((void*)outputPacket.data, &currentPacket.data[_byte1OfStream], _bytesStream);
                offsetStream = writePacket(&writableData, outputPacket, _packetSizeStream, offsetStream);
                previousTimestamp = timestamp;
                previousAccumulation = accumulation;
            }
        }
        _chunksProcessed++;
        _chunkerCounter++;
        // Clear any data locks.
        _writable = WritableData();
        if (_chunkerCounter % 100 == 0)
        {
            std::cout << "K7Chunker::next(): " << _chunksProcessed << " chunks processed. " << "UTC timestamp " << timestamp << " accumulationNumber " << accumulation << " accumulationRate " << rate << std::endl;
        }
    }
    else
    {
        // Must discard the datagram if there is no available space.
        if (!isActive()) return;
        udpSocket->readDatagram(0, 0);
        std::cout << "K7Chunker::next(): Writable data not valid, discarding packets." << std::endl;
    }

    // Update _startTime.
    _startTimestamp = previousTimestamp;
    _startAccumulation = previousAccumulation;
}