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 2020/01/27 13:00:56 UTC

[plc4x] branch develop updated: - Continued working on the BACnet/IP driver (Enrichment of datastreams with data from EDE files)

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

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


The following commit(s) were added to refs/heads/develop by this push:
     new f689ba8  - Continued working on the BACnet/IP driver (Enrichment of datastreams with data from EDE files)
f689ba8 is described below

commit f689ba87ab0a39807751d42e769b9d89a02c0dc5
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Mon Jan 27 14:00:47 2020 +0100

    - Continued working on the BACnet/IP driver (Enrichment of datastreams with data from EDE files)
---
 .../org/apache/plc4x/java/api/value/PlcValues.java |   3 +
 pom.xml                                            |   5 +
 sandbox/test-java-bacnetip-driver/pom.xml          |   4 +
 .../apache/plc4x/java/bacnetip/ede/EdeParser.java  |  76 +++++++++++-
 .../plc4x/java/bacnetip/ede/model/Datapoint.java   | 135 +++++++++++++++++++++
 .../plc4x/java/bacnetip/ede/model/EdeModel.java    |  15 +++
 .../plc4x/java/bacnetip/field/BacNetIpField.java   |  30 +++++
 .../protocol/PassiveBacNetIpProtocolLogic.java     |  54 +++++----
 .../java/bacnetip/PassiveBacNetIpDriverManual.java |  10 +-
 9 files changed, 307 insertions(+), 25 deletions(-)

diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcValues.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcValues.java
index 244d784..e008c87 100644
--- a/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcValues.java
+++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcValues.java
@@ -356,6 +356,9 @@ public class PlcValues {
     }
 
     public static PlcValue of(Object o) {
+        if(o == null) {
+            return null;
+        }
         try {
             String simpleName = o.getClass().getSimpleName();
             Class<?> clazz = o.getClass();
diff --git a/pom.xml b/pom.xml
index 07826aa..47e8772 100644
--- a/pom.xml
+++ b/pom.xml
@@ -289,6 +289,11 @@
         <version>1.19</version>
       </dependency>
       <dependency>
+        <groupId>org.apache.commons</groupId>
+        <artifactId>commons-csv</artifactId>
+        <version>1.7</version>
+      </dependency>
+      <dependency>
         <groupId>commons-io</groupId>
         <artifactId>commons-io</artifactId>
         <version>${commons-io.version}</version>
diff --git a/sandbox/test-java-bacnetip-driver/pom.xml b/sandbox/test-java-bacnetip-driver/pom.xml
index f0cf3f2..46b79aa 100644
--- a/sandbox/test-java-bacnetip-driver/pom.xml
+++ b/sandbox/test-java-bacnetip-driver/pom.xml
@@ -82,6 +82,10 @@
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-annotations</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-csv</artifactId>
+    </dependency>
 
     <dependency>
       <groupId>org.osgi</groupId>
diff --git a/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/ede/EdeParser.java b/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/ede/EdeParser.java
index 7debb84..044655b 100644
--- a/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/ede/EdeParser.java
+++ b/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/ede/EdeParser.java
@@ -18,14 +18,84 @@ under the License.
 */
 package org.apache.plc4x.java.bacnetip.ede;
 
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVParser;
+import org.apache.commons.csv.CSVRecord;
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.bacnetip.ede.model.Datapoint;
 import org.apache.plc4x.java.bacnetip.ede.model.EdeModel;
+import org.apache.plc4x.java.bacnetip.field.BacNetIpField;
 
-import java.io.File;
+import java.io.*;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
 
 public class EdeParser {
 
-    public EdeModel parse(File knxprojFile) {
-        return null;
+    public EdeModel parse(File edeFile) {
+        if(!edeFile.exists()) {
+            throw new PlcRuntimeException("EDE File at " + edeFile.getPath() + " doesn't exist.");
+        }
+        try {
+            Reader in = new FileReader(edeFile);
+            final CSVParser parser = CSVFormat.newFormat(';').parse(in);
+            final Iterator<CSVRecord> iterator = parser.iterator();
+
+            // Skip the header.
+            for(int i = 0; i < 5; i++) {
+                if(!iterator.hasNext()) {
+                    throw new PlcRuntimeException("Invalid EDE file format");
+                }
+                iterator.next();
+            }
+            final CSVRecord layoutVersionRow = iterator.next();
+            // Check if the version of the layout is 3
+            if(!layoutVersionRow.get(1).equals("3")) {
+                throw new PlcRuntimeException("Unsupported EDE file layout version " + layoutVersionRow.get(2) +
+                    ". Currently only version 3 is supported.");
+            }
+            // Just skip the next row.
+            iterator.next();
+            // Get the column names.
+            final CSVRecord columnNames = iterator.next();
+
+
+            Map<BacNetIpField, Datapoint> datapoints = new HashMap<>();
+            // Process the content.
+            iterator.forEachRemaining(record -> {
+                BacNetIpField address = new BacNetIpField(Long.parseLong(
+                    record.get(1)), Integer.parseInt(record.get(3)), Long.parseLong(record.get(4)));
+                String keyName = (record.get(0).length() == 0) ? null : record.get(0);
+                String objectName = (record.get(2).length() == 0) ? null : record.get(2);
+                String description = (record.get(5).length() == 0) ? null : record.get(5);
+                Double defaultValue = (record.get(6).length() == 0) ? null : Double.valueOf(record.get(6).replace(",", "."));
+                Double minValue = (record.get(7).length() == 0) ? null : Double.valueOf(record.get(7).replace(",", "."));
+                Double maxValue = (record.get(8).length() == 0) ? null : Double.valueOf(record.get(8).replace(",", "."));
+                Boolean commandable = (record.get(9).length() == 0) ? null :
+                    record.get(9).equalsIgnoreCase("Y") ? Boolean.TRUE : record.get(9).equalsIgnoreCase("N") ? Boolean.TRUE : null;
+                Boolean supportsCov = (record.get(10).length() == 0) ? null :
+                    record.get(10).equalsIgnoreCase("Y") ? Boolean.TRUE : record.get(10).equalsIgnoreCase("N") ? Boolean.TRUE : null;
+                Double hiLimit = (record.get(11).length() == 0) ? null : Double.valueOf(record.get(11).replace(",", "."));
+                Double lowLimit = (record.get(12).length() == 0) ? null : Double.valueOf(record.get(12).replace(",", "."));
+                String stateTextReference = (record.get(13).length() == 0) ? null : record.get(13);
+                Integer unitCode = (record.get(14).length() == 0) ? null : Integer.valueOf(record.get(14));
+                Integer vendorSpecificAddress = (record.get(15).length() == 0) ? null : Integer.valueOf(record.get(15));
+                Datapoint datapoint = new Datapoint(address, keyName, objectName, description, defaultValue, minValue,
+                    maxValue, commandable, supportsCov, hiLimit, lowLimit, stateTextReference, unitCode,
+                    vendorSpecificAddress);
+                datapoints.put(address, datapoint);
+            });
+            return new EdeModel(datapoints);
+        } catch (IOException e) {
+            throw new PlcRuntimeException("Error parsing EDE file", e);
+        }
+
+    }
+
+    public static void main(String[] args) throws Exception {
+        EdeModel model = new EdeParser().parse(new File("/Users/christofer.dutz/Projects/Apache/PLC4X-Documents/BacNET/Merck/EDE-Files/F135/edeDataText3029.csv"));
+        System.out.println(model);
     }
 
 }
diff --git a/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/ede/model/Datapoint.java b/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/ede/model/Datapoint.java
new file mode 100644
index 0000000..5599826
--- /dev/null
+++ b/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/ede/model/Datapoint.java
@@ -0,0 +1,135 @@
+/*
+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.bacnetip.ede.model;
+
+import org.apache.plc4x.java.api.value.*;
+import org.apache.plc4x.java.bacnetip.field.BacNetIpField;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Datapoint {
+
+    private final BacNetIpField address;
+    private final String keyName;
+    private final String objectName;
+    private final String description;
+    private final Double defaultValue;
+    private final Double minValue;
+    private final Double maxValue;
+    private final Boolean commandable;
+    private final Boolean supportsCov;
+    private final Double hiLimit;
+    private final Double lowLimit;
+    private final String stateTextReference;
+    private final Integer unitCode;
+    private final Integer vendorSpecificAddress;
+
+    public Datapoint(BacNetIpField address, String keyName, String objectName, String description, Double defaultValue, Double minValue, Double maxValue, Boolean commandable, Boolean supportsCov, Double hiLimit, Double lowLimit, String stateTextReference, Integer unitCode, Integer vendorSpecificAddress) {
+        this.address = address;
+        this.keyName = keyName;
+        this.objectName = objectName;
+        this.description = description;
+        this.defaultValue = defaultValue;
+        this.minValue = minValue;
+        this.maxValue = maxValue;
+        this.commandable = commandable;
+        this.supportsCov = supportsCov;
+        this.hiLimit = hiLimit;
+        this.lowLimit = lowLimit;
+        this.stateTextReference = stateTextReference;
+        this.unitCode = unitCode;
+        this.vendorSpecificAddress = vendorSpecificAddress;
+    }
+
+    public BacNetIpField getAddress() {
+        return address;
+    }
+
+    public String getKeyName() {
+        return keyName;
+    }
+
+    public String getObjectName() {
+        return objectName;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public Double getDefaultValue() {
+        return defaultValue;
+    }
+
+    public Double getMinValue() {
+        return minValue;
+    }
+
+    public Double getMaxValue() {
+        return maxValue;
+    }
+
+    public Boolean getCommandable() {
+        return commandable;
+    }
+
+    public Boolean getSupportsCov() {
+        return supportsCov;
+    }
+
+    public Double getHiLimit() {
+        return hiLimit;
+    }
+
+    public Double getLowLimit() {
+        return lowLimit;
+    }
+
+    public String getStateTextReference() {
+        return stateTextReference;
+    }
+
+    public Integer getUnitCode() {
+        return unitCode;
+    }
+
+    public Integer getVendorSpecificAddress() {
+        return vendorSpecificAddress;
+    }
+
+    public Map<String, PlcValue> toPlcValues() {
+        Map<String, PlcValue> values = new HashMap<>();
+        values.put("keyName", (keyName == null) ? null : new PlcString(keyName));
+        values.put("objectName", (objectName == null) ? null : new PlcString(objectName));
+        values.put("description", (description == null) ? null : new PlcString(description));
+        values.put("defaultValue", (defaultValue == null) ? null : new PlcDouble(defaultValue));
+        values.put("minValue", (minValue == null) ? null : new PlcDouble(minValue));
+        values.put("maxValue", (maxValue == null) ? null : new PlcDouble(maxValue));
+        values.put("commandable", (commandable == null) ? null : new PlcBoolean(commandable));
+        values.put("supportsCov", (supportsCov == null) ? null : new PlcBoolean(supportsCov));
+        values.put("hiLimit", (hiLimit == null) ? null : new PlcDouble(hiLimit));
+        values.put("lowLimit", (lowLimit == null) ? null : new PlcDouble(lowLimit));
+        values.put("stateTextReference", (stateTextReference == null) ? null : new PlcString(stateTextReference));
+        values.put("unitCode", (unitCode == null) ? null : new PlcInteger(unitCode));
+        values.put("vendorSpecificAddress", (vendorSpecificAddress == null) ? null : new PlcInteger(vendorSpecificAddress));
+        return values;
+    }
+
+}
diff --git a/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/ede/model/EdeModel.java b/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/ede/model/EdeModel.java
index 6151fa2..b51b41d 100644
--- a/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/ede/model/EdeModel.java
+++ b/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/ede/model/EdeModel.java
@@ -18,5 +18,20 @@ under the License.
 */
 package org.apache.plc4x.java.bacnetip.ede.model;
 
+import org.apache.plc4x.java.bacnetip.field.BacNetIpField;
+
+import java.util.Map;
+
 public class EdeModel {
+
+    private final Map<BacNetIpField, Datapoint> datapoints;
+
+    public EdeModel(Map<BacNetIpField, Datapoint> datapoints) {
+        this.datapoints = datapoints;
+    }
+
+    public Datapoint getDatapoint(BacNetIpField field) {
+        return datapoints.get(field);
+    }
+
 }
diff --git a/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/field/BacNetIpField.java b/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/field/BacNetIpField.java
index 7432827..a76a33a 100644
--- a/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/field/BacNetIpField.java
+++ b/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/field/BacNetIpField.java
@@ -18,6 +18,8 @@ under the License.
 */
 package org.apache.plc4x.java.bacnetip.field;
 
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
 import org.apache.plc4x.java.api.model.PlcField;
@@ -80,6 +82,34 @@ public class BacNetIpField implements PlcField {
     }
 
     @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof BacNetIpField)) {
+            return false;
+        }
+
+        BacNetIpField that = (BacNetIpField) o;
+
+        return new EqualsBuilder()
+            .append(getDeviceIdentifier(), that.getDeviceIdentifier())
+            .append(getObjectType(), that.getObjectType())
+            .append(getObjectInstance(), that.getObjectInstance())
+            .isEquals();
+    }
+
+    @Override
+    public int hashCode() {
+        return new HashCodeBuilder(17, 37)
+            .append(getDeviceIdentifier())
+            .append(getObjectType())
+            .append(getObjectInstance())
+            .toHashCode();
+    }
+
+    @Override
     public String toString() {
         return new ToStringBuilder(this)
             .append("deviceIdentifier", deviceIdentifier)
diff --git a/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/protocol/PassiveBacNetIpProtocolLogic.java b/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/protocol/PassiveBacNetIpProtocolLogic.java
index 0f2cf9b..6973d6c 100644
--- a/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/protocol/PassiveBacNetIpProtocolLogic.java
+++ b/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/protocol/PassiveBacNetIpProtocolLogic.java
@@ -27,16 +27,18 @@ import org.apache.plc4x.java.api.messages.PlcSubscriptionResponse;
 import org.apache.plc4x.java.api.model.PlcConsumerRegistration;
 import org.apache.plc4x.java.api.model.PlcSubscriptionHandle;
 import org.apache.plc4x.java.api.types.PlcResponseCode;
+import org.apache.plc4x.java.api.value.PlcString;
+import org.apache.plc4x.java.api.value.PlcStruct;
 import org.apache.plc4x.java.api.value.PlcValue;
 import org.apache.plc4x.java.bacnetip.configuration.PassiveBacNetIpConfiguration;
 import org.apache.plc4x.java.bacnetip.ede.EdeParser;
+import org.apache.plc4x.java.bacnetip.ede.model.Datapoint;
 import org.apache.plc4x.java.bacnetip.ede.model.EdeModel;
 import org.apache.plc4x.java.bacnetip.field.BacNetIpField;
 import org.apache.plc4x.java.bacnetip.readwrite.*;
 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.generation.ReadBuffer;
 import org.apache.plc4x.java.spi.messages.DefaultPlcSubscriptionEvent;
 import org.apache.plc4x.java.spi.messages.DefaultPlcSubscriptionResponse;
 import org.apache.plc4x.java.spi.messages.InternalPlcSubscriptionRequest;
@@ -80,6 +82,11 @@ public class PassiveBacNetIpProtocolLogic extends Plc4xProtocolBase<BVLC> implem
     }
 
     @Override
