+библиотека .c/.h переписана под универсальные дефайны intX_t uintX_t

+сделано задание размера короткого имени
+ добавлена бета поддержка stm:+
   - парс переменных из файла преокта Keil или makefile CubeIDE
   - запись в utf-8 для STM, вместо cp1251 для TMS
   - другой размер int (32 бита, вместо 16 бит) для STM
This commit is contained in:
2025-07-17 09:18:03 +03:00
parent 4de53090a1
commit ae2c90160e
7 changed files with 284 additions and 341 deletions

View File

@@ -127,19 +127,28 @@ class VarEditor(QWidget):
# Добавляем чекбокс для выбора типовой карты
# --- Создаем верхнее меню ---
menubar = QMenuBar(self)
target_menu = QMenu("Target", menubar)
menubar.setToolTip('Разные размеры int и кодировки файлов')
self.target_menu = QMenu("МК:", menubar)
# Создаем действия для выбора Target
self.action_tms = QAction("TMS", self, checkable=True)
self.action_stm = QAction("STM", self, checkable=True)
# Инициализируем QSettings с именем организации и приложения
self.settings = QSettings("SET", "DebugVarEdit_MainWindow")
# Восстанавливаем сохранённое состояние, если есть
mcu = self.settings.value("mcu_choosen", True, type=str)
self.on_target_selected(mcu)
self.target_menu.setToolTip(f'TMS: Размер int 16 бит. Кодировка cp1251\nSTM: Размер int 32 бита. Кодировка utf-8')
# Группируем действия чтобы выбирался только один
self.action_tms.setChecked(True) # по умолчанию TMS
self.action_tms.triggered.connect(lambda: self.on_target_selected("tms"))
self.action_stm.triggered.connect(lambda: self.on_target_selected("stm"))
self.action_tms.triggered.connect(lambda: self.on_target_selected("TMS"))
self.action_tms.setToolTip('Размер int 16 бит. Кодировка cp1251')
self.action_stm.triggered.connect(lambda: self.on_target_selected("STM"))
self.action_stm.setToolTip('Размер int 32 бита. Кодировка utf-8')
target_menu.addAction(self.action_tms)
target_menu.addAction(self.action_stm)
self.target_menu.addAction(self.action_tms)
self.target_menu.addAction(self.action_stm)
menubar.addMenu(target_menu)
menubar.addMenu(self.target_menu)
# Кнопка сохранения
btn_save = QPushButton(build_title)
@@ -171,8 +180,10 @@ class VarEditor(QWidget):
def on_target_selected(self, target):
self.target_menu.setTitle(f'МК: {target}')
self.settings.setValue("mcu_choosen", target)
self.target = target.lower()
if target == "stm":
if self.target == "stm":
choose_type_map(True)
self.action_stm.setChecked(True)
self.action_tms.setChecked(False)
@@ -272,7 +283,7 @@ class VarEditor(QWidget):
return
try:
run_generate(self.proj_path, self.xml_path, self.output_path)
run_generate(self.proj_path, self.xml_path, self.output_path, self.table._shortname_size)
QMessageBox.information(self, "Готово", "Файл debug_vars.c успешно сгенерирован.")
self.update()
except Exception as e:

View File

