From ee75c7e18df467253701946d267b1f4e420042b0 Mon Sep 17 00:00:00 2001 From: ywh <2227158009@qq.com> Date: Tue, 25 Aug 2026 19:46:47 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E4=B8=8A=E4=BD=8D?= =?UTF-8?q?=E6=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plsr_control_panel.cpython-314.pyc | Bin 65830 -> 73048 bytes .../plsr_modbus_product_test.cpython-314.pyc | Bin 136832 -> 137268 bytes HostComputer/plsr_control_panel.py | 105 +++++++++++++++++- HostComputer/plsr_modbus_product_test.py | 5 + 4 files changed, 106 insertions(+), 4 deletions(-) diff --git a/HostComputer/__pycache__/plsr_control_panel.cpython-314.pyc b/HostComputer/__pycache__/plsr_control_panel.cpython-314.pyc index 89f8c730d707e75b4f1dc2d60c0957fe6c733aae..670fa65b0df288eb85dd082c992c246dff6f7079 100644 GIT binary patch delta 19172 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 delta 14118 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 z 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 delta 12919 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 diff --git a/HostComputer/plsr_control_panel.py b/HostComputer/plsr_control_panel.py index 4f5e3a8..751f3f3 100644 --- a/HostComputer/plsr_control_panel.py +++ b/HostComputer/plsr_control_panel.py @@ -6,6 +6,7 @@ import argparse import queue import threading import time +from datetime import datetime import tkinter as tk import tkinter.font as tkfont from tkinter import messagebox, ttk @@ -48,7 +49,9 @@ from plsr_modbus_product_test import ( BAUD_RATE = 9600 SERIAL_TIMEOUT_SECONDS = 0.8 -STATUS_POLL_SECONDS = 0.25 +DEFAULT_STATUS_POLL_MS = 250 +MIN_STATUS_POLL_MS = 1 +MAX_STATUS_POLL_MS = 3_600_000 SEGMENT_COUNT = 10 SEGMENT_STRIDE = 0x10 MAX_FREQUENCY_HZ = 100_000 @@ -171,10 +174,25 @@ class SerialWorker(threading.Thread): self.stop_event = threading.Event() self.client = None self.next_status_poll = 0.0 + self.polling_enabled = True + self.poll_interval_seconds = DEFAULT_STATUS_POLL_MS / 1000.0 + self.log_callback = None def submit(self, command, **payload): self.commands.put((command, payload)) + def configure_polling(self, enabled, interval_ms): + self.polling_enabled = bool(enabled) + self.poll_interval_seconds = interval_ms / 1000.0 + self.next_status_poll = time.monotonic() + self.poll_interval_seconds + + def set_log_callback(self, callback): + self.log_callback = callback + + def _log(self, message): + if self.log_callback is not None: + self.log_callback("%s %s" % (datetime.now().strftime("%H:%M:%S.%f")[:-3], message)) + def close(self): self.stop_event.set() self.commands.put(("shutdown", {})) @@ -228,6 +246,7 @@ class SerialWorker(threading.Thread): SERIAL_TIMEOUT_SECONDS, ) client.__enter__() + client.log_callback = self._log self.client = client self.next_status_poll = 0.0 self._emit( @@ -298,14 +317,14 @@ class SerialWorker(threading.Thread): diagnostic = read_diagnostic(self._require_client()) self._emit("status", status=status) self._emit("diagnostic", diagnostic=diagnostic) - self.next_status_poll = time.monotonic() + STATUS_POLL_SECONDS + self.next_status_poll = time.monotonic() + self.poll_interval_seconds except (OSError, serial.SerialException) as error: self._handle_connection_error("状态轮询", error) except (TestFailure, ModbusException, RuntimeError) as error: self._emit( "error", operation="状态轮询", message=str(error), modal=False ) - self.next_status_poll = time.monotonic() + 1.0 + self.next_status_poll = time.monotonic() + self.poll_interval_seconds def run(self): try: @@ -322,7 +341,8 @@ class SerialWorker(threading.Thread): self._handle_command(command, payload) if ( - self.client is not None + self.polling_enabled + and self.client is not None and time.monotonic() >= self.next_status_poll ): self._poll_status() @@ -341,10 +361,15 @@ class PlsrControlPanel: self.connected = False self.operation_pending = False self.worker = SerialWorker() + self.worker.set_log_callback(self._queue_log) self.worker.start() + self.log_window = None + self.log_text = None self.port_var = tk.StringVar() self.slave_var = tk.StringVar(value="1") + self.polling_enabled_var = tk.BooleanVar(value=True) + self.poll_interval_var = tk.StringVar(value=str(DEFAULT_STATUS_POLL_MS)) self.connection_var = tk.StringVar(value="未连接") self.footer_var = tk.StringVar(value="请选择串口并连接") self.status_vars = { @@ -410,6 +435,21 @@ class PlsrControlPanel: connection, text="连接", command=self._toggle_connection ) self.connect_button.grid(row=0, column=8, padx=8, pady=8) + + polling = ttk.LabelFrame(connection, text="状态轮询") + polling.grid(row=1, column=0, columnspan=9, padx=8, pady=(0, 6), sticky="ew") + ttk.Checkbutton( + polling, text="启动轮询", variable=self.polling_enabled_var, + command=self._apply_polling_settings, + ).grid(row=0, column=0, padx=(8, 4), pady=6) + ttk.Label(polling, text="轮询间隔").grid(row=0, column=1, padx=(12, 4), pady=6) + ttk.Entry(polling, textvariable=self.poll_interval_var, width=9).grid( + row=0, column=2, padx=4, pady=6 + ) + ttk.Label(polling, text="ms(最小 1 ms)").grid( + row=0, column=3, padx=(4, 8), pady=6 + ) + self.connection_label = ttk.Label( connection, textvariable=self.connection_var, @@ -420,6 +460,10 @@ class PlsrControlPanel: self.connection_label.grid(row=0, column=7, padx=8, pady=8, sticky="e") self.connection_widgets = [self.port_box, refresh_button, slave_entry] + ttk.Button(connection, text="通信日志", command=self._show_log_window).grid( + row=0, column=9, padx=8, pady=8 + ) + self._build_runtime_status() notebook = ttk.Notebook(self.root) @@ -648,6 +692,44 @@ class PlsrControlPanel: elif not ports: self.port_var.set("") + def _queue_log(self, message): + self.worker.results.put(("log", {"message": message})) + + def _show_log_window(self): + if self.log_window is not None and self.log_window.winfo_exists(): + self.log_window.deiconify() + self.log_window.lift() + return + self.log_window = tk.Toplevel(self.root) + self.log_window.title("Modbus 通信日志") + self.log_window.geometry("760x420") + self.log_text = tk.Text(self.log_window, wrap="none", state="disabled") + self.log_text.pack(fill="both", expand=True, padx=8, pady=8) + ttk.Button(self.log_window, text="清空日志", command=self._clear_log).pack(pady=(0, 8)) + + def _clear_log(self): + if self.log_text is not None: + self.log_text.configure(state="normal") + self.log_text.delete("1.0", "end") + self.log_text.configure(state="disabled") + + def _polling_settings(self): + interval_ms = parse_integer( + self.poll_interval_var.get(), "轮询间隔", MIN_STATUS_POLL_MS, MAX_STATUS_POLL_MS + ) + return self.polling_enabled_var.get(), interval_ms + + def _apply_polling_settings(self): + try: + enabled, interval_ms = self._polling_settings() + except ValueError as error: + messagebox.showerror("轮询设置", str(error)) + return + self.worker.configure_polling(enabled, interval_ms) + self.footer_var.set( + "轮询已启动,间隔 %d ms" % interval_ms if enabled else "轮询已停止" + ) + def _set_connected(self, connected, description=""): self.connected = connected self.connect_button.configure( @@ -715,6 +797,15 @@ class PlsrControlPanel: self._update_action_state() self.connection_var.set("正在连接 %s" % port) self.footer_var.set("正在打开串口并读取参数") + try: + enabled, interval_ms = self._polling_settings() + except ValueError as error: + self.connect_button.configure(state="normal") + self.operation_pending = False + self._update_action_state() + messagebox.showerror("轮询设置", str(error)) + return + self.worker.configure_polling(enabled, interval_ms) self.worker.submit("connect", port=port, slave=slave) def _common_words(self): @@ -985,6 +1076,12 @@ class PlsrControlPanel: self._show_status(payload["status"]) elif event == "diagnostic": self._show_diagnostic(payload["diagnostic"]) + elif event == "log": + if self.log_text is not None and self.log_text.winfo_exists(): + self.log_text.configure(state="normal") + self.log_text.insert("end", payload["message"] + "\n") + self.log_text.see("end") + self.log_text.configure(state="disabled") elif event == "operation": self._finish_operation(payload["message"]) elif event == "error": diff --git a/HostComputer/plsr_modbus_product_test.py b/HostComputer/plsr_modbus_product_test.py index 2712939..ff3c33d 100644 --- a/HostComputer/plsr_modbus_product_test.py +++ b/HostComputer/plsr_modbus_product_test.py @@ -504,6 +504,7 @@ class RtuClient: self.frame_gap_seconds = 3.5 * self.character_seconds self.serial_port = None self.last_request_finished = 0.0 + self.log_callback = None def __enter__(self): self.serial_port = serial.Serial( @@ -534,6 +535,8 @@ class RtuClient: self.serial_port.reset_input_buffer() self.serial_port.write(request) self.serial_port.flush() + if hasattr(self, "log_callback") and self.log_callback is not None: + self.log_callback("TX %s" % request.hex(" ")) response = bytearray() deadline = time.monotonic() + self.timeout @@ -564,6 +567,8 @@ class RtuClient: expected_length = 8 self.last_request_finished = time.monotonic() + if self.log_callback is not None: + self.log_callback("RX %s" % bytes(response).hex(" ")) if not response: raise RtuTimeout("no Modbus response to %s" % request.hex(" ")) if expected_length is None: