Carregar ficheiros para "src/core"

This commit is contained in:
2024-12-19 09:21:09 -08:00
parent 16f68bd726
commit 10713c22cf
3 changed files with 392 additions and 0 deletions

131
src/core/file_comparison.py Normal file
View File

@ -0,0 +1,131 @@
from typing import List, Tuple, Dict
import numpy as np
import os
from .file_handler import ECUFile, MapData
from .edc15_maps import EDC15MapDetector
class FileComparison:
def __init__(self, file1: ECUFile, file2: ECUFile):
self.file1 = file1
self.file2 = file2
self.differences = []
self.map_differences = {}
def compare_files(self) -> bool:
"""
Compare two ECU files and find differences
Returns True if differences were found
"""
if not self.file1.data or not self.file2.data:
return False
# Compare file sizes
if len(self.file1.data) != len(self.file2.data):
self.differences.append(("File Size",
len(self.file1.data),
len(self.file2.data)))
return True
# Compare byte by byte
diff_positions = []
for i, (b1, b2) in enumerate(zip(self.file1.data, self.file2.data)):
if b1 != b2:
diff_positions.append((i, b1, b2))
if diff_positions:
self.differences.extend(diff_positions)
return True
return False
def compare_maps(self) -> Dict[str, np.ndarray]:
"""
Compare maps between two files
Returns dictionary of map differences
"""
# Detect maps in both files
detector1 = EDC15MapDetector(self.file1.data)
detector2 = EDC15MapDetector(self.file2.data)
maps1 = detector1.find_potential_maps()
maps2 = detector2.find_potential_maps()
# Compare maps at same addresses
for map1 in maps1:
for map2 in maps2:
if map1.address == map2.address:
map_data1 = detector1.extract_map(map1)
map_data2 = detector2.extract_map(map2)
if not np.array_equal(map_data1.data, map_data2.data):
diff = map_data2.data - map_data1.data
self.map_differences[map1.name] = {
'difference': diff,
'original': map_data1,
'modified': map_data2
}
return self.map_differences
def get_difference_summary(self) -> str:
"""
Generate a human-readable summary of differences
"""
summary = []
if not self.differences and not self.map_differences:
return "No differences found between the files."
if self.differences:
summary.append(f"Found {len(self.differences)} byte differences:")
for i, (pos, val1, val2) in enumerate(self.differences[:10]):
summary.append(f" Offset 0x{pos:X}: {val1:02X} -> {val2:02X}")
if len(self.differences) > 10:
summary.append(f" ... and {len(self.differences)-10} more differences")
if self.map_differences:
summary.append(f"\nFound {len(self.map_differences)} modified maps:")
for map_name, diff_data in self.map_differences.items():
diff = diff_data['difference']
changes = np.count_nonzero(diff)
max_change = np.max(np.abs(diff))
summary.append(f" {map_name}: {changes} changed values, "
f"max difference: {max_change:.2f}")
return "\n".join(summary)
def export_report(self, filepath: str) -> bool:
"""Export comparison report to file"""
try:
with open(filepath, 'w') as f:
# Write header
f.write("ECU File Comparison Report\n")
f.write("=" * 30 + "\n\n")
# Write file information
f.write("File 1: " + os.path.basename(self.file1.file_path) + "\n")
f.write("File 2: " + os.path.basename(self.file2.file_path) + "\n\n")
# Write differences
f.write("Map Differences\n")
f.write("-" * 20 + "\n\n")
for map_name, diff_data in self.map_differences.items():
f.write(f"Map: {map_name}\n")
f.write(f"Total differences: {np.count_nonzero(diff_data['difference'])}\n")
f.write("Changes:\n")
diff = diff_data['difference']
rows, cols = diff.shape
for i in range(rows):
for j in range(cols):
if diff[i, j] != 0:
orig = diff_data['original'].data[i, j]
mod = diff_data['modified'].data[i, j]
f.write(f" Position ({i},{j}): {orig:.2f} -> {mod:.2f}\n")
f.write("\n")
return True
except Exception as e:
return False

234
src/core/file_handler.py Normal file
View File

