Files
STM32F4-Base/IEC60870-Slave/104/IEC104_Link_Master_StartStop.c
T
STF 4c55544f05
CI / L0 静态门禁 (pre-commit) (push) Failing after 45s
CI / L0.5 主机侧单测 (MSVC) (push) Failing after 42s
通信协议代码
2026-10-26 08:31:48 -04:00

306 lines
14 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.
/**
* 主站启停流程:发STARTDT_ACT -> 等STARTDT_CON;请求停止 -> 发STOPDT_ACT -> 等STOPDT_CON。
* 本端不应答对端STARTDT/STOPDT_ACT。共用序号、确认和状态校验由公共模块提供。
* Prepare仅生成报文,Complete提交最终发送结果;FIFO、实际发送和时钟由连接调度层负责。
*
* 主流程:START的Prepare -> 发送ACT -> Complete -> Handle CON;
* STOP的Request -> Prepare(必要时先S再ACT)-> 对应Complete -> Handle CON。
* 两个主动过程串行执行;重复或忙结果不产生新过程,也不授权连接层重置已有超时。
* 主站仅在*_CON_WAITING接受对应CON,不保存早于ACT完成的CON;调度层必须先
* 提交本地已知的ACT完整发送结果,再处理对端CON;TESTFR也采用相同顺序。
* 角色入口经公共Role_Valid完成检查;完整状态校验及角色组合规则集中在Link.c,
* 本文件只实现主站启停流程,不向公共模块提供反向校验入口。
*/
#include "IEC104_Link_Master_StartStop.h"
#include "IEC104_Link_Internal.h"
#include <stddef.h>
/**
* 功能:生成主动STARTDT_ACT请求并记录等待发送完成的状态。
* 输入参数:state——链路状态;request——输出缓冲;capacity——至少6字节。
* 输出参数:request——写入68 04 07 00 00 00;state——转ACT_SEND_PENDING。
* 返回值:6表示生成请求;0表示已有启停过程或稳定状态已为STARTED;
* -1表示参数或状态非法。返回0/-1时不修改状态和输出缓冲。
*/
/* 适用端:仅控制站。 */
int IEC104_Link_Prepare_Startdt_Act(IEC104_LinkState_t *state,
uint8_t *request, uint16_t capacity)
{
if (request == NULL || capacity < IEC104_APCI_LEN ||
!IEC104_Link_Role_Valid_Internal(state, IEC104_ROLE_CONTROLLING)) {
return -1;
}
if (state->dt_procedure != IEC104_DT_PROC_IDLE ||
state->dt_state != IEC104_DT_STOPPED) {
return 0;
}
/* 缓冲已验证且功能码固定,编码必定生成6字节;(void)表示有意忽略返回值。 */
(void)IEC104_Build_Uframe(request, IEC104_U_STARTDT_ACT);
state->dt_procedure = IEC104_DT_PROC_START_ACT_SEND_PENDING;
return IEC104_APCI_LEN;
}
/**
* 功能:根据主动STARTDT_ACT最终发送结果决定是否等待STARTDT_CON。
* 输入参数:state——链路状态;sent_len——最终累计发送字节数,恰好6表示成功。
* 输出参数:成功转START_CON_WAITING;失败结束本次主动请求并回IDLE。
* 返回值:OK表示完整发送并开始等待CON;IGNORED表示没有ACT等待完成;
* FATAL表示状态/角色非法或发送失败。
* 说明:角色和状态合法后先匹配START_ACT_SEND_PENDING,再检查sent_len。
* 无匹配过程时IGNORED不改状态;发送失败只清过程,不提交STARTED,须断连。
* Complete成功后才能分派STARTDT_CON,并以ACT实际完整发送时刻记录t1起点。
*/
/* 适用端:仅控制站。 */
IEC104_LinkResult_t IEC104_Link_Complete_Startdt_Act_Send(
IEC104_LinkState_t *state, int sent_len)
{
if (!IEC104_Link_Role_Valid_Internal(state, IEC104_ROLE_CONTROLLING)) {
return IEC104_LINK_RESULT_FATAL;
}
if (state->dt_procedure != IEC104_DT_PROC_START_ACT_SEND_PENDING) {
return IEC104_LINK_RESULT_IGNORED;
}
if (sent_len != IEC104_APCI_LEN) {
state->dt_procedure = IEC104_DT_PROC_IDLE;
return IEC104_LINK_RESULT_FATAL;
}
state->dt_procedure = IEC104_DT_PROC_START_CON_WAITING;
return IEC104_LINK_RESULT_OK;
}
/**
* 功能:接受主动启动正在等待的STARTDT_CON并提交数据传输启动。
* 输入参数:state——链路状态;apci——已解析的STARTDT_CON。
* 输出参数:主动请求回IDLE,数据传输状态进入或保持STARTED。
* 返回值:OK表示接受并提交;IGNORED表示当前没有等待STARTDT_CON;
* FATAL表示参数、角色、报文或状态非法。
* 说明:仅START_CON_WAITING可提交;ACT_SEND_PENDING时返回IGNORED且不暂存CON。
* 输入校验在过程匹配之前,即使没有等待过程,非法报文仍返回FATAL。
* 非OK时状态不变;成功也不重置序号、S确认或TESTFR。
*/
/* 适用端:仅控制站。 */
IEC104_LinkResult_t IEC104_Link_Handle_Startdt_Con(
IEC104_LinkState_t *state, const IEC104_ApciInfo_t *apci)
{
if (!IEC104_Link_Role_Valid_Internal(state, IEC104_ROLE_CONTROLLING) ||
apci == NULL ||
apci->frame_type != IEC104_FRAME_TYPE_U ||
apci->u_function != IEC104_U_STARTDT_CON) {
return IEC104_LINK_RESULT_FATAL;
}
if (state->dt_procedure != IEC104_DT_PROC_START_CON_WAITING) {
return IEC104_LINK_RESULT_IGNORED;
}
state->dt_procedure = IEC104_DT_PROC_IDLE;
state->dt_state = IEC104_DT_STARTED;
return IEC104_LINK_RESULT_OK;
}
/**
* 功能:判断主动STARTDT流程等待STARTDT_CON是否达到t1超时。
* 输入参数:state——链路状态;elapsed_ms——已经等待的毫秒数;
* t1_ms——t1配置值。
* 输出参数:无,不修改state。
* 返回值:1表示已经超时;0表示未等待CON或尚未超时;-1表示参数或状态非法。
* 说明:两种合法角色均可查询;仅控制站可能处于对应等待过程,被控站返回0。
* 本函数只比较调用方提供的elapsed_ms,不创建定时器,也不覆盖ACT排队/发送时间。
* 调用方持续使用本次ACT实际完整发送时刻计算elapsed_ms,不能因重复请求改用当前时刻。
*/
/* 适用端:控制站与被控站;实际超时只适用于控制站等待CON的过程。 */
int IEC104_Link_T1_Startdt_Con_Expired(const IEC104_LinkState_t *state,
uint32_t elapsed_ms,
uint32_t t1_ms)
{
if (!IEC104_Link_State_Valid_Internal(state) || t1_ms == 0U) {
return -1;
}
if (state->dt_procedure != IEC104_DT_PROC_START_CON_WAITING) {
return 0;
}
return elapsed_ms >= t1_ms ? 1 : 0;
}
/**
* 功能:控制站受理主动停止意图,不构帧;受理后立即关闭新业务I帧发送入口。
* 输入参数:state——控制站链路状态。
* 输出参数:ACCEPTED时dt_procedure转为STOP_REQUESTED,保留稳定状态和序号。
* 返回值:ACCEPTED表示新受理;ALREADY_STOPPED表示已经停止;IN_PROGRESS表示
* 停止已在进行;BUSY表示存在其他启停过程;REQUEST_ERROR表示参数、角色
* 或状态非法。除ACCEPTED外均不修改状态。
* 说明:待确认接收数据不阻止受理;Prepare_Stopdt_Request负责后续S确认/ACT。
* 只有ACCEPTED代表新过程;重复请求不重置连接层的准备/排队或CON等待计时。
*/
/* 适用端:仅控制站。 */
IEC104_StopdtRequestResult_t IEC104_Link_Request_Stopdt(
IEC104_LinkState_t *state)
{
if (!IEC104_Link_Role_Valid_Internal(state, IEC104_ROLE_CONTROLLING)) {
return IEC104_STOPDT_REQUEST_ERROR;
}
switch (state->dt_procedure) {
case IEC104_DT_PROC_STOP_REQUESTED:
case IEC104_DT_PROC_STOP_ACT_SEND_PENDING:
case IEC104_DT_PROC_STOP_CON_WAITING:
return IEC104_STOPDT_REQUEST_IN_PROGRESS;
case IEC104_DT_PROC_IDLE:
break;
default:
return IEC104_STOPDT_REQUEST_BUSY;
}
if (state->dt_state == IEC104_DT_STOPPED) {
return IEC104_STOPDT_REQUEST_ALREADY_STOPPED;
}
state->dt_procedure = IEC104_DT_PROC_STOP_REQUESTED;
return IEC104_STOPDT_REQUEST_ACCEPTED;
}
/**
* 功能:推进已受理的主动停止,按需生成S确认或STOPDT_ACT。
* 输入参数:state——控制站链路状态;request——输出缓冲;capacity——至少6字节;
* tx——报文类型和长度的输出地址,须随报文保存在FIFO节点中。
* 输出参数:FRAME_READY时写入request和tx,并提交S确认或STOPDT_ACT待发送状态。
* 返回值:FRAME_READY表示生成报文;NOT_ACTIVE表示没有对应停止过程;
* WAIT_SEND表示等待已有S/ACT发送完成;WAIT_PEER_CON表示等待STOPDT_CON;
* PREPARE_ERROR表示参数、角色、状态或编码结果非法。
* 非FRAME_READY时不修改状态和输出,也不丢失已受理的停止请求。
* 说明:调用前先用Request_Stopdt受理;在途S帧覆盖当前V(R)时允许ACT紧随排队。
* 按tx.frame_type选择Complete_S_Ack_Send或Complete_Stopdt_Act_Send,按FIFO提交完成事件。
* 构造S时STOP_REQUESTED保持不变,可再次Prepare尝试把ACT排在该S之后;
* 构造ACT不要求va==vs,对端I帧确认是否排空留到Handle_Stopdt_Con检查。
*/
/* 适用端:仅控制站。 */
IEC104_StopdtPrepareResult_t IEC104_Link_Prepare_Stopdt_Request(
IEC104_LinkState_t *state, uint8_t *request, uint16_t capacity,
IEC104_StopdtTx_t *tx)
{
if (!IEC104_Link_Role_Valid_Internal(state, IEC104_ROLE_CONTROLLING) || request == NULL ||
capacity < IEC104_APCI_LEN || tx == NULL) {
return IEC104_STOPDT_PREPARE_ERROR;
}
if (state->dt_procedure == IEC104_DT_PROC_STOP_ACT_SEND_PENDING) {
return IEC104_STOPDT_WAIT_SEND;
}
if (state->dt_procedure == IEC104_DT_PROC_STOP_CON_WAITING) {
return IEC104_STOPDT_WAIT_PEER_CON;
}
if (state->dt_procedure != IEC104_DT_PROC_STOP_REQUESTED) {
return IEC104_STOPDT_NOT_ACTIVE;
}
if (!IEC104_Link_Rx_Ack_Covered_Internal(state)) {
/* 在途S只确认旧V(R)时,先等它完成,再生成最新确认。 */
if (state->s_ack_state == IEC104_S_ACK_SEND_PENDING) {
return IEC104_STOPDT_WAIT_SEND;
}
if (IEC104_Link_Prepare_S_Ack_Internal(state, request, state->vr) != IEC104_APCI_LEN) {
return IEC104_STOPDT_PREPARE_ERROR;
}
tx->frame_type = IEC104_STOPDT_FRAME_S_ACK;
} else {
/* 在途S已覆盖当前V(R)时,ACT可以紧随其后排队。 */
(void)IEC104_Build_Uframe(request, IEC104_U_STOPDT_ACT);
state->dt_procedure = IEC104_DT_PROC_STOP_ACT_SEND_PENDING;
tx->frame_type = IEC104_STOPDT_FRAME_ACT;
}
tx->frame_length = IEC104_APCI_LEN;
return IEC104_STOPDT_FRAME_READY;
}
/**
* 功能:根据主动STOPDT_ACT最终发送结果决定是否等待STOPDT_CON。
* 输入参数:state——链路状态;sent_len——最终累计发送字节数,恰好6表示成功。
* 输出参数:成功转STOP_CON_WAITING;失败结束主动请求并要求上层断连。
* 返回值:OK表示完整发送并开始等待CON;IGNORED表示没有ACT等待完成;
* FATAL表示状态/角色非法或发送失败。
* 说明:角色和状态合法后先匹配STOP_ACT_SEND_PENDING,再检查sent_len。
* 发送失败将过程清为IDLE,但dt_state仍为STARTED;FATAL后必须断连,
* 不能因稳定状态仍为STARTED而继续发送业务I帧。
* 本ACT的Complete必须先于Handle_Stopdt_Con提交;它不代替前序S帧的Complete。
*/
/* 适用端:仅控制站。 */
IEC104_LinkResult_t IEC104_Link_Complete_Stopdt_Act_Send(
IEC104_LinkState_t *state, int sent_len)
{
if (!IEC104_Link_Role_Valid_Internal(state, IEC104_ROLE_CONTROLLING)) {
return IEC104_LINK_RESULT_FATAL;
}
if (state->dt_procedure != IEC104_DT_PROC_STOP_ACT_SEND_PENDING) {
return IEC104_LINK_RESULT_IGNORED;
}
if (sent_len != IEC104_APCI_LEN) {
state->dt_procedure = IEC104_DT_PROC_IDLE;
return IEC104_LINK_RESULT_FATAL;
}
state->dt_procedure = IEC104_DT_PROC_STOP_CON_WAITING;
return IEC104_LINK_RESULT_OK;
}
/**
* 功能:接受主动停止正在等待的STOPDT_CON并提交数据传输停止。
* 输入参数:state——链路状态;apci——已解析的STOPDT_CON。
* 输出参数:主动请求回IDLE,数据传输状态进入或保持STOPPED。
* 返回值:OK表示接受并提交;IGNORED表示当前没有等待STOPDT_CON;
* FATAL表示参数、角色、报文、状态非法,或发送方向未排空/接收确认未覆盖。
* 说明:在途S帧完整覆盖V(R)时允许CON先于S完成回调;保留S待完成状态,
* 后续Complete_S_Ack_Send成功才推进acked_nr,发送失败仍返回FATAL要求断连。
* 此处允许晚到的是S完成事件,不是本ACT完成事件;STOP_ACT_SEND_PENDING
* 时CON返回IGNORED且不暂存。排空检查va==vs表示对端已确认全部预留I帧,
* 公共序号约束保证此时sent_vs也已到达vs,I帧完成须已由调度层按序提交。
*/
/* 适用端:仅控制站。 */
IEC104_LinkResult_t IEC104_Link_Handle_Stopdt_Con(
IEC104_LinkState_t *state, const IEC104_ApciInfo_t *apci)
{
if (!IEC104_Link_Role_Valid_Internal(state, IEC104_ROLE_CONTROLLING) ||
apci == NULL ||
apci->frame_type != IEC104_FRAME_TYPE_U ||
apci->u_function != IEC104_U_STOPDT_CON) {
return IEC104_LINK_RESULT_FATAL;
}
if (state->dt_procedure != IEC104_DT_PROC_STOP_CON_WAITING) {
return IEC104_LINK_RESULT_IGNORED;
}
if (state->va != state->vs || !IEC104_Link_Rx_Ack_Covered_Internal(state)) {
return IEC104_LINK_RESULT_FATAL;
}
state->dt_procedure = IEC104_DT_PROC_IDLE;
state->dt_state = IEC104_DT_STOPPED;
return IEC104_LINK_RESULT_OK;
}
/**
* 功能:判断主动STOPDT流程等待STOPDT_CON是否达到t1超时。
* 输入参数:state——链路状态;elapsed_ms——已经等待的毫秒数;
* t1_ms——t1配置值。
* 输出参数:无,不修改state。
* 返回值:1表示已经超时;0表示未等待CON或尚未超时;-1表示参数或状态非法。
* 说明:两种合法角色均可查询;仅控制站可能处于对应等待过程,被控站返回0。
* elapsed_ms始终从本次ACT实际完整发送时刻计算;重复请求或I/S收发不重置起点。
* 本函数不保存时钟,也不能用作被控站STOP_DRAINING的排空计时器。
*/
/* 适用端:控制站与被控站;实际超时只适用于控制站等待CON的过程。 */
int IEC104_Link_T1_Stopdt_Con_Expired(const IEC104_LinkState_t *state,
uint32_t elapsed_ms,
uint32_t t1_ms)
{
if (!IEC104_Link_State_Valid_Internal(state) || t1_ms == 0U) {
return -1;
}
if (state->dt_procedure != IEC104_DT_PROC_STOP_CON_WAITING) {
return 0;
}
return elapsed_ms >= t1_ms ? 1 : 0;
}