RFID Super Tag 協議模擬器
Super Tag 協議(通常指一種改進型的時分多址或樹狀抗碰撞協議)的核心概念是「標籤主導」與「快速識別」。在這種模式下,標籤不僅僅是等待查詢,還會根據內部的計數器或隨機分配的 Slot 進行主動回應,而 Reader 則根據回應情況發送確認訊號。
在進階的 Super Tag 模擬中,我們通常會加入動態時槽調整與標籤靜默機制。
協議邏輯說明
標籤主導 (Tag-Driven):在此模擬中,每個標籤會根據內部的隨機邏輯決定是否在目前的時間窗發送訊號,這模擬了 Super Tag 協議中標籤自主競爭通道的特性。
狀態監控:
黃色 (Sending):標籤嘗試與 Reader 溝通。
綠色 (Identified):Reader 成功解析標籤 ID 並發送 Ack 訊號,標籤進入靜默(Quiet)狀態。
紅色 (Collision):多個標籤同時說話,訊號相互干擾。
import tkinter as tk
from tkinter import ttk
import random
import time
from threading import Thread, Event
class SuperTagSimulator:
def __init__(self, root):
self.root = root
self.root.title("RFID Super Tag Protocol (標籤主導協議) 模擬器")
# 模擬標籤群 (包含 UID 與初始計數器)
self.tag_data = [
{"uid": "ST-101", "state": "Idle"},
{"uid": "ST-205", "state": "Idle"},
{"uid": "ST-309", "state": "Idle"},
{"uid": "ST-412", "state": "Idle"},
{"uid": "ST-555", "state": "Idle"}
]
# 控制旗標
self.is_running = False
self.stop_requested = False
self.pause_event = Event()
self.pause_event.set()
self.setup_ui()
def setup_ui(self):
# 頂部狀態列
self.header = tk.Frame(self.root, bg="#34495e", pady=10)
self.header.pack(fill="x")
self.lbl_info = tk.Label(self.header, text="Super Tag 系統就緒", font=("Arial", 12, "bold"), fg="white", bg="#34495e")
self.lbl_info.pack()
# 標籤視覺化區域
self.canvas = tk.Canvas(self.root, width=700, height=150, bg="#ecf0f1")
self.canvas.pack(pady=10)
self.tag_visuals = {}
for i, tag in enumerate(self.tag_data):
x = 80 + (i * 130)
rect = self.canvas.create_rectangle(x-40, 40, x+40, 90, fill="white", outline="#7f8c8d", width=2)
txt = self.canvas.create_text(x, 65, text=tag["uid"], font=("Arial", 9, "bold"))
status_txt = self.canvas.create_text(x, 105, text="IDLE", fill="#7f8c8d")
self.tag_visuals[tag["uid"]] = {"rect": rect, "status": status_txt}
# 按鈕控制區
btn_frame = tk.Frame(self.root)
btn_frame.pack(pady=10)
self.btn_run = tk.Button(btn_frame, text="▶ 啟動程序", command=self.start_sim, bg="#27ae60", fg="white", width=12)
self.btn_run.pack(side=tk.LEFT, padx=5)
self.btn_pause = tk.Button(btn_frame, text="⏸ 暫停", command=self.toggle_pause, state=tk.DISABLED, width=12)
self.btn_pause.pack(side=tk.LEFT, padx=5)
self.btn_stop = tk.Button(btn_frame, text="⏹ 結束", command=self.terminate_sim, state=tk.DISABLED, bg="#c0392b", fg="white", width=12)
self.btn_stop.pack(side=tk.LEFT, padx=5)
# 傳輸紀錄表
self.tree = ttk.Treeview(self.root, columns=("Time", "Tag", "Action", "Result"), show="headings", height=8)
self.tree.heading("Time", text="時標")
self.tree.heading("Tag", text="發送標籤")
self.tree.heading("Action", text="通訊動作")
self.tree.heading("Result", text="結果")
self.tree.column("Time", width=80)
self.tree.pack(fill="both", padx=10, pady=10)
def toggle_pause(self):
if not self.is_running: return
if self.pause_event.is_set():
self.pause_event.clear()
self.btn_pause.config(text="▶ 恢復", bg="#f1c40f")
self.lbl_info.config(text="系統已暫停")
else:
self.pause_event.set()
self.btn_pause.config(text="⏸ 暫停", bg="SystemButtonFace")
self.lbl_info.config(text="系統運行中...")
def terminate_sim(self):
self.stop_requested = True
self.pause_event.set() # 確保解開暫停鎖以便退出
def start_sim(self):
self.is_running = True
self.stop_requested = False
self.pause_event.set()
self.btn_run.config(state=tk.DISABLED)
self.btn_pause.config(state=tk.NORMAL)
self.btn_stop.config(state=tk.NORMAL)
for item in self.tree.get_children(): self.tree.delete(item)
Thread(target=self.protocol_logic, daemon=True).start()
def protocol_logic(self):
self.root.after(0, lambda: self.lbl_info.config(text="正在同步所有標籤..."))
time.sleep(1)
# 模擬 Super Tag 的多輪辨識
unidentified_tags = [t["uid"] for t in self.tag_data]
while unidentified_tags and not self.stop_requested:
self.pause_event.wait()
# 隨機選擇本輪嘗試發送的標籤(模擬機率碰撞)
# 在 Super Tag 協議中,標籤會根據內部的 Slot Counter 決定是否發送
attempting_tags = [uid for uid in unidentified_tags if random.random() > 0.4]
timestamp = time.strftime("%H:%M:%S")
if not attempting_tags:
self.log_event(timestamp, "N/A", "Channel Sensing", "Idle (空閒)")
time.sleep(0.8)
continue
# 視覺化:所有嘗試發送的標籤變色
for uid in attempting_tags:
self.update_tag_ui(uid, "#f1c40f", "SENDING...")
time.sleep(1) # 模擬傳輸延遲
if len(attempting_tags) == 1:
# 成功辨識
success_uid = attempting_tags[0]
unidentified_tags.remove(success_uid)
self.log_event(timestamp, success_uid, "Identification", "SUCCESS (辨識成功)")
self.update_tag_ui(success_uid, "#2ecc71", "IDENTIFIED")
# 成功後標籤進入靜默狀態 (Sleep)
else:
# 發生碰撞
self.log_event(timestamp, "Multiple", "Collision", f"{len(attempting_tags)} Tags Clash!")
for uid in attempting_tags:
self.update_tag_ui(uid, "#e74c3c", "COLLISION")
time.sleep(1.2)
# 重置未辨識標籤的顏色
for uid in unidentified_tags:
self.update_tag_ui(uid, "white", "IDLE")
final_msg = "所有標籤辨識完成!" if not self.stop_requested else "模擬已結束"
self.root.after(0, lambda: self.lbl_info.config(text=final_msg))
self.reset_controls()
def update_tag_ui(self, uid, color, status):
def update():
self.canvas.itemconfig(self.tag_visuals[uid]["rect"], fill=color)
self.canvas.itemconfig(self.tag_visuals[uid]["status"], text=status, fill="#2c3e50" if color != "white" else "#7f8c8d")
self.root.after(0, update)
def log_event(self, ts, tag, action, res):
self.root.after(0, lambda: self.tree.insert("", 0, values=(ts, tag, action, res)))
def reset_controls(self):
self.is_running = False
self.btn_run.config(state=tk.NORMAL)
self.btn_pause.config(state=tk.DISABLED, text="⏸ 暫停", bg="SystemButtonFace")
self.btn_stop.config(state=tk.DISABLED)
if __name__ == "__main__":
root = tk.Tk()
app = SuperTagSimulator(root)
root.mainloop()
在 Super Tag 協議中,最重要的效能指標是 「吞吐量與碰撞率的關係」。當標籤數量增加時,碰撞會呈指數級成長。
碰撞與成功率的拉鋸:
在模擬開始時,標籤數量多(未辨識者多),碰撞率 (Collision) 會頻繁上升。
隨著標籤一個個被「辨識成功」並轉為綠色,競爭者變少,成功率 (Success) 會趨於穩定。
效能視覺化:
左上角的長條圖會動態伸縮。如果紅色部分(Collision)太高,代表協議的背離避讓機制(Backoff)需要調整。
import tkinter as tk
from tkinter import ttk
import random
import time
from threading import Thread, Event
class SuperTagProSimulator:
def __init__(self, root):
self.root = root
self.root.title("RFID Super Tag 效能分析模擬器")
# 標籤資料
self.tags = [{"uid": f"TAG-{i:02d}", "identified": False} for i in range(12)]
# 統計數據
self.stats = {"Success": 0, "Collision": 0, "Idle": 0}
self.is_running = False
self.is_paused = False
self.stop_requested = False
self.pause_event = Event()
self.pause_event.set()
self.setup_ui()
def setup_ui(self):
# 頂部:圖表統計區 (使用 Canvas 自行繪製簡易長條圖)
graph_frame = tk.LabelFrame(self.root, text="即時效能統計 (Real-time Stats)", padx=10, pady=10)
graph_frame.pack(fill="x", padx=10, pady=5)
self.stat_canvas = tk.Canvas(graph_frame, width=400, height=120, bg="#f8f9fa")
self.stat_canvas.pack(side=tk.LEFT)
self.bars = {}
self.create_chart()
# 中間:標籤狀態網格
tag_frame = tk.LabelFrame(self.root, text="標籤場景狀態")
tag_frame.pack(fill="both", padx=10, pady=5)
self.tag_labels = {}
grid_container = tk.Frame(tag_frame)
grid_container.pack(pady=5)
for i, tag in enumerate(self.tags):
lbl = tk.Label(grid_container, text=tag["uid"], width=8, relief="groove", bg="white")
lbl.grid(row=i//6, column=i%6, padx=2, pady=2)
self.tag_labels[tag["uid"]] = lbl
# 按鈕控制區
ctrl_frame = tk.Frame(self.root)
ctrl_frame.pack(pady=10)
self.btn_run = tk.Button(ctrl_frame, text="▶ 開始分析", command=self.start_sim, bg="#27ae60", fg="white", width=10)
self.btn_run.pack(side=tk.LEFT, padx=5)
self.btn_pause = tk.Button(ctrl_frame, text="⏸ 暫停", command=self.toggle_pause, state=tk.DISABLED, width=10)
self.btn_pause.pack(side=tk.LEFT, padx=5)
self.btn_stop = tk.Button(ctrl_frame, text="⏹ 結束", command=self.terminate_sim, state=tk.DISABLED, bg="#c0392b", fg="white", width=10)
self.btn_stop.pack(side=tk.LEFT, padx=5)
# 底部狀態列
self.lbl_summary = tk.Label(self.root, text="準備就緒", font=("Arial", 10, "italic"))
self.lbl_summary.pack(pady=5)
def create_chart(self):
# 建立長條圖基礎結構
colors = {"Success": "#2ecc71", "Collision": "#e74c3c", "Idle": "#95a5a6"}
for i, (key, color) in enumerate(colors.items()):
x = 50 + (i * 100)
bar = self.stat_canvas.create_rectangle(x, 100, x+40, 100, fill=color)
txt = self.stat_canvas.create_text(x+20, 110, text=key)
val = self.stat_canvas.create_text(x+20, 90, text="0")
self.bars[key] = {"rect": bar, "val_txt": val}
def update_chart(self):
total = sum(self.stats.values())
if total == 0: return
for key, value in self.stats.items():
height = (value / total) * 80 # 最大高度 80 像素
x0, _, x1, _ = self.stat_canvas.coords(self.bars[key]["rect"])
self.stat_canvas.coords(self.bars[key]["rect"], x0, 100-height, x1, 100)
self.stat_canvas.itemconfig(self.bars[key]["val_txt"], text=str(value))
def toggle_pause(self):
if self.is_paused:
self.is_paused = False
self.pause_event.set()
self.btn_pause.config(text="⏸ 暫停")
else:
self.is_paused = True
self.pause_event.clear()
self.btn_pause.config(text="▶ 恢復")
def terminate_sim(self):
self.stop_requested = True
self.pause_event.set()
def start_sim(self):
self.is_running = True
self.stop_requested = False
self.is_paused = False
self.pause_event.set()
self.stats = {"Success": 0, "Collision": 0, "Idle": 0}
for t in self.tags: t["identified"] = False
self.btn_run.config(state=tk.DISABLED)
self.btn_pause.config(state=tk.NORMAL)
self.btn_stop.config(state=tk.NORMAL)
Thread(target=self.logic_loop, daemon=True).start()
def logic_loop(self):
while not self.stop_requested and any(not t["identified"] for t in self.tags):
self.pause_event.wait()
# 模擬 Super Tag 隨機存取機率
active_tags = [t for t in self.tags if not t["identified"] and random.random() < 0.2]
# 更新所有標籤 UI 為初始色
self.root.after(0, self.reset_labels)
time.sleep(0.3)
if len(active_tags) == 0:
self.stats["Idle"] += 1
elif len(active_tags) == 1:
self.stats["Success"] += 1
tag = active_tags[0]
tag["identified"] = True
self.root.after(0, lambda uid=tag["uid"]: self.tag_labels[uid].config(bg="#2ecc71", fg="white"))
else:
self.stats["Collision"] += 1
for tag in active_tags:
self.root.after(0, lambda uid=tag["uid"]: self.tag_labels[uid].config(bg="#e74c3c", fg="white"))
self.root.after(0, self.update_chart)
self.root.after(0, lambda: self.lbl_summary.config(text=f"已辨識: {sum(t['identified'] for t in self.tags)} / {len(self.tags)}"))
time.sleep(0.7)
self.is_running = False
self.root.after(0, self.reset_buttons)
def reset_labels(self):
for tag in self.tags:
if not tag["identified"]:
self.tag_labels[tag["uid"]].config(bg="white", fg="black")
def reset_buttons(self):
self.btn_run.config(state=tk.NORMAL)
self.btn_pause.config(state=tk.DISABLED)
self.btn_stop.config(state=tk.DISABLED)
if __name__ == "__main__":
root = tk.Tk()
app = SuperTagProSimulator(root)
root.mainloop()
沒有留言:
張貼留言