таблица выбора элементов вынесена в отдельный класс
This commit is contained in:
parent
0d54031dd5
commit
4f949e9854
@ -10,6 +10,7 @@ import VariableTable
|
||||
import setupVars
|
||||
import myXML
|
||||
import time
|
||||
import selectTable
|
||||
|
||||
|
||||
array_re = re.compile(r'^(\w+)\[(\d+)\]$')
|
||||
@ -44,418 +45,75 @@ class VariableSelectorDialog(QDialog):
|
||||
# При изменении состояния чекбокса сохраняем его
|
||||
self.autocomplete_checkbox.stateChanged.connect(self.save_checkbox_state)
|
||||
|
||||
self.search_input = QLineEdit()
|
||||
self.search_input.setPlaceholderText("Поиск по имени переменной...")
|
||||
self.search_input.textChanged.connect(self.on_search_text_changed)
|
||||
|
||||
self.tree = QTreeWidget()
|
||||
self.tree.setHeaderLabels(["Имя переменной", "Тип"])
|
||||
self.tree.setSelectionMode(QTreeWidget.ExtendedSelection)
|
||||
self.tree.setRootIsDecorated(True)
|
||||
self.tree.setUniformRowHeights(True)
|
||||
|
||||
self.tree.setStyleSheet("""
|
||||
QTreeWidget::item:selected {
|
||||
background-color: #87CEFA;
|
||||
color: black;
|
||||
}
|
||||
QTreeWidget::item:hover {
|
||||
background-color: #D3D3D3;
|
||||
}
|
||||
""")
|
||||
|
||||
# Создаем кнопки, они остаются в диалоге
|
||||
self.btn_add = QPushButton("Добавить выбранные")
|
||||
self.btn_add.clicked.connect(self.on_add_clicked)
|
||||
|
||||
self.btn_delete = QPushButton("Удалить выбранные")
|
||||
|
||||
# Создаем экземпляр вашего готового виджета
|
||||
self.vars_widget = selectTable.VariableSelectWidget(self)
|
||||
|
||||
# Собираем новую, более простую компоновку
|
||||
search_layout = QHBoxLayout()
|
||||
search_layout.addWidget(QLabel("Поиск:"))
|
||||
search_layout.addStretch()
|
||||
search_layout.addWidget(self.autocomplete_checkbox)
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.addLayout(search_layout)
|
||||
layout.addWidget(self.vars_widget) # Добавляем ваш виджет целиком
|
||||
|
||||
button_layout = QHBoxLayout()
|
||||
button_layout.addWidget(self.btn_add)
|
||||
button_layout.addWidget(self.btn_delete)
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
# Соединяем сигналы кнопок с методами диалога
|
||||
self.btn_add.clicked.connect(self.on_add_clicked)
|
||||
self.btn_delete.clicked.connect(self.on_delete_clicked)
|
||||
|
||||
self.completer = QCompleter()
|
||||
self.completer.setCompletionMode(QCompleter.PopupCompletion) # важно!
|
||||
self.completer.setCaseSensitivity(Qt.CaseInsensitive)
|
||||
self.completer.setFilterMode(Qt.MatchContains)
|
||||
self.completer.setWidget(self.search_input)
|
||||
# Соединяем чекбокс с методом виджета
|
||||
self.autocomplete_checkbox.stateChanged.connect(self.vars_widget.set_autocomplete)
|
||||
# Устанавливаем начальное состояние автодополнения в виджете
|
||||
self.vars_widget.set_autocomplete(self.autocomplete_checkbox.isChecked())
|
||||
|
||||
|
||||
self.search_input.installEventFilter(self)
|
||||
|
||||
# Создаем горизонтальный layout для "Поиск:" и чекбокса справа
|
||||
search_layout = QHBoxLayout()
|
||||
label_search = QLabel("Поиск:")
|
||||
search_layout.addWidget(label_search, alignment=Qt.AlignLeft)
|
||||
search_layout.addStretch() # чтобы чекбокс прижался вправо
|
||||
search_layout.addWidget(self.autocomplete_checkbox, alignment=Qt.AlignRight)
|
||||
self.completer.activated[str].connect(self.insert_completion)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
layout.addLayout(search_layout) # заменили label и чекбокс
|
||||
layout.addWidget(self.search_input)
|
||||
layout.addWidget(self.tree)
|
||||
layout.addWidget(self.btn_add)
|
||||
layout.addWidget(self.btn_delete)
|
||||
self.setLayout(layout)
|
||||
|
||||
# --- Код в конце __init__ ---
|
||||
self.expanded_vars = setupVars.expand_vars(self.all_vars, self.structs, self.typedefs)
|
||||
self.build_completion_list()
|
||||
self.populate_tree()
|
||||
# Передаем данные в виджет
|
||||
self.vars_widget.set_data(self.expanded_vars)
|
||||
|
||||
|
||||
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):
|
||||
"""
|
||||
Рекурсивно добавляет переменную и её дочерние поля в дерево.
|
||||
Если parent == None, добавляет на верхний уровень.
|
||||
"""
|
||||
name = var['name']
|
||||
type_str = var.get('type', '')
|
||||
show_var = var.get('show_var', 'false') == 'true'
|
||||
|
||||
item = QTreeWidgetItem([name, type_str])
|
||||
item.setData(0, Qt.UserRole, name)
|
||||
full_name = self.get_full_item_name(item)
|
||||
self.node_index[full_name.lower()] = item
|
||||
|
||||
# Делаем bitfield-поля неактивными
|
||||
if "(bitfield:" in type_str:
|
||||
item.setDisabled(True)
|
||||
self.set_tool(item, "Битовые поля недоступны для выбора")
|
||||
|
||||
for i, attr in enumerate(['file', 'extern', 'static']):
|
||||
item.setData(0, Qt.UserRole + 1 + i, var.get(attr))
|
||||
|
||||
if show_var:
|
||||
item.setForeground(0, Qt.gray)
|
||||
item.setForeground(1, Qt.gray)
|
||||
self.set_tool(item, "Уже добавлена")
|
||||
|
||||
if parent is None:
|
||||
self.tree.addTopLevelItem(item)
|
||||
else:
|
||||
parent.addChild(item)
|
||||
|
||||
for child in var.get('children', []):
|
||||
self.add_tree_item_recursively(item, child)
|
||||
|
||||
|
||||
def populate_tree(self, vars_list=None):
|
||||
if vars_list is None:
|
||||
vars_list = self.expanded_vars
|
||||
self.tree.clear()
|
||||
self.node_index.clear()
|
||||
for var in vars_list:
|
||||
self.add_tree_item_recursively(None, var)
|
||||
|
||||
header = self.tree.header()
|
||||
header.setSectionResizeMode(QHeaderView.Interactive) # вручную можно менять
|
||||
self.tree.setColumnWidth(0, 400)
|
||||
self.tree.resizeColumnToContents(1)
|
||||
|
||||
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):
|
||||
text = self.search_input.text().strip().lower()
|
||||
path_parts = text.split('.') if text else []
|
||||
filtered_vars = filter_vars(self.expanded_vars, path_parts)
|
||||
|
||||
# Сначала перерисовываем дерево
|
||||
self.populate_tree(filtered_vars)
|
||||
|
||||
# Теперь node_index уже пересоздан — можно работать
|
||||
expand_level = len(path_parts) - 1 if path_parts else 0
|
||||
for i in range(self.tree.topLevelItemCount()):
|
||||
item = self.tree.topLevelItem(i)
|
||||
self.expand_to_level(item, expand_level)
|
||||
|
||||
# Раскрываем путь до точного совпадения
|
||||
if path_parts:
|
||||
fullname = '.'.join(path_parts)
|
||||
node = self.node_index.get(fullname.lower())
|
||||
if node:
|
||||
parent = node.parent()
|
||||
while parent:
|
||||
parent.setExpanded(True)
|
||||
parent = parent.parent()
|
||||
|
||||
|
||||
|
||||
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:
|
||||
text = self.search_input.text().strip()
|
||||
else:
|
||||
text = text.strip()
|
||||
|
||||
parts = self.split_path(text)
|
||||
path_parts = parts[:-1] if parts else []
|
||||
prefix = parts[-1].lower() if parts else ''
|
||||
ends_with_sep = text.endswith('.') or text.endswith('->') or text.endswith('[')
|
||||
is_index_suggestion = text.endswith('[')
|
||||
|
||||
completions = []
|
||||
|
||||
def find_exact_node(parts):
|
||||
if not parts:
|
||||
return None
|
||||
fullname = parts[0]
|
||||
for p in parts[1:]:
|
||||
fullname += '.' + p
|
||||
return self.node_index.get(fullname.lower())
|
||||
|
||||
if is_index_suggestion:
|
||||
base_text = text[:-1] # убираем '['
|
||||
parent_node = self.find_node_by_fullname(base_text)
|
||||
if not parent_node:
|
||||
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
|
||||
|
||||
if ends_with_sep:
|
||||
node = self.find_node_by_fullname(text[:-1])
|
||||
if node:
|
||||
completions.extend(node.child(i).text(0) for i in range(node.childCount()))
|
||||
elif not path_parts:
|
||||
# Первый уровень — только если имя начинается с prefix
|
||||
for i in range(self.tree.topLevelItemCount()):
|
||||
item = self.tree.topLevelItem(i)
|
||||
name = item.text(0)
|
||||
if name.lower().startswith(prefix):
|
||||
completions.append(name)
|
||||
else:
|
||||
node = find_exact_node(path_parts)
|
||||
if node:
|
||||
for i in range(node.childCount()):
|
||||
child = node.child(i)
|
||||
name = child.text(0)
|
||||
name_parts = child.data(0, Qt.UserRole + 10)
|
||||
if name_parts is None:
|
||||
name_parts = self.split_path(name)
|
||||
child.setData(0, Qt.UserRole + 10, name_parts)
|
||||
if not name_parts:
|
||||
continue
|
||||
last_part = name_parts[-1].lower()
|
||||
if prefix == '' or last_part.startswith(prefix): # ← строго startswith
|
||||
completions.append(name)
|
||||
|
||||
self.completer.setModel(QStringListModel(completions))
|
||||
self.completer.complete()
|
||||
return completions
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Функция для поиска узла с полным именем
|
||||
def find_node_by_fullname(self, name):
|
||||
return self.node_index.get(name.lower())
|
||||
|
||||
def insert_completion(self, text):
|
||||
node = self.find_node_by_fullname(text)
|
||||
if node and node.childCount() > 0 and not (text.endswith('.') or text.endswith('->') or text.endswith('[')):
|
||||
# Определяем разделитель по имени первого ребёнка
|
||||
child_name = node.child(0).text(0)
|
||||
if child_name.startswith(text + '->'):
|
||||
text += '->'
|
||||
elif child_name.startswith(text + '.'):
|
||||
text += '.'
|
||||
elif '[' in child_name:
|
||||
text += '[' # для массивов
|
||||
else:
|
||||
text += '.' # fallback
|
||||
|
||||
if not self._bckspc_pressed:
|
||||
self.search_input.setText(text)
|
||||
self.search_input.setCursorPosition(len(text))
|
||||
|
||||
self.run_completions(text)
|
||||
else:
|
||||
self.search_input.setText(text)
|
||||
self.search_input.setCursorPosition(len(text))
|
||||
|
||||
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:
|
||||
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
|
||||
|
||||
if event.key() == Qt.Key_Backspace:
|
||||
self._bckspc_pressed = True
|
||||
else:
|
||||
self._bckspc_pressed = False
|
||||
|
||||
return super().eventFilter(obj, event)
|
||||
|
||||
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.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()
|
||||
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):
|
||||
self.selected_names = []
|
||||
self.tree.setFocus()
|
||||
|
||||
for item in self.tree.selectedItems():
|
||||
name = item.text(0) # имя переменной (в колонке 1)
|
||||
type_str = item.text(1) # тип переменной (в колонке 2)
|
||||
# 5. Получаем имена из виджета
|
||||
selected_items = self.vars_widget.get_selected_items()
|
||||
if not selected_items:
|
||||
return
|
||||
|
||||
if not name:
|
||||
continue
|
||||
|
||||
self.selected_names.append((name, type_str))
|
||||
for item in selected_items:
|
||||
name = item.text(0)
|
||||
type_str = item.text(1)
|
||||
|
||||
if name in self.var_map:
|
||||
# Если переменная уже есть, просто включаем её и показываем
|
||||
var = self.var_map[name]
|
||||
var['show_var'] = 'true'
|
||||
var['enable'] = 'true'
|
||||
else:
|
||||
# Создаём новый элемент переменной
|
||||
# Получаем родительские параметры
|
||||
file_val = item.data(0, Qt.UserRole + 1)
|
||||
extern_val = item.data(0, Qt.UserRole + 2)
|
||||
static_val = item.data(0, Qt.UserRole + 3)
|
||||
|
||||
new_var = {
|
||||
'name': name,
|
||||
'type': type_str,
|
||||
'show_var': 'true',
|
||||
'enable': 'true',
|
||||
'shortname': name,
|
||||
'pt_type': '',
|
||||
'iq_type': '',
|
||||
'return_type': 'iq_none',
|
||||
'file': file_val,
|
||||
'name': name, 'type': type_str, 'show_var': 'true',
|
||||
'enable': 'true', 'shortname': name, 'pt_type': '', 'iq_type': '',
|
||||
'return_type': 'iq_none', 'file': file_val,
|
||||
'extern': str(extern_val).lower() if extern_val else 'false',
|
||||
'static': str(static_val).lower() if static_val else 'false',
|
||||
}
|
||||
|
||||
# Добавляем в список переменных
|
||||
self.all_vars.append(new_var)
|
||||
self.var_map[name] = new_var # Чтобы в будущем не добавлялось повторно
|
||||
self.var_map[name] = new_var
|
||||
|
||||
self.accept() # Используем accept() вместо done(QDialog.Accepted)
|
||||
|
||||
self.done(QDialog.Accepted)
|
||||
|
||||
|
||||
def on_delete_clicked(self):
|
||||
selected_names = self._get_selected_var_names()
|
||||
# 5. Получаем имена из виджета
|
||||
selected_names = self.vars_widget.get_selected_var_names()
|
||||
if not selected_names:
|
||||
print("nothing selected")
|
||||
return
|
||||
@ -466,46 +124,35 @@ class VariableSelectorDialog(QDialog):
|
||||
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
|
||||
self.update_xml_vars(selected_names, 'false', 'false')
|
||||
self.accept()
|
||||
|
||||
# Проверка пути к XML
|
||||
if not hasattr(self, 'xml_path') or not self.xml_path:
|
||||
from PySide2.QtWidgets import QMessageBox
|
||||
QMessageBox.warning(self, "Ошибка", "Путь к XML не задан, невозможно обновить переменные.")
|
||||
|
||||
|
||||
def update_xml_vars(self, names, show, enable):
|
||||
"""Обновляет флаги show_var и enable в XML файле."""
|
||||
if not self.xml_path:
|
||||
return
|
||||
|
||||
root, tree = myXML.safe_parse_xml(self.xml_path)
|
||||
if root is None:
|
||||
return
|
||||
|
||||
if root is None: return
|
||||
vars_section = root.find('variables')
|
||||
if vars_section is None:
|
||||
return
|
||||
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:
|
||||
if var_elem.attrib.get('name') in names:
|
||||
def set_text(tag, value):
|
||||
el = var_elem.find(tag)
|
||||
if el is None:
|
||||
el = ET.SubElement(var_elem, tag)
|
||||
if el is None: el = ET.SubElement(var_elem, tag)
|
||||
el.text = value
|
||||
set_text('show_var', 'false')
|
||||
set_text('enable', 'false')
|
||||
|
||||
set_text('show_var', show)
|
||||
set_text('enable', enable)
|
||||
myXML.fwrite(root, self.xml_path)
|
||||
|
||||
self.done(QDialog.Accepted)
|
||||
|
||||
def save_checkbox_state(self):
|
||||
self.settings.setValue("autocomplete_enabled", self.autocomplete_checkbox.isChecked())
|
||||
|
||||
|
||||
def set_tool(self, item, text):
|
||||
item.setToolTip(0, text)
|
||||
item.setToolTip(1, text)
|
||||
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
if event.key() == Qt.Key_Delete:
|
||||
@ -611,84 +258,3 @@ class VariableSelectorDialog(QDialog):
|
||||
|
||||
def save_checkbox_state(self):
|
||||
self.settings.setValue("autocomplete_enabled", self.autocomplete_checkbox.isChecked())
|
||||
|
||||
|
||||
|
||||
def split_path(self, 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 sp not in np:
|
||||
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
|
||||
|
||||
|
||||
|
@ -348,7 +348,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:<45} {f_type:<15} {f_iq:<15} {f_ret_iq:<15} {f_short_name:<21}}}, \\{comment_str}'
|
||||
line = f'{{(char *)&{f_name:<57} {f_type:<15} {f_iq:<15} {f_ret_iq:<15} {f_short_name:<21}}}, \\{comment_str}'
|
||||
new_debug_vars[vname] = line
|
||||
|
||||
else:
|
||||
|
458
Src/selectTable.py
Normal file
458
Src/selectTable.py
Normal file
@ -0,0 +1,458 @@
|
||||
# Поместите этот код перед классом VariableSelectorDialog
|
||||
|
||||
import re
|
||||
from PySide2.QtWidgets import (
|
||||
QWidget, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QLineEdit,
|
||||
QHeaderView, QCompleter
|
||||
)
|
||||
from PySide2.QtGui import QKeyEvent
|
||||
from PySide2.QtCore import Qt, QStringListModel
|
||||
|
||||
# Вспомогательные функции, которые теперь будут использоваться виджетом
|
||||
def split_path(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 = split_path(name)
|
||||
if len(name_parts) < len(search_parts):
|
||||
return False
|
||||
|
||||
for sp, np in zip(search_parts, name_parts):
|
||||
if sp not in np:
|
||||
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
|
||||
|
||||
|
||||
class VariableSelectWidget(QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.expanded_vars = []
|
||||
self.node_index = {}
|
||||
self.is_autocomplete_on = True # <--- ДОБАВИТЬ ЭТУ СТРОКУ
|
||||
self._bckspc_pressed = False
|
||||
self.manual_completion_active = False
|
||||
|
||||
# --- UI Элементы ---
|
||||
self.search_input = QLineEdit()
|
||||
self.search_input.setPlaceholderText("Поиск...")
|
||||
|
||||
self.tree = QTreeWidget()
|
||||
self.tree.setHeaderLabels(["Имя переменной", "Тип"])
|
||||
self.tree.setSelectionMode(QTreeWidget.ExtendedSelection)
|
||||
self.tree.setRootIsDecorated(True)
|
||||
self.tree.setUniformRowHeights(True)
|
||||
self.tree.setStyleSheet("""
|
||||
QTreeWidget::item:selected { background-color: #87CEFA; color: black; }
|
||||
QTreeWidget::item:hover { background-color: #D3D3D3; }
|
||||
""")
|
||||
|
||||
self.completer = QCompleter()
|
||||
self.completer.setCompletionMode(QCompleter.PopupCompletion)
|
||||
self.completer.setCaseSensitivity(Qt.CaseInsensitive)
|
||||
self.completer.setFilterMode(Qt.MatchContains)
|
||||
self.completer.setWidget(self.search_input)
|
||||
|
||||
# --- Layout ---
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.addWidget(self.search_input)
|
||||
layout.addWidget(self.tree)
|
||||
|
||||
# --- Соединения ---
|
||||
self.search_input.textChanged.connect(self.on_search_text_changed)
|
||||
self.search_input.installEventFilter(self)
|
||||
self.completer.activated[str].connect(self.insert_completion)
|
||||
|
||||
# --- Публичные методы для управления виджетом снаружи ---
|
||||
|
||||
def set_autocomplete(self, enabled: bool):
|
||||
"""Включает или выключает режим автодополнения."""
|
||||
self.is_autocomplete_on = enabled
|
||||
|
||||
def set_data(self, vars_list):
|
||||
"""Основной метод для загрузки данных в виджет."""
|
||||
self.expanded_vars = vars_list
|
||||
# self.build_completion_list() # Если нужна полная перестройка списка
|
||||
self.populate_tree()
|
||||
|
||||
def get_selected_items(self):
|
||||
"""Возвращает выделенные элементы QTreeWidget."""
|
||||
return self.tree.selectedItems()
|
||||
|
||||
def get_selected_var_names(self):
|
||||
"""Возвращает имена выделенных переменных."""
|
||||
return [item.text(0) for item in self.tree.selectedItems() if item.text(0)]
|
||||
|
||||
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 populate_tree(self, vars_list=None):
|
||||
if vars_list is None:
|
||||
vars_list = self.expanded_vars
|
||||
self.tree.clear()
|
||||
self.node_index.clear()
|
||||
for var in vars_list:
|
||||
self.add_tree_item_recursively(None, var)
|
||||
|
||||
header = self.tree.header()
|
||||
header.setSectionResizeMode(QHeaderView.Interactive) # вручную можно менять
|
||||
self.tree.setColumnWidth(0, 400)
|
||||
self.tree.resizeColumnToContents(1)
|
||||
|
||||
|
||||
def get_full_item_name(self, item):
|
||||
names = []
|
||||
while item:
|
||||
names.append(item.text(0))
|
||||
item = item.parent()
|
||||
return '.'.join(reversed(names))
|
||||
|
||||
def add_tree_item_recursively(self, parent, var):
|
||||
"""
|
||||
Рекурсивно добавляет переменную и её дочерние поля в дерево.
|
||||
Если parent == None, добавляет на верхний уровень.
|
||||
"""
|
||||
name = var['name']
|
||||
type_str = var.get('type', '')
|
||||
show_var = var.get('show_var', 'false') == 'true'
|
||||
|
||||
item = QTreeWidgetItem([name, type_str])
|
||||
item.setData(0, Qt.UserRole, name)
|
||||
full_name = self.get_full_item_name(item)
|
||||
self.node_index[full_name.lower()] = item
|
||||
|
||||
# Делаем bitfield-поля неактивными
|
||||
if "(bitfield:" in type_str:
|
||||
item.setDisabled(True)
|
||||
self.set_tool(item, "Битовые поля недоступны для выбора")
|
||||
|
||||
for i, attr in enumerate(['file', 'extern', 'static']):
|
||||
item.setData(0, Qt.UserRole + 1 + i, var.get(attr))
|
||||
|
||||
if show_var:
|
||||
item.setForeground(0, Qt.gray)
|
||||
item.setForeground(1, Qt.gray)
|
||||
self.set_tool(item, "Уже добавлена")
|
||||
|
||||
if parent is None:
|
||||
self.tree.addTopLevelItem(item)
|
||||
else:
|
||||
parent.addChild(item)
|
||||
|
||||
for child in var.get('children', []):
|
||||
self.add_tree_item_recursively(item, child)
|
||||
|
||||
|
||||
def filter_tree(self):
|
||||
text = self.search_input.text().strip().lower()
|
||||
path_parts = split_path(text) if text else []
|
||||
filtered_vars = filter_vars(self.expanded_vars, path_parts)
|
||||
|
||||
# Сначала перерисовываем дерево
|
||||
self.populate_tree(filtered_vars)
|
||||
|
||||
# Теперь node_index уже пересоздан — можно работать
|
||||
expand_level = len(path_parts) - 1 if path_parts else 0
|
||||
for i in range(self.tree.topLevelItemCount()):
|
||||
item = self.tree.topLevelItem(i)
|
||||
self.expand_to_level(item, expand_level)
|
||||
|
||||
# Раскрываем путь до точного совпадения
|
||||
if path_parts:
|
||||
fullname = '.'.join(path_parts)
|
||||
node = self.node_index.get(fullname.lower())
|
||||
if node:
|
||||
parent = node.parent()
|
||||
while parent:
|
||||
parent.setExpanded(True)
|
||||
parent = parent.parent()
|
||||
|
||||
|
||||
|
||||
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:
|
||||
text = self.search_input.text().strip()
|
||||
else:
|
||||
text = text.strip()
|
||||
|
||||
parts = split_path(text)
|
||||
path_parts = parts[:-1] if parts else []
|
||||
prefix = parts[-1].lower() if parts else ''
|
||||
ends_with_sep = text.endswith('.') or text.endswith('->') or text.endswith('[')
|
||||
is_index_suggestion = text.endswith('[')
|
||||
|
||||
completions = []
|
||||
|
||||
def find_exact_node(parts):
|
||||
if not parts:
|
||||
return None
|
||||
fullname = parts[0]
|
||||
for p in parts[1:]:
|
||||
fullname += '.' + p
|
||||
return self.node_index.get(fullname.lower())
|
||||
|
||||
if is_index_suggestion:
|
||||
base_text = text[:-1] # убираем '['
|
||||
parent_node = self.find_node_by_fullname(base_text)
|
||||
if not parent_node:
|
||||
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
|
||||
|
||||
if ends_with_sep:
|
||||
node = self.find_node_by_fullname(text[:-1])
|
||||
if node:
|
||||
completions.extend(node.child(i).text(0) for i in range(node.childCount()))
|
||||
elif not path_parts:
|
||||
# Первый уровень — только если имя начинается с prefix
|
||||
for i in range(self.tree.topLevelItemCount()):
|
||||
item = self.tree.topLevelItem(i)
|
||||
name = item.text(0)
|
||||
if name.lower().startswith(prefix):
|
||||
completions.append(name)
|
||||
else:
|
||||
node = find_exact_node(path_parts)
|
||||
if node:
|
||||
for i in range(node.childCount()):
|
||||
child = node.child(i)
|
||||
name = child.text(0)
|
||||
name_parts = child.data(0, Qt.UserRole + 10)
|
||||
if name_parts is None:
|
||||
name_parts = split_path(name)
|
||||
child.setData(0, Qt.UserRole + 10, name_parts)
|
||||
if not name_parts:
|
||||
continue
|
||||
last_part = name_parts[-1].lower()
|
||||
if prefix == '' or last_part.startswith(prefix): # ← строго startswith
|
||||
completions.append(name)
|
||||
|
||||
self.completer.setModel(QStringListModel(completions))
|
||||
self.completer.complete()
|
||||
return completions
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Функция для поиска узла с полным именем
|
||||
def find_node_by_fullname(self, name):
|
||||
return self.node_index.get(name.lower())
|
||||
|
||||
def insert_completion(self, text):
|
||||
node = self.find_node_by_fullname(text)
|
||||
if node and node.childCount() > 0 and not (text.endswith('.') or text.endswith('->') or text.endswith('[')):
|
||||
# Определяем разделитель по имени первого ребёнка
|
||||
child_name = node.child(0).text(0)
|
||||
if child_name.startswith(text + '->'):
|
||||
text += '->'
|
||||
elif child_name.startswith(text + '.'):
|
||||
text += '.'
|
||||
elif '[' in child_name:
|
||||
text += '[' # для массивов
|
||||
else:
|
||||
text += '.' # fallback
|
||||
|
||||
if not self._bckspc_pressed:
|
||||
self.search_input.setText(text)
|
||||
self.search_input.setCursorPosition(len(text))
|
||||
|
||||
self.run_completions(text)
|
||||
else:
|
||||
self.search_input.setText(text)
|
||||
self.search_input.setCursorPosition(len(text))
|
||||
|
||||
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:
|
||||
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.is_autocomplete_on:
|
||||
self.manual_completion_active = False
|
||||
self.completer.popup().hide()
|
||||
return True
|
||||
|
||||
if event.key() == Qt.Key_Backspace:
|
||||
self._bckspc_pressed = True
|
||||
else:
|
||||
self._bckspc_pressed = False
|
||||
|
||||
return super().eventFilter(obj, event)
|
||||
|
||||
def run_completions(self, text):
|
||||
completions = self.update_completions(text)
|
||||
|
||||
if not self.is_autocomplete_on 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.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()
|
||||
if text == None:
|
||||
text = self.search_input.text().strip()
|
||||
if self.is_autocomplete_on:
|
||||
self.run_completions(text)
|
||||
else:
|
||||
# Если выключено, показываем подсказки только если флаг ручного вызова True
|
||||
if self.manual_completion_active:
|
||||
self.run_completions(text)
|
||||
else:
|
||||
self.completer.popup().hide()
|
||||
|
||||
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 set_tool(self, item, text):
|
||||
item.setToolTip(0, text)
|
||||
item.setToolTip(1, text)
|
@ -266,6 +266,12 @@ def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, de
|
||||
else:
|
||||
field_type_str = None
|
||||
|
||||
|
||||
if '*' in field_type_str:
|
||||
full_name_prefix = full_name + '*'
|
||||
else:
|
||||
full_name_prefix = full_name
|
||||
|
||||
# Обработка, если поле — строка (тип или массив)
|
||||
if field_type_str:
|
||||
base_subtype, sub_dims = parse_array_dims(field_type_str)
|
||||
@ -311,7 +317,7 @@ def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, de
|
||||
|
||||
if isinstance(field_value, dict):
|
||||
# Это одиночная структура — раскрываем рекурсивно
|
||||
sub_items = expand_struct_recursively(full_name, field_value, structs, typedefs, var_attrs, depth + 1)
|
||||
sub_items = expand_struct_recursively(full_name_prefix, field_value, structs, typedefs, var_attrs, depth + 1)
|
||||
child = {
|
||||
'name': full_name,
|
||||
'type': field_type_str,
|
||||
@ -353,7 +359,7 @@ def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, de
|
||||
'extern': var_attrs.get('extern'),
|
||||
'static': var_attrs.get('static'),
|
||||
}
|
||||
subchildren = expand_struct_recursively(full_name, field_value, structs, typedefs, var_attrs, depth + 1)
|
||||
subchildren = expand_struct_recursively(full_name_prefix, field_value, structs, typedefs, var_attrs, depth + 1)
|
||||
if subchildren:
|
||||
child['children'] = subchildren
|
||||
children.append(child)
|
||||
|
@ -1,46 +1,52 @@
|
||||
#include "debug_tools.h"
|
||||
#include "IQmathLib.h"
|
||||
|
||||
DebugLowLevel_t debug_ll = DEBUG_LOWLEVEL_INIT;
|
||||
|
||||
static int getDebugVar(DebugVar_t *var, long *int_var, float *float_var);
|
||||
static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var);
|
||||
|
||||
|
||||
|
||||
///////////////////////////----EXAPLE-----//////////////////////////////
|
||||
long var_numb = 1;
|
||||
long return_var;
|
||||
long return_ll_var;
|
||||
DebugVar_t dbg_var_ll;
|
||||
int result;
|
||||
char ext_date[] = {7, 233, 11, 07, 16, 50};
|
||||
int Debug_Test_Example(void)
|
||||
void Debug_Test_Example(void)
|
||||
{
|
||||
result = Debug_ReadVar(&dbg_vars[var_numb], &return_var);
|
||||
|
||||
|
||||
if(Debug_LowLevel_Initialize(ext_date) == 0)
|
||||
result = Debug_LowLevel_ReadVar(&dbg_var_ll, &return_ll_var);
|
||||
result = Debug_LowLevel_ReadVar(&return_ll_var);
|
||||
}
|
||||
|
||||
|
||||
int Debug_LowLevel_ReadVar(DebugVar_t *var_ll, long *return_long)
|
||||
///////////////////////////----PUBLIC-----//////////////////////////////
|
||||
int Debug_LowLevel_ReadVar(long *return_long)
|
||||
{
|
||||
if (var_ll == NULL)
|
||||
if (return_long == NULL)
|
||||
return 1;
|
||||
if (debug_ll.isVerified == 0)
|
||||
return 1;
|
||||
|
||||
char *addr = var_ll->Ptr;
|
||||
char *addr = debug_ll.dbg_var.Ptr;
|
||||
unsigned long addr_val = (unsigned long)addr;
|
||||
// Ðàçðåø¸ííûå äèàïàçîíû ïàìÿòè íà TMS320F2812
|
||||
|
||||
// Ðàçðåø¸ííûå äèàïàçîíû ïàìÿòè (èç .cmd ôàéëà)
|
||||
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
|
||||
(addr_val <= 0x0007FF) || // RAMM0 + RAMM1
|
||||
(addr_val >= 0x008120 && addr_val <= 0x009FFC) || // L0 + L1 SARAM
|
||||
(addr_val >= 0x3F8000 && addr_val <= 0x3F9FFF) || // PRAMH0 + DRAMH0
|
||||
(addr_val >= 0x3FF000 && addr_val <= 0x3FFFFF) || // BOOTROM + RESET
|
||||
(addr_val >= 0x080002 && addr_val <= 0x09FFFF) || // RAMEX1
|
||||
(addr_val >= 0x0F0000 && addr_val <= 0x0FFEFF) || // RAMEX4
|
||||
(addr_val >= 0x100002 && addr_val <= 0x103FFF) || // RAMEX0 + RAMEX2 + RAMEX01
|
||||
(addr_val >= 0x102000 && addr_val <= 0x103FFF) // RAMEX2
|
||||
)) {
|
||||
return 2; // àäðåñ âíå äîïóñòèìîãî äèàïàçîíà, èãíîðèðóåì
|
||||
return 2; // Çàïðåù¸ííûé àäðåñ — íåëüçÿ ÷èòàòü
|
||||
}
|
||||
|
||||
convertDebugVarToIQx(var_ll, return_long);
|
||||
convertDebugVarToIQx(&debug_ll.dbg_var, return_long);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -87,31 +93,25 @@ int Debug_LowLevel_Initialize(const char* external_date)
|
||||
}
|
||||
|
||||
// Ïðåîáðàçóåì external_date â ñòðóêòóðó
|
||||
DateTimeHex ext;
|
||||
DateTime_t ext;
|
||||
ext.year = (external_date[0] << 8) | external_date[1];
|
||||
ext.day = external_date[2];
|
||||
ext.month = external_date[3];
|
||||
ext.hour = external_date[4];
|
||||
ext.minute = external_date[5];
|
||||
|
||||
// Çàïîëíèì ñòðóêòóðó build èç ìàêðîñîâ (ïðåäïîëàãàåì, ÷òî îíè ÷èñëîâûå)
|
||||
DateTimeHex build;
|
||||
|
||||
build.year = BUILD_YEAR; // íàïðèìåð 2025
|
||||
build.month = BUILD_MONTH; // íàïðèìåð 7
|
||||
build.day = BUILD_DATA; // íàïðèìåð 11
|
||||
build.hour = BUILD_HOURS; // íàïðèìåð 16
|
||||
build.minute = BUILD_MINUTES;// íàïðèìåð 50
|
||||
|
||||
// Ñðàâíåíèå âñåõ ïîëåé
|
||||
if (ext.year == build.year &&
|
||||
ext.month == build.month &&
|
||||
ext.day == build.day &&
|
||||
ext.hour == build.hour &&
|
||||
ext.minute == build.minute)
|
||||
if (ext.year == debug_ll.build_date.year &&
|
||||
ext.month == debug_ll.build_date.month &&
|
||||
ext.day == debug_ll.build_date.day &&
|
||||
ext.hour == debug_ll.build_date.hour &&
|
||||
ext.minute == debug_ll.build_date.minute)
|
||||
{
|
||||
debug_ll.isVerified = 1;
|
||||
return 0; // Ñîâïàëî
|
||||
}
|
||||
debug_ll.isVerified = 0;
|
||||
|
||||
return 1; // Íå ñîâïàëî
|
||||
}
|
||||
|
@ -76,23 +76,33 @@ typedef struct
|
||||
char name[11]; // 10 ñèìâîëîâ + '\0'
|
||||
}DebugVar_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
int year;
|
||||
char month;
|
||||
char day;
|
||||
char hour;
|
||||
char minute;
|
||||
} DateTimeHex;
|
||||
} DateTime_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DateTime_t build_date;
|
||||
unsigned int isVerified;
|
||||
DebugVar_t dbg_var;
|
||||
}DebugLowLevel_t;
|
||||
extern DebugLowLevel_t debug_ll;
|
||||
#define DATE_INIT {BUILD_YEAR, BUILD_MONTH, BUILD_DATA, BUILD_HOURS, BUILD_MINUTES}
|
||||
#define DEBUG_VAR_INIT {0, pt_uint16, t_iq_none, t_iq_none, "\0"}
|
||||
#define DEBUG_LOWLEVEL_INIT {DATE_INIT, 0, DEBUG_VAR_INIT}
|
||||
|
||||
|
||||
extern int DebugVar_Qnt;
|
||||
extern DebugVar_t dbg_vars[];
|
||||
|
||||
|
||||
int Debug_Test_Example(void);
|
||||
void Debug_Test_Example(void);
|
||||
|
||||
int Debug_LowLevel_ReadVar(DebugVar_t *var_ll, long *return_long);
|
||||
int Debug_LowLevel_ReadVar(long *return_long);
|
||||
int Debug_ReadVar(DebugVar_t *var, long *return_long);
|
||||
int Debug_ReadVarName(DebugVar_t *var, char *name_ptr);
|
||||
int Debug_LowLevel_Initialize(const char* external_date);
|
||||
|
38
debug_vars.c
38
debug_vars.c
@ -315,23 +315,23 @@ extern int zero_ADC[20];
|
||||
int DebugVar_Qnt = 19;
|
||||
#pragma DATA_SECTION(dbg_vars,".dbgvar_info")
|
||||
DebugVar_t dbg_vars[] = {\
|
||||
{(char *)&ADC0finishAddr, pt_int16, t_iq_none, t_iq_none, "ADC0EndAdr" }, \
|
||||
{(char *)&ADC_f[0][0], pt_int16, t_iq_none, t_iq_none, "ADC_f00" }, \
|
||||
{(char *)&ADC_f[0][1], pt_int16, t_iq_none, t_iq_none, "ADC_f01" }, \
|
||||
{(char *)&ADC_f[0][2], pt_int16, t_iq_none, t_iq_none, "ADC_f02" }, \
|
||||
{(char *)&ADC_f[0][3], pt_int16, t_iq_none, t_iq_none, "ADC_f03" }, \
|
||||
{(char *)&ADC_f[0][4], pt_int16, t_iq_none, t_iq_none, "ADC_f04" }, \
|
||||
{(char *)&ADC_f[0][5], pt_int16, t_iq_none, t_iq_none, "ADC_f05" }, \
|
||||
{(char *)&ADC_f[0][6], pt_int16, t_iq_none, t_iq_none, "ADC_f06" }, \
|
||||
{(char *)&ADC_f[0][7], pt_int16, t_iq, t_iq_none, "ADC_f07" }, \
|
||||
{(char *)&ADC_f[0][8], pt_int16, t_iq_none, t_iq_none, "ADC_f08" }, \
|
||||
{(char *)&ADC_f[0][9], pt_int16, t_iq_none, t_iq_none, "ADC_f09" }, \
|
||||
{(char *)&ADC_f[0][10], pt_int16, t_iq_none, t_iq_none, "ADC_f010" }, \
|
||||
{(char *)&ADC_f[0][11], pt_int16, t_iq_none, t_iq_none, "ADC_f011" }, \
|
||||
{(char *)&ADC_f[0][12], pt_int16, t_iq_none, t_iq_none, "ADC_f012" }, \
|
||||
{(char *)&ADC_f[0][13], pt_int16, t_iq_none, t_iq_none, "ADC_f013" }, \
|
||||
{(char *)&ADC_f[0][14], pt_int16, t_iq_none, t_iq_none, "ADC_f014" }, \
|
||||
{(char *)&ADC_f[0][15], pt_int16, t_iq_none, t_iq_none, "ADC_f015" }, \
|
||||
{(char *)&project.cds_tk[2].read.sbus.mask_protect_tk.all, pt_uint16, t_iq_none, t_iq_none, "tk2_ackcur" }, \
|
||||
{(char *)&project.cds_tk[1].plane_address, pt_uint16, t_iq_none, t_iq_none, "tk1_adr" }, \
|
||||
{(char *)&ADC0finishAddr, pt_int16, t_iq_none, t_iq_none, "ADC0EndAdr" }, \
|
||||
{(char *)&ADC_f[0][0], pt_int16, t_iq_none, t_iq_none, "ADC_f00" }, \
|
||||
{(char *)&ADC_f[0][1], pt_int16, t_iq_none, t_iq_none, "ADC_f01" }, \
|
||||
{(char *)&ADC_f[0][2], pt_int16, t_iq_none, t_iq_none, "ADC_f02" }, \
|
||||
{(char *)&ADC_f[0][3], pt_int16, t_iq_none, t_iq_none, "ADC_f03" }, \
|
||||
{(char *)&ADC_f[0][4], pt_int16, t_iq_none, t_iq_none, "ADC_f04" }, \
|
||||
{(char *)&ADC_f[0][5], pt_int16, t_iq_none, t_iq_none, "ADC_f05" }, \
|
||||
{(char *)&ADC_f[0][6], pt_int16, t_iq_none, t_iq_none, "ADC_f06" }, \
|
||||
{(char *)&ADC_f[0][7], pt_int16, t_iq, t_iq_none, "ADC_f07" }, \
|
||||
{(char *)&ADC_f[0][8], pt_int16, t_iq_none, t_iq_none, "ADC_f08" }, \
|
||||
{(char *)&ADC_f[0][9], pt_int16, t_iq_none, t_iq_none, "ADC_f09" }, \
|
||||
{(char *)&ADC_f[0][10], pt_int16, t_iq_none, t_iq_none, "ADC_f010" }, \
|
||||
{(char *)&ADC_f[0][11], pt_int16, t_iq_none, t_iq_none, "ADC_f011" }, \
|
||||
{(char *)&ADC_f[0][12], pt_int16, t_iq_none, t_iq_none, "ADC_f012" }, \
|
||||
{(char *)&ADC_f[0][13], pt_int16, t_iq_none, t_iq_none, "ADC_f013" }, \
|
||||
{(char *)&ADC_f[0][14], pt_int16, t_iq_none, t_iq_none, "ADC_f014" }, \
|
||||
{(char *)&ADC_f[0][15], pt_int16, t_iq_none, t_iq_none, "ADC_f015" }, \
|
||||
{(char *)&project.cds_tk[2].read.sbus.mask_protect_tk.all, pt_uint16, t_iq_none, t_iq_none, "tk2_ackcur" }, \
|
||||
{(char *)&project.cds_tk[1].plane_address, pt_uint16, t_iq_none, t_iq_none, "tk1_adr" }, \
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user