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