+    public void onConnect(ConversationContext<BVLC> context) {
+        context.fireConnected();
+    }
+
+    @Override
     public void close(ConversationContext<BVLC> context) {
         // Nothing to do here ...
     }
@@ -112,22 +119,29 @@ public class PassiveBacNetIpProtocolLogic extends Plc4xProtocolBase<BVLC> implem
                     long objectInstance = valueChange.getIssueConfirmedNotificationsInstanceNumber();
                     BacNetIpField curField = new BacNetIpField(deviceIdentifier, objectType, objectInstance);
 
-                    System.out.println("Value change for " + curField.toString());
-
+                    // The actual value change is in the notifications ... iterate throught them to get it.
                     for (BACnetTagWithContent notification : valueChange.getNotifications()) {
+                        // These are value change notifications. Ignore the rest.
                         if(notification.getPropertyIdentifier()[0] == (short) 0x55) {
                             final BACnetTag baCnetTag = notification.getValue();
-                            if(baCnetTag instanceof BACnetTagApplicationReal) {
-                                System.out.println(baCnetTag);
+                            final PlcValue plcValue = baCnetTag.toPlcValue();
+
+                            // Initialize an enriched version of the PlcStruct.
+                            final Map<String, PlcValue> enrichedPlcValue = new HashMap<>();
+                            enrichedPlcValue.put("address", new PlcString(toString(curField)));
+                            // Add all of the existing attributes.
+                            enrichedPlcValue.putAll(plcValue.getStruct());
+
+                            // Use the information in the edeModel to enrich the information.
+                            if(edeModel != null) {
+                                final Datapoint datapoint = edeModel.getDatapoint(curField);
+                                if(datapoint != null) {
+                                    // Add all the attributes from the ede file.
+                                    enrichedPlcValue.putAll(datapoint.toPlcValues());
+                                }
                             }
-                        }
-                        // Use the information in the edeModel to enrich the information.
-                        if(edeModel != null) {
-                            // TODO: Implement.
-                        }
-                        // Else just output the information without enriching it.
-                        else {
-                            // TODO: Implement.
+                            // Send out the enriched event.
+                            publishEvent(curField, new PlcStruct(enrichedPlcValue));
                         }
                     }
                 }