@@ -12,6 +12,7 @@ from xml.dom import minidom
import myXML
import argparse
shortnameSize = 10
# === Словарь соответствия типов XML → DebugVarType_t ===
type_map_tms = dict([
@@ -186,13 +187,16 @@ type_map_stm32 = dict([
)],
])
type_map = type_map_tms
stm_flag_global = 0
def choose_type_map(stm_flag):
global type_map # объявляем, что будем менять глобальную переменную
global stm_flag_global # объявляем, что будем менять глобальную переменную
if stm_flag:
type_map = type_map_stm32
stm_flag_global = 1
else:
type_map = type_map_tms
stm_flag_global = 0
def map_type_to_pt(typename, varname=None, typedef_map=None):
typename_orig = typename.strip()
@@ -286,9 +290,9 @@ def add_new_vars_to_xml(proj_path, xml_rel_path, output_path):
if os.path.isfile(output_path):
with open(output_path, 'r', encoding='utf-8', errors='ignore') as f:
for line in f:
# {(char *)&some.deep.var.name , pt_uint16 , t_iq15 , "ShortName"},
# {(uint8_t *)&some.deep.var.name , pt_uint16 , t_iq15 , "ShortName"},
m = re.match(
r'{\s*\(char\s*\*\)\s*&([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)\s*,\s*(pt_\w+)\s*,\s*(t?iq_\w+)\s*,\s*"([^"]+)"',
r'{\s*\(uint8_t\s*\*\)\s*&([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)\s*,\s*(pt_\w+)\s*,\s*(t?iq_\w+)\s*,\s*"([^"]+)"',
line)
if m:
full_varname = m.group(1) # e.g., some.deep.var.name
@@ -426,12 +430,27 @@ def read_vars_from_xml(proj_path, xml_rel_path):
return unique_vars, include_files, vars_need_extern
def read_file_try_encodings(filepath):
if not os.path.isfile(filepath):
# Файл не существует — просто вернуть пустую строку или None
return "", None
for enc in ['utf-8', 'cp1251']:
try:
with open(filepath, 'r', encoding=enc) as f:
content = f.read()
return content, enc
except UnicodeDecodeError:
continue
raise UnicodeDecodeError(f"Не удалось прочитать файл {filepath} с кодировками utf-8 и cp1251")
def generate_vars_file(proj_path, xml_path, output_dir):
output_dir = os.path.join(proj_path, output_dir)
os.makedirs(output_dir, exist_ok=True)
output_path = os.path.join(output_dir, 'debug_vars.c')
LIBC_path = os.path.join(output_dir, 'debug_tools.c')
LIBH_path = os.path.join(output_dir, 'debug_tools.h')
# Запись новых переменных для в XML
@@ -479,7 +498,7 @@ def generate_vars_file(proj_path, xml_path, output_dir):
# Дополнительные поля, например комментарий
comment = info.get("comment", "")
short_name = info.get("shortname", f'"{vname}"')
short_trimmed = short_name[:10] # ограничиваем длину до 10
short_trimmed = short_name[:shortnameSize] # ограничиваем длину до 10
if pt_type not in ('pt_struct', 'pt_union'):
f_name = f'{vname},'
@@ -489,7 +508,7 @@ def generate_vars_file(proj_path, xml_path, output_dir):
f_short_name = f'"{short_trimmed}"' # оборачиваем в кавычки
# Добавим комментарий после записи, если он есть
comment_str = f' // {comment}' if comment else ''
line = f'{{(char *)&{f_name:<57} {f_type:<15} {f_iq:<15} {f_ret_iq:<15} {f_short_name:<21}}}, \\{comment_str}'
line = f'{{(uint8_t *)&{f_name:<58} {f_type:<15} {f_iq:<15} {f_ret_iq:<15} {f_short_name:<21}}}, \\{comment_str}'
new_debug_vars[vname] = line
else:
@@ -535,14 +554,19 @@ def generate_vars_file(proj_path, xml_path, output_dir):
out_lines.append(f'\n\n// Определение массива с указателями на переменные для отладки')
out_lines.append(f'int DebugVar_Qnt = {len(all_debug_lines)};')
out_lines.append('#pragma DATA_SECTION(dbg_vars,".dbgvar_info")')
if stm_flag_global == 0:
out_lines.append('#pragma DATA_SECTION(dbg_vars,".dbgvar_info")')
out_lines.append('// pointer_type iq_type return_iq_type short_name')
out_lines.append('DebugVar_t dbg_vars[] = {\\')
out_lines.extend(all_debug_lines)
out_lines.append('};')
out_lines.append('')
# Выберем кодировку для записи файла
# Если встречается несколько, возьмем первую из set
enc_to_write = 'cp1251'
if stm_flag_global == 0:
enc_to_write = 'cp1251'
else:
enc_to_write = 'utf-8'
#print("== GLOBAL VARS FOUND ==")
#for vname, (vtype, path) in vars_in_c.items():
@@ -552,6 +576,16 @@ def generate_vars_file(proj_path, xml_path, output_dir):
with open(output_path, 'w', encoding=enc_to_write) as f:
f.write('\n'.join(out_lines))
if os.path.isfile(LIBC_path):
libc_code, _ = read_file_try_encodings(LIBC_path)
with open(LIBC_path, 'w', encoding=enc_to_write) as f:
f.write(libc_code)
if os.path.isfile(LIBH_path):
libh_code, _ = read_file_try_encodings(LIBH_path)
with open(LIBH_path, 'w', encoding=enc_to_write) as f:
f.write(libh_code)
print(f'Файл debug_vars.c сгенерирован в кодировке, переменных: {len(all_debug_lines)}')
@@ -613,16 +647,17 @@ Usage example:
print(f"Error: Project path '{proj_path}' не является директорией или не существует.")
sys.exit(1)
generate_vars_file(proj_path, xml_path_rel, output_dir_rel)
generate_vars_file(proj_path, xml_path_rel, output_dir_rel, 0)
if __name__ == "__main__":
main()
def run_generate(proj_path, xml_path, output_dir):
def run_generate(proj_path, xml_path, output_dir, shortname_size):
import os
global shortnameSize
shortnameSize = shortname_size
# Normalize absolute paths
proj_path = os.path.abspath(proj_path)
xml_path_abs = os.path.abspath(xml_path)

