From ade419bc12c01bea35560bde021711efdb799b0c Mon Sep 17 00:00:00 2001 From: MarlonBando Date: Fri, 18 Apr 2025 17:00:26 +0200 Subject: [PATCH 1/9] Implement core simulation functionality and add CSV output handling; introduce Simulator class and related components. --- docs/simulator-example.md | 28 +++++ src/__init__.py | 0 src/common/architecture.py | 9 -- src/common/budget.py | 12 --- src/common/component.py | 27 +++++ src/common/core.py | 19 ++++ src/common/csvoutput.py | 53 +++++++++ src/common/csvreader.py | 33 ++++-- src/common/scheduler.py | 2 + src/common/task.py | 29 +++-- src/simulator.py | 213 +++++++++++++++++++++++++++++++++++-- tests/test_simulator.py | 10 ++ 12 files changed, 391 insertions(+), 44 deletions(-) create mode 100644 docs/simulator-example.md create mode 100644 src/__init__.py delete mode 100644 src/common/architecture.py delete mode 100644 src/common/budget.py create mode 100644 src/common/component.py create mode 100644 src/common/core.py create mode 100644 src/common/csvoutput.py create mode 100644 tests/test_simulator.py diff --git a/docs/simulator-example.md b/docs/simulator-example.md new file mode 100644 index 0000000..bb950f6 --- /dev/null +++ b/docs/simulator-example.md @@ -0,0 +1,28 @@ +# Example Walkthrough + +## Initial Setup + +**Core_1 (EDF)** contains two components: + +1. **Image_Processor** + - Budget: 2/6 + - Running: Task_5 (WCET=3) + +2. **Camera_Sensor** + - Budget: 5/9 + - Running: Task_1 (WCET=7) + +## Timeline Events + +| Time | Event | Image_Processor | Camera_Sensor | Core Action | +|------|-------|-----------------|---------------|-------------| +| 0 | Image_Processor starts | budget: 2 → 1 | - | Runs Task_5 (1 unit) | +| 1 | Image_Processor continues | budget: 1 → 0 | - | Runs Task_5 (1 unit) | +| 2 | Image_Processor budget exhausted | Blocked | budget: 5 → 4 | Switches to Camera_Sensor | +| 3-6 | Camera_Sensor execution | - | budget: 4 → 0 | Task_1 paused (4/7 complete) | +| 6 | Image_Processor period reset | New budget: 2 | Blocked | Image_Processor resumes | + +## Key Events + +- **Time 2**: Image_Processor depletes its 2-unit budget and becomes blocked. Camera_Sensor takes control. +- **Time 6**: Image_Processor's period resets (6/6), receiving fresh budget and preempting current execution. \ No newline at end of file diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/common/architecture.py b/src/common/architecture.py deleted file mode 100644 index 28c3a4a..0000000 --- a/src/common/architecture.py +++ /dev/null @@ -1,9 +0,0 @@ -from dataclasses import dataclass - -from common.scheduler import Scheduler - -@dataclass -class Architecture: - core_id:int - speed_factor:float - scheduler: Scheduler diff --git a/src/common/budget.py b/src/common/budget.py deleted file mode 100644 index 0e6a6fb..0000000 --- a/src/common/budget.py +++ /dev/null @@ -1,12 +0,0 @@ -from dataclasses import dataclass - -from common.scheduler import Scheduler - -@dataclass -class Budget: - component_id: str - scheduler:Scheduler - budget:int - period:int - core_id:int - priority:int|None diff --git a/src/common/component.py b/src/common/component.py new file mode 100644 index 0000000..74902d1 --- /dev/null +++ b/src/common/component.py @@ -0,0 +1,27 @@ +from common.scheduler import Scheduler + +class Component: + """ + Stores the budget and timing settings for a component in the system. The budget + controls how long a component can run, similar to a time allowance. + + Attributes: + component_id (str): Name/ID of the component (example: "Camera1") + scheduler (Scheduler): How tasks are ordered - either EDF or RM scheduling + budget (int): How long the component can run (in time units) before it must pause + period (int): How often the runtime budget refreshes (in time units) + core_id (int): Which CPU core this component runs on + priority (int | None): How important this component is compared to others + (used for RM scheduling, None for EDF) + Lower number means higher priority + """ + def __init__(self, component_id: str, scheduler: Scheduler, budget: int, + period: int, core_id: int, priority: int | None): + self.id = component_id + self.scheduler = scheduler + self.budget = budget + self.period = period + self.core_id = core_id + self.priority = priority + self.remaining_budget = budget + self.task_queue = [] diff --git a/src/common/core.py b/src/common/core.py new file mode 100644 index 0000000..7ba0a37 --- /dev/null +++ b/src/common/core.py @@ -0,0 +1,19 @@ +from dataclasses import dataclass + +from common.scheduler import Scheduler + +@dataclass +class Core: + """ + A class representing a processing core architecture with its properties and scheduling capabilities. + Attributes: + core_id (int): The unique identifier for the core. + speed_factor (float): A numerical value representing the core's speed relative to a nominal speed. + 1.0 represents the nominal speed. A value of 0.5 indicates a core that is 50% slower, + and a value of 1.2 indicates a core that is 20% faster. The WCET of tasks assigned to + a core must be adjusted by dividing the nominal WCET by the speed_factor. + scheduler (Scheduler): The scheduler instance responsible for managing task execution on this core. + """ + id:int + speed_factor:float + scheduler: Scheduler diff --git a/src/common/csvoutput.py b/src/common/csvoutput.py new file mode 100644 index 0000000..90af83a --- /dev/null +++ b/src/common/csvoutput.py @@ -0,0 +1,53 @@ +import csv +from typing import List, Dict, Optional +from dataclasses import dataclass + +@dataclass +class TaskResult: + task_name: str + component_id: str + task_schedulable: bool + avg_response_time: float + max_response_time: float + component_schedulable: bool + +class CSVOutput: + def __init__(self, filename: str): + self.filename = filename + self.headers = [ + 'task_name', + 'component_id', + 'task_schedulable', + 'avg_response_time', + 'max_response_time', + 'component_schedulable' + ] + self.results: List[TaskResult] = [] + + def add_task_result(self, result: TaskResult) -> None: + """Add a single task result to the collection.""" + self.results.append(result) + + def add_multiple_results(self, results: List[TaskResult]) -> None: + """Add multiple task results at once.""" + self.results.extend(results) + + def write_results(self) -> None: + """Write all results to the CSV file.""" + with open(self.filename, 'w', newline='') as csvfile: + writer = csv.DictWriter(csvfile, fieldnames=self.headers) + writer.writeheader() + + for result in self.results: + writer.writerow({ + 'task_name': result.task_name, + 'component_id': result.component_id, + 'task_schedulable': 1 if result.task_schedulable else 0, + 'avg_response_time': result.avg_response_time, + 'max_response_time': result.max_response_time, + 'component_schedulable': 1 if result.component_schedulable else 0 + }) + + def clear_results(self) -> None: + """Clear all stored results.""" + self.results.clear() \ No newline at end of file diff --git a/src/common/csvreader.py b/src/common/csvreader.py index f702e84..4204075 100644 --- a/src/common/csvreader.py +++ b/src/common/csvreader.py @@ -1,12 +1,13 @@ import pandas as pd import os -from common.architecture import Architecture -from common.budget import Budget +from common.core import Core +from common.component import Component +from common.task import Task from common.scheduler import Scheduler from common.utils import get_project_root -def read_architectures(csv:str)-> list[Architecture]: +def read_cores(csv:str) -> list[Core]: """ Reads the architecture information from a CSV file and returns a list of Architecture objects. @@ -22,8 +23,8 @@ def read_architectures(csv:str)-> list[Architecture]: architectures = [] for _, row in df.iterrows(): - architecture = Architecture( - core_id=row['core_id'], + architecture = Core( + id=row['core_id'], speed_factor=row['speed_factor'], scheduler=Scheduler[row['scheduler']] ) @@ -31,14 +32,14 @@ def read_architectures(csv:str)-> list[Architecture]: return architectures -def read_budgets(csv:str)-> list[Budget]: +def read_budgets(csv:str) -> list[Component]: csv = _get_csv_path(csv) df = pd.read_csv(csv) budgets = [] for _,row in df.iterrows(): - budget = Budget( + budget = Component( component_id=row['component_id'], scheduler=Scheduler[row['scheduler']], budget=row['budget'], @@ -50,6 +51,24 @@ def read_budgets(csv:str)-> list[Budget]: return budgets +def read_tasks(csv:str) -> list[Task]: + csv = _get_csv_path(csv) + + df = pd.read_csv(csv) + + tasks = [] + for _,row in df.iterrows(): + task = Task( + task_name=row['task_name'], + wcet=row['wcet'], + period=row['period'], + component_id=row['component_id'], + priority=row['priority'] + ) + tasks.append(task) + + return tasks + def _get_csv_path(csv:str) -> str: if os.path.exists(csv): return csv diff --git a/src/common/scheduler.py b/src/common/scheduler.py index 463763c..657a0b0 100644 --- a/src/common/scheduler.py +++ b/src/common/scheduler.py @@ -1,5 +1,7 @@ from enum import Enum +from common.task import Task + class Scheduler(Enum): RM = "Rate Monotonic" EDF = "Earliest Deadline First" diff --git a/src/common/task.py b/src/common/task.py index 93fe38e..0c6ce6d 100644 --- a/src/common/task.py +++ b/src/common/task.py @@ -1,11 +1,22 @@ -from dataclasses import dataclass - -@dataclass class Task: - task_name:str - wcet:int - period:int - component_id:str - priority:int|None - + def __init__(self, task_name: str, wcet: int, period: int, component_id: str, priority: int | None): + self.id = task_name + self.wcet = wcet + self.period = period + self.component_id = component_id + self.priority = priority + + self.remaining_time = self.wcet + + + def __eq__(self, other): + if not isinstance(other, Task): + return NotImplemented + return (self.id == other.id and + self.wcet == other.wcet and + self.period == other.period and + self.component_id == other.component_id and + self.priority == other.priority) + def __repr__(self): + return f"Task(task_name='{self.id}', wcet={self.wcet}, period={self.period}, component_id='{self.component_id}', priority={self.priority})" \ No newline at end of file diff --git a/src/simulator.py b/src/simulator.py index e72531a..af9545a 100644 --- a/src/simulator.py +++ b/src/simulator.py @@ -1,13 +1,212 @@ -from common.csvreader import read_architectures +from common.csvreader import read_cores +from common.csvreader import read_budgets +from common.csvreader import read_tasks +from common.component import Component +from common.scheduler import Scheduler +from common.core import Core +from common.task import Task +from common.csvoutput import TaskResult -def main(): - # Example usage - csv_path = 'data/testcases/1-tiny-test-case/architecture.csv' - architectures = read_architectures(csv_path) - for architecture in architectures: - print(architecture) +class Simulator: + def __init__(self, cores:Core, tasks:Task, components:Component): + self.cores:list[Core] = cores + self.tasks:list[Task] = tasks + self.components:list[Component] = components + self._fill_component_task_queues() + self._adjust_task_wcet() + self.wcet = 0 + self.task_start_times: dict[str, float] = {} # task_id -> start time + self.task_response_times: dict[str, list[float]] = {} # task_id -> list of response times + self.task_deadlines: dict[str, list[bool]] = {} # task_id -> list of deadline met flags + + def run(self): + print("Running simulation...") + + t = 0 # Simulation time in us + + # Initialize response time tracking + for task in self.tasks: + self.task_response_times[task.id] = [] + self.task_deadlines[task.id] = [] + + while True: + print(f"\nTime step: {t}") + terminated = True + + # --- Phase 1: Reset budgets for components whose period renews at time `t` --- + for component in self.components: + if t % component.period == 0: + print(f"Replenishing budget for component {component.id} to {component.budget}") + component.remaining_budget = component.budget # Replenish + + # --- Phase 2: Core-level scheduling (pick which component runs) --- + for core in self.cores: + if self._is_core_empty(core.id): + print(f"Core {core.id} has no more assigned task, skipping...") + continue + + terminated = False + + print(f"\nScheduling for Core {core.id}:") + + + # Get components assigned to this core with remaining budget + eligible_components = [ + c for c in self.components + if c.core_id == core.id and c.remaining_budget > 0 + ] + print(f"Eligible components: {[c.id for c in eligible_components]}") + + if core.scheduler == Scheduler.EDF: + next_component = min(eligible_components, key=lambda c: c.period) # Earliest deadline + elif core.scheduler == Scheduler.RM: + next_component = min(eligible_components, key=lambda c: c.priority) # Highest priority + else: + next_component = None # No valid scheduler found + + print(f"Selected component {next_component.id} for execution") + task_to_run = next_component.task_queue[0] # Get highest-priority task without removing + print(f"Running task {task_to_run.id}, remaining time: {task_to_run.remaining_time}") + + # Track start time when task begins + if task_to_run.remaining_time == task_to_run.wcet: + self.task_start_times[task_to_run.id] = t + + task_to_run.remaining_time -= 1 # Simulate task execution + if task_to_run.remaining_time <= 0: + # Task completed, remove it from the queue + response_time = t + 1 - self.task_start_times[task_to_run.id] + self.task_response_times[task_to_run.id].append(response_time) + + # Check if completed within period/deadline + deadline_met = response_time <= task_to_run.period + self.task_deadlines[task_to_run.id].append(deadline_met) + + _ = next_component.task_queue.pop(0) + next_component.remaining_budget -= 1 # Consume budget + + if terminated: + break + else: + t += 1 + self.wcet = t + print("---------------------------------------------------------") + print("Simulation finished. Simulation time:", t) + print("---------------------------------------------------------") + + def _fill_component_task_queues(self): + for component in self.components: + # Get tasks assigned to this component + tasks_for_component = [ + task for task in self.tasks if task.component_id == component.id + ] + + if component.scheduler == "RM": + tasks_for_component.sort(key=lambda t: t.priority) + else: + tasks_for_component.sort(key=lambda t: t.period) + + component.task_queue = tasks_for_component + + def _adjust_task_wcet(self): + """ + Adjust the WCET of tasks based on the speed factor of the core they are assigned to. + """ + for component in self.components: + core = next((c for c in self.cores if c.id == component.core_id), None) + if not core: + continue + + for task in component.task_queue: + task.wcet = task.wcet / core.speed_factor + task.remaining_time = task.wcet + + def _is_core_empty(self, core_id): + """ + Check if all components assigned to the specified core have empty task queues. + + Args: + core_id: The ID of the core to check + + Returns: + bool: True if no tasks remain for any component on this core, False otherwise + """ + return all( + len(component.task_queue) == 0 + for component in self.components + if component.core_id == core_id + ) + + def get_task_results(self) -> list[TaskResult]: + """Get the simulation results for all tasks. + + Returns: + List[TaskResult]: Results for each task including response times and schedulability. + """ + results = [] + for task in self.tasks: + response_times = self.task_response_times.get(task.id, []) + deadline_flags = self.task_deadlines.get(task.id, []) + + if response_times: + avg_response = sum(response_times) / len(response_times) + max_response = max(response_times) + # Task is schedulable only if ALL instances met their deadlines + task_schedulable = all(deadline_flags) + else: + avg_response = 0.0 + max_response = 0.0 + task_schedulable = False + + # Get component for this task + component = next(c for c in self.components if c.id == task.component_id) + + # A component is schedulable if all its tasks are schedulable + component_tasks = [t for t in self.tasks if t.component_id == component.id] + component_schedulable = all( + all(self.task_deadlines.get(t.id, [])) + for t in component_tasks + ) + + results.append(TaskResult( + task_name=task.id, + component_id=task.component_id, + task_schedulable=task_schedulable, + avg_response_time=avg_response, + max_response_time=max_response, + component_schedulable=component_schedulable + )) + + return results + +def main(): + # Base path for test case files + base_path = 'data/testcases/1-tiny-test-case' + + # Read architectures + architecture_path = f'{base_path}/architecture.csv' + cores = read_cores(architecture_path) + print("Architectures:") + for core in cores: + print(core) + # Read tasks + tasks_path = f'{base_path}/tasks.csv' + tasks = read_tasks(tasks_path) + print("\nTasks:") + for task in tasks: + print(task) + + # Read components + budgets_path = f'{base_path}/budgets.csv' + components = read_budgets(budgets_path) + print("\nBudgets:") + for component in components: + print(component) + + simulator = Simulator(cores, tasks, components) + simulator.run() if __name__ == "__main__": main() \ No newline at end of file diff --git a/tests/test_simulator.py b/tests/test_simulator.py new file mode 100644 index 0000000..62f5a19 --- /dev/null +++ b/tests/test_simulator.py @@ -0,0 +1,10 @@ +import pytest +import sys +from pathlib import Path + +# Add the project root directory to Python path +sys.path.append(str(Path(__file__).parent.parent)) + +from src.simulator import Simulator +from src.common.csvreader import read_cores, read_budgets, read_tasks + From 7c275e02c4aed26ed1a88651d44aea7d713b01e2 Mon Sep 17 00:00:00 2001 From: MarlonBando Date: Fri, 18 Apr 2025 17:42:38 +0200 Subject: [PATCH 2/9] Refactor Simulator initialization: Change parameter order for clarity and update test case to match new structure; Itroduced tests --- src/common/csvreader.py | 2 +- src/common/utils.py | 8 +++++++- src/simulator.py | 4 ++-- tests/test_simulator.py | 29 ++++++++++++++++++++++++++--- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/common/csvreader.py b/src/common/csvreader.py index 4204075..349e9e6 100644 --- a/src/common/csvreader.py +++ b/src/common/csvreader.py @@ -80,5 +80,5 @@ def _get_csv_path(csv:str) -> str: raise FileNotFoundError( f"File {csv} does not exist. " "Pass to the function an absolute path or a relative path from the project root. " - "For example 'data/testcases/1-tiny-test-case/architecture.csv" + "For example 'data/testcases/1-tiny-test-case/architecture.csv", csv ) \ No newline at end of file diff --git a/src/common/utils.py b/src/common/utils.py index ff1f837..1170651 100644 --- a/src/common/utils.py +++ b/src/common/utils.py @@ -4,4 +4,10 @@ def get_project_root() -> str: """ Get the root directory of the project. """ - return os.path.dirname(os.path.dirname(os.path.abspath(__file__))) \ No newline at end of file + + current_file = os.path.abspath(__file__) + common_dir = os.path.dirname(current_file) + src_dir = os.path.dirname(common_dir) + project_root = os.path.dirname(src_dir) + + return project_root \ No newline at end of file diff --git a/src/simulator.py b/src/simulator.py index af9545a..20d5219 100644 --- a/src/simulator.py +++ b/src/simulator.py @@ -8,7 +8,7 @@ from common.csvoutput import TaskResult class Simulator: - def __init__(self, cores:Core, tasks:Task, components:Component): + def __init__(self, cores:Core, components:Component, tasks:Task): self.cores:list[Core] = cores self.tasks:list[Task] = tasks self.components:list[Component] = components @@ -205,7 +205,7 @@ def main(): for component in components: print(component) - simulator = Simulator(cores, tasks, components) + simulator = Simulator(cores, components, tasks) simulator.run() if __name__ == "__main__": diff --git a/tests/test_simulator.py b/tests/test_simulator.py index 62f5a19..e19ba19 100644 --- a/tests/test_simulator.py +++ b/tests/test_simulator.py @@ -1,10 +1,33 @@ -import pytest import sys from pathlib import Path -# Add the project root directory to Python path -sys.path.append(str(Path(__file__).parent.parent)) +# Add the src directory to Python path +sys.path.append(str(Path(__file__).parent.parent / "src")) from src.simulator import Simulator from src.common.csvreader import read_cores, read_budgets, read_tasks +def test_simulator_tiny_case(): + cores = read_cores("data/testcases/1-tiny-test-case/architecture.csv") + budgets = read_budgets("data/testcases/1-tiny-test-case/budgets.csv") + tasks = read_tasks("data/testcases/1-tiny-test-case/tasks.csv") + + # Initialize simulator + simulator = Simulator(cores, budgets, tasks) + + # Run simulation + simulator.run() + + # Get results + results = simulator.get_task_results() + + # Assert expected values + # These values should match your tiny test case expected outcomes + assert len(results) > 0 + # Example assertions (adjust according to your actual test data): + assert results[0].component_schedulable == True + assert results[0].avg_response_time == int(14 / 0.62 + 1) + + assert results[1].component_schedulable == True + assert results[1].avg_response_time == int(33 / 0.62 + 1) + From 1d028a9e6708b84c617108fa7906c98f03f53ff2 Mon Sep 17 00:00:00 2001 From: MarlonBando Date: Fri, 2 May 2025 10:57:49 +0200 Subject: [PATCH 3/9] added random generation of execution time --- data/testcases | 2 +- src/simulator.py | 75 +++++++++++++++++++++++++++++++++++++---- tests/test_simulator.py | 1 + 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/data/testcases b/data/testcases index d255eae..bc12235 160000 --- a/data/testcases +++ b/data/testcases @@ -1 +1 @@ -Subproject commit d255eae14fd7153234d504e2212788a06580efbe +Subproject commit bc12235b1ece81be2976800bba0a06f1ae9a1a91 diff --git a/src/simulator.py b/src/simulator.py index 20d5219..d4655eb 100644 --- a/src/simulator.py +++ b/src/simulator.py @@ -1,3 +1,6 @@ +import random as rand +import numpy as np + from common.csvreader import read_cores from common.csvreader import read_budgets from common.csvreader import read_tasks @@ -7,6 +10,8 @@ from common.task import Task from common.csvoutput import TaskResult +CLOCK_TICK = 1 + class Simulator: def __init__(self, cores:Core, components:Component, tasks:Task): self.cores:list[Core] = cores @@ -29,7 +34,10 @@ def run(self): self.task_response_times[task.id] = [] self.task_deadlines[task.id] = [] - while True: + self._generate_execution_time() + simulation_iteration = 0 + + while simulation_iteration < 1_000: print(f"\nTime step: {t}") terminated = True @@ -72,7 +80,7 @@ def run(self): if task_to_run.remaining_time == task_to_run.wcet: self.task_start_times[task_to_run.id] = t - task_to_run.remaining_time -= 1 # Simulate task execution + task_to_run.remaining_time -= CLOCK_TICK # Simulate task execution if task_to_run.remaining_time <= 0: # Task completed, remove it from the queue response_time = t + 1 - self.task_start_times[task_to_run.id] @@ -82,13 +90,14 @@ def run(self): deadline_met = response_time <= task_to_run.period self.task_deadlines[task_to_run.id].append(deadline_met) - _ = next_component.task_queue.pop(0) - next_component.remaining_budget -= 1 # Consume budget + _ = next_component.task_queue.pop(0) # Pop from the head of the queue + next_component.remaining_budget -= CLOCK_TICK # Consume budget if terminated: - break + simulation_iteration += 1 + self._generate_execution_time() else: - t += 1 + t += CLOCK_TICK self.wcet = t print("---------------------------------------------------------") @@ -122,6 +131,58 @@ def _adjust_task_wcet(self): task.wcet = task.wcet / core.speed_factor task.remaining_time = task.wcet + def _generate_execution_time(self): + """ + Generate execution time for each task based on its WCET and the speed factor of the core. + """ + for component in self.components: + core = next((c for c in self.cores if c.id == component.core_id), None) + if not core: + continue + + for task in component.task_queue: + # Avionics (DO-178C): Typically ≥80% to ensure strict deadline guarantees. + lower_bound = task.wcet * 0.8 + task.remaining_time = self._generate_normal_exec_time(lower_bound, task.wcet) + + def _generate_normal_exec_time(self,wcet, lower_bound): + """ + Generate execution time following a normal distribution bounded between WCET and lower_bound. + + The function uses a normal distribution with: + - mean: average of WCET and lower_bound ((wcet + lower_bound)/2) + - standard deviation: (wcet - lower_bound)/6 to ensure ~99.7% of values fall within ±3σ + + The while loop ensures the generated execution time stays within the specified bounds + by rejecting and regenerating values that fall outside [lower_bound, wcet]. + + Parameters + ---------- + wcet : float + Worst Case Execution Time - upper bound for execution time + lower_bound : float + Minimum possible execution time - lower bound + + Returns + ------- + float + A randomly generated execution time that follows a normal distribution + and falls between lower_bound and wcet inclusive + + Notes + ----- + - Using (wcet - lower_bound)/6 as std dev ensures most values (~99.7%) + fall within the desired range, minimizing rejections in the while loop + - The distribution is symmetrical around the mean, creating a bell curve + between lower_bound and wcet + """ + mean = (wcet + lower_bound) / 2 + std_dev = (wcet - lower_bound) / 6 + while True: # Ensure value stays within bounds + exec_time = np.random.normal(mean, std_dev) + if lower_bound <= exec_time <= wcet: + return exec_time + def _is_core_empty(self, core_id): """ Check if all components assigned to the specified core have empty task queues. @@ -182,7 +243,7 @@ def get_task_results(self) -> list[TaskResult]: def main(): # Base path for test case files - base_path = 'data/testcases/1-tiny-test-case' + base_path = 'data/testcases/3-medium-test-case' # Read architectures architecture_path = f'{base_path}/architecture.csv' diff --git a/tests/test_simulator.py b/tests/test_simulator.py index e19ba19..acc4e7c 100644 --- a/tests/test_simulator.py +++ b/tests/test_simulator.py @@ -31,3 +31,4 @@ def test_simulator_tiny_case(): assert results[1].component_schedulable == True assert results[1].avg_response_time == int(33 / 0.62 + 1) + \ No newline at end of file From 66eab4fd719d0c993803cb19bd009849ec48fcfc Mon Sep 17 00:00:00 2001 From: MarlonBando Date: Sat, 3 May 2025 11:10:38 +0200 Subject: [PATCH 4/9] Add architecture, budgets, and tasks CSV files; enhance simulator functionality with output generation --- .../architecture.csv | 2 + .../11-unschedulable-test-case/budgets.csv | 3 + .../11-unschedulable-test-case/tasks.csv | 9 +++ data/testcases | 2 +- output.csv | 22 ++++++ src/simulator.py | 68 ++++++++++++++----- tests/test_simulator.py | 3 - 7 files changed, 88 insertions(+), 21 deletions(-) create mode 100644 data/custom/11-unschedulable-test-case/architecture.csv create mode 100644 data/custom/11-unschedulable-test-case/budgets.csv create mode 100644 data/custom/11-unschedulable-test-case/tasks.csv create mode 100644 output.csv diff --git a/data/custom/11-unschedulable-test-case/architecture.csv b/data/custom/11-unschedulable-test-case/architecture.csv new file mode 100644 index 0000000..91352c3 --- /dev/null +++ b/data/custom/11-unschedulable-test-case/architecture.csv @@ -0,0 +1,2 @@ +core_id,speed_factor,scheduler +Core_1,1.49,EDF diff --git a/data/custom/11-unschedulable-test-case/budgets.csv b/data/custom/11-unschedulable-test-case/budgets.csv new file mode 100644 index 0000000..ff0d7de --- /dev/null +++ b/data/custom/11-unschedulable-test-case/budgets.csv @@ -0,0 +1,3 @@ +component_id,scheduler,budget,period,core_id,priority +Camera_Sensor,RM,5,9,Core_1, +Image_Processor,EDF,2,6,Core_1, diff --git a/data/custom/11-unschedulable-test-case/tasks.csv b/data/custom/11-unschedulable-test-case/tasks.csv new file mode 100644 index 0000000..2b1d5c2 --- /dev/null +++ b/data/custom/11-unschedulable-test-case/tasks.csv @@ -0,0 +1,9 @@ +task_name,wcet,period,component_id,priority +Task_0,16,100,Camera_Sensor,1 +Task_1,10,50,Camera_Sensor,0 +Task_2,58,70,Camera_Sensor,3 +Task_3,8,200,Camera_Sensor,2 +Task_4,120,900,Camera_Sensor,4 +Task_5,4,25,Image_Processor, +Task_6,4,50,Image_Processor, +Task_7,13,75,Image_Processor diff --git a/data/testcases b/data/testcases index bc12235..3f5da36 160000 --- a/data/testcases +++ b/data/testcases @@ -1 +1 @@ -Subproject commit bc12235b1ece81be2976800bba0a06f1ae9a1a91 +Subproject commit 3f5da3634225777b3643e8a1d5780d7c14c9eda0 diff --git a/output.csv b/output.csv new file mode 100644 index 0000000..2442341 --- /dev/null +++ b/output.csv @@ -0,0 +1,22 @@ +Task,Component,Task Schedulable,Avg Response Time,Max Response Time,Component Schedulable +Task_0,Camera_Sensor,True,30.00,30.00,True +Task_1,Camera_Sensor,True,1.00,1.00,True +Task_2,Camera_Sensor,True,3.00,3.00,True +Task_3,Camera_Sensor,True,78.00,78.00,True +Task_4,Image_Processor,True,1.00,1.00,True +Task_5,Image_Processor,True,1.00,1.00,True +Task_6,Lidar_Sensor,True,9.00,9.00,True +Task_7,Lidar_Sensor,True,1.00,1.00,True +Task_8,Lidar_Sensor,True,45.00,45.00,True +Task_9,Lidar_Sensor,True,115.00,115.00,True +Task_10,Lidar_Sensor,True,55.00,55.00,True +Task_11,Lidar_Sensor,True,1.00,1.00,True +Task_12,GPS_Sensor,True,4.00,4.00,True +Task_13,GPS_Sensor,True,32.00,32.00,True +Task_14,Communication_Unit,True,2.00,2.00,True +Task_15,Communication_Unit,True,21.00,21.00,True +Task_16,Communication_Unit,True,3.00,3.00,True +Task_17,Communication_Unit,True,1.00,1.00,True +Task_18,Proximity_Sensor,True,34.00,34.00,True +Task_19,Proximity_Sensor,True,126.00,126.00,True +Task_20,Proximity_Sensor,True,1.00,1.00,True diff --git a/src/simulator.py b/src/simulator.py index d4655eb..b199c46 100644 --- a/src/simulator.py +++ b/src/simulator.py @@ -11,6 +11,8 @@ from common.csvoutput import TaskResult CLOCK_TICK = 1 +SIMULATION_ITERATIONS = 100 +LOWER_BOUND_PERCENTAGE = 1 class Simulator: def __init__(self, cores:Core, components:Component, tasks:Task): @@ -19,11 +21,16 @@ def __init__(self, cores:Core, components:Component, tasks:Task): self.components:list[Component] = components self._fill_component_task_queues() self._adjust_task_wcet() - self.wcet = 0 + self.task_start_times: dict[str, float] = {} # task_id -> start time self.task_response_times: dict[str, list[float]] = {} # task_id -> list of response times self.task_deadlines: dict[str, list[bool]] = {} # task_id -> list of deadline met flags + for task in self.tasks: + self.task_start_times[task.id] = 0 + self.task_response_times[task.id] = [] + self.task_deadlines[task.id] = [] + def run(self): print("Running simulation...") @@ -37,7 +44,7 @@ def run(self): self._generate_execution_time() simulation_iteration = 0 - while simulation_iteration < 1_000: + while simulation_iteration < SIMULATION_ITERATIONS: print(f"\nTime step: {t}") terminated = True @@ -61,9 +68,13 @@ def run(self): # Get components assigned to this core with remaining budget eligible_components = [ c for c in self.components - if c.core_id == core.id and c.remaining_budget > 0 + if c.core_id == core.id and c.remaining_budget > 0 and len(c.task_queue) > 0 ] print(f"Eligible components: {[c.id for c in eligible_components]}") + if not eligible_components: + print(f"No eligible components for Core {core.id}, skipping...") + continue + if core.scheduler == Scheduler.EDF: next_component = min(eligible_components, key=lambda c: c.period) # Earliest deadline @@ -83,27 +94,53 @@ def run(self): task_to_run.remaining_time -= CLOCK_TICK # Simulate task execution if task_to_run.remaining_time <= 0: # Task completed, remove it from the queue - response_time = t + 1 - self.task_start_times[task_to_run.id] + response_time = t - self.task_start_times[task_to_run.id] + if response_time == 0: + response_time = CLOCK_TICK + self.task_response_times[task_to_run.id].append(response_time) # Check if completed within period/deadline - deadline_met = response_time <= task_to_run.period + deadline_met = t <= task_to_run.period self.task_deadlines[task_to_run.id].append(deadline_met) _ = next_component.task_queue.pop(0) # Pop from the head of the queue next_component.remaining_budget -= CLOCK_TICK # Consume budget if terminated: + print("---------------------------------------------------------") + print("Iteration ", simulation_iteration, " terminated!") + print("---------------------------------------------------------") simulation_iteration += 1 + t = 0 self._generate_execution_time() + self._fill_component_task_queues() else: t += CLOCK_TICK - self.wcet = t print("---------------------------------------------------------") print("Simulation finished. Simulation time:", t) print("---------------------------------------------------------") + def generate_output_file(self, filename: str): + """Generate a CSV output file with task simulation results. + + Args: + filename: Path to the output CSV file + """ + task_results = self.get_task_results() + + # Create CSV file + with open(filename, 'w') as f: + # Write header + f.write("Task,Component,Task Schedulable,Avg Response Time,Max Response Time,Component Schedulable\n") + + # Write data for each task + for result in task_results: + f.write(f"{result.task_name},{result.component_id},{result.task_schedulable}," + f"{result.avg_response_time:.2f},{result.max_response_time:.2f}," + f"{result.component_schedulable}\n") + def _fill_component_task_queues(self): for component in self.components: # Get tasks assigned to this component @@ -135,17 +172,12 @@ def _generate_execution_time(self): """ Generate execution time for each task based on its WCET and the speed factor of the core. """ - for component in self.components: - core = next((c for c in self.cores if c.id == component.core_id), None) - if not core: - continue - - for task in component.task_queue: - # Avionics (DO-178C): Typically ≥80% to ensure strict deadline guarantees. - lower_bound = task.wcet * 0.8 - task.remaining_time = self._generate_normal_exec_time(lower_bound, task.wcet) + for task in self.tasks: + # Avionics (DO-178C): Typically ≥80% to ensure strict deadline guarantees. + lower_bound = task.wcet * LOWER_BOUND_PERCENTAGE + task.remaining_time = self._generate_normal_exec_time(lower_bound, task.wcet) - def _generate_normal_exec_time(self,wcet, lower_bound): + def _generate_normal_exec_time(self,lower_bound, wcet): """ Generate execution time following a normal distribution bounded between WCET and lower_bound. @@ -243,7 +275,7 @@ def get_task_results(self) -> list[TaskResult]: def main(): # Base path for test case files - base_path = 'data/testcases/3-medium-test-case' + base_path = 'data/testcases/7-unschedulable-test-case' # Read architectures architecture_path = f'{base_path}/architecture.csv' @@ -269,5 +301,7 @@ def main(): simulator = Simulator(cores, components, tasks) simulator.run() + simulator.generate_output_file("output.csv") + if __name__ == "__main__": main() \ No newline at end of file diff --git a/tests/test_simulator.py b/tests/test_simulator.py index acc4e7c..5e986d5 100644 --- a/tests/test_simulator.py +++ b/tests/test_simulator.py @@ -1,9 +1,6 @@ import sys from pathlib import Path -# Add the src directory to Python path -sys.path.append(str(Path(__file__).parent.parent / "src")) - from src.simulator import Simulator from src.common.csvreader import read_cores, read_budgets, read_tasks From dc0a6f5c33dc5f1215425bfc62624041c275a1d0 Mon Sep 17 00:00:00 2001 From: MarlonBando Date: Fri, 9 May 2025 17:37:46 +0200 Subject: [PATCH 5/9] Add validation test case data files and update simulator logic for improved task scheduling --- .../13-validation-test-case/architecture.csv | 3 + .../13-validation-test-case/budgets.csv | 3 + data/custom/13-validation-test-case/tasks.csv | 4 + .../14-validation-test-case/architecture.csv | 2 + .../14-validation-test-case/budgets.csv | 3 + data/custom/14-validation-test-case/tasks.csv | 6 + data/custom/15-med-onecore/architecture.csv | 3 + data/custom/15-med-onecore/budgets.csv | 3 + data/custom/15-med-onecore/tasks.csv | 11 + data/custom/16-large-onecore/architecture.csv | 4 + data/custom/16-large-onecore/budgets.csv | 3 + data/custom/16-large-onecore/tasks.csv | 11 + data/testcases | 2 +- output.csv | 31 +- output_2core.csv | 19 ++ src/common/task.py | 2 +- src/simulator.py | 271 +++++++++++------- tests/test_simulator.py | 6 +- 18 files changed, 263 insertions(+), 124 deletions(-) create mode 100644 data/custom/13-validation-test-case/architecture.csv create mode 100644 data/custom/13-validation-test-case/budgets.csv create mode 100644 data/custom/13-validation-test-case/tasks.csv create mode 100644 data/custom/14-validation-test-case/architecture.csv create mode 100644 data/custom/14-validation-test-case/budgets.csv create mode 100644 data/custom/14-validation-test-case/tasks.csv create mode 100644 data/custom/15-med-onecore/architecture.csv create mode 100644 data/custom/15-med-onecore/budgets.csv create mode 100644 data/custom/15-med-onecore/tasks.csv create mode 100644 data/custom/16-large-onecore/architecture.csv create mode 100644 data/custom/16-large-onecore/budgets.csv create mode 100644 data/custom/16-large-onecore/tasks.csv create mode 100644 output_2core.csv diff --git a/data/custom/13-validation-test-case/architecture.csv b/data/custom/13-validation-test-case/architecture.csv new file mode 100644 index 0000000..d63dbcc --- /dev/null +++ b/data/custom/13-validation-test-case/architecture.csv @@ -0,0 +1,3 @@ +core_id,speed_factor,scheduler +C1,1.0,EDF +C2,1.0,RM \ No newline at end of file diff --git a/data/custom/13-validation-test-case/budgets.csv b/data/custom/13-validation-test-case/budgets.csv new file mode 100644 index 0000000..8924fb4 --- /dev/null +++ b/data/custom/13-validation-test-case/budgets.csv @@ -0,0 +1,3 @@ +component_id,scheduler,budget,period,core_id,priority +CompA,EDF,3,6,C1, +CompB,EDF,2,6,C1, \ No newline at end of file diff --git a/data/custom/13-validation-test-case/tasks.csv b/data/custom/13-validation-test-case/tasks.csv new file mode 100644 index 0000000..3530490 --- /dev/null +++ b/data/custom/13-validation-test-case/tasks.csv @@ -0,0 +1,4 @@ +task_name,wcet,period,component_id,priority +T1,1,3,CompA, +T2,1,3,CompA, +T3,2,6,CompA, \ No newline at end of file diff --git a/data/custom/14-validation-test-case/architecture.csv b/data/custom/14-validation-test-case/architecture.csv new file mode 100644 index 0000000..8e8c71e --- /dev/null +++ b/data/custom/14-validation-test-case/architecture.csv @@ -0,0 +1,2 @@ +core_id,speed_factor,scheduler +C1,1.0,EDF \ No newline at end of file diff --git a/data/custom/14-validation-test-case/budgets.csv b/data/custom/14-validation-test-case/budgets.csv new file mode 100644 index 0000000..8ea834e --- /dev/null +++ b/data/custom/14-validation-test-case/budgets.csv @@ -0,0 +1,3 @@ +component_id,scheduler,budget,period,core_id,priority +CompA,EDF,3,6,C1,1 +CompB,EDF,2,6,C1,1 \ No newline at end of file diff --git a/data/custom/14-validation-test-case/tasks.csv b/data/custom/14-validation-test-case/tasks.csv new file mode 100644 index 0000000..12aa416 --- /dev/null +++ b/data/custom/14-validation-test-case/tasks.csv @@ -0,0 +1,6 @@ +task_name,wcet,period,component_id,priority +T1,1,6,CompA, +T2,1,6,CompA, +T3,2,12,CompA, +T4,1,12,CompB, +T5,1,6,CompB, \ No newline at end of file diff --git a/data/custom/15-med-onecore/architecture.csv b/data/custom/15-med-onecore/architecture.csv new file mode 100644 index 0000000..bdd94cb --- /dev/null +++ b/data/custom/15-med-onecore/architecture.csv @@ -0,0 +1,3 @@ +core_id,speed_factor,scheduler +Core_1,1.49,EDF +Core_2,0.62,EDF diff --git a/data/custom/15-med-onecore/budgets.csv b/data/custom/15-med-onecore/budgets.csv new file mode 100644 index 0000000..68ae82c --- /dev/null +++ b/data/custom/15-med-onecore/budgets.csv @@ -0,0 +1,3 @@ +component_id,scheduler,budget,period,core_id,priority +Lidar_Sensor,RM,1,3,Core_2, +Control_Unit,EDF,6,9,Core_2, diff --git a/data/custom/15-med-onecore/tasks.csv b/data/custom/15-med-onecore/tasks.csv new file mode 100644 index 0000000..306e9b2 --- /dev/null +++ b/data/custom/15-med-onecore/tasks.csv @@ -0,0 +1,11 @@ +task_name,wcet,period,component_id,priority +Task_8,1,25,Lidar_Sensor,0 +Task_9,4,100,Lidar_Sensor,2 +Task_10,2,50,Lidar_Sensor,1 +Task_11,3,200,Lidar_Sensor,3 +Task_12,3,75,Control_Unit, +Task_13,4,40,Control_Unit, +Task_14,6,100,Control_Unit, +Task_15,4,50,Control_Unit, +Task_16,5,75,Control_Unit, +Task_17,4,120,Control_Unit, diff --git a/data/custom/16-large-onecore/architecture.csv b/data/custom/16-large-onecore/architecture.csv new file mode 100644 index 0000000..f664efd --- /dev/null +++ b/data/custom/16-large-onecore/architecture.csv @@ -0,0 +1,4 @@ +core_id,speed_factor,scheduler +Core_1,0.54,EDF +Core_2,0.7,EDF +Core_3,0.74,RM diff --git a/data/custom/16-large-onecore/budgets.csv b/data/custom/16-large-onecore/budgets.csv new file mode 100644 index 0000000..f812d93 --- /dev/null +++ b/data/custom/16-large-onecore/budgets.csv @@ -0,0 +1,3 @@ +component_id,scheduler,budget,period,core_id,priority +Lidar_Sensor,RM,1,3,Core_2, +Control_Unit,EDF,4,6,Core_2 diff --git a/data/custom/16-large-onecore/tasks.csv b/data/custom/16-large-onecore/tasks.csv new file mode 100644 index 0000000..a12622c --- /dev/null +++ b/data/custom/16-large-onecore/tasks.csv @@ -0,0 +1,11 @@ +task_name,wcet,period,component_id,priority +Task_12,8,100,Lidar_Sensor,1 +Task_13,9,90,Lidar_Sensor,0 +Task_14,6,300,Lidar_Sensor,2 +Task_15,23,900,Lidar_Sensor,3 +Task_16,2,200,Control_Unit, +Task_17,7,80,Control_Unit, +Task_18,4,800,Control_Unit, +Task_19,12,100,Control_Unit, +Task_20,37,300,Control_Unit, +Task_21,4,50,Control_Unit, diff --git a/data/testcases b/data/testcases index 3f5da36..0166426 160000 --- a/data/testcases +++ b/data/testcases @@ -1 +1 @@ -Subproject commit 3f5da3634225777b3643e8a1d5780d7c14c9eda0 +Subproject commit 0166426bc193a61c1eba36877ea93511ddf7db04 diff --git a/output.csv b/output.csv index 2442341..cb48912 100644 --- a/output.csv +++ b/output.csv @@ -1,22 +1,11 @@ Task,Component,Task Schedulable,Avg Response Time,Max Response Time,Component Schedulable -Task_0,Camera_Sensor,True,30.00,30.00,True -Task_1,Camera_Sensor,True,1.00,1.00,True -Task_2,Camera_Sensor,True,3.00,3.00,True -Task_3,Camera_Sensor,True,78.00,78.00,True -Task_4,Image_Processor,True,1.00,1.00,True -Task_5,Image_Processor,True,1.00,1.00,True -Task_6,Lidar_Sensor,True,9.00,9.00,True -Task_7,Lidar_Sensor,True,1.00,1.00,True -Task_8,Lidar_Sensor,True,45.00,45.00,True -Task_9,Lidar_Sensor,True,115.00,115.00,True -Task_10,Lidar_Sensor,True,55.00,55.00,True -Task_11,Lidar_Sensor,True,1.00,1.00,True -Task_12,GPS_Sensor,True,4.00,4.00,True -Task_13,GPS_Sensor,True,32.00,32.00,True -Task_14,Communication_Unit,True,2.00,2.00,True -Task_15,Communication_Unit,True,21.00,21.00,True -Task_16,Communication_Unit,True,3.00,3.00,True -Task_17,Communication_Unit,True,1.00,1.00,True -Task_18,Proximity_Sensor,True,34.00,34.00,True -Task_19,Proximity_Sensor,True,126.00,126.00,True -Task_20,Proximity_Sensor,True,1.00,1.00,True +Task_12,Lidar_Sensor,False,57.38,96.00,False +Task_13,Lidar_Sensor,True,41.48,51.00,False +Task_14,Lidar_Sensor,False,112.00,112.00,False +Task_15,Lidar_Sensor,False,0.00,0.00,False +Task_16,Control_Unit,True,8.00,28.00,True +Task_17,Control_Unit,True,14.89,24.00,True +Task_18,Control_Unit,True,8.67,9.00,True +Task_19,Control_Unit,True,27.53,41.00,True +Task_20,Control_Unit,True,165.75,176.00,True +Task_21,Control_Unit,True,7.32,8.00,True diff --git a/output_2core.csv b/output_2core.csv new file mode 100644 index 0000000..94599fe --- /dev/null +++ b/output_2core.csv @@ -0,0 +1,19 @@ +Task,Component,Task Schedulable,Avg Response Time,Max Response Time,Component Schedulable +Task_0,Camera_Sensor,True,19.33,22.00,True +Task_1,Camera_Sensor,True,10.44,12.00,True +Task_2,Camera_Sensor,True,132.67,139.00,True +Task_3,Camera_Sensor,True,9.11,12.00,True +Task_4,Camera_Sensor,True,416.00,416.00,True +Task_5,Image_Processor,True,6.56,7.00,True +Task_6,Image_Processor,True,6.06,7.00,True +Task_7,Image_Processor,True,42.08,54.00,True +Task_8,Lidar_Sensor,True,3.42,4.00,True +Task_9,Lidar_Sensor,True,39.56,59.00,True +Task_10,Lidar_Sensor,True,9.67,10.00,True +Task_11,Lidar_Sensor,True,72.67,104.00,True +Task_12,Control_Unit,True,11.25,30.00,False +Task_13,Control_Unit,True,9.40,12.00,False +Task_14,Control_Unit,True,33.33,67.00,False +Task_15,Control_Unit,True,11.64,20.00,False +Task_16,Control_Unit,True,15.21,34.00,False +Task_17,Control_Unit,False,51.44,88.00,False diff --git a/src/common/task.py b/src/common/task.py index 0c6ce6d..727a821 100644 --- a/src/common/task.py +++ b/src/common/task.py @@ -5,7 +5,7 @@ def __init__(self, task_name: str, wcet: int, period: int, component_id: str, pr self.period = period self.component_id = component_id self.priority = priority - + self.release_time = 0 self.remaining_time = self.wcet diff --git a/src/simulator.py b/src/simulator.py index b199c46..056f70e 100644 --- a/src/simulator.py +++ b/src/simulator.py @@ -11,7 +11,7 @@ from common.csvoutput import TaskResult CLOCK_TICK = 1 -SIMULATION_ITERATIONS = 100 +SIMULATION_ITERATIONS = 1 LOWER_BOUND_PERCENTAGE = 1 class Simulator: @@ -19,7 +19,6 @@ def __init__(self, cores:Core, components:Component, tasks:Task): self.cores:list[Core] = cores self.tasks:list[Task] = tasks self.components:list[Component] = components - self._fill_component_task_queues() self._adjust_task_wcet() self.task_start_times: dict[str, float] = {} # task_id -> start time @@ -35,125 +34,108 @@ def run(self): print("Running simulation...") t = 0 # Simulation time in us - + # Initialize response time tracking for task in self.tasks: self.task_response_times[task.id] = [] self.task_deadlines[task.id] = [] - self._generate_execution_time() simulation_iteration = 0 - - while simulation_iteration < SIMULATION_ITERATIONS: - print(f"\nTime step: {t}") - terminated = True - - # --- Phase 1: Reset budgets for components whose period renews at time `t` --- + hyperperiod = self._get_hyperperiod() + + while simulation_iteration < SIMULATION_ITERATIONS: + # Progress tracking every 10,000 iterations + if t % 10_000 == 0: + progress = (t % hyperperiod) / hyperperiod * 100 + print(f"Time: {t}, Progress: {progress:.2f}%") + + # --- Phase 1 Release tasks --- + for task in self.tasks: + if t % task.period == 0: + if t != 0 and task.remaining_time > 0: + self.task_deadlines[task.id].append(False) + + task.remaining_time = self._generate_execution_time(task) + task.release_time = t + component = next(c for c in self.components if c.id == task.component_id) + self._schedule(t, component, task) + + # --- Phase 2: Reset budgets --- for component in self.components: if t % component.period == 0: - print(f"Replenishing budget for component {component.id} to {component.budget}") - component.remaining_budget = component.budget # Replenish + component.remaining_budget = component.budget - # --- Phase 2: Core-level scheduling (pick which component runs) --- + # --- Phase 3: Core-level scheduling --- for core in self.cores: - if self._is_core_empty(core.id): - print(f"Core {core.id} has no more assigned task, skipping...") - continue - - terminated = False - - print(f"\nScheduling for Core {core.id}:") - - - # Get components assigned to this core with remaining budget eligible_components = [ - c for c in self.components + c for c in self.components if c.core_id == core.id and c.remaining_budget > 0 and len(c.task_queue) > 0 ] - print(f"Eligible components: {[c.id for c in eligible_components]}") + if not eligible_components: - print(f"No eligible components for Core {core.id}, skipping...") continue - if core.scheduler == Scheduler.EDF: - next_component = min(eligible_components, key=lambda c: c.period) # Earliest deadline + next_component = min( + eligible_components, + key=lambda c: self._get_task_absolute_deadline(t, c.task_queue[0]) + ) elif core.scheduler == Scheduler.RM: - next_component = min(eligible_components, key=lambda c: c.priority) # Highest priority + next_component = min(eligible_components, key=lambda c: c.priority) else: - next_component = None # No valid scheduler found - - print(f"Selected component {next_component.id} for execution") - task_to_run = next_component.task_queue[0] # Get highest-priority task without removing - print(f"Running task {task_to_run.id}, remaining time: {task_to_run.remaining_time}") - - # Track start time when task begins + next_component = None + + task_to_run = next_component.task_queue[0] + if task_to_run.remaining_time == task_to_run.wcet: self.task_start_times[task_to_run.id] = t - task_to_run.remaining_time -= CLOCK_TICK # Simulate task execution + task_to_run.remaining_time -= CLOCK_TICK if task_to_run.remaining_time <= 0: - # Task completed, remove it from the queue - response_time = t - self.task_start_times[task_to_run.id] + response_time = (t + CLOCK_TICK) - self.task_start_times[task_to_run.id] if response_time == 0: response_time = CLOCK_TICK - + self.task_response_times[task_to_run.id].append(response_time) - - # Check if completed within period/deadline - deadline_met = t <= task_to_run.period - self.task_deadlines[task_to_run.id].append(deadline_met) - - _ = next_component.task_queue.pop(0) # Pop from the head of the queue - next_component.remaining_budget -= CLOCK_TICK # Consume budget - - if terminated: - print("---------------------------------------------------------") - print("Iteration ", simulation_iteration, " terminated!") - print("---------------------------------------------------------") + self.task_deadlines[task_to_run.id].append(True) + + _ = next_component.task_queue.pop(0) + next_component.remaining_budget -= CLOCK_TICK + + if t != 0 and t % hyperperiod == 0: + print(f"\nIteration {simulation_iteration} completed!") + print(f"Summary:") + print(f"- Total time steps: {t}") + print(f"- Hyperperiod: {hyperperiod}") + print("-" * 50) simulation_iteration += 1 t = 0 - self._generate_execution_time() - self._fill_component_task_queues() + self._clear_component_queues() else: t += CLOCK_TICK - print("---------------------------------------------------------") - print("Simulation finished. Simulation time:", t) - print("---------------------------------------------------------") + print("-" * 50) + print(f"Simulation finished. Total simulation time: {t}") + print("-" * 50) def generate_output_file(self, filename: str): """Generate a CSV output file with task simulation results. - + Args: filename: Path to the output CSV file """ task_results = self.get_task_results() - + # Create CSV file with open(filename, 'w') as f: # Write header f.write("Task,Component,Task Schedulable,Avg Response Time,Max Response Time,Component Schedulable\n") - + # Write data for each task for result in task_results: f.write(f"{result.task_name},{result.component_id},{result.task_schedulable}," f"{result.avg_response_time:.2f},{result.max_response_time:.2f}," f"{result.component_schedulable}\n") - - def _fill_component_task_queues(self): - for component in self.components: - # Get tasks assigned to this component - tasks_for_component = [ - task for task in self.tasks if task.component_id == component.id - ] - - if component.scheduler == "RM": - tasks_for_component.sort(key=lambda t: t.priority) - else: - tasks_for_component.sort(key=lambda t: t.period) - - component.task_queue = tasks_for_component def _adjust_task_wcet(self): """ @@ -164,18 +146,29 @@ def _adjust_task_wcet(self): if not core: continue - for task in component.task_queue: + for task in self.tasks: + if task.component_id != component.id: + continue + + # Adjust WCET based on the speed factor of the core task.wcet = task.wcet / core.speed_factor task.remaining_time = task.wcet - def _generate_execution_time(self): + def _clear_component_queues(self): + """ + Clear the task queues of all components. + """ + for component in self.components: + component.task_queue.clear() + component.remaining_budget = component.budget + + def _generate_execution_time(self, task:Task): """ Generate execution time for each task based on its WCET and the speed factor of the core. """ - for task in self.tasks: - # Avionics (DO-178C): Typically ≥80% to ensure strict deadline guarantees. - lower_bound = task.wcet * LOWER_BOUND_PERCENTAGE - task.remaining_time = self._generate_normal_exec_time(lower_bound, task.wcet) + # Avionics (DO-178C): Typically ≥80% to ensure strict deadline guarantees. + lower_bound = task.wcet * LOWER_BOUND_PERCENTAGE + return self._generate_normal_exec_time(lower_bound, task.wcet) def _generate_normal_exec_time(self,lower_bound, wcet): """ @@ -203,7 +196,7 @@ def _generate_normal_exec_time(self,lower_bound, wcet): Notes ----- - - Using (wcet - lower_bound)/6 as std dev ensures most values (~99.7%) + - Using (wcet - lower_bound)/6 as std dev ensures most values (~99.7%) fall within the desired range, minimizing rejections in the while loop - The distribution is symmetrical around the mean, creating a bell curve between lower_bound and wcet @@ -213,27 +206,60 @@ def _generate_normal_exec_time(self,lower_bound, wcet): while True: # Ensure value stays within bounds exec_time = np.random.normal(mean, std_dev) if lower_bound <= exec_time <= wcet: - return exec_time + return exec_time def _is_core_empty(self, core_id): """ Check if all components assigned to the specified core have empty task queues. - + Args: core_id: The ID of the core to check - + Returns: bool: True if no tasks remain for any component on this core, False otherwise """ return all( len(component.task_queue) == 0 - for component in self.components + for component in self.components if component.core_id == core_id ) + def _schedule(self, current_time: int, component: Component, task: Task): + """ + Schedule the tasks for a given component based on its scheduling policy. + First removes any existing instance of the task from queue before scheduling new instance. + + Args: + current_time: Current simulation time + component: The component whose tasks are to be scheduled + task: Task to be scheduled + """ + # Remove existing instance of task if present + component.task_queue = [t for t in component.task_queue if t.id != task.id] + + insert_idx = 0 + match component.scheduler: + case Scheduler.RM: + # Rate Monotonic: Insert based on priority (lower value = higher priority) + for idx, queue_task in enumerate(component.task_queue): + if task.priority > queue_task.priority: + insert_idx = idx + 1 + case Scheduler.EDF: + deadline_new = self._get_task_absolute_deadline(current_time, task) + + insert_idx = 0 + for idx, queue_task in enumerate(component.task_queue): + deadline_queued = self._get_task_absolute_deadline(current_time, queue_task) + if deadline_new > deadline_queued: + insert_idx = idx + 1 + case _: + raise ValueError(f"Unknown scheduling policy: {component.scheduler}") + + component.task_queue.insert(insert_idx, task) + def get_task_results(self) -> list[TaskResult]: """Get the simulation results for all tasks. - + Returns: List[TaskResult]: Results for each task including response times and schedulability. """ @@ -241,7 +267,7 @@ def get_task_results(self) -> list[TaskResult]: for task in self.tasks: response_times = self.task_response_times.get(task.id, []) deadline_flags = self.task_deadlines.get(task.id, []) - + if response_times: avg_response = sum(response_times) / len(response_times) max_response = max(response_times) @@ -251,17 +277,17 @@ def get_task_results(self) -> list[TaskResult]: avg_response = 0.0 max_response = 0.0 task_schedulable = False - + # Get component for this task component = next(c for c in self.components if c.id == task.component_id) - + # A component is schedulable if all its tasks are schedulable component_tasks = [t for t in self.tasks if t.component_id == component.id] component_schedulable = all( - all(self.task_deadlines.get(t.id, [])) + all(self.task_deadlines.get(t.id, [])) for t in component_tasks ) - + results.append(TaskResult( task_name=task.id, component_id=task.component_id, @@ -270,27 +296,80 @@ def get_task_results(self) -> list[TaskResult]: max_response_time=max_response, component_schedulable=component_schedulable )) - + return results + def _get_hyperperiod(self): + """Calculate the system hyperperiod hierarchically. + First calculates the hyperperiod (LCM) of tasks within each component, + then calculates the LCM of all component hyperperiods. + + Example: + Component1 has tasks with periods [2,3] -> LCM = 6 + Component2 has tasks with periods [2,4] -> LCM = 4 + System hyperperiod = LCM(6,4) = 12 + + Returns: + int: The hyperperiod value for the entire system + """ + # Calculate hyperperiod for each component + component_hyperperiods = [] + for component in self.components: + # Get all tasks for this component + component_tasks = [task for task in self.tasks if task.component_id == component.id] + if not component_tasks: + # If component has no tasks, use its own period + component_hyperperiods.append(component.period) + continue + + # Calculate LCM of task periods within this component + task_periods = [task.period for task in component_tasks] + component_lcm = task_periods[0] + for period in task_periods[1:]: + component_lcm = (component_lcm * period) // np.gcd(component_lcm, period) + component_hyperperiods.append(component_lcm) + + # Calculate system hyperperiod as LCM of component hyperperiods + system_hyperperiod = component_hyperperiods[0] + for period in component_hyperperiods[1:]: + system_hyperperiod = (system_hyperperiod * period) // np.gcd(system_hyperperiod, period) + + return system_hyperperiod + + def _get_task_absolute_deadline(self, t: int, task: Task) -> float: + """Calculate the absolute deadline for a task at current time. + Uses remaining execution time as a tie-breaker (shorter remaining time gets priority). + + Args: + t: Current simulation time + task: Task to calculate deadline for + + Returns: + float: Absolute deadline for the task plus a small factor for remaining time + """ + absolute_deadline = task.release_time + task.period + tie_breaker = task.remaining_time * 0.0001 # Small factor to break ties + return absolute_deadline + tie_breaker + def main(): # Base path for test case files - base_path = 'data/testcases/7-unschedulable-test-case' - + base_path = 'data/custom/16-large-onecore' + # base_path = 'data/testcases/4-large-test-case' + # Read architectures architecture_path = f'{base_path}/architecture.csv' cores = read_cores(architecture_path) print("Architectures:") for core in cores: print(core) - + # Read tasks tasks_path = f'{base_path}/tasks.csv' tasks = read_tasks(tasks_path) print("\nTasks:") for task in tasks: print(task) - + # Read components budgets_path = f'{base_path}/budgets.csv' components = read_budgets(budgets_path) diff --git a/tests/test_simulator.py b/tests/test_simulator.py index 5e986d5..9119514 100644 --- a/tests/test_simulator.py +++ b/tests/test_simulator.py @@ -22,10 +22,6 @@ def test_simulator_tiny_case(): # These values should match your tiny test case expected outcomes assert len(results) > 0 # Example assertions (adjust according to your actual test data): - assert results[0].component_schedulable == True - assert results[0].avg_response_time == int(14 / 0.62 + 1) - - assert results[1].component_schedulable == True - assert results[1].avg_response_time == int(33 / 0.62 + 1) + \ No newline at end of file From 3ec1987c7c8c2819b7cb9c372f723f67caf727dd Mon Sep 17 00:00:00 2001 From: MarlonBando Date: Fri, 9 May 2025 20:22:47 +0200 Subject: [PATCH 6/9] introduced jobs --- output.csv | 36 +++++++++---- src/common/component.py | 2 +- src/common/job.py | 23 ++++++++ src/simulator.py | 114 +++++++++++++++++++++++----------------- 4 files changed, 116 insertions(+), 59 deletions(-) create mode 100644 src/common/job.py diff --git a/output.csv b/output.csv index cb48912..a65e049 100644 --- a/output.csv +++ b/output.csv @@ -1,11 +1,29 @@ Task,Component,Task Schedulable,Avg Response Time,Max Response Time,Component Schedulable -Task_12,Lidar_Sensor,False,57.38,96.00,False -Task_13,Lidar_Sensor,True,41.48,51.00,False -Task_14,Lidar_Sensor,False,112.00,112.00,False +Task_0,Camera_Sensor,True,24.61,42.00,True +Task_1,Camera_Sensor,True,57.58,68.00,True +Task_2,Camera_Sensor,True,10.46,20.00,True +Task_3,Camera_Sensor,True,6.75,14.00,True +Task_4,Camera_Sensor,True,9.33,14.00,True +Task_5,Image_Processor,True,7.96,14.00,True +Task_6,Image_Processor,True,26.19,38.00,True +Task_7,Image_Processor,True,11.81,29.00,True +Task_8,Bitmap_Processor,True,165.67,178.00,True +Task_9,Bitmap_Processor,True,104.17,246.00,True +Task_10,Bitmap_Processor,True,32.81,38.00,True +Task_11,Bitmap_Processor,True,228.00,598.00,True +Task_12,Lidar_Sensor,False,50.63,96.00,False +Task_13,Lidar_Sensor,True,40.60,51.00,False +Task_14,Lidar_Sensor,False,195.00,247.00,False Task_15,Lidar_Sensor,False,0.00,0.00,False -Task_16,Control_Unit,True,8.00,28.00,True -Task_17,Control_Unit,True,14.89,24.00,True -Task_18,Control_Unit,True,8.67,9.00,True -Task_19,Control_Unit,True,27.53,41.00,True -Task_20,Control_Unit,True,165.75,176.00,True -Task_21,Control_Unit,True,7.32,8.00,True +Task_16,Control_Unit,True,7.94,28.00,True +Task_17,Control_Unit,True,15.24,24.00,True +Task_18,Control_Unit,True,8.78,10.00,True +Task_19,Control_Unit,True,31.75,52.00,True +Task_20,Control_Unit,True,206.17,227.00,True +Task_21,Control_Unit,True,7.49,9.00,True +Task_22,GPS_Sensor,True,28.82,34.00,True +Task_23,GPS_Sensor,True,16.33,22.00,True +Task_24,Communication_Unit,True,9.00,9.00,True +Task_25,Communication_Unit,True,37.33,50.00,True +Task_26,Communication_Unit,True,209.00,209.00,True +Task_27,Communication_Unit,True,8.80,9.00,True diff --git a/src/common/component.py b/src/common/component.py index 74902d1..082fd66 100644 --- a/src/common/component.py +++ b/src/common/component.py @@ -24,4 +24,4 @@ def __init__(self, component_id: str, scheduler: Scheduler, budget: int, self.core_id = core_id self.priority = priority self.remaining_budget = budget - self.task_queue = [] + self.jobs_queue = [] diff --git a/src/common/job.py b/src/common/job.py new file mode 100644 index 0000000..41cf8a3 --- /dev/null +++ b/src/common/job.py @@ -0,0 +1,23 @@ +class Job: + def __init__(self, task, release_time: int, execution_time: float): + self.id = f"{task.id}_{release_time}" + self.task_id = task.id + self.priority = task.priority + self.release_time: int = release_time + self.execution_time: float = execution_time + self.remaining_time: float = execution_time + self.absolute_deadline: int = release_time + task.period + self.start_time: int = -1 + + # Make it sortable for priority queues if needed (e.g., for EDF) + def __lt__(self, other): + # Example for EDF: earlier deadline is higher priority + if self.absolute_deadline != other.absolute_deadline: + return self.absolute_deadline < other.absolute_deadline + # Tie-breaking (e.g., by original task priority if RM, or by release time) + # For pure EDF, could be arbitrary or based on task ID for determinism + return self.task_template.priority < other.task_template.priority # Assuming lower prio number is higher + + def __repr__(self): + return (f"Job(id={self.id}, task={self.task_template.id}, release={self.release_time}, " + f"deadline={self.absolute_deadline}, rem={self.remaining_time:.2f})") \ No newline at end of file diff --git a/src/simulator.py b/src/simulator.py index 056f70e..906ac88 100644 --- a/src/simulator.py +++ b/src/simulator.py @@ -8,10 +8,11 @@ from common.scheduler import Scheduler from common.core import Core from common.task import Task +from common.job import Job from common.csvoutput import TaskResult CLOCK_TICK = 1 -SIMULATION_ITERATIONS = 1 +SIMULATION_ITERATIONS = 10 LOWER_BOUND_PERCENTAGE = 1 class Simulator: @@ -52,13 +53,7 @@ def run(self): # --- Phase 1 Release tasks --- for task in self.tasks: if t % task.period == 0: - if t != 0 and task.remaining_time > 0: - self.task_deadlines[task.id].append(False) - - task.remaining_time = self._generate_execution_time(task) - task.release_time = t - component = next(c for c in self.components if c.id == task.component_id) - self._schedule(t, component, task) + self.release_task(t, task) # --- Phase 2: Reset budgets --- for component in self.components: @@ -69,7 +64,7 @@ def run(self): for core in self.cores: eligible_components = [ c for c in self.components - if c.core_id == core.id and c.remaining_budget > 0 and len(c.task_queue) > 0 + if c.core_id == core.id and c.remaining_budget > 0 and len(c.jobs_queue) > 0 ] if not eligible_components: @@ -78,28 +73,25 @@ def run(self): if core.scheduler == Scheduler.EDF: next_component = min( eligible_components, - key=lambda c: self._get_task_absolute_deadline(t, c.task_queue[0]) + key=lambda c: c.jobs_queue[0].absolute_deadline ) elif core.scheduler == Scheduler.RM: next_component = min(eligible_components, key=lambda c: c.priority) else: next_component = None - task_to_run = next_component.task_queue[0] - - if task_to_run.remaining_time == task_to_run.wcet: - self.task_start_times[task_to_run.id] = t + job_to_run = next_component.jobs_queue[0] - task_to_run.remaining_time -= CLOCK_TICK - if task_to_run.remaining_time <= 0: - response_time = (t + CLOCK_TICK) - self.task_start_times[task_to_run.id] - if response_time == 0: - response_time = CLOCK_TICK + if job_to_run.remaining_time == job_to_run.execution_time: + job_to_run.start_time = t - self.task_response_times[task_to_run.id].append(response_time) - self.task_deadlines[task_to_run.id].append(True) - - _ = next_component.task_queue.pop(0) + job_to_run.remaining_time -= CLOCK_TICK + + if job_to_run.remaining_time <= 0: + response_time = (t + CLOCK_TICK) - job_to_run.start_time + self.task_response_times[job_to_run.task_id].append(response_time) + self.task_deadlines[job_to_run.task_id].append(t <= job_to_run.absolute_deadline) + _ = next_component.jobs_queue.pop(0) next_component.remaining_budget -= CLOCK_TICK if t != 0 and t % hyperperiod == 0: @@ -137,6 +129,26 @@ def generate_output_file(self, filename: str): f"{result.avg_response_time:.2f},{result.max_response_time:.2f}," f"{result.component_schedulable}\n") + def _release_jobs_if_due(self, current_time: int): + """ + Checks all task templates and releases new jobs if their period is met. + Args: + current_time: The current simulation time (t). + """ + for task in self.tasks: # self.tasks stores Task templates + if current_time % task.period == 0: + # 1. Get the component for this task template + component = next( + (c for c in self.components if c.id == task.component_id), + None + ) + if not component: + raise(f"Warning: Component {task.component_id} not found for task {task.id}") + + actual_execution_time = self._generate_execution_time(task) + new_job = Job(task, current_time, actual_execution_time) + self._schedule(component, new_job) # Assuming _schedule_job takes (Component, Job) + def _adjust_task_wcet(self): """ Adjust the WCET of tasks based on the speed factor of the core they are assigned to. @@ -159,7 +171,7 @@ def _clear_component_queues(self): Clear the task queues of all components. """ for component in self.components: - component.task_queue.clear() + component.jobs_queue.clear() component.remaining_budget = component.budget def _generate_execution_time(self, task:Task): @@ -219,12 +231,31 @@ def _is_core_empty(self, core_id): bool: True if no tasks remain for any component on this core, False otherwise """ return all( - len(component.task_queue) == 0 + len(component.jobs_queue) == 0 for component in self.components if component.core_id == core_id ) - def _schedule(self, current_time: int, component: Component, task: Task): + def release_task(self, t: int, task: Task): + """Releases a single task if its period is met.""" + component = next(c for c in self.components if c.id == task.component_id) + existing_job = next( + (j for j in component.jobs_queue if j.task_id == task.id), + None + ) + if existing_job: + self.task_deadlines[task.id].append( + t <= existing_job.absolute_deadline and + existing_job.remaining_time <= 0 + ) + + execution_time = self._generate_execution_time(task) + job = Job(task, t, execution_time) + job.release_time = t + # component = next(c for c in self.components if c.id == task.component_id) # Already found + self._schedule(t, component, job) + + def _schedule(self, current_time: int, component: Component, job: Job): """ Schedule the tasks for a given component based on its scheduling policy. First removes any existing instance of the task from queue before scheduling new instance. @@ -235,27 +266,27 @@ def _schedule(self, current_time: int, component: Component, task: Task): task: Task to be scheduled """ # Remove existing instance of task if present - component.task_queue = [t for t in component.task_queue if t.id != task.id] + component.jobs_queue = [j for j in component.jobs_queue if j.task_id != job.task_id] insert_idx = 0 match component.scheduler: case Scheduler.RM: # Rate Monotonic: Insert based on priority (lower value = higher priority) - for idx, queue_task in enumerate(component.task_queue): - if task.priority > queue_task.priority: + for idx, queue_job in enumerate(component.jobs_queue): + if job.priority > queue_job.priority: insert_idx = idx + 1 case Scheduler.EDF: - deadline_new = self._get_task_absolute_deadline(current_time, task) + deadline_new = job.absolute_deadline insert_idx = 0 - for idx, queue_task in enumerate(component.task_queue): - deadline_queued = self._get_task_absolute_deadline(current_time, queue_task) + for idx, queue_job in enumerate(component.jobs_queue): + deadline_queued = queue_job.absolute_deadline if deadline_new > deadline_queued: insert_idx = idx + 1 case _: raise ValueError(f"Unknown scheduling policy: {component.scheduler}") - component.task_queue.insert(insert_idx, task) + component.jobs_queue.insert(insert_idx, job) def get_task_results(self) -> list[TaskResult]: """Get the simulation results for all tasks. @@ -336,24 +367,9 @@ def _get_hyperperiod(self): return system_hyperperiod - def _get_task_absolute_deadline(self, t: int, task: Task) -> float: - """Calculate the absolute deadline for a task at current time. - Uses remaining execution time as a tie-breaker (shorter remaining time gets priority). - - Args: - t: Current simulation time - task: Task to calculate deadline for - - Returns: - float: Absolute deadline for the task plus a small factor for remaining time - """ - absolute_deadline = task.release_time + task.period - tie_breaker = task.remaining_time * 0.0001 # Small factor to break ties - return absolute_deadline + tie_breaker - def main(): # Base path for test case files - base_path = 'data/custom/16-large-onecore' + base_path = 'data/testcases/4-large-test-case' # base_path = 'data/testcases/4-large-test-case' # Read architectures From c3afa5094d49b3638185861ce8069ebf70ae194e Mon Sep 17 00:00:00 2001 From: MarlonBando Date: Fri, 9 May 2025 20:36:47 +0200 Subject: [PATCH 7/9] added tie breaking system --- src/common/job.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/job.py b/src/common/job.py index 41cf8a3..abdee7d 100644 --- a/src/common/job.py +++ b/src/common/job.py @@ -6,7 +6,7 @@ def __init__(self, task, release_time: int, execution_time: float): self.release_time: int = release_time self.execution_time: float = execution_time self.remaining_time: float = execution_time - self.absolute_deadline: int = release_time + task.period + self.absolute_deadline: int = release_time + task.period + self.execution_time * 0.0001 # Tie-breaker self.start_time: int = -1 # Make it sortable for priority queues if needed (e.g., for EDF) From 571ab59a7d6bd2689e7fcb684ef831b279fbab54 Mon Sep 17 00:00:00 2001 From: MarlonBando Date: Mon, 12 May 2025 16:43:02 +0200 Subject: [PATCH 8/9] Add new simulator output CSV files for various test cases and update simulator path - Created output files for test cases: 1-tiny-test-case, 2-small-test-case, 3-medium-test-case, 4-large-test-case, 5-huge-test-case, 6-gigantic-test-case, 7-unschedulable-test-case, 8-unschedulable-test-case, and 9-unschedulable-test-case. - Removed outdated output file: output_2core.csv. - Updated the simulator base path to point to the new test case directory: 10-unschedulable-test-case. --- data/simulator_outputs/1-tiny-test-case.csv | 3 + .../10-unschedulable-test-case.csv | 116 ++++++++++++++++++ data/simulator_outputs/2-small-test-case.csv | 10 ++ data/simulator_outputs/3-medium-test-case.csv | 19 +++ .../simulator_outputs/4-large-test-case.csv | 30 ++--- data/simulator_outputs/5-huge-test-case.csv | 62 ++++++++++ .../6-gigantic-test-case.csv | 116 ++++++++++++++++++ .../7-unschedulable-test-case.csv | 22 ++++ .../8-unschedulable-test-case.csv | 29 +++++ .../9-unschedulable-test-case.csv | 62 ++++++++++ output_2core.csv | 19 --- src/simulator.py | 3 +- 12 files changed, 455 insertions(+), 36 deletions(-) create mode 100644 data/simulator_outputs/1-tiny-test-case.csv create mode 100644 data/simulator_outputs/10-unschedulable-test-case.csv create mode 100644 data/simulator_outputs/2-small-test-case.csv create mode 100644 data/simulator_outputs/3-medium-test-case.csv rename output.csv => data/simulator_outputs/4-large-test-case.csv (50%) create mode 100644 data/simulator_outputs/5-huge-test-case.csv create mode 100644 data/simulator_outputs/6-gigantic-test-case.csv create mode 100644 data/simulator_outputs/7-unschedulable-test-case.csv create mode 100644 data/simulator_outputs/8-unschedulable-test-case.csv create mode 100644 data/simulator_outputs/9-unschedulable-test-case.csv delete mode 100644 output_2core.csv diff --git a/data/simulator_outputs/1-tiny-test-case.csv b/data/simulator_outputs/1-tiny-test-case.csv new file mode 100644 index 0000000..f4aa528 --- /dev/null +++ b/data/simulator_outputs/1-tiny-test-case.csv @@ -0,0 +1,3 @@ +Task,Component,Task Schedulable,Avg Response Time,Max Response Time,Component Schedulable +Task_0,Camera_Sensor,True,23.00,23.00,True +Task_1,Camera_Sensor,True,77.00,77.00,True diff --git a/data/simulator_outputs/10-unschedulable-test-case.csv b/data/simulator_outputs/10-unschedulable-test-case.csv new file mode 100644 index 0000000..3fa3899 --- /dev/null +++ b/data/simulator_outputs/10-unschedulable-test-case.csv @@ -0,0 +1,116 @@ +Task,Component,Task Schedulable,Avg Response Time,Max Response Time,Component Schedulable +Task_0,Camera_Sensor,True,10.33,11.00,True +Task_1,Camera_Sensor,True,5.00,6.00,True +Task_2,Image_Processor,True,7.21,12.00,True +Task_3,Image_Processor,True,1.00,1.00,True +Task_4,Bitmap_Processor,True,4.00,5.00,True +Task_5,Bitmap_Processor,True,10.71,11.00,True +Task_6,Lidar_Sensor,True,3.91,10.00,True +Task_7,Lidar_Sensor,True,34.79,49.00,True +Task_8,Lidar_Sensor,True,116.08,141.00,True +Task_9,Lidar_Sensor,True,2.33,6.00,True +Task_10,Control_Unit,True,2.27,9.00,True +Task_11,Control_Unit,True,4.56,10.00,True +Task_12,Control_Unit,True,5.60,10.00,True +Task_13,Control_Unit,True,4.46,10.00,True +Task_14,Control_Unit,True,2.58,9.00,True +Task_15,Control_Unit,True,6.69,12.00,True +Task_16,GPS_Sensor,True,9.29,15.00,True +Task_17,GPS_Sensor,True,1.00,1.00,True +Task_18,Communication_Unit,True,1.00,1.00,True +Task_19,Communication_Unit,True,1.00,1.00,True +Task_20,Communication_Unit,True,7.21,12.00,True +Task_21,Communication_Unit,True,10.38,13.00,True +Task_22,Proximity_Sensor,True,16.00,16.00,True +Task_23,Proximity_Sensor,True,1.00,1.00,True +Task_24,Proximity_Sensor,True,1.00,1.00,True +Task_25,Radar_Sensor,True,1.00,1.00,True +Task_26,Radar_Sensor,True,34.67,37.00,True +Task_27,Radar_Sensor,True,136.58,141.00,True +Task_28,Sonar_Sensor,True,14.38,22.00,True +Task_29,Sonar_Sensor,True,20.00,21.00,True +Task_30,Laser_Sensor,True,62.29,65.00,True +Task_31,Laser_Sensor,True,2.54,7.00,True +Task_32,Laser_Sensor,True,18.17,21.00,True +Task_33,Laser_Sensor,True,6.75,8.00,True +Task_34,Infrared_Sensor,False,0.00,0.00,False +Task_35,Infrared_Sensor,True,25.23,31.00,False +Task_36,Infrared_Sensor,True,2.00,2.00,False +Task_37,Ultraviolet_Sensor,True,3.83,23.00,False +Task_38,Ultraviolet_Sensor,True,51.67,99.00,False +Task_39,Ultraviolet_Sensor,True,15.00,39.00,False +Task_40,Ultraviolet_Sensor,True,7.00,9.00,False +Task_41,Ultraviolet_Sensor,False,135.00,191.00,False +Task_42,Ultraviolet_Sensor,False,0.00,0.00,False +Task_43,Ultraviolet_Sensor,True,4.00,9.00,False +Task_44,Ultraviolet_Sensor,True,35.75,51.00,False +Task_45,Thermal_Sensor,False,0.00,0.00,False +Task_46,Thermal_Sensor,True,3.00,3.00,False +Task_47,Thermal_Sensor,False,11.00,11.00,False +Task_48,Thermal_Sensor,False,0.00,0.00,False +Task_49,Pressure_Sensor,True,8.88,10.00,False +Task_50,Pressure_Sensor,True,22.17,25.00,False +Task_51,Pressure_Sensor,False,0.00,0.00,False +Task_52,Humidity_Sensor,True,2.32,3.00,False +Task_53,Humidity_Sensor,False,5.00,5.00,False +Task_54,Temperature_Sensor,True,30.71,47.00,True +Task_55,Temperature_Sensor,True,1.00,1.00,True +Task_56,Temperature_Sensor,True,240.25,247.00,True +Task_57,Temperature_Sensor,True,7.47,18.00,True +Task_58,Light_Sensor,True,1.00,1.00,True +Task_59,Light_Sensor,True,22.42,32.00,True +Task_60,Light_Sensor,True,221.75,231.00,True +Task_61,Sound_Sensor,True,42.88,54.00,True +Task_62,Sound_Sensor,True,25.88,32.00,True +Task_63,Vibration_Sensor,True,7.50,9.00,True +Task_64,Vibration_Sensor,True,9.00,9.00,True +Task_65,Motion_Sensor,True,10.58,48.00,True +Task_66,Motion_Sensor,True,147.00,173.00,True +Task_67,Acceleration_Sensor,True,1.00,1.00,True +Task_68,Acceleration_Sensor,True,3.69,14.00,True +Task_69,Acceleration_Sensor,True,21.25,35.00,True +Task_70,Acceleration_Sensor,True,241.25,253.00,True +Task_71,Gyroscope_Sensor,True,4.62,10.00,True +Task_72,Gyroscope_Sensor,True,1.00,1.00,True +Task_73,Gyroscope_Sensor,True,1.00,1.00,True +Task_74,Gyroscope_Sensor,True,34.12,38.00,True +Task_75,Gyroscope_Sensor,True,21.25,27.00,True +Task_76,Gyroscope_Sensor,True,1.00,1.00,True +Task_77,Magnetometer_Sensor,True,1.00,1.00,True +Task_78,Magnetometer_Sensor,True,17.88,23.00,True +Task_79,Compass_Sensor,True,2.21,12.00,True +Task_80,Compass_Sensor,True,6.12,15.00,True +Task_81,Compass_Sensor,True,2.93,12.00,True +Task_82,Compass_Sensor,True,50.00,51.00,True +Task_83,Altimeter_Sensor,True,9.48,12.00,False +Task_84,Altimeter_Sensor,True,30.17,48.00,False +Task_85,Altimeter_Sensor,False,0.00,0.00,False +Task_86,Barometer_Sensor,True,10.33,15.00,True +Task_87,Barometer_Sensor,True,1.00,1.00,True +Task_88,Barometer_Sensor,True,56.50,63.00,True +Task_89,Hygrometer_Sensor,True,1.00,1.00,True +Task_90,Hygrometer_Sensor,True,92.81,107.00,True +Task_91,Anemometer_Sensor,False,0.00,0.00,False +Task_92,Anemometer_Sensor,False,21.00,21.00,False +Task_93,Anemometer_Sensor,True,3.00,3.00,False +Task_94,Anemometer_Sensor,False,0.00,0.00,False +Task_95,Rain_Gauge_Sensor,True,5.00,5.00,True +Task_96,Rain_Gauge_Sensor,True,17.00,17.00,True +Task_97,Rain_Gauge_Sensor,True,65.00,65.00,True +Task_98,Snow_Gauge_Sensor,True,7.69,12.00,False +Task_99,Snow_Gauge_Sensor,True,4.62,20.00,False +Task_100,Snow_Gauge_Sensor,True,98.88,140.00,False +Task_101,Snow_Gauge_Sensor,False,329.33,404.00,False +Task_102,Snow_Gauge_Sensor,True,40.50,56.00,False +Task_103,Snow_Gauge_Sensor,True,2.48,11.00,False +Task_104,Snow_Gauge_Sensor,False,0.00,0.00,False +Task_105,Snow_Gauge_Sensor,True,106.17,144.00,False +Task_106,Thermometer_Sensor,True,6.00,8.00,True +Task_107,Thermometer_Sensor,True,2.00,2.00,True +Task_108,Thermometer_Sensor,True,81.00,81.00,True +Task_109,Thermometer_Sensor,True,10.67,13.00,True +Task_110,Pyrometer_Sensor,True,10.00,10.00,False +Task_111,Pyrometer_Sensor,False,28.00,28.00,False +Task_112,Pyrometer_Sensor,False,0.00,0.00,False +Task_113,Photometer_Sensor,True,13.08,16.00,True +Task_114,Photometer_Sensor,True,10.10,13.00,True diff --git a/data/simulator_outputs/2-small-test-case.csv b/data/simulator_outputs/2-small-test-case.csv new file mode 100644 index 0000000..c6873db --- /dev/null +++ b/data/simulator_outputs/2-small-test-case.csv @@ -0,0 +1,10 @@ +Task,Component,Task Schedulable,Avg Response Time,Max Response Time,Component Schedulable +Task_0,Camera_Sensor,True,7.75,8.00,False +Task_1,Camera_Sensor,True,104.33,120.00,False +Task_2,Camera_Sensor,True,4.92,7.00,False +Task_3,Camera_Sensor,False,199.00,205.00,False +Task_4,Image_Processor,True,6.83,12.00,True +Task_5,Image_Processor,True,62.33,78.00,True +Task_6,Image_Processor,True,163.67,256.00,True +Task_7,Image_Processor,True,91.00,158.00,True +Task_8,Image_Processor,True,10.88,16.00,True diff --git a/data/simulator_outputs/3-medium-test-case.csv b/data/simulator_outputs/3-medium-test-case.csv new file mode 100644 index 0000000..c71f134 --- /dev/null +++ b/data/simulator_outputs/3-medium-test-case.csv @@ -0,0 +1,19 @@ +Task,Component,Task Schedulable,Avg Response Time,Max Response Time,Component Schedulable +Task_0,Camera_Sensor,True,19.44,23.00,True +Task_1,Camera_Sensor,True,11.44,13.00,True +Task_2,Camera_Sensor,True,132.67,139.00,True +Task_3,Camera_Sensor,True,9.22,12.00,True +Task_4,Camera_Sensor,True,416.00,416.00,True +Task_5,Image_Processor,True,6.50,7.00,True +Task_6,Image_Processor,True,7.00,7.00,True +Task_7,Image_Processor,True,37.50,43.00,True +Task_8,Lidar_Sensor,True,3.47,4.00,True +Task_9,Lidar_Sensor,True,45.17,62.00,True +Task_10,Lidar_Sensor,True,10.08,15.00,True +Task_11,Lidar_Sensor,True,94.00,103.00,True +Task_12,Control_Unit,True,9.25,18.00,True +Task_13,Control_Unit,True,9.40,12.00,True +Task_14,Control_Unit,True,23.44,37.00,True +Task_15,Control_Unit,True,11.58,22.00,True +Task_16,Control_Unit,True,20.58,36.00,True +Task_17,Control_Unit,True,19.27,28.00,True diff --git a/output.csv b/data/simulator_outputs/4-large-test-case.csv similarity index 50% rename from output.csv rename to data/simulator_outputs/4-large-test-case.csv index a65e049..c7f07e0 100644 --- a/output.csv +++ b/data/simulator_outputs/4-large-test-case.csv @@ -1,26 +1,26 @@ Task,Component,Task Schedulable,Avg Response Time,Max Response Time,Component Schedulable Task_0,Camera_Sensor,True,24.61,42.00,True Task_1,Camera_Sensor,True,57.58,68.00,True -Task_2,Camera_Sensor,True,10.46,20.00,True +Task_2,Camera_Sensor,True,10.43,20.00,True Task_3,Camera_Sensor,True,6.75,14.00,True Task_4,Camera_Sensor,True,9.33,14.00,True -Task_5,Image_Processor,True,7.96,14.00,True -Task_6,Image_Processor,True,26.19,38.00,True -Task_7,Image_Processor,True,11.81,29.00,True +Task_5,Image_Processor,True,7.99,14.00,True +Task_6,Image_Processor,True,26.33,38.00,True +Task_7,Image_Processor,True,11.83,29.00,True Task_8,Bitmap_Processor,True,165.67,178.00,True -Task_9,Bitmap_Processor,True,104.17,246.00,True -Task_10,Bitmap_Processor,True,32.81,38.00,True -Task_11,Bitmap_Processor,True,228.00,598.00,True -Task_12,Lidar_Sensor,False,50.63,96.00,False -Task_13,Lidar_Sensor,True,40.60,51.00,False -Task_14,Lidar_Sensor,False,195.00,247.00,False +Task_9,Bitmap_Processor,True,108.42,287.00,True +Task_10,Bitmap_Processor,True,32.91,38.00,True +Task_11,Bitmap_Processor,True,163.00,330.00,True +Task_12,Lidar_Sensor,False,54.60,96.00,False +Task_13,Lidar_Sensor,True,41.65,51.00,False +Task_14,Lidar_Sensor,False,153.25,208.00,False Task_15,Lidar_Sensor,False,0.00,0.00,False -Task_16,Control_Unit,True,7.94,28.00,True -Task_17,Control_Unit,True,15.24,24.00,True -Task_18,Control_Unit,True,8.78,10.00,True +Task_16,Control_Unit,True,8.00,28.00,True +Task_17,Control_Unit,True,14.96,24.00,True +Task_18,Control_Unit,True,8.67,9.00,True Task_19,Control_Unit,True,31.75,52.00,True -Task_20,Control_Unit,True,206.17,227.00,True -Task_21,Control_Unit,True,7.49,9.00,True +Task_20,Control_Unit,True,206.33,227.00,True +Task_21,Control_Unit,True,7.15,8.00,True Task_22,GPS_Sensor,True,28.82,34.00,True Task_23,GPS_Sensor,True,16.33,22.00,True Task_24,Communication_Unit,True,9.00,9.00,True diff --git a/data/simulator_outputs/5-huge-test-case.csv b/data/simulator_outputs/5-huge-test-case.csv new file mode 100644 index 0000000..8399ea5 --- /dev/null +++ b/data/simulator_outputs/5-huge-test-case.csv @@ -0,0 +1,62 @@ +Task,Component,Task Schedulable,Avg Response Time,Max Response Time,Component Schedulable +Task_0,Camera_Sensor,True,6.24,7.00,True +Task_1,Camera_Sensor,True,83.53,85.00,True +Task_2,Image_Processor,True,27.61,30.00,True +Task_3,Image_Processor,True,50.00,52.00,True +Task_4,Bitmap_Processor,True,33.33,39.00,True +Task_5,Bitmap_Processor,True,219.00,221.00,True +Task_6,Lidar_Sensor,True,106.00,106.00,True +Task_7,Lidar_Sensor,True,4.00,4.00,True +Task_8,Lidar_Sensor,True,3.81,4.00,True +Task_9,Lidar_Sensor,True,223.00,223.00,True +Task_10,Control_Unit,True,3.56,12.00,True +Task_11,Control_Unit,True,28.79,70.00,True +Task_12,Control_Unit,True,19.88,40.00,True +Task_13,Control_Unit,True,5.22,14.00,True +Task_14,Control_Unit,True,61.01,128.00,True +Task_15,Control_Unit,True,23.79,28.00,True +Task_16,GPS_Sensor,True,21.00,21.00,True +Task_17,GPS_Sensor,True,61.00,61.00,True +Task_18,Communication_Unit,True,11.00,13.00,True +Task_19,Communication_Unit,True,6.16,8.00,True +Task_20,Communication_Unit,True,107.60,120.00,True +Task_21,Communication_Unit,True,2.21,5.00,True +Task_22,Proximity_Sensor,True,8.30,11.00,True +Task_23,Proximity_Sensor,True,24.22,28.00,True +Task_24,Proximity_Sensor,True,5.98,10.00,True +Task_25,Radar_Sensor,True,4.07,10.00,True +Task_26,Radar_Sensor,True,56.33,60.00,True +Task_27,Radar_Sensor,True,215.80,224.00,True +Task_28,Sonar_Sensor,True,6.75,9.00,True +Task_29,Sonar_Sensor,True,32.48,33.00,True +Task_30,Laser_Sensor,True,2.33,3.00,True +Task_31,Laser_Sensor,True,34.67,37.00,True +Task_32,Laser_Sensor,True,245.00,245.00,True +Task_33,Laser_Sensor,True,5.67,9.00,True +Task_34,Infrared_Sensor,True,6.00,7.00,True +Task_35,Infrared_Sensor,True,24.83,26.00,True +Task_36,Infrared_Sensor,True,46.00,52.00,True +Task_37,Ultraviolet_Sensor,True,10.37,21.00,True +Task_38,Ultraviolet_Sensor,True,2.01,3.00,True +Task_39,Ultraviolet_Sensor,True,30.97,53.00,True +Task_40,Ultraviolet_Sensor,True,56.13,73.00,True +Task_41,Ultraviolet_Sensor,True,15.67,16.00,True +Task_42,Ultraviolet_Sensor,True,614.40,763.00,True +Task_43,Ultraviolet_Sensor,True,193.90,241.00,True +Task_44,Ultraviolet_Sensor,True,32.51,54.00,True +Task_45,Thermal_Sensor,True,9.42,24.00,True +Task_46,Thermal_Sensor,True,5.56,8.00,True +Task_47,Thermal_Sensor,True,76.47,99.00,True +Task_48,Thermal_Sensor,True,25.78,36.00,True +Task_49,Pressure_Sensor,True,5.92,8.00,True +Task_50,Pressure_Sensor,True,1.00,1.00,True +Task_51,Pressure_Sensor,True,17.02,29.00,True +Task_52,Humidity_Sensor,True,73.36,76.00,True +Task_53,Humidity_Sensor,True,219.16,223.00,True +Task_54,Temperature_Sensor,True,4.97,8.00,True +Task_55,Temperature_Sensor,True,48.40,59.00,True +Task_56,Temperature_Sensor,True,158.06,199.00,True +Task_57,Temperature_Sensor,True,5.97,12.00,True +Task_58,Light_Sensor,True,24.42,32.00,True +Task_59,Light_Sensor,True,2.45,12.00,True +Task_60,Light_Sensor,True,56.98,98.00,True diff --git a/data/simulator_outputs/6-gigantic-test-case.csv b/data/simulator_outputs/6-gigantic-test-case.csv new file mode 100644 index 0000000..afd493c --- /dev/null +++ b/data/simulator_outputs/6-gigantic-test-case.csv @@ -0,0 +1,116 @@ +Task,Component,Task Schedulable,Avg Response Time,Max Response Time,Component Schedulable +Task_0,Camera_Sensor,True,10.36,11.00,True +Task_1,Camera_Sensor,True,5.00,6.00,True +Task_2,Image_Processor,True,7.26,12.00,True +Task_3,Image_Processor,True,1.00,1.00,True +Task_4,Bitmap_Processor,True,4.00,5.00,True +Task_5,Bitmap_Processor,True,10.72,11.00,True +Task_6,Lidar_Sensor,True,3.69,10.00,True +Task_7,Lidar_Sensor,True,21.26,28.00,True +Task_8,Lidar_Sensor,True,56.26,59.00,True +Task_9,Lidar_Sensor,True,2.37,9.00,True +Task_10,Control_Unit,True,2.27,9.00,True +Task_11,Control_Unit,True,4.80,10.00,True +Task_12,Control_Unit,True,5.55,10.00,True +Task_13,Control_Unit,True,4.26,10.00,True +Task_14,Control_Unit,True,2.62,9.00,True +Task_15,Control_Unit,True,7.14,12.00,True +Task_16,GPS_Sensor,True,48.07,53.00,True +Task_17,GPS_Sensor,True,1.00,1.00,True +Task_18,Communication_Unit,True,1.00,1.00,True +Task_19,Communication_Unit,True,1.00,1.00,True +Task_20,Communication_Unit,True,6.66,12.00,True +Task_21,Communication_Unit,True,11.10,15.00,True +Task_22,Proximity_Sensor,True,16.00,16.00,True +Task_23,Proximity_Sensor,True,1.00,1.00,True +Task_24,Proximity_Sensor,True,1.00,1.00,True +Task_25,Radar_Sensor,True,2.00,2.00,True +Task_26,Radar_Sensor,True,32.02,41.00,True +Task_27,Radar_Sensor,True,128.71,137.00,True +Task_28,Sonar_Sensor,True,18.25,34.00,True +Task_29,Sonar_Sensor,True,54.19,76.00,True +Task_30,Laser_Sensor,True,61.67,65.00,True +Task_31,Laser_Sensor,True,2.50,7.00,True +Task_32,Laser_Sensor,True,19.03,28.00,True +Task_33,Laser_Sensor,True,6.33,8.00,True +Task_34,Infrared_Sensor,True,153.67,159.00,True +Task_35,Infrared_Sensor,True,24.69,31.00,True +Task_36,Infrared_Sensor,True,2.16,5.00,True +Task_37,Ultraviolet_Sensor,True,7.80,12.00,True +Task_38,Ultraviolet_Sensor,True,25.33,45.00,True +Task_39,Ultraviolet_Sensor,True,21.25,37.00,True +Task_40,Ultraviolet_Sensor,True,3.81,9.00,True +Task_41,Ultraviolet_Sensor,True,115.20,145.00,True +Task_42,Ultraviolet_Sensor,True,525.00,575.00,True +Task_43,Ultraviolet_Sensor,True,9.80,12.00,True +Task_44,Ultraviolet_Sensor,True,26.00,49.00,True +Task_45,Thermal_Sensor,False,297.50,371.00,False +Task_46,Thermal_Sensor,True,5.00,5.00,False +Task_47,Thermal_Sensor,False,77.47,118.00,False +Task_48,Thermal_Sensor,False,23.67,31.00,False +Task_49,Pressure_Sensor,True,16.17,19.00,True +Task_50,Pressure_Sensor,True,17.00,19.00,True +Task_51,Pressure_Sensor,True,311.50,331.00,True +Task_52,Humidity_Sensor,True,3.67,4.00,True +Task_53,Humidity_Sensor,True,10.00,10.00,True +Task_54,Temperature_Sensor,True,29.73,49.00,True +Task_55,Temperature_Sensor,True,2.29,11.00,True +Task_56,Temperature_Sensor,True,526.95,532.00,True +Task_57,Temperature_Sensor,True,7.64,18.00,True +Task_58,Light_Sensor,True,2.56,19.00,True +Task_59,Light_Sensor,True,54.43,70.00,True +Task_60,Light_Sensor,True,475.68,485.00,True +Task_61,Sound_Sensor,True,134.39,149.00,True +Task_62,Sound_Sensor,True,41.03,61.00,True +Task_63,Vibration_Sensor,True,30.25,33.00,True +Task_64,Vibration_Sensor,True,24.33,25.00,True +Task_65,Motion_Sensor,True,19.00,19.00,True +Task_66,Motion_Sensor,True,141.24,166.00,True +Task_67,Acceleration_Sensor,True,3.15,13.00,True +Task_68,Acceleration_Sensor,True,4.42,14.00,True +Task_69,Acceleration_Sensor,True,28.26,34.00,True +Task_70,Acceleration_Sensor,True,529.28,543.00,True +Task_71,Gyroscope_Sensor,True,5.03,10.00,True +Task_72,Gyroscope_Sensor,True,1.00,1.00,True +Task_73,Gyroscope_Sensor,True,1.00,1.00,True +Task_74,Gyroscope_Sensor,True,35.33,46.00,True +Task_75,Gyroscope_Sensor,True,21.35,27.00,True +Task_76,Gyroscope_Sensor,True,1.00,1.00,True +Task_77,Magnetometer_Sensor,True,1.00,1.00,True +Task_78,Magnetometer_Sensor,True,17.76,23.00,True +Task_79,Compass_Sensor,True,61.89,74.00,True +Task_80,Compass_Sensor,True,26.00,27.00,True +Task_81,Compass_Sensor,True,26.00,26.00,True +Task_82,Compass_Sensor,True,373.67,374.00,True +Task_83,Altimeter_Sensor,True,78.75,82.00,True +Task_84,Altimeter_Sensor,True,105.33,136.00,True +Task_85,Altimeter_Sensor,True,27.33,28.00,True +Task_86,Barometer_Sensor,True,20.16,25.00,True +Task_87,Barometer_Sensor,True,5.08,7.00,True +Task_88,Barometer_Sensor,True,45.50,46.00,True +Task_89,Hygrometer_Sensor,True,1.00,1.00,True +Task_90,Hygrometer_Sensor,True,95.50,109.00,True +Task_91,Anemometer_Sensor,True,11.00,11.00,False +Task_92,Anemometer_Sensor,True,30.08,31.00,False +Task_93,Anemometer_Sensor,True,5.00,5.00,False +Task_94,Anemometer_Sensor,False,0.00,0.00,False +Task_95,Rain_Gauge_Sensor,True,4.04,5.00,True +Task_96,Rain_Gauge_Sensor,True,17.00,17.00,True +Task_97,Rain_Gauge_Sensor,True,65.00,65.00,True +Task_98,Snow_Gauge_Sensor,True,15.98,25.00,True +Task_99,Snow_Gauge_Sensor,True,79.86,111.00,True +Task_100,Snow_Gauge_Sensor,True,63.69,117.00,True +Task_101,Snow_Gauge_Sensor,True,265.73,379.00,True +Task_102,Snow_Gauge_Sensor,True,20.62,45.00,True +Task_103,Snow_Gauge_Sensor,True,11.08,15.00,True +Task_104,Snow_Gauge_Sensor,True,58.20,92.00,True +Task_105,Snow_Gauge_Sensor,True,54.04,105.00,True +Task_106,Thermometer_Sensor,True,6.00,8.00,True +Task_107,Thermometer_Sensor,True,2.00,2.00,True +Task_108,Thermometer_Sensor,True,81.00,81.00,True +Task_109,Thermometer_Sensor,True,10.67,13.00,True +Task_110,Pyrometer_Sensor,True,17.36,19.00,True +Task_111,Pyrometer_Sensor,True,145.07,204.00,True +Task_112,Pyrometer_Sensor,True,30.40,61.00,True +Task_113,Photometer_Sensor,True,54.53,60.00,True +Task_114,Photometer_Sensor,True,20.94,27.00,True diff --git a/data/simulator_outputs/7-unschedulable-test-case.csv b/data/simulator_outputs/7-unschedulable-test-case.csv new file mode 100644 index 0000000..5b6ce99 --- /dev/null +++ b/data/simulator_outputs/7-unschedulable-test-case.csv @@ -0,0 +1,22 @@ +Task,Component,Task Schedulable,Avg Response Time,Max Response Time,Component Schedulable +Task_0,Camera_Sensor,True,44.00,49.00,True +Task_1,Camera_Sensor,True,1.00,1.00,True +Task_2,Camera_Sensor,True,5.00,5.00,True +Task_3,Camera_Sensor,True,211.00,211.00,True +Task_4,Image_Processor,True,1.00,1.00,True +Task_5,Image_Processor,True,2.00,2.00,True +Task_6,Lidar_Sensor,False,24.10,26.00,False +Task_7,Lidar_Sensor,False,2.00,2.00,False +Task_8,Lidar_Sensor,False,140.00,140.00,False +Task_9,Lidar_Sensor,False,0.00,0.00,False +Task_10,Lidar_Sensor,False,0.00,0.00,False +Task_11,Lidar_Sensor,False,2.00,2.00,False +Task_12,GPS_Sensor,True,4.42,5.00,True +Task_13,GPS_Sensor,True,41.00,41.00,True +Task_14,Communication_Unit,True,3.92,8.00,True +Task_15,Communication_Unit,True,76.21,77.00,True +Task_16,Communication_Unit,True,5.42,15.00,True +Task_17,Communication_Unit,True,3.69,13.00,True +Task_18,Proximity_Sensor,True,34.00,36.00,True +Task_19,Proximity_Sensor,True,228.25,229.00,True +Task_20,Proximity_Sensor,True,1.00,1.00,True diff --git a/data/simulator_outputs/8-unschedulable-test-case.csv b/data/simulator_outputs/8-unschedulable-test-case.csv new file mode 100644 index 0000000..0c11a28 --- /dev/null +++ b/data/simulator_outputs/8-unschedulable-test-case.csv @@ -0,0 +1,29 @@ +Task,Component,Task Schedulable,Avg Response Time,Max Response Time,Component Schedulable +Task_0,Camera_Sensor,True,2.33,3.00,True +Task_1,Camera_Sensor,True,24.00,26.00,True +Task_2,Camera_Sensor,True,2.17,6.00,True +Task_3,Camera_Sensor,True,2.42,5.00,True +Task_4,Camera_Sensor,True,2.62,7.00,True +Task_5,Image_Processor,True,2.21,5.00,True +Task_6,Image_Processor,True,2.23,6.00,True +Task_7,Image_Processor,True,2.17,3.00,True +Task_8,Bitmap_Processor,True,15.08,22.00,False +Task_9,Bitmap_Processor,True,43.00,50.00,False +Task_10,Bitmap_Processor,True,6.29,8.00,False +Task_11,Bitmap_Processor,False,0.00,0.00,False +Task_12,Lidar_Sensor,False,14.20,19.00,False +Task_13,Lidar_Sensor,True,4.00,4.00,False +Task_14,Lidar_Sensor,False,0.00,0.00,False +Task_15,Lidar_Sensor,False,0.00,0.00,False +Task_16,Control_Unit,True,2.67,3.00,False +Task_17,Control_Unit,True,2.33,3.00,False +Task_18,Control_Unit,True,5.00,6.00,False +Task_19,Control_Unit,True,26.67,27.00,False +Task_20,Control_Unit,False,0.00,0.00,False +Task_21,Control_Unit,True,4.00,4.00,False +Task_22,GPS_Sensor,True,28.58,33.00,True +Task_23,GPS_Sensor,True,3.83,9.00,True +Task_24,Communication_Unit,True,2.50,4.00,True +Task_25,Communication_Unit,True,41.00,41.00,True +Task_26,Communication_Unit,True,249.00,249.00,True +Task_27,Communication_Unit,True,2.00,2.00,True diff --git a/data/simulator_outputs/9-unschedulable-test-case.csv b/data/simulator_outputs/9-unschedulable-test-case.csv new file mode 100644 index 0000000..254a83d --- /dev/null +++ b/data/simulator_outputs/9-unschedulable-test-case.csv @@ -0,0 +1,62 @@ +Task,Component,Task Schedulable,Avg Response Time,Max Response Time,Component Schedulable +Task_0,Camera_Sensor,True,7.00,7.00,True +Task_1,Camera_Sensor,True,43.00,43.00,True +Task_2,Image_Processor,True,28.33,30.00,True +Task_3,Image_Processor,True,50.00,52.00,True +Task_4,Bitmap_Processor,True,33.22,37.00,True +Task_5,Bitmap_Processor,True,219.00,219.00,True +Task_6,Lidar_Sensor,True,50.50,52.00,True +Task_7,Lidar_Sensor,True,1.00,1.00,True +Task_8,Lidar_Sensor,True,3.75,4.00,True +Task_9,Lidar_Sensor,True,109.00,109.00,True +Task_10,Control_Unit,True,1.00,1.00,True +Task_11,Control_Unit,True,12.81,16.00,True +Task_12,Control_Unit,True,16.83,45.00,True +Task_13,Control_Unit,True,11.44,17.00,True +Task_14,Control_Unit,True,86.00,155.00,True +Task_15,Control_Unit,True,34.38,71.00,True +Task_16,GPS_Sensor,True,1.00,1.00,True +Task_17,GPS_Sensor,True,11.00,11.00,True +Task_18,Communication_Unit,True,1.00,1.00,True +Task_19,Communication_Unit,True,1.00,1.00,True +Task_20,Communication_Unit,True,29.67,30.00,True +Task_21,Communication_Unit,True,1.00,1.00,True +Task_22,Proximity_Sensor,True,1.00,1.00,True +Task_23,Proximity_Sensor,True,25.11,28.00,True +Task_24,Proximity_Sensor,True,2.78,9.00,True +Task_25,Radar_Sensor,True,1.00,1.00,True +Task_26,Radar_Sensor,True,58.25,66.00,True +Task_27,Radar_Sensor,True,219.92,225.00,True +Task_28,Sonar_Sensor,True,6.75,9.00,True +Task_29,Sonar_Sensor,True,32.42,33.00,True +Task_30,Laser_Sensor,True,2.33,3.00,True +Task_31,Laser_Sensor,True,34.67,37.00,True +Task_32,Laser_Sensor,True,248.00,248.00,True +Task_33,Laser_Sensor,True,5.50,8.00,True +Task_34,Infrared_Sensor,True,6.00,7.00,True +Task_35,Infrared_Sensor,True,8.17,16.00,True +Task_36,Infrared_Sensor,True,46.00,52.00,True +Task_37,Ultraviolet_Sensor,True,2.31,11.00,True +Task_38,Ultraviolet_Sensor,True,2.22,10.00,True +Task_39,Ultraviolet_Sensor,True,27.67,54.00,True +Task_40,Ultraviolet_Sensor,True,51.83,60.00,True +Task_41,Ultraviolet_Sensor,True,14.94,16.00,True +Task_42,Ultraviolet_Sensor,True,593.50,737.00,True +Task_43,Ultraviolet_Sensor,True,185.50,225.00,True +Task_44,Ultraviolet_Sensor,True,32.33,57.00,True +Task_45,Thermal_Sensor,True,9.67,22.00,True +Task_46,Thermal_Sensor,True,2.08,5.00,True +Task_47,Thermal_Sensor,True,77.72,99.00,True +Task_48,Thermal_Sensor,True,21.33,38.00,True +Task_49,Pressure_Sensor,True,1.00,1.00,True +Task_50,Pressure_Sensor,True,1.00,1.00,True +Task_51,Pressure_Sensor,True,22.00,22.00,True +Task_52,Humidity_Sensor,True,73.47,77.00,True +Task_53,Humidity_Sensor,True,109.50,115.00,True +Task_54,Temperature_Sensor,True,2.33,6.00,False +Task_55,Temperature_Sensor,True,50.72,55.00,False +Task_56,Temperature_Sensor,False,0.00,0.00,False +Task_57,Temperature_Sensor,True,2.30,6.00,False +Task_58,Light_Sensor,True,24.56,28.00,True +Task_59,Light_Sensor,True,2.68,13.00,True +Task_60,Light_Sensor,True,54.67,62.00,True diff --git a/output_2core.csv b/output_2core.csv deleted file mode 100644 index 94599fe..0000000 --- a/output_2core.csv +++ /dev/null @@ -1,19 +0,0 @@ -Task,Component,Task Schedulable,Avg Response Time,Max Response Time,Component Schedulable -Task_0,Camera_Sensor,True,19.33,22.00,True -Task_1,Camera_Sensor,True,10.44,12.00,True -Task_2,Camera_Sensor,True,132.67,139.00,True -Task_3,Camera_Sensor,True,9.11,12.00,True -Task_4,Camera_Sensor,True,416.00,416.00,True -Task_5,Image_Processor,True,6.56,7.00,True -Task_6,Image_Processor,True,6.06,7.00,True -Task_7,Image_Processor,True,42.08,54.00,True -Task_8,Lidar_Sensor,True,3.42,4.00,True -Task_9,Lidar_Sensor,True,39.56,59.00,True -Task_10,Lidar_Sensor,True,9.67,10.00,True -Task_11,Lidar_Sensor,True,72.67,104.00,True -Task_12,Control_Unit,True,11.25,30.00,False -Task_13,Control_Unit,True,9.40,12.00,False -Task_14,Control_Unit,True,33.33,67.00,False -Task_15,Control_Unit,True,11.64,20.00,False -Task_16,Control_Unit,True,15.21,34.00,False -Task_17,Control_Unit,False,51.44,88.00,False diff --git a/src/simulator.py b/src/simulator.py index 906ac88..7479248 100644 --- a/src/simulator.py +++ b/src/simulator.py @@ -369,8 +369,7 @@ def _get_hyperperiod(self): def main(): # Base path for test case files - base_path = 'data/testcases/4-large-test-case' - # base_path = 'data/testcases/4-large-test-case' + base_path = 'data/testcases/10-unschedulable-test-case' # Read architectures architecture_path = f'{base_path}/architecture.csv' From 3ae72a60600f2add32a7e87b588764856f408b28 Mon Sep 17 00:00:00 2001 From: MarlonBando Date: Mon, 12 May 2025 16:54:46 +0200 Subject: [PATCH 9/9] Refactor task and component ID references in analysis.py; add CSV reading functionality in csvreader.py --- src/analysis.py | 10 +++++----- src/common/csvreader.py | 25 ++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/analysis.py b/src/analysis.py index 564c68e..7a1a36a 100644 --- a/src/analysis.py +++ b/src/analysis.py @@ -16,8 +16,8 @@ def lcm(a: int, b: int) -> int: def adjust_wcet(tasks, budgets, architectures): # Adjust WCET by core speed factor (scaling per architecture) - core_speed_map = {arch.core_id: arch.speed_factor for arch in architectures} - component_core_map = {budget.component_id: budget.core_id for budget in budgets} + core_speed_map = {arch.id: arch.speed_factor for arch in architectures} + component_core_map = {budget.id: budget.core_id for budget in budgets} for task in tasks: core_id = component_core_map[task.component_id] task.wcet = task.wcet / core_speed_map[core_id] @@ -28,7 +28,7 @@ def group_tasks_by_component(tasks, budgets): # Group tasks into their components based on budgets.csv components = {} for budget in budgets: - comp_id = budget.component_id + comp_id = budget.id comp_tasks = [t for t in tasks if t.component_id == comp_id] # Sort for RM priority order if budget.scheduler == Scheduler.RM: @@ -118,7 +118,7 @@ def summarize_by_core(components, architectures): - Also ensure each component passed its local schedulability check """ # Group components per core - core_map = {arch.core_id: [] for arch in architectures} + core_map = {arch.id: [] for arch in architectures} for comp in components.values(): core_map[comp['core_id']].append(comp) @@ -173,7 +173,7 @@ def write_solution_csv(tasks, components, filename='solution.csv'): # noqa: E30 else: demand = DBF.dbf_edf(comp['tasks'], task.period) # Eq.2 rows.append({ - 'task_name': task.task_name, + 'task_name': task.id, 'component_id': cid, 'task_schedulable': int(demand <= supply.sbf(task.period)), 'component_schedulable': int(comp['schedulable']) diff --git a/src/common/csvreader.py b/src/common/csvreader.py index 3e6478b..0fecf8b 100644 --- a/src/common/csvreader.py +++ b/src/common/csvreader.py @@ -82,4 +82,27 @@ def _get_csv_path(csv:str) -> str: f"File {csv} does not exist. " "Pass to the function an absolute path or a relative path from the project root. " "For example 'data/testcases/1-tiny-test-case/architecture.csv", csv - ) \ No newline at end of file + ) + +def read_csv() -> tuple[list[Core], list[Component], list[Task]]: + if len(sys.argv) != 4: + print("Usage: python analysis.py ") + sys.exit(1) + + architecture_file = sys.argv[1] + budget_file = sys.argv[2] + tasks_file = sys.argv[3] + + try: + architectures = read_cores(architecture_file) + budgets = read_budgets(budget_file) + tasks = read_tasks(tasks_file) + + except FileNotFoundError as e: + print(f"Error: File not found - {e}") + sys.exit(1) + except Exception as e: + print(f"Error processing files: {e}") + sys.exit(1) + + return architectures, budgets, tasks \ No newline at end of file