modality_trace_recorder_plugin/
trace_recorder.rs

1use crate::{AttrKeyIndex, Client, ContextHandle, TraceRecorderConfig};
2use async_trait::async_trait;
3use modality_api::{AttrVal, Nanoseconds};
4use modality_ingest_client::IngestError;
5use modality_ingest_protocol::InternedAttrKey;
6use std::collections::HashMap;
7use thiserror::Error;
8use trace_recorder_parser::{
9    snapshot, streaming,
10    time::{Frequency, Timestamp},
11    types::{ObjectHandle, STARTUP_TASK_NAME},
12};
13
14#[derive(Debug, Error)]
15pub enum Error {
16    #[error("Missing startup task ('{}') object properties", STARTUP_TASK_NAME)]
17    MissingStartupTaskProperties,
18
19    #[error("Failed to locate task properties for object handle {0}")]
20    TaskPropertiesLookup(ObjectHandle),
21
22    #[error("Failed to locate ISR properties for object handle {0}")]
23    IsrPropertiesLookup(ObjectHandle),
24
25    #[error(transparent)]
26    SnapshotTraceRecorder(#[from] snapshot::Error),
27
28    #[error(transparent)]
29    StreamingTraceRecorder(#[from] streaming::Error),
30
31    #[error("Encountered an error with the Deviant custom event configuration. {0}.")]
32    DeviantEvent(String),
33
34    #[error("Encountered an ingest client error. {0}")]
35    Ingest(#[from] IngestError),
36
37    #[error(
38        "Encountered and IO error while reading the input stream ({})",
39        .0.kind()
40    )]
41    Io(#[from] std::io::Error),
42}
43
44pub trait NanosecondsExt {
45    const ONE_SECOND: u64 = 1_000_000_000;
46
47    fn resolution_ns(&self) -> Option<Nanoseconds>;
48
49    /// Convert to nanosecond time base using the frequency if non-zero,
50    /// otherwise fall back to unit ticks
51    fn lossy_timestamp_ns<T: Into<Timestamp>>(&self, ticks: T) -> Nanoseconds {
52        let t = ticks.into();
53        self.resolution_ns()
54            .map(|res| Nanoseconds::from(t.get_raw() * res.get_raw()))
55            .unwrap_or_else(|| t.get_raw().into())
56    }
57}
58
59impl NanosecondsExt for Frequency {
60    fn resolution_ns(&self) -> Option<Nanoseconds> {
61        if self.is_unitless() {
62            None
63        } else {
64            Nanoseconds::from(Self::ONE_SECOND / u64::from(self.get_raw())).into()
65        }
66    }
67}
68
69pub struct TimelineDetails<TAK> {
70    pub name_key: TAK,
71    pub name: String,
72    pub description_key: TAK,
73    pub description: String,
74    pub object_handle_key: TAK,
75    pub object_handle: ObjectHandle,
76}
77
78#[async_trait]
79pub trait TraceRecorderExt<TAK: AttrKeyIndex, EAK: AttrKeyIndex> {
80    fn startup_task_handle(&self) -> Result<ObjectHandle, Error>;
81
82    fn object_handle(&self, obj_name: &str) -> Option<ObjectHandle>;
83
84    fn timeline_details(
85        &self,
86        handle: ContextHandle,
87        startup_task_name: Option<&str>,
88    ) -> Result<TimelineDetails<TAK>, Error>;
89
90    async fn setup_common_timeline_attrs(
91        &self,
92        cfg: &TraceRecorderConfig,
93        client: &mut Client<TAK, EAK>,
94    ) -> Result<HashMap<InternedAttrKey, AttrVal>, Error>;
95}