Compare commits
7 Commits
v1.0
...
c94a7e711c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c94a7e711c | ||
|
|
5be6343c33 | ||
|
|
043359fe66 | ||
|
|
c55f38ef1c | ||
|
|
ae2c90160e | ||
|
|
4de53090a1 | ||
|
|
742c4e9e1b |
BIN
DebugVarEdit.exe
BIN
DebugVarEdit.exe
Binary file not shown.
50
README.md
50
README.md
@@ -1,23 +1,56 @@
|
|||||||
# DebugVarEdit — Утилита для генерации переменных для отладки
|
# DebugTools - Просмотр переменных по указателям
|
||||||
|
Модуль состоит из трех файлов:
|
||||||
|
- **debug_tools.c** - реализация считывания переменных
|
||||||
|
- **debug_tools.h** - объявление всякого для считывания переменных
|
||||||
|
- **debug_vars.c** - определение массива считываемых переменных
|
||||||
|
|
||||||
|
Этот модуль предоставляет функциональность для чтения значений переменных во встроенной системе, включая работу с IQ-форматами, защиту доступа и проверку диапазонов памяти.
|
||||||
|
|
||||||
|
Для чтения переменных можно использовать функции:
|
||||||
|
|
||||||
|
```c
|
||||||
|
int Debug_ReadVar(int var_ind, int32_t *return_long);
|
||||||
|
int Debug_ReadVarName(int var_ind, DebugVarName_t name_ptr);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Переменные доступные для чтения определяются в **debug_vars.c** (их можно прописывать вручную или генерировать через **DebugVarEdit**):
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Определение массива с указателями на переменные для отладки
|
||||||
|
int DebugVar_Qnt = 5;
|
||||||
|
#pragma DATA_SECTION(dbg_vars,".dbgvar_info")
|
||||||
|
// pointer_type iq_type return_iq_type short_name
|
||||||
|
DebugVar_t dbg_vars[] = {\
|
||||||
|
{(uint8_t *)&freqTerm, pt_float, t_iq_none, t_iq10, "freqT" }, \
|
||||||
|
{(uint8_t *)&ADC_sf[0][0], pt_int16, t_iq_none, t_iq_none, "ADC_sf00" }, \
|
||||||
|
{(uint8_t *)&ADC_sf[0][1], pt_int16, t_iq_none, t_iq_none, "ADC_sf01" }, \
|
||||||
|
{(uint8_t *)&ADC_sf[0][2], pt_int16, t_iq_none, t_iq_none, "ADC_sf02" }, \
|
||||||
|
{(uint8_t *)&ADC_sf[0][3], pt_int16, t_iq_none, t_iq_none, "ADC_sf03" }, \
|
||||||
|
{(uint8_t *)&Bender[0].KOhms, pt_uint16, t_iq, t_iq10, "Bend0.KOhm" }, \
|
||||||
|
{(uint8_t *)&Bender[0].Times, pt_uint16, t_iq_none, t_iq_none, "Bend0.Time" }, \
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
# DebugVarEdit - Настройка переменных
|
||||||
**DebugVarEdit** — графическое приложение для Windows, предназначенное для настройки и генерации отладочных переменных (`debug_vars.c`) на основе исходного C-проекта. Работает с `makefile` проекта, сохраняет изменения в XML и позволяет удобно редактировать переменные и их типы через интерфейс.
|
**DebugVarEdit** — графическое приложение для Windows, предназначенное для настройки и генерации отладочных переменных (`debug_vars.c`) на основе исходного C-проекта. Работает с `makefile` проекта, сохраняет изменения в XML и позволяет удобно редактировать переменные и их типы через интерфейс.
|
||||||
|
|
||||||
Программа — один исполняемый файл `DebugVarEdit.exe`, не требующий установки и дополнительных зависимостей.
|
Программа — один исполняемый файл `DebugVarEdit.exe`, не требующий установки и дополнительных зависимостей.
|
||||||
|
|
||||||
> Требуется Windows 7 или новее.
|
> Требуется Windows 7 или новее.
|
||||||
|
|
||||||
> [Для разработчиков](#для-разработчиков)
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Как использовать
|
## Как использовать приложение
|
||||||
|
|
||||||
1. Запустите **DebugVarEdit.exe.**
|
1. Запустите **DebugVarEdit.exe.**
|
||||||
|
|
||||||
2. Укажите пути:
|
2. Укажите пути и контроллер:
|
||||||
- **Путь к XML** — новый или существующий файл настроек переменных;
|
- **Путь к XML** — новый или существующий файл настроек переменных;
|
||||||
- **Путь к проекту** — путь к корню проека (папка, которая является корнем для проекта в Code Composer);
|
- **Путь к проекту** — путь к корню проека (папка, которая является корнем для проекта в Code Composer);
|
||||||
- **Путь к makefile** — путь к `makefile` относительно корня проекта;
|
- **Путь к makefile** — путь к `makefile` относительно корня проекта;
|
||||||
- **Путь для debug_vars.c** — папка, куда будет сгенерирован файл `debug_vars.c` с переменными.
|
- **Путь для debug_vars.c** — папка, куда будет сгенерирован файл `debug_vars.c` с переменными.
|
||||||
|
- **МК** — TMS или STM. Они отличаются размером int, и также принятыми кодировками файлов (utf-8/cp1251)
|
||||||
|
|
||||||
3. Нажмите **Сканировать переменные**:
|
3. Нажмите **Сканировать переменные**:
|
||||||
- Программа проанализирует исходники, указанные в makefile, и найдёт все переменные.
|
- Программа проанализирует исходники, указанные в makefile, и найдёт все переменные.
|
||||||
@@ -61,7 +94,7 @@
|
|||||||
## Пример XML-файла
|
## Пример XML-файла
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<project proj_path="C:/myproj" makefile_path="Debug/Makefile" structs_path="debugVars/structs.xml">
|
<project proj_path="C:/myproj" makefile_path="Debug/Makefile" structs_path="Src/DebugTools/structs.xml">
|
||||||
<variables>
|
<variables>
|
||||||
<var name="g_myvar">
|
<var name="g_myvar">
|
||||||
<enable>true</enable>
|
<enable>true</enable>
|
||||||
@@ -79,6 +112,7 @@
|
|||||||
</project>
|
</project>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
---
|
---
|
||||||
|
|
||||||
# Для разработчиков
|
# Для разработчиков
|
||||||
@@ -107,14 +141,14 @@ Src
|
|||||||
|
|
||||||
Для запуска приложения:
|
Для запуска приложения:
|
||||||
- **Python 3.7+**
|
- **Python 3.7+**
|
||||||
- **clang** — используется для парсинга C-кода (требуется `libclang.dll`)
|
- **clang** — используется для парсинга C-кода (требуется `libclang.dll`, лежит в папке Src)
|
||||||
- **PySide2** — GUI-фреймворк
|
- **PySide2** — GUI-фреймворк
|
||||||
- **lxml** — работа с XML
|
- **lxml** — работа с XML
|
||||||
> Python 3.7 и PySide2 рекомендуется для совместимости с Windows 7
|
> Python 3.7 и PySide2 рекомендуется для совместимости с Windows 7
|
||||||
|
|
||||||
Для сборки `.exe`:
|
Для сборки `.exe`:
|
||||||
- **Nuitka** — создает полностью автономный `.exe` без внешних зависимостей
|
- **Nuitka** — создает полностью автономный `.exe` без внешних зависимостей
|
||||||
- **PyInstaller** — создает `.exe` с зависимостью от `pythonXX.dll` (Python должен быть установлен)
|
- **PyInstaller** — создает `.exe` с зависимостью от `pythonXX.dll` (Python должен быть установлен для запуска `.exe`)
|
||||||
|
|
||||||
|
|
||||||
### Сборка:
|
### Сборка:
|
||||||
@@ -134,5 +168,5 @@ Src
|
|||||||
### Установка зависимостей
|
### Установка зависимостей
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install PySide2 lxml nuitka pyinstaller
|
pip install clang PySide2 lxml nuitka pyinstaller
|
||||||
```
|
```
|
||||||
@@ -5,7 +5,7 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import lxml.etree as ET
|
import lxml.etree as ET
|
||||||
from generate_debug_vars import type_map
|
from generate_debug_vars import type_map, choose_type_map
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
import threading
|
import threading
|
||||||
from generate_debug_vars import run_generate
|
from generate_debug_vars import run_generate
|
||||||
@@ -21,8 +21,9 @@ from PySide2.QtWidgets import (
|
|||||||
QApplication, QWidget, QTableWidget, QTableWidgetItem,
|
QApplication, QWidget, QTableWidget, QTableWidgetItem,
|
||||||
QCheckBox, QComboBox, QLineEdit, QVBoxLayout, QHBoxLayout, QPushButton,
|
QCheckBox, QComboBox, QLineEdit, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||||
QCompleter, QAbstractItemView, QLabel, QMessageBox, QFileDialog, QTextEdit,
|
QCompleter, QAbstractItemView, QLabel, QMessageBox, QFileDialog, QTextEdit,
|
||||||
QDialog, QTreeWidget, QTreeWidgetItem, QSizePolicy, QHeaderView
|
QDialog, QTreeWidget, QTreeWidgetItem, QSizePolicy, QHeaderView,
|
||||||
)
|
QMenuBar, QMenu, QAction
|
||||||
|
)
|
||||||
from PySide2.QtGui import QTextCursor, QKeyEvent, QIcon, QFont
|
from PySide2.QtGui import QTextCursor, QKeyEvent, QIcon, QFont
|
||||||
from PySide2.QtCore import Qt, QProcess, QObject, Signal, QSettings
|
from PySide2.QtCore import Qt, QProcess, QObject, Signal, QSettings
|
||||||
|
|
||||||
@@ -58,6 +59,7 @@ class VarEditor(QWidget):
|
|||||||
self.output_path = None
|
self.output_path = None
|
||||||
self._updating = False # Флаг блокировки рекурсии
|
self._updating = False # Флаг блокировки рекурсии
|
||||||
self._resizing = False # флаг блокировки повторного вызова
|
self._resizing = False # флаг блокировки повторного вызова
|
||||||
|
self.target = 'TMS'
|
||||||
self.initUI()
|
self.initUI()
|
||||||
|
|
||||||
def initUI(self):
|
def initUI(self):
|
||||||
@@ -122,6 +124,32 @@ class VarEditor(QWidget):
|
|||||||
self.btn_update_vars = QPushButton(scan_title)
|
self.btn_update_vars = QPushButton(scan_title)
|
||||||
self.btn_update_vars.clicked.connect(self.update_vars_data)
|
self.btn_update_vars.clicked.connect(self.update_vars_data)
|
||||||
|
|
||||||
|
# Добавляем чекбокс для выбора типовой карты
|
||||||
|
# --- Создаем верхнее меню ---
|
||||||
|
menubar = QMenuBar(self)
|
||||||
|
menubar.setToolTip('Разные размеры int и кодировки файлов')
|
||||||
|
self.target_menu = QMenu("МК:", menubar)
|
||||||
|
# Создаем действия для выбора Target
|
||||||
|
self.action_tms = QAction("TMS", self, checkable=True)
|
||||||
|
self.action_stm = QAction("STM", self, checkable=True)
|
||||||
|
# Инициализируем QSettings с именем организации и приложения
|
||||||
|
self.settings = QSettings("SET", "DebugVarEdit_MainWindow")
|
||||||
|
# Восстанавливаем сохранённое состояние, если есть
|
||||||
|
mcu = self.settings.value("mcu_choosen", True, type=str)
|
||||||
|
self.on_target_selected(mcu)
|
||||||
|
|
||||||
|
self.target_menu.setToolTip(f'TMS: Размер int 16 бит. Кодировка cp1251\nSTM: Размер int 32 бита. Кодировка utf-8')
|
||||||
|
# Группируем действия чтобы выбирался только один
|
||||||
|
self.action_tms.triggered.connect(lambda: self.on_target_selected("TMS"))
|
||||||
|
self.action_tms.setToolTip('Размер int 16 бит. Кодировка cp1251')
|
||||||
|
self.action_stm.triggered.connect(lambda: self.on_target_selected("STM"))
|
||||||
|
self.action_stm.setToolTip('Размер int 32 бита. Кодировка utf-8')
|
||||||
|
|
||||||
|
self.target_menu.addAction(self.action_tms)
|
||||||
|
self.target_menu.addAction(self.action_stm)
|
||||||
|
|
||||||
|
menubar.addMenu(self.target_menu)
|
||||||
|
|
||||||
# Кнопка сохранения
|
# Кнопка сохранения
|
||||||
btn_save = QPushButton(build_title)
|
btn_save = QPushButton(build_title)
|
||||||
btn_save.clicked.connect(self.save_build)
|
btn_save.clicked.connect(self.save_build)
|
||||||
@@ -137,6 +165,7 @@ class VarEditor(QWidget):
|
|||||||
self.table = VariableTableWidget()
|
self.table = VariableTableWidget()
|
||||||
# Основной layout
|
# Основной layout
|
||||||
layout = QVBoxLayout()
|
layout = QVBoxLayout()
|
||||||
|
layout.setMenuBar(menubar) # прикрепляем menubar в layout сверху
|
||||||
layout.addLayout(xml_layout)
|
layout.addLayout(xml_layout)
|
||||||
layout.addLayout(proj_layout)
|
layout.addLayout(proj_layout)
|
||||||
layout.addLayout(makefile_layout)
|
layout.addLayout(makefile_layout)
|
||||||
@@ -150,7 +179,20 @@ class VarEditor(QWidget):
|
|||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
|
||||||
|
def on_target_selected(self, target):
|
||||||
|
self.target_menu.setTitle(f'МК: {target}')
|
||||||
|
self.settings.setValue("mcu_choosen", target)
|
||||||
|
self.target = target.lower()
|
||||||
|
if self.target == "stm":
|
||||||
|
choose_type_map(True)
|
||||||
|
self.action_stm.setChecked(True)
|
||||||
|
self.action_tms.setChecked(False)
|
||||||
|
else:
|
||||||
|
choose_type_map(False)
|
||||||
|
self.action_tms.setChecked(True)
|
||||||
|
self.action_stm.setChecked(False)
|
||||||
|
|
||||||
|
|
||||||
def get_xml_path(self):
|
def get_xml_path(self):
|
||||||
xml_path = self.xml_output_edit.text().strip()
|
xml_path = self.xml_output_edit.text().strip()
|
||||||
return xml_path
|
return xml_path
|
||||||
@@ -241,7 +283,7 @@ class VarEditor(QWidget):
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
run_generate(self.proj_path, self.xml_path, self.output_path)
|
run_generate(self.proj_path, self.xml_path, self.output_path, self.table._shortname_size)
|
||||||
QMessageBox.information(self, "Готово", "Файл debug_vars.c успешно сгенерирован.")
|
QMessageBox.information(self, "Готово", "Файл debug_vars.c успешно сгенерирован.")
|
||||||
self.update()
|
self.update()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -358,15 +400,25 @@ class VarEditor(QWidget):
|
|||||||
super().keyPressEvent(event)
|
super().keyPressEvent(event)
|
||||||
|
|
||||||
def __browse_makefile(self):
|
def __browse_makefile(self):
|
||||||
|
if self.target == 'stm':
|
||||||
|
file_filter = "Makefile или Keil-проект (*.uvprojx *.uvproj makefile);;Все файлы (*)"
|
||||||
|
dialog_title = "Выберите Makefile или Keil-проект"
|
||||||
|
else: # 'TMS' или по умолчанию
|
||||||
|
file_filter = "Makefile (makefile);;Все файлы (*)"
|
||||||
|
dialog_title = "Выберите Makefile"
|
||||||
|
|
||||||
file_path, _ = QFileDialog.getOpenFileName(
|
file_path, _ = QFileDialog.getOpenFileName(
|
||||||
self, "Выберите Makefile", filter="Makefile (makefile);;All Files (*)"
|
self,
|
||||||
|
dialog_title,
|
||||||
|
filter=file_filter
|
||||||
)
|
)
|
||||||
if file_path and self.proj_path:
|
if file_path:
|
||||||
path = myXML.make_relative_path(file_path, self.proj_path)
|
if self.proj_path:
|
||||||
else:
|
path = myXML.make_relative_path(file_path, self.proj_path)
|
||||||
path = file_path
|
else:
|
||||||
self.makefile_edit.setText(path)
|
path = file_path
|
||||||
self.makefile_path = path
|
self.makefile_edit.setText(path)
|
||||||
|
self.makefile_path = path
|
||||||
|
|
||||||
def __browse_source_output(self):
|
def __browse_source_output(self):
|
||||||
dir_path = QFileDialog.getExistingDirectory(self, "Выберите папку для debug_vars.c")
|
dir_path = QFileDialog.getExistingDirectory(self, "Выберите папку для debug_vars.c")
|
||||||
@@ -438,12 +490,12 @@ class VarEditor(QWidget):
|
|||||||
self.write_to_xml()
|
self.write_to_xml()
|
||||||
|
|
||||||
|
|
||||||
def __open_variable_selector(self):
|
def __open_variable_selector(self):
|
||||||
|
self.update()
|
||||||
if not self.vars_list:
|
if not self.vars_list:
|
||||||
QMessageBox.warning(self, "Нет переменных", f"Сначала загрузите переменные ({scan_title}).")
|
QMessageBox.warning(self, "Нет переменных", f"Сначала загрузите переменные ({scan_title}).")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.update()
|
|
||||||
dlg = VariableSelectorDialog(self.table, 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()
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ from xml.dom import minidom
|
|||||||
import myXML
|
import myXML
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
|
shortnameSize = 10
|
||||||
|
|
||||||
# === Словарь соответствия типов XML → DebugVarType_t ===
|
# === Словарь соответствия типов XML → DebugVarType_t ===
|
||||||
type_map = dict([
|
type_map_tms = dict([
|
||||||
*[(k, 'pt_int8') for k in ('signed char', 'char')],
|
*[(k, 'pt_int8') for k in ('signed char', 'char')],
|
||||||
*[(k, 'pt_int16') for k in ('int', 'int16', 'short')],
|
*[(k, 'pt_int16') for k in ('int', 'int16', 'short')],
|
||||||
*[(k, 'pt_int32') for k in ('long', 'int32', '_iqx')],
|
*[(k, 'pt_int32') for k in ('long', 'int32', '_iqx')],
|
||||||
@@ -52,6 +53,150 @@ type_map = dict([
|
|||||||
('struct[]', 'pt_arr_struct'),
|
('struct[]', 'pt_arr_struct'),
|
||||||
('union[]', 'pt_arr_union'),
|
('union[]', 'pt_arr_union'),
|
||||||
])
|
])
|
||||||
|
# === Словарь соответствия типов XML → DebugVarType_t ===
|
||||||
|
type_map_stm32 = dict([
|
||||||
|
|
||||||
|
*[(k, 'pt_int8') for k in (
|
||||||
|
'int8_t', 'signed char', 'char'
|
||||||
|
)],
|
||||||
|
|
||||||
|
# --- 8-bit unsigned ---
|
||||||
|
*[(k, 'pt_uint8') for k in (
|
||||||
|
'uint8_t', 'unsigned char'
|
||||||
|
)],
|
||||||
|
|
||||||
|
# --- 16-bit signed ---
|
||||||
|
*[(k, 'pt_int16') for k in (
|
||||||
|
'int16_t', 'short', 'short int', 'signed short', 'signed short int'
|
||||||
|
)],
|
||||||
|
|
||||||
|
# --- 16-bit unsigned ---
|
||||||
|
*[(k, 'pt_uint16') for k in (
|
||||||
|
'uint16_t', 'unsigned short', 'unsigned short int'
|
||||||
|
)],
|
||||||
|
|
||||||
|
# --- 32-bit signed ---
|
||||||
|
*[(k, 'pt_int32') for k in (
|
||||||
|
'int32_t', 'int', 'signed', 'signed int'
|
||||||
|
)],
|
||||||
|
|
||||||
|
# --- 32-bit unsigned ---
|
||||||
|
*[(k, 'pt_uint32') for k in (
|
||||||
|
'uint32_t', 'unsigned', 'unsigned int'
|
||||||
|
)],
|
||||||
|
|
||||||
|
# --- 64-bit signed ---
|
||||||
|
*[(k, 'pt_int64') for k in (
|
||||||
|
'int64_t', 'long long', 'signed long long', 'signed long long int'
|
||||||
|
)],
|
||||||
|
|
||||||
|
# --- 64-bit unsigned ---
|
||||||
|
*[(k, 'pt_uint64') for k in (
|
||||||
|
'uint64_t', 'unsigned long long', 'unsigned long long int'
|
||||||
|
)],
|
||||||
|
|
||||||
|
# --- Float ---
|
||||||
|
*[(k, 'pt_float') for k in (
|
||||||
|
'float', 'float32_t'
|
||||||
|
)],
|
||||||
|
|
||||||
|
# --- Struct and Union ---
|
||||||
|
('struct', 'pt_struct'),
|
||||||
|
('union', 'pt_union'),
|
||||||
|
('struct*', 'pt_ptr_struct'),
|
||||||
|
('union*', 'pt_ptr_union'),
|
||||||
|
('struct[]', 'pt_arr_struct'),
|
||||||
|
('union[]', 'pt_arr_union'),
|
||||||
|
|
||||||
|
# === POINTERS ===
|
||||||
|
|
||||||
|
# 8-bit
|
||||||
|
*[(k, 'pt_ptr_int8') for k in (
|
||||||
|
'int8_t*', 'signed char*', 'char*'
|
||||||
|
)],
|
||||||
|
*[(k, 'pt_ptr_uint8') for k in (
|
||||||
|
'uint8_t*', 'unsigned char*'
|
||||||
|
)],
|
||||||
|
|
||||||
|
# 16-bit
|
||||||
|
*[(k, 'pt_ptr_int16') for k in (
|
||||||
|
'int16_t*', 'short*', 'short int*', 'signed short*', 'signed short int*'
|
||||||
|
)],
|
||||||
|
*[(k, 'pt_ptr_uint16') for k in (
|
||||||
|
'uint16_t*', 'unsigned short*', 'unsigned short int*'
|
||||||
|
)],
|
||||||
|
|
||||||
|
# 32-bit
|
||||||
|
*[(k, 'pt_ptr_int32') for k in (
|
||||||
|
'int32_t*', 'int*', 'signed*', 'signed int*'
|
||||||
|
)],
|
||||||
|
*[(k, 'pt_ptr_uint32') for k in (
|
||||||
|
'uint32_t*', 'unsigned*', 'unsigned int*'
|
||||||
|
)],
|
||||||
|
|
||||||
|
# 64-bit
|
||||||
|
*[(k, 'pt_ptr_int64') for k in (
|
||||||
|
'int64_t*', 'long long*', 'signed long long*', 'signed long long int*'
|
||||||
|
)],
|
||||||
|
*[(k, 'pt_ptr_uint64') for k in (
|
||||||
|
'uint64_t*', 'unsigned long long*', 'unsigned long long int*'
|
||||||
|
)],
|
||||||
|
|
||||||
|
# float*
|
||||||
|
*[(k, 'pt_ptr_float') for k in (
|
||||||
|
'float*', 'float32_t*'
|
||||||
|
)],
|
||||||
|
|
||||||
|
# === ARRAYS ===
|
||||||
|
|
||||||
|
# 8-bit
|
||||||
|
*[(k, 'pt_arr_int8') for k in (
|
||||||
|
'int8_t[]', 'signed char[]', 'char[]'
|
||||||
|
)],
|
||||||
|
*[(k, 'pt_arr_uint8') for k in (
|
||||||
|
'uint8_t[]', 'unsigned char[]'
|
||||||
|
)],
|
||||||
|
|
||||||
|
# 16-bit
|
||||||
|
*[(k, 'pt_arr_int16') for k in (
|
||||||
|
'int16_t[]', 'short[]', 'short int[]', 'signed short[]', 'signed short int[]'
|
||||||
|
)],
|
||||||
|
*[(k, 'pt_arr_uint16') for k in (
|
||||||
|
'uint16_t[]', 'unsigned short[]', 'unsigned short int[]'
|
||||||
|
)],
|
||||||
|
|
||||||
|
# 32-bit
|
||||||
|
*[(k, 'pt_arr_int32') for k in (
|
||||||
|
'int32_t[]', 'int[]', 'signed[]', 'signed int[]'
|
||||||
|
)],
|
||||||
|
*[(k, 'pt_arr_uint32') for k in (
|
||||||
|
'uint32_t[]', 'unsigned[]', 'unsigned int[]'
|
||||||
|
)],
|
||||||
|
|
||||||
|
# 64-bit
|
||||||
|
*[(k, 'pt_arr_int64') for k in (
|
||||||
|
'int64_t[]', 'long long[]', 'signed long long[]', 'signed long long int[]'
|
||||||
|
)],
|
||||||
|
*[(k, 'pt_arr_uint64') for k in (
|
||||||
|
'uint64_t[]', 'unsigned long long[]', 'unsigned long long int[]'
|
||||||
|
)],
|
||||||
|
|
||||||
|
# float[]
|
||||||
|
*[(k, 'pt_arr_float') for k in (
|
||||||
|
'float[]', 'float32_t[]'
|
||||||
|
)],
|
||||||
|
])
|
||||||
|
type_map = type_map_tms
|
||||||
|
stm_flag_global = 0
|
||||||
|
def choose_type_map(stm_flag):
|
||||||
|
global type_map # объявляем, что будем менять глобальную переменную
|
||||||
|
global stm_flag_global # объявляем, что будем менять глобальную переменную
|
||||||
|
if stm_flag:
|
||||||
|
type_map = type_map_stm32
|
||||||
|
stm_flag_global = 1
|
||||||
|
else:
|
||||||
|
type_map = type_map_tms
|
||||||
|
stm_flag_global = 0
|
||||||
|
|
||||||
def map_type_to_pt(typename, varname=None, typedef_map=None):
|
def map_type_to_pt(typename, varname=None, typedef_map=None):
|
||||||
typename_orig = typename.strip()
|
typename_orig = typename.strip()
|
||||||
@@ -140,15 +285,20 @@ def add_new_vars_to_xml(proj_path, xml_rel_path, output_path):
|
|||||||
Возвращает True если что-то добавлено и XML перезаписан, иначе False.
|
Возвращает True если что-то добавлено и XML перезаписан, иначе False.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
pattern = re.compile(
|
||||||
|
r'{\s*\(uint8_t\s*\*\)\s*&([a-zA-Z_][a-zA-Z0-9_]*(?:\[.*?\])?(?:(?:\.|->)[a-zA-Z_][a-zA-Z0-9_]*(?:\[.*?\])?)*)\s*,\s*'
|
||||||
|
r'(pt_\w+)\s*,\s*'
|
||||||
|
r'(t?_?iq\w+)\s*,\s*'
|
||||||
|
r'(t?_?iq\w+)\s*,\s*'
|
||||||
|
r'"([^"]+)"'
|
||||||
|
)
|
||||||
# Считываем существующие переменные
|
# Считываем существующие переменные
|
||||||
parsed_vars = {}
|
parsed_vars = {}
|
||||||
if os.path.isfile(output_path):
|
if os.path.isfile(output_path):
|
||||||
with open(output_path, 'r', encoding='utf-8', errors='ignore') as f:
|
with open(output_path, 'r', encoding='utf-8', errors='ignore') as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
# {(char *)&some.deep.var.name , pt_uint16 , t_iq15 , "ShortName"},
|
# {(uint8_t *)&some.deep.var.name , pt_uint16 , t_iq15 , t_iq10, "ShortName"},
|
||||||
m = re.match(
|
m = pattern.search(line)
|
||||||
r'{\s*\(char\s*\*\)\s*&([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)\s*,\s*(pt_\w+)\s*,\s*(t?iq_\w+)\s*,\s*"([^"]+)"',
|
|
||||||
line)
|
|
||||||
if m:
|
if m:
|
||||||
full_varname = m.group(1) # e.g., some.deep.var.name
|
full_varname = m.group(1) # e.g., some.deep.var.name
|
||||||
pt_type = m.group(2)
|
pt_type = m.group(2)
|
||||||
@@ -285,12 +435,27 @@ def read_vars_from_xml(proj_path, xml_rel_path):
|
|||||||
return unique_vars, include_files, vars_need_extern
|
return unique_vars, include_files, vars_need_extern
|
||||||
|
|
||||||
|
|
||||||
|
def read_file_try_encodings(filepath):
|
||||||
|
if not os.path.isfile(filepath):
|
||||||
|
# Файл не существует — просто вернуть пустую строку или None
|
||||||
|
return "", None
|
||||||
|
for enc in ['utf-8', 'cp1251']:
|
||||||
|
try:
|
||||||
|
with open(filepath, 'r', encoding=enc) as f:
|
||||||
|
content = f.read()
|
||||||
|
return content, enc
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
continue
|
||||||
|
raise UnicodeDecodeError(f"Не удалось прочитать файл {filepath} с кодировками utf-8 и cp1251")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def generate_vars_file(proj_path, xml_path, output_dir):
|
def generate_vars_file(proj_path, xml_path, output_dir):
|
||||||
output_dir = os.path.join(proj_path, output_dir)
|
output_dir = os.path.join(proj_path, output_dir)
|
||||||
os.makedirs(output_dir, exist_ok=True)
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
output_path = os.path.join(output_dir, 'debug_vars.c')
|
output_path = os.path.join(output_dir, 'debug_vars.c')
|
||||||
|
LIBC_path = os.path.join(output_dir, 'debug_tools.c')
|
||||||
|
LIBH_path = os.path.join(output_dir, 'debug_tools.h')
|
||||||
|
|
||||||
|
|
||||||
# Запись новых переменных для в XML
|
# Запись новых переменных для в XML
|
||||||
@@ -338,7 +503,7 @@ def generate_vars_file(proj_path, xml_path, output_dir):
|
|||||||
# Дополнительные поля, например комментарий
|
# Дополнительные поля, например комментарий
|
||||||
comment = info.get("comment", "")
|
comment = info.get("comment", "")
|
||||||
short_name = info.get("shortname", f'"{vname}"')
|
short_name = info.get("shortname", f'"{vname}"')
|
||||||
short_trimmed = short_name[:10] # ограничиваем длину до 10
|
short_trimmed = short_name[:shortnameSize] # ограничиваем длину до 10
|
||||||
|
|
||||||
if pt_type not in ('pt_struct', 'pt_union'):
|
if pt_type not in ('pt_struct', 'pt_union'):
|
||||||
f_name = f'{vname},'
|
f_name = f'{vname},'
|
||||||
@@ -348,7 +513,7 @@ def generate_vars_file(proj_path, xml_path, output_dir):
|
|||||||
f_short_name = f'"{short_trimmed}"' # оборачиваем в кавычки
|
f_short_name = f'"{short_trimmed}"' # оборачиваем в кавычки
|
||||||
# Добавим комментарий после записи, если он есть
|
# Добавим комментарий после записи, если он есть
|
||||||
comment_str = f' // {comment}' if comment else ''
|
comment_str = f' // {comment}' if comment else ''
|
||||||
line = f'{{(char *)&{f_name:<57} {f_type:<15} {f_iq:<15} {f_ret_iq:<15} {f_short_name:<21}}}, \\{comment_str}'
|
line = f'{{(uint8_t *)&{f_name:<58} {f_type:<15} {f_iq:<15} {f_ret_iq:<15} {f_short_name:<21}}}, \\{comment_str}'
|
||||||
new_debug_vars[vname] = line
|
new_debug_vars[vname] = line
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@@ -394,14 +559,19 @@ def generate_vars_file(proj_path, xml_path, output_dir):
|
|||||||
|
|
||||||
out_lines.append(f'\n\n// Определение массива с указателями на переменные для отладки')
|
out_lines.append(f'\n\n// Определение массива с указателями на переменные для отладки')
|
||||||
out_lines.append(f'int DebugVar_Qnt = {len(all_debug_lines)};')
|
out_lines.append(f'int DebugVar_Qnt = {len(all_debug_lines)};')
|
||||||
out_lines.append('#pragma DATA_SECTION(dbg_vars,".dbgvar_info")')
|
if stm_flag_global == 0:
|
||||||
|
out_lines.append('#pragma DATA_SECTION(dbg_vars,".dbgvar_info")')
|
||||||
|
out_lines.append('// pointer_type iq_type return_iq_type short_name')
|
||||||
out_lines.append('DebugVar_t dbg_vars[] = {\\')
|
out_lines.append('DebugVar_t dbg_vars[] = {\\')
|
||||||
out_lines.extend(all_debug_lines)
|
out_lines.extend(all_debug_lines)
|
||||||
out_lines.append('};')
|
out_lines.append('};')
|
||||||
out_lines.append('')
|
out_lines.append('')
|
||||||
# Выберем кодировку для записи файла
|
# Выберем кодировку для записи файла
|
||||||
# Если встречается несколько, возьмем первую из set
|
# Если встречается несколько, возьмем первую из set
|
||||||
enc_to_write = 'cp1251'
|
if stm_flag_global == 0:
|
||||||
|
enc_to_write = 'cp1251'
|
||||||
|
else:
|
||||||
|
enc_to_write = 'utf-8'
|
||||||
|
|
||||||
#print("== GLOBAL VARS FOUND ==")
|
#print("== GLOBAL VARS FOUND ==")
|
||||||
#for vname, (vtype, path) in vars_in_c.items():
|
#for vname, (vtype, path) in vars_in_c.items():
|
||||||
@@ -411,6 +581,16 @@ def generate_vars_file(proj_path, xml_path, output_dir):
|
|||||||
with open(output_path, 'w', encoding=enc_to_write) as f:
|
with open(output_path, 'w', encoding=enc_to_write) as f:
|
||||||
f.write('\n'.join(out_lines))
|
f.write('\n'.join(out_lines))
|
||||||
|
|
||||||
|
if os.path.isfile(LIBC_path):
|
||||||
|
libc_code, _ = read_file_try_encodings(LIBC_path)
|
||||||
|
with open(LIBC_path, 'w', encoding=enc_to_write) as f:
|
||||||
|
f.write(libc_code)
|
||||||
|
|
||||||
|
if os.path.isfile(LIBH_path):
|
||||||
|
libh_code, _ = read_file_try_encodings(LIBH_path)
|
||||||
|
with open(LIBH_path, 'w', encoding=enc_to_write) as f:
|
||||||
|
f.write(libh_code)
|
||||||
|
|
||||||
print(f'Файл debug_vars.c сгенерирован в кодировке, переменных: {len(all_debug_lines)}')
|
print(f'Файл debug_vars.c сгенерирован в кодировке, переменных: {len(all_debug_lines)}')
|
||||||
|
|
||||||
|
|
||||||
@@ -472,16 +652,17 @@ Usage example:
|
|||||||
print(f"Error: Project path '{proj_path}' не является директорией или не существует.")
|
print(f"Error: Project path '{proj_path}' не является директорией или не существует.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
generate_vars_file(proj_path, xml_path_rel, output_dir_rel)
|
generate_vars_file(proj_path, xml_path_rel, output_dir_rel, 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
||||||
def run_generate(proj_path, xml_path, output_dir):
|
def run_generate(proj_path, xml_path, output_dir, shortname_size):
|
||||||
import os
|
import os
|
||||||
|
global shortnameSize
|
||||||
|
shortnameSize = shortname_size
|
||||||
# Normalize absolute paths
|
# Normalize absolute paths
|
||||||
proj_path = os.path.abspath(proj_path)
|
proj_path = os.path.abspath(proj_path)
|
||||||
xml_path_abs = os.path.abspath(xml_path)
|
xml_path_abs = os.path.abspath(xml_path)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
from lxml import etree as ET
|
||||||
|
|
||||||
|
|
||||||
def strip_single_line_comments(code):
|
def strip_single_line_comments(code):
|
||||||
@@ -66,89 +67,176 @@ def find_all_includes_recursive(c_files, include_dirs, processed_files=None):
|
|||||||
return include_files
|
return include_files
|
||||||
|
|
||||||
|
|
||||||
|
def parse_objects_list(objects_list_path, project_root):
|
||||||
|
c_files = []
|
||||||
|
include_dirs = set()
|
||||||
|
|
||||||
|
if not os.path.isfile(objects_list_path):
|
||||||
|
return c_files, include_dirs
|
||||||
|
|
||||||
|
with open(objects_list_path, 'r', encoding='utf-8') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
line = line.strip().strip('"').replace("\\", "/")
|
||||||
|
if line.endswith(".o"):
|
||||||
|
c_file = re.sub(r"\.o$", ".c", line)
|
||||||
|
abs_path = os.path.normpath(os.path.join(project_root, c_file))
|
||||||
|
if os.path.isfile(abs_path):
|
||||||
|
if not any(x in abs_path for x in ["DebugTools", "v120", "v100"]):
|
||||||
|
c_files.append(abs_path)
|
||||||
|
include_dirs.add(os.path.dirname(abs_path))
|
||||||
|
|
||||||
|
return c_files, include_dirs
|
||||||
|
|
||||||
|
|
||||||
|
def parse_uvprojx(uvprojx_path):
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
import os
|
||||||
|
|
||||||
|
tree = ET.parse(uvprojx_path)
|
||||||
|
root = tree.getroot()
|
||||||
|
|
||||||
|
project_dir = os.path.dirname(os.path.abspath(uvprojx_path))
|
||||||
|
|
||||||
|
c_files = []
|
||||||
|
include_dirs = set()
|
||||||
|
defines = set()
|
||||||
|
|
||||||
|
# Найдём C-файлы и директории
|
||||||
|
for file_elem in root.findall(".//FilePath"):
|
||||||
|
file_path = file_elem.text
|
||||||
|
if file_path:
|
||||||
|
abs_path = os.path.normpath(os.path.join(project_dir, file_path))
|
||||||
|
if os.path.isfile(abs_path):
|
||||||
|
if abs_path.endswith(".c"):
|
||||||
|
c_files.append(abs_path)
|
||||||
|
include_dirs.add(os.path.dirname(abs_path))
|
||||||
|
|
||||||
|
# Включаем IncludePath
|
||||||
|
for inc_path_elem in root.findall(".//IncludePath"):
|
||||||
|
path_text = inc_path_elem.text
|
||||||
|
if path_text:
|
||||||
|
paths = path_text.split(';')
|
||||||
|
for p in paths:
|
||||||
|
p = p.strip()
|
||||||
|
if p:
|
||||||
|
abs_inc_path = os.path.normpath(os.path.join(project_dir, p))
|
||||||
|
if os.path.isdir(abs_inc_path):
|
||||||
|
include_dirs.add(abs_inc_path)
|
||||||
|
|
||||||
|
# Добавим <Define>
|
||||||
|
for define_elem in root.findall(".//Define"):
|
||||||
|
def_text = define_elem.text
|
||||||
|
if def_text:
|
||||||
|
for d in def_text.split(','):
|
||||||
|
d = d.strip()
|
||||||
|
if d:
|
||||||
|
defines.add(d)
|
||||||
|
|
||||||
|
h_files = find_all_includes_recursive(c_files, include_dirs)
|
||||||
|
|
||||||
|
return sorted(c_files), sorted(h_files), sorted(include_dirs), sorted(defines)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def parse_makefile(makefile_path, proj_path):
|
def parse_makefile(makefile_path, proj_path):
|
||||||
makefile_dir = os.path.dirname(makefile_path)
|
import os
|
||||||
project_root = proj_path
|
import re
|
||||||
|
|
||||||
|
project_root = os.path.abspath(proj_path)
|
||||||
|
c_files = []
|
||||||
|
include_dirs = set()
|
||||||
|
defines = [] # Заглушка: нет define-параметров из Makefile
|
||||||
|
|
||||||
with open(makefile_path, 'r', encoding='utf-8') as f:
|
with open(makefile_path, 'r', encoding='utf-8') as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
|
|
||||||
objs_lines = []
|
raw_entries = []
|
||||||
collecting = False
|
collecting = False
|
||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
stripped = line.strip()
|
stripped = line.strip()
|
||||||
if stripped.startswith("ORDERED_OBJS") and "+=" in stripped:
|
|
||||||
parts = stripped.split("\\")
|
if (("ORDERED_OBJS" in stripped or "C_SOURCES" in stripped) and ("+=" in stripped or "=" in stripped)):
|
||||||
first_part = parts[0]
|
|
||||||
idx = first_part.find("+=")
|
|
||||||
tail = first_part[idx+2:].strip()
|
|
||||||
if tail:
|
|
||||||
objs_lines.append(tail)
|
|
||||||
collecting = True
|
collecting = True
|
||||||
if len(parts) > 1:
|
|
||||||
for p in parts[1:]:
|
|
||||||
p = p.strip()
|
|
||||||
if p:
|
|
||||||
objs_lines.append(p)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if collecting:
|
if collecting:
|
||||||
if stripped.endswith("\\"):
|
line_clean = stripped.rstrip("\\").strip()
|
||||||
objs_lines.append(stripped[:-1].strip())
|
if line_clean:
|
||||||
else:
|
line_clean = re.sub(r"\$\([^)]+\)", "", line_clean)
|
||||||
objs_lines.append(stripped)
|
line_clean = re.sub(r"\$\{[^}]+\}", "", line_clean)
|
||||||
|
raw_entries.append(line_clean)
|
||||||
|
|
||||||
|
if not stripped.endswith("\\"):
|
||||||
collecting = False
|
collecting = False
|
||||||
|
|
||||||
objs_str = ' '.join(objs_lines)
|
for entry in raw_entries:
|
||||||
|
for token in entry.split():
|
||||||
|
token = token.strip('"')
|
||||||
|
if not token:
|
||||||
|
continue
|
||||||
|
|
||||||
objs_str = re.sub(r"\$\([^)]+\)", "", objs_str)
|
token = token.replace("\\", "/")
|
||||||
|
|
||||||
objs = []
|
if token.endswith(".obj"):
|
||||||
for part in objs_str.split():
|
token = re.sub(r"\.obj$", ".c", token)
|
||||||
part = part.strip()
|
elif token.endswith(".o"):
|
||||||
if part.startswith('"') and part.endswith('"'):
|
token = re.sub(r"\.o$", ".c", token)
|
||||||
part = part[1:-1]
|
|
||||||
if part:
|
|
||||||
objs.append(part)
|
|
||||||
|
|
||||||
c_files = []
|
if token.endswith(".c"):
|
||||||
include_dirs = set()
|
abs_path = os.path.normpath(os.path.join(project_root, token))
|
||||||
|
if os.path.isfile(abs_path):
|
||||||
|
if not any(x in abs_path for x in ["DebugTools", "v120", "v100"]):
|
||||||
|
c_files.append(abs_path)
|
||||||
|
include_dirs.add(os.path.dirname(abs_path))
|
||||||
|
|
||||||
for obj_path in objs:
|
if not c_files:
|
||||||
if "DebugTools" in obj_path:
|
makefile_dir = os.path.dirname(os.path.abspath(makefile_path))
|
||||||
continue
|
objects_list_path = os.path.join(makefile_dir, "objects.list")
|
||||||
if "v120" in obj_path:
|
c_from_objects, inc_from_objects = parse_objects_list(objects_list_path, project_root)
|
||||||
continue
|
c_files.extend(c_from_objects)
|
||||||
if "v100" in obj_path:
|
include_dirs.update(inc_from_objects)
|
||||||
continue
|
|
||||||
|
|
||||||
if obj_path.startswith("Debug\\") or obj_path.startswith("Debug/"):
|
for line in lines:
|
||||||
rel_path = obj_path.replace("Debug\\", "Src\\").replace("Debug/", "Src/")
|
if "-I" in line or "C_INCLUDES" in line:
|
||||||
else:
|
matches = re.findall(r"-I\s*([^\s\\]+)", line)
|
||||||
rel_path = obj_path
|
for match in matches:
|
||||||
|
match = match.strip('"').replace("\\", "/")
|
||||||
|
abs_include = os.path.normpath(os.path.join(project_root, match))
|
||||||
|
if os.path.isdir(abs_include):
|
||||||
|
include_dirs.add(abs_include)
|
||||||
|
|
||||||
abs_path = os.path.normpath(os.path.join(project_root, rel_path))
|
# Добавляем пути с заменой 'Src' на 'Inc', если путь заканчивается на 'Src'
|
||||||
|
additional_includes = set()
|
||||||
root, ext = os.path.splitext(abs_path)
|
for inc in include_dirs:
|
||||||
if ext.lower() == ".obj":
|
if inc.endswith(os.sep + "Src") or inc.endswith("/Src"):
|
||||||
c_path = root + ".c"
|
inc_inc = inc[:-3] + "Inc" # заменяем 'Src' на 'Inc'
|
||||||
else:
|
if os.path.isdir(inc_inc):
|
||||||
c_path = abs_path
|
additional_includes.add(inc_inc)
|
||||||
|
|
||||||
# Проверяем существование файла, если нет — пропускаем
|
|
||||||
if not os.path.isfile(c_path):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Сохраняем только .c файлы
|
|
||||||
if c_path.lower().endswith(".c"):
|
|
||||||
c_files.append(c_path)
|
|
||||||
dir_path = os.path.dirname(c_path)
|
|
||||||
if dir_path and "DebugTools" not in dir_path:
|
|
||||||
include_dirs.add(dir_path)
|
|
||||||
|
|
||||||
|
include_dirs.update(additional_includes)
|
||||||
|
|
||||||
h_files = find_all_includes_recursive(c_files, include_dirs)
|
h_files = find_all_includes_recursive(c_files, include_dirs)
|
||||||
|
|
||||||
|
return sorted(c_files), sorted(h_files), sorted(include_dirs), sorted(defines)
|
||||||
|
|
||||||
return sorted(c_files), sorted(h_files), sorted(include_dirs)
|
|
||||||
|
def parse_project(project_file_path, project_root=None):
|
||||||
|
"""
|
||||||
|
Выбирает парсер в зависимости от расширения project_file_path:
|
||||||
|
- для *.uvprojx и *.uvproj вызывается парсер Keil
|
||||||
|
- для остальных - parse_makefile
|
||||||
|
|
||||||
|
project_root нужен для parse_makefile, если не передан - берется из project_file_path
|
||||||
|
"""
|
||||||
|
ext = os.path.splitext(project_file_path)[1].lower()
|
||||||
|
|
||||||
|
if ext in ['.uvprojx', '.uvproj']:
|
||||||
|
# Парсим Keil проект
|
||||||
|
return parse_uvprojx(project_file_path)
|
||||||
|
else:
|
||||||
|
# Парсим makefile
|
||||||
|
if project_root is None:
|
||||||
|
project_root = os.path.dirname(os.path.abspath(project_file_path))
|
||||||
|
return parse_makefile(project_file_path, project_root)
|
||||||
@@ -11,7 +11,7 @@ from clang import cindex
|
|||||||
from clang.cindex import Config
|
from clang.cindex import Config
|
||||||
import lxml.etree as ET
|
import lxml.etree as ET
|
||||||
from xml.dom import minidom
|
from xml.dom import minidom
|
||||||
from makefile_parser import parse_makefile
|
from makefile_parser import parse_project
|
||||||
from collections import deque
|
from collections import deque
|
||||||
import argparse
|
import argparse
|
||||||
import myXML
|
import myXML
|
||||||
@@ -118,11 +118,11 @@ def get_canonical_typedef_file(var_type, include_dirs):
|
|||||||
break
|
break
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def analyze_variables_across_files(c_files, h_files, include_dirs):
|
def analyze_variables_across_files(c_files, h_files, include_dirs, global_defs):
|
||||||
optional_printf(PRINT_STATUS, "Starting analysis of variables across files...")
|
optional_printf(PRINT_STATUS, "Starting analysis of variables across files...")
|
||||||
index = clang.cindex.Index.create()
|
index = clang.cindex.Index.create()
|
||||||
args = [f"-I{inc}" for inc in include_dirs]
|
define_args = [f"-D{d}" for d in global_defs]
|
||||||
|
args = [f"-I{inc}" for inc in include_dirs] + define_args
|
||||||
unique_vars = {} # имя переменной → словарь с инфой
|
unique_vars = {} # имя переменной → словарь с инфой
|
||||||
h_files_needed = set()
|
h_files_needed = set()
|
||||||
vars_need_extern = {} # имя переменной → словарь без поля 'extern'
|
vars_need_extern = {} # имя переменной → словарь без поля 'extern'
|
||||||
@@ -154,6 +154,7 @@ def analyze_variables_across_files(c_files, h_files, include_dirs):
|
|||||||
# Проверяем, начинается ли имя с "_" и содержит заглавные буквы или служебные символы
|
# Проверяем, начинается ли имя с "_" и содержит заглавные буквы или служебные символы
|
||||||
return bool(re.match(r"^_[_A-Z]", var_name))
|
return bool(re.match(r"^_[_A-Z]", var_name))
|
||||||
|
|
||||||
|
|
||||||
if node.kind == clang.cindex.CursorKind.VAR_DECL:
|
if node.kind == clang.cindex.CursorKind.VAR_DECL:
|
||||||
if node.semantic_parent.kind == clang.cindex.CursorKind.TRANSLATION_UNIT:
|
if node.semantic_parent.kind == clang.cindex.CursorKind.TRANSLATION_UNIT:
|
||||||
is_extern = (node.storage_class == clang.cindex.StorageClass.EXTERN)
|
is_extern = (node.storage_class == clang.cindex.StorageClass.EXTERN)
|
||||||
@@ -170,7 +171,12 @@ def analyze_variables_across_files(c_files, h_files, include_dirs):
|
|||||||
return # игнорируем только явно известные служебные переменные
|
return # игнорируем только явно известные служебные переменные
|
||||||
if node.spelling == 'HUGE': # еще одна служеюная, которую хз как выделять
|
if node.spelling == 'HUGE': # еще одна служеюная, которую хз как выделять
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if 'Drivers' in node.location.file.name:
|
||||||
|
return
|
||||||
|
|
||||||
|
if 'uint' in node.spelling:
|
||||||
|
a = 1
|
||||||
# Проверяем, является ли тип указателем на функцию
|
# Проверяем, является ли тип указателем на функцию
|
||||||
# Признак: в типе есть '(' и ')' и '*', например: "void (*)(int)"
|
# Признак: в типе есть '(' и ')' и '*', например: "void (*)(int)"
|
||||||
if "(" in var_type and "*" in var_type and ")" in var_type:
|
if "(" in var_type and "*" in var_type and ")" in var_type:
|
||||||
@@ -295,6 +301,8 @@ def strip_ptr_and_array(typename):
|
|||||||
|
|
||||||
return typename
|
return typename
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def analyze_typedefs_and_struct(typedefs, structs):
|
def analyze_typedefs_and_struct(typedefs, structs):
|
||||||
optional_printf(PRINT_STATUS, "Resolving typedefs and expanding struct field types...")
|
optional_printf(PRINT_STATUS, "Resolving typedefs and expanding struct field types...")
|
||||||
|
|
||||||
@@ -422,10 +430,28 @@ def contains_anywhere_in_node(node, target: str) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def analyze_typedefs_and_structs_across_files(c_files, include_dirs):
|
|
||||||
|
def try_guess_std_include():
|
||||||
|
# Популярные места, где может лежать stdint.h
|
||||||
|
guesses = [
|
||||||
|
r"C:\Keil_v5\ARM\ARMCLANG\include",
|
||||||
|
r"C:\Program Files (x86)\GNU Arm Embedded Toolchain",
|
||||||
|
r"C:\Program Files (x86)\Arm GNU Toolchain"
|
||||||
|
]
|
||||||
|
|
||||||
|
found = []
|
||||||
|
for base in guesses:
|
||||||
|
for root, dirs, files in os.walk(base):
|
||||||
|
if "stdint.h" in files:
|
||||||
|
found.append(root)
|
||||||
|
return found
|
||||||
|
|
||||||
|
def analyze_typedefs_and_structs_across_files(c_files, include_dirs, global_defs):
|
||||||
optional_printf(PRINT_STATUS, "Starting analysis of typedefs and structs across files...")
|
optional_printf(PRINT_STATUS, "Starting analysis of typedefs and structs across files...")
|
||||||
index = clang.cindex.Index.create()
|
index = clang.cindex.Index.create()
|
||||||
args = [f"-I{inc}" for inc in include_dirs]
|
define_args = [f"-D{d}" for d in global_defs]
|
||||||
|
extra_std_include_dirs = try_guess_std_include()
|
||||||
|
args = [f"-I{inc}" for inc in include_dirs] + extra_std_include_dirs + define_args
|
||||||
|
|
||||||
unique_typedefs_raw = {}
|
unique_typedefs_raw = {}
|
||||||
unique_structs_raw = {}
|
unique_structs_raw = {}
|
||||||
@@ -452,7 +478,6 @@ def analyze_typedefs_and_structs_across_files(c_files, include_dirs):
|
|||||||
|
|
||||||
raw_name = node.spelling
|
raw_name = node.spelling
|
||||||
normalized_name = normalize_type_name(raw_name)
|
normalized_name = normalize_type_name(raw_name)
|
||||||
|
|
||||||
# struct_name всегда с префиксом
|
# struct_name всегда с префиксом
|
||||||
if node.spelling and "unnamed" not in normalized_name:
|
if node.spelling and "unnamed" not in normalized_name:
|
||||||
struct_name = f"{prefix}{normalized_name}"
|
struct_name = f"{prefix}{normalized_name}"
|
||||||
@@ -855,10 +880,10 @@ Usage example:
|
|||||||
print(f"Error: Makefile path '{makefile_path}' does not exist.")
|
print(f"Error: Makefile path '{makefile_path}' does not exist.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
c_files, h_files, include_dirs = parse_makefile(makefile_path, proj_path)
|
c_files, h_files, include_dirs, global_defs = parse_project(makefile_path, proj_path)
|
||||||
|
|
||||||
vars, includes, externs = analyze_variables_across_files(c_files, h_files, include_dirs)
|
vars, includes, externs = analyze_variables_across_files(c_files, h_files, include_dirs, global_defs)
|
||||||
typedefs, structs = analyze_typedefs_and_structs_across_files(c_files, include_dirs)
|
typedefs, structs = analyze_typedefs_and_structs_across_files(c_files, include_dirs, global_defs)
|
||||||
|
|
||||||
vars = dict(sorted(vars.items()))
|
vars = dict(sorted(vars.items()))
|
||||||
includes = get_sorted_headers(c_files, includes, include_dirs)
|
includes = get_sorted_headers(c_files, includes, include_dirs)
|
||||||
@@ -898,10 +923,10 @@ def run_scan(proj_path, makefile_path, output_xml, verbose=2):
|
|||||||
if not os.path.isfile(makefile_path):
|
if not os.path.isfile(makefile_path):
|
||||||
raise FileNotFoundError(f"Makefile path '{makefile_path}' does not exist.")
|
raise FileNotFoundError(f"Makefile path '{makefile_path}' does not exist.")
|
||||||
|
|
||||||
c_files, h_files, include_dirs = parse_makefile(makefile_path, proj_path)
|
c_files, h_files, include_dirs, global_defs = parse_project(makefile_path, proj_path)
|
||||||
|
|
||||||
vars, includes, externs = analyze_variables_across_files(c_files, h_files, include_dirs)
|
vars, includes, externs = analyze_variables_across_files(c_files, h_files, include_dirs, global_defs)
|
||||||
typedefs, structs = analyze_typedefs_and_structs_across_files(c_files, include_dirs)
|
typedefs, structs = analyze_typedefs_and_structs_across_files(c_files, include_dirs, global_defs)
|
||||||
|
|
||||||
vars = dict(sorted(vars.items()))
|
vars = dict(sorted(vars.items()))
|
||||||
includes = get_sorted_headers(c_files, includes, include_dirs)
|
includes = get_sorted_headers(c_files, includes, include_dirs)
|
||||||
|
|||||||
621
Src/tms_debugvar_term.py
Normal file
621
Src/tms_debugvar_term.py
Normal file
@@ -0,0 +1,621 @@
|
|||||||
|
|
||||||
|
import sys
|
||||||
|
import struct
|
||||||
|
import datetime
|
||||||
|
from PySide2 import QtCore, QtWidgets, QtSerialPort
|
||||||
|
from PySide2.QtCore import QTimer
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- CRC util ---
|
||||||
|
def crc16_ibm(data: bytes, *, init=0xFFFF) -> int:
|
||||||
|
"""CRC16-IBM (aka CRC-16/ANSI, polynomial 0xA001 reflected)."""
|
||||||
|
crc = init
|
||||||
|
for b in data:
|
||||||
|
crc ^= b
|
||||||
|
for _ in range(8):
|
||||||
|
if crc & 1:
|
||||||
|
crc = (crc >> 1) ^ 0xA001
|
||||||
|
else:
|
||||||
|
crc >>= 1
|
||||||
|
return crc & 0xFFFF
|
||||||
|
|
||||||
|
class Spoiler(QtWidgets.QWidget):
|
||||||
|
def __init__(self, title="", animationDuration=300, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self._animationDuration = animationDuration
|
||||||
|
|
||||||
|
# --- Toggle button ---
|
||||||
|
self.toggleButton = QtWidgets.QToolButton(self)
|
||||||
|
self.toggleButton.setStyleSheet("QToolButton { border: none; }")
|
||||||
|
self.toggleButton.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
|
||||||
|
self.toggleButton.setArrowType(QtCore.Qt.RightArrow)
|
||||||
|
self.toggleButton.setText(title)
|
||||||
|
self.toggleButton.setCheckable(True)
|
||||||
|
|
||||||
|
# --- Header line ---
|
||||||
|
self.headerLine = QtWidgets.QFrame(self)
|
||||||
|
self.headerLine.setFrameShape(QtWidgets.QFrame.HLine)
|
||||||
|
self.headerLine.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||||
|
|
||||||
|
# --- Content area ---
|
||||||
|
self.contentArea = QtWidgets.QScrollArea(self)
|
||||||
|
self.contentArea.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
||||||
|
self.contentArea.setFrameShape(QtWidgets.QFrame.NoFrame)
|
||||||
|
self.contentArea.setWidgetResizable(True)
|
||||||
|
self._contentWidget = QtWidgets.QWidget()
|
||||||
|
self.contentArea.setWidget(self._contentWidget)
|
||||||
|
self.contentArea.setMaximumHeight(0)
|
||||||
|
|
||||||
|
# --- Анимация только по контенту ---
|
||||||
|
self._ani_content = QtCore.QPropertyAnimation(self.contentArea, b"maximumHeight")
|
||||||
|
self._ani_content.setDuration(animationDuration)
|
||||||
|
self._ani_content.setEasingCurve(QtCore.QEasingCurve.InOutCubic)
|
||||||
|
|
||||||
|
# Следим за шагами анимации → обновляем родителя
|
||||||
|
self._ani_content.valueChanged.connect(self._adjust_parent_size)
|
||||||
|
|
||||||
|
# --- Layout ---
|
||||||
|
self.mainLayout = QtWidgets.QGridLayout(self)
|
||||||
|
self.mainLayout.setVerticalSpacing(0)
|
||||||
|
self.mainLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.mainLayout.addWidget(self.toggleButton, 0, 0, 1, 1)
|
||||||
|
self.mainLayout.addWidget(self.headerLine, 0, 1, 1, 1)
|
||||||
|
self.mainLayout.addWidget(self.contentArea, 1, 0, 1, 2)
|
||||||
|
|
||||||
|
# --- Signals ---
|
||||||
|
self.toggleButton.clicked.connect(self._on_toggled)
|
||||||
|
|
||||||
|
def setContentLayout(self, contentLayout):
|
||||||
|
old = self._contentWidget.layout()
|
||||||
|
if old:
|
||||||
|
QtWidgets.QWidget().setLayout(old)
|
||||||
|
self._contentWidget.setLayout(contentLayout)
|
||||||
|
|
||||||
|
def _adjust_parent_size(self, *_):
|
||||||
|
top = self.window()
|
||||||
|
if top:
|
||||||
|
size = top.size()
|
||||||
|
size.setHeight(top.sizeHint().height()) # берём новую высоту
|
||||||
|
top.resize(size) # ширина остаётся прежней
|
||||||
|
|
||||||
|
def _on_toggled(self, checked: bool):
|
||||||
|
self.toggleButton.setArrowType(QtCore.Qt.DownArrow if checked else QtCore.Qt.RightArrow)
|
||||||
|
|
||||||
|
contentHeight = self._contentWidget.sizeHint().height()
|
||||||
|
self._ani_content.stop()
|
||||||
|
self._ani_content.setStartValue(self.contentArea.maximumHeight())
|
||||||
|
self._ani_content.setEndValue(contentHeight if checked else 0)
|
||||||
|
|
||||||
|
# --- Фиксируем ширину на время анимации ---
|
||||||
|
w = self.width()
|
||||||
|
self.setFixedWidth(w)
|
||||||
|
self._ani_content.finished.connect(lambda: self.setMaximumWidth(16777215)) # сброс фикса
|
||||||
|
|
||||||
|
self._ani_content.start()
|
||||||
|
|
||||||
|
class DebugTerminalWidget(QtWidgets.QWidget):
|
||||||
|
nameRead = QtCore.Signal(int, int, int, str) # index, status, iq, name
|
||||||
|
valueRead = QtCore.Signal(int, int, int, int, float) # index, status, iq, raw16, floatVal
|
||||||
|
portOpened = QtCore.Signal(str)
|
||||||
|
portClosed = QtCore.Signal(str)
|
||||||
|
txBytes = QtCore.Signal(bytes) # raw bytes sent
|
||||||
|
rxBytes = QtCore.Signal(bytes) # raw bytes received (frame only)
|
||||||
|
|
||||||
|
def __init__(self, parent=None, *,
|
||||||
|
start_byte=0x0A,
|
||||||
|
cmd_byte=0x44,
|
||||||
|
name_field_len=11,
|
||||||
|
signed=True,
|
||||||
|
iq_scaling=None,
|
||||||
|
read_timeout_ms=200,
|
||||||
|
auto_crc_check=True,
|
||||||
|
drop_if_busy=False,
|
||||||
|
replace_if_busy=True):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.start_byte = start_byte
|
||||||
|
self.cmd_byte = cmd_byte
|
||||||
|
self.name_field_len = name_field_len
|
||||||
|
self.signed = signed
|
||||||
|
self.read_timeout_ms = read_timeout_ms
|
||||||
|
self.auto_crc_check = auto_crc_check
|
||||||
|
|
||||||
|
# lockstep policy flags
|
||||||
|
self._drop_if_busy = drop_if_busy
|
||||||
|
self._replace_if_busy = replace_if_busy
|
||||||
|
|
||||||
|
if iq_scaling is None:
|
||||||
|
iq_scaling = {n: float(1 << n) for n in range(16)}
|
||||||
|
iq_scaling[0] = 1.0
|
||||||
|
self.iq_scaling = iq_scaling
|
||||||
|
|
||||||
|
# Serial port ---------------------------------------------------------
|
||||||
|
self.serial = QtSerialPort.QSerialPort(self)
|
||||||
|
self.serial.setBaudRate(115200)
|
||||||
|
self.serial.readyRead.connect(self._on_ready_read)
|
||||||
|
self.serial.errorOccurred.connect(self._on_serial_error)
|
||||||
|
self._index_change_timer = QtCore.QTimer(self)
|
||||||
|
self._index_change_timer.setSingleShot(True)
|
||||||
|
self._index_change_timer.timeout.connect(self._on_index_change_timeout)
|
||||||
|
self._index_change_delay_ms = 200 # задержка перед отправкой запроса
|
||||||
|
|
||||||
|
# RX state ------------------------------------------------------------
|
||||||
|
self._rx_buf = bytearray()
|
||||||
|
self._waiting_name = False
|
||||||
|
self._expected_min_len = 0
|
||||||
|
self._expected_exact_len = None # if known exactly
|
||||||
|
|
||||||
|
# Lockstep tx/rx state ------------------------------------------------
|
||||||
|
self._busy = False # True => запрос отправлен, ждём ответ/таймаут
|
||||||
|
self._pending_cmd = None # (frame, is_name, index) ожидающий отправки
|
||||||
|
self._active_index = None # индекс текущего запроса (для сигналов)
|
||||||
|
|
||||||
|
# Timer for per-transaction timeout ----------------------------------
|
||||||
|
self._txn_timer = QtCore.QTimer(self)
|
||||||
|
self._txn_timer.setSingleShot(True)
|
||||||
|
self._txn_timer.timeout.connect(self._on_txn_timeout)
|
||||||
|
|
||||||
|
# Polling timer -------------------------------------------------------
|
||||||
|
self._poll_timer = QtCore.QTimer(self)
|
||||||
|
self._poll_timer.timeout.connect(self._on_poll_timeout)
|
||||||
|
self._polling = False
|
||||||
|
|
||||||
|
self._build_ui()
|
||||||
|
|
||||||
|
self.btn_open.clicked.connect(self._open_close_port)
|
||||||
|
self.btn_refresh.clicked.connect(self.set_available_ports)
|
||||||
|
self.btn_read_name.clicked.connect(self.request_name)
|
||||||
|
self.btn_read_value.clicked.connect(self.request_value)
|
||||||
|
self.btn_poll.clicked.connect(self._toggle_polling)
|
||||||
|
|
||||||
|
self.set_available_ports()
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------ UI ---
|
||||||
|
def _build_ui(self):
|
||||||
|
main_layout = QtWidgets.QVBoxLayout(self)
|
||||||
|
main_layout.setContentsMargins(10, 10, 10, 10)
|
||||||
|
main_layout.setSpacing(12)
|
||||||
|
|
||||||
|
# --- Serial Port Group ---
|
||||||
|
port_group = QtWidgets.QGroupBox("Serial Port")
|
||||||
|
port_layout = QtWidgets.QHBoxLayout(port_group)
|
||||||
|
|
||||||
|
self.cmb_port = QtWidgets.QComboBox()
|
||||||
|
self.btn_refresh = QtWidgets.QPushButton("Refresh")
|
||||||
|
self.cmb_baud = QtWidgets.QComboBox()
|
||||||
|
self.cmb_baud.addItems(["9600", "19200", "38400", "57600", "115200", "230400"])
|
||||||
|
self.cmb_baud.setCurrentText("115200")
|
||||||
|
self.btn_open = QtWidgets.QPushButton("Open")
|
||||||
|
|
||||||
|
port_layout.addWidget(QtWidgets.QLabel("Port:"))
|
||||||
|
port_layout.addWidget(self.cmb_port, 1)
|
||||||
|
port_layout.addWidget(self.btn_refresh)
|
||||||
|
port_layout.addSpacing(20)
|
||||||
|
port_layout.addWidget(QtWidgets.QLabel("Baud rate:"))
|
||||||
|
port_layout.addWidget(self.cmb_baud, 0)
|
||||||
|
port_layout.addWidget(self.btn_open)
|
||||||
|
|
||||||
|
main_layout.addWidget(port_group)
|
||||||
|
|
||||||
|
# --- Variable Control Group ---
|
||||||
|
var_group = QtWidgets.QGroupBox("Watch Variable")
|
||||||
|
var_layout = QtWidgets.QGridLayout(var_group)
|
||||||
|
var_layout.setHorizontalSpacing(10)
|
||||||
|
var_layout.setVerticalSpacing(6)
|
||||||
|
|
||||||
|
self.spin_index = QtWidgets.QSpinBox()
|
||||||
|
self.spin_index.setRange(0, 0x7FFF)
|
||||||
|
self.spin_index.setAccelerated(True)
|
||||||
|
self.spin_index.valueChanged.connect(self._on_index_changed)
|
||||||
|
|
||||||
|
self.chk_hex_index = QtWidgets.QCheckBox("Hex")
|
||||||
|
self.chk_hex_index.stateChanged.connect(self._toggle_index_base)
|
||||||
|
|
||||||
|
self.btn_read_name = QtWidgets.QPushButton("Read Name")
|
||||||
|
self.btn_read_value = QtWidgets.QPushButton("Read Value")
|
||||||
|
self.btn_poll = QtWidgets.QPushButton("Start Polling")
|
||||||
|
|
||||||
|
self.spin_interval = QtWidgets.QSpinBox()
|
||||||
|
self.spin_interval.setRange(100, 5000)
|
||||||
|
self.spin_interval.setValue(500)
|
||||||
|
self.spin_interval.setSuffix(" ms")
|
||||||
|
|
||||||
|
self.edit_name = QtWidgets.QLineEdit()
|
||||||
|
self.edit_name.setReadOnly(True)
|
||||||
|
|
||||||
|
self.edit_value = QtWidgets.QLineEdit()
|
||||||
|
self.edit_value.setReadOnly(True)
|
||||||
|
|
||||||
|
self.lbl_iq = QtWidgets.QLabel("-")
|
||||||
|
self.chk_raw = QtWidgets.QCheckBox("Raw (no IQ format)")
|
||||||
|
|
||||||
|
var_layout.addWidget(QtWidgets.QLabel("Index:"), 0, 0)
|
||||||
|
var_layout.addWidget(self.spin_index, 0, 1)
|
||||||
|
var_layout.addWidget(self.chk_hex_index, 0, 2)
|
||||||
|
|
||||||
|
var_layout.addWidget(self.btn_read_name, 1, 0)
|
||||||
|
var_layout.addWidget(self.btn_read_value, 1, 1)
|
||||||
|
var_layout.addWidget(self.btn_poll, 1, 2)
|
||||||
|
|
||||||
|
var_layout.addWidget(QtWidgets.QLabel("Interval:"), 2, 0)
|
||||||
|
var_layout.addWidget(self.spin_interval, 2, 1)
|
||||||
|
|
||||||
|
var_layout.addWidget(QtWidgets.QLabel("Name:"), 3, 0)
|
||||||
|
var_layout.addWidget(self.edit_name, 3, 1, 1, 2)
|
||||||
|
|
||||||
|
var_layout.addWidget(QtWidgets.QLabel("Value:"), 4, 0)
|
||||||
|
var_layout.addWidget(self.edit_value, 4, 1, 1, 2)
|
||||||
|
|
||||||
|
var_layout.addWidget(QtWidgets.QLabel("IQ:"), 5, 0)
|
||||||
|
var_layout.addWidget(self.lbl_iq, 5, 1)
|
||||||
|
var_layout.addWidget(self.chk_raw, 5, 2)
|
||||||
|
|
||||||
|
main_layout.addWidget(var_group)
|
||||||
|
|
||||||
|
# --- Collapsible UART Log ---
|
||||||
|
self.log_spoiler = Spoiler("UART Log", animationDuration=300, parent=self)
|
||||||
|
|
||||||
|
log_layout = QtWidgets.QVBoxLayout()
|
||||||
|
self.txt_log = QtWidgets.QTextEdit()
|
||||||
|
self.txt_log.setReadOnly(True)
|
||||||
|
self.txt_log.setFontFamily("Courier")
|
||||||
|
log_layout.addWidget(self.txt_log)
|
||||||
|
|
||||||
|
self.log_spoiler.setContentLayout(log_layout)
|
||||||
|
main_layout.addWidget(self.log_spoiler, 1)
|
||||||
|
|
||||||
|
|
||||||
|
def _toggle_log_panel(self, checked):
|
||||||
|
if checked:
|
||||||
|
self.toggle_log_btn.setArrowType(QtCore.Qt.DownArrow)
|
||||||
|
self.log_panel.show()
|
||||||
|
else:
|
||||||
|
self.toggle_log_btn.setArrowType(QtCore.Qt.RightArrow)
|
||||||
|
self.log_panel.hide()
|
||||||
|
# ----------------------------------------------------------- Port mgmt ---
|
||||||
|
def set_available_ports(self):
|
||||||
|
"""Enumerate COM ports and repopulate combo box."""
|
||||||
|
current = self.cmb_port.currentText()
|
||||||
|
self.cmb_port.blockSignals(True)
|
||||||
|
self.cmb_port.clear()
|
||||||
|
for info in QtSerialPort.QSerialPortInfo.availablePorts():
|
||||||
|
self.cmb_port.addItem(info.portName())
|
||||||
|
if current:
|
||||||
|
ix = self.cmb_port.findText(current)
|
||||||
|
if ix >= 0:
|
||||||
|
self.cmb_port.setCurrentIndex(ix)
|
||||||
|
self.cmb_port.blockSignals(False)
|
||||||
|
|
||||||
|
def _open_close_port(self):
|
||||||
|
if self.serial.isOpen():
|
||||||
|
name = self.serial.portName()
|
||||||
|
self.serial.close()
|
||||||
|
self.btn_open.setText("Open")
|
||||||
|
self._log(f"[PORT] Closed {name}")
|
||||||
|
self.portClosed.emit(name)
|
||||||
|
return
|
||||||
|
|
||||||
|
port_name = self.cmb_port.currentText()
|
||||||
|
if not port_name:
|
||||||
|
self._log("[ERR] No port selected")
|
||||||
|
return
|
||||||
|
|
||||||
|
baud = int(self.cmb_baud.currentText())
|
||||||
|
self.serial.setPortName(port_name)
|
||||||
|
self.serial.setBaudRate(baud)
|
||||||
|
if not self.serial.open(QtCore.QIODevice.ReadWrite):
|
||||||
|
self._log(f"[ERR] Failed to open {port_name}: {self.serial.errorString()}")
|
||||||
|
return
|
||||||
|
self.btn_open.setText("Close")
|
||||||
|
self._log(f"[PORT] Opened {port_name} @ {baud}")
|
||||||
|
self.portOpened.emit(port_name)
|
||||||
|
|
||||||
|
# --------------------------------------------------------- Frame build ---
|
||||||
|
def _build_request(self, index: int, read_name: bool) -> bytes:
|
||||||
|
if read_name:
|
||||||
|
dbg = 0x8000 | (index & 0x7FFF)
|
||||||
|
else:
|
||||||
|
dbg = index & 0x7FFF
|
||||||
|
hi = (dbg >> 8) & 0xFF
|
||||||
|
lo = dbg & 0xFF
|
||||||
|
return bytes([self.start_byte & 0xFF, self.cmd_byte & 0xFF, hi, lo])
|
||||||
|
|
||||||
|
# ------------------------------- PUBLIC API (safe single outstanding) ---
|
||||||
|
def request_name(self):
|
||||||
|
self._issue_command(is_name=True)
|
||||||
|
|
||||||
|
def request_value(self):
|
||||||
|
self._issue_command(is_name=False)
|
||||||
|
|
||||||
|
def _issue_command(self, *, is_name: bool):
|
||||||
|
index = int(self.spin_index.value())
|
||||||
|
frame = self._build_request(index, is_name)
|
||||||
|
|
||||||
|
if self._busy:
|
||||||
|
if self._drop_if_busy:
|
||||||
|
self._log("[LOCKSTEP] Busy -> drop new request")
|
||||||
|
return
|
||||||
|
if self._replace_if_busy:
|
||||||
|
self._pending_cmd = (frame, is_name, index)
|
||||||
|
self._log("[LOCKSTEP] Busy -> replaced pending request")
|
||||||
|
else:
|
||||||
|
# queue disabled; ignore
|
||||||
|
self._log("[LOCKSTEP] Busy -> ignore (no replace)")
|
||||||
|
return
|
||||||
|
|
||||||
|
# idle -> send immediately
|
||||||
|
self._start_transaction(frame, is_name, index)
|
||||||
|
|
||||||
|
# ------------------------------------------------------ TXN lifecycle ---
|
||||||
|
def _start_transaction(self, frame: bytes, is_name: bool, index: int):
|
||||||
|
"""Mark busy, compute expected length, send frame, start timeout."""
|
||||||
|
self._busy = True
|
||||||
|
self._active_index = index
|
||||||
|
self._waiting_name = is_name
|
||||||
|
# Expected minimal len: hdr[4] + payload(name/val) + crc/trailer[4]
|
||||||
|
if is_name:
|
||||||
|
self._expected_min_len = 4 + self.name_field_len + 4
|
||||||
|
else:
|
||||||
|
self._expected_min_len = 4 + 2 + 4
|
||||||
|
self._expected_exact_len = self._expected_min_len # protocol fixed-size now
|
||||||
|
self._rx_buf.clear()
|
||||||
|
self._set_ui_busy(True)
|
||||||
|
self._send(frame)
|
||||||
|
self._txn_timer.start(self.read_timeout_ms)
|
||||||
|
|
||||||
|
def _end_transaction(self):
|
||||||
|
"""Common exit path after parse or timeout."""
|
||||||
|
self._txn_timer.stop()
|
||||||
|
self._busy = False
|
||||||
|
self._active_index = None
|
||||||
|
self._expected_min_len = 0
|
||||||
|
self._expected_exact_len = None
|
||||||
|
self._rx_buf.clear()
|
||||||
|
self._set_ui_busy(False)
|
||||||
|
# if we have pending -> fire it now
|
||||||
|
if self._pending_cmd is not None:
|
||||||
|
frame, is_name, index = self._pending_cmd
|
||||||
|
self._pending_cmd = None
|
||||||
|
# start immediately (no recursion issues; single-shot via singleShot)
|
||||||
|
QtCore.QTimer.singleShot(0, lambda f=frame, n=is_name, i=index: self._start_transaction(f, n, i))
|
||||||
|
|
||||||
|
def _on_txn_timeout(self):
|
||||||
|
if not self._busy:
|
||||||
|
return
|
||||||
|
self._log("[TIMEOUT] Response not received in time; aborting transaction")
|
||||||
|
# log any garbage that came in
|
||||||
|
if self._rx_buf:
|
||||||
|
self._log_frame(bytes(self._rx_buf), tx=False)
|
||||||
|
self._end_transaction()
|
||||||
|
|
||||||
|
# --------------------------------------------------------------- TX/RX ---
|
||||||
|
def _send(self, data: bytes):
|
||||||
|
n = self.serial.write(data)
|
||||||
|
if n != len(data):
|
||||||
|
self._log(f"[ERR] Write incomplete: {n}/{len(data)}")
|
||||||
|
self.txBytes.emit(data)
|
||||||
|
self._log_frame(data, tx=True)
|
||||||
|
|
||||||
|
def _on_ready_read(self):
|
||||||
|
if not self._busy:
|
||||||
|
# unexpected data while idle -> just log & drop
|
||||||
|
chunk = self.serial.readAll().data()
|
||||||
|
if chunk:
|
||||||
|
self._log("[WARN] RX while idle -> ignored")
|
||||||
|
self._log_frame(chunk, tx=False)
|
||||||
|
return
|
||||||
|
|
||||||
|
self._rx_buf.extend(self.serial.readAll().data())
|
||||||
|
|
||||||
|
# If exact length known and reached -> parse immediately (no wait for timeout)
|
||||||
|
if self._expected_exact_len is not None and len(self._rx_buf) >= self._expected_exact_len:
|
||||||
|
frame = bytes(self._rx_buf[:self._expected_exact_len])
|
||||||
|
# log rx; if extra bytes remain we'll keep them for next txn (unlikely)
|
||||||
|
self.rxBytes.emit(frame)
|
||||||
|
self._log_frame(frame, tx=False)
|
||||||
|
self._parse_response(frame)
|
||||||
|
# discard everything consumed
|
||||||
|
del self._rx_buf[:self._expected_exact_len]
|
||||||
|
if self._rx_buf:
|
||||||
|
self._log("[WARN] Extra RX bytes after frame -> stash for next txn")
|
||||||
|
self._end_transaction()
|
||||||
|
return
|
||||||
|
|
||||||
|
# If only min len known: check >= min -> try parse; else keep waiting (timer still running)
|
||||||
|
if self._expected_min_len and len(self._rx_buf) >= self._expected_min_len:
|
||||||
|
frame = bytes(self._rx_buf)
|
||||||
|
self.rxBytes.emit(frame)
|
||||||
|
self._log_frame(frame, tx=False)
|
||||||
|
self._parse_response(frame)
|
||||||
|
self._end_transaction()
|
||||||
|
return
|
||||||
|
# else: wait for more data or timeout
|
||||||
|
|
||||||
|
def _on_serial_error(self, err):
|
||||||
|
if err == QtSerialPort.QSerialPort.NoError:
|
||||||
|
return
|
||||||
|
self._log(f"[SERIAL ERR] {self.serial.errorString()} ({err})")
|
||||||
|
# treat as txn failure if busy
|
||||||
|
if self._busy:
|
||||||
|
self._end_transaction()
|
||||||
|
# ------------------------------------------------------------- Parsing ---
|
||||||
|
def _parse_response(self, frame: bytes):
|
||||||
|
# basic length check
|
||||||
|
if len(frame) < 8: # minimal structure
|
||||||
|
self._log("[ERR] Frame too short")
|
||||||
|
return
|
||||||
|
|
||||||
|
# trailer: crcLo crcHi 0 0
|
||||||
|
if len(frame) < 4:
|
||||||
|
return # can't parse yet
|
||||||
|
crc_lo = frame[-4]
|
||||||
|
crc_hi = frame[-3]
|
||||||
|
crc_rx = (crc_hi << 8) | crc_lo
|
||||||
|
z1 = frame[-2]
|
||||||
|
z2 = frame[-1]
|
||||||
|
if z1 != 0 or z2 != 0:
|
||||||
|
self._log("[WARN] Frame trailer not 0,0")
|
||||||
|
|
||||||
|
payload = frame[:-4]
|
||||||
|
if self.auto_crc_check:
|
||||||
|
crc_calc = crc16_ibm(payload)
|
||||||
|
if crc_calc != crc_rx:
|
||||||
|
self._log(f"[CRC FAIL] calc=0x{crc_calc:04X} rx=0x{crc_rx:04X}")
|
||||||
|
else:
|
||||||
|
self._log("[CRC OK]")
|
||||||
|
|
||||||
|
# header fields
|
||||||
|
addr = payload[0]
|
||||||
|
cmd = payload[1]
|
||||||
|
status = payload[2]
|
||||||
|
iq = payload[3]
|
||||||
|
|
||||||
|
if cmd != self.cmd_byte:
|
||||||
|
self._log(f"[WARN] Unexpected cmd 0x{cmd:02X}")
|
||||||
|
|
||||||
|
index = self._active_index if self._active_index is not None else self.spin_index.value()
|
||||||
|
|
||||||
|
if self._waiting_name:
|
||||||
|
name_bytes = payload[4:4 + self.name_field_len]
|
||||||
|
# stop at first NUL
|
||||||
|
nul = name_bytes.find(b"\x00")
|
||||||
|
if nul >= 0:
|
||||||
|
name_str = name_bytes[:nul].decode(errors="replace")
|
||||||
|
else:
|
||||||
|
name_str = name_bytes.decode(errors="replace")
|
||||||
|
self.edit_name.setText(name_str)
|
||||||
|
self.lbl_iq.setText(str(iq))
|
||||||
|
self.nameRead.emit(index, status, iq, name_str)
|
||||||
|
else:
|
||||||
|
# Чтение значения
|
||||||
|
if status != 0:
|
||||||
|
# Ошибка чтения переменной — считаем её недействительной
|
||||||
|
self.edit_value.setText("INVALID")
|
||||||
|
self.edit_value.setStyleSheet("color: red; font-weight: bold;")
|
||||||
|
self.lbl_iq.setText("-")
|
||||||
|
self.valueRead.emit(index, status, iq, 0, float('nan'))
|
||||||
|
self._log(f"[ERR] Variable at index {index} invalid, status={status}")
|
||||||
|
return
|
||||||
|
|
||||||
|
raw_lo = payload[4] if len(payload) > 4 else 0
|
||||||
|
raw_hi = payload[5] if len(payload) > 5 else 0
|
||||||
|
raw16 = (raw_hi << 8) | raw_lo
|
||||||
|
if self.signed and (raw16 & 0x8000):
|
||||||
|
raw_signed = raw16 - 0x10000
|
||||||
|
else:
|
||||||
|
raw_signed = raw16
|
||||||
|
if self.chk_raw.isChecked():
|
||||||
|
disp = str(raw_signed)
|
||||||
|
float_val = float(raw_signed)
|
||||||
|
else:
|
||||||
|
scale = self.iq_scaling.get(iq, 1.0)
|
||||||
|
float_val = raw_signed / scale
|
||||||
|
disp = f"{float_val:.6g}" # compact
|
||||||
|
self.edit_value.setText(disp)
|
||||||
|
self.lbl_iq.setText(str(iq))
|
||||||
|
self.valueRead.emit(index, status, iq, raw_signed, float_val)
|
||||||
|
|
||||||
|
# -------------------------------------------------------------- Helpers ---
|
||||||
|
|
||||||
|
def _toggle_index_base(self, state):
|
||||||
|
val = self.spin_index.value()
|
||||||
|
if state == QtCore.Qt.Checked:
|
||||||
|
self.spin_index.setDisplayIntegerBase(16)
|
||||||
|
self.spin_index.setPrefix("0x")
|
||||||
|
self.spin_index.setValue(val) # refresh display
|
||||||
|
else:
|
||||||
|
self.spin_index.setDisplayIntegerBase(10)
|
||||||
|
self.spin_index.setPrefix("")
|
||||||
|
self.spin_index.setValue(val)
|
||||||
|
|
||||||
|
def _on_index_changed(self, new_index: int):
|
||||||
|
if self._polling:
|
||||||
|
self._index_change_timer.start(self._index_change_delay_ms)
|
||||||
|
|
||||||
|
def _on_index_change_timeout(self):
|
||||||
|
# Здесь запускаем запрос имени или значения по новому индексу
|
||||||
|
if self._polling:
|
||||||
|
# если включён polling — можно просто перезапустить опрос с новым индексом
|
||||||
|
# например:
|
||||||
|
self._restart_polling_cycle()
|
||||||
|
|
||||||
|
def _restart_polling_cycle(self):
|
||||||
|
# Прервать текущую транзакцию (если есть)
|
||||||
|
if self._busy:
|
||||||
|
# Если занято — запустить таймер, который через немного проверит снова
|
||||||
|
# Можно, например, использовать QTimer.singleShot (если PyQt/PySide)
|
||||||
|
QTimer.singleShot(10, self._restart_polling_cycle) # через 100 мс повторить попытку
|
||||||
|
return
|
||||||
|
# можно отправить запрос имени, если нужно
|
||||||
|
self.request_name()
|
||||||
|
# Запустить следующий запрос
|
||||||
|
self._on_poll_timeout()
|
||||||
|
|
||||||
|
def _set_polling_ui(self, polling: bool):
|
||||||
|
# Если polling == True -> блокируем кнопки Read/Write, иначе разблокируем
|
||||||
|
self.btn_read_name.setDisabled(polling)
|
||||||
|
self.btn_read_value.setDisabled(polling)
|
||||||
|
|
||||||
|
def _toggle_polling(self):
|
||||||
|
if self._polling:
|
||||||
|
self._poll_timer.stop()
|
||||||
|
self._polling = False
|
||||||
|
self.btn_poll.setText("Start Polling")
|
||||||
|
self._set_polling_ui(False)
|
||||||
|
self._log("[POLL] Stopped")
|
||||||
|
else:
|
||||||
|
interval = self.spin_interval.value()
|
||||||
|
self._poll_timer.start(interval)
|
||||||
|
self._polling = True
|
||||||
|
self.btn_poll.setText("Stop Polling")
|
||||||
|
self._set_polling_ui(True)
|
||||||
|
self._log(f"[POLL] Started, interval {interval} ms")
|
||||||
|
|
||||||
|
def _on_poll_timeout(self):
|
||||||
|
self._poll_once()
|
||||||
|
|
||||||
|
def _poll_once(self):
|
||||||
|
if self._polling and self.serial.isOpen() and not self._busy:
|
||||||
|
self.request_value()
|
||||||
|
# если busy -> просто пропускаем тик; не ставим pending, чтобы не накапливать очередь
|
||||||
|
|
||||||
|
def _set_ui_busy(self, busy: bool):
|
||||||
|
'''self.btn_read_name.setEnabled(not busy)
|
||||||
|
self.btn_read_value.setEnabled(not busy)
|
||||||
|
# Не запрещаем Stop Polling, иначе нельзя прервать зависший запрос
|
||||||
|
self.spin_index.setEnabled(not busy)'''
|
||||||
|
|
||||||
|
def _log(self, msg: str):
|
||||||
|
ts = datetime.datetime.now().strftime("%H:%M:%S.%f")[:-3]
|
||||||
|
self.txt_log.append(f"{ts} {msg}")
|
||||||
|
|
||||||
|
def _log_frame(self, data: bytes, *, tx: bool):
|
||||||
|
dir_tag = "TX" if tx else "RX"
|
||||||
|
hex_bytes = ' '.join(f"{b:02X}" for b in data)
|
||||||
|
# ascii printable map
|
||||||
|
ascii_bytes = ''.join(chr(b) if 32 <= b < 127 else '.' for b in data)
|
||||||
|
self._log(f"[{dir_tag}] {hex_bytes} |{ascii_bytes}|")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------- Demo harness ---
|
||||||
|
class _DemoWindow(QtWidgets.QMainWindow):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.setWindowTitle("DebugVar Terminal")
|
||||||
|
self.term = DebugTerminalWidget(self)
|
||||||
|
self.setCentralWidget(self.term)
|
||||||
|
# connect sample signals -> print
|
||||||
|
self.term.nameRead.connect(self._on_name)
|
||||||
|
self.term.valueRead.connect(self._on_value)
|
||||||
|
|
||||||
|
def _on_name(self, index, status, iq, name):
|
||||||
|
print(f"Name idx={index} status={status} iq={iq} name='{name}'")
|
||||||
|
|
||||||
|
def _on_value(self, index, status, iq, raw16, floatVal):
|
||||||
|
print(f"Value idx={index} status={status} iq={iq} raw={raw16} val={floatVal}")
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------- main ---
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
win = _DemoWindow()
|
||||||
|
win.show()
|
||||||
|
sys.exit(app.exec_())
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
from PySide2.QtWidgets import (
|
from PySide2.QtWidgets import (
|
||||||
QTableWidget, QTableWidgetItem, QCheckBox, QComboBox, QLineEdit, QCompleter,
|
QTableWidget, QTableWidgetItem, QCheckBox, QComboBox, QLineEdit, QCompleter,
|
||||||
QAbstractItemView, QHeaderView, QLabel,
|
QAbstractItemView, QHeaderView, QLabel, QSpacerItem, QSizePolicy, QSpinBox,
|
||||||
QDialog, QVBoxLayout, QHBoxLayout, QPushButton, QScrollArea, QWidget
|
QDialog, QVBoxLayout, QHBoxLayout, QPushButton, QScrollArea, QWidget
|
||||||
)
|
)
|
||||||
from PySide2.QtGui import QColor, QBrush, QPalette
|
from PySide2.QtGui import QColor, QBrush, QPalette
|
||||||
from PySide2.QtCore import Qt
|
from PySide2.QtCore import Qt, QSettings
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
from generate_debug_vars import type_map
|
from generate_debug_vars import type_map
|
||||||
import time
|
import time
|
||||||
@@ -60,6 +60,57 @@ class FilterDialog(QDialog):
|
|||||||
return [cb.text() for cb in self.checkboxes if cb.isChecked()]
|
return [cb.text() for cb in self.checkboxes if cb.isChecked()]
|
||||||
|
|
||||||
|
|
||||||
|
class SetSizeDialog(QDialog):
|
||||||
|
"""
|
||||||
|
Диалоговое окно для выбора числового значения (размера).
|
||||||
|
"""
|
||||||
|
def __init__(self, parent=None, initial_value=10, min_value=1, max_value=50, title="Укажите размер короткого имени"):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setWindowTitle(title)
|
||||||
|
self.setFixedSize(320, 120) # Задаем фиксированный размер для аккуратного вида
|
||||||
|
|
||||||
|
# Основной вертикальный макет
|
||||||
|
main_layout = QVBoxLayout(self)
|
||||||
|
|
||||||
|
# Макет для ввода значения
|
||||||
|
input_layout = QHBoxLayout()
|
||||||
|
label = QLabel("Количество символов:", self)
|
||||||
|
|
||||||
|
self.spin_box = QSpinBox(self)
|
||||||
|
self.spin_box.setRange(min_value, max_value) # Устанавливаем диапазон допустимых значений
|
||||||
|
initial_value = parent._shortname_size
|
||||||
|
self.spin_box.setValue(initial_value) # Устанавливаем начальное значение
|
||||||
|
self.spin_box.setFocus() # Устанавливаем фокус на поле ввода
|
||||||
|
|
||||||
|
input_layout.addWidget(label)
|
||||||
|
input_layout.addWidget(self.spin_box)
|
||||||
|
main_layout.addLayout(input_layout)
|
||||||
|
|
||||||
|
# Добавляем пустое пространство для лучшего разделения
|
||||||
|
main_layout.addSpacerItem(QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding))
|
||||||
|
|
||||||
|
# Макет для кнопок
|
||||||
|
btn_layout = QHBoxLayout()
|
||||||
|
btn_layout.addStretch() # Добавляем растягивающийся элемент, чтобы кнопки были справа
|
||||||
|
|
||||||
|
btn_ok = QPushButton("OK")
|
||||||
|
btn_cancel = QPushButton("Отмена")
|
||||||
|
|
||||||
|
btn_layout.addWidget(btn_ok)
|
||||||
|
btn_layout.addWidget(btn_cancel)
|
||||||
|
|
||||||
|
main_layout.addLayout(btn_layout)
|
||||||
|
|
||||||
|
# Подключение сигналов к слотам
|
||||||
|
btn_ok.clicked.connect(self.accept) # При нажатии "OK" диалог закроется со статусом "Accepted"
|
||||||
|
btn_cancel.clicked.connect(self.reject) # При нажатии "Отмена" - со статусом "Rejected"
|
||||||
|
|
||||||
|
def get_selected_size(self):
|
||||||
|
"""
|
||||||
|
Возвращает значение, выбранное в QSpinBox.
|
||||||
|
"""
|
||||||
|
return self.spin_box.value()
|
||||||
|
|
||||||
class CtrlScrollComboBox(QComboBox):
|
class CtrlScrollComboBox(QComboBox):
|
||||||
def wheelEvent(self, event):
|
def wheelEvent(self, event):
|
||||||
if event.modifiers() & Qt.ControlModifier:
|
if event.modifiers() & Qt.ControlModifier:
|
||||||
@@ -83,6 +134,11 @@ class VariableTableWidget(QTableWidget):
|
|||||||
])
|
])
|
||||||
self.setEditTriggers(QAbstractItemView.AllEditTriggers)
|
self.setEditTriggers(QAbstractItemView.AllEditTriggers)
|
||||||
self.var_list = []
|
self.var_list = []
|
||||||
|
# Инициализируем QSettings с именем организации и приложения
|
||||||
|
self.settings = QSettings("SET", "DebugVarEdit_VarTable")
|
||||||
|
# Восстанавливаем сохранённое состояние, если есть
|
||||||
|
shortsize = self.settings.value("shortname_size", True, type=int)
|
||||||
|
self._shortname_size = shortsize
|
||||||
|
|
||||||
self.type_options = list(dict.fromkeys(type_map.values()))
|
self.type_options = list(dict.fromkeys(type_map.values()))
|
||||||
self.pt_types_all = [t.replace('pt_', '') for t in self.type_options]
|
self.pt_types_all = [t.replace('pt_', '') for t in self.type_options]
|
||||||
@@ -231,7 +287,7 @@ class VariableTableWidget(QTableWidget):
|
|||||||
t3 = time.time()
|
t3 = time.time()
|
||||||
shortname = short_name_edit.text() if short_name_edit else ""
|
shortname = short_name_edit.text() if short_name_edit else ""
|
||||||
|
|
||||||
long_shortname = len(shortname) > 10
|
long_shortname = len(shortname) > self._shortname_size
|
||||||
found = name in var_names_set
|
found = name in var_names_set
|
||||||
|
|
||||||
color = None
|
color = None
|
||||||
@@ -293,6 +349,13 @@ class VariableTableWidget(QTableWidget):
|
|||||||
if dlg.exec_():
|
if dlg.exec_():
|
||||||
self._ret_type_filter = dlg.get_selected()
|
self._ret_type_filter = dlg.get_selected()
|
||||||
self.update_comboboxes({rows.ret_type: self._ret_type_filter})
|
self.update_comboboxes({rows.ret_type: self._ret_type_filter})
|
||||||
|
|
||||||
|
elif logicalIndex == rows.short_name:
|
||||||
|
dlg = SetSizeDialog(self)
|
||||||
|
if dlg.exec_():
|
||||||
|
self._shortname_size = dlg.get_selected_size()
|
||||||
|
self.settings.setValue("shortname_size", self._shortname_size)
|
||||||
|
self.check()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
453
debug_tools.c
453
debug_tools.c
@@ -1,79 +1,132 @@
|
|||||||
#include "debug_tools.h"
|
#include "debug_tools.h"
|
||||||
#include "IQmathLib.h"
|
|
||||||
|
|
||||||
DebugLowLevel_t debug_ll = DEBUG_LOWLEVEL_INIT;
|
#if !defined(GLOBAL_Q)
|
||||||
|
#define GLOBAL_Q 16
|
||||||
|
#endif
|
||||||
|
|
||||||
static int getDebugVar(DebugVar_t *var, long *int_var, float *float_var);
|
|
||||||
static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var);
|
|
||||||
|
|
||||||
|
DebugLowLevel_t debug_ll = DEBUG_LOWLEVEL_INIT; ///< Ñòðóêòóðà îòëàäêè íèæíåãî óðîâíÿ (èíèöèàëèçàöèÿ)
|
||||||
|
|
||||||
|
static int getDebugVar(DebugVar_t *var, int32_t *int_var, float *float_var);
|
||||||
|
static int convertDebugVarToIQx(DebugVar_t *var, int32_t *ret_var);
|
||||||
|
static int iqTypeToQ(DebugVarIQType_t t);
|
||||||
///////////////////////////----EXAPLE-----//////////////////////////////
|
///////////////////////////----EXAPLE-----//////////////////////////////
|
||||||
long var_numb = 1;
|
int var_numb = 1; ///< Ïðèìåð ïåðåìåííîé äëÿ îòëàäêè
|
||||||
DebugVarName_t var_name;
|
DebugVarName_t var_name; ///< Èìÿ ïåðåìåííîé
|
||||||
long return_var;
|
int32_t return_var; ///< Ïåðåìåííàÿ äëÿ âîçâðàòà ðåçóëüòàòà
|
||||||
long return_ll_var;
|
int32_t return_ll_var; ///< Âîçâðàùàåìîå çíà÷åíèå ñ íèæíåãî óðîâíÿ
|
||||||
int result;
|
int result; ///< Ïåðåìåííàÿ ðåçóëüòàòà
|
||||||
char ext_date[] = {7, 233, 11, 07, 16, 50};
|
DateTime_t ext_date = {2025, 11, 07, 16, 50}; ///< Ïðèìåð âíåøíåé äàòû ñáîðêè
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Ïðèìåð èñïîëüçîâàíèÿ ôóíêöèé îòëàäêè.
|
||||||
|
* @details Äåìîíñòðàöèîííàÿ ôóíêöèÿ äëÿ ðàáîòû ñ ïåðåìåííûìè îòëàäêè.
|
||||||
|
*/
|
||||||
void Debug_Test_Example(void)
|
void Debug_Test_Example(void)
|
||||||
{
|
{
|
||||||
|
return;
|
||||||
result = Debug_ReadVar(var_numb, &return_var);
|
result = Debug_ReadVar(var_numb, &return_var);
|
||||||
result = Debug_ReadVarName(var_numb, var_name);
|
result = Debug_ReadVarName(var_numb, var_name);
|
||||||
|
|
||||||
|
|
||||||
if(Debug_LowLevel_Initialize(ext_date) == 0)
|
if(Debug_LowLevel_Initialize(&ext_date) == 0)
|
||||||
result = Debug_LowLevel_ReadVar(&return_ll_var);
|
result = Debug_LowLevel_ReadVar(&return_ll_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////----PUBLIC-----//////////////////////////////
|
///////////////////////////----PUBLIC-----//////////////////////////////
|
||||||
int Debug_ReadVar(int var_ind, long *return_long)
|
|
||||||
{
|
|
||||||
if(return_long == NULL)
|
|
||||||
return 1;
|
|
||||||
long tmp_var;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ×èòàåò ïåðåìåííóþ ïî èíäåêñó.
|
||||||
|
* @param var_ind – èíäåêñ ïåðåìåííîé.
|
||||||
|
* @param return_32b – óêàçàòåëü äëÿ âîçâðàòà ðåçóëüòàòà.
|
||||||
|
* @return int – 0: óñïåõ, 1: îøèáêà.
|
||||||
|
* @details Èñïîëüçóåòñÿ äëÿ ÷òåíèÿ çíà÷åíèé ïåðåìåííûõ ïî èõ èíäåêñó.
|
||||||
|
*/
|
||||||
|
int Debug_ReadVar(int var_ind, int32_t *return_32b)
|
||||||
|
{
|
||||||
|
int32_t tmp_var;
|
||||||
|
|
||||||
|
if(return_32b == NULL)
|
||||||
|
return 1;
|
||||||
if (var_ind >= DebugVar_Qnt)
|
if (var_ind >= DebugVar_Qnt)
|
||||||
return 1;
|
return 1;
|
||||||
if((dbg_vars[var_numb].ptr_type == pt_struct) || (dbg_vars[var_numb].ptr_type == pt_union) ||
|
if((dbg_vars[var_ind].ptr_type == pt_struct) || (dbg_vars[var_ind].ptr_type == pt_union) ||
|
||||||
(dbg_vars[var_numb].ptr_type == pt_unknown))
|
(dbg_vars[var_ind].ptr_type == pt_unknown))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
return convertDebugVarToIQx(&dbg_vars[var_ind], return_32b);
|
||||||
|
|
||||||
return convertDebugVarToIQx(&dbg_vars[var_numb], return_long);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ×èòàåò âîçâðàùàåìûé òèï ïåðåìåííîé ïî èíäåêñó.
|
||||||
|
* @param var_ind – èíäåêñ ïåðåìåííîé.
|
||||||
|
* @param vartype – óêàçàòåëü äëÿ âîçâðàòà òèïà.
|
||||||
|
* @return int – 0: óñïåõ, 1: îøèáêà.
|
||||||
|
* @details Èñïîëüçóåòñÿ äëÿ ÷òåíèÿ çíà÷åíèé ïåðåìåííûõ ïî èõ èíäåêñó.
|
||||||
|
*/
|
||||||
|
int Debug_ReadVarReturnType(int var_ind, int *vartype)
|
||||||
|
{
|
||||||
|
int rettype;
|
||||||
|
if(vartype == NULL)
|
||||||
|
return 1;
|
||||||
|
if (var_ind >= DebugVar_Qnt)
|
||||||
|
return 1;
|
||||||
|
if((dbg_vars[var_ind].ptr_type == pt_struct) || (dbg_vars[var_ind].ptr_type == pt_union) ||
|
||||||
|
(dbg_vars[var_ind].ptr_type == pt_unknown))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
*vartype = iqTypeToQ(dbg_vars[var_ind].return_type);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ×èòàåò èìÿ ïåðåìåííîé ïî èíäåêñó.
|
||||||
|
* @param var_ind – èíäåêñ ïåðåìåííîé.
|
||||||
|
* @param name_ptr – óêàçàòåëü íà áóôåð èìåíè (DebugVarName_t).
|
||||||
|
* @return int – 0: óñïåõ, 1: îøèáêà.
|
||||||
|
* @details Êîïèðóåò èìÿ ïåðåìåííîé â ïðåäîñòàâëåííûé áóôåð.
|
||||||
|
*/
|
||||||
int Debug_ReadVarName(int var_ind, DebugVarName_t name_ptr)
|
int Debug_ReadVarName(int var_ind, DebugVarName_t name_ptr)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
if(name_ptr == NULL)
|
if(name_ptr == NULL)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (var_ind >= DebugVar_Qnt)
|
if (var_ind >= DebugVar_Qnt)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
int i;
|
|
||||||
// Êîïèðîâàíèå ñ çàùèòîé îò ïåðåïîëíåíèÿ è ÿâíîé îñòàíîâêîé ïî '\0'
|
// Êîïèðîâàíèå ñ çàùèòîé îò ïåðåïîëíåíèÿ è ÿâíîé îñòàíîâêîé ïî '\0'
|
||||||
for (i = 0; i < sizeof(dbg_vars[var_numb].name); i++)
|
for (i = 0; i < sizeof(dbg_vars[var_ind].name); i++)
|
||||||
{
|
{
|
||||||
name_ptr[i] = dbg_vars[var_numb].name[i];
|
name_ptr[i] = dbg_vars[var_ind].name[i];
|
||||||
if (dbg_vars[var_numb].name[i] == '\0')
|
if (dbg_vars[var_ind].name[i] == '\0')
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Ãàðàíòèðîâàííîå çàâåðøåíèå ñòðîêè (íà ñëó÷àé, åñëè â var->name íå áûëî '\0')
|
// Ãàðàíòèðîâàííîå çàâåðøåíèå ñòðîêè (íà ñëó÷àé, åñëè â var->name íå áûëî '\0')
|
||||||
name_ptr[sizeof(dbg_vars[var_numb].name) - 1] = '\0';
|
name_ptr[sizeof(dbg_vars[var_ind].name) - 1] = '\0';
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
int Debug_LowLevel_ReadVar(long *return_long)
|
* @brief ×èòàåò çíà÷åíèå ïåðåìåííîé îòëàäêè ñ íèæíåãî óðîâíÿ.
|
||||||
|
* @param return_32b – óêàçàòåëü íà ïåðåìåííóþ, êóäà çàïèñûâàåòñÿ ðåçóëüòàò.
|
||||||
|
* @return int – 0: óñïåõ, 1: îøèáêà, 2: íåäîïóñòèìûé àäðåñ.
|
||||||
|
* @details Èñïîëüçóåò àäðåññ, ïåðåäàâàåìûé ñ òåðìèíàëêè äëÿ ïîëó÷åíèÿ çíà÷åíèÿ.
|
||||||
|
*/
|
||||||
|
int Debug_LowLevel_ReadVar(int32_t *return_32b)
|
||||||
{
|
{
|
||||||
if (return_long == NULL)
|
uint8_t *addr = debug_ll.dbg_var.Ptr;
|
||||||
|
uint32_t addr_val = (uint32_t)addr;
|
||||||
|
|
||||||
|
if (return_32b == NULL)
|
||||||
return 1;
|
return 1;
|
||||||
if (debug_ll.isVerified == 0)
|
if (debug_ll.isVerified == 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
char *addr = debug_ll.dbg_var.Ptr;
|
|
||||||
unsigned long addr_val = (unsigned long)addr;
|
|
||||||
|
|
||||||
// Ðàçðåø¸ííûå äèàïàçîíû ïàìÿòè (èç .cmd ôàéëà)
|
// Ðàçðåø¸ííûå äèàïàçîíû ïàìÿòè (èç .cmd ôàéëà)
|
||||||
if (!(
|
if (!(
|
||||||
@@ -89,32 +142,28 @@ int Debug_LowLevel_ReadVar(long *return_long)
|
|||||||
return 2; // Çàïðåù¸ííûé àäðåñ — íåëüçÿ ÷èòàòü
|
return 2; // Çàïðåù¸ííûé àäðåñ — íåëüçÿ ÷èòàòü
|
||||||
}
|
}
|
||||||
|
|
||||||
return convertDebugVarToIQx(&debug_ll.dbg_var, return_long);
|
return convertDebugVarToIQx(&debug_ll.dbg_var, return_32b);
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
int Debug_LowLevel_Initialize(const char* external_date)
|
* @brief Èíèöèàëèçàöèÿ îòëàäêè íà íèæíåì óðîâíå ïî äàòå ñáîðêè.
|
||||||
|
* @param external_date – ñòðóêòóðà ñ äàòîé DateTime_t
|
||||||
|
* @return int – 0: ñîâïàäàåò, 1: íå ñîâïàäàåò, -1: îøèáêà.
|
||||||
|
* @details Ñðàâíèâàåò äàòó êîìïèëÿöèè ñ çàïðàøèâàåìîé è èíèöèàëèçèðóåò îòëàäî÷íóþ ïåðåìåííóþ.
|
||||||
|
*/
|
||||||
|
int Debug_LowLevel_Initialize(DateTime_t* external_date)
|
||||||
{
|
{
|
||||||
if (external_date == NULL) {
|
if (external_date == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ïðåîáðàçóåì external_date â ñòðóêòóðó
|
|
||||||
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];
|
|
||||||
|
|
||||||
|
|
||||||
// Ñðàâíåíèå âñåõ ïîëåé
|
// Ñðàâíåíèå âñåõ ïîëåé
|
||||||
if (ext.year == debug_ll.build_date.year &&
|
if (external_date->year == debug_ll.build_date.year &&
|
||||||
ext.month == debug_ll.build_date.month &&
|
external_date->month == debug_ll.build_date.month &&
|
||||||
ext.day == debug_ll.build_date.day &&
|
external_date->day == debug_ll.build_date.day &&
|
||||||
ext.hour == debug_ll.build_date.hour &&
|
external_date->hour == debug_ll.build_date.hour &&
|
||||||
ext.minute == debug_ll.build_date.minute)
|
external_date->minute == debug_ll.build_date.minute)
|
||||||
{
|
{
|
||||||
debug_ll.isVerified = 1;
|
debug_ll.isVerified = 1;
|
||||||
return 0; // Ñîâïàëî
|
return 0; // Ñîâïàëî
|
||||||
@@ -129,6 +178,12 @@ int Debug_LowLevel_Initialize(const char* external_date)
|
|||||||
|
|
||||||
|
|
||||||
/////////////////////----INTERNAL FUNCTIONS-----////////////////////////
|
/////////////////////----INTERNAL FUNCTIONS-----////////////////////////
|
||||||
|
/**
|
||||||
|
* @brief Ïðåîáðàçóåò òèï IQ ïåðåìåííîé â ÷èñëî áèòîâ äëÿ ñäâèãà(Q-ôàêòîð).
|
||||||
|
* @param t – òèï IQ (ïåðå÷èñëåíèå DebugVarIQType_t).
|
||||||
|
* @return int – Q-ôàêòîð (íàïðèìåð, 24), 0: åñëè t_iq_none, -1: îøèáêà.
|
||||||
|
* @details Ñîïîñòàâëÿåò òèï IQ ïåðåìåííîé ñ ñîîòâåòñòâóþùèì Q-çíà÷åíèåì.
|
||||||
|
*/
|
||||||
static int iqTypeToQ(DebugVarIQType_t t)
|
static int iqTypeToQ(DebugVarIQType_t t)
|
||||||
{
|
{
|
||||||
if (t == t_iq_none)
|
if (t == t_iq_none)
|
||||||
@@ -141,9 +196,19 @@ static int iqTypeToQ(DebugVarIQType_t t)
|
|||||||
return -1; // îøèáêà
|
return -1; // îøèáêà
|
||||||
}
|
}
|
||||||
|
|
||||||
static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var)
|
/**
|
||||||
|
* @brief Ïðåîáðàçóåò ïåðåìåííóþ îòëàäêè â IQ ôîðìàò.
|
||||||
|
* @param var – óêàçàòåëü íà ïåðåìåííóþ îòëàäêè.
|
||||||
|
* @param ret_var – óêàçàòåëü äëÿ âîçâðàòà çíà÷åíèÿ â ôîðìàòå 32 áèòà.
|
||||||
|
* @return int – 0: óñïåõ, 1: îøèáêà ÷òåíèÿ, 2: íåïðàâèëüíûé ôîðìàò, 3: ïåðåïîëíåíèå.
|
||||||
|
* @details Îïðåäåëÿåò ôîðìàò IQ ïåðåìåííîé, êîíâåðòèðóåò å¸ â 32b ñ ó÷¸òîì ìàñøòàáà.
|
||||||
|
*/
|
||||||
|
static int convertDebugVarToIQx(DebugVar_t *var, int32_t *ret_var)
|
||||||
{
|
{
|
||||||
long iq_numb, iq_united, iq_final;
|
int32_t iq_numb, iq_united, iq_final;
|
||||||
|
int64_t iq_united64 = 0;
|
||||||
|
int64_t iq_final64 = 0;
|
||||||
|
|
||||||
float float_numb;
|
float float_numb;
|
||||||
|
|
||||||
if(getDebugVar(var, &iq_numb, &float_numb) != 0)
|
if(getDebugVar(var, &iq_numb, &float_numb) != 0)
|
||||||
@@ -155,30 +220,28 @@ static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var)
|
|||||||
if (src_q < 0 || dst_q < 0)
|
if (src_q < 0 || dst_q < 0)
|
||||||
return 2; // íåïðàâèëüíûé ôîðìàò
|
return 2; // íåïðàâèëüíûé ôîðìàò
|
||||||
|
|
||||||
long long iq_united64 = 0;
|
|
||||||
long long iq_final64 = 0;
|
|
||||||
|
|
||||||
// Êîíâåðòàöèÿ ê GLOBAL_Q (64-áèò)
|
// Êîíâåðòàöèÿ ê GLOBAL_Q (64-áèò)
|
||||||
if (var->iq_type == t_iq_none) {
|
if (var->iq_type == t_iq_none) {
|
||||||
if (var->ptr_type == pt_float) {
|
if (var->ptr_type == pt_float) {
|
||||||
// float_numb óìíîæàåì íà 2^GLOBAL_Q (2^24=16777216)
|
// float_numb óìíîæàåì íà 2^GLOBAL_Q
|
||||||
// Ðåçóëüòàò ïðèâîäèì ê long long
|
// Ðåçóëüòàò ïðèâîäèì ê 64 áèòà
|
||||||
iq_united64 = (long long)(float_numb * 16777216.0f);
|
iq_united64 = (int64_t)(float_numb * ((uint32_t)1 << GLOBAL_Q));
|
||||||
} else {
|
} else {
|
||||||
iq_united64 = ((long long)iq_numb) << GLOBAL_Q;
|
iq_united64 = ((int64_t)iq_numb) << GLOBAL_Q;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int shift = GLOBAL_Q - src_q;
|
int shift = GLOBAL_Q - src_q;
|
||||||
if (shift >= 0)
|
if (shift >= 0)
|
||||||
iq_united64 = ((long long)iq_numb) << shift;
|
iq_united64 = ((int64_t)iq_numb) << shift;
|
||||||
else
|
else
|
||||||
iq_united64 = ((long long)iq_numb) >> (-shift);
|
iq_united64 = ((int64_t)iq_numb) >> (-shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Êîíâåðòàöèÿ èç GLOBAL_Q â öåëåâîé IQ (64-áèò)
|
// Êîíâåðòàöèÿ èç GLOBAL_Q â öåëåâîé IQ (64-áèò)
|
||||||
if (var->return_type == t_iq_none) {
|
if (var->return_type == t_iq_none) {
|
||||||
// Âîçâðàùàåì öåëîå, îòáðîñèâ äðîáíóþ ÷àñòü
|
// Âîçâðàùàåì öåëîå, îòáðîñèâ äðîáíóþ ÷àñòü
|
||||||
*ret_var = (long)(iq_united64 >> GLOBAL_Q);
|
*ret_var = (uint32_t)(iq_united64 >> GLOBAL_Q);
|
||||||
} else {
|
} else {
|
||||||
int shift = dst_q - GLOBAL_Q;
|
int shift = dst_q - GLOBAL_Q;
|
||||||
if (shift >= 0)
|
if (shift >= 0)
|
||||||
@@ -186,55 +249,69 @@ static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var)
|
|||||||
else
|
else
|
||||||
iq_final64 = iq_united64 >> (-shift);
|
iq_final64 = iq_united64 >> (-shift);
|
||||||
|
|
||||||
// Ïðîâåðÿåì ïåðåïîëíåíèå int32_t
|
// // Ïðîâåðÿåì ïåðåïîëíåíèå int32_t
|
||||||
if (iq_final64 > LONG_MAX || iq_final64 < LONG_MIN)
|
// if (iq_final64 > 2147483647 || iq_final64 < -2147483648)
|
||||||
return 3; // ïåðåïîëíåíèå
|
// return 3; // ïåðåïîëíåíèå
|
||||||
|
|
||||||
*ret_var = (long)iq_final64;
|
*ret_var = (int32_t)iq_final64;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getDebugVar(DebugVar_t *var, long *int_var, float *float_var)
|
/**
|
||||||
|
* @brief Ïðî÷èòàòü çíà÷åíèå ïåðåìåííîé îòëàäêè.
|
||||||
|
* @param var – óêàçàòåëü íà ñòðóêòóðó DebugVar.
|
||||||
|
* @param int_var – óêàçàòåëü íà ïåðåìåííóþ òèïà 32 áèòà äëÿ âîçâðàòà öåëî÷èñëåííîãî çíà÷åíèÿ.
|
||||||
|
* @param float_var – óêàçàòåëü íà ïåðåìåííóþ òèïà float äëÿ âîçâðàòà çíà÷åíèÿ ñ ïëàâàþùåé òî÷êîé.
|
||||||
|
* @return int – 0: óñïåõ, 1: îøèáêà óêàçàòåëåé èëè íåïîääåðæèâàåìûé òèï, 3/4: îøèáêà âûðàâíèâàíèÿ.
|
||||||
|
* @details  çàâèñèìîñòè îò òèïà ïåðåìåííîé ñ÷èòûâàåò å¸ çíà÷åíèå è ñîõðàíÿåò â ñîîòâåòñòâóþùåì óêàçàòåëå.
|
||||||
|
*/
|
||||||
|
static int getDebugVar(DebugVar_t *var, int32_t *int_var, float *float_var)
|
||||||
{
|
{
|
||||||
|
uint8_t *addr = var->Ptr;
|
||||||
|
uint32_t addr_val = (uint32_t)addr;
|
||||||
|
|
||||||
if (!var || !int_var || !float_var || !var->Ptr)
|
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: // 8 áèò
|
case pt_int8: // 8 áèò
|
||||||
|
if ((addr_val & ALIGN_8BIT) != 0) // ïðîâåðÿåì âûðàâíèâàíèå
|
||||||
|
return 1; // îøèáêà âûðàâíèâàíèÿ
|
||||||
|
*int_var = *((volatile int8_t *)addr);
|
||||||
|
break;
|
||||||
case pt_uint8:
|
case pt_uint8:
|
||||||
// âûðàâíèâàíèå íå íóæíî äëÿ 8 áèò
|
if ((addr_val & ALIGN_8BIT) != 0) // ïðîâåðÿåì âûðàâíèâàíèå
|
||||||
*int_var = *((volatile char *)addr);
|
return 1; // îøèáêà âûðàâíèâàíèÿ
|
||||||
|
*int_var = *((volatile uint8_t *)addr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case pt_int16: // 16 áèò (int)
|
case pt_int16: // 16 áèò (int)
|
||||||
|
if ((addr_val & ALIGN_16BIT) != 0) // ïðîâåðÿåì âûðàâíèâàíèå
|
||||||
|
return 2; // îøèáêà âûðàâíèâàíèÿ
|
||||||
|
*int_var = *((volatile int16_t *)addr);
|
||||||
|
break;
|
||||||
case pt_uint16:
|
case pt_uint16:
|
||||||
*int_var = *((volatile int *)addr);
|
if ((addr_val & ALIGN_16BIT) != 0) // ïðîâåðÿåì âûðàâíèâàíèå
|
||||||
|
return 2; // îøèáêà âûðàâíèâàíèÿ
|
||||||
|
*int_var = *((volatile uint16_t *)addr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case pt_int32: // 32 áèò (long)
|
case pt_int32: // 32 áèò
|
||||||
case pt_uint32:
|
if ((addr_val & ALIGN_32BIT) != 0) // ïðîâåðÿåì âûðàâíèâàíèå
|
||||||
if (addr_val & 0x1) // ïðîâåðÿåì âûðàâíèâàíèå ïî 2 ñëîâàì (4 áàéòà)
|
|
||||||
return 3; // îøèáêà âûðàâíèâàíèÿ
|
return 3; // îøèáêà âûðàâíèâàíèÿ
|
||||||
*int_var = *((volatile long *)addr);
|
*int_var = *((volatile int32_t *)addr);
|
||||||
|
break;
|
||||||
|
case pt_uint32:
|
||||||
|
if ((addr_val & ALIGN_32BIT) != 0) // ïðîâåðÿåì âûðàâíèâàíèå
|
||||||
|
return 3; // îøèáêà âûðàâíèâàíèÿ
|
||||||
|
*int_var = *((volatile uint32_t *)addr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// case pt_int64: // 64 áèò (long long)
|
|
||||||
// case pt_uint64:
|
|
||||||
// if (addr_val & 0x3) // ïðîâåðêà âûðàâíèâàíèÿ ïî 4 ñëîâàì (8 áàéòàì)
|
|
||||||
// return 2; // îøèáêà âûðàâíèâàíèÿ
|
|
||||||
// // Òóò ïðîñòî ÷èòàåì, íî long long ìîæåò íå ïîìåñòèòüñÿ â *int_var
|
|
||||||
// // Ìîæíî çàìåíèòü ëîãèêó ïîä 64-áèòíîå ÷òåíèå ïðè íåîáõîäèìîñòè
|
|
||||||
// *int_var = *((volatile long long *)addr);
|
|
||||||
// break;
|
|
||||||
|
|
||||||
case pt_float: // float (4 áàéòà)
|
case pt_float: // float (4 áàéòà)
|
||||||
if (addr_val & 0x1) // ïðîâåðêà âûðàâíèâàíèÿ ïî 2 ñëîâàì
|
if ((addr_val & ALIGN_FLOAT) != 0) // ïðîâåðêà âûðàâíèâàíèÿ
|
||||||
return 4; // îøèáêà âûðàâíèâàíèÿ
|
return 4; // îøèáêà âûðàâíèâàíèÿ
|
||||||
*float_var = *((volatile float *)addr);
|
*float_var = *((volatile float *)addr);
|
||||||
break;
|
break;
|
||||||
@@ -259,211 +336,3 @@ static int getDebugVar(DebugVar_t *var, long *int_var, float *float_var)
|
|||||||
return 0; // óñïåõ
|
return 0; // óñïåõ
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////// OUTDATE ////////////////
|
|
||||||
|
|
||||||
//
|
|
||||||
// // ïðèâåäåíèå ê îäíîìó IQ
|
|
||||||
// switch(var->iq_type)
|
|
||||||
// {
|
|
||||||
// case t_iq_none:
|
|
||||||
// if(var->ptr_type == pt_float)
|
|
||||||
// {
|
|
||||||
// iq_united = _IQ(float_numb);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// iq_united = _IQ(iq_numb);
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// case t_iq1:
|
|
||||||
// iq_united = _IQ1toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq2:
|
|
||||||
// iq_united = _IQ2toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq3:
|
|
||||||
// iq_united = _IQ3toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq4:
|
|
||||||
// iq_united = _IQ4toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq5:
|
|
||||||
// iq_united = _IQ5toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq6:
|
|
||||||
// iq_united = _IQ6toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq7:
|
|
||||||
// iq_united = _IQ7toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq8:
|
|
||||||
// iq_united = _IQ8toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq9:
|
|
||||||
// iq_united = _IQ9toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq10:
|
|
||||||
// iq_united = _IQ10toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq11:
|
|
||||||
// iq_united = _IQ11toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq12:
|
|
||||||
// iq_united = _IQ12toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq13:
|
|
||||||
// iq_united = _IQ13toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq14:
|
|
||||||
// iq_united = _IQ14toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq15:
|
|
||||||
// iq_united = _IQ15toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq16:
|
|
||||||
// iq_united = _IQ16toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq17:
|
|
||||||
// iq_united = _IQ17toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq18:
|
|
||||||
// iq_united = _IQ18toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq19:
|
|
||||||
// iq_united = _IQ19toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq20:
|
|
||||||
// iq_united = _IQ20toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq21:
|
|
||||||
// iq_united = _IQ21toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq22:
|
|
||||||
// iq_united = _IQ22toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq23:
|
|
||||||
// iq_united = _IQ23toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq24:
|
|
||||||
// iq_united = _IQ24toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq25:
|
|
||||||
// iq_united = _IQ25toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq26:
|
|
||||||
// iq_united = _IQ26toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq27:
|
|
||||||
// iq_united = _IQ27toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq28:
|
|
||||||
// iq_united = _IQ28toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq29:
|
|
||||||
// iq_united = _IQ29toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// case t_iq30:
|
|
||||||
// iq_united = _IQ30toIQ(iq_numb);
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // ïðèâåäåíèå îáùåãî IQ ê çàïðàøèâàåìîìó
|
|
||||||
// switch(var->return_type)
|
|
||||||
// {
|
|
||||||
// case t_iq_none:
|
|
||||||
// iq_final = (long)_IQtoF(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq1:
|
|
||||||
// iq_final = _IQtoIQ1(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq2:
|
|
||||||
// iq_final = _IQtoIQ2(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq3:
|
|
||||||
// iq_final = _IQtoIQ3(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq4:
|
|
||||||
// iq_final = _IQtoIQ4(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq5:
|
|
||||||
// iq_final = _IQtoIQ5(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq6:
|
|
||||||
// iq_final = _IQtoIQ6(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq7:
|
|
||||||
// iq_final = _IQtoIQ7(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq8:
|
|
||||||
// iq_final = _IQtoIQ8(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq9:
|
|
||||||
// iq_final = _IQtoIQ9(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq10:
|
|
||||||
// iq_final = _IQtoIQ10(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq11:
|
|
||||||
// iq_final = _IQtoIQ11(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq12:
|
|
||||||
// iq_final = _IQtoIQ12(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq13:
|
|
||||||
// iq_final = _IQtoIQ13(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq14:
|
|
||||||
// iq_final = _IQtoIQ14(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq15:
|
|
||||||
// iq_final = _IQtoIQ15(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq16:
|
|
||||||
// iq_final = _IQtoIQ16(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq17:
|
|
||||||
// iq_final = _IQtoIQ17(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq18:
|
|
||||||
// iq_final = _IQtoIQ18(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq19:
|
|
||||||
// iq_final = _IQtoIQ19(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq20:
|
|
||||||
// iq_final = _IQtoIQ20(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq21:
|
|
||||||
// iq_final = _IQtoIQ21(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq22:
|
|
||||||
// iq_final = _IQtoIQ22(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq23:
|
|
||||||
// iq_final = _IQtoIQ23(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq24:
|
|
||||||
// iq_final = _IQtoIQ24(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq25:
|
|
||||||
// iq_final = _IQtoIQ25(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq26:
|
|
||||||
// iq_final = _IQtoIQ26(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq27:
|
|
||||||
// iq_final = _IQtoIQ27(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq28:
|
|
||||||
// iq_final = _IQtoIQ28(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq29:
|
|
||||||
// iq_final = _IQtoIQ29(iq_united);
|
|
||||||
// break;
|
|
||||||
// case t_iq30:
|
|
||||||
// iq_final = _IQtoIQ30(iq_united);
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// *ret_var = iq_final;
|
|
||||||
|
|||||||
107
debug_tools.h
107
debug_tools.h
@@ -1,8 +1,40 @@
|
|||||||
#ifndef DEBUG_TOOLS
|
#ifndef DEBUG_TOOLS
|
||||||
#define DEBUG_TOOLS
|
#define DEBUG_TOOLS
|
||||||
#include "IQmathLib.h"
|
#include <stdint.h>
|
||||||
#include "DSP281x_Device.h"
|
#include <limits.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if UINT8_MAX // Åñëè åñòü òèï 8 áèò - çí÷à÷èò àäðåñàöèÿ ïî 8 áèò
|
||||||
|
|
||||||
|
#define ALIGN_8BIT 0x0 ///< Âûðàâíèâàíèå áåç îãðàíè÷åíèé (ëþáîé àäðåñ)
|
||||||
|
#define ALIGN_16BIT 0x1 ///< Âûðàâíèâàíèå: àäðåñ äîëæåí áûòü êðàòåí 2 (addr & 0x1 == 0)
|
||||||
|
#define ALIGN_32BIT 0x3 ///< Âûðàâíèâàíèå: àäðåñ äîëæåí áûòü êðàòåí 4 (addr & 0x3 == 0)
|
||||||
|
#define ALIGN_64BIT 0x7 ///< Âûðàâíèâàíèå: àäðåñ äîëæåí áûòü êðàòåí 8 (addr & 0x7 == 0)
|
||||||
|
#define ALIGN_FLOAT ALIGN_32BIT
|
||||||
|
|
||||||
|
#else // Åñëè íåò òèïà 8 áèò - çíà÷èò àäðåñàöèÿ ïî 16 áèò
|
||||||
|
|
||||||
|
#define ALIGN_8BIT 0x0 ///< Âûðàâíèâàíèå áåç îãðàíè÷åíèé (ëþáîé àäðåñ)
|
||||||
|
#define ALIGN_16BIT 0x0 ///< Âûðàâíèâàíèå áåç îãðàíè÷åíèé (ëþáîé àäðåñ)
|
||||||
|
#define ALIGN_32BIT 0x1 ///< Âûðàâíèâàíèå: àäðåñ äîëæåí áûòü êðàòåí 4 (addr & 0x1 == 0)
|
||||||
|
#define ALIGN_64BIT 0x3 ///< Âûðàâíèâàíèå: àäðåñ äîëæåí áûòü êðàòåí 8 (addr & 0x3 == 0)
|
||||||
|
#define ALIGN_FLOAT ALIGN_32BIT
|
||||||
|
|
||||||
|
#endif //STM32/TMS32
|
||||||
|
|
||||||
|
#if !UINT8_MAX
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef signed char int8_t;
|
||||||
|
#endif
|
||||||
|
#if !defined(NULL)
|
||||||
|
#define NULL 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Òèï äàííûõ, íà êîòîðûé óêàçûâàåò óêàçàòåëü ïåðåìåííîé îòëàäêè.
|
||||||
|
*/
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
pt_unknown, // unknown
|
pt_unknown, // unknown
|
||||||
@@ -14,7 +46,7 @@ typedef enum
|
|||||||
pt_uint16, // unsigned int
|
pt_uint16, // unsigned int
|
||||||
pt_uint32, // unsigned long
|
pt_uint32, // unsigned long
|
||||||
pt_uint64, // unsigned long
|
pt_uint64, // unsigned long
|
||||||
pt_float, // float
|
pt_float, // floatf
|
||||||
pt_struct, // struct
|
pt_struct, // struct
|
||||||
pt_union, // struct
|
pt_union, // struct
|
||||||
// pt_ptr_int8, // signed char*
|
// pt_ptr_int8, // signed char*
|
||||||
@@ -31,6 +63,9 @@ typedef enum
|
|||||||
// pt_arr_uint32, // unsigned long[]
|
// pt_arr_uint32, // unsigned long[]
|
||||||
}DebugVarPtrType_t;
|
}DebugVarPtrType_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Òèïû IQ-ïðåäñòàâëåíèÿ ïåðåìåííîé îòëàäêè.
|
||||||
|
*/
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
t_iq_none,
|
t_iq_none,
|
||||||
@@ -67,45 +102,69 @@ typedef enum
|
|||||||
t_iq30
|
t_iq30
|
||||||
}DebugVarIQType_t;
|
}DebugVarIQType_t;
|
||||||
|
|
||||||
typedef char DebugVarName_t[11];
|
typedef char DebugVarName_t[11]; ///< Èìÿ ïåðåìåííîé îòëàäêè (äî 10 ñèìâîëîâ + \0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Îïèñàíèå ïåðåìåííîé îòëàäêè.
|
||||||
|
*/
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
char* Ptr;
|
uint8_t* Ptr; ///< Óêàçàòåëü íà çíà÷åíèå ïåðåìåííîé
|
||||||
DebugVarPtrType_t ptr_type;
|
DebugVarPtrType_t ptr_type; ///< Òèï çíà÷åíèÿ
|
||||||
DebugVarIQType_t iq_type;
|
DebugVarIQType_t iq_type; ///< Òèï IQ ïåðåìåííîé (åñëè åñòü)
|
||||||
DebugVarIQType_t return_type;
|
DebugVarIQType_t return_type;///< Òèï IQ âîçâðàùàåìîãî çíà÷åíèÿ
|
||||||
DebugVarName_t name;
|
DebugVarName_t name; ///< Èìÿ ïåðåìåííîé
|
||||||
}DebugVar_t;
|
} DebugVar_t;
|
||||||
|
|
||||||
|
typedef long DebugValue_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Ñòðóêòóðà äàòû è âðåìåíè.
|
||||||
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int year;
|
uint16_t year; ///< Ãîä (íàïðèìåð, 2025)
|
||||||
char month;
|
uint8_t month; ///< Ìåñÿö (1-12)
|
||||||
char day;
|
uint8_t day; ///< Äåíü (1-31)
|
||||||
char hour;
|
uint8_t hour; ///< ×àñû (0-23)
|
||||||
char minute;
|
uint8_t minute; ///< Ìèíóòû (0-59)
|
||||||
} DateTime_t;
|
} DateTime_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Ñòðóêòóðà íèæíåãî óðîâíÿ îòëàäêè.
|
||||||
|
*/
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
DateTime_t build_date;
|
DateTime_t build_date; ///< Äàòà ñáîðêè
|
||||||
unsigned int isVerified;
|
unsigned int isVerified; ///< Ôëàã èíèöèàëèçàöèè íèçêîóðîâíåíîé îòëàäêè (0 — íåò, 1 — óñïåøíî)
|
||||||
DebugVar_t dbg_var;
|
DebugVar_t dbg_var; ///< Ïåðåìåííàÿ äëÿ îòëàäêè
|
||||||
}DebugLowLevel_t;
|
}DebugLowLevel_t;
|
||||||
extern DebugLowLevel_t debug_ll;
|
extern DebugLowLevel_t debug_ll; ///< Ãëîáàëüíûé ýêçåìïëÿð îòëàäêè íèæíåãî óðîâíÿ
|
||||||
|
|
||||||
|
|
||||||
|
/** @brief Ìàêðîñ èíèöèàëèçàöèè äàòû */
|
||||||
#define DATE_INIT {BUILD_YEAR, BUILD_MONTH, BUILD_DATA, BUILD_HOURS, BUILD_MINUTES}
|
#define DATE_INIT {BUILD_YEAR, BUILD_MONTH, BUILD_DATA, BUILD_HOURS, BUILD_MINUTES}
|
||||||
|
/** @brief Ìàêðîñ èíèöèàëèçàöèè ïåðåìåííîé îòëàäêè */
|
||||||
#define DEBUG_VAR_INIT {0, pt_uint16, t_iq_none, t_iq_none, "\0"}
|
#define DEBUG_VAR_INIT {0, pt_uint16, t_iq_none, t_iq_none, "\0"}
|
||||||
|
/** @brief Ìàêðîñ èíèöèàëèçàöèè íèæíåãî óðîâíÿ îòëàäêè */
|
||||||
#define DEBUG_LOWLEVEL_INIT {DATE_INIT, 0, DEBUG_VAR_INIT}
|
#define DEBUG_LOWLEVEL_INIT {DATE_INIT, 0, DEBUG_VAR_INIT}
|
||||||
|
|
||||||
|
|
||||||
extern int DebugVar_Qnt;
|
extern int DebugVar_Qnt; ///< Êîëè÷åñòâî ïåðåìåííûõ îòëàäêè
|
||||||
extern DebugVar_t dbg_vars[];
|
extern DebugVar_t dbg_vars[]; ///< Ìàññèâ ïåðåìåííûõ îòëàäêè
|
||||||
|
|
||||||
|
|
||||||
|
/* Ïðèìåð èñïîëüçîâàíèÿ îòëàäêè */
|
||||||
void Debug_Test_Example(void);
|
void Debug_Test_Example(void);
|
||||||
|
|
||||||
int Debug_ReadVar(int var_ind, long *return_long);
|
/* ×èòàåò çíà÷åíèå ïåðåìåííîé ïî èíäåêñó */
|
||||||
|
int Debug_ReadVar(int var_ind, int32_t *return_long);
|
||||||
|
/* ×èòàåò èìÿ ïåðåìåííîé ïî èíäåêñó */
|
||||||
int Debug_ReadVarName(int var_ind, DebugVarName_t name_ptr);
|
int Debug_ReadVarName(int var_ind, DebugVarName_t name_ptr);
|
||||||
int Debug_LowLevel_ReadVar(long *return_long);
|
/* ×èòàåò âîçâðàùàåìûé òèï ïåðåìåííîé ïî èíäåêñó */
|
||||||
int Debug_LowLevel_Initialize(const char* external_date);
|
int Debug_ReadVarReturnType(int var_ind, int *vartype);
|
||||||
|
/* ×èòàåò çíà÷åíèå ïåðåìåííîé ñ íèæíåãî óðîâíÿ */
|
||||||
|
int Debug_LowLevel_ReadVar(int32_t *return_long);
|
||||||
|
/* Èíèöèàëèçèðóåò îòëàäêó íèæíåãî óðîâíÿ */
|
||||||
|
int Debug_LowLevel_Initialize(DateTime_t *external_date);
|
||||||
|
|
||||||
#endif //DEBUG_TOOLS
|
#endif //DEBUG_TOOLS
|
||||||
|
|||||||
23
debug_vars_example.c
Normal file
23
debug_vars_example.c
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#include "debug_tools.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Инклюды для доступа к переменным
|
||||||
|
#include "bender.h"
|
||||||
|
|
||||||
|
// Экстерны для доступа к переменным
|
||||||
|
extern int ADC0finishAddr;
|
||||||
|
|
||||||
|
|
||||||
|
// Определение массива с указателями на переменные для отладки
|
||||||
|
int DebugVar_Qnt = 5;
|
||||||
|
#pragma DATA_SECTION(dbg_vars,".dbgvar_info")
|
||||||
|
// pointer_type iq_type return_iq_type short_name
|
||||||
|
DebugVar_t dbg_vars[] = {\
|
||||||
|
{(uint8_t *)&freqTerm, pt_float, t_iq_none, t_iq10, "freqT" }, \
|
||||||
|
{(uint8_t *)&ADC_sf[0][0], pt_int16, t_iq_none, t_iq_none, "ADC_sf00" }, \
|
||||||
|
{(uint8_t *)&ADC_sf[0][1], pt_int16, t_iq_none, t_iq_none, "ADC_sf01" }, \
|
||||||
|
{(uint8_t *)&ADC_sf[0][2], pt_int16, t_iq_none, t_iq_none, "ADC_sf02" }, \
|
||||||
|
{(uint8_t *)&ADC_sf[0][3], pt_int16, t_iq_none, t_iq_none, "ADC_sf03" }, \
|
||||||
|
{(uint8_t *)&Bender[0].KOhms, pt_uint16, t_iq, t_iq10, "Bend0.KOhm" }, \
|
||||||
|
{(uint8_t *)&Bender[0].Times, pt_uint16, t_iq_none, t_iq_none, "Bend0.Time" }, \
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user