Detect device disconnects
When a device is sending any traffic, the device is considered to be connected. If no traffic if being received from the device, a status is requested. If the device fails to report the status back in a timely manner, it is considered to be disconnected. If the device fails to reply to the status request, it is important to confirm that the device is not applying settings. While the device is applying settings, it will not be sending heartbeats or able to process a status request.pull/25/head
parent
5db07102aa
commit
044c2bb86f
|
|
@ -93,6 +93,7 @@ static constexpr const char* NO_DEVICE_RESPONSE = "Expected a response from the
|
||||||
static constexpr const char* MESSAGE_FORMATTING = "The message was not properly formed.";
|
static constexpr const char* MESSAGE_FORMATTING = "The message was not properly formed.";
|
||||||
static constexpr const char* CANFD_NOT_SUPPORTED = "This device does not support CANFD.";
|
static constexpr const char* CANFD_NOT_SUPPORTED = "This device does not support CANFD.";
|
||||||
static constexpr const char* RTR_NOT_SUPPORTED = "RTR is not supported with CANFD.";
|
static constexpr const char* RTR_NOT_SUPPORTED = "RTR is not supported with CANFD.";
|
||||||
|
static constexpr const char* DEVICE_DISCONNECTED = "The device was disconnected.";
|
||||||
|
|
||||||
// Transport Errors
|
// Transport Errors
|
||||||
static constexpr const char* FAILED_TO_READ = "A read operation failed.";
|
static constexpr const char* FAILED_TO_READ = "A read operation failed.";
|
||||||
|
|
@ -184,6 +185,8 @@ const char* APIEvent::DescriptionForType(Type type) {
|
||||||
return CANFD_NOT_SUPPORTED;
|
return CANFD_NOT_SUPPORTED;
|
||||||
case Type::RTRNotSupported:
|
case Type::RTRNotSupported:
|
||||||
return RTR_NOT_SUPPORTED;
|
return RTR_NOT_SUPPORTED;
|
||||||
|
case Type::DeviceDisconnected:
|
||||||
|
return DEVICE_DISCONNECTED;
|
||||||
|
|
||||||
// Transport Errors
|
// Transport Errors
|
||||||
case Type::FailedToRead:
|
case Type::FailedToRead:
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,32 @@ bool Device::open() {
|
||||||
handleInternalMessage(message);
|
handleInternalMessage(message);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
std::atomic<bool> receivedMessage{false};
|
||||||
|
messageReceivedCallbackID = com->addMessageCallback(MessageCallback(filter, [&](std::shared_ptr<Message> message) {
|
||||||
|
receivedMessage = true;
|
||||||
|
}));
|
||||||
|
|
||||||
|
heartbeatThread = std::thread([&]() {
|
||||||
|
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||||
|
while(true) {
|
||||||
|
// Wait for 110ms for a possible heartbeat
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(110));
|
||||||
|
if(!receivedMessage) {
|
||||||
|
// No heartbeat received, request a status
|
||||||
|
com->sendCommand(Command::RequestStatusUpdate);
|
||||||
|
// The response should come back quickly if the com is quiet
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
// Check if we got a message, and if not, if settings are being applied
|
||||||
|
if(!receivedMessage && !settings->applyingSettings) {
|
||||||
|
if(!stopHeartbeatThread)
|
||||||
|
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
receivedMessage = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onDeviceOpen(); return true; });
|
forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onDeviceOpen(); return true; });
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -215,12 +241,17 @@ bool Device::close() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stopHeartbeatThread = true;
|
||||||
|
|
||||||
if(isOnline())
|
if(isOnline())
|
||||||
goOffline();
|
goOffline();
|
||||||
|
|
||||||
if(internalHandlerCallbackID)
|
if(internalHandlerCallbackID)
|
||||||
com->removeMessageCallback(internalHandlerCallbackID);
|
com->removeMessageCallback(internalHandlerCallbackID);
|
||||||
|
|
||||||
|
if(messageReceivedCallbackID)
|
||||||
|
com->removeMessageCallback(messageReceivedCallbackID);
|
||||||
|
|
||||||
internalHandlerCallbackID = 0;
|
internalHandlerCallbackID = 0;
|
||||||
|
|
||||||
forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onDeviceClose(); return true; });
|
forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onDeviceClose(); return true; });
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,8 @@ bool IDeviceSettings::apply(bool temporary) {
|
||||||
bytestream[6] = (uint8_t)(gs_checksum >> 8);
|
bytestream[6] = (uint8_t)(gs_checksum >> 8);
|
||||||
memcpy(bytestream.data() + 7, getMutableRawStructurePointer(), settings.size());
|
memcpy(bytestream.data() + 7, getMutableRawStructurePointer(), settings.size());
|
||||||
|
|
||||||
|
// Pause I/O with the device while the settings are applied
|
||||||
|
applyingSettings = true;
|
||||||
|
|
||||||
std::shared_ptr<Message> msg = com->waitForMessageSync([this, &bytestream]() {
|
std::shared_ptr<Message> msg = com->waitForMessageSync([this, &bytestream]() {
|
||||||
return com->sendCommand(Command::SetSettings, bytestream);
|
return com->sendCommand(Command::SetSettings, bytestream);
|
||||||
|
|
@ -250,6 +252,8 @@ bool IDeviceSettings::apply(bool temporary) {
|
||||||
}, Main51MessageFilter(Command::SaveSettings), std::chrono::milliseconds(5000));
|
}, Main51MessageFilter(Command::SaveSettings), std::chrono::milliseconds(5000));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applyingSettings = false;
|
||||||
|
|
||||||
refresh(); // Refresh our buffer with what the device has, whether we were successful or not
|
refresh(); // Refresh our buffer with what the device has, whether we were successful or not
|
||||||
|
|
||||||
bool ret = (msg && msg->data[0] == 1); // Device sends 0x01 for success
|
bool ret = (msg && msg->data[0] == 1); // Device sends 0x01 for success
|
||||||
|
|
@ -270,6 +274,8 @@ bool IDeviceSettings::applyDefaults(bool temporary) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applyingSettings = true;
|
||||||
|
|
||||||
std::shared_ptr<Message> msg = com->waitForMessageSync([this]() {
|
std::shared_ptr<Message> msg = com->waitForMessageSync([this]() {
|
||||||
return com->sendCommand(Command::SetDefaultSettings);
|
return com->sendCommand(Command::SetDefaultSettings);
|
||||||
}, Main51MessageFilter(Command::SetDefaultSettings), std::chrono::milliseconds(1000));
|
}, Main51MessageFilter(Command::SetDefaultSettings), std::chrono::milliseconds(1000));
|
||||||
|
|
@ -317,6 +323,8 @@ bool IDeviceSettings::applyDefaults(bool temporary) {
|
||||||
return com->sendCommand(Command::SaveSettings);
|
return com->sendCommand(Command::SaveSettings);
|
||||||
}, Main51MessageFilter(Command::SaveSettings), std::chrono::milliseconds(5000));
|
}, Main51MessageFilter(Command::SaveSettings), std::chrono::milliseconds(5000));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applyingSettings = false;
|
||||||
|
|
||||||
refresh(); // Refresh our buffer with what the device has, whether we were successful or not
|
refresh(); // Refresh our buffer with what the device has, whether we were successful or not
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ public:
|
||||||
MessageFormattingError = 0x2019,
|
MessageFormattingError = 0x2019,
|
||||||
CANFDNotSupported = 0x2020,
|
CANFDNotSupported = 0x2020,
|
||||||
RTRNotSupported = 0x2021,
|
RTRNotSupported = 0x2021,
|
||||||
|
DeviceDisconnected = 0x2022,
|
||||||
|
|
||||||
// Transport Events
|
// Transport Events
|
||||||
FailedToRead = 0x3000,
|
FailedToRead = 0x3000,
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <atomic>
|
||||||
#include "icsneo/api/eventmanager.h"
|
#include "icsneo/api/eventmanager.h"
|
||||||
#include "icsneo/device/neodevice.h"
|
#include "icsneo/device/neodevice.h"
|
||||||
#include "icsneo/device/idevicesettings.h"
|
#include "icsneo/device/idevicesettings.h"
|
||||||
|
|
@ -30,6 +31,8 @@ public:
|
||||||
if(isMessagePollingEnabled())
|
if(isMessagePollingEnabled())
|
||||||
disableMessagePolling();
|
disableMessagePolling();
|
||||||
close();
|
close();
|
||||||
|
if(heartbeatThread.joinable())
|
||||||
|
heartbeatThread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string SerialNumToString(uint32_t serial);
|
static std::string SerialNumToString(uint32_t serial);
|
||||||
|
|
@ -100,6 +103,7 @@ protected:
|
||||||
bool online = false;
|
bool online = false;
|
||||||
int messagePollingCallbackID = 0;
|
int messagePollingCallbackID = 0;
|
||||||
int internalHandlerCallbackID = 0;
|
int internalHandlerCallbackID = 0;
|
||||||
|
int messageReceivedCallbackID = 0;
|
||||||
device_eventhandler_t report;
|
device_eventhandler_t report;
|
||||||
|
|
||||||
// START Initialization Functions
|
// START Initialization Functions
|
||||||
|
|
@ -211,6 +215,9 @@ private:
|
||||||
size_t pollingMessageLimit = 20000;
|
size_t pollingMessageLimit = 20000;
|
||||||
moodycamel::BlockingConcurrentQueue<std::shared_ptr<Message>> pollingContainer;
|
moodycamel::BlockingConcurrentQueue<std::shared_ptr<Message>> pollingContainer;
|
||||||
void enforcePollingMessageLimit();
|
void enforcePollingMessageLimit();
|
||||||
|
|
||||||
|
std::atomic<bool> stopHeartbeatThread{false};
|
||||||
|
std::thread heartbeatThread;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -324,6 +324,7 @@ static_assert(sizeof(UART_SETTINGS) == UART_SETTINGS_SIZE, "UART_SETTINGS is the
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
#include "icsneo/communication/communication.h"
|
#include "icsneo/communication/communication.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
namespace icsneo {
|
namespace icsneo {
|
||||||
|
|
||||||
|
|
@ -404,6 +405,8 @@ public:
|
||||||
|
|
||||||
bool readonly = false;
|
bool readonly = false;
|
||||||
bool disableGSChecksumming = false;
|
bool disableGSChecksumming = false;
|
||||||
|
|
||||||
|
std::atomic<bool> applyingSettings{false};
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<Communication> com;
|
std::shared_ptr<Communication> com;
|
||||||
device_eventhandler_t report;
|
device_eventhandler_t report;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue