You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by jf...@apache.org on 2018/10/27 11:48:19 UTC
[incubator-plc4x] 02/02: [OPM] Added Documentation,
some refactoring.
This is an automated email from the ASF dual-hosted git repository.
jfeinauer pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-plc4x.git
commit a5ae69baf1deed89ac79aeb2b152fc36e8c804e2
Author: Julian Feinauer <j....@pragmaticminds.de>
AuthorDate: Sat Oct 27 13:20:53 2018 +0200
[OPM] Added Documentation, some refactoring.
---
.../plc4x/java/opm/PlcEntityInterceptor.java | 16 ++--
.../apache/plc4x/java/opm/PlcEntityManager.java | 32 +++++++-
.../java/org/apache/plc4x/java/opm/PlcField.java | 3 +
.../utils/opm/src/site/asciidoc/opm/using-opm.adoc | 61 +++++++++++++++
plc4j/utils/opm/src/site/site.xml | 30 ++++++++
.../plc4x/java/opm/PlcEntityManagerTest.java | 87 ++++++++++++----------
6 files changed, 183 insertions(+), 46 deletions(-)
diff --git a/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcEntityInterceptor.java b/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcEntityInterceptor.java
index 48df70e..81d6126 100644
--- a/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcEntityInterceptor.java
+++ b/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcEntityInterceptor.java
@@ -74,6 +74,8 @@ public class PlcEntityInterceptor {
* @param proxy Object to intercept
* @param method Method that was intercepted
* @param callable Callable to call the method after fetching the values
+ * @param address Address of the plc (injected from private field)
+ * @param driverManager DriverManager instance to use (injected from private field)
* @return possible result of the original methods invocation
* @throws OPMException Problems with plc / proxying
*/
@@ -131,7 +133,7 @@ public class PlcEntityInterceptor {
* @param driverManager
* @throws OPMException on various errors.
*/
- private static void refetchAllFields(Object proxy, PlcDriverManager driverManager, String address) throws OPMException {
+ static void refetchAllFields(Object proxy, PlcDriverManager driverManager, String address) throws OPMException {
// Don't log o here as this would cause a second request against a plc so don't touch it, or if you log be aware of that
Class<?> entityClass = proxy.getClass().getSuperclass();
PlcEntity plcEntity = entityClass.getAnnotation(PlcEntity.class);
@@ -188,22 +190,26 @@ public class PlcEntityInterceptor {
String variable = s.substring(0, 1).toLowerCase().concat(s.substring(1));
LOGGER.trace("Looking for field with name {} after invokation of getter {}", variable, m.getName());
PlcField annotation;
+ Field field;
try {
- annotation = m.getDeclaringClass().getDeclaredField(variable).getDeclaredAnnotation(PlcField.class);
+ field = m.getDeclaringClass().getDeclaredField(variable);
+ annotation = field.getDeclaredAnnotation(PlcField.class);
} catch (NoSuchFieldException e) {
throw new OPMException("Unable to identify field annotated field for call to " + m.getName(), e);
}
try (PlcConnection connection = driverManager.getConnection(address)) {
// Catch the exception, if no reader present (see below)
- // Assume to do the query here...
+ // Use Fully qualified Name as field index
+ String fqn = field.getDeclaringClass().getName() + "." + field.getName();
+
PlcReadRequest request = connection.readRequestBuilder()
- .addItem(m.getName(), annotation.value())
+ .addItem(fqn, annotation.value())
.build();
PlcReadResponse response = getPlcReadResponse(request);
- return getTyped(m.getReturnType(), response, m.getName());
+ return getTyped(m.getReturnType(), response, fqn);
} catch (ClassCastException e) {
throw new OPMException("Unable to return response as suitable type", e);
} catch (Exception e) {
diff --git a/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcEntityManager.java b/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcEntityManager.java
index aeb7fa2..7544b29 100644
--- a/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcEntityManager.java
+++ b/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcEntityManager.java
@@ -71,9 +71,11 @@ import static net.bytebuddy.matcher.ElementMatchers.any;
* Thus, all operations on fields that are annotated with {@link PlcField} are always done against the "live" values
* from the PLC.
* <p>
- * All invocations on the getters are forwarded to the {@link PlcEntityInterceptor#intercept(Object, Method, Callable, Object)}
+ * A connected @{@link PlcEntity} can be disconnected calling {@link #disconnect(Object)}, then it behaves like the
+ * regular Pojo it was before.
+ * <p>
+ * All invocations on the getters are forwarded to the {@link PlcEntityInterceptor#intercept(Object, Method, Callable, String, PlcDriverManager)}
* method.
- * // TODO Add detach method
*/
public class PlcEntityManager {
@@ -163,10 +165,36 @@ public class PlcEntityManager {
// Set connection value into the private field
FieldUtils.writeDeclaredField(instance, PLC_ADDRESS_FIELD_NAME, address, true);
FieldUtils.writeDeclaredField(instance, DRIVER_MANAGER_FIELD_NAME, driverManager, true);
+
+ // Initially fetch all values
+ PlcEntityInterceptor.refetchAllFields(instance, driverManager, address);
+
return instance;
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
throw new OPMException("Unable to instantiate Proxy", e);
}
}
+ /**
+ * Disconnects the given instance.
+ * @param entity Instance of a PlcEntity.
+ * @throws OPMException Is thrown when the plc is already disconnected or no entity.
+ */
+ public void disconnect(Object entity) throws OPMException {
+ // Check if this is an entity
+ PlcEntity annotation = entity.getClass().getSuperclass().getAnnotation(PlcEntity.class);
+ if (annotation == null) {
+ throw new OPMException("Unable to disconnect Object, is no entity!");
+ }
+ try {
+ Object manager = FieldUtils.readDeclaredField(entity, DRIVER_MANAGER_FIELD_NAME, true);
+ if (manager == null) {
+ throw new OPMException("Instance is already disconnected!");
+ }
+ FieldUtils.writeDeclaredField(entity, DRIVER_MANAGER_FIELD_NAME, null, true);
+ } catch (IllegalAccessException e) {
+ throw new OPMException("Unbale to fetch driverManager instance on entity instance", e);
+ }
+ }
+
}
diff --git a/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcField.java b/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcField.java
index 9dd132e..cd0474e 100644
--- a/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcField.java
+++ b/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcField.java
@@ -31,4 +31,7 @@ import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface PlcField {
String value();
+ // TODO enable both annotation values in the Interceptor / Entitymanager
+ long cacheDurationMillis() default 1000;
+ boolean throwOnUnavailable() default true;
}
diff --git a/plc4j/utils/opm/src/site/asciidoc/opm/using-opm.adoc b/plc4j/utils/opm/src/site/asciidoc/opm/using-opm.adoc
new file mode 100644
index 0000000..1eb9920
--- /dev/null
+++ b/plc4j/utils/opm/src/site/asciidoc/opm/using-opm.adoc
@@ -0,0 +1,61 @@
+//
+// 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.
+//
+
+== What is Object Plc Mapping (OPM)?
+
+ASDF:
+
+....
+ public class ModbusTcpPlcConnection extends BaseModbusPlcConnection {
+
+ private static final int MODBUS_TCP_PORT = 502;
+
+ public ModbusTcpPlcConnection(InetAddress address, String params) {
+ this(new TcpSocketChannelFactory(address, MODBUS_TCP_PORT), params);
+ logger.info("Configured ModbusTcpPlcConnection with: host-name {}", address.getHostAddress());
+ }
+
+ ModbusTcpPlcConnection(ChannelFactory channelFactory, String params) {
+ super(channelFactory, params);
+ }
+
+ @Override
+ protected ChannelHandler getChannelHandler(CompletableFuture<Void> sessionSetupCompleteFuture) {
+ return new ChannelInitializer() {
+ @Override
+ protected void initChannel(Channel channel) {
+ // Build the protocol stack for communicating with the modbus protocol.
+ ChannelPipeline pipeline = channel.pipeline();
+ pipeline.addLast(new ModbusTcpProtocol());
+ pipeline.addLast(new ModbusProtocol());
+ pipeline.addLast(new Plc4XModbusProtocol());
+ }
+ };
+ }
+
+ }
+....
+
+As you can see in above example there are two constructors.
+The first one is the default, which establishes a connection using the default connector.
+As the TCP variant of the `Modbus` protocol uses normal TCP, a `TcpSocketChannelFactory` instance is used.
+However in order to test the driver, a unit- or integration-test can use the second constructor to inject a different `ChannelFactory`.
+Notice that this constructor can be package-private if the test-case is in the same package.
+Here the `TestConnectionFactory` will allow creating tests without having to worry about the physical connection and all problems that come with it.
+
+The pipeline itself is created in the `getChannelHandler` method.
+Here you have to keep in mind that the layer that is closest to the connection has to be added first, the `PLC4X Layer` last.
diff --git a/plc4j/utils/opm/src/site/site.xml b/plc4j/utils/opm/src/site/site.xml
new file mode 100644
index 0000000..e250730
--- /dev/null
+++ b/plc4j/utils/opm/src/site/site.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ ~ 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.
+ -->
+<project>
+
+ <body>
+ <menu name="Users">
+ </menu>
+ <menu name="Developers">
+ <item name="Object Plc Mapping (OPM)" href="opm/using-opm.html"/>
+ </menu>
+ </body>
+
+</project>
\ No newline at end of file
diff --git a/plc4j/utils/opm/src/test/java/org/apache/plc4x/java/opm/PlcEntityManagerTest.java b/plc4j/utils/opm/src/test/java/org/apache/plc4x/java/opm/PlcEntityManagerTest.java
index 8017d9c..19772c5 100644
--- a/plc4j/utils/opm/src/test/java/org/apache/plc4x/java/opm/PlcEntityManagerTest.java
+++ b/plc4j/utils/opm/src/test/java/org/apache/plc4x/java/opm/PlcEntityManagerTest.java
@@ -19,6 +19,7 @@
package org.apache.plc4x.java.opm;
+import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.plc4x.java.PlcDriverManager;
import org.apache.plc4x.java.api.PlcConnection;
@@ -51,6 +52,8 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.when;
@@ -88,29 +91,7 @@ public class PlcEntityManagerTest {
@Test
public void readComplexObject() throws PlcConnectionException, OPMException {
- Map<String, BaseDefaultFieldItem> map = new HashMap<>();
- String prefix = ConnectedEntity.class.getName() + ".";
- map.put(prefix + "boolVar", new DefaultBooleanFieldItem(true));
- map.put(prefix + "byteVar", new DefaultByteFieldItem((byte) 1));
- map.put(prefix + "shortVar", new DefaultShortFieldItem((short) 1));
- map.put(prefix + "intVar", new DefaultIntegerFieldItem(1));
- map.put(prefix + "longVar", new DefaultLongFieldItem(1l));
- map.put(prefix + "boxedBoolVar", new DefaultLongFieldItem(1L));
- map.put(prefix + "boxedByteVar", new DefaultByteFieldItem((byte) 1));
- map.put(prefix + "boxedShortVar", new DefaultShortFieldItem((short) 1));
- map.put(prefix + "boxedIntegerVar", new DefaultIntegerFieldItem(1));
- map.put(prefix + "boxedLongVar", new DefaultLongFieldItem(1l));
- map.put(prefix + "bigIntegerVar", new DefaultBigIntegerFieldItem(BigInteger.ONE));
- map.put(prefix + "floatVar", new DefaultFloatFieldItem(1f));
- map.put(prefix + "doubleVar", new DefaultDoubleFieldItem(1d));
- map.put(prefix + "bigDecimalVar", new DefaultBigDecimalFieldItem(BigDecimal.ONE));
- map.put(prefix + "localTimeVar", new DefaultLocalTimeFieldItem(LocalTime.of(1, 1)));
- map.put(prefix + "localDateVar", new DefaultLocalDateFieldItem(LocalDate.of(1, 1, 1)));
- map.put(prefix + "localDateTimeVar", new DefaultLocalDateTimeFieldItem(LocalDateTime.of(1, 1, 1, 1, 1)));
- map.put(prefix + "byteArrayVar", new DefaultByteArrayFieldItem(new Byte[]{0x0, 0x1}));
- map.put(prefix + "bigByteArrayVar", new DefaultByteArrayFieldItem(new Byte[]{0x0, 0x1}));
- map.put(prefix + "stringVar", new DefaultStringFieldItem("Hallo"));
- PlcEntityManager manager = getPlcEntityManager(map);
+ PlcEntityManager manager = getInitializedEntityManager();
ConnectedEntity connect = manager.read(ConnectedEntity.class, "s7://localhost:5555/0/0");
@@ -124,6 +105,19 @@ public class PlcEntityManagerTest {
@Test
public void connect_callComplexMethod() throws PlcConnectionException, OPMException {
+ PlcEntityManager manager = getInitializedEntityManager();
+
+ ConnectedEntity connect = manager.connect(ConnectedEntity.class, "s7://localhost:5555/0/0");
+
+ Assert.assertNotNull(connect);
+
+ // Call different mehtod
+ String s = connect.toString();
+
+ assertEquals("ConnectedEntity{boolVar=true, byteVar=1, shortVar=1, intVar=1, longVar=1, boxedBoolVar=true, boxedByteVar=1, boxedShortVar=1, boxedIntegerVar=1, boxedLongVar=1, bigIntegerVar=1, floatVar=1.0, doubleVar=1.0, bigDecimalVar=1, localTimeVar=01:01, localDateVar=0001-01-01, localDateTimeVar=0001-01-01T01:01, byteArrayVar=[0, 1], bigByteArrayVar=[0, 1], stringVar='Hallo'}", s);
+ }
+
+ private PlcEntityManager getInitializedEntityManager() throws PlcConnectionException {
Map<String, BaseDefaultFieldItem> map = new HashMap<>();
String prefix = ConnectedEntity.class.getName() + ".";
map.put(prefix + "boolVar", new DefaultBooleanFieldItem(true));
@@ -146,25 +140,12 @@ public class PlcEntityManagerTest {
map.put(prefix + "byteArrayVar", new DefaultByteArrayFieldItem(new Byte[]{0x0, 0x1}));
map.put(prefix + "bigByteArrayVar", new DefaultByteArrayFieldItem(new Byte[]{0x0, 0x1}));
map.put(prefix + "stringVar", new DefaultStringFieldItem("Hallo"));
- PlcEntityManager manager = getPlcEntityManager(map);
-
- ConnectedEntity connect = manager.connect(ConnectedEntity.class, "s7://localhost:5555/0/0");
-
- Assert.assertNotNull(connect);
-
- // Call different mehtod
- String s = connect.toString();
-
- assertEquals("ConnectedEntity{boolVar=true, byteVar=1, shortVar=1, intVar=1, longVar=1, boxedBoolVar=true, boxedByteVar=1, boxedShortVar=1, boxedIntegerVar=1, boxedLongVar=1, bigIntegerVar=1, floatVar=1.0, doubleVar=1.0, bigDecimalVar=1, localTimeVar=01:01, localDateVar=0001-01-01, localDateTimeVar=0001-01-01T01:01, byteArrayVar=[0, 1], bigByteArrayVar=[0, 1], stringVar='Hallo'}", s);
+ return getPlcEntityManager(map);
}
@Test
public void connect_callGetter() throws PlcConnectionException, OPMException {
- Map<String, BaseDefaultFieldItem> map = new HashMap<>();
- map.put("getIntVar", new DefaultIntegerFieldItem(1));
- map.put("getStringVar", new DefaultStringFieldItem("Hello"));
- map.put("isBoolVar", new DefaultBooleanFieldItem(true));
- PlcEntityManager manager = getPlcEntityManager(map);
+ PlcEntityManager manager = getInitializedEntityManager();
ConnectedEntity connect = manager.connect(ConnectedEntity.class, "s7://localhost:5555/0/0");
@@ -172,10 +153,38 @@ public class PlcEntityManagerTest {
// Call getter
assertEquals(1, connect.getIntVar());
- assertEquals("Hello", connect.getStringVar());
+ assertEquals("Hallo", connect.getStringVar());
assertEquals(true, connect.isBoolVar());
}
+ @Test
+ public void disconnect() throws PlcConnectionException, OPMException, IllegalAccessException {
+ PlcEntityManager manager = getInitializedEntityManager();
+
+ ConnectedEntity connected = manager.connect(ConnectedEntity.class, "s7://localhost:5555/0/0");
+
+ manager.disconnect(connected);
+
+ // Assert disconnected
+ Object o = FieldUtils.readDeclaredField(connected, PlcEntityManager.DRIVER_MANAGER_FIELD_NAME, true);
+ assertNull(o);
+
+ // Call a method and receive the result
+ // We are ok if a result is received and no NPE is thrown, then everything works as expected
+ assertNotNull(connected.toString());
+ assertNotNull(connected.getByteVar());
+ }
+
+ @Test(expected = OPMException.class)
+ public void disconnectTwice_throwsException() throws PlcConnectionException, OPMException {
+ PlcEntityManager manager = getPlcEntityManager(new HashMap<>());
+
+ ConnectedEntity connected = manager.connect(ConnectedEntity.class, "s7://localhost:5555/0/0");
+
+ manager.disconnect(connected);
+ manager.disconnect(connected);
+ }
+
private PlcEntityManager getPlcEntityManager(final Map<String, BaseDefaultFieldItem> responses) throws PlcConnectionException {
driverManager = Mockito.mock(PlcDriverManager.class);
PlcDriverManager mock = driverManager;