tree: 0503fd5eef85a7d6f23b3a47a33149dfe775b562
  1. dbus_sensors/
  2. i2c_sensors/
  3. sensor_configs/
  4. sensor_database/
  5. mod.rs
  6. README.md
streaming_telemetry/src/sensor_database/README.md

Serverless Sensor Database

Introduction

The Serverless Sensor Database aims to simplify sensor management by eliminating the need for separate systemd services, as the approach taken by the OpenBMC D-Bus sensor daemon today. This solution removes the daemon layer and inter-process communication (IPC), resulting in a more straightforward implementation.

Northbound API

The sensor database exposes three main APIs:

  1. add_sensor(): Adds (or removes) a sensor to the database, which will then poll the sensor hardware for value updates.
  2. query_sensor(): Queries sensors by secondary key (one of sensor properties, such as the FRU it belongs to) or by primary key: sensor name.
  3. subscribe(): Allows subscribers to receive sensor events when sensor readings are updated or cross a threshold.

Southbound API

Clients must provide a method to poll the sensor value when adding a sensor to the database, as defined by the asynchronous trait AsyncReadSensor. For I2C sensors, this can be a simple wrapper to read the I2C device via the sysfs interface. This interface abstraction facilitates integration with third-party vendors.

Use case example

The src/main.rs file demonstrates a complete use case from a client's perspective.
Typically, one client handles the database creation, but there is currently no on how many clients can query and subscribe simultaneously.

  1. Adding sensors
    Sensors can be added to the sensor database, and the update task will run in the background automatically:

    // Mock implementation of AsyncReadSensor
    struct MockSensorReader {
        pub is_polling: std::sync::atomic::AtomicBool,
    }
    
    #[async_trait::async_trait]
    impl AsyncReadSensor for MockSensorReader {
        async fn read_value(
            &self,
            sampling_interval: Duration,
            reporting_interval: Duration,
        ) -> Result<Vec<(f64, SystemTime)>, std::io::Error> {
            tokio::time::sleep(sampling_interval).await;
            let now = SystemTime::now();
            let mut values = Vec::new();
            let mut current_time = now;
            while current_time - now < reporting_interval {
                values.push((rand::random::<f64>() * 100.0, current_time));
                current_time += sampling_interval;
            }
            Ok(values)
        }
    
        fn start_polling(&self) {
            self.is_polling.store(true, Ordering::Relaxed);
        }
    
        fn stop_polling(&self) {
            self.is_polling.store(false, Ordering::Relaxed);
        }
    }
    
    // The sensor database can be cloned and shared by other thread, task freely
    let sensor_database = Arc::new(SensorDatabase::default());
    
    let mock_reader = Arc::new(MockSensorReader);
    
    // Add sensors to the database
    // A sensor can have arbitrary number of property as key/value pair
    // The property key names are not restricted, though it should follow the Redfish data scheme
    let sensor1 = Sensor::new(
        "Sensor1".to_string(),
        Some(vec![
            ("FRU".to_string(), "CPU".to_string()),
            ("ReadingType".to_string(), "Temperature".to_string()),
        ]),
        25.5,
        mock_reader.clone(),
        Duration::from_secs(1),
        Duration::from_millis(100),
        Duration::from_secs(5),
    );
    sensor_database.add_sensor(sensor1).await;
    
    let sensor2 = Sensor::new(
        "Sensor2".to_string(),
        Some(vec![
            ("FRU".to_string(), "GPU".to_string()),
            ("ReadingType".to_string(), "Temperature".to_string()),
        ]),
        28.2,
        mock_reader.clone(),
        Duration::from_secs(2),
        Duration::from_millis(200),
        Duration::from_secs(5),
    );
    sensor_database.add_sensor(sensor2).await;
    
    let sensor3 = Sensor::new(
        "Sensor3".to_string(),
        Some(vec![
            ("FRU".to_string(), "CPU".to_string()),
            ("ReadingType".to_string(), "Voltage".to_string()),
        ]),
        3.3,
        mock_reader.clone(),
        Duration::from_secs(3),
        Duration::from_millis(300),
        Duration::from_secs(5),
    );
    sensor_database.add_sensor(sensor3).await;
    
  2. Query sensors

    2.1 By primary key:

    // Query sensor by primary keys: all sensors
    if let Ok(sensors) = sensor_database.query_sensors_by_names(&[]) {
        println!("All sensors in database:");
        for sensor in sensors {
            println!("{:?}", sensor);
        }
    }
    
    // Query sensor by primary keys: one sensor
    if let Ok(sensors) = sensor_database.query_sensors_by_names(&["Sensor1"]) {
        assert!(sensors.len() <= 1);
    }
    

    2.2 By secondary keys:

    // Query all availabe secondary keys in the database
    let keys = sensor_database.query_all_secondary_keys();
    println!("secondary keys: {:?}", keys);
    
    // Query sensors by secondary key
    if let Ok(sensors) = sensor_database.query_sensors_by_property(("FRU", "CPU")) {
        for sensor in sensors {
            println!("Sensor with FRU CPU: {:?}", sensor);
        }
    }
    
  3. Subscribe to sensor events

    3.1 Query sensors by reading type, then subscribe sensor event from all of them

    if let Ok(temperature_sensors) =
        sensor_database.query_sensors_by_property(("ReadingType", "Temperature"))
    {
        for sensor in temperature_sensors {
            println!("Subscribe to temperature sensor: {:?}", sensor);
            if let Some(mut subscription) =
                sensor_database.subscribe_events(&sensor.name, SubscriptionType::OnChange)
            {
                tokio::spawn(async move {
                    while let Some(events) = subscription.event_receiver.recv().await {
                        println!("Received {:?}", event);
                    }
                });
            }
        }
    }
    

    3.2 Subscribe to Periodical events, it will increase the polling rate of this sensor in this case

    if let Some(mut subscription) = sensor_database.subscribe_events(
        "Sensor2",
        SubscriptionType::Periodical(Duration::from_millis(500)),
    ) {
        let sensor_database_clone = sensor_database.clone();
        if let Ok(sensors) = sensor_database_clone.query_sensors_by_names(&["Sensor2"]) {
            println!("Sensor2 before subscription dropped: {:?}", sensors[0]);
        }
    
        tokio::spawn(async move {
            let timeout = tokio::time::sleep(Duration::from_secs(15));
            tokio::pin!(timeout);
    
            loop {
                tokio::select! {
                    _ = &mut timeout => {
                        drop(subscription);
                        println!("Subscription to Sensor2 dropped after 15 seconds");
                        break;
                    },
                    events = subscription.event_receiver.recv() => {
                        if let Some(events) = events {
                            println!("Periodical event: {:?}", event);
                        } else {
                            break;
                        }
                    }
                }
            }
    
            // After this subscription is dropped, polling rate should restore to default
            if let Ok(sensors) = sensor_database_clone.query_sensors_by_names(&["Sensor2"]) {
                println!("Sensor2 after subscription dropped: {:?}", sensors[0]);
            }
        });
    }
    

