From c1f1700932da9b2f0db523a8a2f10b1e85bde6e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20R=C3=BChl?= Date: Fri, 4 Nov 2022 14:27:55 +0100 Subject: [PATCH] feat(plc-simulator/bacnet): bacnet simulator is now able to return a valid hard coded response --- .../server/bacnet/BacnetServerModule.java | 59 ++++---- .../bacnet/protocol/BacnetServerAdapter.java | 137 ++++++++++++++++-- 2 files changed, 153 insertions(+), 43 deletions(-) diff --git a/plc4j/utils/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/bacnet/BacnetServerModule.java b/plc4j/utils/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/bacnet/BacnetServerModule.java index f5a50477871..8c69793edc2 100644 --- a/plc4j/utils/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/bacnet/BacnetServerModule.java +++ b/plc4j/utils/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/bacnet/BacnetServerModule.java @@ -19,35 +19,28 @@ package org.apache.plc4x.simulator.server.bacnet; import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; -import io.netty.channel.*; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.nio.NioDatagramChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.codec.DatagramPacketDecoder; -import io.netty.handler.codec.DatagramPacketEncoder; import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageEncoder; import org.apache.plc4x.java.bacnetip.BacNetIpDriver; import org.apache.plc4x.java.bacnetip.readwrite.BVLC; import org.apache.plc4x.java.bacnetip.readwrite.BacnetConstants; -import org.apache.plc4x.java.cbus.CBusDriver; -import org.apache.plc4x.java.cbus.readwrite.CBusConstants; -import org.apache.plc4x.java.cbus.readwrite.CBusMessage; -import org.apache.plc4x.java.cbus.readwrite.CBusOptions; -import org.apache.plc4x.java.cbus.readwrite.RequestContext; import org.apache.plc4x.java.spi.connection.GeneratedProtocolMessageCodec; import org.apache.plc4x.java.spi.generation.ByteOrder; -import org.apache.plc4x.java.spi.generation.ReadBufferByteBased; -import org.apache.plc4x.java.spi.generation.WriteBufferByteBased; import org.apache.plc4x.simulator.PlcSimulatorConfig; import org.apache.plc4x.simulator.exceptions.SimulatorException; import org.apache.plc4x.simulator.model.Context; import org.apache.plc4x.simulator.server.ServerModule; import org.apache.plc4x.simulator.server.bacnet.protocol.BacnetServerAdapter; +import java.net.InetSocketAddress; import java.util.List; public class BacnetServerModule implements ServerModule { @@ -88,24 +81,28 @@ public void start() throws SimulatorException { .handler(new ChannelInitializer() { @Override public void initChannel(NioDatagramChannel channel) { - ChannelPipeline pipeline = channel.pipeline(); - pipeline.addLast(new DatagramPacketDecoder(new MessageToMessageDecoder() { - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception { - byte[] bytes = new byte[msg.readableBytes()]; - msg.readBytes(bytes); - out.add(BVLC.staticParse(new ReadBufferByteBased(bytes))); - } - })); - pipeline.addLast(new DatagramPacketEncoder<>(new MessageToMessageEncoder() { - @Override - protected void encode(ChannelHandlerContext ctx, BVLC msg, List out) throws Exception { - WriteBufferByteBased writeBuffer = new WriteBufferByteBased(msg.getLengthInBytes()); - msg.serialize(writeBuffer); - out.add(writeBuffer.getBytes()); - } - })); - pipeline.addLast(new BacnetServerAdapter(context)); + channel.pipeline() + .addLast(new MessageToMessageDecoder() { + @Override + protected void decode(ChannelHandlerContext ctx, DatagramPacket msg, List out) throws Exception { + final ByteBuf content = msg.content(); + out.add(content.retain()); + } + }) + .addLast(new MessageToMessageEncoder() { + @Override + protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception { + msg.retain(); + // TODO: find better way to implement request response + out.add(new DatagramPacket(msg, new InetSocketAddress("192.168.178.102", 47808))); + } + }) + .addLast(new GeneratedProtocolMessageCodec<>(BVLC.class, + BVLC::staticParse, ByteOrder.BIG_ENDIAN, + null, + new BacNetIpDriver.ByteLengthEstimator(), + new BacNetIpDriver.CorruptPackageCleaner())) + .addLast(new BacnetServerAdapter(context)); } }); diff --git a/plc4j/utils/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/bacnet/protocol/BacnetServerAdapter.java b/plc4j/utils/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/bacnet/protocol/BacnetServerAdapter.java index 39da9a67ae1..92dfe3fb487 100644 --- a/plc4j/utils/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/bacnet/protocol/BacnetServerAdapter.java +++ b/plc4j/utils/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/bacnet/protocol/BacnetServerAdapter.java @@ -18,24 +18,16 @@ */ package org.apache.plc4x.simulator.server.bacnet.protocol; +import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; -import org.apache.plc4x.java.bacnetip.readwrite.BVLC; -import org.apache.plc4x.java.cbus.readwrite.*; +import org.apache.plc4x.java.bacnetip.readwrite.Error; +import org.apache.plc4x.java.bacnetip.readwrite.*; +import org.apache.plc4x.java.bacnetip.readwrite.utils.StaticHelper; import org.apache.plc4x.simulator.model.Context; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - public class BacnetServerAdapter extends ChannelInboundHandlerAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(BacnetServerAdapter.class); @@ -57,8 +49,129 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception if (!(msg instanceof BVLC)) { return; } + BVLC bvlc = (BVLC) msg; + if (!(bvlc instanceof BVLCOriginalUnicastNPDU)) { + // TODO: write useful error + ctx.writeAndFlush(new BVLCOriginalUnicastNPDU( + new NPDU( + (short) 1, + new NPDUControl(true, false, false, false, NPDUNetworkPriority.NORMAL_MESSAGE), + 0, + (short) 0, + null, + 0, + (short) 0, + null, + (short) 0, + null, + new APDUError( + (short) 0, + BACnetConfirmedServiceChoice.READ_PROPERTY, + new BACnetErrorGeneral(new Error( + new ErrorClassTagged(new BACnetTagHeader((byte) 0, TagClass.APPLICATION_TAGS, (byte) 1, (short) 0, (short) 0, 0, 0L), ErrorClass.COMMUNICATION, 0, (short) 0, TagClass.APPLICATION_TAGS), + new ErrorCodeTagged(new BACnetTagHeader((byte) 0, TagClass.APPLICATION_TAGS, (byte) 1, (short) 0, (short) 0, 0, 0L), ErrorCode.VENDOR_PROPRIETARY_VALUE, 0, (short) 0, TagClass.APPLICATION_TAGS) + )), + 0 + ), + 0 + ), + 0 + )).addListener((ChannelFutureListener) f -> { + if (!f.isSuccess()) { + f.cause().printStackTrace(); + } + }); + return; + } + BVLCOriginalUnicastNPDU bvlcOriginalUnicastNPDU = (BVLCOriginalUnicastNPDU) bvlc; + // TODO: get messageTypeField + APDU apdu = bvlcOriginalUnicastNPDU.getNpdu().getApdu(); + if (!(apdu instanceof APDUConfirmedRequest)) { + // TODO: write useful error + ctx.writeAndFlush(new BVLCOriginalUnicastNPDU( + new NPDU( + (short) 1, + new NPDUControl(true, false, false, false, NPDUNetworkPriority.NORMAL_MESSAGE), + 0, + (short) 0, + null, + 0, + (short) 0, + null, + (short) 0, + null, + new APDUError( + (short) 0, + BACnetConfirmedServiceChoice.READ_PROPERTY, + new BACnetErrorGeneral(new Error( + new ErrorClassTagged(new BACnetTagHeader((byte) 0, TagClass.APPLICATION_TAGS, (byte) 1, (short) 0, (short) 0, 0, 0L), ErrorClass.COMMUNICATION, 0, (short) 0, TagClass.APPLICATION_TAGS), + new ErrorCodeTagged(new BACnetTagHeader((byte) 0, TagClass.APPLICATION_TAGS, (byte) 1, (short) 0, (short) 0, 0, 0L), ErrorCode.VENDOR_PROPRIETARY_VALUE, 0, (short) 0, TagClass.APPLICATION_TAGS) + )), + 0 + ), + 0 + ), + 0 + )).addListener((ChannelFutureListener) f -> { + if (!f.isSuccess()) { + f.cause().printStackTrace(); + } + }); + return; + } + APDUConfirmedRequest apduConfirmedRequest = (APDUConfirmedRequest) apdu; // TODO: implement me + System.out.println("Got request"); System.out.println(msg); + BVLCOriginalUnicastNPDU response = new BVLCOriginalUnicastNPDU( + new NPDU( + (short) 1, + new NPDUControl(false, false, false, false, NPDUNetworkPriority.NORMAL_MESSAGE), + null, + null, + null, + null, + null, + null, + null, + null, + new APDUComplexAck( + false, + false, + apduConfirmedRequest.getInvokeId(), + null, + null, + new BACnetServiceAckReadProperty( + StaticHelper.createBACnetContextTagObjectIdentifier((byte) 0, 2, 1L), + StaticHelper.createBACnetPropertyIdentifierTagged((byte) 1, 85), + null, + new BACnetConstructedDataAnalogValuePresentValue( + StaticHelper.createBACnetOpeningTag((short) 3), + StaticHelper.createBACnetTagHeaderBalanced(true, (short) 3, 3L), + StaticHelper.createBACnetClosingTag((short) 3), + StaticHelper.createBACnetApplicationTagReal(101L), + null, + null + ), + 0L + ), + null, + null, + 0 + ), + 0 + ), + 0 + ); + System.out.println("Writing response"); + System.out.println(response); + ctx.writeAndFlush(response).addListener((ChannelFutureListener) f -> { + if (!f.isSuccess()) { + f.cause().printStackTrace(); + } + }); + ; } + }