Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@

### 新增

- 增加默认关闭的独立 T8 transport fault 观测流,保留 StreamFrame TCP
Session ID、actor 状态与最多 8 KiB 的未完成帧前缀快照。
- T8 观测队列容量显式可配且满时丢弃最旧项,不阻塞协议 actor;默认路径
不创建该通道,旧会话观测不会归入替换会话。
- 增加独立 <code>SecsFrame-TransportFaultTrace/1</code> 信封,沿用
MetadataOnly、RedactedPayload 与 RawPayload 分级,payload codec 默认关闭。
- 增加真实 TCP T8、Session 过滤、队列容量、黄金向量、脱敏复核、防御性
复制和资源限制测试;快照不声明完整 TCP 片段且不能进入重放路径。
- 增加独立 <code>SecsFrame-FaultSampleTrace/1</code> 信封,为公共
<code>DataMessageDecodeFailed</code> 事件提供已成帧 HSMS data body 样本。
- 捕获必须显式选择 MetadataOnly、RedactedPayload 或 RawPayload;默认 codec
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@ Equipment 应用发送策略的 Collection Event、报警目录查询、单报
显式启用的完整控制消息观测流或既有未认领控制事件的十字节头元数据。
解码失败样本另用独立信封,并以元数据为默认导出边界;Body 捕获与 codec
导入导出都必须显式开启,声明的脱敏字节范围会固定清零并严格复核。
控制观测默认关闭,并与业务事件流独立;该包不会自行接管任何连接消费者,
也不会复用原事务标识。
T8 互操作诊断另有默认关闭、容量有界的独立观测流;它保留 transport
Session ID 和最多 8 KiB 的未完成帧前缀快照。Trace 默认仍只导出元数据,
快照捕获和 payload codec 必须分别显式开启。控制与 T8 观测都和业务事件流
独立;该包不会自行接管任何连接消费者,也不会复用原事务标识。
Item 用法与边界见
[docs/SECS-II-ITEMS.md](docs/SECS-II-ITEMS.md),消息集成见
[docs/SECS-MESSAGES.md](docs/SECS-MESSAGES.md),传输适配边界见
Expand Down
13 changes: 11 additions & 2 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ Trace 的时序重放必须显式启用,只在允许发送记录之间等待
该流并创建记录,不自行订阅连接。既有公共未认领控制事件工厂继续可用。
已成帧但 SECS-II Body 解码失败的样本使用独立信封:捕获和 codec 分别要求
显式数据边界,默认只允许头与原 Body 长度;payload-bearing 记录不能进入
重放。T8 和成帧失败片段尚未进入该模型。
重放。T8 使用另一个独立信封:核心必须先显式启用容量有界的观测流,Trace
再选择元数据、脱敏或原始前缀快照;快照最多 8 KiB 且不声明完整 TCP 片段。
其他成帧错误仍未进入该模型。

## 核心原则

Expand Down Expand Up @@ -77,7 +79,8 @@ HSMS 会话切换后,旧会话的控制请求、数据事务和等待中的应
释放的 StreamFrame 生命周期;
- Active 连接把显式 T5 映射为固定 StreamFrame 重试间隔,关闭指数退避;
- 显式 T8 映射为 StreamFrame 原生未完成帧超时,并把对应 FrameError
转换为带 transport Session ID 的 HSMS 关闭原因。
转换为带 transport Session ID 的 HSMS 关闭原因;显式启用时还在同一
transport 事件序列中复制有界前缀快照。

这些能力仍隔离在 <code>IHsmsTransport</code> 后,不进入公共 API。此前
跟踪的 StreamFrame #38/#39 已在 2.3.0 完成迁移,2.3.1 进一步封闭迟到
Expand Down Expand Up @@ -138,6 +141,12 @@ Selected 转发数据消息;提前到达的数据、可识别的不支持类
通道默认不创建,记录不含 Body、原始字节、transport Session generation
或时间戳;它是诊断元数据边界,不参与协议决策。

