35 Commits
Author SHA1 Message Date
Kyle Schwarz 22d2b7c984 Servd: Adjust buffer sizes 2026-03-18 18:21:55 -04:00
Kyle Schwarz 5a4a1489a8 DXX: Update for max packet size 2026-03-18 15:35:31 -04:00
Thomas StoddardandKyle Schwarz b0b7623b4c Core: MACsec: Mirror 128-bit keys 2026-03-16 13:59:40 -04:00
Max BrombachandKyle Schwarz 171422d8e1 Device: RAD-Gemini: Add chip info and fix bootloader pipeline 2026-03-10 15:13:09 -04:00
Kyle Schwarz 81769db025 Device: NeoVIFIRE3: Add CAN networks to settings 2026-03-06 16:08:40 -05:00
Kyle Schwarz 0e60f8f459 Device: RADComet3: Handle AE_LINK_INVALID 2026-03-04 19:37:00 -05:00
Thomas StoddardandKyle Schwarz 0aa7d338fd Bindings: Python: Add formatDisk 2026-03-03 15:38:13 -05:00
Kyle Schwarz 224e840841 Device: Increase DiskFormatProgress timeout 2026-02-17 17:16:49 -05:00
Max Brombach 174c0b80d4 Device: VCAN4-IND: Add chips and bootloader information 2026-02-17 12:00:34 -05:00
Max BrombachandKyle Schwarz 19092bceb6 Device: RAD-MoonT1S: Add bootloader pipeline and chip 2026-02-13 13:17:59 -05:00
Kyle Schwarz 25b673075f All: Copyright 2026 2026-02-13 10:08:09 -05:00
Max BrombachandKyle Schwarz 1a7bc4df47 Device: RADJupiter: Add bootloader pipeline 2026-02-12 17:05:39 -05:00
Max BrombachandKyle Schwarz 20a2474508 Device: Settings: Add missing bit-packed variables to CAN_SETTINGS 2026-02-12 16:25:57 -05:00
Thomas StoddardandKyle Schwarz d18ca9e6eb Device: RAD-Galaxy: Add support for Analog Output 2026-02-11 18:43:30 -05:00
Max BrombachandKyle Schwarz 6cda765fe0 Device: FIRE2: Skip flashing Core chip when chip variant is unknown 2026-02-10 21:20:47 -05:00
Jonathan SchwartzandKyle Schwarz 730aaf5fed Device: FIRE3: Update disk counts 2026-02-06 14:32:31 -05:00
Kyle Schwarz 1f10adb760 ThirdParty: Update icspb & libredxx 2026-01-30 17:12:09 -05:00
Thomas StoddardandKyle Schwarz 6a32823a0f Device: FlexRay: Add additional configuration options for Controller and Cluster 2026-01-30 13:06:39 -05:00
Thomas StoddardandKyle Schwarz 5f16adc103 Device: RADComet3: Add Ethernet settings 2026-01-28 16:00:39 -05:00
Jonathan SchwartzandKyle Schwarz f6926cbb22 Device: Unlock network mutex when going offline 2026-01-27 15:22:12 -05:00
Thomas StoddardandKyle Schwarz 4c7b8e107a Device: RADComet: Add T1S settings 2026-01-23 13:03:39 -05:00
Jonathan SchwartzandKyle Schwarz 0e55101a16 Device: goOnline: Refactor network locking
Only lock known networks.
2026-01-22 16:46:51 -05:00
Max BrombachandKyle Schwarz 68ebb6dae4 Device: Add EnterApplicationPhases step 2026-01-20 14:25:29 -05:00
Jonathan SchwartzandKyle Schwarz 8cb62c2cae Device: Carry bootloader version for potential compatibility checks 2026-01-12 17:45:10 -05:00
Kyle Schwarz d74051f57e Driver: Servd: Refactor to TCP 2026-01-12 13:03:55 -05:00
Thomas Stoddard 530a99d264 Add T1S PLCA and related settings support for Fire3T1S, RadComet3, RadGigaStar2, and RadMoonT1S devices 2026-01-09 18:16:10 +00:00
Thomas StoddardandKyle Schwarz 5ee450353b Bindings: Python: Add T1S members 2026-01-07 23:32:20 -05:00
Thomas StoddardandKyle Schwarz 516bca682c EthernetMessage: Add T1S symbol support 2026-01-07 16:34:34 -05:00
Jonathan SchwartzandKyle Schwarz 3f5150bef3 FirmIO: Fix instability and memory leak issues 2026-01-07 13:35:23 -05:00
Thomas StoddardandKyle Schwarz 2a2d55f20d Bindings: Python : Add baudrate and LIN mode methods 2026-01-07 10:51:25 -05:00
Thomas StoddardandKyle Schwarz 977677e3af Bindings: Add LiveData and LiveDataMessage support in Python bindings 2026-01-05 10:40:14 -05:00
Kyle Schwarz 000036f745 Driver: DXX: Update
Fixes D2XX HANDLE leak.
2025-12-19 22:30:14 -05:00
Kyle Schwarz be6a15c017 Device: Galaxy2: Update supported networks 2025-12-18 12:22:12 -05:00
Nicholas ZamoraandKyle Schwarz 5288385495 Driver: DXX: Update libredxx for FT260 support 2025-12-16 11:05:11 -05:00
Max Brombach d6d9fc16ef Device: Update chips for ValueCAN4_2EL bootloader 2025-12-10 19:47:23 +00:00
69 changed files with 3970 additions and 526 deletions
+3
View File
@@ -5,3 +5,6 @@ KERNEL=="ttyACM?", ATTRS{idVendor}=="093c", GROUP="users", MODE="0666"
ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="093c", KERNEL=="ttyUSB*", \ ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="093c", KERNEL=="ttyUSB*", \
RUN+="/bin/sh -c 'echo $id:1.0>/sys/bus/usb/drivers/ftdi_sio/unbind'" RUN+="/bin/sh -c 'echo $id:1.0>/sys/bus/usb/drivers/ftdi_sio/unbind'"
ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="093c", DRIVER=="usbhid", \
RUN+="/bin/sh -c 'echo $id:1.0>/sys/bus/usb/drivers/usbhid/unbind'"
+2 -2
View File
@@ -360,7 +360,7 @@ if(LIBICSNEO_ENABLE_DXX)
include(FetchContent) include(FetchContent)
FetchContent_Declare(libredxx FetchContent_Declare(libredxx
GIT_REPOSITORY https://github.com/Zeranoe/libredxx.git GIT_REPOSITORY https://github.com/Zeranoe/libredxx.git
GIT_TAG e1fe2bd6ba6079b17037379d78f3f18024b389d7 GIT_TAG e823a96c39a64ab41b7d1632dbe8f86bb854df83
) )
set(LIBREDXX_DISABLE_INSTALL ON) set(LIBREDXX_DISABLE_INSTALL ON)
FetchContent_MakeAvailable(libredxx) FetchContent_MakeAvailable(libredxx)
@@ -392,7 +392,7 @@ endif()
include(FetchContent) include(FetchContent)
FetchContent_Declare(icspb FetchContent_Declare(icspb
GIT_REPOSITORY ${LIBICSNEO_ICSPB_REPO} GIT_REPOSITORY ${LIBICSNEO_ICSPB_REPO}
GIT_TAG 48df5dd7fd0c38034f82a2f94e0eada404d5e2b9 GIT_TAG 3339fa6b83a6b3e7704d41f5c2f2175cfc761a1f
) )
FetchContent_MakeAvailable(icspb) FetchContent_MakeAvailable(icspb)
target_link_libraries(icsneocpp PRIVATE icspb::icspb) target_link_libraries(icsneocpp PRIVATE icspb::icspb)
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2018-2025 Intrepid Control Systems, Inc. Copyright (c) 2018-2026 Intrepid Control Systems, Inc.
Redistribution and use in source and binary forms, with or without modification, Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met: are permitted provided that the following conditions are met:
+1 -1
View File
@@ -29,7 +29,7 @@ BEGIN
VALUE "FileDescription", "Intrepid Control Systems Open Device Communication C API" VALUE "FileDescription", "Intrepid Control Systems Open Device Communication C API"
VALUE "FileVersion", VER_FILEVERSION_STR VALUE "FileVersion", VER_FILEVERSION_STR
VALUE "InternalName", "icsneoc.dll" VALUE "InternalName", "icsneoc.dll"
VALUE "LegalCopyright", "Intrepid Control Systems, Inc. (C) 2018-2025" VALUE "LegalCopyright", "Intrepid Control Systems, Inc. (C) 2018-2026"
VALUE "OriginalFilename", "icsneoc.dll" VALUE "OriginalFilename", "icsneoc.dll"
VALUE "ProductName", "libicsneo" VALUE "ProductName", "libicsneo"
VALUE "ProductVersion", VER_PRODUCTVERSION_STR VALUE "ProductVersion", VER_PRODUCTVERSION_STR
+3
View File
@@ -22,6 +22,7 @@ pybind11_add_module(icsneopy
icsneopy/device/devicetype.cpp icsneopy/device/devicetype.cpp
icsneopy/communication/network.cpp icsneopy/communication/network.cpp
icsneopy/communication/io.cpp icsneopy/communication/io.cpp
icsneopy/communication/livedata.cpp
icsneopy/communication/message/message.cpp icsneopy/communication/message/message.cpp
icsneopy/communication/message/canmessage.cpp icsneopy/communication/message/canmessage.cpp
icsneopy/communication/message/canerrormessage.cpp icsneopy/communication/message/canerrormessage.cpp
@@ -34,11 +35,13 @@ pybind11_add_module(icsneopy
icsneopy/communication/message/spimessage.cpp icsneopy/communication/message/spimessage.cpp
icsneopy/communication/message/scriptstatusmessage.cpp icsneopy/communication/message/scriptstatusmessage.cpp
icsneopy/communication/message/ethphymessage.cpp icsneopy/communication/message/ethphymessage.cpp
icsneopy/communication/message/livedatamessage.cpp
icsneopy/communication/message/callback/messagecallback.cpp icsneopy/communication/message/callback/messagecallback.cpp
icsneopy/communication/message/filter/messagefilter.cpp icsneopy/communication/message/filter/messagefilter.cpp
icsneopy/core/macseccfg.cpp icsneopy/core/macseccfg.cpp
icsneopy/flexray/flexray.cpp icsneopy/flexray/flexray.cpp
icsneopy/disk/diskdriver.cpp icsneopy/disk/diskdriver.cpp
icsneopy/disk/diskdetails.cpp
icsneopy/device/chipid.cpp icsneopy/device/chipid.cpp
icsneopy/device/versionreport.cpp icsneopy/device/versionreport.cpp
icsneopy/device/device.cpp icsneopy/device/device.cpp
@@ -0,0 +1,79 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>
#include <pybind11/native_enum.h>
#include "icsneo/communication/livedata.h"
namespace icsneo {
void init_livedata(pybind11::module_& m) {
// LiveDataValue struct
pybind11::classh<LiveDataValue>(m, "LiveDataValue")
.def(pybind11::init<>())
.def_readwrite("value", &LiveDataValue::value);
// LiveDataArgument struct
pybind11::classh<LiveDataArgument>(m, "LiveDataArgument")
.def(pybind11::init<>())
.def_readwrite("object_type", &LiveDataArgument::objectType)
.def_readwrite("object_index", &LiveDataArgument::objectIndex)
.def_readwrite("signal_index", &LiveDataArgument::signalIndex)
.def_readwrite("value_type", &LiveDataArgument::valueType);
// LiveDataCommand enum
pybind11::native_enum<LiveDataCommand>(m, "LiveDataCommand", "enum.IntEnum")
.value("STATUS", LiveDataCommand::STATUS)
.value("SUBSCRIBE", LiveDataCommand::SUBSCRIBE)
.value("UNSUBSCRIBE", LiveDataCommand::UNSUBSCRIBE)
.value("RESPONSE", LiveDataCommand::RESPONSE)
.value("CLEAR_ALL", LiveDataCommand::CLEAR_ALL)
.value("SET_VALUE", LiveDataCommand::SET_VALUE)
.finalize();
// LiveDataStatus enum
pybind11::native_enum<LiveDataStatus>(m, "LiveDataStatus", "enum.IntEnum")
.value("SUCCESS", LiveDataStatus::SUCCESS)
.value("ERR_UNKNOWN_COMMAND", LiveDataStatus::ERR_UNKNOWN_COMMAND)
.value("ERR_HANDLE", LiveDataStatus::ERR_HANDLE)
.value("ERR_DUPLICATE", LiveDataStatus::ERR_DUPLICATE)
.value("ERR_FULL", LiveDataStatus::ERR_FULL)
.finalize();
// LiveDataObjectType enum
pybind11::enum_<LiveDataObjectType>(m, "LiveDataObjectType")
.value("MISC", LiveDataObjectType::MISC)
.value("SNA", LiveDataObjectType::SNA)
.export_values();
// LiveDataValueType enum
pybind11::native_enum<LiveDataValueType>(m, "LiveDataValueType", "enum.IntEnum")
.value("GPS_LATITUDE", LiveDataValueType::GPS_LATITUDE)
.value("GPS_LONGITUDE", LiveDataValueType::GPS_LONGITUDE)
.value("GPS_ALTITUDE", LiveDataValueType::GPS_ALTITUDE)
.value("GPS_SPEED", LiveDataValueType::GPS_SPEED)
.value("GPS_VALID", LiveDataValueType::GPS_VALID)
.value("GPS_ENABLE", LiveDataValueType::GPS_ENABLE)
.value("MANUAL_TRIGGER", LiveDataValueType::MANUAL_TRIGGER)
.value("TIME_SINCE_MSG", LiveDataValueType::TIME_SINCE_MSG)
.value("GPS_ACCURACY", LiveDataValueType::GPS_ACCURACY)
.value("GPS_BEARING", LiveDataValueType::GPS_BEARING)
.value("GPS_TIME", LiveDataValueType::GPS_TIME)
.value("GPS_TIME_VALID", LiveDataValueType::GPS_TIME_VALID)
.value("DAQ_ENABLE", LiveDataValueType::DAQ_ENABLE)
.finalize();
// LiveDataUtil namespace functions
m.def("get_new_handle", &LiveDataUtil::getNewHandle,
"Generate a new unique LiveData handle");
m.def("livedata_value_to_double", &LiveDataUtil::liveDataValueToDouble,
pybind11::arg("val"),
"Convert LiveDataValue to double (32.32 fixed-point to floating-point)");
m.def("livedata_double_to_value", &LiveDataUtil::liveDataDoubleToValue,
pybind11::arg("d"),
"Convert double to LiveDataValue (32.32 fixed-point format). Returns LiveDataValue or None on failure.");
}
} // namespace icsneo
@@ -18,6 +18,17 @@ void init_ethernetmessage(pybind11::module_& m) {
.def_readwrite("fcs", &EthernetMessage::fcs) .def_readwrite("fcs", &EthernetMessage::fcs)
.def_readwrite("frameTooShort", &EthernetMessage::frameTooShort) .def_readwrite("frameTooShort", &EthernetMessage::frameTooShort)
.def_readwrite("noPadding", &EthernetMessage::noPadding) .def_readwrite("noPadding", &EthernetMessage::noPadding)
.def_readwrite("fcsVerified", &EthernetMessage::fcsVerified)
.def_readwrite("txAborted", &EthernetMessage::txAborted)
.def_readwrite("crcError", &EthernetMessage::crcError)
.def_readwrite("isT1S", &EthernetMessage::isT1S)
.def_readwrite("isT1SSymbol", &EthernetMessage::isT1SSymbol)
.def_readwrite("isT1SBurst", &EthernetMessage::isT1SBurst)
.def_readwrite("txCollision", &EthernetMessage::txCollision)
.def_readwrite("isT1SWake", &EthernetMessage::isT1SWake)
.def_readwrite("t1sNodeId", &EthernetMessage::t1sNodeId)
.def_readwrite("t1sBurstCount", &EthernetMessage::t1sBurstCount)
.def_readwrite("t1sSymbolType", &EthernetMessage::t1sSymbolType)
.def("get_destination_mac", &EthernetMessage::getDestinationMAC, pybind11::return_value_policy::reference) .def("get_destination_mac", &EthernetMessage::getDestinationMAC, pybind11::return_value_policy::reference)
.def("get_source_mac", &EthernetMessage::getSourceMAC, pybind11::return_value_policy::reference) .def("get_source_mac", &EthernetMessage::getSourceMAC, pybind11::return_value_policy::reference)
.def("get_ether_type", &EthernetMessage::getEtherType); .def("get_ether_type", &EthernetMessage::getEtherType);
@@ -0,0 +1,50 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>
#include <pybind11/chrono.h>
#include "icsneo/communication/message/livedatamessage.h"
namespace icsneo {
void init_livedatamessage(pybind11::module_& m) {
// LiveDataMessage base class
pybind11::classh<LiveDataMessage, RawMessage>(m, "LiveDataMessage")
.def(pybind11::init<>())
.def_readwrite("handle", &LiveDataMessage::handle)
.def_readwrite("cmd", &LiveDataMessage::cmd);
// LiveDataCommandMessage (for subscribe/unsubscribe)
pybind11::classh<LiveDataCommandMessage, LiveDataMessage>(m, "LiveDataCommandMessage")
.def(pybind11::init<>())
.def_readwrite("update_period", &LiveDataCommandMessage::updatePeriod)
.def_readwrite("expiration_time", &LiveDataCommandMessage::expirationTime)
.def_readwrite("args", &LiveDataCommandMessage::args)
.def("append_signal_arg", &LiveDataCommandMessage::appendSignalArg,
pybind11::arg("value_type"),
"Append a signal argument to the command message");
// LiveDataValueMessage (received values)
pybind11::classh<LiveDataValueMessage, LiveDataMessage>(m, "LiveDataValueMessage")
.def(pybind11::init<>())
.def_readwrite("num_args", &LiveDataValueMessage::numArgs)
.def_readwrite("values", &LiveDataValueMessage::values);
// LiveDataStatusMessage (status responses)
pybind11::classh<LiveDataStatusMessage, LiveDataMessage>(m, "LiveDataStatusMessage")
.def(pybind11::init<>())
.def_readwrite("requested_command", &LiveDataStatusMessage::requestedCommand)
.def_readwrite("status", &LiveDataStatusMessage::status);
// LiveDataSetValueMessage (for setting values)
pybind11::classh<LiveDataSetValueMessage, LiveDataMessage>(m, "LiveDataSetValueMessage")
.def(pybind11::init<>())
.def_readwrite("args", &LiveDataSetValueMessage::args)
.def_readwrite("values", &LiveDataSetValueMessage::values)
.def("append_set_value", &LiveDataSetValueMessage::appendSetValue,
pybind11::arg("value_type"),
pybind11::arg("value"),
"Append a value to set in the message");
}
} // namespace icsneo
+1 -1
View File
@@ -111,7 +111,7 @@ void init_chipid(pybind11::module_& m) {
.value("RAD_GALAXY_2_ZMPCHIP_ID", ChipID::RAD_GALAXY_2_ZMPCHIP_ID) .value("RAD_GALAXY_2_ZMPCHIP_ID", ChipID::RAD_GALAXY_2_ZMPCHIP_ID)
.value("NewDevice59_MCHIP", ChipID::NewDevice59_MCHIP) .value("NewDevice59_MCHIP", ChipID::NewDevice59_MCHIP)
.value("RADMoon2_Z7010_ZYNQ", ChipID::RADMoon2_Z7010_ZYNQ) .value("RADMoon2_Z7010_ZYNQ", ChipID::RADMoon2_Z7010_ZYNQ)
.value("neoVIFIRE2_CORE_SG4", ChipID::neoVIFIRE2_CORE_SG4) .value("neoVIFIRE2_Core_SG4", ChipID::neoVIFIRE2_Core_SG4)
.value("RADBMS_MCHIP", ChipID::RADBMS_MCHIP) .value("RADBMS_MCHIP", ChipID::RADBMS_MCHIP)
.value("RADMoon2_ZL_MCHIP", ChipID::RADMoon2_ZL_MCHIP) .value("RADMoon2_ZL_MCHIP", ChipID::RADMoon2_ZL_MCHIP)
.value("RADGigastar_USBZ_Z7010_ZYNQ", ChipID::RADGigastar_USBZ_Z7010_ZYNQ) .value("RADGigastar_USBZ_Z7010_ZYNQ", ChipID::RADGigastar_USBZ_Z7010_ZYNQ)
@@ -5,6 +5,7 @@
#include "icsneo/device/device.h" #include "icsneo/device/device.h"
#include "icsneo/device/extensions/deviceextension.h" #include "icsneo/device/extensions/deviceextension.h"
#include "icsneo/disk/diskdetails.h"
#include <fstream> #include <fstream>
@@ -52,11 +53,21 @@ void init_device(pybind11::module_& m) {
.def("start_script", &Device::startScript, pybind11::call_guard<pybind11::gil_scoped_release>()) .def("start_script", &Device::startScript, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("stop_script", &Device::stopScript, pybind11::call_guard<pybind11::gil_scoped_release>()) .def("stop_script", &Device::stopScript, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("supports_tc10", &Device::supportsTC10) .def("supports_tc10", &Device::supportsTC10)
.def("supports_live_data", &Device::supportsLiveData)
.def("subscribe_live_data", &Device::subscribeLiveData, pybind11::arg("message"), pybind11::call_guard<pybind11::gil_scoped_release>())
.def("unsubscribe_live_data", &Device::unsubscribeLiveData, pybind11::arg("handle"), pybind11::call_guard<pybind11::gil_scoped_release>())
.def("clear_all_live_data", &Device::clearAllLiveData, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_value_live_data", &Device::setValueLiveData, pybind11::arg("message"), 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("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("send_eth_phy_msg", &Device::sendEthPhyMsg, pybind11::arg("message"), pybind11::arg("timeout") = std::chrono::milliseconds(50), pybind11::call_guard<pybind11::gil_scoped_release>()) .def("send_eth_phy_msg", &Device::sendEthPhyMsg, pybind11::arg("message"), pybind11::arg("timeout") = std::chrono::milliseconds(50), pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_chip_versions", &Device::getChipVersions, pybind11::arg("refreshComponents") = true, pybind11::call_guard<pybind11::gil_scoped_release>()) .def("get_chip_versions", &Device::getChipVersions, pybind11::arg("refreshComponents") = true, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("supports_disk_formatting", &Device::supportsDiskFormatting, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_disk_count", &Device::getDiskCount, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_disk_details", &Device::getDiskDetails, pybind11::arg("timeout") = std::chrono::milliseconds(100), pybind11::call_guard<pybind11::gil_scoped_release>())
.def("force_disk_config_update", &Device::forceDiskConfigUpdate, pybind11::arg("config"), pybind11::call_guard<pybind11::gil_scoped_release>())
.def("format_disk", [](Device& device, const DiskDetails& config) -> bool { return device.formatDisk(config); }, pybind11::arg("config"), pybind11::call_guard<pybind11::gil_scoped_release>())
.def_readonly("settings", &Device::settings); .def_readonly("settings", &Device::settings);
} }
@@ -12,7 +12,6 @@ namespace icsneo {
struct DeviceSettingsNamespace { struct DeviceSettingsNamespace {
using EthLinkMode = AELinkMode; using EthLinkMode = AELinkMode;
using LinkSpeed = EthLinkSpeed;
}; };
void init_idevicesettings(pybind11::module_& m) { void init_idevicesettings(pybind11::module_& m) {
@@ -23,24 +22,95 @@ void init_idevicesettings(pybind11::module_& m) {
.value("Slave", DeviceSettingsNamespace::EthLinkMode::AE_LINK_SLAVE) .value("Slave", DeviceSettingsNamespace::EthLinkMode::AE_LINK_SLAVE)
.value("Master", DeviceSettingsNamespace::EthLinkMode::AE_LINK_MASTER); .value("Master", DeviceSettingsNamespace::EthLinkMode::AE_LINK_MASTER);
pybind11::enum_<DeviceSettingsNamespace::LinkSpeed>(settings, "EthernetLinkSpeed") pybind11::enum_<EthPhyLinkMode>(settings, "PhyLinkMode")
.value("Speed10M", DeviceSettingsNamespace::LinkSpeed::ETH_SPEED_10) .value("ETH_LINK_MODE_AUTO_NEGOTIATION", ETH_LINK_MODE_AUTO_NEGOTIATION)
.value("Speed100M", DeviceSettingsNamespace::LinkSpeed::ETH_SPEED_100) .value("ETH_LINK_MODE_10MBPS_HALFDUPLEX", ETH_LINK_MODE_10MBPS_HALFDUPLEX)
.value("Speed1G", DeviceSettingsNamespace::LinkSpeed::ETH_SPEED_1000) .value("ETH_LINK_MODE_10MBPS_FULLDUPLEX", ETH_LINK_MODE_10MBPS_FULLDUPLEX)
.value("Speed2_5G", DeviceSettingsNamespace::LinkSpeed::ETH_SPEED_2500) .value("ETH_LINK_MODE_100MBPS_HALFDUPLEX", ETH_LINK_MODE_100MBPS_HALFDUPLEX)
.value("Speed5G", DeviceSettingsNamespace::LinkSpeed::ETH_SPEED_5000) .value("ETH_LINK_MODE_100MBPS_FULLDUPLEX", ETH_LINK_MODE_100MBPS_FULLDUPLEX)
.value("Speed10G", DeviceSettingsNamespace::LinkSpeed::ETH_SPEED_10000); .value("ETH_LINK_MODE_1GBPS_HALFDUPLEX", ETH_LINK_MODE_1GBPS_HALFDUPLEX)
.value("ETH_LINK_MODE_1GBPS_FULLDUPLEX", ETH_LINK_MODE_1GBPS_FULLDUPLEX)
.value("ETH_LINK_MODE_2_5GBPS_FULLDUPLEX", ETH_LINK_MODE_2_5GBPS_FULLDUPLEX)
.value("ETH_LINK_MODE_5GBPS_FULLDUPLEX", ETH_LINK_MODE_5GBPS_FULLDUPLEX)
.value("ETH_LINK_MODE_10GBPS_FULLDUPLEX", ETH_LINK_MODE_10GBPS_FULLDUPLEX);
pybind11::enum_<LINMode>(settings, "LINMode")
.value("Sleep", LINMode::SLEEP_MODE)
.value("Slow", LINMode::SLOW_MODE)
.value("Normal", LINMode::NORMAL_MODE)
.value("Fast", LINMode::FAST_MODE);
pybind11::enum_<MiscIOAnalogVoltage>(settings, "MiscIOAnalogVoltage")
.value("V0", MiscIOAnalogVoltage::V0)
.value("V1", MiscIOAnalogVoltage::V1)
.value("V2", MiscIOAnalogVoltage::V2)
.value("V3", MiscIOAnalogVoltage::V3)
.value("V4", MiscIOAnalogVoltage::V4)
.value("V5", MiscIOAnalogVoltage::V5);
pybind11::classh<IDeviceSettings>(m, "IDeviceSettings") pybind11::classh<IDeviceSettings>(m, "IDeviceSettings")
.def("apply", &IDeviceSettings::apply, pybind11::arg("temporary") = 0, pybind11::call_guard<pybind11::gil_scoped_release>()) .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("apply_defaults", &IDeviceSettings::applyDefaults, pybind11::arg("temporary") = 0, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("refresh", &IDeviceSettings::refresh, pybind11::call_guard<pybind11::gil_scoped_release>())
// Baudrate methods
.def("get_baudrate", &IDeviceSettings::getBaudrateFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_baudrate", &IDeviceSettings::setBaudrateFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_fd_baudrate", &IDeviceSettings::getFDBaudrateFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_fd_baudrate", &IDeviceSettings::setFDBaudrateFor, pybind11::call_guard<pybind11::gil_scoped_release>())
// Termination methods
.def("is_termination_supported", &IDeviceSettings::isTerminationSupportedFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("can_termination_be_enabled", &IDeviceSettings::canTerminationBeEnabledFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("is_termination_enabled", &IDeviceSettings::isTerminationEnabledFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_termination", &IDeviceSettings::setTerminationFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_termination_groups", &IDeviceSettings::getTerminationGroups, pybind11::call_guard<pybind11::gil_scoped_release>())
// LIN methods
.def("is_commander_resistor_enabled", &IDeviceSettings::isCommanderResistorEnabledFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_commander_resistor", &IDeviceSettings::setCommanderResistorFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_lin_mode", &IDeviceSettings::getLINModeFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_lin_mode", &IDeviceSettings::setLINModeFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_lin_commander_response_time", &IDeviceSettings::getLINCommanderResponseTimeFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_lin_commander_response_time", &IDeviceSettings::setLINCommanderResponseTimeFor, pybind11::call_guard<pybind11::gil_scoped_release>())
// Ethernet PHY methods (index-based for switch devices)
.def("get_phy_enable", &IDeviceSettings::getPhyEnable, 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_mode", &IDeviceSettings::getPhyMode, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_phy_speed", &IDeviceSettings::getPhySpeed, 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_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_mode", &IDeviceSettings::setPhyMode, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_phy_speed", &IDeviceSettings::setPhySpeed, 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::call_guard<pybind11::gil_scoped_release>());
// Ethernet PHY methods (network-based for multi-interface devices)
.def("get_phy_enable_for", &IDeviceSettings::getPhyEnableFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_phy_role_for", &IDeviceSettings::getPhyRoleFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_phy_link_mode_for", &IDeviceSettings::getPhyLinkModeFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_phy_enable_for", &IDeviceSettings::setPhyEnableFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_phy_role_for", &IDeviceSettings::setPhyRoleFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_phy_link_mode_for", &IDeviceSettings::setPhyLinkModeFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_supported_phy_link_modes_for", &IDeviceSettings::getSupportedPhyLinkModesFor, pybind11::call_guard<pybind11::gil_scoped_release>())
// 10BASE-T1S methods
.def("is_t1s_plca_enabled", &IDeviceSettings::isT1SPLCAEnabledFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_t1s_plca", &IDeviceSettings::setT1SPLCAFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_t1s_local_id", &IDeviceSettings::getT1SLocalIDFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_t1s_local_id", &IDeviceSettings::setT1SLocalIDFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_t1s_max_nodes", &IDeviceSettings::getT1SMaxNodesFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_t1s_max_nodes", &IDeviceSettings::setT1SMaxNodesFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_t1s_tx_opp_timer", &IDeviceSettings::getT1STxOppTimerFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_t1s_tx_opp_timer", &IDeviceSettings::setT1STxOppTimerFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_t1s_max_burst", &IDeviceSettings::getT1SMaxBurstFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_t1s_max_burst", &IDeviceSettings::setT1SMaxBurstFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_t1s_burst_timer", &IDeviceSettings::getT1SBurstTimerFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_t1s_burst_timer", &IDeviceSettings::setT1SBurstTimerFor, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_misc_io_analog_output_enabled", &IDeviceSettings::setMiscIOAnalogOutputEnabled, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_misc_io_analog_output", &IDeviceSettings::setMiscIOAnalogOutput, pybind11::call_guard<pybind11::gil_scoped_release>())
// Status properties
.def_readonly("disabled", &IDeviceSettings::disabled)
.def_readonly("readonly", &IDeviceSettings::readonly);
} }
} // namespace icsneo } // namespace icsneo
@@ -0,0 +1,29 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "icsneo/disk/diskdetails.h"
namespace icsneo {
void init_diskdetails(pybind11::module_& m) {
pybind11::enum_<DiskLayout>(m, "DiskLayout")
.value("Spanned", DiskLayout::Spanned)
.value("RAID0", DiskLayout::RAID0);
pybind11::classh<DiskInfo>(m, "DiskInfo")
.def(pybind11::init())
.def_readwrite("present", &DiskInfo::present)
.def_readwrite("initialized", &DiskInfo::initialized)
.def_readwrite("formatted", &DiskInfo::formatted)
.def_readwrite("sectors", &DiskInfo::sectors)
.def_readwrite("bytes_per_sector", &DiskInfo::bytesPerSector)
.def("size", &DiskInfo::size);
pybind11::classh<DiskDetails>(m, "DiskDetails")
.def(pybind11::init())
.def_readwrite("layout", &DiskDetails::layout)
.def_readwrite("full_format", &DiskDetails::fullFormat)
.def_readwrite("disks", &DiskDetails::disks);
}
} // namespace icsneo
+5 -1
View File
@@ -64,6 +64,7 @@ void init_extension(pybind11::classh<FlexRayNamespace>& c) {
.def_readwrite("accept_startup_range_microticks", &Controller::Configuration::AcceptStartupRangeMicroticks) .def_readwrite("accept_startup_range_microticks", &Controller::Configuration::AcceptStartupRangeMicroticks)
.def_readwrite("allow_passive_to_active_cycle_pairs", &Controller::Configuration::AllowPassiveToActiveCyclePairs) .def_readwrite("allow_passive_to_active_cycle_pairs", &Controller::Configuration::AllowPassiveToActiveCyclePairs)
.def_readwrite("cluster_drift_damping", &Controller::Configuration::ClusterDriftDamping) .def_readwrite("cluster_drift_damping", &Controller::Configuration::ClusterDriftDamping)
.def_readwrite("allow_halt_due_to_clock", &Controller::Configuration::AllowHaltDueToClock)
.def_readwrite("channel_a", &Controller::Configuration::ChannelA) .def_readwrite("channel_a", &Controller::Configuration::ChannelA)
.def_readwrite("channel_b", &Controller::Configuration::ChannelB) .def_readwrite("channel_b", &Controller::Configuration::ChannelB)
.def_readwrite("decoding_correction_microticks", &Controller::Configuration::DecodingCorrectionMicroticks) .def_readwrite("decoding_correction_microticks", &Controller::Configuration::DecodingCorrectionMicroticks)
@@ -74,6 +75,7 @@ void init_extension(pybind11::classh<FlexRayNamespace>& c) {
.def_readwrite("extern_offset_correction_microticks", &Controller::Configuration::ExternOffsetCorrectionMicroticks) .def_readwrite("extern_offset_correction_microticks", &Controller::Configuration::ExternOffsetCorrectionMicroticks)
.def_readwrite("extern_rate_correction_microticks", &Controller::Configuration::ExternRateCorrectionMicroticks) .def_readwrite("extern_rate_correction_microticks", &Controller::Configuration::ExternRateCorrectionMicroticks)
.def_readwrite("key_slot_id", &Controller::Configuration::KeySlotID) .def_readwrite("key_slot_id", &Controller::Configuration::KeySlotID)
.def_readwrite("key_slot_only_enabled", &Controller::Configuration::KeySlotOnlyEnabled)
.def_readwrite("key_slot_used_for_startup", &Controller::Configuration::KeySlotUsedForStartup) .def_readwrite("key_slot_used_for_startup", &Controller::Configuration::KeySlotUsedForStartup)
.def_readwrite("key_slot_used_for_sync", &Controller::Configuration::KeySlotUsedForSync) .def_readwrite("key_slot_used_for_sync", &Controller::Configuration::KeySlotUsedForSync)
.def_readwrite("latest_tx_minislot", &Controller::Configuration::LatestTxMinislot) .def_readwrite("latest_tx_minislot", &Controller::Configuration::LatestTxMinislot)
@@ -114,6 +116,7 @@ void init_extension(pybind11::classh<FlexRayNamespace>& c) {
.def_readwrite("action_point_offset", &Cluster::Configuration::ActionPointOffset) .def_readwrite("action_point_offset", &Cluster::Configuration::ActionPointOffset)
.def_readwrite("casr_x_low_max", &Cluster::Configuration::CASRxLowMax) .def_readwrite("casr_x_low_max", &Cluster::Configuration::CASRxLowMax)
.def_readwrite("cold_start_attempts", &Cluster::Configuration::ColdStartAttempts) .def_readwrite("cold_start_attempts", &Cluster::Configuration::ColdStartAttempts)
.def_readwrite("cycle_duration_micro_sec", &Cluster::Configuration::CycleDurationMicroSec)
.def_readwrite("dynamic_slot_idle_phase_minislots", &Cluster::Configuration::DynamicSlotIdlePhaseMinislots) .def_readwrite("dynamic_slot_idle_phase_minislots", &Cluster::Configuration::DynamicSlotIdlePhaseMinislots)
.def_readwrite("listen_noise_macroticks", &Cluster::Configuration::ListenNoiseMacroticks) .def_readwrite("listen_noise_macroticks", &Cluster::Configuration::ListenNoiseMacroticks)
.def_readwrite("macroticks_per_cycle", &Cluster::Configuration::MacroticksPerCycle) .def_readwrite("macroticks_per_cycle", &Cluster::Configuration::MacroticksPerCycle)
@@ -159,7 +162,8 @@ void init_flexraymessage(pybind11::module_& m) {
.def_readwrite("sync_frame", &FlexRayMessage::sync) .def_readwrite("sync_frame", &FlexRayMessage::sync)
.def_readwrite("startup_frame", &FlexRayMessage::startup) .def_readwrite("startup_frame", &FlexRayMessage::startup)
.def_readwrite("dynamic_frame", &FlexRayMessage::dynamic) .def_readwrite("dynamic_frame", &FlexRayMessage::dynamic)
.def_readwrite("cycle", &FlexRayMessage::cycle); .def_readwrite("cycle", &FlexRayMessage::cycle)
.def_readwrite("cycle_repetition", &FlexRayMessage::cycleRepetition);
//// TODO: Eliminate FlexRayControlMessage class references in controller class and eliminate getStatus function in bindings //// TODO: Eliminate FlexRayControlMessage class references in controller class and eliminate getStatus function in bindings
} }
+6
View File
@@ -25,6 +25,7 @@ void init_ethernetstatusmessage(pybind11::module_&);
void init_macsecconfig(pybind11::module_&); void init_macsecconfig(pybind11::module_&);
void init_scriptstatusmessage(pybind11::module_&); void init_scriptstatusmessage(pybind11::module_&);
void init_diskdriver(pybind11::module_&); void init_diskdriver(pybind11::module_&);
void init_diskdetails(pybind11::module_&);
void init_deviceextension(pybind11::module_&); void init_deviceextension(pybind11::module_&);
void init_chipid(pybind11::module_&); void init_chipid(pybind11::module_&);
void init_versionreport(pybind11::module_&); void init_versionreport(pybind11::module_&);
@@ -35,6 +36,8 @@ void init_version(pybind11::module_&);
void init_flexray(pybind11::module_& m); void init_flexray(pybind11::module_& m);
void init_idevicesettings(pybind11::module_&); void init_idevicesettings(pybind11::module_&);
void init_ethphymessage(pybind11::module_&); void init_ethphymessage(pybind11::module_&);
void init_livedata(pybind11::module_&);
void init_livedatamessage(pybind11::module_&);
PYBIND11_MODULE(icsneopy, m) { PYBIND11_MODULE(icsneopy, m) {
pybind11::options options; pybind11::options options;
@@ -48,6 +51,7 @@ PYBIND11_MODULE(icsneopy, m) {
init_devicetype(m); init_devicetype(m);
init_network(m); init_network(m);
init_io(m); init_io(m);
init_livedata(m);
init_message(m); init_message(m);
init_canmessage(m); init_canmessage(m);
init_canerrormessage(m); init_canerrormessage(m);
@@ -60,9 +64,11 @@ PYBIND11_MODULE(icsneopy, m) {
init_macsecconfig(m); init_macsecconfig(m);
init_scriptstatusmessage(m); init_scriptstatusmessage(m);
init_spimessage(m); init_spimessage(m);
init_livedatamessage(m);
init_messagefilter(m); init_messagefilter(m);
init_messagecallback(m); init_messagecallback(m);
init_diskdriver(m); init_diskdriver(m);
init_diskdetails(m);
init_flexray(m); init_flexray(m);
init_ethphymessage(m); init_ethphymessage(m);
init_chipid(m); init_chipid(m);
+6 -5
View File
@@ -19,7 +19,8 @@ double liveDataValueToDouble(const LiveDataValue& val) {
return val.value * liveDataFixedPointToDouble; return val.value * liveDataFixedPointToDouble;
} }
bool liveDataDoubleToValue(const double& dFloat, LiveDataValue& value) { std::optional<LiveDataValue> liveDataDoubleToValue(const double& dFloat) {
LiveDataValue value;
union { union {
struct struct
{ {
@@ -56,23 +57,23 @@ bool liveDataDoubleToValue(const double& dFloat, LiveDataValue& value) {
value.value = CminiFixedPt.ValueLarge; value.value = CminiFixedPt.ValueLarge;
if(dFloat == (double)0.0) if(dFloat == (double)0.0)
return true; return value;
//check if double can be stored as 32.32 //check if double can be stored as 32.32
// 0x1 0000 0000 0000 0000 * CM_FIXED_POINT_TO_DOUBLEVALUE = 0x1 0000 0000 // 0x1 0000 0000 0000 0000 * CM_FIXED_POINT_TO_DOUBLEVALUE = 0x1 0000 0000
if(dFloat > INT32_MAX_DOUBLE || dFloat < INT32_MIN_DOUBLE) { if(dFloat > INT32_MAX_DOUBLE || dFloat < INT32_MIN_DOUBLE) {
EventManager::GetInstance().add(APIEvent::Type::FixedPointOverflow, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::FixedPointOverflow, APIEvent::Severity::Error);
return false; return std::nullopt;
} }
// Use absolute value for minimum fixed point check // Use absolute value for minimum fixed point check
double absFloat = (dFloat < 0.0) ? -dFloat : dFloat; double absFloat = (dFloat < 0.0) ? -dFloat : dFloat;
if(absFloat < MIN_FIXED_POINT_DOUBLE) { if(absFloat < MIN_FIXED_POINT_DOUBLE) {
EventManager::GetInstance().add(APIEvent::Type::FixedPointPrecision, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::FixedPointPrecision, APIEvent::Severity::Error);
return false; return std::nullopt;
} }
return true; return value;
} }
} // namespace LiveDataUtil } // namespace LiveDataUtil
+21 -4
View File
@@ -1,5 +1,5 @@
#include "icsneo/communication/packet/ethernetpacket.h" #include "icsneo/communication/packet/ethernetpacket.h"
#include <algorithm> // for std::copy #include <algorithm>
#include <iostream> #include <iostream>
using namespace icsneo; using namespace icsneo;
@@ -10,16 +10,17 @@ std::shared_ptr<EthernetMessage> HardwareEthernetPacket::DecodeToMessage(const s
// Make sure we have enough to read the packet length first // Make sure we have enough to read the packet length first
if(bytestream.size() < sizeof(HardwareEthernetPacket)) if(bytestream.size() < sizeof(HardwareEthernetPacket))
return nullptr; 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 fcsSize = packet->header.FCS_AVAIL ? 4 : 0;
// Ensure Length is sufficient for FCS extraction to avoid invalid iterator arithmetic
if(packet->Length < fcsSize)
return nullptr;
const size_t bytestreamExpectedSize = sizeof(HardwareEthernetPacket) + packet->Length; const size_t bytestreamExpectedSize = sizeof(HardwareEthernetPacket) + packet->Length;
const size_t bytestreamActualSize = bytestream.size(); const size_t bytestreamActualSize = bytestream.size();
if(bytestreamActualSize < bytestreamExpectedSize) if(bytestreamActualSize < bytestreamExpectedSize)
return nullptr; return nullptr;
auto messagePtr = std::make_shared<EthernetMessage>(); auto messagePtr = std::make_shared<EthernetMessage>();
EthernetMessage& message = *messagePtr; EthernetMessage& message = *messagePtr;
// Standard Ethernet fields
message.transmitted = packet->eid.TXMSG; message.transmitted = packet->eid.TXMSG;
if(message.transmitted) if(message.transmitted)
message.description = packet->stats; message.description = packet->stats;
@@ -27,12 +28,28 @@ std::shared_ptr<EthernetMessage> HardwareEthernetPacket::DecodeToMessage(const s
if(message.preemptionEnabled) if(message.preemptionEnabled)
message.preemptionFlags = (uint8_t)((rawWords[0] & 0x03F8) >> 4); message.preemptionFlags = (uint8_t)((rawWords[0] & 0x03F8) >> 4);
message.frameTooShort = packet->header.RUNT_FRAME; message.frameTooShort = packet->header.RUNT_FRAME;
message.noPadding = !packet->header.ENABLE_PADDING;
message.fcsVerified = packet->header.FCS_VERIFIED;
message.txAborted = packet->eid.TXAborted;
message.crcError = packet->header.CRC_ERROR;
if(message.frameTooShort) if(message.frameTooShort)
message.error = true; message.error = true;
// This timestamp is raw off the device (in timestampResolution increments) // This timestamp is raw off the device (in timestampResolution increments)
// Decoder will fix as it has information about the timestampResolution increments // Decoder will fix as it has information about the timestampResolution increments
message.timestamp = packet->timestamp.TS; message.timestamp = packet->timestamp.TS;
// Check if this is a T1S packet and populate T1S-specific fields
message.isT1S = packet->header.T1S_ETHERNET;
if(message.isT1S) {
message.isT1SSymbol = packet->eid.T1S_SYMBOL;
message.isT1SBurst = packet->eid.T1S_BURST;
message.txCollision = packet->t1s_status.TXCollision;
message.isT1SWake = packet->t1s_status.T1SWake;
message.t1sNodeId = packet->t1s_node.T1S_NODE_ID;
message.t1sBurstCount = packet->t1s_node.T1S_BURST_COUNT;
}
const std::vector<uint8_t>::const_iterator databegin = bytestream.begin() + sizeof(HardwareEthernetPacket); const std::vector<uint8_t>::const_iterator databegin = bytestream.begin() + sizeof(HardwareEthernetPacket);
const std::vector<uint8_t>::const_iterator dataend = databegin + packet->Length - fcsSize; const std::vector<uint8_t>::const_iterator dataend = databegin + packet->Length - fcsSize;
message.data.insert(message.data.begin(), databegin, dataend); message.data.insert(message.data.begin(), databegin, dataend);
+29 -1
View File
@@ -144,7 +144,7 @@ typedef union _MACSecSa
{ {
uint8_t index; /*!< SA index */ uint8_t index; /*!< SA index */
uint8_t uint8_t
sak[32]; /*!< 256b SAK: Define the encryption key to be used to encrypte this packet. The lower 128 bits are used for 128-bit ciphers. */ sak[32]; /*!< SAK: All 32 bytes are written to the firmware. For AES-128 the firmware requires bytes [0..15] == bytes [16..31] (mirrored); serialize() handles this automatically. */
uint8_t hashKey[16]; /*!< 128b Hash Key: Key used for authentication. */ uint8_t hashKey[16]; /*!< 128b Hash Key: Key used for authentication. */
uint8_t salt[12]; /*!< 96b Salt value: Salt value used in XPN ciphers. */ uint8_t salt[12]; /*!< 96b Salt value: Salt value used in XPN ciphers. */
uint32_t ssci; /*!< 32b SSCI value: Short Secure Channel Identifier, used in XPN ciphers. */ uint32_t ssci; /*!< 32b SSCI value: Short Secure Channel Identifier, used in XPN ciphers. */
@@ -740,6 +740,34 @@ std::vector<uint8_t> MACsecConfig::serialize() const {
} }
} }
// AES-128 SAK normalization: the firmware expects the 16-byte SAK mirrored
// into both halves of the 32-byte hardware SAK field. Callers only populate
// bytes [0..15]; copy them into [16..31] here, transparent to all callers.
for(uint8_t i = 0; i < static_cast<uint8_t>(rxSecY.size()); i++) {
if(rxSecY[i].cipher == MACsecCipherSuite::GcmAes128 || rxSecY[i].cipher == MACsecCipherSuite::GcmAes128Xpn) {
uint8_t primaryIdx = rxSecYSaIndices[i].first;
if(primaryIdx < maxSa)
memcpy(hwSettings->macsec.rx.sa[primaryIdx].sak + 16, hwSettings->macsec.rx.sa[primaryIdx].sak, 16);
if(rxSecYRekey[i]) {
uint8_t rekeyIdx = rxSecYSaIndices[i].second;
if(rekeyIdx < maxSa)
memcpy(hwSettings->macsec.rx.sa[rekeyIdx].sak + 16, hwSettings->macsec.rx.sa[rekeyIdx].sak, 16);
}
}
}
for(uint8_t i = 0; i < static_cast<uint8_t>(txSecY.size()); i++) {
if(txSecY[i].cipher == MACsecCipherSuite::GcmAes128 || txSecY[i].cipher == MACsecCipherSuite::GcmAes128Xpn) {
uint8_t primaryIdx = txSecYSaIndices[i].first;
if(primaryIdx < maxSa)
memcpy(hwSettings->macsec.tx.sa[primaryIdx].sak + 16, hwSettings->macsec.tx.sa[primaryIdx].sak, 16);
if(txSecYRekey[i]) {
uint8_t rekeyIdx = txSecYSaIndices[i].second;
if(rekeyIdx < maxSa)
memcpy(hwSettings->macsec.tx.sa[rekeyIdx].sak + 16, hwSettings->macsec.tx.sa[rekeyIdx].sak, 16);
}
}
}
if(rxRule.size() == 0) { if(rxRule.size() == 0) {
MACsecRxRule defaultRule; MACsecRxRule defaultRule;
MACSecRule_t* hwRxRule = &hwSettings->macsec.rx.rule[0]; MACSecRule_t* hwRxRule = &hwSettings->macsec.rx.rule[0];
+23 -15
View File
@@ -563,24 +563,27 @@ bool Device::goOnline() {
if(supportsNetworkMutex()) { if(supportsNetworkMutex()) {
assignedClientId = com->getClientIDSync(); assignedClientId = com->getClientIDSync();
if(assignedClientId) { if(assignedClientId) {
std::set<Network::NetID> nets;
for(auto&& net : getSupportedTXNetworks()) {
nets.insert(net.getNetID());
}
// firmware supports clientid/mutex // firmware supports clientid/mutex
networkMutexCallbackHandle = lockAllNetworks(std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max(), NetworkMutexType::Shared, [this](std::shared_ptr<Message> message) { networkMutexCallbackHandle = lockNetworks(nets, std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max(), NetworkMutexType::Shared, [this](std::shared_ptr<Message> message) {
auto netMutexMsg = std::static_pointer_cast<NetworkMutexMessage>(message); auto netMutexMsg = std::static_pointer_cast<NetworkMutexMessage>(message);
if(netMutexMsg->networks.size() && netMutexMsg->event.has_value()) { if(netMutexMsg->networks.size() && netMutexMsg->event.has_value()) {
switch(*netMutexMsg->event) { switch(*netMutexMsg->event) {
case NetworkMutexEvent::Acquired: case NetworkMutexEvent::Acquired:
lockedNetworks.emplace(*netMutexMsg->networks.begin()); lockedNetworks.emplace(*netMutexMsg->networks.begin());
break; break;
case NetworkMutexEvent::Released: { case NetworkMutexEvent::Released: {
auto it = lockedNetworks.find(*netMutexMsg->networks.begin()); auto it = lockedNetworks.find(*netMutexMsg->networks.begin());
if (it != lockedNetworks.end()) if (it != lockedNetworks.end())
lockedNetworks.erase(it); lockedNetworks.erase(it);
break; break;
}
} }
} }
} }
); });
} }
} }
@@ -610,6 +613,11 @@ bool Device::goOffline() {
return true; return true;
} }
if(assignedClientId.has_value()) {
unlockAllNetworks();
assignedClientId.reset();
}
if(!enableNetworkCommunication(false)) if(!enableNetworkCommunication(false))
return false; return false;
@@ -3809,7 +3817,7 @@ bool Device::formatDisk(const DiskDetails& config, const DiskFormatProgress& han
return com->sendCommand(ExtendedCommand::DiskFormatProgress, {}); return com->sendCommand(ExtendedCommand::DiskFormatProgress, {});
}, },
std::make_shared<ExtendedResponseFilter>(ExtendedCommand::DiskFormatProgress), std::make_shared<ExtendedResponseFilter>(ExtendedCommand::DiskFormatProgress),
std::chrono::milliseconds(200) std::chrono::milliseconds(1000)
); );
if(!response) { if(!response) {
+14
View File
@@ -945,4 +945,18 @@ template<typename T> bool IDeviceSettings::applyStructure(const T& newStructure)
memcpy(settings.data(), &newStructure, structSize); memcpy(settings.data(), &newStructure, structSize);
return apply(); return apply();
}
bool IDeviceSettings::setMiscIOAnalogOutputEnabled(uint8_t pin, bool enabled) {
(void)pin;
(void)enabled;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::Error);
return false;
}
bool IDeviceSettings::setMiscIOAnalogOutput(uint8_t pin, MiscIOAnalogVoltage voltage) {
(void)pin;
(void)voltage;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::Error);
return false;
} }
+1 -1
View File
@@ -12,7 +12,7 @@ subprocess.call('cd ..; doxygen docs/icsneoc/Doxyfile', shell=True)
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = 'libicsneo' project = 'libicsneo'
copyright = '2024-2025, Intrepid Control Systems, Inc.' copyright = '2024-2026, Intrepid Control Systems, Inc.'
author = 'Intrepid Control Systems, Inc.' author = 'Intrepid Control Systems, Inc.'
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------
+17
View File
@@ -27,6 +27,15 @@ Complete CAN Example
:language: python :language: python
LiveData Subscription and Monitoring
=====================================
:download:`Download example <../../examples/python/livedata/livedata_example.py>`
.. literalinclude:: ../../examples/python/livedata/livedata_example.py
:language: python
Transmit Ethernet frames on Ethernet 01 Transmit Ethernet frames on Ethernet 01
======================================== ========================================
@@ -82,3 +91,11 @@ SPI Example for 10BASE-T1S
.. literalinclude:: ../../examples/python/spi/spi_example.py .. literalinclude:: ../../examples/python/spi/spi_example.py
:language: python :language: python
Analog Output Control
=====================
:download:`Download example <../../examples/python/analog_out/analog_out_basic.py>`
.. literalinclude:: ../../examples/python/analog_out/analog_out_basic.py
:language: python
+191
View File
@@ -0,0 +1,191 @@
=======================
FlexRay Getting Started
=======================
Prerequisites
=============
- icsneopy library installed
- FlexRay hardware device connected (e.g., Fire3 Flexray)
- Proper FlexRay bus termination (100Ω on each channel end)
Physical Hardware Setup for Two-Node Testing
---------------------------------------------
For testing the basic transmit and receive examples with a single device:
- Hardware: Device with dual FlexRay controllers (e.g., neoVI FIRE 3 Flexray)
- Connection: FLEXRAY_01 Channel A looped to FLEXRAY_02 Channel A
- Termination: 100Ω termination resistors on both ends of the loopback
- Cable: Use proper FlexRay twisted pair cable (impedance matched)
.. note::
The basic transmit/receive examples are configured for this loopback setup
where both controllers act as coldstart nodes. For use on an existing
FlexRay network, see the passive monitoring configuration notes in the
receive example.
FlexRay Coldstart
-----------------
FlexRay networks require at least one "coldstart node" to initialize the network timing.
The coldstart node is responsible for starting the FlexRay communication cycle.
For a complete standalone coldstart example, see the Additional Examples section below.
Basic Setup
===========
1. Import the library and find FlexRay device:
.. code-block:: python
import icsneopy
devices = icsneopy.find_all_devices()
# Find a device with FlexRay support
device = None
for dev in devices:
if dev.get_extension("FlexRay"):
device = dev
break
if not device:
raise RuntimeError("No FlexRay-capable device found")
2. Configure FlexRay controller:
.. literalinclude:: ../../examples/python/flexray/flexray_transmit_basic.py
:language: python
:lines: 12-111
3. Open device and go online:
.. code-block:: python
if not device.open():
raise RuntimeError("Failed to open device")
if not device.go_online():
raise RuntimeError("Failed to go online")
Transmitting FlexRay Frames
============================
This example demonstrates a coldstart node that initiates a FlexRay network
and transmits simulated sensor data continuously in slot 1.
**Hardware Setup**: FLEXRAY_01 looped to FLEXRAY_02
**Usage**:
1. Start the receive example first
2. Start this transmit example second
3. Network will initialize and frames will be transmitted
.. literalinclude:: ../../examples/python/flexray/flexray_transmit_basic.py
:language: python
:lines: 113-170
Key Configuration Parameters:
- **slotid**: The FlexRay slot ID for transmission (1-2047 for static segment)
- **cycle**: The FlexRay cycle number (0-63)
- **cycle_repetition**: How often the frame repeats (1 = every cycle, 2 = every other cycle)
- **channel**: Transmission channel (A, B, or AB for both)
- **key_slot_id**: Must be unique per node on the network
- **key_slot_used_for_startup**: True for coldstart nodes
- **key_slot_used_for_sync**: True to provide synchronization frames
Receiving FlexRay Frames
=========================
This example demonstrates receiving FlexRay frames on FLEXRAY_02 Channel A.
**Hardware Setup**: FLEXRAY_01 looped to FLEXRAY_02
**Configuration Note**: This example is configured with coldstart capability
for two-node loopback testing. For passive monitoring on an existing FlexRay
network:
1. Set ``key_slot_used_for_startup = False`` in the controller configuration
2. Remove the ``controller.set_allow_coldstart(True)`` call
3. Ensure all cluster parameters match the existing network
4. The node will sync and receive without transmitting
**Usage**:
1. Start this receive example first
2. Start the transmit example second
3. Frames from slot 1 will be displayed with hex and decimal payload views
.. literalinclude:: ../../examples/python/flexray/flexray_receive_basic.py
:language: python
:lines: 103-170
FlexRay Coldstart Configuration
================================
To use the Coldstart example, ensure the following:
Set the Flexray network in neoVI Explorer to Coldstart.
No other nodes should be present on the network during testing.
Nothing connected to Fire3 FlexRay bus.
Critical Coldstart Settings
----------------------------
.. literalinclude:: ../../examples/python/flexray/flexray_coldstart.py
:language: python
:lines: 40-48
Configuration Example:
.. literalinclude:: ../../examples/python/flexray/flexray_coldstart.py
:language: python
:lines: 20-64
Setting Coldstart on Controller:
.. code-block:: python
controller.set_allow_coldstart(True)
controller.set_start_when_going_online(True)
Cleanup and Resource Management
================================
Always close the device when finished:
.. code-block:: python
try:
# Your FlexRay operations here
pass
finally:
device.close()
See the basic transmit and receive examples for complete implementations.
Additional Examples
===================
Transmit Basic
--------------
Complete working example with coldstart node transmitting simulated sensor data.
All example files are available for download:
**Transmit Basic** - Coldstart node transmitting simulated sensor data
:download:`flexray_transmit_basic.py <../../examples/python/flexray/flexray_transmit_basic.py>`
**Receive Basic** - Receiving and displaying FlexRay frames with formatted output
:download:`flexray_receive_basic.py <../../examples/python/flexray/flexray_receive_basic.py>`
**Coldstart** - Standalone coldstart example demonstrating network initialization
:download:`flexray_coldstart.py <../../examples/python/flexray/flexray_coldstart.py>`
+1
View File
@@ -7,6 +7,7 @@ icsneopy
can_getting_started can_getting_started
ethernet_getting_started ethernet_getting_started
flexray_getting_started
examples examples
api api
radepsilon radepsilon
+10
View File
@@ -14,6 +14,8 @@ option(LIBICSNEO_BUILD_CPP_APP_ERROR_EXAMPLE "Build the macsec example" ON)
option(LIBICSNEO_BUILD_CPP_FLEXRAY_EXAMPLE "Build the FlexRay example." ON) option(LIBICSNEO_BUILD_CPP_FLEXRAY_EXAMPLE "Build the FlexRay example." ON)
option(LIBICSNEO_BUILD_CPP_SPI_EXAMPLE "Build the SPI example." ON) option(LIBICSNEO_BUILD_CPP_SPI_EXAMPLE "Build the SPI example." ON)
option(LIBICSNEO_BUILD_CPP_MUTEX_EXAMPLE "Build the NetworkMutex example." ON) option(LIBICSNEO_BUILD_CPP_MUTEX_EXAMPLE "Build the NetworkMutex example." ON)
option(LIBICSNEO_BUILD_CPP_ANALOG_OUT_EXAMPLE "Build the analog output example." ON)
option(LIBICSNEO_BUILD_CPP_DISKFORMAT_EXAMPLE "Build the disk format example." ON)
add_compile_options(${LIBICSNEO_COMPILER_WARNINGS}) add_compile_options(${LIBICSNEO_COMPILER_WARNINGS})
@@ -80,3 +82,11 @@ endif()
if(LIBICSNEO_BUILD_CPP_MUTEX_EXAMPLE) if(LIBICSNEO_BUILD_CPP_MUTEX_EXAMPLE)
add_subdirectory(cpp/mutex) add_subdirectory(cpp/mutex)
endif() endif()
if(LIBICSNEO_BUILD_CPP_ANALOG_OUT_EXAMPLE)
add_subdirectory(cpp/analog_out)
endif()
if(LIBICSNEO_BUILD_CPP_DISKFORMAT_EXAMPLE)
add_subdirectory(cpp/diskformat)
endif()
+2
View File
@@ -0,0 +1,2 @@
add_executable(libicsneocpp-analog-out src/analog_out.cpp)
target_link_libraries(libicsneocpp-analog-out icsneocpp)
+157
View File
@@ -0,0 +1,157 @@
/**
* libicsneo Analog Output example
*
* Demonstrates how to configure and control analog outputs on supported devices
*
* Usage: libicsneo-analog-out <pin> <voltage> [deviceSerial] [--yes]
*
* Arguments:
* pin: Pin number (1-3 for RAD Galaxy)
* voltage: Voltage level (0-5)
* deviceSerial: 6 character string for device serial (optional)
* --yes: Skip confirmation prompt
*/
#include <iostream>
#include <thread>
#include <chrono>
#include <string_view>
#include <cstdlib>
#include "icsneo/icsneocpp.h"
static const std::string usage = "Usage: libicsneo-analog-out <pin> <voltage> [deviceSerial] [--yes]\n\n"
"Arguments:\n"
"pin: Pin number (1-3 for RAD Galaxy)\n"
"voltage: Voltage level (0-5)\n"
"deviceSerial: 6 character string for device serial (optional)\n"
"--yes: Skip confirmation prompt\n";
int main(int argc, const char** argv) {
std::vector<std::string_view> args(argv, argv + argc);
// Parse arguments
if(args.size() < 3) {
std::cerr << "Error: Missing required arguments\n" << std::endl;
std::cerr << usage;
return -1;
}
char* endPtr;
long pinNum = std::strtol(args[1].data(), &endPtr, 10);
if(endPtr != args[1].data() + args[1].size() || pinNum < 1 || pinNum > 3) {
std::cerr << "Error: Invalid pin number (must be 1-3)" << std::endl;
return -1;
}
long voltageLevel = std::strtol(args[2].data(), &endPtr, 10);
if(endPtr != args[2].data() + args[2].size() || voltageLevel < 0 || voltageLevel > 5) {
std::cerr << "Error: Invalid voltage level (must be 0-5)" << std::endl;
return -1;
}
icsneo::MiscIOAnalogVoltage voltage = static_cast<icsneo::MiscIOAnalogVoltage>(voltageLevel);
uint8_t pin = static_cast<uint8_t>(pinNum);
// Check for optional arguments
bool skipConfirm = false;
std::string_view serial;
for(size_t i = 3; i < args.size(); i++) {
if(args[i] == "--yes") {
skipConfirm = true;
} else if(serial.empty() && args[i].size() == 6) {
serial = args[i];
}
}
// Confirmation prompt
if(!skipConfirm) {
std::cout << "WARNING: This will set analog output pin " << static_cast<int>(pin)
<< " to " << voltageLevel << "V" << std::endl;
std::cout << "Make sure nothing sensitive is connected to this pin." << std::endl;
std::cout << "Continue? (yes/no): ";
std::string response;
std::getline(std::cin, response);
if(response != "yes") {
std::cout << "Aborted." << std::endl;
return 0;
}
}
std::shared_ptr<icsneo::Device> device = nullptr;
if(!serial.empty()) {
// Find device by serial
auto devices = icsneo::FindAllDevices();
for(auto& dev : devices) {
if(dev->getSerial() == serial) {
device = dev;
break;
}
}
if(!device) {
std::cerr << "Device with serial " << serial << " not found" << std::endl;
return -1;
}
} else {
// Use first available device
auto devices = icsneo::FindAllDevices();
if(devices.empty()) {
std::cerr << "No devices found" << std::endl;
return -1;
}
device = devices[0];
}
std::cout << "Using device: " << device->describe() << std::endl;
if(!device->open()) {
std::cerr << "Failed to open device" << std::endl;
return -1;
}
auto settings = device->settings;
if(!settings) {
std::cerr << "Device settings not available" << std::endl;
device->close();
return -1;
}
std::cout << "Refreshing device settings..." << std::endl;
if(!settings->refresh()) {
std::cerr << "Failed to refresh settings" << std::endl;
device->close();
return -1;
}
// Enable analog output on specified pin
std::cout << "Enabling analog output on pin " << static_cast<int>(pin) << "..." << std::endl;
if(!settings->setMiscIOAnalogOutputEnabled(pin, true)) {
std::cerr << "Failed to enable analog output on pin " << static_cast<int>(pin) << std::endl;
device->close();
return -1;
}
// Set pin to specified voltage
std::cout << "Setting pin " << static_cast<int>(pin) << " to " << voltageLevel << "V..." << std::endl;
if(!settings->setMiscIOAnalogOutput(pin, voltage)) {
std::cerr << "Failed to set voltage on pin " << static_cast<int>(pin) << std::endl;
device->close();
return -1;
}
// Apply settings
std::cout << "Applying settings..." << std::endl;
if(!settings->apply()) {
std::cerr << "Failed to apply settings" << std::endl;
device->close();
return -1;
}
std::cout << "Analog output configured successfully!" << std::endl;
std::cout << "Pin " << static_cast<int>(pin) << ": Enabled at " << voltageLevel << "V" << std::endl;
device->close();
return 0;
}
+2
View File
@@ -0,0 +1,2 @@
add_executable(libicsneocpp-diskformat-example src/DiskFormatExample.cpp)
target_link_libraries(libicsneocpp-diskformat-example icsneocpp)
@@ -0,0 +1,145 @@
#include <iostream>
#include <string>
#include "icsneo/icsneocpp.h"
#include "icsneo/disk/diskdetails.h"
int main() {
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
std::cout << "\nFinding devices... " << std::flush;
auto devices = icsneo::FindAllDevices();
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
if(devices.empty()) {
std::cout << "error: no devices found" << std::endl;
return -1;
}
// List devices and let the user pick one
for(size_t i = 0; i < devices.size(); i++) {
std::cout << " [" << i << "] " << devices[i]->describe() << std::endl;
}
size_t choice = 0;
if(devices.size() > 1) {
std::cout << "Select a device [0-" << (devices.size() - 1) << "]: ";
std::cin >> choice;
if(choice >= devices.size()) {
std::cout << "error: invalid selection" << std::endl;
return -1;
}
}
auto& device = devices[choice];
std::cout << "\nOpening " << device->describe() << "... " << std::flush;
if(!device->open()) {
std::cout << "FAIL" << std::endl;
std::cout << "error: " << icsneo::GetLastError() << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
// Check that this device supports disk formatting
if(!device->supportsDiskFormatting()) {
std::cout << "error: " << device->describe() << " does not support disk formatting" << std::endl;
device->close();
return -1;
}
std::cout << "Disk count: " << device->getDiskCount() << std::endl;
// Query the current disk state from the device
std::cout << "\nQuerying disk details... " << std::flush;
auto details = device->getDiskDetails();
if(!details) {
std::cout << "FAIL" << std::endl;
std::cout << "error: " << icsneo::GetLastError() << std::endl;
device->close();
return -1;
}
std::cout << "OK" << std::endl;
std::cout << " Layout : " << (details->layout == icsneo::DiskLayout::RAID0 ? "RAID0" : "Spanned") << std::endl;
for(size_t i = 0; i < details->disks.size(); i++) {
const auto& disk = details->disks[i];
std::cout << " Disk [" << i << "]:" << std::endl;
std::cout << " Present : " << (disk.present ? "yes" : "no") << std::endl;
std::cout << " Initialized : " << (disk.initialized ? "yes" : "no") << std::endl;
std::cout << " Formatted : " << (disk.formatted ? "yes" : "no") << std::endl;
if(disk.present) {
uint64_t bytes = disk.size();
std::cout << " Size : " << (bytes / (1024 * 1024)) << " MB"
<< " (" << disk.sectors << " sectors x " << disk.bytesPerSector << " bytes)" << std::endl;
}
}
// Build a format configuration.
// We keep the existing layout and re-use the disk geometry reported by the device.
// The 'formatted' flag must be true for each disk you want the device to format.
icsneo::DiskDetails formatConfig;
formatConfig.layout = details->layout;
formatConfig.fullFormat = false; // Quick format; set to true for a full (slow) format
formatConfig.disks = details->disks;
// Mark all present disks for formatting
bool anyPresent = false;
for(auto& disk : formatConfig.disks) {
if(disk.present) {
disk.formatted = true;
anyPresent = true;
}
}
if(!anyPresent) {
std::cout << "\nerror: no disks are present in the device" << std::endl;
device->close();
return -1;
}
std::cout << "\nThis will format the disk(s) in " << device->describe() << "." << std::endl;
std::cout << "All existing data will be lost. Continue? [y/N]: ";
std::string confirm;
std::cin >> confirm;
if(confirm != "y" && confirm != "Y") {
std::cout << "Aborted." << std::endl;
device->close();
return 0;
}
std::cout << "\nStarting format..." << std::endl;
// Progress callback — called every 500 ms while formatting
auto progressHandler = [](uint64_t sectorsFormatted, uint64_t sectorsTotal) -> icsneo::Device::DiskFormatDirective {
double pct = sectorsTotal > 0 ? (100.0 * sectorsFormatted / sectorsTotal) : 0.0;
std::cout << "\r Progress: " << sectorsFormatted << " / " << sectorsTotal
<< " sectors (" << static_cast<int>(pct) << "%)" << std::flush;
return icsneo::Device::DiskFormatDirective::Continue;
};
bool success = device->formatDisk(formatConfig, progressHandler);
std::cout << std::endl; // newline after progress line
if(!success) {
std::cout << "error: format failed: " << icsneo::GetLastError() << std::endl;
device->close();
return -1;
}
std::cout << "Format complete!" << std::endl;
// Verify by re-querying disk details
std::cout << "\nVerifying disk state after format... " << std::flush;
auto postDetails = device->getDiskDetails();
if(!postDetails) {
std::cout << "FAIL (could not re-query disk details)" << std::endl;
} else {
std::cout << "OK" << std::endl;
for(size_t i = 0; i < postDetails->disks.size(); i++) {
const auto& disk = postDetails->disks[i];
std::cout << " Disk [" << i << "] formatted: " << (disk.formatted ? "yes" : "no") << std::endl;
}
}
device->close();
return 0;
}
+31 -14
View File
@@ -29,7 +29,7 @@ int main() {
} }
std::cout << "OK" << std::endl; std::cout << "OK" << std::endl;
// Create a subscription message for the GPS signals // Create a subscription message for the GPS signals and TIME_SINCE_MSG
std::cout << "\tSending a live data subscribe command... "; std::cout << "\tSending a live data subscribe command... ";
auto msg = std::make_shared<icsneo::LiveDataCommandMessage>(); auto msg = std::make_shared<icsneo::LiveDataCommandMessage>();
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_LATITUDE); msg->appendSignalArg(icsneo::LiveDataValueType::GPS_LATITUDE);
@@ -37,6 +37,7 @@ int main() {
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_ACCURACY); msg->appendSignalArg(icsneo::LiveDataValueType::GPS_ACCURACY);
msg->appendSignalArg(icsneo::LiveDataValueType::DAQ_ENABLE); msg->appendSignalArg(icsneo::LiveDataValueType::DAQ_ENABLE);
msg->appendSignalArg(icsneo::LiveDataValueType::MANUAL_TRIGGER); msg->appendSignalArg(icsneo::LiveDataValueType::MANUAL_TRIGGER);
msg->appendSignalArg(icsneo::LiveDataValueType::TIME_SINCE_MSG);
msg->cmd = icsneo::LiveDataCommand::SUBSCRIBE; msg->cmd = icsneo::LiveDataCommand::SUBSCRIBE;
msg->handle = icsneo::LiveDataUtil::getNewHandle(); msg->handle = icsneo::LiveDataUtil::getNewHandle();
msg->updatePeriod = std::chrono::milliseconds(100); msg->updatePeriod = std::chrono::milliseconds(100);
@@ -44,6 +45,9 @@ int main() {
// Transmit the subscription message // Transmit the subscription message
ret = device->subscribeLiveData(msg); ret = device->subscribeLiveData(msg);
std::cout << (ret ? "OK" : "FAIL") << std::endl; std::cout << (ret ? "OK" : "FAIL") << std::endl;
if (!ret) {
std::cout << "\t\tError: " << icsneo::GetLastError() << std::endl;
}
// Register a handler that uses the data after it arrives every ~100ms // Register a handler that uses the data after it arrives every ~100ms
std::cout << "\tStreaming messages for 3 seconds... " << std::endl << std::endl; std::cout << "\tStreaming messages for 3 seconds... " << std::endl << std::endl;
@@ -53,19 +57,21 @@ int main() {
switch(ldMsg->cmd) { switch(ldMsg->cmd) {
case icsneo::LiveDataCommand::STATUS: { case icsneo::LiveDataCommand::STATUS: {
auto msg2 = std::dynamic_pointer_cast<icsneo::LiveDataStatusMessage>(message); auto msg2 = std::dynamic_pointer_cast<icsneo::LiveDataStatusMessage>(message);
std::cout << "[Handle] " << ldMsg->handle << std::endl; std::cout << "[STATUS Message]" << std::endl;
std::cout << "[Requested Command] " << msg2->requestedCommand << std::endl; std::cout << " Handle: " << ldMsg->handle << std::endl;
std::cout << "[Status] " << msg2->status << std::endl << std::endl; std::cout << " Requested Command: " << msg2->requestedCommand << std::endl;
std::cout << " Status: " << msg2->status << std::endl << std::endl;
break; break;
} }
case icsneo::LiveDataCommand::RESPONSE: { case icsneo::LiveDataCommand::RESPONSE: {
auto valueMsg = std::dynamic_pointer_cast<icsneo::LiveDataValueMessage>(message); auto valueMsg = std::dynamic_pointer_cast<icsneo::LiveDataValueMessage>(message);
if((valueMsg->handle == msg->handle) && (valueMsg->values.size() == msg->args.size())) if((valueMsg->handle == msg->handle) && (valueMsg->values.size() == msg->args.size()))
{ {
std::cout << "[Handle] " << msg->handle << std::endl; std::cout << "[Response Message]" << std::endl;
std::cout << "[Values] " << valueMsg->numArgs << std::endl; std::cout << " Handle: " << msg->handle << std::endl;
std::cout << " Number of Values: " << valueMsg->numArgs << std::endl;
for(uint32_t i = 0; i < valueMsg->numArgs; ++i) { for(uint32_t i = 0; i < valueMsg->numArgs; ++i) {
std::cout << "[" << msg->args[i]->valueType << "] "; std::cout << " [" << msg->args[i]->valueType << "] ";
auto scaledValue = icsneo::LiveDataUtil::liveDataValueToDouble(*valueMsg->values[i]); auto scaledValue = icsneo::LiveDataUtil::liveDataValueToDouble(*valueMsg->values[i]);
std::cout << scaledValue << std::endl; std::cout << scaledValue << std::endl;
} }
@@ -86,22 +92,33 @@ int main() {
setValMsg->cmd = icsneo::LiveDataCommand::SET_VALUE; setValMsg->cmd = icsneo::LiveDataCommand::SET_VALUE;
setValMsg->handle = msg->handle; setValMsg->handle = msg->handle;
// Convert the value format // Convert the value format
icsneo::LiveDataValue ldValueDAQEnable; auto ldValueDAQEnable = icsneo::LiveDataUtil::liveDataDoubleToValue(val / 3);
icsneo::LiveDataValue ldValueManTrig; auto ldValueManTrig = icsneo::LiveDataUtil::liveDataDoubleToValue(val);
if (!icsneo::LiveDataUtil::liveDataDoubleToValue(val / 3, ldValueDAQEnable) || auto ldValueTimeSinceMsg = icsneo::LiveDataUtil::liveDataDoubleToValue(val);
!icsneo::LiveDataUtil::liveDataDoubleToValue(val, ldValueManTrig)) { if (!ldValueDAQEnable || !ldValueManTrig || !ldValueTimeSinceMsg) {
std::cout << "\tError: Failed to convert values" << std::endl;
break; break;
} }
setValMsg->appendSetValue(icsneo::LiveDataValueType::DAQ_ENABLE, ldValueDAQEnable); setValMsg->appendSetValue(icsneo::LiveDataValueType::DAQ_ENABLE, *ldValueDAQEnable);
setValMsg->appendSetValue(icsneo::LiveDataValueType::MANUAL_TRIGGER, ldValueManTrig); setValMsg->appendSetValue(icsneo::LiveDataValueType::MANUAL_TRIGGER, *ldValueManTrig);
device->setValueLiveData(setValMsg); setValMsg->appendSetValue(icsneo::LiveDataValueType::TIME_SINCE_MSG, *ldValueTimeSinceMsg);
std::cout << "\tSetting values: DAQ_ENABLE=" << (val / 3)
<< ", MANUAL_TRIGGER=" << val
<< ", TIME_SINCE_MSG=" << val << std::endl;
if (!device->setValueLiveData(setValMsg)) {
std::cout << "\tError setting values: " << icsneo::GetLastError() << std::endl;
}
++val; ++val;
// Run handler for three seconds to observe the signal data // Run handler for three seconds to observe the signal data
std::this_thread::sleep_for(std::chrono::seconds(3)); std::this_thread::sleep_for(std::chrono::seconds(3));
} }
// Unsubscribe from the GPS signals and run handler for one more second // Unsubscribe from the GPS signals and run handler for one more second
// Unsubscription only requires a valid in-use handle, in this case from our previous subscription // Unsubscription only requires a valid in-use handle, in this case from our previous subscription
std::cout << "\tUnsubscribing... ";
ret = device->unsubscribeLiveData(msg->handle); ret = device->unsubscribeLiveData(msg->handle);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
// The handler should no longer print values // The handler should no longer print values
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));
device->removeMessageCallback(handler); device->removeMessageCallback(handler);
@@ -0,0 +1,107 @@
"""
Basic analog output control example using icsneopy library.
Demonstrates how to configure and control analog outputs on supported devices.
Usage: python analog_out_basic.py <pin> <voltage> [--yes]
Arguments:
pin: Pin number (1-3 for RAD Galaxy)
voltage: Voltage level (0-5)
--yes: Skip confirmation prompt
"""
import sys
import icsneopy
def analog_output_example(pin: int, voltage: int, skip_confirm: bool = False):
"""Configure and control analog outputs."""
# Confirmation prompt
if not skip_confirm:
print(f"WARNING: This will set analog output pin {pin} to {voltage}V")
print("Make sure nothing sensitive is connected to this pin.")
response = input("Continue? (yes/no): ")
if response.lower() != "yes":
print("Aborted.")
return
devices = icsneopy.find_all_devices()
if not devices:
raise RuntimeError("No devices found")
device = devices[0]
try:
if not device.open():
raise RuntimeError("Failed to open device")
settings = device.settings
if not settings:
raise RuntimeError("Device settings not available")
print("Refreshing device settings...")
if not settings.refresh():
raise RuntimeError("Failed to refresh settings")
# Enable analog output on specified pin
print(f"Enabling analog output on pin {pin}...")
if not settings.set_misc_io_analog_output_enabled(pin, True):
raise RuntimeError(f"Failed to enable analog output on pin {pin}")
# Map voltage level to enum
voltage_map = {
0: icsneopy.Settings.MiscIOAnalogVoltage.V0,
1: icsneopy.Settings.MiscIOAnalogVoltage.V1,
2: icsneopy.Settings.MiscIOAnalogVoltage.V2,
3: icsneopy.Settings.MiscIOAnalogVoltage.V3,
4: icsneopy.Settings.MiscIOAnalogVoltage.V4,
5: icsneopy.Settings.MiscIOAnalogVoltage.V5
}
voltage_enum = voltage_map[voltage]
# Set pin to specified voltage
print(f"Setting pin {pin} to {voltage}V...")
if not settings.set_misc_io_analog_output(pin, voltage_enum):
raise RuntimeError(f"Failed to set voltage on pin {pin}")
# Apply settings
print("Applying settings...")
if not settings.apply():
raise RuntimeError("Failed to apply settings")
print("Analog output configured successfully!")
print(f"Pin {pin}: Enabled at {voltage}V")
finally:
device.close()
if __name__ == "__main__":
if len(sys.argv) < 3:
print("Error: Missing required arguments\n")
print("Usage: python analog_out_basic.py <pin> <voltage> [--yes]")
print("\nArguments:")
print(" pin: Pin number (1-3 for RAD Galaxy)")
print(" voltage: Voltage level (0-5)")
print(" --yes: Skip confirmation prompt")
sys.exit(1)
try:
pin = int(sys.argv[1])
if pin < 1 or pin > 3:
print("Error: Invalid pin number (must be 1-3)")
sys.exit(1)
voltage = int(sys.argv[2])
if voltage < 0 or voltage > 5:
print("Error: Invalid voltage level (must be 0-5)")
sys.exit(1)
skip_confirm = "--yes" in sys.argv
analog_output_example(pin, voltage, skip_confirm)
except ValueError:
print("Error: Pin and voltage must be integers")
sys.exit(1)
@@ -0,0 +1,114 @@
import sys
import icsneopy
def disk_format_example():
devices = icsneopy.find_all_devices()
if not devices:
print("error: no devices found")
return False
print(f"Found {len(devices)} device(s):")
for i, d in enumerate(devices):
print(f" [{i}] {d}")
if len(devices) == 1:
choice = 0
else:
try:
choice = int(input(f"Select a device [0-{len(devices)-1}]: "))
except (ValueError, EOFError):
print("error: invalid selection")
return False
if choice < 0 or choice >= len(devices):
print("error: invalid selection")
return False
device = devices[choice]
print(f"\nOpening {device}... ", end="", flush=True)
if not device.open():
print("FAIL")
print(f"error: {icsneopy.get_last_error().describe()}")
return False
print("OK")
if not device.supports_disk_formatting():
print(f"error: {device} does not support disk formatting")
device.close()
return False
print(f"Disk count: {device.get_disk_count()}")
# Query current disk state
print("\nQuerying disk details... ", end="", flush=True)
details = device.get_disk_details()
if details is None:
print("FAIL")
print(f"error: {icsneopy.get_last_error().describe()}")
device.close()
return False
print("OK")
layout_name = "RAID0" if details.layout == icsneopy.DiskLayout.RAID0 else "Spanned"
print(f" Layout : {layout_name}")
for i, disk in enumerate(details.disks):
print(f" Disk [{i}]:")
print(f" Present : {'yes' if disk.present else 'no'}")
print(f" Initialized : {'yes' if disk.initialized else 'no'}")
print(f" Formatted : {'yes' if disk.formatted else 'no'}")
if disk.present:
mb = disk.size() // (1024 * 1024)
print(f" Size : {mb} MB ({disk.sectors} sectors x {disk.bytes_per_sector} bytes)")
any_present = any(d.present for d in details.disks)
if not any_present:
print("\nerror: no disks are present in the device")
device.close()
return False
# Build format config from the queried state
fmt = icsneopy.DiskDetails()
fmt.layout = details.layout
fmt.full_format = False # Quick format; set True for a full (slow) format
fmt.disks = details.disks
for disk in fmt.disks:
if disk.present:
disk.formatted = True # mark for formatting
confirm = input(
f"\nThis will format the disk(s) in {device}.\n"
"All existing data will be lost. Continue? [y/N]: "
).strip()
if confirm.lower() != "y":
print("Aborted.")
device.close()
return True
print("\nStarting format...")
state = {"total": 0}
ok = device.format_disk(fmt)
print() # newline after progress line
if not ok:
print(f"error: format failed: {icsneopy.get_last_error().describe()}")
device.close()
return False
print("Format complete!")
# Verify
print("\nVerifying disk state after format... ", end="", flush=True)
post = device.get_disk_details()
if post is None:
print("FAIL (could not re-query disk details)")
else:
print("OK")
for i, disk in enumerate(post.disks):
print(f" Disk [{i}] formatted: {'yes' if disk.formatted else 'no'}")
device.close()
return True
if __name__ == "__main__":
sys.exit(0 if disk_format_example() else 1)
@@ -0,0 +1,218 @@
"""
FlexRay coldstart example using icsneopy library.
Demonstrates coldstart capability where one FlexRay device can start
the network without needing other devices connected.
CRITICAL COLDSTART REQUIREMENTS:
1. key_slot_used_for_startup = True
2. key_slot_used_for_sync = True
3. set_allow_coldstart(True)
4. Each controller needs a unique key_slot_id
5. Proper bus termination (required for FlexRay)
"""
import icsneopy
import time
def get_coldstart_controller_config(slot_id):
"""
Create FlexRay controller configuration for COLDSTART.
The three critical settings for coldstart are marked below.
"""
config = icsneopy.FlexRay.Controller.Configuration()
config.accept_startup_range_microticks = 160
config.allow_halt_due_to_clock = True
config.allow_passive_to_active_cycle_pairs = 15
config.cluster_drift_damping = 2
config.channel_a = True
config.channel_b = True
config.decoding_correction_microticks = 56
config.delay_compensation_a_microticks = 28
config.delay_compensation_b_microticks = 28
config.extern_offset_correction_control = 0
config.extern_rate_correction_control = 0
config.extern_offset_correction_microticks = 0
config.extern_rate_correction_microticks = 0
# CRITICAL FOR COLDSTART: Set the key slot ID
config.key_slot_id = slot_id
config.key_slot_only_enabled = False
# CRITICAL FOR COLDSTART: Enable startup and sync on key slot
config.key_slot_used_for_startup = True # Required for coldstart
config.key_slot_used_for_sync = True # Required for coldstart
config.latest_tx_minislot = 226
config.listen_timeout = 401202
config.macro_initial_offset_a = 7
config.macro_initial_offset_b = 7
config.micro_initial_offset_a = 36
config.micro_initial_offset_b = 36
config.micro_per_cycle = 200000
config.mts_on_a = False
config.mts_on_b = False
config.offset_correction_out_microticks = 189
config.rate_correction_out_microticks = 601
config.second_key_slot_id = 0
config.two_key_slot_mode = False
config.wakeup_pattern = 55
config.wakeup_on_channel_b = False
return config
def get_cluster_config():
"""Create FlexRay cluster configuration."""
config = icsneopy.FlexRay.Cluster.Configuration()
config.speed = icsneopy.FlexRay.Cluster.SpeedType.FLEXRAY_BAUDRATE_10M
config.strobe_point_position = icsneopy.FlexRay.Cluster.SPPType.FLEXRAY_SPP_5
config.action_point_offset = 4
config.casr_x_low_max = 64
config.cold_start_attempts = 8
config.cycle_duration_micro_sec = 5000
config.dynamic_slot_idle_phase_minislots = 1
config.listen_noise_macroticks = 4
config.macroticks_per_cycle = 5000
config.macrotick_duration_micro_sec = 1
config.max_without_clock_correction_fatal = 2
config.max_without_clock_correction_passive = 2
config.minislot_action_point_offset_macroticks = 4
config.minislot_duration_macroticks = 10
config.network_idle_time_macroticks = 40
config.network_management_vector_length_bytes = 1
config.number_of_minislots = 0
config.number_of_static_slots = 32
config.offset_correction_start_macroticks = 4991
config.payload_length_of_static_slot_in_words = 67
config.static_slot_macroticks = 155
config.symbol_window_macroticks = 0
config.symbol_window_action_point_offset_macroticks = 0
config.sync_frame_id_count_max = 15
config.transmission_start_sequence_duration_bits = 11
config.wakeup_rx_idle_bits = 40
config.wakeup_rx_low_bits = 40
config.wakeup_rx_window_bits = 301
config.wakeup_tx_active_bits = 60
config.wakeup_tx_idle_bits = 180
return config
def flexray_coldstart():
"""Perform FlexRay coldstart operation."""
devices = icsneopy.find_all_devices()
if not devices:
raise RuntimeError("No devices found")
# Find a device with FlexRay support
device = None
for dev in devices:
if dev.get_extension("FlexRay"):
device = dev
break
if not device:
raise RuntimeError("No FlexRay-capable device found")
print(f"Using device: {device.get_product_name()} {device.get_serial()}")
try:
# Get FlexRay controllers
controllers = device.get_flexray_controllers()
if not controllers:
raise RuntimeError("Device has no FlexRay controllers")
print(f"Device has {len(controllers)} FlexRay controller(s)")
# Configure controllers for coldstart
cluster_config = get_cluster_config()
base_slot_id = 1
for i, controller in enumerate(controllers):
slot_id = base_slot_id + i
controller_config = get_coldstart_controller_config(slot_id)
print(f"\nConfiguring controller {i} for COLDSTART:")
print(f" Key Slot ID: {slot_id}")
print(f" Key Slot Used for Startup: {controller_config.key_slot_used_for_startup}")
print(f" Key Slot Used for Sync: {controller_config.key_slot_used_for_sync}")
# CRITICAL FOR COLDSTART: Enable coldstart capability
controller.set_allow_coldstart(True)
print(f" Allow Coldstart: True")
# Configure to start when going online
controller.set_start_when_going_online(True)
# Set the configuration
controller.set_configuration(cluster_config, controller_config)
# Open device
print("\nOpening device...")
if not device.open():
raise RuntimeError("Failed to open device")
print("Device opened successfully")
# Go online - this triggers coldstart
print("\nGoing online (coldstart will initiate)...")
if not device.go_online():
raise RuntimeError("Failed to go online - check bus termination and configuration")
print("Device online successfully!")
print("\n" + "=" * 60)
print("✓ FlexRay network started via COLDSTART")
print("=" * 60)
# Transmit test messages on the coldstart key slot
print("\nTransmitting initial test messages...")
for i in range(5):
frame = icsneopy.FlexRayMessage()
frame.network = icsneopy.Network(icsneopy.Network.NetID.FLEXRAY_01)
frame.slotid = base_slot_id # Use the first key slot
frame.cycle = 0
frame.cycle_repetition = 1
frame.channel = icsneopy.FlexRay.Channel.AB
frame.data = (0xAA, 0xBB, 0xCC, 0xDD, i, i+1, i+2, i+3)
if device.transmit(frame):
print(f" ✓ Transmitted message {i+1}")
else:
print(f" ✗ Failed to transmit message {i+1}")
time.sleep(0.1)
print("\n" + "=" * 60)
print("Network is now active and will stay alive.")
print("You can now run transmit/receive examples in another terminal.")
print("Press Ctrl+C to stop and shut down the network.")
print("=" * 60)
# Keep transmitting periodically to maintain network presence
counter = 0
try:
while True:
frame = icsneopy.FlexRayMessage()
frame.network = icsneopy.Network(icsneopy.Network.NetID.FLEXRAY_01)
frame.slotid = base_slot_id
frame.cycle = 0
frame.cycle_repetition = 1
frame.channel = icsneopy.FlexRay.Channel.AB
frame.data = (0xCA, 0xFE, 0xBA, 0xBE, counter & 0xFF,
(counter >> 8) & 0xFF, (counter >> 16) & 0xFF, (counter >> 24) & 0xFF)
device.transmit(frame)
counter += 1
time.sleep(1) # Transmit every second
except KeyboardInterrupt:
print("\n\nStopping coldstart node...")
print("\n✓ Coldstart example completed successfully!")
finally:
device.close()
if __name__ == "__main__":
flexray_coldstart()
@@ -0,0 +1,188 @@
# Basic FlexRay frame reception example using icsneopy library.
import icsneopy
import time
import signal
import sys
def get_controller_config(slot_id):
"""Create a FlexRay controller configuration matching the network.
Args:
slot_id: The key slot ID for this node (must be unique per node)
Returns:
FlexRay.Controller.Configuration with all parameters set
Note:
For passive listening on an existing network, set:
- key_slot_used_for_startup = False
- Remove set_allow_coldstart(True) call below
"""
config = icsneopy.FlexRay.Controller.Configuration()
config.accept_startup_range_microticks = 160
config.allow_halt_due_to_clock = True
config.allow_passive_to_active_cycle_pairs = 15
config.cluster_drift_damping = 2
# Physical channel configuration (Channel A only for this example)
config.channel_a = True
config.channel_b = False # Single channel A only
config.decoding_correction_microticks = 56
config.delay_compensation_a_microticks = 28
config.delay_compensation_b_microticks = 28
config.extern_offset_correction_control = 0
config.extern_rate_correction_control = 0
config.extern_offset_correction_microticks = 0
config.extern_rate_correction_microticks = 0
# KEY SLOT CONFIGURATION - Critical for FlexRay operation
config.key_slot_id = slot_id # Must be unique per node
config.key_slot_only_enabled = False
config.key_slot_used_for_startup = True # True = participate in coldstart
config.key_slot_used_for_sync = True # True = synchronize with network
config.latest_tx_minislot = 226
config.listen_timeout = 401202
config.macro_initial_offset_a = 7
config.macro_initial_offset_b = 7
config.micro_initial_offset_a = 36
config.micro_initial_offset_b = 36
config.micro_per_cycle = 200000
config.mts_on_a = False
config.mts_on_b = False
config.offset_correction_out_microticks = 189
config.rate_correction_out_microticks = 601
config.second_key_slot_id = 0
config.two_key_slot_mode = False
config.wakeup_pattern = 55
config.wakeup_on_channel_b = False
return config
def get_cluster_config():
"""Create a FlexRay cluster configuration matching the network."""
config = icsneopy.FlexRay.Cluster.Configuration()
config.speed = icsneopy.FlexRay.Cluster.SpeedType.FLEXRAY_BAUDRATE_10M
config.strobe_point_position = icsneopy.FlexRay.Cluster.SPPType.FLEXRAY_SPP_5
config.action_point_offset = 4
config.casr_x_low_max = 64
config.cold_start_attempts = 8
config.cycle_duration_micro_sec = 5000
config.dynamic_slot_idle_phase_minislots = 1
config.listen_noise_macroticks = 4
config.macroticks_per_cycle = 5000
config.macrotick_duration_micro_sec = 1
config.max_without_clock_correction_fatal = 2
config.max_without_clock_correction_passive = 2
config.minislot_action_point_offset_macroticks = 4
config.minislot_duration_macroticks = 10
config.network_idle_time_macroticks = 40
config.network_management_vector_length_bytes = 1
config.number_of_minislots = 0
config.number_of_static_slots = 32
config.offset_correction_start_macroticks = 4991
config.payload_length_of_static_slot_in_words = 67
config.static_slot_macroticks = 155
config.symbol_window_macroticks = 0
config.symbol_window_action_point_offset_macroticks = 0
config.sync_frame_id_count_max = 15
config.transmission_start_sequence_duration_bits = 11
config.wakeup_rx_idle_bits = 40
config.wakeup_rx_low_bits = 40
config.wakeup_rx_window_bits = 301
config.wakeup_tx_active_bits = 60
config.wakeup_tx_idle_bits = 180
return config
def receive_flexray_frames():
"""Receive FlexRay frames as passive node with callback handling."""
devices = icsneopy.find_all_devices()
if not devices:
raise RuntimeError("No devices found")
# Find a device with FlexRay support
device = None
for dev in devices:
if dev.get_extension("FlexRay"):
device = dev
break
if not device:
raise RuntimeError("No FlexRay-capable device found")
frame_count = 0
running = True
def on_frame(frame):
nonlocal frame_count
if isinstance(frame, icsneopy.FlexRayMessage):
# Only show frames from slot 1 (filter out null frames)
if frame.slotid == 1:
frame_count += 1
# Nice formatted view of the frame
payload_hex = ' '.join([f'{b:02X}' for b in frame.data[:8]])
payload_dec = ' '.join([f'{b:3d}' for b in frame.data[:8]])
print(f"[Frame {frame_count:4d}] Slot: {frame.slotid:2d} | Cycle: {frame.cycle:2d} | "
f"Channel: {str(frame.channel):10s}")
print(f" Hex: [{payload_hex}]")
print(f" Dec: [{payload_dec}]\n")
def signal_handler(sig, frame):
nonlocal running
print("\nShutting down...")
running = False
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
frame_filter = icsneopy.MessageFilter(icsneopy.Network.NetID.FLEXRAY_02)
callback = icsneopy.MessageCallback(on_frame, frame_filter)
try:
# Configure FlexRay controller 1 (FLEXRAY_02) as passive node
controllers = device.get_flexray_controllers()
if len(controllers) < 2:
raise RuntimeError("Device needs at least 2 FlexRay controllers")
controller = controllers[1] # Use controller 1
cluster_config = get_cluster_config()
controller_config = get_controller_config(slot_id=2)
# Enable coldstart so this node transmits and coldstart node sees activity
controller.set_allow_coldstart(True)
controller.set_configuration(cluster_config, controller_config)
controller.set_start_when_going_online(True)
if not device.open():
raise RuntimeError("Failed to open device")
if not device.go_online():
raise RuntimeError("Failed to go online")
device.add_message_callback(callback)
print("="*60)
print("FlexRay Receive Node - Coldstart Config Loaded")
print("="*60)
print(f"Controller: FLEXRAY_02 | Slot ID: 2 | Channel: A")
print(f"Listening for frames...")
print(f"Start the transmit script now to begin communication")
print("="*60)
print("Press Ctrl+C to stop\n")
while running:
time.sleep(0.1)
print(f"\nTotal frames received: {frame_count}")
finally:
device.close()
if __name__ == "__main__":
receive_flexray_frames()
@@ -0,0 +1,218 @@
# Basic FlexRay frame transmission example using icsneopy library.
import icsneopy
import time
import signal
import sys
import random
def get_controller_config(slot_id, is_coldstart=False):
"""Create a FlexRay controller configuration matching the network.
Args:
slot_id: The key slot ID for this node (must be unique per node)
is_coldstart: True if this node participates in coldstart
Returns:
FlexRay.Controller.Configuration with all parameters set
"""
config = icsneopy.FlexRay.Controller.Configuration()
config.accept_startup_range_microticks = 160
config.allow_halt_due_to_clock = True
config.allow_passive_to_active_cycle_pairs = 15
config.cluster_drift_damping = 2
# Physical channel configuration (Channel A only for this example)
config.channel_a = True
config.channel_b = False # Single channel A only
config.decoding_correction_microticks = 56
config.delay_compensation_a_microticks = 28
config.delay_compensation_b_microticks = 28
config.extern_offset_correction_control = 0
config.extern_rate_correction_control = 0
config.extern_offset_correction_microticks = 0
config.extern_rate_correction_microticks = 0
# KEY SLOT CONFIGURATION - Critical for FlexRay operation
config.key_slot_id = slot_id # Must be unique per node
config.key_slot_only_enabled = False
config.key_slot_used_for_startup = is_coldstart # True = coldstart node
config.key_slot_used_for_sync = is_coldstart # True = provides sync
config.latest_tx_minislot = 226
config.listen_timeout = 401202
config.macro_initial_offset_a = 7
config.macro_initial_offset_b = 7
config.micro_initial_offset_a = 36
config.micro_initial_offset_b = 36
config.micro_per_cycle = 200000
config.mts_on_a = False
config.mts_on_b = False
config.offset_correction_out_microticks = 189
config.rate_correction_out_microticks = 601
config.second_key_slot_id = 0
config.two_key_slot_mode = False
config.wakeup_pattern = 55
config.wakeup_on_channel_b = False
return config
def get_cluster_config():
"""Create a FlexRay cluster configuration matching the network.
All nodes on the FlexRay network must have identical cluster parameters.
These define the timing and structure of the FlexRay communication cycle.
Key parameters:
- cycle_duration_micro_sec: 5000 = 5ms cycle time
- macroticks_per_cycle: 5000 macroticks per cycle
- number_of_static_slots: 32 static slots for guaranteed transmission
- payload_length_of_static_slot_in_words: 67 words = 134 bytes max payload
Returns:
FlexRay.Cluster.Configuration with all timing parameters set
"""
config = icsneopy.FlexRay.Cluster.Configuration()
config.speed = icsneopy.FlexRay.Cluster.SpeedType.FLEXRAY_BAUDRATE_10M
config.strobe_point_position = icsneopy.FlexRay.Cluster.SPPType.FLEXRAY_SPP_5
config.action_point_offset = 4
config.casr_x_low_max = 64
config.cold_start_attempts = 8
config.cycle_duration_micro_sec = 5000
config.dynamic_slot_idle_phase_minislots = 1
config.listen_noise_macroticks = 4
config.macroticks_per_cycle = 5000
config.macrotick_duration_micro_sec = 1
config.max_without_clock_correction_fatal = 2
config.max_without_clock_correction_passive = 2
config.minislot_action_point_offset_macroticks = 4
config.minislot_duration_macroticks = 10
config.network_idle_time_macroticks = 40
config.network_management_vector_length_bytes = 1
config.number_of_minislots = 0
config.number_of_static_slots = 32
config.offset_correction_start_macroticks = 4991
config.payload_length_of_static_slot_in_words = 67
config.static_slot_macroticks = 155
config.symbol_window_macroticks = 0
config.symbol_window_action_point_offset_macroticks = 0
config.sync_frame_id_count_max = 15
config.transmission_start_sequence_duration_bits = 11
config.wakeup_rx_idle_bits = 40
config.wakeup_rx_low_bits = 40
config.wakeup_rx_window_bits = 301
config.wakeup_tx_active_bits = 60
config.wakeup_tx_idle_bits = 180
return config
def transmit_flexray_frame():
"""Transmit FlexRay frames as coldstart node."""
devices = icsneopy.find_all_devices()
if not devices:
raise RuntimeError("No devices found")
# Find a device with FlexRay support
device = None
for dev in devices:
if dev.get_extension("FlexRay"):
device = dev
break
if not device:
raise RuntimeError("No FlexRay-capable device found")
running = True
def signal_handler(sig, frame):
nonlocal running
print("\nShutting down...")
running = False
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
try:
# Configure FlexRay controller 0 (FLEXRAY_01) as coldstart node
controllers = device.get_flexray_controllers()
if not controllers:
raise RuntimeError("Device has no FlexRay controllers")
controller = controllers[0] # Use controller 0
cluster_config = get_cluster_config()
controller_config = get_controller_config(slot_id=1, is_coldstart=True)
# Enable coldstart capability
controller.set_allow_coldstart(True)
controller.set_configuration(cluster_config, controller_config)
controller.set_start_when_going_online(True)
if not device.open():
raise RuntimeError("Failed to open device")
if not device.go_online():
raise RuntimeError("Failed to go online")
print("="*60)
print("FlexRay Transmit Node - Starting Network")
print("="*60)
print(f"Controller: FLEXRAY_01 | Slot ID: 1 | Channel: A")
print(f"Transmitting frames continuously...")
print("="*60)
print("Press Ctrl+C to stop\n")
# Transmit frames continuously starting immediately
counter = 0
sensor_temp = 20.0 # Simulated temperature sensor
sensor_pressure = 100.0 # Simulated pressure sensor
while running:
# Create new frame each time (important for FlexRay)
frame = icsneopy.FlexRayMessage()
frame.network = icsneopy.Network(icsneopy.Network.NetID.FLEXRAY_01)
frame.slotid = 1
frame.cycle = 0
frame.cycle_repetition = 1
frame.channel = icsneopy.FlexRay.Channel.A
# Simulate realistic sensor data
sensor_temp += random.uniform(-0.5, 0.5) # Temperature varies
sensor_pressure += random.uniform(-2.0, 2.0) # Pressure varies
# Pack data: [status, counter, temp_high, temp_low, pressure_high, pressure_low, checksum_placeholder, sequence]
status_byte = 0xA0 | (counter % 16) # Status with rolling bits
temp_int = int(sensor_temp * 10) & 0xFFFF
pressure_int = int(sensor_pressure * 10) & 0xFFFF
frame.data = (
status_byte,
counter & 0xFF,
(temp_int >> 8) & 0xFF,
temp_int & 0xFF,
(pressure_int >> 8) & 0xFF,
pressure_int & 0xFF,
random.randint(0, 255), # Random data
(counter >> 8) & 0xFF
)
success = device.transmit(frame)
if counter % 100 == 0: # Print every 100th to reduce spam
if success:
print(f" [TX {counter}] Temp: {sensor_temp:.1f}°C | Pressure: {sensor_pressure:.1f} kPa")
else:
print(f" Frame {counter}: Failed to transmit")
counter += 1
time.sleep(0.005) # 5ms per cycle
print("\nTransmission complete!")
finally:
device.close()
if __name__ == "__main__":
transmit_flexray_frame()
@@ -0,0 +1,130 @@
"""
LiveData subscription and monitoring example using icsneopy library.
"""
import icsneopy
import time
from datetime import timedelta
def livedata_example():
"""Subscribe to and monitor LiveData signals."""
devices = icsneopy.find_all_devices()
if not devices:
raise RuntimeError("No devices found")
device = devices[0]
print(f"Using device: {device}")
try:
if not device.open():
raise RuntimeError("Failed to open device")
if not device.go_online():
raise RuntimeError("Failed to go online")
device.enable_message_polling()
# Create subscription message
msg = icsneopy.LiveDataCommandMessage()
msg.handle = icsneopy.get_new_handle()
msg.cmd = icsneopy.LiveDataCommand.SUBSCRIBE
msg.update_period = timedelta(milliseconds=500)
msg.expiration_time = timedelta(milliseconds=0)
# Subscribe to various LiveData signals
msg.append_signal_arg(icsneopy.LiveDataValueType.GPS_LATITUDE)
msg.append_signal_arg(icsneopy.LiveDataValueType.GPS_LONGITUDE)
msg.append_signal_arg(icsneopy.LiveDataValueType.GPS_ACCURACY)
msg.append_signal_arg(icsneopy.LiveDataValueType.DAQ_ENABLE)
msg.append_signal_arg(icsneopy.LiveDataValueType.MANUAL_TRIGGER)
msg.append_signal_arg(icsneopy.LiveDataValueType.TIME_SINCE_MSG)
print("\nSubscribing to LiveData signals...")
if not device.subscribe_live_data(msg):
raise RuntimeError(f"Subscription failed: {icsneopy.get_last_error()}")
print("Subscription successful")
print("\nMonitoring LiveData for 5 seconds...")
response_count = 0
start_time = time.time()
while time.time() - start_time < 5:
result = device.get_messages()
messages = result[0] if isinstance(result, tuple) else result
for m in messages:
if isinstance(m, icsneopy.LiveDataStatusMessage):
if m.handle == msg.handle:
print(f"\n[Status] Command: {m.requested_command}, Status: {m.status}")
elif isinstance(m, icsneopy.LiveDataValueMessage):
if m.handle == msg.handle:
response_count += 1
print(f"\n[Response #{response_count}]")
signal_names = ["GPS_LAT", "GPS_LON", "GPS_ACC",
"DAQ_EN", "MAN_TRIG", "TIME_SINCE"]
for idx, val in enumerate(m.values):
value = icsneopy.livedata_value_to_double(val)
name = signal_names[idx] if idx < len(signal_names) else f"Signal_{idx}"
print(f" {name:12s}: {value:10.2f}")
time.sleep(0.1)
print(f"\nReceived {response_count} response messages")
# Demonstrate setting values
print("\nSetting custom values...")
set_msg = icsneopy.LiveDataSetValueMessage()
set_msg.handle = icsneopy.get_new_handle()
set_msg.cmd = icsneopy.LiveDataCommand.SET_VALUE
# Set DAQ_ENABLE
value = icsneopy.livedata_double_to_value(1.0)
if value:
set_msg.append_set_value(icsneopy.LiveDataValueType.DAQ_ENABLE, value)
# Set MANUAL_TRIGGER
value = icsneopy.livedata_double_to_value(1.0)
if value:
set_msg.append_set_value(icsneopy.LiveDataValueType.MANUAL_TRIGGER, value)
if device.set_value_live_data(set_msg):
print("Values set successfully")
time.sleep(0.5)
# Check the results
result = device.get_messages()
messages = result[0] if isinstance(result, tuple) else result
for m in messages:
if isinstance(m, icsneopy.LiveDataStatusMessage):
if m.handle == set_msg.handle:
print(f" Set status: {m.status}")
# Keep device awake by resetting TIME_SINCE_MSG
print("\nResetting TIME_SINCE_MSG to keep device awake...")
reset_msg = icsneopy.LiveDataSetValueMessage()
reset_msg.handle = icsneopy.get_new_handle()
reset_msg.cmd = icsneopy.LiveDataCommand.SET_VALUE
value = icsneopy.livedata_double_to_value(0.0)
if value:
reset_msg.append_set_value(icsneopy.LiveDataValueType.TIME_SINCE_MSG, value)
if device.set_value_live_data(reset_msg):
print("TIME_SINCE_MSG reset to 0")
# Unsubscribe
print("\nUnsubscribing...")
if device.unsubscribe_live_data(msg.handle):
print("Unsubscribed successfully")
finally:
device.close()
print("\nDevice closed")
if __name__ == "__main__":
livedata_example()
+2 -1
View File
@@ -5,6 +5,7 @@
#include <cstdint> #include <cstdint>
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <optional>
#include "icsneo/communication/command.h" #include "icsneo/communication/command.h"
#include "icsneo/api/eventmanager.h" #include "icsneo/api/eventmanager.h"
@@ -157,7 +158,7 @@ namespace LiveDataUtil
LiveDataHandle getNewHandle(); LiveDataHandle getNewHandle();
double liveDataValueToDouble(const LiveDataValue& val); double liveDataValueToDouble(const LiveDataValue& val);
bool liveDataDoubleToValue(const double& dFloat, LiveDataValue& value); std::optional<LiveDataValue> liveDataDoubleToValue(const double& dFloat);
static constexpr uint32_t LiveDataVersion = 1; static constexpr uint32_t LiveDataVersion = 1;
} // namespace LiveDataUtil } // namespace LiveDataUtil
@@ -4,17 +4,17 @@
#ifdef __cplusplus #ifdef __cplusplus
#include "icsneo/communication/message/message.h" #include "icsneo/communication/message/message.h"
#include <string>
// Used for MACAddress.toString() only #include <vector>
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include <cstring>
namespace icsneo { namespace icsneo {
struct MACAddress { struct MACAddress {
uint8_t data[6]; uint8_t data[6];
// Helpers
std::string toString() const { std::string toString() const {
std::stringstream ss; std::stringstream ss;
for(size_t i = 0; i < 6; i++) { for(size_t i = 0; i < 6; i++) {
@@ -33,11 +33,25 @@ struct MACAddress {
class EthernetMessage : public Frame { class EthernetMessage : public Frame {
public: public:
// Standard Ethernet fields
bool preemptionEnabled = false; bool preemptionEnabled = false;
uint8_t preemptionFlags = 0; uint8_t preemptionFlags = 0;
std::optional<uint32_t> fcs; std::optional<uint32_t> fcs;
bool frameTooShort = false; bool frameTooShort = false;
bool noPadding = false; bool noPadding = false;
bool fcsVerified = false;
bool txAborted = false;
bool crcError = false;
bool isT1S = false;
bool isT1SSymbol = false;
bool isT1SBurst = false;
bool txCollision = false;
bool isT1SWake = false;
uint8_t t1sNodeId = 0;
uint8_t t1sBurstCount = 0;
uint8_t t1sSymbolType = 0;
// Accessors // Accessors
const MACAddress& getDestinationMAC() const { return *(const MACAddress*)(data.data() + 0); } const MACAddress& getDestinationMAC() const { return *(const MACAddress*)(data.data() + 0); }
+2 -2
View File
@@ -122,7 +122,7 @@ struct MACsecTxSecY {
struct MACsecTxSa { struct MACsecTxSa {
std::array<uint8_t, 32> sak = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, std::array<uint8_t, 32> sak = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; /*!< 256b SAK: Define the encryption key to be used to encrypte this packet. The lower 128 bits are used for 128-bit ciphers. */ 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; /*!< SAK: For AES-256 fill all 32 bytes. For AES-128 fill only bytes [0..15]; serialize() will mirror them into [16..31] as required by the firmware. */
std::array<uint8_t, 16> hashKey = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; /*!< 128b Hash Key: Key used for authentication. */ std::array<uint8_t, 16> hashKey = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; /*!< 128b Hash Key: Key used for authentication. */
std::array<uint8_t, 12> salt = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; /*!< 96b Salt value: Salt value used in XPN ciphers. */ std::array<uint8_t, 12> salt = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; /*!< 96b Salt value: Salt value used in XPN ciphers. */
uint32_t ssci = 0x01u; /*!< 32b SSCI value: Short Secure Channel Identifier, used in XPN ciphers. */ uint32_t ssci = 0x01u; /*!< 32b SSCI value: Short Secure Channel Identifier, used in XPN ciphers. */
@@ -132,7 +132,7 @@ struct MACsecTxSa {
struct MACsecRxSa { struct MACsecRxSa {
std::array<uint8_t, 32> sak = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, std::array<uint8_t, 32> sak = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; /*!< 256b SAK: Define the encryption key to be used to encrypte this packet. The lower 128 bits are used for 128-bit ciphers. */ 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; /*!< SAK: For AES-256 fill all 32 bytes. For AES-128 fill only bytes [0..15]; serialize() will mirror them into [16..31] as required by the firmware. */
std::array<uint8_t, 16> hashKey = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; /*!< 128b Hash Key: Key used for authentication. */ std::array<uint8_t, 16> hashKey = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; /*!< 128b Hash Key: Key used for authentication. */
std::array<uint8_t, 12> salt = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; /*!< 96b Salt value: Salt value used in XPN ciphers. */ std::array<uint8_t, 12> salt = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; /*!< 96b Salt value: Salt value used in XPN ciphers. */
uint32_t ssci = 0x01u; /*!< 32b SSCI value: Short Secure Channel Identifier, used in XPN ciphers. */ uint32_t ssci = 0x01u; /*!< 32b SSCI value: Short Secure Channel Identifier, used in XPN ciphers. */
+2 -1
View File
@@ -110,7 +110,7 @@ enum class ChipID : uint8_t {
RAD_GALAXY_2_ZMPCHIP_ID = 102, RAD_GALAXY_2_ZMPCHIP_ID = 102,
NewDevice59_MCHIP = 103, NewDevice59_MCHIP = 103,
RADMoon2_Z7010_ZYNQ = 104, RADMoon2_Z7010_ZYNQ = 104,
neoVIFIRE2_CORE_SG4 = 105, neoVIFIRE2_Core_SG4 = 105,
RADBMS_MCHIP = 106, RADBMS_MCHIP = 106,
RADMoon2_ZL_MCHIP = 107, RADMoon2_ZL_MCHIP = 107,
RADGigastar_USBZ_Z7010_ZYNQ = 108, RADGigastar_USBZ_Z7010_ZYNQ = 108,
@@ -128,6 +128,7 @@ enum class ChipID : uint8_t {
RADGALAXY2_SYSMON_CHIP = 123, RADGALAXY2_SYSMON_CHIP = 123,
RADCOMET3_ZCHIP = 125, RADCOMET3_ZCHIP = 125,
Connect_LINUX = 126, Connect_LINUX = 126,
RADMOONT1S_ZCHIP = 130,
RADGigastar2_ZYNQ = 131, RADGigastar2_ZYNQ = 131,
RADGemini_MCHIP = 135, RADGemini_MCHIP = 135,
Invalid = 255 Invalid = 255
+9
View File
@@ -162,6 +162,14 @@ public:
bool hasBootloader() { return !!getBootloader(); } bool hasBootloader() { return !!getBootloader(); }
virtual bool supportsSwVersionValidate() const {
return true;
}
void setBootloaderVersion(const HardwareInfo::Version& version) {
bootloaderVersion = version;
}
static std::string SerialNumToString(uint32_t serial); static std::string SerialNumToString(uint32_t serial);
static uint32_t SerialStringToNum(const std::string& serial); static uint32_t SerialStringToNum(const std::string& serial);
static bool SerialStringIsNumeric(const std::string& serial); static bool SerialStringIsNumeric(const std::string& serial);
@@ -982,6 +990,7 @@ protected:
LEDState ledState; LEDState ledState;
void updateLEDState(); void updateLEDState();
std::optional<HardwareInfo::Version> bootloaderVersion = std::nullopt;
private: private:
neodevice_t data; neodevice_t data;
+295 -13
View File
@@ -54,14 +54,18 @@ enum AELinkMode
AE_LINK_SLAVE AE_LINK_SLAVE
}; };
enum EthLinkSpeed enum EthPhyLinkMode
{ {
ETH_SPEED_10 = 0, ETH_LINK_MODE_AUTO_NEGOTIATION = 0,
ETH_SPEED_100, ETH_LINK_MODE_10MBPS_HALFDUPLEX,
ETH_SPEED_1000, ETH_LINK_MODE_10MBPS_FULLDUPLEX,
ETH_SPEED_2500, ETH_LINK_MODE_100MBPS_HALFDUPLEX,
ETH_SPEED_5000, ETH_LINK_MODE_100MBPS_FULLDUPLEX,
ETH_SPEED_10000, ETH_LINK_MODE_1GBPS_HALFDUPLEX,
ETH_LINK_MODE_1GBPS_FULLDUPLEX,
ETH_LINK_MODE_2_5GBPS_FULLDUPLEX,
ETH_LINK_MODE_5GBPS_FULLDUPLEX,
ETH_LINK_MODE_10GBPS_FULLDUPLEX
}; };
typedef struct typedef struct
@@ -69,7 +73,7 @@ typedef struct
uint16_t networkId; uint16_t networkId;
uint8_t linkStatus; uint8_t linkStatus;
uint8_t linkFullDuplex; uint8_t linkFullDuplex;
uint8_t linkSpeed; // see EthLinkSpeed uint8_t linkSpeed; // 0=10Mbps, 1=100Mbps, 2=1Gbps, 3=2.5Gbps, 4=5Gbps, 5=10Gbps
int8_t linkMode; // for automotive networks - see AELinkMode int8_t linkMode; // for automotive networks - see AELinkMode
} EthernetNetworkStatus; } EthernetNetworkStatus;
@@ -85,7 +89,10 @@ typedef struct
uint8_t TqSync; uint8_t TqSync;
uint16_t BRP; uint16_t BRP;
uint8_t auto_baud; uint8_t auto_baud;
uint8_t innerFrameDelay25us; uint8_t innerFrameDelay25us : 4;
uint8_t rsvd : 1;
uint8_t disableRetransmission : 1;
uint8_t canClk : 2;
} CAN_SETTINGS; } CAN_SETTINGS;
#define CAN_SETTINGS_SIZE 12 #define CAN_SETTINGS_SIZE 12
static_assert(sizeof(CAN_SETTINGS) == CAN_SETTINGS_SIZE, "CAN_SETTINGS is the wrong size!"); static_assert(sizeof(CAN_SETTINGS) == CAN_SETTINGS_SIZE, "CAN_SETTINGS is the wrong size!");
@@ -366,6 +373,11 @@ typedef struct SERDESGEN_SETTINGS_t
#define ETHERNET_SETTINGS2_FLAG_DEVICE_HOSTING_ENABLE 0x10 #define ETHERNET_SETTINGS2_FLAG_DEVICE_HOSTING_ENABLE 0x10
#define ETHERNET_SETTINGS2_FLAG_COMM_IN_USE 0x80 #define ETHERNET_SETTINGS2_FLAG_COMM_IN_USE 0x80
// ETHERNET_SETTINGS2 flags2 bit definitions
#define ETHERNET_SETTINGS2_FLAGS2_LINK_MODE_SLAVE 0x01 // bit0: 0=master, 1=slave
#define ETHERNET_SETTINGS2_FLAGS2_PHY_MODE_LEGACY 0x02 // bit1: 0=IEEE, 1=legacy
#define ETHERNET_SETTINGS2_FLAGS2_LINK_MODE_AUTO 0x04 // bit2: auto master/slave negotiation
typedef struct ETHERNET_SETTINGS2_t typedef struct ETHERNET_SETTINGS2_t
{ {
/* bit0: 0=half duplex, 1=full duplex /* bit0: 0=half duplex, 1=full duplex
@@ -379,7 +391,13 @@ typedef struct ETHERNET_SETTINGS2_t
uint32_t ip_addr; uint32_t ip_addr;
uint32_t netmask; uint32_t netmask;
uint32_t gateway; uint32_t gateway;
uint8_t rsvd[2]; /* FLAGS2
* bit0: link mode - 0=master, 1=slave
* bit1: PHY mode - 0=IEEE, 1=legacy
* bit2: auto master/slave
*/
uint8_t flags2;
uint8_t rsvd;
} ETHERNET_SETTINGS2; } ETHERNET_SETTINGS2;
#define ETHERNET_SETTINGS2_SIZE 16 #define ETHERNET_SETTINGS2_SIZE 16
@@ -732,6 +750,16 @@ typedef struct
namespace icsneo { namespace icsneo {
enum class MiscIOAnalogVoltage : uint8_t
{
V0 = 0,
V1 = 1,
V2 = 2,
V3 = 3,
V4 = 4,
V5 = 5
};
class IDeviceSettings { class IDeviceSettings {
public: public:
using TerminationGroup = std::vector<Network>; using TerminationGroup = std::vector<Network>;
@@ -808,6 +836,26 @@ public:
return reinterpret_cast<LIN_SETTINGS*>((void*)(settings.data() + (offset - settingsInDeviceRAM.data()))); return reinterpret_cast<LIN_SETTINGS*>((void*)(settings.data() + (offset - settingsInDeviceRAM.data())));
} }
virtual const ETHERNET_SETTINGS2* getEthernetSettingsFor(Network net) const { (void)net; return nullptr; }
ETHERNET_SETTINGS2* getMutableEthernetSettingsFor(Network net) {
if(disabled || readonly)
return nullptr;
const uint8_t* offset = (const uint8_t*)getEthernetSettingsFor(net);
if(offset == nullptr)
return nullptr;
return reinterpret_cast<ETHERNET_SETTINGS2*>((void*)(settings.data() + (offset - settingsInDeviceRAM.data())));
}
virtual const AE_SETTINGS* getAESettingsFor(Network net) const { (void)net; return nullptr; }
AE_SETTINGS* getMutableAESettingsFor(Network net) {
if(disabled || readonly)
return nullptr;
const uint8_t* offset = (const uint8_t*)getAESettingsFor(net);
if(offset == nullptr)
return nullptr;
return reinterpret_cast<AE_SETTINGS*>((void*)(settings.data() + (offset - settingsInDeviceRAM.data())));
}
/** /**
* Some devices have groupings of networks, where software * Some devices have groupings of networks, where software
* switchable termination can only be applied to one network * switchable termination can only be applied to one network
@@ -908,38 +956,246 @@ public:
*/ */
bool setLINCommanderResponseTimeFor(Network net, uint8_t bits); bool setLINCommanderResponseTimeFor(Network net, uint8_t bits);
/**
* Set PHY role (Master/Slave/Auto) for switch devices (Epsilon/XL, Jupiter, etc) using port index.
* For all other devices, use setPhyRoleFor() instead.
*
* @param index Port index (0-based)
* @param mode Master/Slave/Auto role
* @return true if successful
*/
virtual bool setPhyMode(uint8_t index, AELinkMode mode) { virtual bool setPhyMode(uint8_t index, AELinkMode mode) {
(void)index, (void)mode; (void)index, (void)mode;
return false; return false;
} }
/**
* Enable/disable PHY for switch devices (Epsilon/XL, Jupiter, etc) using port index.
* For all other devices, use setPhyEnableFor() instead.
*
* @param index Port index (0-based)
* @param enable True to enable, false to disable
* @return true if successful
*/
virtual bool setPhyEnable(uint8_t index, bool enable) { virtual bool setPhyEnable(uint8_t index, bool enable) {
(void)index, (void)enable; (void)index, (void)enable;
return false; return false;
} }
virtual bool setPhySpeed(uint8_t index, EthLinkSpeed speed) { /**
(void)index, (void)speed; * Set PHY link mode (speed and duplex) for switch devices (Epsilon/XL, Jupiter, etc) using port index.
* For all other devices, use setPhyLinkModeFor() instead.
*
* @param index Port index (0-based)
* @param mode Link mode (speed + duplex combination)
* @return true if successful
*/
virtual bool setPhySpeed(uint8_t index, EthPhyLinkMode mode) {
(void)index, (void)mode;
return false; return false;
} }
/**
* Get PHY role (Master/Slave/Auto) for switch devices (Epsilon/XL, Jupiter, etc) using port index.
* For all other devices, use getPhyRoleFor() instead.
*
* @param index Port index (0-based)
* @return Current role, or nullopt if not available
*/
virtual std::optional<AELinkMode> getPhyMode(uint8_t index) { virtual std::optional<AELinkMode> getPhyMode(uint8_t index) {
(void)index; (void)index;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning); report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt; return std::nullopt;
} }
/**
* Get PHY enable state for switch devices (Epsilon/XL, Jupiter, etc) using port index.
* For all other devices, use getPhyEnableFor() instead.
*
* @param index Port index (0-based)
* @return True if enabled, false if disabled, nullopt if not available
*/
virtual std::optional<bool> getPhyEnable(uint8_t index) { virtual std::optional<bool> getPhyEnable(uint8_t index) {
(void)index; (void)index;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning); report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt; return std::nullopt;
} }
virtual std::optional<EthLinkSpeed> getPhySpeed(uint8_t index) { /**
* Get PHY link mode (speed and duplex) for switch devices (Epsilon/XL, Jupiter, etc) using port index.
* For all other devices, use getPhyLinkModeFor() instead.
*
* @param index Port index (0-based)
* @return Current link mode, or nullopt if not available
*/
virtual std::optional<EthPhyLinkMode> getPhySpeed(uint8_t index) {
(void)index; (void)index;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning); report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt; return std::nullopt;
} }
/**
* Set PHY role (Master/Slave/Auto) for network-based devices.
* For switch devices, use setPhyMode() with port index instead.
*
* @param net Network ID
* @param mode Master/Slave/Auto role
* @return true if successful
*/
virtual bool setPhyRoleFor(Network net, AELinkMode mode) {
(void)net, (void)mode;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return false;
}
/**
* Enable/disable PHY for network-based devices.
* For switch devices, use setPhyEnable() with port index instead.
*
* @param net Network ID
* @param enable True to enable, false to disable
* @return true if successful
*/
virtual bool setPhyEnableFor(Network net, bool enable) {
(void)net, (void)enable;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return false;
}
/**
* Get PHY role (Master/Slave/Auto) for network-based devices.
* For switch devices, use getPhyMode() with port index instead.
*
* @param net Network ID
* @return Current role, or nullopt if not available
*/
virtual std::optional<AELinkMode> getPhyRoleFor(Network net) const {
(void)net;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
/**
* Get PHY enable state for network-based devices.
* For switch devices, use getPhyEnable() with port index instead.
*
* @param net Network ID
* @return True if enabled, false if disabled, nullopt if not available
*/
virtual std::optional<bool> getPhyEnableFor(Network net) const {
(void)net;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
/**
* Get supported PHY link modes (combined speed+duplex) for a network.
* Each mode represents a valid hardware configuration.
*
* @param net The network to query
* @return Vector of supported modes, empty if network doesn't support PHY settings
*/
virtual std::vector<EthPhyLinkMode> getSupportedPhyLinkModesFor(Network net) const {
(void)net;
return {}; // Default: no PHY support
}
/**
* Set PHY link mode (speed and duplex together).
*
* @param net The network to configure
* @param mode The link mode to set
* @return true if successful, false if mode not supported or error occurred
*/
virtual bool setPhyLinkModeFor(Network net, EthPhyLinkMode mode) {
(void)net; (void)mode;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return false;
}
/**
* Get current PHY link mode.
*
* @param net The network to query
* @return Current link mode, or nullopt if not available or not configured
*/
virtual std::optional<EthPhyLinkMode> getPhyLinkModeFor(Network net) const {
(void)net;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
virtual std::optional<bool> isT1SPLCAEnabledFor(Network net) const {
(void)net;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
virtual bool setT1SPLCAFor(Network net, bool enable) {
(void)net; (void)enable;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return false;
}
virtual std::optional<uint8_t> getT1SLocalIDFor(Network net) const {
(void)net;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
virtual bool setT1SLocalIDFor(Network net, uint8_t id) {
(void)net; (void)id;
return false;
}
virtual std::optional<uint8_t> getT1SMaxNodesFor(Network net) const {
(void)net;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
virtual bool setT1SMaxNodesFor(Network net, uint8_t nodes) {
(void)net; (void)nodes;
return false;
}
virtual std::optional<uint8_t> getT1STxOppTimerFor(Network net) const {
(void)net;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
virtual bool setT1STxOppTimerFor(Network net, uint8_t timer) {
(void)net; (void)timer;
return false;
}
virtual std::optional<uint8_t> getT1SMaxBurstFor(Network net) const {
(void)net;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
virtual bool setT1SMaxBurstFor(Network net, uint8_t burst) {
(void)net; (void)burst;
return false;
}
virtual std::optional<uint8_t> getT1SBurstTimerFor(Network net) const {
(void)net;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
virtual bool setT1SBurstTimerFor(Network net, uint8_t timer) {
(void)net; (void)timer;
return false;
}
virtual bool setMiscIOAnalogOutputEnabled(uint8_t pin, bool enabled);
virtual bool setMiscIOAnalogOutput(uint8_t pin, MiscIOAnalogVoltage voltage);
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()); }
@@ -981,6 +1237,32 @@ protected:
return nullptr; return nullptr;
return reinterpret_cast<ICSNEO_UNALIGNED(uint64_t*)>((void*)(settings.data() + (offset - settingsInDeviceRAM.data()))); return reinterpret_cast<ICSNEO_UNALIGNED(uint64_t*)>((void*)(settings.data() + (offset - settingsInDeviceRAM.data())));
} }
static bool SetNetworkEnabled(uint64_t* bitfields, size_t count, uint64_t networkID) {
const size_t index = networkID / 64;
const size_t offset = networkID & 0x3F;
if (index >= count)
return false;
bitfields[index] |= (1ULL << offset);
return true;
}
static bool ClearNetworkEnabled(uint64_t* bitfields, size_t count, uint64_t networkID) {
const size_t index = networkID / 64;
const size_t offset = networkID & 0x3F;
if (index >= count)
return false;
bitfields[index] &= ~(1ULL << offset);
return true;
}
static bool GetNetworkEnabled(const uint64_t* bitfields, size_t count, uint64_t networkID) {
const size_t index = networkID / 64;
const size_t offset = networkID & 0x3F;
if (index >= count)
return false;
return (bitfields[index] & (1ULL << offset)) != 0;
}
}; };
} }
@@ -98,13 +98,15 @@ public:
} }
CoreChipVariant getCoreChipVariant() { CoreChipVariant getCoreChipVariant() {
const auto& hardwareInfo = getHardwareInfo(std::chrono::milliseconds(1000)); if(!bootloaderVersion.has_value()) {
if(!hardwareInfo) { const auto& hardwareInfo = getHardwareInfo(std::chrono::milliseconds(1000));
chipVariant = CoreChipVariant::Invalid; if(!hardwareInfo) {
return chipVariant; chipVariant = CoreChipVariant::Invalid;
return chipVariant;
}
setBootloaderVersion(hardwareInfo->bootloaderVersion);
} }
const auto& bootloaderVersion = hardwareInfo->bootloaderVersion; if(bootloaderVersion->major >= CORE_SG4_BL_MAJOR_VERSION_CUTOFF) {
if(bootloaderVersion.major >= CORE_SG4_BL_MAJOR_VERSION_CUTOFF) {
chipVariant = CoreChipVariant::Core_SG4; chipVariant = CoreChipVariant::Core_SG4;
} else { } else {
chipVariant = CoreChipVariant::Core; chipVariant = CoreChipVariant::Core;
@@ -122,7 +124,7 @@ public:
static std::vector<ChipInfo> chipsSG4 = { static std::vector<ChipInfo> chipsSG4 = {
{ChipID::neoVIFIRE2_MCHIP, true, "MCHIP", "fire2_mchip_ief", 0, FirmwareType::IEF}, {ChipID::neoVIFIRE2_MCHIP, true, "MCHIP", "fire2_mchip_ief", 0, FirmwareType::IEF},
{ChipID::neoVIFIRE2_ZYNQ, true, "ZCHIP", "fire2_zchip_ief", 1, FirmwareType::IEF}, {ChipID::neoVIFIRE2_ZYNQ, true, "ZCHIP", "fire2_zchip_ief", 1, FirmwareType::IEF},
{ChipID::neoVIFIRE2_CORE_SG4, true, "Core", "fire2_core_sg4", 2, FirmwareType::IEF} {ChipID::neoVIFIRE2_Core_SG4, true, "Core", "fire2_core_sg4", 2, FirmwareType::IEF}
}; };
if(chipVariant == CoreChipVariant::Core_SG4) { if(chipVariant == CoreChipVariant::Core_SG4) {
@@ -137,8 +139,8 @@ public:
pipeline.add<EnterBootloaderPhase>() pipeline.add<EnterBootloaderPhase>()
.add<FlashPhase>(ChipID::neoVIFIRE2_MCHIP, BootloaderCommunication::RED); .add<FlashPhase>(ChipID::neoVIFIRE2_MCHIP, BootloaderCommunication::RED);
if(chipVariant == CoreChipVariant::Core_SG4) { if(chipVariant == CoreChipVariant::Core_SG4) {
pipeline.add<FlashPhase>(ChipID::neoVIFIRE2_CORE_SG4, BootloaderCommunication::REDCore, false, false); pipeline.add<FlashPhase>(ChipID::neoVIFIRE2_Core_SG4, BootloaderCommunication::REDCore, false, false);
} else { } else if(chipVariant == CoreChipVariant::Core) {
pipeline.add<FlashPhase>(ChipID::neoVIFIRE2_Core, BootloaderCommunication::REDCore, false, false); pipeline.add<FlashPhase>(ChipID::neoVIFIRE2_Core, BootloaderCommunication::REDCore, false, false);
} }
pipeline.add<FlashPhase>(ChipID::neoVIFIRE2_ZYNQ, BootloaderCommunication::RED, false, false) pipeline.add<FlashPhase>(ChipID::neoVIFIRE2_ZYNQ, BootloaderCommunication::RED, false, false)
@@ -147,6 +149,10 @@ public:
return pipeline; return pipeline;
} }
bool supportsSwVersionValidate() const override {
return bootloaderVersion.has_value() && (bootloaderVersion->major > 4 || (bootloaderVersion->major == 4 && bootloaderVersion->minor >= 3));
}
std::vector<VersionReport> getChipVersions(bool refreshComponents = true) override { std::vector<VersionReport> getChipVersions(bool refreshComponents = true) override {
if(chipVariant == CoreChipVariant::Invalid) { if(chipVariant == CoreChipVariant::Invalid) {
getCoreChipVariant(); getCoreChipVariant();
@@ -188,6 +188,22 @@ public:
return &(cfg->can7); return &(cfg->can7);
case Network::NetID::DWCAN_07: case Network::NetID::DWCAN_07:
return &(cfg->can8); return &(cfg->can8);
case Network::NetID::DWCAN_09:
return &(cfg->can9);
case Network::NetID::DWCAN_10:
return &(cfg->can10);
case Network::NetID::DWCAN_11:
return &(cfg->can11);
case Network::NetID::DWCAN_12:
return &(cfg->can12);
case Network::NetID::DWCAN_13:
return &(cfg->can13);
case Network::NetID::DWCAN_14:
return &(cfg->can14);
case Network::NetID::DWCAN_15:
return &(cfg->can15);
case Network::NetID::DWCAN_16:
return &(cfg->can16);
default: default:
return nullptr; return nullptr;
} }
@@ -213,6 +229,22 @@ public:
return &(cfg->canfd7); return &(cfg->canfd7);
case Network::NetID::DWCAN_07: case Network::NetID::DWCAN_07:
return &(cfg->canfd8); return &(cfg->canfd8);
case Network::NetID::DWCAN_09:
return &(cfg->canfd9);
case Network::NetID::DWCAN_10:
return &(cfg->canfd10);
case Network::NetID::DWCAN_11:
return &(cfg->canfd11);
case Network::NetID::DWCAN_12:
return &(cfg->canfd12);
case Network::NetID::DWCAN_13:
return &(cfg->canfd13);
case Network::NetID::DWCAN_14:
return &(cfg->canfd14);
case Network::NetID::DWCAN_15:
return &(cfg->canfd15);
case Network::NetID::DWCAN_16:
return &(cfg->canfd16);
default: default:
return nullptr; return nullptr;
} }
@@ -147,6 +147,10 @@ protected:
return ret; return ret;
} }
size_t getDiskCount() const override {
return 2;
}
bool supportsNetworkMutex() const override { return true; } bool supportsNetworkMutex() const override { return true; }
}; };
@@ -102,6 +102,15 @@ protected:
bool supportsEraseMemory() const override { bool supportsEraseMemory() const override {
return true; return true;
} }
size_t getDiskCount() const override {
return 2;
}
bool supportsNetworkMutex() const override {
return true;
}
}; };
} }
@@ -257,6 +257,153 @@ public:
} }
} }
std::optional<bool> isT1SPLCAEnabledFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional((t1s->flags & ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA) != 0);
}
bool setT1SPLCAFor(Network net, bool enable) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
if(enable)
t1s->flags |= ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
else
t1s->flags &= ~ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
return true;
}
std::optional<uint8_t> getT1SLocalIDFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->local_id);
}
bool setT1SLocalIDFor(Network net, uint8_t id) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->local_id = id;
return true;
}
std::optional<uint8_t> getT1SMaxNodesFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_num_nodes);
}
bool setT1SMaxNodesFor(Network net, uint8_t nodes) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_num_nodes = nodes;
return true;
}
std::optional<uint8_t> getT1STxOppTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->to_timer);
}
bool setT1STxOppTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->to_timer = timer;
return true;
}
std::optional<uint8_t> getT1SMaxBurstFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_burst_count);
}
bool setT1SMaxBurstFor(Network net, uint8_t burst) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_burst_count = burst;
return true;
}
std::optional<uint8_t> getT1SBurstTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->burst_timer);
}
bool setT1SBurstTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->burst_timer = timer;
return true;
}
private:
const ETHERNET10T1S_SETTINGS* getT1SSettingsFor(Network net) const {
auto cfg = getStructurePointer<neovifire3t1slin_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_01: return &(cfg->t1s1);
case Network::NetID::AE_02: return &(cfg->t1s2);
case Network::NetID::AE_03: return &(cfg->t1s3);
case Network::NetID::AE_04: return &(cfg->t1s4);
case Network::NetID::AE_05: return &(cfg->t1s5);
case Network::NetID::AE_06: return &(cfg->t1s6);
case Network::NetID::AE_07: return &(cfg->t1s7);
case Network::NetID::AE_08: return &(cfg->t1s8);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
ETHERNET10T1S_SETTINGS* getMutableT1SSettingsFor(Network net) {
auto cfg = getMutableStructurePointer<neovifire3t1slin_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_01: return &(cfg->t1s1);
case Network::NetID::AE_02: return &(cfg->t1s2);
case Network::NetID::AE_03: return &(cfg->t1s3);
case Network::NetID::AE_04: return &(cfg->t1s4);
case Network::NetID::AE_05: return &(cfg->t1s5);
case Network::NetID::AE_06: return &(cfg->t1s6);
case Network::NetID::AE_07: return &(cfg->t1s7);
case Network::NetID::AE_08: return &(cfg->t1s8);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
protected: protected:
ICSNEO_UNALIGNED(const uint64_t*) getTerminationEnables() const override { ICSNEO_UNALIGNED(const uint64_t*) getTerminationEnables() const override {
auto cfg = getStructurePointer<neovifire3t1slin_settings_t>(); auto cfg = getStructurePointer<neovifire3t1slin_settings_t>();
+1 -1
View File
@@ -58,7 +58,7 @@ public:
return BootloaderPipeline() return BootloaderPipeline()
.add<EnterBootloaderPhase>() .add<EnterBootloaderPhase>()
.add<FlashPhase>(ChipID::RADA2B_ZCHIP, BootloaderCommunication::RAD) .add<FlashPhase>(ChipID::RADA2B_ZCHIP, BootloaderCommunication::RAD)
// .add<ReconnectPhase>() .add<EnterApplicationPhase>(ChipID::RADA2B_ZCHIP)
.add<WaitPhase>(std::chrono::milliseconds(3000)); .add<WaitPhase>(std::chrono::milliseconds(3000));
} }
protected: protected:
@@ -45,6 +45,7 @@ public:
return BootloaderPipeline() return BootloaderPipeline()
.add<EnterBootloaderPhase>() .add<EnterBootloaderPhase>()
.add<FlashPhase>(ChipID::RADComet_ZYNQ, BootloaderCommunication::RAD) .add<FlashPhase>(ChipID::RADComet_ZYNQ, BootloaderCommunication::RAD)
.add<EnterApplicationPhase>(ChipID::RADComet_ZYNQ)
.add<WaitPhase>(std::chrono::milliseconds(3000)) .add<WaitPhase>(std::chrono::milliseconds(3000))
.add<ReconnectPhase>(); .add<ReconnectPhase>();
} }
@@ -116,6 +116,141 @@ public:
return nullptr; return nullptr;
} }
} }
std::optional<bool> isT1SPLCAEnabledFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional((t1s->flags & ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA) != 0);
}
bool setT1SPLCAFor(Network net, bool enable) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
if(enable)
t1s->flags |= ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
else
t1s->flags &= ~ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
return true;
}
std::optional<uint8_t> getT1SLocalIDFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->local_id);
}
bool setT1SLocalIDFor(Network net, uint8_t id) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->local_id = id;
return true;
}
std::optional<uint8_t> getT1SMaxNodesFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_num_nodes);
}
bool setT1SMaxNodesFor(Network net, uint8_t nodes) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_num_nodes = nodes;
return true;
}
std::optional<uint8_t> getT1STxOppTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->to_timer);
}
bool setT1STxOppTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->to_timer = timer;
return true;
}
std::optional<uint8_t> getT1SMaxBurstFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_burst_count);
}
bool setT1SMaxBurstFor(Network net, uint8_t burst) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_burst_count = burst;
return true;
}
std::optional<uint8_t> getT1SBurstTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->burst_timer);
}
bool setT1SBurstTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->burst_timer = timer;
return true;
}
private:
const ETHERNET10T1S_SETTINGS* getT1SSettingsFor(Network net) const {
auto cfg = getStructurePointer<radcomet_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_02: return &(cfg->t1s1);
case Network::NetID::AE_03: return &(cfg->t1s2);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
ETHERNET10T1S_SETTINGS* getMutableT1SSettingsFor(Network net) {
auto cfg = getMutableStructurePointer<radcomet_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_02: return &(cfg->t1s1);
case Network::NetID::AE_03: return &(cfg->t1s2);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
}; };
} }
@@ -69,6 +69,7 @@ public:
return BootloaderPipeline() return BootloaderPipeline()
.add<EnterBootloaderPhase>() .add<EnterBootloaderPhase>()
.add<FlashPhase>(ChipID::RADCOMET3_ZCHIP, BootloaderCommunication::RAD) .add<FlashPhase>(ChipID::RADCOMET3_ZCHIP, BootloaderCommunication::RAD)
.add<EnterApplicationPhase>(ChipID::RADCOMET3_ZCHIP)
.add<WaitPhase>(std::chrono::milliseconds(5000)) .add<WaitPhase>(std::chrono::milliseconds(5000))
.add<ReconnectPhase>(); .add<ReconnectPhase>();
} }
@@ -113,6 +113,464 @@ public:
return nullptr; return nullptr;
} }
} }
const ETHERNET_SETTINGS2* getEthernetSettingsFor(Network net) const override {
auto cfg = getStructurePointer<radcomet3_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::ETHERNET_01:
return &(cfg->ethernet);
case Network::NetID::AE_01:
return &(cfg->ethT1);
case Network::NetID::AE_02:
return &(cfg->ethT1s1);
case Network::NetID::AE_03:
return &(cfg->ethT1s2);
case Network::NetID::AE_04:
return &(cfg->ethT1s3);
case Network::NetID::AE_05:
return &(cfg->ethT1s4);
case Network::NetID::AE_06:
return &(cfg->ethT1s5);
case Network::NetID::AE_07:
return &(cfg->ethT1s6);
default:
return nullptr;
}
}
const AE_SETTINGS* getAESettingsFor(Network net) const override {
auto cfg = getStructurePointer<radcomet3_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_01:
return &(cfg->ae_01);
default:
return nullptr;
}
}
std::optional<bool> isT1SPLCAEnabledFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional((t1s->flags & ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA) != 0);
}
bool setT1SPLCAFor(Network net, bool enable) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
if(enable)
t1s->flags |= ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
else
t1s->flags &= ~ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
return true;
}
std::optional<uint8_t> getT1SLocalIDFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->local_id);
}
bool setT1SLocalIDFor(Network net, uint8_t id) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->local_id = id;
return true;
}
std::optional<uint8_t> getT1SMaxNodesFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_num_nodes);
}
bool setT1SMaxNodesFor(Network net, uint8_t nodes) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_num_nodes = nodes;
return true;
}
std::optional<uint8_t> getT1STxOppTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->to_timer);
}
bool setT1STxOppTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->to_timer = timer;
return true;
}
std::optional<uint8_t> getT1SMaxBurstFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_burst_count);
}
bool setT1SMaxBurstFor(Network net, uint8_t burst) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_burst_count = burst;
return true;
}
std::optional<uint8_t> getT1SBurstTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->burst_timer);
}
bool setT1SBurstTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->burst_timer = timer;
return true;
}
bool setPhyRoleFor(Network net, AELinkMode mode) override {
if (mode != AE_LINK_AUTO && mode != AE_LINK_MASTER && mode != AE_LINK_SLAVE) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
AE_SETTINGS* ae = getMutableAESettingsFor(net);
if (ae == nullptr) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
ae->ucConfigMode = static_cast<uint8_t>(mode);
ETHERNET_SETTINGS2* ethSettings = getMutableEthernetSettingsFor(net);
if (ethSettings == nullptr) {
return false;
}
uint8_t& flags2 = ethSettings->flags2;
switch (mode) {
case AE_LINK_AUTO:
flags2 |= ETHERNET_SETTINGS2_FLAGS2_LINK_MODE_AUTO;
break;
case AE_LINK_MASTER:
flags2 &= ~ETHERNET_SETTINGS2_FLAGS2_LINK_MODE_AUTO;
flags2 &= ~ETHERNET_SETTINGS2_FLAGS2_LINK_MODE_SLAVE;
break;
case AE_LINK_SLAVE:
flags2 &= ~ETHERNET_SETTINGS2_FLAGS2_LINK_MODE_AUTO;
flags2 |= ETHERNET_SETTINGS2_FLAGS2_LINK_MODE_SLAVE;
break;
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
return true;
}
bool setPhyEnableFor(Network net, bool enable) override {
auto cfg = getMutableStructurePointer<radcomet3_settings_t>();
if (cfg == nullptr)
return false;
if (net.getType() != Network::Type::Ethernet && net.getType() != Network::Type::AutomotiveEthernet) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
auto coreMini = net.getCoreMini();
if (!coreMini.has_value()) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
const uint64_t networkID = static_cast<uint64_t>(coreMini.value());
uint64_t bitfields[2] = { cfg->network_enables, cfg->network_enables_2 };
const bool success = enable ?
SetNetworkEnabled(bitfields, 2, networkID) :
ClearNetworkEnabled(bitfields, 2, networkID);
if (!success) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
cfg->network_enables = bitfields[0];
cfg->network_enables_2 = bitfields[1];
return true;
}
std::optional<AELinkMode> getPhyRoleFor(Network net) const override {
const AE_SETTINGS* ae = getAESettingsFor(net);
if (ae == nullptr) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
switch (ae->ucConfigMode) {
case 0:
return std::make_optional(AE_LINK_AUTO);
case 1:
return std::make_optional(AE_LINK_MASTER);
case 2:
return std::make_optional(AE_LINK_SLAVE);
default:
return std::make_optional(AE_LINK_AUTO);
}
}
std::optional<bool> getPhyEnableFor(Network net) const override {
auto cfg = getStructurePointer<radcomet3_settings_t>();
if (cfg == nullptr) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
return std::nullopt;
}
if (net.getType() != Network::Type::Ethernet && net.getType() != Network::Type::AutomotiveEthernet) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
auto coreMini = net.getCoreMini();
if (!coreMini.has_value()) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
const uint64_t networkID = static_cast<uint64_t>(coreMini.value());
const uint64_t bitfields[2] = { cfg->network_enables, cfg->network_enables_2 };
return GetNetworkEnabled(bitfields, 2, networkID);
}
std::vector<EthPhyLinkMode> getSupportedPhyLinkModesFor(Network net) const override {
switch(net.getNetID()) {
case Network::NetID::ETHERNET_01:
return {
ETH_LINK_MODE_AUTO_NEGOTIATION,
ETH_LINK_MODE_10MBPS_FULLDUPLEX,
ETH_LINK_MODE_100MBPS_FULLDUPLEX,
ETH_LINK_MODE_1GBPS_FULLDUPLEX
};
case Network::NetID::AE_01:
return {
ETH_LINK_MODE_AUTO_NEGOTIATION,
ETH_LINK_MODE_100MBPS_FULLDUPLEX,
ETH_LINK_MODE_1GBPS_FULLDUPLEX
};
case Network::NetID::AE_02:
case Network::NetID::AE_03:
case Network::NetID::AE_04:
case Network::NetID::AE_05:
case Network::NetID::AE_06:
case Network::NetID::AE_07:
return {ETH_LINK_MODE_10MBPS_HALFDUPLEX};
default:
return {};
}
}
bool setPhyLinkModeFor(Network net, EthPhyLinkMode mode) override {
auto supported = getSupportedPhyLinkModesFor(net);
if (std::find(supported.begin(), supported.end(), mode) == supported.end()) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
auto cfg = getMutableStructurePointer<radcomet3_settings_t>();
if (cfg == nullptr)
return false;
if (net.getNetID() == Network::NetID::AE_01) {
AE_SETTINGS* ae = getMutableAESettingsFor(net);
if (ae == nullptr) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
switch (mode) {
case ETH_LINK_MODE_AUTO_NEGOTIATION:
ae->link_spd = 3;
cfg->ethT1.link_speed = 2;
cfg->ethT1.flags |= ETHERNET_SETTINGS2_FLAG_AUTO_NEG;
cfg->ethT1.flags |= ETHERNET_SETTINGS2_FLAG_FULL_DUPLEX;
break;
case ETH_LINK_MODE_100MBPS_FULLDUPLEX:
ae->link_spd = 1;
cfg->ethT1.link_speed = 1;
cfg->ethT1.flags &= ~ETHERNET_SETTINGS2_FLAG_AUTO_NEG;
cfg->ethT1.flags |= ETHERNET_SETTINGS2_FLAG_FULL_DUPLEX;
break;
case ETH_LINK_MODE_1GBPS_FULLDUPLEX:
ae->link_spd = 2;
cfg->ethT1.link_speed = 2;
cfg->ethT1.flags &= ~ETHERNET_SETTINGS2_FLAG_AUTO_NEG;
cfg->ethT1.flags |= ETHERNET_SETTINGS2_FLAG_FULL_DUPLEX;
break;
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
} else if (net.getNetID() == Network::NetID::ETHERNET_01) {
switch (mode) {
case ETH_LINK_MODE_AUTO_NEGOTIATION:
cfg->ethernet.flags |= ETHERNET_SETTINGS2_FLAG_AUTO_NEG;
cfg->ethernet.link_speed = 2;
cfg->ethernet.flags |= ETHERNET_SETTINGS2_FLAG_FULL_DUPLEX;
break;
case ETH_LINK_MODE_10MBPS_FULLDUPLEX:
cfg->ethernet.link_speed = 0;
cfg->ethernet.flags &= ~ETHERNET_SETTINGS2_FLAG_AUTO_NEG;
cfg->ethernet.flags |= ETHERNET_SETTINGS2_FLAG_FULL_DUPLEX;
break;
case ETH_LINK_MODE_100MBPS_FULLDUPLEX:
cfg->ethernet.link_speed = 1;
cfg->ethernet.flags &= ~ETHERNET_SETTINGS2_FLAG_AUTO_NEG;
cfg->ethernet.flags |= ETHERNET_SETTINGS2_FLAG_FULL_DUPLEX;
break;
case ETH_LINK_MODE_1GBPS_FULLDUPLEX:
cfg->ethernet.link_speed = 2;
cfg->ethernet.flags &= ~ETHERNET_SETTINGS2_FLAG_AUTO_NEG;
cfg->ethernet.flags |= ETHERNET_SETTINGS2_FLAG_FULL_DUPLEX;
break;
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
}
return true;
}
std::optional<EthPhyLinkMode> getPhyLinkModeFor(Network net) const override {
auto cfg = getStructurePointer<radcomet3_settings_t>();
if (cfg == nullptr) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
if (net.getNetID() == Network::NetID::ETHERNET_01) {
if (cfg->ethernet.flags & ETHERNET_SETTINGS2_FLAG_AUTO_NEG) {
return ETH_LINK_MODE_AUTO_NEGOTIATION;
}
bool fullDuplex = (cfg->ethernet.flags & ETHERNET_SETTINGS2_FLAG_FULL_DUPLEX) != 0;
switch (cfg->ethernet.link_speed) {
case 0:
return fullDuplex ? ETH_LINK_MODE_10MBPS_FULLDUPLEX
: ETH_LINK_MODE_10MBPS_HALFDUPLEX;
case 1:
return fullDuplex ? ETH_LINK_MODE_100MBPS_FULLDUPLEX
: ETH_LINK_MODE_100MBPS_HALFDUPLEX;
case 2:
return fullDuplex ? ETH_LINK_MODE_1GBPS_FULLDUPLEX
: ETH_LINK_MODE_1GBPS_HALFDUPLEX;
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
} else if (net.getNetID() == Network::NetID::AE_01) {
const AE_SETTINGS* ae = &cfg->ae_01;
// Check auto-negotiate
if (ae->link_spd == 3 || (cfg->ethT1.flags & ETHERNET_SETTINGS2_FLAG_AUTO_NEG)) {
return ETH_LINK_MODE_AUTO_NEGOTIATION;
}
// T1 is always full-duplex
switch (ae->link_spd) {
case 1: // 100 Mbps
return ETH_LINK_MODE_100MBPS_FULLDUPLEX;
case 2: // 1000 Mbps
return ETH_LINK_MODE_1GBPS_FULLDUPLEX;
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
} else if (net.getNetID() >= Network::NetID::AE_02 && net.getNetID() <= Network::NetID::AE_07) {
// 10BASE-T1S ports - half-duplex only
return ETH_LINK_MODE_10MBPS_HALFDUPLEX;
} else {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
}
private:
const ETHERNET10T1S_SETTINGS* getT1SSettingsFor(Network net) const {
auto cfg = getStructurePointer<radcomet3_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_02: return &(cfg->t1s1);
case Network::NetID::AE_03: return &(cfg->t1s2);
case Network::NetID::AE_04: return &(cfg->t1s3);
case Network::NetID::AE_05: return &(cfg->t1s4);
case Network::NetID::AE_06: return &(cfg->t1s5);
case Network::NetID::AE_07: return &(cfg->t1s6);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
ETHERNET10T1S_SETTINGS* getMutableT1SSettingsFor(Network net) {
auto cfg = getMutableStructurePointer<radcomet3_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_02: return &(cfg->t1s1);
case Network::NetID::AE_03: return &(cfg->t1s2);
case Network::NetID::AE_04: return &(cfg->t1s3);
case Network::NetID::AE_05: return &(cfg->t1s4);
case Network::NetID::AE_06: return &(cfg->t1s5);
case Network::NetID::AE_07: return &(cfg->t1s6);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
}; };
} }
@@ -168,7 +168,7 @@ public:
return true; return true;
} }
bool setPhySpeed(uint8_t index, EthLinkSpeed speed) override { bool setPhySpeed(uint8_t index, EthPhyLinkMode mode) override {
if (index > RADEPSILON_MAX_PHY) { if (index > RADEPSILON_MAX_PHY) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false; return false;
@@ -178,19 +178,30 @@ public:
return false; return false;
} }
EpsilonPhySpeed epsilonSpeed; EpsilonPhySpeed epsilonSpeed;
switch (speed) { switch (mode) {
case ETH_LINK_MODE_AUTO_NEGOTIATION:
// Auto-negotiate - default to 1G base speed
epsilonSpeed = EpsilonPhySpeed::Speed1000;
break;
case ETH_LINK_MODE_100MBPS_FULLDUPLEX:
epsilonSpeed = EpsilonPhySpeed::Speed100;
break;
case ETH_LINK_MODE_1GBPS_FULLDUPLEX:
epsilonSpeed = EpsilonPhySpeed::Speed1000;
break;
case ETH_LINK_MODE_10GBPS_FULLDUPLEX:
epsilonSpeed = EpsilonPhySpeed::Speed10000;
break;
// Reject half-duplex modes - automotive T1 is full-duplex only
case ETH_LINK_MODE_10MBPS_HALFDUPLEX:
case ETH_LINK_MODE_10MBPS_FULLDUPLEX:
case ETH_LINK_MODE_100MBPS_HALFDUPLEX:
case ETH_LINK_MODE_1GBPS_HALFDUPLEX:
case ETH_LINK_MODE_2_5GBPS_FULLDUPLEX:
case ETH_LINK_MODE_5GBPS_FULLDUPLEX:
default: default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false; 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); cfg->switchSettings.speed[index] = static_cast<uint8_t>(epsilonSpeed);
return true; return true;
@@ -235,7 +246,7 @@ public:
return std::make_optional(static_cast<bool>(cfg->switchSettings.enablePhy[index])); return std::make_optional(static_cast<bool>(cfg->switchSettings.enablePhy[index]));
} }
std::optional<EthLinkSpeed> getPhySpeed(uint8_t index) override { std::optional<EthPhyLinkMode> getPhySpeed(uint8_t index) override {
if (index > RADEPSILON_MAX_PHY) { if (index > RADEPSILON_MAX_PHY) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt; return std::nullopt;
@@ -244,22 +255,18 @@ public:
if (cfg == nullptr) { if (cfg == nullptr) {
return std::nullopt; return std::nullopt;
} }
EthLinkSpeed speed; // Automotive Ethernet T1 is always full-duplex
switch (static_cast<EpsilonPhySpeed>(cfg->switchSettings.speed[index])) { switch (static_cast<EpsilonPhySpeed>(cfg->switchSettings.speed[index])) {
case EpsilonPhySpeed::Speed100:
return ETH_LINK_MODE_100MBPS_FULLDUPLEX;
case EpsilonPhySpeed::Speed1000:
return ETH_LINK_MODE_1GBPS_FULLDUPLEX;
case EpsilonPhySpeed::Speed10000:
return ETH_LINK_MODE_10GBPS_FULLDUPLEX;
default: default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt; 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: private:
@@ -80,6 +80,7 @@ public:
return BootloaderPipeline() return BootloaderPipeline()
.add<EnterBootloaderPhase>() .add<EnterBootloaderPhase>()
.add<FlashPhase>(ChipID::RADGalaxy_ZYNQ, BootloaderCommunication::RAD) .add<FlashPhase>(ChipID::RADGalaxy_ZYNQ, BootloaderCommunication::RAD)
.add<EnterApplicationPhase>(ChipID::RADGalaxy_ZYNQ)
.add<ReconnectPhase>() .add<ReconnectPhase>()
.add<WaitPhase>(std::chrono::milliseconds(3000)); .add<WaitPhase>(std::chrono::milliseconds(3000));
} }
@@ -185,6 +185,92 @@ public:
return nullptr; return nullptr;
} }
} }
bool setMiscIOAnalogOutputEnabled(uint8_t pin, bool enabled) override {
if(!settingsLoaded) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
return false;
}
if(disabled) {
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
return false;
}
if(readonly) {
report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error);
return false;
}
if(pin < 1 || pin > 2) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
auto cfg = getMutableStructurePointer<radgalaxy_settings_t>();
if(cfg == nullptr) {
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::Error);
return false;
}
const uint16_t bitMask = 1 << (pin - 1);
if(enabled) {
// Set pin as output and enable analog mode
cfg->misc_io_initial_ddr |= bitMask;
cfg->misc_io_analog_enable |= bitMask;
} else {
// Disable analog mode (leave DDR as-is)
cfg->misc_io_analog_enable &= ~bitMask;
}
return true;
}
bool setMiscIOAnalogOutput(uint8_t pin, MiscIOAnalogVoltage voltage) override {
if(!settingsLoaded) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
return false;
}
if(disabled) {
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
return false;
}
if(readonly) {
report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error);
return false;
}
if(pin < 1 || pin > 2) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
auto cfg = getMutableStructurePointer<radgalaxy_settings_t>();
if(cfg == nullptr) {
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::Error);
return false;
}
const uint8_t dacValue = static_cast<uint8_t>(voltage);
if(dacValue > 5) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
if(pin == 1) {
// Update low nibble of high byte (bits 8-11), preserve pin 2 value
cfg->misc_io_initial_latch = (cfg->misc_io_initial_latch & 0xF0FF) | (static_cast<uint16_t>(dacValue) << 8);
} else { // pin == 2
// Update high nibble of high byte (bits 12-15), preserve pin 1 value
cfg->misc_io_initial_latch = (cfg->misc_io_initial_latch & 0x0FFF) | (static_cast<uint16_t>(dacValue) << 12);
}
return true;
}
}; };
} }
@@ -49,6 +49,10 @@ public:
Network::NetID::AE_10, Network::NetID::AE_10,
Network::NetID::AE_11, Network::NetID::AE_11,
Network::NetID::AE_12, Network::NetID::AE_12,
Network::NetID::AE_13,
Network::NetID::AE_14,
Network::NetID::AE_15,
Network::NetID::AE_16,
Network::NetID::ISO9141_01, Network::NetID::ISO9141_01,
Network::NetID::ISO9141_02, Network::NetID::ISO9141_02,
@@ -83,9 +87,10 @@ public:
return BootloaderPipeline() return BootloaderPipeline()
.add<EnterBootloaderPhase>() .add<EnterBootloaderPhase>()
.add<FlashPhase>(ChipID::RAD_GALAXY_2_ZMPCHIP_ID, BootloaderCommunication::RAD) .add<FlashPhase>(ChipID::RAD_GALAXY_2_ZMPCHIP_ID, BootloaderCommunication::RAD)
.add<EnterApplicationPhase>(ChipID::RAD_GALAXY_2_ZMPCHIP_ID)
.add<ReconnectPhase>() .add<ReconnectPhase>()
.add<FlashPhase>(ChipID::RADGALAXY2_SYSMON_CHIP, BootloaderCommunication::RADGalaxy2Peripheral) .add<FlashPhase>(ChipID::RADGALAXY2_SYSMON_CHIP, BootloaderCommunication::RADGalaxy2Peripheral)
.add<EnterApplicationPhase>(ChipID::RAD_GALAXY_2_ZMPCHIP_ID) .add<EnterApplicationPhase>(ChipID::RADGALAXY2_SYSMON_CHIP)
.add<ReconnectPhase>() .add<ReconnectPhase>()
.add<WaitPhase>(std::chrono::milliseconds(3000)); .add<WaitPhase>(std::chrono::milliseconds(3000));
} }
@@ -30,15 +30,22 @@ public:
ProductID getProductID() const override { return ProductID::RADGemini; } ProductID getProductID() const override { return ProductID::RADGemini; }
const std::vector<ChipInfo>& getChipInfo() const override {
static std::vector<ChipInfo> chips = {
{ChipID::RADGemini_MCHIP, true, "MCHIP", "radgemini_mchip_ief", 0, FirmwareType::IEF},
};
return chips;
}
BootloaderPipeline getBootloader() override { BootloaderPipeline getBootloader() override {
return BootloaderPipeline() return BootloaderPipeline()
.add<EnterBootloaderPhase>() .add<EnterBootloaderPhase>()
.add<FlashPhase>(ChipID::RADGemini_MCHIP, BootloaderCommunication::RED) .add<FlashPhase>(ChipID::RADGemini_MCHIP, BootloaderCommunication::RED)
.add<EnterApplicationPhase>(ChipID::RADGemini_MCHIP)
.add<ReconnectPhase>() .add<ReconnectPhase>()
.add<WaitPhase>(std::chrono::milliseconds(3000)); .add<WaitPhase>(std::chrono::milliseconds(3000));
} }
protected: protected:
RADGemini(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) { RADGemini(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) {
initialize<RADGeminiSettings>(makeDriver); initialize<RADGeminiSettings>(makeDriver);
@@ -93,11 +93,13 @@ public:
if(com->driver->isEthernet()) { if(com->driver->isEthernet()) {
return BootloaderPipeline() return BootloaderPipeline()
.add<FlashPhase>(ChipID::RADGigastar_ZYNQ, BootloaderCommunication::RAD) .add<FlashPhase>(ChipID::RADGigastar_ZYNQ, BootloaderCommunication::RAD)
.add<EnterApplicationPhase>(ChipID::RADGigastar_ZYNQ)
.add<WaitPhase>(std::chrono::milliseconds(3000)) .add<WaitPhase>(std::chrono::milliseconds(3000))
.add<ReconnectPhase>(); .add<ReconnectPhase>();
} }
return BootloaderPipeline() return BootloaderPipeline()
.add<FlashPhase>(ChipID::RADGigastar_USBZ_ZYNQ, BootloaderCommunication::RAD) .add<FlashPhase>(ChipID::RADGigastar_USBZ_ZYNQ, BootloaderCommunication::RAD)
.add<EnterApplicationPhase>(ChipID::RADGigastar_USBZ_ZYNQ)
.add<WaitPhase>(std::chrono::milliseconds(3000)) .add<WaitPhase>(std::chrono::milliseconds(3000))
.add<ReconnectPhase>(); .add<ReconnectPhase>();
} }
@@ -249,6 +249,153 @@ namespace icsneo
} }
} }
std::optional<bool> isT1SPLCAEnabledFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional((t1s->flags & ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA) != 0);
}
bool setT1SPLCAFor(Network net, bool enable) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
if(enable)
t1s->flags |= ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
else
t1s->flags &= ~ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
return true;
}
std::optional<uint8_t> getT1SLocalIDFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->local_id);
}
bool setT1SLocalIDFor(Network net, uint8_t id) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->local_id = id;
return true;
}
std::optional<uint8_t> getT1SMaxNodesFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_num_nodes);
}
bool setT1SMaxNodesFor(Network net, uint8_t nodes) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_num_nodes = nodes;
return true;
}
std::optional<uint8_t> getT1STxOppTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->to_timer);
}
bool setT1STxOppTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->to_timer = timer;
return true;
}
std::optional<uint8_t> getT1SMaxBurstFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_burst_count);
}
bool setT1SMaxBurstFor(Network net, uint8_t burst) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_burst_count = burst;
return true;
}
std::optional<uint8_t> getT1SBurstTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->burst_timer);
}
bool setT1SBurstTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->burst_timer = timer;
return true;
}
private:
const ETHERNET10T1S_SETTINGS* getT1SSettingsFor(Network net) const {
auto cfg = getStructurePointer<radgigastar2_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_03: return &(cfg->t1s1);
case Network::NetID::AE_04: return &(cfg->t1s2);
case Network::NetID::AE_05: return &(cfg->t1s3);
case Network::NetID::AE_06: return &(cfg->t1s4);
case Network::NetID::AE_07: return &(cfg->t1s5);
case Network::NetID::AE_08: return &(cfg->t1s6);
case Network::NetID::AE_09: return &(cfg->t1s7);
case Network::NetID::AE_10: return &(cfg->t1s8);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
ETHERNET10T1S_SETTINGS* getMutableT1SSettingsFor(Network net) {
auto cfg = getMutableStructurePointer<radgigastar2_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_03: return &(cfg->t1s1);
case Network::NetID::AE_04: return &(cfg->t1s2);
case Network::NetID::AE_05: return &(cfg->t1s3);
case Network::NetID::AE_06: return &(cfg->t1s4);
case Network::NetID::AE_07: return &(cfg->t1s5);
case Network::NetID::AE_08: return &(cfg->t1s6);
case Network::NetID::AE_09: return &(cfg->t1s7);
case Network::NetID::AE_10: return &(cfg->t1s8);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
protected: protected:
ICSNEO_UNALIGNED(const uint64_t *) ICSNEO_UNALIGNED(const uint64_t *)
getTerminationEnables() const override getTerminationEnables() const override
@@ -27,6 +27,21 @@ public:
return supportedNetworks; return supportedNetworks;
} }
const std::vector<ChipInfo>& getChipInfo() const override {
static std::vector<ChipInfo> chips = {
{ChipID::RADJupiter_MCHIP, true, "MCHIP", "jupiter_mchip_ief", 1, FirmwareType::IEF}
};
return chips;
}
BootloaderPipeline getBootloader() override {
return BootloaderPipeline()
.add<EnterBootloaderPhase>()
.add<FlashPhase>(ChipID::RADJupiter_MCHIP, BootloaderCommunication::RED)
.add<EnterApplicationPhase>(ChipID::RADJupiter_MCHIP)
.add<ReconnectPhase>();
}
bool getEthPhyRegControlSupported() const override { return true; } bool getEthPhyRegControlSupported() const override { return true; }
ProductID getProductID() const override { ProductID getProductID() const override {
@@ -37,6 +37,22 @@ public:
ProductID getProductID() const override { ProductID getProductID() const override {
return ProductID::RADMoonT1S; return ProductID::RADMoonT1S;
} }
const std::vector<ChipInfo>& getChipInfo() const override {
static std::vector<ChipInfo> chips = {
{ChipID::RADMOONT1S_ZCHIP, true, "ZCHIP", "RADMoonT1S_SW_bin", 1, FirmwareType::Zip}
};
return chips;
}
BootloaderPipeline getBootloader() override {
return BootloaderPipeline()
.add<EnterBootloaderPhase>()
.add<FlashPhase>(ChipID::RADMOONT1S_ZCHIP, BootloaderCommunication::RAD)
.add<EnterApplicationPhase>(ChipID::RADMOONT1S_ZCHIP)
.add<WaitPhase>(std::chrono::milliseconds(3000))
.add<ReconnectPhase>();
}
protected: protected:
RADMoonT1S(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) { RADMoonT1S(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) {
initialize<RADMoonT1SSettings>(makeDriver); initialize<RADMoonT1SSettings>(makeDriver);
@@ -47,6 +47,169 @@ static_assert(sizeof(radmoont1s_settings_t) == 160, "RADMoonT1S settings size mi
class RADMoonT1SSettings : public IDeviceSettings { class RADMoonT1SSettings : public IDeviceSettings {
public: public:
RADMoonT1SSettings(std::shared_ptr<Communication> com) : IDeviceSettings(com, sizeof(radmoont1s_settings_t)) {} RADMoonT1SSettings(std::shared_ptr<Communication> com) : IDeviceSettings(com, sizeof(radmoont1s_settings_t)) {}
std::optional<bool> isT1SPLCAEnabledFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional((t1s->flags & ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA) != 0);
}
bool setT1SPLCAFor(Network net, bool enable) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
if(enable)
t1s->flags |= ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
else
t1s->flags &= ~ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
return true;
}
std::optional<uint8_t> getT1SLocalIDFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->local_id);
}
bool setT1SLocalIDFor(Network net, uint8_t id) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->local_id = id;
return true;
}
std::optional<uint8_t> getT1SMaxNodesFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_num_nodes);
}
bool setT1SMaxNodesFor(Network net, uint8_t nodes) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_num_nodes = nodes;
return true;
}
std::optional<uint8_t> getT1STxOppTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->to_timer);
}
bool setT1STxOppTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->to_timer = timer;
return true;
}
std::optional<uint8_t> getT1SMaxBurstFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_burst_count);
}
bool setT1SMaxBurstFor(Network net, uint8_t burst) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_burst_count = burst;
return true;
}
std::optional<uint8_t> getT1SBurstTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->burst_timer);
}
bool setT1SBurstTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->burst_timer = timer;
return true;
}
private:
const ETHERNET10T1S_SETTINGS* getT1SSettingsFor(Network net) const {
auto cfg = getStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_01:
return &(cfg->t1s);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
ETHERNET10T1S_SETTINGS* getMutableT1SSettingsFor(Network net) {
auto cfg = getMutableStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_01:
return &(cfg->t1s);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
const ETHERNET10T1S_SETTINGS_EXT* getT1SSettingsExtFor(Network net) const {
auto cfg = getStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_01:
return &(cfg->t1sExt);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
ETHERNET10T1S_SETTINGS_EXT* getMutableT1SSettingsExtFor(Network net) {
auto cfg = getMutableStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_01:
return &(cfg->t1sExt);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
}; };
} }
@@ -67,6 +67,7 @@ public:
const std::vector<ChipInfo>& getChipInfo() const override { const std::vector<ChipInfo>& getChipInfo() const override {
static std::vector<ChipInfo> chips = { static std::vector<ChipInfo> chips = {
{ChipID::ValueCAN4_2EL_MCHIP, true, "MCHIP", "vcan44_mchip_ief", 0, FirmwareType::IEF}, {ChipID::ValueCAN4_2EL_MCHIP, true, "MCHIP", "vcan44_mchip_ief", 0, FirmwareType::IEF},
{ChipID::ValueCAN4_4_MCHIP, true, "MCHIP", "vcan44_mchip_ief", 0, FirmwareType::IEF}
}; };
return chips; return chips;
} }
@@ -74,8 +75,8 @@ public:
BootloaderPipeline getBootloader() override { BootloaderPipeline getBootloader() override {
return BootloaderPipeline() return BootloaderPipeline()
.add<EnterBootloaderPhase>() .add<EnterBootloaderPhase>()
.add<FlashPhase>(ChipID::ValueCAN4_2EL_MCHIP, BootloaderCommunication::RED) .add<FlashPhase>(ChipID::ValueCAN4_4_MCHIP, BootloaderCommunication::RED)
.add<EnterApplicationPhase>(ChipID::ValueCAN4_2EL_MCHIP) .add<EnterApplicationPhase>(ChipID::ValueCAN4_4_MCHIP)
.add<WaitPhase>(std::chrono::milliseconds(3000)) .add<WaitPhase>(std::chrono::milliseconds(3000))
.add<ReconnectPhase>(); .add<ReconnectPhase>();
} }
@@ -30,6 +30,22 @@ public:
ProductID getProductID() const override { ProductID getProductID() const override {
return ProductID::ValueCAN4Industrial; return ProductID::ValueCAN4Industrial;
} }
const std::vector<ChipInfo>& getChipInfo() const override {
static std::vector<ChipInfo> chips = {
{ChipID::ValueCAN4Industrial_MCHIP, true, "MCHIP", "vcan4_ind_mchip_ief", 0, FirmwareType::IEF}
};
return chips;
}
BootloaderPipeline getBootloader() override {
return BootloaderPipeline()
.add<EnterBootloaderPhase>()
.add<FlashPhase>(ChipID::ValueCAN4Industrial_MCHIP, BootloaderCommunication::RED)
.add<EnterApplicationPhase>(ChipID::ValueCAN4Industrial_MCHIP)
.add<WaitPhase>(std::chrono::milliseconds(3000))
.add<ReconnectPhase>();
}
protected: protected:
ValueCAN4Industrial(neodevice_t neodevice, const driver_factory_t& makeDriver) : ValueCAN4(neodevice) { ValueCAN4Industrial(neodevice_t neodevice, const driver_factory_t& makeDriver) : ValueCAN4(neodevice) {
initialize<ValueCAN4IndustrialSettings>(makeDriver); initialize<ValueCAN4IndustrialSettings>(makeDriver);
+17 -5
View File
@@ -19,6 +19,9 @@ class FirmIO : public Driver {
public: public:
static void Find(std::vector<FoundDevice>& foundDevices); static void Find(std::vector<FoundDevice>& foundDevices);
FirmIO(const device_eventhandler_t& report) : Driver(report) {
writeQueueSize = 256;
}
using Driver::Driver; // Inherit constructor using Driver::Driver; // Inherit constructor
~FirmIO(); ~FirmIO();
bool open() override; bool open() override;
@@ -26,16 +29,16 @@ public:
bool close() override; bool close() override;
driver_finder_t getFinder() override { return FirmIO::Find; } driver_finder_t getFinder() override { return FirmIO::Find; }
// bool writeQueueFull() override;
// bool writeQueueAlmostFull() override;
bool writeInternal(const std::vector<uint8_t>& b) override;
private: private:
std::thread readThread, writeThread; std::thread readThread, writeThread;
void readTask(); void readTask();
void writeTask(); void writeTask();
bool writeQueueFull() override;
bool writeQueueAlmostFull() override;
bool writeInternal(const std::vector<uint8_t>& bytes) override;
struct DataInfo { struct DataInfo {
uint32_t type; uint32_t type;
uint32_t offset; uint32_t offset;
@@ -111,7 +114,11 @@ private:
bool free(uint8_t* addr); bool free(uint8_t* addr);
PhysicalAddress translate(uint8_t* addr) const; PhysicalAddress translate(uint8_t* addr) const;
private: uint32_t getUsedBlocks() const { return usedBlocks; }
size_t getTotalBlocks() const { return blocks.size(); }
bool isFull() const { return usedBlocks == blocks.size(); }
struct BlockInfo { struct BlockInfo {
enum class Status : uint32_t { enum class Status : uint32_t {
Free = 0, Free = 0,
@@ -121,6 +128,7 @@ private:
uint8_t* addr; uint8_t* addr;
}; };
private:
std::vector<BlockInfo> blocks; std::vector<BlockInfo> blocks;
std::atomic<uint32_t> usedBlocks; std::atomic<uint32_t> usedBlocks;
@@ -137,6 +145,10 @@ private:
std::mutex outMutex; std::mutex outMutex;
std::optional<MsgQueue> out; std::optional<MsgQueue> out;
std::optional<Mempool> outMemory; std::optional<Mempool> outMemory;
std::atomic<size_t> num_read = 0;
std::atomic<size_t> num_written = 0;
std::atomic<size_t> num_freed = 0;
}; };
} }
+4 -7
View File
@@ -18,27 +18,24 @@ class Servd : public Driver {
public: public:
static void Find(std::vector<FoundDevice>& foundDevices); static void Find(std::vector<FoundDevice>& foundDevices);
static bool Enabled(); static bool Enabled();
Servd(const device_eventhandler_t& err, neodevice_t& forDevice, const std::unordered_set<std::string>& availableDrivers); Servd(const device_eventhandler_t& err, neodevice_t& forDevice, const Address& address);
~Servd() override; ~Servd() override;
bool open() override; bool open() override;
bool isOpen() override; bool isOpen() override;
bool close() override; bool close() override;
bool faa(const std::string& key, int32_t inc, int32_t& orig);
bool enableCommunication(bool enable, bool& sendMsg) override; bool enableCommunication(bool enable, bool& sendMsg) override;
driver_finder_t getFinder() override { return Servd::Find; } driver_finder_t getFinder() override { return Servd::Find; }
private: private:
void alive(); void read();
void read(Address&& address); void write();
void write(Address&& address);
neodevice_t& device; neodevice_t& device;
std::thread aliveThread; // makes sure the client and server are healthy
std::thread writeThread; std::thread writeThread;
std::thread readThread; std::thread readThread;
Socket messageSocket; Socket messageSocket;
bool opened = false; bool opened = false;
bool comEnabled = false; bool comEnabled = false;
std::string driver; std::unique_ptr<Socket> dataSocket;
}; };
} }
+224 -210
View File
@@ -1,210 +1,224 @@
#ifndef __SOCKET_H_ #ifndef __SOCKET_H_
#define __SOCKET_H_ #define __SOCKET_H_
#ifdef __cplusplus #ifdef __cplusplus
#ifdef _WIN32 #ifdef _WIN32
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#define NOMINMAX #define NOMINMAX
#include <windows.h> #include <windows.h>
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#else #else
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <poll.h> #include <poll.h>
#endif #endif
#include <string> #include <string>
#include <chrono> #include <chrono>
namespace icsneo { namespace icsneo {
#ifdef _WIN32 #ifdef _WIN32
class WSA { class WSA {
public: public:
WSA() { WSA() {
// TODO: add error checking // TODO: add error checking
WSAStartup(MAKEWORD(2, 2), &wsaData); WSAStartup(MAKEWORD(2, 2), &wsaData);
} }
~WSA() { ~WSA() {
WSACleanup(); WSACleanup();
} }
private: private:
WSADATA wsaData; WSADATA wsaData;
}; };
#endif #endif
class Address { class Address {
public: public:
Address() = default; Address() = default;
Address(const char* ip, uint16_t port) Address(const char* ip, uint16_t port)
: _ip(ip), _port(port) : _ip(ip), _port(port)
{ {
_sockaddr.sin_family = AF_INET; _sockaddr.sin_family = AF_INET;
inet_pton(AF_INET, ip, &_sockaddr.sin_addr); inet_pton(AF_INET, ip, &_sockaddr.sin_addr);
_sockaddr.sin_port = htons(port); _sockaddr.sin_port = htons(port);
} }
Address(sockaddr_in& sockaddr) Address(sockaddr_in& sockaddr)
: _sockaddr(sockaddr) : _sockaddr(sockaddr)
{ {
char cip[INET_ADDRSTRLEN]; char cip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &sockaddr.sin_addr, cip, sizeof(cip)); inet_ntop(AF_INET, &sockaddr.sin_addr, cip, sizeof(cip));
_ip = cip; _ip = cip;
_port = ntohs(sockaddr.sin_port); _port = ntohs(sockaddr.sin_port);
} }
const std::string& ip() const { return _ip; } const std::string& ip() const { return _ip; }
const uint16_t& port() const { return _port; } const uint16_t& port() const { return _port; }
const sockaddr_in& sockaddr() const { return _sockaddr; } const sockaddr_in& sockaddr() const { return _sockaddr; }
private: private:
std::string _ip; std::string _ip;
uint16_t _port; uint16_t _port;
sockaddr_in _sockaddr; sockaddr_in _sockaddr;
}; };
class Socket { class Socket {
public: public:
#ifdef _WIN32 #ifdef _WIN32
using SocketHandleType = SOCKET; using SocketHandleType = SOCKET;
#else #else
using SocketHandleType = int; using SocketHandleType = int;
#endif #endif
Socket() { template<class... Args>
#ifdef _WIN32 Socket(Args&&... args) {
static WSA wsa; #ifdef _WIN32
#endif static WSA wsa;
mFD = socket(AF_INET, SOCK_DGRAM, 0); #endif
} mFD = socket(std::forward<Args>(args)...);
}
~Socket() {
#ifdef _WIN32 ~Socket() {
closesocket(mFD); #ifdef _WIN32
#else closesocket(mFD);
close(mFD); #else
#endif close(mFD);
} #endif
}
bool set_reuse(bool value) {
int ival = value; bool set_reuse(bool value) {
return ::setsockopt(mFD, SOL_SOCKET, SO_REUSEADDR, (const char*)&ival, sizeof(ival)) != -1; int ival = value;
} return ::setsockopt(mFD, SOL_SOCKET, SO_REUSEADDR, (const char*)&ival, sizeof(ival)) != -1;
}
bool set_nonblocking() {
#ifdef _WIN32 bool set_nonblocking() {
u_long nonblock = 1; #ifdef _WIN32
return ioctlsocket(mFD, FIONBIO, &nonblock) != SOCKET_ERROR; u_long nonblock = 1;
#else return ioctlsocket(mFD, FIONBIO, &nonblock) != SOCKET_ERROR;
return fcntl(mFD, F_SETFL, fcntl(mFD, F_GETFL, 0) | O_NONBLOCK) != -1; #else
#endif return fcntl(mFD, F_SETFL, fcntl(mFD, F_GETFL, 0) | O_NONBLOCK) != -1;
} #endif
}
bool bind(const Address& at) {
return ::bind(mFD, (sockaddr*)&at.sockaddr(), sizeof(sockaddr_in)) != -1; bool set_rcvbuf(uint32_t size) {
} return ::setsockopt(mFD, SOL_SOCKET, SO_RCVBUF, (char*)&size, sizeof(size)) == 0;
}
bool poll(const std::chrono::milliseconds& timeout, bool& in) {
#ifdef _WIN32 bool set_sndbuf(uint32_t size) {
WSAPOLLFD pfd; return ::setsockopt(mFD, SOL_SOCKET, SO_SNDBUF, (char*)&size, sizeof(size)) == 0;
pfd.fd = mFD; }
pfd.events = POLLIN;
if (::WSAPoll(&pfd, 1, static_cast<int>(timeout.count())) == SOCKET_ERROR) { bool connect(const Address& to) {
return false; return ::connect(mFD, (sockaddr*)&to.sockaddr(), sizeof(sockaddr_in)) != -1;
} }
in = pfd.revents & POLLIN;
return true; bool bind(const Address& at) {
#else return ::bind(mFD, (sockaddr*)&at.sockaddr(), sizeof(sockaddr_in)) != -1;
struct pollfd pfd; }
pfd.fd = mFD;
pfd.events = POLLIN; bool poll(const std::chrono::milliseconds& timeout, bool& in) {
pfd.revents = 0; #ifdef _WIN32
if (::poll(&pfd, 1, static_cast<int>(timeout.count())) == -1) { WSAPOLLFD pfd;
return false; pfd.fd = mFD;
} pfd.events = POLLIN;
in = pfd.revents & POLLIN; if (::WSAPoll(&pfd, 1, static_cast<int>(timeout.count())) == SOCKET_ERROR) {
return true; return false;
#endif }
} in = pfd.revents & POLLIN;
return true;
bool sendto(const void* buffer, size_t size, const Address& to) { #else
size_t totalSent = 0; struct pollfd pfd;
do { pfd.fd = mFD;
const auto sent = ::sendto(mFD, (const char*)buffer, (int)size, 0, (sockaddr*)&to.sockaddr(), sizeof(sockaddr_in)); pfd.events = POLLIN;
if (sent == -1) { pfd.revents = 0;
return false; if (::poll(&pfd, 1, static_cast<int>(timeout.count())) == -1) {
} return false;
totalSent += sent; }
} while (totalSent < size); in = pfd.revents & POLLIN;
return true; return true;
} #endif
}
bool recvfrom(void* buffer, size_t& size, Address& from) {
sockaddr_in addr; bool sendto(const void* buffer, size_t size, const Address& to) {
socklen_t addLen = sizeof(addr); size_t totalSent = 0;
const auto read = ::recvfrom(mFD, (char*)buffer, (int)size, 0, (sockaddr*)&addr, &addLen); do {
if (read == -1) { const auto sent = ::sendto(mFD, (const char*)buffer, (int)size, 0, (sockaddr*)&to.sockaddr(), sizeof(sockaddr_in));
return false; if (sent == -1) {
} return false;
size = read; }
from = Address(addr); totalSent += sent;
return true; } while (totalSent < size);
} return true;
}
bool recv(void* buffer, size_t& size) {
const auto read = ::recv(mFD, (char*)buffer, (int)size, 0); bool send(const void* buffer, size_t size) {
if (read == -1) { auto sent = ::send(mFD, (const char*)buffer, (int)size, 0);
return false; if(sent == -1) {
} return false;
size = read; }
return true; return (size_t)sent == size;
} }
template<typename REQ, typename RES> bool recvfrom(void* buffer, size_t& size, Address& from) {
bool transceive(const Address& to, REQ&& request, RES&& response, const std::chrono::milliseconds& timeout) { sockaddr_in addr;
if(!sendto(request.data(), request.size(), to)) { socklen_t addLen = sizeof(addr);
return false; const auto read = ::recvfrom(mFD, (char*)buffer, (int)size, 0, (sockaddr*)&addr, &addLen);
} if (read == -1) {
bool hasData; return false;
if(!poll(timeout, hasData)) { }
return false; size = read;
} from = Address(addr);
if(!hasData) { return true;
return false; }
}
size_t responseSize = response.size(); bool recv(void* buffer, size_t& size) {
if(!recv(response.data(), responseSize)) { const auto read = ::recv(mFD, (char*)buffer, (int)size, 0);
return false; if (read == -1) {
} return false;
response.resize(responseSize); }
return true; size = read;
} return true;
}
bool address(Address& address) const {
sockaddr_in sin; template<typename REQ, typename RES>
socklen_t len = sizeof(sin); bool transceive(REQ&& request, RES&& response, const std::chrono::milliseconds& timeout) {
getsockname(mFD, (sockaddr*)&sin, &len); if(!send(request.data(), request.size())) {
address = Address(sin); return false;
return true; }
} bool hasData;
if(!poll(timeout, hasData)) {
bool join_multicast(const std::string& interfaceIP, const std::string& multicastIP) { return false;
ip_mreq mreq; }
inet_pton(AF_INET, interfaceIP.c_str(), &mreq.imr_interface); if(!hasData) {
inet_pton(AF_INET, multicastIP.c_str(), &mreq.imr_multiaddr); return false;
return setsockopt(mFD, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)) == 0; }
} size_t responseSize = response.size();
if(!recv(response.data(), responseSize)) {
operator bool() const { return mFD != -1; } return false;
operator SocketHandleType() const { return mFD; } }
private: response.resize(responseSize);
SocketHandleType mFD; return true;
}; }
} // namespace icsneo bool address(Address& address) const {
sockaddr_in sin;
#endif // __cplusplus socklen_t len = sizeof(sin);
getsockname(mFD, (sockaddr*)&sin, &len);
#endif // __SOCKET_H_ address = Address(sin);
return true;
}
operator bool() const { return mFD != -1; }
operator SocketHandleType() const { return mFD; }
private:
SocketHandleType mFD;
};
} // namespace icsneo
#endif // __cplusplus
#endif // __SOCKET_H_
+2 -2
View File
@@ -156,7 +156,7 @@ void DXX::read() {
while(!isDisconnected() && !isClosing()) { while(!isDisconnected() && !isClosing()) {
size_t received = buffer.size(); size_t received = buffer.size();
const auto status = libredxx_read(device, buffer.data(), &received); const auto status = libredxx_read(device, buffer.data(), &received, LIBREDXX_ENDPOINT_A);
if(isDisconnected() || isClosing()) { if(isDisconnected() || isClosing()) {
return; return;
} }
@@ -186,7 +186,7 @@ void DXX::write() {
for(size_t totalWritten = 0; totalWritten < writeOp.bytes.size();) { for(size_t totalWritten = 0; totalWritten < writeOp.bytes.size();) {
size_t size = writeOp.bytes.size() - totalWritten; size_t size = writeOp.bytes.size() - totalWritten;
const auto status = libredxx_write(device, &writeOp.bytes[totalWritten], &size); const auto status = libredxx_write(device, &writeOp.bytes[totalWritten], &size, LIBREDXX_ENDPOINT_A);
if(isDisconnected() || isClosing()) { if(isDisconnected() || isClosing()) {
return; return;
} }
+124 -68
View File
@@ -42,16 +42,16 @@ void FirmIO::Find(std::vector<FoundDevice>& found) {
Packetizer packetizer([](APIEvent::Type, APIEvent::Severity) {}); Packetizer packetizer([](APIEvent::Type, APIEvent::Severity) {});
Decoder decoder([](APIEvent::Type, APIEvent::Severity) {}); Decoder decoder([](APIEvent::Type, APIEvent::Severity) {});
using namespace std::chrono; using namespace std::chrono;
const auto start = steady_clock::now();
// Get an absolute wall clock to compare to // Get an absolute wall clock to compare to
const auto overallTimeout = start + milliseconds(500); const auto overallTimeout = steady_clock::now() + milliseconds(200);
while(!temp.readAvailable()) { size_t lastBufferSize = 0;
if(steady_clock::now() > overallTimeout) { while (steady_clock::now() < overallTimeout)
// failed to read out a serial number reponse in time {
break; temp.waitForRx(lastBufferSize + 1, milliseconds(100));
} bool havePacket = packetizer.input(temp.getReadBuffer());
lastBufferSize = temp.getReadBuffer().size();
if(!packetizer.input(temp.getReadBuffer())) if(!havePacket)
continue; // A full packet has not yet been read out continue; // A full packet has not yet been read out
for(const auto& packet : packetizer.output()) { for(const auto& packet : packetizer.output()) {
@@ -75,6 +75,7 @@ void FirmIO::Find(std::vector<FoundDevice>& found) {
}; };
found.push_back(foundDevice); found.push_back(foundDevice);
break; // never going to find two!
} }
} }
} }
@@ -141,17 +142,27 @@ bool FirmIO::open() {
} }
} }
//std::cout << "Flushed " << std::dec << i << " freeing " << toFree.size() << std::endl; // std::cout << "Flushed " << std::dec << i << " freeing " << toFree.size() << std::endl;
while(!toFree.empty()) { auto endTime = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
std::lock_guard<std::mutex> lk(outMutex); while(std::chrono::steady_clock::now() < endTime && !toFree.empty()) {
out->write(&toFree.back()); bool pass = false;
{
std::scoped_lock lk(outMutex);
pass = out->write(&toFree.back());
}
if (!pass)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1));
continue;
}
toFree.pop_back(); toFree.pop_back();
} }
// Create thread // Create threads
// No thread for writing since we don't need the extra buffer
readThread = std::thread(&FirmIO::readTask, this); readThread = std::thread(&FirmIO::readTask, this);
//logThread = std::thread(&FirmIO::logTask, this);
writeThread = std::thread(&FirmIO::writeTask, this);
return true; return true;
} }
@@ -171,6 +182,13 @@ bool FirmIO::close() {
if(readThread.joinable()) if(readThread.joinable())
readThread.join(); readThread.join();
if (writeThread.joinable())
writeThread.join();
// if(logThread.joinable())
// logThread.join();
setIsClosing(false); setIsClosing(false);
setIsDisconnected(false); setIsDisconnected(false);
@@ -194,7 +212,8 @@ bool FirmIO::close() {
void FirmIO::readTask() { void FirmIO::readTask() {
EventManager::GetInstance().downgradeErrorsOnCurrentThread(); EventManager::GetInstance().downgradeErrorsOnCurrentThread();
Msg msg; Msg msg;
std::vector<Msg> toFree; std::vector<Msg::Ref> toFree;
toFree.reserve(outMemory->getTotalBlocks());
// attempt to elevate the thread priority. PRIO_MIN is actually the highest priority but the lowest value. // attempt to elevate the thread priority. PRIO_MIN is actually the highest priority but the lowest value.
int err = setpriority(PRIO_PROCESS, 0, -1); int err = setpriority(PRIO_PROCESS, 0, -1);
@@ -208,7 +227,6 @@ void FirmIO::readTask() {
FD_SET(fd, &rfds); FD_SET(fd, &rfds);
tv.tv_usec = 50000; // 50ms tv.tv_usec = 50000; // 50ms
int ret = ::select(fd + 1, &rfds, NULL, NULL, &tv); int ret = ::select(fd + 1, &rfds, NULL, NULL, &tv);
// std::cout << "select returned " << ret << ' ' << errno << std::endl;
if(ret < 0) if(ret < 0)
report(APIEvent::Type::FailedToRead, APIEvent::Severity::Error); report(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
if(ret <= 0) if(ret <= 0)
@@ -221,24 +239,12 @@ void FirmIO::readTask() {
if(ret < int(sizeof(interruptCount)) || interruptCount < 1) if(ret < int(sizeof(interruptCount)) || interruptCount < 1)
continue; continue;
toFree.clear(); while(in->read(&msg)) {
int i = 0;
while(in->read(&msg) && i++ < 1000) {
switch(msg.command) { switch(msg.command) {
case Msg::Command::ComData: { case Msg::Command::ComData: {
if(toFree.empty() || toFree.back().payload.free.refCount == 6) {
toFree.emplace_back();
toFree.back().command = Msg::Command::ComFree;
toFree.back().payload.free.refCount = 0;
}
// Add this ref to the list of payloads to free toFree.push_back(msg.payload.data.ref);
// After we process these, we'll send this list back to the device ++num_read;
// so that it can free these entries
toFree.back().payload.free.ref[toFree.back().payload.free.refCount] = msg.payload.data.ref;
toFree.back().payload.free.refCount++;
// std::cout << "Got some data @ 0x" << std::hex << msg.payload.data.addr << " " << std::dec << msg.payload.data.len << std::endl;
// Translate the physical address back to our virtual address space // Translate the physical address back to our virtual address space
uint8_t* addr = reinterpret_cast<uint8_t*>(msg.payload.data.addr - PHY_ADDR_BASE + vbase); uint8_t* addr = reinterpret_cast<uint8_t*>(msg.payload.data.addr - PHY_ADDR_BASE + vbase);
@@ -251,58 +257,95 @@ void FirmIO::readTask() {
} }
break; break;
case Msg::Command::ComFree: { case Msg::Command::ComFree: {
std::lock_guard<std::mutex> lk(outMutex); std::scoped_lock lk(outMutex);
// std::cout << "Got some free " << std::hex << msg.payload.free.ref[0] << std::endl;
for(uint32_t i = 0; i < msg.payload.free.refCount; i++) for(uint32_t i = 0; i < msg.payload.free.refCount; i++)
outMemory->free(reinterpret_cast<uint8_t*>(msg.payload.free.ref[i])); outMemory->free(reinterpret_cast<uint8_t*>(msg.payload.free.ref[i]));
break; break;
} }
default:
// std::cout << "invalid command: " << std::hex << static_cast<uint32_t>(msg.command) << std::dec << std::endl;
break;
} }
if (isClosing() || isDisconnected())
break;
} }
while (toFree.size()) {
while(!toFree.empty()) { Msg freeMsg = { Msg::Command::ComFree };
std::lock_guard<std::mutex> lk(outMutex); freeMsg.payload.free.refCount = std::min(static_cast<uint32_t>(toFree.size()), 6u);
out->write(&toFree.back()); for (size_t i = 0; i < freeMsg.payload.free.refCount; ++i) {
toFree.pop_back(); freeMsg.payload.free.ref[i] = toFree[i];
}
std::scoped_lock lk(outMutex);
if (!out->write(&freeMsg)) {
break;
}
num_freed += freeMsg.payload.free.refCount;
toFree.erase(toFree.begin(), toFree.begin() + freeMsg.payload.free.refCount);
} }
} }
while (toFree.size())
{
Msg freeMsg = { Msg::Command::ComFree };
freeMsg.payload.free.refCount = std::min(static_cast<uint32_t>(toFree.size()), 6u);
for (size_t i = 0; i < freeMsg.payload.free.refCount; ++i) {
freeMsg.payload.free.ref[i] = toFree[i];
}
std::scoped_lock lk(outMutex);
if (!out->write(&freeMsg)) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
continue;
}
toFree.erase(toFree.begin(), toFree.begin() + freeMsg.payload.free.refCount);
}
// std::cout << "FirmIO readTask exiting: " << "closing=" << isClosing() << " disconnected=" << isDisconnected() << std::endl;
} }
void FirmIO::writeTask() { void FirmIO::writeTask() {
return; // We're overriding Driver::writeInternal() and doing the work there constexpr uint32_t genInterrupt = 0x01;
} std::pair<std::optional<WriteOperation>, uint8_t*> op;
while (!isClosing() && !isDisconnected()) {
if (!op.first) {
writeQueue.wait_dequeue_timed(op.first, std::chrono::milliseconds(100));
continue;
}
bool FirmIO::writeQueueFull() { if (!op.second) {
return out->isFull(); op.second = outMemory->alloc(static_cast<uint32_t>(op.first->bytes.size()));
} if (op.second == nullptr) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
continue;
}
memcpy(op.second, op.first->bytes.data(), op.first->bytes.size());
}
bool FirmIO::writeQueueAlmostFull() { Msg msg = { Msg::Command::ComData };
// TODO: Better implementation here msg.payload.data.addr = outMemory->translate(op.second);
return writeQueueFull(); msg.payload.data.len = op.first->bytes.size();
msg.payload.data.ref = reinterpret_cast<Msg::Ref>(op.second);
std::scoped_lock lk(outMutex);
if(!out->write(&msg))
{
std::this_thread::sleep_for(std::chrono::milliseconds(1));
continue;
}
++num_written;
::write(fd, &genInterrupt, sizeof(genInterrupt));
op.first.reset();
op.second = nullptr;
}
std::cout << "FirmIO writeTask exiting: " << "closing=" << isClosing() << " disconnected=" << isDisconnected() << std::endl;
} }
bool FirmIO::writeInternal(const std::vector<uint8_t>& bytes) { bool FirmIO::writeInternal(const std::vector<uint8_t>& bytes) {
if(bytes.empty() || bytes.size() > Mempool::BlockSize) if(bytes.empty() || bytes.size() > Mempool::BlockSize)
{
// std::cout << "Invalid write size of " << bytes.size() << std::endl;
return false; return false;
}
std::lock_guard<std::mutex> lk(outMutex);
uint8_t* sharedData = outMemory->alloc(bytes.size()); return writeQueue.enqueue(WriteOperation(bytes));
if(sharedData == nullptr)
return false;
// std::cout << "coping " << bytes.size() << " bytes of data" << std::endl;
memcpy(sharedData, bytes.data(), bytes.size());
Msg msg = { Msg::Command::ComData };
msg.payload.data.addr = outMemory->translate(sharedData);
msg.payload.data.len = static_cast<uint32_t>(bytes.size());
msg.payload.data.ref = reinterpret_cast<Msg::Ref>(sharedData);
if(!out->write(&msg))
return false;
uint32_t genInterrupt = 0x01;
return ::write(fd, &genInterrupt, sizeof(genInterrupt)) == sizeof(genInterrupt);
} }
bool FirmIO::MsgQueue::read(Msg* msg) { bool FirmIO::MsgQueue::read(Msg* msg) {
@@ -369,13 +412,17 @@ bool FirmIO::Mempool::free(uint8_t* addr) {
return b.addr == addr; return b.addr == addr;
}); });
if(found == blocks.end()) if(found == blocks.end()) {
// std::cout << "failed to free block address " << std::hex << reinterpret_cast<uintptr_t>(addr) << std::dec << std::endl;
return false; // Invalid address return false; // Invalid address
}
if(found->status != BlockInfo::Status::Used) if(found->status != BlockInfo::Status::Used) {
// std::cout << "invalid state for free of block address " << std::hex << reinterpret_cast<uintptr_t>(addr) << std::dec << std::endl;
return false; // Double free return false; // Double free
}
usedBlocks--; --usedBlocks;
found->status = BlockInfo::Status::Free; found->status = BlockInfo::Status::Free;
return true; return true;
} }
@@ -383,3 +430,12 @@ bool FirmIO::Mempool::free(uint8_t* addr) {
FirmIO::Mempool::PhysicalAddress FirmIO::Mempool::translate(uint8_t* addr) const { FirmIO::Mempool::PhysicalAddress FirmIO::Mempool::translate(uint8_t* addr) const {
return reinterpret_cast<PhysicalAddress>(addr - virtualAddress + physicalAddress); return reinterpret_cast<PhysicalAddress>(addr - virtualAddress + physicalAddress);
} }
// void FirmIO::logTask()
// {
// while (!isClosing() && !isDisconnected()) {
// std::cout << "FirmIO Stats: RX Count: " << num_read << " TX Count: " << num_written << " Used Blocks (out): " << outMemory->getUsedBlocks() << " Freed Blocks: " << num_freed << std::endl;
// std::this_thread::sleep_for(std::chrono::seconds(1));
// }
// std::cout << "FirmIO logTask exiting: " << "closing=" << isClosing() << " disconnected=" << isDisconnected() << std::endl;
// }
+70 -122
View File
@@ -6,7 +6,7 @@
using namespace icsneo; using namespace icsneo;
#define SERVD_VERSION 1 #define SERVD_VERSION 2
static const Address SERVD_ADDRESS = Address("127.0.0.1", 26741); static const Address SERVD_ADDRESS = Address("127.0.0.1", 26741);
static const std::string SERVD_VERSION_STR = std::to_string(SERVD_VERSION); static const std::string SERVD_VERSION_STR = std::to_string(SERVD_VERSION);
@@ -41,20 +41,17 @@ std::vector<std::string> split(const std::string_view& str, char delim = ' ') {
} }
void Servd::Find(std::vector<FoundDevice>& found) { void Servd::Find(std::vector<FoundDevice>& found) {
Socket socket; Socket socket(AF_INET, SOCK_DGRAM, 0);
socket.connect(SERVD_ADDRESS);
if(!socket.set_nonblocking()) { if(!socket.set_nonblocking()) {
EventManager::GetInstance().add(APIEvent::Type::ServdNonblockError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdNonblockError, APIEvent::Severity::Error);
return; return;
} }
if(!socket.bind(Address("127.0.0.1", 0))) {
EventManager::GetInstance().add(APIEvent::Type::ServdBindError, APIEvent::Severity::Error);
return;
}
std::string response; std::string response;
response.resize(512); response.resize(512);
const std::string version_request = SERVD_VERSION_STR + " version"; const std::string version_request = SERVD_VERSION_STR + " version";
if(!socket.transceive(SERVD_ADDRESS, version_request, response, std::chrono::milliseconds(5000))) { if(!socket.transceive(version_request, response, std::chrono::milliseconds(5000))) {
EventManager::GetInstance().add(APIEvent::Type::ServdTransceiveError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdTransceiveError, APIEvent::Severity::Error);
return; return;
} }
@@ -66,46 +63,39 @@ void Servd::Find(std::vector<FoundDevice>& found) {
response.resize(512); response.resize(512);
const std::string find_request = SERVD_VERSION_STR + " find"; const std::string find_request = SERVD_VERSION_STR + " find";
if(!socket.transceive(SERVD_ADDRESS, find_request, response, std::chrono::milliseconds(5000))) { if(!socket.transceive(find_request, response, std::chrono::milliseconds(5000))) {
EventManager::GetInstance().add(APIEvent::Type::ServdTransceiveError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdTransceiveError, APIEvent::Severity::Error);
return; return;
} }
const auto lines = split(response, '\n'); const auto lines = split(response, '\n');
for(auto&& line : lines) { for(auto&& line : lines) {
const auto cols = split(line, ' '); const auto cols = split(line, ' ');
if(cols.size() < 2) { if(cols.size() < 3) {
EventManager::GetInstance().add(APIEvent::Type::ServdInvalidResponseError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdInvalidResponseError, APIEvent::Severity::Error);
continue; continue;
} }
const auto& serial = cols[0]; const auto& serial = cols[0];
std::unordered_set<std::string> drivers; const auto& ip = cols[1];
for (size_t i = 1; i < cols.size(); ++i) { uint16_t port = 0;
drivers.emplace(cols[i]); try {
port = static_cast<uint16_t>(std::stoi(cols[2]));
} catch (const std::exception&) {
EventManager::GetInstance().add(APIEvent::Type::ServdInvalidResponseError, APIEvent::Severity::Error);
continue;
} }
Address address(ip.c_str(), port);
auto& newFound = found.emplace_back(); auto& newFound = found.emplace_back();
std::copy(serial.begin(), serial.end(), newFound.serial); std::copy(serial.begin(), serial.end(), newFound.serial);
newFound.makeDriver = [=](device_eventhandler_t err, neodevice_t& forDevice) { newFound.makeDriver = [=](device_eventhandler_t err, neodevice_t& forDevice) {
return std::make_unique<Servd>(err, forDevice, drivers); return std::make_unique<Servd>(err, forDevice, address);
}; };
} }
} }
Servd::Servd(const device_eventhandler_t& err, neodevice_t& forDevice, const std::unordered_set<std::string>& availableDrivers) : Servd::Servd(const device_eventhandler_t& err, neodevice_t& forDevice, const Address& address) :
Driver(err), device(forDevice) { Driver(err), device(forDevice), messageSocket(AF_INET, SOCK_DGRAM, 0) {
messageSocket.connect(address);
messageSocket.set_nonblocking(); messageSocket.set_nonblocking();
messageSocket.bind(Address("127.0.0.1", 0));
if(availableDrivers.count("dxx")) {
driver = "dxx"; // prefer USB over Ethernet
} else if(availableDrivers.count("cab")) {
driver = "cab"; // prefer CAB over TCP
} else if(availableDrivers.count("tcp")) {
driver = "tcp";
} else if(availableDrivers.count("vcp")) {
driver = "vcp";
} else {
// just take the first driver
driver = *availableDrivers.begin();
}
} }
Servd::~Servd() { Servd::~Servd() {
@@ -113,21 +103,39 @@ Servd::~Servd() {
} }
bool Servd::open() { bool Servd::open() {
const std::string request = SERVD_VERSION_STR + " open " + std::string(device.serial) + " " + driver; const std::string request = SERVD_VERSION_STR + " open";
std::string response; std::string response;
response.resize(512); response.resize(512);
if(!messageSocket.transceive(SERVD_ADDRESS, request, response, std::chrono::milliseconds(5000))) { if(!messageSocket.transceive(request, response, std::chrono::milliseconds(5000))) {
EventManager::GetInstance().add(APIEvent::Type::ServdTransceiveError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdTransceiveError, APIEvent::Severity::Error);
return false; return false;
} }
const auto tokens = split(response); const auto tokens = split(response);
if(tokens.size() != 4) { if(tokens.size() != 2) {
EventManager::GetInstance().add(APIEvent::Type::ServdInvalidResponseError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdInvalidResponseError, APIEvent::Severity::Error);
return false; return false;
} }
aliveThread = std::thread(&Servd::alive, this); dataSocket = std::make_unique<Socket>(AF_INET, SOCK_STREAM, 0);
readThread = std::thread(&Servd::read, this, Address{tokens[2].c_str(), (uint16_t)std::stol(tokens[3].c_str())}); if(!dataSocket->set_rcvbuf(4 * 1024 * 1024)) {
writeThread = std::thread(&Servd::write, this, Address{tokens[0].c_str(), (uint16_t)std::stol(tokens[1].c_str())}); EventManager::GetInstance().add(APIEvent::Type::SyscallError, APIEvent::Severity::Error);
return false;
}
if(!dataSocket->set_sndbuf(4 * 1024 * 1024)) {
EventManager::GetInstance().add(APIEvent::Type::SyscallError, APIEvent::Severity::Error);
return false;
}
const auto& ip = tokens[0];
uint16_t port = 0;
try {
port = static_cast<uint16_t>(std::stoi(tokens[1]));
} catch (const std::exception&) {
EventManager::GetInstance().add(APIEvent::Type::ServdInvalidResponseError, APIEvent::Severity::Error);
return false;
}
Address address(ip.c_str(), port);
dataSocket->connect(address);
readThread = std::thread(&Servd::read, this);
writeThread = std::thread(&Servd::write, this);
opened = true; opened = true;
return true; return true;
} }
@@ -138,9 +146,6 @@ bool Servd::isOpen() {
bool Servd::close() { bool Servd::close() {
setIsClosing(true); setIsClosing(true);
if(aliveThread.joinable()) {
aliveThread.join();
}
if(readThread.joinable()) { if(readThread.joinable()) {
readThread.join(); readThread.join();
} }
@@ -148,8 +153,16 @@ bool Servd::close() {
writeThread.join(); writeThread.join();
} }
if(isOpen()) { if(isOpen()) {
const std::string request = SERVD_VERSION_STR + " close " + std::string(device.serial); Address localAddress;
messageSocket.sendto(request.data(), request.size(), SERVD_ADDRESS); dataSocket->address(localAddress);
const std::string request = SERVD_VERSION_STR + " close " + localAddress.ip() + " " + std::to_string(localAddress.port());
std::string response;
response.resize(1);
if(!messageSocket.transceive(request, response, std::chrono::milliseconds(5000))) {
EventManager::GetInstance().add(APIEvent::Type::ServdTransceiveError, APIEvent::Severity::Error);
return false;
}
dataSocket.reset();
} }
opened = false; opened = false;
setIsClosing(false); setIsClosing(false);
@@ -159,13 +172,13 @@ bool Servd::close() {
bool Servd::enableCommunication(bool enable, bool& sendMsg) { bool Servd::enableCommunication(bool enable, bool& sendMsg) {
const std::string serialString(device.serial); const std::string serialString(device.serial);
{ {
const std::string request = SERVD_VERSION_STR + " lock " + serialString + " com 1000"; const std::string request = SERVD_VERSION_STR + " lock com 1000";
std::string response; std::string response;
response.resize(1); response.resize(1);
bool locked = false; bool locked = false;
const auto timeout = std::chrono::steady_clock::now() + std::chrono::seconds(1); const auto timeout = std::chrono::steady_clock::now() + std::chrono::seconds(1);
do { do {
if(!messageSocket.transceive(SERVD_ADDRESS, request, response, std::chrono::milliseconds(5000))) { if(!messageSocket.transceive(request, response, std::chrono::milliseconds(5000))) {
return false; return false;
} }
locked = response == "1" ? true : false; locked = response == "1" ? true : false;
@@ -181,10 +194,10 @@ bool Servd::enableCommunication(bool enable, bool& sendMsg) {
} }
uint64_t com = 0; uint64_t com = 0;
{ {
const std::string request = SERVD_VERSION_STR + " load " + serialString + " com"; const std::string request = SERVD_VERSION_STR + " load com";
std::string response; std::string response;
response.resize(20); response.resize(20);
if(!messageSocket.transceive(SERVD_ADDRESS, request, response, std::chrono::milliseconds(5000))) { if(!messageSocket.transceive(request, response, std::chrono::milliseconds(5000))) {
EventManager::GetInstance().add(APIEvent::Type::ServdTransceiveError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdTransceiveError, APIEvent::Severity::Error);
return false; return false;
} }
@@ -202,16 +215,20 @@ bool Servd::enableCommunication(bool enable, bool& sendMsg) {
} }
if(comEnabled != enable) { if(comEnabled != enable) {
com += enable ? 1 : -1; com += enable ? 1 : -1;
const std::string request = SERVD_VERSION_STR + " store " + serialString + " com " + std::to_string(com); const std::string request = SERVD_VERSION_STR + " store com " + std::to_string(com);
if(!messageSocket.sendto(request.data(), request.size(), SERVD_ADDRESS)) { std::string response;
response.resize(1);
if(!messageSocket.transceive(request, response, std::chrono::milliseconds(5000))) {
EventManager::GetInstance().add(APIEvent::Type::ServdSendError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdSendError, APIEvent::Severity::Error);
return false; return false;
} }
} }
comEnabled = enable; comEnabled = enable;
{ {
const std::string request = SERVD_VERSION_STR + " unlock " + serialString + " com"; const std::string request = SERVD_VERSION_STR + " unlock com";
if(!messageSocket.sendto(request.data(), request.size(), SERVD_ADDRESS)) { std::string response;
response.resize(1);
if(!messageSocket.transceive(request, response, std::chrono::milliseconds(5000))) {
EventManager::GetInstance().add(APIEvent::Type::ServdSendError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdSendError, APIEvent::Severity::Error);
return false; return false;
} }
@@ -219,78 +236,11 @@ bool Servd::enableCommunication(bool enable, bool& sendMsg) {
return true; return true;
} }
void Servd::alive() { void Servd::read() {
Socket socket; std::vector<uint8_t> buf(2 * 1024 * 1024);
socket.set_nonblocking();
socket.bind(Address("127.0.0.1", 0));
const std::string statusRequest = SERVD_VERSION_STR + " status " + std::string(device.serial);
std::string statusResponse;
statusResponse.resize(8);
while(!isDisconnected() && !isClosing()) {
if(!socket.sendto(statusRequest.data(), statusRequest.size(), {"127.0.0.1", 26741})) {
EventManager::GetInstance().add(APIEvent::Type::ServdSendError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
bool hasData;
if(!socket.poll(std::chrono::milliseconds(2000), hasData)) {
EventManager::GetInstance().add(APIEvent::Type::ServdPollError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
if(!hasData) {
EventManager::GetInstance().add(APIEvent::Type::ServdNoDataError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
size_t statusResponseSize = statusResponse.size();
if(!socket.recv(statusResponse.data(), statusResponseSize)) {
EventManager::GetInstance().add(APIEvent::Type::ServdRecvError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
statusResponse.resize(statusResponseSize);
if(statusRequest == "closed") {
EventManager::GetInstance().add(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
if(statusResponse != "open") {
EventManager::GetInstance().add(APIEvent::Type::ServdInvalidResponseError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
void Servd::read(Address&& address) {
Socket socket;
socket.set_nonblocking();
socket.set_reuse(true);
#ifdef _WIN32
if(!socket.bind(Address("127.0.0.1", address.port()))) {
EventManager::GetInstance().add(APIEvent::Type::ServdBindError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
#else
if(!socket.bind(Address(address.ip().c_str(), address.port()))) {
EventManager::GetInstance().add(APIEvent::Type::ServdBindError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
#endif
if(!socket.join_multicast("127.0.0.1", address.ip())) {
EventManager::GetInstance().add(APIEvent::Type::ServdJoinMulticastError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
std::vector<uint8_t> buf(65535);
while(!isDisconnected() && !isClosing()) { while(!isDisconnected() && !isClosing()) {
bool hasData; bool hasData;
if(!socket.poll(std::chrono::milliseconds(100), hasData)) { if(!dataSocket->poll(std::chrono::milliseconds(100), hasData)) {
EventManager::GetInstance().add(APIEvent::Type::ServdPollError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdPollError, APIEvent::Severity::Error);
setIsDisconnected(true); setIsDisconnected(true);
return; return;
@@ -299,7 +249,7 @@ void Servd::read(Address&& address) {
continue; continue;
} }
size_t bufSize = buf.size(); size_t bufSize = buf.size();
if(!socket.recv(buf.data(), bufSize)) { if(!dataSocket->recv(buf.data(), bufSize)) {
EventManager::GetInstance().add(APIEvent::Type::ServdRecvError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdRecvError, APIEvent::Severity::Error);
setIsDisconnected(true); setIsDisconnected(true);
return; return;
@@ -308,16 +258,14 @@ void Servd::read(Address&& address) {
} }
} }
void Servd::write(Address&& address) { void Servd::write() {
Socket socket;
socket.bind(Address("127.0.0.1", 0));
WriteOperation writeOp; WriteOperation writeOp;
while(!isDisconnected() && !isClosing()) { while(!isDisconnected() && !isClosing()) {
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100))) { if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100))) {
continue; continue;
} }
if(!isClosing()) { if(!isClosing()) {
if(!socket.sendto(writeOp.bytes.data(), writeOp.bytes.size(), address)) { if(!dataSocket->send(writeOp.bytes.data(), writeOp.bytes.size())) {
EventManager::GetInstance().add(APIEvent::Type::ServdSendError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdSendError, APIEvent::Severity::Error);
setIsDisconnected(true); setIsDisconnected(true);
return; return;