You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cd...@apache.org on 2022/08/31 14:40:47 UTC

[plc4x] 03/03: fix(plc4j/ads): Refactored the ADS driver to support reading of complex types.

This is an automated email from the ASF dual-hosted git repository.

cdutz pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 9f3cc6d0d4f5686e51db1780bf2df12b0ce351c5
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Wed Aug 31 16:40:22 2022 +0200

    fix(plc4j/ads): Refactored the ADS driver to support reading of complex types.
---
 .../org/apache/plc4x/java/ads/field/AdsField.java  |   3 -
 .../plc4x/java/ads/field/AdsFieldHandler.java      |   2 -
 .../plc4x/java/ads/field/DirectAdsField.java       |  25 +-
 .../plc4x/java/ads/field/DirectAdsStringField.java |  16 +-
 .../plc4x/java/ads/field/SymbolicAdsField.java     |  38 +--
 .../java/ads/field/SymbolicAdsStringField.java     |  95 --------
 .../java/ads/model/AdsSubscriptionHandle.java      |   8 +-
 .../plc4x/java/ads/protocol/AdsProtocolLogic.java  | 261 ++++++++++++++++-----
 .../java/ads/readwrite/utils/StaticHelper.java     |   4 +
 9 files changed, 223 insertions(+), 229 deletions(-)

diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/AdsField.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/AdsField.java
index 8d5b1b274..f403d3983 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/AdsField.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/AdsField.java
@@ -18,12 +18,9 @@
  */
 package org.apache.plc4x.java.ads.field;
 
-import org.apache.plc4x.java.ads.readwrite.AdsDataType;
 import org.apache.plc4x.java.api.model.PlcField;
 import org.apache.plc4x.java.spi.utils.Serializable;
 
 public interface AdsField extends PlcField, Serializable {
 
-    AdsDataType getAdsDataType();
-
 }
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/AdsFieldHandler.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/AdsFieldHandler.java
index c94c1c3e8..447755620 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/AdsFieldHandler.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/AdsFieldHandler.java
@@ -30,8 +30,6 @@ public class AdsFieldHandler implements PlcFieldHandler {
             return DirectAdsStringField.of(fieldQuery);
         } else if (DirectAdsField.matches(fieldQuery)) {
             return DirectAdsField.of(fieldQuery);
-        } else if (SymbolicAdsStringField.matches(fieldQuery)) {
-            return SymbolicAdsStringField.of(fieldQuery);
         } else if (SymbolicAdsField.matches(fieldQuery)) {
             return SymbolicAdsField.of(fieldQuery);
         }
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/DirectAdsField.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/DirectAdsField.java
index 7abef7f87..cb6f79793 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/DirectAdsField.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/DirectAdsField.java
@@ -18,9 +18,7 @@
  */
 package org.apache.plc4x.java.ads.field;
 
-import org.apache.plc4x.java.ads.readwrite.AdsDataType;
 import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
-import org.apache.plc4x.java.spi.generation.ParseException;
 import org.apache.plc4x.java.spi.generation.SerializationException;
 import org.apache.plc4x.java.spi.generation.WriteBuffer;
 
