muse-driver-framework 1.2.0__tar.gz → 1.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/CHANGELOG.md +63 -0
  2. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/MANIFEST.in +3 -0
  3. muse_driver_framework-1.3.0/P2_MESSAGE_ACCEPTANCE.md +78 -0
  4. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/PKG-INFO +99 -12
  5. muse_driver_framework-1.2.0/muse_driver_framework.egg-info/PKG-INFO → muse_driver_framework-1.3.0/README.md +93 -27
  6. muse_driver_framework-1.3.0/TCP_TLS_ACCEPTANCE.md +45 -0
  7. muse_driver_framework-1.3.0/UDP_SHARED_ACCEPTANCE.md +115 -0
  8. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/muse_driver_framework/__init__.py +14 -2
  9. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/muse_driver_framework/contracts.py +5 -1
  10. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/muse_driver_framework/driver_framework.py +118 -39
  11. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/muse_driver_framework/feedback.py +6 -0
  12. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/muse_driver_framework/feedback_snapshot.py +5 -1
  13. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/muse_driver_framework/http_device.py +5 -0
  14. muse_driver_framework-1.3.0/muse_driver_framework/message_device.py +216 -0
  15. muse_driver_framework-1.3.0/muse_driver_framework/mqtt_device.py +323 -0
  16. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/muse_driver_framework/muse.py +5 -1
  17. muse_driver_framework-1.3.0/muse_driver_framework/tls.py +24 -0
  18. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/muse_driver_framework/udp.py +19 -27
  19. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/muse_driver_framework/udp_device.py +6 -10
  20. muse_driver_framework-1.3.0/muse_driver_framework/udp_shared.py +131 -0
  21. muse_driver_framework-1.3.0/muse_driver_framework/websocket_device.py +74 -0
  22. muse_driver_framework-1.2.0/README.md → muse_driver_framework-1.3.0/muse_driver_framework.egg-info/PKG-INFO +114 -11
  23. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/muse_driver_framework.egg-info/SOURCES.txt +8 -0
  24. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/muse_driver_framework.egg-info/requires.txt +7 -0
  25. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/pyproject.toml +3 -1
  26. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/muse_driver_framework/framing.py +0 -0
  27. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/muse_driver_framework/logging.py +0 -0
  28. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/muse_driver_framework/py.typed +0 -0
  29. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/muse_driver_framework/stream_sessions.py +0 -0
  30. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/muse_driver_framework.egg-info/dependency_links.txt +0 -0
  31. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/muse_driver_framework.egg-info/top_level.txt +0 -0
  32. {muse_driver_framework-1.2.0 → muse_driver_framework-1.3.0}/setup.cfg +0 -0
@@ -1,5 +1,68 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.3.0 - 2026-11-01 (prepared for PyPI publication)
4
+
5
+ - Promote the tested dev4 runtime to 1.3.0: manager-scoped automatic UDP port
6
+ sharing, optional WS/WSS and MQTT/MQTTS, and verified outbound raw TCP TLS.
7
+ - Keep default plaintext and existing FIFO/latest, ACK, descriptor and feedback
8
+ semantics. No cross-driver broker and no mandatory third-party dependency.
9
+ - Templates/recipes stay in their independent source repository; they are not
10
+ included in wheel/sdist. Existing business-driver requirements stay 1.2.0.
11
+ - Source and local protocol/Dashboard validation are recorded separately from
12
+ public publication and MUSE runtime acceptance; no MUSE upgrade is implied.
13
+
14
+ ## 1.3.0.dev4 - 2026-10-31 (source candidate, unpublished)
15
+
16
+ - Add opt-in verified TLS to outbound raw TCP and the TCP side of TCP+UDP, using
17
+ standard-library SSLContext and the existing queue/ACK/framing/lifecycle.
18
+ - Share CA/client certificate helpers with WSS/MQTTS. Support a certificate DNS
19
+ name override for connections to an IP address; include TLS in connect timeout.
20
+ - Keep plaintext default and reject unsupported TLS server mode; no implicit
21
+ downgrade, STARTTLS, new template, dependency or business-driver migration.
22
+ - Document existing-template adoption and test actual TLS/mTLS sockets, binary
23
+ bytes, certificate errors, cancellation, FIFO/latest, reconnect and local Automator.
24
+
25
+ ## 1.3.0.dev3 - 2026-10-31 (source candidate, unpublished)
26
+
27
+ - Add optional message-oriented WebSocket/WSS clients and managers, preserving
28
+ text/binary boundaries, verified TLS, headers/subprotocols and protocol-owned
29
+ authentication/subscription/state synchronization.
30
+ - Add manager-scoped MQTT 3.1.1 broker sessions and topic routing, reference-counted
31
+ subscriptions, per-device FIFO reception, QoS/retain metadata and MQTTS. Physical
32
+ device online and application RPC ACK stay separate from broker/PUBACK state.
33
+ - Reuse existing device FIFO/latest, ACK hooks, intervals, descriptors, streams
34
+ and thread/loop managers without changing TCP/UDP/HTTP lifecycle implementations.
35
+ - Add complete candidate WS and MQTT templates plus documented OBS, Home Assistant
36
+ and Shelly recipes. Stable five templates and business driver pins stay 1.2.0.
37
+ - Test documented product protocol subsets over real loopback sockets and a real
38
+ Aedes broker, including TLS failures, LWT, broker process restart, device recovery,
39
+ stale-write discard and subscription cleanup. No package publication or MUSE upload.
40
+
41
+ ## 1.3.0.dev2 - 2026-10-31 (source candidate, unpublished)
42
+
43
+ - Unify all UDP channels under `UdpPortBroker`. Identical fixed local binds share
44
+ automatically; port zero allocates one independent ephemeral socket per channel.
45
+ Different fixed binds also remain separate. Remove the shared/exclusive switch
46
+ and the duplicate `SharedUdpChannel` implementation.
47
+ - Pure UDP and TCP+UDP use `manager.udp_channel(...)`; standalone `AsyncUdpChannel`
48
+ owns a private broker or accepts an explicit broker. Keep per-peer queues, source
49
+ filtering, source-port-preserving sends and subscription-based socket release.
50
+ - Extend acceptance for same-peer independent ephemeral/fixed sessions and
51
+ single-to-multiple subscriber transitions. No installed driver or stable pin changes.
52
+
53
+ ## 1.3.0.dev1 - 2026-10-31 (source candidate, unpublished)
54
+
55
+ - Add manager-owned `UdpPortBroker` and opt-in `udp_shared_port` for pure UDP
56
+ and TCP+UDP clients. Share fixed binds and sending source ports, while keeping
57
+ each peer's existing receive queue, parsing, ACK and online lifecycle.
58
+ - Replace blanket pure-UDP bind rejection with common shared/exclusive bind
59
+ validation, used by configuration loaders and direct manager construction.
60
+ Ambiguous source routes remain errors; no SO_REUSEPORT or cross-driver service.
61
+ - Add loopback acceptance using documented subsets of QLab OSC and CasparCG
62
+ AMCP/OSC, including 100 UDP peers and manager restart. Protocol codecs and
63
+ device semantics live in test drivers, not the runtime broker.
64
+ - Keep installed programs and stable template dependency pins unchanged.
65
+
3
66
  ## 1.2.0 - 2026-10-24 (prepared for PyPI publication)
4
67
 
5
68
  - Set package metadata, runtime `__version__` and all five copyable template
@@ -1,4 +1,7 @@
1
1
  include CHANGELOG.md
2
+ include UDP_SHARED_ACCEPTANCE.md
3
+ include P2_MESSAGE_ACCEPTANCE.md
4
+ include TCP_TLS_ACCEPTANCE.md
2
5
  prune examples
3
6
  prune tests
4
7
  prune validation
@@ -0,0 +1,78 @@
1
+ # Roadmap P2:消息传输设计与验收
2
+
3
+ 2026-10-31,未发布候选 `1.3.0.dev3`。对应 [roadmap P2 / issue 3](https://github.com/WeiguoHu/muse-driver-framework/issues/3)。沿用 UDP 阶段的“调研→框架/模板→真实网络模拟→本机 Automator→记录”流程;按用户当前范围,不上传 MUSE、不发布、不更新业务驱动依赖。新候选包含前阶段 UDP broker,不覆盖其记录。
4
+
5
+ ## 设计与职责
6
+
7
+ | 层 | 职责 |
8
+ | --- | --- |
9
+ | 原 _QueuedDeviceClient / _DeviceManagerRuntime | 原设备 FIFO/latest、ACK 钩子、间隔、descriptor、完整实时流、共享线程/loop |
10
+ | message_device.py | WS/MQTT 消息会话的打开、握手、监听、初始查询、设备有效反馈在线、重连、关闭与协议状态清理 |
11
+ | websocket_device.py | 完整 text/binary 消息、URL/path/headers/subprotocol、Ping/Pong、Close code、验证证书的 WSS |
12
+ | mqtt_device.py | 单 manager 的 Broker Session、引用计数订阅、topic filter 路由、有界 FIFO、QoS/retain 元数据、验证证书的 MQTTS |
13
+ | protocol.py / 可选配方 | 厂商 JSON/RPC、token/Challenge 认证、请求 ID、订阅内容、错误/ACK、设备 LWT、真实状态与 descriptor 映射 |
14
+
15
+ 没有为了新传输改写已经验证的 TCP/UDP/HTTP 生命周期;没有给公共框架加入 OBS、HA、Shelly 或 Tasmota 字段。普通框架导入不加载 websockets/aiomqtt/Paho。原有五套模板及四个业务驱动保持公共 1.2.0 精确依赖。
16
+
17
+ WS 握手独占 read_message;完成后 listener 启动,再执行可等待 ACK 的初始查询。传输连接、认证/订阅、有效设备反馈是不同阶段;Ping/Pong、HTTP Upgrade、Broker CONNACK 不自动制造设备 online。断开清空待发写命令,重连通过协议只读查询恢复。
18
+
19
+ MQTT 一条 Broker 会话可路由多个设备。客户端自己解析设备实际 online/LWT,`mark_device_offline()` 只关闭该设备接纳、清其待发命令,不断开其他设备;Online 提示通过 `request_state_sync()` 排入只读查询,实际返回后才上线。已进入网络的命令无法撤回。PUBACK/PUBCOMP 仅是传输确认,应用完成仍使用 prepare/wait ACK 钩子与协议关联。
20
+
21
+ ## 公共入口与配置
22
+
23
+ WS:`AsyncWebSocketDeviceClient` / `AsyncWebSocketDeviceManager`,每 slot 一个 url(ws/wss)。`ws_headers`、`ws_subprotocols`、`ws_max_message_bytes` 默认 1 MiB、`ws_max_queue` 默认16、Ping/Pong 默认20秒。`self.channel.send(str|bytes)` 与 `await self.read_message()` 用于协议握手;正常控制交 descriptor 和现有队列。
24
+
25
+ MQTT:`AsyncMqttDeviceClient` / `AsyncMqttDeviceManager`,manager 接收 `broker={host, port, ...}`;逻辑设备覆盖 subscription_topics。`MqttPublish(topic,payload,qos=0,retain=False)` 和 `MqttMessage(topic,payload,qos,retain)` 保留消息语义。`MqttBrokerSession` / `MqttTopicRouter` 是单 manager/loop 资源,不是跨进程服务。
26
+
27
+ 仅支持 MQTT 3.1.1、clean session;QoS 0/1/2 的协议交换交给库,fresh Client 不重放先前会话中的应用写入;不支持 MQTT 5 属性/会话过期或 MQTT over WebSocket。普通 topic filter +/# 支持按读订阅扇出,同一个客户端匹配多个 filter 时本地只分发一次;QoS 自身可能产生重复,协议层负责请求/事件序号和幂等性。
28
+
29
+ broker 默认 keepalive_seconds=30、timeout_seconds=10、incoming_queue_size=1000;设备 mqtt_queue_size 默认100、mqtt_qos 默认1。全部接收 FIFO 有界,不能将 RPC/事件流自动替换成全局 latest。完整可丢弃的表计帧仍由协议解析后交 MeterStream/XYSeriesStream,不在 MQTT 入站丢 RPC。
30
+
31
+ WSS/MQTTS 默认系统信任、证书和主机名验证,支持 tls_ca_file/tls_ca_data 与可选 tls_cert_file/tls_key_file。相对路径基于 config.json;不提供默认跳过验证。URL 不接受嵌入用户名密码;token/设备认证放私有配置,不打印原始认证报文。WSS/MQTTS 库处理加密,框架不自行实现 TLS。
32
+
33
+ 可选依赖锁定 `[websocket]`: websockets 15.0.1;`[mqtt]`: aiomqtt 2.4.0 + Paho 2.1.0。PyPI 元数据兼容目标 Python 3.9。aiomqtt/Paho 在初始化失败及 TLS 证书失败的 socket 所有权有已复现问题,本候选在局部会话内显式清理 Paho socket,并把该 SSLContext 的握手交 Python 持有,避免证书失败时遗留未归属 ssl socket;不修改全局 SSL/Paho 类。相关库内部接入(队列与 socket)针对精确版本测试,升级依赖必须重跑失败/关闭测试。
34
+
35
+ ## 官方参考
36
+
37
+ - [IPComm 官方项目](https://github.com/amx-itg/muse-driver-amx-ipcomm),[开发站 1.0.6 下载](https://developer.amx.com/assets/api/downloadMuseDriver.php?id=61):实际 JAR 的 WS Endpoint/Adapter/Open/Message/Close/Error、path/headers/TLS 分离可借鉴;其文本/二进制转 byte[] 行为没有照搬。本包未发现 MQTT 实现。
38
+ - [OBS 5.x 协议](https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md):RPC1,Hello/Identify/Identified、SHA256/base64 认证、requestId、场景和音量、事件订阅。
39
+ - [Home Assistant WebSocket](https://contributors.home-assistant.io/core/internals/websocket-api/):auth_required/auth/auth_ok、递增 id、get_states、subscribe_events/state_changed、服务调用。
40
+ - [Shelly Plus 1](https://shelly-api-docs.shelly.cloud/gen2/Devices/Gen2/ShellyPlus1/)、[RPC channels](https://shelly-api-docs.shelly.cloud/gen2/General/RPCChannels/)、[MQTT](https://shelly-api-docs.shelly.cloud/gen2/ComponentsAndServices/Mqtt/):Gen2 relay0,WS/MQTT RPC、唯一 src、Switch.Set/GetStatus、NotifyStatus、online/LWT。
41
+ - [Tasmota 14.6.0](https://github.com/arendst/Tasmota/releases/tag/v14.6.0)、[MQTT 文档](https://tasmota.github.io/docs/MQTT/):POWER 读写、cmnd/stat/tele、RESULT、retained/LWT。
42
+ - [MQTT 3.1.1](https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html)、[websockets](https://websockets.readthedocs.io/en/15.0.1/reference/asyncio/client.html)、[aiomqtt](https://aiomqtt.bo3hm.com/developer-interface)、[Aedes](https://github.com/moscajs/aedes)。
43
+
44
+ OBS 是桌面 AV/媒体场景,HA、Shelly/Tasmota 是楼宇/继电器场景,足够覆盖本阶段的消息语义差异。本轮没有为扩展品牌清单而加入缺少明确公开控制规范的商业设备。模拟器只实现文档子集,不运行厂商服务/固件;Home Assistant 按当前文档、Shelly 按 Gen2接口,不宣称固件全量兼容。Shelly 选用其文档化入站 WS,不把测试 WSS 能力宣传成 Shelly Plus 1 具有入站 WSS;Shelly 应用 Digest 未在默认配方实现。
45
+
46
+ ## 模板
47
+
48
+ `examples/websocket-device`:完整 index/protocol/program/config/requirements/.uploadignore,默认 Shelly WS。可选 obs_example.py、home_assistant_example.py 在 factory 中采用;`接入说明.md` 和文件顶部给出类/方法、字段、改名、上传排除和固定/修改边界。
49
+
50
+ `examples/mqtt-device`:完整同结构,默认 Tasmota,shelly_example.py 可替换客户端,manager/入口和 power descriptor 保持。Broker 配置独立,src 唯一,ACK 与反馈独立。
51
+
52
+ 两套候选 requirements 精确声明未发布 1.3.0.dev3 + extra,明确先用本地源码/wheel 测试,不能假装 PyPI 已存在。复制到独立目录后各自的最小协议/入口测试可执行,不依赖兄弟模板或全局 fake mojo 文件。正式部署需要另行发布并锁定真实版本。
53
+
54
+ ## 本机验收与边界
55
+
56
+ 真实 WS/WSS socket、Aedes 0.51.3 Broker + aiomqtt 模拟设备,不 mock 网络 send/recv。核心测试涵盖消息类型、路径/头/子协议、Close/重连、CA/认证失败、QoS0/1/2、retain、订阅拒绝、引用计数、慢设备 FIFO 溢出隔离。设备协议测试覆盖 OBS/HA/Shelly WS/MQTT/Tasmota,应用 ACK 等待独立于 PUBACK,单设备 LWT、Broker 服务真正重启、晚设备上线只读同步和待发写命令丢弃。
57
+
58
+ MQTT 3.1.1 向已有订阅者实时投递 retained 发布时会清零 retain 位;因此“不把 retained 当作在线证据”针对订阅快照,不是防止外部客户端伪造实时状态的认证机制。正常重复/乱序是否可接受按设备关联字段处理,框架不凭空推断状态先后。
59
+
60
+ 原框架回归、四驱动回归、复制模板测试、可选依赖 import、wheel/sdist 文件和隔离 wheel 导入均单独记录。当前 Python 3.9.7/Conda 环境在部分 WSS 关闭与负向 TLS 服务器上仍输出 asyncio SSL transport ResourceWarning,测试断言未因此失败;本阶段如实保留日志,并以真实会话关闭、线程结束、server.wait_closed 和端口重新 bind 核实所测试资源。不能把本机结果解释成 MUSE 上的内核/SDK 资源验收。
61
+
62
+ 本机 Automator 48080 新增独立 P2 页:实际命令经 HTTP节点到协议 manager,反馈来自设备报文解析。HTTP 测试桥有秒级往返波动,不作为 UI 跟手性结论;协议事件与请求响应耗时在 Python 侧独立测量。CPU/RSS 覆盖组合 Python 模拟器及五个管理器线程,不包含 Automator、浏览器、Broker进程。
63
+
64
+ 源文件、研究、JAR 字节码、测试工具和逐次结果在上级项目 `local-validation/p2-message-transports-20261031/`。完整结果见下;短程运行分别保存,不与完整验收混淆。
65
+
66
+ ## 最终结果(2026-10-31)
67
+
68
+ - CPython 3.9.7:框架运行 224 项,221 通过、3 项环境相关跳过;四个现有驱动合计 103 项全部通过(Precis 45、BLU 18、Meter 6、Alcorn 34)。复制模板的四组独立目录测试通过。
69
+ - 构建 wheel 与 sdist;从独立目录导入 wheel,真实消息传输 9 项和厂商协议 7 项通过。普通 `python -S` 导入没有加载 websockets、aiomqtt 或 Paho。未安装到业务运行环境,未发布。
70
+ - 本机 Automator 新增独立 13 节点;原 671 节点逐项一致,flow_cred.json 与 package.json 哈希保持。页面浏览器错误为零。测试进程及 Broker 已停止,页面保留最终缓存。
71
+ - 十台设备 / 五个 manager:6 条 WS/WSS 会话,另 4 台 MQTT/MQTTS 逻辑设备共享 2 条 Broker 会话。8 项场景全部通过,包括实际 Broker 进程重启、单设备 LWT、WSS 订阅恢复、各 manager 三轮启停与关闭后 8 个服务器端口重新 bind。
72
+ - 持续 180.042 秒、2,640 轮、26,400 次真实解析状态核对全部一致;意外重连 0、接收队列溢出 0。测量的是协议状态事件,不是厂商原生 Meter 流;原有 Meter/XY 完整帧回归保留,本轮不宣称新传输已完成厂商 Meter 实测。
73
+ - 本机事件延迟 p50/p95/p99 = 1.322/3.301/5.616 ms,RPC ACK = 1.218/5.303/9.116 ms;Tasmota 查询 p95 = 7.300 ms。组合 Python 进程 CPU 32.489 秒、峰值 RSS 45,363,200 bytes。此为 loopback 模拟负载,不能推断 MUSE/局域网/界面性能。
74
+ - HTTP 测试命令往返出现 1.088–7.672 秒波动;协议事件另行测量,不能拿事件分位数掩盖 HTTP 测试桥耗时,也不能把该桥耗时归为新消息客户端延迟。后续若验收 UI 跟手性,须独立排查 HTTP/Automator 链路。
75
+
76
+ 本地展示:[P2 验证页](http://127.0.0.1:48080/dashboard/p2-message-validation)。结果目录 `../local-validation/p2-message-transports-20261031/automator-2026-10-31T00-28-17-682Z/` 包含 result/deployment/browser JSON、完整截图、Python 日志及私有部署前备份;`验收汇总.json` 与 `test-logs/` 汇总源码/业务/独立 wheel 记录。运行前记录的所有框架、候选模板、协议模拟器源码哈希与验收后文件一致。
77
+
78
+ 本机阶段完成;真实 MUSE 的依赖安装、热重载、SDK 注册与真实厂商设备互通未执行,不据此关闭包含实机条款的远端 issue,也不提前进入 P3 Builder。
@@ -1,10 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: muse-driver-framework
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: Shared pure-Python framework for AMX MUSE Script-as-Device drivers
5
5
  Author: AMX MUSE driver project
6
6
  Requires-Python: >=3.9
7
7
  Description-Content-Type: text/markdown
8
+ Provides-Extra: websocket
9
+ Requires-Dist: websockets==15.0.1; extra == "websocket"
10
+ Provides-Extra: mqtt
11
+ Requires-Dist: aiomqtt==2.4.0; extra == "mqtt"
12
+ Requires-Dist: paho-mqtt==2.1.0; extra == "mqtt"
8
13
  Provides-Extra: http
9
14
  Requires-Dist: httpx<0.29,>=0.28.1; extra == "http"
10
15
  Provides-Extra: telnet
@@ -18,18 +23,98 @@ Requires-Dist: cryptography<49,>=48.0.1; extra == "ssh"
18
23
 
19
24
  Shared pure-Python runtime framework for AMX MUSE Script-as-Device programs.
20
25
 
21
- Version **1.2.0** packages the current protocol-adaptation work. Package metadata,
22
- runtime `__version__` and all five complete template requirements agree on
23
- 1.2.0. The distributions are prepared locally; upload to PyPI before deploying
24
- templates that require this version. Existing device drivers keep their own
25
- exact requirements until explicitly upgraded.
26
+ Version **1.3.0** is prepared for PyPI publication. It includes manager-scoped
27
+ UDP port sharing, optional WebSocket/MQTT transports and verified outbound raw
28
+ TCP TLS. This checkout has not uploaded the release or upgraded any MUSE program.
29
+ The last verified public release remains **1.2.0** until publication is confirmed;
30
+ existing business-driver and stable template pins are unchanged. After publishing,
31
+ select the exact 1.3.0 requirement for programs adopting these new capabilities.
32
+ Installing a package does not update existing programs.
33
+
34
+ See [UDP shared-port design and acceptance](UDP_SHARED_ACCEPTANCE.md) for the
35
+ configuration, architecture boundaries, protocol references and simulation scope.
36
+ Pure UDP and TCP+UDP clients automatically share identical fixed local binds;
37
+ there is no shared/exclusive configuration switch. Port `0` creates an independent
38
+ OS-assigned socket per channel; different fixed binds also use separate sockets.
39
+ Their existing `create_udp_channel()` / `receive()` / `send()` interface remains.
40
+ A socket routes by resolved source IP+port, or by fixed IP when
41
+ `udp_strict_peer_port` is false. Ambiguous overlapping source routes are rejected.
42
+ Distinct managers and programs do not share listeners. Direct `AsyncUdpChannel`
43
+ construction owns a private broker, unless explicitly given `broker=...`; driver
44
+ overrides use `super().create_udp_channel()` or `manager.udp_channel(...)` to join
45
+ their manager's broker. Per-device FIFO/latest buffers and lifecycle stay separate.
26
46
 
27
47
  PyPI distributes only the framework library and its build/documentation metadata.
28
- The five driver templates, protocol recipes and tests stay in the development
48
+ The five stable and two candidate driver templates, protocol recipes and tests stay in the development
29
49
  checkout; they are not included in either the wheel or source distribution.
30
50
  Copy a template locally, then let its `requirements.txt` declare which framework
31
51
  version MUSE should install. Templates are not synchronized from PyPI.
32
52
 
53
+ ## Raw TCP TLS (1.3.0)
54
+
55
+ Use the existing `AsyncTcpDeviceClient` / `AsyncTcpDeviceManager` with `tls: true`.
56
+ Default `false` preserves plaintext TCP. This option supports outbound
57
+ `tcp_mode: "client"` only; server mode with TLS is rejected explicitly.
58
+ TLS uses Python's standard library, without a new extra or template.
59
+
60
+ `tls_ca_file` / `tls_ca_data` configure private trust; `tls_cert_file` /
61
+ `tls_key_file` optionally present a client certificate. Relative file paths resolve
62
+ from `config.json`. `tls_server_hostname` defaults to `host`; set the certificate's
63
+ DNS name when connecting to an IP endpoint whose certificate identifies that name.
64
+ Certificate and hostname verification remain enabled. The connection deadline
65
+ includes the TLS handshake; failures do not fall back to plaintext.
66
+
67
+ Framing, application login, FIFO/latest, ACK, watchdog and reconnect still use the
68
+ existing TCP protocol/lifecycle. Runtime statistics report `tcpTls` and the active
69
+ `tlsVersion`; TLS itself does not prove application state or device execution.
70
+ The TCP part of `AsyncTcpUdpDeviceClient` can use the same option; its UDP channel
71
+ remains unchanged. Telnet/SSH, STARTTLS and a TLS server are separate protocols or
72
+ modes and are outside this addition.
73
+
74
+ See [TCP TLS design and acceptance](TCP_TLS_ACCEPTANCE.md) and the existing
75
+ `examples/scoped-device/config.tls.example.json`. Stable template requirements
76
+ remain 1.2.0; adopting TLS requires the exact 1.3.0 dependency after this release
77
+ is published. No current business driver is upgraded.
78
+
79
+ ## P2 message transports (1.3.0)
80
+
81
+ `AsyncWebSocketDeviceClient` / `AsyncWebSocketDeviceManager` preserve complete
82
+ text/binary messages with optional `[websocket]` dependencies. Protocol handshake
83
+ owns receive until authenticated; then the listener starts before initial RPC
84
+ queries. Paths, headers, subprotocols and verified WSS CA/client certificates are
85
+ configurable; Ping/Pong alone never marks a physical device online.
86
+
87
+ `AsyncMqttDeviceClient` / `AsyncMqttDeviceManager` separate logical devices from
88
+ one `MqttBrokerSession` per manager. A `MqttTopicRouter` shares subscriptions by
89
+ reference count and routes to bounded per-device FIFOs. `[mqtt]` is optional.
90
+ MQTT 3.1.1, QoS 0/1/2 and retained metadata are supported; MQTT 5 and cross-process
91
+ sharing aren't included. Transport PUBACK/PUBCOMP never substitutes for a device
92
+ action ACK. A new clean session and protocol-owned reads rebuild real state;
93
+ pending writes aren't replayed after session loss.
94
+
95
+ The seven local templates include the new candidate `websocket-device` (Shelly
96
+ WS plus optional OBS/HA recipes) and `mqtt-device` (Tasmota plus Shelly RPC).
97
+ These two require the 1.3.0 message transports and their named extra;
98
+ the original five and installed drivers keep public `1.2.0` pins. Read each
99
+ candidate template's Chinese integration guide before editing protocol hooks.
100
+ The actual WS/TLS sockets and Aedes MQTT broker tests model documented product
101
+ subsets, not vendor firmware certification. See [P2 acceptance](P2_MESSAGE_ACCEPTANCE.md).
102
+
103
+ ## Script-as-Device workflow
104
+
105
+ Maintain one complete local `program.json` and upload the whole driver folder
106
+ with the native VSCode MUSE Plugin. No descriptor-generation step or second
107
+ `device-descriptor.json` is needed. Each driver entry point exports its registered
108
+ descriptor to memory through `context.export.exporter.exportDescriptor()`;
109
+ it does not read the controller-managed `program.json` at runtime. This SDK glue
110
+ stays outside the transport library. The workflow was verified on MUSE 1.15.45.
111
+
112
+ This entry-point change does not require a new framework or panel release.
113
+ Existing drivers must receive updated entry-point source through a full driver
114
+ upload; changing the installed framework alone cannot update their Python files.
115
+ See the development checkout's `Script-as-Device开发与上传流程.md` for the current
116
+ upload, testing and deployment checklist. Keep site configuration private.
117
+
33
118
  The package contains the transport-independent pieces shared by the Precis
34
119
  Driver and Meter Validation programs:
35
120
 
@@ -105,7 +190,7 @@ discard queued writes on failure. Session cleanup uses `session_ended()`.
105
190
  See `examples/http-device/` for a complete copyable driver and simulated-device
106
191
  tests. These APIs are included in 1.2.0, not the previously published 1.1.9
107
192
  package. Test source with `pip install -e '.[http]'`, or install
108
- `muse-driver-framework[http]==1.2.0` after publication.
193
+ the published `muse-driver-framework[http]==1.2.0`.
109
194
 
110
195
  ## Runtime boundaries
111
196
 
@@ -136,7 +221,8 @@ port change without imposing that relationship on independent UDP notifications.
136
221
  The `udpOnline` runtime statistic means the UDP channel opened, not evidence
137
222
  that remote feedback is current; use `udpSocketOpen` for socket diagnostics.
138
223
  The device's public online state remains protocol-specific. This is not an
139
- Art-Net output scheduler or an SSE/WebSocket client.
224
+ Art-Net output scheduler or an SSE client. WebSocket uses the separate optional
225
+ message transport described above, not this TCP/UDP adapter.
140
226
 
141
227
  Each framework import belongs to one model-specific Script-as-Device runtime.
142
228
  The framework does not discover devices, verify model identity, or merge devices
@@ -283,7 +369,7 @@ described below; all are included in 1.2.0. Publication and target-runtime
283
369
  installation are separate steps. Older artifacts are unchanged.
284
370
  See [examples](examples/README.md) for TCP, pure UDP, TCP+UDP and HTTP templates.
285
371
 
286
- All five copyable directories include the MUSE entry point, descriptor,
372
+ The original five copyable directories include the MUSE entry point, descriptor,
287
373
  configuration example, protocol module and minimal loopback tests. The TCP/UDP
288
374
  directories have optional ACK recipes; HTTP uses its request/response flow.
289
375
  The mixed template uses ordinary TCP controls plus a UDP readonly indicator;
@@ -432,8 +518,9 @@ Manager shutdown cancels device-owner tasks first and allows 250 ms for orderly
432
518
  cleanup before canceling residual tasks. The fallback also handles legacy
433
519
  receivers that swallow an initial cancellation during a receive timeout race.
434
520
  New BLU source requires `enqueue_recovery` and refuses to start with old framework
435
- code; its existing exact requirement remains 1.1.9. Adopting 1.2.0 is an explicit
436
- requirements change after publication. Previously published versions remain unchanged.
521
+ code. Its exact requirement has now been explicitly upgraded to the published
522
+ 1.2.0. Previously published versions remain unchanged; this documentation update
523
+ does not rebuild or replace the published 1.2.0 artifacts.
437
524
 
438
525
  The manager constructor accepts the normalized device list, not the config root:
439
526
  pass `config[entries_key]` and `config["connection_stagger_seconds"]`. Root
@@ -1,35 +1,99 @@
1
- Metadata-Version: 2.4
2
- Name: muse-driver-framework
3
- Version: 1.2.0
4
- Summary: Shared pure-Python framework for AMX MUSE Script-as-Device drivers
5
- Author: AMX MUSE driver project
6
- Requires-Python: >=3.9
7
- Description-Content-Type: text/markdown
8
- Provides-Extra: http
9
- Requires-Dist: httpx<0.29,>=0.28.1; extra == "http"
10
- Provides-Extra: telnet
11
- Requires-Dist: telnetlib3<5.1,>=5.0.1; extra == "telnet"
12
- Provides-Extra: ssh
13
- Requires-Dist: asyncssh<2.22,>=2.21.1; python_version < "3.10" and extra == "ssh"
14
- Requires-Dist: asyncssh<2.25,>=2.24.1; python_version >= "3.10" and extra == "ssh"
15
- Requires-Dist: cryptography<49,>=48.0.1; extra == "ssh"
16
-
17
1
  # muse-driver-framework
18
2
 
19
3
  Shared pure-Python runtime framework for AMX MUSE Script-as-Device programs.
20
4
 
21
- Version **1.2.0** packages the current protocol-adaptation work. Package metadata,
22
- runtime `__version__` and all five complete template requirements agree on
23
- 1.2.0. The distributions are prepared locally; upload to PyPI before deploying
24
- templates that require this version. Existing device drivers keep their own
25
- exact requirements until explicitly upgraded.
5
+ Version **1.3.0** is prepared for PyPI publication. It includes manager-scoped
6
+ UDP port sharing, optional WebSocket/MQTT transports and verified outbound raw
7
+ TCP TLS. This checkout has not uploaded the release or upgraded any MUSE program.
8
+ The last verified public release remains **1.2.0** until publication is confirmed;
9
+ existing business-driver and stable template pins are unchanged. After publishing,
10
+ select the exact 1.3.0 requirement for programs adopting these new capabilities.
11
+ Installing a package does not update existing programs.
12
+
13
+ See [UDP shared-port design and acceptance](UDP_SHARED_ACCEPTANCE.md) for the
14
+ configuration, architecture boundaries, protocol references and simulation scope.
15
+ Pure UDP and TCP+UDP clients automatically share identical fixed local binds;
16
+ there is no shared/exclusive configuration switch. Port `0` creates an independent
17
+ OS-assigned socket per channel; different fixed binds also use separate sockets.
18
+ Their existing `create_udp_channel()` / `receive()` / `send()` interface remains.
19
+ A socket routes by resolved source IP+port, or by fixed IP when
20
+ `udp_strict_peer_port` is false. Ambiguous overlapping source routes are rejected.
21
+ Distinct managers and programs do not share listeners. Direct `AsyncUdpChannel`
22
+ construction owns a private broker, unless explicitly given `broker=...`; driver
23
+ overrides use `super().create_udp_channel()` or `manager.udp_channel(...)` to join
24
+ their manager's broker. Per-device FIFO/latest buffers and lifecycle stay separate.
26
25
 
27
26
  PyPI distributes only the framework library and its build/documentation metadata.
28
- The five driver templates, protocol recipes and tests stay in the development
27
+ The five stable and two candidate driver templates, protocol recipes and tests stay in the development
29
28
  checkout; they are not included in either the wheel or source distribution.
30
29
  Copy a template locally, then let its `requirements.txt` declare which framework
31
30
  version MUSE should install. Templates are not synchronized from PyPI.
32
31
 
32
+ ## Raw TCP TLS (1.3.0)
33
+
34
+ Use the existing `AsyncTcpDeviceClient` / `AsyncTcpDeviceManager` with `tls: true`.
35
+ Default `false` preserves plaintext TCP. This option supports outbound
36
+ `tcp_mode: "client"` only; server mode with TLS is rejected explicitly.
37
+ TLS uses Python's standard library, without a new extra or template.
38
+
39
+ `tls_ca_file` / `tls_ca_data` configure private trust; `tls_cert_file` /
40
+ `tls_key_file` optionally present a client certificate. Relative file paths resolve
41
+ from `config.json`. `tls_server_hostname` defaults to `host`; set the certificate's
42
+ DNS name when connecting to an IP endpoint whose certificate identifies that name.
43
+ Certificate and hostname verification remain enabled. The connection deadline
44
+ includes the TLS handshake; failures do not fall back to plaintext.
45
+
46
+ Framing, application login, FIFO/latest, ACK, watchdog and reconnect still use the
47
+ existing TCP protocol/lifecycle. Runtime statistics report `tcpTls` and the active
48
+ `tlsVersion`; TLS itself does not prove application state or device execution.
49
+ The TCP part of `AsyncTcpUdpDeviceClient` can use the same option; its UDP channel
50
+ remains unchanged. Telnet/SSH, STARTTLS and a TLS server are separate protocols or
51
+ modes and are outside this addition.
52
+
53
+ See [TCP TLS design and acceptance](TCP_TLS_ACCEPTANCE.md) and the existing
54
+ `examples/scoped-device/config.tls.example.json`. Stable template requirements
55
+ remain 1.2.0; adopting TLS requires the exact 1.3.0 dependency after this release
56
+ is published. No current business driver is upgraded.
57
+
58
+ ## P2 message transports (1.3.0)
59
+
60
+ `AsyncWebSocketDeviceClient` / `AsyncWebSocketDeviceManager` preserve complete
61
+ text/binary messages with optional `[websocket]` dependencies. Protocol handshake
62
+ owns receive until authenticated; then the listener starts before initial RPC
63
+ queries. Paths, headers, subprotocols and verified WSS CA/client certificates are
64
+ configurable; Ping/Pong alone never marks a physical device online.
65
+
66
+ `AsyncMqttDeviceClient` / `AsyncMqttDeviceManager` separate logical devices from
67
+ one `MqttBrokerSession` per manager. A `MqttTopicRouter` shares subscriptions by
68
+ reference count and routes to bounded per-device FIFOs. `[mqtt]` is optional.
69
+ MQTT 3.1.1, QoS 0/1/2 and retained metadata are supported; MQTT 5 and cross-process
70
+ sharing aren't included. Transport PUBACK/PUBCOMP never substitutes for a device
71
+ action ACK. A new clean session and protocol-owned reads rebuild real state;
72
+ pending writes aren't replayed after session loss.
73
+
74
+ The seven local templates include the new candidate `websocket-device` (Shelly
75
+ WS plus optional OBS/HA recipes) and `mqtt-device` (Tasmota plus Shelly RPC).
76
+ These two require the 1.3.0 message transports and their named extra;
77
+ the original five and installed drivers keep public `1.2.0` pins. Read each
78
+ candidate template's Chinese integration guide before editing protocol hooks.
79
+ The actual WS/TLS sockets and Aedes MQTT broker tests model documented product
80
+ subsets, not vendor firmware certification. See [P2 acceptance](P2_MESSAGE_ACCEPTANCE.md).
81
+
82
+ ## Script-as-Device workflow
83
+
84
+ Maintain one complete local `program.json` and upload the whole driver folder
85
+ with the native VSCode MUSE Plugin. No descriptor-generation step or second
86
+ `device-descriptor.json` is needed. Each driver entry point exports its registered
87
+ descriptor to memory through `context.export.exporter.exportDescriptor()`;
88
+ it does not read the controller-managed `program.json` at runtime. This SDK glue
89
+ stays outside the transport library. The workflow was verified on MUSE 1.15.45.
90
+
91
+ This entry-point change does not require a new framework or panel release.
92
+ Existing drivers must receive updated entry-point source through a full driver
93
+ upload; changing the installed framework alone cannot update their Python files.
94
+ See the development checkout's `Script-as-Device开发与上传流程.md` for the current
95
+ upload, testing and deployment checklist. Keep site configuration private.
96
+
33
97
  The package contains the transport-independent pieces shared by the Precis
34
98
  Driver and Meter Validation programs:
35
99
 
@@ -105,7 +169,7 @@ discard queued writes on failure. Session cleanup uses `session_ended()`.
105
169
  See `examples/http-device/` for a complete copyable driver and simulated-device
106
170
  tests. These APIs are included in 1.2.0, not the previously published 1.1.9
107
171
  package. Test source with `pip install -e '.[http]'`, or install
108
- `muse-driver-framework[http]==1.2.0` after publication.
172
+ the published `muse-driver-framework[http]==1.2.0`.
109
173
 
110
174
  ## Runtime boundaries
111
175
 
@@ -136,7 +200,8 @@ port change without imposing that relationship on independent UDP notifications.
136
200
  The `udpOnline` runtime statistic means the UDP channel opened, not evidence
137
201
  that remote feedback is current; use `udpSocketOpen` for socket diagnostics.
138
202
  The device's public online state remains protocol-specific. This is not an
139
- Art-Net output scheduler or an SSE/WebSocket client.
203
+ Art-Net output scheduler or an SSE client. WebSocket uses the separate optional
204
+ message transport described above, not this TCP/UDP adapter.
140
205
 
141
206
  Each framework import belongs to one model-specific Script-as-Device runtime.
142
207
  The framework does not discover devices, verify model identity, or merge devices
@@ -283,7 +348,7 @@ described below; all are included in 1.2.0. Publication and target-runtime
283
348
  installation are separate steps. Older artifacts are unchanged.
284
349
  See [examples](examples/README.md) for TCP, pure UDP, TCP+UDP and HTTP templates.
285
350
 
286
- All five copyable directories include the MUSE entry point, descriptor,
351
+ The original five copyable directories include the MUSE entry point, descriptor,
287
352
  configuration example, protocol module and minimal loopback tests. The TCP/UDP
288
353
  directories have optional ACK recipes; HTTP uses its request/response flow.
289
354
  The mixed template uses ordinary TCP controls plus a UDP readonly indicator;
@@ -432,8 +497,9 @@ Manager shutdown cancels device-owner tasks first and allows 250 ms for orderly
432
497
  cleanup before canceling residual tasks. The fallback also handles legacy
433
498
  receivers that swallow an initial cancellation during a receive timeout race.
434
499
  New BLU source requires `enqueue_recovery` and refuses to start with old framework
435
- code; its existing exact requirement remains 1.1.9. Adopting 1.2.0 is an explicit
436
- requirements change after publication. Previously published versions remain unchanged.
500
+ code. Its exact requirement has now been explicitly upgraded to the published
501
+ 1.2.0. Previously published versions remain unchanged; this documentation update
502
+ does not rebuild or replace the published 1.2.0 artifacts.
437
503
 
438
504
  The manager constructor accepts the normalized device list, not the config root:
439
505
  pass `config[entries_key]` and `config["connection_stagger_seconds"]`. Root
@@ -0,0 +1,45 @@
1
+ # 原始 TCP+TLS 设计与验收
2
+
3
+ 2026-10-31,候选 **1.3.0.dev4**,未发布/未上传 MUSE。用户授权在现有 TCP 模板中补充 TLS,参考官方 IPComm 1.0.6;完整模板仍为七套。
4
+
5
+ ## 实现
6
+
7
+ `AsyncTcpDeviceClient` / `AsyncTcpDeviceManager` 在主动连接上接受 `tls=true`;默认 false 保持普通 TCP。使用标准库,不新增 extra。TLS 连接成功后继续复用同一套协议分帧、登录、FIFO/latest、ACK、watchdog 与重连。
8
+
9
+ 支持系统/私有 CA、证书及主机名验证、`tls_server_hostname`(连接 IP 与证书 DNS 名分离)、可选客户端证书/密钥。TLS 握手计入 connect_timeout,失败不会改连明文。相对证书文件基于 config.json 解析。当前只实现 client,不实现 TLS server、STARTTLS 或 Telnet-over-TLS;SSH 继续采用原独立适配。
10
+
11
+ 共享 TLS 工具从 message_device 提取到 tls.py,WSS/MQTTS 使用相同函数;现有消息传输专项纳入全量回归。TCP+UDP / 网关模板的主动 TCP 部分可采用该配置;UDP 部分不变。
12
+
13
+ ## IPComm 1.0.6 对照
14
+
15
+ 来源:[官方项目](https://github.com/amx-itg/muse-driver-amx-ipcomm)、[官方二进制下载](https://developer.amx.com/assets/api/downloadMuseDriver.php?id=61)。本次读取已保存 1.0.6 JAR 的 class,而非凭类名推断:TLSClientPersistentDriverAdapter 只设置 TLS 标志,共用 TCP adapter;后者加载 SSLContext 并向 NioEngine.enableTLS 注入连接层配置。
16
+
17
+ 其 SSLContextProvider 在 certificate 为空时使用 TrustAll。本框架继续采用证书/主机名验证,不把其 Java trust store 或 TrustAll 直接移入 Python。原始字节码、JAR 哈希和说明保存在 `../local-validation/tcp-tls-20261031/`。
18
+
19
+ Python 官方依据:[SSLContext](https://docs.python.org/3.9/library/ssl.html)、[asyncio 连接](https://docs.python.org/3.9/library/asyncio-eventloop.html#asyncio.loop.create_connection)。TLS 协商版本由目标 Python/OpenSSL 与设备共同决定,不承诺所有 MUSE 固件具有同一版本/信任库。
20
+
21
+ ## 模板采用
22
+
23
+ 使用原 `examples/scoped-device`,读取 [TLS 接入说明](examples/scoped-device/TLS接入说明.md),参考 config.tls.example.json。无需新客户端类或修改 create_client;设备正文、认证、参数范围/反馈仍在 protocol.py。原五套 requirements 保留 1.2.0,启用 TLS 前必须使用匹配的新框架;未发布候选不能直接假定 PyPI 可安装。两套消息模板继续跟随当前候选版本,四个业务驱动依赖不变。
24
+
25
+ ## 源码结果
26
+
27
+ - CPython 3.9.7:TCP TLS 专项 7 项全部通过。
28
+ - 全框架运行 231 项,228 通过、3 项未安装 Telnet/SSH extra 而跳过;WS/WSS/MQTT/MQTTS、TCP/UDP/HTTP、复制模板测试包含在通过部分。
29
+ - 四现有业务驱动共 103 项通过:Precis 45、BLU 18、Meter 6、Alcorn 34;它们的 requirements 仍锁公共 1.2.0,测试使用候选源码,并未替换安装版本。
30
+ - 实际 TLS/mTLS 验证服务器身份、主机名与客户端身份;错误证书在应用报文发送前失败。二进制测试保持包括非 UTF-8/NUL/换行在内的完整 bytes。
31
+ - 使用当前 Conda Python 3.9 的负向 TLS 服务器可能输出预期证书拒绝/ConnectionReset 和已有 asyncio SSL transport ResourceWarning;日志完整保留,不把这些日志当业务连接失败,也不据本机端口重绑断言 MUSE 内核资源行为。
32
+
33
+ 本机 Automator 结果见下。研究、脚本、私有备份与日志都在上级 `local-validation/tcp-tls-20261031/`;不发布、Push 或上传,不宣称已对接真实厂商加密协议。
34
+
35
+ ## 本机 Automator 最终结果
36
+
37
+ - 独立 Tab `TCP TLS · 本机验证`;状态结果:[本地 JSON](http://127.0.0.1:48080/tcp-tls-validation/status)。新增 10 个验证节点,原 684 节点逐项保持,凭据和 package 哈希保持。未修改业务绑定,也未重启控制器。
38
+ - 4 台同协议模拟设备:3 条 TLS(含 1 条 mTLS)、1 条明文,同 manager/loop;3 条加密连接实际协商 TLSv1.3。
39
+ - 6 项场景全部通过:混合连接、Automator 控制与真实反馈、latest/FIFO/ACK、单设备重连隔离、3 轮管理器启停、持续事件。
40
+ - 持续 60.062 秒,931 轮、3,724 次状态核对一致;意外重连 0,结束时待发 latest 为 0。100 次连续目标在该次突发中合并成 1 次设置,最终目标 99;离散静音顺序保留。
41
+ - manager 线程和所有测试会话停止,4 个服务器端口实际重新 bind。测试 HTTP 适配服务退出,Automator 只保留最后状态缓存。
42
+ - 本机检测等待分位数 p50/p95/p99=12.395/15.028/17.134 ms。这里使用 10 ms 间隔等待状态条件,包含测试轮询发现耗时,不能当作 TLS 本身开销或 UI 延迟。组合 Python 模拟器与管理器进程 CPU 2.498 秒、峰值 RSS 31,551,488 bytes,不含 Automator。
43
+ - 测试 HTTP 命令往返:mute 3.145 秒、volume 0.152 秒,保留实测波动,不以协议事件检测值掩盖测试桥耗时。
44
+
45
+ 完整结果 `../local-validation/tcp-tls-20261031/automator-2026-10-31T01-07-26-899Z/` 包含 result.json、deployment.json、python.log、部署前私有备份与验证 Flow。源码哈希在运行前后核对一致。首次 HTTP 节点注册时序失败单独保留,修正测试工具等待路由就绪后本轮通过。新功能尚未发布、未上传 MUSE;MUSE 的 Python/OpenSSL、证书部署与真实设备协议需要独立验收。