2026年1月16日 星期五

RFID 綜合運營中央控制台 (Integrated Console)

 RFID 綜合運營中央控制台 (Integrated Console)

這套系統已經涵蓋了從物理層 (Anti-collision)傳輸層 (Cipher)網路層 (Nonce/Auth)應用層 (ERP/Payment/Kill) 的所有精華。

這是一個巨大的工程,我們將把之前開發的所有核心模組:抗碰撞掃描 (Anti-collision)串流加密 (Stream Cipher)動態挑戰 (Nonce Challenge)後端數據庫 (Backend DB) 以及 標籤銷毀 (Kill Command) 全部整合進一個專業的「RFID 運營中央控制台」。

這個控制台分為三個邏輯層:

  1. 物理/傳輸層:負責掃描與處理訊號碰撞。

  2. 安全層:負責密鑰管理與隨機數驗證。

  3. 應用/隱私層:負責帳務處理與結帳後的標籤自毀。




import tkinter as tk

from tkinter import ttk, messagebox

import random

import time

from threading import Thread, Event


# ================= 核心安全引擎 =================

class RFIDSecurityEngine:

    @staticmethod

    def stream_cipher(data_hex, key_hex, nonce_hex=None):

        try:

            # 若有 Nonce 則先進行混合 (防重播)

            val = int(data_hex.replace(" ", ""), 16)

            if nonce_hex:

                val ^= int(nonce_hex.replace(" ", ""), 16)

            

            clean_hex = hex(val)[2:].upper()

            bin_data = bin(int(clean_hex, 16))[2:].zfill(len(clean_hex)*4)

            bin_key = bin(int(key_hex, 16))[2:].zfill(16)

            

            # LSFR 邏輯

            state = [int(b) for b in bin_key[:8]]

            keystream = []

            for _ in range(len(bin_data)):

                out = state[-1]

                feedback = state[0] ^ state[2]

                state = [feedback] + state[:-1]

                keystream.append(out)

            

            res_bits = [int(bin_data[i]) ^ keystream[i] for i in range(len(bin_data))]

            hex_out = hex(int("".join(map(str, res_bits)), 2))[2:].upper().zfill(len(clean_hex))

            return " ".join(hex_out[i:i+2] for i in range(0, len(hex_out), 2))

        except: return "ERROR"


# ================= 中央控制台程式 =================

