/** * 主站启停流程:发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 /** * 功能:生成主动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; }