LIN: Network support

pull/56/head
Kyle Johannes 2023-02-03 18:27:08 +00:00
parent 4229d8b66a
commit 539cfa511b
13 changed files with 723 additions and 4 deletions

View File

@ -174,6 +174,7 @@ set(SRC_FILES
communication/message/callback/streamoutput/a2bwavoutput.cpp communication/message/callback/streamoutput/a2bwavoutput.cpp
communication/message/neomessage.cpp communication/message/neomessage.cpp
communication/message/ethphymessage.cpp communication/message/ethphymessage.cpp
communication/message/linmessage.cpp
communication/packet/flexraypacket.cpp communication/packet/flexraypacket.cpp
communication/packet/canpacket.cpp communication/packet/canpacket.cpp
communication/packet/a2bpacket.cpp communication/packet/a2bpacket.cpp
@ -184,6 +185,7 @@ set(SRC_FILES
communication/packet/logicaldiskinfopacket.cpp communication/packet/logicaldiskinfopacket.cpp
communication/packet/wivicommandpacket.cpp communication/packet/wivicommandpacket.cpp
communication/packet/i2cpacket.cpp communication/packet/i2cpacket.cpp
communication/packet/linpacket.cpp
communication/packet/scriptstatuspacket.cpp communication/packet/scriptstatuspacket.cpp
communication/decoder.cpp communication/decoder.cpp
communication/encoder.cpp communication/encoder.cpp
@ -394,6 +396,7 @@ if(LIBICSNEO_BUILD_TESTS)
test/eventmanagertest.cpp test/eventmanagertest.cpp
test/ethernetpacketizertest.cpp test/ethernetpacketizertest.cpp
test/i2cencoderdecodertest.cpp test/i2cencoderdecodertest.cpp
test/linencoderdecodertest.cpp
test/a2bencoderdecodertest.cpp test/a2bencoderdecodertest.cpp
) )

View File

@ -11,6 +11,7 @@
#include "icsneo/communication/message/a2bmessage.h" #include "icsneo/communication/message/a2bmessage.h"
#include "icsneo/communication/message/flexray/control/flexraycontrolmessage.h" #include "icsneo/communication/message/flexray/control/flexraycontrolmessage.h"
#include "icsneo/communication/message/i2cmessage.h" #include "icsneo/communication/message/i2cmessage.h"
#include "icsneo/communication/message/linmessage.h"
#include "icsneo/communication/command.h" #include "icsneo/communication/command.h"
#include "icsneo/device/device.h" #include "icsneo/device/device.h"
#include "icsneo/communication/packet/canpacket.h" #include "icsneo/communication/packet/canpacket.h"
@ -24,6 +25,7 @@
#include "icsneo/communication/packet/wivicommandpacket.h" #include "icsneo/communication/packet/wivicommandpacket.h"
#include "icsneo/communication/packet/i2cpacket.h" #include "icsneo/communication/packet/i2cpacket.h"
#include "icsneo/communication/packet/scriptstatuspacket.h" #include "icsneo/communication/packet/scriptstatuspacket.h"
#include "icsneo/communication/packet/linpacket.h"
#include <iostream> #include <iostream>
using namespace icsneo; using namespace icsneo;
@ -362,6 +364,18 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
msg.network = packet->network; msg.network = packet->network;
return true; return true;
} }
case Network::Type::LIN: {
result = HardwareLINPacket::DecodeToMessage(packet->data);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false; // A nullptr was returned, the packet was not long enough to decode
}
LINMessage& msg = *static_cast<LINMessage*>(result.get());
msg.network = packet->network;
return true;
}
} }
// For the moment other types of messages will automatically be decoded as raw messages // For the moment other types of messages will automatically be decoded as raw messages

View File

