EventSourceAdapter.h 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #pragma once
  9. #include <AzCore/Serialization/EditContext.h>
  10. #include <ROS2/ROS2Bus.h>
  11. #include <ROS2/ROS2TypeIds.h>
  12. #include <ROS2/Sensor/Events/SensorEventSource.h>
  13. namespace ROS2
  14. {
  15. namespace Internal
  16. {
  17. template<
  18. template<
  19. template<typename...>
  20. class, // EventType
  21. template<typename...>
  22. class, // EventHandlerType
  23. typename...> // Event parameters
  24. class C,
  25. class T>
  26. struct is_specialization_of : AZStd::false_type
  27. {
  28. };
  29. template<
  30. template<
  31. template<typename...>
  32. class, // EventType
  33. template<typename...>
  34. class, // EventHandlerType
  35. typename...> // Event parameters
  36. class Base,
  37. template<typename...>
  38. class EventType,
  39. template<typename...>
  40. class EventHandlerType,
  41. typename... Args>
  42. struct is_specialization_of<Base, Base<EventType, EventHandlerType, Args...>> : AZStd::true_type
  43. {
  44. };
  45. } // namespace Internal
  46. //! Class adapting event source (ROS2::SensorEventSource) to configurable working frequency. This is handled via adapted event, in
  47. //! a similar manner like it is done in SensorEventSource. EventSourceAdapter has its internal handler that connects to
  48. //! SensorEventSource source event, and signals adapted event according to frequency set (ROS2::EventSourceAdapter::SetFrequency).
  49. //! User can connect to this event using ROS2::EventSourceAdapter::ConnectToAdaptedEvent method. This class should be used, instead
  50. //! of using directly a class derived from SensorEventSource, when specific working frequency is required. Following this path, user can
  51. //! still use source event - ROS2::EventSourceAdapter::ConnectToSourceEvent. This template has to be resolved using a class derived from
  52. //! SensorEventSource specialization.
  53. //! @see ROS2::SensorEventSource
  54. template<class EventSourceT>
  55. class EventSourceAdapter
  56. {
  57. public:
  58. // Require non-abstract type derived from SensorEventSource specialization.
  59. static_assert(Internal::is_specialization_of<SensorEventSource, typename EventSourceT::SourceBaseType>::value);
  60. static_assert(AZStd::is_base_of<typename EventSourceT::SourceBaseType, EventSourceT>::value);
  61. static_assert(AZStd::is_abstract<EventSourceT>::value == false);
  62. static void Reflect(AZ::ReflectContext* context)
  63. {
  64. EventSourceT::Reflect(context);
  65. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  66. {
  67. serializeContext->Class<EventSourceAdapter<EventSourceT>>()
  68. ->Version(1)
  69. ->Field("Adapted frequency", &EventSourceAdapter<EventSourceT>::m_adaptedFrequency)
  70. ->Field("Event source configuration", &EventSourceAdapter<EventSourceT>::m_eventSource);
  71. if (auto* editContext = serializeContext->GetEditContext())
  72. {
  73. editContext
  74. ->Class<EventSourceAdapter<EventSourceT>>(
  75. "Event Source Adapter", "Adapts sensor event source to specified working frequency")
  76. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  77. ->ElementAttribute(AZ::Edit::Attributes::AutoExpand, true)
  78. ->DataElement(
  79. AZ::Edit::UIHandlers::Default,
  80. &EventSourceAdapter<EventSourceT>::m_adaptedFrequency,
  81. "Adapted frequency",
  82. "Adapter event signalling frequency");
  83. }
  84. }
  85. }
  86. //! Starts event source adapter - assigns internal adapted event handler and starts managed event source. Adapted frequency can be
  87. //! set using ROS2::EventSourceAdapter::SetFrequency method.
  88. void Start()
  89. {
  90. m_sourceAdaptingEventHandler = typename EventSourceT::SourceEventHandlerType(
  91. [this](auto&&... args)
  92. {
  93. const float sourceDeltaTime = m_eventSource.GetDeltaTime(AZStd::forward<decltype(args)>(args)...);
  94. m_adaptedDeltaTime += sourceDeltaTime;
  95. if (!IsPublicationDeadline(sourceDeltaTime))
  96. {
  97. return;
  98. }
  99. m_lastDeltaTime = m_adaptedDeltaTime;
  100. m_sensorAdaptedEvent.Signal(m_adaptedDeltaTime, AZStd::forward<decltype(args)>(args)...);
  101. m_adaptedDeltaTime = 0.0f;
  102. });
  103. m_eventSource.ConnectToSourceEvent(m_sourceAdaptingEventHandler);
  104. m_eventSource.Start();
  105. }
  106. //! Stops event source adapter - stops event source and disconnects internal adapted event handler from source event. If it will be
  107. //! necessary, this implementation allows multiple consecutive calls of Start / Stop, however user must also investigate specific
  108. //! event source implementation with such case in mind.
  109. void Stop()
  110. {
  111. m_eventSource.Stop();
  112. m_sourceAdaptingEventHandler.Disconnect();
  113. }
  114. //! Sets adapter working frequency. By design, adapter will not work correctly, if this frequency will be greater than used event
  115. //! source frequency - e.g. adapter will be requested to work in 60Hz, when using event source working in 30Hz. In general, adapted
  116. //! frequency should be equal or lower than event source frequency - this is forced internally
  117. //! (ROS2::EventSourceAdapter::IsPublicationDeadline). Optimal (highest precision in timing events) working conditions take place
  118. //! when event source frequency is an integer multiple of adapted frequency.
  119. //! @param adaptedFrequency Adapter working frequency. When set to zero or less adapter will be assumed to work in 1Hz.
  120. void SetFrequency(float adaptedFrequency)
  121. {
  122. m_adaptedFrequency = adaptedFrequency;
  123. }
  124. //! Gets adapter working frequency, based on the last obtained delta time between adapted events.
  125. //! If the adapter has not been started yet, the frequency will be zero.
  126. [[nodiscard]] float GetEffectiveFrequency() const
  127. {
  128. if (m_lastDeltaTime == 0.0f)
  129. {
  130. return 0.0f;
  131. }
  132. return 1.0f / m_lastDeltaTime;
  133. }
  134. //! Connects given event handler to source event (ROS2::SensorEventSource). That event is signalled regardless of adapted frequency
  135. //! set for event source adapter (ROS2::EventSourceAdapter::SetFrequency). Its frequency depends only on specific event source
  136. //! implementation. If different working frequency is required (main purpose of ROS2::EventSourceAdapter), user should see
  137. //! ROS2::EventSourceAdapter::ConnectToAdaptedEvent method.
  138. //! @param sourceEventHandler Event handler for source event (frequency not managed by event source adapter).
  139. //! @see ROS2::SensorEventSource
  140. void ConnectToSourceEvent(typename EventSourceT::SourceEventHandlerType& sourceEventHandler)
  141. {
  142. m_eventSource.ConnectToSourceEvent(sourceEventHandler);
  143. }
  144. //! Connects given event handler to adapted event (ROS2::EventSourceAdapter). This event is signalled with a frequency set with
  145. //! ROS2::EventSourceAdapter::SetFrequency method.
  146. //! @param adaptedEventHandler Event handler for adapted event.
  147. void ConnectToAdaptedEvent(typename EventSourceT::AdaptedEventHandlerType& adaptedEventHandler)
  148. {
  149. adaptedEventHandler.Connect(m_sensorAdaptedEvent);
  150. }
  151. private:
  152. //! Uses:
  153. //! - internal tick counter,
  154. //! - last delta time of event source and
  155. //! - frequency set for adapter
  156. //! to support managing calls from event source. In other words, uses delta time of event source to calculate average number of
  157. //! source event calls per adapted event call.
  158. //! @param sourceDeltaTime Delta time of event source.
  159. //! @return Whether it is time to signal adapted event.
  160. [[nodiscard]] bool IsPublicationDeadline(float sourceDeltaTime)
  161. {
  162. if (--m_tickCounter > 0)
  163. {
  164. return false;
  165. }
  166. const float sourceFrequencyEstimation = 1.0f / sourceDeltaTime;
  167. const float numberOfFrames =
  168. m_adaptedFrequency <= sourceFrequencyEstimation ? (sourceFrequencyEstimation / m_adaptedFrequency) : 1.0f;
  169. m_tickCounter = aznumeric_cast<int>(AZStd::round(numberOfFrames));
  170. return true;
  171. }
  172. EventSourceT m_eventSource{}; ///< Event source managed by this adapter.
  173. //! Event handler for adapting event source to specific frequency.
  174. typename EventSourceT::SourceEventHandlerType m_sourceAdaptingEventHandler{};
  175. //! Adapted event that is called with specific frequency.
  176. typename EventSourceT::AdaptedEventType m_sensorAdaptedEvent{};
  177. float m_adaptedFrequency{ 30.0f }; ///< Adapted frequency value.
  178. float m_lastDeltaTime{ 0.0f }; ///< Last difference in time between adapted events, used to compute effective frequency value.
  179. float m_adaptedDeltaTime{ 0.0f }; ///< Accumulator for calculating adapted delta time.
  180. int m_tickCounter{ 0 }; ///< Internal counter for controlling adapter frequency.
  181. };
  182. AZ_TYPE_INFO_TEMPLATE(EventSourceAdapter, EventSourceAdapterTypeId, AZ_TYPE_INFO_CLASS)
  183. } // namespace ROS2