diff --git a/docs/time/_assets/app/app_class.puml b/docs/TimeDaemon/_assets/app/app_class.puml
similarity index 100%
rename from docs/time/_assets/app/app_class.puml
rename to docs/TimeDaemon/_assets/app/app_class.puml
diff --git a/docs/time/_assets/app/app_init_seq.puml b/docs/TimeDaemon/_assets/app/app_init_seq.puml
similarity index 100%
rename from docs/time/_assets/app/app_init_seq.puml
rename to docs/TimeDaemon/_assets/app/app_init_seq.puml
diff --git a/docs/time/_assets/app/app_workflow_seq.puml b/docs/TimeDaemon/_assets/app/app_workflow_seq.puml
similarity index 100%
rename from docs/time/_assets/app/app_workflow_seq.puml
rename to docs/TimeDaemon/_assets/app/app_workflow_seq.puml
diff --git a/docs/time/_assets/ctrlflow/ctrlflow_class.puml b/docs/TimeDaemon/_assets/ctrlflow/ctrlflow_class.puml
similarity index 100%
rename from docs/time/_assets/ctrlflow/ctrlflow_class.puml
rename to docs/TimeDaemon/_assets/ctrlflow/ctrlflow_class.puml
diff --git a/docs/time/_assets/ctrlflow/ctrlflow_init_seq.puml b/docs/TimeDaemon/_assets/ctrlflow/ctrlflow_init_seq.puml
similarity index 100%
rename from docs/time/_assets/ctrlflow/ctrlflow_init_seq.puml
rename to docs/TimeDaemon/_assets/ctrlflow/ctrlflow_init_seq.puml
diff --git a/docs/time/_assets/ctrlflow/ctrlflow_workflow_seq.puml b/docs/TimeDaemon/_assets/ctrlflow/ctrlflow_workflow_seq.puml
similarity index 100%
rename from docs/time/_assets/ctrlflow/ctrlflow_workflow_seq.puml
rename to docs/TimeDaemon/_assets/ctrlflow/ctrlflow_workflow_seq.puml
diff --git a/docs/time/_assets/dd_class.puml b/docs/TimeDaemon/_assets/dd_class.puml
similarity index 100%
rename from docs/time/_assets/dd_class.puml
rename to docs/TimeDaemon/_assets/dd_class.puml
diff --git a/docs/time/_assets/dd_data_control_flow.puml b/docs/TimeDaemon/_assets/dd_data_control_flow.puml
similarity index 100%
rename from docs/time/_assets/dd_data_control_flow.puml
rename to docs/TimeDaemon/_assets/dd_data_control_flow.puml
diff --git a/docs/time/_assets/dd_deployment.puml b/docs/TimeDaemon/_assets/dd_deployment.puml
similarity index 100%
rename from docs/time/_assets/dd_deployment.puml
rename to docs/TimeDaemon/_assets/dd_deployment.puml
diff --git a/docs/time/_assets/examples/abs_time/abs_time_data_control_flow.puml b/docs/TimeDaemon/_assets/examples/abs_time/abs_time_data_control_flow.puml
similarity index 100%
rename from docs/time/_assets/examples/abs_time/abs_time_data_control_flow.puml
rename to docs/TimeDaemon/_assets/examples/abs_time/abs_time_data_control_flow.puml
diff --git a/docs/time/_assets/examples/abs_time/abs_time_deployment.puml b/docs/TimeDaemon/_assets/examples/abs_time/abs_time_deployment.puml
similarity index 100%
rename from docs/time/_assets/examples/abs_time/abs_time_deployment.puml
rename to docs/TimeDaemon/_assets/examples/abs_time/abs_time_deployment.puml
diff --git a/docs/time/_assets/examples/qvt/qvt_data_control_flow.puml b/docs/TimeDaemon/_assets/examples/qvt/qvt_data_control_flow.puml
similarity index 100%
rename from docs/time/_assets/examples/qvt/qvt_data_control_flow.puml
rename to docs/TimeDaemon/_assets/examples/qvt/qvt_data_control_flow.puml
diff --git a/docs/time/_assets/examples/qvt/qvt_deployment.puml b/docs/TimeDaemon/_assets/examples/qvt/qvt_deployment.puml
similarity index 100%
rename from docs/time/_assets/examples/qvt/qvt_deployment.puml
rename to docs/TimeDaemon/_assets/examples/qvt/qvt_deployment.puml
diff --git a/docs/time/_assets/ipc/ipc_class.puml b/docs/TimeDaemon/_assets/ipc/ipc_class.puml
similarity index 100%
rename from docs/time/_assets/ipc/ipc_class.puml
rename to docs/TimeDaemon/_assets/ipc/ipc_class.puml
diff --git a/docs/time/_assets/ipc/ipc_init_seq.puml b/docs/TimeDaemon/_assets/ipc/ipc_init_seq.puml
similarity index 100%
rename from docs/time/_assets/ipc/ipc_init_seq.puml
rename to docs/TimeDaemon/_assets/ipc/ipc_init_seq.puml
diff --git a/docs/time/_assets/ipc/ipc_publish_seq.puml b/docs/TimeDaemon/_assets/ipc/ipc_publish_seq.puml
similarity index 100%
rename from docs/time/_assets/ipc/ipc_publish_seq.puml
rename to docs/TimeDaemon/_assets/ipc/ipc_publish_seq.puml
diff --git a/docs/time/_assets/ipc/ipc_receive_seq.puml b/docs/TimeDaemon/_assets/ipc/ipc_receive_seq.puml
similarity index 100%
rename from docs/time/_assets/ipc/ipc_receive_seq.puml
rename to docs/TimeDaemon/_assets/ipc/ipc_receive_seq.puml
diff --git a/docs/time/_assets/msg_broker/msg_broker_class.puml b/docs/TimeDaemon/_assets/msg_broker/msg_broker_class.puml
similarity index 100%
rename from docs/time/_assets/msg_broker/msg_broker_class.puml
rename to docs/TimeDaemon/_assets/msg_broker/msg_broker_class.puml
diff --git a/docs/time/_assets/msg_broker/msg_broker_init_seq.puml b/docs/TimeDaemon/_assets/msg_broker/msg_broker_init_seq.puml
similarity index 100%
rename from docs/time/_assets/msg_broker/msg_broker_init_seq.puml
rename to docs/TimeDaemon/_assets/msg_broker/msg_broker_init_seq.puml
diff --git a/docs/time/_assets/msg_broker/msg_broker_workflow_seq.puml b/docs/TimeDaemon/_assets/msg_broker/msg_broker_workflow_seq.puml
similarity index 100%
rename from docs/time/_assets/msg_broker/msg_broker_workflow_seq.puml
rename to docs/TimeDaemon/_assets/msg_broker/msg_broker_workflow_seq.puml
diff --git a/docs/time/_assets/mw/mw_class.puml b/docs/TimeDaemon/_assets/mw/mw_class.puml
similarity index 100%
rename from docs/time/_assets/mw/mw_class.puml
rename to docs/TimeDaemon/_assets/mw/mw_class.puml
diff --git a/docs/time/_assets/mw/mw_time_receive_seq.puml b/docs/TimeDaemon/_assets/mw/mw_time_receive_seq.puml
similarity index 100%
rename from docs/time/_assets/mw/mw_time_receive_seq.puml
rename to docs/TimeDaemon/_assets/mw/mw_time_receive_seq.puml
diff --git a/docs/time/_assets/mw/mw_time_receive_simple_seq.puml b/docs/TimeDaemon/_assets/mw/mw_time_receive_simple_seq.puml
similarity index 100%
rename from docs/time/_assets/mw/mw_time_receive_simple_seq.puml
rename to docs/TimeDaemon/_assets/mw/mw_time_receive_simple_seq.puml
diff --git a/docs/time/_assets/ptp_machine/ptp_machine_class.puml b/docs/TimeDaemon/_assets/ptp_machine/ptp_machine_class.puml
similarity index 100%
rename from docs/time/_assets/ptp_machine/ptp_machine_class.puml
rename to docs/TimeDaemon/_assets/ptp_machine/ptp_machine_class.puml
diff --git a/docs/time/_assets/ptp_machine/ptp_machine_get_new_data_seq.puml b/docs/TimeDaemon/_assets/ptp_machine/ptp_machine_get_new_data_seq.puml
similarity index 100%
rename from docs/time/_assets/ptp_machine/ptp_machine_get_new_data_seq.puml
rename to docs/TimeDaemon/_assets/ptp_machine/ptp_machine_get_new_data_seq.puml
diff --git a/docs/time/_assets/ptp_machine/ptp_machine_init_seq.puml b/docs/TimeDaemon/_assets/ptp_machine/ptp_machine_init_seq.puml
similarity index 100%
rename from docs/time/_assets/ptp_machine/ptp_machine_init_seq.puml
rename to docs/TimeDaemon/_assets/ptp_machine/ptp_machine_init_seq.puml
diff --git a/docs/time/_assets/sad_deployment.puml b/docs/TimeDaemon/_assets/sad_deployment.puml
similarity index 100%
rename from docs/time/_assets/sad_deployment.puml
rename to docs/TimeDaemon/_assets/sad_deployment.puml
diff --git a/docs/TimeDaemon/_assets/sad_sdat_components.drawio b/docs/TimeDaemon/_assets/sad_sdat_components.drawio
new file mode 100644
index 0000000..be54d13
--- /dev/null
+++ b/docs/TimeDaemon/_assets/sad_sdat_components.drawio
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/TimeDaemon/_assets/sad_vt_components.drawio b/docs/TimeDaemon/_assets/sad_vt_components.drawio
new file mode 100644
index 0000000..b90cdd9
--- /dev/null
+++ b/docs/TimeDaemon/_assets/sad_vt_components.drawio
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/time/_assets/ver_machine/ver_class.puml b/docs/TimeDaemon/_assets/ver_machine/ver_class.puml
similarity index 100%
rename from docs/time/_assets/ver_machine/ver_class.puml
rename to docs/TimeDaemon/_assets/ver_machine/ver_class.puml
diff --git a/docs/time/_assets/ver_machine/ver_init_seq.puml b/docs/TimeDaemon/_assets/ver_machine/ver_init_seq.puml
similarity index 100%
rename from docs/time/_assets/ver_machine/ver_init_seq.puml
rename to docs/TimeDaemon/_assets/ver_machine/ver_init_seq.puml
diff --git a/docs/time/_assets/ver_machine/ver_verification_seq.puml b/docs/TimeDaemon/_assets/ver_machine/ver_verification_seq.puml
similarity index 100%
rename from docs/time/_assets/ver_machine/ver_verification_seq.puml
rename to docs/TimeDaemon/_assets/ver_machine/ver_verification_seq.puml
diff --git a/docs/TimeDaemon/index.rst b/docs/TimeDaemon/index.rst
new file mode 100644
index 0000000..aa2d387
--- /dev/null
+++ b/docs/TimeDaemon/index.rst
@@ -0,0 +1,983 @@
+Concept for TimeDaemon
+======================
+
+.. contents:: Table of Contents
+ :depth: 3
+ :local:
+
+TimeDaemon concept
+------------------
+
+Use Cases
+~~~~~~~~~
+
+TimeDaemon is the non Autosar adaptive process who is intended to get the Vehicle Time from the ptp slave daemon (ptpd or any other), verify and validate the timepoints and distribute time information across the clients.
+
+More precisely we can specify the following use cases for the time daemon:
+
+1. Providing current Vehicle time to different applications
+2. Setting the synchronization qualifier (aka Synchronized, Timeout, so on)
+3. Providing needed information for diagnostics
+4. Providing needed information for addition verification, ex SafeCarTime
+
+The raw architectural diagram is represented below.
+
+.. raw:: html
+
+
+
+.. uml:: _assets/sad_deployment.puml
+ :alt: Raw architectural diagram
+
+.. raw:: html
+
+
+
+Components decomposition
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The design consists of several sw components:
+
+1. `Application <#application-sw-component>`_
+2. `Message Broker <#message-broker-sw-component>`_
+3. `ControlFlowDivider <#controlflowdivider-sw-component>`_
+4. `PTP Machine <#ptp-machine-sw-component>`_
+5. `Verification Machine <#verification-machine-sw-component>`_
+6. `IPC Machine <#ipc-machine-sw-component>`_
+7. `score::time::svt <#score-time-synchronizedvehicletime-sw-component>`_
+
+Deployment view
+~~~~~~~~~~~~~~~
+
+The design deployment is represented on the following diagram:
+
+.. raw:: html
+
+
+
+.. uml:: _assets/dd_deployment.puml
+ :alt: Deployment View
+
+.. raw:: html
+
+
+
+Class view
+~~~~~~~~~~
+
+Main classes and components are presented on this diagram:
+
+.. raw:: html
+
+
+
+.. uml:: _assets/dd_class.puml
+ :alt: Class View
+ :width: 100%
+ :align: center
+
+.. raw:: html
+
+
+
+Data and control flow
+~~~~~~~~~~~~~~~~~~~~~
+
+The Data and Control flow are presented in the following diagram:
+
+.. raw:: html
+
+
+
+.. uml:: _assets/dd_data_control_flow.puml
+ :alt: Data and Control flow View
+
+.. raw:: html
+
+
+
+On this view you could see several "workers" scopes:
+
+1. PTP retrieving scope
+2. PTPTimeInfo handling scope
+3. PTPTimeInfo receiving on Application side scope
+
+Each control flow is implemented with the dedicated thread or process and is independent form another ones.
+
+Control flows
+^^^^^^^^^^^^^
+
+PTP retrieving scope
+''''''''''''''''''''
+
+This control flow is responsible for the:
+
+1. retrieve the latest information from the ptp stack and
+2. provide it to the ``PTPTimeInfo handling`` control flow
+
+PTPTimeInfo handling scope
+'''''''''''''''''''''''''''
+
+This control flow is responsible for the:
+
+1. Validate the time information, provided by the ``PTP retrieving`` workflow and
+2. publish it to the ``Applications`` via some IPC
+
+PTPTimeInfo receiving on Application side scope
+''''''''''''''''''''''''''''''''''''''''''''''''
+
+This control flow is responsible for the:
+
+1. Propagate the time information from the ``PTPTimeInfo handling`` to the business logic of the applications.
+
+Data types or events
+^^^^^^^^^^^^^^^^^^^^
+
+There are also several data types, which components are communicating to each other:
+
+Raw ptp data
+''''''''''''
+
+``raw_ptp_data`` is the data, which is provided by ``PTPMachine`` component and is just the raw data from ptp stack. is handled in the "PTP retrieving scope"
+
+Input ptp data
+''''''''''''''
+
+``input_ptp_data`` is the same data as `raw_ptp_data <#raw-ptp-data>`_ but which is handled already in "PTPTimeInfo handling scope"
+
+Verified ptp data
+'''''''''''''''''
+
+``verified_ptp_data`` is the `input_ptp_data <#input-ptp-data>`_ which was verified according to the business logic and updated accordingly. This data should be published to the Applications.
+
+SW Components decomposition
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Application SW component
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``Application`` component is the main entry point for the ``TimeDaemon``. It is responsible for orchestrating the overall lifecycle and initialization of all daemon components.
+
+The ``TimebaseHandler`` component is an timebase-specific logic implementation. There might be several handlers available in the ``Application`` per amount of timebases supported. This separation allows for different timebase implementations while maintaining a consistent application structure.
+
+Component requirements
+''''''''''''''''''''''
+
+The ``Application`` has the following requirements:
+
+- The ``Application`` shall implement the ``Initialize()`` method to create and initialize all daemon components
+- The ``Application`` shall implement the ``Run()`` method to start all components and wait for termination
+- The ``Application`` shall connect components to the ``MessageBroker`` by setting up all required subscriptions during initialization stage
+- The ``Application`` shall support extension for different timebases.
+
+Class view
+''''''''''
+
+The Class Diagram is presented below:
+
+.. raw:: html
+
+
+
+.. uml:: _assets/app/app_class.puml
+ :alt: Class Diagram
+
+.. raw:: html
+
+
+
+Initialization flow
+'''''''''''''''''''
+
+During initialization, the ``Application`` uses the ``MachineFactory`` to create, configure and subscribe all components in a specific order:
+
+- Create the ``MessageBroker`` first, as other components depend on it
+- Create ProactiveMachines (``PtpMachine``, ``ControlFlowDivider``) that drive system behavior
+
+ - Initialize each component
+ - Set up MessageBroker subscriptions to component notifications
+ - Set up component subscriptions to MessageBroker topics
+
+- Create ReactiveMachines (``VerificationMachine``, ``IPCMachine``) that respond to events
+
+ - Initialize each component
+ - Set up MessageBroker subscriptions to component notifications
+ - Set up component subscriptions to MessageBroker topics
+
+The initialization workflow is represented in the following sequence diagram:
+
+.. raw:: html
+
+
+
+.. uml:: _assets/app/app_init_seq.puml
+ :alt: Initialization workflow
+
+.. raw:: html
+
+
+
+Execution and shutdown flow
+''''''''''''''''''''''''''''
+
+During execution, the ``Application``:
+
+- Starts all ``ProactiveMachines`` in the correct order
+- Monitors the stop token for termination requests
+- When termination is requested, stops all ``ProactiveMachines`` in reverse order
+
+The execution and shutdown workflow is represented in the following sequence diagram:
+
+.. raw:: html
+
+
+
+.. uml:: _assets/app/app_workflow_seq.puml
+ :alt: Execution workflow
+
+.. raw:: html
+
+
+
+Message Broker SW component
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``Message Broker`` component is the central communication hub that implements the Publish-Subscribe pattern within the ``TimeDaemon``. It enables decoupled communication between components by managing topics and distributing messages to interested subscribers.
+
+The component maintains a registry of topics and their subscribers, delivering messages to all registered subscribers when a component publishes to a topic. This decoupling allows components to evolve independently without direct dependencies on each other.
+
+Component requirements
+''''''''''''''''''''''
+
+The ``Message Broker`` has the following requirements:
+
+- The ``Message Broker`` shall maintain a registry of topics and their subscribers
+- The ``Message Broker`` shall allow components to subscribe to topics of interest
+- The ``Message Broker`` shall distribute messages to all subscribers when a topic is published to
+
+Class view
+''''''''''
+
+The Class Diagram is presented below:
+
+.. raw:: html
+
+
+
+.. uml:: _assets/msg_broker/msg_broker_class.puml
+ :alt: Class Diagram
+
+.. raw:: html
+
+
+
+Initialization flow
+'''''''''''''''''''
+
+During initialization, all machine objects, see ``BaseMachine``, the ``Application`` component needs to subscribe machines to ``Message Broker`` to the topics of interest.
+
+The initialization workflow is represented in the following sequence diagram:
+
+.. raw:: html
+
+
+
+.. uml:: _assets/msg_broker/msg_broker_init_seq.puml
+ :alt: Initialization workflow
+
+.. raw:: html
+
+
+
+Message flow
+''''''''''''
+
+The message flow through the ``Message Broker`` is represented in the following sequence diagram:
+
+.. raw:: html
+
+
+
+.. uml:: _assets/msg_broker/msg_broker_workflow_seq.puml
+ :alt: Message DiagramFlow
+
+.. raw:: html
+
+
+
+Concurrency aspects
+'''''''''''''''''''
+
+The ``Message Broker`` doesn't provide any synchronization between the publish-callback invoking processes.
+Moreover, the callback invoke will happened in the scope of the thread, where the ``publish`` method is called.
+To separate the control flows, the `ControlFlowDivider <#controlflowdivider-sw-component>`_ shall be used
+
+Scalability
+'''''''''''
+
+The ``Message Broker`` can be extended to support configuration-driven subscriptions, where topic relationships are defined in configuration files rather than hardcoded.
+
+ControlFlowDivider SW component
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``ControlFlowDivider`` component is responsible for separating control (execution) flows within the ``TimeDaemon`` and providing the execution control flow for the data processing. It contains dedicated threads where data is published to the ``Message Broker``, ensuring that blocking operations in one component do not affect the execution of other components and data missing is not affecting the data analysis in processing pipeline.
+
+This component acts as a crucial intermediary that maintains the responsiveness of the system by decoupling the execution contexts of different operations, particularly between the PTP data retrieval and the time data processing pipelines.
+
+Component requirements
+''''''''''''''''''''''
+
+The ``ControlFlowDivider`` has the following requirements:
+
+- The ``ControlFlowDivider`` shall provide separate execution threads for different control flows
+- The ``ControlFlowDivider`` shall isolate components from execution time variations in other components
+- The ``ControlFlowDivider`` shall maintain consistent data publishing rates to the subscribers
+- - The ``ControlFlowDivider`` shall push the last received data to the subscribers if there is no new data for some time with the predefined rate, to avoid data missing in the processing pipeline
+- The ``ControlFlowDivider`` shall enable periodic processing of the pipeline through consistent event generation
+- The ``ControlFlowDivider`` shall buffer incoming data from fast producers
+
+Class view
+''''''''''
+
+The Class Diagram is presented below:
+
+.. raw:: html
+
+
+
+.. uml:: _assets/ctrlflow/ctrlflow_class.puml
+ :alt: Class Diagram
+
+.. raw:: html
+
+
+
+Initialization flow
+'''''''''''''''''''
+
+During initialization, the ``ControlFlowDivider`` performs the following steps:
+
+- Initialize internal data structures (queue, mutex, condition variable)
+- Create a worker thread to process data independently
+- Start the worker thread which enters a waiting state
+
+The initialization workflow is represented in the following sequence diagram:
+
+.. raw:: html
+
+
+
+.. uml:: _assets/ctrlflow/ctrlflow_init_seq.puml
+ :alt: Initialization workflow
+
+.. raw:: html
+
+
+
+Message flow
+''''''''''''
+
+When the ``ControlFlowDivider`` receives new data from the ``PTP Machine`` via the ``Message Broker``, it processes it through the following workflow:
+
+1. The ``Message Broker`` executes the onNewData callback and provides the new data
+2. The data is placed in a thread-safe queue and exists from the callback
+3. The worker thread wakes up, retrieves the data from the queue and
+4. The worker thread publishes the retrieved data to the `input_ptp_data <#input-ptp-data>`_ topic
+5. if there was no data for some timeout, the worker shall published the empty data to the `input_ptp_data <#input-ptp-data>`_ topic.
+
+This separation of control flows ensures that slow or blocking operations in the PTP stack communication do not affect the responsiveness of time data processing in the ``TimeDaemon``.
+
+The execution workflow is represented in the following sequence diagram:
+
+.. raw:: html
+
+
+
+.. uml:: _assets/ctrlflow/ctrlflow_workflow_seq.puml
+ :alt: Execution workflow
+
+.. raw:: html
+
+
+
+PTP Machine SW component
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``PTP Machine`` component shall retrieve all needed information from the ptp stack (ex ``ptpd``) and provide it to the ``Message Broker`` for routing.
+All communication with the ptp stack ight use ``devctl`` calls, which take some time, thus these calls shall be done in the dedicated thread.
+
+Component requirements
+''''''''''''''''''''''
+
+The ``PTP Machine`` has the following requirements:
+
+- The ``PTP Machine`` shall retrieve the latest time information from the PTP stack (e.g., ``ptpd``)
+- The ``PTP Machine`` shall publish retrieved time information to the ``Message Broker`` using the defined topic
+- The ``PTP Machine`` shall format data according to the ``PTPTimeInfo`` structure required by downstream components
+- The ``PTP Machine`` shall retrieve time information at a consistent rate to maintain time synchronization
+- The ``PTP Machine`` shall maintain consistent publishing rates for time data even when experiencing delays in PTP stack communication.
+- The ``PTP Machine`` shall support exchangeability with different PTP stack implementations
+
+Class view
+''''''''''
+
+The Class Diagram is presented below.
+
+.. raw:: html
+
+
+
+.. uml:: _assets/ptp_machine/ptp_machine_class.puml
+ :alt: Class Diagram
+
+.. raw:: html
+
+
+
+As long as it wraps the particular communication with the ptp stack, the implementations should be easily exchangeable with another one in case of stack change.
+
+Component initialization
+'''''''''''''''''''''''''
+
+During initialization the ``PTP Machine`` shall initialize the ptp stack to be able to communicate with it.
+
+The initialization workflow is described below.
+
+.. raw:: html
+
+
+
+.. uml:: _assets/ptp_machine/ptp_machine_init_seq.puml
+ :alt: Initialization workflow
+
+.. raw:: html
+
+
+
+Publish new data
+''''''''''''''''
+
+After ``PTP Machine`` collects new data from the ptp stack, the component shall publish it to the ``Message Broker`` as `raw-ptp-data <#raw-ptp-data>`_.
+
+The publish workflow is described below.
+
+.. raw:: html
+
+
+
+.. uml:: _assets/ptp_machine/ptp_machine_get_new_data_seq.puml
+ :alt: Publish workflow
+
+.. raw:: html
+
+
+
+Verification Machine SW component
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``Verification Machine`` component is responsible for validating and qualifying the time information received from the ``PTP Machine``. It applies various validation rules to ensure the time data meets quality requirements before distribution to applications.
+
+The component implements a pipeline pattern where each stage performs a specific validation and adds appropriate qualifiers to the time data. This modular design allows for easy extension with additional validation steps.
+
+Component requirements
+''''''''''''''''''''''
+
+The ``Verification Machine`` has the following requirements:
+
+- The ``Verification Machine`` shall validate and qualify time information received from the PTP Machine
+- The ``Verification Machine`` shall validate if the time base is synchronized state
+- The ``Verification Machine`` shall validate if the time base is in timeout state
+- The ``Verification Machine`` shall validate timestamp for time jumps based on local clock
+- The ``Verification Machine`` shall subscribe to the `input_ptp_data <#input-ptp-data>`_ topic via the ``Message Broker``
+- The ``Verification Machine`` shall publish verified time data to the ``Message Broker`` using the `verified-ptp-data <#verified-ptp-data>`_ topic
+- The ``Verification Machine`` shall support extensibility to add new validation stages in the pipeline
+
+Class view
+''''''''''
+
+The Class Diagram is presented below.
+
+.. raw:: html
+
+
+
+.. uml:: _assets/ver_machine/ver_class.puml
+ :alt: Class Diagram
+
+.. raw:: html
+
+
+
+Component initialization
+'''''''''''''''''''''''''
+
+During initialization, the ``Verification Machine`` performs the following steps:
+
+1. Set up the validation pipeline by creating and connecting validation stages
+
+The component shall be subscribed by the ``Application`` to the `input_ptp_data <#input-ptp-data>`_ topic of the ``MessageBroker``
+
+The initialization workflow is represented in the following sequence diagram:
+
+.. raw:: html
+
+
+
+.. uml:: _assets/ver_machine/ver_init_seq.puml
+ :alt: Initialization workflow
+
+.. raw:: html
+
+
+
+Data verification workflow
+'''''''''''''''''''''''''''
+
+When the ``Verification Machine`` receives new PTP data, it processes it through the validation pipeline:
+
+.. raw:: html
+
+
+
+.. uml:: _assets/ver_machine/ver_verification_seq.puml
+ :alt: Validation pipeline
+
+.. raw:: html
+
+
+
+IPC Machine SW component
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``IPC Machine`` component shall get the `verified-ptp-data <#verified-ptp-data>`_ from the ``Verification Machine`` and provide it to the ``score::time::svt`` through the ``score::communication`` module. As the fast initial implementation, a custom shared memory backend is used.
+
+The component provides two sub components: publisher and receiver to be deployed on the TimeDaemon and Application sides accordingly.
+
+Component requirements
+''''''''''''''''''''''
+
+The ``IPC Machine`` has the following requirements:
+
+- The ``IPC Machine`` shall provide verified time data to the ``score::time::svt`` component through the ``score::communication`` module
+- The ``IPC Machine`` shall create and initialize the IPC
+- The ``IPC Machine`` shall support multiple client applications accessing the same time data
+- The ``IPC Machine`` shall subscribe to the `verified_ptp_data <#verified-ptp-data>`_ topic via the ``Message Broker``
+
+Class view
+''''''''''
+
+The Class Diagram is presented below.
+
+.. raw:: html
+
+
+
+.. uml:: _assets/ipc/ipc_class.puml
+ :alt: Class Diagram
+
+.. raw:: html
+
+
+
+Component initialization
+'''''''''''''''''''''''''
+
+Initialization is divided to two parts:
+
+1. Initialization on the TimeDaemon side
+2. Initialization on the Application side
+
+Important thing, the ``score::communication`` publisher shall be created and offered by the ``TimeDaemon`` before the Application side subscriber can connect. The Application shall retry until the service is found.
+
+The main workflow is described below.
+
+.. raw:: html
+
+
+
+.. uml:: _assets/ipc/ipc_init_seq.puml
+ :alt: Main workflow
+
+.. raw:: html
+
+
+
+The component shall be subscribed during initialization by the ``Application`` on the `verified-ptp-data <#verified-ptp-data>`_ updates from the ``Message Broker``
+
+Publish new data
+''''''''''''''''
+
+When ``IPC Machine`` receives the new `verified-ptp-data <#verified-ptp-data>`_ from Message Broker, it shall serialize data and publish it via ``score::communication``.
+
+As long as there are different use cases by using it, like:
+
+1. Get current Vehicle time
+2. Get data for diagnostics
+
+All ``PTPTimeInfo`` data (or almost all) shall be published to the subscribed applications.
+
+The publish workflow is described below.
+
+.. raw:: html
+
+
+
+.. uml:: _assets/ipc/ipc_publish_seq.puml
+ :alt: Publish workflow
+
+.. raw:: html
+
+
+
+Receive data
+''''''''''''
+
+From Application side the receiver shall subscribe via ``score::communication`` and provide the data to the caller.
+
+The receive workflow is described below.
+
+.. raw:: html
+
+
+
+.. uml:: _assets/ipc/ipc_receive_seq.puml
+ :alt: Receive workflow
+
+.. raw:: html
+
+
+
+score::time::SynchronizedVehicleTime SW Component
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+``score::time::svt`` is the interface towards Applications, how they could get the access to the Vehicle Time.
+
+Component requirements
+''''''''''''''''''''''
+
+The ``score::time::svt`` has the following requirements:
+
+- The ``score::time::svt`` shall expose vehicle time amd it synchronization status to applications
+- The ``score::time::svt`` shall retrieve time data from ``IPC Machine`` receiver component
+- The ``score::time::svt`` shall adjust vehicle time with local clock to provide accurate timestamps
+- The ``score::time::svt`` shall support fast and low-latency time access via the ``Now()`` method
+
+Class view
+''''''''''
+
+The Class Diagram is presented below.
+
+.. raw:: html
+
+
+
+.. uml:: _assets/mw/mw_class.puml
+ :alt: Class Diagram
+
+.. raw:: html
+
+
+
+Receive data
+''''''''''''
+
+In case of receiving data, the ``Application`` shall just call ``score::time::svt::Now()`` and it shall return the latest published Vehicle Time, which is already adjusted with local clock.
+
+To do so, in the ``score::time::svt`` there is a thread, who polls for new data the ``IPCMachine::receiver`` and put the data to the process-internal shared buffer (memory), from where it is being read on ``score::time::svt::Now()`` call.
+
+The main workflow is described below.
+
+.. raw:: html
+
+
+
+.. uml:: _assets/mw/mw_time_receive_seq.puml
+ :alt: Receive data workflow
+
+.. raw:: html
+
+
+
+This design guarantees very low latency of the executing the ``score::time::svt::Now()`` function but brings additional efforts for the thread, memory buffer, synchronizing and so on.
+
+Receive data (simplified)
+''''''''''''''''''''''''''
+
+As an alternative design, the receiving concept could be simplified and ``score::time::svt::Now()`` could directly invoke the ``IPCMachine::receiver`` call, adjust the ``Vehicle time`` and return it to the ``Application``.
+
+The design is represented below.
+
+.. raw:: html
+
+
+
+.. uml:: _assets/mw/mw_time_receive_simple_seq.puml
+ :alt: Receive data (simplified) workflow
+
+.. raw:: html
+
+
+
+In this case, there will be no need for additional thread, shared buffer and synchronization, but the ``score::time::svt::Now()`` call will take longer. To decide which approach to use, additional tests shall be
+
+Deployment
+''''''''''
+
+The implementation of ``score::time::svt::details::timed`` could be placed in parallel to other implementations, like ``score::time::svt::details::mocked`` one and could be selected by Bazel select. Also it will ease the integration process.
+
+Logging configuration
+~~~~~~~~~~~~~~~~~~~~~
+
+The daemon should have the following logging contexts:
+
+.. list-table:: Logging Contexts
+ :header-rows: 1
+ :widths: 30 20 50
+
+ * - component
+ - App/Context ID
+ - Comments
+ * - TimeDaemon
+ - TDON
+ - **T**\ ime\ **D**\ aem\ **ON**
+ * - Application
+ - TDAP
+ - **T**\ ime\ **D**\ aemon **AP**\ plication
+ * - MessageBroker
+ - TDMB
+ - **T**\ ime\ **D**\ aemon **M**\ essage\ **B**\ roker
+ * - ControlFlowDivider
+ - TDCD
+ - **T**\ ime\ **D**\ aemon **C**\ ontrolFlow\ **D**\ ivider
+ * - PTPMachine
+ - TDPM
+ - **T**\ ime\ **D**\ aemon **P**\ TP\ **M**\ achine
+ * - VerificationMachine
+ - TDVM
+ - **T**\ ime\ **D**\ aemon **V**\ erification\ **M**\ achine
+ * - IPCMachine::receiver
+ - TDIR
+ - **T**\ ime\ **D**\ aemon **I**\ PCMachine::\ **R**\ eceiver
+ * - IPCMachine::publisher
+ - TDIP
+ - **T**\ ime\ **D**\ aemon **I**\ PCMachine::\ **P**\ ublisher
+
+Variability
+~~~~~~~~~~~
+
+Configuration files
+^^^^^^^^^^^^^^^^^^^
+
+The ``TimeDaemon`` uses structured configuration files to enable customization of its runtime behavior. These data could be configured:
+
+1. Component-specific Configuration:
+
+ a. Each component can have dedicated configuration sections
+ b. Parameters such as update rates, timeouts, and thresholds can be specified
+
+2. Topic Configuration:
+
+ a. Topics for the ``Message Broker`` can be defined in configuration
+ b. Publisher and subscriber relationships can be specified externally
+ c. Component roles (publisher/subscriber) can be assigned through configuration
+
+3. File Format and Structure: The configuration files use JSON format for readability and easy parsing:
+
+.. code-block:: json
+
+ {
+ "message_broker": {
+ "topics": [
+ {
+ "name": "raw_ptp_data",
+ "publishers": ["PtpMachine"],
+ "subscribers": ["ControlFlowDivider"]
+ },
+ {
+ "name": "input_ptp_data",
+ "publishers": ["ControlFlowDivider"],
+ "subscribers": ["VerificationMachine"]
+ },
+ {
+ "name": "verified_ptp_data",
+ "publishers": ["VerificationMachine"],
+ "subscribers": ["IPCMachine"]
+ }
+ ]
+ },
+ "ptp_machine": {
+ "update_interval_ms": 50,
+ "ptp_stack_type": "ptp",
+ "ptp_stack_parameters": {
+ "device": "/dev/ptp0"
+ }
+ },
+ "control_flow_divider": {
+ "timeout_ms": 500,
+ "publishing_rate_ms": 100
+ },
+ "verification_machine": {
+ "validation_stages": ["synchronization", "timejumps", "timeout"],
+ "timejumps_parameters": {
+ "max_backward_jump_ns": 100000
+ },
+ "timeout_parameters": {
+ "threshold_ns": 100000
+ }
+ },
+ "ipc_machine": {
+ "shared_memory_name": "vehicle_time",
+ "shared_memory_size": 4096
+ }
+ }
+
+Scalability
+^^^^^^^^^^^
+
+The ``TimeDaemon``'s architecture supports scalability in the following ways:
+
+Component Extensibility:
+''''''''''''''''''''''''
+
+1. New machine components can be added by implementing the ``BaseMachine`` interface
+2. Additional validation stages can be plugged into the ``VerificationMachine`` pipeline
+3. Alternative IPC mechanisms or communication with ptp stack can be implemented by alternative the ``IPCMachine`` or ``PTPMachine`` implementation
+
+Example based on Qualified Vehicle Time integration
+'''''''''''''''''''''''''''''''''''''''''''''''''''
+
+The ``Qualified Vehicle Time`` integration extends the standard ``TimeDaemon`` architecture with:
+
+1. A ``Qualified Vehicle Time`` component that performs additional time qualification and provide new topics: ``qualified_ptp_data`` and ``diagnostic_sct_data``
+2. A dedicated IPC channel for SCT diagnostic data
+3. A ``score::time::qvt`` library for diagnostic applications
+
+.. raw:: html
+
+
+
+.. uml:: _assets/examples/qvt/qvt_deployment.puml
+ :alt: Deployment view
+
+.. raw:: html
+
+
+
+The ``Qualified Vehicle Time`` component is integrated into the existing processing pipeline:
+
+1. It subscribes to the `verified_ptp_data <#verified-ptp-data>`_ topic from the ``VerificationMachine``
+2. It processes and qualifies the time data with additional QVT-specific checks
+3. It publishes two types of data:
+
+ a. Qualified time data to the standard IPC Machine towards clients interested in the qualified Vehicle Time
+ b. Diagnostic data to a dedicated QVT IPC channel towards Diagnostic and Central Validator notifications
+
+The extended data flow with Qualified Vehicle Time integration is shown below:
+
+.. raw:: html
+
+
+
+.. uml:: _assets/examples/qvt/qvt_data_control_flow.puml
+ :alt: Data flow
+
+.. raw:: html
+
+
+
+Example based on Absolute Time integration
+''''''''''''''''''''''''''''''''''''''''''
+
+Another example of the ``TimeDaemon`` extension is the integration of an ``Absolute Time`` source, such as GNSS, to provide absolute time information alongside the relative Vehicle Time from PTP.
+
+The ``Absolute Time`` integration extends the standard ``TimeDaemon`` architecture with:
+
+1. An ``SDatMachine`` component that retrieves absolute time from GNSS via SOMEIP or other sources and provide new topics: ``absolute_time_data``
+2. A dedicated verification stage in the ``VerificationMachine`` for Absolute Time qualification
+3. A dedicated IPC channel for Absolute Time data
+4. A ``score::time::abs`` library for applications requiring absolute time on Clients side.
+
+The way how it is integrated is presented below.
+
+.. raw:: html
+
+
+
+.. uml:: _assets/examples/abs_time/abs_time_deployment.puml
+ :alt: Data flow
+
+.. raw:: html
+
+
+
+
+The control and data flow with Absolute Time integration is shown below.
+
+.. raw:: html
+
+
+
+.. uml:: _assets/examples/abs_time/abs_time_data_control_flow.puml
+ :alt: Data flow
+
+.. raw:: html
+
+
+
+Using in test environment
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Using in ITF
+^^^^^^^^^^^^
+
+Normal behavior is expected.
+
+Using in Component Tests on the host
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Overview
+''''''''
+
+The ``TimeDaemon`` can be utilized in the ``Component Tests`` environment to enable comprehensive testing of time-dependent components without relying on physical PTP hardware.
+This approach allows test cases to manipulate time values and synchronization states to validate application behavior under various timing conditions.
+
+For the Component tests the ``PtpMachine::PtpEngine`` library is the only one platform-dependent.
+Thus the ``TimeDaemon`` components remain largely unchanged except for the ``PTPMachine`` component, which is replaced with an test-specific implementation that can be controlled via test cases
+This component shall:
+
+1. simulate "normal" ``PTPMachine`` behavior
+2. have the communication channel to the test case and react on the manipulations
+
+Next steps: plugin system
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``TimeDaemon`` could be extended with a flexible plugin system that enables dynamic component loading, configuration, subscription and extension without requiring code changes or recompilation.
+
+Plugin Architecture
+^^^^^^^^^^^^^^^^^^^
+
+The plugin system is structured around the following key elements:
+
+1. ``Component Registry``: A central registry that maintains information about available component implementations
+2. ``Component Factory``: Creates component instances based on configuration
+3. ``Plugin Manager``: Loads and initializes plugins at runtime
+4. ``Configuration-Driven Assembly``: Components and their relationships defined in configuration files
+
+Component Creation Process
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+During ``TimeDaemon`` initialization:
+
+1. The ``Plugin Manager`` loads all specified plugins from configured directories or bazel targets
+2. Each plugin registers its component factories with the registry
+3. The ``Application`` reads the component configuration
+4. For each component in the configuration:
+
+ a. The appropriate factory is retrieved from the registry
+ b. The component is created with its specified parameters
+ c. Components are connected based on the ``MessageBroker`` topic configuration
+
+ASIL-B qualification
+~~~~~~~~~~~~~~~~~~~~~
+
+Clean separation of concerns allows ``score::time::svt`` as well as ``TimeDaemon`` to be qualified according to ASIL-B requirements following ISO 26262 standard.
diff --git a/docs/index.rst b/docs/index.rst
index 14bf088..ae5cd0b 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -36,12 +36,13 @@ The main responsibilities of TimeDaemon include:
- **Providing diagnostic information** for system monitoring
- **Supporting additional verification mechanisms** such as QualifiedVehicleTime (QVT) for safety-critical applications
-For a detailed concept and architectural design, please refer to the :doc:`TimeDaemon Concept Documentation `.
+For a detailed concept and architectural design, please refer to the :doc:`TimeDaemon Concept Documentation `.
.. toctree::
:maxdepth: 2
:caption: Contents:
+ TimeDaemon/index
time/index
Project Layout
diff --git a/docs/time/_assets/architecture_layers.puml b/docs/time/_assets/architecture_layers.puml
new file mode 100644
index 0000000..d56f229
--- /dev/null
+++ b/docs/time/_assets/architecture_layers.puml
@@ -0,0 +1,87 @@
+@startuml architecture_layers
+
+title score::time — Architecture Layers
+
+skinparam packageStyle rectangle
+skinparam backgroundColor white
+skinparam linetype ortho
+
+legend top left
+ | <#LightBlue> | Public headers (clients include these) |
+ | <#LightGreen> | Framework layer (clock/ package) |
+ | <#Wheat> | Time-domain tag packages |
+ | <#LightCoral> | Internal details (clients never include) |
+endlegend
+
+package "Client Code" #LightYellow {
+ [Application] as app
+ [Unit Test] as test
+}
+
+package "Public Interface — clock/" #LightGreen {
+ [Clock] as clock
+ [VehicleClock alias] as vclock
+ [HplsClock alias] as hclock
+ [SteadyClock alias] as sclock
+ [SystemClock alias] as sysclock
+}
+
+package "Test utilities — clock/ (testonly)" #LightYellow {
+ [test_utils::ScopedClockOverride] as guard
+ [test_utils::ClockTestFactory] as factory
+}
+
+package "Time-Domain Tags" #Wheat {
+ package "vehicle_time/" {
+ [VehicleTime tag] as vtag
+ [VehicleTimeStatus] as vstatus
+ [PTP callback types] as vcb
+ }
+ package "hpls_time/" {
+ [HplsTime tag] as htag
+ }
+ package "steady_time/" {
+ [std::chrono::steady_clock tag] as stag
+ }
+ package "system_time/" {
+ [std::chrono::system_clock tag] as systag
+ }
+ package "ptp/" {
+ [TimeSlaveSyncData] as tss
+ [PDelayMeasurementData] as pdm
+ }
+}
+
+package "Internal — details/ (never included by clients)" #LightCoral {
+ [VehicleClockIface] as viface
+ [HplsClockIface] as hiface
+ [Production backend] as prod
+ [Stub backend] as stub
+}
+
+app --> vclock : #include vehicle_clock.h
+app --> hclock : #include hpls_clock.h
+test --> guard : ScopedClockOverride
+test --> factory : ClockTestFactory::Make(mock)
+
+vclock --> clock
+hclock --> clock
+sclock --> clock
+sysclock --> clock
+
+clock --> vtag
+clock --> htag
+clock --> stag
+clock --> systag
+
+vtag --> vstatus
+vtag --> vcb
+vcb --> tss
+vcb --> pdm
+
+clock ..> viface : uses (via shared_ptr)
+clock ..> hiface : uses (via shared_ptr)
+viface <|-- prod
+viface <|-- stub
+
+@enduml
diff --git a/docs/time/_assets/class_overview.puml b/docs/time/_assets/class_overview.puml
new file mode 100644
index 0000000..03f5673
--- /dev/null
+++ b/docs/time/_assets/class_overview.puml
@@ -0,0 +1,145 @@
+@startuml class_overview
+
+title score::time — Class Overview
+
+skinparam backgroundColor white
+skinparam classBackgroundColor #FAFAFA
+skinparam classBorderColor #888888
+
+legend top left
+ | <#LightGreen> | Public framework types |
+ | <#Wheat> | Time-domain tag structs (public) |
+ | <#LightBlue> | PTP notification types (public) |
+ | <#LightCoral> | Internal interfaces (details/) |
+endlegend
+
+' --- Framework ---
+class "Clock" as clock #LightGreen {
+ + {static} GetInstance() : Clock
+ + Now() : Snapshot
+ + Subscribe(cb) : void
+ + Unsubscribe() : void
+ + IsAvailable() : bool [VehicleTime only]
+ + WaitUntilAvailable(token, until) : bool [VehicleTime only]
+ --
+ - impl_ : shared_ptr
+ - {static} instance_cache_ : weak_ptr
+ - {static} instance_override_ : shared_ptr
+}
+
+class "test_utils::ScopedClockOverride" as guard #LightGreen {
+ + ScopedClockOverride(const shared_ptr&)
+ + ~ScopedClockOverride()
+ --
+ move-only, non-re-entrant
+}
+
+class "test_utils::ClockTestFactory" as factory #LightGreen {
+ + {static} Make(const shared_ptr&) : Clock
+ --
+ all-static, no global state
+}
+
+class "ClockSnapshot" as snapshot #LightGreen {
+ + TimePoint() : TimepointT
+ + Status() : StatusT
+}
+
+class "ClockStatus" as status #LightGreen {
+ + IsFlagActive(flag) : bool
+ + IsAnyOfFlagsActive(flags) : bool
+ + IsSynchronized() : bool [VehicleTime specialization]
+ + IsValid() : bool [VehicleTime specialization]
+ + AddFlag(flag) : void
+ + ClearFlag(flag) : void
+}
+
+' --- Tags ---
+class "VehicleTime" as vtag #Wheat {
+ + enum StatusFlag
+ + using Duration = nanoseconds
+ + using Timepoint = time_point
+ + using TimeSlaveSyncDataReceivedCallback
+ + using PDelayMeasurementFinishedCallback
+}
+
+class "VehicleTimeStatus" as vstatus #Wheat {
+ + flags : ClockStatus
+ + rate_deviation : double
+ + IsSynchronized() : bool
+ + IsValid() : bool
+ + IsFlagActive(flag) : bool
+}
+
+class "HplsTime" as htag #Wheat {
+ + using Duration = nanoseconds
+ + using Timepoint = time_point
+}
+
+note as ntag
+ std::chrono::steady_clock and
+ std::chrono::system_clock are also
+ valid Tag types — built-in specializations
+ provided by clock_traits.h
+end note
+
+' --- PTP types ---
+class "TimeSlaveSyncData" as tss #LightBlue {
+ + time_slave_sync_data
+ + port_identity
+ + timestamp
+}
+
+class "PDelayMeasurementData" as pdm #LightBlue {
+ + pdelay_result
+ + port_identity
+}
+
+' --- Internal ---
+abstract class "VehicleClockIface" as viface #LightCoral {
+ + {abstract} Now() : Snapshot
+ + {abstract} IsAvailable() : bool
+ + {abstract} WaitUntilAvailable(token, until) : bool
+ + {abstract} SetTimeSlaveSyncDataReceivedCallback(cb)
+ + {abstract} UnsetTimeSlaveSyncDataReceivedCallback()
+ + {abstract} SetPDelayMeasurementFinishedCallback(cb)
+ + {abstract} UnsetPDelayMeasurementFinishedCallback()
+}
+
+abstract class "HplsClockIface" as hiface #LightCoral {
+ + {abstract} Now() : Snapshot
+}
+
+class "VehicleClockMock" as vmock #LightCoral {
+ MOCK_METHOD: Now, IsAvailable,
+ WaitUntilAvailable,
+ SetTimeSlaveSyncDataReceivedCallback,
+ UnsetTimeSlaveSyncDataReceivedCallback,
+ SetPDelayMeasurementFinishedCallback,
+ UnsetPDelayMeasurementFinishedCallback
+}
+
+class "HplsClockMock" as hmock #LightCoral {
+ MOCK_METHOD: Now
+}
+
+' --- Relationships ---
+clock "1" *--> "1" viface : impl_ (VehicleTime)
+clock "1" *--> "1" hiface : impl_ (HplsTime)
+guard ..> clock : OverrideForTest / ResetOverride
+factory ..> clock : Make() — private ctor (friend)
+
+clock --> snapshot : returns from Now()
+snapshot *-- vstatus : Status()
+
+viface <|-- vmock : test double
+hiface <|-- hmock : test double
+
+vtag --> vstatus
+vtag --> tss
+vtag --> pdm
+vstatus *-- status
+
+htag .r. ntag
+
+@enduml
diff --git a/docs/time/_assets/uc_availability.puml b/docs/time/_assets/uc_availability.puml
new file mode 100644
index 0000000..ed58b8d
--- /dev/null
+++ b/docs/time/_assets/uc_availability.puml
@@ -0,0 +1,68 @@
+@startuml uc_availability
+
+title UC2 — Availability Waiting at initialization phase
+
+skinparam backgroundColor white
+hide footbox
+autonumber "[00]"
+
+legend top left
+ |= Color |= Description |
+ |<#LightSalmon> | Application business logic |
+ |<#LightGreen> | score::time framework |
+ |<#LightCoral> | VehicleTime backend |
+endlegend
+
+box "Application" #e6f0ff
+ participant "Business\nlogic" as app #LightSalmon
+end box
+
+box "score::time" #f0fff0
+ participant "VehicleClock\n(Clock)" as clock #LightGreen
+ participant "VehicleClockIface\n(Backend)" as backend #LightCoral
+end box
+
+app -> clock : GetInstance()
+activate clock
+clock --> app : handle
+deactivate clock
+
+note over app
+ Non-blocking variant
+end note
+
+app -> clock : IsAvailable()
+activate clock
+clock -> backend : IsAvailable()
+activate backend
+backend --> clock : false (PTP not yet ready)
+deactivate backend
+clock --> app : false
+deactivate clock
+
+app -> app : ... wait / retry ...
+
+note over app
+ Blocking variant with stop_token + deadline
+end note
+
+app -> clock : WaitUntilAvailable(stop_token,\n steady_clock::now() + 30s)
+activate clock
+loop until available OR deadline OR stop requested
+ clock -> backend : IsAvailable()
+ activate backend
+ backend --> clock : bool
+ deactivate backend
+end
+clock --> app : true (became available)
+deactivate clock
+
+note over of app
+ Only Clock domains that require
+ async init expose IsAvailable().
+ HplsTime / SteadyTime / SystemTime
+ are always ready — calling IsAvailable()
+ on them is a compile error.
+end note
+
+@enduml
diff --git a/docs/time/_assets/uc_polling.puml b/docs/time/_assets/uc_polling.puml
new file mode 100644
index 0000000..7e42fb4
--- /dev/null
+++ b/docs/time/_assets/uc_polling.puml
@@ -0,0 +1,56 @@
+@startuml uc_polling
+
+title UC1 — Time Polling with Status Check
+
+skinparam backgroundColor white
+hide footbox
+autonumber "[00]"
+
+legend top left
+ |= Color |= Description |
+ |<#LightSalmon> | Application business logic |
+ |<#LightGreen> | score::time framework |
+ |<#LightCoral> | VehicleTime backend |
+endlegend
+
+box "Application" #e6f0ff
+ participant "Application\ncode" as app #LightSalmon
+end box
+
+box "score::time" #f0fff0
+ participant "VehicleClock\n(Clock)" as clock #LightGreen
+ participant "VehicleClockIface\n(Backend)" as backend #LightCoral
+end box
+
+app -> clock : VehicleClock::GetInstance()
+activate clock
+clock -> clock : check cache / override
+clock --> app : Clock handle
+deactivate clock
+
+app -> clock : Now()
+activate clock
+clock -> backend : Now()
+activate backend
+note right of backend
+ reads PTP SHM seqlock
+ atomically
+end note
+backend --> clock : ClockSnapshot{timepoint, status}
+deactivate backend
+clock --> app : ClockSnapshot
+deactivate clock
+
+alt snapshot.Status().IsSynchronized()
+ app -> app : snapshot.TimePoint()
+ note right of app
+ use the timepoint value
+ end note
+else not synchronized
+ note right of app
+ time is not synchronized yet —
+ handle this case (e.g. retry later)
+ end note
+end
+
+@enduml
diff --git a/docs/time/_assets/uc_subscription.puml b/docs/time/_assets/uc_subscription.puml
new file mode 100644
index 0000000..11551f8
--- /dev/null
+++ b/docs/time/_assets/uc_subscription.puml
@@ -0,0 +1,69 @@
+@startuml uc_subscription
+
+title UC3 — Async PTP Event Subscription
+
+skinparam backgroundColor white
+hide footbox
+autonumber "[00]"
+
+legend top left
+ |= Color |= Description |
+ |<#LightSalmon> | Application code |
+ |<#LightGreen> | score::time framework |
+ |<#LightCoral> | VehicleTime backend |
+endlegend
+
+box "Application" #e6f0ff
+ participant "Application\ncomponent" as app #LightSalmon
+end box
+
+box "score::time" #f0fff0
+ participant "VehicleClock\n(Clock)" as clock #LightGreen
+ participant "VehicleClockIface\n(Backend)" as backend #LightCoral
+end box
+
+app -> clock : GetInstance()
+activate clock
+clock --> app : handle
+deactivate clock
+
+note over app
+ Register callbacks once at init
+end note
+
+app -> clock : Subscribe>(cb)
+activate clock
+clock -> backend : SetTimeSlaveSyncDataReceivedCallback(cb)
+activate backend
+backend --> clock
+deactivate backend
+clock --> app
+deactivate clock
+
+app -> clock : Subscribe>(cb)
+activate clock
+clock -> backend : SetPDelayMeasurementFinishedCallback(cb)
+activate backend
+backend --> clock
+deactivate backend
+clock --> app
+deactivate clock
+
+... PTP sync event arrives from hardware ...
+
+backend -> app : cb(TimeSlaveSyncData)
+note right of backend
+ called on backend thread
+ — callback must be thread-safe
+end note
+
+... PTP peer-delay measurement completes ...
+
+backend -> app : cb(PDelayMeasurementData)
+
+note over app
+ To remove a subscription:
+ clock.Unsubscribe>()
+end note
+
+@enduml
diff --git a/docs/time/_assets/uc_testing.puml b/docs/time/_assets/uc_testing.puml
new file mode 100644
index 0000000..096adc1
--- /dev/null
+++ b/docs/time/_assets/uc_testing.puml
@@ -0,0 +1,89 @@
+@startuml uc_testing
+
+title UC6 — Unit Test with test_utils::ScopedClockOverride (scope-bound)
+
+skinparam backgroundColor white
+hide footbox
+autonumber "[00]"
+
+legend top left
+ |= Color |= Description |
+ |<#LightSalmon> | Test code |
+ |<#LightGreen> | score::time framework |
+ |<#PaleGoldenRod> | Mock backend (GMock) |
+ |<#LightBlue> | Application Under Test |
+endlegend
+
+box "Test" #fafaf0
+ participant "TEST()" as test #LightSalmon
+end box
+
+box "score::time" #f0fff0
+ participant "ScopedClockOverride\n" as guard #LightGreen
+ participant "VehicleClock\n(Clock)" as clock #LightGreen
+end box
+
+box "Mock" #fffff0
+ participant "VehicleClockMock" as mock #PaleGoldenRod
+end box
+
+box "System Under Test" #e6f0ff
+ participant "MyService\n(calls GetInstance())" as sut #LightBlue
+end box
+
+test -> mock : make_shared()
+activate mock
+mock --> test : shared_ptr
+
+test -> guard : test_utils::ScopedClockOverride{mock}
+activate guard
+guard -> clock : OverrideForTest(mock)
+activate clock
+note right of clock
+ static override_ = mock
+ next GetInstance() returns mock
+end note
+clock --> guard
+deactivate clock
+
+test -> test : EXPECT_CALL(*mock, Now())\n .WillOnce(Return(snapshot))
+
+test -> sut : MySvc svc{}\nsvc.DoSomething()
+activate sut
+
+sut -> clock : VehicleClock::GetInstance()
+activate clock
+note right of clock
+ override_ is set → returns mock
+end note
+clock --> sut : Clock wrapping mock
+deactivate clock
+
+sut -> clock : Now()
+activate clock
+clock -> mock : Now()
+activate mock
+mock --> clock : ClockSnapshot{...}
+deactivate mock
+clock --> sut : snapshot
+deactivate clock
+
+sut --> test
+deactivate sut
+
+note over guard
+ guard goes out of scope
+end note
+test -> guard : (destructor)
+guard -> clock : ResetOverride()
+activate clock
+note right of clock
+ static override_ = nullptr
+ production backend resumes
+end note
+clock --> guard
+deactivate clock
+deactivate guard
+deactivate mock
+
+@enduml
diff --git a/docs/time/index.rst b/docs/time/index.rst
index aa2d387..3abe1ed 100644
--- a/docs/time/index.rst
+++ b/docs/time/index.rst
@@ -1,78 +1,102 @@
-Concept for TimeDaemon
-======================
+score::time — Unified Clock Interface
+======================================
.. contents:: Table of Contents
:depth: 3
:local:
-TimeDaemon concept
-------------------
+Overview
+--------
-Use Cases
-~~~~~~~~~
+``score::time`` provides a **unified, clock-domain-agnostic API** for reading time snapshots,
+subscribing to PTP protocol events, and checking clock readiness — all through a single
+template wrapper ``Clock``.
-TimeDaemon is the non Autosar adaptive process who is intended to get the Vehicle Time from the ptp slave daemon (ptpd or any other), verify and validate the timepoints and distribute time information across the clients.
+The design separates three concerns:
-More precisely we can specify the following use cases for the time daemon:
+1. **What kind of time** — expressed as a *tag struct* (``VehicleTime``, ``HplsTime``,
+ ``std::chrono::steady_clock``, ``std::chrono::system_clock``).
+2. **How to access it** — always via ``Clock::GetInstance()``; never via a factory class.
+3. **How to use it in testing** — via ``test_utils::ScopedClockOverride`` (scope-bound global
+ override when the SUT calls ``GetInstance()`` internally) or
+ ``test_utils::ClockTestFactory`` (direct constructor injection, no global state).
-1. Providing current Vehicle time to different applications
-2. Setting the synchronization qualifier (aka Synchronized, Timeout, so on)
-3. Providing needed information for diagnostics
-4. Providing needed information for addition verification, ex SafeCarTime
+Clock domains
+~~~~~~~~~~~~~
-The raw architectural diagram is represented below.
+.. list-table::
+ :header-rows: 1
+ :widths: 20 30 50
+
+ * - Clock alias
+ - Tag
+ - Status concept
+ * - ``VehicleClock``
+ - ``VehicleTime``
+ - ``VehicleTimeStatus`` (PTP sync flags + rate deviation)
+ * - ``HplsClock``
+ - ``HplsTime``
+ - ``NoStatus`` (always-ready local steady clock)
+ * - ``SteadyClock``
+ - ``std::chrono::steady_clock``
+ - ``NoStatus``
+ * - ``SystemClock``
+ - ``std::chrono::system_clock``
+ - ``NoStatus``
+
+Relation to automotive time synchronization standards
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``score::time`` addresses the same problem domain as the time-base management modules
+found in automotive middleware stacks: reading a time snapshot, inspecting
+synchronization quality flags, waiting for clock availability, and subscribing to
+PTP protocol events.
+
+The key design upgrade ``score::time`` brings over typical C-style automotive APIs is
+replacing the **runtime integer time-base selector** with a **compile-time ``Tag``
+template parameter**. This gives full type-safety and zero runtime dispatch for
+time-domain selection: a component that depends on ``Clock`` simply cannot
+accidentally read ``VehicleTime`` at runtime — the compiler enforces the distinction.
+All other structural concepts (composite snapshot result, quality status flags, layered
+backend hiding) follow the same principles as established automotive time
+synchronization practice, expressed in modern C++.
+
+Architecture
+------------
.. raw:: html
-.. uml:: _assets/sad_deployment.puml
- :alt: Raw architectural diagram
+.. uml:: _assets/architecture_layers.puml
+ :alt: Architecture layer diagram
.. raw:: html
-Components decomposition
-~~~~~~~~~~~~~~~~~~~~~~~~~
+The library has three layers:
-The design consists of several sw components:
+- **Public headers** under ``score/time//`` — tag structs and callback types that
+ clients include directly.
+- **Framework layer** under ``score/time/clock/`` — the ``Clock`` wrapper, traits,
+ subscription hooks, and the test utilities (``clock_test_utils`` Bazel target). This
+ layer has no backend dependency.
+ The ``clock_test_utils`` target (``scoped_clock_override.h``, ``clock_test_factory.h``)
+ is ``testonly`` and must not appear in production deps.
+- **Internal** under ``score/time//details/`` — pure-virtual backend interfaces and
+ production implementations. *Clients must never include anything from a* ``details/``
+ *subfolder.*
-1. `Application <#application-sw-component>`_
-2. `Message Broker <#message-broker-sw-component>`_
-3. `ControlFlowDivider <#controlflowdivider-sw-component>`_
-4. `PTP Machine <#ptp-machine-sw-component>`_
-5. `Verification Machine <#verification-machine-sw-component>`_
-6. `IPC Machine <#ipc-machine-sw-component>`_
-7. `score::time::svt <#score-time-synchronizedvehicletime-sw-component>`_
-
-Deployment view
-~~~~~~~~~~~~~~~
-
-The design deployment is represented on the following diagram:
+Class overview
+~~~~~~~~~~~~~~
.. raw:: html
-.. uml:: _assets/dd_deployment.puml
- :alt: Deployment View
-
-.. raw:: html
-
-
-
-Class view
-~~~~~~~~~~
-
-Main classes and components are presented on this diagram:
-
-.. raw:: html
-
-
-
-.. uml:: _assets/dd_class.puml
- :alt: Class View
+.. uml:: _assets/class_overview.puml
+ :alt: Class overview
:width: 100%
:align: center
@@ -80,904 +104,317 @@ Main classes and components are presented on this diagram:
-Data and control flow
-~~~~~~~~~~~~~~~~~~~~~
-
-The Data and Control flow are presented in the following diagram:
-
-.. raw:: html
-
-
-
-.. uml:: _assets/dd_data_control_flow.puml
- :alt: Data and Control flow View
-
-.. raw:: html
-
-
-
-On this view you could see several "workers" scopes:
-
-1. PTP retrieving scope
-2. PTPTimeInfo handling scope
-3. PTPTimeInfo receiving on Application side scope
-
-Each control flow is implemented with the dedicated thread or process and is independent form another ones.
-
-Control flows
-^^^^^^^^^^^^^
-
-PTP retrieving scope
-''''''''''''''''''''
-
-This control flow is responsible for the:
-
-1. retrieve the latest information from the ptp stack and
-2. provide it to the ``PTPTimeInfo handling`` control flow
-
-PTPTimeInfo handling scope
-'''''''''''''''''''''''''''
-
-This control flow is responsible for the:
-
-1. Validate the time information, provided by the ``PTP retrieving`` workflow and
-2. publish it to the ``Applications`` via some IPC
-
-PTPTimeInfo receiving on Application side scope
-''''''''''''''''''''''''''''''''''''''''''''''''
-
-This control flow is responsible for the:
-
-1. Propagate the time information from the ``PTPTimeInfo handling`` to the business logic of the applications.
-
-Data types or events
-^^^^^^^^^^^^^^^^^^^^
-
-There are also several data types, which components are communicating to each other:
-
-Raw ptp data
-''''''''''''
-
-``raw_ptp_data`` is the data, which is provided by ``PTPMachine`` component and is just the raw data from ptp stack. is handled in the "PTP retrieving scope"
-
-Input ptp data
-''''''''''''''
-
-``input_ptp_data`` is the same data as `raw_ptp_data <#raw-ptp-data>`_ but which is handled already in "PTPTimeInfo handling scope"
-
-Verified ptp data
-'''''''''''''''''
-
-``verified_ptp_data`` is the `input_ptp_data <#input-ptp-data>`_ which was verified according to the business logic and updated accordingly. This data should be published to the Applications.
-
-SW Components decomposition
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Application SW component
-^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The ``Application`` component is the main entry point for the ``TimeDaemon``. It is responsible for orchestrating the overall lifecycle and initialization of all daemon components.
-
-The ``TimebaseHandler`` component is an timebase-specific logic implementation. There might be several handlers available in the ``Application`` per amount of timebases supported. This separation allows for different timebase implementations while maintaining a consistent application structure.
-
-Component requirements
-''''''''''''''''''''''
-
-The ``Application`` has the following requirements:
-
-- The ``Application`` shall implement the ``Initialize()`` method to create and initialize all daemon components
-- The ``Application`` shall implement the ``Run()`` method to start all components and wait for termination
-- The ``Application`` shall connect components to the ``MessageBroker`` by setting up all required subscriptions during initialization stage
-- The ``Application`` shall support extension for different timebases.
-
-Class view
-''''''''''
-
-The Class Diagram is presented below:
-
-.. raw:: html
-
-
-
-.. uml:: _assets/app/app_class.puml
- :alt: Class Diagram
-
-.. raw:: html
-
-
-
-Initialization flow
-'''''''''''''''''''
-
-During initialization, the ``Application`` uses the ``MachineFactory`` to create, configure and subscribe all components in a specific order:
-
-- Create the ``MessageBroker`` first, as other components depend on it
-- Create ProactiveMachines (``PtpMachine``, ``ControlFlowDivider``) that drive system behavior
-
- - Initialize each component
- - Set up MessageBroker subscriptions to component notifications
- - Set up component subscriptions to MessageBroker topics
-
-- Create ReactiveMachines (``VerificationMachine``, ``IPCMachine``) that respond to events
-
- - Initialize each component
- - Set up MessageBroker subscriptions to component notifications
- - Set up component subscriptions to MessageBroker topics
-
-The initialization workflow is represented in the following sequence diagram:
-
-.. raw:: html
-
-
-
-.. uml:: _assets/app/app_init_seq.puml
- :alt: Initialization workflow
-
-.. raw:: html
-
-
-
-Execution and shutdown flow
-''''''''''''''''''''''''''''
-
-During execution, the ``Application``:
-
-- Starts all ``ProactiveMachines`` in the correct order
-- Monitors the stop token for termination requests
-- When termination is requested, stops all ``ProactiveMachines`` in reverse order
-
-The execution and shutdown workflow is represented in the following sequence diagram:
-
-.. raw:: html
-
-
-
-.. uml:: _assets/app/app_workflow_seq.puml
- :alt: Execution workflow
-
-.. raw:: html
-
-
-
-Message Broker SW component
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The ``Message Broker`` component is the central communication hub that implements the Publish-Subscribe pattern within the ``TimeDaemon``. It enables decoupled communication between components by managing topics and distributing messages to interested subscribers.
-
-The component maintains a registry of topics and their subscribers, delivering messages to all registered subscribers when a component publishes to a topic. This decoupling allows components to evolve independently without direct dependencies on each other.
-
-Component requirements
-''''''''''''''''''''''
-
-The ``Message Broker`` has the following requirements:
-
-- The ``Message Broker`` shall maintain a registry of topics and their subscribers
-- The ``Message Broker`` shall allow components to subscribe to topics of interest
-- The ``Message Broker`` shall distribute messages to all subscribers when a topic is published to
-
-Class view
-''''''''''
-
-The Class Diagram is presented below:
-
-.. raw:: html
-
-
-
-.. uml:: _assets/msg_broker/msg_broker_class.puml
- :alt: Class Diagram
-
-.. raw:: html
-
-
-
-Initialization flow
-'''''''''''''''''''
-
-During initialization, all machine objects, see ``BaseMachine``, the ``Application`` component needs to subscribe machines to ``Message Broker`` to the topics of interest.
-
-The initialization workflow is represented in the following sequence diagram:
-
-.. raw:: html
-
-
-
-.. uml:: _assets/msg_broker/msg_broker_init_seq.puml
- :alt: Initialization workflow
-
-.. raw:: html
-
-
-
-Message flow
-''''''''''''
-
-The message flow through the ``Message Broker`` is represented in the following sequence diagram:
-
-.. raw:: html
-
-
-
-.. uml:: _assets/msg_broker/msg_broker_workflow_seq.puml
- :alt: Message DiagramFlow
-
-.. raw:: html
-
-
-
-Concurrency aspects
-'''''''''''''''''''
-
-The ``Message Broker`` doesn't provide any synchronization between the publish-callback invoking processes.
-Moreover, the callback invoke will happened in the scope of the thread, where the ``publish`` method is called.
-To separate the control flows, the `ControlFlowDivider <#controlflowdivider-sw-component>`_ shall be used
-
-Scalability
-'''''''''''
-
-The ``Message Broker`` can be extended to support configuration-driven subscriptions, where topic relationships are defined in configuration files rather than hardcoded.
-
-ControlFlowDivider SW component
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The ``ControlFlowDivider`` component is responsible for separating control (execution) flows within the ``TimeDaemon`` and providing the execution control flow for the data processing. It contains dedicated threads where data is published to the ``Message Broker``, ensuring that blocking operations in one component do not affect the execution of other components and data missing is not affecting the data analysis in processing pipeline.
-
-This component acts as a crucial intermediary that maintains the responsiveness of the system by decoupling the execution contexts of different operations, particularly between the PTP data retrieval and the time data processing pipelines.
-
-Component requirements
-''''''''''''''''''''''
-
-The ``ControlFlowDivider`` has the following requirements:
-
-- The ``ControlFlowDivider`` shall provide separate execution threads for different control flows
-- The ``ControlFlowDivider`` shall isolate components from execution time variations in other components
-- The ``ControlFlowDivider`` shall maintain consistent data publishing rates to the subscribers
-- - The ``ControlFlowDivider`` shall push the last received data to the subscribers if there is no new data for some time with the predefined rate, to avoid data missing in the processing pipeline
-- The ``ControlFlowDivider`` shall enable periodic processing of the pipeline through consistent event generation
-- The ``ControlFlowDivider`` shall buffer incoming data from fast producers
-
-Class view
-''''''''''
-
-The Class Diagram is presented below:
-
-.. raw:: html
-
-
-
-.. uml:: _assets/ctrlflow/ctrlflow_class.puml
- :alt: Class Diagram
-
-.. raw:: html
-
-
-
-Initialization flow
-'''''''''''''''''''
-
-During initialization, the ``ControlFlowDivider`` performs the following steps:
-
-- Initialize internal data structures (queue, mutex, condition variable)
-- Create a worker thread to process data independently
-- Start the worker thread which enters a waiting state
-
-The initialization workflow is represented in the following sequence diagram:
-
-.. raw:: html
-
-
-
-.. uml:: _assets/ctrlflow/ctrlflow_init_seq.puml
- :alt: Initialization workflow
-
-.. raw:: html
-
-
-
-Message flow
-''''''''''''
-
-When the ``ControlFlowDivider`` receives new data from the ``PTP Machine`` via the ``Message Broker``, it processes it through the following workflow:
-
-1. The ``Message Broker`` executes the onNewData callback and provides the new data
-2. The data is placed in a thread-safe queue and exists from the callback
-3. The worker thread wakes up, retrieves the data from the queue and
-4. The worker thread publishes the retrieved data to the `input_ptp_data <#input-ptp-data>`_ topic
-5. if there was no data for some timeout, the worker shall published the empty data to the `input_ptp_data <#input-ptp-data>`_ topic.
-
-This separation of control flows ensures that slow or blocking operations in the PTP stack communication do not affect the responsiveness of time data processing in the ``TimeDaemon``.
-
-The execution workflow is represented in the following sequence diagram:
-
-.. raw:: html
-
-
-
-.. uml:: _assets/ctrlflow/ctrlflow_workflow_seq.puml
- :alt: Execution workflow
-
-.. raw:: html
-
-
-
-PTP Machine SW component
-^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The ``PTP Machine`` component shall retrieve all needed information from the ptp stack (ex ``ptpd``) and provide it to the ``Message Broker`` for routing.
-All communication with the ptp stack ight use ``devctl`` calls, which take some time, thus these calls shall be done in the dedicated thread.
-
-Component requirements
-''''''''''''''''''''''
-
-The ``PTP Machine`` has the following requirements:
-
-- The ``PTP Machine`` shall retrieve the latest time information from the PTP stack (e.g., ``ptpd``)
-- The ``PTP Machine`` shall publish retrieved time information to the ``Message Broker`` using the defined topic
-- The ``PTP Machine`` shall format data according to the ``PTPTimeInfo`` structure required by downstream components
-- The ``PTP Machine`` shall retrieve time information at a consistent rate to maintain time synchronization
-- The ``PTP Machine`` shall maintain consistent publishing rates for time data even when experiencing delays in PTP stack communication.
-- The ``PTP Machine`` shall support exchangeability with different PTP stack implementations
-
-Class view
-''''''''''
-
-The Class Diagram is presented below.
-
-.. raw:: html
-
-
-
-.. uml:: _assets/ptp_machine/ptp_machine_class.puml
- :alt: Class Diagram
-
-.. raw:: html
-
-
-
-As long as it wraps the particular communication with the ptp stack, the implementations should be easily exchangeable with another one in case of stack change.
-
-Component initialization
-'''''''''''''''''''''''''
-
-During initialization the ``PTP Machine`` shall initialize the ptp stack to be able to communicate with it.
-
-The initialization workflow is described below.
-
-.. raw:: html
-
-
-
-.. uml:: _assets/ptp_machine/ptp_machine_init_seq.puml
- :alt: Initialization workflow
-
-.. raw:: html
-
-
-
-Publish new data
-''''''''''''''''
-
-After ``PTP Machine`` collects new data from the ptp stack, the component shall publish it to the ``Message Broker`` as `raw-ptp-data <#raw-ptp-data>`_.
-
-The publish workflow is described below.
-
-.. raw:: html
-
-
-
-.. uml:: _assets/ptp_machine/ptp_machine_get_new_data_seq.puml
- :alt: Publish workflow
-
-.. raw:: html
-
-
-
-Verification Machine SW component
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The ``Verification Machine`` component is responsible for validating and qualifying the time information received from the ``PTP Machine``. It applies various validation rules to ensure the time data meets quality requirements before distribution to applications.
-
-The component implements a pipeline pattern where each stage performs a specific validation and adds appropriate qualifiers to the time data. This modular design allows for easy extension with additional validation steps.
-
-Component requirements
-''''''''''''''''''''''
-
-The ``Verification Machine`` has the following requirements:
-
-- The ``Verification Machine`` shall validate and qualify time information received from the PTP Machine
-- The ``Verification Machine`` shall validate if the time base is synchronized state
-- The ``Verification Machine`` shall validate if the time base is in timeout state
-- The ``Verification Machine`` shall validate timestamp for time jumps based on local clock
-- The ``Verification Machine`` shall subscribe to the `input_ptp_data <#input-ptp-data>`_ topic via the ``Message Broker``
-- The ``Verification Machine`` shall publish verified time data to the ``Message Broker`` using the `verified-ptp-data <#verified-ptp-data>`_ topic
-- The ``Verification Machine`` shall support extensibility to add new validation stages in the pipeline
-
-Class view
-''''''''''
-
-The Class Diagram is presented below.
-
-.. raw:: html
-
-
-
-.. uml:: _assets/ver_machine/ver_class.puml
- :alt: Class Diagram
-
-.. raw:: html
-
-
-
-Component initialization
-'''''''''''''''''''''''''
-
-During initialization, the ``Verification Machine`` performs the following steps:
-
-1. Set up the validation pipeline by creating and connecting validation stages
-
-The component shall be subscribed by the ``Application`` to the `input_ptp_data <#input-ptp-data>`_ topic of the ``MessageBroker``
-
-The initialization workflow is represented in the following sequence diagram:
-
-.. raw:: html
-
-
-
-.. uml:: _assets/ver_machine/ver_init_seq.puml
- :alt: Initialization workflow
-
-.. raw:: html
-
-
-
-Data verification workflow
-'''''''''''''''''''''''''''
-
-When the ``Verification Machine`` receives new PTP data, it processes it through the validation pipeline:
-
-.. raw:: html
-
-
-
-.. uml:: _assets/ver_machine/ver_verification_seq.puml
- :alt: Validation pipeline
-
-.. raw:: html
-
-
-
-IPC Machine SW component
-^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The ``IPC Machine`` component shall get the `verified-ptp-data <#verified-ptp-data>`_ from the ``Verification Machine`` and provide it to the ``score::time::svt`` through the ``score::communication`` module. As the fast initial implementation, a custom shared memory backend is used.
-
-The component provides two sub components: publisher and receiver to be deployed on the TimeDaemon and Application sides accordingly.
-
-Component requirements
-''''''''''''''''''''''
-
-The ``IPC Machine`` has the following requirements:
-
-- The ``IPC Machine`` shall provide verified time data to the ``score::time::svt`` component through the ``score::communication`` module
-- The ``IPC Machine`` shall create and initialize the IPC
-- The ``IPC Machine`` shall support multiple client applications accessing the same time data
-- The ``IPC Machine`` shall subscribe to the `verified_ptp_data <#verified-ptp-data>`_ topic via the ``Message Broker``
-
-Class view
-''''''''''
-
-The Class Diagram is presented below.
-
-.. raw:: html
-
-
-
-.. uml:: _assets/ipc/ipc_class.puml
- :alt: Class Diagram
-
-.. raw:: html
-
-
-
-Component initialization
-'''''''''''''''''''''''''
-
-Initialization is divided to two parts:
-
-1. Initialization on the TimeDaemon side
-2. Initialization on the Application side
+Use Cases
+---------
-Important thing, the ``score::communication`` publisher shall be created and offered by the ``TimeDaemon`` before the Application side subscriber can connect. The Application shall retry until the service is found.
+UC1 — Time polling with status check
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The main workflow is described below.
+The most common pattern: obtain a snapshot and inspect the synchronization quality
+before using the time value.
.. raw:: html
-.. uml:: _assets/ipc/ipc_init_seq.puml
- :alt: Main workflow
+.. uml:: _assets/uc_polling.puml
+ :alt: UC1 — Time polling sequence
.. raw:: html
-The component shall be subscribed during initialization by the ``Application`` on the `verified-ptp-data <#verified-ptp-data>`_ updates from the ``Message Broker``
-
-Publish new data
-''''''''''''''''
-
-When ``IPC Machine`` receives the new `verified-ptp-data <#verified-ptp-data>`_ from Message Broker, it shall serialize data and publish it via ``score::communication``.
-
-As long as there are different use cases by using it, like:
-
-1. Get current Vehicle time
-2. Get data for diagnostics
+**Code example:**
-All ``PTPTimeInfo`` data (or almost all) shall be published to the subscribed applications.
+.. code-block:: cpp
-The publish workflow is described below.
+ #include "score/time/vehicle_time/vehicle_clock.h"
-.. raw:: html
+ void MyComponent::CheckTime()
+ {
+ auto clock = score::time::VehicleClock::GetInstance();
+ auto snapshot = clock.Now();
+
+ if (snapshot.Status().IsSynchronized()) {
+ // Time is valid and synchronized with the PTP Grand Master.
+ auto tp = snapshot.TimePoint();
+ // use tp ...
+ } else if (snapshot.Status().IsFlagActive(
+ score::time::VehicleTime::StatusFlag::kTimeOut)) {
+ // Clock has not received a sync message within the timeout window.
+ HandleTimeout();
+ }
+ }
-
+Status flags for ``VehicleTime``:
-.. uml:: _assets/ipc/ipc_publish_seq.puml
- :alt: Publish workflow
++---------------------------+--------------------------------------------------------------+
+| Flag | Meaning |
++===========================+==============================================================+
+| ``kSynchronized`` | Synchronized at least once to the PTP Grand Master |
++---------------------------+--------------------------------------------------------------+
+| ``kSynchToGateway`` | Currently in sync with the PTP gateway |
++---------------------------+--------------------------------------------------------------+
+| ``kTimeOut`` | No sync message received within the configured time window |
++---------------------------+--------------------------------------------------------------+
+| ``kTimeLeapFuture`` | A large forward adjustment was applied |
++---------------------------+--------------------------------------------------------------+
+| ``kTimeLeapPast`` | A large backward adjustment was applied |
++---------------------------+--------------------------------------------------------------+
+| ``kUnknown`` | Status cannot be determined |
++---------------------------+--------------------------------------------------------------+
-.. raw:: html
+``IsSynchronized()`` returns ``true`` only when ``kSynchronized`` is set **and** none of
+``{kTimeOut, kTimeLeapFuture, kTimeLeapPast}`` is set.
-
+UC2 — Availability waiting at startup
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Receive data
-''''''''''''
-
-From Application side the receiver shall subscribe via ``score::communication`` and provide the data to the caller.
-
-The receive workflow is described below.
+PTP-backed clocks (``VehicleTime``) require an async connection to the PTP stack. Use
+``IsAvailable()`` for a non-blocking probe, or ``WaitUntilAvailable()`` when it is
+acceptable to block the calling thread.
.. raw:: html
-.. uml:: _assets/ipc/ipc_receive_seq.puml
- :alt: Receive workflow
+.. uml:: _assets/uc_availability.puml
+ :alt: UC2 — Availability waiting sequence
.. raw:: html
-score::time::SynchronizedVehicleTime SW Component
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-``score::time::svt`` is the interface towards Applications, how they could get the access to the Vehicle Time.
-
-Component requirements
-''''''''''''''''''''''
-
-The ``score::time::svt`` has the following requirements:
-
-- The ``score::time::svt`` shall expose vehicle time amd it synchronization status to applications
-- The ``score::time::svt`` shall retrieve time data from ``IPC Machine`` receiver component
-- The ``score::time::svt`` shall adjust vehicle time with local clock to provide accurate timestamps
-- The ``score::time::svt`` shall support fast and low-latency time access via the ``Now()`` method
+**Code example:**
-Class view
-''''''''''
+.. code-block:: cpp
-The Class Diagram is presented below.
+ #include "score/time/vehicle_time/vehicle_clock.h"
+ #include
+ #include
-.. raw:: html
+ void MyService::Init(const score::cpp::stop_token& stop)
+ {
+ auto clock = score::time::VehicleClock::GetInstance();
-
+ // Non-blocking probe:
+ if (!clock.IsAvailable()) {
+ LOG_WARN("VehicleTime not yet available, waiting ...");
+ }
-.. uml:: _assets/mw/mw_class.puml
- :alt: Class Diagram
+ // Blocking wait — returns true if ready, false on timeout or stop request:
+ const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds{30};
+ if (!clock.WaitUntilAvailable(stop, deadline)) {
+ LOG_ERROR("VehicleTime did not become available within 30 s");
+ return;
+ }
-.. raw:: html
+ auto snapshot = clock.Now();
+ // ...
+ }
-
+.. note::
-Receive data
-''''''''''''
+ ``IsAvailable()`` and ``WaitUntilAvailable()`` are **only available on clock domains that
+ require an async transport** (currently ``VehicleTime``). Calling them on ``HplsTime``,
+ ``SteadyClock``, or ``SystemClock`` is a **compile error** — those clocks are always ready.
-In case of receiving data, the ``Application`` shall just call ``score::time::svt::Now()`` and it shall return the latest published Vehicle Time, which is already adjusted with local clock.
+UC3 — Async PTP event subscription
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-To do so, in the ``score::time::svt`` there is a thread, who polls for new data the ``IPCMachine::receiver`` and put the data to the process-internal shared buffer (memory), from where it is being read on ``score::time::svt::Now()`` call.
+``VehicleTime`` delivers two kinds of PTP protocol events via callbacks:
-The main workflow is described below.
+- ``TimeSlaveSyncData`` — fired on each PTP sync message.
+- ``PDelayMeasurementData`` — fired when a peer-delay measurement completes.
.. raw:: html
-.. uml:: _assets/mw/mw_time_receive_seq.puml
- :alt: Receive data workflow
+.. uml:: _assets/uc_subscription.puml
+ :alt: UC3 — Subscription sequence
.. raw:: html
-This design guarantees very low latency of the executing the ``score::time::svt::Now()`` function but brings additional efforts for the thread, memory buffer, synchronizing and so on.
+**Code example:**
-Receive data (simplified)
-''''''''''''''''''''''''''
+.. code-block:: cpp
-As an alternative design, the receiving concept could be simplified and ``score::time::svt::Now()`` could directly invoke the ``IPCMachine::receiver`` call, adjust the ``Vehicle time`` and return it to the ``Application``.
+ #include "score/time/vehicle_time/vehicle_clock.h"
+ #include "score/time/ptp/time_slave_sync_data.h"
+ #include "score/time/ptp/pdelay_measurement_data.h"
-The design is represented below.
+ void MyDiagHandler::RegisterCallbacks()
+ {
+ auto clock = score::time::VehicleClock::GetInstance();
-.. raw:: html
+ clock.Subscribe>(
+ [this](const auto& data) { OnTimeSyncData(data); });
-
+ clock.Subscribe>(
+ [this](const auto& data) { OnPDelayData(data); });
+ }
-.. uml:: _assets/mw/mw_time_receive_simple_seq.puml
- :alt: Receive data (simplified) workflow
+ void MyDiagHandler::Shutdown()
+ {
+ auto clock = score::time::VehicleClock::GetInstance();
+ clock.Unsubscribe>();
+ clock.Unsubscribe>();
+ }
-.. raw:: html
+.. warning::
-
+ Callbacks are invoked on the **backend thread** — the callback implementation must be
+ thread-safe.
-In this case, there will be no need for additional thread, shared buffer and synchronization, but the ``score::time::svt::Now()`` call will take longer. To decide which approach to use, additional tests shall be
+UC4 — Status flag mapping (diagnostics)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Deployment
-''''''''''
+When mapping ``VehicleTime`` status flags to diagnostic bits (e.g. DTC bitmasks), access
+the flag type via the tag struct directly:
-The implementation of ``score::time::svt::details::timed`` could be placed in parallel to other implementations, like ``score::time::svt::details::mocked`` one and could be selected by Bazel select. Also it will ease the integration process.
+.. code-block:: cpp
-Logging configuration
-~~~~~~~~~~~~~~~~~~~~~
+ #include "score/time/vehicle_time/vehicle_clock.h"
+ #include
-The daemon should have the following logging contexts:
+ using SvtFlag = score::time::VehicleTime::StatusFlag;
-.. list-table:: Logging Contexts
- :header-rows: 1
- :widths: 30 20 50
-
- * - component
- - App/Context ID
- - Comments
- * - TimeDaemon
- - TDON
- - **T**\ ime\ **D**\ aem\ **ON**
- * - Application
- - TDAP
- - **T**\ ime\ **D**\ aemon **AP**\ plication
- * - MessageBroker
- - TDMB
- - **T**\ ime\ **D**\ aemon **M**\ essage\ **B**\ roker
- * - ControlFlowDivider
- - TDCD
- - **T**\ ime\ **D**\ aemon **C**\ ontrolFlow\ **D**\ ivider
- * - PTPMachine
- - TDPM
- - **T**\ ime\ **D**\ aemon **P**\ TP\ **M**\ achine
- * - VerificationMachine
- - TDVM
- - **T**\ ime\ **D**\ aemon **V**\ erification\ **M**\ achine
- * - IPCMachine::receiver
- - TDIR
- - **T**\ ime\ **D**\ aemon **I**\ PCMachine::\ **R**\ eceiver
- * - IPCMachine::publisher
- - TDIP
- - **T**\ ime\ **D**\ aemon **I**\ PCMachine::\ **P**\ ublisher
-
-Variability
-~~~~~~~~~~~
-
-Configuration files
-^^^^^^^^^^^^^^^^^^^
-
-The ``TimeDaemon`` uses structured configuration files to enable customization of its runtime behavior. These data could be configured:
-
-1. Component-specific Configuration:
-
- a. Each component can have dedicated configuration sections
- b. Parameters such as update rates, timeouts, and thresholds can be specified
-
-2. Topic Configuration:
-
- a. Topics for the ``Message Broker`` can be defined in configuration
- b. Publisher and subscriber relationships can be specified externally
- c. Component roles (publisher/subscriber) can be assigned through configuration
-
-3. File Format and Structure: The configuration files use JSON format for readability and easy parsing:
-
-.. code-block:: json
+ static const std::map kDiagBitMap = {
+ {SvtFlag::kSynchronized, 0x01U},
+ {SvtFlag::kTimeOut, 0x02U},
+ {SvtFlag::kTimeLeapFuture, 0x04U},
+ {SvtFlag::kTimeLeapPast, 0x08U},
+ {SvtFlag::kUnknown, 0x80U},
+ };
+ uint8_t BuildDiagByte(const score::time::VehicleTimeStatus& status)
{
- "message_broker": {
- "topics": [
- {
- "name": "raw_ptp_data",
- "publishers": ["PtpMachine"],
- "subscribers": ["ControlFlowDivider"]
- },
- {
- "name": "input_ptp_data",
- "publishers": ["ControlFlowDivider"],
- "subscribers": ["VerificationMachine"]
- },
- {
- "name": "verified_ptp_data",
- "publishers": ["VerificationMachine"],
- "subscribers": ["IPCMachine"]
- }
- ]
- },
- "ptp_machine": {
- "update_interval_ms": 50,
- "ptp_stack_type": "ptp",
- "ptp_stack_parameters": {
- "device": "/dev/ptp0"
- }
- },
- "control_flow_divider": {
- "timeout_ms": 500,
- "publishing_rate_ms": 100
- },
- "verification_machine": {
- "validation_stages": ["synchronization", "timejumps", "timeout"],
- "timejumps_parameters": {
- "max_backward_jump_ns": 100000
- },
- "timeout_parameters": {
- "threshold_ns": 100000
+ uint8_t result{0U};
+ for (const auto& entry : kDiagBitMap) {
+ if (status.IsFlagActive(entry.first)) {
+ result |= entry.second;
+ }
}
- },
- "ipc_machine": {
- "shared_memory_name": "vehicle_time",
- "shared_memory_size": 4096
- }
+ return result;
}
-Scalability
-^^^^^^^^^^^
-
-The ``TimeDaemon``'s architecture supports scalability in the following ways:
-
-Component Extensibility:
-''''''''''''''''''''''''
-
-1. New machine components can be added by implementing the ``BaseMachine`` interface
-2. Additional validation stages can be plugged into the ``VerificationMachine`` pipeline
-3. Alternative IPC mechanisms or communication with ptp stack can be implemented by alternative the ``IPCMachine`` or ``PTPMachine`` implementation
-
-Example based on Qualified Vehicle Time integration
-'''''''''''''''''''''''''''''''''''''''''''''''''''
-
-The ``Qualified Vehicle Time`` integration extends the standard ``TimeDaemon`` architecture with:
-
-1. A ``Qualified Vehicle Time`` component that performs additional time qualification and provide new topics: ``qualified_ptp_data`` and ``diagnostic_sct_data``
-2. A dedicated IPC channel for SCT diagnostic data
-3. A ``score::time::qvt`` library for diagnostic applications
+UC5 — HPLSC as reference steady clock
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.. raw:: html
-
-
-
-.. uml:: _assets/examples/qvt/qvt_deployment.puml
- :alt: Deployment view
-
-.. raw:: html
+``HplsClock`` (High-Precision Local Steady Clock) is a monotonic clock that provides
+nanosecond-resolution time without any PTP dependency. It has **no status** — it is always
+ready.
-
-
-The ``Qualified Vehicle Time`` component is integrated into the existing processing pipeline:
-
-1. It subscribes to the `verified_ptp_data <#verified-ptp-data>`_ topic from the ``VerificationMachine``
-2. It processes and qualifies the time data with additional QVT-specific checks
-3. It publishes two types of data:
+.. code-block:: cpp
- a. Qualified time data to the standard IPC Machine towards clients interested in the qualified Vehicle Time
- b. Diagnostic data to a dedicated QVT IPC channel towards Diagnostic and Central Validator notifications
-
-The extended data flow with Qualified Vehicle Time integration is shown below:
-
-.. raw:: html
+ #include "score/time/hpls_time/hpls_clock.h"
+ #include
-
-
-.. uml:: _assets/examples/qvt/qvt_data_control_flow.puml
- :alt: Data flow
-
-.. raw:: html
-
-
-
-Example based on Absolute Time integration
-''''''''''''''''''''''''''''''''''''''''''
-
-Another example of the ``TimeDaemon`` extension is the integration of an ``Absolute Time`` source, such as GNSS, to provide absolute time information alongside the relative Vehicle Time from PTP.
-
-The ``Absolute Time`` integration extends the standard ``TimeDaemon`` architecture with:
-
-1. An ``SDatMachine`` component that retrieves absolute time from GNSS via SOMEIP or other sources and provide new topics: ``absolute_time_data``
-2. A dedicated verification stage in the ``VerificationMachine`` for Absolute Time qualification
-3. A dedicated IPC channel for Absolute Time data
-4. A ``score::time::abs`` library for applications requiring absolute time on Clients side.
-
-The way how it is integrated is presented below.
-
-.. raw:: html
-
-
-
-.. uml:: _assets/examples/abs_time/abs_time_deployment.puml
- :alt: Data flow
-
-.. raw:: html
-
-
-
-
-The control and data flow with Absolute Time integration is shown below.
-
-.. raw:: html
-
-
-
-.. uml:: _assets/examples/abs_time/abs_time_data_control_flow.puml
- :alt: Data flow
-
-.. raw:: html
-
-
-
-Using in test environment
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Using in ITF
-^^^^^^^^^^^^
-
-Normal behavior is expected.
-
-Using in Component Tests on the host
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Overview
-''''''''
-
-The ``TimeDaemon`` can be utilized in the ``Component Tests`` environment to enable comprehensive testing of time-dependent components without relying on physical PTP hardware.
-This approach allows test cases to manipulate time values and synchronization states to validate application behavior under various timing conditions.
-
-For the Component tests the ``PtpMachine::PtpEngine`` library is the only one platform-dependent.
-Thus the ``TimeDaemon`` components remain largely unchanged except for the ``PTPMachine`` component, which is replaced with an test-specific implementation that can be controlled via test cases
-This component shall:
-
-1. simulate "normal" ``PTPMachine`` behavior
-2. have the communication channel to the test case and react on the manipulations
-
-Next steps: plugin system
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+ void MyValidator::CheckDeadline()
+ {
+ auto hpls = score::time::HplsClock::GetInstance();
+ auto deadline = hpls.Now().TimePoint() + std::chrono::seconds{3};
-The ``TimeDaemon`` could be extended with a flexible plugin system that enables dynamic component loading, configuration, subscription and extension without requiring code changes or recompilation.
+ // ... do work ...
-Plugin Architecture
-^^^^^^^^^^^^^^^^^^^
+ if (hpls.Now().TimePoint() > deadline) {
+ HandleDeadlineExceeded();
+ }
+ }
-The plugin system is structured around the following key elements:
+Bazel dependencies
+------------------
-1. ``Component Registry``: A central registry that maintains information about available component implementations
-2. ``Component Factory``: Creates component instances based on configuration
-3. ``Plugin Manager``: Loads and initializes plugins at runtime
-4. ``Configuration-Driven Assembly``: Components and their relationships defined in configuration files
+Choose the target that matches your use case:
-Component Creation Process
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
+.. list-table::
+ :header-rows: 1
+ :widths: 55 45
+
+ * - Target
+ - When to use
+ * - ``//score/time/vehicle_time:vehicle_time``
+ - Production binary — includes real PTP backend
+ * - ``//score/time/vehicle_time:vehicle_time_mock``
+ - Unit test — ``VehicleClockMock`` + scope-bound override or constructor injection
+ * - ``//score/time/vehicle_time:interface``
+ - Header-only, no backend — interface/type usage only
+ * - ``//score/time/hpls_time:hpls_time``
+ - Production binary — HPLS steady clock
+ * - ``//score/time/hpls_time:hpls_time_mock``
+ - Unit test — ``HplsClockMock`` + scope-bound override or constructor injection
+ * - ``//score/time/hpls_time:interface``
+ - Header-only
+ * - ``//score/time/steady_time:steady_time``
+ - ``std::chrono::steady_clock`` wrapper
+ * - ``//score/time/system_time:system_time``
+ - ``std::chrono::system_clock`` wrapper
+ * - ``//score/time/ptp:ptp_types``
+ - PTP notification data types (for callbacks)
+
+Design decisions
+----------------
+
+No factory classes
+~~~~~~~~~~~~~~~~~~
+
+The old ``SynchronizedVehicleTime`` API required clients to instantiate factory objects
+directly. ``score::time`` removes all factory nesting: ``Clock::GetInstance()`` is
+the sole entry point, and the production backend is chosen at **link time** by the Bazel
+alias target.
+
+Opacity of ``details/``
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Virtual dispatch exists solely to enable GMock test doubles. The vtable is hidden inside
+``details/`` — public headers never declare a virtual function. ``Clock`` is a plain
+value type. The ``*_mock.h`` headers are the only public headers permitted to include
+``details/`` internals.
+
+``ClockSnapshot`` replaces nested ``TimeStatus``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The old ``SynchronizedVehicleTime::TimeStatus`` had public mutable data members, raw-integer
+constructors, and non-const accessors. ``ClockSnapshot`` is a simple
+immutable two-field struct:
+
+.. code-block:: cpp
+
+ auto snap = VehicleClock::GetInstance().Now();
+ snap.TimePoint(); // std::chrono::time_point
+ snap.Status(); // VehicleTimeStatus — const reference
+
+Generic code works for all clock domains:
+
+.. code-block:: cpp
+
+ template
+ auto Age(score::time::Clock& clk,
+ typename score::time::Clock::time_point ref)
+ {
+ return clk.Now().TimePoint() - ref;
+ }
-During ``TimeDaemon`` initialization:
+``Subscribe`` collapses four named callback methods
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-1. The ``Plugin Manager`` loads all specified plugins from configured directories or bazel targets
-2. Each plugin registers its component factories with the registry
-3. The ``Application`` reads the component configuration
-4. For each component in the configuration:
+The old API required four separate named methods (``Set*``, ``Unset*`` for each event type).
+The ``Clock`` wrapper exposes a single pair ``Subscribe()`` / ``Unsubscribe()``.
+The ``SubscriptionHook`` specialisation bridges to the named virtual methods
+on the backend interface — which must remain non-template (C++ forbids virtual templates).
- a. The appropriate factory is retrieved from the registry
- b. The component is created with its specified parameters
- c. Components are connected based on the ``MessageBroker`` topic configuration
+Extending with a new clock domain
+----------------------------------
-ASIL-B qualification
-~~~~~~~~~~~~~~~~~~~~~
+Adding a new time domain (e.g. ``SdatTime``) requires only new files — no existing file
+is modified:
-Clean separation of concerns allows ``score::time::svt`` as well as ``TimeDaemon`` to be qualified according to ASIL-B requirements following ISO 26262 standard.
+1. Create ``score/time/sdat_time/sdat_time.h`` — tag struct with ``Duration`` and ``Timepoint``.
+2. Create ``score/time/sdat_time/details/sdat_time_iface.h`` — pure-virtual backend interface.
+3. Create ``score/time/sdat_time/details/sdat_prod_impl.cpp`` — production backend.
+4. Add ``ClockTraits`` specialisation in ``score/time/sdat_time/sdat_clock.h``.
+5. Create ``score/time/sdat_time/sdat_clock_mock.h`` — GMock test double.
+6. Add ``sdat_time``, ``sdat_time_mock``, ``interface`` aliases in ``score/time/sdat_time/BUILD``.
diff --git a/examples/time/BUILD b/examples/time/BUILD
new file mode 100644
index 0000000..fe87ebd
--- /dev/null
+++ b/examples/time/BUILD
@@ -0,0 +1,33 @@
+# *******************************************************************************
+# Copyright (c) 2026 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Apache License Version 2.0 which is available at
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# SPDX-License-Identifier: Apache-2.0
+# *******************************************************************************
+
+load("@score_baselibs//:bazel/unit_tests.bzl", "cc_unit_test_suites_for_host_and_qnx")
+
+# Aggregates the unit_test_suite targets from all example time-domain applications.
+#
+# Run all example tests with:
+# bazel test //examples/time:unit_test_suite_host
+#
+# Or run everything — library, TimeDaemon, and examples — together:
+# bazel test //score/time:unit_test_suite_host
+cc_unit_test_suites_for_host_and_qnx(
+ name = "unit_test_suite",
+ cc_unit_tests = [],
+ test_suites_from_sub_packages = [
+ "//examples/time/vehicle_time:unit_test_suite",
+ "//examples/time/hpls_time:unit_test_suite",
+ "//examples/time/steady_time:unit_test_suite",
+ "//examples/time/system_time:unit_test_suite",
+ ],
+ visibility = ["//visibility:public"],
+)
diff --git a/examples/time/hpls_time/BUILD b/examples/time/hpls_time/BUILD
new file mode 100644
index 0000000..f09db68
--- /dev/null
+++ b/examples/time/hpls_time/BUILD
@@ -0,0 +1,64 @@
+# *******************************************************************************
+# Copyright (c) 2026 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Apache License Version 2.0 which is available at
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# SPDX-License-Identifier: Apache-2.0
+# *******************************************************************************
+
+load("@score_baselibs//:bazel/unit_tests.bzl", "cc_unit_test_suites_for_host_and_qnx")
+load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES")
+
+# Library containing only the testable business-logic — no main(), no signal handling.
+cc_library(
+ name = "time_handler",
+ hdrs = ["hpls_time_handler.h"],
+ features = COMPILER_WARNING_FEATURES,
+ deps = [
+ "//score/time/hpls_time:interface",
+ ],
+)
+
+# Example binary: reads HplsTime once per second and prints the snapshot to stdout.
+#
+# Run with:
+# bazel run //examples/time/hpls_time
+#
+# HplsTime wraps a local monotonic hardware clock (high_resolution_clock on Linux,
+# ClockCycles() on QNX) and has no synchronisation-quality concept, so the snapshot
+# carries only a time-point.
+cc_binary(
+ name = "hpls_time",
+ srcs = ["main.cpp"],
+ features = COMPILER_WARNING_FEATURES,
+ deps = [
+ ":time_handler",
+ # Single entry point: brings in HplsClock (hpls_clock.h)
+ # AND the production HplsTime backend (CreateBackend()).
+ "//score/time/hpls_time",
+ ],
+)
+
+# Unit test for the time handler: replaces HplsClock with a mock.
+cc_test(
+ name = "hpls_time_handler_test",
+ srcs = ["hpls_time_handler_test.cpp"],
+ features = COMPILER_WARNING_FEATURES,
+ deps = [
+ ":time_handler",
+ "//score/time/hpls_time:hpls_time_mock",
+ "@googletest//:gtest",
+ "@googletest//:gtest_main",
+ ],
+)
+
+cc_unit_test_suites_for_host_and_qnx(
+ name = "unit_test_suite",
+ cc_unit_tests = [":hpls_time_handler_test"],
+ visibility = ["//examples/time:__pkg__"],
+)
diff --git a/examples/time/hpls_time/hpls_time_handler.h b/examples/time/hpls_time/hpls_time_handler.h
new file mode 100644
index 0000000..3edfa10
--- /dev/null
+++ b/examples/time/hpls_time/hpls_time_handler.h
@@ -0,0 +1,67 @@
+/********************************************************************************
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+#ifndef EXAMPLES_TIME_HPLS_TIME_HPLS_TIME_HANDLER_H
+#define EXAMPLES_TIME_HPLS_TIME_HPLS_TIME_HANDLER_H
+
+#include "score/time/hpls_time/hpls_clock.h"
+
+#include
+
+namespace examples
+{
+namespace time
+{
+namespace hpls_time
+{
+
+/// @brief A snapshot of the time report produced by HplsTimeHandler.
+struct TimeReport
+{
+ /// @brief Current HPLS monotonic time in nanoseconds since an unspecified epoch.
+ std::int64_t time_ns{0};
+};
+
+/// @brief Convenience wrapper that reads HplsClock in one call.
+///
+/// @par Testing pattern
+/// @code
+/// auto mock = std::make_shared();
+/// score::time::test_utils::ScopedClockOverride guard{mock};
+/// EXPECT_CALL(*mock, Now()).WillOnce(Return(...));
+/// HplsTimeHandler handler;
+/// const auto report = handler.GetCurrentTime();
+/// @endcode
+class HplsTimeHandler
+{
+ public:
+ HplsTimeHandler() = default;
+ ~HplsTimeHandler() = default;
+
+ HplsTimeHandler(const HplsTimeHandler&) = delete;
+ HplsTimeHandler& operator=(const HplsTimeHandler&) = delete;
+ HplsTimeHandler(HplsTimeHandler&&) = delete;
+ HplsTimeHandler& operator=(HplsTimeHandler&&) = delete;
+
+ /// @brief Reads the current HPLS time and returns a report.
+ [[nodiscard]] TimeReport GetCurrentTime() const noexcept
+ {
+ const auto snapshot = score::time::HplsClock::GetInstance().Now();
+ return TimeReport{snapshot.TimePointNs().count()};
+ }
+};
+
+} // namespace hpls_time
+} // namespace time
+} // namespace examples
+
+#endif // EXAMPLES_TIME_HPLS_TIME_HPLS_TIME_HANDLER_H
diff --git a/examples/time/hpls_time/hpls_time_handler_test.cpp b/examples/time/hpls_time/hpls_time_handler_test.cpp
new file mode 100644
index 0000000..db1f2e0
--- /dev/null
+++ b/examples/time/hpls_time/hpls_time_handler_test.cpp
@@ -0,0 +1,79 @@
+/********************************************************************************
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+#include "examples/time/hpls_time/hpls_time_handler.h"
+
+#include "score/time/hpls_time/hpls_clock_mock.h"
+#include "score/time/clock/scoped_clock_override.h"
+#include "score/time/clock/clock_snapshot.h"
+#include "score/time/clock/no_status.h"
+
+#include
+#include
+
+#include
+#include
+
+using ::testing::Return;
+
+namespace examples
+{
+namespace time
+{
+namespace hpls_time
+{
+namespace test
+{
+
+class HplsTimeHandlerTest : public ::testing::Test
+{
+ protected:
+ HplsTimeHandlerTest()
+ : mock_{std::make_shared()}
+ , guard_{mock_}
+ {
+ }
+
+ std::shared_ptr mock_;
+ score::time::test_utils::ScopedClockOverride guard_;
+};
+
+TEST_F(HplsTimeHandlerTest, ReportContainsTimePointFromMock)
+{
+ const score::time::HplsTime::Timepoint tp{std::chrono::nanoseconds{7'654'321'000LL}};
+ EXPECT_CALL(*mock_, Now()).WillOnce(Return(
+ score::time::ClockSnapshot{
+ tp, score::time::NoStatus{}}));
+
+ HplsTimeHandler handler;
+ const TimeReport report = handler.GetCurrentTime();
+
+ EXPECT_EQ(report.time_ns, 7'654'321'000LL);
+}
+
+TEST_F(HplsTimeHandlerTest, ReportContainsZeroForEpochTimepoint)
+{
+ const score::time::HplsTime::Timepoint tp{std::chrono::nanoseconds{0}};
+ EXPECT_CALL(*mock_, Now()).WillOnce(Return(
+ score::time::ClockSnapshot{
+ tp, score::time::NoStatus{}}));
+
+ HplsTimeHandler handler;
+ const TimeReport report = handler.GetCurrentTime();
+
+ EXPECT_EQ(report.time_ns, 0LL);
+}
+
+} // namespace test
+} // namespace hpls_time
+} // namespace time
+} // namespace examples
diff --git a/examples/time/hpls_time/main.cpp b/examples/time/hpls_time/main.cpp
new file mode 100644
index 0000000..b553880
--- /dev/null
+++ b/examples/time/hpls_time/main.cpp
@@ -0,0 +1,85 @@
+/********************************************************************************
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+/**
+ * @file
+ * @brief Example: periodic HplsTime report printer.
+ *
+ * Uses HplsTimeHandler to obtain the current high-precision local steady time
+ * and prints it to stdout once per second until interrupted by SIGINT or SIGTERM.
+ *
+ * The handler class can be unit-tested in isolation — see time_handler_test.cpp.
+ */
+
+#include "examples/time/hpls_time/hpls_time_handler.h"
+
+#include
+#include
+#include
+#include
+#include
+
+namespace
+{
+
+/** @brief Flag set by the signal handler to request a clean shutdown. */
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+volatile std::sig_atomic_t gShutdownRequested{0};
+
+/** @brief Signal handler for SIGINT / SIGTERM. */
+extern "C" void HandleSignal(int /*signal*/) noexcept
+{
+ gShutdownRequested = 1;
+}
+
+/**
+ * @brief Prints a single TimeReport to stdout.
+ *
+ * @param report The time report to format.
+ * @param seq Monotonic sequence number of this print.
+ */
+void PrintReport(const examples::time::hpls_time::TimeReport& report, std::uint64_t seq) noexcept
+{
+ const auto seconds = report.time_ns / 1'000'000'000LL;
+ const auto nanoseconds = report.time_ns % 1'000'000'000LL;
+
+ std::cout << "[" << seq << "]"
+ << " time=" << seconds << "." << nanoseconds << " s\n";
+}
+
+} // namespace
+
+int main()
+{
+ // Install signal handlers for clean shutdown.
+ static_cast(std::signal(SIGINT, HandleSignal));
+ static_cast(std::signal(SIGTERM, HandleSignal));
+
+ examples::time::hpls_time::HplsTimeHandler handler;
+
+ std::cout << "HplsTime printer started. Press Ctrl+C to stop.\n";
+
+ std::uint64_t seq{0U};
+
+ while (gShutdownRequested == 0)
+ {
+ const auto report = handler.GetCurrentTime();
+ PrintReport(report, seq);
+ ++seq;
+
+ std::this_thread::sleep_for(std::chrono::seconds{1});
+ }
+
+ std::cout << "Shutdown requested. Exiting.\n";
+ return 0;
+}
diff --git a/examples/time/steady_time/BUILD b/examples/time/steady_time/BUILD
new file mode 100644
index 0000000..cc51419
--- /dev/null
+++ b/examples/time/steady_time/BUILD
@@ -0,0 +1,64 @@
+# *******************************************************************************
+# Copyright (c) 2026 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Apache License Version 2.0 which is available at
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# SPDX-License-Identifier: Apache-2.0
+# *******************************************************************************
+
+load("@score_baselibs//:bazel/unit_tests.bzl", "cc_unit_test_suites_for_host_and_qnx")
+load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES")
+
+# Library containing only the testable business-logic — no main(), no signal handling.
+cc_library(
+ name = "time_handler",
+ hdrs = ["steady_time_handler.h"],
+ features = COMPILER_WARNING_FEATURES,
+ deps = [
+ "//score/time/steady_time:interface",
+ ],
+)
+
+# Example binary: reads SteadyTime once per second and prints the snapshot to stdout.
+#
+# Run with:
+# bazel run //examples/time/steady_time
+#
+# SteadyClock wraps std::chrono::steady_clock — a monotonic clock that measures
+# time relative to an unspecified epoch (typically system boot). The snapshot
+# carries only a time-point; there is no synchronisation status.
+cc_binary(
+ name = "steady_time",
+ srcs = ["main.cpp"],
+ features = COMPILER_WARNING_FEATURES,
+ deps = [
+ ":time_handler",
+ # Single entry point: brings in SteadyClock (steady_clock.h)
+ # AND the production SteadyTime backend (CreateBackend()).
+ "//score/time/steady_time",
+ ],
+)
+
+# Unit test for the time handler: replaces SteadyClock with a mock.
+cc_test(
+ name = "steady_time_handler_test",
+ srcs = ["steady_time_handler_test.cpp"],
+ features = COMPILER_WARNING_FEATURES,
+ deps = [
+ ":time_handler",
+ "//score/time/steady_time:steady_time_mock",
+ "@googletest//:gtest",
+ "@googletest//:gtest_main",
+ ],
+)
+
+cc_unit_test_suites_for_host_and_qnx(
+ name = "unit_test_suite",
+ cc_unit_tests = [":steady_time_handler_test"],
+ visibility = ["//examples/time:__pkg__"],
+)
diff --git a/examples/time/steady_time/main.cpp b/examples/time/steady_time/main.cpp
new file mode 100644
index 0000000..2412904
--- /dev/null
+++ b/examples/time/steady_time/main.cpp
@@ -0,0 +1,85 @@
+/********************************************************************************
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+/**
+ * @file
+ * @brief Example: periodic SteadyTime report printer.
+ *
+ * Uses SteadyTimeHandler to obtain the current monotonic time and prints it
+ * to stdout once per second until interrupted by SIGINT or SIGTERM.
+ *
+ * The handler class can be unit-tested in isolation — see time_handler_test.cpp.
+ */
+
+#include "examples/time/steady_time/steady_time_handler.h"
+
+#include
+#include
+#include
+#include
+#include
+
+namespace
+{
+
+/** @brief Flag set by the signal handler to request a clean shutdown. */
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+volatile std::sig_atomic_t gShutdownRequested{0};
+
+/** @brief Signal handler for SIGINT / SIGTERM. */
+extern "C" void HandleSignal(int /*signal*/) noexcept
+{
+ gShutdownRequested = 1;
+}
+
+/**
+ * @brief Prints a single TimeReport to stdout.
+ *
+ * @param report The time report to format.
+ * @param seq Monotonic sequence number of this print.
+ */
+void PrintReport(const examples::time::steady_time::TimeReport& report, std::uint64_t seq) noexcept
+{
+ const auto seconds = report.monotonic_ns / 1'000'000'000LL;
+ const auto nanoseconds = report.monotonic_ns % 1'000'000'000LL;
+
+ std::cout << "[" << seq << "]"
+ << " monotonic=" << seconds << "." << nanoseconds << " s\n";
+}
+
+} // namespace
+
+int main()
+{
+ // Install signal handlers for clean shutdown.
+ static_cast(std::signal(SIGINT, HandleSignal));
+ static_cast(std::signal(SIGTERM, HandleSignal));
+
+ examples::time::steady_time::SteadyTimeHandler handler;
+
+ std::cout << "SteadyTime printer started. Press Ctrl+C to stop.\n";
+
+ std::uint64_t seq{0U};
+
+ while (gShutdownRequested == 0)
+ {
+ const auto report = handler.GetCurrentTime();
+ PrintReport(report, seq);
+ ++seq;
+
+ std::this_thread::sleep_for(std::chrono::seconds{1});
+ }
+
+ std::cout << "Shutdown requested. Exiting.\n";
+ return 0;
+}
diff --git a/examples/time/steady_time/steady_time_handler.h b/examples/time/steady_time/steady_time_handler.h
new file mode 100644
index 0000000..5b52851
--- /dev/null
+++ b/examples/time/steady_time/steady_time_handler.h
@@ -0,0 +1,67 @@
+/********************************************************************************
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+#ifndef EXAMPLES_TIME_STEADY_TIME_STEADY_TIME_HANDLER_H
+#define EXAMPLES_TIME_STEADY_TIME_STEADY_TIME_HANDLER_H
+
+#include "score/time/steady_time/steady_clock.h"
+
+#include
+
+namespace examples
+{
+namespace time
+{
+namespace steady_time
+{
+
+/// @brief A snapshot of the time report produced by SteadyTimeHandler.
+struct TimeReport
+{
+ /// @brief Current monotonic time in nanoseconds since an unspecified epoch (typically boot).
+ std::int64_t monotonic_ns{0};
+};
+
+/// @brief Convenience wrapper that reads SteadyClock in one call.
+///
+/// @par Testing pattern
+/// @code
+/// auto mock = std::make_shared();
+/// score::time::test_utils::ScopedClockOverride guard{mock};
+/// EXPECT_CALL(*mock, Now()).WillOnce(Return(...));
+/// SteadyTimeHandler handler;
+/// const auto report = handler.GetCurrentTime();
+/// @endcode
+class SteadyTimeHandler
+{
+ public:
+ SteadyTimeHandler() = default;
+ ~SteadyTimeHandler() = default;
+
+ SteadyTimeHandler(const SteadyTimeHandler&) = delete;
+ SteadyTimeHandler& operator=(const SteadyTimeHandler&) = delete;
+ SteadyTimeHandler(SteadyTimeHandler&&) = delete;
+ SteadyTimeHandler& operator=(SteadyTimeHandler&&) = delete;
+
+ /// @brief Reads the current steady time and returns a report.
+ [[nodiscard]] TimeReport GetCurrentTime() const noexcept
+ {
+ const auto snapshot = score::time::SteadyClock::GetInstance().Now();
+ return TimeReport{snapshot.TimePointNs().count()};
+ }
+};
+
+} // namespace steady_time
+} // namespace time
+} // namespace examples
+
+#endif // EXAMPLES_TIME_STEADY_TIME_STEADY_TIME_HANDLER_H
diff --git a/examples/time/steady_time/steady_time_handler_test.cpp b/examples/time/steady_time/steady_time_handler_test.cpp
new file mode 100644
index 0000000..0a0442c
--- /dev/null
+++ b/examples/time/steady_time/steady_time_handler_test.cpp
@@ -0,0 +1,79 @@
+/********************************************************************************
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+#include "examples/time/steady_time/steady_time_handler.h"
+
+#include "score/time/steady_time/steady_clock_mock.h"
+#include "score/time/clock/scoped_clock_override.h"
+#include "score/time/clock/clock_snapshot.h"
+#include "score/time/clock/no_status.h"
+
+#include
+#include
+
+#include
+#include
+
+using ::testing::Return;
+
+namespace examples
+{
+namespace time
+{
+namespace steady_time
+{
+namespace test
+{
+
+class SteadyTimeHandlerTest : public ::testing::Test
+{
+ protected:
+ SteadyTimeHandlerTest()
+ : mock_{std::make_shared()}
+ , guard_{mock_}
+ {
+ }
+
+ std::shared_ptr mock_;
+ score::time::test_utils::ScopedClockOverride guard_;
+};
+
+TEST_F(SteadyTimeHandlerTest, ReportContainsMonotonicTimeFromMock)
+{
+ const std::chrono::steady_clock::time_point tp{std::chrono::nanoseconds{3'000'000'000LL}};
+ EXPECT_CALL(*mock_, Now()).WillOnce(Return(
+ score::time::ClockSnapshot{
+ tp, score::time::NoStatus{}}));
+
+ SteadyTimeHandler handler;
+ const TimeReport report = handler.GetCurrentTime();
+
+ EXPECT_EQ(report.monotonic_ns, 3'000'000'000LL);
+}
+
+TEST_F(SteadyTimeHandlerTest, ReportContainsZeroForEpochTimepoint)
+{
+ const std::chrono::steady_clock::time_point tp{std::chrono::nanoseconds{0}};
+ EXPECT_CALL(*mock_, Now()).WillOnce(Return(
+ score::time::ClockSnapshot{
+ tp, score::time::NoStatus{}}));
+
+ SteadyTimeHandler handler;
+ const TimeReport report = handler.GetCurrentTime();
+
+ EXPECT_EQ(report.monotonic_ns, 0LL);
+}
+
+} // namespace test
+} // namespace steady_time
+} // namespace time
+} // namespace examples
diff --git a/examples/time/system_time/BUILD b/examples/time/system_time/BUILD
new file mode 100644
index 0000000..b8c4cea
--- /dev/null
+++ b/examples/time/system_time/BUILD
@@ -0,0 +1,63 @@
+# *******************************************************************************
+# Copyright (c) 2026 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Apache License Version 2.0 which is available at
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# SPDX-License-Identifier: Apache-2.0
+# *******************************************************************************
+
+load("@score_baselibs//:bazel/unit_tests.bzl", "cc_unit_test_suites_for_host_and_qnx")
+load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES")
+
+# Library containing only the testable business-logic — no main(), no signal handling.
+cc_library(
+ name = "time_handler",
+ hdrs = ["system_time_handler.h"],
+ features = COMPILER_WARNING_FEATURES,
+ deps = [
+ "//score/time/system_time:interface",
+ ],
+)
+
+# Example binary: reads SystemTime once per second and prints the snapshot to stdout.
+#
+# Run with:
+# bazel run //examples/time/system_time
+#
+# SystemClock wraps std::chrono::system_clock — the system wall-clock (Unix epoch).
+# The snapshot carries only a time-point; there is no synchronisation status.
+cc_binary(
+ name = "system_time",
+ srcs = ["main.cpp"],
+ features = COMPILER_WARNING_FEATURES,
+ deps = [
+ ":time_handler",
+ # Single entry point: brings in SystemClock (system_clock.h)
+ # AND the production SystemTime backend (CreateBackend()).
+ "//score/time/system_time",
+ ],
+)
+
+# Unit test for the time handler: replaces SystemClock with a mock.
+cc_test(
+ name = "system_time_handler_test",
+ srcs = ["system_time_handler_test.cpp"],
+ features = COMPILER_WARNING_FEATURES,
+ deps = [
+ ":time_handler",
+ "//score/time/system_time:system_time_mock",
+ "@googletest//:gtest",
+ "@googletest//:gtest_main",
+ ],
+)
+
+cc_unit_test_suites_for_host_and_qnx(
+ name = "unit_test_suite",
+ cc_unit_tests = [":system_time_handler_test"],
+ visibility = ["//examples/time:__pkg__"],
+)
diff --git a/examples/time/system_time/main.cpp b/examples/time/system_time/main.cpp
new file mode 100644
index 0000000..760d568
--- /dev/null
+++ b/examples/time/system_time/main.cpp
@@ -0,0 +1,85 @@
+/********************************************************************************
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+/**
+ * @file
+ * @brief Example: periodic SystemTime report printer.
+ *
+ * Uses SystemTimeHandler to obtain the current wall-clock (Unix epoch) time
+ * and prints it to stdout once per second until interrupted by SIGINT or SIGTERM.
+ *
+ * The handler class can be unit-tested in isolation — see time_handler_test.cpp.
+ */
+
+#include "examples/time/system_time/system_time_handler.h"
+
+#include
+#include
+#include
+#include
+#include
+
+namespace
+{
+
+/** @brief Flag set by the signal handler to request a clean shutdown. */
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+volatile std::sig_atomic_t gShutdownRequested{0};
+
+/** @brief Signal handler for SIGINT / SIGTERM. */
+extern "C" void HandleSignal(int /*signal*/) noexcept
+{
+ gShutdownRequested = 1;
+}
+
+/**
+ * @brief Prints a single TimeReport to stdout.
+ *
+ * @param report The time report to format.
+ * @param seq Monotonic sequence number of this print.
+ */
+void PrintReport(const examples::time::system_time::TimeReport& report, std::uint64_t seq) noexcept
+{
+ const auto seconds = report.unix_ns / 1'000'000'000LL;
+ const auto nanoseconds = report.unix_ns % 1'000'000'000LL;
+
+ std::cout << "[" << seq << "]"
+ << " unix=" << seconds << "." << nanoseconds << " s\n";
+}
+
+} // namespace
+
+int main()
+{
+ // Install signal handlers for clean shutdown.
+ static_cast(std::signal(SIGINT, HandleSignal));
+ static_cast(std::signal(SIGTERM, HandleSignal));
+
+ examples::time::system_time::SystemTimeHandler handler;
+
+ std::cout << "SystemTime printer started. Press Ctrl+C to stop.\n";
+
+ std::uint64_t seq{0U};
+
+ while (gShutdownRequested == 0)
+ {
+ const auto report = handler.GetCurrentTime();
+ PrintReport(report, seq);
+ ++seq;
+
+ std::this_thread::sleep_for(std::chrono::seconds{1});
+ }
+
+ std::cout << "Shutdown requested. Exiting.\n";
+ return 0;
+}
diff --git a/examples/time/system_time/system_time_handler.h b/examples/time/system_time/system_time_handler.h
new file mode 100644
index 0000000..65abb40
--- /dev/null
+++ b/examples/time/system_time/system_time_handler.h
@@ -0,0 +1,67 @@
+/********************************************************************************
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+#ifndef EXAMPLES_TIME_SYSTEM_TIME_SYSTEM_TIME_HANDLER_H
+#define EXAMPLES_TIME_SYSTEM_TIME_SYSTEM_TIME_HANDLER_H
+
+#include "score/time/system_time/system_clock.h"
+
+#include
+
+namespace examples
+{
+namespace time
+{
+namespace system_time
+{
+
+/// @brief A snapshot of the time report produced by SystemTimeHandler.
+struct TimeReport
+{
+ /// @brief Current wall-clock (Unix epoch) time in nanoseconds.
+ std::int64_t unix_ns{0};
+};
+
+/// @brief Convenience wrapper that reads SystemClock in one call.
+///
+/// @par Testing pattern
+/// @code
+/// auto mock = std::make_shared();
+/// score::time::test_utils::ScopedClockOverride guard{mock};
+/// EXPECT_CALL(*mock, Now()).WillOnce(Return(...));
+/// SystemTimeHandler handler;
+/// const auto report = handler.GetCurrentTime();
+/// @endcode
+class SystemTimeHandler
+{
+ public:
+ SystemTimeHandler() = default;
+ ~SystemTimeHandler() = default;
+
+ SystemTimeHandler(const SystemTimeHandler&) = delete;
+ SystemTimeHandler& operator=(const SystemTimeHandler&) = delete;
+ SystemTimeHandler(SystemTimeHandler&&) = delete;
+ SystemTimeHandler& operator=(SystemTimeHandler&&) = delete;
+
+ /// @brief Reads the current system (wall-clock) time and returns a report.
+ [[nodiscard]] TimeReport GetCurrentTime() const noexcept
+ {
+ const auto snapshot = score::time::SystemClock::GetInstance().Now();
+ return TimeReport{snapshot.TimePointNs().count()};
+ }
+};
+
+} // namespace system_time
+} // namespace time
+} // namespace examples
+
+#endif // EXAMPLES_TIME_SYSTEM_TIME_SYSTEM_TIME_HANDLER_H
diff --git a/examples/time/system_time/system_time_handler_test.cpp b/examples/time/system_time/system_time_handler_test.cpp
new file mode 100644
index 0000000..522995c
--- /dev/null
+++ b/examples/time/system_time/system_time_handler_test.cpp
@@ -0,0 +1,81 @@
+/********************************************************************************
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+#include "examples/time/system_time/system_time_handler.h"
+
+#include "score/time/system_time/system_clock_mock.h"
+#include "score/time/clock/scoped_clock_override.h"
+#include "score/time/clock/clock_snapshot.h"
+#include "score/time/clock/no_status.h"
+
+#include
+#include
+
+#include
+#include
+
+using ::testing::Return;
+
+namespace examples
+{
+namespace time
+{
+namespace system_time
+{
+namespace test
+{
+
+class SystemTimeHandlerTest : public ::testing::Test
+{
+ protected:
+ SystemTimeHandlerTest()
+ : mock_{std::make_shared()}
+ , guard_{mock_}
+ {
+ }
+
+ std::shared_ptr mock_;
+ score::time::test_utils::ScopedClockOverride guard_;
+};
+
+TEST_F(SystemTimeHandlerTest, ReportContainsUnixTimeFromMock)
+{
+ // 2026-01-01 00:00:00 UTC in nanoseconds since Unix epoch
+ constexpr std::int64_t kYear2026Ns = 1'767'225'600LL * 1'000'000'000LL;
+ const std::chrono::system_clock::time_point tp{std::chrono::nanoseconds{kYear2026Ns}};
+ EXPECT_CALL(*mock_, Now()).WillOnce(Return(
+ score::time::ClockSnapshot{
+ tp, score::time::NoStatus{}}));
+
+ SystemTimeHandler handler;
+ const TimeReport report = handler.GetCurrentTime();
+
+ EXPECT_EQ(report.unix_ns, kYear2026Ns);
+}
+
+TEST_F(SystemTimeHandlerTest, ReportContainsZeroForEpochTimepoint)
+{
+ const std::chrono::system_clock::time_point tp{std::chrono::nanoseconds{0}};
+ EXPECT_CALL(*mock_, Now()).WillOnce(Return(
+ score::time::ClockSnapshot{
+ tp, score::time::NoStatus{}}));
+
+ SystemTimeHandler handler;
+ const TimeReport report = handler.GetCurrentTime();
+
+ EXPECT_EQ(report.unix_ns, 0LL);
+}
+
+} // namespace test
+} // namespace system_time
+} // namespace time
+} // namespace examples
diff --git a/examples/time/vehicle_time/BUILD b/examples/time/vehicle_time/BUILD
new file mode 100644
index 0000000..813753c
--- /dev/null
+++ b/examples/time/vehicle_time/BUILD
@@ -0,0 +1,74 @@
+# *******************************************************************************
+# Copyright (c) 2026 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Apache License Version 2.0 which is available at
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# SPDX-License-Identifier: Apache-2.0
+# *******************************************************************************
+
+load("@score_baselibs//:bazel/unit_tests.bzl", "cc_unit_test_suites_for_host_and_qnx")
+load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES")
+
+# Library containing only the testable business-logic — no main(), no signal handling.
+cc_library(
+ name = "time_handler",
+ hdrs = ["vehicle_time_handler.h"],
+ features = COMPILER_WARNING_FEATURES,
+ deps = [
+ "//score/time/hpls_time:interface",
+ "//score/time/vehicle_time:interface",
+ ],
+)
+
+# Example binary: reads VehicleTime + HplsTime once per second and prints a combined report.
+#
+# Run with:
+# bazel run //examples/time/vehicle_time
+#
+# The production VehicleTime backend (TimeDaemon TDR receiver) is pulled in via
+# //score/time/vehicle_time:vehicle_time. On a development host that backend will
+# return zero-valued snapshots with kTimeOut set — which is the expected behaviour
+# when the TimeDaemon is not running.
+cc_binary(
+ name = "vehicle_time",
+ srcs = ["main.cpp"],
+ features = COMPILER_WARNING_FEATURES,
+ deps = [
+ ":time_handler",
+ # Single entry point: brings in VehicleClock (vehicle_clock.h)
+ # AND the production VehicleTime backend (CreateBackend()).
+ "//score/time/vehicle_time",
+ # VehicleTime's production backend uses HplsClock internally.
+ # Provide its production backend so CreateBackend() resolves at link time.
+ "//score/time/hpls_time",
+ # mw::log frontend is pulled in transitively via td_impl.
+ # A binary must link exactly one concrete recorder implementation.
+ "@score_baselibs//score/mw/log:console_only_backend",
+ ],
+)
+
+# Unit test for the time handler: replaces both VehicleClock and HplsClock with mocks.
+cc_test(
+ name = "vehicle_time_handler_test",
+ srcs = ["vehicle_time_handler_test.cpp"],
+ features = COMPILER_WARNING_FEATURES,
+ deps = [
+ ":time_handler",
+ "//score/time/hpls_time:hpls_time_mock",
+ "//score/time/vehicle_time:vehicle_time_mock",
+ "@googletest//:gtest",
+ "@googletest//:gtest_main",
+ "@score_baselibs//score/mw/log:console_only_backend",
+ ],
+)
+
+cc_unit_test_suites_for_host_and_qnx(
+ name = "unit_test_suite",
+ cc_unit_tests = [":vehicle_time_handler_test"],
+ visibility = ["//examples/time:__pkg__"],
+)
diff --git a/examples/time/vehicle_time/main.cpp b/examples/time/vehicle_time/main.cpp
new file mode 100644
index 0000000..56b49fb
--- /dev/null
+++ b/examples/time/vehicle_time/main.cpp
@@ -0,0 +1,93 @@
+/********************************************************************************
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+/**
+ * @file
+ * @brief Example: periodic VehicleTime + HplsTime report printer.
+ *
+ * Uses VehicleTimeHandler to obtain a combined snapshot of the PTP-synchronized
+ * vehicle time and the local monotonic HPLS time, then prints both to stdout
+ * once per second until interrupted by SIGINT or SIGTERM.
+ *
+ * The handler class can be unit-tested in isolation — see time_handler_test.cpp.
+ */
+
+#include "examples/time/vehicle_time/vehicle_time_handler.h"
+
+#include
+#include
+#include
+#include
+#include
+
+namespace
+{
+
+/** @brief Flag set by the signal handler to request a clean shutdown. */
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+volatile std::sig_atomic_t gShutdownRequested{0};
+
+/** @brief Signal handler for SIGINT / SIGTERM. */
+extern "C" void HandleSignal(int /*signal*/) noexcept
+{
+ gShutdownRequested = 1;
+}
+
+/**
+ * @brief Prints a single TimeReport to stdout.
+ *
+ * @param report The combined time report to format.
+ * @param seq Monotonic sequence number of this print.
+ */
+void PrintReport(const examples::time::vehicle_time::TimeReport& report, std::uint64_t seq) noexcept
+{
+ const auto v_sec = report.vehicle_time_ns / 1'000'000'000LL;
+ const auto v_ns = report.vehicle_time_ns % 1'000'000'000LL;
+ const auto h_sec = report.hpls_time_ns / 1'000'000'000LL;
+ const auto h_ns = report.hpls_time_ns % 1'000'000'000LL;
+
+ std::cout << "[" << seq << "]"
+ << " vehicle=" << v_sec << "." << v_ns << " s"
+ << " hpls=" << h_sec << "." << h_ns << " s"
+ << " synchronized=" << (report.synchronized ? "yes" : "no")
+ << " valid=" << (report.valid ? "yes" : "no")
+ << " rate_deviation=" << report.rate_deviation
+ << "\n";
+}
+
+} // namespace
+
+int main()
+{
+ // Install signal handlers for clean shutdown.
+ static_cast(std::signal(SIGINT, HandleSignal));
+ static_cast(std::signal(SIGTERM, HandleSignal));
+
+ examples::time::vehicle_time::VehicleTimeHandler handler;
+
+ std::cout << "VehicleTime + HplsTime printer started. Press Ctrl+C to stop.\n";
+
+ std::uint64_t seq{0U};
+
+ while (gShutdownRequested == 0)
+ {
+ const auto report = handler.GetCurrentTime();
+ PrintReport(report, seq);
+ ++seq;
+
+ std::this_thread::sleep_for(std::chrono::seconds{1});
+ }
+
+ std::cout << "Shutdown requested. Exiting.\n";
+ return 0;
+}
diff --git a/examples/time/vehicle_time/vehicle_time_handler.h b/examples/time/vehicle_time/vehicle_time_handler.h
new file mode 100644
index 0000000..48b1b5c
--- /dev/null
+++ b/examples/time/vehicle_time/vehicle_time_handler.h
@@ -0,0 +1,97 @@
+/********************************************************************************
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+#ifndef EXAMPLES_TIME_VEHICLE_TIME_VEHICLE_TIME_HANDLER_H
+#define EXAMPLES_TIME_VEHICLE_TIME_VEHICLE_TIME_HANDLER_H
+
+#include "score/time/vehicle_time/vehicle_clock.h"
+#include "score/time/hpls_time/hpls_clock.h"
+
+#include
+
+namespace examples
+{
+namespace time
+{
+namespace vehicle_time
+{
+
+/// @brief A snapshot of the combined time report produced by VehicleTimeHandler.
+struct TimeReport
+{
+ /// @brief Current vehicle (PTP-synchronized) time, nanoseconds since VehicleTime epoch.
+ std::int64_t vehicle_time_ns{0};
+
+ /// @brief Current local monotonic time from HplsClock, nanoseconds since process boot.
+ /// Provided as a local-time reference alongside the network-synchronized vehicle time.
+ std::int64_t hpls_time_ns{0};
+
+ /// @brief True if the vehicle time is currently synchronized to the PTP grand master.
+ bool synchronized{false};
+
+ /// @brief True if the vehicle time is in a valid (non-error) state.
+ bool valid{false};
+
+ /// @brief Fractional rate deviation of the local clock relative to the grand master.
+ /// Unit: dimensionless (e.g. 1.0e-9 == 1 ppb). Zero when not synchronized.
+ double rate_deviation{0.0};
+};
+
+/// @brief Convenience wrapper that reads VehicleClock and HplsClock in one call.
+///
+/// This class demonstrates how to write a component that depends on two different
+/// time bases. In unit tests, both clocks can be replaced independently via
+/// ScopedClockOverride, without any special constructor injection.
+///
+/// @par Testing pattern
+/// @code
+/// auto vehicle_mock = std::make_shared();
+/// auto hpls_mock = std::make_shared();
+/// score::time::test_utils::ScopedClockOverride vg{vehicle_mock};
+/// score::time::test_utils::ScopedClockOverride hg{hpls_mock};
+/// EXPECT_CALL(*vehicle_mock, Now()).WillOnce(Return(...));
+/// EXPECT_CALL(*hpls_mock, Now()).WillOnce(Return(...));
+/// VehicleTimeHandler handler;
+/// const auto report = handler.GetCurrentTime();
+/// @endcode
+class VehicleTimeHandler
+{
+ public:
+ VehicleTimeHandler() = default;
+ ~VehicleTimeHandler() = default;
+
+ VehicleTimeHandler(const VehicleTimeHandler&) = delete;
+ VehicleTimeHandler& operator=(const VehicleTimeHandler&) = delete;
+ VehicleTimeHandler(VehicleTimeHandler&&) = delete;
+ VehicleTimeHandler& operator=(VehicleTimeHandler&&) = delete;
+
+ /// @brief Reads the current vehicle time and local HPLS time and returns a combined report.
+ [[nodiscard]] TimeReport GetCurrentTime() const noexcept
+ {
+ const auto vehicle_snapshot = score::time::VehicleClock::GetInstance().Now();
+ const auto hpls_snapshot = score::time::HplsClock::GetInstance().Now();
+
+ return TimeReport{
+ vehicle_snapshot.TimePointNs().count(),
+ hpls_snapshot.TimePointNs().count(),
+ vehicle_snapshot.Status().IsSynchronized(),
+ vehicle_snapshot.Status().IsValid(),
+ vehicle_snapshot.Status().rate_deviation,
+ };
+ }
+};
+
+} // namespace vehicle_time
+} // namespace time
+} // namespace examples
+
+#endif // EXAMPLES_TIME_VEHICLE_TIME_VEHICLE_TIME_HANDLER_H
diff --git a/examples/time/vehicle_time/vehicle_time_handler_test.cpp b/examples/time/vehicle_time/vehicle_time_handler_test.cpp
new file mode 100644
index 0000000..0d655c0
--- /dev/null
+++ b/examples/time/vehicle_time/vehicle_time_handler_test.cpp
@@ -0,0 +1,133 @@
+/********************************************************************************
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+#include "examples/time/vehicle_time/vehicle_time_handler.h"
+
+#include "score/time/vehicle_time/vehicle_clock_mock.h"
+#include "score/time/hpls_time/hpls_clock_mock.h"
+#include "score/time/clock/scoped_clock_override.h"
+#include "score/time/clock/clock_snapshot.h"
+
+#include
+#include
+
+#include
+#include
+
+using ::testing::Return;
+
+namespace examples
+{
+namespace time
+{
+namespace vehicle_time
+{
+namespace test
+{
+
+/// @brief Test fixture: replaces both VehicleClock and HplsClock with mocks.
+///
+/// This is the key pattern for components that depend on multiple time bases:
+/// each clock domain has its own ScopedClockOverride that injects the mock
+/// backend for the duration of the test.
+class VehicleTimeHandlerTest : public ::testing::Test
+{
+ protected:
+ VehicleTimeHandlerTest()
+ : vehicle_mock_{std::make_shared()}
+ , hpls_mock_{std::make_shared()}
+ , vehicle_guard_{vehicle_mock_}
+ , hpls_guard_{hpls_mock_}
+ {
+ }
+
+ std::shared_ptr vehicle_mock_;
+ std::shared_ptr hpls_mock_;
+ score::time::test_utils::ScopedClockOverride vehicle_guard_;
+ score::time::test_utils::ScopedClockOverride hpls_guard_;
+};
+
+TEST_F(VehicleTimeHandlerTest, ReportContainsSynchronizedVehicleTimeAndHplsTime)
+{
+ // Prepare a synchronized vehicle time snapshot.
+ score::time::VehicleTimeStatus status;
+ status.flags = score::time::ClockStatus{
+ {score::time::VehicleTime::StatusFlag::kSynchronized}};
+ status.rate_deviation = 1.5e-9;
+
+ const score::time::VehicleTime::Timepoint vehicle_tp{std::chrono::nanoseconds{5'000'000'000LL}};
+ const score::time::ClockSnapshot
+ vehicle_snapshot{vehicle_tp, status};
+
+ const score::time::HplsTime::Timepoint hpls_tp{std::chrono::nanoseconds{1'234'567'890LL}};
+ const score::time::ClockSnapshot
+ hpls_snapshot{hpls_tp, score::time::NoStatus{}};
+
+ EXPECT_CALL(*vehicle_mock_, Now()).WillOnce(Return(vehicle_snapshot));
+ EXPECT_CALL(*hpls_mock_, Now()).WillOnce(Return(hpls_snapshot));
+
+ VehicleTimeHandler handler;
+ const TimeReport report = handler.GetCurrentTime();
+
+ EXPECT_EQ(report.vehicle_time_ns, 5'000'000'000LL);
+ EXPECT_EQ(report.hpls_time_ns, 1'234'567'890LL);
+ EXPECT_TRUE(report.synchronized);
+ EXPECT_TRUE(report.valid);
+ EXPECT_DOUBLE_EQ(report.rate_deviation, 1.5e-9);
+}
+
+TEST_F(VehicleTimeHandlerTest, ReportShowsNotSynchronizedWhenTimeOutFlagIsSet)
+{
+ score::time::VehicleTimeStatus status;
+ status.flags = score::time::ClockStatus{
+ {score::time::VehicleTime::StatusFlag::kTimeOut}};
+
+ EXPECT_CALL(*vehicle_mock_, Now()).WillOnce(Return(
+ score::time::ClockSnapshot{
+ score::time::VehicleTime::Timepoint{}, status}));
+ EXPECT_CALL(*hpls_mock_, Now()).WillOnce(Return(
+ score::time::ClockSnapshot{
+ score::time::HplsTime::Timepoint{}, score::time::NoStatus{}}));
+
+ VehicleTimeHandler handler;
+ const TimeReport report = handler.GetCurrentTime();
+
+ EXPECT_FALSE(report.synchronized);
+ EXPECT_EQ(report.vehicle_time_ns, 0LL);
+}
+
+TEST_F(VehicleTimeHandlerTest, HplsTimeIsIndependentFromVehicleTimeSynchronization)
+{
+ // Even when vehicle time is not synchronized, hpls_time_ns is still populated
+ // from the local monotonic clock.
+ score::time::VehicleTimeStatus unsync_status;
+
+ const score::time::HplsTime::Timepoint hpls_tp{std::chrono::nanoseconds{99'000'000LL}};
+
+ EXPECT_CALL(*vehicle_mock_, Now()).WillOnce(Return(
+ score::time::ClockSnapshot{
+ score::time::VehicleTime::Timepoint{}, unsync_status}));
+ EXPECT_CALL(*hpls_mock_, Now()).WillOnce(Return(
+ score::time::ClockSnapshot{
+ hpls_tp, score::time::NoStatus{}}));
+
+ VehicleTimeHandler handler;
+ const TimeReport report = handler.GetCurrentTime();
+
+ EXPECT_EQ(report.hpls_time_ns, 99'000'000LL);
+ EXPECT_FALSE(report.synchronized);
+}
+
+} // namespace test
+} // namespace vehicle_time
+} // namespace time
+} // namespace examples
diff --git a/score/TimeDaemon/code/common/data_types/BUILD b/score/TimeDaemon/code/common/data_types/BUILD
index e6b718d..5d4698d 100644
--- a/score/TimeDaemon/code/common/data_types/BUILD
+++ b/score/TimeDaemon/code/common/data_types/BUILD
@@ -23,7 +23,7 @@ cc_library(
features = COMPILER_WARNING_FEATURES,
tags = ["QM"],
visibility = ["//score/TimeDaemon:__subpackages__"],
- deps = ["//score/time/HighPrecisionLocalSteadyClock:interface"],
+ deps = ["//score/time/hpls_time:interface"],
)
cc_test(
diff --git a/score/TimeDaemon/code/common/data_types/ptp_time_info.h b/score/TimeDaemon/code/common/data_types/ptp_time_info.h
index 648a214..1bf8336 100644
--- a/score/TimeDaemon/code/common/data_types/ptp_time_info.h
+++ b/score/TimeDaemon/code/common/data_types/ptp_time_info.h
@@ -17,7 +17,7 @@
#include
#include
-#include "score/time/HighPrecisionLocalSteadyClock/high_precision_local_steady_clock.h"
+#include "score/time/hpls_time/hpls_clock.h"
namespace score
{
@@ -79,7 +79,7 @@ struct PtpTimeInfo
/**
* The local time base, used in the ptp evaluations
*/
- using ReferenceClock = score::time::HighPrecisionLocalSteadyClock;
+ using ReferenceClock = score::time::HplsClock;
std::chrono::nanoseconds ptp_assumed_time;
ReferenceClock::time_point local_time;
diff --git a/score/TimeDaemon/code/ipc/BUILD b/score/TimeDaemon/code/ipc/BUILD
index 2dc48b1..5f9e4f6 100644
--- a/score/TimeDaemon/code/ipc/BUILD
+++ b/score/TimeDaemon/code/ipc/BUILD
@@ -21,8 +21,6 @@ alias(
tags = ["QM"],
visibility = [
"//score/TimeDaemon:__subpackages__",
- # Added visibility for integration test purpose
- "//score/time/SynchronizedVehicleTime/details/tdr:__pkg__",
],
)
@@ -34,7 +32,7 @@ alias(
visibility = [
# Added visibility for integration test purpose
"//score/TimeDaemon:__subpackages__",
- "//score/time/SynchronizedVehicleTime/details/tdr:__pkg__",
+ "//score/time/vehicle_time/details/td_impl:__pkg__",
],
)
@@ -43,7 +41,9 @@ alias(
name = "svt_receiver_mock",
actual = "//score/TimeDaemon/code/ipc/svt/receiver:factory_mock",
tags = ["QM"],
- visibility = ["//score/time/SynchronizedVehicleTime/details/tdr:__pkg__"],
+ visibility = [
+ "//score/time/vehicle_time/details/td_impl:__pkg__",
+ ],
)
# receiver interface alias
diff --git a/score/TimeDaemon/code/ipc/svt/svt_time_info.h b/score/TimeDaemon/code/ipc/svt/svt_time_info.h
index 2e221b2..aad741b 100644
--- a/score/TimeDaemon/code/ipc/svt/svt_time_info.h
+++ b/score/TimeDaemon/code/ipc/svt/svt_time_info.h
@@ -17,8 +17,6 @@
#include
#include
-#include "score/time/HighPrecisionLocalSteadyClock/high_precision_local_steady_clock.h"
-
#include "score/TimeDaemon/code/common/data_types/ptp_time_info.h"
#include "score/TimeDaemon/code/ipc/data_converter.h"
diff --git a/score/TimeDaemon/code/ptp_machine/core/BUILD b/score/TimeDaemon/code/ptp_machine/core/BUILD
index 2f9ee04..7f9d03a 100644
--- a/score/TimeDaemon/code/ptp_machine/core/BUILD
+++ b/score/TimeDaemon/code/ptp_machine/core/BUILD
@@ -27,7 +27,7 @@ cc_library(
deps = [
"//score/TimeDaemon/code/common/data_flow",
"//score/TimeDaemon/code/common/machines",
- "//score/time/HighPrecisionLocalSteadyClock",
+ "//score/time/hpls_time:interface",
"@score_baselibs//score/mw/log:frontend",
],
)
diff --git a/score/TimeDaemon/code/ptp_machine/stub/BUILD b/score/TimeDaemon/code/ptp_machine/stub/BUILD
index d304be4..8c9e742 100644
--- a/score/TimeDaemon/code/ptp_machine/stub/BUILD
+++ b/score/TimeDaemon/code/ptp_machine/stub/BUILD
@@ -42,14 +42,14 @@ load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER
"gptp_stub_machine",
False,
[
- "//score/time/HighPrecisionLocalSteadyClock",
+ "//score/time/hpls_time:hpls_time",
],
),
(
"gptp_stub_machine_for_utests",
True,
[
- "//score/time/HighPrecisionLocalSteadyClock:utest_mock",
+ "//score/time/hpls_time:hpls_time_mock",
],
),
]
diff --git a/score/TimeDaemon/code/ptp_machine/stub/details/BUILD b/score/TimeDaemon/code/ptp_machine/stub/details/BUILD
index 254b3cb..c9fe5bf 100644
--- a/score/TimeDaemon/code/ptp_machine/stub/details/BUILD
+++ b/score/TimeDaemon/code/ptp_machine/stub/details/BUILD
@@ -28,7 +28,7 @@ cc_library(
deps = [
"//score/TimeDaemon/code/common:logging_contexts",
"//score/TimeDaemon/code/common/data_types:ptp_time_info",
- "//score/time/HighPrecisionLocalSteadyClock:interface",
+ "//score/time/hpls_time:interface",
"@score_baselibs//score/mw/log:frontend",
],
)
diff --git a/score/TimeDaemon/code/ptp_machine/stub/details/stub_ptp_engine.cpp b/score/TimeDaemon/code/ptp_machine/stub/details/stub_ptp_engine.cpp
index 7350f2e..f3d2965 100644
--- a/score/TimeDaemon/code/ptp_machine/stub/details/stub_ptp_engine.cpp
+++ b/score/TimeDaemon/code/ptp_machine/stub/details/stub_ptp_engine.cpp
@@ -13,7 +13,6 @@
#include "score/TimeDaemon/code/ptp_machine/stub/details/stub_ptp_engine.h"
#include "score/TimeDaemon/code/common/logging_contexts.h"
#include "score/mw/log/logging.h"
-#include "score/time/HighPrecisionLocalSteadyClock/details/factory_impl.h"
#include
#include
@@ -32,7 +31,7 @@ std::uint16_t sequence_id_{0U};
} // namespace
-StubPTPEngine::StubPTPEngine(std::unique_ptr local_clock) noexcept
+StubPTPEngine::StubPTPEngine(PtpTimeInfo::ReferenceClock local_clock) noexcept
: local_clock_{std::move(local_clock)}
{
score::mw::log::LogInfo(kGPtpMachineContext) << "StubPTPEngine created!";
@@ -62,9 +61,9 @@ bool StubPTPEngine::ReadPTPSnapshot(PtpTimeInfo& info)
bool StubPTPEngine::ReadTimeValueAndStatus(PtpTimeInfo& time_info) noexcept
{
- const auto now = local_clock_->Now();
- time_info.local_time = now;
- time_info.ptp_assumed_time = now.time_since_epoch();
+ const auto snapshot = local_clock_.Now();
+ time_info.local_time = snapshot.TimePoint();
+ time_info.ptp_assumed_time = snapshot.TimeSinceEpoch();
time_info.rate_deviation = 0.0;
time_info.status = PtpStatus{true, false, false, false, true};
@@ -76,7 +75,7 @@ bool StubPTPEngine::ReadTimeValueAndStatus(PtpTimeInfo& time_info) noexcept
bool StubPTPEngine::ReadSyncMeasurementData(PtpTimeInfo& time_info) const noexcept
{
// Stub: timestamps derived from local clock so they increase monotonically
- const auto now_ns = static_cast(local_clock_->Now().time_since_epoch().count());
+ const auto now_ns = static_cast(local_clock_.Now().TimeSinceEpoch().count());
time_info.sync_fup_data.precise_origin_timestamp = now_ns;
time_info.sync_fup_data.reference_global_timestamp = now_ns;
@@ -94,7 +93,7 @@ bool StubPTPEngine::ReadSyncMeasurementData(PtpTimeInfo& time_info) const noexce
bool StubPTPEngine::ReadPDelayMeasurementData(PtpTimeInfo& time_info) const noexcept
{
// Stub: simulate a round-trip with 1 µs one-way pdelay anchored to local clock
- const auto now_ns = static_cast(local_clock_->Now().time_since_epoch().count());
+ const auto now_ns = static_cast(local_clock_.Now().TimeSinceEpoch().count());
constexpr std::uint64_t kOnewayDelayNs{1'000U}; // 1 µs simulated one-way pdelay
time_info.pdelay_data.request_origin_timestamp = now_ns;
diff --git a/score/TimeDaemon/code/ptp_machine/stub/details/stub_ptp_engine.h b/score/TimeDaemon/code/ptp_machine/stub/details/stub_ptp_engine.h
index baed75d..5beeee3 100644
--- a/score/TimeDaemon/code/ptp_machine/stub/details/stub_ptp_engine.h
+++ b/score/TimeDaemon/code/ptp_machine/stub/details/stub_ptp_engine.h
@@ -37,7 +37,7 @@ namespace details
class StubPTPEngine final
{
public:
- explicit StubPTPEngine(std::unique_ptr local_clock) noexcept;
+ explicit StubPTPEngine(PtpTimeInfo::ReferenceClock local_clock) noexcept;
~StubPTPEngine() noexcept = default;
StubPTPEngine& operator=(const StubPTPEngine&) & noexcept = delete;
StubPTPEngine& operator=(StubPTPEngine&&) & noexcept = delete;
@@ -81,7 +81,7 @@ class StubPTPEngine final
bool ReadSyncMeasurementData(PtpTimeInfo& time_info) const noexcept;
private:
- std::unique_ptr local_clock_;
+ PtpTimeInfo::ReferenceClock local_clock_;
};
} // namespace details
diff --git a/score/TimeDaemon/code/ptp_machine/stub/factory.cpp b/score/TimeDaemon/code/ptp_machine/stub/factory.cpp
index 8e67c18..efb997f 100644
--- a/score/TimeDaemon/code/ptp_machine/stub/factory.cpp
+++ b/score/TimeDaemon/code/ptp_machine/stub/factory.cpp
@@ -11,7 +11,7 @@
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/
#include "score/TimeDaemon/code/ptp_machine/stub/factory.h"
-#include "score/time/HighPrecisionLocalSteadyClock/details/factory_impl.h"
+#include "score/time/hpls_time/hpls_clock.h"
namespace score
{
@@ -21,9 +21,7 @@ namespace td
std::shared_ptr CreateGPTPStubMachine(const std::string& name)
{
constexpr std::chrono::milliseconds updateInterval(50);
- score::time::HighPrecisionLocalSteadyClock::FactoryImpl clockFactory{};
- auto clock = clockFactory.CreateHighPrecisionLocalSteadyClock();
- return std::make_shared(name, updateInterval, std::move(clock));
+ return std::make_shared(name, updateInterval, score::time::HplsClock::GetInstance());
}
} // namespace td
diff --git a/score/TimeDaemon/code/ptp_machine/stub/gptp_stub_machine_integration_test.cpp b/score/TimeDaemon/code/ptp_machine/stub/gptp_stub_machine_integration_test.cpp
index 54b060b..02936b3 100644
--- a/score/TimeDaemon/code/ptp_machine/stub/gptp_stub_machine_integration_test.cpp
+++ b/score/TimeDaemon/code/ptp_machine/stub/gptp_stub_machine_integration_test.cpp
@@ -12,8 +12,7 @@
********************************************************************************/
#include "score/TimeDaemon/code/ptp_machine/stub/factory.h"
#include "score/TimeDaemon/code/ptp_machine/stub/details/stub_ptp_engine.h"
-#include "score/time/HighPrecisionLocalSteadyClock/high_precision_local_steady_clock.h"
-#include "score/time/HighPrecisionLocalSteadyClock/details/factory_impl.h"
+#include "score/time/hpls_time/hpls_clock.h"
#include
#include
@@ -32,9 +31,6 @@ class GPTPStubMachineIntegrationTest : public ::testing::Test
protected:
void SetUp() override
{
- score::time::HighPrecisionLocalSteadyClock::FactoryImpl hplsc_factory{};
- clock_ = hplsc_factory.CreateHighPrecisionLocalSteadyClock();
-
machine_ = CreateGPTPStubMachine("StubPTPMachine");
machine_->SetPublishCallback([this](const PtpTimeInfo& data) {
@@ -52,7 +48,6 @@ class GPTPStubMachineIntegrationTest : public ::testing::Test
machine_.reset();
}
- std::unique_ptr clock_;
std::shared_ptr machine_;
std::promise promise_data_published_;
PtpTimeInfo published_data_;
@@ -73,13 +68,14 @@ TEST_F(GPTPStubMachineIntegrationTest, GetSynchronizedDataTest)
{
EXPECT_TRUE(machine_->Init());
- const auto time_before_start = clock_->Now().time_since_epoch();
+ auto clock = score::time::HplsClock::GetInstance();
+ const auto time_before_start = clock.Now().TimeSinceEpoch();
machine_->Start();
auto future = promise_data_published_.get_future();
ASSERT_EQ(future.wait_for(std::chrono::milliseconds(300)), std::future_status::ready);
- const auto time_after_publish = clock_->Now().time_since_epoch();
+ const auto time_after_publish = clock.Now().TimeSinceEpoch();
{
std::lock_guard lock(publish_data_guard_);
diff --git a/score/TimeDaemon/code/verification_machine/svt/BUILD b/score/TimeDaemon/code/verification_machine/svt/BUILD
index fab6f6d..983db0c 100644
--- a/score/TimeDaemon/code/verification_machine/svt/BUILD
+++ b/score/TimeDaemon/code/verification_machine/svt/BUILD
@@ -52,6 +52,7 @@ cc_test(
tags = ["unit"],
deps = [
":svt_verification_machine",
+ "//score/time/hpls_time:hpls_time_mock",
"@googletest//:gtest",
"@googletest//:gtest_main",
"@score_baselibs//score/mw/log:console_only_backend",
diff --git a/score/TimeDaemon/code/verification_machine/svt/factory.cpp b/score/TimeDaemon/code/verification_machine/svt/factory.cpp
index 5dc702b..2d3d7aa 100644
--- a/score/TimeDaemon/code/verification_machine/svt/factory.cpp
+++ b/score/TimeDaemon/code/verification_machine/svt/factory.cpp
@@ -14,7 +14,7 @@
#include "score/TimeDaemon/code/verification_machine/svt/validators/synchronization_validator.h"
#include "score/TimeDaemon/code/verification_machine/svt/validators/time_jumps_validator.h"
#include "score/TimeDaemon/code/verification_machine/svt/validators/timeout_validator.h"
-#include "score/time/HighPrecisionLocalSteadyClock/details/factory_impl.h"
+#include "score/time/hpls_time/hpls_clock.h"
namespace score
{
@@ -23,18 +23,17 @@ namespace td
std::shared_ptr CreateSvtVerificationMachine(const std::string& name)
{
- const auto hplsc_factory = score::time::HighPrecisionLocalSteadyClock::FactoryImpl();
auto machine = std::make_shared(
name,
[]() {
return std::make_unique(/*args for validation*/);
},
- [&hplsc_factory]() {
- return std::make_unique(hplsc_factory.CreateHighPrecisionLocalSteadyClock(),
+ []() {
+ return std::make_unique(score::time::HplsClock::GetInstance(),
std::chrono::nanoseconds{3'300'000'000});
},
- [&hplsc_factory]() {
- return std::make_unique(hplsc_factory.CreateHighPrecisionLocalSteadyClock(),
+ []() {
+ return std::make_unique(score::time::HplsClock::GetInstance(),
std::chrono::nanoseconds(500'000),
std::chrono::nanoseconds(5'000'000'000),
2U);
diff --git a/score/TimeDaemon/code/verification_machine/svt/validators/BUILD b/score/TimeDaemon/code/verification_machine/svt/validators/BUILD
index ce5e5b2..6b3e8b6 100644
--- a/score/TimeDaemon/code/verification_machine/svt/validators/BUILD
+++ b/score/TimeDaemon/code/verification_machine/svt/validators/BUILD
@@ -33,7 +33,7 @@ cc_library(
"//score/TimeDaemon/code/common:logging_contexts",
"//score/TimeDaemon/code/common/data_types:ptp_time_info",
"//score/TimeDaemon/code/verification_machine/core:verification_machine",
- "//score/time/HighPrecisionLocalSteadyClock",
+ "//score/time/hpls_time:interface",
"@score_baselibs//score/mw/log:frontend",
],
)
@@ -56,7 +56,7 @@ cc_test(
tags = ["unit"],
deps = [
":validators",
- "//score/time/HighPrecisionLocalSteadyClock:utest_mock",
+ "//score/time/hpls_time:hpls_time_mock",
"@googletest//:gtest",
"@googletest//:gtest_main",
"@score_baselibs//score/mw/log:console_only_backend",
@@ -71,7 +71,7 @@ cc_test(
tags = ["unit"],
deps = [
":validators",
- "//score/time/HighPrecisionLocalSteadyClock:utest_mock",
+ "//score/time/hpls_time:hpls_time_mock",
"@googletest//:gtest",
"@googletest//:gtest_main",
"@score_baselibs//score/mw/log:console_only_backend",
@@ -86,7 +86,7 @@ cc_test(
tags = ["unit"],
deps = [
":validators",
- "//score/time/HighPrecisionLocalSteadyClock:utest_mock",
+ "//score/time/hpls_time:hpls_time_mock",
"@googletest//:gtest",
"@googletest//:gtest_main",
"@score_baselibs//score/mw/log:console_only_backend",
diff --git a/score/TimeDaemon/code/verification_machine/svt/validators/time_jumps_validator.cpp b/score/TimeDaemon/code/verification_machine/svt/validators/time_jumps_validator.cpp
index 5db1d36..c3cade7 100644
--- a/score/TimeDaemon/code/verification_machine/svt/validators/time_jumps_validator.cpp
+++ b/score/TimeDaemon/code/verification_machine/svt/validators/time_jumps_validator.cpp
@@ -19,7 +19,7 @@ namespace score
namespace td
{
-TimeJumpsValidator::TimeJumpsValidator(std::unique_ptr debouncing_clock,
+TimeJumpsValidator::TimeJumpsValidator(PtpTimeInfo::ReferenceClock debouncing_clock,
std::chrono::nanoseconds max_time_jump_allowed,
std::chrono::nanoseconds sync_debounce_threshold,
std::uint8_t valid_frames_threshold)
@@ -122,7 +122,7 @@ void TimeJumpsValidator::HandleIdleState(const PtpTimeInfo& data)
void TimeJumpsValidator::HandleInitialSyncDebouncingState()
{
- if ((sync_debouncing_init_time_ + sync_debounce_threshold_) < debouncing_clock_->Now().time_since_epoch())
+ if ((sync_debouncing_init_time_ + sync_debounce_threshold_) < debouncing_clock_.Now().TimeSinceEpoch())
{
GoToTimeJumpHandling();
}
@@ -177,7 +177,7 @@ void TimeJumpsValidator::UpdateStatus(PtpTimeInfo& data)
void TimeJumpsValidator::GoToInitialSyncDebouncing()
{
// Set debouncing timer, so we will calculate sync debouncing time from this point
- sync_debouncing_init_time_ = debouncing_clock_->Now().time_since_epoch();
+ sync_debouncing_init_time_ = debouncing_clock_.Now().TimeSinceEpoch();
current_state_ = ProcessingStates::kInitialSyncDebouncing;
score::mw::log::LogDebug(kVerificationMachineContext)
<< "TimeJumpsValidator: Switch to kInitialSyncDebouncing state";
diff --git a/score/TimeDaemon/code/verification_machine/svt/validators/time_jumps_validator.h b/score/TimeDaemon/code/verification_machine/svt/validators/time_jumps_validator.h
index 6c80846..a65f61b 100644
--- a/score/TimeDaemon/code/verification_machine/svt/validators/time_jumps_validator.h
+++ b/score/TimeDaemon/code/verification_machine/svt/validators/time_jumps_validator.h
@@ -36,7 +36,7 @@ namespace td
class TimeJumpsValidator : public VerificationStage
{
public:
- TimeJumpsValidator(std::unique_ptr debouncing_clock,
+ TimeJumpsValidator(PtpTimeInfo::ReferenceClock debouncing_clock,
std::chrono::nanoseconds max_time_jump_allowed,
std::chrono::nanoseconds sync_debounce_threshold,
std::uint8_t valid_frames_threshold);
@@ -74,7 +74,7 @@ class TimeJumpsValidator : public VerificationStage
ProcessingStates current_state_;
std::optional last_sync_frame_;
PtpTimeInfo::ReferenceClock::duration sync_debouncing_init_time_;
- std::unique_ptr debouncing_clock_;
+ PtpTimeInfo::ReferenceClock debouncing_clock_;
std::uint8_t valid_frames_cnt_;
};
diff --git a/score/TimeDaemon/code/verification_machine/svt/validators/time_jumps_validator_test.cpp b/score/TimeDaemon/code/verification_machine/svt/validators/time_jumps_validator_test.cpp
index 4fca5a5..c1ebb9a 100644
--- a/score/TimeDaemon/code/verification_machine/svt/validators/time_jumps_validator_test.cpp
+++ b/score/TimeDaemon/code/verification_machine/svt/validators/time_jumps_validator_test.cpp
@@ -12,7 +12,7 @@
********************************************************************************/
#include "score/TimeDaemon/code/verification_machine/svt/validators/time_jumps_validator.h"
-#include "score/time/HighPrecisionLocalSteadyClock/high_precision_local_steady_clock_mock.h"
+#include "score/time/hpls_time/hpls_clock_mock.h"
#include "gmock/gmock.h"
#include
@@ -61,21 +61,22 @@ INSTANTIATE_TEST_CASE_P(
TEST_P(TimeJumpsValidatorParamTest, ValidationTest)
{
- auto timeout_clock = std::make_unique();
- // Get a raw pointer to the mock object before moving it
- const auto& timeout_clock_mock = timeout_clock.get();
+ auto mock = std::make_shared();
TimeJumpsValidator validator(
- std::move(timeout_clock), std::chrono::nanoseconds(500'000), std::chrono::nanoseconds(5'000'000), 2U);
+ score::time::test_utils::ClockTestFactory::Make(mock),
+ std::chrono::nanoseconds(500'000), std::chrono::nanoseconds(5'000'000), 2U);
// Pass synchronized state debouncing
- EXPECT_CALL(*timeout_clock_mock, Now())
+ EXPECT_CALL(*mock, Now())
// For initial time
- .WillOnce(
- ::testing::Return(score::time::HighPrecisionLocalSteadyClock::time_point{std::chrono::nanoseconds(0)}))
+ .WillOnce(::testing::Return(
+ score::time::ClockSnapshot{
+ score::time::HplsTime::Timepoint{std::chrono::nanoseconds(0)}, {}}))
// For threshold pass
.WillOnce(::testing::Return(
- score::time::HighPrecisionLocalSteadyClock::time_point{std::chrono::nanoseconds(6'000'000'000)}));
+ score::time::ClockSnapshot{
+ score::time::HplsTime::Timepoint{std::chrono::nanoseconds(6'000'000'000)}, {}}));
PtpTimeInfo entry_data{};
entry_data.status.is_synchronized = true;
diff --git a/score/TimeDaemon/code/verification_machine/svt/validators/timeout_validator.cpp b/score/TimeDaemon/code/verification_machine/svt/validators/timeout_validator.cpp
index 15d122c..545a351 100644
--- a/score/TimeDaemon/code/verification_machine/svt/validators/timeout_validator.cpp
+++ b/score/TimeDaemon/code/verification_machine/svt/validators/timeout_validator.cpp
@@ -19,11 +19,11 @@ namespace score
namespace td
{
-TimeoutValidator::TimeoutValidator(std::unique_ptr timeout_clock,
+TimeoutValidator::TimeoutValidator(PtpTimeInfo::ReferenceClock timeout_clock,
std::chrono::nanoseconds reception_timeout)
: threshold_{reception_timeout}, timeout_clock_{std::move(timeout_clock)}
{
- reception_time_ = timeout_clock_->Now().time_since_epoch();
+ reception_time_ = timeout_clock_.Now().TimeSinceEpoch();
}
void TimeoutValidator::DoValidation(PtpTimeInfo& data)
@@ -31,7 +31,7 @@ void TimeoutValidator::DoValidation(PtpTimeInfo& data)
if (IsNewFrameReceived(data))
{
// As long as we receive new frames, we update the reception time
- reception_time_ = timeout_clock_->Now().time_since_epoch();
+ reception_time_ = timeout_clock_.Now().TimeSinceEpoch();
// reset timeout flag
data.status.is_timeout = false;
@@ -41,7 +41,7 @@ void TimeoutValidator::DoValidation(PtpTimeInfo& data)
else
{
// In case no new frame -> check if timeout occurs
- const auto now = timeout_clock_->Now().time_since_epoch();
+ const auto now = timeout_clock_.Now().TimeSinceEpoch();
const auto now_nano = std::chrono::duration_cast(now);
const auto reception_time_nano = std::chrono::duration_cast(reception_time_);
score::mw::log::LogDebug(kVerificationMachineContext)
diff --git a/score/TimeDaemon/code/verification_machine/svt/validators/timeout_validator.h b/score/TimeDaemon/code/verification_machine/svt/validators/timeout_validator.h
index 49025ab..ddfd0be 100644
--- a/score/TimeDaemon/code/verification_machine/svt/validators/timeout_validator.h
+++ b/score/TimeDaemon/code/verification_machine/svt/validators/timeout_validator.h
@@ -31,7 +31,7 @@ namespace td
class TimeoutValidator : public VerificationStage
{
public:
- explicit TimeoutValidator(std::unique_ptr timeout_clock,
+ explicit TimeoutValidator(PtpTimeInfo::ReferenceClock timeout_clock,
std::chrono::nanoseconds reception_timeout);
virtual ~TimeoutValidator() = default;
@@ -42,7 +42,7 @@ class TimeoutValidator : public VerificationStage
bool IsNewFrameReceived(const PtpTimeInfo& data);
const std::chrono::nanoseconds threshold_;
- std::unique_ptr timeout_clock_;
+ PtpTimeInfo::ReferenceClock timeout_clock_;
PtpTimeInfo::ReferenceClock::duration reception_time_;
std::optional last_received_data_;
};
diff --git a/score/TimeDaemon/code/verification_machine/svt/validators/timeout_validator_test.cpp b/score/TimeDaemon/code/verification_machine/svt/validators/timeout_validator_test.cpp
index 9ce2234..f43ca1d 100644
--- a/score/TimeDaemon/code/verification_machine/svt/validators/timeout_validator_test.cpp
+++ b/score/TimeDaemon/code/verification_machine/svt/validators/timeout_validator_test.cpp
@@ -12,7 +12,7 @@
********************************************************************************/
#include "score/TimeDaemon/code/verification_machine/svt/validators/timeout_validator.h"
-#include "score/time/HighPrecisionLocalSteadyClock/high_precision_local_steady_clock_mock.h"
+#include "score/time/hpls_time/hpls_clock_mock.h"
#include "gmock/gmock.h"
#include
@@ -90,11 +90,10 @@ INSTANTIATE_TEST_SUITE_P(
TEST_P(TimeoutValidatorParamTest, ValidationTest)
{
- auto timeout_clock = std::make_unique();
- // Get a raw pointer to the mock object before moving it
- const auto& timeout_clock_mock = timeout_clock.get();
+ auto mock = std::make_shared();
- TimeoutValidator validator(std::move(timeout_clock), std::chrono::nanoseconds{3'300'000'000});
+ TimeoutValidator validator(score::time::test_utils::ClockTestFactory::Make(mock),
+ std::chrono::nanoseconds{3'300'000'000});
for (const auto& param : GetParam().sequence)
{
@@ -104,9 +103,10 @@ TEST_P(TimeoutValidatorParamTest, ValidationTest)
std::chrono::nanoseconds cur_ptp_time{0};
PtpTimeInfo::ReferenceClock::time_point cur_local_time{std::chrono::nanoseconds{0}};
PtpTimeInfo in_data = {cur_ptp_time, cur_local_time, 0, {}, in_sync_data, {}};
- EXPECT_CALL(*timeout_clock_mock, Now())
+ EXPECT_CALL(*mock, Now())
.WillOnce(::testing::Return(
- score::time::HighPrecisionLocalSteadyClock::time_point{param.simulated_current_time_ns}));
+ score::time::ClockSnapshot{
+ score::time::HplsTime::Timepoint{param.simulated_current_time_ns}, {}}));
auto result = validator.Process(in_data);
EXPECT_EQ(result.status.is_timeout, param.is_expected_timeout)
<< " for simulated_sequence_id_for_sync_msg = " << param.simulated_sequence_id_for_sync_msg
diff --git a/score/time/BUILD b/score/time/BUILD
new file mode 100644
index 0000000..5472365
--- /dev/null
+++ b/score/time/BUILD
@@ -0,0 +1,37 @@
+# *******************************************************************************
+# Copyright (c) 2026 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Apache License Version 2.0 which is available at
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# SPDX-License-Identifier: Apache-2.0
+# *******************************************************************************
+
+load("@score_baselibs//:bazel/unit_tests.bzl", "cc_unit_test_suites_for_host_and_qnx")
+load("@score_baselibs//third_party/itf:py_unittest_qnx_test.bzl", "py_unittest_qnx_test")
+
+py_unittest_qnx_test(
+ name = "qnx_unit_test_cases",
+ test_suites = [
+ "//score/time/hpls_time:qnx_unit_test_cases",
+ ],
+ visibility = ["//score/time:__pkg__"],
+)
+
+cc_unit_test_suites_for_host_and_qnx(
+ name = "unit_test_suite",
+ cc_unit_tests = [],
+ test_suites_from_sub_packages = [
+ "//score/time/clock:unit_test_suite",
+ "//score/time/ptp:unit_test_suite",
+ "//score/time/hpls_time:unit_test_suite",
+ "//score/time/vehicle_time:unit_test_suite",
+ "//score/time/steady_time:unit_test_suite",
+ "//score/time/system_time:unit_test_suite",
+ ],
+ visibility = ["//score:__pkg__"],
+)
diff --git a/score/time/HighPrecisionLocalSteadyClock/BUILD b/score/time/HighPrecisionLocalSteadyClock/BUILD
deleted file mode 100644
index ba1d318..0000000
--- a/score/time/HighPrecisionLocalSteadyClock/BUILD
+++ /dev/null
@@ -1,83 +0,0 @@
-# *******************************************************************************
-# Copyright (c) 2026 Contributors to the Eclipse Foundation
-#
-# See the NOTICE file(s) distributed with this work for additional
-# information regarding copyright ownership.
-#
-# This program and the accompanying materials are made available under the
-# terms of the Apache License Version 2.0 which is available at
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# SPDX-License-Identifier: Apache-2.0
-# *******************************************************************************
-
-load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES")
-load("@score_baselibs//third_party/itf:py_unittest_qnx_test.bzl", "py_unittest_qnx_test")
-
-alias(
- name = "HighPrecisionLocalSteadyClock",
- actual = "//score/time/HighPrecisionLocalSteadyClock/details",
- visibility = ["//visibility:public"],
-)
-
-alias(
- name = "utest_mock",
- testonly = True,
- actual = "//score/time/HighPrecisionLocalSteadyClock/details:factory_impl_for_utests",
- visibility = ["//visibility:public"],
-)
-
-cc_library(
- name = "interface",
- srcs = [
- "factory.cpp",
- "high_precision_local_steady_clock.cpp",
- ],
- hdrs = [
- "factory.h",
- "high_precision_local_steady_clock.h",
- ],
- features = COMPILER_WARNING_FEATURES,
- tags = ["FFI"],
- visibility = ["//visibility:public"],
- deps = [
- "//score/time/HighPrecisionLocalSteadyClock/details:factory_impl_header",
- ],
-)
-
-cc_library(
- name = "interface_mocks",
- testonly = True,
- srcs = [
- "factory_mock.cpp",
- "high_precision_local_steady_clock_mock.cpp",
- ],
- hdrs = [
- "factory_mock.h",
- "high_precision_local_steady_clock_mock.h",
- ],
- features = COMPILER_WARNING_FEATURES,
- visibility = [
- "//visibility:public",
- ],
- deps = [
- ":interface",
- "@googletest//:gtest",
- ],
-)
-
-py_unittest_qnx_test(
- name = "qnx_unit_test_cases",
- test_suites = [
- "//score/time/HighPrecisionLocalSteadyClock/details:qnx_unit_test_cases",
- ],
- visibility = ["//score/time:__pkg__"],
-)
-
-test_suite(
- name = "unit_tests",
- tests = [
- "//score/time/HighPrecisionLocalSteadyClock/details:unit_tests",
- ],
- visibility = ["//score/time:__subpackages__"],
-)
diff --git a/score/time/HighPrecisionLocalSteadyClock/details/BUILD b/score/time/HighPrecisionLocalSteadyClock/details/BUILD
deleted file mode 100644
index 685ff03..0000000
--- a/score/time/HighPrecisionLocalSteadyClock/details/BUILD
+++ /dev/null
@@ -1,75 +0,0 @@
-# *******************************************************************************
-# Copyright (c) 2026 Contributors to the Eclipse Foundation
-#
-# See the NOTICE file(s) distributed with this work for additional
-# information regarding copyright ownership.
-#
-# This program and the accompanying materials are made available under the
-# terms of the Apache License Version 2.0 which is available at
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# SPDX-License-Identifier: Apache-2.0
-# *******************************************************************************
-
-load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES")
-load("@score_baselibs//third_party/itf:py_unittest_qnx_test.bzl", "py_unittest_qnx_test")
-
-cc_library(
- name = "factory_impl_header",
- hdrs = ["factory_impl.h"],
- features = COMPILER_WARNING_FEATURES,
- tags = ["FFI"],
- visibility = [
- "//score/time/HighPrecisionLocalSteadyClock:__subpackages__",
- ],
-)
-
-filegroup(
- name = "factory_impl_stub_srcs",
- srcs = [
- "factory_impl_stub.cpp",
- ],
- visibility = [
- "//score/time/HighPrecisionLocalSteadyClock:__subpackages__",
- ],
-)
-
-cc_library(
- name = "factory_impl_for_utests",
- testonly = True,
- srcs = ["factory_impl_stub.cpp"],
- features = COMPILER_WARNING_FEATURES,
- visibility = ["//score/time/HighPrecisionLocalSteadyClock:__subpackages__"],
- deps = [
- "//score/time/HighPrecisionLocalSteadyClock:interface_mocks",
- "@googletest//:gtest",
- ],
-)
-
-alias(
- name = "details",
- actual = select({
- "@platforms//os:qnx": "//score/time/HighPrecisionLocalSteadyClock/details/qtime:qclock",
- "//conditions:default": "//score/time/HighPrecisionLocalSteadyClock/details/system_clock",
- }),
- visibility = [
- "//score/time/HighPrecisionLocalSteadyClock:__subpackages__",
- ],
-)
-
-py_unittest_qnx_test(
- name = "qnx_unit_test_cases",
- test_suites = [
- "//score/time/HighPrecisionLocalSteadyClock/details/qtime:qnx_unit_test_cases",
- "//score/time/HighPrecisionLocalSteadyClock/details/system_clock:qnx_unit_test_cases",
- ],
- visibility = ["//score/time/HighPrecisionLocalSteadyClock:__pkg__"],
-)
-
-test_suite(
- name = "unit_tests",
- tests = [
- "//score/time/HighPrecisionLocalSteadyClock/details/system_clock:unit_tests",
- ],
- visibility = ["//score/time/HighPrecisionLocalSteadyClock:__pkg__"],
-)
diff --git a/score/time/HighPrecisionLocalSteadyClock/details/factory_impl.h b/score/time/HighPrecisionLocalSteadyClock/details/factory_impl.h
deleted file mode 100644
index 0f75297..0000000
--- a/score/time/HighPrecisionLocalSteadyClock/details/factory_impl.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2026 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Apache License Version 2.0 which is available at
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * SPDX-License-Identifier: Apache-2.0
- ********************************************************************************/
-#ifndef SCORE_TIME_HIGHPRECISIONLOCALSTEADYCLOCK_DETAILS_FACTORY_IMPL_H
-#define SCORE_TIME_HIGHPRECISIONLOCALSTEADYCLOCK_DETAILS_FACTORY_IMPL_H
-
-#include "score/time/HighPrecisionLocalSteadyClock/factory.h"
-#include "score/time/HighPrecisionLocalSteadyClock/high_precision_local_steady_clock.h"
-
-namespace score
-{
-namespace time
-{
-
-///
-/// \brief Implementation of score::time::HighPrecisionLocalSteadyClock::Factory which provides
-/// score::time::HighPrecisionLocalSteadyClock objects.
-///
-class HighPrecisionLocalSteadyClock::FactoryImpl final : public score::time::HighPrecisionLocalSteadyClock::Factory
-{
- public:
- constexpr FactoryImpl() noexcept = default;
- FactoryImpl(FactoryImpl&&) noexcept = delete;
- FactoryImpl(const FactoryImpl&) noexcept = delete;
- FactoryImpl& operator=(FactoryImpl&&) noexcept = delete;
- FactoryImpl& operator=(const FactoryImpl&) noexcept = delete;
- ~FactoryImpl() noexcept override = default;
-
- std::unique_ptr CreateHighPrecisionLocalSteadyClock() const override;
-};
-
-} // namespace time
-} // namespace score
-
-#endif // #ifndef SCORE_TIME_HIGHPRECISIONLOCALSTEADYCLOCK_DETAILS_FACTORY_IMPL_H
diff --git a/score/time/HighPrecisionLocalSteadyClock/details/factory_impl_stub.cpp b/score/time/HighPrecisionLocalSteadyClock/details/factory_impl_stub.cpp
deleted file mode 100644
index 700c39f..0000000
--- a/score/time/HighPrecisionLocalSteadyClock/details/factory_impl_stub.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2026 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Apache License Version 2.0 which is available at
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * SPDX-License-Identifier: Apache-2.0
- ********************************************************************************/
-#include "score/time/HighPrecisionLocalSteadyClock/details/factory_impl.h"
-#include "score/time/HighPrecisionLocalSteadyClock/factory_mock.h"
-
-namespace score
-{
-namespace time
-{
-
-std::unique_ptr
-HighPrecisionLocalSteadyClock::FactoryImpl::CreateHighPrecisionLocalSteadyClock() const
-{
- static HighPrecisionLocalSteadyClockFactoryMock factoryObj;
- return factoryObj.CreateHighPrecisionLocalSteadyClock();
-}
-
-} // namespace time
-} // namespace score
diff --git a/score/time/HighPrecisionLocalSteadyClock/details/qtime/factory_test.cpp b/score/time/HighPrecisionLocalSteadyClock/details/qtime/factory_test.cpp
deleted file mode 100644
index 3ef7239..0000000
--- a/score/time/HighPrecisionLocalSteadyClock/details/qtime/factory_test.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2026 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Apache License Version 2.0 which is available at
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * SPDX-License-Identifier: Apache-2.0
- ********************************************************************************/
-#include "score/time/HighPrecisionLocalSteadyClock/details/factory_impl.h"
-#include "score/time/HighPrecisionLocalSteadyClock/details/qtime/qclock.h"
-
-#include
-
-namespace score
-{
-namespace time
-{
-namespace details
-{
-namespace qtime
-{
-namespace
-{
-
-TEST(TestFactory, Destruction)
-{
- RecordProperty("Description",
- "This test verifies different variants of object construction and destruction for the factory");
- RecordProperty("Verifies", "score::time::HighPrecisionLocalSteadyClock::Factory");
- RecordProperty("TestType", "Interface test");
- RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience");
- RecordProperty("ASIL", "QM");
- RecordProperty("Priority", "3");
-
- // Given an instance via base-class pointer
- std::unique_ptr factory_base =
- std::make_unique();
- // When being reset
- factory_base.reset();
- // Then it gets destructed
-
- // Given an instance via direct pointer
- auto factory_impl = std::make_unique();
- // When being reset
- factory_impl.reset();
- // Then it gets destructed
-
- {
- // Given an instance via object on stack
- HighPrecisionLocalSteadyClock::FactoryImpl factory;
- // When going out of scope
- (void)factory;
- }
- // Then it gets destructed
-}
-
-TEST(TestFactory, CreateHighPrecisionLocalSteadyClock)
-{
- RecordProperty("Description",
- "This test verifies that the HighPrecisionLocalSteadyClock object is created from the factory ");
- RecordProperty("Verifies",
- "score::time::HighPrecisionLocalSteadyClock::FactoryImpl::CreateHighPrecisionLocalSteadyClock()");
- RecordProperty("TestType", "Interface test");
- RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience");
- RecordProperty("ASIL", "QM");
- RecordProperty("Priority", "3");
-
- // Given a Factory instance
- HighPrecisionLocalSteadyClock::FactoryImpl factory;
-
- // When CreateHighPrecisionLocalSteadyClock() gets called
- auto first_instance = factory.CreateHighPrecisionLocalSteadyClock();
-
- // Then a valid HighPrecisionLocalSteadyClock must be returned
- ASSERT_NE(nullptr, first_instance);
-
- // Which must be of type qtime::QClock
- ASSERT_NE(nullptr, dynamic_cast(first_instance.get()));
-}
-
-} // namespace
-} // namespace qtime
-} // namespace details
-} // namespace time
-} // namespace score
diff --git a/score/time/HighPrecisionLocalSteadyClock/details/qtime/qclock.cpp b/score/time/HighPrecisionLocalSteadyClock/details/qtime/qclock.cpp
deleted file mode 100644
index 8b809c9..0000000
--- a/score/time/HighPrecisionLocalSteadyClock/details/qtime/qclock.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2026 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Apache License Version 2.0 which is available at
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * SPDX-License-Identifier: Apache-2.0
- ********************************************************************************/
-#include "score/time/HighPrecisionLocalSteadyClock/details/qtime/qclock.h"
-
-#include "score/time/HighPrecisionLocalSteadyClock/details/qtime/tick_provider.h"
-
-#include "score/lib/os/qnx/neutrino.h"
-
-namespace score
-{
-namespace time
-{
-namespace details
-{
-namespace qtime
-{
-
-HighPrecisionLocalSteadyClock::time_point QClock::Now() noexcept
-{
- return HighPrecisionLocalSteadyClock::time_point{
- ClockCyclesToNanoseconds(score:os::qnx::Neutrino::instance().ClockCycles())};
-}
-
-std::chrono::nanoseconds QClock::ClockCyclesToNanoseconds(const std::uint64_t clock_cycles) const noexcept
-{
- const std::uint64_t cycles_per_sec = GetClockCyclesPerSec();
- std::chrono::nanoseconds converted_thicks{0};
-
- if (cycles_per_sec > 0U)
- {
- constexpr auto billion = static_cast(std::nano::den);
- constexpr auto max_value = static_cast(std::chrono::nanoseconds::max().count());
- constexpr std::uint64_t upper_bound{max_value / billion};
- const std::uint64_t division_result{clock_cycles / cycles_per_sec};
- const std::uint64_t division_rest{clock_cycles % cycles_per_sec};
- if ((division_result <= upper_bound) && (division_rest <= upper_bound))
- {
- converted_thicks =
- std::chrono::nanoseconds{division_result * billion + division_rest * billion / cycles_per_sec};
- }
- }
-
- return converted_thicks;
-}
-
-} // namespace qtime
-} // namespace details
-} // namespace time
-} // namespace score
diff --git a/score/time/HighPrecisionLocalSteadyClock/details/qtime/qclock.h b/score/time/HighPrecisionLocalSteadyClock/details/qtime/qclock.h
deleted file mode 100644
index 4abffea..0000000
--- a/score/time/HighPrecisionLocalSteadyClock/details/qtime/qclock.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2026 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Apache License Version 2.0 which is available at
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * SPDX-License-Identifier: Apache-2.0
- ********************************************************************************/
-#ifndef SCORE_TIME_HIGHPRECISIONLOCALSTEADYCLOCK_DETAILS_QTIME_QCLOCK_H
-#define SCORE_TIME_HIGHPRECISIONLOCALSTEADYCLOCK_DETAILS_QTIME_QCLOCK_H
-
-#include "score/time/HighPrecisionLocalSteadyClock/high_precision_local_steady_clock.h"
-
-namespace score
-{
-namespace time
-{
-namespace details
-{
-namespace qtime
-{
-
-///
-/// \brief The class represents quasi-realtime clock that offers nanosecond resolution in PLP format.
-///
-class QClock final : public ::score::time::HighPrecisionLocalSteadyClock
-{
- public:
- QClock() noexcept = default;
- QClock& operator=(const QClock&) noexcept = delete;
- QClock& operator=(QClock&&) noexcept = delete;
- QClock(const QClock&) noexcept = delete;
- QClock(QClock&&) noexcept = delete;
- ~QClock() noexcept override = default;
-
- /// \brief Method for obtaining the current clock value of the HPLSC.
- ///
- /// \details overrides ::score::time::HighPrecisionLocalSteadyClock::Now()
- ///
- /// \return current clock value and its status of the underlying timebase
- ///
- time_point Now() noexcept override;
-
- private:
- std::chrono::nanoseconds ClockCyclesToNanoseconds(const std::uint64_t clock_cycles) const noexcept;
-};
-
-} // namespace qtime
-} // namespace details
-} // namespace time
-} // namespace score
-
-#endif // #ifndef SCORE_TIME_HIGHPRECISIONLOCALSTEADYCLOCK_DETAILS_QTIME_QCLOCK_H
diff --git a/score/time/HighPrecisionLocalSteadyClock/details/qtime/qclock_test.cpp b/score/time/HighPrecisionLocalSteadyClock/details/qtime/qclock_test.cpp
deleted file mode 100644
index 346f505..0000000
--- a/score/time/HighPrecisionLocalSteadyClock/details/qtime/qclock_test.cpp
+++ /dev/null
@@ -1,226 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2026 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Apache License Version 2.0 which is available at
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * SPDX-License-Identifier: Apache-2.0
- ********************************************************************************/
-#include "score/time/HighPrecisionLocalSteadyClock/details/qtime/qclock.h"
-
-#include "score/time/HighPrecisionLocalSteadyClock/details/qtime/tick_provider_mock.h"
-
-#include "score/lib/os/mocklib/qnx/neutrino_qnx_mock.h"
-
-#include
-
-#include "score/time/HighPrecisionLocalSteadyClock/details/factory_impl.h"
-#include
-
-namespace score
-{
-namespace time
-{
-namespace details
-{
-namespace test
-{
-
-using namespace ::testing;
-
-struct TestCaseParams
-{
- uint64_t tickCount{};
- uint64_t tickPerSec{};
- uint64_t tickInNano{};
-
- friend void PrintTo(const TestCaseParams& point, std::ostream* os)
- {
- *os << "In-params: " << point.tickCount << ", " << point.tickPerSec << ", " << point.tickInNano;
- }
-};
-
-class HighPrecisionLocalSteadyClock : public ::testing::TestWithParam
-{
- public:
- using TickProviderMock = score::time::details::qtime::TickProviderMock;
-
- void SetUp() override
- {
- TickProviderMock::CreateMockInstance();
- }
-
- void TearDown() override
- {
- TickProviderMock::DestroyMockInstance();
- }
-};
-
-INSTANTIATE_TEST_SUITE_P(HighPrecisionLocalSteadyClockInstantiate,
- HighPrecisionLocalSteadyClock,
- ::testing::Values(
- // TestCaseParams (inTimePoint, tickPerSec, tickInNano)
- // Zero ticks
- TestCaseParams{0, 19'200'000, 0},
-
- // Some values
- TestCaseParams{3'462'637'232, 19'200'000, 180'345'689'166},
-
- // overflow test:
- // ClockCycles at 960s ~= 2361000000 * 960 = 2,266,560,000,000
- TestCaseParams{2'266'560'000'000, 2'361'000'000, 960'000'000'000},
-
- // 1 hour ~ 3.600.000.277.053 nano ~ 8.499.600.000.000 ticks
- TestCaseParams{8'499'600'654'123, 2'361'000'000, 3'600'000'277'053},
-
- // 24 hours ~ 203990415698952 ticks
- TestCaseParams{203'990'415'698'952, 2'361'000'000, 86'400'006'649'280},
-
- // 48 hours ~ 407980831397904 ticks
- TestCaseParams{407'980'831'397'904, 2'361'000'000, 172'800'013'298'561},
-
- // 96 hours ~ 815.961.662.795.808 ticks
- TestCaseParams{815'961'662'795'808, 2'361'000'000, 345'600'026'597'123},
-
- // 22 days ~ 4'503'599'627'370'495 ticks
- TestCaseParams{0x000FFFFFFFFFFFFF, 2'361'000'000, 1'907'496'665'552'941},
-
- // 353 days ~ 18'446'744'073'709'551'615 ticks
- TestCaseParams{0x00FFFFFFFFFFFFFF, 2'361'000'000, 30'519'946'648'847'071},
-
- // 15 years ~ 295'147'905'179'352'825'855 ticks
- TestCaseParams{0x0FFFFFFFFFFFFFFF, 2'361'000'000, 488'319'146'381'553'144},
-
- // 247 years ~ 4'722'366'482'869'645'213'695 ticks
- TestCaseParams{0xFFFFFFFFFFFFFFFF, 2'361'000'000, 7'813'106'342'104'850'324},
-
- // boundry conditions check
- TestCaseParams{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 1000000000},
-
- // when result overflow it will return 0
- TestCaseParams{0xFFFFFFFFFFFFFFFF, 1, 0},
- TestCaseParams{0xFFFFFFFFFFFFFFFF, 1'000'000'000, 0},
- TestCaseParams{0xFFFFFFFFF, 1, 0},
-
- // Zero tick per sec
- TestCaseParams{3462637232, 0, 0}));
-
-TEST_P(HighPrecisionLocalSteadyClock, TimePointIsNotNegative)
-{
- RecordProperty("Description",
- "This test verifies if HighPrecisionLocalSteadyClock::Now() returns tick in nanoseconds"
- "based on equation: time point in nano = tick count * 1e9 / tick per second");
- RecordProperty("Verifies", "28819169, 28823471");
- RecordProperty("TestType", "Requirements-based test");
- RecordProperty("DerivationTechnique", "Analysis of requirements");
- RecordProperty("ASIL", "QM");
- RecordProperty("Priority", "3");
-
- const auto params{GetParam()};
-
- score:os::qnx::NeutrinoMock neutrinoMock;
- score:os::qnx::Neutrino::set_testing_instance(neutrinoMock);
-
- EXPECT_CALL(neutrinoMock, ClockCycles()).Times(Exactly(1)).WillOnce(Return(params.tickCount));
-
- EXPECT_CALL(TickProviderMock::GetMockInstance(), GetClockCyclesPerSec())
- .Times(Exactly(1))
- .WillOnce(Return(params.tickPerSec));
-
- std::unique_ptr qclock =
- std::make_unique();
- const auto time_point = qclock->Now();
-
- EXPECT_EQ(params.tickInNano, time_point.time_since_epoch().count());
-}
-
-TEST(HighPrecisionLocalSteadyClock, WhenClockCyclesFailToProvideValidValueShouldReturnZero)
-{
- RecordProperty("Description",
- "This test performs fault injection by simulating Neutrino::ClockCycles() returning zero "
- "to verify that HighPrecisionLocalSteadyClock::Now() handles the system call failure gracefully "
- "and returns a valid non-negative time point");
- RecordProperty("Verifies", "score::time::HighPrecisionLocalSteadyClock::Now()");
- RecordProperty("TestType", "Fault injection test");
- RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience");
- RecordProperty("ASIL", "QM");
- RecordProperty("Priority", "3");
-
- // Define constants
- constexpr uint64_t FAILURE = 0;
- constexpr uint64_t MINIMAL_VALID_CLOCK_CYCLES_PER_SEC = 10;
-
- using FactoryImpl = score::time::HighPrecisionLocalSteadyClock::FactoryImpl;
- using QClock = score::time::details::qtime::QClock;
- using TickProviderMock = score::time::details::qtime::TickProviderMock;
-
- TickProviderMock::CreateMockInstance();
- auto& tickProvider = TickProviderMock::GetMockInstance();
-
- score:os::qnx::NeutrinoMock neutrinoMock;
- score:os::qnx::Neutrino::set_testing_instance(neutrinoMock);
-
- // Simulate ClockCycles() failure → returns 0
- EXPECT_CALL(neutrinoMock, ClockCycles()).WillOnce(Return(FAILURE));
- EXPECT_CALL(tickProvider, GetClockCyclesPerSec()).WillOnce(Return(MINIMAL_VALID_CLOCK_CYCLES_PER_SEC));
-
- FactoryImpl factory{};
- auto clock = factory.CreateHighPrecisionLocalSteadyClock();
- ASSERT_NE(clock, nullptr);
-
- auto qclock = dynamic_cast(clock.get());
- ASSERT_NE(qclock, nullptr);
-
- auto time_point = qclock->Now();
- EXPECT_EQ(time_point.time_since_epoch().count(), 0U);
-
- TickProviderMock::DestroyMockInstance();
-}
-
-TEST(HighPrecisionLocalSteadyClock, WhenClockCyclesProvideValidValueShouldReturnValidTimePoint)
-{
- // Define constants
- constexpr uint64_t VALID_CLOCK_CYCLES_PER_SECOND = 100;
- constexpr uint64_t VALID_CLOCK_CYCLES = 25;
-
- using FactoryImpl = score::time::HighPrecisionLocalSteadyClock::FactoryImpl;
- using QClock = score::time::details::qtime::QClock;
- using TickProviderMock = score::time::details::qtime::TickProviderMock;
-
- // Create TickProviderMock singleton instance
- TickProviderMock::CreateMockInstance();
- auto& tickProvider = TickProviderMock::GetMockInstance();
-
- // Create NeutrinoMock to simulate system ClockCycles() API
- score:os::qnx::NeutrinoMock neutrinoMock;
- score:os::qnx::Neutrino::set_testing_instance(neutrinoMock);
-
- // Set expectations for TickProviderMock and NeutrinoMock
- EXPECT_CALL(tickProvider, GetClockCyclesPerSec()).WillOnce(Return(VALID_CLOCK_CYCLES_PER_SECOND));
- EXPECT_CALL(neutrinoMock, ClockCycles()).WillOnce(Return(VALID_CLOCK_CYCLES));
-
- // Get concrete factory implementation and create the clock
- FactoryImpl factory{};
- auto clock = factory.CreateHighPrecisionLocalSteadyClock();
- ASSERT_NE(clock, nullptr);
-
- auto qclock = dynamic_cast(clock.get());
- ASSERT_NE(qclock, nullptr);
-
- // Call Now() and verify tick count to nanoseconds conversion
- auto time_point = qclock->Now();
- EXPECT_EQ(time_point.time_since_epoch().count(),
- VALID_CLOCK_CYCLES * 1'000'000'000U / VALID_CLOCK_CYCLES_PER_SECOND);
-
- // Destroy TickProviderMock to clean up singleton state
- TickProviderMock::DestroyMockInstance();
-}
-
-} // namespace test
-} // namespace details
-} // namespace time
-} // namespace score
diff --git a/score/time/HighPrecisionLocalSteadyClock/details/qtime/tick_provider_mock.h b/score/time/HighPrecisionLocalSteadyClock/details/qtime/tick_provider_mock.h
deleted file mode 100644
index 491706c..0000000
--- a/score/time/HighPrecisionLocalSteadyClock/details/qtime/tick_provider_mock.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2026 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Apache License Version 2.0 which is available at
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * SPDX-License-Identifier: Apache-2.0
- ********************************************************************************/
-#ifndef SCORE_TIME_HIGHPRECISIONLOCALSTEADYCLOCK_DETAILS_TICK_PROVIDER_MOCK_H
-#define SCORE_TIME_HIGHPRECISIONLOCALSTEADYCLOCK_DETAILS_TICK_PROVIDER_MOCK_H
-
-#include "score/time/HighPrecisionLocalSteadyClock/details/qtime/tick_provider.h"
-
-#include
-#include
-
-namespace score
-{
-namespace time
-{
-namespace details
-{
-namespace qtime
-{
-
-class TickProviderMock
-{
- public:
- MOCK_METHOD(std::uint64_t, GetClockCyclesPerSec, (), ());
-
- MOCK_METHOD(std::uint64_t, ClockCycles, (), ());
-
- static TickProviderMock& GetMockInstance() noexcept
- {
- const std::lock_guard guard{mutex};
- assert(mock_instance.operator bool());
- return *mock_instance;
- }
- static void CreateMockInstance()
- {
- const std::lock_guard guard{mutex};
- mock_instance = std::make_unique();
- }
- static void DestroyMockInstance() noexcept
- {
- const std::lock_guard guard{mutex};
- testing::Mock::VerifyAndClearExpectations(mock_instance.get());
- mock_instance.reset();
- }
-
- private:
- static std::unique_ptr mock_instance;
- static std::mutex mutex;
-};
-
-} // namespace qtime
-} // namespace details
-} // namespace time
-} // namespace score
-
-#endif // #define SCORE_TIME_HIGHPRECISIONLOCALSTEADYCLOCK_DETAILS_TICK_PROVIDER_MOCK_H
diff --git a/score/time/HighPrecisionLocalSteadyClock/details/system_clock/system_clock.h b/score/time/HighPrecisionLocalSteadyClock/details/system_clock/system_clock.h
deleted file mode 100644
index 7f3466b..0000000
--- a/score/time/HighPrecisionLocalSteadyClock/details/system_clock/system_clock.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2026 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Apache License Version 2.0 which is available at
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * SPDX-License-Identifier: Apache-2.0
- ********************************************************************************/
-#ifndef SCORE_TIME_HIGHPRECISIONLOCALSTEADYCLOCK_DETAILS_SYSTEMCLOCK_SYSTEMCLOCK_H
-#define SCORE_TIME_HIGHPRECISIONLOCALSTEADYCLOCK_DETAILS_SYSTEMCLOCK_SYSTEMCLOCK_H
-
-#include "score/time/HighPrecisionLocalSteadyClock/high_precision_local_steady_clock.h"
-
-namespace score
-{
-namespace time
-{
-namespace details
-{
-namespace sys_time
-{
-
-///
-/// \brief The class represents quasi-realtime clock that offers nanosecond resolution in PLP format.
-///
-class SystemClock final : public ::score::time::HighPrecisionLocalSteadyClock
-{
- public:
- SystemClock() noexcept;
- SystemClock& operator=(const SystemClock&) noexcept = delete;
- SystemClock& operator=(SystemClock&&) noexcept = delete;
- SystemClock(const SystemClock&) noexcept = delete;
- SystemClock(SystemClock&&) noexcept = delete;
- ~SystemClock() noexcept override;
-
- /// \brief Method for obtaining the current clock value of the HPLSC.
- ///
- /// \details overrides ::score::time::HighPrecisionLocalSteadyClock::Now()
- ///
- /// \return current clock value and its status of the underlying timebase
- ///
- time_point Now() noexcept override;
-};
-
-} // namespace sys_time
-} // namespace details
-} // namespace time
-} // namespace score
-
-#endif // #ifndef SCORE_TIME_HIGHPRECISIONLOCALSTEADYCLOCK_DETAILS_SYSTEMCLOCK_SYSTEMCLOCK_H
diff --git a/score/time/HighPrecisionLocalSteadyClock/factory.h b/score/time/HighPrecisionLocalSteadyClock/factory.h
deleted file mode 100644
index 7452ddd..0000000
--- a/score/time/HighPrecisionLocalSteadyClock/factory.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2026 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Apache License Version 2.0 which is available at
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * SPDX-License-Identifier: Apache-2.0
- ********************************************************************************/
-#ifndef SCORE_TIME_HIGHPRECISIONLOCALSTEADYCLOCK_FACTORY_H
-#define SCORE_TIME_HIGHPRECISIONLOCALSTEADYCLOCK_FACTORY_H
-
-#include "score/time/HighPrecisionLocalSteadyClock/high_precision_local_steady_clock.h"
-
-#include
-
-namespace score
-{
-namespace time
-{
-///
-/// \brief Interface class for factory providing HighPrecisionLocalSteadyClock objects.
-///
-class HighPrecisionLocalSteadyClock::Factory
-{
- public:
- constexpr Factory() noexcept = default;
- constexpr Factory(Factory&&) noexcept = delete;
- constexpr Factory(const Factory&) noexcept = delete;
- constexpr Factory& operator=(Factory&&) & noexcept = delete;
- constexpr Factory& operator=(const Factory&) & noexcept = delete;
- virtual ~Factory() noexcept;
-
- /// \brief Method for obtaining access to the pre-configured timebase.
- ///
- /// \return unique_ptr to the HighPrecisionLocalSteadyClock object
- ///
- virtual std::unique_ptr CreateHighPrecisionLocalSteadyClock() const = 0;
-};
-
-} // namespace time
-} // namespace score
-
-#endif // #ifndef SCORE_TIME_HIGHPRECISIONLOCALSTEADYCLOCK_FACTORY_H
diff --git a/score/time/HighPrecisionLocalSteadyClock/factory_mock.h b/score/time/HighPrecisionLocalSteadyClock/factory_mock.h
deleted file mode 100644
index 25d2d62..0000000
--- a/score/time/HighPrecisionLocalSteadyClock/factory_mock.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2026 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Apache License Version 2.0 which is available at
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * SPDX-License-Identifier: Apache-2.0
- ********************************************************************************/
-#ifndef SCORE_TIME_HIGHPRECISIONLOCALSTEADYCLOCK_FACTORY_MOCK_H
-#define SCORE_TIME_HIGHPRECISIONLOCALSTEADYCLOCK_FACTORY_MOCK_H
-
-#include "score/time/HighPrecisionLocalSteadyClock/factory.h"
-
-#include