diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f4351c..babaf25 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -246,6 +246,7 @@ set(SRC_FILES communication/message/linmessage.cpp communication/message/livedatamessage.cpp communication/message/tc10statusmessage.cpp + communication/message/gptpstatusmessage.cpp communication/packet/flexraypacket.cpp communication/packet/canpacket.cpp communication/packet/a2bpacket.cpp diff --git a/api/icsneocpp/event.cpp b/api/icsneocpp/event.cpp index fd26018..265b6df 100644 --- a/api/icsneocpp/event.cpp +++ b/api/icsneocpp/event.cpp @@ -120,6 +120,7 @@ static constexpr const char* DISK_NOT_CONNECTED = "The program tried to access a static constexpr const char* UNEXPECTED_RESPONSE = "Received an unexpected or invalid response from the device."; static constexpr const char* LIN_SETTINGS_NOT_AVAILABLE = "LIN settings are not available for this device."; static constexpr const char* MODE_NOT_FOUND = "The mode was not found."; +static constexpr const char* GPTP_NOT_SUPPORTED = "GPTP clock synchronization is not supported on this device."; // Transport Errors static constexpr const char* FAILED_TO_READ = "A read operation failed."; @@ -185,6 +186,7 @@ static constexpr const char* VSA_OTHER_ERROR = "Unknown error in VSA read API."; static constexpr const char* TOO_MANY_EVENTS = "Too many events have occurred. The list has been truncated."; static constexpr const char* UNKNOWN = "An unknown internal error occurred."; static constexpr const char* INVALID = "An invalid internal error occurred."; + const char* APIEvent::DescriptionForType(Type type) { switch(type) { // API Errors @@ -345,6 +347,8 @@ const char* APIEvent::DescriptionForType(Type type) { return GETIFADDRS_ERROR; case Type::SendToError: return SEND_TO_ERROR; + case Type::GPTPNotSupported: + return GPTP_NOT_SUPPORTED; // FTD3XX case Type::FTOK: diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 1d8659c..2007fda 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -16,6 +16,7 @@ pybind11_add_module(icsneopy icsneopy/communication/message/linmessage.cpp icsneopy/communication/message/tc10statusmessage.cpp icsneopy/communication/message/mdiomessage.cpp + icsneopy/communication/message/gptpstatusmessage.cpp icsneopy/communication/message/callback/messagecallback.cpp icsneopy/communication/message/filter/messagefilter.cpp icsneopy/device/device.cpp diff --git a/bindings/python/icsneopy/communication/message/gptpstatusmessage.cpp b/bindings/python/icsneopy/communication/message/gptpstatusmessage.cpp new file mode 100644 index 0000000..1355227 --- /dev/null +++ b/bindings/python/icsneopy/communication/message/gptpstatusmessage.cpp @@ -0,0 +1,79 @@ +#include +#include +#include + +#include "icsneo/communication/message/gptpstatusmessage.h" + +namespace icsneo { + +void init_gptpstatusmessage(pybind11::module_& m) { + pybind11::class_, Message> gptpStatus(m, "GPTPStatus"); + + pybind11::class_(gptpStatus, "Timestamp") + .def_readonly("seconds", &GPTPStatus::Timestamp::seconds) + .def_readonly("nanoseconds", &GPTPStatus::Timestamp::nanoseconds) + .def("to_seconds", &GPTPStatus::Timestamp::toSeconds, pybind11::call_guard()); + + pybind11::class_(gptpStatus, "ScaledNanoSeconds") + .def_readonly("nanosecondsMSB", &GPTPStatus::ScaledNanoSeconds::nanosecondsMSB) + .def_readonly("nanosecondsLSB", &GPTPStatus::ScaledNanoSeconds::nanosecondsLSB) + .def_readonly("fractionalNanoseconds", &GPTPStatus::ScaledNanoSeconds::fractionalNanoseconds); + + pybind11::class_(gptpStatus, "PortID") + .def_readonly("clockIdentity", &GPTPStatus::PortID::clockIdentity) + .def_readonly("portNumber", &GPTPStatus::PortID::portNumber); + + pybind11::class_(gptpStatus, "ClockQuality") + .def_readonly("clockClass", &GPTPStatus::ClockQuality::clockClass) + .def_readonly("clockAccuracy", &GPTPStatus::ClockQuality::clockAccuracy) + .def_readonly("offsetScaledLogVariance", &GPTPStatus::ClockQuality::offsetScaledLogVariance); + + pybind11::class_(gptpStatus, "SystemID") + .def_readonly("priority1", &GPTPStatus::SystemID::priority1) + .def_readonly("clockQuality", &GPTPStatus::SystemID::clockQuality) + .def_readonly("priority2", &GPTPStatus::SystemID::priority2) + .def_readonly("clockID", &GPTPStatus::SystemID::clockID); + + pybind11::class_(gptpStatus, "PriorityVector") + .def_readonly("sysID", &GPTPStatus::PriorityVector::sysID) + .def_readonly("stepsRemoved", &GPTPStatus::PriorityVector::stepsRemoved) + .def_readonly("portID", &GPTPStatus::PriorityVector::portID) + .def_readonly("portNumber", &GPTPStatus::PriorityVector::portNumber); + + pybind11::class_(gptpStatus, "ParentDS") + .def_readonly("parentPortIdentity", &GPTPStatus::ParentDS::parentPortIdentity) + .def_readonly("cumulativeRateRatio", &GPTPStatus::ParentDS::cumulativeRateRatio) + .def_readonly("grandmasterIdentity", &GPTPStatus::ParentDS::grandmasterIdentity) + .def_readonly("gmClockQualityClockClass", &GPTPStatus::ParentDS::gmClockQualityClockClass) + .def_readonly("gmClockQualityClockAccuracy", &GPTPStatus::ParentDS::gmClockQualityClockAccuracy) + .def_readonly("gmClockQualityOffsetScaledLogVariance", &GPTPStatus::ParentDS::gmClockQualityOffsetScaledLogVariance) + .def_readonly("gmPriority1", &GPTPStatus::ParentDS::gmPriority1) + .def_readonly("gmPriority2", &GPTPStatus::ParentDS::gmPriority2); + + pybind11::class_(gptpStatus, "CurrentDS") + .def_readonly("stepsRemoved", &GPTPStatus::CurrentDS::stepsRemoved) + .def_readonly("offsetFromMaster", &GPTPStatus::CurrentDS::offsetFromMaster) + .def_readonly("lastgmPhaseChange", &GPTPStatus::CurrentDS::lastgmPhaseChange) + .def_readonly("lastgmFreqChange", &GPTPStatus::CurrentDS::lastgmFreqChange) + .def_readonly("gmTimeBaseIndicator", &GPTPStatus::CurrentDS::gmTimeBaseIndicator) + .def_readonly("gmChangeCount", &GPTPStatus::CurrentDS::gmChangeCount) + .def_readonly("timeOfLastgmChangeEvent", &GPTPStatus::CurrentDS::timeOfLastgmChangeEvent) + .def_readonly("timeOfLastgmPhaseChangeEvent", &GPTPStatus::CurrentDS::timeOfLastgmPhaseChangeEvent) + .def_readonly("timeOfLastgmFreqChangeEvent", &GPTPStatus::CurrentDS::timeOfLastgmFreqChangeEvent); + + gptpStatus.def_readonly("currentTime", &GPTPStatus::currentTime) + .def_readonly("gmPriority", &GPTPStatus::gmPriority) + .def_readonly("msOffsetNs", &GPTPStatus::msOffsetNs) + .def_readonly("isSync", &GPTPStatus::isSync) + .def_readonly("linkStatus", &GPTPStatus::linkStatus) + .def_readonly("linkDelayNS", &GPTPStatus::linkDelayNS) + .def_readonly("selectedRole", &GPTPStatus::selectedRole) + .def_readonly("asCapable", &GPTPStatus::asCapable) + .def_readonly("isSyntonized", &GPTPStatus::isSyntonized) + .def_readonly("lastRXSyncTS", &GPTPStatus::lastRXSyncTS) + .def_readonly("currentDS", &GPTPStatus::currentDS) + .def_readonly("parentDS", &GPTPStatus::parentDS); + +} + +} // namespace icsneo diff --git a/bindings/python/icsneopy/communication/message/message.cpp b/bindings/python/icsneopy/communication/message/message.cpp index 627a7e6..ddec0ed 100644 --- a/bindings/python/icsneopy/communication/message/message.cpp +++ b/bindings/python/icsneopy/communication/message/message.cpp @@ -31,7 +31,8 @@ void init_message(pybind11::module_& m) { .value("LiveData", Message::Type::LiveData) .value("HardwareInfo", Message::Type::HardwareInfo) .value("TC10Status", Message::Type::TC10Status) - .value("AppError", Message::Type::AppError); + .value("AppError", Message::Type::AppError) + .value("GPTPStatus", Message::Type::GPTPStatus); message.def(pybind11::init()); message.def_readonly("type", &Message::type); diff --git a/bindings/python/icsneopy/device/device.cpp b/bindings/python/icsneopy/device/device.cpp index 1fc484d..80db3bc 100644 --- a/bindings/python/icsneopy/device/device.cpp +++ b/bindings/python/icsneopy/device/device.cpp @@ -39,6 +39,7 @@ void init_device(pybind11::module_& m) { .def("request_tc10_wake", &Device::requestTC10Wake, pybind11::call_guard()) .def("request_tc10_sleep", &Device::requestTC10Sleep, pybind11::call_guard()) .def("get_tc10_status", &Device::getTC10Status, pybind11::call_guard()) + .def("get_gptp_status", &Device::getGPTPStatus, pybind11::call_guard()) .def("__repr__", &Device::describe); } diff --git a/bindings/python/icsneopy/icsneocpp.cpp b/bindings/python/icsneopy/icsneocpp.cpp index 622b468..fb49bd8 100644 --- a/bindings/python/icsneopy/icsneocpp.cpp +++ b/bindings/python/icsneopy/icsneocpp.cpp @@ -16,6 +16,7 @@ void init_canmessage(pybind11::module_&); void init_ethernetmessage(pybind11::module_&); void init_linmessage(pybind11::module_&); void init_tc10statusmessage(pybind11::module_&); +void init_gptpstatusmessage(pybind11::module_&); void init_mdiomessage(pybind11::module_&); void init_device(pybind11::module_&); void init_messagefilter(pybind11::module_&); @@ -36,6 +37,7 @@ PYBIND11_MODULE(icsneopy, m) { init_ethernetmessage(m); init_linmessage(m); init_tc10statusmessage(m); + init_gptpstatusmessage(m); init_mdiomessage(m); init_messagefilter(m); init_messagecallback(m); diff --git a/communication/decoder.cpp b/communication/decoder.cpp index 34e9b8e..2400def 100644 --- a/communication/decoder.cpp +++ b/communication/decoder.cpp @@ -19,6 +19,7 @@ #include "icsneo/communication/message/diskdatamessage.h" #include "icsneo/communication/message/hardwareinfo.h" #include "icsneo/communication/message/tc10statusmessage.h" +#include "icsneo/communication/message/gptpstatusmessage.h" #include "icsneo/communication/message/apperrormessage.h" #include "icsneo/communication/command.h" #include "icsneo/device/device.h" @@ -285,11 +286,11 @@ bool Decoder::decode(std::shared_ptr& result, const std::shared_ptrdata.size() < sizeof(ExtendedResponseMessage::PackedGenericResponse)) + if(packet->data.size() < sizeof(ExtendedResponseMessage::ResponseHeader)) break; // Handle as a raw message, might not be a generic response - const auto& resp = *reinterpret_cast(packet->data.data()); - switch(resp.header.command) { + const auto& resp = *reinterpret_cast(packet->data.data()); + switch(resp.command) { case ExtendedCommand::GetComponentVersions: result = ComponentVersionPacket::DecodeToMessage(packet->data); return true; @@ -299,15 +300,23 @@ bool Decoder::decode(std::shared_ptr& result, const std::shared_ptrdata); return true; - case ExtendedCommand::GenericReturn: - result = std::make_shared(resp.command, resp.returnCode); + case ExtendedCommand::GenericReturn: { + if(packet->data.size() < sizeof(ExtendedResponseMessage::PackedGenericResponse)) + break; + const auto& packedResp = *reinterpret_cast(packet->data.data()); + result = std::make_shared(packedResp.command, packedResp.returnCode); return true; + } case ExtendedCommand::LiveData: result = HardwareLiveDataPacket::DecodeToMessage(packet->data, report); return true; case ExtendedCommand::GetTC10Status: result = TC10StatusMessage::DecodeToMessage(packet->data); return true; + case ExtendedCommand::GetGPTPStatus: { + result = GPTPStatus::DecodeToMessage(packet->data, report); + return true; + } default: // No defined handler, treat this as a RawMessage break; diff --git a/communication/message/gptpstatusmessage.cpp b/communication/message/gptpstatusmessage.cpp new file mode 100644 index 0000000..e62e5aa --- /dev/null +++ b/communication/message/gptpstatusmessage.cpp @@ -0,0 +1,219 @@ +#include +#include +#include + +namespace icsneo { +typedef double float64; +typedef int64_t time_interval; +typedef uint64_t _clock_identity; + + +#pragma pack(push, 1) + +struct port_identity { + _clock_identity clock_identity; + uint16_t port_number; +}; + +struct _scaled_ns { + int16_t nanoseconds_msb; + int64_t nanoseconds_lsb; + int16_t fractional_nanoseconds; +}; + +struct _clock_quality { + uint8_t clock_class; + uint8_t clock_accuracy; + uint16_t offset_scaled_log_variance; +}; + +struct system_identity { + uint8_t priority_1; + struct _clock_quality clock_quality; + uint8_t priority_2; + _clock_identity clock_identity; +}; + +struct _timestamp { + uint16_t seconds_msb; + uint32_t seconds_lsb; + uint32_t nanoseconds; +}; + +struct priority_vector { + struct system_identity sysid; + uint16_t steps_removed; + struct port_identity portid; + uint16_t port_number; +}; + + +// IEEE 802.1AS-2020 14.3 +// This is a read-only data structure +struct _current_ds { + uint16_t steps_removed; + time_interval offset_from_master; + struct _scaled_ns last_gm_phase_change; + float64 last_gm_freq_change; + uint16_t gm_time_base_indicator; + uint32_t gm_change_count; + uint32_t time_of_last_gm_change_event; + uint32_t time_of_last_gm_phase_change_event; + uint32_t time_of_last_gm_freq_change_event; +}; + +// IEEE 802.1AS-2020 14.4 +// This is a read-only data structure +struct _parent_ds { + struct port_identity parent_port_identity; + int32_t cumulative_rate_ratio; + _clock_identity grandmaster_identity; + uint8_t gm_clock_quality_clock_class; + uint8_t gm_clock_quality_clock_accuracy; + uint16_t gm_clock_quality_offset_scaled_log_variance; + uint8_t gm_priority1; + uint8_t gm_priority2; +}; + +struct _GPTPStatus +{ + struct _timestamp current_time; + struct priority_vector gm_priority; + int64_t ms_offset_ns; + uint8_t is_sync; + uint8_t link_status; + int64_t link_delay_ns; + uint8_t selected_role; + uint8_t as_capable; + uint8_t is_syntonized; + struct _timestamp last_rx_sync_ts; // t2 in IEEE 1588-2019 Figure-16 + struct _current_ds current_ds; + struct _parent_ds parent_ds; +}; + +#pragma pack(pop) + +static void SetField(GPTPStatus::Timestamp& output, const _timestamp& input) { + output.seconds = ((uint64_t)(input.seconds_msb) << 32) | ((uint64_t)input.seconds_lsb); + output.nanoseconds = input.nanoseconds; +} + +static void SetField(GPTPStatus::PortID& output, const port_identity& input) { + output.clockIdentity = input.clock_identity; + output.portNumber = input.port_number; +} +static void SetField(GPTPStatus::ClockQuality& output, const _clock_quality& input) { + output.clockClass = input.clock_class; + output.clockAccuracy = input.clock_accuracy; + output.offsetScaledLogVariance = input.offset_scaled_log_variance; +} + +static void SetField(GPTPStatus::SystemID& output, const system_identity& input) { + output.priority1 = input.priority_1; + SetField(output.clockQuality, input.clock_quality); + output.priority2 = input.priority_2; + output.clockID = input.clock_identity; +} + +static void SetField(GPTPStatus::ScaledNanoSeconds& output, const _scaled_ns& input) { + output.nanosecondsMSB = input.nanoseconds_msb; + output.nanosecondsLSB = input.nanoseconds_lsb; + output.fractionalNanoseconds = input.fractional_nanoseconds; +} + +static void SetField(GPTPStatus::PriorityVector& output, const priority_vector& input) { + SetField(output.sysID, input.sysid); + output.stepsRemoved = input.steps_removed; + SetField(output.portID, input.portid); + output.portNumber = input.port_number; +} + +static void SetField(GPTPStatus::CurrentDS& output, const _current_ds& input) { + output.stepsRemoved = input.steps_removed; + output.offsetFromMaster = input.offset_from_master; + SetField(output.lastgmPhaseChange, input.last_gm_phase_change); + output.lastgmFreqChange = input.last_gm_freq_change; + output.gmTimeBaseIndicator = input.gm_time_base_indicator; + output.gmChangeCount = input.gm_change_count; + output.timeOfLastgmChangeEvent = input.time_of_last_gm_change_event; + output.timeOfLastgmPhaseChangeEvent = input.time_of_last_gm_phase_change_event; + output.timeOfLastgmFreqChangeEvent = input.time_of_last_gm_freq_change_event; +} + +static void SetField(GPTPStatus::ParentDS& output, const _parent_ds& input) { + SetField(output.parentPortIdentity, input.parent_port_identity); + output.cumulativeRateRatio = input.cumulative_rate_ratio; + output.grandmasterIdentity = input.grandmaster_identity; + output.gmClockQualityClockClass = input.gm_clock_quality_clock_class; + output.gmClockQualityClockAccuracy = input.gm_clock_quality_clock_accuracy; + output.gmClockQualityOffsetScaledLogVariance = input.gm_clock_quality_offset_scaled_log_variance; + output.gmPriority1 = input.gm_priority1; + output.gmPriority2 = input.gm_priority2; +} + +[[maybe_unused]] static void SetField(uint8_t& output, const uint8_t& input) { + output = input; +} + +[[maybe_unused]] static void SetField(uint16_t& output, const uint16_t& input) { + output = input; +} + +[[maybe_unused]] static void SetField(uint32_t& output, const uint32_t& input) { + output = input; +} + +[[maybe_unused]] static void SetField(uint64_t& output, const uint64_t& input) { + output = input; +} + +[[maybe_unused]] static void SetField(int8_t& output, const int8_t& input) { + output = input; +} + +[[maybe_unused]] static void SetField(int16_t& output, const int16_t& input) { + output = input; +} + +[[maybe_unused]] static void SetField(int32_t& output, const int32_t& input) { + output = input; +} + +[[maybe_unused]] static void SetField(int64_t& output, const int64_t& input) { + output = input; +} + +std::shared_ptr GPTPStatus::DecodeToMessage(std::vector& bytes, const device_eventhandler_t&) { + + // The following does not lead to overflow since we only call this function if it has at least ResponseHeader length bytes + std::shared_ptr res = std::make_shared(); + auto* header = reinterpret_cast(bytes.data()); + uint16_t length = header->length; + _GPTPStatus* input = reinterpret_cast<_GPTPStatus*>(bytes.data() + sizeof(ExtendedResponseMessage::ResponseHeader)); + +#define CheckLengthAndSet(output, input) if(length >= sizeof(decltype(input))) { \ + SetField(output, input); \ + length -= sizeof(decltype(input)); \ + } else {\ + memset(&output, 0, sizeof(decltype(output))); \ + length = 0; \ + res->shortFormat = true; \ + } + + CheckLengthAndSet(res->currentTime, input->current_time); + CheckLengthAndSet(res->gmPriority, input->gm_priority); + CheckLengthAndSet(res->msOffsetNs, input->ms_offset_ns); + CheckLengthAndSet(res->isSync, input->is_sync); + CheckLengthAndSet(res->linkStatus, input->link_status); + CheckLengthAndSet(res->linkDelayNS, input->link_delay_ns); + CheckLengthAndSet(res->selectedRole, input->selected_role); + CheckLengthAndSet(res->asCapable, input->as_capable); + CheckLengthAndSet(res->isSyntonized, input->is_syntonized); + CheckLengthAndSet(res->lastRXSyncTS, input->last_rx_sync_ts); + CheckLengthAndSet(res->currentDS, input->current_ds); + CheckLengthAndSet(res->parentDS, input->parent_ds); + + return res; +} + +} \ No newline at end of file diff --git a/device/device.cpp b/device/device.cpp index 7ef98dc..524dea0 100644 --- a/device/device.cpp +++ b/device/device.cpp @@ -3335,3 +3335,34 @@ std::optional Device::getTC10Status(Network::NetID network) { return *typed; } + +std::optional Device::getGPTPStatus(std::chrono::milliseconds timeout) { + if(!supportsGPTP()) { + report(APIEvent::Type::GPTPNotSupported, APIEvent::Severity::Error); + return std::nullopt; + } + + if(!isOpen()) { + report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error); + return std::nullopt; + } + + std::shared_ptr response = com->waitForMessageSync( + [this](){ + return com->sendCommand(ExtendedCommand::GetGPTPStatus, {}); + }, + std::make_shared(Message::Type::GPTPStatus), + timeout + ); + + if(!response) { + report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error); + return std::nullopt; + } + auto retMsg = std::static_pointer_cast(response); + if(!retMsg) { + return std::nullopt; + } + + return *retMsg; +} diff --git a/include/icsneo/api/event.h b/include/icsneo/api/event.h index f8c1539..666d1d9 100644 --- a/include/icsneo/api/event.h +++ b/include/icsneo/api/event.h @@ -109,6 +109,7 @@ public: LINSettingsNotAvailable = 0x2053, ModeNotFound = 0x2054, AppErrorParsingFailed = 0x2055, + GPTPNotSupported = 0x2056, // Transport Events FailedToRead = 0x3000, diff --git a/include/icsneo/communication/command.h b/include/icsneo/communication/command.h index 1fb96fb..9700fd2 100644 --- a/include/icsneo/communication/command.h +++ b/include/icsneo/communication/command.h @@ -51,6 +51,7 @@ enum class ExtendedCommand : uint16_t { StartDHCPServer = 0x0016, StopDHCPServer = 0x0017, GetSupportedFeatures = 0x0018, + GetGPTPStatus = 0x0019, GetComponentVersions = 0x001A, Reboot = 0x001C, SetRootFSEntryFlags = 0x0027, diff --git a/include/icsneo/communication/message/gptpstatusmessage.h b/include/icsneo/communication/message/gptpstatusmessage.h new file mode 100644 index 0000000..7738a48 --- /dev/null +++ b/include/icsneo/communication/message/gptpstatusmessage.h @@ -0,0 +1,103 @@ +#ifndef __GPTPSTATUSMESSAGE_H_ +#define __GPTPSTATUSMESSAGE_H_ + +#ifdef __cplusplus + +#include "icsneo/communication/message/message.h" +#include "icsneo/communication/command.h" +#include "icsneo/api/eventmanager.h" + + +namespace icsneo { + +class GPTPStatus : public Message { +public: + typedef uint64_t TimeInterval; + typedef uint64_t ClockID; + + struct Timestamp { + uint64_t seconds; + uint32_t nanoseconds; + + double toSeconds() const { + static constexpr double billion = 1e9; + return (double)seconds + ((double)nanoseconds) / billion; + } + }; + + struct ScaledNanoSeconds { + int16_t nanosecondsMSB; // The most significant bits + int64_t nanosecondsLSB; // The least significant bits + int16_t fractionalNanoseconds; // Fractional part + }; + + struct PortID { + ClockID clockIdentity; + uint16_t portNumber; + }; + struct ClockQuality { + uint8_t clockClass; + uint8_t clockAccuracy; + uint16_t offsetScaledLogVariance; + }; + + struct SystemID { + uint8_t priority1; + ClockQuality clockQuality; + uint8_t priority2; + ClockID clockID; + }; + + struct PriorityVector { + SystemID sysID; + uint16_t stepsRemoved; + PortID portID; + uint16_t portNumber; + }; + + struct ParentDS { + PortID parentPortIdentity; + int32_t cumulativeRateRatio; + ClockID grandmasterIdentity; + uint8_t gmClockQualityClockClass; + uint8_t gmClockQualityClockAccuracy; + uint16_t gmClockQualityOffsetScaledLogVariance; + uint8_t gmPriority1; + uint8_t gmPriority2; + }; + + struct CurrentDS { + uint16_t stepsRemoved; + TimeInterval offsetFromMaster; + ScaledNanoSeconds lastgmPhaseChange; + double lastgmFreqChange; + uint16_t gmTimeBaseIndicator; + uint32_t gmChangeCount; + uint32_t timeOfLastgmChangeEvent; + uint32_t timeOfLastgmPhaseChangeEvent; + uint32_t timeOfLastgmFreqChangeEvent; + }; + + GPTPStatus() : Message(Message::Type::GPTPStatus) {} + static std::shared_ptr DecodeToMessage(std::vector& bytes, const device_eventhandler_t& report); + + Timestamp currentTime; + PriorityVector gmPriority; + int64_t msOffsetNs; + uint8_t isSync; + uint8_t linkStatus; + int64_t linkDelayNS; + uint8_t selectedRole; + uint8_t asCapable; + uint8_t isSyntonized; + Timestamp lastRXSyncTS; // t2 in IEEE 1588-2019 Figure-16 + CurrentDS currentDS; + ParentDS parentDS; + + bool shortFormat = false; // Set to true if the above variables weren't set (some firmware versions do not contain all the above variables) +}; + +} + +#endif +#endif diff --git a/include/icsneo/communication/message/message.h b/include/icsneo/communication/message/message.h index 259ce7a..0414fcb 100644 --- a/include/icsneo/communication/message/message.h +++ b/include/icsneo/communication/message/message.h @@ -41,6 +41,7 @@ public: HardwareInfo = 0x8010, TC10Status = 0x8011, AppError = 0x8012, + GPTPStatus = 0x8013 }; Message(Type t) : type(t) {} diff --git a/include/icsneo/device/device.h b/include/icsneo/device/device.h index 84856ea..8a67c5b 100644 --- a/include/icsneo/device/device.h +++ b/include/icsneo/device/device.h @@ -50,6 +50,7 @@ #include "icsneo/disk/vsa/vsa.h" #include "icsneo/disk/vsa/vsaparser.h" #include "icsneo/communication/message/versionmessage.h" +#include "icsneo/communication/message/gptpstatusmessage.h" #define ICSNEO_FINDABLE_DEVICE_BASE(className, type) \ @@ -727,12 +728,15 @@ public: virtual bool supportsComponentVersions() const { return false; } virtual bool supportsTC10() const { return false; } + + virtual bool supportsGPTP() const { return false; } bool requestTC10Wake(Network::NetID network); bool requestTC10Sleep(Network::NetID network); std::optional getTC10Status(Network::NetID network); + std::optional getGPTPStatus(std::chrono::milliseconds timeout = std::chrono::milliseconds(100)); protected: bool online = false; diff --git a/include/icsneo/device/idevicesettings.h b/include/icsneo/device/idevicesettings.h index 968f362..aafaab9 100644 --- a/include/icsneo/device/idevicesettings.h +++ b/include/icsneo/device/idevicesettings.h @@ -444,20 +444,21 @@ typedef struct LOGGER_SETTINGS_t #define RAD_GPTP_NUM_PORTS 1 // 1 because only supported as gPTP endpoint typedef struct RAD_GPTP_SETTINGS_t { - uint32_t neighborPropDelayThresh;//ns - uint32_t sys_phc_sync_interval;//ns - int8_t logPDelayReqInterval;// log2ms - int8_t logSyncInterval;// log2ms - int8_t logAnnounceInterval;// log2ms + uint32_t neighborPropDelayThresh; //ns + uint32_t sys_phc_sync_interval; //ns + int8_t logPDelayReqInterval; // log2ms + int8_t logSyncInterval; // log2ms + int8_t logAnnounceInterval; // log2ms uint8_t profile; uint8_t priority1; uint8_t clockclass; uint8_t clockaccuracy; uint8_t priority2; uint16_t offset_scaled_log_variance; - uint8_t gPTPportRole[RAD_GPTP_NUM_PORTS]; - uint8_t portEnable[RAD_GPTP_NUM_PORTS]; - uint8_t rsvd[16]; + uint8_t gptpPortRole; + uint8_t gptpEnabledPort; + uint8_t enableClockSyntonization; + uint8_t rsvd[15]; } RAD_GPTP_SETTINGS;//36 Bytes with RAD_GPTP_NUM_PORTS = 1 #define RAD_GPTP_SETTINGS_SIZE 36 diff --git a/include/icsneo/device/tree/neovifire3/neovifire3.h b/include/icsneo/device/tree/neovifire3/neovifire3.h index 9c0c702..e4835dd 100644 --- a/include/icsneo/device/tree/neovifire3/neovifire3.h +++ b/include/icsneo/device/tree/neovifire3/neovifire3.h @@ -79,6 +79,8 @@ protected: bool supportsLiveData() const override { return true; } + bool supportsGPTP() const override { return true; } + std::optional getCoreminiStartAddressFlash() const override { return 33*1024*1024; } @@ -90,6 +92,7 @@ protected: bool supportsEraseMemory() const override { return true; } + }; } diff --git a/include/icsneo/device/tree/neovired2/neovired2.h b/include/icsneo/device/tree/neovired2/neovired2.h index e4ba909..d25e7ce 100644 --- a/include/icsneo/device/tree/neovired2/neovired2.h +++ b/include/icsneo/device/tree/neovired2/neovired2.h @@ -36,6 +36,7 @@ public: } bool supportsComponentVersions() const override { return true; } + bool supportsGPTP() const override { return true; } protected: NeoVIRED2(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) { diff --git a/include/icsneo/device/tree/rada2b/rada2b.h b/include/icsneo/device/tree/rada2b/rada2b.h index 8a39ac0..20ab58e 100644 --- a/include/icsneo/device/tree/rada2b/rada2b.h +++ b/include/icsneo/device/tree/rada2b/rada2b.h @@ -40,6 +40,7 @@ public: } size_t getEthernetActivationLineCount() const override { return 1; } + bool supportsGPTP() const override { return true; } protected: RADA2B(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) { @@ -77,6 +78,7 @@ protected: std::optional getCoreminiStartAddressSD() const override { return 0; } + }; } diff --git a/include/icsneo/device/tree/radcomet/radcometbase.h b/include/icsneo/device/tree/radcomet/radcometbase.h index 4c2c8b6..db76a7f 100644 --- a/include/icsneo/device/tree/radcomet/radcometbase.h +++ b/include/icsneo/device/tree/radcomet/radcometbase.h @@ -31,6 +31,7 @@ public: } bool getEthPhyRegControlSupported() const override { return true; } + bool supportsGPTP() const override { return true; } protected: using Device::Device; diff --git a/include/icsneo/device/tree/radcomet3/radcomet3.h b/include/icsneo/device/tree/radcomet3/radcomet3.h index 476d8f2..896d323 100644 --- a/include/icsneo/device/tree/radcomet3/radcomet3.h +++ b/include/icsneo/device/tree/radcomet3/radcomet3.h @@ -45,6 +45,7 @@ public: bool getEthPhyRegControlSupported() const override { return true; } bool supportsTC10() const override { return true; } + bool supportsGPTP() const override { return true; } protected: RADComet3(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) { diff --git a/include/icsneo/device/tree/radgalaxy/radgalaxy.h b/include/icsneo/device/tree/radgalaxy/radgalaxy.h index 887740c..e9e3049 100644 --- a/include/icsneo/device/tree/radgalaxy/radgalaxy.h +++ b/include/icsneo/device/tree/radgalaxy/radgalaxy.h @@ -61,6 +61,7 @@ public: } size_t getEthernetActivationLineCount() const override { return 1; } + bool supportsGPTP() const override { return true; } protected: RADGalaxy(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) { diff --git a/include/icsneo/device/tree/radgalaxy2/radgalaxy2.h b/include/icsneo/device/tree/radgalaxy2/radgalaxy2.h index 1946438..7eccfe9 100644 --- a/include/icsneo/device/tree/radgalaxy2/radgalaxy2.h +++ b/include/icsneo/device/tree/radgalaxy2/radgalaxy2.h @@ -65,6 +65,7 @@ public: size_t getEthernetActivationLineCount() const override { return 1; } bool supportsTC10() const override { return true; } + bool supportsGPTP() const override { return true; } protected: RADGalaxy2(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) { diff --git a/include/icsneo/device/tree/radgigastar/radgigastar.h b/include/icsneo/device/tree/radgigastar/radgigastar.h index debed94..66b5a48 100644 --- a/include/icsneo/device/tree/radgigastar/radgigastar.h +++ b/include/icsneo/device/tree/radgigastar/radgigastar.h @@ -22,7 +22,8 @@ public: bool getEthPhyRegControlSupported() const override { return true; } bool supportsTC10() const override { return true; } - + bool supportsGPTP() const override { return true; } + protected: RADGigastar(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) { initialize(makeDriver); diff --git a/include/icsneo/device/tree/radgigastar2/radgigastar2.h b/include/icsneo/device/tree/radgigastar2/radgigastar2.h index 700e168..b82f940 100644 --- a/include/icsneo/device/tree/radgigastar2/radgigastar2.h +++ b/include/icsneo/device/tree/radgigastar2/radgigastar2.h @@ -76,7 +76,8 @@ namespace icsneo bool getEthPhyRegControlSupported() const override { return true; } bool supportsTC10() const override { return true; } - + bool supportsGPTP() const override { return true; } + protected: RADGigastar2(neodevice_t neodevice, const driver_factory_t &makeDriver) : Device(neodevice) { diff --git a/include/icsneo/device/tree/radmars/radmars.h b/include/icsneo/device/tree/radmars/radmars.h index e122789..4e72bc4 100644 --- a/include/icsneo/device/tree/radmars/radmars.h +++ b/include/icsneo/device/tree/radmars/radmars.h @@ -19,6 +19,7 @@ public: ICSNEO_FINDABLE_DEVICE(RADMars, DeviceType::RADMars, "GL"); size_t getEthernetActivationLineCount() const override { return 1; } + bool supportsGPTP() const override { return true; } protected: RADMars(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) {