modify 修复前端展示配置与实际插件配置不一致的bug
This commit is contained in:
@@ -361,6 +361,23 @@ tags = ["aibox_sophon_adapter", "sophon", "adapter"]
|
|||||||
控制流程:`POST {api_base}/api/alg_task_fetch`(查全部任务的 `AlgTaskSession`)→
|
控制流程:`POST {api_base}/api/alg_task_fetch`(查全部任务的 `AlgTaskSession`)→
|
||||||
逐个 `POST {api_base}/api/alg_task_control`(启停控制,按返回 `Result.Code` 判定成败并记日志)。
|
逐个 `POST {api_base}/api/alg_task_control`(启停控制,按返回 `Result.Code` 判定成败并记日志)。
|
||||||
|
|
||||||
|
### IN2 后端自适应(SE5 / SE9)
|
||||||
|
|
||||||
|
由 `diwatch.make_in2_backend` 自动选择,优先级:显式 `device` → sysfs 状态文件(SE9)→ gpio-keys event 设备(SE5)→ 放弃启动。
|
||||||
|
|
||||||
|
| 板卡 | 后端 | 说明 |
|
||||||
|
| ---- | ---- | ---- |
|
||||||
|
| SE9 | `SysfsBackend` | 轮询 `/sys/bus/platform/devices/gpio-in-2/gpio_status`,`poll_interval` / `debounce` 生效 |
|
||||||
|
| SE5 | `EventBackend` | 无 gpio-in-2 节点,IN2 与 IN1 同走 gpio-keys 事件设备(`event1`),键码 `key`(默认 `KEY_DOWN=108`) |
|
||||||
|
|
||||||
|
SE5 首次部署需确认两件事:
|
||||||
|
|
||||||
|
1. **event 读权限**:`linaro` 用户运行 diwatch 报 `Permission denied: '/dev/input/event1'` 时,
|
||||||
|
执行 `sudo usermod -aG input linaro` 并重新登录(服务以 root 运行则不受影响)。
|
||||||
|
2. **IN2 实际键码**:操作 IN2 的同时运行 `python3 -m diwatch detect -t 5`,输出里的 `code=` 即键码;
|
||||||
|
写入 `task_switch.key`(或 CLI `in2 -k <键码>`)。若 detect 无任何输出,说明该 SE5 的 IN2
|
||||||
|
未接入 gpio-keys,需查 `sudo cat /sys/kernel/debug/gpio` 确认接线。
|
||||||
|
|
||||||
行为约定:
|
行为约定:
|
||||||
|
|
||||||
1. **只响应电平跳变**,不按插件启动时的初始电平动作(避免插件重启时误触发全量启停)。
|
1. **只响应电平跳变**,不按插件启动时的初始电平动作(避免插件重启时误触发全量启停)。
|
||||||
|
|||||||
@@ -191,6 +191,8 @@ class AiboxSophonAdapterPlugin(Plugin):
|
|||||||
poll_interval=switch_cfg.get("poll_interval", 0.05),
|
poll_interval=switch_cfg.get("poll_interval", 0.05),
|
||||||
debounce=switch_cfg.get("debounce", 3),
|
debounce=switch_cfg.get("debounce", 3),
|
||||||
active_low=switch_cfg.get("active_low", False),
|
active_low=switch_cfg.get("active_low", False),
|
||||||
|
device=switch_cfg.get("device"),
|
||||||
|
key=switch_cfg.get("key"),
|
||||||
)
|
)
|
||||||
if not task_switch.start(asyncio.get_running_loop()):
|
if not task_switch.start(asyncio.get_running_loop()):
|
||||||
task_switch = None
|
task_switch = None
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import time
|
|||||||
|
|
||||||
from .config import (
|
from .config import (
|
||||||
EV_KEY, VALUE_RELEASE, VALUE_PRESS, VALUE_REPEAT,
|
EV_KEY, VALUE_RELEASE, VALUE_PRESS, VALUE_REPEAT,
|
||||||
DEFAULT_EVENT_DEVICE, GPIO_KEYS_NAME,
|
DEFAULT_EVENT_DEVICE, GPIO_KEYS_NAME, DEFAULT_IN2_KEY,
|
||||||
DEBOUNCE, DEFAULT_INTERVAL,
|
DEBOUNCE, DEFAULT_INTERVAL,
|
||||||
find_status_file,
|
find_status_file,
|
||||||
)
|
)
|
||||||
@@ -259,6 +259,36 @@ def make_in1_backend(device=None, key=None, in_index=1):
|
|||||||
# 4) 都找不到,返回一个 open 时会报错的 EventBackend
|
# 4) 都找不到,返回一个 open 时会报错的 EventBackend
|
||||||
return EventBackend(DEFAULT_EVENT_DEVICE, key=key)
|
return EventBackend(DEFAULT_EVENT_DEVICE, key=key)
|
||||||
|
|
||||||
|
|
||||||
|
def make_in2_backend(device=None, key=None, in_index=2,
|
||||||
|
interval=DEFAULT_INTERVAL, debounce=DEBOUNCE):
|
||||||
|
"""自动为 IN2 选择后端:优先 sysfs(SE9),回退 gpio-keys event(SE5)。
|
||||||
|
|
||||||
|
SE5 无 gpio-in-2 状态文件,IN2 与 IN1 同走 gpio-keys 事件设备(不同键码)。
|
||||||
|
|
||||||
|
优先级:
|
||||||
|
1) 显式 device(event 设备路径) -> EventBackend
|
||||||
|
2) 查找 gpio-in-<in_index> 的状态文件 -> SysfsBackend
|
||||||
|
3) 自动查找 gpio-keys 的 event 设备 -> EventBackend(key 缺省 DEFAULT_IN2_KEY)
|
||||||
|
4) 都找不到 -> 返回 None(由调用方给出可诊断错误)
|
||||||
|
"""
|
||||||
|
# 1) 显式指定 event 设备
|
||||||
|
if device and device != "auto":
|
||||||
|
return EventBackend(device, key=key if key is not None else DEFAULT_IN2_KEY)
|
||||||
|
|
||||||
|
# 2) sysfs 状态文件(SE9)
|
||||||
|
status = find_status_file(in_index)
|
||||||
|
if status:
|
||||||
|
return SysfsBackend(status, debounce=debounce, interval=interval)
|
||||||
|
|
||||||
|
# 3) 回退到 gpio-keys 事件设备(SE5)
|
||||||
|
ev = EventBackend.find_event_device(GPIO_KEYS_NAME)
|
||||||
|
if ev:
|
||||||
|
return EventBackend(ev, key=key if key is not None else DEFAULT_IN2_KEY)
|
||||||
|
|
||||||
|
# 4) 都找不到
|
||||||
|
return None
|
||||||
|
|
||||||
def find_event_device(name=GPIO_KEYS_NAME):
|
def find_event_device(name=GPIO_KEYS_NAME):
|
||||||
"""模块级别别名,等价于 EventBackend.find_event_device."""
|
"""模块级别别名,等价于 EventBackend.find_event_device."""
|
||||||
return EventBackend.find_event_device(name)
|
return EventBackend.find_event_device(name)
|
||||||
@@ -269,6 +299,7 @@ __all__ = [
|
|||||||
"SysfsBackend",
|
"SysfsBackend",
|
||||||
"make_backend",
|
"make_backend",
|
||||||
"make_in1_backend",
|
"make_in1_backend",
|
||||||
|
"make_in2_backend",
|
||||||
"find_status_file",
|
"find_status_file",
|
||||||
"find_event_device",
|
"find_event_device",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -11,10 +11,12 @@
|
|||||||
python3 -m diwatch in1 -d /dev/input/event0 # 手动指定 event
|
python3 -m diwatch in1 -d /dev/input/event0 # 手动指定 event
|
||||||
python3 -m diwatch in1 --no-active-low # 不翻转极性
|
python3 -m diwatch in1 --no-active-low # 不翻转极性
|
||||||
|
|
||||||
# IN2(sysfs 轮询)
|
# IN2(优先 sysfs/SE9;SE5 无 gpio-in-2 时回退 gpio-keys event)
|
||||||
python3 -m diwatch in2
|
python3 -m diwatch in2
|
||||||
python3 -m diwatch in2 0.005
|
python3 -m diwatch in2 0.005
|
||||||
python3 -m diwatch in2 --check
|
python3 -m diwatch in2 --check
|
||||||
|
python3 -m diwatch in2 -k 108 # 指定 event 键码
|
||||||
|
python3 -m diwatch detect # 扫描全部 event 设备,查 IN1/IN2 实际键码
|
||||||
|
|
||||||
# 同时监听
|
# 同时监听
|
||||||
python3 -m diwatch both
|
python3 -m diwatch both
|
||||||
@@ -29,15 +31,16 @@ import time
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from .backends import (
|
from .backends import (
|
||||||
EventBackend, SysfsBackend,
|
EventBackend, SysfsBackend, EVENT_STRUCT,
|
||||||
make_in1_backend, find_status_file, find_event_device,
|
make_in1_backend, make_in2_backend,
|
||||||
|
find_status_file, find_event_device,
|
||||||
)
|
)
|
||||||
from .core import DIChannel, fmt_dur
|
from .core import DIChannel, fmt_dur
|
||||||
from .devicetree import derive_gpio
|
from .devicetree import derive_gpio
|
||||||
from .config import (
|
from .config import (
|
||||||
DEFAULT_EVENT_DEVICE, KEY_UP, DEFAULT_INTERVAL,
|
DEFAULT_EVENT_DEVICE, KEY_UP, DEFAULT_INTERVAL,
|
||||||
IN1_INDEX, IN2_INDEX, IN2_GPIO_NUM, IN2_GPIOCHIP,
|
IN1_INDEX, IN2_INDEX, IN2_GPIO_NUM, IN2_GPIOCHIP,
|
||||||
STATUS_CANDIDATES,
|
STATUS_CANDIDATES, DEFAULT_IN2_KEY,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -63,14 +66,24 @@ def build_in1(args, log):
|
|||||||
|
|
||||||
|
|
||||||
def build_in2(args, log):
|
def build_in2(args, log):
|
||||||
dev = find_status_file(IN2_INDEX)
|
backend = make_in2_backend(
|
||||||
if not dev:
|
device=getattr(args, "device", None),
|
||||||
return None
|
key=getattr(args, "key", None),
|
||||||
backend = SysfsBackend(
|
in_index=IN2_INDEX,
|
||||||
dev,
|
|
||||||
interval=args.interval or DEFAULT_INTERVAL,
|
interval=args.interval or DEFAULT_INTERVAL,
|
||||||
)
|
)
|
||||||
return DIChannel("IN2", backend, active_low=False, log=log)
|
if backend is None:
|
||||||
|
return None
|
||||||
|
ch = DIChannel(
|
||||||
|
"IN2", backend,
|
||||||
|
active_low=getattr(args, "active_low", False),
|
||||||
|
log=log,
|
||||||
|
)
|
||||||
|
if getattr(args, "press_command", None):
|
||||||
|
ch.on_press = lambda ts: run_cmd(args.press_command)
|
||||||
|
if getattr(args, "release_command", None):
|
||||||
|
ch.on_release = lambda ts: run_cmd(args.release_command)
|
||||||
|
return ch
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -108,11 +121,13 @@ def cmd_in2(args):
|
|||||||
log = make_logger("IN2")
|
log = make_logger("IN2")
|
||||||
ch = build_in2(args, log)
|
ch = build_in2(args, log)
|
||||||
if ch is None:
|
if ch is None:
|
||||||
print(f"错误:找不到 IN{IN2_INDEX} 状态文件。已尝试:", file=sys.stderr)
|
print(f"错误:找不到 IN{IN2_INDEX} 的可用后端。已尝试:", file=sys.stderr)
|
||||||
for c in STATUS_CANDIDATES[IN2_INDEX]:
|
for c in STATUS_CANDIDATES[IN2_INDEX]:
|
||||||
print(f" {c}", file=sys.stderr)
|
print(f" sysfs: {c}", file=sys.stderr)
|
||||||
print("排查:ls /sys/bus/platform/devices/ | grep gpio-in",
|
print(f" event: 自动查找 gpio-keys(默认键码 {DEFAULT_IN2_KEY})", file=sys.stderr)
|
||||||
file=sys.stderr)
|
print("排查:ls /sys/bus/platform/devices/ | grep gpio-in", file=sys.stderr)
|
||||||
|
print(" ls -l /dev/input/event*", file=sys.stderr)
|
||||||
|
print(f" python3 -m diwatch detect # 查 IN2 实际键码", file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
if args.check:
|
if args.check:
|
||||||
@@ -127,16 +142,22 @@ def cmd_in2(args):
|
|||||||
return 1
|
return 1
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
print(f"错误:打开 IN2 失败:{e}", file=sys.stderr)
|
print(f"错误:打开 IN2 失败:{e}", file=sys.stderr)
|
||||||
|
print("若为 Permission denied,把当前用户加入 input 组:"
|
||||||
|
"sudo usermod -aG input $USER 后重新登录", file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
try:
|
try:
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
print(f"IN{IN2_INDEX} 监听开始 {datetime.datetime.now():%Y-%m-%d %H:%M:%S}")
|
print(f"IN{IN2_INDEX} 监听开始 {datetime.datetime.now():%Y-%m-%d %H:%M:%S}")
|
||||||
print(f" IN{IN2_INDEX} = GPIO {IN2_GPIO_NUM} = "
|
if isinstance(ch.backend, SysfsBackend):
|
||||||
f"gpiochip{IN2_GPIOCHIP} 第 {IN2_GPIO_NUM - IN2_GPIOCHIP} 线")
|
print(f" IN{IN2_INDEX} = GPIO {IN2_GPIO_NUM} = "
|
||||||
print(f" 状态文件:{ch.backend.path}")
|
f"gpiochip{IN2_GPIOCHIP} 第 {IN2_GPIO_NUM - IN2_GPIOCHIP} 线")
|
||||||
print(f" 轮询间隔:{ch.backend.interval}s "
|
print(f" 状态文件:{ch.backend.path}")
|
||||||
f"抗抖:连续 {ch.backend.debounce} 次采样一致")
|
print(f" 轮询间隔:{ch.backend.interval}s "
|
||||||
|
f"抗抖:连续 {ch.backend.debounce} 次采样一致")
|
||||||
|
else:
|
||||||
|
print(f" 后端:{ch.backend.name} 键码:{ch.backend.key}")
|
||||||
|
print(" 提示:若操作 IN2 无事件输出,运行 python3 -m diwatch detect 查实际键码")
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
ch.run(duration=args.duration or None)
|
ch.run(duration=args.duration or None)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
@@ -246,16 +267,23 @@ def print_summary(channels):
|
|||||||
# --check for IN2
|
# --check for IN2
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
def do_check_in2(ch):
|
def do_check_in2(ch):
|
||||||
print(f"=== IN{IN2_INDEX} 编号自检 ===")
|
print(f"=== IN{IN2_INDEX} 自检 ===")
|
||||||
print()
|
print()
|
||||||
print(f"状态文件:{ch.backend.path}")
|
be = ch.backend
|
||||||
|
if not isinstance(be, SysfsBackend):
|
||||||
|
print(f"后端:{be.name} 键码:{be.key}")
|
||||||
|
print("event 后端无初始电平可读;操作一次 IN2 应出现 value=1(按下/有电)与 0(释放/断电)。")
|
||||||
|
print(f"若无任何输出,说明键码不是 {be.key},用 python3 -m diwatch detect 查实际键码。")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
print(f"状态文件:{be.path}")
|
||||||
print()
|
print()
|
||||||
print("--- 从设备树现场复算 ---")
|
print("--- 从设备树现场复算 ---")
|
||||||
ok = True
|
ok = True
|
||||||
for n in (1, 2):
|
for n in (1, 2):
|
||||||
got = derive_gpio(n)
|
got = derive_gpio(n)
|
||||||
if got is None:
|
if got is None:
|
||||||
print(f" IN{n}:复算失败(设备树不可读或节点缺失)")
|
print(f" IN{n}:复算失败(设备树不可读或节点缺失,SE5 无此节点属正常)")
|
||||||
ok = False
|
ok = False
|
||||||
continue
|
continue
|
||||||
gpio_num, label, pin = got
|
gpio_num, label, pin = got
|
||||||
@@ -269,15 +297,114 @@ def do_check_in2(ch):
|
|||||||
if ok:
|
if ok:
|
||||||
print(f"结论:IN{IN2_INDEX} = GPIO {IN2_GPIO_NUM},推导成立。")
|
print(f"结论:IN{IN2_INDEX} = GPIO {IN2_GPIO_NUM},推导成立。")
|
||||||
else:
|
else:
|
||||||
print("结论:推导与实测不符,请人工核对。")
|
print("结论:推导与实测不符(SE9 正常成立;SE5 走 event 后端,此节可忽略)。")
|
||||||
print()
|
print()
|
||||||
print("--- 当前电平 ---")
|
print("--- 当前电平 ---")
|
||||||
raw = ch.backend.read_raw()
|
raw = be.read_raw()
|
||||||
if raw is not None:
|
if raw is not None:
|
||||||
print(f" gpio_status = {raw} (全局 GPIO {IN2_GPIO_NUM})")
|
print(f" gpio_status = {raw} (全局 GPIO {IN2_GPIO_NUM})")
|
||||||
print(" 注:待机时读到 0 是正常的。")
|
print(" 注:待机时读到 0 是正常的。")
|
||||||
else:
|
else:
|
||||||
print(f" 读取失败:{ch.backend.path} 内容不是 0/1。")
|
print(f" 读取失败:{be.path} 内容不是 0/1。")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# detect:扫描全部 event 设备,实时打印按键事件(确认 IN1/IN2 键码)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
KEY_NAMES = {
|
||||||
|
1: "KEY_ESC", 28: "KEY_ENTER", 103: "KEY_UP", 108: "KEY_DOWN",
|
||||||
|
105: "KEY_LEFT", 106: "KEY_RIGHT", 114: "KEY_VOLUMEDOWN",
|
||||||
|
115: "KEY_VOLUMEUP", 116: "KEY_POWER",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_detect(args):
|
||||||
|
"""扫描可读的 /dev/input/event* 设备,监听 duration 秒并打印所有 EV_KEY 事件。"""
|
||||||
|
import glob
|
||||||
|
import select
|
||||||
|
|
||||||
|
devices = sorted(glob.glob("/dev/input/event*"))
|
||||||
|
if not devices:
|
||||||
|
print("错误:/dev/input/ 下没有 event 设备", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
names = {} # fd -> (device, 名称)
|
||||||
|
poller = select.poll()
|
||||||
|
for dev in devices:
|
||||||
|
try:
|
||||||
|
fd = os.open(dev, os.O_RDONLY | os.O_NONBLOCK)
|
||||||
|
except PermissionError:
|
||||||
|
print(f" 跳过 {dev}(权限不足;用 sudo 运行,或把用户加入 input 组)",
|
||||||
|
file=sys.stderr)
|
||||||
|
continue
|
||||||
|
except OSError as e:
|
||||||
|
print(f" 跳过 {dev}({e})", file=sys.stderr)
|
||||||
|
continue
|
||||||
|
name = ""
|
||||||
|
try:
|
||||||
|
with open(f"/sys/class/input/{os.path.basename(dev)}/device/name") as f:
|
||||||
|
name = f.read().strip()
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
names[fd] = (dev, name)
|
||||||
|
poller.register(fd, select.POLLIN)
|
||||||
|
|
||||||
|
if not names:
|
||||||
|
print("错误:没有可读的 event 设备(Permission denied 时用 sudo 运行)", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
duration = args.duration or 5.0
|
||||||
|
print("=" * 60)
|
||||||
|
print(f"监听 {len(names)} 个 event 设备 {duration:g} 秒,请现在操作 IN1 / IN2 ...")
|
||||||
|
for fd, (dev, name) in names.items():
|
||||||
|
print(f" {dev} [{name}]")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
deadline = time.time() + duration
|
||||||
|
count = 0
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
remain_ms = int((deadline - time.time()) * 1000)
|
||||||
|
if remain_ms <= 0:
|
||||||
|
break
|
||||||
|
if not poller.poll(min(remain_ms, 200)):
|
||||||
|
continue
|
||||||
|
for fd in list(names):
|
||||||
|
try:
|
||||||
|
data = os.read(fd, EVENT_STRUCT.size * 64)
|
||||||
|
except OSError:
|
||||||
|
continue
|
||||||
|
if not data:
|
||||||
|
continue
|
||||||
|
dev, _name = names[fd]
|
||||||
|
for off in range(0, len(data) - EVENT_STRUCT.size + 1, EVENT_STRUCT.size):
|
||||||
|
_sec, _usec, etype, code, value = EVENT_STRUCT.unpack_from(data, off)
|
||||||
|
if etype != 0x01: # 只看 EV_KEY
|
||||||
|
continue
|
||||||
|
if value == 2: # 长按 repeat 太吵
|
||||||
|
continue
|
||||||
|
count += 1
|
||||||
|
key = KEY_NAMES.get(code, "?")
|
||||||
|
val = "按下" if value == 1 else "释放"
|
||||||
|
print(f" {datetime.datetime.now():%H:%M:%S.%f} {dev} "
|
||||||
|
f"code={code:<4} ({key}) {val}")
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
for fd in names:
|
||||||
|
try:
|
||||||
|
os.close(fd)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print()
|
||||||
|
if count == 0:
|
||||||
|
print("未捕获任何按键事件。若已操作 IN2 仍无输出,说明 SE5 的 IN2 不走 event 设备,")
|
||||||
|
print("请检查:ls /sys/bus/platform/devices/ | grep -i gpio;"
|
||||||
|
"sudo cat /sys/kernel/debug/gpio")
|
||||||
|
else:
|
||||||
|
print(f"共捕获 {count} 个按键事件。IN2 对应的 code 即为 -k/--key(或 task_switch.key)应填的键码。")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
@@ -309,15 +436,32 @@ def build_parser():
|
|||||||
p1.set_defaults(func=cmd_in1, interval=None)
|
p1.set_defaults(func=cmd_in1, interval=None)
|
||||||
|
|
||||||
# ---- in2 ----
|
# ---- in2 ----
|
||||||
p2 = sub.add_parser("in2", help="监听 IN2(sysfs gpio_status)")
|
p2 = sub.add_parser("in2", help="监听 IN2(优先 sysfs/SE9,回退 gpio-keys event/SE5)")
|
||||||
p2.add_argument("interval", nargs="?", type=float, default=DEFAULT_INTERVAL,
|
p2.add_argument("interval", nargs="?", type=float, default=DEFAULT_INTERVAL,
|
||||||
help=f"轮询间隔秒(默认 {DEFAULT_INTERVAL})")
|
help=f"sysfs 轮询间隔秒(默认 {DEFAULT_INTERVAL})")
|
||||||
p2.add_argument("--check", action="store_true",
|
p2.add_argument("--check", action="store_true",
|
||||||
help="只自检:复算编号 + 打印当前电平")
|
help="只自检:复算编号 + 打印当前电平")
|
||||||
|
p2.add_argument("-d", "--device", default=None,
|
||||||
|
help="显式指定 event 设备(回退 event 模式用);默认自动选择")
|
||||||
|
p2.add_argument("-k", "--key", type=lambda s: int(s, 0), default=None,
|
||||||
|
help=f"event 模式键码(默认 {DEFAULT_IN2_KEY},用 detect 查实际键码)")
|
||||||
|
p2.add_argument("--active-low", dest="active_low", action="store_true",
|
||||||
|
default=False, help="电平取反(默认不取反)")
|
||||||
|
p2.add_argument("--no-active-low", dest="active_low", action="store_false",
|
||||||
|
help="不翻转(默认)")
|
||||||
|
p2.add_argument("--press-command", help="有电时执行的 shell 命令")
|
||||||
|
p2.add_argument("--release-command", help="断电时执行的 shell 命令")
|
||||||
p2.add_argument("-t", "--duration", type=float, default=0,
|
p2.add_argument("-t", "--duration", type=float, default=0,
|
||||||
help="监听秒数,0=一直跑")
|
help="监听秒数,0=一直跑")
|
||||||
p2.set_defaults(func=cmd_in2)
|
p2.set_defaults(func=cmd_in2)
|
||||||
|
|
||||||
|
# ---- detect ----
|
||||||
|
pd = sub.add_parser("detect",
|
||||||
|
help="扫描全部 event 设备并打印按键事件(确认 IN1/IN2 键码)")
|
||||||
|
pd.add_argument("-t", "--duration", type=float, default=5.0,
|
||||||
|
help="监听秒数(默认 5)")
|
||||||
|
pd.set_defaults(func=cmd_detect)
|
||||||
|
|
||||||
# ---- both ----
|
# ---- both ----
|
||||||
p3 = sub.add_parser("both", help="同时监听 IN1 + IN2")
|
p3 = sub.add_parser("both", help="同时监听 IN1 + IN2")
|
||||||
p3.add_argument("-d", "--device", default="auto",
|
p3.add_argument("-d", "--device", default="auto",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import os
|
|||||||
# ---- input 事件协议(IN1/event 后端用)----
|
# ---- input 事件协议(IN1/event 后端用)----
|
||||||
EV_KEY = 0x01
|
EV_KEY = 0x01
|
||||||
KEY_UP = 103
|
KEY_UP = 103
|
||||||
|
KEY_DOWN = 108
|
||||||
VALUE_RELEASE = 0
|
VALUE_RELEASE = 0
|
||||||
VALUE_PRESS = 1
|
VALUE_PRESS = 1
|
||||||
VALUE_REPEAT = 2
|
VALUE_REPEAT = 2
|
||||||
@@ -14,6 +15,11 @@ VALUE_REPEAT = 2
|
|||||||
DEFAULT_EVENT_DEVICE = "/dev/input/event1"
|
DEFAULT_EVENT_DEVICE = "/dev/input/event1"
|
||||||
GPIO_KEYS_NAME = "gpio-keys"
|
GPIO_KEYS_NAME = "gpio-keys"
|
||||||
|
|
||||||
|
# SE5 的 IN2 与 IN1 同走 gpio-keys 事件设备(event1),仅键码不同;
|
||||||
|
# 默认取 KEY_DOWN,实测不符时用 `diwatch in2 -k <键码>` 覆盖,
|
||||||
|
# 键码可用 `diwatch detect` 现场确认。
|
||||||
|
DEFAULT_IN2_KEY = KEY_DOWN
|
||||||
|
|
||||||
# ---- IN 通道编号 ----
|
# ---- IN 通道编号 ----
|
||||||
IN1_INDEX = 1
|
IN1_INDEX = 1
|
||||||
IN2_INDEX = 2
|
IN2_INDEX = 2
|
||||||
|
|||||||
@@ -23,15 +23,19 @@ import threading
|
|||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
from .diwatch.backends import SysfsBackend
|
from .diwatch.backends import EventBackend, make_in2_backend
|
||||||
from .diwatch.config import IN2_INDEX, find_status_file
|
|
||||||
from .diwatch.core import DIChannel
|
from .diwatch.core import DIChannel
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class In2TaskSwitch:
|
class In2TaskSwitch:
|
||||||
"""IN2 数字输入 → 全部算法任务启停开关。"""
|
"""IN2 数字输入 → 全部算法任务启停开关。
|
||||||
|
|
||||||
|
后端自适应(make_in2_backend):SE9 走 sysfs gpio_status 轮询;
|
||||||
|
SE5 无 gpio-in-2 时回退 gpio-keys event 设备(默认键码 KEY_DOWN=108,
|
||||||
|
可用 device/key 参数覆盖,键码用 diwatch detect 查询)。
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -41,6 +45,8 @@ class In2TaskSwitch:
|
|||||||
poll_interval: float = 0.05,
|
poll_interval: float = 0.05,
|
||||||
debounce: int = 3,
|
debounce: int = 3,
|
||||||
active_low: bool = False,
|
active_low: bool = False,
|
||||||
|
device: str | None = None,
|
||||||
|
key: int | None = None,
|
||||||
timeout: float = 10.0,
|
timeout: float = 10.0,
|
||||||
):
|
):
|
||||||
self.api_base = api_base.rstrip("/")
|
self.api_base = api_base.rstrip("/")
|
||||||
@@ -49,6 +55,8 @@ class In2TaskSwitch:
|
|||||||
self.poll_interval = poll_interval
|
self.poll_interval = poll_interval
|
||||||
self.debounce = debounce
|
self.debounce = debounce
|
||||||
self.active_low = active_low
|
self.active_low = active_low
|
||||||
|
self.device = device
|
||||||
|
self.key = key
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
|
||||||
self._channel: DIChannel | None = None
|
self._channel: DIChannel | None = None
|
||||||
@@ -58,16 +66,18 @@ class In2TaskSwitch:
|
|||||||
|
|
||||||
# ---- 生命周期 ----
|
# ---- 生命周期 ----
|
||||||
def start(self, loop: asyncio.AbstractEventLoop) -> bool:
|
def start(self, loop: asyncio.AbstractEventLoop) -> bool:
|
||||||
"""启动监听线程;找不到 IN2 状态文件时返回 False(非盒子环境属正常)。"""
|
"""启动监听线程;找不到 IN2 可用后端时返回 False(非盒子环境属正常)。"""
|
||||||
status = find_status_file(IN2_INDEX)
|
backend = make_in2_backend(
|
||||||
if not status:
|
device=self.device, key=self.key,
|
||||||
|
interval=self.poll_interval, debounce=self.debounce,
|
||||||
|
)
|
||||||
|
if backend is None:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"[TaskSwitch] 找不到 IN2 状态文件,任务启停开关未启动"
|
"[TaskSwitch] 找不到 IN2 可用后端(sysfs 状态文件与 gpio-keys "
|
||||||
"(非盒子环境属正常现象)"
|
"event 设备均不存在),任务启停开关未启动(非盒子环境属正常现象)"
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
backend = SysfsBackend(status, debounce=self.debounce, interval=self.poll_interval)
|
|
||||||
self._channel = DIChannel(
|
self._channel = DIChannel(
|
||||||
"IN2", backend,
|
"IN2", backend,
|
||||||
active_low=self.active_low,
|
active_low=self.active_low,
|
||||||
@@ -79,11 +89,21 @@ class In2TaskSwitch:
|
|||||||
target=self._run_channel, name="in2-task-switch", daemon=True,
|
target=self._run_channel, name="in2-task-switch", daemon=True,
|
||||||
)
|
)
|
||||||
self._thread.start()
|
self._thread.start()
|
||||||
|
backend_desc = (
|
||||||
|
f"event:{getattr(backend, 'device', '?')}, 键码={backend.key}"
|
||||||
|
if isinstance(backend, EventBackend)
|
||||||
|
else f"sysfs:{backend.path}"
|
||||||
|
)
|
||||||
logger.info(
|
logger.info(
|
||||||
f"[TaskSwitch] IN2 任务启停开关已启动: 状态文件={status}, "
|
f"[TaskSwitch] IN2 任务启停开关已启动: 后端={backend_desc}, "
|
||||||
f"api_base={self.api_base}, "
|
f"api_base={self.api_base}, "
|
||||||
f"启动命令={self.start_command}, 停止命令={self.stop_command}"
|
f"启动命令={self.start_command}, 停止命令={self.stop_command}"
|
||||||
)
|
)
|
||||||
|
if isinstance(backend, EventBackend):
|
||||||
|
logger.info(
|
||||||
|
"[TaskSwitch] event 后端无初始电平,以首个事件为准;"
|
||||||
|
"若操作 IN2 无反应,用 diwatch detect 查实际键码并配置 task_switch.key"
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
@@ -96,7 +116,7 @@ class In2TaskSwitch:
|
|||||||
|
|
||||||
def _run_channel(self) -> None:
|
def _run_channel(self) -> None:
|
||||||
try:
|
try:
|
||||||
self._channel.run()
|
self._channel.run() # type: ignore
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[TaskSwitch] IN2 监听线程异常退出: {type(e).__name__}: {e}")
|
logger.error(f"[TaskSwitch] IN2 监听线程异常退出: {type(e).__name__}: {e}")
|
||||||
|
|
||||||
@@ -156,7 +176,8 @@ class In2TaskSwitch:
|
|||||||
"""查询全部任务的 AlgTaskSession 列表;失败返回空列表。"""
|
"""查询全部任务的 AlgTaskSession 列表;失败返回空列表。"""
|
||||||
try:
|
try:
|
||||||
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
||||||
resp = await client.post(f"{self.api_base}/api/alg_task_fetch", json={})
|
# resp = await client.post(f"{self.api_base}/api/alg_task_fetch", json={})
|
||||||
|
resp = await client.get(f"{self.api_base}/api/alg_task_fetch")
|
||||||
if not resp.is_success:
|
if not resp.is_success:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"[TaskSwitch] 查询任务失败: Status={resp.status_code}, "
|
f"[TaskSwitch] 查询任务失败: Status={resp.status_code}, "
|
||||||
|
|||||||
Reference in New Issue
Block a user