Allow disconnections to be signaled by drivers
This allows for disconnections to be detected quickly where possible. It also makes sure other driver errors aren't thrown in the event of a disconnection.pull/32/head
parent
dfe2d23d85
commit
a5b27a15b0
|
|
@ -43,7 +43,7 @@ void Communication::joinThreads() {
|
||||||
bool Communication::close() {
|
bool Communication::close() {
|
||||||
joinThreads();
|
joinThreads();
|
||||||
|
|
||||||
if(!isOpen()) {
|
if(!isOpen() && !isDisconnected()) {
|
||||||
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -55,6 +55,10 @@ bool Communication::isOpen() {
|
||||||
return driver->isOpen();
|
return driver->isOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Communication::isDisconnected() {
|
||||||
|
return driver->isDisconnected();
|
||||||
|
}
|
||||||
|
|
||||||
bool Communication::sendPacket(std::vector<uint8_t>& bytes) {
|
bool Communication::sendPacket(std::vector<uint8_t>& bytes) {
|
||||||
// This is here so that other communication types (like multichannel) can override it
|
// This is here so that other communication types (like multichannel) can override it
|
||||||
return rawWrite(bytes);
|
return rawWrite(bytes);
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,7 @@ bool Device::open() {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
// Check if we got a message, and if not, if settings are being applied
|
// Check if we got a message, and if not, if settings are being applied
|
||||||
if(!receivedMessage && !settings->applyingSettings) {
|
if(!receivedMessage && !settings->applyingSettings) {
|
||||||
if(!stopHeartbeatThread)
|
if(!stopHeartbeatThread && !isDisconnected())
|
||||||
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
|
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -316,6 +316,11 @@ bool Device::goOnline() {
|
||||||
bool Device::goOffline() {
|
bool Device::goOffline() {
|
||||||
forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onGoOffline(); return true; });
|
forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onGoOffline(); return true; });
|
||||||
|
|
||||||
|
if(isDisconnected()) {
|
||||||
|
online = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if(!com->sendCommand(Command::EnableNetworkCommunication, false))
|
if(!com->sendCommand(Command::EnableNetworkCommunication, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ public:
|
||||||
bool open();
|
bool open();
|
||||||
bool close();
|
bool close();
|
||||||
bool isOpen();
|
bool isOpen();
|
||||||
|
bool isDisconnected();
|
||||||
virtual void spawnThreads();
|
virtual void spawnThreads();
|
||||||
virtual void joinThreads();
|
virtual void joinThreads();
|
||||||
bool rawWrite(const std::vector<uint8_t>& bytes) { return driver->write(bytes); }
|
bool rawWrite(const std::vector<uint8_t>& bytes) { return driver->write(bytes); }
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ public:
|
||||||
virtual ~Driver() {}
|
virtual ~Driver() {}
|
||||||
virtual bool open() = 0;
|
virtual bool open() = 0;
|
||||||
virtual bool isOpen() = 0;
|
virtual bool isOpen() = 0;
|
||||||
|
virtual bool isDisconnected() { return disconnected; };
|
||||||
virtual bool close() = 0;
|
virtual bool close() = 0;
|
||||||
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);
|
||||||
|
|
@ -47,6 +48,7 @@ protected:
|
||||||
moodycamel::BlockingConcurrentQueue<WriteOperation> writeQueue;
|
moodycamel::BlockingConcurrentQueue<WriteOperation> writeQueue;
|
||||||
std::thread readThread, writeThread;
|
std::thread readThread, writeThread;
|
||||||
std::atomic<bool> closing{false};
|
std::atomic<bool> closing{false};
|
||||||
|
std::atomic<bool> disconnected{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ public:
|
||||||
virtual bool close();
|
virtual bool close();
|
||||||
virtual bool isOnline() const { return online; }
|
virtual bool isOnline() const { return online; }
|
||||||
virtual bool isOpen() const { return com->isOpen(); }
|
virtual bool isOpen() const { return com->isOpen(); }
|
||||||
|
virtual bool isDisconnected() const { return com->isDisconnected(); }
|
||||||
virtual bool goOnline();
|
virtual bool goOnline();
|
||||||
virtual bool goOffline();
|
virtual bool goOffline();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ public:
|
||||||
static std::vector<neodevice_t> FindByProduct(int product);
|
static std::vector<neodevice_t> FindByProduct(int product);
|
||||||
|
|
||||||
FTDI(const device_eventhandler_t& err, neodevice_t& forDevice);
|
FTDI(const device_eventhandler_t& err, neodevice_t& forDevice);
|
||||||
~FTDI() { close(); }
|
~FTDI() { if(isOpen()) close(); }
|
||||||
bool open();
|
bool open();
|
||||||
bool close();
|
bool close();
|
||||||
bool isOpen() { return ftdi.isOpen(); }
|
bool isOpen() { return ftdi.isOpen(); }
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ public:
|
||||||
* Other POSIX systems (BSDs, QNX, etc) will need bespoke code written in the future
|
* Other POSIX systems (BSDs, QNX, etc) will need bespoke code written in the future
|
||||||
*/
|
*/
|
||||||
STM32(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err), device(forDevice) {}
|
STM32(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err), device(forDevice) {}
|
||||||
|
~STM32() { if(isOpen()) close(); }
|
||||||
static std::vector<neodevice_t> FindByProduct(int product);
|
static std::vector<neodevice_t> FindByProduct(int product);
|
||||||
|
|
||||||
bool open();
|
bool open();
|
||||||
|
|
@ -38,6 +39,7 @@ private:
|
||||||
|
|
||||||
void readTask();
|
void readTask();
|
||||||
void writeTask();
|
void writeTask();
|
||||||
|
bool fdIsValid();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include "libusb-1.0/libusb.h"
|
||||||
#include "icsneo/platform/ftdi.h"
|
#include "icsneo/platform/ftdi.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -81,7 +82,7 @@ bool FTDI::open() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FTDI::close() {
|
bool FTDI::close() {
|
||||||
if(!isOpen()) {
|
if(!isOpen() && !isDisconnected()) {
|
||||||
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -94,9 +95,12 @@ bool FTDI::close() {
|
||||||
if(writeThread.joinable())
|
if(writeThread.joinable())
|
||||||
writeThread.join();
|
writeThread.join();
|
||||||
|
|
||||||
bool ret = ftdi.closeDevice();
|
bool ret = true;
|
||||||
if(!ret)
|
if(!isDisconnected()) {
|
||||||
report(APIEvent::Type::DriverFailedToClose, APIEvent::Severity::Error);
|
ret = ftdi.closeDevice();
|
||||||
|
if(!ret)
|
||||||
|
report(APIEvent::Type::DriverFailedToClose, APIEvent::Severity::Error);
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t flush;
|
uint8_t flush;
|
||||||
WriteOperation flushop;
|
WriteOperation flushop;
|
||||||
|
|
@ -184,11 +188,17 @@ void FTDI::readTask() {
|
||||||
constexpr size_t READ_BUFFER_SIZE = 8;
|
constexpr size_t READ_BUFFER_SIZE = 8;
|
||||||
uint8_t readbuf[READ_BUFFER_SIZE];
|
uint8_t readbuf[READ_BUFFER_SIZE];
|
||||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||||
while(!closing) {
|
while(!closing && !isDisconnected()) {
|
||||||
auto readBytes = ftdi.read(readbuf, READ_BUFFER_SIZE);
|
auto readBytes = ftdi.read(readbuf, READ_BUFFER_SIZE);
|
||||||
if(readBytes < 0)
|
if(readBytes < 0) {
|
||||||
report(APIEvent::Type::FailedToRead, APIEvent::Severity::EventWarning);
|
if(readBytes == LIBUSB_ERROR_NO_DEVICE || readBytes == LIBUSB_ERROR_PIPE) {
|
||||||
else
|
if(!isDisconnected()) {
|
||||||
|
disconnected = true;
|
||||||
|
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
report(APIEvent::Type::FailedToRead, APIEvent::Severity::EventWarning);
|
||||||
|
} else
|
||||||
readQueue.enqueue_bulk(readbuf, readBytes);
|
readQueue.enqueue_bulk(readbuf, readBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -196,16 +206,23 @@ void FTDI::readTask() {
|
||||||
void FTDI::writeTask() {
|
void FTDI::writeTask() {
|
||||||
WriteOperation writeOp;
|
WriteOperation writeOp;
|
||||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||||
while(!closing) {
|
while(!closing && !isDisconnected()) {
|
||||||
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
|
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
while(offset < writeOp.bytes.size()) {
|
while(offset < writeOp.bytes.size()) {
|
||||||
auto writeBytes = ftdi.write(writeOp.bytes.data() + offset, (int)writeOp.bytes.size() - offset);
|
auto writeBytes = ftdi.write(writeOp.bytes.data() + offset, (int)writeOp.bytes.size() - offset);
|
||||||
if(writeBytes < 0)
|
if(writeBytes < 0) {
|
||||||
report(APIEvent::Type::FailedToWrite, APIEvent::Severity::EventWarning);
|
if(writeBytes == LIBUSB_ERROR_NO_DEVICE || writeBytes == LIBUSB_ERROR_PIPE) {
|
||||||
else
|
if(!isDisconnected()) {
|
||||||
|
disconnected = true;
|
||||||
|
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
report(APIEvent::Type::FailedToWrite, APIEvent::Severity::EventWarning);
|
||||||
|
} else
|
||||||
offset += writeBytes;
|
offset += writeBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,13 @@ bool STM32::open() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fd = ::open(ttyPath.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
|
// Some devices can take a while to boot
|
||||||
|
for(int i = 0; i != 50; ++i) {
|
||||||
|
fd = ::open(ttyPath.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
|
||||||
|
if(fd != -1)
|
||||||
|
break;
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
}
|
||||||
if(!isOpen()) {
|
if(!isOpen()) {
|
||||||
//std::cout << "Open of " << ttyPath.c_str() << " failed with " << strerror(errno) << ' ';
|
//std::cout << "Open of " << ttyPath.c_str() << " failed with " << strerror(errno) << ' ';
|
||||||
report(APIEvent::Type::DriverFailedToOpen, APIEvent::Severity::Error);
|
report(APIEvent::Type::DriverFailedToOpen, APIEvent::Severity::Error);
|
||||||
|
|
@ -82,7 +88,7 @@ bool STM32::isOpen() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool STM32::close() {
|
bool STM32::close() {
|
||||||
if(!isOpen()) {
|
if(!isOpen() && !isDisconnected()) {
|
||||||
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -117,7 +123,7 @@ void STM32::readTask() {
|
||||||
constexpr size_t READ_BUFFER_SIZE = 2048;
|
constexpr size_t READ_BUFFER_SIZE = 2048;
|
||||||
uint8_t readbuf[READ_BUFFER_SIZE];
|
uint8_t readbuf[READ_BUFFER_SIZE];
|
||||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||||
while(!closing) {
|
while(!closing && !isDisconnected()) {
|
||||||
fd_set rfds = {0};
|
fd_set rfds = {0};
|
||||||
struct timeval tv = {0};
|
struct timeval tv = {0};
|
||||||
FD_SET(fd, &rfds);
|
FD_SET(fd, &rfds);
|
||||||
|
|
@ -126,19 +132,37 @@ void STM32::readTask() {
|
||||||
auto bytesRead = ::read(fd, readbuf, READ_BUFFER_SIZE);
|
auto bytesRead = ::read(fd, readbuf, READ_BUFFER_SIZE);
|
||||||
if(bytesRead > 0)
|
if(bytesRead > 0)
|
||||||
readQueue.enqueue_bulk(readbuf, bytesRead);
|
readQueue.enqueue_bulk(readbuf, bytesRead);
|
||||||
|
else {
|
||||||
|
if(!fdIsValid() && !isDisconnected()) {
|
||||||
|
disconnected = true;
|
||||||
|
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void STM32::writeTask() {
|
void STM32::writeTask() {
|
||||||
WriteOperation writeOp;
|
WriteOperation writeOp;
|
||||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||||
while(!closing) {
|
while(!closing && !isDisconnected()) {
|
||||||
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
|
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const ssize_t writeSize = (ssize_t)writeOp.bytes.size();
|
const ssize_t writeSize = (ssize_t)writeOp.bytes.size();
|
||||||
ssize_t actualWritten = ::write(fd, writeOp.bytes.data(), writeSize);
|
ssize_t actualWritten = ::write(fd, writeOp.bytes.data(), writeSize);
|
||||||
if(actualWritten != writeSize)
|
if(actualWritten != writeSize) {
|
||||||
report(APIEvent::Type::FailedToWrite, APIEvent::Severity::Error);
|
if(!fdIsValid()) {
|
||||||
|
if(!isDisconnected()) {
|
||||||
|
disconnected = true;
|
||||||
|
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
report(APIEvent::Type::FailedToWrite, APIEvent::Severity::Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool STM32::fdIsValid() {
|
||||||
|
struct termios tty = {};
|
||||||
|
return tcgetattr(fd, &tty) == 0 ? true : false;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -346,7 +346,7 @@ void VCP::readTask() {
|
||||||
IOTaskState state = LAUNCH;
|
IOTaskState state = LAUNCH;
|
||||||
DWORD bytesRead = 0;
|
DWORD bytesRead = 0;
|
||||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||||
while(!closing) {
|
while(!closing && !isDisconnected()) {
|
||||||
switch(state) {
|
switch(state) {
|
||||||
case LAUNCH: {
|
case LAUNCH: {
|
||||||
COMSTAT comStatus;
|
COMSTAT comStatus;
|
||||||
|
|
@ -365,8 +365,15 @@ void VCP::readTask() {
|
||||||
auto lastError = GetLastError();
|
auto lastError = GetLastError();
|
||||||
if(lastError == ERROR_IO_PENDING)
|
if(lastError == ERROR_IO_PENDING)
|
||||||
state = WAIT;
|
state = WAIT;
|
||||||
else if(lastError != ERROR_SUCCESS)
|
else if(lastError != ERROR_SUCCESS) {
|
||||||
report(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
|
if(lastError == ERROR_ACCESS_DENIED) {
|
||||||
|
if(!isDisconnected()) {
|
||||||
|
disconnected = true;
|
||||||
|
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
report(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case WAIT: {
|
case WAIT: {
|
||||||
|
|
@ -392,7 +399,7 @@ void VCP::writeTask() {
|
||||||
VCP::WriteOperation writeOp;
|
VCP::WriteOperation writeOp;
|
||||||
DWORD bytesWritten = 0;
|
DWORD bytesWritten = 0;
|
||||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||||
while(!closing) {
|
while(!closing && !isDisconnected()) {
|
||||||
switch(state) {
|
switch(state) {
|
||||||
case LAUNCH: {
|
case LAUNCH: {
|
||||||
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
|
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
|
||||||
|
|
@ -406,7 +413,12 @@ void VCP::writeTask() {
|
||||||
if(winerr == ERROR_IO_PENDING) {
|
if(winerr == ERROR_IO_PENDING) {
|
||||||
state = WAIT;
|
state = WAIT;
|
||||||
}
|
}
|
||||||
else
|
else if(winerr == ERROR_ACCESS_DENIED) {
|
||||||
|
if(!isDisconnected()) {
|
||||||
|
disconnected = true;
|
||||||
|
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
|
||||||
|
}
|
||||||
|
} else
|
||||||
report(APIEvent::Type::FailedToWrite, APIEvent::Severity::Error);
|
report(APIEvent::Type::FailedToWrite, APIEvent::Severity::Error);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue