C2: Add LIN message support
parent
6dc005fcea
commit
f5f6d0828b
|
|
@ -216,3 +216,133 @@ icsneoc2_error_t icsneoc2_message_is_can(icsneoc2_message_t* message, bool* is_c
|
||||||
return icsneoc2_error_success;
|
return icsneoc2_error_success;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
icsneoc2_error_t icsneoc2_message_is_lin(icsneoc2_message_t* message, bool* is_lin) {
|
||||||
|
if(!message || !is_lin) {
|
||||||
|
return icsneoc2_error_invalid_parameters;
|
||||||
|
}
|
||||||
|
*is_lin = std::dynamic_pointer_cast<LINMessage>(message->message) != nullptr;
|
||||||
|
return icsneoc2_error_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
icsneoc2_error_t icsneoc2_message_lin_create(icsneoc2_message_t** message, uint8_t id) {
|
||||||
|
if(!message) {
|
||||||
|
return icsneoc2_error_invalid_parameters;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
*message = new icsneoc2_message_t;
|
||||||
|
(*message)->message = std::make_shared<LINMessage>(id);
|
||||||
|
} catch(const std::bad_alloc&) {
|
||||||
|
return icsneoc2_error_out_of_memory;
|
||||||
|
}
|
||||||
|
return icsneoc2_error_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
icsneoc2_error_t icsneoc2_message_lin_props_get(const icsneoc2_message_t* message,
|
||||||
|
uint8_t* id, uint8_t* protected_id, uint8_t* checksum,
|
||||||
|
icsneoc2_lin_msg_type_t* msg_type, bool* is_enhanced_checksum) {
|
||||||
|
if(!message) {
|
||||||
|
return icsneoc2_error_invalid_parameters;
|
||||||
|
}
|
||||||
|
auto lin_msg = std::dynamic_pointer_cast<LINMessage>(message->message);
|
||||||
|
if(!lin_msg) {
|
||||||
|
return icsneoc2_error_invalid_type;
|
||||||
|
}
|
||||||
|
if(id) {
|
||||||
|
*id = lin_msg->ID;
|
||||||
|
}
|
||||||
|
if(protected_id) {
|
||||||
|
*protected_id = lin_msg->protectedID;
|
||||||
|
}
|
||||||
|
if(checksum) {
|
||||||
|
*checksum = lin_msg->checksum;
|
||||||
|
}
|
||||||
|
if(msg_type) {
|
||||||
|
*msg_type = static_cast<icsneoc2_lin_msg_type_t>(lin_msg->linMsgType);
|
||||||
|
}
|
||||||
|
if(is_enhanced_checksum) {
|
||||||
|
*is_enhanced_checksum = lin_msg->isEnhancedChecksum;
|
||||||
|
}
|
||||||
|
return icsneoc2_error_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
icsneoc2_error_t icsneoc2_message_lin_props_set(icsneoc2_message_t* message,
|
||||||
|
const uint8_t* id, const uint8_t* checksum,
|
||||||
|
const icsneoc2_lin_msg_type_t* msg_type, const bool* is_enhanced_checksum) {
|
||||||
|
if(!message) {
|
||||||
|
return icsneoc2_error_invalid_parameters;
|
||||||
|
}
|
||||||
|
auto lin_msg = std::dynamic_pointer_cast<LINMessage>(message->message);
|
||||||
|
if(!lin_msg) {
|
||||||
|
return icsneoc2_error_invalid_type;
|
||||||
|
}
|
||||||
|
if(id) {
|
||||||
|
lin_msg->ID = *id & 0x3Fu;
|
||||||
|
lin_msg->protectedID = lin_msg->calcProtectedID(lin_msg->ID);
|
||||||
|
}
|
||||||
|
if(checksum) {
|
||||||
|
lin_msg->checksum = *checksum;
|
||||||
|
}
|
||||||
|
if(msg_type) {
|
||||||
|
lin_msg->linMsgType = static_cast<LINMessage::Type>(*msg_type);
|
||||||
|
}
|
||||||
|
if(is_enhanced_checksum) {
|
||||||
|
lin_msg->isEnhancedChecksum = *is_enhanced_checksum;
|
||||||
|
}
|
||||||
|
return icsneoc2_error_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
icsneoc2_error_t icsneoc2_message_lin_err_flags_get(const icsneoc2_message_t* message, icsneoc2_lin_err_flags_t* err_flags) {
|
||||||
|
if(!message || !err_flags) {
|
||||||
|
return icsneoc2_error_invalid_parameters;
|
||||||
|
}
|
||||||
|
auto lin_msg = std::dynamic_pointer_cast<LINMessage>(message->message);
|
||||||
|
if(!lin_msg) {
|
||||||
|
return icsneoc2_error_invalid_type;
|
||||||
|
}
|
||||||
|
*err_flags = 0;
|
||||||
|
if(lin_msg->errFlags.ErrRxBreakOnly) *err_flags |= ICSNEOC2_LIN_ERR_RX_BREAK_ONLY;
|
||||||
|
if(lin_msg->errFlags.ErrRxBreakSyncOnly) *err_flags |= ICSNEOC2_LIN_ERR_RX_BREAK_SYNC_ONLY;
|
||||||
|
if(lin_msg->errFlags.ErrTxRxMismatch) *err_flags |= ICSNEOC2_LIN_ERR_TX_RX_MISMATCH;
|
||||||
|
if(lin_msg->errFlags.ErrRxBreakNotZero) *err_flags |= ICSNEOC2_LIN_ERR_RX_BREAK_NOT_ZERO;
|
||||||
|
if(lin_msg->errFlags.ErrRxBreakTooShort) *err_flags |= ICSNEOC2_LIN_ERR_RX_BREAK_TOO_SHORT;
|
||||||
|
if(lin_msg->errFlags.ErrRxSyncNot55) *err_flags |= ICSNEOC2_LIN_ERR_RX_SYNC_NOT_55;
|
||||||
|
if(lin_msg->errFlags.ErrRxDataLenOver8) *err_flags |= ICSNEOC2_LIN_ERR_RX_DATA_LEN_OVER_8;
|
||||||
|
if(lin_msg->errFlags.ErrFrameSync) *err_flags |= ICSNEOC2_LIN_ERR_FRAME_SYNC;
|
||||||
|
if(lin_msg->errFlags.ErrFrameMessageID) *err_flags |= ICSNEOC2_LIN_ERR_FRAME_MESSAGE_ID;
|
||||||
|
if(lin_msg->errFlags.ErrFrameResponderData) *err_flags |= ICSNEOC2_LIN_ERR_FRAME_RESPONDER_DATA;
|
||||||
|
if(lin_msg->errFlags.ErrChecksumMatch) *err_flags |= ICSNEOC2_LIN_ERR_CHECKSUM_MATCH;
|
||||||
|
return icsneoc2_error_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
icsneoc2_error_t icsneoc2_message_lin_status_flags_get(const icsneoc2_message_t* message, icsneoc2_lin_status_flags_t* status_flags) {
|
||||||
|
if(!message || !status_flags) {
|
||||||
|
return icsneoc2_error_invalid_parameters;
|
||||||
|
}
|
||||||
|
auto lin_msg = std::dynamic_pointer_cast<LINMessage>(message->message);
|
||||||
|
if(!lin_msg) {
|
||||||
|
return icsneoc2_error_invalid_type;
|
||||||
|
}
|
||||||
|
*status_flags = 0;
|
||||||
|
if(lin_msg->statusFlags.TxChecksumEnhanced) *status_flags |= ICSNEOC2_LIN_STATUS_TX_CHECKSUM_ENHANCED;
|
||||||
|
if(lin_msg->statusFlags.TxCommander) *status_flags |= ICSNEOC2_LIN_STATUS_TX_COMMANDER;
|
||||||
|
if(lin_msg->statusFlags.TxResponder) *status_flags |= ICSNEOC2_LIN_STATUS_TX_RESPONDER;
|
||||||
|
if(lin_msg->statusFlags.TxAborted) *status_flags |= ICSNEOC2_LIN_STATUS_TX_ABORTED;
|
||||||
|
if(lin_msg->statusFlags.UpdateResponderOnce) *status_flags |= ICSNEOC2_LIN_STATUS_UPDATE_RESPONDER_ONCE;
|
||||||
|
if(lin_msg->statusFlags.HasUpdatedResponderOnce) *status_flags |= ICSNEOC2_LIN_STATUS_HAS_UPDATED_RESPONDER_ONCE;
|
||||||
|
if(lin_msg->statusFlags.BusRecovered) *status_flags |= ICSNEOC2_LIN_STATUS_BUS_RECOVERED;
|
||||||
|
if(lin_msg->statusFlags.BreakOnly) *status_flags |= ICSNEOC2_LIN_STATUS_BREAK_ONLY;
|
||||||
|
return icsneoc2_error_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
icsneoc2_error_t icsneoc2_message_lin_calc_checksum(icsneoc2_message_t* message) {
|
||||||
|
if(!message) {
|
||||||
|
return icsneoc2_error_invalid_parameters;
|
||||||
|
}
|
||||||
|
auto lin_msg = std::dynamic_pointer_cast<LINMessage>(message->message);
|
||||||
|
if(!lin_msg) {
|
||||||
|
return icsneoc2_error_invalid_type;
|
||||||
|
}
|
||||||
|
LINMessage::calcChecksum(*lin_msg);
|
||||||
|
return icsneoc2_error_success;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,10 +102,71 @@ static std::vector<uint8_t> EncodeFromMessageCAN(std::shared_ptr<Frame> frame, c
|
||||||
return encoded;
|
return encoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<uint8_t> EncodeFromMessageLIN(std::shared_ptr<Frame> /* frame */, const device_eventhandler_t& report) {
|
static std::vector<uint8_t> EncodeFromMessageLIN(std::shared_ptr<Frame> frame, const device_eventhandler_t& report) {
|
||||||
// TODO
|
auto linmsg = std::dynamic_pointer_cast<LINMessage>(frame);
|
||||||
report(APIEvent::Type::UnsupportedTXNetwork, APIEvent::Severity::Error);
|
if(!linmsg) {
|
||||||
return {};
|
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if(linmsg->linMsgType == LINMessage::Type::NOT_SET) {
|
||||||
|
report(APIEvent::Type::RequiredParameterNull, APIEvent::Severity::Error);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t dataLen = std::min<size_t>(8, linmsg->data.size());
|
||||||
|
std::vector<uint8_t> encoded;
|
||||||
|
encoded.resize(sizeof(TransmitMessage));
|
||||||
|
|
||||||
|
TransmitMessage* const msg = (TransmitMessage*)encoded.data();
|
||||||
|
HardwareLINPacket* const linpacket = (HardwareLINPacket*)(msg->commonHeader);
|
||||||
|
memset(linpacket, 0, sizeof(HardwareLINPacket));
|
||||||
|
|
||||||
|
// Protected ID and ID
|
||||||
|
linmsg->protectedID = linmsg->calcProtectedID(linmsg->ID);
|
||||||
|
linpacket->CoreMiniBitsLIN.ID = linmsg->protectedID & 0x3F;
|
||||||
|
|
||||||
|
// LIN message type flags
|
||||||
|
switch(linmsg->linMsgType) {
|
||||||
|
case LINMessage::Type::LIN_COMMANDER_MSG:
|
||||||
|
linpacket->CoreMiniBitsLIN.TXCommander = 1;
|
||||||
|
break;
|
||||||
|
case LINMessage::Type::LIN_HEADER_ONLY:
|
||||||
|
linpacket->CoreMiniBitsLIN.TXCommander = 1;
|
||||||
|
break;
|
||||||
|
case LINMessage::Type::LIN_UPDATE_RESPONDER:
|
||||||
|
linpacket->CoreMiniBitsLIN.TXResponder = 1;
|
||||||
|
break;
|
||||||
|
case LINMessage::Type::LIN_BREAK_ONLY:
|
||||||
|
linpacket->CoreMiniBitsLIN.BreakOnly = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enhanced checksum
|
||||||
|
linpacket->CoreMiniBitsLIN.TxChkSumEnhanced = linmsg->isEnhancedChecksum ? 1 : 0;
|
||||||
|
|
||||||
|
// Data and checksum
|
||||||
|
bool hasData = (linmsg->linMsgType == LINMessage::Type::LIN_COMMANDER_MSG ||
|
||||||
|
linmsg->linMsgType == LINMessage::Type::LIN_UPDATE_RESPONDER) && dataLen > 0;
|
||||||
|
|
||||||
|
if(hasData) {
|
||||||
|
// len includes data bytes + 1 checksum byte
|
||||||
|
linpacket->CoreMiniBitsLIN.len = static_cast<uint16_t>(dataLen + 1);
|
||||||
|
std::copy(linmsg->data.begin(), linmsg->data.begin() + dataLen, linpacket->data);
|
||||||
|
// Checksum goes after data: in data[dataLen] if < 8, otherwise in LINByte9
|
||||||
|
if(dataLen < 8)
|
||||||
|
linpacket->data[dataLen] = linmsg->checksum;
|
||||||
|
else
|
||||||
|
linpacket->CoreMiniBitsLIN.LINByte9 = linmsg->checksum;
|
||||||
|
} else {
|
||||||
|
linpacket->CoreMiniBitsLIN.len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description/stats and network
|
||||||
|
linpacket->stats = linmsg->description;
|
||||||
|
|
||||||
|
return encoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> TransmitMessage::EncodeFromMessage(std::shared_ptr<Frame> frame, uint32_t client_id, const device_eventhandler_t& report) {
|
std::vector<uint8_t> TransmitMessage::EncodeFromMessage(std::shared_ptr<Frame> frame, uint32_t client_id, const device_eventhandler_t& report) {
|
||||||
|
|
@ -127,6 +188,8 @@ std::vector<uint8_t> TransmitMessage::EncodeFromMessage(std::shared_ptr<Frame> f
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
// common fields
|
// common fields
|
||||||
|
if(result.empty())
|
||||||
|
return result;
|
||||||
TransmitMessage* const msg = (TransmitMessage*)result.data();
|
TransmitMessage* const msg = (TransmitMessage*)result.data();
|
||||||
msg->options.clientId = client_id;
|
msg->options.clientId = client_id;
|
||||||
msg->options.networkId = static_cast<uint32_t>(frame->network.getNetID());
|
msg->options.networkId = static_cast<uint32_t>(frame->network.getNetID());
|
||||||
|
|
|
||||||
|
|
@ -33,3 +33,19 @@ Device Info
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/c2/device_info/src/main.c
|
.. literalinclude:: ../../examples/c2/device_info/src/main.c
|
||||||
:language: c
|
:language: c
|
||||||
|
|
||||||
|
LIN
|
||||||
|
===
|
||||||
|
|
||||||
|
:download:`Download example <../../examples/c2/lin/src/main.c>`
|
||||||
|
|
||||||
|
.. literalinclude:: ../../examples/c2/lin/src/main.c
|
||||||
|
:language: c
|
||||||
|
|
||||||
|
LIN Transmit
|
||||||
|
============
|
||||||
|
|
||||||
|
:download:`Download example <../../examples/c2/lin_transmit/src/main.c>`
|
||||||
|
|
||||||
|
.. literalinclude:: ../../examples/c2/lin_transmit/src/main.c
|
||||||
|
:language: c
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ option(LIBICSNEO_BUILD_C2_READ_MESSAGES_EXAMPLE "Build the C2 read messages exam
|
||||||
option(LIBICSNEO_BUILD_C2_DISKFORMAT_EXAMPLE "Build the C2 disk format example." ON)
|
option(LIBICSNEO_BUILD_C2_DISKFORMAT_EXAMPLE "Build the C2 disk format example." ON)
|
||||||
option(LIBICSNEO_BUILD_C2_RECONNECT_EXAMPLE "Build the C2 reconnect example." ON)
|
option(LIBICSNEO_BUILD_C2_RECONNECT_EXAMPLE "Build the C2 reconnect example." ON)
|
||||||
option(LIBICSNEO_BUILD_C2_DEVICE_INFO_EXAMPLE "Build the C2 device info example." ON)
|
option(LIBICSNEO_BUILD_C2_DEVICE_INFO_EXAMPLE "Build the C2 device info example." ON)
|
||||||
|
option(LIBICSNEO_BUILD_C2_LIN_EXAMPLE "Build the C2 LIN example." ON)
|
||||||
|
option(LIBICSNEO_BUILD_C2_LIN_TRANSMIT_EXAMPLE "Build the C2 LIN transmit example." ON)
|
||||||
option(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE "Build the simple C++ example." ON)
|
option(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE "Build the simple C++ example." ON)
|
||||||
option(LIBICSNEO_BUILD_CPP_INTERACTIVE_EXAMPLE "Build the command-line interactive C++ example." ON)
|
option(LIBICSNEO_BUILD_CPP_INTERACTIVE_EXAMPLE "Build the command-line interactive C++ example." ON)
|
||||||
option(LIBICSNEO_BUILD_CPP_A2B_EXAMPLE "Build the A2B example." ON)
|
option(LIBICSNEO_BUILD_CPP_A2B_EXAMPLE "Build the A2B example." ON)
|
||||||
|
|
@ -57,6 +59,14 @@ if(LIBICSNEO_BUILD_C2_DEVICE_INFO_EXAMPLE)
|
||||||
add_subdirectory(c2/device_info)
|
add_subdirectory(c2/device_info)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(LIBICSNEO_BUILD_C2_LIN_EXAMPLE)
|
||||||
|
add_subdirectory(c2/lin)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(LIBICSNEO_BUILD_C2_LIN_TRANSMIT_EXAMPLE)
|
||||||
|
add_subdirectory(c2/lin_transmit)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE)
|
if(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE)
|
||||||
add_subdirectory(cpp/simple)
|
add_subdirectory(cpp/simple)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
add_executable(libicsneoc2-lin-example src/main.c)
|
||||||
|
target_link_libraries(libicsneoc2-lin-example icsneoc2-static)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
target_compile_definitions(libicsneoc2-lin-example PRIVATE _CRT_SECURE_NO_WARNINGS)
|
||||||
|
endif()
|
||||||
|
|
@ -0,0 +1,311 @@
|
||||||
|
/* Note: This example requires LIN 1 and LIN 2 channels to be connected on the device */
|
||||||
|
|
||||||
|
#include <icsneo/icsneoc2.h>
|
||||||
|
#include <icsneo/icsneoc2messages.h>
|
||||||
|
#include <icsneo/icsneoc2settings.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
void sleep_ms(uint32_t ms) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
Sleep(ms);
|
||||||
|
#else
|
||||||
|
sleep(ms / 1000);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int print_error_code(const char* message, icsneoc2_error_t error) {
|
||||||
|
char error_str[64];
|
||||||
|
size_t error_str_len = sizeof(error_str);
|
||||||
|
icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_str_len);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
printf("%s: Failed to get string for error code %d with error code %d\n", message, error, res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
printf("%s: \"%s\" (%u)\n", message, error_str, error);
|
||||||
|
return (int)error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_events(const char* device_description) {
|
||||||
|
icsneoc2_event_t* events[1024] = {0};
|
||||||
|
size_t events_count = 1024;
|
||||||
|
for(size_t i = 0; i < events_count; ++i) {
|
||||||
|
icsneoc2_error_t res = icsneoc2_event_get(&events[i], NULL);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
for(size_t j = 0; j < i; ++j) {
|
||||||
|
icsneoc2_event_free(events[j]);
|
||||||
|
}
|
||||||
|
(void)print_error_code("\tFailed to get device events", res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(events[i] == NULL) {
|
||||||
|
events_count = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i < events_count; i++) {
|
||||||
|
char event_description[255] = {0};
|
||||||
|
size_t event_description_length = 255;
|
||||||
|
icsneoc2_error_t res = icsneoc2_event_description_get(events[i], event_description, &event_description_length);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
print_error_code("\tFailed to get event description", res);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
printf("\t%s: Event %zu: %s\n", device_description, i, event_description);
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i < events_count; i++) {
|
||||||
|
icsneoc2_event_free(events[i]);
|
||||||
|
}
|
||||||
|
printf("\t%s: Received %zu events\n", device_description, events_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_lin_messages(icsneoc2_message_t** messages, size_t count) {
|
||||||
|
for(size_t i = 0; i < count; i++) {
|
||||||
|
bool is_lin = false;
|
||||||
|
icsneoc2_error_t res = icsneoc2_message_is_lin(messages[i], &is_lin);
|
||||||
|
if(res != icsneoc2_error_success || !is_lin)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
uint8_t id = 0;
|
||||||
|
uint8_t protected_id = 0;
|
||||||
|
uint8_t checksum = 0;
|
||||||
|
icsneoc2_lin_msg_type_t msg_type = 0;
|
||||||
|
bool is_enhanced_checksum = false;
|
||||||
|
res = icsneoc2_message_lin_props_get(messages[i], &id, &protected_id, &checksum, &msg_type, &is_enhanced_checksum);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
print_error_code("\tFailed to get LIN properties", res);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
icsneoc2_netid_t netid = 0;
|
||||||
|
icsneoc2_message_netid_get(messages[i], &netid);
|
||||||
|
char netid_name[128] = {0};
|
||||||
|
size_t netid_name_length = 128;
|
||||||
|
icsneoc2_netid_name_get(netid, netid_name, &netid_name_length);
|
||||||
|
|
||||||
|
uint8_t data[64] = {0};
|
||||||
|
size_t data_length = 64;
|
||||||
|
icsneoc2_message_data_get(messages[i], data, &data_length);
|
||||||
|
|
||||||
|
icsneoc2_lin_err_flags_t err_flags = 0;
|
||||||
|
icsneoc2_message_lin_err_flags_get(messages[i], &err_flags);
|
||||||
|
|
||||||
|
printf("\t%s RX | ID: 0x%02x | Protected ID: 0x%02x\n", netid_name, id, protected_id);
|
||||||
|
printf("\tData: [");
|
||||||
|
for(size_t x = 0; x < data_length; x++) {
|
||||||
|
printf(" 0x%02x", data[x]);
|
||||||
|
}
|
||||||
|
printf(" ]\n");
|
||||||
|
printf("\tChecksum type: %s\n", is_enhanced_checksum ? "Enhanced" : "Classic");
|
||||||
|
printf("\tChecksum: 0x%02x\n", checksum);
|
||||||
|
printf("\tChecksum valid: %s\n\n", (!(err_flags & ICSNEOC2_LIN_ERR_CHECKSUM_MATCH)) ? "yes" : "no");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
/* Open the first available device */
|
||||||
|
printf("Opening first available device...\n");
|
||||||
|
icsneoc2_device_t* device = NULL;
|
||||||
|
icsneoc2_error_t res = icsneoc2_device_open_first(0, icsneoc2_open_options_default, &device);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
return print_error_code("\tFailed to open first device", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
char description[255] = {0};
|
||||||
|
size_t description_length = 255;
|
||||||
|
res = icsneoc2_device_description_get(device, description, &description_length);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
icsneoc2_device_close(device);
|
||||||
|
return print_error_code("\tFailed to get device description", res);
|
||||||
|
}
|
||||||
|
printf("\tOpened device: %s\n\n", description);
|
||||||
|
|
||||||
|
/* Configure LIN settings */
|
||||||
|
int64_t baud = 19200;
|
||||||
|
|
||||||
|
printf("Enable LIN 01 commander resistor... ");
|
||||||
|
res = icsneoc2_settings_commander_resistor_set(device, icsneoc2_netid_lin_01, true);
|
||||||
|
printf("%s\n", res == icsneoc2_error_success ? "OK" : "FAIL");
|
||||||
|
|
||||||
|
printf("Disable LIN 02 commander resistor... ");
|
||||||
|
res = icsneoc2_settings_commander_resistor_set(device, icsneoc2_netid_lin_02, false);
|
||||||
|
printf("%s\n", res == icsneoc2_error_success ? "OK" : "FAIL");
|
||||||
|
|
||||||
|
printf("Setting LIN 01 baudrate to %" PRId64 " bit/s... ", baud);
|
||||||
|
res = icsneoc2_settings_baudrate_set(device, icsneoc2_netid_lin_01, baud);
|
||||||
|
printf("%s\n", res == icsneoc2_error_success ? "OK" : "FAIL");
|
||||||
|
|
||||||
|
printf("Setting LIN 02 baudrate to %" PRId64 " bit/s... ", baud);
|
||||||
|
res = icsneoc2_settings_baudrate_set(device, icsneoc2_netid_lin_02, baud);
|
||||||
|
printf("%s\n", res == icsneoc2_error_success ? "OK" : "FAIL");
|
||||||
|
|
||||||
|
printf("Setting LIN 01 mode to NORMAL... ");
|
||||||
|
res = icsneoc2_settings_lin_mode_set(device, icsneoc2_netid_lin_01, icsneoc2_lin_mode_normal);
|
||||||
|
printf("%s\n", res == icsneoc2_error_success ? "OK" : "FAIL");
|
||||||
|
|
||||||
|
printf("Setting LIN 02 mode to NORMAL... ");
|
||||||
|
res = icsneoc2_settings_lin_mode_set(device, icsneoc2_netid_lin_02, icsneoc2_lin_mode_normal);
|
||||||
|
printf("%s\n", res == icsneoc2_error_success ? "OK" : "FAIL");
|
||||||
|
|
||||||
|
printf("Applying settings... ");
|
||||||
|
res = icsneoc2_settings_apply(device);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
print_events(description);
|
||||||
|
icsneoc2_device_close(device);
|
||||||
|
return print_error_code("\tFailed to apply settings", res);
|
||||||
|
}
|
||||||
|
printf("OK\n");
|
||||||
|
|
||||||
|
printf("Getting LIN 01 baudrate... ");
|
||||||
|
int64_t read_baud = 0;
|
||||||
|
res = icsneoc2_settings_baudrate_get(device, icsneoc2_netid_lin_01, &read_baud);
|
||||||
|
if(res == icsneoc2_error_success)
|
||||||
|
printf("OK, %" PRId64 " bit/s\n", read_baud);
|
||||||
|
else
|
||||||
|
printf("FAIL\n");
|
||||||
|
|
||||||
|
printf("Getting LIN 02 baudrate... ");
|
||||||
|
res = icsneoc2_settings_baudrate_get(device, icsneoc2_netid_lin_02, &read_baud);
|
||||||
|
if(res == icsneoc2_error_success)
|
||||||
|
printf("OK, %" PRId64 " bit/s\n\n", read_baud);
|
||||||
|
else
|
||||||
|
printf("FAIL\n\n");
|
||||||
|
|
||||||
|
/* Transmit a LIN responder data update on LIN 02 */
|
||||||
|
printf("Transmitting a LIN 02 responder data frame... ");
|
||||||
|
{
|
||||||
|
icsneoc2_message_t* msg = NULL;
|
||||||
|
res = icsneoc2_message_lin_create(&msg, 0x11);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
print_events(description);
|
||||||
|
icsneoc2_device_close(device);
|
||||||
|
return print_error_code("\tFailed to create LIN message", res);
|
||||||
|
}
|
||||||
|
icsneoc2_lin_msg_type_t msg_type = icsneoc2_lin_msg_type_update_responder;
|
||||||
|
res = icsneoc2_message_lin_props_set(msg, NULL, NULL, &msg_type, NULL);
|
||||||
|
uint8_t data[] = {0xaa, 0xbb, 0xcc, 0xdd, 0x11, 0x22, 0x33, 0x44};
|
||||||
|
res += icsneoc2_message_data_set(msg, data, sizeof(data));
|
||||||
|
res += icsneoc2_message_netid_set(msg, icsneoc2_netid_lin_02);
|
||||||
|
res += icsneoc2_message_lin_calc_checksum(msg);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
icsneoc2_message_free(msg);
|
||||||
|
print_events(description);
|
||||||
|
icsneoc2_device_close(device);
|
||||||
|
return print_error_code("\tFailed to set LIN message properties", res);
|
||||||
|
}
|
||||||
|
res = icsneoc2_device_message_transmit(device, msg);
|
||||||
|
icsneoc2_message_free(msg);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
print_events(description);
|
||||||
|
icsneoc2_device_close(device);
|
||||||
|
return print_error_code("\tFailed to transmit LIN responder message", res);
|
||||||
|
}
|
||||||
|
printf("OK\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Transmit a LIN commander header on LIN 01 */
|
||||||
|
printf("Transmitting a LIN 01 commander header... ");
|
||||||
|
{
|
||||||
|
icsneoc2_message_t* msg = NULL;
|
||||||
|
res = icsneoc2_message_lin_create(&msg, 0x11);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
print_events(description);
|
||||||
|
icsneoc2_device_close(device);
|
||||||
|
return print_error_code("\tFailed to create LIN message", res);
|
||||||
|
}
|
||||||
|
icsneoc2_lin_msg_type_t msg_type = icsneoc2_lin_msg_type_header_only;
|
||||||
|
res = icsneoc2_message_lin_props_set(msg, NULL, NULL, &msg_type, NULL);
|
||||||
|
res += icsneoc2_message_netid_set(msg, icsneoc2_netid_lin_01);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
icsneoc2_message_free(msg);
|
||||||
|
print_events(description);
|
||||||
|
icsneoc2_device_close(device);
|
||||||
|
return print_error_code("\tFailed to set LIN message properties", res);
|
||||||
|
}
|
||||||
|
res = icsneoc2_device_message_transmit(device, msg);
|
||||||
|
icsneoc2_message_free(msg);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
print_events(description);
|
||||||
|
icsneoc2_device_close(device);
|
||||||
|
return print_error_code("\tFailed to transmit LIN header", res);
|
||||||
|
}
|
||||||
|
printf("OK\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep_ms(100);
|
||||||
|
|
||||||
|
/* Transmit a LIN commander message with data on LIN 01 */
|
||||||
|
printf("Transmitting a LIN 01 commander frame with data... ");
|
||||||
|
{
|
||||||
|
icsneoc2_message_t* msg = NULL;
|
||||||
|
res = icsneoc2_message_lin_create(&msg, 0x22);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
print_events(description);
|
||||||
|
icsneoc2_device_close(device);
|
||||||
|
return print_error_code("\tFailed to create LIN message", res);
|
||||||
|
}
|
||||||
|
icsneoc2_lin_msg_type_t msg_type = icsneoc2_lin_msg_type_commander_msg;
|
||||||
|
bool enhanced = true;
|
||||||
|
res = icsneoc2_message_lin_props_set(msg, NULL, NULL, &msg_type, &enhanced);
|
||||||
|
uint8_t data[] = {0x11, 0x22, 0x33, 0x44, 0xaa, 0xbb, 0xcc, 0xdd};
|
||||||
|
res += icsneoc2_message_data_set(msg, data, sizeof(data));
|
||||||
|
res += icsneoc2_message_netid_set(msg, icsneoc2_netid_lin_01);
|
||||||
|
res += icsneoc2_message_lin_calc_checksum(msg);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
icsneoc2_message_free(msg);
|
||||||
|
print_events(description);
|
||||||
|
icsneoc2_device_close(device);
|
||||||
|
return print_error_code("\tFailed to set LIN message properties", res);
|
||||||
|
}
|
||||||
|
res = icsneoc2_device_message_transmit(device, msg);
|
||||||
|
icsneoc2_message_free(msg);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
print_events(description);
|
||||||
|
icsneoc2_device_close(device);
|
||||||
|
return print_error_code("\tFailed to transmit LIN commander message", res);
|
||||||
|
}
|
||||||
|
printf("OK\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep_ms(100);
|
||||||
|
|
||||||
|
/* Read back any received messages and display LIN frames */
|
||||||
|
icsneoc2_message_t* messages[2048] = {0};
|
||||||
|
size_t message_count = 2048;
|
||||||
|
printf("Getting messages...\n");
|
||||||
|
for(size_t i = 0; i < message_count; ++i) {
|
||||||
|
res = icsneoc2_device_message_get(device, &messages[i], 0);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
for(size_t j = 0; j < i; ++j) {
|
||||||
|
icsneoc2_message_free(messages[j]);
|
||||||
|
}
|
||||||
|
print_events(description);
|
||||||
|
icsneoc2_device_close(device);
|
||||||
|
return print_error_code("\tFailed to get messages", res);
|
||||||
|
}
|
||||||
|
if(messages[i] == NULL) {
|
||||||
|
message_count = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\tReceived %zu messages\n", message_count);
|
||||||
|
process_lin_messages(messages, message_count);
|
||||||
|
for(size_t i = 0; i < message_count; ++i) {
|
||||||
|
icsneoc2_message_free(messages[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
print_events(description);
|
||||||
|
printf("Closing device... ");
|
||||||
|
res = icsneoc2_device_close(device);
|
||||||
|
printf("%s\n", res == icsneoc2_error_success ? "OK" : "FAIL");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
add_executable(libicsneoc2-lin-transmit-example src/main.c)
|
||||||
|
target_link_libraries(libicsneoc2-lin-transmit-example icsneoc2-static)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
target_compile_definitions(libicsneoc2-lin-transmit-example PRIVATE _CRT_SECURE_NO_WARNINGS)
|
||||||
|
endif()
|
||||||
|
|
@ -0,0 +1,276 @@
|
||||||
|
#include <icsneo/icsneoc2.h>
|
||||||
|
#include <icsneo/icsneoc2messages.h>
|
||||||
|
#include <icsneo/icsneoc2settings.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
void sleep_ms(uint32_t ms) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
Sleep(ms);
|
||||||
|
#else
|
||||||
|
sleep(ms / 1000);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int print_error_code(const char* message, icsneoc2_error_t error) {
|
||||||
|
char error_str[64];
|
||||||
|
size_t error_str_len = sizeof(error_str);
|
||||||
|
icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_str_len);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
printf("%s: Failed to get string for error code %d with error code %d\n", message, error, res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
printf("%s: \"%s\" (%u)\n", message, error_str, error);
|
||||||
|
return (int)error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int read_int(const char* prompt, int min_val, int max_val) {
|
||||||
|
int value;
|
||||||
|
while(1) {
|
||||||
|
printf("%s", prompt);
|
||||||
|
if(scanf("%d", &value) != 1) {
|
||||||
|
/* Clear invalid input */
|
||||||
|
int c;
|
||||||
|
while((c = getchar()) != '\n' && c != EOF) {}
|
||||||
|
printf("Invalid input, try again.\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(value < min_val || value > max_val) {
|
||||||
|
printf("Please enter a value between %d and %d.\n", min_val, max_val);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int read_hex(const char* prompt, int min_val, int max_val) {
|
||||||
|
int value;
|
||||||
|
while(1) {
|
||||||
|
printf("%s", prompt);
|
||||||
|
if(scanf("%x", &value) != 1) {
|
||||||
|
/* Clear invalid input */
|
||||||
|
int c;
|
||||||
|
while((c = getchar()) != '\n' && c != EOF) {}
|
||||||
|
printf("Invalid input, try again.\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(value < min_val || value > max_val) {
|
||||||
|
printf("Please enter a value between 0x%X and 0x%X.\n", min_val, max_val);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
icsneoc2_error_t res;
|
||||||
|
|
||||||
|
/* ===== Device Selection ===== */
|
||||||
|
printf("Searching for devices...\n");
|
||||||
|
icsneoc2_device_info_t* found_devices = NULL;
|
||||||
|
res = icsneoc2_device_enumerate(0, &found_devices);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
return print_error_code("Failed to enumerate devices", res);
|
||||||
|
}
|
||||||
|
if(!found_devices) {
|
||||||
|
printf("No devices found.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Count and display devices */
|
||||||
|
int device_count = 0;
|
||||||
|
for(icsneoc2_device_info_t* cur = found_devices; cur; cur = icsneoc2_device_info_next(cur)) {
|
||||||
|
char desc[128] = {0};
|
||||||
|
size_t desc_len = sizeof(desc);
|
||||||
|
icsneoc2_device_info_description_get(cur, desc, &desc_len);
|
||||||
|
printf(" [%d] %s\n", device_count + 1, desc);
|
||||||
|
device_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int device_choice = read_int("Select device: ", 1, device_count);
|
||||||
|
|
||||||
|
/* Find the selected device_info node */
|
||||||
|
icsneoc2_device_info_t* selected_info = found_devices;
|
||||||
|
for(int i = 1; i < device_choice; i++) {
|
||||||
|
selected_info = icsneoc2_device_info_next(selected_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open the selected device */
|
||||||
|
icsneoc2_device_t* device = NULL;
|
||||||
|
res = icsneoc2_device_create(selected_info, &device);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
icsneoc2_enumeration_free(found_devices);
|
||||||
|
return print_error_code("Failed to create device from device info", res);
|
||||||
|
}
|
||||||
|
res = icsneoc2_device_open(device, icsneoc2_open_options_default);
|
||||||
|
icsneoc2_enumeration_free(found_devices);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
icsneoc2_device_free(device);
|
||||||
|
return print_error_code("Failed to open device", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
char description[128] = {0};
|
||||||
|
size_t description_length = sizeof(description);
|
||||||
|
icsneoc2_device_description_get(device, description, &description_length);
|
||||||
|
printf("Opened device: %s\n\n", description);
|
||||||
|
|
||||||
|
/* ===== LIN Network Selection ===== */
|
||||||
|
/* Get all supported TX networks */
|
||||||
|
size_t tx_net_count = 0;
|
||||||
|
res = icsneoc2_device_supported_tx_networks_get(device, NULL, &tx_net_count);
|
||||||
|
if(res != icsneoc2_error_success || tx_net_count == 0) {
|
||||||
|
printf("No supported TX networks.\n");
|
||||||
|
icsneoc2_device_close(device);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
icsneoc2_netid_t* tx_networks = (icsneoc2_netid_t*)malloc(tx_net_count * sizeof(icsneoc2_netid_t));
|
||||||
|
if(!tx_networks) {
|
||||||
|
printf("Out of memory.\n");
|
||||||
|
icsneoc2_device_close(device);
|
||||||
|
icsneoc2_device_free(device);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
res = icsneoc2_device_supported_tx_networks_get(device, tx_networks, &tx_net_count);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
free(tx_networks);
|
||||||
|
icsneoc2_device_close(device);
|
||||||
|
icsneoc2_device_free(device);
|
||||||
|
return print_error_code("Failed to get TX networks", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Filter for LIN networks */
|
||||||
|
icsneoc2_netid_t lin_networks[64] = {0};
|
||||||
|
char lin_names[64][128] = {{0}};
|
||||||
|
size_t lin_count = 0;
|
||||||
|
for(size_t i = 0; i < tx_net_count && lin_count < 64; i++) {
|
||||||
|
icsneoc2_message_t* tmp = NULL;
|
||||||
|
res = icsneoc2_message_lin_create(&tmp, 0);
|
||||||
|
if(res != icsneoc2_error_success) continue;
|
||||||
|
res = icsneoc2_message_netid_set(tmp, tx_networks[i]);
|
||||||
|
if(res != icsneoc2_error_success) { icsneoc2_message_free(tmp); continue; }
|
||||||
|
icsneoc2_network_type_t ntype = 0;
|
||||||
|
res = icsneoc2_message_network_type_get(tmp, &ntype);
|
||||||
|
icsneoc2_message_free(tmp);
|
||||||
|
if(res != icsneoc2_error_success) continue;
|
||||||
|
if(ntype == icsneoc2_network_type_lin) {
|
||||||
|
lin_networks[lin_count] = tx_networks[i];
|
||||||
|
size_t name_len = 128;
|
||||||
|
icsneoc2_netid_name_get(tx_networks[i], lin_names[lin_count], &name_len);
|
||||||
|
lin_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(tx_networks);
|
||||||
|
|
||||||
|
if(lin_count == 0) {
|
||||||
|
printf("No LIN networks available on this device.\n");
|
||||||
|
icsneoc2_device_close(device);
|
||||||
|
icsneoc2_device_free(device);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Available LIN networks:\n");
|
||||||
|
for(size_t i = 0; i < lin_count; i++) {
|
||||||
|
printf(" [%zu] %s\n", i + 1, lin_names[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lin_choice = read_int("Select LIN network: ", 1, (int)lin_count);
|
||||||
|
icsneoc2_netid_t selected_netid = lin_networks[lin_choice - 1];
|
||||||
|
printf("Selected: %s\n\n", lin_names[lin_choice - 1]);
|
||||||
|
|
||||||
|
/* ===== Commander / Responder Selection ===== */
|
||||||
|
printf("Message type:\n");
|
||||||
|
printf(" [1] Commander frame\n");
|
||||||
|
printf(" [2] Responder frame (update responder + header only)\n");
|
||||||
|
int type_choice = read_int("Select message type: ", 1, 2);
|
||||||
|
bool is_commander = (type_choice == 1);
|
||||||
|
printf("Selected: %s\n\n", is_commander ? "Commander" : "Responder");
|
||||||
|
|
||||||
|
uint8_t id_choice = (uint8_t)read_hex("Select LIN ID (0x00-0x3F): ", 0, 0x3F);
|
||||||
|
printf("Selected: 0x%02X\n\n", id_choice);
|
||||||
|
|
||||||
|
/* ===== Configure LIN ===== */
|
||||||
|
printf("Configuring %s... ", lin_names[lin_choice - 1]);
|
||||||
|
res = icsneoc2_settings_commander_resistor_set(device, selected_netid, is_commander);
|
||||||
|
res += icsneoc2_settings_baudrate_set(device, selected_netid, 19200);
|
||||||
|
res += icsneoc2_settings_lin_mode_set(device, selected_netid, icsneoc2_lin_mode_normal);
|
||||||
|
res += icsneoc2_settings_apply(device);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
icsneoc2_device_close(device);
|
||||||
|
icsneoc2_device_free(device);
|
||||||
|
return print_error_code("Failed to configure LIN", res);
|
||||||
|
}
|
||||||
|
printf("OK\n\n");
|
||||||
|
|
||||||
|
/* ===== Transmit Loop ===== */
|
||||||
|
printf("Transmitting on %s every second for 10 seconds...\n", lin_names[lin_choice - 1]);
|
||||||
|
uint8_t counter = 0;
|
||||||
|
for(int i = 0; i < 10; i++) {
|
||||||
|
icsneoc2_message_t* msg = NULL;
|
||||||
|
res = icsneoc2_message_lin_create(&msg, id_choice);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
print_error_code("\tFailed to create LIN message", res);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t data[] = {counter, counter + 1, counter + 2, counter + 3, 0xAA, 0xBB, 0xCC, 0xDD};
|
||||||
|
bool enhanced = true;
|
||||||
|
|
||||||
|
if(is_commander) {
|
||||||
|
/* Commander: send header-only frame to poll the bus */
|
||||||
|
icsneoc2_lin_msg_type_t msg_type = icsneoc2_lin_msg_type_header_only;
|
||||||
|
res = icsneoc2_message_lin_props_set(msg, NULL, NULL, &msg_type, &enhanced);
|
||||||
|
res += icsneoc2_message_netid_set(msg, selected_netid);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
icsneoc2_message_free(msg);
|
||||||
|
print_error_code("\tFailed to set commander properties", res);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
res = icsneoc2_device_message_transmit(device, msg);
|
||||||
|
icsneoc2_message_free(msg);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
print_error_code("\tFailed to transmit header", res);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Responder: update the responder table with new data */
|
||||||
|
icsneoc2_lin_msg_type_t msg_type = icsneoc2_lin_msg_type_update_responder;
|
||||||
|
res = icsneoc2_message_lin_props_set(msg, NULL, NULL, &msg_type, &enhanced);
|
||||||
|
res += icsneoc2_message_data_set(msg, data, sizeof(data));
|
||||||
|
res += icsneoc2_message_lin_calc_checksum(msg);
|
||||||
|
res += icsneoc2_message_netid_set(msg, selected_netid);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
icsneoc2_message_free(msg);
|
||||||
|
print_error_code("\tFailed to update responder", res);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
res = icsneoc2_device_message_transmit(device, msg);
|
||||||
|
icsneoc2_message_free(msg);
|
||||||
|
if(res != icsneoc2_error_success) {
|
||||||
|
print_error_code("\tFailed to transmit responder update", res);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("[%2d/10] Transmitted %s msg ID=0x%02X, counter=%u\n",
|
||||||
|
i + 1, is_commander ? "commander" : "responder", id_choice, counter);
|
||||||
|
counter += 4;
|
||||||
|
sleep_ms(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
printf("\nClosing device... ");
|
||||||
|
res = icsneoc2_device_close(device);
|
||||||
|
printf("%s\n", res == icsneoc2_error_success ? "OK" : "FAIL");
|
||||||
|
icsneoc2_device_free(device);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef __LINMESSAGE_H_
|
#ifndef __LINMESSAGE_H_
|
||||||
#define __LINMESSAGE_H_
|
#define __LINMESSAGE_H_
|
||||||
|
|
||||||
|
#include "icsneo/icsneoc2messages.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
||||||
#include "icsneo/communication/message/message.h"
|
#include "icsneo/communication/message/message.h"
|
||||||
|
|
@ -36,14 +38,14 @@ struct LINStatusFlags {
|
||||||
|
|
||||||
class LINMessage : public Frame {
|
class LINMessage : public Frame {
|
||||||
public:
|
public:
|
||||||
enum class Type : uint8_t {
|
enum class Type : icsneoc2_lin_msg_type_t {
|
||||||
NOT_SET = 0,
|
NOT_SET = icsneoc2_lin_msg_type_not_set,
|
||||||
LIN_COMMANDER_MSG,
|
LIN_COMMANDER_MSG = icsneoc2_lin_msg_type_commander_msg,
|
||||||
LIN_HEADER_ONLY,
|
LIN_HEADER_ONLY = icsneoc2_lin_msg_type_header_only,
|
||||||
LIN_BREAK_ONLY,
|
LIN_BREAK_ONLY = icsneoc2_lin_msg_type_break_only,
|
||||||
LIN_SYNC_ONLY,
|
LIN_SYNC_ONLY = icsneoc2_lin_msg_type_sync_only,
|
||||||
LIN_UPDATE_RESPONDER,
|
LIN_UPDATE_RESPONDER = icsneoc2_lin_msg_type_update_responder,
|
||||||
LIN_ERROR
|
LIN_ERROR = icsneoc2_lin_msg_type_error
|
||||||
};
|
};
|
||||||
|
|
||||||
static void calcChecksum(LINMessage& message);
|
static void calcChecksum(LINMessage& message);
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,139 @@ icsneoc2_error_t icsneoc2_message_is_frame(icsneoc2_message_t* message, bool* is
|
||||||
*/
|
*/
|
||||||
icsneoc2_error_t icsneoc2_message_is_can(icsneoc2_message_t* message, bool* is_can);
|
icsneoc2_error_t icsneoc2_message_is_can(icsneoc2_message_t* message, bool* is_can);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a message is a LIN message
|
||||||
|
*
|
||||||
|
* @param[in] message The message to check.
|
||||||
|
* @param[out] is_lin Pointer to a bool to copy the LIN 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_lin(icsneoc2_message_t* message, bool* is_lin);
|
||||||
|
|
||||||
|
// ---- LIN Message Types ----
|
||||||
|
|
||||||
|
typedef enum _icsneoc2_lin_msg_type_t {
|
||||||
|
icsneoc2_lin_msg_type_not_set = 0,
|
||||||
|
icsneoc2_lin_msg_type_commander_msg = 1,
|
||||||
|
icsneoc2_lin_msg_type_header_only = 2,
|
||||||
|
icsneoc2_lin_msg_type_break_only = 3,
|
||||||
|
icsneoc2_lin_msg_type_sync_only = 4,
|
||||||
|
icsneoc2_lin_msg_type_update_responder = 5,
|
||||||
|
icsneoc2_lin_msg_type_error = 6,
|
||||||
|
} _icsneoc2_lin_msg_type_t;
|
||||||
|
|
||||||
|
typedef uint8_t icsneoc2_lin_msg_type_t;
|
||||||
|
|
||||||
|
// LIN error flags bitmask
|
||||||
|
#define ICSNEOC2_LIN_ERR_RX_BREAK_ONLY 0x0001
|
||||||
|
#define ICSNEOC2_LIN_ERR_RX_BREAK_SYNC_ONLY 0x0002
|
||||||
|
#define ICSNEOC2_LIN_ERR_TX_RX_MISMATCH 0x0004
|
||||||
|
#define ICSNEOC2_LIN_ERR_RX_BREAK_NOT_ZERO 0x0008
|
||||||
|
#define ICSNEOC2_LIN_ERR_RX_BREAK_TOO_SHORT 0x0010
|
||||||
|
#define ICSNEOC2_LIN_ERR_RX_SYNC_NOT_55 0x0020
|
||||||
|
#define ICSNEOC2_LIN_ERR_RX_DATA_LEN_OVER_8 0x0040
|
||||||
|
#define ICSNEOC2_LIN_ERR_FRAME_SYNC 0x0080
|
||||||
|
#define ICSNEOC2_LIN_ERR_FRAME_MESSAGE_ID 0x0100
|
||||||
|
#define ICSNEOC2_LIN_ERR_FRAME_RESPONDER_DATA 0x0200
|
||||||
|
#define ICSNEOC2_LIN_ERR_CHECKSUM_MATCH 0x0400
|
||||||
|
|
||||||
|
typedef uint32_t icsneoc2_lin_err_flags_t;
|
||||||
|
|
||||||
|
// LIN status flags bitmask
|
||||||
|
#define ICSNEOC2_LIN_STATUS_TX_CHECKSUM_ENHANCED 0x01
|
||||||
|
#define ICSNEOC2_LIN_STATUS_TX_COMMANDER 0x02
|
||||||
|
#define ICSNEOC2_LIN_STATUS_TX_RESPONDER 0x04
|
||||||
|
#define ICSNEOC2_LIN_STATUS_TX_ABORTED 0x08
|
||||||
|
#define ICSNEOC2_LIN_STATUS_UPDATE_RESPONDER_ONCE 0x10
|
||||||
|
#define ICSNEOC2_LIN_STATUS_HAS_UPDATED_RESPONDER_ONCE 0x20
|
||||||
|
#define ICSNEOC2_LIN_STATUS_BUS_RECOVERED 0x40
|
||||||
|
#define ICSNEOC2_LIN_STATUS_BREAK_ONLY 0x80
|
||||||
|
|
||||||
|
typedef uint32_t icsneoc2_lin_status_flags_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a LIN message.
|
||||||
|
*
|
||||||
|
* @param[out] message Pointer to receive the new LIN message handle.
|
||||||
|
* @param[in] id The LIN frame ID (0-63). Bits above 0x3F are masked off.
|
||||||
|
*
|
||||||
|
* @return icsneoc2_error_t icsneoc2_error_success if successful.
|
||||||
|
*
|
||||||
|
* @see icsneoc2_message_free
|
||||||
|
*/
|
||||||
|
icsneoc2_error_t icsneoc2_message_lin_create(icsneoc2_message_t** message, uint8_t id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the LIN-specific properties of a message.
|
||||||
|
*
|
||||||
|
* Any output pointer may be NULL to skip that field.
|
||||||
|
*
|
||||||
|
* @param[in] message The message to query (must be a LIN message).
|
||||||
|
* @param[out] id Pointer to receive the LIN frame ID.
|
||||||
|
* @param[out] protected_id Pointer to receive the protected ID (ID with parity bits).
|
||||||
|
* @param[out] checksum Pointer to receive the checksum byte.
|
||||||
|
* @param[out] msg_type Pointer to receive the LIN message type.
|
||||||
|
* @param[out] is_enhanced_checksum Pointer to receive whether enhanced checksum is used.
|
||||||
|
*
|
||||||
|
* @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_type if not a LIN message.
|
||||||
|
*/
|
||||||
|
icsneoc2_error_t icsneoc2_message_lin_props_get(const icsneoc2_message_t* message,
|
||||||
|
uint8_t* id, uint8_t* protected_id, uint8_t* checksum,
|
||||||
|
icsneoc2_lin_msg_type_t* msg_type, bool* is_enhanced_checksum);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the LIN-specific properties of a message.
|
||||||
|
*
|
||||||
|
* Any input pointer may be NULL to skip that field. Setting the ID also recalculates the protected ID.
|
||||||
|
*
|
||||||
|
* @param[in] message The message to modify (must be a LIN message).
|
||||||
|
* @param[in] id Pointer to the LIN frame ID to set (0-63). NULL to skip.
|
||||||
|
* @param[in] checksum Pointer to the checksum byte to set. NULL to skip.
|
||||||
|
* @param[in] msg_type Pointer to the LIN message type to set. NULL to skip.
|
||||||
|
* @param[in] is_enhanced_checksum Pointer to set enhanced checksum mode. NULL to skip.
|
||||||
|
*
|
||||||
|
* @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_type if not a LIN message.
|
||||||
|
*/
|
||||||
|
icsneoc2_error_t icsneoc2_message_lin_props_set(icsneoc2_message_t* message,
|
||||||
|
const uint8_t* id, const uint8_t* checksum,
|
||||||
|
const icsneoc2_lin_msg_type_t* msg_type, const bool* is_enhanced_checksum);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the LIN error flags of a message.
|
||||||
|
*
|
||||||
|
* @param[in] message The message to query (must be a LIN message).
|
||||||
|
* @param[out] err_flags Pointer to receive the error flags bitmask.
|
||||||
|
*
|
||||||
|
* @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_type if not a LIN message.
|
||||||
|
*
|
||||||
|
* @see ICSNEOC2_LIN_ERR_*
|
||||||
|
*/
|
||||||
|
icsneoc2_error_t icsneoc2_message_lin_err_flags_get(const icsneoc2_message_t* message, icsneoc2_lin_err_flags_t* err_flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the LIN status flags of a message.
|
||||||
|
*
|
||||||
|
* @param[in] message The message to query (must be a LIN message).
|
||||||
|
* @param[out] status_flags Pointer to receive the status flags bitmask.
|
||||||
|
*
|
||||||
|
* @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_type if not a LIN message.
|
||||||
|
*
|
||||||
|
* @see ICSNEOC2_LIN_STATUS_*
|
||||||
|
*/
|
||||||
|
icsneoc2_error_t icsneoc2_message_lin_status_flags_get(const icsneoc2_message_t* message, icsneoc2_lin_status_flags_t* status_flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate and set the checksum on a LIN message.
|
||||||
|
*
|
||||||
|
* Uses enhanced or classic checksum based on the isEnhancedChecksum property.
|
||||||
|
*
|
||||||
|
* @param[in] message The LIN message to calculate the checksum for.
|
||||||
|
*
|
||||||
|
* @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_type if not a LIN message.
|
||||||
|
*/
|
||||||
|
icsneoc2_error_t icsneoc2_message_lin_calc_checksum(icsneoc2_message_t* message);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the network type of a message
|
* Get the network type of a message
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include <icsneo/icsneoc2settings.h>
|
#include <icsneo/icsneoc2settings.h>
|
||||||
#include <icsneo/icsneoc2messages.h>
|
#include <icsneo/icsneoc2messages.h>
|
||||||
#include <icsneo/device/devicetype.h>
|
#include <icsneo/device/devicetype.h>
|
||||||
|
#include <icsneo/communication/message/linmessage.h>
|
||||||
|
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -131,6 +132,15 @@ TEST(icsneoc2, test_icsneoc2_error_invalid_parameters_and_invalid_device)
|
||||||
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_valid(NULL, NULL));
|
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_message_is_valid(NULL, NULL));
|
||||||
|
|
||||||
|
// 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_lin_create(NULL, 0));
|
||||||
|
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_message_lin_props_get(NULL, NULL, NULL, NULL, NULL, NULL));
|
||||||
|
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_message_lin_props_set(NULL, NULL, NULL, NULL, NULL));
|
||||||
|
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_message_lin_err_flags_get(NULL, NULL));
|
||||||
|
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_message_lin_status_flags_get(NULL, NULL));
|
||||||
|
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_message_lin_calc_checksum(NULL));
|
||||||
|
|
||||||
// Test utility functions with NULL parameters
|
// Test utility functions with NULL parameters
|
||||||
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_version_get(NULL, NULL));
|
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_version_get(NULL, NULL));
|
||||||
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_serial_num_to_string(0, NULL, NULL));
|
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_serial_num_to_string(0, NULL, NULL));
|
||||||
|
|
@ -688,4 +698,228 @@ TEST(icsneoc2, test_icsneoc2_script_error_codes)
|
||||||
ASSERT_GT(len, 0u);
|
ASSERT_GT(len, 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(icsneoc2, test_lin_message_create_and_props)
|
||||||
|
{
|
||||||
|
// Create a LIN message with ID 0x15
|
||||||
|
icsneoc2_message_t* msg = nullptr;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_create(&msg, 0x15));
|
||||||
|
ASSERT_NE(msg, nullptr);
|
||||||
|
|
||||||
|
// Verify it reports as LIN
|
||||||
|
bool is_lin = false;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_is_lin(msg, &is_lin));
|
||||||
|
ASSERT_TRUE(is_lin);
|
||||||
|
|
||||||
|
// Verify it does NOT report as CAN
|
||||||
|
bool is_can = true;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_is_can(msg, &is_can));
|
||||||
|
ASSERT_FALSE(is_can);
|
||||||
|
|
||||||
|
// CAN props should fail on a LIN message
|
||||||
|
uint64_t arb_id = 0;
|
||||||
|
ASSERT_EQ(icsneoc2_error_invalid_type, icsneoc2_message_can_props_get(msg, &arb_id, NULL));
|
||||||
|
|
||||||
|
// Read back default props
|
||||||
|
uint8_t id = 0xFF, protected_id = 0, checksum = 0xFF;
|
||||||
|
icsneoc2_lin_msg_type_t msg_type = 0xFF;
|
||||||
|
bool enhanced = true;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_props_get(msg, &id, &protected_id, &checksum, &msg_type, &enhanced));
|
||||||
|
ASSERT_EQ(id, 0x15);
|
||||||
|
ASSERT_NE(protected_id, 0); // Should have parity bits
|
||||||
|
ASSERT_EQ(checksum, 0);
|
||||||
|
ASSERT_EQ(msg_type, icsneoc2_lin_msg_type_not_set);
|
||||||
|
ASSERT_FALSE(enhanced);
|
||||||
|
|
||||||
|
// ID should be masked to 6 bits
|
||||||
|
icsneoc2_message_t* msg_masked = nullptr;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_create(&msg_masked, 0xFF));
|
||||||
|
uint8_t masked_id = 0xFF;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_props_get(msg_masked, &masked_id, NULL, NULL, NULL, NULL));
|
||||||
|
ASSERT_EQ(masked_id, 0x3F); // 0xFF & 0x3F
|
||||||
|
icsneoc2_message_free(msg_masked);
|
||||||
|
|
||||||
|
icsneoc2_message_free(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(icsneoc2, test_lin_message_props_set)
|
||||||
|
{
|
||||||
|
icsneoc2_message_t* msg = nullptr;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_create(&msg, 0x00));
|
||||||
|
|
||||||
|
// Set individual properties using NULL to skip others
|
||||||
|
uint8_t new_id = 0x2A;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_props_set(msg, &new_id, NULL, NULL, NULL));
|
||||||
|
|
||||||
|
uint8_t read_id = 0, read_pid = 0;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_props_get(msg, &read_id, &read_pid, NULL, NULL, NULL));
|
||||||
|
ASSERT_EQ(read_id, 0x2A);
|
||||||
|
ASSERT_NE(read_pid, 0x2A); // Protected ID should differ (has parity bits)
|
||||||
|
|
||||||
|
// Set checksum
|
||||||
|
uint8_t new_checksum = 0xAB;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_props_set(msg, NULL, &new_checksum, NULL, NULL));
|
||||||
|
uint8_t read_checksum = 0;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_props_get(msg, NULL, NULL, &read_checksum, NULL, NULL));
|
||||||
|
ASSERT_EQ(read_checksum, 0xAB);
|
||||||
|
|
||||||
|
// Set msg type
|
||||||
|
icsneoc2_lin_msg_type_t new_type = icsneoc2_lin_msg_type_commander_msg;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_props_set(msg, NULL, NULL, &new_type, NULL));
|
||||||
|
icsneoc2_lin_msg_type_t read_type = 0;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_props_get(msg, NULL, NULL, NULL, &read_type, NULL));
|
||||||
|
ASSERT_EQ(read_type, icsneoc2_lin_msg_type_commander_msg);
|
||||||
|
|
||||||
|
// Set enhanced checksum
|
||||||
|
bool new_enhanced = true;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_props_set(msg, NULL, NULL, NULL, &new_enhanced));
|
||||||
|
bool read_enhanced = false;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_props_get(msg, NULL, NULL, NULL, NULL, &read_enhanced));
|
||||||
|
ASSERT_TRUE(read_enhanced);
|
||||||
|
|
||||||
|
// LIN props set on a CAN message should fail
|
||||||
|
icsneoc2_message_t* can_msg = nullptr;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_can_create(&can_msg));
|
||||||
|
ASSERT_EQ(icsneoc2_error_invalid_type, icsneoc2_message_lin_props_set(can_msg, &new_id, NULL, NULL, NULL));
|
||||||
|
ASSERT_EQ(icsneoc2_error_invalid_type, icsneoc2_message_lin_props_get(can_msg, &read_id, NULL, NULL, NULL, NULL));
|
||||||
|
icsneoc2_message_free(can_msg);
|
||||||
|
|
||||||
|
icsneoc2_message_free(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(icsneoc2, test_lin_message_data_and_netid)
|
||||||
|
{
|
||||||
|
icsneoc2_message_t* msg = nullptr;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_create(&msg, 0x10));
|
||||||
|
|
||||||
|
// Set data
|
||||||
|
uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_data_set(msg, data, sizeof(data)));
|
||||||
|
|
||||||
|
// Read data back
|
||||||
|
uint8_t read_data[8] = {0};
|
||||||
|
size_t read_len = sizeof(read_data);
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_data_get(msg, read_data, &read_len));
|
||||||
|
ASSERT_EQ(read_len, sizeof(data));
|
||||||
|
ASSERT_EQ(memcmp(data, read_data, sizeof(data)), 0);
|
||||||
|
|
||||||
|
// Set and verify netid
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_netid_set(msg, icsneoc2_netid_lin_01));
|
||||||
|
icsneoc2_netid_t netid = 0;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_netid_get(msg, &netid));
|
||||||
|
ASSERT_EQ(netid, icsneoc2_netid_lin_01);
|
||||||
|
|
||||||
|
// Verify is_frame and is_raw
|
||||||
|
bool is_frame = false, is_raw = false;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_is_frame(msg, &is_frame));
|
||||||
|
ASSERT_TRUE(is_frame);
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_is_raw(msg, &is_raw));
|
||||||
|
ASSERT_TRUE(is_raw);
|
||||||
|
|
||||||
|
icsneoc2_message_free(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(icsneoc2, test_lin_message_flags)
|
||||||
|
{
|
||||||
|
// Create a LIN message and verify default flags are clear
|
||||||
|
icsneoc2_message_t* msg = nullptr;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_create(&msg, 0x01));
|
||||||
|
|
||||||
|
icsneoc2_lin_err_flags_t err_flags = 0xFFFFFFFF;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_err_flags_get(msg, &err_flags));
|
||||||
|
ASSERT_EQ(err_flags, 0u);
|
||||||
|
|
||||||
|
icsneoc2_lin_status_flags_t status_flags = 0xFFFFFFFF;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_status_flags_get(msg, &status_flags));
|
||||||
|
ASSERT_EQ(status_flags, 0u);
|
||||||
|
|
||||||
|
// Error/status flags on CAN message should fail
|
||||||
|
icsneoc2_message_t* can_msg = nullptr;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_can_create(&can_msg));
|
||||||
|
ASSERT_EQ(icsneoc2_error_invalid_type, icsneoc2_message_lin_err_flags_get(can_msg, &err_flags));
|
||||||
|
ASSERT_EQ(icsneoc2_error_invalid_type, icsneoc2_message_lin_status_flags_get(can_msg, &status_flags));
|
||||||
|
icsneoc2_message_free(can_msg);
|
||||||
|
|
||||||
|
icsneoc2_message_free(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(icsneoc2, test_lin_message_calc_checksum)
|
||||||
|
{
|
||||||
|
icsneoc2_message_t* msg = nullptr;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_create(&msg, 0x01));
|
||||||
|
|
||||||
|
// Set some data and calculate checksum (classic)
|
||||||
|
uint8_t data[] = {0x01, 0x02, 0x03};
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_data_set(msg, data, sizeof(data)));
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_calc_checksum(msg));
|
||||||
|
|
||||||
|
uint8_t checksum = 0;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_props_get(msg, NULL, NULL, &checksum, NULL, NULL));
|
||||||
|
ASSERT_NE(checksum, 0); // Checksum should be non-zero for this data
|
||||||
|
|
||||||
|
// Now set enhanced checksum and recalculate — should give a different value
|
||||||
|
uint8_t classic_checksum = checksum;
|
||||||
|
bool enhanced = true;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_props_set(msg, NULL, NULL, NULL, &enhanced));
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_calc_checksum(msg));
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_lin_props_get(msg, NULL, NULL, &checksum, NULL, NULL));
|
||||||
|
ASSERT_NE(checksum, classic_checksum); // Enhanced and classic should differ
|
||||||
|
|
||||||
|
// calc_checksum on a CAN message should fail
|
||||||
|
icsneoc2_message_t* can_msg = nullptr;
|
||||||
|
ASSERT_EQ(icsneoc2_error_success, icsneoc2_message_can_create(&can_msg));
|
||||||
|
ASSERT_EQ(icsneoc2_error_invalid_type, icsneoc2_message_lin_calc_checksum(can_msg));
|
||||||
|
icsneoc2_message_free(can_msg);
|
||||||
|
|
||||||
|
icsneoc2_message_free(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(icsneoc2, test_lin_msg_type_enum_values)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(icsneoc2_lin_msg_type_not_set, 0);
|
||||||
|
ASSERT_EQ(icsneoc2_lin_msg_type_commander_msg, 1);
|
||||||
|
ASSERT_EQ(icsneoc2_lin_msg_type_header_only, 2);
|
||||||
|
ASSERT_EQ(icsneoc2_lin_msg_type_break_only, 3);
|
||||||
|
ASSERT_EQ(icsneoc2_lin_msg_type_sync_only, 4);
|
||||||
|
ASSERT_EQ(icsneoc2_lin_msg_type_update_responder, 5);
|
||||||
|
ASSERT_EQ(icsneoc2_lin_msg_type_error, 6);
|
||||||
|
ASSERT_EQ(sizeof(icsneoc2_lin_msg_type_t), sizeof(uint8_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(icsneoc2, test_lin_msg_type_cpp_enum_sync)
|
||||||
|
{
|
||||||
|
using T = icsneo::LINMessage::Type;
|
||||||
|
ASSERT_EQ(static_cast<uint8_t>(T::NOT_SET), icsneoc2_lin_msg_type_not_set);
|
||||||
|
ASSERT_EQ(static_cast<uint8_t>(T::LIN_COMMANDER_MSG), icsneoc2_lin_msg_type_commander_msg);
|
||||||
|
ASSERT_EQ(static_cast<uint8_t>(T::LIN_HEADER_ONLY), icsneoc2_lin_msg_type_header_only);
|
||||||
|
ASSERT_EQ(static_cast<uint8_t>(T::LIN_BREAK_ONLY), icsneoc2_lin_msg_type_break_only);
|
||||||
|
ASSERT_EQ(static_cast<uint8_t>(T::LIN_SYNC_ONLY), icsneoc2_lin_msg_type_sync_only);
|
||||||
|
ASSERT_EQ(static_cast<uint8_t>(T::LIN_UPDATE_RESPONDER), icsneoc2_lin_msg_type_update_responder);
|
||||||
|
ASSERT_EQ(static_cast<uint8_t>(T::LIN_ERROR), icsneoc2_lin_msg_type_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(icsneoc2, test_lin_flag_bitmask_values)
|
||||||
|
{
|
||||||
|
// Error flags should be distinct bits
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_ERR_RX_BREAK_ONLY, 0x0001);
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_ERR_RX_BREAK_SYNC_ONLY, 0x0002);
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_ERR_TX_RX_MISMATCH, 0x0004);
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_ERR_RX_BREAK_NOT_ZERO, 0x0008);
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_ERR_RX_BREAK_TOO_SHORT, 0x0010);
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_ERR_RX_SYNC_NOT_55, 0x0020);
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_ERR_RX_DATA_LEN_OVER_8, 0x0040);
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_ERR_FRAME_SYNC, 0x0080);
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_ERR_FRAME_MESSAGE_ID, 0x0100);
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_ERR_FRAME_RESPONDER_DATA, 0x0200);
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_ERR_CHECKSUM_MATCH, 0x0400);
|
||||||
|
|
||||||
|
// Status flags should be distinct bits
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_STATUS_TX_CHECKSUM_ENHANCED, 0x01);
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_STATUS_TX_COMMANDER, 0x02);
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_STATUS_TX_RESPONDER, 0x04);
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_STATUS_TX_ABORTED, 0x08);
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_STATUS_UPDATE_RESPONDER_ONCE, 0x10);
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_STATUS_HAS_UPDATED_RESPONDER_ONCE, 0x20);
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_STATUS_BUS_RECOVERED, 0x40);
|
||||||
|
ASSERT_EQ(ICSNEOC2_LIN_STATUS_BREAK_ONLY, 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue