Merge remote-tracking branch 'remotes/origin/development'

master v2.0
michal.szwaj 2018-04-01 17:38:34 +02:00
commit b2af4614e3
38 changed files with 1851 additions and 241 deletions

View File

@ -12,6 +12,7 @@ C++ object-oriented library containing implementation of core AndroidAuto(tm) fu
### Supported functionalities
- AOAP (Android Open Accessory Protocol)
- USB transport
- TCP transport
- USB hotplug
- AndroidAuto(tm) protocol
- SSL encryption

View File

@ -41,6 +41,7 @@ struct DataBuffer
DataBuffer(void* _data, Data::size_type _size, Data::size_type offset = 0);
explicit DataBuffer(Data& _data, Data::size_type offset = 0);
bool operator==(const std::nullptr_t&) const;
bool operator==(const DataBuffer& buffer) const;
Data::value_type* data;
Data::size_type size;
@ -54,6 +55,7 @@ struct DataConstBuffer
DataConstBuffer(const void* _data, Data::size_type _size, Data::size_type offset = 0);
explicit DataConstBuffer(const Data& _data, Data::size_type offset = 0);
bool operator==(const std::nullptr_t&) const;
bool operator==(const DataConstBuffer& buffer) const;
const Data::value_type* cdata;
Data::size_type size;

View File

@ -41,6 +41,8 @@ public:
bool operator!() const;
bool operator==(const Error& other) const;
bool operator==(const ErrorCode& code) const;
bool operator!=(const ErrorCode& code) const;
private:
ErrorCode code_;

View File

@ -29,40 +29,40 @@ namespace error
enum class ErrorCode
{
NONE,
USB_CLAIM_INTERFACE,
USB_INVALID_CONFIG_DESCRIPTOR,
USB_OBTAIN_INTERFACE_DESCRIPTOR,
USB_EMPTY_INTERFACES,
USB_INVALID_DEVICE_ENDPOINTS,
USB_INVALID_TRANSFER_METHOD,
USB_TRANSFER_ALLOCATION,
USB_LIST_DEVICES,
USB_OBTAIN_CONFIG_DESCRIPTOR,
USB_TRANSFER,
USB_SINK_COMMIT_OVERFLOW,
USB_SINK_CONSUME_UNDERFLOW,
USB_AOAP_PROTOCOL_VERSION,
USB_EMPTY_DEVICE_LIST,
USB_AOAP_DEVICE_NOT_FOUND,
SSL_READ_CERTIFICATE,
SSL_READ_PRIVATE_KEY,
SSL_METHOD,
SSL_CONTEXT_CREATION,
SSL_USE_CERTIFICATE,
SSL_USE_PRIVATE_KEY,
SSL_HANDLER_CREATION,
SSL_READ_BIO_CREATION,
SSL_WRITE_BIO_CREATION,
SSL_HANDSHAKE,
SSL_WRITE,
SSL_READ,
SSL_BIO_READ,
SSL_BIO_WRITE,
MESSENGER_INTERTWINED_CHANNELS,
OPERATION_ABORTED,
OPERATION_IN_PROGRESS,
PARSE_PAYLOAD
NONE = 0,
USB_CLAIM_INTERFACE = 1,
USB_INVALID_CONFIG_DESCRIPTOR = 2,
USB_OBTAIN_INTERFACE_DESCRIPTOR = 3,
USB_EMPTY_INTERFACES = 4,
USB_INVALID_DEVICE_ENDPOINTS = 5,
USB_INVALID_TRANSFER_METHOD = 6,
USB_TRANSFER_ALLOCATION = 7,
USB_LIST_DEVICES = 8,
USB_OBTAIN_CONFIG_DESCRIPTOR = 9,
USB_TRANSFER = 10,
DATA_SINK_COMMIT_OVERFLOW = 11,
DATA_SINK_CONSUME_UNDERFLOW = 12,
USB_AOAP_PROTOCOL_VERSION = 13,
USB_AOAP_DEVICE_NOT_FOUND = 14,
SSL_READ_CERTIFICATE = 15,
SSL_READ_PRIVATE_KEY = 16,
SSL_METHOD = 17,
SSL_CONTEXT_CREATION = 18,
SSL_USE_CERTIFICATE = 19,
SSL_USE_PRIVATE_KEY = 20,
SSL_HANDLER_CREATION = 21,
SSL_READ_BIO_CREATION = 22,
SSL_WRITE_BIO_CREATION = 23,
SSL_HANDSHAKE = 24,
SSL_WRITE = 25,
SSL_READ = 26,
SSL_BIO_READ = 27,
SSL_BIO_WRITE = 28,
MESSENGER_INTERTWINED_CHANNELS = 29,
OPERATION_ABORTED = 30,
OPERATION_IN_PROGRESS = 31,
PARSE_PAYLOAD = 32,
TCP_TRANSFER = 33
};
}

View File

@ -28,11 +28,9 @@ namespace aasdk
namespace io
{
class IOContextWrapper: boost::noncopyable
class IOContextWrapper
{
public:
typedef std::shared_ptr<IOContextWrapper> Pointer;
IOContextWrapper();
explicit IOContextWrapper(boost::asio::io_service& ioService);
explicit IOContextWrapper(boost::asio::io_service::strand& strand);

View File

@ -51,13 +51,13 @@ public:
}
Promise(boost::asio::io_service& ioService)
: ioContextWrapper_(std::make_shared<IOContextWrapper>(ioService))
: ioContextWrapper_(ioService)
{
}
Promise(boost::asio::io_service::strand& strand)
: ioContextWrapper_(std::make_shared<IOContextWrapper>(strand))
: ioContextWrapper_(strand)
{
}
@ -66,8 +66,8 @@ public:
{
std::lock_guard<decltype(mutex_)> lock(mutex_);
resolveHandler_ = std::make_shared<ResolveHandler>(std::move(resolveHandler));
rejectHandler_ = rejectHandler == nullptr ? nullptr : std::make_shared<RejectHandler>(std::move(rejectHandler));
resolveHandler_ = std::move(resolveHandler);
rejectHandler_ = std::move(rejectHandler);
}
void resolve(ResolveArgumentType argument)
@ -76,14 +76,13 @@ public:
if(resolveHandler_ != nullptr && this->isPending())
{
ioContextWrapper_->post([argument = std::move(argument), resolveHandler = std::move(resolveHandler_)]() mutable {
(*resolveHandler)(std::move(argument));
ioContextWrapper_.post([argument = std::move(argument), resolveHandler = std::move(resolveHandler_)]() mutable {
resolveHandler(std::move(argument));
});
}
ioContextWrapper_->reset();
ioContextWrapper_.reset();
rejectHandler_.reset();
rejectHandler_ = RejectHandler();
}
void reject(ErrorArgumentType error)
@ -92,25 +91,24 @@ public:
if(rejectHandler_ != nullptr && this->isPending())
{
ioContextWrapper_->post([error = std::move(error), rejectHandler = std::move(rejectHandler_)]() mutable {
(*rejectHandler)(std::move(error));
ioContextWrapper_.post([error = std::move(error), rejectHandler = std::move(rejectHandler_)]() mutable {
rejectHandler(std::move(error));
});
}
ioContextWrapper_->reset();
ioContextWrapper_.reset();
resolveHandler_.reset();
resolveHandler_ = ResolveHandler();
}
private:
bool isPending() const
{
return ioContextWrapper_ != nullptr && ioContextWrapper_->isActive();
return ioContextWrapper_.isActive();
}
std::shared_ptr<ResolveHandler> resolveHandler_;
std::shared_ptr<RejectHandler> rejectHandler_;
IOContextWrapper::Pointer ioContextWrapper_;
ResolveHandler resolveHandler_;
RejectHandler rejectHandler_;
IOContextWrapper ioContextWrapper_;
std::mutex mutex_;
};
@ -134,13 +132,13 @@ public:
}
Promise(boost::asio::io_service& ioService)
: ioContextWrapper_(std::make_shared<IOContextWrapper>(ioService))
: ioContextWrapper_(ioService)
{
}
Promise(boost::asio::io_service::strand& strand)
: ioContextWrapper_(std::make_shared<IOContextWrapper>(strand))
: ioContextWrapper_(strand)
{
}
@ -149,8 +147,8 @@ public:
{
std::lock_guard<decltype(mutex_)> lock(mutex_);
resolveHandler_ = std::make_shared<ResolveHandler>(std::move(resolveHandler));
rejectHandler_ = rejectHandler == nullptr ? nullptr : std::make_shared<RejectHandler>(std::move(rejectHandler));
resolveHandler_ = std::move(resolveHandler);
rejectHandler_ = std::move(rejectHandler);
}
void resolve()
@ -159,14 +157,13 @@ public:
if(resolveHandler_ != nullptr && this->isPending())
{
ioContextWrapper_->post([resolveHandler = std::move(resolveHandler_)]() mutable {
(*resolveHandler)();
ioContextWrapper_.post([resolveHandler = std::move(resolveHandler_)]() mutable {
resolveHandler();
});
}
ioContextWrapper_->reset();
ioContextWrapper_.reset();
rejectHandler_.reset();
rejectHandler_ = RejectHandler();
}
void reject(ErrorArgumentType error)
@ -175,25 +172,24 @@ public:
if(rejectHandler_ != nullptr && this->isPending())
{
ioContextWrapper_->post([error = std::move(error), rejectHandler = std::move(rejectHandler_)]() mutable {
(*rejectHandler)(std::move(error));
ioContextWrapper_.post([error = std::move(error), rejectHandler = std::move(rejectHandler_)]() mutable {
rejectHandler(std::move(error));
});
}
ioContextWrapper_->reset();
ioContextWrapper_.reset();
resolveHandler_.reset();
resolveHandler_ = ResolveHandler();
}
private:
bool isPending() const
{
return ioContextWrapper_ != nullptr && ioContextWrapper_->isActive();
return ioContextWrapper_.isActive();
}
std::shared_ptr<ResolveHandler> resolveHandler_;
std::shared_ptr<RejectHandler> rejectHandler_;
IOContextWrapper::Pointer ioContextWrapper_;
ResolveHandler resolveHandler_;
RejectHandler rejectHandler_;
IOContextWrapper ioContextWrapper_;
std::mutex mutex_;
};
@ -216,13 +212,13 @@ public:
}
Promise(boost::asio::io_service& ioService)
: ioContextWrapper_(std::make_shared<IOContextWrapper>(ioService))
: ioContextWrapper_(ioService)
{
}
Promise(boost::asio::io_service::strand& strand)
: ioContextWrapper_(std::make_shared<IOContextWrapper>(strand))
: ioContextWrapper_(strand)
{
}
@ -231,8 +227,8 @@ public:
{
std::lock_guard<decltype(mutex_)> lock(mutex_);
resolveHandler_ = std::make_shared<ResolveHandler>(std::move(resolveHandler));
rejectHandler_ = rejectHandler == nullptr ? nullptr : std::make_shared<RejectHandler>(std::move(rejectHandler));
resolveHandler_ = std::move(resolveHandler);
rejectHandler_ = std::move(rejectHandler);
}
void resolve()
@ -241,14 +237,13 @@ public:
if(resolveHandler_ != nullptr && this->isPending())
{
ioContextWrapper_->post([resolveHandler = std::move(resolveHandler_)]() mutable {
(*resolveHandler)();
ioContextWrapper_.post([resolveHandler = std::move(resolveHandler_)]() mutable {
resolveHandler();
});
}
ioContextWrapper_->reset();
ioContextWrapper_.reset();
rejectHandler_.reset();
rejectHandler_ = RejectHandler();
}
void reject()
@ -257,25 +252,24 @@ public:
if(rejectHandler_ != nullptr && this->isPending())
{
ioContextWrapper_->post([rejectHandler = std::move(rejectHandler_)]() mutable {
(*rejectHandler)();
ioContextWrapper_.post([rejectHandler = std::move(rejectHandler_)]() mutable {
rejectHandler();
});
}
ioContextWrapper_->reset();
ioContextWrapper_.reset();
resolveHandler_.reset();
resolveHandler_ = ResolveHandler();
}
private:
bool isPending() const
{
return ioContextWrapper_ != nullptr && ioContextWrapper_->isActive();
return ioContextWrapper_.isActive();
}
std::shared_ptr<ResolveHandler> resolveHandler_;
std::shared_ptr<RejectHandler> rejectHandler_;
IOContextWrapper::Pointer ioContextWrapper_;
ResolveHandler resolveHandler_;
RejectHandler rejectHandler_;
IOContextWrapper ioContextWrapper_;
std::mutex mutex_;
};
@ -299,13 +293,13 @@ public:
}
Promise(boost::asio::io_service& ioService)
: ioContextWrapper_(std::make_shared<IOContextWrapper>(ioService))
: ioContextWrapper_(ioService)
{
}
Promise(boost::asio::io_service::strand& strand)
: ioContextWrapper_(std::make_shared<IOContextWrapper>(strand))
: ioContextWrapper_(strand)
{
}
@ -314,8 +308,8 @@ public:
{
std::lock_guard<decltype(mutex_)> lock(mutex_);
resolveHandler_ = std::make_shared<ResolveHandler>(std::move(resolveHandler));
rejectHandler_ = rejectHandler == nullptr ? nullptr : std::make_shared<RejectHandler>(std::move(rejectHandler));
resolveHandler_ = std::move(resolveHandler);
rejectHandler_ = std::move(rejectHandler);
}
void resolve(ResolveArgumentType argument)
@ -324,14 +318,13 @@ public:
if(resolveHandler_ != nullptr && this->isPending())
{
ioContextWrapper_->post([argument = std::move(argument), resolveHandler = std::move(resolveHandler_)]() mutable {
(*resolveHandler)(std::move(argument));
ioContextWrapper_.post([argument = std::move(argument), resolveHandler = std::move(resolveHandler_)]() mutable {
resolveHandler(std::move(argument));
});
}
ioContextWrapper_->reset();
ioContextWrapper_.reset();
rejectHandler_.reset();
rejectHandler_ = RejectHandler();
}
void reject()
@ -340,25 +333,24 @@ public:
if(rejectHandler_ != nullptr && this->isPending())
{
ioContextWrapper_->post([rejectHandler = std::move(rejectHandler_)]() mutable {
(*rejectHandler)();
ioContextWrapper_.post([rejectHandler = std::move(rejectHandler_)]() mutable {
rejectHandler();
});
}
ioContextWrapper_->reset();
ioContextWrapper_.reset();
resolveHandler_.reset();
resolveHandler_ = ResolveHandler();
}
private:
bool isPending() const
{
return ioContextWrapper_ != nullptr && ioContextWrapper_->isActive();
return ioContextWrapper_.isActive();
}
std::shared_ptr<ResolveHandler> resolveHandler_;
std::shared_ptr<RejectHandler> rejectHandler_;
IOContextWrapper::Pointer ioContextWrapper_;
ResolveHandler resolveHandler_;
RejectHandler rejectHandler_;
IOContextWrapper ioContextWrapper_;
std::mutex mutex_;
};

View File

@ -0,0 +1,48 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <memory>
#include <f1x/aasdk/Common/Data.hpp>
#include <f1x/aasdk/IO/Promise.hpp>
namespace f1x
{
namespace aasdk
{
namespace tcp
{
class ITCPEndpoint
{
public:
typedef std::shared_ptr<ITCPEndpoint> Pointer;
typedef io::Promise<size_t> Promise;
typedef std::shared_ptr<boost::asio::ip::tcp::socket> SocketPointer;
virtual ~ITCPEndpoint() = default;
virtual void send(common::DataConstBuffer buffer, Promise::Pointer promise) = 0;
virtual void receive(common::DataBuffer buffer, Promise::Pointer promise) = 0;
virtual void stop() = 0;
};
}
}
}

View File

@ -0,0 +1,49 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <functional>
#include <boost/asio/ip/tcp.hpp>
#include <f1x/aasdk/Common/Data.hpp>
namespace f1x
{
namespace aasdk
{
namespace tcp
{
class ITCPWrapper
{
public:
typedef std::function<void(const boost::system::error_code&, size_t)> Handler;
typedef std::function<void(const boost::system::error_code&)> ConnectHandler;
virtual ~ITCPWrapper() = default;
virtual void asyncWrite(boost::asio::ip::tcp::socket& socket, common::DataConstBuffer buffer, Handler handler) = 0;
virtual void asyncRead(boost::asio::ip::tcp::socket& socket, common::DataBuffer buffer, Handler handler) = 0;
virtual void close(boost::asio::ip::tcp::socket& socket) = 0;
virtual void asyncConnect(boost::asio::ip::tcp::socket& socket, const std::string& hostname, uint16_t port, ConnectHandler handler) = 0;
virtual boost::system::error_code connect(boost::asio::ip::tcp::socket& socket, const std::string& hostname, uint16_t port) = 0;
};
}
}
}

View File

@ -0,0 +1,52 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <boost/asio/ip/tcp.hpp>
#include <f1x/aasdk/TCP/ITCPEndpoint.hpp>
#include <f1x/aasdk/TCP/ITCPWrapper.hpp>
namespace f1x
{
namespace aasdk
{
namespace tcp
{
class TCPEndpoint: public ITCPEndpoint, public std::enable_shared_from_this<TCPEndpoint>
{
public:
TCPEndpoint(ITCPWrapper& tcpWrapper, SocketPointer socket);
void send(common::DataConstBuffer buffer, Promise::Pointer promise) override;
void receive(common::DataBuffer buffer, Promise::Pointer promise) override;
void stop() override;
private:
using std::enable_shared_from_this<TCPEndpoint>::shared_from_this;
void asyncOperationHandler(const boost::system::error_code& ec, size_t bytesTransferred, Promise::Pointer promise);
ITCPWrapper& tcpWrapper_;
SocketPointer socket_;
};
}
}
}

View File

@ -0,0 +1,42 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <f1x/aasdk/TCP/ITCPWrapper.hpp>
namespace f1x
{
namespace aasdk
{
namespace tcp
{
class TCPWrapper: public ITCPWrapper
{
public:
void asyncWrite(boost::asio::ip::tcp::socket& socket, common::DataConstBuffer buffer, Handler handler) override;
void asyncRead(boost::asio::ip::tcp::socket& socket, common::DataBuffer buffer, Handler handler) override;
void close(boost::asio::ip::tcp::socket& socket) override;
void asyncConnect(boost::asio::ip::tcp::socket& socket, const std::string& hostname, uint16_t port, ConnectHandler handler) override;
boost::system::error_code connect(boost::asio::ip::tcp::socket& socket, const std::string& hostname, uint16_t port) override;
};
}
}
}

View File

@ -30,10 +30,10 @@ namespace aasdk
namespace transport
{
class USBDataSink
class DataSink
{
public:
USBDataSink();
DataSink();
common::DataBuffer fill();
void commit(common::Data::size_type size);

View File

@ -0,0 +1,48 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <f1x/aasdk/TCP/ITCPEndpoint.hpp>
#include <f1x/aasdk/Transport/Transport.hpp>
namespace f1x
{
namespace aasdk
{
namespace transport
{
class TCPTransport: public Transport
{
public:
TCPTransport(boost::asio::io_service& ioService, tcp::ITCPEndpoint::Pointer tcpEndpoint);
void stop() override;
private:
void enqueueReceive(common::DataBuffer buffer) override;
void enqueueSend(SendQueue::iterator queueElement) override;
void sendHandler(SendQueue::iterator queueElement, const error::Error& e);
tcp::ITCPEndpoint::Pointer tcpEndpoint_;
};
}
}
}

View File

@ -0,0 +1,65 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <list>
#include <queue>
#include <boost/asio.hpp>
#include <f1x/aasdk/Transport/ITransport.hpp>
#include <f1x/aasdk/Transport/DataSink.hpp>
namespace f1x
{
namespace aasdk
{
namespace transport
{
class Transport: public ITransport, public std::enable_shared_from_this<Transport>, boost::noncopyable
{
public:
Transport(boost::asio::io_service& ioService);
void receive(size_t size, ReceivePromise::Pointer promise) override;
void send(common::Data data, SendPromise::Pointer promise) override;
protected:
typedef std::list<std::pair<size_t, ReceivePromise::Pointer>> ReceiveQueue;
typedef std::list<std::pair<common::Data, SendPromise::Pointer>> SendQueue;
using std::enable_shared_from_this<Transport>::shared_from_this;
void receiveHandler(size_t bytesTransferred);
void distributeReceivedData();
void rejectReceivePromises(const error::Error& e);
virtual void enqueueReceive(common::DataBuffer buffer) = 0;
virtual void enqueueSend(SendQueue::iterator queueElement) = 0;
DataSink receivedDataSink_;
boost::asio::io_service::strand receiveStrand_;
ReceiveQueue receiveQueue_;
boost::asio::io_service::strand sendStrand_;
SendQueue sendQueue_;
};
}
}
}

View File

@ -18,11 +18,8 @@
#pragma once
#include <list>
#include <queue>
#include <boost/asio.hpp>
#include <f1x/aasdk/Transport/ITransport.hpp>
#include <f1x/aasdk/Transport/USBDataSink.hpp>
#include <f1x/aasdk/Transport/Transport.hpp>
#include <f1x/aasdk/USB/IAOAPDevice.hpp>
namespace f1x
@ -32,35 +29,20 @@ namespace aasdk
namespace transport
{
class USBTransport: public ITransport, public std::enable_shared_from_this<USBTransport>, boost::noncopyable
class USBTransport: public Transport
{
public:
USBTransport(boost::asio::io_service& ioService, usb::IAOAPDevice::Pointer aoapDevice);
void receive(size_t size, ReceivePromise::Pointer promise) override;
void send(common::Data data, SendPromise::Pointer promise) override;
void stop() override;
private:
typedef std::list<std::pair<size_t, ReceivePromise::Pointer>> InTransferQueue;
typedef std::list<std::pair<common::Data, SendPromise::Pointer>> OutTransferQueue;
using std::enable_shared_from_this<USBTransport>::shared_from_this;
void receiveHandler(size_t bytesTransferred);
void distributeReceivedData();
void rejectReceivePromises(const error::Error& e);
void doSend(OutTransferQueue::iterator queueElementIter, common::Data::size_type offset);
void sendHandler(OutTransferQueue::iterator queueElementIter, common::Data::size_type offset, size_t bytesTransferred);
void enqueueReceive(common::DataBuffer buffer) override;
void enqueueSend(SendQueue::iterator queueElement) override;
void doSend(SendQueue::iterator queueElement, common::Data::size_type offset);
void sendHandler(SendQueue::iterator queueElement, common::Data::size_type offset, size_t bytesTransferred);
usb::IAOAPDevice::Pointer aoapDevice_;
USBDataSink usbReceivedDataSink_;
boost::asio::io_service::strand receiveStrand_;
InTransferQueue receiveQueue_;
boost::asio::io_service::strand sendStrand_;
OutTransferQueue sendQueue_;
static constexpr uint32_t cSendTimeoutMs = 10000;
static constexpr uint32_t cReceiveTimeoutMs = 0;

View File

@ -0,0 +1,58 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <boost/asio.hpp>
#include <f1x/aasdk/USB/IUSBWrapper.hpp>
#include <f1x/aasdk/USB/IAccessoryModeQueryChainFactory.hpp>
#include <f1x/aasdk/USB/IConnectedAccessoriesEnumerator.hpp>
namespace f1x
{
namespace aasdk
{
namespace usb
{
class ConnectedAccessoriesEnumerator: public IConnectedAccessoriesEnumerator, public std::enable_shared_from_this<ConnectedAccessoriesEnumerator>
{
public:
ConnectedAccessoriesEnumerator(IUSBWrapper& usbWrapper, boost::asio::io_service& ioService, IAccessoryModeQueryChainFactory& queryChainFactory);
void enumerate(Promise::Pointer promise) override;
void cancel() override;
private:
using std::enable_shared_from_this<ConnectedAccessoriesEnumerator>::shared_from_this;
void queryNextDevice();
DeviceHandle getNextDeviceHandle();
void reset();
IUSBWrapper& usbWrapper_;
boost::asio::io_service::strand strand_;
IAccessoryModeQueryChainFactory& queryChainFactory_;
IAccessoryModeQueryChain::Pointer queryChain_;
Promise::Pointer promise_;
DeviceListHandle deviceListHandle_;
DeviceList::iterator actualDeviceIter_;
};
}
}
}

View File

@ -0,0 +1,44 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <memory>
#include <f1x/aasdk/IO/Promise.hpp>
namespace f1x
{
namespace aasdk
{
namespace usb
{
class IConnectedAccessoriesEnumerator
{
public:
typedef std::shared_ptr<IConnectedAccessoriesEnumerator> Pointer;
typedef io::Promise<bool> Promise;
virtual ~IConnectedAccessoriesEnumerator() = default;
virtual void enumerate(Promise::Pointer promise) = 0;
virtual void cancel() = 0;
};
}
}
}

View File

@ -19,7 +19,6 @@
#pragma once
#include <boost/asio.hpp>
#include <libusb.h>
#include <list>
#include <f1x/aasdk/USB/IUSBHub.hpp>
#include <f1x/aasdk/USB/IAccessoryModeQueryChainFactory.hpp>

View File

@ -0,0 +1,44 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <gmock/gmock.h>
#include <f1x/aasdk/TCP/ITCPEndpoint.hpp>
namespace f1x
{
namespace aasdk
{
namespace tcp
{
namespace ut
{
class TCPEndpointMock: public ITCPEndpoint
{
public:
MOCK_METHOD2(send, void(common::DataConstBuffer buffer, Promise::Pointer promise));
MOCK_METHOD2(receive, void(common::DataBuffer buffer, Promise::Pointer promise));
MOCK_METHOD0(stop, void());
};
}
}
}
}

View File

@ -0,0 +1,43 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <gmock/gmock.h>
#include <f1x/aasdk/TCP/ITCPEndpoint.hpp>
namespace f1x
{
namespace aasdk
{
namespace tcp
{
namespace ut
{
class TCPEndpointPromiseHandlerMock
{
public:
MOCK_METHOD1(onResolve, void(size_t transferredBytes));
MOCK_METHOD1(onReject, void(const error::Error& e));
};
}
}
}
}

View File

@ -0,0 +1,46 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <gmock/gmock.h>
#include <f1x/aasdk/TCP/ITCPWrapper.hpp>
namespace f1x
{
namespace aasdk
{
namespace tcp
{
namespace ut
{
class TCPWrapperMock: public ITCPWrapper
{
public:
MOCK_METHOD3(asyncWrite, void(boost::asio::ip::tcp::socket& socket, common::DataConstBuffer buffer, Handler handler));
MOCK_METHOD3(asyncRead, void(boost::asio::ip::tcp::socket& socket, common::DataBuffer buffer, Handler handler));
MOCK_METHOD1(close, void(boost::asio::ip::tcp::socket& socket));
MOCK_METHOD4(asyncConnect, void(boost::asio::ip::tcp::socket& socket, const std::string& hostname, uint16_t port, ConnectHandler handler));
MOCK_METHOD3(connect, boost::system::error_code(boost::asio::ip::tcp::socket& socket, const std::string& hostname, uint16_t port));
};
}
}
}
}

View File

@ -0,0 +1,44 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <gmock/gmock.h>
#include <f1x/aasdk/USB/IUSBWrapper.hpp>
#include <f1x/aasdk/Error/Error.hpp>
namespace f1x
{
namespace aasdk
{
namespace usb
{
namespace ut
{
class ConnectedAccessoriesEnumeratorPromiseHandlerMock
{
public:
MOCK_METHOD1(onResolve, void(bool result));
MOCK_METHOD1(onReject, void(const error::Error& e));
};
}
}
}
}

View File

@ -65,6 +65,11 @@ bool DataBuffer::operator==(const std::nullptr_t&) const
return data == nullptr || size == 0;
}
bool DataBuffer::operator==(const DataBuffer& buffer) const
{
return data == buffer.data && size == buffer.size;
}
DataConstBuffer::DataConstBuffer()
: cdata(nullptr)
, size(0)
@ -109,6 +114,11 @@ bool DataConstBuffer::operator==(const std::nullptr_t&) const
return cdata == nullptr || size == 0;
}
bool DataConstBuffer::operator==(const DataConstBuffer& buffer) const
{
return cdata == buffer.cdata && size == buffer.size;
}
common::Data createData(const DataConstBuffer& buffer)
{
common::Data data;

View File

@ -57,7 +57,7 @@ const char* Error::what() const noexcept
bool Error::operator!() const
{
return code_ != ErrorCode::NONE || nativeCode_ != 0;
return code_ == ErrorCode::NONE;
}
bool Error::operator==(const Error& other) const
@ -65,6 +65,16 @@ bool Error::operator==(const Error& other) const
return code_ == other.code_ && nativeCode_ == other.nativeCode_;
}
bool Error::operator==(const ErrorCode& code) const
{
return code_ == code;
}
bool Error::operator!=(const ErrorCode& code) const
{
return !operator==(code);
}
}
}
}

View File

@ -133,9 +133,9 @@ void Messenger::rejectSendPromiseQueue(const error::Error& e)
void Messenger::stop()
{
this->rejectReceivePromiseQueue(error::Error(error::ErrorCode::OPERATION_ABORTED));
this->rejectSendPromiseQueue(error::Error(error::ErrorCode::OPERATION_ABORTED));
channelReceiveMessageQueue_.clear();
receiveStrand_.dispatch([this, self = this->shared_from_this()]() {
channelReceiveMessageQueue_.clear();
});
}
}

