shape-runtime 0.3.0

Bytecode compiler, builtins, and runtime infrastructure for Shape
Documentation
/// @module std::iot::simulation
/// IoT Device Monitoring Simulation
///
/// Wrapper functions for monitoring IoT devices using the high-performance
/// simulation engine. Processes sensor readings and generates alerts.

// ===== Single Device Monitoring =====

/// Monitor a single device's sensor readings
///
/// @param readings - Table of sensor readings
/// @param config - MonitoringConfig for the device
///
/// @returns Simulation result with final device state and alerts
///
/// @example
/// let result = monitor_device(temperature_readings, {
///     device_id: "sensor-001",
///     thresholds: symmetric_thresholds(25.0, 5.0, 10.0),
///     offline_timeout: 300
/// });
pub fn monitor_device(readings, config) {
    let init_state = {
        device_id: config.device_id,
        status: "unknown",
        last_reading: 0.0,
        last_timestamp: 0,
        readings_count: 0,
        anomaly_count: 0,
        alert_count: 0,
        cumulative_error: 0.0,
        uptime_seconds: 0.0,
        // Running statistics for anomaly detection
        running_mean: 0.0,
        running_m2: 0.0  // For Welford's online variance algorithm
    };

    readings.simulate(
        |reading, state, idx| {
            let new_state = process_reading(reading, state, config, idx);
            new_state
        },
        {
            initial_state: init_state,
            collect_results: config.collect_alerts
        }
    )
}

/// Process a single reading and update state
pub fn process_reading(reading, state, config, idx) {
    // Update reading count
    let count = state.readings_count + 1;

    // Update running mean and variance (Welford's algorithm)
    let delta = reading.value - state.running_mean;
    let new_mean = state.running_mean + delta / count;
    let delta2 = reading.value - new_mean;
    let new_m2 = state.running_m2 + delta * delta2;

    // Calculate running standard deviation
    let running_std = if count > 1 {
        sqrt(new_m2 / (count - 1))
    } else {
        0.0
    };

    // Determine device status
    let new_status = "online";
    if reading.quality < 0.5 {
        new_status = "degraded";
    }

    // Check thresholds if configured
    let threshold_severity = "ok";
    if config.thresholds != None {
        threshold_severity = check_threshold_severity(reading.value, config.thresholds);
    }

    // Check for anomaly
    let is_anomaly = false;
    if count > 10 && running_std > 0 {
        let z_score = abs(reading.value - new_mean) / running_std;
        is_anomaly = z_score > config.anomaly_threshold;
    }

    // Update anomaly count
    let anomaly_count = state.anomaly_count;
    if is_anomaly {
        anomaly_count = anomaly_count + 1;
    }

    // Generate alert if needed
    let alert = None;
    let alert_count = state.alert_count;

    if threshold_severity == "critical" {
        alert = {
            severity: "critical",
            device_id: config.device_id,
            alert_type: "threshold",
            message: "Critical threshold exceeded",
            value: reading.value,
            index: idx
        };
        alert_count = alert_count + 1;
    } else if threshold_severity == "warning" {
        alert = {
            severity: "warning",
            device_id: config.device_id,
            alert_type: "threshold",
            message: "Warning threshold exceeded",
            value: reading.value,
            index: idx
        };
        alert_count = alert_count + 1;
    } else if is_anomaly {
        alert = {
            severity: "warning",
            device_id: config.device_id,
            alert_type: "anomaly",
            message: "Anomalous reading detected",
            value: reading.value,
            index: idx
        };
        alert_count = alert_count + 1;
    }

    // Build new state
    let new_state = {
        device_id: config.device_id,
        status: new_status,
        last_reading: reading.value,
        last_timestamp: reading.timestamp,
        readings_count: count,
        anomaly_count: anomaly_count,
        alert_count: alert_count,
        cumulative_error: state.cumulative_error,
        uptime_seconds: state.uptime_seconds,
        running_mean: new_mean,
        running_m2: new_m2
    };

    // Return with alert if generated
    if alert != None {
        { state: new_state, result: alert }
    } else {
        new_state
    }
}

/// Check value against thresholds and return severity level
pub fn check_threshold_severity(value, thresholds) {
    if value >= thresholds.high_critical || value <= thresholds.low_critical {
        "critical"
    } else if value >= thresholds.high_warning || value <= thresholds.low_warning {
        "warning"
    } else {
        "ok"
    }
}

// ===== Multi-Sensor Monitoring =====

/// Monitor multiple correlated sensors
///
/// @param sensors - Object mapping sensor names to reading series
/// @param config - Monitoring configuration
/// @param correlation_check - Optional function to check cross-sensor correlations
///
/// @example
/// let result = monitor_sensors(
///     { temperature: temp_readings, pressure: pressure_readings },
///     config,
///     (ctx, state) => {
///         // Check for dangerous temp+pressure combination
///         if ctx.temperature > 80 && ctx.pressure > 100 {
///             { alert: "critical", message: "Dangerous conditions" }
///         } else {
///             None
///         }
///     }
/// );
pub fn monitor_sensors(sensors, config, correlation_check = None) {
    let init_state = {
        sensor_states: {},
        total_readings: 0,
        total_anomalies: 0,
        total_alerts: 0,
        correlation_alerts: 0
    };

    simulate_correlated(
        sensors,
        |ctx, state, idx| {
            let new_state = state;
            new_state.total_readings = state.total_readings + 1;

            // Check correlation if provided
            let alert = None;
            if correlation_check != None {
                let check_result = correlation_check(ctx, state);
                if check_result != None && check_result.alert != None {
                    alert = {
                        severity: check_result.alert,
                        alert_type: "correlation",
                        message: check_result.message,
                        index: idx
                    };
                    new_state.correlation_alerts = state.correlation_alerts + 1;
                    new_state.total_alerts = state.total_alerts + 1;
                }
            }

            if alert != None {
                { state: new_state, result: alert }
            } else {
                new_state
            }
        },
        { initial_state: init_state }
    )
}

// ===== Monitoring Report =====

/// Generate monitoring summary report
pub fn monitoring_report(result) {
    let state = result.final_state;
    let alerts = result.results;

    let critical_count = 0;
    let warning_count = 0;

    for alert in alerts {
        if alert.severity == "critical" {
            critical_count = critical_count + 1;
        } else if alert.severity == "warning" {
            warning_count = warning_count + 1;
        }
    }

    {
        device_id: state.device_id,
        status: state.status,
        readings_processed: state.readings_count,
        anomalies_detected: state.anomaly_count,
        total_alerts: state.alert_count,
        critical_alerts: critical_count,
        warning_alerts: warning_count,
        last_reading: state.last_reading,
        mean_reading: state.running_mean
    }
}

/// Print monitoring report
pub fn print_monitoring_report(report) {
    print("=== Device Monitoring Report ===");
    print("Device: " + report.device_id);
    print("Status: " + report.status);
    print("");
    print("Readings Processed: " + report.readings_processed);
    print("Anomalies Detected: " + report.anomalies_detected);
    print("");
    print("Total Alerts: " + report.total_alerts);
    print("  Critical: " + report.critical_alerts);
    print("  Warning: " + report.warning_alerts);
    print("");
    print("Last Reading: " + report.last_reading);
    print("Mean Reading: " + report.mean_reading);
}