@@ -41,24 +39,24 @@ public class DirectAdsField implements AdsField {
 
     private final long indexOffset;
 
-    private final AdsDataType adsDataType;
+    private final String adsDataTypeName;
 
     private final int numberOfElements;
 
-    public DirectAdsField(long indexGroup, long indexOffset, AdsDataType adsDataType, Integer numberOfElements) {
+    public DirectAdsField(long indexGroup, long indexOffset, String adsDataTypeName, Integer numberOfElements) {
         //ByteValue.checkUnsignedBounds(indexGroup, 4);
         this.indexGroup = indexGroup;
         //ByteValue.checkUnsignedBounds(indexOffset, 4);
         this.indexOffset = indexOffset;
-        this.adsDataType = Objects.requireNonNull(adsDataType);
+        this.adsDataTypeName = Objects.requireNonNull(adsDataTypeName);
         this.numberOfElements = numberOfElements != null ? numberOfElements : 1;
         if (this.numberOfElements <= 0) {
             throw new IllegalArgumentException("numberOfElements must be greater then zero. Was " + this.numberOfElements);
         }
     }
 
-    public static DirectAdsField of(long indexGroup, long indexOffset, AdsDataType adsDataType, Integer numberOfElements) {
-        return new DirectAdsField(indexGroup, indexOffset, adsDataType, numberOfElements);
+    public static DirectAdsField of(long indexGroup, long indexOffset, String adsDataTypeName, Integer numberOfElements) {
+        return new DirectAdsField(indexGroup, indexOffset, adsDataTypeName, numberOfElements);
     }
 
     public static DirectAdsField of(String address) {
@@ -88,12 +86,11 @@ public class DirectAdsField implements AdsField {
         }
 
         String adsDataTypeString = matcher.group("adsDataType");
-        AdsDataType adsDataType = AdsDataType.valueOf(adsDataTypeString);
 
         String numberOfElementsString = matcher.group("numberOfElements");
         Integer numberOfElements = numberOfElementsString != null ? Integer.valueOf(numberOfElementsString) : null;
 
-        return new DirectAdsField(indexGroup, indexOffset, adsDataType, numberOfElements);
+        return new DirectAdsField(indexGroup, indexOffset, adsDataTypeString, numberOfElements);
     }
 
     public static boolean matches(String address) {
@@ -108,14 +105,8 @@ public class DirectAdsField implements AdsField {
         return indexOffset;
     }
 
-    @Override
-    public AdsDataType getAdsDataType() {
-        return adsDataType;
-    }
-
-    @Override
-    public String getPlcDataType() {
-        return adsDataType.toString();
+    public String getAdsDataTypeName() {
+        return adsDataTypeName;
     }
 
     @Override
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/DirectAdsStringField.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/DirectAdsStringField.java
index d003c4612..04d4109cc 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/DirectAdsStringField.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/DirectAdsStringField.java
@@ -18,7 +18,6 @@
  */
 package org.apache.plc4x.java.ads.field;
 
-import org.apache.plc4x.java.ads.readwrite.AdsDataType;
 import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
 import org.apache.plc4x.java.spi.generation.SerializationException;
 import org.apache.plc4x.java.spi.generation.WriteBuffer;
@@ -37,13 +36,13 @@ public class DirectAdsStringField extends DirectAdsField implements AdsStringFie
 
     private final int stringLength;
 
-    public DirectAdsStringField(long indexGroup, long indexOffset, AdsDataType adsDataType, int stringLength, Integer numberOfElements) {
-        super(indexGroup, indexOffset, adsDataType, numberOfElements);
+    public DirectAdsStringField(long indexGroup, long indexOffset, String adsDataTypeName, int stringLength, Integer numberOfElements) {
+        super(indexGroup, indexOffset, adsDataTypeName, numberOfElements);
         this.stringLength = stringLength;
     }
 
-    public static DirectAdsStringField of(long indexGroup, long indexOffset, AdsDataType adsDataType, int stringLength, Integer numberOfElements) {
-        return new DirectAdsStringField(indexGroup, indexOffset, adsDataType, stringLength, numberOfElements);
+    public static DirectAdsStringField of(long indexGroup, long indexOffset, String adsDataTypeName, int stringLength, Integer numberOfElements) {
+        return new DirectAdsStringField(indexGroup, indexOffset, adsDataTypeName, stringLength, numberOfElements);
     }
 
     public static DirectAdsStringField of(String address) {
@@ -72,16 +71,15 @@ public class DirectAdsStringField extends DirectAdsField implements AdsStringFie
             indexOffset = Long.parseLong(indexOffsetString);
         }
 
-        String adsDataTypeString = matcher.group("adsDataType");
-        AdsDataType adsDataType = AdsDataType.valueOf(adsDataTypeString);
+        String adsDataTypeName = matcher.group("adsDataType");
 
         String stringLengthString = matcher.group("stringLength");
-        Integer stringLength = stringLengthString != null ? Integer.valueOf(stringLengthString) : null;
+        int stringLength = stringLengthString != null ? Integer.parseInt(stringLengthString) : 0;
 
         String numberOfElementsString = matcher.group("numberOfElements");
         Integer numberOfElements = numberOfElementsString != null ? Integer.valueOf(numberOfElementsString) : null;
 
-        return new DirectAdsStringField(indexGroup, indexOffset, adsDataType, stringLength, numberOfElements);
+        return new DirectAdsStringField(indexGroup, indexOffset, adsDataTypeName, stringLength, numberOfElements);
     }
 
     public static boolean matches(String address) {
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/SymbolicAdsField.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/SymbolicAdsField.java
index 4d13dd92f..140b1fb60 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/SymbolicAdsField.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/SymbolicAdsField.java
@@ -18,7 +18,6 @@
  */
 package org.apache.plc4x.java.ads.field;
 
-import org.apache.plc4x.java.ads.readwrite.AdsDataType;
 import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
 import org.apache.plc4x.java.spi.generation.SerializationException;
 import org.apache.plc4x.java.spi.generation.WriteBuffer;
@@ -33,22 +32,12 @@ import java.util.regex.Pattern;
  */
 public class SymbolicAdsField implements AdsField {
 
-    private static final Pattern SYMBOLIC_ADDRESS_PATTERN = Pattern.compile("^(?<symbolicAddress>.+):(?<adsDataType>\\w+)(\\[(?<numberOfElements>\\d+)])?");
+    private static final Pattern SYMBOLIC_ADDRESS_PATTERN = Pattern.compile("^(?<symbolicAddress>.+)");
 
     private final String symbolicAddress;
 
-    private final AdsDataType adsDataType;
-
-    private final int numberOfElements;
-
-    public SymbolicAdsField(String symbolicAddress, AdsDataType adsDataType, Integer numberOfElements) {
+    public SymbolicAdsField(String symbolicAddress) {
         this.symbolicAddress = Objects.requireNonNull(symbolicAddress);
-        this.adsDataType = Objects.requireNonNull(adsDataType);
-        this.numberOfElements = numberOfElements != null ? numberOfElements : 1;
-        if (this.numberOfElements <= 0) {
-            throw new IllegalArgumentException("numberOfElements must be greater then zero. Was " + this.numberOfElements);
-        }
-
     }
 
     public static SymbolicAdsField of(String address) {
@@ -58,13 +47,7 @@ public class SymbolicAdsField implements AdsField {
         }
         String symbolicAddress = matcher.group("symbolicAddress");
 
-        String adsDataTypeString = matcher.group("adsDataType");
-        AdsDataType adsDataType = AdsDataType.valueOf(adsDataTypeString);
-
-        String numberOfElementsString = matcher.group("numberOfElements");
-        Integer numberOfElements = numberOfElementsString != null ? Integer.valueOf(numberOfElementsString) : null;
-
-        return new SymbolicAdsField(symbolicAddress, adsDataType, numberOfElements);
+        return new SymbolicAdsField(symbolicAddress);
     }
 
     public static boolean matches(String address) {
@@ -75,21 +58,6 @@ public class SymbolicAdsField implements AdsField {
         return symbolicAddress;
     }
 
-    @Override
-    public AdsDataType getAdsDataType() {
-        return adsDataType;
-    }
-
-    @Override
-    public String getPlcDataType() {
-        return adsDataType.toString();
-    }
-
-    @Override
-    public int getNumberOfElements() {
-        return numberOfElements;
-    }
-
     @Override
     public boolean equals(Object o) {
         if (this == o) {
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/SymbolicAdsStringField.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/SymbolicAdsStringField.java
deleted file mode 100644
index ef064afb5..000000000
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/SymbolicAdsStringField.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.plc4x.java.ads.field;
-
-import org.apache.plc4x.java.ads.readwrite.AdsDataType;
-import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
-import org.apache.plc4x.java.spi.generation.SerializationException;
-import org.apache.plc4x.java.spi.generation.WriteBuffer;
-
-import java.nio.charset.StandardCharsets;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * ADS address witch is defined by symbolic name (e.g. {@code Main.items[0]}).
- */
-public class SymbolicAdsStringField extends SymbolicAdsField implements AdsStringField {
-
-    private static final Pattern SYMBOLIC_ADDRESS_STRING_PATTERN = Pattern.compile("^(?<symbolicAddress>.+):(?<adsDataType>'STRING'|'WSTRING')\\((?<stringLength>\\d{1,3})\\)(\\[(?<numberOfElements>\\d+)])?");
-
-    private final int stringLength;
-
-    private SymbolicAdsStringField(String symbolicAddress, AdsDataType adsDataType, int stringLength, Integer numberOfElements) {
-        super(symbolicAddress, adsDataType, numberOfElements);
-        this.stringLength = stringLength;
-    }
-
-    public static SymbolicAdsStringField of(String address) {
-        Matcher matcher = SYMBOLIC_ADDRESS_STRING_PATTERN.matcher(address);
-        if (!matcher.matches()) {
-            throw new PlcInvalidFieldException(address, SYMBOLIC_ADDRESS_STRING_PATTERN, "{address}");
-        }
-        String symbolicAddress = matcher.group("symbolicAddress");
-
-        String adsDataTypeString = matcher.group("adsDataType");
-        AdsDataType adsDataType = AdsDataType.valueOf(adsDataTypeString);
-
-        String stringLengthString = matcher.group("stringLength");
-        Integer stringLength = stringLengthString != null ? Integer.valueOf(stringLengthString) : null;
-
-        String numberOfElementsString = matcher.group("numberOfElements");
-        Integer numberOfElements = numberOfElementsString != null ? Integer.valueOf(numberOfElementsString) : null;
-
-        return new SymbolicAdsStringField(symbolicAddress, adsDataType, stringLength, numberOfElements);
-    }
-
-    public static boolean matches(String address) {
-        return SYMBOLIC_ADDRESS_STRING_PATTERN.matcher(address).matches();
-    }
-
-    @Override
-    public int getStringLength() {
-        return stringLength;
-    }
-
-    @Override
-    public String toString() {
-        return "SymbolicAdsStringField{" +
-            "symbolicAddress='" + getSymbolicAddress() + '\'' +
-            ", stringLength=" + stringLength +
-            '}';
-    }
-
-    @Override
-    public void serialize(WriteBuffer writeBuffer) throws SerializationException {
-        writeBuffer.pushContext(getClass().getSimpleName());
-
-        String symbolicAddress = getSymbolicAddress();
-        writeBuffer.writeString("symbolicAddress", symbolicAddress.getBytes(StandardCharsets.UTF_8).length * 8, StandardCharsets.UTF_8.name(), symbolicAddress);
-
-        writeBuffer.writeUnsignedLong("numberOfElements", 32, getNumberOfElements());
-
-        String dataType = getPlcDataType();
-        writeBuffer.writeString("dataType", dataType.getBytes(StandardCharsets.UTF_8).length * 8, StandardCharsets.UTF_8.name(), dataType);
-
-        writeBuffer.writeInt("stringLength", 32, getStringLength());
-        writeBuffer.popContext(getClass().getSimpleName());
-    }
-}
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/model/AdsSubscriptionHandle.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/model/AdsSubscriptionHandle.java
index a1f89670d..b1dc8ff72 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/model/AdsSubscriptionHandle.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/model/AdsSubscriptionHandle.java
@@ -18,7 +18,7 @@
  */
 package org.apache.plc4x.java.ads.model;
 
-import org.apache.plc4x.java.ads.readwrite.AdsDataType;
+import org.apache.plc4x.java.ads.readwrite.AdsDataTypeTableEntry;
 import org.apache.plc4x.java.spi.messages.PlcSubscriber;
 import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionHandle;
 
@@ -28,11 +28,11 @@ public class AdsSubscriptionHandle extends DefaultPlcSubscriptionHandle {
 
     private final String plcFieldName;
 
-    private final AdsDataType adsDataType;
+    private final AdsDataTypeTableEntry adsDataType;
 
     private final Long notificationHandle;
 
-    public AdsSubscriptionHandle(PlcSubscriber plcSubscriber, String plcFieldName, AdsDataType adsDataType, Long notificationHandle) {
+    public AdsSubscriptionHandle(PlcSubscriber plcSubscriber, String plcFieldName, AdsDataTypeTableEntry adsDataType, Long notificationHandle) {
         super(plcSubscriber);
         this.plcFieldName = plcFieldName;
         this.adsDataType = adsDataType;
@@ -43,7 +43,7 @@ public class AdsSubscriptionHandle extends DefaultPlcSubscriptionHandle {
         return plcFieldName;
     }
 
-    public AdsDataType getAdsDataType() {
+    public AdsDataTypeTableEntry getAdsDataType() {
         return adsDataType;
     }
 
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java
index 3f2c09a9c..abe27b7f0 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java
@@ -46,6 +46,8 @@ import org.apache.plc4x.java.spi.model.DefaultPlcConsumerRegistration;
 import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionField;
 import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
 import org.apache.plc4x.java.spi.values.IEC61131ValueHandler;
+import org.apache.plc4x.java.spi.values.PlcList;
+import org.apache.plc4x.java.spi.values.PlcStruct;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -72,14 +74,14 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
 
     private final Map<DefaultPlcConsumerRegistration, Consumer<PlcSubscriptionEvent>> consumers = new ConcurrentHashMap<>();
 
-    private final ConcurrentHashMap<SymbolicAdsField, DirectAdsField> symbolicFieldMapping;
+//    private final ConcurrentHashMap<SymbolicAdsField, DirectAdsField> symbolicFieldMapping;
     private final ConcurrentHashMap<SymbolicAdsField, CompletableFuture<Void>> pendingResolutionRequests;
 
     private final Map<String, AdsSymbolTableEntry> symbolTable;
     private final Map<String, AdsDataTypeTableEntry> dataTypeTable;
 
     public AdsProtocolLogic() {
-        symbolicFieldMapping = new ConcurrentHashMap<>();
+//        symbolicFieldMapping = new ConcurrentHashMap<>();
         pendingResolutionRequests = new ConcurrentHashMap<>();
         symbolTable = new HashMap<>();
         dataTypeTable = new HashMap<>();
@@ -410,18 +412,10 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
     protected CompletableFuture<PlcReadResponse> singleRead(PlcReadRequest readRequest, DirectAdsField directAdsField) {
         CompletableFuture<PlcReadResponse> future = new CompletableFuture<>();
 
-        long size;
-        if (directAdsField.getAdsDataType() == AdsDataType.STRING) {
-            // If an explicit size is given with the string, use this, if not use 256
-            size = (directAdsField instanceof AdsStringField) ?
-                ((AdsStringField) directAdsField).getStringLength() + 1 : 81;
-        } else if (directAdsField.getAdsDataType() == AdsDataType.WSTRING) {
-            // If an explicit size is given with the string, use this, if not use 512
-            size = (directAdsField instanceof AdsStringField) ?
-                ((long) ((AdsStringField) directAdsField).getStringLength() + 1) * 2 : 162;
-        } else {
-            size = directAdsField.getAdsDataType().getNumBytes();
-        }
+        String dataTypeName = directAdsField.getAdsDataTypeName();
+        AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(dataTypeName);
+        long size = adsDataTypeTableEntry.getSize();
+
         AmsPacket amsPacket = new AdsReadRequest(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
             configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(), 0, getInvokeId(),
             directAdsField.getIndexGroup(), directAdsField.getIndexOffset(),size * directAdsField.getNumberOfElements());
@@ -457,18 +451,9 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
         // Calculate the expected size of the response data.
         long expectedResponseDataSize = directAdsFields.stream().mapToLong(
             field -> {
-                long size;
-                if (field.getAdsDataType() == AdsDataType.STRING) {
-                    // If an explicit size is given with the string, use this, if not use 256
-                    size = (field instanceof AdsStringField) ?
-                        ((AdsStringField) field).getStringLength() + 1 : 256;
-                } else if (field.getAdsDataType() == AdsDataType.WSTRING) {
-                    // If an explicit size is given with the string, use this, if not use 512
-                    size = (field instanceof AdsStringField) ?
-                        ((long) ((AdsStringField) field).getStringLength() + 1) * 2 : 512;
-                } else {
-                    size = field.getAdsDataType().getNumBytes();
-                }
+                String dataTypeName = field.getAdsDataTypeName();
+                AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(dataTypeName);
+                long size = adsDataTypeTableEntry.getSize();
                 // Status code + payload size
                 return 4 + (size * field.getNumberOfElements());
             }).sum();
@@ -476,11 +461,15 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
         // With multi-requests, the index-group is fixed and the index offset indicates the number of elements.
         AmsPacket amsPacket = new AdsReadWriteRequest(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
             configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(),
-            0, getInvokeId(), ReservedIndexGroups.ADSIGRP_MULTIPLE_READ.getValue(), directAdsFields.size(), expectedResponseDataSize,
-            directAdsFields.stream().map(directAdsField -> new AdsMultiRequestItemRead(
-                    directAdsField.getIndexGroup(), directAdsField.getIndexOffset(),
-                    ((long) directAdsField.getAdsDataType().getNumBytes() * directAdsField.getNumberOfElements())))
-                .collect(Collectors.toList()), null);
+            0, getInvokeId(), ReservedIndexGroups.ADSIGRP_MULTIPLE_READ.getValue(), directAdsFields.size(),
+            expectedResponseDataSize, directAdsFields.stream().map(directAdsField -> {
+                String dataTypeName = directAdsField.getAdsDataTypeName();
+                AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(dataTypeName);
+                long size = adsDataTypeTableEntry.getSize();
+                return new AdsMultiRequestItemRead(
+                        directAdsField.getIndexGroup(), directAdsField.getIndexOffset(),
+                        (size * directAdsField.getNumberOfElements()));
+            }).collect(Collectors.toList()), null);
         AmsTCPPacket amsTCPPacket = new AmsTCPPacket(amsPacket);
 
         // Start a new request-transaction (Is ended in the response-handler)
@@ -535,14 +524,20 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
         if (readBuffer != null) {
             Map<String, ResponseItem<PlcValue>> values = new HashMap<>();
             for (String fieldName : readRequest.getFieldNames()) {
-                AdsField field = (AdsField) readRequest.getField(fieldName);
+                DirectAdsField field;
+                if(readRequest.getField(fieldName) instanceof DirectAdsField) {
+                    field = (DirectAdsField) readRequest.getField(fieldName);
+                } else {
+                    SymbolicAdsField symbolicAdsField = (SymbolicAdsField) readRequest.getField(fieldName);
+                    field = getDirectAdsFieldForSymbolicName(symbolicAdsField);
+                }
                 // If the response-code was anything but OK, we don't need to parse the payload.
                 if (responseCodes.get(fieldName) != PlcResponseCode.OK) {
                     values.put(fieldName, new ResponseItem<>(responseCodes.get(fieldName), null));
                 }
                 // If the response-code was ok, parse the data returned.
                 else {
-                    values.put(fieldName, parsePlcValue(field, readBuffer));
+                    values.put(fieldName, parseResponseItem(field, readBuffer));
                 }
             }
             return new DefaultPlcReadResponse(readRequest, values);
@@ -559,34 +554,91 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
         }
     }
 
-    private ResponseItem<PlcValue> parsePlcValue(AdsField field, ReadBuffer readBuffer) {
+    private ResponseItem<PlcValue> parseResponseItem(DirectAdsField field, ReadBuffer readBuffer) {
         try {
+            String dataTypeName = field.getAdsDataTypeName();
+            AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(dataTypeName);
+            PlcValueType plcValueType = getPlcValueTypeForAdsDataType(adsDataTypeTableEntry);
+
             int strLen = 0;
-            if ((field.getAdsDataType() == AdsDataType.STRING) || (field.getAdsDataType() == AdsDataType.WSTRING)) {
+            if ((plcValueType == PlcValueType.STRING) || (plcValueType == PlcValueType.WSTRING)) {
                 strLen = (field instanceof AdsStringField) ? ((AdsStringField) field).getStringLength() : 256;
             }
             final int stringLength = strLen;
             if (field.getNumberOfElements() == 1) {
-                return new ResponseItem<>(PlcResponseCode.OK,
-                    DataItem.staticParse(readBuffer, field.getAdsDataType().getPlcValueType(), stringLength));
+                return new ResponseItem<>(PlcResponseCode.OK, parsePlcValue(plcValueType, adsDataTypeTableEntry, stringLength, readBuffer));
             } else {
                 // Fetch all
                 final PlcValue[] resultItems = IntStream.range(0, field.getNumberOfElements()).mapToObj(i -> {
                     try {
-                        return DataItem.staticParse(readBuffer, field.getAdsDataType().getPlcValueType(), stringLength);
+                        return parsePlcValue(plcValueType, adsDataTypeTableEntry, stringLength, readBuffer);
                     } catch (ParseException e) {
-                        LOGGER.warn("Error parsing field item of type: '{}' (at position {}})", field.getAdsDataType(), i, e);
+                        LOGGER.warn("Error parsing field item of type: '{}' (at position {}})", field.getAdsDataTypeName(), i, e);
                     }
                     return null;
                 }).toArray(PlcValue[]::new);
                 return new ResponseItem<>(PlcResponseCode.OK, IEC61131ValueHandler.of(resultItems));
             }
         } catch (Exception e) {
-            LOGGER.warn(String.format("Error parsing field item of type: '%s'", field.getAdsDataType()), e);
+            LOGGER.warn(String.format("Error parsing field item of type: '%s'", field.getAdsDataTypeName()), e);
             return new ResponseItem<>(PlcResponseCode.INTERNAL_ERROR, null);
         }
     }
 
+    private PlcValue parsePlcValue(PlcValueType plcValueType, AdsDataTypeTableEntry adsDataTypeTableEntry, int stringLength, ReadBuffer readBuffer) throws ParseException {
+        switch (plcValueType) {
+            case Struct:
+                Map<String, PlcValue> properties = new HashMap<>();
+                int startPos = readBuffer.getPos();
+                int curPos = 0;
+                for (AdsDataTypeTableChildEntry child : adsDataTypeTableEntry.getChildren()) {
+                    if(child.getOffset() > curPos) {
+                        long skipBytes = child.getOffset() - curPos;
+                        for(long i = 0; i < skipBytes; i++) {
+                            readBuffer.readByte();
+                        }
+                    }
+                    String propertyName = child.getPropertyName();
+                    AdsDataTypeTableEntry propertyDataTypeTableEntry = dataTypeTable.get(child.getDataTypeName());
+                    PlcValueType propertyPlcValueType = getPlcValueTypeForAdsDataType(propertyDataTypeTableEntry);
+                    PlcValue propertyValue = parsePlcValue(propertyPlcValueType, propertyDataTypeTableEntry, stringLength, readBuffer);
+                    properties.put(propertyName, propertyValue);
+                    curPos = readBuffer.getPos() - startPos;
+                }
+                return new PlcStruct(properties);
+            case List:
+                return parseArrayLevel(adsDataTypeTableEntry, adsDataTypeTableEntry.getArrayInfo(), readBuffer);
+            default:
+                return DataItem.staticParse(readBuffer, plcValueType, stringLength);
+        }
+    }
+
+    private PlcValue parseArrayLevel(AdsDataTypeTableEntry adsDataTypeTableEntry, List<AdsDataTypeArrayInfo> arrayLayers, ReadBuffer readBuffer) throws ParseException {
+        // If this is the last layer of the Array, parse the values themselves.
+        if(arrayLayers.isEmpty()) {
+            String dataTypeName = adsDataTypeTableEntry.getDataTypeName();
+            dataTypeName = dataTypeName.substring(dataTypeName.lastIndexOf(" OF ") + 4);
+            int stringLength = 0;
+            if(dataTypeName.startsWith("STRING(")) {
+                stringLength = Integer.parseInt(dataTypeName.substring(7, dataTypeName.length() - 1));
+            } else if(dataTypeName.startsWith("WSTRING(")) {
+                stringLength = Integer.parseInt(dataTypeName.substring(8, dataTypeName.length() - 1));
+            }
+            AdsDataTypeTableEntry elementDataTypeTableEntry = dataTypeTable.get(dataTypeName);
+            PlcValueType plcValueType = getPlcValueTypeForAdsDataType(elementDataTypeTableEntry);
+            return parsePlcValue(plcValueType, elementDataTypeTableEntry, stringLength, readBuffer);
+        }
+
+        List<PlcValue> elements = new ArrayList<>();
+        List<AdsDataTypeArrayInfo> arrayInfo = adsDataTypeTableEntry.getArrayInfo();
+        AdsDataTypeArrayInfo firstLayer = arrayInfo.get(0);
+        for(int i = 0; i < firstLayer.getNumElements(); i++) {
+            List<AdsDataTypeArrayInfo> remainingLayers = arrayInfo.subList(1, arrayInfo.size());
+            elements.add(parseArrayLevel(adsDataTypeTableEntry, remainingLayers, readBuffer));
+        }
+        return new PlcList(elements);
+    }
+
     @Override
     public CompletableFuture<PlcWriteResponse> write(PlcWriteRequest writeRequest) {
         // Get all ADS addresses in their resolved state.
@@ -650,7 +702,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
     protected CompletableFuture<PlcWriteResponse> singleWrite(PlcWriteRequest writeRequest, DirectAdsField directAdsField) {
         CompletableFuture<PlcWriteResponse> future = new CompletableFuture<>();
 
-        final String fieldName = writeRequest.getFieldNames().iterator().next();
+/*        final String fieldName = writeRequest.getFieldNames().iterator().next();
         final AdsField plcField = (AdsField) writeRequest.getField(fieldName);
         final PlcValue plcValue = writeRequest.getPlcValue(fieldName);
         final int stringLength;
@@ -694,7 +746,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
                 }));
         } catch (Exception e) {
             future.completeExceptionally(new PlcException("Error"));
-        }
+        }*/
         return future;
     }
 
@@ -703,7 +755,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
 
         // Calculate the size of all fields together.
         // Calculate the expected size of the response data.
-        int expectedRequestDataSize = directAdsFields.stream().mapToInt(
+/*        int expectedRequestDataSize = directAdsFields.stream().mapToInt(
             field -> field.getAdsDataType().getNumBytes() * field.getNumberOfElements()).sum();
         byte[] writeBuffer = new byte[expectedRequestDataSize];
         int pos = 0;
@@ -762,7 +814,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
                 }
                 // Finish the request-transaction.
                 transaction.endRequest();
-            }));
+            }));*/
         return future;
     }
 
@@ -845,15 +897,19 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
 
         List<AmsTCPPacket> amsTCPPackets = subscribeRequest.getFields().stream()
             .map(field -> (DefaultPlcSubscriptionField) field)
-            .map(field -> new AmsTCPPacket(new AdsAddDeviceNotificationRequest(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
-                configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(),
-                0, getInvokeId(),
-                symbolicFieldMapping.get((SymbolicAdsField) field.getPlcField()).getIndexGroup(),
-                symbolicFieldMapping.get((SymbolicAdsField) field.getPlcField()).getIndexOffset(),
-                (long) ((AdsField) field.getPlcField()).getAdsDataType().getNumBytes() * field.getNumberOfElements(),
-                field.getPlcSubscriptionType() == PlcSubscriptionType.CYCLIC ? 3 : 4, // if it's not cyclic, it's on change or event
-                0, // there is no api for that yet
-                field.getDuration().orElse(Duration.ZERO).toMillis())))
+            .map(field -> {
+                AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(((DirectAdsField) field.getPlcField()).getAdsDataTypeName());
+                DirectAdsField directAdsField = getDirectAdsFieldForSymbolicName(field);
+                return new AmsTCPPacket(new AdsAddDeviceNotificationRequest(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
+                    configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(),
+                    0, getInvokeId(),
+                    directAdsField.getIndexGroup(),
+                    directAdsField.getIndexOffset(),
+                    adsDataTypeTableEntry.getSize() * field.getNumberOfElements(),
+                    field.getPlcSubscriptionType() == PlcSubscriptionType.CYCLIC ? 3 : 4, // if it's not cyclic, it's on change or event
+                    0, // there is no api for that yet
+                    field.getDuration().orElse(Duration.ZERO).toMillis()));
+            })
             .collect(Collectors.toList());
 
         Map<String, ResponseItem<PlcSubscriptionHandle>> responses = new HashMap<>();
@@ -887,12 +943,15 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
                 .unwrap(responseAmsPacket -> (AdsAddDeviceNotificationResponse) responseAmsPacket.getUserdata())
                 .handle(response -> {
                     if (response.getResult() == ReturnCode.OK) {
+                        DefaultPlcSubscriptionField subscriptionField = (DefaultPlcSubscriptionField) subscriptionRequest.getField(fieldName);
+                        AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(((DirectAdsField) subscriptionField.getPlcField()).getAdsDataTypeName());
+
                         // Collect notification handle from individual response.
                         responses.put(fieldName, new ResponseItem<>(
                             parsePlcResponseCode(response.getResult()),
                             new AdsSubscriptionHandle(this,
                                 fieldName,
-                                ((AdsField) ((DefaultPlcSubscriptionField) subscriptionRequest.getField(fieldName)).getPlcField()).getAdsDataType(),
+                                adsDataTypeTableEntry,
                                 response.getNotificationHandle())));
 
                         // After receiving the last ADD_DEVICE_NOTIFICATION response, complete the PLC4X response.
@@ -1022,7 +1081,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
         Map<String, ResponseItem<PlcValue>> values = new HashMap<>();
         ReadBufferByteBased readBuffer = new ReadBufferByteBased(data, ByteOrder.LITTLE_ENDIAN);
         values.put(subscriptionHandle.getPlcFieldName(), new ResponseItem<>(PlcResponseCode.OK,
-            DataItem.staticParse(readBuffer, subscriptionHandle.getAdsDataType().getPlcValueType(), data.length)));
+            DataItem.staticParse(readBuffer, getPlcValueTypeForAdsDataType(subscriptionHandle.getAdsDataType()), data.length)));
         return values;
     }
 
@@ -1052,7 +1111,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
 
         // Find out for which of these symbolic addresses no resolution has been initiated.
         final List<SymbolicAdsField> symbolicFieldsNeedingResolution = referencedSymbolicFields.stream()
-            .filter(symbolicAdsField -> !symbolicFieldMapping.containsKey(symbolicAdsField))
+            .filter(symbolicAdsField -> getDirectAdsFieldForSymbolicName(symbolicAdsField) == null)
             .collect(Collectors.toList());
 
         // If there are unresolved symbolic addresses, initiate the resolution
@@ -1095,7 +1154,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
                     List<DirectAdsField> directAdsFields = new ArrayList<>(fields.size());
                     for (PlcField field : fields) {
                         if (field instanceof SymbolicAdsField) {
-                            directAdsFields.add(symbolicFieldMapping.get(field));
+                            directAdsFields.add(getDirectAdsFieldForSymbolicName(field));
                         } else {
                             directAdsFields.add((DirectAdsField) field);
                         }
@@ -1107,7 +1166,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
             // If all fields were resolved, we can continue instantly.
             future.complete(fields.stream().map(plcField -> {
                 if (plcField instanceof SymbolicAdsField) {
-                    return symbolicFieldMapping.get(plcField);
+                    return getDirectAdsFieldForSymbolicName(plcField);
                 } else {
                     return (DirectAdsField) plcField;
                 }
@@ -1146,10 +1205,12 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
                         // Read the handle.
                         long handle = readBuffer.readUnsignedLong(32);
 
-                        DirectAdsField directAdsField = new DirectAdsField(
+/*                        DirectAdsField directAdsField = new DirectAdsField(
                             ReservedIndexGroups.ADSIGRP_SYM_VALBYHND.getValue(), handle,
-                            symbolicAdsField.getAdsDataType(), symbolicAdsField.getNumberOfElements());
-                        symbolicFieldMapping.put(symbolicAdsField, directAdsField);
+                            symbolicAdsField.getAdsDataTypeName(), symbolicAdsField.getNumberOfElements());*/
+
+                        // TODO: Find out how to read the datatype for the given symbolic field
+                        //symbolicFieldMapping.put(symbolicAdsField, directAdsField);
                         future.complete(null);
                     } catch (ParseException e) {
                         future.completeExceptionally(e);
@@ -1210,10 +1271,11 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
                             // Read the handle.
                             long handle = readBuffer.readUnsignedLong(32);
 
-                            DirectAdsField directAdsField = new DirectAdsField(
+                            /*DirectAdsField directAdsField = new DirectAdsField(
                                 ReservedIndexGroups.ADSIGRP_SYM_VALBYHND.getValue(), handle,
-                                symbolicAdsField.getAdsDataType(), symbolicAdsField.getNumberOfElements());
-                            symbolicFieldMapping.put(symbolicAdsField, directAdsField);
+                                symbolicAdsField.getAdsDataTypeName(), symbolicAdsField.getNumberOfElements());*/
+                            // TODO: Find out how to read the datatype for the given symbolic field
+                            //symbolicFieldMapping.put(symbolicAdsField, directAdsField);
                         } else {
                             // TODO: Handle the case of unsuccessful resolution ..
                         }
@@ -1236,6 +1298,77 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
         return invokeId;
     }
 
+    protected DirectAdsField getDirectAdsFieldForSymbolicName(PlcField field) {
+        if(field instanceof DirectAdsField) {
+            return (DirectAdsField) field;
+        }
+
+        SymbolicAdsField symbolicAdsField = (SymbolicAdsField) field;
+        String symbolicAddress = symbolicAdsField.getSymbolicAddress();
+        String[] addressParts = symbolicAddress.split("\\.");
+
+        // If the number of parts are less than 2, we can find the entry in the symbol table directly.
+        if(addressParts.length < 2) {
+            // We can't find it, so we need to resolve it.
+            if(!symbolTable.containsKey(symbolicAddress)) {
+                return null;
+            }
+
+            AdsSymbolTableEntry adsSymbolTableEntry = symbolTable.get(symbolicAddress);
+            AdsDataTypeTableEntry dataTypeTableEntry = dataTypeTable.get(adsSymbolTableEntry.getDataTypeName());
+            return new DirectAdsField(adsSymbolTableEntry.getGroup(), adsSymbolTableEntry.getOffset(),
+                dataTypeTableEntry.getDataTypeName(), dataTypeTableEntry.getArrayDimensions());
+        }
+        // Otherwise we'll have to crawl through the dataType definitions.
+        else {
+            String symbolName = addressParts[0] + "." + addressParts[1];
+            AdsSymbolTableEntry adsSymbolTableEntry = symbolTable.get(symbolName);
+            AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(adsSymbolTableEntry.getDataTypeName());
+            return resolveDirectAdsFieldForSymbolicNameFromDataType(
+                Arrays.asList(addressParts).subList(2, addressParts.length),
+                adsSymbolTableEntry.getGroup(), adsSymbolTableEntry.getOffset(), adsDataTypeTableEntry);
+        }
+    }
+
+    protected DirectAdsField resolveDirectAdsFieldForSymbolicNameFromDataType(List<String> remainingAddressParts, long currentGroup, long currentOffset, AdsDataTypeTableEntry adsDataTypeTableEntry) {
+        if(remainingAddressParts.isEmpty()) {
+            // TODO: Implement the Array support
+            return new DirectAdsField(currentGroup, currentOffset, adsDataTypeTableEntry.getDataTypeName(), null);
+        }
+
+        // Go through all children looking for a matching one.
+        for (AdsDataTypeTableChildEntry child : adsDataTypeTableEntry.getChildren()) {
+            if(child.getPropertyName().equals(remainingAddressParts.get(0))) {
+                AdsDataTypeTableEntry childAdsDataTypeTableEntry = dataTypeTable.get(child.getDataTypeName());
+                return resolveDirectAdsFieldForSymbolicNameFromDataType(
+                    remainingAddressParts.subList(1, remainingAddressParts.size()),
+                    currentGroup, currentOffset + child.getOffset(), childAdsDataTypeTableEntry);
+            }
+        }
+
+        throw new PlcRuntimeException(String.format("Couldn't find child with name '%s' for type '%s'",
+            remainingAddressParts.get(0), adsDataTypeTableEntry.getDataTypeName()));
+    }
+
+    protected PlcValueType getPlcValueTypeForAdsDataType(AdsDataTypeTableEntry dataTypeTableEntry) {
+        String dataTypeName = dataTypeTableEntry.getDataTypeName();
+        if(dataTypeName.startsWith("STRING(")) {
+            dataTypeName = "STRING";
+        } else if(dataTypeName.startsWith("WSTRING(")) {
+            dataTypeName = "WSTRING";
+        }
+        // First check, if this is a primitive type.
+        try {
+            return PlcValueType.valueOf(dataTypeName);
+        } catch (IllegalArgumentException e) {
+            // Then check if this is an array.
+            if(dataTypeTableEntry.getArrayDimensions() > 0) {
+                return PlcValueType.List;
+            }
+            return PlcValueType.Struct;
+       }
+    }
+
     protected byte[] getNullByteTerminatedArray(String value) {
         byte[] valueBytes = value.getBytes();
         byte[] nullTerminatedBytes = new byte[valueBytes.length + 1];
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/readwrite/utils/StaticHelper.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/readwrite/utils/StaticHelper.java
index 8e0b02735..f0b874735 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/readwrite/utils/StaticHelper.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/readwrite/utils/StaticHelper.java
@@ -47,6 +47,8 @@ public class StaticHelper {
                         break;
                     }
                 }
+                // Read the terminating byte.
+                readBuffer.readByte();
                 final byte[] byteArray = new byte[bytes.size()];
                 for (int i = 0; i < bytes.size(); i++) {
                     byteArray[i] = bytes.get(i);
@@ -68,6 +70,8 @@ public class StaticHelper {
                         break;
                     }
                 }
+                // Read the terminating byte.
+                readBuffer.readByte();
                 final byte[] byteArray = new byte[bytes.size()];
                 for (int i = 0; i < bytes.size(); i++) {
                     byteArray[i] = bytes.get(i);