Transmits now block when the buffer fills
parent
5a6a1c990a
commit
6ffc364eba
|
|
@ -63,6 +63,7 @@ static constexpr const char* ERROR_FAILED_TO_READ = "A read operation failed.";
|
||||||
static constexpr const char* ERROR_FAILED_TO_WRITE = "A write operation failed.";
|
static constexpr const char* ERROR_FAILED_TO_WRITE = "A write operation failed.";
|
||||||
static constexpr const char* ERROR_DRIVER_FAILED_TO_OPEN = "The device driver encountered a low-level error while opening the device.";
|
static constexpr const char* ERROR_DRIVER_FAILED_TO_OPEN = "The device driver encountered a low-level error while opening the device.";
|
||||||
static constexpr const char* ERROR_PACKET_CHECKSUM_ERROR = "There was a checksum error while decoding a packet. The packet was dropped.";
|
static constexpr const char* ERROR_PACKET_CHECKSUM_ERROR = "There was a checksum error while decoding a packet. The packet was dropped.";
|
||||||
|
static constexpr const char* ERROR_TRANSMIT_BUFFER_FULL = "The transmit buffer is full and the device is set to non-blocking.";
|
||||||
static constexpr const char* ERROR_PCAP_COULD_NOT_START = "The PCAP driver could not be started. Ethernet devices will not be found.";
|
static constexpr const char* ERROR_PCAP_COULD_NOT_START = "The PCAP driver could not be started. Ethernet devices will not be found.";
|
||||||
static constexpr const char* ERROR_PCAP_COULD_NOT_FIND_DEVICES = "The PCAP driver failed to find devices. Ethernet devices will not be found.";
|
static constexpr const char* ERROR_PCAP_COULD_NOT_FIND_DEVICES = "The PCAP driver failed to find devices. Ethernet devices will not be found.";
|
||||||
|
|
||||||
|
|
@ -110,6 +111,8 @@ const char* APIError::DescriptionForType(ErrorType type) {
|
||||||
return ERROR_DRIVER_FAILED_TO_OPEN;
|
return ERROR_DRIVER_FAILED_TO_OPEN;
|
||||||
case PacketChecksumError:
|
case PacketChecksumError:
|
||||||
return ERROR_PACKET_CHECKSUM_ERROR;
|
return ERROR_PACKET_CHECKSUM_ERROR;
|
||||||
|
case TransmitBufferFull:
|
||||||
|
return ERROR_TRANSMIT_BUFFER_FULL;
|
||||||
case PCAPCouldNotStart:
|
case PCAPCouldNotStart:
|
||||||
return ERROR_PCAP_COULD_NOT_START;
|
return ERROR_PCAP_COULD_NOT_START;
|
||||||
case PCAPCouldNotFindDevices:
|
case PCAPCouldNotFindDevices:
|
||||||
|
|
@ -154,6 +157,7 @@ APIError::Severity APIError::SeverityForType(ErrorType type) {
|
||||||
case FailedToWrite:
|
case FailedToWrite:
|
||||||
case DriverFailedToOpen:
|
case DriverFailedToOpen:
|
||||||
case PacketChecksumError:
|
case PacketChecksumError:
|
||||||
|
case TransmitBufferFull:
|
||||||
// Other Errors
|
// Other Errors
|
||||||
case TooManyErrors:
|
case TooManyErrors:
|
||||||
case Unknown:
|
case Unknown:
|
||||||
|
|
|
||||||
|
|
@ -39,5 +39,16 @@ bool ICommunication::readWait(std::vector<uint8_t>& bytes, std::chrono::millisec
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ICommunication::write(const std::vector<uint8_t>& bytes) {
|
bool ICommunication::write(const std::vector<uint8_t>& bytes) {
|
||||||
|
if(writeBlocks) {
|
||||||
|
std::unique_lock<std::mutex> lk(writeMutex);
|
||||||
|
if(writeQueue.size_approx() > writeQueueSize) {
|
||||||
|
writeCV.wait(lk);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(writeQueue.size_approx() > writeQueueSize) {
|
||||||
|
err(APIError::TransmitBufferFull);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return writeQueue.enqueue(WriteOperation(bytes));
|
return writeQueue.enqueue(WriteOperation(bytes));
|
||||||
}
|
}
|
||||||
|
|
@ -54,6 +54,7 @@ public:
|
||||||
FailedToWrite = 0x3001,
|
FailedToWrite = 0x3001,
|
||||||
DriverFailedToOpen = 0x3002,
|
DriverFailedToOpen = 0x3002,
|
||||||
PacketChecksumError = 0x3003,
|
PacketChecksumError = 0x3003,
|
||||||
|
TransmitBufferFull = 0x3004,
|
||||||
PCAPCouldNotStart = 0x3102,
|
PCAPCouldNotStart = 0x3102,
|
||||||
PCAPCouldNotFindDevices = 0x3103,
|
PCAPCouldNotFindDevices = 0x3103,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,16 @@
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include "icsneo/api/errormanager.h"
|
||||||
#include "icsneo/third-party/concurrentqueue/blockingconcurrentqueue.h"
|
#include "icsneo/third-party/concurrentqueue/blockingconcurrentqueue.h"
|
||||||
|
|
||||||
namespace icsneo {
|
namespace icsneo {
|
||||||
|
|
||||||
class ICommunication {
|
class ICommunication {
|
||||||
public:
|
public:
|
||||||
|
ICommunication(const device_errorhandler_t& handler) : err(handler) {}
|
||||||
virtual ~ICommunication() {}
|
virtual ~ICommunication() {}
|
||||||
virtual bool open() = 0;
|
virtual bool open() = 0;
|
||||||
virtual bool isOpen() = 0;
|
virtual bool isOpen() = 0;
|
||||||
|
|
@ -18,12 +22,21 @@ public:
|
||||||
virtual bool read(std::vector<uint8_t>& bytes, size_t limit = 0);
|
virtual bool read(std::vector<uint8_t>& bytes, size_t limit = 0);
|
||||||
virtual bool readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout = std::chrono::milliseconds(100), size_t limit = 0);
|
virtual bool readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout = std::chrono::milliseconds(100), size_t limit = 0);
|
||||||
virtual bool write(const std::vector<uint8_t>& bytes);
|
virtual bool write(const std::vector<uint8_t>& bytes);
|
||||||
|
inline void onWrite() {
|
||||||
|
if(writeQueue.size_approx() < (writeQueueSize * 3/4))
|
||||||
|
writeCV.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
device_errorhandler_t err;
|
||||||
|
|
||||||
|
size_t writeQueueSize = 50;
|
||||||
|
bool writeBlocks = true; // Otherwise it just fails when the queue is full
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
class WriteOperation {
|
class WriteOperation {
|
||||||
public:
|
public:
|
||||||
WriteOperation() {}
|
WriteOperation() {}
|
||||||
WriteOperation(std::vector<uint8_t> b) { bytes = b; }
|
WriteOperation(const std::vector<uint8_t>& b) : bytes(b) {}
|
||||||
std::vector<uint8_t> bytes;
|
std::vector<uint8_t> bytes;
|
||||||
};
|
};
|
||||||
enum IOTaskState {
|
enum IOTaskState {
|
||||||
|
|
@ -34,6 +47,8 @@ protected:
|
||||||
virtual void writeTask() = 0;
|
virtual void writeTask() = 0;
|
||||||
moodycamel::BlockingConcurrentQueue<uint8_t> readQueue;
|
moodycamel::BlockingConcurrentQueue<uint8_t> readQueue;
|
||||||
moodycamel::BlockingConcurrentQueue<WriteOperation> writeQueue;
|
moodycamel::BlockingConcurrentQueue<WriteOperation> writeQueue;
|
||||||
|
std::mutex writeMutex;
|
||||||
|
std::condition_variable writeCV;
|
||||||
std::thread readThread, writeThread;
|
std::thread readThread, writeThread;
|
||||||
std::atomic<bool> closing{false};
|
std::atomic<bool> closing{false};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ class FTDI : public ICommunication {
|
||||||
public:
|
public:
|
||||||
static std::vector<neodevice_t> FindByProduct(int product);
|
static std::vector<neodevice_t> FindByProduct(int product);
|
||||||
|
|
||||||
FTDI(device_errorhandler_t err, neodevice_t& forDevice);
|
FTDI(const device_errorhandler_t& err, neodevice_t& forDevice);
|
||||||
~FTDI() { close(); }
|
~FTDI() { close(); }
|
||||||
bool open();
|
bool open();
|
||||||
bool close();
|
bool close();
|
||||||
|
|
@ -56,7 +56,6 @@ private:
|
||||||
bool openable; // Set to false in the constructor if the object has not been found in searchResultDevices
|
bool openable; // Set to false in the constructor if the object has not been found in searchResultDevices
|
||||||
|
|
||||||
neodevice_t& device;
|
neodevice_t& device;
|
||||||
device_errorhandler_t err;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ namespace icsneo {
|
||||||
|
|
||||||
class STM32 : public ICommunication {
|
class STM32 : public ICommunication {
|
||||||
public:
|
public:
|
||||||
STM32(device_errorhandler_t err, neodevice_t& forDevice) : device(forDevice), err(err) {}
|
STM32(const device_errorhandler_t& err, neodevice_t& forDevice) : ICommunication(err), device(forDevice) {}
|
||||||
static std::vector<neodevice_t> FindByProduct(int product);
|
static std::vector<neodevice_t> FindByProduct(int product);
|
||||||
|
|
||||||
bool open();
|
bool open();
|
||||||
|
|
@ -20,7 +20,6 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
neodevice_t& device;
|
neodevice_t& device;
|
||||||
device_errorhandler_t err;
|
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
static constexpr neodevice_handle_t HANDLE_OFFSET = 10;
|
static constexpr neodevice_handle_t HANDLE_OFFSET = 10;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ namespace icsneo {
|
||||||
|
|
||||||
class FTDI : public VCP {
|
class FTDI : public VCP {
|
||||||
public:
|
public:
|
||||||
FTDI(device_errorhandler_t err, neodevice_t& forDevice) : VCP(err, forDevice) {}
|
FTDI(const device_errorhandler_t& err, neodevice_t& forDevice) : VCP(err, forDevice) {}
|
||||||
static std::vector<neodevice_t> FindByProduct(int product) { return VCP::FindByProduct(product, { L"serenum" /*, L"ftdibus" */ }); }
|
static std::vector<neodevice_t> FindByProduct(int product) { return VCP::FindByProduct(product, { L"serenum" /*, L"ftdibus" */ }); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ public:
|
||||||
static std::string GetEthDevSerialFromMacAddress(uint8_t product, uint16_t macSerial);
|
static std::string GetEthDevSerialFromMacAddress(uint8_t product, uint16_t macSerial);
|
||||||
static bool IsHandleValid(neodevice_handle_t handle);
|
static bool IsHandleValid(neodevice_handle_t handle);
|
||||||
|
|
||||||
PCAP(device_errorhandler_t err, neodevice_t& forDevice);
|
PCAP(const device_errorhandler_t& err, neodevice_t& forDevice);
|
||||||
bool open();
|
bool open();
|
||||||
bool isOpen();
|
bool isOpen();
|
||||||
bool close();
|
bool close();
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ namespace icsneo {
|
||||||
|
|
||||||
class STM32 : public VCP {
|
class STM32 : public VCP {
|
||||||
public:
|
public:
|
||||||
STM32(device_errorhandler_t err, neodevice_t& forDevice) : VCP(err, forDevice) {}
|
STM32(const device_errorhandler_t& err, neodevice_t& forDevice) : VCP(err, forDevice) {}
|
||||||
static std::vector<neodevice_t> FindByProduct(int product) { return VCP::FindByProduct(product, { L"usbser" }); }
|
static std::vector<neodevice_t> FindByProduct(int product) { return VCP::FindByProduct(product, { L"usbser" }); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ public:
|
||||||
static bool IsHandleValid(neodevice_handle_t handle);
|
static bool IsHandleValid(neodevice_handle_t handle);
|
||||||
typedef void(*fn_boolCallback)(bool success);
|
typedef void(*fn_boolCallback)(bool success);
|
||||||
|
|
||||||
VCP(device_errorhandler_t err, neodevice_t& forDevice) : device(forDevice), err(err) {
|
VCP(const device_errorhandler_t& err, neodevice_t& forDevice) : ICommunication(err), device(forDevice) {
|
||||||
overlappedRead.hEvent = INVALID_HANDLE_VALUE;
|
overlappedRead.hEvent = INVALID_HANDLE_VALUE;
|
||||||
overlappedWrite.hEvent = INVALID_HANDLE_VALUE;
|
overlappedWrite.hEvent = INVALID_HANDLE_VALUE;
|
||||||
overlappedWait.hEvent = INVALID_HANDLE_VALUE;
|
overlappedWait.hEvent = INVALID_HANDLE_VALUE;
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ std::vector<neodevice_t> FTDI::FindByProduct(int product) {
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
FTDI::FTDI(device_errorhandler_t err, neodevice_t& forDevice) : device(forDevice), err(err) {
|
FTDI::FTDI(const device_errorhandler_t& err, neodevice_t& forDevice) : ICommunication(err), device(forDevice) {
|
||||||
openable = strlen(forDevice.serial) > 0 && device.handle >= 0 && device.handle < (neodevice_handle_t)handles.size();
|
openable = strlen(forDevice.serial) > 0 && device.handle >= 0 && device.handle < (neodevice_handle_t)handles.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -183,5 +183,6 @@ void FTDI::writeTask() {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ftdi.write(writeOp.bytes.data(), (int)writeOp.bytes.size());
|
ftdi.write(writeOp.bytes.data(), (int)writeOp.bytes.size());
|
||||||
|
onWrite();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -285,5 +285,6 @@ void STM32::writeTask() {
|
||||||
ssize_t actualWritten = ::write(fd, writeOp.bytes.data(), writeSize);
|
ssize_t actualWritten = ::write(fd, writeOp.bytes.data(), writeSize);
|
||||||
if(actualWritten != writeSize)
|
if(actualWritten != writeSize)
|
||||||
err(APIError::FailedToWrite);
|
err(APIError::FailedToWrite);
|
||||||
|
onWrite();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -177,7 +177,7 @@ bool PCAP::IsHandleValid(neodevice_handle_t handle) {
|
||||||
return (netifIndex < knownInterfaces.size());
|
return (netifIndex < knownInterfaces.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
PCAP::PCAP(device_errorhandler_t err, neodevice_t& forDevice) : device(forDevice), err(err) {
|
PCAP::PCAP(const device_errorhandler_t& err, neodevice_t& forDevice) : ICommunication(err), device(forDevice) {
|
||||||
if(IsHandleValid(device.handle)) {
|
if(IsHandleValid(device.handle)) {
|
||||||
interface = knownInterfaces[(device.handle >> 24) & 0xFF];
|
interface = knownInterfaces[(device.handle >> 24) & 0xFF];
|
||||||
interface.fp = nullptr; // We're going to open our own connection to the interface. This should already be nullptr but just in case.
|
interface.fp = nullptr; // We're going to open our own connection to the interface. This should already be nullptr but just in case.
|
||||||
|
|
@ -287,6 +287,7 @@ void PCAP::writeTask() {
|
||||||
auto bs = sendPacket.getBytestream();
|
auto bs = sendPacket.getBytestream();
|
||||||
if(!closing)
|
if(!closing)
|
||||||
pcap.sendpacket(interface.fp, bs.data(), (int)bs.size());
|
pcap.sendpacket(interface.fp, bs.data(), (int)bs.size());
|
||||||
|
onWrite();
|
||||||
// TODO Handle packet send errors
|
// TODO Handle packet send errors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -389,6 +389,8 @@ void VCP::writeTask() {
|
||||||
bytesWritten = 0;
|
bytesWritten = 0;
|
||||||
if(WriteFile(handle, writeOp.bytes.data(), (DWORD)writeOp.bytes.size(), nullptr, &overlappedWrite))
|
if(WriteFile(handle, writeOp.bytes.data(), (DWORD)writeOp.bytes.size(), nullptr, &overlappedWrite))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
onWrite();
|
||||||
|
|
||||||
auto winerr = GetLastError();
|
auto winerr = GetLastError();
|
||||||
if(winerr == ERROR_IO_PENDING) {
|
if(winerr == ERROR_IO_PENDING) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue