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 2021/07/11 15:32:06 UTC

[plc4x] branch feature/profinet-chris updated (fe89bcd -> 6c81528)

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

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


    from fe89bcd  plc4x: unified golang and java xml writing of spi types
     add 3d27849  plc4x: unified golang and java xml writing of spi types
     add 1954d40  plc4x: fixed datatypes and bitlengths of field types for spi (ads,eip,modbus,s7)
     add 2c746dc  It turns out the KNX discovery used "SendRequest" for the search. This resulted in only one response being handled and the second response being discarded. Refactored the code to use a loop consuming the default-channel, hereby allowing multiple responses.
     add a0afb3c  plc4j: small cleanup and refactorings on BacNetIpProtocolLogic
     add 35ff4b5  plc4go: initial bacnet draft
     new 6bed9e0  Merge branch 'develop' of github.com:apache/plc4x into feature/profinet-chris
     new 6c81528  - Fine-tuned the discovery to clean up allocated resources after finishing discovery - Made the example actually return something - Added an "executeWithHandler" which is able to intercept incoming events as they come in

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:
 .../testing/protocols/ads/DriverTestsuite.xml      |  24 +--
 .../testing/protocols/eip/DriverTestsuite.xml      |   2 +-
 .../testing/protocols/modbus/DriverTestsuite.xml   |  24 +--
 .../testing/protocols/s7/DriverTestsuite.xml       |  16 +-
 plc4go/internal/plc4go/ads/Field.go                |  17 +-
 plc4go/internal/plc4go/bacnetip/Connection.go      |  77 ++++++++
 plc4go/internal/plc4go/bacnetip/Driver.go          |  81 +++++++-
 plc4go/internal/plc4go/bacnetip/Field.go           |  92 +++++++++
 .../plc4go/{eip => bacnetip}/FieldHandler.go       |  30 +--
 .../plc4go/{s7 => bacnetip}/MessageCodec.go        |  16 +-
 plc4go/internal/plc4go/bacnetip/Subscriber.go      |  70 +++++++
 .../plc4go/{modbus => bacnetip}/ValueHandler.go    |   2 +-
 plc4go/internal/plc4go/eip/Field.go                |   3 +-
 plc4go/internal/plc4go/knxnetip/Discoverer.go      |  61 +++---
 plc4go/internal/plc4go/modbus/Field.go             |   5 +-
 plc4go/internal/plc4go/s7/Field.go                 |  45 ++++-
 .../plc4x/java/api/messages/PlcDiscoveryItem.java  |   3 +-
 .../java/api/messages/PlcDiscoveryItemHandler.java |   7 +
 .../java/api/messages/PlcDiscoveryRequest.java     |   2 +
 .../java/api/messages/PlcDiscoveryResponse.java    |   8 +
 .../plc4x/java/ads/field/DirectAdsField.java       |   7 +-
 .../plc4x/java/ads/field/DirectAdsStringField.java |   6 +-
 .../plc4x/java/ads/field/SymbolicAdsField.java     |   2 +-
 .../java/ads/field/SymbolicAdsStringField.java     |   6 +-
 .../plc4x/java/eip/readwrite/field/EipField.java   |   3 +-
 .../plc4x/java/modbus/field/ModbusField.java       |   5 +-
 .../apache/plc4x/java/profinet/ProfinetDriver.java |   8 +-
 .../profinet/discovery/ProfinetPlcDiscoverer.java  | 183 ++++++++++--------
 .../plc4x/java/s7/readwrite/field/S7Field.java     |  11 +-
 .../java/s7/readwrite/field/S7StringField.java     |   8 +-
 .../examples/hellodiscovery/HelloDiscovery.java    |  22 ++-
 .../hello-discovery/src/main/resources/logback.xml |   2 +-
 .../java/spi/messages/DefaultPlcDiscoveryItem.java |   7 +-
 .../spi/messages/DefaultPlcDiscoveryRequest.java   |   5 +
 .../spi/messages/DefaultPlcDiscoveryResponse.java  |   8 +
 .../plc4x/java/spi/messages/PlcDiscoverer.java     |   3 +
 .../transport/rawsocket/RawSocketTransport.java    |   4 +-
 .../resources/protocols/ads/DriverTestsuite.xml    |  24 +--
 .../resources/protocols/eip/DriverTestsuite.xml    |   2 +-
 .../resources/protocols/modbus/DriverTestsuite.xml |  24 +--
 .../resources/protocols/s7/DriverTestsuite.xml     |  16 +-
 .../bacnetip/protocol/BacNetIpProtocolLogic.java   | 207 +++++++++++----------
 42 files changed, 782 insertions(+), 366 deletions(-)
 create mode 100644 plc4go/internal/plc4go/bacnetip/Connection.go
 create mode 100644 plc4go/internal/plc4go/bacnetip/Field.go
 copy plc4go/internal/plc4go/{eip => bacnetip}/FieldHandler.go (65%)
 copy plc4go/internal/plc4go/{s7 => bacnetip}/MessageCodec.go (89%)
 create mode 100644 plc4go/internal/plc4go/bacnetip/Subscriber.go
 copy plc4go/internal/plc4go/{modbus => bacnetip}/ValueHandler.go (98%)
 create mode 100644 plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItemHandler.java

[plc4x] 01/02: Merge branch 'develop' of github.com:apache/plc4x into feature/profinet-chris

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

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

commit 6bed9e0b1aaeb48f181bc4ccd4f10030da5ddc99
Merge: fe89bcd 35ff4b5
Author: cdutz <ch...@c-ware.de>
AuthorDate: Sun Jul 11 13:23:55 2021 +0200

    Merge branch 'develop' of github.com:apache/plc4x into feature/profinet-chris
    
     Conflicts:
    	plc4go/assets/testing/protocols/ads/DriverTestsuite.xml
    	plc4go/assets/testing/protocols/eip/DriverTestsuite.xml
    	plc4go/assets/testing/protocols/modbus/DriverTestsuite.xml
    	plc4go/assets/testing/protocols/s7/DriverTestsuite.xml
    	plc4go/internal/plc4go/ads/Field.go
    	plc4go/internal/plc4go/eip/Field.go
    	plc4go/internal/plc4go/modbus/Field.go
    	plc4go/internal/plc4go/s7/Field.go
    	plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/DirectAdsField.java
    	plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/SymbolicAdsStringField.java
    	plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/readwrite/field/EipField.java
    	plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/field/ModbusField.java
    	plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/field/S7Field.java
    	protocols/ads/src/test/resources/protocols/ads/DriverTestsuite.xml
    	protocols/eip/src/test/resources/protocols/eip/DriverTestsuite.xml
    	protocols/modbus/src/test/resources/protocols/modbus/DriverTestsuite.xml
    	protocols/s7/src/test/resources/protocols/s7/DriverTestsuite.xml

 .../testing/protocols/ads/DriverTestsuite.xml      |  24 +--
 .../testing/protocols/eip/DriverTestsuite.xml      |   2 +-
 .../testing/protocols/modbus/DriverTestsuite.xml   |  24 +--
 .../testing/protocols/s7/DriverTestsuite.xml       |  16 +-
 plc4go/internal/plc4go/ads/Field.go                |  17 +-
 plc4go/internal/plc4go/bacnetip/Connection.go      |  77 ++++++++
 plc4go/internal/plc4go/bacnetip/Driver.go          |  81 +++++++-
 plc4go/internal/plc4go/bacnetip/Field.go           |  92 +++++++++
 plc4go/internal/plc4go/bacnetip/FieldHandler.go    |  64 +++++++
 plc4go/internal/plc4go/bacnetip/MessageCodec.go    | 102 ++++++++++
 plc4go/internal/plc4go/bacnetip/Subscriber.go      |  70 +++++++
 .../plc4go/bacnetip/{Driver.go => ValueHandler.go} |  12 +-
 plc4go/internal/plc4go/eip/Field.go                |   3 +-
 plc4go/internal/plc4go/knxnetip/Discoverer.go      |  61 +++---
 plc4go/internal/plc4go/modbus/Field.go             |   5 +-
 plc4go/internal/plc4go/s7/Field.go                 |  45 ++++-
 .../plc4x/java/ads/field/DirectAdsField.java       |   7 +-
 .../plc4x/java/ads/field/DirectAdsStringField.java |   6 +-
 .../plc4x/java/ads/field/SymbolicAdsField.java     |   2 +-
 .../java/ads/field/SymbolicAdsStringField.java     |   6 +-
 .../plc4x/java/eip/readwrite/field/EipField.java   |   3 +-
 .../plc4x/java/modbus/field/ModbusField.java       |   5 +-
 .../plc4x/java/s7/readwrite/field/S7Field.java     |  11 +-
 .../java/s7/readwrite/field/S7StringField.java     |   8 +-
 .../resources/protocols/ads/DriverTestsuite.xml    |  24 +--
 .../resources/protocols/eip/DriverTestsuite.xml    |   2 +-
 .../resources/protocols/modbus/DriverTestsuite.xml |  24 +--
 .../resources/protocols/s7/DriverTestsuite.xml     |  16 +-
 .../bacnetip/protocol/BacNetIpProtocolLogic.java   | 207 +++++++++++----------
 29 files changed, 764 insertions(+), 252 deletions(-)

[plc4x] 02/02: - Fine-tuned the discovery to clean up allocated resources after finishing discovery - Made the example actually return something - Added an "executeWithHandler" which is able to intercept incoming events as they come in

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

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

commit 6c815282637499618008008761a0fa311dad610d
Author: cdutz <ch...@c-ware.de>
AuthorDate: Sun Jul 11 17:31:49 2021 +0200

    - Fine-tuned the discovery to clean up allocated resources after finishing discovery
    - Made the example actually return something
    - Added an "executeWithHandler" which is able to intercept incoming events as they come in
---
 .../plc4x/java/api/messages/PlcDiscoveryItem.java  |   3 +-
 .../java/api/messages/PlcDiscoveryItemHandler.java |   7 +
 .../java/api/messages/PlcDiscoveryRequest.java     |   2 +
 .../java/api/messages/PlcDiscoveryResponse.java    |   8 +
 .../apache/plc4x/java/profinet/ProfinetDriver.java |   8 +-
 .../profinet/discovery/ProfinetPlcDiscoverer.java  | 183 ++++++++++++---------
 .../examples/hellodiscovery/HelloDiscovery.java    |  22 +--
 .../hello-discovery/src/main/resources/logback.xml |   2 +-
 .../java/spi/messages/DefaultPlcDiscoveryItem.java |   7 +-
 .../spi/messages/DefaultPlcDiscoveryRequest.java   |   5 +
 .../spi/messages/DefaultPlcDiscoveryResponse.java  |   8 +
 .../plc4x/java/spi/messages/PlcDiscoverer.java     |   3 +
 .../transport/rawsocket/RawSocketTransport.java    |   4 +-
 13 files changed, 165 insertions(+), 97 deletions(-)

diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItem.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItem.java
index fddc77d..c94d513 100644
--- a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItem.java
+++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItem.java
@@ -18,7 +18,6 @@
  */
 package org.apache.plc4x.java.api.messages;
 
