首页 - 关于研博 - 技术笔记 - IEC104协议解析
IEC104协议解析
2025.03.07

一、IEC104协议核心特性与应用场景

 IEC104(IEC 60870-5-104)是电力系统中广泛使用的通信协议,基于TCP/IP实现主从站(SCADA与RTU/变电站设备)的实时数据交互‌。其核心功能包括:

1.四遥操作‌:

  • 遥测‌(YC):采集电压、电流等模拟量数据(如类型标识0x0D)‌。

  • 遥信‌(YX):监测开关状态等数字量信号(如M_SP_NA_1单点遥信)‌。

  • 遥控‌(YK):远程控制断路器分合闸(如C_SC_NA_1指令)‌。

  • 遥调‌(YT):参数调节(如变压器分接头调整)‌。

2.‌协议分层‌:

  • APCI‌(应用协议控制信息):包含启动字符0x68、长度域及控制域(I/S/U帧标识)‌。

  • ASDU‌(应用服务数据单元):承载业务数据(如公共地址、传送原因、信息体列表)‌。 

二、 编解码实现 

(一)解码器

public class IEC104FrameDecoder extends LengthFieldBasedFrameDecoder {
    private static final int MAX_FRAME_LENGTH = 253;
    private static final int LENGTH_FIELD_OFFSET = 1;
    private static final int LENGTH_FIELD_LENGTH = 1;

    public IEC104FrameDecoder() {
        super(MAX_FRAME_LENGTH, LENGTH_FIELD_OFFSET, LENGTH_FIELD_LENGTH, 0, 0); // ‌:ml-citation{ref="1,2" data="citationList"}
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        // 校验启动字符
        if (in.getByte(in.readerIndex()) != 0x68) {
            ctx.close();
            throw new ProtocolException("Invalid start byte 0x" + 
                                        Integer.toHexString(in.getByte(in.readerIndex()))); // ‌:ml-citation{ref="2,5" data="citationList"}
        }

        // 动态分割报文
        ByteBuf frame = (ByteBuf) super.decode(ctx, in);
        if (frame == null) return null;

        // 解析APCI
        frame.readByte(); // 跳过0x68
        int apduLength = frame.readUnsignedByte();
        if (frame.readableBytes() < apduLength) {
            return null; // 等待完整报文‌:ml-citation{ref="2,5" data="citationList"}
        }

        ByteBuf apduContent = frame.readSlice(apduLength);
        return parseAPDU(apduContent);
    }

    private APDU parseAPDU(ByteBuf buf) {
        // 解析控制域(4字节)
        int ctrl1 = buf.readUnsignedByte();
        int ctrl2 = buf.readUnsignedByte();
        int ctrl3 = buf.readUnsignedByte();
        int ctrl4 = buf.readUnsignedByte();

        ControlFieldType type;
        int sendSeq = 0, recvSeq = 0;
        UFrameCommand uCommand = null;

        // I帧(低比特位为0)
        if ((ctrl1 & 0x01) == 0) { // ‌:ml-citation{ref="2,4" data="citationList"}
            type = ControlFieldType.I_FRAME;
            sendSeq = ((ctrl1 & 0xFE) >> 1) | (ctrl2 << 7);
            recvSeq = ((ctrl3 & 0xFE) >> 1) | (ctrl4 << 7);
        } 
            // S帧(低2比特为01)
        else if ((ctrl1 & 0x03) == 0x01) { // ‌:ml-citation{ref="2,4" data="citationList"}
            type = ControlFieldType.S_FRAME;
            recvSeq = ((ctrl3 & 0xFE) >> 1) | (ctrl4 << 7);
        } 
            // U帧(低2比特为11)
        else { // ‌:ml-citation{ref="2,4" data="citationList"}
            type = ControlFieldType.U_FRAME;
            uCommand = UFrameCommand.fromBytes(ctrl1, ctrl2);
        }

        // 解析ASDU(仅I帧携带数据)
        ASDU asdu = null;
        if (type == ControlFieldType.I_FRAME && buf.readableBytes() >= 6) {
            asdu = new ASDU();
            asdu.setTypeId(buf.readUnsignedByte()); // 类型标识‌:ml-citation{ref="2,3" data="citationList"}
            asdu.setVariableStruct(buf.readUnsignedByte()); // 可变结构限定词‌:ml-citation{ref="2,3" data="citationList"}
            asdu.setCause(buf.readUnsignedShortLE()); // 传送原因(小端)‌:ml-citation{ref="2,5" data="citationList"}
            asdu.setCommonAddress(buf.readUnsignedShortLE()); // 公共地址‌:ml-citation{ref="2,5" data="citationList"}

            // 解析信息体(示例:遥测数据)
            int objCount = asdu.getVariableStruct() & 0x7F; // SQ=0时取低7位‌:ml-citation{ref="2,3" data="citationList"}
            for (int i = 0; i < objCount; i++) {
                InfoObject obj = new InfoObject();
                obj.setAddress(buf.readUnsignedMediumLE()); // 3字节地址小端‌:ml-citation{ref="2,5" data="citationList"}
                if (asdu.getTypeId() == 0x0D) { // 浮点型遥测
                    obj.setValue(buf.readFloatLE()); // ‌:ml-citation{ref="2,3" data="citationList"}
                }
                asdu.addInfoObject(obj);
            }
        }

        return new APDU(type, sendSeq, recvSeq, uCommand, asdu);
    }
}