可选 T8 观测通道同样由 actor 写入,但使用容量有界、满时丢弃最旧项的
独立队列,避免未消费的原始快照形成无界内存或阻塞协议处理。状态机只接受
当前 transport Session ID 的 fault;观测保留最多 8 KiB 前缀,不参与关闭
决策。StreamFrame 2.5.0 的其他 FrameError 不带 session/epoch,核心不会
用读取“当前 ID”的方式把它们冒充为严格会话关联事件。

### 会话绑定的数据事务

内部 <code>HsmsDataTransactionManager</code> 独占消费会话事件,并把
Expand Down
27 changes: 26 additions & 1 deletion docs/HSMS-CONNECTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ StreamFrame 运输适配、HSMS-SS 会话 actor 和数据事务 actor,调用
- 接收入站 Primary 或未匹配消息,并使用不可伪造令牌回复一次;
- Linktest、Deselect 和 Separate 控制命令;
- 状态、数据、未消费控制消息和解码失败的单消费者异步事件流;
- 默认关闭、与业务事件独立的完整控制消息头元数据观测流。
- 默认关闭、与业务事件独立的完整控制消息头元数据观测流;
- 默认关闭、容量有界的 T8 transport Session 前缀快照观测流。

Host/Equipment 是后续业务能力角色,不由 Active/Passive 推断。
需要按运行期 Stream/Function 处理 Primary 时,可在本事件流上组合
Expand All @@ -37,6 +38,8 @@ Host/Equipment 是后续业务能力角色,不由 Active/Passive 推断。
| T7 | TCP 建立后等待进入 Selected |
| T8 | 未完成消息接收进展等待 |
| EnableControlMessageObservation | 是否创建完整控制消息元数据观测流;默认 false |
| EnableTransportFaultObservation | 是否创建 T8 前缀快照观测流;默认 false |
| TransportFaultObservationCapacity | T8 队列容量;满时丢弃最旧项,默认 16 |

仓库没有内置或暗示标准默认值。T5 必须能无损转换成 StreamFrame 使用的
整毫秒范围;Passive 监听重试不被解释成 T5。每个取值应依据团队合法获得
Expand Down Expand Up @@ -123,6 +126,28 @@ await foreach (var observation in connection
transport Session generation 或时间戳,也不改变未认领
<code>ControlMessageReceived</code> 业务事件的既有行为。

T8 原始诊断必须另外显式设置
<code>EnableTransportFaultObservation</code>。该流不占用业务事件消费者:

~~~csharp
await foreach (var fault in connection
.GetTransportFaultObservationsAsync(cancellationToken))
{
var record = SecsTraceTransportFaultRecord.Create(
DateTimeOffset.UtcNow,
fault,
SecsTraceTransportFaultCaptureOptions.MetadataOnly());
}
~~~

<code>HsmsTransportFaultObservation</code> 保留
<code>IncompleteFrameTimeout</code>、StreamFrame TCP Session ID、actor
观察状态和防御性复制的前缀快照。快照最多 8 KiB,可能包含四字节 HSMS
长度前缀;StreamFrame 2.5.0 不报告原缓冲总长度或完整性,因此不得把它
解释成完整 TCP 片段。队列按配置容量保留最新项,满时丢弃最旧项而不阻塞
协议 actor;应用启用后应持续消费。该观测在对应会话关闭事件处理前写入,
但两个独立异步流之间不承诺消费者可见的全局顺序。

带错误的状态事件和解码失败事件还会提供可选
<code>HsmsDiagnostic</code>,用于按稳定代码、层级、操作与计时器处理,
无需解析异常消息。调用任务抛出的异常可通过
Expand Down
7 changes: 7 additions & 0 deletions docs/HSMS-DIAGNOSTICS.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ System Bytes 和远端状态字节仍属于运维元数据,导出文件仍应
带 Body 的导入或导出都要再次显式开启。该信封不包含异常、TCP 长度前缀、
分片、transport Session generation 或 T8 未完成片段。

T8 另有默认关闭的 <code>HsmsTransportFaultObservation</code> 独立流和
<code>SecsFrame-TransportFaultTrace/1</code> 信封。观测只保留当前 T8 所属
transport Session ID、actor 状态和 StreamFrame 已复制的最多 8 KiB 前缀;
队列有界且满时丢弃最旧项。Trace 捕获必须再选择仅元数据、声明范围清零或
原始快照,默认 codec 仍拒绝带快照的记录。它不包含异常、原缓冲总长度、
完整性标记或分片时序,也不能用于重放。

## 标准边界

诊断名称描述 SecsFrame 已实现并测试的工程行为,不新增 SEMI 默认值,也
Expand Down
12 changes: 9 additions & 3 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ E30-0526 材料。下一可执行优先级已转入阶段 3;SML 调试读写
- [x] 公共未认领控制消息的受限头元数据 Trace 导出和严格读取。
- [x] 默认关闭、不含 Body/原始字节的完整控制面头元数据观测。
- [x] 显式分级、默认不导出 Body 的数据消息解码失败样本。
- T8/成帧失败原始片段等运输层互操作诊断扩展。
- [x] 默认关闭、有界且显式分级的 T8 前缀快照诊断。
- 其他成帧失败原始片段等运输层互操作诊断扩展。

当前 Trace 基线不接管连接事件流,也不把原始标识写回线上。时序重放默认
关闭,并继续经过显式 allowlist 与公共发送 API。诊断快照始终排除
Expand All @@ -124,8 +125,13 @@ Body、原始线上字节、transport generation 或时间戳。后续故障原
已先覆盖公共事件中忠实保留的完整 data frame:捕获分为 MetadataOnly、
RedactedPayload 与 RawPayload,payload codec 默认关闭,声明脱敏范围必须
全为零且可严格复核。它不包含 TCP 长度前缀或分片,也不能进入重放。
T8/成帧失败片段仍需先把 StreamFrame 原始错误数据与 transport session
安全关联,并保持默认不捕获、容量限制和跨会话隔离。
T8 现通过默认关闭的独立流与当前 transport Session ID 关联;队列容量有界、
满时丢弃最旧项,StreamFrame 提供的最多 8 KiB 前缀再按 MetadataOnly、
RedactedPayload 或 RawPayload 捕获,payload codec 默认关闭。该前缀可能含
四字节长度头,但上游不报告原缓冲总长度或完整性,不能解释为完整 TCP 片段,
也不能进入重放。其他 DecodeFailed、IncompleteFrameOverflow 与
DiscardedByResync 仍需要 StreamFrame 在 FrameError 中提供原始 session/epoch
归属后再扩展,不能用回调时读取当前 Session ID 替代严格关联。
E173 SMN 与 E172 SEDD 仍需先核对授权标准及允许分发的 Schema/表示形式。

## 发布门槛
Expand Down
20 changes: 15 additions & 5 deletions docs/STREAMFRAME-ADAPTER.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ ID 一样在 Connected 发布点分配,闭合状态已经对外可见但会话
[PR #52](https://github.com/CSJ608/StreamFrame/pull/52)。

SecsFrame 不再维护基于原始字节回调的 T8 监视器、发送确认计数器、会话
信封 codec 或自建 TCP Session ID。公共 API 和内部
<code>IHsmsTransport</code> 契约保持不变,上层 HSMS 状态机不直接依赖
信封 codec 或自建 TCP Session ID。内部 <code>IHsmsTransport</code> 现可在
显式启用时转发 T8 的会话关联前缀快照;上层 HSMS 状态机仍不直接依赖
StreamFrame 类型。

## 会话边界
Expand Down Expand Up @@ -83,13 +83,23 @@ StreamFrame 只在已有未完成帧时运行该超时:空闲连接和完整
触发;接收进展重置计时;到期先发布
<code>FrameErrorKind.IncompleteFrameTimeout</code>,再拆除对应会话。
适配器把该诊断转换为携带 transport Session ID 的
<code>HsmsT8TimeoutException</code>,不再次请求重连。
<code>HsmsT8TimeoutException</code>,不再次请求重连。公共连接另提供默认
关闭的独立 T8 观测流:适配器在错误回调同步执行期间验证当前 Session ID,
先复制 <code>FrameErrorEventArgs.Bytes</code>,再由会话 actor 过滤旧 Session
并附加观察状态。默认路径不发布该 transport fault 事件。

StreamFrame 2.5.0 的 T8 Bytes 是已复制、最多 8 KiB 的缓冲前缀,可能包含
四字节 HSMS 长度头;API 不提供原缓冲总长度或完整性,SecsFrame 因而只称
其为前缀快照。观测通道容量显式可配,满时丢弃最旧项,不能反向阻塞协议
actor。DecodeFailed、IncompleteFrameOverflow 和 DiscardedByResync 的公共
FrameError 不携带 session/epoch,本切片不把它们映射成会话关联观测。

## 验证边界

适配器单元测试覆盖原生 Session ID、迟到消息、发送完成、并发入队、会话
失效、显式关闭原因、关闭/释放竞态和 T8 诊断映射。真实 TCP 回环测试覆盖
分片帧收发、实际 T8 到期,以及会话替换时排队发送的精确失效与新 Socket
失效、显式关闭原因、关闭/释放竞态、T8 诊断映射、快照复制、旧会话过滤和
有界队列。真实 TCP 回环测试覆盖分片帧收发、实际 T8 到期及前缀快照,以及
会话替换时排队发送的精确失效与新 Socket
内容隔离;Passive 重连竞态测试反复并发对端关闭与本地显式关闭,验证监听
恢复、Session ID 单调递增和原会话精确关闭。完整套件继续覆盖 T3/T6 从
发送完成点启动以及旧会话不重放。
Expand Down
69 changes: 65 additions & 4 deletions docs/TRACE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
- 显式启用的完整控制观测或公共未认领控制事件的十字节头元数据导出与
严格读取。
- 已成帧但 SECS-II Body 解码失败的显式分级故障样本。
- 显式启用、会话关联且容量有界的 T8 前缀快照分级导出。

当前切片不是原始网络抓包器,不记录 TCP 字节、transport Session generation、
控制帧的线上原始字节或异常对象,也不会接管 <code>HsmsConnection</code> 的
单消费者事件流。
当前切片不是原始网络抓包器;除显式启用的 T8 有界前缀外,不记录 TCP
字节、transport Session generation、控制帧的线上原始字节或异常对象,
也不会接管 <code>HsmsConnection</code> 的单消费者事件流。
应用应在自己的事件循环中从 <code>HsmsIncomingDataMessage</code> 创建接收记录,
并在调用发送 API 前显式创建发送记录。

Expand Down Expand Up @@ -184,6 +185,64 @@ payload 捕获默认限制为 64 KiB;超限立即失败,不截断样本。
未完成片段,也没有重放 API。清零范围可验证,但哪些范围包含敏感数据仍是
调用方的数据分类责任;未经独立审查不得把 Redacted 等同于已安全公开。

## T8 前缀快照

连接必须先显式启用独立且容量有界的 T8 观测流;Trace 不自动订阅:

~~~csharp
var options = new HsmsConnectionOptions(
ipAddress,
port,
HsmsConnectionMode.Passive,
sessionId,
t3,
t5,
t6,
t7,
t8,
enableTransportFaultObservation: true,
transportFaultObservationCapacity: 16);

await foreach (var fault in connection
.GetTransportFaultObservationsAsync(cancellationToken))
{
var record = SecsTraceTransportFaultRecord.Create(
DateTimeOffset.UtcNow,
fault,
SecsTraceTransportFaultCaptureOptions.MetadataOnly());

var text = new SecsTraceTransportFaultCodec().Encode(
new[] { record });
}
~~~

观测保留 <code>IncompleteFrameTimeout</code>、transport Session ID、actor
状态和 StreamFrame 已复制的前缀。通道满时丢弃最旧项而不阻塞协议 actor;
默认关闭时不创建通道。快照最多 8 KiB,可能包含四字节 HSMS 长度前缀,
但 StreamFrame 2.5.0 不提供原缓冲总长度或完整性标记,因此不得宣称它是
完整 TCP 片段。

<code>SecsTraceTransportFaultCaptureOptions</code> 同样要求明确选择
MetadataOnly、RedactedPayload 或 RawPayload。payload 模式不允许截断,
超过调用方收紧后的限制立即失败;Redacted 范围相对快照起点,必须有序、
非重叠、不越界并在记录中全部为零。记录和公共观测都防御性复制字节。

独立信封使用九个单空格分隔字段:

~~~text
SecsFrame-TransportFaultTrace/1
TransportFault 2026-08-29T05:00:00.0000000Z IncompleteFrameTimeout Connected 17 RedactedPayload 10 4:6 0000000A000000000000
~~~

字段依次为 UTC 时间、fault kind、HSMS 状态、transport Session ID、数据
分类、StreamFrame 实际提供的快照长度、脱敏范围和大写十六进制快照。默认
codec 只允许 MetadataOnly;带快照的导入和导出必须再次显式启用。信封不含
异常、原缓冲总长度、分片时序或完整性推断,也没有重放 API。

当前只接受 T8。StreamFrame 2.5.0 的其他 FrameError 不携带原始 session/epoch,
SecsFrame 不会以回调时读取当前 Session ID 的方式把它们包装成严格会话关联
样本。

## 结构化脱敏

脱敏发生在不可变 Item 树上,而不是对 SML 文本做字符串替换:
Expand Down Expand Up @@ -278,4 +337,6 @@ var results = await new SecsTraceReplayer().ReplayWithTimingAsync(
测试。控制信封另有完整 Select/Linktest 双向观测、未匹配 Reject 黄金向量、
未知 SType 往返、Data Message 拒绝、严格字段与资源限制测试。故障样本
信封另有三种分类、payload 默认拒绝、范围清零复核、防御性复制、畸形字段
和 Body/记录/文本资源限制测试。
和 Body/记录/文本资源限制测试。T8 信封另有真实 TCP 前缀、旧 Session
过滤、有界队列、三种分类、默认拒绝、脱敏复核、畸形字段和快照/记录/文本
资源限制测试。
2 changes: 1 addition & 1 deletion src/SecsFrame.Trace/SecsTraceByteRedactionRange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace SecsFrame.Trace;

/// <summary>Identifies a zero-based range within an HSMS data-message body.</summary>
/// <summary>Identifies a zero-based range within a fault-sample byte payload.</summary>
[StructLayout(LayoutKind.Auto)]
public readonly record struct SecsTraceByteRedactionRange
{
Expand Down
8 changes: 4 additions & 4 deletions src/SecsFrame.Trace/SecsTraceFaultSampleDataClassification.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
namespace SecsFrame.Trace;

/// <summary>Identifies how much payload data a fault-sample record contains.</summary>
/// <summary>Identifies how much byte content a fault-sample record contains.</summary>
public enum SecsTraceFaultSampleDataClassification
{
/// <summary>Only the HSMS header and original body length are retained.</summary>
/// <summary>Only stable metadata and the observed byte length are retained.</summary>
MetadataOnly,

/// <summary>The body is retained after declared byte ranges are zeroed.</summary>
/// <summary>The bytes are retained after declared ranges are zeroed.</summary>
RedactedPayload,

/// <summary>The original body bytes are retained without redaction.</summary>
/// <summary>The original observed bytes are retained without redaction.</summary>
RawPayload,
}
Loading
Loading