diff --git a/CMakeLists.txt b/CMakeLists.txt index d769479..0d5c7ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -238,6 +238,7 @@ set(SRC_FILES communication/message/flexray/control/flexraycontrolmessage.cpp communication/message/callback/streamoutput/a2bwavoutput.cpp communication/message/a2bmessage.cpp + communication/message/apperrormessage.cpp communication/message/neomessage.cpp communication/message/ethphymessage.cpp communication/message/linmessage.cpp @@ -519,6 +520,7 @@ if(LIBICSNEO_BUILD_UNIT_TESTS) test/unit/mdioencoderdecodertest.cpp test/unit/livedataencoderdecodertest.cpp test/unit/ringbuffertest.cpp + test/unit/apperrordecodertest.cpp ) target_link_libraries(libicsneo-unit-tests gtest gtest_main) diff --git a/communication/decoder.cpp b/communication/decoder.cpp index 9825e0c..1400c9c 100644 --- a/communication/decoder.cpp +++ b/communication/decoder.cpp @@ -18,6 +18,7 @@ #include "icsneo/communication/message/diskdatamessage.h" #include "icsneo/communication/message/hardwareinfo.h" #include "icsneo/communication/message/tc10statusmessage.h" +#include "icsneo/communication/message/apperrormessage.h" #include "icsneo/communication/command.h" #include "icsneo/device/device.h" #include "icsneo/communication/packet/canpacket.h" @@ -398,6 +399,14 @@ bool Decoder::decode(std::shared_ptr& result, const std::shared_ptrdata.resize(length); return decode(result, packet); } + case Network::NetID::RED_App_Error: { + result = AppErrorMessage::DecodeToMessage(packet->data, report); + if(!result) { + report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning); + return false; + } + return true; + } case Network::NetID::ReadSettings: { auto msg = std::make_shared(); msg->response = ReadSettingsMessage::Response(packet->data[0]); diff --git a/communication/message/apperrormessage.cpp b/communication/message/apperrormessage.cpp new file mode 100644 index 0000000..5486940 --- /dev/null +++ b/communication/message/apperrormessage.cpp @@ -0,0 +1,143 @@ +#include + +namespace icsneo { + +#pragma pack(push, 2) + +typedef struct { + uint16_t error_type; + uint16_t network_id; + uint32_t uiTimeStamp10uS; + uint32_t uiTimeStamp10uSMSB; +} AppErrorData; + +#pragma pack(pop) + +std::shared_ptr AppErrorMessage::DecodeToMessage(const std::vector& bytestream, const device_eventhandler_t& report) { + const AppErrorData* data = reinterpret_cast(bytestream.data()); + if(!data) { + report(APIEvent::Type::AppErrorParsingFailed, APIEvent::Severity::Error); + return nullptr; + } + auto appErr = std::make_shared(); + appErr->errorType = data->error_type; + appErr->errorNetID = static_cast(data->network_id); + appErr->timestamp10us = data->uiTimeStamp10uS; + appErr->timestamp10usMSB = data->uiTimeStamp10uSMSB; + appErr->network = Network::NetID::RED_App_Error; + return appErr; +} + +AppErrorType AppErrorMessage::getAppErrorType() { + AppErrorType errType = static_cast(errorType); + if(errType > AppErrorType::AppNoError) { + return AppErrorType::AppNoError; + } + return errType; +} + +std::string AppErrorMessage::getAppErrorString() { + auto netIDString = Network::GetNetIDString(errorNetID); + AppErrorType errType = static_cast(errorType); + switch (errType) { + case AppErrorType::AppErrorRxMessagesFull: + return std::string(netIDString) + ": RX message buffer full"; + case AppErrorType::AppErrorTxMessagesFull: + return std::string(netIDString) + ": TX message buffer full"; + case AppErrorType::AppErrorTxReportMessagesFull: + return std::string(netIDString) + ": TX report buffer full"; + case AppErrorType::AppErrorBadCommWithDspIC: + return "Received bad packet from DSP IC"; + case AppErrorType::AppErrorDriverOverflow: + return std::string(netIDString) + ": Driver overflow"; + case AppErrorType::AppErrorPCBuffOverflow: + return "PC buffer overflow"; + case AppErrorType::AppErrorPCChksumError: + return "PC checksum error"; + case AppErrorType::AppErrorPCMissedByte: + return "PC missed byte"; + case AppErrorType::AppErrorPCOverrunError: + return "PC overrun error"; + case AppErrorType::AppErrorSettingFailure: + return std::string(netIDString) + ": Settings incorrectly set"; + case AppErrorType::AppErrorTooManySelectedNetworks: + return "Too many selected networks"; + case AppErrorType::AppErrorNetworkNotEnabled: + return std::string(netIDString) + ": Network not enabled"; + case AppErrorType::AppErrorRtcNotCorrect: + return "RTC not correct"; + case AppErrorType::AppErrorLoadedDefaultSettings: + return "Loaded default settings"; + case AppErrorType::AppErrorFeatureNotUnlocked: + return "Feature not unlocked"; + case AppErrorType::AppErrorFeatureRtcCmdDropped: + return "RTC command dropped"; + case AppErrorType::AppErrorTxMessagesFlushed: + return "TX message buffer flushed"; + case AppErrorType::AppErrorTxMessagesHalfFull: + return "TX message buffer half full"; + case AppErrorType::AppErrorNetworkNotValid: + return "Network is not valid"; + case AppErrorType::AppErrorTxInterfaceNotImplemented: + return "TX interface is not implemented"; + case AppErrorType::AppErrorTxMessagesCommEnableIsOff: + return "TX message communication is disabled"; + case AppErrorType::AppErrorRxFilterMatchCountExceeded: + return "RX filter match count exceeded"; + case AppErrorType::AppErrorEthPreemptionNotEnabled: + return std::string(netIDString) + ": Ethernet preemption not enabled"; + case AppErrorType::AppErrorTxNotSupportedInMode: + return std::string(netIDString) + ": Transmit is not supported in this mode"; + case AppErrorType::AppErrorJumboFramesNotSupported: + return std::string(netIDString) + ": Jumbo frames not supported"; + case AppErrorType::AppErrorEthernetIpFragment: + return "Ethernet IP fragment received"; + case AppErrorType::AppErrorTxMessagesUnderrun: + return std::string(netIDString) + ": Transmit buffer underrun"; + case AppErrorType::AppErrorDeviceFanFailure: + return "Device fan failure"; + case AppErrorType::AppErrorDeviceOvertemperature: + return "Device overtemperature"; + case AppErrorType::AppErrorTxMessageIndexOutOfRange: + return "Transmit message index out of range"; + case AppErrorType::AppErrorUndersizedFrameDropped: + return std::string(netIDString) + ": Undersized frame dropped"; + case AppErrorType::AppErrorOversizedFrameDropped: + return std::string(netIDString) + ": Oversized frame dropped"; + case AppErrorType::AppErrorWatchdogEvent: + return "Watchdog event occured"; + case AppErrorType::AppErrorSystemClockFailure: + return "Device clock failed"; + case AppErrorType::AppErrorSystemClockRecovered: + return "Device clock recovered"; + case AppErrorType::AppErrorSystemPeripheralReset: + return "Device peripheral reset"; + case AppErrorType::AppErrorSystemCommunicationFailure: + return "Device communication failure"; + case AppErrorType::AppErrorTxMessagesUnsupportedSourceOrPacketId: + return std::string(netIDString) + ": Transmit unsupported source or packet ID"; + case AppErrorType::AppErrorWbmsManagerConnectFailed: + return std::string(netIDString) + ": Failed to connect to managers with settings"; + case AppErrorType::AppErrorWbmsManagerConnectBadState: + return std::string(netIDString) + ": Connected to managers in a invalid state"; + case AppErrorType::AppErrorWbmsManagerConnectTimeout: + return std::string(netIDString) + ": Timeout while attempting to connect to managers"; + case AppErrorType::AppErrorFailedToInitializeLoggerDisk: + return "Device failed to initialize storage disk"; + case AppErrorType::AppErrorInvalidSetting: + return std::string(netIDString) + ": Invalid settings"; + case AppErrorType::AppErrorSystemFailureRequestedReset: + return "Device rebooted to recover from an unexpected error condition"; + case AppErrorType::AppErrorPortKeyMistmatch: + return std::string(netIDString) + ": Mismatch between key in manager and stored key"; + case AppErrorType::AppErrorErrorBufferOverflow: + return "Device error buffer overflow"; + case AppErrorType::AppNoError: + return "No error"; + default: + return "Unknown error"; + } + return "Unknown error"; +} + +} // namespace icsneo \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index a127c94..6379bdb 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -9,6 +9,7 @@ option(LIBICSNEO_BUILD_CPP_LIVEDATA_EXAMPLE "Build the Live Data example." ON) option(LIBICSNEO_BUILD_CPP_COREMINI_EXAMPLE "Build the Coremini example." ON) option(LIBICSNEO_BUILD_CPP_MDIO_EXAMPLE "Build the MDIO example." ON) option(LIBICSNEO_BUILD_CPP_VSA_EXAMPLE "Build the VSA example." ON) +option(LIBICSNEO_BUILD_CPP_APP_ERROR_EXAMPLE "Build the app error example." ON) # Disabled until we properly build these in-tree # option(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE "Build the command-line interactive C# example." OFF) @@ -58,6 +59,10 @@ if(LIBICSNEO_BUILD_CPP_VSA_EXAMPLE) add_subdirectory(cpp/vsa) endif() +if(LIBICSNEO_BUILD_CPP_APP_ERROR_EXAMPLE) + add_subdirectory(cpp/apperror) +endif() + # if(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE) # add_subdirectory(csharp) # endif() diff --git a/examples/cpp/apperror/CMakeLists.txt b/examples/cpp/apperror/CMakeLists.txt new file mode 100644 index 0000000..1a810a2 --- /dev/null +++ b/examples/cpp/apperror/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(libicsneocpp-app-error src/AppErrorExample.cpp) +target_link_libraries(libicsneocpp-app-error icsneocpp) \ No newline at end of file diff --git a/examples/cpp/apperror/src/AppErrorExample.cpp b/examples/cpp/apperror/src/AppErrorExample.cpp new file mode 100644 index 0000000..2854a87 --- /dev/null +++ b/examples/cpp/apperror/src/AppErrorExample.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include + +#include "icsneo/icsneocpp.h" +#include "icsneo/communication/message/apperrormessage.h" +#include "icsneo/communication/message/message.h" +/* + * App errors are responses from the device indicating internal runtime errors + * NOTE: To trigger the app error in this example, disable the HSCAN network on the device + * (e.g. with neoVI Explorer) + */ +int main() { + std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl; + std::cout << "\nFinding devices... " << std::flush; + auto devices = icsneo::FindAllDevices(); + std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl; + + // List off the devices + for(auto& device : devices) + std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl; + std::cout << std::endl; + + for(auto device : devices) { + std::cout << "Connecting to " << device->describe() << "... "; + bool ret = device->open(); + if(!ret) { // Failed to open + std::cout << "FAIL" << std::endl; + std::cout << icsneo::GetLastError() << std::endl << std::endl; + continue; + } + std::cout << "OK" << std::endl << std::endl; + + // Create an app error message filter, including "internal" messages + auto filter = std::make_shared(icsneo::Message::Type::AppError); + filter->includeInternalInAny = true; + + // ...and register a callback with it. + // Add your error handling here + auto handler = device->addMessageCallback(std::make_shared(filter, [](std::shared_ptr message) { + auto msg = std::static_pointer_cast(message); + if(icsneo::Network::NetID::RED_App_Error == msg->network.getNetID()) { + std::cout << std::endl << "App error reported:" << std::endl; + std::cout << msg->getAppErrorString() << std::endl << std::endl; + } + })); + + std::cout << "Going online... "; + ret = device->goOnline(); + if(!ret) { + std::cout << "FAIL" << std::endl; + device->close(); + continue; + } + std::cout << "OK" << std::endl; + + // Prepare a CAN message + std::cout << std::endl << "Transmitting a CAN frame... "; + auto txMessage = std::make_shared(); + txMessage->network = icsneo::Network::NetID::HSCAN; + txMessage->arbid = 0x22; + txMessage->data.insert(txMessage->data.end(), {0xaa, 0xbb, 0xcc}); + // The DLC will come from the length of the data vector + txMessage->isExtended = false; + txMessage->isCANFD = false; + + // Transmit a CAN message on HSCAN, even though HSCAN is disabled on the device! + // Expect to see an app error caught in the callback defined above + ret = device->transmit(txMessage); + std::cout << (ret ? "OK" : "FAIL") << std::endl; + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Go offline, stop sending and receiving traffic + device->removeMessageCallback(handler); + std::cout << "Going offline... "; + ret = device->goOffline(); + std::cout << (ret ? "OK" : "FAIL") << std::endl; + std::cout << "Disconnecting... "; + ret = device->close(); + std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl; + } + return 0; +} \ No newline at end of file diff --git a/include/icsneo/api/event.h b/include/icsneo/api/event.h index bfc3da2..f8c1539 100644 --- a/include/icsneo/api/event.h +++ b/include/icsneo/api/event.h @@ -108,6 +108,7 @@ public: LiveDataNotSupported = 0x2052, LINSettingsNotAvailable = 0x2053, ModeNotFound = 0x2054, + AppErrorParsingFailed = 0x2055, // Transport Events FailedToRead = 0x3000, diff --git a/include/icsneo/communication/message/apperrormessage.h b/include/icsneo/communication/message/apperrormessage.h new file mode 100644 index 0000000..ba8ceef --- /dev/null +++ b/include/icsneo/communication/message/apperrormessage.h @@ -0,0 +1,80 @@ +#ifndef __APPERRORMESSAGE_H_ +#define __APPERRORMESSAGE_H_ + +#ifdef __cplusplus + +#include "icsneo/communication/message/message.h" +#include +#include +#include "icsneo/api/eventmanager.h" + + +namespace icsneo { + +enum class AppErrorType : uint16_t { + AppErrorRxMessagesFull = 0, + AppErrorTxMessagesFull = 1, + AppErrorTxReportMessagesFull = 2, + AppErrorBadCommWithDspIC = 3, + AppErrorDriverOverflow = 4, + AppErrorPCBuffOverflow = 5, + AppErrorPCChksumError = 6, + AppErrorPCMissedByte = 7, + AppErrorPCOverrunError = 8, + AppErrorSettingFailure = 9, + AppErrorTooManySelectedNetworks = 10, + AppErrorNetworkNotEnabled = 11, + AppErrorRtcNotCorrect = 12, + AppErrorLoadedDefaultSettings = 13, + AppErrorFeatureNotUnlocked = 14, + AppErrorFeatureRtcCmdDropped = 15, + AppErrorTxMessagesFlushed = 16, + AppErrorTxMessagesHalfFull = 17, + AppErrorNetworkNotValid = 18, + AppErrorTxInterfaceNotImplemented = 19, + AppErrorTxMessagesCommEnableIsOff = 20, + AppErrorRxFilterMatchCountExceeded = 21, + AppErrorEthPreemptionNotEnabled = 22, + AppErrorTxNotSupportedInMode = 23, + AppErrorJumboFramesNotSupported = 24, + AppErrorEthernetIpFragment = 25, + AppErrorTxMessagesUnderrun = 26, + AppErrorDeviceFanFailure = 27, + AppErrorDeviceOvertemperature = 28, + AppErrorTxMessageIndexOutOfRange = 29, + AppErrorUndersizedFrameDropped = 30, + AppErrorOversizedFrameDropped = 31, + AppErrorWatchdogEvent = 32, + AppErrorSystemClockFailure = 33, + AppErrorSystemClockRecovered = 34, + AppErrorSystemPeripheralReset = 35, + AppErrorSystemCommunicationFailure = 36, + AppErrorTxMessagesUnsupportedSourceOrPacketId = 37, + AppErrorWbmsManagerConnectFailed = 38, + AppErrorWbmsManagerConnectBadState = 39, + AppErrorWbmsManagerConnectTimeout = 40, + AppErrorFailedToInitializeLoggerDisk = 41, + AppErrorInvalidSetting = 42, + AppErrorSystemFailureRequestedReset = 43, + AppErrorPortKeyMistmatch = 45, + AppErrorErrorBufferOverflow = 254, + AppNoError = 255 +}; + +class AppErrorMessage : public RawMessage { +public: + AppErrorMessage() : RawMessage(Message::Type::AppError, Network::NetID::RED_App_Error) {} + uint16_t errorType; + Network::NetID errorNetID; + uint32_t timestamp10us; + uint32_t timestamp10usMSB; + + static std::shared_ptr DecodeToMessage(const std::vector& bytestream, const device_eventhandler_t& report); + AppErrorType getAppErrorType(); + std::string getAppErrorString(); +}; + +} // namespace icsneo + +#endif // __cplusplus +#endif \ No newline at end of file diff --git a/include/icsneo/communication/message/message.h b/include/icsneo/communication/message/message.h index 7c5be97..259ce7a 100644 --- a/include/icsneo/communication/message/message.h +++ b/include/icsneo/communication/message/message.h @@ -40,6 +40,7 @@ public: LiveData = 0x800f, HardwareInfo = 0x8010, TC10Status = 0x8011, + AppError = 0x8012, }; Message(Type t) : type(t) {} diff --git a/include/icsneo/communication/network.h b/include/icsneo/communication/network.h index 2bc4596..613d3fc 100644 --- a/include/icsneo/communication/network.h +++ b/include/icsneo/communication/network.h @@ -537,6 +537,7 @@ public: case NetID::NeoMemoryWriteDone: case NetID::RED_GET_RTC: case NetID::DiskData: + case NetID::RED_App_Error: return Type::Internal; case NetID::Invalid: case NetID::Any: diff --git a/test/unit/apperrordecodertest.cpp b/test/unit/apperrordecodertest.cpp new file mode 100644 index 0000000..f0919ce --- /dev/null +++ b/test/unit/apperrordecodertest.cpp @@ -0,0 +1,101 @@ +#include "icsneo/icsneocpp.h" +#include "icsneo/communication/encoder.h" +#include "icsneo/communication/message/apperrormessage.h" +#include "icsneo/communication/packetizer.h" +#include "icsneo/device/tree/neovired2/neovired2.h" +#include "icsneo/api/eventmanager.h" +#include "gtest/gtest.h" +#include +#include + +using namespace icsneo; + +class REDAppErrorDecoderTest : public ::testing::Test { +protected: + void SetUp() override { + report = [](APIEvent::Type, APIEvent::Severity) { + // Unless caught by the test, the packetizer should not throw errors + EXPECT_TRUE(false); + }; + + packetizer.emplace([this](APIEvent::Type t, APIEvent::Severity s) { report(t, s); }); + packetEncoder.emplace([this](APIEvent::Type t, APIEvent::Severity s) { report(t, s); }); + packetDecoder.emplace([this](APIEvent::Type t, APIEvent::Severity s) { report(t, s); }); + } + + device_eventhandler_t report; + std::optional packetEncoder; + std::optional packetizer; + std::optional packetDecoder; + + RingBuffer ringBuffer = RingBuffer(128); + + std::vector testErrorData = + {0xaa, 0x0c, + 0x12, 0x00, //size + 0x34, 0x00, //netID + 0x0b, 0x00, //error_type + 0x01, 0x00, //network_id + 0x33, 0x44, 0x55, 0x66, //uiTime10us + 0x77, 0x88, 0x99, 0xAA, //uiTime10usMSB + }; +}; + +TEST_F(REDAppErrorDecoderTest, PacketDecoderTest) { + std::shared_ptr decodeMsg; + + auto msg1 = std::make_shared(); + msg1->errorType = static_cast(AppErrorType::AppErrorNetworkNotEnabled); + msg1->errorNetID = Network::NetID::HSCAN; + msg1->timestamp10us = 0x66554433; + msg1->timestamp10usMSB = 0xAA998877; + msg1->network = icsneo::Network::NetID::RED_App_Error; + + ringBuffer.clear(); + ringBuffer.write(testErrorData); + + EXPECT_TRUE(packetizer->input(ringBuffer)); + auto packets = packetizer->output(); + + EXPECT_TRUE(packetDecoder->decode(decodeMsg, packets.back())); + EXPECT_NE(decodeMsg, nullptr); + auto testMessage = std::dynamic_pointer_cast(decodeMsg); + EXPECT_EQ(msg1->network, testMessage->network); + EXPECT_EQ(msg1->errorType, testMessage->errorType); + EXPECT_EQ(msg1->errorNetID, testMessage->errorNetID); + EXPECT_EQ(msg1->timestamp10us, testMessage->timestamp10us); + EXPECT_EQ(msg1->timestamp10usMSB, testMessage->timestamp10usMSB); + packets.pop_back(); +} + +TEST_F(REDAppErrorDecoderTest, GetErrorStringTest) { + std::shared_ptr decodeMsg; + + ringBuffer.clear(); + ringBuffer.write(testErrorData); + + EXPECT_TRUE(packetizer->input(ringBuffer)); + auto packets = packetizer->output(); + + EXPECT_TRUE(packetDecoder->decode(decodeMsg, packets.back())); + EXPECT_NE(decodeMsg, nullptr); + auto testMessage = std::dynamic_pointer_cast(decodeMsg); + EXPECT_EQ("HSCAN: Network not enabled", testMessage->getAppErrorString()); + packets.pop_back(); +} + +TEST_F(REDAppErrorDecoderTest, GetTypeTest) { + std::shared_ptr decodeMsg; + + ringBuffer.clear(); + ringBuffer.write(testErrorData); + + EXPECT_TRUE(packetizer->input(ringBuffer)); + auto packets = packetizer->output(); + + EXPECT_TRUE(packetDecoder->decode(decodeMsg, packets.back())); + EXPECT_NE(decodeMsg, nullptr); + auto testMessage = std::dynamic_pointer_cast(decodeMsg); + EXPECT_EQ(AppErrorType::AppErrorNetworkNotEnabled, testMessage->getAppErrorType()); + packets.pop_back(); +}