class RFIDCentralConsole:

    def __init__(self, root):

        self.root = root

        self.root.title("RFID 旗艦級中央運營控制台")

        self.root.geometry("900x750")

        

        # 系統狀態

        self.db = {

            "USERS": {

                "53 71 37 A4 F6": {"name": "張小明", "balance": 1000, "key": "AC55"},

                "12 AB 34 CD EF": {"name": "李華", "balance": 50, "key": "B88B"}

            },

            "PRODUCTS": {

                "P01": {"name": "無人機", "price": 450, "kill_pwd": "BADC"},

                "P02": {"name": "傳感器", "price": 120, "kill_pwd": "DEAD"},

                "P03": {"name": "加密晶片", "price": 300, "kill_pwd": "FACE"}

            }

        }

        self.inventory_cart = []

        self.killed_tags = set()

        self.is_running = False


        self.setup_ui()


    def setup_ui(self):

        # --- 頂部:數據庫監控 ---

        db_frame = tk.LabelFrame(self.root, text="後端資產管理系統 (Backend ERP)", padx=10, pady=5)

        db_frame.pack(fill="x", padx=10, pady=5)

        

        self.db_tree = ttk.Treeview(db_frame, columns=("UID", "Name", "Balance", "Key"), show="headings", height=3)

        for col in ("UID", "Name", "Balance", "Key"): self.db_tree.heading(col, text=col)

        self.db_tree.pack(fill="x")

        self.update_db_view()


        # --- 中間:工作區域 (分為左掃描、右結帳) ---

        work_frame = tk.Frame(self.root)

        work_frame.pack(fill="both", expand=True, padx=10)


        # 左側:倉庫/賣場掃描 (Anti-collision)

        scan_frame = tk.LabelFrame(work_frame, text="空中介面掃描 (Air Interface)", padx=10)

        scan_frame.pack(side=tk.LEFT, fill="both", expand=True)

        

        self.btn_scan = tk.Button(scan_frame, text="🔍 批次抗碰撞掃描", command=self.run_anti_collision, bg="#3498db", fg="white")

        self.btn_scan.pack(fill="x", pady=5)

        

        self.scan_list = tk.Listbox(scan_frame, height=8, font=("Courier", 9))

        self.scan_list.pack(fill="both", expand=True)


        # 右側:結帳與安全驗證

        pay_frame = tk.LabelFrame(work_frame, text="安全支付終端 (Payment Terminal)", padx=10)

        pay_frame.pack(side=tk.RIGHT, fill="both", expand=True)

        

        self.lbl_total = tk.Label(pay_frame, text="應付金額: $0", font=("Arial", 12, "bold"), fg="#e67e22")

        self.lbl_total.pack(pady=10)

        

        tk.Label(pay_frame, text="選擇感應卡片:").pack()

        for uid, info in self.db["USERS"].items():

            tk.Button(pay_frame, text=f"感應 {info['name']}", command=lambda u=uid: self.process_payment(u)).pack(fill="x", pady=2)


        # --- 底部:系統日誌與傳輸監控 ---

        log_frame = tk.LabelFrame(self.root, text="系統原始日誌 (Raw Logs)", padx=10)

        log_frame.pack(fill="x", padx=10, pady=10)

        

        self.log_text = tk.Text(log_frame, height=10, bg="#2c3e50", fg="#ecf0f1", font=("Consolas", 9))

        self.log_text.pack(fill="x", pady=5)


    # ---------------- 邏輯處理 ----------------

    def update_db_view(self):

        for i in self.db_tree.get_children(): self.db_tree.delete(i)

        for uid, info in self.db["USERS"].items():

            self.db_tree.insert("", tk.END, values=(uid, info["name"], f"${info['balance']}", info["key"]))


    def run_anti_collision(self):

        if self.is_running: return

        self.is_running = True

        self.inventory_cart = []

        self.scan_list.delete(0, tk.END)

        Thread(target=self._scan_thread, daemon=True).start()


    def _scan_thread(self):

        self.log(">>> [運營] 啟動批次掃描程序...")

        products = list(self.db["PRODUCTS"].keys())

        # 模擬 8 個時槽 (Slots)

        for slot in range(8):

            time.sleep(0.4)

            # 隨機選中商品,且排除已銷毀的

            available = [p for p in products if p not in self.killed_tags and random.random() < 0.3]

            

            if len(available) == 1:

                pid = available[0]

                item = self.db["PRODUCTS"][pid]

                self.inventory_cart.append(item)

                self.root.after(0, lambda p=pid, n=item['name']: self.scan_list.insert(tk.END, f"Slot {slot}: {n} (${item['price']})"))

                self.log(f"Slot {slot}: 辨識成功 -> {item['name']}")

            elif len(available) > 1:

                self.log(f"Slot {slot}: 偵測到碰撞!訊號干擾中...")

        

        total = sum(i['price'] for i in self.inventory_cart)

        self.root.after(0, lambda: self.lbl_total.config(text=f"應付金額: ${total}"))

        self.is_running = False


    def process_payment(self, user_uid):

        if not self.inventory_cart or self.is_running: return

        self.is_running = True

        Thread(target=self._pay_thread, args=(user_uid,), daemon=True).start()


    def _pay_thread(self, uid):

        total = sum(i['price'] for i in self.inventory_cart)

        user = self.db["USERS"][uid]

        

        self.log(f"\n[安全] 向卡片 {uid} 發起挑戰...")

        nonce = hex(random.getrandbits(16))[2:].upper().zfill(4)

        time.sleep(1)

        

        # 模擬加密響應

        cipher_resp = RFIDSecurityEngine.stream_cipher(uid, user["key"], nonce)

        self.log(f"[傳輸] 接收密文: {cipher_resp}")

        

        # 驗證

        decrypted = RFIDSecurityEngine.stream_cipher(cipher_resp, user["key"], nonce)

        if decrypted.replace(" ","") == uid.replace(" ",""):

            if user["balance"] >= total:

                user["balance"] -= total

                self.log(f"[成功] 扣款 ${total}。正在發送 Kill Command 銷毀商品標籤...")

                

                # 執行 Kill Command (隱私保護)

                for item in self.inventory_cart:

                    # 查找對應的 PID

                    for pid, p_info in self.db["PRODUCTS"].items():

                        if p_info["name"] == item["name"]:

                            self.killed_tags.add(pid)

                            self.log(f"-> Tag {pid} 已銷毀 (Kill PWD used)")

                

                self.inventory_cart = []

                self.root.after(0, self.update_db_view)

                self.root.after(0, lambda: self.lbl_total.config(text="應付金額: $0"))

                messagebox.showinfo("系統通知", "支付成功!商品標籤已停用以保護隱私。")

            else:

                self.log("[錯誤] 餘額不足。")

        else:

            self.log("[警告] 密鑰錯誤,偵測到潛在非法卡片!")

        

        self.is_running = False


    def log(self, msg):

        self.root.after(0, lambda: self.log_text.insert(tk.END, msg + "\n"))

        self.root.after(0, lambda: self.log_text.see(tk.END))


if __name__ == "__main__":

    root = tk.Tk()

    app = RFIDCentralConsole(root)

    root.mainloop()


沒有留言:

張貼留言

ASK 調變模擬器程式碼

ASK 調變模擬器程式碼 為什麼第二張圖要「放大 (Zoom)」? 1 kHz  的週期是 1000 us 。 如果不放大,第二張圖會因為波形太密而看起來像一塊實心的紅磚。 import tkinter as tk from tkinter import ttk import n...