@Override
    protected List<PlcReadRequest> processReadRequest(PlcReadRequest readRequest, DriverContext driverContext) {
        S7DriverContext s7DriverContext = (S7DriverContext) driverContext;
        if(!s7DriverContext.isEnableBlockReadOptimizer()) {
            return super.processReadRequest(readRequest, s7DriverContext);
        }

        // Sort the tags by area
        // (We can only read multiple tags in one byte array, if they are located in the same area)
        Map<String, Map<PlcTag, String>> sortedTagsPerArea = new HashMap<>();
        for (String tagName : readRequest.getTagNames()) {
            PlcTag tag = readRequest.getTag(tagName);
            if(tag instanceof S7SzlTag) {
                if(!sortedTagsPerArea.containsKey("SZL")) {
                    sortedTagsPerArea.put("SZL", createSortedTagMap());
                }
                sortedTagsPerArea.get("SZL").put(tag, tagName);
            } else if(tag instanceof S7ClkTag) {
                if(!sortedTagsPerArea.containsKey("CLK")) {
                    sortedTagsPerArea.put("CLK", createSortedTagMap());
                }
                sortedTagsPerArea.get("CLK").put(tag, tagName);
            } else if(tag instanceof S7Tag) {
                S7Tag s7Tag = (S7Tag) tag;
                MemoryArea memoryArea = s7Tag.getMemoryArea();
                // When reading DATA_BLOCKS we need to also use the block number.
                String areaName = memoryArea.getShortName();
                if(memoryArea ==  MemoryArea.DATA_BLOCKS) {
                    areaName += s7Tag.getBlockNumber();
                } else if(memoryArea == MemoryArea.INSTANCE_DATA_BLOCKS) {
                    areaName += s7Tag.getBlockNumber();
                }
                if(!sortedTagsPerArea.containsKey(areaName)) {
                    sortedTagsPerArea.put(areaName, createSortedTagMap());
                }
                sortedTagsPerArea.get(areaName).put(tag, tagName);
            } else {
                System.out.println("Ignored");
            }
        }

        // Build groups of byte-arrays to fetch
        // Strategies:
        // - If two consecutive tags are more than S7_ADDRESS_ANY_SIZE bytes apart,
        //   reading them in one block is less efficient than reading them separately.
        //   Other than that, extend the byte array to include the next tag.
        Map<TagNameSize, PlcTagItem<PlcTag>> optimizedTagMap = new TreeMap<>();
        for (Map<PlcTag, String> tagList : sortedTagsPerArea.values()) {
            MemoryArea currentMemoryArea = null;
            int currentDataBlockNumber = -1;
            int currentChunkStartByteOffset = -1;
            int currentChunkEndByteOffset = -1;
            Map<PlcTag, String> currentChunkTags = createSortedTagMap();
            for (PlcTag plcTag : tagList.keySet()) {
                // We don't do anything for Szl and Clk tags.
                if(plcTag instanceof S7SzlTag) {
                    // TODO: Implement the size
                    optimizedTagMap.put(new TagNameSize(tagList.get(plcTag), 0), new DefaultPlcTagItem<>(plcTag));
                } else if (plcTag instanceof S7ClkTag) {
                    // TODO: Implement the size
                    optimizedTagMap.put(new TagNameSize(tagList.get(plcTag), 0), new DefaultPlcTagItem<>(plcTag));
                }
                // Var-length strings, are a performance nightmare. Trying to optimize reading them is probably not
                // worth the effort. For now, we simply handle them as un-chunked tags.
                else if(plcTag instanceof S7StringVarLengthTag) {
                    // A var-length string tag simply reads 2 or 4 bytes.
                    optimizedTagMap.put(new TagNameSize(tagList.get(plcTag),
                        ((S7StringVarLengthTag) plcTag).getDataType() == TransportSize.STRING ? 2 : 4),
                        new DefaultPlcTagItem<>(plcTag));
                }

                // Only regular tags are optimized.
                else if (plcTag instanceof S7Tag) {
                    S7Tag s7Tag = (S7Tag) plcTag;

                    int curTagSize = s7Tag.getDataType().getSizeInBytes() * s7Tag.getNumberOfElements();
                    // In case of fixed length strings, a string starts with two bytes: max length,
                    // actual length and then the string bytes after that.
                    if(s7Tag instanceof S7StringFixedLengthTag) {
                        S7StringFixedLengthTag stringFixedLengthTag = (S7StringFixedLengthTag) s7Tag;
                        int bytesPerChar = (stringFixedLengthTag.getDataType() == TransportSize.WSTRING) ? 2 : 1;
                        curTagSize = ((2 * bytesPerChar) + (stringFixedLengthTag.getStringLength() * bytesPerChar)) * s7Tag.getNumberOfElements();
                    }

                    // If this is the first tag, use that as starting point.
                    if(currentMemoryArea == null) {
                        currentMemoryArea = s7Tag.getMemoryArea();
                        currentDataBlockNumber = s7Tag.getBlockNumber();
                        currentChunkStartByteOffset = s7Tag.getByteOffset();
                        currentChunkEndByteOffset = s7Tag.getByteOffset() + curTagSize;
                    }
                    // If the next tag would be more bytes away than a s7 address item requires, it's cheaper to
                    // split up into multiple items.
                    else if(currentChunkEndByteOffset + S7_ADDRESS_ANY_SIZE < s7Tag.getByteOffset()) {
                        // Save the current chunk.
                        optimizedTagMap.put(new TagNameSize("__chunk__" + optimizedTagMap.size(), currentChunkEndByteOffset - currentChunkStartByteOffset),
                            new DefaultPlcTagItem<>(
                                new S7TagChunk(TransportSize.BYTE, currentMemoryArea, currentDataBlockNumber,
                                    currentChunkStartByteOffset, (byte) 0,
                                    currentChunkEndByteOffset - currentChunkStartByteOffset,
                                    currentChunkTags, 0, 1, currentChunkEndByteOffset - currentChunkStartByteOffset)));

                        // Start a new one.
                        currentChunkStartByteOffset = s7Tag.getByteOffset();
                        currentChunkEndByteOffset = s7Tag.getByteOffset() + curTagSize;
                        currentChunkTags = createSortedTagMap();
                    }
                    // Otherwise extend the array size to include this tag.
                    else {
                        currentChunkEndByteOffset = s7Tag.getByteOffset() + curTagSize;
                    }

                    // Add the tag to the list of tags for the current chunk.
                    currentChunkTags.put(s7Tag, tagList.get(s7Tag));
                }
            }

            // Finish the last chunk.
            optimizedTagMap.put(new TagNameSize("__chunk__" + optimizedTagMap.size(), currentChunkEndByteOffset - currentChunkStartByteOffset),
                new DefaultPlcTagItem<>(
                    new S7TagChunk(TransportSize.BYTE, currentMemoryArea, currentDataBlockNumber,
                        currentChunkStartByteOffset, (byte) 0,
                        currentChunkEndByteOffset - currentChunkStartByteOffset,
                        currentChunkTags, 0, 1, currentChunkEndByteOffset - currentChunkStartByteOffset)));
        }

        // Go through all chunks. If there are ones larger than the max PDU size, split them up into
        // multiple tags, that utilize the packets to the maximum.
        final int maxRequestSize = ((S7DriverContext) driverContext).getPduSize() - (EMPTY_READ_RESPONSE_SIZE + 4);
        Map<TagNameSize, PlcTagItem<PlcTag>> optimizedTagMap2 = new TreeMap<>();
        for (TagNameSize tagNameSize : optimizedTagMap.keySet()) {
            PlcTagItem<PlcTag> curTagItem = optimizedTagMap.get(tagNameSize);
            S7Tag curTag = (S7Tag) curTagItem.getTag();
            if(tagNameSize.getTagSize() > maxRequestSize) {
                String curTagNameBase = tagNameSize.getTagName();
                int curTagFragmentNumber = 0;
                int curTagSize = tagNameSize.getTagSize();
                int curTagOffset = curTag.getByteOffset();
                int curPartIndex = 0;
                int totalPartCount = (curTagSize / maxRequestSize) + 1;
                while(curTagSize > maxRequestSize) {
                    optimizedTagMap2.put(new TagNameSize(curTagNameBase + "." + curTagFragmentNumber, maxRequestSize),
                        new DefaultPlcTagItem<>(
                            new S7TagChunk(curTag.getDataType(), curTag.getMemoryArea(), curTag.getBlockNumber(), curTagOffset, (byte) 0, maxRequestSize,
                                (curTag instanceof S7TagChunk) ? ((S7TagChunk) curTag).getChunkTags() : Collections.singletonMap(curTag, tagNameSize.getTagName()),
                                curPartIndex, totalPartCount, curTagSize)));

                    curTagOffset += maxRequestSize;
                    curTagSize -= maxRequestSize;
                    curTagFragmentNumber++;
                    curPartIndex++;
                }
                optimizedTagMap2.put(new TagNameSize(curTagNameBase + "." + curTagFragmentNumber, curTagSize),
                    new DefaultPlcTagItem<>(
                        new S7TagChunk(curTag.getDataType(), curTag.getMemoryArea(), curTag.getBlockNumber(), curTagOffset, (byte) 0, curTagSize,
                            (curTag instanceof S7TagChunk) ? ((S7TagChunk) curTag).getChunkTags() : Collections.singletonMap(curTag, tagNameSize.getTagName()),
                            curPartIndex, totalPartCount, curTagSize)));
            }
            // Just copy over tags that fit into a request.
            else {
                optimizedTagMap2.put(tagNameSize, optimizedTagMap.get(tagNameSize));
            }
        }

        // Using the First Fit Decreasing (FFD) bin-packing algorithm try to find the ideal
        // packing for utilizing request sizes.
        // 1. Assign a size to each tag
        // 2. Sort the tags by size (biggest first)
        // 3. Repeat this, till all tags are consumed
        //      1. Take the first packet of the list
        //      2. If the tag itself exceeds the max request size, keep on splitting it into chunks until
        //         the rest would fit into a request. Then proceed with the rest as if it was a normal tag
        //      2. Go through the existing list of requests and check if the current tag would fit
        //          1. If it fits, add it to the request
        //          2. If it doesn't fit go to the next request and check
        //          3. If you reach the end, and it didn't fit any of the previous requests, add a new one
        LinkedHashMap<String, PlcTagItem<PlcTag>> executableTagMap = new LinkedHashMap<>();
        for (TagNameSize tagNameSize : optimizedTagMap2.keySet()) {
            // TODO: Implement the algorithm above.
            executableTagMap.put(tagNameSize.getTagName(), optimizedTagMap2.get(tagNameSize));
        }

        return super.processReadRequest(
            new DefaultPlcReadRequest(((DefaultPlcReadRequest) readRequest).getReader(), executableTagMap),
            driverContext);
    }
In contrast to the S7Optimizer, the S7BlockReadOptimizer only looks at the bytes that require being read
and focuses on reading raw blocks of bytes instead of doing individual reads for each item.
plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7light/readwrite/optimizer/S7BlockReadOptimizer.java