306 lines
12 KiB
Python
306 lines
12 KiB
Python
import tkinter as tk
|
|
from tkinter import filedialog, messagebox, ttk, scrolledtext
|
|
from view_parameters import ViewParametersDialog
|
|
from edc15_info_window import EDC15InfoWindow
|
|
import struct
|
|
import os
|
|
import logging
|
|
from logger import logger # Use the centralized logger
|
|
|
|
# Create a logger
|
|
# logger = logging.getLogger(__name__)
|
|
|
|
class FlashWindow:
|
|
def __init__(self, master, main_window):
|
|
try:
|
|
logger.info("Initializing FlashWindow")
|
|
|
|
# Validate input
|
|
if master is None:
|
|
raise ValueError("Master window cannot be None")
|
|
|
|
# Store references
|
|
self.parent = master
|
|
self.main_window = main_window # Referência à janela principal
|
|
|
|
# Create window
|
|
self.window = tk.Toplevel(master)
|
|
self.window.title("Flash File - EDC15")
|
|
self.window.geometry("800x600")
|
|
|
|
# Minimizar janela principal quando abrir Flash
|
|
self.main_window.iconify()
|
|
|
|
# Configurar para restaurar janela principal quando fechar
|
|
self.window.protocol("WM_DELETE_WINDOW", self.on_close)
|
|
|
|
# Add a frame to the new window
|
|
self.flash_frame = ttk.Frame(self.window)
|
|
self.flash_frame.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)
|
|
|
|
# Create buttons frame
|
|
self.buttons_frame = ttk.Frame(self.flash_frame)
|
|
self.buttons_frame.pack(fill=tk.X, pady=(0, 10))
|
|
|
|
# Add Open and Save buttons
|
|
ttk.Button(self.buttons_frame, text="New Project", command=self.open_file).pack(side=tk.LEFT, padx=5)
|
|
ttk.Button(self.buttons_frame, text="Save File", command=self.save_file).pack(side=tk.LEFT, padx=5)
|
|
ttk.Button(self.buttons_frame, text="Parâmetros", command=self.show_parameters).pack(side=tk.LEFT, padx=5)
|
|
|
|
# Botão para alternar entre Hexa e Decimal
|
|
self.btn_view_mode = ttk.Button(self.buttons_frame, text="Hexa", command=self.toggle_view_mode)
|
|
self.btn_view_mode.pack(side=tk.LEFT, padx=5)
|
|
|
|
# Botão 2D
|
|
self.btn_2d_view = ttk.Button(self.buttons_frame, text="2D", command=self.open_2d_graph)
|
|
self.btn_2d_view.pack(side=tk.LEFT, padx=5)
|
|
|
|
# File path label
|
|
self.file_path = tk.StringVar()
|
|
self.file_path_label = ttk.Label(self.flash_frame, textvariable=self.file_path, wraplength=780)
|
|
self.file_path_label.pack(fill=tk.X, pady=5)
|
|
|
|
# Create text area for binary content
|
|
self.content_frame = ttk.LabelFrame(self.flash_frame, text="Binary Content")
|
|
self.content_frame.pack(fill=tk.BOTH, expand=True, pady=5)
|
|
|
|
self.text_area = scrolledtext.ScrolledText(self.content_frame, wrap=tk.WORD, width=80, height=20)
|
|
self.text_area.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
|
self.text_area.config(font=('Courier', 10))
|
|
|
|
# Store binary data and view parameters
|
|
self.binary_data = None
|
|
self.view_params = {
|
|
'first_cell': '0000F0',
|
|
'offset': 0,
|
|
'columns': 16,
|
|
'hex_view': False,
|
|
'show_diff': True,
|
|
'value_type': '8bit',
|
|
'address_type': '8bit'
|
|
}
|
|
|
|
logger.info("FlashWindow initialized successfully")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error initializing FlashWindow: {e}", exc_info=True)
|
|
messagebox.showerror("Initialization Error", str(e))
|
|
if hasattr(self, 'window'):
|
|
self.window.destroy()
|
|
|
|
def format_value(self, data, start_idx, value_type, hex_view):
|
|
try:
|
|
# Primeiro, garantir que temos bytes suficientes
|
|
if value_type == '8bit' and len(data) < 1:
|
|
return "?" * (2 if hex_view else 3)
|
|
elif value_type == '16bit' and len(data) < 2:
|
|
return "?" * (4 if hex_view else 5)
|
|
elif (value_type == '32bit' or value_type == 'float') and len(data) < 4:
|
|
return "?" * (8 if hex_view else 10)
|
|
|
|
# Agora processar o valor
|
|
if value_type == '8bit':
|
|
value = data[0]
|
|
if hex_view:
|
|
return f"{value:02X}"
|
|
return f"{value:03d}"
|
|
|
|
elif value_type == '16bit':
|
|
value = struct.unpack('>H', data[:2])[0] # big-endian
|
|
if hex_view:
|
|
return f"{value:04X}"
|
|
return f"{value:05d}"
|
|
|
|
elif value_type == '32bit':
|
|
value = struct.unpack('>I', data[:4])[0] # big-endian
|
|
if hex_view:
|
|
return f"{value:08X}"
|
|
return f"{value:010d}"
|
|
|
|
elif value_type == 'float':
|
|
value = struct.unpack('>f', data[:4])[0] # big-endian
|
|
return f"{value:10.4f}"
|
|
|
|
except (IndexError, struct.error) as e:
|
|
return "?" * (2 if hex_view else 3)
|
|
|
|
def get_value_size(self, value_type):
|
|
if value_type == '8bit':
|
|
return 1
|
|
elif value_type == '16bit':
|
|
return 2
|
|
else: # 32bit ou float
|
|
return 4
|
|
|
|
def show_parameters(self):
|
|
dialog = ViewParametersDialog(self.window, self.view_params)
|
|
result = dialog.show()
|
|
if result:
|
|
self.view_params = result
|
|
self.display_binary_data()
|
|
|
|
def open_file(self):
|
|
"""Open and display a binary file"""
|
|
try:
|
|
file_path = filedialog.askopenfilename(
|
|
filetypes=[("Binary files", "*.bin"), ("All files", "*.*")]
|
|
)
|
|
|
|
if file_path:
|
|
logger.info(f"Opening file in Flash window: {file_path}")
|
|
|
|
# Read file data
|
|
with open(file_path, 'rb') as file:
|
|
self.binary_data = file.read()
|
|
|
|
# Show EDC15 info window with binary data
|
|
info_window = EDC15InfoWindow(self.window, os.path.basename(file_path), self.binary_data)
|
|
info_window.transient(self.window)
|
|
info_window.grab_set()
|
|
self.window.wait_window(info_window)
|
|
|
|
# Check if project was created or window was closed
|
|
if not info_window.project_created or info_window.window_closed:
|
|
# Reset binary data and file path if project not created
|
|
self.binary_data = None
|
|
self.file_path.set("")
|
|
self.text_area.delete('1.0', tk.END)
|
|
return
|
|
|
|
# Display file content
|
|
self.file_path.set(f"Selected file: {file_path}")
|
|
self.display_binary_data()
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error opening file: {e}", exc_info=True)
|
|
messagebox.showerror("Error", f"Error opening file: {e}")
|
|
|
|
def display_binary_data(self):
|
|
if self.binary_data:
|
|
self.text_area.delete('1.0', tk.END)
|
|
|
|
# Get parameters
|
|
bytes_per_row = self.view_params['columns']
|
|
start_offset = self.view_params['offset']
|
|
hex_view = self.view_params['hex_view']
|
|
value_type = self.view_params['value_type']
|
|
|
|
# Update frame title
|
|
mode_text = "Hexadecimal" if hex_view else "Decimal"
|
|
value_text = f"{value_type} values"
|
|
if value_type == 'float':
|
|
value_text = "Floating point values"
|
|
self.content_frame.configure(text=f"Binary Content - {mode_text} ({value_text})")
|
|
|
|
# Calculate display range
|
|
display_data = self.binary_data[start_offset:]
|
|
|
|
# Get the size of each value based on type
|
|
value_size = self.get_value_size(value_type)
|
|
values_per_row = bytes_per_row // value_size
|
|
|
|
for i in range(0, len(display_data), bytes_per_row):
|
|
# Format offset (from the first_cell value)
|
|
base_offset = int(self.view_params['first_cell'], 16)
|
|
current_offset = base_offset + i
|
|
offset = f"{current_offset:08X}"
|
|
|
|
# Get row data
|
|
row_data = display_data[i:i + bytes_per_row]
|
|
|
|
# Format values
|
|
values = []
|
|
ascii_values = []
|
|
|
|
# Process each value in the row
|
|
for j in range(0, len(row_data), value_size):
|
|
value_data = row_data[j:j + value_size]
|
|
if len(value_data) == value_size:
|
|
# Format the value according to type
|
|
value_str = self.format_value(value_data, j, value_type, hex_view)
|
|
values.append(value_str)
|
|
|
|
# ASCII representation (only for 8-bit values)
|
|
if value_type == '8bit':
|
|
b = value_data[0]
|
|
ascii_values.append(chr(b) if 32 <= b <= 126 else '.')
|
|
else:
|
|
ascii_values.extend(['.' for _ in range(value_size)])
|
|
|
|
# Join values and ASCII
|
|
values_part = " ".join(values)
|
|
ascii_part = "".join(ascii_values)
|
|
|
|
# Add the line to display
|
|
line = f"{offset}: {values_part} | {ascii_part}\n"
|
|
self.text_area.insert(tk.END, line)
|
|
|
|
self.text_area.see('1.0') # Scroll to top
|
|
|
|
def save_file(self):
|
|
if not self.binary_data:
|
|
messagebox.showwarning("Warning", "No data to save. Please open a file first.")
|
|
return
|
|
|
|
file_path = filedialog.asksaveasfilename(
|
|
title="Save Binary File",
|
|
defaultextension=".bin",
|
|
filetypes=[
|
|
("Binary files", "*.bin"),
|
|
("All files", "*.*")
|
|
]
|
|
)
|
|
if file_path:
|
|
try:
|
|
with open(file_path, 'wb') as f:
|
|
f.write(self.binary_data)
|
|
self.file_path.set(f"File saved to: {file_path}")
|
|
except Exception as e:
|
|
messagebox.showerror("Error", f"Error saving file: {str(e)}")
|
|
|
|
def open_2d_graph(self):
|
|
try:
|
|
# Ensure binary data is available
|
|
if not hasattr(self, 'binary_data') or self.binary_data is None:
|
|
logger.warning("No binary data available to plot graph")
|
|
messagebox.showwarning("Graph Error", "No binary data available to plot.")
|
|
return
|
|
|
|
# Import BinaryGraphWindow dynamically to avoid circular imports
|
|
from binary_graph import BinaryGraphWindow
|
|
|
|
# Convert binary data to byte values if not already done
|
|
# Ensure we're passing a list of integers
|
|
byte_values = [int(byte) for byte in self.binary_data]
|
|
|
|
# Open binary graph window
|
|
graph_window = BinaryGraphWindow(self.window, byte_values)
|
|
|
|
logger.info("Binary graph window opened successfully")
|
|
|
|
except ImportError as e:
|
|
logger.error(f"Failed to import BinaryGraphWindow: {e}")
|
|
messagebox.showerror("Import Error", "Could not load graph module.")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error opening binary graph: {e}", exc_info=True)
|
|
messagebox.showerror("Graph Error", f"Could not open graph: {e}")
|
|
|
|
def toggle_view_mode(self):
|
|
# Alternar entre hexadecimal e decimal
|
|
current_mode = self.view_params.get('hex_view', False)
|
|
self.view_params['hex_view'] = not current_mode
|
|
|
|
# Atualizar texto do botão
|
|
self.btn_view_mode.config(text="Decimal" if current_mode else "Hexa")
|
|
|
|
# Redesenhar dados
|
|
self.display_binary_data()
|
|
|
|
def on_close(self):
|
|
# Restaurar janela principal
|
|
self.main_window.deiconify()
|
|
|
|
# Fechar janela Flash
|
|
self.window.destroy()
|