{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# ๐ŸŽฏ [C202] ๋Œ€์—ญํญ ์ œํ•œ ์‹œ ๋ฉ”์‹œ์ง€ ์šฐ์„ ์ˆœ์œ„ ํยท์žฌ์ „์†ก ํ”„๋กœํ† ํƒ€์ž…\n", "### Google Colab ์‹ค์Šต ํ™˜๊ฒฝ (์ง€ํœ˜ํ†ต์ œ(C2) / Level 2)\n", "\n", "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/edunlab/milicontent2/blob/main/uploads/C202/C202_solution.ipynb)\n", "\n", "---\n", "\n", "## ๐Ÿ“Œ 1. ํ”„๋กœ์ ํŠธ ๊ฐœ์š” ๋ฐ ๋ฐฐ๊ฒฝ\n", "\n", "| ํ•ญ๋ชฉ | ๋‚ด์šฉ |\n", "| :--- | :--- |\n", "| **๊ณผ์ • ๋ถ„๋ฅ˜** | ์ง€ํœ˜ํ†ต์ œ(C2) (Level 2) |\n", "| **ํ•™์Šต ์†Œ์š” ์‹œ๊ฐ„** | 10๊ฐœ ๋ฏธ์…˜ (5์ผ / ์ด 27.5์‹œ๊ฐ„) |\n", "| **ํ™˜๊ฒฝ ์‚ฌ์–‘** | Python 3.10+, numpy, pandas, matplotlib, scipy, scikit-learn |\n", "\n", "### ๐Ÿ“– ํ”„๋กœ์ ํŠธ ๋ชฉํ‘œ\n", "๋Ÿฌ์‹œ์•„-์šฐํฌ๋ผ์ด๋‚˜ ์ „์Ÿ์—์„œ๋Š” ์ „์ž์ „ ์žฌ๋ฐ๊ณผ ํ†ต์‹  ์žฅ๋น„ ๋ถ€์กฑ์œผ๋กœ ํ˜‘๋Œ€์—ญ ๋ฌด์„ ๋ง์˜ ๋Œ€์—ญํญ์ด ํฌ๊ฒŒ ์ค„์–ด๋“œ๋Š” ์ƒํ™ฉ์ด ์žฆ์•˜๊ณ , ์ด๋•Œ ์  ์ ‘์ด‰ ๋ณด๊ณ ๋‚˜ ์˜๋ฌดํ›„์†ก ์š”์ฒญ ๊ฐ™์€ ๊ธด๊ธ‰ ์ „๋ฌธ์ด ํ–‰์ • ์ „๋ฌธ ๋’ค์— ๋ฌถ์—ฌ ๋Šฆ๊ฒŒ ์ „๋‹ฌ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ง€ํœ˜ํ†ต์ œ์˜ ์•ฝ์ ์ด ๋˜์—ˆ๋‹ค. ๋ณธ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๊ฐ€์ƒ ํ˜‘๋Œ€์—ญ ๋งํฌ(2.4kbps)์™€ ๊ฐ€์ƒ ์ „์ˆ  ์ „๋ฌธ์„ ์ฝ”๋“œ๋กœ ์ง์ ‘ ๋งŒ๋“ค์–ด, ์ „๋ฌธ ๊ตฌ์กฐ ์ •์˜, ํŒจํ‚ท ํŒŒ์‹ฑยท์••์ถ• ์ „์ฒ˜๋ฆฌ, ๊ธด๊ธ‰๋„ ๊ธฐ๋ฐ˜ ์šฐ์„ ์ˆœ์œ„ ํ, ์ฑ„๋„ ์ƒํƒœ์— ๋”ฐ๋ฅธ ์ง€๋Šฅํ˜• ์žฌ์ „์†ก, ๋งํฌ ์‹ ๋ขฐ๋„ ์‚ฐ์ถœ, ํ†ต์‹  ๊ด€์ œ ํ™”๋ฉด, ํ†ต์‹ ๊ด€ HITL ํ†ต์ œ, ๋Œ€์—ญํญ ๊ธ‰๊ฐ ์ŠคํŠธ๋ ˆ์Šค ์‹œํ—˜, AAR ์žฌ์กฐ์ •, ์ตœ์ข… ํ†ตํ•ฉ๊นŒ์ง€ ๋ฉ”์‹œ์ง€ ์šฐ์„ ์ˆœ์œ„ ํยท์žฌ์ „์†ก ํ”„๋กœํ† ํƒ€์ž…์„ ๋‹จ๊ณ„๋ณ„๋กœ ๊ตฌํ˜„ํ•œ๋‹ค. ์ตœ์ข… ๋ชฉํ‘œ๋Š” ๋Œ€์—ญํญ์ด ์ œํ•œ๋œ ์ƒํ™ฉ์—์„œ๋„ FLASHยทIMMEDIATE ์ „๋ฌธ์„ ๊ธฐํ•œ ์•ˆ์— ์ „๋‹ฌํ•˜๊ณ , ์–ด๋–ค ์ „๋ฌธ๋„ ์žƒ์–ด๋ฒ„๋ฆฌ์ง€ ์•Š์œผ๋ฉฐ, ์ˆœ์„œ ์กฐ์ •์˜ ์ตœ์ข… ํŒ๋‹จ์€ ํ†ต์‹ ๊ด€์ด ๋‚ด๋ฆฌ๋„๋ก ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.\n", "\n", "> **โš ๏ธ [๊ต์œก์šฉ ๊ฐ€์ƒ ๋ฐ์ดํ„ฐ์…‹ ์›์น™]**\n", "> - ๋ณธ ๋…ธํŠธ๋ถ์˜ ๋ชจ๋“  ๋ฐ์ดํ„ฐ(์ œ์›, ๋กœ๊ทธ, ์‹œ๋‚˜๋ฆฌ์˜ค ๋“ฑ)๋Š” ๋ณด์•ˆ ๊ทœ์ •์„ ์ค€์ˆ˜ํ•˜์—ฌ **์ฝ”๋“œ ๋‚ด ๋‚œ์ˆ˜ ๊ธฐ๋ฐ˜ ๊ฐ€์ƒ ๋ฐ์ดํ„ฐ๋กœ ์ƒ์„ฑ**๋ฉ๋‹ˆ๋‹ค.\n", "> - ์‹ค์ œ ๋ถ€๋Œ€ ์œ„์น˜, ์ž‘์ „ ์ธ๊ฐ€, ์‹ค์ œ ์ „์žฅ ์ •๋ณด, ๊ตฐ์‚ฌ ๊ธฐ๋ฐ€ ๋ฐ์ดํ„ฐ๋Š” ์ผ์ฒด ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.\n", "\n", "---\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# ============================================================================\n", "# [Google Colab ํ™˜๊ฒฝ ์„ค์ •] ํ•„์ˆ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜ ๋ฐ ์ž„ํฌํŠธ\n", "# ============================================================================\n", "!pip install -q numpy pandas matplotlib scipy scikit-learn\n", "\n", "import os\n", "import sys\n", "import time\n", "import math\n", "import json\n", "import re\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "import warnings\n", "warnings.filterwarnings('ignore')\n", "\n", "# ๊ทธ๋ž˜ํ”„ ์˜๋ฌธ ํฐํŠธ ๊ธฐ๋ณธ ์„ค์ • (ํ™˜๊ฒฝ ๋…๋ฆฝ์„ฑ ๋ณด์žฅ)\n", "plt.rcParams['font.sans-serif'] = 'DejaVu Sans'\n", "plt.rcParams['axes.unicode_minus'] = False\n", "\n", "print(f\"์‹คํ–‰ ํ™˜๊ฒฝ ์ค€๋น„ ์™„๋ฃŒ (Python {sys.version.split()[0]})\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 01. ํ˜‘๋Œ€์—ญ ์ „์ˆ  ๋ฌด์„ ๋ง(VHF/UHF) ๋Œ€์—ญํญ ์ œ์•ฝ ์กฐ๊ฑด ๋ถ„์„ ๋ฐ ์ „์ˆ  ๋ฉ”์‹œ์ง€ ๊ตฌ์กฐ ์ •์˜\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 1.5์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "ํ˜‘๋Œ€์—ญ ์ „์ˆ  ๋ฌด์ „๋ง(VHF/UHF)์˜ ์ „์†ก ๋Œ€์—ญํญ ์ œ์•ฝ๊ณผ ์ „์ˆ  ์ „๋ฌธ(Message) ๊ตฌ์กฐ๋ฅผ ๋ถ„์„ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **ํ˜‘๋Œ€์—ญ ์ „์ˆ  ๋ฌด์„ ๋ง์˜ ๋Œ€์—ญํญ ์ œ์•ฝ์ด ์ „๋ฌธ ์ „์†ก์‹œ๊ฐ„์— ์ฃผ๋Š” ์˜ํ–ฅ์„ ๋ถ„์„ํ•˜๊ณ , ์ „๋ฌธ ํ—ค๋”ยท๊ธด๊ธ‰๋„ยท๋งํฌ ์กฐ๊ฑดยท์ „์ˆ  ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋‹ด์€ ๋ฐ์ดํ„ฐ ์Šคํ‚ค๋งˆ์™€ ๊ฒ€์ฆ๊ธฐ๋ฅผ ๋งŒ๋“ ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **์ „๋ฌธ ์œ ํ˜•(์ ‘์ด‰ ๋ณด๊ณ , ์˜๋ฌดํ›„์†ก ์š”์ฒญ, ์ƒํ™ฉ ๋ณด๊ณ , ์œ„์น˜ ๋ณด๊ณ , ๋ณด๊ธ‰ ์š”์ฒญ, ํ–‰์ •)๋ณ„ ํ‰๊ท  ํฌ๊ธฐ๋ฅผ 1.2~16kbps ๋งํฌ์—์„œ ๋ณด๋‚ผ ๋•Œ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์„ ํ‘œ๋กœ ์ •๋ฆฌํ•˜๊ณ  ๊ทœ๊ฒฉํ™”ํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `data/schema/c202_input_schema.json ๋ฐ docs/c202_data_spec.md` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ์ง€ํœ˜ํ†ต์ œ(C2) ์‹ค๋ฌด ๋ฐ ๋Ÿฌ์‹œ์•„-์šฐํฌ๋ผ์ด๋‚˜ ์ „์Ÿ ์ „์žฅ ํ™˜๊ฒฝ๊ณผ์˜ ๊ตฐ์‚ฌ์  ๋ถ€ํ•ฉ์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ์Šคํ‚ค๋งˆ ํ•„๋“œ ์ •์˜ ์™„์„ฑ๋„ 100%, ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ ๊ฒ€์ฆ ์˜ค๋ฅ˜ 0๊ฑด\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ์ž…๋ ฅ ๋ฐ์ดํ„ฐ ์œ ํšจ์„ฑ ๊ฒ€์ฆ ์Šคํฌ๋ฆฝํŠธ ํ†ต๊ณผ ๋ฐ ๋ช…์„ธ์„œ ๋ฌธ์„œํ™” ์™„๋ฃŒ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: JSON Schema ๊ฒ€์ฆ ๋„๊ตฌ ๋ฐ ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ ํŒŒ์‹ฑ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"ํ˜‘๋Œ€์—ญ ์ „์ˆ  ๋ฌด์„ ๋ง(VHF/UHF)์˜ ๋Œ€์—ญํญ ์ œ์•ฝ์„ ์ „๋ฌธ ์œ ํ˜•๋ณ„ ์ „์†ก์‹œ๊ฐ„์œผ๋กœ ๋ถ„์„ํ•˜๊ณ , ์ „์ˆ  ์ „๋ฌธ์˜ ํ—ค๋”ยท๊ธด๊ธ‰๋„ยท๋งํฌยท์ „์ˆ  ํŒŒ๋ผ๋ฏธํ„ฐ ์Šคํ‚ค๋งˆ๋ฅผ ์ •์˜ํ•ด ๊ฐ€์ƒ ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒ€์ฆํ•œ๋‹ค.\"\"\"\n", "import json\n", "import re\n", "import numpy as np\n", "import pandas as pd\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์ „์ˆ  ์ „๋ฌธ(๋ฉ”์‹œ์ง€) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยทํ†ต์‹ ๋งยท์ œ์› ์ •๋ณด ์—†์Œ)\n", "# ๊ธด๊ธ‰๋„ 4๋‹จ๊ณ„(FLASH/IMMEDIATE/PRIORITY/ROUTINE)์™€ ์ „๋ฌธ ์œ ํ˜•๋ณ„ ํฌ๊ธฐ๋ฅผ ๊ฐ€์ง„ ์ „๋ฌธ์„ ๋งŒ๋“ ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PREC = [\"FLASH\", \"IMMEDIATE\", \"PRIORITY\", \"ROUTINE\"]\n", "RANK = {p: i for i, p in enumerate(PREC)}\n", "DEADLINE = {\"FLASH\": 10, \"IMMEDIATE\": 30, \"PRIORITY\": 120, \"ROUTINE\": 600} # ๊ฐ€์ƒ ์ „๋‹ฌ ๋ชฉํ‘œ ์‹œ๊ฐ„(์ดˆ)\n", "MSG_TYPES = { # ์œ ํ˜•: (๊ธด๊ธ‰๋„ ํ›„๋ณด, ํ™•๋ฅ , ๋ณธ๋ฌธ ํฌ๊ธฐ ๋ฒ”์œ„ bytes, ๋ฐœ์ƒ ๋น„์œจ)\n", " \"CONTACT\": ([\"FLASH\", \"IMMEDIATE\"], [0.7, 0.3], (120, 300), 0.08),\n", " \"MEDEVAC\": ([\"FLASH\", \"IMMEDIATE\"], [0.4, 0.6], (150, 350), 0.05),\n", " \"SITREP\": ([\"IMMEDIATE\", \"PRIORITY\"], [0.2, 0.8], (600, 2500), 0.20),\n", " \"POSREP\": ([\"PRIORITY\", \"ROUTINE\"], [0.5, 0.5], (60, 120), 0.35),\n", " \"LOGREQ\": ([\"PRIORITY\", \"ROUTINE\"], [0.4, 0.6], (300, 1200), 0.14),\n", " \"ADMIN\": ([\"ROUTINE\"], [1.0], (1000, 5000), 0.18),\n", "}\n", "\n", "\n", "def make_messages(duration=600, rate=0.12, seed=7, t_start=0.0, prefix=\"M\"):\n", " \"\"\"duration์ดˆ ๋™์•ˆ ํ‰๊ท  rate๊ฑด/์ดˆ๋กœ ๋ฐœ์ƒํ•˜๋Š” ๊ฐ€์ƒ ์ „๋ฌธ ๋ชฉ๋ก.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " n = int(rng.poisson(rate * duration))\n", " times = np.sort(rng.uniform(t_start, t_start + duration, n))\n", " types = list(MSG_TYPES)\n", " msgs = []\n", " for i, t0 in enumerate(times):\n", " ty = types[rng.choice(len(types), p=[MSG_TYPES[k][3] for k in types])]\n", " cands, p, (lo, hi), _ = MSG_TYPES[ty]\n", " msgs.append({\"id\": f\"{prefix}{i:04d}\", \"t0\": float(t0), \"type\": ty, \"prec\": str(rng.choice(cands, p=p)),\n", " \"size\": int(rng.integers(lo, hi + 1)), \"src\": f\"U{int(rng.integers(1, 9)):02d}\"})\n", " return msgs\n", "\n", "\n", "class Link:\n", " \"\"\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ํ˜‘๋Œ€์—ญ ๋ฌด์„  ๋งํฌ(๊ธฐ๋ณธ 2.4kbps). ์ดˆ ๋‹จ์œ„ ์ƒํƒœ(good/bad/jam)์— ๋”ฐ๋ผ\n", " ๋น„ํŠธ์˜ค๋ฅ˜์œจ์ด ๋ฐ”๋€Œ๊ณ (Gilbert-Elliott ๋ชจ๋ธ), ๊ตฌ๊ฐ„๋ณ„ ๋Œ€์—ญํญ ๊ฐ์†Œ๋„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.\"\"\"\n", " BER = {\"good\": 1e-5, \"bad\": 2e-4, \"jam\": 1.2e-3}\n", "\n", " def __init__(self, horizon=4000, bw_bps=2400, seed=1, p_gb=0.02, p_bg=0.15, jam=(), bw_drop=(), rtt=(1.2, 0.4)):\n", " rng = np.random.default_rng(seed)\n", " st, s = [], \"good\"\n", " for _ in range(int(horizon) + 1):\n", " s = (\"bad\" if rng.random() < p_gb else \"good\") if s == \"good\" else (\"good\" if rng.random() < p_bg else \"bad\")\n", " st.append(s)\n", " for a, b in jam:\n", " for k in range(int(a), min(int(b), len(st))):\n", " st[k] = \"jam\"\n", " self.states, self.bw_bps, self.bw_drop, self.rtt_par = st, bw_bps, list(bw_drop), rtt\n", "\n", " def state(self, t):\n", " return self.states[min(int(t), len(self.states) - 1)]\n", "\n", " def bw(self, t):\n", " f = 1.0\n", " for a, b, fac in self.bw_drop:\n", " if a <= t < b:\n", " f = fac\n", " return self.bw_bps * f\n", "\n", " def p_loss(self, nbytes, t):\n", " return 1 - (1 - self.BER[self.state(t)]) ** (8 * nbytes)\n", "\n", " def rtt(self, rng):\n", " return self.rtt_par[0] + rng.exponential(self.rtt_par[1])\n", "# ---------------------------------------------------------------------------\n", "\n", "# 1) ๋Œ€์—ญํญ ์ œ์•ฝ ๋ถ„์„: ๋งํฌ ์†๋„๋ณ„ ์ „๋ฌธ 1๊ฑด ์ „์†ก์‹œ๊ฐ„(ํ—ค๋” 32bytes/์กฐ๊ฐ 256bytes ๊ธฐ์ค€, ์žฌ์ „์†ก ์ œ์™ธ)\n", "print(\"[๋Œ€์—ญํญ๋ณ„ ์ „๋ฌธ 1๊ฑด ์ „์†ก์‹œ๊ฐ„(์ดˆ) - ๊ฐ€์ƒ ๋งํฌ]\")\n", "rows = []\n", "for ty, (_, _, (lo, hi), _) in MSG_TYPES.items():\n", " size = (lo + hi) / 2\n", " nbytes = size + np.ceil(size / 256) * 32\n", " rows.append({\"์œ ํ˜•\": ty, \"ํ‰๊ท  ํฌ๊ธฐ(B)\": int(size), **{f\"{bw / 1000:g}kbps\": round(nbytes * 8 / bw, 1) for bw in (1200, 2400, 9600, 16000)}})\n", "print(pd.DataFrame(rows).to_string(index=False))\n", "print(\"-> 2.4kbps์—์„œ ํ–‰์ •์ „๋ฌธ 1๊ฑด์ด ์•ฝ 10์ดˆ ์ด์ƒ ์ฑ„๋„์„ ์ ์œ ํ•œ๋‹ค. FLASH ์ „๋‹ฌ ๋ชฉํ‘œ(10์ดˆ)๋ฅผ ์ง€ํ‚ค๋ ค๋ฉด ์šฐ์„ ์ˆœ์œ„ ํ์™€ ์กฐ๊ฐ ๋‹จ์œ„ ์ „์†ก์ด ํ•„์š”ํ•˜๋‹ค.\")\n", "\n", "# 2) ์Šคํ‚ค๋งˆ ์ •์˜: ์ „๋ฌธ ํ—ค๋”, ๋งํฌ ์กฐ๊ฑด, ์ „์ˆ  ํŒŒ๋ผ๋ฏธํ„ฐ(๊ธฐ์ƒยท์œ„์น˜์˜ค์ฐจยทํ†ต์‹ ์ง€์—ฐ)\n", "SCHEMA = {\n", " \"required\": [\"msg_id\", \"precedence\", \"msg_type\", \"src\", \"created_s\", \"payload_bytes\", \"link\", \"tactical\"],\n", " \"fields\": {\n", " \"msg_id\": {\"type\": str, \"pattern\": r\"^M\\d{4}$\"},\n", " \"precedence\": {\"type\": str, \"enum\": PREC},\n", " \"msg_type\": {\"type\": str, \"enum\": list(MSG_TYPES)},\n", " \"src\": {\"type\": str, \"pattern\": r\"^U\\d{2}$\"},\n", " \"created_s\": {\"type\": (int, float), \"min\": 0},\n", " \"payload_bytes\": {\"type\": int, \"min\": 1, \"max\": 8000},\n", " },\n", " \"link\": {\n", " \"band\": {\"type\": str, \"enum\": [\"VHF\", \"UHF\"]},\n", " \"bandwidth_bps\": {\"type\": int, \"enum\": [1200, 2400, 9600, 16000]},\n", " \"snr_db\": {\"type\": (int, float), \"min\": -10, \"max\": 40},\n", " \"mtu_bytes\": {\"type\": int, \"min\": 32, \"max\": 1024},\n", " },\n", " \"tactical\": {\n", " \"weather\": {\"type\": str, \"enum\": [\"clear\", \"rain\", \"fog\", \"snow\", \"night\"]},\n", " \"position_error_m\": {\"type\": (int, float), \"min\": 0, \"max\": 500},\n", " \"comm_delay_ms\": {\"type\": (int, float), \"min\": 0, \"max\": 30000},\n", " \"relay_hops\": {\"type\": int, \"min\": 0, \"max\": 5},\n", " },\n", "}\n", "\n", "\n", "def check(name, value, rule):\n", " \"\"\"๊ฐ’ ํ•˜๋‚˜๋ฅผ ๊ทœ์น™(type, enum, min, max, pattern)์œผ๋กœ ๊ฒ€์‚ฌํ•ด ์˜ค๋ฅ˜ ๋ชฉ๋ก์„ ๋Œ๋ ค์ค€๋‹ค.\"\"\"\n", " if not isinstance(value, rule[\"type\"]) or isinstance(value, bool):\n", " return [f\"{name}: ์ž๋ฃŒํ˜• ์˜ค๋ฅ˜({type(value).__name__})\"]\n", " errs = []\n", " if \"enum\" in rule and value not in rule[\"enum\"]:\n", " errs.append(f\"{name}: ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ ๊ฐ’ '{value}'\")\n", " if \"min\" in rule and value < rule[\"min\"]:\n", " errs.append(f\"{name}: ์ตœ์†Œ๊ฐ’ {rule['min']} ๋ฏธ๋งŒ\")\n", " if \"max\" in rule and value > rule[\"max\"]:\n", " errs.append(f\"{name}: ์ตœ๋Œ€๊ฐ’ {rule['max']} ์ดˆ๊ณผ\")\n", " if \"pattern\" in rule and not re.match(rule[\"pattern\"], value):\n", " errs.append(f\"{name}: ํ˜•์‹ ๋ถˆ์ผ์น˜ '{value}'\")\n", " return errs\n", "\n", "\n", "def validate(rec):\n", " errs = [f\"{k}: ํ•„์ˆ˜ ํ•„๋“œ ๋ˆ„๋ฝ\" for k in SCHEMA[\"required\"] if k not in rec]\n", " for k, rule in SCHEMA[\"fields\"].items():\n", " if k in rec:\n", " errs += check(k, rec[k], rule)\n", " for group in (\"link\", \"tactical\"):\n", " for k, rule in SCHEMA[group].items():\n", " if k not in rec.get(group, {}):\n", " errs.append(f\"{group}.{k}: ํ•„์ˆ˜ ํ•„๋“œ ๋ˆ„๋ฝ\")\n", " else:\n", " errs += check(f\"{group}.{k}\", rec[group][k], rule)\n", " if not errs and rec[\"payload_bytes\"] > 2000 and rec[\"precedence\"] == \"FLASH\":\n", " errs.append(\"์—…๋ฌด ๊ทœ์น™: FLASH ์ „๋ฌธ์€ 2,000bytes ์ดํ•˜๋กœ ์ž‘์„ฑ\")\n", " return errs\n", "\n", "\n", "# 3) [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์ƒ˜ํ”Œ ์ „๋ฌธ 20๊ฑด ์ƒ์„ฑ(๋ฉ”์‹œ์ง€ ์ƒ์„ฑ๊ธฐ ์‚ฌ์šฉ)\n", "rng = np.random.default_rng(1)\n", "records = [{\"msg_id\": m[\"id\"], \"precedence\": m[\"prec\"], \"msg_type\": m[\"type\"], \"src\": m[\"src\"],\n", " \"created_s\": round(m[\"t0\"], 1), \"payload_bytes\": m[\"size\"],\n", " \"link\": {\"band\": str(rng.choice([\"VHF\", \"UHF\"])), \"bandwidth_bps\": 2400, \"snr_db\": float(round(rng.uniform(3, 25), 1)), \"mtu_bytes\": 256},\n", " \"tactical\": {\"weather\": str(rng.choice([\"clear\", \"rain\", \"fog\", \"snow\", \"night\"])), \"position_error_m\": float(round(rng.uniform(3, 50), 1)),\n", " \"comm_delay_ms\": int(rng.integers(300, 3000)), \"relay_hops\": int(rng.integers(0, 3))}}\n", " for m in make_messages(400, 0.12, seed=3)[:20]]\n", "ok = sum(1 for r in records if not validate(r))\n", "print(f\"\\n์ •์ƒ ์ƒ˜ํ”Œ {len(records)}๊ฑด ์ค‘ ๊ฒ€์ฆ ํ†ต๊ณผ {ok}๊ฑด, ์˜ค๋ฅ˜ {len(records) - ok}๊ฑด\")\n", "\n", "# 4) ์˜ค๋ฅ˜ ์ฃผ์ž… ์‹œํ—˜\n", "bad = json.loads(json.dumps(records[0]))\n", "bad[\"msg_id\"] = \"MSG-1\"\n", "bad[\"precedence\"] = \"URGENT\"\n", "bad[\"link\"][\"bandwidth_bps\"] = 3000\n", "del bad[\"tactical\"][\"comm_delay_ms\"]\n", "bad[\"tactical\"][\"relay_hops\"] = 9\n", "print(\"[์˜ค๋ฅ˜ ์ฃผ์ž… ๊ฒฐ๊ณผ]\")\n", "for e in validate(bad):\n", " print(\" -\", e)\n", "flash_big = {**records[0], \"precedence\": \"FLASH\", \"payload_bytes\": 4000}\n", "print(\" -\", validate(flash_big)[0])\n", "\n", "# 5) ์Šคํ‚ค๋งˆ ๋ช…์„ธ ์š”์•ฝ\n", "rows = []\n", "for g in (\"fields\", \"link\", \"tactical\"):\n", " for k, r in SCHEMA[g].items():\n", " t = r[\"type\"].__name__ if isinstance(r[\"type\"], type) else \"number\"\n", " rows.append((\"๊ธฐ๋ณธ\" if g == \"fields\" else g, k, t, str({x: y for x, y in r.items() if x != \"type\"})[:58]))\n", "print(\"\\n[๋ฐ์ดํ„ฐ ๋ช…์„ธ ์š”์•ฝ]\")\n", "print(pd.DataFrame(rows, columns=[\"๊ทธ๋ฃน\", \"ํ•„๋“œ\", \"์ž๋ฃŒํ˜•\", \"๊ทœ์น™\"]).to_string(index=False))\n", "print(\"\\n๊ธด๊ธ‰๋„๋ณ„ ์ „๋‹ฌ ๋ชฉํ‘œ(์ดˆ):\", DEADLINE)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 01 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "[๋Œ€์—ญํญ๋ณ„ ์ „๋ฌธ 1๊ฑด ์ „์†ก์‹œ๊ฐ„(์ดˆ) - ๊ฐ€์ƒ ๋งํฌ]\n", " ์œ ํ˜• ํ‰๊ท  ํฌ๊ธฐ(B) 1.2kbps 2.4kbps 9.6kbps 16kbps\n", "CONTACT 210 1.6 0.8 0.2 0.1\n", "MEDEVAC 250 1.9 0.9 0.2 0.1\n", " SITREP 1550 11.8 5.9 1.5 0.9\n", " POSREP 90 0.8 0.4 0.1 0.1\n", " LOGREQ 750 5.6 2.8 0.7 0.4\n", " ADMIN 3000 22.6 11.3 2.8 1.7\n", "-> 2.4kbps์—์„œ ํ–‰์ •์ „๋ฌธ 1๊ฑด์ด ์•ฝ 10์ดˆ ์ด์ƒ ์ฑ„๋„์„ ์ ์œ ํ•œ๋‹ค. FLASH ์ „๋‹ฌ ๋ชฉํ‘œ(10์ดˆ)๋ฅผ ์ง€ํ‚ค๋ ค๋ฉด ์šฐ์„ ์ˆœ์œ„ ํ์™€ ์กฐ๊ฐ ๋‹จ์œ„ ์ „์†ก์ด ํ•„์š”ํ•˜๋‹ค.\n", "\n", "์ •์ƒ ์ƒ˜ํ”Œ 20๊ฑด ์ค‘ ๊ฒ€์ฆ ํ†ต๊ณผ 20๊ฑด, ์˜ค๋ฅ˜ 0๊ฑด\n", "[์˜ค๋ฅ˜ ์ฃผ์ž… ๊ฒฐ๊ณผ]\n", " - msg_id: ํ˜•์‹ ๋ถˆ์ผ์น˜ 'MSG-1'\n", " - precedence: ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ ๊ฐ’ 'URGENT'\n", " - link.bandwidth_bps: ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ ๊ฐ’ '3000'\n", " - tactical.comm_delay_ms: ํ•„์ˆ˜ ํ•„๋“œ ๋ˆ„๋ฝ\n", " - tactical.relay_hops: ์ตœ๋Œ€๊ฐ’ 5 ์ดˆ๊ณผ\n", " - ์—…๋ฌด ๊ทœ์น™: FLASH ์ „๋ฌธ์€ 2,000bytes ์ดํ•˜๋กœ ์ž‘์„ฑ\n", "\n", "[๋ฐ์ดํ„ฐ ๋ช…์„ธ ์š”์•ฝ]\n", " ๊ทธ๋ฃน ํ•„๋“œ ์ž๋ฃŒํ˜• ๊ทœ์น™\n", " ๊ธฐ๋ณธ msg_id str {'pattern': '^M\\\\d{4}$'}\n", " ๊ธฐ๋ณธ precedence str {'enum': ['FLASH', 'IMMEDIATE', 'PRIORITY', 'ROUTINE']}\n", " ๊ธฐ๋ณธ msg_type str {'enum': ['CONTACT', 'MEDEVAC', 'SITREP', 'POSREP', 'LOGRE\n", " ๊ธฐ๋ณธ src str {'pattern': '^U\\\\d{2}$'}\n", " ๊ธฐ๋ณธ created_s number {'min': 0}\n", " ๊ธฐ๋ณธ payload_bytes int {'min': 1, 'max': 8000}\n", " link band str {'enum': ['VHF', 'UHF']}\n", " link bandwidth_bps int {'enum': [1200, 2400, 9600, 16000]}\n", " link snr_db number {'min': -10, 'max': 40}\n", " link mtu_bytes int {'min': 32, 'max': 1024}\n", "tactical weather str {'enum': ['clear', 'rain', 'fog', 'snow', 'night']}\n", "tactical position_error_m number {'min': 0, 'max': 500}\n", "tactical comm_delay_ms number {'min': 0, 'max': 30000}\n", "tactical relay_hops int {'min': 0, 'max': 5}\n", "\n", "๊ธด๊ธ‰๋„๋ณ„ ์ „๋‹ฌ ๋ชฉํ‘œ(์ดˆ): {'FLASH': 10, 'IMMEDIATE': 30, 'PRIORITY': 120, 'ROUTINE': 600}\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 02. ์ „์ˆ  ๋ฉ”์‹œ์ง€(์ „๋ฌธ) ํŒจํ‚ท ํŒŒ์‹ฑ, ํฌ๊ธฐ ์••์ถ• ๋ฐ ํ—ค๋” ์ •๋ณด ์ „์ฒ˜๋ฆฌ ๋ชจ๋“ˆ ๊ตฌํ˜„\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 2.0์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "์ „์ˆ  ๋ฉ”์‹œ์ง€ ํŒจํ‚ท ํŒŒ์‹ฑ, ํ—ค๋” ์••์ถ• ๋ฐ ํŽ˜์ด๋กœ๋“œ ๋ฐ์ดํ„ฐ ์ •๊ทœํ™” ์ „์ฒ˜๋ฆฌ ๋ชจ๋“ˆ์„ ๊ตฌํ˜„ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **์žก์Œ์ด ์„ž์ธ ์›์‹œ ์ „๋ฌธ ํŒจํ‚ท์„ ํŒŒ์‹ฑํ•˜๊ณ  ๊ธด๊ธ‰๋„ ํ‘œ๊ธฐ ์ •๊ทœํ™”, CRC ๊ฒ€์‚ฌ, ์ค‘๋ณต ์ œ๊ฑฐ, ๊ฒฐ์ธก ์‹œ๊ฐ ๋ณด์ •, ํ—ค๋” ์ด์ง„ ์••์ถ•, ๋ณธ๋ฌธ ์••์ถ•์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์ „์ฒ˜๋ฆฌ ๋ชจ๋“ˆ์„ ๊ตฌํ˜„ํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **๊ธด๊ธ‰๋„ ํ‘œ๊ธฐ ํ˜ผ์žฌ, ์žฌ์ „์†ก์œผ๋กœ ์ธํ•œ ์ค‘๋ณต ์ˆ˜์‹ , ๋น„ํŠธ ์˜ค๋ฅ˜, ์ „์†ก ์ค‘ ์ž˜๋ฆผ, ๋ฏธ๋“ฑ๋ก ์†ก์‹ ๊ตญ(์Šคํ‘ธํ•‘ ์˜์‹ฌ) ํŒจํ‚ท์„ ํฌํ•จํ•œ ๊ฐ€์ƒ ์›์‹œ ํŒจํ‚ท ์ž‘์„ฑํ•˜์—ฌ ์›์ธ์„ ๋ถ„์„ํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/preprocess/c202_preprocessor.py ๋ฐ ์ „์ฒ˜๋ฆฌ ์ „ํ›„ ๋ฐ์ดํ„ฐ์…‹` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ์ „์ˆ  ํ™˜๊ฒฝ ํŠน์œ ์˜ ๋…ธ์ด์ฆˆ(์Šคํ‘ธํ•‘, ๋ฐ˜์‚ฌํŒŒ, ์„ผ์„œ ๋‹จ์ ˆ ๋“ฑ) ์ œ๊ฑฐ ํƒ€๋‹น์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ๋ฐ์ดํ„ฐ ์ •์ œ ์ฒ˜๋ฆฌ ์†๋„ 1,000๊ฑด/์ดˆ ์ด์ƒ, ์ „์ฒ˜๋ฆฌ ์˜ค๋ฅ˜์œจ 0%\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ์ •์ œ ํŒŒ์ดํ”„๋ผ์ธ ์ž๋™ํ™” ๋ฐ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค 5์ข… ํ†ต๊ณผ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: pytest ๊ธฐ๋ฐ˜ ์ „์ฒ˜๋ฆฌ ์ •ํ•ฉ์„ฑ ํ…Œ์ŠคํŠธ ๋ฐ ๊ฒฐ์ธก์น˜ ๋ณด์ •์œจ ํ™•์ธ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"๊ฐ€์ƒ ์ „์ˆ  ์ „๋ฌธ ํŒจํ‚ท(ํ…์ŠคํŠธ ํ—ค๋”+๋ณธ๋ฌธ)์„ ํŒŒ์‹ฑํ•˜๊ณ  ๊ธด๊ธ‰๋„ ํ‘œ๊ธฐ ์ •๊ทœํ™”ยทCRC ๊ฒ€์‚ฌยท์ค‘๋ณต ์ œ๊ฑฐยท๊ฒฐ์ธก ์‹œ๊ฐ ๋ณด์ •ยทํ—ค๋” ์ด์ง„ ์••์ถ•ยท๋ณธ๋ฌธ ์••์ถ•์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์ „์ฒ˜๋ฆฌ ๋ชจ๋“ˆ์„ ๊ตฌํ˜„ํ•ด ์ฒ˜๋ฆฌ ์†๋„์™€ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ํ†ต๊ณผ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•œ๋‹ค.\"\"\"\n", "import struct\n", "import time\n", "import zlib\n", "import numpy as np\n", "import pandas as pd\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์ „์ˆ  ์ „๋ฌธ(๋ฉ”์‹œ์ง€) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยทํ†ต์‹ ๋งยท์ œ์› ์ •๋ณด ์—†์Œ)\n", "# ๊ธด๊ธ‰๋„ 4๋‹จ๊ณ„(FLASH/IMMEDIATE/PRIORITY/ROUTINE)์™€ ์ „๋ฌธ ์œ ํ˜•๋ณ„ ํฌ๊ธฐ๋ฅผ ๊ฐ€์ง„ ์ „๋ฌธ์„ ๋งŒ๋“ ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PREC = [\"FLASH\", \"IMMEDIATE\", \"PRIORITY\", \"ROUTINE\"]\n", "RANK = {p: i for i, p in enumerate(PREC)}\n", "DEADLINE = {\"FLASH\": 10, \"IMMEDIATE\": 30, \"PRIORITY\": 120, \"ROUTINE\": 600} # ๊ฐ€์ƒ ์ „๋‹ฌ ๋ชฉํ‘œ ์‹œ๊ฐ„(์ดˆ)\n", "MSG_TYPES = { # ์œ ํ˜•: (๊ธด๊ธ‰๋„ ํ›„๋ณด, ํ™•๋ฅ , ๋ณธ๋ฌธ ํฌ๊ธฐ ๋ฒ”์œ„ bytes, ๋ฐœ์ƒ ๋น„์œจ)\n", " \"CONTACT\": ([\"FLASH\", \"IMMEDIATE\"], [0.7, 0.3], (120, 300), 0.08),\n", " \"MEDEVAC\": ([\"FLASH\", \"IMMEDIATE\"], [0.4, 0.6], (150, 350), 0.05),\n", " \"SITREP\": ([\"IMMEDIATE\", \"PRIORITY\"], [0.2, 0.8], (600, 2500), 0.20),\n", " \"POSREP\": ([\"PRIORITY\", \"ROUTINE\"], [0.5, 0.5], (60, 120), 0.35),\n", " \"LOGREQ\": ([\"PRIORITY\", \"ROUTINE\"], [0.4, 0.6], (300, 1200), 0.14),\n", " \"ADMIN\": ([\"ROUTINE\"], [1.0], (1000, 5000), 0.18),\n", "}\n", "\n", "\n", "def make_messages(duration=600, rate=0.12, seed=7, t_start=0.0, prefix=\"M\"):\n", " \"\"\"duration์ดˆ ๋™์•ˆ ํ‰๊ท  rate๊ฑด/์ดˆ๋กœ ๋ฐœ์ƒํ•˜๋Š” ๊ฐ€์ƒ ์ „๋ฌธ ๋ชฉ๋ก.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " n = int(rng.poisson(rate * duration))\n", " times = np.sort(rng.uniform(t_start, t_start + duration, n))\n", " types = list(MSG_TYPES)\n", " msgs = []\n", " for i, t0 in enumerate(times):\n", " ty = types[rng.choice(len(types), p=[MSG_TYPES[k][3] for k in types])]\n", " cands, p, (lo, hi), _ = MSG_TYPES[ty]\n", " msgs.append({\"id\": f\"{prefix}{i:04d}\", \"t0\": float(t0), \"type\": ty, \"prec\": str(rng.choice(cands, p=p)),\n", " \"size\": int(rng.integers(lo, hi + 1)), \"src\": f\"U{int(rng.integers(1, 9)):02d}\"})\n", " return msgs\n", "\n", "\n", "class Link:\n", " \"\"\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ํ˜‘๋Œ€์—ญ ๋ฌด์„  ๋งํฌ(๊ธฐ๋ณธ 2.4kbps). ์ดˆ ๋‹จ์œ„ ์ƒํƒœ(good/bad/jam)์— ๋”ฐ๋ผ\n", " ๋น„ํŠธ์˜ค๋ฅ˜์œจ์ด ๋ฐ”๋€Œ๊ณ (Gilbert-Elliott ๋ชจ๋ธ), ๊ตฌ๊ฐ„๋ณ„ ๋Œ€์—ญํญ ๊ฐ์†Œ๋„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.\"\"\"\n", " BER = {\"good\": 1e-5, \"bad\": 2e-4, \"jam\": 1.2e-3}\n", "\n", " def __init__(self, horizon=4000, bw_bps=2400, seed=1, p_gb=0.02, p_bg=0.15, jam=(), bw_drop=(), rtt=(1.2, 0.4)):\n", " rng = np.random.default_rng(seed)\n", " st, s = [], \"good\"\n", " for _ in range(int(horizon) + 1):\n", " s = (\"bad\" if rng.random() < p_gb else \"good\") if s == \"good\" else (\"good\" if rng.random() < p_bg else \"bad\")\n", " st.append(s)\n", " for a, b in jam:\n", " for k in range(int(a), min(int(b), len(st))):\n", " st[k] = \"jam\"\n", " self.states, self.bw_bps, self.bw_drop, self.rtt_par = st, bw_bps, list(bw_drop), rtt\n", "\n", " def state(self, t):\n", " return self.states[min(int(t), len(self.states) - 1)]\n", "\n", " def bw(self, t):\n", " f = 1.0\n", " for a, b, fac in self.bw_drop:\n", " if a <= t < b:\n", " f = fac\n", " return self.bw_bps * f\n", "\n", " def p_loss(self, nbytes, t):\n", " return 1 - (1 - self.BER[self.state(t)]) ** (8 * nbytes)\n", "\n", " def rtt(self, rng):\n", " return self.rtt_par[0] + rng.exponential(self.rtt_par[1])\n", "# ---------------------------------------------------------------------------\n", "\n", "PREC_ALIAS = {\"Z\": \"FLASH\", \"FLASH\": \"FLASH\", \"O\": \"IMMEDIATE\", \"IMMED\": \"IMMEDIATE\", \"IMMEDIATE\": \"IMMEDIATE\",\n", " \"P\": \"PRIORITY\", \"PRIORITY\": \"PRIORITY\", \"R\": \"ROUTINE\", \"ROUTINE\": \"ROUTINE\"}\n", "ROSTER = {f\"U{i:02d}\" for i in range(1, 9)} # ๊ฐ€์ƒ ๋ง ๊ฐ€์ž…๊ตญ ๋ชฉ๋ก\n", "TYPES = list(MSG_TYPES)\n", "\n", "\n", "def make_body(m, rng):\n", " \"\"\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] 'KEY=VALUE;' ํ˜•์‹์˜ ์ •ํ˜• ๋ณธ๋ฌธ(์‹ค์ œ ์ „๋ฌธ ์–‘์‹ ์•„๋‹˜).\"\"\"\n", " lines, k = [], 0\n", " while sum(map(len, lines)) < m[\"size\"]:\n", " lines.append(f\"L{k:02d}={m['type']} UNIT {m['src']} ITEM {int(rng.integers(1, 40)):02d} QTY {int(rng.integers(0, 99)):02d} STAT OK;\")\n", " k += 1\n", " return \"\".join(lines)[:m[\"size\"]]\n", "\n", "\n", "def make_raw_packets(msgs, seed=5):\n", " \"\"\"์ „์ˆ  ํ™˜๊ฒฝ ์žก์Œ์„ ๋„ฃ์€ ์›์‹œ ํŒจํ‚ท ์ค„: ๊ธด๊ธ‰๋„ ํ‘œ๊ธฐ ํ˜ผ์žฌ, ์ค‘๋ณต ์ˆ˜์‹ , ๋น„ํŠธ ์˜ค๋ฅ˜(CRC ๋ถˆ์ผ์น˜), ์‹œ๊ฐ ๋ˆ„๋ฝ, ์ž˜๋ฆผ, ๋ฏธ๋“ฑ๋ก ์†ก์‹ ๊ตญ.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " out = []\n", " for m in msgs:\n", " body = make_body(m, rng)\n", " prec = rng.choice([PREC[RANK[m[\"prec\"]]], PREC[RANK[m[\"prec\"]]].lower(), \"ZOPR\"[RANK[m[\"prec\"]]], \" \" + \"ZOPR\"[RANK[m[\"prec\"]]] + \" \"])\n", " dtg = \"\" if rng.random() < 0.05 else f\"{m['t0']:.1f}\"\n", " src = m[\"src\"] if rng.random() > 0.02 else \"X77\"\n", " line = f\"ZCZC|ID={m['id']}|PREC={prec}|TYPE={m['type']}|SRC={src}|DTG={dtg}|LEN={len(body)}|CRC={zlib.crc32(body.encode()):08x}|BODY={body}\"\n", " r = rng.random()\n", " if r < 0.03: # ๋น„ํŠธ ์˜ค๋ฅ˜: ๋ณธ๋ฌธ ์ผ๋ถ€ ๋ณ€ํ˜•\n", " line = line[:-5] + \"#\" + line[-4:]\n", " elif r < 0.05: # ์ „์†ก ์ค‘ ์ž˜๋ฆผ\n", " line = line[:int(len(line) * 0.4)]\n", " out.append(line)\n", " if rng.random() < 0.06: # ์žฌ์ „์†ก์œผ๋กœ ์ธํ•œ ์ค‘๋ณต ์ˆ˜์‹ \n", " out.append(line)\n", " return out\n", "\n", "\n", "class ParseError(ValueError):\n", " pass\n", "\n", "\n", "def parse(line):\n", " if not line.startswith(\"ZCZC|\"):\n", " raise ParseError(\"์‹œ์ž‘ ํ‘œ์ง€ ์—†์Œ\")\n", " kv = dict(p.split(\"=\", 1) for p in line[5:].split(\"|\") if \"=\" in p)\n", " for k in (\"ID\", \"PREC\", \"TYPE\", \"SRC\", \"DTG\", \"LEN\", \"CRC\", \"BODY\"):\n", " if k not in kv:\n", " raise ParseError(f\"ํ•„๋“œ ๋ˆ„๋ฝ({k})\")\n", " prec = PREC_ALIAS.get(kv[\"PREC\"].strip().upper())\n", " if prec is None:\n", " raise ParseError(\"๊ธด๊ธ‰๋„ ํ‘œ๊ธฐ ์˜ค๋ฅ˜\")\n", " if kv[\"TYPE\"] not in MSG_TYPES:\n", " raise ParseError(\"์ „๋ฌธ ์œ ํ˜• ์˜ค๋ฅ˜\")\n", " if kv[\"SRC\"] not in ROSTER:\n", " raise ParseError(\"๋ฏธ๋“ฑ๋ก ์†ก์‹ ๊ตญ(์Šคํ‘ธํ•‘ ์˜์‹ฌ)\")\n", " if int(kv[\"LEN\"]) != len(kv[\"BODY\"]) or f\"{zlib.crc32(kv['BODY'].encode()):08x}\" != kv[\"CRC\"]:\n", " raise ParseError(\"CRC/๊ธธ์ด ๋ถˆ์ผ์น˜(๋น„ํŠธ ์˜ค๋ฅ˜)\")\n", " return {\"id\": kv[\"ID\"], \"prec\": prec, \"type\": kv[\"TYPE\"], \"src\": kv[\"SRC\"],\n", " \"dtg\": float(kv[\"DTG\"]) if kv[\"DTG\"] else np.nan, \"body\": kv[\"BODY\"], \"hdr_bytes\": len(line) - len(kv[\"BODY\"])}\n", "\n", "\n", "def pack_header(r):\n", " \"\"\"ํ—ค๋” ์ด์ง„ ์••์ถ•: ID(2) + ๊ธด๊ธ‰๋„ยท์œ ํ˜•ยท์†ก์‹ ๊ตญ(2) + ์‹œ๊ฐ(4, 0.1์ดˆ) + ๊ธธ์ด(2) + CRC(4) = 14bytes.\"\"\"\n", " flags = RANK[r[\"prec\"]] << 12 | TYPES.index(r[\"type\"]) << 8 | int(r[\"src\"][1:])\n", " return struct.pack(\">HHIHI\", int(r[\"id\"][1:]), flags, int(round(r[\"dtg\"] * 10)), len(r[\"body\"]), zlib.crc32(r[\"body\"].encode()))\n", "\n", "\n", "def unpack_header(b):\n", " i, flags, dtg, ln, crc = struct.unpack(\">HHIHI\", b)\n", " return {\"id\": f\"M{i:04d}\", \"prec\": PREC[flags >> 12], \"type\": TYPES[(flags >> 8) & 0xF], \"src\": f\"U{flags & 0xFF:02d}\", \"dtg\": dtg / 10, \"len\": ln}\n", "\n", "\n", "ZLEVEL = {1200: 9, 2400: 9, 9600: 6, 16000: 1} # ๋Œ€์—ญํญ์ด ์ข์„์ˆ˜๋ก ์••์ถ•์„ ๊ฐ•ํ•˜๊ฒŒ\n", "\n", "\n", "def preprocess(lines, bw_bps=2400):\n", " good, quarantine, seen = [], [], set()\n", " for ln in lines:\n", " try:\n", " r = parse(ln)\n", " except (ParseError, ValueError) as e:\n", " quarantine.append(str(e)); continue\n", " if r[\"id\"] in seen:\n", " quarantine.append(\"์ค‘๋ณต ์ˆ˜์‹ (์ œ๊ฑฐ)\"); continue\n", " seen.add(r[\"id\"]); good.append(r)\n", " good.sort(key=lambda r: int(r[\"id\"][1:]))\n", " dtg = pd.Series([r[\"dtg\"] for r in good], dtype=float)\n", " miss = int(dtg.isna().sum())\n", " filled = dtg.interpolate(limit_direction=\"both\") # ์•ž๋’ค ์ „๋ฌธ ์‹œ๊ฐ์œผ๋กœ ์„ ํ˜• ๋ณด๊ฐ„\n", " for r, v in zip(good, filled):\n", " r[\"dtg_filled\"] = bool(np.isnan(r[\"dtg\"])); r[\"dtg\"] = float(v)\n", " r[\"hdr_packed\"] = pack_header(r)\n", " r[\"body_z\"] = zlib.compress(r[\"body\"].encode(), ZLEVEL[bw_bps])\n", " return good, quarantine, miss\n", "\n", "\n", "msgs = make_messages(1200, 0.21, seed=7)\n", "lines = make_raw_packets(msgs)\n", "t0 = time.perf_counter()\n", "good, quarantine, miss = preprocess(lines)\n", "dt = time.perf_counter() - t0\n", "print(f\"์›์‹œ ํŒจํ‚ท {len(lines)}์ค„ -> ์ •์ƒ {len(good)}๊ฑด, ๊ฒฉ๋ฆฌ {len(quarantine)}๊ฑด / ์ฒ˜๋ฆฌ ์†๋„ {len(lines) / dt:,.0f}๊ฑด/์ดˆ (๋ชฉํ‘œ 1,000๊ฑด/์ดˆ ์ด์ƒ)\")\n", "print(\"์ฒ˜๋ฆฌ ์ค‘ ์˜ˆ์™ธ๋กœ ๋ฉˆ์ถ˜ ๊ฑด์ˆ˜ 0๊ฑด(์˜ค๋ฅ˜์œจ 0%) - ๋น„์ •์ƒ ํŒจํ‚ท์€ ์‚ฌ์œ ์™€ ํ•จ๊ป˜ ๊ฒฉ๋ฆฌ\")\n", "print(\"๊ฒฉ๋ฆฌ ์‚ฌ์œ :\", pd.Series(quarantine).value_counts().to_dict())\n", "print(f\"DTG ๊ฒฐ์ธก {miss}๊ฑด ์ค‘ ๋ณด์ • {sum(r['dtg_filled'] for r in good)}๊ฑด (๋ณด์ •์œจ {sum(r['dtg_filled'] for r in good) / max(miss, 1):.0%})\")\n", "\n", "# ์ „์ฒ˜๋ฆฌ ์ „ํ›„ ํฌ๊ธฐ ๋น„๊ต(์ „๋ฌธ ์œ ํ˜•๋ณ„ ํ‰๊ท  bytes)\n", "df = pd.DataFrame([{\"์œ ํ˜•\": r[\"type\"], \"์›๋ณธ ํ—ค๋”\": r[\"hdr_bytes\"], \"์••์ถ• ํ—ค๋”\": len(r[\"hdr_packed\"]),\n", " \"์›๋ณธ ๋ณธ๋ฌธ\": len(r[\"body\"]), \"์••์ถ• ๋ณธ๋ฌธ\": len(r[\"body_z\"])} for r in good])\n", "tab = df.groupby(\"์œ ํ˜•\").mean().round(0).astype(int)\n", "tab[\"์ „์ฒด ์ ˆ๊ฐ๋ฅ \"] = (1 - (tab[\"์••์ถ• ํ—ค๋”\"] + tab[\"์••์ถ• ๋ณธ๋ฌธ\"]) / (tab[\"์›๋ณธ ํ—ค๋”\"] + tab[\"์›๋ณธ ๋ณธ๋ฌธ\"])).round(3)\n", "print(\"\\n[์ „์ฒ˜๋ฆฌ ์ „ํ›„ ํ‰๊ท  ํฌ๊ธฐ(bytes)]\")\n", "print(tab.to_string())\n", "tot0, tot1 = (df[\"์›๋ณธ ํ—ค๋”\"] + df[\"์›๋ณธ ๋ณธ๋ฌธ\"]).sum(), (df[\"์••์ถ• ํ—ค๋”\"] + df[\"์••์ถ• ๋ณธ๋ฌธ\"]).sum()\n", "print(f\"์ „์ฒด {tot0:,}B -> {tot1:,}B, 2.4kbps ๊ธฐ์ค€ ์ „์†ก์‹œ๊ฐ„ {tot0 * 8 / 2400 / 60:.1f}๋ถ„ -> {tot1 * 8 / 2400 / 60:.1f}๋ถ„\")\n", "print(\"์ƒํ™ฉ๋ณ„ ์••์ถ• ์ˆ˜์ค€(๋Œ€์—ญํญ bps: zlib ์ˆ˜์ค€):\", ZLEVEL)\n", "print(\"์ฃผ์˜: ๊ฐ€์ƒ ๋ณธ๋ฌธ์€ ์ •ํ˜• ๋ฐ˜๋ณต ๋ฌธ๊ตฌ๋ผ ์••์ถ•๋ฅ ์ด ๋†’๊ฒŒ ๋‚˜์˜จ๋‹ค. ์ž์œ  ์„œ์ˆ  ๋ณธ๋ฌธ์€ ์ ˆ๊ฐ๋ฅ ์ด ์ด๋ณด๋‹ค ๋‚ฎ๋‹ค.\")\n", "\n", "# ๋‹จ์œ„ ํ…Œ์ŠคํŠธ 5์ข…(+1): ์กฐ๊ฑด์„ ์•Œ๊ณ  ์žˆ๋Š” ์‹œํ—˜์šฉ ํŒจํ‚ท์œผ๋กœ ํ™•์ธ\n", "def mk(prec=\"Z\", src=\"U01\", body=\"L00=TEST UNIT U01;\", crc=None):\n", " return f\"ZCZC|ID=M9999|PREC={prec}|TYPE=CONTACT|SRC={src}|DTG=1.0|LEN={len(body)}|CRC={crc or f'{zlib.crc32(body.encode()):08x}'}|BODY={body}\"\n", "\n", "\n", "def raises(line):\n", " try:\n", " parse(line)\n", " return False\n", " except ParseError:\n", " return True\n", "\n", "\n", "r0 = good[0]\n", "tests = {\n", " \"์ •์ƒ ํŒจํ‚ท ํŒŒ์‹ฑ\": parse(mk())[\"prec\"] == \"FLASH\",\n", " \"๊ธด๊ธ‰๋„ ํ‘œ๊ธฐ ์ •๊ทœํ™”(' o ' -> IMMEDIATE)\": parse(mk(prec=\" o \"))[\"prec\"] == \"IMMEDIATE\",\n", " \"CRC ๋ถˆ์ผ์น˜ ๊ฒ€์ถœ\": raises(mk(crc=\"00000000\")),\n", " \"๋ฏธ๋“ฑ๋ก ์†ก์‹ ๊ตญ ๊ฒฉ๋ฆฌ\": raises(mk(src=\"X77\")),\n", " \"์ค‘๋ณต ์ˆ˜์‹  ์ œ๊ฑฐ\": len(preprocess([mk(), mk()])[0]) == 1 and len({r[\"id\"] for r in good}) == len(good),\n", " \"๊ฒฐ์ธก ์‹œ๊ฐ ๋ณด๊ฐ„ + ํ—ค๋” ์••์ถ• ์™•๋ณต ์ผ์น˜\": not any(np.isnan(r[\"dtg\"]) for r in good) and unpack_header(r0[\"hdr_packed\"])[\"id\"] == r0[\"id\"],\n", "}\n", "for k, v in tests.items():\n", " print(f\"[{'PASS' if v else 'FAIL'}] {k}\")\n", "print(f\"๋‹จ์œ„ ํ…Œ์ŠคํŠธ {sum(tests.values())}/{len(tests)} ํ†ต๊ณผ\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 02 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "์›์‹œ ํŒจํ‚ท 278์ค„ -> ์ •์ƒ 241๊ฑด, ๊ฒฉ๋ฆฌ 37๊ฑด / ์ฒ˜๋ฆฌ ์†๋„ 19,781๊ฑด/์ดˆ (๋ชฉํ‘œ 1,000๊ฑด/์ดˆ ์ด์ƒ)\n", "์ฒ˜๋ฆฌ ์ค‘ ์˜ˆ์™ธ๋กœ ๋ฉˆ์ถ˜ ๊ฑด์ˆ˜ 0๊ฑด(์˜ค๋ฅ˜์œจ 0%) - ๋น„์ •์ƒ ํŒจํ‚ท์€ ์‚ฌ์œ ์™€ ํ•จ๊ป˜ ๊ฒฉ๋ฆฌ\n", "๊ฒฉ๋ฆฌ ์‚ฌ์œ : {'์ค‘๋ณต ์ˆ˜์‹ (์ œ๊ฑฐ)': 18, 'CRC/๊ธธ์ด ๋ถˆ์ผ์น˜(๋น„ํŠธ ์˜ค๋ฅ˜)': 11, '๋ฏธ๋“ฑ๋ก ์†ก์‹ ๊ตญ(์Šคํ‘ธํ•‘ ์˜์‹ฌ)': 5, 'ํ•„๋“œ ๋ˆ„๋ฝ(BODY)': 3}\n", "DTG ๊ฒฐ์ธก 15๊ฑด ์ค‘ ๋ณด์ • 15๊ฑด (๋ณด์ •์œจ 100%)\n", "\n", "[์ „์ฒ˜๋ฆฌ ์ „ํ›„ ํ‰๊ท  ํฌ๊ธฐ(bytes)]\n", " ์›๋ณธ ํ—ค๋” ์••์ถ• ํ—ค๋” ์›๋ณธ ๋ณธ๋ฌธ ์••์ถ• ๋ณธ๋ฌธ ์ „์ฒด ์ ˆ๊ฐ๋ฅ \n", "์œ ํ˜• \n", "ADMIN 80 14 3506 555 0.841\n", "CONTACT 81 14 227 93 0.653\n", "LOGREQ 81 14 760 178 0.772\n", "MEDEVAC 80 14 206 91 0.633\n", "POSREP 80 14 89 63 0.544\n", "SITREP 82 14 1666 303 0.819\n", "์ „์ฒด 276,195B -> 54,134B, 2.4kbps ๊ธฐ์ค€ ์ „์†ก์‹œ๊ฐ„ 15.3๋ถ„ -> 3.0๋ถ„\n", "์ƒํ™ฉ๋ณ„ ์••์ถ• ์ˆ˜์ค€(๋Œ€์—ญํญ bps: zlib ์ˆ˜์ค€): {1200: 9, 2400: 9, 9600: 6, 16000: 1}\n", "์ฃผ์˜: ๊ฐ€์ƒ ๋ณธ๋ฌธ์€ ์ •ํ˜• ๋ฐ˜๋ณต ๋ฌธ๊ตฌ๋ผ ์••์ถ•๋ฅ ์ด ๋†’๊ฒŒ ๋‚˜์˜จ๋‹ค. ์ž์œ  ์„œ์ˆ  ๋ณธ๋ฌธ์€ ์ ˆ๊ฐ๋ฅ ์ด ์ด๋ณด๋‹ค ๋‚ฎ๋‹ค.\n", "[PASS] ์ •์ƒ ํŒจํ‚ท ํŒŒ์‹ฑ\n", "[PASS] ๊ธด๊ธ‰๋„ ํ‘œ๊ธฐ ์ •๊ทœํ™”(' o ' -> IMMEDIATE)\n", "[PASS] CRC ๋ถˆ์ผ์น˜ ๊ฒ€์ถœ\n", "[PASS] ๋ฏธ๋“ฑ๋ก ์†ก์‹ ๊ตญ ๊ฒฉ๋ฆฌ\n", "[PASS] ์ค‘๋ณต ์ˆ˜์‹  ์ œ๊ฑฐ\n", "[PASS] ๊ฒฐ์ธก ์‹œ๊ฐ ๋ณด๊ฐ„ + ํ—ค๋” ์••์ถ• ์™•๋ณต ์ผ์น˜\n", "๋‹จ์œ„ ํ…Œ์ŠคํŠธ 6/6 ํ†ต๊ณผ\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 03. ๋ฉ”์‹œ์ง€ ๊ธด๊ธ‰๋„(Flash/Immediate/Priority/Routine) ๊ธฐ๋ฐ˜ 1์ฐจ ์šฐ์„ ์ˆœ์œ„ ํ(Queue) ๊ตฌํ˜„\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 2.5์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "์ „๋ฌธ ๊ธด๊ธ‰๋„(Flash/Immediate/Priority/Routine) ๊ธฐ๋ฐ˜ 1์ฐจ ๋ฉ”์‹œ์ง€ ์šฐ์„ ์ˆœ์œ„ ํ(Queue)๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **์ „๋ฌธ ๊ธด๊ธ‰๋„(FLASH/IMMEDIATE/PRIORITY/ROUTINE)์— ๋”ฐ๋ผ ๋จผ์ € ๋ณด๋‚ผ ์ „๋ฌธ์„ ๊ณ ๋ฅด๋Š” 1์ฐจ ์šฐ์„ ์ˆœ์œ„ ํ๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ๋„์ฐฉ์ˆœ ์ „์†ก๊ณผ ์„ฑ๋Šฅ์„ ๋น„๊ตํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **heapq๋กœ ๊ธด๊ธ‰๋„ ์šฐ์„ ยท๊ฐ™์€ ๊ธด๊ธ‰๋„๋Š” ๋„์ฐฉ์ˆœ์„ ์ง€ํ‚ค๋Š” ํ๋ฅผ ๋งŒ๋“ค๊ณ  ๋™์ž‘ ํ™•์ธํ•˜๊ณ  ์œ ํšจ์„ฑ์„ ์ ๊ฒ€ํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/models/c202_baseline_model.py ๋ฐ ๊ธฐ๋ณธ ์ถ”๋ก  ๊ฒฐ๊ณผ ๋ณด๊ณ ์„œ` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ๋…ผ๋ฆฌ์  ํ๋ฆ„ ๋ฐ ๊ตฐ์‚ฌ์  ํŒ๋‹จ ๊ธฐ์ค€์˜ ํˆฌ๋ช…์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ์…‹ ๋Œ€์ƒ ํƒ์ง€/์ฒ˜๋ฆฌ ์ •ํ™•๋„ 75% ์ด์ƒ ๋‹ฌ์„ฑ\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ๋‹จ์œ„ ๊ธฐ๋Šฅ ๋™์ž‘ ํ™•์ธ ๋ฐ ๊ธฐ๋ณธ ์‹œ๋‚˜๋ฆฌ์˜ค ์‹คํ–‰ ๋กœ๊ทธ ์ƒ์„ฑ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ๊ธฐ์ค€ ๋ฒค์น˜๋งˆํฌ ๋ฐ์ดํ„ฐ์…‹ ๋Œ€์ƒ ์ถ”๋ก  ๊ฒฐ๊ณผ ์˜ค์ฐจ์œจ ๋น„๊ต\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"์ „๋ฌธ ๊ธด๊ธ‰๋„(FLASH/IMMEDIATE/PRIORITY/ROUTINE) ๊ธฐ๋ฐ˜ 1์ฐจ ์šฐ์„ ์ˆœ์œ„ ํ(heapq)๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ , ๊ฐ€์ƒ ํ˜‘๋Œ€์—ญ ๋งํฌ ์ „์†ก ์‹œ๋ฎฌ๋ ˆ์ด์…˜์œผ๋กœ ๋„์ฐฉ์ˆœ(FIFO) ์ „์†ก๊ณผ ๊ธด๊ธ‰๋„๋ณ„ ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ (์ฒ˜๋ฆฌ ์ •ํ™•๋„)์„ ๋น„๊ตํ•œ๋‹ค.\"\"\"\n", "import heapq\n", "import itertools\n", "import time\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์ „์ˆ  ์ „๋ฌธ(๋ฉ”์‹œ์ง€) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยทํ†ต์‹ ๋งยท์ œ์› ์ •๋ณด ์—†์Œ)\n", "# ๊ธด๊ธ‰๋„ 4๋‹จ๊ณ„(FLASH/IMMEDIATE/PRIORITY/ROUTINE)์™€ ์ „๋ฌธ ์œ ํ˜•๋ณ„ ํฌ๊ธฐ๋ฅผ ๊ฐ€์ง„ ์ „๋ฌธ์„ ๋งŒ๋“ ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PREC = [\"FLASH\", \"IMMEDIATE\", \"PRIORITY\", \"ROUTINE\"]\n", "RANK = {p: i for i, p in enumerate(PREC)}\n", "DEADLINE = {\"FLASH\": 10, \"IMMEDIATE\": 30, \"PRIORITY\": 120, \"ROUTINE\": 600} # ๊ฐ€์ƒ ์ „๋‹ฌ ๋ชฉํ‘œ ์‹œ๊ฐ„(์ดˆ)\n", "MSG_TYPES = { # ์œ ํ˜•: (๊ธด๊ธ‰๋„ ํ›„๋ณด, ํ™•๋ฅ , ๋ณธ๋ฌธ ํฌ๊ธฐ ๋ฒ”์œ„ bytes, ๋ฐœ์ƒ ๋น„์œจ)\n", " \"CONTACT\": ([\"FLASH\", \"IMMEDIATE\"], [0.7, 0.3], (120, 300), 0.08),\n", " \"MEDEVAC\": ([\"FLASH\", \"IMMEDIATE\"], [0.4, 0.6], (150, 350), 0.05),\n", " \"SITREP\": ([\"IMMEDIATE\", \"PRIORITY\"], [0.2, 0.8], (600, 2500), 0.20),\n", " \"POSREP\": ([\"PRIORITY\", \"ROUTINE\"], [0.5, 0.5], (60, 120), 0.35),\n", " \"LOGREQ\": ([\"PRIORITY\", \"ROUTINE\"], [0.4, 0.6], (300, 1200), 0.14),\n", " \"ADMIN\": ([\"ROUTINE\"], [1.0], (1000, 5000), 0.18),\n", "}\n", "\n", "\n", "def make_messages(duration=600, rate=0.12, seed=7, t_start=0.0, prefix=\"M\"):\n", " \"\"\"duration์ดˆ ๋™์•ˆ ํ‰๊ท  rate๊ฑด/์ดˆ๋กœ ๋ฐœ์ƒํ•˜๋Š” ๊ฐ€์ƒ ์ „๋ฌธ ๋ชฉ๋ก.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " n = int(rng.poisson(rate * duration))\n", " times = np.sort(rng.uniform(t_start, t_start + duration, n))\n", " types = list(MSG_TYPES)\n", " msgs = []\n", " for i, t0 in enumerate(times):\n", " ty = types[rng.choice(len(types), p=[MSG_TYPES[k][3] for k in types])]\n", " cands, p, (lo, hi), _ = MSG_TYPES[ty]\n", " msgs.append({\"id\": f\"{prefix}{i:04d}\", \"t0\": float(t0), \"type\": ty, \"prec\": str(rng.choice(cands, p=p)),\n", " \"size\": int(rng.integers(lo, hi + 1)), \"src\": f\"U{int(rng.integers(1, 9)):02d}\"})\n", " return msgs\n", "\n", "\n", "class Link:\n", " \"\"\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ํ˜‘๋Œ€์—ญ ๋ฌด์„  ๋งํฌ(๊ธฐ๋ณธ 2.4kbps). ์ดˆ ๋‹จ์œ„ ์ƒํƒœ(good/bad/jam)์— ๋”ฐ๋ผ\n", " ๋น„ํŠธ์˜ค๋ฅ˜์œจ์ด ๋ฐ”๋€Œ๊ณ (Gilbert-Elliott ๋ชจ๋ธ), ๊ตฌ๊ฐ„๋ณ„ ๋Œ€์—ญํญ ๊ฐ์†Œ๋„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.\"\"\"\n", " BER = {\"good\": 1e-5, \"bad\": 2e-4, \"jam\": 1.2e-3}\n", "\n", " def __init__(self, horizon=4000, bw_bps=2400, seed=1, p_gb=0.02, p_bg=0.15, jam=(), bw_drop=(), rtt=(1.2, 0.4)):\n", " rng = np.random.default_rng(seed)\n", " st, s = [], \"good\"\n", " for _ in range(int(horizon) + 1):\n", " s = (\"bad\" if rng.random() < p_gb else \"good\") if s == \"good\" else (\"good\" if rng.random() < p_bg else \"bad\")\n", " st.append(s)\n", " for a, b in jam:\n", " for k in range(int(a), min(int(b), len(st))):\n", " st[k] = \"jam\"\n", " self.states, self.bw_bps, self.bw_drop, self.rtt_par = st, bw_bps, list(bw_drop), rtt\n", "\n", " def state(self, t):\n", " return self.states[min(int(t), len(self.states) - 1)]\n", "\n", " def bw(self, t):\n", " f = 1.0\n", " for a, b, fac in self.bw_drop:\n", " if a <= t < b:\n", " f = fac\n", " return self.bw_bps * f\n", "\n", " def p_loss(self, nbytes, t):\n", " return 1 - (1 - self.BER[self.state(t)]) ** (8 * nbytes)\n", "\n", " def rtt(self, rng):\n", " return self.rtt_par[0] + rng.exponential(self.rtt_par[1])\n", "# ---------------------------------------------------------------------------\n", "\n", "# ---- ์šฐ์„ ์ˆœ์œ„ ํยท์žฌ์ „์†ก ์ด์‚ฐ ์‚ฌ๊ฑด ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ ๊ณตํ†ต ํ•จ์ˆ˜ ----\n", "HDR = 32 # ์ „๋ฌธ ์กฐ๊ฐ(ํŒจํ‚ท) ํ—ค๋” bytes (๋ฏธ์…˜ 2์˜ ํ—ค๋” ์••์ถ• ์ ์šฉ ์‹œ 12 bytes)\n", "\n", "\n", "def policy_key(m, t, policy, aging):\n", " if policy == \"fifo\": # ๋„์ฐฉ ์ˆœ์„œ\n", " return (m[\"t0\"],)\n", " if policy == \"strict\": # ๊ธด๊ธ‰๋„ ์šฐ์„ , ๊ฐ™์œผ๋ฉด ๋„์ฐฉ ์ˆœ์„œ\n", " return (RANK[m[\"prec\"]], m[\"t0\"])\n", " # aging: ๊ธด๊ธ‰๋„ + (๋Œ€๊ธฐ์‹œ๊ฐ„ / ์ „๋‹ฌ ๋ชฉํ‘œ์‹œ๊ฐ„) ๊ฐ€์ค‘. ์˜ค๋ž˜ ๊ธฐ๋‹ค๋ฆฐ ์ „๋ฌธ๊ณผ ๊ธฐํ•œ์ด ์ž„๋ฐ•ํ•œ ์ „๋ฌธ์ด ์•ž์œผ๋กœ ์˜จ๋‹ค.\n", " return (RANK[m[\"prec\"]] - aging * (t - m[\"t0\"]) / DEADLINE[m[\"prec\"]], m[\"t0\"])\n", "\n", "\n", "def simulate(msgs, link, policy=\"strict\", mtu=256, preempt=True, rto_mode=\"fixed\", rto_s=4.0, aging=0.0,\n", " redundancy=False, adapt_mtu=False, hdr=HDR, seed=0, until=None, admit=None):\n", " \"\"\"์ „๋ฌธ ๋ชฉ๋ก์„ ๋งํฌ๋กœ ๋ณด๋‚ด๋Š” ์ด์‚ฐ ์‚ฌ๊ฑด ์‹œ๋ฎฌ๋ ˆ์ด์…˜.\n", " - ์ „๋ฌธ์€ mtu ํฌ๊ธฐ ์กฐ๊ฐ์œผ๋กœ ๋‚˜๋ˆ  ๋ณด๋‚ธ๋‹ค. ์กฐ๊ฐ์ด ์†์‹ค๋˜๋ฉด RTO ํ›„ ์žฌ์ „์†กํ•œ๋‹ค(์ž๋ฃŒ๋Š” ๋ฒ„๋ฆฌ์ง€ ์•Š์Œ).\n", " - preempt=False๋ฉด ํ•œ ์ „๋ฌธ์˜ ์กฐ๊ฐ์„ ๋๊นŒ์ง€ ์—ฐ์† ์ „์†กํ•œ๋‹ค(ํฐ ์ „๋ฌธ์ด ๊ธด๊ธ‰ ์ „๋ฌธ์„ ๋ง‰๋Š” ํ˜„์ƒ ์žฌํ˜„).\n", " - rto_mode='adaptive'๋ฉด ์™•๋ณต์‹œ๊ฐ„(RTT) ์ถ”์ •๊ฐ’์œผ๋กœ RTO๋ฅผ ๊ณ„์‚ฐํ•œ๋‹ค(Jacobson ๋ฐฉ์‹).\n", " - redundancy=True๋ฉด ๋งํฌ ์—ดํ™” ์ถ”์ • ์‹œ FLASHยทIMMEDIATE ์กฐ๊ฐ์„ 2ํšŒ ์—ฐ์† ์ „์†กํ•œ๋‹ค.\n", " - adapt_mtu=True๋ฉด ๋งํฌ ์—ดํ™” ์ถ”์ • ์‹œ ์กฐ๊ฐ ํฌ๊ธฐ๋ฅผ ์ค„์ธ๋‹ค.\n", " - admit(m, t)์ด False์ธ ์ „๋ฌธ์€ ์ƒˆ ์กฐ๊ฐ์„ ๋ณด๋‚ด์ง€ ์•Š๊ณ  ์ €์žฅํ•ด ๋‘”๋‹ค(์„ฑ๋Šฅ ์ €ํ•˜ ๋ชจ๋“œ).\"\"\"\n", " rng = np.random.default_rng(seed)\n", " pend = sorted(({**m, \"unsent\": m[\"size\"], \"ready\": [], \"frags\": [], \"done\": None, \"first\": None} for m in msgs), key=lambda m: m[\"t0\"])\n", " t, i, active, cur = 0.0, 0, [], None\n", " srtt, rttvar, loss = None, None, 0.0\n", " st = {\"tx\": 0, \"retx\": 0, \"spurious\": 0, \"bytes\": 0, \"decide_ms\": [], \"flag\": {}, \"log\": []}\n", " until = until if until is not None else (pend[-1][\"t0\"] if pend else 0) + 6000\n", " while (i < len(pend) or active) and t < until:\n", " while i < len(pend) and pend[i][\"t0\"] <= t:\n", " active.append(pend[i]); i += 1\n", " td = time.perf_counter()\n", " for m in active: # ACK๊ฐ€ ์ด๋ฏธ ๋„์ฐฉํ•œ ์žฌ์ „์†ก ์˜ˆ์•ฝ์€ ์ทจ์†Œ\n", " m[\"ready\"] = [e for e in m[\"ready\"] if not (e[1][\"ack\"] is not None and e[1][\"ack\"] <= t)]\n", " ready = [m for m in active if any(r <= t for r, _ in m[\"ready\"]) or (m[\"unsent\"] > 0 and (admit is None or admit(m, t)))]\n", " if not ready:\n", " nxt = ([pend[i][\"t0\"]] if i < len(pend) else []) + [r for m in active for r, _ in m[\"ready\"] if r > t]\n", " t = min(nxt) if nxt else t + 1.0\n", " continue\n", " if not preempt and cur in ready and cur[\"unsent\"] > 0:\n", " m = cur\n", " else:\n", " m = min(ready, key=lambda x: policy_key(x, t, policy, aging))\n", " st[\"decide_ms\"].append((time.perf_counter() - td) * 1000)\n", " due = [e for e in m[\"ready\"] if e[0] <= t]\n", " if due:\n", " e = min(due, key=lambda x: x[0]); m[\"ready\"].remove(e); frag = e[1]\n", " st[\"retx\"] += 1; st[\"spurious\"] += frag[\"recv\"]\n", " else:\n", " size = mtu if not adapt_mtu else (mtu if loss < 0.15 else (mtu // 2 if loss < 0.4 else mtu // 4))\n", " size = min(m[\"unsent\"], size); m[\"unsent\"] -= size\n", " frag = {\"b\": size, \"recv\": False, \"ack\": None, \"tries\": 0}\n", " m[\"frags\"].append(frag)\n", " cur = m\n", " m[\"first\"] = t if m[\"first\"] is None else m[\"first\"]\n", " nb = frag[\"b\"] + hdr\n", " copies = 2 if (redundancy and RANK[m[\"prec\"]] <= 1 and loss > 0.15) else 1\n", " for _ in range(copies):\n", " t += nb * 8 / link.bw(t)\n", " st[\"tx\"] += 1; st[\"bytes\"] += nb\n", " ok = rng.random() >= link.p_loss(nb, t)\n", " st[\"log\"].append((t, nb, ok, m[\"prec\"], m[\"id\"])) # ์ „์†ก ๊ธฐ๋ก(์‹œ๊ฐ, bytes, ์„ฑ๊ณต, ๊ธด๊ธ‰๋„, ์ „๋ฌธ)\n", " loss = 0.85 * loss + 0.15 * (0.0 if ok else 1.0) # ์ตœ๊ทผ ์†์‹ค๋ฅ  ์ถ”์ •(EWMA)\n", " st[\"flag\"][int(t)] = st[\"flag\"].get(int(t), False) or loss > 0.12 # ๋งํฌ ์—ดํ™” ํŒ์ •(์ดˆ ๋‹จ์œ„)\n", " if ok and not frag[\"recv\"]:\n", " frag[\"recv\"], frag[\"rt\"] = True, t\n", " r = link.rtt(rng); frag[\"ack\"] = t + r\n", " if srtt is None:\n", " srtt, rttvar = r, r / 2\n", " else:\n", " rttvar = 0.75 * rttvar + 0.25 * abs(srtt - r); srtt = 0.875 * srtt + 0.125 * r\n", " frag[\"tries\"] += 1\n", " rto = rto_s if (rto_mode == \"fixed\" or srtt is None) else float(np.clip(srtt + 4 * rttvar, 1.0, 30.0))\n", " if not frag[\"recv\"] or frag[\"ack\"] > t + rto: # ์†์‹ค ๋˜๋Š” ACK ์ง€์—ฐ -> ์žฌ์ „์†ก ์˜ˆ์•ฝ\n", " m[\"ready\"].append((t + rto, frag))\n", " if m[\"done\"] is None and m[\"unsent\"] == 0 and all(f[\"recv\"] for f in m[\"frags\"]):\n", " m[\"done\"] = max(f[\"rt\"] for f in m[\"frags\"])\n", " active = [x for x in active if not (x[\"unsent\"] == 0 and not x[\"ready\"] and all(f[\"recv\"] for f in x[\"frags\"]))]\n", " res = [{\"id\": m[\"id\"], \"type\": m[\"type\"], \"prec\": m[\"prec\"], \"size\": m[\"size\"], \"t0\": m[\"t0\"], \"done\": m[\"done\"], \"wait\": None if m[\"first\"] is None else m[\"first\"] - m[\"t0\"],\n", " \"retx\": sum(f[\"tries\"] for f in m[\"frags\"]) - len(m[\"frags\"]),\n", " \"latency\": None if m[\"done\"] is None else m[\"done\"] - m[\"t0\"]} for m in pend]\n", " for r in res:\n", " r[\"on_time\"] = r[\"latency\"] is not None and r[\"latency\"] <= DEADLINE[r[\"prec\"]]\n", " return res, st\n", "\n", "\n", "def summarize(res):\n", " \"\"\"๊ธด๊ธ‰๋„๋ณ„ ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ ยท์ง€์—ฐ. ์ข…ํ•ฉ ์ •ํ™•๋„ = ๊ธด๊ธ‰๋„๋ณ„ ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ ์˜ ํ‰๊ท .\"\"\"\n", " df = pd.DataFrame(res)\n", " rows = []\n", " for p in PREC:\n", " d = df[df[\"prec\"] == p]\n", " lat = d[\"latency\"].dropna()\n", " rows.append({\"๊ธด๊ธ‰๋„\": p, \"๊ฑด์ˆ˜\": len(d), \"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \": round(d[\"on_time\"].mean(), 3) if len(d) else np.nan,\n", " \"ํ‰๊ท  ์ง€์—ฐ(s)\": round(lat.mean(), 1) if len(lat) else np.nan,\n", " \"95% ์ง€์—ฐ(s)\": round(lat.quantile(0.95), 1) if len(lat) else np.nan, \"๋ฏธ์ „๋‹ฌ\": int(d[\"done\"].isna().sum())})\n", " tab = pd.DataFrame(rows)\n", " return tab, round(float(tab[\"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \"].mean()), 3)\n", "\n", "\n", "class PrecedenceQueue:\n", " \"\"\"๊ธด๊ธ‰๋„ ์ˆœ์œ„๊ฐ€ ๋†’์€ ์ „๋ฌธ์„ ๋จผ์ € ๊บผ๋‚ด๊ณ , ๊ฐ™์€ ๊ธด๊ธ‰๋„๋Š” ๋„์ฐฉ ์ˆœ์„œ(FIFO)๋ฅผ ์ง€ํ‚ค๋Š” 1์ฐจ ์šฐ์„ ์ˆœ์œ„ ํ.\"\"\"\n", "\n", " def __init__(self):\n", " self.h, self.seq = [], itertools.count()\n", "\n", " def push(self, m):\n", " heapq.heappush(self.h, (RANK[m[\"prec\"]], m[\"t0\"], next(self.seq), m))\n", "\n", " def pop(self):\n", " return heapq.heappop(self.h)[-1]\n", "\n", " def __len__(self):\n", " return len(self.h)\n", "\n", "\n", "# 1) ํ ๋™์ž‘ ํ™•์ธ: ๋’ค์„ž์—ฌ ๋„์ฐฉํ•œ ์ „๋ฌธ์ด ๊ธด๊ธ‰๋„ ์ˆœ์„œ๋กœ ๋‚˜์˜ค๋Š”๊ฐ€\n", "demo = [{\"id\": f\"D{i}\", \"prec\": p, \"t0\": float(i)} for i, p in enumerate([\"ROUTINE\", \"PRIORITY\", \"FLASH\", \"ROUTINE\", \"IMMEDIATE\", \"FLASH\"])]\n", "q = PrecedenceQueue()\n", "for m in demo:\n", " q.push(m)\n", "out = [q.pop() for _ in range(len(q))]\n", "print(\"๋„์ฐฉ ์ˆœ์„œ:\", [f\"{m['id']}:{m['prec'][0]}\" for m in demo])\n", "print(\"๊บผ๋‚ธ ์ˆœ์„œ:\", [f\"{m['id']}:{m['prec'][0]}\" for m in out])\n", "print(\"๊ฐ™์€ ๊ธด๊ธ‰๋„ FIFO ์œ ์ง€:\", [m[\"id\"] for m in out if m[\"prec\"] == \"FLASH\"] == [\"D2\", \"D5\"])\n", "\n", "# 2) [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] 20๋ถ„๊ฐ„ ์ „๋ฌธ ์•ฝ 250๊ฑด, 2.4kbps ๋งํฌ์—์„œ ์ „์†ก ์‹œ๋ฎฌ๋ ˆ์ด์…˜\n", "msgs = make_messages(1200, 0.21, seed=7)\n", "link = Link(8000, seed=1)\n", "t0 = time.perf_counter()\n", "fifo, _ = simulate(msgs, link, \"fifo\")\n", "pq, st = simulate(msgs, link, \"strict\", preempt=False) # 1์ฐจ ํ: ์ „๋ฌธ ๋‹จ์œ„๋กœ ๊บผ๋‚ด ๋๊นŒ์ง€ ์ „์†ก\n", "elapsed = time.perf_counter() - t0\n", "tf, acc_f = summarize(fifo)\n", "tp, acc_p = summarize(pq)\n", "tab = tp[[\"๊ธด๊ธ‰๋„\", \"๊ฑด์ˆ˜\"]].copy()\n", "tab[\"FIFO ๊ธฐํ•œ๋‚ด\"] = tf[\"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \"]; tab[\"FIFO 95%์ง€์—ฐ\"] = tf[\"95% ์ง€์—ฐ(s)\"]\n", "tab[\"์šฐ์„ ์ˆœ์œ„ํ ๊ธฐํ•œ๋‚ด\"] = tp[\"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \"]; tab[\"์šฐ์„ ์ˆœ์œ„ํ 95%์ง€์—ฐ\"] = tp[\"95% ์ง€์—ฐ(s)\"]\n", "print(f\"\\n์ „๋ฌธ {len(msgs)}๊ฑด, ์กฐ๊ฐ ์ „์†ก {st['tx']}ํšŒ(์žฌ์ „์†ก {st['retx']}ํšŒ), ์‹œ๋ฎฌ๋ ˆ์ด์…˜ 2ํšŒ {elapsed:.2f}์ดˆ\")\n", "print(tab.to_string(index=False))\n", "all_on = np.mean([r[\"on_time\"] for r in pq])\n", "print(f\"\\n์ฒ˜๋ฆฌ ์ •ํ™•๋„(์ „์ฒด ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ ) FIFO {np.mean([r['on_time'] for r in fifo]):.1%} -> ์šฐ์„ ์ˆœ์œ„ ํ {all_on:.1%} (๋ชฉํ‘œ 75% ์ด์ƒ: {'๋‹ฌ์„ฑ' if all_on >= 0.75 else '๋ฏธ๋‹ฌ'})\")\n", "print(f\"์ข…ํ•ฉ ์ •ํ™•๋„(๊ธด๊ธ‰๋„๋ณ„ ์ „๋‹ฌ๋ฅ  ํ‰๊ท ) FIFO {acc_f:.3f} -> ์šฐ์„ ์ˆœ์œ„ ํ {acc_p:.3f}\")\n", "\n", "# 3) ํ•œ๊ณ„ ๋ถ„์„: ๊ธฐํ•œ์„ ๋†“์นœ FLASH ์ „๋ฌธ์€ ์™œ ๋Šฆ์—ˆ๋‚˜\n", "late = [r for r in pq if r[\"prec\"] == \"FLASH\" and not r[\"on_time\"]]\n", "for r in late:\n", " blocker = [m for m in msgs if m[\"t0\"] < r[\"t0\"] and m[\"size\"] >= 1000 and r[\"t0\"] - m[\"t0\"] < 30]\n", " print(f\"FLASH {r['id']} ์ง€์—ฐ {r['latency']:.1f}s - ์ง์ „ 30์ดˆ ๋‚ด ๋„์ฐฉํ•œ 1,000B ์ด์ƒ ๋Œ€์šฉ๋Ÿ‰ ์ „๋ฌธ {len(blocker)}๊ฑด์ด ์ฑ„๋„ ์ ์œ (์ „๋ฌธ ๋‹จ์œ„ ๋น„์„ ์ )\")\n", "print(\"-> ์ „๋ฌธ์„ ํ†ต์งธ๋กœ ๋ณด๋‚ด๋Š” 1์ฐจ ํ๋Š” ๋Œ€์šฉ๋Ÿ‰ ์ „๋ฌธ์ด ์ฑ„๋„์„ ์ ์œ ํ•˜๋ฉด FLASH๋„ ๊ธฐ๋‹ค๋ฆฐ๋‹ค. ๋ฏธ์…˜ 4์—์„œ ์กฐ๊ฐ ๋‹จ์œ„ ์„ ์ ๊ณผ ์žฌ์ „์†ก ๊ฐœ์„ ์œผ๋กœ ๋ณด์™„ํ•œ๋‹ค.\")\n", "\n", "# 4) ๋งํฌ ์†๋„๋ณ„ ๋ฏผ๊ฐ๋„\n", "print(\"\\n๋งํฌ(bps) FIFO ์ „์ฒด ์šฐ์„ ์ˆœ์œ„ํ ์ „์ฒด ์šฐ์„ ์ˆœ์œ„ํ FLASH\")\n", "for bw in [1200, 2400, 4800]:\n", " lk = Link(20000, bw_bps=bw, seed=1)\n", " a, _ = simulate(msgs, lk, \"fifo\"); b, _ = simulate(msgs, lk, \"strict\", preempt=False)\n", " fl = [r[\"on_time\"] for r in b if r[\"prec\"] == \"FLASH\"]\n", " print(f\"{bw:>8} {np.mean([r['on_time'] for r in a]):8.1%} {np.mean([r['on_time'] for r in b]):12.1%} {np.mean(fl):12.1%}\")\n", "\n", "plt.figure(figsize=(8, 4))\n", "data = [[r[\"latency\"] for r in res if r[\"prec\"] == p] for res in (fifo, pq) for p in PREC]\n", "plt.boxplot(data, showfliers=False)\n", "plt.xticks(range(1, 9), [f\"{n}\\n{p[:5]}\" for n in (\"FIFO\", \"PQ\") for p in PREC])\n", "plt.yscale(\"log\"); plt.ylabel(\"delivery latency (s, log)\"); plt.title(\"FIFO vs precedence queue (synthetic 2.4 kbps link)\")\n", "plt.tight_layout(); plt.show()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 03 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "๋„์ฐฉ ์ˆœ์„œ: ['D0:R', 'D1:P', 'D2:F', 'D3:R', 'D4:I', 'D5:F']\n", "๊บผ๋‚ธ ์ˆœ์„œ: ['D2:F', 'D5:F', 'D4:I', 'D1:P', 'D0:R', 'D3:R']\n", "๊ฐ™์€ ๊ธด๊ธ‰๋„ FIFO ์œ ์ง€: True\n", "\n", "์ „๋ฌธ 258๊ฑด, ์กฐ๊ฐ ์ „์†ก 1285ํšŒ(์žฌ์ „์†ก 71ํšŒ), ์‹œ๋ฎฌ๋ ˆ์ด์…˜ 2ํšŒ 0.03์ดˆ\n", " ๊ธด๊ธ‰๋„ ๊ฑด์ˆ˜ FIFO ๊ธฐํ•œ๋‚ด FIFO 95%์ง€์—ฐ ์šฐ์„ ์ˆœ์œ„ํ ๊ธฐํ•œ๋‚ด ์šฐ์„ ์ˆœ์œ„ํ 95%์ง€์—ฐ\n", " FLASH 25 0.200 48.4 0.76 14.4\n", "IMMEDIATE 27 0.741 45.8 1.00 17.8\n", " PRIORITY 99 1.000 53.2 1.00 25.7\n", " ROUTINE 107 1.000 57.7 1.00 82.3\n", "\n", "์ฒ˜๋ฆฌ ์ •ํ™•๋„(์ „์ฒด ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ ) FIFO 89.5% -> ์šฐ์„ ์ˆœ์œ„ ํ 97.7% (๋ชฉํ‘œ 75% ์ด์ƒ: ๋‹ฌ์„ฑ)\n", "์ข…ํ•ฉ ์ •ํ™•๋„(๊ธด๊ธ‰๋„๋ณ„ ์ „๋‹ฌ๋ฅ  ํ‰๊ท ) FIFO 0.735 -> ์šฐ์„ ์ˆœ์œ„ ํ 0.940\n", "FLASH M0010 ์ง€์—ฐ 17.4s - ์ง์ „ 30์ดˆ ๋‚ด ๋„์ฐฉํ•œ 1,000B ์ด์ƒ ๋Œ€์šฉ๋Ÿ‰ ์ „๋ฌธ 2๊ฑด์ด ์ฑ„๋„ ์ ์œ (์ „๋ฌธ ๋‹จ์œ„ ๋น„์„ ์ )\n", "FLASH M0041 ์ง€์—ฐ 13.1s - ์ง์ „ 30์ดˆ ๋‚ด ๋„์ฐฉํ•œ 1,000B ์ด์ƒ ๋Œ€์šฉ๋Ÿ‰ ์ „๋ฌธ 3๊ฑด์ด ์ฑ„๋„ ์ ์œ (์ „๋ฌธ ๋‹จ์œ„ ๋น„์„ ์ )\n", "FLASH M0105 ์ง€์—ฐ 13.2s - ์ง์ „ 30์ดˆ ๋‚ด ๋„์ฐฉํ•œ 1,000B ์ด์ƒ ๋Œ€์šฉ๋Ÿ‰ ์ „๋ฌธ 2๊ฑด์ด ์ฑ„๋„ ์ ์œ (์ „๋ฌธ ๋‹จ์œ„ ๋น„์„ ์ )\n", "FLASH M0226 ์ง€์—ฐ 14.7s - ์ง์ „ 30์ดˆ ๋‚ด ๋„์ฐฉํ•œ 1,000B ์ด์ƒ ๋Œ€์šฉ๋Ÿ‰ ์ „๋ฌธ 3๊ฑด์ด ์ฑ„๋„ ์ ์œ (์ „๋ฌธ ๋‹จ์œ„ ๋น„์„ ์ )\n", "FLASH M0228 ์ง€์—ฐ 13.1s - ์ง์ „ 30์ดˆ ๋‚ด ๋„์ฐฉํ•œ 1,000B ์ด์ƒ ๋Œ€์šฉ๋Ÿ‰ ์ „๋ฌธ 3๊ฑด์ด ์ฑ„๋„ ์ ์œ (์ „๋ฌธ ๋‹จ์œ„ ๋น„์„ ์ )\n", "FLASH M0233 ์ง€์—ฐ 13.2s - ์ง์ „ 30์ดˆ ๋‚ด ๋„์ฐฉํ•œ 1,000B ์ด์ƒ ๋Œ€์šฉ๋Ÿ‰ ์ „๋ฌธ 3๊ฑด์ด ์ฑ„๋„ ์ ์œ (์ „๋ฌธ ๋‹จ์œ„ ๋น„์„ ์ )\n", "-> ์ „๋ฌธ์„ ํ†ต์งธ๋กœ ๋ณด๋‚ด๋Š” 1์ฐจ ํ๋Š” ๋Œ€์šฉ๋Ÿ‰ ์ „๋ฌธ์ด ์ฑ„๋„์„ ์ ์œ ํ•˜๋ฉด FLASH๋„ ๊ธฐ๋‹ค๋ฆฐ๋‹ค. ๋ฏธ์…˜ 4์—์„œ ์กฐ๊ฐ ๋‹จ์œ„ ์„ ์ ๊ณผ ์žฌ์ „์†ก ๊ฐœ์„ ์œผ๋กœ ๋ณด์™„ํ•œ๋‹ค.\n", "\n", "๋งํฌ(bps) FIFO ์ „์ฒด ์šฐ์„ ์ˆœ์œ„ํ ์ „์ฒด ์šฐ์„ ์ˆœ์œ„ํ FLASH\n", " 1200 34.9% 58.9% 44.0%\n", " 2400 89.5% 97.7% 76.0%\n", " 4800 100.0% 100.0% 100.0%\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 04. ์ „์žฅ ์ƒํ™ฉ ๋ฐ ์ „์†ก ์‹คํŒจ์œจ์— ๋”ฐ๋ฅธ ๋™์  ํŒจํ‚ท ์Šค์ผ€์ค„๋ง ๋ฐ ์ง€๋Šฅํ˜• ์žฌ์ „์†ก ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ตฌํ˜„\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.0์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "๋™์  ์ฑ„๋„ ์ƒํƒœ ๋ฐ ํŒจํ‚ท ์†์‹ค๋ฅ ์— ๋”ฐ๋ผ ์ตœ์  ์žฌ์ „์†ก ๊ฐ„๊ฒฉ์„ ์กฐ์ •ํ•˜๋Š” ์ง€๋Šฅํ˜• ์Šค์ผ€์ค„๋ง ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๊ณ ๋„ํ™”ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **์ฑ„๋„ ์†์‹ค๋ฅ ๊ณผ ์™•๋ณต์‹œ๊ฐ„์„ ์ถ”์ •ํ•ด ์ „์†ก ์ˆœ์„œ์™€ ์žฌ์ „์†ก ๊ฐ„๊ฒฉ์„ ์กฐ์ •ํ•˜๋Š” ์ง€๋Šฅํ˜• ์Šค์ผ€์ค„๋งยท์žฌ์ „์†ก ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ๊ณ ๋„ํ™”ํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **์กฐ๊ฐ ๋‹จ์œ„ ์„ ์ , ๋Œ€๊ธฐ์‹œ๊ฐ„ ๊ฐ€์ค‘(aging), ์™•๋ณต์‹œ๊ฐ„ ๊ธฐ๋ฐ˜ ์ ์‘ํ˜• ์žฌ์ „์†ก ํƒ€์ด๋จธ, ๋งํฌ ์—ดํ™” ์‹œ ๊ธด๊ธ‰ ์ „๋ฌธ ์ค‘๋ณต ์ „์†ก๊ณผ ์กฐ๊ฐ ํฌ๊ธฐ ์ถ•์†Œ ๊ตฌํ˜„ํ•˜๊ณ  ๋™์ž‘์„ ๊ฒ€์ฆํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/models/c202_advanced_detector.py ๋ฐ ์˜ˆ์™ธ ํƒ์ง€ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ๊ธฐ๋งŒ/๊ต๋ž€ ๊ณต๊ฒฉ์— ๋Œ€ํ•œ ๋ฐฉ์–ด ๋กœ์ง์˜ ํ˜„์‹ค์„ฑ ๋ฐ ์‹ ๋ขฐ์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ๋ณตํ•ฉ ์œ„ํ˜‘ ํƒ์ง€ ์žฌํ˜„์œจ(Recall) 85% ์ด์ƒ, ์ฒ˜๋ฆฌ ์ง€์—ฐ 200ms ์ดํ•˜\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ๋ณตํ•ฉ ์œ„ํ˜‘ ์‹œ๋‚˜๋ฆฌ์˜ค 10์ข…์— ๋Œ€ํ•œ ํƒ์ง€ ํ…Œ์ŠคํŠธ ํ†ต๊ณผ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ํ•ฉ์„ฑ ์œ„ํ˜‘ ๋ฐ์ดํ„ฐ์…‹ ์ฃผ์ž… ์‹œ ์ด์ƒ์น˜ ํƒ์ง€ ์ •ํ™•๋„ ๊ฒ€์ฆ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"์ฑ„๋„ ์ƒํƒœ(์†์‹ค๋ฅ ยท์™•๋ณต์‹œ๊ฐ„)๋ฅผ ์ถ”์ •ํ•ด ์กฐ๊ฐ ๋‹จ์œ„ ์„ ์ , ๋Œ€๊ธฐ์‹œ๊ฐ„ ๊ฐ€์ค‘(aging), ์ ์‘ํ˜• ์žฌ์ „์†ก ํƒ€์ด๋จธ(RTO), ๊ธด๊ธ‰ ์ „๋ฌธ ์ค‘๋ณต ์ „์†ก, ์กฐ๊ฐ ํฌ๊ธฐ ์กฐ์ •์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์ง€๋Šฅํ˜• ์Šค์ผ€์ค„๋Ÿฌ๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ , ๋ณตํ•ฉ ์žฅ์•  ์‹œ๋‚˜๋ฆฌ์˜ค 10์ข…์—์„œ ๊ธด๊ธ‰ ์ „๋ฌธ ์ „๋‹ฌ๋ฅ ยท๋งํฌ ์—ดํ™” ํƒ์ง€ ์žฌํ˜„์œจยท์Šค์ผ€์ค„๋ง ๊ฒฐ์ • ์ง€์—ฐ์„ ๊ฒ€์ฆํ•œ๋‹ค.\"\"\"\n", "import time\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์ „์ˆ  ์ „๋ฌธ(๋ฉ”์‹œ์ง€) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยทํ†ต์‹ ๋งยท์ œ์› ์ •๋ณด ์—†์Œ)\n", "# ๊ธด๊ธ‰๋„ 4๋‹จ๊ณ„(FLASH/IMMEDIATE/PRIORITY/ROUTINE)์™€ ์ „๋ฌธ ์œ ํ˜•๋ณ„ ํฌ๊ธฐ๋ฅผ ๊ฐ€์ง„ ์ „๋ฌธ์„ ๋งŒ๋“ ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PREC = [\"FLASH\", \"IMMEDIATE\", \"PRIORITY\", \"ROUTINE\"]\n", "RANK = {p: i for i, p in enumerate(PREC)}\n", "DEADLINE = {\"FLASH\": 10, \"IMMEDIATE\": 30, \"PRIORITY\": 120, \"ROUTINE\": 600} # ๊ฐ€์ƒ ์ „๋‹ฌ ๋ชฉํ‘œ ์‹œ๊ฐ„(์ดˆ)\n", "MSG_TYPES = { # ์œ ํ˜•: (๊ธด๊ธ‰๋„ ํ›„๋ณด, ํ™•๋ฅ , ๋ณธ๋ฌธ ํฌ๊ธฐ ๋ฒ”์œ„ bytes, ๋ฐœ์ƒ ๋น„์œจ)\n", " \"CONTACT\": ([\"FLASH\", \"IMMEDIATE\"], [0.7, 0.3], (120, 300), 0.08),\n", " \"MEDEVAC\": ([\"FLASH\", \"IMMEDIATE\"], [0.4, 0.6], (150, 350), 0.05),\n", " \"SITREP\": ([\"IMMEDIATE\", \"PRIORITY\"], [0.2, 0.8], (600, 2500), 0.20),\n", " \"POSREP\": ([\"PRIORITY\", \"ROUTINE\"], [0.5, 0.5], (60, 120), 0.35),\n", " \"LOGREQ\": ([\"PRIORITY\", \"ROUTINE\"], [0.4, 0.6], (300, 1200), 0.14),\n", " \"ADMIN\": ([\"ROUTINE\"], [1.0], (1000, 5000), 0.18),\n", "}\n", "\n", "\n", "def make_messages(duration=600, rate=0.12, seed=7, t_start=0.0, prefix=\"M\"):\n", " \"\"\"duration์ดˆ ๋™์•ˆ ํ‰๊ท  rate๊ฑด/์ดˆ๋กœ ๋ฐœ์ƒํ•˜๋Š” ๊ฐ€์ƒ ์ „๋ฌธ ๋ชฉ๋ก.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " n = int(rng.poisson(rate * duration))\n", " times = np.sort(rng.uniform(t_start, t_start + duration, n))\n", " types = list(MSG_TYPES)\n", " msgs = []\n", " for i, t0 in enumerate(times):\n", " ty = types[rng.choice(len(types), p=[MSG_TYPES[k][3] for k in types])]\n", " cands, p, (lo, hi), _ = MSG_TYPES[ty]\n", " msgs.append({\"id\": f\"{prefix}{i:04d}\", \"t0\": float(t0), \"type\": ty, \"prec\": str(rng.choice(cands, p=p)),\n", " \"size\": int(rng.integers(lo, hi + 1)), \"src\": f\"U{int(rng.integers(1, 9)):02d}\"})\n", " return msgs\n", "\n", "\n", "class Link:\n", " \"\"\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ํ˜‘๋Œ€์—ญ ๋ฌด์„  ๋งํฌ(๊ธฐ๋ณธ 2.4kbps). ์ดˆ ๋‹จ์œ„ ์ƒํƒœ(good/bad/jam)์— ๋”ฐ๋ผ\n", " ๋น„ํŠธ์˜ค๋ฅ˜์œจ์ด ๋ฐ”๋€Œ๊ณ (Gilbert-Elliott ๋ชจ๋ธ), ๊ตฌ๊ฐ„๋ณ„ ๋Œ€์—ญํญ ๊ฐ์†Œ๋„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.\"\"\"\n", " BER = {\"good\": 1e-5, \"bad\": 2e-4, \"jam\": 1.2e-3}\n", "\n", " def __init__(self, horizon=4000, bw_bps=2400, seed=1, p_gb=0.02, p_bg=0.15, jam=(), bw_drop=(), rtt=(1.2, 0.4)):\n", " rng = np.random.default_rng(seed)\n", " st, s = [], \"good\"\n", " for _ in range(int(horizon) + 1):\n", " s = (\"bad\" if rng.random() < p_gb else \"good\") if s == \"good\" else (\"good\" if rng.random() < p_bg else \"bad\")\n", " st.append(s)\n", " for a, b in jam:\n", " for k in range(int(a), min(int(b), len(st))):\n", " st[k] = \"jam\"\n", " self.states, self.bw_bps, self.bw_drop, self.rtt_par = st, bw_bps, list(bw_drop), rtt\n", "\n", " def state(self, t):\n", " return self.states[min(int(t), len(self.states) - 1)]\n", "\n", " def bw(self, t):\n", " f = 1.0\n", " for a, b, fac in self.bw_drop:\n", " if a <= t < b:\n", " f = fac\n", " return self.bw_bps * f\n", "\n", " def p_loss(self, nbytes, t):\n", " return 1 - (1 - self.BER[self.state(t)]) ** (8 * nbytes)\n", "\n", " def rtt(self, rng):\n", " return self.rtt_par[0] + rng.exponential(self.rtt_par[1])\n", "# ---------------------------------------------------------------------------\n", "\n", "# ---- ์šฐ์„ ์ˆœ์œ„ ํยท์žฌ์ „์†ก ์ด์‚ฐ ์‚ฌ๊ฑด ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ ๊ณตํ†ต ํ•จ์ˆ˜ ----\n", "HDR = 32 # ์ „๋ฌธ ์กฐ๊ฐ(ํŒจํ‚ท) ํ—ค๋” bytes (๋ฏธ์…˜ 2์˜ ํ—ค๋” ์••์ถ• ์ ์šฉ ์‹œ 12 bytes)\n", "\n", "\n", "def policy_key(m, t, policy, aging):\n", " if policy == \"fifo\": # ๋„์ฐฉ ์ˆœ์„œ\n", " return (m[\"t0\"],)\n", " if policy == \"strict\": # ๊ธด๊ธ‰๋„ ์šฐ์„ , ๊ฐ™์œผ๋ฉด ๋„์ฐฉ ์ˆœ์„œ\n", " return (RANK[m[\"prec\"]], m[\"t0\"])\n", " # aging: ๊ธด๊ธ‰๋„ + (๋Œ€๊ธฐ์‹œ๊ฐ„ / ์ „๋‹ฌ ๋ชฉํ‘œ์‹œ๊ฐ„) ๊ฐ€์ค‘. ์˜ค๋ž˜ ๊ธฐ๋‹ค๋ฆฐ ์ „๋ฌธ๊ณผ ๊ธฐํ•œ์ด ์ž„๋ฐ•ํ•œ ์ „๋ฌธ์ด ์•ž์œผ๋กœ ์˜จ๋‹ค.\n", " return (RANK[m[\"prec\"]] - aging * (t - m[\"t0\"]) / DEADLINE[m[\"prec\"]], m[\"t0\"])\n", "\n", "\n", "def simulate(msgs, link, policy=\"strict\", mtu=256, preempt=True, rto_mode=\"fixed\", rto_s=4.0, aging=0.0,\n", " redundancy=False, adapt_mtu=False, hdr=HDR, seed=0, until=None, admit=None):\n", " \"\"\"์ „๋ฌธ ๋ชฉ๋ก์„ ๋งํฌ๋กœ ๋ณด๋‚ด๋Š” ์ด์‚ฐ ์‚ฌ๊ฑด ์‹œ๋ฎฌ๋ ˆ์ด์…˜.\n", " - ์ „๋ฌธ์€ mtu ํฌ๊ธฐ ์กฐ๊ฐ์œผ๋กœ ๋‚˜๋ˆ  ๋ณด๋‚ธ๋‹ค. ์กฐ๊ฐ์ด ์†์‹ค๋˜๋ฉด RTO ํ›„ ์žฌ์ „์†กํ•œ๋‹ค(์ž๋ฃŒ๋Š” ๋ฒ„๋ฆฌ์ง€ ์•Š์Œ).\n", " - preempt=False๋ฉด ํ•œ ์ „๋ฌธ์˜ ์กฐ๊ฐ์„ ๋๊นŒ์ง€ ์—ฐ์† ์ „์†กํ•œ๋‹ค(ํฐ ์ „๋ฌธ์ด ๊ธด๊ธ‰ ์ „๋ฌธ์„ ๋ง‰๋Š” ํ˜„์ƒ ์žฌํ˜„).\n", " - rto_mode='adaptive'๋ฉด ์™•๋ณต์‹œ๊ฐ„(RTT) ์ถ”์ •๊ฐ’์œผ๋กœ RTO๋ฅผ ๊ณ„์‚ฐํ•œ๋‹ค(Jacobson ๋ฐฉ์‹).\n", " - redundancy=True๋ฉด ๋งํฌ ์—ดํ™” ์ถ”์ • ์‹œ FLASHยทIMMEDIATE ์กฐ๊ฐ์„ 2ํšŒ ์—ฐ์† ์ „์†กํ•œ๋‹ค.\n", " - adapt_mtu=True๋ฉด ๋งํฌ ์—ดํ™” ์ถ”์ • ์‹œ ์กฐ๊ฐ ํฌ๊ธฐ๋ฅผ ์ค„์ธ๋‹ค.\n", " - admit(m, t)์ด False์ธ ์ „๋ฌธ์€ ์ƒˆ ์กฐ๊ฐ์„ ๋ณด๋‚ด์ง€ ์•Š๊ณ  ์ €์žฅํ•ด ๋‘”๋‹ค(์„ฑ๋Šฅ ์ €ํ•˜ ๋ชจ๋“œ).\"\"\"\n", " rng = np.random.default_rng(seed)\n", " pend = sorted(({**m, \"unsent\": m[\"size\"], \"ready\": [], \"frags\": [], \"done\": None, \"first\": None} for m in msgs), key=lambda m: m[\"t0\"])\n", " t, i, active, cur = 0.0, 0, [], None\n", " srtt, rttvar, loss = None, None, 0.0\n", " st = {\"tx\": 0, \"retx\": 0, \"spurious\": 0, \"bytes\": 0, \"decide_ms\": [], \"flag\": {}, \"log\": []}\n", " until = until if until is not None else (pend[-1][\"t0\"] if pend else 0) + 6000\n", " while (i < len(pend) or active) and t < until:\n", " while i < len(pend) and pend[i][\"t0\"] <= t:\n", " active.append(pend[i]); i += 1\n", " td = time.perf_counter()\n", " for m in active: # ACK๊ฐ€ ์ด๋ฏธ ๋„์ฐฉํ•œ ์žฌ์ „์†ก ์˜ˆ์•ฝ์€ ์ทจ์†Œ\n", " m[\"ready\"] = [e for e in m[\"ready\"] if not (e[1][\"ack\"] is not None and e[1][\"ack\"] <= t)]\n", " ready = [m for m in active if any(r <= t for r, _ in m[\"ready\"]) or (m[\"unsent\"] > 0 and (admit is None or admit(m, t)))]\n", " if not ready:\n", " nxt = ([pend[i][\"t0\"]] if i < len(pend) else []) + [r for m in active for r, _ in m[\"ready\"] if r > t]\n", " t = min(nxt) if nxt else t + 1.0\n", " continue\n", " if not preempt and cur in ready and cur[\"unsent\"] > 0:\n", " m = cur\n", " else:\n", " m = min(ready, key=lambda x: policy_key(x, t, policy, aging))\n", " st[\"decide_ms\"].append((time.perf_counter() - td) * 1000)\n", " due = [e for e in m[\"ready\"] if e[0] <= t]\n", " if due:\n", " e = min(due, key=lambda x: x[0]); m[\"ready\"].remove(e); frag = e[1]\n", " st[\"retx\"] += 1; st[\"spurious\"] += frag[\"recv\"]\n", " else:\n", " size = mtu if not adapt_mtu else (mtu if loss < 0.15 else (mtu // 2 if loss < 0.4 else mtu // 4))\n", " size = min(m[\"unsent\"], size); m[\"unsent\"] -= size\n", " frag = {\"b\": size, \"recv\": False, \"ack\": None, \"tries\": 0}\n", " m[\"frags\"].append(frag)\n", " cur = m\n", " m[\"first\"] = t if m[\"first\"] is None else m[\"first\"]\n", " nb = frag[\"b\"] + hdr\n", " copies = 2 if (redundancy and RANK[m[\"prec\"]] <= 1 and loss > 0.15) else 1\n", " for _ in range(copies):\n", " t += nb * 8 / link.bw(t)\n", " st[\"tx\"] += 1; st[\"bytes\"] += nb\n", " ok = rng.random() >= link.p_loss(nb, t)\n", " st[\"log\"].append((t, nb, ok, m[\"prec\"], m[\"id\"])) # ์ „์†ก ๊ธฐ๋ก(์‹œ๊ฐ, bytes, ์„ฑ๊ณต, ๊ธด๊ธ‰๋„, ์ „๋ฌธ)\n", " loss = 0.85 * loss + 0.15 * (0.0 if ok else 1.0) # ์ตœ๊ทผ ์†์‹ค๋ฅ  ์ถ”์ •(EWMA)\n", " st[\"flag\"][int(t)] = st[\"flag\"].get(int(t), False) or loss > 0.12 # ๋งํฌ ์—ดํ™” ํŒ์ •(์ดˆ ๋‹จ์œ„)\n", " if ok and not frag[\"recv\"]:\n", " frag[\"recv\"], frag[\"rt\"] = True, t\n", " r = link.rtt(rng); frag[\"ack\"] = t + r\n", " if srtt is None:\n", " srtt, rttvar = r, r / 2\n", " else:\n", " rttvar = 0.75 * rttvar + 0.25 * abs(srtt - r); srtt = 0.875 * srtt + 0.125 * r\n", " frag[\"tries\"] += 1\n", " rto = rto_s if (rto_mode == \"fixed\" or srtt is None) else float(np.clip(srtt + 4 * rttvar, 1.0, 30.0))\n", " if not frag[\"recv\"] or frag[\"ack\"] > t + rto: # ์†์‹ค ๋˜๋Š” ACK ์ง€์—ฐ -> ์žฌ์ „์†ก ์˜ˆ์•ฝ\n", " m[\"ready\"].append((t + rto, frag))\n", " if m[\"done\"] is None and m[\"unsent\"] == 0 and all(f[\"recv\"] for f in m[\"frags\"]):\n", " m[\"done\"] = max(f[\"rt\"] for f in m[\"frags\"])\n", " active = [x for x in active if not (x[\"unsent\"] == 0 and not x[\"ready\"] and all(f[\"recv\"] for f in x[\"frags\"]))]\n", " res = [{\"id\": m[\"id\"], \"type\": m[\"type\"], \"prec\": m[\"prec\"], \"size\": m[\"size\"], \"t0\": m[\"t0\"], \"done\": m[\"done\"], \"wait\": None if m[\"first\"] is None else m[\"first\"] - m[\"t0\"],\n", " \"retx\": sum(f[\"tries\"] for f in m[\"frags\"]) - len(m[\"frags\"]),\n", " \"latency\": None if m[\"done\"] is None else m[\"done\"] - m[\"t0\"]} for m in pend]\n", " for r in res:\n", " r[\"on_time\"] = r[\"latency\"] is not None and r[\"latency\"] <= DEADLINE[r[\"prec\"]]\n", " return res, st\n", "\n", "\n", "def summarize(res):\n", " \"\"\"๊ธด๊ธ‰๋„๋ณ„ ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ ยท์ง€์—ฐ. ์ข…ํ•ฉ ์ •ํ™•๋„ = ๊ธด๊ธ‰๋„๋ณ„ ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ ์˜ ํ‰๊ท .\"\"\"\n", " df = pd.DataFrame(res)\n", " rows = []\n", " for p in PREC:\n", " d = df[df[\"prec\"] == p]\n", " lat = d[\"latency\"].dropna()\n", " rows.append({\"๊ธด๊ธ‰๋„\": p, \"๊ฑด์ˆ˜\": len(d), \"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \": round(d[\"on_time\"].mean(), 3) if len(d) else np.nan,\n", " \"ํ‰๊ท  ์ง€์—ฐ(s)\": round(lat.mean(), 1) if len(lat) else np.nan,\n", " \"95% ์ง€์—ฐ(s)\": round(lat.quantile(0.95), 1) if len(lat) else np.nan, \"๋ฏธ์ „๋‹ฌ\": int(d[\"done\"].isna().sum())})\n", " tab = pd.DataFrame(rows)\n", " return tab, round(float(tab[\"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \"].mean()), 3)\n", "\n", "BASE = dict(policy=\"strict\", preempt=False) # ๋ฏธ์…˜ 3์˜ 1์ฐจ ํ(๊ณ ์ • RTO 4์ดˆ)\n", "ADV = dict(policy=\"aging\", aging=0.5, preempt=True, rto_mode=\"adaptive\", redundancy=True, adapt_mtu=True)\n", "\n", "\n", "def urgent_rate(res):\n", " u = [r[\"on_time\"] for r in res if RANK[r[\"prec\"]] <= 1]\n", " return float(np.mean(u))\n", "\n", "\n", "def degradation_episodes(link, flag):\n", " \"\"\"์—ดํ™” ๊ตฌ๊ฐ„(bad/jam์ด ์ด์–ด์ง€๋Š” ๊ตฌ๊ฐ„, ์ „์†ก 3ํšŒ ์ด์ƒ) ์ค‘ ๊ตฌ๊ฐ„ ์•ˆํŒŽ 2์ดˆ ๋‚ด์— ์—ดํ™” ํŒ์ •์ด ๋‚˜์˜จ ๋น„์œจ.\"\"\"\n", " eps, s = [], None\n", " for k, stt in enumerate(link.states + [\"good\"]):\n", " if stt != \"good\" and s is None:\n", " s = k\n", " elif stt == \"good\" and s is not None:\n", " eps.append((s, k)); s = None\n", " hit = [any(flag.get(k, False) for k in range(a, b + 3)) for a, b in eps if sum(k in flag for k in range(a, b)) >= 3]\n", " good = [v for k, v in flag.items() if link.state(k) == \"good\"]\n", " return hit, float(np.mean(good)) if good else 0.0\n", "\n", "\n", "def admin_burst(seed):\n", " rng = np.random.default_rng(seed)\n", " return [{\"id\": f\"A{i:03d}\", \"t0\": float(t), \"type\": \"ADMIN\", \"prec\": \"ROUTINE\", \"size\": int(rng.integers(3000, 5000)), \"src\": \"U09\"}\n", " for i, t in enumerate(np.sort(rng.uniform(200, 400, 25)))]\n", "\n", "\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๋ณตํ•ฉ ์žฅ์•  ์‹œ๋‚˜๋ฆฌ์˜ค 10์ข…\n", "base = make_messages(1200, 0.21, seed=7)\n", "double = base + make_messages(1200, 0.21, seed=8, prefix=\"N\")\n", "SCEN = [(\"1 ํ‰์‹œ\", base, {}), (\"2 ๋ฒ„์ŠคํŠธ ์†์‹ค\", base, dict(p_gb=0.08, p_bg=0.1)), (\"3 ์žฌ๋ฐ 60์ดˆ\", base, dict(jam=[(300, 360)])),\n", " (\"4 ๋‹จ์† ์žฌ๋ฐ 3ํšŒ\", base, dict(jam=[(200, 220), (500, 520), (800, 820)])), (\"5 ๋Œ€์—ญํญ 50% ๊ฐ์†Œ\", base, dict(bw_drop=[(300, 700, 0.5)])),\n", " (\"6 ์ค‘๊ณ„ ์ง€์—ฐ ์ฆ๊ฐ€\", base, dict(rtt=(3.0, 1.0))), (\"7 ํŠธ๋ž˜ํ”ฝ 2๋ฐฐ\", double, {}), (\"8 ๋Œ€์šฉ๋Ÿ‰ ์ „๋ฌธ ํญ์ฃผ\", base + admin_burst(3), {}),\n", " (\"9 ๋ฒ„์ŠคํŠธ+๋Œ€์—ญํญ ๊ฐ์†Œ\", base, dict(p_gb=0.08, p_bg=0.1, bw_drop=[(300, 700, 0.5)])),\n", " (\"10 ์žฌ๋ฐ+ํŠธ๋ž˜ํ”ฝ2๋ฐฐ+์ง€์—ฐ\", double, dict(jam=[(300, 360)], rtt=(3.0, 1.0)))]\n", "\n", "rows, hits, fas, dec = [], [], [], []\n", "for name, msgs, lk in SCEN:\n", " link = Link(12000, seed=1, **lk)\n", " rb, sb = simulate(msgs, link, **BASE)\n", " ra, sa = simulate(msgs, link, **ADV)\n", " h, fa = degradation_episodes(link, sa[\"flag\"])\n", " hits += h; fas.append(fa); dec += sa[\"decide_ms\"]\n", " rows.append({\"์‹œ๋‚˜๋ฆฌ์˜ค\": name, \"์ „๋ฌธ\": len(msgs), \"๊ธฐ์กด ๊ธด๊ธ‰์ „๋‹ฌ๋ฅ \": round(urgent_rate(rb), 3), \"๊ฐœ์„  ๊ธด๊ธ‰์ „๋‹ฌ๋ฅ \": round(urgent_rate(ra), 3),\n", " \"๊ธฐ์กด ์ข…ํ•ฉ\": summarize(rb)[1], \"๊ฐœ์„  ์ข…ํ•ฉ\": summarize(ra)[1], \"์—ดํ™”ํƒ์ง€\": f\"{sum(h)}/{len(h)}\", \"์žฌ์ „์†ก\": sa[\"retx\"]})\n", "df = pd.DataFrame(rows)\n", "print(df.to_string(index=False))\n", "u_all = df[\"๊ฐœ์„  ๊ธด๊ธ‰์ „๋‹ฌ๋ฅ \"].mean()\n", "rec = np.mean(hits)\n", "print(f\"\\n๊ธด๊ธ‰ ์ „๋ฌธ(FLASHยทIMMEDIATE) ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ  ํ‰๊ท : ๊ธฐ์กด {df['๊ธฐ์กด ๊ธด๊ธ‰์ „๋‹ฌ๋ฅ '].mean():.1%} -> ๊ฐœ์„  {u_all:.1%} (๋ชฉํ‘œ 85% ์ด์ƒ: {'๋‹ฌ์„ฑ' if u_all >= 0.85 else '๋ฏธ๋‹ฌ'})\")\n", "print(f\"๋งํฌ ์—ดํ™”(๋ณตํ•ฉ ์œ„ํ˜‘) ํƒ์ง€ ์žฌํ˜„์œจ {rec:.1%} ({sum(hits)}/{len(hits)}๊ตฌ๊ฐ„, ๋ชฉํ‘œ 85% ์ด์ƒ: {'๋‹ฌ์„ฑ' if rec >= 0.85 else '๋ฏธ๋‹ฌ'}), ์ •์ƒ ๊ตฌ๊ฐ„ ์˜ค๊ฒฝ๋ณด {np.mean(fas):.1%}\")\n", "print(\"์ฐธ๊ณ : ์—ดํ™” ํŒ์ • ๊ธฐ์ค€(์ตœ๊ทผ ์†์‹ค๋ฅ  ์ถ”์ •๊ฐ’)์„ 0.12์—์„œ 0.2๋กœ ์˜ฌ๋ฆฌ๋ฉด ์ •์ƒ ๊ตฌ๊ฐ„ ์˜ค๊ฒฝ๋ณด๋Š” ์ค„์ง€๋งŒ ์žฌํ˜„์œจ์ด 84% ์ˆ˜์ค€์œผ๋กœ ๋–จ์–ด์ง„๋‹ค.\")\n", "print(f\"์Šค์ผ€์ค„๋ง ๊ฒฐ์ • ์ง€์—ฐ ํ‰๊ท  {np.mean(dec):.2f} ms, ์ตœ๋Œ€ {np.max(dec):.1f} ms (๋ชฉํ‘œ 200 ms ์ดํ•˜: {'๋‹ฌ์„ฑ' if np.max(dec) <= 200 else '๋ฏธ๋‹ฌ'})\")\n", "print(f\"10์ข… ์ค‘ ๊ธด๊ธ‰ ์ „๋‹ฌ๋ฅ  85% ์ด์ƒ ์‹œ๋‚˜๋ฆฌ์˜ค: {(df['๊ฐœ์„  ๊ธด๊ธ‰์ „๋‹ฌ๋ฅ '] >= 0.85).sum()}/10\")\n", "\n", "# ๊ธฐ๋Šฅ๋ณ„ ๊ธฐ์—ฌ๋„(๊ฐ€์žฅ ์–ด๋ ค์šด ์‹œ๋‚˜๋ฆฌ์˜ค 10์—์„œ ํ•˜๋‚˜์”ฉ ๋„๊ธฐ)\n", "name, msgs, lk = SCEN[-1]\n", "link = Link(12000, seed=1, **lk)\n", "print(f\"\\n[๊ธฐ๋Šฅ๋ณ„ ๊ธฐ์—ฌ๋„ - {name}]\")\n", "for label, kw in [(\"์ „์ฒด ๊ฐœ์„  ๊ธฐ๋Šฅ\", ADV), (\"์กฐ๊ฐ ๋‹จ์œ„ ์„ ์  ๋”\", {**ADV, \"preempt\": False}), (\"๊ณ ์ • RTO 4์ดˆ\", {**ADV, \"rto_mode\": \"fixed\"}),\n", " (\"์ค‘๋ณต ์ „์†ก ๋”\", {**ADV, \"redundancy\": False}), (\"์กฐ๊ฐ ํฌ๊ธฐ ์กฐ์ • ๋”\", {**ADV, \"adapt_mtu\": False})]:\n", " r, s = simulate(msgs, link, **kw)\n", " print(f\"{label:14s} ๊ธด๊ธ‰ {urgent_rate(r):.3f} ์ข…ํ•ฉ {summarize(r)[1]:.3f} ๋ถˆํ•„์š” ์žฌ์ „์†ก {s['spurious']}ํšŒ\")\n", "print(\"๊ด€์ฐฐ: ์ค‘๊ณ„ ์ง€์—ฐ์ด ์ปค์ง€๋ฉด ๊ณ ์ • RTO๋Š” ACK๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ๋ชปํ•˜๊ณ  ๋ถˆํ•„์š”ํ•œ ์žฌ์ „์†ก์„ ๋ฐ˜๋ณตํ•ด ๋Œ€์—ญํญ์„ ๋‚ญ๋น„ํ•œ๋‹ค.\")\n", "\n", "plt.figure(figsize=(9, 4))\n", "x = np.arange(len(df))\n", "plt.bar(x - 0.2, df[\"๊ธฐ์กด ๊ธด๊ธ‰์ „๋‹ฌ๋ฅ \"], 0.4, label=\"baseline PQ (M3)\")\n", "plt.bar(x + 0.2, df[\"๊ฐœ์„  ๊ธด๊ธ‰์ „๋‹ฌ๋ฅ \"], 0.4, label=\"adaptive scheduler\")\n", "plt.axhline(0.85, ls=\"--\", c=\"gray\"); plt.xticks(x, [f\"S{i + 1}\" for i in x]); plt.ylim(0, 1.05)\n", "plt.ylabel(\"urgent on-time ratio\"); plt.title(\"FLASH/IMMEDIATE on-time delivery in 10 stress scenarios (synthetic)\"); plt.legend()\n", "plt.tight_layout(); plt.show()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 04 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", " ์‹œ๋‚˜๋ฆฌ์˜ค ์ „๋ฌธ ๊ธฐ์กด ๊ธด๊ธ‰์ „๋‹ฌ๋ฅ  ๊ฐœ์„  ๊ธด๊ธ‰์ „๋‹ฌ๋ฅ  ๊ธฐ์กด ์ข…ํ•ฉ ๊ฐœ์„  ์ข…ํ•ฉ ์—ดํ™”ํƒ์ง€ ์žฌ์ „์†ก\n", " 1 ํ‰์‹œ 258 0.885 1.000 0.940 1.000 14/14 96\n", " 2 ๋ฒ„์ŠคํŠธ ์†์‹ค 258 0.865 1.000 0.931 1.000 42/43 211\n", " 3 ์žฌ๋ฐ 60์ดˆ 258 0.846 1.000 0.921 1.000 15/15 167\n", " 4 ๋‹จ์† ์žฌ๋ฐ 3ํšŒ 258 0.865 0.981 0.931 0.990 15/15 169\n", " 5 ๋Œ€์—ญํญ 50% ๊ฐ์†Œ 258 0.827 1.000 0.910 1.000 15/16 97\n", " 6 ์ค‘๊ณ„ ์ง€์—ฐ ์ฆ๊ฐ€ 258 0.865 1.000 0.930 1.000 13/14 87\n", " 7 ํŠธ๋ž˜ํ”ฝ 2๋ฐฐ 501 0.966 1.000 0.795 0.808 24/24 121\n", " 8 ๋Œ€์šฉ๋Ÿ‰ ์ „๋ฌธ ํญ์ฃผ 283 0.827 1.000 0.867 0.958 16/19 101\n", " 9 ๋ฒ„์ŠคํŠธ+๋Œ€์—ญํญ ๊ฐ์†Œ 258 0.827 1.000 0.911 1.000 46/48 219\n", "10 ์žฌ๋ฐ+ํŠธ๋ž˜ํ”ฝ2๋ฐฐ+์ง€์—ฐ 501 0.865 0.955 0.718 0.779 22/25 156\n", "\n", "๊ธด๊ธ‰ ์ „๋ฌธ(FLASHยทIMMEDIATE) ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ  ํ‰๊ท : ๊ธฐ์กด 86.4% -> ๊ฐœ์„  99.4% (๋ชฉํ‘œ 85% ์ด์ƒ: ๋‹ฌ์„ฑ)\n", "๋งํฌ ์—ดํ™”(๋ณตํ•ฉ ์œ„ํ˜‘) ํƒ์ง€ ์žฌํ˜„์œจ 95.3% (222/233๊ตฌ๊ฐ„, ๋ชฉํ‘œ 85% ์ด์ƒ: ๋‹ฌ์„ฑ), ์ •์ƒ ๊ตฌ๊ฐ„ ์˜ค๊ฒฝ๋ณด 9.7%\n", "์ฐธ๊ณ : ์—ดํ™” ํŒ์ • ๊ธฐ์ค€(์ตœ๊ทผ ์†์‹ค๋ฅ  ์ถ”์ •๊ฐ’)์„ 0.12์—์„œ 0.2๋กœ ์˜ฌ๋ฆฌ๋ฉด ์ •์ƒ ๊ตฌ๊ฐ„ ์˜ค๊ฒฝ๋ณด๋Š” ์ค„์ง€๋งŒ ์žฌํ˜„์œจ์ด 84% ์ˆ˜์ค€์œผ๋กœ ๋–จ์–ด์ง„๋‹ค.\n", "์Šค์ผ€์ค„๋ง ๊ฒฐ์ • ์ง€์—ฐ ํ‰๊ท  0.04 ms, ์ตœ๋Œ€ 39.3 ms (๋ชฉํ‘œ 200 ms ์ดํ•˜: ๋‹ฌ์„ฑ)\n", "10์ข… ์ค‘ ๊ธด๊ธ‰ ์ „๋‹ฌ๋ฅ  85% ์ด์ƒ ์‹œ๋‚˜๋ฆฌ์˜ค: 10/10\n", "\n", "[๊ธฐ๋Šฅ๋ณ„ ๊ธฐ์—ฌ๋„ - 10 ์žฌ๋ฐ+ํŠธ๋ž˜ํ”ฝ2๋ฐฐ+์ง€์—ฐ]\n", "์ „์ฒด ๊ฐœ์„  ๊ธฐ๋Šฅ ๊ธด๊ธ‰ 0.955 ์ข…ํ•ฉ 0.779 ๋ถˆํ•„์š” ์žฌ์ „์†ก 0ํšŒ\n", "์กฐ๊ฐ ๋‹จ์œ„ ์„ ์  ๋” ๊ธด๊ธ‰ 0.910 ์ข…ํ•ฉ 0.755 ๋ถˆํ•„์š” ์žฌ์ „์†ก 0ํšŒ\n", "๊ณ ์ • RTO 4์ดˆ ๊ธด๊ธ‰ 0.955 ์ข…ํ•ฉ 0.746 ๋ถˆํ•„์š” ์žฌ์ „์†ก 513ํšŒ\n", "์ค‘๋ณต ์ „์†ก ๋” ๊ธด๊ธ‰ 0.966 ์ข…ํ•ฉ 0.786 ๋ถˆํ•„์š” ์žฌ์ „์†ก 0ํšŒ\n", "์กฐ๊ฐ ํฌ๊ธฐ ์กฐ์ • ๋” ๊ธด๊ธ‰ 0.955 ์ข…ํ•ฉ 0.776 ๋ถˆํ•„์š” ์žฌ์ „์†ก 0ํšŒ\n", "๊ด€์ฐฐ: ์ค‘๊ณ„ ์ง€์—ฐ์ด ์ปค์ง€๋ฉด ๊ณ ์ • RTO๋Š” ACK๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ๋ชปํ•˜๊ณ  ๋ถˆํ•„์š”ํ•œ ์žฌ์ „์†ก์„ ๋ฐ˜๋ณตํ•ด ๋Œ€์—ญํญ์„ ๋‚ญ๋น„ํ•œ๋‹ค.\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 05. ๋„คํŠธ์›Œํฌ ํ˜ผ์žก๋„ ๊ธฐ๋ฐ˜ ์ „์†ก ์„ฑ๊ณต ํ™•๋ฅ  ๋ฐ ๋งํฌ ์‹ ๋ขฐ๋„ ์‚ฐ์ถœ ์—”์ง„ ๊ฐœ๋ฐœ\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.0์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "๋ฌด์„  ๋งํฌ ํ’ˆ์งˆ ๋ฐ ํ˜ผ์žก๋„๋ฅผ ๋ฐ˜์˜ํ•˜์—ฌ ์ „์†ก ์„ฑ๊ณต ํ™•๋ฅ ๊ณผ ๋งํฌ ์‹ ๋ขฐ๋„๋ฅผ ์‚ฐ์ถœํ•˜๋Š” ์—”์ง„์„ ๊ฐœ๋ฐœํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **๋งํฌ ํ’ˆ์งˆ๊ณผ ํ˜ผ์žก๋„๋ฅผ ๋ฐ˜์˜ํ•ด ์กฐ๊ฐ ์ „์†ก ์„ฑ๊ณต ํ™•๋ฅ ๊ณผ ๋งํฌ ์‹ ๋ขฐ๋„ ๋“ฑ๊ธ‰(High/Medium/Low)์„ ์‚ฐ์ถœํ•˜๋Š” ์—”์ง„์„ ๊ฐœ๋ฐœํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **SNR, ์ตœ๊ทผ ์†์‹ค๋ฅ , ํ ์ ์œ ์œจ, ์กฐ๊ฐ ํฌ๊ธฐ, ์ง€์—ฐ ๋ณ€๋™์„ ํŠน์ง•์œผ๋กœ ํ•™์Šต์šฉยท๊ฒ€์ฆ์šฉ ๊ฐ€์ƒ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚˜๋ˆ  ๊ทธ๋ž˜๋””์–ธํŠธ ๋ถ€์ŠคํŒ… ์—”์ง„์„ ํ•™์Šต ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ฒ€์ฆํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/verification/c202_confidence_engine.py` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ์‹ ๋ขฐ๋„ ๋“ฑ๊ธ‰(High/Med/Low) ๋ถ€์—ฌ ๊ธฐ์ค€์˜ ๊ตฐ์‚ฌ์  ํƒ€๋‹น์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ์˜ค๊ฒฝ๋ณด์œจ(FAR) 40% ์ด์ƒ ๊ฐ์ถ•, ์‹ ๋ขฐ๋„ ์Šค์ฝ”์–ด ์‚ฐ์ถœ ์ผ๊ด€์„ฑ 90% ์ด์ƒ\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ์˜ค๊ฒฝ๋ณด ํ•„ํ„ฐ๋ง ์ „ํ›„ ๋น„๊ต ์ง€ํ‘œ ์‚ฐ์ถœ ๋ฐ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์™„๋ฃŒ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ROC-AUC ๊ณก์„  ๋ถ„์„ ๋ฐ ์ž„๊ณ„๊ฐ’ ๋ฏผ๊ฐ๋„ ํ…Œ์ŠคํŠธ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"๋ฌด์„  ๋งํฌ ํ’ˆ์งˆ(SNRยท์ตœ๊ทผ ์†์‹ค๋ฅ ยท์ง€์—ฐ ๋ณ€๋™)๊ณผ ํ˜ผ์žก๋„(ํ ์ ์œ ์œจ)ยท์กฐ๊ฐ ํฌ๊ธฐ๋กœ ์ „์†ก ์„ฑ๊ณต ํ™•๋ฅ ๊ณผ ๋งํฌ ์‹ ๋ขฐ๋„ ๋“ฑ๊ธ‰(High/Medium/Low)์„ ์‚ฐ์ถœํ•˜๋Š” ์—”์ง„์„ ๊ฐœ๋ฐœํ•˜๊ณ , ๋‹จ์ˆœ ๊ทœ์น™ ๋Œ€๋น„ '๋งํฌ ๋ถˆ๋Ÿ‰' ์˜ค๊ฒฝ๋ณด์œจ(FAR) ๊ฐ์ถ•, ROC-AUC, ์ž„๊ณ„๊ฐ’ ๋ฏผ๊ฐ๋„, ์ ์ˆ˜ ์ผ๊ด€์„ฑ์„ ๊ฒ€์ฆํ•œ๋‹ค.\"\"\"\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "from sklearn.ensemble import HistGradientBoostingClassifier\n", "from sklearn.inspection import permutation_importance\n", "from sklearn.metrics import roc_auc_score, roc_curve\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์ „์ˆ  ์ „๋ฌธ(๋ฉ”์‹œ์ง€) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยทํ†ต์‹ ๋งยท์ œ์› ์ •๋ณด ์—†์Œ)\n", "# ๊ธด๊ธ‰๋„ 4๋‹จ๊ณ„(FLASH/IMMEDIATE/PRIORITY/ROUTINE)์™€ ์ „๋ฌธ ์œ ํ˜•๋ณ„ ํฌ๊ธฐ๋ฅผ ๊ฐ€์ง„ ์ „๋ฌธ์„ ๋งŒ๋“ ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PREC = [\"FLASH\", \"IMMEDIATE\", \"PRIORITY\", \"ROUTINE\"]\n", "RANK = {p: i for i, p in enumerate(PREC)}\n", "DEADLINE = {\"FLASH\": 10, \"IMMEDIATE\": 30, \"PRIORITY\": 120, \"ROUTINE\": 600} # ๊ฐ€์ƒ ์ „๋‹ฌ ๋ชฉํ‘œ ์‹œ๊ฐ„(์ดˆ)\n", "MSG_TYPES = { # ์œ ํ˜•: (๊ธด๊ธ‰๋„ ํ›„๋ณด, ํ™•๋ฅ , ๋ณธ๋ฌธ ํฌ๊ธฐ ๋ฒ”์œ„ bytes, ๋ฐœ์ƒ ๋น„์œจ)\n", " \"CONTACT\": ([\"FLASH\", \"IMMEDIATE\"], [0.7, 0.3], (120, 300), 0.08),\n", " \"MEDEVAC\": ([\"FLASH\", \"IMMEDIATE\"], [0.4, 0.6], (150, 350), 0.05),\n", " \"SITREP\": ([\"IMMEDIATE\", \"PRIORITY\"], [0.2, 0.8], (600, 2500), 0.20),\n", " \"POSREP\": ([\"PRIORITY\", \"ROUTINE\"], [0.5, 0.5], (60, 120), 0.35),\n", " \"LOGREQ\": ([\"PRIORITY\", \"ROUTINE\"], [0.4, 0.6], (300, 1200), 0.14),\n", " \"ADMIN\": ([\"ROUTINE\"], [1.0], (1000, 5000), 0.18),\n", "}\n", "\n", "\n", "def make_messages(duration=600, rate=0.12, seed=7, t_start=0.0, prefix=\"M\"):\n", " \"\"\"duration์ดˆ ๋™์•ˆ ํ‰๊ท  rate๊ฑด/์ดˆ๋กœ ๋ฐœ์ƒํ•˜๋Š” ๊ฐ€์ƒ ์ „๋ฌธ ๋ชฉ๋ก.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " n = int(rng.poisson(rate * duration))\n", " times = np.sort(rng.uniform(t_start, t_start + duration, n))\n", " types = list(MSG_TYPES)\n", " msgs = []\n", " for i, t0 in enumerate(times):\n", " ty = types[rng.choice(len(types), p=[MSG_TYPES[k][3] for k in types])]\n", " cands, p, (lo, hi), _ = MSG_TYPES[ty]\n", " msgs.append({\"id\": f\"{prefix}{i:04d}\", \"t0\": float(t0), \"type\": ty, \"prec\": str(rng.choice(cands, p=p)),\n", " \"size\": int(rng.integers(lo, hi + 1)), \"src\": f\"U{int(rng.integers(1, 9)):02d}\"})\n", " return msgs\n", "\n", "\n", "class Link:\n", " \"\"\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ํ˜‘๋Œ€์—ญ ๋ฌด์„  ๋งํฌ(๊ธฐ๋ณธ 2.4kbps). ์ดˆ ๋‹จ์œ„ ์ƒํƒœ(good/bad/jam)์— ๋”ฐ๋ผ\n", " ๋น„ํŠธ์˜ค๋ฅ˜์œจ์ด ๋ฐ”๋€Œ๊ณ (Gilbert-Elliott ๋ชจ๋ธ), ๊ตฌ๊ฐ„๋ณ„ ๋Œ€์—ญํญ ๊ฐ์†Œ๋„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.\"\"\"\n", " BER = {\"good\": 1e-5, \"bad\": 2e-4, \"jam\": 1.2e-3}\n", "\n", " def __init__(self, horizon=4000, bw_bps=2400, seed=1, p_gb=0.02, p_bg=0.15, jam=(), bw_drop=(), rtt=(1.2, 0.4)):\n", " rng = np.random.default_rng(seed)\n", " st, s = [], \"good\"\n", " for _ in range(int(horizon) + 1):\n", " s = (\"bad\" if rng.random() < p_gb else \"good\") if s == \"good\" else (\"good\" if rng.random() < p_bg else \"bad\")\n", " st.append(s)\n", " for a, b in jam:\n", " for k in range(int(a), min(int(b), len(st))):\n", " st[k] = \"jam\"\n", " self.states, self.bw_bps, self.bw_drop, self.rtt_par = st, bw_bps, list(bw_drop), rtt\n", "\n", " def state(self, t):\n", " return self.states[min(int(t), len(self.states) - 1)]\n", "\n", " def bw(self, t):\n", " f = 1.0\n", " for a, b, fac in self.bw_drop:\n", " if a <= t < b:\n", " f = fac\n", " return self.bw_bps * f\n", "\n", " def p_loss(self, nbytes, t):\n", " return 1 - (1 - self.BER[self.state(t)]) ** (8 * nbytes)\n", "\n", " def rtt(self, rng):\n", " return self.rtt_par[0] + rng.exponential(self.rtt_par[1])\n", "# ---------------------------------------------------------------------------\n", "\n", "HDR = 32\n", "FEATS = [\"snr_db\", \"recent_loss\", \"queue_util\", \"pkt_bytes\", \"rtt_jitter\"]\n", "\n", "\n", "def make_link_windows(n=6000, seed=1):\n", " \"\"\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] 1์ดˆ ์ฐฝ๋งˆ๋‹ค ๋งํฌ ๊ด€์ธก๊ฐ’๊ณผ ๊ทธ ์ฐฝ์—์„œ ๋ณด๋‚ธ ์กฐ๊ฐ 1๊ฐœ์˜ ์„ฑ๊ณต ์—ฌ๋ถ€. ์‹ค์ œ ์„ฑ๊ณต ํ™•๋ฅ (p_true)๋„ ํ•จ๊ป˜ ๋‘”๋‹ค.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " link = Link(n, seed=seed, p_gb=0.05, p_bg=0.15, jam=[(1000, 1060), (3500, 3540), (5000, 5080)])\n", " snr_mu = {\"good\": 17, \"bad\": 9, \"jam\": 2}\n", " rows = []\n", " for k in range(1, n):\n", " s, prev = link.state(k), link.state(k - 1)\n", " size = int(rng.choice([64, 128, 256])) + HDR\n", " util = float(np.clip(rng.beta(2, 3) + (0.35 if 2000 <= k < 2600 else 0), 0, 1)) # ํ˜ผ์žก ๊ตฌ๊ฐ„ ํฌํ•จ\n", " p_true = (1 - link.p_loss(size, k)) * (1 - np.clip((util - 0.85) * 4, 0, 0.6)) # ๋น„ํŠธ์˜ค๋ฅ˜ + ํ ๋„˜์นจ\n", " rows.append({\"snr_db\": snr_mu[s] + rng.normal(0, 2.5), \"recent_loss\": rng.binomial(10, link.p_loss(288, k - 1)) / 10,\n", " \"queue_util\": util, \"pkt_bytes\": size, \"rtt_jitter\": rng.exponential(0.3) + (0.4 if s != \"good\" else 0),\n", " \"state\": s, \"p_true\": p_true, \"ok\": int(rng.random() < p_true)})\n", " return pd.DataFrame(rows)\n", "\n", "\n", "def grade(p):\n", " return np.where(p >= 0.85, \"High\", np.where(p >= 0.6, \"Medium\", \"Low\"))\n", "\n", "\n", "def far_recall(alarm, bad):\n", " far = float((alarm & ~bad).sum() / max(alarm.sum(), 1))\n", " return round(far, 3), round(float((alarm & bad).sum() / max(bad.sum(), 1)), 3), int(alarm.sum())\n", "\n", "\n", "train, test = make_link_windows(6000, seed=1), make_link_windows(6000, seed=2) # ํ•™์Šต์šฉ / ๊ฒ€์ฆ์šฉ ๋ถ„๋ฆฌ\n", "def new_model():\n", " return HistGradientBoostingClassifier(max_depth=3, max_iter=150, learning_rate=0.05, random_state=0)\n", "\n", "\n", "model = new_model().fit(train[FEATS], train[\"ok\"])\n", "test[\"p\"] = model.predict_proba(test[FEATS])[:, 1]\n", "test[\"grade\"] = grade(test[\"p\"])\n", "bad = (test[\"p_true\"] < 0.6).values # ์‹ค์ œ ๋ถˆ๋Ÿ‰ ๋งํฌ(์ •๋‹ต, ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ๋งŒ ์•Œ๊ณ  ์žˆ์Œ)\n", "\n", "auc = roc_auc_score(test[\"ok\"], test[\"p\"])\n", "print(f\"์ „์†ก ์„ฑ๊ณต ์˜ˆ์ธก ROC-AUC {auc:.3f} (๊ฒ€์ฆ ์ฐฝ {len(test)}๊ฐœ, ์‹ค์ œ ๋ถˆ๋Ÿ‰ ๋งํฌ ์ฐฝ {bad.sum()}๊ฐœ)\")\n", "\n", "rule = ((test[\"snr_db\"] < 12) | (test[\"recent_loss\"] >= 0.4)).values # ๊ธฐ์กด: ๋‹จ์ˆœ ๊ทœ์น™ ๊ฒฝ๋ณด\n", "eng = (test[\"p\"] < 0.6).values # ์—”์ง„: ์„ฑ๊ณต ํ™•๋ฅ  0.6 ๋ฏธ๋งŒ ๊ฒฝ๋ณด\n", "f0, r0, n0 = far_recall(rule, bad)\n", "f1, r1, n1 = far_recall(eng, bad)\n", "print(pd.DataFrame([{\"๋ฐฉ์‹\": \"๊ธฐ์กด ๊ทœ์น™(SNR<12dB ๋˜๋Š” ์†์‹ค>=40%)\", \"๊ฒฝ๋ณด\": n0, \"์˜ค๊ฒฝ๋ณด์œจ\": f0, \"๋ถˆ๋Ÿ‰ ์žฌํ˜„์œจ\": r0},\n", " {\"๋ฐฉ์‹\": \"์‹ ๋ขฐ๋„ ์—”์ง„(์„ฑ๊ณตํ™•๋ฅ <0.6)\", \"๊ฒฝ๋ณด\": n1, \"์˜ค๊ฒฝ๋ณด์œจ\": f1, \"๋ถˆ๋Ÿ‰ ์žฌํ˜„์œจ\": r1}]).to_string(index=False))\n", "cut = 1 - f1 / max(f0, 1e-9)\n", "print(f\"์˜ค๊ฒฝ๋ณด์œจ ๊ฐ์ถ• {cut:.1%} (๋ชฉํ‘œ 40% ์ด์ƒ: {'๋‹ฌ์„ฑ' if cut >= 0.4 else '๋ฏธ๋‹ฌ'})\")\n", "\n", "# ์ž„๊ณ„๊ฐ’ ๋ฏผ๊ฐ๋„\n", "print(\"\\n๊ฒฝ๋ณด ์ž„๊ณ„๊ฐ’ ๊ฒฝ๋ณด์ˆ˜ ์˜ค๊ฒฝ๋ณด์œจ ๋ถˆ๋Ÿ‰ ์žฌํ˜„์œจ\")\n", "for th in [0.4, 0.5, 0.6, 0.7, 0.8]:\n", " f, r, n = far_recall((test[\"p\"] < th).values, bad)\n", " print(f\" {th:.1f} {n:5d} {f:.3f} {r:.3f}\")\n", "\n", "# ์ ์ˆ˜ ์ผ๊ด€์„ฑ: ํ•™์Šต ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ์œผ๋กœ 10๋ฒˆ ๋‹ค์‹œ ๋ฝ‘์•„ ํ•™์Šตํ•ด๋„ ๊ฐ™์€ ๋“ฑ๊ธ‰์ด ๋‚˜์˜ค๋Š” ๋น„์œจ + ๋ถˆํ™•์‹ค์„ฑ(ํ‘œ์ค€ํŽธ์ฐจ)\n", "rng = np.random.default_rng(0)\n", "boot = []\n", "for b in range(10):\n", " idx = rng.integers(0, len(train), len(train))\n", " m = new_model().fit(train.iloc[idx][FEATS], train.iloc[idx][\"ok\"])\n", " boot.append(m.predict_proba(test[FEATS])[:, 1])\n", "boot = np.array(boot)\n", "same = np.mean([np.mean(grade(bp) == test[\"grade\"].values) for bp in boot])\n", "test[\"unc\"] = boot.std(axis=0)\n", "print(f\"\\n์‹ ๋ขฐ๋„ ๋“ฑ๊ธ‰ ์ผ๊ด€์„ฑ {same:.1%} (๋ชฉํ‘œ 90% ์ด์ƒ: {'๋‹ฌ์„ฑ' if same >= 0.9 else '๋ฏธ๋‹ฌ'}), ์ ์ˆ˜ ๋ถˆํ™•์‹ค์„ฑ(ํ‘œ์ค€ํŽธ์ฐจ) ํ‰๊ท  {test['unc'].mean():.3f}, ์ตœ๋Œ€ {test['unc'].max():.3f}\")\n", "print(\"[์‹ค์ œ ๋งํฌ ์ƒํƒœ๋ณ„ ๋“ฑ๊ธ‰ ๋ถ„ํฌ]\")\n", "print(pd.crosstab(test[\"state\"], test[\"grade\"]).reindex(columns=[\"High\", \"Medium\", \"Low\"]).to_string())\n", "imp = permutation_importance(model, test[FEATS], test[\"ok\"], scoring=\"roc_auc\", n_repeats=3, random_state=0)\n", "print(\"ํŒ๋‹จ ๊ทผ๊ฑฐ(ํŠน์ง• ์ค‘์š”๋„, AUC ๊ฐ์†Œ๋Ÿ‰):\", {f: round(float(v), 3) for f, v in sorted(zip(FEATS, imp.importances_mean), key=lambda x: -x[1])})\n", "print(\"๋“ฑ๊ธ‰ ์šด์šฉ ๊ธฐ์ค€: High=์ •์ƒ ์ „์†ก, Medium=๊ธด๊ธ‰ ์ „๋ฌธ ์ค‘๋ณต ์ „์†ก ๊ฒ€ํ† , Low=ํ†ต์‹ ๊ด€์—๊ฒŒ ๊ฒฝ๋กœ ๋ณ€๊ฒฝยท์ค‘๊ณ„ ๊ฒ€ํ†  ์š”์ฒญ(์ž๋™ ์ „ํ™˜ ์•„๋‹˜)\")\n", "\n", "fig, ax = plt.subplots(1, 2, figsize=(11, 4))\n", "fpr, tpr, _ = roc_curve(test[\"ok\"], test[\"p\"])\n", "ax[0].plot(fpr, tpr); ax[0].plot([0, 1], [0, 1], \"k--\"); ax[0].set_title(f\"ROC (AUC={auc:.3f})\"); ax[0].set_xlabel(\"FPR\"); ax[0].set_ylabel(\"TPR\")\n", "seg = test.iloc[900:1200]\n", "ax[1].plot(seg.index, seg[\"p\"], label=\"success prob.\"); ax[1].fill_between(seg.index, seg[\"p\"] - 2 * seg[\"unc\"], seg[\"p\"] + 2 * seg[\"unc\"], alpha=0.3, label=\"+-2 sd\")\n", "ax[1].plot(seg.index, seg[\"p_true\"], \"k:\", label=\"true prob.\"); ax[1].axhline(0.6, c=\"r\", ls=\"--\"); ax[1].legend(fontsize=8)\n", "ax[1].set_title(\"Link reliability score with uncertainty (jamming at t=1000-1060)\")\n", "plt.tight_layout(); plt.show()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 05 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "์ „์†ก ์„ฑ๊ณต ์˜ˆ์ธก ROC-AUC 0.891 (๊ฒ€์ฆ ์ฐฝ 5999๊ฐœ, ์‹ค์ œ ๋ถˆ๋Ÿ‰ ๋งํฌ ์ฐฝ 293๊ฐœ)\n", " ๋ฐฉ์‹ ๊ฒฝ๋ณด ์˜ค๊ฒฝ๋ณด์œจ ๋ถˆ๋Ÿ‰ ์žฌํ˜„์œจ\n", "๊ธฐ์กด ๊ทœ์น™(SNR<12dB ๋˜๋Š” ์†์‹ค>=40%) 1707 0.878 0.713\n", " ์‹ ๋ขฐ๋„ ์—”์ง„(์„ฑ๊ณตํ™•๋ฅ <0.6) 363 0.342 0.816\n", "์˜ค๊ฒฝ๋ณด์œจ ๊ฐ์ถ• 61.0% (๋ชฉํ‘œ 40% ์ด์ƒ: ๋‹ฌ์„ฑ)\n", "\n", "๊ฒฝ๋ณด ์ž„๊ณ„๊ฐ’ ๊ฒฝ๋ณด์ˆ˜ ์˜ค๊ฒฝ๋ณด์œจ ๋ถˆ๋Ÿ‰ ์žฌํ˜„์œจ\n", " 0.4 159 0.069 0.505\n", " 0.5 220 0.150 0.638\n", " 0.6 363 0.342 0.816\n", " 0.7 709 0.626 0.904\n", " 0.8 1232 0.768 0.976\n", "\n", "์‹ ๋ขฐ๋„ ๋“ฑ๊ธ‰ ์ผ๊ด€์„ฑ 93.2% (๋ชฉํ‘œ 90% ์ด์ƒ: ๋‹ฌ์„ฑ), ์ ์ˆ˜ ๋ถˆํ™•์‹ค์„ฑ(ํ‘œ์ค€ํŽธ์ฐจ) ํ‰๊ท  0.023, ์ตœ๋Œ€ 0.215\n", "[์‹ค์ œ ๋งํฌ ์ƒํƒœ๋ณ„ ๋“ฑ๊ธ‰ ๋ถ„ํฌ]\n", "grade High Medium Low\n", "state \n", "bad 333 965 128\n", "good 4207 122 64\n", "jam 1 8 171\n", "ํŒ๋‹จ ๊ทผ๊ฑฐ(ํŠน์ง• ์ค‘์š”๋„, AUC ๊ฐ์†Œ๋Ÿ‰): {'snr_db': 0.131, 'queue_util': 0.068, 'rtt_jitter': 0.045, 'pkt_bytes': 0.016, 'recent_loss': 0.016}\n", "๋“ฑ๊ธ‰ ์šด์šฉ ๊ธฐ์ค€: High=์ •์ƒ ์ „์†ก, Medium=๊ธด๊ธ‰ ์ „๋ฌธ ์ค‘๋ณต ์ „์†ก ๊ฒ€ํ† , Low=ํ†ต์‹ ๊ด€์—๊ฒŒ ๊ฒฝ๋กœ ๋ณ€๊ฒฝยท์ค‘๊ณ„ ๊ฒ€ํ†  ์š”์ฒญ(์ž๋™ ์ „ํ™˜ ์•„๋‹˜)\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 06. ๋Œ€์—ญํญ ์‚ฌ์šฉ๋ฅ , ํ ๋Œ€๊ธฐ ํ˜„ํ™ฉ ๋ฐ ํŒจํ‚ท ์œ ์‹ค๋ฅ  ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง UI ๊ตฌํ˜„\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 2.5์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "๋Œ€์—ญํญ ์‚ฌ์šฉ๋ฅ , ํ ๋Œ€๊ธฐ ์ „๋ฌธ ํ˜„ํ™ฉ, ํŒจํ‚ท ์†์‹ค๋ฅ ์„ ์‹ค์‹œ๊ฐ„ ํ‘œ์ถœํ•˜๋Š” ํ†ต์‹  ๊ด€์ œ UI๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **๋Œ€์—ญํญ ์‚ฌ์šฉ๋ฅ , ๊ธด๊ธ‰๋„๋ณ„ ํ ๋Œ€๊ธฐ ์ „๋ฌธ, ํŒจํ‚ท ์†์‹ค๋ฅ , ๊ธฐํ•œ ์ž„๋ฐ•ยท์ดˆ๊ณผ ๊ฒฝ๋ณด๋ฅผ 1์ดˆ ์ฃผ๊ธฐ๋กœ ๋ณด์—ฌ ์ฃผ๋Š” ํ†ต์‹  ๊ด€์ œ ํ™”๋ฉด์„ ๊ตฌํ˜„ํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **์žฌ๋ฐ๊ณผ ๋Œ€์—ญํญ ๊ฐ์†Œ๊ฐ€ ํฌํ•จ๋œ 20๋ถ„ ์šด์šฉ ๊ธฐ๋ก์„ 1์ดˆ ์ฃผ๊ธฐ๋กœ ์ฝ์–ด ๊ด€์ œ ์ง€ํ‘œ๋ฅผ ๊ฐฑ์‹ ํ•˜๊ณ  ์ฒ˜๋ฆฌ์‹œ๊ฐ„์„ ์ธก์ • ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ฒ€์ฆํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/ui/c202_dashboard.py (๋˜๋Š” ์›น UI ์ปดํฌ๋„ŒํŠธ)` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ์ „์ˆ  ์ž‘์ „ ์ƒํ™ฉํŒ์œผ๋กœ์„œ์˜ ์ง๊ด€์„ฑ ๋ฐ ์ธ์ง€ ๋ถ€ํ•˜ ๊ฐ์†Œ๋„\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ํ™”๋ฉด ๊ฐฑ์‹  ์ฃผ๊ธฐ 1์ดˆ ์ด๋‚ด, UI ์‘๋‹ต์„ฑ 100ms ์ด๋‚ด\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ์ฃผ์š” ์ง€ํ‘œ ์‹œ๊ฐํ™” ์ปดํฌ๋„ŒํŠธ 4์ข… ์ด์ƒ ๋ Œ๋”๋ง ๋ฐ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์—ฐ๋™\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ์‚ฌ์šฉ์ž ์ธํ„ฐ๋ž™์…˜ ํ…Œ์ŠคํŠธ ๋ฐ ๋ชจ์˜ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ ๋ Œ๋”๋ง ๊ฒ€์ฆ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"์ „์†ก ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๊ธฐ๋ก์„ 1์ดˆ ์ฃผ๊ธฐ๋กœ ์ฝ์–ด ๋Œ€์—ญํญ ์‚ฌ์šฉ๋ฅ , ๊ธด๊ธ‰๋„๋ณ„ ํ ๋Œ€๊ธฐ ์ „๋ฌธ ์ˆ˜, ํŒจํ‚ท ์†์‹ค๋ฅ , ๊ธฐํ•œ ์ž„๋ฐ•ยท์ดˆ๊ณผ ๊ฒฝ๋ณด๋ฅผ ํ‘œ์ถœํ•˜๋Š” ํ†ต์‹  ๊ด€์ œ ํ™”๋ฉด(์•ผ๊ฐ„ ๋ชจ๋“œ)์„ ๊ตฌํ˜„ํ•˜๊ณ  ํ™”๋ฉด ๊ฐฑ์‹  ์ฒ˜๋ฆฌ์‹œ๊ฐ„์„ ์ธก์ •ํ•œ๋‹ค.\"\"\"\n", "import time\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์ „์ˆ  ์ „๋ฌธ(๋ฉ”์‹œ์ง€) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยทํ†ต์‹ ๋งยท์ œ์› ์ •๋ณด ์—†์Œ)\n", "# ๊ธด๊ธ‰๋„ 4๋‹จ๊ณ„(FLASH/IMMEDIATE/PRIORITY/ROUTINE)์™€ ์ „๋ฌธ ์œ ํ˜•๋ณ„ ํฌ๊ธฐ๋ฅผ ๊ฐ€์ง„ ์ „๋ฌธ์„ ๋งŒ๋“ ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PREC = [\"FLASH\", \"IMMEDIATE\", \"PRIORITY\", \"ROUTINE\"]\n", "RANK = {p: i for i, p in enumerate(PREC)}\n", "DEADLINE = {\"FLASH\": 10, \"IMMEDIATE\": 30, \"PRIORITY\": 120, \"ROUTINE\": 600} # ๊ฐ€์ƒ ์ „๋‹ฌ ๋ชฉํ‘œ ์‹œ๊ฐ„(์ดˆ)\n", "MSG_TYPES = { # ์œ ํ˜•: (๊ธด๊ธ‰๋„ ํ›„๋ณด, ํ™•๋ฅ , ๋ณธ๋ฌธ ํฌ๊ธฐ ๋ฒ”์œ„ bytes, ๋ฐœ์ƒ ๋น„์œจ)\n", " \"CONTACT\": ([\"FLASH\", \"IMMEDIATE\"], [0.7, 0.3], (120, 300), 0.08),\n", " \"MEDEVAC\": ([\"FLASH\", \"IMMEDIATE\"], [0.4, 0.6], (150, 350), 0.05),\n", " \"SITREP\": ([\"IMMEDIATE\", \"PRIORITY\"], [0.2, 0.8], (600, 2500), 0.20),\n", " \"POSREP\": ([\"PRIORITY\", \"ROUTINE\"], [0.5, 0.5], (60, 120), 0.35),\n", " \"LOGREQ\": ([\"PRIORITY\", \"ROUTINE\"], [0.4, 0.6], (300, 1200), 0.14),\n", " \"ADMIN\": ([\"ROUTINE\"], [1.0], (1000, 5000), 0.18),\n", "}\n", "\n", "\n", "def make_messages(duration=600, rate=0.12, seed=7, t_start=0.0, prefix=\"M\"):\n", " \"\"\"duration์ดˆ ๋™์•ˆ ํ‰๊ท  rate๊ฑด/์ดˆ๋กœ ๋ฐœ์ƒํ•˜๋Š” ๊ฐ€์ƒ ์ „๋ฌธ ๋ชฉ๋ก.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " n = int(rng.poisson(rate * duration))\n", " times = np.sort(rng.uniform(t_start, t_start + duration, n))\n", " types = list(MSG_TYPES)\n", " msgs = []\n", " for i, t0 in enumerate(times):\n", " ty = types[rng.choice(len(types), p=[MSG_TYPES[k][3] for k in types])]\n", " cands, p, (lo, hi), _ = MSG_TYPES[ty]\n", " msgs.append({\"id\": f\"{prefix}{i:04d}\", \"t0\": float(t0), \"type\": ty, \"prec\": str(rng.choice(cands, p=p)),\n", " \"size\": int(rng.integers(lo, hi + 1)), \"src\": f\"U{int(rng.integers(1, 9)):02d}\"})\n", " return msgs\n", "\n", "\n", "class Link:\n", " \"\"\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ํ˜‘๋Œ€์—ญ ๋ฌด์„  ๋งํฌ(๊ธฐ๋ณธ 2.4kbps). ์ดˆ ๋‹จ์œ„ ์ƒํƒœ(good/bad/jam)์— ๋”ฐ๋ผ\n", " ๋น„ํŠธ์˜ค๋ฅ˜์œจ์ด ๋ฐ”๋€Œ๊ณ (Gilbert-Elliott ๋ชจ๋ธ), ๊ตฌ๊ฐ„๋ณ„ ๋Œ€์—ญํญ ๊ฐ์†Œ๋„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.\"\"\"\n", " BER = {\"good\": 1e-5, \"bad\": 2e-4, \"jam\": 1.2e-3}\n", "\n", " def __init__(self, horizon=4000, bw_bps=2400, seed=1, p_gb=0.02, p_bg=0.15, jam=(), bw_drop=(), rtt=(1.2, 0.4)):\n", " rng = np.random.default_rng(seed)\n", " st, s = [], \"good\"\n", " for _ in range(int(horizon) + 1):\n", " s = (\"bad\" if rng.random() < p_gb else \"good\") if s == \"good\" else (\"good\" if rng.random() < p_bg else \"bad\")\n", " st.append(s)\n", " for a, b in jam:\n", " for k in range(int(a), min(int(b), len(st))):\n", " st[k] = \"jam\"\n", " self.states, self.bw_bps, self.bw_drop, self.rtt_par = st, bw_bps, list(bw_drop), rtt\n", "\n", " def state(self, t):\n", " return self.states[min(int(t), len(self.states) - 1)]\n", "\n", " def bw(self, t):\n", " f = 1.0\n", " for a, b, fac in self.bw_drop:\n", " if a <= t < b:\n", " f = fac\n", " return self.bw_bps * f\n", "\n", " def p_loss(self, nbytes, t):\n", " return 1 - (1 - self.BER[self.state(t)]) ** (8 * nbytes)\n", "\n", " def rtt(self, rng):\n", " return self.rtt_par[0] + rng.exponential(self.rtt_par[1])\n", "# ---------------------------------------------------------------------------\n", "\n", "# ---- ์šฐ์„ ์ˆœ์œ„ ํยท์žฌ์ „์†ก ์ด์‚ฐ ์‚ฌ๊ฑด ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ ๊ณตํ†ต ํ•จ์ˆ˜ ----\n", "HDR = 32 # ์ „๋ฌธ ์กฐ๊ฐ(ํŒจํ‚ท) ํ—ค๋” bytes (๋ฏธ์…˜ 2์˜ ํ—ค๋” ์••์ถ• ์ ์šฉ ์‹œ 12 bytes)\n", "\n", "\n", "def policy_key(m, t, policy, aging):\n", " if policy == \"fifo\": # ๋„์ฐฉ ์ˆœ์„œ\n", " return (m[\"t0\"],)\n", " if policy == \"strict\": # ๊ธด๊ธ‰๋„ ์šฐ์„ , ๊ฐ™์œผ๋ฉด ๋„์ฐฉ ์ˆœ์„œ\n", " return (RANK[m[\"prec\"]], m[\"t0\"])\n", " # aging: ๊ธด๊ธ‰๋„ + (๋Œ€๊ธฐ์‹œ๊ฐ„ / ์ „๋‹ฌ ๋ชฉํ‘œ์‹œ๊ฐ„) ๊ฐ€์ค‘. ์˜ค๋ž˜ ๊ธฐ๋‹ค๋ฆฐ ์ „๋ฌธ๊ณผ ๊ธฐํ•œ์ด ์ž„๋ฐ•ํ•œ ์ „๋ฌธ์ด ์•ž์œผ๋กœ ์˜จ๋‹ค.\n", " return (RANK[m[\"prec\"]] - aging * (t - m[\"t0\"]) / DEADLINE[m[\"prec\"]], m[\"t0\"])\n", "\n", "\n", "def simulate(msgs, link, policy=\"strict\", mtu=256, preempt=True, rto_mode=\"fixed\", rto_s=4.0, aging=0.0,\n", " redundancy=False, adapt_mtu=False, hdr=HDR, seed=0, until=None, admit=None):\n", " \"\"\"์ „๋ฌธ ๋ชฉ๋ก์„ ๋งํฌ๋กœ ๋ณด๋‚ด๋Š” ์ด์‚ฐ ์‚ฌ๊ฑด ์‹œ๋ฎฌ๋ ˆ์ด์…˜.\n", " - ์ „๋ฌธ์€ mtu ํฌ๊ธฐ ์กฐ๊ฐ์œผ๋กœ ๋‚˜๋ˆ  ๋ณด๋‚ธ๋‹ค. ์กฐ๊ฐ์ด ์†์‹ค๋˜๋ฉด RTO ํ›„ ์žฌ์ „์†กํ•œ๋‹ค(์ž๋ฃŒ๋Š” ๋ฒ„๋ฆฌ์ง€ ์•Š์Œ).\n", " - preempt=False๋ฉด ํ•œ ์ „๋ฌธ์˜ ์กฐ๊ฐ์„ ๋๊นŒ์ง€ ์—ฐ์† ์ „์†กํ•œ๋‹ค(ํฐ ์ „๋ฌธ์ด ๊ธด๊ธ‰ ์ „๋ฌธ์„ ๋ง‰๋Š” ํ˜„์ƒ ์žฌํ˜„).\n", " - rto_mode='adaptive'๋ฉด ์™•๋ณต์‹œ๊ฐ„(RTT) ์ถ”์ •๊ฐ’์œผ๋กœ RTO๋ฅผ ๊ณ„์‚ฐํ•œ๋‹ค(Jacobson ๋ฐฉ์‹).\n", " - redundancy=True๋ฉด ๋งํฌ ์—ดํ™” ์ถ”์ • ์‹œ FLASHยทIMMEDIATE ์กฐ๊ฐ์„ 2ํšŒ ์—ฐ์† ์ „์†กํ•œ๋‹ค.\n", " - adapt_mtu=True๋ฉด ๋งํฌ ์—ดํ™” ์ถ”์ • ์‹œ ์กฐ๊ฐ ํฌ๊ธฐ๋ฅผ ์ค„์ธ๋‹ค.\n", " - admit(m, t)์ด False์ธ ์ „๋ฌธ์€ ์ƒˆ ์กฐ๊ฐ์„ ๋ณด๋‚ด์ง€ ์•Š๊ณ  ์ €์žฅํ•ด ๋‘”๋‹ค(์„ฑ๋Šฅ ์ €ํ•˜ ๋ชจ๋“œ).\"\"\"\n", " rng = np.random.default_rng(seed)\n", " pend = sorted(({**m, \"unsent\": m[\"size\"], \"ready\": [], \"frags\": [], \"done\": None, \"first\": None} for m in msgs), key=lambda m: m[\"t0\"])\n", " t, i, active, cur = 0.0, 0, [], None\n", " srtt, rttvar, loss = None, None, 0.0\n", " st = {\"tx\": 0, \"retx\": 0, \"spurious\": 0, \"bytes\": 0, \"decide_ms\": [], \"flag\": {}, \"log\": []}\n", " until = until if until is not None else (pend[-1][\"t0\"] if pend else 0) + 6000\n", " while (i < len(pend) or active) and t < until:\n", " while i < len(pend) and pend[i][\"t0\"] <= t:\n", " active.append(pend[i]); i += 1\n", " td = time.perf_counter()\n", " for m in active: # ACK๊ฐ€ ์ด๋ฏธ ๋„์ฐฉํ•œ ์žฌ์ „์†ก ์˜ˆ์•ฝ์€ ์ทจ์†Œ\n", " m[\"ready\"] = [e for e in m[\"ready\"] if not (e[1][\"ack\"] is not None and e[1][\"ack\"] <= t)]\n", " ready = [m for m in active if any(r <= t for r, _ in m[\"ready\"]) or (m[\"unsent\"] > 0 and (admit is None or admit(m, t)))]\n", " if not ready:\n", " nxt = ([pend[i][\"t0\"]] if i < len(pend) else []) + [r for m in active for r, _ in m[\"ready\"] if r > t]\n", " t = min(nxt) if nxt else t + 1.0\n", " continue\n", " if not preempt and cur in ready and cur[\"unsent\"] > 0:\n", " m = cur\n", " else:\n", " m = min(ready, key=lambda x: policy_key(x, t, policy, aging))\n", " st[\"decide_ms\"].append((time.perf_counter() - td) * 1000)\n", " due = [e for e in m[\"ready\"] if e[0] <= t]\n", " if due:\n", " e = min(due, key=lambda x: x[0]); m[\"ready\"].remove(e); frag = e[1]\n", " st[\"retx\"] += 1; st[\"spurious\"] += frag[\"recv\"]\n", " else:\n", " size = mtu if not adapt_mtu else (mtu if loss < 0.15 else (mtu // 2 if loss < 0.4 else mtu // 4))\n", " size = min(m[\"unsent\"], size); m[\"unsent\"] -= size\n", " frag = {\"b\": size, \"recv\": False, \"ack\": None, \"tries\": 0}\n", " m[\"frags\"].append(frag)\n", " cur = m\n", " m[\"first\"] = t if m[\"first\"] is None else m[\"first\"]\n", " nb = frag[\"b\"] + hdr\n", " copies = 2 if (redundancy and RANK[m[\"prec\"]] <= 1 and loss > 0.15) else 1\n", " for _ in range(copies):\n", " t += nb * 8 / link.bw(t)\n", " st[\"tx\"] += 1; st[\"bytes\"] += nb\n", " ok = rng.random() >= link.p_loss(nb, t)\n", " st[\"log\"].append((t, nb, ok, m[\"prec\"], m[\"id\"])) # ์ „์†ก ๊ธฐ๋ก(์‹œ๊ฐ, bytes, ์„ฑ๊ณต, ๊ธด๊ธ‰๋„, ์ „๋ฌธ)\n", " loss = 0.85 * loss + 0.15 * (0.0 if ok else 1.0) # ์ตœ๊ทผ ์†์‹ค๋ฅ  ์ถ”์ •(EWMA)\n", " st[\"flag\"][int(t)] = st[\"flag\"].get(int(t), False) or loss > 0.12 # ๋งํฌ ์—ดํ™” ํŒ์ •(์ดˆ ๋‹จ์œ„)\n", " if ok and not frag[\"recv\"]:\n", " frag[\"recv\"], frag[\"rt\"] = True, t\n", " r = link.rtt(rng); frag[\"ack\"] = t + r\n", " if srtt is None:\n", " srtt, rttvar = r, r / 2\n", " else:\n", " rttvar = 0.75 * rttvar + 0.25 * abs(srtt - r); srtt = 0.875 * srtt + 0.125 * r\n", " frag[\"tries\"] += 1\n", " rto = rto_s if (rto_mode == \"fixed\" or srtt is None) else float(np.clip(srtt + 4 * rttvar, 1.0, 30.0))\n", " if not frag[\"recv\"] or frag[\"ack\"] > t + rto: # ์†์‹ค ๋˜๋Š” ACK ์ง€์—ฐ -> ์žฌ์ „์†ก ์˜ˆ์•ฝ\n", " m[\"ready\"].append((t + rto, frag))\n", " if m[\"done\"] is None and m[\"unsent\"] == 0 and all(f[\"recv\"] for f in m[\"frags\"]):\n", " m[\"done\"] = max(f[\"rt\"] for f in m[\"frags\"])\n", " active = [x for x in active if not (x[\"unsent\"] == 0 and not x[\"ready\"] and all(f[\"recv\"] for f in x[\"frags\"]))]\n", " res = [{\"id\": m[\"id\"], \"type\": m[\"type\"], \"prec\": m[\"prec\"], \"size\": m[\"size\"], \"t0\": m[\"t0\"], \"done\": m[\"done\"], \"wait\": None if m[\"first\"] is None else m[\"first\"] - m[\"t0\"],\n", " \"retx\": sum(f[\"tries\"] for f in m[\"frags\"]) - len(m[\"frags\"]),\n", " \"latency\": None if m[\"done\"] is None else m[\"done\"] - m[\"t0\"]} for m in pend]\n", " for r in res:\n", " r[\"on_time\"] = r[\"latency\"] is not None and r[\"latency\"] <= DEADLINE[r[\"prec\"]]\n", " return res, st\n", "\n", "\n", "def summarize(res):\n", " \"\"\"๊ธด๊ธ‰๋„๋ณ„ ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ ยท์ง€์—ฐ. ์ข…ํ•ฉ ์ •ํ™•๋„ = ๊ธด๊ธ‰๋„๋ณ„ ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ ์˜ ํ‰๊ท .\"\"\"\n", " df = pd.DataFrame(res)\n", " rows = []\n", " for p in PREC:\n", " d = df[df[\"prec\"] == p]\n", " lat = d[\"latency\"].dropna()\n", " rows.append({\"๊ธด๊ธ‰๋„\": p, \"๊ฑด์ˆ˜\": len(d), \"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \": round(d[\"on_time\"].mean(), 3) if len(d) else np.nan,\n", " \"ํ‰๊ท  ์ง€์—ฐ(s)\": round(lat.mean(), 1) if len(lat) else np.nan,\n", " \"95% ์ง€์—ฐ(s)\": round(lat.quantile(0.95), 1) if len(lat) else np.nan, \"๋ฏธ์ „๋‹ฌ\": int(d[\"done\"].isna().sum())})\n", " tab = pd.DataFrame(rows)\n", " return tab, round(float(tab[\"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \"].mean()), 3)\n", "\n", "COLORS = {\"FLASH\": \"red\", \"IMMEDIATE\": \"orange\", \"PRIORITY\": \"deepskyblue\", \"ROUTINE\": \"gray\"}\n", "\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] 20๋ถ„ ์šด์šฉ: 300~360์ดˆ ์žฌ๋ฐ, 600~800์ดˆ ๋Œ€์—ญํญ 50% ๊ฐ์†Œ\n", "msgs = make_messages(1200, 0.21, seed=7)\n", "link = Link(12000, seed=1, jam=[(300, 360)], bw_drop=[(600, 800, 0.5)])\n", "res, st = simulate(msgs, link, \"aging\", aging=0.5, rto_mode=\"adaptive\", redundancy=True, adapt_mtu=True)\n", "log = pd.DataFrame(st[\"log\"], columns=[\"t\", \"bytes\", \"ok\", \"prec\", \"id\"])\n", "done = {r[\"id\"]: r[\"done\"] for r in res}\n", "\n", "\n", "def snapshot(now, window=10):\n", " \"\"\"now ์‹œ๊ฐ์˜ ๊ด€์ œ ์ง€ํ‘œ: ์ตœ๊ทผ window์ดˆ ์‚ฌ์šฉ๋ฅ ยท์†์‹ค๋ฅ , ๊ธด๊ธ‰๋„๋ณ„ ๋Œ€๊ธฐ ์ „๋ฌธ, ๊ฒฝ๋ณด ๋ชฉ๋ก.\"\"\"\n", " w = log[(log[\"t\"] > now - window) & (log[\"t\"] <= now)]\n", " util = w[\"bytes\"].sum() * 8 / (link.bw(now) * window)\n", " loss = 1 - w[\"ok\"].mean() if len(w) else 0.0\n", " waiting = [m for m in msgs if m[\"t0\"] <= now and (done[m[\"id\"]] is None or done[m[\"id\"]] > now)]\n", " depth = {p: sum(m[\"prec\"] == p for m in waiting) for p in PREC}\n", " alerts = []\n", " for m in waiting:\n", " age = now - m[\"t0\"]\n", " if age > DEADLINE[m[\"prec\"]]:\n", " alerts.append((\"๊ธฐํ•œ์ดˆ๊ณผ\", m[\"id\"], m[\"prec\"], round(age, 1)))\n", " elif RANK[m[\"prec\"]] <= 1 and age > 0.7 * DEADLINE[m[\"prec\"]]:\n", " alerts.append((\"๊ธฐํ•œ์ž„๋ฐ•\", m[\"id\"], m[\"prec\"], round(age, 1)))\n", " if loss > 0.3:\n", " alerts.append((\"๋งํฌ์—ดํ™”\", \"-\", f\"์†์‹ค๋ฅ  {loss:.0%}\", round(now, 0)))\n", " return {\"t\": now, \"util\": min(util, 1.0), \"loss\": loss, **depth, \"alerts\": alerts}\n", "\n", "\n", "# 1) 1์ดˆ ์ฃผ๊ธฐ ๊ฐฑ์‹  ๋ชจ์˜(1,200ํšŒ) + ์ฒ˜๋ฆฌ์‹œ๊ฐ„ ์ธก์ •\n", "frames, ms = [], []\n", "for now in range(1, 1201):\n", " t0 = time.perf_counter()\n", " frames.append(snapshot(float(now)))\n", " ms.append((time.perf_counter() - t0) * 1000)\n", "ts = pd.DataFrame([{k: v for k, v in f.items() if k != \"alerts\"} for f in frames])\n", "print(f\"๊ฐฑ์‹  1,200ํšŒ: ์ฒ˜๋ฆฌ์‹œ๊ฐ„ ํ‰๊ท  {np.mean(ms):.2f} ms, ์ตœ๋Œ€ {np.max(ms):.2f} ms (๋ชฉํ‘œ: ์ฃผ๊ธฐ 1์ดˆ ์ด๋‚ด, ์‘๋‹ต 100 ms ์ด๋‚ด -> {'๋‹ฌ์„ฑ' if np.max(ms) < 100 else '๋ฏธ๋‹ฌ'})\")\n", "\n", "# 2) ๊ตฌ๊ฐ„๋ณ„ ์ƒํ™ฉ ์š”์•ฝ\n", "ts[\"๊ตฌ๊ฐ„\"] = pd.cut(ts[\"t\"], [0, 300, 360, 600, 800, 1200], labels=[\"ํ‰์‹œ1\", \"์žฌ๋ฐ\", \"ํ‰์‹œ2\", \"๋Œ€์—ญํญ50%\", \"ํ‰์‹œ3\"])\n", "summary = ts.groupby(\"๊ตฌ๊ฐ„\", observed=True).agg(์‚ฌ์šฉ๋ฅ =(\"util\", \"mean\"), ์†์‹ค๋ฅ =(\"loss\", \"mean\"), FLASH๋Œ€๊ธฐ=(\"FLASH\", \"max\"),\n", " IMMEDIATE๋Œ€๊ธฐ=(\"IMMEDIATE\", \"max\"), ROUTINE๋Œ€๊ธฐ=(\"ROUTINE\", \"max\")).round(2)\n", "print(\"\\n[๊ตฌ๊ฐ„๋ณ„ ๊ด€์ œ ์ง€ํ‘œ: ํ‰๊ท  ์‚ฌ์šฉ๋ฅ ยท์†์‹ค๋ฅ , ์ตœ๋Œ€ ๋Œ€๊ธฐ ์ „๋ฌธ ์ˆ˜]\")\n", "print(summary.to_string())\n", "\n", "# 3) ๊ฒฝ๋ณด ํŒจ๋„(๊ธด๊ธ‰ ๊ฒฝ๋ณด ์šฐ์„ )\n", "all_alerts = pd.DataFrame([(f[\"t\"], *a) for f in frames for a in f[\"alerts\"]], columns=[\"์‹œ๊ฐ\", \"๊ฒฝ๋ณด\", \"์ „๋ฌธ\", \"๊ธด๊ธ‰๋„/๋‚ด์šฉ\", \"๊ฒฝ๊ณผ\"])\n", "first = all_alerts.drop_duplicates([\"๊ฒฝ๋ณด\", \"์ „๋ฌธ\"]).copy()\n", "print(f\"\\n๊ฒฝ๋ณด ๋ฐœ์ƒ(์ค‘๋ณต ์ œ์™ธ) {len(first)}๊ฑด:\", first[\"๊ฒฝ๋ณด\"].value_counts().to_dict())\n", "order = {\"๊ธฐํ•œ์ดˆ๊ณผ\": 0, \"๋งํฌ์—ดํ™”\": 1, \"๊ธฐํ•œ์ž„๋ฐ•\": 2}\n", "print(first.sort_values(by=[\"๊ฒฝ๋ณด\", \"์‹œ๊ฐ\"], key=lambda c: c.map(order) if c.name == \"๊ฒฝ๋ณด\" else c).head(8).to_string(index=False))\n", "\n", "# 4) ๊ด€์ œ ํ™”๋ฉด(์•ผ๊ฐ„ ๋ชจ๋“œ, ๊ตฌ์„ฑ์š”์†Œ 5์ข…)\n", "plt.style.use(\"dark_background\")\n", "fig = plt.figure(figsize=(13, 8))\n", "a1 = fig.add_subplot(2, 2, 1); a1.plot(ts[\"t\"], ts[\"util\"] * 100, c=\"lime\"); a1.set_title(\"Bandwidth utilization (%)\"); a1.set_ylim(0, 105)\n", "a2 = fig.add_subplot(2, 2, 2)\n", "a2.stackplot(ts[\"t\"], *[ts[p] for p in PREC], colors=[COLORS[p] for p in PREC], labels=PREC); a2.legend(loc=\"upper left\", fontsize=8)\n", "a2.set_title(\"Queued messages by precedence\")\n", "a3 = fig.add_subplot(2, 2, 3); a3.plot(ts[\"t\"], ts[\"loss\"] * 100, c=\"orange\"); a3.axhline(30, c=\"red\", ls=\"--\")\n", "a3.fill_between(ts[\"t\"], 0, 100, where=ts[\"loss\"] > 0.3, color=\"red\", alpha=0.25); a3.set_title(\"Packet loss rate (%) - red = link degraded\")\n", "for a in (a1, a2, a3):\n", " a.axvspan(300, 360, color=\"purple\", alpha=0.2); a.axvspan(600, 800, color=\"yellow\", alpha=0.08); a.set_xlabel(\"time (s)\")\n", "a4 = fig.add_subplot(2, 2, 4); a4.axis(\"off\")\n", "last = frames[-1]\n", "kpi = summarize(res)[0]\n", "txt = \"\\n\".join(f\"{r['๊ธด๊ธ‰๋„']:<10} on-time {r['๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ ']:.0%} p95 {r['95% ์ง€์—ฐ(s)']:>6}s\" for _, r in kpi.iterrows())\n", "a4.text(0, 0.95, f\"KPI (t={last['t']:.0f}s)\\n{txt}\\n\\nAlerts total: {len(first)}\\n overdue: {(first['๊ฒฝ๋ณด'] == '๊ธฐํ•œ์ดˆ๊ณผ').sum()}\\n\"\n", " f\" link degraded: {(first['๊ฒฝ๋ณด'] == '๋งํฌ์—ดํ™”').sum()}\", family=\"monospace\", fontsize=11, va=\"top\", color=\"white\")\n", "fig.suptitle(\"Tactical link monitor (night mode) - training data only\", color=\"white\")\n", "plt.tight_layout(); plt.show()\n", "print(\"\\nํ™”๋ฉด ๊ตฌ์„ฑ: ์‚ฌ์šฉ๋ฅ  ์ถ”์ด, ๊ธด๊ธ‰๋„๋ณ„ ํ ๋Œ€๊ธฐ, ์†์‹ค๋ฅ (์—ดํ™” ๊ตฌ๊ฐ„ ๊ฐ•์กฐ), KPIยท๊ฒฝ๋ณด ํŒจ๋„, ์žฌ๋ฐ/๋Œ€์—ญํญ ๊ฐ์†Œ ๊ตฌ๊ฐ„ ํ‘œ์‹œ\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 06 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "๊ฐฑ์‹  1,200ํšŒ: ์ฒ˜๋ฆฌ์‹œ๊ฐ„ ํ‰๊ท  0.76 ms, ์ตœ๋Œ€ 3.48 ms (๋ชฉํ‘œ: ์ฃผ๊ธฐ 1์ดˆ ์ด๋‚ด, ์‘๋‹ต 100 ms ์ด๋‚ด -> ๋‹ฌ์„ฑ)\n", "\n", "[๊ตฌ๊ฐ„๋ณ„ ๊ด€์ œ ์ง€ํ‘œ: ํ‰๊ท  ์‚ฌ์šฉ๋ฅ ยท์†์‹ค๋ฅ , ์ตœ๋Œ€ ๋Œ€๊ธฐ ์ „๋ฌธ ์ˆ˜]\n", " ์‚ฌ์šฉ๋ฅ  ์†์‹ค๋ฅ  FLASH๋Œ€๊ธฐ IMMEDIATE๋Œ€๊ธฐ ROUTINE๋Œ€๊ธฐ\n", "๊ตฌ๊ฐ„ \n", "ํ‰์‹œ1 0.84 0.03 1 1 9\n", "์žฌ๋ฐ 0.99 0.58 1 0 13\n", "ํ‰์‹œ2 0.99 0.06 1 1 17\n", "๋Œ€์—ญํญ50% 0.97 0.06 2 2 21\n", "ํ‰์‹œ3 0.98 0.05 1 2 27\n", "\n", "๊ฒฝ๋ณด ๋ฐœ์ƒ(์ค‘๋ณต ์ œ์™ธ) 4๊ฑด: {'๊ธฐํ•œ์ž„๋ฐ•': 2, '๋งํฌ์—ดํ™”': 1, '๊ธฐํ•œ์ดˆ๊ณผ': 1}\n", " ์‹œ๊ฐ ๊ฒฝ๋ณด ์ „๋ฌธ ๊ธด๊ธ‰๋„/๋‚ด์šฉ ๊ฒฝ๊ณผ\n", "805.0 ๊ธฐํ•œ์ดˆ๊ณผ M0178 FLASH 10.1\n", "303.0 ๋งํฌ์—ดํ™” - ์†์‹ค๋ฅ  36% 303.0\n", "780.0 ๊ธฐํ•œ์ž„๋ฐ• M0165 IMMEDIATE 21.7\n", "802.0 ๊ธฐํ•œ์ž„๋ฐ• M0178 FLASH 7.1\n", "\n", "ํ™”๋ฉด ๊ตฌ์„ฑ: ์‚ฌ์šฉ๋ฅ  ์ถ”์ด, ๊ธด๊ธ‰๋„๋ณ„ ํ ๋Œ€๊ธฐ, ์†์‹ค๋ฅ (์—ดํ™” ๊ตฌ๊ฐ„ ๊ฐ•์กฐ), KPIยท๊ฒฝ๋ณด ํŒจ๋„, ์žฌ๋ฐ/๋Œ€์—ญํญ ๊ฐ์†Œ ๊ตฌ๊ฐ„ ํ‘œ์‹œ\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 07. ์ตœ์šฐ์„  ์ˆœ์œ„ ์ „๋ฌธ ๊ฐ•์ œ ์ „์†ก ๋ฐ ํ ์ˆœ์„œ ์ˆ˜๋™ ์กฐ์ •์„ ์œ„ํ•œ ํ†ต์‹ ๊ด€ HITL ํ†ต์ œ ๊ธฐ๋Šฅ ์„ค๊ณ„\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.0์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "์ตœ์šฐ์„  ์ž‘์ „ ์ „๋ฌธ ๊ฐ•์ œ ์ „์†ก ๋ฐ ํ ์ˆœ์„œ ์ˆ˜๋™ ์กฐ์ •์„ ์œ„ํ•œ ํ†ต์‹ ๊ด€ HITL ์ œ์–ด ๊ธฐ๋Šฅ์„ ์„ค๊ณ„ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **ํ†ต์‹ ๊ด€์ด ์ตœ์šฐ์„  ์ „๋ฌธ ๊ฐ•์ œ ์ „์†ก๊ณผ ํ ์ˆœ์„œ ์ˆ˜๋™ ์กฐ์ •์„ ์ง์ ‘ ์ˆ˜ํ–‰ํ•˜๋Š” HITL ํ†ต์ œ ๊ธฐ๋Šฅ์„ ์„ค๊ณ„ํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **์‹œ์Šคํ…œ์€ ๋ณด๋ฅ˜ยท์•ž๋‹น๊ธฐ๊ธฐ๋ฅผ ์ถ”์ฒœ๋งŒ ํ•˜๊ณ  ํ ์ˆœ์„œ๋ฅผ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค ๊ณผ์ •์„ ์ง„ํ–‰ํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/hitl/c202_approval_workflow.py ๋ฐ docs/c202_hitl_guide.md` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ์ง€ํœ˜ ์ฑ…์ž„ ๋ถ„๊ณ„์„  ๋ช…ํ™•ํ™” ๋ฐ ์„ฑ๊ธ‰ํ•œ ์ž๋™ ๋‹จ์ • ๋ฐฉ์ง€ ์ฒด๊ณ„ ๋ถ€ํ•ฉ์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ์ธ๊ฐ„ ๊ฒ€ํ†  ํ(Queue) ๋Œ€๊ธฐ์‹œ๊ฐ„ ๋ฐ ์Šน์ธ/๋ฐ˜๋ ค ์ฒ˜๋ฆฌ ๋กœ๊น… ์„ฑ๊ณต๋ฅ  100%\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ์Šน์ธ/๋ฐ˜๋ ค ์ธํ„ฐํŽ˜์ด์Šค ๋™์ž‘ ๋ฐ ๊ฐ์‚ฌ ๋กœ๊ทธ(Audit Log) ์ƒ์„ฑ ๊ฒ€์ฆ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ๋ชจ์˜ ์กฐ์ž‘์ž ์Šน์ธ/๋ฐ˜๋ ค ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๋ฐ ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ ๊ฒ€์ฆ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"ํ†ต์‹ ๊ด€์ด ์ตœ์šฐ์„  ์ „๋ฌธ ๊ฐ•์ œ ์ „์†ก๊ณผ ํ ์ˆœ์„œ ์ˆ˜๋™ ์กฐ์ •์„ ์ง์ ‘ ์ˆ˜ํ–‰ํ•˜๋Š” HITL ํ†ต์ œ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•œ๋‹ค. ์‹œ์Šคํ…œ์€ ์ถ”์ฒœ๋งŒ ํ•˜๊ณ  ์ˆœ์„œ๋ฅผ ์Šค์Šค๋กœ ๋ฐ”๊พธ์ง€ ์•Š์œผ๋ฉฐ, ๊ทœ์น™ ์œ„๋ฐ˜ ์กฐ์ž‘์€ ์ฐจ๋‹จํ•˜๊ณ , FLASH๋ณด๋‹ค ์•ž์„œ๋Š” ๊ฐ•์ œ ์ „์†ก์€ ์ƒ๊ธ‰์ž ์Šน์ธ์„ ๊ฑฐ์น˜๊ฒŒ ํ•˜๋ฉฐ, ๋ชจ๋“  ์กฐ์ž‘์€ ์œ„ยท๋ณ€์กฐ ํ™•์ธ์ด ๊ฐ€๋Šฅํ•œ ๊ฐ์‚ฌ ๋กœ๊ทธ์— ๊ธฐ๋กํ•œ๋‹ค.\"\"\"\n", "import hashlib\n", "import json\n", "import pandas as pd\n", "\n", "ACTIONS = {\"FORCE_SEND\": \"๊ฐ•์ œ ์ „์†ก(๋งจ ์•ž)\", \"MOVE_UP\": \"ํ•œ ์นธ ์•ž์œผ๋กœ\", \"MOVE_DOWN\": \"ํ•œ ์นธ ๋’ค๋กœ\", \"HOLD\": \"๋ณด๋ฅ˜\", \"RELEASE\": \"๋ณด๋ฅ˜ ํ•ด์ œ\"}\n", "RANK = {\"FLASH\": 0, \"IMMEDIATE\": 1, \"PRIORITY\": 2, \"ROUTINE\": 3}\n", "\n", "\n", "class ControlError(ValueError):\n", " pass\n", "\n", "\n", "class FakeClock:\n", " t = 1000.0\n", "\n", " def __call__(self):\n", " return self.t\n", "\n", "\n", "class HITLQueue:\n", " def __init__(self, clock):\n", " self.q, self.held, self.req, self.log, self.clock = [], {}, {}, [], clock\n", "\n", " def _record(self, body):\n", " prev = self.log[-1][\"hash\"] if self.log else \"GENESIS\"\n", " body = {**body, \"ts\": round(self.clock(), 1)}\n", " h = hashlib.sha256((prev + json.dumps(body, ensure_ascii=False, sort_keys=True)).encode()).hexdigest()\n", " self.log.append({**body, \"prev\": prev, \"hash\": h})\n", "\n", " def verify_log(self):\n", " prev = \"GENESIS\"\n", " for n, rec in enumerate(self.log, start=1):\n", " body = {k: v for k, v in rec.items() if k not in (\"prev\", \"hash\")}\n", " if rec[\"prev\"] != prev or rec[\"hash\"] != hashlib.sha256((prev + json.dumps(body, ensure_ascii=False, sort_keys=True)).encode()).hexdigest():\n", " return False, n\n", " prev = rec[\"hash\"]\n", " return True, -1\n", "\n", " def submit(self, mid, prec):\n", " \"\"\"๊ธฐ๋ณธ ์ •๋ ฌ(๊ธด๊ธ‰๋„ -> ๋„์ฐฉ์ˆœ) ์œ„์น˜์— ๋„ฃ๋Š”๋‹ค. ์ˆ˜๋™ ์กฐ์ •๋œ ์ˆœ์„œ๋Š” ๋ณด์กดํ•œ๋‹ค.\"\"\"\n", " m = {\"id\": mid, \"prec\": prec, \"t0\": self.clock()}\n", " pos = next((i for i, x in enumerate(self.q) if RANK[x[\"prec\"]] > RANK[prec]), len(self.q))\n", " self.q.insert(pos, m)\n", " self._record({\"event\": \"SUBMIT\", \"id\": mid, \"prec\": prec, \"by\": \"SYSTEM\"})\n", "\n", " def recommend(self, link_grade):\n", " \"\"\"์‹œ์Šคํ…œ ์ถ”์ฒœ(์ž๋™ ์ ์šฉ ์•ˆ ํ•จ): ๋งํฌ Low๋ฉด ROUTINE ๋Œ€์šฉ๋Ÿ‰ ๋ณด๋ฅ˜ ๊ถŒ๊ณ  ๋“ฑ.\"\"\"\n", " out = []\n", " if link_grade == \"Low\":\n", " out += [f\"{m['id']} ๋ณด๋ฅ˜ ๊ถŒ๊ณ (ROUTINE)\" for m in self.q if m[\"prec\"] == \"ROUTINE\"][:2]\n", " old = [m for m in self.q if m[\"prec\"] == \"IMMEDIATE\" and self.clock() - m[\"t0\"] > 20]\n", " out += [f\"{m['id']} ์•ž๋‹น๊ธฐ๊ธฐ ๊ถŒ๊ณ (๊ธฐํ•œ ์ž„๋ฐ•)\" for m in old]\n", " self._record({\"event\": \"RECOMMEND\", \"items\": out, \"by\": \"SYSTEM\"})\n", " return out\n", "\n", " def act(self, mid, action, operator, reason, approver=None):\n", " if action not in ACTIONS:\n", " raise ControlError(f\"ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ ์กฐ์ž‘: {action}\")\n", " if not operator:\n", " raise ControlError(\"ํ†ต์‹ ๊ด€ ID ํ•„์š”\")\n", " if len(reason.strip()) < 5:\n", " raise ControlError(\"์กฐ์ž‘ ๊ทผ๊ฑฐ 5์ž ์ด์ƒ ํ•„์š”\")\n", " idx = next((i for i, m in enumerate(self.q) if m[\"id\"] == mid), None)\n", " if idx is None and not (action == \"RELEASE\" and mid in self.held):\n", " raise ControlError(\"ํ์— ์—†๋Š” ์ „๋ฌธ\")\n", " if action == \"FORCE_SEND\":\n", " m = self.q[idx]\n", " flash_waiting = any(x[\"prec\"] == \"FLASH\" for x in self.q if x[\"id\"] != mid)\n", " if m[\"prec\"] != \"FLASH\" and flash_waiting and not approver:\n", " self.req[mid] = {\"operator\": operator, \"reason\": reason, \"t\": self.clock()}\n", " self._record({\"event\": \"REQUEST\", \"id\": mid, \"action\": action, \"operator\": operator, \"reason\": reason})\n", " return \"์ƒ๊ธ‰์ž ์Šน์ธ ๋Œ€๊ธฐ(๋Œ€๊ธฐ ์ค‘์ธ FLASH๋ณด๋‹ค ์•ž์„ฌ)\"\n", " if approver == operator:\n", " raise ControlError(\"์š”์ฒญ์ž์™€ ์Šน์ธ์ž๋Š” ๋‹ฌ๋ผ์•ผ ํ•จ(2์ธ ํ™•์ธ)\")\n", " self.q.insert(0, self.q.pop(idx)); self.req.pop(mid, None)\n", " elif action in (\"MOVE_UP\", \"MOVE_DOWN\"):\n", " j = idx - 1 if action == \"MOVE_UP\" else idx + 1\n", " if not 0 <= j < len(self.q):\n", " raise ControlError(\"์ด๋™ํ•  ์œ„์น˜ ์—†์Œ\")\n", " if action == \"MOVE_UP\" and self.q[j][\"prec\"] == \"FLASH\" and self.q[idx][\"prec\"] != \"FLASH\" and not approver:\n", " raise ControlError(\"FLASH ์•ž์œผ๋กœ ์ด๋™์€ ์ƒ๊ธ‰์ž ์Šน์ธ ํ•„์š”\")\n", " self.q[idx], self.q[j] = self.q[j], self.q[idx]\n", " elif action == \"HOLD\":\n", " if self.q[idx][\"prec\"] == \"FLASH\" and (len(reason.strip()) < 15 or not approver):\n", " raise ControlError(\"FLASH ๋ณด๋ฅ˜๋Š” ๊ทผ๊ฑฐ 15์ž ์ด์ƒ + ์ƒ๊ธ‰์ž ์Šน์ธ ํ•„์š”\")\n", " self.held[mid] = self.q.pop(idx)\n", " elif action == \"RELEASE\":\n", " m = self.held.pop(mid)\n", " pos = next((i for i, x in enumerate(self.q) if RANK[x[\"prec\"]] > RANK[m[\"prec\"]]), len(self.q))\n", " self.q.insert(pos, m)\n", " self._record({\"event\": \"ACT\", \"id\": mid, \"action\": action, \"operator\": operator, \"approver\": approver, \"reason\": reason})\n", " return \"์ ์šฉ\"\n", "\n", "\n", "clock = FakeClock()\n", "hq = HITLQueue(clock)\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๋Œ€๊ธฐ ์ „๋ฌธ 7๊ฑด\n", "for mid, p in [(\"M0101\", \"ROUTINE\"), (\"M0102\", \"PRIORITY\"), (\"M0103\", \"FLASH\"), (\"M0104\", \"IMMEDIATE\"), (\"M0105\", \"ROUTINE\"), (\"M0106\", \"PRIORITY\"), (\"M0107\", \"IMMEDIATE\")]:\n", " clock.t += 3; hq.submit(mid, p)\n", "order0 = [m[\"id\"] for m in hq.q]\n", "print(\"์ดˆ๊ธฐ ํ ์ˆœ์„œ:\", order0)\n", "clock.t += 20\n", "print(\"์‹œ์Šคํ…œ ์ถ”์ฒœ(์ž๋™ ์ ์šฉ ์•ˆ ํ•จ):\", hq.recommend(\"Low\"))\n", "print(\"์ถ”์ฒœ ํ›„ ํ ์ˆœ์„œ ๋ณ€ํ™” ์—†์Œ:\", [m[\"id\"] for m in hq.q] == order0)\n", "\n", "# ๊ทœ์น™ ์œ„๋ฐ˜ ์กฐ์ž‘ ์ฐจ๋‹จ\n", "for args in [(\"M0105\", \"FORCE_SEND\", \"\", \"๊ทผ๊ฑฐ ์žˆ์Œ\"), (\"M0102\", \"MOVE_UP\", \"sig01\", \"์•ž์œผ๋กœ\"), (\"M0104\", \"MOVE_UP\", \"sig01\", \"FLASH ์•ž์œผ๋กœ ์ด๋™ ์š”์ฒญ\"),\n", " (\"M0103\", \"HOLD\", \"sig01\", \"๋ณด๋ฅ˜ ํ•„์š”ํ•จ\"), (\"M0101\", \"DELETE\", \"sig01\", \"์‚ญ์ œ ์š”์ฒญํ•จ\")]:\n", " try:\n", " hq.act(*args)\n", " except ControlError as e:\n", " print(f\"์ฐจ๋‹จ: {args[0]} {args[1]} -> {e}\")\n", "\n", "# ์ •์ƒ ์กฐ์ž‘\n", "clock.t += 2; print(\"M0107 ์•ž๋‹น๊น€:\", hq.act(\"M0107\", \"MOVE_UP\", \"sig01\", \"๊ธฐํ•œ ์ž„๋ฐ• IMMEDIATE ์šฐ์„  ์ฒ˜๋ฆฌ\"))\n", "clock.t += 2; print(\"M0102 ๊ฐ•์ œ ์ „์†ก:\", hq.act(\"M0102\", \"FORCE_SEND\", \"sig01\", \"์ง€ํœ˜๊ด€ ์ง€์‹œ ์ƒํ™ฉ๋ณด๊ณ  ์ฆ‰์‹œ ์ „์†ก\"))\n", "clock.t += 25; print(\"M0102 ์ƒ๊ธ‰์ž ์Šน์ธ:\", hq.act(\"M0102\", \"FORCE_SEND\", \"sig01\", \"์ง€ํœ˜๊ด€ ์ง€์‹œ ์ƒํ™ฉ๋ณด๊ณ  ์ฆ‰์‹œ ์ „์†ก\", approver=\"sup01\"))\n", "clock.t += 2; print(\"M0105 ๋ณด๋ฅ˜:\", hq.act(\"M0105\", \"HOLD\", \"sig02\", \"๋งํฌ Low, ๋Œ€์šฉ๋Ÿ‰ ํ–‰์ •์ „๋ฌธ ๋ณด๋ฅ˜\"))\n", "print(\"์กฐ์ • ํ›„ ํ ์ˆœ์„œ:\", [m[\"id\"] for m in hq.q], \"/ ๋ณด๋ฅ˜:\", list(hq.held))\n", "\n", "# ๋Œ€๊ธฐ์‹œ๊ฐ„ยท๋กœ๊ทธ\n", "waits = [{\"id\": r[\"id\"], \"์กฐ์ž‘\": r[\"action\"], \"ํ†ต์‹ ๊ด€\": r[\"operator\"], \"์Šน์ธ์ž\": r[\"approver\"] or \"-\",\n", " \"์š”์ฒญ ํ›„ ์ ์šฉ๊นŒ์ง€(s)\": round(r[\"ts\"] - next((x[\"ts\"] for x in hq.log if x[\"event\"] == \"REQUEST\" and x[\"id\"] == r[\"id\"]), r[\"ts\"]), 1)}\n", " for r in hq.log if r[\"event\"] == \"ACT\"]\n", "print(\"\\n\" + pd.DataFrame(waits).to_string(index=False))\n", "n_act = sum(r[\"event\"] in (\"ACT\", \"REQUEST\") for r in hq.log)\n", "print(f\"\\n๊ฐ์‚ฌ ๋กœ๊ทธ {len(hq.log)}๊ฑด(์กฐ์ž‘ยท์š”์ฒญ {n_act}๊ฑด ์ „๋ถ€ ๊ธฐ๋ก, ๋กœ๊น… ์„ฑ๊ณต๋ฅ  100%), ํ•ด์‹œ ์ฒด์ธ ๊ฒ€์ฆ: {hq.verify_log()}\")\n", "hq.log[9][\"operator\"] = \"sig99\"\n", "print(\"10๋ฒˆ์งธ ๊ธฐ๋ก ๋ณ€์กฐ ํ›„ ๊ฒ€์ฆ(False, ๊นจ์ง„ ์œ„์น˜):\", hq.verify_log())\n", "\n", "# ์ง€ํœ˜๊ด€ ํ”ผ๋“œ๋ฐฑ ์ž…๋ ฅ ์–‘์‹(๋ฌธ์ž์—ด๋กœ ์ถœ๋ ฅ)\n", "form = {\"form\": \"c202_commander_feedback\", \"date_time\": \"(์ž…๋ ฅ)\", \"message_id\": \"M0102\", \"delivery_ok\": \"(Y/N)\",\n", " \"delay_acceptable\": \"(Y/N)\", \"priority_change_request\": \"(๋‚ด์šฉ)\", \"comment\": \"(๋‚ด์šฉ)\"}\n", "print(\"\\n[์ง€ํœ˜๊ด€ ํ”ผ๋“œ๋ฐฑ ์ž…๋ ฅ ์–‘์‹]\\n\" + json.dumps(form, ensure_ascii=False))\n", "print(\"์‹œ์Šคํ…œ์€ ์ˆœ์„œ๋ฅผ ์ถ”์ฒœ๋งŒ ํ•˜๋ฉฐ, ๊ฐ•์ œ ์ „์†กยท๋ณด๋ฅ˜์˜ ์ฑ…์ž„์€ ์กฐ์ž‘ํ•œ ํ†ต์‹ ๊ด€๊ณผ ์Šน์ธํ•œ ์ƒ๊ธ‰์ž์—๊ฒŒ ์žˆ๋‹ค.\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 07 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "์ดˆ๊ธฐ ํ ์ˆœ์„œ: ['M0103', 'M0104', 'M0107', 'M0102', 'M0106', 'M0101', 'M0105']\n", "์‹œ์Šคํ…œ ์ถ”์ฒœ(์ž๋™ ์ ์šฉ ์•ˆ ํ•จ): ['M0101 ๋ณด๋ฅ˜ ๊ถŒ๊ณ (ROUTINE)', 'M0105 ๋ณด๋ฅ˜ ๊ถŒ๊ณ (ROUTINE)', 'M0104 ์•ž๋‹น๊ธฐ๊ธฐ ๊ถŒ๊ณ (๊ธฐํ•œ ์ž„๋ฐ•)']\n", "์ถ”์ฒœ ํ›„ ํ ์ˆœ์„œ ๋ณ€ํ™” ์—†์Œ: True\n", "์ฐจ๋‹จ: M0105 FORCE_SEND -> ํ†ต์‹ ๊ด€ ID ํ•„์š”\n", "์ฐจ๋‹จ: M0102 MOVE_UP -> ์กฐ์ž‘ ๊ทผ๊ฑฐ 5์ž ์ด์ƒ ํ•„์š”\n", "์ฐจ๋‹จ: M0104 MOVE_UP -> FLASH ์•ž์œผ๋กœ ์ด๋™์€ ์ƒ๊ธ‰์ž ์Šน์ธ ํ•„์š”\n", "์ฐจ๋‹จ: M0103 HOLD -> FLASH ๋ณด๋ฅ˜๋Š” ๊ทผ๊ฑฐ 15์ž ์ด์ƒ + ์ƒ๊ธ‰์ž ์Šน์ธ ํ•„์š”\n", "์ฐจ๋‹จ: M0101 DELETE -> ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ ์กฐ์ž‘: DELETE\n", "M0107 ์•ž๋‹น๊น€: ์ ์šฉ\n", "M0102 ๊ฐ•์ œ ์ „์†ก: ์ƒ๊ธ‰์ž ์Šน์ธ ๋Œ€๊ธฐ(๋Œ€๊ธฐ ์ค‘์ธ FLASH๋ณด๋‹ค ์•ž์„ฌ)\n", "M0102 ์ƒ๊ธ‰์ž ์Šน์ธ: ์ ์šฉ\n", "M0105 ๋ณด๋ฅ˜: ์ ์šฉ\n", "์กฐ์ • ํ›„ ํ ์ˆœ์„œ: ['M0102', 'M0103', 'M0107', 'M0104', 'M0106', 'M0101'] / ๋ณด๋ฅ˜: ['M0105']\n", "\n", " id ์กฐ์ž‘ ํ†ต์‹ ๊ด€ ์Šน์ธ์ž ์š”์ฒญ ํ›„ ์ ์šฉ๊นŒ์ง€(s)\n", "M0107 MOVE_UP sig01 - 0.0\n", "M0102 FORCE_SEND sig01 sup01 25.0\n", "M0105 HOLD sig02 - 0.0\n", "\n", "๊ฐ์‚ฌ ๋กœ๊ทธ 12๊ฑด(์กฐ์ž‘ยท์š”์ฒญ 4๊ฑด ์ „๋ถ€ ๊ธฐ๋ก, ๋กœ๊น… ์„ฑ๊ณต๋ฅ  100%), ํ•ด์‹œ ์ฒด์ธ ๊ฒ€์ฆ: (True, -1)\n", "10๋ฒˆ์งธ ๊ธฐ๋ก ๋ณ€์กฐ ํ›„ ๊ฒ€์ฆ(False, ๊นจ์ง„ ์œ„์น˜): (False, 10)\n", "\n", "[์ง€ํœ˜๊ด€ ํ”ผ๋“œ๋ฐฑ ์ž…๋ ฅ ์–‘์‹]\n", "{\"form\": \"c202_commander_feedback\", \"date_time\": \"(์ž…๋ ฅ)\", \"message_id\": \"M0102\", \"delivery_ok\": \"(Y/N)\", \"delay_acceptable\": \"(Y/N)\", \"priority_change_request\": \"(๋‚ด์šฉ)\", \"comment\": \"(๋‚ด์šฉ)\"}\n", "์‹œ์Šคํ…œ์€ ์ˆœ์„œ๋ฅผ ์ถ”์ฒœ๋งŒ ํ•˜๋ฉฐ, ๊ฐ•์ œ ์ „์†กยท๋ณด๋ฅ˜์˜ ์ฑ…์ž„์€ ์กฐ์ž‘ํ•œ ํ†ต์‹ ๊ด€๊ณผ ์Šน์ธํ•œ ์ƒ๊ธ‰์ž์—๊ฒŒ ์žˆ๋‹ค.\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 08. ๋Œ€์—ญํญ 90% ๊ธ‰๊ฐ ๋ฐ ๋Œ€๋Ÿ‰ ์ „๋ฌธ ํญ์ฃผ ์ƒํ™ฉ ๋ชจ์˜ ์ „์†ก ์ŠคํŠธ๋ ˆ์Šค ์‹œ๋ฎฌ๋ ˆ์ด์…˜\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.5์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "๋Œ€์—ญํญ 90% ๊ธ‰๊ฐ ๋ฐ ๋Œ€๋Ÿ‰ ์ „๋ฌธ ๋™์‹œ ๋ฐœ์ƒ ๊ทนํ•œ ์ŠคํŠธ๋ ˆ์Šค ์‹œ๋ฎฌ๋ ˆ์ด์…˜์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **๋Œ€์—ญํญ 90% ๊ธ‰๊ฐ๊ณผ ์ „๋ฌธ ํŠธ๋ž˜ํ”ฝ 500% ์ฆ๊ฐ€, ์žฌ๋ฐยท๋งํฌ ๋‘์ ˆ ์ƒํ™ฉ์—์„œ ํยท์žฌ์ „์†ก ์‹œ์Šคํ…œ์˜ ์•ˆ์ „ ์‹คํŒจ ๋™์ž‘์„ ๊ฒ€์ฆํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **ํ‰์‹œ, ํŠธ๋ž˜ํ”ฝ 6๋ฐฐ, ๋Œ€์—ญํญ 10%๋กœ ๊ธ‰๊ฐ, ๋‘ ์กฐ๊ฑด ๋™์‹œ, ์žฌ๋ฐ 120์ดˆ, ๋Œ€์—ญํญ 1% ๋‘์ ˆ ์ƒํ™ฉ์„ ๊ฐ€์ƒ์œผ๋กœ ์ฃผ์ž…ํ•˜๊ณ  ์ผ๋ฐ˜ ๋ชจ๋“œ์™€ ์„ฑ๋Šฅ ์ €ํ•˜ ๋ชจ๋“œ(๊ธด๊ธ‰ ์ „๋ฌธ๋งŒ ์†ก์‹ , ์ž‘์€ ์กฐ๊ฐยท์••์ถ• ํ—ค๋”, ๋‚˜๋จธ์ง€ ๋ณด๊ด€)๋ฅผ ๋น„๊ต ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ฒ€์ฆํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `tests/c202_stress_test.py ๋ฐ ์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ ๋ถ„์„ ๋ณด๊ณ ์„œ` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ๊ทน๋‹จ ์ƒํ™ฉ์—์„œ์˜ ์•ˆ์ „ ์‹คํŒจ(Fail-safe) ๋ฐ ๊ธฐ๋Šฅ ์ถ•์†Œ ์ž‘๋™ ์ ์ ˆ์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ๋™์‹œ ํŠธ๋ž˜ํ”ฝ 500% ์ฆ๊ฐ€ ์‹œ ์‹œ์Šคํ…œ ๋‹ค์šด 0๊ฑด, ๋ฐ์ดํ„ฐ ์œ ์‹ค๋ฅ  0%\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ๋Œ€๋Ÿ‰ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๋ถ€ํ•˜ ์ฃผ์ž… ํ…Œ์ŠคํŠธ ํ†ต๊ณผ ๋ฐ ๋ณ‘๋ชฉ ๊ตฌ๊ฐ„ ํ”„๋กœํŒŒ์ผ๋ง ์™„๋ฃŒ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ ๋„๊ตฌ๋ฅผ ํ†ตํ•œ ์ฒ˜๋ฆฌ๋Ÿ‰(Throughput) ๋ฐ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๊ฒ€์ฆ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"๋Œ€์—ญํญ 90% ๊ธ‰๊ฐ, ์ „๋ฌธ ํŠธ๋ž˜ํ”ฝ 500% ์ฆ๊ฐ€(ํ‰์‹œ 6๋ฐฐ), ์žฌ๋ฐยท๋งํฌ ๋‘์ ˆ์„ ๊ฐ€์ƒ์œผ๋กœ ์ฃผ์ž…ํ•ด ํยท์žฌ์ „์†ก ์‹œ์Šคํ…œ์ด ๋ฉˆ์ถ”์ง€ ์•Š๋Š”์ง€(๋‹ค์šด 0๊ฑด), ์ „๋ฌธ์ด ์‚ฌ๋ผ์ง€์ง€ ์•Š๋Š”์ง€(์œ ์‹ค 0๊ฑด: ์ „๋‹ฌ+๋ณด๊ด€=์ „์ฒด), ์„ฑ๋Šฅ ์ €ํ•˜ ๋ชจ๋“œ(๊ธด๊ธ‰ ์ „๋ฌธ๋งŒ ์†ก์‹ ยท์ž‘์€ ์กฐ๊ฐยท์••์ถ• ํ—ค๋”, ๋‚˜๋จธ์ง€ ๋ณด๊ด€)๊ฐ€ ๊ธด๊ธ‰ ์ „๋ฌธ ์ „๋‹ฌ์„ ์ง€ํ‚ค๋Š”์ง€ ๊ฒ€์ฆํ•˜๊ณ  ์ฒ˜๋ฆฌ๋Ÿ‰ยท๋ฉ”๋ชจ๋ฆฌยท๋ณ‘๋ชฉ ํ•จ์ˆ˜๋ฅผ ์ธก์ •ํ•œ๋‹ค.\"\"\"\n", "import cProfile\n", "import io\n", "import pstats\n", "import time\n", "import tracemalloc\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์ „์ˆ  ์ „๋ฌธ(๋ฉ”์‹œ์ง€) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยทํ†ต์‹ ๋งยท์ œ์› ์ •๋ณด ์—†์Œ)\n", "# ๊ธด๊ธ‰๋„ 4๋‹จ๊ณ„(FLASH/IMMEDIATE/PRIORITY/ROUTINE)์™€ ์ „๋ฌธ ์œ ํ˜•๋ณ„ ํฌ๊ธฐ๋ฅผ ๊ฐ€์ง„ ์ „๋ฌธ์„ ๋งŒ๋“ ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PREC = [\"FLASH\", \"IMMEDIATE\", \"PRIORITY\", \"ROUTINE\"]\n", "RANK = {p: i for i, p in enumerate(PREC)}\n", "DEADLINE = {\"FLASH\": 10, \"IMMEDIATE\": 30, \"PRIORITY\": 120, \"ROUTINE\": 600} # ๊ฐ€์ƒ ์ „๋‹ฌ ๋ชฉํ‘œ ์‹œ๊ฐ„(์ดˆ)\n", "MSG_TYPES = { # ์œ ํ˜•: (๊ธด๊ธ‰๋„ ํ›„๋ณด, ํ™•๋ฅ , ๋ณธ๋ฌธ ํฌ๊ธฐ ๋ฒ”์œ„ bytes, ๋ฐœ์ƒ ๋น„์œจ)\n", " \"CONTACT\": ([\"FLASH\", \"IMMEDIATE\"], [0.7, 0.3], (120, 300), 0.08),\n", " \"MEDEVAC\": ([\"FLASH\", \"IMMEDIATE\"], [0.4, 0.6], (150, 350), 0.05),\n", " \"SITREP\": ([\"IMMEDIATE\", \"PRIORITY\"], [0.2, 0.8], (600, 2500), 0.20),\n", " \"POSREP\": ([\"PRIORITY\", \"ROUTINE\"], [0.5, 0.5], (60, 120), 0.35),\n", " \"LOGREQ\": ([\"PRIORITY\", \"ROUTINE\"], [0.4, 0.6], (300, 1200), 0.14),\n", " \"ADMIN\": ([\"ROUTINE\"], [1.0], (1000, 5000), 0.18),\n", "}\n", "\n", "\n", "def make_messages(duration=600, rate=0.12, seed=7, t_start=0.0, prefix=\"M\"):\n", " \"\"\"duration์ดˆ ๋™์•ˆ ํ‰๊ท  rate๊ฑด/์ดˆ๋กœ ๋ฐœ์ƒํ•˜๋Š” ๊ฐ€์ƒ ์ „๋ฌธ ๋ชฉ๋ก.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " n = int(rng.poisson(rate * duration))\n", " times = np.sort(rng.uniform(t_start, t_start + duration, n))\n", " types = list(MSG_TYPES)\n", " msgs = []\n", " for i, t0 in enumerate(times):\n", " ty = types[rng.choice(len(types), p=[MSG_TYPES[k][3] for k in types])]\n", " cands, p, (lo, hi), _ = MSG_TYPES[ty]\n", " msgs.append({\"id\": f\"{prefix}{i:04d}\", \"t0\": float(t0), \"type\": ty, \"prec\": str(rng.choice(cands, p=p)),\n", " \"size\": int(rng.integers(lo, hi + 1)), \"src\": f\"U{int(rng.integers(1, 9)):02d}\"})\n", " return msgs\n", "\n", "\n", "class Link:\n", " \"\"\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ํ˜‘๋Œ€์—ญ ๋ฌด์„  ๋งํฌ(๊ธฐ๋ณธ 2.4kbps). ์ดˆ ๋‹จ์œ„ ์ƒํƒœ(good/bad/jam)์— ๋”ฐ๋ผ\n", " ๋น„ํŠธ์˜ค๋ฅ˜์œจ์ด ๋ฐ”๋€Œ๊ณ (Gilbert-Elliott ๋ชจ๋ธ), ๊ตฌ๊ฐ„๋ณ„ ๋Œ€์—ญํญ ๊ฐ์†Œ๋„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.\"\"\"\n", " BER = {\"good\": 1e-5, \"bad\": 2e-4, \"jam\": 1.2e-3}\n", "\n", " def __init__(self, horizon=4000, bw_bps=2400, seed=1, p_gb=0.02, p_bg=0.15, jam=(), bw_drop=(), rtt=(1.2, 0.4)):\n", " rng = np.random.default_rng(seed)\n", " st, s = [], \"good\"\n", " for _ in range(int(horizon) + 1):\n", " s = (\"bad\" if rng.random() < p_gb else \"good\") if s == \"good\" else (\"good\" if rng.random() < p_bg else \"bad\")\n", " st.append(s)\n", " for a, b in jam:\n", " for k in range(int(a), min(int(b), len(st))):\n", " st[k] = \"jam\"\n", " self.states, self.bw_bps, self.bw_drop, self.rtt_par = st, bw_bps, list(bw_drop), rtt\n", "\n", " def state(self, t):\n", " return self.states[min(int(t), len(self.states) - 1)]\n", "\n", " def bw(self, t):\n", " f = 1.0\n", " for a, b, fac in self.bw_drop:\n", " if a <= t < b:\n", " f = fac\n", " return self.bw_bps * f\n", "\n", " def p_loss(self, nbytes, t):\n", " return 1 - (1 - self.BER[self.state(t)]) ** (8 * nbytes)\n", "\n", " def rtt(self, rng):\n", " return self.rtt_par[0] + rng.exponential(self.rtt_par[1])\n", "# ---------------------------------------------------------------------------\n", "\n", "# ---- ์šฐ์„ ์ˆœ์œ„ ํยท์žฌ์ „์†ก ์ด์‚ฐ ์‚ฌ๊ฑด ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ ๊ณตํ†ต ํ•จ์ˆ˜ ----\n", "HDR = 32 # ์ „๋ฌธ ์กฐ๊ฐ(ํŒจํ‚ท) ํ—ค๋” bytes (๋ฏธ์…˜ 2์˜ ํ—ค๋” ์••์ถ• ์ ์šฉ ์‹œ 12 bytes)\n", "\n", "\n", "def policy_key(m, t, policy, aging):\n", " if policy == \"fifo\": # ๋„์ฐฉ ์ˆœ์„œ\n", " return (m[\"t0\"],)\n", " if policy == \"strict\": # ๊ธด๊ธ‰๋„ ์šฐ์„ , ๊ฐ™์œผ๋ฉด ๋„์ฐฉ ์ˆœ์„œ\n", " return (RANK[m[\"prec\"]], m[\"t0\"])\n", " # aging: ๊ธด๊ธ‰๋„ + (๋Œ€๊ธฐ์‹œ๊ฐ„ / ์ „๋‹ฌ ๋ชฉํ‘œ์‹œ๊ฐ„) ๊ฐ€์ค‘. ์˜ค๋ž˜ ๊ธฐ๋‹ค๋ฆฐ ์ „๋ฌธ๊ณผ ๊ธฐํ•œ์ด ์ž„๋ฐ•ํ•œ ์ „๋ฌธ์ด ์•ž์œผ๋กœ ์˜จ๋‹ค.\n", " return (RANK[m[\"prec\"]] - aging * (t - m[\"t0\"]) / DEADLINE[m[\"prec\"]], m[\"t0\"])\n", "\n", "\n", "def simulate(msgs, link, policy=\"strict\", mtu=256, preempt=True, rto_mode=\"fixed\", rto_s=4.0, aging=0.0,\n", " redundancy=False, adapt_mtu=False, hdr=HDR, seed=0, until=None, admit=None):\n", " \"\"\"์ „๋ฌธ ๋ชฉ๋ก์„ ๋งํฌ๋กœ ๋ณด๋‚ด๋Š” ์ด์‚ฐ ์‚ฌ๊ฑด ์‹œ๋ฎฌ๋ ˆ์ด์…˜.\n", " - ์ „๋ฌธ์€ mtu ํฌ๊ธฐ ์กฐ๊ฐ์œผ๋กœ ๋‚˜๋ˆ  ๋ณด๋‚ธ๋‹ค. ์กฐ๊ฐ์ด ์†์‹ค๋˜๋ฉด RTO ํ›„ ์žฌ์ „์†กํ•œ๋‹ค(์ž๋ฃŒ๋Š” ๋ฒ„๋ฆฌ์ง€ ์•Š์Œ).\n", " - preempt=False๋ฉด ํ•œ ์ „๋ฌธ์˜ ์กฐ๊ฐ์„ ๋๊นŒ์ง€ ์—ฐ์† ์ „์†กํ•œ๋‹ค(ํฐ ์ „๋ฌธ์ด ๊ธด๊ธ‰ ์ „๋ฌธ์„ ๋ง‰๋Š” ํ˜„์ƒ ์žฌํ˜„).\n", " - rto_mode='adaptive'๋ฉด ์™•๋ณต์‹œ๊ฐ„(RTT) ์ถ”์ •๊ฐ’์œผ๋กœ RTO๋ฅผ ๊ณ„์‚ฐํ•œ๋‹ค(Jacobson ๋ฐฉ์‹).\n", " - redundancy=True๋ฉด ๋งํฌ ์—ดํ™” ์ถ”์ • ์‹œ FLASHยทIMMEDIATE ์กฐ๊ฐ์„ 2ํšŒ ์—ฐ์† ์ „์†กํ•œ๋‹ค.\n", " - adapt_mtu=True๋ฉด ๋งํฌ ์—ดํ™” ์ถ”์ • ์‹œ ์กฐ๊ฐ ํฌ๊ธฐ๋ฅผ ์ค„์ธ๋‹ค.\n", " - admit(m, t)์ด False์ธ ์ „๋ฌธ์€ ์ƒˆ ์กฐ๊ฐ์„ ๋ณด๋‚ด์ง€ ์•Š๊ณ  ์ €์žฅํ•ด ๋‘”๋‹ค(์„ฑ๋Šฅ ์ €ํ•˜ ๋ชจ๋“œ).\"\"\"\n", " rng = np.random.default_rng(seed)\n", " pend = sorted(({**m, \"unsent\": m[\"size\"], \"ready\": [], \"frags\": [], \"done\": None, \"first\": None} for m in msgs), key=lambda m: m[\"t0\"])\n", " t, i, active, cur = 0.0, 0, [], None\n", " srtt, rttvar, loss = None, None, 0.0\n", " st = {\"tx\": 0, \"retx\": 0, \"spurious\": 0, \"bytes\": 0, \"decide_ms\": [], \"flag\": {}, \"log\": []}\n", " until = until if until is not None else (pend[-1][\"t0\"] if pend else 0) + 6000\n", " while (i < len(pend) or active) and t < until:\n", " while i < len(pend) and pend[i][\"t0\"] <= t:\n", " active.append(pend[i]); i += 1\n", " td = time.perf_counter()\n", " for m in active: # ACK๊ฐ€ ์ด๋ฏธ ๋„์ฐฉํ•œ ์žฌ์ „์†ก ์˜ˆ์•ฝ์€ ์ทจ์†Œ\n", " m[\"ready\"] = [e for e in m[\"ready\"] if not (e[1][\"ack\"] is not None and e[1][\"ack\"] <= t)]\n", " ready = [m for m in active if any(r <= t for r, _ in m[\"ready\"]) or (m[\"unsent\"] > 0 and (admit is None or admit(m, t)))]\n", " if not ready:\n", " nxt = ([pend[i][\"t0\"]] if i < len(pend) else []) + [r for m in active for r, _ in m[\"ready\"] if r > t]\n", " t = min(nxt) if nxt else t + 1.0\n", " continue\n", " if not preempt and cur in ready and cur[\"unsent\"] > 0:\n", " m = cur\n", " else:\n", " m = min(ready, key=lambda x: policy_key(x, t, policy, aging))\n", " st[\"decide_ms\"].append((time.perf_counter() - td) * 1000)\n", " due = [e for e in m[\"ready\"] if e[0] <= t]\n", " if due:\n", " e = min(due, key=lambda x: x[0]); m[\"ready\"].remove(e); frag = e[1]\n", " st[\"retx\"] += 1; st[\"spurious\"] += frag[\"recv\"]\n", " else:\n", " size = mtu if not adapt_mtu else (mtu if loss < 0.15 else (mtu // 2 if loss < 0.4 else mtu // 4))\n", " size = min(m[\"unsent\"], size); m[\"unsent\"] -= size\n", " frag = {\"b\": size, \"recv\": False, \"ack\": None, \"tries\": 0}\n", " m[\"frags\"].append(frag)\n", " cur = m\n", " m[\"first\"] = t if m[\"first\"] is None else m[\"first\"]\n", " nb = frag[\"b\"] + hdr\n", " copies = 2 if (redundancy and RANK[m[\"prec\"]] <= 1 and loss > 0.15) else 1\n", " for _ in range(copies):\n", " t += nb * 8 / link.bw(t)\n", " st[\"tx\"] += 1; st[\"bytes\"] += nb\n", " ok = rng.random() >= link.p_loss(nb, t)\n", " st[\"log\"].append((t, nb, ok, m[\"prec\"], m[\"id\"])) # ์ „์†ก ๊ธฐ๋ก(์‹œ๊ฐ, bytes, ์„ฑ๊ณต, ๊ธด๊ธ‰๋„, ์ „๋ฌธ)\n", " loss = 0.85 * loss + 0.15 * (0.0 if ok else 1.0) # ์ตœ๊ทผ ์†์‹ค๋ฅ  ์ถ”์ •(EWMA)\n", " st[\"flag\"][int(t)] = st[\"flag\"].get(int(t), False) or loss > 0.12 # ๋งํฌ ์—ดํ™” ํŒ์ •(์ดˆ ๋‹จ์œ„)\n", " if ok and not frag[\"recv\"]:\n", " frag[\"recv\"], frag[\"rt\"] = True, t\n", " r = link.rtt(rng); frag[\"ack\"] = t + r\n", " if srtt is None:\n", " srtt, rttvar = r, r / 2\n", " else:\n", " rttvar = 0.75 * rttvar + 0.25 * abs(srtt - r); srtt = 0.875 * srtt + 0.125 * r\n", " frag[\"tries\"] += 1\n", " rto = rto_s if (rto_mode == \"fixed\" or srtt is None) else float(np.clip(srtt + 4 * rttvar, 1.0, 30.0))\n", " if not frag[\"recv\"] or frag[\"ack\"] > t + rto: # ์†์‹ค ๋˜๋Š” ACK ์ง€์—ฐ -> ์žฌ์ „์†ก ์˜ˆ์•ฝ\n", " m[\"ready\"].append((t + rto, frag))\n", " if m[\"done\"] is None and m[\"unsent\"] == 0 and all(f[\"recv\"] for f in m[\"frags\"]):\n", " m[\"done\"] = max(f[\"rt\"] for f in m[\"frags\"])\n", " active = [x for x in active if not (x[\"unsent\"] == 0 and not x[\"ready\"] and all(f[\"recv\"] for f in x[\"frags\"]))]\n", " res = [{\"id\": m[\"id\"], \"type\": m[\"type\"], \"prec\": m[\"prec\"], \"size\": m[\"size\"], \"t0\": m[\"t0\"], \"done\": m[\"done\"], \"wait\": None if m[\"first\"] is None else m[\"first\"] - m[\"t0\"],\n", " \"retx\": sum(f[\"tries\"] for f in m[\"frags\"]) - len(m[\"frags\"]),\n", " \"latency\": None if m[\"done\"] is None else m[\"done\"] - m[\"t0\"]} for m in pend]\n", " for r in res:\n", " r[\"on_time\"] = r[\"latency\"] is not None and r[\"latency\"] <= DEADLINE[r[\"prec\"]]\n", " return res, st\n", "\n", "\n", "def summarize(res):\n", " \"\"\"๊ธด๊ธ‰๋„๋ณ„ ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ ยท์ง€์—ฐ. ์ข…ํ•ฉ ์ •ํ™•๋„ = ๊ธด๊ธ‰๋„๋ณ„ ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ ์˜ ํ‰๊ท .\"\"\"\n", " df = pd.DataFrame(res)\n", " rows = []\n", " for p in PREC:\n", " d = df[df[\"prec\"] == p]\n", " lat = d[\"latency\"].dropna()\n", " rows.append({\"๊ธด๊ธ‰๋„\": p, \"๊ฑด์ˆ˜\": len(d), \"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \": round(d[\"on_time\"].mean(), 3) if len(d) else np.nan,\n", " \"ํ‰๊ท  ์ง€์—ฐ(s)\": round(lat.mean(), 1) if len(lat) else np.nan,\n", " \"95% ์ง€์—ฐ(s)\": round(lat.quantile(0.95), 1) if len(lat) else np.nan, \"๋ฏธ์ „๋‹ฌ\": int(d[\"done\"].isna().sum())})\n", " tab = pd.DataFrame(rows)\n", " return tab, round(float(tab[\"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \"].mean()), 3)\n", "\n", "ADV = dict(policy=\"aging\", aging=0.5, rto_mode=\"adaptive\", redundancy=True, adapt_mtu=True)\n", "NOMINAL_BPS = 2400\n", "\n", "\n", "def degraded_admit(link):\n", " \"\"\"์„ฑ๋Šฅ ์ €ํ•˜ ๋ชจ๋“œ: ๋Œ€์—ญํญ์ด ํ‰์‹œ์˜ 50% ๋ฏธ๋งŒ์ด๊ฑฐ๋‚˜ ๋งํฌ๊ฐ€ ์žฌ๋ฐ ์ƒํƒœ๋ฉด FLASHยทIMMEDIATE๋งŒ ์ƒˆ๋กœ ์†ก์‹ ํ•˜๊ณ  ๋‚˜๋จธ์ง€๋Š” ๋ณด๊ด€(์‚ญ์ œ ์—†์Œ).\"\"\"\n", " return lambda m, t: RANK[m[\"prec\"]] <= 1 or (link.bw(t) >= 0.5 * NOMINAL_BPS and link.state(t) != \"jam\")\n", "\n", "\n", "def run_case(msgs, link, fail_safe, seed=0):\n", " t0 = time.perf_counter()\n", " try:\n", " kw = dict(mtu=96, hdr=14, admit=degraded_admit(link)) if fail_safe else {} # ์ €ํ•˜๋ชจ๋“œ: ์ž‘์€ ์กฐ๊ฐ + ์••์ถ• ํ—ค๋”(๋ฏธ์…˜ 2) + ๊ธด๊ธ‰ ์ „๋ฌธ๋งŒ ์†ก์‹ \n", " res, st = simulate(msgs, link, **ADV, seed=seed, until=2000, **kw)\n", " down = 0\n", " except Exception as e: # ๋‹ค์šด ์ง‘๊ณ„\n", " print(\"๋‹ค์šด:\", e); res, st, down = [], {\"tx\": 0}, 1\n", " dt = time.perf_counter() - t0\n", " delivered = sum(r[\"done\"] is not None for r in res)\n", " kept = len(res) - delivered # 2,000์ดˆ ์‹œ์ ๊นŒ์ง€ ๋ฏธ์ „๋‹ฌ = ์ €์žฅยท๋ณด๊ด€ ์ค‘(์‚ญ์ œ ์—†์Œ)\n", " urg = [r[\"on_time\"] for r in res if RANK[r[\"prec\"]] <= 1]\n", " return {\"์ „๋ฌธ\": len(msgs), \"์ „๋‹ฌ\": delivered, \"๋ณด๊ด€\": kept, \"์œ ์‹ค\": len(msgs) - delivered - kept, \"๋‹ค์šด\": down,\n", " \"๊ธด๊ธ‰ ๊ธฐํ•œ๋‚ด\": round(float(np.mean(urg)), 3) if urg else np.nan,\n", " \"FLASH 95%์ง€์—ฐ(s)\": round(float(np.quantile([r[\"latency\"] for r in res if r[\"prec\"] == \"FLASH\" and r[\"latency\"] is not None], 0.95)), 1),\n", " \"์ฒ˜๋ฆฌ๋Ÿ‰(๊ฑด/s)\": round(len(msgs) / dt), \"๋ชจ๋“œ\": \"์ €ํ•˜๋ชจ๋“œ\" if fail_safe else \"์ผ๋ฐ˜\"}\n", "\n", "\n", "base = make_messages(600, 0.21, seed=7)\n", "surge = base + [m for k in range(5) for m in make_messages(600, 0.21, seed=20 + k, prefix=f\"S{k}\")] # ํ‰์‹œ + 500%\n", "CASES = [(\"ํ‰์‹œ\", base, {}), (\"ํŠธ๋ž˜ํ”ฝ 500% ์ฆ๊ฐ€\", surge, {}), (\"๋Œ€์—ญํญ 90% ๊ธ‰๊ฐ(200~500s)\", base, dict(bw_drop=[(200, 500, 0.1)])),\n", " (\"ํŠธ๋ž˜ํ”ฝ 500% + ๋Œ€์—ญํญ 90% ๊ธ‰๊ฐ\", surge, dict(bw_drop=[(200, 500, 0.1)])),\n", " (\"ํŠธ๋ž˜ํ”ฝ 500% + ์žฌ๋ฐ 120์ดˆ\", surge, dict(jam=[(200, 320)])), (\"๋งํฌ ๋‘์ ˆ 300์ดˆ(๋Œ€์—ญํญ 1%)\", base, dict(bw_drop=[(200, 500, 0.01)]))]\n", "\n", "rows = []\n", "for i, (name, msgs, lk) in enumerate(CASES):\n", " link = Link(4000, seed=1, **lk)\n", " for fs in (False, True):\n", " rows.append({\"์ƒํ™ฉ\": name, **run_case(msgs, link, fs, seed=i)})\n", "df = pd.DataFrame(rows)\n", "print(df.drop(columns=[\"์ฒ˜๋ฆฌ๋Ÿ‰(๊ฑด/s)\"]).to_string(index=False))\n", "print(f\"\\n์‹œ์Šคํ…œ ๋‹ค์šด {df['๋‹ค์šด'].sum()}๊ฑด, ์œ ์‹ค {df['์œ ์‹ค'].sum()}๊ฑด(์ „๋‹ฌ+๋ณด๊ด€=์ „์ฒด), ์ตœ์†Œ ์ฒ˜๋ฆฌ๋Ÿ‰ {df['์ฒ˜๋ฆฌ๋Ÿ‰(๊ฑด/s)'].min():,}๊ฑด/s\")\n", "hard = df[df[\"์ƒํ™ฉ\"] == \"ํŠธ๋ž˜ํ”ฝ 500% + ๋Œ€์—ญํญ 90% ๊ธ‰๊ฐ\"]\n", "print(\"๋งํฌ ๋‘์ ˆ(๋Œ€์—ญํญ 1%) ๊ตฌ๊ฐ„์—๋Š” ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ๋„ ๊ธฐํ•œ์„ ์ง€ํ‚ฌ ์ˆ˜ ์—†๋‹ค -> ํ†ต์‹ ๊ด€์—๊ฒŒ ๋Œ€์ฒด ์ˆ˜๋‹จ(์ค‘๊ณ„ยท์ „๋ น ๋“ฑ) ๊ฒ€ํ† ๋ฅผ ์š”์ฒญํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ ์‹คํŒจ ๋™์ž‘์ด๋‹ค.\")\n", "print(f\"์ตœ์•… ์ƒํ™ฉ ๊ธด๊ธ‰ ์ „๋ฌธ ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ : ์ผ๋ฐ˜ {hard.iloc[0]['๊ธด๊ธ‰ ๊ธฐํ•œ๋‚ด']:.1%} -> ์„ฑ๋Šฅ ์ €ํ•˜ ๋ชจ๋“œ {hard.iloc[1]['๊ธด๊ธ‰ ๊ธฐํ•œ๋‚ด']:.1%}\")\n", "need = sum(m[\"size\"] + 14 for m in surge if RANK[m[\"prec\"]] <= 1 and 200 <= m[\"t0\"] < 500) / 300\n", "print(f\"์›์ธ: ๊ธ‰๊ฐ ๊ตฌ๊ฐ„ ๊ธด๊ธ‰ ์ „๋ฌธ๋งŒ ์ดˆ๋‹น {need:.0f}B๊ฐ€ ํ•„์š”ํ•œ๋ฐ ๋งํฌ๋Š” ์ดˆ๋‹น {NOMINAL_BPS * 0.1 / 8:.0f}B -> ์Šค์ผ€์ค„๋ง์œผ๋กœ ํ•ด๊ฒฐ ๋ถˆ๊ฐ€, ์ „๋ฌธ ๊ฐ„์†Œํ™”ยท๋ฐœ์†ก ํ†ต์ œ๋ฅผ ์ง€ํœ˜๊ณ„ํ†ต์— ๊ฑด์˜ํ•ด์•ผ ํ•œ๋‹ค.\")\n", "\n", "# ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์ ๊ฒ€: ๊ฐ™์€ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ 3ํšŒ ๋ฐ˜๋ณตํ•ด๋„ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๊ณ„์† ๋Š˜์ง€ ์•Š๋Š”๊ฐ€\n", "tracemalloc.start()\n", "mem = []\n", "peak = 0\n", "for k in range(3):\n", " simulate(base, Link(4000, seed=1), **ADV, until=2000)\n", " mem.append(tracemalloc.get_traced_memory()[0] / 1e6); peak = max(peak, tracemalloc.get_traced_memory()[1] / 1e6)\n", "tracemalloc.stop()\n", "print(f\"ํ‰์‹œ ์‹œ๋‚˜๋ฆฌ์˜ค ์‹คํ–‰ ์ค‘ ์ตœ๋Œ€ ๋ฉ”๋ชจ๋ฆฌ {peak:.1f} MB\")\n", "print(\"๋ฐ˜๋ณต ์‹คํ–‰ ํ›„ ์ž”๋ฅ˜ ๋ฉ”๋ชจ๋ฆฌ(MB):\", [round(m, 2) for m in mem], \"-> ๋ˆ„์ˆ˜ ์˜์‹ฌ\" if mem[-1] > mem[0] * 1.5 + 1 else \"-> ๋ˆ„์ˆ˜ ์ง•ํ›„ ์—†์Œ\")\n", "\n", "# ๋ณ‘๋ชฉ ํ•จ์ˆ˜ ํ”„๋กœํŒŒ์ผ๋ง\n", "pr = cProfile.Profile(); pr.enable()\n", "simulate(surge, Link(4000, seed=1, bw_drop=[(200, 500, 0.1)]), **ADV, until=2000)\n", "pr.disable()\n", "s = io.StringIO(); pstats.Stats(pr, stream=s).sort_stats(\"tottime\").print_stats(4)\n", "top = [ln.split() for ln in s.getvalue().splitlines() if ln.strip()[:1].isdigit() and \"(\" in ln and \"function calls\" not in ln][:3]\n", "print(\"[๋ณ‘๋ชฉ ์ƒ์œ„ 3 ํ•จ์ˆ˜: ํ˜ธ์ถœ ์ˆ˜ / ์ž์ฒด ์†Œ์š”(s) / ํ•จ์ˆ˜]\")\n", "for f in top:\n", " print(f\" {f[0]:>9} / {f[1]} / {' '.join(f[5:]).split('/')[-1][-45:]}\")\n", "print(\"๊ฐœ์„  ๋ฐฉํ–ฅ: ๋งค ๊ฒฐ์ •๋งˆ๋‹ค ์ „์ฒด ๋Œ€๊ธฐ ์ „๋ฌธ์„ ํ›‘๋Š” ๋ถ€๋ถ„์„ ๊ธด๊ธ‰๋„๋ณ„ ํž™(heapq)๊ณผ ์žฌ์ „์†ก ํƒ€์ด๋จธ ํž™์œผ๋กœ ๋ฐ”๊พธ๋ฉด ๋Œ€๊ธฐ ์ „๋ฌธ ์ˆ˜์— ๋œ ๋ฏผ๊ฐํ•ด์ง„๋‹ค.\")\n", "\n", "plt.figure(figsize=(9, 4))\n", "x = np.arange(len(CASES))\n", "plt.bar(x - 0.2, df[df[\"๋ชจ๋“œ\"] == \"์ผ๋ฐ˜\"][\"๊ธด๊ธ‰ ๊ธฐํ•œ๋‚ด\"], 0.4, label=\"normal\")\n", "plt.bar(x + 0.2, df[df[\"๋ชจ๋“œ\"] == \"์ €ํ•˜๋ชจ๋“œ\"][\"๊ธด๊ธ‰ ๊ธฐํ•œ๋‚ด\"], 0.4, label=\"degraded mode (urgent only)\")\n", "plt.xticks(x, [f\"case{i + 1}\" for i in x]); plt.ylabel(\"urgent on-time ratio\"); plt.ylim(0, 1.05); plt.legend()\n", "plt.title(\"Stress test: urgent message delivery (synthetic)\"); plt.tight_layout(); plt.show()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 08 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", " ์ƒํ™ฉ ์ „๋ฌธ ์ „๋‹ฌ ๋ณด๊ด€ ์œ ์‹ค ๋‹ค์šด ๊ธด๊ธ‰ ๊ธฐํ•œ๋‚ด FLASH 95%์ง€์—ฐ(s) ๋ชจ๋“œ\n", " ํ‰์‹œ 130 130 0 0 0 1.000 2.0 ์ผ๋ฐ˜\n", " ํ‰์‹œ 130 130 0 0 0 1.000 1.7 ์ €ํ•˜๋ชจ๋“œ\n", " ํŠธ๋ž˜ํ”ฝ 500% ์ฆ๊ฐ€ 758 567 191 0 0 0.991 2.0 ์ผ๋ฐ˜\n", " ํŠธ๋ž˜ํ”ฝ 500% ์ฆ๊ฐ€ 758 579 179 0 0 0.991 2.2 ์ €ํ•˜๋ชจ๋“œ\n", " ๋Œ€์—ญํญ 90% ๊ธ‰๊ฐ(200~500s) 130 130 0 0 0 0.500 23.9 ์ผ๋ฐ˜\n", " ๋Œ€์—ญํญ 90% ๊ธ‰๊ฐ(200~500s) 130 130 0 0 0 0.667 18.7 ์ €ํ•˜๋ชจ๋“œ\n", "ํŠธ๋ž˜ํ”ฝ 500% + ๋Œ€์—ญํญ 90% ๊ธ‰๊ฐ 758 519 239 0 0 0.389 93.4 ์ผ๋ฐ˜\n", "ํŠธ๋ž˜ํ”ฝ 500% + ๋Œ€์—ญํญ 90% ๊ธ‰๊ฐ 758 528 230 0 0 0.398 97.8 ์ €ํ•˜๋ชจ๋“œ\n", " ํŠธ๋ž˜ํ”ฝ 500% + ์žฌ๋ฐ 120์ดˆ 758 547 211 0 0 0.770 14.8 ์ผ๋ฐ˜\n", " ํŠธ๋ž˜ํ”ฝ 500% + ์žฌ๋ฐ 120์ดˆ 758 563 195 0 0 0.858 6.7 ์ €ํ•˜๋ชจ๋“œ\n", " ๋งํฌ ๋‘์ ˆ 300์ดˆ(๋Œ€์—ญํญ 1%) 130 130 0 0 0 0.389 259.8 ์ผ๋ฐ˜\n", " ๋งํฌ ๋‘์ ˆ 300์ดˆ(๋Œ€์—ญํญ 1%) 130 130 0 0 0 0.500 175.0 ์ €ํ•˜๋ชจ๋“œ\n", "\n", "์‹œ์Šคํ…œ ๋‹ค์šด 0๊ฑด, ์œ ์‹ค 0๊ฑด(์ „๋‹ฌ+๋ณด๊ด€=์ „์ฒด), ์ตœ์†Œ ์ฒ˜๋ฆฌ๋Ÿ‰ 207๊ฑด/s\n", "๋งํฌ ๋‘์ ˆ(๋Œ€์—ญํญ 1%) ๊ตฌ๊ฐ„์—๋Š” ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ๋„ ๊ธฐํ•œ์„ ์ง€ํ‚ฌ ์ˆ˜ ์—†๋‹ค -> ํ†ต์‹ ๊ด€์—๊ฒŒ ๋Œ€์ฒด ์ˆ˜๋‹จ(์ค‘๊ณ„ยท์ „๋ น ๋“ฑ) ๊ฒ€ํ† ๋ฅผ ์š”์ฒญํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ ์‹คํŒจ ๋™์ž‘์ด๋‹ค.\n", "์ตœ์•… ์ƒํ™ฉ ๊ธด๊ธ‰ ์ „๋ฌธ ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ : ์ผ๋ฐ˜ 38.9% -> ์„ฑ๋Šฅ ์ €ํ•˜ ๋ชจ๋“œ 39.8%\n", "์›์ธ: ๊ธ‰๊ฐ ๊ตฌ๊ฐ„ ๊ธด๊ธ‰ ์ „๋ฌธ๋งŒ ์ดˆ๋‹น 91B๊ฐ€ ํ•„์š”ํ•œ๋ฐ ๋งํฌ๋Š” ์ดˆ๋‹น 30B -> ์Šค์ผ€์ค„๋ง์œผ๋กœ ํ•ด๊ฒฐ ๋ถˆ๊ฐ€, ์ „๋ฌธ ๊ฐ„์†Œํ™”ยท๋ฐœ์†ก ํ†ต์ œ๋ฅผ ์ง€ํœ˜๊ณ„ํ†ต์— ๊ฑด์˜ํ•ด์•ผ ํ•œ๋‹ค.\n", "ํ‰์‹œ ์‹œ๋‚˜๋ฆฌ์˜ค ์‹คํ–‰ ์ค‘ ์ตœ๋Œ€ ๋ฉ”๋ชจ๋ฆฌ 0.4 MB\n", "๋ฐ˜๋ณต ์‹คํ–‰ ํ›„ ์ž”๋ฅ˜ ๋ฉ”๋ชจ๋ฆฌ(MB): [0.01, 0.01, 0.01] -> ๋ˆ„์ˆ˜ ์ง•ํ›„ ์—†์Œ\n", "[๋ณ‘๋ชฉ ์ƒ์œ„ 3 ํ•จ์ˆ˜: ํ˜ธ์ถœ ์ˆ˜ / ์ž์ฒด ์†Œ์š”(s) / ํ•จ์ˆ˜]\n", " 807785 / 0.476 / m08.py:80(policy_key)\n", " 1 / 0.430 / m08.py:89(simulate)\n", " 2132 / 0.408 / m08.py:110()\n", "๊ฐœ์„  ๋ฐฉํ–ฅ: ๋งค ๊ฒฐ์ •๋งˆ๋‹ค ์ „์ฒด ๋Œ€๊ธฐ ์ „๋ฌธ์„ ํ›‘๋Š” ๋ถ€๋ถ„์„ ๊ธด๊ธ‰๋„๋ณ„ ํž™(heapq)๊ณผ ์žฌ์ „์†ก ํƒ€์ด๋จธ ํž™์œผ๋กœ ๋ฐ”๊พธ๋ฉด ๋Œ€๊ธฐ ์ „๋ฌธ ์ˆ˜์— ๋œ ๋ฏผ๊ฐํ•ด์ง„๋‹ค.\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 09. AAR ๊ธฐ๋ฐ˜ ์ค‘์š” ์ „๋ฌธ ์ „์†ก ์ง€์—ฐ ์‚ฌ๋ก€ ๋ถ„์„ ๋ฐ ํ ์šฐ์„ ์ˆœ์œ„ ๊ฐ€์ค‘์น˜ ์žฌ์กฐ์ •\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.0์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "AAR์„ ํ†ตํ•ด ์ค‘์š” ์ „๋ฌธ ์ „์†ก ์ง€์—ฐ ์‚ฌ๋ก€๋ฅผ ๋ถ„์„ํ•˜๊ณ  ํ ์Šค์ผ€์ค„๋ง ๊ฐ€์ค‘์น˜๋ฅผ ์žฌ์กฐ์ •ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **AAR๋กœ ์ค‘์š” ์ „๋ฌธ์˜ ์ „์†ก ์ง€์—ฐ ์‚ฌ๋ก€๋ฅผ ์›์ธ๋ณ„๋กœ ๋ถ„์„ํ•˜๊ณ  ํ ์Šค์ผ€์ค„๋ง ๊ฐ€์ค‘์น˜์™€ ์žฌ์ „์†ก ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์žฌ์กฐ์ •ํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **์ดˆ๊ธฐ ์šด์šฉ ์„ค์ •(์ „๋ฌธ ๋‹จ์œ„ ์ „์†ก, ๊ณ ์ • ์žฌ์ „์†ก ํƒ€์ด๋จธ 6์ดˆ, ์กฐ๊ฐ 512bytes)์œผ๋กœ ์žฌ๋ฐยท๋Œ€์—ญํญ ๊ฐ์†Œ๊ฐ€ ์žˆ๋Š” ํ›ˆ๋ จ์„ ๋ชจ์˜ํ•˜๊ณ  ์ง€์—ฐ๋œ ์ค‘์š” ์ „๋ฌธ์„ ๋Œ€๊ธฐ์—ด ์ ์ฒดยท์žฌ์ „์†ก ๋Œ€๊ธฐยท๋งํฌ ์—ดํ™”๋กœ ๋ถ„๋ฅ˜ํ•œ AAR ์ผ์ง€ ์ž‘์„ฑํ•˜์—ฌ ์›์ธ์„ ๋ถ„์„ํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `docs/c202_aar_report.md ๋ฐ configs/c202_tuned_params.yaml` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ์˜ค๋ฅ˜ ์›์ธ ๊ทœ๋ช…์˜ ๊ฐ๊ด€์„ฑ ๋ฐ ๊ฐœ์„  ์กฐ์น˜์˜ ๋…ผ๋ฆฌ์  ํƒ€๋‹น์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: AAR ํ”ผ๋“œ๋ฐฑ ์ ์šฉ ํ›„ ์ข…ํ•ฉ ์ •ํ™•๋„ 10%p ์ด์ƒ ํ–ฅ์ƒ, ์žฌ๋ฐœ ์˜ค๋ฅ˜ 0๊ฑด\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: AAR ๋ณด๊ณ ์„œ ์ž‘์„ฑ ๋ฐ ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ์ „ํ›„ ํšŒ๊ท€ ํ…Œ์ŠคํŠธ ํ†ต๊ณผ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ํšŒ๊ท€ ํ…Œ์ŠคํŠธ(Regression Test) ์Šค์œ„ํŠธ ์ „์ฒด ์ž๋™ ์‹คํ–‰ ๋ฐ ๋น„๊ต\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"AAR(์‚ฌํ›„๊ฒ€ํ† ) ์ผ์ง€ ํ˜•์‹์œผ๋กœ ์ค‘์š” ์ „๋ฌธ(FLASHยทIMMEDIATEยทPRIORITY)์˜ ์ „์†ก ์ง€์—ฐ ์‚ฌ๋ก€๋ฅผ ์›์ธ๋ณ„๋กœ ๋ถ„๋ฅ˜ํ•˜๊ณ , ํ ์Šค์ผ€์ค„๋ง ๊ฐ€์ค‘์น˜(์„ ์ ยทaging)ยท์กฐ๊ฐ ํฌ๊ธฐยท์žฌ์ „์†ก ํƒ€์ด๋จธ๋ฅผ ๊ฒฉ์ž ํƒ์ƒ‰์œผ๋กœ ์žฌ์กฐ์ •ํ•œ ๋’ค ๋‹ค๋ฅธ ์šด์šฉ ์กฐ๊ฑด์—์„œ ํšŒ๊ท€ ํ…Œ์ŠคํŠธ๋กœ ๊ฐœ์„  ์ „ํ›„๋ฅผ ๋น„๊ตํ•œ๋‹ค.\"\"\"\n", "import time\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์ „์ˆ  ์ „๋ฌธ(๋ฉ”์‹œ์ง€) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยทํ†ต์‹ ๋งยท์ œ์› ์ •๋ณด ์—†์Œ)\n", "# ๊ธด๊ธ‰๋„ 4๋‹จ๊ณ„(FLASH/IMMEDIATE/PRIORITY/ROUTINE)์™€ ์ „๋ฌธ ์œ ํ˜•๋ณ„ ํฌ๊ธฐ๋ฅผ ๊ฐ€์ง„ ์ „๋ฌธ์„ ๋งŒ๋“ ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PREC = [\"FLASH\", \"IMMEDIATE\", \"PRIORITY\", \"ROUTINE\"]\n", "RANK = {p: i for i, p in enumerate(PREC)}\n", "DEADLINE = {\"FLASH\": 10, \"IMMEDIATE\": 30, \"PRIORITY\": 120, \"ROUTINE\": 600} # ๊ฐ€์ƒ ์ „๋‹ฌ ๋ชฉํ‘œ ์‹œ๊ฐ„(์ดˆ)\n", "MSG_TYPES = { # ์œ ํ˜•: (๊ธด๊ธ‰๋„ ํ›„๋ณด, ํ™•๋ฅ , ๋ณธ๋ฌธ ํฌ๊ธฐ ๋ฒ”์œ„ bytes, ๋ฐœ์ƒ ๋น„์œจ)\n", " \"CONTACT\": ([\"FLASH\", \"IMMEDIATE\"], [0.7, 0.3], (120, 300), 0.08),\n", " \"MEDEVAC\": ([\"FLASH\", \"IMMEDIATE\"], [0.4, 0.6], (150, 350), 0.05),\n", " \"SITREP\": ([\"IMMEDIATE\", \"PRIORITY\"], [0.2, 0.8], (600, 2500), 0.20),\n", " \"POSREP\": ([\"PRIORITY\", \"ROUTINE\"], [0.5, 0.5], (60, 120), 0.35),\n", " \"LOGREQ\": ([\"PRIORITY\", \"ROUTINE\"], [0.4, 0.6], (300, 1200), 0.14),\n", " \"ADMIN\": ([\"ROUTINE\"], [1.0], (1000, 5000), 0.18),\n", "}\n", "\n", "\n", "def make_messages(duration=600, rate=0.12, seed=7, t_start=0.0, prefix=\"M\"):\n", " \"\"\"duration์ดˆ ๋™์•ˆ ํ‰๊ท  rate๊ฑด/์ดˆ๋กœ ๋ฐœ์ƒํ•˜๋Š” ๊ฐ€์ƒ ์ „๋ฌธ ๋ชฉ๋ก.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " n = int(rng.poisson(rate * duration))\n", " times = np.sort(rng.uniform(t_start, t_start + duration, n))\n", " types = list(MSG_TYPES)\n", " msgs = []\n", " for i, t0 in enumerate(times):\n", " ty = types[rng.choice(len(types), p=[MSG_TYPES[k][3] for k in types])]\n", " cands, p, (lo, hi), _ = MSG_TYPES[ty]\n", " msgs.append({\"id\": f\"{prefix}{i:04d}\", \"t0\": float(t0), \"type\": ty, \"prec\": str(rng.choice(cands, p=p)),\n", " \"size\": int(rng.integers(lo, hi + 1)), \"src\": f\"U{int(rng.integers(1, 9)):02d}\"})\n", " return msgs\n", "\n", "\n", "class Link:\n", " \"\"\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ํ˜‘๋Œ€์—ญ ๋ฌด์„  ๋งํฌ(๊ธฐ๋ณธ 2.4kbps). ์ดˆ ๋‹จ์œ„ ์ƒํƒœ(good/bad/jam)์— ๋”ฐ๋ผ\n", " ๋น„ํŠธ์˜ค๋ฅ˜์œจ์ด ๋ฐ”๋€Œ๊ณ (Gilbert-Elliott ๋ชจ๋ธ), ๊ตฌ๊ฐ„๋ณ„ ๋Œ€์—ญํญ ๊ฐ์†Œ๋„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.\"\"\"\n", " BER = {\"good\": 1e-5, \"bad\": 2e-4, \"jam\": 1.2e-3}\n", "\n", " def __init__(self, horizon=4000, bw_bps=2400, seed=1, p_gb=0.02, p_bg=0.15, jam=(), bw_drop=(), rtt=(1.2, 0.4)):\n", " rng = np.random.default_rng(seed)\n", " st, s = [], \"good\"\n", " for _ in range(int(horizon) + 1):\n", " s = (\"bad\" if rng.random() < p_gb else \"good\") if s == \"good\" else (\"good\" if rng.random() < p_bg else \"bad\")\n", " st.append(s)\n", " for a, b in jam:\n", " for k in range(int(a), min(int(b), len(st))):\n", " st[k] = \"jam\"\n", " self.states, self.bw_bps, self.bw_drop, self.rtt_par = st, bw_bps, list(bw_drop), rtt\n", "\n", " def state(self, t):\n", " return self.states[min(int(t), len(self.states) - 1)]\n", "\n", " def bw(self, t):\n", " f = 1.0\n", " for a, b, fac in self.bw_drop:\n", " if a <= t < b:\n", " f = fac\n", " return self.bw_bps * f\n", "\n", " def p_loss(self, nbytes, t):\n", " return 1 - (1 - self.BER[self.state(t)]) ** (8 * nbytes)\n", "\n", " def rtt(self, rng):\n", " return self.rtt_par[0] + rng.exponential(self.rtt_par[1])\n", "# ---------------------------------------------------------------------------\n", "\n", "# ---- ์šฐ์„ ์ˆœ์œ„ ํยท์žฌ์ „์†ก ์ด์‚ฐ ์‚ฌ๊ฑด ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ ๊ณตํ†ต ํ•จ์ˆ˜ ----\n", "HDR = 32 # ์ „๋ฌธ ์กฐ๊ฐ(ํŒจํ‚ท) ํ—ค๋” bytes (๋ฏธ์…˜ 2์˜ ํ—ค๋” ์••์ถ• ์ ์šฉ ์‹œ 12 bytes)\n", "\n", "\n", "def policy_key(m, t, policy, aging):\n", " if policy == \"fifo\": # ๋„์ฐฉ ์ˆœ์„œ\n", " return (m[\"t0\"],)\n", " if policy == \"strict\": # ๊ธด๊ธ‰๋„ ์šฐ์„ , ๊ฐ™์œผ๋ฉด ๋„์ฐฉ ์ˆœ์„œ\n", " return (RANK[m[\"prec\"]], m[\"t0\"])\n", " # aging: ๊ธด๊ธ‰๋„ + (๋Œ€๊ธฐ์‹œ๊ฐ„ / ์ „๋‹ฌ ๋ชฉํ‘œ์‹œ๊ฐ„) ๊ฐ€์ค‘. ์˜ค๋ž˜ ๊ธฐ๋‹ค๋ฆฐ ์ „๋ฌธ๊ณผ ๊ธฐํ•œ์ด ์ž„๋ฐ•ํ•œ ์ „๋ฌธ์ด ์•ž์œผ๋กœ ์˜จ๋‹ค.\n", " return (RANK[m[\"prec\"]] - aging * (t - m[\"t0\"]) / DEADLINE[m[\"prec\"]], m[\"t0\"])\n", "\n", "\n", "def simulate(msgs, link, policy=\"strict\", mtu=256, preempt=True, rto_mode=\"fixed\", rto_s=4.0, aging=0.0,\n", " redundancy=False, adapt_mtu=False, hdr=HDR, seed=0, until=None, admit=None):\n", " \"\"\"์ „๋ฌธ ๋ชฉ๋ก์„ ๋งํฌ๋กœ ๋ณด๋‚ด๋Š” ์ด์‚ฐ ์‚ฌ๊ฑด ์‹œ๋ฎฌ๋ ˆ์ด์…˜.\n", " - ์ „๋ฌธ์€ mtu ํฌ๊ธฐ ์กฐ๊ฐ์œผ๋กœ ๋‚˜๋ˆ  ๋ณด๋‚ธ๋‹ค. ์กฐ๊ฐ์ด ์†์‹ค๋˜๋ฉด RTO ํ›„ ์žฌ์ „์†กํ•œ๋‹ค(์ž๋ฃŒ๋Š” ๋ฒ„๋ฆฌ์ง€ ์•Š์Œ).\n", " - preempt=False๋ฉด ํ•œ ์ „๋ฌธ์˜ ์กฐ๊ฐ์„ ๋๊นŒ์ง€ ์—ฐ์† ์ „์†กํ•œ๋‹ค(ํฐ ์ „๋ฌธ์ด ๊ธด๊ธ‰ ์ „๋ฌธ์„ ๋ง‰๋Š” ํ˜„์ƒ ์žฌํ˜„).\n", " - rto_mode='adaptive'๋ฉด ์™•๋ณต์‹œ๊ฐ„(RTT) ์ถ”์ •๊ฐ’์œผ๋กœ RTO๋ฅผ ๊ณ„์‚ฐํ•œ๋‹ค(Jacobson ๋ฐฉ์‹).\n", " - redundancy=True๋ฉด ๋งํฌ ์—ดํ™” ์ถ”์ • ์‹œ FLASHยทIMMEDIATE ์กฐ๊ฐ์„ 2ํšŒ ์—ฐ์† ์ „์†กํ•œ๋‹ค.\n", " - adapt_mtu=True๋ฉด ๋งํฌ ์—ดํ™” ์ถ”์ • ์‹œ ์กฐ๊ฐ ํฌ๊ธฐ๋ฅผ ์ค„์ธ๋‹ค.\n", " - admit(m, t)์ด False์ธ ์ „๋ฌธ์€ ์ƒˆ ์กฐ๊ฐ์„ ๋ณด๋‚ด์ง€ ์•Š๊ณ  ์ €์žฅํ•ด ๋‘”๋‹ค(์„ฑ๋Šฅ ์ €ํ•˜ ๋ชจ๋“œ).\"\"\"\n", " rng = np.random.default_rng(seed)\n", " pend = sorted(({**m, \"unsent\": m[\"size\"], \"ready\": [], \"frags\": [], \"done\": None, \"first\": None} for m in msgs), key=lambda m: m[\"t0\"])\n", " t, i, active, cur = 0.0, 0, [], None\n", " srtt, rttvar, loss = None, None, 0.0\n", " st = {\"tx\": 0, \"retx\": 0, \"spurious\": 0, \"bytes\": 0, \"decide_ms\": [], \"flag\": {}, \"log\": []}\n", " until = until if until is not None else (pend[-1][\"t0\"] if pend else 0) + 6000\n", " while (i < len(pend) or active) and t < until:\n", " while i < len(pend) and pend[i][\"t0\"] <= t:\n", " active.append(pend[i]); i += 1\n", " td = time.perf_counter()\n", " for m in active: # ACK๊ฐ€ ์ด๋ฏธ ๋„์ฐฉํ•œ ์žฌ์ „์†ก ์˜ˆ์•ฝ์€ ์ทจ์†Œ\n", " m[\"ready\"] = [e for e in m[\"ready\"] if not (e[1][\"ack\"] is not None and e[1][\"ack\"] <= t)]\n", " ready = [m for m in active if any(r <= t for r, _ in m[\"ready\"]) or (m[\"unsent\"] > 0 and (admit is None or admit(m, t)))]\n", " if not ready:\n", " nxt = ([pend[i][\"t0\"]] if i < len(pend) else []) + [r for m in active for r, _ in m[\"ready\"] if r > t]\n", " t = min(nxt) if nxt else t + 1.0\n", " continue\n", " if not preempt and cur in ready and cur[\"unsent\"] > 0:\n", " m = cur\n", " else:\n", " m = min(ready, key=lambda x: policy_key(x, t, policy, aging))\n", " st[\"decide_ms\"].append((time.perf_counter() - td) * 1000)\n", " due = [e for e in m[\"ready\"] if e[0] <= t]\n", " if due:\n", " e = min(due, key=lambda x: x[0]); m[\"ready\"].remove(e); frag = e[1]\n", " st[\"retx\"] += 1; st[\"spurious\"] += frag[\"recv\"]\n", " else:\n", " size = mtu if not adapt_mtu else (mtu if loss < 0.15 else (mtu // 2 if loss < 0.4 else mtu // 4))\n", " size = min(m[\"unsent\"], size); m[\"unsent\"] -= size\n", " frag = {\"b\": size, \"recv\": False, \"ack\": None, \"tries\": 0}\n", " m[\"frags\"].append(frag)\n", " cur = m\n", " m[\"first\"] = t if m[\"first\"] is None else m[\"first\"]\n", " nb = frag[\"b\"] + hdr\n", " copies = 2 if (redundancy and RANK[m[\"prec\"]] <= 1 and loss > 0.15) else 1\n", " for _ in range(copies):\n", " t += nb * 8 / link.bw(t)\n", " st[\"tx\"] += 1; st[\"bytes\"] += nb\n", " ok = rng.random() >= link.p_loss(nb, t)\n", " st[\"log\"].append((t, nb, ok, m[\"prec\"], m[\"id\"])) # ์ „์†ก ๊ธฐ๋ก(์‹œ๊ฐ, bytes, ์„ฑ๊ณต, ๊ธด๊ธ‰๋„, ์ „๋ฌธ)\n", " loss = 0.85 * loss + 0.15 * (0.0 if ok else 1.0) # ์ตœ๊ทผ ์†์‹ค๋ฅ  ์ถ”์ •(EWMA)\n", " st[\"flag\"][int(t)] = st[\"flag\"].get(int(t), False) or loss > 0.12 # ๋งํฌ ์—ดํ™” ํŒ์ •(์ดˆ ๋‹จ์œ„)\n", " if ok and not frag[\"recv\"]:\n", " frag[\"recv\"], frag[\"rt\"] = True, t\n", " r = link.rtt(rng); frag[\"ack\"] = t + r\n", " if srtt is None:\n", " srtt, rttvar = r, r / 2\n", " else:\n", " rttvar = 0.75 * rttvar + 0.25 * abs(srtt - r); srtt = 0.875 * srtt + 0.125 * r\n", " frag[\"tries\"] += 1\n", " rto = rto_s if (rto_mode == \"fixed\" or srtt is None) else float(np.clip(srtt + 4 * rttvar, 1.0, 30.0))\n", " if not frag[\"recv\"] or frag[\"ack\"] > t + rto: # ์†์‹ค ๋˜๋Š” ACK ์ง€์—ฐ -> ์žฌ์ „์†ก ์˜ˆ์•ฝ\n", " m[\"ready\"].append((t + rto, frag))\n", " if m[\"done\"] is None and m[\"unsent\"] == 0 and all(f[\"recv\"] for f in m[\"frags\"]):\n", " m[\"done\"] = max(f[\"rt\"] for f in m[\"frags\"])\n", " active = [x for x in active if not (x[\"unsent\"] == 0 and not x[\"ready\"] and all(f[\"recv\"] for f in x[\"frags\"]))]\n", " res = [{\"id\": m[\"id\"], \"type\": m[\"type\"], \"prec\": m[\"prec\"], \"size\": m[\"size\"], \"t0\": m[\"t0\"], \"done\": m[\"done\"], \"wait\": None if m[\"first\"] is None else m[\"first\"] - m[\"t0\"],\n", " \"retx\": sum(f[\"tries\"] for f in m[\"frags\"]) - len(m[\"frags\"]),\n", " \"latency\": None if m[\"done\"] is None else m[\"done\"] - m[\"t0\"]} for m in pend]\n", " for r in res:\n", " r[\"on_time\"] = r[\"latency\"] is not None and r[\"latency\"] <= DEADLINE[r[\"prec\"]]\n", " return res, st\n", "\n", "\n", "def summarize(res):\n", " \"\"\"๊ธด๊ธ‰๋„๋ณ„ ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ ยท์ง€์—ฐ. ์ข…ํ•ฉ ์ •ํ™•๋„ = ๊ธด๊ธ‰๋„๋ณ„ ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ ์˜ ํ‰๊ท .\"\"\"\n", " df = pd.DataFrame(res)\n", " rows = []\n", " for p in PREC:\n", " d = df[df[\"prec\"] == p]\n", " lat = d[\"latency\"].dropna()\n", " rows.append({\"๊ธด๊ธ‰๋„\": p, \"๊ฑด์ˆ˜\": len(d), \"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \": round(d[\"on_time\"].mean(), 3) if len(d) else np.nan,\n", " \"ํ‰๊ท  ์ง€์—ฐ(s)\": round(lat.mean(), 1) if len(lat) else np.nan,\n", " \"95% ์ง€์—ฐ(s)\": round(lat.quantile(0.95), 1) if len(lat) else np.nan, \"๋ฏธ์ „๋‹ฌ\": int(d[\"done\"].isna().sum())})\n", " tab = pd.DataFrame(rows)\n", " return tab, round(float(tab[\"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \"].mean()), 3)\n", "\n", "INITIAL = dict(policy=\"strict\", preempt=False, rto_mode=\"fixed\", rto_s=6.0, mtu=512, aging=0.0) # ์ดˆ๊ธฐ ์šด์šฉ ์„ค์ •\n", "\n", "\n", "def aar_scenario(seed):\n", " \"\"\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ํ›ˆ๋ จ 1ํšŒ๋ถ„: ์žฌ๋ฐ 60์ดˆ + ๋Œ€์—ญํญ 50% ๊ฐ์†Œ 200์ดˆ + ์ค‘๊ณ„ ์ง€์—ฐ์ด ํฐ ๋ง.\"\"\"\n", " msgs = make_messages(1200, 0.21, seed=seed)\n", " link = Link(12000, seed=seed + 100, jam=[(300, 360)], bw_drop=[(600, 800, 0.5)], rtt=(2.0, 0.8))\n", " return msgs, link\n", "\n", "\n", "def aar_log(res, link):\n", " rows = []\n", " for r in res:\n", " if RANK[r[\"prec\"]] <= 2 and not r[\"on_time\"]:\n", " bad = any(link.state(k) != \"good\" or link.bw(k) < link.bw_bps for k in range(int(r[\"t0\"]), int(r[\"t0\"] + (r[\"latency\"] or 60)) + 1))\n", " if r[\"retx\"] > 0 and bad:\n", " cause = \"๋งํฌ ์—ดํ™” ์ค‘ ์žฌ์ „์†ก\"\n", " elif r[\"retx\"] > 0:\n", " cause = \"์žฌ์ „์†ก ํƒ€์ด๋จธ(RTO) ๋Œ€๊ธฐ\"\n", " elif r[\"wait\"] is not None and r[\"wait\"] > DEADLINE[r[\"prec\"]] / 2:\n", " cause = \"๋Œ€๊ธฐ์—ด ์ ์ฒดยทํ•˜์œ„ ์ „๋ฌธ ์ ์œ \"\n", " else:\n", " cause = \"๋Œ€์šฉ๋Ÿ‰ ์ „๋ฌธ ๋ถ„ํ•  ์ „์†ก ์ง€์—ฐ\"\n", " rows.append({\"์ „๋ฌธ\": r[\"id\"], \"๊ธด๊ธ‰๋„\": r[\"prec\"], \"ํฌ๊ธฐ\": r[\"size\"], \"์ง€์—ฐ(s)\": round(r[\"latency\"] or -1, 1),\n", " \"์ฒซ ์†ก์‹  ๋Œ€๊ธฐ(s)\": round(r[\"wait\"] or -1, 1), \"์žฌ์ „์†ก\": r[\"retx\"], \"์›์ธ\": cause})\n", " return pd.DataFrame(rows, columns=[\"์ „๋ฌธ\", \"๊ธด๊ธ‰๋„\", \"ํฌ๊ธฐ\", \"์ง€์—ฐ(s)\", \"์ฒซ ์†ก์‹  ๋Œ€๊ธฐ(s)\", \"์žฌ์ „์†ก\", \"์›์ธ\"])\n", "\n", "\n", "# 1) ๋ฌด์—‡์ด ์ผ์–ด๋‚ฌ๋‚˜: ์ดˆ๊ธฐ ์„ค์ •์œผ๋กœ ํ›ˆ๋ จ ์šด์šฉ\n", "msgs, link = aar_scenario(7)\n", "before_res, _ = simulate(msgs, link, **INITIAL)\n", "tab0, acc0 = summarize(before_res)\n", "log0 = aar_log(before_res, link)\n", "print(\"[1. ๋ฌด์—‡์ด ์ผ์–ด๋‚ฌ๋‚˜] ์ดˆ๊ธฐ ์„ค์ • ์ข…ํ•ฉ ์ •ํ™•๋„(๊ธด๊ธ‰๋„๋ณ„ ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ  ํ‰๊ท ):\", acc0)\n", "print(tab0[[\"๊ธด๊ธ‰๋„\", \"๊ฑด์ˆ˜\", \"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \", \"95% ์ง€์—ฐ(s)\"]].to_string(index=False))\n", "print(\"\\n[2. ์™œ ์ผ์–ด๋‚ฌ๋‚˜] ์ค‘์š” ์ „๋ฌธ ์ง€์—ฐ ์›์ธ ๋ถ„๋ฅ˜\")\n", "print(log0.groupby([\"๊ธด๊ธ‰๋„\", \"์›์ธ\"]).size().rename(\"๊ฑด์ˆ˜\").to_string())\n", "\n", "# 2) ๋ฌด์—‡์„ ๋ฐ”๊พธ๋‚˜: ๊ฒฉ์ž ํƒ์ƒ‰(์ œ์•ฝ: FLASH ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ  95% ์ด์ƒ)\n", "grid = []\n", "for preempt in (False, True):\n", " for rto in (\"fixed\", \"adaptive\"):\n", " for mtu in (128, 256, 512):\n", " for aging in (0.0, 0.5, 1.0):\n", " cfg = dict(policy=\"aging\" if aging else \"strict\", preempt=preempt, rto_mode=rto, rto_s=6.0, mtu=mtu, aging=aging)\n", " tab, acc = summarize(simulate(msgs, link, **cfg)[0])\n", " grid.append({**cfg, \"FLASH\": tab.loc[0, \"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \"], \"์ข…ํ•ฉ์ •ํ™•๋„\": acc})\n", "g = pd.DataFrame(grid)\n", "ok = g[g[\"FLASH\"] >= 0.95].sort_values([\"์ข…ํ•ฉ์ •ํ™•๋„\", \"FLASH\"], ascending=False)\n", "best = ok.iloc[0]\n", "TUNED = dict(policy=best[\"policy\"], preempt=bool(best[\"preempt\"]), rto_mode=best[\"rto_mode\"], rto_s=6.0, mtu=int(best[\"mtu\"]), aging=float(best[\"aging\"]))\n", "print(f\"\\n[3. ๋ฌด์—‡์„ ๋ฐ”๊พธ๋‚˜] ๊ฒฉ์ž {len(g)}๊ฐœ ์ค‘ ์ œ์•ฝ ์ถฉ์กฑ {len(ok)}๊ฐœ, ์ƒ์œ„ 3๊ฐœ\")\n", "print(ok.head(3)[[\"preempt\", \"rto_mode\", \"mtu\", \"aging\", \"FLASH\", \"์ข…ํ•ฉ์ •ํ™•๋„\"]].to_string(index=False))\n", "\n", "after_res, _ = simulate(msgs, link, **TUNED)\n", "tab1, acc1 = summarize(after_res)\n", "log1 = aar_log(after_res, link)\n", "print(f\"\\n์ข…ํ•ฉ ์ •ํ™•๋„ {acc0:.3f} -> {acc1:.3f} ({(acc1 - acc0) * 100:+.1f}%p, ๋ชฉํ‘œ +10%p: {'๋‹ฌ์„ฑ' if acc1 - acc0 >= 0.10 else '๋ฏธ๋‹ฌ'})\")\n", "print(f\"FLASH ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ  {tab0.loc[0, '๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ ']:.1%} -> {tab1.loc[0, '๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ ']:.1%}, ์ค‘์š” ์ „๋ฌธ ์ง€์—ฐ {len(log0)}๊ฑด -> {len(log1)}๊ฑด\")\n", "repeat = sorted(set(log0[\"์ „๋ฌธ\"]) & set(log1[\"์ „๋ฌธ\"]))\n", "print(f\"์žฌ๋ณด์ • ํ›„์—๋„ ๋ฐ˜๋ณต๋œ ์ง€์—ฐ ์ „๋ฌธ: {repeat if repeat else '์—†์Œ(์žฌ๋ฐœ 0๊ฑด)'}\")\n", "for _, r in log1[log1[\"์ „๋ฌธ\"].isin(repeat)].iterrows():\n", " print(f\" {r['์ „๋ฌธ']} {r['๊ธด๊ธ‰๋„']} ์ง€์—ฐ {r['์ง€์—ฐ(s)']}s, ์žฌ์ „์†ก {r['์žฌ์ „์†ก']}ํšŒ -> ์›์ธ: {r['์›์ธ']} (์Šค์ผ€์ค„๋ง์œผ๋กœ ์ค„์ด๊ธฐ ์–ด๋ ค์šด ์œ ํ˜•, ๋งํฌ ์—ดํ™” ์‹œ ์ค‘๋ณต ์ „์†ก ๊ฒ€ํ† )\")\n", "\n", "# 3) ํšŒ๊ท€ ํ…Œ์ŠคํŠธ: ๋‹ค๋ฅธ ํ›ˆ๋ จ 3ํšŒ(๋‹ค๋ฅธ ์ „๋ฌธยท๋งํฌ)์—์„œ๋„ ๊ฐœ์„ ์ด ์œ ์ง€๋˜๋Š”๊ฐ€\n", "print(\"\\n[ํšŒ๊ท€ ํ…Œ์ŠคํŠธ] ํ›ˆ๋ จ ์ดˆ๊ธฐ ์žฌ๋ณด์ • FLASH(์žฌ๋ณด์ •)\")\n", "reg = []\n", "for sd in (11, 12, 13):\n", " m2, l2 = aar_scenario(sd)\n", " a0 = summarize(simulate(m2, l2, **INITIAL)[0])[1]\n", " t1, a1 = summarize(simulate(m2, l2, **TUNED)[0])\n", " reg.append(a1 >= a0)\n", " print(f\" #{sd} {a0:.3f} {a1:.3f} {t1.loc[0, '๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ ']:.3f}\")\n", "print(f\"ํšŒ๊ท€ ํ…Œ์ŠคํŠธ {sum(reg)}/{len(reg)} ํ†ต๊ณผ(์žฌ๋ณด์ • ํ›„ ์ข…ํ•ฉ ์ •ํ™•๋„๊ฐ€ ๋–จ์–ด์ง€์ง€ ์•Š์Œ)\")\n", "\n", "yaml = (f\"scheduler:\\n policy: {TUNED['policy']}\\n preempt_fragment: {str(TUNED['preempt']).lower()}\\n aging: {TUNED['aging']}\\n\"\n", " f\"fragment_mtu_bytes: {TUNED['mtu']}\\nretransmission:\\n rto_mode: {TUNED['rto_mode']}\\n fixed_rto_s: 6.0\\nconstraint:\\n min_flash_on_time: 0.95\")\n", "print(\"\\n[์žฌ๋ณด์ • ํŒŒ๋ผ๋ฏธํ„ฐ(c202_tuned_params.yaml ๋‚ด์šฉ)]\\n\" + yaml)\n", "print(\"์ง€์† ๊ฐœ์„ : ํ›ˆ๋ จ๋งˆ๋‹ค AAR ์ผ์ง€๋ฅผ ๋ˆ„์ ํ•ด ๊ฐ™์€ ๊ฒฉ์ž ํƒ์ƒ‰์„ ๋‹ค์‹œ ์ˆ˜ํ–‰ํ•˜๊ณ , ๋ณ€๊ฒฝ ์ „ ํšŒ๊ท€ ํ…Œ์ŠคํŠธ ํ†ต๊ณผ๋ฅผ ๋ฐฐํฌ ์กฐ๊ฑด์œผ๋กœ ๋‘”๋‹ค.\")\n", "\n", "plt.figure(figsize=(7, 4))\n", "for pre, c in ((False, \"tab:gray\"), (True, \"tab:blue\")):\n", " s = g[g[\"preempt\"] == pre]\n", " plt.scatter(s[\"FLASH\"], s[\"์ข…ํ•ฉ์ •ํ™•๋„\"], c=c, label=f\"fragment preempt={pre}\")\n", "plt.scatter([best[\"FLASH\"]], [best[\"์ข…ํ•ฉ์ •ํ™•๋„\"]], s=200, facecolors=\"none\", edgecolors=\"red\", label=\"selected\")\n", "plt.axvline(0.95, ls=\"--\", c=\"gray\"); plt.xlabel(\"FLASH on-time ratio\"); plt.ylabel(\"overall accuracy (macro on-time)\")\n", "plt.legend(); plt.title(\"AAR re-tuning grid (synthetic)\"); plt.tight_layout(); plt.show()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 09 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "[1. ๋ฌด์—‡์ด ์ผ์–ด๋‚ฌ๋‚˜] ์ดˆ๊ธฐ ์„ค์ • ์ข…ํ•ฉ ์ •ํ™•๋„(๊ธด๊ธ‰๋„๋ณ„ ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ  ํ‰๊ท ): 0.921\n", " ๊ธด๊ธ‰๋„ ๊ฑด์ˆ˜ ๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ  95% ์ง€์—ฐ(s)\n", " FLASH 25 0.720 19.4\n", "IMMEDIATE 27 0.963 25.6\n", " PRIORITY 99 1.000 52.0\n", " ROUTINE 107 1.000 232.4\n", "\n", "[2. ์™œ ์ผ์–ด๋‚ฌ๋‚˜] ์ค‘์š” ์ „๋ฌธ ์ง€์—ฐ ์›์ธ ๋ถ„๋ฅ˜\n", "๊ธด๊ธ‰๋„ ์›์ธ \n", "FLASH ๋Œ€๊ธฐ์—ด ์ ์ฒดยทํ•˜์œ„ ์ „๋ฌธ ์ ์œ  4\n", " ๋งํฌ ์—ดํ™” ์ค‘ ์žฌ์ „์†ก 3\n", "IMMEDIATE ๋งํฌ ์—ดํ™” ์ค‘ ์žฌ์ „์†ก 1\n", "\n", "[3. ๋ฌด์—‡์„ ๋ฐ”๊พธ๋‚˜] ๊ฒฉ์ž 36๊ฐœ ์ค‘ ์ œ์•ฝ ์ถฉ์กฑ 18๊ฐœ, ์ƒ์œ„ 3๊ฐœ\n", " preempt rto_mode mtu aging FLASH ์ข…ํ•ฉ์ •ํ™•๋„\n", " True fixed 128 0.0 0.96 0.99\n", " True fixed 128 0.5 0.96 0.99\n", " True fixed 128 1.0 0.96 0.99\n", "\n", "์ข…ํ•ฉ ์ •ํ™•๋„ 0.921 -> 0.990 (+6.9%p, ๋ชฉํ‘œ +10%p: ๋ฏธ๋‹ฌ)\n", "FLASH ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ  72.0% -> 96.0%, ์ค‘์š” ์ „๋ฌธ ์ง€์—ฐ 8๊ฑด -> 1๊ฑด\n", "์žฌ๋ณด์ • ํ›„์—๋„ ๋ฐ˜๋ณต๋œ ์ง€์—ฐ ์ „๋ฌธ: ['M0070']\n", " M0070 FLASH ์ง€์—ฐ 41.1s, ์žฌ์ „์†ก 10ํšŒ -> ์›์ธ: ๋งํฌ ์—ดํ™” ์ค‘ ์žฌ์ „์†ก (์Šค์ผ€์ค„๋ง์œผ๋กœ ์ค„์ด๊ธฐ ์–ด๋ ค์šด ์œ ํ˜•, ๋งํฌ ์—ดํ™” ์‹œ ์ค‘๋ณต ์ „์†ก ๊ฒ€ํ† )\n", "\n", "[ํšŒ๊ท€ ํ…Œ์ŠคํŠธ] ํ›ˆ๋ จ ์ดˆ๊ธฐ ์žฌ๋ณด์ • FLASH(์žฌ๋ณด์ •)\n", " #11 0.936 1.000 1.000\n", " #12 0.953 0.988 0.952\n", " #13 0.943 0.951 0.870\n", "ํšŒ๊ท€ ํ…Œ์ŠคํŠธ 3/3 ํ†ต๊ณผ(์žฌ๋ณด์ • ํ›„ ์ข…ํ•ฉ ์ •ํ™•๋„๊ฐ€ ๋–จ์–ด์ง€์ง€ ์•Š์Œ)\n", "\n", "[์žฌ๋ณด์ • ํŒŒ๋ผ๋ฏธํ„ฐ(c202_tuned_params.yaml ๋‚ด์šฉ)]\n", "scheduler:\n", " policy: strict\n", " preempt_fragment: true\n", " aging: 0.0\n", "fragment_mtu_bytes: 128\n", "retransmission:\n", " rto_mode: fixed\n", " fixed_rto_s: 6.0\n", "constraint:\n", "... (์ดํ•˜ ์ƒ๋žต)\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 10. ๋Œ€์—ญํญ ์ œํ•œ ํ™˜๊ฒฝ ๋ฉ”์‹œ์ง€ ํยท์žฌ์ „์†ก ์‹œ์Šคํ…œ ํ†ตํ•ฉ ๋ฐ ์ „์ˆ ํ†ต์‹  ๋ธŒ๋ฆฌํ•‘ ํŒจํ‚ค์ง€ ์™„์„ฑ\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.5์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "๋Œ€์—ญํญ ์ œํ•œ ์‹œ ๋ฉ”์‹œ์ง€ ์šฐ์„ ์ˆœ์œ„ ํยท์žฌ์ „์†ก ์‹œ์Šคํ…œ ํ†ตํ•ฉ ๋ฐ ๋ฐฐํฌ ๊ฐ€์ด๋“œ๋ฅผ ์™„์„ฑํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **ํŒจํ‚ท ์ˆ˜์‹ ๋ถ€ํ„ฐ ์ „์ฒ˜๋ฆฌ, ํยท์žฌ์ „์†ก, ๋งํฌ ๋“ฑ๊ธ‰, ํ†ต์‹ ๊ด€ ์š”์ฒญ ๋Œ€๊ธฐ์—ด, ๊ด€์ œ ํ™”๋ฉด๊นŒ์ง€ ํ†ตํ•ฉํ•˜๊ณ  ์ „์ˆ ํ†ต์‹  ๋ธŒ๋ฆฌํ•‘ ํŒจํ‚ค์ง€๋ฅผ ์™„์„ฑํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **์žฌ์กฐ์ • ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „์ฒด ๊ณผ์ •์„ ํ•œ ๋ฒˆ์— ์‹คํ–‰ํ•˜๋Š” ํ†ตํ•ฉ ์ฝ”๋“œ๋ฅผ ์™„์„ฑํ•˜๊ณ  ์†์ƒยท๋ฏธ๋“ฑ๋ก ํŒจํ‚ท์ด ๊ฒฉ๋ฆฌ๋˜๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์œ ํšจ์„ฑ์„ ์ ๊ฒ€ํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 2๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/main_c202.py, README.md, docs/c202_final_report.pdf(md)` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ๊ตฐ ์‹ค๋ฌด ์ฆ‰์‹œ ๋„์ž… ๊ฐ€๋Šฅ์„ฑ, ์šด์šฉ ์‹ ๋ขฐ๋„ ๋ฐ ์ „๋žต์  ๊ธฐ๋Œ€ํšจ๊ณผ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: End-to-End ํ†ตํ•ฉ ํŒŒ์ดํ”„๋ผ์ธ ์‹คํ–‰ ์„ฑ๊ณต๋ฅ  100%, ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€ 85% ์ด์ƒ\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ์›ํด๋ฆญ ์‹คํ–‰ ์Šคํฌ๋ฆฝํŠธ ๋™์ž‘ ํ™•์ธ ๋ฐ ์ตœ์ข… ๋ฐœํ‘œ ์‚ฐ์ถœ๋ฌผ ์ž‘์„ฑ ์™„๋ฃŒ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ์ตœ์ข… ๊ฒ€์ฆ ์œ„์›ํšŒ(ํ‰๊ฐ€๊ด€) ๋ชจ์˜ ์‹œ์—ฐ ๋ฐ ์ „์ฒด ๊ธฐ๋Šฅ ํ†ตํ•ฉ ๊ฒ€์ฆ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"์›์‹œ ์ „๋ฌธ ํŒจํ‚ท ํŒŒ์‹ฑยท๊ฒ€์ฆยท์••์ถ•๋ถ€ํ„ฐ ์šฐ์„ ์ˆœ์œ„ ํ, ์ง€๋Šฅํ˜• ์žฌ์ „์†ก, ๋งํฌ ์‹ ๋ขฐ๋„ ๋“ฑ๊ธ‰, ํ†ต์‹ ๊ด€ HITL ์š”์ฒญ ๋Œ€๊ธฐ์—ด, ๊ด€์ œ ํ™”๋ฉด, AAR ์žฌ๋ณด์ • ํŒŒ๋ผ๋ฏธํ„ฐ ์ ์šฉ๊นŒ์ง€ ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ์„ ํ•œ ํŒŒ์ผ๋กœ ํ†ตํ•ฉ ์‹คํ–‰ํ•˜๊ณ , ํ•จ์ˆ˜ ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ํฌํ•จํ•œ ์ž์ฒด ์ ๊ฒ€ ๊ฒฐ๊ณผ์™€ ์ „์ˆ ํ†ต์‹  ๋ธŒ๋ฆฌํ•‘ ์š”์•ฝ์„ ์ถœ๋ ฅํ•œ๋‹ค.\"\"\"\n", "import hashlib\n", "import inspect\n", "import json\n", "import sys\n", "import time\n", "import zlib\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์ „์ˆ  ์ „๋ฌธ(๋ฉ”์‹œ์ง€) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยทํ†ต์‹ ๋งยท์ œ์› ์ •๋ณด ์—†์Œ)\n", "# ๊ธด๊ธ‰๋„ 4๋‹จ๊ณ„(FLASH/IMMEDIATE/PRIORITY/ROUTINE)์™€ ์ „๋ฌธ ์œ ํ˜•๋ณ„ ํฌ๊ธฐ๋ฅผ ๊ฐ€์ง„ ์ „๋ฌธ์„ ๋งŒ๋“ ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PREC = [\"FLASH\", \"IMMEDIATE\", \"PRIORITY\", \"ROUTINE\"]\n", "RANK = {p: i for i, p in enumerate(PREC)}\n", "DEADLINE = {\"FLASH\": 10, \"IMMEDIATE\": 30, \"PRIORITY\": 120, \"ROUTINE\": 600} # ๊ฐ€์ƒ ์ „๋‹ฌ ๋ชฉํ‘œ ์‹œ๊ฐ„(์ดˆ)\n", "MSG_TYPES = { # ์œ ํ˜•: (๊ธด๊ธ‰๋„ ํ›„๋ณด, ํ™•๋ฅ , ๋ณธ๋ฌธ ํฌ๊ธฐ ๋ฒ”์œ„ bytes, ๋ฐœ์ƒ ๋น„์œจ)\n", " \"CONTACT\": ([\"FLASH\", \"IMMEDIATE\"], [0.7, 0.3], (120, 300), 0.08),\n", " \"MEDEVAC\": ([\"FLASH\", \"IMMEDIATE\"], [0.4, 0.6], (150, 350), 0.05),\n", " \"SITREP\": ([\"IMMEDIATE\", \"PRIORITY\"], [0.2, 0.8], (600, 2500), 0.20),\n", " \"POSREP\": ([\"PRIORITY\", \"ROUTINE\"], [0.5, 0.5], (60, 120), 0.35),\n", " \"LOGREQ\": ([\"PRIORITY\", \"ROUTINE\"], [0.4, 0.6], (300, 1200), 0.14),\n", " \"ADMIN\": ([\"ROUTINE\"], [1.0], (1000, 5000), 0.18),\n", "}\n", "\n", "\n", "def make_messages(duration=600, rate=0.12, seed=7, t_start=0.0, prefix=\"M\"):\n", " \"\"\"duration์ดˆ ๋™์•ˆ ํ‰๊ท  rate๊ฑด/์ดˆ๋กœ ๋ฐœ์ƒํ•˜๋Š” ๊ฐ€์ƒ ์ „๋ฌธ ๋ชฉ๋ก.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " n = int(rng.poisson(rate * duration))\n", " times = np.sort(rng.uniform(t_start, t_start + duration, n))\n", " types = list(MSG_TYPES)\n", " msgs = []\n", " for i, t0 in enumerate(times):\n", " ty = types[rng.choice(len(types), p=[MSG_TYPES[k][3] for k in types])]\n", " cands, p, (lo, hi), _ = MSG_TYPES[ty]\n", " msgs.append({\"id\": f\"{prefix}{i:04d}\", \"t0\": float(t0), \"type\": ty, \"prec\": str(rng.choice(cands, p=p)),\n", " \"size\": int(rng.integers(lo, hi + 1)), \"src\": f\"U{int(rng.integers(1, 9)):02d}\"})\n", " return msgs\n", "\n", "\n", "class Link:\n", " \"\"\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ํ˜‘๋Œ€์—ญ ๋ฌด์„  ๋งํฌ(๊ธฐ๋ณธ 2.4kbps). ์ดˆ ๋‹จ์œ„ ์ƒํƒœ(good/bad/jam)์— ๋”ฐ๋ผ\n", " ๋น„ํŠธ์˜ค๋ฅ˜์œจ์ด ๋ฐ”๋€Œ๊ณ (Gilbert-Elliott ๋ชจ๋ธ), ๊ตฌ๊ฐ„๋ณ„ ๋Œ€์—ญํญ ๊ฐ์†Œ๋„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.\"\"\"\n", " BER = {\"good\": 1e-5, \"bad\": 2e-4, \"jam\": 1.2e-3}\n", "\n", " def __init__(self, horizon=4000, bw_bps=2400, seed=1, p_gb=0.02, p_bg=0.15, jam=(), bw_drop=(), rtt=(1.2, 0.4)):\n", " rng = np.random.default_rng(seed)\n", " st, s = [], \"good\"\n", " for _ in range(int(horizon) + 1):\n", " s = (\"bad\" if rng.random() < p_gb else \"good\") if s == \"good\" else (\"good\" if rng.random() < p_bg else \"bad\")\n", " st.append(s)\n", " for a, b in jam:\n", " for k in range(int(a), min(int(b), len(st))):\n", " st[k] = \"jam\"\n", " self.states, self.bw_bps, self.bw_drop, self.rtt_par = st, bw_bps, list(bw_drop), rtt\n", "\n", " def state(self, t):\n", " return self.states[min(int(t), len(self.states) - 1)]\n", "\n", " def bw(self, t):\n", " f = 1.0\n", " for a, b, fac in self.bw_drop:\n", " if a <= t < b:\n", " f = fac\n", " return self.bw_bps * f\n", "\n", " def p_loss(self, nbytes, t):\n", " return 1 - (1 - self.BER[self.state(t)]) ** (8 * nbytes)\n", "\n", " def rtt(self, rng):\n", " return self.rtt_par[0] + rng.exponential(self.rtt_par[1])\n", "# ---------------------------------------------------------------------------\n", "\n", "# ---- ์šฐ์„ ์ˆœ์œ„ ํยท์žฌ์ „์†ก ์ด์‚ฐ ์‚ฌ๊ฑด ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ ๊ณตํ†ต ํ•จ์ˆ˜ ----\n", "HDR = 32 # ์ „๋ฌธ ์กฐ๊ฐ(ํŒจํ‚ท) ํ—ค๋” bytes (๋ฏธ์…˜ 2์˜ ํ—ค๋” ์••์ถ• ์ ์šฉ ์‹œ 12 bytes)\n", "\n", "\n", "def policy_key(m, t, policy, aging):\n", " if policy == \"fifo\": # ๋„์ฐฉ ์ˆœ์„œ\n", " return (m[\"t0\"],)\n", " if policy == \"strict\": # ๊ธด๊ธ‰๋„ ์šฐ์„ , ๊ฐ™์œผ๋ฉด ๋„์ฐฉ ์ˆœ์„œ\n", " return (RANK[m[\"prec\"]], m[\"t0\"])\n", " # aging: ๊ธด๊ธ‰๋„ + (๋Œ€๊ธฐ์‹œ๊ฐ„ / ์ „๋‹ฌ ๋ชฉํ‘œ์‹œ๊ฐ„) ๊ฐ€์ค‘. ์˜ค๋ž˜ ๊ธฐ๋‹ค๋ฆฐ ์ „๋ฌธ๊ณผ ๊ธฐํ•œ์ด ์ž„๋ฐ•ํ•œ ์ „๋ฌธ์ด ์•ž์œผ๋กœ ์˜จ๋‹ค.\n", " return (RANK[m[\"prec\"]] - aging * (t - m[\"t0\"]) / DEADLINE[m[\"prec\"]], m[\"t0\"])\n", "\n", "\n", "def simulate(msgs, link, policy=\"strict\", mtu=256, preempt=True, rto_mode=\"fixed\", rto_s=4.0, aging=0.0,\n", " redundancy=False, adapt_mtu=False, hdr=HDR, seed=0, until=None, admit=None):\n", " \"\"\"์ „๋ฌธ ๋ชฉ๋ก์„ ๋งํฌ๋กœ ๋ณด๋‚ด๋Š” ์ด์‚ฐ ์‚ฌ๊ฑด ์‹œ๋ฎฌ๋ ˆ์ด์…˜.\n", " - ์ „๋ฌธ์€ mtu ํฌ๊ธฐ ์กฐ๊ฐ์œผ๋กœ ๋‚˜๋ˆ  ๋ณด๋‚ธ๋‹ค. ์กฐ๊ฐ์ด ์†์‹ค๋˜๋ฉด RTO ํ›„ ์žฌ์ „์†กํ•œ๋‹ค(์ž๋ฃŒ๋Š” ๋ฒ„๋ฆฌ์ง€ ์•Š์Œ).\n", " - preempt=False๋ฉด ํ•œ ์ „๋ฌธ์˜ ์กฐ๊ฐ์„ ๋๊นŒ์ง€ ์—ฐ์† ์ „์†กํ•œ๋‹ค(ํฐ ์ „๋ฌธ์ด ๊ธด๊ธ‰ ์ „๋ฌธ์„ ๋ง‰๋Š” ํ˜„์ƒ ์žฌํ˜„).\n", " - rto_mode='adaptive'๋ฉด ์™•๋ณต์‹œ๊ฐ„(RTT) ์ถ”์ •๊ฐ’์œผ๋กœ RTO๋ฅผ ๊ณ„์‚ฐํ•œ๋‹ค(Jacobson ๋ฐฉ์‹).\n", " - redundancy=True๋ฉด ๋งํฌ ์—ดํ™” ์ถ”์ • ์‹œ FLASHยทIMMEDIATE ์กฐ๊ฐ์„ 2ํšŒ ์—ฐ์† ์ „์†กํ•œ๋‹ค.\n", " - adapt_mtu=True๋ฉด ๋งํฌ ์—ดํ™” ์ถ”์ • ์‹œ ์กฐ๊ฐ ํฌ๊ธฐ๋ฅผ ์ค„์ธ๋‹ค.\n", " - admit(m, t)์ด False์ธ ์ „๋ฌธ์€ ์ƒˆ ์กฐ๊ฐ์„ ๋ณด๋‚ด์ง€ ์•Š๊ณ  ์ €์žฅํ•ด ๋‘”๋‹ค(์„ฑ๋Šฅ ์ €ํ•˜ ๋ชจ๋“œ).\"\"\"\n", " rng = np.random.default_rng(seed)\n", " pend = sorted(({**m, \"unsent\": m[\"size\"], \"ready\": [], \"frags\": [], \"done\": None, \"first\": None} for m in msgs), key=lambda m: m[\"t0\"])\n", " t, i, active, cur = 0.0, 0, [], None\n", " srtt, rttvar, loss = None, None, 0.0\n", " st = {\"tx\": 0, \"retx\": 0, \"spurious\": 0, \"bytes\": 0, \"decide_ms\": [], \"flag\": {}, \"log\": []}\n", " until = until if until is not None else (pend[-1][\"t0\"] if pend else 0) + 6000\n", " while (i < len(pend) or active) and t < until:\n", " while i < len(pend) and pend[i][\"t0\"] <= t:\n", " active.append(pend[i]); i += 1\n", " td = time.perf_counter()\n", " for m in active: # ACK๊ฐ€ ์ด๋ฏธ ๋„์ฐฉํ•œ ์žฌ์ „์†ก ์˜ˆ์•ฝ์€ ์ทจ์†Œ\n", " m[\"ready\"] = [e for e in m[\"ready\"] if not (e[1][\"ack\"] is not None and e[1][\"ack\"] <= t)]\n", " ready = [m for m in active if any(r <= t for r, _ in m[\"ready\"]) or (m[\"unsent\"] > 0 and (admit is None or admit(m, t)))]\n", " if not ready:\n", " nxt = ([pend[i][\"t0\"]] if i < len(pend) else []) + [r for m in active for r, _ in m[\"ready\"] if r > t]\n", " t = min(nxt) if nxt else t + 1.0\n", " continue\n", " if not preempt and cur in ready and cur[\"unsent\"] > 0:\n", " m = cur\n", " else:\n", " m = min(ready, key=lambda x: policy_key(x, t, policy, aging))\n", " st[\"decide_ms\"].append((time.perf_counter() - td) * 1000)\n", " due = [e for e in m[\"ready\"] if e[0] <= t]\n", " if due:\n", " e = min(due, key=lambda x: x[0]); m[\"ready\"].remove(e); frag = e[1]\n", " st[\"retx\"] += 1; st[\"spurious\"] += frag[\"recv\"]\n", " else:\n", " size = mtu if not adapt_mtu else (mtu if loss < 0.15 else (mtu // 2 if loss < 0.4 else mtu // 4))\n", " size = min(m[\"unsent\"], size); m[\"unsent\"] -= size\n", " frag = {\"b\": size, \"recv\": False, \"ack\": None, \"tries\": 0}\n", " m[\"frags\"].append(frag)\n", " cur = m\n", " m[\"first\"] = t if m[\"first\"] is None else m[\"first\"]\n", " nb = frag[\"b\"] + hdr\n", " copies = 2 if (redundancy and RANK[m[\"prec\"]] <= 1 and loss > 0.15) else 1\n", " for _ in range(copies):\n", " t += nb * 8 / link.bw(t)\n", " st[\"tx\"] += 1; st[\"bytes\"] += nb\n", " ok = rng.random() >= link.p_loss(nb, t)\n", " st[\"log\"].append((t, nb, ok, m[\"prec\"], m[\"id\"])) # ์ „์†ก ๊ธฐ๋ก(์‹œ๊ฐ, bytes, ์„ฑ๊ณต, ๊ธด๊ธ‰๋„, ์ „๋ฌธ)\n", " loss = 0.85 * loss + 0.15 * (0.0 if ok else 1.0) # ์ตœ๊ทผ ์†์‹ค๋ฅ  ์ถ”์ •(EWMA)\n", " st[\"flag\"][int(t)] = st[\"flag\"].get(int(t), False) or loss > 0.12 # ๋งํฌ ์—ดํ™” ํŒ์ •(์ดˆ ๋‹จ์œ„)\n", " if ok and not frag[\"recv\"]:\n", " frag[\"recv\"], frag[\"rt\"] = True, t\n", " r = link.rtt(rng); frag[\"ack\"] = t + r\n", " if srtt is None:\n", " srtt, rttvar = r, r / 2\n", " else:\n", " rttvar = 0.75 * rttvar + 0.25 * abs(srtt - r); srtt = 0.875 * srtt + 0.125 * r\n", " frag[\"tries\"] += 1\n", " rto = rto_s if (rto_mode == \"fixed\" or srtt is None) else float(np.clip(srtt + 4 * rttvar, 1.0, 30.0))\n", " if not frag[\"recv\"] or frag[\"ack\"] > t + rto: # ์†์‹ค ๋˜๋Š” ACK ์ง€์—ฐ -> ์žฌ์ „์†ก ์˜ˆ์•ฝ\n", " m[\"ready\"].append((t + rto, frag))\n", " if m[\"done\"] is None and m[\"unsent\"] == 0 and all(f[\"recv\"] for f in m[\"frags\"]):\n", " m[\"done\"] = max(f[\"rt\"] for f in m[\"frags\"])\n", " active = [x for x in active if not (x[\"unsent\"] == 0 and not x[\"ready\"] and all(f[\"recv\"] for f in x[\"frags\"]))]\n", " res = [{\"id\": m[\"id\"], \"type\": m[\"type\"], \"prec\": m[\"prec\"], \"size\": m[\"size\"], \"t0\": m[\"t0\"], \"done\": m[\"done\"], \"wait\": None if m[\"first\"] is None else m[\"first\"] - m[\"t0\"],\n", " \"retx\": sum(f[\"tries\"] for f in m[\"frags\"]) - len(m[\"frags\"]),\n", " \"latency\": None if m[\"done\"] is None else m[\"done\"] - m[\"t0\"]} for m in pend]\n", " for r in res:\n", " r[\"on_time\"] = r[\"latency\"] is not None and r[\"latency\"] <= DEADLINE[r[\"prec\"]]\n", " return res, st\n", "\n", "\n", "def summarize(res):\n", " \"\"\"๊ธด๊ธ‰๋„๋ณ„ ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ ยท์ง€์—ฐ. ์ข…ํ•ฉ ์ •ํ™•๋„ = ๊ธด๊ธ‰๋„๋ณ„ ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ ์˜ ํ‰๊ท .\"\"\"\n", " df = pd.DataFrame(res)\n", " rows = []\n", " for p in PREC:\n", " d = df[df[\"prec\"] == p]\n", " lat = d[\"latency\"].dropna()\n", " rows.append({\"๊ธด๊ธ‰๋„\": p, \"๊ฑด์ˆ˜\": len(d), \"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \": round(d[\"on_time\"].mean(), 3) if len(d) else np.nan,\n", " \"ํ‰๊ท  ์ง€์—ฐ(s)\": round(lat.mean(), 1) if len(lat) else np.nan,\n", " \"95% ์ง€์—ฐ(s)\": round(lat.quantile(0.95), 1) if len(lat) else np.nan, \"๋ฏธ์ „๋‹ฌ\": int(d[\"done\"].isna().sum())})\n", " tab = pd.DataFrame(rows)\n", " return tab, round(float(tab[\"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \"].mean()), 3)\n", "\n", "CONFIG = {\"policy\": \"strict\", \"preempt\": True, \"rto_mode\": \"fixed\", \"rto_s\": 6.0, \"mtu\": 128, \"aging\": 0.0, # ๋ฏธ์…˜ 9 ์žฌ๋ณด์ •๊ฐ’\n", " \"redundancy\": True, \"hdr\": 14} # ๋ฏธ์…˜ 4 ์ค‘๋ณต ์ „์†ก, ๋ฏธ์…˜ 2 ํ—ค๋” ์••์ถ•\n", "ALIAS = {\"Z\": \"FLASH\", \"O\": \"IMMEDIATE\", \"P\": \"PRIORITY\", \"R\": \"ROUTINE\"}\n", "ROSTER = {f\"U{i:02d}\" for i in range(1, 9)}\n", "\n", "\n", "def to_packet(m):\n", " body = \"\".join(f\"{m['type']} {m['src']} I{(k * 7919 + int(m['t0'] * 10)) % 997:03d} Q{(k * 31 + m['size']) % 89:02d};\" for k in range(m[\"size\"] // 20 + 1))[:m[\"size\"]]\n", " return f\"ZCZC|ID={m['id']}|PREC={'ZOPR'[RANK[m['prec']]]}|TYPE={m['type']}|SRC={m['src']}|T={m['t0']:.1f}|CRC={zlib.crc32(body.encode()):08x}|BODY={body}\"\n", "\n", "\n", "def parse(line):\n", " kv = dict(p.split(\"=\", 1) for p in line.split(\"|\")[1:] if \"=\" in p)\n", " if set(kv) != {\"ID\", \"PREC\", \"TYPE\", \"SRC\", \"T\", \"CRC\", \"BODY\"} or kv[\"PREC\"] not in ALIAS or kv[\"SRC\"] not in ROSTER:\n", " raise ValueError(\"ํ˜•์‹ยท๊ธด๊ธ‰๋„ยท์†ก์‹ ๊ตญ ์˜ค๋ฅ˜\")\n", " if f\"{zlib.crc32(kv['BODY'].encode()):08x}\" != kv[\"CRC\"]:\n", " raise ValueError(\"CRC ๋ถˆ์ผ์น˜\")\n", " body_z = zlib.compress(kv[\"BODY\"].encode(), 9)\n", " return {\"id\": kv[\"ID\"], \"prec\": ALIAS[kv[\"PREC\"]], \"type\": kv[\"TYPE\"], \"src\": kv[\"SRC\"], \"t0\": float(kv[\"T\"]), \"size\": len(body_z)}\n", "\n", "\n", "def link_grade(loss):\n", " return \"High\" if loss < 0.1 else (\"Medium\" if loss < 0.3 else \"Low\")\n", "\n", "\n", "class RequestQueue:\n", " \"\"\"ํ†ต์‹ ๊ด€ ์กฐ์ž‘ ์š”์ฒญ ๋Œ€๊ธฐ์—ด: ์‹œ์Šคํ…œ์€ ์š”์ฒญ๋งŒ ๋งŒ๋“ค๊ณ  ์ ์šฉ์€ ์‚ฌ๋žŒ์ด ํ•œ๋‹ค.\"\"\"\n", "\n", " def __init__(self):\n", " self.items, self.log = [], []\n", "\n", " def submit(self, kind, target, why):\n", " prev = self.log[-1][\"hash\"] if self.log else \"GENESIS\"\n", " body = {\"event\": \"REQUEST\", \"kind\": kind, \"target\": target, \"why\": why}\n", " self.log.append({**body, \"prev\": prev, \"hash\": hashlib.sha256((prev + json.dumps(body, ensure_ascii=False, sort_keys=True)).encode()).hexdigest()})\n", " self.items.append({**body, \"status\": \"PENDING\"})\n", "\n", "\n", "# ํ•จ์ˆ˜ ์ปค๋ฒ„๋ฆฌ์ง€ ์ธก์ •(ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ sys.setprofile): ์ด ํŒŒ์ผ์—์„œ ์ •์˜ํ•œ ํ•จ์ˆ˜ ์ค‘ ์‹ค์ œ ํ˜ธ์ถœ๋œ ๋น„์œจ\n", "called = set()\n", "sys.setprofile(lambda f, e, a: called.add(f.f_code.co_name) if e == \"call\" and f.f_globals is globals() else None)\n", "t_start = time.perf_counter()\n", "\n", "# ---------------- 1. ์ˆ˜์‹ ยท์ „์ฒ˜๋ฆฌ ----------------\n", "msgs = make_messages(1200, 0.5, seed=21) # ํ‰์‹œ์˜ ์•ฝ 2.4๋ฐฐ ํŠธ๋ž˜ํ”ฝ\n", "lines = [to_packet(m) for m in msgs]\n", "lines[5] = lines[5].replace(\"SRC=U\", \"SRC=X\") # ์†์ƒยท๋ฏธ๋“ฑ๋ก ์†ก์‹ ๊ตญ ํŒจํ‚ท ์ฃผ์ž…\n", "lines[9] = lines[9][:-3] + \"###\"\n", "parsed, rejected = [], {}\n", "for ln in lines:\n", " try:\n", " parsed.append(parse(ln))\n", " except ValueError as e:\n", " rejected[ln.split(\"|\")[1][3:]] = str(e)\n", "\n", "# ---------------- 2. ํยท์žฌ์ „์†ก ----------------\n", "link = Link(12000, seed=31, jam=[(300, 360)], bw_drop=[(600, 800, 0.5)], rtt=(2.0, 0.8))\n", "res, st = simulate(parsed, link, **CONFIG)\n", "tab, acc = summarize(res)\n", "lg = pd.DataFrame(st[\"log\"], columns=[\"t\", \"bytes\", \"ok\", \"prec\", \"id\"])\n", "lg[\"sec\"] = (lg[\"t\"] // 10 * 10).astype(int)\n", "win = lg.groupby(\"sec\")[\"ok\"].apply(lambda s: 1 - s.mean()).rename(\"loss\").reset_index()\n", "win[\"grade\"] = win[\"loss\"].map(link_grade)\n", "\n", "# ---------------- 3. HITL ์š”์ฒญ(์ž๋™ ์ ์šฉ ์—†์Œ) ----------------\n", "rq = RequestQueue()\n", "for _, w in win[win[\"grade\"] == \"Low\"].iterrows():\n", " rq.submit(\"ROUTE_REVIEW\", f\"t={w['sec']}s\", f\"์†์‹ค๋ฅ  {w['loss']:.0%}, ๊ฒฝ๋กœยท์ค‘๊ณ„ ๊ฒ€ํ† \")\n", "for r in res:\n", " if r[\"prec\"] == \"FLASH\" and not r[\"on_time\"]:\n", " rq.submit(\"FLASH_LATE\", r[\"id\"], f\"์ง€์—ฐ {r['latency']:.1f}s, ํ†ต์‹ ๊ด€ ํ™•์ธ\")\n", "sys.setprofile(None)\n", "\n", "# ---------------- 4. ์ž์ฒด ์ ๊ฒ€ ----------------\n", "mine = [n for n, o in list(globals().items()) if inspect.isfunction(o) and o.__module__ == \"__main__\"]\n", "mine += [\"submit\", \"state\", \"bw\", \"p_loss\", \"rtt\", \"__init__\"]\n", "cov = len(set(mine) & called) / len(set(mine))\n", "urg = np.mean([r[\"on_time\"] for r in res if RANK[r[\"prec\"]] <= 1])\n", "elapsed = time.perf_counter() - t_start\n", "chk = {\n", " \"์†์ƒ ํŒจํ‚ท 2๊ฑด ๊ฒฉ๋ฆฌ\": len(rejected) == 2,\n", " \"์ •์ƒ ์ „๋ฌธ ์ „๋ถ€ ํ ๋“ฑ๋ก\": len(parsed) == len(msgs) - 2,\n", " \"์ „๋ฌธ ์œ ์‹ค 0๊ฑด(์ „๋‹ฌ+๋ณด๊ด€=์ „์ฒด)\": sum(r[\"done\"] is not None for r in res) + sum(r[\"done\"] is None for r in res) == len(parsed),\n", " \"FLASH ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ  90% ์ด์ƒ\": tab.loc[0, \"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \"] >= 0.9,\n", " \"๊ธด๊ธ‰ ์ „๋ฌธ ๊ธฐํ•œ ๋‚ด 85% ์ด์ƒ\": urg >= 0.85,\n", " \"์ข…ํ•ฉ ์ •ํ™•๋„ 0.9 ์ด์ƒ\": acc >= 0.9,\n", " \"์ž๋™ ์ ์šฉ 0๊ฑด(์š”์ฒญ ์ „๋ถ€ PENDING)\": all(i[\"status\"] == \"PENDING\" for i in rq.items),\n", " \"๊ฐ์‚ฌ ๋กœ๊ทธ ๊ฑด์ˆ˜ ์ผ์น˜\": len(rq.log) == len(rq.items),\n", " \"์Šค์ผ€์ค„๋ง ๊ฒฐ์ • 200ms ์ด๋‚ด\": max(st[\"decide_ms\"]) < 200,\n", " \"ํ•จ์ˆ˜ ์ปค๋ฒ„๋ฆฌ์ง€ 85% ์ด์ƒ\": cov >= 0.85,\n", "}\n", "passed = sum(chk.values())\n", "for k, v in chk.items():\n", " print(f\"[{'PASS' if v else 'FAIL'}] {k}\")\n", "print(f\"์ž์ฒด ์ ๊ฒ€ {passed}/{len(chk)} ํ†ต๊ณผ, ํ•จ์ˆ˜ ์ปค๋ฒ„๋ฆฌ์ง€ {cov:.0%}, ํŒŒ์ดํ”„๋ผ์ธ ์‹คํ–‰ ์„ฑ๊ณต {'์˜ˆ' if passed == len(chk) else '์•„๋‹ˆ์˜ค'}\")\n", "print(tab[[\"๊ธด๊ธ‰๋„\", \"๊ฑด์ˆ˜\", \"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \", \"95% ์ง€์—ฐ(s)\", \"๋ฏธ์ „๋‹ฌ\"]].to_string(index=False))\n", "\n", "# ---------------- 5. ๊ด€์ œ ํ™”๋ฉด ----------------\n", "fig, ax = plt.subplots(2, 2, figsize=(12, 7))\n", "ax[0, 0].bar(tab[\"๊ธด๊ธ‰๋„\"], tab[\"๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ \"], color=[\"red\", \"orange\", \"deepskyblue\", \"gray\"]); ax[0, 0].set_ylim(0, 1.05); ax[0, 0].set_title(\"On-time ratio by precedence\")\n", "ax[0, 1].plot(win[\"sec\"], win[\"loss\"] * 100); ax[0, 1].axhline(30, c=\"r\", ls=\"--\"); ax[0, 1].set_title(\"Loss rate per 10 s (%)\")\n", "for a, b, c in [(300, 360, \"purple\"), (600, 800, \"gold\")]:\n", " ax[0, 1].axvspan(a, b, color=c, alpha=0.2)\n", "lat = pd.DataFrame(res)\n", "for p, c in zip(PREC, [\"red\", \"orange\", \"deepskyblue\", \"gray\"]):\n", " d = lat[lat[\"prec\"] == p]; ax[1, 0].scatter(d[\"t0\"], d[\"latency\"], s=8, c=c, label=p)\n", "ax[1, 0].set_yscale(\"log\"); ax[1, 0].legend(fontsize=7); ax[1, 0].set_title(\"Delivery latency (s, log)\")\n", "ax[1, 1].axis(\"off\")\n", "ax[1, 1].text(0, 0.9, f\"Messages {len(parsed)} (+{len(rejected)} rejected)\\nOverall acc {acc:.3f}\\nUrgent ok {urg:.1%}\\n\"\n", " f\"HITL pending {len(rq.items)}\\nSelf-check {passed}/{len(chk)}\", family=\"monospace\", fontsize=13, va=\"top\")\n", "plt.suptitle(\"C202 integrated message queue - training data only\"); plt.tight_layout(); plt.show()\n", "\n", "# ---------------- 6. ์ „์ˆ ํ†ต์‹  ๋ธŒ๋ฆฌํ•‘ ์š”์•ฝ ----------------\n", "print(f\"\"\"\n", "[์ „์ˆ ํ†ต์‹  ๋ธŒ๋ฆฌํ•‘ ์š”์•ฝ - ๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ]\n", "1. ์ฒ˜๋ฆฌ: ์ˆ˜์‹  {len(lines)}๊ฑด ์ค‘ {len(parsed)}๊ฑด ์ „์†ก, ์†์ƒยท๋ฏธ๋“ฑ๋ก {len(rejected)}๊ฑด ๊ฒฉ๋ฆฌ, ์ „์ฒด ์†Œ์š” {elapsed:.1f}s\n", "2. ์„ฑ๋Šฅ: ์ข…ํ•ฉ ์ •ํ™•๋„ {acc:.3f}, FLASH ๊ธฐํ•œ ๋‚ด {tab.loc[0, '๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ ']:.1%}, ๊ธด๊ธ‰ ์ „๋ฌธ ๊ธฐํ•œ ๋‚ด {urg:.1%}, ์žฌ์ „์†ก {st['retx']}ํšŒ\n", "3. ํ†ต์‹ ๊ด€ ๊ฒ€ํ†  ์š”์ฒญ: {len(rq.items)}๊ฑด(๊ฒฝ๋กœ ๊ฒ€ํ†  {sum(i['kind'] == 'ROUTE_REVIEW' for i in rq.items)}, FLASH ์ง€์—ฐ {sum(i['kind'] == 'FLASH_LATE' for i in rq.items)}), ์ž๋™ ์ ์šฉ 0๊ฑด\n", "4. ์šด์šฉ ์œ ์˜: ๊ฐ•์ œ ์ „์†กยท๋ณด๋ฅ˜๋Š” ํ†ต์‹ ๊ด€์ด ๊ทผ๊ฑฐ๋ฅผ ๊ธฐ๋กํ•ด ์ˆ˜ํ–‰ํ•˜๊ณ , FLASH๋ณด๋‹ค ์•ž์„œ๋Š” ์กฐ์ž‘์€ ์ƒ๊ธ‰์ž ์Šน์ธ์„ ๋ฐ›๋Š”๋‹ค.\n", "5. ํ•œ๊ณ„ยทํ™•์žฅ: ๊ฐ€์ƒ ๋งํฌ ๋ชจ๋ธ ๊ธฐ์ค€ ๊ฒฐ๊ณผ๋กœ ์‹ค์ œ ์žฅ๋น„ยท๋ง์—์„œ๋Š” ์žฌ์ธก์ •์ด ํ•„์š”ํ•˜๋‹ค. ์ธ์ ‘ ์ฒด๊ณ„ ์—ฐ๋™์€ ์ „๋ฌธ ์ž…๋ ฅยท์ „๋‹ฌ ๊ฒฐ๊ณผ ์กฐํšŒ API๋กœ ํ™•์žฅํ•œ๋‹ค.\"\"\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 10 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "[PASS] ์†์ƒ ํŒจํ‚ท 2๊ฑด ๊ฒฉ๋ฆฌ\n", "[PASS] ์ •์ƒ ์ „๋ฌธ ์ „๋ถ€ ํ ๋“ฑ๋ก\n", "[PASS] ์ „๋ฌธ ์œ ์‹ค 0๊ฑด(์ „๋‹ฌ+๋ณด๊ด€=์ „์ฒด)\n", "[PASS] FLASH ๊ธฐํ•œ ๋‚ด ์ „๋‹ฌ๋ฅ  90% ์ด์ƒ\n", "[PASS] ๊ธด๊ธ‰ ์ „๋ฌธ ๊ธฐํ•œ ๋‚ด 85% ์ด์ƒ\n", "[PASS] ์ข…ํ•ฉ ์ •ํ™•๋„ 0.9 ์ด์ƒ\n", "[PASS] ์ž๋™ ์ ์šฉ 0๊ฑด(์š”์ฒญ ์ „๋ถ€ PENDING)\n", "[PASS] ๊ฐ์‚ฌ ๋กœ๊ทธ ๊ฑด์ˆ˜ ์ผ์น˜\n", "[PASS] ์Šค์ผ€์ค„๋ง ๊ฒฐ์ • 200ms ์ด๋‚ด\n", "[PASS] ํ•จ์ˆ˜ ์ปค๋ฒ„๋ฆฌ์ง€ 85% ์ด์ƒ\n", "์ž์ฒด ์ ๊ฒ€ 10/10 ํ†ต๊ณผ, ํ•จ์ˆ˜ ์ปค๋ฒ„๋ฆฌ์ง€ 100%, ํŒŒ์ดํ”„๋ผ์ธ ์‹คํ–‰ ์„ฑ๊ณต ์˜ˆ\n", " ๊ธด๊ธ‰๋„ ๊ฑด์ˆ˜ ๊ธฐํ•œ๋‚ด ์ „๋‹ฌ๋ฅ  95% ์ง€์—ฐ(s) ๋ฏธ์ „๋‹ฌ\n", " FLASH 45 0.978 7.3 0\n", "IMMEDIATE 55 1.000 7.3 0\n", " PRIORITY 248 1.000 8.3 0\n", " ROUTINE 271 1.000 46.8 0\n", "\n", "[์ „์ˆ ํ†ต์‹  ๋ธŒ๋ฆฌํ•‘ ์š”์•ฝ - ๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ]\n", "1. ์ฒ˜๋ฆฌ: ์ˆ˜์‹  621๊ฑด ์ค‘ 619๊ฑด ์ „์†ก, ์†์ƒยท๋ฏธ๋“ฑ๋ก 2๊ฑด ๊ฒฉ๋ฆฌ, ์ „์ฒด ์†Œ์š” 0.2s\n", "2. ์„ฑ๋Šฅ: ์ข…ํ•ฉ ์ •ํ™•๋„ 0.994, FLASH ๊ธฐํ•œ ๋‚ด 97.8%, ๊ธด๊ธ‰ ์ „๋ฌธ ๊ธฐํ•œ ๋‚ด 99.0%, ์žฌ์ „์†ก 111ํšŒ\n", "3. ํ†ต์‹ ๊ด€ ๊ฒ€ํ†  ์š”์ฒญ: 7๊ฑด(๊ฒฝ๋กœ ๊ฒ€ํ†  6, FLASH ์ง€์—ฐ 1), ์ž๋™ ์ ์šฉ 0๊ฑด\n", "4. ์šด์šฉ ์œ ์˜: ๊ฐ•์ œ ์ „์†กยท๋ณด๋ฅ˜๋Š” ํ†ต์‹ ๊ด€์ด ๊ทผ๊ฑฐ๋ฅผ ๊ธฐ๋กํ•ด ์ˆ˜ํ–‰ํ•˜๊ณ , FLASH๋ณด๋‹ค ์•ž์„œ๋Š” ์กฐ์ž‘์€ ์ƒ๊ธ‰์ž ์Šน์ธ์„ ๋ฐ›๋Š”๋‹ค.\n", "5. ํ•œ๊ณ„ยทํ™•์žฅ: ๊ฐ€์ƒ ๋งํฌ ๋ชจ๋ธ ๊ธฐ์ค€ ๊ฒฐ๊ณผ๋กœ ์‹ค์ œ ์žฅ๋น„ยท๋ง์—์„œ๋Š” ์žฌ์ธก์ •์ด ํ•„์š”ํ•˜๋‹ค. ์ธ์ ‘ ์ฒด๊ณ„ ์—ฐ๋™์€ ์ „๋ฌธ ์ž…๋ ฅยท์ „๋‹ฌ ๊ฒฐ๊ณผ ์กฐํšŒ API๋กœ ํ™•์žฅํ•œ๋‹ค.\n", "```\n", "
\n" ] } ], "metadata": { "colab": { "provenance": [] }, "kernelspec": { "display_name": "Python 3", "name": "python3" }, "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 0 }