ROS-Dynamic-Executor-Experi.../analysis/analysis.ipynb

328 lines
51 KiB
Text
Raw Normal View History

{
"cells": [
{
"cell_type": "code",
2025-04-29 09:31:19 +02:00
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import json\n",
"\n",
"import matplotlib.pyplot as plt\n",
"from dataclasses import dataclass\n",
"import numpy as np"
]
},
{
"cell_type": "code",
2025-04-29 09:31:19 +02:00
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"this_dir = os.path.dirname(os.path.abspath(''))\n",
"# results is in \"../results\"\n",
"results_dir = os.path.join(this_dir, \"results\")"
]
},
{
"cell_type": "code",
2025-04-29 09:31:19 +02:00
"execution_count": 3,
"metadata": {},
2025-04-29 09:31:19 +02:00
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Experiment file not found: /workspaces/ROS-Dynamic-Executor-Experiments/results/existing_system_monitor/uas_edf.json\n"
]
}
],
"source": [
2025-04-04 16:25:13 +02:00
"#experiment_folder = \"casestudy_example\"\n",
"#experiment_name = \"cs_example_edf\"\n",
"experiment_folder = \"existing_system_monitor\"\n",
"experiment_name = \"uas_edf\"\n",
"\n",
"experiment_file = os.path.join(results_dir, experiment_folder, experiment_name + \".json\")\n",
"if not os.path.exists(experiment_file):\n",
" print(\"Experiment file not found: \", experiment_file)"
]
},
{
"cell_type": "code",
2025-04-29 09:31:19 +02:00
"execution_count": 4,
"metadata": {},
2025-04-29 09:31:19 +02:00
"outputs": [
{
"ename": "FileNotFoundError",
"evalue": "[Errno 2] No such file or directory: '/workspaces/ROS-Dynamic-Executor-Experiments/results/existing_system_monitor/uas_edf.json'",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mFileNotFoundError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[0;32mIn[4], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28;43mopen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mexperiment_file\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mas\u001b[39;00m f:\n\u001b[1;32m 2\u001b[0m experiment_data_raw \u001b[38;5;241m=\u001b[39m json\u001b[38;5;241m.\u001b[39mload(f)\n",
"File \u001b[0;32m/workspaces/ROS-Dynamic-Executor-Experiments/venv310/lib/python3.10/site-packages/IPython/core/interactiveshell.py:324\u001b[0m, in \u001b[0;36m_modified_open\u001b[0;34m(file, *args, **kwargs)\u001b[0m\n\u001b[1;32m 317\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m file \u001b[38;5;129;01min\u001b[39;00m {\u001b[38;5;241m0\u001b[39m, \u001b[38;5;241m1\u001b[39m, \u001b[38;5;241m2\u001b[39m}:\n\u001b[1;32m 318\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 319\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mIPython won\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mt let you open fd=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfile\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m by default \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 320\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mas it is likely to crash IPython. If you know what you are doing, \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 321\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124myou can use builtins\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m open.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 322\u001b[0m )\n\u001b[0;32m--> 324\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mio_open\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfile\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n",
"\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: '/workspaces/ROS-Dynamic-Executor-Experiments/results/existing_system_monitor/uas_edf.json'"
]
}
],
"source": [
"with open(experiment_file) as f:\n",
" experiment_data_raw = json.load(f)"
]
},
{
"cell_type": "code",
2025-04-29 09:31:19 +02:00
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Number of records: 42\n",
"First record: {'entry': {'operation': 'start_work', 'chain': 0, 'node': 'CustomListenerNode/CustomListenerNode-wildfire_talk', 'count': 0}, 'time': 0.0}\n",
"Operation types: ['start_work', 'end_work']\n"
]
}
],
"source": [
"def pre_process_data(data):\n",
" for record in data:\n",
" record[\"time\"] = int(record[\"time\"])\n",
"\n",
" min_time = min([record[\"time\"] for record in data])\n",
" for record in data:\n",
" record[\"time\"] -= min_time\n",
" record[\"time\"] /= (1000 * 1000)\n",
"\n",
" if record[\"entry\"][\"operation\"] == \"next_deadline\":\n",
" #print(\"Record: \", record)\n",
" record[\"entry\"][\"deadline\"] = int(record[\"entry\"][\"deadline\"])\n",
" record[\"entry\"][\"deadline\"] -= min_time\n",
" record[\"entry\"][\"deadline\"] /= (1000 * 1000)\n",
"\n",
" # data = sorted(data, key=lambda x: x[\"time\"])\n",
" return data\n",
"\n",
"experiment_data = pre_process_data(experiment_data_raw)\n",
"\n",
"print(\"Number of records: \", len(experiment_data))\n",
"print(\"First record: \", experiment_data[0])\n",
"operation_types = list(set([record[\"entry\"][\"operation\"] for record in experiment_data]))\n",
"print(\"Operation types: \", operation_types)"
]
},
{
"cell_type": "code",
2025-04-29 09:31:19 +02:00
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"@dataclass\n",
"class Record:\n",
" start_time: float\n",
" end_time: float\n",
" node_name: str\n",
"\n",
"@dataclass\n",
"class RecordLine:\n",
" node_name: str\n",
" count: int\n",
"\n",
" def __eq__(self, other):\n",
" return self.node_name == other.node_name and self.count == other.count\n",
"\n",
" def __hash__(self):\n",
" return hash((self.node_name, self.count))\n",
"\n",
"def get_records(data) -> list[Record]:\n",
" # used to match start_work and end_work records\n",
" current_records: dict[RecordLine, Record] = {}\n",
" records = []\n",
" for record in data:\n",
" if record[\"entry\"][\"operation\"] == \"start_work\":\n",
" current_record = Record(start_time=record[\"time\"], node_name=record[\"entry\"][\"node\"], end_time=None)\n",
" current_record_line = RecordLine(node_name=record[\"entry\"][\"node\"], count=record[\"entry\"][\"count\"])\n",
" if current_record_line in current_records:\n",
" raise Exception(\"Overlapping records\")\n",
" current_records[current_record_line] = current_record\n",
" elif record[\"entry\"][\"operation\"] == \"end_work\":\n",
" current_record_line = RecordLine(node_name=record[\"entry\"][\"node\"], count=record[\"entry\"][\"count\"])\n",
" if current_record_line not in current_records:\n",
" raise Exception(\"No start record\")\n",
" current_record = current_records[current_record_line]\n",
" current_record.end_time = record[\"time\"]\n",
" records.append(current_record)\n",
" del current_records[current_record_line]\n",
" return records\n",
"\n",
"records = get_records(experiment_data)"
]
},
{
"cell_type": "code",
2025-04-29 09:31:19 +02:00
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Number of nodes: 2\n"
]
}
],
"source": [
"num_nodes = len(set([record.node_name for record in records]))\n",
"print(\"Number of nodes: \", num_nodes)"
]
},
{
"cell_type": "code",
2025-04-29 09:31:19 +02:00
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "ab61935f394e43aeaa093dc378e11680",
"version_major": 2,
"version_minor": 0
},
"image/png": "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+PXXX4+NGzfG3LlzY/jw4dXLTTfdFBs3boyIiEsuuaR6/bnnnnvUwXreeedVPx41alREREyaNOmg61599dWIiHj66adj5cqV3ebz4Q9/OLq6uuLFF1+s3m/q1Klvee4bbrghdu3aVb1s3rz5qOcPAFBrvfoikD179kRExLe//e2DQnHgwIER8b+/H/jGG29ERMTgwYOP+hxvvk+lUjnsdV1dXdU5zZ8/PxYsWHDQsc4888zqx8OGDXvLc9fV1UVdXd1RzxkAoD+pBuDEiRNj8+bNsXnz5uqjgOvXr4+dO3fGe97znuqYxx57LGbNmlU9wNq1a6sfjxo1KsaMGRMvvPBCXHnllYc84emnn37QdQdecbt///5eWFJ373vf+2L9+vVx9tln9/qxAQCOR9UAnD59ekyaNCmuvPLK+NrXvhb79u2LT33qU9HW1lZ9evQzn/lMzJkzJ6ZOnRrvf//746677opnn3023vWud1UPuHTp0liwYEE0NjbGjBkzorOzM5588sl47bXX4rrrrjvkJMaOHRuVSiX+5V/+JWbOnBn19fUxfPjwXlng5z//+fjd3/3d+PSnPx1XX311DBs2LNavXx8PPPBAfOMb3+iVcwAAHE+qvwNYqVRi1apV8c53vjN+//d/P6ZPnx7vete74p577qkO/vjHPx6LFi2Kz33uczFlypT4xS9+EZ/85Ce7HfDqq6+OO++8M1asWBGTJk2Ktra2WLlyZYwfP/6wkzj99NNj6dKlcf3118eoUaMOeoVuT5x33nnx0EMPxfPPPx8f+MAHYvLkybF48eIYM2ZMr50DAOB4Uinl/75fCketo6MjGhsbY9euXdHQ0FDr6QAAb4Of373wt4ABADi+CEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQg
"text/html": [
"\n",
" <div style=\"display: inline-block;\">\n",
" <div class=\"jupyter-widgets widget-label\" style=\"text-align: center;\">\n",
" Figure\n",
" </div>\n",
" <img src='data:image/png;base64,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+PXXX4+NGzfG3LlzY/jw4dXLTTfdFBs3boyIiEsuuaR6/bnnnnvUwXreeedVPx41alREREyaNOmg61599dWIiHj66adj5cqV3ebz4Q9/OLq6uuLFF1+s3m/q1Klvee4bbrghdu3aVb1s3rz5qOcPAFBrvfoikD179kRExLe//e2DQnHgwIER8b+/H/jGG29ERMTgwYOP+hxvvk+lUjnsdV1dXdU5zZ8/PxYsWHDQsc4888zqx8OGDXvLc9fV1UVdXd1RzxkAoD+pBuDEiRNj8+bNsXnz5uqjgOvXr4+dO3fGe97znuqYxx57LGbNmlU9wNq1a6sfjxo1KsaMGRMvvPBCXHnllYc84emnn37QdQdecbt///5eWFJ373vf+2L9+vVx9tln9/qxAQCOR9UAnD59ekyaNCmuvPLK+NrXvhb79u2LT33qU9HW1lZ9evQzn/lMzJkzJ6ZOnRrvf//746677opnn3023vWud1UPuHTp0liwYEE0NjbGjBkzorOzM5588sl47bXX4rrrrjvkJMaOHRuVSiX+5V/+JWbOnBn19fUxfPjwXlng5z//+fjd3/3d+PSnPx1XX311DBs2LNavXx8PPPBAfOMb3+iVcwAAHE+qvwNYqVRi1apV8c53vjN+//d/P6ZPnx7vete74p577qkO/vjHPx6LFi2Kz33uczFlypT4xS9+EZ/85Ce7HfDqq6+OO++8M1asWBGTJk2Ktra2WLlyZYwfP/6wkzj99NNj6dKlcf3118eoUaMOeoVuT5x33nnx0EMPxfPPPx8f+MAHYvLkybF48eIYM2ZMr50DAOB4Uinl/75fCketo6MjGhsbY9euXdHQ0FDr6QAAb4Of373wt4ABADi+CEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQ
" </div>\n",
" "
],
"text/plain": [
"Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%matplotlib widget\n",
"\n",
"# swimlane plot\n",
"name_to_id = {name: i for i, name in enumerate(set([record.node_name for record in records]))}\n",
"fig, ax = plt.subplots()\n",
"for i, record in enumerate(records):\n",
" # ax.plot([record.start_time, record.end_time], [name_to_id[record.node_name], name_to_id[record.node_name]], label=record.node_name)\n",
" ax.broken_barh([(record.start_time, record.end_time - record.start_time)], (name_to_id[record.node_name] - 0.4, 0.8), facecolors='blue')\n",
"ax.set_yticks(range(num_nodes))\n",
"ax.set_yticklabels(name_to_id.keys())\n",
"plt.show()"
]
},
{
"cell_type": "code",
2025-04-29 09:31:19 +02:00
"execution_count": null,
"metadata": {},
2025-04-02 15:51:09 +02:00
"outputs": [],
"source": [
"@dataclass\n",
"class Deadline:\n",
" chain_id: int\n",
" deadline: float\n",
" on_time: bool\n",
"\n",
"def get_deadlines(data) -> list[Deadline]:\n",
" deadlines = []\n",
" for record in data:\n",
" if record[\"entry\"][\"operation\"] == \"next_deadline\" and \"on_time\" in record[\"entry\"]:\n",
" deadlines.append(Deadline(chain_id=record[\"entry\"][\"chain_id\"], deadline=record[\"entry\"][\"deadline\"], on_time=record[\"entry\"][\"on_time\"]))\n",
" return deadlines\n",
"\n",
"deadlines = get_deadlines(experiment_data)"
]
},
{
"cell_type": "code",
2025-04-29 09:31:19 +02:00
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "4461f17363834d11a2f9b35f79bbb477",
"version_major": 2,
"version_minor": 0
},
"image/png": "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+PXXX4+NGzfG3LlzY/jw4dXLTTfdFBs3boyIiEsuuaR6/bnnnnvUwXreeedVPx41alREREyaNOmg61599dWIiHj66adj5cqV3ebz4Q9/OLq6uuLFF1+s3m/q1Klvee4bbrghdu3aVb1s3rz5qOcPAFBrvfoikD179kRExLe//e2DQnHgwIER8b+/H/jGG29ERMTgwYOP+hxvvk+lUjnsdV1dXdU5zZ8/PxYsWHDQsc4888zqx8OGDXvLc9fV1UVdXd1RzxkAoD+pBuDEiRNj8+bNsXnz5uqjgOvXr4+dO3fGe97znuqYxx57LGbNmlU9wNq1a6sfjxo1KsaMGRMvvPBCXHnllYc84emnn37QdQdecbt///5eWFJ373vf+2L9+vVx9tln9/qxAQCOR9UAnD59ekyaNCmuvPLK+NrXvhb79u2LT33qU9HW1lZ9evQzn/lMzJkzJ6ZOnRrvf//746677opnn3023vWud1UPuHTp0liwYEE0NjbGjBkzorOzM5588sl47bXX4rrrrjvkJMaOHRuVSiX+5V/+JWbOnBn19fUxfPjwXlng5z//+fjd3/3d+PSnPx1XX311DBs2LNavXx8PPPBAfOMb3+iVcwAAHE+qvwNYqVRi1apV8c53vjN+//d/P6ZPnx7vete74p577qkO/vjHPx6LFi2Kz33uczFlypT4xS9+EZ/85Ce7HfDqq6+OO++8M1asWBGTJk2Ktra2WLlyZYwfP/6wkzj99NNj6dKlcf3118eoUaMOeoVuT5x33nnx0EMPxfPPPx8f+MAHYvLkybF48eIYM2ZMr50DAOB4Uinl/75fCketo6MjGhsbY9euXdHQ0FDr6QAAb4Of373wt4ABADi+CEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQg
"text/html": [
"\n",
" <div style=\"display: inline-block;\">\n",
" <div class=\"jupyter-widgets widget-label\" style=\"text-align: center;\">\n",
" Figure\n",
" </div>\n",
" <img src='data:image/png;base64,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+PXXX4+NGzfG3LlzY/jw4dXLTTfdFBs3boyIiEsuuaR6/bnnnnvUwXreeedVPx41alREREyaNOmg61599dWIiHj66adj5cqV3ebz4Q9/OLq6uuLFF1+s3m/q1Klvee4bbrghdu3aVb1s3rz5qOcPAFBrvfoikD179kRExLe//e2DQnHgwIER8b+/H/jGG29ERMTgwYOP+hxvvk+lUjnsdV1dXdU5zZ8/PxYsWHDQsc4888zqx8OGDXvLc9fV1UVdXd1RzxkAoD+pBuDEiRNj8+bNsXnz5uqjgOvXr4+dO3fGe97znuqYxx57LGbNmlU9wNq1a6sfjxo1KsaMGRMvvPBCXHnllYc84emnn37QdQdecbt///5eWFJ373vf+2L9+vVx9tln9/qxAQCOR9UAnD59ekyaNCmuvPLK+NrXvhb79u2LT33qU9HW1lZ9evQzn/lMzJkzJ6ZOnRrvf//746677opnn3023vWud1UPuHTp0liwYEE0NjbGjBkzorOzM5588sl47bXX4rrrrjvkJMaOHRuVSiX+5V/+JWbOnBn19fUxfPjwXlng5z//+fjd3/3d+PSnPx1XX311DBs2LNavXx8PPPBAfOMb3+iVcwAAHE+qvwNYqVRi1apV8c53vjN+//d/P6ZPnx7vete74p577qkO/vjHPx6LFi2Kz33uczFlypT4xS9+EZ/85Ce7HfDqq6+OO++8M1asWBGTJk2Ktra2WLlyZYwfP/6wkzj99NNj6dKlcf3118eoUaMOeoVuT5x33nnx0EMPxfPPPx8f+MAHYvLkybF48eIYM2ZMr50DAOB4Uinl/75fCketo6MjGhsbY9euXdHQ0FDr6QAAb4Of373wt4ABADi+CEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQjAAEAkhGAAADJCEAAgGQEIABAMgIQACAZAQgAkIwABABIRgACACQ
" </div>\n",
" "
],
"text/plain": [
"Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# plot with lines for deadlines\n",
"fig, ax = plt.subplots()\n",
"for i, record in enumerate(records):\n",
" ax.broken_barh([(record.start_time, record.end_time - record.start_time)], (name_to_id[record.node_name] - 0.4, 0.8), facecolors='blue')\n",
"\n",
"# draw a vertical line for each deadline\n",
"for deadline in deadlines:\n",
" # may have to adjust the y value depending on your chain layout\n",
2025-04-04 16:25:13 +02:00
" if deadline.on_time:\n",
" print(\"On time deadline: \", deadline)\n",
" ax.plot([deadline.deadline, deadline.deadline], [0, num_nodes], color=('green' if deadline.on_time else 'red'))\n",
"\n",
"ax.set_yticks(range(num_nodes))\n",
"ax.set_yticklabels(name_to_id.keys())\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "venv310",
"language": "python",
2025-03-29 14:11:51 +01:00
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.16"
}
},
"nbformat": 4,
"nbformat_minor": 4
}