Skip to main content

Monitor

Struct Monitor 

Source
pub struct Monitor {
    pub config: MonitorConfig,
    pub pauses: Pauses,
    pub workdays: Workdays,
    pub last_activity: Arc<Mutex<Instant>>,
    pub activity_start: Arc<Mutex<Option<Instant>>>,
    /* private fields */
}
Expand description

The core activity monitor responsible for tracking user presence and managing workday and pause records.

Orchestrates all aspects of activity monitoring, from low-level input detection to high-level workday management.

Β§Thread Safety

The monitor uses thread-safe primitives to coordinate between:

  • Input Thread: Captures keyboard/mouse events via rdev
  • Monitor Thread: Runs the main monitoring loop
  • Shared State: Activity timestamps and workday tracking

Β§Configuration Impact

All timing behavior is controlled by the MonitorConfig:

  • Responsiveness: Lower poll_interval = more responsive state changes
  • Sensitivity: Lower pause_threshold = more sensitive pause detection
  • Workday Logic: Higher activity_threshold = more deliberate workday starts
  • Data Quality: Higher min_* values = cleaner, less noisy data

FieldsΒ§

Β§config: MonitorConfig

Configuration settings for the monitor, such as thresholds.

This configuration controls all timing and behavior aspects of the monitor. Changes to this configuration require restarting the monitor to take effect, as the values are used throughout the monitoring loop.

Β§pauses: Pauses

Database interface for managing pause records.

This interface handles all pause-related database operations:

  • Recording pause start times when inactivity is detected
  • Recording pause end times when activity resumes
  • Querying existing pause data for validation and reporting
Β§workdays: Workdays

Database interface for managing workday records.

This interface handles workday lifecycle management:

  • Creating new workday records when sustained activity is detected
  • Updating workday end times as activity continues
  • Querying workday status for duplicate prevention
Β§last_activity: Arc<Mutex<Instant>>

Timestamp of the last detected user activity (keyboard, mouse).

This timestamp is continuously updated by the input event listener running in a separate thread. It’s protected by a Mutex for thread-safe access between the input thread and the monitoring loop.

The timestamp is used to:

  • Calculate inactivity duration for pause detection
  • Determine if activity is β€œrecent” for state transitions
  • Provide timing information for workday management
Β§activity_start: Arc<Mutex<Option<Instant>>>

Optional timestamp marking the beginning of a period of sustained activity.

This field implements a β€œsustained activity” detection mechanism to prevent false workday starts from brief, accidental input events. The timestamp is:

  • Set when activity begins after a period of inactivity
  • Reset to None when a pause begins or workday is created
  • Used to calculate activity duration for workday start logic

Β§Workday Start Logic

A workday is created when:

  1. activity_start is set (continuous activity period began)
  2. Duration since activity_start exceeds activity_threshold
  3. No workday record exists for the current date

ImplementationsΒ§

SourceΒ§

impl Monitor

Source

pub fn new(config: MonitorConfig) -> Result<Self>

Creates a new Monitor instance.

This constructor initializes all monitor components and sets up the background input event listener. It performs several critical setup operations that are essential for proper monitoring functionality.

Β§Initialization Process
  1. Database Connections: Establishes connections to workday and pause databases
  2. Shared State: Creates thread-safe containers for activity tracking
  3. Input Listener: Spawns background thread for keyboard/mouse monitoring
  4. State Setup: Initializes monitor in Active state
Β§Input Event Handling

The constructor spawns a dedicated thread running rdev::listen() to capture:

  • Keyboard Events: Key presses and releases
  • Mouse Events: Button clicks, movements, and scroll wheel
  • Timestamp Updates: Continuous activity timestamp maintenance
Β§Thread Architecture
Main Thread          Input Thread
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Monitor   │────▢│  rdev::listen() β”‚
β”‚    Loop     β”‚     β”‚   (keyboard/    β”‚
β”‚             │◀────│     mouse)      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚                      β”‚
       └──── Shared State β”€β”€β”€β”€β”˜
       (last_activity, activity_start)
Β§Error Handling

The input listener includes error handling for:

  • Input device access failures
  • Platform-specific event capture issues
  • Thread communication problems

Errors in the input thread are logged but don’t crash the main monitor, allowing for graceful degradation when input monitoring isn’t available.

Β§Arguments
  • config - The MonitorConfig containing timing and behavior settings
Β§Returns

Returns Ok(Monitor) with a fully initialized monitor ready to start the monitoring loop, or an error if database initialization fails.

Β§Error Scenarios
  • Database Connection: Cannot connect to SQLite database files
  • Database Schema: Database schema is incompatible or corrupted
  • File Permissions: Cannot read/write database files
  • Resource Exhaustion: System cannot create necessary threads or allocate memory
Β§Examples
use kasl::libs::config::MonitorConfig;
use kasl::libs::monitor::Monitor;

// Create monitor with default configuration
let config = MonitorConfig::default();
let mut monitor = Monitor::new(config)?;