@ -9,7 +9,7 @@
#include "icsneo/communication/packet/i2cpacket.h" #include "icsneo/communication/packet/i2cpacket.h"
#include "icsneo/communication/message/i2cmessage.h" #include "icsneo/communication/message/i2cmessage.h"
#include "icsneo/communication/packet/a2bpacket.h" #include "icsneo/communication/packet/a2bpacket.h"
#include "icsneo/communication/packet/linpacket.h"
using namespace icsneo; using namespace icsneo;
@ -96,6 +96,18 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
} }
break; break;
} // End of Network::Type::I2C } // End of Network::Type::I2C
case Network::Type::LIN: {
auto linmsg = std::dynamic_pointer_cast<LINMessage>(message);
if(!linmsg) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false;
}
buffer = &result;
if(!HardwareLINPacket::EncodeFromMessage(*linmsg, result, report)) {
return false;
}
break;
} // End of Network::Type::LIN
default: default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error); report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return false; return false;
@ -182,7 +194,8 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
// Size for the host-to-device long format is the size of the entire packet + 1 // Size for the host-to-device long format is the size of the entire packet + 1
// So +1 for AA header, +1 for short format header, +2 for long format size, and +2 for long format NetID // So +1 for AA header, +1 for short format header, +2 for long format size, and +2 for long format NetID
// Then an extra +1, due to a firmware idiosyncrasy // Then an extra +1, due to a firmware idiosyncrasy
uint16_t size = uint16_t(buffer->size()) + 1 + 1 + 2 + 2 + 1; uint16_t size = static_cast<uint16_t>(buffer->size()) + 1 + 1 + 2 + 2 + 1;
buffer->insert(buffer->begin(), { buffer->insert(buffer->begin(), {
(uint8_t)Network::NetID::RED, // 0x0C for long message (uint8_t)Network::NetID::RED, // 0x0C for long message
(uint8_t)size, // Size, little endian 16-bit (uint8_t)size, // Size, little endian 16-bit

View File

@ -0,0 +1,22 @@
#include "icsneo/communication/message/linmessage.h"
#include <numeric>
namespace icsneo {
void LINMessage::calcChecksum(LINMessage& message) {
uint16_t sum = 0;
auto limitFunc = [](uint16_t x, uint16_t y) -> uint16_t {
if ((x + y) > 0xFFu)
return ((x + y) - 0xFFu);
else
return (x + y);
};
message.checksum = static_cast<uint8_t>(std::accumulate(message.data.begin(), message.data.end(), sum, limitFunc));
if(message.isEnhancedChecksum)
message.checksum = static_cast<uint8_t>(limitFunc(message.checksum, message.protectedID));
message.checksum ^= 0xFFu;
}
} //namespace icsneo

View File

@ -0,0 +1,153 @@
#include "icsneo/communication/packet/linpacket.h"
#include "icsneo/communication/message/linmessage.h"
#include "icsneo/communication/packetizer.h"
namespace icsneo {
std::shared_ptr<Message> HardwareLINPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
auto msg = std::make_shared<LINMessage>();
const HardwareLINPacket* packet = reinterpret_cast<const HardwareLINPacket*>(bytestream.data());
size_t numDataBytes = packet->CoreMiniBitsLIN.len;
size_t numHeaderBytes = sizeof(HardwareLINPacket::CoreMiniBitsLIN);
if( (sizeof(HardwareLINPacket) != bytestream.size()) ||
((numDataBytes + numHeaderBytes) > bytestream.size()) )
return nullptr;
if(numDataBytes)
--numDataBytes; //If data is present, there will be a checksum included
msg->network = Network::GetNetIDFromCoreMiniNetwork(static_cast<Network::CoreMini>(packet->networkID));
msg->protectedID = packet->CoreMiniBitsLIN.ID;
msg->ID = (packet->CoreMiniBitsLIN.ID & 0x3Fu);
msg->isEnhancedChecksum = static_cast<bool>(packet->CoreMiniBitsLIN.TxChkSumEnhanced);
/* Minimum one responder byte and one checksum byte. */
if(2u > packet->CoreMiniBitsLIN.len)
msg->type = LINMessage::Type::LIN_ERROR;
auto dataStart = bytestream.begin() + numHeaderBytes;
std::copy(dataStart, (dataStart+numDataBytes), std::back_inserter(msg->data));
/* If OK, validate the checksum*/
auto isChecksumInvalid = [&]() -> bool {
/* messages with no data have no checksum (e.g. header only) */
if(!msg->data.size())
return true;
uint8_t checkSum = (8 > numDataBytes) ? *(dataStart + numDataBytes) : packet->CoreMiniBitsLIN.LINByte9;
LINMessage::calcChecksum(*msg);
if(checkSum != msg->checksum) {
msg->isEnhancedChecksum = true;
LINMessage::calcChecksum(*msg);
if(checkSum != msg->checksum) {
msg->isEnhancedChecksum = false;
msg->checksum = checkSum;
return true;
}
}
return false;
};
/* if any of the status bits are set, then this is
either a failed reception or a bus status update. */
msg->errFlags =
{
static_cast<bool>(packet->CoreMiniBitsLIN.ErrRxOnlyBreak),
static_cast<bool>(packet->CoreMiniBitsLIN.ErrRxOnlyBreakSync),
static_cast<bool>(packet->CoreMiniBitsLIN.ErrTxRxMismatch),
static_cast<bool>(packet->CoreMiniBitsLIN.ErrRxBreakNotZero),
static_cast<bool>(packet->CoreMiniBitsLIN.ErrRxBreakTooShort),
static_cast<bool>(packet->CoreMiniBitsLIN.ErrRxSyncNot55),
static_cast<bool>(packet->CoreMiniBitsLIN.ErrRxDataGreater8),
static_cast<bool>(packet->CoreMiniBitsLIN.SyncFerr),
static_cast<bool>(packet->CoreMiniBitsLIN.MidFerr),
static_cast<bool>(packet->CoreMiniBitsLIN.ResponderByteFerr),
isChecksumInvalid(), /* ErrChecksumMatch */
};
msg->statusFlags =
{
static_cast<bool>(packet->CoreMiniBitsLIN.TxChkSumEnhanced),
static_cast<bool>(packet->CoreMiniBitsLIN.TXCommander),
static_cast<bool>(packet->CoreMiniBitsLIN.TXResponder),
static_cast<bool>(packet->CoreMiniBitsLIN.TxAborted),
static_cast<bool>(packet->CoreMiniBitsLIN.UpdateResponderOnce),
static_cast<bool>(packet->CoreMiniBitsLIN.HasUpdatedResponderOnce),
static_cast<bool>(packet->CoreMiniBitsLIN.BusRecovered),
static_cast<bool>(packet->CoreMiniBitsLIN.BreakOnly)
};
if(msg->statusFlags.TxCommander || msg->statusFlags.TxResponder)
msg->type = LINMessage::Type::LIN_COMMANDER_MSG;
else if(msg->statusFlags.BreakOnly)
msg->type = LINMessage::Type::LIN_BREAK_ONLY;
if( msg->errFlags.ErrRxBreakOnly || msg->errFlags.ErrRxBreakSyncOnly ||
msg->errFlags.ErrTxRxMismatch || msg->errFlags.ErrRxBreakNotZero ||
msg->errFlags.ErrRxBreakTooShort || msg->errFlags.ErrRxSyncNot55 ||
msg->errFlags.ErrRxDataLenOver8 || msg->errFlags.ErrFrameSync ||
msg->errFlags.ErrFrameMessageID || msg->errFlags.ErrChecksumMatch ||
msg->errFlags.ErrFrameResponderData )
{ msg->type = LINMessage::Type::LIN_ERROR; }
msg->timestamp = packet->timestamp;
return msg;
}
bool HardwareLINPacket::EncodeFromMessage(LINMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report)
{
uint8_t size = ((std::min<size_t>(8ul, message.data.size()) + 3ul) & 0xFu);
if(size > 3) { ++size; } // add a checksum byte if there's data
switch(message.type) {
case LINMessage::Type::LIN_HEADER_ONLY:
case LINMessage::Type::LIN_COMMANDER_MSG:
{
size |= 0x80u;
break;
}
case LINMessage::Type::LIN_BREAK_ONLY:
{
size |= 0x20u;
break;
}
case LINMessage::Type::NOT_SET:
{
report(APIEvent::Type::RequiredParameterNull, APIEvent::Severity::Error);
return false;
}
default:
break;
}
message.protectedID = message.ID;
auto bit = [&](uint8_t pos)->uint8_t { return ((message.protectedID >> pos) & 0x1u); };
message.protectedID |= (~(bit(1) ^ bit(3) ^ bit(4) ^ bit(5)) << 7);
message.protectedID |= ((bit(0) ^ bit(1) ^ bit(2) ^ bit(4)) << 6);
bytestream.insert(bytestream.end(),
{
static_cast<uint8_t>(0x00u),
static_cast<uint8_t>(size),
static_cast<uint8_t>((message.description >> 8) & 0xFF),
static_cast<uint8_t>(message.description & 0xFF),
static_cast<uint8_t>(message.protectedID)
});
switch(message.type) {
case(LINMessage::Type::LIN_COMMANDER_MSG):
case(LINMessage::Type::LIN_UPDATE_RESPONDER):
{
std::copy(message.data.begin(), message.data.end(), std::back_inserter(bytestream));
LINMessage::calcChecksum(message);
bytestream.push_back(message.checksum);
break;
}
default:
break;
}
if(bytestream.size() % 2)
bytestream.push_back(0x41); //padding
return true;
}
} //namespace icsneo

View File

@ -2,8 +2,7 @@ option(LIBICSNEO_BUILD_C_INTERACTIVE_EXAMPLE "Build the command-line interactive
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)
option(LIBICSNEO_BUILD_CPP_LIN_EXAMPLE "Build the LIN example." ON)
# Disabled until we properly build these in-tree # Disabled until we properly build these in-tree
# option(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE "Build the command-line interactive C# example." OFF) # option(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE "Build the command-line interactive C# example." OFF)
# option(LIBICSNEO_BUILD_JAVA_INTERACTIVE_EXAMPLE "Build the command-line interactive Java example." OFF) # option(LIBICSNEO_BUILD_JAVA_INTERACTIVE_EXAMPLE "Build the command-line interactive Java example." OFF)
@ -24,6 +23,9 @@ if(LIBICSNEO_BUILD_CPP_A2B_EXAMPLE)
add_subdirectory(cpp/a2b) add_subdirectory(cpp/a2b)
endif() endif()
if(LIBICSNEO_BUILD_CPP_LIN_EXAMPLE)
add_subdirectory(cpp/lin)
endif()
# if(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE) # if(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE)
# add_subdirectory(csharp) # add_subdirectory(csharp)
# endif() # endif()

View File

@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-lin VERSION 0.1.0)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-lin src/LINExample.cpp)
target_link_libraries(libicsneocpp-lin icsneocpp)

View File

@ -0,0 +1,151 @@
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
#include "icsneo/icsneocpp.h"
#include "icsneo/communication/message/linmessage.h"
/* Note: This example requires LIN 1 and LIN 2 channels to be connected on the device */
char getCharInput(std::vector<char> allowed) {
bool found = false;
std::string input;
while(!found) {
std::cin >> input;
if(input.length() == 1) {
for(char compare : allowed) {
if(compare == input.c_str()[0]) {
found = true;
break;
}
}
}
if(!found) {
std::cout << "Input did not match expected options. Please try again." << std::endl;
std::cout << "<X or x to quit>" << std::endl;
}
}
return input.c_str()[0];
}
int main() {
// Print version
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
std::cout << "\nFinding devices... " << std::flush;
auto devices = icsneo::FindAllDevices(); // This is type std::vector<std::shared_ptr<icsneo::Device>>
// You now hold the shared_ptrs for these devices, you are considered to "own" these devices from a memory perspective
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
// List off the devices
for(auto& device : devices)
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
std::cout << std::endl;
for(auto& device : devices) {
std::cout << "Connecting to " << device->describe() << "... ";
bool ret = device->open();
if(!ret) { // Failed to open
std::cout << "FAIL" << std::endl;
std::cout << icsneo::GetLastError() << std::endl << std::endl;
continue;
}
std::cout << "OK" << std::endl;
// The concept of going "online" tells the connected device to start listening, i.e. ACKing traffic and giving it to us
std::cout << "\tGoing online... ";
ret = device->goOnline();
if(!ret) {
std::cout << "FAIL" << std::endl;
device->close();
continue;
}
std::cout << "OK" << std::endl;
// A real application would just check the result of icsneo_goOnline() rather than calling this
// This function is intended to be called later on if needed
std::cout << "\tChecking online status... ";
ret = device->isOnline();
if(!ret) {
std::cout << "FAIL\n" << std::endl;
device->close();
continue;
}
std::cout << "OK" << std::endl;
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> message) {
if(icsneo::Message::Type::Frame == message->type) {
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
if(icsneo::Network::Type::LIN == frame->network.getType()) {
auto msg = std::static_pointer_cast<icsneo::LINMessage>(message);
std::cout << msg->network << " RX frame | ID: 0x" << std::hex << static_cast<int>(msg->ID) << " | ";
std::cout << "Protected ID: 0x" << static_cast<int>(msg->protectedID) << "\n" << "Data: ";
for(uint8_t& each : msg->data) {
std::cout << "0x" << static_cast<int>(each) << " ";
}
std::cout << "\nChecksum type: " << (msg->isEnhancedChecksum ? "Enhanced" : "Classic");
std::cout << "\nChecksum: 0x" << static_cast<int>(msg->checksum) << "\n";
std::cout << "Is checksum valid: " << ((!msg->errFlags.ErrChecksumMatch) ? "yes" : "no") << "\n\n";
}
}
}));
// We can transmit messages
std::cout << "\tTransmitting a LIN responder data frame... ";
auto lin_r = std::make_shared<icsneo::LINMessage>();
lin_r->network = icsneo::Network::NetID::LIN2;
lin_r->ID = 0x11;
lin_r->type = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER;
lin_r->data = {0xaa, 0xbb, 0xcc, 0xdd, 0x11, 0x22, 0x33, 0x44};
ret = device->transmit(lin_r); // This will return false if the device does not support LIN
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "\tTransmitting a LIN commander frame... ";
auto lin_c = std::make_shared<icsneo::LINMessage>();
lin_c->network = icsneo::Network::NetID::LIN;
lin_c->ID = 0x11;
lin_c->type = icsneo::LINMessage::Type::LIN_HEADER_ONLY;
ret = device->transmit(lin_c);
std::cout << (ret ? "OK" : "FAIL") << std::endl << std::endl;
std::cout << "\tTransmitting a LIN commander frame with responder data... ";
auto lin_d = std::make_shared<icsneo::LINMessage>();
lin_d->network = icsneo::Network::NetID::LIN;
lin_d->ID = 0x22;
lin_d->isEnhancedChecksum = true;
lin_d->type = icsneo::LINMessage::Type::LIN_COMMANDER_MSG;
lin_d->data = {0x11, 0x22, 0x33, 0x44, 0xaa, 0xbb, 0xcc, 0xdd};
ret = device->transmit(lin_d);
std::cout << (ret ? "OK" : "FAIL") << std::endl << std::endl;
std::cout << "<X or x to quit>\n\n";
// Go offline, stop sending and receiving traffic
auto shutdown = [&](){
device->removeMessageCallback(handler);
std::cout << "\tGoing offline... ";
ret = device->goOffline();
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "\tDisconnecting... ";
ret = device->close();
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
};
while(true) {
char input = getCharInput(std::vector<char> {'X', 'x'});
switch(input) {
case 'X':
case 'x':
shutdown();
printf("Exiting program\n");
return 0;
default:
break;
}
}
}
return 0;
}