-import java.net.URL;
 import java.util.Map;
 
 public interface PlcDiscoveryItem {
@@ -36,7 +35,7 @@ public interface PlcDiscoveryItem {
     /**
      * @return returns the part of the url, the given transport needs in order to connect (plc.mycompany.de, 192.168.42.23, /dev/serial, COM1)
      */
-    URL getTransportUrl();
+    String getTransportUrl();
 
     /**
      * @return returns a map of all configuration options (usually encoded after the transport url's "?" character (rack=1&slot=1, little-endian=true, ...)
diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItemHandler.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItemHandler.java
new file mode 100644
index 0000000..13fa882
--- /dev/null
+++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItemHandler.java
@@ -0,0 +1,7 @@
+package org.apache.plc4x.java.api.messages;
+
+public interface PlcDiscoveryItemHandler {
+
+    void handle(PlcDiscoveryItem discoveryItem);
+
+}
diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryRequest.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryRequest.java
index 577e0bf..afe8579 100644
--- a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryRequest.java
+++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryRequest.java
@@ -24,6 +24,8 @@ public interface PlcDiscoveryRequest extends PlcRequest {
 
     CompletableFuture<? extends PlcDiscoveryResponse> execute();
 
+    CompletableFuture<? extends PlcDiscoveryResponse> executeWithHandler(PlcDiscoveryItemHandler handler);
+
     interface Builder extends PlcRequestBuilder {
 
         @Override
diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryResponse.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryResponse.java
index 85d42a7..c4db35b 100644
--- a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryResponse.java
+++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryResponse.java
@@ -18,8 +18,16 @@
  */
 package org.apache.plc4x.java.api.messages;
 
+import org.apache.plc4x.java.api.types.PlcResponseCode;
+
+import java.util.List;
+
 public interface PlcDiscoveryResponse extends PlcResponse {
 
     PlcDiscoveryRequest getRequest();
 
+    PlcResponseCode getResponseCode();
+
+    List<PlcDiscoveryItem> getValues();
+
 }
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/ProfinetDriver.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/ProfinetDriver.java
index 0a261f7..4354935 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/ProfinetDriver.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/ProfinetDriver.java
@@ -20,7 +20,6 @@ package org.apache.plc4x.java.profinet;
 
 import io.netty.buffer.ByteBuf;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryRequest;
-import org.apache.plc4x.java.api.messages.PlcDiscoveryResponse;
 import org.apache.plc4x.java.api.metadata.PlcDriverMetadata;
 import org.apache.plc4x.java.profinet.config.ProfinetConfiguration;
 import org.apache.plc4x.java.profinet.discovery.ProfinetPlcDiscoverer;
@@ -32,8 +31,6 @@ import org.apache.plc4x.java.profinet.readwrite.io.EthernetFrameIO;
 import org.apache.plc4x.java.spi.connection.GeneratedDriverBase;
 import org.apache.plc4x.java.spi.connection.ProtocolStackConfigurer;
 import org.apache.plc4x.java.spi.messages.DefaultPlcDiscoveryRequest;
-import org.apache.plc4x.java.spi.messages.DefaultPlcWriteRequest;
-import org.apache.plc4x.java.spi.messages.PlcDiscoverer;
 import org.apache.plc4x.java.spi.values.IEC61131ValueHandler;
 import org.apache.plc4x.java.api.value.PlcValueHandler;
 import org.apache.plc4x.java.spi.configuration.Configuration;
@@ -41,14 +38,15 @@ import org.apache.plc4x.java.spi.connection.SingleProtocolStackConfigurer;
 import org.apache.plc4x.java.spi.optimizer.BaseOptimizer;
 import org.apache.plc4x.java.spi.optimizer.SingleFieldOptimizer;
 
-import java.util.concurrent.CompletableFuture;
 import java.util.function.ToIntFunction;
 
 public class ProfinetDriver extends GeneratedDriverBase<EthernetFrame> {
 
+    public static final String DRIVER_CODE = "profinet";
+
     @Override
     public String getProtocolCode() {
-        return "profinet";
+        return DRIVER_CODE;
     }
 
     @Override
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
index dc43f21..ba7ef24 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
@@ -18,8 +18,13 @@ under the License.
 */
 package org.apache.plc4x.java.profinet.discovery;
 
+import org.apache.plc4x.java.api.exceptions.PlcException;
+import org.apache.plc4x.java.api.messages.PlcDiscoveryItem;
+import org.apache.plc4x.java.api.messages.PlcDiscoveryItemHandler;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryRequest;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryResponse;
+import org.apache.plc4x.java.api.types.PlcResponseCode;
+import org.apache.plc4x.java.profinet.ProfinetDriver;
 import org.apache.plc4x.java.profinet.readwrite.*;
 import org.apache.plc4x.java.profinet.readwrite.io.EthernetFrameIO;
 import org.apache.plc4x.java.profinet.readwrite.types.VirtualLanPriority;
@@ -27,7 +32,10 @@ import org.apache.plc4x.java.spi.generation.ParseException;
 import org.apache.plc4x.java.spi.generation.ReadBuffer;
 import org.apache.plc4x.java.spi.generation.ReadBufferByteBased;
 import org.apache.plc4x.java.spi.generation.WriteBufferByteBased;
+import org.apache.plc4x.java.spi.messages.DefaultPlcDiscoveryItem;
+import org.apache.plc4x.java.spi.messages.DefaultPlcDiscoveryResponse;
 import org.apache.plc4x.java.spi.messages.PlcDiscoverer;
+import org.apache.plc4x.java.transport.rawsocket.RawSocketTransport;
 import org.pcap4j.core.*;
 import org.pcap4j.packet.Dot1qVlanTagPacket;
 import org.pcap4j.packet.EthernetPacket;
@@ -35,9 +43,10 @@ import org.pcap4j.packet.IllegalRawDataException;
 import org.pcap4j.packet.Packet;
 import org.pcap4j.packet.namednumber.EtherType;
 import org.pcap4j.util.LinkLayerAddress;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -46,6 +55,7 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
 
     private static final EtherType PN_EtherType = EtherType.getInstance((short) 0x8892);
 
+    // The constants for the different block names and their actual meaning.
     private static final String DEVICE_TYPE_NAME = "DEVICE_PROPERTIES_OPTION-1";
     private static final String DEVICE_NAME_OF_STATION = "DEVICE_PROPERTIES_OPTION-2";
     private static final String DEVICE_ID = "DEVICE_PROPERTIES_OPTION-3";
@@ -54,62 +64,110 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
     private static final String DEVICE_INSTANCE = "DEVICE_PROPERTIES_OPTION-7";
     private static final String IP_OPTION_IP = "IP_OPTION-2";
 
-    public ProfinetPlcDiscoverer() {
-    }
+    private final Logger logger = LoggerFactory.getLogger(ProfinetPlcDiscoverer.class);
 
     @Override
     public CompletableFuture<PlcDiscoveryResponse> discover(PlcDiscoveryRequest discoveryRequest) {
-        Map<String, DCP_Identify_ResPDU> pnDevices = new HashMap<>();
+        return discoverWithHandler(discoveryRequest, null);
+    }
+
+    public CompletableFuture<PlcDiscoveryResponse> discoverWithHandler(PlcDiscoveryRequest discoveryRequest, PlcDiscoveryItemHandler handler) {
+        CompletableFuture<PlcDiscoveryResponse> future = new CompletableFuture<>();
+        Set<PcapHandle> openHandles = new HashSet<>();
+        List<PlcDiscoveryItem> values = new ArrayList<>();
         try {
             for (PcapNetworkInterface dev : Pcaps.findAllDevs()) {
-                if(!dev.isLoopBack() && dev.isRunning()) {
+                if (!dev.isLoopBack() && dev.isRunning()) {
                     for (LinkLayerAddress linkLayerAddress : dev.getLinkLayerAddresses()) {
                         org.pcap4j.util.MacAddress macAddress = (org.pcap4j.util.MacAddress) linkLayerAddress;
                         PcapHandle handle = dev.openLive(65536, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, 10);
-                        PcapHandle sendHandle = dev.openLive(65536, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, 10);
+                        openHandles.add(handle);
+
                         ExecutorService pool = Executors.newSingleThreadExecutor();
 
                         // Only react on PROFINET DCP packets targeted at our current MAC address.
-                        // TODO: Find out how to filter based on the ether frame type ...
                         handle.setFilter(
                             "((ether proto 0x8100) or (ether proto 0x8892)) and (ether dst " + Pcaps.toBpfString(macAddress) + ")",
                             BpfProgram.BpfCompileMode.OPTIMIZE);
 
                         PacketListener listener =
                             packet -> {
-                                if(packet instanceof EthernetPacket) {
+                                // EthernetPacket is the highest level of abstraction we can be expecting.
+                                // Everything inside this we will have to decode ourselves.
+                                if (packet instanceof EthernetPacket) {
                                     EthernetPacket ethernetPacket = (EthernetPacket) packet;
                                     boolean isPnPacket = false;
-                                    if(ethernetPacket.getPayload() instanceof Dot1qVlanTagPacket) {
+                                    // I have observed some times the ethernet packets being wrapped inside a VLAN
+                                    // Packet, in this case we simply unpack the content.
+                                    if (ethernetPacket.getPayload() instanceof Dot1qVlanTagPacket) {
                                         Dot1qVlanTagPacket vlanPacket = (Dot1qVlanTagPacket) ethernetPacket.getPayload();
-                                        if(PN_EtherType.equals(vlanPacket.getHeader().getType())) {
+                                        if (PN_EtherType.equals(vlanPacket.getHeader().getType())) {
                                             isPnPacket = true;
                                         }
-                                    } else if(PN_EtherType.equals(ethernetPacket.getHeader().getType())) {
+                                    } else if (PN_EtherType.equals(ethernetPacket.getHeader().getType())) {
                                         isPnPacket = true;
                                     }
 
                                     // It's a PROFINET packet.
-                                    if(isPnPacket) {
+                                    if (isPnPacket) {
                                         ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
                                         try {
                                             EthernetFrame ethernetFrame = EthernetFrameIO.staticParse(reader);
-                                            String sourceMacAddress = toMacAddressString(ethernetFrame.getSource());
                                             DCP_PDU pdu;
-                                            if(ethernetFrame.getPayload() instanceof VirtualLanEthernetFramePayload) {
+                                            // Access the pdu data (either directly or by
+                                            // unpacking the content of the VLAN packet.
+                                            if (ethernetFrame.getPayload() instanceof VirtualLanEthernetFramePayload) {
                                                 VirtualLanEthernetFramePayload vlefpl = (VirtualLanEthernetFramePayload) ethernetFrame.getPayload();
                                                 pdu = ((ProfinetEthernetFramePayload) vlefpl.getPayload()).getPdu();
                                             } else {
                                                 pdu = ((ProfinetEthernetFramePayload) ethernetFrame.getPayload()).getPdu();
                                             }
-                                            if(pdu instanceof DCP_Identify_ResPDU) {
-                                                DCP_Identify_ResPDU identify_resPDU = (DCP_Identify_ResPDU) pdu;
-                                                if(!pnDevices.containsKey(sourceMacAddress)) {
-                                                    pnDevices.put(sourceMacAddress, identify_resPDU);
+                                            // Inspect the PDU itself
+                                            // (in this case we only process identify response packets)
+                                            if (pdu instanceof DCP_Identify_ResPDU) {
+                                                DCP_Identify_ResPDU identifyResPDU = (DCP_Identify_ResPDU) pdu;
+
+                                                Map<String, DCP_Block> blocks = new HashMap<>();
+                                                for (DCP_Block block : identifyResPDU.getBlocks()) {
+                                                    String blockName = block.getOption().name() + "-" + block.getSuboption().toString();
+                                                    blocks.put(blockName, block);
+                                                }
+
+                                                // The mac address of the device we found
+                                                org.pcap4j.util.MacAddress srcAddr = ethernetPacket.getHeader().getSrcAddr();
+                                                // The mac address of the local network device
+                                                org.pcap4j.util.MacAddress dstAddr = ethernetPacket.getHeader().getDstAddr();
+
+                                                String deviceTypeName = "unknown";
+                                                if (blocks.containsKey(DEVICE_TYPE_NAME)) {
+                                                    DCP_BlockDevicePropertiesDeviceVendor block = (DCP_BlockDevicePropertiesDeviceVendor) blocks.get(DEVICE_TYPE_NAME);
+                                                    deviceTypeName = new String(block.getDeviceVendorValue());
                                                 }
+                                                String deviceName = "unknown";
+                                                if (blocks.containsKey(DEVICE_NAME_OF_STATION)) {
+                                                    DCP_BlockDevicePropertiesNameOfStation block = (DCP_BlockDevicePropertiesNameOfStation) blocks.get(DEVICE_NAME_OF_STATION);
+                                                    deviceName = new String(block.getNameOfStation());
+                                                }
+
+                                                String transportUrl = srcAddr.toString();
+                                                Map<String, String> options =
+                                                    Collections.singletonMap("localMacAddress", dstAddr.toString());
+                                                String name = deviceTypeName + " - " + deviceName;
+                                                PlcDiscoveryItem value = new DefaultPlcDiscoveryItem(
+                                                    ProfinetDriver.DRIVER_CODE, RawSocketTransport.TRANSPORT_CODE,
+                                                    transportUrl, options, name);
+                                                values.add(value);
+
+                                                // If we have a discovery handler, pass it to the handler callback
+                                                if (handler != null) {
+                                                    handler.handle(value);
+                                                }
+
+                                                logger.debug("Found new device: '{}' with connection-url '{}'",
+                                                    value.getName(), value.getConnectionUrl());
                                             }
                                         } catch (ParseException e) {
-                                            e.printStackTrace();
+                                            logger.error("Got error decoding packet", e);
                                         }
                                     }
                                 }
@@ -134,70 +192,46 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
                         WriteBufferByteBased buffer = new WriteBufferByteBased(34);
                         EthernetFrameIO.staticSerialize(buffer, identificationRequest);
                         Packet packet = EthernetPacket.newPacket(buffer.getData(), 0, 34);
-                        sendHandle.sendPacket(packet);
+                        handle.sendPacket(packet);
                     }
                 }
             }
-        } catch (PcapNativeException | ParseException e) {
-            e.printStackTrace();
-        } catch (NotOpenException e) {
-            e.printStackTrace();
-        } catch (IllegalRawDataException e) {
-            e.printStackTrace();
-        }
-        try {
-            Thread.sleep(5000);
-        } catch (InterruptedException e) {
-            e.printStackTrace();
+        } catch (IllegalRawDataException | NotOpenException | PcapNativeException | ParseException e) {
+            logger.error("Got an exception while processing raw socket data", e);
+            future.completeExceptionally(new PlcException("Got an internal error while performing discovery"));
+            for (PcapHandle openHandle : openHandles) {
+                openHandle.close();
+            }
+            return future;
         }
 
-        System.out.println(String.format("Found %d PROFINET devices:", pnDevices.size()));
-        for (DCP_Identify_ResPDU pnDevice : pnDevices.values()) {
-            outputPnDevice(pnDevice);
-        }
+        // Create a timer that completes the future after a given time with all the responses it found till then.
+        Timer timer = new Timer("Discovery Timeout");
+        timer.schedule(new TimerTask() {
+            public void run() {
+                PlcDiscoveryResponse response =
+                    new DefaultPlcDiscoveryResponse(discoveryRequest, PlcResponseCode.OK, values);
+                future.complete(response);
+                for (PcapHandle openHandle : openHandles) {
+                    openHandle.close();
+                }
+            }
+        }, 5000L);
 
-        return null;
+        return future;
     }
 
     private static MacAddress toPlc4xMacAddress(org.pcap4j.util.MacAddress pcap4jMacAddress) {
         byte[] address = pcap4jMacAddress.getAddress();
-        return new MacAddress(new short[]{ (short) address[0], (short) address[1], (short) address[2], (short) address[3], (short) address[4], (short) address[5]});
-    }
-
-    private static String toMacAddressString(MacAddress macAddress) {
-        return String.format("%x2:%x2:%x2:%x2:%x2:%x2", macAddress.getAddress()[0], macAddress.getAddress()[1],
-            macAddress.getAddress()[2], macAddress.getAddress()[3], macAddress.getAddress()[4], macAddress.getAddress()[5]);
-    }
-
-    private static void outputPnDevice(DCP_Identify_ResPDU pnDevice) {
-        Map<String, DCP_Block> blocks = new HashMap<>();
-        for (DCP_Block block : pnDevice.getBlocks()) {
-            String blockName = block.getOption().name() + "-" + block.getSuboption().toString();
-            blocks.put(blockName, block);
-        }
-
-        String deviceTypeName = "unknown";
-        if(blocks.containsKey(DEVICE_TYPE_NAME)) {
-            DCP_BlockDevicePropertiesDeviceVendor block = (DCP_BlockDevicePropertiesDeviceVendor) blocks.get(DEVICE_TYPE_NAME);
-            deviceTypeName = new String(block.getDeviceVendorValue());
-        }
-        String deviceName = "unknown";
-        if(blocks.containsKey(DEVICE_NAME_OF_STATION)) {
-            DCP_BlockDevicePropertiesNameOfStation block = (DCP_BlockDevicePropertiesNameOfStation) blocks.get(DEVICE_NAME_OF_STATION);
-            deviceName = new String(block.getNameOfStation());
-        }
-        String ipAddress = "unknown";
-        if(blocks.containsKey(IP_OPTION_IP)) {
-            DCP_BlockIpIpParameter block = (DCP_BlockIpIpParameter) blocks.get(IP_OPTION_IP);
-            ipAddress = String.format("%d.%d.%d.%d", block.getIpAddress()[0], block.getIpAddress()[1], block.getIpAddress()[2], block.getIpAddress()[3]);
-        }
-        System.out.println(String.format("Found '%s' with name '%s' on IP: %s%n\t%s", deviceTypeName, deviceName, ipAddress, pnDevice));
+        return new MacAddress(new short[]{address[0], address[1], address[2], address[3], address[4], address[5]});
     }
 
     private static class Task implements Runnable {
 
-        private PcapHandle handle;
-        private PacketListener listener;
+        private final Logger logger = LoggerFactory.getLogger(Task.class);
+
+        private final PcapHandle handle;
+        private final PacketListener listener;
 
         public Task(PcapHandle handle, PacketListener listener) {
             this.handle = handle;
@@ -208,12 +242,11 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
         public void run() {
             try {
                 handle.loop(10, listener);
-            } catch (PcapNativeException e) {
-                e.printStackTrace();
             } catch (InterruptedException e) {
-                e.printStackTrace();
-            } catch (NotOpenException e) {
-                e.printStackTrace();
+                logger.error("Got error handling raw socket", e);
+                Thread.currentThread().interrupt();
+            } catch (PcapNativeException | NotOpenException e) {
+                logger.error("Got error handling raw socket", e);
             }
         }
     }
diff --git a/plc4j/examples/hello-discovery/src/main/java/org/apache/plc4x/java/examples/hellodiscovery/HelloDiscovery.java b/plc4j/examples/hello-discovery/src/main/java/org/apache/plc4x/java/examples/hellodiscovery/HelloDiscovery.java
index c2cfdb4..8003c28 100644
--- a/plc4j/examples/hello-discovery/src/main/java/org/apache/plc4x/java/examples/hellodiscovery/HelloDiscovery.java
+++ b/plc4j/examples/hello-discovery/src/main/java/org/apache/plc4x/java/examples/hellodiscovery/HelloDiscovery.java
@@ -19,19 +19,13 @@ under the License.
 package org.apache.plc4x.java.examples.hellodiscovery;
 
 import org.apache.plc4x.java.PlcDriverManager;
-import org.apache.plc4x.java.api.PlcConnection;
 import org.apache.plc4x.java.api.PlcDriver;
-import org.apache.plc4x.java.api.messages.PlcDiscoveryRequest;
-import org.apache.plc4x.java.api.messages.PlcDiscoveryResponse;
-import org.apache.plc4x.java.api.messages.PlcReadRequest;
-import org.apache.plc4x.java.api.messages.PlcReadResponse;
+import org.apache.plc4x.java.api.messages.*;
 import org.apache.plc4x.java.api.types.PlcResponseCode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
 
 public class HelloDiscovery {
 
@@ -53,18 +47,26 @@ public class HelloDiscovery {
         PlcDriverManager plcDriverManager = new PlcDriverManager();
         Set<String> driverCodes = plcDriverManager.listDrivers();
         for (String driverCode : driverCodes) {
-            logger.info(String.format("Executing Discovery for Driver: %s", driverCode));
+            logger.info("Executing Discovery for Driver: {}", driverCode);
             PlcDriver driver = plcDriverManager.getDriver(driverCode);
 
             // Check if this driver supports discovery.
             if(driver.getMetadata().canDiscover()) {
                 PlcDiscoveryRequest discoveryRequest = driver.discoveryRequestBuilder().build();
-                discoveryRequest.execute();
+                PlcDiscoveryResponse discoveryResponse = discoveryRequest.executeWithHandler(
+                    discoveryItem -> logger.info("Intercepted discovery of device with name: {} with connection url: {}",
+                        discoveryItem.getName(), discoveryItem.getConnectionUrl())).get();
+                if(discoveryResponse.getResponseCode() == PlcResponseCode.OK) {
+                    logger.info("Discovery finished successfully:");
+                    for (PlcDiscoveryItem discoveryItem : discoveryResponse.getValues()) {
+                        logger.info("Found device with name: {} with connection url: {}",
+                            discoveryItem.getName(), discoveryItem.getConnectionUrl());
+                    }
+                }
             } else {
                 logger.info("This driver doesn't support discovery");
             }
         }
-
     }
 
 }
diff --git a/plc4j/examples/hello-discovery/src/main/resources/logback.xml b/plc4j/examples/hello-discovery/src/main/resources/logback.xml
index a8ddebb..27d40c0 100644
--- a/plc4j/examples/hello-discovery/src/main/resources/logback.xml
+++ b/plc4j/examples/hello-discovery/src/main/resources/logback.xml
@@ -29,7 +29,7 @@
     </encoder>
   </appender>
 
-  <root level="trace">
+  <root level="info">
     <appender-ref ref="STDOUT" />
   </root>
 
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryItem.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryItem.java
index ac1fbdf..db1174f 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryItem.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryItem.java
@@ -35,14 +35,14 @@ public class DefaultPlcDiscoveryItem implements PlcDiscoveryItem, Serializable {
 
     private final String protocolCode;
     private final String transportCode;
-    private final URL transportUrl;
+    private final String transportUrl;
     private final Map<String, String> options;
     private final String name;
 
     @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
     public DefaultPlcDiscoveryItem(@JsonProperty("protocolCode") String protocolCode,
                                    @JsonProperty("transportCode") String transportCode,
-                                   @JsonProperty("transportUrl") URL transportUrl,
+                                   @JsonProperty("transportUrl") String transportUrl,
                                    @JsonProperty("options") Map<String, String> options,
                                    @JsonProperty("name") String name) {
         this.protocolCode = protocolCode;
@@ -63,7 +63,7 @@ public class DefaultPlcDiscoveryItem implements PlcDiscoveryItem, Serializable {
     }
 
     @Override
-    public URL getTransportUrl() {
+    public String getTransportUrl() {
         return transportUrl;
     }
 
@@ -85,6 +85,7 @@ public class DefaultPlcDiscoveryItem implements PlcDiscoveryItem, Serializable {
             boolean first = true;
             for (Map.Entry<String, String> optionEntry : options.entrySet()) {
                 if(first) {
+                    sb.append("?");
                     first = false;
                 } else {
                     sb.append("&");
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryRequest.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryRequest.java
index 79334cc..1cefe38 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryRequest.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryRequest.java
@@ -42,6 +42,11 @@ public class DefaultPlcDiscoveryRequest implements PlcDiscoveryRequest, Serializ
     }
 
     @Override
+    public CompletableFuture<? extends PlcDiscoveryResponse> executeWithHandler(PlcDiscoveryItemHandler handler) {
+        return discoverer.discoverWithHandler(this, handler);
+    }
+
+    @Override
     public void serialize(WriteBuffer writeBuffer) throws ParseException {
         writeBuffer.pushContext("PlcDiscoveryRequest");
 
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryResponse.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryResponse.java
index c219ff9..b3f5022 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryResponse.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryResponse.java
@@ -20,6 +20,7 @@ package org.apache.plc4x.java.spi.messages;
 
 import com.fasterxml.jackson.annotation.*;
 import org.apache.plc4x.java.api.messages.*;
+import org.apache.plc4x.java.api.types.PlcResponseCode;
 import org.apache.plc4x.java.spi.generation.ParseException;
 import org.apache.plc4x.java.spi.generation.WriteBuffer;
 import org.apache.plc4x.java.spi.utils.Serializable;
@@ -30,12 +31,15 @@ import java.util.*;
 public class DefaultPlcDiscoveryResponse implements PlcDiscoveryResponse, Serializable {
 
     private final PlcDiscoveryRequest request;
+    private final PlcResponseCode responseCode;
     private final List<PlcDiscoveryItem> values;
 
     @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
     public DefaultPlcDiscoveryResponse(@JsonProperty("request") PlcDiscoveryRequest request,
+                                       @JsonProperty("responseCode") PlcResponseCode responseCode,
                                        @JsonProperty("values") List<PlcDiscoveryItem> values) {
         this.request = request;
+        this.responseCode = responseCode;
         this.values = values;
     }
 
@@ -44,6 +48,10 @@ public class DefaultPlcDiscoveryResponse implements PlcDiscoveryResponse, Serial
         return request;
     }
 
+    public PlcResponseCode getResponseCode() {
+        return responseCode;
+    }
+
     public List<PlcDiscoveryItem> getValues() {
         return values;
     }
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/PlcDiscoverer.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/PlcDiscoverer.java
index 28ace34..a7ca16a 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/PlcDiscoverer.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/PlcDiscoverer.java
@@ -18,6 +18,7 @@
  */
 package org.apache.plc4x.java.spi.messages;
 
+import org.apache.plc4x.java.api.messages.PlcDiscoveryItemHandler;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryRequest;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryResponse;
 
@@ -36,4 +37,6 @@ public interface PlcDiscoverer {
      */
     CompletableFuture<PlcDiscoveryResponse> discover(PlcDiscoveryRequest discoveryRequest);
 
+    CompletableFuture<PlcDiscoveryResponse> discoverWithHandler(PlcDiscoveryRequest discoveryRequest, PlcDiscoveryItemHandler handler);
+
 }
diff --git a/plc4j/transports/raw-socket/src/main/java/org/apache/plc4x/java/transport/rawsocket/RawSocketTransport.java b/plc4j/transports/raw-socket/src/main/java/org/apache/plc4x/java/transport/rawsocket/RawSocketTransport.java
index 5153d7a..84ccb93 100644
--- a/plc4j/transports/raw-socket/src/main/java/org/apache/plc4x/java/transport/rawsocket/RawSocketTransport.java
+++ b/plc4j/transports/raw-socket/src/main/java/org/apache/plc4x/java/transport/rawsocket/RawSocketTransport.java
@@ -24,9 +24,11 @@ import org.apache.plc4x.java.utils.rawsockets.netty.address.RawSocketAddress;
 
 public class RawSocketTransport implements Transport {
 
+    public static final String TRANSPORT_CODE = "raw";
+
     @Override
     public String getTransportCode() {
-        return "raw";
+        return TRANSPORT_CODE;
     }
 
     @Override