Skip to content

Commit 2307b6a

Browse files
committed
fix(WebServer): decoupling monitoring channel timer
1 parent 3a865f3 commit 2307b6a

4 files changed

Lines changed: 195 additions & 99 deletions

File tree

Tools/WebServer/monitor.py

Lines changed: 64 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -373,52 +373,48 @@ def _get_channel_value(device, mode):
373373
return None, f"Unknown mode: {mode}", False
374374

375375

376-
def _create_monitor_tick(device):
377-
"""Create a monitor tick callback bound to a specific device."""
376+
def _create_channel_tick(device, channel):
377+
"""Create a monitor tick callback for a specific channel (0 or 1)."""
378378

379-
def monitor_tick():
379+
def channel_tick():
380380
if not device.monitor_running:
381381
return
382382

383-
# CH0 监控
384-
mode_0 = getattr(device, "monitor_mode_0", "none")
385-
percent_0, error_0, immediate_0 = _get_channel_value(device, mode_0)
383+
if channel == 0:
384+
mode = getattr(device, "monitor_mode_0", "none")
385+
else:
386+
mode = getattr(device, "monitor_mode_1", "none")
386387

387-
if error_0 is None and percent_0 is not None:
388-
device.last_percent_0 = percent_0
389-
motor_value_0 = map_value(
390-
percent_0, 0, 100, device.motor_min, device.motor_max
391-
)
392-
# CH0: --id is omitted since firmware defaults to 0
393-
cmd_str_0 = f"ctrl -c SET_MOTOR_VALUE -M {int(motor_value_0)}"
394-
if immediate_0:
395-
cmd_str_0 += " -I"
396-
if device.ser:
397-
serial_write_direct(device, f"{cmd_str_0}\r\n")
388+
percent, error, immediate = _get_channel_value(device, mode)
398389

399-
# CH1 监控
400-
mode_1 = getattr(device, "monitor_mode_1", "none")
401-
percent_1, error_1, immediate_1 = _get_channel_value(device, mode_1)
390+
if error is None and percent is not None:
391+
if channel == 0:
392+
device.last_percent_0 = percent
393+
else:
394+
device.last_percent_1 = percent
402395

403-
if error_1 is None and percent_1 is not None:
404-
device.last_percent_1 = percent_1
405-
motor_value_1 = map_value(
406-
percent_1, 0, 100, device.motor_min, device.motor_max
407-
)
408-
# CH1: --id 1 is required for non-zero motor ID
409-
cmd_str_1 = f"ctrl -c SET_MOTOR_VALUE -M {int(motor_value_1)} --id 1"
410-
if immediate_1:
411-
cmd_str_1 += " -I"
396+
motor_value = map_value(percent, 0, 100, device.motor_min, device.motor_max)
397+
398+
if channel == 0:
399+
cmd_str = f"ctrl -c SET_MOTOR_VALUE -M {int(motor_value)}"
400+
else:
401+
cmd_str = f"ctrl -c SET_MOTOR_VALUE -M {int(motor_value)} --id 1"
402+
403+
if immediate:
404+
cmd_str += " -I"
412405
if device.ser:
413-
serial_write_direct(device, f"{cmd_str_1}\r\n")
406+
serial_write_direct(device, f"{cmd_str}\r\n")
414407

415408
# 更新 legacy last_percent (用于兼容)
416-
device.last_percent = percent_0 if percent_0 is not None else (percent_1 or 0)
409+
p0 = device.last_percent_0
410+
p1 = device.last_percent_1
411+
device.last_percent = p0 if p0 is not None else (p1 or 0)
417412

418-
# 阈值报警检查 (独立于监控模式)
419-
check_threshold_alarm(device)
413+
# 阈值报警检查 (独立于监控模式,仅在 CH0 tick 中执行避免重复)
414+
if channel == 0:
415+
check_threshold_alarm(device)
420416

421-
return monitor_tick
417+
return channel_tick
422418

423419

424420
def _create_cmd_file_tick(device):
@@ -462,14 +458,24 @@ def setup():
462458
device.monitor_running = True
463459
tm = get_device_timer_manager(device)
464460
if tm is not None:
465-
device.monitor_timer = tm.add(
466-
device.period, _create_monitor_tick(device), "monitor"
467-
)
461+
# CH0 独立定时器
462+
mode_0 = getattr(device, "monitor_mode_0", "none")
463+
if mode_0 and mode_0 != "none":
464+
device.monitor_timer_0 = tm.add(
465+
device.period_0, _create_channel_tick(device, 0), "monitor_ch0"
466+
)
467+
# CH1 独立定时器
468+
mode_1 = getattr(device, "monitor_mode_1", "none")
469+
if mode_1 and mode_1 != "none":
470+
device.monitor_timer_1 = tm.add(
471+
device.period_1, _create_channel_tick(device, 1), "monitor_ch1"
472+
)
468473
device.cmd_file_timer = tm.add(
469474
1.0, _create_cmd_file_tick(device), "cmd_file"
470475
)
471476
logger.info(
472-
f"Monitor started: mode={mode}, mode_0={device.monitor_mode_0}, mode_1={device.monitor_mode_1}"
477+
f"Monitor started: mode={mode}, mode_0={device.monitor_mode_0}@{device.period_0}s, "
478+
f"mode_1={device.monitor_mode_1}@{device.period_1}s"
473479
)
474480

475481
run_in_device_worker(device, setup, timeout=2.0)
@@ -485,9 +491,12 @@ def cleanup():
485491
device.monitor_mode = None
486492
tm = get_device_timer_manager(device)
487493
if tm is not None:
488-
if device.monitor_timer is not None:
489-
tm.remove(device.monitor_timer)
490-
device.monitor_timer = None
494+
if device.monitor_timer_0 is not None:
495+
tm.remove(device.monitor_timer_0)
496+
device.monitor_timer_0 = None
497+
if device.monitor_timer_1 is not None:
498+
tm.remove(device.monitor_timer_1)
499+
device.monitor_timer_1 = None
491500
if device.cmd_file_timer is not None:
492501
tm.remove(device.cmd_file_timer)
493502
device.cmd_file_timer = None
@@ -498,7 +507,17 @@ def cleanup():
498507
return True, None
499508

500509

501-
def update_monitor_period(device, period):
502-
"""Update monitor timer period for a device."""
503-
if device.monitor_timer is not None:
504-
device.monitor_timer.set_interval(period)
510+
def update_monitor_period(device, period, channel=None):
511+
"""Update monitor timer period for a device.
512+
513+
Args:
514+
device: Device state object.
515+
period: New period in seconds.
516+
channel: None for both, 0 for CH0, 1 for CH1.
517+
"""
518+
if channel is None or channel == 0:
519+
if device.monitor_timer_0 is not None:
520+
device.monitor_timer_0.set_interval(period)
521+
if channel is None or channel == 1:
522+
if device.monitor_timer_1 is not None:
523+
device.monitor_timer_1.set_interval(period)

Tools/WebServer/routes.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -672,13 +672,13 @@ def api_monitor_config():
672672
device.monitor_mode_1 = data["mode_1"]
673673
if "period_0" in data:
674674
device.period_0 = float(data["period_0"])
675+
update_monitor_period(device, device.period_0, channel=0)
675676
if "period_1" in data:
676677
device.period_1 = float(data["period_1"])
678+
update_monitor_period(device, device.period_1, channel=1)
677679

678-
# 使用最小周期作为统一周期
679-
min_period = min(device.period_0, device.period_1)
680-
device.period = min_period
681-
update_monitor_period(device, min_period)
680+
# 同步 legacy period 为最小值(兼容)
681+
device.period = min(device.period_0, device.period_1)
682682

683683
state.save_config()
684684
return jsonify({"success": True})

Tools/WebServer/state.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
"motor_unit_0",
3131
"motor_unit_1",
3232
"period",
33+
"period_0",
34+
"period_1",
3335
"cmd_file",
3436
"cmd_file_enabled",
3537
"audio_db_min",
@@ -120,7 +122,8 @@ def __init__(self, device_id, name="Device"):
120122
self.worker = None
121123

122124
# Monitor timer references
123-
self.monitor_timer = None
125+
self.monitor_timer_0 = None # CH0 independent timer
126+
self.monitor_timer_1 = None # CH1 independent timer
124127
self.cmd_file_timer = None
125128

126129
def to_dict(self):

0 commit comments

Comments
 (0)