diff --git a/communication/encoder.cpp b/communication/encoder.cpp index 9efef05..0e1449d 100644 --- a/communication/encoder.cpp +++ b/communication/encoder.cpp @@ -35,15 +35,25 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector& result, auto ethmsg = std::dynamic_pointer_cast(message); if(!ethmsg) { report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error); - return false; // The message was not a properly formed EthernetMessage + return false; } + shortFormat = false; // Ensure long-format RED header is added + netid = static_cast(Network::NetID::ETHERNET_TX_WRAP); buffer = &result; - if(!HardwareEthernetPacket::EncodeFromMessage(*ethmsg, result, report)) + + if(!HardwareEthernetPacket::EncodeFromMessage(*ethmsg, result, report)) { + report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error); return false; + } + + if(result.empty()) { + report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error); + return false; + } break; - } // End of Network::Type::Ethernet + } case Network::Type::CAN: case Network::Type::SWCAN: case Network::Type::LSFTCAN: { @@ -230,7 +240,6 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector& result, (uint8_t)(netid >> 8) }); } - result = packetizer.packetWrap(*buffer, shortFormat); return true; } diff --git a/communication/packet/ethernetpacket.cpp b/communication/packet/ethernetpacket.cpp index e4209c1..7c438ff 100644 --- a/communication/packet/ethernetpacket.cpp +++ b/communication/packet/ethernetpacket.cpp @@ -1,5 +1,5 @@ #include "icsneo/communication/packet/ethernetpacket.h" -#include // memcpy +#include // for std::copy #include using namespace icsneo; @@ -7,40 +7,31 @@ using namespace icsneo; std::shared_ptr HardwareEthernetPacket::DecodeToMessage(const std::vector& bytestream, const device_eventhandler_t& report) { const HardwareEthernetPacket* packet = (const HardwareEthernetPacket*)((const void*)bytestream.data()); const uint16_t* rawWords = (const uint16_t*)bytestream.data(); - // Make sure we have enough to read the packet length first if(bytestream.size() < sizeof(HardwareEthernetPacket)) return nullptr; - // packet->Length will also encompass the two uint16_t's at the end of the struct, make sure that at least they are here if(packet->Length < 4) return nullptr; - const size_t fcsSize = packet->header.FCS_AVAIL ? 4 : 0; const size_t bytestreamExpectedSize = sizeof(HardwareEthernetPacket) + packet->Length; const size_t bytestreamActualSize = bytestream.size(); if(bytestreamActualSize < bytestreamExpectedSize) return nullptr; - // Check for oversized packets, noting that some devices will send an extra byte to have an even number of bytes if(bytestreamActualSize > bytestreamExpectedSize + 1) report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning); - auto messagePtr = std::make_shared(); EthernetMessage& message = *messagePtr; - message.transmitted = packet->eid.TXMSG; if(message.transmitted) message.description = packet->stats; - message.preemptionEnabled = packet->header.PREEMPTION_ENABLED; if(message.preemptionEnabled) message.preemptionFlags = (uint8_t)((rawWords[0] & 0x03F8) >> 4); - message.frameTooShort = packet->header.RUNT_FRAME; if(message.frameTooShort) message.error = true; - // This timestamp is raw off the device (in timestampResolution increments) // Decoder will fix as it has information about the timestampResolution increments message.timestamp = packet->timestamp.TS; @@ -48,7 +39,7 @@ std::shared_ptr HardwareEthernetPacket::DecodeToMessage(const s const std::vector::const_iterator databegin = bytestream.begin() + sizeof(HardwareEthernetPacket); const std::vector::const_iterator dataend = databegin + packet->Length - fcsSize; message.data.insert(message.data.begin(), databegin, dataend); - + if(fcsSize) { uint32_t& fcs = message.fcs.emplace(); std::copy(dataend, dataend + fcsSize, (uint8_t*)&fcs); @@ -59,41 +50,83 @@ std::shared_ptr HardwareEthernetPacket::DecodeToMessage(const s bool HardwareEthernetPacket::EncodeFromMessage(const EthernetMessage& message, std::vector& bytestream, const device_eventhandler_t&) { const size_t unpaddedSize = message.data.size(); - size_t paddedSize = unpaddedSize; - uint16_t description = message.description; - - if(!message.noPadding && unpaddedSize < 60) - paddedSize = 60; // Pad out short messages - - size_t sizeWithHeader = paddedSize + 4; // DescriptionID and Padded Count + if(unpaddedSize == 0) + return false; // Description ID Most Significant bit is used to identify preemption frames + uint16_t description = message.description; if(description & 0x8000) return false; - if(message.preemptionEnabled) { - sizeWithHeader++; // Make space for the preemption flags + const bool preempt = message.preemptionEnabled; + // full header including parent + const size_t headerByteCount = preempt ? 15 : 14; + // local header for netID, description, and flags + const size_t localHeader = preempt ? 10 : 9; + // allocate space for fcs override + const size_t fcsSize = message.fcs ? 4 : 0; + + if(preempt) description |= 0x8000; + + size_t paddedSize = unpaddedSize; + if(!message.noPadding && unpaddedSize < 60) + paddedSize = 60; + + // size of full payload including optional fcs + size_t payloadSize = paddedSize + fcsSize; + + + // totalBufferSize is local header + ethernet payload and option fcs + const size_t totalBufferSize = localHeader + paddedSize + fcsSize; + + bytestream.clear(); + bytestream.reserve(totalBufferSize); + + // Header size field (little endian) + bytestream.push_back(static_cast(payloadSize & 0xFF)); + bytestream.push_back(static_cast((payloadSize >> 8) & 0xFF)); + + // Description (big endian) + bytestream.push_back(static_cast(description >> 8)); + bytestream.push_back(static_cast(description)); + + bytestream.push_back(0x00); + bytestream.push_back(static_cast(headerByteCount)); + + // Network ID (little endian) + uint16_t realID = static_cast(message.network.getNetID()); + bytestream.push_back(static_cast(realID & 0xFF)); + bytestream.push_back(static_cast((realID >> 8) & 0xFF)); + + // Flags + constexpr uint8_t FLAG_PADDING = 0x01; + constexpr uint8_t FLAG_FCS = 0x04; + constexpr uint8_t FLAG_PREEMPTION = 0x08; + + uint8_t flags = 0x00; + if(!message.noPadding) flags |= FLAG_PADDING; + if(message.fcs) flags |= FLAG_FCS; + if(message.preemptionEnabled) flags |= FLAG_PREEMPTION; + + bytestream.push_back(flags); + + if(preempt) + bytestream.push_back(static_cast(message.preemptionFlags)); + + bytestream.insert(bytestream.end(), message.data.begin(), message.data.end()); + + // Only zero-fill when we want padding + if(!message.noPadding && unpaddedSize < 60) { + size_t paddingNeeded = 60 - unpaddedSize; + bytestream.insert(bytestream.end(), paddingNeeded, 0); // Zero-fill for padding } - - bytestream.reserve(sizeWithHeader + 8); // Also reserve space for the bytes we'll use later on - bytestream.resize(sizeWithHeader); - size_t index = 0; - // Padded size, little endian - bytestream[index++] = uint8_t(paddedSize); - bytestream[index++] = uint8_t(paddedSize >> 8); - - // Description ID, big endian - bytestream[index++] = uint8_t(description >> 8); - bytestream[index++] = uint8_t(description); - - // The header is one byte larger if preemption is enabled, shifting the data - if(message.preemptionEnabled) - bytestream[index++] = message.preemptionFlags; - - // We only copy in the unpadded size, the rest will be 0 - memcpy(bytestream.data() + index, message.data.data(), unpaddedSize); + if(message.fcs) { + uint32_t fcs = message.fcs.value(); + const uint8_t* fcsBytes = reinterpret_cast(&fcs); + bytestream.insert(bytestream.end(), fcsBytes, fcsBytes + sizeof(fcs)); + } return true; -} \ No newline at end of file +} diff --git a/include/icsneo/communication/network.h b/include/icsneo/communication/network.h index a9cf93a..df77009 100644 --- a/include/icsneo/communication/network.h +++ b/include/icsneo/communication/network.h @@ -143,6 +143,7 @@ public: I2C_03 = 518, // previously I2C3 I2C_04 = 519, // previously I2C4 ETHERNET_02 = 520, // previously Ethernet2 + ETHERNET_TX_WRAP = 521, A2B_01 = 522, // previously A2B1 A2B_02 = 523, // previously A2B2 ETHERNET_03 = 524, // previously Ethernet3 diff --git a/include/icsneo/communication/packet/ethernetpacket.h b/include/icsneo/communication/packet/ethernetpacket.h index dbe338d..0f4e000 100644 --- a/include/icsneo/communication/packet/ethernetpacket.h +++ b/include/icsneo/communication/packet/ethernetpacket.h @@ -7,6 +7,9 @@ #include "icsneo/api/eventmanager.h" #include #include +#include +#include +#include namespace icsneo { @@ -18,30 +21,59 @@ struct HardwareEthernetPacket { static std::shared_ptr DecodeToMessage(const std::vector& bytestream, const device_eventhandler_t& report); static bool EncodeFromMessage(const EthernetMessage& message, std::vector& bytestream, const device_eventhandler_t& report); + // Word 0 - Header flags (offset 0) struct { icscm_bitfield FCS_AVAIL : 1; icscm_bitfield RUNT_FRAME : 1; - icscm_bitfield DISABLE_PADDING : 1; + icscm_bitfield ENABLE_PADDING : 1; icscm_bitfield PREEMPTION_ENABLED : 1; icscm_bitfield MPACKET_TYPE : 4; icscm_bitfield MPACKET_FRAG_CNT : 2; - icscm_bitfield : 6; + icscm_bitfield UPDATE_CHECKSUMS : 1; + icscm_bitfield FCS_OVERRIDE : 1; + icscm_bitfield CRC_ERROR : 1; + icscm_bitfield FCS_VERIFIED : 1; + icscm_bitfield DISABLE_TX_RECEIPT : 1; + icscm_bitfield T1S_ETHERNET : 1; } header; + + // Word 1 - Extended ID flags struct { icscm_bitfield txlen : 12; icscm_bitfield TXMSG : 1; - icscm_bitfield : 3; + icscm_bitfield TXAborted : 1; + icscm_bitfield T1S_SYMBOL : 1; + icscm_bitfield T1S_BURST : 1; } eid; - icscm_bitfield reserved; - unsigned char data[8]; + + // Word 2 - T1S Status flags + struct { + icscm_bitfield TXCollision : 1; + icscm_bitfield T1SWake : 1; + icscm_bitfield : 14; + } t1s_status; + + // Word 3 - T1S Node Information + struct { + icscm_bitfield T1S_BURST_COUNT : 8; + icscm_bitfield T1S_NODE_ID : 8; + uint8_t RESERVED[6]; + } t1s_node; + + // Words 4-7 - Reserved/Padding uint16_t stats; + + // Words 8-11 - Timestamp (offset 14-22) struct { uint64_t TS : 60; - uint64_t : 3; // Reserved for future status bits + uint64_t : 3; uint64_t IsExtended : 1; } timestamp; + + uint16_t NetworkID; uint16_t Length; + }; #pragma pack(pop) @@ -50,4 +82,4 @@ struct HardwareEthernetPacket { #endif // __cplusplus -#endif \ No newline at end of file +#endif // __ETHERNETPACKET_H__