# ToUpload/parsers/parse_stl.py # -*- coding: utf-8 -*- from lxml import etree import traceback import re # Needed for substitutions in get_access_text_stl # Importar desde las utilidades del parser # ns y get_multilingual_text son necesarios from .parser_utils import ns, get_multilingual_text # --- Funciones Auxiliares de Reconstrucción STL --- def get_access_text_stl(access_element): """ Reconstruye una representación textual simple de un Access en STL. Intenta manejar los diferentes tipos de acceso definidos en el XSD. """ if access_element is None: return "_ERR_ACCESS_" # --- Símbolo (Variable, Constante Simbólica) --- # Busca dentro del usando el namespace stl symbol_elem = access_element.xpath("./stl:Symbol", namespaces=ns) if symbol_elem: components = symbol_elem[0].xpath("./stl:Component", namespaces=ns) parts = [] for i, comp in enumerate(components): name = comp.get("Name", "_ERR_COMP_") # Comprobar HasQuotes (puede estar en el Access o Componente, priorizar Componente) has_quotes_comp = comp.get("HasQuotes", "false").lower() == "true" has_quotes_access = False access_parent = comp.xpath("ancestor::stl:Access[1]", namespaces=ns) if access_parent: has_quotes_attr = access_parent[0].xpath( "./stl:BooleanAttribute[@Name='HasQuotes']/text()", namespaces=ns ) has_quotes_access = ( has_quotes_attr and has_quotes_attr[0].lower() == "true" ) has_quotes = has_quotes_comp or has_quotes_access is_temp = name.startswith("#") if i > 0: parts.append(".") # Separador para estructuras # Aplicar comillas si es necesario if has_quotes or ( i == 0 and not is_temp and '"' not in name and "." not in name ): # Añadir comillas si HasQuotes es true, o si es el primer componente, # no es temporal, no tiene ya comillas, y no es parte de una DB (ej. DB10.DBX0.0) parts.append(f'"{name}"') else: parts.append(name) # Índices de Array (Access anidado dentro de Component) index_access = comp.xpath("./stl:Access", namespaces=ns) if index_access: indices = [get_access_text_stl(ia) for ia in index_access] # Limpiar índices (quitar saltos de línea, etc.) indices_cleaned = [idx.replace("\n", "").strip() for idx in indices] parts.append(f"[{','.join(indices_cleaned)}]") return "".join(parts) # --- Constante Literal --- # Busca dentro del usando el namespace stl constant_elem = access_element.xpath("./stl:Constant", namespaces=ns) if constant_elem: # Obtener valor y tipo val_elem = constant_elem[0].xpath("./stl:ConstantValue/text()", namespaces=ns) type_elem = constant_elem[0].xpath("./stl:ConstantType/text()", namespaces=ns) const_type = ( type_elem[0].strip().lower() if type_elem and type_elem[0] is not None else "" ) const_val = ( val_elem[0].strip() if val_elem and val_elem[0] is not None else "_ERR_CONST_" ) # Añadir prefijos estándar STL if const_type == "time": return f"T#{const_val}" if const_type == "s5time": return f"S5T#{const_val}" if const_type == "date": return f"D#{const_val}" if const_type == "dt": return f"DT#{const_val}" if const_type == "time_of_day" or const_type == "tod": return f"TOD#{const_val}" if const_type == "ltime": return f"LT#{const_val}" # Añadido LTIME if const_type == "dtl": return f"DTL#{const_val}" # Añadido DTL # Strings y Chars (Escapar comillas simples internas) if const_type == "string": replaced_val = const_val.replace("'", "''") return f"'{replaced_val}'" if const_type == "char": replaced_val = const_val.replace("'", "''") return f"'{replaced_val}'" if const_type == "wstring": replaced_val = const_val.replace("'", "''") return f"WSTRING#'{replaced_val}'" if const_type == "wchar": replaced_val = const_val.replace("'", "''") return f"WCHAR#'{replaced_val}'" # Tipos numéricos con prefijo opcional (Hexadecimal) if const_val.startswith("16#"): if const_type == "byte": return f"B#{const_val}" if const_type == "word": return f"W#{const_val}" if const_type == "dword": return f"DW#{const_val}" if const_type == "lword": return f"LW#{const_val}" # Añadido LWORD # Formato Real (añadir .0 si es necesario) if ( const_type in ["real", "lreal"] and "." not in const_val and "e" not in const_val.lower() ): # Verificar si es un número antes de añadir .0 try: float(const_val) # Intenta convertir a float return f"{const_val}.0" except ValueError: return const_val # No es número, devolver tal cual # Otros tipos numéricos o desconocidos return const_val # Valor por defecto # --- Etiqueta (Label) --- # Busca