View File

@ -0,0 +1,75 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#include <f1x/aasdk/TCP/TCPEndpoint.hpp>
namespace f1x
{
namespace aasdk
{
namespace tcp
{
TCPEndpoint::TCPEndpoint(ITCPWrapper& tcpWrapper, SocketPointer socket)
: tcpWrapper_(tcpWrapper)
, socket_(std::move(socket))
{
}
void TCPEndpoint::send(common::DataConstBuffer buffer, Promise::Pointer promise)
{
tcpWrapper_.asyncWrite(*socket_, std::move(buffer),
std::bind(&TCPEndpoint::asyncOperationHandler,
this->shared_from_this(),
std::placeholders::_1,
std::placeholders::_2,
std::move(promise)));
}
void TCPEndpoint::receive(common::DataBuffer buffer, Promise::Pointer promise)
{
tcpWrapper_.asyncRead(*socket_, std::move(buffer),
std::bind(&TCPEndpoint::asyncOperationHandler,
this->shared_from_this(),
std::placeholders::_1,
std::placeholders::_2,
std::move(promise)));
}
void TCPEndpoint::stop()
{
tcpWrapper_.close(*socket_);
}
void TCPEndpoint::asyncOperationHandler(const boost::system::error_code& ec, size_t bytesTransferred, Promise::Pointer promise)
{
if(!ec)
{
promise->resolve(bytesTransferred);
}
else
{
auto error = ec == boost::asio::error::operation_aborted ? error::Error(error::ErrorCode::OPERATION_ABORTED) : error::Error(error::ErrorCode::TCP_TRANSFER, static_cast<uint32_t>(ec.value()));
promise->reject(error);
}
}
}
}
}

