diff --git a/CMakeLists.txt b/CMakeLists.txt index babaf25..7732b07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -247,6 +247,7 @@ set(SRC_FILES communication/message/livedatamessage.cpp communication/message/tc10statusmessage.cpp communication/message/gptpstatusmessage.cpp + communication/message/ethernetstatusmessage.cpp communication/packet/flexraypacket.cpp communication/packet/canpacket.cpp communication/packet/a2bpacket.cpp diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 128da44..47813a0 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -18,6 +18,7 @@ pybind11_add_module(icsneopy icsneopy/communication/message/tc10statusmessage.cpp icsneopy/communication/message/mdiomessage.cpp icsneopy/communication/message/gptpstatusmessage.cpp + icsneopy/communication/message/ethernetstatusmessage.cpp icsneopy/communication/message/callback/messagecallback.cpp icsneopy/communication/message/filter/messagefilter.cpp icsneopy/device/device.cpp diff --git a/bindings/python/icsneopy/communication/message/ethernetstatusmessage.cpp b/bindings/python/icsneopy/communication/message/ethernetstatusmessage.cpp new file mode 100644 index 0000000..cedc3ac --- /dev/null +++ b/bindings/python/icsneopy/communication/message/ethernetstatusmessage.cpp @@ -0,0 +1,35 @@ +#include +#include +#include + +#include "icsneo/communication/message/ethernetstatusmessage.h" + +namespace icsneo { + +void init_ethernetstatusmessage(pybind11::module_& m) { + pybind11::class_, Message> ethernetStatusMessage(m, "EthernetStatusMessage"); + + pybind11::enum_(ethernetStatusMessage, "LinkSpeed") + .value("LinkSpeedAuto", EthernetStatusMessage::LinkSpeed::LinkSpeedAuto) + .value("LinkSpeed10", EthernetStatusMessage::LinkSpeed::LinkSpeed10) + .value("LinkSpeed100", EthernetStatusMessage::LinkSpeed::LinkSpeed100) + .value("LinkSpeed1000", EthernetStatusMessage::LinkSpeed::LinkSpeed1000) + .value("LinkSpeed2500", EthernetStatusMessage::LinkSpeed::LinkSpeed2500) + .value("LinkSpeed5000", EthernetStatusMessage::LinkSpeed::LinkSpeed5000) + .value("LinkSpeed10000", EthernetStatusMessage::LinkSpeed::LinkSpeed10000); + + pybind11::enum_(ethernetStatusMessage, "LinkMode") + .value("LinkModeAuto", EthernetStatusMessage::LinkMode::LinkModeAuto) + .value("LinkModeMaster", EthernetStatusMessage::LinkMode::LinkModeMaster) + .value("LinkModeSlave", EthernetStatusMessage::LinkMode::LinkModeSlave) + .value("LinkModeInvalid", EthernetStatusMessage::LinkMode::LinkModeInvalid); + + ethernetStatusMessage + .def_readonly("network", &EthernetStatusMessage::network) + .def_readonly("state", &EthernetStatusMessage::state) + .def_readonly("speed", &EthernetStatusMessage::speed) + .def_readonly("duplex", &EthernetStatusMessage::duplex) + .def_readonly("mode", &EthernetStatusMessage::mode); +} + +} // namespace icsneo diff --git a/bindings/python/icsneopy/communication/message/message.cpp b/bindings/python/icsneopy/communication/message/message.cpp index ddec0ed..0fc73cc 100644 --- a/bindings/python/icsneopy/communication/message/message.cpp +++ b/bindings/python/icsneopy/communication/message/message.cpp @@ -32,7 +32,8 @@ void init_message(pybind11::module_& m) { .value("HardwareInfo", Message::Type::HardwareInfo) .value("TC10Status", Message::Type::TC10Status) .value("AppError", Message::Type::AppError) - .value("GPTPStatus", Message::Type::GPTPStatus); + .value("GPTPStatus", Message::Type::GPTPStatus) + .value("EthernetStatus", Message::Type::EthernetStatus); message.def(pybind11::init()); message.def_readonly("type", &Message::type); diff --git a/bindings/python/icsneopy/icsneocpp.cpp b/bindings/python/icsneopy/icsneocpp.cpp index 1abd18b..97b0316 100644 --- a/bindings/python/icsneopy/icsneocpp.cpp +++ b/bindings/python/icsneopy/icsneocpp.cpp @@ -19,12 +19,15 @@ void init_linmessage(pybind11::module_&); void init_tc10statusmessage(pybind11::module_&); void init_gptpstatusmessage(pybind11::module_&); void init_mdiomessage(pybind11::module_&); +void init_ethernetstatusmessage(pybind11::module_&); void init_device(pybind11::module_&); void init_messagefilter(pybind11::module_&); void init_messagecallback(pybind11::module_&); void init_version(pybind11::module_&); PYBIND11_MODULE(icsneopy, m) { + pybind11::options options; + options.disable_enum_members_docstring(); m.doc() = "libicsneo Python module"; init_event(m); @@ -41,6 +44,7 @@ PYBIND11_MODULE(icsneopy, m) { init_tc10statusmessage(m); init_gptpstatusmessage(m); init_mdiomessage(m); + init_ethernetstatusmessage(m); init_messagefilter(m); init_messagecallback(m); init_device(m); diff --git a/communication/decoder.cpp b/communication/decoder.cpp index 2400def..c7b1fdb 100644 --- a/communication/decoder.cpp +++ b/communication/decoder.cpp @@ -21,6 +21,7 @@ #include "icsneo/communication/message/tc10statusmessage.h" #include "icsneo/communication/message/gptpstatusmessage.h" #include "icsneo/communication/message/apperrormessage.h" +#include "icsneo/communication/message/ethernetstatusmessage.h" #include "icsneo/communication/command.h" #include "icsneo/device/device.h" #include "icsneo/communication/packet/canpacket.h" @@ -238,21 +239,26 @@ bool Decoder::decode(std::shared_ptr& result, const std::shared_ptrdata); - if(!result) { + const auto can = std::dynamic_pointer_cast(HardwareCANPacket::DecodeToMessage(packet->data)); + if(!can) { report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error); - return false; // A nullptr was returned, the packet was malformed + return false; + } + + if(can->arbid == 0x162) { + result = EthernetStatusMessage::DecodeToMessage(can->data); + + if(!result) { + report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error); + return false; + } + } else { + // TODO: move more handleNeoVIMessage handling here, the Decoder layer will parse the message and the Device layer can cache the values + can->network = packet->network; + result = can; } - // Timestamps are in (resolution) ns increments since 1/1/2007 GMT 00:00:00.0000 - // The resolution depends on the device - auto* raw = dynamic_cast(result.get()); - if(raw == nullptr) { - report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error); - return false; // A nullptr was returned, the packet was malformed - } - raw->timestamp *= timestampResolution; - raw->network = packet->network; + result->timestamp *= timestampResolution; return true; } case Network::NetID::DeviceStatus: { diff --git a/communication/message/ethernetstatusmessage.cpp b/communication/message/ethernetstatusmessage.cpp new file mode 100644 index 0000000..2f7edd8 --- /dev/null +++ b/communication/message/ethernetstatusmessage.cpp @@ -0,0 +1,57 @@ +#include "icsneo/communication/message/ethernetstatusmessage.h" + +using namespace icsneo; + +#pragma pack(push, 1) +enum LinkSpeed { + ethSpeed10, + ethSpeed100, + ethSpeed1000, + ethSpeedAutoNeg, + ethSpeed2500, + ethSpeed5000, + ethSpeed10000, +}; + +enum LinkMode { + OPETH_LINK_AUTO, + OPETH_LINK_MASTER, + OPETH_LINK_SLAVE, + OPETH_LINK_INVALID = 255, +}; + +struct Packet { + uint8_t state; + uint8_t speed; + uint8_t duplex; + uint16_t network; + uint8_t mode; +}; +#pragma pack(pop) + +std::shared_ptr EthernetStatusMessage::DecodeToMessage(const std::vector& bytestream) { + if(bytestream.size() < sizeof(Packet)) { + return nullptr; + } + Packet* packet = (Packet*)bytestream.data(); + LinkSpeed speed; + switch(packet->speed) { + case ethSpeed10: speed = EthernetStatusMessage::LinkSpeed::LinkSpeed10; break; + case ethSpeed100: speed = EthernetStatusMessage::LinkSpeed::LinkSpeed100; break; + case ethSpeed1000: speed = EthernetStatusMessage::LinkSpeed::LinkSpeed1000; break; + case ethSpeedAutoNeg: speed = EthernetStatusMessage::LinkSpeed::LinkSpeedAuto; break; + case ethSpeed2500: speed = EthernetStatusMessage::LinkSpeed::LinkSpeed2500; break; + case ethSpeed5000: speed = EthernetStatusMessage::LinkSpeed::LinkSpeed5000; break; + case ethSpeed10000: speed = EthernetStatusMessage::LinkSpeed::LinkSpeed10000; break; + default: return nullptr; + } + LinkMode mode; + switch(packet->mode) { + case OPETH_LINK_INVALID: mode = EthernetStatusMessage::LinkMode::LinkModeInvalid; break; + case OPETH_LINK_AUTO: mode = EthernetStatusMessage::LinkMode::LinkModeAuto; break; + case OPETH_LINK_MASTER: mode = EthernetStatusMessage::LinkMode::LinkModeMaster; break; + case OPETH_LINK_SLAVE: mode = EthernetStatusMessage::LinkMode::LinkModeSlave; break; + default: return nullptr; + } + return std::make_shared(packet->network, packet->state, speed, packet->duplex, mode); +} diff --git a/device/device.cpp b/device/device.cpp index 19bf220..8ab668f 100644 --- a/device/device.cpp +++ b/device/device.cpp @@ -1729,15 +1729,6 @@ void Device::handleInternalMessage(std::shared_ptr message) { case Message::Type::RawMessage: { auto rawMessage = std::static_pointer_cast(message); switch(rawMessage->network.getNetID()) { - case Network::NetID::Device: { - // Device is not guaranteed to be a CANMessage, it might be a RawMessage - // if it couldn't be decoded to a CANMessage. We only care about the - // CANMessage decoding right now. - auto canmsg = std::dynamic_pointer_cast(message); - if(canmsg) - handleNeoVIMessage(std::move(canmsg)); - break; - } case Network::NetID::DeviceStatus: // Device Status format is unique per device, so the devices need to decode it themselves handleDeviceStatus(rawMessage); @@ -1747,6 +1738,15 @@ void Device::handleInternalMessage(std::shared_ptr message) { } break; } + case Message::Type::Frame: { + // Device is not guaranteed to be a CANMessage, it might be a RawMessage + // if it couldn't be decoded to a CANMessage. We only care about the + // CANMessage decoding right now. + auto canmsg = std::dynamic_pointer_cast(message); + if(canmsg) + handleNeoVIMessage(std::move(canmsg)); + break; + } default: break; } forEachExtension([&](const std::shared_ptr& ext) { diff --git a/docs/icsneopy/examples.rst b/docs/icsneopy/examples.rst index ba511e7..48e4b1a 100644 --- a/docs/icsneopy/examples.rst +++ b/docs/icsneopy/examples.rst @@ -50,3 +50,41 @@ Receive CAN frames on HSCAN # rx for 10s time.sleep(10) + + +Monitor Ethernet Status +======================= + +.. code-block:: python + + import icsneopy + import time + + def main(): + devices = icsneopy.find_all_devices() + if len(devices) == 0: + print("error: no devices found") + return False + + device = devices[0] + print(f"info: monitoring Ethernet status on {device}") + + def on_message(message): + print(f"info: network: {message.network}, state: {message.state}, speed: {message.speed}, duplex: {message.duplex}, mode: {message.mode}") + + filter = icsneopy.MessageFilter(icsneopy.Message.Type.EthernetStatus) + callback = icsneopy.MessageCallback(on_message, filter) + device.add_message_callback(callback) + + if not device.open(): + print("error: unable to open device") + return False + + if not device.go_online(): + print("error: unable to go online") + return False + + while True: + time.sleep(1) + + main() diff --git a/include/icsneo/communication/message/ethernetstatusmessage.h b/include/icsneo/communication/message/ethernetstatusmessage.h new file mode 100644 index 0000000..adacbbc --- /dev/null +++ b/include/icsneo/communication/message/ethernetstatusmessage.h @@ -0,0 +1,43 @@ +#ifndef __ETHERNETSTATUSMESSAGE_H__ +#define __ETHERNETSTATUSMESSAGE_H__ + +#ifdef __cplusplus + +#include "icsneo/communication/message/message.h" + +#include + +namespace icsneo { + +class EthernetStatusMessage : public Message { +public: + enum class LinkSpeed { + LinkSpeedAuto, + LinkSpeed10, + LinkSpeed100, + LinkSpeed1000, + LinkSpeed2500, + LinkSpeed5000, + LinkSpeed10000, + }; + enum class LinkMode { + LinkModeAuto, + LinkModeMaster, + LinkModeSlave, + LinkModeInvalid, + }; + EthernetStatusMessage(Network net, bool state, LinkSpeed speed, bool duplex, LinkMode mode) : Message(Type::EthernetStatus), + network(net), state(state), speed(speed), duplex(duplex), mode(mode) {} + Network network; + bool state; + LinkSpeed speed; + bool duplex; + LinkMode mode; + static std::shared_ptr DecodeToMessage(const std::vector& bytestream); +}; + +}; // namespace icsneo + +#endif // __cplusplus + +#endif // __ETHERNETSTATUSMESSAGE_H__ diff --git a/include/icsneo/communication/message/message.h b/include/icsneo/communication/message/message.h index 0414fcb..440ab33 100644 --- a/include/icsneo/communication/message/message.h +++ b/include/icsneo/communication/message/message.h @@ -41,7 +41,8 @@ public: HardwareInfo = 0x8010, TC10Status = 0x8011, AppError = 0x8012, - GPTPStatus = 0x8013 + GPTPStatus = 0x8013, + EthernetStatus = 0x8014, }; Message(Type t) : type(t) {}