# power_constant_module.py
# Versão: 2.7
# Data: 14/03/2025
# Descrição: Módulo 100% standalone, com gerenciamento de porta otimizado, método de leitura de RSSI corrigido,
# potência configurável. Quando a tag não é detectada: pontos vermelhos isolados na curva (sem ligação entre si)
# para deixar claro que a tag não está sendo lida; quando detectada: curva azul ligada.

import tkinter as tk
from tkinter import ttk, messagebox
import queue
import threading
import time
import os
import sys
import ctypes
import struct
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib
from src.core.port_manager import get_port_manager
from .i18n import get_translator, t
from .app_console import fc_log

try:
    matplotlib.use('TkAgg')
except Exception:
    matplotlib.use('Agg')

# --- BLOCO DE CONTROLE DE HARDWARE EMBUTIDO ---

DLL_NAME = "UHFRFID.dll"
try:
    if hasattr(sys, '_MEIPASS'):
        dll_path = os.path.join(sys._MEIPASS, DLL_NAME)
    else:
        dll_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), DLL_NAME)
    rfid_sdk = ctypes.CDLL(dll_path)
    rfid_sdk.UHF_RFID_Open.argtypes = [ctypes.c_ubyte, ctypes.c_int]; rfid_sdk.UHF_RFID_Open.restype = ctypes.c_int
    rfid_sdk.UHF_RFID_Close.argtypes = [ctypes.c_ubyte]; rfid_sdk.UHF_RFID_Close.restype = ctypes.c_int
    rfid_sdk.UHF_RFID_Set.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_uint, ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint)]; rfid_sdk.UHF_RFID_Set.restype = ctypes.c_int
    HARDWARE_AVAILABLE = True
except (OSError, AttributeError):
    rfid_sdk = None
    HARDWARE_AVAILABLE = False

# COM_PORT = 4 ### ALTERADO: Removido
BAUD_RATE = 115200
RFID_CMD_SET_TXPOWER = 0x10
RFID_CMD_SET_FREQ_TABLE = 0x14
RFID_CMD_INV_TAG = 0x80
RFID_CMD_GET_RSSIVALU = 0x64 # Não mais usado para leitura principal
RFID_CMD_STOP_INVENTORY = 0x8C
RFID_CMD_SET_CW_STATUS = 0x24
RFID_CMD_SET_REGION = 0x2C
RFID_CMD_GET_TEMPERATURE = 0x34
RFID_CMD_GET_PORT_LOSS = 0x32
TEMPERATURE_LIMIT = 50.0
REFLECTED_POWER_LIMIT = 10.0  # dBm - Potência refletida máxima aceitável

# Tabela para conversão ADC -> RSSI
RxAdcTable = [
    0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0005, 0x0007, 0x000B, 0x0010, 0x0116, 0x011D, 0x0126,
    0x0131, 0x013E, 0x024C, 0x0260, 0x0374, 0x048B, 0x05A5, 0x06C3, 0x08E6, 0x09FF, 0x0BFF, 0x0EFF,
    0x10FF, 0x14FF, 0x17FF, 0x1CFF, 0x21FF, 0x26FF, 0x2DFF, 0x34FF, 0x3CFF, 0x46FF, 0x50FF, 0x5DFF,
    0x6AFF, 0x7AFF, 0x8BFF, 0x9EFF, 0xB4FF, 0xCCFF, 0xE7FF, 0xFFFF
]

class TagInfo:
    def __init__(self, epc, rssi):
        self.epc = epc; self.rssi = rssi
    def __repr__(self):
        return f"Tag(EPC={self.epc}, RSSI={self.rssi}dBm)"

# --- FIM DO BLOCO DE CONTROLE DE HARDWARE ---

