Advertisement
Week045

Untitled

Nov 22nd, 2023
1,059
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 13.37 KB | None | 0 0
  1. import sys
  2. import serial
  3. from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QComboBox, QLabel, QLineEdit, QTextEdit, QMessageBox, \
  4.     QPushButton
  5. from PyQt5.QtCore import QThread, pyqtSignal, Qt, QTimer
  6. from PyQt5.QtGui import QFont, QTextCursor, QTextCharFormat
  7. from PyQt5 import QtCore
  8.  
  9.  
  10. class SerialDataReader(QThread):
  11.     data_received = pyqtSignal(bytes)
  12.  
  13.     def __init__(self, serial_port):
  14.         super().__init__()
  15.         self.serial_port = serial_port
  16.         self.running = True
  17.  
  18.     def stop(self):
  19.         self.running = False
  20.  
  21.     def run(self):
  22.         while self.running:
  23.             if self.should_emit_data():
  24.                 data = self.read_data()
  25.                 self.data_received.emit(data)
  26.  
  27.     def should_emit_data(self):
  28.         return self.serial_port and self.serial_port.is_open
  29.  
  30.     def read_data(self):
  31.         return self.serial_port.read(self.serial_port.in_waiting)
  32.  
  33.  
  34. class TextEditCustom(QTextEdit):
  35.     def __init__(self, *args, **kwargs):
  36.         super().__init__(*args, **kwargs)
  37.         self.cursor_position = QTextCursor.End
  38.  
  39.     def keyPressEvent(self, event):
  40.         if event.key() == Qt.Key_Backspace:
  41.             event.ignore()
  42.         else:
  43.             super().keyPressEvent(event)
  44.             self.move_cursor_to_end()
  45.  
  46.     def move_cursor_to_end(self):
  47.         cursor = self.textCursor()
  48.         cursor.movePosition(QTextCursor.End)
  49.         self.setTextCursor(cursor)
  50.  
  51.  
  52. def serial_ports():
  53.     ports = ['COM%s' % (i + 1) for i in range(9)]
  54.     result = []
  55.     for port in ports:
  56.         try:
  57.             s = serial.Serial(port, 9600)
  58.             s.close()
  59.             result.append(port[3::])
  60.         except (OSError, serial.SerialException):
  61.             pass
  62.     return result
  63.  
  64.  
  65. class SerialCommunicationWindow(QMainWindow):
  66.     error_occurred = pyqtSignal()
  67.  
  68.     def __init__(self):
  69.         super().__init__()
  70.  
  71.         self.serial_port = None
  72.         self.serial_baudrate = 9600
  73.         self.serial_stopbits = serial.STOPBITS_ONE
  74.         self.serial_data_bits = serial.EIGHTBITS
  75.         self.serial_parity = serial.PARITY_NONE
  76.         self.serial_flow_control = False
  77.         self.serial_read_timeout = None
  78.         self.serial_write_timeout = None
  79.  
  80.         font = QFont("Times New Roman", 12)
  81.  
  82.         app = QApplication.instance()
  83.         if not app:
  84.             app = QApplication([])
  85.  
  86.         app.setFont(font)
  87.  
  88.         self.resize(609, 530)
  89.         self.setMinimumSize(1000, 530)
  90.         self.setMaximumSize(1001, 530)
  91.         # self.setFixedSize(QSize(609, 530))
  92.         # self.setFixedWidth(609)
  93.         # self.setFixedHeight(530)
  94.  
  95.         self.central_widget = QWidget(self)
  96.         self.setCentralWidget(self.central_widget)
  97.         self.central_widget.setStyleSheet("background-color: #73B1F4;")
  98.  
  99.         self.input_label = QLabel('Input:', self.central_widget)
  100.         self.input_label.setGeometry(QtCore.QRect(30, 10, 121, 21))
  101.         self.input_label.setFont(font)
  102.         self.input_label.setAlignment(Qt.AlignCenter)  # выравнивание по центру
  103.         self.input_label.setGeometry(QtCore.QRect(30, 10, 351, 21))
  104.         self.input_message = TextEditCustom(self.central_widget)
  105.         self.input_message.setGeometry(QtCore.QRect(30, 50, 351, 161))
  106.         self.input_message.setFont(font)
  107.         self.input_message.setStyleSheet("background-color: lightBlue;")
  108.  
  109.         self.output_label = QLabel('Output:', self.central_widget)
  110.         self.output_label.setGeometry(QtCore.QRect(30, 300, 121, 21))
  111.         self.output_label.setFont(font)
  112.         self.output_label.setAlignment(Qt.AlignCenter)
  113.         self.output_label.setGeometry(QtCore.QRect(30, 300, 351, 21))
  114.         self.output_message = QTextEdit(self.central_widget)
  115.         self.output_message.setReadOnly(True)
  116.         self.output_message.setGeometry(QtCore.QRect(30, 340, 351, 161))
  117.         self.output_message.setFont(font)
  118.         self.output_message.setStyleSheet("background-color: lightBlue;")
  119.  
  120.         self.com_port_label = QLabel('COM port:', self.central_widget)
  121.         self.com_port_label.setGeometry(QtCore.QRect(410, 50, 110, 21))
  122.         self.com_port_label.setFont(font)
  123.         self.com_port_input = QLineEdit(self.central_widget)
  124.         self.com_port_input.setGeometry(QtCore.QRect(520, 50, 50, 22))
  125.         self.com_port_input.setStyleSheet("background-color: lightBlue;")
  126.  
  127.         self.stopbits_label = QLabel('Stop bits:', self.central_widget)
  128.         self.stopbits_label.setGeometry(QtCore.QRect(410, 90, 110, 21))
  129.         self.stopbits_label.setFont(font)
  130.         self.stopbits_input = QComboBox(self.central_widget)
  131.         self.stopbits_input.setGeometry(QtCore.QRect(520, 90, 60, 22))
  132.         self.stopbits_input.addItems(['1', '1.5', '2'])
  133.         self.stopbits_input.setStyleSheet("background-color: lightBlue;")
  134.  
  135.         self.bytes_received_label = QLabel('Bytes received:', self.central_widget)
  136.         self.bytes_received_label.setGeometry(QtCore.QRect(410, 370, 220, 21))
  137.         self.bytes_received_label.setFont(font)
  138.         self.bytes_received_value = QLabel('0', self.central_widget)
  139.         self.bytes_received_value.setGeometry(QtCore.QRect(537, 370, 230, 21))
  140.  
  141.         self.baudrate_label = QLabel('Baudrate:', self.central_widget)
  142.         self.baudrate_label.setGeometry(QtCore.QRect(410, 340, 120, 21))
  143.         self.baudrate_label.setFont(font)
  144.         self.baudrate_value = QLabel(str(self.serial_baudrate), self.central_widget)
  145.         self.baudrate_value.setGeometry(QtCore.QRect(500, 340, 130, 21))
  146.  
  147.         self.kadr_label = QLabel('Packet structure:', self.central_widget)
  148.         self.kadr_label.setGeometry(QtCore.QRect(410, 400, 140, 21))
  149.         self.kadr_window = QTextEdit(self.central_widget)
  150.         self.kadr_window.setGeometry(QtCore.QRect(410, 430, 560, 70))
  151.         # self.output_message.setGeometry(QtCore.QRect(30, 340, 351, 161))
  152.         self.kadr_window.setReadOnly(True)
  153.         self.kadr_window.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
  154.         self.kadr_window.setStyleSheet("background-color: lightBlue;")
  155.  
  156.         clear_button = QPushButton('Clear', self.central_widget)
  157.         clear_button.setGeometry(150, 240, 110, 30)
  158.         clear_button.setFont(font)
  159.         clear_button.clicked.connect(self.clear_text_areas)
  160.         clear_button.setStyleSheet("background-color: lightBlue;")
  161.  
  162.         self.keyword_combinations = ["kw2", "kw1"]
  163.  
  164.         self.available_com_ports = serial_ports()
  165.         self.last_used_port_index = 0
  166.         self.previous_port = None
  167.         self.user_ports = []
  168.  
  169.         if self.available_com_ports:
  170.             self.set_available_port()
  171.  
  172.         self.text_to_send = ""
  173.  
  174.         self.error_occurred.connect(self.handle_error)
  175.         self.stopbits_input.currentIndexChanged.connect(
  176.             lambda index: setattr(self, 'serial_stopbits', float(self.stopbits_input.itemText(index))))
  177.         self.com_port_input.textChanged.connect(self.connection)
  178.         self.input_message.textChanged.connect(self.send_data)
  179.  
  180.     def clear_text_areas(self):
  181.         self.input_message.clear()
  182.         self.output_message.clear()
  183.  
  184.     def set_available_port(self):
  185.         for i in range(self.last_used_port_index, len(self.available_com_ports)):
  186.             try:
  187.                 s = serial.Serial('COM' + self.available_com_ports[i], 9600)
  188.                 s.close()
  189.                 self.com_port_input.setText(self.available_com_ports[i])
  190.                 self.last_used_port_index = i + 1
  191.                 self.connection()
  192.                 break
  193.             except serial.SerialException:
  194.                 continue
  195.  
  196.     def connection(self):
  197.         port_number_str = self.com_port_input.text()
  198.  
  199.         if not port_number_str:
  200.             QMessageBox.critical(self, 'Error', "Please enter a valid COM port number.")
  201.             return
  202.  
  203.         try:
  204.             port_number = int(port_number_str)
  205.             if not (1 <= port_number <= 9):
  206.                 QMessageBox.critical(self, 'Error', "COM port number should be between 1 and 9.")
  207.                 return
  208.  
  209.             port_name = f'COM{port_number}'
  210.         except ValueError:
  211.             QMessageBox.critical(self, 'Error', "Please enter a valid numeric COM port number.")
  212.             return
  213.  
  214.         self.previous_port = port_number_str
  215.         if self.previous_port not in self.user_ports:
  216.             self.user_ports.append(self.previous_port)
  217.  
  218.         try:
  219.             self.disconnection()
  220.             self.setup_serial_port(port_name)
  221.             self.start_read()
  222.         except serial.SerialException as e:
  223.             error_message = str(e)
  224.             if "FileNotFoundError" in error_message:
  225.                 error_message = "COM port not found!"
  226.             QMessageBox.critical(self, 'Error', error_message)
  227.             QTimer.singleShot(1000, self.error_occurred.emit)
  228.  
  229.     def handle_error(self):
  230.         if self.user_ports:
  231.             for port in self.user_ports:
  232.                 if port in self.available_com_ports:
  233.                     self.com_port_input.setText(port)
  234.                     QTimer.singleShot(1000, lambda: self.connection())  # Add a delay before reconnecting
  235.                     return
  236.         QTimer.singleShot(1000, lambda: self.set_available_port())  # Add a delay before setting an available port
  237.  
  238.     def setup_serial_port(self, port_name):
  239.         self.serial_port = serial.Serial(
  240.             port_name,
  241.             self.serial_baudrate,
  242.             stopbits=self.serial_stopbits,
  243.             bytesize=self.serial_data_bits,
  244.             parity=self.serial_parity,
  245.             xonxoff=self.serial_flow_control,
  246.             timeout=self.serial_read_timeout,
  247.             write_timeout=self.serial_write_timeout
  248.         )
  249.  
  250.     def start_read(self):
  251.         self.read_thread = SerialDataReader(self.serial_port)
  252.         self.read_thread.data_received.connect(self.data_received)
  253.         self.read_thread.start()
  254.  
  255.     def disconnection(self):
  256.         if self.serial_port:
  257.             self.read_thread.stop()
  258.             self.serial_port.close()
  259.             self.serial_port = None
  260.  
  261.  
  262.     ###############################################################################
  263.     def send_data(self):
  264.         packet_length = 31
  265.         if self.serial_port:
  266.             text = self.input_message.toPlainText()
  267.             if text:
  268.                 self.text_to_send += text[-1]
  269.                 while len(self.text_to_send) >= packet_length:
  270.                     data_to_send = self.create_packet(self.text_to_send[:packet_length])
  271.                     self.text_to_send = self.text_to_send[packet_length:]
  272.                     self.show_packet(data_to_send)
  273.                     self.serial_port.write(data_to_send.encode())
  274.  
  275.     ################################################################################
  276.     def data_received(self, data):
  277.         if self.serial_port:
  278.             self.bytes_received_value.setText(
  279.                 str(int(self.bytes_received_value.text()) + len(data)))
  280.             data = self.byte_destuffing(data[1::].decode())
  281.             self.output_message.moveCursor(QTextCursor.End)
  282.             self.output_message.insertPlainText(data[3:-1])
  283.  
  284.     ################################################################################
  285.     def show_packet(self, data):
  286.         data = data.replace('\n', '\\n')
  287.         self.kadr_window.clear()
  288.         cursor = self.kadr_window.textCursor()
  289.         cursor.beginEditBlock()
  290.         cursor.movePosition(QTextCursor.End)
  291.  
  292.         format = QTextCharFormat()
  293.         format.setFontWeight(QFont.Bold)
  294.         format.setForeground(Qt.blue)
  295.         normal_format = QTextCharFormat()
  296.         normal_format.setFontWeight(QFont.Normal)
  297.         normal_format.setForeground(Qt.black)
  298.  
  299.         i = 0
  300.         while i < len(data):
  301.             if any(data[i:i + len(keyword)] == keyword for keyword in self.keyword_combinations):
  302.                 for keyword in self.keyword_combinations:
  303.                     if data[i:i + len(keyword)] == keyword:
  304.                         cursor.setCharFormat(format)
  305.                         cursor.insertText(data[i:i + len(keyword)])
  306.                         i += len(keyword)
  307.                         break
  308.             else:
  309.                 cursor.setCharFormat(normal_format)
  310.                 cursor.insertText(data[i])
  311.                 i += 1
  312.         cursor.endEditBlock()
  313.  
  314.     def byte_stuffing(self, data):
  315.  
  316.         data = data.replace('$', 'kw2')
  317.         data = data.replace('#x', 'kw1')
  318.         return data
  319.  
  320.     def byte_destuffing(self, data):
  321.         data = data.replace('kw1', '#x')
  322.         data = data.replace('kw2', '$')
  323.         return data
  324.  
  325.     def create_packet(self, data):
  326.         n = 24
  327.         flag = '#' + chr(ord('a') + n - 1)
  328.         source_address = self.com_port_input.text()
  329.         destination_address = '0'
  330.         fcs = '0'
  331.         packet = destination_address + source_address + data + fcs
  332.         print(packet)
  333.         packet = self.byte_stuffing(packet)
  334.         print(packet)
  335.         packet = flag + packet
  336.         print(packet)
  337.         return packet
  338.  
  339.     def closeEvent(self, event):
  340.         if self.serial_port:
  341.             self.read_thread.stop()
  342.             self.serial_port.close()
  343.             self.serial_port = None
  344.         event.accept()
  345.  
  346.  
  347. if __name__ == '__main__':
  348.     app = QApplication(sys.argv)
  349.     window = SerialCommunicationWindow()
  350.     window.show()
  351.     sys.exit(app.exec_())
  352.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement