الرئيسية قاعدة المعرفة البرمجة والمنطق مشروع تطبيقي: بناء نظام مراقبة صناعي متكامل بلغة Rust
البرمجة والمنطق

مشروع تطبيقي: بناء نظام مراقبة صناعي متكامل بلغة Rust

نظرة عامة والهندسة المعمارية

نبني نظام مراقبة صناعي بأربع مراحل:

قارئ المستشعرات ──→ معالج البيانات ──→ محرك الإنذارات ──→ لوحة المتابعة
  (sensor)           (processor)         (alarm)            (dashboard)
   ↓                    ↓                   ↓                   ↓
 قراءة خام         تصفية + معايرة      قواعد + إشعارات      عرض الحالة

كل مرحلة تستخدم مفاهيم تعلمناها: هياكل، سمات، أنماط، خيوط، async، واختبارات.

هيكل المشروع: مساحة عمل متعددة الحزم

نُطبّق ما تعلمناه في الدرس 11 عن مساحات العمل:

# Cargo.toml (جذر مساحة العمل)
[workspace]
members = [
    "sensor",
    "alarm",
    "dashboard",
]

[workspace.dependencies]
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
factory-monitor/
├── Cargo.toml              # مساحة العمل
├── sensor/
│   ├── Cargo.toml
│   └── src/lib.rs          # قراءة وتحقق
├── alarm/
│   ├── Cargo.toml
│   └── src/lib.rs          # قواعد وإشعارات
├── dashboard/
│   ├── Cargo.toml
│   └── src/main.rs         # نقطة الدخول + العرض
└── tests/
    └── integration.rs      # اختبارات النظام الكامل

وحدة المستشعرات: قراءة البيانات والتحقق منها

// sensor/src/lib.rs
use serde::{Serialize, Deserialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SensorType {
    Temperature,
    Pressure,
    Vibration,
}

#[derive(Debug, Clone)]
pub struct Reading {
    pub sensor_id: u32,
    pub sensor_type: SensorType,
    pub value: f64,
    pub timestamp: u64,
}

#[derive(Debug, PartialEq)]
pub enum ReadingError {
    OutOfRange { value: f64, min: f64, max: f64 },
    SensorOffline(u32),
    InvalidData,
}

// سمة تُعرّف سلوك أي مستشعر
pub trait SensorReader {
    fn read(&self) -> Result<Reading, ReadingError>;
    fn sensor_id(&self) -> u32;
}

// تحقق من صحة القراءة حسب النوع
pub fn validate(reading: &Reading) -> Result<(), ReadingError> {
    let (min, max) = match reading.sensor_type {
        SensorType::Temperature => (-40.0, 200.0),
        SensorType::Pressure    => (0.0, 100.0),
        SensorType::Vibration   => (0.0, 50.0),
    };
    if reading.value < min || reading.value > max {
        Err(ReadingError::OutOfRange {
            value: reading.value, min, max
        })
    } else {
        Ok(())
    }
}

/// تطبيق معامل المعايرة على القراءة
pub fn apply_calibration(reading: &mut Reading, factor: f64) {
    reading.value *= factor;
}

محرك الإنذارات: القواعد والإشعارات

// alarm/src/lib.rs
use sensor::{Reading, SensorType};

#[derive(Debug, Clone, PartialEq)]
pub enum AlarmLevel { Info, Warning, Critical, Emergency }

#[derive(Debug)]
pub struct AlarmRule {
    pub sensor_id: u32,
    pub threshold: f64,
    pub level: AlarmLevel,
    pub message: String,
}

#[derive(Debug)]
pub struct Alarm {
    pub rule: AlarmRule,
    pub actual_value: f64,
}

pub struct AlarmEngine {
    rules: Vec<AlarmRule>,
}

impl AlarmEngine {
    pub fn new() -> Self {
        Self { rules: Vec::new() }
    }

    pub fn add_rule(&mut self, rule: AlarmRule) {
        self.rules.push(rule);
    }

    /// تقييم القراءات وإرجاع الإنذارات المُطلَقة
    pub fn evaluate(&self, readings: &[Reading]) -> Vec<Alarm> {
        // نستخدم المُكررات: تصفية ← تحويل ← تجميع
        self.rules.iter()
            .filter_map(|rule| {
                readings.iter()
                    .find(|r| r.sensor_id == rule.sensor_id)
                    .filter(|r| r.value > rule.threshold)
                    .map(|r| Alarm {
                        actual_value: r.value,
                        rule: AlarmRule {
                            sensor_id: rule.sensor_id,
                            threshold: rule.threshold,
                            level: rule.level.clone(),
                            message: rule.message.clone(),
                        },
                    })
            })
            .collect()
    }

    /// ترتيب الإنذارات: الطوارئ أولاً
    pub fn sort_by_severity(alarms: &mut [Alarm]) {
        alarms.sort_by(|a, b| {
            let priority = |l: &AlarmLevel| match l {
                AlarmLevel::Emergency => 0,
                AlarmLevel::Critical  => 1,
                AlarmLevel::Warning   => 2,
                AlarmLevel::Info      => 3,
            };
            priority(&a.rule.level).cmp(&priority(&b.rule.level))
        });
    }
}

بيئة التشغيل غير المتزامنة: مراقبة متوازية

// dashboard/src/main.rs
use tokio::sync::mpsc;
use tokio::time::{interval, Duration};
use sensor::{Reading, SensorType, validate};
use alarm::{AlarmEngine, AlarmRule, AlarmLevel};

// محاكاة قراءة مستشعر عبر الشبكة
async fn poll_sensor(id: u32, s_type: SensorType) -> Reading {
    tokio::time::sleep(Duration::from_millis(50)).await;
    Reading {
        sensor_id: id,
        sensor_type: s_type,
        value: 25.0 + (id as f64 * 3.7) % 80.0,
        timestamp: 0,
    }
}

#[tokio::main]
async fn main() {
    // قناة لإرسال القراءات من المهام إلى المعالج
    let (tx, mut rx) = mpsc::channel::<Reading>(100);

    // مهمة: استطلاع دوري للمستشعرات
    let tx_clone = tx.clone();
    tokio::spawn(async move {
        let mut tick = interval(Duration::from_secs(5));
        let sensors = vec![
            (1, SensorType::Temperature),
            (2, SensorType::Pressure),
            (3, SensorType::Vibration),
        ];
        loop {
            tick.tick().await;
            for (id, s_type) in &sensors {
                let reading = poll_sensor(*id, s_type.clone()).await;
                if validate(&reading).is_ok() {
                    let _ = tx_clone.send(reading).await;
                }
            }
        }
    });
    drop(tx); // نُغلق المُرسل الأصلي

    // إعداد محرك الإنذارات
    let mut engine = AlarmEngine::new();
    engine.add_rule(AlarmRule {
        sensor_id: 1, threshold: 80.0,
        level: AlarmLevel::Critical,
        message: "حرارة الفرن مرتفعة".into(),
    });

    // المعالج الرئيسي: استقبال وتقييم
    let mut batch = Vec::new();
    while let Some(reading) = rx.recv().await {
        println!("  مستشعر {}: {:.1}", reading.sensor_id, reading.value);
        batch.push(reading);

        if batch.len() >= 3 {
            let mut alarms = engine.evaluate(&batch);
            if !alarms.is_empty() {
                AlarmEngine::sort_by_severity(&mut alarms);
                for a in &alarms {
                    println!("[{:?}] {}", a.rule.level, a.rule.message);
                }
            }
            batch.clear();
        }
    }
}

إضافة اختبارات للمسارات الحرجة

// sensor/src/lib.rs — داخل نفس الملف
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_temperature_valid_range() {
        let r = Reading {
            sensor_id: 1, sensor_type: SensorType::Temperature,
            value: 75.0, timestamp: 0,
        };
        assert!(validate(&r).is_ok());
    }

    #[test]
    fn test_pressure_out_of_range() {
        let r = Reading {
            sensor_id: 2, sensor_type: SensorType::Pressure,
            value: 150.0, timestamp: 0,
        };
        assert!(validate(&r).is_err());
    }
}

