mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-08-05 01:18:36 +02:00
Add error system
This commit is contained in:
@@ -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/message/callback/messagecallback.h"
|
||||
#include "icsneo/communication/message/serialnumbermessage.h"
|
||||
#include "icsneo/api/errormanager.h"
|
||||
#include "icsneo/communication/packetizer.h"
|
||||
#include "icsneo/communication/encoder.h"
|
||||
#include "icsneo/communication/decoder.h"
|
||||
@@ -22,10 +23,11 @@ namespace icsneo {
|
||||
class Communication {
|
||||
public:
|
||||
Communication(
|
||||
device_errorhandler_t err,
|
||||
std::unique_ptr<ICommunication> com,
|
||||
std::shared_ptr<Packetizer> p,
|
||||
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(); }
|
||||
|
||||
bool open();
|
||||
@@ -50,6 +52,7 @@ public:
|
||||
std::shared_ptr<Packetizer> packetizer; // Ownership is shared with the encoder
|
||||
std::unique_ptr<Encoder> encoder;
|
||||
std::unique_ptr<Decoder> decoder;
|
||||
device_errorhandler_t err;
|
||||
|
||||
protected:
|
||||
std::unique_ptr<ICommunication> impl;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "icsneo/communication/message/canmessage.h"
|
||||
#include "icsneo/communication/packet.h"
|
||||
#include "icsneo/communication/network.h"
|
||||
#include "icsneo/api/errormanager.h"
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
@@ -16,9 +17,12 @@ namespace icsneo {
|
||||
class Decoder {
|
||||
public:
|
||||
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);
|
||||
|
||||
private:
|
||||
device_errorhandler_t err;
|
||||
typedef uint16_t icscm_bitfield;
|
||||
struct HardwareCANPacket {
|
||||
struct {
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace icsneo {
|
||||
|
||||
class Encoder {
|
||||
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, 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 = {});
|
||||
@@ -23,6 +23,7 @@ public:
|
||||
bool supportCANFD = false;
|
||||
private:
|
||||
std::shared_ptr<Packetizer> packetizer;
|
||||
device_errorhandler_t err;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -11,10 +11,11 @@ namespace icsneo {
|
||||
class MultiChannelCommunication : public Communication {
|
||||
public:
|
||||
MultiChannelCommunication(
|
||||
device_errorhandler_t err,
|
||||
std::unique_ptr<ICommunication> com,
|
||||
std::shared_ptr<Packetizer> p,
|
||||
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 joinThreads() override;
|
||||
bool sendPacket(std::vector<uint8_t>& bytes) override;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define __PACKETIZER_H_
|
||||
|
||||
#include "icsneo/communication/packet.h"
|
||||
#include "icsneo/api/errormanager.h"
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
@@ -11,6 +12,9 @@ namespace icsneo {
|
||||
class Packetizer {
|
||||
public:
|
||||
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);
|
||||
|
||||
bool input(const std::vector<uint8_t>& bytes);
|
||||
@@ -37,6 +41,8 @@ private:
|
||||
std::deque<uint8_t> bytes;
|
||||
|
||||
std::vector<std::shared_ptr<Packet>> processedPackets;
|
||||
|
||||
device_errorhandler_t err;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
#include "icsneo/api/errormanager.h"
|
||||
#include "icsneo/device/neodevice.h"
|
||||
#include "icsneo/device/idevicesettings.h"
|
||||
#include "icsneo/device/nullsettings.h"
|
||||
@@ -71,6 +72,7 @@ protected:
|
||||
int messagePollingCallbackID = 0;
|
||||
int internalHandlerCallbackID = 0;
|
||||
std::shared_ptr<Communication> com;
|
||||
device_errorhandler_t err;
|
||||
|
||||
// START Initialization Functions
|
||||
Device(neodevice_t neodevice = { 0 }) {
|
||||
@@ -79,26 +81,44 @@ protected:
|
||||
}
|
||||
|
||||
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>
|
||||
std::unique_ptr<ICommunication> makeTransport() { return std::unique_ptr<ICommunication>(new Transport(getWritableNeoDevice())); }
|
||||
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 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 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 std::shared_ptr<Communication> makeCommunication(
|
||||
std::unique_ptr<ICommunication> t,
|
||||
std::shared_ptr<Packetizer> p,
|
||||
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) {}
|
||||
|
||||
template<typename Settings>
|
||||
|
||||
@@ -283,11 +283,7 @@ public:
|
||||
static constexpr uint16_t GS_VERSION = 5;
|
||||
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
|
||||
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) {}
|
||||
IDeviceSettings(std::shared_ptr<Communication> com, size_t size) : com(com), err(com->err), structSize(size) {}
|
||||
virtual ~IDeviceSettings() {}
|
||||
bool ok() { return !disabled && settingsLoaded; }
|
||||
|
||||
@@ -313,9 +309,15 @@ public:
|
||||
bool readonly = false;
|
||||
protected:
|
||||
std::shared_ptr<Communication> com;
|
||||
device_errorhandler_t err;
|
||||
size_t structSize;
|
||||
bool settingsLoaded = false;
|
||||
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 {
|
||||
public:
|
||||
// Calls the base constructor with "createInoperableSettings"
|
||||
NullSettings(std::shared_ptr<Communication> com = std::shared_ptr<Communication>()) : IDeviceSettings(nullptr) { (void)com; }
|
||||
// Calls the protected base constructor with "createInoperableSettings"
|
||||
NullSettings(std::shared_ptr<Communication> com) : IDeviceSettings(nullptr, com) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ protected:
|
||||
std::shared_ptr<Packetizer> packetizer,
|
||||
std::unique_ptr<Encoder> encoder,
|
||||
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:
|
||||
Plasion(neodevice_t neodevice) : Device(neodevice) {}
|
||||
|
||||
Reference in New Issue
Block a user