use crate::VirtualMachineRuntime; use crate::fs::{FsState, VirtualFS}; use crate::os::{FsFacade, LifecycleFacade, SessionsFacade, VmFacade, WindowFacade}; use crate::process::ProcessManager; use crate::services::memcard::MemcardService; use crate::services::window_manager::WindowManager; use crate::task::TaskManager; use prometeu_hal::log::{LogEvent, LogLevel, LogService, LogSource}; use prometeu_hal::telemetry::CertificationConfig; use std::collections::HashMap; use std::sync::Arc; pub struct SystemOS { pub(super) vm_runtime: VirtualMachineRuntime, pub(super) process_manager: ProcessManager, pub(super) task_manager: TaskManager, pub(super) window_manager: WindowManager, pub(super) log_service: LogService, pub(super) fs: VirtualFS, pub(super) fs_state: FsState, pub(super) memcard: MemcardService, pub(super) open_files: HashMap, pub(super) next_handle: u32, } impl SystemOS { pub fn new(cap_config: Option) -> Self { let log_service = LogService::new(4096); let mut os = Self { vm_runtime: VirtualMachineRuntime::new_with_log_counter( cap_config, Arc::clone(&log_service.logs_count), ), process_manager: ProcessManager::new(), task_manager: TaskManager::new(), window_manager: WindowManager::new(), log_service, fs: VirtualFS::new(), fs_state: FsState::Unmounted, memcard: MemcardService::new(), open_files: HashMap::new(), next_handle: 1, }; os.log(LogLevel::Info, LogSource::Pos, 0, "PrometeuOS starting...".to_string()); os } pub fn log(&mut self, level: LogLevel, source: LogSource, tag: u16, msg: String) { self.vm_runtime.log(&mut self.log_service, level, source, tag, msg); } pub fn trace(&mut self, source: LogSource, msg: String) { self.log(LogLevel::Trace, source, 0, msg); } pub fn trace_tag(&mut self, source: LogSource, tag: u16, msg: String) { self.log(LogLevel::Trace, source, tag, msg); } pub fn debug(&mut self, source: LogSource, msg: String) { self.log(LogLevel::Debug, source, 0, msg); } pub fn debug_tag(&mut self, source: LogSource, tag: u16, msg: String) { self.log(LogLevel::Debug, source, tag, msg); } pub fn info(&mut self, source: LogSource, msg: String) { self.log(LogLevel::Info, source, 0, msg); } pub fn info_tag(&mut self, source: LogSource, tag: u16, msg: String) { self.log(LogLevel::Info, source, tag, msg); } pub fn warn(&mut self, source: LogSource, msg: String) { self.log(LogLevel::Warn, source, 0, msg); } pub fn warn_tag(&mut self, source: LogSource, tag: u16, msg: String) { self.log(LogLevel::Warn, source, tag, msg); } pub fn error(&mut self, source: LogSource, msg: String) { self.log(LogLevel::Error, source, 0, msg); } pub fn error_tag(&mut self, source: LogSource, tag: u16, msg: String) { self.log(LogLevel::Error, source, tag, msg); } pub fn recent_logs(&self, n: usize) -> Vec { self.log_service.get_recent(n) } pub fn logs_after(&self, seq: u64) -> Vec { self.log_service.get_after(seq) } pub fn lifecycle(&mut self) -> LifecycleFacade<'_> { LifecycleFacade { os: self } } pub fn sessions(&mut self) -> SessionsFacade<'_> { SessionsFacade { os: self } } pub fn vm(&mut self) -> VmFacade<'_> { VmFacade { os: self } } pub fn fs(&mut self) -> FsFacade<'_> { FsFacade { os: self } } pub fn window(&mut self) -> WindowFacade<'_> { WindowFacade { os: self } } pub(super) fn clear_cartridge_service_state(&mut self) { self.open_files.clear(); self.next_handle = 1; self.memcard.clear_all_staging(); } } #[cfg(test)] mod tests { use super::*; use crate::os::{LifecycleError, LifecycleOperation}; use crate::process::ProcessState; use crate::task::{TaskId, TaskState}; fn process_state_for_task(os: &SystemOS, task_id: TaskId) -> ProcessState { let task = os.task_manager.get(task_id).expect("task should exist"); os.process_manager.get(task.process_id).expect("process should exist").state } #[test] fn set_foreground_task_marks_task_and_process_running() { let mut os = SystemOS::new(None); let task_id = os.sessions().create_vm_game_task(42, "Sector Crawl"); os.lifecycle().suspend_task(task_id).expect("suspend should succeed"); os.lifecycle().set_foreground_task(task_id).expect("foreground should succeed"); assert_eq!( os.task_manager.get(task_id).expect("task should exist").state, TaskState::Foreground ); assert_eq!(process_state_for_task(&os, task_id), ProcessState::Running); } #[test] fn suspend_task_marks_task_and_process_suspended() { let mut os = SystemOS::new(None); let task_id = os.sessions().create_vm_game_task(42, "Sector Crawl"); os.lifecycle().suspend_task(task_id).expect("suspend should succeed"); assert_eq!( os.task_manager.get(task_id).expect("task should exist").state, TaskState::Suspended ); assert_eq!(process_state_for_task(&os, task_id), ProcessState::Suspended); } #[test] fn resume_task_marks_suspended_task_foreground_and_process_running() { let mut os = SystemOS::new(None); let task_id = os.sessions().create_vm_game_task(42, "Sector Crawl"); os.lifecycle().suspend_task(task_id).expect("suspend should succeed"); os.lifecycle().resume_task(task_id).expect("resume should succeed"); assert_eq!( os.task_manager.get(task_id).expect("task should exist").state, TaskState::Foreground ); assert_eq!(process_state_for_task(&os, task_id), ProcessState::Running); } #[test] fn close_task_marks_task_closed_and_process_stopped_without_removing_entities() { let mut os = SystemOS::new(None); let task_id = os.sessions().create_vm_game_task(42, "Sector Crawl"); let process_id = os.task_manager.get(task_id).expect("task should exist").process_id; os.lifecycle().close_task(task_id).expect("close should succeed"); assert_eq!( os.task_manager.get(task_id).expect("task should remain").state, TaskState::Closed ); assert_eq!( os.process_manager.get(process_id).expect("process should remain").state, ProcessState::Stopped ); } #[test] fn crash_task_marks_task_and_process_crashed() { let mut os = SystemOS::new(None); let task_id = os.sessions().create_vm_game_task(42, "Sector Crawl"); os.lifecycle().crash_task(task_id, None).expect("crash should succeed"); assert_eq!( os.task_manager.get(task_id).expect("task should exist").state, TaskState::Crashed ); assert_eq!(process_state_for_task(&os, task_id), ProcessState::Crashed); } #[test] fn lifecycle_operation_missing_task_returns_typed_error() { let mut os = SystemOS::new(None); assert_eq!( os.lifecycle().suspend_task(TaskId(999)), Err(LifecycleError::TaskNotFound(TaskId(999))) ); } #[test] fn lifecycle_operation_missing_process_returns_typed_error() { let mut os = SystemOS::new(None); let task_id = os.sessions().create_vm_game_task(42, "Sector Crawl"); let process_id = os.task_manager.get(task_id).expect("task should exist").process_id; os.process_manager.mark_stopped(process_id); os.process_manager.remove_stopped(); assert_eq!( os.lifecycle().suspend_task(task_id), Err(LifecycleError::ProcessNotFound(process_id)) ); } #[test] fn resume_task_from_non_suspended_state_returns_invalid_transition() { let mut os = SystemOS::new(None); let task_id = os.sessions().create_vm_game_task(42, "Sector Crawl"); assert_eq!( os.lifecycle().resume_task(task_id), Err(LifecycleError::InvalidTransition { task_id, from: TaskState::Foreground, operation: LifecycleOperation::Resume, }) ); } }