C2: CAN error message support

master
David Rebbe 2026-04-17 15:39:38 +00:00 committed by Kyle Schwarz
parent f5f6d0828b
commit 9c4323987f
10 changed files with 416 additions and 40 deletions

View File

@ -6,6 +6,7 @@
#include "icsneo/icsneocpp.h" #include "icsneo/icsneocpp.h"
#include "icsneo/communication/message/message.h" #include "icsneo/communication/message/message.h"
#include "icsneo/communication/message/canmessage.h" #include "icsneo/communication/message/canmessage.h"
#include "icsneo/communication/message/canerrormessage.h"
#include "icsneo/communication/message/linmessage.h" #include "icsneo/communication/message/linmessage.h"
#include "icsneo/communication/message/ethernetmessage.h" #include "icsneo/communication/message/ethernetmessage.h"
#include "icsneo/communication/packet/canpacket.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; 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<Frame>(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) { icsneoc2_error_t icsneoc2_message_netid_get(icsneoc2_message_t* message, icsneoc2_netid_t* netid) {
if(!message || !netid) { if(!message || !netid) {
return icsneoc2_error_invalid_parameters; 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) { if(can_msg->errorStateIndicator) {
*flags |= ICSNEOC2_MESSAGE_CAN_FLAGS_ESI; *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; 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<CANMessage>(message->message) != nullptr; *is_can = std::dynamic_pointer_cast<CANMessage>(message->message) != nullptr;
return icsneoc2_error_success; 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<CANErrorMessage>(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<CANErrorMessage>(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<icsneoc2_can_error_code_t>(can_err->errorCode);
}
if(data_error_code) {
*data_error_code = static_cast<icsneoc2_can_error_code_t>(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) { icsneoc2_error_t icsneoc2_message_is_lin(icsneoc2_message_t* message, bool* is_lin) {

View File

@ -15,7 +15,10 @@ void init_canmessage(pybind11::module_& m) {
.def_readwrite("isExtended", &CANMessage::isExtended) .def_readwrite("isExtended", &CANMessage::isExtended)
.def_readwrite("isCANFD", &CANMessage::isCANFD) .def_readwrite("isCANFD", &CANMessage::isCANFD)
.def_readwrite("baudrateSwitch", &CANMessage::baudrateSwitch) .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 } // namespace icsneo

View File

@ -119,7 +119,12 @@ std::shared_ptr<Message> HardwareCANPacket::DecodeToMessage(const std::vector<ui
} }
msg->transmitted = data->eid.TXMSG; msg->transmitted = data->eid.TXMSG;
// Set the generic frame error state
msg->error = data->eid.TXAborted || data->eid.TXError || data->eid.TXLostArb; 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; msg->description = data->stats;
return msg; return msg;

View File

@ -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 process_message(icsneoc2_message_t** messages, size_t messages_count);
int transmit_can_messages(icsneoc2_device_t* device);
int main() { int main() {
// Open the first available device with default options // Open the first available device with default options
printf("Opening first available device...\n"); printf("Opening first available device...\n");
@ -88,6 +90,10 @@ int main() {
}; };
printf("\tOpened device: %s\n", description); printf("\tOpened device: %s\n", description);
// Transmit messages for debugging purposes
// transmit_can_messages(open_device);
// sleep_ms(1000);
// Get the messages // Get the messages
icsneoc2_message_t* messages[20000] = {0}; icsneoc2_message_t* messages[20000] = {0};
size_t message_count = 20000; 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) { int process_message(icsneoc2_message_t** messages, size_t messages_count) {
// Print the type and bus type of each message // Print the type and bus type of each message
size_t tx_count = 0; 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++) { for(size_t i = 0; i < messages_count; i++) {
icsneoc2_message_t* message = messages[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; 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) { if(res != icsneoc2_error_success) {
return print_error_code("\tFailed to check if message is a frame", res); 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) { if(res != icsneoc2_error_success) {
return print_error_code("\tFailed to get message bus type name", res); 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) { if(res != icsneoc2_error_success) {
return print_error_code("\tFailed to get message is transmit", res); 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); printf("\t%zd) network type: %s (%u)\n", i, network_type_name, network_type);
if(network_type == icsneoc2_network_type_can) { if(network_type == icsneoc2_network_type_can) {
uint32_t arbid = 0; uint64_t arbid = 0;
int32_t dlc = 0; int32_t dlc = 0;
icsneoc2_netid_t netid = 0; icsneoc2_netid_t netid = 0;
bool is_remote = false; icsneoc2_message_can_flags_t can_flags = 0;
bool is_canfd = false;
bool is_extended = false;
uint8_t data[64] = {0}; uint8_t data[64] = {0};
size_t data_length = 64; size_t data_length = 64;
char netid_name[128] = {0}; char netid_name[128] = {0};
size_t netid_name_length = 128; size_t netid_name_length = 128;
bool is_error = false;
bool is_tx = false;
icsneoc2_error_t result = icsneoc2_message_netid_get(message, &netid); icsneoc2_error_t result = icsneoc2_message_netid_get(message, &netid);
result += icsneoc2_netid_name_get(netid, netid_name, &netid_name_length); 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_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) { if(result != icsneoc2_error_success) {
printf("\tFailed get get CAN parameters (error: %u) for index %zu\n", result, i); printf("\tFailed get get CAN parameters (error: %u) for index %zu\n", result, i);
continue; 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: ["); printf("\t Data: [");
for(size_t x = 0; x < data_length; x++) { for(size_t x = 0; x < data_length; x++) {
printf(" 0x%x", data[x]); printf(" 0x%x", data[x]);
@ -230,14 +293,14 @@ int process_message(icsneoc2_message_t** messages, size_t messages_count) {
printf(" ]\n"); 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; return icsneoc2_error_success;
} }
int transmit_can_messages(icsneoc2_device_t* device) { int transmit_can_messages(icsneoc2_device_t* device) {
uint64_t counter = 0; uint64_t counter = 0;
const size_t msg_count = 100; const size_t msg_count = 10;
printf("\tTransmitting %zd messages...\n", msg_count); printf("\tTransmitting %zd messages...\n", msg_count);
for(size_t i = 0; i < msg_count; i++) { for(size_t i = 0; i < msg_count; i++) {
// Create the message // Create the message
@ -248,8 +311,12 @@ int transmit_can_messages(icsneoc2_device_t* device) {
} }
// Set the message attributes // Set the message attributes
res = icsneoc2_message_netid_set(message, icsneoc2_netid_dwcan_01); 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)); res += icsneoc2_message_data_set(message, (uint8_t*)&counter, sizeof(counter));
if(res != icsneoc2_error_success) { if(res != icsneoc2_error_success) {
icsneoc2_message_free(message);
return print_error_code("\tFailed to modify message", res); return print_error_code("\tFailed to modify message", res);
} }
res = icsneoc2_device_message_transmit(device, message); res = icsneoc2_device_message_transmit(device, message);

View File

@ -358,10 +358,42 @@ void print_events(const char* device_description) {
int process_messages(icsneoc2_message_t** messages, size_t messages_count) { int process_messages(icsneoc2_message_t** messages, size_t messages_count) {
// Print the type and bus type of each message // Print the type and bus type of each message
size_t tx_count = 0; size_t tx_count = 0;
size_t can_error_count = 0;
for(size_t i = 0; i < messages_count; i++) { for(size_t i = 0; i < messages_count; i++) {
icsneoc2_message_t* message = messages[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; 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) { if(res != icsneoc2_error_success) {
return print_error_code("\tFailed to check if message is a frame", res); 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); printf("\t%zd) network type: %s (%u)\n", i, network_type_name, network_type);
if(network_type == icsneoc2_network_type_can) { if(network_type == icsneoc2_network_type_can) {
uint32_t arbid = 0; uint64_t arbid = 0;
int32_t dlc = 0; int32_t dlc = 0;
icsneoc2_netid_t netid = 0; icsneoc2_netid_t netid = 0;
bool is_remote = false; icsneoc2_message_can_flags_t can_flags = 0;
bool is_canfd = false;
bool is_extended = false;
uint8_t data[64] = {0}; uint8_t data[64] = {0};
size_t data_length = 64; size_t data_length = 64;
char netid_name[128] = {0}; char netid_name[128] = {0};
size_t netid_name_length = 128; size_t netid_name_length = 128;
icsneoc2_error_t result = icsneoc2_message_netid_get(message, &netid); icsneoc2_error_t result = icsneoc2_message_netid_get(message, &netid);
result += icsneoc2_netid_name_get(netid, netid_name, &netid_name_length); 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_data_get(message, data, &data_length);
if(result != icsneoc2_error_success) { if(result != icsneoc2_error_success) {
printf("\tFailed get get CAN parameters (error: %u) for index %zu\n", result, i); printf("\tFailed get get CAN parameters (error: %u) for index %zu\n", result, i);
continue; 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: ["); printf("\t Data: [");
for(size_t x = 0; x < data_length; x++) { for(size_t x = 0; x < data_length; x++) {
printf(" 0x%x", data[x]); printf(" 0x%x", data[x]);
@ -419,7 +469,7 @@ int process_messages(icsneoc2_message_t** messages, size_t messages_count) {
printf(" ]\n"); 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; return icsneoc2_error_success;
} }

View File

@ -4,24 +4,24 @@
#ifdef __cplusplus #ifdef __cplusplus
#include "icsneo/communication/message/message.h" #include "icsneo/communication/message/message.h"
#include "icsneo/icsneoc2types.h"
namespace icsneo { namespace icsneo {
enum class CANErrorCode : uint8_t enum class CANErrorCode : icsneoc2_can_error_code_t
{ {
NoError = 0, NoError = icsneoc2_can_error_code_no_error,
StuffError = 1, StuffError = icsneoc2_can_error_code_stuff_error,
FormError = 2, FormError = icsneoc2_can_error_code_form_error,
AckError = 3, AckError = icsneoc2_can_error_code_ack_error,
Bit1Error = 4, Bit1Error = icsneoc2_can_error_code_bit1_error,
Bit0Error = 5, Bit0Error = icsneoc2_can_error_code_bit0_error,
CRCError = 6, CRCError = icsneoc2_can_error_code_crc_error,
NoChange = 7 NoChange = icsneoc2_can_error_code_no_change
}; };
class CANErrorMessage : public Message { class CANErrorMessage : public RawMessage {
public: public:
CANErrorMessage() : Message(Type::CANError) {} CANErrorMessage() : RawMessage(Type::CANError) {}
Network network;
uint8_t transmitErrorCount; uint8_t transmitErrorCount;
uint8_t receiveErrorCount; uint8_t receiveErrorCount;
bool busOff; bool busOff;

View File

@ -16,6 +16,9 @@ public:
bool isCANFD = false; bool isCANFD = false;
bool baudrateSwitch = false; // CAN FD only bool baudrateSwitch = false; // CAN FD only
bool errorStateIndicator = false; // CAN FD only bool errorStateIndicator = false; // CAN FD only
bool txAborted = false;
bool txLostArb = false;
bool txError = false;
}; };
} }

View File

@ -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); 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 * Get the Network ID (netid) of a bus message
* *
@ -102,6 +113,9 @@ icsneoc2_error_t icsneoc2_message_data_get(icsneoc2_message_t* message, uint8_t*
#define ICSNEOC2_MESSAGE_CAN_FLAGS_FDF 0x04 // FD Format Indicator #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_BRS 0x08 // Bit Rate Switch (FD only)
#define ICSNEOC2_MESSAGE_CAN_FLAGS_ESI 0x10 // Error State Indicator (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; 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[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] 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. * @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. * @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); 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -400,6 +400,28 @@ typedef enum _icsneoc2_memory_type_t {
typedef uint8_t 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -2,8 +2,11 @@
#include <icsneo/icsneoc2.h> #include <icsneo/icsneoc2.h>
#include <icsneo/icsneoc2settings.h> #include <icsneo/icsneoc2settings.h>
#include <icsneo/icsneoc2messages.h> #include <icsneo/icsneoc2messages.h>
#include "../../api/icsneoc2/icsneoc2_internal.h"
#include <icsneo/device/devicetype.h> #include <icsneo/device/devicetype.h>
#include <icsneo/communication/message/linmessage.h> #include <icsneo/communication/message/linmessage.h>
#include <icsneo/communication/message/canmessage.h>
#include <icsneo/communication/message/canerrormessage.h>
#include <vector> #include <vector>
@ -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_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_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_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 // LIN message NULL parameter checks
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_message_is_lin(NULL, NULL)); 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); ASSERT_EQ(icsneoc2_open_options_default, expected);
} }
TEST(icsneoc2, test_icsneoc2_message_is_error)
{
icsneoc2_message_t message;
message.message = std::make_shared<CANMessage>();
bool value = false;
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_is_error(&message, &value));
ASSERT_FALSE(value);
auto frame = std::dynamic_pointer_cast<Frame>(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<CANErrorMessage>();
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<CANMessage>();
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<CANMessage>();
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) TEST(icsneoc2, test_icsneoc2_disk_format_enums)
{ {
// Disk layout enum values // Disk layout enum values
@ -671,6 +737,53 @@ TEST(icsneoc2, test_icsneoc2_memory_type_enums)
ASSERT_EQ(1, icsneoc2_memory_type_sd); 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<icsneoc2_can_error_code_t>(_T::NoError), icsneoc2_can_error_code_no_error);
ASSERT_EQ(static_cast<icsneoc2_can_error_code_t>(_T::StuffError), icsneoc2_can_error_code_stuff_error);
ASSERT_EQ(static_cast<icsneoc2_can_error_code_t>(_T::FormError), icsneoc2_can_error_code_form_error);
ASSERT_EQ(static_cast<icsneoc2_can_error_code_t>(_T::AckError), icsneoc2_can_error_code_ack_error);
ASSERT_EQ(static_cast<icsneoc2_can_error_code_t>(_T::Bit1Error), icsneoc2_can_error_code_bit1_error);
ASSERT_EQ(static_cast<icsneoc2_can_error_code_t>(_T::Bit0Error), icsneoc2_can_error_code_bit0_error);
ASSERT_EQ(static_cast<icsneoc2_can_error_code_t>(_T::CRCError), icsneoc2_can_error_code_crc_error);
ASSERT_EQ(static_cast<icsneoc2_can_error_code_t>(_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) TEST(icsneoc2, test_icsneoc2_script_error_codes)
{ {
// Verify script error codes exist and have distinct string representations // Verify script error codes exist and have distinct string representations