diff --git a/DebugVarEdit.exe b/DebugVarEdit.exe index f0a6f88..6f5265e 100644 Binary files a/DebugVarEdit.exe and b/DebugVarEdit.exe differ diff --git a/Src/DebugVarEdit_GUI.py b/Src/DebugVarEdit_GUI.py index 8aaff12..24eb5ef 100644 --- a/Src/DebugVarEdit_GUI.py +++ b/Src/DebugVarEdit_GUI.py @@ -7,6 +7,7 @@ import subprocess import lxml.etree as ET from generate_debug_vars import type_map, choose_type_map from enum import IntEnum +from tms_debugvar_term import _DemoWindow import threading from generate_debug_vars import run_generate import var_setup @@ -17,6 +18,7 @@ import scan_vars import myXML import time + from PySide2.QtWidgets import ( QApplication, QWidget, QTableWidget, QTableWidgetItem, QCheckBox, QComboBox, QLineEdit, QVBoxLayout, QHBoxLayout, QPushButton, @@ -148,7 +150,18 @@ class VarEditor(QWidget): self.target_menu.addAction(self.action_tms) self.target_menu.addAction(self.action_stm) + self.terminal_menu = QMenu("Открыть Терминал", menubar) + + self.action_terminal_tms = QAction("TMS DemoTerminal", self) + self.action_terminal_modbus = QAction("Modbus DemoTerminal", self) + self.action_terminal_tms.triggered.connect(lambda: self.open_terminal("TMS")) + self.action_terminal_modbus.triggered.connect(lambda: self.open_terminal("MODBUS")) + + self.terminal_menu.addAction(self.action_terminal_tms) + #self.terminal_menu.addAction(self.action_terminal_modbus) + menubar.addMenu(self.target_menu) + menubar.addMenu(self.terminal_menu) # Кнопка сохранения btn_save = QPushButton(build_title) @@ -178,7 +191,16 @@ class VarEditor(QWidget): self.setLayout(layout) + def open_terminal(self, target): + target = target.lower() + + if target == "tms": + self.terminal_widget = _DemoWindow() # _DemoWindow наследует QWidget + self.terminal_widget.show() + elif target == "modbus": + a=1 + def on_target_selected(self, target): self.target_menu.setTitle(f'МК: {target}') self.settings.setValue("mcu_choosen", target) diff --git a/Src/tms_debugvar_term.py b/Src/tms_debugvar_term.py index 7ae90b3..0326b7a 100644 --- a/Src/tms_debugvar_term.py +++ b/Src/tms_debugvar_term.py @@ -1,10 +1,12 @@ -import sys -import struct -import datetime from PySide2 import QtCore, QtWidgets, QtSerialPort -from PySide2.QtCore import QTimer +import datetime +# ------------------------------- Константы протокола ------------------------ +WATCH_SERVICE_BIT = 0x8000 +DEBUG_OK = 0 # ожидаемый код успешного чтения +SIGN_BIT_MASK = 0x80 +FRAC_MASK_FULL = 0x7F # если используем 7 бит дробной части # ---------------------------------------------------------------- CRC util --- def crc16_ibm(data: bytes, *, init=0xFFFF) -> int: """CRC16-IBM (aka CRC-16/ANSI, polynomial 0xA001 reflected).""" @@ -92,33 +94,30 @@ class Spoiler(QtWidgets.QWidget): self._ani_content.start() + +# --------------------------- DebugTerminalWidget --------------------------- 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 + nameRead = QtCore.Signal(int, int, int, str) # index, status, iq, name + valueRead = QtCore.Signal(int, int, int, int, float) # для одиночного + valuesRead = QtCore.Signal(int, int, list, list, list, list) # base, count, idx_list, iq_list, raw_list, float_list portOpened = QtCore.Signal(str) portClosed = QtCore.Signal(str) - txBytes = QtCore.Signal(bytes) # raw bytes sent - rxBytes = QtCore.Signal(bytes) # raw bytes received (frame only) + txBytes = QtCore.Signal(bytes) + rxBytes = QtCore.Signal(bytes) def __init__(self, parent=None, *, start_byte=0x0A, cmd_byte=0x44, - name_field_len=11, - signed=True, iq_scaling=None, - read_timeout_ms=200, + read_timeout_ms=250, auto_crc_check=True, drop_if_busy=False, replace_if_busy=True): super().__init__(parent) - self.start_byte = start_byte + self.device_addr = 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 @@ -127,159 +126,173 @@ class DebugTerminalWidget(QtWidgets.QWidget): iq_scaling[0] = 1.0 self.iq_scaling = iq_scaling - # Serial port --------------------------------------------------------- + # Serial 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 ------------------------------------------------------------ + # State self._rx_buf = bytearray() - self._waiting_name = False - self._expected_min_len = 0 - self._expected_exact_len = None # if known exactly + self._busy = False + self._pending_cmd = None # (frame, meta) + self._txn_meta = None # {'service':bool,'index':int,'varqnt':int,'chain':...} - # 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 + # Кэш: index -> (status, iq, name) + self._name_cache = {} + + # Очередь требуемых service индексов перед чтением блока + self._service_queue = [] # список индексов + self._pending_data_after_services = None # (base, count) + 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._connect_ui() self.set_available_ports() - # ------------------------------------------------------------------ UI --- + # ------------------------------ 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) + layout = QtWidgets.QVBoxLayout(self) + # --- Serial group --- + g_serial = QtWidgets.QGroupBox("Serial Port") + hs = QtWidgets.QHBoxLayout(g_serial) 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.addItems(["9600","19200","38400","57600","115200","230400"]) self.cmb_baud.setCurrentText("115200") self.btn_open = QtWidgets.QPushButton("Open") + hs.addWidget(QtWidgets.QLabel("Port:")) + hs.addWidget(self.cmb_port, 1) + hs.addWidget(self.btn_refresh) + hs.addSpacing(10) + hs.addWidget(QtWidgets.QLabel("Baud:")) + hs.addWidget(self.cmb_baud) + hs.addWidget(self.btn_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) + # --- Watch group (будет растягиваться) --- + g_watch = QtWidgets.QGroupBox("Watch Variables") + grid = QtWidgets.QGridLayout(g_watch) + grid.setHorizontalSpacing(8) + grid.setVerticalSpacing(4) 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.spin_count = QtWidgets.QSpinBox(); self.spin_count.setRange(1,255); self.spin_count.setValue(1) + self.btn_read_service = QtWidgets.QPushButton("Read Name/Type") + self.btn_read_values = QtWidgets.QPushButton("Read Value(s)") 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.spin_interval = QtWidgets.QSpinBox(); self.spin_interval.setRange(50,10000); self.spin_interval.setValue(500); self.spin_interval.setSuffix(" ms") + self.chk_auto_service = QtWidgets.QCheckBox("Auto service before values if miss cache"); self.chk_auto_service.setChecked(True) + self.chk_raw = QtWidgets.QCheckBox("Raw (no IQ scaling)") + self.lbl_name = QtWidgets.QLineEdit(); self.lbl_name.setReadOnly(True) self.lbl_iq = QtWidgets.QLabel("-") - self.chk_raw = QtWidgets.QCheckBox("Raw (no IQ format)") + self.edit_single_value = QtWidgets.QLineEdit(); self.edit_single_value.setReadOnly(True) - 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) + # --- Таблица: теперь 5 столбцов (если уже поменяли) --- + self.tbl_values = QtWidgets.QTableWidget(0, 5) + self.tbl_values.setHorizontalHeaderLabels(["Index","Name","IQ","Raw","Scaled"]) + self.tbl_values.setSizePolicy(QtWidgets.QSizePolicy.Expanding, + QtWidgets.QSizePolicy.Expanding) + hh = self.tbl_values.horizontalHeader() + hh.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents) + hh.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents) + hh.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents) + hh.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents) + hh.setSectionResizeMode(4, QtWidgets.QHeaderView.Stretch) - 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) + vh = self.tbl_values.verticalHeader() + vh.setVisible(False) - var_layout.addWidget(QtWidgets.QLabel("Interval:"), 2, 0) - var_layout.addWidget(self.spin_interval, 2, 1) + r = 0 + grid.addWidget(QtWidgets.QLabel("Base Index:"), r, 0) + grid.addWidget(self.spin_index, r, 1) + grid.addWidget(self.chk_hex_index, r, 2); r += 1 - var_layout.addWidget(QtWidgets.QLabel("Name:"), 3, 0) - var_layout.addWidget(self.edit_name, 3, 1, 1, 2) + grid.addWidget(QtWidgets.QLabel("Count:"), r, 0) + grid.addWidget(self.spin_count, r, 1); r += 1 - var_layout.addWidget(QtWidgets.QLabel("Value:"), 4, 0) - var_layout.addWidget(self.edit_value, 4, 1, 1, 2) + grid.addWidget(self.btn_read_service, r, 0) + grid.addWidget(self.btn_read_values, r, 1) + grid.addWidget(self.btn_poll, r, 2); r += 1 - var_layout.addWidget(QtWidgets.QLabel("IQ:"), 5, 0) - var_layout.addWidget(self.lbl_iq, 5, 1) - var_layout.addWidget(self.chk_raw, 5, 2) + grid.addWidget(QtWidgets.QLabel("Interval:"), r, 0) + grid.addWidget(self.spin_interval, r, 1) + grid.addWidget(self.chk_auto_service, r, 2); r += 1 - main_layout.addWidget(var_group) - - # --- Collapsible UART Log --- + grid.addWidget(QtWidgets.QLabel("Name:"), r, 0) + grid.addWidget(self.lbl_name, r, 1, 1, 2); r += 1 + + grid.addWidget(QtWidgets.QLabel("IQ:"), r, 0) + grid.addWidget(self.lbl_iq, r, 1) + grid.addWidget(self.chk_raw, r, 2); r += 1 + + grid.addWidget(QtWidgets.QLabel("Single:"), r, 0) + grid.addWidget(self.edit_single_value, r, 1, 1, 2); r += 1 + + grid.addWidget(QtWidgets.QLabel("Array Values:"), r, 0); r += 1 + + # --- Строка с таблицей, назначаем stretch --- + grid.addWidget(self.tbl_values, r, 0, 1, 3) + grid.setRowStretch(r, 1) # таблица тянется + # Все предыдущие строки по умолчанию имеют stretch=0 + # Можно явно grid.setRowStretch(i, 0) при желании + + # --- Добавляем группы в главный layout --- + layout.addWidget(g_serial, 0) # не растягивается (stretch=0) + layout.addWidget(g_watch, 1) # растягивается (stretch=1) + + # --- UART Log (минимальная высота) --- self.log_spoiler = Spoiler("UART Log", animationDuration=300, parent=self) + self.log_spoiler.setSizePolicy(QtWidgets.QSizePolicy.Expanding, + QtWidgets.QSizePolicy.Minimum) log_layout = QtWidgets.QVBoxLayout() self.txt_log = QtWidgets.QTextEdit() self.txt_log.setReadOnly(True) self.txt_log.setFontFamily("Courier") + self.txt_log.setSizePolicy(QtWidgets.QSizePolicy.Expanding, + QtWidgets.QSizePolicy.Minimum) log_layout.addWidget(self.txt_log) - self.log_spoiler.setContentLayout(log_layout) - main_layout.addWidget(self.log_spoiler, 1) + # Добавляем лог последним, но без stretch (0) + layout.addWidget(self.log_spoiler, 0) - 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 --- + # Строчки распределения: g_watch = 1, остальное = 0 + # Если хочешь принудительно: + # layout.setStretchFactor(g_serial, 0) # PySide2: нет прямого метода, можно: + layout.setStretch(layout.indexOf(g_serial), 0) + layout.setStretch(layout.indexOf(g_watch), 1) + layout.setStretch(layout.indexOf(self.log_spoiler), 0) + + def _connect_ui(self): + self.btn_refresh.clicked.connect(self.set_available_ports) + self.btn_open.clicked.connect(self._open_close_port) + self.btn_read_service.clicked.connect(self.request_service_single) + self.btn_read_values.clicked.connect(self.request_values) + self.btn_poll.clicked.connect(self._toggle_polling) + self.chk_hex_index.stateChanged.connect(self._toggle_index_base) + + # ----------------------------- SERIAL MGMT ---------------------------- def set_available_ports(self): - """Enumerate COM ports and repopulate combo box.""" - current = self.cmb_port.currentText() + cur = 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 cur: + ix = self.cmb_port.findText(cur) if ix >= 0: self.cmb_port.setCurrentIndex(ix) self.cmb_port.blockSignals(False) @@ -292,308 +305,395 @@ class DebugTerminalWidget(QtWidgets.QWidget): self._log(f"[PORT] Closed {name}") self.portClosed.emit(name) return - - port_name = self.cmb_port.currentText() - if not port_name: + port = self.cmb_port.currentText() + if not port: self._log("[ERR] No port selected") return - - baud = int(self.cmb_baud.currentText()) - self.serial.setPortName(port_name) - self.serial.setBaudRate(baud) + self.serial.setPortName(port) + self.serial.setBaudRate(int(self.cmb_baud.currentText())) if not self.serial.open(QtCore.QIODevice.ReadWrite): - self._log(f"[ERR] Failed to open {port_name}: {self.serial.errorString()}") + self._log(f"[ERR] Open fail {port}: {self.serial.errorString()}") return self.btn_open.setText("Close") - self._log(f"[PORT] Opened {port_name} @ {baud}") - self.portOpened.emit(port_name) + self._log(f"[PORT] Opened {port}") + self.portOpened.emit(port) - # --------------------------------------------------------- Frame build --- - def _build_request(self, index: int, read_name: bool) -> bytes: - if read_name: - dbg = 0x8000 | (index & 0x7FFF) - else: - dbg = index & 0x7FFF + # ---------------------------- FRAME BUILD ----------------------------- + def _build_request(self, index: int, *, service: bool, varqnt: int) -> bytes: + dbg = index & 0x7FFF + if service: + dbg |= WATCH_SERVICE_BIT hi = (dbg >> 8) & 0xFF lo = dbg & 0xFF - return bytes([self.start_byte & 0xFF, self.cmd_byte & 0xFF, hi, lo]) + q = varqnt & 0xFF + payload = bytes([self.device_addr & 0xFF, self.cmd_byte & 0xFF, hi, lo, q]) + crc = crc16_ibm(payload) + return payload + bytes([crc & 0xFF, (crc >> 8) & 0xFF]) - # ------------------------------- PUBLIC API (safe single outstanding) --- - def request_name(self): - self._issue_command(is_name=True) + # ----------------------------- PUBLIC API ----------------------------- + def request_service_single(self): + idx = int(self.spin_index.value()) + self._enqueue_or_start(idx, service=True, varqnt=0) - 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) + def request_values(self): + base = int(self.spin_index.value()) + count = int(self.spin_count.value()) + needed = [] + if self.chk_auto_service.isChecked(): + for i in range(base, base+count): + if i not in self._name_cache: + needed.append(i) + if needed: + self._service_queue = needed[:] # копия + self._pending_data_after_services = (base, count) + self._log(f"[AUTO] Need service for {len(needed)} indices: {needed}") + self._kick_service_queue() + else: + self._enqueue_or_start(base, service=False, varqnt=count) + # -------------------------- SERVICE QUEUE FLOW ------------------------ + def _kick_service_queue(self): if self._busy: - if self._drop_if_busy: - self._log("[LOCKSTEP] Busy -> drop new request") + return # дождёмся завершения + if self._service_queue: + nxt = self._service_queue.pop(0) + # не используем chain, просто по завершению снова вызовем _kick_service_queue + self._enqueue_or_start(nxt, service=True, varqnt=0, queue_mode=True) + elif self._pending_data_after_services: + base, count = self._pending_data_after_services + self._pending_data_after_services = None + self._enqueue_or_start(base, service=False, varqnt=count) + + # ------------------------ TRANSACTION SCHEDULER ----------------------- + def _enqueue_or_start(self, index, service: bool, varqnt: int, chain_after=None, queue_mode=False): + frame = self._build_request(index, service=service, varqnt=varqnt) + meta = {'service': service, 'index': index, 'varqnt': varqnt, 'chain': chain_after, 'queue_mode': queue_mode} + if self._busy: + if self._drop_if_busy and not self._replace_if_busy: + self._log("[LOCKSTEP] Busy -> drop") return if self._replace_if_busy: - self._pending_cmd = (frame, is_name, index) - self._log("[LOCKSTEP] Busy -> replaced pending request") + self._pending_cmd = (frame, meta) + self._log("[LOCKSTEP] Busy -> replaced pending") else: - # queue disabled; ignore - self._log("[LOCKSTEP] Busy -> ignore (no replace)") + self._log("[LOCKSTEP] Busy -> ignore") return + self._start_txn(frame, meta) - # 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.""" + def _start_txn(self, frame: bytes, meta: dict): 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._txn_meta = meta 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.""" + def _end_txn(self): self._txn_timer.stop() + queue_mode = False + chain = None + if self._txn_meta: + queue_mode = self._txn_meta.get('queue_mode', False) + chain = self._txn_meta.get('chain') + self._txn_meta = None 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 + + # Если был chain -> запустить его + if chain: + base, serv, q = chain + self._enqueue_or_start(base, service=serv, varqnt=q) + return + if self._pending_cmd is not None: - frame, is_name, index = self._pending_cmd + frame, meta = 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)) + QtCore.QTimer.singleShot(0, lambda f=frame,m=meta: self._start_txn(f,m)) + return + + # Если это был элемент очереди service -> continue + if queue_mode: + QtCore.QTimer.singleShot(0, self._kick_service_queue) + return 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 + self._log("[TIMEOUT] No response") if self._rx_buf: self._log_frame(bytes(self._rx_buf), tx=False) - self._end_transaction() + self._end_txn() - # --------------------------------------------------------------- TX/RX --- + # ------------------------------- 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)}") + w = self.serial.write(data) + if w != len(data): + self._log(f"[ERR] Write short {w}/{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 not self._busy: if self._rx_buf: - self._log("[WARN] Extra RX bytes after frame -> stash for next txn") - self._end_transaction() + self._log("[WARN] Data while idle -> drop") + self._log_frame(bytes(self._rx_buf), tx=False) + self._rx_buf.clear() return + self._try_parse() - # 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) + # ------------------------------- PARSING ------------------------------- + def _try_parse(self): + if not self._txn_meta: + return + service = self._txn_meta['service'] + buf = self._rx_buf + trailer_len = 4 + if service: + if len(buf) < 7 + trailer_len: # adr cmd vhi vlo status iq nameLen + trailer + return + name_len = buf[6] + expected = 7 + name_len + trailer_len + if len(buf) < expected: + return + frame = bytes(buf[:expected]) + del buf[:expected] self.rxBytes.emit(frame) self._log_frame(frame, tx=False) - self._parse_response(frame) - self._end_transaction() + self._parse_service_frame(frame) + self._end_txn() + else: + if len(buf) < 6 + trailer_len: # adr cmd vhi vlo varqnt status + trailer + return + varqnt = buf[4] + status = buf[5] + if status != DEBUG_OK: + expected = 8 + trailer_len # + errIndex(2) + if len(buf) < expected: + return + frame = bytes(buf[:expected]) + del buf[:expected] + self.rxBytes.emit(frame) + self._log_frame(frame, tx=False) + self._parse_data_frame(frame, error_mode=True) + self._end_txn() + else: + expected = 6 + varqnt*2 + trailer_len + if len(buf) < expected: + return + frame = bytes(buf[:expected]) + del buf[:expected] + self.rxBytes.emit(frame) + self._log_frame(frame, tx=False) + self._parse_data_frame(frame, error_mode=False) + self._end_txn() + + def _check_crc(self, payload: bytes, crc_lo: int, crc_hi: int): + if not self.auto_crc_check: + return True + crc_rx = (crc_hi << 8) | crc_lo + 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}") + return False + self._log("[CRC OK]") + return True + + @staticmethod + def _clear_service_bit(vhi, vlo): + return ((vhi & 0x7F) << 8) | vlo + + def _parse_service_frame(self, frame: bytes): + payload = frame[:-4] + crc_lo, crc_hi = frame[-4], frame[-3] + if len(payload) < 7: + self._log("[ERR] Service frame too short") return - # else: wait for more data or timeout + self._check_crc(payload, crc_lo, crc_hi) + adr, cmd, vhi, vlo, status, iq_raw, name_len = payload[:7] + index = self._clear_service_bit(vhi, vlo) + if len(payload) < 7 + name_len: + self._log("[ERR] Service name truncated") + return + name_bytes = payload[7:7+name_len] + name = name_bytes.decode(errors='replace') + + # ### PATCH: извлекаем признаки + is_signed = (iq_raw & SIGN_BIT_MASK) != 0 + frac_bits = iq_raw & FRAC_MASK_FULL + + if status == DEBUG_OK: + # Кэшируем расширенный кортеж + self._name_cache[index] = (status, iq_raw, name, is_signed, frac_bits) + + self.nameRead.emit(index, status, iq_raw, name) + + if self.spin_count.value() == 1 and index == self.spin_index.value(): + if status == DEBUG_OK: + self.lbl_name.setText(name) + # Отображаем IQ как «число_дробных_бит + s/u» + self.lbl_iq.setText(f"{frac_bits}{'s' if is_signed else 'u'}") + else: + self.lbl_name.setText('') + self.lbl_iq.setText('-') + + self._log(f"[SERVICE] idx={index} status={status} iq_raw=0x{iq_raw:02X} " + f"sign={'S' if is_signed else 'U'} frac={frac_bits} name='{name}'") + + def _parse_data_frame(self, frame: bytes, *, error_mode: bool): + payload = frame[:-4] + crc_lo, crc_hi = frame[-4], frame[-3] + if len(payload) < 6: + self._log("[ERR] Data frame too short") + return + self._check_crc(payload, crc_lo, crc_hi) + adr, cmd, vhi, vlo, varqnt, status = payload[:6] + base = self._clear_service_bit(vhi, vlo) + if error_mode: + if len(payload) < 8: + self._log("[ERR] Error frame truncated") + return + err_hi, err_lo = payload[6:8] + bad_index = (err_hi << 8) | err_lo + self._log(f"[DATA] ERROR status={status} bad_index={bad_index}") + self.valueRead.emit(bad_index, status, 0, 0, float('nan')) + self.valuesRead.emit(base, 0, [], [], [], []) + return + + # Нормальный кадр + if len(payload) < 6 + varqnt*2: + self._log("[ERR] Data payload truncated") + return + raw_vals = [] + pos = 6 + for _ in range(varqnt): + hi = payload[pos]; lo = payload[pos+1]; pos += 2 + raw16 = (hi << 8) | lo + raw_vals.append(raw16) # пока храним как 0..65535 + + idx_list = [] + iq_list = [] + name_list = [] + scaled_list = [] + display_raw_list = [] # для таблицы Raw (с учётом знака, если знак есть) + + for ofs, raw16 in enumerate(raw_vals): + idx = base + ofs + # В кэше теперь 5 элементов + status_i, iq_raw, name_i, is_signed, frac_bits = self._name_cache.get( + idx, + (DEBUG_OK, 0, '', False, 0) + ) + + # Приведение знака + if is_signed and (raw16 & 0x8000): + value_int = raw16 - 0x10000 # signed 16-bit + else: + value_int = raw16 + + # Масштаб + if self.chk_raw.isChecked(): + scale = 1.0 + else: + # scale берём: если в словаре нет — вычисляем 2**frac_bits + scale = self.iq_scaling.get(frac_bits, 2.0 ** frac_bits) + + scaled = value_int / scale + + idx_list.append(idx) + iq_list.append(iq_raw) # сырой байт (с битом знака) + name_list.append(name_i) + scaled_list.append(scaled) + display_raw_list.append(value_int) + + # Populate table + self._populate_table(idx_list, name_list, iq_list, display_raw_list, scaled_list) + + if varqnt == 1: + self.edit_single_value.setText( + str(display_raw_list[0]) if self.chk_raw.isChecked() else f"{scaled_list[0]:.6g}" + ) + if idx_list[0] == self.spin_index.value(): + # Отобразим мета + # Достаём из кэша снова (или можно из name_list/iq_list + пересчитать) + _, iq_raw0, name0, is_signed0, frac0 = self._name_cache.get(idx_list[0], (DEBUG_OK, 0, '', False, 0)) + self.lbl_name.setText(name0) + self.lbl_iq.setText(f"{frac0}{'s' if is_signed0 else 'u'}") + self.valueRead.emit(idx_list[0], status, iq_list[0], display_raw_list[0], scaled_list[0]) + else: + self.edit_single_value.setText("") + self.valuesRead.emit(base, varqnt, idx_list, iq_list, display_raw_list, scaled_list) + + self._log(f"[DATA] base={base} q={varqnt} values={[f'{v:.6g}' for v in scaled_list] if not self.chk_raw.isChecked() else raw_vals}") + + def _populate_table(self, idxs, names, iqs, raws, scaled): + self.tbl_values.setRowCount(len(idxs)) + for row, (idx, nm, iq_raw, rv, sv) in enumerate(zip(idxs, names, iqs, raws, scaled)): + is_signed = (iq_raw & SIGN_BIT_MASK) != 0 + frac_bits = iq_raw & FRAC_MASK_FULL + iq_disp = f"{frac_bits}{'s' if is_signed else 'u'}" + raw_display = str(rv) + scaled_display = raw_display if self.chk_raw.isChecked() else f"{sv:.6g}" + items = [ + QtWidgets.QTableWidgetItem(str(idx)), + QtWidgets.QTableWidgetItem(nm), + QtWidgets.QTableWidgetItem(iq_disp), + QtWidgets.QTableWidgetItem(raw_display), + QtWidgets.QTableWidgetItem(scaled_display), + ] + for it in items: + it.setFlags(it.flags() & ~QtCore.Qt.ItemIsEditable) + for c, it in enumerate(items): + self.tbl_values.setItem(row, c, it) + + # ------------------------------ POLLING -------------------------------- + def _toggle_polling(self): + if self._polling: + self._poll_timer.stop(); self._polling=False; self.btn_poll.setText("Start Polling"); self._log("[POLL] Stopped") + self.btn_read_service.setEnabled(True) + self.btn_read_values.setEnabled(True) + else: + self._poll_timer.start(self.spin_interval.value()); self._polling=True; self.btn_poll.setText("Stop Polling"); self._log(f"[POLL] Started interval={self.spin_interval.value()}ms") + self.btn_read_service.setEnabled(False) + self.btn_read_values.setEnabled(False) + + def _on_poll_timeout(self): + if not self.serial.isOpen() or self._busy: + return + self.request_values() + + # ------------------------------ HELPERS -------------------------------- + def _toggle_index_base(self, st): + val = self.spin_index.value() + if st == QtCore.Qt.Checked: + self.spin_index.setDisplayIntegerBase(16); self.spin_index.setPrefix("0x") + else: + self.spin_index.setDisplayIntegerBase(10); self.spin_index.setPrefix("") + self.spin_index.setValue(val) + + def _set_ui_busy(self, busy: bool): + if self._polling == False: + self.btn_read_service.setEnabled(not busy) + self.btn_read_values.setEnabled(not busy) 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)''' + self._end_txn() + # ------------------------------ LOGGING -------------------------------- def _log(self, msg: str): - ts = datetime.datetime.now().strftime("%H:%M:%S.%f")[:-3] + 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}|") - + tag = 'TX' if tx else 'RX' + hexs = ' '.join(f"{b:02X}" for b in data) + ascii_part = ''.join(chr(b) if 32 <= b < 127 else '.' for b in data) + self._log(f"[{tag}] {hexs} |{ascii_part}|") # ---------------------------------------------------------- Demo harness --- class _DemoWindow(QtWidgets.QMainWindow): @@ -611,10 +711,18 @@ class _DemoWindow(QtWidgets.QMainWindow): def _on_value(self, index, status, iq, raw16, floatVal): print(f"Value idx={index} status={status} iq={iq} raw={raw16} val={floatVal}") - - -# ----------------------------------------------------------------- main --- + + def closeEvent(self, event): + """Вызывается при закрытии окна.""" + # Явно удаляем центральный виджет + self.setCentralWidget(None) + if self.term: + self.term.deleteLater() + self.term = None + super().closeEvent(event) +# ------------------------------- Demo -------------------------------------- if __name__ == "__main__": + import sys app = QtWidgets.QApplication(sys.argv) win = _DemoWindow() win.show() diff --git a/Src/var_table.py b/Src/var_table.py index 3c2337a..e427bfb 100644 --- a/Src/var_table.py +++ b/Src/var_table.py @@ -253,6 +253,8 @@ class VariableTableWidget(QTableWidget): ret_combo = CtrlScrollComboBox() ret_combo.addItems(self.iq_types) value = var['return_type'].replace('t_', '') + if value not in self.iq_types: + ret_combo.addItem(value) ret_combo.setCurrentText(value) ret_combo.currentTextChanged.connect(on_change_callback) ret_combo.setStyleSheet(style_with_padding) @@ -375,10 +377,9 @@ class VariableTableWidget(QTableWidget): combo.clear() combo.addItems(allowed_items) - if current in allowed_items: - combo.setCurrentText(current) - else: - combo.setCurrentIndex(0) + if current not in allowed_items: + combo.addItem(current) + combo.setCurrentText(current) combo.blockSignals(False) diff --git a/debug_tools.c b/debug_tools.c index 1251cf9..7a3a7df 100644 --- a/debug_tools.c +++ b/debug_tools.c @@ -26,7 +26,7 @@ void Debug_Test_Example(void) { return; result = Debug_ReadVar(var_numb, &return_var); - result = Debug_ReadVarName(var_numb, var_name); + result = Debug_ReadVarName(var_numb, var_name, 0); if(Debug_LowLevel_Initialize(&ext_date) == 0) @@ -47,39 +47,76 @@ int Debug_ReadVar(int var_ind, int32_t *return_32b) int32_t tmp_var; if(return_32b == NULL) - return 1; + return DEBUG_ERR_INTERNAL; if (var_ind >= DebugVar_Qnt) - return 1; + return DEBUG_ERR_VAR_NUMB; 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; + return DEBUG_ERR_INVALID_VAR; return convertDebugVarToIQx(&dbg_vars[var_ind], return_32b); } /** - * @brief . + * @brief (IQ) . * @param var_ind . * @param vartype . * @return int 0: , 1: . - * @details . + * @details (IQ) . */ int Debug_ReadVarReturnType(int var_ind, int *vartype) { int rettype; if(vartype == NULL) - return 1; + return DEBUG_ERR_INTERNAL; if (var_ind >= DebugVar_Qnt) - return 1; + return DEBUG_ERR_VAR_NUMB; 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; + return DEBUG_ERR_INVALID_VAR; *vartype = iqTypeToQ(dbg_vars[var_ind].return_type); return 0; } +/** + * @brief . + * @param var_ind . + * @param vartype . + * @return int 0: , 1: . + * @details . + */ +int Debug_ReadVarType(int var_ind, int *vartype) +{ + int rettype; + if(vartype == NULL) + return DEBUG_ERR_INTERNAL; + if (var_ind >= DebugVar_Qnt) + return DEBUG_ERR_VAR_NUMB; + 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 DEBUG_ERR_INVALID_VAR; + + *vartype = dbg_vars[var_ind].ptr_type; + + switch(dbg_vars[var_ind].ptr_type) + { + case pt_int8: + case pt_int16: + case pt_int32: + case pt_float: + *vartype = dbg_vars[var_ind].ptr_type | DEBUG_SIGNED_VAR; + break; + + default: + *vartype = dbg_vars[var_ind].ptr_type; + break; + } + + return 0; +} + /** * @brief . * @param var_ind . @@ -87,22 +124,26 @@ int Debug_ReadVarReturnType(int var_ind, int *vartype) * @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 *length) { int i; if(name_ptr == NULL) - return 1; + return DEBUG_ERR_INTERNAL; if (var_ind >= DebugVar_Qnt) - return 1; + return DEBUG_ERR_VAR_NUMB; // '\0' for (i = 0; i < sizeof(dbg_vars[var_ind].name); i++) { name_ptr[i] = dbg_vars[var_ind].name[i]; if (dbg_vars[var_ind].name[i] == '\0') + { + if(length != NULL) + *length = i; break; + } } // ( , var->name '\0') name_ptr[sizeof(dbg_vars[var_ind].name) - 1] = '\0'; @@ -123,9 +164,9 @@ int Debug_LowLevel_ReadVar(int32_t *return_32b) uint32_t addr_val = (uint32_t)addr; if (return_32b == NULL) - return 1; + return DEBUG_ERR_INTERNAL; if (debug_ll.isVerified == 0) - return 1; + return DEBUG_ERR_DATATIME; // ( .cmd ) @@ -139,7 +180,7 @@ int Debug_LowLevel_ReadVar(int32_t *return_32b) (addr_val >= 0x100002 && addr_val <= 0x103FFF) || // RAMEX0 + RAMEX2 + RAMEX01 (addr_val >= 0x102000 && addr_val <= 0x103FFF) // RAMEX2 )) { - return 2; // + return DEBUG_ERR_ADDR; // } return convertDebugVarToIQx(&debug_ll.dbg_var, return_32b); @@ -154,7 +195,7 @@ int Debug_LowLevel_ReadVar(int32_t *return_32b) int Debug_LowLevel_Initialize(DateTime_t* external_date) { if (external_date == NULL) { - return -1; + return DEBUG_ERR_INTERNAL; } @@ -170,7 +211,7 @@ int Debug_LowLevel_Initialize(DateTime_t* external_date) } debug_ll.isVerified = 0; - return 1; // + return DEBUG_ERR_DATATIME; // } @@ -193,7 +234,7 @@ static int iqTypeToQ(DebugVarIQType_t t) else if (t >= t_iq1 && t <= t_iq30) return (int)t - (int)t_iq1 + 1; // t_iq1 -> 1, t_iq2 -> 2 .. else - return -1; // + return 0; // } /** @@ -208,19 +249,17 @@ static int convertDebugVarToIQx(DebugVar_t *var, int32_t *ret_var) int32_t iq_numb, iq_united, iq_final; int64_t iq_united64 = 0; int64_t iq_final64 = 0; + int status; float float_numb; - if(getDebugVar(var, &iq_numb, &float_numb) != 0) - return 1; + status = getDebugVar(var, &iq_numb, &float_numb); + if(status != 0) + return status; int src_q = iqTypeToQ(var->iq_type); int dst_q = iqTypeToQ(var->return_type); - if (src_q < 0 || dst_q < 0) - return 2; // - - // GLOBAL_Q (64-) if (var->iq_type == t_iq_none) { if (var->ptr_type == pt_float) { @@ -249,10 +288,6 @@ static int convertDebugVarToIQx(DebugVar_t *var, int32_t *ret_var) else iq_final64 = iq_united64 >> (-shift); -// // int32_t -// if (iq_final64 > 2147483647 || iq_final64 < -2147483648) -// return 3; // - *ret_var = (int32_t)iq_final64; } @@ -273,51 +308,51 @@ static int getDebugVar(DebugVar_t *var, int32_t *int_var, float *float_var) uint32_t addr_val = (uint32_t)addr; if (!var || !int_var || !float_var || !var->Ptr) - return 1; // : null + return DEBUG_ERR_INTERNAL; // : null switch (var->ptr_type) { case pt_int8: // 8 if ((addr_val & ALIGN_8BIT) != 0) // - return 1; // + return DEBUG_ERR_ADDR_ALIGN; // *int_var = *((volatile int8_t *)addr); break; case pt_uint8: if ((addr_val & ALIGN_8BIT) != 0) // - return 1; // + return DEBUG_ERR_ADDR_ALIGN; // *int_var = *((volatile uint8_t *)addr); break; case pt_int16: // 16 (int) if ((addr_val & ALIGN_16BIT) != 0) // - return 2; // + return DEBUG_ERR_ADDR_ALIGN; // *int_var = *((volatile int16_t *)addr); break; case pt_uint16: if ((addr_val & ALIGN_16BIT) != 0) // - return 2; // + return DEBUG_ERR_ADDR_ALIGN; // *int_var = *((volatile uint16_t *)addr); break; case pt_int32: // 32 if ((addr_val & ALIGN_32BIT) != 0) // - return 3; // + return DEBUG_ERR_ADDR_ALIGN; // *int_var = *((volatile int32_t *)addr); break; case pt_uint32: if ((addr_val & ALIGN_32BIT) != 0) // - return 3; // + return DEBUG_ERR_ADDR_ALIGN; // *int_var = *((volatile uint32_t *)addr); break; case pt_float: // float (4 ) if ((addr_val & ALIGN_FLOAT) != 0) // - return 4; // + return DEBUG_ERR_ADDR_ALIGN; // *float_var = *((volatile float *)addr); break; default: - return 1; // + return DEBUG_ERR_INVALID_VAR; // // // case pt_ptr_int8: // case pt_ptr_int16: diff --git a/debug_tools.h b/debug_tools.h index f037da2..10f51db 100644 --- a/debug_tools.h +++ b/debug_tools.h @@ -31,6 +31,18 @@ #define NULL 0 #endif +#define DEBUG_SIGNED_VAR (1<<7) + +#define DEBUG_OK (0) +#define DEBUG_ERR (1<<7) +#define DEBUG_ERR_VAR_NUMB (1<<0) | DEBUG_ERR +#define DEBUG_ERR_INVALID_VAR (1<<1) | DEBUG_ERR +#define DEBUG_ERR_ADDR (1<<2) | DEBUG_ERR +#define DEBUG_ERR_ADDR_ALIGN (1<<3) | DEBUG_ERR +#define DEBUG_ERR_INTERNAL (1<<4) | DEBUG_ERR +#define DEBUG_ERR_DATATIME (1<<5) | DEBUG_ERR + + /** * @brief , . @@ -159,9 +171,11 @@ void Debug_Test_Example(void); /* */ 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 *length); +/* (IQ) */ int Debug_ReadVarReturnType(int var_ind, int *vartype); +/* */ +int Debug_ReadVarType(int var_ind, int *vartype); /* */ int Debug_LowLevel_ReadVar(int32_t *return_long); /* */