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

sruehl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-plc4x.git

commit c326e36d5ca3b038226c7e924e1678ecf286eeb1
Author: Sebastian Rühl <sru...@apache.org>
AuthorDate: Thu Jul 5 14:18:33 2018 +0200

    added modbus connection tests
---
 .../modbus/connection/BaseModbusPlcConnection.java |  24 +-
 .../modbus/connection/ModbusTcpPlcConnection.java  |   4 +
 .../{CoilAddress.java => CoilModbusAddress.java}   |   8 +-
 .../java/modbus/netty/Plc4XModbusProtocol.java     |  14 +-
 .../connection/BaseModbusPlcConnectionTest.java    | 166 ++++++++++++
 .../connection/ModbusConnectionFactoryTest.java    |  95 +++++++
 .../connection/ModbusSerialPlcConnectionTest.java  | 286 +++++++++++++++++++++
 .../connection/ModbusTcpPlcConnectionTests.java    | 134 ++++++++++
 8 files changed, 711 insertions(+), 20 deletions(-)

diff --git 
a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/connection/BaseModbusPlcConnection.java
 
b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/connection/BaseModbusPlcConnection.java
index 3c65ee6..0056363 100644
--- 
a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/connection/BaseModbusPlcConnection.java
+++ 
b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/connection/BaseModbusPlcConnection.java
@@ -18,6 +18,7 @@ under the License.
 */
 package org.apache.plc4x.java.modbus.connection;
 
+import io.netty.channel.ChannelFuture;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.plc4x.java.api.connection.PlcReader;
 import org.apache.plc4x.java.api.connection.PlcWriter;
@@ -65,8 +66,8 @@ public abstract class BaseModbusPlcConnection extends 
AbstractPlcConnection impl
             return ReadHoldingRegistersModbusAddress.of(addressString);
         } else if 
(ReadInputRegistersModbusAddress.ADDRESS_PATTERN.matcher(addressString).matches())
 {
             return ReadInputRegistersModbusAddress.of(addressString);
-        } else if 
(CoilAddress.ADDRESS_PATTERN.matcher(addressString).matches()) {
-            return CoilAddress.of(addressString);
+        } else if 
(CoilModbusAddress.ADDRESS_PATTERN.matcher(addressString).matches()) {
+            return CoilModbusAddress.of(addressString);
         } else if 
(RegisterAddress.ADDRESS_PATTERN.matcher(addressString).matches()) {
             return RegisterAddress.of(addressString);
         }
@@ -76,19 +77,24 @@ public abstract class BaseModbusPlcConnection extends 
AbstractPlcConnection impl
     @Override
     public CompletableFuture<PlcReadResponse> read(PlcReadRequest readRequest) 
{
         CompletableFuture<PlcReadResponse> readFuture = new 
CompletableFuture<>();
-        PlcRequestContainer<PlcReadRequest, PlcReadResponse> container =
-            new PlcRequestContainer<>(readRequest, readFuture);
-        channel.writeAndFlush(container);
+        ChannelFuture channelFuture = channel.writeAndFlush(new 
PlcRequestContainer<>(readRequest, readFuture));
+        channelFuture.addListener(future -> {
+            if (!future.isSuccess()) {
+                readFuture.completeExceptionally(future.cause());
+            }
+        });
         return readFuture;
     }
 
     @Override
     public CompletableFuture<PlcWriteResponse> write(PlcWriteRequest 
writeRequest) {
         CompletableFuture<PlcWriteResponse> writeFuture = new 
CompletableFuture<>();
-        PlcRequestContainer<PlcWriteRequest, PlcWriteResponse> container =
-            new PlcRequestContainer<>(writeRequest, writeFuture);
-        channel.writeAndFlush(container);
+        ChannelFuture channelFuture = channel.writeAndFlush(new 
PlcRequestContainer<>(writeRequest, writeFuture));
+        channelFuture.addListener(future -> {
+            if (!future.isSuccess()) {
+                writeFuture.completeExceptionally(future.cause());
+            }
+        });
         return writeFuture;
     }
-
 }
diff --git 
a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/connection/ModbusTcpPlcConnection.java
 
b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/connection/ModbusTcpPlcConnection.java
index c418031..92f5337 100644
--- 
a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/connection/ModbusTcpPlcConnection.java
+++ 
b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/connection/ModbusTcpPlcConnection.java
@@ -71,4 +71,8 @@ public class ModbusTcpPlcConnection extends 
BaseModbusPlcConnection {
             }
         };
     }
+
+    public InetAddress getRemoteAddress() {
+        return ((TcpSocketChannelFactory) channelFactory).getAddress();
+    }
 }
diff --git 
a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/CoilAddress.java
 
b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/CoilModbusAddress.java
similarity index 86%
rename from 
plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/CoilAddress.java
rename to 
plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/CoilModbusAddress.java
index fb83da9..414c968 100644
--- 
a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/CoilAddress.java
+++ 
b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/CoilModbusAddress.java
@@ -23,20 +23,20 @@ import 
org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-public class CoilAddress extends ModbusAddress {
+public class CoilModbusAddress extends ModbusAddress {
 
     public static final Pattern ADDRESS_PATTERN = Pattern.compile("coil:" + 
ModbusAddress.ADDRESS_PATTERN);
 
-    protected CoilAddress(int address) {
+    protected CoilModbusAddress(int address) {
         super(address);
     }
 
-    public static CoilAddress of(String addressString) {
+    public static CoilModbusAddress of(String addressString) {
         Matcher matcher = ADDRESS_PATTERN.matcher(addressString);
         if (!matcher.matches()) {
             throw new PlcRuntimeException(addressString + " doesn't match" + 
ADDRESS_PATTERN);
         }
         int address = Integer.valueOf(matcher.group("address"));
-        return new CoilAddress(address);
+        return new CoilModbusAddress(address);
     }
 }
diff --git 
a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocol.java
 
b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocol.java
index 288cda7..8f751fa 100644
--- 
a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocol.java
+++ 
b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocol.java
@@ -89,15 +89,15 @@ public class Plc4XModbusProtocol extends 
MessageToMessageCodec<ModbusTcpPayload,
                 int intToWrite = register[0] << 8 | register[1];
                 modbusRequest = new 
WriteSingleRegisterRequest(registerAddress.getAddress(), intToWrite);
             }
-        } else if (address instanceof CoilAddress) {
-            CoilAddress coilAddress = (CoilAddress) address;
+        } else if (address instanceof CoilModbusAddress) {
+            CoilModbusAddress coilModbusAddress = (CoilModbusAddress) address;
             if (quantity > 1) {
                 byte[] bytesToWrite = 
produceCoilValue(writeRequestItem.getValues());
-                modbusRequest = new 
WriteMultipleCoilsRequest(coilAddress.getAddress(), quantity, bytesToWrite);
+                modbusRequest = new 
WriteMultipleCoilsRequest(coilModbusAddress.getAddress(), quantity, 
bytesToWrite);
             } else {
                 byte[] coil = produceCoilValue(writeRequestItem.getValues());
                 boolean booleanToWrite = (coil[0] >> 8) == 1;
-                modbusRequest = new 
WriteSingleCoilRequest(coilAddress.getAddress(), booleanToWrite);
+                modbusRequest = new 
WriteSingleCoilRequest(coilModbusAddress.getAddress(), booleanToWrite);
             }
         } else {
             throw new PlcProtocolException("Unsupported address type" + 
address.getClass());
@@ -119,9 +119,9 @@ public class Plc4XModbusProtocol extends 
MessageToMessageCodec<ModbusTcpPayload,
 
         ModbusAddress address = (ModbusAddress) readRequestItem.getAddress();
         ModbusPdu modbusRequest;
-        if (address instanceof CoilAddress) {
-            CoilAddress coilAddress = (CoilAddress) address;
-            modbusRequest = new ReadCoilsRequest(coilAddress.getAddress(), 
quantity);
+        if (address instanceof CoilModbusAddress) {
+            CoilModbusAddress coilModbusAddress = (CoilModbusAddress) address;
+            modbusRequest = new 
ReadCoilsRequest(coilModbusAddress.getAddress(), quantity);
         } else if (address instanceof RegisterAddress) {
             RegisterAddress registerAddress = (RegisterAddress) address;
             modbusRequest = new 
ReadHoldingRegistersRequest(registerAddress.getAddress(), quantity);
diff --git 
a/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/connection/BaseModbusPlcConnectionTest.java
 
b/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/connection/BaseModbusPlcConnectionTest.java
new file mode 100644
index 0000000..9c00a4b
--- /dev/null
+++ 
b/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/connection/BaseModbusPlcConnectionTest.java
@@ -0,0 +1,166 @@
+/*
+ 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.modbus.connection;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelHandler;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.GenericFutureListener;
+import org.apache.plc4x.java.api.messages.PlcReadRequest;
+import org.apache.plc4x.java.api.messages.PlcReadResponse;
+import org.apache.plc4x.java.api.messages.PlcWriteRequest;
+import org.apache.plc4x.java.api.messages.PlcWriteResponse;
+import org.apache.plc4x.java.api.messages.specific.TypeSafePlcReadRequest;
+import org.apache.plc4x.java.api.messages.specific.TypeSafePlcReadResponse;
+import org.apache.plc4x.java.api.messages.specific.TypeSafePlcWriteRequest;
+import org.apache.plc4x.java.api.messages.specific.TypeSafePlcWriteResponse;
+import org.apache.plc4x.java.base.connection.ChannelFactory;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.plc4x.java.base.util.Junit5Backport.assertThrows;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+@SuppressWarnings("unchecked")
+public class BaseModbusPlcConnectionTest {
+
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(BaseModbusPlcConnectionTest.class);
+
+    private BaseModbusPlcConnection SUT;
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private ChannelFactory channelFactory;
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Channel channel;
+
+    @Before
+    public void setUp() throws Exception {
+        SUT = new BaseModbusPlcConnection(channelFactory, null) {
+            @Override
+            protected ChannelHandler getChannelHandler(CompletableFuture<Void> 
sessionSetupCompleteFuture) {
+                return null;
+            }
+        };
+
+        when(channelFactory.createChannel(any())).thenReturn(channel);
+
+        SUT.connect();
+    }
+
+    @Test
+    public void lazyConstructor() {
+        new BaseModbusPlcConnection(channelFactory, null) {
+            @Override
+            protected ChannelHandler getChannelHandler(CompletableFuture<Void> 
sessionSetupCompleteFuture) {
+                return null;
+            }
+        };
+    }
+
+    @Test
+    public void read() {
+        CompletableFuture<PlcReadResponse> read = 
SUT.read(mock(PlcReadRequest.class));
+        assertNotNull(read);
+        CompletableFuture<TypeSafePlcReadResponse<Object>> typeSafeRead = 
SUT.read(mock(TypeSafePlcReadRequest.class));
+        assertNotNull(typeSafeRead);
+
+        simulatePipelineError(() -> SUT.read(mock(PlcReadRequest.class)));
+        simulatePipelineError(() -> 
SUT.read(mock(TypeSafePlcReadRequest.class)));
+    }
+
+    @Test
+    public void write() {
+        CompletableFuture<PlcWriteResponse> write = 
SUT.write(mock(PlcWriteRequest.class));
+        assertNotNull(write);
+        CompletableFuture<TypeSafePlcWriteResponse<Object>> typeSafeWrite = 
SUT.write(mock(TypeSafePlcWriteRequest.class));
+        assertNotNull(typeSafeWrite);
+
+        simulatePipelineError(() -> SUT.write(mock(PlcWriteRequest.class)));
+        simulatePipelineError(() -> 
SUT.write(mock(TypeSafePlcWriteRequest.class)));
+    }
+
+    public void simulatePipelineError(FutureProducingTestRunnable 
futureProducingTestRunnable) {
+        ChannelFuture channelFuture = mock(ChannelFuture.class);
+        // Simulate error in the pipeline
+        when(channelFuture.addListener(any())).thenAnswer(invocation -> {
+            Future future = mock(Future.class);
+            when(future.isSuccess()).thenReturn(false);
+            when(future.cause()).thenReturn(new DummyException());
+            GenericFutureListener genericFutureListener = 
invocation.getArgument(0);
+            genericFutureListener.operationComplete(future);
+            return mock(ChannelFuture.class);
+        });
+        when(channel.writeAndFlush(any())).thenReturn(channelFuture);
+        assertThrows(DummyException.class, () -> {
+            CompletableFuture completableFuture = 
futureProducingTestRunnable.run();
+            try {
+                completableFuture.get(3, TimeUnit.SECONDS);
+                fail("Should have thrown a ExecutionException");
+            } catch (ExecutionException e) {
+                if (e.getCause() instanceof DummyException) {
+                    throw (DummyException) e.getCause();
+                }
+                throw e;
+            }
+        });
+    }
+
+    @Test
+    public void testToString() {
+        String s = SUT.toString();
+        assertNotNull(s);
+    }
+
+    /**
+     * Variant of {@link Runnable} which adds a {@code throws Exception} to 
the {@code run} signature.
+     */
+    private interface TestRunnable {
+        /**
+         * @throws Exception when the test throws a exception.
+         * @see Runnable#run()
+         */
+        void run() throws Exception;
+    }
+
+    private static class DummyException extends Exception {
+
+    }
+
+    @FunctionalInterface
+    private interface FutureProducingTestRunnable {
+        CompletableFuture run() throws Exception;
+    }
+}
\ No newline at end of file
diff --git 
a/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/connection/ModbusConnectionFactoryTest.java
 
b/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/connection/ModbusConnectionFactoryTest.java
new file mode 100644
index 0000000..c4e3711
--- /dev/null
+++ 
b/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/connection/ModbusConnectionFactoryTest.java
@@ -0,0 +1,95 @@
+/*
+ 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.modbus.connection;
+
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.plc4x.java.base.connection.AbstractPlcConnection;
+import org.apache.plc4x.java.base.connection.SerialChannelFactory;
+import org.apache.plc4x.java.base.connection.TcpSocketChannelFactory;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.net.InetAddress;
+
+import static org.apache.plc4x.java.base.util.Junit5Backport.assertThrows;
+import static org.junit.Assert.assertEquals;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ModbusConnectionFactoryTest {
+
+    @InjectMocks
+    private ModbusConnectionFactory SUT;
+
+    @Mock
+    private InetAddress inetAddress;
+
+
+    @Test
+    public void modbusTcpPlcConnectionOf() throws Exception {
+        {
+            assertThrows(NullPointerException.class, () -> 
SUT.modbusTcpPlcConnectionOf(null, null, null));
+        }
+        {
+            ModbusTcpPlcConnection modbusTcpPlcConnection = 
SUT.modbusTcpPlcConnectionOf(inetAddress, null, null);
+            assertGeneratedPort(modbusTcpPlcConnection);
+        }
+        {
+            ModbusTcpPlcConnection modbusTcpPlcConnection = 
SUT.modbusTcpPlcConnectionOf(inetAddress, 13, null);
+            assertEquals(inetAddress, 
modbusTcpPlcConnection.getRemoteAddress());
+            assertPort(modbusTcpPlcConnection, 13);
+        }
+        {
+            ModbusTcpPlcConnection modbusTcpPlcConnection = 
SUT.modbusTcpPlcConnectionOf(inetAddress, null, "xyz");
+            assertEquals(inetAddress, 
modbusTcpPlcConnection.getRemoteAddress());
+            assertGeneratedPort(modbusTcpPlcConnection);
+        }
+    }
+
+    public void assertGeneratedPort(ModbusTcpPlcConnection 
modbusTcpPlcConnection) throws Exception {
+        assertPort(modbusTcpPlcConnection, 502);
+    }
+
+    public void assertPort(ModbusTcpPlcConnection modbusTcpPlcConnection, int 
port) throws Exception {
+        TcpSocketChannelFactory channelFactory = (TcpSocketChannelFactory) 
FieldUtils
+            .getDeclaredField(AbstractPlcConnection.class, "channelFactory", 
true)
+            .get(modbusTcpPlcConnection);
+        assertEquals(port, channelFactory.getPort());
+    }
+
+    @Test
+    public void modbusSerialPlcConnectionOf() throws Exception {
+        {
+            assertThrows(NullPointerException.class, () -> 
SUT.modbusSerialPlcConnectionOf(null, null));
+        }
+        {
+            ModbusSerialPlcConnection modbusSerialPlcConnection = 
SUT.modbusSerialPlcConnectionOf("/dev/ttyS01", null);
+            assertPort(modbusSerialPlcConnection, "/dev/ttyS01");
+        }
+    }
+
+    public void assertPort(ModbusSerialPlcConnection 
modbusSerialPlcConnection, String serialPort) throws Exception {
+        SerialChannelFactory channelFactory = (SerialChannelFactory) FieldUtils
+            .getDeclaredField(AbstractPlcConnection.class, "channelFactory", 
true)
+            .get(modbusSerialPlcConnection);
+        assertEquals(serialPort, channelFactory.getSerialPort());
+    }
+}
\ No newline at end of file
diff --git 
a/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/connection/ModbusSerialPlcConnectionTest.java
 
b/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/connection/ModbusSerialPlcConnectionTest.java
new file mode 100644
index 0000000..4339c42
--- /dev/null
+++ 
b/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/connection/ModbusSerialPlcConnectionTest.java
@@ -0,0 +1,286 @@
+/*
+ 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.modbus.connection;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.embedded.EmbeddedChannel;
+import io.netty.channel.jsc.JSerialCommDeviceAddress;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.commons.lang3.reflect.MethodUtils;
+import org.apache.plc4x.java.api.messages.PlcReadRequest;
+import org.apache.plc4x.java.api.messages.PlcReadResponse;
+import org.apache.plc4x.java.base.connection.AbstractPlcConnection;
+import org.apache.plc4x.java.base.connection.SerialChannelFactory;
+import org.apache.plc4x.java.modbus.model.*;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+@Ignore("Not yet implemented in modbus")
+public class ModbusSerialPlcConnectionTest {
+
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(ModbusSerialPlcConnectionTest.class);
+
+    private ModbusSerialPlcConnection SUT;
+
+    @Before
+    public void setUp() {
+        SUT = ModbusSerialPlcConnection.of("/dev/tty0", null);
+    }
+
+    @After
+    public void tearDown() {
+        SUT = null;
+    }
+
+    @Test
+    public void emptyParseAddress() {
+        try {
+            SUT.parseAddress("");
+        } catch (IllegalArgumentException exception) {
+            assertTrue("Unexpected exception", 
exception.getMessage().startsWith("address  doesn't match "));
+        }
+    }
+
+    @Test
+    public void parseCoilModbusAddress() {
+        try {
+            CoilModbusAddress address = (CoilModbusAddress) 
SUT.parseAddress("0/1");
+            assertEquals(address.getAddress(), 0);
+        } catch (IllegalArgumentException exception) {
+            fail("valid data block address");
+        }
+    }
+
+    @Test
+    public void parseMaskWriteRegisterModbusAddress() {
+        try {
+            MaskWriteRegisterModbusAddress address = 
(MaskWriteRegisterModbusAddress) SUT.parseAddress("0/1");
+            assertEquals(address.getAddress(), 0);
+        } catch (IllegalArgumentException exception) {
+            fail("valid data block address");
+        }
+    }
+
+    @Test
+    public void parseReadDiscreteInputsModbusAddress() {
+        try {
+            ReadDiscreteInputsModbusAddress address = 
(ReadDiscreteInputsModbusAddress) SUT.parseAddress("0/1");
+            assertEquals(address.getAddress(), 0);
+        } catch (IllegalArgumentException exception) {
+            fail("valid data block address");
+        }
+    }
+
+    @Test
+    public void parseReadHoldingRegistersModbusAddress() {
+        try {
+            ReadHoldingRegistersModbusAddress address = 
(ReadHoldingRegistersModbusAddress) SUT.parseAddress("0/1");
+            assertEquals(address.getAddress(), 0);
+        } catch (IllegalArgumentException exception) {
+            fail("valid data block address");
+        }
+    }
+
+    @Test
+    public void parseReadInputRegistersModbusAddress() {
+        try {
+            ReadInputRegistersModbusAddress address = 
(ReadInputRegistersModbusAddress) SUT.parseAddress("0/1");
+            assertEquals(address.getAddress(), 0);
+        } catch (IllegalArgumentException exception) {
+            fail("valid data block address");
+        }
+    }
+
+    @Test
+    public void parseRegisterAddress() {
+        try {
+            RegisterAddress address = (RegisterAddress) 
SUT.parseAddress("0/1");
+            assertEquals(address.getAddress(), 0);
+        } catch (IllegalArgumentException exception) {
+            fail("valid data block address");
+        }
+    }
+
+    @Test
+    public void testRead() throws Exception {
+        prepareSerialSimulator();
+        CompletableFuture<PlcReadResponse> read = SUT.read(new 
PlcReadRequest(String.class, SUT.parseAddress("0/0")));
+        PlcReadResponse plcReadResponse = read.get(30, TimeUnit.SECONDS);
+        assertNotNull(plcReadResponse);
+    }
+
+    private void prepareSerialSimulator() throws Exception {
+        Field channelFactoryField = 
FieldUtils.getField(AbstractPlcConnection.class, "channelFactory", true);
+        SerialChannelFactory serialChannelFactory = (SerialChannelFactory) 
channelFactoryField.get(SUT);
+        SerialChannelFactory serialChannelFactorySpied = 
spy(serialChannelFactory);
+        EmbeddedChannel embeddedChannel = new 
EmbeddedChannel(SUT.getChannelHandler(null));
+        embeddedChannel.connect(new JSerialCommDeviceAddress("/dev/tty0"));
+        
doReturn(embeddedChannel).when(serialChannelFactorySpied).createChannel(any());
+        channelFactoryField.set(SUT, serialChannelFactorySpied);
+        SUT.connect();
+        new SerialSimulator(embeddedChannel).start();
+    }
+
+    private class SerialSimulator extends Thread {
+
+        private EmbeddedChannel embeddedChannel;
+
+        private SimulatorState state = SimulatorState.RECEIVE_REQUEST;
+
+        private byte[] currentInvokeId = new byte[0];
+
+        public SerialSimulator(EmbeddedChannel embeddedChannel) {
+            super("Serial Simulator");
+            this.embeddedChannel = embeddedChannel;
+        }
+
+        @Override
+        public void run() {
+            while (true) {
+                LOGGER.trace("in state {}. CurrentInvokeId: {}", state, 
currentInvokeId);
+                switch (state) {
+                    // Receiving state
+                    case RECEIVE_REQUEST: {
+                        LOGGER.info("Waiting for normal message");
+                        ByteBuf outputBuffer;
+                        while ((outputBuffer = embeddedChannel.readOutbound()) 
== null) {
+                            LOGGER.trace("No buffer available yet");
+                            if (!trySleep()) {
+                                return;
+                            }
+                        }
+                        // TODO
+                        int headerBytes = 4711;
+                        LOGGER.info("Skipping " + headerBytes + " bytes");
+                        outputBuffer.skipBytes(headerBytes);
+                        short dataLength = outputBuffer.readUnsignedByte();
+                        LOGGER.info("Expect at least " + dataLength + "bytes");
+                        while (outputBuffer.readableBytes() < dataLength) {
+                            if (!trySleep()) {
+                                return;
+                            }
+                        }
+                        byte[] bytes = new byte[dataLength];
+                        LOGGER.info("Read " + dataLength + "bytes. Having " + 
outputBuffer.readableBytes() + "bytes");
+                        outputBuffer.readBytes(bytes);
+                        currentInvokeId = Arrays.copyOfRange(bytes, 28, 32);
+                        // TODO
+                        outputBuffer.skipBytes(4711);
+                        LOGGER.info("Wrote Inbound");
+                        state = SimulatorState.ACK_MESSAGE;
+                        if (!trySleep()) {
+                            return;
+                        }
+                    }
+                    break;
+                    case ACK_MESSAGE: {
+                        // TODO
+                        ByteBuf byteBuf = Unpooled.buffer();
+                        try {
+                            MethodUtils.invokeMethod(byteBuf, true, 
"setRefCnt", 2);
+                        } catch (NoSuchMethodException | 
IllegalAccessException | InvocationTargetException e) {
+                            throw new RuntimeException(e);
+                        }
+                        embeddedChannel.writeOneInbound(byteBuf);
+                        LOGGER.info("Acked Message");
+                        state = SimulatorState.SEND_RESPONSE;
+                    }
+                    case SEND_RESPONSE: {
+                        LOGGER.info("Sending data message");
+                        //TODO:
+                        ByteBuf byteBuf = Unpooled.buffer();
+                        try {
+                            MethodUtils.invokeMethod(byteBuf, true, 
"setRefCnt", 2);
+                        } catch (NoSuchMethodException | 
IllegalAccessException | InvocationTargetException e) {
+                            throw new RuntimeException(e);
+                        }
+                        embeddedChannel.writeOneInbound(byteBuf);
+                        LOGGER.info("Wrote Inbound");
+                        state = SimulatorState.WAIT_FOR_ACK;
+                        if (!trySleep()) {
+                            return;
+                        }
+                    }
+                    break;
+                    case WAIT_FOR_ACK: {
+                        LOGGER.info("Waiting for ack message");
+                        ByteBuf outputBuffer;
+                        while ((outputBuffer = embeddedChannel.readOutbound()) 
== null) {
+                            if (!trySleep()) {
+                                return;
+                            }
+                        }
+                        //TODO:
+                        int headerBytes = 4711;
+                        LOGGER.info("Skipping " + headerBytes + " bytes");
+                        outputBuffer.skipBytes(headerBytes);
+                        short dataLength = outputBuffer.readUnsignedByte();
+                        LOGGER.info("Expect " + dataLength + "bytes");
+                        state = SimulatorState.DONE;
+                        if (!trySleep()) {
+                            return;
+                        }
+                    }
+                    case DONE: {
+                        LOGGER.info("Plc is Done. Goodbye");
+                        return;
+                    }
+                    default:
+                        throw new IllegalStateException("Illegal state number" 
+ state);
+                }
+            }
+
+        }
+
+        private boolean trySleep() {
+            try {
+                TimeUnit.MILLISECONDS.sleep(10);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+                Thread.currentThread().interrupt();
+                return false;
+            }
+            return true;
+        }
+    }
+
+    private enum SimulatorState {
+        RECEIVE_REQUEST,
+        ACK_MESSAGE,
+        SEND_RESPONSE,
+        WAIT_FOR_ACK,
+        DONE
+    }
+}
\ No newline at end of file
diff --git 
a/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/connection/ModbusTcpPlcConnectionTests.java
 
b/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/connection/ModbusTcpPlcConnectionTests.java
new file mode 100644
index 0000000..10293e3
--- /dev/null
+++ 
b/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/connection/ModbusTcpPlcConnectionTests.java
@@ -0,0 +1,134 @@
+/*
+ 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.modbus.connection;
+
+import io.netty.channel.Channel;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.plc4x.java.modbus.model.*;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.InetAddress;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
+
+public class ModbusTcpPlcConnectionTests {
+
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(ModbusTcpPlcConnectionTests.class);
+
+    private ModbusTcpPlcConnection SUT;
+
+    private Channel channelMock;
+
+    private ExecutorService executorService;
+
+    @Before
+    public void setUp() throws Exception {
+        SUT = ModbusTcpPlcConnection.of(InetAddress.getByName("localhost"), 
null);
+        channelMock = mock(Channel.class, RETURNS_DEEP_STUBS);
+        FieldUtils.writeField(SUT, "channel", channelMock, true);
+        executorService = Executors.newFixedThreadPool(10);
+    }
+
+    @After
+    public void tearDown() {
+        executorService.shutdownNow();
+        SUT = null;
+    }
+
+    @Test
+    public void emptyParseAddress() {
+        try {
+            SUT.parseAddress("");
+        } catch (IllegalArgumentException exception) {
+            assertTrue("Unexpected exception", 
exception.getMessage().startsWith("address  doesn't match "));
+        }
+    }
+
+    @Test
+    public void parseCoilModbusAddress() {
+        try {
+            CoilModbusAddress address = (CoilModbusAddress) 
SUT.parseAddress("coil:0");
+            assertEquals(address.getAddress(), 0);
+        } catch (IllegalArgumentException exception) {
+            fail("valid data block address");
+        }
+    }
+
+    @Test
+    public void parseMaskWriteRegisterModbusAddress() {
+        try {
+            MaskWriteRegisterModbusAddress address = 
(MaskWriteRegisterModbusAddress) SUT.parseAddress("maskwrite:1/2/3");
+            assertEquals(address.getAddress(), 1);
+            assertEquals(address.getAndMask(), 2);
+            assertEquals(address.getOrMask(), 3);
+        } catch (IllegalArgumentException exception) {
+            fail("valid data block address");
+        }
+    }
+
+    @Test
+    public void parseReadDiscreteInputsModbusAddress() {
+        try {
+            ReadDiscreteInputsModbusAddress address = 
(ReadDiscreteInputsModbusAddress) SUT.parseAddress("readdiscreteinputs:0");
+            assertEquals(address.getAddress(), 0);
+        } catch (IllegalArgumentException exception) {
+            fail("valid data block address");
+        }
+    }
+
+    @Test
+    public void parseReadHoldingRegistersModbusAddress() {
+        try {
+            ReadHoldingRegistersModbusAddress address = 
(ReadHoldingRegistersModbusAddress) SUT.parseAddress("readholdingregisters:0");
+            assertEquals(address.getAddress(), 0);
+        } catch (IllegalArgumentException exception) {
+            fail("valid data block address");
+        }
+    }
+
+    @Test
+    public void parseReadInputRegistersModbusAddress() {
+        try {
+            ReadInputRegistersModbusAddress address = 
(ReadInputRegistersModbusAddress) SUT.parseAddress("readinputregisters:0");
+            assertEquals(address.getAddress(), 0);
+        } catch (IllegalArgumentException exception) {
+            fail("valid data block address");
+        }
+    }
+
+    @Test
+    public void parseRegisterAddress() {
+        try {
+            RegisterAddress address = (RegisterAddress) 
SUT.parseAddress("register:0");
+            assertEquals(address.getAddress(), 0);
+        } catch (IllegalArgumentException exception) {
+            fail("valid data block address");
+        }
+    }
+
+}
\ No newline at end of file

Reply via email to