mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-08-05 09:28:40 +02:00
Event refactor builds on Windows
This commit is contained in:
@@ -1,145 +0,0 @@
|
||||
#ifndef __ICSNEO_API_ERRORMANAGER_H_
|
||||
#define __ICSNEO_API_ERRORMANAGER_H_
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <thread>
|
||||
#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>& outErrors, ErrorFilter filter, size_t max = 0) { get(outErrors, max, filter); }
|
||||
void get(std::vector<APIError>& outErrors, size_t max = 0, ErrorFilter filter = ErrorFilter());
|
||||
bool getLastError(APIError& outErrors, ErrorFilter filter = ErrorFilter());
|
||||
bool getLastError(APIError& errorOutput, std::thread::id id);
|
||||
|
||||
void add(APIError error) {
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
add_internal(error);
|
||||
}
|
||||
void add(APIError error, std::thread::id id) {
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
if(id == std::thread::id())
|
||||
add_internal(error);
|
||||
else
|
||||
add_internal_threaded(error, id);
|
||||
}
|
||||
void add(APIError::ErrorType type) {
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
add_internal(APIError::APIError(type));
|
||||
}
|
||||
void add(APIError::ErrorType type, std::thread::id id) {
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
if(id == std::thread::id())
|
||||
add_internal(APIError::APIError(type));
|
||||
else
|
||||
add_internal_threaded(APIError::APIError(type), id);
|
||||
|
||||
}
|
||||
void add(APIError::ErrorType type, const Device* forDevice) {
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
add_internal(APIError::APIError(type, forDevice));
|
||||
}
|
||||
void add(APIError::ErrorType type, const Device* forDevice, std::thread::id id) {
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
if(id == std::thread::id())
|
||||
add_internal(APIError::APIError(type, forDevice));
|
||||
else
|
||||
add_internal_threaded(APIError::APIError(type, forDevice), id);
|
||||
}
|
||||
|
||||
void discard(ErrorFilter filter = ErrorFilter());
|
||||
|
||||
void setErrorLimit(size_t newLimit) {
|
||||
if(newLimit == errorLimit)
|
||||
return;
|
||||
|
||||
if(newLimit < 10) {
|
||||
add(APIError::ParameterOutOfRange);
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
errorLimit = newLimit;
|
||||
if(enforceLimit())
|
||||
add(APIError::TooManyErrors);
|
||||
}
|
||||
|
||||
size_t getErrorLimit() const { return errorLimit; }
|
||||
|
||||
private:
|
||||
ErrorManager() {}
|
||||
// Used by functions for threadsafety
|
||||
mutable std::mutex mutex;
|
||||
|
||||
// Stores all errors
|
||||
std::list<APIError> errors;
|
||||
std::unordered_map<std::thread::id, APIError> lastUserErrors;
|
||||
size_t errorLimit = 10000;
|
||||
|
||||
size_t count_internal(ErrorFilter filter = ErrorFilter()) const;
|
||||
|
||||
/**
|
||||
* Places a {id, error} pair into the lastUserErrors
|
||||
* If the key id already exists in the map, replace the error of that pair with the new one
|
||||
*/
|
||||
void add_internal_threaded(APIError error, std::thread::id id) {
|
||||
auto iter = lastUserErrors.find(id);
|
||||
if(iter == lastUserErrors.end()) {
|
||||
lastUserErrors.insert(std::make_pair(id, error));
|
||||
} else {
|
||||
iter->second = error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If errors is not full, add the error at the end
|
||||
* Otherwise, remove the least significant errors, push the error to the back and push a APIError::TooManyErrors to the back (in that order)
|
||||
*/
|
||||
void add_internal(APIError error) {
|
||||
// Ensure the error list is at most exactly full (size of errorLimit - 1, leaving room for a potential APIError::TooManyErrors)
|
||||
enforceLimit();
|
||||
|
||||
// We are exactly full, either because the list was truncated or because we were simply full before
|
||||
if(errors.size() == errorLimit - 1) {
|
||||
// If the error is worth adding
|
||||
if(APIError::SeverityForType(error.getType()) >= lowestCurrentSeverity()) {
|
||||
discardLeastSevere(1);
|
||||
errors.push_back(error);
|
||||
}
|
||||
|
||||
errors.push_back(APIError(APIError::TooManyErrors));
|
||||
} else {
|
||||
errors.push_back(error);
|
||||
}
|
||||
}
|
||||
|
||||
bool enforceLimit(); // Returns whether the limit enforcement resulted in an overflow
|
||||
|
||||
APIError::Severity lowestCurrentSeverity();
|
||||
void discardLeastSevere(size_t count = 1);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef __ICSNEO_API_ERROR_H_
|
||||
#define __ICSNEO_API_ERROR_H_
|
||||
#ifndef __ICSNEO_API_EVENT_H_
|
||||
#define __ICSNEO_API_EVENT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
@@ -7,11 +7,11 @@
|
||||
typedef struct {
|
||||
const char* description;
|
||||
time_t timestamp;
|
||||
uint32_t errorNumber;
|
||||
uint32_t eventNumber;
|
||||
uint8_t severity;
|
||||
char serial[7];
|
||||
uint8_t reserved[16];
|
||||
} neoerror_t;
|
||||
} neoevent_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -24,19 +24,19 @@ namespace icsneo {
|
||||
|
||||
class Device;
|
||||
|
||||
class APIError {
|
||||
class APIEvent {
|
||||
public:
|
||||
typedef std::chrono::system_clock ErrorClock;
|
||||
typedef std::chrono::time_point<ErrorClock> ErrorTimePoint;
|
||||
typedef std::chrono::system_clock EventClock;
|
||||
typedef std::chrono::time_point<EventClock> EventTimePoint;
|
||||
|
||||
enum ErrorType : uint32_t {
|
||||
enum class Type : uint32_t {
|
||||
Any = 0, // Used for filtering, should not appear in data
|
||||
|
||||
// API Errors
|
||||
InvalidNeoDevice = 0x1000,
|
||||
// API Events
|
||||
InvalidNeoDevice = 0x1000, // api
|
||||
RequiredParameterNull = 0x1001,
|
||||
BufferInsufficient = 0x1002,
|
||||
OutputTruncated = 0x1003,
|
||||
OutputTruncated = 0x1003, // just a warning
|
||||
ParameterOutOfRange = 0x1004,
|
||||
DeviceCurrentlyOpen = 0x1005,
|
||||
DeviceCurrentlyClosed = 0x1006,
|
||||
@@ -47,10 +47,10 @@ public:
|
||||
UnsupportedTXNetwork = 0x1011,
|
||||
MessageMaxLengthExceeded = 0x1012,
|
||||
|
||||
// Device Errors
|
||||
// Device Events
|
||||
PollingMessageOverflow = 0x2000,
|
||||
NoSerialNumber = 0x2001,
|
||||
IncorrectSerialNumber = 0x2002,
|
||||
NoSerialNumber = 0x2001, // api
|
||||
IncorrectSerialNumber = 0x2002, // api
|
||||
SettingsReadError = 0x2003,
|
||||
SettingsVersionError = 0x2004,
|
||||
SettingsLengthError = 0x2005,
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
CANFDNotSupported = 0x2020,
|
||||
RTRNotSupported = 0x2021,
|
||||
|
||||
// Transport Errors
|
||||
// Transport Events
|
||||
FailedToRead = 0x3000,
|
||||
FailedToWrite = 0x3001,
|
||||
DriverFailedToOpen = 0x3002,
|
||||
@@ -81,66 +81,65 @@ public:
|
||||
PCAPCouldNotStart = 0x3102,
|
||||
PCAPCouldNotFindDevices = 0x3103,
|
||||
PacketDecodingError = 0x3104,
|
||||
|
||||
TooManyErrors = 0xFFFFFFFE,
|
||||
|
||||
NoErrorFound = 0xFFFFFFFD,
|
||||
TooManyEvents = 0xFFFFFFFE,
|
||||
Unknown = 0xFFFFFFFF
|
||||
};
|
||||
enum class Severity : uint8_t {
|
||||
Any = 0, // Used for filtering, should not appear in data
|
||||
Info = 0x10,
|
||||
Warning = 0x20,
|
||||
EventInfo = 0x10,
|
||||
EventWarning = 0x20,
|
||||
Error = 0x30
|
||||
};
|
||||
|
||||
APIError() : errorStruct({}), device(nullptr) {}
|
||||
APIError(ErrorType error);
|
||||
APIError(ErrorType error, const Device* device);
|
||||
|
||||
const neoerror_t* getNeoError() const noexcept { return &errorStruct; }
|
||||
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
|
||||
ErrorTimePoint getTimestamp() const noexcept { return timepoint; }
|
||||
APIEvent() : eventStruct({}), device(nullptr), serial(), timepoint() {}
|
||||
APIEvent(APIEvent::Type event, APIEvent::Severity severity, const Device* device = nullptr);
|
||||
|
||||
const neoevent_t* getNeoEvent() const noexcept { return &eventStruct; }
|
||||
Type getType() const noexcept { return Type(eventStruct.eventNumber); }
|
||||
Severity getSeverity() const noexcept { return Severity(eventStruct.severity); }
|
||||
std::string getDescription() const noexcept { return std::string(eventStruct.description); }
|
||||
const Device* getDevice() const noexcept { return device; } // Will return nullptr if this is an API-wide event
|
||||
EventTimePoint getTimestamp() const noexcept { return timepoint; }
|
||||
|
||||
bool isForDevice(const 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();
|
||||
friend std::ostream& operator<<(std::ostream& os, const APIEvent& event) {
|
||||
os << event.describe();
|
||||
return os;
|
||||
}
|
||||
|
||||
static const char* DescriptionForType(ErrorType type);
|
||||
static Severity SeverityForType(ErrorType type);
|
||||
static const char* DescriptionForType(Type type);
|
||||
|
||||
private:
|
||||
neoerror_t errorStruct;
|
||||
neoevent_t eventStruct;
|
||||
std::string serial;
|
||||
ErrorTimePoint timepoint;
|
||||
EventTimePoint timepoint;
|
||||
const Device* device;
|
||||
|
||||
void init(ErrorType error);
|
||||
void init(Type event, APIEvent::Severity);
|
||||
};
|
||||
|
||||
class ErrorFilter {
|
||||
class EventFilter {
|
||||
public:
|
||||
ErrorFilter() {} // Empty filter matches anything
|
||||
ErrorFilter(APIError::ErrorType error) : type(error) {}
|
||||
ErrorFilter(APIError::Severity severity) : severity(severity) {}
|
||||
ErrorFilter(const Device* device, APIError::ErrorType error = APIError::Any) : type(error), matchOnDevicePtr(true), device(device) {}
|
||||
ErrorFilter(const Device* device, APIError::Severity severity) : 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) : severity(severity), serial(serial) {}
|
||||
EventFilter() {} // Empty filter matches anything
|
||||
EventFilter(APIEvent::Type type) : type(type) {}
|
||||
EventFilter(APIEvent::Severity severity) : severity(severity) {}
|
||||
EventFilter(const Device* device, APIEvent::Type type = APIEvent::Type::Any) : type(type), matchOnDevicePtr(true), device(device) {}
|
||||
EventFilter(const Device* device, APIEvent::Severity severity) : severity(severity), matchOnDevicePtr(true), device(device) {}
|
||||
EventFilter(std::string serial, APIEvent::Type type = APIEvent::Type::Any) : type(type), serial(serial) {}
|
||||
EventFilter(std::string serial, APIEvent::Severity severity) : severity(severity), serial(serial) {}
|
||||
|
||||
bool match(const APIError& error) const noexcept;
|
||||
bool match(const APIEvent& event) const noexcept;
|
||||
|
||||
APIError::Severity severity = APIError::Severity::Any;
|
||||
APIError::ErrorType type = APIError::Any;
|
||||
APIEvent::Type type = APIEvent::Type::Any;
|
||||
APIEvent::Severity severity = APIEvent::Severity::Any;
|
||||
bool matchOnDevicePtr = false;
|
||||
const Device* device = nullptr; // nullptr will match on "no device, generic API error"
|
||||
const Device* device = nullptr; // nullptr will match on "no device, generic API event"
|
||||
std::string serial; // Empty serial will match any, including no device. Not affected by matchOnDevicePtr
|
||||
};
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
#ifndef __ICSNEO_API_EVENTMANAGER_H_
|
||||
#define __ICSNEO_API_EVENTMANAGER_H_
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <thread>
|
||||
#include "icsneo/api/event.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
typedef std::function<void (APIEvent::Type, APIEvent::Severity)> device_eventhandler_t;
|
||||
|
||||
class EventManager {
|
||||
public:
|
||||
static EventManager& GetInstance();
|
||||
|
||||
size_t count(EventFilter filter = EventFilter()) const {
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
return count_internal(filter);
|
||||
};
|
||||
|
||||
std::vector<APIEvent> get(EventFilter filter, size_t max = 0) { return get(max, filter); }
|
||||
std::vector<APIEvent> get(size_t max = 0, EventFilter filter = EventFilter()) {
|
||||
std::vector<APIEvent> ret;
|
||||
get(ret, filter, max);
|
||||
return ret;
|
||||
}
|
||||
void get(std::vector<APIEvent>& outEvents, EventFilter filter, size_t max = 0) { get(outEvents, max, filter); }
|
||||
void get(std::vector<APIEvent>& outEvents, size_t max = 0, EventFilter filter = EventFilter());
|
||||
|
||||
APIEvent getLastError();
|
||||
|
||||
void add(APIEvent event) {
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
add_internal(event);
|
||||
}
|
||||
void add(APIEvent::Type type, APIEvent::Severity severity, const Device* forDevice = nullptr) {
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
add_internal(APIEvent::APIEvent(type, severity, forDevice));
|
||||
}
|
||||
|
||||
void discard(EventFilter filter = EventFilter());
|
||||
|
||||
void setEventLimit(size_t newLimit) {
|
||||
if(newLimit == eventLimit)
|
||||
return;
|
||||
|
||||
if(newLimit < 10) {
|
||||
add(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::EventWarning);
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
eventLimit = newLimit;
|
||||
if(enforceLimit())
|
||||
add(APIEvent::Type::TooManyEvents, APIEvent::Severity::EventWarning);
|
||||
}
|
||||
|
||||
size_t getEventLimit() const { return eventLimit; }
|
||||
|
||||
private:
|
||||
EventManager() {}
|
||||
// Used by functions for threadsafety
|
||||
mutable std::mutex mutex;
|
||||
|
||||
// Stores all events
|
||||
std::list<APIEvent> events;
|
||||
std::unordered_map<std::thread::id, APIEvent> lastUserErrors;
|
||||
size_t eventLimit = 10000;
|
||||
|
||||
size_t count_internal(EventFilter filter = EventFilter()) const;
|
||||
|
||||
void add_internal(APIEvent event) {
|
||||
if(event.getSeverity() == APIEvent::Severity::Error)
|
||||
add_internal_error(event);
|
||||
else
|
||||
add_internal_event(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Places a {id, event} pair into the lastUserErrors
|
||||
* If the key id already exists in the map, replace the event of that pair with the new one
|
||||
*/
|
||||
void add_internal_error(APIEvent event) {
|
||||
auto iter = lastUserErrors.find(std::this_thread::get_id());
|
||||
if(iter == lastUserErrors.end())
|
||||
lastUserErrors.insert(std::make_pair(std::this_thread::get_id(), event));
|
||||
else
|
||||
iter->second = event;
|
||||
}
|
||||
|
||||
/**
|
||||
* If events is not full, add the event at the end
|
||||
* Otherwise, remove the least significant events, push the event to the back and push a APIEvent::TooManyEvents to the back (in that order)
|
||||
*/
|
||||
void add_internal_event(APIEvent event) {
|
||||
// Ensure the event list is at most exactly full (size of eventLimit - 1, leaving room for a potential APIEvent::TooManyEvents)
|
||||
enforceLimit();
|
||||
|
||||
// We are exactly full, either because the list was truncated or because we were simply full before
|
||||
if(events.size() == eventLimit - 1) {
|
||||
// If the event is worth adding
|
||||
if(event.getSeverity() >= lowestCurrentSeverity()) {
|
||||
discardLeastSevere(1);
|
||||
events.push_back(event);
|
||||
}
|
||||
|
||||
events.push_back(APIEvent(APIEvent::Type::TooManyEvents, APIEvent::Severity::EventWarning));
|
||||
} else {
|
||||
events.push_back(event);
|
||||
}
|
||||
}
|
||||
|
||||
bool enforceLimit(); // Returns whether the limit enforcement resulted in an overflow
|
||||
|
||||
APIEvent::Severity lowestCurrentSeverity();
|
||||
void discardLeastSevere(size_t count = 1);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user