Add error system
parent
a331a2afa8
commit
3a42372dcd
|
|
@ -59,6 +59,8 @@ set(SRC_FILES ${COMMON_SRC} ${PLATFORM_SRC})
|
||||||
|
|
||||||
add_library(icsneocpp
|
add_library(icsneocpp
|
||||||
api/icsneocpp/icsneocpp.cpp
|
api/icsneocpp/icsneocpp.cpp
|
||||||
|
api/icsneocpp/error.cpp
|
||||||
|
api/icsneocpp/errormanager.cpp
|
||||||
${SRC_FILES}
|
${SRC_FILES}
|
||||||
)
|
)
|
||||||
target_include_directories(icsneocpp
|
target_include_directories(icsneocpp
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#include "icsneo/icsneoc.h"
|
#include "icsneo/icsneoc.h"
|
||||||
#include "icsneo/icsneocpp.h"
|
#include "icsneo/icsneocpp.h"
|
||||||
#include "icsneo/platform/dynamiclib.h"
|
#include "icsneo/platform/dynamiclib.h"
|
||||||
|
#include "icsneo/api/errormanager.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
@ -25,8 +26,10 @@ static std::map<devicehandle_t, std::vector<std::shared_ptr<Message>>> polledMes
|
||||||
void icsneo_findAllDevices(neodevice_t* devices, size_t* count) {
|
void icsneo_findAllDevices(neodevice_t* devices, size_t* count) {
|
||||||
std::vector<std::shared_ptr<Device>> foundDevices = icsneo::FindAllDevices();
|
std::vector<std::shared_ptr<Device>> foundDevices = icsneo::FindAllDevices();
|
||||||
|
|
||||||
if(count == nullptr)
|
if(count == nullptr) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::RequiredParameterNull);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(devices == nullptr) {
|
if(devices == nullptr) {
|
||||||
*count = foundDevices.size();
|
*count = foundDevices.size();
|
||||||
|
|
@ -39,7 +42,7 @@ void icsneo_findAllDevices(neodevice_t* devices, size_t* count) {
|
||||||
*count = foundDevices.size();
|
*count = foundDevices.size();
|
||||||
size_t outputSize = *count;
|
size_t outputSize = *count;
|
||||||
if(outputSize > inputSize) {
|
if(outputSize > inputSize) {
|
||||||
// TODO an error should be returned that the data was truncated
|
ErrorManager::GetInstance().add(APIError::OutputTruncated);
|
||||||
outputSize = inputSize;
|
outputSize = inputSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,9 +57,22 @@ void icsneo_freeUnconnectedDevices() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_serialNumToString(uint32_t num, char* str, size_t* count) {
|
bool icsneo_serialNumToString(uint32_t num, char* str, size_t* count) {
|
||||||
|
// TAG String copy function
|
||||||
|
if(count == nullptr) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::RequiredParameterNull);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto result = Device::SerialNumToString(num);
|
auto result = Device::SerialNumToString(num);
|
||||||
|
|
||||||
|
if(str == nullptr) {
|
||||||
|
*count = result.length() + 1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(*count < result.length()) {
|
if(*count < result.length()) {
|
||||||
*count = result.length() + 1; // This is how big of a buffer we need
|
*count = result.length() + 1; // This is how big of a buffer we need
|
||||||
|
ErrorManager::GetInstance().add(APIError::BufferInsufficient);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,8 +99,10 @@ bool icsneo_isValidNeoDevice(const neodevice_t* device) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_openDevice(const neodevice_t* device) {
|
bool icsneo_openDevice(const neodevice_t* device) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(!device->device->open())
|
if(!device->device->open())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -104,8 +122,10 @@ bool icsneo_openDevice(const neodevice_t* device) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_closeDevice(const neodevice_t* device) {
|
bool icsneo_closeDevice(const neodevice_t* device) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(!device->device->close())
|
if(!device->device->close())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -123,47 +143,61 @@ bool icsneo_closeDevice(const neodevice_t* device) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_goOnline(const neodevice_t* device) {
|
bool icsneo_goOnline(const neodevice_t* device) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return device->device->goOnline();
|
return device->device->goOnline();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_goOffline(const neodevice_t* device) {
|
bool icsneo_goOffline(const neodevice_t* device) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return device->device->goOffline();
|
return device->device->goOffline();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_isOnline(const neodevice_t* device) {
|
bool icsneo_isOnline(const neodevice_t* device) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return device->device->isOnline();
|
return device->device->isOnline();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_enableMessagePolling(const neodevice_t* device) {
|
bool icsneo_enableMessagePolling(const neodevice_t* device) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
device->device->enableMessagePolling();
|
device->device->enableMessagePolling();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_disableMessagePolling(const neodevice_t* device) {
|
bool icsneo_disableMessagePolling(const neodevice_t* device) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return device->device->disableMessagePolling();
|
return device->device->disableMessagePolling();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_getMessages(const neodevice_t* device, neomessage_t* messages, size_t* items) {
|
bool icsneo_getMessages(const neodevice_t* device, neomessage_t* messages, size_t* items) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(items == nullptr)
|
if(items == nullptr) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::RequiredParameterNull);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(messages == nullptr) {
|
if(messages == nullptr) {
|
||||||
// A NULL value for messages means the user wants the current size of the buffer into items
|
// A NULL value for messages means the user wants the current size of the buffer into items
|
||||||
|
|
@ -189,79 +223,117 @@ bool icsneo_getMessages(const neodevice_t* device, neomessage_t* messages, size_
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t icsneo_getPollingMessageLimit(const neodevice_t* device) {
|
size_t icsneo_getPollingMessageLimit(const neodevice_t* device) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return device->device->getPollingMessageLimit();
|
return device->device->getPollingMessageLimit();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_setPollingMessageLimit(const neodevice_t* device, size_t newLimit) {
|
bool icsneo_setPollingMessageLimit(const neodevice_t* device, size_t newLimit) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
device->device->setPollingMessageLimit(newLimit);
|
device->device->setPollingMessageLimit(newLimit);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_getProductName(const neodevice_t* device, char* str, size_t* maxLength) {
|
bool icsneo_getProductName(const neodevice_t* device, char* str, size_t* maxLength) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
// TAG String copy function
|
||||||
|
if(maxLength == nullptr) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::RequiredParameterNull);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
*maxLength = device->device->getType().toString().copy(str, *maxLength);
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string output = device->device->getType().toString();
|
||||||
|
|
||||||
|
if(str == nullptr) {
|
||||||
|
*maxLength = output.length();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*maxLength = output.copy(str, *maxLength);
|
||||||
str[*maxLength] = '\0';
|
str[*maxLength] = '\0';
|
||||||
|
|
||||||
|
if(output.length() > *maxLength)
|
||||||
|
ErrorManager::GetInstance().add(APIError::OutputTruncated);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_settingsRefresh(const neodevice_t* device) {
|
bool icsneo_settingsRefresh(const neodevice_t* device) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return device->device->settings->refresh();
|
return device->device->settings->refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_settingsApply(const neodevice_t* device) {
|
bool icsneo_settingsApply(const neodevice_t* device) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return device->device->settings->apply();
|
return device->device->settings->apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_settingsApplyTemporary(const neodevice_t* device) {
|
bool icsneo_settingsApplyTemporary(const neodevice_t* device) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return device->device->settings->apply(true);
|
return device->device->settings->apply(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_settingsApplyDefaults(const neodevice_t* device) {
|
bool icsneo_settingsApplyDefaults(const neodevice_t* device) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return device->device->settings->applyDefaults();
|
return device->device->settings->applyDefaults();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_settingsApplyDefaultsTemporary(const neodevice_t* device) {
|
bool icsneo_settingsApplyDefaultsTemporary(const neodevice_t* device) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return device->device->settings->applyDefaults(true);
|
return device->device->settings->applyDefaults(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_setBaudrate(const neodevice_t* device, uint16_t netid, uint32_t newBaudrate) {
|
bool icsneo_setBaudrate(const neodevice_t* device, uint16_t netid, uint32_t newBaudrate) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return device->device->settings->setBaudrateFor(netid, newBaudrate);
|
return device->device->settings->setBaudrateFor(netid, newBaudrate);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_transmit(const neodevice_t* device, const neomessage_t* message) {
|
bool icsneo_transmit(const neodevice_t* device, const neomessage_t* message) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return device->device->transmit(CreateMessageFromNeoMessage(message));
|
return device->device->transmit(CreateMessageFromNeoMessage(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_transmitMessages(const neodevice_t* device, const neomessage_t* messages, size_t count) {
|
bool icsneo_transmitMessages(const neodevice_t* device, const neomessage_t* messages, size_t count) {
|
||||||
|
// Transmit implements neodevice_t check so it is not needed here
|
||||||
// TODO This can be implemented faster
|
// TODO This can be implemented faster
|
||||||
for(size_t i = 0; i < count; i++) {
|
for(size_t i = 0; i < count; i++) {
|
||||||
if(!icsneo_transmit(device, messages + i))
|
if(!icsneo_transmit(device, messages + i))
|
||||||
|
|
@ -271,10 +343,24 @@ bool icsneo_transmitMessages(const neodevice_t* device, const neomessage_t* mess
|
||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_describeDevice(const neodevice_t* device, char* str, size_t* maxLength) {
|
bool icsneo_describeDevice(const neodevice_t* device, char* str, size_t* maxLength) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
// TAG String copy function
|
||||||
|
if(maxLength == nullptr) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::RequiredParameterNull);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
*maxLength = device->device->describe().copy(str, *maxLength);
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
ErrorManager::GetInstance().add(APIError::InvalidNeoDevice);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string output = device->device->describe();
|
||||||
|
|
||||||
|
*maxLength = output.copy(str, *maxLength);
|
||||||
str[*maxLength] = '\0';
|
str[*maxLength] = '\0';
|
||||||
|
|
||||||
|
if(output.length() > *maxLength)
|
||||||
|
ErrorManager::GetInstance().add(APIError::OutputTruncated);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
#include "icsneo/api/error.h"
|
||||||
|
#include "icsneo/device/device.h"
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
APIError::APIError(ErrorType error) : errorStruct({}) {
|
||||||
|
init(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
APIError::APIError(ErrorType error, const Device* forDevice) : errorStruct({}) {
|
||||||
|
device = forDevice;
|
||||||
|
serial = device->getSerial();
|
||||||
|
errorStruct.serial[serial.copy(errorStruct.serial, sizeof(errorStruct.serial))] = '\0';
|
||||||
|
|
||||||
|
init(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APIError::init(ErrorType error) {
|
||||||
|
timepoint = std::chrono::high_resolution_clock::now();
|
||||||
|
errorStruct.description = DescriptionForType(error);
|
||||||
|
errorStruct.errorNumber = (uint32_t)error;
|
||||||
|
errorStruct.severity = (uint8_t)SeverityForType(error);
|
||||||
|
errorStruct.timestamp = std::chrono::high_resolution_clock::to_time_t(timepoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string APIError::describe() const noexcept {
|
||||||
|
std::stringstream ss;
|
||||||
|
if(device)
|
||||||
|
ss << *device; // Makes use of device.describe()
|
||||||
|
else
|
||||||
|
ss << "API";
|
||||||
|
ss << " Error: ";
|
||||||
|
ss << getDescription();
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool APIError::isForDevice(std::string serial) const noexcept {
|
||||||
|
if(!device || serial.length() == 0)
|
||||||
|
return false;
|
||||||
|
return device->getSerial() == serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
// API Errors
|
||||||
|
static constexpr const char* ERROR_INVALID_NEODEVICE = "The provided neodevice_t object was invalid.";
|
||||||
|
static constexpr const char* ERROR_REQUIRED_PARAMETER_NULL = "A required parameter was NULL.";
|
||||||
|
static constexpr const char* ERROR_BUFFER_INSUFFICIENT = "The provided buffer was insufficient. No data was written.";
|
||||||
|
static constexpr const char* ERROR_OUTPUT_TRUNCATED = "The output was too large for the provided buffer and has been truncated.";
|
||||||
|
static constexpr const char* ERROR_PARAMETER_OUT_OF_RANGE = "A parameter was out of range.";
|
||||||
|
|
||||||
|
// Device Errors
|
||||||
|
static constexpr const char* ERROR_POLLING_MESSAGE_OVERFLOW = "Too many messages have been recieved for the polling message buffer, some have been lost!";
|
||||||
|
static constexpr const char* ERROR_NO_SERIAL_NUMBER = "Communication could not be established with the device. Perhaps it is not powered with 12 volts?";
|
||||||
|
static constexpr const char* ERROR_INCORRECT_SERIAL_NUMBER = "The device did not return the expected serial number!";
|
||||||
|
static constexpr const char* ERROR_SETTINGS_READ = "The device settings could not be read.";
|
||||||
|
static constexpr const char* ERROR_SETTINGS_VERSION = "The settings version is incorrect, please update your firmware with neoVI Explorer.";
|
||||||
|
static constexpr const char* ERROR_SETTINGS_LENGTH = "The settings length is incorrect, please update your firmware with neoVI Explorer.";
|
||||||
|
static constexpr const char* ERROR_SETTINGS_CHECKSUM = "The settings checksum is incorrect, attempting to set defaults may remedy this issue.";
|
||||||
|
static constexpr const char* ERROR_SETTINGS_NOT_AVAILABLE = "Settings are not available for this device.";
|
||||||
|
|
||||||
|
static constexpr const char* ERROR_TOO_MANY_ERRORS = "Too many errors have occurred. The list has been truncated.";
|
||||||
|
static constexpr const char* ERROR_UNKNOWN = "An unknown internal error occurred.";
|
||||||
|
static constexpr const char* ERROR_INVALID = "An invalid internal error occurred.";
|
||||||
|
const char* APIError::DescriptionForType(ErrorType type) {
|
||||||
|
switch(type) {
|
||||||
|
// API Errors
|
||||||
|
case InvalidNeoDevice:
|
||||||
|
return ERROR_INVALID_NEODEVICE;
|
||||||
|
case RequiredParameterNull:
|
||||||
|
return ERROR_REQUIRED_PARAMETER_NULL;
|
||||||
|
case BufferInsufficient:
|
||||||
|
return ERROR_BUFFER_INSUFFICIENT;
|
||||||
|
case OutputTruncated:
|
||||||
|
return ERROR_OUTPUT_TRUNCATED;
|
||||||
|
case ParameterOutOfRange:
|
||||||
|
return ERROR_PARAMETER_OUT_OF_RANGE;
|
||||||
|
|
||||||
|
// Device Errors
|
||||||
|
case PollingMessageOverflow:
|
||||||
|
return ERROR_POLLING_MESSAGE_OVERFLOW;
|
||||||
|
case NoSerialNumber:
|
||||||
|
return ERROR_NO_SERIAL_NUMBER;
|
||||||
|
case IncorrectSerialNumber:
|
||||||
|
return ERROR_INCORRECT_SERIAL_NUMBER;
|
||||||
|
case SettingsReadError:
|
||||||
|
return ERROR_SETTINGS_READ;
|
||||||
|
case SettingsVersionError:
|
||||||
|
return ERROR_SETTINGS_VERSION;
|
||||||
|
case SettingsLengthError:
|
||||||
|
return ERROR_SETTINGS_LENGTH;
|
||||||
|
case SettingsChecksumError:
|
||||||
|
return ERROR_SETTINGS_CHECKSUM;
|
||||||
|
case SettingsNotAvailable:
|
||||||
|
return ERROR_SETTINGS_NOT_AVAILABLE;
|
||||||
|
|
||||||
|
// Other Errors
|
||||||
|
case TooManyErrors:
|
||||||
|
return ERROR_TOO_MANY_ERRORS;
|
||||||
|
case Unknown:
|
||||||
|
return ERROR_UNKNOWN;
|
||||||
|
default:
|
||||||
|
return ERROR_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
APIError::Severity APIError::SeverityForType(ErrorType type) {
|
||||||
|
switch(type) {
|
||||||
|
// API Warnings
|
||||||
|
case OutputTruncated:
|
||||||
|
// Device Warnings
|
||||||
|
case PollingMessageOverflow:
|
||||||
|
return Severity::Warning;
|
||||||
|
|
||||||
|
// API Errors
|
||||||
|
case InvalidNeoDevice:
|
||||||
|
case RequiredParameterNull:
|
||||||
|
case BufferInsufficient:
|
||||||
|
case ParameterOutOfRange:
|
||||||
|
// Device Errors
|
||||||
|
case NoSerialNumber:
|
||||||
|
case IncorrectSerialNumber:
|
||||||
|
case SettingsReadError:
|
||||||
|
case SettingsVersionError:
|
||||||
|
case SettingsLengthError:
|
||||||
|
case SettingsChecksumError:
|
||||||
|
case SettingsNotAvailable:
|
||||||
|
// Other Errors
|
||||||
|
case TooManyErrors:
|
||||||
|
case Unknown:
|
||||||
|
default:
|
||||||
|
return Severity::Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ErrorFilter::match(const APIError& error) const noexcept {
|
||||||
|
if(type != APIError::Any && type != error.getType())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(matchOnDevicePtr && !error.isForDevice(device))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(severity != APIError::Severity::Any && severity != error.getSeverity())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(serial.length() != 0 && !error.isForDevice(serial))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
#include "icsneo/api/errormanager.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
static std::unique_ptr<ErrorManager> singleton;
|
||||||
|
|
||||||
|
ErrorManager& ErrorManager::GetInstance() {
|
||||||
|
if(!singleton)
|
||||||
|
singleton = std::unique_ptr<ErrorManager>(new ErrorManager());
|
||||||
|
return *singleton.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ErrorManager::get(std::vector<APIError>& errorOutput, size_t max, ErrorFilter filter) {
|
||||||
|
std::lock_guard<std::mutex> lk(mutex);
|
||||||
|
|
||||||
|
if(max == 0) // A limit of 0 indicates no limit
|
||||||
|
max = (size_t)-1;
|
||||||
|
|
||||||
|
size_t count = 0;
|
||||||
|
errorOutput.clear();
|
||||||
|
auto it = errors.begin();
|
||||||
|
while(it != errors.end()) {
|
||||||
|
if(filter.match(*it)) {
|
||||||
|
errorOutput.push_back(*it);
|
||||||
|
errors.erase(it++);
|
||||||
|
if(count++ >= max)
|
||||||
|
break; // We now have as many written to output as we can
|
||||||
|
} else {
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ErrorManager::getOne(APIError& errorOutput, ErrorFilter filter) {
|
||||||
|
std::vector<APIError> output;
|
||||||
|
get(output, filter, 1);
|
||||||
|
|
||||||
|
if(output.size() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
errorOutput = output[0];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ErrorManager::discard(ErrorFilter filter) {
|
||||||
|
std::lock_guard<std::mutex> lk(mutex);
|
||||||
|
errors.remove_if([&filter](const APIError& error) {
|
||||||
|
return filter.match(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ErrorManager::count_internal(ErrorFilter filter) const {
|
||||||
|
size_t ret = 0;
|
||||||
|
for(auto& error : errors)
|
||||||
|
if(filter.match(error))
|
||||||
|
ret++;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ErrorManager::beforeAddCheck(APIError::ErrorType type) {
|
||||||
|
if(enforceLimit()) { // The enforceLimit will add the "TooManyErrors" error for us if necessary
|
||||||
|
// We need to decide whether to add this error or drop it
|
||||||
|
// We would have to remove something if we added this error
|
||||||
|
if(APIError::SeverityForType(type) < lowestCurrentSeverity())
|
||||||
|
return false; // Don't add this one, we are already full of higher priority items
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ErrorManager::enforceLimit() {
|
||||||
|
if(errors.size() + 1 < errorLimit)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool hasTooManyWarningAlready = count_internal(ErrorFilter(APIError::TooManyErrors)) != 0;
|
||||||
|
size_t amountToRemove = (errors.size() + (hasTooManyWarningAlready ? 0 : 1)) - errorLimit;
|
||||||
|
|
||||||
|
discardLeastSevere(amountToRemove);
|
||||||
|
if(!hasTooManyWarningAlready)
|
||||||
|
add_internal(APIError::TooManyErrors);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
APIError::Severity ErrorManager::lowestCurrentSeverity() {
|
||||||
|
if(errors.empty())
|
||||||
|
return APIError::Severity(0);
|
||||||
|
|
||||||
|
APIError::Severity lowest = APIError::Severity::Error;
|
||||||
|
auto it = errors.begin();
|
||||||
|
while(it != errors.end()) {
|
||||||
|
if((*it).getSeverity() < lowest)
|
||||||
|
lowest = (*it).getSeverity();
|
||||||
|
}
|
||||||
|
return lowest;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ErrorManager::discardLeastSevere(size_t count) {
|
||||||
|
if(count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ErrorFilter infoFilter(APIError::Severity::Info);
|
||||||
|
auto it = errors.begin();
|
||||||
|
while(it != errors.end()) {
|
||||||
|
if(infoFilter.match(*it)) {
|
||||||
|
errors.erase(it++);
|
||||||
|
if(--count == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count != 0) {
|
||||||
|
ErrorFilter warningFilter(APIError::Severity::Warning);
|
||||||
|
it = errors.begin();
|
||||||
|
while(it != errors.end()) {
|
||||||
|
if(warningFilter.match(*it)) {
|
||||||
|
errors.erase(it++);
|
||||||
|
if(--count == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count != 0) {
|
||||||
|
ErrorFilter warningFilter(APIError::Severity::Warning);
|
||||||
|
it = errors.begin();
|
||||||
|
while(it != errors.end()) {
|
||||||
|
if(warningFilter.match(*it)) {
|
||||||
|
errors.erase(it++);
|
||||||
|
if(--count == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -127,8 +127,10 @@ void Communication::readTask() {
|
||||||
if(packetizer->input(readBytes)) {
|
if(packetizer->input(readBytes)) {
|
||||||
for(auto& packet : packetizer->output()) {
|
for(auto& packet : packetizer->output()) {
|
||||||
std::shared_ptr<Message> msg;
|
std::shared_ptr<Message> msg;
|
||||||
if(!decoder->decode(msg, packet))
|
if(!decoder->decode(msg, packet)) {
|
||||||
continue; // TODO Report an error to the user, we failed to decode this packet
|
err(APIError::Unknown); // TODO Use specific error
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for(auto& cb : messageCallbacks) {
|
for(auto& cb : messageCallbacks) {
|
||||||
if(!closing) { // We might have closed while reading or processing
|
if(!closing) { // We might have closed while reading or processing
|
||||||
|
|
|
||||||
|
|
@ -103,8 +103,10 @@ void MultiChannelCommunication::readTask() {
|
||||||
if(packetizer->input(payloadBytes)) {
|
if(packetizer->input(payloadBytes)) {
|
||||||
for(auto& packet : packetizer->output()) {
|
for(auto& packet : packetizer->output()) {
|
||||||
std::shared_ptr<Message> msg;
|
std::shared_ptr<Message> msg;
|
||||||
if(!decoder->decode(msg, packet))
|
if(!decoder->decode(msg, packet)) {
|
||||||
continue; // TODO Report an error to the user, we failed to decode this packet
|
err(APIError::Unknown); // TODO Use specific error
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for(auto& cb : messageCallbacks) { // We might have closed while reading or processing
|
for(auto& cb : messageCallbacks) { // We might have closed while reading or processing
|
||||||
if(!closing) {
|
if(!closing) {
|
||||||
|
|
|
||||||
|
|
@ -122,13 +122,15 @@ void Device::enforcePollingMessageLimit() {
|
||||||
while(pollingContainer.size_approx() > pollingMessageLimit) {
|
while(pollingContainer.size_approx() > pollingMessageLimit) {
|
||||||
std::shared_ptr<Message> throwAway;
|
std::shared_ptr<Message> throwAway;
|
||||||
pollingContainer.try_dequeue(throwAway);
|
pollingContainer.try_dequeue(throwAway);
|
||||||
// TODO Flag an error for the user!
|
err(APIError::PollingMessageOverflow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Device::open() {
|
bool Device::open() {
|
||||||
if(!com)
|
if(!com) {
|
||||||
|
err(APIError::Unknown);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(!com->open())
|
if(!com->open())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -141,13 +143,13 @@ bool Device::open() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(!serial) {
|
if(!serial) {
|
||||||
std::cout << "Failed to get serial number in " << i << " tries" << std::endl;
|
err(APIError::NoSerialNumber);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string currentSerial = getNeoDevice().serial;
|
std::string currentSerial = getNeoDevice().serial;
|
||||||
if(currentSerial != serial->deviceSerial) {
|
if(currentSerial != serial->deviceSerial) {
|
||||||
std::cout << "Found device had serial " << getNeoDevice().serial << " but connected device has serial " << serial->deviceSerial.c_str() << "!" << std::endl;
|
err(APIError::IncorrectSerialNumber);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,8 +168,10 @@ bool Device::open() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Device::close() {
|
bool Device::close() {
|
||||||
if(!com)
|
if(!com) {
|
||||||
|
err(APIError::Unknown);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(internalHandlerCallbackID)
|
if(internalHandlerCallbackID)
|
||||||
com->removeMessageCallback(internalHandlerCallbackID);
|
com->removeMessageCallback(internalHandlerCallbackID);
|
||||||
|
|
@ -213,22 +217,6 @@ bool Device::transmit(std::vector<std::shared_ptr<Message>> messages) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Transport, typename Settings>
|
|
||||||
void Device::initialize() {
|
|
||||||
auto transport = makeTransport<Transport>();
|
|
||||||
setupTransport(transport.get());
|
|
||||||
auto packetizer = makePacketizer();
|
|
||||||
setupPacketizer(packetizer.get());
|
|
||||||
auto encoder = makeEncoder(packetizer);
|
|
||||||
setupEncoder(encoder.get());
|
|
||||||
auto decoder = makeDecoder();
|
|
||||||
setupDecoder(decoder.get());
|
|
||||||
com = makeCommunication(std::move(transport), packetizer, std::move(encoder), std::move(decoder));
|
|
||||||
setupCommunication(com.get());
|
|
||||||
settings = makeSettings<Settings>(com);
|
|
||||||
setupSettings(settings.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Device::handleInternalMessage(std::shared_ptr<Message> message) {
|
void Device::handleInternalMessage(std::shared_ptr<Message> message) {
|
||||||
switch(message->network.getNetID()) {
|
switch(message->network.getNetID()) {
|
||||||
case Network::NetID::Reset_Status:
|
case Network::NetID::Reset_Status:
|
||||||
|
|
|
||||||
|
|
@ -43,16 +43,22 @@ uint16_t IDeviceSettings::CalculateGSChecksum(const std::vector<uint8_t>& settin
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IDeviceSettings::refresh(bool ignoreChecksum) {
|
bool IDeviceSettings::refresh(bool ignoreChecksum) {
|
||||||
if(disabled)
|
if(disabled) {
|
||||||
|
err(APIError::SettingsNotAvailable);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> rxSettings;
|
std::vector<uint8_t> rxSettings;
|
||||||
bool ret = com->getSettingsSync(rxSettings);
|
bool ret = com->getSettingsSync(rxSettings);
|
||||||
if(!ret)
|
if(!ret) {
|
||||||
|
err(APIError::SettingsReadError);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(rxSettings.size() < 6) // We need to at least have the header of GLOBAL_SETTINGS
|
if(rxSettings.size() < 6) { // We need to at least have the header of GLOBAL_SETTINGS
|
||||||
|
err(APIError::SettingsReadError);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
constexpr size_t gs_size = 3 * sizeof(uint16_t);
|
constexpr size_t gs_size = 3 * sizeof(uint16_t);
|
||||||
size_t rxLen = rxSettings.size() - gs_size;
|
size_t rxLen = rxSettings.size() - gs_size;
|
||||||
|
|
@ -63,17 +69,17 @@ bool IDeviceSettings::refresh(bool ignoreChecksum) {
|
||||||
rxSettings.erase(rxSettings.begin(), rxSettings.begin() + gs_size);
|
rxSettings.erase(rxSettings.begin(), rxSettings.begin() + gs_size);
|
||||||
|
|
||||||
if(gs_version != 5) {
|
if(gs_version != 5) {
|
||||||
std::cout << "gs_version was " << gs_version << " instead of 5.\nPlease update your firmware." << std::endl;
|
err(APIError::SettingsVersionError);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(rxLen != gs_len) {
|
if(rxLen != gs_len) {
|
||||||
std::cout << "rxLen was " << rxLen << " and gs_len was " << gs_len << " while reading settings" << std::endl;
|
err(APIError::SettingsLengthError);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!ignoreChecksum && gs_chksum != CalculateGSChecksum(rxSettings)) {
|
if(!ignoreChecksum && gs_chksum != CalculateGSChecksum(rxSettings)) {
|
||||||
std::cout << "Checksum mismatch while reading settings" << std::endl;
|
err(APIError::SettingsChecksumError);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,7 +87,7 @@ bool IDeviceSettings::refresh(bool ignoreChecksum) {
|
||||||
settingsLoaded = true;
|
settingsLoaded = true;
|
||||||
|
|
||||||
if(settings.size() != structSize) {
|
if(settings.size() != structSize) {
|
||||||
std::cout << "Settings size was " << settings.size() << " bytes but it should be " << structSize << " bytes for this device" << std::endl;
|
err(APIError::SettingsLengthError);
|
||||||
settingsLoaded = false;
|
settingsLoaded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
#ifndef __ICSNEO_API_ERROR_H_
|
||||||
|
#define __ICSNEO_API_ERROR_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define CONSTEXPR constexpr
|
||||||
|
#else
|
||||||
|
#define CONSTEXPR const
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* description;
|
||||||
|
time_t timestamp;
|
||||||
|
uint32_t errorNumber;
|
||||||
|
uint8_t severity;
|
||||||
|
char serial[7];
|
||||||
|
uint8_t reserved[16];
|
||||||
|
} neoerror_t;
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <chrono>
|
||||||
|
#include <string>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
namespace icsneo {
|
||||||
|
|
||||||
|
class Device;
|
||||||
|
|
||||||
|
class APIError {
|
||||||
|
public:
|
||||||
|
enum ErrorType : uint32_t {
|
||||||
|
Any = 0, // Used for filtering, should not appear in data
|
||||||
|
|
||||||
|
// API Errors
|
||||||
|
InvalidNeoDevice = 0x1000,
|
||||||
|
RequiredParameterNull = 0x1001,
|
||||||
|
BufferInsufficient = 0x1002,
|
||||||
|
OutputTruncated = 0x1003,
|
||||||
|
ParameterOutOfRange = 0x1004,
|
||||||
|
|
||||||
|
// Device Errors
|
||||||
|
PollingMessageOverflow = 0x2000,
|
||||||
|
NoSerialNumber = 0x2001,
|
||||||
|
IncorrectSerialNumber = 0x2002,
|
||||||
|
SettingsReadError = 0x2003,
|
||||||
|
SettingsVersionError = 0x2004,
|
||||||
|
SettingsLengthError = 0x2005,
|
||||||
|
SettingsChecksumError = 0x2006,
|
||||||
|
SettingsNotAvailable = 0x2007,
|
||||||
|
|
||||||
|
TooManyErrors = 0xFFFFFFFE,
|
||||||
|
Unknown = 0xFFFFFFFF
|
||||||
|
};
|
||||||
|
enum class Severity : uint8_t {
|
||||||
|
Any = 0, // Used for filtering, should not appear in data
|
||||||
|
Info = 0x10,
|
||||||
|
Warning = 0x20,
|
||||||
|
Error = 0x30
|
||||||
|
};
|
||||||
|
|
||||||
|
APIError(ErrorType error);
|
||||||
|
APIError(ErrorType error, const Device* device);
|
||||||
|
|
||||||
|
ErrorType getType() const noexcept { return ErrorType(errorStruct.errorNumber); }
|
||||||
|
Severity getSeverity() const noexcept { return Severity(errorStruct.severity); }
|
||||||
|
std::string getDescription() const noexcept { return std::string(errorStruct.description); }
|
||||||
|
const Device* getDevice() const noexcept { return device; } // Will return nullptr if this is an API-wide error
|
||||||
|
std::chrono::time_point<std::chrono::high_resolution_clock> getTimestamp() const noexcept { return timepoint; }
|
||||||
|
|
||||||
|
bool isForDevice(Device* forDevice) const noexcept { return forDevice == device; }
|
||||||
|
bool isForDevice(std::string serial) const noexcept;
|
||||||
|
|
||||||
|
// As opposed to getDescription, this will also add text such as "neoVI FIRE 2 CY2468 Error: " to fully describe the problem
|
||||||
|
std::string describe() const noexcept;
|
||||||
|
friend std::ostream& operator<<(std::ostream& os, const APIError& error) {
|
||||||
|
os << error.describe();
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* DescriptionForType(ErrorType type);
|
||||||
|
static Severity SeverityForType(ErrorType type);
|
||||||
|
|
||||||
|
private:
|
||||||
|
neoerror_t errorStruct;
|
||||||
|
std::string serial;
|
||||||
|
std::chrono::time_point<std::chrono::high_resolution_clock> timepoint;
|
||||||
|
const Device* device;
|
||||||
|
|
||||||
|
void init(ErrorType error);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ErrorFilter {
|
||||||
|
public:
|
||||||
|
ErrorFilter() {} // Empty filter matches anything
|
||||||
|
ErrorFilter(APIError::ErrorType error) : type(error) {}
|
||||||
|
ErrorFilter(APIError::Severity severity) : severity(severity) {}
|
||||||
|
ErrorFilter(Device* device, APIError::ErrorType error = APIError::Any) : type(error), matchOnDevicePtr(true), device(device) {}
|
||||||
|
ErrorFilter(Device* device, APIError::Severity severity = APIError::Severity::Any) : severity(severity), matchOnDevicePtr(true), device(device) {}
|
||||||
|
ErrorFilter(std::string serial, APIError::ErrorType error = APIError::Any) : type(error), serial(serial) {}
|
||||||
|
ErrorFilter(std::string serial, APIError::Severity severity = APIError::Severity::Any) : severity(severity), serial(serial) {}
|
||||||
|
|
||||||
|
bool match(const APIError& error) const noexcept;
|
||||||
|
|
||||||
|
APIError::Severity severity = APIError::Severity::Any;
|
||||||
|
APIError::ErrorType type = APIError::Any;
|
||||||
|
bool matchOnDevicePtr = false;
|
||||||
|
Device* device = nullptr; // nullptr will match on "no device, generic API error"
|
||||||
|
std::string serial; // Empty serial will match any, including no device. Not affected by matchOnDevicePtr
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
#ifndef __ICSNEO_API_ERRORMANAGER_H_
|
||||||
|
#define __ICSNEO_API_ERRORMANAGER_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
#include <mutex>
|
||||||
|
#include <functional>
|
||||||
|
#include "icsneo/api/error.h"
|
||||||
|
|
||||||
|
namespace icsneo {
|
||||||
|
|
||||||
|
typedef std::function<void (APIError::ErrorType)> device_errorhandler_t;
|
||||||
|
|
||||||
|
class ErrorManager {
|
||||||
|
public:
|
||||||
|
static ErrorManager& GetInstance();
|
||||||
|
|
||||||
|
size_t count(ErrorFilter filter = ErrorFilter()) const {
|
||||||
|
std::lock_guard<std::mutex> lk(mutex);
|
||||||
|
return count_internal(filter);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<APIError> get(ErrorFilter filter, size_t max = 0) { return get(max, filter); }
|
||||||
|
std::vector<APIError> get(size_t max = 0, ErrorFilter filter = ErrorFilter()) {
|
||||||
|
std::vector<APIError> ret;
|
||||||
|
get(ret, filter, max);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
void get(std::vector<APIError>& errors, ErrorFilter filter, size_t max = 0) { get(errors, max, filter); }
|
||||||
|
void get(std::vector<APIError>& errors, size_t max = 0, ErrorFilter filter = ErrorFilter());
|
||||||
|
bool getOne(APIError& error, ErrorFilter filter = ErrorFilter());
|
||||||
|
|
||||||
|
void add(APIError error) {
|
||||||
|
std::lock_guard<std::mutex> lk(mutex);
|
||||||
|
add_internal(error);
|
||||||
|
}
|
||||||
|
void add(APIError::ErrorType type) {
|
||||||
|
std::lock_guard<std::mutex> lk(mutex);
|
||||||
|
add_internal(type);
|
||||||
|
}
|
||||||
|
void add(APIError::ErrorType type, const Device* forDevice) {
|
||||||
|
std::lock_guard<std::mutex> lk(mutex);
|
||||||
|
add_internal(type, forDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
void discard(ErrorFilter filter = ErrorFilter());
|
||||||
|
|
||||||
|
void setErrorLimit(size_t newLimit) {
|
||||||
|
if(newLimit < 10) {
|
||||||
|
add(APIError::ParameterOutOfRange);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lk(mutex);
|
||||||
|
errorLimit = newLimit;
|
||||||
|
enforceLimit();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getErrorLimit() const { return errorLimit; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ErrorManager() {}
|
||||||
|
mutable std::mutex mutex;
|
||||||
|
std::list<APIError> errors;
|
||||||
|
size_t errorLimit = 10000;
|
||||||
|
|
||||||
|
size_t count_internal(ErrorFilter filter = ErrorFilter()) const;
|
||||||
|
|
||||||
|
void add_internal(APIError error) {
|
||||||
|
if(!beforeAddCheck(error.getType()))
|
||||||
|
return;
|
||||||
|
errors.push_back(error);
|
||||||
|
enforceLimit();
|
||||||
|
}
|
||||||
|
void add_internal(APIError::ErrorType type) {
|
||||||
|
if(!beforeAddCheck(type))
|
||||||
|
return;
|
||||||
|
errors.emplace_back(type);
|
||||||
|
enforceLimit();
|
||||||
|
}
|
||||||
|
void add_internal(APIError::ErrorType type, const Device* forDevice) {
|
||||||
|
if(!beforeAddCheck(type))
|
||||||
|
return;
|
||||||
|
errors.emplace_back(type, forDevice);
|
||||||
|
enforceLimit();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool beforeAddCheck(APIError::ErrorType type); // Returns whether the error should be added
|
||||||
|
|
||||||
|
bool enforceLimit(); // Returns whether the limit enforcement resulted in an overflow
|
||||||
|
|
||||||
|
APIError::Severity lowestCurrentSeverity();
|
||||||
|
void discardLeastSevere(size_t count = 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#include "icsneo/communication/packet.h"
|
#include "icsneo/communication/packet.h"
|
||||||
#include "icsneo/communication/message/callback/messagecallback.h"
|
#include "icsneo/communication/message/callback/messagecallback.h"
|
||||||
#include "icsneo/communication/message/serialnumbermessage.h"
|
#include "icsneo/communication/message/serialnumbermessage.h"
|
||||||
|
#include "icsneo/api/errormanager.h"
|
||||||
#include "icsneo/communication/packetizer.h"
|
#include "icsneo/communication/packetizer.h"
|
||||||
#include "icsneo/communication/encoder.h"
|
#include "icsneo/communication/encoder.h"
|
||||||
#include "icsneo/communication/decoder.h"
|
#include "icsneo/communication/decoder.h"
|
||||||
|
|
@ -22,10 +23,11 @@ namespace icsneo {
|
||||||
class Communication {
|
class Communication {
|
||||||
public:
|
public:
|
||||||
Communication(
|
Communication(
|
||||||
|
device_errorhandler_t err,
|
||||||
std::unique_ptr<ICommunication> com,
|
std::unique_ptr<ICommunication> com,
|
||||||
std::shared_ptr<Packetizer> p,
|
std::shared_ptr<Packetizer> p,
|
||||||
std::unique_ptr<Encoder> e,
|
std::unique_ptr<Encoder> e,
|
||||||
std::unique_ptr<Decoder> md) : packetizer(p), encoder(std::move(e)), decoder(std::move(md)), impl(std::move(com)) {}
|
std::unique_ptr<Decoder> md) : packetizer(p), encoder(std::move(e)), decoder(std::move(md)), err(err), impl(std::move(com)) {}
|
||||||
virtual ~Communication() { close(); }
|
virtual ~Communication() { close(); }
|
||||||
|
|
||||||
bool open();
|
bool open();
|
||||||
|
|
@ -50,6 +52,7 @@ public:
|
||||||
std::shared_ptr<Packetizer> packetizer; // Ownership is shared with the encoder
|
std::shared_ptr<Packetizer> packetizer; // Ownership is shared with the encoder
|
||||||
std::unique_ptr<Encoder> encoder;
|
std::unique_ptr<Encoder> encoder;
|
||||||
std::unique_ptr<Decoder> decoder;
|
std::unique_ptr<Decoder> decoder;
|
||||||
|
device_errorhandler_t err;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::unique_ptr<ICommunication> impl;
|
std::unique_ptr<ICommunication> impl;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include "icsneo/communication/message/canmessage.h"
|
#include "icsneo/communication/message/canmessage.h"
|
||||||
#include "icsneo/communication/packet.h"
|
#include "icsneo/communication/packet.h"
|
||||||
#include "icsneo/communication/network.h"
|
#include "icsneo/communication/network.h"
|
||||||
|
#include "icsneo/api/errormanager.h"
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
@ -16,9 +17,12 @@ namespace icsneo {
|
||||||
class Decoder {
|
class Decoder {
|
||||||
public:
|
public:
|
||||||
static uint64_t GetUInt64FromLEBytes(uint8_t* bytes);
|
static uint64_t GetUInt64FromLEBytes(uint8_t* bytes);
|
||||||
|
|
||||||
|
Decoder(device_errorhandler_t err) : err(err) {}
|
||||||
bool decode(std::shared_ptr<Message>& result, const std::shared_ptr<Packet>& packet);
|
bool decode(std::shared_ptr<Message>& result, const std::shared_ptr<Packet>& packet);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
device_errorhandler_t err;
|
||||||
typedef uint16_t icscm_bitfield;
|
typedef uint16_t icscm_bitfield;
|
||||||
struct HardwareCANPacket {
|
struct HardwareCANPacket {
|
||||||
struct {
|
struct {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ namespace icsneo {
|
||||||
|
|
||||||
class Encoder {
|
class Encoder {
|
||||||
public:
|
public:
|
||||||
Encoder(std::shared_ptr<Packetizer> packetizerInstance) : packetizer(packetizerInstance) {}
|
Encoder(device_errorhandler_t err, std::shared_ptr<Packetizer> p) : packetizer(p), err(err) {}
|
||||||
bool encode(std::vector<uint8_t>& result, const std::shared_ptr<Message>& message);
|
bool encode(std::vector<uint8_t>& result, const std::shared_ptr<Message>& message);
|
||||||
bool encode(std::vector<uint8_t>& result, Command cmd, bool boolean) { return encode(result, cmd, std::vector<uint8_t>({ (uint8_t)boolean })); }
|
bool encode(std::vector<uint8_t>& result, Command cmd, bool boolean) { return encode(result, cmd, std::vector<uint8_t>({ (uint8_t)boolean })); }
|
||||||
bool encode(std::vector<uint8_t>& result, Command cmd, std::vector<uint8_t> arguments = {});
|
bool encode(std::vector<uint8_t>& result, Command cmd, std::vector<uint8_t> arguments = {});
|
||||||
|
|
@ -23,6 +23,7 @@ public:
|
||||||
bool supportCANFD = false;
|
bool supportCANFD = false;
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Packetizer> packetizer;
|
std::shared_ptr<Packetizer> packetizer;
|
||||||
|
device_errorhandler_t err;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,11 @@ namespace icsneo {
|
||||||
class MultiChannelCommunication : public Communication {
|
class MultiChannelCommunication : public Communication {
|
||||||
public:
|
public:
|
||||||
MultiChannelCommunication(
|
MultiChannelCommunication(
|
||||||
|
device_errorhandler_t err,
|
||||||
std::unique_ptr<ICommunication> com,
|
std::unique_ptr<ICommunication> com,
|
||||||
std::shared_ptr<Packetizer> p,
|
std::shared_ptr<Packetizer> p,
|
||||||
std::unique_ptr<Encoder> e,
|
std::unique_ptr<Encoder> e,
|
||||||
std::unique_ptr<Decoder> md) : Communication(std::move(com), p, std::move(e), std::move(md)) {}
|
std::unique_ptr<Decoder> md) : Communication(err, std::move(com), p, std::move(e), std::move(md)) {}
|
||||||
void spawnThreads() override;
|
void spawnThreads() override;
|
||||||
void joinThreads() override;
|
void joinThreads() override;
|
||||||
bool sendPacket(std::vector<uint8_t>& bytes) override;
|
bool sendPacket(std::vector<uint8_t>& bytes) override;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#define __PACKETIZER_H_
|
#define __PACKETIZER_H_
|
||||||
|
|
||||||
#include "icsneo/communication/packet.h"
|
#include "icsneo/communication/packet.h"
|
||||||
|
#include "icsneo/api/errormanager.h"
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
@ -11,6 +12,9 @@ namespace icsneo {
|
||||||
class Packetizer {
|
class Packetizer {
|
||||||
public:
|
public:
|
||||||
static uint8_t ICSChecksum(const std::vector<uint8_t>& data);
|
static uint8_t ICSChecksum(const std::vector<uint8_t>& data);
|
||||||
|
|
||||||
|
Packetizer(device_errorhandler_t err) : err(err) {}
|
||||||
|
|
||||||
std::vector<uint8_t>& packetWrap(std::vector<uint8_t>& data, bool shortFormat);
|
std::vector<uint8_t>& packetWrap(std::vector<uint8_t>& data, bool shortFormat);
|
||||||
|
|
||||||
bool input(const std::vector<uint8_t>& bytes);
|
bool input(const std::vector<uint8_t>& bytes);
|
||||||
|
|
@ -37,6 +41,8 @@ private:
|
||||||
std::deque<uint8_t> bytes;
|
std::deque<uint8_t> bytes;
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Packet>> processedPackets;
|
std::vector<std::shared_ptr<Packet>> processedPackets;
|
||||||
|
|
||||||
|
device_errorhandler_t err;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include "icsneo/api/errormanager.h"
|
||||||
#include "icsneo/device/neodevice.h"
|
#include "icsneo/device/neodevice.h"
|
||||||
#include "icsneo/device/idevicesettings.h"
|
#include "icsneo/device/idevicesettings.h"
|
||||||
#include "icsneo/device/nullsettings.h"
|
#include "icsneo/device/nullsettings.h"
|
||||||
|
|
@ -71,6 +72,7 @@ protected:
|
||||||
int messagePollingCallbackID = 0;
|
int messagePollingCallbackID = 0;
|
||||||
int internalHandlerCallbackID = 0;
|
int internalHandlerCallbackID = 0;
|
||||||
std::shared_ptr<Communication> com;
|
std::shared_ptr<Communication> com;
|
||||||
|
device_errorhandler_t err;
|
||||||
|
|
||||||
// START Initialization Functions
|
// START Initialization Functions
|
||||||
Device(neodevice_t neodevice = { 0 }) {
|
Device(neodevice_t neodevice = { 0 }) {
|
||||||
|
|
@ -79,26 +81,44 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Transport, typename Settings = NullSettings>
|
template<typename Transport, typename Settings = NullSettings>
|
||||||
void initialize();
|
void initialize() {
|
||||||
|
err = makeErrorHandler();
|
||||||
|
auto transport = makeTransport<Transport>();
|
||||||
|
setupTransport(transport.get());
|
||||||
|
auto packetizer = makePacketizer();
|
||||||
|
setupPacketizer(packetizer.get());
|
||||||
|
auto encoder = makeEncoder(packetizer);
|
||||||
|
setupEncoder(encoder.get());
|
||||||
|
auto decoder = makeDecoder();
|
||||||
|
setupDecoder(decoder.get());
|
||||||
|
com = makeCommunication(std::move(transport), packetizer, std::move(encoder), std::move(decoder));
|
||||||
|
setupCommunication(com.get());
|
||||||
|
settings = makeSettings<Settings>(com);
|
||||||
|
setupSettings(settings.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual device_errorhandler_t makeErrorHandler() {
|
||||||
|
return [this](APIError::ErrorType type) { ErrorManager::GetInstance().add(type, this); };
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Transport>
|
template<typename Transport>
|
||||||
std::unique_ptr<ICommunication> makeTransport() { return std::unique_ptr<ICommunication>(new Transport(getWritableNeoDevice())); }
|
std::unique_ptr<ICommunication> makeTransport() { return std::unique_ptr<ICommunication>(new Transport(getWritableNeoDevice())); }
|
||||||
virtual void setupTransport(ICommunication* transport) {}
|
virtual void setupTransport(ICommunication* transport) {}
|
||||||
|
|
||||||
virtual std::shared_ptr<Packetizer> makePacketizer() { return std::make_shared<Packetizer>(); }
|
virtual std::shared_ptr<Packetizer> makePacketizer() { return std::make_shared<Packetizer>(err); }
|
||||||
virtual void setupPacketizer(Packetizer* packetizer) {}
|
virtual void setupPacketizer(Packetizer* packetizer) {}
|
||||||
|
|
||||||
virtual std::unique_ptr<Encoder> makeEncoder(std::shared_ptr<Packetizer> p) { return std::unique_ptr<Encoder>(new Encoder(p)); }
|
virtual std::unique_ptr<Encoder> makeEncoder(std::shared_ptr<Packetizer> p) { return std::unique_ptr<Encoder>(new Encoder(err, p)); }
|
||||||
virtual void setupEncoder(Encoder* encoder) {}
|
virtual void setupEncoder(Encoder* encoder) {}
|
||||||
|
|
||||||
virtual std::unique_ptr<Decoder> makeDecoder() { return std::unique_ptr<Decoder>(new Decoder()); }
|
virtual std::unique_ptr<Decoder> makeDecoder() { return std::unique_ptr<Decoder>(new Decoder(err)); }
|
||||||
virtual void setupDecoder(Decoder* decoder) {}
|
virtual void setupDecoder(Decoder* decoder) {}
|
||||||
|
|
||||||
virtual std::shared_ptr<Communication> makeCommunication(
|
virtual std::shared_ptr<Communication> makeCommunication(
|
||||||
std::unique_ptr<ICommunication> t,
|
std::unique_ptr<ICommunication> t,
|
||||||
std::shared_ptr<Packetizer> p,
|
std::shared_ptr<Packetizer> p,
|
||||||
std::unique_ptr<Encoder> e,
|
std::unique_ptr<Encoder> e,
|
||||||
std::unique_ptr<Decoder> d) { return std::make_shared<Communication>(std::move(t), p, std::move(e), std::move(d)); }
|
std::unique_ptr<Decoder> d) { return std::make_shared<Communication>(err, std::move(t), p, std::move(e), std::move(d)); }
|
||||||
virtual void setupCommunication(Communication* com) {}
|
virtual void setupCommunication(Communication* com) {}
|
||||||
|
|
||||||
template<typename Settings>
|
template<typename Settings>
|
||||||
|
|
|
||||||
|
|
@ -283,11 +283,7 @@ public:
|
||||||
static constexpr uint16_t GS_VERSION = 5;
|
static constexpr uint16_t GS_VERSION = 5;
|
||||||
static uint16_t CalculateGSChecksum(const std::vector<uint8_t>& settings);
|
static uint16_t CalculateGSChecksum(const std::vector<uint8_t>& settings);
|
||||||
|
|
||||||
// Parameter createInoperableSettings exists because it is serving as a warning that you probably don't want to do this
|
IDeviceSettings(std::shared_ptr<Communication> com, size_t size) : com(com), err(com->err), structSize(size) {}
|
||||||
typedef void* warn_t;
|
|
||||||
IDeviceSettings(warn_t createInoperableSettings) : disabled(true), readonly(true), structSize(0) { (void)createInoperableSettings; }
|
|
||||||
|
|
||||||
IDeviceSettings(std::shared_ptr<Communication> com, size_t size) : com(com), structSize(size) {}
|
|
||||||
virtual ~IDeviceSettings() {}
|
virtual ~IDeviceSettings() {}
|
||||||
bool ok() { return !disabled && settingsLoaded; }
|
bool ok() { return !disabled && settingsLoaded; }
|
||||||
|
|
||||||
|
|
@ -313,9 +309,15 @@ public:
|
||||||
bool readonly = false;
|
bool readonly = false;
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<Communication> com;
|
std::shared_ptr<Communication> com;
|
||||||
|
device_errorhandler_t err;
|
||||||
size_t structSize;
|
size_t structSize;
|
||||||
bool settingsLoaded = false;
|
bool settingsLoaded = false;
|
||||||
std::vector<uint8_t> settings;
|
std::vector<uint8_t> settings;
|
||||||
|
|
||||||
|
// Parameter createInoperableSettings exists because it is serving as a warning that you probably don't want to do this
|
||||||
|
typedef void* warn_t;
|
||||||
|
IDeviceSettings(warn_t createInoperableSettings, std::shared_ptr<Communication> com)
|
||||||
|
: disabled(true), readonly(true), err(com->err), structSize(0) { (void)createInoperableSettings; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ namespace icsneo {
|
||||||
|
|
||||||
class NullSettings : public IDeviceSettings {
|
class NullSettings : public IDeviceSettings {
|
||||||
public:
|
public:
|
||||||
// Calls the base constructor with "createInoperableSettings"
|
// Calls the protected base constructor with "createInoperableSettings"
|
||||||
NullSettings(std::shared_ptr<Communication> com = std::shared_ptr<Communication>()) : IDeviceSettings(nullptr) { (void)com; }
|
NullSettings(std::shared_ptr<Communication> com) : IDeviceSettings(nullptr, com) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ protected:
|
||||||
std::shared_ptr<Packetizer> packetizer,
|
std::shared_ptr<Packetizer> packetizer,
|
||||||
std::unique_ptr<Encoder> encoder,
|
std::unique_ptr<Encoder> encoder,
|
||||||
std::unique_ptr<Decoder> decoder
|
std::unique_ptr<Decoder> decoder
|
||||||
) override { return std::make_shared<MultiChannelCommunication>(std::move(transport), packetizer, std::move(encoder), std::move(decoder)); }
|
) override { return std::make_shared<MultiChannelCommunication>(err, std::move(transport), packetizer, std::move(encoder), std::move(decoder)); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Plasion(neodevice_t neodevice) : Device(neodevice) {}
|
Plasion(neodevice_t neodevice) : Device(neodevice) {}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue