신호생성 repo (24. 1. 5 ~).
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

1186 lines
84 KiB

{
"cells": [
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"import os\n",
"import sumolib\n",
"import copy\n",
"import json\n",
"from tqdm import tqdm\n",
"from datetime import datetime"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"m = 105\n",
"midnight = int(datetime(2024, 1, 5, 0, 0, 0).timestamp())\n",
"next_day = int(datetime(2024, 1, 6, 0, 0, 0).timestamp())\n",
"fmins = range(midnight, next_day, 300)\n",
"\n",
"# 현재시각\n",
"present_time = fmins[m]\n",
"sim_start = fmins[m] - 300\n",
"# network and dataframes\n",
"net = sumolib.net.readNet('../Data/networks/sn.net.xml')\n",
"inter_node = pd.read_csv('../Data/tables/inter_node.csv', index_col=0)\n",
"plan = pd.read_csv('../Data/tables/plan.csv', index_col=0)\n",
"match6 = pd.read_csv('../Intermediates/match6.csv', index_col=0)\n",
"match6 = match6[['node_id', 'phase_no', 'ring_type', 'inc_edge', 'out_edge']].reset_index(drop=True)\n",
"histid = pd.read_csv(f'../Intermediates/histid/histid_{present_time}.csv', index_col=0)\n",
"histid = histid.reset_index(drop=True).drop(columns=['inter_no'])\n",
"\n",
"# helper dictionaries and lists\n",
"inter_node_p = inter_node[inter_node.inter_type=='parent']\n",
"inter2node = dict(zip(inter_node_p['inter_no'], inter_node_p['node_id']))\n",
"node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no']))\n",
"pa2ch = {'i0':['u00'], 'i1':[], 'i2':['u20'], 'i3':['c30', 'u30', 'u31', 'u32'], 'i6':['u60'], 'i7':[], 'i8':[], 'i9':[]}\n",
"node_ids = sorted(inter_node.node_id.unique())\n",
"parent_ids = sorted(inter_node[inter_node.inter_type=='parent'].node_id.unique())\n",
"nodes = [net.getNode(node_id) for node_id in node_ids]"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"def attach_children(histid, match6, parent_ids, pa2ch):\n",
" '''\n",
" 자식교차로에 대한 진입·진출 엣지 정보를 붙여주는 함수\n",
"\n",
" input :\n",
" (1) histid\n",
" - 각 교차로에 대한 (시작유닉스, A현시, B현시)별 현시시간, 진입·진출엣지\n",
" - 부모교차로(주교차로)에 대해서만 값이 지정되어 있음\n",
" (2) match6\n",
" - (현시, 링)별 진입·진출엣지\n",
" - 자식교차로(유턴 및 연동교차로)에 대해서도 값이 지정되어 있음\n",
" (3) parent_ids : 부모교차로 목록\n",
" (4) pa2ch : 각 부모교차로id를 부모교차로가 포함하고 있는 자식교차로들의 id들의 리스트로 대응시키는 딕셔너리\n",
"\n",
" output : histids\n",
" - 모든(부모 및 자식) 교차로에 대한 시작유닉스 (시작유닉스, A현시, B현시)별 현시시간, 진입·진출엣지\n",
" '''\n",
" new_histids = []\n",
" for parent_id in parent_ids:\n",
" for child_id in pa2ch[parent_id]:\n",
" new_histid = histid.copy()[histid.node_id==parent_id]\n",
" new_histid[['inc_edge_A', 'out_edge_A', 'inc_edge_B', 'out_edge_B']] = np.nan\n",
" for i, row in new_histid.iterrows():\n",
" phas_A = row.phas_A\n",
" phas_B = row.phas_B\n",
" new_match = match6[match6.node_id==child_id]\n",
" Arow = new_match[(new_match.phase_no==phas_A) & (new_match.ring_type=='A')]\n",
" if ~ Arow[['inc_edge', 'out_edge']].isna().all().all():\n",
" inc_edge = Arow.iloc[0].inc_edge\n",
" out_edge = Arow.iloc[0].out_edge\n",
" new_histid.loc[i, ['inc_edge_A', 'out_edge_A']] = [inc_edge, out_edge]\n",
" Brow = new_match[(new_match.phase_no==phas_B) & (new_match.ring_type=='B')]\n",
" if ~ Brow[['inc_edge', 'out_edge']].isna().all().all():\n",
" inc_edge = Brow.iloc[0].inc_edge\n",
" out_edge = Brow.iloc[0].out_edge\n",
" new_histid.loc[i, ['inc_edge_B', 'out_edge_B']] = [inc_edge, out_edge]\n",
" new_histid.loc[i, 'node_id'] = child_id\n",
" new_histids.append(new_histid)\n",
" new_histids = pd.concat(new_histids)\n",
" histids = pd.concat([histid.copy(), new_histids])\n",
" histids = histids.sort_values(by=['start_unix', 'node_id', 'phas_A', 'phas_B']).reset_index(drop=True)\n",
" return histids\n",
"histids = attach_children(histid, match6, parent_ids, pa2ch)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def initialize_states(net, nodes, histids):\n",
" '''\n",
" 신호 초기화\n",
"\n",
" input :\n",
" (1) net : 네트워크\n",
" (2) nodes : 노드 목록\n",
" (3) histids : 모든 교차로에 대한 시작유닉스 (시작유닉스, A현시, B현시)별 현시시간, 진입·진출엣지\n",
"\n",
" output : node2init\n",
" - 각 노드를 초기화된 신호로 맵핑하는 딕셔너리\n",
" - 초기화된 신호란, 우회전을 g로 나머지는 r로 지정한 신호를 말함.\n",
" '''\n",
" node2init = {}\n",
" for node in nodes:\n",
" node_id = node.getID()\n",
" conns = [(c.getJunctionIndex(), c) for c in node.getConnections()]\n",
" conns = [c for c in conns if c[0] >= 0]\n",
" conns = sorted(conns, key=lambda x: x[0])\n",
" state = []\n",
" for i, ci in conns:\n",
" if ci.getTLLinkIndex() < 0:\n",
" continue\n",
" are_foes = False\n",
" for j, cj in conns:\n",
" if ci.getTo() == cj.getTo():\n",
" continue\n",
" if node.areFoes(i, j):\n",
" are_foes = True\n",
" break\n",
" state.append('r' if are_foes else 'g')\n",
" node2init[node_id] = state\n",
"\n",
" # 어떤 연결과도 상충이 일어나지는 않지만, 신호가 부여되어 있는 경우에는 r을 부여\n",
" for _, row in histids.iterrows():\n",
" node_id = row['node_id']\n",
" inc_edge_A = row.inc_edge_A\n",
" inc_edge_B = row.inc_edge_B\n",
" out_edge_A = row.out_edge_A\n",
" out_edge_B = row.out_edge_B\n",
"\n",
" if pd.isna(inc_edge_A) or pd.isna(out_edge_A):\n",
" pass\n",
" else:\n",
" inc_edge_A = net.getEdge(inc_edge_A)\n",
" out_edge_A = net.getEdge(out_edge_A)\n",
" for conn in inc_edge_A.getConnections(out_edge_A):\n",
" index = conn.getTLLinkIndex()\n",
" if index >= 0:\n",
" node2init[node_id][index] = 'r'\n",
"\n",
" if pd.isna(inc_edge_B) or pd.isna(out_edge_B):\n",
" pass\n",
" else:\n",
" inc_edge_B = net.getEdge(inc_edge_B)\n",
" out_edge_B = net.getEdge(out_edge_B)\n",
" for conn in inc_edge_B.getConnections(out_edge_B):\n",
" index = conn.getTLLinkIndex()\n",
" if index >= 0:\n",
" node2init[node_id][index] = 'r'\n",
" return node2init\n",
"node2init = initialize_states(net, nodes, histids)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"def assign_signals(histids, node2init, net):\n",
" '''\n",
" 진입·진출엣지를 신호문자열로 배정\n",
"\n",
" input :\n",
" (1) histids : 모든 교차로에 대한 (시작유닉스, A현시, B현시)별 현시시간, 진입·진출엣지\n",
" (2) node2init : 각 노드를 초기화된 신호로 맵핑하는 딕셔너리\n",
" (3) net : 네트워크\n",
"\n",
" output : sigtable\n",
" - 모든 교차로에 대한 (시작유닉스, A현시, B현시)별 현시시간, 신호문자열\n",
" - 황색 및 적색신호는 아직 반영되지 않았음.\n",
" '''\n",
" sigtable = histids.copy()\n",
" sigtable['init_state'] = sigtable['node_id'].map(node2init)\n",
" sigtable['state'] = sigtable['init_state'].map(lambda x:''.join(x))\n",
" for i, row in sigtable.iterrows():\n",
" node_id = row.node_id\n",
" inc_edge_A = row.inc_edge_A\n",
" inc_edge_B = row.inc_edge_B\n",
" out_edge_A = row.out_edge_A\n",
" out_edge_B = row.out_edge_B\n",
" state = copy.deepcopy(node2init)[node_id]\n",
" if pd.isna(inc_edge_A) or pd.isna(out_edge_A):\n",
" pass\n",
" else:\n",
" inc_edge_A = net.getEdge(inc_edge_A)\n",
" out_edge_A = net.getEdge(out_edge_A)\n",
" for conn in inc_edge_A.getConnections(out_edge_A):\n",
" index = conn.getTLLinkIndex()\n",
" if index >= 0:\n",
" state[index] = 'G'\n",
" sigtable.at[i, 'state'] = ''.join(state)\n",
"\n",
" if pd.isna(inc_edge_B) or pd.isna(out_edge_B):\n",
" pass\n",
" else:\n",
" inc_edge_B = net.getEdge(inc_edge_B)\n",
" out_edge_B = net.getEdge(out_edge_B)\n",
" for conn in inc_edge_B.getConnections(out_edge_B):\n",
" index = conn.getTLLinkIndex()\n",
" if index >= 0:\n",
" state[index] = 'G'\n",
" sigtable.at[i, 'state'] = ''.join(state)\n",
" sigtable = sigtable.dropna(subset='state')\n",
" sigtable = sigtable.reset_index(drop=True)\n",
" sigtable['phase_sumo'] = sigtable.groupby(['node_id', 'start_unix']).cumcount()\n",
" sigtable = sigtable[['node_id', 'start_unix', 'phase_sumo', 'duration', 'state']]\n",
" sigtable = sigtable.sort_values(by=['start_unix', 'node_id'])\n",
" sigtable['start_dt'] = sigtable['start_unix'].apply(lambda x:datetime.fromtimestamp(x))\n",
" return sigtable\n",
"sigtable = assign_signals(histids, node2init, net)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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Y+KV37vvvqtBgwYpGAyqV69emj9/vtO757mW8t2zZ4/+3//7f+rTp4+ysrLUrVs33X777aqqqmr0OuTrAYPj9tprr5lf/vKX5qWXXjKSzIIFC6KOe+mll8yAAQNMcXGxefjhhxutGz16tBkwYIBZuXKlWbZsmenVq5cZN25cZH1VVZUpLCw048ePNxs3bjR/+ctfTFZWlpkzZ46De+YfLWX86aefmvz8fDNlyhSzbt068+mnn5q///3vZteuXZExN998sykpKTHvvPOOWbt2rTn77LPNsGHDIuuPHDli+vfvb0aMGGHWr19vXnvtNdOxY0czdepUt3bTMy3le+ONN5qePXuaJUuWmK1bt5o5c+aY9PR08/e//z0yhnytjRo1ysybN89s3LjRlJWVmR/+8IemW7duprq6OjImHvl9/vnnJjs720yePNls2rTJ/PGPfzTp6elm0aJFru6v21rKd8OGDebHP/6xWbhwofn000/NO++8Y3r37m2uvPLKyGuQrzdoPuLEqvn4xz/+Ybp06WI2btxounfv3qj52LRpk5Fk1qxZE1n2+uuvm0AgYHbs2GGMMeaxxx4z7du3NzU1NZExd955p+nTp49j++JX0TK+6qqrzDXXXGP5nMrKStOqVSvz4osvRpZ9/PHHRpJZsWKFMab+AJyWlmbKy8sjYx5//HGTk5PTKPdkFy3ffv36mfvvv7/RskGDBplf/vKXxhjyjVVFRYWRZN577z1jTPzy+8UvfmH69evXaFtXXXWVGTVqlNO75CtN843mhRdeMJmZmaa2ttYYQ75e4bKLg8LhsK699lpNmTJF/fr1O2b9ihUrlJeXp8GDB0eWjRgxQmlpaVq1alVkzHnnnafMzMzImFGjRmnz5s3au3ev8zvhY+FwWK+++qpOPvlkjRo1SgUFBRoyZEijSwelpaWqra3ViBEjIstOOeUUdevWTStWrJBUn/Fpp52mwsLCyJhRo0YpFArpo48+cm1//GjYsGFauHChduzYIWOMlixZok8++UQjR46URL6xajjdn5+fLyl++a1YsaLRazSMaXiNVNE0X6sxOTk5ysio/2kz8vUGzYeDZs6cqYyMDN1+++1R15eXl6ugoKDRsoyMDOXn56u8vDwy5uh/FJIijxvGpKqKigpVV1drxowZGj16tN58801dccUV+vGPf6z33ntPUn1GmZmZysvLa/TcwsJCMrbhj3/8o/r27auuXbsqMzNTo0eP1uzZs3XeeedJIt9YhMNhTZo0ScOHD1f//v0lxS8/qzGhUEgHDx50Ynd8J1q+TX399df6zW9+o5tuuimyjHy94btftU0WpaWlevTRR7Vu3ToFAgGvy0lK4XBYknT55ZfrP/7jPyRJp59+upYvX64nnnhC559/vpflJYU//vGPWrlypRYuXKju3btr6dKluu2221RcXHzM/xNE82677TZt3LhR77//vtelJKWW8g2FQrrkkkvUt29f3Xfffe4Wh2Nw5sMhy5YtU0VFhbp166aMjAxlZGRo27ZtuuOOO3TSSSdJkoqKilRRUdHoeUeOHNGePXtUVFQUGdP0k+8NjxvGpKqOHTsqIyNDffv2bbT81FNPjdztUlRUpMOHD6uysrLRmF27dpFxCw4ePKi7775bs2bN0qWXXqrvf//7mjhxoq666ir9/ve/l0S+dk2cOFGvvPKKlixZoq5du0aWxys/qzE5OTnKysqK9+74jlW+Dfbt26fRo0erXbt2WrBggVq1ahVZR77eoPlwyLXXXqsPP/xQZWVlkb/i4mJNmTJFb7zxhiRp6NChqqysVGlpaeR5ixcvVjgc1pAhQyJjli5dqtra2siYt956S3369FH79u3d3SmfyczM1JlnnnnMrYuffPKJunfvLkk644wz1KpVK73zzjuR9Zs3b9b27ds1dOhQSfUZb9iwoVEj+NZbbyknJ+eYxiaV1NbWqra2Vmlpjf8zkZ6eHjnrRL7NM8Zo4sSJWrBggRYvXqwePXo0Wh+v/IYOHdroNRrGNLxGsmopX6n+jMfIkSOVmZmphQsXqnXr1o3Wk69HPP7Aa0Lbt2+fWb9+vVm/fr2RZGbNmmXWr19vtm3bFnV807tdjKm/1XbgwIFm1apV5v333ze9e/dudKttZWWlKSwsNNdee63ZuHGjef755012dnbK3GrbUsYvvfSSadWqlZk7d67ZsmVL5Ba4ZcuWRV7j5ptvNt26dTOLFy82a9euNUOHDjVDhw6NrG+41W7kyJGmrKzMLFq0yHTq1CklbgVtKd/zzz/f9OvXzyxZssR8/vnnZt68eaZ169bmsccei7wG+Vq75ZZbTG5urnn33XfNzp07I38HDhyIjIlHfg23gk6ZMsV8/PHHZvbs2SlxK2hL+VZVVZkhQ4aY0047zXz66aeNxhw5csQYQ75eofk4AUuWLDGSjvmbMGFC1PHRmo9vvvnGjBs3zrRt29bk5OSY66+/3uzbt6/RmA8++MCcc845JhgMmi5dupgZM2Y4tEf+YyfjP/3pT6ZXr16mdevWZsCAAeZvf/tbo9c4ePCgufXWW0379u1Ndna2ueKKK8zOnTsbjfniiy/MD37wA5OVlWU6duxo7rjjjsiteMmspXx37txprrvuOlNcXGxat25t+vTpY/7whz+YcDgceQ3ytRYtW0lm3rx5kTHxym/JkiXm9NNPN5mZmeZ73/teo20kq5bytZrfkszWrVsjr0O+7gsYY4yz51YAAAC+w2c+AACAq2g+AACAq2g+AACAq2g+AACAq2g+AACAq2g+AACAq2g+AACAq2g+AACw4YEHHtCwYcOUnZ19zC8RWwkEAlH/HnroociYPXv2aPz48crJyVFeXp5uuOEGVVdXR329Tz/9VO3atWt2+88//7wCgYDGjBnTaPlLL72kkSNHqkOHDgoEAiorK7O1D03Nnj1bp556qrKystSnTx/9+c9/jvk1aD4AAPjWBRdcoPnz50ddd/jwYf30pz/VLbfcYvv1du7c2ejv6aefViAQ0JVXXhkZM378eH300Ud666239Morr2jp0qW66aabjnmt2tpajRs3Tueee67l9r744gv9/Oc/jzpm//79OuecczRz5kzb9Tf1+OOPa+rUqbrvvvv00Ucfadq0abrtttv08ssvx/ZCXn/FKgAAfnH++ee3+NXp8+bNM7m5ucf1+pdffrm56KKLIo83bdpkJJk1a9ZElr3++usmEAiYHTt2NHruL37xC3PNNddYbv/IkSNm2LBh5qmnnjITJkwwl19+edQatm7daiSZ9evXH7Nu79695oYbbjAdO3Y07dq1MxdeeKEpKyuLrB86dKj5+c9/3ug5kydPNsOHD7ex99/hzAcAAC7YtWuXXn31Vd1www2RZStWrFBeXp4GDx4cWTZixAilpaVp1apVkWWLFy/Wiy++qNmzZ1u+/v3336+CgoJGrx+rn/70p6qoqNDrr7+u0tJSDRo0SBdffLH27NkjSaqpqTnml4GzsrK0evXqRr++3hKaDwAAXPDMM8+oXbt2+vGPfxxZVl5eroKCgkbjMjIylJ+fr/LycknSN998o+uuu07z589XTk5O1Nd+//339ac//UlPPvnkcdf3/vvva/Xq1XrxxRc1ePBg9e7dW7///e+Vl5en//mf/5EkjRo1Sk899ZRKS0tljNHatWv11FNPqba2Vl9//bXtbdF8AABS1oMPPqi2bdtG/pYtW6abb7650bLt27fHZVtPP/20xo8ff8yZg5bceOON+ud//medd955Udfv27dP1157rZ588kl17NjxuOv74IMPVF1drQ4dOjTa/61bt+qzzz6TJP3qV7/SD37wA5199tlq1aqVLr/8ck2YMEGSlJZmv6XIOO4qAQBIcDfffLPGjh0beTx+/HhdeeWVjc5OFBcXn/B2li1bps2bN+uvf/1ro+VFRUWqqKhotOzIkSPas2ePioqKJNVfclm4cKF+//vfS5KMMQqHw8rIyNDcuXM1aNAgffHFF7r00ksjrxEOhyXVn0XZvHmzevbs2WKN1dXV6ty5s959991j1jXcXZOVlaWnn35ac+bM0a5du9S5c2fNnTtX7dq1U6dOnWznQfMBAEhZ+fn5ys/PjzzOyspSQUGBevXqFdft/OlPf9IZZ5yhAQMGNFo+dOhQVVZWqrS0VGeccYak+mYjHA5ryJAhkuo/F1JXVxd5zt///nfNnDlTy5cvV5cuXZSVlaUNGzY0et177rlH+/bt06OPPqqSkhJbNQ4aNEjl5eXKyMjQSSed1OzYVq1aqWvXrpLqb+390Y9+xJkPAADibfv27dqzZ4+2b9+uurq6yPdk9OrVS23btpUknXLKKZo+fbquuOKKyPNCoZBefPFF/eEPfzjmNU899VSNHj1aN954o5544gnV1tZq4sSJuvrqqyNnXE499dRGz1m7dq3S0tLUv3//yLKj/7f03ZmKo5c31P7VV19JkjZv3iyp/uxLUVGRRowYoaFDh2rMmDH63e9+p5NPPllfffWVXn31VV1xxRUaPHiwPvnkE61evVpDhgzR3r17NWvWLG3cuFHPPPNMTFnymQ8AAGz49a9/rYEDB+ree+9VdXW1Bg4cqIEDB2rt2rWRMZs3b1ZVVVWj5z3//PMyxmjcuHFRX/e5557TKaecoosvvlg//OEPdc4552ju3Llxr3/hwoUaOHCgLrnkEknS1VdfrYEDB+qJJ56QVP+FaK+99prOO+88XX/99Tr55JN19dVXa9u2bSosLJQk1dXV6Q9/+IMGDBigf/qnf9KhQ4e0fPnyFs+UNBUwxpi47h0AAEAzOPMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABc9f8BT+V5YpO7bx8AAAAASUVORK5CYII=",
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"k = 0\n",
"for node_id, group in sigtable.groupby('node_id'):\n",
" k += 1\n",
" plt.plot(group.start_unix.unique(), [k] * len(group.start_unix.unique()), marker='o')\n",
" plt.axvline(present_time - 300, c='r', linewidth=.5)\n",
" plt.axvline(present_time, c='r', linewidth=.5)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'c30': 4,\n",
" 'i0': 3,\n",
" 'i1': 4,\n",
" 'i2': 4,\n",
" 'i3': 4,\n",
" 'i6': 4,\n",
" 'i7': 4,\n",
" 'i8': 4,\n",
" 'i9': 4,\n",
" 'u00': 3,\n",
" 'u20': 4,\n",
" 'u30': 4,\n",
" 'u31': 4,\n",
" 'u32': 4,\n",
" 'u60': 4}"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# node2num_cycles : A dictionary that maps a node_id to the number of cycles\n",
"def get_node2num_cycles(plan, node_ids):\n",
" Aplan = plan.copy()[['inter_no'] + [f'dura_A{j}' for j in range(1,9)] + ['cycle']]\n",
" grouped = Aplan.groupby('inter_no')\n",
" df = grouped.agg({'cycle': 'min'}).reset_index()\n",
" df = df.rename(columns={'cycle': 'min_cycle'})\n",
" df['num_cycle'] = 300 // df['min_cycle'] + 2\n",
" inter2num_cycles = dict(zip(df['inter_no'], df['num_cycle']))\n",
" node2numcycles = {node_id : inter2num_cycles[node2inter[node_id]] for node_id in node_ids}\n",
" with open('../Intermediates/node2numcycles.json', 'w') as file:\n",
" json.dump(node2numcycles, file, indent=4)\n",
" return node2numcycles\n",
"node2num_cycles = get_node2num_cycles(plan, node_ids)\n",
"node2num_cycles"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"def set_timepoints(sigtable, node2num_cycles, present_time):\n",
" offsets = {}\n",
" Sigtable = []\n",
" sim_start = present_time - 300\n",
" for node_id, group in sigtable.groupby('node_id'):\n",
" lsbs = group[group['start_unix'] < sim_start]['start_unix'].max() # the last start_unix before sim_start\n",
" offsets[node_id] = lsbs - sim_start\n",
" group = group[group.start_unix >= lsbs]\n",
" start_unixes = np.array(group.start_unix)\n",
" start_unixes = np.sort(np.unique(start_unixes))[:node2num_cycles[node_id]]\n",
" group = group[group.start_unix.isin(start_unixes)]\n",
" Sigtable.append(group)\n",
" Sigtable = pd.concat(Sigtable)\n",
" return Sigtable, offsets\n",
"Sigtable, offsets = set_timepoints(sigtable, node2num_cycles, present_time)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'c30': 4,\n",
" 'i0': 3,\n",
" 'i1': 4,\n",
" 'i2': 4,\n",
" 'i3': 4,\n",
" 'i6': 4,\n",
" 'i7': 4,\n",
" 'i8': 4,\n",
" 'i9': 4,\n",
" 'u00': 3,\n",
" 'u20': 4,\n",
" 'u30': 4,\n",
" 'u31': 4,\n",
" 'u32': 4,\n",
" 'u60': 4}"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"node2num_cycles"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1 c30 4\n",
"2 i0 3\n",
"3 i1 4\n",
"4 i2 4\n",
"5 i3 4\n",
"6 i6 4\n",
"7 i7 4\n",
"8 i8 4\n",
"9 i9 4\n",
"10 u00 3\n",
"11 u20 4\n",
"12 u30 4\n",
"13 u31 4\n",
"14 u32 4\n",
"15 u60 4\n"
]
},
{
"data": {
"image/png": "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8GVf1GLfvn1mw4YNZsOGDUaSmTlzptmwYYPZvn272bdvn7njjjvMypUrzbZt28yHH35oBg4caHr16mUOHDgQWUdeXp4ZMGCAWb16tfn4449Nr169zLXXXhu5v7S01GRlZZnrr7/ebNy40bz22msmPT3dzJkzJxEvOWZ1bRtjjHnzzTdNixYtzNy5c83WrVvN008/bVJTU83y5csj67j55pvNCSecYBYtWmTWrVtnhg4daoYOHRq5/9ChQ6Zfv35m5MiRpqCgwCxcuNB07tzZTJ06tdlfb0PVt32MMaasrMykp6eb2bNn17oOr753zj//fHPaaaeZxYsXm6+//tq89NJLplWrVuaZZ56JrMPL753XX3/dLF682Hz11Vfmr3/9q+nevbu54oorotbh1u1zyy23mGAwaJYsWWIKCwsjfxUVFZE2Trz2r7/+2qSnp5s777zTfPHFF2bWrFkmNTXVLFy4sFlfb0PFsn0KCwvNhg0bzHPPPWckmWXLlpkNGzaY7777LtKmOfY9FChNdPg0rKP/xo8fbyoqKszIkSNN586dTYsWLUz37t3NjTfeGHXqmjHGfPfdd+baa681bdu2NYFAwPziF78w+/bti2rzySefmHPOOcf4/X7TtWtXM3369OZ8mY1S17Y57IUXXjAnn3yyadWqlenfv7/561//GrWO77//3tx6662mffv2Jj093Vx++eWmsLAwqs0333xjLrnkEtO6dWvTqVMn85vf/MZUVlY2x0tskli2z5w5c0zr1q1NaWlprevw6nunsLDQTJgwweTk5JhWrVqZ3r17myeeeMJUV1dH1uHl985TTz1lunXrZlq0aGFOOOEEc++99x5zarBbt09t20WSeemllyJtnHrtixcvNmeeeaZp2bKlOemkk6Kew1axbJ8HHnig3jbNse/x/dBhAAAAa/AdFAAAYB0KFAAAYB0KFAAAYB0KFAAAYB0KFAAAYB0KFAAAYB0KFAAAYB0KFAAAYvDII49o2LBhSk9PP+bXkI/H5/PV+vf4449H2uzZs0fjxo1TIBBQRkaGbrjhBpWXl9e6vi+//FLt2rWr8/lfe+01+Xw+jR07Nmr5m2++qZEjR6pjx47y+XwqKCiI6TUcbdasWTr11FPVunVr9e7dW3/84x8btZ76UKAAAPCDCy64QPPmzav1voMHD+rnP/+5brnllpjXV1hYGPX34osvyufz6corr4y0GTdunD7//HN98MEHevvtt7Vs2TLddNNNx6yrsrJS1157rc4999zjPt8333yjO+64o9Y2+/fv1znnnKMZM2bE3P+jzZ49W1OnTtWDDz6ozz//XA899JAmTpyot956q9HrPK7GXzAXAAB3Of/88+u9ZP1LL71kgsFgo9Y/ZswYc9FFF0Vub9q0yUgya9eujSx79913jc/nMzt37ox67G9/+1tz3XXXHff5Dx06ZIYNG2aef/55M378eDNmzJha+7Bt2zYjyWzYsOGY+/bu3WtuuOEG06lTJ9OuXTtz4YUXmoKCgsj9Q4cONXfccUfUY6ZMmWKGDx8ew6tvGI6gAADQDIqLi/XOO+/ohhtuiCxbuXKlMjIyNHjw4MiyESNGKCUlRatXr44sW7Rokd544w3NmjXruOt/+OGHlZmZGbX+hvr5z3+ukpISvfvuu8rPz9fAgQN18cUXa8+ePZKkcDisVq1aRT2mdevWWrNmTdQvFzuBAgUAgGbw8ssvq127drriiisiy4qKipSZmRnVLi0tTR06dFBRUZEk6bvvvtOECRM0b948BQKBWtf98ccf64UXXtBzzz3X6P59/PHHWrNmjd544w0NHjxYvXr10u9//3tlZGTof/7nfyRJo0aN0vPPP6/8/HwZY7Ru3To9//zzqqys1D//+c9GP3dtKFAAAJ716KOPqm3btpG/5cuX6+abb45atmPHDkee68UXX9S4ceOOOQJRnxtvvFH/9m//pvPOO6/W+/ft26frr79ezz33nDp16tTo/n3yyScqLy9Xx44do17/tm3b9NVXX0mS7rvvPl1yySX6yU9+ohYtWmjMmDEaP368JCklxdmSIs3RtQEAkERuvvlmXXXVVZHb48aN05VXXhl1lCMnJ6fJz7N8+XJt3rxZf/nLX6KWZ2dnq6SkJGrZoUOHtGfPHmVnZ0uqmd5ZsGCBfv/730uSjDGqrq5WWlqa5s6dq4EDB+qbb77RpZdeGllHdXW1pJqjMZs3b1bPnj3r7WN5ebm6dOmiJUuWHHPf4bOGWrdurRdffFFz5sxRcXGxunTporlz56pdu3bq3LlzzNsjFhQoAADP6tChgzp06BC53bp1a2VmZurkk0929HleeOEFDRo0SP37949aPnToUJWWlio/P1+DBg2SVFOQVFdXa8iQIZJqvqdSVVUVeczf/vY3zZgxQytWrFDXrl3VunVrffbZZ1Hrvffee7Vv3z499dRTys3NjamPAwcOVFFRkdLS0nTiiSfW2bZFixbq1q2bpJrTmn/2s59xBAUAgETYsWOH9uzZox07dqiqqipyHZGTTz5Zbdu2lST16dNH06ZN0+WXXx55XCgU0htvvKEnnnjimHWeeuqpysvL04033qhnn31WlZWVmjRpkq655prIkZtTTz016jHr1q1TSkqK+vXrF1l25L+lH494HLn8cN937dolSdq8ebOkmqM42dnZGjFihIYOHaqxY8fqscce0ymnnKJdu3bpnXfe0eWXX67Bgwdry5YtWrNmjYYMGaK9e/dq5syZ2rhxo15++eXGbNI68R0UAABicP/992vAgAF64IEHVF5ergEDBmjAgAFat25dpM3mzZtVVlYW9bjXXntNxhhde+21ta731VdfVZ8+fXTxxRfrX/7lX3TOOedo7ty5jvd/wYIFGjBggEaPHi1JuuaaazRgwAA9++yzkmouKvf3v/9d5513nn7xi1/olFNO0TXXXKPt27crKytLklRVVaUnnnhC/fv3109/+lMdOHBAK1asqPeIS2P4jDHG8bUCAAA0AUdQAACAdShQAACAdShQAACAdShQAACAdShQAACAdShQAACAdShQAACAdShQAACAdShQAACAdShQAACAdShQAACAdShQAACAdf5/efe8nZG0xXQAAAAASUVORK5CYII=",
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"k = 0\n",
"for node_id, group in Sigtable.groupby('node_id'):\n",
" k += 1\n",
" print(k, node_id, node2num_cycles[node_id])\n",
" plt.plot(group.start_unix.unique(), [k] * len(group.start_unix.unique()), marker='o')\n",
" plt.axvline(present_time - 300, c='r', linewidth=.5)\n",
" plt.axvline(present_time, c='r', linewidth=.5)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>node_id</th>\n",
" <th>start_unix</th>\n",
" <th>phase_sumo</th>\n",
" <th>duration</th>\n",
" <th>state</th>\n",
" <th>start_dt</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>i7</td>\n",
" <td>1704411320</td>\n",
" <td>0</td>\n",
" <td>44</td>\n",
" <td>GGrggGG</td>\n",
" <td>2024-01-05 08:35:20</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>i7</td>\n",
" <td>1704411320</td>\n",
" <td>1</td>\n",
" <td>44</td>\n",
" <td>rrrggrr</td>\n",
" <td>2024-01-05 08:35:20</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>i7</td>\n",
" <td>1704411320</td>\n",
" <td>2</td>\n",
" <td>26</td>\n",
" <td>GGrggGG</td>\n",
" <td>2024-01-05 08:35:20</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>i7</td>\n",
" <td>1704411320</td>\n",
" <td>3</td>\n",
" <td>26</td>\n",
" <td>rrrggrr</td>\n",
" <td>2024-01-05 08:35:20</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>c30</td>\n",
" <td>1704411350</td>\n",
" <td>0</td>\n",
" <td>38</td>\n",
" <td>rrrrrr</td>\n",
" <td>2024-01-05 08:35:50</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>410</th>\n",
" <td>u20</td>\n",
" <td>1704412330</td>\n",
" <td>1</td>\n",
" <td>13</td>\n",
" <td>ggrggg</td>\n",
" <td>2024-01-05 08:52:10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>411</th>\n",
" <td>u20</td>\n",
" <td>1704412330</td>\n",
" <td>2</td>\n",
" <td>38</td>\n",
" <td>ggGggg</td>\n",
" <td>2024-01-05 08:52:10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>412</th>\n",
" <td>u20</td>\n",
" <td>1704412330</td>\n",
" <td>3</td>\n",
" <td>18</td>\n",
" <td>ggrggg</td>\n",
" <td>2024-01-05 08:52:10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>413</th>\n",
" <td>i9</td>\n",
" <td>1704412340</td>\n",
" <td>0</td>\n",
" <td>26</td>\n",
" <td>GGGG</td>\n",
" <td>2024-01-05 08:52:20</td>\n",
" </tr>\n",
" <tr>\n",
" <th>414</th>\n",
" <td>i9</td>\n",
" <td>1704412340</td>\n",
" <td>1</td>\n",
" <td>64</td>\n",
" <td>rrrr</td>\n",
" <td>2024-01-05 08:52:20</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>415 rows × 6 columns</p>\n",
"</div>"
],
"text/plain": [
" node_id start_unix phase_sumo duration state start_dt\n",
"0 i7 1704411320 0 44 GGrggGG 2024-01-05 08:35:20\n",
"1 i7 1704411320 1 44 rrrggrr 2024-01-05 08:35:20\n",
"2 i7 1704411320 2 26 GGrggGG 2024-01-05 08:35:20\n",
"3 i7 1704411320 3 26 rrrggrr 2024-01-05 08:35:20\n",
"4 c30 1704411350 0 38 rrrrrr 2024-01-05 08:35:50\n",
".. ... ... ... ... ... ...\n",
"410 u20 1704412330 1 13 ggrggg 2024-01-05 08:52:10\n",
"411 u20 1704412330 2 38 ggGggg 2024-01-05 08:52:10\n",
"412 u20 1704412330 3 18 ggrggg 2024-01-05 08:52:10\n",
"413 i9 1704412340 0 26 GGGG 2024-01-05 08:52:20\n",
"414 i9 1704412340 1 64 rrrr 2024-01-05 08:52:20\n",
"\n",
"[415 rows x 6 columns]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>node_id</th>\n",
" <th>start_unix</th>\n",
" <th>phase_sumo</th>\n",
" <th>duration</th>\n",
" <th>state</th>\n",
" <th>start_dt</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>66</th>\n",
" <td>c30</td>\n",
" <td>1704411509</td>\n",
" <td>0</td>\n",
" <td>38</td>\n",
" <td>rrrrrr</td>\n",
" <td>2024-01-05 08:38:29</td>\n",
" </tr>\n",
" <tr>\n",
" <th>67</th>\n",
" <td>c30</td>\n",
" <td>1704411509</td>\n",
" <td>1</td>\n",
" <td>39</td>\n",
" <td>GGGGGG</td>\n",
" <td>2024-01-05 08:38:29</td>\n",
" </tr>\n",
" <tr>\n",
" <th>68</th>\n",
" <td>c30</td>\n",
" <td>1704411509</td>\n",
" <td>2</td>\n",
" <td>41</td>\n",
" <td>GGGGGG</td>\n",
" <td>2024-01-05 08:38:29</td>\n",
" </tr>\n",
" <tr>\n",
" <th>69</th>\n",
" <td>c30</td>\n",
" <td>1704411509</td>\n",
" <td>3</td>\n",
" <td>21</td>\n",
" <td>GGGGGG</td>\n",
" <td>2024-01-05 08:38:29</td>\n",
" </tr>\n",
" <tr>\n",
" <th>128</th>\n",
" <td>c30</td>\n",
" <td>1704411670</td>\n",
" <td>0</td>\n",
" <td>38</td>\n",
" <td>rrrrrr</td>\n",
" <td>2024-01-05 08:41:10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>247</th>\n",
" <td>u60</td>\n",
" <td>1704411970</td>\n",
" <td>0</td>\n",
" <td>24</td>\n",
" <td>ggggggggr</td>\n",
" <td>2024-01-05 08:46:10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>248</th>\n",
" <td>u60</td>\n",
" <td>1704411970</td>\n",
" <td>1</td>\n",
" <td>19</td>\n",
" <td>ggggggggr</td>\n",
" <td>2024-01-05 08:46:10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>249</th>\n",
" <td>u60</td>\n",
" <td>1704411970</td>\n",
" <td>2</td>\n",
" <td>39</td>\n",
" <td>ggggggggG</td>\n",
" <td>2024-01-05 08:46:10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>250</th>\n",
" <td>u60</td>\n",
" <td>1704411970</td>\n",
" <td>3</td>\n",
" <td>65</td>\n",
" <td>ggggggggr</td>\n",
" <td>2024-01-05 08:46:10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>251</th>\n",
" <td>u60</td>\n",
" <td>1704411970</td>\n",
" <td>4</td>\n",
" <td>23</td>\n",
" <td>ggggggggr</td>\n",
" <td>2024-01-05 08:46:10</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>238 rows × 6 columns</p>\n",
"</div>"
],
"text/plain": [
" node_id start_unix phase_sumo duration state start_dt\n",
"66 c30 1704411509 0 38 rrrrrr 2024-01-05 08:38:29\n",
"67 c30 1704411509 1 39 GGGGGG 2024-01-05 08:38:29\n",
"68 c30 1704411509 2 41 GGGGGG 2024-01-05 08:38:29\n",
"69 c30 1704411509 3 21 GGGGGG 2024-01-05 08:38:29\n",
"128 c30 1704411670 0 38 rrrrrr 2024-01-05 08:41:10\n",
".. ... ... ... ... ... ...\n",
"247 u60 1704411970 0 24 ggggggggr 2024-01-05 08:46:10\n",
"248 u60 1704411970 1 19 ggggggggr 2024-01-05 08:46:10\n",
"249 u60 1704411970 2 39 ggggggggG 2024-01-05 08:46:10\n",
"250 u60 1704411970 3 65 ggggggggr 2024-01-05 08:46:10\n",
"251 u60 1704411970 4 23 ggggggggr 2024-01-05 08:46:10\n",
"\n",
"[238 rows x 6 columns]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"display(sigtable)\n",
"display(Sigtable)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"def assign_red_yellow(Sigtable):\n",
" '''\n",
" 적색, 황색신호를 반영한 신호문자열 배정\n",
"\n",
" input : Sigtable\n",
" - 모든 교차로에 대한 (시작유닉스, 세부현시번호)별 현시시간, 신호문자열, 진입·진출엣지\n",
" * 세부현시란 오버랩을 반영한 현시번호를 뜻함.\n",
"\n",
" output : SIGTABLE\n",
" - 모든 교차로에 대한 (시작유닉스, 녹황적세부현시번호)별 현시시간, (황·적색신호가 포함된) 신호문자열\n",
" * 녹황적세부현시번호란 세부현시번호에 r, g, y 옵션까지 포함된 현시번호를 뜻함.\n",
" '''\n",
" SIGTABLE = []\n",
" for node_id, group in Sigtable.groupby('node_id'):\n",
" new_rows_list = []\n",
" for i in range(1, len(group)):\n",
" prev_row = group.iloc[i-1:i].copy()\n",
" next_row = group.iloc[i:i+1].copy()\n",
" new_rows = pd.concat([prev_row, prev_row, next_row]).reset_index(drop=True)\n",
" new_rows.loc[0, 'phase_sumo'] = str(prev_row.phase_sumo.iloc[0]) + '_g'\n",
" new_rows.loc[0, 'duration'] = new_rows.loc[0, 'duration'] - 5\n",
" new_rows.loc[1, 'phase_sumo'] = str(prev_row.phase_sumo.iloc[0]) + '_y'\n",
" new_rows.loc[1, 'duration'] = 4\n",
" yellow_state = ''\n",
" red_state = ''\n",
" for a, b in zip(prev_row.state.iloc[0], next_row.state.iloc[0]):\n",
" if a == 'G' and b == 'r':\n",
" yellow_state += 'y'\n",
" red_state += 'r'\n",
" else:\n",
" yellow_state += a\n",
" red_state += a\n",
" new_rows.loc[2, 'phase_sumo'] = str(next_row.phase_sumo.iloc[0]) + '__r'\n",
" new_rows.loc[2, 'duration'] = 1\n",
" new_rows.loc[1, 'state'] = yellow_state\n",
" new_rows.loc[2, 'state'] = red_state\n",
" new_rows_list.append(new_rows)\n",
" next_row['phase_sumo'] = str(next_row.phase_sumo.iloc[0]) + '_g'\n",
" next_row['duration'] -= 5\n",
" # next_row.loc['duration'] -= 5\n",
" new_rows_list.append(next_row)\n",
" new_rows = pd.concat(new_rows_list)\n",
" SIGTABLE.append(new_rows)\n",
" SIGTABLE = pd.concat(SIGTABLE).sort_values(by=['node_id', 'start_unix', 'phase_sumo']).reset_index(drop=True)\n",
" return SIGTABLE\n",
"SIGTABLE = assign_red_yellow(Sigtable)"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1577804400\n",
"2145884400\n",
"4021\n",
"4021\n"
]
}
],
"source": [
"max_unix, min_unix = int(datetime(2020, 1, 1).timestamp()), int(datetime(2038, 1, 1).timestamp())\n",
"print(max_unix)\n",
"print(min_unix)\n",
"history = pd.read_csv('../Data/tables/history.csv', index_col=0)\n",
"K = 0\n",
"for _, row in history.iterrows():\n",
" unixbool = min_unix <= row['end_unix'] <= max_unix\n",
" print(min_unix, row['end_unix'], max_unix)\n",
" if not unixbool:\n",
" K += 1\n",
"print(K)\n",
"print(len(history))"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>node_id</th>\n",
" <th>start_unix</th>\n",
" <th>phase_sumo</th>\n",
" <th>duration</th>\n",
" <th>state</th>\n",
" <th>start_dt</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>100</th>\n",
" <td>i1</td>\n",
" <td>1704411740</td>\n",
" <td>1__r</td>\n",
" <td>1</td>\n",
" <td>grrGGGrgrr</td>\n",
" <td>2024-01-05 08:42:20</td>\n",
" </tr>\n",
" <tr>\n",
" <th>101</th>\n",
" <td>i1</td>\n",
" <td>1704411740</td>\n",
" <td>1_g</td>\n",
" <td>88</td>\n",
" <td>grrGGGGgrr</td>\n",
" <td>2024-01-05 08:42:20</td>\n",
" </tr>\n",
" <tr>\n",
" <th>102</th>\n",
" <td>i1</td>\n",
" <td>1704411740</td>\n",
" <td>1_y</td>\n",
" <td>4</td>\n",
" <td>grryyyygrr</td>\n",
" <td>2024-01-05 08:42:20</td>\n",
" </tr>\n",
" <tr>\n",
" <th>103</th>\n",
" <td>i1</td>\n",
" <td>1704411740</td>\n",
" <td>2__r</td>\n",
" <td>1</td>\n",
" <td>grrrrrrgrr</td>\n",
" <td>2024-01-05 08:42:20</td>\n",
" </tr>\n",
" <tr>\n",
" <th>104</th>\n",
" <td>i1</td>\n",
" <td>1704411740</td>\n",
" <td>2_g</td>\n",
" <td>35</td>\n",
" <td>grrrrrrgGG</td>\n",
" <td>2024-01-05 08:42:20</td>\n",
" </tr>\n",
" <tr>\n",
" <th>105</th>\n",
" <td>i1</td>\n",
" <td>1704411740</td>\n",
" <td>2_y</td>\n",
" <td>4</td>\n",
" <td>grrrrrrgyy</td>\n",
" <td>2024-01-05 08:42:20</td>\n",
" </tr>\n",
" <tr>\n",
" <th>106</th>\n",
" <td>i1</td>\n",
" <td>1704411910</td>\n",
" <td>0__r</td>\n",
" <td>1</td>\n",
" <td>grrrrrrgrr</td>\n",
" <td>2024-01-05 08:45:10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>107</th>\n",
" <td>i1</td>\n",
" <td>1704411910</td>\n",
" <td>0_g</td>\n",
" <td>32</td>\n",
" <td>gGGGGGrgrr</td>\n",
" <td>2024-01-05 08:45:10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>108</th>\n",
" <td>i1</td>\n",
" <td>1704411910</td>\n",
" <td>0_y</td>\n",
" <td>4</td>\n",
" <td>gyyGGGrgrr</td>\n",
" <td>2024-01-05 08:45:10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>109</th>\n",
" <td>i1</td>\n",
" <td>1704411910</td>\n",
" <td>1__r</td>\n",
" <td>1</td>\n",
" <td>grrGGGrgrr</td>\n",
" <td>2024-01-05 08:45:10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>110</th>\n",
" <td>i1</td>\n",
" <td>1704411910</td>\n",
" <td>1_g</td>\n",
" <td>88</td>\n",
" <td>grrGGGGgrr</td>\n",
" <td>2024-01-05 08:45:10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>111</th>\n",
" <td>i1</td>\n",
" <td>1704411910</td>\n",
" <td>1_y</td>\n",
" <td>4</td>\n",
" <td>grryyyygrr</td>\n",
" <td>2024-01-05 08:45:10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>112</th>\n",
" <td>i1</td>\n",
" <td>1704411910</td>\n",
" <td>2__r</td>\n",
" <td>1</td>\n",
" <td>grrrrrrgrr</td>\n",
" <td>2024-01-05 08:45:10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>113</th>\n",
" <td>i1</td>\n",
" <td>1704411910</td>\n",
" <td>2_g</td>\n",
" <td>35</td>\n",
" <td>grrrrrrgGG</td>\n",
" <td>2024-01-05 08:45:10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>114</th>\n",
" <td>i1</td>\n",
" <td>1704411910</td>\n",
" <td>2_y</td>\n",
" <td>4</td>\n",
" <td>grrrrrrgyy</td>\n",
" <td>2024-01-05 08:45:10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>115</th>\n",
" <td>i1</td>\n",
" <td>1704412080</td>\n",
" <td>0__r</td>\n",
" <td>1</td>\n",
" <td>grrrrrrgrr</td>\n",
" <td>2024-01-05 08:48:00</td>\n",
" </tr>\n",
" <tr>\n",
" <th>116</th>\n",
" <td>i1</td>\n",
" <td>1704412080</td>\n",
" <td>0_g</td>\n",
" <td>32</td>\n",
" <td>gGGGGGrgrr</td>\n",
" <td>2024-01-05 08:48:00</td>\n",
" </tr>\n",
" <tr>\n",
" <th>117</th>\n",
" <td>i1</td>\n",
" <td>1704412080</td>\n",
" <td>0_y</td>\n",
" <td>4</td>\n",
" <td>gyyGGGrgrr</td>\n",
" <td>2024-01-05 08:48:00</td>\n",
" </tr>\n",
" <tr>\n",
" <th>118</th>\n",
" <td>i1</td>\n",
" <td>1704412080</td>\n",
" <td>1__r</td>\n",
" <td>1</td>\n",
" <td>grrGGGrgrr</td>\n",
" <td>2024-01-05 08:48:00</td>\n",
" </tr>\n",
" <tr>\n",
" <th>119</th>\n",
" <td>i1</td>\n",
" <td>1704412080</td>\n",
" <td>1_g</td>\n",
" <td>88</td>\n",
" <td>grrGGGGgrr</td>\n",
" <td>2024-01-05 08:48:00</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" node_id start_unix phase_sumo duration state start_dt\n",
"100 i1 1704411740 1__r 1 grrGGGrgrr 2024-01-05 08:42:20\n",
"101 i1 1704411740 1_g 88 grrGGGGgrr 2024-01-05 08:42:20\n",
"102 i1 1704411740 1_y 4 grryyyygrr 2024-01-05 08:42:20\n",
"103 i1 1704411740 2__r 1 grrrrrrgrr 2024-01-05 08:42:20\n",
"104 i1 1704411740 2_g 35 grrrrrrgGG 2024-01-05 08:42:20\n",
"105 i1 1704411740 2_y 4 grrrrrrgyy 2024-01-05 08:42:20\n",
"106 i1 1704411910 0__r 1 grrrrrrgrr 2024-01-05 08:45:10\n",
"107 i1 1704411910 0_g 32 gGGGGGrgrr 2024-01-05 08:45:10\n",
"108 i1 1704411910 0_y 4 gyyGGGrgrr 2024-01-05 08:45:10\n",
"109 i1 1704411910 1__r 1 grrGGGrgrr 2024-01-05 08:45:10\n",
"110 i1 1704411910 1_g 88 grrGGGGgrr 2024-01-05 08:45:10\n",
"111 i1 1704411910 1_y 4 grryyyygrr 2024-01-05 08:45:10\n",
"112 i1 1704411910 2__r 1 grrrrrrgrr 2024-01-05 08:45:10\n",
"113 i1 1704411910 2_g 35 grrrrrrgGG 2024-01-05 08:45:10\n",
"114 i1 1704411910 2_y 4 grrrrrrgyy 2024-01-05 08:45:10\n",
"115 i1 1704412080 0__r 1 grrrrrrgrr 2024-01-05 08:48:00\n",
"116 i1 1704412080 0_g 32 gGGGGGrgrr 2024-01-05 08:48:00\n",
"117 i1 1704412080 0_y 4 gyyGGGrgrr 2024-01-05 08:48:00\n",
"118 i1 1704412080 1__r 1 grrGGGrgrr 2024-01-05 08:48:00\n",
"119 i1 1704412080 1_g 88 grrGGGGgrr 2024-01-05 08:48:00"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"SIGTABLE[100:120]"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"def make_tl_file(SIGTABLE, offsets, present_time):\n",
" strings = ['<additional>\\n']\n",
" for node_id, group in SIGTABLE.groupby('node_id'):\n",
" strings.append(f' <tlLogic id=\"{node_id}\" type=\"static\" programID=\"{node_id}_prog\" offset=\"{offsets[node_id]}\">\\n')\n",
" for i, row in group.iterrows():\n",
" duration = row.duration\n",
" state = row.state\n",
" strings.append(f' <phase duration=\"{duration}\" state=\"{state}\"/>\\n')\n",
" strings.append(' </tlLogic>\\n')\n",
" strings.append('</additional>')\n",
" strings = ''.join(strings)\n",
" # 저장\n",
" path_output = f'../Results/sn_{present_time}.add.xml'\n",
" with open(path_output, 'w') as f:\n",
" f.write(strings)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
"def generate_signals(m):\n",
" midnight = int(datetime(2024, 1, 5, 0, 0, 0).timestamp())\n",
" next_day = int(datetime(2024, 1, 6, 0, 0, 0).timestamp())\n",
" fmins = range(midnight, next_day, 300)\n",
"\n",
" # 현재시각\n",
" present_time = fmins[m]\n",
" sim_start = fmins[m] - 300\n",
" \n",
" # network and dataframes\n",
" net = sumolib.net.readNet('../Data/networks/sn.net.xml')\n",
" inter_node = pd.read_csv('../data/tables/inter_node.csv', index_col=0)\n",
" plan = pd.read_csv('../Data/tables/plan.csv', index_col=0)\n",
" match6 = pd.read_csv('../Intermediates/match6.csv', index_col=0)\n",
" match6 = match6[['node_id', 'phase_no', 'ring_type', 'inc_edge', 'out_edge']].reset_index(drop=True)\n",
" histid = pd.read_csv(f'../Intermediates/histid/histid_{present_time}.csv', index_col=0)\n",
" histid = histid.reset_index(drop=True).drop(columns=['inter_no'])\n",
" \n",
" # helper dictionaries and lists\n",
" inter_node_p = inter_node[inter_node.inter_type=='parent']\n",
" inter2node = dict(zip(inter_node_p['inter_no'], inter_node_p['node_id']))\n",
" node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no']))\n",
" pa2ch = {'i0':['u00'], 'i1':[], 'i2':['u20'], 'i3':['c30', 'u30', 'u31', 'u32'], 'i6':['u60'], 'i7':[], 'i8':[], 'i9':[]}\n",
" node_ids = sorted(inter_node.node_id.unique())\n",
" parent_ids = sorted(inter_node[inter_node.inter_type=='parent'].node_id.unique())\n",
" nodes = [net.getNode(node_id) for node_id in node_ids]\n",
"\n",
" # histids\n",
" histids = attach_children(histid, match6, parent_ids, pa2ch)\n",
"\n",
" # node2init\n",
" node2init = initialize_states(net, nodes, histids)\n",
"\n",
" # sigtable\n",
" sigtable = assign_signals(histids, node2init, net)\n",
"\n",
" with open('../Intermediates/node2numcycles.json', 'r') as file:\n",
" node2numcycles = json.load(file)\n",
"\n",
" # Sigtable\n",
" Sigtable, offsets = set_timepoints(sigtable, node2num_cycles, present_time)\n",
"\n",
" # SIGTABLE\n",
" SIGTABLE = assign_red_yellow(Sigtable)\n",
"\n",
" make_tl_file(SIGTABLE, offsets, present_time)\n",
" print(f'A signal file (add.xml) has been created for the timeslot between {datetime.fromtimestamp(sim_start)} and {datetime.fromtimestamp(present_time)} ({sim_start} ~ {present_time})')"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"A signal file (add.xml) has been created for the timeslot between 2024-01-05 08:40:00 and 2024-01-05 08:45:00 (1704411600 ~ 1704411900)\n",
"A signal file (add.xml) has been created for the timeslot between 2024-01-05 08:45:00 and 2024-01-05 08:50:00 (1704411900 ~ 1704412200)\n"
]
}
],
"source": [
"generate_signals(105)\n",
"generate_signals(106)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "rts",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
}
},
"nbformat": 4,
"nbformat_minor": 2
}