(二) 编码器

public class IEC104Encoder extends MessageToByteEncoder {
    private final Recycler bufferRecycler = new Recycler<>() {
        @Override
        protected ByteBuf newObject(Handle handle) {
            return Unpooled.buffer(512);
        }
    };

    @Override
    protected void encode(ChannelHandlerContext ctx, APDU msg, ByteBuf out) {
        ByteBuf apciBuf = bufferRecycler.get();
        try {
            // 构建控制域
            switch (msg.getType()) { // ‌:ml-citation{ref="2,4" data="citationList"}
                case I_FRAME:
                    apciBuf.writeByte((msg.getSendSeq() << 1) & 0xFF);
                    apciBuf.writeByte((msg.getSendSeq() >> 7) & 0xFF);
                    apciBuf.writeByte((msg.getRecvSeq() << 1) & 0xFF);
                    apciBuf.writeByte((msg.getRecvSeq() >> 7) & 0xFF);
                    break;
                case S_FRAME:
                    apciBuf.writeByte(0x01);
                    apciBuf.writeByte(0x00);
                    apciBuf.writeByte((msg.getRecvSeq() << 1) & 0xFF);
                    apciBuf.writeByte((msg.getRecvSeq() >> 7) & 0xFF);
                    break;
                case U_FRAME:
                    apciBuf.writeByte(msg.getUCommand().getCtrlByte1());
                    apciBuf.writeByte(msg.getUCommand().getCtrlByte2());
                    apciBuf.writeZero(2); // 填充0x00
                    break;
            }

            // 构建ASDU(I帧)
            if (msg.getType() == ControlFieldType.I_FRAME && msg.getAsdu() != null) { 
                ASDU asdu = msg.getAsdu();
                apciBuf.writeByte(asdu.getTypeId()); // ‌:ml-citation{ref="2,3" data="citationList"}
                apciBuf.writeByte(asdu.getVariableStruct()); 
                apciBuf.writeShortLE(asdu.getCause()); // 小端写入‌:ml-citation{ref="2,5" data="citationList"}
                apciBuf.writeShortLE(asdu.getCommonAddress());

                // 信息体编码(示例:单点遥信)
                for (InfoObject obj : asdu.getInfoObjects()) {
                    apciBuf.writeMediumLE(obj.getAddress()); // 3字节小端‌:ml-citation{ref="2,5" data="citationList"}
                    if (asdu.getTypeId() == 0x01) { // 单点状态
                        apciBuf.writeByte(obj.getStatus() & 0x01); // ‌:ml-citation{ref="2,3" data="citationList"}
                    }
                }
            }

            // 构建完整APDU
            out.writeByte(0x68); // 启动字符‌:ml-citation{ref="2,5" data="citationList"}
            out.writeByte(apciBuf.readableBytes()); // 长度域‌:ml-citation{ref="2,5" data="citationList"}
            out.writeBytes(apciBuf);
        } finally {
            bufferRecycler.recycle(apciBuf);
        }
    }
}

(三) 实体类

// APDU实体类(对象池实现)
public class APDU implements Recyclable {
    private static final Recycler recycler = new Recycler<>() { /*...*/ };

    // 控制域字段
    private ControlFieldType type;
    private int sendSeq;
    private int recvSeq;
    private UFrameCommand uCommand;

    // ASDU字段
    private ASDU asdu;

}

// ASDU数据结构
public class ASDU {
    private int typeId;          // 类型标识‌:ml-citation{ref="2,3" data="citationList"}
    private int variableStruct;  // 可变结构限定词‌:ml-citation{ref="2,3" data="citationList"}
    private int cause;           // 传送原因(低字节在前)‌:ml-citation{ref="2,5" data="citationList"}
    private int commonAddress;   // 公共地址‌:ml-citation{ref="2,5" data="citationList"}
    private List infoObjects = new ArrayList<>();
}

// 信息体对象
public class InfoObject {
    private int address;  // 3字节地址‌:ml-citation{ref="2,5" data="citationList"}
    private float value;  // 遥测值
    private byte status;  // 遥信状态(0/1)‌:ml-citation{ref="2,3" data="citationList"}
}

(四) 关键实现说明

‌1.动态沾包处理通过LengthFieldBasedFrameDecoder实现变长报文切割,支持最大253字节APDU报文‌

‌2.控制域编码优化

  • I帧发送序号采用左移1位存储(避免序列号溢出)‌

  • U帧支持STARTDT_ACT/TESTFR_ACT等标准命令‌

3‌.数据格式处理

  • 信息体地址使用3字节‌小端序‌编码(适配电力设备规范)‌

  • 遥测值采用IEEE754浮点型编码,遥信状态用单字节存储‌

三、业务逻辑处理

  • 总召唤流程‌:主站发送C_IC_NA_1指令后,从站按点表顺序返回所有遥测、遥信数据,通过多帧I帧传输(每帧最大249字节)。

  • 变化数据主动上报‌:从站检测到数据变化时,通过ASDU的‌传送原因‌字段(如0x03表示突发上送)触发实时推送‌。

获取相关资料
下载地址将会发送至您填写的邮箱
相关新闻
电动汽车GB/T32960-2016协议介绍开发
2025-02-21
HJ212环境监测数据传输协议
2025-02-14
电能表DLT645协议解析
2025-02-07
  • 在线客服
  • 电话咨询
  • 微信
  • 短视频