Compare commits

...

3 Commits

Author SHA1 Message Date
e4fcfd11d7 Доработана функции для считывания переменных
Добавлены бета-функции для считывания переменны по адресу

+фиксы багов

future:
- в селекторе сделать две таблички для всех переменных и для выборанных
- по кнопке переносить переменные из всех в выбранные
- переменные из выбранных и добавлять в основную табличку
- сделать отдельный класс для таблички - который будет принимать спиоск переменных для отображения
2025-07-11 16:46:51 +03:00
d3f1e824fa оптимизировано. на первом запуске Выбора переменных конечно подвисает, но поиск работает относительно шустро 2025-07-11 10:48:52 +03:00
ad7b9126b7 массивы вроде работают но тормозит. начата работа над оптимизацией 2025-07-11 09:44:33 +03:00
12 changed files with 2931 additions and 2259 deletions

Binary file not shown.

View File

@ -8,14 +8,14 @@ import xml.etree.ElementTree as ET
from generateVars import type_map from generateVars import type_map
from enum import IntEnum from enum import IntEnum
import threading import threading
from scanVars import run_scan
from generateVars import run_generate from generateVars import run_generate
import setupVars import setupVars
from VariableSelector import VariableSelectorDialog from VariableSelector import VariableSelectorDialog
from VariableTable import VariableTableWidget, rows from VariableTable import VariableTableWidget, rows
from scanVarGUI import ProcessOutputWindowDummy from scanVarGUI import ProcessOutputWindow
import scanVars import scanVars
import myXML import myXML
import time
from PySide2.QtWidgets import ( from PySide2.QtWidgets import (
QApplication, QWidget, QTableWidget, QTableWidgetItem, QApplication, QWidget, QTableWidget, QTableWidgetItem,
@ -201,25 +201,10 @@ class VarEditor(QWidget):
# Создаём окно с кнопкой "Готово" # Создаём окно с кнопкой "Готово"
self.proc_win = ProcessOutputWindowDummy(self.__after_scanvars_finished) self.proc_win = ProcessOutputWindow(self.proj_path, self.makefile_path, self.xml_path,
self.emitting_stream = self.proc_win.emitting_stream # ключевая строка! on_done_callback=self.__after_scanvars_finished)
self.proc_win.show() self.proc_win.start_scan()
def run_scan_wrapper():
try:
old_stdout = sys.stdout
sys.stdout = self.emitting_stream
run_scan(self.proj_path, self.makefile_path, self.xml_path)
except Exception as e:
self.emitting_stream.text_written.emit(f"\n[ОШИБКА] {e}")
finally:
sys.stdout = old_stdout
self.emitting_stream.text_written.emit("\n--- Анализ завершён ---")
self.proc_win.btn_close.setEnabled(True)
threading.Thread(target=run_scan_wrapper, daemon=True).start()
def save_build(self): def save_build(self):
@ -434,15 +419,10 @@ class VarEditor(QWidget):
if not selected_rows: if not selected_rows:
return return
# Удаляем из vars_list те, у кого show_var == true и имя совпадает
filtered_vars = [v for v in self.vars_list if v.get('show_var', 'false') == 'true']
for row in selected_rows: for row in selected_rows:
if 0 <= row < len(filtered_vars): if 0 <= row < len(self.vars_list):
var_to_remove = filtered_vars[row] # Меняем флаг show_var для переменной с этим индексом
for v in self.vars_list: self.vars_list[row]['show_var'] = 'false'
if v['name'] == var_to_remove['name']:
v['show_var'] = 'false'
break
self.table.populate(self.vars_list, self.structs, self.write_to_xml) self.table.populate(self.vars_list, self.structs, self.write_to_xml)
self.write_to_xml() self.write_to_xml()
@ -453,7 +433,7 @@ class VarEditor(QWidget):
QMessageBox.warning(self, "Нет переменных", f"Сначала загрузите переменные ({scan_title}).") QMessageBox.warning(self, "Нет переменных", f"Сначала загрузите переменные ({scan_title}).")
return return
dlg = VariableSelectorDialog(self.vars_list, self.structs, self.typedef_map, self.xml_path, self) dlg = VariableSelectorDialog(self.table, self.vars_list, self.structs, self.typedef_map, self.xml_path, self)
if dlg.exec_(): if dlg.exec_():
self.write_to_xml() self.write_to_xml()
self.update() self.update()

View File

@ -1,31 +1,36 @@
import re import re
import xml.etree.ElementTree as ET
from PySide2.QtWidgets import ( from PySide2.QtWidgets import (
QDialog, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QPushButton, QDialog, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QPushButton,
QLineEdit, QLabel, QHeaderView, QCompleter, QCheckBox, QHBoxLayout QLineEdit, QLabel, QHeaderView, QCompleter, QCheckBox, QHBoxLayout
) )
from PySide2.QtGui import QKeySequence, QKeyEvent from PySide2.QtGui import QKeySequence, QKeyEvent
from PySide2.QtCore import Qt, QStringListModel, QSettings from PySide2.QtCore import Qt, QStringListModel, QSettings
import VariableTable
import setupVars import setupVars
import myXML import myXML
import time
array_re = re.compile(r'^(\w+)\[(\d+)\]$') array_re = re.compile(r'^(\w+)\[(\d+)\]$')
class VariableSelectorDialog(QDialog): class VariableSelectorDialog(QDialog):
def __init__(self, all_vars, structs, typedefs, xml_path=None, parent=None): def __init__(self, table, all_vars, structs, typedefs, xml_path=None, parent=None):
super().__init__(parent) super().__init__(parent)
self.setWindowTitle("Выбор переменных") self.setWindowTitle("Выбор переменных")
self.setAttribute(Qt.WA_DeleteOnClose) self.setAttribute(Qt.WA_DeleteOnClose)
self.resize(600, 500) self.resize(600, 500)
self.selected_names = [] self.selected_names = []
self._bckspc_pressed = False # флаг подавления добавления разделителя
self.table = table
self.all_vars = all_vars self.all_vars = all_vars
self.structs = structs self.structs = structs
self.typedefs = typedefs self.typedefs = typedefs
self.expanded_vars = [] self.expanded_vars = []
self.var_map = {v['name']: v for v in all_vars} self.var_map = {v['name']: v for v in all_vars}
self.node_index = {}
self.xml_path = xml_path # сохраняем путь к xml self.xml_path = xml_path # сохраняем путь к xml
self.manual_completion_active = False
# --- Добавляем чекбокс для автодополнения --- # --- Добавляем чекбокс для автодополнения ---
self.autocomplete_checkbox = QCheckBox("Включить автодополнение") self.autocomplete_checkbox = QCheckBox("Включить автодополнение")
@ -91,9 +96,32 @@ class VariableSelectorDialog(QDialog):
self.setLayout(layout) self.setLayout(layout)
self.expanded_vars = setupVars.expand_vars(self.all_vars, self.structs, self.typedefs) self.expanded_vars = setupVars.expand_vars(self.all_vars, self.structs, self.typedefs)
self.build_completion_list()
self.populate_tree() self.populate_tree()
def get_full_item_name(self, item):
names = []
while item:
names.append(item.text(0))
item = item.parent()
return '.'.join(reversed(names))
def build_completion_list(self):
# Собираем список полных имён всех переменных и вложенных полей
completions = []
def recurse(var, prefix=''):
fullname = f"{prefix}.{var['name']}" if prefix else var['name']
completions.append(fullname)
for child in var.get('children', []):
recurse(child, fullname)
for v in self.expanded_vars:
recurse(v)
self.all_completions = completions
def add_tree_item_recursively(self, parent, var): def add_tree_item_recursively(self, parent, var):
""" """
Рекурсивно добавляет переменную и её дочерние поля в дерево. Рекурсивно добавляет переменную и её дочерние поля в дерево.
@ -105,6 +133,8 @@ class VariableSelectorDialog(QDialog):
item = QTreeWidgetItem([name, type_str]) item = QTreeWidgetItem([name, type_str])
item.setData(0, Qt.UserRole, name) item.setData(0, Qt.UserRole, name)
full_name = self.get_full_item_name(item)
self.node_index[full_name.lower()] = item
# Делаем bitfield-поля неактивными # Делаем bitfield-поля неактивными
if "(bitfield:" in type_str: if "(bitfield:" in type_str:
@ -128,254 +158,259 @@ class VariableSelectorDialog(QDialog):
self.add_tree_item_recursively(item, child) self.add_tree_item_recursively(item, child)
def populate_tree(self): def populate_tree(self, vars_list=None):
if vars_list is None:
vars_list = self.expanded_vars
self.tree.clear() self.tree.clear()
self.node_index.clear()
for var in self.expanded_vars: for var in vars_list:
self.add_tree_item_recursively(None, var) self.add_tree_item_recursively(None, var)
header = self.tree.header() header = self.tree.header()
header.setSectionResizeMode(QHeaderView.Interactive) # вручную можно менять header.setSectionResizeMode(QHeaderView.Interactive) # вручную можно менять
self.tree.setColumnWidth(0, 400) self.tree.setColumnWidth(0, 400)
self.tree.resizeColumnToContents(1) self.tree.resizeColumnToContents(1)
""" header.setSectionResizeMode(0, QHeaderView.Stretch)
header.setSectionResizeMode(1, QHeaderView.ResizeToContents) """ def expand_to_level(self, item, level, current_level=0):
"""
Рекурсивно раскрывает узлы до заданного уровня.
"""
if current_level < level:
item.setExpanded(True)
else:
item.setExpanded(False)
for i in range(item.childCount()):
self.expand_to_level(item.child(i), level, current_level + 1)
def filter_tree(self): def filter_tree(self):
text = self.search_input.text().strip().lower() text = self.search_input.text().strip().lower()
path_parts = self.split_path(text) if text else [] path_parts = text.split('.') if text else []
filtered_vars = filter_vars(self.expanded_vars, path_parts)
def hide_all(item): # Сначала перерисовываем дерево
item.setHidden(True) self.populate_tree(filtered_vars)
for i in range(item.childCount()):
hide_all(item.child(i))
def path_matches_search(name, search_parts): # Теперь node_index уже пересоздан — можно работать
name_parts = self.split_path(name.lower()) expand_level = len(path_parts) - 1 if path_parts else 0
if len(name_parts) < len(search_parts): for i in range(self.tree.topLevelItemCount()):
return False item = self.tree.topLevelItem(i)
for sp, np in zip(search_parts, name_parts): self.expand_to_level(item, expand_level)
if not np.startswith(sp):
return False
return True
def show_matching_path(item, level=0): # Раскрываем путь до точного совпадения
name = item.text(0).lower() if path_parts:
fullname = '.'.join(path_parts)
# По умолчанию не совпадает node = self.node_index.get(fullname.lower())
matched = False if node:
parent = node.parent()
# Проверяем путь while parent:
if not path_parts: parent.setExpanded(True)
matched = True parent = parent.parent()
elif path_matches_search(name, path_parts[:level+1]):
matched = True
# Исключаем "плоские" элементы на верхнем уровне при глубоком поиске
if level == 0 and item.childCount() == 0 and len(path_parts) > 1:
matched = False
item.setHidden(not matched)
# Раскрываем узел, если он соответствует и ещё есть путь вниз
if matched and level < len(path_parts) - 1:
item.setExpanded(True)
else:
item.setExpanded(False)
matched_any_child = False
for i in range(item.childCount()):
child = item.child(i)
if show_matching_path(child, level + 1):
matched_any_child = True
return matched or matched_any_child
# Если в поиске нет точки — особая логика для первого уровня
if '.' not in text and '->' not in text and text != '':
for i in range(self.tree.topLevelItemCount()):
item = self.tree.topLevelItem(i)
name = item.text(0).lower()
if text in name:
item.setHidden(False)
item.setExpanded(False) # НЕ раскрываем потомков
else:
hide_all(item)
item.setHidden(True)
else:
# Обычная логика с поиском по пути
for i in range(self.tree.topLevelItemCount()):
item = self.tree.topLevelItem(i)
hide_all(item)
show_matching_path(item, 0)
def update_completions(self, text = None): def find_node_by_path(self, root_vars, path_list):
current_level = root_vars
node = None
for part in path_list:
node = None
for var in current_level:
if var['name'] == part:
node = var
break
if node is None:
return None
current_level = node.get('children', [])
return node
def update_completions(self, text=None):
if text is None: if text is None:
text = self.search_input.text().strip() text = self.search_input.text().strip()
else: else:
text = text.strip() text = text.strip()
parts = self.split_path(text) parts = self.split_path(text)
path_parts = parts[:-1] path_parts = parts[:-1] if parts else []
prefix = parts[-1].lower() if not text.endswith(('.', '>')) else '' prefix = parts[-1].lower() if parts else ''
ends_with_sep = text.endswith('.') or text.endswith('->') or text.endswith('[')
is_index_suggestion = text.endswith('[')
# Если путь есть (например: project.adc или project.adc.), ищем внутри него completions = []
search_deep = len(path_parts) > 0
def find_path_items(path_parts): def find_exact_node(parts):
items = [self.tree.topLevelItem(i) for i in range(self.tree.topLevelItemCount())] # Ищем точный узел по полному пути, используя node_index
# Постепенно собираем fullname из parts
if not parts:
return None
fullname = parts[0]
for p in parts[1:]:
fullname += '.' + p
return self.node_index.get(fullname.lower())
for part in path_parts: if is_index_suggestion:
part_lower = part.lower() base_text = text[:-1] # убираем '['
matched = [] parent_node = self.find_node_by_fullname(base_text)
if not parent_node:
# если base_text может содержать индекс типа foo[12], попробуем очистить
base_text_clean = re.sub(r'\[\d+\]$', '', base_text)
parent_node = self.find_node_by_fullname(base_text_clean)
if parent_node:
seen = set()
for i in range(parent_node.childCount()):
child = parent_node.child(i)
cname = child.text(0)
m = re.match(rf'^{re.escape(base_text)}\[(\d+)\]$', cname)
if m and cname not in seen:
completions.append(cname)
seen.add(cname)
self.completer.setModel(QStringListModel(completions))
return completions
for item in items: if ends_with_sep:
# Берём последний фрагмент имени item, разделённого точками # Путь завершен, показываем детей узла
item_name_part = self.split_path(item.text(0))[-1].lower() node = self.find_node_by_fullname(text[:-1])
if node:
if item_name_part == part_lower: completions.extend(node.child(i).text(0) for i in range(node.childCount()))
matched.append(item) elif not path_parts:
# Первый уровень — по вхождению
if not matched:
return []
items = []
# Собираем детей для следующего уровня поиска
for node in matched:
for i in range(node.childCount()):
items.append(node.child(i))
return matched
if not search_deep:
# Без точки — ищем только в топ-уровне, фильтруя по prefix
items = []
for i in range(self.tree.topLevelItemCount()): for i in range(self.tree.topLevelItemCount()):
item = self.tree.topLevelItem(i) item = self.tree.topLevelItem(i)
name_part = self.split_path(item.text(0))[-1].lower() name = item.text(0).lower()
if name_part.startswith(prefix): if prefix in name:
items.append(item) completions.append(item.text(0))
completions = [item.text(0) for item in items]
else: else:
# С точкой — углубляемся по пути и показываем имена детей node = find_exact_node(path_parts)
if len(path_parts) == 0: if node:
items = [self.tree.topLevelItem(i) for i in range(self.tree.topLevelItemCount())] for i in range(node.childCount()):
else: child = node.child(i)
items = find_path_items(path_parts) name = child.text(0)
# Оптимизируем split_path - кэширование
completions = [] name_parts = child.data(0, Qt.UserRole + 10)
for item in items: if name_parts is None:
for i in range(item.childCount()): name_parts = self.split_path(name)
child = item.child(i) child.setData(0, Qt.UserRole + 10, name_parts)
name_part = self.split_path(child.text(0))[-1].lower() if not name_parts:
if prefix == '' or name_part.startswith(prefix): continue
completions.append(child.text(0)) last_part = name_parts[-1].lower()
if prefix == '' or prefix in last_part: # здесь изменено
completions.append(name)
self.completer.complete()
self.completer.setModel(QStringListModel(completions))
return completions return completions
def eventFilter(self, obj, event):
if obj == self.search_input and isinstance(event, QKeyEvent):
if event.key() == Qt.Key_Space and event.modifiers() & Qt.ControlModifier:
completions = self.update_completions()
self.completer.complete()
text = self.search_input.text().strip()
if len(completions) == 1 and completions[0].lower() == text.lower():
# Найдем узел с таким именем
def find_exact_item(name):
stack = [self.tree.topLevelItem(i) for i in range(self.tree.topLevelItemCount())]
while stack:
node = stack.pop()
if node.text(0).lower() == name.lower():
return node
for i in range(node.childCount()):
stack.append(node.child(i))
return None
node = find_exact_item(completions[0])
if node and node.childCount() > 0:
# Используем первую подсказку, чтобы определить нужный разделитель
completions = self.update_completions(text + '.')
suggestion = completions[0]
# Ищем, какой символ идёт после текущего текста
separator = '.'
if suggestion.startswith(text):
rest = suggestion[len(text):]
if rest.startswith('->'):
separator = '->'
elif rest.startswith('.'):
separator = '.'
self.search_input.setText(text + separator)
completions = self.update_completions()
self.completer.setModel(QStringListModel(completions))
self.completer.complete()
return True
# Иначе просто показываем подсказки
self.completer.setModel(QStringListModel(completions))
if completions:
self.completer.complete()
return True
return super().eventFilter(obj, event)
# Функция для поиска узла с полным именем # Функция для поиска узла с полным именем
def find_node_by_fullname(self, name): def find_node_by_fullname(self, name):
stack = [self.tree.topLevelItem(i) for i in range(self.tree.topLevelItemCount())] return self.node_index.get(name.lower())
while stack:
node = stack.pop()
if node.text(0).lower() == name.lower():
return node
for i in range(node.childCount()):
stack.append(node.child(i))
return None
def insert_completion(self, text): def insert_completion(self, text):
node = self.find_node_by_fullname(text) node = self.find_node_by_fullname(text)
if node and node.childCount() > 0 and not (text.endswith('.') or text.endswith('->')): if node and node.childCount() > 0 and not (text.endswith('.') or text.endswith('->') or text.endswith('[')):
# Определяем разделитель по имени первого ребёнка # Определяем разделитель по имени первого ребёнка
child_name = node.child(0).text(0) child_name = node.child(0).text(0)
if child_name.startswith(text + '->'): if child_name.startswith(text + '->'):
text += '->' text += '->'
else: elif child_name.startswith(text + '.'):
text += '.' text += '.'
elif '[' in child_name:
text += '[' # для массивов
else:
text += '.' # fallback
self.search_input.setText(text) if not self._bckspc_pressed:
self.search_input.setCursorPosition(len(text)) self.search_input.setText(text)
self.update_completions() self.search_input.setCursorPosition(len(text))
self.completer.complete()
self.run_completions(text)
else: else:
self.search_input.setText(text) self.search_input.setText(text)
self.search_input.setCursorPosition(len(text)) self.search_input.setCursorPosition(len(text))
def on_search_text_changed(self, text): def eventFilter(self, obj, event):
if self.autocomplete_checkbox.isChecked(): if obj == self.search_input and isinstance(event, QKeyEvent):
completions = self.update_completions(text) if event.key() == Qt.Key_Space and event.modifiers() & Qt.ControlModifier:
node = self.find_node_by_fullname(text) self.manual_completion_active = True
text = self.search_input.text().strip()
self.run_completions(text)
elif event.key() == Qt.Key_Escape:
# Esc — выключаем ручной режим и скрываем подсказки, если autocomplete выключен
if not self.autocomplete_checkbox.isChecked():
self.manual_completion_active = False
self.completer.popup().hide()
return True
should_show = False if event.key() == Qt.Key_Backspace:
self._bckspc_pressed = True
else:
self._bckspc_pressed = False
if completions: return super().eventFilter(obj, event)
if len(completions) > 1:
should_show = True
elif len(completions) == 1:
single_node = self.find_node_by_fullname(completions[0])
if single_node and single_node.childCount() > 0:
should_show = True
elif node and node.childCount() > 0 and not (text.endswith('.') or text.endswith('->')):
should_show = True
if should_show: def run_completions(self, text):
completions = self.update_completions(text)
if not self.autocomplete_checkbox.isChecked() and self._bckspc_pressed:
text = text[:-1]
if len(completions) == 1 and completions[0].lower() == text.lower():
# Найдем узел с таким именем
def find_exact_item(name):
stack = [self.tree.topLevelItem(i) for i in range(self.tree.topLevelItemCount())]
while stack:
node = stack.pop()
if node.text(0).lower() == name.lower():
return node
for i in range(node.childCount()):
stack.append(node.child(i))
return None
node = find_exact_item(completions[0])
if node and node.childCount() > 0:
# Используем первую подсказку, чтобы определить нужный разделитель
completions = self.update_completions(text + '.')
suggestion = completions[0]
# Ищем, какой символ идёт после текущего текста
separator = '.'
if suggestion.startswith(text):
rest = suggestion[len(text):]
if rest.startswith(text + '->'):
separator += '->'
elif rest.startswith(text + '.'):
separator += '.'
elif '[' in rest:
separator += '[' # для массивов
else:
separator += '.' # fallback
if not self._bckspc_pressed:
self.search_input.setText(text + separator)
completions = self.update_completions(text)
self.completer.setModel(QStringListModel(completions)) self.completer.setModel(QStringListModel(completions))
self.completer.complete() self.completer.complete()
return True
# Иначе просто показываем подсказки
self.completer.setModel(QStringListModel(completions))
if completions:
self.completer.complete()
return True
def on_search_text_changed(self, text):
self.filter_tree() self.filter_tree()
if text == None:
text = self.search_input.text().strip()
if self.autocomplete_checkbox.isChecked():
self.run_completions(text)
else:
# Если выключено, показываем подсказки только если флаг ручного вызова True
if self.manual_completion_active:
self.run_completions(text)
else:
self.completer.popup().hide()
def on_add_clicked(self): def on_add_clicked(self):
self.selected_names = [] self.selected_names = []
@ -447,9 +482,7 @@ class VariableSelectorDialog(QDialog):
QMessageBox.warning(self, "Ошибка", "Путь к XML не задан, невозможно обновить переменные.") QMessageBox.warning(self, "Ошибка", "Путь к XML не задан, невозможно обновить переменные.")
return return
import xml.etree.ElementTree as ET root, tree = myXML.safe_parse_xml(self.xml_path)
tree = ET.parse(self.xml_path)
root = tree.getroot()
if root is None: if root is None:
return return
@ -470,7 +503,6 @@ class VariableSelectorDialog(QDialog):
myXML.fwrite(root, self.xml_path) myXML.fwrite(root, self.xml_path)
self.populate_tree()
self.done(QDialog.Accepted) self.done(QDialog.Accepted)
@ -488,8 +520,50 @@ class VariableSelectorDialog(QDialog):
def delete_selected_vars(self): def delete_selected_vars(self):
selected_names = self._get_selected_var_names() selected_names = self._get_selected_var_names()
if not selected_names: if not selected_names:
print("nothing selected")
return return
# Обновляем var_map и all_vars
for name in selected_names:
if name in self.var_map:
self.var_map[name]['show_var'] = 'false'
self.var_map[name]['enable'] = 'false'
for v in self.all_vars:
if v['name'] == name:
v['show_var'] = 'false'
v['enable'] = 'false'
break
# Проверка пути к XML
if not hasattr(self, 'xml_path') or not self.xml_path:
from PySide2.QtWidgets import QMessageBox
QMessageBox.warning(self, "Ошибка", "Путь к XML не задан, невозможно обновить переменные.")
return
root, tree = myXML.safe_parse_xml(self.xml_path)
if root is None:
return
vars_section = root.find('variables')
if vars_section is None:
return
for var_elem in vars_section.findall('var'):
name = var_elem.attrib.get('name')
if name in selected_names:
def set_text(tag, value):
el = var_elem.find(tag)
if el is None:
el = ET.SubElement(var_elem, tag)
el.text = value
set_text('show_var', 'false')
set_text('enable', 'false')
myXML.fwrite(root, self.xml_path)
self.table.populate(self.all_vars, self.structs, None)
# Проверка пути к XML # Проверка пути к XML
if not hasattr(self, 'xml_path') or not self.xml_path: if not hasattr(self, 'xml_path') or not self.xml_path:
from PySide2.QtWidgets import QMessageBox from PySide2.QtWidgets import QMessageBox
@ -497,8 +571,7 @@ class VariableSelectorDialog(QDialog):
return return
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
tree = ET.parse(self.xml_path) root, tree = myXML.safe_parse_xml(self.xml_path)
root = tree.getroot()
if root is None: if root is None:
return return
@ -517,10 +590,22 @@ class VariableSelectorDialog(QDialog):
# Удаляем из all_vars (глобально) # Удаляем из all_vars (глобально)
self.all_vars[:] = [v for v in self.all_vars if v['name'] not in selected_names] self.all_vars[:] = [v for v in self.all_vars if v['name'] not in selected_names]
# Удаляем из expanded_vars (тоже глобально)
def filter_out_selected(vars_list):
filtered = []
for v in vars_list:
if v['name'] not in selected_names:
# Рекурсивно фильтруем детей, если есть
if 'children' in v:
v = v.copy()
v['children'] = filter_out_selected(v['children'])
filtered.append(v)
return filtered
self.expanded_vars[:] = filter_out_selected(self.expanded_vars)
if removed_any: if removed_any:
myXML.fwrite(root, self.xml_path) myXML.fwrite(root, self.xml_path)
self.populate_tree()
self.filter_tree() self.filter_tree()
def _get_selected_var_names(self): def _get_selected_var_names(self):
@ -532,6 +617,82 @@ class VariableSelectorDialog(QDialog):
self.settings.setValue("autocomplete_enabled", self.autocomplete_checkbox.isChecked()) self.settings.setValue("autocomplete_enabled", self.autocomplete_checkbox.isChecked())
def split_path(self, path): def split_path(self, path):
# Разбиваем по точке или по -> (учитываем, что -> длиной 2 символа) """
return re.split(r'\.|->', path) Разбивает путь на компоненты:
- 'foo[2].bar[1]->baz' ['foo', [2]', 'bar', '[1]' 'baz']
"""
tokens = []
token = ''
i = 0
while i < len(path):
c = path[i]
# Разделители: '->' и '.'
if c == '-' and path[i:i+2] == '->':
if token:
tokens.append(token)
token = ''
i += 2
continue
elif c == '.':
if token:
tokens.append(token)
token = ''
i += 1
continue
elif c == '[':
# Заканчиваем текущий токен, если есть
if token:
tokens.append(token)
token = ''
# Собираем индекс [N]
idx = ''
while i < len(path) and path[i] != ']':
idx += path[i]
i += 1
if i < len(path) and path[i] == ']':
idx += ']'
i += 1
tokens.append(idx)
continue
else:
token += c
i += 1
if token:
tokens.append(token)
return tokens
def filter_vars(vars_list, path_parts):
"""Рекурсивно фильтруем vars_list по path_parts и возвращаем только подходящие."""
filtered = []
def matches_path(name, search_parts):
name_parts = name.lower().split('.')
if len(name_parts) < len(search_parts):
return False
for sp, np in zip(search_parts, name_parts):
if not np.startswith(sp):
return False
return True
for var in vars_list:
fullname = var.get('fullname', var['name']) # желательно иметь полное имя
# Если фильтра нет — берем всё
if not path_parts or matches_path(fullname, path_parts):
# Копируем узел с рекурсией по детям
new_var = var.copy()
if 'children' in var:
new_var['children'] = filter_vars(var['children'], path_parts)
filtered.append(new_var)
else:
# Но даже если этот узел не подходит, может подойти его потомок
if 'children' in var:
child_filtered = filter_vars(var['children'], path_parts)
if child_filtered:
new_var = var.copy()
new_var['children'] = child_filtered
filtered.append(new_var)
return filtered

View File

@ -1,6 +1,12 @@
import sys import sys
import re import re
import multiprocessing
import sys
import contextlib
import io
import json
from scanVars import run_scan
from VariableTable import VariableTableWidget, rows from VariableTable import VariableTableWidget, rows
from PySide2.QtWidgets import ( from PySide2.QtWidgets import (
@ -10,7 +16,7 @@ from PySide2.QtWidgets import (
QDialog, QTreeWidget, QTreeWidgetItem, QSizePolicy, QHeaderView, QProgressBar QDialog, QTreeWidget, QTreeWidgetItem, QSizePolicy, QHeaderView, QProgressBar
) )
from PySide2.QtGui import QTextCursor, QKeyEvent from PySide2.QtGui import QTextCursor, QKeyEvent
from PySide2.QtCore import Qt, QProcess, QObject, Signal, QSettings from PySide2.QtCore import Qt, QProcess, QObject, Signal, QTimer
class EmittingStream(QObject): class EmittingStream(QObject):
@ -67,24 +73,28 @@ class EmittingStream(QObject):
self._buffer = "" self._buffer = ""
class ProcessOutputWindowDummy(QDialog):
def __init__(self, on_done_callback): class ProcessOutputWindow(QDialog):
def __init__(self, proj_path, makefile_path, xml_path, on_done_callback=None):
super().__init__() super().__init__()
self.setWindowTitle("Поиск переменных...") self.setWindowTitle("Поиск переменных...")
self.resize(600, 480) self.resize(600, 480)
self.setModal(True) self.setModal(True)
self.proj_path = proj_path
self.makefile_path = makefile_path
self.xml_path = xml_path
self._on_done_callback = on_done_callback
self.layout = QVBoxLayout(self) self.layout = QVBoxLayout(self)
self.output_edit = QTextEdit() self.output_edit = QTextEdit()
self.output_edit.setReadOnly(True) self.output_edit.setReadOnly(True)
self.layout.addWidget(self.output_edit) self.layout.addWidget(self.output_edit)
# Метка с именем прогрессбара
self.progress_label = QLabel("Progress:") self.progress_label = QLabel("Progress:")
self.layout.addWidget(self.progress_label) self.layout.addWidget(self.progress_label)
# Прогрессбар
self.progress_bar = QProgressBar() self.progress_bar = QProgressBar()
self.progress_bar.setMinimum(0) self.progress_bar.setMinimum(0)
self.progress_bar.setValue(0) self.progress_bar.setValue(0)
@ -93,29 +103,55 @@ class ProcessOutputWindowDummy(QDialog):
self.btn_close = QPushButton("Закрыть") self.btn_close = QPushButton("Закрыть")
self.btn_close.setEnabled(False) self.btn_close.setEnabled(False)
self.layout.addWidget(self.btn_close) self.layout.addWidget(self.btn_close)
self.btn_close.clicked.connect(self.__handle_done) self.btn_close.clicked.connect(self.__handle_done)
self._on_done_callback = on_done_callback
self.emitting_stream = EmittingStream() self.queue = None
self.emitting_stream.text_written.connect(self.append_text) self.proc = None
self.emitting_stream.progress_updated.connect(self.update_progress)
sys.stdout = self.emitting_stream
def __handle_done(self): def start_scan(self):
sys.stdout = sys.__stdout__ # восстановить stdout self.queue = multiprocessing.Queue()
if self._on_done_callback: self.proc = multiprocessing.Process(
self._on_done_callback() target=run_scan_process,
self.accept() args=(self.proj_path, self.makefile_path, self.xml_path, self.queue),
daemon=True)
self.proc.start()
self.timer = QTimer(self)
self.timer.timeout.connect(self.poll_queue)
self.timer.start(100)
self.show()
def poll_queue(self):
try:
while True:
msg = self.queue.get_nowait()
if msg is None:
# Конец процесса
self.btn_close.setEnabled(True)
self.append_text("\n--- Анализ завершён ---")
self.timer.stop()
return
# Пытаемся разобрать JSON-сообщение
if isinstance(msg, str) and msg.startswith("PROGRESS_MSG:"):
try:
data = json.loads(msg[len("PROGRESS_MSG:"):])
self.update_progress(data["bar_name"], data["current"], data["total"])
except Exception:
# Если не удалось распарсить, выводим как текст
self.append_text(msg)
else:
self.append_text(msg)
except Exception:
pass # Очередь пустая
def append_text(self, text): def append_text(self, text):
cursor = self.output_edit.textCursor() cursor = self.output_edit.textCursor()
cursor.movePosition(QTextCursor.End) cursor.movePosition(QTextCursor.End)
if not text.endswith('\n'): if not text.endswith('\n'):
text += '\n' text += '\n'
for line in text.splitlines(True): cursor.insertText(text)
cursor.insertText(line)
self.output_edit.setTextCursor(cursor) self.output_edit.setTextCursor(cursor)
self.output_edit.ensureCursorVisible() self.output_edit.ensureCursorVisible()
@ -123,3 +159,61 @@ class ProcessOutputWindowDummy(QDialog):
self.progress_label.setText(f"{bar_name}") self.progress_label.setText(f"{bar_name}")
self.progress_bar.setMaximum(total) self.progress_bar.setMaximum(total)
self.progress_bar.setValue(current) self.progress_bar.setValue(current)
def __handle_done(self):
self.close()
def closeEvent(self, event):
if self.proc and self.proc.is_alive():
self.proc.terminate()
self.proc.join()
self.btn_close.setEnabled(True)
self.append_text("Сканирование прервано.")
def run_scan_process(proj_path, makefile_path, xml_path, queue):
class QueueWriter(io.TextIOBase):
def __init__(self):
self._buffer = ""
self._current_bar_name = None
def write(self, txt):
self._buffer += txt
while '\n' in self._buffer:
line, self._buffer = self._buffer.split('\n', 1)
# Обработка прогресса
if line.startswith('Progress: "') and line.endswith('"'):
# Название прогресс-бара
bar_name = line[len('Progress: "'):-1]
self._current_bar_name = bar_name
elif re.match(r'^Progress:\s*\d+\s*/\s*\d+$', line):
m = re.match(r'^Progress:\s*(\d+)\s*/\s*(\d+)$', line)
if m:
current = int(m.group(1))
total = int(m.group(2))
bar_name = self._current_bar_name or "Progress"
# Отправляем специальное сообщение в очередь в формате JSON
msg = {
"bar_name": bar_name,
"current": current,
"total": total
}
queue.put("PROGRESS_MSG:" + json.dumps(msg))
else:
# Обычный вывод
queue.put(line)
def flush(self):
if self._buffer.strip():
queue.put(self._buffer.strip())
self._buffer = ""
sys.stdout = QueueWriter()
sys.stderr = sys.stdout
try:
run_scan(proj_path, makefile_path, xml_path)
except Exception as e:
queue.put(f"[ОШИБКА] {e}")
finally:
queue.put(None) # сигнал окончания

View File

@ -14,7 +14,6 @@ from xml.dom import minidom
from parseMakefile import parse_makefile from parseMakefile import parse_makefile
from collections import deque from collections import deque
import argparse import argparse
from setupVars import make_relative_path
import myXML import myXML
BITFIELD_WIDTHS = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32} BITFIELD_WIDTHS = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}
@ -582,12 +581,12 @@ def generate_xml_output(proj_path, xml_path, unique_vars, h_files_needed, vars_n
} }
if makefile_path: if makefile_path:
rel_makefile = make_relative_path(makefile_path, proj_path) rel_makefile = myXML.make_relative_path(makefile_path, proj_path)
if not os.path.isabs(rel_makefile): if not os.path.isabs(rel_makefile):
analysis_attrs["makefile_path"] = rel_makefile.replace("\\", "/") analysis_attrs["makefile_path"] = rel_makefile.replace("\\", "/")
if structs_xml_path: if structs_xml_path:
rel_struct = make_relative_path(structs_xml_path, proj_path) rel_struct = myXML.make_relative_path(structs_xml_path, proj_path)
if not os.path.isabs(rel_struct): if not os.path.isabs(rel_struct):
analysis_attrs["structs_path"] = rel_struct.replace("\\", "/") analysis_attrs["structs_path"] = rel_struct.replace("\\", "/")
@ -919,6 +918,5 @@ def run_scan(proj_path, makefile_path, output_xml, verbose=2):
print("[XML] Creating vars.xml...") print("[XML] Creating vars.xml...")
generate_xml_output(proj_path, output_xml, vars, includes, externs, structs_xml, makefile_path) generate_xml_output(proj_path, output_xml, vars, includes, externs, structs_xml, makefile_path)
print("Progress: 2/2")
print('Progress: "Done"') print('Progress: "Done"')
print('Progress: 1/1') print("Progress: 2/2")

View File

@ -153,37 +153,195 @@ def parse_structs(filename):
def parse_array_dims(type_str):
"""Возвращает базовый тип и список размеров массива"""
dims = list(map(int, re.findall(r'\[(\d+)\]', type_str)))
base_type = re.sub(r'\[\d+\]', '', type_str).strip()
return base_type, dims
def generate_array_names(prefix, dims, depth=0):
"""Рекурсивно генерирует имена для всех элементов многомерного массива"""
if not dims:
return [prefix]
result = []
for i in range(dims[0]):
new_prefix = f"{prefix}[{i}]"
children = generate_array_names(new_prefix, dims[1:], depth + 1)
result.append({
'name': new_prefix,
'children': children if len(dims) > 1 else None
})
return result
def flatten_array_tree(array_tree):
"""Разворачивает дерево массивов в линейный список с вложенными children"""
result = []
for node in array_tree:
entry = {'name': node['name']}
if node['children']:
entry['children'] = flatten_array_tree(node['children'])
result.append(entry)
return result
def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, depth=0): def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, depth=0):
if depth > 10: if depth > 10:
return [] return []
# Если type_str — словарь структуры # Вспомогательная функция для обработки массивов
def process_array(prefix, type_str, structs, typedefs, var_attrs, depth=0):
base_type, array_dims = parse_array_dims(type_str)
if not array_dims:
return []
# На текущем уровне берем первый размер массива
current_dim = array_dims[0]
# Оставшиеся размеры — все, кроме первого
remaining_dims = array_dims[1:]
# Для создания типа с оставшимися размерами:
if remaining_dims:
# Формируем строку типа для оставшихся измерений массива, например int[16]
remaining_type_str = f"{base_type}{''.join(f'[{d}]' for d in remaining_dims)}"
else:
remaining_type_str = base_type
array_tree = []
for i in range(current_dim):
name = f"{prefix}[{i}]"
# Для каждого элемента передаем уже оставшийся тип массива
children = expand_struct_recursively(name, remaining_type_str, structs, typedefs, var_attrs, depth + 1)
node = {
'name': name,
'type': remaining_type_str if remaining_dims else base_type,
'pt_type': '',
'iq_type': '',
'return_type': '',
'file': var_attrs.get('file'),
'extern': var_attrs.get('extern'),
'static': var_attrs.get('static'),
}
if children:
node['children'] = children
array_tree.append(node)
return array_tree
# Если type_str — уже распарсенная структура (dict)
if isinstance(type_str, dict): if isinstance(type_str, dict):
fields = type_str fields = type_str
else: else:
# Проверяем, массив ли это
base_type, array_dims = parse_array_dims(type_str)
if array_dims:
return process_array(prefix, type_str, structs, typedefs, var_attrs, depth)
# Ищем структуру по имени типа
base_type = scanVars.strip_ptr_and_array(type_str) base_type = scanVars.strip_ptr_and_array(type_str)
fields = structs.get(base_type) fields = structs.get(base_type)
if not isinstance(fields, dict): if not isinstance(fields, dict):
# Не структура и не массив — просто возвращаем пустой список
return [] return []
children = [] children = []
for field_name, field_value in fields.items(): for field_name, field_value in fields.items():
# Пропускаем поле 'type', оно служит для хранения имени типа
if field_name == 'type': if field_name == 'type':
continue continue
# Определяем разделитель между prefix и полем # Формируем полное имя поля
if prefix.endswith('*'): if prefix.endswith('*'):
separator = '->' separator = '->'
# Для красоты можно убрать пробелы у указателя
# например, если prefix="ptr*" -> "ptr->field"
full_name = f"{prefix[:-1]}{separator}{field_name}" full_name = f"{prefix[:-1]}{separator}{field_name}"
else: else:
separator = '.' separator = '.'
full_name = f"{prefix}{separator}{field_name}" full_name = f"{prefix}{separator}{field_name}"
# Определяем тип поля
if isinstance(field_value, dict) and isinstance(field_value.get('type'), str):
field_type_str = field_value['type']
elif isinstance(field_value, str):
field_type_str = field_value
else:
field_type_str = None
# Обработка, если поле — строка (тип или массив)
if field_type_str:
base_subtype, sub_dims = parse_array_dims(field_type_str)
if sub_dims:
# Массив — раскрываем элементы
array_parent = {
'name': full_name,
'type': field_type_str,
'pt_type': '',
'iq_type': '',
'return_type': '',
'file': var_attrs.get('file'),
'extern': var_attrs.get('extern'),
'static': var_attrs.get('static'),
}
array_children = []
flat_names = generate_array_names(full_name, sub_dims)
for node in flat_names:
# node — dict с ключом 'name' и (возможно) 'children'
sub_items = expand_struct_recursively(node['name'], base_subtype, structs, typedefs, var_attrs, depth + 1)
child_node = {
'name': node['name'],
'type': base_subtype,
'pt_type': '',
'iq_type': '',
'return_type': '',
'file': var_attrs.get('file'),
'extern': var_attrs.get('extern'),
'static': var_attrs.get('static'),
}
if sub_items:
child_node['children'] = sub_items
array_children.append(child_node)
array_parent['children'] = array_children
children.append(array_parent)
continue
# Игнорируем указатели на функции
if "(" in field_type_str and "*" in field_type_str and ")" in field_type_str:
continue
if isinstance(field_value, dict):
# Это одиночная структура — раскрываем рекурсивно
sub_items = expand_struct_recursively(full_name, field_value, structs, typedefs, var_attrs, depth + 1)
child = {
'name': full_name,
'type': field_type_str,
'pt_type': '',
'iq_type': '',
'return_type': '',
'file': var_attrs.get('file'),
'extern': var_attrs.get('extern'),
'static': var_attrs.get('static'),
}
if sub_items:
child['children'] = sub_items
children.append(child)
else:
# Обычное поле (int, float, etc.)
child = {
'name': full_name,
'type': field_type_str,
'pt_type': '',
'iq_type': '',
'return_type': '',
'file': var_attrs.get('file'),
'extern': var_attrs.get('extern'),
'static': var_attrs.get('static'),
}
children.append(child)
continue
# Если поле — dict без 'type' или со сложной структурой, обрабатываем как вложенную структуру
if isinstance(field_value, dict): if isinstance(field_value, dict):
# Если вложенная структура — берем её имя типа из поля 'type' или пустую строку
type_name = field_value.get('type', '') type_name = field_value.get('type', '')
child = { child = {
'name': full_name, 'name': full_name,
@ -195,35 +353,14 @@ def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, de
'extern': var_attrs.get('extern'), 'extern': var_attrs.get('extern'),
'static': var_attrs.get('static'), 'static': var_attrs.get('static'),
} }
if '*' in type_name and not full_name.endswith('*'):
full_name += '*'
# Рекурсивно раскрываем вложенные поля
subchildren = expand_struct_recursively(full_name, field_value, structs, typedefs, var_attrs, depth + 1) subchildren = expand_struct_recursively(full_name, field_value, structs, typedefs, var_attrs, depth + 1)
if subchildren: if subchildren:
child['children'] = subchildren child['children'] = subchildren
else: children.append(child)
# Простое поле — строка типа
# Пропускаем указатели на функции
if isinstance(field_value, str) and "(" in field_value and "*" in field_value and ")" in field_value:
continue
child = {
'name': full_name,
'type': field_value,
'pt_type': '',
'iq_type': '',
'return_type': '',
'file': var_attrs.get('file'),
'extern': var_attrs.get('extern'),
'static': var_attrs.get('static'),
}
children.append(child)
return children return children
def expand_vars(vars_list, structs, typedefs): def expand_vars(vars_list, structs, typedefs):
""" """
Раскрывает структуры и массивы структур в деревья. Раскрывает структуры и массивы структур в деревья.
@ -233,26 +370,26 @@ def expand_vars(vars_list, structs, typedefs):
for var in vars_list: for var in vars_list:
pt_type = var.get('pt_type', '') pt_type = var.get('pt_type', '')
raw_type = var.get('type', '') raw_type = var.get('type', '')
base_type = scanVars.strip_ptr_and_array(raw_type)
fields = structs.get(base_type) if var['name'] == 'project':
a = 1
if pt_type.startswith('pt_ptr_') and isinstance(fields, dict): if pt_type.startswith('pt_ptr_'):
new_var = var.copy() new_var = var.copy()
new_var['children'] = expand_struct_recursively(var['name']+'*', raw_type, structs, typedefs, var) new_var['children'] = expand_struct_recursively(var['name']+'*', raw_type, structs, typedefs, var)
expanded.append(new_var) expanded.append(new_var)
if pt_type.startswith('pt_arr_') and isinstance(fields, dict): if pt_type.startswith('pt_arr_'):
new_var = var.copy() new_var = var.copy()
new_var['children'] = expand_struct_recursively(var['name'], raw_type, structs, typedefs, var) new_var['children'] = expand_struct_recursively(var['name'], raw_type, structs, typedefs, var)
expanded.append(new_var) expanded.append(new_var)
elif pt_type == 'pt_struct' and isinstance(fields, dict): elif pt_type == 'pt_struct':
new_var = var.copy() new_var = var.copy()
new_var['children'] = expand_struct_recursively(var['name'], raw_type, structs, typedefs, var) new_var['children'] = expand_struct_recursively(var['name'], raw_type, structs, typedefs, var)
expanded.append(new_var) expanded.append(new_var)
elif pt_type == 'pt_union' and isinstance(fields, dict): elif pt_type == 'pt_union':
new_var = var.copy() new_var = var.copy()
new_var['children'] = expand_struct_recursively(var['name'], raw_type, structs, typedefs, var) new_var['children'] = expand_struct_recursively(var['name'], raw_type, structs, typedefs, var)
expanded.append(new_var) expanded.append(new_var)

View File

View File

@ -2,28 +2,133 @@
#include "IQmathLib.h" #include "IQmathLib.h"
static int getDebugVar(DebugVar_t *var, long *int_var, float *float_var); static int getDebugVar(DebugVar_t *var, long *int_var, float *float_var);
static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var, DebugVarIQType_t iq_type_final); static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var);
DebugVarIQType_t dbg_type = t_iq24;
long Debug_ReadVar(DebugVar_t *var, DebugVarIQType_t iq_type_final)
int Debug_LowLevel_ReadVar(DebugVar_t *var_ll, long *return_long)
{ {
long tmp_var; if (var_ll == NULL)
if((var->ptr_type == pt_struct) || (var->ptr_type == pt_union) || (var->ptr_type == pt_unknown)) return 1;
return;
convertDebugVarToIQx(var, &tmp_var, dbg_type); char *addr = var_ll->Ptr;
unsigned long addr_val = (unsigned long)addr;
// Ðàçðåø¸ííûå äèàïàçîíû ïàìÿòè íà TMS320F2812
if (!(
(addr_val <= 0x0007FF) || // RAMM0 + RAMM1
(addr_val >= 0x008000 && addr_val <= 0x009FFF) || // L0 + L1 SARAM
(addr_val >= 0x3F8000 && addr_val <= 0x3F9FFF) || // PRAMH0 + DRAMH0
(addr_val >= 0x3D8000 && addr_val <= 0x3EFFFF) || // Flash A-F
(addr_val >= 0x3FF000 && addr_val <= 0x3FFFFF) // Boot ROM / Reset
)) {
return 2; // àäðåñ âíå äîïóñòèìîãî äèàïàçîíà, èãíîðèðóåì
}
return tmp_var; convertDebugVarToIQx(var_ll, return_long);
return 0;
} }
static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var, DebugVarIQType_t iq_type_final)
int Debug_ReadVar(DebugVar_t *var, long *return_long)
{
long tmp_var;
if (var == NULL)
return 1;
if((var->ptr_type == pt_struct) || (var->ptr_type == pt_union) ||
(var->ptr_type == pt_unknown) || (var->return_type == pt_unknown))
return 1;
convertDebugVarToIQx(var, return_long);
return 0;
}
int Debug_ReadVarName(DebugVar_t *var, char *name_ptr)
{
if((var == NULL)||(name_ptr == NULL))
return 1;
int i;
// Êîïèðîâàíèå ñ çàùèòîé îò ïåðåïîëíåíèÿ è ÿâíîé îñòàíîâêîé ïî '\0'
for (i = 0; i < sizeof(var->name); i++)
{
name_ptr[i] = var->name[i];
if (var->name[i] == '\0')
break;
}
// Ãàðàíòèðîâàííîå çàâåðøåíèå ñòðîêè (íà ñëó÷àé, åñëè â var->name íå áûëî '\0')
name_ptr[sizeof(var->name) - 1] = '\0';
return 0;
}
int Debug_LowLevel_Initialize(const uint8_t* external_date)
{
if (external_date == NULL) {
return -1;
}
// Ïðåîáðàçóåì external_date â ñòðóêòóðó
DateTimeHex ext;
ext.year = (external_date[0] << 8) | external_date[1];
ext.month = external_date[2];
ext.day = external_date[3];
ext.hour = external_date[4];
ext.minute = external_date[5];
// Ïàðñèì BUILD_FULL_DATE "YYYYMMDD_HHMM"
DateTimeHex build;
char buf[5] = {0};
// Ãîä
memcpy(buf, BUILD_FULL_DATE + 0, 4);
build.year = (uint16_t)atoi(buf);
// Ìåñÿö
memcpy(buf, BUILD_FULL_DATE + 4, 2);
build.month = (uint8_t)atoi(buf);
// Äåíü
memcpy(buf, BUILD_FULL_DATE + 6, 2);
build.day = (uint8_t)atoi(buf);
// ×àñ
memcpy(buf, BUILD_FULL_DATE + 9, 2);
build.hour = (uint8_t)atoi(buf);
// Ìèíóòû
memcpy(buf, BUILD_FULL_DATE + 11, 2);
build.minute = (uint8_t)atoi(buf);
// Ñðàâíåíèå âñåõ ïîëåé
if (ext.year == build.year &&
ext.month == build.month &&
ext.day == build.day &&
ext.hour == build.hour &&
ext.minute == build.minute)
{
return 0; // Ñîâïàëî
}
return 1; // Íå ñîâïàëî
}
/////////////////////----INTERNAL FUNCTIONS-----////////////////////////
static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var)
{ {
long iq_numb, iq_united, iq_final; long iq_numb, iq_united, iq_final;
float float_numb; float float_numb;
if(getDebugVar(var, &iq_numb, &float_numb) == 1) if(getDebugVar(var, &iq_numb, &float_numb) != 0)
return 1; return 1;
// ïðèâåäåíèå ê îäíîìó IQ // ïðèâåäåíèå ê îäíîìó IQ
switch(var->iq_type) switch(var->iq_type)
@ -131,7 +236,7 @@ static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var, DebugVarIQType_t
} }
// ïðèâåäåíèå îáùåãî IQ ê çàïðàøèâàåìîìó // ïðèâåäåíèå îáùåãî IQ ê çàïðàøèâàåìîìó
switch(iq_type_final) switch(var->return_type)
{ {
case t_iq_none: case t_iq_none:
iq_final = (int)_IQtoF(iq_united); iq_final = (int)_IQtoF(iq_united);
@ -233,42 +338,53 @@ static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var, DebugVarIQType_t
} }
static int getDebugVar(DebugVar_t *var, long *int_var, float *float_var) static int getDebugVar(DebugVar_t *var, long *int_var, float *float_var)
{ {
if (!var || !int_var || !float_var) if (!var || !int_var || !float_var || !var->Ptr)
return 1; // îøèáêà: null óêàçàòåëü return 1; // îøèáêà: null óêàçàòåëü
char *addr = var->Ptr;
unsigned long addr_val = (unsigned long)addr;
switch (var->ptr_type) switch (var->ptr_type)
{ {
case pt_int8: // signed char case pt_int8: // 8 áèò
*int_var = *((signed char *)var->Ptr); case pt_uint8:
// âûðàâíèâàíèå íå íóæíî äëÿ 8 áèò
*int_var = *((volatile char *)addr);
break; break;
case pt_int16: // int case pt_int16: // 16 áèò (int)
*int_var = *((int *)var->Ptr); case pt_uint16:
if (addr_val & 0x1) // ïðîâåðêà âûðàâíèâàíèÿ ïî 2 áàéòàì
return 2; // îøèáêà âûðàâíèâàíèÿ
*int_var = *((volatile int *)addr);
break; break;
case pt_int32: // long case pt_int32: // 32 áèò (long)
*int_var = *((long *)var->Ptr); case pt_uint32:
if (addr_val & 0x3) // ïðîâåðêà âûðàâíèâàíèÿ ïî 4 áàéòàì
return 3; // îøèáêà âûðàâíèâàíèÿ
*int_var = *((volatile long *)addr);
break; break;
case pt_uint8: // unsigned char // case pt_int64: // 64 áèò (long long)
*int_var = *((unsigned char *)var->Ptr); // case pt_uint64:
break; // if (addr_val & 0x7) // ïðîâåðêà âûðàâíèâàíèÿ ïî 8 áàéòàì
// return 2; // îøèáêà âûðàâíèâàíèÿ
case pt_uint16: // unsigned int // // Òóò ïðîñòî ÷èòàåì, íî long long ìîæåò íå ïîìåñòèòüñÿ â *int_var
*int_var = *((unsigned int *)var->Ptr); // // Ìîæíî çàìåíèòü ëîãèêó ïîä 64-áèòíîå ÷òåíèå ïðè íåîáõîäèìîñòè
break; // *int_var = *((volatile long long *)addr);
// break;
case pt_uint32: // unsigned long
*int_var = *((unsigned long *)var->Ptr); case pt_float: // float (4 áàéòà)
break; if (addr_val & 0x3) // ïðîâåðêà âûðàâíèâàíèÿ ïî 4 áàéòàì
return 4; // îøèáêà âûðàâíèâàíèÿ
case pt_float: // float *float_var = *((volatile float *)addr);
*float_var = *((float *)var->Ptr);
break; break;
default:
return 1; // íåïîääåðæèâàåìûé òèï
// äëÿ óêàçàòåëåé è ìàññèâîâ íå ïîääåðæèâàåòñÿ ÷òåíèå // äëÿ óêàçàòåëåé è ìàññèâîâ íå ïîääåðæèâàåòñÿ ÷òåíèå
// case pt_ptr_int8: // case pt_ptr_int8:
// case pt_ptr_int16: // case pt_ptr_int16:
@ -282,8 +398,6 @@ static int getDebugVar(DebugVar_t *var, long *int_var, float *float_var)
// case pt_arr_uint8: // case pt_arr_uint8:
// case pt_arr_uint16: // case pt_arr_uint16:
// case pt_arr_uint32: // case pt_arr_uint32:
default:
return 1;
} }
return 0; // óñïåõ return 0; // óñïåõ

View File

@ -9,9 +9,11 @@ typedef enum
pt_int8, // signed char pt_int8, // signed char
pt_int16, // int pt_int16, // int
pt_int32, // long pt_int32, // long
pt_int64, // long
pt_uint8, // unsigned char pt_uint8, // unsigned char
pt_uint16, // unsigned int pt_uint16, // unsigned int
pt_uint32, // unsigned long pt_uint32, // unsigned long
pt_uint64, // unsigned long
pt_float, // float pt_float, // float
pt_struct, // struct pt_struct, // struct
pt_union, // struct pt_union, // struct
@ -70,12 +72,25 @@ typedef struct
char* Ptr; char* Ptr;
DebugVarPtrType_t ptr_type; DebugVarPtrType_t ptr_type;
DebugVarIQType_t iq_type; DebugVarIQType_t iq_type;
char name[10]; DebugVarIQType_t return_type;
char name[11]; // 10 ñèìâîëîâ + '\0'
}DebugVar_t; }DebugVar_t;
typedef struct {
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
} DateTimeHex;
extern int DebugVar_Qnt; extern int DebugVar_Qnt;
extern DebugVar_t dbg_vars[]; extern DebugVar_t dbg_vars[];
long Debug_ReadVar(DebugVar_t *var, DebugVarIQType_t iq_type_final);
int Debug_LowLevel_ReadVar(DebugVar_t *var_ll, long *return_long);
int Debug_ReadVar(DebugVar_t *var, long *return_long);
int Debug_ReadVarName(DebugVar_t *var, char *name_ptr);
#endif //DEBUG_TOOLS #endif //DEBUG_TOOLS

View File

@ -3,33 +3,33 @@
// Èíêëþäû äëÿ äîñòóïà ê ïåðåìåííûì // Èíêëþäû äëÿ äîñòóïà ê ïåðåìåííûì
#include "vector.h"
#include "f281xpwm.h"
#include "log_can.h"
#include "RS_Functions_modbus.h" #include "RS_Functions_modbus.h"
#include "errors.h"
#include "pwm_vector_regul.h"
#include "xp_project.h" #include "xp_project.h"
#include "xp_write_xpwm_time.h" #include "xp_write_xpwm_time.h"
#include "teta_calc.h"
#include "vector.h"
#include "v_pwm24.h" #include "v_pwm24.h"
#include "errors.h"
#include "dq_to_alphabeta_cos.h"
#include "log_can.h"
#include "f281xpwm.h"
#include "pwm_vector_regul.h"
#include "adc_tools.h" #include "adc_tools.h"
#include "rotation_speed.h" #include "rotation_speed.h"
#include "dq_to_alphabeta_cos.h"
#include "teta_calc.h"
#include "CAN_Setup.h"
#include "log_to_memory.h"
#include "log_params.h"
#include "CRC_Functions.h"
#include "global_time.h"
#include "RS_Functions.h" #include "RS_Functions.h"
#include "detect_phase_break2.h"
#include "x_parallel_bus.h"
#include "x_serial_bus.h"
#include "Spartan2E_Functions.h"
#include "xp_controller.h" #include "xp_controller.h"
#include "xPeriphSP6_loader.h" #include "xPeriphSP6_loader.h"
#include "xp_rotation_sensor.h" #include "xp_rotation_sensor.h"
#include "x_serial_bus.h"
#include "Spartan2E_Functions.h"
#include "x_parallel_bus.h"
#include "svgen_dq.h" #include "svgen_dq.h"
#include "detect_phase_break2.h"
#include "log_to_memory.h"
#include "CRC_Functions.h"
#include "global_time.h"
#include "CAN_Setup.h"
#include "log_params.h"
#include "pid_reg3.h" #include "pid_reg3.h"
#include "IQmathLib.h" #include "IQmathLib.h"
#include "doors_control.h" #include "doors_control.h"
@ -314,9 +314,26 @@ extern int zero_ADC[20];
// Îïðåäåëåíèå ìàññèâà ñ óêàçàòåëÿìè íà ïåðåìåííûå äëÿ îòëàäêè // Îïðåäåëåíèå ìàññèâà ñ óêàçàòåëÿìè íà ïåðåìåííûå äëÿ îòëàäêè
int DebugVar_Qnt = 2; int DebugVar_Qnt = 19;
#pragma DATA_SECTION(dbg_vars,".dbgvar_info") #pragma DATA_SECTION(dbg_vars,".dbgvar_info")
DebugVar_t dbg_vars[] = {\ DebugVar_t dbg_vars[] = {\
{(char *)&ADC0finishAddr, pt_uint8, t_iq7, pt_uint8, "asdasjjjjj" }, \ {(char *)&ADC1startAddr, pt_int16, t_iq_none, pt_int16, "ADC1StrAdr" }, \
{(char *)&project.cds_tk.count_elements_pbus, pt_uint16, t_iq_none, pt_uint16, "project.cd" }, \ {(char *)&project.cds_tk[0].plane_address, pt_uint16, t_iq_none, pt_uint16, "tk0_Adr" }, \
{(char *)&ADC_sf[0][0], pt_int16, t_iq_none, pt_int16, "ADC_sf00" }, \
{(char *)&ADC_sf[0][1], pt_int16, t_iq_none, pt_int16, "ADC_sf01" }, \
{(char *)&ADC_sf[0][2], pt_int16, t_iq_none, pt_int16, "ADC_sf02" }, \
{(char *)&ADC_sf[0][3], pt_int16, t_iq_none, pt_int16, "ADC_sf03" }, \
{(char *)&ADC_sf[0][4], pt_int16, t_iq_none, pt_int16, "ADC_sf04" }, \
{(char *)&ADC_sf[0][5], pt_int16, t_iq_none, pt_int16, "ADC_sf05" }, \
{(char *)&ADC_sf[0][6], pt_int16, t_iq_none, pt_int16, "ADC_sf06" }, \
{(char *)&ADC_sf[0][7], pt_int16, t_iq_none, pt_int16, "ADC_sf07" }, \
{(char *)&ADC_sf[0][8], pt_int16, t_iq_none, pt_int16, "ADC_sf08" }, \
{(char *)&ADC_sf[0][9], pt_int16, t_iq_none, pt_int16, "ADC_sf09" }, \
{(char *)&ADC_sf[0][10], pt_int16, t_iq_none, pt_int16, "ADC_sf010" }, \
{(char *)&ADC_sf[0][11], pt_int16, t_iq_none, pt_int16, "ADC_sf011" }, \
{(char *)&ADC_sf[0][12], pt_int16, t_iq_none, pt_int16, "ADC_sf012" }, \
{(char *)&ADC_sf[0][13], pt_int16, t_iq_none, pt_int16, "ADC_sf013" }, \
{(char *)&ADC_sf[0][14], pt_int16, t_iq_none, pt_int16, "ADC_sf014" }, \
{(char *)&ADC_sf[0][15], pt_int16, t_iq_none, pt_int16, "ADC_sf015" }, \
{(char *)&project.cds_tk[0].read.sbus.mask_protect_tk.all, pt_uint16, t_iq_none, pt_uint16, "project.cd" }, \
}; };

File diff suppressed because it is too large Load Diff

1110
vars.xml

File diff suppressed because it is too large Load Diff