View File

@ -0,0 +1,132 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#include <boost/test/unit_test.hpp>
#include <f1x/aasdk/TCP/UT/TCPWrapper.mock.hpp>
#include <f1x/aasdk/TCP/UT/TCPEndpointPromiseHandler.mock.hpp>
#include <f1x/aasdk/TCP/TCPEndpoint.hpp>
namespace f1x
{
namespace aasdk
{
namespace tcp
{
namespace ut
{
using ::testing::_;
using ::testing::SaveArg;
class TCPEndpointUnitTest
{
protected:
TCPEndpointUnitTest()
: socket_(std::make_shared<boost::asio::ip::tcp::socket>(ioService_))
, promise_(ITCPEndpoint::Promise::defer(ioService_))
{
promise_->then(std::bind(&TCPEndpointPromiseHandlerMock::onResolve, &promiseHandlerMock_, std::placeholders::_1),
std::bind(&TCPEndpointPromiseHandlerMock::onReject, &promiseHandlerMock_, std::placeholders::_1));
}
TCPWrapperMock tcpWrapperMock_;
TCPEndpointPromiseHandlerMock promiseHandlerMock_;
boost::asio::io_service ioService_;
ITCPEndpoint::SocketPointer socket_;
ITCPEndpoint::Promise::Pointer promise_;
};
BOOST_FIXTURE_TEST_CASE(TCPEndpoint_Receive, TCPEndpointUnitTest)
{
auto tcpEndpoint = std::make_shared<TCPEndpoint>(tcpWrapperMock_, std::move(socket_));
common::DataBuffer buffer;
ITCPWrapper::Handler handler;
EXPECT_CALL(tcpWrapperMock_, asyncRead(_, _, _)).WillOnce(DoAll(SaveArg<1>(&buffer), SaveArg<2>(&handler)));
common::Data actualData(100, 0);
tcpEndpoint->receive(common::DataBuffer(actualData), std::move(promise_));
const common::Data expectedData(actualData.size(), 0x5F);
std::copy(expectedData.begin(), expectedData.end(), buffer.data);
EXPECT_CALL(promiseHandlerMock_, onResolve(expectedData.size()));
EXPECT_CALL(promiseHandlerMock_, onReject(_)).Times(0);
handler(boost::system::error_code(), expectedData.size());
ioService_.run();
BOOST_CHECK_EQUAL_COLLECTIONS(actualData.begin(), actualData.end(), expectedData.begin(), expectedData.end());
}
BOOST_FIXTURE_TEST_CASE(TCPEndpoint_ReceiveError, TCPEndpointUnitTest)
{
auto tcpEndpoint = std::make_shared<TCPEndpoint>(tcpWrapperMock_, std::move(socket_));
common::DataBuffer buffer;
ITCPWrapper::Handler handler;
EXPECT_CALL(tcpWrapperMock_, asyncRead(_, _, _)).WillOnce(DoAll(SaveArg<1>(&buffer), SaveArg<2>(&handler)));
common::Data actualData(100, 0);
tcpEndpoint->receive(common::DataBuffer(actualData), std::move(promise_));
EXPECT_CALL(promiseHandlerMock_, onResolve(_)).Times(0);
EXPECT_CALL(promiseHandlerMock_, onReject(error::Error(error::ErrorCode::TCP_TRANSFER, boost::asio::error::bad_descriptor)));
handler(boost::asio::error::bad_descriptor, 0);
ioService_.run();
}
BOOST_FIXTURE_TEST_CASE(TCPEndpoint_Send, TCPEndpointUnitTest)
{
auto tcpEndpoint = std::make_shared<TCPEndpoint>(tcpWrapperMock_, std::move(socket_));
common::Data actualData(100, 0);
common::DataConstBuffer buffer(actualData);
ITCPWrapper::Handler handler;
EXPECT_CALL(tcpWrapperMock_, asyncWrite(_, buffer, _)).WillOnce(SaveArg<2>(&handler));
tcpEndpoint->send(common::DataConstBuffer(actualData), std::move(promise_));
EXPECT_CALL(promiseHandlerMock_, onResolve(actualData.size()));
EXPECT_CALL(promiseHandlerMock_, onReject(_)).Times(0);
handler(boost::system::error_code(), actualData.size());
ioService_.run();
}
BOOST_FIXTURE_TEST_CASE(TCPEndpoint_SendError, TCPEndpointUnitTest)
{
auto tcpEndpoint = std::make_shared<TCPEndpoint>(tcpWrapperMock_, std::move(socket_));
common::Data actualData(100, 0);
common::DataConstBuffer buffer(actualData);
ITCPWrapper::Handler handler;
EXPECT_CALL(tcpWrapperMock_, asyncWrite(_, buffer, _)).WillOnce(SaveArg<2>(&handler));
tcpEndpoint->send(common::DataConstBuffer(actualData), std::move(promise_));
EXPECT_CALL(promiseHandlerMock_, onResolve(_)).Times(0);
EXPECT_CALL(promiseHandlerMock_, onReject(error::Error(error::ErrorCode::OPERATION_ABORTED)));
handler(boost::asio::error::operation_aborted, 0);
ioService_.run();
}
}
}
}
}

View File

@ -0,0 +1,60 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#include <boost/asio.hpp>
#include <f1x/aasdk/TCP/TCPWrapper.hpp>
namespace f1x
{
namespace aasdk
{
namespace tcp
{
void TCPWrapper::asyncWrite(boost::asio::ip::tcp::socket& socket, common::DataConstBuffer buffer, Handler handler)
{
boost::asio::async_write(socket, boost::asio::buffer(buffer.cdata, buffer.size), std::move(handler));
}
void TCPWrapper::asyncRead(boost::asio::ip::tcp::socket& socket, common::DataBuffer buffer, Handler handler)
{
socket.async_receive(boost::asio::buffer(buffer.data, buffer.size), std::move(handler));
}
void TCPWrapper::close(boost::asio::ip::tcp::socket& socket)
{
boost::system::error_code ec;
socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
socket.close(ec);
}
void TCPWrapper::asyncConnect(boost::asio::ip::tcp::socket& socket, const std::string& hostname, uint16_t port, ConnectHandler handler)
{
socket.async_connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(hostname), port), std::move(handler));
}
boost::system::error_code TCPWrapper::connect(boost::asio::ip::tcp::socket& socket, const std::string& hostname, uint16_t port)
{
boost::system::error_code ec;
socket.connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(hostname), port), ec);
return ec;
}
}
}
}

View File

