补充总召唤的广播地址,电度召唤+时钟同步业务的实现
CI / L0 静态门禁 (pre-commit) (push) Failing after 44s
CI / L0.5 主机侧单测 (MSVC) (push) Failing after 42s

This commit is contained in:
STF
2026-10-29 03:20:19 -07:00
parent 4c55544f05
commit 7596f3371a
267 changed files with 130894 additions and 1436 deletions
@@ -1,6 +1,6 @@
# IEC 60870-5-101/104 公共时间工具
`IEC60870_Time.c/h` 为 IEC 60870-5-101 和 IEC 60870-5-104 提供共用的公历计算、CP16/CP24/CP56Time2a 编解码、本地标准时间转换及纪元秒数工具。时间源模块集中在`IEC60870_Clock.c/h`:Clock.c包含公共校验和Windows读写,Clock.h包含业务接口及RTC预留声明。时间源限定为Windows系统时钟或RTC,由`IEC60870_CLOCK_USE_RTC`在编译时二选一;RTC具体芯片驱动暂不实现,也不规划其他操作系统适配。时间相关的 ASDU 业务入口尚未接入;下文约定后续业务使用本地标准时间,并给出调用示例。原始解码保留源日历和标志,业务再调用转换接口取得本地标准时间日历。公共模块不维护连接状态、共享可变状态或动态内存,可独立于协议链路编译和链接;时间戳转日期可选择平台库或保留的纯整数算法。
`IEC60870_Time.c/h` 为 IEC 60870-5-101 和 IEC 60870-5-104 提供共用的公历计算、CP16/CP24/CP56Time2a 编解码、本地标准时间转换及纪元秒数工具。时间源模块集中在`IEC60870_Clock.c/h`:Clock.c包含公共校验和Windows读写,Clock.h包含业务接口及RTC预留声明。时间源限定为Windows系统时钟或RTC,由`IEC60870_CLOCK_USE_RTC`在编译时二选一;RTC具体芯片驱动暂不实现,也不规划其他操作系统适配。Type 103业务已由独立的[ClockSynchronization模块](../104/IEC104_ClockSynchronization_设计架构.md)接入,应用显式执行设时动作;公共Time/Clock仍不自行分派ASDU。原始解码保留源日历和标志,业务再调用转换接口取得本地标准时间日历。公共模块不维护连接状态、共享可变状态或动态内存,可独立于协议链路编译和链接;时间戳转日期可选择平台库或保留的纯整数算法。
## 101/104 的共用边界
@@ -14,7 +14,7 @@
IEC 60870-5-101:2003/AMD1:2015 第7.2.6.18条将SU规定为可选且不推荐使用的功能;如果选用,SU=1与时间值早一小时且SU=0的标签表示同一时刻。第7.4.6条要求不使用夏令时时将SU置0。参见[标准修订单第4至6页](https://preview.sist.si/sist-preview/22451/3a4cae004abe434883eba6133f897d87/IEC-60870-5-101-2003-AMD1-2015.pdf)。
**后续业务发送入口负责保证:使用本地标准时间(固定UTC+8),并显式设置`is_summer_time=0U`。** 公共编解码继续支持SU=0和1,按输入保留合法的SU值;公共编码接口不强制SU=0,也不会自动清除SU=1。该发送约束由后续业务入口落实,目前文档中的发送函数仅为调用示例。
**应用新生成的业务时标使用本地标准时间(固定UTC+8),并显式设置`is_summer_time=0U`。** 公共编解码继续支持SU=0和1,按输入保留合法的SU值;公共编码接口不强制SU=0,也不会自动清除SU=1。Type 103的ACT_CON属于原请求回显,保留原CP56及SU,不套用新生成时标的SU=0规则。本文中的通用发送函数仍为调用示例。
业务发送时,输入日历须已经是本地标准时间。若来源是夏令时日历,应先完成时间转换,再以SU=0编码,不能只把SU清零而保留夏令时的小时值。接收时保留SU=1的兼容处理:原始解码后调用`IEC60870_Time_CP56_To_Local`,取得已处理SU的本地标准时间日历;接收兼容不改变本地发送SU=0的约定。
@@ -30,7 +30,7 @@ IEC 60870-5-101:2003/AMD1:2015 第7.2.6.18条将SU规定为可选且不推荐使
| 业务发送示例 | 调用方提供已是本地标准时间的日历,示例显式设置SU=0,IV由零初始化保持为0 |
| 业务接收示例 | `From_CP56 → CP56_To_Local`,直接取得本地标准时间日历 |
`From_CP56`只保留源日历及SU;`CP56_To_Local`读取完整解析值,输出已经修正为本地标准时间的`IEC60870_WallTime_t`。**调用方不需要再加八小时或再减夏令时的一小时。** 使用独立输出存储时,原始输入不改写;若输入与输出重叠,成功写回会覆盖重叠的存储,调用方须按转换后的时间基准使用结果,再次编码时构造新的CP56值并置SU=0。目前仓库尚未提供实际的校时业务处理器,下文给出本地标准时间收发的调用方式。
`From_CP56`只保留源日历及SU;`CP56_To_Local`读取完整解析值,输出已经修正为本地标准时间的`IEC60870_WallTime_t`。**调用方不需要再加八小时或再减夏令时的一小时。** 使用独立输出存储时,原始输入不改写;若输入与输出重叠,成功写回会覆盖重叠的存储,调用方须按转换后的时间基准使用结果,再次生成时标时构造新的CP56值并置SU=0。ClockSynchronization使用`CP56_To_Seconds`取得UTC秒数,在64位毫秒域补偿受理后的滞留,再调用`Seconds_To_Local`;确认直接回显原请求,不重编码补偿后的日历。
转换接口的`source_standard_utc_offset_minutes`表示“源报文标准当地时间减UTC”的分钟数,向东为正、向西为负。双方约定本地标准时间时传`IEC60870_LOCAL_UTC_OFFSET_MINUTES`(480);源报文为UTC时传0,源标准时区为UTC-5时传-300。-1439..1439是本接口约定的参数范围;输出时区始终是本地标准时间。
@@ -64,8 +64,8 @@ SU本身不携带时区信息;源偏移须来自双方约定。这里的“本
| CP56转本地标准时间,检查IV、处理SU和源时区,输出完整日历 | `IEC60870_Time_CP56_To_Local` |
| 统一读时、设时入口,校验日历、重算星期并检查时间质量标志 | `IEC60870_Clock` |
| Windows系统时钟读写,UTC与固定UTC+8日历互换 | `IEC60870_Clock.c`中的Windows分支 |
| 发送前保证日历已是本地标准时间,并统一设置SU=0 | ASDU 业务层,后续业务发送入口落实 |
| 事件时间戳、校时 ASDU 的业务处理,确定时间约定并调用转换接口 | ASDU 业务层 |
| 新生成时标使用本地标准时间并设置SU=0;请求回显保留原时标 | ASDU业务层,Type 103确认由ClockSynchronization按原文生成 |
| 校时ASDU校验、期限、滞留补偿及确认 | `IEC104_ClockSynchronization`;设备权限、实际设时及事件时间戳仍由应用接入 |
| 实现`IEC60870_Clock_RTC_Read/Set`,处理RTC寄存器、BCD、时间基准、掉电标志及读写一致性 | 板级接入层和具体RTC驱动,暂不实现 |
| t1/t2/t3 及发送期限使用的单调计时 | 平台提供 `now_ms`,Link/Connection 使用 |
@@ -75,14 +75,14 @@ SU本身不携带时区信息;源偏移须来自双方约定。这里的“本
## 当前接入状态与待补齐项
当前已实现日期转换、星期计算、CP56Time2a编解码、统一时间源接口及Windows系统时钟适配。完整的协议时间业务还需补齐以下接入:
当前已实现日期转换、星期计算、CP56Time2a编解码、统一时间源接口、Windows系统时钟适配及Type 103业务模块。公共工具、协议处理及设备接入的边界如下:
| 项目 | 当前状态与待补齐内容 |
|---|---|
| 协议时间源读取及设置 | 已提供直接调用的`Clock_Read/Clock_Set`及Windows读写实现;时间源限定为Windows或RTC,RTC目前仅预留接口。日志读取仍使用原有入口 |
| RTC数据适配 | 接入函数声明已提供,具体芯片驱动暂不实现;后续接入时再明确RTC采用UTC还是本地标准时间、返回整数还是BCD、年份表示和毫秒来源,并按芯片要求取得一致的日期、时分秒及亚秒值,整理为固定UTC+8的公共日历 |
| 时间有效性及失败处理 | `Clock_Read`分别返回操作状态和`is_invalid`,校验日期及0/1标志,失败保留输出;具体驱动负责识别未初始化、掉电或失去可信时间。下文示例传递质量到CP56的IV;实际业务仍须决定是否发送无效时间。IV=1也不能用全零日期占位 |
| ASDU业务收发与校时 | `IEC60870_InformationObjects.c`的带时标类型复用基础对象值处理,并调用CP24/CP56编解码;保护事件和延时命令使用CP16。含103校时命令,保留IV/SU。`IEC60870_Asdu.c`维护容器与原文应答。事件采集、时间源读取、校时权限、平台设时及成功/失败应答的业务决策仍需应用接入 |
| ASDU业务收发与校时 | `IEC60870_InformationObjects.c`复用CP16/CP24/CP56编解码,`IEC60870_Asdu.c`维护容器与原文应答;ClockSynchronization处理Type 103校验、排队、补偿、显式执行及成功/失败确认。事件采集、校时权限及平台设时仍由应用action接入;示例默认模拟,显式system模式调用Clock_Set |
| 日期转换实现 | 保留Windows CRT和纯整数算法两种实现。RTC直接提供日历时无需时间戳展开;需要转换时间戳且没有Windows CRT时使用整数算法,不规划增加其他操作系统时间库 |
| 项目内的时间模块回归 | `iec60870_asdu_test.c`已覆盖CP56已知字节、IV/SU保留、非法日期和保留位,并分别使用两种时间实现编译运行;这不等于完整日期转换一致性测试。独立转换边界及平台读写失败、时间质量用例仍待补齐 |
@@ -181,7 +181,7 @@ if (!IEC60870_Time_CP56_To_Local(&timestamp,
return IEC60870_Clock_Set(&local_time);
```
该例假定源标准时区也是UTC+8;其他源时区应传双方约定的偏移。`Clock_Set`只接收日历,不能自行判断报文IV、SU或校时权限;不能跳过转换直接把解码的源日历交给设时接口。以上函数和片段说明接入顺序,尚未注册为ASDU业务处理器,实际成功/失败的业务应答仍由ASDU层负责。
该例假定源标准时区也是UTC+8;其他源时区应传双方约定的偏移。`Clock_Set`只接收日历,不能自行判断报文IV、SU或校时权限;不能跳过转换直接把解码的源日历交给设时接口。以上片段演示公共接口的直接用法。通过ClockSynchronization接入时,action收到的`request->local_time`已完成校验、时区换算及受理后滞留补偿,可直接交给Clock_Set,不再次换算;肯定/否定确认由业务模块生成。
## 时间戳转日期的实现选择
@@ -237,7 +237,7 @@ Windows显式使用64位版本,避免32位`time_t`限制原有的1970..2099年
- **星期**:`has_weekday=0` 编码为报文星期0;为1时从日期计算1=周一至7=周日。解码允许星期0,此时仍计算内存中的 `time.weekday`;非零星期必须与日期一致,否则失败。这样不依赖调用方传入的过期星期,也不会默默接受相互矛盾的日期和星期。
- **IV**:编解码保留该标志。IV=1表示业务时间无效,不等于报文格式无法解析;调用方必须检查该标志后再决定是否记录或使用时间。即使IV=1,本模块也要求日期和时间字段合法,不接受全零日期等占位数据。
- **SU**:公共编解码支持0和1,复制标志而不调整日历值;本地标准时间转换接口在SU=1时扣除一小时,并处理源标准时区与本地标准时间的差值。编码或转换时SU大于1均失败。后续业务发送入口负责设置SU=0;接收时将完整解析值交给`CP56_To_Local`,不丢弃SU后直接使用原时间。
- **SU**:公共编解码支持0和1,复制标志而不调整日历值;本地标准时间转换接口在SU=1时扣除一小时,并处理源标准时区与本地标准时间的差值。编码或转换时SU大于1均失败。应用新生成时标使用SU=0,Type 103确认保留原SU;接收时将完整解析值交给转换接口,不丢弃SU后直接使用原时间。
- **保留位**:编码统一清零,解码严格拒绝非零值。分钟bit6按本模块采用的布局视为保留位,不接受将它另作扩展标志的报文。这是明确的接入约束,不能把此接口当作任意七字节数据的无损容器。
数值校验成功只说明日期真实存在、各字段符合上述约定。它不保证远端时钟准确,也不决定校时权限或业务接受策略。