{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# ๐ฏ [C202] ๋์ญํญ ์ ํ ์ ๋ฉ์์ง ์ฐ์ ์์ ํยท์ฌ์ ์ก ํ๋กํ ํ์
\n",
"### Google Colab ์ค์ต ํ๊ฒฝ (์งํํต์ (C2) / Level 2)\n",
"\n",
"[](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",
" # [TODO : ํ์ ์ค์ต 1] ํ๋์ญ ์ ์ ๋ฌด์ ๋ง(VHF/UHF) ๋์ญํญ ์ ์ฝ ์กฐ๊ฑด ๋ถ์ ๋ฐ ์ ์ ๋ฉ์์ง ๊ตฌ์กฐ ์ ์ ๊ด๋ จ ํต์ฌ ๋ก์ง์ ๊ตฌํํ์ธ์.\n",
" # ํํธ: ์๋จ ์ฃผ์ ๋ฐ ๋ฐ์ดํฐ ์คํค๋ง/์ง์นจ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํฉ๋๋ค.\n",
" # <<< ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์ >>>\n",
" pass # ์์ฑ ํ pass๋ฅผ ์ ๊ฑฐํ์ธ์.\n",
"def validate(rec):\n",
" # [TODO : ํ์ ์ค์ต 1] ํ๋์ญ ์ ์ ๋ฌด์ ๋ง(VHF/UHF) ๋์ญํญ ์ ์ฝ ์กฐ๊ฑด ๋ถ์ ๋ฐ ์ ์ ๋ฉ์์ง ๊ตฌ์กฐ ์ ์ ๊ด๋ จ ํต์ฌ ๋ก์ง์ ๊ตฌํํ์ธ์.\n",
" # ํํธ: ์๋จ ์ฃผ์ ๋ฐ ๋ฐ์ดํฐ ์คํค๋ง/์ง์นจ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํฉ๋๋ค.\n",
" # <<< ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์ >>>\n",
" pass # ์์ฑ ํ pass๋ฅผ ์ ๊ฑฐํ์ธ์.\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",
" # [TODO : ํ์ ์ค์ต 2] ์ ์ ๋ฉ์์ง(์ ๋ฌธ) ํจํท ํ์ฑ, ํฌ๊ธฐ ์์ถ ๋ฐ ํค๋ ์ ๋ณด ์ ์ฒ๋ฆฌ ๋ชจ๋ ๊ตฌํ ๊ด๋ จ ํต์ฌ ๋ก์ง์ ๊ตฌํํ์ธ์.\n",
" # ํํธ: ์๋จ ์ฃผ์ ๋ฐ ๋ฐ์ดํฐ ์คํค๋ง/์ง์นจ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํฉ๋๋ค.\n",
" # <<< ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์ >>>\n",
" pass # ์์ฑ ํ pass๋ฅผ ์ ๊ฑฐํ์ธ์.\n",
"def pack_header(r):\n",
" \"\"\"ํค๋ ์ด์ง ์์ถ: ID(2) + ๊ธด๊ธ๋ยท์ ํยท์ก์ ๊ตญ(2) + ์๊ฐ(4, 0.1์ด) + ๊ธธ์ด(2) + CRC(4) = 14bytes.\"\"\"\n",
" # [TODO : ํ์ ์ค์ต 2] ์ ์ ๋ฉ์์ง(์ ๋ฌธ) ํจํท ํ์ฑ, ํฌ๊ธฐ ์์ถ ๋ฐ ํค๋ ์ ๋ณด ์ ์ฒ๋ฆฌ ๋ชจ๋ ๊ตฌํ ๊ด๋ จ ํต์ฌ ๋ก์ง์ ๊ตฌํํ์ธ์.\n",
" # ํํธ: ์๋จ ์ฃผ์ ๋ฐ ๋ฐ์ดํฐ ์คํค๋ง/์ง์นจ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํฉ๋๋ค.\n",
" # <<< ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์ >>>\n",
" pass # ์์ฑ ํ pass๋ฅผ ์ ๊ฑฐํ์ธ์.\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",
" # [TODO : ํ์ ์ค์ต 3] ๋ฉ์์ง ๊ธด๊ธ๋(Flash/Immediate/Priority/Routine) ๊ธฐ๋ฐ 1์ฐจ ์ฐ์ ์์ ํ(Queue) ๊ตฌํ ๊ด๋ จ ํต์ฌ ๋ก์ง์ ๊ตฌํํ์ธ์.\n",
" # ํํธ: ์๋จ ์ฃผ์ ๋ฐ ๋ฐ์ดํฐ ์คํค๋ง/์ง์นจ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํฉ๋๋ค.\n",
" # <<< ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์ >>>\n",
" pass # ์์ฑ ํ pass๋ฅผ ์ ๊ฑฐํ์ธ์.\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",
" # [TODO : ํ์ ์ค์ต 3] ๋ฉ์์ง ๊ธด๊ธ๋(Flash/Immediate/Priority/Routine) ๊ธฐ๋ฐ 1์ฐจ ์ฐ์ ์์ ํ(Queue) ๊ตฌํ ๊ด๋ จ ํต์ฌ ๋ก์ง์ ๊ตฌํํ์ธ์.\n",
" # ํํธ: ์๋จ ์ฃผ์ ๋ฐ ๋ฐ์ดํฐ ์คํค๋ง/์ง์นจ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํฉ๋๋ค.\n",
" # <<< ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์ >>>\n",
" pass # ์์ฑ ํ pass๋ฅผ ์ ๊ฑฐํ์ธ์.\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",
" # [TODO : ํ์ ์ค์ต 4] ์ ์ฅ ์ํฉ ๋ฐ ์ ์ก ์คํจ์จ์ ๋ฐ๋ฅธ ๋์ ํจํท ์ค์ผ์ค๋ง ๋ฐ ์ง๋ฅํ ์ฌ์ ์ก ์๊ณ ๋ฆฌ์ฆ ๊ตฌํ ๊ด๋ จ ํต์ฌ ๋ก์ง์ ๊ตฌํํ์ธ์.\n",
" # ํํธ: ์๋จ ์ฃผ์ ๋ฐ ๋ฐ์ดํฐ ์คํค๋ง/์ง์นจ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํฉ๋๋ค.\n",
" # <<< ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์ >>>\n",
" pass # ์์ฑ ํ pass๋ฅผ ์ ๊ฑฐํ์ธ์.\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",
" # [TODO : ํ์ ์ค์ต 4] ์ ์ฅ ์ํฉ ๋ฐ ์ ์ก ์คํจ์จ์ ๋ฐ๋ฅธ ๋์ ํจํท ์ค์ผ์ค๋ง ๋ฐ ์ง๋ฅํ ์ฌ์ ์ก ์๊ณ ๋ฆฌ์ฆ ๊ตฌํ ๊ด๋ จ ํต์ฌ ๋ก์ง์ ๊ตฌํํ์ธ์.\n",
" # ํํธ: ์๋จ ์ฃผ์ ๋ฐ ๋ฐ์ดํฐ ์คํค๋ง/์ง์นจ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํฉ๋๋ค.\n",
" # <<< ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์ >>>\n",
" pass # ์์ฑ ํ pass๋ฅผ ์ ๊ฑฐํ์ธ์.\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",
" # [TODO : ํ์ ์ค์ต 5] ๋คํธ์ํฌ ํผ์ก๋ ๊ธฐ๋ฐ ์ ์ก ์ฑ๊ณต ํ๋ฅ ๋ฐ ๋งํฌ ์ ๋ขฐ๋ ์ฐ์ถ ์์ง ๊ฐ๋ฐ ๊ด๋ จ ํต์ฌ ๋ก์ง์ ๊ตฌํํ์ธ์.\n",
" # ํํธ: ์๋จ ์ฃผ์ ๋ฐ ๋ฐ์ดํฐ ์คํค๋ง/์ง์นจ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํฉ๋๋ค.\n",
" # <<< ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์ >>>\n",
" pass # ์์ฑ ํ pass๋ฅผ ์ ๊ฑฐํ์ธ์.\n",
"def far_recall(alarm, bad):\n",
" # [TODO : ํ์ ์ค์ต 5] ๋คํธ์ํฌ ํผ์ก๋ ๊ธฐ๋ฐ ์ ์ก ์ฑ๊ณต ํ๋ฅ ๋ฐ ๋งํฌ ์ ๋ขฐ๋ ์ฐ์ถ ์์ง ๊ฐ๋ฐ ๊ด๋ จ ํต์ฌ ๋ก์ง์ ๊ตฌํํ์ธ์.\n",
" # ํํธ: ์๋จ ์ฃผ์ ๋ฐ ๋ฐ์ดํฐ ์คํค๋ง/์ง์นจ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํฉ๋๋ค.\n",
" # <<< ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์ >>>\n",
" pass # ์์ฑ ํ pass๋ฅผ ์ ๊ฑฐํ์ธ์.\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",
" # [TODO : ํ์ ์ค์ต 6] ๋์ญํญ ์ฌ์ฉ๋ฅ , ํ ๋๊ธฐ ํํฉ ๋ฐ ํจํท ์ ์ค๋ฅ ์ค์๊ฐ ๋ชจ๋ํฐ๋ง UI ๊ตฌํ ๊ด๋ จ ํต์ฌ ๋ก์ง์ ๊ตฌํํ์ธ์.\n",
" # ํํธ: ์๋จ ์ฃผ์ ๋ฐ ๋ฐ์ดํฐ ์คํค๋ง/์ง์นจ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํฉ๋๋ค.\n",
" # <<< ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์ >>>\n",
" pass # ์์ฑ ํ pass๋ฅผ ์ ๊ฑฐํ์ธ์.\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",
" # [TODO : ํ์ ์ค์ต 6] ๋์ญํญ ์ฌ์ฉ๋ฅ , ํ ๋๊ธฐ ํํฉ ๋ฐ ํจํท ์ ์ค๋ฅ ์ค์๊ฐ ๋ชจ๋ํฐ๋ง UI ๊ตฌํ ๊ด๋ จ ํต์ฌ ๋ก์ง์ ๊ตฌํํ์ธ์.\n",
" # ํํธ: ์๋จ ์ฃผ์ ๋ฐ ๋ฐ์ดํฐ ์คํค๋ง/์ง์นจ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํฉ๋๋ค.\n",
" # <<< ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์ >>>\n",
" pass # ์์ฑ ํ pass๋ฅผ ์ ๊ฑฐํ์ธ์.\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": [
"# [TODO : ํ์ ์ค์ต 7] ์ต์ฐ์ ์์ ์ ๋ฌธ ๊ฐ์ ์ ์ก ๋ฐ ํ ์์ ์๋ ์กฐ์ ์ ์ํ ํต์ ๊ด HITL ํต์ ๊ธฐ๋ฅ ์ค๊ณ\n",
"# ๋ฏธ์
์ง์นจ ๋ฐ ๊ฒ์ฆ ๊ธฐ์ค์ ํ์ธํ๊ณ ์ฝ๋๋ฅผ ์คํ ๋ฐ ํ๋ํ์ธ์.\n",
"\"\"\"ํต์ ๊ด์ด ์ต์ฐ์ ์ ๋ฌธ ๊ฐ์ ์ ์ก๊ณผ ํ ์์ ์๋ ์กฐ์ ์ ์ง์ ์ํํ๋ 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",
" # [TODO : ํ์ ์ค์ต 8] ๋์ญํญ 90% ๊ธ๊ฐ ๋ฐ ๋๋ ์ ๋ฌธ ํญ์ฃผ ์ํฉ ๋ชจ์ ์ ์ก ์คํธ๋ ์ค ์๋ฎฌ๋ ์ด์
๊ด๋ จ ํต์ฌ ๋ก์ง์ ๊ตฌํํ์ธ์.\n",
" # ํํธ: ์๋จ ์ฃผ์ ๋ฐ ๋ฐ์ดํฐ ์คํค๋ง/์ง์นจ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํฉ๋๋ค.\n",
" # <<< ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์ >>>\n",
" pass # ์์ฑ ํ pass๋ฅผ ์ ๊ฑฐํ์ธ์.\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",
" # [TODO : ํ์ ์ค์ต 8] ๋์ญํญ 90% ๊ธ๊ฐ ๋ฐ ๋๋ ์ ๋ฌธ ํญ์ฃผ ์ํฉ ๋ชจ์ ์ ์ก ์คํธ๋ ์ค ์๋ฎฌ๋ ์ด์
๊ด๋ จ ํต์ฌ ๋ก์ง์ ๊ตฌํํ์ธ์.\n",
" # ํํธ: ์๋จ ์ฃผ์ ๋ฐ ๋ฐ์ดํฐ ์คํค๋ง/์ง์นจ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํฉ๋๋ค.\n",
" # <<< ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์ >>>\n",
" pass # ์์ฑ ํ pass๋ฅผ ์ ๊ฑฐํ์ธ์.\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",
" # [TODO : ํ์ ์ค์ต 9] AAR ๊ธฐ๋ฐ ์ค์ ์ ๋ฌธ ์ ์ก ์ง์ฐ ์ฌ๋ก ๋ถ์ ๋ฐ ํ ์ฐ์ ์์ ๊ฐ์ค์น ์ฌ์กฐ์ ๊ด๋ จ ํต์ฌ ๋ก์ง์ ๊ตฌํํ์ธ์.\n",
" # ํํธ: ์๋จ ์ฃผ์ ๋ฐ ๋ฐ์ดํฐ ์คํค๋ง/์ง์นจ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํฉ๋๋ค.\n",
" # <<< ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์ >>>\n",
" pass # ์์ฑ ํ pass๋ฅผ ์ ๊ฑฐํ์ธ์.\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",
" # [TODO : ํ์ ์ค์ต 9] AAR ๊ธฐ๋ฐ ์ค์ ์ ๋ฌธ ์ ์ก ์ง์ฐ ์ฌ๋ก ๋ถ์ ๋ฐ ํ ์ฐ์ ์์ ๊ฐ์ค์น ์ฌ์กฐ์ ๊ด๋ จ ํต์ฌ ๋ก์ง์ ๊ตฌํํ์ธ์.\n",
" # ํํธ: ์๋จ ์ฃผ์ ๋ฐ ๋ฐ์ดํฐ ์คํค๋ง/์ง์นจ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํฉ๋๋ค.\n",
" # <<< ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์ >>>\n",
" pass # ์์ฑ ํ pass๋ฅผ ์ ๊ฑฐํ์ธ์.\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",
" # [TODO : ํ์ ์ค์ต 10] ๋์ญํญ ์ ํ ํ๊ฒฝ ๋ฉ์์ง ํยท์ฌ์ ์ก ์์คํ
ํตํฉ ๋ฐ ์ ์ ํต์ ๋ธ๋ฆฌํ ํจํค์ง ์์ฑ ๊ด๋ จ ํต์ฌ ๋ก์ง์ ๊ตฌํํ์ธ์.\n",
" # ํํธ: ์๋จ ์ฃผ์ ๋ฐ ๋ฐ์ดํฐ ์คํค๋ง/์ง์นจ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํฉ๋๋ค.\n",
" # <<< ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์ >>>\n",
" pass # ์์ฑ ํ pass๋ฅผ ์ ๊ฑฐํ์ธ์.\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",
" # [TODO : ํ์ ์ค์ต 10] ๋์ญํญ ์ ํ ํ๊ฒฝ ๋ฉ์์ง ํยท์ฌ์ ์ก ์์คํ
ํตํฉ ๋ฐ ์ ์ ํต์ ๋ธ๋ฆฌํ ํจํค์ง ์์ฑ ๊ด๋ จ ํต์ฌ ๋ก์ง์ ๊ตฌํํ์ธ์.\n",
" # ํํธ: ์๋จ ์ฃผ์ ๋ฐ ๋ฐ์ดํฐ ์คํค๋ง/์ง์นจ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํฉ๋๋ค.\n",
" # <<< ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์ >>>\n",
" pass # ์์ฑ ํ pass๋ฅผ ์ ๊ฑฐํ์ธ์.\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
}