demo version
This commit is contained in:
parent
fbb282a801
commit
672d6daa8e
125 changed files with 17918 additions and 1481 deletions
|
@ -0,0 +1,445 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Nerian Vision GmbH
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*******************************************************************************/
|
||||
|
||||
#if __GNUC__ == 4 && __GNUC_MINOR__ < 9
|
||||
// This is a very ugly workaround for GCC bug 54562. If omitted,
|
||||
// passing timeouts to collectReceivedImage() is broken.
|
||||
#include <bits/c++config.h>
|
||||
#undef _GLIBCXX_USE_CLOCK_MONOTONIC
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include "visiontransfer/asynctransfer.h"
|
||||
#include "visiontransfer/alignedallocator.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace visiontransfer;
|
||||
using namespace visiontransfer::internal;
|
||||
|
||||
namespace visiontransfer {
|
||||
|
||||
/*************** Pimpl class containing all private members ***********/
|
||||
|
||||
class AsyncTransfer::Pimpl {
|
||||
public:
|
||||
Pimpl(const char* address, const char* service,
|
||||
ImageProtocol::ProtocolType protType, bool server,
|
||||
int bufferSize, int maxUdpPacketSize);
|
||||
~Pimpl();
|
||||
|
||||
// Redeclaration of public members
|
||||
void sendImageSetAsync(const ImageSet& imageSet, bool deleteData);
|
||||
bool collectReceivedImageSet(ImageSet& imageSet, double timeout);
|
||||
int getNumDroppedFrames() const;
|
||||
bool isConnected() const;
|
||||
void disconnect();
|
||||
std::string getRemoteAddress() const;
|
||||
bool tryAccept();
|
||||
|
||||
private:
|
||||
static constexpr int NUM_BUFFERS = 6;
|
||||
static constexpr int SEND_THREAD_SHORT_WAIT_MS = 1;
|
||||
static constexpr int SEND_THREAD_LONG_WAIT_MS = 10;
|
||||
|
||||
// The encapsulated image transfer object
|
||||
ImageTransfer imgTrans;
|
||||
|
||||
// Variable for controlling thread termination
|
||||
volatile bool terminate;
|
||||
|
||||
// There are two threads, one for sending and one for receiving.
|
||||
// Each has a mutex and condition variable for synchronization.
|
||||
std::thread sendThread;
|
||||
std::mutex sendMutex;
|
||||
std::condition_variable sendCond;
|
||||
std::condition_variable sendWaitCond;
|
||||
|
||||
std::thread receiveThread;
|
||||
std::timed_mutex receiveMutex;
|
||||
std::condition_variable_any receiveCond;
|
||||
std::condition_variable_any receiveWaitCond;
|
||||
|
||||
// Objects for exchanging images with the send and receive threads
|
||||
ImageSet receivedSet;
|
||||
std::vector<unsigned char, AlignedAllocator<unsigned char> > receivedData[NUM_BUFFERS];
|
||||
bool newDataReceived;
|
||||
|
||||
ImageSet sendImageSet;
|
||||
bool sendSetValid;
|
||||
bool deleteSendData;
|
||||
|
||||
// Exception occurred in one of the threads
|
||||
std::exception_ptr receiveException;
|
||||
std::exception_ptr sendException;
|
||||
|
||||
bool sendThreadCreated;
|
||||
bool receiveThreadCreated;
|
||||
|
||||
// Main loop for sending thread
|
||||
void sendLoop();
|
||||
|
||||
// Main loop for receiving;
|
||||
void receiveLoop();
|
||||
|
||||
void createSendThread();
|
||||
};
|
||||
|
||||
/******************** Stubs for all public members ********************/
|
||||
|
||||
AsyncTransfer::AsyncTransfer(const char* address, const char* service,
|
||||
ImageProtocol::ProtocolType protType, bool server,
|
||||
int bufferSize, int maxUdpPacketSize)
|
||||
: pimpl(new Pimpl(address, service, protType, server, bufferSize, maxUdpPacketSize)) {
|
||||
}
|
||||
|
||||
AsyncTransfer::AsyncTransfer(const DeviceInfo& device, int bufferSize, int maxUdpPacketSize)
|
||||
: pimpl(new Pimpl(device.getIpAddress().c_str(), "7681", static_cast<ImageProtocol::ProtocolType>(device.getNetworkProtocol()),
|
||||
false, bufferSize, maxUdpPacketSize)) {
|
||||
}
|
||||
|
||||
AsyncTransfer::~AsyncTransfer() {
|
||||
delete pimpl;
|
||||
}
|
||||
|
||||
void AsyncTransfer::sendImageSetAsync(const ImageSet& imageSet, bool deleteData) {
|
||||
pimpl->sendImageSetAsync(imageSet, deleteData);
|
||||
}
|
||||
|
||||
bool AsyncTransfer::collectReceivedImageSet(ImageSet& imageSet, double timeout) {
|
||||
return pimpl->collectReceivedImageSet(imageSet, timeout);
|
||||
}
|
||||
|
||||
int AsyncTransfer::getNumDroppedFrames() const {
|
||||
return pimpl->getNumDroppedFrames();
|
||||
}
|
||||
|
||||
bool AsyncTransfer::isConnected() const {
|
||||
return pimpl->isConnected();
|
||||
}
|
||||
|
||||
void AsyncTransfer::disconnect() {
|
||||
return pimpl->disconnect();
|
||||
}
|
||||
|
||||
std::string AsyncTransfer::getRemoteAddress() const {
|
||||
return pimpl->getRemoteAddress();
|
||||
}
|
||||
|
||||
bool AsyncTransfer::tryAccept() {
|
||||
return pimpl->tryAccept();
|
||||
}
|
||||
|
||||
/******************** Implementation in pimpl class *******************/
|
||||
|
||||
AsyncTransfer::Pimpl::Pimpl(const char* address, const char* service,
|
||||
ImageProtocol::ProtocolType protType, bool server,
|
||||
int bufferSize, int maxUdpPacketSize)
|
||||
: imgTrans(address, service, protType, server, bufferSize, maxUdpPacketSize),
|
||||
terminate(false), newDataReceived(false), sendSetValid(false),
|
||||
deleteSendData(false), sendThreadCreated(false),
|
||||
receiveThreadCreated(false) {
|
||||
|
||||
if(server) {
|
||||
createSendThread();
|
||||
}
|
||||
}
|
||||
|
||||
AsyncTransfer::Pimpl::~Pimpl() {
|
||||
terminate = true;
|
||||
|
||||
sendCond.notify_all();
|
||||
receiveCond.notify_all();
|
||||
sendWaitCond.notify_all();
|
||||
receiveWaitCond.notify_all();
|
||||
|
||||
if(sendThreadCreated && sendThread.joinable()) {
|
||||
sendThread.join();
|
||||
}
|
||||
|
||||
if(receiveThreadCreated && receiveThread.joinable()) {
|
||||
receiveThread.join();
|
||||
}
|
||||
|
||||
if(sendSetValid && deleteSendData) {
|
||||
delete[] sendImageSet.getPixelData(0);
|
||||
delete[] sendImageSet.getPixelData(1);
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncTransfer::Pimpl::createSendThread() {
|
||||
if(!sendThreadCreated) {
|
||||
// Lazy initialization of the send thread as it is not always needed
|
||||
unique_lock<mutex> lock(sendMutex);
|
||||
sendThread = thread(bind(&AsyncTransfer::Pimpl::sendLoop, this));
|
||||
sendThreadCreated = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncTransfer::Pimpl::sendImageSetAsync(const ImageSet& imageSet, bool deleteData) {
|
||||
createSendThread();
|
||||
|
||||
while(true) {
|
||||
unique_lock<mutex> lock(sendMutex);
|
||||
|
||||
// Test for errors
|
||||
if(sendException) {
|
||||
std::rethrow_exception(sendException);
|
||||
}
|
||||
|
||||
if(!sendSetValid) {
|
||||
sendImageSet = imageSet;
|
||||
sendSetValid = true;
|
||||
deleteSendData = deleteData;
|
||||
|
||||
// Wake up the sender thread
|
||||
sendCond.notify_one();
|
||||
|
||||
return;
|
||||
} else {
|
||||
// Wait for old data to be processed first
|
||||
sendWaitCond.wait(lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncTransfer::Pimpl::collectReceivedImageSet(ImageSet& imageSet, double timeout) {
|
||||
if(!receiveThreadCreated) {
|
||||
// Lazy initialization of receive thread
|
||||
unique_lock<timed_mutex> lock(receiveMutex);
|
||||
receiveThreadCreated = true;
|
||||
receiveThread = thread(bind(&AsyncTransfer::Pimpl::receiveLoop, this));
|
||||
}
|
||||
|
||||
// Acquire mutex
|
||||
unique_lock<timed_mutex> lock(receiveMutex, std::defer_lock);
|
||||
if(timeout < 0) {
|
||||
lock.lock();
|
||||
} else {
|
||||
std::chrono::steady_clock::time_point lockStart =
|
||||
std::chrono::steady_clock::now();
|
||||
if(!lock.try_lock_for(std::chrono::microseconds(static_cast<unsigned int>(timeout*1e6)))) {
|
||||
// Timed out
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update timeout
|
||||
unsigned int lockDuration = static_cast<unsigned int>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now() - lockStart).count());
|
||||
timeout = std::max(0.0, timeout - lockDuration*1e-6);
|
||||
}
|
||||
|
||||
// Test for errors
|
||||
if(receiveException) {
|
||||
std::rethrow_exception(receiveException);
|
||||
}
|
||||
|
||||
if(timeout == 0 && !newDataReceived) {
|
||||
// No image has been received and we are not blocking
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there is no data yet then keep on waiting
|
||||
if(!newDataReceived) {
|
||||
if(timeout < 0) {
|
||||
while(!terminate && !receiveException && !newDataReceived) {
|
||||
receiveCond.wait(lock);
|
||||
}
|
||||
} else {
|
||||
receiveCond.wait_for(lock, std::chrono::microseconds(static_cast<unsigned int>(timeout*1e6)));
|
||||
}
|
||||
}
|
||||
|
||||
// Test for errors again
|
||||
if(receiveException) {
|
||||
std::rethrow_exception(receiveException);
|
||||
}
|
||||
|
||||
if(newDataReceived) {
|
||||
// Get the received image
|
||||
imageSet = receivedSet;
|
||||
|
||||
newDataReceived = false;
|
||||
receiveWaitCond.notify_one();
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncTransfer::Pimpl::sendLoop() {
|
||||
{
|
||||
// Delay the thread start
|
||||
unique_lock<mutex> lock(sendMutex);
|
||||
}
|
||||
|
||||
ImageSet imgSet;
|
||||
bool deleteSet = false;
|
||||
|
||||
try {
|
||||
while(!terminate) {
|
||||
// Wait for next image
|
||||
{
|
||||
unique_lock<mutex> lock(sendMutex);
|
||||
// Wait for next frame to be queued
|
||||
bool firstWait = true;
|
||||
while(!terminate && !sendSetValid) {
|
||||
imgTrans.transferData();
|
||||
sendCond.wait_for(lock, std::chrono::milliseconds(
|
||||
firstWait ? SEND_THREAD_SHORT_WAIT_MS : SEND_THREAD_LONG_WAIT_MS));
|
||||
firstWait = false;
|
||||
}
|
||||
if(!sendSetValid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
imgSet = sendImageSet;
|
||||
deleteSet = deleteSendData;
|
||||
sendSetValid = false;
|
||||
|
||||
sendWaitCond.notify_one();
|
||||
}
|
||||
|
||||
if(!terminate) {
|
||||
imgTrans.setTransferImageSet(imgSet);
|
||||
imgTrans.transferData();
|
||||
}
|
||||
|
||||
if(deleteSet) {
|
||||
for (int i=0; i<imgSet.getNumberOfImages(); ++i) {
|
||||
delete[] imgSet.getPixelData(i);
|
||||
}
|
||||
deleteSet = false;
|
||||
}
|
||||
}
|
||||
} catch(...) {
|
||||
// Store the exception for later
|
||||
if(!sendException) {
|
||||
sendException = std::current_exception();
|
||||
}
|
||||
sendWaitCond.notify_all();
|
||||
|
||||
// Don't forget to free the memory
|
||||
if(deleteSet) {
|
||||
for (int i=0; i<imgSet.getNumberOfImages(); ++i) {
|
||||
delete[] imgSet.getPixelData(i);
|
||||
}
|
||||
deleteSet = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncTransfer::Pimpl::receiveLoop() {
|
||||
{
|
||||
// Delay the thread start
|
||||
unique_lock<timed_mutex> lock(receiveMutex);
|
||||
}
|
||||
|
||||
try {
|
||||
ImageSet currentSet;
|
||||
int bufferIndex = 0;
|
||||
|
||||
while(!terminate) {
|
||||
// Receive new image
|
||||
if(!imgTrans.receiveImageSet(currentSet)) {
|
||||
// No image available
|
||||
continue;
|
||||
}
|
||||
|
||||
// Copy the pixel data
|
||||
for(int i=0;i<currentSet.getNumberOfImages();i++) {
|
||||
int bytesPerPixel = currentSet.getBytesPerPixel(i);
|
||||
int newStride = currentSet.getWidth() * bytesPerPixel;
|
||||
int totalSize = currentSet.getHeight() * newStride;
|
||||
if(static_cast<int>(receivedData[i + bufferIndex].size()) < totalSize) {
|
||||
receivedData[i + bufferIndex].resize(totalSize);
|
||||
}
|
||||
if(newStride == currentSet.getRowStride(i)) {
|
||||
memcpy(&receivedData[i + bufferIndex][0], currentSet.getPixelData(i),
|
||||
newStride*currentSet.getHeight());
|
||||
} else {
|
||||
for(int y = 0; y<currentSet.getHeight(); y++) {
|
||||
memcpy(&receivedData[i + bufferIndex][y*newStride],
|
||||
¤tSet.getPixelData(i)[y*currentSet.getRowStride(i)],
|
||||
newStride);
|
||||
}
|
||||
currentSet.setRowStride(i, newStride);
|
||||
}
|
||||
currentSet.setPixelData(i, &receivedData[i + bufferIndex][0]);
|
||||
}
|
||||
|
||||
{
|
||||
unique_lock<timed_mutex> lock(receiveMutex);
|
||||
|
||||
// Wait for previously received data to be processed
|
||||
while(newDataReceived) {
|
||||
receiveWaitCond.wait_for(lock, std::chrono::milliseconds(100));
|
||||
if(terminate) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Notify that a new image set has been received
|
||||
newDataReceived = true;
|
||||
receivedSet = currentSet;
|
||||
receiveCond.notify_one();
|
||||
}
|
||||
|
||||
// Increment index for data buffers
|
||||
bufferIndex = (bufferIndex + currentSet.getNumberOfImages()) % NUM_BUFFERS;
|
||||
}
|
||||
} catch(...) {
|
||||
// Store the exception for later
|
||||
if(!receiveException) {
|
||||
receiveException = std::current_exception();
|
||||
}
|
||||
receiveCond.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncTransfer::Pimpl::isConnected() const {
|
||||
return imgTrans.isConnected();
|
||||
}
|
||||
|
||||
void AsyncTransfer::Pimpl::disconnect() {
|
||||
imgTrans.disconnect();
|
||||
}
|
||||
|
||||
std::string AsyncTransfer::Pimpl::getRemoteAddress() const {
|
||||
return imgTrans.getRemoteAddress();
|
||||
}
|
||||
|
||||
int AsyncTransfer::Pimpl::getNumDroppedFrames() const {
|
||||
return imgTrans.getNumDroppedFrames();
|
||||
}
|
||||
|
||||
bool AsyncTransfer::Pimpl::tryAccept() {
|
||||
return imgTrans.tryAccept();
|
||||
}
|
||||
|
||||
constexpr int AsyncTransfer::Pimpl::NUM_BUFFERS;
|
||||
constexpr int AsyncTransfer::Pimpl::SEND_THREAD_SHORT_WAIT_MS;
|
||||
constexpr int AsyncTransfer::Pimpl::SEND_THREAD_LONG_WAIT_MS;
|
||||
|
||||
} // namespace
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue