Files
STM32F4-Base/IEC60870-Slave/104/business/IEC104_ClockSynchronization.h
T
STF 7596f3371a
CI / L0 静态门禁 (pre-commit) (push) Failing after 44s
CI / L0.5 主机侧单测 (MSVC) (push) Failing after 42s
补充总召唤的广播地址,电度召唤+时钟同步业务的实现
2026-10-29 03:20:19 -07:00

141 lines
11 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 104时钟同步(C_CS_NA_1):接收主站校时请求,帮助应用校准供SOE、日志等使用的设备时间。
* 本模块负责校验、时区换算、站内滞留补偿及ACT_CON;实际设时由应用提供的action完成。
* Handle_Event/Process不调用应用代码;Execute_Next每次至多同步执行一个action,不保存回调。
* 设备日历与协议单调计时分开:设置设备时间不应改变Net提供的单调毫秒基准。
* 所有接口与Slave在同一线程串行调用;模块内应答严格按请求顺序。
* 执行后的ACT_CON使用Try路径,不越过HIGH/NORMAL待发原文;确认只回显请求时标。
* 退出顺序:Cancel -> Slave_Close -> 读空并分派通知 -> Destroy -> Remove_Station -> Slave_Destroy。
*/
#ifndef IEC104_CLOCK_SYNCHRONIZATION_H
#define IEC104_CLOCK_SYNCHRONIZATION_H
#include "IEC104_Slave.h"
#include "IEC60870_Time.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct IEC104_ClockSynchronization IEC104_ClockSynchronization_t;
typedef struct {
int16_t master_utc_offset_minutes; /* 主站标准当地时间减UTC的分钟数,-1439..1439;默认480,不含SU。 */
uint32_t execute_timeout_ms; /* 受理至执行检查的期限,单位ms,默认15000;0禁用期限,仍检查时间范围。 */
} IEC104_ClockSynchronizationConfig_t;
/* 交给action的只读纯值请求,指针仅在本次回调期间有效。
* utc_ms/local_time已补偿受理至base_ms的等待;应用不再叠加时区或同一段等待。
* 不补偿网络、Slave通知FIFO及action内部耗时;base_ms不是硬件实际生效时刻。 */
typedef struct {
uint64_t generation, receive_serial; /* generation:原TCP会话代次;receive_serial:原I帧接收流水号。 */
uint64_t station_id; /* 定向请求的原站点注册编号,不是CA;广播为0,不绑定某个目标站。 */
uint16_t common_address; /* 请求原CA,广播保留65535;逐站确认的CA由模块另行填写。 */
uint8_t originator_address; /* 请求原OA,供应用识别发起方;本字段不是权限校验结果。 */
IEC60870_CP56Time2a_t master_time; /* 原CP56解码值,IV=0,保留主站日历和SU;未作时区换算及滞留补偿。 */
IEC60870_WallTime_t local_time; /* 对应base_ms的固定UTC+8标准日历,可直接交给Clock_Set或RTC适配。 */
uint64_t utc_ms; /* 对应base_ms的UTC纪元毫秒,自1970-01-01 00:00:00 UTC起算。 */
/* accepted_ms:Handle_Event受理时刻;base_ms:执行检查时刻。均为Net单调毫秒,允许为0。 */
uint64_t accepted_ms, base_ms;
} IEC104_ClockSynchronizationRequest_t;
/* 应用同步设时动作:context由Execute_Next原样传入,允许NULL;request为本次校验后的只读请求。
* true报告设置成功,false未报告成功;false不保证时钟未变,模块不提供硬件回滚。
* 须短时完成,不保留request指针等待异步执行;权限、允许回拨幅度和设备操作由应用判断。
* 不得重入本模块的修改入口或推进Slave事件循环;回调内仅允许查询本模块Get_Status。
* 每条通过执行检查的新请求各调用一次,不按内容去重;主站重发旧时标仍可能再次回拨。 */
typedef bool (*IEC104_ClockSynchronizationAction_t)(void *context,
const IEC104_ClockSynchronizationRequest_t *request);
typedef enum {
IEC104_CLOCK_SYNCHRONIZATION_EVENT_ERROR = -1, /* 参数、事件归属、重入或内部准备失败。 */
IEC104_CLOCK_SYNCHRONIZATION_EVENT_IGNORED = 0, /* 非103 I帧或本模块当前不能接手的通知。 */
IEC104_CLOCK_SYNCHRONIZATION_EVENT_HANDLED = 1 /* 含拒绝和丢新,不表示已应答。 */
} IEC104_ClockSynchronizationEventResult_t;
typedef enum {
IEC104_CLOCK_SYNCHRONIZATION_EXECUTE_ERROR = -1, /* 参数、重入或服务状态查询失败;未调用action。 */
IEC104_CLOCK_SYNCHRONIZATION_EXECUTE_EMPTY = 0, /* 无可执行请求;仍可能有待提交应答。 */
IEC104_CLOCK_SYNCHRONIZATION_EXECUTE_WAIT = 1, /* 先处理未读Slave通知。 */
IEC104_CLOCK_SYNCHRONIZATION_EXECUTE_OK = 2 /* 已调用一次动作;确认可能因后续STOP/故障取消。 */
} IEC104_ClockSynchronizationExecuteResult_t;
/* 只读调度快照;计数从Create起累计,Cancel及连接启停不清零,不代表对端已经收到应答。 */
typedef struct {
uint64_t generation; /* 已分派给模块的会话代次;消费DISCONNECTED后为0。 */
bool active; /* 模块根据已分派通知及推进结果维护的激活状态,不替代当前网络状态查询。 */
bool events_pending; /* Slave尚有未读通知;应先NextEvent并按FIFO分派,再执行或续发。 */
bool execute_pending; /* 仍有占槽的待执行请求;不保证当前会话、期限已允许调用action。 */
bool confirm_pending; /* action已开始且确认尚未全部交接,含回调期间;不含仅校验失败的回复。 */
bool need_process; /* 当前有状态清理、到期或待尝试回复;false仍不能跳过每轮Process的WAIT重试。 */
bool timer_active; /* 存在尚未执行且启用了执行期限的请求。 */
uint32_t timeout_remaining_ms; /* 所有执行期限的最小剩余ms;到期为0,无计时任务时也为0。 */
unsigned pending_requests; /* 占用任务槽数,含待执行、回调中及待回复;一条广播始终只占一槽。 */
/* accepted:合法且成功入槽的请求数,含后来超时/取消者。
* executed:action已返回的次数;synchronized:其中返回true的次数。 */
unsigned long accepted, executed, synchronized;
/* submitted:发送层返回OK的ASDU数,广播逐目标计数;negative_submitted为其中P/N=1的数量。 */
unsigned long submitted, negative_submitted;
/* rejected:受理校验拒绝数,含无法应答的畸形请求,不含后续超时或action失败。
* waits:发送返回WAIT的次数,重试可重复累计;不计等待Slave通知的次数。
* cancelled:取消的占槽请求数,广播部分确认后取消也只计一次,不保证设备时间未变。
* overloads/dropped_requests:槽满丢新的次数/请求数,同时增加;对象数错误/对象截短只计rejected。
* timeouts:启用期限时,执行前到期或单调倒退而转否定的请求数。
* time_errors:执行检查的换算/范围失败,以及禁用期限时的单调倒退,不含受理校验拒绝。 */
unsigned long rejected, waits, cancelled, overloads, dropped_requests, timeouts, time_errors;
} IEC104_ClockSynchronizationStatus_t;
/* 向config写默认源时区480分钟、执行期限15000ms;config为NULL时无操作。
* 只填写配置,不创建实例;调用方可修改后交给Create。 */
void IEC104_ClockSynchronization_Config_Default(IEC104_ClockSynchronizationConfig_t *config);
/* 为有效slave创建独立校时实例;config须非NULL,按值复制,返回后不再借用配置指针。
* slave由应用拥有,寿命须覆盖本实例;可在Slave_Init前创建,但必须早于消费该服务的通知。
* 每服务只允许一个实例,按共用站点表路由;不分配堆内存、不建立监听、不保存应用action。
* 成功返回池内句柄;参数/配置非法、重复绑定或池/身份资源耗尽返回NULL。
* IEC104_CLOCK_SYNCHRONIZATION_CAPACITY默认等于TCP服务容量;
* IEC104_CLOCK_SYNCHRONIZATION_TASK_CAPACITY默认4,限定每实例的任务槽数。 */
IEC104_ClockSynchronization_t *IEC104_ClockSynchronization_Create(IEC104_Slave_t *slave,
const IEC104_ClockSynchronizationConfig_t *config);
/* 归还self占用的静态池槽;true成功,成功后必须停止使用旧句柄。
* 句柄无效、仍有占槽任务或处于action回调中返回false;不隐式Cancel,也不关闭Slave。 */
bool IEC104_ClockSynchronization_Destroy(IEC104_ClockSynchronization_t *self);
/* 取消self的全部占槽任务并归还站点引用/快照,每条请求增加一次cancelled。
* 不停用连接,可继续接手后续请求;不撤回已交接帧、不回滚设备时间。
* 无返回值;无效句柄或action回调中调用均无操作。 */
void IEC104_ClockSynchronization_Cancel(IEC104_ClockSynchronization_t *self);
/* 将event按Slave FIFO次序交给self;event须来自所属Slave的NextEvent或合法Copy_Event副本。
* 所有连接/启停通知都须分派,103 I帧由本模块接手;不修改输入事件,不调用action。
* 合法请求保存为待执行任务;错误回复可当场提交。COT/CA/IOA优先回45/46/47,
* 其余可对应的非法请求否定7;不能保留唯一完整对象时仅计拒绝,槽满丢新并计数、不断链。
* 广播只执行一次动作,按受理时的快照逐站确认;已删除的待提交目标跳过,新注册站不参与。
* 返回HANDLED包含保存、拒绝、丢新和状态通知处理,不表示已应答;IGNORED表示未接手;
* ERROR表示参数/归属/重入或内部准备失败,不可作为IGNORED继续生成兜底应答。 */
IEC104_ClockSynchronizationEventResult_t IEC104_ClockSynchronization_Handle_Event(
IEC104_ClockSynchronization_t *self, const IEC104_SlaveEvent_t *event);
/* 为self有界推进执行期限及队首回复,不调用action;须在每轮网络推进、通知分派后调用。
* true表示完成本轮检查,可能仍在等待、已取消任务或没有任务,不表示应答成功。
* 参数、重入、查询或组包错误返回false。发送ERROR/FATAL取消任务后返回true,
* 正常STOP待通知的ERROR先让出;应用继续消费Slave通知处理后续断线/启停。
* 即使need_process为false也须每轮调用,以重试发送WAIT;不能以忙循环代替网络推进。 */
bool IEC104_ClockSynchronization_Process(IEC104_ClockSynchronization_t *self);
/* 为self执行最早的待执行请求:action须非NULL,context原样传给它,本函数不保存两者。
* 先检查会话、未读通知、期限及补偿范围;每次至多调用一次action,过期请求只转为否定回复。
* 可越过较早的待回复任务执行后续请求,但确认仍按模块内请求顺序提交,不插队。
* OK表示action已经返回,可能报告失败,确认也可能待发或被后续STOP/故障取消;
* WAIT表示先处理Slave通知;EMPTY表示已无可执行请求,仍可能有待回复任务;
* ERROR为参数、重入或服务查询失败。WAIT/EMPTY/ERROR均未调用action。
* 动作开始后不再判该任务的执行期限;确认发送WAIT只重试确认,绝不重复动作。 */
IEC104_ClockSynchronizationExecuteResult_t IEC104_ClockSynchronization_Execute_Next(
IEC104_ClockSynchronization_t *self, IEC104_ClockSynchronizationAction_t action, void *context);
/* 将self的当前只读快照写入非NULL的status;true成功,句柄/参数或服务查询失败返回false且不改输出。
* 可在action回调内查询,不推进任务、不预留发送资源;正在执行的任务不计期限,confirm_pending为true。
* 执行后的确认走Try,已有HIGH/NORMAL待发内容会阻止提交;持续发布可能推迟确认。
* 需要确认先于新的带时标上报时,由发布方在confirm_pending期间暂停此类新上报并继续推进网络。 */
bool IEC104_ClockSynchronization_Get_Status(const IEC104_ClockSynchronization_t *self,
IEC104_ClockSynchronizationStatus_t *status);
#ifdef __cplusplus
}
#endif
#endif /* IEC104_CLOCK_SYNCHRONIZATION_H */