diff --git a/CHANGELOG.md b/CHANGELOG.md
index cea3b3b..9a71f01 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,11 @@
### 新增
+- 公共 HsmsConnectionOptions 增加工程参考计时器值
+ ReferenceT3–ReferenceT8 与
+ CreateWithReferenceTimers 便捷工厂;参考值与 MIT 许可的
+ Secs4Net 实现及通行 HSMS-SS 部署实践交叉参照,未经授权 E37/E37.1 核对,
+ 不构成标准默认值声明,显式构造路径保持不变。
- 增加本机 SecsFrame.CommunicationDemo 通讯测试工具,提供
Active/Passive 显式连接配置、真实 TCP 回环、严格 SML 编辑与样例、
数据/控制消息操作,以及最多 500 条的会话内活动和结构化诊断视图。
diff --git a/docs/HSMS-CONNECTION.md b/docs/HSMS-CONNECTION.md
index 8dae7ad..9e241f6 100644
--- a/docs/HSMS-CONNECTION.md
+++ b/docs/HSMS-CONNECTION.md
@@ -45,6 +45,89 @@ Host/Equipment 是后续业务能力角色,不由 Active/Passive 推断。
整毫秒范围;Passive 监听重试不被解释成 T5。每个取值应依据团队合法获得
的 E37/E37.1 版本、设备通信手册和互操作证据确定。
+## 应用配置绑定示例
+
+库本身不依赖任何配置框架,也不读取应用配置文件;下面的绑定代码属于
+宿主应用,展示在 ASP.NET Core 或通用主机中从 appsettings.json
+构造连接选项的一种推荐方式。
+
+~~~json
+{
+ "Hsms": {
+ "IpAddress": "192.0.2.10",
+ "Port": 5000,
+ "ConnectionMode": "Active",
+ "SessionId": 0,
+ "T3": "00:00:45",
+ "T5": "00:00:10",
+ "T6": "00:00:05",
+ "T7": "00:00:10",
+ "T8": "00:00:05"
+ }
+}
+~~~
+
+计时键使用标准 TimeSpan 文本格式,配置绑定器可以直接转换。
+把计时键声明为可空并从配置中省略,即可回退到工程参考值;参考值的来源
+与非标准核对口径见
+[STREAMFRAME-ADAPTER.md](STREAMFRAME-ADAPTER.md)。
+
+~~~csharp
+public sealed class HsmsConnectionSettings
+{
+ public string IpAddress { get; set; } = "";
+ public int Port { get; set; }
+ public HsmsConnectionMode ConnectionMode { get; set; }
+ public ushort SessionId { get; set; }
+ public TimeSpan? T3 { get; set; }
+ public TimeSpan? T5 { get; set; }
+ public TimeSpan? T6 { get; set; }
+ public TimeSpan? T7 { get; set; }
+ public TimeSpan? T8 { get; set; }
+
+ public HsmsConnectionOptions ToConnectionOptions()
+ {
+ if (string.IsNullOrWhiteSpace(IpAddress))
+ {
+ throw new InvalidOperationException(
+ "The Hsms:IpAddress configuration value is required.");
+ }
+
+ return new HsmsConnectionOptions(
+ IPAddress.Parse(IpAddress),
+ Port,
+ ConnectionMode,
+ SessionId,
+ T3 ?? HsmsConnectionOptions.ReferenceT3,
+ T5 ?? HsmsConnectionOptions.ReferenceT5,
+ T6 ?? HsmsConnectionOptions.ReferenceT6,
+ T7 ?? HsmsConnectionOptions.ReferenceT7,
+ T8 ?? HsmsConnectionOptions.ReferenceT8);
+ }
+}
+~~~
+
+在主机启动时绑定并注册,让非法输入在启动期立即失败:
+
+~~~csharp
+builder.Services.AddSingleton(_ =>
+{
+ var settings = builder.Configuration
+ .GetSection("Hsms")
+ .Get()
+ ?? throw new InvalidOperationException(
+ "The 'Hsms' configuration section is missing.");
+
+ return settings.ToConnectionOptions();
+});
+~~~
+
+HsmsConnectionOptions 构造函数继续承担全部边界校验:端口
+区间、控制 Session ID、T5/T8 的整毫秒与范围约束在绑定时立即抛出参数
+异常,宿主应用应让它在启动期中断而不是捕获吞掉。该示例只组装启动
+参数,不改变连接生命周期——修改配置后需要构造新的选项和连接,已建立
+的连接不支持运行中调整计时器。
+
## 生命周期
~~~csharp
diff --git a/docs/STREAMFRAME-ADAPTER.md b/docs/STREAMFRAME-ADAPTER.md
index 6a18d07..eaa782e 100644
--- a/docs/STREAMFRAME-ADAPTER.md
+++ b/docs/STREAMFRAME-ADAPTER.md
@@ -86,7 +86,11 @@ StreamFrame 的 SessionExpiredException 在内部边界转换为
## T5 与 T8
内部 HsmsTransportOptions 要求调用方显式给出 T5 与 T8,
-不内置未经授权标准核对的默认值。Active 连接把 T5 映射为固定的
+不内置未经授权标准核对的默认值。公共 HsmsConnectionOptions
+另提供显式标注来源的 ReferenceT3–ReferenceT8 与
+CreateWithReferenceTimers 工厂;这些工程参考值与 MIT 许可的
+Secs4Net 实现及通行部署实践交叉参照,未经授权标准核对,也不改变显式
+构造路径的必填语义。Active 连接把 T5 映射为固定的
ConnectRetryDelayMs 并关闭最大退避;Passive 监听重试配置
保持调用方设置。原 StreamConnectionOptions 始终复制后再
适配,不会被原地修改。
diff --git a/src/SecsFrame/Hsms/Connection/HsmsConnectionOptions.cs b/src/SecsFrame/Hsms/Connection/HsmsConnectionOptions.cs
index c550145..3b2eed7 100644
--- a/src/SecsFrame/Hsms/Connection/HsmsConnectionOptions.cs
+++ b/src/SecsFrame/Hsms/Connection/HsmsConnectionOptions.cs
@@ -11,6 +11,46 @@ public sealed class HsmsConnectionOptions
/// The default number of retained T8 observations.
public const int DefaultTransportFaultObservationCapacity = 16;
+ /// The engineering reference T3 reply timeout, 45 seconds.
+ ///
+ /// Cross-referenced with the MIT-licensed Secs4Net implementation and
+ /// common HSMS-SS deployment practice; not verified against an authorized
+ /// SEMI E37/E37.1 copy and not a claim of standard default values.
+ ///
+ public static readonly TimeSpan ReferenceT3 = TimeSpan.FromSeconds(45);
+
+ /// The engineering reference T5 connect retry interval, 10 seconds.
+ ///
+ /// Cross-referenced with the MIT-licensed Secs4Net implementation and
+ /// common HSMS-SS deployment practice; not verified against an authorized
+ /// SEMI E37/E37.1 copy and not a claim of standard default values.
+ ///
+ public static readonly TimeSpan ReferenceT5 = TimeSpan.FromSeconds(10);
+
+ /// The engineering reference T6 control reply timeout, 5 seconds.
+ ///
+ /// Cross-referenced with the MIT-licensed Secs4Net implementation and
+ /// common HSMS-SS deployment practice; not verified against an authorized
+ /// SEMI E37/E37.1 copy and not a claim of standard default values.
+ ///
+ public static readonly TimeSpan ReferenceT6 = TimeSpan.FromSeconds(5);
+
+ /// The engineering reference T7 selection timeout, 10 seconds.
+ ///
+ /// Cross-referenced with the MIT-licensed Secs4Net implementation and
+ /// common HSMS-SS deployment practice; not verified against an authorized
+ /// SEMI E37/E37.1 copy and not a claim of standard default values.
+ ///
+ public static readonly TimeSpan ReferenceT7 = TimeSpan.FromSeconds(10);
+
+ /// The engineering reference T8 incomplete-message timeout, 5 seconds.
+ ///
+ /// Cross-referenced with the MIT-licensed Secs4Net implementation and
+ /// common HSMS-SS deployment practice; not verified against an authorized
+ /// SEMI E37/E37.1 copy and not a claim of standard default values.
+ ///
+ public static readonly TimeSpan ReferenceT8 = TimeSpan.FromSeconds(5);
+
/// Creates explicit HSMS connection settings.
/// The remote address in Active mode or local bind address in Passive mode.
/// The TCP port, from 1 through 65535.
@@ -97,6 +137,53 @@ public HsmsConnectionOptions(
TransportFaultObservationCapacity = transportFaultObservationCapacity;
}
+ ///
+ /// Creates connection settings that fill T3 through T8 with the
+ /// engineering reference values.
+ ///
+ /// The remote address in Active mode or local bind address in Passive mode.
+ /// The TCP port, from 1 through 65535.
+ /// Which peer initiates the TCP connection.
+ /// The protocol Session ID used by outgoing data messages.
+ ///
+ /// Enables the separate restricted-metadata control-message observation stream.
+ ///
+ ///
+ /// Enables the separate bounded T8 prefix-snapshot observation stream.
+ ///
+ ///
+ /// The maximum queued T8 observations before the oldest item is discarded.
+ ///
+ ///
+ /// The reference timers are cross-referenced with the MIT-licensed
+ /// Secs4Net implementation and common HSMS-SS deployment practice. They
+ /// have not been verified against an authorized SEMI E37/E37.1 copy and
+ /// are not standard default values; callers that need audited values
+ /// must use the explicit constructor.
+ ///
+ public static HsmsConnectionOptions CreateWithReferenceTimers(
+ IPAddress ipAddress,
+ int port,
+ HsmsConnectionMode connectionMode,
+ ushort sessionId,
+ bool enableControlMessageObservation = false,
+ bool enableTransportFaultObservation = false,
+ int transportFaultObservationCapacity =
+ DefaultTransportFaultObservationCapacity)
+ => new(
+ ipAddress,
+ port,
+ connectionMode,
+ sessionId,
+ ReferenceT3,
+ ReferenceT5,
+ ReferenceT6,
+ ReferenceT7,
+ ReferenceT8,
+ enableControlMessageObservation,
+ enableTransportFaultObservation,
+ transportFaultObservationCapacity);
+
/// Gets the remote or local bind IP address.
public IPAddress IpAddress { get; }
diff --git a/test/SecsFrame.Tests/HsmsConnectionOptionsTests.cs b/test/SecsFrame.Tests/HsmsConnectionOptionsTests.cs
index ec50948..60605c5 100644
--- a/test/SecsFrame.Tests/HsmsConnectionOptionsTests.cs
+++ b/test/SecsFrame.Tests/HsmsConnectionOptionsTests.cs
@@ -174,6 +174,79 @@ public void T8_beyond_StreamFrame_range_is_rejected()
(long)int.MaxValue + 1)));
}
+ [Fact]
+ public void Reference_timers_match_commonly_deployed_values()
+ {
+ Assert.Equal(
+ TimeSpan.FromSeconds(45),
+ HsmsConnectionOptions.ReferenceT3);
+ Assert.Equal(
+ TimeSpan.FromSeconds(10),
+ HsmsConnectionOptions.ReferenceT5);
+ Assert.Equal(
+ TimeSpan.FromSeconds(5),
+ HsmsConnectionOptions.ReferenceT6);
+ Assert.Equal(
+ TimeSpan.FromSeconds(10),
+ HsmsConnectionOptions.ReferenceT7);
+ Assert.Equal(
+ TimeSpan.FromSeconds(5),
+ HsmsConnectionOptions.ReferenceT8);
+ }
+
+ [Fact]
+ public void Reference_timer_factory_fills_all_timers()
+ {
+ var options = HsmsConnectionOptions.CreateWithReferenceTimers(
+ IPAddress.Loopback,
+ 5000,
+ HsmsConnectionMode.Passive,
+ 10);
+
+ Assert.Equal(IPAddress.Loopback, options.IpAddress);
+ Assert.Equal(5000, options.Port);
+ Assert.Equal(HsmsConnectionMode.Passive, options.ConnectionMode);
+ Assert.Equal(10, options.SessionId);
+ Assert.Equal(HsmsConnectionOptions.ReferenceT3, options.T3);
+ Assert.Equal(HsmsConnectionOptions.ReferenceT5, options.T5);
+ Assert.Equal(HsmsConnectionOptions.ReferenceT6, options.T6);
+ Assert.Equal(HsmsConnectionOptions.ReferenceT7, options.T7);
+ Assert.Equal(HsmsConnectionOptions.ReferenceT8, options.T8);
+ Assert.False(options.EnableControlMessageObservation);
+ Assert.False(options.EnableTransportFaultObservation);
+ Assert.Equal(
+ HsmsConnectionOptions.DefaultTransportFaultObservationCapacity,
+ options.TransportFaultObservationCapacity);
+ }
+
+ [Fact]
+ public void Reference_timer_factory_passes_through_observation_settings()
+ {
+ var options = HsmsConnectionOptions.CreateWithReferenceTimers(
+ IPAddress.Loopback,
+ 5000,
+ HsmsConnectionMode.Active,
+ 10,
+ enableControlMessageObservation: true,
+ enableTransportFaultObservation: true,
+ transportFaultObservationCapacity: 7);
+
+ Assert.True(options.EnableControlMessageObservation);
+ Assert.True(options.EnableTransportFaultObservation);
+ Assert.Equal(7, options.TransportFaultObservationCapacity);
+ }
+
+ [Fact]
+ public void Reference_timer_factory_applies_the_same_input_validation()
+ {
+ Assert.Throws(() =>
+ HsmsConnectionOptions.CreateWithReferenceTimers(
+ IPAddress.Loopback,
+ port: 0,
+ HsmsConnectionMode.Active,
+ 10));
+ }
+
private static HsmsConnectionOptions Create(
IPAddress? ipAddress = null,
int port = 5000,