// Start monitoring
monitor.run().await?;
Source

pub async fn run(&mut self) -> Result<()>

Runs the main monitoring loop.

This asynchronous function implements the core monitoring logic that runs continuously until the monitor is stopped. It orchestrates state management, activity detection, and database operations in a coordinated fashion.

Β§Loop Architecture

The monitoring loop operates on a fixed polling interval and performs these operations each cycle:

  1. Activity Detection: Check if recent input activity occurred
  2. State Evaluation: Determine appropriate state based on activity
  3. State Transitions: Handle transitions between Active and InPause
  4. Workday Management: Ensure workday records are properly maintained
  5. Sleep: Wait for the next polling interval
Β§State Machine Logic
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Monitor Loop                             β”‚
β”‚                                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    Activity?    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚   Active    │─────────No─────▢│  handle_inactivity  β”‚   β”‚
β”‚  β”‚             β”‚                 β”‚                     β”‚   β”‚
β”‚  β”‚             │◀────────────────│  (start pause)      β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚        β”‚                                                   β”‚
β”‚     Activity                                                β”‚
β”‚        β”‚                                                   β”‚
β”‚        β–Ό                                                   β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                   β”‚
β”‚  β”‚ ensure_workday_     β”‚                                   β”‚
β”‚  β”‚ started             β”‚                                   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                   β”‚
β”‚                                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    Activity?    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  InPause    │─────────Yes────▢│handle_return_from_  β”‚   β”‚
β”‚  β”‚             β”‚                 β”‚pause                β”‚   β”‚
β”‚  β”‚             │◀────────────────│                     β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                 β”‚  (end pause)        β”‚   β”‚
β”‚                                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Β§Configuration-Driven Behavior

The loop behavior is entirely controlled by the MonitorConfig:

  • poll_interval: Controls loop frequency and CPU usage
  • pause_threshold: Determines when inactivity becomes a pause
  • activity_threshold: Controls workday start detection sensitivity
  • Special Case: If pause_threshold is 0, monitoring is disabled
Β§Database Operations

The loop performs these database operations as needed:

  • Workday Creation: When sustained activity is detected
  • Pause Recording: When inactivity exceeds threshold
  • Pause Completion: When activity resumes after pause
  • Workday Updates: Continuous end time updates during activity
Β§Error Handling

Database errors during the monitoring loop are handled gracefully:

  • Errors are logged with detailed information
  • The loop continues running to avoid service interruption
  • Critical errors are propagated to stop the monitor
Β§Performance Characteristics
  • CPU Usage: Directly proportional to polling frequency (1/poll_interval)
  • Memory Usage: Constant, no accumulation of historical data
  • I/O Operations: Minimal, only database writes for state changes
  • Network Usage: None during monitoring
Β§Returns

Returns Ok(()) when the monitoring loop is explicitly stopped, or an error if a critical database operation fails that prevents continued monitoring.

Β§Error Scenarios
  • Database Connection Loss: SQLite database becomes unavailable
  • Disk Space Exhaustion: Cannot write to database files
  • Permission Changes: Database files become read-only
  • System Resource Exhaustion: Cannot allocate memory for operations
Β§Examples
use kasl::libs::config::MonitorConfig;
use kasl::libs::monitor::Monitor;

// Start monitoring with custom configuration
let config = MonitorConfig {
    poll_interval: 1000,      // Check every second
    pause_threshold: 120,     // Pause after 2 minutes
    activity_threshold: 30,   // Workday starts after 30s
    ..Default::default()
};

let mut monitor = Monitor::new(config)?;
monitor.run().await?; // Runs indefinitely
Source

pub fn detect_activity(&self) -> bool

Checks if any user activity has been detected within the last poll interval.

This method implements the core activity detection logic that drives the monitor’s state machine. It determines whether the user is currently active based on the recency of input events captured by the background input listener thread.

Β§Detection Logic

Activity is considered β€œdetected” when the time elapsed since the last input event is less than the polling interval. This approach ensures that activity detection is synchronized with the monitoring loop’s polling frequency.

Timeline: ───●───●─────────●──────────────────▢
         input input   poll               now
                         ↑
                  elapsed < poll_interval
                    = Activity Detected
Β§Sensitivity Tuning

The detection sensitivity can be adjusted through configuration:

  • Lower poll_interval: More sensitive, detects brief activity
  • Higher poll_interval: Less sensitive, requires sustained activity
Β§Thread Safety

This method safely accesses the shared last_activity timestamp that is continuously updated by the input listener thread. The mutex protection ensures data consistency without blocking the input thread.

Β§Debug Logging

When debug mode is enabled (KASL_DEBUG=1), this method logs detailed timing information to help with troubleshooting and configuration tuning:

  • Elapsed time since last activity
  • Activity detection result
  • Timing relationship to poll interval
Β§Returns

Returns true if user activity was detected within the last poll interval, false if the user appears to be inactive.

Β§Performance Considerations

