// 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 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 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() ); } }
// 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*>(¤tPacket), _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, ¤tPacket.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; }