@ -0,0 +1,234 @@
import os
import numpy as np
from typing import Dict, Optional, Tuple
from dataclasses import dataclass
from .edc15p_maps import MapDefinition, get_map_definition, list_maps_by_group, EDC15P_MAPS
import shutil
@dataclass
class MapData:
"""Map data structure"""
name: str
map_id: str
data: np.ndarray
rows: int
cols: int
x_axis: np.ndarray
y_axis: np.ndarray
address: str
units: str = ""
description: str = ""
group: str = ""
# Additional properties for map manager
data_format: str = "16-bit Hi-Lo"
display_mode: str = "Original" # Original, Difference, or Percent
is_signed: bool = False
swap_axis: bool = False
display_hex: bool = False
is_reciprocal: bool = False
value_min: float = 0.0
value_max: float = 0.0
factor: float = 1.0
offset: float = 0.0
precision: int = 0
formula: str = ""
# X-axis properties
x_name: str = "RPM"
x_address: str = ""
x_data_source: str = "EPROM"
x_data_format: str = "16-bit Hi-Lo"
x_step: int = 0
x_factor: float = 1.0
x_offset: float = 0.0
x_precision: int = 0
x_is_signed: bool = False
x_is_inverted: bool = False
x_display_hex: bool = False
x_is_reciprocal: bool = False
x_formula: str = ""
# Y-axis properties
y_name: str = "FUEL"
y_address: str = ""
y_data_source: str = "EPROM"
y_data_format: str = "16-bit Hi-Lo"
y_step: int = 0
y_factor: float = 1.0
y_offset: float = 0.0
y_precision: int = 0
y_is_signed: bool = False
y_is_inverted: bool = False
y_display_hex: bool = False
y_is_reciprocal: bool = False
y_formula: str = ""
def get_value(self, row: int, col: int) -> float:
"""Get value at specified coordinates"""
return float(self.data[row, col])
def set_value(self, row: int, col: int, value: float):
"""Set value at specified coordinates"""
self.data[row, col] = value
class ECUFile:
"""Class to handle ECU file operations"""
def __init__(self, filepath: str):
self.filepath = filepath
self.backup_path = f"{filepath}.backup"
self.content = None
self.maps: Dict[str, MapData] = {} # Key is map_id
self.modified = False
def read_file(self) -> bool:
"""Read and parse the ECU file"""
try:
with open(self.filepath, 'rb') as f:
self.content = f.read()
# Create backup if it doesn't exist
if not os.path.exists(self.backup_path):
shutil.copy2(self.filepath, self.backup_path)
# Extract maps
self._extract_map_data()
return True
except Exception as e:
return False
def get_raw_content(self) -> Optional[bytes]:
"""Get raw binary content of the file"""
return self.content
def read_map_data(self, map_id: str, map_def: MapDefinition) -> Tuple[np.ndarray, bool]:
"""Read map data from binary file using map definition"""
try:
# Convert hex address to integer
address = int(map_def.address, 16)
# Calculate total bytes needed (2 bytes per value - 16 bit)
total_bytes = map_def.rows * map_def.cols * 2
# Ensure we have enough data
if address + total_bytes > len(self.content):
return np.zeros((map_def.rows, map_def.cols)), False
# Read raw bytes from file
raw_data = self.content[address:address + total_bytes]
# Convert bytes to 16-bit integers (little-endian)
data = np.frombuffer(raw_data, dtype=np.uint16, count=map_def.rows * map_def.cols)
data = data.reshape(map_def.rows, map_def.cols)
# Verify data shape
if data.shape != (map_def.rows, map_def.cols):
return np.zeros((map_def.rows, map_def.cols)), False
return data, True
except Exception as e:
return np.zeros((map_def.rows, map_def.cols)), False
def _extract_map_data(self):
"""Extract real map data from ECU file"""
if not self.content:
return
# Get all map definitions by group
groups = list_maps_by_group()
for group_name, maps in groups.items():
for map_def in maps:
try:
# Find map ID
map_id = next((k for k, v in EDC15P_MAPS.items() if v == map_def), None)
if not map_id:
continue
# Read map data
data, success = self.read_map_data(map_id, map_def)
if not success:
continue
# Create MapData object with all necessary information
self.maps[map_id] = MapData(
name=map_def.name,
map_id=map_id,
data=data,
rows=map_def.rows,
cols=map_def.cols,
x_axis=np.array(map_def.x_axis),
y_axis=np.array(map_def.y_axis),
address=map_def.address,
units=map_def.units,
description=map_def.description,
group=map_def.group
)
except Exception:
continue
def _update_file_content(self):
"""Update file content with modified maps"""
if not self.content:
return
# Get all map definitions
for map_id, map_data in self.maps.items():
try:
# Get address
address = int(map_data.address, 16)
# Convert data to bytes (16-bit little-endian)
raw_data = map_data.data.astype(np.uint16).tobytes()
# Update content
self.content = (
self.content[:address] +
raw_data +
self.content[address + len(raw_data):]
)
except Exception as e:
continue
def save_file(self, filepath: Optional[str] = None) -> bool:
"""Save the file content"""
try:
# Update content with modified maps
self._update_file_content()
# Use provided filepath or default to original
save_path = filepath or self.filepath
with open(save_path, 'wb') as f:
f.write(self.content)
self.modified = False
return True
except Exception as e:
return False
def restore_backup(self) -> bool:
"""Restore from backup file"""
try:
if not os.path.exists(self.backup_path):
return False
# Read backup content
with open(self.backup_path, 'rb') as f:
self.content = f.read()
# Re-extract maps
self.maps.clear()
self._extract_map_data()
# Save restored content
return self.save_file()
except Exception as e:
return False

View File

@ -0,0 +1,27 @@
class ProjectManager:
def __init__(self):
self.current_project = None
def create_project(self, name, path):
"""
Create a new ECU project
"""
pass
def open_project(self, path):
"""
Open an existing ECU project
"""
pass
def save_project(self):
"""
Save current project
"""
pass
def validate_file(self, file_path):
"""
Validate ECU file integrity
"""
pass