Add error system

This commit is contained in:
Paul Hollinsky
2018-10-26 20:53:30 -04:00
parent a331a2afa8
commit 3a42372dcd
19 changed files with 691 additions and 72 deletions
+115
View File
@@ -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
+98
View File
@@ -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
+4 -1
View File
@@ -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;
+4
View File
@@ -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 {
+2 -1
View File
@@ -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;
};
}
+25 -5
View File
@@ -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>
+7 -5
View File
@@ -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; }
};
}
+2 -2
View File
@@ -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) {}
};
}
+1 -1
View File
@@ -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) {}