class PowerConstantModule(tk.Frame):
    ### ALTERADO: Adicionado com_port=4 ###
    def __init__(self, parent, app_shell=None, demo_mode=False, com_port=4):
        fc_log("constant_power", "PowerConstantModule __init__ starting")
        fc_log("constant_power", f"app_shell={app_shell} demo_mode={demo_mode} com_port={com_port}")
        super().__init__(parent)
        self.parent = parent
        self.app_shell = app_shell
        self.demo_mode = demo_mode
        self.com_port = com_port ### NOVO: Armazena a porta COM ###
        self.com_port_lock = threading.Lock()
        self.port_manager = get_port_manager()
        self.translator = get_translator()
        self._widget_refs = {}  # Para armazenar referências aos widgets para atualização de idioma

        self.frequency = 915.0
        self.power = 15.0
        self.samples_per_point = 3
        self.pause_between_samples = 0.01
        self.pause_between_points = 0.1
        self._power_entry_validating = False
        
        self.test_running = False
        self.test_paused = False  # NOVO: Controle de pausa
        self.data_queue = queue.Queue()
        self.time_data = []
        self.rssi_data = []
        self.tag_detected_data = []  # True = tag lida, False = tag não detectada (plotar ponto vermelho isolado)
        self.test_start_time = None  # NOVO: Tempo de início do teste
        self.paused_time = 0  # NOVO: Tempo total pausado
        self.pause_start_time = None  # NOVO: Momento do início da pausa
        
        # NOVO: Para controle de tags detectadas
        self.previous_epcs = set()  # EPCs do ciclo anterior
        self.current_epcs = set()   # EPCs do ciclo atual
        
        # Inicializa limites da licença com valores padrão (modo browser)
        # CORREÇÃO: Usa valores mais restritivos como padrão para forçar atualização
        self.license_limits = {'min_freq': 800, 'max_freq': 1000, 'min_power': 5, 'max_power': 20, 'is_licensed': False}
        
        # DEBUG: Mostra estado inicial
        
        # CORREÇÃO DEFINITIVA: Força o uso dos limites corretos da licença
        if app_shell:
            try:
                # Tenta obter limites da licença ativa
                if hasattr(app_shell, '_calculate_license_limits'):
                    valid_lics = ["PowerConstant", "FastChecker"]
                    license_limits = app_shell._calculate_license_limits(valid_lics)
                    if license_limits.get('is_licensed', False):
                        self.license_limits = license_limits
                    else:
                        # Se não há licença, usa limites restritivos
                        self.license_limits = {'min_freq': 800, 'max_freq': 1000, 'min_power': 5, 'max_power': 20, 'is_licensed': False}
                elif hasattr(app_shell, 'license_limits'):
                    self.license_limits = app_shell.license_limits
                else:
                    # Fallback: usa limites restritivos
                    self.license_limits = {'min_freq': 800, 'max_freq': 1000, 'min_power': 5, 'max_power': 20, 'is_licensed': False}
            except Exception as e:
                # Em caso de erro, usa limites restritivos
                self.license_limits = {'min_freq': 800, 'max_freq': 1000, 'min_power': 5, 'max_power': 20, 'is_licensed': False}
        else:
            # Sem app_shell, usa limites restritivos
            self.license_limits = {'min_freq': 800, 'max_freq': 1000, 'min_power': 5, 'max_power': 20, 'is_licensed': False}
        
        # DEBUG: Mostra estado final
        
        self.setup_ui()
        
        # NOVO: Atualiza limites de licença após setup da UI
        self._update_license_limits()
        
        if not HARDWARE_AVAILABLE:
            messagebox.showerror(t('constant_power.dll_error'), t('constant_power.dll_error_msg'))
            self.start_button.config(state="disabled")
        else:
            self.after(100, lambda: self._initial_reader_cleanup())
        
        self.process_data_queue()
        
        # Timer para verificar EPCs periodicamente
        self.after(1000, self._check_epc_timeout)
        
        # REQUISITOS: Captura porta + Reset ao entrar
        self._initialize_hardware_on_enter()

    def _initialize_hardware_on_enter(self):
        """REQUISITOS 1+4: Captura porta e reseta hardware - com preparação robusta"""
        if not HARDWARE_AVAILABLE or not rfid_sdk:
            return
        
        try:
            fc_log("constant_power", "Initializing hardware")
            
            # CORREÇÃO: Fecha porta primeiro para garantir estado limpo
            try:
                rfid_sdk.UHF_RFID_Close(self.com_port)
                time.sleep(0.1)  # Delay para garantir que a porta foi fechada
            except:
                pass  # Ignora erro se porta já estiver fechada
            
            # CORREÇÃO: Tenta abrir porta com retry (até 3 tentativas)
            port_opened = False
            for attempt in range(3):
                if rfid_sdk.UHF_RFID_Open(self.com_port, BAUD_RATE) == 0:
                    port_opened = True
                    fc_log("constant_power", f"COM{self.com_port} acquired (attempt {attempt + 1})")
                    break
                else:
                    if attempt < 2:
                        # Tenta fechar novamente antes da próxima tentativa
                        try:
                            rfid_sdk.UHF_RFID_Close(self.com_port)
                        except:
                            pass
                        time.sleep(0.3)  # Delay entre tentativas
            
            if port_opened:
                # REQUISITO 3: Para comandos ativos
                out_buf = ctypes.create_string_buffer(64)
                out_len = ctypes.c_uint(0)
                rfid_sdk.UHF_RFID_Set(RFID_CMD_STOP_INVENTORY, None, 0, out_buf, ctypes.byref(out_len))
                
                # Fecha porta (será reaberta quando necessário)
                rfid_sdk.UHF_RFID_Close(self.com_port)
                
                fc_log("constant_power", "Hardware initialized (no beep)")
            else:
                fc_log("constant_power", "Failed to acquire port after 3 attempts")
                
        except Exception as e:
            fc_log("constant_power", f"Hardware init error: {e}")
    
    def get_temperature(self):
        """Lê a temperatura do hardware RFID"""
        if not HARDWARE_AVAILABLE or not rfid_sdk:
            return None
        try:
            output_buffer = ctypes.create_string_buffer(64)
            output_len = ctypes.c_uint(0)
            status = rfid_sdk.UHF_RFID_Set(RFID_CMD_GET_TEMPERATURE, None, 0, output_buffer, ctypes.byref(output_len))
            if status == 0 and output_len.value >= 3:
                temp_val = struct.unpack('>h', output_buffer.raw[1:3])[0]
                return temp_val / 100.0
        except Exception as e:
            fc_log("constant_power", f"Temperature read error: {e}")
        return None

    def get_reflected_power(self):
        """Lê a Potência Refletida (dBm) via RFID_CMD_GET_PORT_LOSS"""
        if not HARDWARE_AVAILABLE or not rfid_sdk:
            fc_log("constant_power", "Hardware unavailable for reflected power read")
            return None
        
        # CRÍTICO: Abre porta antes de ler
        fc_log("constant_power", f"Opening COM{self.com_port} for reflected power read")
        if not self.port_manager.acquire_port("PowerConstant", timeout=2.0):
            fc_log("constant_power", f"Timeout acquiring COM{self.com_port}")
            return None
        
        try:
            # CORREÇÃO: Fechar porta primeiro para garantir estado limpo
            try:
                rfid_sdk.UHF_RFID_Close(self.com_port)
                time.sleep(0.05)  # Pequeno delay para garantir que a porta foi fechada
            except:
                pass  # Ignora erro se porta já estiver fechada
            
            # Tentar abrir a porta (com retry)
            port_opened = False
            for attempt in range(3):
                if rfid_sdk.UHF_RFID_Open(self.com_port, BAUD_RATE) == 0:
                    port_opened = True
                    fc_log("constant_power", f"COM{self.com_port} open (attempt {attempt + 1})")
                    break
                else:
                    if attempt < 2:
                        time.sleep(0.1)  # Aguarda antes de tentar novamente
            
            if not port_opened:
                fc_log("constant_power", f"Failed to open COM{self.com_port} after 3 attempts")
                self.port_manager.release_port("PowerConstant")
                return None
            
            # Ler potência refletida
            output_buffer = ctypes.create_string_buffer(64)
            output_len = ctypes.c_uint(0)
            status = rfid_sdk.UHF_RFID_Set(RFID_CMD_GET_PORT_LOSS, b'', 0, output_buffer, ctypes.byref(output_len))
            fc_log("constant_power", f"CMD 0x32 status={status} response_len={output_len.value}")
            
            if status == 0 and output_len.value >= 3:
                adc_value = struct.unpack('>H', output_buffer.raw[1:3])[0]
                reflected_power_dbm = -25 + next((i for i, val in enumerate(RxAdcTable) if adc_value <= val), len(RxAdcTable) - 1)
                fc_log("constant_power", f"ADC={adc_value} reflected_power={reflected_power_dbm:.1f} dBm")
                return reflected_power_dbm
        except Exception as e:
            fc_log("constant_power", f"Reflected power read error: {e}")
        finally:
            try:
                rfid_sdk.UHF_RFID_Close(self.com_port)
            except:
                pass
            self.port_manager.release_port("PowerConstant")
            fc_log("constant_power", f"COM{self.com_port} closed")
        return None

    def destroy(self):
        """REQUISITOS 2+3+4: Libera porta, para comandos, reseta"""
        try:
            fc_log("constant_power", "Cleanup starting")
            
            if hasattr(self, 'test_running') and self.test_running:
                self.test_running = False
            
            if HARDWARE_AVAILABLE and rfid_sdk:
                try:
                    if self.port_manager.acquire_port("PowerConstant", timeout=2.0):
                        try:
                            # CORREÇÃO: Fechar porta primeiro para garantir estado limpo
                            try:
                                rfid_sdk.UHF_RFID_Close(self.com_port)
                                time.sleep(0.05)  # Pequeno delay para garantir que a porta foi fechada
                            except:
                                pass  # Ignora erro se porta já estiver fechada
                            
                            # Tentar abrir a porta (com retry)
                            port_opened = False
                            for attempt in range(3):
                                if rfid_sdk.UHF_RFID_Open(self.com_port, BAUD_RATE) == 0:
                                    port_opened = True
                                    break
                                else:
                                    if attempt < 2:
                                        time.sleep(0.1)  # Aguarda antes de tentar novamente
                            
                            if port_opened:
                                out_buf = ctypes.create_string_buffer(64)
                                out_len = ctypes.c_uint(0)
                                rfid_sdk.UHF_RFID_Set(RFID_CMD_STOP_INVENTORY, None, 0, out_buf, ctypes.byref(out_len))
                                rfid_sdk.UHF_RFID_Close(self.com_port)
                                fc_log("constant_power", "Commands stopped, port released (no beep)")
                        finally:
                            self.port_manager.release_port("PowerConstant")
                except Exception as e:
                    fc_log("constant_power", f"Cleanup error: {e}")
            
            fc_log("constant_power", "Cleanup done")
        except:
            pass
        
        try:
            super().destroy()
        except:
            pass
    
    def _update_license_limits(self):
        """Atualiza os limites de licença e a interface visual"""
        if self.app_shell and hasattr(self.app_shell, '_calculate_license_limits'):
            try:
                # Usa o mesmo padrão do Threshold que funciona corretamente
                license_limits = self.app_shell._calculate_license_limits(["PowerConstant", "FastChecker"])
                if license_limits.get('is_licensed', False):
                    self.license_limits = license_limits
                    fc_log("constant_power", f"License limits updated freq={license_limits.get('min_freq')}-{license_limits.get('max_freq')} MHz power={license_limits.get('min_power')}-{license_limits.get('max_power')} dBm")
                    fc_log("constant_power", f"Active license: '{license_limits.get('license_name', 'N/A')}'")
                    
                    # Atualiza labels visuais se existirem
                    if hasattr(self, 'freq_limits_label'):
                        min_freq = self.license_limits.get('min_freq', 800)
                        max_freq = self.license_limits.get('max_freq', 1000)
                        self.freq_limits_label.config(text=f"({min_freq}-{max_freq})")
                        
                    if hasattr(self, 'power_limits_label'):
                        min_power = self.license_limits.get('min_power', 5)
                        max_power = self.license_limits.get('max_power', 20)
                        self.power_limits_label.config(text=f"({min_power}-{max_power})")
                        
                else:
                    fc_log("constant_power", "No active license during limits update")
            except Exception as e:
                pass
    
    def _update_license_limits_from_app_shell(self):
        """CORREÇÃO DEFINITIVA: Atualiza limites de licença a partir do app_shell"""
        try:
            if not hasattr(self, 'app_shell') or self.app_shell is None:
                self.license_limits = {'min_freq': 800, 'max_freq': 1000, 'min_power': 5, 'max_power': 20, 'is_licensed': False}
                self._update_ui_limits()
                return
            
            # SEMPRE tenta obter limites da licença ativa
            valid_lics = ["PowerConstant", "FastChecker"]
            license_limits = self.app_shell._calculate_license_limits(valid_lics)
            
            if license_limits.get('is_licensed', False):
                self.license_limits = license_limits
            else:
                # Se não há licença, usa limites restritivos
                self.license_limits = {'min_freq': 800, 'max_freq': 1000, 'min_power': 5, 'max_power': 20, 'is_licensed': False}
            
            self._update_ui_limits()
            
        except Exception as e:
            # Em caso de erro, usa limites restritivos
            self.license_limits = {'min_freq': 800, 'max_freq': 1000, 'min_power': 5, 'max_power': 20, 'is_licensed': False}
            self._update_ui_limits()
    
    def _update_ui_limits(self):
        """Atualiza os limites na interface visual"""
        try:
            # Atualiza labels visuais se existirem
            if hasattr(self, 'freq_limits_label'):
                min_freq = self.license_limits.get('min_freq', 800)
                max_freq = self.license_limits.get('max_freq', 1000)
                self.freq_limits_label.config(text=f"({min_freq}-{max_freq})")
                
            if hasattr(self, 'power_limits_label'):
                min_power = self.license_limits.get('min_power', 5)
                max_power = self.license_limits.get('max_power', 20)
                self.power_limits_label.config(text=f"({min_power}-{max_power})")
                
        except Exception as e:
            pass
        
    def setup_ui(self):
        # Layout principal: barra lateral + área principal
        main_container = tk.Frame(self)
        main_container.pack(fill="both", expand=True, padx=5, pady=5)
        
        # Configuração do grid
        main_container.columnconfigure(1, weight=1)  # Área principal ocupa espaço restante
        main_container.rowconfigure(0, weight=1)
        
        # BARRA LATERAL (esquerda)
        self.sidebar = tk.Frame(main_container, width=300, bg='#f0f0f0')
        self.sidebar.grid(row=0, column=0, sticky="nsew", padx=(0, 5))
        self.sidebar.grid_propagate(False)  # Mantém largura fixa
        
        # ÁREA PRINCIPAL (direita)
        self.main_area = tk.Frame(main_container)
        self.main_area.grid(row=0, column=1, sticky="nsew")
        self.main_area.columnconfigure(0, weight=1)
        self.main_area.rowconfigure(0, weight=1)
        
        # Construir componentes da barra lateral
        self._build_sidebar()
        
        # Construir área principal
        self._build_main_area()

    def _build_sidebar(self):
        """Constrói a barra lateral com todas as seções"""
        # 1. Configuração do Teste
        self._build_test_config_section()
        
        # 2. Controles
        self._build_controls_section()

    def _build_test_config_section(self):
        """Seção de configuração do teste"""
        config_frame = tk.LabelFrame(self.sidebar, text=t('constant_power.test_config'), padx=10, pady=10, font=("Helvetica", 10))
        config_frame.pack(fill="x", padx=5, pady=5)
        self._widget_refs['config_frame'] = config_frame
        
        # Mensagem de modo browser dentro da Configuração do Teste
        try:
            if not hasattr(self, 'browser_frame'):
                self.browser_frame = tk.Frame(config_frame, bg='#f0f8ff')
                tk.Label(self.browser_frame, text="•", fg="blue", font=("Helvetica", 10), bg='#f0f8ff').pack(side='left', anchor='w')
                browser_mode_label = tk.Label(self.browser_frame, text=f" {t('constant_power.browser_mode')}", fg="blue", font=("Helvetica", 10), bg='#f0f8ff')
                browser_mode_label.pack(side='left', anchor='w')
                self._widget_refs['browser_mode_label'] = browser_mode_label
            if self.license_limits.get('is_licensed', False):
                self.browser_frame.pack_forget()
            else:
                self.browser_frame.pack(fill='x', pady=(0, 5))
        except Exception:
            pass
        
        # Frequência
        freq_frame = tk.Frame(config_frame)
        freq_frame.pack(fill="x", pady=2)
        freq_label = tk.Label(freq_frame, text=t('constant_power.frequency_mhz'))
        freq_label.pack(anchor="w")
        self._widget_refs['freq_label'] = freq_label
        
        freq_input_frame = tk.Frame(freq_frame)
        freq_input_frame.pack(fill="x", pady=(2, 0))
        
        self.freq_var = tk.StringVar(value=str(self.frequency))
        self.freq_entry = tk.Entry(freq_input_frame, textvariable=self.freq_var, width=10)
        self.freq_entry.pack(side="left")
        
        # Label dos limites da licença para frequência
        min_freq = self.license_limits.get('min_freq', 800)
        max_freq = self.license_limits.get('max_freq', 1000)
        self.freq_limits_label = tk.Label(freq_input_frame, text=f"({min_freq}-{max_freq})", 
                                         font=("Arial", 8), fg="blue")
        self.freq_limits_label.pack(side="left", padx=(2, 0))
        
        # Potência
        power_frame = tk.Frame(config_frame)
        power_frame.pack(fill="x", pady=2)
        power_label = tk.Label(power_frame, text=t('constant_power.power_dbm'))
        power_label.pack(anchor="w")
        self._widget_refs['power_label'] = power_label
        
        power_input_frame = tk.Frame(power_frame)
        power_input_frame.pack(fill="x", pady=(2, 0))
        
        self.power_var = tk.DoubleVar(value=self.power)
        self.power_entry = tk.Entry(power_input_frame, textvariable=self.power_var, width=10)
        self.power_entry.pack(side="left")
        self.power_entry.bind("<FocusOut>", self._on_power_entry_commit)
        self.power_entry.bind("<Return>", self._on_power_entry_commit)
        
        # Label dos limites da licença para potência
        min_power = self.license_limits.get('min_power', 5)
        max_power = self.license_limits.get('max_power', 20)
        self.power_limits_label = tk.Label(power_input_frame, text=f"({min_power}-{max_power})", 
                                          font=("Arial", 8), fg="blue")
        self.power_limits_label.pack(side="left", padx=(2, 0))
        
        # Campos removidos - não são utilizados no código

    def _build_controls_section(self):
        """Seção de controles"""
        controls_frame = tk.LabelFrame(self.sidebar, text=t('constant_power.controls'), padx=10, pady=10, font=("Helvetica", 10))
        controls_frame.pack(fill="x", padx=5, pady=5)
        self._widget_refs['controls_frame'] = controls_frame
        
        # Botão de teste
        self.start_button = tk.Button(controls_frame, text=t('constant_power.test'), command=self.start_test, font=("Arial", 10))
        self.start_button.pack(fill="x", pady=2)
        self._widget_refs['start_button'] = self.start_button
        
        # Botão de parar
        self.stop_button = tk.Button(controls_frame, text=t('constant_power.stop'), command=self.stop_test, font=("Arial", 10, "bold"), state="disabled")
        self.stop_button.pack(fill="x", pady=2)
        self._widget_refs['stop_button'] = self.stop_button
        
        # Botão de limpar
        self.clear_button = tk.Button(controls_frame, text=t('constant_power.clear_graph'), command=self.clear_graph, font=("Arial", 10))
        self.clear_button.pack(fill="x", pady=2)
        self._widget_refs['clear_button'] = self.clear_button
    
    def refresh_language(self):
        """Atualiza todos os textos da interface quando o idioma é alterado"""
        try:
            # Atualiza títulos de frames
            if 'config_frame' in self._widget_refs:
                self._widget_refs['config_frame'].config(text=t('constant_power.test_config'))
            if 'controls_frame' in self._widget_refs:
                self._widget_refs['controls_frame'].config(text=t('constant_power.controls'))
            if 'graph_frame' in self._widget_refs:
                self._widget_refs['graph_frame'].config(text=t('constant_power.graph_title'))
            
            # Atualiza labels
            if 'freq_label' in self._widget_refs:
                self._widget_refs['freq_label'].config(text=t('constant_power.frequency_mhz'))
            if 'power_label' in self._widget_refs:
                self._widget_refs['power_label'].config(text=t('constant_power.power_dbm'))
            if 'browser_mode_label' in self._widget_refs:
                self._widget_refs['browser_mode_label'].config(text=f" {t('constant_power.browser_mode')}")
            
            # Atualiza botões
            if 'start_button' in self._widget_refs:
                # Só atualiza se não estiver desabilitado e em teste
                if self.start_button.cget('state') != 'disabled':
                    self._widget_refs['start_button'].config(text=t('constant_power.test'))
            if 'stop_button' in self._widget_refs:
                # Atualiza baseado no estado do botão
                current_text = self.stop_button.cget('text')
                if 'Pausar' in current_text or 'Pause' in current_text:
                    self.stop_button.config(text=t('constant_power.pause'))
                elif 'Continuar' in current_text or 'Resume' in current_text:
                    self.stop_button.config(text=t('constant_power.resume'))
                elif 'Parar' in current_text or 'Stop' in current_text:
                    self.stop_button.config(text=t('constant_power.stop'))
            if 'clear_button' in self._widget_refs:
                self._widget_refs['clear_button'].config(text=t('constant_power.clear_graph'))
        except Exception as e:
            fc_log("constant_power", f"refresh_language error: {e}")

    def _build_main_area(self):
        """Constrói a área principal com gráfico"""
        # Área do gráfico
        self._build_graph_area()

    def _build_graph_area(self):
        """Constrói a área do gráfico"""
        graph_frame = tk.LabelFrame(self.main_area, text=t('constant_power.graph_title'), padx=10, pady=10, font=("Helvetica", 10))
        graph_frame.grid(row=0, column=0, sticky="nsew", padx=5, pady=5)
        self._widget_refs['graph_frame'] = graph_frame
        graph_frame.columnconfigure(0, weight=1)
        graph_frame.rowconfigure(0, weight=1)
        
        self.fig = Figure(figsize=(10, 6), dpi=100)
        self.ax = self.fig.add_subplot(111)
        self.ax.set_xlabel("Tempo (s)")
        self.ax.set_ylabel("RSSI (dBm)")
        self.ax.grid(True, alpha=0.3)
        # Escala inicial desejada: janela de tempo padrão e RSSI de -85 a -10 dBm
        self.ax.set_xlim(0, 60)
        self.ax.set_ylim(-85, -10)
        
        self.canvas = FigureCanvasTkAgg(self.fig, master=graph_frame)
        self.canvas.draw()
        self.canvas.get_tk_widget().grid(row=0, column=0, sticky="nsew")

    def _initial_reader_cleanup(self):
        def cleanup_worker():
            fc_log("constant_power", "Initial silent cleanup running")
            with self.com_port_lock:
                ### ALTERADO: usa self.com_port ###
                if self.port_manager.acquire_port("PowerConstant", timeout=2.0):
                    acquired = True
                else:
                    acquired = False
                
                if acquired:
                    # CORREÇÃO: Fechar porta primeiro para garantir estado limpo
                    try:
                        rfid_sdk.UHF_RFID_Close(self.com_port)
                        time.sleep(0.05)  # Pequeno delay para garantir que a porta foi fechada
                    except:
                        pass  # Ignora erro se porta já estiver fechada
                    
                    # Tentar abrir a porta (com retry)
                    port_opened = False
                    for attempt in range(3):
                        if rfid_sdk.UHF_RFID_Open(self.com_port, BAUD_RATE) == 0:
                            port_opened = True
                            break
                        else:
                            if attempt < 2:
                                time.sleep(0.1)  # Aguarda antes de tentar novamente
                    
                    if port_opened:
                        try:
                            out_buf = ctypes.create_string_buffer(64)
                            out_len = ctypes.c_uint(0)
                            rfid_sdk.UHF_RFID_Set(RFID_CMD_STOP_INVENTORY, None, 0, out_buf, ctypes.byref(out_len))
                            rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_CW_STATUS, ctypes.c_char_p(b'\x00'), 1, out_buf, ctypes.byref(out_len))
                            # CORREÇÃO: Status contextual baseado na licença
                            is_licensed = self.license_limits.get('is_licensed', False)
                            if is_licensed:
                                fc_log("constant_power", "Initial cleanup done (licensed).")
                            else:
                                fc_log("constant_power", "Browser mode active.")
                        finally:
                            ### ALTERADO: usa self.com_port ###
                            try:
                                rfid_sdk.UHF_RFID_Close(self.com_port)
                            except:
                                pass
                            if acquired:
                                self.port_manager.release_port("PowerConstant")
                    else:
                        # Falha ao abrir porta - libera o port_manager
                        if acquired:
                            self.port_manager.release_port("PowerConstant")
        threading.Thread(target=cleanup_worker, daemon=True).start()

        

        
    def _update_power_entry_state(self):
        """Atualiza o estado da caixa de entrada de potência baseado no estado do teste"""
        if hasattr(self, 'power_entry'):
            if self.test_paused:
                # Quando pausado, permite edição
                self.power_entry.config(state='normal')
            else:
                # Quando rodando, bloqueia edição
                self.power_entry.config(state='disabled')
    
    def update_ui_state(self):
        """Atualiza o estado da UI baseado na licença"""
        try:
            is_licensed = self.license_limits.get('is_licensed', False)
            
            # Status baseado na licença
            
            if is_licensed:
                # Com licença, botões ficam habilitados (respeitando outras condições)
                if not self.test_running:
                    self.start_button.config(state='normal')
                self.clear_button.config(state='normal')
                
                # Campos de entrada habilitados com licença
                self.freq_entry.config(state='normal')
                if not self.test_running or self.test_paused:
                    self.power_entry.config(state='normal')
                    
            else:
                # Sem licença (modo browser): bloquear apenas ações que acionam o reader
                self.start_button.config(state='disabled')
                self.stop_button.config(state='disabled')
                self.clear_button.config(state='normal')  # Sempre permite limpar
                
                # Permitir edição de campos em modo browser
                self.freq_entry.config(state='normal')
                if not self.test_running or self.test_paused:
                    self.power_entry.config(state='normal')
                else:
                    self.power_entry.config(state='disabled')
                
        except Exception as e:
            pass
        

    
    def _update_epc_display(self, epcs):
        """Atualiza a caixa de exibição do EPC com as tags detectadas"""
        
        if hasattr(self, 'epc_text'):
            self.epc_text.config(state='normal')
            self.epc_text.delete(1.0, tk.END)
            
            if epcs:
                # Formata os EPCs detectados
                epc_text = f"Tags detectadas ({len(epcs)}):\n"
                for i, epc in enumerate(epcs, 1):
                    epc_text += f"{i}. {epc}\n"
                
                # Insere o texto
                self.epc_text.insert(tk.END, epc_text)
                
                # Aplica cor baseada no número de tags
                if len(epcs) > 1:
                    # Múltiplas tags = VERMELHO
                    self.epc_text.tag_add("multiple_tags", "1.0", "end")
                    self.epc_text.tag_config("multiple_tags", foreground="red")
                    fc_log("constant_power", f"Multiple tags ({len(epcs)}), EPC text red")
                else:
                    # Uma tag ou sem tags = AZUL
                    self.epc_text.tag_add("single_tag", "1.0", "end")
                    self.epc_text.tag_config("single_tag", foreground="blue")
                    fc_log("constant_power", f"{len(epcs)} tag(s) detected, EPC text blue")
            else:
                # Mostra mensagem quando não há tags
                self.epc_text.insert(tk.END, "Nenhuma tag detectada (pontos vermelhos no gráfico)")
            
            self.epc_text.config(state='disabled')
    
    def _sync_power_entry(self):
        """Sincroniza a caixa de entrada de potência com o valor atual"""
        if hasattr(self, 'power_var') and hasattr(self, 'power'):
            self.power_var.set(self.power)

    def _get_license_power_bounds(self):
        return (
            float(self.license_limits.get('min_power', 5)),
            float(self.license_limits.get('max_power', 20)),
        )

    def _is_power_within_license(self, power_val):
        lo, hi = self._get_license_power_bounds()
        return lo <= float(power_val) <= hi

    def _show_power_outside_license_popup(self, power_val):
        min_p, max_p = self._get_license_power_bounds()
        messagebox.showerror(
            t('constant_power.power_outside_license'),
            t('constant_power.power_outside_license_msg').format(
                power=power_val, min_power=min_p, max_power=max_p
            ),
            parent=self,
        )

    def _on_power_entry_commit(self, event=None):
        """Bloqueia potência fora dos limites da licença ao sair do campo ou pressionar Enter."""
        if self._power_entry_validating:
            return
        if not hasattr(self, "power_entry") or not hasattr(self, "power_var"):
            return
        if str(self.power_entry.cget("state")) == "disabled":
            return

        self._power_entry_validating = True
        try:
            try:
                entered = float(self.power_var.get())
            except (TypeError, ValueError, tk.TclError):
                messagebox.showerror(
                    t('constant_power.invalid_values'),
                    t('constant_power.invalid_values_msg'),
                    parent=self,
                )
                self.power_var.set(self.power)
                return

            if not self._is_power_within_license(entered):
                self._show_power_outside_license_popup(entered)
                self.power_var.set(self.power)
                return

            self.power = entered
        finally:
            self._power_entry_validating = False
        if event is not None and getattr(event, "keysym", None) == "Return":
            return "break"
        return None

    def start_test(self):
        if self.demo_mode:
            messagebox.showinfo(t('constant_power.demo_mode'), t('constant_power.demo_mode_msg'))
            return
        
        # PROTEÇÃO DE TEMPERATURA: Verifica antes de iniciar
        temp = self.get_temperature()
        if temp is not None and temp >= TEMPERATURE_LIMIT:
            messagebox.showerror(t('constant_power.safety_stop_temp'), t('constant_power.safety_stop_temp_msg').format(temp=temp, limit=TEMPERATURE_LIMIT), parent=self)
            return
        
        # PROTEÇÃO DE POTÊNCIA REFLETIDA: Verifica antes de iniciar
        fc_log("constant_power", "Checking reflected power before test")
        reflected_power = self.get_reflected_power()
        fc_log("constant_power", f"Reflected power={reflected_power} dBm limit={REFLECTED_POWER_LIMIT} dBm")
        if reflected_power is not None and reflected_power > REFLECTED_POWER_LIMIT:
            messagebox.showerror(t('constant_power.reflected_power_high'), t('constant_power.reflected_power_high_msg').format(power=reflected_power, limit=REFLECTED_POWER_LIMIT), parent=self)
            fc_log("constant_power", "Test blocked: reflected power too high")
            return
        else:
            fc_log("constant_power", "Reflected power OK, test allowed")
        
        # NOVO: Validação dos limites da licença
        if not self.license_limits.get('is_licensed', False):
            messagebox.showerror(t('constant_power.invalid_license'), t('constant_power.invalid_license_msg'), parent=self)
            return
            
        if not HARDWARE_AVAILABLE:
            messagebox.showerror(t('constant_power.hardware_unavailable'), t('constant_power.hardware_unavailable_msg'))
            return
        try:
            if self.test_running and not self.test_paused:
                # Se está rodando, pausa
                self.toggle_test()
                return
            
            self.frequency = float(self.freq_var.get())
            # SEMPRE usa a potência da caixa de entrada
            self.power = float(self.power_var.get())
            
            # DEBUG: Log dos valores lidos
            
            # CORREÇÃO: Validação da frequência contra múltiplas faixas da licença
            min_freq = self.license_limits.get('min_freq', 800)
            max_freq = self.license_limits.get('max_freq', 1000)
            
            # DEBUG: Mostra os limites atuais da licença para frequência
            
            # Validação contra múltiplas faixas
            freq_valid = False
            freq_ranges = self.license_limits.get('freq_ranges', [])
            excluded_ranges = self.license_limits.get('excluded_ranges', [])
            
            if freq_ranges:
                # Valida contra faixas específicas
                for i, (range_min, range_max) in enumerate(freq_ranges):
                    if range_min <= self.frequency <= range_max:
                        # Verifica se não está em uma faixa excluída
                        in_excluded = False
                        for excl_min, excl_max in excluded_ranges:
                            if excl_min <= self.frequency <= excl_max:
                                in_excluded = True
                                break
                        
                        if not in_excluded:
                            freq_valid = True
                            break
                        else:
                            pass
                    else:
                        pass
            else:
                # Fallback para validação simples
                freq_valid = (min_freq <= self.frequency <= max_freq)
            
            if not freq_valid:
                ranges_str = " ou ".join([f"{r[0]}-{r[1]} MHz" for r in freq_ranges]) if freq_ranges else f"{min_freq}-{max_freq} MHz"
                excluded_str = " e ".join([f"{r[0]}-{r[1]} MHz" for r in excluded_ranges]) if excluded_ranges else "nenhuma"
                messagebox.showerror(t('constant_power.freq_outside_license'), t('constant_power.freq_outside_license_msg').format(freq=self.frequency, allowed=ranges_str, excluded=excluded_str))
                return
            
            # CORREÇÃO: Validação da potência contra limites da licença
            if not self._is_power_within_license(self.power):
                self._show_power_outside_license_popup(self.power)
                return
            
            # Se não está pausado, limpa os dados (novo teste)
            if not self.test_paused:
                self.time_data.clear(); self.rssi_data.clear(); self.tag_detected_data.clear()
                while not self.data_queue.empty(): self.data_queue.get()
                self.test_start_time = time.time()
                self.paused_time = 0
            
            self.test_running = True
            self.test_paused = False
            self.start_button.config(state="disabled")
            self.stop_button.config(state="normal", text=t('constant_power.pause'), command=self.toggle_test)
            
            # Desabilita edição da potência quando o teste inicia
            self._update_power_entry_state()
            
            self.reading_thread = threading.Thread(target=self.rfid_interrogation_worker, daemon=True)
            self.reading_thread.start()
            
        except ValueError: messagebox.showerror(t('constant_power.invalid_values'), t('constant_power.invalid_values_msg'))
        except Exception as e: messagebox.showerror(t('constant_power.error_starting'), t('constant_power.error_starting_msg').format(error=str(e)))
            
    def toggle_test(self):
        """Alterna entre pausar e continuar o teste"""
        if self.test_running:
            # Pausar teste
            self.test_running = False
            self.test_paused = True
            self.pause_start_time = time.time()  # Marca início da pausa
            self.data_queue.put({'type': 'pause'})
            self.stop_button.config(text=t('constant_power.resume'), command=self.continue_test)
            
            # NOVO: Não sincroniza automaticamente - deixa o usuário controlar
            
            # Habilita edição da potência quando pausado
            self._update_power_entry_state()
        elif self.test_paused:
            # Se está pausado e clica em "Continuar", continua o teste
            self.continue_test()
    
    def continue_test(self):
        """Continua o teste pausado"""
        if self.test_paused:
            # SEMPRE lê a nova potência da caixa de entrada e aplica
            try:
                new_power = float(self.power_var.get())
            except (TypeError, ValueError, tk.TclError):
                messagebox.showerror(
                    t('constant_power.invalid_values'),
                    t('constant_power.invalid_values_msg'),
                    parent=self,
                )
                self._sync_power_entry()
                return

            if not self._is_power_within_license(new_power):
                self._show_power_outside_license_popup(new_power)
                self._sync_power_entry()
                return

            self.power = new_power
            if hasattr(self, '_set_power'):
                self._set_power(new_power)
            
            # Adiciona tempo pausado ao total
            self.paused_time += time.time() - self.pause_start_time
            self.test_paused = False
            self.test_running = True
            self.stop_button.config(text=t('constant_power.pause'), command=self.toggle_test)
            
            # Desabilita edição da potência quando rodando
            self._update_power_entry_state()
            
            # Reinicia o thread de leitura
            self.reading_thread = threading.Thread(target=self.rfid_interrogation_worker, daemon=True)
            self.reading_thread.start()
    
    def stop_test(self):
        """Para o teste completamente e reseta tudo"""
        self.test_running = False
        self.test_paused = False
        self.data_queue.put({'type': 'stop'})
        
        # CORREÇÃO: Status contextual baseado na licença
        is_licensed = self.license_limits.get('is_licensed', False)
        status_text = "Status: Aguardando..." if is_licensed else "• modo browser"
        
        self.after(100, lambda: [
            self.start_button.config(state="normal", text=t('constant_power.test')), 
            self.stop_button.config(state="disabled", text=t('constant_power.pause'), command=self.toggle_test),
            self._update_power_entry_state()  # Reabilita edição da potência
        ])
        
    def clear_graph(self):
        if self.test_running or self.test_paused: 
            self.stop_test()
        self.time_data.clear(); self.rssi_data.clear(); self.tag_detected_data.clear()
        
        # Limpa o gráfico e reconfigura
        self.ax.clear()
        self.ax.set_xlabel("Tempo (s)")
        self.ax.set_ylabel("RSSI (dBm)")
        self.ax.grid(True, alpha=0.3)
        self.ax.set_xlim(0, 60)
        self.ax.set_ylim(-85, -10)
        self.canvas.draw()
        
        # Limpa também a caixa de EPC
        if hasattr(self, 'epc_text'):
            self.epc_text.config(state='normal')
            self.epc_text.delete(1.0, tk.END)
            self.epc_text.insert(tk.END, "Nenhuma tag detectada")
            # Remove todas as tags de cor
            self.epc_text.tag_remove("multiple_tags", "1.0", "end")
            self.epc_text.tag_remove("single_tag", "1.0", "end")
            self.epc_text.config(state='disabled')
        
        # Limpa os conjuntos de EPC
        self.previous_epcs.clear()
        self.current_epcs.clear()
        
        # Habilita entrada de potência (força habilitação)
        if hasattr(self, 'power_entry'):
            self.power_entry.config(state='normal')
    
    def _check_epc_timeout(self):
        """Verifica periodicamente se os EPCs ainda estão válidos"""
        if hasattr(self, 'epc_text') and self.test_running:
            # Se não há EPCs atuais mas a caixa ainda mostra tags, força limpeza
            if not self.current_epcs:
                self._update_epc_display([])
        
        # Agenda próxima verificação
        if self.test_running:
            self.after(1000, self._check_epc_timeout)
        
    def rfid_interrogation_worker(self):
        """Worker thread que executa o teste - com retry robusto para garantir abertura da porta"""
        try:
            # CORREÇÃO: Tenta fechar a porta silenciosamente antes de abrir (garante estado limpo)
            # Isso ajuda quando você vai direto para o módulo sem passar por outros módulos
            try:
                rfid_sdk.UHF_RFID_Close(self.com_port)
                time.sleep(0.1)  # Delay maior para garantir que a porta foi fechada
            except:
                pass  # Ignora erro se porta já estiver fechada ou não estiver aberta
            
            # CORREÇÃO: Retry robusto para abrir a porta (até 3 tentativas com delay)
            # Isso garante que a porta seja aberta mesmo se houver algum problema temporário
            port_opened = False
            for attempt in range(3):
                if rfid_sdk.UHF_RFID_Open(self.com_port, BAUD_RATE) == 0:
                    port_opened = True
                    fc_log("constant_power", f"COM{self.com_port} open for test (attempt {attempt + 1})")
                    break
                else:
                    if attempt < 2:
                        # Se falhou, tenta fechar novamente antes da próxima tentativa
                        try:
                            rfid_sdk.UHF_RFID_Close(self.com_port)
                        except:
                            pass
                        time.sleep(0.3)  # Delay maior entre tentativas
            
            if not port_opened:
                self.data_queue.put({'error': f'Falha ao abrir a porta COM{self.com_port} após 3 tentativas'})
                return
            
            try:
                if not self._set_region_brazil():
                    fc_log("constant_power", "Warning: failed to set Brazil region.")
                
                if not self._set_frequency(self.frequency) or not self._set_power(self.power):
                    self.data_queue.put({'error': 'Falha ao configurar freq/potência'}); return
                
                # Usa o tempo de início já definido ou define um novo
                if self.test_start_time is None:
                    self.test_start_time = time.time()
                
                while self.test_running:
                    # Calcula tempo considerando pausas
                    current_time = time.time()
                    elapsed_time = current_time - self.test_start_time - self.paused_time
                    rssi_values = []
                    
                    # NOVO: Atualiza EPCs do ciclo anterior
                    self.previous_epcs = self.current_epcs.copy()
                    self.current_epcs.clear()
                    
                    for _ in range(self.samples_per_point):
                        if not self.test_running: break
                        tags = self._scan_for_tags_internal()
                        for tag in tags:
                            if hasattr(tag, 'rssi') and tag.rssi is not None:
                                rssi_values.append(tag.rssi)
                            # Captura o EPC da tag
                            if hasattr(tag, 'epc') and tag.epc:
                                self.current_epcs.add(tag.epc)
                                # print(f"🔍 DEBUG: EPC adicionado: {tag.epc}")
                        time.sleep(self.pause_between_samples)
                    
                    # print(f"🔍 DEBUG: EPCs coletados neste ciclo: {self.current_epcs}")
                    
                    # SEMPRE envia dados, com RSSI real ou valor padrão
                    if rssi_values:
                        avg_rssi = sum(rssi_values) / len(rssi_values)
                        fc_log("constant_power", f"RSSI avg={avg_rssi:.1f} dBm")
                    else:
                        # Quando não há tags detectadas, usa -70 dBm
                        avg_rssi = -70.0
                        fc_log("constant_power", f"No tags, plotting {avg_rssi} dBm")
                    
                    # Envia dados incluindo EPCs detectados e status de mudança
                    epc_list = list(self.current_epcs) if self.current_epcs else []
                    epcs_changed = self.current_epcs != self.previous_epcs
                    # print(f"🔍 DEBUG: epc_list: {epc_list}")
                    # print(f"🔍 DEBUG: epcs_changed: {epcs_changed}")
                    # print(f"🔍 DEBUG: previous_epcs: {list(self.previous_epcs)}")
                    
                    tag_detected = len(rssi_values) > 0
                    self.data_queue.put({
                        'type': 'update', 
                        'time': elapsed_time, 
                        'rssi': avg_rssi,
                        'tag_detected': tag_detected,
                        'epcs': epc_list,
                        'epcs_changed': epcs_changed,
                        'previous_epcs': list(self.previous_epcs)
                    })
                    
                    time.sleep(self.pause_between_points)
            finally:
                fc_log("constant_power", "Closing COM port at end of worker.")
                ### ALTERADO: usa self.com_port ###
                try:
                    rfid_sdk.UHF_RFID_Close(self.com_port)
                except:
                    pass
        except Exception as e:
            self.data_queue.put({'error': f'Erro no teste: {str(e)}'})
        
    def process_data_queue(self):
        try:
            message = self.data_queue.get_nowait()
            msg_type = message.get('type')
            
            if msg_type == 'update':
                # SEMPRE adiciona dados (RSSI real ou -70 dBm)
                self.time_data.append(message['time'])
                self.rssi_data.append(message['rssi'])
                self.tag_detected_data.append(message.get('tag_detected', True))
                self.update_plot()
                
                # Atualiza a caixa de EPC com as tags detectadas
                if 'epcs' in message:
                    epcs = message['epcs']
                    epcs_changed = message.get('epcs_changed', False)
                    
                    # Atualiza a caixa de EPC com as tags detectadas
                    
                    # SEMPRE atualiza a exibição quando há mudança ou quando não há tags
                    if epcs_changed or not epcs:
                        self._update_epc_display(epcs)
                        
                        # Log das mudanças para debug
                        if epcs:
                            fc_log("constant_power", f"EPCs detected: {epcs}")
                        else:
                            fc_log("constant_power", "EPC cleared, no tag")
                    
                    # Força atualização se não há tags (mesmo sem mudança)
                    if not epcs and epcs != []:
                        self._update_epc_display([])
            elif msg_type == 'pause':
                # Teste pausado - não faz nada, apenas para sair do loop
                pass
            elif msg_type == 'stop':
                self.test_running = False
                self.test_paused = False
            elif 'error' in message:
                self.test_running = False
                self.test_paused = False
                messagebox.showerror(t('constant_power.test_error_title'), message['error'])
        except queue.Empty:
            pass
        self.after(50, self.process_data_queue)
            
    def update_plot(self):
        if not self.time_data or not self.rssi_data: return
        self.ax.clear()
        self.ax.set_xlabel('Tempo (s)'); self.ax.set_ylabel('RSSI (dBm)')
        self.ax.grid(True, alpha=0.3); self.ax.set_ylim(-85, -10)
        
        # Se o teste está pausado, não atualiza a janela de tempo
        if self.test_paused:
            current_time = max(self.time_data) if self.time_data else 0
            window_start = max(0, current_time - 60)
        else:
            # Teste rodando - janela de tempo se move
            current_time = max(self.time_data) if self.time_data else 0
            window_start = max(0, current_time - 60)
        
        # Dados dentro da janela; tag_detected_data pode ser menor em testes antigos
        tag_detected = getattr(self, 'tag_detected_data', None) or [True] * len(self.time_data)
        if len(tag_detected) != len(self.time_data):
            tag_detected = [True] * len(self.time_data)
        
        valid_with_flag = [
            (t, r, detected)
            for t, r, detected in zip(self.time_data, self.rssi_data, tag_detected)
            if r is not None and t >= window_start
        ]
        
        if valid_with_flag:
            adjusted = [(t - window_start, r, d) for t, r, d in valid_with_flag]
            times_ok = [a[0] for a in adjusted if a[2]]
            rssis_ok = [a[1] for a in adjusted if a[2]]
            times_no_tag = [a[0] for a in adjusted if not a[2]]
            rssis_no_tag = [a[1] for a in adjusted if not a[2]]
            self.ax.set_xlim(0, 60)
            # Curva azul ligada: pontos onde a tag foi lida
            if times_ok:
                self.ax.plot(times_ok, rssis_ok, 'b-o', linewidth=0.8, markersize=2, label='RSSI (tag lida)')
            # Pontos vermelhos não conectados (apenas scatter, sem linha): tag não detectada
            if times_no_tag:
                self.ax.scatter(times_no_tag, rssis_no_tag, c='red', s=45, marker='o', zorder=5,
                               edgecolors='darkred', linewidths=0.8, label='Tag não detectada')
        else:
            self.ax.set_xlim(0, 60)

        self.canvas.draw()
        
    def _set_power(self, power_dbm):
        fc_log("constant_power", f"_set_power called: {power_dbm} dBm")
        try:
            # Implementação corrigida - usa a mesma implementação do rssi_power_module
            power_val = int(power_dbm * 100)
            power_data = bytes([0, 0]) + power_val.to_bytes(2, 'big') * 2
            out_buf = ctypes.create_string_buffer(32)
            out_len = ctypes.c_uint(0)
            status = rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_TXPOWER, ctypes.c_char_p(power_data), 6, out_buf, ctypes.byref(out_len))
            fc_log("constant_power", f"_set_power status={status} ok={status == 0} power_val={power_val} power_data={power_data.hex()}")
            return status == 0
        except Exception as e:
            fc_log("constant_power", f"_set_power error: {e}")
            return False

    def _set_frequency(self, freq_mhz):
        freq_data = (1).to_bytes(1, 'big') + int(freq_mhz * 1000).to_bytes(3, 'big')
        out_buf = ctypes.create_string_buffer(32)
        out_len = ctypes.c_uint(0)
        status = rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_FREQ_TABLE, ctypes.c_char_p(freq_data), 4, out_buf, ctypes.byref(out_len))
        return status == 0

    def _set_region_brazil(self):
        region_data = bytes([0x01, 0x06])
        out_buf = ctypes.create_string_buffer(32)
        out_len = ctypes.c_uint(0)
        status = rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_REGION, ctypes.c_char_p(region_data), len(region_data), out_buf, ctypes.byref(out_len))
        return status == 0

    def _scan_for_tags_internal(self):
        tags_found = []
        out_buf = ctypes.create_string_buffer(256)
        out_len = ctypes.c_uint(0)
        status = rfid_sdk.UHF_RFID_Set(RFID_CMD_INV_TAG, bytes([0x00, 0x64]), 2, out_buf, ctypes.byref(out_len))
        
        if status == 0 and out_len.value > 5:
            try:
                epc = out_buf.raw[2:out_len.value - 3].hex().upper()
                rssi_bytes = out_buf.raw[out_len.value - 3:out_len.value - 1]
                rssi = struct.unpack('>h', rssi_bytes)[0] / 10.0
                
                if -100 <= rssi <= 0:
                    tags_found.append(TagInfo(epc, rssi))
            except (struct.error, IndexError) as e:
                fc_log("constant_power", f"Tag response decode error: {e}")
                
        return tags_found

if __name__ == '__main__':
    root = tk.Tk()
    root.title("Teste de Constant Power (Standalone)")
    root.geometry("800x600")

    from port_manager import get_com_port_number
    com_port = get_com_port_number()
    if com_port is None:
        messagebox.showerror(t('constant_power.hardware_unavailable'), t('constant_power.hardware_unavailable_msg'))
        root.destroy()
    else:
        app = PowerConstantModule(root, com_port=com_port)
        app.pack(fill="both", expand=True)
        root.mainloop()