203 lines
7.7 KiB
Python
203 lines
7.7 KiB
Python
import sys
|
||
import serial.tools.list_ports
|
||
from PySide2.QtWidgets import *
|
||
from PySide2.QtCore import *
|
||
from PySide2.QtGui import *
|
||
|
||
from rawprotocol import RawProtocol, RawProtocolWidget
|
||
from serialbus import SerialBus, SerialTab
|
||
|
||
|
||
|
||
class MainWindow(QMainWindow):
|
||
"""Главное окно приложения"""
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
# Создаем низкоуровневый протокол
|
||
self.raw_protocol = RawProtocol()
|
||
# Создаем высокоуровневый протокол, передавая ему low-level
|
||
self.serial_bus = SerialBus(self.raw_protocol)
|
||
self.init_ui()
|
||
|
||
def init_ui(self):
|
||
self.setWindowTitle("Терминал")
|
||
self.setGeometry(100, 100, 800, 600)
|
||
|
||
# Главный виджет и компоновка
|
||
main_widget = QWidget()
|
||
main_layout = QVBoxLayout()
|
||
|
||
# Центральный виджет с вкладками
|
||
self.tab_widget = QTabWidget()
|
||
|
||
# Виджет для RAW протокола (всегда внизу)
|
||
self.raw_widget = RawProtocolWidget(self.raw_protocol)
|
||
|
||
# Вкладка для серийной шины
|
||
self.serial_tab = SerialTab(self.serial_bus, self.raw_protocol, self.raw_widget)
|
||
self.tab_widget.addTab(self.serial_tab, "Серийная шина")
|
||
|
||
main_layout.addWidget(self.tab_widget)
|
||
|
||
main_layout.addWidget(self.raw_widget)
|
||
|
||
main_widget.setLayout(main_layout)
|
||
self.setCentralWidget(main_widget)
|
||
|
||
# Создание меню
|
||
self.create_menu()
|
||
|
||
# Создание панели инструментов
|
||
self.create_toolbar()
|
||
|
||
def create_menu(self):
|
||
menubar = self.menuBar()
|
||
|
||
# Меню Файл
|
||
file_menu = menubar.addMenu("Файл")
|
||
|
||
exit_action = QAction("Выход", self)
|
||
exit_action.triggered.connect(self.close)
|
||
file_menu.addAction(exit_action)
|
||
|
||
# Меню Помощь
|
||
help_menu = menubar.addMenu("Помощь")
|
||
|
||
protocol_action = QAction("Информация о протоколе", self)
|
||
protocol_action.triggered.connect(self.show_protocol_info)
|
||
help_menu.addAction(protocol_action)
|
||
|
||
def create_toolbar(self):
|
||
toolbar = self.addToolBar("Подключение")
|
||
|
||
# Выбор COM порта
|
||
toolbar.addWidget(QLabel("COM порт:"))
|
||
|
||
self.port_combo = QComboBox()
|
||
self.port_combo.setMinimumWidth(150)
|
||
self.refresh_ports()
|
||
toolbar.addWidget(self.port_combo)
|
||
|
||
# Кнопка обновления портов
|
||
refresh_btn = QToolButton()
|
||
refresh_btn.setText("⟳")
|
||
refresh_btn.setToolTip("Обновить список портов")
|
||
refresh_btn.clicked.connect(self.refresh_ports)
|
||
toolbar.addWidget(refresh_btn)
|
||
|
||
# Кнопка подключения/отключения
|
||
self.connect_btn = QPushButton("Подключить")
|
||
self.connect_btn.clicked.connect(self.toggle_connection)
|
||
toolbar.addWidget(self.connect_btn)
|
||
|
||
# Индикатор статуса
|
||
self.status_label = QLabel("Отключено")
|
||
self.status_label.setStyleSheet("color: red;")
|
||
toolbar.addWidget(self.status_label)
|
||
|
||
toolbar.addSeparator()
|
||
|
||
# Настройки скорости
|
||
toolbar.addWidget(QLabel("Скорость:"))
|
||
self.baud_combo = QComboBox()
|
||
self.baud_combo.addItems(["9600", "19200", "38400", "57600", "115200", "230400", "460800", "921600"])
|
||
self.baud_combo.setCurrentText("115200")
|
||
toolbar.addWidget(self.baud_combo)
|
||
|
||
def refresh_ports(self):
|
||
"""Обновление списка доступных COM портов"""
|
||
self.port_combo.clear()
|
||
ports = serial.tools.list_ports.comports()
|
||
for port in ports:
|
||
self.port_combo.addItem(f"{port.device} - {port.description}")
|
||
|
||
if not ports:
|
||
self.port_combo.addItem("Порты не найдены")
|
||
|
||
def toggle_connection(self):
|
||
"""Подключение/отключение от COM порта"""
|
||
if self.raw_protocol.is_connected:
|
||
# Отключение
|
||
self.raw_protocol.disconnect()
|
||
self.connect_btn.setText("Подключить")
|
||
self.status_label.setText("Отключено")
|
||
self.status_label.setStyleSheet("color: red;")
|
||
|
||
# Блокировка элементов управления
|
||
self.serial_tab.bus_write_btn.setEnabled(False)
|
||
self.serial_tab.bus_read_btn.setEnabled(False)
|
||
self.raw_widget.raw_send_btn.setEnabled(False)
|
||
self.raw_widget.raw_read_btn.setEnabled(False)
|
||
else:
|
||
# Подключение
|
||
port_text = self.port_combo.currentText()
|
||
if " - " in port_text:
|
||
port_name = port_text.split(" - ")[0]
|
||
else:
|
||
port_name = port_text
|
||
|
||
try:
|
||
baudrate = int(self.baud_combo.currentText())
|
||
if self.raw_protocol.connect(port_name, baudrate):
|
||
self.connect_btn.setText("Отключить")
|
||
self.status_label.setText("Подключено")
|
||
self.status_label.setStyleSheet("color: green;")
|
||
|
||
# Разблокировка элементов управления
|
||
self.serial_tab.bus_write_btn.setEnabled(True)
|
||
self.serial_tab.bus_read_btn.setEnabled(True)
|
||
self.raw_widget.raw_send_btn.setEnabled(True)
|
||
self.raw_widget.raw_read_btn.setEnabled(True)
|
||
else:
|
||
QMessageBox.warning(self, "Ошибка", "Не удалось подключиться к порту")
|
||
except Exception as e:
|
||
QMessageBox.warning(self, "Ошибка", f"Ошибка подключения: {e}")
|
||
|
||
def show_protocol_info(self):
|
||
"""Показать информацию о протоколе"""
|
||
info = """
|
||
ИНФОРМАЦИЯ О ПРОТОКОЛЕ:
|
||
|
||
На основе кода контроллера:
|
||
|
||
1. ФОРМАТ СООБЩЕНИЙ:
|
||
[адрес][команда][данные][CRC16-IBM]
|
||
|
||
2. КЛЮЧЕВЫЕ АДРЕСА:
|
||
- 0x200A: Регистр данных шины (запись)
|
||
- 0x200B: Регистр управления шиной
|
||
- 0x200F: Регистр данных шины (чтение)
|
||
|
||
3. ПРОТОКОЛ ШИНЫ:
|
||
ЗАПИСЬ:
|
||
1. POKE 0x200A <данные>
|
||
2. POKE 0x200B 0x80A0 | (адрес << 4)
|
||
|
||
ЧТЕНИЕ:
|
||
1. POKE 0x200B 0x00A0 | (адрес << 4)
|
||
2. PEEK 0x200F
|
||
|
||
4. ЧТО НУЖНО УТОЧНИТЬ:
|
||
- Точные коды команд CMD_RS232_PEEK и CMD_RS232_POKE
|
||
- Длину полей в сообщениях
|
||
- Формат ответов от контроллера
|
||
"""
|
||
|
||
QMessageBox.information(self, "Информация о протоколе", info)
|
||
|
||
|
||
def main():
|
||
app = QApplication(sys.argv)
|
||
|
||
# Установка стиля
|
||
app.setStyle("Fusion")
|
||
|
||
window = MainWindow()
|
||
window.show()
|
||
|
||
sys.exit(app.exec_())
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main() |