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 e07e2cb..06b6107 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 @@ -25,6 +25,7 @@ import li.strolch.privilege.model.Certificate; import li.strolch.privilege.model.PrivilegeContext; import li.strolch.runtime.configuration.ComponentConfiguration; import li.strolch.utils.collections.MapOfMaps; +import li.strolch.utils.dbc.DBC; public class DefaultPlcHandler extends StrolchComponent implements PlcHandler, PlcConnectionStateChangeListener { @@ -59,6 +60,8 @@ public class DefaultPlcHandler extends StrolchComponent implements PlcHandler, P @Override public PlcAddress getPlcAddress(String resource, String action) { + DBC.PRE.assertNotNull("resource must not be null", resource); + DBC.PRE.assertNotEmpty("action must not be empty", action); PlcAddress plcAddress = this.plcAddresses.getElement(resource, action); if (plcAddress == null) throw new IllegalStateException("PlcAddress for " + resource + "-" + action + " does not exist!"); @@ -67,6 +70,8 @@ public class DefaultPlcHandler extends StrolchComponent implements PlcHandler, P @Override public String getPlcAddressId(String resource, String action) { + DBC.PRE.assertNotNull("resource must not be null", resource); + DBC.PRE.assertNotEmpty("action must not be empty", action); PlcAddress plcAddress = getPlcAddress(resource, action); String addressId = this.addressesToResourceId.get(plcAddress); if (addressId == null) diff --git a/strolch-plc-core/src/main/java/li/strolch/plc/core/PlcService.java b/strolch-plc-core/src/main/java/li/strolch/plc/core/PlcService.java index 55e2fdc..5699556 100644 --- a/strolch-plc-core/src/main/java/li/strolch/plc/core/PlcService.java +++ b/strolch-plc-core/src/main/java/li/strolch/plc/core/PlcService.java @@ -81,7 +81,7 @@ public abstract class PlcService implements PlcListener { } protected StrolchTransaction openTx(PrivilegeContext ctx, boolean readOnly) { - return this.plcHandler.openTx(ctx.getCertificate(), readOnly); + return this.container.getRealm(ctx.getCertificate()).openTx(ctx.getCertificate(), getClass(), readOnly); } protected void runAsAgent(PrivilegedRunnable runnable) throws Exception { diff --git a/strolch-plc-gw-server/src/main/java/li/strolch/plc/gw/server/PlcGwServerHandler.java b/strolch-plc-gw-server/src/main/java/li/strolch/plc/gw/server/PlcGwServerHandler.java index 3233267..d5c53f0 100644 --- a/strolch-plc-gw-server/src/main/java/li/strolch/plc/gw/server/PlcGwServerHandler.java +++ b/strolch-plc-gw-server/src/main/java/li/strolch/plc/gw/server/PlcGwServerHandler.java @@ -24,6 +24,7 @@ import li.strolch.privilege.model.Certificate; import li.strolch.rest.StrolchSessionHandler; import li.strolch.runtime.configuration.ComponentConfiguration; import li.strolch.utils.collections.MapOfLists; +import li.strolch.utils.dbc.DBC; import li.strolch.websocket.WebSocketRemoteIp; public class PlcGwServerHandler extends StrolchComponent { @@ -54,10 +55,13 @@ public class PlcGwServerHandler extends StrolchComponent { } public boolean isPlcConnected(String plcId) { + DBC.PRE.assertNotEmpty("plcId must not be empty", plcId); return this.plcSessionsByPlcId.containsKey(plcId); } public void register(PlcAddressKey addressKey, String plcId, PlcNotificationListener listener) { + DBC.PRE.assertNotNull("addressKey must not be null", addressKey); + DBC.PRE.assertNotEmpty("plcId must not be empty", plcId); MapOfLists plcListeners = this.plcAddressListenersByPlcId.get(plcId); if (plcListeners == null) { plcListeners = new MapOfLists<>(); @@ -67,9 +71,13 @@ public class PlcGwServerHandler extends StrolchComponent { synchronized (plcListeners) { plcListeners.addElement(addressKey, listener); } + + logger.info("Registered listener on plc " + plcId + " key " + addressKey + ": " + listener); } public void unregister(PlcAddressKey addressKey, String plcId, PlcNotificationListener listener) { + DBC.PRE.assertNotNull("addressKey must not be null", addressKey); + DBC.PRE.assertNotEmpty("plcId must not be empty", plcId); MapOfLists plcListeners = this.plcAddressListenersByPlcId.get(plcId); if (plcListeners == null) return; @@ -77,6 +85,8 @@ public class PlcGwServerHandler extends StrolchComponent { synchronized (plcListeners) { plcListeners.removeElement(addressKey, listener); } + + logger.info("Unregistered listener from plc " + plcId + " key " + addressKey + ": " + listener); } public void sendMessage(PlcAddressKey addressKey, String plcId, boolean value, diff --git a/strolch-plc-gw-server/src/main/java/li/strolch/plc/gw/server/PlcGwService.java b/strolch-plc-gw-server/src/main/java/li/strolch/plc/gw/server/PlcGwService.java new file mode 100644 index 0000000..0f496ac --- /dev/null +++ b/strolch-plc-gw-server/src/main/java/li/strolch/plc/gw/server/PlcGwService.java @@ -0,0 +1,140 @@ +package li.strolch.plc.gw.server; + +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import li.strolch.agent.api.ComponentContainer; +import li.strolch.execution.ExecutionHandler; +import li.strolch.persistence.api.StrolchTransaction; +import li.strolch.plc.model.PlcAddressKey; +import li.strolch.plc.model.PlcNotificationListener; +import li.strolch.plc.model.PlcServiceState; +import li.strolch.privilege.model.PrivilegeContext; +import li.strolch.runtime.privilege.PrivilegedRunnable; +import li.strolch.runtime.privilege.PrivilegedRunnableWithResult; +import li.strolch.utils.dbc.DBC; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class PlcGwService implements PlcNotificationListener, PlcAddressResponseListener { + + protected static final Logger logger = LoggerFactory.getLogger(PlcGwService.class); + protected final String plcId; + protected final ComponentContainer container; + protected final PlcGwServerHandler plcHandler; + + private PlcServiceState state; + + public PlcGwService(String plcId, ComponentContainer container, PlcGwServerHandler plcHandler) { + DBC.PRE.assertNotEmpty("plcId must be set!", plcId); + DBC.PRE.assertNotNull("container must be set!", container); + DBC.PRE.assertNotNull("plcHandler must be set!", plcHandler); + this.plcId = plcId; + this.container = container; + this.plcHandler = plcHandler; + this.state = PlcServiceState.Unregistered; + } + + public PlcServiceState getState() { + return this.state; + } + + public void start(StrolchTransaction tx) { + this.state = PlcServiceState.Started; + } + + public void stop() { + this.state = PlcServiceState.Stopped; + } + + public void register() { + this.state = PlcServiceState.Registered; + } + + public void unregister() { + this.state = PlcServiceState.Unregistered; + } + + protected void register(PlcAddressKey key) { + this.plcHandler.register(key, this.plcId, this); + } + + protected void unregister(PlcAddressKey key) { + this.plcHandler.unregister(key, this.plcId, this); + } + + public void sendMessage(PlcAddressKey addressKey, String plcId, boolean value, + PlcAddressResponseListener listener) { + this.plcHandler.sendMessage(addressKey, plcId, value, this); + } + + public void sendMessage(PlcAddressKey addressKey, String plcId, int value, PlcAddressResponseListener listener) { + this.plcHandler.sendMessage(addressKey, plcId, value, this); + } + + public void sendMessage(PlcAddressKey addressKey, String plcId, double value, PlcAddressResponseListener listener) { + this.plcHandler.sendMessage(addressKey, plcId, value, this); + } + + public void sendMessage(PlcAddressKey addressKey, String plcId, String value, PlcAddressResponseListener listener) { + this.plcHandler.sendMessage(addressKey, plcId, value, this); + } + + public void sendMessage(PlcAddressKey addressKey, String plcId, PlcAddressResponseListener listener) { + this.plcHandler.sendMessage(addressKey, plcId, this); + } + + protected StrolchTransaction openTx(PrivilegeContext ctx, boolean readOnly) { + return this.container.getRealm(ctx.getCertificate()).openTx(ctx.getCertificate(), getClass(), readOnly); + } + + protected void runAsAgent(PrivilegedRunnable runnable) throws Exception { + this.container.getPrivilegeHandler().runAsAgent(runnable); + } + + protected ExecutionHandler getExecutionHandler() { + return this.container.getComponent(ExecutionHandler.class); + } + + protected T runAsAgentWithResult(PrivilegedRunnableWithResult runnable) throws Exception { + return this.container.getPrivilegeHandler().runAsAgentWithResult(runnable); + } + + protected ScheduledFuture schedule(PrivilegedRunnable runnable, long delay, TimeUnit delayUnit) { + return this.container.getAgent().getScheduledExecutor(PlcGwService.class.getSimpleName()).schedule(() -> { + try { + this.container.getPrivilegeHandler().runAsAgent(runnable); + } catch (Exception e) { + handleFailedSchedule(e); + } + }, delay, delayUnit); + } + + protected ScheduledFuture scheduleAtFixedRate(PrivilegedRunnable runnable, long initialDelay, long period, + TimeUnit delayUnit) { + return this.container.getAgent().getScheduledExecutor(PlcGwService.class.getSimpleName()) + .scheduleAtFixedRate(() -> { + try { + this.container.getPrivilegeHandler().runAsAgent(runnable); + } catch (Exception e) { + handleFailedSchedule(e); + } + }, initialDelay, period, delayUnit); + } + + protected ScheduledFuture scheduleWithFixedDelay(PrivilegedRunnable runnable, long initialDelay, long period, + TimeUnit delayUnit) { + return this.container.getAgent().getScheduledExecutor(PlcGwService.class.getSimpleName()) + .scheduleWithFixedDelay(() -> { + try { + this.container.getPrivilegeHandler().runAsAgent(runnable); + } catch (Exception e) { + handleFailedSchedule(e); + } + }, initialDelay, period, delayUnit); + } + + protected void handleFailedSchedule(Exception e) { + logger.error("Failed to execute " + getClass().getSimpleName(), e); + } +} diff --git a/strolch-plc-gw-server/src/main/java/li/strolch/plc/gw/server/policy/execution/PlcExecutionPolicy.java b/strolch-plc-gw-server/src/main/java/li/strolch/plc/gw/server/policy/execution/PlcExecutionPolicy.java index 68ba995..7e82ca6 100644 --- a/strolch-plc-gw-server/src/main/java/li/strolch/plc/gw/server/policy/execution/PlcExecutionPolicy.java +++ b/strolch-plc-gw-server/src/main/java/li/strolch/plc/gw/server/policy/execution/PlcExecutionPolicy.java @@ -1,8 +1,6 @@ package li.strolch.plc.gw.server.policy.execution; -import static li.strolch.model.StrolchModelConstants.BAG_PARAMETERS; import static li.strolch.plc.gw.server.PlcServerContants.BUNDLE_STROLCH_PLC_GW_SERVER; -import static li.strolch.plc.model.PlcConstants.PARAM_PLC_ID; import static li.strolch.runtime.StrolchConstants.SYSTEM_USER_AGENT; import li.strolch.execution.policy.SimpleExecution; @@ -10,70 +8,62 @@ import li.strolch.handler.operationslog.LogMessage; import li.strolch.handler.operationslog.LogSeverity; import li.strolch.model.Locator; import li.strolch.model.activity.Action; -import li.strolch.model.parameter.StringParameter; import li.strolch.persistence.api.StrolchTransaction; import li.strolch.plc.gw.server.PlcGwServerHandler; import li.strolch.plc.model.PlcAddressKey; import li.strolch.plc.model.PlcNotificationListener; +import li.strolch.utils.helper.StringHelper; public abstract class PlcExecutionPolicy extends SimpleExecution implements PlcNotificationListener { - private String realm; + protected String realm; - private Locator actionLoc; - private String plcId; - private PlcGwServerHandler plcHandler; - private PlcAddressKey addressKey; + protected String actionType; + protected Locator actionLoc; + + protected PlcGwServerHandler plcHandler; public PlcExecutionPolicy(StrolchTransaction tx) { super(tx); this.realm = tx.getRealmName(); } - protected void initialize(Action action) { - this.actionLoc = action.getLocator(); + protected abstract String getPlcId(); - // set all fields - getPlcId(action); - getPlcHandler(); - getAddressKey(action); + protected void initialize(Action action) { + this.actionType = action.getType(); + this.actionLoc = action.getLocator(); + this.plcHandler = getComponent(PlcGwServerHandler.class); + } + + protected void toExecuted() { + + unregister(); + + long delay = 5L; + logger.info( + "Delaying toExecuted of " + getActionLoc() + " by " + StringHelper.formatMillisecondsDuration(delay)); + getDelayedExecutionTimer().execute(this.realm, getContainer(), getActionLoc(), delay); + } + + public String getActionType() { + return this.actionType; } public Locator getActionLoc() { return this.actionLoc; } - protected String getPlcId(Action action) { - if (this.plcId == null) { - StringParameter plcIdP = action.findParameter(BAG_PARAMETERS, PARAM_PLC_ID, true); - this.plcId = plcIdP.getValue(); - } - - return this.plcId; + protected void register() { + // do nothing } - protected PlcAddressKey getAddressKey(Action action) { - if (this.addressKey == null) - this.addressKey = PlcAddressKey.valueOf(action.getResourceId(), action.getType()); - return this.addressKey; + protected void unregister() { + // do nothing } - protected PlcGwServerHandler getPlcHandler() { - if (this.plcHandler == null) - this.plcHandler = getComponent(PlcGwServerHandler.class); - return this.plcHandler; - } - - protected void register(Action action) { - getPlcHandler().register(getAddressKey(action), getPlcId(action), this); - } - - protected void unregister(Action action) { - getPlcHandler().unregister(getAddressKey(action), getPlcId(action), this); - } - - protected boolean assertPlcConnected(Action action) { - if (getPlcHandler().isPlcConnected(getPlcId(action))) + protected boolean assertPlcConnected() { + if (this.plcHandler.isPlcConnected(getPlcId())) return true; toError(msgPlcNotConnected(this.realm)); @@ -90,11 +80,11 @@ public abstract class PlcExecutionPolicy extends SimpleExecution implements PlcN protected LogMessage msgPlcNotConnected(String realm) { return new LogMessage(realm, SYSTEM_USER_AGENT, getActionLoc(), LogSeverity.Warning, - BUNDLE_STROLCH_PLC_GW_SERVER, "execution.plc.notConnected").value("plc", this.plcId); + BUNDLE_STROLCH_PLC_GW_SERVER, "execution.plc.notConnected").value("plc", getPlcId()); } protected LogMessage msgConnectionLostToPlc(String realm) { return new LogMessage(realm, SYSTEM_USER_AGENT, getActionLoc(), LogSeverity.Error, BUNDLE_STROLCH_PLC_GW_SERVER, - "execution.plc.connectionLost").value("plc", this.plcId); + "execution.plc.connectionLost").value("plc", getPlcId()); } } diff --git a/strolch-plc-model/src/main/java/li/strolch/plc/model/PlcNotificationListener.java b/strolch-plc-model/src/main/java/li/strolch/plc/model/PlcNotificationListener.java index 60f7bf4..3d0c52b 100644 --- a/strolch-plc-model/src/main/java/li/strolch/plc/model/PlcNotificationListener.java +++ b/strolch-plc-model/src/main/java/li/strolch/plc/model/PlcNotificationListener.java @@ -4,5 +4,7 @@ public interface PlcNotificationListener { void handleNotification(PlcAddressKey addressKey, Object value); - void handleConnectionLost(); + default void handleConnectionLost() { + // no-op + } } diff --git a/strolch-plc-model/src/main/java/li/strolch/plc/model/PlcResponse.java b/strolch-plc-model/src/main/java/li/strolch/plc/model/PlcResponse.java index dfa49af..017de9c 100644 --- a/strolch-plc-model/src/main/java/li/strolch/plc/model/PlcResponse.java +++ b/strolch-plc-model/src/main/java/li/strolch/plc/model/PlcResponse.java @@ -34,6 +34,14 @@ public class PlcResponse { this.state = state; } + public boolean isSent() { + return this.state == PlcResponseState.Sent; + } + + public boolean isFailed() { + return this.state == PlcResponseState.Failed; + } + public String getStateMsg() { return this.stateMsg; }