1106 lines
47 KiB
C
1106 lines
47 KiB
C
/**
|
||
* 公共链路逻辑:初始化、完整状态校验、I/S序号、k/w窗口、S确认与TESTFR。
|
||
* 主站启停见IEC104_Link_Master_StartStop.c,子站启停见IEC104_Link_Slave_StartStop.c。
|
||
* 本文件不执行TCP收发;连接调度层串行提交接收、发送完成和时间事件。
|
||
*
|
||
* 协作边界:本文件定义Internal.h中的跨文件辅助函数,供两个角色实现调用。
|
||
* State_Valid按本端角色调用本文件内的static过程校验,不反向依赖角色实现。
|
||
* 带_Internal后缀的函数具有外部链接,但只供链路实现使用;其余局部辅助函数
|
||
* 采用static。公开入口负责校验,内部步骤按各自前置条件复用已校验的状态。
|
||
* 主要提交点:Prepare_I推进vs;Handle_I/S推进va(I另推进vr);
|
||
* Complete_I_Send/Complete_S_Ack_Send提交实际发送的N(R),其中I帧完成还推进sent_vs。
|
||
*/
|
||
#include "IEC104_Link_Common.h"
|
||
#include "IEC104_Link_Internal.h"
|
||
|
||
#include <stddef.h>
|
||
|
||
/* 仅用于本文件内I/S帧的序号规划;只有READY时才读取快照并编码。 */
|
||
typedef enum {
|
||
IEC104_PLAN_ERROR,
|
||
IEC104_PLAN_SKIPPED,
|
||
IEC104_PLAN_READY
|
||
} IEC104_PlanResult_t;
|
||
|
||
/**
|
||
* 功能:计算15位IEC-104序号从from前进到to的距离。
|
||
* 输入参数:from——起始序号;to——目标序号。
|
||
* 输出参数:无。
|
||
* 返回值:0至32767范围内的循环距离。
|
||
*/
|
||
static uint16_t Number_Distance(uint16_t from, uint16_t to)
|
||
{
|
||
/* 按15位序号环计算距离,使32767到0的距离为1。 */
|
||
return (uint16_t)((to - from) & IEC104_SEQUENCE_MASK);
|
||
}
|
||
|
||
/**
|
||
* 功能:将15位IEC-104序号循环加1。
|
||
* 输入参数:value——当前序号。
|
||
* 输出参数:无。
|
||
* 返回值:递增后的序号;32767的下一个序号为0。
|
||
*/
|
||
static uint16_t Number_Increment(uint16_t value)
|
||
{
|
||
/* 与0x7FFF相与,自动将32768折回0。 */
|
||
return (uint16_t)((value + 1U) & IEC104_SEQUENCE_MASK);
|
||
}
|
||
|
||
/**
|
||
* 功能:检查序号、稳定状态、标志和TESTFR等公共基础字段。
|
||
* 输入参数:state——链路状态。
|
||
* 输出参数:无。
|
||
* 返回值:公共基础字段合法返回1,否则返回0。
|
||
* 说明:S确认、角色和启停过程由后续完整校验检查。
|
||
*/
|
||
static int Link_State_Fields_Valid(const IEC104_LinkState_t *state)
|
||
{
|
||
if (state == NULL ||
|
||
state->vs > IEC104_SEQUENCE_MASK ||
|
||
state->sent_vs > IEC104_SEQUENCE_MASK ||
|
||
state->va > IEC104_SEQUENCE_MASK ||
|
||
state->vr > IEC104_SEQUENCE_MASK ||
|
||
state->acked_nr > IEC104_SEQUENCE_MASK ||
|
||
state->s_ack_nr > IEC104_SEQUENCE_MASK) {
|
||
return 0;
|
||
}
|
||
|
||
/* 确认位置不得越过本地完成位置;用共同尾端的循环距离检查,兼容序号回绕。 */
|
||
if (Number_Distance(state->sent_vs, state->vs) > Number_Distance(state->va, state->vs)) {
|
||
return 0;
|
||
}
|
||
|
||
switch (state->dt_state) {
|
||
case IEC104_DT_STOPPED:
|
||
case IEC104_DT_STARTED:
|
||
break;
|
||
default:
|
||
return 0;
|
||
}
|
||
|
||
switch (state->testfr_state) {
|
||
case IEC104_TESTFR_IDLE:
|
||
case IEC104_TESTFR_ACT_SEND_PENDING:
|
||
case IEC104_TESTFR_CON_PENDING:
|
||
return 1;
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 功能:计算已经接收但本端尚未通过I/S帧确认的I帧数量。
|
||
* 输入参数:state——序号字段有效的链路状态。
|
||
* 输出参数:无。
|
||
* 返回值:acked_nr到V(R)之间的15位循环距离。
|
||
* 前置条件:state非NULL,acked_nr和vr为15位序号,未确认区间未占满序号环。
|
||
* 调用关系:公共确认逻辑及Slave停止排空共用,因此保留外部链接;不重复校验。
|
||
*/
|
||
uint16_t IEC104_Link_Unconfirmed_Rx_Internal(const IEC104_LinkState_t *state)
|
||
{
|
||
return Number_Distance(state->acked_nr, state->vr);
|
||
}
|
||
|
||
/**
|
||
* 功能:判断接收确认已完成,或已由在途S帧覆盖当前接收位置。
|
||
* 输入参数:state——已通过完整合法性检查的链路状态。
|
||
* 输出参数:无,不修改state。
|
||
* 返回值:1表示acked_nr或在途S帧已覆盖当前V(R),否则返回0。
|
||
* 说明:不要求S完成回调已提交;不能用只覆盖旧V(R)的S帧替代最新确认。
|
||
* 调用关系:Master准备STOPDT_ACT和接收STOPDT_CON时分别检查,不能缓存旧结果。
|
||
* 返回1不推进acked_nr;在途S仍须按FIFO完成,失败仍须断连。
|
||
*/
|
||
int IEC104_Link_Rx_Ack_Covered_Internal(const IEC104_LinkState_t *state)
|
||
{
|
||
return state->acked_nr == state->vr ||
|
||
(state->s_ack_state == IEC104_S_ACK_SEND_PENDING &&
|
||
state->s_ack_nr == state->vr);
|
||
}
|
||
|
||
/**
|
||
* 功能:计算已提交发送完成、且尚未被对端确认的I帧数量。
|
||
* 输入参数:state——合法链路状态,确认位置没有越过本地完成位置。
|
||
* 输出参数:无,不修改state。
|
||
* 返回值:需等待对端确认的已完成帧数;仅有排队帧时返回0。
|
||
* 说明:ACK必须在对应Complete之后处理,已完成未确认区间为va..sent_vs。
|
||
*/
|
||
static uint16_t Sent_Unacknowledged_I_Count(const IEC104_LinkState_t *state)
|
||
{
|
||
return Number_Distance(state->va, state->sent_vs);
|
||
}
|
||
|
||
/**
|
||
* 功能:检查普通S确认帧的发送状态和快照组合是否一致。
|
||
* 输入参数:state——待检查的链路状态。
|
||
* 输出参数:无。
|
||
* 返回值:组合一致返回1,否则返回0。
|
||
* 说明:调用前必须已经通过Link_State_Fields_Valid()。
|
||
*/
|
||
static int S_Ack_State_Valid(const IEC104_LinkState_t *state)
|
||
{
|
||
switch (state->s_ack_state) {
|
||
case IEC104_S_ACK_IDLE:
|
||
return state->s_ack_nr == 0U;
|
||
case IEC104_S_ACK_SEND_PENDING:
|
||
/* N(R)=0是合法快照,是否在当前确认区间内留到完成时检查。 */
|
||
return 1;
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 功能:检查主站dt_state与dt_procedure组合,拒绝子站专用过程。
|
||
* 前置条件:state非NULL,公共字段及S状态已合法,role为CONTROLLING。
|
||
* 返回值:IDLE允许两种稳定状态;启动过程须STOPPED,停止过程须STARTED;
|
||
* 满足时返回1,其余返回0。只校验组合,不修改状态或验证报文。
|
||
* 调用关系:仅供本文件内State_Valid按角色分派,使用static内部链接。
|
||
* 不得反调State_Valid/Role_Valid,否则形成递归。
|
||
*/
|
||
static int Master_State_Consistent(const IEC104_LinkState_t *state)
|
||
{
|
||
switch (state->dt_procedure) {
|
||
case IEC104_DT_PROC_IDLE:
|
||
return 1;
|
||
case IEC104_DT_PROC_START_ACT_SEND_PENDING:
|
||
case IEC104_DT_PROC_START_CON_WAITING:
|
||
return state->dt_state == IEC104_DT_STOPPED;
|
||
case IEC104_DT_PROC_STOP_REQUESTED:
|
||
case IEC104_DT_PROC_STOP_ACT_SEND_PENDING:
|
||
case IEC104_DT_PROC_STOP_CON_WAITING:
|
||
return state->dt_state == IEC104_DT_STARTED;
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 子站只使用IDLE和STOP_DRAINING;后者表示已停用、等待确认完成。
|
||
* CON发送进度归SendBuffer,不参与启停组合校验。
|
||
*/
|
||
static int Slave_State_Consistent(const IEC104_LinkState_t *state)
|
||
{
|
||
return state->dt_procedure == IEC104_DT_PROC_IDLE ||
|
||
(state->dt_procedure == IEC104_DT_PROC_STOP_DRAINING && state->dt_state == IEC104_DT_STOPPED);
|
||
}
|
||
|
||
/**
|
||
* 功能:完整检查链路状态的字段定义域和跨流程一致性。
|
||
* 输入参数:state——链路状态;允许NULL,返回0。
|
||
* 输出参数:无,不修改state。
|
||
* 返回值:公共字段、S状态及本端角色的过程组合合法返回1,否则返回0。
|
||
* 调用关系:公共入口直接调用,角色入口经Role_Valid调用;本函数依次检查
|
||
* Fields、S_Ack,再分派本文件内的Master/Slave_State_Consistent,不会推进过程。
|
||
* 说明:角色末级校验不得反调本函数或Role_Valid,避免递归;本检查不证明事件
|
||
* 属于当前连接,也不替代本次N(S)/N(R)、发送长度和FIFO顺序检查。
|
||
*/
|
||
int IEC104_Link_State_Valid_Internal(const IEC104_LinkState_t *state)
|
||
{
|
||
if (!Link_State_Fields_Valid(state) || !S_Ack_State_Valid(state)) {
|
||
return 0;
|
||
}
|
||
switch (state->role) {
|
||
case IEC104_ROLE_CONTROLLING:
|
||
return Master_State_Consistent(state);
|
||
case IEC104_ROLE_CONTROLLED:
|
||
return Slave_State_Consistent(state);
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 功能:校验链路后生成只含数值的I帧发送进度快照。
|
||
* 输入参数:state——本连接链路状态;所有访问须串行。
|
||
* 输出参数:progress——仅返回1时写入,不得与state重叠;失败时保持原值。
|
||
* 返回值:1表示成功;0表示指针或完整链路状态非法。
|
||
* 说明:由本模块把内部序号映射为公开的进度含义,不提交任何接收或完成事件。
|
||
*/
|
||
int IEC104_Link_Get_Send_Progress(
|
||
const IEC104_LinkState_t *state, IEC104_IFrameSendProgress_t *progress)
|
||
{
|
||
IEC104_IFrameSendProgress_t prepared;
|
||
|
||
if (progress == NULL || !IEC104_Link_State_Valid_Internal(state)) {
|
||
return 0;
|
||
}
|
||
prepared.next_frame_ns = state->vs;
|
||
prepared.next_completion_ns = state->sent_vs;
|
||
prepared.peer_ack_nr = state->va;
|
||
*progress = prepared;
|
||
return 1;
|
||
}
|
||
|
||
/**
|
||
* 功能:生成链路观察快照,由Link统一解释未确认接收数量及启停/探测状态。
|
||
* 输入参数:state——当前链路;status——非NULL且不与state重叠的输出。
|
||
* 返回值:1成功并写status,0参数或完整状态非法;失败不改输出,不预留或推进链路。
|
||
* 说明:连接层可据未确认数量结束t2区间;控制动作准入另用Can_Prepare接口。
|
||
*/
|
||
int IEC104_Link_Get_Status(const IEC104_LinkState_t *state, IEC104_LinkStatus_t *status)
|
||
{
|
||
IEC104_LinkStatus_t prepared;
|
||
if (status == NULL || !IEC104_Link_State_Valid_Internal(state)) {
|
||
return 0;
|
||
}
|
||
prepared.role = state->role;
|
||
prepared.dt_state = state->dt_state;
|
||
prepared.dt_procedure = state->dt_procedure;
|
||
prepared.testfr_state = state->testfr_state;
|
||
prepared.unconfirmed_rx_count = IEC104_Link_Unconfirmed_Rx_Internal(state);
|
||
*status = prepared;
|
||
return 1;
|
||
}
|
||
|
||
/**
|
||
* 功能:检查链路是否符合绑定新连接发送管理器的初始状态条件。
|
||
* 输入参数:state——待检查状态,允许NULL。
|
||
* 输出参数:无,不修改链路。
|
||
* 返回值:状态合法、所有序号为0、STOPPED且各过程空闲时返回1,否则0。
|
||
* 说明:完整校验同时保证空闲S快照为0;不检查连接历史或实际TCP状态。
|
||
*/
|
||
int IEC104_Link_Is_Initial_State(const IEC104_LinkState_t *state)
|
||
{
|
||
return IEC104_Link_State_Valid_Internal(state) &&
|
||
state->vs == 0U && state->sent_vs == 0U && state->va == 0U &&
|
||
state->vr == 0U && state->acked_nr == 0U &&
|
||
state->dt_state == IEC104_DT_STOPPED && state->dt_procedure == IEC104_DT_PROC_IDLE &&
|
||
state->testfr_state == IEC104_TESTFR_IDLE && state->s_ack_state == IEC104_S_ACK_IDLE;
|
||
}
|
||
|
||
/**
|
||
* 功能:判断对端确认号是否位于V(A)到已提交本地完成位置sent_vs之间。
|
||
* 输入参数:state——有效链路状态;receive_number——对端携带的N(R)。
|
||
* 输出参数:无。
|
||
* 返回值:N(R)为15位且位于va到sent_vs的前向闭区间时返回1;越过完成位置或退回va之前均返回0。
|
||
*/
|
||
static int Acknowledge_Number_Valid(const IEC104_LinkState_t *state,
|
||
uint16_t receive_number)
|
||
{
|
||
uint16_t outstanding_count;
|
||
uint16_t acknowledged_count;
|
||
|
||
if (receive_number > IEC104_SEQUENCE_MASK) {
|
||
return 0;
|
||
}
|
||
|
||
outstanding_count = Number_Distance(state->va, state->sent_vs);
|
||
acknowledged_count = Number_Distance(state->va, receive_number);
|
||
|
||
/* Prepare只预留序号;必须先提交对应Complete,才允许ACK覆盖该帧。 */
|
||
return acknowledged_count <= outstanding_count;
|
||
}
|
||
|
||
/**
|
||
* 功能:提交一份本地已经完整发送的I/S帧所携带的N(R)快照。
|
||
* 输入参数:state——已通过完整校验的链路状态;receive_number——实际上线报文携带的N(R)。
|
||
* 输出参数:快照合法时推进acked_nr;非法时不修改状态。
|
||
* 返回值:OK表示快照已提交;FATAL表示快照非法。
|
||
* 说明:这是I/S完成流程共用的内部操作,不判断具体帧类型或发送长度。
|
||
* Complete_I_Send和Complete_S_Ack_Send已校验state;这里仅校验确认号定义域
|
||
* 和acked_nr..vr区间。返回OK表示本地发送提交,不表示对端已处理该确认。
|
||
*/
|
||
static IEC104_LinkResult_t Commit_Sent_NR(
|
||
IEC104_LinkState_t *state, uint16_t receive_number)
|
||
{
|
||
uint16_t acknowledged_count;
|
||
uint16_t outstanding_count;
|
||
|
||
if (receive_number > IEC104_SEQUENCE_MASK) {
|
||
return IEC104_LINK_RESULT_FATAL;
|
||
}
|
||
acknowledged_count = Number_Distance(state->acked_nr, receive_number);
|
||
outstanding_count = IEC104_Link_Unconfirmed_Rx_Internal(state);
|
||
if (acknowledged_count > outstanding_count) {
|
||
return IEC104_LINK_RESULT_FATAL;
|
||
}
|
||
|
||
state->acked_nr = receive_number;
|
||
return IEC104_LINK_RESULT_OK;
|
||
}
|
||
|
||
/**
|
||
* 功能:把链路状态对象设置为指定角色的新连接初始状态。
|
||
* 输入参数:state——非NULL状态对象;role——已由调用方校验或规范化的角色。
|
||
* 输出参数:保留传入角色,清零序号并复位确认、启停和探测状态。
|
||
* 返回值:无。
|
||
* 说明:仅供State_Init与State_Reset复用,不负责校验指针和角色。
|
||
*/
|
||
static void Initialize_State_Fields(IEC104_LinkState_t *state,
|
||
IEC104_LinkRole_t role)
|
||
{
|
||
state->role = role;
|
||
state->vs = 0U;
|
||
state->sent_vs = 0U;
|
||
state->va = 0U;
|
||
state->vr = 0U;
|
||
state->acked_nr = 0U;
|
||
state->s_ack_state = IEC104_S_ACK_IDLE;
|
||
state->s_ack_nr = 0U;
|
||
state->dt_state = IEC104_DT_STOPPED;
|
||
state->dt_procedure = IEC104_DT_PROC_IDLE;
|
||
state->testfr_state = IEC104_TESTFR_IDLE;
|
||
}
|
||
|
||
/**
|
||
* 功能:初始化一条连接的链路窗口与超时默认配置。
|
||
* 输入参数:config——待初始化的配置对象;允许为NULL。
|
||
* 输出参数:有效对象写入k、w、t1、t2和t3默认值。
|
||
* 返回值:无。
|
||
*/
|
||
/* 适用端:控制站与被控站。 */
|
||
void IEC104_Link_Config_Default(IEC104_LinkConfig_t *config)
|
||
{
|
||
if (config == NULL) {
|
||
return;
|
||
}
|
||
|
||
config->k = IEC104_DEFAULT_K;
|
||
config->w = IEC104_DEFAULT_W;
|
||
config->t1_ms = IEC104_DEFAULT_T1_MS;
|
||
config->t2_ms = IEC104_DEFAULT_T2_MS;
|
||
config->t3_ms = IEC104_DEFAULT_T3_MS;
|
||
}
|
||
|
||
/**
|
||
* 功能:为新连接初始化链路状态,并固定本端角色。
|
||
* 输入参数:state——待初始化的状态对象;role——CONTROLLING或CONTROLLED。
|
||
* 输出参数:成功时写入role,清零序号和确认快照,数据传输置STOPPED,
|
||
* 启停过程、S确认和TESTFR置IDLE。
|
||
* 返回值:1表示初始化成功;0表示state为NULL或role非法,此时不修改状态。
|
||
* 说明:仅初始化链路算法状态,连接配置由Config_Default单独初始化。
|
||
* 新连接处理任何链路事件前须初始化成功,不要求在TCP建连成功后才调用。
|
||
* 本模块未实现连接生命周期管理;旧事件隔离和状态内存安全复用由接入方负责。
|
||
*/
|
||
/* 适用端:控制站与被控站,由role参数确定本端角色。 */
|
||
int IEC104_Link_State_Init(IEC104_LinkState_t *state,
|
||
IEC104_LinkRole_t role)
|
||
{
|
||
if (state == NULL ||
|
||
(role != IEC104_ROLE_CONTROLLING &&
|
||
role != IEC104_ROLE_CONTROLLED)) {
|
||
return 0;
|
||
}
|
||
|
||
Initialize_State_Fields(state, role);
|
||
return 1;
|
||
}
|
||
|
||
/**
|
||
* 功能:复位链路序号、S确认、启停和探测状态,清除待完成过程的算法记录。
|
||
* 输入参数:state——待复位的链路状态;允许为NULL。
|
||
* 输出参数:state——保留有效角色,序号及S快照清零,回到STOPPED/IDLE;
|
||
* S确认和TESTFR置IDLE;非法角色规范化为INVALID。
|
||
* 返回值:无。
|
||
* 说明:本函数只复位链路算法字段,不能用于致命故障后在原连接上继续通信。
|
||
* 外部接入要求:调用方负责关闭故障连接,并防止旧事件提交到重新初始化后的状态;
|
||
* 对应连接管理、任务调度和定时器管理当前未实现,不属于本函数行为。
|
||
* Reset会读取原role,非空state须具有确定的字段内容,不能传入未初始化的自动变量。
|
||
*/
|
||
/* 适用端:控制站与被控站。 */
|
||
void IEC104_Link_State_Reset(IEC104_LinkState_t *state)
|
||
{
|
||
IEC104_LinkRole_t role;
|
||
|
||
if (state == NULL) {
|
||
return;
|
||
}
|
||
|
||
role = state->role;
|
||
if (role != IEC104_ROLE_CONTROLLING &&
|
||
role != IEC104_ROLE_CONTROLLED) {
|
||
role = IEC104_ROLE_INVALID;
|
||
}
|
||
Initialize_State_Fields(state, role);
|
||
}
|
||
|
||
/* I/S启停策略集中于下方判断函数;只查询当前状态,不缓存收发权限。 */
|
||
/* 子站START受理后即可接收I帧;STOP受理即关闭,后到START重新开放。 */
|
||
static int I_Frame_Receive_Allowed(const IEC104_LinkState_t *state)
|
||
{
|
||
return state->dt_state == IEC104_DT_STARTED &&
|
||
(state->role != IEC104_ROLE_CONTROLLED || state->dt_procedure == IEC104_DT_PROC_IDLE);
|
||
}
|
||
|
||
/**
|
||
* 功能:判断当前是否允许业务层预留新的I帧。
|
||
* 输入参数:state——已通过完整合法性检查的链路状态。
|
||
* 输出参数:无,不修改state。
|
||
* 返回值:1表示可预留新I帧,0表示当前方向尚未开放或正在停止。
|
||
* 说明:控制站在STARTDT_ACT之后可提前发送命令,无须等待STARTDT_CON。
|
||
* ACT待完成时新I帧必须排在ACT后,FIFO发送及完成顺序由调用方保证。
|
||
* 被控站受理START即开放准备;发送存储须保证新I帧不越过已有CON。
|
||
* 此处只检查启停门控,k窗口和序号环边界由Plan_I_Frame继续检查。
|
||
*/
|
||
static int New_I_Send_Allowed(const IEC104_LinkState_t *state)
|
||
{
|
||
if (state->dt_state == IEC104_DT_STARTED) {
|
||
return state->dt_procedure == IEC104_DT_PROC_IDLE;
|
||
}
|
||
|
||
return state->role == IEC104_ROLE_CONTROLLING &&
|
||
(state->dt_procedure == IEC104_DT_PROC_START_ACT_SEND_PENDING ||
|
||
state->dt_procedure == IEC104_DT_PROC_START_CON_WAITING);
|
||
}
|
||
|
||
/* 子站停止等待ACK时允许S;STOP_CON已生成后的STOPPED/IDLE拒绝S。 */
|
||
static int S_Frame_Receive_Allowed(const IEC104_LinkState_t *state)
|
||
{
|
||
return state->dt_state == IEC104_DT_STARTED ||
|
||
(state->role == IEC104_ROLE_CONTROLLED &&
|
||
state->dt_procedure == IEC104_DT_PROC_STOP_DRAINING);
|
||
}
|
||
|
||
/* 子站的S和CON共享FIFO;无需再等待START_CON的网络完成。 */
|
||
static int Dt_Allows_S_Ack(const IEC104_LinkState_t *state)
|
||
{
|
||
return state->role != IEC104_ROLE_CONTROLLED || state->dt_state == IEC104_DT_STARTED ||
|
||
state->dt_procedure == IEC104_DT_PROC_STOP_DRAINING;
|
||
}
|
||
|
||
/**
|
||
* 功能:判断停止阶段是否要求普通S确认绕过w门限。
|
||
* 输入参数:state——已通过完整合法性检查的链路状态。
|
||
* 输出参数:无,不修改state。
|
||
* 返回值:停止请求及排空的四个阶段为1,其余为0。
|
||
* 说明:只决定确认紧迫性,不绕过在途S或无待确认数据检查。
|
||
*/
|
||
static int Dt_Requires_Immediate_Ack(const IEC104_LinkState_t *state)
|
||
{
|
||
return state->dt_procedure == IEC104_DT_PROC_STOP_REQUESTED ||
|
||
state->dt_procedure == IEC104_DT_PROC_STOP_ACT_SEND_PENDING ||
|
||
state->dt_procedure == IEC104_DT_PROC_STOP_CON_WAITING ||
|
||
state->dt_procedure == IEC104_DT_PROC_STOP_DRAINING;
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* 功能:检查I帧的发送序号和确认号,并更新链路状态。
|
||
* 输入参数:state——当前链路状态;apci——已完成基础解析的I帧APCI。
|
||
* 输出参数:成功时更新V(A)和V(R);待确认数量由acked_nr到V(R)推导。
|
||
* 返回值:OK表示接收方向已经开放、序号合法并提交;FATAL表示接收方向未开放,
|
||
* 或状态、参数、帧类型、序号非法。
|
||
*/
|
||
/* 适用端:控制站与被控站。 */
|
||
IEC104_LinkResult_t IEC104_Link_Handle_I_Frame(
|
||
IEC104_LinkState_t *state, const IEC104_ApciInfo_t *apci)
|
||
{
|
||
uint16_t next_receive_number;
|
||
|
||
/* 先完成所有检查;合法状态的V(R)为15位,N(S)==V(R)也保证N(S)未越界。 */
|
||
if (!IEC104_Link_State_Valid_Internal(state) || apci == NULL ||
|
||
!I_Frame_Receive_Allowed(state) ||
|
||
apci->frame_type != IEC104_FRAME_TYPE_I ||
|
||
apci->send_number != state->vr ||
|
||
!Acknowledge_Number_Valid(state, apci->receive_number)) {
|
||
return IEC104_LINK_RESULT_FATAL;
|
||
}
|
||
|
||
next_receive_number = Number_Increment(state->vr);
|
||
/* 未确认区间不得占满15位序号环,否则acked_nr==V(R)会被误判为没有积压。 */
|
||
if (next_receive_number == state->acked_nr) {
|
||
return IEC104_LINK_RESULT_FATAL;
|
||
}
|
||
|
||
/* I帧中的N(R)确认本端发送数据,合法N(S)使本端的期望接收序号V(R)加1。 */
|
||
state->va = apci->receive_number;
|
||
state->vr = next_receive_number;
|
||
|
||
return IEC104_LINK_RESULT_OK;
|
||
}
|
||
|
||
/**
|
||
* 功能:检查S帧的确认号并更新已确认发送序号。
|
||
* 输入参数:state——当前链路状态;apci——已完成基础解析的S帧APCI。
|
||
* 输出参数:成功时更新V(A)。
|
||
* 返回值:OK表示接收阶段和确认号合法并提交;其他情况返回FATAL且不修改状态。
|
||
* 说明:STARTED下允许接收S;被控站STOPPED下仅STOP_DRAINING允许,以完成停止排空。
|
||
* 被控站START受理即STARTED,无须等待START_CON完成;STOPPED/IDLE拒绝S。
|
||
*/
|
||
/* 适用端:控制站与被控站。 */
|
||
IEC104_LinkResult_t IEC104_Link_Handle_S_Frame(
|
||
IEC104_LinkState_t *state, const IEC104_ApciInfo_t *apci)
|
||
{
|
||
if (!IEC104_Link_State_Valid_Internal(state) || apci == NULL ||
|
||
apci->frame_type != IEC104_FRAME_TYPE_S ||
|
||
!S_Frame_Receive_Allowed(state) ||
|
||
!Acknowledge_Number_Valid(state, apci->receive_number)) {
|
||
return IEC104_LINK_RESULT_FATAL;
|
||
}
|
||
|
||
/* S帧不携带用户数据,只用N(R)推进本端的发送确认位置。 */
|
||
state->va = apci->receive_number;
|
||
return IEC104_LINK_RESULT_OK;
|
||
}
|
||
|
||
/**
|
||
* 功能:只根据链路状态规划下一帧I帧的序号快照,不编码报文也不修改状态。
|
||
* 输入参数:state——当前链路状态;k——发送窗口上限;
|
||
* tx——调用方提供的非NULL临时快照。
|
||
* 输出参数:仅READY时tx写入当前V(S)、V(R),frame_length暂置0,等待编码层填写。
|
||
* 返回值:READY表示可生成;SKIPPED表示门控或窗口阻止发送;ERROR表示参数/状态非法。
|
||
* 说明:本函数是只读规划阶段。调用方必须在同一串行临界区内完成编码,并且
|
||
* 只有编码成功后才能把tx提交给发送队列并推进V(S)。
|
||
* Can_Prepare_I_Frame与Prepare_I_Frame共用本函数;tx均指向栈上临时对象,故此处不再判空。
|
||
*/
|
||
static IEC104_PlanResult_t Plan_I_Frame(const IEC104_LinkState_t *state,
|
||
uint16_t k,
|
||
IEC104_IFrameTx_t *tx)
|
||
{
|
||
uint16_t next_send_number;
|
||
uint16_t outstanding_count;
|
||
|
||
if (!IEC104_Link_State_Valid_Internal(state) ||
|
||
k == 0U || k > IEC104_SEQUENCE_MASK) {
|
||
return IEC104_PLAN_ERROR;
|
||
}
|
||
if (!New_I_Send_Allowed(state)) {
|
||
return IEC104_PLAN_SKIPPED;
|
||
}
|
||
|
||
outstanding_count = Number_Distance(state->va, state->vs);
|
||
if (outstanding_count >= k) {
|
||
return IEC104_PLAN_SKIPPED;
|
||
}
|
||
|
||
next_send_number = Number_Increment(state->vs);
|
||
/* 未完成区间包含在未确认区间内;保留显式检查,避免完成边界的回绕歧义。 */
|
||
if (next_send_number == state->sent_vs) {
|
||
return IEC104_PLAN_SKIPPED;
|
||
}
|
||
|
||
tx->send_number = state->vs;
|
||
tx->receive_number = state->vr;
|
||
tx->frame_length = 0U;
|
||
return IEC104_PLAN_READY;
|
||
}
|
||
|
||
/* 只读复用实际构帧的启停、k窗口及序号边界检查,不编码、不预留N(S)。 */
|
||
int IEC104_Link_Can_Prepare_I_Frame(const IEC104_LinkState_t *state, uint16_t k)
|
||
{
|
||
IEC104_IFrameTx_t planned_tx;
|
||
IEC104_PlanResult_t planned = Plan_I_Frame(state, k, &planned_tx);
|
||
return planned == IEC104_PLAN_READY ? 1 : (planned == IEC104_PLAN_SKIPPED ? 0 : -1);
|
||
}
|
||
|
||
/**
|
||
* 功能:检查启停状态和k窗口,构造I帧并立即预留其N(S)。
|
||
* 输入参数:state——链路状态;k——发送窗口;asdu/asdu_len——完整ASDU;
|
||
* frame/capacity——输出缓冲及容量;tx——发送队列保存的构帧快照。
|
||
* 输出参数:成功时写入frame和tx,并推进V(S)。
|
||
* 返回值:成功返回完整APDU长度;0表示当前不允许新I帧或窗口已满;
|
||
* -1表示参数或状态非法。
|
||
* 说明:成功后N(S)不可撤销;报文须进入严格FIFO队列,发送失败由上层断连。
|
||
*/
|
||
/* 适用端:控制站与被控站。 */
|
||
int IEC104_Link_Prepare_I_Frame(IEC104_LinkState_t *state,
|
||
uint16_t k,
|
||
const uint8_t *asdu,
|
||
uint16_t asdu_len,
|
||
uint8_t *frame,
|
||
uint16_t capacity,
|
||
IEC104_IFrameTx_t *tx)
|
||
{
|
||
IEC104_IFrameTx_t planned_tx;
|
||
IEC104_PlanResult_t planned;
|
||
int length;
|
||
|
||
if (asdu == NULL ||
|
||
asdu_len < IEC104_ASDU_HEADER_LEN ||
|
||
asdu_len > IEC104_MAX_ASDU_LEN ||
|
||
frame == NULL ||
|
||
capacity < (uint16_t)(IEC104_APCI_LEN + asdu_len) ||
|
||
tx == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
planned = Plan_I_Frame(state, k, &planned_tx);
|
||
if (planned != IEC104_PLAN_READY) {
|
||
return planned == IEC104_PLAN_SKIPPED ? 0 : -1;
|
||
}
|
||
|
||
length = IEC104_Build_Iframe(frame,
|
||
planned_tx.send_number,
|
||
planned_tx.receive_number,
|
||
asdu, (int)asdu_len);
|
||
if (length < 0) {
|
||
return -1;
|
||
}
|
||
|
||
/* 编码成功后才一次性提交状态和发送队列快照。 */
|
||
planned_tx.frame_length = (uint16_t)length;
|
||
*tx = planned_tx;
|
||
state->vs = Number_Increment(planned_tx.send_number);
|
||
return length;
|
||
}
|
||
|
||
/**
|
||
* 功能:提交一帧由Prepare_I_Frame生成的I帧最终本地发送结果。
|
||
* 输入参数:state——链路状态;tx——构帧时保存的I帧快照;
|
||
* sent_len——最终累计成功发送的字节数,等于快照长度才算成功;发送失败也可传-1。
|
||
* 输出参数:完整发送且快照合法时推进acked_nr和sent_vs;失败时不修改状态。
|
||
* 返回值:OK表示完整发送并提交;FATAL表示参数、状态、长度、顺序或快照非法。
|
||
* 说明:Send限定为本地发送提交;返回OK不表示对端已经确认本帧。
|
||
* V(S)已在Prepare阶段推进;本接口按FIFO推进发送完成边界,不再次推进V(S)。
|
||
*/
|
||
/* 适用端:控制站与被控站。 */
|
||
IEC104_LinkResult_t IEC104_Link_Complete_I_Send(
|
||
IEC104_LinkState_t *state, const IEC104_IFrameTx_t *tx, int sent_len)
|
||
{
|
||
IEC104_LinkResult_t result;
|
||
|
||
if (!IEC104_Link_State_Valid_Internal(state) || tx == NULL ||
|
||
tx->send_number > IEC104_SEQUENCE_MASK ||
|
||
tx->receive_number > IEC104_SEQUENCE_MASK ||
|
||
tx->frame_length <
|
||
(uint16_t)(IEC104_APCI_LEN + IEC104_ASDU_HEADER_LEN) ||
|
||
tx->frame_length >
|
||
(uint16_t)(IEC104_APCI_LEN + IEC104_MAX_ASDU_LEN) ||
|
||
sent_len != (int)tx->frame_length) {
|
||
return IEC104_LINK_RESULT_FATAL;
|
||
}
|
||
|
||
/* 没有待完成帧或N(S)不匹配时拒绝,避免重复/乱序回调提交到别的帧。 */
|
||
if (state->sent_vs == state->vs ||
|
||
tx->send_number != state->sent_vs) {
|
||
return IEC104_LINK_RESULT_FATAL;
|
||
}
|
||
|
||
result = Commit_Sent_NR(state, tx->receive_number);
|
||
if (result != IEC104_LINK_RESULT_OK) {
|
||
return result;
|
||
}
|
||
state->sent_vs = Number_Increment(tx->send_number);
|
||
return IEC104_LINK_RESULT_OK;
|
||
}
|
||
|
||
/**
|
||
* 功能:判断最早已完成发送且尚未被对端确认的I帧是否达到t1超时。
|
||
* 输入参数:state——链路状态;elapsed_ms——该帧实际完成发送后经过的毫秒数;
|
||
* t1_ms——t1配置值。
|
||
* 输出参数:无,不修改state。
|
||
* 返回值:1表示已经超时;0表示无已完成未确认帧或尚未超时;-1表示参数非法。
|
||
* 说明:只预留未Complete的帧不参与t1;时间戳和队列仍由调用方维护。
|
||
*/
|
||
/* 适用端:控制站与被控站。 */
|
||
int IEC104_Link_T1_I_Ack_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 (Sent_Unacknowledged_I_Count(state) == 0U) {
|
||
return 0;
|
||
}
|
||
|
||
return elapsed_ms >= t1_ms ? 1 : 0;
|
||
}
|
||
|
||
/**
|
||
* 功能:只根据链路状态规划S确认的N(R),不编码报文也不修改状态。
|
||
* 输入参数:state——当前链路状态;w——接收确认门限;
|
||
* immediate——非0表示绕过w门限立即确认;
|
||
* receive_number——用于接收待编码N(R)的输出地址。
|
||
* 输出参数:仅READY时写入当前V(R)。
|
||
* 返回值:READY表示应生成;SKIPPED表示当前无需或已有S帧在途。
|
||
* 前置条件:state已通过完整校验,w在1..32767内,receive_number为有效输出地址。
|
||
* 说明:STOPDT排空半态会自动绕过w门限。实际S帧编码成功后,构帧步骤才把
|
||
* receive_number保存到s_ack_nr并进入SEND_PENDING。
|
||
*/
|
||
static IEC104_PlanResult_t Plan_S_Ack(const IEC104_LinkState_t *state,
|
||
uint16_t w, int immediate,
|
||
uint16_t *receive_number)
|
||
{
|
||
uint16_t unconfirmed_count;
|
||
|
||
if (!Dt_Allows_S_Ack(state)) {
|
||
return IEC104_PLAN_SKIPPED;
|
||
}
|
||
if (state->s_ack_state == IEC104_S_ACK_SEND_PENDING) {
|
||
return IEC104_PLAN_SKIPPED;
|
||
}
|
||
|
||
unconfirmed_count = IEC104_Link_Unconfirmed_Rx_Internal(state);
|
||
if (unconfirmed_count == 0U) {
|
||
return IEC104_PLAN_SKIPPED;
|
||
}
|
||
|
||
if (!immediate && unconfirmed_count < w &&
|
||
!Dt_Requires_Immediate_Ack(state)) {
|
||
return IEC104_PLAN_SKIPPED;
|
||
}
|
||
|
||
*receive_number = state->vr;
|
||
return IEC104_PLAN_READY;
|
||
}
|
||
|
||
/**
|
||
* 功能:只读查询普通S确认需求,供连接层在取得控制槽前决定是否调用发送准备接口。
|
||
* 输入参数:state——当前链路;w——合法接收门限;immediate——非0立即确认。
|
||
* 返回值:1需要且允许准备,0当前无需/不能准备,-1参数或状态非法。
|
||
* 说明:直接复用构帧使用的Plan_S_Ack;查询不消耗发送编号、不改变S快照或acked_nr。
|
||
*/
|
||
int IEC104_Link_Can_Prepare_S_Ack(const IEC104_LinkState_t *state, uint16_t w, int immediate)
|
||
{
|
||
uint16_t receive_number;
|
||
if (!IEC104_Link_State_Valid_Internal(state) || w == 0U || w > IEC104_SEQUENCE_MASK) {
|
||
return -1;
|
||
}
|
||
return Plan_S_Ack(state, w, immediate, &receive_number) == IEC104_PLAN_READY;
|
||
}
|
||
|
||
/**
|
||
* 功能:按已规划的N(R)编码S帧,成功后提交公共S确认状态。
|
||
* 输入参数:state——链路状态;response——输出缓冲;receive_number——规划的N(R)。
|
||
* 输出参数:成功时写入6字节S帧,将N(R)保存到s_ack_nr并置SEND_PENDING。
|
||
* 前置条件:state已通过完整校验且无S在途;response非NULL且至少6字节;
|
||
* receive_number为本次选定的有效15位确认号。
|
||
* 说明:普通S确认和STOPDT共用此步骤;使用传入快照,不重新规划或读取V(R)。
|
||
* 本步骤不推进acked_nr,须等Complete_S_Ack_Send确认发送成功后提交。
|
||
* 返回值:6表示编码并提交成功;-1表示编码失败,此时不提交状态。
|
||
* 调用关系:本文件的普通确认/t2以及Master、Slave的停止准备均调用本函数,
|
||
* 所以不能直接设为static;是否应发送、容量和角色均由调用入口检查。
|
||
*/
|
||
int IEC104_Link_Prepare_S_Ack_Internal(IEC104_LinkState_t *state,
|
||
uint8_t *response, uint16_t receive_number)
|
||
{
|
||
int length = IEC104_Build_Sframe(response, receive_number);
|
||
|
||
if (length != IEC104_APCI_LEN) {
|
||
return -1;
|
||
}
|
||
|
||
/* 编码成功后提交与报文字节完全一致的N(R)快照。 */
|
||
state->s_ack_nr = receive_number;
|
||
state->s_ack_state = IEC104_S_ACK_SEND_PENDING;
|
||
return length;
|
||
}
|
||
|
||
/**
|
||
* 功能:普通S确认与t2共用的规划、编码和提交路径。
|
||
* 输入参数:state——链路状态;w——确认门限;immediate——非0立即确认;
|
||
* response——输出缓冲。
|
||
* 输出参数:生成时写入S帧并记录其N(R)快照和在途状态;未生成时保持原值。
|
||
* 前置条件:state和w已校验,response至少有IEC104_APCI_LEN字节空间。
|
||
* 返回值:6表示生成,0表示当前不生成,-1表示编码失败。
|
||
*/
|
||
static int Prepare_S_Ack_Validated(IEC104_LinkState_t *state,
|
||
uint16_t w, int immediate,
|
||
uint8_t *response)
|
||
{
|
||
uint16_t receive_number;
|
||
IEC104_PlanResult_t planned = Plan_S_Ack(state, w, immediate, &receive_number);
|
||
|
||
if (planned != IEC104_PLAN_READY) {
|
||
return 0;
|
||
}
|
||
return IEC104_Link_Prepare_S_Ack_Internal(state, response, receive_number);
|
||
}
|
||
|
||
/**
|
||
* 功能:按w门限、显式立即确认或STOPDT排空要求生成普通S帧并保存N(R)快照。
|
||
* 输入参数:state——链路状态;w——接收确认门限;immediate——非0跳过w门限,仍受启停及S在途限制;
|
||
* response——输出缓冲;capacity——至少6字节。
|
||
* 输出参数:成功时写入S帧并记录SEND_PENDING及构帧时V(R)。
|
||
* 返回值:6表示生成;0表示当前无需或不能由普通流程生成;-1表示参数/状态非法。
|
||
*/
|
||
/* 适用端:控制站与被控站。 */
|
||
int IEC104_Link_Prepare_S_Ack(IEC104_LinkState_t *state,
|
||
uint16_t w, int immediate,
|
||
uint8_t *response, uint16_t capacity)
|
||
{
|
||
if (!IEC104_Link_State_Valid_Internal(state) || w == 0U || w > IEC104_SEQUENCE_MASK ||
|
||
response == NULL || capacity < IEC104_APCI_LEN) {
|
||
return -1;
|
||
}
|
||
|
||
return Prepare_S_Ack_Validated(state, w, immediate, response);
|
||
}
|
||
|
||
/**
|
||
* 功能:提交公共S确认帧的最终发送结果,普通确认与两端停止流程共用。
|
||
* 输入参数:state——链路状态;sent_len——最终累计发送字节数。
|
||
* 输出参数:成功时按快照推进acked_nr并回IDLE;失败或快照失效时不推进确认。
|
||
* 返回值:OK表示发送成功并提交;IGNORED表示没有S帧等待完成;
|
||
* FATAL表示状态非法、发送失败或快照失效。
|
||
* 说明:状态非法直接返回且不清理;状态合法但无S在途时先返回IGNORED,
|
||
* 不检查sent_len。有S在途时,发送失败或快照失效均清除S状态和快照。
|
||
* 成功同样清除S状态/快照,所有结果均保留当前启停过程。
|
||
* 普通确认及两端STOPDT中的S帧均由本接口完成。本接口不携带帧身份,
|
||
* 调用方须按原发送节点恰好分派一次。
|
||
*/
|
||
/* 适用端:控制站与被控站。 */
|
||
IEC104_LinkResult_t IEC104_Link_Complete_S_Ack_Send(
|
||
IEC104_LinkState_t *state, int sent_len)
|
||
{
|
||
IEC104_LinkResult_t result;
|
||
|
||
if (!IEC104_Link_State_Valid_Internal(state)) {
|
||
return IEC104_LINK_RESULT_FATAL;
|
||
}
|
||
if (state->s_ack_state != IEC104_S_ACK_SEND_PENDING) {
|
||
return IEC104_LINK_RESULT_IGNORED;
|
||
}
|
||
|
||
if (sent_len != IEC104_APCI_LEN) {
|
||
state->s_ack_state = IEC104_S_ACK_IDLE;
|
||
state->s_ack_nr = 0U;
|
||
return IEC104_LINK_RESULT_FATAL;
|
||
}
|
||
|
||
result = Commit_Sent_NR(state, state->s_ack_nr);
|
||
state->s_ack_state = IEC104_S_ACK_IDLE;
|
||
state->s_ack_nr = 0U;
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 功能:在最早已接收但本端尚未确认的I帧达到t2等待时间后生成S确认帧。
|
||
* 输入参数:state——链路状态;elapsed_ms——已经等待的毫秒数;
|
||
* t2_ms——t2配置值;response——输出缓冲;capacity——至少6字节。
|
||
* 输出参数:到期且可生成时写入S帧,并保存普通S帧的N(R)快照。
|
||
* 返回值:6表示生成;0表示尚不需要或当前已有流程负责;-1表示参数/状态非法。
|
||
*/
|
||
/* 适用端:控制站与被控站。 */
|
||
int IEC104_Link_Prepare_T2_Ack(IEC104_LinkState_t *state,
|
||
uint32_t elapsed_ms, uint32_t t2_ms,
|
||
uint8_t *response, uint16_t capacity)
|
||
{
|
||
if (!IEC104_Link_State_Valid_Internal(state) || response == NULL ||
|
||
capacity < IEC104_APCI_LEN || t2_ms == 0U) {
|
||
return -1;
|
||
}
|
||
|
||
if (elapsed_ms < t2_ms) {
|
||
return 0;
|
||
}
|
||
|
||
/* 已校验输入;到期后由共用规划判断待确认数据和在途S帧。 */
|
||
return Prepare_S_Ack_Validated(state, 1U, 1, response);
|
||
}
|
||
|
||
/**
|
||
* 功能:检查链路状态自洽且本端固定角色与调用接口要求一致。
|
||
* 输入参数:state——待检查的链路状态对象;role——接口要求的本端角色。
|
||
* 输出参数:无,不修改state。
|
||
* 返回值:状态自洽且角色匹配返回1,否则返回0。
|
||
* 调用关系:Master/Slave角色专用公开入口使用;先调用State_Valid再比较角色,
|
||
* 短路求值保证state为NULL时不会解引用。末级角色校验不得反调此函数。
|
||
*/
|
||
int IEC104_Link_Role_Valid_Internal(const IEC104_LinkState_t *state,
|
||
IEC104_LinkRole_t role)
|
||
{
|
||
return IEC104_Link_State_Valid_Internal(state) && state->role == role;
|
||
}
|
||
|
||
/**
|
||
* 功能:处理TESTFR_ACT,复用U帧构造接口生成TESTFR_CON应答。
|
||
* 输入参数:apci——已通过APCI解析的TESTFR_ACT;response——输出缓冲;
|
||
* capacity——缓冲实际容量,至少6字节。
|
||
* 输出参数:response——写入68 04 83 00 00 00。
|
||
* 返回值:OK表示生成固定6字节应答;FATAL表示参数或报文非法。
|
||
* 说明:不读写链路状态,不执行网络发送,不处理主动探测或超时。
|
||
* 此应答没有对应Complete接口;发送队列负责发送结果和失败断连,不能
|
||
* 把TESTFR_CON发送完成交给处理本端主动ACT的Complete_Testfr_Act_Send。
|
||
*/
|
||
/* 适用端:控制站与被控站。 */
|
||
IEC104_LinkResult_t IEC104_Link_Handle_Testfr_Act(
|
||
const IEC104_ApciInfo_t *apci, uint8_t *response, uint16_t capacity)
|
||
{
|
||
if (apci == NULL || response == NULL || capacity < IEC104_APCI_LEN ||
|
||
apci->frame_type != IEC104_FRAME_TYPE_U ||
|
||
apci->u_function != IEC104_U_TESTFR_ACT) {
|
||
return IEC104_LINK_RESULT_FATAL;
|
||
}
|
||
|
||
/* 缓冲已验证且功能码固定,编码必定生成6字节;(void)表示有意忽略返回值。 */
|
||
(void)IEC104_Build_Uframe(response, IEC104_U_TESTFR_CON);
|
||
return IEC104_LINK_RESULT_OK;
|
||
}
|
||
|
||
/* 已通过完整状态检查;查询与构帧共用唯一的主动探测准入条件。 */
|
||
static int Testfr_Act_Allowed(const IEC104_LinkState_t *state)
|
||
{
|
||
return state->testfr_state == IEC104_TESTFR_IDLE;
|
||
}
|
||
|
||
/**
|
||
* 功能:查询当前是否允许新一轮主动TESTFR,不读取时钟或改变状态。
|
||
* 输入参数:state——待检查链路;调用方另外判断是否已满足t3空闲时长。
|
||
* 返回值:1可准备,0当前已有探测,-1参数或完整状态非法。
|
||
* 说明:普通主动探测和t3构帧均复用Testfr_Act_Allowed,不在连接层另判枚举值。
|
||
*/
|
||
int IEC104_Link_Can_Prepare_Testfr_Act(const IEC104_LinkState_t *state)
|
||
{
|
||
if (!IEC104_Link_State_Valid_Internal(state)) {
|
||
return -1;
|
||
}
|
||
return Testfr_Act_Allowed(state);
|
||
}
|
||
|
||
/**
|
||
* 功能:主动TESTFR与t3共用的状态检查、编码和提交路径。
|
||
* 输入参数:state——链路状态;request——输出缓冲。
|
||
* 输出参数:生成时写入6字节TESTFR_ACT,并进入TESTFR_ACT_SEND_PENDING。
|
||
* 前置条件:state已校验,request至少有IEC104_APCI_LEN字节空间。
|
||
* 返回值:6表示生成,0表示已有探测。
|
||
* 说明:与只读查询共用Testfr_Act_Allowed,真正发送完成后才开始等待CON;
|
||
* 本函数不判断链路空闲时间,参数校验和t3时间比较由公开入口负责。
|
||
*/
|
||
static int Prepare_Testfr_Act_Validated(IEC104_LinkState_t *state,
|
||
uint8_t *request)
|
||
{
|
||
if (!Testfr_Act_Allowed(state)) {
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 缓冲已验证且TESTFR_ACT为合法常量,编码器必定写入6字节并返回6。
|
||
* (void)只显式丢弃这次调用的返回值,函数仍会执行编码并写入request。
|
||
*/
|
||
(void)IEC104_Build_Uframe(request, IEC104_U_TESTFR_ACT);
|
||
state->testfr_state = IEC104_TESTFR_ACT_SEND_PENDING;
|
||
return IEC104_APCI_LEN;
|
||
}
|
||
|
||
/**
|
||
* 功能:生成主动TESTFR_ACT请求,并记录等待发送完成的状态。
|
||
* 输入参数:state——链路状态;request——输出缓冲;capacity——实际容量,至少6字节。
|
||
* 输出参数:request——写入68 04 43 00 00 00;state——探测转为ACT_SEND_PENDING。
|
||
* 返回值:6表示生成请求;0表示已有探测进行中;-1表示参数或链路状态非法。
|
||
* 说明:返回0或-1时不修改状态和缓冲;不执行发送,不修改启停、序号及确认位置。
|
||
* 返回6后发送层必须保证恰好提交一次Complete;发送超时也以失败长度提交。
|
||
*/
|
||
/* 适用端:控制站与被控站。 */
|
||
int IEC104_Link_Prepare_Testfr_Act(IEC104_LinkState_t *state,
|
||
uint8_t *request, uint16_t capacity)
|
||
{
|
||
if (!IEC104_Link_State_Valid_Internal(state) || request == NULL || capacity < IEC104_APCI_LEN) {
|
||
return -1;
|
||
}
|
||
|
||
return Prepare_Testfr_Act_Validated(state, request);
|
||
}
|
||
|
||
/**
|
||
* 功能:在链路空闲达到t3后生成主动TESTFR_ACT请求。
|
||
* 输入参数:state——链路状态;idle_elapsed_ms——空闲毫秒数;
|
||
* t3_ms——t3配置值;request——输出缓冲;capacity——至少6字节。
|
||
* 输出参数:到期且允许生成时写入TESTFR_ACT,并复用主动探测发送状态。
|
||
* 返回值:6表示生成;0表示尚未到期或已有探测;-1表示参数或链路状态非法。
|
||
* 说明:先校验完整状态和参数,再比较t3时间;返回6只表示生成报文,尚未发送。
|
||
* 连接层统一定义有效链路活动,并更新t3的单调计时起点;本函数不保存时刻。
|
||
* t3活动更新不得重置正在等待TESTFR_CON的t1起点。
|
||
*/
|
||
/* 适用端:控制站与被控站。 */
|
||
int IEC104_Link_Prepare_T3_Testfr(IEC104_LinkState_t *state,
|
||
uint32_t idle_elapsed_ms,
|
||
uint32_t t3_ms,
|
||
uint8_t *request,
|
||
uint16_t capacity)
|
||
{
|
||
if (!IEC104_Link_State_Valid_Internal(state) || request == NULL ||
|
||
capacity < IEC104_APCI_LEN || t3_ms == 0U) {
|
||
return -1;
|
||
}
|
||
|
||
if (idle_elapsed_ms < t3_ms) {
|
||
return 0;
|
||
}
|
||
|
||
/* 已校验输入;t3到期后直接复用主动探测准备路径。 */
|
||
return Prepare_Testfr_Act_Validated(state, request);
|
||
}
|
||
|
||
/**
|
||
* 功能:提交TESTFR_ACT最终发送结果,成功后开始等待TESTFR_CON。
|
||
* 输入参数:state——链路状态;sent_len——最终累计成功发送字节数,恰好6表示成功。
|
||
* 输出参数:有匹配ACT时,完整发送成功转CON_PENDING;发送失败回IDLE并由上层断连。
|
||
* 返回值:OK表示完整发送并提交;IGNORED表示没有待完成的ACT;
|
||
* FATAL表示状态非法或发送失败。
|
||
* 说明:部分写入仍在继续时不要调用;状态合法但无匹配ACT时先返回IGNORED,不检查长度。
|
||
* 调用方须先提交ACT完整发送结果,再分派对端CON;不缓存提前CON。
|
||
* 返回OK后,由连接层以本次ACT实际完整发送时刻为等待CON的t1起点,
|
||
* 不能用延迟处理Complete时的当前时刻代替;本函数不操作外部定时器。
|
||
*/
|
||
/* 适用端:控制站与被控站。 */
|
||
IEC104_LinkResult_t IEC104_Link_Complete_Testfr_Act_Send(
|
||
IEC104_LinkState_t *state, int sent_len)
|
||
{
|
||
if (!IEC104_Link_State_Valid_Internal(state)) {
|
||
return IEC104_LINK_RESULT_FATAL;
|
||
}
|
||
|
||
if (state->testfr_state != IEC104_TESTFR_ACT_SEND_PENDING) {
|
||
return IEC104_LINK_RESULT_IGNORED;
|
||
}
|
||
|
||
if (sent_len != IEC104_APCI_LEN) {
|
||
state->testfr_state = IEC104_TESTFR_IDLE;
|
||
return IEC104_LINK_RESULT_FATAL;
|
||
}
|
||
|
||
/* 完整发送成功只开始等待对端确认;收到CON后才结束本轮探测。 */
|
||
state->testfr_state = IEC104_TESTFR_CON_PENDING;
|
||
return IEC104_LINK_RESULT_OK;
|
||
}
|
||
|
||
/**
|
||
* 功能:接受正在等待的TESTFR_CON,结束本端主动探测。
|
||
* 输入参数:state——链路状态;apci——已通过APCI解析的TESTFR_CON。
|
||
* 输出参数:仅CON_PENDING时回IDLE;非OK时不修改状态。
|
||
* 返回值:OK表示接受确认;IGNORED表示当前未等待CON;
|
||
* FATAL表示参数、报文或链路状态非法。
|
||
* 说明:输入校验先于等待状态检查;ACT_SEND_PENDING时返回IGNORED且不缓存CON。
|
||
* 调用方须先提交ACT完整发送结果,再处理CON;本接口不修改启停状态、
|
||
* 链路序号及确认位置,也不生成应答报文。返回OK后由连接层取消本轮等待计时,
|
||
* 本接口不操作外部定时器。
|
||
*/
|
||
/* 适用端:控制站与被控站。 */
|
||
IEC104_LinkResult_t IEC104_Link_Handle_Testfr_Con(
|
||
IEC104_LinkState_t *state, const IEC104_ApciInfo_t *apci)
|
||
{
|
||
if (!IEC104_Link_State_Valid_Internal(state) || apci == NULL ||
|
||
apci->frame_type != IEC104_FRAME_TYPE_U ||
|
||
apci->u_function != IEC104_U_TESTFR_CON) {
|
||
return IEC104_LINK_RESULT_FATAL;
|
||
}
|
||
|
||
if (state->testfr_state != IEC104_TESTFR_CON_PENDING) {
|
||
return IEC104_LINK_RESULT_IGNORED;
|
||
}
|
||
|
||
state->testfr_state = IEC104_TESTFR_IDLE;
|
||
return IEC104_LINK_RESULT_OK;
|
||
}
|
||
|
||
/**
|
||
* 功能:判断主动TESTFR流程等待TESTFR_CON是否达到t1超时。
|
||
* 输入参数:state——链路状态;elapsed_ms——距当前探测ACT实际完整发送的毫秒数;
|
||
* t1_ms——t1配置值,必须大于0。
|
||
* 输出参数:无,不修改state。
|
||
* 返回值:1表示已经超时;0表示未等待CON或尚未超时;-1表示参数或链路状态非法。
|
||
* 说明:先校验状态及t1_ms;合法时仅对CON_PENDING判定t1,ACT_SEND_PENDING返回0。
|
||
* ACT_SEND_PENDING表示本地发送结果尚未提交,发送层须限制排队和发送等待时间。
|
||
* 连接层定时器只负责唤醒;回调须校验连接代次,并在连接串行调度中按当前
|
||
* 有效探测的起点重新计算elapsed_ms,不能沿用同一连接上旧探测的时间差。
|
||
* 本函数不识别探测轮次,也不操作定时器或连接;只有返回1才按t1超时断连。
|
||
*/
|
||
/* 适用端:控制站与被控站。 */
|
||
int IEC104_Link_T1_Testfr_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->testfr_state != IEC104_TESTFR_CON_PENDING) {
|
||
return 0;
|
||
}
|
||
|
||
return elapsed_ms >= t1_ms ? 1 : 0;
|
||
}
|