diff --git a/api/icsneoc2/icsneoc2messages.cpp b/api/icsneoc2/icsneoc2messages.cpp index e438eb2..f8f60fc 100644 --- a/api/icsneoc2/icsneoc2messages.cpp +++ b/api/icsneoc2/icsneoc2messages.cpp @@ -471,6 +471,7 @@ icsneoc2_error_t icsneoc2_message_lin_status_flags_get(const icsneoc2_message_t* 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; + if(lin_msg->statusFlags.WakeupRequest) *status_flags |= ICSNEOC2_LIN_STATUS_WAKEUP_REQUEST; return icsneoc2_error_success; } diff --git a/bindings/python/icsneopy/communication/message/linmessage.cpp b/bindings/python/icsneopy/communication/message/linmessage.cpp index ec95e80..55036e8 100644 --- a/bindings/python/icsneopy/communication/message/linmessage.cpp +++ b/bindings/python/icsneopy/communication/message/linmessage.cpp @@ -28,7 +28,8 @@ void init_linmessage(pybind11::module_& m) { .def_readwrite("UpdateResponderOnce", &LINStatusFlags::UpdateResponderOnce) .def_readwrite("HasUpdatedResponderOnce", &LINStatusFlags::HasUpdatedResponderOnce) .def_readwrite("BusRecovered", &LINStatusFlags::BusRecovered) - .def_readwrite("BreakOnly", &LINStatusFlags::BreakOnly); + .def_readwrite("BreakOnly", &LINStatusFlags::BreakOnly) + .def_readwrite("WakeupRequest", &LINStatusFlags::WakeupRequest); pybind11::classh linMessage(m, "LINMessage"); @@ -39,7 +40,8 @@ void init_linmessage(pybind11::module_& m) { .value("LIN_BREAK_ONLY", LINMessage::Type::LIN_BREAK_ONLY) .value("LIN_SYNC_ONLY", LINMessage::Type::LIN_SYNC_ONLY) .value("LIN_UPDATE_RESPONDER", LINMessage::Type::LIN_UPDATE_RESPONDER) - .value("LIN_ERROR", LINMessage::Type::LIN_ERROR); + .value("LIN_ERROR", LINMessage::Type::LIN_ERROR) + .value("LIN_WAKEUP_REQUEST", LINMessage::Type::LIN_WAKEUP_REQUEST); linMessage .def(pybind11::init<>()) diff --git a/communication/message/transmitmessage.cpp b/communication/message/transmitmessage.cpp index d37ada7..3c7104c 100644 --- a/communication/message/transmitmessage.cpp +++ b/communication/message/transmitmessage.cpp @@ -137,6 +137,7 @@ static std::vector EncodeFromMessageLIN(std::shared_ptr frame, c linpacket->CoreMiniBitsLIN.TXResponder = 1; break; case LINMessage::Type::LIN_BREAK_ONLY: + linpacket->CoreMiniBitsLIN.TXCommander = 1; linpacket->CoreMiniBitsLIN.BreakOnly = 1; break; default: diff --git a/communication/packet/linpacket.cpp b/communication/packet/linpacket.cpp index 7050254..e6b1ac6 100644 --- a/communication/packet/linpacket.cpp +++ b/communication/packet/linpacket.cpp @@ -72,18 +72,30 @@ std::shared_ptr HardwareLINPacket::DecodeToMessage(const std::vector(packet->CoreMiniBitsLIN.UpdateResponderOnce), static_cast(packet->CoreMiniBitsLIN.HasUpdatedResponderOnce), static_cast(packet->CoreMiniBitsLIN.BusRecovered), - static_cast(packet->CoreMiniBitsLIN.BreakOnly) + static_cast(packet->CoreMiniBitsLIN.BreakOnly), + static_cast(packet->CoreMiniBitsLIN.WakeupRequest) }; - if(msg->statusFlags.TxCommander || msg->statusFlags.TxResponder) + // Wake type only for a wake-only pulse. A UART/LIN break or a complete + // frame may also carry WakeupRequest; those keep their existing type. + const bool wakeupPulse = msg->statusFlags.WakeupRequest && + packet->CoreMiniBitsLIN.len == 0 && + packet->CoreMiniBitsLIN.ID == 0 && + !msg->statusFlags.BreakOnly && + !msg->errFlags.ErrRxBreakOnly && + !msg->errFlags.ErrRxBreakSyncOnly; + if(wakeupPulse) + msg->linMsgType = LINMessage::Type::LIN_WAKEUP_REQUEST; + else if(msg->statusFlags.TxCommander || msg->statusFlags.TxResponder) msg->linMsgType = LINMessage::Type::LIN_COMMANDER_MSG; else if(msg->statusFlags.BreakOnly) msg->linMsgType = LINMessage::Type::LIN_BREAK_ONLY; - if( msg->errFlags.ErrRxBreakOnly || msg->errFlags.ErrRxBreakSyncOnly || + if( !wakeupPulse && + (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->errFlags.ErrFrameResponderData) ) { msg->linMsgType = LINMessage::Type::LIN_ERROR; } msg->timestamp = packet->timestamp; @@ -103,7 +115,7 @@ bool HardwareLINPacket::EncodeFromMessage(LINMessage& message, std::vector(); + if(cfg == nullptr) + return nullptr; + switch(net.getNetID()) { + case Network::NetID::LIN_01: + return &(cfg->lin1); + default: + return nullptr; + } + } + std::optional isT1SPLCAEnabledFor(Network net) const override { const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net); if(t1s == nullptr) diff --git a/include/icsneo/device/tree/radcomet3/radcomet3settings.h b/include/icsneo/device/tree/radcomet3/radcomet3settings.h index 3081cdd..1839a08 100644 --- a/include/icsneo/device/tree/radcomet3/radcomet3settings.h +++ b/include/icsneo/device/tree/radcomet3/radcomet3settings.h @@ -115,6 +115,18 @@ public: } } + const LIN_SETTINGS* getLINSettingsFor(Network net) const override { + auto cfg = getStructurePointer(); + if(cfg == nullptr) + return nullptr; + switch(net.getNetID()) { + case Network::NetID::LIN_01: + return &(cfg->lin1); + default: + return nullptr; + } + } + const ETHERNET_SETTINGS2* getEthernetSettingsFor(Network net) const override { auto cfg = getStructurePointer(); if(cfg == nullptr) diff --git a/include/icsneo/icsneoc2messages.h b/include/icsneo/icsneoc2messages.h index a460ea2..b122a10 100644 --- a/include/icsneo/icsneoc2messages.h +++ b/include/icsneo/icsneoc2messages.h @@ -381,6 +381,7 @@ typedef enum _icsneoc2_lin_msg_type_t { icsneoc2_lin_msg_type_sync_only = 4, icsneoc2_lin_msg_type_update_responder = 5, icsneoc2_lin_msg_type_error = 6, + icsneoc2_lin_msg_type_wakeup_request = 7, } _icsneoc2_lin_msg_type_t; typedef uint8_t icsneoc2_lin_msg_type_t; @@ -409,6 +410,7 @@ typedef uint32_t icsneoc2_lin_err_flags_t; #define ICSNEOC2_LIN_STATUS_HAS_UPDATED_RESPONDER_ONCE 0x20 #define ICSNEOC2_LIN_STATUS_BUS_RECOVERED 0x40 #define ICSNEOC2_LIN_STATUS_BREAK_ONLY 0x80 +#define ICSNEOC2_LIN_STATUS_WAKEUP_REQUEST 0x100 typedef uint32_t icsneoc2_lin_status_flags_t; diff --git a/test/unit/icsneoc2.cpp b/test/unit/icsneoc2.cpp index 95b7bb3..89de7eb 100644 --- a/test/unit/icsneoc2.cpp +++ b/test/unit/icsneoc2.cpp @@ -1862,6 +1862,7 @@ TEST(icsneoc2, test_lin_msg_type_enum_values) 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(icsneoc2_lin_msg_type_wakeup_request, 7); ASSERT_EQ(sizeof(icsneoc2_lin_msg_type_t), sizeof(uint8_t)); } @@ -1875,6 +1876,7 @@ TEST(icsneoc2, test_lin_msg_type_cpp_enum_sync) ASSERT_EQ(static_cast(T::LIN_SYNC_ONLY), icsneoc2_lin_msg_type_sync_only); ASSERT_EQ(static_cast(T::LIN_UPDATE_RESPONDER), icsneoc2_lin_msg_type_update_responder); ASSERT_EQ(static_cast(T::LIN_ERROR), icsneoc2_lin_msg_type_error); + ASSERT_EQ(static_cast(T::LIN_WAKEUP_REQUEST), icsneoc2_lin_msg_type_wakeup_request); } TEST(icsneoc2, test_lin_flag_bitmask_values) @@ -1901,6 +1903,7 @@ TEST(icsneoc2, test_lin_flag_bitmask_values) 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); + ASSERT_EQ(ICSNEOC2_LIN_STATUS_WAKEUP_REQUEST, 0x100); } TEST(icsneoc2, test_icsneoc2_eth_create) diff --git a/test/unit/linencoderdecodertest.cpp b/test/unit/linencoderdecodertest.cpp index 9a8d365..ca9d7bf 100644 --- a/test/unit/linencoderdecodertest.cpp +++ b/test/unit/linencoderdecodertest.cpp @@ -2,12 +2,14 @@ #include "icsneo/communication/encoder.h" #include "icsneo/communication/packet/linpacket.h" #include "icsneo/communication/message/linmessage.h" +#include "icsneo/communication/message/transmitmessage.h" #include "icsneo/communication/packetizer.h" #include "icsneo/core/ringbuffer.h" #include "icsneo/api/eventmanager.h" #include "gtest/gtest.h" #include #include +#include using namespace icsneo; @@ -55,6 +57,16 @@ protected: 0x00, 0x00, 0xE2, 0x41}; + // Break-only LIN 1: commander (0x80) | break-only (0x20) | len 3 => 0xa3 + // ID 0x00 pID 0x80 + std::vector testControllerBreakOnly = + {0xaa, 0x0c, + 0x0d, 0x00, + 0x10, 0x00, + 0x00, 0xa3, + 0x00, 0x00, + 0x80, 0x41}; + std::vector recvBytes = {0xaa, 0x0c, 0x22, 0x00, 0x10, 0x00, 0x88, 0x03, @@ -139,6 +151,29 @@ TEST_F(LINEncoderDecoderTest, PacketEncoderControllerHeaderTest) { EXPECT_EQ(bytestream, testControllerHeaderOnly); } +TEST_F(LINEncoderDecoderTest, PacketEncoderControllerBreakOnlyTest) { + std::vector bytestream; + auto message = std::make_shared(static_cast(0x00u)); + message->network = icsneo::Network::NetID::LIN_01; + message->linMsgType = icsneo::LINMessage::Type::LIN_BREAK_ONLY; + message->isEnhancedChecksum = false; + packetEncoder->encode(*packetizer, bytestream, message); + EXPECT_EQ(bytestream, testControllerBreakOnly); +} + +TEST_F(LINEncoderDecoderTest, TransmitMessageBreakOnlySetsCommander) { + auto message = std::make_shared(static_cast(0x00u)); + message->network = icsneo::Network::NetID::LIN_01; + message->linMsgType = icsneo::LINMessage::Type::LIN_BREAK_ONLY; + auto bytes = TransmitMessage::EncodeFromMessage(message, 1, report); + ASSERT_FALSE(bytes.empty()); + ASSERT_GE(bytes.size(), sizeof(TransmitMessage)); + auto* tx = reinterpret_cast(bytes.data()); + auto* lin = reinterpret_cast(tx->commonHeader); + EXPECT_EQ(lin->CoreMiniBitsLIN.TXCommander, 1); + EXPECT_EQ(lin->CoreMiniBitsLIN.BreakOnly, 1); +} + TEST_F(LINEncoderDecoderTest, PacketEncoderControllerWithDataTest) { std::vector bytestream; auto message = std::make_shared(static_cast(0x11u)); @@ -192,4 +227,46 @@ TEST_F(LINEncoderDecoderTest, PacketDecoderTest) { EXPECT_EQ(msg2->isEnhancedChecksum, testMessage2->isEnhancedChecksum); EXPECT_EQ(msg2->data, testMessage2->data); EXPECT_EQ(msg2->checksum, testMessage2->checksum); -} \ No newline at end of file +} + +static std::shared_ptr decodeLinPacket(const HardwareLINPacket& pkt) { + std::vector bytes(sizeof(pkt)); + std::memcpy(bytes.data(), &pkt, sizeof(pkt)); + return std::dynamic_pointer_cast(HardwareLINPacket::DecodeToMessage(bytes)); +} + +TEST_F(LINEncoderDecoderTest, WakeupPulseDecodeTest) { + HardwareLINPacket pkt{}; + pkt.CoreMiniBitsLIN.WakeupRequest = 1; + auto msg = decodeLinPacket(pkt); + ASSERT_NE(msg, nullptr); + EXPECT_EQ(msg->linMsgType, LINMessage::Type::LIN_WAKEUP_REQUEST); + EXPECT_TRUE(msg->statusFlags.WakeupRequest); +} + +TEST_F(LINEncoderDecoderTest, WakeupFlagOnUartBreakStaysError) { + HardwareLINPacket pkt{}; + pkt.CoreMiniBitsLIN.WakeupRequest = 1; + pkt.CoreMiniBitsLIN.ErrRxOnlyBreak = 1; + auto msg = decodeLinPacket(pkt); + ASSERT_NE(msg, nullptr); + EXPECT_EQ(msg->linMsgType, LINMessage::Type::LIN_ERROR); + EXPECT_TRUE(msg->statusFlags.WakeupRequest); + EXPECT_TRUE(msg->errFlags.ErrRxBreakOnly); +} + +TEST_F(LINEncoderDecoderTest, WakeupFlagOnCompleteFrameKeepsCommander) { + HardwareLINPacket pkt{}; + pkt.CoreMiniBitsLIN.WakeupRequest = 1; + pkt.CoreMiniBitsLIN.TXCommander = 1; + pkt.CoreMiniBitsLIN.ID = 0x10; + pkt.CoreMiniBitsLIN.len = 3; // two responder bytes plus the checksum + pkt.data[0] = 0xaa; + pkt.data[1] = 0xbb; + pkt.data[2] = 0x99; // classic checksum, otherwise the frame decodes as LIN_ERROR + auto msg = decodeLinPacket(pkt); + ASSERT_NE(msg, nullptr); + EXPECT_EQ(msg->linMsgType, LINMessage::Type::LIN_COMMANDER_MSG); + EXPECT_TRUE(msg->statusFlags.WakeupRequest); + EXPECT_EQ(msg->ID, 0x10); +}