Sample output

All sensors in database:
Sensor { name: "Sensor2", secondary_keys: Some([("FRU", "GPU"), ("ReadingType", "Temperature")]), value: 28.2, default_update_interval: 2s, minimum_update_interval: 200ms, current_update_interval: 2s }
Sensor { name: "Sensor3", secondary_keys: Some([("FRU", "CPU"), ("ReadingType", "Voltage")]), value: 3.3, default_update_interval: 3s, minimum_update_interval: 300ms, current_update_interval: 3s }
Sensor { name: "Sensor1", secondary_keys: Some([("FRU", "CPU"), ("ReadingType", "Temperature")]), value: 25.5, default_update_interval: 1s, minimum_update_interval: 100ms, current_update_interval: 1s }
secondary keys: ["ReadingType", "FRU"]
Sensor with FRU CPU: Sensor { name: "Sensor1", secondary_keys: Some([("FRU", "CPU"), ("ReadingType", "Temperature")]), value: 25.5, default_update_interval: 1s, minimum_update_interval: 100ms, current_update_interval: 1s }
Sensor with FRU CPU: Sensor { name: "Sensor3", secondary_keys: Some([("FRU", "CPU"), ("ReadingType", "Voltage")]), value: 3.3, default_update_interval: 3s, minimum_update_interval: 300ms, current_update_interval: 3s }
Subscribe to temperature sensor: Sensor { name: "Sensor1", secondary_keys: Some([("FRU", "CPU"), ("ReadingType", "Temperature")]), value: 25.5, default_update_interval: 1s, minimum_update_interval: 100ms, current_update_interval: 1s }
Subscribe to temperature sensor: Sensor { name: "Sensor2", secondary_keys: Some([("FRU", "GPU"), ("ReadingType", "Temperature")]), value: 28.2, default_update_interval: 2s, minimum_update_interval: 200ms, current_update_interval: 2s }
Sensor2 before subscription dropped: Sensor { name: "Sensor2", secondary_keys: Some([("FRU", "GPU"), ("ReadingType", "Temperature")]), value: 28.2, default_update_interval: 2s, minimum_update_interval: 200ms, current_update_interval: 500ms }
Threshold event: SensorEvent { sensor_name: "Sensor3", event_type: "ReadingAboveUpperCriticalThreshold", value: 66.81906494537024, timestamp: SystemTime { tv_sec: 616376, tv_nsec: 949746575 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 21.584232735733732, timestamp: SystemTime { tv_sec: 616376, tv_nsec: 949765075 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 21.584232735733732, timestamp: SystemTime { tv_sec: 616376, tv_nsec: 949765075 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 69.1666621987969, timestamp: SystemTime { tv_sec: 616376, tv_nsec: 949819185 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 27.17841306446591, timestamp: SystemTime { tv_sec: 616376, tv_nsec: 949907735 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 84.47258342422754, timestamp: SystemTime { tv_sec: 616377, tv_nsec: 450807398 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 84.47258342422754, timestamp: SystemTime { tv_sec: 616377, tv_nsec: 450807398 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 52.19185780053795, timestamp: SystemTime { tv_sec: 616377, tv_nsec: 949638010 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 41.0184389080178, timestamp: SystemTime { tv_sec: 616377, tv_nsec: 950859630 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 41.0184389080178, timestamp: SystemTime { tv_sec: 616377, tv_nsec: 950859630 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 10.663082880221953, timestamp: SystemTime { tv_sec: 616378, tv_nsec: 450710813 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 19.332324110778732, timestamp: SystemTime { tv_sec: 616378, tv_nsec: 949541614 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 96.96464437129602, timestamp: SystemTime { tv_sec: 616378, tv_nsec: 950754015 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 96.96464437129602, timestamp: SystemTime { tv_sec: 616378, tv_nsec: 950754015 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 60.130064755036315, timestamp: SystemTime { tv_sec: 616379, tv_nsec: 450635678 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 70.11134209796973, timestamp: SystemTime { tv_sec: 616379, tv_nsec: 949471390 } }
Threshold event: SensorEvent { sensor_name: "Sensor3", event_type: "ReadingAboveUpperCriticalThreshold", value: 69.2936468310648, timestamp: SystemTime { tv_sec: 616379, tv_nsec: 949516641 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 23.670158981953605, timestamp: SystemTime { tv_sec: 616379, tv_nsec: 950686761 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 23.670158981953605, timestamp: SystemTime { tv_sec: 616379, tv_nsec: 950686761 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 35.64272633290464, timestamp: SystemTime { tv_sec: 616380, tv_nsec: 450565693 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 79.04166544362107, timestamp: SystemTime { tv_sec: 616380, tv_nsec: 949381665 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 78.73320899792597, timestamp: SystemTime { tv_sec: 616380, tv_nsec: 950587055 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 78.73320899792597, timestamp: SystemTime { tv_sec: 616380, tv_nsec: 950587055 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 83.97403520782987, timestamp: SystemTime { tv_sec: 616381, tv_nsec: 450610554 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 83.97403520782987, timestamp: SystemTime { tv_sec: 616381, tv_nsec: 450610554 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 12.907887112545191, timestamp: SystemTime { tv_sec: 616381, tv_nsec: 949571346 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 98.49627930286773, timestamp: SystemTime { tv_sec: 616381, tv_nsec: 950822646 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 98.49627930286773, timestamp: SystemTime { tv_sec: 616381, tv_nsec: 950822646 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 10.20421975311514, timestamp: SystemTime { tv_sec: 616382, tv_nsec: 450770329 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 49.60535847359011, timestamp: SystemTime { tv_sec: 616382, tv_nsec: 949697881 } }
Threshold event: SensorEvent { sensor_name: "Sensor3", event_type: "ReadingAboveUpperCriticalThreshold", value: 70.12447978660987, timestamp: SystemTime { tv_sec: 616382, tv_nsec: 949761681 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 37.2271559676151, timestamp: SystemTime { tv_sec: 616382, tv_nsec: 949913131 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 37.2271559676151, timestamp: SystemTime { tv_sec: 616382, tv_nsec: 949913131 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 96.12690188343063, timestamp: SystemTime { tv_sec: 616383, tv_nsec: 450912084 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 96.12690188343063, timestamp: SystemTime { tv_sec: 616383, tv_nsec: 450912084 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 46.05559067326017, timestamp: SystemTime { tv_sec: 616383, tv_nsec: 949793666 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 58.6062459835362, timestamp: SystemTime { tv_sec: 616383, tv_nsec: 949945386 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 90.1068155038148, timestamp: SystemTime { tv_sec: 616384, tv_nsec: 450840449 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 90.1068155038148, timestamp: SystemTime { tv_sec: 616384, tv_nsec: 450840449 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 97.66549110020112, timestamp: SystemTime { tv_sec: 616384, tv_nsec: 949781520 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 10.749746695592933, timestamp: SystemTime { tv_sec: 616384, tv_nsec: 949904521 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 98.59713397398946, timestamp: SystemTime { tv_sec: 616385, tv_nsec: 450419022 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 98.59713397398946, timestamp: SystemTime { tv_sec: 616385, tv_nsec: 450419022 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 39.108196890669, timestamp: SystemTime { tv_sec: 616385, tv_nsec: 949361553 } }
Threshold event: SensorEvent { sensor_name: "Sensor3", event_type: "ReadingAboveUpperCriticalThreshold", value: 44.311761498755544, timestamp: SystemTime { tv_sec: 616385, tv_nsec: 949460443 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 54.214033390729, timestamp: SystemTime { tv_sec: 616385, tv_nsec: 950586254 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 54.214033390729, timestamp: SystemTime { tv_sec: 616385, tv_nsec: 950586254 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 63.10464395868666, timestamp: SystemTime { tv_sec: 616386, tv_nsec: 450337215 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 12.46727393215803, timestamp: SystemTime { tv_sec: 616386, tv_nsec: 949295857 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 74.64159774182104, timestamp: SystemTime { tv_sec: 616386, tv_nsec: 950569378 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 74.64159774182104, timestamp: SystemTime { tv_sec: 616386, tv_nsec: 950569378 } }
Sensor1 removed
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 67.72627000633484, timestamp: SystemTime { tv_sec: 616387, tv_nsec: 450524749 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 10.61407360597495, timestamp: SystemTime { tv_sec: 616387, tv_nsec: 950665052 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 10.61407360597495, timestamp: SystemTime { tv_sec: 616387, tv_nsec: 950665052 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 73.03188841980563, timestamp: SystemTime { tv_sec: 616388, tv_nsec: 450580235 } }
Threshold event: SensorEvent { sensor_name: "Sensor3", event_type: "ReadingAboveUpperCriticalThreshold", value: 45.10623518566299, timestamp: SystemTime { tv_sec: 616388, tv_nsec: 949513705 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 75.90624780730381, timestamp: SystemTime { tv_sec: 616388, tv_nsec: 950773136 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 75.90624780730381, timestamp: SystemTime { tv_sec: 616388, tv_nsec: 950773136 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 36.24425676700527, timestamp: SystemTime { tv_sec: 616389, tv_nsec: 450797448 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 36.24425676700527, timestamp: SystemTime { tv_sec: 616389, tv_nsec: 450797448 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 35.18804590845163, timestamp: SystemTime { tv_sec: 616389, tv_nsec: 950786450 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 50.87769030250661, timestamp: SystemTime { tv_sec: 616390, tv_nsec: 450759950 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 50.87769030250661, timestamp: SystemTime { tv_sec: 616390, tv_nsec: 450759950 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 73.2571597068527, timestamp: SystemTime { tv_sec: 616390, tv_nsec: 950706282 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 26.875453446612042, timestamp: SystemTime { tv_sec: 616391, tv_nsec: 450640964 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 26.875453446612042, timestamp: SystemTime { tv_sec: 616391, tv_nsec: 450640964 } }
Threshold event: SensorEvent { sensor_name: "Sensor3", event_type: "ReadingAboveUpperCriticalThreshold", value: 96.91900929324757, timestamp: SystemTime { tv_sec: 616391, tv_nsec: 949590695 } }
Subscription to Sensor2 dropped after 15 seconds
Sensor2 after subscription dropped: Sensor { name: "Sensor2", secondary_keys: Some([("FRU", "GPU"), ("ReadingType", "Temperature")]), value: 26.875453446612042, default_update_interval: 2s, minimum_update_interval: 200ms, current_update_interval: 2s }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 28.443838708091974, timestamp: SystemTime { tv_sec: 616391, tv_nsec: 950837846 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 87.3475020265932, timestamp: SystemTime { tv_sec: 616391, tv_nsec: 952096976 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 87.54493122423006, timestamp: SystemTime { tv_sec: 616393, tv_nsec: 952624054 } }
Threshold event: SensorEvent { sensor_name: "Sensor3", event_type: "ReadingAboveUpperCriticalThreshold", value: 41.209779406863646, timestamp: SystemTime { tv_sec: 616394, tv_nsec: 949069079 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 11.548348109805461, timestamp: SystemTime { tv_sec: 616395, tv_nsec: 952321215 } }
Threshold event: SensorEvent { sensor_name: "Sensor3", event_type: "ReadingAboveUpperCriticalThreshold", value: 70.84722931073456, timestamp: SystemTime { tv_sec: 616397, tv_nsec: 949857082 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 51.72073433375517, timestamp: SystemTime { tv_sec: 616397, tv_nsec: 952071873 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 70.96642522743821, timestamp: SystemTime { tv_sec: 616399, tv_nsec: 952442080 } }
Threshold event: SensorEvent { sensor_name: "Sensor3", event_type: "ReadingAboveUpperCriticalThreshold", value: 91.40068010103276, timestamp: SystemTime { tv_sec: 616400, tv_nsec: 949875114 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 53.65360977964993, timestamp: SystemTime { tv_sec: 616401, tv_nsec: 952438499 } }
^CExiting...