View File

@ -0,0 +1,65 @@
#ifndef __LINMESSAGE_H_
#define __LINMESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
#include "icsneo/communication/network.h"
#include <vector>
namespace icsneo {
struct LINErrorFlags {
bool ErrRxBreakOnly = false;
bool ErrRxBreakSyncOnly = false;
bool ErrTxRxMismatch = false;
bool ErrRxBreakNotZero = false;
bool ErrRxBreakTooShort = false;
bool ErrRxSyncNot55 = false;
bool ErrRxDataLenOver8 = false;
bool ErrFrameSync = false;
bool ErrFrameMessageID = false;
bool ErrFrameResponderData = false;
bool ErrChecksumMatch = false;
};
struct LINStatusFlags {
bool TxChecksumEnhanced = false;
bool TxCommander = false;
bool TxResponder = false;
bool TxAborted = false;
bool UpdateResponderOnce = false;
bool HasUpdatedResponderOnce = false;
bool BusRecovered = false;
bool BreakOnly = false;
};
class LINMessage : public Frame {
public:
static void calcChecksum(LINMessage& message);
enum class Type {
NOT_SET,
LIN_COMMANDER_MSG,
LIN_HEADER_ONLY,
LIN_BREAK_ONLY,
LIN_SYNC_ONLY,
LIN_UPDATE_RESPONDER,
LIN_ERROR
};
uint8_t ID = 0;
uint8_t protectedID = 0;
uint8_t checksum = 0;
LINMessage::Type type = Type::NOT_SET;
bool isEnhancedChecksum = false;
bool isLINStd2x = true;
LINErrorFlags errFlags;
LINStatusFlags statusFlags;
};
}
#endif // __cplusplus
#endif

