diff --git a/api/icsneoc2/icsneoc2messages.cpp b/api/icsneoc2/icsneoc2messages.cpp index 99b98d8..f577ff5 100644 --- a/api/icsneoc2/icsneoc2messages.cpp +++ b/api/icsneoc2/icsneoc2messages.cpp @@ -6,6 +6,7 @@ #include "icsneo/icsneocpp.h" #include "icsneo/communication/message/message.h" #include "icsneo/communication/message/canmessage.h" +#include "icsneo/communication/message/canerrormessage.h" #include "icsneo/communication/message/linmessage.h" #include "icsneo/communication/message/ethernetmessage.h" #include "icsneo/communication/packet/canpacket.h" @@ -34,6 +35,19 @@ icsneoc2_error_t icsneoc2_message_is_transmit(icsneoc2_message_t* message, bool* return icsneoc2_error_success; } +icsneoc2_error_t icsneoc2_message_is_error(icsneoc2_message_t* message, bool* value) { + if(!message || !value) { + return icsneoc2_error_invalid_parameters; + } + auto frame = std::dynamic_pointer_cast(message->message); + if(!frame) { + return icsneoc2_error_invalid_type; + } + *value = frame->error; + + return icsneoc2_error_success; +} + icsneoc2_error_t icsneoc2_message_netid_get(icsneoc2_message_t* message, icsneoc2_netid_t* netid) { if(!message || !netid) { return icsneoc2_error_invalid_parameters; @@ -176,6 +190,15 @@ icsneoc2_error_t icsneoc2_message_can_props_get(icsneoc2_message_t* message, uin if(can_msg->errorStateIndicator) { *flags |= ICSNEOC2_MESSAGE_CAN_FLAGS_ESI; } + if(can_msg->txAborted) { + *flags |= ICSNEOC2_MESSAGE_CAN_FLAGS_TX_ABORTED; + } + if(can_msg->txLostArb) { + *flags |= ICSNEOC2_MESSAGE_CAN_FLAGS_TX_LOST_ARB; + } + if(can_msg->txError) { + *flags |= ICSNEOC2_MESSAGE_CAN_FLAGS_TX_ERROR; + } } return icsneoc2_error_success; } @@ -214,7 +237,53 @@ icsneoc2_error_t icsneoc2_message_is_can(icsneoc2_message_t* message, bool* is_c } *is_can = std::dynamic_pointer_cast(message->message) != nullptr; return icsneoc2_error_success; +} +icsneoc2_error_t icsneoc2_message_is_can_error(icsneoc2_message_t* message, bool* is_can_error) { + if(!message || !is_can_error) { + return icsneoc2_error_invalid_parameters; + } + *is_can_error = std::dynamic_pointer_cast(message->message) != nullptr; + return icsneoc2_error_success; +} + +icsneoc2_error_t icsneoc2_message_can_error_props_get( + icsneoc2_message_t *message, uint8_t *tx_err_count, uint8_t *rx_err_count, + icsneoc2_can_error_code_t *error_code, + icsneoc2_can_error_code_t *data_error_code, + icsneoc2_message_can_error_flags_t *flags) { + if(!message) { + return icsneoc2_error_invalid_parameters; + } + auto can_err = std::dynamic_pointer_cast(message->message); + if(!can_err) { + return icsneoc2_error_invalid_type; + } + if(tx_err_count) { + *tx_err_count = can_err->transmitErrorCount; + } + if(rx_err_count) { + *rx_err_count = can_err->receiveErrorCount; + } + if(error_code) { + *error_code = static_cast(can_err->errorCode); + } + if(data_error_code) { + *data_error_code = static_cast(can_err->dataErrorCode); + } + if(flags) { + *flags = 0; + if(can_err->busOff) { + *flags |= ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_BUS_OFF; + } + if(can_err->errorPassive) { + *flags |= ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_ERROR_PASSIVE; + } + if(can_err->errorWarn) { + *flags |= ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_ERROR_WARN; + } + } + return icsneoc2_error_success; } icsneoc2_error_t icsneoc2_message_is_lin(icsneoc2_message_t* message, bool* is_lin) { diff --git a/bindings/python/icsneopy/communication/message/canmessage.cpp b/bindings/python/icsneopy/communication/message/canmessage.cpp index ece6608..2a13878 100644 --- a/bindings/python/icsneopy/communication/message/canmessage.cpp +++ b/bindings/python/icsneopy/communication/message/canmessage.cpp @@ -15,7 +15,10 @@ void init_canmessage(pybind11::module_& m) { .def_readwrite("isExtended", &CANMessage::isExtended) .def_readwrite("isCANFD", &CANMessage::isCANFD) .def_readwrite("baudrateSwitch", &CANMessage::baudrateSwitch) - .def_readwrite("errorStateIndicator", &CANMessage::errorStateIndicator); + .def_readwrite("errorStateIndicator", &CANMessage::errorStateIndicator) + .def_readwrite("txAborted", &CANMessage::txAborted) + .def_readwrite("txLostArb", &CANMessage::txLostArb) + .def_readwrite("txError", &CANMessage::txError); } } // namespace icsneo diff --git a/communication/packet/canpacket.cpp b/communication/packet/canpacket.cpp index 85e559c..234c95f 100644 --- a/communication/packet/canpacket.cpp +++ b/communication/packet/canpacket.cpp @@ -119,7 +119,12 @@ std::shared_ptr HardwareCANPacket::DecodeToMessage(const std::vectortransmitted = data->eid.TXMSG; + // Set the generic frame error state msg->error = data->eid.TXAborted || data->eid.TXError || data->eid.TXLostArb; + // Set specific error states for CANError + msg->txAborted = data->eid.TXAborted; + msg->txLostArb = data->eid.TXLostArb; + msg->txError = data->eid.TXError; msg->description = data->stats; return msg; diff --git a/examples/c2/read_messages/src/main.c b/examples/c2/read_messages/src/main.c index cad6b6f..4fdba3a 100644 --- a/examples/c2/read_messages/src/main.c +++ b/examples/c2/read_messages/src/main.c @@ -69,6 +69,8 @@ int print_error_code(const char* message, icsneoc2_error_t error) { */ int process_message(icsneoc2_message_t** messages, size_t messages_count); +int transmit_can_messages(icsneoc2_device_t* device); + int main() { // Open the first available device with default options printf("Opening first available device...\n"); @@ -88,6 +90,10 @@ int main() { }; printf("\tOpened device: %s\n", description); + // Transmit messages for debugging purposes + // transmit_can_messages(open_device); + // sleep_ms(1000); + // Get the messages icsneoc2_message_t* messages[20000] = {0}; size_t message_count = 20000; @@ -169,10 +175,49 @@ void print_events(const char* device_description) { int process_message(icsneoc2_message_t** messages, size_t messages_count) { // Print the type and bus type of each message size_t tx_count = 0; + size_t can_error_count = 0; + icsneoc2_error_t res = icsneoc2_error_success; for(size_t i = 0; i < messages_count; i++) { icsneoc2_message_t* message = messages[i]; + + bool is_can_error = false; + res = icsneoc2_message_is_can_error(message, &is_can_error); + if(res != icsneoc2_error_success) { + return print_error_code("\tFailed to check if message is a CAN error", res); + } + if(is_can_error) { + icsneoc2_network_type_t network_type; + uint8_t tec = 0; + uint8_t rec = 0; + icsneoc2_can_error_code_t error_code = 0; + icsneoc2_can_error_code_t data_error_code = 0; + icsneoc2_message_can_error_flags_t error_flags = 0; + icsneoc2_netid_t netid = 0; + char network_type_name[128] = {0}; + size_t network_type_name_length = 128; + char netid_name[128] = {0}; + size_t netid_name_length = 128; + + res = icsneoc2_message_network_type_get(message, &network_type); + res += icsneoc2_network_type_name_get(network_type, network_type_name, &network_type_name_length); + res += icsneoc2_message_netid_get(message, &netid); + res += icsneoc2_netid_name_get(netid, netid_name, &netid_name_length); + res += icsneoc2_message_can_error_props_get(message, &tec, &rec, &error_code, &data_error_code, &error_flags); + if(res != icsneoc2_error_success) { + return print_error_code("\tFailed to get CAN error properties", res); + } + + printf("\t%zd) CAN Error on %s [%s] (0x%x): TEC=%u REC=%u ErrorCode=%u DataErrorCode=%u%s%s%s\n", + i, netid_name, network_type_name, netid, tec, rec, error_code, data_error_code, + (error_flags & ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_BUS_OFF) ? " [BusOff]" : "", + (error_flags & ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_ERROR_PASSIVE) ? " [ErrorPassive]" : "", + (error_flags & ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_ERROR_WARN) ? " [ErrorWarn]" : ""); + can_error_count++; + continue; + } + bool is_frame = false; - icsneoc2_error_t res = icsneoc2_message_is_frame(message, &is_frame); + res = icsneoc2_message_is_frame(message, &is_frame); if(res != icsneoc2_error_success) { return print_error_code("\tFailed to check if message is a frame", res); } @@ -192,37 +237,55 @@ int process_message(icsneoc2_message_t** messages, size_t messages_count) { if(res != icsneoc2_error_success) { return print_error_code("\tFailed to get message bus type name", res); } - bool is_tx = false; - res = icsneoc2_message_is_transmit(message, &is_tx); + if(res != icsneoc2_error_success) { return print_error_code("\tFailed to get message is transmit", res); } - if(is_tx) { - tx_count++; - continue; - } printf("\t%zd) network type: %s (%u)\n", i, network_type_name, network_type); - if(network_type == icsneoc2_network_type_can) { - uint32_t arbid = 0; + uint64_t arbid = 0; int32_t dlc = 0; icsneoc2_netid_t netid = 0; - bool is_remote = false; - bool is_canfd = false; - bool is_extended = false; + icsneoc2_message_can_flags_t can_flags = 0; uint8_t data[64] = {0}; size_t data_length = 64; char netid_name[128] = {0}; size_t netid_name_length = 128; + bool is_error = false; + bool is_tx = false; icsneoc2_error_t result = icsneoc2_message_netid_get(message, &netid); result += icsneoc2_netid_name_get(netid, netid_name, &netid_name_length); + result += icsneoc2_message_can_props_get(message, &arbid, &can_flags); result += icsneoc2_message_data_get(message, data, &data_length); + result += icsneoc2_message_is_transmit(message, &is_tx); + result += icsneoc2_message_is_error(message, &is_error); if(result != icsneoc2_error_success) { printf("\tFailed get get CAN parameters (error: %u) for index %zu\n", result, i); continue; } - printf("\t NetID: %s (0x%x)\tArbID: 0x%x\t DLC: %u\t Remote: %d\t CANFD: %d\t Extended: %d\t Data length: %zu\n", netid_name, netid, arbid, dlc, is_remote, is_canfd, is_extended, data_length); + tx_count += is_tx ? 1 : 0; + bool is_remote = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_RTR) != 0; + bool is_extended = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_IDE) != 0; + bool is_canfd = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_FDF) != 0; + bool is_brs = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_BRS) != 0; + bool is_esi = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_ESI) != 0; + bool tx_aborted = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_TX_ABORTED) != 0; + bool tx_lost_arb = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_TX_LOST_ARB) != 0; + bool tx_error = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_TX_ERROR) != 0; + dlc = (int32_t)data_length; + + printf("\t %s%s\n", is_tx ? "TX" : "RX", is_error ? " [Error]" : ""); + printf("\t NetID: %s (0x%x)\tArbID: 0x%llx\tDLC: %u\tLen: %zu\n", netid_name, netid, (unsigned long long)arbid, dlc, data_length); + printf("\t Flags:%s%s%s%s%s%s%s%s\n", + is_remote ? " RTR" : "", + is_extended ? " IDE" : "", + is_canfd ? " FDF" : "", + is_brs ? " BRS" : "", + is_esi ? " ESI" : "", + tx_aborted ? " TX_ABORTED" : "", + tx_lost_arb ? " TX_LOST_ARB" : "", + tx_error ? " TX_ERROR" : ""); printf("\t Data: ["); for(size_t x = 0; x < data_length; x++) { printf(" 0x%x", data[x]); @@ -230,14 +293,14 @@ int process_message(icsneoc2_message_t** messages, size_t messages_count) { printf(" ]\n"); } } - printf("\tReceived %zu messages total, %zu were TX messages\n", messages_count, tx_count); + printf("\tReceived %zu messages total, %zu were TX messages, %zu were CAN errors\n", messages_count, tx_count, can_error_count); return icsneoc2_error_success; } int transmit_can_messages(icsneoc2_device_t* device) { uint64_t counter = 0; - const size_t msg_count = 100; + const size_t msg_count = 10; printf("\tTransmitting %zd messages...\n", msg_count); for(size_t i = 0; i < msg_count; i++) { // Create the message @@ -248,8 +311,12 @@ int transmit_can_messages(icsneoc2_device_t* device) { } // Set the message attributes res = icsneoc2_message_netid_set(message, icsneoc2_netid_dwcan_01); + uint64_t arb_id = 0x10; + uint64_t flags = 0; + res += icsneoc2_message_can_props_set(message, &arb_id, &flags); res += icsneoc2_message_data_set(message, (uint8_t*)&counter, sizeof(counter)); if(res != icsneoc2_error_success) { + icsneoc2_message_free(message); return print_error_code("\tFailed to modify message", res); } res = icsneoc2_device_message_transmit(device, message); diff --git a/examples/c2/simple/src/main.c b/examples/c2/simple/src/main.c index 0445f3d..abe4e00 100644 --- a/examples/c2/simple/src/main.c +++ b/examples/c2/simple/src/main.c @@ -358,10 +358,42 @@ void print_events(const char* device_description) { int process_messages(icsneoc2_message_t** messages, size_t messages_count) { // Print the type and bus type of each message size_t tx_count = 0; + size_t can_error_count = 0; for(size_t i = 0; i < messages_count; i++) { icsneoc2_message_t* message = messages[i]; + + // Check for CAN error messages + bool is_can_error = false; + icsneoc2_error_t res = icsneoc2_message_is_can_error(message, &is_can_error); + if(res != icsneoc2_error_success) { + return print_error_code("\tFailed to check if message is a CAN error", res); + } + if(is_can_error) { + uint8_t tec = 0; + uint8_t rec = 0; + icsneoc2_can_error_code_t error_code = 0; + icsneoc2_can_error_code_t data_error_code = 0; + icsneoc2_message_can_error_flags_t error_flags = 0; + icsneoc2_netid_t netid = 0; + res = icsneoc2_message_netid_get(message, &netid); + res += icsneoc2_message_can_error_props_get(message, &tec, &rec, &error_code, &data_error_code, &error_flags); + if(res != icsneoc2_error_success) { + return print_error_code("\tFailed to get CAN error properties", res); + } + char netid_name[128] = {0}; + size_t netid_name_length = 128; + icsneoc2_netid_name_get(netid, netid_name, &netid_name_length); + printf("\t%zd) CAN Error on %s (0x%x): TEC=%u REC=%u ErrorCode=%u DataErrorCode=%u%s%s%s\n", + i, netid_name, netid, tec, rec, error_code, data_error_code, + (error_flags & ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_BUS_OFF) ? " [BusOff]" : "", + (error_flags & ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_ERROR_PASSIVE) ? " [ErrorPassive]" : "", + (error_flags & ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_ERROR_WARN) ? " [ErrorWarn]" : ""); + can_error_count++; + continue; + } + bool is_frame = false; - icsneoc2_error_t res = icsneoc2_message_is_frame(message, &is_frame); + res = icsneoc2_message_is_frame(message, &is_frame); if(res != icsneoc2_error_success) { return print_error_code("\tFailed to check if message is a frame", res); } @@ -394,24 +426,42 @@ int process_messages(icsneoc2_message_t** messages, size_t messages_count) { printf("\t%zd) network type: %s (%u)\n", i, network_type_name, network_type); if(network_type == icsneoc2_network_type_can) { - uint32_t arbid = 0; + uint64_t arbid = 0; int32_t dlc = 0; icsneoc2_netid_t netid = 0; - bool is_remote = false; - bool is_canfd = false; - bool is_extended = false; + icsneoc2_message_can_flags_t can_flags = 0; uint8_t data[64] = {0}; size_t data_length = 64; char netid_name[128] = {0}; size_t netid_name_length = 128; icsneoc2_error_t result = icsneoc2_message_netid_get(message, &netid); result += icsneoc2_netid_name_get(netid, netid_name, &netid_name_length); + result += icsneoc2_message_can_props_get(message, &arbid, &can_flags); result += icsneoc2_message_data_get(message, data, &data_length); if(result != icsneoc2_error_success) { printf("\tFailed get get CAN parameters (error: %u) for index %zu\n", result, i); continue; } - printf("\t NetID: %s (0x%x)\tArbID: 0x%x\t DLC: %u\t Remote: %d\t CANFD: %d\t Extended: %d\t Data length: %zu\n", netid_name, netid, arbid, dlc, is_remote, is_canfd, is_extended, data_length); + bool is_remote = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_RTR) != 0; + bool is_extended = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_IDE) != 0; + bool is_canfd = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_FDF) != 0; + bool is_brs = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_BRS) != 0; + bool is_esi = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_ESI) != 0; + bool tx_aborted = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_TX_ABORTED) != 0; + bool tx_lost_arb = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_TX_LOST_ARB) != 0; + bool tx_error = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_TX_ERROR) != 0; + dlc = (int32_t)data_length; + + printf("\t NetID: %s (0x%x)\tArbID: 0x%llx\tDLC: %u\tLen: %zu\n", netid_name, netid, (unsigned long long)arbid, dlc, data_length); + printf("\t Flags:%s%s%s%s%s%s%s%s\n", + is_remote ? " RTR" : "", + is_extended ? " IDE" : "", + is_canfd ? " FDF" : "", + is_brs ? " BRS" : "", + is_esi ? " ESI" : "", + tx_aborted ? " TX_ABORTED" : "", + tx_lost_arb ? " TX_LOST_ARB" : "", + tx_error ? " TX_ERROR" : ""); printf("\t Data: ["); for(size_t x = 0; x < data_length; x++) { printf(" 0x%x", data[x]); @@ -419,7 +469,7 @@ int process_messages(icsneoc2_message_t** messages, size_t messages_count) { printf(" ]\n"); } } - printf("\tReceived %zu messages total, %zu were TX messages\n", messages_count, tx_count); + printf("\tReceived %zu messages total, %zu were TX messages, %zu were CAN errors\n", messages_count, tx_count, can_error_count); return icsneoc2_error_success; } diff --git a/include/icsneo/communication/message/canerrormessage.h b/include/icsneo/communication/message/canerrormessage.h index 43bf157..43ab9b1 100644 --- a/include/icsneo/communication/message/canerrormessage.h +++ b/include/icsneo/communication/message/canerrormessage.h @@ -4,24 +4,24 @@ #ifdef __cplusplus #include "icsneo/communication/message/message.h" +#include "icsneo/icsneoc2types.h" namespace icsneo { -enum class CANErrorCode : uint8_t +enum class CANErrorCode : icsneoc2_can_error_code_t { - NoError = 0, - StuffError = 1, - FormError = 2, - AckError = 3, - Bit1Error = 4, - Bit0Error = 5, - CRCError = 6, - NoChange = 7 + NoError = icsneoc2_can_error_code_no_error, + StuffError = icsneoc2_can_error_code_stuff_error, + FormError = icsneoc2_can_error_code_form_error, + AckError = icsneoc2_can_error_code_ack_error, + Bit1Error = icsneoc2_can_error_code_bit1_error, + Bit0Error = icsneoc2_can_error_code_bit0_error, + CRCError = icsneoc2_can_error_code_crc_error, + NoChange = icsneoc2_can_error_code_no_change }; -class CANErrorMessage : public Message { +class CANErrorMessage : public RawMessage { public: - CANErrorMessage() : Message(Type::CANError) {} - Network network; + CANErrorMessage() : RawMessage(Type::CANError) {} uint8_t transmitErrorCount; uint8_t receiveErrorCount; bool busOff; diff --git a/include/icsneo/communication/message/canmessage.h b/include/icsneo/communication/message/canmessage.h index 76338fe..4271496 100644 --- a/include/icsneo/communication/message/canmessage.h +++ b/include/icsneo/communication/message/canmessage.h @@ -16,6 +16,9 @@ public: bool isCANFD = false; bool baudrateSwitch = false; // CAN FD only bool errorStateIndicator = false; // CAN FD only + bool txAborted = false; + bool txLostArb = false; + bool txError = false; }; } diff --git a/include/icsneo/icsneoc2messages.h b/include/icsneo/icsneoc2messages.h index 5e9b745..c22b83e 100644 --- a/include/icsneo/icsneoc2messages.h +++ b/include/icsneo/icsneoc2messages.h @@ -41,6 +41,17 @@ icsneoc2_error_t icsneoc2_message_free(icsneoc2_message_t* message); */ icsneoc2_error_t icsneoc2_message_is_transmit(icsneoc2_message_t* message, bool* value); +/** + * Get the frame error status of a message. + * + * @param[in] message The message to check. + * @param[out] value Pointer to a bool to copy the frame error status into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters or + * icsneoc2_error_invalid_type otherwise. + */ +icsneoc2_error_t icsneoc2_message_is_error(icsneoc2_message_t* message, bool* value); + /** * Get the Network ID (netid) of a bus message * @@ -97,11 +108,14 @@ icsneoc2_error_t icsneoc2_message_data_set(icsneoc2_message_t* message, uint8_t* */ icsneoc2_error_t icsneoc2_message_data_get(icsneoc2_message_t* message, uint8_t* data, size_t* data_length); -#define ICSNEOC2_MESSAGE_CAN_FLAGS_RTR 0x01 // Remote Transmission Request -#define ICSNEOC2_MESSAGE_CAN_FLAGS_IDE 0x02 // Identifier Extension -#define ICSNEOC2_MESSAGE_CAN_FLAGS_FDF 0x04 // FD Format Indicator -#define ICSNEOC2_MESSAGE_CAN_FLAGS_BRS 0x08 // Bit Rate Switch (FD only) -#define ICSNEOC2_MESSAGE_CAN_FLAGS_ESI 0x10 // Error State Indicator (FD only) +#define ICSNEOC2_MESSAGE_CAN_FLAGS_RTR 0x01 // Remote Transmission Request +#define ICSNEOC2_MESSAGE_CAN_FLAGS_IDE 0x02 // Identifier Extension +#define ICSNEOC2_MESSAGE_CAN_FLAGS_FDF 0x04 // FD Format Indicator +#define ICSNEOC2_MESSAGE_CAN_FLAGS_BRS 0x08 // Bit Rate Switch (FD only) +#define ICSNEOC2_MESSAGE_CAN_FLAGS_ESI 0x10 // Error State Indicator (FD only) +#define ICSNEOC2_MESSAGE_CAN_FLAGS_TX_ABORTED 0x20 // CAN transmit was aborted +#define ICSNEOC2_MESSAGE_CAN_FLAGS_TX_LOST_ARB 0x40 // CAN transmit lost arbitration +#define ICSNEOC2_MESSAGE_CAN_FLAGS_TX_ERROR 0x80 // CAN transmit reported an error typedef uint64_t icsneoc2_message_can_flags_t; @@ -122,6 +136,7 @@ icsneoc2_error_t icsneoc2_message_can_props_set(icsneoc2_message_t* message, con * @param[in] message The message to check. * @param[out] arb_id Pointer to a uint64_t to copy the arbitration ID into. If NULL, it's ignored. * @param[out] flags Pointer to a series of flags. If NULL, it's ignored. See icsneoc2_message_can_flags_t for details. + * TX status flags are read-only and are only reported for CAN frames received back from the device. * * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters or icsneoc2_error_invalid_type otherwise. */ @@ -316,6 +331,35 @@ icsneoc2_error_t icsneoc2_message_lin_calc_checksum(icsneoc2_message_t* message) */ icsneoc2_error_t icsneoc2_message_network_type_get(icsneoc2_message_t* message, icsneoc2_network_type_t* network_type); +/** + * Check if a message is a CAN error message + * + * @param[in] message The message to check. + * @param[out] is_can_error Pointer to a bool to copy the CAN error status of the message into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +icsneoc2_error_t icsneoc2_message_is_can_error(icsneoc2_message_t* message, bool* is_can_error); + +/** + * Get the CAN error specific properties of a message + * + * @param[in] message The message to check. + * @param[out] tx_err_count Pointer to a uint8_t to copy the transmit error count into. If NULL, it's ignored. + * @param[out] rx_err_count Pointer to a uint8_t to copy the receive error count into. If NULL, it's ignored. + * @param[out] error_code Pointer to a icsneoc2_can_error_code_t to copy the error code into. If NULL, it's ignored. + * @param[out] data_error_code Pointer to a icsneoc2_can_error_code_t to copy the data phase error code into. If NULL, it's ignored. + * @param[out] flags Pointer to a icsneoc2_message_can_error_flags_t to copy the error flags into. If NULL, it's ignored. + * See ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_* for controller-error bits. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters or icsneoc2_error_invalid_type otherwise. + */ +icsneoc2_error_t icsneoc2_message_can_error_props_get( + icsneoc2_message_t *message, uint8_t *tx_err_count, uint8_t *rx_err_count, + icsneoc2_can_error_code_t *error_code, + icsneoc2_can_error_code_t *data_error_code, + icsneoc2_message_can_error_flags_t *flags); + #ifdef __cplusplus } #endif diff --git a/include/icsneo/icsneoc2types.h b/include/icsneo/icsneoc2types.h index cc10720..968201a 100644 --- a/include/icsneo/icsneoc2types.h +++ b/include/icsneo/icsneoc2types.h @@ -400,6 +400,28 @@ typedef enum _icsneoc2_memory_type_t { typedef uint8_t icsneoc2_memory_type_t; +typedef enum _icsneoc2_can_error_code_t { + icsneoc2_can_error_code_no_error = 0, // No error + icsneoc2_can_error_code_stuff_error, // Stuff error + icsneoc2_can_error_code_form_error, // Form error + icsneoc2_can_error_code_ack_error, // Ack error + icsneoc2_can_error_code_bit1_error, // Bit1 error + icsneoc2_can_error_code_bit0_error, // Bit0 error + icsneoc2_can_error_code_crc_error, // CRC error + icsneoc2_can_error_code_no_change, // No change + + // Must be last entry. Don't use as a CAN error code. + icsneoc2_can_error_code_maxsize +} _icsneoc2_can_error_code_t; + +typedef uint8_t icsneoc2_can_error_code_t; + +#define ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_BUS_OFF 0x01 // Bus off state +#define ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_ERROR_PASSIVE 0x02 // Error passive state +#define ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_ERROR_WARN 0x04 // Error warning state + +typedef uint64_t icsneoc2_message_can_error_flags_t; + #ifdef __cplusplus } #endif diff --git a/test/unit/icsneoc2.cpp b/test/unit/icsneoc2.cpp index 3c74d1d..715e94e 100644 --- a/test/unit/icsneoc2.cpp +++ b/test/unit/icsneoc2.cpp @@ -2,8 +2,11 @@ #include #include #include +#include "../../api/icsneoc2/icsneoc2_internal.h" #include #include +#include +#include #include @@ -130,7 +133,10 @@ TEST(icsneoc2, test_icsneoc2_error_invalid_parameters_and_invalid_device) ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_message_network_type_get(NULL, NULL)); ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_message_is_transmit(NULL, NULL)); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_message_is_error(NULL, NULL)); ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_message_is_valid(NULL, NULL)); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_message_is_can_error(NULL, NULL)); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_message_can_error_props_get(NULL, NULL, NULL, NULL, NULL, NULL)); // LIN message NULL parameter checks ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_message_is_lin(NULL, NULL)); @@ -626,6 +632,66 @@ TEST(icsneoc2, test_icsneoc2_open_options_default) ASSERT_EQ(icsneoc2_open_options_default, expected); } +TEST(icsneoc2, test_icsneoc2_message_is_error) +{ + icsneoc2_message_t message; + message.message = std::make_shared(); + + bool value = false; + ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_is_error(&message, &value)); + ASSERT_FALSE(value); + + auto frame = std::dynamic_pointer_cast(message.message); + ASSERT_NE(frame, nullptr); + frame->error = true; + + ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_is_error(&message, &value)); + ASSERT_TRUE(value); + + icsneoc2_message_t raw_message; + raw_message.message = std::make_shared(); + ASSERT_EQ(icsneoc2_error_invalid_type, icsneoc2_message_is_error(&raw_message, &value)); +} + +TEST(icsneoc2, test_icsneoc2_message_can_props_get_can_tx_flags) +{ + icsneoc2_message_t message; + auto can_message = std::make_shared(); + message.message = can_message; + + uint64_t arb_id = 0; + icsneoc2_message_can_flags_t flags = 0; + + ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_can_props_get(&message, &arb_id, &flags)); + ASSERT_EQ(arb_id, 0u); + ASSERT_EQ(flags, 0u); + + can_message->txAborted = true; + can_message->txLostArb = true; + can_message->txError = true; + + ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_can_props_get(&message, &arb_id, &flags)); + ASSERT_EQ(flags, + ICSNEOC2_MESSAGE_CAN_FLAGS_TX_ABORTED | + ICSNEOC2_MESSAGE_CAN_FLAGS_TX_LOST_ARB | + ICSNEOC2_MESSAGE_CAN_FLAGS_TX_ERROR); +} + +TEST(icsneoc2, test_icsneoc2_message_can_error_props_get_invalid_type_for_can_message) +{ + icsneoc2_message_t message; + message.message = std::make_shared(); + + uint8_t tx_err_count = 0; + uint8_t rx_err_count = 0; + icsneoc2_can_error_code_t error_code = icsneoc2_can_error_code_no_error; + icsneoc2_can_error_code_t data_error_code = icsneoc2_can_error_code_no_error; + icsneoc2_message_can_error_flags_t flags = 0; + + ASSERT_EQ(icsneoc2_error_invalid_type, + icsneoc2_message_can_error_props_get(&message, &tx_err_count, &rx_err_count, &error_code, &data_error_code, &flags)); +} + TEST(icsneoc2, test_icsneoc2_disk_format_enums) { // Disk layout enum values @@ -671,6 +737,53 @@ TEST(icsneoc2, test_icsneoc2_memory_type_enums) ASSERT_EQ(1, icsneoc2_memory_type_sd); } +TEST(icsneoc2, test_icsneoc2_can_error_code_t) +{ + // CAN error code enum values + ASSERT_EQ(icsneoc2_can_error_code_no_error, 0); + ASSERT_EQ(icsneoc2_can_error_code_stuff_error, 1); + ASSERT_EQ(icsneoc2_can_error_code_form_error, 2); + ASSERT_EQ(icsneoc2_can_error_code_ack_error, 3); + ASSERT_EQ(icsneoc2_can_error_code_bit1_error, 4); + ASSERT_EQ(icsneoc2_can_error_code_bit0_error, 5); + ASSERT_EQ(icsneoc2_can_error_code_crc_error, 6); + ASSERT_EQ(icsneoc2_can_error_code_no_change, 7); + ASSERT_EQ(icsneoc2_can_error_code_maxsize, 8); + + using _T = icsneo::CANErrorCode; + ASSERT_EQ(static_cast(_T::NoError), icsneoc2_can_error_code_no_error); + ASSERT_EQ(static_cast(_T::StuffError), icsneoc2_can_error_code_stuff_error); + ASSERT_EQ(static_cast(_T::FormError), icsneoc2_can_error_code_form_error); + ASSERT_EQ(static_cast(_T::AckError), icsneoc2_can_error_code_ack_error); + ASSERT_EQ(static_cast(_T::Bit1Error), icsneoc2_can_error_code_bit1_error); + ASSERT_EQ(static_cast(_T::Bit0Error), icsneoc2_can_error_code_bit0_error); + ASSERT_EQ(static_cast(_T::CRCError), icsneoc2_can_error_code_crc_error); + ASSERT_EQ(static_cast(_T::NoChange), icsneoc2_can_error_code_no_change); + + ASSERT_EQ(sizeof(icsneoc2_can_error_code_t), sizeof(uint8_t)); + + // CAN error flag bitmask values + ASSERT_EQ(ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_BUS_OFF, 0x01); + ASSERT_EQ(ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_ERROR_PASSIVE, 0x02); + ASSERT_EQ(ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_ERROR_WARN, 0x04); + + ASSERT_EQ(sizeof(icsneoc2_message_can_error_flags_t), sizeof(uint64_t)); +} + +TEST(icsneoc2, test_icsneoc2_message_can_flags_t) +{ + ASSERT_EQ(ICSNEOC2_MESSAGE_CAN_FLAGS_RTR, 0x01); + ASSERT_EQ(ICSNEOC2_MESSAGE_CAN_FLAGS_IDE, 0x02); + ASSERT_EQ(ICSNEOC2_MESSAGE_CAN_FLAGS_FDF, 0x04); + ASSERT_EQ(ICSNEOC2_MESSAGE_CAN_FLAGS_BRS, 0x08); + ASSERT_EQ(ICSNEOC2_MESSAGE_CAN_FLAGS_ESI, 0x10); + ASSERT_EQ(ICSNEOC2_MESSAGE_CAN_FLAGS_TX_ABORTED, 0x20); + ASSERT_EQ(ICSNEOC2_MESSAGE_CAN_FLAGS_TX_LOST_ARB, 0x40); + ASSERT_EQ(ICSNEOC2_MESSAGE_CAN_FLAGS_TX_ERROR, 0x80); + + ASSERT_EQ(sizeof(icsneoc2_message_can_flags_t), sizeof(uint64_t)); +} + TEST(icsneoc2, test_icsneoc2_script_error_codes) { // Verify script error codes exist and have distinct string representations