From d0adc91e4b1a6c4ae1e02924fc034fb7d4da5fa0 Mon Sep 17 00:00:00 2001 From: Robert von Burg Date: Wed, 5 Feb 2020 22:59:48 +0100 Subject: [PATCH] [New] Implemented implicit virtual connections and added verbose logging --- .../strolch/plc/core/DefaultPlcHandler.java | 25 +++-- .../li/strolch/plc/core/hw/DefaultPlc.java | 52 ++++++++-- .../main/java/li/strolch/plc/core/hw/Plc.java | 2 + .../VirtualBooleanValueConnection.java | 17 ---- .../VirtualStringValueConnection.java | 17 ---- .../core/hw/i2c/PCF8574InputConnection.java | 9 ++ .../li/strolch/plc/core/PlcHandlerTest.java | 59 +++++++++-- .../test-runtime/data/defaultModel.xml | 98 ++++++++++++++----- .../plc/gw/client/PlcGwClientHandler.java | 8 +- .../plc/gw/server/PlcGwServerHandler.java | 2 +- 10 files changed, 201 insertions(+), 88 deletions(-) delete mode 100644 strolch-plc-core/src/main/java/li/strolch/plc/core/hw/connections/VirtualBooleanValueConnection.java delete mode 100644 strolch-plc-core/src/main/java/li/strolch/plc/core/hw/connections/VirtualStringValueConnection.java diff --git a/strolch-plc-core/src/main/java/li/strolch/plc/core/DefaultPlcHandler.java b/strolch-plc-core/src/main/java/li/strolch/plc/core/DefaultPlcHandler.java index 7e072d1..e07e2cb 100644 --- a/strolch-plc-core/src/main/java/li/strolch/plc/core/DefaultPlcHandler.java +++ b/strolch-plc-core/src/main/java/li/strolch/plc/core/DefaultPlcHandler.java @@ -36,6 +36,7 @@ public class DefaultPlcHandler extends StrolchComponent implements PlcHandler, P private MapOfMaps plcTelegrams; private Map addressesToResourceId; private PlcListener globalListener; + private boolean verbose; public DefaultPlcHandler(ComponentContainer container, String componentName) { super(container, componentName); @@ -86,6 +87,7 @@ public class DefaultPlcHandler extends StrolchComponent implements PlcHandler, P this.plcAddresses = new MapOfMaps<>(); this.plcTelegrams = new MapOfMaps<>(); this.addressesToResourceId = new HashMap<>(); + this.verbose = configuration.getBoolean("verbose", false); super.initialize(configuration); } @@ -136,6 +138,7 @@ public class DefaultPlcHandler extends StrolchComponent implements PlcHandler, P MapOfMaps plcTelegrams = new MapOfMaps<>(); Map addressesToResourceId = new HashMap<>(); this.plc = configure(validateCtx(), plcAddresses, plcTelegrams, addressesToResourceId); + this.plc.setVerbose(this.verbose); this.plcAddresses = plcAddresses; this.plcTelegrams = plcTelegrams; this.addressesToResourceId = addressesToResourceId; @@ -199,7 +202,9 @@ public class DefaultPlcHandler extends StrolchComponent implements PlcHandler, P } private void updatePlcAddress(PlcAddress address, Object value) { - long s = nanoTime(); + long s = 0L; + if (this.verbose) + s = nanoTime(); String addressId = this.addressesToResourceId.get(address); if (addressId == null) { @@ -213,13 +218,13 @@ public class DefaultPlcHandler extends StrolchComponent implements PlcHandler, P Resource addressRes = tx.getResourceBy(TYPE_PLC_ADDRESS, addressId, true); // see if we need to invert a boolean flag - if (address.valueType == StrolchValueType.BOOLEAN && address.inverted) { + if (address.valueType == StrolchValueType.BOOLEAN && address.inverted) value = !((boolean) value); - } Parameter valueP = addressRes.getParameter(PARAM_VALUE, true); - logger.info("PlcAddress {}-{} has changed from {} to {}", address.resource, address.action, - valueP.getValue(), value); + if (this.verbose) + logger.info("PlcAddress {}-{} has changed from {} to {}", address.resource, address.action, + valueP.getValue(), value); valueP.accept(new SetParameterValueVisitor(value)); tx.update(addressRes); @@ -229,11 +234,14 @@ public class DefaultPlcHandler extends StrolchComponent implements PlcHandler, P logger.error("Failed to update PlcAddress " + addressId + " with new value " + value, e); } - logger.info("async update " + address.address + " took " + (formatNanoDuration(nanoTime() - s))); + if (this.verbose) + logger.info("async update " + address.address + " took " + (formatNanoDuration(nanoTime() - s))); } private void updateConnectionState(PlcConnection plcConnection) { - long s = nanoTime(); + long s = 0L; + if (this.verbose) + s = nanoTime(); try { try (StrolchTransaction tx = openTx(validateCtx().getCertificate(), "updateConnectionState", false)) { @@ -247,7 +255,8 @@ public class DefaultPlcHandler extends StrolchComponent implements PlcHandler, P logger.error("Failed to update state for connection " + plcConnection.getId(), e); } - logger.info("updateConnectionState took " + (formatNanoDuration(nanoTime() - s))); + if (this.verbose) + logger.info("updateConnectionState took " + (formatNanoDuration(nanoTime() - s))); } private PrivilegeContext validateCtx() { diff --git a/strolch-plc-core/src/main/java/li/strolch/plc/core/hw/DefaultPlc.java b/strolch-plc-core/src/main/java/li/strolch/plc/core/hw/DefaultPlc.java index 3ffe76b..5dab09e 100644 --- a/strolch-plc-core/src/main/java/li/strolch/plc/core/hw/DefaultPlc.java +++ b/strolch-plc-core/src/main/java/li/strolch/plc/core/hw/DefaultPlc.java @@ -22,6 +22,7 @@ public class DefaultPlc implements Plc { private PlcListener globalListener; private MapOfLists listeners; private PlcConnectionStateChangeListener connectionStateChangeListener; + private boolean verbose; public DefaultPlc() { this.notificationMappings = new HashMap<>(); @@ -30,6 +31,11 @@ public class DefaultPlc implements Plc { this.connectionsByAddress = new HashMap<>(); } + @Override + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + @Override public void setGlobalListener(PlcListener listener) { this.globalListener = listener; @@ -52,14 +58,15 @@ public class DefaultPlc implements Plc { @Override public void notify(String address, Object value) { - logger.info("Update for " + address + " with value " + value); - PlcAddress plcAddress = this.notificationMappings.get(address); if (plcAddress == null) { logger.warn("No mapping to PlcAddress for hwAddress " + address); return; } + logger.info("Update for {}-{} @ {} with value {}", plcAddress.resource, plcAddress.action, plcAddress.address, + value); + List listeners = this.listeners.getList(plcAddress); if (listeners == null || listeners.isEmpty()) { logger.warn("No listeners for key " + plcAddress); @@ -79,16 +86,19 @@ public class DefaultPlc implements Plc { @Override public void send(PlcAddress plcAddress) { - logger.info("Sending " + plcAddress.resource + "-" + plcAddress.action + ": " + plcAddress.defaultValue - + " (default)"); - validateConnection(plcAddress).send(plcAddress.address, plcAddress.defaultValue); + if (this.verbose) + logger.info("Sending {}-{}: {} (default)", plcAddress.resource, plcAddress.action, plcAddress.defaultValue); + if (!isVirtual(plcAddress)) + validateConnection(plcAddress).send(plcAddress.address, plcAddress.defaultValue); notify(plcAddress.address, plcAddress.defaultValue); } @Override public void send(PlcAddress plcAddress, Object value) { - logger.info("Sending " + plcAddress.resource + "-" + plcAddress.action + ": " + value); - validateConnection(plcAddress).send(plcAddress.address, value); + if (this.verbose) + logger.info("Sending {}-{}: {}", plcAddress.resource, plcAddress.action, value); + if (!isVirtual(plcAddress)) + validateConnection(plcAddress).send(plcAddress.address, value); notify(plcAddress.address, value); } @@ -154,18 +164,40 @@ public class DefaultPlc implements Plc { @Override public void registerNotificationMapping(PlcAddress address) { - if (!this.connectionsByAddress.containsKey(address.address)) + + boolean virtual = isVirtual(address); + if (virtual) + validateVirtualAddress(address); + else if (!this.connectionsByAddress.containsKey(address.address)) throw new IllegalStateException( "There is no connection registered for address " + address.address + " for key " + address); - logger.info("Registered address mapping for " + address); if (address.type != PlcAddressType.Notification) throw new IllegalArgumentException("Key must be of type " + PlcAddressType.Notification + ": " + address); PlcAddress replaced = this.notificationMappings.put(address.address, address); - if (replaced != null) { + if (replaced != null) throw new IllegalArgumentException( "Replaced mapping for address " + address.address + " for key " + replaced + " with " + address); + + logger.info("Registered address mapping for " + address); + } + + private void validateVirtualAddress(PlcAddress address) { + + if (address.address.equals("virtualBoolean") || address.address.equals("virtualBoolean.")) { + throw new IllegalStateException( + "Virtual address " + address.address + " is missing sub component for " + address); + } + + if (address.address.equals("virtualString") || address.address.equals("virtualString.")) { + throw new IllegalStateException( + "Virtual address " + address.address + " is missing sub component for " + address); } } + + private boolean isVirtual(PlcAddress address) { + return address.address.startsWith("virtualBoolean") // + || address.address.startsWith("virtualString"); + } } diff --git a/strolch-plc-core/src/main/java/li/strolch/plc/core/hw/Plc.java b/strolch-plc-core/src/main/java/li/strolch/plc/core/hw/Plc.java index 3fb410e..8fa6ee3 100644 --- a/strolch-plc-core/src/main/java/li/strolch/plc/core/hw/Plc.java +++ b/strolch-plc-core/src/main/java/li/strolch/plc/core/hw/Plc.java @@ -31,4 +31,6 @@ public interface Plc { void notifyConnectionStateChanged(PlcConnection connection); void setConnectionStateChangeListener(PlcConnectionStateChangeListener listener); + + void setVerbose(boolean verbose); } diff --git a/strolch-plc-core/src/main/java/li/strolch/plc/core/hw/connections/VirtualBooleanValueConnection.java b/strolch-plc-core/src/main/java/li/strolch/plc/core/hw/connections/VirtualBooleanValueConnection.java deleted file mode 100644 index a9b46d0..0000000 --- a/strolch-plc-core/src/main/java/li/strolch/plc/core/hw/connections/VirtualBooleanValueConnection.java +++ /dev/null @@ -1,17 +0,0 @@ -package li.strolch.plc.core.hw.connections; - -import li.strolch.plc.core.hw.Plc; - -public class VirtualBooleanValueConnection extends SimplePlcConnection { - - public VirtualBooleanValueConnection(Plc plc, String id) { - super(plc, id); - } - - @Override - public void send(String address, Object value) { - boolean bool = (boolean) value; - logger.info("Setting address " + this.id + " to " + bool); - // this.plc.notify(this.id, bool); - } -} diff --git a/strolch-plc-core/src/main/java/li/strolch/plc/core/hw/connections/VirtualStringValueConnection.java b/strolch-plc-core/src/main/java/li/strolch/plc/core/hw/connections/VirtualStringValueConnection.java deleted file mode 100644 index 1184d0a..0000000 --- a/strolch-plc-core/src/main/java/li/strolch/plc/core/hw/connections/VirtualStringValueConnection.java +++ /dev/null @@ -1,17 +0,0 @@ -package li.strolch.plc.core.hw.connections; - -import li.strolch.plc.core.hw.Plc; - -public class VirtualStringValueConnection extends SimplePlcConnection { - - public VirtualStringValueConnection(Plc plc, String id) { - super(plc, id); - } - - @Override - public void send(String address, Object value) { - String string = (String) value; - logger.info("Setting address " + this.id + " to " + string); - // this.plc.notify(this.id, string); - } -} diff --git a/strolch-plc-core/src/main/java/li/strolch/plc/core/hw/i2c/PCF8574InputConnection.java b/strolch-plc-core/src/main/java/li/strolch/plc/core/hw/i2c/PCF8574InputConnection.java index 1b80bf7..59e15ea 100644 --- a/strolch-plc-core/src/main/java/li/strolch/plc/core/hw/i2c/PCF8574InputConnection.java +++ b/strolch-plc-core/src/main/java/li/strolch/plc/core/hw/i2c/PCF8574InputConnection.java @@ -139,6 +139,8 @@ public class PCF8574InputConnection extends PlcConnection { if (gpioController.getProvisionedPins().stream().map(GpioPin::getPin).anyMatch(interruptPin::equals)) throw new IllegalStateException("Pin " + interruptPin + " is already provisioned!"); this.interruptGpioPin = gpioController.provisionDigitalInputPin(interruptPin, this.interruptResistance); + logger.info("Provisioned GPIO Input pin " + this.interruptGpioPin + " with PinPullResistance " + + this.interruptResistance); this.interruptGpioPin.removeAllListeners(); this.interruptGpioPin.addListener((GpioPinListenerDigital) this::handleInterrupt); @@ -165,6 +167,7 @@ public class PCF8574InputConnection extends PlcConnection { if (this.interruptGpioPin != null) { this.interruptGpioPin.removeAllListeners(); PlcGpioController.getInstance().unprovisionPin(this.interruptGpioPin); + logger.info("Provisioned GPIO Input pin " + this.interruptGpioPin); } this.inputDevices = null; @@ -193,6 +196,11 @@ public class PCF8574InputConnection extends PlcConnection { for (int i = 0; i < this.inputDevices.length; i++) { I2CDevice i2CDevice = this.inputDevices[i]; + if (i2CDevice == null) { + logger.warn("Ignoring invalid I2C Device 0x" + toHexString(this.addresses[i])); + continue; + } + byte data = (byte) i2CDevice.read(); if (this.verbose) @@ -236,6 +244,7 @@ public class PCF8574InputConnection extends PlcConnection { } } catch (Exception e) { ok = false; + this.inputDevices[i] = null; logger.error("Failed to read initial state for address 0x" + toHexString((byte) i2CDevice.getAddress()), e); } diff --git a/strolch-plc-core/src/test/java/li/strolch/plc/core/PlcHandlerTest.java b/strolch-plc-core/src/test/java/li/strolch/plc/core/PlcHandlerTest.java index 9ed713d..06db2d4 100644 --- a/strolch-plc-core/src/test/java/li/strolch/plc/core/PlcHandlerTest.java +++ b/strolch-plc-core/src/test/java/li/strolch/plc/core/PlcHandlerTest.java @@ -2,6 +2,8 @@ package li.strolch.plc.core; import static li.strolch.plc.model.PlcConstants.PARAM_VALUE; import static li.strolch.plc.model.PlcConstants.TYPE_PLC_ADDRESS; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import java.util.concurrent.atomic.AtomicReference; @@ -14,7 +16,6 @@ import li.strolch.plc.model.PlcState; import li.strolch.privilege.model.Certificate; import li.strolch.testbase.runtime.RuntimeMock; import org.junit.AfterClass; -import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.slf4j.Logger; @@ -29,7 +30,7 @@ public class PlcHandlerTest { private static Certificate cert; @BeforeClass - public static void beforeClass() throws Exception { + public static void beforeClass() { runtimeMock = new RuntimeMock().mockRuntime(TARGET_PATH, SRC_RUNTIME); runtimeMock.startContainer(); cert = runtimeMock.loginAdmin(); @@ -47,33 +48,71 @@ public class PlcHandlerTest { } @Test - public void shouldStartPlcHandler() throws InterruptedException { + public void shouldStartPlcHandler() { PlcHandler plcHandler = runtimeMock.getComponent(PlcHandler.class); - Assert.assertEquals(PlcState.Started, plcHandler.getPlcState()); + assertEquals(PlcState.Started, plcHandler.getPlcState()); PlcConnection loggerOutput = plcHandler.getPlc().getConnection("loggerOutput"); - Assert.assertEquals(ConnectionState.Connected, loggerOutput.getState()); + assertEquals(ConnectionState.Connected, loggerOutput.getState()); PlcConnection barcodeReader = plcHandler.getPlc().getConnection("barcodeReader"); - Assert.assertEquals(ConnectionState.Connected, barcodeReader.getState()); + assertEquals(ConnectionState.Connected, barcodeReader.getState()); String plcAddressId = plcHandler.getPlcAddressId("PLC", "Started"); - Assert.assertEquals("addrPlcStarted", plcAddressId); + assertEquals("addrPlcStarted", plcAddressId); try (StrolchTransaction tx = runtimeMock.openUserTx(cert, true)) { Resource plcStartedAddr = tx.getResourceBy(TYPE_PLC_ADDRESS, plcAddressId, true); BooleanParameter valueP = plcStartedAddr.getParameter(PARAM_VALUE, true); - Assert.assertEquals(true, valueP.getValue()); + assertEquals(true, valueP.getValue()); } } @Test - public void shouldNotifyPlcService() throws InterruptedException { + public void shouldNotifyPlcService() { PlcHandler plcHandler = runtimeMock.getComponent(PlcHandler.class); AtomicReference value = new AtomicReference<>(""); plcHandler.registerListener("BarcodeReader", "Barcode", (address, v) -> value.set((String) v)); plcHandler.send("BarcodeReader", "ReadBarcode", "DoRead"); - Assert.assertNotEquals("", value.get()); + assertNotEquals("", value.get()); + } + + @Test + public void shouldSendVirtualBoolean() { + + PlcHandler plcHandler = runtimeMock.getComponent(PlcHandler.class); + String addressId = plcHandler.getPlcAddressId("PLC", "Running"); + AtomicReference value; + try (StrolchTransaction tx = runtimeMock.openUserTx(cert, true)) { + Resource address = tx.getResourceBy(TYPE_PLC_ADDRESS, addressId, true); + value = new AtomicReference<>(address.getParameter(PARAM_VALUE, true).getValue()); + } + assertEquals(false, value.get()); + + plcHandler.registerListener("PLC", "Running", (address, v) -> value.set((Boolean) v)); + plcHandler.send("PLC", "Running"); + assertEquals(true, value.get()); + plcHandler.send("PLC", "NotRunning"); + assertEquals(false, value.get()); + } + + @Test + public void shouldSendVirtualString() { + + PlcHandler plcHandler = runtimeMock.getComponent(PlcHandler.class); + String addressId = plcHandler.getPlcAddressId("Server", "Connected"); + AtomicReference value; + try (StrolchTransaction tx = runtimeMock.openUserTx(cert, true)) { + Resource address = tx.getResourceBy(TYPE_PLC_ADDRESS, addressId, true); + value = new AtomicReference<>(address.getParameter(PARAM_VALUE, true).getValue()); + } + assertEquals("Disconnected", value.get()); + + plcHandler.registerListener("Server", "Connected", (address, v) -> value.set((String) v)); + plcHandler.send("Server", "Connected", "Connected"); + assertEquals("Connected", value.get()); + plcHandler.send("Server", "Connected", "Disconnected"); + assertEquals("Disconnected", value.get()); } } diff --git a/strolch-plc-core/src/test/resources/test-runtime/data/defaultModel.xml b/strolch-plc-core/src/test/resources/test-runtime/data/defaultModel.xml index f5cfc62..aafceaa 100644 --- a/strolch-plc-core/src/test/resources/test-runtime/data/defaultModel.xml +++ b/strolch-plc-core/src/test/resources/test-runtime/data/defaultModel.xml @@ -14,26 +14,6 @@ - - - - - - - - - - - - - - - - @@ -98,20 +78,92 @@ + Value="addrPlcStarted, addrPlcRunning, addrServerConnected"/> + Value="telPlcStarted, telPlcStopped, telPlcRunning, telPlcNotRunning, telServerConnected, telServerDisconnected"/> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +