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 2023/06/08 13:55:22 UTC

[plc4x] branch chore/profinet-phase-3 updated (8896bcbff6 -> a9aa5b8fc4)

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

cdutz pushed a change to branch chore/profinet-phase-3
in repository https://gitbox.apache.org/repos/asf/plc4x.git


    from 8896bcbff6 refactor(plc4j/udp-transport): Made it generally possible to open a UDP transport with a fixed local port
     new 6588e9a1e7 refactor(plc4j/profinet): Added some comments and made the tests use the classloader to load the test-data instead of a fixed file-reference.
     new a9aa5b8fc4 chore(plc4j/profinet-ng):  - Implemented the types needed to read "RealIdentifictionData" from the Device.  - Updated the connection process to use the information from that instead of the I&M0 data.  - Implemented the browse functionality.  - Documented the things I found out.

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 plc4j/drivers/profinet-ng/README.md                | 101 +++++++++--
 .../java/profinet/readwrite/PnIoCm_Block.java      |   2 +
 .../readwrite/PnIoCm_Block_ModuleDiff.java         |  18 +-
 .../PnIoCm_Block_RealIdentificationData.java}      |  69 ++++----
 .../readwrite/PnIoCm_RealIdentificationApi.java}   |  75 ++++----
 ...java => PnIoCm_RealIdentificationApi_Slot.java} | 111 +++++-------
 ...a => PnIoCm_RealIdentificationApi_Subslot.java} | 107 +++++------
 .../profinet/context/ProfinetDriverContext.java    |  10 ++
 .../java/profinet/packets/PnDcpPacketFactory.java  |  92 +++++++++-
 .../profinet/protocol/ProfinetProtocolLogic.java   | 195 ++++++++++++++++++---
 .../plc4x/java/profinet/tag/ProfinetTag.java       |  66 +++++--
 .../java/profinet/tag/ProfinetTagHandler.java      |   2 +-
 .../profinet/utils/ProfinetDataTypeMapper.java     | 125 +++++++++++++
 .../plc4x/java/profinet/ManualParserTest.java}     |  16 +-
 .../plc4x/java/profinet/ManualProfinetIoTest.java  |  11 +-
 .../java/profinet/readwrite/PnIoCm_Block.java      |   2 +
 .../readwrite/PnIoCm_Block_ModuleDiff.java         |  18 +-
 ...va => PnIoCm_Block_RealIdentificationData.java} |  69 ++++----
 ...kApi.java => PnIoCm_RealIdentificationApi.java} |  75 ++++----
 .../PnIoCm_RealIdentificationApi_Slot.java}        | 111 +++++-------
 .../PnIoCm_RealIdentificationApi_Subslot.java}     | 107 +++++------
 .../profinet/context/ProfinetDeviceContext.java    |   6 +
 .../plc4x/java/profinet/ManualProfinetIoTest.java  |  10 +-
 .../profinet/gsdml/ProfinetConfigurationTests.java |  15 +-
 .../profinet/gsdml/ProfinetGSDMLParseTest.java     |   7 +-
 .../main/resources/protocols/profinet/pnio.mspec   |  46 +++--
 26 files changed, 945 insertions(+), 521 deletions(-)
 copy plc4j/drivers/{profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_ModuleDiff.java => profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_RealIdentificationData.java} (77%)
 copy plc4j/drivers/{profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_ModuleDiffBlockApi.java => profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi.java} (66%)
 copy plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/{PnIoCm_ModuleDiffBlockApi_Module.java => PnIoCm_RealIdentificationApi_Slot.java} (60%)
 copy plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/{PnIoCm_IoCs.java => PnIoCm_RealIdentificationApi_Subslot.java} (59%)
 create mode 100644 plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/utils/ProfinetDataTypeMapper.java
 copy plc4j/drivers/{profinet/src/test/java/org/apache/plc4x/java/profinet/DummyMessageWrapper.java => profinet-ng/src/test/java/org/apache/plc4x/java/profinet/ManualParserTest.java} (56%)
 copy plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/{PnIoCm_Block_ModuleDiff.java => PnIoCm_Block_RealIdentificationData.java} (77%)
 copy plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/{PnIoCm_ModuleDiffBlockApi.java => PnIoCm_RealIdentificationApi.java} (66%)
 copy plc4j/drivers/{profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_ModuleDiffBlockApi_Module.java => profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi_Slot.java} (60%)
 copy plc4j/drivers/{profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_IoCs.java => profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi_Subslot.java} (59%)


[plc4x] 01/02: refactor(plc4j/profinet): Added some comments and made the tests use the classloader to load the test-data instead of a fixed file-reference.

Posted by cd...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

cdutz pushed a commit to branch chore/profinet-phase-3
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 6588e9a1e7e444c16e104c781c6dc9f51b8f4219
Author: Christofer Dutz <cd...@apache.org>
AuthorDate: Thu Jun 8 15:53:07 2023 +0200

    refactor(plc4j/profinet): Added some comments and made the tests use the classloader to load the test-data instead of a fixed file-reference.
---
 .../java/profinet/context/ProfinetDeviceContext.java      |  6 ++++++
 .../apache/plc4x/java/profinet/ManualProfinetIoTest.java  | 10 ++++++----
 .../java/profinet/gsdml/ProfinetConfigurationTests.java   | 15 +++++----------
 .../plc4x/java/profinet/gsdml/ProfinetGSDMLParseTest.java |  7 ++++---
 4 files changed, 21 insertions(+), 17 deletions(-)

diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/context/ProfinetDeviceContext.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/context/ProfinetDeviceContext.java
index 26364bc1b3..599a421938 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/context/ProfinetDeviceContext.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/context/ProfinetDeviceContext.java
@@ -370,6 +370,7 @@ public class ProfinetDeviceContext implements DriverContext, HasConfiguration<Pr
 
     private void extractGSDFileInfo(ProfinetISO15745Profile gsdFile) throws PlcConnectionException {
 
+        // Find the DeviceAccessPoint specified by the "deviceAccess" parameter
         for (ProfinetDeviceAccessPointItem deviceAccessItem : gsdFile.getProfileBody().getApplicationProcess().getDeviceAccessPointList()) {
             if (deviceAccess.equals(deviceAccessItem.getId())) {
                 this.deviceAccessItem = deviceAccessItem;
@@ -379,6 +380,9 @@ public class ProfinetDeviceContext implements DriverContext, HasConfiguration<Pr
             throw new PlcConnectionException("Unable to find Device Access Item - " + this.deviceAccess);
         }
 
+        // The DAP itself is always slot 0 (Defined by "FixedInSlots").
+        // The PhysicalSlots therefore should always be in a format "0..x" format
+        // (Except, if the device wouldn't have any modules, which wouldn't make sense)
         Matcher matcher = RANGE_PATTERN.matcher(deviceAccessItem.getPhysicalSlots());
         if (!matcher.matches()) {
             throw new PlcConnectionException("Physical Slots Range is not in the correct format " + deviceAccessItem.getPhysicalSlots());
@@ -393,9 +397,11 @@ public class ProfinetDeviceContext implements DriverContext, HasConfiguration<Pr
         this.modules[deviceAccessItem.getFixedInSlots()] = new ProfinetModuleImpl(deviceAccessItem, 0, 0, deviceAccessItem.getFixedInSlots());
 
         List<ProfinetModuleItemRef> usableSubModules = this.deviceAccessItem.getUseableModules();
+        // The first slot is always 0 which is the DAP slot, so in general we'll always start with 1
         int currentSlot = deviceAccessItem.getFixedInSlots() + 1;
         int inputOffset = this.modules[deviceAccessItem.getFixedInSlots()].getInputIoPsSize();
         int outputOffset = this.modules[deviceAccessItem.getFixedInSlots()].getOutputIoCsSize();
+        // Iterate over each module.
         for (String subModule : this.subModules) {
             if (subModule.equals("")) {
                 this.modules[currentSlot] = new ProfinetEmptyModule();
diff --git a/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/ManualProfinetIoTest.java b/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/ManualProfinetIoTest.java
index b7e7c24833..bb5b854553 100644
--- a/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/ManualProfinetIoTest.java
+++ b/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/ManualProfinetIoTest.java
@@ -20,10 +20,7 @@ package org.apache.plc4x.java.profinet;
 
 import org.apache.plc4x.java.DefaultPlcDriverManager;
 import org.apache.plc4x.java.api.PlcConnection;
-import org.apache.plc4x.java.api.messages.PlcBrowseRequest;
-import org.apache.plc4x.java.api.messages.PlcBrowseResponse;
-import org.apache.plc4x.java.api.messages.PlcSubscriptionRequest;
-import org.apache.plc4x.java.api.messages.PlcSubscriptionResponse;
+import org.apache.plc4x.java.api.messages.*;
 import org.apache.plc4x.java.api.types.PlcResponseCode;
 import org.apache.plc4x.java.profinet.device.ProfinetSubscriptionHandle;
 import org.apache.plc4x.java.profinet.tag.ProfinetTag;
@@ -45,6 +42,11 @@ public class ManualProfinetIoTest {
 
         PlcBrowseRequest browseRequest = connection.browseRequestBuilder().addQuery("all", "*").build();
         PlcBrowseResponse plcBrowseResponse = browseRequest.execute().get(4000, TimeUnit.MILLISECONDS);
+        for (String queryName : plcBrowseResponse.getQueryNames()) {
+            for (PlcBrowseItem value : plcBrowseResponse.getValues(queryName)) {
+                System.out.println(value.getTag().getAddressString());
+            }
+        }
         System.out.println(plcBrowseResponse);
         // Wireshark filters:
         // - S7 1200: eth.addr == 001c0605bcdc
diff --git a/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/gsdml/ProfinetConfigurationTests.java b/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/gsdml/ProfinetConfigurationTests.java
index 16321d3134..26589e1bb0 100644
--- a/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/gsdml/ProfinetConfigurationTests.java
+++ b/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/gsdml/ProfinetConfigurationTests.java
@@ -29,17 +29,12 @@ import org.apache.plc4x.java.profinet.device.*;
 import org.apache.plc4x.java.spi.configuration.ConfigurationFactory;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInstance;
-import org.pcap4j.core.PcapNativeException;
-import org.pcap4j.core.PcapNetworkInterface;
-import org.pcap4j.core.Pcaps;
-import org.pcap4j.util.Inet4NetworkAddress;
 
 import java.io.File;
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
+import java.io.InputStreamReader;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 
 import static org.junit.jupiter.api.Assertions.*;
 
@@ -166,7 +161,7 @@ public class ProfinetConfigurationTests {
         }
 
         XmlMapper xmlMapper = new XmlMapper();
-        assertDoesNotThrow(() -> devices.get("DEVICE_NAME_1").getDeviceContext().setGsdFile(xmlMapper.readValue(new File("src/test/resources/gsdml.xml"), ProfinetISO15745Profile.class)));
+        assertDoesNotThrow(() -> devices.get("DEVICE_NAME_1").getDeviceContext().setGsdFile(xmlMapper.readValue(new InputStreamReader(Objects.requireNonNull(getClass().getClassLoader().getResourceAsStream("gsdml.xml"))), ProfinetISO15745Profile.class)));
     }
 
     @Test
@@ -190,7 +185,7 @@ public class ProfinetConfigurationTests {
         }
 
         XmlMapper xmlMapper = new XmlMapper();
-        assertDoesNotThrow(() -> devices.get("DEVICE_NAME_1").getDeviceContext().setGsdFile(xmlMapper.readValue(new File("src/test/resources/gsdml.xml"), ProfinetISO15745Profile.class)));
+        assertDoesNotThrow(() -> devices.get("DEVICE_NAME_1").getDeviceContext().setGsdFile(xmlMapper.readValue(new InputStreamReader(Objects.requireNonNull(getClass().getClassLoader().getResourceAsStream("gsdml.xml"))), ProfinetISO15745Profile.class)));
     }
 
     @Test
@@ -300,7 +295,7 @@ public class ProfinetConfigurationTests {
         }
         XmlMapper xmlMapper = new XmlMapper();
 
-        assertDoesNotThrow(() -> devices.get("DEVICE NAME 1").getDeviceContext().setGsdFile(xmlMapper.readValue(new File("src/test/resources/gsdml.xml"), ProfinetISO15745Profile.class)));
+        assertDoesNotThrow(() -> devices.get("DEVICE NAME 1").getDeviceContext().setGsdFile(xmlMapper.readValue(new InputStreamReader(Objects.requireNonNull(getClass().getClassLoader().getResourceAsStream("gsdml.xml"))), ProfinetISO15745Profile.class)));
 
         for (String deviceName : deviceNames) {
             assert(devices.containsKey(deviceName));
diff --git a/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/gsdml/ProfinetGSDMLParseTest.java b/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/gsdml/ProfinetGSDMLParseTest.java
index 7224910da8..1edf264474 100644
--- a/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/gsdml/ProfinetGSDMLParseTest.java
+++ b/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/gsdml/ProfinetGSDMLParseTest.java
@@ -24,8 +24,9 @@ import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInstance;
 
-import java.io.File;
 import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Objects;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
@@ -38,7 +39,7 @@ public class ProfinetGSDMLParseTest {
     public void setUp() {
         try {
             XmlMapper xmlMapper = new XmlMapper();
-            this.gsdml = xmlMapper.readValue(new File("src/test/resources/gsdml.xml"), ProfinetISO15745Profile.class);
+            this.gsdml = xmlMapper.readValue(new InputStreamReader(Objects.requireNonNull(getClass().getClassLoader().getResourceAsStream("gsdml.xml"))), ProfinetISO15745Profile.class);
         } catch(IOException e) {
             assert false;
         }
@@ -51,7 +52,7 @@ public class ProfinetGSDMLParseTest {
 
     @Test
     public void readGsdmlFileStartupMode()  {
-        ProfinetInterfaceSubmoduleItem interfaceModule = (ProfinetInterfaceSubmoduleItem) this.gsdml.getProfileBody().getApplicationProcess().getDeviceAccessPointList().get(0).getSystemDefinedSubmoduleList().getInterfaceSubmodules().get(0);
+        ProfinetInterfaceSubmoduleItem interfaceModule = this.gsdml.getProfileBody().getApplicationProcess().getDeviceAccessPointList().get(0).getSystemDefinedSubmoduleList().getInterfaceSubmodules().get(0);
         assertEquals(interfaceModule.getApplicationRelations().getStartupMode(), "Advanced");
     }
 


[plc4x] 02/02: chore(plc4j/profinet-ng): - Implemented the types needed to read "RealIdentifictionData" from the Device. - Updated the connection process to use the information from that instead of the I&M0 data. - Implemented the browse functionality. - Documented the things I found out.

Posted by cd...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

cdutz pushed a commit to branch chore/profinet-phase-3
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit a9aa5b8fc4fc75911c57e1c3a7e5b2aea1ba17c7
Author: Christofer Dutz <cd...@apache.org>
AuthorDate: Thu Jun 8 15:55:11 2023 +0200

    chore(plc4j/profinet-ng):
     - Implemented the types needed to read "RealIdentifictionData" from the Device.
     - Updated the connection process to use the information from that instead of the I&M0 data.
     - Implemented the browse functionality.
     - Documented the things I found out.
---
 plc4j/drivers/profinet-ng/README.md                | 101 ++++++++--
 .../java/profinet/readwrite/PnIoCm_Block.java      |   2 +
 .../readwrite/PnIoCm_Block_ModuleDiff.java         |  18 +-
 ...va => PnIoCm_Block_RealIdentificationData.java} |  69 +++----
 .../readwrite/PnIoCm_RealIdentificationApi.java    | 183 ++++++++++++++++++
 .../PnIoCm_RealIdentificationApi_Slot.java         | 208 +++++++++++++++++++++
 .../PnIoCm_RealIdentificationApi_Subslot.java      | 161 ++++++++++++++++
 .../profinet/context/ProfinetDriverContext.java    |  10 +
 .../java/profinet/packets/PnDcpPacketFactory.java  |  92 ++++++++-
 .../profinet/protocol/ProfinetProtocolLogic.java   | 195 +++++++++++++++++--
 .../plc4x/java/profinet/tag/ProfinetTag.java       |  66 +++++--
 .../java/profinet/tag/ProfinetTagHandler.java      |   2 +-
 .../profinet/utils/ProfinetDataTypeMapper.java     | 125 +++++++++++++
 .../plc4x/java/profinet/ManualParserTest.java}     |  21 +--
 .../plc4x/java/profinet/ManualProfinetIoTest.java  |  11 +-
 .../java/profinet/readwrite/PnIoCm_Block.java      |   2 +
 .../readwrite/PnIoCm_Block_ModuleDiff.java         |  18 +-
 .../PnIoCm_Block_RealIdentificationData.java}      |  69 +++----
 .../readwrite/PnIoCm_RealIdentificationApi.java    | 183 ++++++++++++++++++
 .../PnIoCm_RealIdentificationApi_Slot.java         | 208 +++++++++++++++++++++
 .../PnIoCm_RealIdentificationApi_Subslot.java      | 161 ++++++++++++++++
 .../main/resources/protocols/profinet/pnio.mspec   |  46 ++++-
 22 files changed, 1790 insertions(+), 161 deletions(-)

diff --git a/plc4j/drivers/profinet-ng/README.md b/plc4j/drivers/profinet-ng/README.md
index 1fb6d6c6c6..39ef9850e9 100644
--- a/plc4j/drivers/profinet-ng/README.md
+++ b/plc4j/drivers/profinet-ng/README.md
@@ -21,20 +21,97 @@
 
 ## Connection
 
-In general a connection is ethernet based and therefore needs only a MAC address.
-We could simply send out every packet on every network device and listen to all for incoming packets, but that would be quite inefficient.
+PR IO generally works via raw Ethernet communication, so in general the Source and Target MAC address are the only bits of information needed. 
+However, does the Profinet family of protocols also have a number of assisting protocols. 
+The probably most important one is the Profinet CM (Context Manager) protocol, which is needed for establishing connections.
+This protocol is based on DCE/RPC messages, which are transported via UDP.
+In order to do this, an IP address is needed.
 
-During discovery, we get information like MAC address, IP address, Vendor-Id, Device-ID, but in theory the mac address should be enough.
-So I am thinking of two options: 
+Therefore, the simplest connection string for a Profinet device will be:
 
-1. The connection string contains an ip address
-2. The connection string doesn't contain an ip address
+    profinet://192.168.24.31
 
-If an IP address is present, we could theoretically simply use that ip address and try to find a local network device able to reach this.
-However on a system there might be multiple devices able to reach that target. 
-In my case my primary internet connection goes via Wi-Fi and a secondary goes via cable (So I can pass the full network to a VM and still be online via Wi-Fi). 
-We want to use the fastest option.
+The driver will do an ARP lookup in order to get the MAC address of the remote device and in order to select the right device.
+
+While most PN devices will be configured by the engineering tools and hereby also will be assigned an IP address, some devices need to be configured by the Profinet Master on connection establishment.
+For these devices we will have to come up with a mechanism, where the connection string will contain a mac-address and an ip-address as option. 
+The driver would be responsible for configuring the device before establishing a connection. 
+A connection string would look like this in this case:
+
+    profinet://60:6d:3c:3d:a9:a3?ip-address=192.168.24.31
+
+## GSD Device Profiles
+
+A Profinet device is described by a GSD file. 
+A GSD file is an XML file containing a description of the device.
+Only with this information can we provide browsing functionality, verify requests and reduce the amount of information passed to the driver to an absolute minimum.
+
+There are multiple coordinates needed to identify which resources a PN device offers.
+
+The most important ones are the vendor-id and the device-id. This tuple uniquely identifies a PN device or a family of devices.
+
+When connecting to a remote device, we execute a PN-DCP Identification request is sent to the device. 
+This is the same type of request used in the Auto-Discovery of PLC4X to find PN devices. 
+The main difference in this case however is that we don't send it to the broadcast MAC address, but send it to the devices MAC address instead.
+In the response we can get the vendor-id and the device-id along with a number of additional information (IP settings, name of the device, type of device).
+With this information we then iterate over the GSD files in our "gsd directory" (Which defaults to "~/.gsd"). 
+As soon as we found an XML file with a matching pair of vendor-id and product-id we use that device profile for this connection.
+
+A PN device always had one fixed component, the so-called DAP (Device Access Point). 
+This is always located in "slot 0" and you can think of this as the IO component that provides the connection between the network and the devices, using the Profinet protocol.
+Most device profiles only have one DAP and in this case, we simply use that. 
+However, there are some devices where the manufacturer updated the device over time and didn't give the new device a new product id.
+In this case, device profiles can contain a variety of DAPs. 
+
+If we are connecting to such a device it is important to know which DAP the devices provides.
+
+Next to the DAP in Slot 0 come the actual devices. 
+In some devices they are integrated into the same housing, only logically having multiple slots.
+This is the case for my Advantec Adam modules belong to this category the same way my Siemens Simodode Pro V PN device does.
+
+Others however allow understanding this concept a bit better though. 
+So-called Bus-Couplers for example provide PN access to IO devices. 
+Here you have the bus-coupler in slot 0 (which is the left-most element) and then you add on IO modules by sliding them onto the right. 
+When adding a new module to the existing, the new module is always added to the right.
+
+In PN terms each of these IO devices take a slot. 
+The left-most IO-device (next to the bus-coupler) is in slot 1, the next one in 2 and so on.
+
+As you can imagine that there is a great difference in how these modules work and what information they provide or are able to accept. 
+An 8-times digital Input definitely has a different set of information as an output or an analog input.
+These differences are modeled in so-called "Modules" in the GSD files. 
+As depending on the used DAP there might be differences in which modules are supported in which slot the DAP element contains a list of "usable modules".
+This describes which types of modules are allowed on which slot.
+
+In case of a Wago bus-coupler for example this looks like this:
+
+    <UseableModules>
+		<ModuleItemRef ModuleItemTarget="0606_0000" AllowedInSlots="1..250"/>
+		<ModuleItemRef ModuleItemTarget="0610_0000" AllowedInSlots="1..250"/>
+		<ModuleItemRef ModuleItemTarget="0611_0000" AllowedInSlots="1..250"/>
+		<ModuleItemRef ModuleItemTarget="00DI_02DIA_0000" AllowedInSlots="1..250"/>
+		<ModuleItemRef ModuleItemTarget="0400_0000" AllowedInSlots="1..250"/>
+		<ModuleItemRef ModuleItemTarget="0401_0000" AllowedInSlots="1..250"/>
+		<ModuleItemRef ModuleItemTarget="0402_0000" AllowedInSlots="1..250"/>
+		<ModuleItemRef ModuleItemTarget="0403_0000" AllowedInSlots="1..250"/>
+		<ModuleItemRef ModuleItemTarget="0405_0000" AllowedInSlots="1..250"/>
+		<ModuleItemRef ModuleItemTarget="0406_0000" AllowedInSlots="1..250"/>
+        ...
+    <UseableModules>
+
+You can see that the bus-coupler generally allows adding 250 devices. 
+Each entry in this list being one IO module Product Wago has to offer.
+"0606_0000" being a "Power Supply 24 V DC, max. 1.0 A; Ex i; diagnostics" and "0403_0000" being a "4-channel digital input; 24 V DC; 0.2 ms input filter; 2- to 3-conductor connection; high-side switching"
+
+## Auto-Configuring the connection 
+
+So in order to know which type of module is present in which slot, we need to ask the PN device.
+
+This is done by reading the "RealIdentificationData" from the device using PN-CM (DCE/RPC, UDP) as part of the connection establishment process.
+
+This information gives us detailed information on how many slots are being used, which slot uses which module and which sub-slots this device has and what subModules these map to.
+From the information on which module is used in slot 0 we also know which DAP is being used.
+
+With this information can we fully provide PLC4X browse functionality, and we are able to validate request before sending anything to the device. 
 
-So I was thinking of sending an ARP (Address Resolution Protocol) request for resolving the remote address and to take the ip address returned first. 
 
-As soon as we have found the local network device then we can send a PN_DCP 
\ No newline at end of file
diff --git a/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block.java b/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block.java
index 6d70c8e103..e4bf5c8538 100644
--- a/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block.java
+++ b/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block.java
@@ -147,6 +147,8 @@ public abstract class PnIoCm_Block implements Message {
       builder = PnIoCm_Block_ModuleDiff.staticParsePnIoCm_BlockBuilder(readBuffer);
     } else if (EvaluationHelper.equals(blockType, PnIoCm_BlockType.AR_SERVER_BLOCK)) {
       builder = PnIoCm_Block_ArServer.staticParsePnIoCm_BlockBuilder(readBuffer);
+    } else if (EvaluationHelper.equals(blockType, PnIoCm_BlockType.REAL_IDENTIFICATION_DATA)) {
+      builder = PnIoCm_Block_RealIdentificationData.staticParsePnIoCm_BlockBuilder(readBuffer);
     } else if (EvaluationHelper.equals(blockType, PnIoCm_BlockType.I_AND_M_0)) {
       builder = PnIoCm_Block_IAndM0.staticParsePnIoCm_BlockBuilder(readBuffer);
     } else if (EvaluationHelper.equals(blockType, PnIoCm_BlockType.I_AND_M_1)) {
diff --git a/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_ModuleDiff.java b/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_ModuleDiff.java
index dbe6423401..082fedbf8a 100644
--- a/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_ModuleDiff.java
+++ b/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_ModuleDiff.java
@@ -96,12 +96,12 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
         writeUnsignedShort(writeBuffer, 8),
         WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
-    // Implicit Field (numberOfApis) (Used for parsing, but its value is not stored as it's
-    // implicitly given by the objects content)
-    int numberOfApis = (int) (COUNT(getApis()));
+    // Implicit Field (numApis) (Used for parsing, but its value is not stored as it's implicitly
+    // given by the objects content)
+    int numApis = (int) (COUNT(getApis()));
     writeImplicitField(
-        "numberOfApis",
-        numberOfApis,
+        "numApis",
+        numApis,
         writeUnsignedInt(writeBuffer, 16),
         WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
@@ -132,7 +132,7 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
     // Simple field (blockVersionLow)
     lengthInBits += 8;
 
-    // Implicit Field (numberOfApis)
+    // Implicit Field (numApis)
     lengthInBits += 16;
 
     // Array field
@@ -171,9 +171,9 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
             readUnsignedShort(readBuffer, 8),
             WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
-    int numberOfApis =
+    int numApis =
         readImplicitField(
-            "numberOfApis",
+            "numApis",
             readUnsignedInt(readBuffer, 16),
             WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
@@ -182,7 +182,7 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
             "apis",
             new DataReaderComplexDefault<>(
                 () -> PnIoCm_ModuleDiffBlockApi.staticParse(readBuffer), readBuffer),
-            numberOfApis,
+            numApis,
             WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
     readBuffer.closeContext("PnIoCm_Block_ModuleDiff");
diff --git a/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_ModuleDiff.java b/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_RealIdentificationData.java
similarity index 77%
copy from plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_ModuleDiff.java
copy to plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_RealIdentificationData.java
index dbe6423401..c8a51fc8d7 100644
--- a/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_ModuleDiff.java
+++ b/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_RealIdentificationData.java
@@ -35,20 +35,20 @@ import org.apache.plc4x.java.spi.generation.*;
 
 // Code generated by code-generation. DO NOT EDIT.
 
-public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
+public class PnIoCm_Block_RealIdentificationData extends PnIoCm_Block implements Message {
 
   // Accessors for discriminator values.
   public PnIoCm_BlockType getBlockType() {
-    return PnIoCm_BlockType.MODULE_DIFF_BLOCK;
+    return PnIoCm_BlockType.REAL_IDENTIFICATION_DATA;
   }
 
   // Properties.
   protected final short blockVersionHigh;
   protected final short blockVersionLow;
-  protected final List<PnIoCm_ModuleDiffBlockApi> apis;
+  protected final List<PnIoCm_RealIdentificationApi> apis;
 
-  public PnIoCm_Block_ModuleDiff(
-      short blockVersionHigh, short blockVersionLow, List<PnIoCm_ModuleDiffBlockApi> apis) {
+  public PnIoCm_Block_RealIdentificationData(
+      short blockVersionHigh, short blockVersionLow, List<PnIoCm_RealIdentificationApi> apis) {
     super();
     this.blockVersionHigh = blockVersionHigh;
     this.blockVersionLow = blockVersionLow;
@@ -63,7 +63,7 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
     return blockVersionLow;
   }
 
-  public List<PnIoCm_ModuleDiffBlockApi> getApis() {
+  public List<PnIoCm_RealIdentificationApi> getApis() {
     return apis;
   }
 
@@ -71,7 +71,7 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
   protected void serializePnIoCm_BlockChild(WriteBuffer writeBuffer) throws SerializationException {
     PositionAware positionAware = writeBuffer;
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
-    writeBuffer.pushContext("PnIoCm_Block_ModuleDiff");
+    writeBuffer.pushContext("PnIoCm_Block_RealIdentificationData");
 
     // Implicit Field (blockLength) (Used for parsing, but its value is not stored as it's
     // implicitly given by the objects content)
@@ -96,12 +96,12 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
         writeUnsignedShort(writeBuffer, 8),
         WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
-    // Implicit Field (numberOfApis) (Used for parsing, but its value is not stored as it's
-    // implicitly given by the objects content)
-    int numberOfApis = (int) (COUNT(getApis()));
+    // Implicit Field (numApis) (Used for parsing, but its value is not stored as it's implicitly
+    // given by the objects content)
+    int numApis = (int) (COUNT(getApis()));
     writeImplicitField(
-        "numberOfApis",
-        numberOfApis,
+        "numApis",
+        numApis,
         writeUnsignedInt(writeBuffer, 16),
         WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
@@ -109,7 +109,7 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
     writeComplexTypeArrayField(
         "apis", apis, writeBuffer, WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
-    writeBuffer.popContext("PnIoCm_Block_ModuleDiff");
+    writeBuffer.popContext("PnIoCm_Block_RealIdentificationData");
   }
 
   @Override
@@ -120,7 +120,7 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
   @Override
   public int getLengthInBits() {
     int lengthInBits = super.getLengthInBits();
-    PnIoCm_Block_ModuleDiff _value = this;
+    PnIoCm_Block_RealIdentificationData _value = this;
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
 
     // Implicit Field (blockLength)
@@ -132,13 +132,13 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
     // Simple field (blockVersionLow)
     lengthInBits += 8;
 
-    // Implicit Field (numberOfApis)
+    // Implicit Field (numApis)
     lengthInBits += 16;
 
     // Array field
     if (apis != null) {
       int i = 0;
-      for (PnIoCm_ModuleDiffBlockApi element : apis) {
+      for (PnIoCm_RealIdentificationApi element : apis) {
         ThreadLocalHelper.lastItemThreadLocal.set(++i >= apis.size());
         lengthInBits += element.getLengthInBits();
       }
@@ -149,7 +149,7 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
 
   public static PnIoCm_BlockBuilder staticParsePnIoCm_BlockBuilder(ReadBuffer readBuffer)
       throws ParseException {
-    readBuffer.pullContext("PnIoCm_Block_ModuleDiff");
+    readBuffer.pullContext("PnIoCm_Block_RealIdentificationData");
     PositionAware positionAware = readBuffer;
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
 
@@ -171,42 +171,43 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
             readUnsignedShort(readBuffer, 8),
             WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
-    int numberOfApis =
+    int numApis =
         readImplicitField(
-            "numberOfApis",
+            "numApis",
             readUnsignedInt(readBuffer, 16),
             WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
-    List<PnIoCm_ModuleDiffBlockApi> apis =
+    List<PnIoCm_RealIdentificationApi> apis =
         readCountArrayField(
             "apis",
             new DataReaderComplexDefault<>(
-                () -> PnIoCm_ModuleDiffBlockApi.staticParse(readBuffer), readBuffer),
-            numberOfApis,
+                () -> PnIoCm_RealIdentificationApi.staticParse(readBuffer), readBuffer),
+            numApis,
             WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
-    readBuffer.closeContext("PnIoCm_Block_ModuleDiff");
+    readBuffer.closeContext("PnIoCm_Block_RealIdentificationData");
     // Create the instance
-    return new PnIoCm_Block_ModuleDiffBuilderImpl(blockVersionHigh, blockVersionLow, apis);
+    return new PnIoCm_Block_RealIdentificationDataBuilderImpl(
+        blockVersionHigh, blockVersionLow, apis);
   }
 
-  public static class PnIoCm_Block_ModuleDiffBuilderImpl
+  public static class PnIoCm_Block_RealIdentificationDataBuilderImpl
       implements PnIoCm_Block.PnIoCm_BlockBuilder {
     private final short blockVersionHigh;
     private final short blockVersionLow;
-    private final List<PnIoCm_ModuleDiffBlockApi> apis;
+    private final List<PnIoCm_RealIdentificationApi> apis;
 
-    public PnIoCm_Block_ModuleDiffBuilderImpl(
-        short blockVersionHigh, short blockVersionLow, List<PnIoCm_ModuleDiffBlockApi> apis) {
+    public PnIoCm_Block_RealIdentificationDataBuilderImpl(
+        short blockVersionHigh, short blockVersionLow, List<PnIoCm_RealIdentificationApi> apis) {
       this.blockVersionHigh = blockVersionHigh;
       this.blockVersionLow = blockVersionLow;
       this.apis = apis;
     }
 
-    public PnIoCm_Block_ModuleDiff build() {
-      PnIoCm_Block_ModuleDiff pnIoCm_Block_ModuleDiff =
-          new PnIoCm_Block_ModuleDiff(blockVersionHigh, blockVersionLow, apis);
-      return pnIoCm_Block_ModuleDiff;
+    public PnIoCm_Block_RealIdentificationData build() {
+      PnIoCm_Block_RealIdentificationData pnIoCm_Block_RealIdentificationData =
+          new PnIoCm_Block_RealIdentificationData(blockVersionHigh, blockVersionLow, apis);
+      return pnIoCm_Block_RealIdentificationData;
     }
   }
 
@@ -215,10 +216,10 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
     if (this == o) {
       return true;
     }
-    if (!(o instanceof PnIoCm_Block_ModuleDiff)) {
+    if (!(o instanceof PnIoCm_Block_RealIdentificationData)) {
       return false;
     }
-    PnIoCm_Block_ModuleDiff that = (PnIoCm_Block_ModuleDiff) o;
+    PnIoCm_Block_RealIdentificationData that = (PnIoCm_Block_RealIdentificationData) o;
     return (getBlockVersionHigh() == that.getBlockVersionHigh())
         && (getBlockVersionLow() == that.getBlockVersionLow())
         && (getApis() == that.getApis())
diff --git a/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi.java b/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi.java
new file mode 100644
index 0000000000..a9da45154a
--- /dev/null
+++ b/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi.java
@@ -0,0 +1,183 @@
+/*
+ * 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.profinet.readwrite;
+
+import static org.apache.plc4x.java.spi.codegen.fields.FieldReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.fields.FieldWriterFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataWriterFactory.*;
+import static org.apache.plc4x.java.spi.generation.StaticHelper.*;
+
+import java.time.*;
+import java.util.*;
+import org.apache.plc4x.java.api.exceptions.*;
+import org.apache.plc4x.java.api.value.*;
+import org.apache.plc4x.java.spi.codegen.*;
+import org.apache.plc4x.java.spi.codegen.fields.*;
+import org.apache.plc4x.java.spi.codegen.io.*;
+import org.apache.plc4x.java.spi.generation.*;
+
+// Code generated by code-generation. DO NOT EDIT.
+
+public class PnIoCm_RealIdentificationApi implements Message {
+
+  // Constant values.
+  public static final Long API = 0x00000000L;
+
+  // Properties.
+  protected final List<PnIoCm_RealIdentificationApi_Slot> slots;
+
+  public PnIoCm_RealIdentificationApi(List<PnIoCm_RealIdentificationApi_Slot> slots) {
+    super();
+    this.slots = slots;
+  }
+
+  public List<PnIoCm_RealIdentificationApi_Slot> getSlots() {
+    return slots;
+  }
+
+  public long getApi() {
+    return API;
+  }
+
+  public void serialize(WriteBuffer writeBuffer) throws SerializationException {
+    PositionAware positionAware = writeBuffer;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+    writeBuffer.pushContext("PnIoCm_RealIdentificationApi");
+
+    // Const Field (api)
+    writeConstField(
+        "api",
+        API,
+        writeUnsignedLong(writeBuffer, 32),
+        WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    // Implicit Field (numSlots) (Used for parsing, but its value is not stored as it's implicitly
+    // given by the objects content)
+    int numSlots = (int) (COUNT(getSlots()));
+    writeImplicitField(
+        "numSlots",
+        numSlots,
+        writeUnsignedInt(writeBuffer, 16),
+        WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    // Array Field (slots)
+    writeComplexTypeArrayField(
+        "slots", slots, writeBuffer, WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    writeBuffer.popContext("PnIoCm_RealIdentificationApi");
+  }
+
+  @Override
+  public int getLengthInBytes() {
+    return (int) Math.ceil((float) getLengthInBits() / 8.0);
+  }
+
+  @Override
+  public int getLengthInBits() {
+    int lengthInBits = 0;
+    PnIoCm_RealIdentificationApi _value = this;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+    // Const Field (api)
+    lengthInBits += 32;
+
+    // Implicit Field (numSlots)
+    lengthInBits += 16;
+
+    // Array field
+    if (slots != null) {
+      int i = 0;
+      for (PnIoCm_RealIdentificationApi_Slot element : slots) {
+        ThreadLocalHelper.lastItemThreadLocal.set(++i >= slots.size());
+        lengthInBits += element.getLengthInBits();
+      }
+    }
+
+    return lengthInBits;
+  }
+
+  public static PnIoCm_RealIdentificationApi staticParse(ReadBuffer readBuffer, Object... args)
+      throws ParseException {
+    PositionAware positionAware = readBuffer;
+    return staticParse(readBuffer);
+  }
+
+  public static PnIoCm_RealIdentificationApi staticParse(ReadBuffer readBuffer)
+      throws ParseException {
+    readBuffer.pullContext("PnIoCm_RealIdentificationApi");
+    PositionAware positionAware = readBuffer;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+    long api =
+        readConstField(
+            "api",
+            readUnsignedLong(readBuffer, 32),
+            PnIoCm_RealIdentificationApi.API,
+            WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    int numSlots =
+        readImplicitField(
+            "numSlots",
+            readUnsignedInt(readBuffer, 16),
+            WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    List<PnIoCm_RealIdentificationApi_Slot> slots =
+        readCountArrayField(
+            "slots",
+            new DataReaderComplexDefault<>(
+                () -> PnIoCm_RealIdentificationApi_Slot.staticParse(readBuffer), readBuffer),
+            numSlots,
+            WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    readBuffer.closeContext("PnIoCm_RealIdentificationApi");
+    // Create the instance
+    PnIoCm_RealIdentificationApi _pnIoCm_RealIdentificationApi;
+    _pnIoCm_RealIdentificationApi = new PnIoCm_RealIdentificationApi(slots);
+    return _pnIoCm_RealIdentificationApi;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof PnIoCm_RealIdentificationApi)) {
+      return false;
+    }
+    PnIoCm_RealIdentificationApi that = (PnIoCm_RealIdentificationApi) o;
+    return (getSlots() == that.getSlots()) && true;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(getSlots());
+  }
+
+  @Override
+  public String toString() {
+    WriteBufferBoxBased writeBufferBoxBased = new WriteBufferBoxBased(true, true);
+    try {
+      writeBufferBoxBased.writeSerializable(this);
+    } catch (SerializationException e) {
+      throw new RuntimeException(e);
+    }
+    return "\n" + writeBufferBoxBased.getBox().toString() + "\n";
+  }
+}
diff --git a/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi_Slot.java b/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi_Slot.java
new file mode 100644
index 0000000000..fd1c5ac8e4
--- /dev/null
+++ b/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi_Slot.java
@@ -0,0 +1,208 @@
+/*
+ * 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.profinet.readwrite;
+
+import static org.apache.plc4x.java.spi.codegen.fields.FieldReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.fields.FieldWriterFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataWriterFactory.*;
+import static org.apache.plc4x.java.spi.generation.StaticHelper.*;
+
+import java.time.*;
+import java.util.*;
+import org.apache.plc4x.java.api.exceptions.*;
+import org.apache.plc4x.java.api.value.*;
+import org.apache.plc4x.java.spi.codegen.*;
+import org.apache.plc4x.java.spi.codegen.fields.*;
+import org.apache.plc4x.java.spi.codegen.io.*;
+import org.apache.plc4x.java.spi.generation.*;
+
+// Code generated by code-generation. DO NOT EDIT.
+
+public class PnIoCm_RealIdentificationApi_Slot implements Message {
+
+  // Properties.
+  protected final int slotNumber;
+  protected final long moduleIdentNumber;
+  protected final List<PnIoCm_RealIdentificationApi_Subslot> subslots;
+
+  public PnIoCm_RealIdentificationApi_Slot(
+      int slotNumber, long moduleIdentNumber, List<PnIoCm_RealIdentificationApi_Subslot> subslots) {
+    super();
+    this.slotNumber = slotNumber;
+    this.moduleIdentNumber = moduleIdentNumber;
+    this.subslots = subslots;
+  }
+
+  public int getSlotNumber() {
+    return slotNumber;
+  }
+
+  public long getModuleIdentNumber() {
+    return moduleIdentNumber;
+  }
+
+  public List<PnIoCm_RealIdentificationApi_Subslot> getSubslots() {
+    return subslots;
+  }
+
+  public void serialize(WriteBuffer writeBuffer) throws SerializationException {
+    PositionAware positionAware = writeBuffer;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+    writeBuffer.pushContext("PnIoCm_RealIdentificationApi_Slot");
+
+    // Simple Field (slotNumber)
+    writeSimpleField(
+        "slotNumber",
+        slotNumber,
+        writeUnsignedInt(writeBuffer, 16),
+        WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    // Simple Field (moduleIdentNumber)
+    writeSimpleField(
+        "moduleIdentNumber",
+        moduleIdentNumber,
+        writeUnsignedLong(writeBuffer, 32),
+        WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    // Implicit Field (numSubslots) (Used for parsing, but its value is not stored as it's
+    // implicitly given by the objects content)
+    int numSubslots = (int) (COUNT(getSubslots()));
+    writeImplicitField(
+        "numSubslots",
+        numSubslots,
+        writeUnsignedInt(writeBuffer, 16),
+        WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    // Array Field (subslots)
+    writeComplexTypeArrayField(
+        "subslots", subslots, writeBuffer, WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    writeBuffer.popContext("PnIoCm_RealIdentificationApi_Slot");
+  }
+
+  @Override
+  public int getLengthInBytes() {
+    return (int) Math.ceil((float) getLengthInBits() / 8.0);
+  }
+
+  @Override
+  public int getLengthInBits() {
+    int lengthInBits = 0;
+    PnIoCm_RealIdentificationApi_Slot _value = this;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+    // Simple field (slotNumber)
+    lengthInBits += 16;
+
+    // Simple field (moduleIdentNumber)
+    lengthInBits += 32;
+
+    // Implicit Field (numSubslots)
+    lengthInBits += 16;
+
+    // Array field
+    if (subslots != null) {
+      int i = 0;
+      for (PnIoCm_RealIdentificationApi_Subslot element : subslots) {
+        ThreadLocalHelper.lastItemThreadLocal.set(++i >= subslots.size());
+        lengthInBits += element.getLengthInBits();
+      }
+    }
+
+    return lengthInBits;
+  }
+
+  public static PnIoCm_RealIdentificationApi_Slot staticParse(ReadBuffer readBuffer, Object... args)
+      throws ParseException {
+    PositionAware positionAware = readBuffer;
+    return staticParse(readBuffer);
+  }
+
+  public static PnIoCm_RealIdentificationApi_Slot staticParse(ReadBuffer readBuffer)
+      throws ParseException {
+    readBuffer.pullContext("PnIoCm_RealIdentificationApi_Slot");
+    PositionAware positionAware = readBuffer;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+    int slotNumber =
+        readSimpleField(
+            "slotNumber",
+            readUnsignedInt(readBuffer, 16),
+            WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    long moduleIdentNumber =
+        readSimpleField(
+            "moduleIdentNumber",
+            readUnsignedLong(readBuffer, 32),
+            WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    int numSubslots =
+        readImplicitField(
+            "numSubslots",
+            readUnsignedInt(readBuffer, 16),
+            WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    List<PnIoCm_RealIdentificationApi_Subslot> subslots =
+        readCountArrayField(
+            "subslots",
+            new DataReaderComplexDefault<>(
+                () -> PnIoCm_RealIdentificationApi_Subslot.staticParse(readBuffer), readBuffer),
+            numSubslots,
+            WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    readBuffer.closeContext("PnIoCm_RealIdentificationApi_Slot");
+    // Create the instance
+    PnIoCm_RealIdentificationApi_Slot _pnIoCm_RealIdentificationApi_Slot;
+    _pnIoCm_RealIdentificationApi_Slot =
+        new PnIoCm_RealIdentificationApi_Slot(slotNumber, moduleIdentNumber, subslots);
+    return _pnIoCm_RealIdentificationApi_Slot;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof PnIoCm_RealIdentificationApi_Slot)) {
+      return false;
+    }
+    PnIoCm_RealIdentificationApi_Slot that = (PnIoCm_RealIdentificationApi_Slot) o;
+    return (getSlotNumber() == that.getSlotNumber())
+        && (getModuleIdentNumber() == that.getModuleIdentNumber())
+        && (getSubslots() == that.getSubslots())
+        && true;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(getSlotNumber(), getModuleIdentNumber(), getSubslots());
+  }
+
+  @Override
+  public String toString() {
+    WriteBufferBoxBased writeBufferBoxBased = new WriteBufferBoxBased(true, true);
+    try {
+      writeBufferBoxBased.writeSerializable(this);
+    } catch (SerializationException e) {
+      throw new RuntimeException(e);
+    }
+    return "\n" + writeBufferBoxBased.getBox().toString() + "\n";
+  }
+}
diff --git a/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi_Subslot.java b/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi_Subslot.java
new file mode 100644
index 0000000000..b8dd21bc9e
--- /dev/null
+++ b/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi_Subslot.java
@@ -0,0 +1,161 @@
+/*
+ * 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.profinet.readwrite;
+
+import static org.apache.plc4x.java.spi.codegen.fields.FieldReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.fields.FieldWriterFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataWriterFactory.*;
+import static org.apache.plc4x.java.spi.generation.StaticHelper.*;
+
+import java.time.*;
+import java.util.*;
+import org.apache.plc4x.java.api.exceptions.*;
+import org.apache.plc4x.java.api.value.*;
+import org.apache.plc4x.java.spi.codegen.*;
+import org.apache.plc4x.java.spi.codegen.fields.*;
+import org.apache.plc4x.java.spi.codegen.io.*;
+import org.apache.plc4x.java.spi.generation.*;
+
+// Code generated by code-generation. DO NOT EDIT.
+
+public class PnIoCm_RealIdentificationApi_Subslot implements Message {
+
+  // Properties.
+  protected final int subslotNumber;
+  protected final long submoduleIdentNumber;
+
+  public PnIoCm_RealIdentificationApi_Subslot(int subslotNumber, long submoduleIdentNumber) {
+    super();
+    this.subslotNumber = subslotNumber;
+    this.submoduleIdentNumber = submoduleIdentNumber;
+  }
+
+  public int getSubslotNumber() {
+    return subslotNumber;
+  }
+
+  public long getSubmoduleIdentNumber() {
+    return submoduleIdentNumber;
+  }
+
+  public void serialize(WriteBuffer writeBuffer) throws SerializationException {
+    PositionAware positionAware = writeBuffer;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+    writeBuffer.pushContext("PnIoCm_RealIdentificationApi_Subslot");
+
+    // Simple Field (subslotNumber)
+    writeSimpleField(
+        "subslotNumber",
+        subslotNumber,
+        writeUnsignedInt(writeBuffer, 16),
+        WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    // Simple Field (submoduleIdentNumber)
+    writeSimpleField(
+        "submoduleIdentNumber",
+        submoduleIdentNumber,
+        writeUnsignedLong(writeBuffer, 32),
+        WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    writeBuffer.popContext("PnIoCm_RealIdentificationApi_Subslot");
+  }
+
+  @Override
+  public int getLengthInBytes() {
+    return (int) Math.ceil((float) getLengthInBits() / 8.0);
+  }
+
+  @Override
+  public int getLengthInBits() {
+    int lengthInBits = 0;
+    PnIoCm_RealIdentificationApi_Subslot _value = this;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+    // Simple field (subslotNumber)
+    lengthInBits += 16;
+
+    // Simple field (submoduleIdentNumber)
+    lengthInBits += 32;
+
+    return lengthInBits;
+  }
+
+  public static PnIoCm_RealIdentificationApi_Subslot staticParse(
+      ReadBuffer readBuffer, Object... args) throws ParseException {
+    PositionAware positionAware = readBuffer;
+    return staticParse(readBuffer);
+  }
+
+  public static PnIoCm_RealIdentificationApi_Subslot staticParse(ReadBuffer readBuffer)
+      throws ParseException {
+    readBuffer.pullContext("PnIoCm_RealIdentificationApi_Subslot");
+    PositionAware positionAware = readBuffer;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+    int subslotNumber =
+        readSimpleField(
+            "subslotNumber",
+            readUnsignedInt(readBuffer, 16),
+            WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    long submoduleIdentNumber =
+        readSimpleField(
+            "submoduleIdentNumber",
+            readUnsignedLong(readBuffer, 32),
+            WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    readBuffer.closeContext("PnIoCm_RealIdentificationApi_Subslot");
+    // Create the instance
+    PnIoCm_RealIdentificationApi_Subslot _pnIoCm_RealIdentificationApi_Subslot;
+    _pnIoCm_RealIdentificationApi_Subslot =
+        new PnIoCm_RealIdentificationApi_Subslot(subslotNumber, submoduleIdentNumber);
+    return _pnIoCm_RealIdentificationApi_Subslot;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof PnIoCm_RealIdentificationApi_Subslot)) {
+      return false;
+    }
+    PnIoCm_RealIdentificationApi_Subslot that = (PnIoCm_RealIdentificationApi_Subslot) o;
+    return (getSubslotNumber() == that.getSubslotNumber())
+        && (getSubmoduleIdentNumber() == that.getSubmoduleIdentNumber())
+        && true;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(getSubslotNumber(), getSubmoduleIdentNumber());
+  }
+
+  @Override
+  public String toString() {
+    WriteBufferBoxBased writeBufferBoxBased = new WriteBufferBoxBased(true, true);
+    try {
+      writeBufferBoxBased.writeSerializable(this);
+    } catch (SerializationException e) {
+      throw new RuntimeException(e);
+    }
+    return "\n" + writeBufferBoxBased.getBox().toString() + "\n";
+  }
+}
diff --git a/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/context/ProfinetDriverContext.java b/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/context/ProfinetDriverContext.java
index 4c7bfedd79..684dd86f72 100644
--- a/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/context/ProfinetDriverContext.java
+++ b/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/context/ProfinetDriverContext.java
@@ -24,6 +24,7 @@ import org.apache.commons.codec.binary.Hex;
 import org.apache.plc4x.java.profinet.gsdml.ProfinetISO15745Profile;
 import org.apache.plc4x.java.profinet.readwrite.DceRpc_ActivityUuid;
 import org.apache.plc4x.java.profinet.readwrite.DceRpc_Packet;
+import org.apache.plc4x.java.profinet.readwrite.PnIoCm_Block_RealIdentificationData;
 import org.apache.plc4x.java.profinet.readwrite.Uuid;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.context.DriverContext;
@@ -51,6 +52,7 @@ public class ProfinetDriverContext implements DriverContext {
     private int remotePortImplicitCommunication;
 
     private ProfinetISO15745Profile deviceProfile;
+    private PnIoCm_Block_RealIdentificationData identificationData;
 
     // PN-CM Related:
     private final DceRpc_ActivityUuid activityUuid;
@@ -148,6 +150,14 @@ public class ProfinetDriverContext implements DriverContext {
         this.deviceProfile = deviceProfile;
     }
 
+    public PnIoCm_Block_RealIdentificationData getIdentificationData() {
+        return identificationData;
+    }
+
+    public void setIdentificationData(PnIoCm_Block_RealIdentificationData identificationData) {
+        this.identificationData = identificationData;
+    }
+
     public DceRpc_ActivityUuid getActivityUuid() {
         return activityUuid;
     }
diff --git a/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/packets/PnDcpPacketFactory.java b/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/packets/PnDcpPacketFactory.java
index 679ff88ef4..9a0a7dafd9 100644
--- a/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/packets/PnDcpPacketFactory.java
+++ b/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/packets/PnDcpPacketFactory.java
@@ -70,8 +70,6 @@ public class PnDcpPacketFactory {
     }
 
     public static Ethernet_Frame createReadIAndM0BlockRequest(RawSocketChannel pnChannel, ProfinetDriverContext driverContext) {
-        Random rand = new Random();
-
         InetSocketAddress localAddress = (InetSocketAddress) pnChannel.getLocalAddress();
         InetSocketAddress remoteAddress = (InetSocketAddress) pnChannel.getRemoteAddress();
 
@@ -95,6 +93,7 @@ public class PnDcpPacketFactory {
         );
 
         // Serialize it to a byte-payload
+        Random rand = new Random();
         Ethernet_FramePayload_IPv4 udpFrame = new Ethernet_FramePayload_IPv4(
             rand.nextInt(65536),
             true,
@@ -115,6 +114,7 @@ public class PnDcpPacketFactory {
     }
 
     public static CompletableFuture<PnIoCm_Block_IAndM0> sendReadIAndM0BlockRequest(ConversationContext<Ethernet_Frame> context, RawSocketChannel pnChannel, ProfinetDriverContext driverContext) {
+        // TODO: Handle error responses quickly (If the device doesn't support PN CM, then we can abort a lot quicker.
         CompletableFuture<PnIoCm_Block_IAndM0> future = new CompletableFuture<>();
         context.sendRequest(PnDcpPacketFactory.createReadIAndM0BlockRequest(pnChannel, driverContext))
             .expectResponse(Ethernet_Frame.class, Duration.ofMillis(6000))
@@ -150,4 +150,92 @@ public class PnDcpPacketFactory {
         return future;
     }
 
+    public static Ethernet_Frame createReadRealIdentificationDataRequest(RawSocketChannel pnChannel, ProfinetDriverContext driverContext) {
+        InetSocketAddress localAddress = (InetSocketAddress) pnChannel.getLocalAddress();
+        InetSocketAddress remoteAddress = (InetSocketAddress) pnChannel.getRemoteAddress();
+
+        DceRpc_Packet packet = new DceRpc_Packet(
+            DceRpc_PacketType.REQUEST, true, false, false,
+            IntegerEncoding.LITTLE_ENDIAN, CharacterEncoding.ASCII, FloatingPointEncoding.IEEE,
+            new DceRpc_ObjectUuid((byte) 0x00, 0x0001, driverContext.getDeviceId(), driverContext.getVendorId()),
+            new DceRpc_InterfaceUuid_DeviceInterface(),
+            driverContext.getActivityUuid(),
+            0,
+            driverContext.getAndIncrementIdentification(),
+            DceRpc_Operation.READ_IMPLICIT,
+            (short) 0,
+            new PnIoCm_Packet_Req(16696, 16696, 0,
+                Collections.singletonList(
+                    new IODReadRequestHeader((short) 1, (short) 0, 0,
+                        new Uuid(new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
+                        0, 0, 1, 0xF000, 16696,
+                        new Uuid(new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}))
+                ))
+        );
+
+        // Serialize it to a byte-payload
+        Random rand = new Random();
+        Ethernet_FramePayload_IPv4 udpFrame = new Ethernet_FramePayload_IPv4(
+            rand.nextInt(65536),
+            true,
+            false,
+            (short) 64,
+            new IpAddress(localAddress.getAddress().getAddress()),
+            new IpAddress(remoteAddress.getAddress().getAddress()),
+            driverContext.getLocalPort(),
+            driverContext.getRemotePortImplicitCommunication(),
+            packet
+        );
+        MacAddress srcAddress = new MacAddress(pnChannel.getLocalMacAddress().getAddress());
+        MacAddress dstAddress = new MacAddress(pnChannel.getRemoteMacAddress().getAddress());
+        return new Ethernet_Frame(
+            dstAddress,
+            srcAddress,
+            udpFrame);
+    }
+
+    public static CompletableFuture<PnIoCm_Block_RealIdentificationData> sendRealIdentificationDataRequest(ConversationContext<Ethernet_Frame> context, RawSocketChannel pnChannel, ProfinetDriverContext driverContext) {
+        CompletableFuture<PnIoCm_Block_RealIdentificationData> future = new CompletableFuture<>();
+        context.sendRequest(PnDcpPacketFactory.createReadRealIdentificationDataRequest(pnChannel, driverContext))
+            .expectResponse(Ethernet_Frame.class, Duration.ofMillis(6000))
+            .onTimeout(future::completeExceptionally)
+            .onError((ethernetFrame, throwable) -> future.completeExceptionally(throwable))
+            .unwrap(ethernetFrame -> {
+                if(ethernetFrame.getPayload() instanceof Ethernet_FramePayload_VirtualLan) {
+                    return ((Ethernet_FramePayload_VirtualLan) ethernetFrame.getPayload()).getPayload();
+                }
+                return ethernetFrame.getPayload();
+            })
+            .check(ethernetFramePayload -> ethernetFramePayload instanceof Ethernet_FramePayload_IPv4)
+            .unwrap(ethernetFramePayload -> (Ethernet_FramePayload_IPv4) ethernetFramePayload)
+            .unwrap(Ethernet_FramePayload_IPv4::getPayload)
+            .unwrap(DceRpc_Packet::getPayload)
+            .handle(dceRpcPacket -> {
+                if(dceRpcPacket instanceof PnIoCm_Packet_Rej) {
+                    future.completeExceptionally(new PlcRuntimeException("PN CM not supported"));
+                    return;
+                }
+                if(!(dceRpcPacket instanceof PnIoCm_Packet_Res)) {
+                    future.completeExceptionally(new PlcRuntimeException("Unexpected response type"));
+                    return;
+                }
+                PnIoCm_Packet_Res dceRpcPacketRes = (PnIoCm_Packet_Res) dceRpcPacket;
+                if(dceRpcPacketRes.getBlocks().size() != 2) {
+                    future.completeExceptionally(new PlcRuntimeException("Expected 2 blocks in the response"));
+                    return;
+                }
+                if(!(dceRpcPacketRes.getBlocks().get(0) instanceof IODReadResponseHeader)) {
+                    future.completeExceptionally(new PlcRuntimeException("The first block was expected to be of type IODReadResponseHeader"));
+                    return;
+                }
+                if(!(dceRpcPacketRes.getBlocks().get(1) instanceof PnIoCm_Block_RealIdentificationData)) {
+                    future.completeExceptionally(new PlcRuntimeException("The second block was expected to be of type PnIoCm_Block_RealIdentificationData"));
+                    return;
+                }
+                PnIoCm_Block_RealIdentificationData realIdentificationData = (PnIoCm_Block_RealIdentificationData) dceRpcPacketRes.getBlocks().get(1);
+                future.complete(realIdentificationData);
+            });
+        return future;
+    }
+
 }
diff --git a/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java b/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
index 92c31509c5..d95029f517 100644
--- a/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
+++ b/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
@@ -21,26 +21,29 @@ package org.apache.plc4x.java.profinet.protocol;
 
 import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
 import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.api.messages.PlcBrowseItem;
 import org.apache.plc4x.java.api.messages.PlcBrowseRequest;
 import org.apache.plc4x.java.api.messages.PlcBrowseResponse;
+import org.apache.plc4x.java.api.types.PlcResponseCode;
 import org.apache.plc4x.java.profinet.config.ProfinetConfiguration;
 import org.apache.plc4x.java.profinet.context.ProfinetDriverContext;
 import org.apache.plc4x.java.profinet.discovery.ProfinetDiscoverer;
 import org.apache.plc4x.java.profinet.gsdml.*;
 import org.apache.plc4x.java.profinet.packets.PnDcpPacketFactory;
 import org.apache.plc4x.java.profinet.readwrite.*;
+import org.apache.plc4x.java.profinet.tag.ProfinetTag;
+import org.apache.plc4x.java.profinet.utils.ProfinetDataTypeMapper;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.Plc4xProtocolBase;
 import org.apache.plc4x.java.spi.configuration.HasConfiguration;
 import org.apache.plc4x.java.spi.context.DriverContext;
+import org.apache.plc4x.java.spi.messages.DefaultPlcBrowseItem;
+import org.apache.plc4x.java.spi.messages.DefaultPlcBrowseResponse;
 import org.apache.plc4x.java.utils.rawsockets.netty.RawSocketChannel;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.CompletableFuture;
 
 public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> implements HasConfiguration<ProfinetConfiguration> {
@@ -76,7 +79,8 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> imp
         MacAddress localMacAddress = new MacAddress(rawSocketChannel.getLocalMacAddress().getAddress());
 
         // Construct and send the search request.
-        CompletableFuture<PnDcp_Pdu_IdentifyRes> future = PnDcpPacketFactory.sendIdentificationRequest(context, localMacAddress, remoteMacAddress);
+        CompletableFuture<PnDcp_Pdu_IdentifyRes> future =
+            PnDcpPacketFactory.sendIdentificationRequest(context, localMacAddress, remoteMacAddress);
         future.whenComplete((identifyRes, throwable) -> {
             if(throwable != null) {
                 logger.error("Unable to determine vendor-id or product-id, closing channel...", throwable);
@@ -96,9 +100,11 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> imp
             }
 
             // Look up the GSD file for this device ...
-            ProfinetISO15745Profile deviceProfile = configuration.getGsdProfile(driverContext.getVendorId(), driverContext.getDeviceId());
+            ProfinetISO15745Profile deviceProfile =
+                configuration.getGsdProfile(driverContext.getVendorId(), driverContext.getDeviceId());
             if (deviceProfile == null) {
-                logger.error("Unable to find GSD profile for device with vendor-id {} and device-id {}", driverContext.getVendorId(), driverContext.getDeviceId());
+                logger.error("Unable to find GSD profile for device with vendor-id {} and device-id {}",
+                    driverContext.getVendorId(), driverContext.getDeviceId());
                 context.getChannel().close();
                 return;
             }
@@ -134,8 +140,58 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> imp
             // which allows us to find out which DAP to use.
             else if(deviceProfile.getProfileBody().getApplicationProcess().getDeviceAccessPointList().size() > 1) {
                 RawSocketChannel pnChannel = ((RawSocketChannel) context.getChannel());
+                // Try to read the RealIdentificationData ...
+                CompletableFuture<PnIoCm_Block_RealIdentificationData> future1 =
+                    PnDcpPacketFactory.sendRealIdentificationDataRequest(context, pnChannel, driverContext);
+                future1.whenComplete((realIdentificationData, throwable1) -> {
+                    if(throwable1 != null) {
+                        logger.error("Unable to detect device access point, closing channel...", throwable1);
+                        context.getChannel().close();
+                        return;
+                    }
+                    driverContext.setIdentificationData(realIdentificationData);
+
+                    // Get the module identification number of slot 0 (Which is always the DAP)
+                    long dapModuleIdentificationNumber = 0;
+                    outerLoop:
+                    for (PnIoCm_RealIdentificationApi api : realIdentificationData.getApis()) {
+                        for (PnIoCm_RealIdentificationApi_Slot slot : api.getSlots()) {
+                            if(slot.getSlotNumber() == 0) {
+                                dapModuleIdentificationNumber = slot.getModuleIdentNumber();
+                                break outerLoop;
+                            }
+                        }
+                    }
+                    if(dapModuleIdentificationNumber == 0){
+                        logger.error("Unable to detect device access point, closing channel...", throwable1);
+                        context.getChannel().close();
+                        return;
+                    }
+
+                    // Iterate through all available DAPs and find the one with the matching module ident number.
+                    for (ProfinetDeviceAccessPointItem curDap : deviceProfile.getProfileBody().getApplicationProcess().getDeviceAccessPointList()) {
+                        String moduleIdentNumberStr = curDap.getModuleIdentNumber();
+                        if(moduleIdentNumberStr.startsWith("0x") || moduleIdentNumberStr.startsWith("0X")) {
+                            moduleIdentNumberStr = moduleIdentNumberStr.substring(2);
+                        }
+                        long moduleIdentNumber = Long.parseLong(moduleIdentNumberStr, 16);
+                        if(moduleIdentNumber == dapModuleIdentificationNumber) {
+                            driverContext.setDapId(curDap.getId());
+                            break;
+                        }
+                    }
+                    // Abort, if we weren't able to detect a DAP.
+                    if(driverContext.getDapId() == null) {
+                        logger.error("Unable to auto-detect the device access point, closing channel...");
+                        context.getChannel().close();
+                        return;
+                    }
+
+                    context.fireConnected();
+                });
                 // Try to read the I&M0 block
-                CompletableFuture<PnIoCm_Block_IAndM0> pnIoCmBlockIAndM0CompletableFuture = PnDcpPacketFactory.sendReadIAndM0BlockRequest(context, pnChannel, driverContext);
+                // * Commented out this code, as here it performed the same task as the ReadRealIdentificationData approach and If the other has side-effects, I can roll-back to this.
+                /*CompletableFuture<PnIoCm_Block_IAndM0> pnIoCmBlockIAndM0CompletableFuture = PnDcpPacketFactory.sendReadIAndM0BlockRequest(context, pnChannel, driverContext);
                 pnIoCmBlockIAndM0CompletableFuture.whenComplete((pnIoCmBlockIAndM0, throwable1) -> {
                     if(throwable1 != null) {
                         logger.error("Unable to detect device access point, closing channel...", throwable1);
@@ -164,16 +220,20 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> imp
                     if(driverContext.getDapId() == null) {
                         logger.error("Unable to auto-detect the device access point, closing channel...");
                         context.getChannel().close();
-                    } else {
-                        context.fireConnected();
+                        return;
                     }
-                });
+
+                    context.fireConnected();
+                });*/
             }
+
             // If the current device only has one DAP (like most devices), simply use that.
             else if (deviceProfile.getProfileBody().getApplicationProcess().getDeviceAccessPointList().size() == 1) {
                 driverContext.setDapId(deviceProfile.getProfileBody().getApplicationProcess().getDeviceAccessPointList().get(0).getId());
                 context.fireConnected();
-            } else {
+            }
+
+            else {
                 logger.error("GSD descriptor doesn't contain any device access points");
                 context.getChannel().close();
             }
@@ -190,18 +250,109 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> imp
         if (driverContext.getDeviceProfile() == null) {
             return CompletableFuture.failedFuture(new PlcConnectionException("Unable to find GSD file for given device"));
         }
+        if (driverContext.getDapId() == null) {
+            return CompletableFuture.failedFuture(new PlcConnectionException("DAP not set"));
+        }
 
         ProfinetISO15745Profile deviceProfile = driverContext.getDeviceProfile();
-        for (ProfinetModuleItem module : deviceProfile.getProfileBody().getApplicationProcess().getModuleList()) {
-            for (ProfinetVirtualSubmoduleItem submodule : module.getVirtualSubmoduleList()) {
 
+        // Build an index of all modules.
+        Map<Long, ProfinetModuleItem> moduleMap = new HashMap<>();
+        for (ProfinetModuleItem profinetModuleItem : deviceProfile.getProfileBody().getApplicationProcess().getModuleList()) {
+            String moduleIdentNumberString = profinetModuleItem.getModuleIdentNumber();
+            if(moduleIdentNumberString.startsWith("0x") || moduleIdentNumberString.startsWith("0X")) {
+                moduleIdentNumberString = moduleIdentNumberString.substring(2);
             }
+            long moduleIdentNumber = Long.parseLong(moduleIdentNumberString,16);
+            moduleMap.put(moduleIdentNumber, profinetModuleItem);
+        }
+
+        // Build an index of the String names.
+        Map<String, String> textMapping = new HashMap<>();
+        for (ProfinetTextIdValue profinetTextIdValue : deviceProfile.getProfileBody().getApplicationProcess().getExternalTextList().getPrimaryLanguage().getText()) {
+            textMapping.put(profinetTextIdValue.getTextId(), profinetTextIdValue.getValue());
         }
-        return super.browse(browseRequest);
-    }
 
+        Map<String, PlcResponseCode> responseCodes = new HashMap<>();
+        Map<String, List<PlcBrowseItem>> values = new HashMap<>();
+        for (String queryName : browseRequest.getQueryNames()) {
+            List<PlcBrowseItem> items = new ArrayList<>();
+            for (PnIoCm_RealIdentificationApi api : driverContext.getIdentificationData().getApis()) {
+                for (PnIoCm_RealIdentificationApi_Slot slot : api.getSlots()) {
+                    // Slot 0 is always the DAP module, I haven't come across any DataItems here ...
+                    if(slot.getSlotNumber() == 0) {
+                        continue;
+                    }
 
+                    // Find the matching module.
+                    long moduleIdentNumber = slot.getModuleIdentNumber();
+                    ProfinetModuleItem slotModule = moduleMap.get(moduleIdentNumber);
+                    if(slotModule == null) {
+                        return CompletableFuture.failedFuture(new PlcRuntimeException(
+                            "Module with ident number " + moduleIdentNumber + " not found in GSD."));
+                    }
 
+                    for (PnIoCm_RealIdentificationApi_Subslot subslot : slot.getSubslots()) {
+                        // Find the submodule
+                        ProfinetVirtualSubmoduleItem subSlotSubModule = null;
+                        String moduleIdentNumberHex = String.format("0x%08X", subslot.getSubmoduleIdentNumber());
+                        for (ProfinetVirtualSubmoduleItem virtualSubmoduleItem : slotModule.getVirtualSubmoduleList()) {
+                            if (virtualSubmoduleItem.getSubmoduleIdentNumber().equalsIgnoreCase(moduleIdentNumberHex)) {
+                                subSlotSubModule = virtualSubmoduleItem;
+                                break;
+                            }
+                        }
+                        if (subSlotSubModule == null) {
+                            return CompletableFuture.failedFuture(new PlcRuntimeException(
+                                "SubModule with ident number " + subslot.getSubmoduleIdentNumber() + " not found in GSD."));
+                       }
+
+                        // Add all the input tags.
+                        for (ProfinetIoDataInput profinetIoDataInput : subSlotSubModule.getIoData().getInput()) {
+                            for (int i = 0; i < profinetIoDataInput.getDataItemList().size(); i++) {
+                                ProfinetDataItem profinetDataItem = profinetIoDataInput.getDataItemList().get(i);
+                                ProfinetDataTypeMapper.DataTypeInformation dataTypeInformation =
+                                    ProfinetDataTypeMapper.getPlcValueType(profinetDataItem);
+                                String name = profinetDataItem.getTextId();
+                                // Try to replace the text id with a meaningful value.
+                                if (textMapping.containsKey(name)) {
+                                    name = textMapping.get(name);
+                                }
+                                items.add(new DefaultPlcBrowseItem(new ProfinetTag(
+                                    slot.getSlotNumber(), subslot.getSubslotNumber(), ProfinetTag.Direction.INPUT,
+                                    i, dataTypeInformation.getPlcValueType(), dataTypeInformation.getNumElements()),
+                                    name, false, true, true,
+                                    Collections.emptyMap(), Collections.emptyMap()));
+                            }
+                        }
+
+                        // Add all the output tags.
+                        for (ProfinetIoDataOutput profinetIoDataOutput : subSlotSubModule.getIoData().getOutput()) {
+                            for (int i = 0; i < profinetIoDataOutput.getDataItemList().size(); i++) {
+                                ProfinetDataItem profinetDataItem = profinetIoDataOutput.getDataItemList().get(i);
+                                ProfinetDataTypeMapper.DataTypeInformation dataTypeInformation =
+                                    ProfinetDataTypeMapper.getPlcValueType(profinetDataItem);
+                                String name = profinetDataItem.getTextId();
+                                // Try to replace the text id with a meaningful value.
+                                if (textMapping.containsKey(name)) {
+                                    name = textMapping.get(name);
+                                }
+                                items.add(new DefaultPlcBrowseItem(new ProfinetTag(
+                                    slot.getSlotNumber(), subslot.getSubslotNumber(), ProfinetTag.Direction.OUTPUT,
+                                    i, dataTypeInformation.getPlcValueType(), dataTypeInformation.getNumElements()),
+                                    name, false, true, true,
+                                    Collections.emptyMap(), Collections.emptyMap()));
+                            }
+                        }
+                    }
+                }
+            }
+            responseCodes.put(queryName, PlcResponseCode.OK);
+            values.put(queryName, items);
+        }
+        PlcBrowseResponse response = new DefaultPlcBrowseResponse(browseRequest, responseCodes, values);
+        return CompletableFuture.completedFuture(response);
+    }
 
     protected void extractBlockInfo(List<PnDcp_Block> blocks) {
         // Index the blocks of the response
@@ -212,17 +363,20 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> imp
         }
 
         if (blockMap.containsKey(ProfinetDiscoverer.DEVICE_TYPE_NAME)) {
-            PnDcp_Block_DevicePropertiesDeviceVendor block = (PnDcp_Block_DevicePropertiesDeviceVendor) blockMap.get(ProfinetDiscoverer.DEVICE_TYPE_NAME);
+            PnDcp_Block_DevicePropertiesDeviceVendor block =
+                (PnDcp_Block_DevicePropertiesDeviceVendor) blockMap.get(ProfinetDiscoverer.DEVICE_TYPE_NAME);
             driverContext.setDeviceType(new String(block.getDeviceVendorValue()));
         }
 
         if (blockMap.containsKey(ProfinetDiscoverer.DEVICE_NAME_OF_STATION)) {
-            PnDcp_Block_DevicePropertiesNameOfStation block = (PnDcp_Block_DevicePropertiesNameOfStation) blockMap.get(ProfinetDiscoverer.DEVICE_NAME_OF_STATION);
+            PnDcp_Block_DevicePropertiesNameOfStation block =
+                (PnDcp_Block_DevicePropertiesNameOfStation) blockMap.get(ProfinetDiscoverer.DEVICE_NAME_OF_STATION);
             driverContext.setDeviceName(new String(block.getNameOfStation()));
         }
 
         if (blockMap.containsKey(ProfinetDiscoverer.DEVICE_ROLE)) {
-            PnDcp_Block_DevicePropertiesDeviceRole block = (PnDcp_Block_DevicePropertiesDeviceRole) blockMap.get(ProfinetDiscoverer.DEVICE_ROLE);
+            PnDcp_Block_DevicePropertiesDeviceRole block =
+                (PnDcp_Block_DevicePropertiesDeviceRole) blockMap.get(ProfinetDiscoverer.DEVICE_ROLE);
             List<String> roles = new ArrayList<>();
             if (block.getPnioSupervisor()) {
                 roles.add("SUPERVISOR");
@@ -240,7 +394,8 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> imp
         }
 
         if (blockMap.containsKey(ProfinetDiscoverer.DEVICE_ID)) {
-            PnDcp_Block_DevicePropertiesDeviceId block = (PnDcp_Block_DevicePropertiesDeviceId) blockMap.get(ProfinetDiscoverer.DEVICE_ID);
+            PnDcp_Block_DevicePropertiesDeviceId block =
+                (PnDcp_Block_DevicePropertiesDeviceId) blockMap.get(ProfinetDiscoverer.DEVICE_ID);
             driverContext.setVendorId(block.getVendorId());
             driverContext.setDeviceId(block.getDeviceId());
         }
diff --git a/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/tag/ProfinetTag.java b/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/tag/ProfinetTag.java
index c7dd3f40ed..94152631fc 100644
--- a/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/tag/ProfinetTag.java
+++ b/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/tag/ProfinetTag.java
@@ -29,18 +29,24 @@ import java.util.regex.Pattern;
 
 public class ProfinetTag implements PlcTag {
 
-    public static final Pattern ADDRESS_PATTERN = Pattern.compile("(?<address>[\\w\\-. ]+)(:(?<datatype>[a-zA-Z_]+)){1}(\\[(?<quantity>\\d+)])?");
-    private final String address;
-    private final int quantity;
+    public static final Pattern ADDRESS_PATTERN = Pattern.compile("(?<slot>\\d+).(?<subSlot>\\d+).(?<direction>INPUT|OUTPUT)(.(?<index>\\d+))?:(?<dataType>[a-zA-Z_]+)(\\[(?<numElements>\\d+)])?");
+    private final int slot;
+    private final int subSlot;
+    private final Direction direction;
+    private final int index;
     private final PlcValueType dataType;
+    private final int numElements;
 
-    protected ProfinetTag(String address, Integer quantity, PlcValueType dataType) {
-        this.address = address;
-        this.quantity = (quantity != null) ? quantity : 1;
-        if (this.quantity <= 0) {
-            throw new IllegalArgumentException("quantity must be greater than zero. Was " + this.quantity);
-        }
+    public ProfinetTag(int slot, int subSlot, Direction direction, int index, PlcValueType dataType, int numElements) {
+        this.slot = slot;
+        this.subSlot = subSlot;
+        this.direction = direction;
+        this.index = index;
         this.dataType = dataType;
+        this.numElements = numElements;
+        if (this.numElements <= 0) {
+            throw new IllegalArgumentException("numElements must be greater than zero. Was " + this.numElements);
+        }
     }
 
     public static ProfinetTag of(String addressString) {
@@ -49,15 +55,43 @@ public class ProfinetTag implements PlcTag {
             throw new PlcInvalidTagException(addressString, ADDRESS_PATTERN);
         }
 
-        String quantity = matcher.group("quantity") == null ? "1" :  matcher.group("quantity");
-        PlcValueType plcValueType = PlcValueType.valueOf(matcher.group("datatype"));
+        int slot = Integer.parseInt(matcher.group("slot"));
+        int subSlot = Integer.parseInt(matcher.group("subSlot"));
+        Direction direction = Direction.valueOf(matcher.group("direction"));
+        int index = Integer.parseInt(matcher.group("index"));
+        PlcValueType dataType = PlcValueType.valueOf(matcher.group("dataType"));
+        int numElements = (matcher.group("numElements") != null) ? Integer.parseInt(matcher.group("numElements")) : 1;
+
+        return new ProfinetTag(slot, subSlot, direction, index, dataType, numElements);
+    }
+
+    public int getSlot() {
+        return slot;
+    }
+
+    public int getSubSlot() {
+        return subSlot;
+    }
+
+    public Direction getDirection() {
+        return direction;
+    }
 
-        return new ProfinetTag(matcher.group("address"), Integer.parseInt(quantity), plcValueType);
+    public int getIndex() {
+        return index;
+    }
+
+    public PlcValueType getDataType() {
+        return dataType;
+    }
+
+    public int getNumElements() {
+        return numElements;
     }
 
     @Override
     public String getAddressString() {
-        return address;
+        return String.format("%d.%d.%s.%d:%s%s", slot, subSlot, direction, index, dataType, (numElements > 1) ? "[" + numElements + "]" : "");
     }
 
     @Override
@@ -69,4 +103,10 @@ public class ProfinetTag implements PlcTag {
     public List<ArrayInfo> getArrayInfo() {
         return PlcTag.super.getArrayInfo();
     }
+
+    public static enum Direction {
+        INPUT,
+        OUTPUT
+    }
+
 }
diff --git a/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/tag/ProfinetTagHandler.java b/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/tag/ProfinetTagHandler.java
index bea7f3eb54..e160945b0c 100644
--- a/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/tag/ProfinetTagHandler.java
+++ b/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/tag/ProfinetTagHandler.java
@@ -26,7 +26,7 @@ public class ProfinetTagHandler implements PlcTagHandler {
 
     @Override
     public PlcTag parseTag(String tagAddress) {
-        return null;
+        return ProfinetTag.of(tagAddress);
     }
 
     @Override
diff --git a/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/utils/ProfinetDataTypeMapper.java b/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/utils/ProfinetDataTypeMapper.java
new file mode 100644
index 0000000000..bb420b3760
--- /dev/null
+++ b/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/utils/ProfinetDataTypeMapper.java
@@ -0,0 +1,125 @@
+/*
+ * 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
+ *
+ *   http://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.profinet.utils;
+
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.api.types.PlcValueType;
+import org.apache.plc4x.java.profinet.gsdml.ProfinetDataItem;
+
+public class ProfinetDataTypeMapper {
+
+    public static class DataTypeInformation {
+        private final PlcValueType plcValueType;
+        private final int numElements;
+
+        public DataTypeInformation(PlcValueType plcValueType, int numElements) {
+            this.plcValueType = plcValueType;
+            this.numElements = numElements;
+        }
+
+        public PlcValueType getPlcValueType() {
+            return plcValueType;
+        }
+
+        public int getNumElements() {
+            return numElements;
+        }
+    }
+
+    public static DataTypeInformation getPlcValueType(ProfinetDataItem dataItem) {
+        if(dataItem.isUseAsBits()) {
+            switch (dataItem.getDataType()) {
+                case "Integer8":
+                case "Unsigned8":
+                    return new DataTypeInformation(PlcValueType.BOOL, 8);
+                case "Integer16":
+                case "Unsigned16":
+                    return new DataTypeInformation(PlcValueType.BOOL, 16);
+                case "Integer32":
+                case "Unsigned32":
+                    return new DataTypeInformation(PlcValueType.BOOL, 32);
+                case "Integer64":
+                case "Unsigned64":
+                    return new DataTypeInformation(PlcValueType.BOOL, 64);
+                case "Float32":
+                case "Float64":
+                case "Date":
+                case "TimeOfDay with date indication":
+                case "TimeOfDay without date indication":
+                case "TimeDifference with date indication":
+                case "TimeDifference without date indication":
+                case "NetworkTime":
+                case "NetworkTimeDifference":
+                case "VisibleString":
+                    break;
+                case "OctetString":
+                    return new DataTypeInformation(PlcValueType.BOOL, dataItem.getLength());
+                case "Unsigned8+Unsigned8":
+                case "Float32+Unsigned8":
+                case "Float32+Status8":
+                case "F_MessageTrailer4Byte":
+                case "F_MessageTrailer5Byte":
+            }
+        }
+        switch (dataItem.getDataType()) {
+            case "Integer8":
+                return new DataTypeInformation(PlcValueType.SINT, 1);
+            case "Integer16":
+                return new DataTypeInformation(PlcValueType.INT, 1);
+            case "Integer32":
+                return new DataTypeInformation(PlcValueType.DINT, 1);
+            case "Integer64":
+                return new DataTypeInformation(PlcValueType.LINT, 1);
+            case "Unsigned8":
+                return new DataTypeInformation(PlcValueType.USINT, 1);
+            case "Unsigned16":
+                return new DataTypeInformation(PlcValueType.UINT, 1);
+            case "Unsigned32":
+                return new DataTypeInformation(PlcValueType.UDINT, 1);
+            case "Unsigned64":
+                return new DataTypeInformation(PlcValueType.ULINT, 1);
+            case "Float32":
+                return new DataTypeInformation(PlcValueType.REAL, 1);
+            case "Float64":
+                return new DataTypeInformation(PlcValueType.LREAL, 1);
+            case "Date":
+            case "TimeOfDay with date indication":
+            case "TimeOfDay without date indication":
+            case "TimeDifference with date indication":
+            case "TimeDifference without date indication":
+            case "NetworkTime":
+            case "NetworkTimeDifference":
+                break;
+            case "VisibleString":
+                return new DataTypeInformation(PlcValueType.STRING, 1);
+            case "OctetString":
+                // This should actually never happen, as the OctetString should always be used with "useAsBits".
+                return new DataTypeInformation(PlcValueType.BOOL, dataItem.getLength());
+            case "Unsigned8+Unsigned8":
+            case "Float32+Unsigned8":
+            case "Float32+Status8":
+            case "F_MessageTrailer4Byte":
+            case "F_MessageTrailer5Byte":
+        }
+
+        throw new PlcRuntimeException("Unable to find PlcValueType for dataType " + dataItem.getDataType());
+    }
+
+}
diff --git a/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/tag/ProfinetTagHandler.java b/plc4j/drivers/profinet-ng/src/test/java/org/apache/plc4x/java/profinet/ManualParserTest.java
similarity index 52%
copy from plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/tag/ProfinetTagHandler.java
copy to plc4j/drivers/profinet-ng/src/test/java/org/apache/plc4x/java/profinet/ManualParserTest.java
index bea7f3eb54..525b1ffc2c 100644
--- a/plc4j/drivers/profinet-ng/src/main/java/org/apache/plc4x/java/profinet/tag/ProfinetTagHandler.java
+++ b/plc4j/drivers/profinet-ng/src/test/java/org/apache/plc4x/java/profinet/ManualParserTest.java
@@ -7,7 +7,7 @@
  * "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
+ *   http://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
@@ -16,22 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.plc4x.java.profinet.tag;
 
-import org.apache.plc4x.java.api.model.PlcQuery;
-import org.apache.plc4x.java.api.model.PlcTag;
-import org.apache.plc4x.java.spi.connection.PlcTagHandler;
+package org.apache.plc4x.java.profinet;
 
-public class ProfinetTagHandler implements PlcTagHandler {
-
-    @Override
-    public PlcTag parseTag(String tagAddress) {
-        return null;
-    }
-
-    @Override
-    public PlcQuery parseQuery(String query) {
-        return null;//new ProfinetPlcQuery(query);
+public class ManualParserTest {
+    public static void main(String[] args) {
+        //f8e43bb69bbf883f990006ef0800450000fc7f2b00001e116a7ac0a8181fc0a818dcc006889400e858e804020a00100000000000a0de976cd111827100010904002a0100a0de976cd111827100a02442df7d19afcce656869149af7d30641c9b3ad2a500000001000000020000000500ffffffff900000000000000000007c00000038410000000000007c0000008009003c010000000000000000000000000000000000000000000000000000010000f0000000003c0000000000000000000000000000000000000000000000000013003801010001000000000002000000000010000400010000000180000000000280 [...]
     }
-
 }
diff --git a/plc4j/drivers/profinet-ng/src/test/java/org/apache/plc4x/java/profinet/ManualProfinetIoTest.java b/plc4j/drivers/profinet-ng/src/test/java/org/apache/plc4x/java/profinet/ManualProfinetIoTest.java
index 32828807f3..59cdd1643a 100644
--- a/plc4j/drivers/profinet-ng/src/test/java/org/apache/plc4x/java/profinet/ManualProfinetIoTest.java
+++ b/plc4j/drivers/profinet-ng/src/test/java/org/apache/plc4x/java/profinet/ManualProfinetIoTest.java
@@ -21,17 +21,26 @@ package org.apache.plc4x.java.profinet;
 
 import org.apache.plc4x.java.DefaultPlcDriverManager;
 import org.apache.plc4x.java.api.PlcConnection;
+import org.apache.plc4x.java.api.messages.PlcBrowseItem;
 import org.apache.plc4x.java.api.messages.PlcBrowseRequest;
 import org.apache.plc4x.java.api.messages.PlcBrowseResponse;
 
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 public class ManualProfinetIoTest {
 
     public static void main(String[] args) throws Exception {
+        //try(PlcConnection connection =  new DefaultPlcDriverManager().getConnection("profinet:raw://192.168.24.41")) {
         try(PlcConnection connection =  new DefaultPlcDriverManager().getConnection("profinet:raw://192.168.24.31")) {
             PlcBrowseRequest browseRequest = connection.browseRequestBuilder().addQuery("all", "*").build();
-            Thread.sleep(10000);
+            PlcBrowseResponse plcBrowseResponse = browseRequest.execute().get();
+            for (String queryName : plcBrowseResponse.getQueryNames()) {
+                List<PlcBrowseItem> values = plcBrowseResponse.getValues(queryName);
+                for (PlcBrowseItem value : values) {
+                    System.out.println(value.getName() + ": " + value.getTag().getAddressString());
+                }
+            }
             /*PlcBrowseResponse plcBrowseResponse = browseRequest.execute().get(30000, TimeUnit.MILLISECONDS);
             System.out.println(plcBrowseResponse);*/
         }
diff --git a/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block.java b/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block.java
index 6d70c8e103..e4bf5c8538 100644
--- a/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block.java
+++ b/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block.java
@@ -147,6 +147,8 @@ public abstract class PnIoCm_Block implements Message {
       builder = PnIoCm_Block_ModuleDiff.staticParsePnIoCm_BlockBuilder(readBuffer);
     } else if (EvaluationHelper.equals(blockType, PnIoCm_BlockType.AR_SERVER_BLOCK)) {
       builder = PnIoCm_Block_ArServer.staticParsePnIoCm_BlockBuilder(readBuffer);
+    } else if (EvaluationHelper.equals(blockType, PnIoCm_BlockType.REAL_IDENTIFICATION_DATA)) {
+      builder = PnIoCm_Block_RealIdentificationData.staticParsePnIoCm_BlockBuilder(readBuffer);
     } else if (EvaluationHelper.equals(blockType, PnIoCm_BlockType.I_AND_M_0)) {
       builder = PnIoCm_Block_IAndM0.staticParsePnIoCm_BlockBuilder(readBuffer);
     } else if (EvaluationHelper.equals(blockType, PnIoCm_BlockType.I_AND_M_1)) {
diff --git a/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_ModuleDiff.java b/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_ModuleDiff.java
index dbe6423401..082fedbf8a 100644
--- a/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_ModuleDiff.java
+++ b/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_ModuleDiff.java
@@ -96,12 +96,12 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
         writeUnsignedShort(writeBuffer, 8),
         WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
-    // Implicit Field (numberOfApis) (Used for parsing, but its value is not stored as it's
-    // implicitly given by the objects content)
-    int numberOfApis = (int) (COUNT(getApis()));
+    // Implicit Field (numApis) (Used for parsing, but its value is not stored as it's implicitly
+    // given by the objects content)
+    int numApis = (int) (COUNT(getApis()));
     writeImplicitField(
-        "numberOfApis",
-        numberOfApis,
+        "numApis",
+        numApis,
         writeUnsignedInt(writeBuffer, 16),
         WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
@@ -132,7 +132,7 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
     // Simple field (blockVersionLow)
     lengthInBits += 8;
 
-    // Implicit Field (numberOfApis)
+    // Implicit Field (numApis)
     lengthInBits += 16;
 
     // Array field
@@ -171,9 +171,9 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
             readUnsignedShort(readBuffer, 8),
             WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
-    int numberOfApis =
+    int numApis =
         readImplicitField(
-            "numberOfApis",
+            "numApis",
             readUnsignedInt(readBuffer, 16),
             WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
@@ -182,7 +182,7 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
             "apis",
             new DataReaderComplexDefault<>(
                 () -> PnIoCm_ModuleDiffBlockApi.staticParse(readBuffer), readBuffer),
-            numberOfApis,
+            numApis,
             WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
     readBuffer.closeContext("PnIoCm_Block_ModuleDiff");
diff --git a/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_ModuleDiff.java b/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_RealIdentificationData.java
similarity index 77%
copy from plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_ModuleDiff.java
copy to plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_RealIdentificationData.java
index dbe6423401..c8a51fc8d7 100644
--- a/plc4j/drivers/profinet-ng/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_ModuleDiff.java
+++ b/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_Block_RealIdentificationData.java
@@ -35,20 +35,20 @@ import org.apache.plc4x.java.spi.generation.*;
 
 // Code generated by code-generation. DO NOT EDIT.
 
-public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
+public class PnIoCm_Block_RealIdentificationData extends PnIoCm_Block implements Message {
 
   // Accessors for discriminator values.
   public PnIoCm_BlockType getBlockType() {
-    return PnIoCm_BlockType.MODULE_DIFF_BLOCK;
+    return PnIoCm_BlockType.REAL_IDENTIFICATION_DATA;
   }
 
   // Properties.
   protected final short blockVersionHigh;
   protected final short blockVersionLow;
-  protected final List<PnIoCm_ModuleDiffBlockApi> apis;
+  protected final List<PnIoCm_RealIdentificationApi> apis;
 
-  public PnIoCm_Block_ModuleDiff(
-      short blockVersionHigh, short blockVersionLow, List<PnIoCm_ModuleDiffBlockApi> apis) {
+  public PnIoCm_Block_RealIdentificationData(
+      short blockVersionHigh, short blockVersionLow, List<PnIoCm_RealIdentificationApi> apis) {
     super();
     this.blockVersionHigh = blockVersionHigh;
     this.blockVersionLow = blockVersionLow;
@@ -63,7 +63,7 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
     return blockVersionLow;
   }
 
-  public List<PnIoCm_ModuleDiffBlockApi> getApis() {
+  public List<PnIoCm_RealIdentificationApi> getApis() {
     return apis;
   }
 
@@ -71,7 +71,7 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
   protected void serializePnIoCm_BlockChild(WriteBuffer writeBuffer) throws SerializationException {
     PositionAware positionAware = writeBuffer;
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
-    writeBuffer.pushContext("PnIoCm_Block_ModuleDiff");
+    writeBuffer.pushContext("PnIoCm_Block_RealIdentificationData");
 
     // Implicit Field (blockLength) (Used for parsing, but its value is not stored as it's
     // implicitly given by the objects content)
@@ -96,12 +96,12 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
         writeUnsignedShort(writeBuffer, 8),
         WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
-    // Implicit Field (numberOfApis) (Used for parsing, but its value is not stored as it's
-    // implicitly given by the objects content)
-    int numberOfApis = (int) (COUNT(getApis()));
+    // Implicit Field (numApis) (Used for parsing, but its value is not stored as it's implicitly
+    // given by the objects content)
+    int numApis = (int) (COUNT(getApis()));
     writeImplicitField(
-        "numberOfApis",
-        numberOfApis,
+        "numApis",
+        numApis,
         writeUnsignedInt(writeBuffer, 16),
         WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
@@ -109,7 +109,7 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
     writeComplexTypeArrayField(
         "apis", apis, writeBuffer, WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
-    writeBuffer.popContext("PnIoCm_Block_ModuleDiff");
+    writeBuffer.popContext("PnIoCm_Block_RealIdentificationData");
   }
 
   @Override
@@ -120,7 +120,7 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
   @Override
   public int getLengthInBits() {
     int lengthInBits = super.getLengthInBits();
-    PnIoCm_Block_ModuleDiff _value = this;
+    PnIoCm_Block_RealIdentificationData _value = this;
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
 
     // Implicit Field (blockLength)
@@ -132,13 +132,13 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
     // Simple field (blockVersionLow)
     lengthInBits += 8;
 
-    // Implicit Field (numberOfApis)
+    // Implicit Field (numApis)
     lengthInBits += 16;
 
     // Array field
     if (apis != null) {
       int i = 0;
-      for (PnIoCm_ModuleDiffBlockApi element : apis) {
+      for (PnIoCm_RealIdentificationApi element : apis) {
         ThreadLocalHelper.lastItemThreadLocal.set(++i >= apis.size());
         lengthInBits += element.getLengthInBits();
       }
@@ -149,7 +149,7 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
 
   public static PnIoCm_BlockBuilder staticParsePnIoCm_BlockBuilder(ReadBuffer readBuffer)
       throws ParseException {
-    readBuffer.pullContext("PnIoCm_Block_ModuleDiff");
+    readBuffer.pullContext("PnIoCm_Block_RealIdentificationData");
     PositionAware positionAware = readBuffer;
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
 
@@ -171,42 +171,43 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
             readUnsignedShort(readBuffer, 8),
             WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
-    int numberOfApis =
+    int numApis =
         readImplicitField(
-            "numberOfApis",
+            "numApis",
             readUnsignedInt(readBuffer, 16),
             WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
-    List<PnIoCm_ModuleDiffBlockApi> apis =
+    List<PnIoCm_RealIdentificationApi> apis =
         readCountArrayField(
             "apis",
             new DataReaderComplexDefault<>(
-                () -> PnIoCm_ModuleDiffBlockApi.staticParse(readBuffer), readBuffer),
-            numberOfApis,
+                () -> PnIoCm_RealIdentificationApi.staticParse(readBuffer), readBuffer),
+            numApis,
             WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
 
-    readBuffer.closeContext("PnIoCm_Block_ModuleDiff");
+    readBuffer.closeContext("PnIoCm_Block_RealIdentificationData");
     // Create the instance
-    return new PnIoCm_Block_ModuleDiffBuilderImpl(blockVersionHigh, blockVersionLow, apis);
+    return new PnIoCm_Block_RealIdentificationDataBuilderImpl(
+        blockVersionHigh, blockVersionLow, apis);
   }
 
-  public static class PnIoCm_Block_ModuleDiffBuilderImpl
+  public static class PnIoCm_Block_RealIdentificationDataBuilderImpl
       implements PnIoCm_Block.PnIoCm_BlockBuilder {
     private final short blockVersionHigh;
     private final short blockVersionLow;
-    private final List<PnIoCm_ModuleDiffBlockApi> apis;
+    private final List<PnIoCm_RealIdentificationApi> apis;
 
-    public PnIoCm_Block_ModuleDiffBuilderImpl(
-        short blockVersionHigh, short blockVersionLow, List<PnIoCm_ModuleDiffBlockApi> apis) {
+    public PnIoCm_Block_RealIdentificationDataBuilderImpl(
+        short blockVersionHigh, short blockVersionLow, List<PnIoCm_RealIdentificationApi> apis) {
       this.blockVersionHigh = blockVersionHigh;
       this.blockVersionLow = blockVersionLow;
       this.apis = apis;
     }
 
-    public PnIoCm_Block_ModuleDiff build() {
-      PnIoCm_Block_ModuleDiff pnIoCm_Block_ModuleDiff =
-          new PnIoCm_Block_ModuleDiff(blockVersionHigh, blockVersionLow, apis);
-      return pnIoCm_Block_ModuleDiff;
+    public PnIoCm_Block_RealIdentificationData build() {
+      PnIoCm_Block_RealIdentificationData pnIoCm_Block_RealIdentificationData =
+          new PnIoCm_Block_RealIdentificationData(blockVersionHigh, blockVersionLow, apis);
+      return pnIoCm_Block_RealIdentificationData;
     }
   }
 
@@ -215,10 +216,10 @@ public class PnIoCm_Block_ModuleDiff extends PnIoCm_Block implements Message {
     if (this == o) {
       return true;
     }
-    if (!(o instanceof PnIoCm_Block_ModuleDiff)) {
+    if (!(o instanceof PnIoCm_Block_RealIdentificationData)) {
       return false;
     }
-    PnIoCm_Block_ModuleDiff that = (PnIoCm_Block_ModuleDiff) o;
+    PnIoCm_Block_RealIdentificationData that = (PnIoCm_Block_RealIdentificationData) o;
     return (getBlockVersionHigh() == that.getBlockVersionHigh())
         && (getBlockVersionLow() == that.getBlockVersionLow())
         && (getApis() == that.getApis())
diff --git a/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi.java b/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi.java
new file mode 100644
index 0000000000..a9da45154a
--- /dev/null
+++ b/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi.java
@@ -0,0 +1,183 @@
+/*
+ * 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.profinet.readwrite;
+
+import static org.apache.plc4x.java.spi.codegen.fields.FieldReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.fields.FieldWriterFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataWriterFactory.*;
+import static org.apache.plc4x.java.spi.generation.StaticHelper.*;
+
+import java.time.*;
+import java.util.*;
+import org.apache.plc4x.java.api.exceptions.*;
+import org.apache.plc4x.java.api.value.*;
+import org.apache.plc4x.java.spi.codegen.*;
+import org.apache.plc4x.java.spi.codegen.fields.*;
+import org.apache.plc4x.java.spi.codegen.io.*;
+import org.apache.plc4x.java.spi.generation.*;
+
+// Code generated by code-generation. DO NOT EDIT.
+
+public class PnIoCm_RealIdentificationApi implements Message {
+
+  // Constant values.
+  public static final Long API = 0x00000000L;
+
+  // Properties.
+  protected final List<PnIoCm_RealIdentificationApi_Slot> slots;
+
+  public PnIoCm_RealIdentificationApi(List<PnIoCm_RealIdentificationApi_Slot> slots) {
+    super();
+    this.slots = slots;
+  }
+
+  public List<PnIoCm_RealIdentificationApi_Slot> getSlots() {
+    return slots;
+  }
+
+  public long getApi() {
+    return API;
+  }
+
+  public void serialize(WriteBuffer writeBuffer) throws SerializationException {
+    PositionAware positionAware = writeBuffer;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+    writeBuffer.pushContext("PnIoCm_RealIdentificationApi");
+
+    // Const Field (api)
+    writeConstField(
+        "api",
+        API,
+        writeUnsignedLong(writeBuffer, 32),
+        WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    // Implicit Field (numSlots) (Used for parsing, but its value is not stored as it's implicitly
+    // given by the objects content)
+    int numSlots = (int) (COUNT(getSlots()));
+    writeImplicitField(
+        "numSlots",
+        numSlots,
+        writeUnsignedInt(writeBuffer, 16),
+        WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    // Array Field (slots)
+    writeComplexTypeArrayField(
+        "slots", slots, writeBuffer, WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    writeBuffer.popContext("PnIoCm_RealIdentificationApi");
+  }
+
+  @Override
+  public int getLengthInBytes() {
+    return (int) Math.ceil((float) getLengthInBits() / 8.0);
+  }
+
+  @Override
+  public int getLengthInBits() {
+    int lengthInBits = 0;
+    PnIoCm_RealIdentificationApi _value = this;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+    // Const Field (api)
+    lengthInBits += 32;
+
+    // Implicit Field (numSlots)
+    lengthInBits += 16;
+
+    // Array field
+    if (slots != null) {
+      int i = 0;
+      for (PnIoCm_RealIdentificationApi_Slot element : slots) {
+        ThreadLocalHelper.lastItemThreadLocal.set(++i >= slots.size());
+        lengthInBits += element.getLengthInBits();
+      }
+    }
+
+    return lengthInBits;
+  }
+
+  public static PnIoCm_RealIdentificationApi staticParse(ReadBuffer readBuffer, Object... args)
+      throws ParseException {
+    PositionAware positionAware = readBuffer;
+    return staticParse(readBuffer);
+  }
+
+  public static PnIoCm_RealIdentificationApi staticParse(ReadBuffer readBuffer)
+      throws ParseException {
+    readBuffer.pullContext("PnIoCm_RealIdentificationApi");
+    PositionAware positionAware = readBuffer;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+    long api =
+        readConstField(
+            "api",
+            readUnsignedLong(readBuffer, 32),
+            PnIoCm_RealIdentificationApi.API,
+            WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    int numSlots =
+        readImplicitField(
+            "numSlots",
+            readUnsignedInt(readBuffer, 16),
+            WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    List<PnIoCm_RealIdentificationApi_Slot> slots =
+        readCountArrayField(
+            "slots",
+            new DataReaderComplexDefault<>(
+                () -> PnIoCm_RealIdentificationApi_Slot.staticParse(readBuffer), readBuffer),
+            numSlots,
+            WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    readBuffer.closeContext("PnIoCm_RealIdentificationApi");
+    // Create the instance
+    PnIoCm_RealIdentificationApi _pnIoCm_RealIdentificationApi;
+    _pnIoCm_RealIdentificationApi = new PnIoCm_RealIdentificationApi(slots);
+    return _pnIoCm_RealIdentificationApi;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof PnIoCm_RealIdentificationApi)) {
+      return false;
+    }
+    PnIoCm_RealIdentificationApi that = (PnIoCm_RealIdentificationApi) o;
+    return (getSlots() == that.getSlots()) && true;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(getSlots());
+  }
+
+  @Override
+  public String toString() {
+    WriteBufferBoxBased writeBufferBoxBased = new WriteBufferBoxBased(true, true);
+    try {
+      writeBufferBoxBased.writeSerializable(this);
+    } catch (SerializationException e) {
+      throw new RuntimeException(e);
+    }
+    return "\n" + writeBufferBoxBased.getBox().toString() + "\n";
+  }
+}
diff --git a/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi_Slot.java b/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi_Slot.java
new file mode 100644
index 0000000000..fd1c5ac8e4
--- /dev/null
+++ b/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi_Slot.java
@@ -0,0 +1,208 @@
+/*
+ * 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.profinet.readwrite;
+
+import static org.apache.plc4x.java.spi.codegen.fields.FieldReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.fields.FieldWriterFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataWriterFactory.*;
+import static org.apache.plc4x.java.spi.generation.StaticHelper.*;
+
+import java.time.*;
+import java.util.*;
+import org.apache.plc4x.java.api.exceptions.*;
+import org.apache.plc4x.java.api.value.*;
+import org.apache.plc4x.java.spi.codegen.*;
+import org.apache.plc4x.java.spi.codegen.fields.*;
+import org.apache.plc4x.java.spi.codegen.io.*;
+import org.apache.plc4x.java.spi.generation.*;
+
+// Code generated by code-generation. DO NOT EDIT.
+
+public class PnIoCm_RealIdentificationApi_Slot implements Message {
+
+  // Properties.
+  protected final int slotNumber;
+  protected final long moduleIdentNumber;
+  protected final List<PnIoCm_RealIdentificationApi_Subslot> subslots;
+
+  public PnIoCm_RealIdentificationApi_Slot(
+      int slotNumber, long moduleIdentNumber, List<PnIoCm_RealIdentificationApi_Subslot> subslots) {
+    super();
+    this.slotNumber = slotNumber;
+    this.moduleIdentNumber = moduleIdentNumber;
+    this.subslots = subslots;
+  }
+
+  public int getSlotNumber() {
+    return slotNumber;
+  }
+
+  public long getModuleIdentNumber() {
+    return moduleIdentNumber;
+  }
+
+  public List<PnIoCm_RealIdentificationApi_Subslot> getSubslots() {
+    return subslots;
+  }
+
+  public void serialize(WriteBuffer writeBuffer) throws SerializationException {
+    PositionAware positionAware = writeBuffer;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+    writeBuffer.pushContext("PnIoCm_RealIdentificationApi_Slot");
+
+    // Simple Field (slotNumber)
+    writeSimpleField(
+        "slotNumber",
+        slotNumber,
+        writeUnsignedInt(writeBuffer, 16),
+        WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    // Simple Field (moduleIdentNumber)
+    writeSimpleField(
+        "moduleIdentNumber",
+        moduleIdentNumber,
+        writeUnsignedLong(writeBuffer, 32),
+        WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    // Implicit Field (numSubslots) (Used for parsing, but its value is not stored as it's
+    // implicitly given by the objects content)
+    int numSubslots = (int) (COUNT(getSubslots()));
+    writeImplicitField(
+        "numSubslots",
+        numSubslots,
+        writeUnsignedInt(writeBuffer, 16),
+        WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    // Array Field (subslots)
+    writeComplexTypeArrayField(
+        "subslots", subslots, writeBuffer, WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    writeBuffer.popContext("PnIoCm_RealIdentificationApi_Slot");
+  }
+
+  @Override
+  public int getLengthInBytes() {
+    return (int) Math.ceil((float) getLengthInBits() / 8.0);
+  }
+
+  @Override
+  public int getLengthInBits() {
+    int lengthInBits = 0;
+    PnIoCm_RealIdentificationApi_Slot _value = this;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+    // Simple field (slotNumber)
+    lengthInBits += 16;
+
+    // Simple field (moduleIdentNumber)
+    lengthInBits += 32;
+
+    // Implicit Field (numSubslots)
+    lengthInBits += 16;
+
+    // Array field
+    if (subslots != null) {
+      int i = 0;
+      for (PnIoCm_RealIdentificationApi_Subslot element : subslots) {
+        ThreadLocalHelper.lastItemThreadLocal.set(++i >= subslots.size());
+        lengthInBits += element.getLengthInBits();
+      }
+    }
+
+    return lengthInBits;
+  }
+
+  public static PnIoCm_RealIdentificationApi_Slot staticParse(ReadBuffer readBuffer, Object... args)
+      throws ParseException {
+    PositionAware positionAware = readBuffer;
+    return staticParse(readBuffer);
+  }
+
+  public static PnIoCm_RealIdentificationApi_Slot staticParse(ReadBuffer readBuffer)
+      throws ParseException {
+    readBuffer.pullContext("PnIoCm_RealIdentificationApi_Slot");
+    PositionAware positionAware = readBuffer;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+    int slotNumber =
+        readSimpleField(
+            "slotNumber",
+            readUnsignedInt(readBuffer, 16),
+            WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    long moduleIdentNumber =
+        readSimpleField(
+            "moduleIdentNumber",
+            readUnsignedLong(readBuffer, 32),
+            WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    int numSubslots =
+        readImplicitField(
+            "numSubslots",
+            readUnsignedInt(readBuffer, 16),
+            WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    List<PnIoCm_RealIdentificationApi_Subslot> subslots =
+        readCountArrayField(
+            "subslots",
+            new DataReaderComplexDefault<>(
+                () -> PnIoCm_RealIdentificationApi_Subslot.staticParse(readBuffer), readBuffer),
+            numSubslots,
+            WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    readBuffer.closeContext("PnIoCm_RealIdentificationApi_Slot");
+    // Create the instance
+    PnIoCm_RealIdentificationApi_Slot _pnIoCm_RealIdentificationApi_Slot;
+    _pnIoCm_RealIdentificationApi_Slot =
+        new PnIoCm_RealIdentificationApi_Slot(slotNumber, moduleIdentNumber, subslots);
+    return _pnIoCm_RealIdentificationApi_Slot;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof PnIoCm_RealIdentificationApi_Slot)) {
+      return false;
+    }
+    PnIoCm_RealIdentificationApi_Slot that = (PnIoCm_RealIdentificationApi_Slot) o;
+    return (getSlotNumber() == that.getSlotNumber())
+        && (getModuleIdentNumber() == that.getModuleIdentNumber())
+        && (getSubslots() == that.getSubslots())
+        && true;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(getSlotNumber(), getModuleIdentNumber(), getSubslots());
+  }
+
+  @Override
+  public String toString() {
+    WriteBufferBoxBased writeBufferBoxBased = new WriteBufferBoxBased(true, true);
+    try {
+      writeBufferBoxBased.writeSerializable(this);
+    } catch (SerializationException e) {
+      throw new RuntimeException(e);
+    }
+    return "\n" + writeBufferBoxBased.getBox().toString() + "\n";
+  }
+}
diff --git a/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi_Subslot.java b/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi_Subslot.java
new file mode 100644
index 0000000000..b8dd21bc9e
--- /dev/null
+++ b/plc4j/drivers/profinet/src/main/generated/org/apache/plc4x/java/profinet/readwrite/PnIoCm_RealIdentificationApi_Subslot.java
@@ -0,0 +1,161 @@
+/*
+ * 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.profinet.readwrite;
+
+import static org.apache.plc4x.java.spi.codegen.fields.FieldReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.fields.FieldWriterFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataWriterFactory.*;
+import static org.apache.plc4x.java.spi.generation.StaticHelper.*;
+
+import java.time.*;
+import java.util.*;
+import org.apache.plc4x.java.api.exceptions.*;
+import org.apache.plc4x.java.api.value.*;
+import org.apache.plc4x.java.spi.codegen.*;
+import org.apache.plc4x.java.spi.codegen.fields.*;
+import org.apache.plc4x.java.spi.codegen.io.*;
+import org.apache.plc4x.java.spi.generation.*;
+
+// Code generated by code-generation. DO NOT EDIT.
+
+public class PnIoCm_RealIdentificationApi_Subslot implements Message {
+
+  // Properties.
+  protected final int subslotNumber;
+  protected final long submoduleIdentNumber;
+
+  public PnIoCm_RealIdentificationApi_Subslot(int subslotNumber, long submoduleIdentNumber) {
+    super();
+    this.subslotNumber = subslotNumber;
+    this.submoduleIdentNumber = submoduleIdentNumber;
+  }
+
+  public int getSubslotNumber() {
+    return subslotNumber;
+  }
+
+  public long getSubmoduleIdentNumber() {
+    return submoduleIdentNumber;
+  }
+
+  public void serialize(WriteBuffer writeBuffer) throws SerializationException {
+    PositionAware positionAware = writeBuffer;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+    writeBuffer.pushContext("PnIoCm_RealIdentificationApi_Subslot");
+
+    // Simple Field (subslotNumber)
+    writeSimpleField(
+        "subslotNumber",
+        subslotNumber,
+        writeUnsignedInt(writeBuffer, 16),
+        WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    // Simple Field (submoduleIdentNumber)
+    writeSimpleField(
+        "submoduleIdentNumber",
+        submoduleIdentNumber,
+        writeUnsignedLong(writeBuffer, 32),
+        WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    writeBuffer.popContext("PnIoCm_RealIdentificationApi_Subslot");
+  }
+
+  @Override
+  public int getLengthInBytes() {
+    return (int) Math.ceil((float) getLengthInBits() / 8.0);
+  }
+
+  @Override
+  public int getLengthInBits() {
+    int lengthInBits = 0;
+    PnIoCm_RealIdentificationApi_Subslot _value = this;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+    // Simple field (subslotNumber)
+    lengthInBits += 16;
+
+    // Simple field (submoduleIdentNumber)
+    lengthInBits += 32;
+
+    return lengthInBits;
+  }
+
+  public static PnIoCm_RealIdentificationApi_Subslot staticParse(
+      ReadBuffer readBuffer, Object... args) throws ParseException {
+    PositionAware positionAware = readBuffer;
+    return staticParse(readBuffer);
+  }
+
+  public static PnIoCm_RealIdentificationApi_Subslot staticParse(ReadBuffer readBuffer)
+      throws ParseException {
+    readBuffer.pullContext("PnIoCm_RealIdentificationApi_Subslot");
+    PositionAware positionAware = readBuffer;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+    int subslotNumber =
+        readSimpleField(
+            "subslotNumber",
+            readUnsignedInt(readBuffer, 16),
+            WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    long submoduleIdentNumber =
+        readSimpleField(
+            "submoduleIdentNumber",
+            readUnsignedLong(readBuffer, 32),
+            WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN));
+
+    readBuffer.closeContext("PnIoCm_RealIdentificationApi_Subslot");
+    // Create the instance
+    PnIoCm_RealIdentificationApi_Subslot _pnIoCm_RealIdentificationApi_Subslot;
+    _pnIoCm_RealIdentificationApi_Subslot =
+        new PnIoCm_RealIdentificationApi_Subslot(subslotNumber, submoduleIdentNumber);
+    return _pnIoCm_RealIdentificationApi_Subslot;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof PnIoCm_RealIdentificationApi_Subslot)) {
+      return false;
+    }
+    PnIoCm_RealIdentificationApi_Subslot that = (PnIoCm_RealIdentificationApi_Subslot) o;
+    return (getSubslotNumber() == that.getSubslotNumber())
+        && (getSubmoduleIdentNumber() == that.getSubmoduleIdentNumber())
+        && true;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(getSubslotNumber(), getSubmoduleIdentNumber());
+  }
+
+  @Override
+  public String toString() {
+    WriteBufferBoxBased writeBufferBoxBased = new WriteBufferBoxBased(true, true);
+    try {
+      writeBufferBoxBased.writeSerializable(this);
+    } catch (SerializationException e) {
+      throw new RuntimeException(e);
+    }
+    return "\n" + writeBufferBoxBased.getBox().toString() + "\n";
+  }
+}
diff --git a/protocols/profinet/src/main/resources/protocols/profinet/pnio.mspec b/protocols/profinet/src/main/resources/protocols/profinet/pnio.mspec
index cc6261f2c1..079d456ea2 100644
--- a/protocols/profinet/src/main/resources/protocols/profinet/pnio.mspec
+++ b/protocols/profinet/src/main/resources/protocols/profinet/pnio.mspec
@@ -315,19 +315,27 @@
             [array    PnIoCm_ExpectedSubmoduleBlockReqApi apis   count         'numberOfApis'       ]
         ]
         ['MODULE_DIFF_BLOCK' PnIoCm_Block_ModuleDiff
-            [implicit      uint 16          blockLength      'lengthInBytes - 4']
-            [simple        uint 8           blockVersionHigh                    ]
-            [simple        uint 8           blockVersionLow                     ]
-            [implicit uint 16                numberOfApis         'COUNT(apis)'                     ]
-            [array    PnIoCm_ModuleDiffBlockApi apis              count         'numberOfApis'      ]
+            [implicit uint 16                   blockLength      'lengthInBytes - 4'          ]
+            [simple   uint 8                    blockVersionHigh                              ]
+            [simple   uint 8                    blockVersionLow                               ]
+            [implicit uint 16                   numApis          'COUNT(apis)'                ]
+            [array    PnIoCm_ModuleDiffBlockApi apis             count               'numApis']
         ]
         ['AR_SERVER_BLOCK' PnIoCm_Block_ArServer
-            [implicit      uint 16          blockLength      'lengthInBytes - 4']
-            [simple        uint 8           blockVersionHigh                    ]
-            [simple        uint 8           blockVersionLow                     ]
-            [simple   PascalString16BitLength            stationName            ]
-            [padding  uint 8      pad '0x00'          '20 - 6 - (stationName.stringLength)'              ]
+            [implicit uint 16                 blockLength      'lengthInBytes - 4'                                      ]
+            [simple   uint 8                  blockVersionHigh                                                          ]
+            [simple   uint 8                  blockVersionLow                                                           ]
+            [simple   PascalString16BitLength stationName                                                               ]
+            [padding  uint 8                  pad              '0x00'              '20 - 6 - (stationName.stringLength)']
+        ]
+        ['REAL_IDENTIFICATION_DATA' PnIoCm_Block_RealIdentificationData
+            [implicit uint 16                      blockLength      'lengthInBytes - 4'          ]
+            [simple   uint 8                       blockVersionHigh                              ]
+            [simple   uint 8                       blockVersionLow                               ]
+            [implicit uint 16                      numApis          'COUNT(apis)'                ]
+            [array    PnIoCm_RealIdentificationApi apis             count               'numApis']
         ]
+
         // https://cache.industry.siemens.com/dl/files/491/26435491/att_859456/v1/PGH_IO-Base_0.pdf (page 231)
         ['I_AND_M_0' PnIoCm_Block_IAndM0
             [implicit      uint   16        blockLength      'lengthInBytes - 4']
@@ -445,6 +453,24 @@
     [simple PnIoCm_AddInfo   addInfo             ]
 ]
 
+[type PnIoCm_RealIdentificationApi byteOrder='BIG_ENDIAN'
+    [const    uint 32                           api      0x00000000               ]
+    [implicit uint 16                           numSlots 'COUNT(slots)'           ]
+    [array    PnIoCm_RealIdentificationApi_Slot slots    count          'numSlots']
+]
+
+[type PnIoCm_RealIdentificationApi_Slot byteOrder='BIG_ENDIAN'
+    [simple   uint 16                              slotNumber                                       ]
+    [simple   uint 32                              moduleIdentNumber                                ]
+    [implicit uint 16                              numSubslots       'COUNT(subslots)'              ]
+    [array    PnIoCm_RealIdentificationApi_Subslot subslots          count             'numSubslots']
+]
+
+[type PnIoCm_RealIdentificationApi_Subslot byteOrder='BIG_ENDIAN'
+    [simple        uint 16                subslotNumber                 ]
+    [simple        uint 32                submoduleIdentNumber          ]
+]
+
 [discriminatedType PnIoCm_Submodule byteOrder='BIG_ENDIAN'
     [simple        uint 16                slotNumber                    ]
     [simple        uint 32                submoduleIdentNumber          ]