@@ -196,22 +210,20 @@ public class PassiveBacNetIpProtocolLogic extends Plc4xProtocolBase<BVLC> implem
         consumerIdMap.remove(consumerRegistration.getConsumerHash());
     }
 
-    protected void publishEvent(String name, PlcValue plcValue) {
+    protected void publishEvent(BacNetIpField field, PlcValue plcValue) {
         // Create a subscription event from the input.
         final PlcSubscriptionEvent event = new DefaultPlcSubscriptionEvent(Instant.now(),
-            Collections.singletonMap(name, Pair.of(PlcResponseCode.OK, plcValue)));
+            Collections.singletonMap("event", Pair.of(PlcResponseCode.OK, plcValue)));
 
         // Send the subscription event to all listeners.
         for (Consumer<PlcSubscriptionEvent> consumer : consumerIdMap.values()) {
+            // TODO: Check if the subscription matches the current field ..
             consumer.accept(event);
         }
     }
 
-    /*protected PlcValue toPlcValue(BACnetTag tag) {
-        if(tag instanceof BACnetTagApplicationReal) {
-            BACnetTagApplicationReal baCnetTagApplicationReal = (BACnetTagApplicationReal) tag;
-            baCnetTagApplicationReal.getData();
-        }
-    }*/
+    private String toString(BacNetIpField field) {
+        return field.getDeviceIdentifier() + "/" + field.getObjectType() + "/" + field.getObjectInstance();
+    }
 
 }
diff --git a/sandbox/test-java-bacnetip-driver/src/test/java/org/apache/plc4x/java/bacnetip/PassiveBacNetIpDriverManual.java b/sandbox/test-java-bacnetip-driver/src/test/java/org/apache/plc4x/java/bacnetip/PassiveBacNetIpDriverManual.java
index 4e6f903..92b3802 100644
--- a/sandbox/test-java-bacnetip-driver/src/test/java/org/apache/plc4x/java/bacnetip/PassiveBacNetIpDriverManual.java
+++ b/sandbox/test-java-bacnetip-driver/src/test/java/org/apache/plc4x/java/bacnetip/PassiveBacNetIpDriverManual.java
@@ -19,13 +19,21 @@ under the License.
 package org.apache.plc4x.java.bacnetip;
 
 import org.apache.plc4x.java.api.PlcConnection;
+import org.apache.plc4x.java.api.messages.PlcSubscriptionResponse;
+import org.apache.plc4x.java.api.value.PlcStruct;
+import org.apache.plc4x.java.spi.messages.DefaultPlcSubscriptionEvent;
 
 public class PassiveBacNetIpDriverManual {
 
     public static void main(String[] args) throws Exception {
         final PassiveBacNetIpDriver driver = new PassiveBacNetIpDriver();
-        final PlcConnection connection = driver.getConnection("bacnet-ip:pcap:///Users/christofer.dutz/Projects/Apache/PLC4X-Documents/BacNET/Merck/Captures/BACnet.pcapng");
+        final PlcConnection connection = driver.getConnection("bacnet-ip:pcap:///Users/christofer.dutz/Projects/Apache/PLC4X-Documents/BacNET/Merck/Captures/BACnet.pcapng?ede-file-path=/Users/christofer.dutz/Projects/Apache/PLC4X-Documents/BacNET/Merck/EDE-Files/M32/edeDataText3014.csv");
         connection.connect();
+        final PlcSubscriptionResponse subscriptionResponse = connection.subscriptionRequestBuilder().addEventField("Hurz", "*/*/*").build().execute().get();
+        subscriptionResponse.getSubscriptionHandle("Hurz").register(plcSubscriptionEvent -> {
+            PlcStruct plcStruct = (PlcStruct) ((DefaultPlcSubscriptionEvent) plcSubscriptionEvent).getValues().get("event").getRight();
+            System.out.println(plcStruct);
+        });
     }
 
 }