View File

@ -18,6 +18,9 @@ public:
CANErrorCount = 0x100, CANErrorCount = 0x100,
LINHeaderOnly = 0x200,
LINBreak = 0x201,
// Past 0x8000 are all for internal use only // Past 0x8000 are all for internal use only
Invalid = 0x8000, Invalid = 0x8000,
RawMessage = 0x8001, RawMessage = 0x8001,

View File

@ -0,0 +1,66 @@
#ifndef __LINPACKET_H__
#define __LINPACKET_H__
#ifdef __cplusplus
#include "icsneo/communication/message/linmessage.h"
#include "icsneo/api/eventmanager.h"
#include "icsneo/communication/packetizer.h"
#include "icsneo/communication/network.h"
#include <cstdint>
#include <memory>
namespace icsneo {
#pragma pack(push, 2)
struct HardwareLINPacket {
static std::shared_ptr<Message> DecodeToMessage(const std::vector<uint8_t>& bytestream);
static bool EncodeFromMessage(LINMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report);
struct {
//CxLIN3
uint16_t ErrRxOnlyBreak : 1;
uint16_t ErrRxOnlyBreakSync : 1;
uint16_t ID : 11;
uint16_t NETWORKINDEX : 3;//DO NOT CLOBBER THIS
// CxLIN
uint8_t LINByte9;
uint8_t ErrTxRxMismatch : 1;
uint8_t TxChkSumEnhanced : 1;
uint8_t TXCommander : 1;
uint8_t TXResponder : 1;
uint8_t ErrRxBreakNotZero : 1;
uint8_t ErrRxBreakTooShort : 1;
uint8_t ErrRxSyncNot55 : 1;
uint8_t ErrRxDataGreater8 : 1;
// CxLIN2
uint16_t len : 4;
uint16_t ExtendedNetworkIndexBit2 : 1;//DO NOT CLOBBER THIS
uint16_t UpdateResponderOnce : 1;
uint16_t HasUpdatedResponderOnce : 1;
uint16_t ExtendedNetworkIndexBit : 1;//DO NOT CLOBBER THIS
uint16_t BusRecovered : 1;
uint16_t SyncFerr : 1; //Framing error in sync byte
uint16_t MidFerr : 1; // Framing error in message id
uint16_t ResponderByteFerr : 1; //Framing error in one of our responder bytes.
uint16_t TxAborted : 1;//!< This transmit was aborted.
uint16_t BreakOnly : 1;
uint16_t : 2;
} CoreMiniBitsLIN;
uint8_t data[8];
uint16_t stats; //CxTRB0STAT
uint64_t timestamp; //Large timestamp
//CoreMiniMsgExtendedHdr
uint16_t networkID;
uint16_t length;
};
#pragma pack(pop)
} //namespace libicsneo
#endif //_cplusplus
#endif

View File

@ -18,6 +18,7 @@
#include "icsneo/communication/message/ethphymessage.h" #include "icsneo/communication/message/ethphymessage.h"
#include "icsneo/communication/message/i2cmessage.h" #include "icsneo/communication/message/i2cmessage.h"
#include "icsneo/communication/message/a2bmessage.h" #include "icsneo/communication/message/a2bmessage.h"
#include "icsneo/communication/message/linmessage.h"
#include "icsneo/communication/message/callback/streamoutput/a2bwavoutput.h" #include "icsneo/communication/message/callback/streamoutput/a2bwavoutput.h"

View File

@ -0,0 +1,199 @@
#include "icsneo/icsneocpp.h"
#include "icsneo/communication/encoder.h"
#include "icsneo/communication/packet/linpacket.h"
#include "icsneo/communication/message/linmessage.h"
#include "icsneo/communication/packetizer.h"
#include "icsneo/api/eventmanager.h"
#include "gtest/gtest.h"
#include <vector>
#include <iostream>
using namespace icsneo;
class LINEncoderDecoderTest : public ::testing::Test {
protected:
void SetUp() override {
report = [](APIEvent::Type, APIEvent::Severity) {
// Unless caught by the test, the packetizer should not throw errors
EXPECT_TRUE(false);
};
packetizer.emplace([this](APIEvent::Type t, APIEvent::Severity s) {
report(t, s);
});
packetEncoder.emplace([this](APIEvent::Type t, APIEvent::Severity s) {
report(t, s);
});
packetDecoder.emplace([this](APIEvent::Type t, APIEvent::Severity s) {
report(t, s);
});
}
device_eventhandler_t report;
std::optional<Encoder> packetEncoder;
std::optional<Packetizer> packetizer;
std::optional<Decoder> packetDecoder;
//Responder load data before response LIN 2
// ID 0x22 pID 0xE2 length 8
std::vector<uint8_t> testRespData =
{0xaa, 0x0c,
0x15, 0x00,
0x30, 0x00,
0x00, 0x0c,
0x00, 0x00,
0xe2,
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
0x99};
//Controller header LIN 1
// ID 0x22 pID 0xE2 length 8
std::vector<uint8_t> testControllerHeaderOnly =
{0xaa, 0x0c,
0x0d, 0x00,
0x10, 0x00,
0x00, 0x83,
0x00, 0x00,
0xE2, 0x41};
std::vector<uint8_t> recvBytes =
{0xaa, 0x0c, 0x22, 0x00,
0x10, 0x00, 0x88, 0x03,
0x00, 0x08, 0x04, 0x00,
0xaa, 0xbb, 0xcc, 0xcc,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xb3, 0x34,
0xa8, 0x10, 0x29, 0x13,
0x48, 0x00, 0x02, 0x00,
0x00, 0x00,
0xaa, 0x0c, 0x22, 0x00,
0x30, 0x00, 0x88, 0x03,
0x00, 0x04, 0x04, 0x00,
0xaa, 0xbb, 0xcc, 0xcc,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xb4, 0x34,
0xa8, 0x10, 0x29, 0x13,
0x48, 0x00, 0x03, 0x00,
0x00, 0x00};
std::vector<uint8_t> testControllerWithData =
{0xaa, 0x0c,
0x11, 0x00,
0x10, 0x00,
0x00, 0x87,
0x00, 0x00,
0x11, 0xaa,
0xbb, 0xcc,
0xcc, 0x41};
};
TEST_F(LINEncoderDecoderTest, ProtectedIDCalcTest) {
std::vector<uint8_t> bytestream;
auto message = std::make_shared<icsneo::LINMessage>();
message->network = icsneo::Network::NetID::LIN;
message->ID = 0x22;
message->type = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER;
message->isEnhancedChecksum = false;
packetEncoder->encode(*packetizer, bytestream, message);
EXPECT_EQ(message->protectedID, 0xE2);
}
TEST_F(LINEncoderDecoderTest, ChecksumCalcTestClassic) {
std::vector<uint8_t> bytestream;
auto message = std::make_shared<icsneo::LINMessage>();
message->network = icsneo::Network::NetID::LIN2;
message->ID = 0x22;
message->type = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER;
message->isEnhancedChecksum = false;
message->data = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
packetEncoder->encode(*packetizer, bytestream, message);
EXPECT_EQ(message->checksum, 0x99);
}
TEST_F(LINEncoderDecoderTest, ChecksumCalcTestEnhanced) {
std::vector<uint8_t> bytestream;
auto message = std::make_shared<icsneo::LINMessage>();
message->network = icsneo::Network::NetID::LIN2;
message->ID = 0x22;
message->type = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER;
message->isEnhancedChecksum = true;
message->data = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
packetEncoder->encode(*packetizer, bytestream, message);
EXPECT_EQ(message->checksum, 0xB6);
}
TEST_F(LINEncoderDecoderTest, PacketEncoderResponderLoadTest) {
std::vector<uint8_t> bytestream;
auto message = std::make_shared<icsneo::LINMessage>();
message->network = icsneo::Network::NetID::LIN2;
message->ID = 0x22;
message->type = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER;
message->isEnhancedChecksum = false;
message->data = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
packetEncoder->encode(*packetizer, bytestream, message);
EXPECT_EQ(bytestream, testRespData);
}
TEST_F(LINEncoderDecoderTest, PacketEncoderControllerHeaderTest) {
std::vector<uint8_t> bytestream;
auto message = std::make_shared<icsneo::LINMessage>();
message->network = icsneo::Network::NetID::LIN;
message->ID = 0x22;
message->type = icsneo::LINMessage::Type::LIN_HEADER_ONLY;
message->isEnhancedChecksum = false;
packetEncoder->encode(*packetizer, bytestream, message);
EXPECT_EQ(bytestream, testControllerHeaderOnly);
}
TEST_F(LINEncoderDecoderTest, PacketEncoderControllerWithDataTest) {
std::vector<uint8_t> bytestream;
auto message = std::make_shared<icsneo::LINMessage>();
message->network = icsneo::Network::NetID::LIN;
message->ID = 0x11;
message->type = icsneo::LINMessage::Type::LIN_COMMANDER_MSG;
message->isEnhancedChecksum = false;
message->data = {0xaa, 0xbb, 0xcc};
packetEncoder->encode(*packetizer, bytestream, message);
EXPECT_EQ(bytestream, testControllerWithData);
}
TEST_F(LINEncoderDecoderTest, PacketDecoderTest) {
std::shared_ptr<icsneo::Message> decodeMsg;
auto msg1 = std::make_shared<icsneo::LINMessage>();
auto msg2 = std::make_shared<icsneo::LINMessage>();
msg1->network = icsneo::Network::NetID::LIN2;
msg1->ID = 0x22;
msg1->type = icsneo::LINMessage::Type::LIN_COMMANDER_MSG;
msg1->isEnhancedChecksum = false;
msg1->data = {0xaa, 0xbb, 0xcc};
msg1->checksum = 0xcc;
msg2->network = icsneo::Network::NetID::LIN;
msg2->ID = 0x22;
msg2->type = icsneo::LINMessage::Type::LIN_COMMANDER_MSG;
msg2->isEnhancedChecksum = false;
msg2->data = {0xaa, 0xbb, 0xcc};
msg2->checksum = 0xcc;
EXPECT_TRUE(packetizer->input(recvBytes));
auto packets = packetizer->output();
if(packets.size() != 2) { EXPECT_TRUE(false); }
//LIN2 frame from device
EXPECT_TRUE(packetDecoder->decode(decodeMsg, packets.back()));
auto testMessage = std::dynamic_pointer_cast<icsneo::LINMessage>(decodeMsg);
EXPECT_EQ(msg1->network, testMessage->network);
EXPECT_EQ(msg1->ID, testMessage->ID);
EXPECT_EQ(msg1->type, testMessage->type);
EXPECT_EQ(msg1->isEnhancedChecksum, testMessage->isEnhancedChecksum);
EXPECT_EQ(msg1->data, testMessage->data);
EXPECT_EQ(msg1->checksum, testMessage->checksum);
packets.pop_back();
//LIN1 frame from device
EXPECT_TRUE(packetDecoder->decode(decodeMsg, packets.back()));
auto testMessage2 = std::dynamic_pointer_cast<icsneo::LINMessage>(decodeMsg);
EXPECT_EQ(msg2->network, testMessage2->network);
EXPECT_EQ(msg2->ID, testMessage2->ID);
EXPECT_EQ(msg2->type, testMessage2->type);
EXPECT_EQ(msg2->isEnhancedChecksum, testMessage2->isEnhancedChecksum);
EXPECT_EQ(msg2->data, testMessage2->data);
EXPECT_EQ(msg2->checksum, testMessage2->checksum);
}