@Override
    protected PlcReadResponse processReadResponses(PlcReadRequest readRequest, Map<PlcReadRequest, SubResponse<PlcReadResponse>> readResponses, DriverContext driverContext) {
        ModbusContext modbusContext = (ModbusContext) driverContext;
        try {
            // Build an index of all the data returned by all requests.
            // This data should contain all the bits needed to create the response of the original request.
            Map<String, List<Response>> responses = new HashMap<>();
            for (PlcReadRequest optimizedReadRequest : readResponses.keySet()) {
                PlcReadResponse optimizedReadResponse = readResponses.get(optimizedReadRequest).getResponse();
                if (optimizedReadResponse == null) {
                    continue;
                }
                // Optimized read requests only contain one ModbusTag.
                String tagName = optimizedReadRequest.getTagNames().stream().findFirst().orElse(null);
                if (tagName == null) {
                    continue;
                }
                ModbusTag modbusTag = (ModbusTag) optimizedReadRequest.getTag(tagName);
                String tagType = modbusTag.getClass().getSimpleName().substring("ModbusTag".length());
                if (!responses.containsKey(tagType)) {
                    responses.put(tagType, new ArrayList<>());
                }
                PlcResponseCode responseCode = optimizedReadResponse.getResponseCode(tagName);
                int startingAddress = modbusTag.getAddress();
                int endingAddressRegister = modbusTag.getAddress() + modbusTag.getNumberOfElements();
                int endingAddressCoil = modbusTag.getAddress() + modbusTag.getNumberOfElements();
                byte[] responseData = (responseCode == PlcResponseCode.OK) ? optimizedReadResponse.getPlcValue(tagName).getRaw() : null;
                responses.get(tagType).add(new Response(responseCode, startingAddress,
                    endingAddressRegister, endingAddressCoil, responseData));
            }

            // Now go through the original requests and try to answer them by using the raw data we now have.
            Map<String, PlcResponseItem<PlcValue>> values = new HashMap<>();
            for (String tagName : readRequest.getTagNames()) {
                ModbusTag modbusTag = (ModbusTag) readRequest.getTag(tagName);
                String tagType = modbusTag.getClass().getSimpleName().substring("ModbusTag".length());
                if (!responses.containsKey(tagType)) {
                    values.put(tagName, new DefaultPlcResponseItem<>(PlcResponseCode.NOT_FOUND, null));
                    continue;
                }
                // Go through all responses till we find one where that contains the current tag's data.
                for (Response response : responses.get(tagType)) {
                    if((modbusTag instanceof ModbusTagCoil) || (modbusTag instanceof ModbusTagDiscreteInput)) {
                        if(response.matchesCoil(modbusTag)) {
                            // If this response was invalid, return all associated addresses as equally invalid.
                            // TODO: Possibly it would be worth doing a single item request for each of these
                            //  tags in order to find out which ones are actually invalid as if one item in the
                            //  current request exceeds the address range, all items in this chunk will fail, even
                            //  if only one element was invalid.
                            if(response.getResponseCode() != PlcResponseCode.OK) {
                                values.put(tagName, new DefaultPlcResponseItem<>(response.getResponseCode(), null));
                                break;
                            }

                            // Coils and Discrete Inputs are read completely different from registers.

                            // Calculate the byte that contains the response for this Coil
                            byte[] responseData = response.getResponseData();
                            int bitPosition = modbusTag.getAddress() - response.startingAddress;
                            int bytePosition = bitPosition / 8;
                            int bitPositionInByte = bitPosition % 8;
                            boolean isBitSet = (responseData[bytePosition] & (1 << bitPositionInByte)) != 0;
                            values.put(tagName, new DefaultPlcResponseItem<>(PlcResponseCode.OK, new PlcBOOL(isBitSet)));
                            break;
                        }
                    }
                    // Read a normal register.
                    else if (response.matchesRegister(modbusTag)) {
                        // If this response was invalid, return all associated addresses as equally invalid.
                        // TODO: Possibly it would be worth doing a single item request for each of these
                        //  tags in order to find out which ones are actually invalid as if one item in the
                        //  current request exceeds the address range, all items in this chunk will fail, even
                        //  if only one element was invalid.
                        if(response.getResponseCode() != PlcResponseCode.OK) {
                            values.put(tagName, new DefaultPlcResponseItem<>(response.getResponseCode(), null));
                            break;
                        }

                        var byteOrder = modbusTag.getByteOrder() != null ? modbusTag.getByteOrder() : modbusContext.getByteOrder();

                        byte[] responseData = response.getResponseDataForTag(modbusTag);
                        ReadBuffer readBuffer = getReadBuffer(responseData, byteOrder);
                        try {
                            PlcValue plcValue = DataItem.staticParse(readBuffer, modbusTag.getDataType(),
                                modbusTag.getNumberOfElements(),
                                byteOrder == ModbusByteOrder.BIG_ENDIAN);
                            values.put(tagName, new DefaultPlcResponseItem<>(PlcResponseCode.OK, plcValue));
                        } catch (ParseException e) {
                            values.put(tagName, new DefaultPlcResponseItem<>(PlcResponseCode.INTERNAL_ERROR, null));
                        }
                        break;
                    }
                }
                // If no response was found that contains the data, that's probably something we need to fix.
                if (!values.containsKey(tagName)) {
                    values.put(tagName, new DefaultPlcResponseItem<>(PlcResponseCode.INTERNAL_ERROR, null));
                }
            }

            return new DefaultPlcReadResponse(readRequest, values);
        }
        // TODO: Remove this part once the driver is more stable.
        catch (Exception e) {
            try {
                logger.error("Error processing response:", e);
                WriteBufferXmlBased wb = new WriteBufferXmlBased();
                ((DefaultPlcReadRequest) readRequest).serialize(wb);
                logger.error("Original Request:\n{}", wb.getXmlString(), e);
                for (PlcReadRequest subRequest : readResponses.keySet()) {
                    SubResponse<PlcReadResponse> subResponse = readResponses.get(subRequest);
                    WriteBufferXmlBased wbSubRequest = new WriteBufferXmlBased();
                    ((DefaultPlcReadRequest) subRequest).serialize(wbSubRequest);
                    logger.error("Sub Request:\n{}", wbSubRequest.getXmlString());
                    if(subResponse.isSuccess()) {
                        PlcReadResponse plcReadResponse = subResponse.getResponse();
                        WriteBufferXmlBased wbSubResponse = new WriteBufferXmlBased();
                        ((DefaultPlcReadResponse) plcReadResponse).serialize(wbSubResponse);
                        logger.error("Sub Response (Success):\n{}", wbSubResponse.getXmlString());
                    } else {
                        Throwable throwable = subResponse.getThrowable();
                        logger.error("Sub Response (Error):", throwable);
                    }
                }
            } catch (SerializationException ex) {
                throw new RuntimeException(ex);
            }
            return null;
        }
    }
When reading large chunks of data, here we need to read the parts that were originally requested.

@param readRequest original read request
@param readResponses map of the sub-requests that were executed
@return post processed response reflecting the unmodified user request items
plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/optimizer/ModbusOptimizer.java