View File

@@ -154,6 +154,7 @@ def analyze_variables_across_files(c_files, h_files, include_dirs, global_defs):
# Проверяем, начинается ли имя с "_" и содержит заглавные буквы или служебные символы
return bool(re.match(r"^_[_A-Z]", var_name))
if node.kind == clang.cindex.CursorKind.VAR_DECL:
if node.semantic_parent.kind == clang.cindex.CursorKind.TRANSLATION_UNIT:
is_extern = (node.storage_class == clang.cindex.StorageClass.EXTERN)
@@ -170,7 +171,12 @@ def analyze_variables_across_files(c_files, h_files, include_dirs, global_defs):
return # игнорируем только явно известные служебные переменные
if node.spelling == 'HUGE': # еще одна служеюная, которую хз как выделять
return
if 'Drivers' in node.location.file.name:
return
if 'uint' in node.spelling:
a = 1
# Проверяем, является ли тип указателем на функцию
# Признак: в типе есть '(' и ')' и '*', например: "void (*)(int)"
if "(" in var_type and "*" in var_type and ")" in var_type:
@@ -464,8 +470,6 @@ def analyze_typedefs_and_structs_across_files(c_files, include_dirs, global_defs
def visit(node):
if node.kind == clang.cindex.CursorKind.TYPEDEF_DECL:
name = node.spelling
if 'ADC_HandleTypeDef' in name:
a =1
underlying = node.underlying_typedef_type.spelling
typedefs[name] = underlying
@@ -473,10 +477,7 @@ def analyze_typedefs_and_structs_across_files(c_files, include_dirs, global_defs
prefix = "struct " if node.kind == clang.cindex.CursorKind.STRUCT_DECL else "union "
raw_name = node.spelling
if 'struct (unnamed struct at F:\\Work\\Projects\\NIIET\\MZKT\\MZKT\\Drivers\\STM32F4xx_HAL_Driver\\Inc\\stm32f4xx_hal_adc.h:195:9)' in raw_name:
a =1
normalized_name = normalize_type_name(raw_name)
# struct_name всегда с префиксом
if node.spelling and "unnamed" not in normalized_name:
struct_name = f"{prefix}{normalized_name}"

View File

@@ -1,10 +1,10 @@
from PySide2.QtWidgets import (
QTableWidget, QTableWidgetItem, QCheckBox, QComboBox, QLineEdit, QCompleter,
QAbstractItemView, QHeaderView, QLabel,
QAbstractItemView, QHeaderView, QLabel, QSpacerItem, QSizePolicy, QSpinBox,
QDialog, QVBoxLayout, QHBoxLayout, QPushButton, QScrollArea, QWidget
)
from PySide2.QtGui import QColor, QBrush, QPalette
from PySide2.QtCore import Qt
from PySide2.QtCore import Qt, QSettings
from enum import IntEnum
from generate_debug_vars import type_map
import time
@@ -60,6 +60,57 @@ class FilterDialog(QDialog):
return [cb.text() for cb in self.checkboxes if cb.isChecked()]
class SetSizeDialog(QDialog):
"""
Диалоговое окно для выбора числового значения (размера).
"""
def __init__(self, parent=None, initial_value=10, min_value=1, max_value=50, title="Укажите размер короткого имени"):
super().__init__(parent)
self.setWindowTitle(title)
self.setFixedSize(320, 120) # Задаем фиксированный размер для аккуратного вида
# Основной вертикальный макет
main_layout = QVBoxLayout(self)
# Макет для ввода значения
input_layout = QHBoxLayout()
label = QLabel("Количество символов:", self)
self.spin_box = QSpinBox(self)
self.spin_box.setRange(min_value, max_value) # Устанавливаем диапазон допустимых значений
initial_value = parent._shortname_size
self.spin_box.setValue(initial_value) # Устанавливаем начальное значение
self.spin_box.setFocus() # Устанавливаем фокус на поле ввода
input_layout.addWidget(label)
input_layout.addWidget(self.spin_box)
main_layout.addLayout(input_layout)
# Добавляем пустое пространство для лучшего разделения
main_layout.addSpacerItem(QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding))
# Макет для кнопок
btn_layout = QHBoxLayout()
btn_layout.addStretch() # Добавляем растягивающийся элемент, чтобы кнопки были справа
btn_ok = QPushButton("OK")
btn_cancel = QPushButton("Отмена")
btn_layout.addWidget(btn_ok)
btn_layout.addWidget(btn_cancel)
main_layout.addLayout(btn_layout)
# Подключение сигналов к слотам
btn_ok.clicked.connect(self.accept) # При нажатии "OK" диалог закроется со статусом "Accepted"
btn_cancel.clicked.connect(self.reject) # При нажатии "Отмена" - со статусом "Rejected"
def get_selected_size(self):
"""
Возвращает значение, выбранное в QSpinBox.
"""
return self.spin_box.value()
class CtrlScrollComboBox(QComboBox):
def wheelEvent(self, event):
if event.modifiers() & Qt.ControlModifier:
@@ -83,6 +134,11 @@ class VariableTableWidget(QTableWidget):
])
self.setEditTriggers(QAbstractItemView.AllEditTriggers)
self.var_list = []
# Инициализируем QSettings с именем организации и приложения
self.settings = QSettings("SET", "DebugVarEdit_VarTable")
# Восстанавливаем сохранённое состояние, если есть
shortsize = self.settings.value("shortname_size", True, type=int)
self._shortname_size = shortsize
self.type_options = list(dict.fromkeys(type_map.values()))
self.pt_types_all = [t.replace('pt_', '') for t in self.type_options]
@@ -231,7 +287,7 @@ class VariableTableWidget(QTableWidget):
t3 = time.time()
shortname = short_name_edit.text() if short_name_edit else ""
long_shortname = len(shortname) > 10
long_shortname = len(shortname) > self._shortname_size
found = name in var_names_set
color = None
@@ -293,6 +349,13 @@ class VariableTableWidget(QTableWidget):
if dlg.exec_():
self._ret_type_filter = dlg.get_selected()
self.update_comboboxes({rows.ret_type: self._ret_type_filter})
elif logicalIndex == rows.short_name:
dlg = SetSizeDialog(self)
if dlg.exec_():
self._shortname_size = dlg.get_selected_size()
self.settings.setValue("shortname_size", self._shortname_size)
self.check()