Kaynağa Gözat

修改了上位机

codex/plsr-2026-minimal
ywh 3 hafta önce
ebeveyn
işleme
ee75c7e18d
4 değiştirilmiş dosya ile 106 ekleme ve 4 silme
  1. BIN
      HostComputer/__pycache__/plsr_control_panel.cpython-314.pyc
  2. BIN
      HostComputer/__pycache__/plsr_modbus_product_test.cpython-314.pyc
  3. +101
    -4
      HostComputer/plsr_control_panel.py
  4. +5
    -0
      HostComputer/plsr_modbus_product_test.py

BIN
HostComputer/__pycache__/plsr_control_panel.cpython-314.pyc Dosyayı Görüntüle


BIN
HostComputer/__pycache__/plsr_modbus_product_test.cpython-314.pyc Dosyayı Görüntüle


+ 101
- 4
HostComputer/plsr_control_panel.py Dosyayı Görüntüle

@@ -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":


+ 5
- 0
HostComputer/plsr_modbus_product_test.py Dosyayı Görüntüle

@@ -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:


Yükleniyor…
İptal
Kaydet