Epsilon: Add PHY settings

pull/64/merge
Jonathan Schwartz 2025-04-09 14:08:55 +00:00 committed by Kyle Schwarz
parent f743f32e63
commit 70733b3d96
11 changed files with 385 additions and 7 deletions

View File

@ -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* 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* 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* 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 // Transport Errors
static constexpr const char* FAILED_TO_READ = "A read operation failed."; 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; return LIN_SETTINGS_NOT_AVAILABLE;
case Type::ModeNotFound: case Type::ModeNotFound:
return MODE_NOT_FOUND; return MODE_NOT_FOUND;
case Type::SettingNotAvaiableDevice:
return SETTING_NOT_AVAILABLE;
// Transport Errors // Transport Errors
case Type::FailedToRead: case Type::FailedToRead:
return FAILED_TO_READ; return FAILED_TO_READ;

View File

@ -38,6 +38,7 @@ pybind11_add_module(icsneopy
icsneopy/communication/message/flexray/flexraymessage.cpp icsneopy/communication/message/flexray/flexraymessage.cpp
icsneopy/disk/diskdriver.cpp icsneopy/disk/diskdriver.cpp
icsneopy/device/device.cpp icsneopy/device/device.cpp
icsneopy/device/idevicesettings.cpp
icsneopy/icsneocpp.cpp icsneopy/icsneocpp.cpp
) )
target_link_libraries(icsneopy PRIVATE icsneocpp) target_link_libraries(icsneopy PRIVATE icsneocpp)

View File

@ -51,7 +51,8 @@ void init_device(pybind11::module_& m) {
.def("supports_tc10", &Device::supportsTC10) .def("supports_tc10", &Device::supportsTC10)
.def("transmit", pybind11::overload_cast<std::shared_ptr<Frame>>(&Device::transmit), pybind11::call_guard<pybind11::gil_scoped_release>()) .def("transmit", pybind11::overload_cast<std::shared_ptr<Frame>>(&Device::transmit), pybind11::call_guard<pybind11::gil_scoped_release>())
.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<pybind11::gil_scoped_release>()) .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<pybind11::gil_scoped_release>())
.def("write_macsec_config", &Device::writeMACsecConfig, pybind11::call_guard<pybind11::gil_scoped_release>()); .def("write_macsec_config", &Device::writeMACsecConfig, pybind11::call_guard<pybind11::gil_scoped_release>())
.def_readonly("settings", &Device::settings);
} }
} // namespace icsneo } // namespace icsneo

View File

@ -0,0 +1,47 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
#include <pybind11/functional.h>
#include <pybind11/chrono.h>
#include "icsneo/device/idevicesettings.h"
#include <fstream>
namespace icsneo {
struct DeviceSettingsNamespace {
using EthLinkMode = OPEthLinkMode;
using LinkSpeed = EthLinkSpeed;
};
void init_idevicesettings(pybind11::module_& m) {
pybind11::class_<DeviceSettingsNamespace> settings(m, "Settings");
pybind11::enum_<DeviceSettingsNamespace::EthLinkMode>(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_<DeviceSettingsNamespace::LinkSpeed>(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_<IDeviceSettings, std::shared_ptr<IDeviceSettings>>(m, "IDeviceSettings")
.def("apply", &IDeviceSettings::apply, pybind11::arg("temporary") = 0, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("apply_defaults", &IDeviceSettings::applyDefaults, pybind11::arg("temporary") = 0, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_phy_enable", &IDeviceSettings::getPhyEnable, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_phy_mode", &IDeviceSettings::getPhyMode, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_phy_speed", &IDeviceSettings::getPhySpeed, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_phy_enable", &IDeviceSettings::setPhyEnable, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_phy_mode", &IDeviceSettings::setPhyMode, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_phy_speed", &IDeviceSettings::setPhySpeed, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("refresh", &IDeviceSettings::refresh, pybind11::arg("ignoreChecksum") = 0, pybind11::call_guard<pybind11::gil_scoped_release>());
}
} // namespace icsneo

View File

@ -29,6 +29,7 @@ void init_messagefilter(pybind11::module_&);
void init_messagecallback(pybind11::module_&); void init_messagecallback(pybind11::module_&);
void init_version(pybind11::module_&); void init_version(pybind11::module_&);
void init_flexraymessage(pybind11::module_& m); void init_flexraymessage(pybind11::module_& m);
void init_idevicesettings(pybind11::module_&);
PYBIND11_MODULE(icsneopy, m) { PYBIND11_MODULE(icsneopy, m) {
pybind11::options options; pybind11::options options;
@ -58,6 +59,7 @@ PYBIND11_MODULE(icsneopy, m) {
init_diskdriver(m); init_diskdriver(m);
init_device(m); init_device(m);
init_flexraymessage(m); init_flexraymessage(m);
init_idevicesettings(m);
m.def("find_all_devices", &FindAllDevices); m.def("find_all_devices", &FindAllDevices);
m.def("get_supported_devices", &GetSupportedDevices); m.def("get_supported_devices", &GetSupportedDevices);

View File

@ -7,3 +7,4 @@ icsneopy
examples examples
api api
radepsilon

View File

@ -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()

View File

@ -110,6 +110,7 @@ public:
ModeNotFound = 0x2054, ModeNotFound = 0x2054,
AppErrorParsingFailed = 0x2055, AppErrorParsingFailed = 0x2055,
GPTPNotSupported = 0x2056, GPTPNotSupported = 0x2056,
SettingNotAvaiableDevice = 0x2057,
// Transport Events // Transport Events
FailedToRead = 0x3000, FailedToRead = 0x3000,

View File

@ -621,7 +621,7 @@ public:
std::optional<bool> SetRootDirectoryEntryFlags(uint8_t mask, uint8_t values, uint32_t collectionEntryByteAddress); std::optional<bool> SetRootDirectoryEntryFlags(uint8_t mask, uint8_t values, uint32_t collectionEntryByteAddress);
std::shared_ptr<Communication> com; std::shared_ptr<Communication> com;
std::unique_ptr<IDeviceSettings> settings; std::shared_ptr<IDeviceSettings> settings;
std::optional<size_t> getGenericBinarySize(uint16_t binaryIndex); std::optional<size_t> getGenericBinarySize(uint16_t binaryIndex);
bool readBinaryFile(std::ostream& stream, uint16_t binaryIndex); bool readBinaryFile(std::ostream& stream, uint16_t binaryIndex);
@ -814,8 +814,8 @@ protected:
} }
template<typename Settings> template<typename Settings>
std::unique_ptr<IDeviceSettings> makeSettings(std::shared_ptr<Communication> comm) { std::shared_ptr<IDeviceSettings> makeSettings(std::shared_ptr<Communication> comm) {
return std::unique_ptr<IDeviceSettings>(new Settings(comm)); return std::make_shared<Settings>(comm);
} }
virtual void setupSettings(IDeviceSettings&) {} virtual void setupSettings(IDeviceSettings&) {}

View File

@ -59,6 +59,9 @@ enum EthLinkSpeed
ETH_SPEED_10 = 0, ETH_SPEED_10 = 0,
ETH_SPEED_100, ETH_SPEED_100,
ETH_SPEED_1000, ETH_SPEED_1000,
ETH_SPEED_2500,
ETH_SPEED_5000,
ETH_SPEED_10000,
}; };
typedef struct typedef struct
@ -851,6 +854,38 @@ public:
*/ */
bool setLINCommanderResponseTimeFor(Network net, uint8_t bits); 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<OPEthLinkMode> getPhyMode(uint8_t index) {
(void)index;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
virtual std::optional<bool> getPhyEnable(uint8_t index) {
(void)index;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
virtual std::optional<EthLinkSpeed> getPhySpeed(uint8_t index) {
(void)index;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
const void* getRawStructurePointer() const { return settingsInDeviceRAM.data(); } const void* getRawStructurePointer() const { return settingsInDeviceRAM.data(); }
void* getMutableRawStructurePointer() { return settings.data(); } void* getMutableRawStructurePointer() { return settings.data(); }
template<typename T> const T* getStructurePointer() const { return reinterpret_cast<const T*>(getRawStructurePointer()); } template<typename T> const T* getStructurePointer() const { return reinterpret_cast<const T*>(getRawStructurePointer()); }

View File

@ -4,6 +4,7 @@
#include <stdint.h> #include <stdint.h>
#include "icsneo/device/idevicesettings.h" #include "icsneo/device/idevicesettings.h"
#include "icsneo/communication/network.h" #include "icsneo/communication/network.h"
#include <optional>
#ifdef __cplusplus #ifdef __cplusplus
@ -122,6 +123,155 @@ public:
return nullptr; 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<radepsilon_settings_t>();
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<uint8_t>(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<radepsilon_settings_t>();
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<radepsilon_settings_t>();
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<uint8_t>(epsilonSpeed);
return true;
}
std::optional<OPEthLinkMode> getPhyMode(uint8_t index) override {
if (index > RADEPSILON_MAX_PHY) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
auto cfg = getStructurePointer<radepsilon_settings_t>();
if (cfg == nullptr) {
return std::nullopt;
}
OPEthLinkMode mode;
switch (static_cast<EpsilonPhyMode>(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<bool> getPhyEnable(uint8_t index) override {
if (index > RADEPSILON_MAX_PHY) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
auto cfg = getStructurePointer<radepsilon_settings_t>();
if (cfg == nullptr) {
return false;
}
return std::make_optional(static_cast<bool>(cfg->switchSettings.enablePhy[index]));
}
std::optional<EthLinkSpeed> getPhySpeed(uint8_t index) override {
if (index > RADEPSILON_MAX_PHY) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
auto cfg = getStructurePointer<radepsilon_settings_t>();
if (cfg == nullptr) {
return std::nullopt;
}
EthLinkSpeed speed;
switch (static_cast<EpsilonPhySpeed>(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 }; // namespace icsneo