import tkinter as tk from tkinter import ttk, messagebox import psycopg2 from datetime import datetime import logging import re import os class EDC15InfoWindow(tk.Toplevel): def __init__(self, parent, filename, binary_data=None): logging.info("Initializing EDC15InfoWindow") try: super().__init__(parent) self.title("EDC15 File Information") self.filename = filename self.binary_data = binary_data # Flag to track if project is created self.project_created = False # Flag to track if window was closed by user self.window_closed = False # Add protocol for window close self.protocol("WM_DELETE_WINDOW", self.on_closing) # Load ECU types and car brands from files self.ecu_types = self.load_ecu_types() self.car_brands = self.load_car_brands() # Load readout methods self.readout_methods = self.load_readout_methods() # Hardware number prefixes self.hw_prefixes = ['0281', '0265', '0255', '0275', '0235', '0285'] # Software number prefix self.sw_prefix = '103' # Lists to store all found numbers self.all_hw_numbers = [] self.all_sw_numbers = [] # Configure window self.geometry("800x600") logging.info("EDC15InfoWindow geometry configured") # Create main frame main_frame = ttk.Frame(self, padding="10") main_frame.pack(fill="both", expand=True) logging.info("EDC15InfoWindow main frame created") # Customer Section customer_frame = ttk.LabelFrame(main_frame, text="Customer", padding="5") customer_frame.pack(fill="x", padx=5, pady=5) ttk.Label(customer_frame, text="Name:").grid(row=0, column=0, sticky=tk.W) self.name_var = tk.StringVar() self.name_entry = ttk.Combobox(customer_frame, textvariable=self.name_var) self.name_entry.grid(row=0, column=1, sticky=(tk.W, tk.E)) ttk.Label(customer_frame, text="Phone:").grid(row=1, column=0, sticky=tk.W) self.phone_var = tk.StringVar() ttk.Entry(customer_frame, textvariable=self.phone_var).grid(row=1, column=1, sticky=(tk.W, tk.E)) ttk.Label(customer_frame, text="License Plate:").grid(row=2, column=0, sticky=tk.W) self.license_var = tk.StringVar() ttk.Entry(customer_frame, textvariable=self.license_var).grid(row=2, column=1, sticky=(tk.W, tk.E)) # Car Section with Comments car_frame = ttk.LabelFrame(main_frame, text="Car", padding="5") car_frame.pack(fill="x", padx=5, pady=5) # Left side - Car details car_left_frame = ttk.Frame(car_frame) car_left_frame.pack(side="left", fill="x", expand=True, padx=5) # Brand ttk.Label(car_left_frame, text="Brand:").grid(row=0, column=0, sticky="w", padx=5, pady=2) self.brand_var = tk.StringVar() self.brand_combo = ttk.Combobox(car_left_frame, textvariable=self.brand_var, values=self.car_brands) self.brand_combo.grid(row=0, column=1, sticky="ew", padx=5, pady=2) self.brand_combo.bind('<>', self.on_brand_selected) # Model ttk.Label(car_left_frame, text="Model:").grid(row=1, column=0, sticky="w", padx=5, pady=2) self.model_var = tk.StringVar() self.model_combo = ttk.Combobox(car_left_frame, textvariable=self.model_var) self.model_combo.grid(row=1, column=1, sticky="ew", padx=5, pady=2) # Engine ttk.Label(car_left_frame, text="Engine:").grid(row=2, column=0, sticky="w", padx=5, pady=2) self.engine_var = tk.StringVar() ttk.Entry(car_left_frame, textvariable=self.engine_var).grid(row=2, column=1, sticky="ew", padx=5, pady=2) # HP ttk.Label(car_left_frame, text="HP:").grid(row=3, column=0, sticky="w", padx=5, pady=2) self.hp_var = tk.StringVar() ttk.Entry(car_left_frame, textvariable=self.hp_var).grid(row=3, column=1, sticky="ew", padx=5, pady=2) # KW ttk.Label(car_left_frame, text="KW:").grid(row=4, column=0, sticky="w", padx=5, pady=2) self.kw_var = tk.StringVar() ttk.Entry(car_left_frame, textvariable=self.kw_var).grid(row=4, column=1, sticky="ew", padx=5, pady=2) # Year year_label = tk.Label(car_left_frame, text="Year:") year_label.grid(row=5, column=0, sticky='w', padx=5, pady=2) self.year_var = tk.StringVar() self.year_combo = ttk.Combobox(car_left_frame, textvariable=self.year_var, state="readonly", width=20) self.year_combo['values'] = list(map(str, range(1993, 2009))) # Years from 1993 to 2008 self.year_combo.grid(row=5, column=1, sticky='w', padx=5, pady=2) self.year_combo.set('') # Default empty selection # VIN ttk.Label(car_left_frame, text="VIN:").grid(row=6, column=0, sticky="w", padx=5, pady=2) self.vin_var = tk.StringVar() ttk.Entry(car_left_frame, textvariable=self.vin_var).grid(row=6, column=1, sticky="ew", padx=5, pady=2) # Transmission transmission_label = tk.Label(car_left_frame, text="Transmission:") transmission_label.grid(row=7, column=0, sticky='w', padx=5, pady=2) self.transmission_var = tk.StringVar() self.transmission_combo = ttk.Combobox(car_left_frame, textvariable=self.transmission_var, state="readonly", width=20) self.transmission_combo['values'] = ['Manual', 'Automatic'] self.transmission_combo.grid(row=7, column=1, sticky='w', padx=5, pady=2) self.transmission_combo.set('') # Default empty selection # Right side - Comments car_right_frame = ttk.LabelFrame(car_frame, text="Comments", padding="5") car_right_frame.pack(side="right", fill="both", expand=True, padx=5) self.comment_text = tk.Text(car_right_frame, height=10, width=30, wrap=tk.WORD) comment_scrollbar = ttk.Scrollbar(car_right_frame, orient="vertical", command=self.comment_text.yview) self.comment_text.configure(yscrollcommand=comment_scrollbar.set) self.comment_text.pack(side="left", fill="both", expand=True) comment_scrollbar.pack(side="right", fill="y") # ECU and Project Section ecu_project_frame = ttk.LabelFrame(main_frame, text="ECU and Project", padding="5") ecu_project_frame.pack(fill="x", padx=5, pady=5) # Create a frame to hold ECU and Project side by side ecu_project_inner_frame = ttk.Frame(ecu_project_frame) ecu_project_inner_frame.pack(fill="x", expand=True) # Left side - ECU details ecu_left_frame = ttk.Frame(ecu_project_inner_frame) ecu_left_frame.pack(side="left", fill="x", expand=True, padx=5) ttk.Label(ecu_left_frame, text="ECU Type:").grid(row=0, column=0, sticky="w", padx=5, pady=2) self.ecu_type_var = tk.StringVar() self.ecu_type_combo = ttk.Combobox(ecu_left_frame, textvariable=self.ecu_type_var, values=self.ecu_types) self.ecu_type_combo.grid(row=0, column=1, sticky="ew", padx=5, pady=2) ttk.Label(ecu_left_frame, text="Hardware:").grid(row=1, column=0, sticky="w", padx=5, pady=2) self.hardware_var = tk.StringVar() ttk.Entry(ecu_left_frame, textvariable=self.hardware_var).grid(row=1, column=1, sticky="ew", padx=5, pady=2) ttk.Label(ecu_left_frame, text="Software:").grid(row=2, column=0, sticky="w", padx=5, pady=2) self.software_var = tk.StringVar() ttk.Entry(ecu_left_frame, textvariable=self.software_var).grid(row=2, column=1, sticky="ew", padx=5, pady=2) # Right side - Project details project_right_frame = ttk.Frame(ecu_project_inner_frame) project_right_frame.pack(side="right", fill="x", expand=True, padx=5) ttk.Label(project_right_frame, text="Type:").grid(row=0, column=0, sticky="w", padx=5, pady=2) self.project_type_var = tk.StringVar() self.project_type_combo = ttk.Combobox(project_right_frame, textvariable=self.project_type_var) self.project_type_combo['values'] = [ 'BOOST CALIBRATION', 'COLD START', 'DPF REMOVAL', 'DTC REMOVAL', 'EGR REMOVAL', 'EXHAUST FLAP', 'FLAPS REMOVAL', 'HOT START', 'IMMO REMOVAL', 'LAMBDA', 'LAUNCH CONTROL', 'MAF REMOVAL', 'SPEED LIMITER REMOVAL', 'TUNING', 'TVA REMOVAL' ] self.project_type_combo.grid(row=0, column=1, sticky="ew", padx=5, pady=2) self.project_type_combo.config(state="readonly") ttk.Label(project_right_frame, text="State:").grid(row=1, column=0, sticky="w", padx=5, pady=2) self.state_var = tk.StringVar(value="NEW") self.state_combo = ttk.Combobox(project_right_frame, textvariable=self.state_var) self.state_combo['values'] = [ 'NEW', 'DEVELOPING', 'TESTING', 'FINISH' ] self.state_combo.grid(row=1, column=1, sticky="ew", padx=5, pady=2) self.state_combo.config(state="readonly") ttk.Label(project_right_frame, text="Readout by:").grid(row=2, column=0, sticky="w", padx=5, pady=2) self.readout_var = tk.StringVar() self.readout_combo = ttk.Combobox(project_right_frame, textvariable=self.readout_var, values=self.readout_methods) self.readout_combo.grid(row=2, column=1, sticky="ew", padx=5, pady=2) self.readout_combo.config(state="readonly") # Buttons at the bottom button_frame = ttk.Frame(main_frame) button_frame.pack(fill="x", padx=5, pady=10) ttk.Button(button_frame, text="Create Project", command=self.create_project).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="Cancel", command=self.destroy).pack(side=tk.RIGHT, padx=5) # Find hardware and software numbers if binary data is available if self.binary_data: self.find_all_numbers() self.update_comment_with_extra_numbers() except Exception as e: logging.error(f"Error initializing EDC15InfoWindow: {str(e)}") def load_ecu_types(self): """Load ECU types from list_EDC15.txt file""" try: with open('list_EDC15.txt', 'r') as file: return [line.strip() for line in file if line.strip()] except Exception as e: logging.error(f"Error loading ECU types: {str(e)}") return [] def load_car_brands(self): """Load car brands from car_brands.txt""" try: brands_file = os.path.join(os.path.dirname(__file__), 'car_brands.txt') if os.path.exists(brands_file): with open(brands_file, 'r') as f: brands = [line.strip() for line in f if line.strip()] logging.info(f"Loaded {len(brands)} car brands") return sorted(brands) else: logging.warning("car_brands.txt not found") return [] except Exception as e: logging.error(f"Error loading car brands: {str(e)}") return [] def load_readout_methods(self): """Load readout methods from readout.txt file""" try: with open(os.path.join(os.path.dirname(__file__), 'readout.txt'), 'r') as f: return [line.strip() for line in f if line.strip()] except FileNotFoundError: logging.warning("readout.txt file not found") return [] except Exception as e: logging.error(f"Error reading readout.txt: {str(e)}") return [] def find_all_numbers(self): """Find all hardware and software numbers in binary data""" try: if not self.binary_data: return # Convert binary data to ASCII string ascii_data = self.binary_data.decode('ascii', errors='ignore') # Find hardware numbers hw_pattern = '(' + '|'.join(self.hw_prefixes) + ')[0-9]{5}' hw_matches = re.finditer(hw_pattern, ascii_data) # Store all hardware numbers found for match in hw_matches: number = match.group(0) if len(number) == 9 and number.isdigit(): self.all_hw_numbers.append(number) # Set first hardware number found to the hardware field if self.all_hw_numbers: self.hardware_var.set(self.all_hw_numbers[0]) logging.info(f"Found primary hardware number: {self.all_hw_numbers[0]}") # Find software numbers sw_pattern = f'{self.sw_prefix}[0-9]{{6}}' sw_matches = re.finditer(sw_pattern, ascii_data) # Store all software numbers found for match in sw_matches: number = match.group(0) if len(number) == 9 and number.isdigit(): self.all_sw_numbers.append(number) # Set first software number found to the software field if self.all_sw_numbers: self.software_var.set(self.all_sw_numbers[0]) logging.info(f"Found primary software number: {self.all_sw_numbers[0]}") except Exception as e: logging.error(f"Error finding numbers: {str(e)}") def update_comment_with_extra_numbers(self): """Update comment field with additional numbers found, excluding the ones already shown in hardware/software fields""" try: # Get the current hardware and software numbers from the fields current_hw = self.hardware_var.get() current_sw = self.software_var.get() # Filter out numbers that are already shown in the fields extra_hw_numbers = [num for num in self.all_hw_numbers if num != current_hw] extra_sw_numbers = [num for num in self.all_sw_numbers if num != current_sw] if extra_hw_numbers or extra_sw_numbers: comment = "Additional numbers found:\n" if extra_hw_numbers: comment += "\nHardware numbers:\n" comment += "\n".join(extra_hw_numbers) if extra_sw_numbers: comment += "\nSoftware numbers:\n" comment += "\n".join(extra_sw_numbers) self.comment_text.delete('1.0', tk.END) self.comment_text.insert('1.0', comment) logging.info("Updated comment with extra numbers") except Exception as e: logging.error(f"Error updating comment with extra numbers: {str(e)}") def on_brand_selected(self, event=None): """Update model list when brand is selected""" try: selected_brand = self.brand_var.get() if selected_brand: # Convert brand name to filename format filename_brand = selected_brand if selected_brand == "Citroen": filename_brand = "Citroen" elif selected_brand == "Mercedes-Benz": filename_brand = "Mercedes" # Construct path to model file model_file = os.path.join(os.path.dirname(__file__), 'Model_car', f'{filename_brand}.txt') if os.path.exists(model_file): # Load models from file with open(model_file, 'r') as f: models = [line.strip() for line in f if line.strip()] # Update model combobox self.model_combo['values'] = sorted(models) logging.info(f"Loaded {len(models)} models for {selected_brand}") else: self.model_combo['values'] = [] logging.warning(f"No model file found for {selected_brand}") # Clear current model selection self.model_var.set('') except Exception as e: logging.error(f"Error loading models for {selected_brand}: {str(e)}") self.model_combo['values'] = [] def save_data(self): try: conn = psycopg2.connect( dbname="hexaw", user="postgres", password="postgres", host="localhost" ) cur = conn.cursor() # Create table if it doesn't exist (removed producer field) cur.execute(""" CREATE TABLE IF NOT EXISTS edc15_files ( id SERIAL PRIMARY KEY, filename TEXT, customer_name TEXT, customer_phone TEXT, license_plate TEXT, car_brand TEXT, car_model TEXT, car_engine TEXT, car_hp TEXT, car_kw TEXT, car_year TEXT, car_vin TEXT, car_transmission TEXT, ecu_type TEXT, ecu_part TEXT, ecu_software TEXT, ecu_hardware TEXT, project_type TEXT, project_state TEXT, readout_by TEXT, comment TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) """) # Insert data (removed producer field) cur.execute(""" INSERT INTO edc15_files ( filename, customer_name, customer_phone, license_plate, car_brand, car_model, car_engine, car_hp, car_kw, car_year, car_vin, car_transmission, ecu_type, ecu_part, ecu_software, ecu_hardware, project_type, project_state, readout_by, comment ) VALUES ( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s ) """, ( self.filename, self.name_var.get(), self.phone_var.get(), self.license_var.get(), self.brand_var.get(), self.model_var.get(), self.engine_var.get(), self.hp_var.get(), self.kw_var.get(), self.year_combo.get(), self.vin_var.get(), self.transmission_combo.get(), self.ecu_type_var.get(), '', self.software_var.get(), self.hardware_var.get(), self.project_type_var.get(), self.state_var.get(), self.readout_var.get(), self.comment_text.get("1.0", tk.END).strip() )) conn.commit() messagebox.showinfo("Success", "Data saved successfully!") self.destroy() except Exception as e: messagebox.showerror("Error", f"Failed to save data: {str(e)}") logging.error(f"Database error: {str(e)}") finally: if 'cur' in locals(): cur.close() if 'conn' in locals(): conn.close() def create_project(self): try: # Validate required fields before creating project if not all([ self.name_var.get().strip(), self.brand_combo.get(), self.model_combo.get(), self.project_type_combo.get(), self.state_combo.get(), self.readout_combo.get() ]): messagebox.showwarning("Incomplete Information", "Please fill in all required fields.") return # Existing project creation logic conn = psycopg2.connect( dbname="hexawork", user="postgres", password="postgres", host="localhost" ) cur = conn.cursor() cur.execute(""" INSERT INTO edc15_files ( filename, customer_name, customer_phone, license_plate, car_brand, car_model, car_engine, car_hp, car_kw, car_year, car_vin, car_transmission, ecu_type, ecu_part, ecu_software, ecu_hardware, project_type, project_state, readout_by, comment ) VALUES ( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s ) """, ( self.filename, self.name_var.get(), self.phone_var.get(), self.license_var.get(), self.brand_combo.get(), self.model_combo.get(), self.engine_var.get(), self.hp_var.get(), self.kw_var.get(), self.year_combo.get(), self.vin_var.get(), self.transmission_combo.get(), self.ecu_type_var.get(), '', self.software_var.get(), self.hardware_var.get(), self.project_type_var.get(), self.state_var.get(), self.readout_var.get(), self.comment_text.get("1.0", tk.END).strip() )) conn.commit() # Set project created flag self.project_created = True messagebox.showinfo("Success", "Project created successfully!") self.destroy() except Exception as e: logging.error(f"Error creating project: {str(e)}") messagebox.showerror("Error", f"Failed to create project: {str(e)}") finally: if 'conn' in locals(): cur.close() conn.close() def on_closing(self): """Handle window closing event""" self.window_closed = True self.destroy()