mod core;
mod controller;
pub mod scheduler;
pub mod pool;
pub mod discovery_scheduler;
pub mod discovery;
use std::sync::Arc;
use serde::{Serialize, Deserialize};
use tokio::sync::RwLock;
use std::collections::HashMap as FxHashMap;
use crate::observability::{RiMetricsRegistry, RiMetric, RiMetricConfig, RiMetricType};
#[cfg(feature = "pyo3")]
use pyo3::prelude::*;
pub use core::{RiDevice, RiDeviceType, RiDeviceStatus, RiDeviceCapabilities, RiDeviceControlConfig, RiDeviceConfig, RiNetworkDeviceInfo, RiDeviceHealthMetrics};
pub use controller::RiDeviceController;
pub use pool::{RiResourcePool, RiResourcePoolManager, RiConnectionPoolStatistics, RiResourcePoolConfig};
pub use scheduler::RiDeviceScheduler;
pub use discovery_scheduler::{RiDeviceDiscoveryEngine, RiResourceScheduler};
pub use discovery::{
RiDeviceDiscovery,
DiscoveryConfig,
DiscoveryStats,
DiscoveryStrategy,
HardwareCategory,
PlatformInfo,
PlatformType,
Architecture,
PlatformCompatibility,
ProviderRegistry,
RiHardwareProvider,
PluginRegistry,
RiHardwareDiscoveryPlugin,
PluginMetadata,
PluginStatus,
PluginError,
AsyncDiscovery,
};
use crate::core::{RiResult, RiServiceContext};
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub struct RiDeviceControlModule {
controller: Arc<RwLock<RiDeviceController>>,
scheduler: Arc<RwLock<RiDeviceScheduler>>,
discovery_engine: Arc<RwLock<RiResourceScheduler>>,
resource_pools: FxHashMap<String, Arc<RiResourcePool>>,
config: RiDeviceControlConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub struct RiDeviceSchedulingConfig {
pub discovery_enabled: bool,
pub discovery_interval_secs: u64,
pub auto_scheduling_enabled: bool,
pub max_concurrent_tasks: usize,
pub resource_allocation_timeout_secs: u64,
}
impl Default for RiDeviceSchedulingConfig {
fn default() -> Self {
Self {
discovery_enabled: true,
discovery_interval_secs: 30,
auto_scheduling_enabled: true,
max_concurrent_tasks: 100,
resource_allocation_timeout_secs: 60,
}
}
}
#[cfg(feature = "pyo3")]
#[pymethods]
impl RiDeviceSchedulingConfig {
#[new]
fn py_new() -> Self {
Self::default()
}
#[staticmethod]
fn default_config() -> Self {
Self::default()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub struct RiDiscoveryResult {
pub discovered_devices: Vec<RiDevice>,
pub updated_devices: Vec<RiDevice>,
pub removed_devices: Vec<String>, pub total_devices: usize,
}
#[cfg(feature = "pyo3")]
#[pymethods]
impl RiDiscoveryResult {
#[new]
fn py_new() -> Self {
Self {
discovered_devices: Vec::new(),
updated_devices: Vec::new(),
removed_devices: Vec::new(),
total_devices: 0,
}
}
#[staticmethod]
fn default_result() -> Self {
Self::default()
}
fn discovered_devices_impl(&self) -> Vec<RiDevice> {
self.discovered_devices.clone()
}
fn updated_devices_impl(&self) -> Vec<RiDevice> {
self.updated_devices.clone()
}
fn removed_devices_impl(&self) -> Vec<String> {
self.removed_devices.clone()
}
fn total_devices_impl(&self) -> usize {
self.total_devices
}
fn __str__(&self) -> String {
format!("RiDiscoveryResult(discovered: {}, updated: {}, removed: {}, total: {})",
self.discovered_devices.len(), self.updated_devices.len(),
self.removed_devices.len(), self.total_devices)
}
}
impl Default for RiDiscoveryResult {
fn default() -> Self {
Self {
discovered_devices: Vec::new(),
updated_devices: Vec::new(),
removed_devices: Vec::new(),
total_devices: 0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub struct RiResourceRequest {
pub request_id: String,
pub device_type: RiDeviceType,
pub required_capabilities: RiDeviceCapabilities,
pub priority: u8, pub timeout_secs: u64,
pub sla_class: Option<RiRequestSlaClass>,
pub resource_weights: Option<RiResourceWeights>,
pub affinity: Option<RiAffinityRules>,
pub anti_affinity: Option<RiAffinityRules>,
}
#[cfg(feature = "pyo3")]
#[pymethods]
impl RiResourceRequest {
#[new]
#[pyo3(signature = (request_id, device_type, required_capabilities, priority=5, timeout_secs=60))]
fn py_new(request_id: String, device_type: RiDeviceType, required_capabilities: RiDeviceCapabilities, priority: u8, timeout_secs: u64) -> Self {
Self {
request_id,
device_type,
required_capabilities,
priority,
timeout_secs,
sla_class: None,
resource_weights: None,
affinity: None,
anti_affinity: None,
}
}
#[pyo3(name = "request_id")]
fn request_id_impl(&self) -> String {
self.request_id.clone()
}
#[pyo3(name = "device_type")]
fn device_type_impl(&self) -> RiDeviceType {
self.device_type
}
#[pyo3(name = "required_capabilities")]
fn required_capabilities_impl(&self) -> RiDeviceCapabilities {
self.required_capabilities.clone()
}
#[pyo3(name = "priority")]
fn priority_impl(&self) -> u8 {
self.priority
}
#[pyo3(name = "timeout_secs")]
fn timeout_secs_impl(&self) -> u64 {
self.timeout_secs
}
#[pyo3(name = "sla_class")]
fn sla_class_impl(&self) -> Option<RiRequestSlaClass> {
self.sla_class
}
#[pyo3(name = "resource_weights")]
fn resource_weights_impl(&self) -> Option<RiResourceWeights> {
self.resource_weights.clone()
}
#[pyo3(name = "affinity")]
fn affinity_impl(&self) -> Option<RiAffinityRules> {
self.affinity.clone()
}
#[pyo3(name = "anti_affinity")]
fn anti_affinity_impl(&self) -> Option<RiAffinityRules> {
self.anti_affinity.clone()
}
#[pyo3(name = "set_priority")]
fn set_priority_impl(&mut self, priority: u8) {
self.priority = priority;
}
#[pyo3(name = "set_timeout_secs")]
fn set_timeout_secs_impl(&mut self, timeout_secs: u64) {
self.timeout_secs = timeout_secs;
}
#[pyo3(name = "set_sla_class")]
fn set_sla_class_impl(&mut self, sla_class: Option<RiRequestSlaClass>) {
self.sla_class = sla_class;
}
#[pyo3(name = "set_resource_weights")]
fn set_resource_weights_impl(&mut self, resource_weights: Option<RiResourceWeights>) {
self.resource_weights = resource_weights;
}
#[pyo3(name = "set_affinity")]
fn set_affinity_impl(&mut self, affinity: Option<RiAffinityRules>) {
self.affinity = affinity;
}
#[pyo3(name = "set_anti_affinity")]
fn set_anti_affinity_impl(&mut self, anti_affinity: Option<RiAffinityRules>) {
self.anti_affinity = anti_affinity;
}
fn __str__(&self) -> String {
format!("RiResourceRequest(id: {}, type: {:?}, priority: {}, timeout: {}s)",
self.request_id, self.device_type, self.priority, self.timeout_secs)
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub enum RiRequestSlaClass {
Critical,
High,
Medium,
Low,
}
#[cfg(feature = "pyo3")]
#[pymethods]
impl RiRequestSlaClass {
fn __str__(&self) -> String {
match self {
RiRequestSlaClass::Critical => "Critical".to_string(),
RiRequestSlaClass::High => "High".to_string(),
RiRequestSlaClass::Medium => "Medium".to_string(),
RiRequestSlaClass::Low => "Low".to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub struct RiResourceWeights {
pub compute_weight: f64,
pub memory_weight: f64,
pub storage_weight: f64,
pub bandwidth_weight: f64,
}
#[cfg(feature = "pyo3")]
#[pymethods]
impl RiResourceWeights {
#[new]
#[pyo3(signature = (compute_weight=1.0, memory_weight=1.0, storage_weight=1.0, bandwidth_weight=1.0))]
fn py_new(compute_weight: f64, memory_weight: f64, storage_weight: f64, bandwidth_weight: f64) -> Self {
Self {
compute_weight,
memory_weight,
storage_weight,
bandwidth_weight,
}
}
#[staticmethod]
fn default_weights() -> Self {
Self::default()
}
#[pyo3(name = "compute_weight")]
fn compute_weight_impl(&self) -> f64 { self.compute_weight }
#[pyo3(name = "memory_weight")]
fn memory_weight_impl(&self) -> f64 { self.memory_weight }
#[pyo3(name = "storage_weight")]
fn storage_weight_impl(&self) -> f64 { self.storage_weight }
#[pyo3(name = "bandwidth_weight")]
fn bandwidth_weight_impl(&self) -> f64 { self.bandwidth_weight }
#[pyo3(name = "set_compute_weight")]
fn set_compute_weight_impl(&mut self, weight: f64) { self.compute_weight = weight; }
#[pyo3(name = "set_memory_weight")]
fn set_memory_weight_impl(&mut self, weight: f64) { self.memory_weight = weight; }
#[pyo3(name = "set_storage_weight")]
fn set_storage_weight_impl(&mut self, weight: f64) { self.storage_weight = weight; }
#[pyo3(name = "set_bandwidth_weight")]
fn set_bandwidth_weight_impl(&mut self, weight: f64) { self.bandwidth_weight = weight; }
fn __str__(&self) -> String {
format!("RiResourceWeights(compute: {}, memory: {}, storage: {}, bandwidth: {})",
self.compute_weight, self.memory_weight, self.storage_weight, self.bandwidth_weight)
}
}
impl Default for RiResourceWeights {
fn default() -> Self {
Self {
compute_weight: 1.0,
memory_weight: 1.0,
storage_weight: 1.0,
bandwidth_weight: 1.0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub struct RiAffinityRules {
pub required_labels: FxHashMap<String, String>,
pub preferred_labels: FxHashMap<String, String>,
pub forbidden_labels: FxHashMap<String, String>,
}
#[cfg(feature = "pyo3")]
#[pymethods]
impl RiAffinityRules {
#[new]
fn py_new() -> Self {
Self {
required_labels: FxHashMap::default(),
preferred_labels: FxHashMap::default(),
forbidden_labels: FxHashMap::default(),
}
}
#[staticmethod]
fn default_rules() -> Self {
Self::default()
}
#[pyo3(name = "required_labels")]
fn required_labels_impl(&self) -> FxHashMap<String, String> {
self.required_labels.clone()
}
#[pyo3(name = "preferred_labels")]
fn preferred_labels_impl(&self) -> FxHashMap<String, String> {
self.preferred_labels.clone()
}
#[pyo3(name = "forbidden_labels")]
fn forbidden_labels_impl(&self) -> FxHashMap<String, String> {
self.forbidden_labels.clone()
}
fn __str__(&self) -> String {
format!("RiAffinityRules(required: {}, preferred: {}, forbidden: {})",
self.required_labels.len(), self.preferred_labels.len(), self.forbidden_labels.len())
}
}
impl Default for RiAffinityRules {
fn default() -> Self {
Self {
required_labels: FxHashMap::default(),
preferred_labels: FxHashMap::default(),
forbidden_labels: FxHashMap::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub struct RiResourceAllocation {
pub allocation_id: String,
pub device_id: String,
pub device_name: String,
pub allocated_at: chrono::DateTime<chrono::Utc>,
pub expires_at: chrono::DateTime<chrono::Utc>,
pub request: RiResourceRequest,
}
#[cfg(feature = "pyo3")]
#[pymethods]
impl RiResourceAllocation {
#[new]
fn py_new(allocation_id: String, device_id: String, device_name: String, request: RiResourceRequest) -> Self {
let now = chrono::Utc::now();
let expires_at = now + chrono::TimeDelta::seconds(request.timeout_secs as i64);
Self {
allocation_id,
device_id,
device_name,
allocated_at: now,
expires_at,
request,
}
}
#[pyo3(name = "allocation_id")]
fn allocation_id_impl(&self) -> String {
self.allocation_id.clone()
}
#[pyo3(name = "device_id")]
fn device_id_impl(&self) -> String {
self.device_id.clone()
}
#[pyo3(name = "device_name")]
fn device_name_impl(&self) -> String {
self.device_name.clone()
}
#[pyo3(name = "allocated_at")]
fn allocated_at_impl(&self) -> String {
self.allocated_at.to_rfc3339()
}
#[pyo3(name = "expires_at")]
fn expires_at_impl(&self) -> String {
self.expires_at.to_rfc3339()
}
#[pyo3(name = "request")]
fn request_impl(&self) -> RiResourceRequest {
self.request.clone()
}
#[pyo3(name = "is_expired")]
fn is_expired_impl(&self) -> bool {
chrono::Utc::now() > self.expires_at
}
#[pyo3(name = "remaining_time")]
fn remaining_time_impl(&self) -> i64 {
(self.expires_at - chrono::Utc::now()).num_seconds()
}
fn __str__(&self) -> String {
format!("RiResourceAllocation(id: {}, device: {} ({}), expires: {})",
self.allocation_id, self.device_name, self.device_id, self.expires_at)
}
}
impl Default for RiDeviceControlModule {
fn default() -> Self {
Self::new()
}
}
impl RiDeviceControlModule {
pub fn new() -> Self {
let controller = Arc::new(RwLock::new(RiDeviceController::new()));
let resource_pool_manager = Arc::new(RwLock::new(RiResourcePoolManager::new()));
let scheduler = Arc::new(RwLock::new(RiDeviceScheduler::new(resource_pool_manager)));
let discovery_engine = Arc::new(RwLock::new(RiResourceScheduler::new()));
Self {
controller,
scheduler,
discovery_engine,
resource_pools: FxHashMap::default(),
config: crate::device::core::RiDeviceControlConfig::default(),
}
}
pub fn with_config(mut self, config: crate::device::core::RiDeviceControlConfig) -> Self {
self.config = config;
self
}
pub async fn discover_devices(&self) -> RiResult<RiDiscoveryResult> {
if !self.config.enable_cpu_discovery && !self.config.enable_gpu_discovery &&
!self.config.enable_memory_discovery && !self.config.enable_storage_discovery &&
!self.config.enable_network_discovery {
return Ok(RiDiscoveryResult {
discovered_devices: vec![],
updated_devices: vec![],
removed_devices: vec![],
total_devices: 0,
});
}
let mut controller = self.controller.write().await;
controller.discover_devices().await
}
pub async fn allocate_resource(&self, request: RiResourceRequest) -> RiResult<Option<RiResourceAllocation>> {
let scheduling_enabled = match request.device_type {
RiDeviceType::CPU => self.config.enable_cpu_discovery,
RiDeviceType::GPU => self.config.enable_gpu_discovery,
RiDeviceType::Memory => self.config.enable_memory_discovery,
RiDeviceType::Storage => self.config.enable_storage_discovery,
RiDeviceType::Network => self.config.enable_network_discovery,
_ => true, };
if !scheduling_enabled {
return Ok(None);
}
let allocation_request = crate::device::scheduler::RiAllocationRequest {
device_type: request.device_type,
capabilities: request.required_capabilities,
priority: request.priority as u32,
timeout_secs: request.timeout_secs,
sla_class: request.sla_class,
resource_weights: request.resource_weights,
affinity: request.affinity,
anti_affinity: request.anti_affinity,
};
let scheduler = self.scheduler.write().await;
let device = scheduler.select_device(&allocation_request).await;
if let Some(device) = device {
let allocation = RiResourceAllocation {
allocation_id: uuid::Uuid::new_v4().to_string(),
device_id: device.id().to_string(),
device_name: device.name().to_string(),
allocated_at: chrono::Utc::now(),
expires_at: chrono::Utc::now() + chrono::Duration::seconds(allocation_request.timeout_secs as i64),
request: RiResourceRequest {
request_id: request.request_id,
device_type: allocation_request.device_type,
required_capabilities: allocation_request.capabilities,
priority: request.priority,
timeout_secs: allocation_request.timeout_secs,
sla_class: allocation_request.sla_class,
resource_weights: allocation_request.resource_weights,
affinity: allocation_request.affinity,
anti_affinity: allocation_request.anti_affinity,
},
};
let mut controller = self.controller.write().await;
controller.allocate_device(&allocation.device_id, &allocation.allocation_id).await?;
Ok(Some(allocation))
} else {
Ok(None)
}
}
pub async fn release_resource(&self, allocation_id: &str) -> RiResult<()> {
let mut controller = self.controller.write().await;
controller.release_device_by_allocation(allocation_id).await
}
pub async fn get_device_status(&self) -> RiResult<Vec<RiDevice>> {
let controller = self.controller.read().await;
Ok(controller.get_all_devices())
}
pub fn get_resource_pool_status(&self) -> FxHashMap<String, RiResourcePoolStatus> {
let mut status = FxHashMap::default();
for (pool_name, pool) in &self.resource_pools {
status.insert(pool_name.clone(), pool.get_status());
}
status
}
#[allow(dead_code)]
fn create_device_metrics(&self, registry: Arc<RiMetricsRegistry>) -> RiResult<()> {
let device_total_config = RiMetricConfig {
metric_type: RiMetricType::Gauge,
name: "dms_device_total".to_string(),
help: "Total number of devices by type and status".to_string(),
buckets: vec![],
quantiles: vec![],
max_age: std::time::Duration::from_secs(300),
age_buckets: 5,
};
let device_total_metric = Arc::new(RiMetric::new(device_total_config));
registry.register(device_total_metric)?;
let allocation_attempts_config = RiMetricConfig {
metric_type: RiMetricType::Counter,
name: "dms_device_allocation_attempts_total".to_string(),
help: "Total number of device allocation attempts".to_string(),
buckets: vec![],
quantiles: vec![],
max_age: std::time::Duration::from_secs(0),
age_buckets: 0,
};
let allocation_attempts_metric = Arc::new(RiMetric::new(allocation_attempts_config));
registry.register(allocation_attempts_metric)?;
let allocation_success_config = RiMetricConfig {
metric_type: RiMetricType::Counter,
name: "dms_device_allocation_success_total".to_string(),
help: "Total number of successful device allocations".to_string(),
buckets: vec![],
quantiles: vec![],
max_age: std::time::Duration::from_secs(0),
age_buckets: 0,
};
let allocation_success_metric = Arc::new(RiMetric::new(allocation_success_config));
registry.register(allocation_success_metric)?;
let allocation_failure_config = RiMetricConfig {
metric_type: RiMetricType::Counter,
name: "dms_device_allocation_failure_total".to_string(),
help: "Total number of failed device allocations".to_string(),
buckets: vec![],
quantiles: vec![],
max_age: std::time::Duration::from_secs(0),
age_buckets: 0,
};
let allocation_failure_metric = Arc::new(RiMetric::new(allocation_failure_config));
registry.register(allocation_failure_metric)?;
let discovery_attempts_config = RiMetricConfig {
metric_type: RiMetricType::Counter,
name: "dms_device_discovery_attempts_total".to_string(),
help: "Total number of device discovery attempts".to_string(),
buckets: vec![],
quantiles: vec![],
max_age: std::time::Duration::from_secs(0),
age_buckets: 0,
};
let discovery_attempts_metric = Arc::new(RiMetric::new(discovery_attempts_config));
registry.register(discovery_attempts_metric)?;
let discovery_success_config = RiMetricConfig {
metric_type: RiMetricType::Counter,
name: "dms_device_discovery_success_total".to_string(),
help: "Total number of successful device discoveries".to_string(),
buckets: vec![],
quantiles: vec![],
max_age: std::time::Duration::from_secs(0),
age_buckets: 0,
};
let discovery_success_metric = Arc::new(RiMetric::new(discovery_success_config));
registry.register(discovery_success_metric)?;
let resource_utilization_config = RiMetricConfig {
metric_type: RiMetricType::Gauge,
name: "dms_device_resource_utilization".to_string(),
help: "Resource utilization by device type".to_string(),
buckets: vec![],
quantiles: vec![],
max_age: std::time::Duration::from_secs(300),
age_buckets: 5,
};
let resource_utilization_metric = Arc::new(RiMetric::new(resource_utilization_config));
registry.register(resource_utilization_metric)?;
Ok(())
}
}
#[cfg(feature = "pyo3")]
#[pyo3::prelude::pymethods]
impl RiDeviceControlModule {
fn get_config(&self) -> String {
format!("{:?}", self.config)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass(get_all, set_all))]
pub struct RiResourcePoolStatus {
pub total_capacity: usize,
pub available_capacity: usize,
pub allocated_capacity: usize,
pub pending_requests: usize,
pub utilization_rate: f64,
}
#[cfg(feature = "pyo3")]
#[pyo3::prelude::pymethods]
impl RiResourcePoolStatus {
#[new]
#[pyo3(signature = (total_capacity=0, available_capacity=0, allocated_capacity=0, pending_requests=0, utilization_rate=0.0))]
fn py_new(total_capacity: usize, available_capacity: usize, allocated_capacity: usize, pending_requests: usize, utilization_rate: f64) -> Self {
Self {
total_capacity,
available_capacity,
allocated_capacity,
pending_requests,
utilization_rate,
}
}
#[staticmethod]
fn default_status() -> Self {
Self::default()
}
#[pyo3(name = "total_capacity")]
fn total_capacity_impl(&self) -> usize {
self.total_capacity
}
#[pyo3(name = "available_capacity")]
fn available_capacity_impl(&self) -> usize {
self.available_capacity
}
#[pyo3(name = "allocated_capacity")]
fn allocated_capacity_impl(&self) -> usize {
self.allocated_capacity
}
#[pyo3(name = "pending_requests")]
fn pending_requests_impl(&self) -> usize {
self.pending_requests
}
#[pyo3(name = "utilization_rate")]
fn utilization_rate_impl(&self) -> f64 {
self.utilization_rate
}
fn __str__(&self) -> String {
format!("RiResourcePoolStatus(total: {}, available: {}, allocated: {}, utilization: {:.2}%)",
self.total_capacity, self.available_capacity, self.allocated_capacity, self.utilization_rate)
}
}
impl Default for RiResourcePoolStatus {
fn default() -> Self {
Self {
total_capacity: 0,
available_capacity: 0,
allocated_capacity: 0,
pending_requests: 0,
utilization_rate: 0.0,
}
}
}
#[async_trait::async_trait]
impl crate::core::RiModule for RiDeviceControlModule {
fn name(&self) -> &str {
"Ri.DeviceControl"
}
fn is_critical(&self) -> bool {
false }
async fn init(&mut self, ctx: &mut RiServiceContext) -> RiResult<()> {
let binding = ctx.config();
let cfg = binding.config();
let device_config = parse_device_config(cfg.get("device"));
let mut controller = self.controller.write().await;
controller.discover_system_devices(&device_config).await?;
drop(controller);
let discovery_engine = RiResourceScheduler::new();
let mut discovery_guard = self.discovery_engine.write().await;
*discovery_guard = discovery_engine;
if let Some(metrics_registry) = ctx.metrics_registry() {
let mut controller = self.controller.write().await;
controller.initialize_metrics(&metrics_registry)?;
drop(controller);
}
let logger = ctx.logger();
logger.info("Ri.DeviceControl", "Device control module initialized with real hardware discovery")?;
Ok(())
}
}
impl crate::core::ServiceModule for RiDeviceControlModule {
fn name(&self) -> &str {
"Ri.DeviceControl"
}
fn is_critical(&self) -> bool {
false
}
fn priority(&self) -> i32 {
25
}
fn dependencies(&self) -> Vec<&str> {
vec![]
}
fn init(&mut self, _ctx: &mut crate::core::RiServiceContext) -> crate::core::RiResult<()> {
Ok(())
}
fn start(&mut self, _ctx: &mut crate::core::RiServiceContext) -> crate::core::RiResult<()> {
Ok(())
}
fn shutdown(&mut self, _ctx: &mut crate::core::RiServiceContext) -> crate::core::RiResult<()> {
Ok(())
}
}
fn parse_device_config(config_str: Option<&String>) -> crate::device::core::RiDeviceControlConfig {
match config_str {
Some(config) => {
let trimmed = config.trim();
if trimmed.starts_with('{') {
if let Ok(result) = serde_json::from_str::<crate::device::core::RiDeviceControlConfig>(trimmed) {
return result;
}
}
if trimmed.starts_with("---") || trimmed.contains("discovery_enabled:") {
if let Ok(result) = serde_yaml::from_str::<crate::device::core::RiDeviceControlConfig>(trimmed) {
return result;
}
}
if trimmed.contains('[') || trimmed.contains("discovery_enabled") {
if let Ok(result) = toml::from_str::<crate::device::core::RiDeviceControlConfig>(trimmed) {
return result;
}
}
serde_json::from_str::<crate::device::core::RiDeviceControlConfig>(trimmed)
.unwrap_or_default()
}
None => crate::device::core::RiDeviceControlConfig::default(),
}
}