use axum::extract::FromRef; use crate::router::build_router; use plc_platform_core::platform_context::PlatformContext; #[derive(Clone, Debug)] pub struct AppConfig { pub database_url: String, pub server_host: String, pub server_port: u16, } impl AppConfig { pub fn from_env() -> Self { Self { database_url: std::env::var("DATABASE_URL") .expect("DATABASE_URL must be set"), server_host: std::env::var("OPS_SERVER_HOST") .unwrap_or_else(|_| "127.0.0.1".to_string()), server_port: std::env::var("OPS_SERVER_PORT") .ok() .and_then(|value| value.parse().ok()) .unwrap_or(3100), } } } #[derive(Clone)] pub struct AppState { pub app_name: &'static str, pub config: AppConfig, pub platform: PlatformContext, } impl FromRef for PlatformContext { fn from_ref(state: &AppState) -> Self { state.platform.clone() } } pub async fn run() { dotenv::dotenv().ok(); plc_platform_core::util::log::init_logger(); let _single_instance = match plc_platform_core::util::single_instance::try_acquire("PLCControl.OperationSystem") { Ok(guard) => guard, Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => { tracing::warn!("Another operation-system instance is already running"); return; } Err(err) => { tracing::error!("Failed to initialize single instance guard: {}", err); return; } }; let config = AppConfig::from_env(); let builder = plc_platform_core::bootstrap::bootstrap_platform(&config.database_url) .await .expect("Failed to bootstrap platform"); let platform = builder.build(); let state = AppState { app_name: "operation-system", config, platform, }; let app = build_router(state.clone()); let addr = format!("{}:{}", state.config.server_host, state.config.server_port); tracing::info!("Starting operation-system server at http://{}", addr); let listener = tokio::net::TcpListener::bind(&addr) .await .expect("operation-system listener should bind"); axum::serve(listener, app) .await .expect("operation-system server should run"); } pub fn test_state() -> AppState { let database_url = "postgres://plc:plc@localhost/plc_control_test".to_string(); let pool = sqlx::postgres::PgPoolOptions::new() .connect_lazy(&database_url) .expect("lazy pool should build"); let connection_manager = std::sync::Arc::new(plc_platform_core::connection::ConnectionManager::new()); let ws_manager = std::sync::Arc::new(plc_platform_core::websocket::WebSocketManager::new()); AppState { app_name: "operation-system", config: AppConfig { database_url, server_host: "127.0.0.1".to_string(), server_port: 0, }, platform: PlatformContext::new(pool, connection_manager, ws_manager), } }