This method is called once per polling cycle and performs minimal work:

  • Single mutex lock/unlock operation
  • Simple timestamp arithmetic
  • Optional debug logging

The implementation is designed for frequent calling with minimal overhead.

Β§Examples
use kasl::libs::monitor::Monitor;
use kasl::libs::config::MonitorConfig;

let monitor = Monitor::new(MonitorConfig::default())?;

// Check for activity
if monitor.detect_activity() {
    println!("User is active");
} else {
    println!("User appears inactive");
}
Source

pub fn ensure_workday_started(&mut self, today: NaiveDate) -> Result<()>

Ensures that a workday record has been started for the current day if sustained activity is detected.

This method implements intelligent workday start detection to automatically create workday records when the user begins sustained work activity. It prevents false workday starts from brief, accidental input while ensuring that genuine work sessions are properly captured.

Β§Sustained Activity Logic

The workday start detection uses a multi-stage process:

  1. Activity Initiation: activity_start timestamp is set by input events
  2. Duration Check: Calculate time elapsed since activity started
  3. Threshold Validation: Compare duration against activity_threshold
  4. Workday Creation: Create workday record if threshold is exceeded
  5. Tracker Reset: Clear activity_start to prevent duplicate creation
Timeline: ──●───●───●───●──────────●─────▢
         start              threshold   now
        activity             exceeded
            ↑                   ↑
     activity_start        workday
        is set             created
Β§Duplicate Prevention

The method includes multiple safeguards against duplicate workday creation:

  • Existence Check: Verifies no workday record exists for the date
  • Activity Validation: Requires valid activity_start timestamp
  • Threshold Enforcement: Demands sustained activity duration
  • Reset Mechanism: Clears tracker after successful creation
Β§Configuration Impact

The activity_threshold setting controls workday start sensitivity:

  • Lower Values (10-30s): Quick workday detection, good for focused work
  • Higher Values (60-120s): Conservative detection, avoids false starts
  • Very High Values (300s+): Only detects deliberate work sessions
Β§Database Operations

When creating a workday, the method:

  1. Queries for existing workday records on the target date
  2. Creates a new workday record with the current timestamp
  3. Handles any database errors gracefully
Β§Error Handling

Database errors during workday creation are logged but don’t crash the monitor. This ensures continuous monitoring even if individual database operations encounter issues.

Β§State Management

After successful workday creation:

  • activity_start is reset to None
  • Monitor continues tracking for pause detection
  • Future activity updates the workday end time
  • No additional workday records are created for the date
Β§Arguments
  • today - The current date for workday record creation
Β§Returns

Returns Ok(()) if workday management completed successfully, or an error if critical database operations fail.

Β§Error Scenarios
  • Database Connection: Cannot connect to workdays database
  • Database Query: Cannot check for existing workday records
  • Database Insert: Cannot create new workday record
  • Date Validation: Invalid date format or system clock issues
Β§Database Schema Impact

This method may create records in the workdays table:

INSERT INTO workdays (date, start_time) VALUES (?, ?);
Β§Examples
// This method is called automatically during the monitoring loop
// when the user is active and no workday exists for the current date

// Example: User starts work at 9:00 AM
// - First input at 9:00:00 sets activity_start
// - Continued input until 9:00:30
// - If activity_threshold = 30s, workday is created at 9:00:30
// - Workday start_time is recorded as current timestamp

Auto Trait ImplementationsΒ§

Blanket ImplementationsΒ§

SourceΒ§

impl<T> Any for T
where T: 'static + ?Sized,

SourceΒ§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
SourceΒ§

impl<T> Borrow<T> for T
where T: ?Sized,

SourceΒ§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
SourceΒ§

impl<T> BorrowMut<T> for T
where T: ?Sized,

SourceΒ§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
SourceΒ§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

SourceΒ§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

SourceΒ§

impl<T> From<T> for T

SourceΒ§

fn from(t: T) -> T

Returns the argument unchanged.

SourceΒ§

impl<T> Instrument for T

SourceΒ§

fn instrument(self, span: Span) -> Instrumented<Self> β“˜

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
SourceΒ§

fn in_current_span(self) -> Instrumented<Self> β“˜

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
SourceΒ§

impl<T, U> Into<U> for T
where U: From<T>,

SourceΒ§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

SourceΒ§

impl<T> PolicyExt for T
where T: ?Sized,

SourceΒ§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
SourceΒ§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
SourceΒ§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

SourceΒ§

impl<T> Same for T

SourceΒ§

type Output = T

Should always be Self
SourceΒ§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

SourceΒ§

type Error = Infallible

The type returned in the event of a conversion error.
SourceΒ§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
SourceΒ§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

SourceΒ§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
SourceΒ§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
SourceΒ§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

SourceΒ§

fn vzip(self) -> V

SourceΒ§

impl<T> WithSubscriber for T

SourceΒ§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> β“˜
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
SourceΒ§

fn with_current_subscriber(self) -> WithDispatch<Self> β“˜

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more