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
Kyle Schwarz 2020-10-08 17:38:10 -04:00 committed by Paul Hollinsky
parent dfe2d23d85
commit a5b27a15b0
10 changed files with 95 additions and 27 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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); }

View File

@ -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};
}; };
} }

View File

@ -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();

View File

@ -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(); }

View File

@ -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();
}; };
} }

View File

@ -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;
} }

View File

@ -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;
}

View File

@ -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;