@@ -0,0 +1,3172 @@
+package com.digitalpetri.enip.logix.services;%0A%0Aimport java.nio.ByteOrder;%0Aimport java.util.Collections;%0Aimport java.util.List;%0Aimport java.util.function.Consumer;%0A%0Aimport com.digitalpetri.enip.cip.CipResponseException;%0Aimport com.digitalpetri.enip.cip.epath.EPath.PaddedEPath;%0Aimport com.digitalpetri.enip.cip.services.CipService;%0Aimport com.digitalpetri.enip.cip.structs.MessageRouterRequest;%0Aimport com.digitalpetri.enip.cip.structs.MessageRouterResponse;%0Aimport com.google.common.collect.Lists;%0Aimport io.netty.buffer.ByteBuf;%0Aimport io.netty.buffer.Unpooled;%0Aimport io.netty.util.ReferenceCountUtil;%0A%0Apublic class ReadTagFragmentedService implements CipService%3CByteBuf%3E %7B%0A%0A    public static final int SERVICE_CODE = 0x52;%0A%0A    private final Consumer%3CByteBuf%3E dataEncoder = this::encode;%0A%0A    private final List%3CByteBuf%3E buffers = Collections.synchronizedList(Lists.newArrayList());%0A    private volatile int offset = 0;%0A%0A    private final PaddedEPath requestPath;%0A    private final int elementCount;%0A%0A    public ReadTagFragmentedService(PaddedEPath requestPath) %7B%0A        this(requestPath, 1);%0A    %7D%0A%0A    public ReadTagFragmentedService(PaddedEPath requestPath, int elementCount) %7B%0A        this.requestPath = requestPath;%0A        this.elementCount = elementCount;%0A    %7D%0A%0A    @Override%0A    public void encodeRequest(ByteBuf buffer) %7B%0A        MessageRouterRequest request = new MessageRouterRequest(%0A                SERVICE_CODE,%0A                requestPath,%0A                dataEncoder%0A        );%0A%0A        MessageRouterRequest.encode(request, buffer);%0A    %7D%0A%0A    private void encode(ByteBuf buffer) %7B%0A        buffer.writeShort(elementCount);%0A        buffer.writeInt(offset);%0A    %7D%0A%0A    @Override%0A    public ByteBuf decodeResponse(ByteBuf buffer) throws CipResponseException, PartialResponseException %7B%0A        MessageRouterResponse response = MessageRouterResponse.decode(buffer);%0A%0A        int status = response.getGeneralStatus();%0A%0A        if (status == 0x00 %7C%7C status == 0x06) %7B%0A            ByteBuf data = response.getData();%0A%0A            boolean structured = data.getShort(data.readerIndex()) == 0x02A0;%0A            ByteBuf header = structured ? data.readSlice(4) : data.readSlice(2);%0A            ByteBuf fragment = data.slice().retain();%0A%0A            buffers.add(fragment);%0A            offset += fragment.readableBytes();%0A            ReferenceCountUtil.safeRelease(data);%0A%0A            if (status == 0x00) %7B%0A                synchronized (buffers) %7B%0A                    ByteBuf composite = Unpooled.compositeBuffer()%0A                            .addComponent(header.retain())%0A                            .addComponents(buffers)%0A                            .writerIndex(header.readableBytes() + offset)%0A                            .order(ByteOrder.LITTLE_ENDIAN);%0A%0A                    // Clean up so this service can be re-used...%0A                    buffers.clear();%0A                    offset = 0;%0A%0A                    return composite;%0A                %7D%0A            %7D else %7B%0A                throw PartialResponseException.INSTANCE;%0A            %7D%0A        %7D else %7B%0A            throw new CipResponseException(status, response.getAdditionalStatus());%0A        %7D%0A    %7D%0A%0A%7D%0A

Implement ReadTagFragmented service