From 70733b3d96f5cbcf170d404d93d33c340be2a886 Mon Sep 17 00:00:00 2001 From: Jonathan Schwartz Date: Wed, 9 Apr 2025 14:08:55 +0000 Subject: [PATCH] Epsilon: Add PHY settings --- api/icsneocpp/event.cpp | 3 + bindings/python/CMakeLists.txt | 1 + bindings/python/icsneopy/device/device.cpp | 3 +- .../icsneopy/device/idevicesettings.cpp | 47 ++++++ bindings/python/icsneopy/icsneocpp.cpp | 2 + docs/icsneopy/index.rst | 1 + docs/icsneopy/radepsilon.rst | 137 ++++++++++++++++ include/icsneo/api/event.h | 7 +- include/icsneo/device/device.h | 6 +- include/icsneo/device/idevicesettings.h | 35 ++++ .../tree/radepsilon/radepsilonsettings.h | 150 ++++++++++++++++++ 11 files changed, 385 insertions(+), 7 deletions(-) create mode 100644 bindings/python/icsneopy/device/idevicesettings.cpp create mode 100644 docs/icsneopy/radepsilon.rst diff --git a/api/icsneocpp/event.cpp b/api/icsneocpp/event.cpp index 265b6df..8c23b7f 100644 --- a/api/icsneocpp/event.cpp +++ b/api/icsneocpp/event.cpp @@ -121,6 +121,7 @@ static constexpr const char* UNEXPECTED_RESPONSE = "Received an unexpected or in static constexpr const char* LIN_SETTINGS_NOT_AVAILABLE = "LIN settings are not available for this device."; static constexpr const char* MODE_NOT_FOUND = "The mode was not found."; static constexpr const char* GPTP_NOT_SUPPORTED = "GPTP clock synchronization is not supported on this device."; +static constexpr const char* SETTING_NOT_AVAILABLE = "Requested a setting that is not available on this device"; // Transport Errors static constexpr const char* FAILED_TO_READ = "A read operation failed."; @@ -316,6 +317,8 @@ const char* APIEvent::DescriptionForType(Type type) { return LIN_SETTINGS_NOT_AVAILABLE; case Type::ModeNotFound: return MODE_NOT_FOUND; + case Type::SettingNotAvaiableDevice: + return SETTING_NOT_AVAILABLE; // Transport Errors case Type::FailedToRead: return FAILED_TO_READ; diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 23a8268..7e6935f 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -38,6 +38,7 @@ pybind11_add_module(icsneopy icsneopy/communication/message/flexray/flexraymessage.cpp icsneopy/disk/diskdriver.cpp icsneopy/device/device.cpp + icsneopy/device/idevicesettings.cpp icsneopy/icsneocpp.cpp ) target_link_libraries(icsneopy PRIVATE icsneocpp) diff --git a/bindings/python/icsneopy/device/device.cpp b/bindings/python/icsneopy/device/device.cpp index 897a313..423ec28 100644 --- a/bindings/python/icsneopy/device/device.cpp +++ b/bindings/python/icsneopy/device/device.cpp @@ -51,7 +51,8 @@ void init_device(pybind11::module_& m) { .def("supports_tc10", &Device::supportsTC10) .def("transmit", pybind11::overload_cast>(&Device::transmit), pybind11::call_guard()) .def("upload_coremini", [](Device& device, std::string& path, Disk::MemoryType memType) { std::ifstream ifs(path, std::ios::binary); return device.uploadCoremini(ifs, memType); }, pybind11::call_guard()) - .def("write_macsec_config", &Device::writeMACsecConfig, pybind11::call_guard()); + .def("write_macsec_config", &Device::writeMACsecConfig, pybind11::call_guard()) + .def_readonly("settings", &Device::settings); } } // namespace icsneo diff --git a/bindings/python/icsneopy/device/idevicesettings.cpp b/bindings/python/icsneopy/device/idevicesettings.cpp new file mode 100644 index 0000000..a55aec5 --- /dev/null +++ b/bindings/python/icsneopy/device/idevicesettings.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include + +#include "icsneo/device/idevicesettings.h" + +#include + +namespace icsneo { + +struct DeviceSettingsNamespace { + using EthLinkMode = OPEthLinkMode; + using LinkSpeed = EthLinkSpeed; +}; + +void init_idevicesettings(pybind11::module_& m) { + pybind11::class_ settings(m, "Settings"); + + pybind11::enum_(settings, "EthernetLinkMode") + .value("Auto", DeviceSettingsNamespace::EthLinkMode::OPETH_LINK_AUTO) + .value("Slave", DeviceSettingsNamespace::EthLinkMode::OPETH_LINK_SLAVE) + .value("Master", DeviceSettingsNamespace::EthLinkMode::OPETH_LINK_MASTER); + + pybind11::enum_(settings, "EthernetLinkSpeed") + .value("Speed10M", DeviceSettingsNamespace::LinkSpeed::ETH_SPEED_10) + .value("Speed100M", DeviceSettingsNamespace::LinkSpeed::ETH_SPEED_100) + .value("Speed1G", DeviceSettingsNamespace::LinkSpeed::ETH_SPEED_1000) + .value("Speed2_5G", DeviceSettingsNamespace::LinkSpeed::ETH_SPEED_2500) + .value("Speed5G", DeviceSettingsNamespace::LinkSpeed::ETH_SPEED_5000) + .value("Speed10G", DeviceSettingsNamespace::LinkSpeed::ETH_SPEED_10000); + + pybind11::class_>(m, "IDeviceSettings") + .def("apply", &IDeviceSettings::apply, pybind11::arg("temporary") = 0, pybind11::call_guard()) + .def("apply_defaults", &IDeviceSettings::applyDefaults, pybind11::arg("temporary") = 0, pybind11::call_guard()) + .def("get_phy_enable", &IDeviceSettings::getPhyEnable, pybind11::call_guard()) + .def("get_phy_mode", &IDeviceSettings::getPhyMode, pybind11::call_guard()) + .def("get_phy_speed", &IDeviceSettings::getPhySpeed, pybind11::call_guard()) + .def("set_phy_enable", &IDeviceSettings::setPhyEnable, pybind11::call_guard()) + .def("set_phy_mode", &IDeviceSettings::setPhyMode, pybind11::call_guard()) + .def("set_phy_speed", &IDeviceSettings::setPhySpeed, pybind11::call_guard()) + .def("refresh", &IDeviceSettings::refresh, pybind11::arg("ignoreChecksum") = 0, pybind11::call_guard()); +} + +} // namespace icsneo + diff --git a/bindings/python/icsneopy/icsneocpp.cpp b/bindings/python/icsneopy/icsneocpp.cpp index 9437937..d169444 100644 --- a/bindings/python/icsneopy/icsneocpp.cpp +++ b/bindings/python/icsneopy/icsneocpp.cpp @@ -29,6 +29,7 @@ void init_messagefilter(pybind11::module_&); void init_messagecallback(pybind11::module_&); void init_version(pybind11::module_&); void init_flexraymessage(pybind11::module_& m); +void init_idevicesettings(pybind11::module_&); PYBIND11_MODULE(icsneopy, m) { pybind11::options options; @@ -58,6 +59,7 @@ PYBIND11_MODULE(icsneopy, m) { init_diskdriver(m); init_device(m); init_flexraymessage(m); + init_idevicesettings(m); m.def("find_all_devices", &FindAllDevices); m.def("get_supported_devices", &GetSupportedDevices); diff --git a/docs/icsneopy/index.rst b/docs/icsneopy/index.rst index d6197d2..d318980 100644 --- a/docs/icsneopy/index.rst +++ b/docs/icsneopy/index.rst @@ -7,3 +7,4 @@ icsneopy examples api + radepsilon diff --git a/docs/icsneopy/radepsilon.rst b/docs/icsneopy/radepsilon.rst new file mode 100644 index 0000000..a36173f --- /dev/null +++ b/docs/icsneopy/radepsilon.rst @@ -0,0 +1,137 @@ +======================================= +Python Examples for RAD-Epsilon Devices +======================================= + + + +Working with PHY Settings +========================= + +.. code-block:: python + + import sys + import icsneopy + + + class PhyStaticInfo: + + def __init__(self, index, type, switch_port): + self.index = index + self.type = type + self.switch_port = switch_port + + def __repr__(self): + return f"{self.switch_port}: Type {self.type} / Index {self.index}" + + + # Switch Port name to PHY index and PHY type + RAD_EPSILON_PHY_INFO = { + "1": PhyStaticInfo(0, "88Q2112", "1"), + "2": PhyStaticInfo(1, "88Q2112", "2"), + "3": PhyStaticInfo(2, "88Q2112", "3"), + "4": PhyStaticInfo(3, "88Q2112", "4"), + "5": PhyStaticInfo(4, "88Q2112", "5"), + "6": PhyStaticInfo(5, "88Q2112", "6"), + "9": PhyStaticInfo(6, "88Q2112", "9"), + "10": PhyStaticInfo(7, "88Q2112", "10") + } + + RAD_EPSILON_XL_PHY_INFO = { + "A1": PhyStaticInfo(0, "88Q222x", "A1"), + "A2": PhyStaticInfo(1, "88Q222x", "A2"), + "A3": PhyStaticInfo(2, "88Q222x", "A3"), + "A4": PhyStaticInfo(3, "88Q222x", "A4"), + "A5": PhyStaticInfo(4, "88Q222x", "A5"), + "A6": PhyStaticInfo(5, "88Q222x", "A6"), + "A11": PhyStaticInfo(6, "88Q222x", "A11"), + "A8": PhyStaticInfo(7, "88Q222x", "A8"), + "B1": PhyStaticInfo(8, "88Q222x", "B1"), + "B2": PhyStaticInfo(9, "88Q222x", "B2"), + "B3": PhyStaticInfo(10, "88Q222x", "B3"), + "B4": PhyStaticInfo(11, "88Q222x", "B4"), + "B5": PhyStaticInfo(12, "88Q222x", "B5"), + "B6": PhyStaticInfo(13, "88Q222x", "B6"), + "B7": PhyStaticInfo(14, "88Q222x", "B7"), + "B8": PhyStaticInfo(15, "88Q222x", "B8"), + "A9": PhyStaticInfo(16, "88X3310", "A9"), + "B9": PhyStaticInfo(17, "88X3310", "B9"), + } + + + def find_specific_device(type: icsneopy.DeviceType.Enum, serial: str | None = None) -> icsneopy.Device | None: + devices = icsneopy.find_all_devices() + for device in devices: + device_type = device.get_type().get_device_type() + if serial: # always find by serial if specified + if device.get_serial() == serial: + return device + elif device_type == type: + return device + return None + + + def find_rad_epsilonxl(serial: str | None = None) -> icsneopy.Device | None: + return find_specific_device(icsneopy.DeviceType.Enum.RADEpsilonXL, serial) + + + def find_rad_epsilon(serial: str | None = None) -> icsneopy.Device | None: + return find_specific_device(icsneopy.DeviceType.Enum.RADEpsilon, serial) + + + def report_phy_settings(device: icsneopy.Device, phy: PhyStaticInfo) -> bool: + phy_enable = device.settings.get_phy_enable(phy.index) + phy_mode = device.settings.get_phy_mode(phy.index) + phy_speed = device.settings.get_phy_speed(phy.index) + if phy_enable is None or phy_mode is None or phy_speed is None: + print(f"failed to get settings for PHY{phy.index} on port {phy.switch_port}") + print(f"PHY {phy.index} ({phy.type})") + print(f"\tPort: {phy.switch_port}") + print(f"\tEnabled: {'TRUE' if phy_enable else 'FALSE'}") + print(f"\tMode: {phy_mode}") + print(f"\tSpeed: {phy_speed}") + return True + + def configure_phy(device: icsneopy.Device, phy: PhyStaticInfo, enable: bool, mode: int, speed: int) -> bool: + if not device.settings.set_phy_enable(phy.index, enable): + print(f"failed to set enable for PHY{phy.index} at port {phy.switch_port}") + return False + if not device.settings.set_phy_mode(phy.index, mode): + print(f"failed to set mode for PHY{phy.index} at port {phy.switch_port}") + return False + if not device.settings.set_phy_speed(phy.index, speed): + print(f"failed to set speed for PHY{phy.index} at port {phy.switch_port}") + return False + if not device.settings.apply(True): + print(f"failed to apply settings") + print(icsneopy.get_last_error()) + return False + print(f"Successfully set settings for PHY{phy.index} at port {phy.switch_port}") + return True + + + # find the first RAD-EpsilonXL + device = find_rad_epsilonxl() + if not device: + print("failed to find a RAD-EpsilonXL to open") + sys.exit(-1) + if not device.open(): + print(f"Failed to open {device.describe()}") + print(icsneopy.get_last_error()) + sys.exit(-1) + print(f"Opened {device.describe()}") + print("Applying default settings") + if not device.settings.apply_defaults(): + print(icsneopy.get_last_error()) + sys.exit(-1) + print("Reporting PHY settings") + for phy in RAD_EPSILON_XL_PHY_INFO.values(): + report_phy_settings(device, phy) + # Setting Port A1 to Master mode, enabled, 1G speed + phy = RAD_EPSILON_XL_PHY_INFO["A1"] + if configure_phy(device, phy, True, icsneopy.Settings.EthernetLinkMode.Master, icsneopy.Settings.EthernetLinkSpeed.Speed1G) and device.settings.refresh(True): + report_phy_settings(device, phy) # report them back + else: + print(icsneopy.get_last_error()) + sys.exit(-1) + device.close() + diff --git a/include/icsneo/api/event.h b/include/icsneo/api/event.h index 666d1d9..941559c 100644 --- a/include/icsneo/api/event.h +++ b/include/icsneo/api/event.h @@ -110,6 +110,7 @@ public: ModeNotFound = 0x2054, AppErrorParsingFailed = 0x2055, GPTPNotSupported = 0x2056, + SettingNotAvaiableDevice = 0x2057, // Transport Events FailedToRead = 0x3000, @@ -186,19 +187,19 @@ public: APIEvent() : eventStruct({}), serial(), timepoint(), device(nullptr) {} APIEvent(APIEvent::Type event, APIEvent::Severity severity, const Device* device = nullptr); - + const neoevent_t* getNeoEvent() const noexcept { return &eventStruct; } Type getType() const noexcept { return Type(eventStruct.eventNumber); } Severity getSeverity() const noexcept { return Severity(eventStruct.severity); } std::string getDescription() const noexcept { return std::string(eventStruct.description); } const Device* getDevice() const noexcept { return device; } // Will return nullptr if this is an API-wide event EventTimePoint getTimestamp() const noexcept { return timepoint; } - + void downgradeFromError() noexcept; bool isForDevice(const Device* forDevice) const noexcept { return forDevice == device; } bool isForDevice(std::string serial) const noexcept; - + // As opposed to getDescription, this will also add text such as "neoVI FIRE 2 CY2468 Error: " to fully describe the problem std::string describe() const noexcept; friend std::ostream& operator<<(std::ostream& os, const APIEvent& event) { diff --git a/include/icsneo/device/device.h b/include/icsneo/device/device.h index 71997f9..e940de8 100644 --- a/include/icsneo/device/device.h +++ b/include/icsneo/device/device.h @@ -621,7 +621,7 @@ public: std::optional SetRootDirectoryEntryFlags(uint8_t mask, uint8_t values, uint32_t collectionEntryByteAddress); std::shared_ptr com; - std::unique_ptr settings; + std::shared_ptr settings; std::optional getGenericBinarySize(uint16_t binaryIndex); bool readBinaryFile(std::ostream& stream, uint16_t binaryIndex); @@ -814,8 +814,8 @@ protected: } template - std::unique_ptr makeSettings(std::shared_ptr comm) { - return std::unique_ptr(new Settings(comm)); + std::shared_ptr makeSettings(std::shared_ptr comm) { + return std::make_shared(comm); } virtual void setupSettings(IDeviceSettings&) {} diff --git a/include/icsneo/device/idevicesettings.h b/include/icsneo/device/idevicesettings.h index aafaab9..c9f04db 100644 --- a/include/icsneo/device/idevicesettings.h +++ b/include/icsneo/device/idevicesettings.h @@ -59,6 +59,9 @@ enum EthLinkSpeed ETH_SPEED_10 = 0, ETH_SPEED_100, ETH_SPEED_1000, + ETH_SPEED_2500, + ETH_SPEED_5000, + ETH_SPEED_10000, }; typedef struct @@ -851,6 +854,38 @@ public: */ bool setLINCommanderResponseTimeFor(Network net, uint8_t bits); + virtual bool setPhyMode(uint8_t index, OPEthLinkMode mode) { + (void)index, (void)mode; + return false; + } + + virtual bool setPhyEnable(uint8_t index, bool enable) { + (void)index, (void)enable; + return false; + } + + virtual bool setPhySpeed(uint8_t index, EthLinkSpeed speed) { + (void)index, (void)speed; + return false; + } + + virtual std::optional getPhyMode(uint8_t index) { + (void)index; + report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning); + return std::nullopt; + } + + virtual std::optional getPhyEnable(uint8_t index) { + (void)index; + report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning); + return std::nullopt; + } + + virtual std::optional getPhySpeed(uint8_t index) { + (void)index; + report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning); + return std::nullopt; + } const void* getRawStructurePointer() const { return settingsInDeviceRAM.data(); } void* getMutableRawStructurePointer() { return settings.data(); } template const T* getStructurePointer() const { return reinterpret_cast(getRawStructurePointer()); } diff --git a/include/icsneo/device/tree/radepsilon/radepsilonsettings.h b/include/icsneo/device/tree/radepsilon/radepsilonsettings.h index ee29f82..5f79524 100644 --- a/include/icsneo/device/tree/radepsilon/radepsilonsettings.h +++ b/include/icsneo/device/tree/radepsilon/radepsilonsettings.h @@ -4,6 +4,7 @@ #include #include "icsneo/device/idevicesettings.h" #include "icsneo/communication/network.h" +#include #ifdef __cplusplus @@ -122,6 +123,155 @@ public: return nullptr; } } + + bool setPhyMode(uint8_t index, OPEthLinkMode mode) override { + if (index > RADEPSILON_MAX_PHY) { + report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); + return false; + } + auto cfg = getMutableStructurePointer(); + if (cfg == nullptr) { + return false; + } + EpsilonPhyMode epsilonMode; + switch (mode) { + default: + report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); + return false; + case OPETH_LINK_AUTO: + epsilonMode = EpsilonPhyMode::Auto; + break; + case OPETH_LINK_SLAVE: + epsilonMode = EpsilonPhyMode::Slave; + break; + case OPETH_LINK_MASTER: + epsilonMode = EpsilonPhyMode::Master; + break; + + } + cfg->switchSettings.phyMode[index] = static_cast(epsilonMode); + return true; + } + + bool setPhyEnable(uint8_t index, bool enable) override { + if (index > RADEPSILON_MAX_PHY) { + report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); + return false; + } + auto cfg = getMutableStructurePointer(); + if (cfg == nullptr) { + return false; + } + cfg->switchSettings.enablePhy[index] = enable; + return true; + } + + bool setPhySpeed(uint8_t index, EthLinkSpeed speed) override { + if (index > RADEPSILON_MAX_PHY) { + report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); + return false; + } + auto cfg = getMutableStructurePointer(); + if (cfg == nullptr) { + return false; + } + EpsilonPhySpeed epsilonSpeed; + switch (speed) { + default: + report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); + return false; + case ETH_SPEED_100: + epsilonSpeed = EpsilonPhySpeed::Speed100; + break; + case ETH_SPEED_1000: + epsilonSpeed = EpsilonPhySpeed::Speed1000; + break; + case ETH_SPEED_10000: + epsilonSpeed = EpsilonPhySpeed::Speed10000; + break; + } + cfg->switchSettings.speed[index] = static_cast(epsilonSpeed); + return true; + } + + std::optional getPhyMode(uint8_t index) override { + if (index > RADEPSILON_MAX_PHY) { + report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); + return std::nullopt; + } + auto cfg = getStructurePointer(); + if (cfg == nullptr) { + return std::nullopt; + } + OPEthLinkMode mode; + switch (static_cast(cfg->switchSettings.phyMode[index])) { + default: + report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); + return std::nullopt; + case EpsilonPhyMode::Auto: + mode = OPETH_LINK_AUTO; + break; + case EpsilonPhyMode::Slave: + mode = OPETH_LINK_SLAVE; + break; + case EpsilonPhyMode::Master: + mode = OPETH_LINK_MASTER; + break; + } + return std::make_optional(mode); + } + + std::optional getPhyEnable(uint8_t index) override { + if (index > RADEPSILON_MAX_PHY) { + report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); + return std::nullopt; + } + auto cfg = getStructurePointer(); + if (cfg == nullptr) { + return false; + } + return std::make_optional(static_cast(cfg->switchSettings.enablePhy[index])); + } + + std::optional getPhySpeed(uint8_t index) override { + if (index > RADEPSILON_MAX_PHY) { + report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); + return std::nullopt; + } + auto cfg = getStructurePointer(); + if (cfg == nullptr) { + return std::nullopt; + } + EthLinkSpeed speed; + switch (static_cast(cfg->switchSettings.speed[index])) { + default: + report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); + return std::nullopt; + case EpsilonPhySpeed::Speed100: + speed = ETH_SPEED_100; + break; + case EpsilonPhySpeed::Speed1000: + speed = ETH_SPEED_1000; + break; + case EpsilonPhySpeed::Speed10000: + speed = ETH_SPEED_10000; + break; + } + return std::make_optional(speed); + } + +private: + enum class EpsilonPhyMode : uint8_t { + Auto = 0, + Master = 1, + Slave = 2, + }; + enum class EpsilonPhySpeed : uint8_t { + Speed100 = 0, + Speed1000 = 1, + Speed10000 = 2, + }; + }; }; // namespace icsneo