170 lines
6.2 KiB
C
170 lines
6.2 KiB
C
/**
|
|
* 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;
|
|
}
|
|
}
|