From d37d5bb23e847438052bd9163069986fd55c0d7d Mon Sep 17 00:00:00 2001 From: Paul Hollinsky Date: Fri, 21 Dec 2018 20:32:27 -0500 Subject: [PATCH] Support Ethernet and Broad-R Reach TX and RX --- CMakeLists.txt | 2 + communication/decoder.cpp | 90 ++-------- communication/encoder.cpp | 100 ++--------- communication/message/neomessage.cpp | 14 ++ communication/packet/canpacket.cpp | 164 ++++++++++++++++++ communication/packet/ethernetpacket.cpp | 86 +++++++++ include/icsneo/communication/decoder.h | 46 +---- .../communication/message/ethernetmessage.h | 48 +++++ .../icsneo/communication/message/message.h | 5 +- .../icsneo/communication/message/neomessage.h | 21 ++- include/icsneo/communication/network.h | 24 +-- .../icsneo/communication/packet/canpacket.h | 52 ++++++ .../communication/packet/ethernetpacket.h | 44 +++++ include/icsneo/icsneocpp.h | 3 + 14 files changed, 490 insertions(+), 209 deletions(-) create mode 100644 communication/packet/canpacket.cpp create mode 100644 communication/packet/ethernetpacket.cpp create mode 100644 include/icsneo/communication/message/ethernetmessage.h create mode 100644 include/icsneo/communication/packet/canpacket.h create mode 100644 include/icsneo/communication/packet/ethernetpacket.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 25c4f62..74b17a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,8 @@ endif() set(COMMON_SRC communication/message/neomessage.cpp + communication/packet/canpacket.cpp + communication/packet/ethernetpacket.cpp communication/decoder.cpp communication/encoder.cpp communication/packetizer.cpp diff --git a/communication/decoder.cpp b/communication/decoder.cpp index 0a3fe36..d213a4a 100644 --- a/communication/decoder.cpp +++ b/communication/decoder.cpp @@ -5,6 +5,8 @@ #include "icsneo/communication/message/readsettingsmessage.h" #include "icsneo/communication/command.h" #include "icsneo/device/device.h" +#include "icsneo/communication/packet/canpacket.h" +#include "icsneo/communication/packet/ethernetpacket.h" #include using namespace icsneo; @@ -18,82 +20,28 @@ uint64_t Decoder::GetUInt64FromLEBytes(uint8_t* bytes) { bool Decoder::decode(std::shared_ptr& result, const std::shared_ptr& packet) { switch(packet->network.getType()) { + case Network::Type::Ethernet: + result = HardwareEthernetPacket::DecodeToMessage(packet->data); + if(!result) + return false; // A nullptr was returned, the packet was not long enough to decode + + // Timestamps are in (multiplier) ns increments since 1/1/2007 GMT 00:00:00.0000 + // The resolution (multiplier) depends on the device + result->timestamp *= timestampMultiplier; + result->network = packet->network; + return true; case Network::Type::CAN: { if(packet->data.size() < 24) return false; - HardwareCANPacket* data = (HardwareCANPacket*)packet->data.data(); - auto msg = std::make_shared(); - msg->network = packet->network; + result = HardwareCANPacket::DecodeToMessage(packet->data); + if(!result) + return false; // A nullptr was returned, the packet was malformed - // Timestamp calculation - msg->timestamp = data->timestamp.TS * 25; // Timestamps are in 25ns increments since 1/1/2007 GMT 00:00:00.0000 - - // Arb ID - if(data->header.IDE) { // Extended 29-bit ID - msg->arbid = (data->header.SID & 0x7ff) << 18; - msg->arbid |= (data->eid.EID & 0xfff) << 6; - msg->arbid |= (data->dlc.EID2 & 0x3f); - msg->isExtended = true; - } else { // Standard 11-bit ID - msg->arbid = data->header.SID; - } - - // DLC - uint8_t length = data->dlc.DLC; - msg->dlcOnWire = length; // This will hold the real DLC on wire 0x0 - 0xF - if(data->header.EDL && data->timestamp.IsExtended) { // CAN FD - msg->isCANFD = true; - msg->baudrateSwitch = data->header.BRS; // CAN FD Baudrate Switch - if(length > 8) { - switch(length) { // CAN FD Length Decoding - case 0x9: - length = 12; - break; - case 0xa: - length = 16; - break; - case 0xb: - length = 20; - break; - case 0xc: - length = 24; - break; - case 0xd: - length = 32; - break; - case 0xe: - length = 48; - break; - case 0xf: - length = 64; - break; - default: - return false; - } - } - } else if(length > 8) { // This is a standard CAN frame with a length of more than 8 - // Yes, this is possible. On the wire, the length field is a nibble, and we do want to return an accurate value - // We don't want to overread our buffer, though, so make sure we cap the length - length = 8; - } - - // Data - // The first 8 bytes are always in the standard place - if((data->dlc.RTR && data->header.IDE) || (!data->header.IDE && data->header.SRR)) { // Remote Request Frame - msg->data.resize(length); // This data will be all zeros, but the length will be set - msg->isRemote = true; - } else { - msg->data.reserve(length); - msg->data.insert(msg->data.end(), data->data, data->data + (length > 8 ? 8 : length)); - if(length > 8) { // If there are more than 8 bytes, they come at the end of the message - // Messages with extra data are formatted as message, then uint16_t netid, then uint16_t length, then extra data - uint8_t* extraDataStart = packet->data.data() + sizeof(HardwareCANPacket) + 2 + 2; - msg->data.insert(msg->data.end(), extraDataStart, extraDataStart + (length - 8)); - } - } - - result = msg; + // Timestamps are in (multiplier) ns increments since 1/1/2007 GMT 00:00:00.0000 + // The resolution (multiplier) depends on the device + result->timestamp *= timestampMultiplier; + result->network = packet->network; return true; } case Network::Type::Internal: { diff --git a/communication/encoder.cpp b/communication/encoder.cpp index 9fdd110..c5b9794 100644 --- a/communication/encoder.cpp +++ b/communication/encoder.cpp @@ -1,4 +1,7 @@ #include "icsneo/communication/encoder.h" +#include "icsneo/communication/message/ethernetmessage.h" +#include "icsneo/communication/packet/ethernetpacket.h" +#include "icsneo/communication/packet/canpacket.h" using namespace icsneo; @@ -8,9 +11,18 @@ bool Encoder::encode(std::vector& result, const std::shared_ptrnetwork.getType()) { - case Network::Type::CAN: { - useResultAsBuffer = true; + case Network::Type::Ethernet: { + auto ethmsg = std::dynamic_pointer_cast(message); + if(!ethmsg) + return false; // The message was not a properly formed EthernetMessage + useResultAsBuffer = true; + if(!HardwareEthernetPacket::EncodeFromMessage(*ethmsg, result)) + return false; + + break; + } // End of Network::Type::Ethernet + case Network::Type::CAN: { auto canmsg = std::dynamic_pointer_cast(message); if(!canmsg) return false; // The message was not a properly formed CANMessage @@ -18,88 +30,10 @@ bool Encoder::encode(std::vector& result, const std::shared_ptrisCANFD) return false; // This device does not support CAN FD - if(canmsg->isCANFD && canmsg->isRemote) - return false; // RTR frames can not be used with CAN FD + useResultAsBuffer = true; + if(!HardwareCANPacket::EncodeFromMessage(*canmsg, result)) + return false; // The CANMessage was malformed - const size_t dataSize = canmsg->data.size(); - if(dataSize > 64 || (dataSize > 8 && !canmsg->isCANFD)) - return false; // Too much data for the protocol - - uint8_t lengthNibble = uint8_t(canmsg->data.size()); - if(lengthNibble > 8) { - switch(lengthNibble) { - case 12: - lengthNibble = 0x9; - break; - case 16: - lengthNibble = 0xA; - break; - case 20: - lengthNibble = 0xB; - break; - case 24: - lengthNibble = 0xC; - break; - case 32: - lengthNibble = 0xD; - break; - case 48: - lengthNibble = 0xE; - break; - case 64: - lengthNibble = 0xF; - break; - default: - return false; // CAN FD frame may have had an incorrect byte count - } - } - - // Pre-allocate as much memory as we will possibly need for speed - result.reserve(17 + dataSize); - - result.push_back(0 /* byte count here later */ << 4 | (uint8_t(canmsg->network.getNetID()) & 0xF)); - result.insert(result.end(), {0,0}); // Two bytes for Description ID, big endian, not used in API currently - - // Next 2-4 bytes are ArbID - if(canmsg->isExtended) { - if(canmsg->arbid >= 0x20000000) // Extended messages use 29-bit arb IDs - return false; - - result.insert(result.end(), { - (uint8_t)(canmsg->arbid >> 21), - (uint8_t)(((((canmsg->arbid & 0x001C0000) >> 13) & 0xFF) + (((canmsg->arbid & 0x00030000) >> 16) & 0xFF)) | 8), - (uint8_t)(canmsg->arbid >> 8), - (uint8_t)canmsg->arbid - }); - } else { - if(canmsg->arbid >= 0x800) // Standard messages use 11-bit arb IDs - return false; - - result.insert(result.end(), { - (uint8_t)(canmsg->arbid >> 3), - (uint8_t)((canmsg->arbid & 0x7) << 5) - }); - } - - // Status and DLC bits - if(canmsg->isCANFD) { - result.push_back(0x0F); // FD Frame - uint8_t fdStatusByte = lengthNibble; - if(canmsg->baudrateSwitch) - fdStatusByte |= 0x80; // BRS status bit - result.push_back(fdStatusByte); - } else { - // TODO Support high voltage wakeup, bitwise-or in 0x8 here to enable - uint8_t statusNibble = canmsg->isRemote ? 0x4 : 0x0; - result.push_back((statusNibble << 4) | lengthNibble); - } - - // Now finally the payload - result.insert(result.end(), canmsg->data.begin(), canmsg->data.end()); - result.push_back(0); - - // Fill in the length byte from earlier - result[0] |= result.size() << 4; break; } // End of Network::Type::CAN default: diff --git a/communication/message/neomessage.cpp b/communication/message/neomessage.cpp index aef02ff..f6dd2df 100644 --- a/communication/message/neomessage.cpp +++ b/communication/message/neomessage.cpp @@ -1,5 +1,6 @@ #include "icsneo/communication/message/neomessage.h" #include "icsneo/communication/message/canmessage.h" +#include "icsneo/communication/message/ethernetmessage.h" using namespace icsneo; @@ -13,6 +14,8 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr message) { neomsg.length = message->data.size(); neomsg.data = message->data.data(); neomsg.timestamp = message->timestamp; + neomsg.status.globalError = message->error; + neomsg.status.transmitMessage = message->transmitted; switch(type) { case Network::Type::CAN: { @@ -27,6 +30,17 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr message) { can.status.canfdBRS = canmsg->baudrateSwitch; break; } + case Network::Type::Ethernet: { + neomessage_eth_t& eth = *(neomessage_eth_t*)&neomsg; + auto ethmsg = std::static_pointer_cast(message); + eth.preemptionFlags = ethmsg->preemptionFlags; + eth.status.incompleteFrame = ethmsg->frameTooShort; + // TODO Fill in extra status bits + //eth.status.xyz = ethmsg->preemptionEnabled; + //eth.status.xyz = ethmsg->fcsAvailable; + //eth.status.xyz = ethmsg->noPadding; + break; + } default: // TODO Implement others break; diff --git a/communication/packet/canpacket.cpp b/communication/packet/canpacket.cpp new file mode 100644 index 0000000..417c50b --- /dev/null +++ b/communication/packet/canpacket.cpp @@ -0,0 +1,164 @@ +#include "icsneo/communication/packet/canpacket.h" + +using namespace icsneo; + +std::shared_ptr HardwareCANPacket::DecodeToMessage(const std::vector& bytestream) { + const HardwareCANPacket* data = (const HardwareCANPacket*)bytestream.data(); + + auto msg = std::make_shared(); + + // Arb ID + if(data->header.IDE) { // Extended 29-bit ID + msg->arbid = (data->header.SID & 0x7ff) << 18; + msg->arbid |= (data->eid.EID & 0xfff) << 6; + msg->arbid |= (data->dlc.EID2 & 0x3f); + msg->isExtended = true; + } else { // Standard 11-bit ID + msg->arbid = data->header.SID; + } + + // DLC + uint8_t length = data->dlc.DLC; + msg->dlcOnWire = length; // This will hold the real DLC on wire 0x0 - 0xF + if(data->header.EDL && data->timestamp.IsExtended) { // CAN FD + msg->isCANFD = true; + msg->baudrateSwitch = data->header.BRS; // CAN FD Baudrate Switch + if(length > 8) { + switch(length) { // CAN FD Length Decoding + case 0x9: + length = 12; + break; + case 0xa: + length = 16; + break; + case 0xb: + length = 20; + break; + case 0xc: + length = 24; + break; + case 0xd: + length = 32; + break; + case 0xe: + length = 48; + break; + case 0xf: + length = 64; + break; + default: + return nullptr; + } + } + } else if(length > 8) { // This is a standard CAN frame with a length of more than 8 + // Yes, this is possible. On the wire, the length field is a nibble, and we do want to return an accurate value + // We don't want to overread our buffer, though, so make sure we cap the length + length = 8; + } + + // Data + // The first 8 bytes are always in the standard place + if((data->dlc.RTR && data->header.IDE) || (!data->header.IDE && data->header.SRR)) { // Remote Request Frame + msg->data.resize(length); // This data will be all zeros, but the length will be set + msg->isRemote = true; + } else { + msg->data.reserve(length); + msg->data.insert(msg->data.end(), data->data, data->data + (length > 8 ? 8 : length)); + if(length > 8) { // If there are more than 8 bytes, they come at the end of the message + // Messages with extra data are formatted as message, then uint16_t netid, then uint16_t length, then extra data + const auto extraDataStart = bytestream.begin() + sizeof(HardwareCANPacket) + 2 + 2; + msg->data.insert(msg->data.end(), extraDataStart, extraDataStart + (length - 8)); + } + } + + return msg; +} + +bool HardwareCANPacket::EncodeFromMessage(const CANMessage& message, std::vector& result) { + if(message.isCANFD && message.isRemote) + return false; // RTR frames can not be used with CAN FD + + const size_t dataSize = message.data.size(); + if(dataSize > 64 || (dataSize > 8 && !message.isCANFD)) + return false; // Too much data for the protocol + + uint8_t lengthNibble = uint8_t(message.data.size()); + if(lengthNibble > 8) { + switch(lengthNibble) { + case 12: + lengthNibble = 0x9; + break; + case 16: + lengthNibble = 0xA; + break; + case 20: + lengthNibble = 0xB; + break; + case 24: + lengthNibble = 0xC; + break; + case 32: + lengthNibble = 0xD; + break; + case 48: + lengthNibble = 0xE; + break; + case 64: + lengthNibble = 0xF; + break; + default: + return false; // CAN FD frame may have had an incorrect byte count + } + } + + // Pre-allocate as much memory as we will possibly need for speed + result.reserve(17 + dataSize); + + result.push_back(0 /* byte count here later */ << 4 | (uint8_t(message.network.getNetID()) & 0xF)); + + // Two bytes for Description ID, big endian + result.insert(result.end(), { uint8_t(message.description >> 8), uint8_t(message.description) }); + + // Next 2-4 bytes are ArbID + if(message.isExtended) { + if(message.arbid >= 0x20000000) // Extended messages use 29-bit arb IDs + return false; + + result.insert(result.end(), { + (uint8_t)(message.arbid >> 21), + (uint8_t)(((((message.arbid & 0x001C0000) >> 13) & 0xFF) + (((message.arbid & 0x00030000) >> 16) & 0xFF)) | 8), + (uint8_t)(message.arbid >> 8), + (uint8_t)message.arbid + }); + } else { + if(message.arbid >= 0x800) // Standard messages use 11-bit arb IDs + return false; + + result.insert(result.end(), { + (uint8_t)(message.arbid >> 3), + (uint8_t)((message.arbid & 0x7) << 5) + }); + } + + // Status and DLC bits + if(message.isCANFD) { + result.push_back(0x0F); // FD Frame + uint8_t fdStatusByte = lengthNibble; + if(message.baudrateSwitch) + fdStatusByte |= 0x80; // BRS status bit + result.push_back(fdStatusByte); + } else { + // TODO Support high voltage wakeup, bitwise-or in 0x8 here to enable + uint8_t statusNibble = message.isRemote ? 0x4 : 0x0; + result.push_back((statusNibble << 4) | lengthNibble); + } + + // Now finally the payload + result.insert(result.end(), message.data.begin(), message.data.end()); + result.push_back(0); + + // Fill in the length byte from earlier + result[0] |= result.size() << 4; + + return true; +} \ No newline at end of file diff --git a/communication/packet/ethernetpacket.cpp b/communication/packet/ethernetpacket.cpp new file mode 100644 index 0000000..e380f9f --- /dev/null +++ b/communication/packet/ethernetpacket.cpp @@ -0,0 +1,86 @@ +#include "icsneo/communication/packet/ethernetpacket.h" +#include // memcpy +#include + +using namespace icsneo; + +std::shared_ptr HardwareEthernetPacket::DecodeToMessage(const std::vector& bytestream) { + 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; + + size_t bytesOnWire = packet->Length - (sizeof(uint16_t) * 2); + if(bytestream.size() < sizeof(HardwareEthernetPacket) + bytesOnWire) + return nullptr; + + if(bytestream.size() > sizeof(HardwareEthernetPacket) + bytesOnWire) + std::cout << "There is an extra " << (sizeof(HardwareEthernetPacket) + bytesOnWire) << " bytes at the end" << std::endl; + + 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.fcsAvailable = packet->header.FCS_AVAIL; + + message.frameTooShort = packet->header.RUNT_FRAME; + if(message.frameTooShort) + message.error = true; + + // This timestamp is raw off the device (in timestampMultiplier increments) + // Decoder will fix as it has information about the timestampMultiplier increments + message.timestamp = packet->timestamp.TS; + + // Network ID is also not set, this will be fixed in the Decoder as well + + const std::vector::const_iterator databegin = bytestream.begin() + (sizeof(HardwareEthernetPacket) - (sizeof(uint16_t) * 2)); + const std::vector::const_iterator dataend = databegin + bytesOnWire; + message.data.insert(message.data.begin(), databegin, dataend); + + return messagePtr; +} + +bool HardwareEthernetPacket::EncodeFromMessage(const EthernetMessage& message, std::vector& bytestream) { + const size_t unpaddedSize = message.data.size(); + size_t paddedSize = unpaddedSize; + + if(!message.noPadding && unpaddedSize < 60) + paddedSize = 60; // Pad out short messages + + size_t sizeWithHeader = paddedSize + 5; // DescriptionID and Premption Flags + + 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(message.description >> 8); + bytestream[index++] = uint8_t(message.description); + + // Yes, we reserved and allocated space for the preemption flags even if we're not putting them there + // And yes, the data is intended to move over one byte + 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); + + return true; +} \ No newline at end of file diff --git a/include/icsneo/communication/decoder.h b/include/icsneo/communication/decoder.h index ddcde4f..792cd37 100644 --- a/include/icsneo/communication/decoder.h +++ b/include/icsneo/communication/decoder.h @@ -10,8 +10,6 @@ #include #include -#pragma pack(push, 1) - namespace icsneo { class Decoder { @@ -20,44 +18,13 @@ public: Decoder(device_errorhandler_t err) : err(err) {} bool decode(std::shared_ptr& result, const std::shared_ptr& packet); - + + int timestampMultiplier = 25; + private: device_errorhandler_t err; - typedef uint16_t icscm_bitfield; - struct HardwareCANPacket { - struct { - icscm_bitfield IDE : 1; - icscm_bitfield SRR : 1; - icscm_bitfield SID : 11; - icscm_bitfield EDL : 1; - icscm_bitfield BRS : 1; - icscm_bitfield ESI : 1; - } header; - struct { - icscm_bitfield EID : 12; - icscm_bitfield TXMSG : 1; - icscm_bitfield TXAborted : 1; - icscm_bitfield TXLostArb : 1; - icscm_bitfield TXError : 1; - } eid; - struct { - icscm_bitfield DLC : 4; - icscm_bitfield RB0 : 1; - icscm_bitfield IVRIF : 1; - icscm_bitfield HVEnable : 1;// must be cleared before passing into CAN driver - icscm_bitfield ExtendedNetworkIndexBit : 1;//DO NOT CLOBBER THIS - icscm_bitfield RB1 : 1; - icscm_bitfield RTR : 1; - icscm_bitfield EID2 : 6; - } dlc; - unsigned char data[8]; - uint16_t stats; - struct { - uint64_t TS : 60; - uint64_t : 3; // Reserved for future status bits - uint64_t IsExtended : 1; - } timestamp; - }; + +#pragma pack(push, 1) #ifdef _MSC_VER #pragma warning(push) @@ -99,10 +66,9 @@ private: uint16_t busVoltage; uint16_t deviceTemperature; }; +#pragma pack(pop) }; } -#pragma pack(pop) - #endif \ No newline at end of file diff --git a/include/icsneo/communication/message/ethernetmessage.h b/include/icsneo/communication/message/ethernetmessage.h new file mode 100644 index 0000000..749ad0d --- /dev/null +++ b/include/icsneo/communication/message/ethernetmessage.h @@ -0,0 +1,48 @@ +#ifndef __ETHERNETMESSAGE_H_ +#define __ETHERNETMESSAGE_H_ + +#include "icsneo/communication/message/message.h" + +// Used for MACAddress.toString() only +#include +#include + +namespace icsneo { + +struct MACAddress { + uint8_t data[6]; + + // Helpers + std::string toString() const { + std::stringstream ss; + for(size_t i = 0; i < 6; i++) { + ss << std::hex << std::setw(2) << std::setfill('0') << (int)data[i]; + if(i != 5) + ss << ':'; + } + return ss.str(); + } + + friend std::ostream& operator<<(std::ostream& os, const MACAddress& mac) { + os << mac.toString(); + return os; + } +}; + +class EthernetMessage : public Message { +public: + bool preemptionEnabled = false; + uint8_t preemptionFlags = 0; + bool fcsAvailable = false; + bool frameTooShort = false; + bool noPadding = false; + + // Accessors + const MACAddress& getDestinationMAC() const { return *(const MACAddress*)(data.data() + 0); } + const MACAddress& getSourceMAC() const { return *(const MACAddress*)(data.data() + 6); } + uint16_t getEtherType() const { return (data[12] << 8) | data[13]; } +}; + +} + +#endif \ No newline at end of file diff --git a/include/icsneo/communication/message/message.h b/include/icsneo/communication/message/message.h index 41255b9..3ed805c 100644 --- a/include/icsneo/communication/message/message.h +++ b/include/icsneo/communication/message/message.h @@ -11,7 +11,10 @@ public: virtual ~Message() = default; Network network; std::vector data; - uint64_t timestamp; + uint64_t timestamp = 0; + uint16_t description = 0; + bool transmitted = false; + bool error = false; }; } diff --git a/include/icsneo/communication/message/neomessage.h b/include/icsneo/communication/message/neomessage.h index 582bea4..6db8c93 100644 --- a/include/icsneo/communication/message/neomessage.h +++ b/include/icsneo/communication/message/neomessage.h @@ -102,18 +102,20 @@ typedef union { typedef struct { neomessage_statusbitfield_t status; uint64_t timestamp; + uint64_t timestampReserved; const uint8_t* data; size_t length; uint8_t header[4]; uint16_t netid; uint8_t type; uint8_t reserved[17]; -} neomessage_t; // 64 bytes total +} neomessage_t; // 72 bytes total // Any time you add another neomessage_*_t type, make sure to add it to the static_asserts below! typedef struct { neomessage_statusbitfield_t status; uint64_t timestamp; + uint64_t timestampReserved; const uint8_t* data; size_t length; uint32_t arbid; @@ -123,13 +125,28 @@ typedef struct { uint8_t reserved[16]; } neomessage_can_t; +typedef struct { + neomessage_statusbitfield_t status; + uint64_t timestamp; + uint64_t timestampReserved; + const uint8_t* data; + size_t length; + uint8_t preemptionFlags; + uint8_t reservedHeader[3]; + uint16_t netid; + uint8_t type; + uint8_t reserved[17]; +} neomessage_eth_t; + #pragma pack(pop) #ifdef __cplusplus #include "icsneo/communication/message/message.h" #include -static_assert(sizeof(neomessage_can_t) == sizeof(neomessage_t), "All types of neomessage_t must be the same size!"); +static_assert(sizeof(neomessage_t) == 72, "neomessage_t may not change size from 72 bytes!"); +static_assert(sizeof(neomessage_can_t) == sizeof(neomessage_t), "All types of neomessage_t must be the same size! (CAN is not)"); +static_assert(sizeof(neomessage_eth_t) == sizeof(neomessage_t), "All types of neomessage_t must be the same size! (Ethernet is not)"); namespace icsneo { diff --git a/include/icsneo/communication/network.h b/include/icsneo/communication/network.h index 5c0fb60..e183b30 100644 --- a/include/icsneo/communication/network.h +++ b/include/icsneo/communication/network.h @@ -245,11 +245,11 @@ public: case NetID::LIN: return "LIN"; case NetID::OP_Ethernet1: - return "Ethernet 1"; + return "OP (BR) Ethernet 1"; case NetID::OP_Ethernet2: - return "Ethernet 2"; + return "OP (BR) Ethernet 2"; case NetID::OP_Ethernet3: - return "Ethernet 3"; + return "OP (BR) Ethernet 3"; case NetID::RED_EXT_MEMORYREAD: return "RED_EXT_MEMORYREAD"; case NetID::RED_INT_MEMORYREAD: @@ -299,9 +299,9 @@ public: case NetID::HSCAN3: return "HSCAN 3"; case NetID::OP_Ethernet4: - return "Ethernet 4"; + return "OP (BR) Ethernet 4"; case NetID::OP_Ethernet5: - return "Ethernet 5"; + return "OP (BR) Ethernet 5"; case NetID::ISO4: return "ISO 4"; case NetID::LIN2: @@ -351,19 +351,19 @@ public: case NetID::TextAPI_To_Host: return "TextAPI To Host"; case NetID::OP_Ethernet6: - return "Ethernet 6"; + return "OP (BR) Ethernet 6"; case NetID::Red_VBat: return "Red VBat"; case NetID::OP_Ethernet7: - return "Ethernet 7"; + return "OP (BR) Ethernet 7"; case NetID::OP_Ethernet8: - return "Ethernet 8"; + return "OP (BR) Ethernet 8"; case NetID::OP_Ethernet9: - return "Ethernet 9"; + return "OP (BR) Ethernet 9"; case NetID::OP_Ethernet10: - return "Ethernet 10"; + return "OP (BR) Ethernet 10"; case NetID::OP_Ethernet11: - return "Ethernet 11"; + return "OP (BR) Ethernet 11"; case NetID::FlexRay1a: return "FlexRay 1a"; case NetID::FlexRay1b: @@ -379,7 +379,7 @@ public: case NetID::FlexRay2: return "FlexRay 2"; case NetID::OP_Ethernet12: - return "Ethernet 12"; + return "OP (BR) Ethernet 12"; case NetID::MOST25: return "MOST25"; case NetID::MOST50: diff --git a/include/icsneo/communication/packet/canpacket.h b/include/icsneo/communication/packet/canpacket.h new file mode 100644 index 0000000..2b2145d --- /dev/null +++ b/include/icsneo/communication/packet/canpacket.h @@ -0,0 +1,52 @@ +#ifndef __CANPACKET_H__ +#define __CANPACKET_H__ + +#include "icsneo/communication/message/canmessage.h" +#include +#include + +namespace icsneo { + +typedef uint16_t icscm_bitfield; + +struct HardwareCANPacket { + static std::shared_ptr DecodeToMessage(const std::vector& bytestream); + static bool EncodeFromMessage(const CANMessage& message, std::vector& bytestream); + + struct { + icscm_bitfield IDE : 1; + icscm_bitfield SRR : 1; + icscm_bitfield SID : 11; + icscm_bitfield EDL : 1; + icscm_bitfield BRS : 1; + icscm_bitfield ESI : 1; + } header; + struct { + icscm_bitfield EID : 12; + icscm_bitfield TXMSG : 1; + icscm_bitfield TXAborted : 1; + icscm_bitfield TXLostArb : 1; + icscm_bitfield TXError : 1; + } eid; + struct { + icscm_bitfield DLC : 4; + icscm_bitfield RB0 : 1; + icscm_bitfield IVRIF : 1; + icscm_bitfield HVEnable : 1;// must be cleared before passing into CAN driver + icscm_bitfield ExtendedNetworkIndexBit : 1;//DO NOT CLOBBER THIS + icscm_bitfield RB1 : 1; + icscm_bitfield RTR : 1; + icscm_bitfield EID2 : 6; + } dlc; + unsigned char data[8]; + uint16_t stats; + struct { + uint64_t TS : 60; + uint64_t : 3; // Reserved for future status bits + uint64_t IsExtended : 1; + } timestamp; +}; + +} + +#endif \ No newline at end of file diff --git a/include/icsneo/communication/packet/ethernetpacket.h b/include/icsneo/communication/packet/ethernetpacket.h new file mode 100644 index 0000000..c1fceae --- /dev/null +++ b/include/icsneo/communication/packet/ethernetpacket.h @@ -0,0 +1,44 @@ +#ifndef __ETHERNETPACKET_H__ +#define __ETHERNETPACKET_H__ + +#include "icsneo/communication/message/ethernetmessage.h" +#include +#include + +namespace icsneo { + +typedef uint16_t icscm_bitfield; + +struct HardwareEthernetPacket { + static std::shared_ptr DecodeToMessage(const std::vector& bytestream); + static bool EncodeFromMessage(const EthernetMessage& message, std::vector& bytestream); + + struct { + icscm_bitfield FCS_AVAIL : 1; + icscm_bitfield RUNT_FRAME : 1; + icscm_bitfield DISABLE_PADDING : 1; + icscm_bitfield PREEMPTION_ENABLED : 1; + icscm_bitfield MPACKET_TYPE : 4; + icscm_bitfield MPACKET_FRAG_CNT : 2; + icscm_bitfield : 6; + } header; + struct { + icscm_bitfield txlen : 12; + icscm_bitfield TXMSG : 1; + icscm_bitfield : 3; + } eid; + icscm_bitfield reserved; + unsigned char data[8]; + uint16_t stats; + struct { + uint64_t TS : 60; + uint64_t : 3; // Reserved for future status bits + uint64_t IsExtended : 1; + } timestamp; + uint16_t NetworkID; + uint16_t Length; +}; + +} + +#endif \ No newline at end of file diff --git a/include/icsneo/icsneocpp.h b/include/icsneo/icsneocpp.h index 75fc432..b071b0b 100644 --- a/include/icsneo/icsneocpp.h +++ b/include/icsneo/icsneocpp.h @@ -8,6 +8,9 @@ #include "icsneo/api/version.h" #include "icsneo/api/errormanager.h" +#include "icsneo/communication/message/canmessage.h" +#include "icsneo/communication/message/ethernetmessage.h" + namespace icsneo { std::vector> FindAllDevices();