306 lines
14 KiB
C
306 lines
14 KiB
C
/**
|
||
* 主站启停流程:发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;
|
||
}
|