// tests/integration.rs — اختبار النظام الكامل
#[test]
fn test_alarm_triggers_on_high_temp() {
    let mut engine = alarm::AlarmEngine::new();
    engine.add_rule(alarm::AlarmRule {
        sensor_id: 1, threshold: 80.0,
        level: alarm::AlarmLevel::Critical,
        message: "حرارة مرتفعة".into(),
    });

    let readings = vec![sensor::Reading {
        sensor_id: 1, sensor_type: sensor::SensorType::Temperature,
        value: 95.0, timestamp: 0,
    }];

    let alarms = engine.evaluate(&readings);
    assert_eq!(alarms.len(), 1);
    assert_eq!(alarms[0].rule.level, alarm::AlarmLevel::Critical);
}

تشغيل النظام الكامل

# تشغيل جميع الاختبارات أولاً
cargo test --workspace

# ثم تشغيل النظام
cargo run -p dashboard

المخرجات المتوقعة:

  مستشعر 1: 28.7°C
  مستشعر 2: 32.4 bar
  مستشعر 3: 11.1 mm/s
  ---
  مستشعر 1: 88.3°C
[Critical] حرارة الفرن مرتفعة

الخلاصة والخطوات القادمة

في هذه السلسلة من 15 درساً تعلمنا:

  • الأساسيات: المتغيرات، الأنواع، التحكم في التدفق
  • الملكية: نظام Rust الفريد لإدارة الذاكرة بأمان
  • الهياكل والسمات: بناء أنظمة مرنة وقابلة للتوسيع
  • معالجة الأخطاء: Result وOption بدلاً من الانهيار
  • المجموعات والمكررات: معالجة بيانات المستشعرات بكفاءة
  • البرمجة غير المتزامنة: مراقبة مئات المستشعرات بموارد قليلة
  • الاختبارات: ضمان أن الكود الصناعي يعمل بشكل صحيح

خطوات للاستمرار في التعلم:

  • Rust المدمج: embedded-hal وno_std للتحكم المباشر بوحدات PLC
  • أطر الويب: Axum أو Actix لبناء لوحات متابعة عبر الإنترنت
  • قواعد البيانات: SurrealDB أو SQLx لتخزين بيانات المستشعرات
  • MQTT/OPC-UA: بروتوكولات الاتصال الصناعية
  • WebAssembly: تشغيل كود Rust في المتصفح للوحات تفاعلية

كل درس في هذه السلسلة أعطاك أداة — الآن لديك صندوق أدوات كامل لبناء أنظمة صناعية آمنة وفعّالة بلغة Rust.

project monitoring dashboard alarms architecture full-stack مشروع تطبيقي نظام مراقبة لوحة المراقبة الإنذارات الهندسة المعمارية التطبيق الشامل