diff --git a/.gitignore b/.gitignore index 9605ff6..afc9798 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ tests/.tmp .pytest_cache .vscode -__pycache__ \ No newline at end of file +__pycache__ +.vscode +.ruff_cache diff --git a/Exercises/Response-Time Analysis/.vscode/launch.json b/Exercises/Response-Time Analysis/.vscode/launch.json index 474e99b..0f57c54 100644 --- a/Exercises/Response-Time Analysis/.vscode/launch.json +++ b/Exercises/Response-Time Analysis/.vscode/launch.json @@ -5,7 +5,7 @@ "name": "Debug C++", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}/build/response_time_analysis", + "program": "${workspaceFolder}/build/response_time_analysis", "args": ["${workspaceFolder}/exercise-TC2.csv"], "stopAtEntry": false, "cwd": "${workspaceFolder}", @@ -22,4 +22,4 @@ "preLaunchTask": "build" } ] -} \ No newline at end of file +} diff --git a/Exercises/Response-Time Analysis/.vscode/tasks.json b/Exercises/Response-Time Analysis/.vscode/tasks.json index 956cd19..96a19f5 100644 --- a/Exercises/Response-Time Analysis/.vscode/tasks.json +++ b/Exercises/Response-Time Analysis/.vscode/tasks.json @@ -18,4 +18,4 @@ "problemMatcher": ["$gcc"] } ] -} \ No newline at end of file +} diff --git a/Exercises/Response-Time Analysis/src/response_time_analysis.cpp b/Exercises/Response-Time Analysis/src/response_time_analysis.cpp index f3e0315..5651015 100644 --- a/Exercises/Response-Time Analysis/src/response_time_analysis.cpp +++ b/Exercises/Response-Time Analysis/src/response_time_analysis.cpp @@ -11,8 +11,8 @@ struct Task { string id; int BCET; int WCET; - int period; - int deadline; + int period; + int deadline; int priority; // Lower number => higher priority for RMS, if you wish }; @@ -68,7 +68,7 @@ void RTA_test(vector& tasks) while (R != last_R) { last_R = R; - int I = 0; + int I = 0; for (size_t j = 0; j < i; j++) { I += ceil((double)R / tasks[j].period) * tasks[j].WCET; diff --git a/Exercises/Very Simple Simulator/.vscode/c_cpp_properties.json b/Exercises/Very Simple Simulator/.vscode/c_cpp_properties.json deleted file mode 100644 index 8f6a31f..0000000 --- a/Exercises/Very Simple Simulator/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "configurations": [ - { - "name": "Linux", - "includePath": [ - "${workspaceFolder}/**" - ], - "defines": [], - "compilerPath": "/usr/bin/g++", - "cStandard": "c11", - "cppStandard": "c++17", - "intelliSenseMode": "gcc-x64" - } - ], - "version": 4 -} \ No newline at end of file diff --git a/Exercises/Very Simple Simulator/.vscode/launch.json b/Exercises/Very Simple Simulator/.vscode/launch.json deleted file mode 100644 index 204b3a0..0000000 --- a/Exercises/Very Simple Simulator/.vscode/launch.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Debug C++", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceFolder}/build/very_simple_simulator", - "args": ["${workspaceFolder}/exercise-TC2.csv"], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ], - "preLaunchTask": "build" - } - ] -} \ No newline at end of file diff --git a/Exercises/Very Simple Simulator/.vscode/settings.json b/Exercises/Very Simple Simulator/.vscode/settings.json deleted file mode 100644 index 0cba2e6..0000000 --- a/Exercises/Very Simple Simulator/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "files.associations": { - "iostream": "cpp" - } -} \ No newline at end of file diff --git a/Exercises/Very Simple Simulator/.vscode/tasks.json b/Exercises/Very Simple Simulator/.vscode/tasks.json deleted file mode 100644 index 11023fd..0000000 --- a/Exercises/Very Simple Simulator/.vscode/tasks.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "label": "build", - "type": "shell", - "command": "g++", - "args": [ - "-g", - "${workspaceFolder}/src/very_simple_simulator.cpp", - "-o", - "${workspaceFolder}/build/very_simple_simulator" - ], - "group": "build", - "problemMatcher": [ - "$gcc" - ] - }, - { - "type": "cppbuild", - "label": "C/C++: g++ build active file", - "command": "/usr/bin/g++", - "args": [ - "-fdiagnostics-color=always", - "-g", - "${file}", - "-o", - "${fileDirname}/${fileBasenameNoExtension}" - ], - "options": { - "cwd": "${fileDirname}" - }, - "problemMatcher": [ - "$gcc" - ], - "group": { - "kind": "build", - "isDefault": true - }, - "detail": "Task generated by Debugger." - } - ] -} \ No newline at end of file diff --git a/Exercises/Very Simple Simulator/02225_Exercise.pdf b/Exercises/VerySimpleSimulator/02225_Exercise.pdf similarity index 100% rename from Exercises/Very Simple Simulator/02225_Exercise.pdf rename to Exercises/VerySimpleSimulator/02225_Exercise.pdf diff --git a/Exercises/Very Simple Simulator/README.txt b/Exercises/VerySimpleSimulator/README.txt similarity index 99% rename from Exercises/Very Simple Simulator/README.txt rename to Exercises/VerySimpleSimulator/README.txt index f028b24..294350d 100644 --- a/Exercises/Very Simple Simulator/README.txt +++ b/Exercises/VerySimpleSimulator/README.txt @@ -3,7 +3,7 @@ README.txt The files in the Exerercise/Content tab are test cases are provided as a starting point for the 02225 DRTS exercise. They are in CSV (Comma-Separated Values) format and contain the model parameters for periodic tasks: - + - Task: Task name - BCET: Best-case execution time - WCET: Worst-case execution time diff --git a/Exercises/Very Simple Simulator/build/very_simple_simulator b/Exercises/VerySimpleSimulator/build/very_simple_simulator similarity index 100% rename from Exercises/Very Simple Simulator/build/very_simple_simulator rename to Exercises/VerySimpleSimulator/build/very_simple_simulator diff --git a/Exercises/Very Simple Simulator/exercise-TC1.csv b/Exercises/VerySimpleSimulator/exercise-TC1.csv similarity index 85% rename from Exercises/Very Simple Simulator/exercise-TC1.csv rename to Exercises/VerySimpleSimulator/exercise-TC1.csv index 89e3ebe..03e6cf2 100644 --- a/Exercises/Very Simple Simulator/exercise-TC1.csv +++ b/Exercises/VerySimpleSimulator/exercise-TC1.csv @@ -5,4 +5,4 @@ T3,1,1,10,10,2 T4,1,2,12,12,3 T5,1,2,15,15,4 T6,1,3,20,20,5 -T7,1,4,30,30,6 \ No newline at end of file +T7,1,4,30,30,6 diff --git a/Exercises/VerySimpleSimulator/exercise-TC1_s.csv b/Exercises/VerySimpleSimulator/exercise-TC1_s.csv new file mode 100644 index 0000000..6183855 --- /dev/null +++ b/Exercises/VerySimpleSimulator/exercise-TC1_s.csv @@ -0,0 +1,8 @@ +Task,WCRT,Deadline,Status +T1,1,6,true +T2,54,60,true +T3,2,10,true +T4,4,12,true +T5,6,15,true +T6,10,20,true +T7,28,30,true diff --git a/Exercises/Very Simple Simulator/exercise-TC2.csv b/Exercises/VerySimpleSimulator/exercise-TC2.csv similarity index 86% rename from Exercises/Very Simple Simulator/exercise-TC2.csv rename to Exercises/VerySimpleSimulator/exercise-TC2.csv index 2427ced..407f27f 100644 --- a/Exercises/Very Simple Simulator/exercise-TC2.csv +++ b/Exercises/VerySimpleSimulator/exercise-TC2.csv @@ -9,4 +9,4 @@ T7,4,6,75,75,7 T8,5,9,100,100,8 T9,3,12,120,120,9 T10,5,11,150,150,10 -T11,6,15,300,300,11 \ No newline at end of file +T11,6,15,300,300,11 diff --git a/Exercises/Very Simple Simulator/exercise-TC3.csv b/Exercises/VerySimpleSimulator/exercise-TC3.csv similarity index 86% rename from Exercises/Very Simple Simulator/exercise-TC3.csv rename to Exercises/VerySimpleSimulator/exercise-TC3.csv index 8cd09de..2330943 100644 --- a/Exercises/Very Simple Simulator/exercise-TC3.csv +++ b/Exercises/VerySimpleSimulator/exercise-TC3.csv @@ -7,4 +7,4 @@ T5,1,22,200,200,5 T6,5,27,300,300,6 T7,8,29,320,320,7 T8,10,34,400,400,8 -T9,22,35,480,480,9 \ No newline at end of file +T9,22,35,480,480,9 diff --git a/Exercises/VerySimpleSimulator/src/jobs.py b/Exercises/VerySimpleSimulator/src/jobs.py new file mode 100644 index 0000000..d984372 --- /dev/null +++ b/Exercises/VerySimpleSimulator/src/jobs.py @@ -0,0 +1,43 @@ +from dataclasses import dataclass +import numpy as np + + +# struct to keep track of the jobs +@dataclass +class Jobs: + task: np.array + release_time: int + remaining_time: int + response_time: int + + +# function to check if every job is done +def check_remaining_jobs(jobs: np.array) -> bool: + """ + Checks if there are any jobs remaining to be executed. + Parameters: + jobs (np.array): The list of jobs to check. + Returns: + bool: True if there are jobs remaining, False otherwise. + """ + for job in jobs: + if job.remaining_time > 0: + return True + return False + + +# function to check if the job is ready to be executed +def get_ready_list(jobs: np.array, current_time: int) -> np.array: + """ + Get the list of jobs that are ready to be executed. + Args: + jobs (np.array): An array of job objects. Each job object is expected to have 'release_time' and 'remaining_time' attributes. + current_time (int): The current time against which the jobs' readiness is evaluated. + Returns: + np.array: An array of job objects that are ready to be executed. A job is considered ready if its release_time is less than or equal to the current_time and its remaining_time is greater than 0. + """ + ready_job: np.array = [] + for job in jobs: + if job.release_time <= current_time and job.remaining_time > 0: + ready_job.append(job) + return ready_job diff --git a/Exercises/VerySimpleSimulator/src/task_handler.py b/Exercises/VerySimpleSimulator/src/task_handler.py new file mode 100644 index 0000000..d4dbbaf --- /dev/null +++ b/Exercises/VerySimpleSimulator/src/task_handler.py @@ -0,0 +1,26 @@ +import pandas as pd +import numpy as np + + +def output_result_csv(ouput_path: str, wcrt_dict: dict, tasks: np.array) -> None: + """ + Writes the worst-case response time dictionary to a CSV file. + Parameters: + output_path (str): The file path to write the CSV file to. + wcrt_dict (dict): The dictionary containing the worst-case response times for each task. + """ + # Create a list to store the results + results = [] + + for job in tasks: + task = job["Task"] + wcrt = wcrt_dict[task] + deadline = job["Deadline"] + status = "true" if wcrt <= deadline else "false" + results.append([task, wcrt, deadline, status]) + + # Create a DataFrame from the results + results_df = pd.DataFrame(results, columns=["Task", "WCRT", "Deadline", "Status"]) + + # Save the DataFrame to a CSV file + results_df.to_csv(ouput_path.removesuffix(".csv") + "_s.csv", index=False) diff --git a/Exercises/Very Simple Simulator/src/very_simple_simulator.cpp b/Exercises/VerySimpleSimulator/src/very_simple_simulator.cpp similarity index 88% rename from Exercises/Very Simple Simulator/src/very_simple_simulator.cpp rename to Exercises/VerySimpleSimulator/src/very_simple_simulator.cpp index d250a83..9235b30 100644 --- a/Exercises/Very Simple Simulator/src/very_simple_simulator.cpp +++ b/Exercises/VerySimpleSimulator/src/very_simple_simulator.cpp @@ -11,8 +11,8 @@ struct Task { string id; int BCET; int WCET; - int period; - int deadline; + int period; + int deadline; int priority; // Lower number => higher priority for RMS }; @@ -28,7 +28,7 @@ struct Job { void readTasksCSV(const string& filename, vector& tasks) { ifstream file(filename); - if (!file.is_open()) + if (!file.is_open()) { cerr << "Error: Could not open file " << filename << endl; return; @@ -68,7 +68,7 @@ void readTasksCSV(const string& filename, vector& tasks) // Function to find the job with the highest priority Job *highest_priority(vector& readyList) { - if (readyList.empty()) + if (readyList.empty()) { return nullptr; } @@ -77,7 +77,7 @@ Job *highest_priority(vector& readyList) readyList.begin(), readyList.end(), [](Job* a, Job* b) { - return a->task.priority < b->task.priority; + return a->task.priority < b->task.priority; } ); } @@ -101,7 +101,7 @@ int advanceTime() int main(int argc, char* argv[]) { - if (argc != 2) + if (argc != 2) { cerr << "Usage: " << argv[0] << " " << endl; return 1; @@ -112,7 +112,7 @@ int main(int argc, char* argv[]) readTasksCSV(filename, tasks); // Simulation time (n cycles) - int n = 1000; + int n = 1000; int currentTime = 0; // List of all jobs that are in the system @@ -128,16 +128,16 @@ int main(int argc, char* argv[]) while (currentTime <= n) { // For each task, if it is time to release a new job, create it. - for (size_t i = 0; i < tasks.size(); ++i) + for (size_t i = 0; i < tasks.size(); ++i) { - if (currentTime >= nextReleaseTimes[i]) + if (currentTime >= nextReleaseTimes[i]) { Job newJob { tasks[i], currentTime, tasks[i].WCET, 0 }; jobs.push_back(newJob); nextReleaseTimes[i] += tasks[i].period; } } - + vector readyList = get_ready(jobs, currentTime); Job* currentJob = highest_priority(readyList); @@ -148,16 +148,16 @@ int main(int argc, char* argv[]) currentJob->remainingTime -= delta; // If the job has completed execution, calculate its response time and update WCRT - if (currentJob->remainingTime <= 0) + if (currentJob->remainingTime <= 0) { currentJob->responseTime = currentTime - currentJob->releaseTime; - + // Find the corresponding task index auto it = find_if(tasks.begin(), tasks.end(), [&](const Task& t) { return t.id == currentJob->task.id; }); - if (it != tasks.end()) + if (it != tasks.end()) { size_t taskIndex = distance(tasks.begin(), it); worstCaseResponseTimes[taskIndex] = max( @@ -178,12 +178,12 @@ int main(int argc, char* argv[]) // Output the worst-case response times for each task cout << "Task\tWCRT\tDeadline\tStatus" << endl; cout << "---------------------------------" << endl; - for (size_t i = 0; i < tasks.size(); ++i) + for (size_t i = 0; i < tasks.size(); ++i) { string status = (worstCaseResponseTimes[i] <= tasks[i].deadline) ? "✓" : "✗"; - cout << " " << tasks[i].id << "\t" - << worstCaseResponseTimes[i] << "\t" - << tasks[i].deadline << "\t\t" + cout << " " << tasks[i].id << "\t" + << worstCaseResponseTimes[i] << "\t" + << tasks[i].deadline << "\t\t" << status << endl; } diff --git a/Exercises/VerySimpleSimulator/src/very_simple_simulator.py b/Exercises/VerySimpleSimulator/src/very_simple_simulator.py new file mode 100644 index 0000000..4fcef44 --- /dev/null +++ b/Exercises/VerySimpleSimulator/src/very_simple_simulator.py @@ -0,0 +1,64 @@ +import pandas as pd +import numpy as np +from Exercises.VerySimpleSimulator.src.jobs import ( + Jobs, + check_remaining_jobs, + get_ready_list, +) +from Exercises.VerySimpleSimulator.src.task_handler import output_result_csv + + +def run_cycle(input_path: str) -> None: + # Print the contents of the CSV file + # Task BCET(best case execution time) WCET(worst case execution time) Period Deadline Priority + tasks = pd.read_csv(input_path) + tasks_dict = tasks.to_dict(orient="records") + + # init number of cycles and current time + num_cycles: int = 1000 + current_time: int = 0 + + # init the tasks + jobs_queue: np.array = [] + for task in tasks_dict: + jobs_queue.append(Jobs(task, 0, task["WCET"], 0)) + + # init the worst case dictionary example[T0, 0; T1,0; ...] + # wcrt_dict = {tasks.values[i][0]: 0 for i in range(len(tasks))} + wcrt_dict = {task: 0.0 for task in tasks["Task"]} + + while current_time <= num_cycles and check_remaining_jobs(jobs_queue): + # release job at the start of the period + for task in tasks_dict: + if current_time % task["Period"] == 0 and current_time != 0: + jobs_queue.append(Jobs(task, current_time, task["WCET"], 0)) + + ready_job = get_ready_list(jobs_queue, current_time) + highest_priority_job = None + if ready_job: + highest_priority_job = min(ready_job, key=lambda job: job.task["Priority"]) + + if highest_priority_job: + # advance by 1 + current_time += 1 + # compute the task + highest_priority_job.remaining_time -= 1 + # check if the task is done + if highest_priority_job.remaining_time <= 0: + highest_priority_job.response_time = ( + current_time - highest_priority_job.release_time + ) + wcrt_dict[highest_priority_job.task["Task"]] = max( + wcrt_dict[highest_priority_job.task["Task"]], + highest_priority_job.response_time, + ) + for i, job in enumerate(jobs_queue): + if job.task["Priority"] == highest_priority_job.task["Priority"]: + jobs_queue.pop(i) + break + else: + current_time += 1 + + # Convert wcrt_dict values to float + wcrt_dict = {k: float(v) for k, v in wcrt_dict.items()} + output_result_csv(input_path, wcrt_dict, tasks_dict) diff --git a/tests/res/files/ex1.csv b/tests/res/files/ex1.csv new file mode 100644 index 0000000..a9aed26 --- /dev/null +++ b/tests/res/files/ex1.csv @@ -0,0 +1,8 @@ +Task,BCET,WCET,Period,Deadline,Priority +T1,0,1,6,6,1 +T2,3,4,60,60,7 +T3,1,1,10,10,2 +T4,1,2,12,12,3 +T5,1,2,15,15,4 +T6,1,3,20,20,5 +T7,1,4,30,30,6 diff --git a/tests/res/files/ex1_s.csv b/tests/res/files/ex1_s.csv new file mode 100644 index 0000000..82d76d8 --- /dev/null +++ b/tests/res/files/ex1_s.csv @@ -0,0 +1,8 @@ +Task,WCRT,Deadline,Status +T1,1.0,6,true +T2,54.0,60,true +T3,2.0,10,true +T4,4.0,12,true +T5,6.0,15,true +T6,10.0,20,true +T7,28.0,30,true diff --git a/tests/res/files/ex2.csv b/tests/res/files/ex2.csv new file mode 100644 index 0000000..be4deef --- /dev/null +++ b/tests/res/files/ex2.csv @@ -0,0 +1,12 @@ +Task,BCET,WCET,Period,Deadline,Priority +T1,0,1,15,15,1 +T2,1,2,20,20,2 +T3,2,3,25,25,3 +T4,2,4,30,30,4 +T5,3,5,50,50,5 +T6,3,5,60,60,6 +T7,4,6,75,75,7 +T8,5,9,100,100,8 +T9,3,12,120,120,9 +T10,5,11,150,150,10 +T11,6,15,300,300,11 diff --git a/tests/res/files/ex2_s.csv b/tests/res/files/ex2_s.csv new file mode 100644 index 0000000..27cee2c --- /dev/null +++ b/tests/res/files/ex2_s.csv @@ -0,0 +1,12 @@ +Task,WCRT,Deadline,Status +T1,1.0,15,true +T2,3.0,20,true +T3,6.0,25,true +T4,10.0,30,true +T5,15.0,50,true +T6,23.0,60,true +T7,37.0,75,true +T8,49.0,100,true +T9,98.0,120,true +T10,197.0,150,false +T11,580.0,300,false diff --git a/tests/res/files/ex3.csv b/tests/res/files/ex3.csv new file mode 100644 index 0000000..7bfe7f1 --- /dev/null +++ b/tests/res/files/ex3.csv @@ -0,0 +1,10 @@ +Task,BCET,WCET,Period,Deadline,Priority +T1,1,3,40,40,1 +T2,2,7,80,80,2 +T3,1,13,100,100,3 +T4,3,18,160,160,4 +T5,1,22,200,200,5 +T6,5,27,300,300,6 +T7,8,29,320,320,7 +T8,10,34,400,400,8 +T9,22,35,480,480,9 diff --git a/tests/res/files/ex3_s.csv b/tests/res/files/ex3_s.csv new file mode 100644 index 0000000..bd4875b --- /dev/null +++ b/tests/res/files/ex3_s.csv @@ -0,0 +1,10 @@ +Task,WCRT,Deadline,Status +T1,3.0,40,true +T2,10.0,80,true +T3,23.0,100,true +T4,44.0,160,true +T5,66.0,200,true +T6,116.0,300,true +T7,148.0,320,true +T8,258.0,400,true +T9,296.0,480,true diff --git a/tests/test_exercises.py b/tests/test_exercises.py index 59ee4ca..c9fe4a3 100644 --- a/tests/test_exercises.py +++ b/tests/test_exercises.py @@ -1,25 +1,27 @@ import os import pandas as pd - -# TODO: add import for the function +from Exercises.VerySimpleSimulator.src.very_simple_simulator import run_cycle def test_exercise_1(): run_test("ex1") + def test_exercise_2(): run_test("ex2") + def test_exercise_3(): run_test("ex3") -def run_test(filename:str): - solution_file = os.path.join(get_res_path(),"solutions",f"{filename}_s.csv") - input_file = os.path.join(get_res_path(),"inputs",f"{filename}.csv") - output_file = os.path.join(get_tmp_path(),f"{filename}_s.csv") +def run_test(filename: str): + solution_file = os.path.join(get_res_path(), "solutions", f"{filename}_s.csv") + + input_file = os.path.join(get_res_path(), "files", f"{filename}.csv") + output_file = os.path.join(get_res_path(), "files", f"{filename}_s.csv") - # TODO: call the function to test + run_cycle(input_file) output_df = pd.read_csv(output_file) solution_df = pd.read_csv(solution_file) @@ -28,7 +30,7 @@ def run_test(filename:str): print(f"Solution dataframe shape: {solution_df.shape}") if not output_df.shape == solution_df.shape: print("Error: dataframe shape are different") - assert(False) + assert False print("Output dataframe:") print(output_df) @@ -37,26 +39,29 @@ def run_test(filename:str): print("Solution dataframe:") print(solution_df) print("==============================") - assert(output_df.equals(solution_df)) + assert output_df.equals(solution_df) + def get_root(): - ''' + """ Get the project root - ''' + """ return os.path.dirname(os.path.dirname(__file__)) + def get_res_path(): - ''' + """ Get the absoulte path of the tests/res folder - ''' - return os.path.join(get_root(),"tests","res") + """ + return os.path.join(get_root(), "tests", "res") + def get_tmp_path(): - ''' + """ Get the absoulte path of the tests/.tmp folder. If it doesn't exist it creates it - ''' + """ tmp_path = os.path.join(get_root(), "tests", ".tmp") if not os.path.exists(tmp_path): os.makedirs(tmp_path) - return tmp_path \ No newline at end of file + return tmp_path