README.en.md 4.6 KB

本工具是本人基于工作所需开发的针对于IEC104通讯的辅助工具

该工具针对U帧和S帧已经做好了收发逻辑,但是I帧的具体处理逻辑需要使用者自己实现

本工具基于netty框架。建议有netty使用经验和对104通讯规约的人使用。如果不了解104通讯规约,理解本工具的工作流程可能会有些麻烦。

使用方式

Matser的创建 以及数据的接收处理:

Matser的创建

SimpleMasterBuilder simpleMasterBuilder=new SimpleMasterBuilder("127.0.0.1",2404);
        simpleMasterBuilder.create();

上面 这种master仅支持一个ip和端口号,与之相对的是HSMasterBuilder 支持主备链接 ,主动切换。

 HSMasterBuilder masterBuilder=new HSMasterBuilder("127.0.0.1",2404).setSpareIp("127.0.0.2");
        masterBuilder.create();

create() 方法会阻塞线程,如果不希望阻塞线程可以使用createByUnBlock(),以工具内的单线程池执行。

数据的接收

首先向slave端发送总召唤

        //创建总召唤类型I帧
        TotalSummonType totalSummonType=new TotalSummonType();
        //反向生成asdu
        Asdu asdu = totalSummonType.generateBack();
        //配置总召唤发送原因
        asdu.setNot(6);
        //配置公共地址位
        asdu.setCommonAddress(1);
        Apdu apdu=new Apdu().setAsdu(asdu);
        masterBuilder.sendFrame(apdu);

实现对应数据类型的数据接收后的处理,以数据类型为13的浮点数为例

@Slf4j
@AsduType(typeId=13)
public class HandleShortFloat extends ShortFloatType {


    /**
     * 处理短浮点数据
     *
     * @param apdu
     * @return
     */
    @Override
    public byte[][] handleAndAnswer(Apdu apdu) {
        log.info("----------处理短浮点数据---------");
        Map<Integer, Float> map = new HashMap<>();
        HandleShortFloat handleShortFloat = (HandleShortFloat) apdu.getAsdu().getDataFrame();
        List<InformationBodyAddress> address = handleShortFloat.getAddresses();
        Map<IeMeasuredQuality, Float> datas = handleShortFloat.getDatas();
        int i = 0;
        //存入共享服务端
        if (apdu.getAsdu().getVsq().getSq() == 0) {
            log.warn("------处理短浮点单一寻址-----");
            for (Map.Entry<IeMeasuredQuality, Float> e : datas.entrySet()) {
                map.put(address.get(i++).getAddress(), e.getValue());
            }
        } else if (apdu.getAsdu().getVsq().getSq() == 1) {
            log.warn("------处理短浮点连续寻址-----");
            i = address.get(0).getAddress();
            for (Map.Entry<IeMeasuredQuality, Float> e : datas.entrySet()) {
                map.put(i++, e.getValue());
            }
        }
        //将map 储起来
       //TODO
        
        //如果有需要返回数据帧可以创建byte数据  向内置入要返回的数据帧encode()后的数组
        return null;
    }
}

Slave端的创建及数据的发送

Slave端的创建

SlaverBuilder slaverBuilder=new SlaverBuilder();
slaverBuilder.create();

数据响应 响应总召唤

@AsduType(typeId = 100)
@Slf4j
public class HandleTotalSummonType extends TotalSummonType {


	@Override
	public byte[][] handleAndAnswer(Apdu apdu) throws Exception {
		TotalSummonType dataFrameType = (TotalSummonType) (apdu.getAsdu().getDataFrame());
		Channel channel = apdu.getChannel();
		int commonAddress = apdu.getAsdu().getCommonAddress();
		if (dataFrameType.getValue() == 20 && apdu.getAsdu().getCot().getNot() == 6) {
			//1.回应激活确认总召唤
			SendDataFrameHelper.sendTotalSummonFrame(channel, commonAddress, 7);
			//2.回应连续寻址短浮点i帧
			SendDataFrameHelper.sendYcDataFrame(channel, //TODO 传入<Integer,Number>类型的数据map
                                                , commonAddress, 20);
			//3.回应激活终止总召唤总召唤
			SendDataFrameHelper.sendTotalSummonFrame(channel, commonAddress, 8);
		}
		return null;
	}


突发上送数据 向所有接入的master突发上送map中的数据

for (Channel channel : slaverBuilder.getChannels()) {
				try {
					SendDataFrameHelper.sendYcDataFrameDiscontinuity(channel, //TODO 传入<Integer,Number>类型的数据map
                                                                     , 1, 3);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
更加深入的应用请研究代码 apdu asdu 和各个类型的报文帧 都支持重写,包括拆包工具也支持重写,如果有疑问可以向 weiyigulu524710549@gmail.com 邮箱留言