@ -17,7 +17,7 @@
*/
#include <cstring>
#include <f1x/aasdk/Transport/USBDataSink.hpp>
#include <f1x/aasdk/Transport/DataSink.hpp>
#include <f1x/aasdk/Error/Error.hpp>
namespace f1x
@ -27,44 +27,45 @@ namespace aasdk
namespace transport
{
USBDataSink::USBDataSink()
DataSink::DataSink()
: data_(common::cStaticDataSize)
{
}
common::DataBuffer USBDataSink::fill()
common::DataBuffer DataSink::fill()
{
const auto offset = data_.size();
data_.resize(data_.size() + cChunkSize);
return common::DataBuffer(&data_[offset], cChunkSize);
auto ptr = data_.is_linearized() ? &data_[offset] : data_.linearize() + offset;
return common::DataBuffer(ptr, cChunkSize);
}
void USBDataSink::commit(common::Data::size_type size)
void DataSink::commit(common::Data::size_type size)
{
if(size > cChunkSize)
{
throw error::Error(error::ErrorCode::USB_SINK_COMMIT_OVERFLOW);
throw error::Error(error::ErrorCode::DATA_SINK_COMMIT_OVERFLOW);
}
data_.resize(data_.size() - (cChunkSize - size));
data_.erase_end((cChunkSize - size));
}
common::Data::size_type USBDataSink::getAvailableSize()
common::Data::size_type DataSink::getAvailableSize()
{
return data_.size();
}
common::Data USBDataSink::consume(common::Data::size_type size)
common::Data DataSink::consume(common::Data::size_type size)
{
if(size > data_.size())
{
throw error::Error(error::ErrorCode::USB_SINK_CONSUME_UNDERFLOW);
throw error::Error(error::ErrorCode::DATA_SINK_CONSUME_UNDERFLOW);
}
common::Data data;
common::copy(data, common::DataConstBuffer(&data_[0], size));
data_.erase(data_.begin(), data_.begin() + size);
common::Data data(size, 0);
std::copy(data_.begin(), data_.begin() + size, data.begin());
data_.erase_begin(size);
return data;
}

View File

@ -0,0 +1,88 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#include <f1x/aasdk/Transport/TCPTransport.hpp>
namespace f1x
{
namespace aasdk
{
namespace transport
{
TCPTransport::TCPTransport(boost::asio::io_service& ioService, tcp::ITCPEndpoint::Pointer tcpEndpoint)
: Transport(ioService)
, tcpEndpoint_(std::move(tcpEndpoint))
{
}
void TCPTransport::enqueueReceive(common::DataBuffer buffer)
{
auto receivePromise = tcp::ITCPEndpoint::Promise::defer(receiveStrand_);
receivePromise->then([this, self = this->shared_from_this()](auto bytesTransferred) {
this->receiveHandler(bytesTransferred);
},
[this, self = this->shared_from_this()](auto e) {
this->rejectReceivePromises(e);
});
tcpEndpoint_->receive(buffer, std::move(receivePromise));
}
void TCPTransport::enqueueSend(SendQueue::iterator queueElement)
{
auto sendPromise = tcp::ITCPEndpoint::Promise::defer(sendStrand_);
sendPromise->then([this, self = this->shared_from_this(), queueElement](auto) {
this->sendHandler(queueElement, error::Error());
},
[this, self = this->shared_from_this(), queueElement](auto e) {
this->sendHandler(queueElement, e);
});
tcpEndpoint_->send(common::DataConstBuffer(queueElement->first), std::move(sendPromise));
}
void TCPTransport::stop()
{
tcpEndpoint_->stop();
}
void TCPTransport::sendHandler(SendQueue::iterator queueElement, const error::Error& e)
{
if(!e)
{
queueElement->second->resolve();
}
else
{
queueElement->second->reject(e);
}
sendQueue_.erase(queueElement);
if(!sendQueue_.empty())
{
this->enqueueSend(sendQueue_.begin());
}
}
}
}
}

View File

@ -0,0 +1,279 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#include <boost/test/unit_test.hpp>
#include <f1x/aasdk/TCP/UT/TCPEndpoint.mock.hpp>
#include <f1x/aasdk/Transport/UT/TransportReceivePromiseHandler.mock.hpp>
#include <f1x/aasdk/Transport/UT/TransportSendPromiseHandler.mock.hpp>
#include <f1x/aasdk/Transport/TCPTransport.hpp>
namespace f1x
{
namespace aasdk
{
namespace transport
{
namespace ut
{
using ::testing::ReturnRef;
using ::testing::SaveArg;
using ::testing::_;
using ::testing::AtLeast;
class TCPTransportUnitTest
{
protected:
TCPTransportUnitTest()
: receivePromise_(ITransport::ReceivePromise::defer(ioService_))
, sendPromise_(ITransport::SendPromise::defer(ioService_))
, tcpEndpoint_(&tcpEndpointMock_, [](auto*) {})
{
receivePromise_->then(std::bind(&TransportReceivePromiseHandlerMock::onResolve, &receivePromiseHandlerMock_, std::placeholders::_1),
std::bind(&TransportReceivePromiseHandlerMock::onReject, &receivePromiseHandlerMock_, std::placeholders::_1));
sendPromise_->then(std::bind(&TransportSendPromiseHandlerMock::onResolve, &sendPromiseHandlerMock_),
std::bind(&TransportSendPromiseHandlerMock::onReject, &sendPromiseHandlerMock_, std::placeholders::_1));
}
boost::asio::io_service ioService_;
tcp::ut::TCPEndpointMock tcpEndpointMock_;
TransportReceivePromiseHandlerMock receivePromiseHandlerMock_;
ITransport::ReceivePromise::Pointer receivePromise_;
TransportSendPromiseHandlerMock sendPromiseHandlerMock_;
ITransport::SendPromise::Pointer sendPromise_;
tcp::ITCPEndpoint::Pointer tcpEndpoint_;
};
BOOST_FIXTURE_TEST_CASE(TCPTransport_ReceiveAtOnce, TCPTransportUnitTest)
{
const size_t receiveSize = 100;
tcp::ITCPEndpoint::Promise::Pointer tcpEndpointPromise;
common::DataBuffer dataBuffer;
EXPECT_CALL(tcpEndpointMock_, receive(_, _)).WillOnce(DoAll(SaveArg<0>(&dataBuffer), SaveArg<1>(&tcpEndpointPromise)));
auto transport(std::make_shared<TCPTransport>(ioService_, tcpEndpoint_));
transport->receive(receiveSize, std::move(receivePromise_));
ioService_.run();
ioService_.reset();
BOOST_TEST(dataBuffer.size >= receiveSize);
common::Data expectedData(receiveSize, 0x5E);
std::copy(expectedData.begin(), expectedData.end(), dataBuffer.data);
EXPECT_CALL(receivePromiseHandlerMock_, onResolve(expectedData)).Times(1);
EXPECT_CALL(receivePromiseHandlerMock_, onReject(_)).Times(0);
tcpEndpointPromise->resolve(receiveSize);
ioService_.run();
}
BOOST_FIXTURE_TEST_CASE(TCPTransport_ReceiveInPieces, TCPTransportUnitTest)
{
const size_t stepsCount = 100;
const size_t receiveSize = 1000 * stepsCount;
const size_t stepSize = receiveSize / stepsCount;
auto transport(std::make_shared<TCPTransport>(ioService_, tcpEndpoint_));
transport->receive(receiveSize, std::move(receivePromise_));
tcp::ITCPEndpoint::Promise::Pointer tcpEndpointPromise;
common::DataBuffer dataBuffer;
EXPECT_CALL(tcpEndpointMock_, receive(_, _)).Times(AtLeast(stepsCount))
.WillRepeatedly(DoAll(SaveArg<0>(&dataBuffer), SaveArg<1>(&tcpEndpointPromise)));
common::Data expectedData(receiveSize, 0x5E);
EXPECT_CALL(receivePromiseHandlerMock_, onResolve(expectedData)).Times(1);
EXPECT_CALL(receivePromiseHandlerMock_, onReject(_)).Times(0);
for(size_t i = 0; i < stepsCount; ++i)
{
ioService_.run();
ioService_.reset();
BOOST_TEST(dataBuffer.size >= stepSize);
std::fill(dataBuffer.data, dataBuffer.data + stepSize, 0x5E);
tcpEndpointPromise->resolve(stepSize);
ioService_.run();
}
}
BOOST_FIXTURE_TEST_CASE(TCPTransport_OnlyOneReceiveAtATime, TCPTransportUnitTest)
{
const size_t receiveSize = 200;
const size_t stepSize = receiveSize / 2;
tcp::ITCPEndpoint::Promise::Pointer tcpEndpointPromise;
common::DataBuffer dataBuffer;
EXPECT_CALL(tcpEndpointMock_, receive(_, _)).WillOnce(DoAll(SaveArg<0>(&dataBuffer), SaveArg<1>(&tcpEndpointPromise)));
auto transport(std::make_shared<TCPTransport>(ioService_, tcpEndpoint_));
transport->receive(stepSize, std::move(receivePromise_));
ioService_.run();
ioService_.reset();
BOOST_TEST(dataBuffer.size >= receiveSize);
std::fill(dataBuffer.data, dataBuffer.data + stepSize, 0x5E);
std::fill(dataBuffer.data + stepSize, dataBuffer.data + receiveSize, 0x5F);
auto secondPromise = ITransport::ReceivePromise::defer(ioService_);
TransportReceivePromiseHandlerMock secondPromiseHandlerMock;
secondPromise->then(std::bind(&TransportReceivePromiseHandlerMock::onResolve, &secondPromiseHandlerMock, std::placeholders::_1),
std::bind(&TransportReceivePromiseHandlerMock::onReject, &secondPromiseHandlerMock, std::placeholders::_1));
transport->receive(stepSize, std::move(secondPromise));
ioService_.run();
ioService_.reset();
common::Data expectedData(stepSize, 0x5E);
EXPECT_CALL(receivePromiseHandlerMock_, onResolve(expectedData)).Times(1);
EXPECT_CALL(receivePromiseHandlerMock_, onReject(_)).Times(0);
common::Data secondExpectedData(stepSize, 0x5F);
EXPECT_CALL(secondPromiseHandlerMock, onResolve(secondExpectedData)).Times(1);
EXPECT_CALL(secondPromiseHandlerMock, onReject(_)).Times(0);
tcpEndpointPromise->resolve(receiveSize);
ioService_.run();
}
BOOST_FIXTURE_TEST_CASE(TCPTransport_ReceiveError, TCPTransportUnitTest)
{
tcp::ITCPEndpoint::Promise::Pointer tcpEndpointPromise;
EXPECT_CALL(tcpEndpointMock_, receive(_, _)).WillOnce(SaveArg<1>(&tcpEndpointPromise));
auto transport(std::make_shared<TCPTransport>(ioService_, tcpEndpoint_));
transport->receive(1000, std::move(receivePromise_));
auto secondPromise = ITransport::ReceivePromise::defer(ioService_);
secondPromise->then(std::bind(&TransportReceivePromiseHandlerMock::onResolve, &receivePromiseHandlerMock_, std::placeholders::_1),
std::bind(&TransportReceivePromiseHandlerMock::onReject, &receivePromiseHandlerMock_, std::placeholders::_1));
transport->receive(1000, std::move(secondPromise));
ioService_.run();
ioService_.reset();
const error::Error e(error::ErrorCode::TCP_TRANSFER, 11);
EXPECT_CALL(receivePromiseHandlerMock_, onResolve(_)).Times(0);
EXPECT_CALL(receivePromiseHandlerMock_, onReject(e)).Times(2);
tcpEndpointPromise->reject(e);
ioService_.run();
}
BOOST_FIXTURE_TEST_CASE(TCPTransport_Send, TCPTransportUnitTest)
{
tcp::ITCPEndpoint::Promise::Pointer tcpEndpointPromise;
common::DataConstBuffer buffer;
EXPECT_CALL(tcpEndpointMock_, send(_, _)).WillOnce(DoAll(SaveArg<0>(&buffer), SaveArg<1>(&tcpEndpointPromise)));
auto transport(std::make_shared<TCPTransport>(ioService_, tcpEndpoint_));
const common::Data expectedData(1000, 0x5E);
transport->send(expectedData, std::move(sendPromise_));
ioService_.run();
ioService_.reset();
common::Data actualData(buffer.cdata, buffer.cdata + buffer.size);
BOOST_CHECK_EQUAL_COLLECTIONS(actualData.begin(), actualData.end(), expectedData.begin(), expectedData.end());
EXPECT_CALL(sendPromiseHandlerMock_, onReject(_)).Times(0);
EXPECT_CALL(sendPromiseHandlerMock_, onResolve());
tcpEndpointPromise->resolve(expectedData.size());
ioService_.run();
}
BOOST_FIXTURE_TEST_CASE(TCPTransport_OnlyOneSendAtATime, TCPTransportUnitTest)
{
tcp::ITCPEndpoint::Promise::Pointer tcpEndpointPromise;
common::DataConstBuffer buffer;
EXPECT_CALL(tcpEndpointMock_, send(_, _)).Times(2).WillRepeatedly(DoAll(SaveArg<0>(&buffer), SaveArg<1>(&tcpEndpointPromise)));
auto transport(std::make_shared<TCPTransport>(ioService_, tcpEndpoint_));
const common::Data expectedData1(1000, 0x5E);
transport->send(expectedData1, std::move(sendPromise_));
ioService_.run();
ioService_.reset();
const common::Data expectedData2(3000, 0x5F);
auto secondSendPromise = ITransport::SendPromise::defer(ioService_);
TransportSendPromiseHandlerMock secondSendPromiseHandlerMock;
secondSendPromise->then(std::bind(&TransportSendPromiseHandlerMock::onResolve, &secondSendPromiseHandlerMock),
std::bind(&TransportSendPromiseHandlerMock::onReject, &secondSendPromiseHandlerMock, std::placeholders::_1));
transport->send(expectedData2, std::move(secondSendPromise));
ioService_.run();
ioService_.reset();
common::Data actualData1(buffer.cdata, buffer.cdata + buffer.size);
BOOST_CHECK_EQUAL_COLLECTIONS(actualData1.begin(), actualData1.end(), expectedData1.begin(), expectedData1.end());
EXPECT_CALL(sendPromiseHandlerMock_, onReject(_)).Times(0);
EXPECT_CALL(sendPromiseHandlerMock_, onResolve());
tcpEndpointPromise->resolve(expectedData1.size());
ioService_.run();
ioService_.reset();
common::Data actualData2(buffer.cdata, buffer.cdata + buffer.size);
BOOST_CHECK_EQUAL_COLLECTIONS(actualData2.begin(), actualData2.end(), expectedData2.begin(), expectedData2.end());
EXPECT_CALL(secondSendPromiseHandlerMock, onReject(_)).Times(0);
EXPECT_CALL(secondSendPromiseHandlerMock, onResolve());
tcpEndpointPromise->resolve(expectedData2.size());
ioService_.run();
}
BOOST_FIXTURE_TEST_CASE(TCPTransport_SendError, TCPTransportUnitTest)
{
tcp::ITCPEndpoint::Promise::Pointer tcpEndpointPromise;
EXPECT_CALL(tcpEndpointMock_, send(_, _)).Times(2).WillRepeatedly(SaveArg<1>(&tcpEndpointPromise));
auto transport(std::make_shared<TCPTransport>(ioService_, tcpEndpoint_));
const common::Data expectedData1(1000, 0x5E);
transport->send(expectedData1, std::move(sendPromise_));
ioService_.run();
ioService_.reset();
auto secondSendPromise = ITransport::SendPromise::defer(ioService_);
TransportSendPromiseHandlerMock secondSendPromiseHandlerMock;
secondSendPromise->then(std::bind(&TransportSendPromiseHandlerMock::onResolve, &secondSendPromiseHandlerMock),
std::bind(&TransportSendPromiseHandlerMock::onReject, &secondSendPromiseHandlerMock, std::placeholders::_1));
const common::Data expectedData2(3000, 0x5F);
transport->send(expectedData2, std::move(secondSendPromise));
ioService_.run();
ioService_.reset();
const error::Error e(error::ErrorCode::USB_TRANSFER, 15);
EXPECT_CALL(sendPromiseHandlerMock_, onReject(e));
EXPECT_CALL(sendPromiseHandlerMock_, onResolve()).Times(0);
tcpEndpointPromise->reject(e);
ioService_.run();
ioService_.reset();
EXPECT_CALL(secondSendPromiseHandlerMock, onReject(_)).Times(0);
EXPECT_CALL(secondSendPromiseHandlerMock, onResolve());
tcpEndpointPromise->resolve(expectedData2.size());
ioService_.run();
}
}
}
}
}

View File

@ -0,0 +1,109 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#include <f1x/aasdk/Transport/Transport.hpp>
namespace f1x
{
namespace aasdk
{
namespace transport
{
Transport::Transport(boost::asio::io_service& ioService)
: receiveStrand_(ioService)
, sendStrand_(ioService)
{}
void Transport::receive(size_t size, ReceivePromise::Pointer promise)
{
receiveStrand_.dispatch([this, self = this->shared_from_this(), size, promise = std::move(promise)]() mutable {
receiveQueue_.emplace_back(std::make_pair(size, std::move(promise)));
if(receiveQueue_.size() == 1)
{
try
{
this->distributeReceivedData();
}
catch(const error::Error& e)
{
this->rejectReceivePromises(e);
}
}
});
}
void Transport::receiveHandler(size_t bytesTransferred)
{
try
{
receivedDataSink_.commit(bytesTransferred);
this->distributeReceivedData();
}
catch(const error::Error& e)
{
this->rejectReceivePromises(e);
}
}
void Transport::distributeReceivedData()
{
for(auto queueElement = receiveQueue_.begin(); queueElement != receiveQueue_.end();)
{
if(receivedDataSink_.getAvailableSize() < queueElement->first)
{
auto buffer = receivedDataSink_.fill();
this->enqueueReceive(std::move(buffer));
break;
}
else
{
auto data(receivedDataSink_.consume(queueElement->first));
queueElement->second->resolve(std::move(data));
queueElement = receiveQueue_.erase(queueElement);
}
}
}
void Transport::rejectReceivePromises(const error::Error& e)
{
for(auto& queueElement : receiveQueue_)
{
queueElement.second->reject(e);
}
receiveQueue_.clear();
}
void Transport::send(common::Data data, SendPromise::Pointer promise)
{
sendStrand_.dispatch([this, self = this->shared_from_this(), data = std::move(data), promise = std::move(promise)]() mutable {
sendQueue_.emplace_back(std::make_pair(std::move(data), std::move(promise)));
if(sendQueue_.size() == 1)
{
this->enqueueSend(sendQueue_.begin());
}
});
}
}
}
}

View File

@ -26,116 +26,57 @@ namespace transport
{
USBTransport::USBTransport(boost::asio::io_service& ioService, usb::IAOAPDevice::Pointer aoapDevice)
: aoapDevice_(std::move(aoapDevice))
, receiveStrand_(ioService)
, sendStrand_(ioService)
: Transport(ioService)
, aoapDevice_(std::move(aoapDevice))
{}
void USBTransport::receive(size_t size, ReceivePromise::Pointer promise)
void USBTransport::enqueueReceive(common::DataBuffer buffer)
{
receiveStrand_.dispatch([this, self = this->shared_from_this(), size, promise = std::move(promise)]() mutable {
receiveQueue_.emplace_back(std::make_pair(size, std::move(promise)));
auto usbEndpointPromise = usb::IUSBEndpoint::Promise::defer(receiveStrand_);
usbEndpointPromise->then([this, self = this->shared_from_this()](auto bytesTransferred) {
this->receiveHandler(bytesTransferred);
},
[this, self = this->shared_from_this()](auto e) {
this->rejectReceivePromises(e);
});
if(receiveQueue_.size() == 1)
{
try
{
this->distributeReceivedData();
}
catch(const error::Error& e)
{
this->rejectReceivePromises(e);
}
}
});
aoapDevice_->getInEndpoint().bulkTransfer(buffer, cReceiveTimeoutMs, std::move(usbEndpointPromise));
}
void USBTransport::receiveHandler(size_t bytesTransferred)
void USBTransport::enqueueSend(SendQueue::iterator queueElement)
{
try
{
usbReceivedDataSink_.commit(bytesTransferred);
this->distributeReceivedData();
}
catch(const error::Error& e)
{
this->rejectReceivePromises(e);
}
this->doSend(queueElement, 0);
}
void USBTransport::distributeReceivedData()
{
for(auto queueElement = receiveQueue_.begin(); queueElement != receiveQueue_.end();)
{
if(usbReceivedDataSink_.getAvailableSize() < queueElement->first)
{
auto buffer = usbReceivedDataSink_.fill();
auto usbEndpointPromise = usb::IUSBEndpoint::Promise::defer(receiveStrand_);
usbEndpointPromise->then(std::bind(&USBTransport::receiveHandler, this->shared_from_this(), std::placeholders::_1),
std::bind(&USBTransport::rejectReceivePromises, this->shared_from_this(), std::placeholders::_1));
aoapDevice_->getInEndpoint().bulkTransfer(buffer, cReceiveTimeoutMs, std::move(usbEndpointPromise));
break;
}
else
{
auto data(usbReceivedDataSink_.consume(queueElement->first));
queueElement->second->resolve(std::move(data));
queueElement = receiveQueue_.erase(queueElement);
}
}
}
void USBTransport::rejectReceivePromises(const error::Error& e)
{
for(auto& queueElement : receiveQueue_)
{
queueElement.second->reject(e);
}
receiveQueue_.clear();
}
void USBTransport::send(common::Data data, SendPromise::Pointer promise)
{
sendStrand_.dispatch([this, self = this->shared_from_this(), data = std::move(data), promise = std::move(promise)]() mutable {
sendQueue_.emplace_back(std::make_pair(std::move(data), std::move(promise)));
if(sendQueue_.size() == 1)
{
this->doSend(sendQueue_.begin(), 0);
}
});
}
void USBTransport::doSend(OutTransferQueue::iterator queueElementIter, common::Data::size_type offset)
void USBTransport::doSend(SendQueue::iterator queueElement, common::Data::size_type offset)
{
auto usbEndpointPromise = usb::IUSBEndpoint::Promise::defer(sendStrand_);
usbEndpointPromise->then([this, self = this->shared_from_this(), queueElementIter, offset](size_t bytesTransferred) mutable {
this->sendHandler(queueElementIter, offset, bytesTransferred);
usbEndpointPromise->then([this, self = this->shared_from_this(), queueElement, offset](size_t bytesTransferred) mutable {
this->sendHandler(queueElement, offset, bytesTransferred);
},
[this, self = this->shared_from_this(), queueElementIter](const error::Error& e) mutable {
queueElementIter->second->reject(e);
sendQueue_.erase(queueElementIter);
[this, self = this->shared_from_this(), queueElement](const error::Error& e) mutable {
queueElement->second->reject(e);
sendQueue_.erase(queueElement);
if(!sendQueue_.empty())
{
this->doSend(sendQueue_.begin(), 0);
}
});
aoapDevice_->getOutEndpoint().bulkTransfer(common::DataBuffer(queueElementIter->first, offset), cSendTimeoutMs, std::move(usbEndpointPromise));
aoapDevice_->getOutEndpoint().bulkTransfer(common::DataBuffer(queueElement->first, offset), cSendTimeoutMs, std::move(usbEndpointPromise));
}
void USBTransport::sendHandler(OutTransferQueue::iterator queueElementIter, common::Data::size_type offset, size_t bytesTransferred)
void USBTransport::sendHandler(SendQueue::iterator queueElement, common::Data::size_type offset, size_t bytesTransferred)
{
if(offset + bytesTransferred < queueElementIter->first.size())
if(offset + bytesTransferred < queueElement->first.size())
{
this->doSend(queueElementIter, offset + bytesTransferred);
this->doSend(queueElement, offset + bytesTransferred);
}
else
{
queueElementIter->second->resolve();
sendQueue_.erase(queueElementIter);
queueElement->second->resolve();
sendQueue_.erase(queueElement);
if(!sendQueue_.empty())
{

View File

@ -279,8 +279,7 @@ BOOST_FIXTURE_TEST_CASE(USBTransport_OnlyOneSendAtATime, USBTransportUnitTest)
BOOST_FIXTURE_TEST_CASE(USBTransport_SendError, USBTransportUnitTest)
{
usb::IUSBEndpoint::Promise::Pointer usbEndpointPromise;
common::DataBuffer buffer;
EXPECT_CALL(outEndpointMock_, bulkTransfer(_, _, _)).Times(2).WillRepeatedly(DoAll(SaveArg<0>(&buffer), SaveArg<2>(&usbEndpointPromise)));
EXPECT_CALL(outEndpointMock_, bulkTransfer(_, _, _)).Times(2).WillRepeatedly(SaveArg<2>(&usbEndpointPromise));
USBTransport::Pointer transport(std::make_shared<USBTransport>(ioService_, aoapDevice_));
const common::Data expectedData1(1000, 0x5E);
@ -288,13 +287,12 @@ BOOST_FIXTURE_TEST_CASE(USBTransport_SendError, USBTransportUnitTest)
ioService_.run();
ioService_.reset();
const common::Data expectedData2(3000, 0x5F);
auto secondSendPromise = ITransport::SendPromise::defer(ioService_);
TransportSendPromiseHandlerMock secondSendPromiseHandlerMock;
secondSendPromise->then(std::bind(&TransportSendPromiseHandlerMock::onResolve, &secondSendPromiseHandlerMock),
std::bind(&TransportSendPromiseHandlerMock::onReject, &secondSendPromiseHandlerMock, std::placeholders::_1));
const common::Data expectedData2(3000, 0x5F);
transport->send(expectedData2, std::move(secondSendPromise));
ioService_.run();
ioService_.reset();

View File

@ -66,11 +66,13 @@ void AccessoryModeQueryChain::start(DeviceHandle handle, Promise::Pointer promis
void AccessoryModeQueryChain::cancel()
{
if(activeQuery_ != nullptr)
{
activeQuery_->cancel();
activeQuery_.reset();
}
strand_.dispatch([this, self = this->shared_from_this()]() {
if(activeQuery_ != nullptr)
{
activeQuery_->cancel();
activeQuery_.reset();
}
});
}
void AccessoryModeQueryChain::startQuery(AccessoryModeQueryType queryType, IUSBEndpoint::Pointer usbEndpoint, IAccessoryModeQuery::Promise::Pointer queryPromise)

View File

@ -0,0 +1,134 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#include <f1x/aasdk/USB/ConnectedAccessoriesEnumerator.hpp>
namespace f1x
{
namespace aasdk
{
namespace usb
{
ConnectedAccessoriesEnumerator::ConnectedAccessoriesEnumerator(IUSBWrapper& usbWrapper, boost::asio::io_service& ioService, IAccessoryModeQueryChainFactory& queryChainFactory)
: usbWrapper_(usbWrapper)
, strand_(ioService)
, queryChainFactory_(queryChainFactory)
{
}
void ConnectedAccessoriesEnumerator::enumerate(Promise::Pointer promise)
{
strand_.dispatch([this, self = this->shared_from_this(), promise = std::move(promise)]() mutable {
if(promise_ != nullptr)
{
promise->reject(error::Error(error::ErrorCode::OPERATION_IN_PROGRESS));
}
else
{
promise_ = std::move(promise);
auto result = usbWrapper_.getDeviceList(deviceListHandle_);
if(result < 0)
{
promise_->reject(error::Error(error::ErrorCode::USB_LIST_DEVICES));
}
else if(deviceListHandle_->empty())
{
promise_->resolve(false);
}
else
{
actualDeviceIter_ = deviceListHandle_->begin();
this->queryNextDevice();
}
}
});
}
void ConnectedAccessoriesEnumerator::cancel()
{
strand_.dispatch([this, self = this->shared_from_this()]() mutable {
if(queryChain_ != nullptr)
{
queryChain_->cancel();
queryChain_.reset();
}
});
}
void ConnectedAccessoriesEnumerator::queryNextDevice()
{
auto deviceHandle = this->getNextDeviceHandle();
if(deviceHandle != nullptr)
{
queryChain_ = queryChainFactory_.create();
auto queryChainPromise = IAccessoryModeQueryChain::Promise::defer(strand_);
queryChainPromise->then([this, self = this->shared_from_this()](DeviceHandle) mutable {
promise_->resolve(true);
this->reset();
},
[this, self = this->shared_from_this()](const error::Error& e) mutable {
if(e != error::ErrorCode::OPERATION_ABORTED)
{
this->queryNextDevice();
}
});
queryChain_->start(std::move(deviceHandle), std::move(queryChainPromise));
}
else if(actualDeviceIter_ == deviceListHandle_->end())
{
promise_->resolve(false);
this->reset();
}
}
DeviceHandle ConnectedAccessoriesEnumerator::getNextDeviceHandle()
{
DeviceHandle handle;
while(actualDeviceIter_ != deviceListHandle_->end())
{
auto openResult = usbWrapper_.open(*actualDeviceIter_, handle);
++actualDeviceIter_;
if(openResult == 0)
{
break;
}
}
return handle;
}
void ConnectedAccessoriesEnumerator::reset()
{
queryChain_.reset();
deviceListHandle_.reset();
actualDeviceIter_ = DeviceList::iterator();
promise_.reset();
}
}
}
}

View File

@ -0,0 +1,211 @@
/*
* This file is part of aasdk library project.
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
*
* aasdk 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.
* aasdk 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 aasdk. If not, see <http://www.gnu.org/licenses/>.
*/
#include <boost/test/unit_test.hpp>
#include <f1x/aasdk/USB/UT/USBWrapper.mock.hpp>
#include <f1x/aasdk/USB/UT/AccessoryModeQueryChainFactory.mock.hpp>
#include <f1x/aasdk/USB/UT/AccessoryModeQueryChain.mock.hpp>
#include <f1x/aasdk/USB/UT/ConnectedAccessoriesEnumeratorPromiseHandler.mock.hpp>
#include <f1x/aasdk/USB/ConnectedAccessoriesEnumerator.hpp>
namespace f1x
{
namespace aasdk
{
namespace usb
{
namespace ut
{
using ::testing::_;
using ::testing::SetArgReferee;
using ::testing::Return;
using ::testing::SaveArg;
class ConnectedAccessoriesEnumeratorUnitTest
{
protected:
ConnectedAccessoriesEnumeratorUnitTest()
: queryChain_(&queryChainMock_, [](auto*) {})
, deviceListHandle_(&deviceList_, [](auto*) {})
, device_(reinterpret_cast<libusb_device*>(-1))
, deviceHandle_(reinterpret_cast<libusb_device_handle*>(&dummyDeviceHandle_), [](auto*) {})
, promise_(IConnectedAccessoriesEnumerator::Promise::defer(ioService_))
{
promise_->then(std::bind(&ConnectedAccessoriesEnumeratorPromiseHandlerMock::onResolve, &promiseHandlerMock_, std::placeholders::_1),
std::bind(&ConnectedAccessoriesEnumeratorPromiseHandlerMock::onReject, &promiseHandlerMock_, std::placeholders::_1));
}
boost::asio::io_service ioService_;
USBWrapperMock usbWrapperMock_;
AccessoryModeQueryChainFactoryMock queryChainFactoryMock_;
AccessoryModeQueryChainMock queryChainMock_;
IAccessoryModeQueryChain::Pointer queryChain_;
DeviceList deviceList_;
DeviceListHandle deviceListHandle_;
libusb_device* device_;
USBWrapperMock::DummyDeviceHandle dummyDeviceHandle_;
DeviceHandle deviceHandle_;
ConnectedAccessoriesEnumeratorPromiseHandlerMock promiseHandlerMock_;
IConnectedAccessoriesEnumerator::Promise::Pointer promise_;
};
BOOST_FIXTURE_TEST_CASE(ConnectedAccessoriesEnumerator_FirstDeviceIsAOAPCapables, ConnectedAccessoriesEnumeratorUnitTest)
{
deviceList_.push_back(reinterpret_cast<libusb_device*>(1));
EXPECT_CALL(queryChainFactoryMock_, create()).WillOnce(Return(queryChain_));
auto connectedAccessoriesEnumerator(std::make_shared<ConnectedAccessoriesEnumerator>(usbWrapperMock_, ioService_, queryChainFactoryMock_));
EXPECT_CALL(usbWrapperMock_, getDeviceList(_)).WillOnce(DoAll(SetArgReferee<0>(deviceListHandle_), Return(0)));
connectedAccessoriesEnumerator->enumerate(std::move(promise_));
EXPECT_CALL(usbWrapperMock_, open(*deviceList_.begin(), _)).WillOnce(DoAll(SetArgReferee<1>(deviceHandle_), Return(0)));
IAccessoryModeQueryChain::Promise::Pointer queryChainPromise;
EXPECT_CALL(queryChainMock_, start(deviceHandle_, _)).WillOnce(SaveArg<1>(&queryChainPromise));
ioService_.run();
ioService_.reset();
EXPECT_CALL(promiseHandlerMock_, onResolve(true));
EXPECT_CALL(promiseHandlerMock_, onReject(_)).Times(0);
queryChainPromise->resolve(deviceHandle_);
ioService_.run();
}
BOOST_FIXTURE_TEST_CASE(ConnectedAccessoriesEnumerator_SecondDeviceIsAOAPCapable, ConnectedAccessoriesEnumeratorUnitTest)
{
deviceList_.push_back(reinterpret_cast<libusb_device*>(1));
deviceList_.push_back(reinterpret_cast<libusb_device*>(2));
auto connectedAccessoriesEnumerator(std::make_shared<ConnectedAccessoriesEnumerator>(usbWrapperMock_, ioService_, queryChainFactoryMock_));
EXPECT_CALL(queryChainFactoryMock_, create()).Times(deviceList_.size()).WillRepeatedly(Return(queryChain_));
EXPECT_CALL(usbWrapperMock_, getDeviceList(_)).WillOnce(DoAll(SetArgReferee<0>(deviceListHandle_), Return(0)));
connectedAccessoriesEnumerator->enumerate(std::move(promise_));
EXPECT_CALL(usbWrapperMock_, open(*deviceList_.begin(), _)).WillOnce(DoAll(SetArgReferee<1>(deviceHandle_), Return(0)));
IAccessoryModeQueryChain::Promise::Pointer queryChainPromise;
EXPECT_CALL(queryChainMock_, start(deviceHandle_, _)).WillRepeatedly(SaveArg<1>(&queryChainPromise));
ioService_.run();
ioService_.reset();
EXPECT_CALL(promiseHandlerMock_, onResolve(_)).Times(0);
EXPECT_CALL(promiseHandlerMock_, onReject(_)).Times(0);
// open second device
USBWrapperMock::DummyDeviceHandle dummyDeviceHandle2;
DeviceHandle deviceHandle2(reinterpret_cast<libusb_device_handle*>(&dummyDeviceHandle2), [](auto*) {});
EXPECT_CALL(usbWrapperMock_, open(*(++deviceList_.begin()), _)).WillOnce(DoAll(SetArgReferee<1>(deviceHandle2), Return(0)));
IAccessoryModeQueryChain::Promise::Pointer queryChainPromise2;
EXPECT_CALL(queryChainMock_, start(deviceHandle2, _)).WillRepeatedly(SaveArg<1>(&queryChainPromise2));
queryChainPromise->reject(error::Error(error::ErrorCode::USB_AOAP_PROTOCOL_VERSION));
ioService_.run();
ioService_.reset();
EXPECT_CALL(promiseHandlerMock_, onResolve(true));
EXPECT_CALL(promiseHandlerMock_, onReject(_)).Times(0);
queryChainPromise2->resolve(deviceHandle2);
ioService_.run();
}
BOOST_FIXTURE_TEST_CASE(ConnectedAccessoriesEnumerator_NoAOAPCapableDevice, ConnectedAccessoriesEnumeratorUnitTest)
{
for(size_t i = 1; i < 1000; ++i)
{
deviceList_.push_back(reinterpret_cast<libusb_device*>(i));
}
EXPECT_CALL(usbWrapperMock_, getDeviceList(_)).WillOnce(DoAll(SetArgReferee<0>(deviceListHandle_), Return(0)));
EXPECT_CALL(queryChainFactoryMock_, create()).Times(deviceList_.size()).WillRepeatedly(Return(queryChain_));
auto connectedAccessoriesEnumerator(std::make_shared<ConnectedAccessoriesEnumerator>(usbWrapperMock_, ioService_, queryChainFactoryMock_));
connectedAccessoriesEnumerator->enumerate(std::move(promise_));
EXPECT_CALL(promiseHandlerMock_, onResolve(false));
EXPECT_CALL(promiseHandlerMock_, onReject(_)).Times(0);
for(const auto& device : deviceList_)
{
USBWrapperMock::DummyDeviceHandle dummyDeviceHandle;
DeviceHandle deviceHandle(reinterpret_cast<libusb_device_handle*>(&dummyDeviceHandle), [](auto*) {});
EXPECT_CALL(usbWrapperMock_, open(device, _)).WillOnce(DoAll(SetArgReferee<1>(deviceHandle), Return(0)));
IAccessoryModeQueryChain::Promise::Pointer queryChainPromise;
EXPECT_CALL(queryChainMock_, start(deviceHandle, _)).WillRepeatedly(SaveArg<1>(&queryChainPromise));
ioService_.run();
ioService_.reset();
queryChainPromise->reject(error::Error(error::ErrorCode::USB_AOAP_PROTOCOL_VERSION));
}
ioService_.run();
}
BOOST_FIXTURE_TEST_CASE(ConnectedAccessoriesEnumerator_GetDeviceListFailed, ConnectedAccessoriesEnumeratorUnitTest)
{
EXPECT_CALL(usbWrapperMock_, getDeviceList(_)).WillOnce(DoAll(SetArgReferee<0>(deviceListHandle_), Return(-1)));
EXPECT_CALL(promiseHandlerMock_, onResolve(_)).Times(0);
EXPECT_CALL(promiseHandlerMock_, onReject(error::Error(error::ErrorCode::USB_LIST_DEVICES)));
auto connectedAccessoriesEnumerator(std::make_shared<ConnectedAccessoriesEnumerator>(usbWrapperMock_, ioService_, queryChainFactoryMock_));
connectedAccessoriesEnumerator->enumerate(std::move(promise_));
ioService_.run();
}
BOOST_FIXTURE_TEST_CASE(ConnectedAccessoriesEnumerator_EmptyDevicesList, ConnectedAccessoriesEnumeratorUnitTest)
{
EXPECT_CALL(usbWrapperMock_, getDeviceList(_)).WillOnce(DoAll(SetArgReferee<0>(deviceListHandle_), Return(0)));
EXPECT_CALL(promiseHandlerMock_, onResolve(false));
EXPECT_CALL(promiseHandlerMock_, onReject(_)).Times(0);
auto connectedAccessoriesEnumerator(std::make_shared<ConnectedAccessoriesEnumerator>(usbWrapperMock_, ioService_, queryChainFactoryMock_));
connectedAccessoriesEnumerator->enumerate(std::move(promise_));
ioService_.run();
}
BOOST_FIXTURE_TEST_CASE(ConnectedAccessoriesEnumerator_OpenDeviceFailed, ConnectedAccessoriesEnumeratorUnitTest)
{
for(size_t i = 1; i < 1000; ++i)
{
deviceList_.push_back(reinterpret_cast<libusb_device*>(i));
}
EXPECT_CALL(usbWrapperMock_, getDeviceList(_)).WillOnce(DoAll(SetArgReferee<0>(deviceListHandle_), Return(0)));
auto connectedAccessoriesEnumerator(std::make_shared<ConnectedAccessoriesEnumerator>(usbWrapperMock_, ioService_, queryChainFactoryMock_));
connectedAccessoriesEnumerator->enumerate(std::move(promise_));
EXPECT_CALL(promiseHandlerMock_, onResolve(false));
EXPECT_CALL(promiseHandlerMock_, onReject(_)).Times(0);
for(const auto& device : deviceList_)
{
EXPECT_CALL(usbWrapperMock_, open(device, _)).WillOnce(DoAll(SetArgReferee<1>(nullptr), Return(0xFFF)));
}
ioService_.run();
}
}
}
}
}

View File

@ -159,7 +159,8 @@ void USBEndpoint::transferHandler(libusb_transfer *transfer)
}
else
{
promise->reject(error::Error(error::ErrorCode::USB_TRANSFER, transfer->status));
auto error = transfer->status == LIBUSB_TRANSFER_CANCELLED ? error::Error(error::ErrorCode::OPERATION_ABORTED) : error::Error(error::ErrorCode::USB_TRANSFER, transfer->status);
promise->reject(error);
}
self->usbWrapper_.freeTransfer(transfer);

View File

@ -283,7 +283,7 @@ BOOST_FIXTURE_TEST_CASE(USBEndpoint_BulkTransferFailed, USBEndpointUnitTest)
transferCallback(&transfer);
EXPECT_CALL(usbWrapperMock_, freeTransfer(&transfer));
EXPECT_CALL(promiseHandlerMock_, onReject(error::Error(error::ErrorCode::USB_TRANSFER, transfer.status))).Times(1);
EXPECT_CALL(promiseHandlerMock_, onReject(error::Error(error::ErrorCode::OPERATION_ABORTED))).Times(1);
EXPECT_CALL(promiseHandlerMock_, onResolve(_)).Times(0);
ioService_.run();
}