diff --git a/.github/workflows/syntax-check.yml b/.github/workflows/syntax-check.yml
index 824efae..9c53551 100644
--- a/.github/workflows/syntax-check.yml
+++ b/.github/workflows/syntax-check.yml
@@ -3,7 +3,7 @@ name: Python Syntax Check
on:
pull_request:
branches:
- - main # or your default branch
+ - main # default branch
jobs:
syntax-check:
diff --git a/CloudKernel/ARCHITECTURE.md b/CloudKernel/ARCHITECTURE.md
new file mode 100644
index 0000000..91f5287
--- /dev/null
+++ b/CloudKernel/ARCHITECTURE.md
@@ -0,0 +1,77 @@
+# CloudKernel Architecture
+
+## Purpose
+
+CloudKernel models a cloud-hypervisor execution cycle with Java concurrency primitives while exposing runtime behavior through a real-time Swing dashboard.
+
+## Layers
+
+- Entry: Main.java
+- Configuration: config.ConfigLoader
+- Concurrency Core: core.BootManager, core.ClockSynchronizer
+- Runtime Entities: entities.VirtualMachine, entities.ResourceManager, entities.VMState, entities.VMPriority, entities.VMStats
+- Observability: utils.GUILogger, utils.StatsCollector
+- UI: ui.CloudKernelGUI and component panels
+- Shutdown: shutdown.ShutdownManager
+
+## Concurrency Contracts
+
+### BootManager
+
+- Uses CountDownLatch(4).
+- Boots disk, RAM, network stack, and CPU scheduler asynchronously.
+- Exposes latch for visual countdown.
+
+### ClockSynchronizer
+
+- Uses CyclicBarrier(vmCount).
+- Increments global cycle in barrier action.
+- Records cycle completion in StatsCollector.
+
+### ResourceManager
+
+- Uses three fair semaphores:
+ - CPU permits
+ - Memory permits
+ - Network permits
+- Uses timeout-based tryAcquire for deadlock-resistant resource requests.
+- Tracks current holders for UI rendering.
+
+### VirtualMachine
+
+- Executes configured number of cycles.
+- Transitions through VMState values.
+- Requests CPU, memory, network resources sequentially.
+- Synchronizes on cyclic barrier each cycle.
+
+## UI Composition
+
+CloudKernelGUI builds the dashboard from modular components:
+
+- VMCard
+- ResourceMonitorPanel
+- BarrierPanel
+- LogPanel
+- StatsBar
+- ControlPanel
+- DashboardUpdater
+
+DashboardUpdater centralizes UI refresh operations and dispatches thread-crossing updates via SwingUtilities.invokeLater.
+
+## Data And Events
+
+- GUILogger writes every event to terminal and broadcasts to GUI listeners.
+- CloudKernelGUI parses selected log patterns to drive visual state changes.
+- StatsCollector aggregates per-VM and system-wide counters for the stats bar and summary output.
+
+## Runtime Flow
+
+1. Main launches CloudKernelGUI on the Swing event dispatch thread.
+2. GUI initializes managers and registers listeners.
+3. User starts simulation through ControlPanel.
+4. BootManager performs subsystem initialization.
+5. VirtualMachine threads execute cycles with resource access and barrier sync.
+6. DashboardUpdater continuously refreshes resources, VM cards, barrier state, and stats.
+7. ShutdownManager prints graceful summary on termination.
+
+
\ No newline at end of file
diff --git a/CloudKernel/README.md b/CloudKernel/README.md
index 8ffa132..b93b93b 100644
--- a/CloudKernel/README.md
+++ b/CloudKernel/README.md
@@ -1,181 +1,129 @@
-# CloudKernel ☁️⚙️
+# CloudKernel
-## Overview
+CloudKernel is a Java concurrency simulator with a professional Swing dashboard that visualizes hypervisor-like VM scheduling, shared resource contention, and synchronization.
-**CloudKernel** is a small Java-based simulation created for our **Operating Systems Lab**.
-The purpose of this project is to show how a hypervisor-like system can manage multiple **Virtual Machines (VMs)** while coordinating shared resources.
+## 📋 Project Information
-Instead of building a real operating system, this project focuses on demonstrating **important OS concepts** like synchronization, resource sharing, and concurrent execution using Java threads.
-
-The program simulates a system where several virtual machines start after the system boots, run tasks together, and share limited network resources.
-
----
-
-## 🎓 Academic Information
-
-**Course:** Operating Systems Lab
-**Semester:** 4th Semester
-
-**Submitted to:**
-Mam Amara Nadeem
-
-**Submitted by:**
-
-- **Moavia Amir** (2k24_BSAI_72)
-- **Ali Raza** (2k24_BSAI_44)
-- **Muhammad Arslan Nasir** (2k24_BSAI_26)
-
-**Submission Date:**
-March 03, 2026
-
----
-
-## 🎯 Project Goals
-
-This project was designed to help understand how operating systems manage:
-
-- System boot coordination
-- Thread synchronization
-- Limited resource sharing
-- Parallel execution of processes
-
-All these ideas are implemented using **Java concurrency utilities**.
-
----
-
-## ⚙️ Key Concepts Used
-
-### 1. System Boot Coordination
-
-Before any virtual machine starts running, the system must finish its boot process.
-
-We simulate this using **CountDownLatch**.
-It ensures that resources like **Disk and RAM** are ready before the virtual machines begin execution.
+| Field | Details |
+|-------|---------|
+| **Subject** | Operating Systems |
+| **Semester** | 4th Semester — BSAI 2k24 |
+| **Institute** | NFC Institute of Engineering & Technology, Multan |
+| **Department** | Artificial Intelligence |
+| **Submitted To** | Mam Amara Nadeem — [ammara.visiting@nfciet.edu.pk](mailto:ammara.visiting@nfciet.edu.pk) |
+| **Submission Date** | March 03, 2026 |
---
-### 2. VM Cycle Synchronization
+## 👥 Team Members
+
+| Name | Roll Number | Email |
+|------|-------------|-------|
+| Muawiya Amir | 2k24_BSAI_72 | [2k24bsai72@undergrad.nfciet.edu.pk](mailto:2k24bsai72@undergrad.nfciet.edu.pk) |
+| Ali Raza | 2k24_BSAI_44 | [2k24bsai44@undergrad.nfciet.edu.pk](mailto:2k24bsai44@undergrad.nfciet.edu.pk) |
+| Muhammad Arslan Nasir | 2k24_BSAI_26 | [2k24bsai26@undergrad.nfciet.edu.pk](mailto:2k24bsai26@undergrad.nfciet.edu.pk) |
+## Highlights
+
+- Dark-theme dashboard: Cloud hypervisor monitor layout.
+- Boot orchestration with CountDownLatch.
+- VM cycle synchronization with CyclicBarrier.
+- Shared CPU, memory, and network resources with fair semaphores and timeout handling.
+- Color-coded live logs streamed to terminal and GUI simultaneously.
+- Live stats for cycles, operations, contentions, timeouts, and uptime.
+- Configurable behavior through config.properties.
+
+
+## Final Package Structure
+
+```text
+CloudKernel/
+ src/
+ Main.java
+ config/
+ ConfigLoader.java
+ core/
+ BootManager.java
+ ClockSynchronizer.java
+ entities/
+ ResourceManager.java
+ VirtualMachine.java
+ VMPriority.java
+ VMState.java
+ VMStats.java
+ shutdown/
+ ShutdownManager.java
+ ui/
+ BarrierPanel.java
+ CloudKernelGUI.java
+ ControlPanel.java
+ DashboardUpdater.java
+ LogPanel.java
+ ResourceMonitorPanel.java
+ StatsBar.java
+ VMCard.java
+ utils/
+ GUILogger.java
+ StatsCollector.java
+ config.properties
+ ARCHITECTURE.md
+ doc/
+ PROJECT_PROPOSAL.md
+ PROJECT_Report.md
+ Project_presentation.ppt
-Each virtual machine performs its work in cycles.
-To keep them synchronized, we use **CyclicBarrier**.
-
-This means all VMs must finish a cycle before the next one begins.
-
----
-### 3. Limited Network Access
-
-In real systems, hardware resources are limited.
-In this simulation, only **two VMs can use the network at the same time**.
-
-This is managed using a **Semaphore**, which controls access to the shared network ports.
-
----
-
-## 🧩 Project Structure
-```
-CloudKernel
-│
-├── src
-│ │
-│ ├── Main.java
-│ │
-│ ├── core
-│ │ ├── BootManager.java
-│ │ ├── ClockSynchronizer.java
-│ │ └── NetworkPortManager.java
-│ │
-│ ├── entities
-│ │ └── VirtualMachine.java
-│ │
-│ └── utils
-│ └── Logger.java
-│
-├── doc
-│ └── proposal
-│
-└── README.md
```
----
-## 🏗 System Workflow
+## GUI Overview
-The program runs in the following order:
+Main window sections:
-```
-System Boot
-│
-▼
-BootManager initializes resources
-│
-▼
-Virtual Machines start (Threads)
-│
-▼
-VMs execute cycles together
-│
-▼
-Network access controlled by Semaphore
-│
-▼
-Logs printed to terminal
-```
+- Header: title, digital clock, online indicator.
+- Boot panel: resource chips and latch countdown.
+- VM dashboard: one card per VM with state, priority, progress, and resource indicators.
+- Left sidebar: semaphore slot view for CPU, memory, and network.
+- Barrier panel: arrival dots and cycle display.
+- Right sidebar: color-coded live event log.
+- Bottom bars: statistics and controls.
----
+## Core Concurrency Model
-## ▶️ How to Run the Project
+- Boot phase: CountDownLatch initialized to four boot tasks.
+- Runtime phase: each VM executes for configured cycles.
+- Resource phase: each VM requests CPU, memory, and network permits with timeout.
+- Synchronization phase: all VMs rendezvous at a CyclicBarrier before the next cycle.
-### 1. Compile the project
+## Configuration
-```bash
-javac -d out -sourcepath src src/Main.java
-```
-### 2. Run the program
-```bash
-java -cp out Main
-```
-## 🖥 Example Output
+Edit config.properties before running:
-When the program runs, you may see output like:
-```
-[BOOT] Disk initialized
-[BOOT] RAM initialized
-[BOOT] System ready
-
-[VM-1] Starting execution
-[VM-2] Starting execution
-[VM-3] Starting execution
+- vm.count
+- cycle.count
+- semaphore.cpu.permits
+- semaphore.memory.permits
+- semaphore.network.permits
+- task.duration.min
+- task.duration.max
+- timeout.duration
+- gui.enabled
+- gui.theme
+- gui.font
+- logging.level
+- stats.enabled
-[VM-1] Requesting network access
-[VM-2] Requesting network access
+## Build And Run
-[VM-1] Using network port
-[VM-2] Using network port
+From CloudKernel root:
-[VM-3] Waiting for network port
+```powershell
+javac -encoding UTF-8 -d bin (Get-ChildItem -Recurse src -Filter *.java | ForEach-Object { $_.FullName })
+java -cp "bin;." Main
```
-The Logger class keeps the output organized so it is easier to read.
-
-## 🧠 What We Learned
-
-While building this project, we understood how operating systems handle:
-
-+ **Thread** synchronization
-
-+ **Shared** resource management
-
-+ **Parallel** execution
-
-+ **Process** coordination
-
-These concepts are important for understanding how real operating systems and cloud platforms work.
-
----
-## 📌 Conclusion
+## Notes
-CloudKernel is a simple educational simulation that demonstrates how a hypervisor-like system can coordinate virtual machines and manage shared resources.
+- All UI updates triggered by worker threads are dispatched through SwingUtilities.invokeLater.
+- Main.java contains only the GUI entry point.
+- Legacy duplicate docs and unused legacy classes were removed to keep one canonical implementation path.
-Although it is a simplified model, it provides a clear understanding of synchronization and concurrency in operating systems.
----
\ No newline at end of file
diff --git a/CloudKernel/bin/Main.class b/CloudKernel/bin/Main.class
new file mode 100644
index 0000000..93019a2
Binary files /dev/null and b/CloudKernel/bin/Main.class differ
diff --git a/CloudKernel/bin/config/ConfigLoader.class b/CloudKernel/bin/config/ConfigLoader.class
new file mode 100644
index 0000000..054488e
Binary files /dev/null and b/CloudKernel/bin/config/ConfigLoader.class differ
diff --git a/CloudKernel/bin/core/BootManager.class b/CloudKernel/bin/core/BootManager.class
new file mode 100644
index 0000000..691bd88
Binary files /dev/null and b/CloudKernel/bin/core/BootManager.class differ
diff --git a/CloudKernel/bin/core/ClockSynchronizer.class b/CloudKernel/bin/core/ClockSynchronizer.class
new file mode 100644
index 0000000..2069d39
Binary files /dev/null and b/CloudKernel/bin/core/ClockSynchronizer.class differ
diff --git a/CloudKernel/bin/entities/ResourceManager.class b/CloudKernel/bin/entities/ResourceManager.class
new file mode 100644
index 0000000..0e89778
Binary files /dev/null and b/CloudKernel/bin/entities/ResourceManager.class differ
diff --git a/CloudKernel/bin/entities/VMPriority.class b/CloudKernel/bin/entities/VMPriority.class
new file mode 100644
index 0000000..f0ae0bf
Binary files /dev/null and b/CloudKernel/bin/entities/VMPriority.class differ
diff --git a/CloudKernel/bin/entities/VMState.class b/CloudKernel/bin/entities/VMState.class
new file mode 100644
index 0000000..93dc68e
Binary files /dev/null and b/CloudKernel/bin/entities/VMState.class differ
diff --git a/CloudKernel/bin/entities/VMStats.class b/CloudKernel/bin/entities/VMStats.class
new file mode 100644
index 0000000..d97b51b
Binary files /dev/null and b/CloudKernel/bin/entities/VMStats.class differ
diff --git a/CloudKernel/bin/entities/VirtualMachine.class b/CloudKernel/bin/entities/VirtualMachine.class
new file mode 100644
index 0000000..3e11420
Binary files /dev/null and b/CloudKernel/bin/entities/VirtualMachine.class differ
diff --git a/CloudKernel/bin/shutdown/ShutdownManager.class b/CloudKernel/bin/shutdown/ShutdownManager.class
new file mode 100644
index 0000000..8c3d233
Binary files /dev/null and b/CloudKernel/bin/shutdown/ShutdownManager.class differ
diff --git a/CloudKernel/bin/ui/BarrierPanel.class b/CloudKernel/bin/ui/BarrierPanel.class
new file mode 100644
index 0000000..036f4ff
Binary files /dev/null and b/CloudKernel/bin/ui/BarrierPanel.class differ
diff --git a/CloudKernel/bin/ui/CloudKernelGUI.class b/CloudKernel/bin/ui/CloudKernelGUI.class
new file mode 100644
index 0000000..9cfa3a4
Binary files /dev/null and b/CloudKernel/bin/ui/CloudKernelGUI.class differ
diff --git a/CloudKernel/bin/ui/ControlPanel$1.class b/CloudKernel/bin/ui/ControlPanel$1.class
new file mode 100644
index 0000000..0c9a3fb
Binary files /dev/null and b/CloudKernel/bin/ui/ControlPanel$1.class differ
diff --git a/CloudKernel/bin/ui/ControlPanel.class b/CloudKernel/bin/ui/ControlPanel.class
new file mode 100644
index 0000000..3d85f4c
Binary files /dev/null and b/CloudKernel/bin/ui/ControlPanel.class differ
diff --git a/CloudKernel/bin/ui/DashboardUpdater.class b/CloudKernel/bin/ui/DashboardUpdater.class
new file mode 100644
index 0000000..2bce142
Binary files /dev/null and b/CloudKernel/bin/ui/DashboardUpdater.class differ
diff --git a/CloudKernel/bin/ui/LogPanel.class b/CloudKernel/bin/ui/LogPanel.class
new file mode 100644
index 0000000..32d37e4
Binary files /dev/null and b/CloudKernel/bin/ui/LogPanel.class differ
diff --git a/CloudKernel/bin/ui/ResourceMonitorPanel.class b/CloudKernel/bin/ui/ResourceMonitorPanel.class
new file mode 100644
index 0000000..5a9d663
Binary files /dev/null and b/CloudKernel/bin/ui/ResourceMonitorPanel.class differ
diff --git a/CloudKernel/bin/ui/StatsBar.class b/CloudKernel/bin/ui/StatsBar.class
new file mode 100644
index 0000000..2f2787d
Binary files /dev/null and b/CloudKernel/bin/ui/StatsBar.class differ
diff --git a/CloudKernel/bin/ui/VMCard$RoundedBorder.class b/CloudKernel/bin/ui/VMCard$RoundedBorder.class
new file mode 100644
index 0000000..d230354
Binary files /dev/null and b/CloudKernel/bin/ui/VMCard$RoundedBorder.class differ
diff --git a/CloudKernel/bin/ui/VMCard.class b/CloudKernel/bin/ui/VMCard.class
new file mode 100644
index 0000000..96d9c0f
Binary files /dev/null and b/CloudKernel/bin/ui/VMCard.class differ
diff --git a/CloudKernel/bin/utils/GUILogger$LogEntry.class b/CloudKernel/bin/utils/GUILogger$LogEntry.class
new file mode 100644
index 0000000..489e000
Binary files /dev/null and b/CloudKernel/bin/utils/GUILogger$LogEntry.class differ
diff --git a/CloudKernel/bin/utils/GUILogger$LogListener.class b/CloudKernel/bin/utils/GUILogger$LogListener.class
new file mode 100644
index 0000000..ce64a66
Binary files /dev/null and b/CloudKernel/bin/utils/GUILogger$LogListener.class differ
diff --git a/CloudKernel/bin/utils/GUILogger.class b/CloudKernel/bin/utils/GUILogger.class
new file mode 100644
index 0000000..31fc239
Binary files /dev/null and b/CloudKernel/bin/utils/GUILogger.class differ
diff --git a/CloudKernel/bin/utils/StatsCollector.class b/CloudKernel/bin/utils/StatsCollector.class
new file mode 100644
index 0000000..9060bfa
Binary files /dev/null and b/CloudKernel/bin/utils/StatsCollector.class differ
diff --git a/CloudKernel/config.properties b/CloudKernel/config.properties
new file mode 100644
index 0000000..e165178
--- /dev/null
+++ b/CloudKernel/config.properties
@@ -0,0 +1,38 @@
+# Number of virtual machines to create for each simulation run.
+vm.count=5
+
+# Number of execution cycles each virtual machine performs.
+cycle.count=4
+
+# Number of concurrent CPU permits available to VMs.
+semaphore.cpu.permits=3
+
+# Number of concurrent memory permits available to VMs.
+semaphore.memory.permits=2
+
+# Number of concurrent network permits available to VMs.
+semaphore.network.permits=2
+
+# Minimum simulated workload duration in milliseconds.
+task.duration.min=500
+
+# Maximum simulated workload duration in milliseconds.
+task.duration.max=1500
+
+# Maximum wait duration in milliseconds for acquiring a resource permit.
+timeout.duration=2000
+
+# Enables or disables GUI startup mode.
+gui.enabled=true
+
+# Preferred GUI theme identifier used by the dashboard.
+gui.theme=dark
+
+# Preferred GUI font family hint.
+gui.font=JetBrains Mono
+
+# Global logging level: VERBOSE, NORMAL, or QUIET.
+logging.level=NORMAL
+
+# Enables or disables runtime statistics collection.
+stats.enabled=true
diff --git a/CloudKernel/doc/CloudKernel.pptx b/CloudKernel/doc/CloudKernel.pptx
new file mode 100644
index 0000000..9804a71
Binary files /dev/null and b/CloudKernel/doc/CloudKernel.pptx differ
diff --git a/CloudKernel/doc/CloudKernel_ProjectReport.pdf b/CloudKernel/doc/CloudKernel_ProjectReport.pdf
deleted file mode 100644
index 8b8891e..0000000
Binary files a/CloudKernel/doc/CloudKernel_ProjectReport.pdf and /dev/null differ
diff --git a/CloudKernel/doc/CloudKernel_QA.pdf b/CloudKernel/doc/CloudKernel_QA.pdf
new file mode 100644
index 0000000..00f71b1
Binary files /dev/null and b/CloudKernel/doc/CloudKernel_QA.pdf differ
diff --git a/CloudKernel/doc/CloudKernel_Report.pdf b/CloudKernel/doc/CloudKernel_Report.pdf
new file mode 100644
index 0000000..ba95e6d
Binary files /dev/null and b/CloudKernel/doc/CloudKernel_Report.pdf differ
diff --git a/CloudKernel/doc/PROJECT_PROPOSAL.md b/CloudKernel/doc/PROJECT_PROPOSAL.md
new file mode 100644
index 0000000..24b2e41
--- /dev/null
+++ b/CloudKernel/doc/PROJECT_PROPOSAL.md
@@ -0,0 +1,68 @@
+# CloudKernel Final Project Proposal Update
+
+## Project Title
+
+CloudKernel: Multi-Threaded Hypervisor Monitor
+
+## Final Delivered Scope
+
+The final project delivers a Java-based operating-system lab simulation with:
+
+- A full Swing dashboard for live monitoring.
+- Multi-VM concurrent execution.
+- Shared resource arbitration through semaphores.
+- Global cycle synchronization through a cyclic barrier.
+- Boot orchestration through a countdown latch.
+- Dual-channel logging and live statistics.
+
+## Objectives Achieved
+
+- Demonstrate practical use of CountDownLatch, Semaphore, and CyclicBarrier.
+- Visualize VM lifecycle transitions in real time.
+- Measure system-level and VM-level behavior using statistics.
+- Provide configurable runtime parameters without code changes.
+
+## Final Technical Stack
+
+- Language: Java
+- GUI: Swing
+- Concurrency: java.util.concurrent
+- Build: javac
+- Runtime: java
+
+## Final Package Design
+
+- config: configuration loading
+- core: synchronization and boot coordinators
+- entities: VM and resource domain logic
+- ui: modular dashboard components
+- utils: logging and stats
+- shutdown: graceful shutdown behavior
+
+## Configuration-Driven Behavior
+
+The simulator uses config.properties for:
+
+- VM count and cycle count
+- CPU, memory, and network permit capacities
+- Task duration range and timeout duration
+- GUI and logging toggles
+
+## Deliverables
+
+- Fully integrated source code under src.
+- Updated README and architecture documentation.
+- Cleaned project with obsolete duplicate files removed.
+- Consistent Javadoc comments and naming conventions.
+- Successful compile validation with current structure.
+
+## Future Extensions
+
+- Export runtime metrics to CSV or JSON.
+- Add configurable VM scheduling policies.
+- Add persistence and replay mode for event timelines.
+
+
+
+
+
\ No newline at end of file
diff --git a/CloudKernel/img/banner.png b/CloudKernel/img/banner.png
new file mode 100644
index 0000000..28de80d
Binary files /dev/null and b/CloudKernel/img/banner.png differ
diff --git a/CloudKernel/img/screen.png b/CloudKernel/img/screen.png
new file mode 100644
index 0000000..254eb20
Binary files /dev/null and b/CloudKernel/img/screen.png differ
diff --git a/CloudKernel/out/Main.class b/CloudKernel/out/Main.class
deleted file mode 100644
index 745078b..0000000
Binary files a/CloudKernel/out/Main.class and /dev/null differ
diff --git a/CloudKernel/out/core/BootManager.class b/CloudKernel/out/core/BootManager.class
deleted file mode 100644
index 4c9ffa3..0000000
Binary files a/CloudKernel/out/core/BootManager.class and /dev/null differ
diff --git a/CloudKernel/out/core/ClockSynchronizer.class b/CloudKernel/out/core/ClockSynchronizer.class
deleted file mode 100644
index 65ce430..0000000
Binary files a/CloudKernel/out/core/ClockSynchronizer.class and /dev/null differ
diff --git a/CloudKernel/out/core/NetworkPortManager.class b/CloudKernel/out/core/NetworkPortManager.class
deleted file mode 100644
index cf004f0..0000000
Binary files a/CloudKernel/out/core/NetworkPortManager.class and /dev/null differ
diff --git a/CloudKernel/out/entities/VirtualMachine.class b/CloudKernel/out/entities/VirtualMachine.class
deleted file mode 100644
index 3a8e891..0000000
Binary files a/CloudKernel/out/entities/VirtualMachine.class and /dev/null differ
diff --git a/CloudKernel/out/utils/Logger.class b/CloudKernel/out/utils/Logger.class
deleted file mode 100644
index 9ba1646..0000000
Binary files a/CloudKernel/out/utils/Logger.class and /dev/null differ
diff --git a/CloudKernel/src/Main.java b/CloudKernel/src/Main.java
index 44ceb3a..6faed7e 100644
--- a/CloudKernel/src/Main.java
+++ b/CloudKernel/src/Main.java
@@ -1,58 +1,18 @@
-import core.BootManager;
-import core.ClockSynchronizer;
-import core.NetworkPortManager;
-import entities.VirtualMachine;
-import utils.Logger;
+import ui.CloudKernelGUI;
-// Entry point for the CloudKernel simulation.
-public class Main {
-
- private static final int NUM_VMS = 3;
- private static final int NUM_CYCLES = 2;
-
- public static void main(String[] args) throws InterruptedException {
- // Phase 1: boot
- Logger.section("PHASE 1: SYSTEM BOOT [CountDownLatch]");
- Logger.log("HYPERVISOR", "CloudKernel v1.0 starting...", Logger.BOLD + Logger.GREEN);
-
- BootManager bootManager = new BootManager();
- bootManager.initDisk();
- bootManager.initRAM();
- bootManager.awaitBootCompletion();
-
- Thread.sleep(500);
+import javax.swing.SwingUtilities;
- // Phase 2: VM execution
- Logger.section("PHASE 2: VM EXECUTION [CyclicBarrier + Semaphore]");
- int[] cycleNum = { 0 };
- ClockSynchronizer clock = new ClockSynchronizer(NUM_VMS, cycleNum);
- NetworkPortManager networkManager = new NetworkPortManager();
-
- Logger.log("HYPERVISOR",
- "Launching " + NUM_VMS + " VMs for " + NUM_CYCLES + " cycles each...",
- Logger.CYAN);
-
- Thread[] vmThreads = new Thread[NUM_VMS];
- for (int i = 1; i <= NUM_VMS; i++) {
- int workDuration = 600 + (i * 200);
-
- VirtualMachine vm = new VirtualMachine(
- "VM-" + i, NUM_CYCLES, clock, networkManager, workDuration);
- vmThreads[i - 1] = new Thread(vm, "VM-" + i);
- vmThreads[i - 1].start();
- }
-
- for (Thread t : vmThreads) {
- t.join();
- }
+/**
+ * Application entry point for CloudKernel.
+ */
+public class Main {
- // Phase 3: shutdown
- Logger.section("PHASE 3: SYSTEM SHUTDOWN");
- Logger.log("HYPERVISOR", "All VMs have completed execution.", Logger.GREEN);
- Logger.log("HYPERVISOR", "Releasing all system resources...", Logger.YELLOW);
- Thread.sleep(300);
- Logger.log("HYPERVISOR", "CloudKernel has shut down cleanly. Goodbye. [OK]", Logger.BOLD + Logger.GREEN);
- Logger.separator();
- System.out.println();
+ /**
+ * Starts the CloudKernel GUI on the Swing event dispatch thread.
+ *
+ * @param args command-line arguments (unused)
+ */
+ public static void main(String[] args) {
+ SwingUtilities.invokeLater(() -> new CloudKernelGUI().setVisible(true));
}
}
\ No newline at end of file
diff --git a/CloudKernel/src/config/ConfigLoader.java b/CloudKernel/src/config/ConfigLoader.java
new file mode 100644
index 0000000..5da04fe
--- /dev/null
+++ b/CloudKernel/src/config/ConfigLoader.java
@@ -0,0 +1,190 @@
+package config;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+/**
+ * Loads simulator configuration from config.properties.
+ * Falls back to sane defaults when file loading fails.
+ */
+public class ConfigLoader {
+
+ private static final String CONFIG_FILE = "config.properties";
+ private final Properties properties;
+
+ /**
+ * Creates a loader and immediately reads configuration values.
+ */
+ public ConfigLoader() {
+ properties = new Properties();
+ loadConfig();
+ }
+
+ /**
+ * Loads configuration from file or uses defaults.
+ */
+ private void loadConfig() {
+ try (InputStream input = new FileInputStream(CONFIG_FILE)) {
+ properties.load(input);
+ System.out.println("[CONFIG] Loaded configuration from: " + CONFIG_FILE);
+ } catch (FileNotFoundException e) {
+ System.out.println("[CONFIG] config.properties not found. Using defaults.");
+ loadDefaults();
+ } catch (IOException e) {
+ System.out.println("[CONFIG] Error reading config file: " + e.getMessage());
+ loadDefaults();
+ }
+ }
+
+ /**
+ * Applies default values for all recognized properties.
+ */
+ private void loadDefaults() {
+ properties.setProperty("vm.count", "5");
+ properties.setProperty("cycle.count", "4");
+ properties.setProperty("semaphore.cpu.permits", "3");
+ properties.setProperty("semaphore.memory.permits", "2");
+ properties.setProperty("semaphore.network.permits", "2");
+ properties.setProperty("task.duration.min", "500");
+ properties.setProperty("task.duration.max", "1500");
+ properties.setProperty("timeout.duration", "2000");
+ properties.setProperty("gui.enabled", "true");
+ properties.setProperty("logging.level", "NORMAL");
+ properties.setProperty("stats.enabled", "true");
+ }
+
+ /**
+ * Reads an integer property.
+ *
+ * @param key property name
+ * @param defaultValue fallback value
+ * @return parsed integer or fallback
+ */
+ public int getInt(String key, int defaultValue) {
+ try {
+ return Integer.parseInt(properties.getProperty(key, String.valueOf(defaultValue)));
+ } catch (NumberFormatException e) {
+ System.out.println("[CONFIG] Invalid integer for key: " + key + ", using default: " + defaultValue);
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Reads a long property.
+ *
+ * @param key property name
+ * @param defaultValue fallback value
+ * @return parsed long or fallback
+ */
+ public long getLong(String key, long defaultValue) {
+ try {
+ return Long.parseLong(properties.getProperty(key, String.valueOf(defaultValue)));
+ } catch (NumberFormatException e) {
+ System.out.println("[CONFIG] Invalid long for key: " + key + ", using default: " + defaultValue);
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Reads a string property.
+ *
+ * @param key property name
+ * @param defaultValue fallback value
+ * @return property value or fallback
+ */
+ public String getString(String key, String defaultValue) {
+ return properties.getProperty(key, defaultValue);
+ }
+
+ /**
+ * Reads a boolean property.
+ *
+ * @param key property name
+ * @param defaultValue fallback value
+ * @return parsed boolean or fallback
+ */
+ public boolean getBoolean(String key, boolean defaultValue) {
+ String value = properties.getProperty(key, String.valueOf(defaultValue));
+ return Boolean.parseBoolean(value);
+ }
+
+ /**
+ * @return configured VM count
+ */
+ public int getVMCount() {
+ return getInt("vm.count", 5);
+ }
+
+ /**
+ * @return configured cycle count per VM
+ */
+ public int getCycleCount() {
+ return getInt("cycle.count", 4);
+ }
+
+ /**
+ * @return configured CPU permits
+ */
+ public int getCPUPermits() {
+ return getInt("semaphore.cpu.permits", 3);
+ }
+
+ /**
+ * @return configured memory permits
+ */
+ public int getMemoryPermits() {
+ return getInt("semaphore.memory.permits", 2);
+ }
+
+ /**
+ * @return configured network permits
+ */
+ public int getNetworkPermits() {
+ return getInt("semaphore.network.permits", 2);
+ }
+
+ /**
+ * @return minimum task duration in milliseconds
+ */
+ public int getTaskDurationMin() {
+ return getInt("task.duration.min", 500);
+ }
+
+ /**
+ * @return maximum task duration in milliseconds
+ */
+ public int getTaskDurationMax() {
+ return getInt("task.duration.max", 1500);
+ }
+
+ /**
+ * @return resource acquisition timeout in milliseconds
+ */
+ public long getTimeoutDuration() {
+ return getLong("timeout.duration", 2000);
+ }
+
+ /**
+ * @return true if GUI mode is enabled
+ */
+ public boolean isGUIEnabled() {
+ return getBoolean("gui.enabled", true);
+ }
+
+ /**
+ * @return configured logging level label
+ */
+ public String getLoggingLevel() {
+ return getString("logging.level", "NORMAL");
+ }
+
+ /**
+ * @return true if stats collection is enabled
+ */
+ public boolean isStatsEnabled() {
+ return getBoolean("stats.enabled", true);
+ }
+}
diff --git a/CloudKernel/src/core/BootManager.java b/CloudKernel/src/core/BootManager.java
index 7a7697a..aa04354 100644
--- a/CloudKernel/src/core/BootManager.java
+++ b/CloudKernel/src/core/BootManager.java
@@ -1,34 +1,51 @@
package core;
-import utils.Logger;
+import utils.GUILogger;
import java.util.concurrent.CountDownLatch;
-// Handles system boot readiness using CountDownLatch.
+/**
+ * Coordinates CloudKernel boot initialization using a countdown latch.
+ */
public class BootManager {
- private final CountDownLatch bootLatch = new CountDownLatch(2);
+ private final CountDownLatch bootLatch = new CountDownLatch(4);
+ private final GUILogger logger;
+ /**
+ * Creates a boot manager.
+ *
+ * @param logger logger used for boot event reporting
+ */
+ public BootManager(GUILogger logger) {
+ this.logger = logger;
+ }
+
+ /**
+ * Starts disk subsystem initialization on its own thread.
+ */
public void initDisk() {
new Thread(() -> {
try {
- Logger.log("BOOT", "Disk subsystem starting...", Logger.YELLOW);
+ logger.log("SYSTEM", "Disk subsystem starting...", "BOOT");
Thread.sleep(1500);
- Logger.log("BOOT", "Disk subsystem initialized. [OK]", Logger.GREEN);
+ logger.log("SYSTEM", "Disk subsystem initialized. [OK]", "BOOT");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
- // Count down in finally to avoid deadlock on errors.
bootLatch.countDown();
}
}, "Disk-Init-Thread").start();
}
+ /**
+ * Starts RAM subsystem initialization on its own thread.
+ */
public void initRAM() {
new Thread(() -> {
try {
- Logger.log("BOOT", "RAM subsystem starting...", Logger.YELLOW);
+ logger.log("SYSTEM", "RAM subsystem starting...", "BOOT");
Thread.sleep(1000);
- Logger.log("BOOT", "RAM subsystem initialized. [OK]", Logger.GREEN);
+ logger.log("SYSTEM", "RAM subsystem initialized. [OK]", "BOOT");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
@@ -37,9 +54,57 @@ public void initRAM() {
}, "RAM-Init-Thread").start();
}
+ /**
+ * Starts network stack initialization on its own thread.
+ */
+ public void initNetworkStack() {
+ new Thread(() -> {
+ try {
+ logger.log("SYSTEM", "Network Stack initializing...", "BOOT");
+ Thread.sleep(1200);
+ logger.log("SYSTEM", "Network Stack initialized. [OK]", "BOOT");
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ } finally {
+ bootLatch.countDown();
+ }
+ }, "Network-Init-Thread").start();
+ }
+
+ /**
+ * Starts CPU scheduler initialization on its own thread.
+ */
+ public void initCPUScheduler() {
+ new Thread(() -> {
+ try {
+ logger.log("SYSTEM", "CPU Scheduler initializing...", "BOOT");
+ Thread.sleep(800);
+ logger.log("SYSTEM", "CPU Scheduler initialized. [OK]", "BOOT");
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ } finally {
+ bootLatch.countDown();
+ }
+ }, "CPU-Init-Thread").start();
+ }
+
+ /**
+ * Blocks until all boot tasks complete.
+ *
+ * @throws InterruptedException if interrupted while waiting
+ */
public void awaitBootCompletion() throws InterruptedException {
- Logger.log("BOOT", "Hypervisor waiting for subsystems...", Logger.CYAN);
+ logger.boot("Hypervisor waiting for all subsystems...");
bootLatch.await();
- Logger.log("BOOT", "All subsystems ready. CloudKernel is ONLINE. [OK]", Logger.GREEN);
+ logger.boot("All subsystems ready. CloudKernel is ONLINE.");
+ }
+
+ /**
+ * Returns the latch used by the boot phase.
+ *
+ * @return boot latch
+ */
+ public CountDownLatch getBootLatch() {
+ return bootLatch;
}
}
\ No newline at end of file
diff --git a/CloudKernel/src/core/ClockSynchronizer.java b/CloudKernel/src/core/ClockSynchronizer.java
index cfbf217..5fd89f3 100644
--- a/CloudKernel/src/core/ClockSynchronizer.java
+++ b/CloudKernel/src/core/ClockSynchronizer.java
@@ -1,28 +1,64 @@
package core;
-import utils.Logger;
+import utils.GUILogger;
+import utils.StatsCollector;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
-// Keeps all VM threads synchronized at each cycle.
+/**
+ * Synchronizes VM cycle boundaries through a cyclic barrier.
+ */
public class ClockSynchronizer {
private final CyclicBarrier barrier;
+ private final GUILogger logger;
+ private final StatsCollector stats;
+ private int cycleCount = 0;
+
+ /**
+ * Creates a clock synchronizer.
+ *
+ * @param vmCount number of participating VM threads
+ * @param logger logger instance
+ * @param stats statistics collector
+ */
+ public ClockSynchronizer(int vmCount, GUILogger logger, StatsCollector stats) {
+ this.logger = logger;
+ this.stats = stats;
- public ClockSynchronizer(int vmCount, int[] cycleNum) {
Runnable clockTick = () -> {
- cycleNum[0]++;
- Logger.log("CLOCK",
- "=== Global Clock Tick #" + cycleNum[0] +
- " - All VMs synchronized. Next cycle begins. ===",
- Logger.BOLD + Logger.CYAN);
+ cycleCount++;
+ GUILogger.cycleComplete(cycleCount);
+ logger.boot("Global Clock Tick #" + cycleCount + " - All VMs synchronized.");
+ stats.recordCycleCompletion();
};
this.barrier = new CyclicBarrier(vmCount, clockTick);
}
+ /**
+ * Waits for all VMs to arrive at the barrier and synchronizes the cycle.
+ *
+ * @param vmName VM identifier for log messages
+ * @throws InterruptedException if interrupted while waiting
+ * @throws BrokenBarrierException if the barrier is broken
+ */
public void sync(String vmName) throws InterruptedException, BrokenBarrierException {
- Logger.log(vmName, "Work unit done. Waiting at clock barrier...", Logger.YELLOW);
+ logger.log(vmName, "Work unit done. Waiting at clock barrier...", "BARRIER");
barrier.await();
}
+
+ /**
+ * @return underlying cyclic barrier
+ */
+ public CyclicBarrier getBarrier() {
+ return barrier;
+ }
+
+ /**
+ * @return completed cycle count
+ */
+ public int getCycleCount() {
+ return cycleCount;
+ }
}
\ No newline at end of file
diff --git a/CloudKernel/src/core/NetworkPortManager.java b/CloudKernel/src/core/NetworkPortManager.java
deleted file mode 100644
index 9cb5c4a..0000000
--- a/CloudKernel/src/core/NetworkPortManager.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package core;
-
-import utils.Logger;
-import java.util.concurrent.Semaphore;
-
-// Manages limited network ports using a fair semaphore.
-public class NetworkPortManager {
-
- private static final int TOTAL_PORTS = 2;
- private final Semaphore networkPorts = new Semaphore(TOTAL_PORTS, true);
-
- public void acquirePort(String vmName) throws InterruptedException {
- Logger.log(vmName,
- "Requesting Network Port... (available: " + networkPorts.availablePermits() + "/" + TOTAL_PORTS + ")",
- Logger.YELLOW);
-
- networkPorts.acquire();
- int inUse = TOTAL_PORTS - networkPorts.availablePermits();
-
- Logger.log(vmName,
- "Network Port GRANTED. (in use: " + inUse + "/" + TOTAL_PORTS + ") Transmitting data...",
- Logger.GREEN);
- }
-
- public void releasePort(String vmName) {
- networkPorts.release();
- Logger.log(vmName,
- "Network Port RELEASED. (available: " + networkPorts.availablePermits() + "/" + TOTAL_PORTS + ")",
- Logger.CYAN);
- }
-}
\ No newline at end of file
diff --git a/CloudKernel/src/entities/ResourceManager.java b/CloudKernel/src/entities/ResourceManager.java
new file mode 100644
index 0000000..66f73c5
--- /dev/null
+++ b/CloudKernel/src/entities/ResourceManager.java
@@ -0,0 +1,194 @@
+package entities;
+
+import utils.GUILogger;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Manages shared CPU, memory, and network resources using fair semaphores.
+ */
+public class ResourceManager {
+ private final Semaphore cpuSemaphore;
+ private final Semaphore memorySemaphore;
+ private final Semaphore networkSemaphore;
+ private final int cpuPermits;
+ private final int memoryPermits;
+ private final int networkPermits;
+
+ private final Map cpuHolders = new ConcurrentHashMap<>();
+ private final Map memoryHolders = new ConcurrentHashMap<>();
+ private final Map networkHolders = new ConcurrentHashMap<>();
+
+ private final GUILogger logger;
+ private final long timeout;
+
+ /**
+ * Creates a resource manager.
+ *
+ * @param cpuPermits total CPU permits
+ * @param memoryPermits total memory permits
+ * @param networkPermits total network permits
+ * @param timeout resource acquisition timeout in milliseconds
+ * @param logger logger for resource events
+ */
+ public ResourceManager(int cpuPermits, int memoryPermits, int networkPermits, long timeout, GUILogger logger) {
+ this.cpuSemaphore = new Semaphore(cpuPermits, true);
+ this.memorySemaphore = new Semaphore(memoryPermits, true);
+ this.networkSemaphore = new Semaphore(networkPermits, true);
+ this.cpuPermits = cpuPermits;
+ this.memoryPermits = memoryPermits;
+ this.networkPermits = networkPermits;
+ this.timeout = timeout;
+ this.logger = logger;
+ }
+
+ /**
+ * Attempts to acquire a CPU permit.
+ *
+ * @param vmName VM requesting the permit
+ * @return true if permit acquired, false on timeout
+ * @throws InterruptedException if interrupted while waiting
+ */
+ public boolean acquireCPU(String vmName) throws InterruptedException {
+ if (cpuSemaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS)) {
+ String coreId = "CPU-" + (cpuPermits - cpuSemaphore.availablePermits());
+ cpuHolders.put(coreId, vmName);
+ logger.log(vmName,
+ "acquired CPU core. (available: " + cpuSemaphore.availablePermits() + "/" + cpuPermits + ")",
+ "CPU");
+ return true;
+ } else {
+ logger.log(vmName, "[TIMEOUT] Could not acquire CPU core within " + timeout + "ms", "TIMEOUT");
+ return false;
+ }
+ }
+
+ /**
+ * Releases a CPU permit for the specified VM.
+ *
+ * @param vmName VM releasing the permit
+ */
+ public void releaseCPU(String vmName) {
+ cpuSemaphore.release();
+ cpuHolders.values().remove(vmName);
+ logger.log(vmName,
+ "released CPU core. (available: " + cpuSemaphore.availablePermits() + "/" + cpuPermits + ")",
+ "CPU");
+ }
+
+ /**
+ * Attempts to acquire a memory permit.
+ *
+ * @param vmName VM requesting the permit
+ * @return true if permit acquired, false on timeout
+ * @throws InterruptedException if interrupted while waiting
+ */
+ public boolean acquireMemory(String vmName) throws InterruptedException {
+ if (memorySemaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS)) {
+ String memId = "MEM-" + (memoryPermits - memorySemaphore.availablePermits());
+ memoryHolders.put(memId, vmName);
+ logger.log(vmName,
+ "acquired Memory block. (available: " + memorySemaphore.availablePermits() + "/"
+ + memoryPermits + ")",
+ "MEMORY");
+ return true;
+ } else {
+ logger.log(vmName, "[TIMEOUT] Could not acquire Memory block within " + timeout + "ms", "TIMEOUT");
+ return false;
+ }
+ }
+
+ /**
+ * Releases a memory permit for the specified VM.
+ *
+ * @param vmName VM releasing the permit
+ */
+ public void releaseMemory(String vmName) {
+ memorySemaphore.release();
+ memoryHolders.values().remove(vmName);
+ logger.log(vmName,
+ "released Memory block. (available: " + memorySemaphore.availablePermits() + "/" + memoryPermits
+ + ")",
+ "MEMORY");
+ }
+
+ /**
+ * Attempts to acquire a network permit.
+ *
+ * @param vmName VM requesting the permit
+ * @return true if permit acquired, false on timeout
+ * @throws InterruptedException if interrupted while waiting
+ */
+ public boolean acquireNetwork(String vmName) throws InterruptedException {
+ if (networkSemaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS)) {
+ String portId = "PORT-" + (networkPermits - networkSemaphore.availablePermits());
+ networkHolders.put(portId, vmName);
+ logger.log(vmName,
+ "acquired Network port. (available: " + networkSemaphore.availablePermits() + "/"
+ + networkPermits + ")",
+ "NETWORK");
+ return true;
+ } else {
+ logger.log(vmName, "[TIMEOUT] Could not acquire Network port within " + timeout + "ms", "TIMEOUT");
+ return false;
+ }
+ }
+
+ /**
+ * Releases a network permit for the specified VM.
+ *
+ * @param vmName VM releasing the permit
+ */
+ public void releaseNetwork(String vmName) {
+ networkSemaphore.release();
+ networkHolders.values().remove(vmName);
+ logger.log(vmName,
+ "released Network port. (available: " + networkSemaphore.availablePermits() + "/" + networkPermits
+ + ")",
+ "NETWORK");
+ }
+
+ /**
+ * @return available CPU permits
+ */
+ public int getCPUAvailable() {
+ return cpuSemaphore.availablePermits();
+ }
+
+ /**
+ * @return available memory permits
+ */
+ public int getMemoryAvailable() {
+ return memorySemaphore.availablePermits();
+ }
+
+ /**
+ * @return available network permits
+ */
+ public int getNetworkAvailable() {
+ return networkSemaphore.availablePermits();
+ }
+
+ /**
+ * @return map of CPU slot holders
+ */
+ public Map getCPUHolders() {
+ return cpuHolders;
+ }
+
+ /**
+ * @return map of memory slot holders
+ */
+ public Map getMemoryHolders() {
+ return memoryHolders;
+ }
+
+ /**
+ * @return map of network slot holders
+ */
+ public Map getNetworkHolders() {
+ return networkHolders;
+ }
+}
diff --git a/CloudKernel/src/entities/VMPriority.java b/CloudKernel/src/entities/VMPriority.java
new file mode 100644
index 0000000..8f87f18
--- /dev/null
+++ b/CloudKernel/src/entities/VMPriority.java
@@ -0,0 +1,56 @@
+package entities;
+
+/**
+ * Priority levels assigned to virtual machines.
+ */
+public enum VMPriority {
+ LOW(1, "LOW", "\u001B[36m"), // Cyan
+ MEDIUM(2, "MEDIUM", "\u001B[33m"), // Yellow
+ HIGH(3, "HIGH", "\u001B[31m"); // Red
+
+ private final int value;
+ private final String label;
+ private final String color;
+
+ VMPriority(int value, String label, String color) {
+ this.value = value;
+ this.label = label;
+ this.color = color;
+ }
+
+ /**
+ * @return numeric priority value
+ */
+ public int getValue() {
+ return value;
+ }
+
+ /**
+ * @return display label
+ */
+ public String getLabel() {
+ return label;
+ }
+
+ /**
+ * @return ANSI color code for console output
+ */
+ public String getColor() {
+ return color;
+ }
+
+ /**
+ * Returns a random priority.
+ *
+ * @return randomly selected priority
+ */
+ public static VMPriority getRandomPriority() {
+ int rand = (int) (Math.random() * 3);
+ return values()[rand];
+ }
+
+ @Override
+ public String toString() {
+ return color + label + "\u001B[0m";
+ }
+}
diff --git a/CloudKernel/src/entities/VMState.java b/CloudKernel/src/entities/VMState.java
new file mode 100644
index 0000000..0149d74
--- /dev/null
+++ b/CloudKernel/src/entities/VMState.java
@@ -0,0 +1,43 @@
+package entities;
+
+/**
+ * Defines VM lifecycle states used by the simulator and dashboard.
+ */
+public enum VMState {
+ BOOTING("BOOTING", "\u001B[33m"), // Yellow
+ READY("READY", "\u001B[32m"), // Green
+ RUNNING("RUNNING", "\u001B[36m"), // Cyan
+ REQUESTING_RESOURCE("REQUESTING", "\u001B[33m"), // Yellow
+ USING_RESOURCE("USING", "\u001B[32m"), // Green
+ RELEASING("RELEASING", "\u001B[34m"), // Blue
+ BARRIER_WAIT("BARRIER WAIT", "\u001B[35m"), // Purple
+ TIMEOUT("TIMEOUT", "\u001B[31m"), // Red
+ SHUTDOWN("SHUTDOWN", "\u001B[37m"); // White
+
+ private final String label;
+ private final String color;
+
+ VMState(String label, String color) {
+ this.label = label;
+ this.color = color;
+ }
+
+ /**
+ * @return display label for the state
+ */
+ public String getLabel() {
+ return label;
+ }
+
+ /**
+ * @return ANSI color code used for console rendering
+ */
+ public String getColor() {
+ return color;
+ }
+
+ @Override
+ public String toString() {
+ return color + label + "\u001B[0m";
+ }
+}
diff --git a/CloudKernel/src/entities/VMStats.java b/CloudKernel/src/entities/VMStats.java
new file mode 100644
index 0000000..79124fe
--- /dev/null
+++ b/CloudKernel/src/entities/VMStats.java
@@ -0,0 +1,102 @@
+package entities;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Stores per-VM runtime statistics.
+ */
+public class VMStats {
+ private final String vmName;
+ private final AtomicInteger tasksCompleted = new AtomicInteger(0);
+ private final AtomicInteger networkUses = new AtomicInteger(0);
+ private final AtomicInteger cpuUses = new AtomicInteger(0);
+ private final AtomicInteger memoryUses = new AtomicInteger(0);
+ private final AtomicInteger timeouts = new AtomicInteger(0);
+ private final AtomicLong totalWaitTime = new AtomicLong(0);
+ private final AtomicInteger waitCount = new AtomicInteger(0);
+
+ public VMStats(String vmName) {
+ this.vmName = vmName;
+ }
+
+ /** Records one completed task unit. */
+ public void recordTaskCompleted() {
+ tasksCompleted.incrementAndGet();
+ }
+
+ /** Records one network acquisition. */
+ public void recordNetworkUse() {
+ networkUses.incrementAndGet();
+ }
+
+ /** Records one CPU acquisition. */
+ public void recordCPUUse() {
+ cpuUses.incrementAndGet();
+ }
+
+ /** Records one memory acquisition. */
+ public void recordMemoryUse() {
+ memoryUses.incrementAndGet();
+ }
+
+ /** Records one timeout event. */
+ public void recordTimeout() {
+ timeouts.incrementAndGet();
+ }
+
+ /**
+ * Adds a wait duration sample.
+ *
+ * @param waitTimeMs wait duration in milliseconds
+ */
+ public void recordWaitTime(long waitTimeMs) {
+ totalWaitTime.addAndGet(waitTimeMs);
+ waitCount.incrementAndGet();
+ }
+
+ /** @return total completed tasks */
+ public int getTasksCompleted() {
+ return tasksCompleted.get();
+ }
+
+ /** @return total network acquisitions */
+ public int getNetworkUses() {
+ return networkUses.get();
+ }
+
+ /** @return total CPU acquisitions */
+ public int getCPUUses() {
+ return cpuUses.get();
+ }
+
+ /** @return total memory acquisitions */
+ public int getMemoryUses() {
+ return memoryUses.get();
+ }
+
+ /** @return total timeout count */
+ public int getTimeouts() {
+ return timeouts.get();
+ }
+
+ /** @return average wait time in milliseconds */
+ public long getAverageWaitTime() {
+ if (waitCount.get() == 0)
+ return 0;
+ return totalWaitTime.get() / waitCount.get();
+ }
+
+ /** @return total resource uses across CPU, memory, and network */
+ public int getTotalResourceUses() {
+ return networkUses.get() + cpuUses.get() + memoryUses.get();
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "%s | Tasks: %d | Network: %d | CPU: %d | Memory: %d | Timeouts: %d | Avg Wait: %dms",
+ vmName, tasksCompleted.get(), networkUses.get(), cpuUses.get(),
+ memoryUses.get(), timeouts.get(), getAverageWaitTime());
+ }
+}
diff --git a/CloudKernel/src/entities/VirtualMachine.java b/CloudKernel/src/entities/VirtualMachine.java
index eb0bfd8..5580b57 100644
--- a/CloudKernel/src/entities/VirtualMachine.java
+++ b/CloudKernel/src/entities/VirtualMachine.java
@@ -1,51 +1,182 @@
package entities;
import core.ClockSynchronizer;
-import core.NetworkPortManager;
-import utils.Logger;
+import utils.GUILogger;
+import utils.StatsCollector;
+
import java.util.concurrent.BrokenBarrierException;
-// Represents one VM thread in the simulation.
+/**
+ * Represents a virtual machine worker in the simulation.
+ */
public class VirtualMachine implements Runnable {
private final String name;
+ private final int vmId;
private final int cycles;
private final ClockSynchronizer clock;
- private final NetworkPortManager networkManager;
+ private final ResourceManager resourceManager;
private final int workDuration;
+ private final GUILogger logger;
+ private final VMPriority priority;
+ private final VMStats stats;
+ private final StatsCollector statsCollector;
+
+ private VMState currentState;
- public VirtualMachine(String name, int cycles, ClockSynchronizer clock,
- NetworkPortManager networkManager, int workDuration) {
+ /**
+ * Creates a virtual machine runnable.
+ *
+ * @param name VM name
+ * @param vmId VM numeric identifier
+ * @param cycles number of cycles to execute
+ * @param clock cycle synchronizer
+ * @param resourceManager shared resource manager
+ * @param workDuration simulated workload duration in milliseconds
+ * @param logger logger for runtime events
+ * @param priority assigned VM priority
+ * @param stats per-VM statistics collector
+ * @param statsCollector global statistics collector
+ */
+ public VirtualMachine(String name, int vmId, int cycles, ClockSynchronizer clock,
+ ResourceManager resourceManager, int workDuration, GUILogger logger,
+ VMPriority priority, VMStats stats, StatsCollector statsCollector) {
this.name = name;
+ this.vmId = vmId;
this.cycles = cycles;
this.clock = clock;
- this.networkManager = networkManager;
+ this.resourceManager = resourceManager;
this.workDuration = workDuration;
+ this.logger = logger;
+ this.priority = priority;
+ this.stats = stats;
+ this.statsCollector = statsCollector;
+ this.currentState = VMState.BOOTING;
}
+ /**
+ * Executes the VM lifecycle across all configured cycles.
+ */
@Override
public void run() {
- Logger.log(name, "Virtual Machine is ONLINE.", Logger.GREEN);
-
try {
+ setState(VMState.READY);
+ logger.log(name, "Virtual Machine is ONLINE. Priority: " + priority, "BOOT");
+
for (int i = 1; i <= cycles; i++) {
- Logger.log(name, "Cycle " + i + " - executing workload...", Logger.CYAN);
+ logger.log(name, "Starting Cycle " + i + " of " + cycles, "INFO");
+
+ setState(VMState.RUNNING);
+ logger.log(name, "Executing workload for " + workDuration + "ms...", "INFO");
Thread.sleep(workDuration);
+ stats.recordTaskCompleted();
- networkManager.acquirePort(name);
- Thread.sleep(500);
- networkManager.releasePort(name);
+ // Request and use CPU
+ setState(VMState.REQUESTING_RESOURCE);
+ long cpuWaitStart = System.currentTimeMillis();
+ if (resourceManager.acquireCPU(name)) {
+ setState(VMState.USING_RESOURCE);
+ Thread.sleep(300);
+ resourceManager.releaseCPU(name);
+ setState(VMState.RELEASING);
+ stats.recordCPUUse();
+ stats.recordWaitTime(System.currentTimeMillis() - cpuWaitStart);
+ } else {
+ stats.recordTimeout();
+ statsCollector.recordTimeout();
+ }
+ // Request and use Memory
+ setState(VMState.REQUESTING_RESOURCE);
+ long memWaitStart = System.currentTimeMillis();
+ if (resourceManager.acquireMemory(name)) {
+ setState(VMState.USING_RESOURCE);
+ Thread.sleep(250);
+ resourceManager.releaseMemory(name);
+ setState(VMState.RELEASING);
+ stats.recordMemoryUse();
+ stats.recordWaitTime(System.currentTimeMillis() - memWaitStart);
+ } else {
+ stats.recordTimeout();
+ statsCollector.recordTimeout();
+ }
+
+ // Request and use Network
+ setState(VMState.REQUESTING_RESOURCE);
+ long netWaitStart = System.currentTimeMillis();
+ if (resourceManager.acquireNetwork(name)) {
+ setState(VMState.USING_RESOURCE);
+ Thread.sleep(500);
+ resourceManager.releaseNetwork(name);
+ setState(VMState.RELEASING);
+ stats.recordNetworkUse();
+ stats.recordWaitTime(System.currentTimeMillis() - netWaitStart);
+ } else {
+ stats.recordTimeout();
+ statsCollector.recordTimeout();
+ }
+
+ // Barrier synchronization
+ setState(VMState.BARRIER_WAIT);
+ logger.log(name, "All resources released. Waiting at barrier...", "BARRIER");
clock.sync(name);
+ setState(VMState.READY);
}
- Logger.log(name, "All cycles complete. Shutting down gracefully. [OK]", Logger.GREEN);
+ setState(VMState.SHUTDOWN);
+ logger.log(name, "All cycles complete. Shutting down gracefully. [OK]", "BOOT");
} catch (InterruptedException e) {
- Logger.log(name, "Interrupted during execution!", Logger.RED);
+ logger.log(name, "Interrupted during execution!", "ERROR");
+ setState(VMState.SHUTDOWN);
Thread.currentThread().interrupt();
} catch (BrokenBarrierException e) {
- Logger.log(name, "Clock barrier broken - system error!", Logger.RED);
+ logger.log(name, "Clock barrier broken - system error!", "ERROR");
+ setState(VMState.SHUTDOWN);
}
}
+
+ /**
+ * Updates the current VM state.
+ *
+ * @param newState next state
+ */
+ private void setState(VMState newState) {
+ this.currentState = newState;
+ }
+
+ /**
+ * @return current VM state
+ */
+ public VMState getCurrentState() {
+ return currentState;
+ }
+
+ /**
+ * @return assigned VM priority
+ */
+ public VMPriority getPriority() {
+ return priority;
+ }
+
+ /**
+ * @return per-VM statistics object
+ */
+ public VMStats getStats() {
+ return stats;
+ }
+
+ /**
+ * @return VM display name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return numeric VM identifier
+ */
+ public int getVMId() {
+ return vmId;
+ }
}
\ No newline at end of file
diff --git a/CloudKernel/src/shutdown/ShutdownManager.java b/CloudKernel/src/shutdown/ShutdownManager.java
new file mode 100644
index 0000000..214a47f
--- /dev/null
+++ b/CloudKernel/src/shutdown/ShutdownManager.java
@@ -0,0 +1,38 @@
+package shutdown;
+
+import utils.GUILogger;
+import utils.StatsCollector;
+
+/**
+ * Registers a JVM shutdown hook to print a graceful summary.
+ */
+public class ShutdownManager {
+ private static StatsCollector statsCollector;
+
+ /**
+ * Creates and registers the shutdown hook.
+ *
+ * @param stats stats collector used for summary output
+ */
+ public ShutdownManager(StatsCollector stats) {
+ ShutdownManager.statsCollector = stats;
+ setupShutdownHook();
+ }
+
+ /** Registers the shutdown hook with the runtime. */
+ private void setupShutdownHook() {
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ System.out.println();
+ GUILogger.section("INTERRUPTED - GRACEFUL SHUTDOWN");
+ System.out.println(GUILogger.YELLOW + "Received shutdown signal..." + GUILogger.RESET);
+
+ if (statsCollector != null) {
+ statsCollector.printSummary();
+ }
+
+ System.out.println();
+ System.out.println(GUILogger.GREEN + "CloudKernel shut down gracefully." + GUILogger.RESET);
+ System.out.println();
+ }));
+ }
+}
diff --git a/CloudKernel/src/ui/BarrierPanel.java b/CloudKernel/src/ui/BarrierPanel.java
new file mode 100644
index 0000000..9ef4b4d
--- /dev/null
+++ b/CloudKernel/src/ui/BarrierPanel.java
@@ -0,0 +1,118 @@
+package ui;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import java.awt.*;
+
+/**
+ * Visualizes CyclicBarrier arrivals and cycle transitions.
+ */
+public class BarrierPanel extends JPanel {
+ private static final Color BG_PANEL = new Color(16, 24, 38);
+ private static final Color TEXT_PRIMARY = new Color(200, 216, 240);
+ private static final Color ACCENT_PURPLE = new Color(180, 80, 255);
+ private static final Color ACCENT_GREEN = new Color(0, 255, 136);
+ private static final Color ACCENT_CYAN = new Color(0, 212, 255);
+
+ private final JLabel cycleLabel;
+ private final JLabel arrivedLabel;
+ private final JLabel[] dots;
+ private final Timer flashTimer;
+
+ /**
+ * Creates the barrier panel.
+ *
+ * @param vmCount number of VM participants
+ */
+ public BarrierPanel(int vmCount) {
+ setLayout(new BorderLayout(0, 8));
+ setBackground(BG_PANEL);
+ setBorder(new EmptyBorder(8, 10, 8, 10));
+
+ JLabel title = new JLabel("CYCLIC BARRIER - Global Clock Sync");
+ title.setForeground(ACCENT_PURPLE);
+ title.setFont(CloudKernelGUI.uiFont(Font.BOLD, 12));
+ add(title, BorderLayout.NORTH);
+
+ JPanel center = new JPanel(new BorderLayout());
+ center.setOpaque(false);
+
+ JPanel dotsRow = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0));
+ dotsRow.setOpaque(false);
+ dots = new JLabel[vmCount];
+ for (int i = 0; i < vmCount; i++) {
+ JLabel dot = new JLabel("\u25cf VM-" + (i + 1));
+ dot.setFont(CloudKernelGUI.getSymbolFont(Font.BOLD, 11));
+ dot.setForeground(new Color(92, 99, 116));
+ dots[i] = dot;
+ dotsRow.add(dot);
+ }
+
+ center.add(dotsRow, BorderLayout.CENTER);
+
+ JPanel infoRow = new JPanel(new FlowLayout(FlowLayout.LEFT, 18, 0));
+ infoRow.setOpaque(false);
+ cycleLabel = new JLabel("CYCLE: 0");
+ cycleLabel.setForeground(TEXT_PRIMARY);
+ cycleLabel.setFont(CloudKernelGUI.uiFont(Font.BOLD, 11));
+ arrivedLabel = new JLabel("Arrived: 0 / " + vmCount);
+ arrivedLabel.setForeground(ACCENT_CYAN);
+ arrivedLabel.setFont(CloudKernelGUI.uiFont(Font.PLAIN, 11));
+ infoRow.add(cycleLabel);
+ infoRow.add(arrivedLabel);
+
+ center.add(infoRow, BorderLayout.SOUTH);
+ add(center, BorderLayout.CENTER);
+
+ flashTimer = new Timer(260, e -> resetAfterFlash());
+ flashTimer.setRepeats(false);
+ }
+
+ /**
+ * Marks one VM as arrived.
+ *
+ * @param vmId VM id
+ * @param arrivedCount arrived count
+ * @param vmCount total VM count
+ */
+ public void markArrived(int vmId, int arrivedCount, int vmCount) {
+ if (vmId >= 1 && vmId <= dots.length) {
+ dots[vmId - 1].setForeground(ACCENT_PURPLE);
+ }
+ arrivedLabel.setText("Arrived: " + arrivedCount + " / " + vmCount);
+ }
+
+ /**
+ * Updates displayed cycle number.
+ *
+ * @param cycle cycle number
+ */
+ public void setCycle(int cycle) {
+ cycleLabel.setText("CYCLE: " + cycle);
+ }
+
+ /**
+ * Flashes all dots green and schedules state reset.
+ *
+ * @param vmCount total VM count
+ */
+ public void flashAllAndReset(int vmCount) {
+ for (JLabel dot : dots) {
+ dot.setForeground(ACCENT_GREEN);
+ }
+ arrivedLabel.setText("Arrived: " + vmCount + " / " + vmCount);
+ flashTimer.restart();
+ }
+
+ /** Resets dot state after flash animation. */
+ private void resetAfterFlash() {
+ for (JLabel dot : dots) {
+ dot.setForeground(new Color(92, 99, 116));
+ }
+ String text = arrivedLabel.getText();
+ if (text.contains("/")) {
+ String[] parts = text.split("/");
+ arrivedLabel.setText("Arrived: 0 / " + parts[1].trim());
+ }
+ }
+}
diff --git a/CloudKernel/src/ui/CloudKernelGUI.java b/CloudKernel/src/ui/CloudKernelGUI.java
new file mode 100644
index 0000000..0ac3784
--- /dev/null
+++ b/CloudKernel/src/ui/CloudKernelGUI.java
@@ -0,0 +1,622 @@
+package ui;
+
+import config.ConfigLoader;
+import core.BootManager;
+import core.ClockSynchronizer;
+import entities.ResourceManager;
+import entities.VMPriority;
+import entities.VMState;
+import entities.VMStats;
+import entities.VirtualMachine;
+import shutdown.ShutdownManager;
+import utils.GUILogger;
+import utils.StatsCollector;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.MatteBorder;
+import java.awt.*;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Main Swing dashboard window for CloudKernel runtime monitoring.
+ */
+public class CloudKernelGUI extends JFrame {
+ private static final Color BG_DARK = new Color(10, 14, 26);
+ private static final Color BG_HEADER = new Color(12, 19, 32);
+ private static final Color TEXT_PRIMARY = new Color(200, 216, 240);
+ private static final Color ACCENT_CYAN = new Color(0, 212, 255);
+ private static final Color ACCENT_GREEN = new Color(0, 255, 136);
+
+ private static final Pattern VM_NAME_PATTERN = Pattern.compile("VM-(\\d+)");
+ private static final Pattern CYCLE_PATTERN = Pattern.compile("Cycle (\\d+)");
+
+ private final ConfigLoader config;
+ private final StatsCollector statsCollector;
+ private final GUILogger logger;
+ private final BootManager bootManager;
+ private final ClockSynchronizer clockSynchronizer;
+ private final ResourceManager resourceManager;
+
+ private final int numVMs;
+ private final int numCycles;
+
+ private final AtomicBoolean running = new AtomicBoolean(false);
+ private final AtomicBoolean paused = new AtomicBoolean(false);
+
+ private final JLabel clockLabel = new JLabel("00:00:00");
+ private final JLabel onlineLabel = new JLabel("\u25cf SYSTEM OFFLINE");
+ private final JPanel[] bootChips = new JPanel[4];
+ private final JLabel latchLabel = new JLabel("LATCH: 4");
+
+ private final Map vmCards = new LinkedHashMap<>();
+ private final Set barrierArrivals = ConcurrentHashMap.newKeySet();
+
+ private ResourceMonitorPanel resourceMonitorPanel;
+ private BarrierPanel barrierPanel;
+ private LogPanel logPanel;
+ private StatsBar statsBar;
+ private ControlPanel controlPanel;
+ private DashboardUpdater dashboardUpdater;
+
+ private Timer clockTimer;
+ private Timer onlinePulseTimer;
+ private Timer refreshTimer;
+ private Timer bootLatchTimer;
+
+ /**
+ * Creates and wires the complete dashboard UI and simulation observers.
+ */
+ public CloudKernelGUI() {
+ this.config = new ConfigLoader();
+ this.statsCollector = new StatsCollector();
+ this.logger = new GUILogger();
+ this.numVMs = config.getVMCount();
+ this.numCycles = config.getCycleCount();
+
+ this.bootManager = new BootManager(logger);
+ this.clockSynchronizer = new ClockSynchronizer(numVMs, logger, statsCollector);
+ this.resourceManager = new ResourceManager(
+ config.getCPUPermits(),
+ config.getMemoryPermits(),
+ config.getNetworkPermits(),
+ config.getTimeoutDuration(),
+ logger);
+
+ new ShutdownManager(statsCollector);
+
+ setupFrame();
+ setupLayout();
+ setupTimers();
+ registerLoggerListener();
+ }
+
+ /** Configures top-level frame properties. */
+ private void setupFrame() {
+ setTitle("CloudKernel — Cloud Hypervisor Monitor");
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setLayout(new BorderLayout());
+ setSize(1280, 800);
+ setResizable(false);
+ setLocationRelativeTo(null);
+ getContentPane().setBackground(BG_DARK);
+ }
+
+ /** Builds and attaches all visual dashboard sections. */
+ private void setupLayout() {
+ JPanel topContainer = new JPanel();
+ topContainer.setLayout(new BoxLayout(topContainer, BoxLayout.Y_AXIS));
+ topContainer.setBackground(BG_DARK);
+ topContainer.add(buildHeader());
+ topContainer.add(buildBootPanel());
+ add(topContainer, BorderLayout.NORTH);
+
+ resourceMonitorPanel = new ResourceMonitorPanel(config.getCPUPermits(), config.getMemoryPermits(),
+ config.getNetworkPermits());
+ logPanel = new LogPanel();
+ statsBar = new StatsBar();
+ barrierPanel = new BarrierPanel(numVMs);
+
+ JPanel vmDashboard = new JPanel(new GridLayout(1, numVMs, 8, 0));
+ vmDashboard.setBackground(BG_DARK);
+ vmDashboard.setBorder(new EmptyBorder(10, 10, 8, 10));
+ for (int i = 1; i <= numVMs; i++) {
+ VMCard card = new VMCard("VM-" + i);
+ vmCards.put(card.getVmName(), card);
+ vmDashboard.add(card);
+ }
+
+ dashboardUpdater = new DashboardUpdater(vmCards, resourceMonitorPanel, barrierPanel, statsBar);
+
+ JPanel centerColumn = new JPanel(new BorderLayout(0, 8));
+ centerColumn.setBackground(BG_DARK);
+ centerColumn.add(vmDashboard, BorderLayout.CENTER);
+ centerColumn.add(barrierPanel, BorderLayout.SOUTH);
+
+ controlPanel = new ControlPanel(
+ this::startSimulation,
+ this::togglePause,
+ this::resetDashboard,
+ speed -> GUILogger
+ .boot("Simulation speed set to " + String.format("%.1fx", speed) + " (visual only)."));
+
+ JPanel bottom = new JPanel(new BorderLayout());
+ bottom.setBackground(BG_DARK);
+ bottom.add(statsBar, BorderLayout.CENTER);
+ bottom.add(controlPanel, BorderLayout.SOUTH);
+
+ add(resourceMonitorPanel, BorderLayout.WEST);
+ add(centerColumn, BorderLayout.CENTER);
+ add(logPanel, BorderLayout.EAST);
+ add(bottom, BorderLayout.SOUTH);
+
+ resourceMonitorPanel.setPreferredSize(new Dimension(310, 0));
+ logPanel.setPreferredSize(new Dimension(370, 0));
+ }
+
+ /**
+ * Creates the top header bar.
+ *
+ * @return header panel
+ */
+ private JPanel buildHeader() {
+ JPanel header = new JPanel(new BorderLayout());
+ header.setBackground(BG_HEADER);
+ header.setPreferredSize(new Dimension(1280, 52));
+ header.setBorder(new MatteBorder(0, 0, 1, 0, new Color(30, 45, 69)));
+
+ JLabel title = new JLabel("\u2601 CLOUDKERNEL HYPERVISOR");
+ title.setFont(getSymbolFont(Font.BOLD, 18));
+ title.setForeground(ACCENT_CYAN);
+ title.setBorder(new EmptyBorder(0, 12, 0, 0));
+ header.add(title, BorderLayout.WEST);
+
+ JPanel right = new JPanel(new FlowLayout(FlowLayout.RIGHT, 16, 14));
+ right.setOpaque(false);
+
+ clockLabel.setFont(uiFont(Font.PLAIN, 14));
+ clockLabel.setForeground(TEXT_PRIMARY);
+
+ onlineLabel.setFont(getSymbolFont(Font.BOLD, 13));
+ onlineLabel.setForeground(new Color(90, 98, 112));
+
+ right.add(clockLabel);
+ right.add(onlineLabel);
+ header.add(right, BorderLayout.EAST);
+
+ return header;
+ }
+
+ /**
+ * Creates the boot visualization panel.
+ *
+ * @return boot panel
+ */
+ private JPanel buildBootPanel() {
+ JPanel panel = new JPanel(new BorderLayout(10, 8));
+ panel.setBackground(BG_DARK);
+ panel.setBorder(new EmptyBorder(10, 12, 10, 12));
+
+ JLabel title = new JLabel("BOOT MANAGER - CountDownLatch");
+ title.setForeground(ACCENT_CYAN);
+ title.setFont(uiFont(Font.BOLD, 13));
+ panel.add(title, BorderLayout.NORTH);
+
+ JPanel chipRow = new JPanel(new GridLayout(1, 4, 8, 0));
+ chipRow.setOpaque(false);
+
+ bootChips[0] = bootChip("Disk");
+ bootChips[1] = bootChip("RAM");
+ bootChips[2] = bootChip("Network Stack");
+ bootChips[3] = bootChip("CPU Scheduler");
+
+ for (JPanel chip : bootChips) {
+ chipRow.add(chip);
+ }
+
+ panel.add(chipRow, BorderLayout.CENTER);
+
+ latchLabel.setForeground(new Color(130, 150, 180));
+ latchLabel.setFont(uiFont(Font.BOLD, 12));
+ panel.add(latchLabel, BorderLayout.SOUTH);
+
+ return panel;
+ }
+
+ /**
+ * Creates one boot subsystem chip.
+ *
+ * @param name chip label
+ * @return chip panel
+ */
+ private JPanel bootChip(String name) {
+ JPanel chip = new JPanel(new BorderLayout());
+ chip.setBackground(new Color(30, 45, 69));
+ chip.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createLineBorder(new Color(42, 63, 98), 1),
+ new EmptyBorder(7, 8, 7, 8)));
+
+ JLabel label = new JLabel(name, SwingConstants.CENTER);
+ label.setForeground(TEXT_PRIMARY);
+ label.setFont(uiFont(Font.BOLD, 12));
+ chip.add(label, BorderLayout.CENTER);
+
+ return chip;
+ }
+
+ /** Initializes all Swing timers used by the dashboard. */
+ private void setupTimers() {
+ clockTimer = new Timer(1000,
+ e -> clockLabel.setText(LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))));
+ clockTimer.start();
+
+ onlinePulseTimer = new Timer(500, e -> {
+ if (running.get()) {
+ Color current = onlineLabel.getForeground();
+ onlineLabel.setForeground(current.equals(ACCENT_GREEN) ? new Color(80, 140, 92) : ACCENT_GREEN);
+ }
+ });
+
+ refreshTimer = new Timer(220, e -> refreshPanels());
+
+ bootLatchTimer = new Timer(150, e -> {
+ long count = bootManager.getBootLatch().getCount();
+ if (count == 0) {
+ latchLabel.setText("LATCH: 0 (RELEASED)");
+ bootLatchTimer.stop();
+ } else {
+ latchLabel.setText("LATCH: " + count);
+ }
+ });
+ }
+
+ /** Registers the logger listener that feeds the live log and visual updates. */
+ private void registerLoggerListener() {
+ GUILogger.addListener(entry -> SwingUtilities.invokeLater(() -> {
+ logPanel.appendEntry(entry);
+ processLogEvent(entry);
+ }));
+ }
+
+ /**
+ * Applies event-driven UI changes from one log entry.
+ *
+ * @param entry parsed logger entry
+ */
+ private void processLogEvent(GUILogger.LogEntry entry) {
+ String message = entry.message;
+ String category = entry.category.toUpperCase();
+ String vmName = entry.vmName;
+
+ if ("BOOT".equals(category) && "SYSTEM".equals(vmName)) {
+ if (message.contains("Disk subsystem initialized")) {
+ setBootChipReady(0);
+ }
+ if (message.contains("RAM subsystem initialized")) {
+ setBootChipReady(1);
+ }
+ if (message.contains("Network Stack initialized")) {
+ setBootChipReady(2);
+ }
+ if (message.contains("CPU Scheduler initialized")) {
+ setBootChipReady(3);
+ }
+ }
+
+ if (!vmCards.containsKey(vmName)) {
+ if (message.contains("Global Clock Tick #")) {
+ int cycle = extractLastNumber(message);
+ barrierArrivals.clear();
+ dashboardUpdater.updateBarrierCycle(cycle, numVMs);
+ }
+ return;
+ }
+
+ if (message.contains("Starting Cycle")) {
+ Matcher cycleMatcher = CYCLE_PATTERN.matcher(message);
+ if (cycleMatcher.find()) {
+ int cycle = Integer.parseInt(cycleMatcher.group(1));
+ int progress = (int) Math.round((cycle * 100.0) / Math.max(1, numCycles));
+ vmCards.get(vmName).setCycleProgress(progress);
+ dashboardUpdater.update(vmName, VMState.RUNNING);
+ }
+ return;
+ }
+
+ if (message.contains("Executing workload")) {
+ dashboardUpdater.update(vmName, VMState.RUNNING);
+ return;
+ }
+
+ if (message.contains("acquired")) {
+ dashboardUpdater.update(vmName, VMState.USING_RESOURCE);
+ return;
+ }
+
+ if (message.contains("released")) {
+ dashboardUpdater.update(vmName, VMState.REQUESTING_RESOURCE);
+ return;
+ }
+
+ if (message.contains("[TIMEOUT]")) {
+ dashboardUpdater.update(vmName, VMState.TIMEOUT);
+ return;
+ }
+
+ if ("BARRIER".equals(category)) {
+ dashboardUpdater.update(vmName, VMState.BARRIER_WAIT);
+ Integer vmId = extractVmId(vmName);
+ if (vmId != null) {
+ barrierArrivals.add(vmId);
+ dashboardUpdater.updateBarrierArrivals(vmId, barrierArrivals.size(), numVMs);
+ }
+ return;
+ }
+
+ if (message.contains("Shutting down gracefully")) {
+ dashboardUpdater.update(vmName, VMState.SHUTDOWN);
+ }
+ }
+
+ /**
+ * Marks one boot chip as initialized.
+ *
+ * @param index chip index
+ */
+ private void setBootChipReady(int index) {
+ if (index < 0 || index >= bootChips.length) {
+ return;
+ }
+ bootChips[index].setBackground(new Color(0, 255, 136));
+ for (Component child : bootChips[index].getComponents()) {
+ if (child instanceof JLabel) {
+ child.setForeground(new Color(5, 18, 16));
+ }
+ }
+ bootChips[index].repaint();
+ }
+
+ /** Refreshes all data-driven dashboard widgets. */
+ private void refreshPanels() {
+ dashboardUpdater.updateResourceSlots(
+ resourceManager.getCPUHolders(),
+ resourceManager.getMemoryHolders(),
+ resourceManager.getNetworkHolders());
+
+ dashboardUpdater.updateVMStats(statsCollector.getAllVMStats(), numCycles);
+ dashboardUpdater.updateSystemStats(statsCollector);
+ }
+
+ /** Starts one simulation run if not already running. */
+ private synchronized void startSimulation() {
+ if (running.getAndSet(true)) {
+ return;
+ }
+
+ controlPanel.setBootEnabled(false);
+ controlPanel.setPauseEnabled(true);
+ controlPanel.setResetEnabled(true);
+
+ onlineLabel.setText("\u25cf SYSTEM ONLINE");
+ onlineLabel.setForeground(ACCENT_GREEN);
+ onlinePulseTimer.start();
+ refreshTimer.start();
+ bootLatchTimer.start();
+
+ Thread simulation = new Thread(this::runSimulation, "CloudKernel-Simulation");
+ simulation.start();
+ }
+
+ /** Toggles pause/resume UI mode for observer controls. */
+ private void togglePause() {
+ paused.set(!paused.get());
+ if (paused.get()) {
+ controlPanel.setPauseText("\u25b6 RESUME");
+ GUILogger.boot("Pause requested from dashboard (observer mode). VM threads continue unchanged.");
+ } else {
+ controlPanel.setPauseText("|| PAUSE");
+ GUILogger.boot("Dashboard resumed.");
+ }
+ }
+
+ /** Resets dashboard visuals when simulation is not running. */
+ private synchronized void resetDashboard() {
+ if (running.get()) {
+ GUILogger.boot("Reset requested while running. Observer mode keeps worker threads unchanged.");
+ return;
+ }
+
+ for (int i = 0; i < bootChips.length; i++) {
+ bootChips[i].setBackground(new Color(30, 45, 69));
+ for (Component child : bootChips[i].getComponents()) {
+ if (child instanceof JLabel) {
+ child.setForeground(TEXT_PRIMARY);
+ }
+ }
+ }
+
+ latchLabel.setText("LATCH: 4");
+ barrierArrivals.clear();
+ logPanel.clear();
+ dashboardUpdater.reset();
+
+ for (VMCard card : vmCards.values()) {
+ card.setState(VMState.BOOTING);
+ card.setTaskCount(0);
+ card.setAvgWait(0);
+ card.setCycleProgress(0);
+ card.setResourceHold(false, false, false);
+ }
+
+ onlineLabel.setText("\u25cf SYSTEM OFFLINE");
+ onlineLabel.setForeground(new Color(90, 98, 112));
+ controlPanel.setPauseText("|| PAUSE");
+ controlPanel.setBootEnabled(true);
+ }
+
+ /** Executes the full boot and VM simulation lifecycle. */
+ private void runSimulation() {
+ try {
+ GUILogger.section("PHASE 1: SYSTEM BOOT [CountDownLatch]");
+ bootManager.initDisk();
+ bootManager.initRAM();
+ bootManager.initNetworkStack();
+ bootManager.initCPUScheduler();
+ bootManager.awaitBootCompletion();
+
+ GUILogger.section("PHASE 2: VM EXECUTION [CyclicBarrier + Semaphore]");
+ GUILogger.boot("Launching " + numVMs + " VMs for " + numCycles + " cycles each...");
+
+ Thread[] vmThreads = new Thread[numVMs];
+ for (int i = 0; i < numVMs; i++) {
+ int vmId = i + 1;
+ String vmName = "VM-" + vmId;
+ VMPriority priority = VMPriority.getRandomPriority();
+ VMStats vmStats = statsCollector.getOrCreateVMStats(vmName);
+
+ VMCard card = vmCards.get(vmName);
+ if (card != null) {
+ SwingUtilities.invokeLater(() -> card.setPriority(priority));
+ }
+
+ int workDuration = 600 + (vmId * 200);
+ VirtualMachine vm = new VirtualMachine(
+ vmName,
+ vmId,
+ numCycles,
+ clockSynchronizer,
+ resourceManager,
+ workDuration,
+ logger,
+ priority,
+ vmStats,
+ statsCollector);
+
+ vmThreads[i] = new Thread(vm, vmName);
+ vmThreads[i].start();
+ }
+
+ for (Thread vmThread : vmThreads) {
+ vmThread.join();
+ }
+
+ GUILogger.section("PHASE 3: SYSTEM SHUTDOWN");
+ GUILogger.boot("All VMs have completed execution. CloudKernel shutting down.");
+ statsCollector.printSummary();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ GUILogger.boot("Simulation interrupted.");
+ } finally {
+ SwingUtilities.invokeLater(() -> {
+ running.set(false);
+ onlinePulseTimer.stop();
+ refreshTimer.stop();
+ onlineLabel.setText("\u25cf SYSTEM OFFLINE");
+ onlineLabel.setForeground(new Color(90, 98, 112));
+ controlPanel.setPauseEnabled(false);
+ controlPanel.setBootEnabled(true);
+ });
+ }
+ }
+
+ /**
+ * Parses VM id from a VM name string.
+ *
+ * @param vmName VM name
+ * @return parsed id or null when missing
+ */
+ private Integer extractVmId(String vmName) {
+ Matcher matcher = VM_NAME_PATTERN.matcher(vmName);
+ if (matcher.find()) {
+ return Integer.parseInt(matcher.group(1));
+ }
+ return null;
+ }
+
+ /**
+ * Extracts the trailing number from a message string.
+ *
+ * @param message source message
+ * @return trailing number or zero
+ */
+ private int extractLastNumber(String message) {
+ StringBuilder number = new StringBuilder();
+ for (int i = message.length() - 1; i >= 0; i--) {
+ char c = message.charAt(i);
+ if (Character.isDigit(c)) {
+ number.insert(0, c);
+ } else if (number.length() > 0) {
+ break;
+ }
+ }
+ return number.length() == 0 ? 0 : Integer.parseInt(number.toString());
+ }
+
+ /**
+ * Creates a UI font with project defaults.
+ *
+ * @param style font style
+ * @param size font size
+ * @return configured font
+ */
+ public static Font uiFont(int style, int size) {
+ return new Font(fontFamily(), style, size);
+ }
+
+ /**
+ * Creates a font that is likely to render Unicode symbols consistently.
+ *
+ * @param style font style
+ * @param size font size
+ * @return resolved symbol font
+ */
+ public static Font getSymbolFont(int style, int size) {
+ String[] candidates = {"Segoe UI Symbol", "DejaVu Sans", "Arial Unicode MS", "Symbola", "Dialog"};
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ Set available = new HashSet<>(Arrays.asList(ge.getAvailableFontFamilyNames()));
+ for (String name : candidates) {
+ if (available.contains(name)) {
+ return new Font(name, style, size);
+ }
+ }
+ return new Font("Dialog", style, size);
+ }
+
+ /**
+ * Resolves the preferred monospace family available on the machine.
+ *
+ * @return resolved font family name
+ */
+ public static String fontFamily() {
+ String[] available = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
+ for (String name : available) {
+ if ("JetBrains Mono".equalsIgnoreCase(name)) {
+ return "JetBrains Mono";
+ }
+ }
+ return "Consolas";
+ }
+
+ /**
+ * Formats uptime as mm:ss.
+ *
+ * @param uptimeMs uptime in milliseconds
+ * @return formatted uptime
+ */
+ public static String formatUptime(long uptimeMs) {
+ long totalSeconds = uptimeMs / 1000;
+ long minutes = totalSeconds / 60;
+ long seconds = totalSeconds % 60;
+ return String.format("%02d:%02d", minutes, seconds);
+ }
+}
diff --git a/CloudKernel/src/ui/ControlPanel.java b/CloudKernel/src/ui/ControlPanel.java
new file mode 100644
index 0000000..c0a8f3f
--- /dev/null
+++ b/CloudKernel/src/ui/ControlPanel.java
@@ -0,0 +1,135 @@
+package ui;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.LineBorder;
+import java.awt.*;
+import java.util.function.Consumer;
+
+/**
+ * Bottom control bar for simulation actions and speed configuration.
+ */
+public class ControlPanel extends JPanel {
+ private static final Color BG_PANEL = new Color(12, 19, 32);
+ private static final Color ACCENT_CYAN = new Color(0, 212, 255);
+ private static final Color ACCENT_RED = new Color(255, 90, 90);
+
+ private final JButton bootButton;
+ private final JButton pauseButton;
+ private final JButton resetButton;
+ private final JSlider speedSlider;
+ private final JLabel speedLabel;
+
+ /**
+ * Creates the control panel.
+ *
+ * @param onBoot callback for boot button
+ * @param onPause callback for pause button
+ * @param onReset callback for reset button
+ * @param onSpeedChanged callback for slider changes
+ */
+ public ControlPanel(Runnable onBoot, Runnable onPause, Runnable onReset, Consumer onSpeedChanged) {
+ setLayout(new FlowLayout(FlowLayout.CENTER, 14, 10));
+ setBackground(BG_PANEL);
+ setBorder(new EmptyBorder(4, 8, 8, 8));
+
+ bootButton = createButton("\u25b6 BOOT SYSTEM", ACCENT_CYAN);
+ pauseButton = createButton("|| PAUSE", new Color(255, 191, 0));
+ resetButton = createButton("\u21ba RESET", ACCENT_RED);
+
+ bootButton.addActionListener(e -> onBoot.run());
+ pauseButton.addActionListener(e -> onPause.run());
+ resetButton.addActionListener(e -> onReset.run());
+
+ add(bootButton);
+ add(pauseButton);
+ add(resetButton);
+
+ JLabel sliderTitle = new JLabel("SIMULATION SPEED");
+ sliderTitle.setForeground(new Color(200, 216, 240));
+ sliderTitle.setFont(CloudKernelGUI.uiFont(Font.BOLD, 11));
+ add(sliderTitle);
+
+ speedLabel = new JLabel("1.0x");
+ speedLabel.setForeground(Color.WHITE);
+ speedLabel.setFont(CloudKernelGUI.uiFont(Font.BOLD, 12));
+
+ speedSlider = new JSlider(50, 300, 100);
+ speedSlider.setPreferredSize(new Dimension(170, 26));
+ speedSlider.setBackground(BG_PANEL);
+ speedSlider.setForeground(ACCENT_CYAN);
+ speedSlider.addChangeListener(e -> {
+ double value = speedSlider.getValue() / 100.0;
+ speedLabel.setText(String.format("%.1fx", value));
+ onSpeedChanged.accept(value);
+ });
+ add(speedSlider);
+ add(speedLabel);
+ }
+
+ /**
+ * Creates a styled dashboard button.
+ *
+ * @param text button text
+ * @param borderColor border color
+ * @return styled button
+ */
+ private JButton createButton(String text, Color borderColor) {
+ JButton button = new JButton(text);
+ button.setFont(CloudKernelGUI.getSymbolFont(Font.BOLD, 12));
+ button.setForeground(Color.WHITE);
+ button.setBackground(new Color(20, 29, 45));
+ button.setBorder(new LineBorder(borderColor, 2));
+ button.setFocusPainted(false);
+ button.setPreferredSize(new Dimension(160, 34));
+
+ button.addMouseListener(new java.awt.event.MouseAdapter() {
+ @Override
+ public void mouseEntered(java.awt.event.MouseEvent evt) {
+ button.setBackground(new Color(32, 42, 62));
+ }
+
+ @Override
+ public void mouseExited(java.awt.event.MouseEvent evt) {
+ button.setBackground(new Color(20, 29, 45));
+ }
+ });
+ return button;
+ }
+
+ /**
+ * Enables or disables the boot button.
+ *
+ * @param enabled true to enable
+ */
+ public void setBootEnabled(boolean enabled) {
+ bootButton.setEnabled(enabled);
+ }
+
+ /**
+ * Updates pause button text.
+ *
+ * @param text button text
+ */
+ public void setPauseText(String text) {
+ pauseButton.setText(text);
+ }
+
+ /**
+ * Enables or disables the pause button.
+ *
+ * @param enabled true to enable
+ */
+ public void setPauseEnabled(boolean enabled) {
+ pauseButton.setEnabled(enabled);
+ }
+
+ /**
+ * Enables or disables the reset button.
+ *
+ * @param enabled true to enable
+ */
+ public void setResetEnabled(boolean enabled) {
+ resetButton.setEnabled(enabled);
+ }
+}
diff --git a/CloudKernel/src/ui/DashboardUpdater.java b/CloudKernel/src/ui/DashboardUpdater.java
new file mode 100644
index 0000000..6d04ed9
--- /dev/null
+++ b/CloudKernel/src/ui/DashboardUpdater.java
@@ -0,0 +1,172 @@
+package ui;
+
+import entities.VMState;
+import entities.VMStats;
+import utils.StatsCollector;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Centralized UI refresh adapter for dashboard widgets.
+ */
+public class DashboardUpdater {
+ private final Map cards;
+ private final ResourceMonitorPanel resourcePanel;
+ private final BarrierPanel barrierPanel;
+ private final StatsBar statsBar;
+
+ /**
+ * Creates an updater bound to all dashboard sections.
+ *
+ * @param cards VM cards indexed by VM name
+ * @param resourcePanel resource panel
+ * @param barrierPanel barrier panel
+ * @param statsBar stats bar
+ */
+ public DashboardUpdater(Map cards,
+ ResourceMonitorPanel resourcePanel,
+ BarrierPanel barrierPanel,
+ StatsBar statsBar) {
+ this.cards = cards;
+ this.resourcePanel = resourcePanel;
+ this.barrierPanel = barrierPanel;
+ this.statsBar = statsBar;
+ }
+
+ /**
+ * Updates one VM state card.
+ *
+ * @param vmName VM name
+ * @param state VM state
+ */
+ public void update(String vmName, VMState state) {
+ SwingUtilities.invokeLater(() -> {
+ VMCard card = cards.get(vmName);
+ if (card != null) {
+ card.setState(state);
+ }
+ });
+ }
+
+ /**
+ * Refreshes displayed resource holders and VM resource indicators.
+ *
+ * @param cpuHolders CPU holder map
+ * @param memoryHolders memory holder map
+ * @param networkHolders network holder map
+ */
+ public void updateResourceSlots(Map cpuHolders,
+ Map memoryHolders,
+ Map networkHolders) {
+ SwingUtilities.invokeLater(() -> {
+ List cpu = sortedValues(cpuHolders);
+ List memory = sortedValues(memoryHolders);
+ List network = sortedValues(networkHolders);
+
+ resourcePanel.updateCPU(cpu);
+ resourcePanel.updateMemory(memory);
+ resourcePanel.updateNetwork(network);
+
+ for (VMCard card : cards.values()) {
+ String vm = card.getVmName();
+ card.setResourceHold(cpu.contains(vm), memory.contains(vm), network.contains(vm));
+ }
+ });
+ }
+
+ /**
+ * Updates barrier arrival state for one VM.
+ *
+ * @param vmId VM numeric id
+ * @param arrived arrived VM count
+ * @param total total VM count
+ */
+ public void updateBarrierArrivals(int vmId, int arrived, int total) {
+ SwingUtilities.invokeLater(() -> barrierPanel.markArrived(vmId, arrived, total));
+ }
+
+ /**
+ * Updates cycle value and executes barrier flash animation.
+ *
+ * @param cycle cycle number
+ * @param total total VM count
+ */
+ public void updateBarrierCycle(int cycle, int total) {
+ SwingUtilities.invokeLater(() -> {
+ barrierPanel.setCycle(cycle);
+ barrierPanel.flashAllAndReset(total);
+ });
+ }
+
+ /**
+ * Updates VM cards and aggregate operation counters.
+ *
+ * @param vmStats per-VM stats map
+ * @param totalCycles configured total cycles
+ */
+ public void updateVMStats(Map vmStats, int totalCycles) {
+ SwingUtilities.invokeLater(() -> {
+ int networkOps = 0;
+ int cpuOps = 0;
+ int totalTimeouts = 0;
+
+ for (Map.Entry entry : vmStats.entrySet()) {
+ VMStats stats = entry.getValue();
+ VMCard card = cards.get(entry.getKey());
+ if (card != null) {
+ card.setTaskCount(stats.getTasksCompleted());
+ card.setAvgWait(stats.getAverageWaitTime());
+ int progress = totalCycles == 0 ? 0
+ : Math.min(100, (stats.getTasksCompleted() * 100) / Math.max(1, totalCycles * 4));
+ card.setCycleProgress(progress);
+ }
+
+ networkOps += stats.getNetworkUses();
+ cpuOps += stats.getCPUUses();
+ totalTimeouts += stats.getTimeouts();
+ }
+
+ statsBar.setValue("Network Ops", String.valueOf(networkOps));
+ statsBar.setValue("CPU Ops", String.valueOf(cpuOps));
+ statsBar.setValue("Timeouts", String.valueOf(totalTimeouts));
+ });
+ }
+
+ /**
+ * Updates system-wide statistics cards.
+ *
+ * @param statsCollector collector instance
+ */
+ public void updateSystemStats(StatsCollector statsCollector) {
+ SwingUtilities.invokeLater(() -> {
+ statsBar.setValue("Total Cycles", String.valueOf(statsCollector.getTotalCycles()));
+ statsBar.setValue("Contentions", String.valueOf(statsCollector.getTotalContentions()));
+ statsBar.setValue("Uptime", CloudKernelGUI.formatUptime(statsCollector.getUptimeMs()));
+ });
+ }
+
+ /** Resets stats bar values to initial state. */
+ public void reset() {
+ SwingUtilities.invokeLater(statsBar::reset);
+ }
+
+ /**
+ * Converts a slot holder map into a key-sorted holder list.
+ *
+ * @param map holder map
+ * @return sorted holder values
+ */
+ private List sortedValues(Map map) {
+ List keys = new ArrayList<>(map.keySet());
+ Collections.sort(keys);
+ List values = new ArrayList<>();
+ for (String key : keys) {
+ values.add(map.get(key));
+ }
+ return values;
+ }
+}
diff --git a/CloudKernel/src/ui/LogPanel.java b/CloudKernel/src/ui/LogPanel.java
new file mode 100644
index 0000000..8412958
--- /dev/null
+++ b/CloudKernel/src/ui/LogPanel.java
@@ -0,0 +1,95 @@
+package ui;
+
+import utils.GUILogger;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyledDocument;
+import java.awt.*;
+
+/**
+ * Styled live log panel for categorized simulation events.
+ */
+public class LogPanel extends JPanel {
+ private static final Color BG_PANEL = new Color(16, 24, 38);
+ private static final Color BG_LOG = new Color(11, 18, 30);
+ private static final Color TEXT_PRIMARY = new Color(200, 216, 240);
+ private static final Color ACCENT_CYAN = new Color(0, 212, 255);
+
+ private final JTextPane textPane;
+
+ /** Creates the log panel UI. */
+ public LogPanel() {
+ setLayout(new BorderLayout(0, 8));
+ setBackground(BG_PANEL);
+ setBorder(new EmptyBorder(8, 10, 8, 10));
+
+ JLabel title = new JLabel("LIVE LOG PANEL");
+ title.setForeground(ACCENT_CYAN);
+ title.setFont(CloudKernelGUI.uiFont(Font.BOLD, 12));
+ add(title, BorderLayout.NORTH);
+
+ textPane = new JTextPane();
+ textPane.setEditable(false);
+ textPane.setBackground(BG_LOG);
+ textPane.setForeground(TEXT_PRIMARY);
+ textPane.setFont(CloudKernelGUI.uiFont(Font.PLAIN, 11));
+
+ JScrollPane scrollPane = new JScrollPane(textPane);
+ add(scrollPane, BorderLayout.CENTER);
+ }
+
+ /**
+ * Appends one entry and auto-scrolls to bottom.
+ *
+ * @param entry log entry
+ */
+ public void appendEntry(GUILogger.LogEntry entry) {
+ StyledDocument doc = textPane.getStyledDocument();
+ SimpleAttributeSet attrs = new SimpleAttributeSet();
+ StyleConstants.setForeground(attrs, colorForCategory(entry.category));
+ StyleConstants.setFontFamily(attrs, CloudKernelGUI.fontFamily());
+ StyleConstants.setFontSize(attrs, 11);
+
+ String line = String.format("[%s] [%s] %s -> %s%n", entry.timestamp, entry.category, entry.vmName,
+ entry.message);
+ try {
+ doc.insertString(doc.getLength(), line, attrs);
+ textPane.setCaretPosition(doc.getLength());
+ } catch (Exception ignored) {
+ }
+ }
+
+ /** Clears all log content. */
+ public void clear() {
+ textPane.setText("");
+ }
+
+ /**
+ * Maps category to display color.
+ *
+ * @param category log category
+ * @return display color
+ */
+ private Color colorForCategory(String category) {
+ String upper = category.toUpperCase();
+ if ("BOOT".equals(upper)) {
+ return new Color(0, 212, 255);
+ }
+ if ("NETWORK".equals(upper) || "CPU".equals(upper) || "MEMORY".equals(upper)) {
+ return new Color(0, 255, 136);
+ }
+ if ("WAITING".equals(upper)) {
+ return new Color(255, 191, 0);
+ }
+ if ("BARRIER".equals(upper)) {
+ return new Color(180, 80, 255);
+ }
+ if ("TIMEOUT".equals(upper) || "ERROR".equals(upper)) {
+ return new Color(255, 90, 90);
+ }
+ return TEXT_PRIMARY;
+ }
+}
diff --git a/CloudKernel/src/ui/ResourceMonitorPanel.java b/CloudKernel/src/ui/ResourceMonitorPanel.java
new file mode 100644
index 0000000..bc6aaf5
--- /dev/null
+++ b/CloudKernel/src/ui/ResourceMonitorPanel.java
@@ -0,0 +1,137 @@
+package ui;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.LineBorder;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Displays live semaphore slot occupancy for CPU, memory, and network
+ * resources.
+ */
+public class ResourceMonitorPanel extends JPanel {
+ private static final Color BG_PANEL = new Color(16, 24, 38);
+ private static final Color TEXT_PRIMARY = new Color(200, 216, 240);
+ private static final Color TEXT_MUTED = new Color(130, 150, 180);
+ private static final Color ACCENT_CYAN = new Color(0, 212, 255);
+ private static final Color ACCENT_GREEN = new Color(0, 255, 136);
+
+ private final List cpuSlots = new ArrayList<>();
+ private final List memorySlots = new ArrayList<>();
+ private final List networkSlots = new ArrayList<>();
+
+ /**
+ * Creates the resource monitor panel.
+ *
+ * @param cpuPermits CPU permit count
+ * @param memoryPermits memory permit count
+ * @param networkPermits network permit count
+ */
+ public ResourceMonitorPanel(int cpuPermits, int memoryPermits, int networkPermits) {
+ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ setBackground(BG_PANEL);
+ setBorder(new EmptyBorder(10, 10, 10, 10));
+
+ JLabel title = new JLabel("RESOURCE MONITOR");
+ title.setForeground(ACCENT_CYAN);
+ title.setFont(CloudKernelGUI.uiFont(Font.BOLD, 13));
+ title.setAlignmentX(Component.LEFT_ALIGNMENT);
+ add(title);
+ add(Box.createVerticalStrut(10));
+
+ add(createSection("CPU Cores - Semaphore (" + cpuPermits + " permits)", cpuPermits, cpuSlots));
+ add(Box.createVerticalStrut(8));
+ add(createSection("Memory Blocks - Semaphore (" + memoryPermits + " permits)", memoryPermits, memorySlots));
+ add(Box.createVerticalStrut(8));
+ add(createSection("Network Ports - Semaphore (" + networkPermits + " permits)", networkPermits, networkSlots));
+ }
+
+ /**
+ * Builds one resource section row.
+ *
+ * @param titleText section title
+ * @param count number of slots
+ * @param targets backing labels list
+ * @return section panel
+ */
+ private JPanel createSection(String titleText, int count, List targets) {
+ JPanel section = new JPanel();
+ section.setLayout(new BoxLayout(section, BoxLayout.Y_AXIS));
+ section.setOpaque(false);
+ section.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ JLabel title = new JLabel(titleText);
+ title.setForeground(TEXT_PRIMARY);
+ title.setFont(CloudKernelGUI.uiFont(Font.PLAIN, 11));
+ title.setAlignmentX(Component.LEFT_ALIGNMENT);
+ section.add(title);
+ section.add(Box.createVerticalStrut(5));
+
+ JPanel row = new JPanel(new GridLayout(1, count, 6, 0));
+ row.setOpaque(false);
+
+ for (int i = 0; i < count; i++) {
+ JLabel slot = new JLabel("FREE", SwingConstants.CENTER);
+ slot.setOpaque(true);
+ slot.setBackground(new Color(25, 35, 52));
+ slot.setForeground(TEXT_MUTED);
+ slot.setFont(CloudKernelGUI.uiFont(Font.BOLD, 10));
+ slot.setBorder(new LineBorder(new Color(42, 63, 98), 1));
+ targets.add(slot);
+ row.add(slot);
+ }
+
+ section.add(row);
+ return section;
+ }
+
+ /**
+ * Updates CPU slot labels.
+ *
+ * @param holders current holder list
+ */
+ public void updateCPU(List holders) {
+ updateSlots(cpuSlots, holders);
+ }
+
+ /**
+ * Updates memory slot labels.
+ *
+ * @param holders current holder list
+ */
+ public void updateMemory(List holders) {
+ updateSlots(memorySlots, holders);
+ }
+
+ /**
+ * Updates network slot labels.
+ *
+ * @param holders current holder list
+ */
+ public void updateNetwork(List holders) {
+ updateSlots(networkSlots, holders);
+ }
+
+ /**
+ * Applies holder labels to a slot collection.
+ *
+ * @param slots visual slots
+ * @param holders holder names
+ */
+ private void updateSlots(List slots, List holders) {
+ for (int i = 0; i < slots.size(); i++) {
+ JLabel slot = slots.get(i);
+ if (i < holders.size()) {
+ slot.setText(holders.get(i));
+ slot.setForeground(ACCENT_GREEN);
+ slot.setBackground(new Color(18, 62, 46));
+ } else {
+ slot.setText("FREE");
+ slot.setForeground(TEXT_MUTED);
+ slot.setBackground(new Color(25, 35, 52));
+ }
+ }
+ }
+}
diff --git a/CloudKernel/src/ui/StatsBar.java b/CloudKernel/src/ui/StatsBar.java
new file mode 100644
index 0000000..b14dc76
--- /dev/null
+++ b/CloudKernel/src/ui/StatsBar.java
@@ -0,0 +1,81 @@
+package ui;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.LineBorder;
+import java.awt.*;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Bottom statistics row that displays live aggregate metrics.
+ */
+public class StatsBar extends JPanel {
+ private static final Color BG_BAR = new Color(12, 19, 32);
+ private static final Color BG_CARD = new Color(16, 24, 38);
+ private static final Color TEXT_MUTED = new Color(130, 150, 180);
+
+ private final Map valueLabels = new LinkedHashMap<>();
+
+ /** Creates the stats bar with all predefined stat cards. */
+ public StatsBar() {
+ setLayout(new GridLayout(1, 6, 8, 0));
+ setBackground(BG_BAR);
+ setBorder(new EmptyBorder(6, 8, 6, 8));
+ setPreferredSize(new Dimension(0, 78));
+
+ addStat("Total Cycles");
+ addStat("Network Ops");
+ addStat("CPU Ops");
+ addStat("Timeouts");
+ addStat("Contentions");
+ addStat("Uptime");
+ }
+
+ /**
+ * Adds one metric card.
+ *
+ * @param name metric name
+ */
+ private void addStat(String name) {
+ JPanel card = new JPanel(new GridLayout(2, 1));
+ card.setBackground(BG_CARD);
+ card.setBorder(new LineBorder(new Color(30, 45, 69), 1));
+
+ JLabel top = new JLabel(name, SwingConstants.CENTER);
+ top.setFont(CloudKernelGUI.uiFont(Font.PLAIN, 10));
+ top.setForeground(TEXT_MUTED);
+
+ JLabel value = new JLabel("0", SwingConstants.CENTER);
+ value.setFont(CloudKernelGUI.uiFont(Font.BOLD, 18));
+ value.setForeground(Color.WHITE);
+
+ valueLabels.put(name, value);
+ card.add(top);
+ card.add(value);
+ add(card);
+ }
+
+ /**
+ * Sets one metric value.
+ *
+ * @param name metric name
+ * @param value formatted metric value
+ */
+ public void setValue(String name, String value) {
+ JLabel label = valueLabels.get(name);
+ if (label != null) {
+ label.setText(value);
+ }
+ }
+
+ /** Resets all metric cards to startup values. */
+ public void reset() {
+ setValue("Total Cycles", "0");
+ setValue("Network Ops", "0");
+ setValue("CPU Ops", "0");
+ setValue("Timeouts", "0");
+ setValue("Contentions", "0");
+ setValue("Uptime", "00:00");
+ }
+}
diff --git a/CloudKernel/src/ui/VMCard.java b/CloudKernel/src/ui/VMCard.java
new file mode 100644
index 0000000..c73de2c
--- /dev/null
+++ b/CloudKernel/src/ui/VMCard.java
@@ -0,0 +1,290 @@
+package ui;
+
+import entities.VMPriority;
+import entities.VMState;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.LineBorder;
+import java.awt.*;
+
+/**
+ * Visual card representation for a single VM.
+ */
+public class VMCard extends JPanel {
+ private static final Color BG_CARD = new Color(16, 24, 38);
+ private static final Color TEXT_PRIMARY = new Color(200, 216, 240);
+ private static final Color TEXT_MUTED = new Color(130, 150, 180);
+ private static final Color ACCENT_CYAN = new Color(0, 212, 255);
+ private static final Color ACCENT_GREEN = new Color(0, 255, 136);
+ private static final Color ACCENT_AMBER = new Color(255, 191, 0);
+ private static final Color ACCENT_PURPLE = new Color(180, 80, 255);
+ private static final Color ACCENT_RED = new Color(255, 90, 90);
+
+ private final String vmName;
+ private final JLabel nameLabel;
+ private final JLabel priorityLabel;
+ private final JLabel stateLabel;
+ private final JProgressBar progressBar;
+ private final JLabel cpuDot;
+ private final JLabel memoryDot;
+ private final JLabel networkDot;
+ private final JLabel tasksLabel;
+ private final JLabel waitLabel;
+
+ private VMState state = VMState.BOOTING;
+
+ /**
+ * Creates a VM dashboard card.
+ *
+ * @param vmName VM name label
+ */
+ public VMCard(String vmName) {
+ this.vmName = vmName;
+ setLayout(new BorderLayout(0, 8));
+ setBackground(BG_CARD);
+ setBorder(new RoundedBorder(12, new Color(30, 45, 69)));
+ setOpaque(true);
+
+ JPanel top = new JPanel(new BorderLayout());
+ top.setOpaque(false);
+ top.setBorder(new EmptyBorder(8, 10, 0, 10));
+
+ nameLabel = new JLabel(vmName);
+ nameLabel.setFont(CloudKernelGUI.uiFont(Font.BOLD, 14));
+ nameLabel.setForeground(Color.WHITE);
+ top.add(nameLabel, BorderLayout.WEST);
+
+ priorityLabel = new JLabel("LOW");
+ priorityLabel.setFont(CloudKernelGUI.uiFont(Font.BOLD, 11));
+ priorityLabel.setOpaque(true);
+ priorityLabel.setBackground(new Color(0, 90, 120));
+ priorityLabel.setForeground(Color.WHITE);
+ priorityLabel.setBorder(new EmptyBorder(3, 8, 3, 8));
+ top.add(priorityLabel, BorderLayout.EAST);
+
+ add(top, BorderLayout.NORTH);
+
+ JPanel center = new JPanel();
+ center.setOpaque(false);
+ center.setLayout(new BoxLayout(center, BoxLayout.Y_AXIS));
+ center.setBorder(new EmptyBorder(0, 10, 6, 10));
+
+ stateLabel = new JLabel(state.getLabel());
+ stateLabel.setForeground(ACCENT_AMBER);
+ stateLabel.setFont(CloudKernelGUI.uiFont(Font.BOLD, 11));
+ stateLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ center.add(stateLabel);
+ center.add(Box.createVerticalStrut(6));
+
+ progressBar = new JProgressBar(0, 100);
+ progressBar.setValue(0);
+ progressBar.setStringPainted(false);
+ progressBar.setBackground(new Color(28, 38, 56));
+ progressBar.setForeground(ACCENT_CYAN);
+ progressBar.setBorder(new LineBorder(new Color(42, 63, 98), 1));
+ progressBar.setPreferredSize(new Dimension(190, 10));
+ progressBar.setMaximumSize(new Dimension(Integer.MAX_VALUE, 10));
+ progressBar.setAlignmentX(Component.LEFT_ALIGNMENT);
+ center.add(progressBar);
+ center.add(Box.createVerticalStrut(8));
+
+ JPanel resources = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 0));
+ resources.setOpaque(false);
+ resources.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ cpuDot = createDot("CPU");
+ memoryDot = createDot("MEM");
+ networkDot = createDot("NET");
+
+ resources.add(cpuDot);
+ resources.add(memoryDot);
+ resources.add(networkDot);
+ center.add(resources);
+ center.add(Box.createVerticalStrut(8));
+
+ tasksLabel = new JLabel("Tasks: 0");
+ tasksLabel.setFont(CloudKernelGUI.uiFont(Font.PLAIN, 11));
+ tasksLabel.setForeground(TEXT_MUTED);
+ tasksLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ center.add(tasksLabel);
+
+ waitLabel = new JLabel("Avg Wait: 0ms");
+ waitLabel.setFont(CloudKernelGUI.uiFont(Font.PLAIN, 11));
+ waitLabel.setForeground(TEXT_MUTED);
+ waitLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ center.add(waitLabel);
+
+ add(center, BorderLayout.CENTER);
+ }
+
+ /**
+ * Creates a resource indicator label.
+ *
+ * @param text dot text
+ * @return configured label
+ */
+ private JLabel createDot(String text) {
+ JLabel dot = new JLabel("\u25cf " + text);
+ dot.setForeground(new Color(75, 86, 105));
+ dot.setFont(CloudKernelGUI.getSymbolFont(Font.BOLD, 10));
+ return dot;
+ }
+
+ /** @return VM name bound to the card */
+ public String getVmName() {
+ return vmName;
+ }
+
+ /**
+ * Updates displayed priority badge.
+ *
+ * @param priority VM priority
+ */
+ public void setPriority(VMPriority priority) {
+ String label = priority.getLabel();
+ priorityLabel.setText(label);
+ if (priority == VMPriority.HIGH) {
+ priorityLabel.setBackground(new Color(130, 30, 30));
+ } else if (priority == VMPriority.MEDIUM) {
+ priorityLabel.setBackground(new Color(135, 92, 0));
+ } else {
+ priorityLabel.setBackground(new Color(0, 90, 120));
+ }
+ }
+
+ /**
+ * Updates state text and border color.
+ *
+ * @param newState next VM state
+ */
+ public void setState(VMState newState) {
+ this.state = newState;
+ stateLabel.setText(newState.getLabel());
+
+ Color stateColor = colorForState(newState);
+ stateLabel.setForeground(stateColor);
+ setBorder(new RoundedBorder(12, borderForState(newState)));
+ }
+
+ /**
+ * Sets cycle progress as a percentage.
+ *
+ * @param progressPercent progress value in [0, 100]
+ */
+ public void setCycleProgress(int progressPercent) {
+ progressBar.setValue(Math.max(0, Math.min(progressPercent, 100)));
+ }
+
+ /**
+ * Sets completed task count text.
+ *
+ * @param tasks task count
+ */
+ public void setTaskCount(int tasks) {
+ tasksLabel.setText("Tasks: " + tasks);
+ }
+
+ /**
+ * Sets average wait-time text.
+ *
+ * @param waitMs average wait in milliseconds
+ */
+ public void setAvgWait(long waitMs) {
+ waitLabel.setText("Avg Wait: " + waitMs + "ms");
+ }
+
+ /**
+ * Updates resource hold indicators.
+ *
+ * @param cpu whether CPU is currently held
+ * @param memory whether memory is currently held
+ * @param network whether network is currently held
+ */
+ public void setResourceHold(boolean cpu, boolean memory, boolean network) {
+ cpuDot.setForeground(cpu ? ACCENT_GREEN : new Color(75, 86, 105));
+ memoryDot.setForeground(memory ? ACCENT_GREEN : new Color(75, 86, 105));
+ networkDot.setForeground(network ? ACCENT_GREEN : new Color(75, 86, 105));
+ }
+
+ /**
+ * Returns text color for a VM state.
+ *
+ * @param vmState VM state
+ * @return mapped state color
+ */
+ private Color colorForState(VMState vmState) {
+ if (vmState == VMState.RUNNING) {
+ return ACCENT_CYAN;
+ }
+ if (vmState == VMState.USING_RESOURCE) {
+ return ACCENT_GREEN;
+ }
+ if (vmState == VMState.BARRIER_WAIT) {
+ return ACCENT_PURPLE;
+ }
+ if (vmState == VMState.TIMEOUT) {
+ return ACCENT_RED;
+ }
+ if (vmState == VMState.REQUESTING_RESOURCE) {
+ return ACCENT_AMBER;
+ }
+ return TEXT_PRIMARY;
+ }
+
+ /**
+ * Returns border color for a VM state.
+ *
+ * @param vmState VM state
+ * @return mapped border color
+ */
+ private Color borderForState(VMState vmState) {
+ if (vmState == VMState.RUNNING) {
+ return ACCENT_CYAN;
+ }
+ if (vmState == VMState.USING_RESOURCE) {
+ return ACCENT_GREEN;
+ }
+ if (vmState == VMState.BARRIER_WAIT) {
+ return ACCENT_PURPLE;
+ }
+ if (vmState == VMState.TIMEOUT) {
+ return ACCENT_RED;
+ }
+ if (vmState == VMState.REQUESTING_RESOURCE) {
+ return ACCENT_AMBER;
+ }
+ return new Color(30, 45, 69);
+ }
+
+ /** Rounded border painter for card emphasis. */
+ private static class RoundedBorder extends EmptyBorder {
+ private final int radius;
+ private final Color color;
+
+ /**
+ * Creates a rounded border.
+ *
+ * @param radius corner radius
+ * @param color border color
+ */
+ RoundedBorder(int radius, Color color) {
+ super(2, 2, 2, 2);
+ this.radius = radius;
+ this.color = color;
+ }
+
+ /**
+ * Paints the rounded card border.
+ */
+ @Override
+ public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
+ Graphics2D g2 = (Graphics2D) g.create();
+ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ g2.setColor(color);
+ g2.setStroke(new BasicStroke(1.5f));
+ g2.drawRoundRect(x, y, width - 1, height - 1, radius, radius);
+ g2.dispose();
+ }
+ }
+}
diff --git a/CloudKernel/src/utils/GUILogger.java b/CloudKernel/src/utils/GUILogger.java
new file mode 100644
index 0000000..357ba68
--- /dev/null
+++ b/CloudKernel/src/utils/GUILogger.java
@@ -0,0 +1,239 @@
+package utils;
+
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Logger that writes to terminal output and GUI listeners simultaneously.
+ */
+public class GUILogger {
+
+ private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss.S");
+
+ // ANSI Color codes
+ public static final String RESET = "\u001B[0m";
+ public static final String BOLD = "\u001B[1m";
+ public static final String RED = "\u001B[31m";
+ public static final String GREEN = "\u001B[32m";
+ public static final String YELLOW = "\u001B[33m";
+ public static final String BLUE = "\u001B[34m";
+ public static final String PURPLE = "\u001B[35m";
+ public static final String CYAN = "\u001B[36m";
+ public static final String WHITE = "\u001B[37m";
+
+ // Log level constants
+ public static final int VERBOSE = 0;
+ public static final int NORMAL = 1;
+ public static final int QUIET = 2;
+
+ private static int logLevel = NORMAL;
+ private static final List listeners = new CopyOnWriteArrayList<>();
+
+ /** Creates a logger instance. */
+ public GUILogger() {
+ }
+
+ /**
+ * Sets logger verbosity.
+ *
+ * @param level one of VERBOSE, NORMAL, QUIET
+ */
+ public static void setLogLevel(int level) {
+ logLevel = level;
+ }
+
+ /**
+ * Registers a GUI log listener.
+ *
+ * @param listener listener to register
+ */
+ public static void addListener(LogListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * Removes a GUI log listener.
+ *
+ * @param listener listener to remove
+ */
+ public static void removeListener(LogListener listener) {
+ listeners.remove(listener);
+ }
+
+ /**
+ * Logs a categorized VM message.
+ *
+ * @param vmName VM name or source actor
+ * @param message event message
+ * @param category message category
+ */
+ public static void log(String vmName, String message, String category) {
+ if (logLevel == QUIET)
+ return;
+
+ String timestamp = LocalTime.now().format(TIME_FORMAT);
+ String color = getCategoryColor(category);
+ String logLine = String.format(
+ "%s[%s] [%-8s] %s -> %s%s",
+ color, timestamp, category, vmName, message, RESET);
+
+ // Print to console
+ System.out.println(logLine);
+
+ // Notify GUI listeners
+ notifyListeners(new LogEntry(timestamp, category, vmName, message, color));
+ }
+
+ /**
+ * Logs a system boot event.
+ *
+ * @param message event message
+ */
+ public static void boot(String message) {
+ if (logLevel == QUIET)
+ return;
+
+ String timestamp = LocalTime.now().format(TIME_FORMAT);
+ String logLine = String.format(
+ "%s[%s] [%-8s] %s%s",
+ CYAN, timestamp, "BOOT", message, RESET);
+
+ System.out.println(logLine);
+ notifyListeners(new LogEntry(timestamp, "BOOT", "SYSTEM", message, CYAN));
+ }
+
+ /**
+ * Logs a cycle separator block.
+ *
+ * @param cycleNum cycle number
+ */
+ public static void cycleSeparator(int cycleNum) {
+ if (logLevel == QUIET)
+ return;
+
+ System.out.println(BOLD + "═══════════════════════════════════════════════════════════" + RESET);
+ System.out.println(BOLD + CYAN + " CYCLE #" + cycleNum + " BEGINS" + RESET);
+ System.out.println(BOLD + "═══════════════════════════════════════════════════════════" + RESET);
+ }
+
+ /**
+ * Logs cycle completion.
+ *
+ * @param cycleNum cycle number
+ */
+ public static void cycleComplete(int cycleNum) {
+ if (logLevel == QUIET)
+ return;
+
+ System.out.println(BOLD + CYAN + " CYCLE #" + cycleNum + " COMPLETE" + RESET);
+ System.out.println(BOLD + "═══════════════════════════════════════════════════════════" + RESET);
+ }
+
+ /**
+ * Returns ANSI color based on message category.
+ *
+ * @param category event category
+ * @return ANSI color code
+ */
+ private static String getCategoryColor(String category) {
+ switch (category.toUpperCase()) {
+ case "BOOT":
+ return CYAN;
+ case "CPU":
+ case "NETWORK":
+ case "MEMORY":
+ return GREEN;
+ case "WAITING":
+ return YELLOW;
+ case "BARRIER":
+ return PURPLE;
+ case "TIMEOUT":
+ case "ERROR":
+ return RED;
+ default:
+ return WHITE;
+ }
+ }
+
+ /**
+ * Emits a log entry to all listeners.
+ *
+ * @param entry formatted log entry
+ */
+ private static void notifyListeners(LogEntry entry) {
+ for (LogListener listener : listeners) {
+ listener.onLogEntry(entry);
+ }
+ }
+
+ /**
+ * Immutable GUI log entry payload.
+ */
+ public static class LogEntry {
+ public final String timestamp;
+ public final String category;
+ public final String vmName;
+ public final String message;
+ public final String color;
+
+ /**
+ * Creates an immutable log entry payload.
+ *
+ * @param timestamp event timestamp
+ * @param category category tag
+ * @param vmName source VM or component
+ * @param message message body
+ * @param color ANSI color used for console output
+ */
+ public LogEntry(String timestamp, String category, String vmName, String message, String color) {
+ this.timestamp = timestamp;
+ this.category = category;
+ this.vmName = vmName;
+ this.message = message;
+ this.color = color;
+ }
+
+ /**
+ * @return formatted display text
+ */
+ @Override
+ public String toString() {
+ return String.format("[%s] [%s] %s -> %s", timestamp, category, vmName, message);
+ }
+ }
+
+ /**
+ * Listener contract for GUI log streams.
+ */
+ public interface LogListener {
+ /**
+ * Receives one emitted log entry.
+ *
+ * @param entry log entry
+ */
+ void onLogEntry(LogEntry entry);
+ }
+
+ /** Writes a visual separator to terminal output. */
+ public static void separator() {
+ if (logLevel == QUIET)
+ return;
+ System.out.println(BOLD + "─".repeat(65) + RESET);
+ }
+
+ /**
+ * Writes a section heading to terminal output.
+ *
+ * @param title heading text
+ */
+ public static void section(String title) {
+ if (logLevel == QUIET)
+ return;
+ System.out.println();
+ separator();
+ System.out.println(BOLD + " " + title + RESET);
+ separator();
+ }
+}
diff --git a/CloudKernel/src/utils/Logger.java b/CloudKernel/src/utils/Logger.java
deleted file mode 100644
index 84495e3..0000000
--- a/CloudKernel/src/utils/Logger.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package utils;
-
-import java.time.LocalTime;
-import java.time.format.DateTimeFormatter;
-
-// Small utility for formatted console logging.
-public class Logger {
-
- private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss");
-
- public static final String RESET = "\u001B[0m";
- public static final String GREEN = "\u001B[32m";
- public static final String CYAN = "\u001B[36m";
- public static final String YELLOW = "\u001B[33m";
- public static final String RED = "\u001B[31m";
- public static final String BOLD = "\u001B[1m";
-
- private Logger() {
- // Prevent instantiation.
- }
-
- public static void log(String tag, String message, String color) {
- String timestamp = LocalTime.now().format(TIME_FORMAT);
- System.out.printf("%s[%s] %-12s%s %s%n",
- color, timestamp, "[" + tag + "]", RESET, message);
- }
-
- public static void separator() {
- System.out.println(BOLD + "-".repeat(65) + RESET);
- }
-
- public static void section(String title) {
- System.out.println();
- separator();
- System.out.println(BOLD + " " + title + RESET);
- separator();
- }
-}
\ No newline at end of file
diff --git a/CloudKernel/src/utils/StatsCollector.java b/CloudKernel/src/utils/StatsCollector.java
new file mode 100644
index 0000000..f8c7a0f
--- /dev/null
+++ b/CloudKernel/src/utils/StatsCollector.java
@@ -0,0 +1,139 @@
+package utils;
+
+import entities.VMStats;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Collects system-wide and per-VM simulation statistics.
+ */
+public class StatsCollector {
+ private final Map vmStats = new LinkedHashMap<>();
+ private final AtomicInteger totalCycles = new AtomicInteger(0);
+ private final AtomicInteger totalContentions = new AtomicInteger(0);
+ private final AtomicInteger peakConcurrency = new AtomicInteger(0);
+ private final AtomicLong startTime = new AtomicLong(0);
+ private final AtomicInteger totalTimeouts = new AtomicInteger(0);
+
+ /** Creates a new collector and captures simulation start time. */
+ public StatsCollector() {
+ startTime.set(System.currentTimeMillis());
+ }
+
+ /**
+ * Returns existing stats for a VM or creates a new entry.
+ *
+ * @param vmName VM name
+ * @return mutable VM stats object
+ */
+ public VMStats getOrCreateVMStats(String vmName) {
+ return vmStats.computeIfAbsent(vmName, k -> new VMStats(vmName));
+ }
+
+ /** Records one completed cycle event. */
+ public void recordCycleCompletion() {
+ totalCycles.incrementAndGet();
+ }
+
+ /** Records one resource contention event. */
+ public void recordContention() {
+ totalContentions.incrementAndGet();
+ }
+
+ /**
+ * Updates peak concurrency if a higher value is observed.
+ *
+ * @param concurrency current observed concurrency
+ */
+ public void recordPeakConcurrency(int concurrency) {
+ int current = peakConcurrency.get();
+ while (concurrency > current) {
+ peakConcurrency.compareAndSet(current, concurrency);
+ current = peakConcurrency.get();
+ }
+ }
+
+ /** Records one timeout event. */
+ public void recordTimeout() {
+ totalTimeouts.incrementAndGet();
+ }
+
+ /** Prints a human-readable summary to console output. */
+ public void printSummary() {
+ long elapsedMs = System.currentTimeMillis() - startTime.get();
+ long hours = elapsedMs / 3600000;
+ long minutes = (elapsedMs % 3600000) / 60000;
+ long seconds = (elapsedMs % 60000) / 1000;
+
+ System.out.println();
+ System.out.println(
+ GUILogger.BOLD + "═══════════════════════════════════════════════════════════" + GUILogger.RESET);
+ System.out.println(GUILogger.BOLD + GUILogger.CYAN + " SIMULATION SUMMARY" + GUILogger.RESET);
+ System.out.println(
+ GUILogger.BOLD + "═══════════════════════════════════════════════════════════" + GUILogger.RESET);
+
+ System.out.println();
+ System.out.println(GUILogger.BOLD + "System Statistics:" + GUILogger.RESET);
+ System.out.println(String.format(" Total Cycles Completed: %d", totalCycles.get()));
+ System.out.println(String.format(" Total Contentions: %d", totalContentions.get()));
+ System.out.println(String.format(" Peak Concurrency: %d VMs", peakConcurrency.get()));
+ System.out.println(String.format(" Total Timeouts: %d", totalTimeouts.get()));
+ System.out.println(String.format(" Total Uptime: %02d:%02d:%02d", hours, minutes, seconds));
+
+ System.out.println();
+ System.out.println(GUILogger.BOLD + "Per-VM Statistics:" + GUILogger.RESET);
+ System.out.println(
+ GUILogger.BOLD + String.format(" %-8s | Tasks | Network | CPU | Memory | Timeouts | Avg Wait", "VM")
+ + GUILogger.RESET);
+ System.out.println(GUILogger.BOLD + " " + "─".repeat(70) + GUILogger.RESET);
+
+ for (VMStats stats : vmStats.values()) {
+ System.out.println(String.format(
+ " %-8s | %5d | %7d | %3d | %6d | %8d | %6dms",
+ stats.toString().split("\\|")[0],
+ stats.getTasksCompleted(),
+ stats.getNetworkUses(),
+ stats.getCPUUses(),
+ stats.getMemoryUses(),
+ stats.getTimeouts(),
+ stats.getAverageWaitTime()));
+ }
+
+ System.out.println();
+ System.out.println(
+ GUILogger.BOLD + "═══════════════════════════════════════════════════════════" + GUILogger.RESET);
+ }
+
+ /** @return total completed cycles */
+ public int getTotalCycles() {
+ return totalCycles.get();
+ }
+
+ /** @return total contention count */
+ public int getTotalContentions() {
+ return totalContentions.get();
+ }
+
+ /** @return highest concurrent VM count observed */
+ public int getPeakConcurrency() {
+ return peakConcurrency.get();
+ }
+
+ /** @return total timeout count */
+ public int getTotalTimeouts() {
+ return totalTimeouts.get();
+ }
+
+ /** @return uptime in milliseconds */
+ public long getUptimeMs() {
+ return System.currentTimeMillis() - startTime.get();
+ }
+
+ /** @return snapshot copy of per-VM stats map */
+ public Map getAllVMStats() {
+ return new LinkedHashMap<>(vmStats);
+ }
+}