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 2019/11/10 18:00:43 UTC
[plc4x] branch feature/knxnet-ip updated: - Refactored the KNX
protocol to support different types of use-cases.
This is an automated email from the ASF dual-hosted git repository.
cdutz pushed a commit to branch feature/knxnet-ip
in repository https://gitbox.apache.org/repos/asf/plc4x.git
The following commit(s) were added to refs/heads/feature/knxnet-ip by this push:
new a843ce6 - Refactored the KNX protocol to support different types of use-cases.
a843ce6 is described below
commit a843ce65647b7627e7d5b2e1e010ee10475c9e0c
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Sun Nov 10 19:00:35 2019 +0100
- Refactored the KNX protocol to support different types of use-cases.
---
.../apache/plc4x/java/knxnetip/KnxNetIpDriver.java | 4 +-
.../knxnetip/connection/KnxNetIpConnection.java | 2 +
.../knxnetip/protocol/KnxNetIpPlc4xProtocol.java | 74 +++++++++++++++++++
.../knxnetip/protocol/KnxNetIpProtocolLogic.java | 83 +++++++++++-----------
.../apache/plc4x/java/knxnetip/ManualKnxNetIp.java | 2 +-
5 files changed, 122 insertions(+), 43 deletions(-)
diff --git a/sandbox/test-java-knxnetip-driver/src/main/java/org/apache/plc4x/java/knxnetip/KnxNetIpDriver.java b/sandbox/test-java-knxnetip-driver/src/main/java/org/apache/plc4x/java/knxnetip/KnxNetIpDriver.java
index 067e1ae..ff90d1a 100644
--- a/sandbox/test-java-knxnetip-driver/src/main/java/org/apache/plc4x/java/knxnetip/KnxNetIpDriver.java
+++ b/sandbox/test-java-knxnetip-driver/src/main/java/org/apache/plc4x/java/knxnetip/KnxNetIpDriver.java
@@ -22,7 +22,7 @@ import org.apache.plc4x.java.api.PlcConnection;
import org.apache.plc4x.java.api.authentication.PlcAuthentication;
import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
import org.apache.plc4x.java.knxnetip.connection.KnxNetIpConnection;
-import org.apache.plc4x.java.knxnetip.protocol.KnxNetIpProtocolLogic;
+import org.apache.plc4x.java.knxnetip.protocol.KnxNetIpPlc4xProtocol;
import org.apache.plc4x.java.spi.PlcDriver;
import java.net.InetAddress;
@@ -57,7 +57,7 @@ public class KnxNetIpDriver implements PlcDriver {
try {
InetAddress serverInetAddress = InetAddress.getByName(host);
- PlcConnection connection = new KnxNetIpConnection(serverInetAddress, params, new KnxNetIpProtocolLogic());
+ PlcConnection connection = new KnxNetIpConnection(serverInetAddress, params, new KnxNetIpPlc4xProtocol());
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
connection.close();
diff --git a/sandbox/test-java-knxnetip-driver/src/main/java/org/apache/plc4x/java/knxnetip/connection/KnxNetIpConnection.java b/sandbox/test-java-knxnetip-driver/src/main/java/org/apache/plc4x/java/knxnetip/connection/KnxNetIpConnection.java
index eda590b..adf74fd 100644
--- a/sandbox/test-java-knxnetip-driver/src/main/java/org/apache/plc4x/java/knxnetip/connection/KnxNetIpConnection.java
+++ b/sandbox/test-java-knxnetip-driver/src/main/java/org/apache/plc4x/java/knxnetip/connection/KnxNetIpConnection.java
@@ -32,6 +32,7 @@ import org.apache.plc4x.java.base.events.ConnectedEvent;
import org.apache.plc4x.java.base.events.DisconnectEvent;
import org.apache.plc4x.java.base.messages.*;
import org.apache.plc4x.java.knxnetip.model.KnxNetIpField;
+import org.apache.plc4x.java.knxnetip.protocol.KnxNetIpProtocolLogic;
import org.apache.plc4x.java.knxnetip.protocol.KnxNetIpProtocolPackets;
import java.net.InetAddress;
@@ -86,6 +87,7 @@ public class KnxNetIpConnection extends NettyPlcConnection implements PlcReader
// Unpack the ByteBuf included in the DatagramPackage.
pipeline.addLast(new DatagramUnpackingHandler());
pipeline.addLast(new KnxNetIpProtocolPackets());
+ pipeline.addLast(new KnxNetIpProtocolLogic());
pipeline.addLast(handler);
}
};
diff --git a/sandbox/test-java-knxnetip-driver/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpPlc4xProtocol.java b/sandbox/test-java-knxnetip-driver/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpPlc4xProtocol.java
new file mode 100644
index 0000000..6fc80fe
--- /dev/null
+++ b/sandbox/test-java-knxnetip-driver/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpPlc4xProtocol.java
@@ -0,0 +1,74 @@
+/*
+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.knxnetip.protocol;
+
+import io.netty.channel.ChannelHandlerContext;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.plc4x.java.base.PlcMessageToMessageCodec;
+import org.apache.plc4x.java.base.messages.PlcRequestContainer;
+import org.apache.plc4x.java.knxnetip.readwrite.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+public class KnxNetIpPlc4xProtocol extends PlcMessageToMessageCodec<KNXNetIPMessage, PlcRequestContainer> {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(KnxNetIpPlc4xProtocol.class);
+
+ @Override
+ protected void encode(ChannelHandlerContext ctx, PlcRequestContainer msg, List<Object> out) {
+
+ }
+
+ @Override
+ protected void decode(ChannelHandlerContext ctx, KNXNetIPMessage msg, List<Object> out) {
+ if(msg instanceof TunnelingRequest) {
+ TunnelingRequest tunnelingRequest = (TunnelingRequest) msg;
+ CEMIBusmonInd busmonInd = (CEMIBusmonInd) tunnelingRequest.getCemi();
+ if (busmonInd.getCemiFrame() instanceof CEMIFrameData) {
+ outputStringRepresentation((CEMIFrameData) busmonInd.getCemiFrame());
+ }
+ }
+ }
+
+ private void outputStringRepresentation(CEMIFrameData data) {
+ final KNXAddress sourceAddress = data.getSourceAddress();
+ final KNXAddress destinationAddress = data.getDestinationAddress();
+ final boolean groupAddress = data.getGroupAddress();
+ final byte[] payload = data.getData();
+ String payloadString = Hex.encodeHexString(payload);
+ if(groupAddress) {
+ final byte destAddressUpperByte = (byte)
+ ((destinationAddress.getMainGroup() << 4) | (destinationAddress.getMiddleGroup() & 0xFF));
+ final byte mainGroup = (byte) (destAddressUpperByte >> 3);
+ final byte middleGroup = (byte) (destAddressUpperByte & 7);
+ LOGGER.info(String.format("Telegram from %d.%d.%d to %d/%d/%d with payload %s",
+ sourceAddress.getMainGroup(), sourceAddress.getMiddleGroup(), sourceAddress.getSubGroup(),
+ mainGroup, middleGroup, destinationAddress.getSubGroup(),
+ payloadString));
+ } else {
+ LOGGER.info(String.format("Telegram from %d.%d.%d to %d.%d.%d with payload %s",
+ sourceAddress.getMainGroup(), sourceAddress.getMiddleGroup(), sourceAddress.getSubGroup(),
+ destinationAddress.getMainGroup(), destinationAddress.getMiddleGroup(), destinationAddress.getSubGroup(),
+ payloadString));
+ }
+ }
+
+}
diff --git a/sandbox/test-java-knxnetip-driver/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpProtocolLogic.java b/sandbox/test-java-knxnetip-driver/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpProtocolLogic.java
index b1f6b90..109829d 100644
--- a/sandbox/test-java-knxnetip-driver/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpProtocolLogic.java
+++ b/sandbox/test-java-knxnetip-driver/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpProtocolLogic.java
@@ -20,13 +20,11 @@ package org.apache.plc4x.java.knxnetip.protocol;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramChannel;
-import org.apache.commons.codec.binary.Hex;
import org.apache.plc4x.java.base.PlcMessageToMessageCodec;
import org.apache.plc4x.java.base.events.ConnectEvent;
import org.apache.plc4x.java.base.events.ConnectedEvent;
import org.apache.plc4x.java.base.events.DisconnectEvent;
import org.apache.plc4x.java.base.events.DisconnectedEvent;
-import org.apache.plc4x.java.base.messages.PlcRequestContainer;
import org.apache.plc4x.java.knxnetip.events.KnxGatewayFoundEvent;
import org.apache.plc4x.java.knxnetip.readwrite.*;
import org.apache.plc4x.java.knxnetip.readwrite.types.HostProtocolCode;
@@ -36,11 +34,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetSocketAddress;
-import java.util.Arrays;
-import java.util.List;
+import java.util.*;
import java.util.concurrent.CompletableFuture;
-public class KnxNetIpProtocolLogic extends PlcMessageToMessageCodec<KNXNetIPMessage, PlcRequestContainer> {
+public class KnxNetIpProtocolLogic extends PlcMessageToMessageCodec<KNXNetIPMessage, KNXNetIPMessage> {
private static final Logger LOGGER = LoggerFactory.getLogger(KnxNetIpProtocolLogic.class);
@@ -50,6 +47,7 @@ public class KnxNetIpProtocolLogic extends PlcMessageToMessageCodec<KNXNetIPMess
private int localPort;
private short communicationChannelId;
+ private Timer connectionStateTimer;
private CompletableFuture<Void> disconnectFuture;
@Override
@@ -82,16 +80,18 @@ public class KnxNetIpProtocolLogic extends PlcMessageToMessageCodec<KNXNetIPMess
}
@Override
- protected void encode(ChannelHandlerContext ctx, PlcRequestContainer msg, List<Object> out) {
- // Ignore ...
+ protected void encode(ChannelHandlerContext ctx, KNXNetIPMessage msg, List<Object> out) {
+ out.add(msg);
}
@Override
protected void decode(ChannelHandlerContext ctx, KNXNetIPMessage msg, List<Object> out) {
+ // Handle search responses to find the device able to provide tunneling services
if(msg instanceof SearchResponse) {
SearchResponse searchResponse = (SearchResponse) msg;
+ // Check if this device supports tunneling services.
final ServiceId tunnelingService = Arrays.stream(searchResponse.getDibSuppSvcFamilies().getServiceIds()).filter(serviceId -> serviceId instanceof KnxNetIpTunneling).findFirst().orElse(null);
- // If this service has the
+ // If this device supports this type of service, tell the driver, we found a suitable device.
if(tunnelingService != null) {
gatewayAddress = searchResponse.getDibDeviceInfo().getKnxAddress();
gatewayName = new String(searchResponse.getDibDeviceInfo().getDeviceFriendlyName()).trim();
@@ -99,35 +99,61 @@ public class KnxNetIpProtocolLogic extends PlcMessageToMessageCodec<KNXNetIPMess
gatewayAddress.getMainGroup(), gatewayAddress.getMiddleGroup(), gatewayAddress.getSubGroup()));
ctx.channel().pipeline().fireUserEventTriggered(new KnxGatewayFoundEvent());
}
- } else if(msg instanceof ConnectionResponse) {
+ }
+
+ // Handle the response to a connection request.
+ else if(msg instanceof ConnectionResponse) {
ConnectionResponse connectionResponse = (ConnectionResponse) msg;
Status status = connectionResponse.getStatus();
// Remember the communication channel id.
communicationChannelId = connectionResponse.getCommunicationChannelId();
- if(status == Status.NO_ERROR) {
+ if (status == Status.NO_ERROR) {
LOGGER.info(String.format("Connected to KNX Gateway '%s' with KNX address '%d.%d.%d'", gatewayName,
gatewayAddress.getMainGroup(), gatewayAddress.getMiddleGroup(), gatewayAddress.getSubGroup()));
ctx.channel().pipeline().fireUserEventTriggered(new ConnectedEvent());
+ // Start a timer to check the connection state every 60 seconds.
+ connectionStateTimer = new Timer();
+ connectionStateTimer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ ConnectionStateRequest connectionStateRequest =
+ new ConnectionStateRequest(communicationChannelId,
+ new HPAIControlEndpoint(HostProtocolCode.IPV4_UDP, localIPAddress, localPort));
+ ctx.channel().writeAndFlush(connectionStateRequest);
+ }
+ }, 60000, 60000);
} else {
LOGGER.error(String.format("Error connecting to KNX Gateway '%s' with KNX address '%d.%d.%d'", gatewayName,
gatewayAddress.getMainGroup(), gatewayAddress.getMiddleGroup(), gatewayAddress.getSubGroup()));
}
- } else if(msg instanceof TunnelingRequest) {
+ }
+
+ // Handle the responses to the connection state requests.
+ else if(msg instanceof ConnectionStateResponse) {
+ ConnectionStateResponse connectionStateResponse = (ConnectionStateResponse) msg;
+ if(connectionStateResponse.getStatus() != Status.NO_ERROR) {
+ LOGGER.error(String.format("Connection state problems. Got %s",
+ connectionStateResponse.getStatus().name()));
+ }
+ }
+
+ // Handle a normal tunneling request, which is delivering KNX data.
+ else if(msg instanceof TunnelingRequest) {
TunnelingRequest tunnelingRequest = (TunnelingRequest) msg;
final short curCommunicationChannelId =
tunnelingRequest.getTunnelingRequestDataBlock().getCommunicationChannelId();
// Only if the communication channel id match, do anything with the request.
- if(curCommunicationChannelId != communicationChannelId) {
+ if(curCommunicationChannelId == communicationChannelId) {
final short sequenceCounter = tunnelingRequest.getTunnelingRequestDataBlock().getSequenceCounter();
TunnelingResponse tunnelingResponse = new TunnelingResponse(
new TunnelingResponseDataBlock(communicationChannelId, sequenceCounter, Status.NO_ERROR));
ctx.channel().writeAndFlush(tunnelingResponse);
- CEMIBusmonInd busmonInd = (CEMIBusmonInd) tunnelingRequest.getCemi();
- if (busmonInd.getCemiFrame() instanceof CEMIFrameData) {
- outputStringRepresentation((CEMIFrameData) busmonInd.getCemiFrame());
- }
+ out.add(tunnelingRequest);
}
- } else if(msg instanceof DisconnectResponse) {
+ }
+
+ // Handle the cleaning up after getting the response to a disconnect request.
+ else if(msg instanceof DisconnectResponse) {
// In general we should probably check if the disconnect was successful, but in
// the end we couldn't do much if the disconnect would fail.
ctx.channel().pipeline().fireUserEventTriggered(new DisconnectedEvent());
@@ -140,27 +166,4 @@ public class KnxNetIpProtocolLogic extends PlcMessageToMessageCodec<KNXNetIPMess
}
}
- private void outputStringRepresentation(CEMIFrameData data) {
- final KNXAddress sourceAddress = data.getSourceAddress();
- final KNXAddress destinationAddress = data.getDestinationAddress();
- final boolean groupAddress = data.getGroupAddress();
- final byte[] payload = data.getData();
- String payloadString = Hex.encodeHexString(payload);
- if(groupAddress) {
- final byte destAddressUpperByte = (byte)
- ((destinationAddress.getMainGroup() << 4) | (destinationAddress.getMiddleGroup() & 0xFF));
- final byte mainGroup = (byte) (destAddressUpperByte >> 3);
- final byte middleGroup = (byte) (destAddressUpperByte & 7);
- LOGGER.info(String.format("Telegram from %d.%d.%d to %d/%d/%d with payload %s",
- sourceAddress.getMainGroup(), sourceAddress.getMiddleGroup(), sourceAddress.getSubGroup(),
- mainGroup, middleGroup, destinationAddress.getSubGroup(),
- payloadString));
- } else {
- LOGGER.info(String.format("Telegram from %d.%d.%d to %d.%d.%d with payload %s",
- sourceAddress.getMainGroup(), sourceAddress.getMiddleGroup(), sourceAddress.getSubGroup(),
- destinationAddress.getMainGroup(), destinationAddress.getMiddleGroup(), destinationAddress.getSubGroup(),
- payloadString));
- }
- }
-
}
diff --git a/sandbox/test-java-knxnetip-driver/src/test/java/org/apache/plc4x/java/knxnetip/ManualKnxNetIp.java b/sandbox/test-java-knxnetip-driver/src/test/java/org/apache/plc4x/java/knxnetip/ManualKnxNetIp.java
index 1d3dbcb..0732417 100644
--- a/sandbox/test-java-knxnetip-driver/src/test/java/org/apache/plc4x/java/knxnetip/ManualKnxNetIp.java
+++ b/sandbox/test-java-knxnetip-driver/src/test/java/org/apache/plc4x/java/knxnetip/ManualKnxNetIp.java
@@ -27,7 +27,7 @@ public class ManualKnxNetIp {
public static void main(String[] args) throws Exception {
final PlcConnection connection = new PlcDriverManager().getConnection("knxnet-ip://192.168.42.11");
- TimeUnit.SECONDS.sleep(30);
+ TimeUnit.SECONDS.sleep(300);
connection.close();
}