/** * IEC 60870-5-104旧单路接收兼容层。 * * 逐字节成帧后调用Codec解析APCI,再使用私有链路状态检查I/S序号。 * 编解码独立实现在IEC104_Codec.c;本入口仍保留单路全局状态。 */ #include "IEC104.h" #include "IEC104_Link_Common.h" #include "IEC104_Log.h" /* 当前单路接收解析器;静态存储期保证启动时处于RX_SYNC且计数为零。 */ static IEC104_RxParser_t g_rx_parser; /* * 旧单路收帧入口只用于成帧和I/S序号检查,不分派U帧启停流程; * 因此兼容路径预设数据传输已启动。完整连接应显式持有自己的状态。 */ static IEC104_LinkState_t g_link_state = { .role = IEC104_ROLE_CONTROLLED, .dt_state = IEC104_DT_STARTED }; /** * 功能:内部复位当前APDU接收进度,重新进入等待起始字节状态。 * 输入参数:无。 * 输出参数:无。 * 返回值:无。 */ static void Rx_Reset_State(void) { g_rx_parser.stage = RX_SYNC; g_rx_parser.len = 0; g_rx_parser.expected_len = 0; } /** * 功能:公开复位接收状态机,丢弃当前未完成APDU。 * 输入参数:无。 * 输出参数:无;内部接收阶段恢复同步搜索,已收长度和预期长度清零。 * 返回值:无。 * 说明:不清零接收缓冲区,不修改旧单路入口的链路序号状态。 */ void IEC104_Rx_Reset(void) { Rx_Reset_State(); } /** * 功能:复位旧单路成帧入口的I/S序号状态,保留当前APDU接收进度。 * 输入参数:无。 * 输出参数:无;内部g_link_state重新初始化为被控站,并恢复STARTED预设。 * 返回值:无。 * 说明:仅影响IEC104_Rx_Feed_Byte使用的私有全局状态。外部连接的独立状态对象 * 使用IEC104_Link_State_Reset复位,该接口恢复STOPPED;旧会话结束后再复位。 */ void IEC104_Legacy_Link_Reset(void) { /* 兼容入口不处理STARTDT U帧,复位后仍预设已进入数据传输阶段。 */ (void)IEC104_Link_State_Init( &g_link_state, IEC104_ROLE_CONTROLLED); g_link_state.dt_state = IEC104_DT_STARTED; } /** * 功能:检查已收齐APDU的APCI及适用的I/S序号,再复位接收进度;不解析ASDU内容。 * 输入参数:无。 * 输出参数:无。 * 返回值:返回完整帧、帧格式错误或链路序号错误对应的接收结果。 */ static IEC104_RxResult_t Rx_Finish_Frame(void) { IEC104_ApciInfo_t apci; uint16_t frame_len = g_rx_parser.len; if (!IEC104_Parse_APCI(g_rx_parser.buf, frame_len, &apci)) { IEC104_LOG_WARN("[IEC104] invalid APCI frame; receiver reset"); Rx_Reset_State(); return IEC104_RX_FRAME_ERROR; } /* I帧检查N(S)连续性和N(R),S帧只检查N(R);U帧不使用序号。 */ if ((apci.frame_type == IEC104_FRAME_TYPE_I && IEC104_Link_Handle_I_Frame(&g_link_state, &apci) == IEC104_LINK_RESULT_FATAL) || (apci.frame_type == IEC104_FRAME_TYPE_S && IEC104_Link_Handle_S_Frame(&g_link_state, &apci) == IEC104_LINK_RESULT_FATAL)) { IEC104_LOG_WARN("[IEC104] invalid I/S frame sequence; receiver reset"); Rx_Reset_State(); return IEC104_RX_SEQUENCE_ERROR; } /* 仅复位成帧进度,已通过检查的链路序号必须保留给下一帧。 */ IEC104_LOG_DEBUG("[IEC104] RX frame ready: type=%d, len=%u", (int)apci.frame_type, (unsigned)frame_len); Rx_Reset_State(); return IEC104_RX_FRAME_READY; } /** * 功能:把一个字节送入IEC-104接收状态机。 * 输入参数:byte——当前收到的一个字节。 * 输出参数:无。 * 返回值:返回IEC104_RxResult_t,用于区分未收齐、完整帧和各类错误。 */ IEC104_RxResult_t IEC104_Rx_Feed_Byte(uint8_t byte) { switch (g_rx_parser.stage) { case RX_SYNC: /* 同步阶段:丢弃所有非0x68字节,直到找到APDU起始字节。 */ if (byte == IEC104_START_BYTE) { g_rx_parser.buf[0] = byte; g_rx_parser.len = 1; g_rx_parser.stage = RX_LEN; } return IEC104_RX_IN_PROGRESS; case RX_LEN: /* RX_LEN 必须由 RX_SYNC 收到 0x68 后进入。 */ if (g_rx_parser.len != 1U || g_rx_parser.buf[0] != IEC104_START_BYTE) { IEC104_LOG_ERR("[IEC104] inconsistent RX_LEN state; receiver reset"); Rx_Reset_State(); return IEC104_RX_INTERNAL_ERROR; } g_rx_parser.buf[1] = byte; g_rx_parser.len = 2; /* 长度域包含 4 字节控制域和 ASDU,协议允许范围为 4..253。 */ if (byte < IEC104_MIN_LENGTH_FIELD || byte > IEC104_MAX_LENGTH_FIELD) { IEC104_LOG_WARN("[IEC104] invalid length %u; valid range is %u..%u; receiver reset", (unsigned)byte, (unsigned)IEC104_MIN_LENGTH_FIELD, (unsigned)IEC104_MAX_LENGTH_FIELD); Rx_Reset_State(); return IEC104_RX_FRAME_ERROR; } g_rx_parser.expected_len = (uint16_t)(2 + byte); g_rx_parser.stage = RX_BODY; return IEC104_RX_IN_PROGRESS; case RX_BODY: /* 写缓冲区前检查内部状态,防止状态损坏造成越界写入。 */ if (g_rx_parser.len >= (uint16_t)sizeof(g_rx_parser.buf)) { IEC104_LOG_ERR("[IEC104] invalid RX_BODY bounds: len=%u, expected_len=%u, buffer_capacity=%u; receiver reset", (unsigned)g_rx_parser.len, (unsigned)g_rx_parser.expected_len, (unsigned)sizeof(g_rx_parser.buf)); Rx_Reset_State(); return IEC104_RX_INTERNAL_ERROR; } /* 报文体阶段:按长度域给出的目标长度逐字节收集APDU。 */ g_rx_parser.buf[g_rx_parser.len++] = byte; if (g_rx_parser.len == g_rx_parser.expected_len) return Rx_Finish_Frame(); return IEC104_RX_IN_PROGRESS; default: /* 理论上不会进入此分支;若状态被破坏,则记录错误并自恢复。 */ IEC104_LOG_ERR("[IEC104] invalid parser state; receiver reset"); Rx_Reset_State(); return IEC104_RX_INTERNAL_ERROR; } }